Integer arithmetic

The Z88 provides multiplication and division routines for 16 and 24 bit binary numbers as well as routines to convert between binary values and decimal numbers represented by ASCII digits. The arithmetic routines use the HL and DE registers (BHL and CDE for 24 bits) as input parameters with the result in HL (or BHL). In the case of division DE (or CDE) returns the remainder. The only error that is flagged is division by zero; Fc = 0 and A = RC_FAIL ($16). No overflow error is provided in the case of multiplication, so it is up to the user of these routines to check for overflow, if necessary.

GN_M16      unsigned 16bit multiplication
GN_D16      unsigned 16bit division
GN_M24      unsigned 24bit multiplication
GN_D24      unsigned 24bit division

The conversion routines are quite flexible with the ASCII source or destination being either in memory, at a stream or at a filter. The binary source or destination can either be the BC register pair (16 bits) or a 4 byte buffer in memory (32 bits). When converting from binary to ASCII you have four formatting options. Bits 4 to 7 of the Accumulator (A) define the width of the output field (where zero means as large as is necessary). Bits 1 and 2 of A are set to output a space before and after the decimal digits respectively. If bit 0 of A is set then instead of generating spaces in order to push the number to the right hand edge of the numeric field, zero digits are generated instead.

GN_Gdn      convert ASCII string to binary number
GN_Pdn      convert binary integer to an ASCII string

Example 

We now take an opportunity to present a more substantial example than before. The following code fragment accepts a number from the keyboard, converts it to binary, multiplies it by 10, converts back to ASCII decimal and finally outputs it to the screen. Included in this fragment is the memory allocation necessary to obtain an input buffer for the input line routine. Note that some static workspace has to be used here, to store the memory pool handle. This is because an error handler will need to be able to access this handle to close off the memory pool. An error handler associated with this piece of code needs to fetch the memory handle from static workspace and close the memory pool before exciting the application.

 
include "memory.def"            ; memory call definitions and parameters
include "stdio.def"             ; standard I/O call defs. & parameters
include "integer.def"           ; integer call definitions
include "errors.def"            ; error code calls and definitions

; The routine reads a number from the keyboard, multiplies it by 10 and 
; then outputs the answer 
; 
; The routine assumes an open memory pool already exists 
; and that its handle is in IX 
; It is further assumed the memory pool is associated with segment 2 
; (OS_Mop called with A = MM_S2) 
; 
; Note that because of the input line routine this code is pre-emptable. 
; An error handler would need to be able to access the open pool handle in 
; order to close the pool before before quitting - this can be done if the open 
; pool handle is stored in static workspace ; 

; first allocate memory

.main       call mall               ; allocate input buffer
            oz   GN_Nln             ; newline before input
            xor  a                  ; set options for GN_Sip (empty buffer)
; now get number from keyboard
            push iy
            pop  de                 ; DE = address of input buffer

.getline    ld   b, 6               ; buffer length
            oz   GN_Sip             ; get the ASCII integer
            jr   nc, cont1          ; if no errors then continue
            cp   RC_SUSP
            ret  nz                 ; exit if some other error

            ; reposition the cursor before calling GN_Sip again
            ld   a, 13
            oz   OS_Out             ; output a carriage return
            ld   a, 1               ; option for GN_Sip (buffer has contents)
            jr   getline            ; try again
; convert the ASCII number to integer and multiply by 10
.cont1      oz   GN_Nln             ; newline
            push iy
            pop  hl                 ; HL points at buffer address
            ld   de, 2              ; option for GN_Gdn to store result in BC
            ld   b, 6               ; don't attempt to read more than 6 chars.
            oz   GN_Gdn             ; convert from ASCII decimal to integer
            ld   d,b
            ld   e,c                ; result in DE
            ld   hl, 10             ; multiply DE by 10
            oz   GN_M16             ; multiply...
            ld   b,h
            ld   c,l                ; result in BC
; convert and output answer
            push iy
            pop  de                 ; DE points at buffer address
            xor  a                  ; set convert options (A = 0)
            oz   GN_Pdn             ; convert integer to ASCII decimal
                                    ; (convert HL to ASCII decimal at buffer)
            xor  a
            ld   (de),a             ; null-terminate string result
            push iy
            pop  hl                 ; HL points at start of buffer
            oz   GN_Sop             ; write result to standard output
            oz   GN_Nln
; now de-allocate memory
            push iy
            pop  hl                 ; address of allocated memory
            ld   c, MS_S2
            oz   OS_Mgb             ; get current bank binding at segment 2
            ld   a,b
            ld   bc, size           ; size of memory chunk
            oz   OS_Mfr             ; free memory (used for input buffer)
            ret
; subroutine to allocate memory
.mall       ld   bc, size           ; allocate a whole page (256 bytes)
            xor  a                  ; as per spec
            oz   OS_Mal             ; allocate memory
            oz   OS_Mpb             ; page the memory into segment 2
            push hl
            pop  iy                 ; IY points at address of allocated memory
            ret