z80:Miscellaneous Routines
A list of small routines that can help you. Feel free to add your own.
Battery Status
Directly polls the hardware for battery status. Thanks to Steve Riekeberg.
;Name: BatteryStatus
;Description: Returns if the batteries are good, or low and need changing.
;Inputs: None
;Outputs: Non-Zero = Good Batteries
Zero = Batteries Low
;Destroys: A
GetBatteryStatus:
in a, (2)
and 01h
ret
Alternatively, save a byte and 3cc and have the output in the c flag (c means good, nc bad):
GetBatteryStatus:
in a,(2) \ rra \ ret
Boundary Check
Original: Adm. Wiggin. New: Zeda. Checks if coordinates is inside a designated box.
#define x 10
#define y 10
#define width 10
#define height 20
check:
;Input: a,b = x,y
;Output: c if the coords are in the box, nc if not.
;destorys af
;25cc or 47cc, 11 bytes
;compare too: 33cc~75cc, 20 bytes
sub x
sub width
ret nc
ld a,b
sub y
sub height
ret
Center Text
Displays a string centered on the screen (small font). Thanks to WikiGuru.
;input:hl points to string ;output: string displayed centered on screen ;destroyed: all registers ;other remarks: (penrow) must be set before calling this sub-routine ; first byte of string must be the length of the string centertxt: bcall(_SStringLength) ld a,96 ;width of screen sub b ;subtract width of string rra ;divide by 2 to be centered ld (pencol),a ld b,(hl) inc hl bcall(_VPutSN) ret ;to call: ... ld hl,0 ld (penrow),hl ld hl,txtTest call centertxt ... txtTest: .db 11,"Sample Text"
Direct Input
Used for direct input. See here to learn more about direct input. Thanks to Steve Riekeberg.
;Name: DirectInput
;Inputs: A = Key Group
;Outputs: A = Key Code; Zero If None
;Destroys: A, B
DirectInput:
ld b,a
ld a,$ff ;Reset the keypad
out (1),a
ld a,b
out (1),a
in a,(1)
ret
Hotspot
Checks if h>b, l>c, h<d, and l<e. Thanks to kermmartian and DarkerLine.
;----------Hot spot detection-----------
;inputs: b,c (first x and y cor)
; d,e (last x and y cor)
; h,l (current x,y)
;output: "c" flag [either true (set) or false (reset)]
hdetect:
ld a,h
cp b
ccf
ret nc
cp d
ret nc
ld a,l
cp c
ccf
ret nc
cp e
ret
On/Off Calc
Turns the calculator "off" and waits for user to press on. Note: Pulling batteries out will result in Ram Clear. Thanks to Taricorp.
;no inputs ld a,02h out (10h),a ;Turn off LCD ld a,08h out (3),a call LCD_Delay ld a,36h out (4),a ld a,01h out (3),a halt ;It halts until ON is pressed ld a,0Bh out (3),a ld a,03h out (10h),a ;Turn the LCD back on res onKey,(iy+keyFlags)
Find Next App
This routine is useful if you want to manually search through the installed apps on a calculator.
FindNextApp:
;Inputs:
; B is the base page of the app.
;Outputs:
; c flag set if the field was found
; nc means the app header subfield was not found
; B is the number of pages of the app
; A is the page of the next app
; H is the input page number
; L is the page number of the currently swapped in page in bank 1
;
;Comments:
; This code must run in RAM
in a,(6)
ld c,a
push bc ;b is the app page, c is the current page
ld a,b
out (6),a
call FindNumPages
pop hl ;h is the app page, l is the current page
ld b,a
ld a,l
out (6),a
ld a,h
ret nc
sub b
scf
ret
FindNumPages:
;Outputs:
; c flag set if the field was found
; nc means the app header subfield was not found
; A is the number of app pages
; B is 0
; (HL) is the number of app pages
ld hl,4000h
ld bc,128
ld a,c
or a
FNPLoop:
cpir
ret po
ret nz
inc a
cp (hl)
jr z,$+6
dec a
jp FNPLoop
inc l
ld a,(hl)
scf
ret
incFPtr
When reading through data in MemBank1, this routine is particularly useful as it adjusts the flash pages accordingly, as well as the pointer HL. If HL does not point to data in MemBank1 (4000h~7FFFh) then HL will simply be incremented, which is useful if the data might be in RAM or flash.
incFPtr:
;15cc or 24cc or 69cc, avg: ~15.03845cc
inc l \ ret nz
incptr:
inc h \ ret po
ld h,a
in a,(6) \ inc a \ out (6),a
ld a,h
ld h,40h
ret
It destroys no registers and is very efficient. However, if you are really in need of speed, such as with a ReadArc routine, you can try something like this (conditionally calling the routine):
inc l \ call z,incptr
SortA
This code is not as efficient as it could be, but it works. it sorts a bunch of bytes to be in ascending order:
SortA:
;Inputs:
; BC is the size
; HL is where the data is located
;Takes at least n iterations, at most n(n+1)/2
ld d,h
ld e,l
SortALoop:
push bc
push de
ld a,(hl)
call SwapBytes
SortLoop1:
jp po,NextSort
cp (hl)
cpi
jr c,SortLoop1
dec hl
call z,SwapBytes
jr c,SortLoop1
EndSwap:
pop de
jr SortALoop+1
NextSort:
pop hl
pop bc
dec hl
add hl,bc
or a
sbc hl,de
ret c
ret z
ld b,h
ld c,l
inc bc
ex de,hl
jp SortA+1
SwapBytes:
inc bc
push af
ld a,(de)
ldi
dec hl
ld (hl),a
inc hl
pop af
scf
ret
Set15MHz
This routine will set the calculator to 15MHz mode if possible:
Set15MHz:
;Ouputs:
; Sets the calculator to 15MHz if possible
; z flag set if the calc is at 6MHz, nz if at 15MHz
in a,(2)
sbc a,a
and 3
out (20h),a
ret
Set6MHz
This is pretty tiny (3 bytes) so it isn't worth making it a subroutine. In other words, just use it inline:
xor a out (20h),a