2019-04-15 01:11:13 +10:00
|
|
|
; core
|
|
|
|
;
|
|
|
|
; Routines used by pretty much all parts. You will want to include it first
|
|
|
|
; in your glue file.
|
|
|
|
|
|
|
|
; *** CONSTS ***
|
|
|
|
ASCII_CR .equ 0x0d
|
|
|
|
ASCII_LF .equ 0x0a
|
|
|
|
|
2019-04-24 03:29:16 +10:00
|
|
|
; *** DATA ***
|
|
|
|
; Useful data to point to, when a pointer is needed.
|
|
|
|
P_NULL: .db 0
|
|
|
|
|
2019-04-23 04:26:16 +10:00
|
|
|
; *** REGISTER FIDDLING ***
|
2019-04-15 01:11:13 +10:00
|
|
|
|
|
|
|
; add the value of A into DE
|
|
|
|
addDE:
|
2019-05-01 05:51:39 +10:00
|
|
|
push af
|
2019-04-15 01:11:13 +10:00
|
|
|
add a, e
|
|
|
|
jr nc, .end ; no carry? skip inc
|
|
|
|
inc d
|
|
|
|
.end:
|
|
|
|
ld e, a
|
2019-05-01 05:51:39 +10:00
|
|
|
pop af
|
2019-04-15 01:11:13 +10:00
|
|
|
ret
|
|
|
|
|
2019-04-16 05:08:00 +10:00
|
|
|
; copy (DE) into DE, little endian style (addresses in z80 are always have
|
|
|
|
; their LSB before their MSB)
|
|
|
|
intoDE:
|
|
|
|
push af
|
|
|
|
ld a, (de)
|
|
|
|
inc de
|
|
|
|
ex af, af'
|
|
|
|
ld a, (de)
|
|
|
|
ld d, a
|
|
|
|
ex af, af'
|
|
|
|
ld e, a
|
|
|
|
pop af
|
|
|
|
ret
|
|
|
|
|
2019-04-24 03:29:16 +10:00
|
|
|
intoHL:
|
|
|
|
push de
|
|
|
|
ex de, hl
|
|
|
|
call intoDE
|
|
|
|
ex de, hl
|
|
|
|
pop de
|
|
|
|
ret
|
|
|
|
|
2019-04-15 13:14:07 +10:00
|
|
|
; add the value of A into HL
|
|
|
|
addHL:
|
2019-05-01 05:51:39 +10:00
|
|
|
push af
|
2019-04-15 13:14:07 +10:00
|
|
|
add a, l
|
|
|
|
jr nc, .end ; no carry? skip inc
|
|
|
|
inc h
|
|
|
|
.end:
|
|
|
|
ld l, a
|
2019-05-01 05:51:39 +10:00
|
|
|
pop af
|
2019-04-15 13:14:07 +10:00
|
|
|
ret
|
|
|
|
|
2019-04-16 06:53:11 +10:00
|
|
|
|
|
|
|
; Write the contents of HL in (DE)
|
|
|
|
writeHLinDE:
|
|
|
|
push af
|
|
|
|
ld a, l
|
|
|
|
ld (de), a
|
|
|
|
inc de
|
|
|
|
ld a, h
|
|
|
|
ld (de), a
|
2019-04-24 09:42:10 +10:00
|
|
|
dec de
|
2019-04-16 06:53:11 +10:00
|
|
|
pop af
|
|
|
|
ret
|
|
|
|
|
2019-04-15 07:01:28 +10:00
|
|
|
; jump to the location pointed to by IX. This allows us to call IX instead of
|
|
|
|
; just jumping it. We use IX because we never use this for arguments.
|
|
|
|
callIX:
|
|
|
|
jp (ix)
|
|
|
|
ret
|
|
|
|
|
2019-04-24 05:50:26 +10:00
|
|
|
callIY:
|
|
|
|
jp (iy)
|
|
|
|
ret
|
|
|
|
|
2019-04-23 04:26:16 +10:00
|
|
|
; Ensures that Z is unset (more complicated than it sounds...)
|
|
|
|
unsetZ:
|
|
|
|
push bc
|
|
|
|
ld b, a
|
|
|
|
inc b
|
|
|
|
cp b
|
|
|
|
pop bc
|
|
|
|
ret
|
|
|
|
|
|
|
|
; *** STRINGS ***
|
|
|
|
|
2019-04-24 03:29:16 +10:00
|
|
|
; Fill B bytes at (HL) with A
|
|
|
|
fill:
|
|
|
|
push bc
|
|
|
|
push hl
|
|
|
|
.loop:
|
|
|
|
ld (hl), a
|
|
|
|
inc hl
|
|
|
|
djnz .loop
|
|
|
|
pop hl
|
|
|
|
pop bc
|
|
|
|
ret
|
|
|
|
|
2019-04-23 23:37:22 +10:00
|
|
|
; Increase HL until the memory address it points to is equal to A for a maximum
|
|
|
|
; of 0xff bytes. Returns the new HL value as well as the number of bytes
|
|
|
|
; iterated in A.
|
|
|
|
; If a null char is encountered before we find A, processing is stopped in the
|
|
|
|
; same way as if we found our char (so, we look for A *or* 0)
|
|
|
|
findchar:
|
2019-04-15 03:56:04 +10:00
|
|
|
push bc
|
2019-04-23 23:37:22 +10:00
|
|
|
ld c, a ; let's use C as our cp target
|
2019-04-15 03:56:04 +10:00
|
|
|
ld a, 0xff
|
|
|
|
ld b, a
|
|
|
|
|
|
|
|
.loop: ld a, (hl)
|
2019-04-23 23:37:22 +10:00
|
|
|
cp c
|
|
|
|
jr z, .end
|
2019-04-15 03:56:04 +10:00
|
|
|
cp 0
|
|
|
|
jr z, .end
|
|
|
|
inc hl
|
|
|
|
djnz .loop
|
|
|
|
.end:
|
|
|
|
; We ran 0xff-B loops. That's the result that goes in A.
|
|
|
|
ld a, 0xff
|
|
|
|
sub a, b
|
|
|
|
pop bc
|
|
|
|
ret
|
|
|
|
|
2019-04-15 01:11:13 +10:00
|
|
|
; Format the lower nibble of A into a hex char and stores the result in A.
|
|
|
|
fmtHex:
|
|
|
|
and a, 0xf
|
|
|
|
cp 10
|
|
|
|
jr nc, .alpha ; if >= 10, we have alpha
|
|
|
|
add a, '0'
|
|
|
|
ret
|
|
|
|
.alpha:
|
|
|
|
add a, 'A'-10
|
|
|
|
ret
|
|
|
|
|
|
|
|
; Formats value in A into a string hex pair. Stores it in the memory location
|
|
|
|
; that HL points to. Does *not* add a null char at the end.
|
|
|
|
fmtHexPair:
|
|
|
|
push af
|
|
|
|
|
|
|
|
; let's start with the rightmost char
|
|
|
|
inc hl
|
|
|
|
call fmtHex
|
|
|
|
ld (hl), a
|
|
|
|
|
|
|
|
; and now with the leftmost
|
|
|
|
dec hl
|
|
|
|
pop af
|
|
|
|
push af
|
|
|
|
and a, 0xf0
|
|
|
|
rra \ rra \ rra \ rra
|
|
|
|
call fmtHex
|
|
|
|
ld (hl), a
|
|
|
|
|
|
|
|
pop af
|
|
|
|
ret
|
|
|
|
|
|
|
|
; Parse the hex char at A and extract it's 0-15 numerical value. Put the result
|
|
|
|
; in A.
|
|
|
|
;
|
|
|
|
; On success, the carry flag is reset. On error, it is set.
|
|
|
|
parseHex:
|
|
|
|
; First, let's see if we have an easy 0-9 case
|
|
|
|
cp '0'
|
|
|
|
jr c, .error ; if < '0', we have a problem
|
|
|
|
cp '9'+1
|
|
|
|
jr nc, .alpha ; if >= '9'+1, we might have alpha
|
|
|
|
; We are in the 0-9 range
|
|
|
|
sub a, '0' ; C is clear
|
|
|
|
ret
|
|
|
|
|
|
|
|
.alpha:
|
|
|
|
call upcase
|
|
|
|
cp 'A'
|
|
|
|
jr c, .error ; if < 'A', we have a problem
|
|
|
|
cp 'F'+1
|
|
|
|
jr nc, .error ; if >= 'F', we have a problem
|
|
|
|
; We have alpha.
|
|
|
|
sub a, 'A'-10 ; C is clear
|
|
|
|
ret
|
|
|
|
|
|
|
|
.error:
|
|
|
|
scf
|
|
|
|
ret
|
|
|
|
|
|
|
|
; Parses 2 characters of the string pointed to by HL and returns the numerical
|
|
|
|
; value in A. If the second character is a "special" character (<0x21) we don't
|
|
|
|
; error out: the result will be the one from the first char only.
|
2019-04-15 13:14:07 +10:00
|
|
|
; HL is set to point to the last char of the pair.
|
2019-04-15 01:11:13 +10:00
|
|
|
;
|
|
|
|
; On success, the carry flag is reset. On error, it is set.
|
|
|
|
parseHexPair:
|
|
|
|
push bc
|
|
|
|
|
|
|
|
ld a, (hl)
|
|
|
|
call parseHex
|
|
|
|
jr c, .end ; error? goto end, keeping the C flag on
|
|
|
|
rla \ rla \ rla \ rla ; let's push this in MSB
|
|
|
|
ld b, a
|
|
|
|
inc hl
|
|
|
|
ld a, (hl)
|
2019-04-15 01:54:18 +10:00
|
|
|
cp 0x21
|
|
|
|
jr c, .single ; special char? single digit
|
2019-04-15 01:11:13 +10:00
|
|
|
call parseHex
|
|
|
|
jr c, .end ; error?
|
|
|
|
or b ; join left-shifted + new. we're done!
|
|
|
|
; C flag was set on parseHex and is necessarily clear at this point
|
|
|
|
jr .end
|
|
|
|
|
|
|
|
.single:
|
|
|
|
; If we have a single digit, our result is already stored in B, but
|
|
|
|
; we have to right-shift it back.
|
|
|
|
ld a, b
|
|
|
|
and a, 0xf0
|
|
|
|
rra \ rra \ rra \ rra
|
2019-04-15 13:14:07 +10:00
|
|
|
dec hl
|
2019-04-15 01:11:13 +10:00
|
|
|
|
|
|
|
.end:
|
2019-04-15 07:01:28 +10:00
|
|
|
pop bc
|
|
|
|
ret
|
|
|
|
|
2019-04-15 01:11:13 +10:00
|
|
|
; Compares strings pointed to by HL and DE up to A count of characters. If
|
|
|
|
; equal, Z is set. If not equal, Z is reset.
|
|
|
|
strncmp:
|
|
|
|
push bc
|
|
|
|
push hl
|
|
|
|
push de
|
|
|
|
|
|
|
|
ld b, a
|
|
|
|
.loop:
|
|
|
|
ld a, (de)
|
|
|
|
cp (hl)
|
2019-04-15 07:01:28 +10:00
|
|
|
jr nz, .end ; not equal? break early. NZ is carried out
|
|
|
|
; to the called
|
|
|
|
cp 0 ; If our chars are null, stop the cmp
|
|
|
|
jr z, .end ; The positive result will be carried to the
|
|
|
|
; caller
|
2019-04-15 01:11:13 +10:00
|
|
|
inc hl
|
|
|
|
inc de
|
|
|
|
djnz .loop
|
2019-04-15 07:01:28 +10:00
|
|
|
; We went through all chars with success, but our current Z flag is
|
|
|
|
; unset because of the cp 0. Let's do a dummy CP to set the Z flag.
|
|
|
|
cp a
|
2019-04-15 01:11:13 +10:00
|
|
|
|
|
|
|
.end:
|
|
|
|
pop de
|
|
|
|
pop hl
|
|
|
|
pop bc
|
|
|
|
; Because we don't call anything else than CP that modify the Z flag,
|
|
|
|
; our Z value will be that of the last cp (reset if we broke the loop
|
|
|
|
; early, set otherwise)
|
|
|
|
ret
|
|
|
|
|
|
|
|
; Transforms the character in A, if it's in the a-z range, into its upcase
|
|
|
|
; version.
|
|
|
|
upcase:
|
|
|
|
cp 'a'
|
|
|
|
ret c ; A < 'a'. nothing to do
|
|
|
|
cp 'z'+1
|
|
|
|
ret nc ; A >= 'z'+1. nothing to do
|
|
|
|
; 'a' - 'A' == 0x20
|
|
|
|
sub 0x20
|
|
|
|
ret
|
2019-04-15 03:56:04 +10:00
|
|
|
|