7cf3ed38da
Most of register fiddling routines (which is now the only thing contained in care.asm) are used by almost all userspace apps, often in inner loops. That makes the penalty of using jump tables for those a bit too high. Moreover, it burdens jump tables needlessly. Because this unit is very small (now that string routines are out), it makes sense to always include it in binaries.
106 lines
2.3 KiB
NASM
106 lines
2.3 KiB
NASM
; core
|
|
;
|
|
; Routines used pretty much all everywhere. Unlike all other kernel units,
|
|
; this unit is designed to be included directly by userspace apps, not accessed
|
|
; through jump tables. The reason for this is that jump tables are a little
|
|
; costly in terms of machine cycles and that these routines are not very costly
|
|
; in terms of binary space.
|
|
; Therefore, this unit has to stay small and tight because it's repeated both
|
|
; in the kernel and in userspace. It should also be exclusively for routines
|
|
; used in the kernel.
|
|
|
|
; add the value of A into DE
|
|
addDE:
|
|
push af
|
|
add a, e
|
|
jr nc, .end ; no carry? skip inc
|
|
inc d
|
|
.end:
|
|
ld e, a
|
|
pop af
|
|
noop: ; piggy backing on the first "ret" we have
|
|
ret
|
|
|
|
; add the value of A into HL
|
|
; affects carry flag according to the 16-bit addition, Z, S and P untouched.
|
|
addHL:
|
|
push de
|
|
ld d, 0
|
|
ld e, a
|
|
add hl, de
|
|
pop de
|
|
ret
|
|
|
|
|
|
; copy (HL) into DE, then exchange the two, utilising the optimised HL instructions.
|
|
; ld must be done little endian, so least significant byte first.
|
|
intoHL:
|
|
push de
|
|
ld e, (hl)
|
|
inc hl
|
|
ld d, (hl)
|
|
ex de, hl
|
|
pop de
|
|
ret
|
|
|
|
intoDE:
|
|
ex de, hl
|
|
call intoHL
|
|
ex de, hl ; de preserved by intoHL, so no push/pop needed
|
|
ret
|
|
|
|
intoIX:
|
|
push ix
|
|
ex (sp), hl ;swap hl with ix, on the stack
|
|
call intoHL
|
|
ex (sp), hl ;restore hl from stack
|
|
pop ix
|
|
ret
|
|
|
|
; Compare HL with DE and sets Z and C in the same way as a regular cp X where
|
|
; HL is A and DE is X.
|
|
cpHLDE:
|
|
push hl
|
|
or a ;reset carry flag
|
|
sbc hl, de ;There is no 'sub hl, de', so we must use sbc
|
|
pop hl
|
|
ret
|
|
|
|
; Write the contents of HL in (DE)
|
|
; de and hl are preserved, so no pushing/popping necessary
|
|
writeHLinDE:
|
|
ex de, hl
|
|
ld (hl), e
|
|
inc hl
|
|
ld (hl), d
|
|
dec hl
|
|
ex de, hl
|
|
ret
|
|
|
|
; Call the method (IX) is a pointer to. In other words, call intoIX before
|
|
; callIX
|
|
callIXI:
|
|
push ix
|
|
call intoIX
|
|
call callIX
|
|
pop ix
|
|
ret
|
|
|
|
; jump to the location pointed to by IX. This allows us to call IX instead of
|
|
; just jumping it. We use IX because we seldom use this for arguments.
|
|
callIX:
|
|
jp (ix)
|
|
|
|
callIY:
|
|
jp (iy)
|
|
|
|
; Ensures that Z is unset (more complicated than it sounds...)
|
|
; There are often better inline alternatives, either replacing rets with
|
|
; appropriate jmps, or if an 8 bit register is known to not be 0, an inc
|
|
; then a dec. If a is nonzero, 'or a' is optimal.
|
|
unsetZ:
|
|
or a ;if a nonzero, Z reset
|
|
ret nz
|
|
cp 1 ;if a is zero, Z reset
|
|
ret
|