Difference between revisions of "Z80:Miscellaneous Routines"

From Learn @ Cemetech
Jump to navigationJump to search
(Created page with "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. ;N...")
 
(formatting, hotspot,)
 
(4 intermediate revisions by 2 users not shown)
Line 17: Line 17:
 
       and 01h
 
       and 01h
 
       ret
 
       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 =
 
= Boundary Check =
 
+
Original: Adm. Wiggin.
Checks if coordinates is inside a designated box. Thanks to Adm. Wiggin.
+
New: Zeda.
 
+
Checks if coordinates is inside a designated box.
    ; Your box coords (will not work with 0, keep x1 < x2, and y1 < y2 [duh])
+
     #define x 10
     #define x1 10
+
     #define y 10
     #define y1 10
+
     #define width 10
     #define x2 20
+
     #define height 20
     #define y2 30
 
   
 
 
     check:
 
     check:
     ; a = x
+
     ;Input: a,b = x,y
    ; b = y
+
     ;Output: c if the coords are in the box, nc if not.
     ; returns c flag set if good
+
     ;destorys af
     ; returns c flag reset if no good (nc)
+
     ;25cc or 47cc, 11 bytes
     ; destorys af (quite obvious if one knows any z80 at all)
+
    ;compare too: 33cc~75cc, 20 bytes
      cp x1
+
       sub x
       jr c,nogood
+
       sub width
       cp x2+1
+
       ret nc
       jr nc,nogood
 
 
       ld a,b
 
       ld a,b
       cp y1
+
       sub y
       jr c,nogood
+
       sub height
      cp y2+1
 
      jr nc,nogood
 
    ;if it gets here, it's in the box!  Yay!  Hooray!  Peoples rejoice!
 
    ; as a side effect of the last operation, the carry flag is already set.
 
      ret
 
    nogood:
 
      or a;to reset the carry flag
 
 
       ret
 
       ret
 
  
 
= Center Text =
 
= Center Text =
Line 154: Line 144:
 
     out (10h),a ;Turn the LCD back on
 
     out (10h),a ;Turn the LCD back on
 
     res onKey,(iy+keyFlags)
 
     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
  
  

Latest revision as of 22:09, 31 May 2016

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