collapseos/kernel/core.asm

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