.TITLE ZALU .IDENT /V1.5/ ; ++ ; This is the Z-machine handler for ALU opcodes. ; (c) 2000 by Johnny Billquist ; ; History: ; ; 00-07-19 BQT Initial coding started. ; Y1.0 00-08-14 BQT First release. ; Y1.1 00-09-01 04:00 BQT Bugfix. Random didn't work when argument was ; 1. For some reason, the DIV instr. don't work ; as expected when a divide by 1 is done. ; Y1.2 00-09-08 02:00 BQT Made SEED global. ; V1.3 00-09-17 21:00 BQT Improved random number algorithm. ; V1.4 03-01-22 17:30 BQT Removed special case for DIV with 1. ; Not sure where from I got that it didn't work. ; V1.5 05-02-21 00:30 BQT Improved code speed. ; -- .INCLUDE /ZMAC/ .PSECT DATA,D,RW ; SEED:: .WORD 0 ; Random seed. ; .PSECT CODE,I,RO ; ; 2OP:7 7 test bitmap flags ?(label) ; Jump if arg1 & arg2 = arg2 ; .ENABL LSB TEST:: .INSTR "test",#2 MOV (R2)+,R0 ; Get arguments. MOV (R2),R1 COM R0 ; Invert first argument. BIC R0,R1 ; And do a clear on second arg. ; (This means we'll do an AND). CMP R1,(R2) ; Is the result the same as arg2? BEQ 10$ JMP BFALSE ; No. 10$: JMP BTRUE ; Yes. .DSABL LSB ; ; 2OP:8 8 or a b -> (result) ; OR - arg0 ^ arg1 ; .ENABL LSB OR:: .INSTR "or",#2 MOV (R2)+,R0 ; Get arguments... BIS (R2),R0 ; and OR them. CALLR RESULT ; That's it. .DSABL LSB ; ; 2OP:9 9 and a b -> (result) ; ; AND - arg0 & arg1 ; .ENABL LSB AND:: .INSTR "and",#2 MOV (R2)+,R0 ; Get argument... COM (R2) ; The second we want inverted. BIC (R2),R0 ; That means a BIC becomes AND. CALLR RESULT ; That's it. .DSABL LSB ; ; 2OP:20 14 add a b -> (result) ; ; ADD - arg0 + arg1 ; .ENABL LSB ZADD:: .INSTR "add",#2 MOV (R2)+,R0 ; Get 1:st arg. ADD (R2),R0 ; Add second arg. CALLR RESULT ; That's it. .DSABL LSB ; ; 2OP:21 15 sub a b -> (result) ; SUB - arg0 - arg1 ; .ENABL LSB ZSUB:: .INSTR "sub",#2 MOV (R2)+,R0 ; Get 1:st arg. SUB (R2),R0 ; Substract second arg. CALLR RESULT ; That's it. .DSABL LSB ; ; 2OP:22 16 mul a b -> (result) ; MUL - arg0 * arg1 ; .ENABL LSB ZMUL:: .INSTR "mul",#2 MOV (R2)+,R1 ; Get 1:st arg. MUL (R2),R1 ; Multiply second arg. MOV R1,R0 CALLR RESULT ; That's it. .DSABL LSB ; ; 2OP:23 17 div a b -> (result) ; ; DIV - arg0 / arg1 ; .ENABL LSB ZDIV:: .INSTR "div",#2 MOV (R2)+,R1 ; Get 1:st arg. SXT R0 ; as a 32-bit signed value. DIV (R2),R0 ; Divide with second arg. CALLR RESULT ; That's it. .DSABL LSB ; ; 2OP:24 18 mod a b -> (result) ; MOD - arg0 % arg1 ; .ENABL LSB ZMOD:: .INSTR "mod",#2 MOV (R2)+,R1 ; Get 1:st arg. SXT R0 ; as a 32-bit signed value. DIV (R2),R0 ; Divide. MOV R1,R0 ; Get remainder. CALLR RESULT ; That's it. .DSABL LSB ; ; 1OP:133 5 inc (variable) ; .ENABL LSB ZINC:: .INSTR "inc",#1 MOV (R2),R0 ; Get variable number. CALL VARVAL ; Get value. INC R1 ; Increment it. CALLR STOREV ; That's it. .DSABL LSB ; ; 1OP:134 6 dec (variable) ; ; Decrement variable. ; .ENABL LSB ZDEC:: .INSTR "dec",#1 MOV (R2),R0 ; Get variable number. CALL VARVAL ; Get value. DEC R1 ; Decrement. CALLR STOREV ; That's it. .DSABL LSB ; ; 1OP:143 F 1/4 not value -> (result) ; VAR:248 18 5/6 not value -> (result) ; .ENABL LSB ZNOT:: .INSTR "not",#1 MOV (R2),R0 ; Get value. COM R0 ; Invert it. CALLR RESULT ; That's it. .DSABL LSB ; ; VAR:231 7 random range -> (result) ; ; Get a random value. If arg > 0 give a value between 1 and arg. ; If arg = 0, set seed to random start. ; If arg < 0, use that as random seed. ; .ENABL LSB RANDOM:: .INSTR "random",#1 MOV (R2),R0 ; Get range. BGT 10$ ; Usual stuff. BNE 5$ ; We have a seed. CALL GETRND ; No seed? Well, get one. 5$: MOV R0,SEED ; Save seed value. CLR R0 ; No result. CALLR RESULT ; That's it. ; ; Time to get a new random value. ; 10$: MOV SEED,R3 ; Silly algorithm. ADD #17.,R3 MUL #8197.,R3 MOV R3,SEED ; ; We now have the new seed created, and also this random number is in ; R3. We should now use this to create a good random number in the requested ; range. ; ; We cannot simply divide here, since unless the requested range is a ; power of two, we'll get a non-uniform spread. ; ; If we were to use FP, the algorithm would be: ; seed/MAXINT+1 * range. ; ; Now we shall avoid FP, and we'll do it the other way around... ; (seed * range) / MAXINT+1. ; ; Since we cannot in any good way represent MAXINT+1, we'll play ; that MAXINT is only a fourth what it really is, and see that ; 0 <= seed < MAXINT ; CMP R0,#1 ; Only one possible value? BEQ 11$ ; Yes. BIC #140000,R3 ; No. Set seed condition. MUL R3,R0 ; Multiply with range. DIV #40000,R0 ; Divide by MAXINT. INC R0 ; Result should be 1-range. 11$: CALLR RESULT ; That's it. .DSABL LSB ; ; EXT:2 2 5 log_shift number places -> (result) ; Do a logic shift of arg1 by arg2 places. If arg2 is ; negative, shift is to the right, otherwise to the left. ; .ENABL LSB LOGSFT:: .INSTR "log_shift",#2 CLR R0 ; Clear high 16 bits. MOV (R2)+,R1 ; Number ASHC (R2),R0 ; Shift 32 bits, to insure ; no sign trick goes on. MOV R1,R0 ; Get result in R0. CALLR RESULT ; Done. .DSABL LSB ; ; EXT:3 3 5/- art_shift number places -> (result) ; ; Do an arithmetic shift of arg1 by arg2 places. ; If arg2 is negative, shift is to the right, otherwise ; to the left. ; .ENABL LSB ARTSFT:: .INSTR "art_shift",#2 MOV (R2)+,R0 ; Get number. ASH (R2),R0 ; Do the shift. CALLR RESULT ; Done. .DSABL LSB ; ; 2OP:4 4 dec_chk (variable) value ?(label) ; ; Decrement (arg1) and check if less than arg2. ; .ENABL LSB DECCHK:: .INSTR "dec_chk",#2 MOV (R2)+,R0 ; Get variable. CALL VARVAL ; Get value. DEC R1 ; Decrement. CALL STOREV ; Save new value. CMP R1,(R2) ; Check. BLT 10$ JMP BFALSE 10$: JMP BTRUE .DSABL LSB ; ; 2OP:5 5 inc_chk (variable) value ?(label) ; Increment (arg1) and check if greater than arg2. ; .ENABL LSB INCCHK:: .INSTR "inc_chk",#2 MOV (R2)+,R0 ; Get variable. CALL VARVAL ; Get value. INC R1 ; Increment. CALL STOREV ; Save new value. CMP R1,(R2) ; Check. BGT 10$ JMP BFALSE 10$: JMP BTRUE .DSABL LSB ; .END