;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;
;Arithmetic operations:
;
;neg* : Calculate 2's complement of a 16-bit register
;abs* : Ensure a 16-bit register is +ve
;
abshl: bit 7,h
ret z
neghl: push af
ld a,h ;To ones complement
cpl
ld h,a
ld a,l
cpl
ld l,a
inc hl ;To twos complement
pop af
ret
;
absde: bit 7,d
ret z
negde: push af
ld a,d ;To ones complement
cpl
ld d,a
ld a,e
cpl
ld e,a
inc de ;To twos complement
pop af
ret
;
absbc: bit 7,b
ret z
negbc: push af
ld a,b ;To ones complement
cpl
ld b,a
ld a,c
cpl
ld c,a
inc bc ;To twos complement
pop af
ret
;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;
;Signed 16-bit comparison
;
cpsign:
;
;IF HL < DE, return Carry set. IF HL >= DE, return Carry clear. IF HL=DE
;return Zero set.
;
;Check if one is -ve and the other positive
;
ld a,h
xor d ;IF the sign bits were different, bit 7 of A is set
bit 7,a
jr nz,dsign
;
;Signs are the same.
;
; << v1.01 Do not reverse the comparison if both values are negative.
; (In 2s-complement, 0FFFFh (-1) > 0FFFDh (-3) )
;
; So just fall through to the unsigned comparison.
; >>
;
cphlde:
cpusgn: ld a,h
cp d
ret nz
ld a,l
cp e
ret
;
cpdebc: ld a,d
cp b
ret nz
ld a,e
cp c
ret
;
dsign: bit 7,h ;Is HL the negative one?
scf
ret nz ;IF so, HL < DE. Return NZ C
ncnz: xor a
inc a ;Force NZ
ret ;Otherwise, HL >= DE. Return NZ NC
;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;
;Signed 16-bit multiplication, BC * DE -> DE
;
mult16: push af
push bc
push hl
call m16w
pop hl
pop bc
pop af
ret
;
m16w: ld a,b
xor d
bit 7,a ;Negative * positive
jr nz,mixmult
call absde
call absbc
jr umult16
;
mixmult:
call absde
call absbc
call umult16
jp negde
;
;Unsigned 16-bit multiplication, HLDE := BC * DE
;
umult16:
ld hl,0
ld a,16 ;16-bit multiplication
umulta: bit 0,e
jr z,umultb
add hl,bc
umultb: srl h
rr l
rr d
rr e
dec a
jr nz,umulta
ret
;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;
;Compute BC mod DE, returns result in HL.
;
smod16: push de
push bc
call mo16w
pop bc
pop de
ret
;
mo16w: call absde ;a mod (-b) == a mod b
bit 7,b ;(-a) mod b == - (a mod b)
jr z,udiv16
call absbc
call udiv16
jp neghl
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;
;Divide BC by DE, returns result in BC.
;
sdiv16: push de
push hl
call d16w
pop hl
pop de
ret
;
d16w: ld a,b ;Same sign, or opposite signs?
xor d
bit 7,a
jr nz,mixdiv
call absde
call absbc
jr udiv16
mixdiv: call absde
call absbc
call udiv16
ret nc
call negbc
scf
ret
;
udiv16: ld a,d ;Divides BC by DE. Gives result in BC, remainder in HL.
or e
ret z ;Return NC if dividing by 0
ld hl,0
ld a,16
udiv1: scf
rl c
rl b
adc hl,hl
sbc hl,de
jr nc,udiv2
add hl,de
dec c
udiv2: dec a
jr nz,udiv1
scf
ret
;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;<< v0.03 overhauled
;
;
;Random number generator...
;
rmode: defb 0 ;0: pseudo-random 1:counting 1-n
rseed: defw 0
rlast: defw 0
;
;Seed with a randomish number
;
srandr: call ZXRNDI ;Get some randomish number
ld a,r
xor d
ld d,a ;DE = randomish number
ld (rseed),de
xor a
ld (rmode),a
ld (rlast),de
ret
;
;Seed with a fixed number (-HL)
;
srand: call abshl
ld (rseed),hl
ld de,1000
call cphlde
ld a,1
jr c,srand1
inc a
srand1: ld (rmode),a
ld hl,0
ld (rlast),hl
ret
;
random: bit 7,h ;IF HL negative, set predictable mode
jr nz,srand ;
ld a,h ;IF HL 0, seed from random number
or l
jr z,srandr
ld a,(rmode) ;Mode 1: Generate random number by stepping
dec a
jr z,steprnd
;
;Conventional pseudo-random-number generator
;
;Using the algorithm: ADD 1; multiply by 75; extract MOD 65537; subtract 1
; and 32-bit arithmetic
;
push hl ;HL = max value (0 < result <= HL)
ld hl,(rseed)
pred0: ld de,0 ;DEHL = value
;
;ADD 1 to DEHL.
;
inc hl
ld a,h
or l
jr nz,pred1
inc de
pred1:
;
;Multiply by 75. 75 = 64+8+2+1...
;
push de
push hl ;1xDEHL
call dblhde ;*2
push de
push hl ;2xDEHL
call dblhde ;*4
call dblhde ;*8
push de
push hl ;8xDEHL
call dblhde ;*16
call dblhde ;*32
call dblhde ;*64
call addpop ;+X*8 = X*72
call addpop ;+X*2 = X*74
call addpop ;+X = X*75
;
;Extract modulo 65537 (0x10001).
;
modlp: ld a,e
cp 1
jr c,end655 ;IF it's under 0x10000, it's under 0x10001
jr nz,sub655 ;IF it's above 0x1FFFF, it's above 0x10001
ld a,h ;0x10000 <= DEHL <= 0x1FFFF
or a
jr nz,sub655 ;IF it's above 0x100FF, it's above 0x10001
ld a,l ;0x10000 <= DEHL <= 0x100FF
cp 2
jr c,end655 ;IF it's <= 0x10001, return
;
;Subtract 65537 from DEHL...
;
sub655: dec de
dec hl
jr modlp
;
end655:
;
;Subtract 1
;
dec hl
ld (rseed),hl
ld b,h
ld c,l
;
;BC = number (16-bit). Reduce modulo parameter
;
res 7,b
pop de
call smod16
inc hl ;<<v0.03>> value is 0 < n <= max
; not 0 <=n < max
scf
ret ;Return value in HL.
steprnd:
push hl
ld bc,(rlast) ;Stepping 1,2, ... seed
inc bc
;
;BC = number candidate
;
ld de,(rseed)
call smod16 ;BC := BC mod (seed)
ld (rlast),hl
pop de
ld b,h
ld c,l
res 7,b
call smod16 ;Reduce mod parameter
inc hl ;0 < x <= param
scf
ret
;
;Double DEHL
;
dblhde: sla l
rl h
rl e
rl d
ret
;
addpop: pop bc ;return address
ld (apret+1),bc
pop bc ;To add to the low word
add hl,bc
pop bc ;To add to the high word
ex de,hl
adc hl,bc
ex de,hl
apret: jp 0
;
; >> v0.03