Model I ROM Explained – Part 1

Page Customization:

Display OPCodes
Display Labels



These 3 pages are a breakdown of the Model I ROM, with comments to help you understand what is going on 0000H-0FFFFH / 1001H-2002H / 2003H-2FFFH. The two thing to keep in mind while reading this disassembly are:


  • The ROM had to be one big long program starting from 0000H and ending at 2FFFH and everything it does had to be wedged into that single long block of code. This means that jumps to other locations are a necessity because once a particular portion of the ROM executes, if it was allowed to keep going, unintended portions would also execute. With this, there is a LOT of jumping away.
  • There are only so many variables and only so many Z-80 instructions. There is no Z-80 instruction, for example, LD BC,SP. With this, there is a huge amount of variable swapping. I would dare say that whatever part of the ROM isn’t jumping, is just swapping variables around. The ROM would be a fraction of the size if there were more variables and more Z-80 instructions.

ROM Constants

The ROM was written by Bill Gates, Paul Allen, and M Davidoff on a PDP-10. Since the source code was cross-compiled, the authors put in some variables and constants

$CODE
0
Used by the authors when testing. When applied to the ROM itself, all references to $CODE are 0
CURCHR
95
Cursor character
STRSZD
50
Number of bytes reserved for the STRING area. This can be adjusted in BASIC by using the CLEAR nnnn function, which defaults to 50
DSEL$
37E1
Disk Drive Select Latch Address. Value of 01H is Drive :0
PRTAD$
37E8
Line Printer Address. If a parallel line printer is in use, it is mapped to this address
CSEL$
37E4
Cassette Select Latch Address. Values: 0-1 – SELECT TAPE DRIVE: 0=#1 1=#2
FDCAD$
37EC
Floppy Disk Controller COMMAND/STATUS
KEYAD$
3800
Keyboard Address Matrix
DSPAD$
3C00
Video RAM / Screen Addresses
CHARC
4099H
Last key pressed on the keyboard
CASIO$
255
The cassette I/O port
VALTYP
40AFH
Type variable flag for value in Work-Register-Area-1. Will be 2 for Integer, 3 for string, 4 for single precision, and 8 for double precision
DSCTMP
40D3H
VARPTR storage area for string currently being created by BASIC
CLMWID
14
Comma column width
DATPSC
256
Data bytes in a diskette sector
LINLN
80
Terminal maximum line length
LPTLEN
80 / 132
Printer maximum line width, Regular / LPT3
BUFLEN
72 / 255
Buffer size for INPUT / Long Lines
NAMLEN
40
Maximum filename length
NUMTMP
3
Number of String Temporaries
NUMLEV
0*20+19+2*5
Number of STACK Levels Reserved


0000H-0004H – POWER UP ROUTINE – “START”

0000-0002 Disables the interrupts, clears the A register, then jumps to initialisation routine at 674H
0000
 ↳ START
DIF3
Disables the interrupts and turns off clock
0001
XOR AAF
Clears the A Register and status
0002-0004
Go to the Level II BASIC initialization routine
0005-0007
JP 4000HJP RST1$C3 00 40
Go to RST 0008H code via 4000H
0008
 ↳ SYNTAX

(RST 008H)
Jumps to 4000H. 4000H passes control to 1C96H. This routine is used for scanning strings. It compares the character pointed to by the HL Register Pair with the character pointed to by the return address on the top of the STACK (Note that a RST instruction is in effect a CALL and places a return address on the STACK) formula: (HL)=((SP))? If they are not equal an SN ERROR will result; if they are equal then the return address on the STACK will be incremented to bypass the test character and control will be passed to RST 10H logic. RST 8H is used to look for expected characters in a string and then return with (HL) pointing to the next non-blank character. (see RST l0H) (BC and DE registers unaffected.). This routine can be used by CALLing 1C96H or RST 8H.
This is the COMPARE SYMBOL routine which comparess the symbol in the input string pointed to by HL Register to the value in the location following the RST 08 call. If there is a match, control is returned to address of the RST 08 instruction 2 with the next symbol in in Register A and HL incremented by one. If the two characters do not match, a syntax error message is given and control returns to the Input Phase)
0008 (RST 8H) Jumps to 4000H. 4000H passes control to 1C96H. This routine is used for scanning strings. It compares the character pointed to by the HL Register Pair with the character pointed to by the return address on the top of the STACK (Note that a RST instruction is in effect a CALL and places a return address on the STACK) formula: (HL)=((SP))? If they are not equal an SN ERROR will result; if they are equal then the return address on the STACK will be incremented to bypass the test character and control will be passed to RST 10H logic. RST 8H is used to look for expected characters in a string and then return with (HL) pointing to the next non-blank character. (see RST l0H) (BC and DE registers unaffected.). This routine can be used by CALLing 1C96H or RST 8H

000BH-000CH – DISK ROUTINE – “WHERE”

000B
 ↳ WHERE
POP HLE1
Get the address from the STACK and put it in Register Pair HL
000C
JP (HL)E9
Jump to the location of the address in Register Pair HL

000DH-000FH – DISK BOOTSTRAP – “$BOOT”

000D-000F
 ↳ $BOOT
Jump to the disk load and run sector routine at 069FH
0010 (RST 10H) jumps to 1D78H through 4003H. This routine INCrements HL and tests the characters pointed to by the HL Register Pair. It will bypass any spaces and CHAR’S 9 and 10 (shifted left and down arrows respectively). Upon return from this routine HL will point to the next non-blank character; the carry flag will be SET if HL is pointing to a numeric ASCII character and the Z flag will be SET if the character pointed to happens to be zero (ASCII 30H) or 3AH (“:”). (BC and DE registers are unaffected) This routine can be used by CALLing 1D78H or RST l0H

0010H-0012H – RST 10 – GET A CHARACTER FROM THE BUFFER

0010
(RST 010H)
Jumps to 1D78H (RST 0010H vector) through 4003H.

This routine INCrements HL and tests the characters pointed to by the HL Register Pair. It will bypass any spaces and CHAR’S 9 and 10 (shifted left and down arrows respectively). Upon return from this routine HL will point to the next non-blank character; the carry flag will be SET (C=1) if HL is pointing to a number and the Z flag will be SET if the character pointed to happens to be zero (ASCII 30H) or 3AH (“:”). The carry flag will be RESET (0) if the character is non-numeric. (BC and DE registers are unaffected) This routine can be used by CALLing 1D78H or RST l0H

0013H-0017H – INPUT ROUTINE – $GET

This routine Inputs a byte from an input device. When calling, DE = starting address of DCB of device. On exit, A = byte received from device, Z set if device ready. Uses AF.
0013
 ↳ $GET
PUSH BCC5
Save the value in Register Pair BC on the STACK
0014-0015
LD B,01H06 01
Load Register B with the device type entry code of 01H
0016-0017
Jump to the Level II BASIC driver entry routine at 0046H
0018 (RST 18H) Jumps to lC90H through 4006H. This routine can be called by using RST 18H or CALL lC90H. It compares two 16 bit values in HL and DE and sets the S and Z flags accordingly (they are set in the same way as for a normal 8 bit CP). All registers are unchanged except for A
0018
 ↳ RST18
Jumps to 1C90H through 4006H. This routine can be called by using RST 18H or CALL lC90H. It compares two 16 bit values in HL and DE and sets the S and Z flags accordingly (they are set in the same way as for a normal 8 bit CP). All registers are unchanged except for A.
This is the COMPARE DE:HL routine, which numerically compares DE and HL. Will not work for signed integers (except positive ones). Uses the A-register only. The result of the comparison is returned in the status Register as: CARRY SET=HL<DE; NO CARRY=HL>DE; NZ=Unequal; Z=Equal)

001BH-001EH – DRIVER ENTRY ROUTINE – Part 1 – “PUT”

This routine outputs a byte to a device. When calling, A = output byte, DE = starting address of DCB of device. On exit, Z set if device ready. Uses AF.
001B
 ↳ PUT
PUSH BCC5
Save the value in Register Pair BC on the STACK
001C-001D
LD B,02H06 02
Load Register B with the device type entry code of 02H
001E-001F
Jump to the Level II BASIC driver entry routine at 0046H
0020 (RST 20H) This routine jumps to 25D9H through 4009H. If the NTF=8 then C=RESET or else C=SET, Z flag will be SET if NTF=3 (S flag is valid also.). After execution of RST 20H or CALL 25D9H, A will contain the value NTF-3, all other registers are unchanged. (The NTF will be discussed in the arithmetic section.)
0020
(RST 020H)
This routine jumps to 25D9H through 4009H. If the NTF=8 then C=RESET or else C=SET, Z flag will be SET if NTF=3 (S flag is valid also.). After execution of RST 20H or CALL 25D9H, A will contain the value NTF-3, all other registers are unchanged.
Returns a combination of STATUS flags and unique numeric values in the A Register according to the data mode flag (40AFH). Integer = NZ/C/M/E and A is -1; String = Z/C/P/E and A is 0; Single Precision = NZ/C/P/O and A is 1; and Double Precision is NZ/NC/P/E and A is 5. This CALL is usually made to determine the type of the current value in the ACCumulator (i.e., 4121H-4122H). It should be used with caution, however since the mode flag and ACCumulator can get out of phase particularly if some of the CALLS described here are used to load ACCumulator

0023H-0027H – DISK ROUTINE – “$CTL”

0023
 ↳ $CTL
PUSH BCC5
Save the value in Register Pair BC on the STACK
0024-0025
LD B,04H06 04
Load Register B with the device type entry code of 04H
0026-0027
Jump to the Level II BASIC driver entry routine at 0046H
0028 (RST 28H) Jumps to 400CH which contains C9H (RET) under Level II BASIC. This vector is only used by Disk BASIC. It is called by the BREAK key routine, and can be used to intercept the BREAK key logic
0028
(RST 028H)
Jumps to 400CH which contains C9H (RET) under Level II BASIC. This vector is only used by Disk BASIC. It is called by the BREAK key routine, and can be used to intercept the BREAK key logic.
This is the DOS FUNCTION CALL routine at RST 28 (which passes request code in A-register to DOS for processing. Returns for non-disk system. For disk systems, the A Register must contain a legitimate DOS function code. If the code is positive, the CALL is ignored and control returns to the caller. Note that the DOS routine discards the return address stored on the STACK by the RST instruction. After processing control will be returned to the previous address on the STACK)

002BH-002FH – KEYBOARD ROUTINE – “$KBD”

Keyboard scanning routine. After CALLing 2BH, the A Register will contain the ASCII value for the key that was pressed. The A Register will contain 00H if no key was pressed at the time. Apart from the AF Register Pair the DE Register Pair is also used by the routine.
This Routine Performs an instantaneous scan of the keyboard. If no key is depressed control is returned to the caller with the A Register and status Register set to zero. If any key (except the BREAK key) is active the ASCII value for that character is returned in the A-register. If the BREAK key is active, a RST 28 with a system request code of 01 is executed. The RST instruction results in a JUMP to the DOS Exit 400C. On non-disk systems the Exit returns, on disk systems control is passed to SYS0 where the request code will be inspected and ignored, because system request codes must have bit 8 on. After inspection of the code, control is returned to the caller of 002B. Characters detected at 002B are not displayed. Uses DE, status, and A register

Of the 3 keyboard scanning routines, this is the most fundamental one. If no key is pressed when the CALL is executed, the code falls through with A = 00H. If you want to wait for a key to be pressed, you would use CALL 0049 or you would write a routine that jumps back to the call if A is 0.

This routine loads DE with address of keyboard DCB and scans keyboard. On exit, if no key pressed the A Register will contain a zero byte, else the character input from the keyboard wi 11 be returned in A. Character is not echoed to video. Uses AF,DE (to save DE use routine at 03588).

Scan Keyboard: Performs an instantaneous scan of the keyboard. If no key is depressed control is returned to the caller with in Register A and status Register set to zero. If any key (except the BREAK key) is active the ASCII value for that character is returned in the A-register. If the BREAK key is active, a RST 28 with a system request code of 01 is executed. The RST instruction results in a JUMP to the DOS Exit 400C. On non-disk Systems the Exit returns, on disk systems control is passed to SYS0 where the request code will be inspected and ignored, because system request codes must have bit 8 on. After inspection of the code, control is returned to the caller of 002B. Characters detected at 002B are not displayed. Uses DE, status, and A register
002B-002D
 ↳ $KBD
LD DE,4015HLD DE,KDCB$11 15 40
Load Register Pair DE with the starting address of the keyboard device control block.
Note: 4015H holds Keyboard DCB – Device type
002E-002F
Jump to the Level II BASIC driver entry routine
0030 (RST 30H) This location passes control to 400FH which contains a RET (C9H) under Level II. This location is only used by a Disk system
0030
(RST 030H)
This location passes control to 400FH which contains a RET (C9H) under Level II. This location is only used by a Disk system.
This is the LOAD DEBUG routine, and loads the DEBUG program and transfers control to it. When DEBUG processing is complete, control is returned to the orginal caller. For non-disk systems control is returned immediately

0033H-0037H – VIDEO ROUTINE – “$DSP”

Character print routine. A CALL 33H will print a character at the current cursor position. The A Register must contain the ASCII code for the character or graphics figure that is to be printed before CALLing this routine. The DE Register Pair is used by the routine.
A call to 0033H displays the character in the A-register on the video. Control codes are permitted. All registers are used.
0033-0035
 ↳ $DSP
LD DE,401DHLD DE,DDCB$11 1D 40
Load Register Pair DE with the starting address of the video display device control block.
Note: 401DH holds Video DCB – Device type
0036-0037
Jump to the Level II BASIC driver entry routine at 001BH
0038 (RST 38H) This location will pass control to 4012H. This location is only used by a Disk system
0038
(RST 038H)
This location will pass control to 4012H. This location is only used by a Disk system.
This is the INTERRUPT ENTRY POINT routine at RST 38H which is the system entry point for all interrupts. It contains a jump to a section of code in the Communications Region designed to field interrupts. That section of code consists of a DI (disables further interrupts) followed by a RET (returns to the point of interrupt) for non-disk systems, or a jump to an interrupt processor in SYSO if it is a DOS system. For DOS systems the interrupt handler consists of a task scheduler, where the exact cause of the interrupt is determined (usually a clock interrupt) and the next task from the task control block is executed. After task completion, control returns to the point of interrupt

003BH-003FH – PRINTER ROUTINE – “$PRT”

Character LPRINT routine. Same as 33H but outputs to line printer. (Contents of A Register will be printed).

A call to 003BH causes the character contained in the C-register to be sent to the printer. A line count is maintained by the driver in the DCB. When a full page has been printed (66 lines), the line count is reset and the status Register returned to the caller is set to zero. Control codes recognized by the printer driver are:
  • 00=Returns the printer status in the upper two bits of the A-register and sets the status as zero if not busy, and non-zero if busy.
  • OB=Unconditionally skips to the top of the next page.
  • OC=Resets the line count (DCB 4) and compares its previous value to the lines per page (DCB 3) value. If the line count was zero, no action is taken. If the line count was non-zero then a skip to the top form is performed.
  • OD=Line terminator. Causes line count to be incremented and tested for full page. Usually causes the printer to begin printing.

Character LPRINT routine. Same as 33H but outputs to line printer. (Contents of A Register will be printed.)
003B-003D
 ↳ $PRT
LD DE,4025HLD DE,PDCB$11 25 40
Load Register Pair DE with the starting address of the printer device control block.
Note: 4025H holds Printer DCB – Device type
003E-003F
Jump to the Level II BASIC printer driver entry routine

0040H-0042H – INPUT ROUTINE – “$KEYIN”

0040-0042
 ↳ $KEYIN
Jump to the “WAIT FOR NEXT LINE” keyboard input routine at 05D9 (which takes keyboard entry until a carriage return, a break, or buffer overrun occurs)
0043
RET
004400
NOP
0045
NOP

0046H-0048H – DRIVER ENTRY ROUTINE – Part 2 – “CIOJ”

0046-0048
 ↳ CIOJ
C3 C2 03
Jump to the Level II BASIC keyboard driver entry routine

0049H-004FH – KEYBOARD ROUTINE – “$KEY”

A call to 0049H returns as soon as any key on keyboard is pressed, exactly how the INKEY$ function works in BASIC. ASCII value for character entered is returned in A register. If you don’t want the program to hold while waiting for a key, you would use CALL 002BH instead.

Character input routine. This routine is the same as 2BH except that it will not return until a key is pressed, which often makes it often more useful than 2BH. Character is returned in the A Register (AF and DE used)

Wait For Keyboard Input: Returns as soon as any key on keyboard is pressed. ASCII value for character entered is returned in A- register. Uses A, status and DE registers
0049-004B
 ↳ $KEY
Go scan the keyboard and return with the key pressed, if any, in Register A.
A CALL to this memory location returns as soon as any key on keyboard is pressed. ASCII value for character entered is returned in A register. Uses A, status, and DE registers.

Character input routine. This routine is the same as 2BH (=Scan the Keyboard routine) except that it will not return until a key is pressed
004C
OR AB7
Check the value in Register A to see if a key was pressed
004D
RET NZC0
Return if a key was pressed
004E-004F
Loop until a key is pressed

0050H-005FH – KEYBOARD LOOKUP TABLE – “KEYTAB”

This is a table of control characters used by BASIC.
0050
 ↳ KEYTAB
0DH
ENTER (0DH)
0051
0DH
Shift ENTER (0DH)
0052
1FH
CLEAR (1FH)
0053
1FH
Shift CLEAR (1FH)
0054
01H
BREAK (01H)
0055
01H
Shift BREAK (01H)
0056
5BH
Up Arrow (5BH)
0057
1BH
Shift Up Arrow (1BH) aka Escape
0058
0AH
Down Arrow (0AH) aka Line Feed
0059
1AH
ROM before v1.3 – Shift Down Arrow (1AH); ROM v1.3 – NOP to permit Shift-Down-Arrow to act as a control key
005A
08H
Left Arrow (08H)
005B
18H
Shift Left Arrow (18H)
005C
09H
Right Arrow (09H)
005D
19H
Shift Right Arrow (19H)
005E
20H
SPACE (20H)
005F
20H
Shift SPACE (20H)

0060H-0065H – DELAY ROUTINE – “$PAUSE”

This is a delay loop. The BC Register Pair is used as the loop counter. The duration of the delay, in microseconds, is the value of BC times 14.66. Register A is used.
0060
 ↳ PAUSE
DEC BC0B
Decrement the counter in Register Pair BC
0061-0062
LD A,B
OR C78 B1
There is no way to COMPARE BC against anything, so the common “trick” is to load Register A with Register B and then OR it against Register C. If you do this, Register A can only be zero if both Registers B and C are zero
0063-0064
Loop until the counter in Register Pair BC is equal to zero
0065
RETC9
RETurn to CALLer
0066 is the location to which program control jumps when the RESET button is pressed (Non Maskable Interrupt address).5
0066-0068
 ↳ NMI
LD SP,0600HLD SP,$CODE + 3000 Octal31 00 06
Set the STACK pointer to 0600H.
This is the location to which program control jumps when the RESET button is pressed (Non Maskable Interrupt address)

0069H-0074H – NMI INTERRUPT ROUTINE (RESET) – “$INITIO”

This part of the initialization routine checks to see if a disk drive is connected. If so, it will jump to 00H. (This is why the reset button will reinitialize DOS.)
0069-006B
LD A,(37ECH)LD A,FDCAD$3A EC 37
Load Register A with the disk controller status (stored in 37ECH)
006C
INC A3C
Increment the disk controller status in Register A
006D-006E
CP 02HFE 02
Check the value in Register A to see if a disk is present. It is usually FFH if there is no expansion interface operating
006F-0071
If a disk is present, go to the Level II BASIC power up routine
0072-0074
Since we are without disk drives at this, this would be for power on or reset … so jump to the Level II BASIC READY routine at 06CCH

0075H-0104H – INITIALIZATION ROUTINE – “INIT2”

This is part of the Level II initialization procedure. It moves a block of memory from 18F7H to 191EH up to 4080H to 40A7H. (reserved RAM. area).>Note: 4080H-408DH is a division support routine.
0075-0077
LD DE,4080HLD DE,RAMLOW11 80 40
Load Register Pair DE with the ROM storage location of the Level II BASIC division routine.
Note: 4080H-408DH is a division support routine
0078-007A
LD HL,18F7HLD HL,CONSTR21 F7 18
Load Register Pair HL with the RAM storage location of the Level II BASIC division routine
007B-007D
LD BC,0027HLD BC,CNSLNR+101 27 00
Load Register Pair BC with the length of the Level II BASIC division routine (39 bytes)
007E-007F
LDIRED B0
Move the Level II BASIC division routine in ROM (18F7H-191DH) to RAM (4080H-40A6H)
0080-0082
LD HL,41E5HLD HL,BUFINI-321 E5 41
Continue with the communication region initialization by loading Register Pair HL with a RAM pointer to 41E5H
0083-0084
LD (HL),3AHLD (HL),”:”36 3A
Save a 3AH (which a “:”) at the location of the memory pointer in Register Pair HL (which is 41E5H)
0085
INC HL23
Increment the memory pointer in Register Pair HL from 41E5H to 41E6H
0086
LD (HL),B70
Zero out 41E6H (the location of the memory pointer in Register Pair HL)
0087
INC HL23
Increment the memory pointer in Register Pair HL from 41E6H to 41E7H
0088-0089
LD (HL),2CHLD (HL),”,”36 2C
Save a 2CH (which is a “,”) at 41E7H (the location of the memory pointer in Register Pair HL)
008A
INC HL23
Increment the memory pointer in Register Pair HL from 41E7H to 41E8H, which is the input/output buffer BUFINI
This loads 40A7H with the I/O buffer location address 41E8H. (40A7H is the I/O buffer pointer and can be changed to relocate the buffer.)
008B-008D
LD (40A7H),HLLD (BUFPNT),HL22 A7 40
This loads the input buffer pointer (held at 40A7H) with the keyboard buffer location address of 41E8H. (40A7H is the I/O buffer pointer and can be changed to relocate the buffer.). Save the value in Register Pair HL as the starting address of the keyboard input buffer area.
Note: 40A7H-40A8H holds the input Buffer pointer
008E-0090
LD DE,012DHLD DE,NAVERR11 2D 01
In prepartaion for a jump, load Register Pair DE with the starting address of the ?L3 ERROR routine

0091H-0104H – The rest of the initialization routine. First, it fills the RAM locations pointing to all 28 DOS BASIC commands, set them to pointo ?L3 ERROR, ask MEMORY SIZE ?, sets the memory pointers accordingly and prints RADIO SHACK LEVEL II BASIC , then it jumps to 1A19H which is the entry point for the BASIC command mode

The rest of the initialization routine. Asks MEMORY SIZE ?, sets the memory pointers accordingly and prints RADIO SHACK LEVEL II BASIC , then it jumps to lAl9H which is the entry point for the BASIC command mode
0091-0092
LD B,1CHLD B,ERCNT06 1C
Since there are 28 pre-defined DOS BASIC commands in ROM, load Register B with the number of times (=28) to save the jump to the ?L3 ERROR routine
0093-0095
LD HL,4152HLD HL,ERCALL21 52 41
Load Register Pair HL with the starting address of the Disk Basic links (which is 4152H) in preparation for generating an error if disk basic commands are attempted.
Note: 4152H-41A3H holds Disk Basic links
0096-0097
 ↳ ERLOPS
LD (HL),0C3H36 C3
Save a C3H (the first OPCODE for JP nnnn) to the first of every 3 byte instruction in the Disk Basic command list
0098
INC HL23
Increment the memory pointer in Register Pair HL to point to the 2nd of each 3 byte instruction in the Disk Basic command list
0099
LD (HL),E73
Save the LSB of the ?L3 ERROR routine’s starting address in Register E (i.e., a 2DH) to the 2nd of each 3 byte instruction in the Disk Basic command list
009A
INC HL23
Increment the memory pointer in Register Pair HL to the 3rd of each 3 byte instruction in the Disk Basic command list
009B
LD (HL),D72
Save the MSB of the ?L3 ERROR routine’s starting address in Register D (i.e., a 01H) to the 3rd of each 3 byte instruction in the Disk Basic command list
009C
INC HL23
Increment the memory pointer in Register Pair HL to the 1st byte of the next Disk Basic command in the list
009D-009E
Do this 28 times (=84 locations) until all of the Disk Basic links have been set to jump to the ?L3 ERROR routine
009F-00A0
LD B,15HLD B,RETCNT06 15
Load Register B with the number of DOS links to set to RETs. Note: HL is 41A6H at this point.. In the original ROM source, this was “LD B,RETCNT”
00A1-00A2
 ↳ LOPRTS
LD (HL),0C9H36 C9
Save a C9H to the location of the memory pointer in Register Pair HL fill it with RET instructions (C9H is RET)
00A3
INC HL23
Increment the memory pointer in Register Pair HL, as it is irrelevant what this memory location holds since RET is a single OPCODE
00A4
INC HL23
Increment the memory pointer in Register Pair HL, as it is irrelevant what this memory location holds since RET is a single OPCODE
00A5
INC HL23
Increment the memory pointer in Register Pair HL to point to the next error jump instruction
00A6-00A7
Loop from 4156H until all of the DOS links have been set to RETs

001BH-001FH – VIDEO AND PRINTER ROUTINE

00A8-00AA
LD HL,42E8HLD HL,ENBINI21 E8 42
Load Register Pair HL with the starting address of user RAM (which is 42E8H). In the original ROM source, this address was “ENBINI” although this is also defined as “TSTACK” elsewhere
00AB
LD (HL),B70
Zero the end of the buffer (i.e., 42E8H, the location of the memory pointer in Register Pair HL)
00AC-00AE
LD SP,41F8HLD SP,BUFINI+2031 F8 41
Set the current STACK pointer to 41F8H (which is 16888). In the original ROM source, this address was set as “BUFINI+20”
00AF-00B1
Go initialize the Level II BASIC variables and pointers
00B2-00B4
Call the CLEAR SCREEN routine at 01C9 (which clears the screen, changes to 64 characters, and homes the screen)
00B5-00B7
 ↳ MEMGET
LD HL,0105HLD HL,MEMMSG21 05 01
Load Register Pair HL with the starting address of the MEMORY SIZE? message in ROM. In the original ROM source, this address was set as “LD HL,MEMMSG”
00B8-00BA
Call the WRITE MESSAGE routine at 28A7H to print the message pointed to by HL.
NOTE:
  • The routine at 28A7 displays the message pointed to by HL on current system output device (usually video).
  • The string to be displayed must be terminated by a byte of machine zeros or a carriage return code 0D.
  • If terminated with a carriage return, control is returned to the caller after taking the DOS exit at 41D0H (JP 5B99H).
00BB-00BD
Print a “?” and get input from the keyboard
00BE-00BF
If the BREAK key was pressed, ask again. Note: 1BB3H jumps around A LOT but it is 0661H which processes a BREAK key, and starts by setting the carry flag
00C0
Since we now need to increment the input buffer pointer until it points to the first character of the input, call the EXAMINE NEXT SYMBOL routine at RST 10H.

The RST 10H routine parses the characters starting at HL+1 for the first non-SPACE,non-09H,non-0BH character it finds. On exit, Register A will hold that character, and the C FLAG is set if its alphabetic, and NC FLAG if its alphanumeric. All strings must have a 00H at the end.
00C1
OR AB7
Set the status flag based on if the character at the location of the input buffer pointer in Register A is an end of the input character (00H)
00C2-00C3
Jump forward to 00D6H if there was a response to the MEMORY SIZE? question
00C4-00C6
 ↳ MEMSIZ
LD HL,434CHLD HL,TSTACK+10021 4C 43
If just an ENTER was hit, need to figure it out dynamically, so load Register Pair HL with the starting address for the memory size check, which is 100 bytes (064H) after ENBINI
00C7
 ↳ LOOPMM
INC HL23
We are going to start testing RAM at 17229 (i.e., 434DH) toward 65535, so increment the memory pointer in Register Pair HL
00C8-00C9
LD A,H
OR L7C
There is no way to COMPARE HL against anything, so the common “trick” is to load Register A with Register H and then OR it against Register L. If you do this, Register A can only be zero if both Registers H and L are zero
00CA-00CB
Since we need to scan all the way up to 65535, jump to 00E7H (which drops the memory size pointer by 1) if the current memory pointer in Register Pair HL is equal to zero
00CC
LD A,(HL)7E
Load Register A with the value at the location of the current memory pointer in Register Pair HL
00CD
LD B,A47
Load Register B with the value in Register A to preserve it, as A is about to get used
00CE
CPL2F
Complement the value in Register A (which is basically a test pattern)
00CF
LD (HL),A77
Save the test pattern in Register A to the location of the current memory pointer in Register Pair HL
00D0
CP (HL)BE
Check to see if the value at the location of the memory pointer in Register Pair HL is the same as the value in Register A
00D1
LD (HL),B70
Put back the original memory value (which was saved in B) to the location of the memory pointed in Register Pair HL
00D2-00D3
If the address exists, loop back to 00C7H until the end of memory is found
00D4-00D5
If the address didn’t exist, jump to 00E7H (which goes to he next address and tries again)
00D6-00D8
 ↳ TYPMEM
Here the MEMORY SIZE? answer is in HL so call the ASCII TO INTEGER routine at 1E5AH (which will put the answer into DE in integer format).
NOTE:
  • The routine at 1E5A converts the ASCII string pointed to by HL to an integer deposited into DE. If the routine finds a non-numerica character, the conversion is stopped
00D9
OR AB7
Check to see if Register A is equal to zero
00DA-00DC
Display a ?SN ERROR if Register A is not equal to zero
00DD
EX DE,HLEB
Swap DE (where the integer version of the MEMORY SIZE? answer is located) and HL, so that Register Pair HL now has with the MEMORY SIZE answer again, but in integer format
00DE
DEC HL2B
Decrement the MEMORY SIZE? in Register Pair HL
00DF-00E0
LD A,8FH3E 8F
Load Register A with a memory test value of 8F or 10001111
00E1
LD B,(HL)46
Load Register B with the value at the location of the MEMORY SIZE? pointer in Register Pair HL (to save the data thats there)
00E2
LD (HL),A77
Put the test pattern (in A which is 8FH) into that the location of the MEMORY SIZE? pointer in Register Pair HL
00E3
CP (HL)BE
Check to see if the value in the memory location set in HL matches the test pattern in A
00E4
LD (HL),B70
Restore the old memory contents back
00E5-00E6
The test at MEMORY SIZE? -1 failed so we have to ask MEMORY SIZE again by jumping to 00B5H
00E7
 ↳ USEDEF
DEC HL2B
Decrement the memory size pointer in Register Pair HL, so it is the amount of memory – 2
00E8-00EA
LD DE,4414HLD DE,TSTACK+30011 14 44
Load Register Pair DE with the minimum MEMORY SIZE? response, which is 300 bytes (012CH) past ENBINI
00EB
Now we need to check to see if the MEMORY SIZE? pointer (in HL) is less than the minimum MEMORY SIZE? response (in DE), so we call the COMPARE DE:HL routine, which numerically compares DE and HL. Will not work for signed integers (except positive ones). Uses the A-register only. The result of the comparison is returned in the status Register as: CARRY SET=HL<DE; NO CARRY=HL>DE; NZ=Unequal; Z=Equal)
00EC-00EE
If C is set, then the amount of actual memory (in HL) is less than the minimum memory required (in DE), so we have to go to the Level II BASIC error routine and display an OM ERROR
00EF-00F1
 ↳ STRSZD
LD DE,FFCEHLD DE,65536-STRSZD11 CE FF
Load Register Pair DE with the default size of the string area (i.e., negative fifty)
00F2-00F4
LD (40B1H),HLLD (MEMSIZ),HL22 B1 40
Save the MEMORY SIZE? amount (which is in HL) to 40B1H (which holds the MEMORY SIZE? pointer)
00F5
ADD HL,DE19
Subtract the size of the string data (which was -50) from the highest memory address (stored in HE)
00F6-00F8
LD (40A0H),HLLD (STKTOP),HL22 A0 40
Save the start of string space pointer (which is now held Register Pair HL) to 40A0H.
Note: 40A0H-40A1H holds the start of string space pointer
00F9-00FB
Go initialize/reset the Level II BASIC variables and pointers
00FC-00FE
LD HL,0111HLD HL,HDGMSG21 11 01
Load Register Pair HL with the starting address of the RADIO SHACK LEVEL II BASIC message. 00FFH-0101H Go display the RADIO SHACK LEVEL II BASIC message
00FF-0101
We need to display the RADIO SHACK LEVEL II BASIC message so we call the WRITE MESSAGE routine at 28A7.
NOTE:
  • The routine at 28A7 displays the message pointed to by HL on current system output device (usually video).
  • The string to be displayed must be terminated by a byte of machine zeros or a carriage return code 0D.
  • If terminated with a carriage return, control is returned to the caller after taking the DOS exit at 41D0H (JP 5B99H).
0102-0104
Go to the Level II BASIC READY routine

0105H-0110H – MESSAGE STORAGE

The “MEMORY SIZE” message is located here
0105-0110
 ↳ MEMMSG
“MEMORY SIZE?” + 00H
0111-012C
“RADIO SHACK LEVEL II BASIC” + 0DH +00H
*0105H-010D
“MEM SIZE”+00H
In ROM 1.2, this shortend the “MEMORY SIZE”
*010E-011B
“R/S L2 BASIC” + 0DH + 00H
In ROM 1.2, this is shortend version
*011C
PUSH BC
*In ROM v1.2 this is the debounce routine. First, Put the Row Address in BC into the STACK so BC can be used for the debounce routine
*011D-011F
LD BC,0500H
Put 0500H into BC (which would be the debounce delay)
*0120-0122
CALL 0060HCALL PAUSE
Call the delay at 0060H based on the delay count in BC (which is 7.33 milliseconds)
*0123
POP BC
Restore the Row Address back to BC
*0124
LD A,(BC)
Reload the original flags from active row
*0125
AND E
Combine the current flag lists with the original flag bits
*0126
RET Z
Return to caller if zero because the row was not active on the second test
*0127
LD A,D
Otherwise, we have a legitimate active row
*0128
RCLA
Multiply the row index by 2. (Note: This was needed because a RCLA was sacrificed at the call location to give up the bytes needed for the call)
*0129
RCLA
Multiply the row index by 2 again. (Note: This was needed because a RCLA was sacrificed at the call location to give up the bytes needed for the call)
*012A-012C
JP 03FEH
Resume the keyboard driver routine

012DH-0131H – ?L3 ERROR ROUTINE – “NAVERR”

012D-012E
 ↳ NAVERR
LD E,2CHLD E,ERRNAV1E 2C
Load Register E with the ?L3 ERROR code of 2CH
012F-0131
Go to the Level II BASIC error routine with 2CH loaded into Register E

0132H-0134H – LEVEL II BASIC POINT COMMAND ENTRY POINT – “GRPHCS” or “POINT”

0132, 0135, 0138 These are the entry points for the POINT, SET and the RESET commands in that order, see Part 2 for more data on the graphics routines
0132
 ↳ GRPHCS
Since we need to bump the current BASIC program pointer until it points to the next character, call the EXAMINE NEXT SYMBOL routine at RST 10H.

The RST 10H routine parses the characters starting at HL+1 for the first non-SPACE,non-09H,non-0BH character it finds. On exit, Register A will hold that character, and the C FLAG is set if its alphabetic, and NC FLAG if its alphanumeric. All strings must have a 00H at the end.
0133
XOR AAF
A will wind up being 0 if the POINT command was entered … otherwise
0134
LD BC,803EH01 3E 80
Z-80 Trick! The byte at this memory location, 01H, is there to turn the real instruction that follows (the operative action of the SET command) into a harmless LD BC,xxxx. This way, they didn’t have to jump over SET or RESET to get to the common graphics code. If parsing straight down, this loads BC with 0380H and then moves to 0136H. But if jump straight to 0136H, you skip that 01H opcode, and get a real instruciton of 3EH 80H

0135H-0137H – LEVEL II BASIC SET COMMAND – “SET”

0135-0136
 ↳ SET
LD A,80H3E 80
Load Register A with 80H (which is 128) which is SET
0136
ADD A,B80
Z-80 Trick – See the note at 0134H for an explanation

0138H-0139H – LEVEL II BASIC RESET COMMAND ENTRY POINT – “RESET”

0138-0139
 ↳ RESET
LD A,01H3E 01
Load Register A with 01H which is RESET

013AH-019CH GRAPHICS ROUTINE

Common code for SET/RESET/POINT – A will be 0 if POINT, 80H if SET and 1 for RESET.
013A
PUSH AFF5
Save the flag which indicates which command was requested (held in Register A) to the STACK
013B-013C
RST 08H 28HSYNCHK “(“CF
Since SET/RESET/POINT all need a “(” to start with, call the COMPARE SYMBOL routine which comparess the symbol in the input string pointed to by HL Register to the value in the location following the RST 08 call. If there is a match, control is returned to address of the RST 08 instruction 2 with the next symbol in in Register A and HL incremented by one. If the two characters do not match, a syntax error message is given and control returns to the Input Phase)
013D-013F
CALL 2B1CHCALL GETBYTCD 1C 2B
Go evaluate the expression at the location of the current BASIC program pointer in Register Pair HL (which is the X variable) and return with the 8-bit value in Register A
0140-0141
CP 80HFE 80
Check to see if the X value in Register A is greater than 128
0142-0144
If A is greater than 128, go to 1E4AH to display a ?FC ERROR
0145
PUSH AFF5
Save the requested coordinate’s X value in Register A on the STACK
0146-0147
RST 08H 2CHSYNCHK “,”CF
At this point we have SET/RESET/POINT, an open parenthesis, and an X variable, so now we must find a ,. To do this call the COMPARE SYMBOL routine which comparess the symbol in the input string pointed to by HL Register to the value in the location following the RST 08 call. If there is a match, control is returned to address of the RST 08 instruction 2 with the next symbol in in Register A and HL incremented by one. If the two characters do not match, a syntax error message is given and control returns to the Input Phase)
0148-014A
Go evaluate the expression at the location of the current BASIC program pointer in Register Pair HL (which is the Y variable) and return with the 8-bit value in Register A
014B-014C
CP 30HFE 30
Check to see if the Y value in Register A is greater than 48
014D-014F
If the Y value is greater than 48, go to 1E4AH to display a ?FC ERROR
This is a suitable entry point for the graphics routines. (see Part 2)
0150-0151
LD D,0FFH16 FF
Prepare to divide y coordinate by 3 … load Register D with starting quotient
0152
 ↳ LOPMD3
INC D14
Increment the quotient in Register D
0153-0154
SUB 03HD6 03
Divide by subtraction; in this case subtract 3 from Register A
0155-0156
Loop until Register D equals the Y value divided by 3
0157-0158
ADD 03HC6 03
Make the remainder positive by adjust the remainder in Register A by adding back 3
0159
LD C,A4F
Save the remainder in Register C
015A
POP AFF1
Get the requested coordindate’s X value from the STACK and put it in Register A
015B
ADD A,A87
Multiply the X value in Register A by two, storing the result in Register A
015C
LD E,A5F
Load Register E with the newly doubled Register A
015D-015E
LD B,02H06 02
Load Register B with the number of times to shift Register Pair DE right (which is 2)
015F
 ↳ SHFTW
LD A,D7A
Load Register A with the adjusted Y value in Register D
0160
RRA1F
Divide the adjusted Y value in Register A by two. RRA rotates the contents of Register A right one bit position, with Bit 0 going to the CARRY FLAG, and the CARRY FLAG going to Bit 7. RRA also can be used to divide a number in 2.
0161
LD D,A57
Save the new Y value in Register A in Register D
0162
LD A,E7B
Load Register A with the adjusted X value in Register E
0163
RRA1F
Divide the adjusted X value in Register A by two. RRA rotates the contents of Register A right one bit position, with Bit 0 going to the CARRY FLAG, and the CARRY FLAG going to Bit 7. RRA also can be used to divide a number in 2.
0164
LD E,A5F
Load Register E with the new X value in Register A
0165-0166
Loop until the memory offset in Register Pair DE has been figured
0167
LD A,C79
Now we need to computer the position of the point so load Register A with the value in Register C
0168
ADC A,A8F
Multiply the value in Register A by two and add the value of the Carry flag to Register A
0169
INC A3C
Increment the value in Register A
016A
LD B,A47
Save the bit position in Register A in Register B
016B
XOR AAF
Zero Register A and reset the Carry flag
016C
SCF37
Set the Carry flag
016D
 ↳ PWR2
ADC A,A8F
Add the value of the Carry flag to Register A
016E-016F
Loop back to the prior instruction until the graphic mask has been completed in Register A
0170
LD C,A4F
Save the graphic mask in Register A in Register C
0171
LD A,D7A
Load Register A with the MSB of the video memory offset in Register D
0172-0173
OR 3CHOR 0011 1100F6 3C
Mask the MSB of the video memory offset in Register A with 0011 1100 so that it will point to the correct location in video RAM (i.e., the applicable screen RAM address)
0174
LD D,A57
Save the MSB of the video memory pointer in Register A in Register D
0175
LD A,(DE)1A
Load Register A with the character at the location of the video memory pointer in Register Pair DE
0176
OR AB7
Check to see if the character in Register A is a graphic character
0177-0179
Skip over the next instruction if the character in Register A is a graphic character
017A-017B
LD A,80H3E 80
Since the character at the screen location turned out not to be a graphics character, we need to set it to a blank graphics character, so load Register A with a blank graphic character which is CHR$(128)
017C
 ↳ FND4
LD B,A47
Save the character which is being modified by the SET/RESET (held in Register A) into Register B
017D
POP AFF1
Get the graphic character and the flags from the STACK and put it in Register A
017E
OR AB7
Set the flags according to the graphic mode in Register A
017F
LD A,B78
Get the existing graphic character on the screen (held in Register B) and put it in Register A
0180-0181
Jump forward to 0192H if the graphic mode is POINT
0182
LD (DE),A12
Save the graphic character in Register A at the location of the video memory pointer in Register Pair DE
0183-0185
Jump forward to 018FH if the graphic mode is SET
0186
LD A,C79
Load Register A with the graphic mask in Register C
0187
CPL2F
Reverse the graphic mask in Register A
0188
LD C,A4F
Load Register C with the adjusted graphic mask in Register A
0189
LD A,(DE)1A
Load Register A with the character at the location of the video memory pointer in Register Pair DE
018A
AND CA1
RESET the graphic bit by combining the graphic mask in Register C with the graphic character in Register A
018B
 ↳ FINSTB
LD (DE),A12
Save the adjusted graphic character in Register A at the location of the video memory pointer in Register Pair DE
018C-018D
 ↳ FINPTB
RST 08H 29HSYNCHK “)”CF
Check the syntax. The character at the location of the current BASIC program pointer in Register Pair HL must be a ) character (“)” is 29H)
018E
RETC9
RETurn to CALLer
018F
 ↳ SBIT
OR CB1
SET the graphic bit by combining the graphic mask in Register C with the graphic character in Register A
0190-0191
Jump back a few bytes to 018BH
0192
 ↳ TBIT
AND CA1
POINT the graphic bit by combining the graphic mask in Register C with the graphic character in Register A
0193-0194
ADD 0FFHADD -1C6 FF
Subtract one from the value in Register A
0195
SBC A,A9F
Adjust the value in Register A so that A will equal zero if the bit was off in Register A
0196
PUSH HLE5
Save the current BASIC program pointer in Register Pair HL on the STACK
0197-0199
Save the value in Register A as the current result in the ACCumulator (i.e., 4121H-4122H)
019A
POP HLE1
Get the current BASIC program pointer from the STACK and put it in Register Pair HL
019B-019C
Jump to 018CH

019DH-01C8H – LEVEL II BASIC INKEY$ ROUTINE – “INKEY”

019D
 ↳ INKEY
Since we need to bump the current BASIC program pointer in Register Pair HL until it points to the next character, call the EXAMINE NEXT SYMBOL routine at RST 10H.

The RST 10H routine parses the characters starting at HL+1 for the first non-SPACE,non-09H,non-0BH character it finds. On exit, Register A will hold that character, and the C FLAG is set if its alphabetic, and NC FLAG if its alphanumeric. All strings must have a 00H at the end.
019E
PUSH HLE5
Save the current BASIC program pointer in Register Pair HL on the STACK
019F-01A1
LD A,(4099H)LD A,(CHARC)3A 99 40
Put the last key pressed (stored at 4099H) and put it in Register A
01A2
OR AB7
Set the status flags
01A3-01A4
Jump to 01ABH (to skip the next character scan) if a key has been pressed
01A5-01A7
 ↳ MRCHRI
Go scan the keyboard
01A8
OR AB7
Set the status flags
01A9-01AA
Jump to 01BCH if a key wasn’t pressed
01AB
 ↳ BUFCIN
PUSH AFF5
Save the key pressed (held in Register A) to the top of STACK
01AC
XOR AAF
Clear the buffered character by zeroing Register A
01AD-01AF
LD (4099H),ALD (CHARC),A32 99 40
Save the value in Register A as the last key pressed (which is kept at 16537).
Note: 4099H holds the last key pressed
01B0
INC A3C
Increment the value in Register A (which is going to represet the size of the character string to be built)
01B1-01B3
Make sure there is room 1 byte of space in the string space RAM area by calling 2857H to make an entry in string space
01B4
POP AFF1
Get the last key pressed from the STACK and put it in Register A
01B5-01B7
LD HL,(40D4H)LD HL,(DSCTMP+1)2A D4 40
Load Register Pair HL with 2nd byte of the VARPTR for the string being created (which starts at 40D3H)
01B8
LD (HL),A77
Save the last key pressed in Register A at the location of the string pointer in Register Pair HL
01B9-01BB
Go save the string’s VARPTR as the current result
01BC-01BE
 ↳ NULRT
LD HL,1928HLD HL,REDDY-121 28 19
Load Register Pair HL with the starting address of the “READY” message (which is 6440)
01BF-01C1
LD (4121H),HLLD (FACLO),HL22 21 41
Save the address in Register Pair HL as the current result in the ACCumulator (i.e., 4121H-4122H)
01C2-01C3
LD A,03H3E 03
Load Register A with a string number type flag
01C4-01C6
LD (40AFH),ALD (VALTYP),A32 AF 40
Save the value in Register A as the current number type (which is at 16559).
Note: 40AFH holds current variable’s type flag
01C7
POP HLE1
Get the current BASIC program pointer from the STACK and put it in Register Pair HL
01C8
RETC9
RETurn to CALLer

01C9H-01D2H – LEVEL II BASIC CLS ROUTINE – “CLS”

A CALL 1C9H will clear the screen, select 64 characters and home the cursor. All registers are used.

To use a ROM call to clear the screen, CALL 01C9H. The cursor is reset to the home position, which is 3C00H.
01C9-01CA
 ↳ CLS
LD A,1CH3E 1C
Load Register A with the ASCII character to home the cursor
01CB-01CD
Go send the character in Register A (i.e., the ASCII character to home the cursor) to the video display
01CE-01CF
LD A,1FH3E 1F
Load Register A with the ASCII character to clear to the end of the screen
01D0-01D2
Go send the character in Register A (i.e., the ASCII character to clear to the end of the screen) to the video display

01D3H-01D8H – LEVEL II BASIC RANDOM ROUTINE – “RANDOM”

This is part of the RANDOM routine which takes a value out of the REFRESH register, stores it in location 40ABH and then returns.

A call to 01D3H reseeds the random number seed (location 40AB) with the current contents of the refresh register.

NOTE: To run a RANDOM (seed the random number generator) via a ROM call just call CALL 01D3H. This causes the contents of R (memory refresh) to be stored in 40ABH. The entire 24 bit seed is stored in 40AAH-40ACH.
01D3
 ↳ RANDOM
LD A,RED 5F
Load Register A with the current value of the refresh register
01D5-01D7
LD (40ABH),ALD (RNDX+1),A32 AB 40
Save the pseudi-random value in Register A to 40ABH (the random number seed)
01D8
RETC9
RETurn to CALLer

01D9H-01F7H – CASSETTE ROUTINE

Output a pulse to the cassette recorder
01D9-01DB
 ↳ CTPULS
LD HL,0FC01HLD HL,<252*256>+121 01 FC
Load Register Pair HL with the command to send to the cassette … here, a top of pulse
01DC-01DE
Go send the command to the cassette controller
Delay loop between pulses
01DF-01E0
LD B,0BH06 0B
Load Register B with the delay count of 80 milliseconds
01E1-01E2
 ↳ CT1
Loop for delay
01E3-01E5
LD HL,0FC02HLD HL,<252*256>+221 02 FC
Load Register Pair HL with the command to send to the cassette … here, a bottom of pulse
01E6-01E8
Go send the command to the cassette controller
01E9-01EA
LD B,0BH06 0B
Load Register B with the delay count of 80 milliseconds
01EB-01EC
 ↳ CT2
Loop for delay
01ED-01EF
LD HL,0FC00HLD HL,H,<252*256>21 00 FC
Load Register Pair HL with the command to send to the cassette
01F0-01F2
Go send the command to the cassette controller … here, a terminate pulse
01F3-01F4
LD B,5CH06 5C
Load Register B with the delay count of 671 milliseconds
01F5-01F6
 ↳ CT3
Loop for delay
01F7
RETC9
RETurn to CALLer

01F8H-01FDH – CASSETTE ROUTINE (TURN OFF CASSETTE) – “CTOFF”

01F8
 ↳ CTOFF
PUSH HLE5
Save the value in Register Pair HL on the STACK
01F9-01FB
LD HL,FB00HLD HL,<251*256>21 00 FB
Load Register Pair HL with the command to send to the cassette to turn it off
01FC-01FD
Jump to the cassette driver routine at 0219H

01FEH-0211H- CASSETTE ROUTINE (EVALUATE DRIVE NUMBER) – “CTON”

01FE
 ↳ CTON
LD A,(HL)7E
Get the character at the location of the current BASIC program pointer and put it in Register A
01FF-0200
SUB 23HSUB “#”D6 23
Check to see if the character in Register A is a # character
0201-0202
LD A,00H3E 00
Zero Register A
0203-0204
Jump to the TURN ON MOTOR routine at 0212H if the character at the location of the current BASIC program pointer in Register Pair HL isn’t a # character
0205-0207
Go evaluate the drive number at the location of the current BASIC program pointer in Register Pair HL and return with the drive number in Register E
0208-0209
RST 08H 2CHSYNCHK “,”CF
We need to see if the character at the location of the current BASIC program pointer in Register Pair HL is a ,, so call the RST 08H routine to do so
020A
LD A,E7B
Need to convert from the negative number to positive so, load Register A with the drive number in Register E .
020B
AND DA2
… and combine the MSB of the drive number in Register D with the LSB of the drive number in Register A and then .
020C-020D
ADD 02HC6 02
… add 2 to make the drive number in Register A positive
020E-0210
If the drive number in Register A is invalid, jump to 14E4H to display a FC ERROR
0211
DEC A3D
Decrement the drive number in Register A

0212H-021DH – CASSETTE ROUTINE (TURN ON CASSETTE)
“DEFDRV”

CALL 212H will select the cassette unit specified in A-Register and start the motor. Units are numbered from one. Put 00H in A Register to turn on cassette 1, or O1H to turn on cassette 2. All registers are used.

To use a ROM call to turn on the cassette, execute the following instructions: LD A,0 and then CALL 0212H
0212-0214
 ↳ DEFDRV
LD (37E4H),A32 E4 37
Set the current drive as specified by Register A
0215
PUSH HLE5
Save the current BASIC program pointer in Register Pair HL on the STACK so we can use HL for the next instruction
0216-0218
LD HL,0FF04HH,<255*256>+421 04 FF
Load Register Pair HL with the command to turn on the cassette motor
0219-021B
 ↳ CTCHG2
Go send the “turn on the cassette motor” command stored in HL to the cassette controller
021C
POP HLE1
Get the current BASIC program pointer from the STACK and put it in Register Pair HL
021D
RETC9
RETurn to CALLer

021EH-022BH – CASSETTE ROUTINE – “CTSTAT”

021E-0220
 ↳ CTSTAT
LD HL,0FF00HLD HL,<255*256>21 00 FF
Load Register Pair HL with the mask to preserve video controller flags, but otherwise clear the cassette latch
0221-0223
 ↳ CTCHG
LD A,(403DH)LD A,(CAST$)3A 3D 40
Load Register A with the contents of 403DH, which contains, among other things, the screen resolution (32 or 64 wide; Bit 3) the tape relay on/off instruction (Bit 2) and the positive/negative audio pulses (Bits 0-1).
Note: 403DH-4040H is used by DOS
0224
AND HA4
Combine the value in Register H with the contents of (403DH)
0225
OR LB5
Combine the value to send to the cassette in Register L with the adjusted value in Register A
0226-0227
OUT (0FFH),AOUT (CASIO$),AD3 FF
Send the value in Register A to port 255 (which is the cassette and video port)
0228-022A
LD (403DH),ALD (CAST$),A32 3D 40
Save the value in Register A into (403DH).
Note: 403DH-4040H is used by DOS
022B
RETC9
RETurn to CALLer

022CH-0234H – CASSETTE ROUTINE (BLINK **) – “BCASIN”

Alternately displays and clears an asterisk in the upper right hand comer. Uses all registers.
022C-022E
 ↳ BCASIN
LD A,(3C3FH)3A 3F 3C
Get the character being displayed in the upper right hand corner of the video display from 3C3FH and put that character in Register A
022F-0230
XOR 0AHEE 0A
If the character in Register A is a * then make it a SPACE, else if the character in Register A is a SPACE make it a *
0231-0233
LD (3C3FH),A32 3F 3C
Display the character in Register A in the upper right hand comer of the video display
0234
RETC9
RETurn to CALLer

0235H-0240H – CASSETTE ROUTINE (READ A BYTE) – “CASIN”

Read One Byte: Reads one byte from the currently selected unit. The byte read is returned in the A-register. All other registers are preserved

This routine will read a byte from tape. A CALL 235H will return with the byte read from tape in the A Register BC, DE and HL are unchanged

To use a ROM call to read a character from cassette (after the cassette has been turned on and leader and sync have been found), CALL 0235H. The input character will be in the A register. Again, the routine at 0235H must be called frequently enough to sustain the 500 baud rate if more than one character is to be read.
0235
CASIN
PUSH BCC5
Save the value in Register Pair BC on the STACK
0236
PUSH HLE5
Save the value in Register Pair HL on the STACK
0237-0238
LD B,08H06 08
Load Register B (which is what DJNZ decrements to loop) with the number of bits to read (which is 8)
0239-023B
CTB0
Go read a bit from the cassette recorder (the resulting byte from the 8 bits will be accumulated into the A register)
023C-023D
Loop that instruction until all eight bits have been read into A
023E
POP HLE1
Get the value from the STACK and put it in Register Pair HL
023F
POP BCC1
Get the value from the STACK and put it in Register Pair BC
0240
RETC9
RETurn to CALLer

0241H-0260H – CASSETTE ROUTINE (READ A BIT) – “CTBIT”

Routine waits for timing pulse, and then performs a timing loop. When the time is up it tests the tape for a bit, which will be “1” if present and “0” if not. A CALL 241H is used by 235H eight times to input one byte.

0264 Writes the byte in the A Register to tape. BC, DE and HL are unchanged by a CALL 264H
0241
 ↳ CTBIT
PUSH BCC5
Save the value in Register Pair BC on the STACK
0242
PUSH AFF5
Save the value in Register A on the STACK
0243-0244
CB0
IN A,(0FFH)IN A,(CASIO$)DB FF
Read a bit from the cassette port (waiting for a clock pulse)
0245
RLA17
Rotate Register A left one bit position with the contents of bit 7 copied to the carry flag. By doing this, the start bit would rotate to the carry flag for easy testing
0246-0247
If NC is set, we do not yet have the start bit, so loop back to 0243H until the start bit is found
0248-0249
LD B,40H06 41
No that we have the start bit we need a delay, so load Register B with the delay count of 40H
*0248H-0249
LD B,60H
In ROM v1.2, load B with a delay count of 60H (=476/703 microseconds) instead of 40H
024A-024B
CB1
DJNZ 024AHDJNZ CB110 FE
Loop (don’t jump unless B is zero; with a decrement of B each time) for delay
024C-024E
CALL 021EHCALL CTSTATCD 1E 02
Go clear the cassette controller so we can get to reading
024F-0250
LD B,76H06 76
We need a delay so load Register B with the delay count of 76H
*024FH-0250
LD B,85H
In ROM v1.2, load B with a delay count of 85H (865/975 microseconds) instead of 76H
0251-0252
CB2
Loop for delay
0253-0254
IN A,(0FFH)DB FF
Read a bit from the cassette port
0255
LD B,A47
Since we are going to need to use A, load A’s value (the bit read from the cassette port) into Register B
0256
POP AFF1
Get the value from the STACK (which was the bits already read for this byte) and put it in Register A
0257-0258
RLC BCB 10
Shift the that data bit read in Register B into the Carry flag
0259
RLA17
Shift the value in the Carry flag (which was B) into Register A
025A
PUSH AFF5
Save the value in Register A on the STACK because its gonna get erased by the next step
025B-025D
Go clear the cassette controller so we can get to reading
025E
POP AFF1
Get the value from the STACK and put it in Register A
025F
POP BCC1
Get the value from the STACK and put it in Register Pair BC
0260
RETC9
RETurn to CALLer

0261H-0263H – CASSETTE ROUTINE – “TWOCSO”

0261-0263
 ↳ TWOCSO
Write the clock pulse by calling the WRITE ONE BYTE TO CASSETTE routine at 0264H (which writes the byte in the A Register to the cassette drive selected in the A register)

0264H-027DH – CASSETTE ROUTINE (WRITE A BYTE) – “CASOUT”

Writes the byte in the A Register to tape. BC, DE and HL are unchanged by a CALL 264H.

To use a ROM call to write a character onto cassette tape (after the cassette has been turned on and leader and sync have been recorded), load the character into the A Register And CALL 0264H. If more than one character is to be written, the CALL 0264H must be executed with sufficient frequency to sustain the 500 baud recording rate. The routine provides automatic timing.
0264
 ↳ CASOUT
PUSH HLE5
Save the value in Register Pair HL on the STACK
0265
PUSH BCC5
Save the value in Register Pair BC on the STACK
0266
PUSH DED5
Save the value in Register Pair DE on the STACK
0267
PUSH AFF5
Save the value in Register A on the STACK
0268-0269
LD C,08H0E 08
Load Register C with the number of bits to be written (i.e., 8)
026A
LD D,A57
Load Register D with the byte to be written from Register A because A is about to get used in the next line
026B-026D
BYT0
Go write the start/clock bit
026E
LD A,D7A
Load Register A with the byte to be written in Register D
026F
RLCA07
Shift the bit to be written into the Carry flag
0270
LD D,A57
Save the adjusted byte to be written in Register A in Register D
0271-0272
Jump if the bit to be written is equal to zero
0273-0275
Go write that (non-zero) bit
0276
BYT1
DEC C0D
Decrement the counter in Register C
0277-0278
Loop until all eight bits have been written
0279
POP AFF1
Get the value from the STACK and put it in Register A
027A
POP DED1
Get the value from the STACK and put it in Register Pair DE
027B
POP BCC1
Get the value from the STACK and put it in Register Pair BC
027C
POP HLE1
Get the value from the STACK and put it in Register Pair HL
027D
RETC9
RETurn to CALLer

027EH-0283H – CASSETTE ROUTINE

027E-027F
 ↳ BYT2
LD B,87H06 87
Load Register B with the delay count of 87H (Decimal: 135)
0280-0281
 ↳ BYT3
Loop for the delay
0282-0283
Jump to 0276H to count the number of bits written

0284H-0292H – CASSETTE ROUTINE (TURN ON CASSETTE AND WRITE LEADER) – “CWRTON”

A call to 0284H writes a Level II leader on currently selected unit. The leader consists of 256 (decimal) binary zeros followed by a A5H. Uses the B and A registers.
0284-0286
 ↳ CWONWL
Go evaluate the cassette drive number and turn on that cassette drive’s motor
0287-0288
LD B,0FFH06 FF
Writes tape leader and the A5H sync byte. DE and HL are unchanged.

Load Register B with the number of bytes to be written
0289
XOR AAF
Zero Register A
028A-028C
CSAV1
Calls the WRITE ONE BYTE TO CASSETTE routine at 0264H (which writes the byte in the A Register to the cassette drive selected in the A register)
028D-028E
Loop until leader has been written
028F-0290
LD A,A5H3E A5
Load Register A with the sync byte value
0291-0292
Calls the WRITE ONE BYTE TO CASSETTE routine at 0264H (which writes the byte in the A Register to the cassette drive selected in the A register). In this case, it writes the SYNC byte

0293H-029EH – CASSETTE ROUTINE (TURN ON CASSETTE AND READ LEADER) – “CSRDON”

0293-0295
 ↳ CSRDON
Go evaluate the drive number and turn on that cassette drive’s motor
Read Leader: Reads the currently selected cassette unit until an end of leader (A5) is found. An asterisk is displayed in the upper right hand corner of the video display when the end is found. Uses the A-register

Reads from tape until the leader is found, then keeps going until it is bypassed and the sync byte (ASH) is found, when it returns. DE, BC and HL are unchanged by this
0296
 ↳ CSRDON+3
PUSH HLE5
Reads from tape until the leader is found, then keeps going until it is bypassed and the sync byte (A5H) is found, when it returns. DE, BC and HL are unchanged by this.

Save the current BASIC program pointer in Register Pair HL on the STACK
0297
XOR AAF
Zero Register A and status flags
0298-029A
CLOD1
Top of a loop. GOSUB to read a byte from the cassette and return with it in Register A
029B-029C
CP A5HFE A5
Check to see if the byte read from the cassette in Register A is a sync byte
029D-029E
Loop until sync byte found

029FH-02A8H – CASSETTE ROUTINE

Places the double asterisk in the right top corner to show that the sync byte has been found
029F-02A0
LD A,2FH LD A,”*”3E 2A
Places the double asterisk in the right top corner to show that the sync byte has been found.

Load Register A with a * character. (“*” is 2AH)
02A1-02A3
LD (3C3EH),A32 3E 3C
Display the * character in Register A on the video display at location 15422
02A4-02A6
LD (3C3FH),A32 3F 3C
Display the * character in Register A on the video display at location 15423
02A7
POP HLE1
Get the current BASIC program pointer from the STACK and put it in Register Pair HL
02A8
RETC9
RETurn to CALLer

02A9H-0329H – LEVEL II SYSTEM ROUTINE-ENTRY POINT – “ENBLK”

02A9-02AB
 ↳ ENBLK
Go read 2 bytes from the cassette, which should be the start/execution address, and return with it in Register Pair HL
02AC-02AE
LD (40DFH),HLLD (TEMP),HL22 DF 40
Save the just read execution address from HL into 40DFH.
Note: 40DFH-40E0H is also used by DOS
02AF-02B1
Go turn off the cassette motor
02B2-02B4
 ↳ SYSTEM
Go call the DOS link at 41E2H.
In NEWDOS 2.1, this is called during a SYSTEM operation
This location passes control to the routine used by the BASIC command SYSTEM
02B5-02B7
LD SP,4288HLD SP,BUFINI+16031 88 42
Set the STACK pointer to 4288H (which is the assumed load address). This location passes control to the routine used by the BASIC command SYSTEM
02B8-02BA
GOSUB to display a carriage return on the video display if necessary
02BB-02BC
LD A,2AHLD A,”*”3E 2A
Load Register A with an * character (which will form the next prompt)
02BD-02BF
Go display the * character in Register A on the video display
02C0-02C2
We need a filename now, so go get the input from the keyboard
02C3-02C5
If a BREAK key was hit (because the Carry flag is now on), go to the Level II BASIC READY routine
02C6
Since we need to bump the input buffer pointer in Register Pair HL until it points to the first character input, call the EXAMINE NEXT SYMBOL routine at RST 10H.

The RST 10H routine parses the characters starting at HL+1 for the first non-SPACE,non-09H,non-0BH character it finds. On exit, Register A will hold that character, and the C FLAG is set if its alphabetic, and NC FLAG if its alphanumeric. All strings must have a 00H at the end.
02C7-02C9
Display a ?SN ERROR if there wasn’t any input
02CA-02CB
CP 2FHLD A,”/”FE 2F
Check to see if the character at the location of the input buffer pointer in Register A is a / character
02CC-02CD
Jump to 031DH if the character at the location of the input buffer pointer in Register A is a /
02CE-02D0
Go turn on the cassette motor
02D1-02D3
LOPHD
Top of a small loop. Calls the READ ONE BYTE FROM CASSETTE routine at 0235H (whichh reads one byte from the cassette drive specified in Register A, and returns the byte in Register A)
02D4-02D5
CP 55HFE 55
Check to see if the byte read from the cassette in Register A is a header byte (=55H)
02D6-02D7
Loop until the header byte is found
02D8-02D9
LD B,06H06 06
If were here, we got the header byte, so load Register B with the length of the filename to read from the cassette (which is 6 characters)
02DA
 ↳ CHKBYT
LD A,(HL)7E
Load Register A with the character at the location of the current input buffer pointer in Register Pair HL
02DB
OR AB7
Check to see if the character at the location of the current input buffer pointer in Register A is an end of input character
02DC-02DD
Jump out of this ‘read the filename from the cassette’ routine if the character at the location of the current input buffer pointer in Register A is an end of input character
02DE-02E0
Calls the READ ONE BYTE FROM CASSETTE routine at 0235H (which reads one byte from the cassette drive specified in Register A, and returns the byte in Register A)
02E1
CP (HL)BE
Check to see if the character at the location of the current input buffer pointer in Register Pair HL is the same as the character read from the cassette in Register A
02E2
INC HL23
Increment the input buffer pointer in Register Pair HL
02E3-02E4
Jump to 02D1H (skip to the next program on cassette) if the character at the location of the current input buffer pointer in Register Pair HL isn’t the same as the character read from the cassette in Register A
*02E2-02E3
Jump to 02D1H (skip to the next program on cassette) if the character at the location of the current input buffer pointer in Register Pair HL isn’t the same as the character read from the cassette in Register A
*02E4
INC HL
Increment the input buffer pointer in Register Pair HL
02E5-02E6
Loop until the whole of the filename has been read from the cassette and checked against the user response
02E7-02E9
GETDT
Call the BLINK ASTERISK routine at 022CH which alternatively displays and clears an asterisk in the upper right hand corner of the video display
02EA-02EC
 ↳ GETDT2
Calls the READ ONE BYTE FROM CASSETTE routine at 0235H (whichh reads one byte from the cassette drive specified in Register A, and returns the byte in Register A)
02ED-02EE
CP 78HFE 78
Check to see if the byte read from the cassette in Register A is an execution address header byte (which is 78H)
02EF-02F0
Jump if the byte read from the cassette in Register A is an execution address header byte
02F1-02F2
CP 3CHFE 3C
Check to see if the byte read from the cassette in Register A is a file block header byte (which is 3CH)
02F3-02F4
Loop until either an execution address header byte or a file block header byte is read from the cassette
02F5-02F7
Calls the READ ONE BYTE FROM CASSETTE routine at 0235H (which reads one byte from the cassette drive specified in Register A, and returns the byte in Register A)
02F8
LD B,A47
Load Register B with the count of bytes to be loaded in Register A
02F9-02FB
Read the file block’s starting address from the cassette and return with it in Register Pair HL
02FC
ADD A,L85
For purposes of calculating a checksum, add the LSB of the file block’s starting address in Register L to the MSB of the file block’s starting address in Register A
02FD
LD C,A4F
Load Register C with the file block’s starting checksum in Register A
02FE-0300
 ↳ LDATIN
Calls the READ ONE BYTE FROM CASSETTE routine at 0235H (which reads one byte from the cassette drive specified in Register A, and returns the byte in Register A)
0301
LD (HL),A77
Save the byte read from the cassette in Register A at the location of the memory pointer in Register Pair HL
0302
INC HL23
Increment the memory pointer in Register Pair HL
0303
ADD A,C81
Add the value of the current checksum in Register C to the value in Register A
0304
LD C,A4F
Load Register C with the updated checksum in Register A
0305-0306
Loop until the whole file block has been read
0307-0309
Calls the READ ONE BYTE FROM CASSETTE routine at 0235H (which reads one byte from the cassette drive specified in Register A, and returns the byte in Register A). This reads in the checksum from cassette
030A
CP CB9
Check to see if the computed checksum in Register C is the same as the checksum read from the cassette in Register A
030B-030C
If its the same, jump to 02E7H because the next instructions are for bad checksums
030D-030E
LD A,43H3E 43
Load Register A with a C character
030F-0311
LD (3C3EH),A32 3E 3C
Display the C character in Register A on the video display (at 15422)
0312-0313
Jump to 02EAH and keep reading bytes

0314H – Read 2 bytes from the tape into Register Pair HL – “CADRIN”

This routine is commonly used by the SYSTEM routine to read the last two bytes on tape which give the entry point. A JP (HL) can then be executed to jump to the location specified, when used for this purpose. Only HL is used by this routine
0314
 ↳ CADRIN
Calls the READ ONE BYTE FROM CASSETTE routine at 0235H (whichh reads one byte from the cassette drive specified in Register A, and returns the byte in Register A)
0317
LD L,A6F
Load Register L with the byte read from the cassette in Register A (which is the LSB of the 16 bit value)
0318-031A
Calls the READ ONE BYTE FROM CASSETTE routine at 0235H (whichh reads one byte from the cassette drive specified in Register A, and returns the byte in Register A)
031B
LD H,A67
Load Register H with the byte read from the cassette in Register A (which is the MSB of the 16 bit value)
031C
RETC9
RETurn to CALLer

031DH – Execute the Cassette Program which was Loaded – “GODO”

031D
 ↳ GODO
EX DE,HLEB
Load Register Pair DE with the pointer to the BASIC command line being processed (held in Register Pair HL)
031E-0320
LD HL,(40DFH)LD HL,(TEMP)2A DF 40
Load Register Pair HL with the execution address (which is stored at 40DFH).
Note: 40DFH-40E0H is also used by DOS
0321
EX DE,HLEB
So that we can run a RST 10H in the next instruction, we need to exchange the execution address in Register Pair HL with the input buffer pointer in Register Pair DE
0322
Since we need to bump the current input buffer pointer in Register Pair HL until it points to the next character, call the EXAMINE NEXT SYMBOL routine at RST 10H.

The RST 10H routine parses the characters starting at HL+1 for the first non-SPACE,non-09H,non-0BH character it finds. On exit, Register A will hold that character, and the C FLAG is set if its alphabetic, and NC FLAG if its alphanumeric. All strings must have a 00H at the end.
0323-0325
Call the ASCII TO INTEGER routine at 1E5AH. NOTE: The routine at 1E5A converts the ASCII string pointed to by HL to an integer deposited into DE. If the routine finds a non-numeric character, the conversion is stopped
0326-0327
Jump if it turns out there weren’t any digits (i.e., bad input) in the input
0328
EX DE,HLEB
Since there were digits (or else we would have jumped in the prior instruction), exchange the input buffer pointer in Register Pair HL with the execution address in Register Pair DE
0329
JP (HL)E9
Jump to the execution address (i.e. “/xxxx”) which is in Register Pair HL

032AH-0347H – OUTPUT ROUTINE – “OUTCH1” and “OUTDO”

This is a general purpose output routine which outputs a byte from the A Register to video, tape or printer. In order to use it, the location 409CH must be loaded with -1 for tape, 0 for video or 1 for the line printer.
Note: 409CH holds the current output device flag: -1=cassette, 0=video and 1=printer.

This routine outputs a byte to device determined by byte stored at (409CH) – FFH=Tape, 0=Video, l=Printer. When calling, A = output byte. Uses AF. Warning: This routine CALLs a Disk BASIC link at address 41ClH which may have to be “plugged” with a RETurn (C9H) instruction.
032A
 ↳ OUTDO
PUSH BCC5
We are going to need to use Register C, so push Register Pair BC into the STACK
032B
LD C,A4F
Load Register C with the character to be output in Register A
032C-032E
Go call the DOS link at 41ClH.
In NEWDOS 2.1, this writes to the system output device
032F-0331
LD A,(409CH)LD A,(PRTFLG)3A 9C 40
Load Register A with the current output device number stored in 409CH.
Note: 409CH holds the current output device flag: -1=cassette, 0=video and 1=printer
0332
OR AB7
Since LD doesn’t set flags, in order to be able to test Register A using flags we need to execute an OR A first. This will enable us to set the flags according to the current output device number in Register A
0333
LD A,C79
Load Register A with the character to be output in Register C
0334
POP BCC1
Get the value from the STACK and put it in Register Pair BC
At this point, A is either +1, -1, or 0. The ROM handles this by testing for a positive number (1 = Cassette), and then a non-zero number (-1 = Printer), and then flows down (0 = Display) if neither of those apply
0335-0337
If the value of the current output device number is positive it means CASSETTE, so jump to the the WRITE ONE BYTE TO CASSETTE routine at 0264H (which writes the byte in the A Register to the cassette drive selected in the A register)
0338-0339
Jump to 039CH if the character in Register A is to be sent to the printer

033AH-0347H – OUTPUT ROUTINE – “OUT2D”

A Print routine which performs the same function as 33H except that it doesn’t destroy the contents of the DE Register Pair. This means that all the general purpose registers are saved, which is often desirable

To use a ROM call to print a single character at the current cursor position, and to update the cursor position, load the ASCII value of the character into the A Register And then CALL 033AH.

To display special functions using a ROM call, load the A Register with the value given below for the special function and then CALL 033AH.
  1. Backspace and erase previous character – 08H
  2. Carriage return and linefeed – 0DH
  3. Turn on cursor – 0EH
  4. Turn off cursor – 0FH
  5. Convert to 32 characters per line mode – 17H
  6. Backspace cursor – 18H
  7. Advance cursor one position – 19H
  8. Downward line feed – 1AH
  9. Upward line feed – 1BH
  10. Home (cursor to upper left corner) – 1CH
  11. Move cursor to beginning of current line – 1DH
  12. Erase from cursor position to end of line – 1EH
  13. Erase from cursor position to end of screen – 1FH
033A
 ↳ OUT2D
PUSH DED5
If we’re here, then that value in A wasn’t going to the cassette or the printer, so it must be going to the video. This routine performs the same function as 33H except that it doesn’t destroy the contents of the DE Register Pair. This means that all the general purpose registers are saved, which is often desirable.

Save the value in Register Pair DE on the STACK
033B-033D
Call the DISPLAY A CHARACTER routine at 0033H (which puts the character in Register A on the video screen)
033E
PUSH AFF5
Save the character in Register A on the STACK
033F-0341
Go update the current cursor position and test to see if the display memory is full
0342-0344
LD (40A6H),ALD (TTYPOS),A32 A6 40
Save the current cursor line position stored in 40A6H to Register A.
Note: 40A6H holds the current cursor line position
0345
POP AFF1
Get the character from the STACK and put it in Register A
0346
POP DED1
Get the value from the STACK and put it in Register Pair DE
0347
RETC9
RETurn to CALLer

0348H-0357H – VIDEO ROUTINE – “DSPPOS”

0348-034A
 ↳ DSPPOS
LD A,(403DH)LD A,(CAST$)3A 3D 40
Load Register A with the contents of 403DH, which contains, among other things, the screen resolution (32 or 64 wide; Bit 3) the tape relay on/off instruction (Bit 2) and the positive/negative audio pulses (Bits 0-1).
Note: 403DH-4040H is used by DOS
034B-034C
AND 08HAND 0000 1000E6 08
Mask Register A against 00001000 to isolate Bit 3 (the 32/64 character per line flag) in Register A
034D-034F
LD A,(4020H)LD A,(CURSOR)3A 20 40
Load Register A with the LSB of the current cursor position.
Note: 4020H-4021H holds Video DCB – Cursor location
0350-0351
If Bit 3 of 403DH was a zero, then we have 64 characters per line mode so JUMP down a few instructions to skip over the division needed to drop everything by half to 32 character mode
0352
RRCA0F
Divide the LSB of the current cursor position in Register A by two
0353-0354
AND 1FHAND 0001 1111E6 1F
Mask the cursor line position in Register A for 32 character per line (AND against 0001 1111) to force its position to be no less than 3C00H
0355-0356
AND 3FHAND 0011 1111E6 3F
Mask the cursor line position in Register A for 64 characters per line (AND against 0011 1111) to force its position to be no more than 3FFFH
0357
RETC9
RETurn to CALLer

0358H-0360H – KEYBOARD ROUTINE – “ISCHAR”
Here is the routine to simulate the INKEY$ function. It performs exactly the same function as 2BH but it restores all registers, whereas 2BH destroys the contents of the DE Register Pair. This makes 35BH more useful than 2BH

0358-035A
 ↳ ISCHAR
Go call the DOS link at 41C4H
Here is the routine to simulate the INKEY$ function. It performs exactly the same function as 2BH but it restores all registers, whereas 2BH destroys the contents of the DE Register Pair. This makes 35BH more useful than 2BH
035B
PUSH DED5
Since the next routine uses DE, save the value in Register Pair DE on the STACK
035C-035E
Call the SCAN KEYBOARD routine at 002BH
035F
POP DED1
Get the value from the STACK and put it in Register Pair DE
0360
RETC9
RETurn to CALLer

0361H-0383H – INPUT ROUTINE – “INLIN”

This is one of the general purpose input routines (see 5D9 and 1BB3 also). This routine inputs a string from the keyboard, up to a maximum of 240 characters (F0H), and echoes them to the screen. It puts this data into a buffer located at the address pointed to by the buffer pointer at 40A7H. (e.g. If 40A7H contains 5000H the data will be stored from 5000H onwards). The string is terminated with a zero byte. The program returns from this routine as soon as the ENTER key has been pressed. When it does so, HL contains the start address of the input string and B contains the length of the string. (RST 10H can be used to make HL point to the first character of the string, if required.).
Note: 40A7H-40A8H holds the input Buffer pointer.
0361
 ↳ INLIN
XOR AAF
Zero Register A to clear the buffered character
0362-0364
LD (4099H),ALD (CHARC),A32 99 40
Save the value in Register A as the last key pressed (which is stored in 4099H).
Note: 4099H holds the Last key pressed
0365-0367
LD (40A6H),ALD (TTYPOS),A32 A6 40
Save the value in Register A as the current cursor line position (which is stored in 40A6H).
Note: 40A6H holds the current cursor line position
0368-036A
Go call the DOS link at 41AFH.
In NEWDOS 2.1, this is the satrt of keyboard input
036B
PUSH BCC5
Save Register Pair BC on the STACK
036C-036E
LD HL,(40A7H)LD HL,(BUFPNT)2A A7 40
Load Register Pair HL with the starting address of the input buffer (which is stored in 40A7H).
Note: 40A7H-40A8H holds the input Buffer pointer
036F-0370
LD B,0F0H06 F0
Load Register B with the length of the input buffer (which is 240)
0371-0373
“WAIT FOR NEXT LINE” keyboard input routine at 05D9H (which takes keyboard entry until a carriage return, a break, or buffer overrun occurs)
0374
PUSH AFF5
Save the flags on the STACK
0375
LD C,B48
Load Register C with the length of the input in Register B
0376-0377
LD B,00H06 00
Zero Register B so that Register Pair BC will have the length of the input
0378
ADD HL,BC09
Add the length of the input in Register Pair BC to the starting address of the input buffer in Register Pair HL
0379-037A
LD (HL),00H36 00
Save an end of the input character at the location of the end of input pointer in Register Pair HL
037B-037D
LD HL,(40A7H)LD HL,(BUFPNT)2A A7 40
Load Register Pair HL with the starting address of the input buffer (which is 40A7H).
Note: 40A7H-40A8H holds the input Buffer pointer
037E
POP AFF1
Get the flags from the STACK
037F
POP BCC1
Get the value from the STACK and put it in Register Pair BC
0380
DEC HL2B
Decrement the input buffer pointer in Register Pair HL (so that HL is the input area pointer – 1)
0381
RET CD8
Return if the BREAK key was pressed
0382
XOR AAF
Otherwise (i.e., the BREAK key was not pressed), zero all the status flags
0383
RETC9
RETurn to CALLer

0384H-038AH – KEYBOARD ROUTINE – “INCHR”

Waits for keypress
0384-0386
 ↳ INCHR
Go scan the keyboard
0387
OR AB7
Check to see if a key was pressed
0388
RET NZC0
Return if a key was pressed (meaning OR A was set to NZ)
0389-038A
Loop until a key is pressed

038BH-039BH – PRINTER ROUTINE – “FINLPT”

This routine resets device type flag at 409CH to zero (output to video display), also outputs a carriage return to the line printer if printer is not at beginning of line (determined by checking the contents of the printer line position flag at 409BH – if flag contains zero, printer is at start of line). Note that if printer line position flag does not contain zero and the printer is not on line, the computer will “hang up” waiting for a “printer ready” signal.
038B
 ↳ FINLPT
XOR AAF
Zero Register A, which then means it contains the device code for VIDEO
038C-038E
LD (409CH),ALD (PRTFLG),A32 9C 40
Save the value in Register A (the current output device code of video) to 409CH.
Note: 409CH holds the current output device flag: -1=cassette, 0=video and 1=printer
038F-0391
LD A,(409BH)LD A,(LPTPOS)3A 9B 40
Load Register A with the current printer carriage position (which is stored at 409BH).
Note: 409BH holds the printer carriage position
0392
OR AB7
Set the flags for the carriage position in Register A
0393
RET ZC8
Return if the carriage position in Register A is equal to zero
0394-0395
LD A,0DHLD A,ENTER3E 0D
Load Register A with a “CARRIAGE RETURN
0396
PUSH DED5
Save the value in Register Pair DE on the STACK
0397-0399
Go send the carriage return character in Register A to the printer
039A
POP DED1
Get the value from the STACK and put it in Register Pair DE
039B
RETC9
RETurn to CALLer

039CH-03C1H – PRINTER ROUTINE – “OUTLPT”

This is the LPRINT routine. All registers are saved. The byte to be printed should be in the A register.
039C
 ↳ OUTLPT
PUSH AFF5
Save the value in Register Pair AF on the STACK
039D
PUSH DED5
Save the value in Register Pair DE on the STACK
039E
PUSH BCC5
Save the value in Register Pair BC on the STACK
039F
LD C,A4F
Load Register C with the character to be sent to the printer in Register A
03A0-03A1
LD E,00H1E 00
Zero Register E (which will ultimately hold the new character/line count of 0CH, 0DH, or 0AH)
03A2-03A3
 ↳ OUTDO
CP 0CHFE 0C
Check to see if the character to be sent to the printer in Register A is equal to 0CH (which is ‘skip to next line’)
03A4-03A5
Jump to 03B6H if the character to be sent to the printer in Register A is equal to 0CH
03A6-03A7
CP 0AHLD A,LINE FEEDFE 0A
Check to see if the character to be sent to the printer in Register A is a line feed character (i.e., 0AH)
03A8-03A9
Jump to 03ADH if the character to be sent to the printer in Register A isn’t a line feed character
03AA-03AB
LD A,0DHLD A,CARRIAGE RETURN3E 0D
Load Register A with a carriage return character (i.e., 0DH)
03AC
LD C,A4F
Load Register C with the character to be sent to printer in Register A
03AD-03AE
 ↳ LZRNOT
CP 0DHFE 0D
Check to see if the character to be sent to the printer in Register A is a carriage return character
03AF-03B0
Jump to 03B6H if the character to be sent to the printer in Register A is a carriage return character
03B1-03B3
LD A,(409BH)LD A,(LPTPOS)3A 9B 40
Load Register A with the current printer carriage position (stored in 409BH).
Note: 409BH holds the printer carriage position
03B4
INC A3C
Increment the current carriage position in Register A
03B5
LD E,A5F
Load Register E with the current carriage position in Register A
03B6
 ↳ LZRPOS
LD A,E7B
Load Register A with the current carriage position in Register E. Why do this since its obviously already done? Becasuse this is a jump point!
03B7-03B9
LD (409BH),ALD (LPTPOS),A32 9B 40
Save the current carriage position (which is stored in 409BH) in Register A.
Note: 409BH holds the printer carriage position
03BA
LD A,C79
Load Register A with the character to be sent to the printer in Register C
03BB-03BD
Call the PRINT CHARACTER routine at 003B (which sends the character in the C Register to the printer)
03BE
POP BCC1
Get the value from the STACK and put it in Register Pair BC
03BF
POP DED1
Get the value from the STACK and put it in Register Pair DE
03C0
POP AFF1
Get the value from the STACK and put it in Register Pair AF
03C1
RETC9
RETurn to CALLer

03C2H-03E2H – DRIVER ENTRY ROUTINE – “CIO”

This routine is called from a RST 14 (with a device code of 01H in Register B), RST 1C (with a device code of 02H in Register B), and RST 24 (with a device code of 04H in Register B).

On entry, BC shoud contain the Device Control Block and A may contain (if needed) the output control/data

According to the original ROM notes, this is the Character I/O Linkage to Device Driver routine. On entry Register Pair DE to point at the Device Control Block and Register A will hold the output or control data, if any. On exit, the codes depend on whether a byte or a control code was passed. If this was an I/O operation, Register A will hold that input/output data byte. If this was a I/O control operation, Register A will hold the device status and the Z FLAG will be set if the device is ready.
03C2
 ↳ CIO
PUSH HLE5
Save Register Pair HL on the STACK
03C3-03C4
PUSH IXDD E5
Save the value in Register Pair IX on the STACK
03C5
PUSH DED5
Save the starting address of the device control block in Register Pair DE on the STACK
03C6-03C7
POP IXDD E1
Get the starting address of the device control block from the STACK and put it in Register Pair IX, so now IX = DCB + 0
03C8
PUSH DED5
Save the value in Register Pair DE on the STACK
03C9-03CB
LD HL,03DDHLD HL,CIORTN21 DD 03
Load Register Pair HL with a return address of 03DDH
03CC
PUSH HLE5
Save the return address in Register Pair HL on the STACK
03CD
LD C,A4F
Save the character to process (current held in Register A) to Register C so we can use Register A
03CE
LD A,(DE)1A
Load Register A with the device type code (stored the memory location pointed to by DE)
03CF
AND BA0
Isolate the device code bits in A by AND’ing with the device codes in B
03D0
CP BB8
Check to see if the updated device type code in Register A is the same as the driver entry code in Register B
03D1-03D3
Jump to the DOS exit link at 4033H if the updated device type code in Register A isn’t the same as the driver entry code in Register B
03D4-03D5
CP 02HFE 02
At this point we know that the updated device type code in A is the same as the driver code entry, so let’s move on. First, reset the flags
03D6-03D8
LD L,(IX+01H)DD 6E 01
Load Register L with the LSB of the driver entry address at the location of the device control block pointer in Register Pair IX plus one
03D9-03DB
LD H,(IX+02H)DD 66 02
Load Register H with the MSB of the driver entry address at the location of the device control block pointer in Register Pair IX plus one
03DC
JP (HL)E9
Jump to the driver entry address in Register Pair HL
03DD
 ↳ CIORTN
POP DED1
Get the value from the STACK and put it in Register Pair DE
03DE-03DF
POP IXDD E1
Get the value from the STACK and put it in Register Pair IX
03E0
POP HLE1
Get the value from the STACK and put it in Register Pair HL
03E1
POP BCC1
Get the value from the STACK and put it in Register Pair BC
03E2
RETC9
RETurn to CALLer

03E3H-0457H – KEYBOARD DRIVER – “KEY”

This is the keyboard driver. It scans the keyboard and converts the bit pattern obtained to ASCII and stores it in the A register.

According to the original ROM notes, this is the Keyboard Driver. On exit, Register A to hold the data byte received (or 0 if none). On entry, [IX] should point to the DCB, which is laid out as follows:
  • DCB + 0 = DCB Type
  • DCB + 1 = Driver Address (LSB)
  • DCB + 2 = Driver Addres (MSB)
  • DCB + 3 = 0
  • DCB + 4 = 0
  • DCB + 5 = 0
  • DCB + 6 = “K”
  • DCB + 7 = “I”
03E3-03E5
 ↳ KEY
LD HL,4036HLD HL,KYBT$21 36 40
Load Register Pair HL with the keyboard work area’s starting address (which is 4036H).
Note: 4036H-403CH is the keyboard work area
03E6-03E8
LD BC,3801HLD BC,KEYAD$+101 01 38
Load Register Pair BC with the keyboard memory’s starting address (which is 3801H)
03E9-03EA
LD D,00H16 00
Zero Register D, which will be used to track the keyboard code
03EB
 ↳ KEYLP
LD A,(BC)0A
Load Register A with the value at the location of the keyboard memory pointer in Register Pair BC (which is row N)
03EC
LD E,A5F
Load Register E with the keyboard memory value in Register A (8 column bits)
03ED
XOR (HL)AE
Check for inequality by XORing the value at the location of the keyboard work area pointer in Register Pair HL with the keyboard memory value in Register A
03EE
LD (HL),E73
Save the keyboard memory value (the column bits) in Register E at the location of the keyboard work area pointer in Register Pair HL
03EF
AND EA3
Test for the active row by masking the adjusted value in Register A with the value at the location of the keyboard work area pointer in Register Pair HL
03F0-03F1
Jump to 03FAH if the new key pressed is in row N
03F2
INC D14
Increment the keyboard row counter in Register D
03F3
INC L2C
Increment the keyboard work area pointer in Register Pair HL
03F4-03F5
RLC CCB 01
Adjust the keyboard memory pointer in Register Pair BC by stepping it from 3801 to 3840 by rotating the bits left (RLC)
03F6-03F8
Jump to 03EBH if the whole of keyboard memory hasn’t been checked (by POSITIVE bit [Bit 7] being on). This has the effect of looping over the first 6 rows of the keyboard but NOT doing row 7, which has the shift key
03F9
RETC9
RETurn to CALLer

03FAH-040AH – Accept a Keyboard Downstroke and Convert it to ASCII – “KEYDWN”

03FA
 ↳ KEYDWN
LD E,A5F
Save the column number from the new keypress in Register A in Register E
03FB
LD A,D7A
Load Register A with the row counter in Register D (this is going to cycle from 0 through 6)
03FC
RLCA07
Multiply the row counter in Register A by two
03FD
RLCA07
Multiply the row counter in Register A by two
*03FB-03FD
JP 011CH
For ROM v1.2, jump to the new keyboard debounce routine. That routine includes the now deleted RCLA, RCLA instructions which had to be killed to make room for this 3 byte jump opcode
03FE
RLCA07
Multiply the row counter in Register A by two
03FF
LD D,A57
Load Register D with the row counter in Register A
0400-0401
LD C,01H0E 01
Load Register C with the starting column counter (as bit 0)
0402
 ↳ KEYDLP
LD A,C79
Load Register A with the column counter in Register C (as a mask)
0403
AND EA3
Turn off some bits so we can check to see if the column counter in Register A is the same as the active column number in Register E
0404-0405
Jump to 040BH if the column counter in Register A is the same as the active column number in Register E
0406
INC D14
Increment the column number (which is held in Register D)
0407-0408
RLC CCB 01
Adjust the column counter in Register C left 1 bit to align the mask
0409-040A
Loop back to 0402H until the value in Register D is adjusted for its column

040BH-0428H – Part of the Keyboard routine – “KEYFND”
We now have identified the key. Next we need to see if it is shifted

040B-040D
 ↳ KEYFND
LD A,(3880H)LD A,(KEYAD$+80H)3A 80 38
Load Register A with the value of the SHIFT but from memory location 3880H
040E
LD B,A47
Load Register B with the SHIFT FLAG value in Register A
040F
LD A,D7A
Load Register A with the value in Register D (Row * 8 + column 0-7)
0410-0411
ADD 40HC6 40
Adjust the ASCII value in Register A (Row * 8 + column (0-7) + 64 decimal)
0412-0413
CP 60HFE 60
Check to see if the value in Register A is alphabetic by testing for the 4 rows (@, A-Z)
0414-0415
Jump to 0429H if the value in Register A is nonalphabetic
0416-0417
RRC BCB 08
Put the SHIFT value in Register B in the carry flag (as the RRC command rotates right, and puts bit 0 into the carry bit)
0418-0419
Jump if the SHIFT key wasn’t pressed (i.e., the rotated right Register B’s bit zero wasn’t a zero)
041A-041B
ADD 20HC6 20
Adjust the value in Register A for lower case (by ADDing 20H to it)
041C
LD D,A57
Load Register D with the adjusted character in Register A
041D-041F
LD A,(3840H)LD A,(KEYAD$+40H)3A 40 38
Load Register A with the value at keyboard memory row six
0420-0421
AND 10HAND 0001 0000E6 10
Turn off some bits so we can check to see if the down arrow key (or ENTER) was pressed (by ANDing it against 0001 0000)
0422-0423
Jump to 044CH if down arrow (or ENTER) wasn’t pressed
0424
LD A,D7A
Load Register A with the character in Register D
0425-0426
SUB 60HD6 60
Adjust the value of the character in Register A down by 96, possibly to make it a CONTROL KEY
0427-0428
Jump to 044BH

0429H-043CH – Part of the Keyboard routine – “KEYNAL”
If we are here, the character was not alphanumeric, so need to check for special and/or shift

0429-042A
 ↳ KEYNAL
SUB 70HD6 70
Adjust the value of the character in Register A down by 112 (for a special key, like ENTER or SPACE)
042B-042C
Jump to 043DH if the character in Register A is for keyboard row six
042D-042E
ADD 40HC6 40
Readjust the value of character in Register A by adding 64 to adjust to rows 4-5
042F-0430
CP 3CHFE 3C
Check to see if the character in Register A is a 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, “:”, “;”, or “,” ,character
0431-0432
Jump to 0435H for a simple inversion if the character in Register A is one of those characters
0433-0434
XOR 10HXOR 0001 0000EE 10
Adjust the character in Register A by XORing against 0001 0000 to invert bit 5
0435-0436
 ↳ KEYINV
RRC BCB 08
Put the SHIFT value in Register B into the Carry flag
0437-0438
Jump to 044BH if the SHIFT key wasn’t pressed
0439-043A
XOR 10HEE 10
Adjust the character in Register A by XORing against 0001 0000 to reinvert bit 5
043B-043C
Jump to 044BH to output

043DH-044AH – Part of the Keyboard routine – “KEYSPL”
This routine does a special key conversion via a table

043D
 ↳ KEYSPL
RLCA07
Adjust the value in Register A to be (ROW*8 + COLUMN-48) * 2)
043E-043F
RRC BCB 08
Put the SHIFT value in Register B into the Carry flag
0440-0441
Jump to 0443H if the SHIFT key wasn’t pressed
0442
INC A3C
Increment the value in Register A to turn (ROW*8 + COLUMN-48) * 2) into (COLUMN*2+1)
0443-0445
 ↳ KEYNSF
LD HL,0050HLD HL,KEYTAB21 50 00
Load Register Pair HL with the starting address of the keyboard lookup table at 50H, which is the last row
0446
LD C,A4F
Load Register C with the offset in Register A (which is either 43D or 442)
0447-0448
LD B,00H06 00
Zero Register B
0449
ADD HL,BC09
Add the offset in Register Pair BC to the starting address of the keyboard lookup table in Register Pair HL
044A
LD A,(HL)7E
Load Register A with the ASCII value at the location of the keyboard lookup table pointer in Register Pair HL

044BH-044BH – Part of the Keyboard routine – “KEYRTN”
This routine will debounce the keyboard downstroke and return

044B
 ↳ KEYRTN
LD D,A57
Load Register D with the ASCII value for the key pressed in Register A
044C-044E
 ↳ KEYRT2
LD BC,0DACHLD BC,350001 AC 0D
Load Register Pair BC with the delay count (which is 3500)
044F-0451
Call the delay routine at 0060H (which will delay BC times 14.65)
0452
LD A,D7A
Load Register A with the ASCII value for the key pressed (saved in Register D)
0453-0454
CP 01HFE 01
Check to see if the BREAK key was pressed
0455
RET NZC0
Return if the BREAK key wasn’t pressed
0456
RST 28HEF
If the BREAK key was pressed,call the DOS FUNCTION CALL routine at RST 28 (which passes request code in A-register to DOS for processing. Returns for non-disk system. For disk systems, the A Register must contain a legitimate DOS function code. If the code is positive, the CALL is ignored and control returns to the caller. Note that the DOS routine discards the return address stored on the STACK by the RST instruction. After processing control will be returned to the previous address on the STACK)
044B
RET57
RETurn to CALLer

0458H-058CH – DISPLAY DRIVER – “DSP”

This is the video driver. On entry, the character to be displayed should be in the C register. On exit, A would contain the character at the cursor (if called for an INPUT). This routine handles scrolling etc.

Register IX points to the DCB, so IX+0 = the DCB type, IX+1 = LSB of the Driver Address, IX+2 = MSB of the Driver Address, IX+3 = LSB of the Cursor Position, IX+4 = MSB of the Cursor Position, IX+5 = Cursor Character, IX+6 = “D”, and IX+7=”O”

According to the original ROM notes, this is the Display Driver. On exit, Register A to hold the character read from the new cursor position (if the routine was called to look for that). On entry, [IX] should point to the DCB, which is laid out as follows:
  • DCB + 0 = DCB Type
  • DCB + 1 = Driver Address (LSB)
  • DCB + 2 = Driver Addres (MSB)
  • DCB + 3 = Cursor Position Address (LSB)
  • DCB + 4 = Cursor Position Address (MSB)
  • DCB + 5 = Cursor Character
  • DCB + 6 = “D”
  • DCB + 7 = “O”
0458-045A
 ↳ DSP
LD L,(IX+03H)DD 6E 03
Load Register L with the LSB of the current cursor position at the location of the video device control block pointer in Register Pair IX plus three
045B-045D
LD H,(IX+04H)DD 66 04
Load Register H with the MSB of the current cursor position at the location of the video device control block pointer in Register Pair IX plus four
045E-045F
Jump to 049AH if get last character
0460-0462
LD A,(IX+05H)DD 7E 05
Load Register A with the cursor on/off flag (which is stored at the location of the video device control block pointer in Register Pair IX plus five)
0463
OR AB7
Check to see if the cursor is on or off
0464-0465
Jump to 0467H if the cursor is off
0466
LD (HL),A77
Display the character in Register A at the location of the current cursor position in Register Pair HL since the cursor is on
0467
 ↳ DSPGRP
LD A,C79
Load Register A with the character to be displayed from Register C
0468-0469
CP 20HFE 20
Check to see if the character to be displayed in Register A is a control code
046A-046C
Since CP returns C set if Register A (the character to be displayed) is less than the test value (20H; meaning it is a control character below SPACE), jump to 0506H if the character to be displayed in Register A is a control code
046D-046E
CP 80HFE 80
Check to see if the character to be displayed in Register A is a graphic character or space compression code
046F-0470
Since CP returns NC set if Register A (the character to be displayed) is greater than or equal to the test value (80H; meaning it is a graphic character or a space compression code), jump to 04A6H if the character to be displayed in Register A is a graphic character or space compression code
0471-0472
CP 40HFE 40
Check to see if the character to be displayed in Register A is an alphabetic character
0473-0474
Since CP returns C set if Register A (the character to be displayed) is less than the test value (40H; meaning it is below the “@” – so a special character or a number), jump to 047DH if the character to be displayed in Register A is a nonalphabetic character
0475-0476
SUB 40HSUB “@”D6 40
If we are still here, then the character in Register A is between 40H (“@”) and 7FH (the last character before the graphics). Drop this down 40H
0477-0478
CP 20HFE 20
Check to see if the character to be displayed in Register A is a lower case character
0479-047A
Since CP returns C set if Register A (the newly subtracted character) is less than the test value (20H or 32), jump to 047DH since the character to be displayed in Register A isn’t lower case
047B-047C
SUB 20HD6 20
Convert the lower case character in Register A to upper case by subtracting 32
047D-047F
 ↳ DSPCHR
Go to 0541H display the character in Register A (and scroll the screen if necessary)
0480
 ↳ DSPSKP
LD A,H7C
Load Register A with the MSB of the current cursor position in Register H
0481-0482
AND 03HE6 03
Turn off some bits in Register A so that it will be in the range of video memory (3C00H-3FFFH) by ANDing it against 0000 0011
0483-0484
OR 3CHOR 0011 1100F6 3C
Turn on some bits in Register A to force the MSB of the buffer to be 3C-3Fh (i.e., video memory) by ORing it against 0011 1100
0485
LD H,A67
Load Register H with the updated MSB value (which was forced to be within video memory) in Register A
0486
LD D,(HL)56
Load Register D with the value at the location of the current cursor position in Register Pair HL
0487-0489
LD A,(IX+05H)DD 7E 05
Load Register A with the cursor on/off flag at the location of the video device control block pointer in Register Pair IX plus five
048A
OR AB7
Check to see if the cursor is on or off
048B-048C
Jump to 0492H if the cursor is off
048D-048F
LD (IX+05H),DDD 72 05
Since the cursor is on, save the character being displayed in Register D as the cursor on/ off flag at the location of the video device control block pointer in Register Pair IX plus five
0490-0491
LD (HL),5FHLD (HL),CURCHR36 5F
Display the cursor character (of 5FH) at the current location of the cursor in Register Pair HL
0492-0494
 ↳ DSPRTN
LD (IX+03H),LDD 75
Save the LSB of the current cursor position in Register L at the location of the video device control block in Register Pair IX plus three
0495-0497
LD (IX+04H),HDD 74
Save the MSB of the current cursor position in Register H at the location of the video device control block in Register Pair IX plus four
0498
LD A,C79
Load Register A with the character that was displayed in Register C
0499
RETC9
RETurn to CALLer

049AH – Read the character at the current position of the display – “DSPRD”

049A-049C
 ↳ DSPRD
LD A,(IX+05H)DD 7E 05
Load Register A with the cursor on/off flag
049D
OR AB7
Check to see if the cursor is on or off
049E
RET NZC0
Return if the cursor is on and is therefore covering the character
049F
LD A,(HL)7E
If the cursor is off, show the character it was hiding instead by loading Register A with the character at the location of the current cursor position in Register Pair HL
04A0
RETC9
RETurn to CALLer

04A1H – Go to the beginning of the line – “DSPBOL”

04A1
 ↳ DSPBOL
LD A,L7D
Load Register A with the LSB of the current position in Register L
04A2-04A3
AND 0C0HAND 1100 0000E6 C0
Turn off some bits so we can it will point to the beginning of the line by ANDing it against 1100 0000 to remove the lowest 6 bits (so it will be XX00H, XX40H, XX80H, or XXC0H). This is the same thing as a CARRIAGE RETURN but without the associated LINE FEED
04A4
LD L,A6F
Load Register L with the updated value in Register A
04A5
RETC9
Return with the new video buffer address stored in HL

04A6H – Handle graphic characters – “DSPHRC”

04A6-04A7
 ↳ DSPGRC
CP C0HFE C0
Check to see if the character to be displayed in Register A is a space compression character
04A8-04A9
Since CP returns C set if Register A (the character to be displayed) is less than the test value (C0H; meaning it is below the space compression characters or, another way, is a graphic character), jump to 047DH if the character to be displayed in Register A isn’t a space compression character (which means it is a graphic character)
04AA-04AB
SUB C0HD6 C0
Now we know we have a space compresison code, so adjust the value in Register A so that it will hold the number of spaces to be displayed
04AC-04AD
Jump to 0480H if there aren’t any spaces to be displayed
04AE
LD B,A47
Now we know it is a space compression character and that at least one space is to be displayed, so load Register B with the number of spaces to be displayed in Register A (since the space compression codes are sequential from 0C0H up)

04AFH – Handle Space Compression characters – “DSPSPC”

04AFH-04B0
 ↳ DSPSPC
LD A,20H3E 20
Load Register A with a space character
04B1-04B3
Gosub to 0541H to display the blank character in Register A (and scroll the screen if necessary)
04B4-04B5
Loop back to 04AFH until all of the spaces have been displayed
04B6-04B7
Once all the spaces have been displayed, jump to 0480H

04B8H – Turn the cursor on – “DSPCON”

04B8
 ↳ DSPCON
LD A,(HL)7E
Load Register A with the character being displayed at the location of the current cursor position in Register Pair HL
04B9-04BB
 ↳ DSPCN2
LD (IX+05H),ADD 77 05
Save the value in Register A as the cursor on/off flag at the location of the video device control block pointer in Register Pair IX plus five
04BC
RETC9
RETurn to CALLer

04BDH – Turn the cursor off – “DSPCOF”

04BD
 ↳ DSPCOF
XOR AAF
Zero Register A to turn the cursor flag off
04BE-04BF
Jump to 04B9H to put a 0 into (IX+05H) and RETurn

04C0H – Home the cursor – “DSPHOM”

04C0-04C2
 ↳ DSPHOM
LD HL,3C00HLD HL,DSPAD$21 00 3C
Load Register Pair HL with the starting address of video memory (which is 3C00H)
04C3-04C5
LD A,(403DH)LD A,(CAST$)3A 3D 40
Load Register A with the contents of 403DH, which contains, among other things, the screen resolution (32 or 64 wide; Bit 3) the tape relay on/off instruction (Bit 2) and the positive/negative audio pulses (Bits 0-1).
Note: 403DH-4040H is used by DOS
04C6-04C7
AND 0F7HAND 1111 0111E6 F7
Mask Register A against 1111 0111, forcing Bit 3 to OFF to denote 64 characters per line
04C8-04CA
LD (403DH),ALD (CAST$),A32 3D 40
Put the masked Register A back into 403DH
04CB-04CC
OUT (0FFH),AD3 FF
Send the value in Register A out port 255 which is the video/cassette port
04CD
RETC9
RETurn to CALLer

04CEH – Backspace – “DSPBSP”

04CE
 ↳ DSPBSP
DEC HL2B
Decrement the current cursor position (i.e., backspace) in Register Pair HL
04CF-04D1
LD A,(CAST$)LD A,(403DH)3A 3D 40
Load Register A with the 32/64 character per line flag (which is at 403DH).
Note: 403DH-4040H is used by DOS
04D2-04D3
AND 08HAND 0000 1000E6 08
Turn off some bits so we can check to see if it’s 32 or 64 characters per line by ANDing it against 0000 1000
04D4-04D5
Since the AND leaves us with either a 0 (64 characters per line) or a 1 (32 characters per line), jump to 04D7H if it’s 64 characters per line
04D6
DEC HL2B
Right now we know it is 32 characters per line (since we didn’t jump away), so we need to backspace AGAIN by decrementing the current cursor position in Register Pair HL
04D7-04D8
 ↳ DSPBS2
LD (HL),20HLD (HL),” “36 20
Display a space character at the location of current cursor position in Register Pair HL
04D9
RETC9
RETurn to CALLer

04DAH – Cursor Left – “DSPLFT”

04DA-04DC
 ↳ DSPLFT
LD A,(403DH)LD A,(CAST$)3A 3D 40
Load Register A with the 32/64 character per line flag (which is at 403DH).
Note: 403DH-4040H is used by DOS
04DD-04DE
AND 08HAND 0000 1000E6 08
Mask Register A against 0000 1000 to isolate Bit 3 and set the flags Z and NZ based on that bit
04DF-04E1
If Bit 3 was not zero then we have 32 characters per line, so GOSUB to 04E2H. Note: This is a little trick. By doing a GOSUB to the next line, you effectively block the RET at the end of the routine from jumping out, and instead it jumps back here; thus running the routine TWICE
04E2
 ↳ DSPLF2
LD A,L7D
We are actually here regardless of what Bit 3 was. Load Register A with the LSB of the current cursor position in Register L
04E3-04E4
AND 3FHAND 0011 1111E6 3F
Mask the value in Register A by ANDing it against 0011 1111 to backspace LSB of curstor to the previous line and then ..
04E5
DEC HL2B
Backspace the cursor by 1 by decrementing the current cursor position in Register Pair HL
04E6
RET NZC0
Return if still on the same line

04E7H – Cursor Down – “DSPDWN”

This is a space saver because if the cursor isn’t on the same line it needs to move down, so jumping here is also jumping to a CURSOR DOWN routine that was simply a fall-through from a wrap around
04E7-04E9
 ↳ DSPDWN
LD DE,0040H11 40 00
We know we are not on the same line anymore so load Register Pair DE with the length of a line on the video display (which is 64)
04EA
ADD HL,DE19
Skip down 1 line by adding the length of a line on the video display in Register Pair DE to the current cursor position in Register Pair HL
04EB
RETC9
RETurn to CALLer

04ECH – Cursor Right – “DSPRHT”

04EC
 ↳ DSPRHT
INC HL23
Increment the current cursor position in Register Pair HL
04ED
LD A,L7D
Load Register A with the LSB of the current cursor position in Register L
04EE-04EF
AND 3FHAND 0011 1111E6 3F
Turn off some bits so we can check to see if the cursor is still on the same line via an overflow through ANDing it against 0011 1111
04F0
RET NZC0
Return if the cursor is still on the same line

04F1H – Cursor Up – “DSPUP”

Same trick as dealing with CURSOR DOWN if there was an overflow, this does a CURSOR UP if you back up too far
04F1-04F3
 ↳ DSPUP
LD DE,0FFC0H11 C0 FF
Now we know the cursor is no longer on the same line, so we need to load Register Pair DE with a negative length of a line on the video display (which is -64)
04F4
ADD HL,DE19
Add the negative length of a line on the video display in Register Pair DE (i.e., -64) to the current cursor position in Register Pair HL
04F5
RETC9
RETurn to CALLer

04F6H – Set up 32-Character mode – “DSPETB”

04F6-04F8
 ↳ DSPETB
LD A,(403DH)LD A,(CAST$)3A 3D 40
This routine is going to change the display to 32 character mode so first we need to load Register A with the 32/64 character per line flag stored at 04F6H.
Note: 403DH-4040H is used by DOS
04F9-04FA
OR 08HOR 0000 1000F6 08
Turn on some bits in Register A to adjust the value in Register A for 32 characters per line by ORing it against 0000 1000
04FB-04FD
LD (403DH),ALD (CAST$),A32 3D 40
Save the resulting value in Register A back into 403DH (the 32/64 character per line flag).
Note: 403DH-4040H is used by DOS
04FE-04FF
OUT (0FFH),AD3 FF
Send the value in Register A out the port 255 (the video/cassette)
0500
INC HL23
Increment the current cursor position in Register Pair HL
0501
LD A,L7D
Load Register A with the LSB of the current cursor position in Register L
0502-0503
AND 0FEHE6 FE
Turn off some bits so we can make the LSB value (in Register A) to be an even value (since we are in 32 character per line mode) by ANDing it against 1111 1110
0504
LD L,A6F
Load Register L with the updated value in Register A
0505
RETC9
RETurn to CALLer

0506H – Process control characters – “DSPCTL”

0506-0508
 ↳ DSPCTL
LD DE,0480HLD DE,DSPSKP11 80 04
Load Register Pair DE with the return address
0509
PUSH DED5
Save the return address in Register Pair DE on the STACK
050A-050B
CP 08HFE 08
Check to see if the character in Register A is a backspace and erase character (i.e., 08H)
050C-050D
Since CP returns C set if Register A (the character to be displayed) is less than the test value (08H), jump to 04CEH as the character to be displayed in Register A is a backspace cursor and erase character
050E-050F
CP 0AHFE 0A
Check to see if the character in Register A is less than a line feed character
0510
RET CD8
Return if the character to be displayed in Register A is less than a line feed character so we can ignore them
0511-0512
CP 0EHFE 0E
Check to see if the character to be displayed in Register A is less than or equal to a turn on the cursor character
0513-0514
Jump if the character to be displayed in Register A is less than a turn on the cursor character so that a carriage return will be displayed
0515-0516
Jump if the character to be displayed in Register A is a turn on the cursor character
0517-0518
CP 0FHFE 0F
Check to see if the character in Register A is a turn off the cursor character
0519-051A
Jump if the character to be displayed in Register A is a turn off the cursor character
051B-051C
CP 17HFE 17
Check to see if the character to be displayed in Register A is a turn on the 32 character per line mode character
051D-051E
Jump if the character to be displayed in Register A is a turn on the 32 character per line mode character
051F-0520
CP 18HFE 18
Check to see if the character to be displayed in Register A is a left arrow character
0521-0522
Jump if the character to be displayed in Register A is a left arrow character
0523-0524
CP 19HFE 19
Check to see if the character to be displayed in Register A is a right arrow character
0525-0526
Jump if the character to be displayed in Register A is a right arrow character
0527-0528
CP 1AHFE 1A
Check to see if the character to be displayed in Register A is a down arrow character
0529-052A
Jump if the character to be displayed in Register A is a down arrow character
052B-052C
CP 1BHFE 1B
Check to see if the character to be displayed in Register A is an up arrow character
052D-052E
Jump if the character to be displayed in Register A is an up arrow character
052F-0530
CP 1CHFE 1C
Check to see if the character to be displayed in Register A is a home the cursor character
0531-0532
Jump if the character to be displayed in Register A is a home the cursor character
0533-0534
CP 1DHFE 1D
Check to see if the character to be displayed in Register A is a backspace to the beginning of the line character
0535-0537
Jump if the character to be displayed in Register A is a backspace to the beginning of the line character
0538-0539
CP 1EHFE 1E
Check to see if the character to be displayed in Register A is an erase to the end of the line character
053A-053B
Jump if the character to be displayed in Register A is an erase to the end of the line character
053C-053D
CP 1FHFE 1F
Check to see if the character to be displayed in Register A is an erase to the end of the screen character
053E-053F
Jump if the character to be displayed in Register A is an erase to the end of the screen character
0540
RETC9
Return if the character to be displayed isn’t any of the above control codes

0541H – Part of the Display routine – “DSPOUT”

Output the character held in Register A and move the cursor, scrolling the screen if necessary
0541
 ↳ DSPOUT
LD (HL),A77
Put the character stored in A into the memory location stored in HL
0542
INC HL23
Increment the current cursor position in Register Pair HL
0543-0545
LD A,(403DH)LD A,(CAST$)3A 3D 40
Load Register A with the 32/64 character per line flag (stored in 403DH).
Note: 403DH-4040H is used by DOS
0546-0547
AND 08HAND 0000 1000E6 08
Turn off some bits so we can check to see if it’s 32 or 64 characters per line by ANDing it against 0000 1000
0548-0549
Jump to 054BH if it’s 64 characters per line
054A
INC HL23
Increment the current cursor position in Register Pair HL
054B
 ↳ DSPOT2
LD A,H7C
Load Register A with the MSB of the current cursor position in Register H
054C-054D
CP 40HFE 40
Check to see if the end of video memory plus one has been reached
054E
RET NZC0
Return if the end of video memory plus one hasn’t been reached
054F-0551
LD DE,FFC0H11 C0 FF
Load Register Pair DE with a negative length of a line on the video display (i.e., -64)
0552
ADD HL,DE19
Move the pointer back 1 line by adding the negative length of a line on the video display in Register Pair DE to the current cursor position in Register Pair HL
0553
PUSH HLE5
Save the current cursor position in Register Pair HL on the STACK

0554H – Part of the Display routine – “DSPROL”

Scroll the screen upward by one line
0554-0556
 ↳ DSPROL
LD DE,3C00HLD DE,DSPAD$11 00 3C
Load Register Pair DE with the starting address of video memory
0557-0559
LD HL,3C40HLD HL,DSPAD$+6421 40 3C
Load Register Pair HL with the starting address of the second line of the video memory
055A
PUSH BCC5
Save the value in Register Pair BC on the STACK
055B-055D
LD BC,03C0HLD BC,1024 – 6401 C0 03
Load Register Pair BC with the length of video memory to be moved (which is 15 lines or 960 bytes)
055E-055F
LDIRED B0
Move the last fifteen lines of video memory to the first fifteen lines of video memory to scroll 1 line
0560
POP BCC1
Get the value from the STACK and put it in Register Pair BC
0561
EX DE,HLEB
Load Register Pair HL with the starting address of the sixteenth line of video memory
0562-0563
Jump forward to 057DH to erase the last line

0564H – Part of the Display routine – “DSPCR”

Display a carriage return / line feed
0564
 ↳ DSPCR
LD A,L7D
Load Register A with the LSB of the current cursor position in Register L
0565-0566
AND C0HE6 C0
Turn off some bits in Register A so that it’s the start of the current line by ANDing it with 1100 0000
0567
LD L,A6F
Load Register L with the updated value in Register A
0568
PUSH HLE5
Save the current cursor position in Register Pair HL on the STACK
0569-056B
LD DE,0040H11 40 00
Load Register Pair DE with the length of a line on the video display (which is 40H or 64 Decimal)
056C
ADD HL,DE19
Add the length of a line on the video display in Register Pair DE to the current cursor position in Register Pair HL
056D
LD A,H7C
Load Register A with the MSB of the current cursor position in Register H
056E-056F
CP 40HFE 40
Check to see if the end of video memory plus one has been reached (which is 40H or 64 Decimal)
0570-0571
Jump to 0554H (to scroll the screen) if the end of video memory plus one has been reached
0572
POP DED1
Throw away the entry at the top of the STACK

0573H – Part of the Display routine – “DSPEOL”

Erase to the end of the line
0573
DSPEOL
PUSH HLE5
Save the new cursor position in Register Pair HL on the STACK
0574
LD D,H54
Load Register D with the MSB of the current cursor position in Register H
0575
LD A,L7D
Load Register A with the LSB of the current cursor position in Register L
0576-0577
OR 3FHOR 0011 1111F6 3F
Turn on some bits in Register A to isolate the screen line by masking the value in Register A by ORing it against 0011 1111
0578
LD E,A5F
Save the updated value in Register A in Register E
0579
INC DE13
Increment the adjusted cursor position in Register Pair DE
057A-057B
Jump forward to the line blanking code of 0580H

057CH – Part of the Display routine – “DSPEOF”

Erase to the end of the frame

To use a ROM call to clear the video screen from (including) position N – where N is an integer between 0 and 1023 (decimal), inclusive, to the end of the display, Load the HL Register with the value 3C00H + N and then CALL 057CH
057C
 ↳ DSPEOF
PUSH HLE5
Save the cursor position in Register Pair HL on the STACK.

Clear to end of frame routine. To use this routine load the HL Register Pair with the screen address from which you want the erasing to start. The DE and A registers are used
057D-057F
 ↳ DSPERF
LD DE,4000HLD DE,DSPAD$+102411 00 40
Load Register Pair DE with the end of video memory plus one (which is 4000H)
0580-0581
 ↳ DSPERA
LD (HL),20HLD (HL),” “36 20
Display a space (which is 20H) at the video memory pointer in Register Pair HL
0582
INC HL23
Increment the video memory pointer in Register Pair HL
0583
LD A,H7C
Load Register A with the MSB of the video memory pointer held in Register H
0584
CP DBA
Check to see if the MSB of the video memory pointer in Register A is the same as the MSB of the ending memory pointer in Register D
0585-0586
LOOP back to 0580H (display a space and move forward 1) if the MSB of the video memory pointer in Register A isn’t the same as the MSB of the ending memory pointer in Register D
0587
LD A,L7D
Load Register A with the LSB of the video memory pointer in Register L
0588
CP EBB
Check to see if the LSB of the video memory pointer in Register A is the same as the LSB of the ending memory pointer in Register E
0589-058A
LOOP back to 0580H (display a space and move forward 1) if the LSB of the video memory pointer in Register A isn’t the same as the LSB of the ending memory pointer in Register E
058B
POP HLE1
Get the current cursor position from the STACK and put it in Register Pair HL
058C
RETC9
RETurn to CALLer

058DH-0D8H – PRINTER DRIVER – “PRT”

According to the original ROM notes, this is the Printer Driver. On entry, Register C to hold the character to be sent to the printer, and [IX] should point to the DCB, which is laid out as follows:
  • DCB + 0 = DCB Type
  • DCB + 1 = Driver Address (LSB)
  • DCB + 2 = Driver Addres (MSB)
  • DCB + 3 = Lines Per Page (or 0 if top-of-page)
  • DCB + 4 = Line Counter
  • DCB + 5 = 0
  • DCB + 6 = “P”
  • DCB + 7 = “R”
058D
 ↳ PRT
LD A,C79
Load Register A with the character to be sent to the printer in Register C
058E
OR AB7
Set the status flags and test A to see if it is zero
058F-0590
Jump to 05D1H (get the printer status and return) if the character to be sent to the printer in Register A is equal to zero
0591-0592
CP 0BHFE 0B
Check to see if the character to be sent to the printer in Register A is a skip to the top of the form character which is CHR$(11)
0593-0594
Jump to 059FH if the character to be sent to the printer in Register A is a skip to the top of the form character (a/k/a a vertical tab)
0595-0596
CP 0CHFE 0C
Check to see if the character to be sent to the printer in Register A is a conditional skip to the top of the form character which is CHR$(12)
0597-0598
Jump to 05B4H if the character to be sent to the printer in Register A isn’t a conditional skip to the top of the form character (a/k/a isn’t a form feed)
0599
XOR AAF
Zero Register A (which causes the null character to be printed)
059A-059C
OR (IX+03H)DD B6 03
Check to see if the number of lines per page at the location of the printer device control block pointer in Register Pair IX plus three is the same as the value in Register A
059D-059E
Jump to 05B4H if zero lines are to be skipped
059F-05A1
LD A,(IX+03H)DD 7E 03
Load Register A with the number of lines per page at the location of the printer device control block in Register Pair IX plus three
05A2-05A4
 ↳ PRTVT
SUB (IX+04H)DD 96 04
Subtract the lines printed so far at the location of the printer device control block pointer in Register Pair IX plus four from the number of lines per page in Register A
05A5
LD B,A47
A now has the number of lines to skip, so load B with with the number of lines to skip (since DJNZ loops based on B)
05A6-05A8
 ↳ PRTFF
Call the GET PRINTER STATUS routine at 05D1H (which returns the status of the line printer in the status Register As 0 if the printer is ready, and otherwise with a reason code if not ready)
05A9-05AA
Loop back to 05A6H until the printer is ready
05AB-05AC
LD A,0AH3E 0A
Now the printer is ready and B has the number of lines to skip. Load Register A with a line feed character which is CHR$(10)

05AD-05AF
LD (37E8H),ALD (PRTAD$),A32 E8 37
Send the value in Register A to the printer by loading it into memory location 37E8H
05B0-05B1
Loop back to 05A6H until all of the lines have been skipped
05B2-05B3
Now all the lines have been skipped, so jump to 05CCH to reset the line counter and return
05B4
 ↳ PRTIT
PUSH AFF5
Save the character to be sent to the printer in Register A on the STACK
05B5-05B7
 ↳ PRTIT2
Call the GET PRINTER STATUS (which returns the status of the line printer in the status Register As 0 if the printer is ready, and otherwise with a reason code if not ready)
05B8-05B9
Loop back to 05B5H until the printer is ready
05BA
POP AFF1
Get the character to be sent to the printer from the STACK and put it in Register A
05BB-05BD
LD (37E8H),ALD (PRTAD$),A32 E8 37
Send the character in Register A to the printer by putting the character into 37E8H
05BE-05BF
CP 0DHFE 0D
Check to see if the character which was just sent to the printer was a carriage return
05C0
RET NZC0
If the character sent to the printer in Register A isn’t a carriage return, return out of the print routine
05C1-05C3
INC (IX+04H)DD 34 04
Now we know the printer just got a carriage return, so increment the number of lines printed so far at the location of the printer device control block pointer in Register Pair IX plus four
05C4-05C6
LD A,(IX+04H)DD 7E 04
Load Register A with the number of lines printed so far at the location of the printer device control block pointer in Register Pair IX plus four
05C7-05C9
CP (IX+03H)DD BE 03
Check to see if the number of lines printed so far in Register A is the same as the number of lines per page at the location of the printer device control block pointer in Register Pair IX plus three
05CA
LD A,C79
Load Register A with the character sent to the printer in Register C
05CB
RET NZC0
Return if the number of lines printed so far isn’t the same as the number of lines per page
05CC-05CF
 ↳ PRTOP
LD (IX+04H),04HDD 36 04 00
Zero the number of lines printed so far at the location of the printer device control block pointer in Register Pair IX plus four
05D0
RETC9
RETurn to CALLer

05D1H – Part of the Printer Routine – “PRTSTA”
Get printer status routine

A call to 05D1 will return the status of the line printer in the status Register As 0 if the printer is ready/selected, and non-zero if not ready, as follows:
  • Bit 7 = Printer Busy. 1=Busy
  • Bit 6 = Paper Status. 1= Out of paper.
  • Bit 5 = Printer Ready. 1 = Ready.
  • All other bits are not used.
05D1-05D3
LD A,(37E8H)LD A,(PRTAD$)3A E8 37
Get the status value from the printer (which is held in 37E8H) and put it in Register A
05D4-05D5
AND F0HAND 1111 0000E6 F0
Since only bits 7, 6, and 5 are used when checking status, mask out the rest of Register A by ANDing it against 1111 0000
05D6-05D7
CP 30HFE 30
Check to see if the printer is ready by comparing the ANDed value against 0011 0000 (which isolates bits 5 and 4, so the result is either ZERO if they are set or anything else if they are not)
05D8
RETC9
RETurn to CALLer

05D9H-0673H – Part of the Keyboard Routine – “KEYIN”
Keyboard Line Handler Routine

This is the most basic of the string input routines and is used by the two others (1BB3H and 0361H) as a subroutine. To use it, load HL with the required buffer address and the B Register with the maximum buffer length required. Keyboard input over the specified maximum buffer length is ignored, and after pressing the (ENTER) key it will return with HL containing the original buffer address and B with the string length.

A call to this memory location Accepts keyboard input and stores each character in a buffer supplied by caller. Input continues until either a carriage return or a BREAK is typed, or until the buffer is full. All edit control codes are recognized, e.g. TAB, BACKSPACE, etc.

On exit the registers contain: HL=Buffer address, B=Number of characters transmitted excluding last, C=Orginal buffer size, A=Last character received if a carriage return or BREAK is typed. Carry Set if break key was terminator, reset otherwise. If the buffer is full, the A Register will contain the buffer size.

Accepts keyboard input and stores each character in a buffer supplied by caller. Input continues until either a carriage return or a BREAK is typed, or until the buffer is full. All edit control codes are recognized, e.g. TAB, BACKSPACE, etc

To use a ROM call to accept a restricted number of keyboard characters for input (n), use:
LD HL,(40A7H)
LD B,n
CALL 05D9H
.

Up to n characters will be accepted, after which the keyboard will simply be ignored until the ENTER (or LEFT ARROW, or BREAK, or CLEAR) key is pressed. These characters will be stored in consecutive memory cells starting at the address contained in 40A7H-40A8H (the keyboard buffer area), with a 0DH (carriage return) byte at the end. Upon completion, the HL Register Pair will contain the address of the first character of the stored input, and the B Register will contain the number of characters entered. NOTE: No “?” is displayed as a result of the execution of the above program. If the “?” display is desired to prompt the typing of the input, precede the above program segment with:
LD A,3FH
CALL 033AH
LD A,20H
CALL 033AH


According to the original ROM comments, on entry, HL to point to the input line address in RAM and Register B to hold the maximum number of input characters to fetch. On exit, Register A should hold the number of characters entered
05D9
 ↳ KEYIN
PUSH HLE5
Save the start of the input buffer area pointer in Register Pair HL on the STACK
05DA-05DB
LD A,0EH3E 0E
Load Register A with a turn on the cursor character (which is 14)
05DC-05DE
Display a cursor by calling the DISPLAY A CHARACTER routine at 0033H (which puts the character in Register A on the video screen)
05DF
LD C,B48
Load Register C with the size of the input buffer in Register B
05E0-05E2
 ↳ KLNNXT
Call the “WAIT FOR KEYBOARD INPUT” routine at 0049H, so as to wait until a key is pressed
05E3-05E4
CP 20HCP ” “FE 20
Check to see if the key that was pressed in Register A is greater than a SPACE
05E5-05E6
Jump if the key that was pressed in Register A is displayable (i.e., greater than or equal to a SPACE)
05E7-05E8
CP 0DHFE 0D
Check to see if the key that was pressed in Register A is a CARRIAGE RETURN
05E9-05EB
Jump if the key that was pressed in Register A is a CARRIAGE RETURN
05EC-05ED
CP 1FHFE 1F
Check to see if the key that was pressed in Register A is the CLEAR key
05EE-05EF
Jump if the key that was pressed in Register A is the CLEAR key
05F0-05F1
CP 01HFE 01
Check to see if the key that was pressed in Register A is the BREAK key
05F2-05F3
Jump if the key that was pressed in Register A is the BREAK key
05F4-05F6
LD DE,05E0HLD DE,KLNNXT11 E0 05
Load Register Pair DE with the return address of 05E0H
05F7
PUSH DED5
Save the return address in Register Pair DE on the STACK
05F8-05F9
CP 08HFE 08
Check to see if the key that was pressed in Register A is a backspace (which is 08) the cursor and erase character
05FA-05FB
Jump if the key was pressed in Register A is a backspace the cursor and erase character
05FC-05FD
CP 18HFE 18
Check to see if the key that was pressed in Register A is a backspace character
05FE-05FF
Jump if the key that was pressed in Register A is a backspace character
0600-0601
CP 09HFE 09
Check to see if the key that was pressed in Register A is a tab character
0602-0603
Jump if the key that was pressed in Register A is a tab character
0604-0605
CP 19HFE 19
Check to see if the key that was pressed in Register A is a turn on the 32 character per line mode character
0606-0607
Jump if the key that was pressed in Register A is a turn on the 32 character per line mode character
0608-0609
CP 0AHFE 0A
Check to see if the key that was pressed in Register A is a line feed character of CHR$(10)
060A
RET NZC0
Return (to 05E0H) if the key that was pressed in Register A isn’t a line feed character
060B
POP DED1
Get the return address from the STACK and put it in Register Pair DE (so that it isn’t 05E0H anymore)
060C
 ↳ KLNCHR
LD (HL),A77
We now know that the key pressed is a printable character so save the key that was pressed in Register A at the location of the input buffer pointer in Register Pair HL
060D
LD A,B78
Load Register A with the length of the buffer remaining in Register B
060E
OR AB7
Check to see if there is any more of the input buffer remaining (and set status)
060F-0610
Jump to 05E0H if the end of the input buffer has been reached
0611
LD A,(HL)7E
Now we know the end of the input buffer has not been reached, so load Register A with the value at the location of the input buffer pointer in Register Pair HL
0612
INC HL23
Increment the input buffer pointer in Register Pair HL
0613-0615
Display the character by calling the DISPLAY A CHARACTER routine at 0033H (which puts the character in Register A on the video screen)
0616
DEC B05
Decrement the number of bytes remaining in the input buffer area in Register B
0617-0618
Jump to 05E0H to get the next character

0541H – Part of the Display routine – “KLNCLR”
Clear the screen

0619H-061B
 ↳ KLNCLR
Call the CLEAR SCREEN routine at 01C9H (which clears the screen, changes to 64 characters, and homes the screen)
061C
LD B,C41
Load Register B with the length of the input buffer in Register C (which resets the counter of characters transmitted)
061D
POP HLE1
Get the starting address for the input buffer area from the STACK and put it in Register Pair HL (which resets the buffer address)
061E
PUSH HLE5
Save the starting address for the input buffer area in Register Pair HL on the STACK
061F-0621
Jump to 05E0H (to get the next character, which is now the first character in the buffer)

0622H – Part of the Display routine – “KLNCNL”

Cancel the accumulated line
0622-0624
 ↳ KLNCNL
Gosub to wait for the next key and back up the input buffer pointer in Register Pair HL if necessary
0625
DEC HL2B
Backup to the previous character (the one before the CARRIAGE RETURN) by decrementing the input buffer pointer in Register Pair HL
0626
LD A,(HL)7E
Load Register A with the character at the location of the input buffer pointer in Register Pair HL
0627
INC HL23
Increment the input buffer pointer in Register Pair HL to the net availabile position
0628-0629
CP 0AHFE 0A
Check to see if the character in Register A is the line feed character of CHR$(10)
062A
RET ZC8
Return if the character in Register A is a line feed character
062B
 ↳ KLNCAN
LD A,B78
Now we know that character wasn’t a line feed, so we need to test for a buffer full. This loads Register A with the number of bytes remaining in the input buffer area in Register B
062C
CP CB9
Check to see if the number of characters remaining in the input buffer area in Register A is the same as the length of the input buffer area in Register C
062D-062E
Jump to 0622H if there is room for more characters
062F
RETC9
The buffer is full! Return

0630H – Part of the Display routine – “KLNBSP”

Backspace one character. On entry Register B to hold the number of characters received, and Register C to hold the size of the buffer
0630
 ↳ KLNBSP
LD A,B78
Load Register A with the number of bytes remaining in the input buffer area in Register B
0631
CP CB9
Compare the number of bytes remaining in the input buffer (held in Register A) against the size of the buffer (held in Register C) to see if the buffer is full
0632
RET ZC8
Return if the input buffer area is full
0633
DEC HL2B
Decrement the input buffer area pointer in Register Pair HL to backspace the previous character .
0634
LD A,(HL)7E
… and then get that character into Register A
0635-0636
CP 0AHFE 0A
Check to see if the character in Register A is the line feed character of CHR$(10)
0637
INC HL23
Increment the input buffer area pointer in Register Pair HL
0638
RET ZC8
Return if the character in Register A is a line feed character
0639
DEC HL2B
Decrement the input buffer area pointer in Register Pair HL to backspace the previous character in the buffer .
063A-063B
LD A,08H3E 08
Load Register A with a backspace of CHR$(08) and then .
063C-063E
Effectuate the backspace by calling the DISPLAY A CHARACTER routine at 0033H (which puts the character in Register A on the video screen)
063F
INC B04
Increment the number of characters remaining in the input buffer area in Register B
0640
RETC9
RETurn to CALLer

0641H – Part of the Display routine – “KLNETB”

Turn on 32 Character Mode
0641-0642
 ↳ KLNETB
LD A,17HLD A,0001 01113E 17
Load Register A with mask of 00010111 so as to turn on the 32 character per line mode character
0643-0645
Call the DISPLAY A CHARACTER routine at 0033H (which puts the character in Register A on the video screen). Since that is the 32 character per line mode, that’s what happens

0646H – Part of the Display routine – “KLNHT”

Process a horizontal tab
0646-0648
 ↳ KLNHT
Go get the cursor line position and return with it in Register A
0649-064A
AND 07H<ADD 0000 0111span class=”opcode2″>E6 07
Turn off some bits so we can mask the cursor line position in Register A by ANDing it against 0000 0111
064B
CPL2F
Inverse the value in Register A
064C
INC A3C
Increment the value in Register A so that it is 1 <= A <= 8
064D-064E
ADD 08HC6 08
Clear the upper bits of the counter
064F
LD E,A5F
Load Register E with the number of spaces to be added in Register A
0650
 ↳ KLNHTL
LD A,B78
Load Register A with the number of bytes remaining in the input buffer area in Register B
0651
OR AB7
Check to see if the buffer is full
0652
RET ZC8
Return if the input buffer is full
0653-0654
LD A,20HLD A,” “3E 20
Load Register A with a space character
0655
LD (HL),A77
Save the space character in Register A at the location of the input buffer area pointer in Register Pair HL
0656
INC HL23
Increment the input buffer area pointer in Register Pair HL
0657
PUSH DED5
Save the value in Register Pair DE on the STACK
0658-065A
Display the space by calling the DISPLAY A CHARACTER routine at 0033H (which puts the character in Register A on the video screen)
065B
POP DED1
Get the value from the STACK and put it in Register Pair DE
065C
DEC B05
Since you just displayed one of the spaces, decrement the number of bytes remaining in the input buffer area in Register B .
065D
DEC E1D
… and decrement the number of spaces to be added in Register E
065E
RET ZC8
Return if the number of spaces has been added to the input buffer
065F-0660
Loop back to 0650H until all the spaces have been added to the input buffer

0661H – Part of the Display routine – “KLNBRK”

Process a Carriage Return and Automatic Line Feed
0661
 ↳ KLNBRK
SCF37
Set the Carry flag. This is done because the routine is going to exit with the CARRY flag set as an indication that BREAK was hit
0662
 ↳ KLNCR
PUSH AFF5
Save the value in Register Pair AF on the STACK, which saves the CARRY flag
0663-0664
LD A,0DH3E 0D
Load Register A with a carriage return character
0665
LD (HL),A77
Save the carriage return character in Register A at the location of the input buffer area pointer in Register Pair HL
0666-0668
Display the carriage return by calling the DISPLAY A CHARACTER routine at 0033H (which puts the character in Register A on the video screen). Since that is a CARRIAGE RETURN, that’s what happens
0669-066A
LD A,0FH3E 0F
Load Register A with a turn off the cursor character
066B-066D
Turn off the cursor by calling the DISPLAY A CHARACTER routine at 0033H (which puts the character in Register A on the video screen)
066E
LD A,C79
Load Register A with the length of the input (=buffer size) in Register C
066F
SUB B90
Subtract the number of bytes remaining in the input buffer area in Register B from the length of the input buffer area in Register A
0670
LD B,A47
Load Register B with the number of characters in the input buffer area in Register A
0671
POP AFF1
Get the value from the STACK and put it in Register Pair AF. This also sets the CARRY flag if BREAK and unsets it if CARRIAGE RETURN
0672
POP HLE1
Get the starting address of the input buffer area from the STACK and put it in Register Pair HL
0673
RETC9
RETurn to CALLer

0674H-06D1H – INITIALIZATION ROUTINE – “INIT”

0674-0675
 ↳ INIT
OUT (0FFH),AD3 FF
Send the zero in Register A out the video/cassette port. Earlier versions of the ROM looped back to this instruction instead of the next one, which pounded 0FFH!
0676-0678
LD HL,06D2HLD HL,INITR21 D2 06
Load Register Pair HL with the starting address of the RST’s and the video/keyboard/printer DCB’s
0679-067B
LD DE,4000HLD DE,RST1$11 00 40
Load Register Pair DE with the starting address of the communications region
067C-067E
LD BC,0036HLD BC,INITRL01 36 00
Load Register Pair BC with the length of the ROM area to be moved (which is 54 bytes)
067F-0680
LDIRED B0
Move the RST’s and DCB’s (06D2H-0707H) to the RAM vector area of 4000H-4035H
0681
DEC A3D
Decrement the value in Register A
0682
DEC A3D
Decrement the value in Register A
0683-0684
Loop back to 0674H until the block move has occurred 128 times, jump to 0674H (which sends a click to the cassette port)
*0683-0684
Loop back to 0676H until the block move has occurred 128 times, jump to 0676H (which runs the routine AFTER the click, so no more click)
0685-0686
LD B,27HLD B,SIZRM$06 27
Load Register B with the number of bytes of memory to be zeroed (which is 39)
0687
 ↳ CLRAM
LD (DE),A12
Save the zero in Register A at the location of the memory pointer in Register Pair DE
0688
INC DE13
Increment the destination pointer in Register Pair DE
0689-068A
Loop back to 0687H until all of the memory locations between 4036H and 4062H have been zeroed

068BH – INITIALIZATION ROUTINE – “CHKMAN”

Check for a manual override
068B-068D
 ↳ CHKMAN
LD A,(3840H)LD A,(KEYAD$+40H)3A 40 38
Load Register A with the location of keyboard memory for the BREAK key (which is 14400)
068E-068F
AND 04H<AND 0000 0100E6 04
Turn off some bits so we can check to see if the BREAK key is being pressed
0690-0692
Jump to 0075H if the BREAK key was pressed, and flow down if it wasn’t
This is the beginning of the routine which boots a diskette
0693-0695
LD SP,407DHLD SP,TSTK$31 7D 40
Set the STACK pointer to 407DH
0696-0698
LD A,(37ECH)LD A,(FDCAD$)3A EC 37
Load Register A with the status of the disk controller (which is stored in 37ECH)
0699
INC A3C
Increment the value in Register A so that we can .
069A-069B
CP 02HFE 02
… check to see if the disk controller is present
069C-069E
Jump to the Level II Initialization Routine at 0075H if the disk controller isn’t present

069FH – Bootstrap from Diskette – “BOOT”

069F-06A0
 ↳ BOOT
LD A,01H3E 01
So now we know there is a disk controller, so lets load Register A with the unit select mask for Drive :0
06A1-06A3
LD (37E1H),ALD (DSEL$),A32 E1 37
Select Drive :0 and turn the motor on by loading 01H into 37E1H
06A4-06A6
LD HL,37ECHLD HL,FDCAD$21 EC 37
Load Register Pair HL with the address of the disk command/status Register of 37ECH
06A7-06A9
LD DE,37EFHLD DE,FDCAD$+311 EF 37
Load Register Pair DE with the address of the disk data Register of 37EFH
06AA-06AB
LD (HL),03H36 03
Load the disk command/status Register (37ECH) with command 03H (RESTORE and POSITION TO TRACK 0). More specifically, this sends 0000 0011 to the register, which is broken down as follows (left to right): Restore (0000), Do NOT load head (0), Verify Off (0), 20 ms step (11)
06AC-06AE
LD BC,0000H01 00 00
Load Register Pair BC with the delay count
06AF-06B1
Call the delay routine at 0060H (which will delay BC times 14.65; about 3 seconds)
06B2-06B3
 ↳ BOOTDL
BIT 0,(HL)CB 46
Top of a loop.Check to see if the diskette controller is busy by testing Bit 0 of 37ECH (Floppy Disk Controller Status). Bit 0 is the BUSY status bit. Per WD, commands should only be loaded into the command Register when the Busy Status but is off
06B4-06B5
Loop back to that test in 06B2H until the disk is no longer busy
06B6
XOR AAF
Zero Register A
This routine loads Drive 0, Track 0, Sector 0 into 4200H-4455H, and then jumps to 4200H
06B7-06B9
LD (37EEH),ALD (FDCAD$+2),A32 EE 37
Save the value in Register A in the disk sector Register (at 37EEH)
06BA-06BC
LD BC,4200HLD BC,MEM$01 00 42
Load Register Pair BC with the address in memory to place the sector read (which is 4200H)
06BD-06BE
LD A,8CH3E 8C
Load Register A with the command to read the sector. More specifically, send 10001100, which is broken down (from left to right) as Read Sector (100xxx00), Single Record (0), IBM Format (1), Enable HLD, HLT, and 10 msec delay
06BF
LD (HL),A77
Put the command in Register A (read the sector) in the disk command Register of 37ECH. This then reads Drive 0 Track 0 Sector 0 into 4200H-4455H
06C0-06C1
 ↳ BOOTLP
BIT 1,(HL)CB 4E
Top of a loop to check the disk command/status Register of 37ECH to see if there is data available
06C2-06C3
Loop back to the prior instruction until there is data available
06C4
LD A,(DE)1A
So now we know there is data available, so we load Register A with the byte read from the disk (i.e., the data in disk data Register of 37EFH)
06C5
LD (BC),A02
Save the value in Register A at the location of the memory pointer in Register Pair BC (4200H-4455H)
06C6
INC C0C
Increment the LSB of the memory pointer in Register C
06C7-06C8
Loop back to 06C0H until the whole 256 bytes of the sector has been read into 4200H-4455H
06C9-06CB
Now that the entire first sector has been read into 4200H-4455H, jump there!

06CCH-06CEH – Alternative re-entry point into BASIC – “RESETR”

This is an alternative re-entry point into BASIC. A JP 6CCH is often better than a jump to lA19H as the latter sometimes does strange things to any resident BASIC program
06CC-06CE
 ↳ RESETR
LD BC,1A18HLD BC,STPRDY01 18 1A
Load Register Pair BC with the starting address of the Level II BASIC READY routine (which is kept at 1A18H)
06CF-06D1
Jump to 19AEH to initialize BASIC’s variables and pointers

06D2H-0707H – ROM STORAGE LOCATION FOR DATA TO BE MOVED TO RAM BY THE INITIALIZATION PROCESS – “INITR”

06D2
 ↳ INITR
This will be 4000H – it is a jump to the RST 08H routine (COMPARE SYMBOL routine). DOS will overwrite this value
06D5
This will be 4003H – it is a jump to RST 10H (get the next character). DOS will overwrite this value
06D8
This will be 4006H – it is a jump to RST 18H (compare DE and HL). DOS will overwrite this value
06DB
This will be 4009H – it is a jump to RST 20H (tests for data type). DOS will overwrite this value
06DE
RETC9
This will be 400CH – is a RETurn from RST 28H (which is a jump to 4BA2H for DOS)
06DF
NOP
06E000
NOP
06E1
RETC9
This will be 400FH – it is a RETurn from RST 30H (which is a jump to 44B4H for DOS)
06E2
NOP
06E300
NOP
This will be 4012H – RST 38H vector DI/RET (JP 4518H for DOS)
06E4
EIFB
This is the interrupt entry point vector
06E5
RET
06E600
NOP
This is the keyboard DCB
06E7
01
Keyboard DCB + 0
06E8
03 E3
Keyboard DCB + 1 – Driver Address
06EA
00
Keyboard DCB + 3
06EB
00
Keyboard DCB + 4
06EC
00
Keyboard DCB + 5
06ED
“K”
Keyboard DCB + 6
06EE
“I”
Keyboard DCB + 7
This is the display DCB
06EF
07
Display DCB + 0
06F0-06F1
58 04
Display DCB + 1,2 – Driver Address
06F2-06F3
00 3C
Display DCB + 3,4 – Cursor Position Address
06F4
0
Display DCB + 5 – Cursor Character
06F5
“D”
Display DCB + 6
06F6
“O”
Display DCB + 7
This is the printer DCB
06F7
6
Printer DCB + 0
06F8-06F9
8D 05
Printer DCB + 1,2 – Driver Address
06FA
43
Printer DCB + 3 – Lines per page
06FB
0
Printer DCB + 4 – Line Counter
06FC
0
Printer DCB + 5
06FD
“P”
Printer DCB + 6
06FE
“R”
Printer DCB + 7
06FF
JP 5000HC3 00 50
This will be 402DH, and SYS 0 will change this to JP 4400H
0702
RST 00HC7
This will be 4030H, and SYS 0 will change this to LD A,A3H
0703
NOP
070400
NOP
This will be 4032H, and SYS 0 will change this to RST 28H
0705
LD A,00H3E 00
This will be 4033H, and SYS 0 will change this to 44BBH
0707
RET

070BH – MATH!

The math routines in the Level II ROM are fairly complex because they have to be. The following is a brief-ish description of the overall intentions of the authors:

RAM Locations / Purpose

DFACLO4Four lowest orders for double precision
FACLO3Low order of Mantissa, Middle Order of Mantissa, High Order of Mantissa
FAC2Exponent, Temporary Complement of the Sign in the MSB
ARGLO7Temporary location of second argument for double precision
ARG1
FBUFFRBuffer for FOUT

Floating Point Formula

  • The sign is the first bit of the mantissa
  • The mantissa is 24 bits long
  • THe binary point is to the left of the MSB
  • The manitssa is positive, with a one assumed to be where the sign bit is
  • The sign of the exponent is the first bit of the exponent
  • The exponent is stored in excess of 80H (i.e., it is a signed 8 bit number with 80H added to it)
  • An exponent of zero means the number is zero, and all other bytes are ignored

In memory a number looks like this:

  • Bits 17-24 of the mantissa
  • Bits 9-16 of the mantissa
  • The sign is in Bit 7
  • Bits 2-8 of the mantassa are in bits 6-0
  • The exponent is stored as a signed number + 80H
  • Bit 1 of the mantissa is always a 1

Calling Math Routines

To call a ONE argument routine, the argument should be in the FAC

To call a TWO argument routine, the first argument should be in BCDE and the second argument should be in the FAC

Regardless of which is desired, the result will be in the FAC

ROM routines with a “S” point to two argument operations which have (HL) pointing to the first argument instead of it being in BCDE. “MOVERM” is called to get the argument into the registers.

ROM routines with a “T” assume that the first argument is on the stack. “POPR” is used to get the arguments into the registers. Note: Never CALL a “T” routine, the return address will be confused with a number.

Stack Usage

The to LO’s are pushed first, and then the HO and finally the sign. The lower byte of each part is in the lower memory address, so when the number ios POPed into the registers, the higher order byte will be in the higher order register of the register pair (i.e., B, D, and H).


070BH-070FH – SINGLE PRECISION ADDITION, ACCumulator = (HL) + ACCumulator – “FADDH”

Single-precision addition (ACCumulator=(HL)+ACC) involving a buffer pointed to by the HL Register Pair and ACCumulator (i.e., 4121H-4122H). This part of the program loads the BCDE registers with the value from the buffer, then passes control to 716H.
0708-070A
 ↳ FADDH
21 80 13
LD HL,1380HLD HL,FHALF
Load Register Pair HL address of the single precision value 1/2, which is stored in ROM at 1380H. This would be applicable if the entry jump was to this address (FADDH). If the entry is to FADDS, then this wouldn’t occur.
070B-070D
 ↳ FADDS
Move the argument from (HL) into the registers via a call to 09C2H (which loads a SINGLE PRECISION value pointed to by Register Pair HL into Register Pairs BC and DE)
070E-070F
Actually do the addition via a JUMP to the SINGLE PRECISION ADD routine at 0716H (which adds the single precision value in (BC/DE) to the single precision value in the ACCumulator (i.e., 4121H-4122H). The sum is left in the ACCumulator

0710H-0712H – SINGLE PRECISION SUBTRACTION, ACCumulator = (HL) – ACCumulator
“FSUBS”

Single-precision subtraction (ACC=(HL)-ACC). This loads the BCDE registers with the value from (HL), then passes control to 713H.
0710-0712
 ↳ FSUBS
Load a SINGLE PRECISION value pointed to by Register Pair HL into Register Pairs BC and DE via a GOSUB to MOVRM

0713H-0715H – SINGLE PRECISION SUBTRACTION, ACCumulator = BCDE – ACCumulator – “FSUB”

Single-precision subtraction (ACCumulator=BCDE-ACCumulator). The routine actually inverts ACCumulator (i.e., 4121H-4122H) and adds it to the contents of the BCDE registers which, in effect, is a subtraction. The result will be stored in the ACCumulator (i.e., 4121H-4122H).

Single Precision Subtract: Subtracts the single precision value in (BC/DE) from the single precision value in the ACCumulator. The difference is left in the ACCumulator

Single-precision subtraction (ACC=BCDE-ACC). The routine actually inverts the ACC and adds it to the contents of the BCDE registers which, in effect, is a subtraction. The result will be stored in the arithmetic work area (ACC)

Note: If you wanted to subtract two single precision numbers, store the minuend in the BCDE registers and store the subtrahend in 4121H-4124H and then CALL 0713H. The result (in single precision format) is in 4121H-4124H in approximately 670 microseconds.

0713-0715
 ↳ FSUB
Go reverse the sign of the single precision value in Register Pairs BC and DE so that the addition routine just below can be used.

0716H-0752H – SINGLE PRECISION ADDITION, ACCumulator = BCDE + ACCumulator – “FADD”

Single-precision addition (ACCumulator=BCDE+ACC). This routine adds two single-precision values and stores the result in the ACCumulator area.

Note: If you wanted to add 2 single precision numbers via a ROM call, store one input into BCDE (with the exponent in B and the LSB in E) and the other into 4121H-4124H, and then call 0716H. The single precision result will be in 4121H-4124H approximately 1.3 milliseconds later.

Single Precision Add: Add the single precision value in (BC/DE) to the single precision value in the ACCumulator. The sum is left in the ACCumulator

Single-precision addition (ACC=BCDE+ACC). This routine adds two singleprecision values and stores the result in the ACC area

Formula: FAC:=ARG+FAC

Routine ALTERS A,B,C,D,E,H,L

If INTFSF=1 the format of floating point numbers will be:
  • Reg B – SIGN AND BITS 1-7 OF EXPONENT
  • Reg C – Bit 8 of exponent ;and bits 2-8 of mantissa
  • Reg D – Bits 9-16 of mantissa
  • Reg E – Bits 17-24 of mantissa, and likewise for the ACCumulator format
  • Note: The exponent for intel will be 7FH
0716
 ↳ FADD
LD A,B78
First we need to check to see if the first argument is zero, so we load Register A with the exponent of the single precision value in Register B
0717
OR AB7
Set the flags based on Register B to check to see if the single precision value in Register Pairs BC and DE is equal to zero
0718
RET ZC8
Return if the single precision value in Register Pairs BC and DE is equal to zero because the result is already in the ACCumulator.
0719-071B
LD A,(4124H)LD A,(FAC)3A 24 41
Next, we want to test to see if the exponent is zero, because if it is, then the answer is already in the registers. First, load Register A with the exponent of the single precision value in the ACCumulator (i.e., 4121H-4122H)
071C
OR AB7
Set the flags based on the exponent (now in A) is equal to zero
071D-071F
If the exponent is zero, then the result is already in BCDE, so CALL MOVFR to move the SINGLE PRECISION value in DC/DE into ACCumulator.

At this point we know that we are going to actually do the math, so the next step is to get the smaller number into the registers (BCDE) so we can just shift it rith and align the binary points of both numbers. If we do this, then we just add or subtract them bytewise.

0720
SUB B90
Subtract the value of the exponent for the single precision value in Register B from the value of the exponent for the single precision value in the ACCumulator (i.e., 4121H-4122H) in Register A so we can see which is smaller. NC will be set if BCDE < ACCumulator.
0721-0722
If the single precision value in Register Pairs BC and DE is smaller than the single precision value in the ACCumulator (i.e., 4121H-4122H), JUMP to FADD1 since they are in the right order.
0723
CPL2F
If we are here, then we want to swap the two numbers. First, we negate the shift count (adjust the difference in the exponents in Register A so that it is positive)
0724
INC A3C
Increment the difference in the exponents in Register A so that it will be the correct positive number
0725
EX DE,HLEB
Swap the ACCumulator and the Registers
0726-0728
Call 09A4 which moves the SINGLE PRECISION value in the ACCumulator to the STACK (stored in LSB/MSB/Exponent order)
0729
EX DE,HLEB
Load Register Pair DE with the 16-bit value in Register Pair HL
072A-072C
Call 09B4H which moves the SINGLE PRECISION value in DC/DE into ACCumulator
072D
POP BCC1
Next we finish the swap buyt putting the old ACCumulator into the registers with two POPs. First, get the 16-bit value from the STACK and put it in Register Pair BC
072E
POP DED1
Get the 16-bit value from the STACK and put it in Register Pair DE
At this point, the smaller number is in ABCD, so we proceed with the math.
072F-0730
 ↳ FADD1
CP 19HFE 19
The highest math we can do is 24 bits, so first check to make sure we are not going to exceed that. To do this we check to see if the difference in the exponents in Register A is greater than 24
0731
RET NCD0
If the math is going to exceed 24 bits, then we need to fail and RETurn
0732
PUSH AFF5
Save the shift count (the difference in the exponents in Register A) on the STACK
0733-0735
Set the sign bits for the single precision values and return with the equality of the sign bits in Register A
0736
LD H,A67
Save the sign bits (the subtraction flag) into Register A
0737
POP AFF1
Get shift count (the difference of the exponents) back from the STACK and put it in Register A
0738-073A
Shift the single precision value in Register Pairs BC and DE until it lines up with the single precision value in the ACCumulator
If the numbers have the same sign, then we add them. if the signs are different, then we have to subtract them. we have to do this because the mantissas are positive. judging by the exponents, the larger number is in the ACCumulator, so if we subtract, the sign of the result should be the sign of the ACCumulator; however, if the exponents are the same, the number in the registers could be bigger, so after we subtract them, we have to check if the result was negative. if it was, we negate the number in the registers and complement the sign of the ACCumulator. (here the ACCumulator is unpacked) if we have to add the numbers, the sign of the result is the sign of the ACCumulator. so, in either case, when we are all done, the sign of the result will be the sign of the ACCumulator.
073B
OR HB4
Get the subtraction flag to see if the sign bits are equal
073C-073E
LD HL,4121HLD HL,FACLO21 21 41
Load Register Pair HL with the starting address of ACCumulator
073F-0741
If the signs were differet, then we move to subtractions
0742-0744
Otherwise, we add the single precision value in BCDE to the single precision value in the ACCumulator
0745-0747
If there was NO overflow then we will JUMP away to round
0748
INC HL23
If we’re still here, then there was an overflow, but the most it can overflow is 1 bit, so we increment the memory pointer in Register Pair HL, so that it points to the exponent in the ACCumulator
0749
INC (HL)34
Increment the exponent in the ACCumulator at the location of the memory pointer in Register Pair HL
074A-074C
Check for another overflow (i.e., the exponent in the ACCumulator is too large) in which case go to 07B2H to output an OV ERROR message
074D-074E
LD L,01H2E 01
Prepare to shift the result one one bit and shift the CARRY FLAG in by load ingRegister L with the number of bits to shift the single precision result in Register Pairs BC and DE
074F-0751
Go shift the single precision result in Register Pairs BC and DE
0752-0753
Finish up by routing the results via a JUMP to 0796H

0754H-077CH – SINGLE PRECISION MATH ROUTINE – “FADD3”

This routine will subtract CDEB from ((HL)+0,1,2),0.
0754
 ↳ FADD3
XOR AAF
Zero Register A to negate the unflow byte and subtract the numbers.
0755
SUB B90
Subtract the 8-bit value in Register B from the value in Register A
0756
LD B,A47
Save the result into Register A
0757
LD A,(HL)7E
Prepare to subtract the low order numbers. First, load Register A with the value at the memory pointer in Register Pair HL
0758
SBC A,E9B
Subtract the value in Register E from the value in Register A
0759
LD E,A5F
Load Register E with the result in Register A
075A
INC HL23
Increment the memory pointer in Register Pair HL to point to the next byte (the middle order numbers) to deal with
075B
LD A,(HL)7E
Prepare to subtract the middle order numbers. First, load Register A with the value at the location of the memory pointer in Register Pair HL
075C
SBC A,D9A
Subtract the value in Register D from the value in Register A
075D
LD D,A57
Load Register D with the result in Register A
075E
INC HL23
Increment the memory pointer in Register Pair HL to point to the next byte (the high order numbers) to deal with
075F
LD A,(HL)7E
Load Register A with the value at the location of the memory pointer in Register Pair HL
0760
SBC A,C99
Subtract the value in Register C from the value in Register A
0761
LD C,A4F
Load Register C with the result in Register A
With that out the way, we need to make sure we have a positive mantissa (or else we will need to negate the number).
0762-0764
 ↳ FADFLT
If the Carry flag is set (which is to indicate that the number was negative), go convert the single precision value to a positive number
This next routine normalizes CDEB. In doing so, ABCDE and HL are all modified. This routine shifts the mantissa left until the MSB is a 1.
0765
 ↳ NORMAL
LD L,B68
Put the lowest two bytes into (HL)
0766
LD H,E63
Load Register H with the LSB of the single precision value in Register E
0767
XOR AAF
Zero Register A so that Register B can track the shift count.
0768
 ↳ NORM1
LD B,A47
Save the shift count from Register A into Register B.
0769
LD A,C79
Load Register A with the MSB of the single precision value in Register C
076A
OR AB7
Check to see if the value in Register A is equal to zero
076B-076C
So long as we have a non-Zero value, JUMP to shift one place
076D
LD C,D4A
Shift the NMSB into the MSB by loading Register C with the value in Register D
076E
LD D,H54
Shift the LSB into the NMSB by loading Register D with the value in Register H
076F
LD H,L65
Load Register H with the value in Register L
0770
LD L,A6F
Load Register L with the value in Register A
0771
LD A,B78
Load Register A with the new shift count (exponent counter) in Register B
0772-0773
SUB 08HD6 08
Subtract the number of bits just shifted from the new exponent counter in Register A
0774-0775
CP E0HFE E0
Check to see if we shifted 4 bytes of zeroes. If no (NZ) we will need to shift over 8 more.
0776-0777
If we did not shift 4 bytes of ZERO’es, shift 8 more via a loop until shift is completed
This routine will ZERO out the ACCumulator, changing only Register A in the process. A will exit as 0.
0778
 ↳ ZERO
XOR AAF
Zero Register A
0779-077B
 ↳ ZERO0
LD (4124H),ALD (FAC),A32 24 41
Make the ACCUmulator’s exponent = whatever is in A. If entered from above, then it will be 0. This is done because Level II treats a number as zero if its exponent is zero.
077C
RETC9
Return with a single precision value of zero in the ACCumulator

077DH-07A7H – SINGLE PRECISION MATH SUPPORT ROUTINE – “NORM2”

077D
 ↳ NORM2
DEC B05
Decrement the shift count (exponent counter) in Register B
077E
ADD HL,HL29
Rotate (HL) left by 1 and shift in a 0
077F
LD A,D7A
Rotate the next higher order (NMSB) left 1 as well.
0780
RLA17
Shift the NMSB in Register A left one bit and shift a bit from Register Pair HL if necessary
0781
LD D,A57
Save the adjusted NMSB in Register A into Register D
0782
LD A,C79
Load Register A with the MSB in Register C
0783
ADC A,A8F
Shift the MSB in Register A left one bit and shift a bit from Register D if necessary. The flags will get set as well.
0784
LD C,A4F
Load Register C with the adjusted value in Register A
0785-0787
 ↳ NORM3
IF the P FLAG is set, then we have more normalization to do so loop until the most significant bit of the single precision value is equal to one
If we are here, then we have a fully normalized result, so let us continue.
0788
LD A,B78
Load Register A with the new shift count (exponent counter) in Register B
0789
LD E,H5C
Load Register E with the LSB of the low order part of the single precision value
078A
LD B,L45
Load Register B with the MSB of the low order part of the single precision value
078B
OR AB7
Check to see if there were any bits shifted
078C-078D
Jump if there weren’t any bits shifted
078E-0790
LD HL,4124HLD HL,FAC21 24 41
Load Register Pair HL with the address of the exponent in the ACCumulator
0791
ADD A,(HL)86
Add the value of the original exponent at the location of the memory pointer in Register Pair HL to the number of bits shifted in Register A
0792
LD (HL),A77
Save the new exponent in Register A at the location of the memory pointer in Register Pair HL
0793-0794
Jump if exponent is too small (i.e., an underflow). This jump is to code which just zeroes out A, puts it into (4124H), and RETurns
0795
RET ZC8
Return if exponent is equal to zero. Otherwise, we will pass down to the “ROUND” routine
The “ROUND” routine rounds the result in CDEB and puts the result into the ACCumulator. All registers are affected. CDE is rounded up or down based on the MSB of Register B.
0796
 ↳ ROUND
LD A,B78
Load Register A with the LSB of the single precision value in Register B
0797-0799
 ↳ ROUNDB
LD HL,4124HLD HL,FAC21 24 41
Load Register Pair HL with the address of the exponent in the ACCumulator. Note: The FDIV ROM Routine enters the routine here at ROUNDB with Register A set differently.
079A
OR AB7
Set the status flags to enable us to see if we need if we need to round up. If the M FLAG is set (the most significant bit of the value in Register A), we will need to round up.
079B-079D
If the M FLAG is set (if the most significant bit in the value in Register A is set), GOSUB to round up
079E
LD B,(HL)46
Put the exponent (modified or not), whcih is currently held in the RAM location pointed to by HL, into Register B.
079F
INC HL23
Increment the memory pointer in Register Pair HL to now point to the sign
07A0
LD A,(HL)7E
Load Register A with the value of the sign at the location of the memory pointer in Register Pair HL
07A1-07A2
AND 80HAND 1000 0000E6 80
Turn off some bits so we can mask the sign bit in Register A (1000 0000)
07A3
XOR CA9
Set the sign bit in Register A
07A4
LD C,A4F
Load Register C with the sign
07A5-07A7
Save the number into the ACCumulator via a JUMP to 09B4H which moves the SINGLE PRECISION value in BC/DE into ACCumulator

07A8H-07B6H – SINGLE PRECISION MATH SUPPORT ROUTINE – “ROUNDA”

This is a subroutine within the ROUND round. This will add one to C/D/E.
07A8
 ↳ ROUNDA
INC E1C
Increment the LSB of the single precision value (which is stored in Register E). Note: This is the entry point from QUINT.
07A9
RET NZC0
If the NZ FLAG is set, then we have no overflow, so we are done!
07AAH
INC D14
Increment the NMSB of the single precision value (which is stored in Register D)
07AB
RET NZC0
If the NZ FLAG is set, then we have no overflow, so we are done!
07AC
INC C0C
Increment the MSB of the single precision value (which is stored in Register C).
07AD
RET NZC0
If the NZ FLAG is set, then we have no overflow, so we are done!
07AE-07AF
LD C,800E 80
If we are still here then the number overflowed all 3 registers. With this, we need to adjust the MSB of the single precision value in Register C and then …
07B0
INC (HL)34
… update the exponent (which is stored in the RAM location pointed to by HL)
07B1
RET NZC0
If the NZ FLAG is set, then we have no overflow, so we are done! If that overflowed as well, then we are out of luck and we pass through to an error.

07B2H – ?OV ERROR entry point – “OVERR”

07B2H-07B3
 ↳ OVERR
LD E,0AHLD E,ERROV1E 0A
Load Register E with an ?OV ERROR code.
07B4-07B6
Go to the Level II BASIC error routine and display an OV ERROR message if the value has overflowed

07B7H-07C2H SINGLE PRECISION MATH ROUTINE – “FADDA”

This routine adds (HL+2),)(HL+1),(HL+0) to C,D,E. This is called by FADD and FOUT.
07B7
 ↳ FADDA
LD A,(HL)7E
Load Register A with the LSB of the single precision value in the ACCumulator (pointed to by Register Pair HL)
07B8
ADD A,E83
Add Register E (the LSB of the other number being added; stored in Register E) to the LSB of the single precision value in the ACCumulator
07B9
LD E,A5F
… and put that sum into Register E
07BA
INC HL23
Onto the middle number/NMSB. Increment the memory pointer in Register Pair HL to point to the NMSB (ACCumulator + 1).
07BB
LD A,(HL)7E
Load Register A with the NMSB of the single precision value in the ACCumulator at the location of the memory pointer in Register Pair HL
07BC
ADC A,D8A
Add the NMSB of the single precision value in Register D to the NMSB of the single precision value in Register A
07BD
LD D,A57
Load Register D with the result in Register A
07BE
INC HL23
Onto the high order number/MSB. Increment the memory pointer in Register Pair HL to point to the MSB (ACCumulator + 2).
07BF
LD A,(HL)7E
Load Register A with the MSB of the single precision value in the ACCumulator at the location of the memory pointer in Register Pair HL
07C0
ADC A,C89
Add the MSB of the single precision value in Register C to the MSB of the single precision value in Register A
07C1
LD C,A4F
Load Register C with the result in Register A
07C2
RETC9
RETurn to CALLer

07C3H-07D6H – SINGLE PRECISION MATH ROUTINE – NEGR

This routine negates the number in C/D/E/B. CALLd by FADD and QUINT. Alters everything except Register H.
07C3-07C5
 ↳ NEGR
LD HL,4125HLD HL,FAC+121 25 41
Load Register Pair HL with the address of the sign flag storage location.
07C6
LD A,(HL)7E
Load Register A with the value of the sign flag at the location of the memory pointer in Register Pair HL
07C7
CPL2F
Complement the sign flag in Register A
07C8
LD (HL),A77
Save the adjusted sign flag in Register A back to (FAC+1)
07C9
XOR AAF
Zero Register A. This will allow us to zero Register L and to do negative math
07CA
LD L,A6F
Load Register L with a 0 (held in Register A) so that we can keep getting a zero back into Register A for the below math using less code.
07CB
SUB B90
NEGate the low order/LSB number by subtracting Register B from zero (held in Register A)
07CC
LD B,A47
Save that negated Register B back to Register B
07CD
LD A,L7D
Load Register A with zero
07CE
SBC A,E9B
NEGate the next highest order number by subtracting Register E from zero (held in Register A)
07CF
LD E,A5F
Save that negated Register E back to Register E
07D0
LD A,L7D
Load Register A with zero
07D1
SBC A,D9A
NEGate the next highest order number by subtracting Register D from zero (held in Register A)
07D2
LD D,A57
Save that negated Register D back to Register D
07D3
LD A,L7D
Load Register A with zero
07D4
SBC A,C99
NEGate the highest order number/MSB by subtracting Register C from zero (held in Register A)
07D5
LD C,A4F
Save that negated Register C back to Register C
07D6
RETC9
RETurn to CALLer

07D7H-07F7H – SINGLE PRECISION MATH ROUTINE – “SHIFTR”

This routine will shift the number in C/D/E right the number of times held in Register A. The general idea is to shift right 8 places as many times as is possible within the number of times in A, and then jump out to shift single bits once you can’t shift 8 at a time anymore. Alters everything except Register H.
07D7-07D8
 ↳ SHIFTR
LD B,00H06 00
Load Register B, which will hold the overflow byte, with zero to reset the overflow byte
07D9-07DA
 ↳ SHFTR1
SUB 08HD6 08
Top of a loop. For speed, first check to see if the shift counter in Register A still indicates at least 8 bits have to be shifted right
07DB-07DC
If the CARRY FLAG is set, then there isn’t room to shift 8 bytes, so we are going to need to shift only 1 byte, by JUMPing away to SHFTR2. This is the routine’s exit.
If we are here, then we are good to shift 8 bytes at once. So B to E, E to D, D to C, and then Zero out C …
07DD
LD B,E43
Load Register B with the LSB of the single precision value in Register E
07DE
LD E,D5A
Load Register E with the NMSB of the single precision value in Register D
07DF
LD D,C51
Load Register D with the MSB of the single precision value in Register C
07E0-07E1
LD C,00H0E 00
Load Register C with zero
07E2-07E3
Loop back to see if we can keep shifting

07E4 – SINGLE PRECISION MATH ROUTINE – “SHFTR2”

This routine will shift the number in C/D/E right the number of times held in Register A, but one byte at a time.
07E4-07E5
 ↳ SHFTR2
ADD 09HC6 09
Adjust the shift counter in Register A to its correct value for working with individual bits instead of bytes
07E6
LD L,A6F
Load Register L with the shift counter in Register A so that L will hold the counter for shifts at the single bit level
07E7
 ↳ SHFTR3
XOR AAF
Top of a loop. Clear the CARRY FLAG.
07E8
DEC L2D
Decrement the bit shift counter (held in Register L)
07E9
RET ZC8
Return if there are no more bits to be shifted. This is the routine’s exit.
07EA
LD A,C79
If we are here, then there are L bits to shift. First, load Register A with the High Order/MSB of the single precision value in Register C
07EB
 ↳ SHRADD
RRA1F
Shift the MSB of the single precision value in Register A one place to the right. Note: FADD enters at this point withRegister A set differently. RRA rotates the contents of Register A right one bit position, with Bit 0 going to the CARRY FLAG, and the CARRY FLAG going to Bit 7. RRA also can be used to divide a number in 2.
07EC
LD C,A4F
Save the bit shifted MSB (held in Register A) back into Register C
07ED
LD A,D7A
Load Register A with the NMSB of the single precision value in Register D
07EE
RRA1F
Shift the NMSB of the single precision value in Register A one place to the right and pick up the value of the Carry flag. RRA rotates the contents of Register A right one bit position, with Bit 0 going to the CARRY FLAG, and the CARRY FLAG going to Bit 7. RRA also can be used to divide a number in 2.
07EF
LD D,A57
Save the bit shifted NMSB (held in Register A) back into Register D
07F0
LD A,E7B
Load Register A with the LSB of the single precision value in Register E
07F1
RRA1F
Shift the LSB of the single precision value in Register A one place to the right and pick up the value of the Carry flag. RRA rotates the contents of Register A right one bit position, with Bit 0 going to the CARRY FLAG, and the CARRY FLAG going to Bit 7. RRA also can be used to divide a number in 2.
07F2
LD E,A5F
Save the bit shifted LSB (held in Register A) back into Register D
07F3
LD A,B78
Load Register A with the overflow byte (held in Register B)
07F4
RRA1F
Shift the overflow byte one place to the right and pick up the value of the Carry flag. RRA rotates the contents of Register A right one bit position, with Bit 0 going to the CARRY FLAG, and the CARRY FLAG going to Bit 7. RRA also can be used to divide a number in 2.
07F5
LD B,A47
Save the bit shifted overflow byte (held in Register A) back into Register B
07F6-07F7
Loop until all of the bits have been shifted

07F8H-07FBH – SINGLE PRECISION CONSTANT STORAGE LOCATION
– “FONE”

07F8-07FB
 ↳ FONE
00 00 00 8100
A single precision constant equal to 1.0 is stored here

07FCH-0808H – SINGLE PRECISION CONSTANTS STORAGE LOCATION 2 – “LOGCN2”

07FC
 ↳ LOGCN2
0303
The number of single precision constants which follows is stored here
07FD-0800
AA 56 19 80AA
A single precision constant equal to 0.598978650 is stored here
0801-0804
F1 22 76 80F1
A single precision constant equal to 0.961470632 is stored here
0805-0808
45 AA 38 8245
A single precision constant equal to 2.88539129 is stored here

0809H-0846H – LEVEL II BASIC LOG ROUTINE – “LOG”

The LOG(n) routine, (ACCumulator=LOG (ACCumulator)). This routine finds the natural log (base E) of the single precision value in the ACCumulator area.

The result is returned as a single precision value in the ACCumulator

To use a ROM call to find LOG(n), where X is a positive single precision variable, store the value of n in 4121H-4124H and then CALL 0809H. The result (in single precision format) is in 4121H-4124Hin approximately 19 milliseconds. NOTE: A fatal error occurs if the value of the input variable is zero or negative.
0809-080B
 ↳ LOG
Go check the sign (or zero value) of the single precision value in the ACCumulator
080C
OR AB7
Set the flags.
080D-080F
If the ACCumulator value is <= ZERO then we cannot proceed so go the Level II BASIC error routine and display a ?FC ERROR message. The SIGN routine will only return 00H, 01H, or FFH, so PE will be set if its 00H or FFH, but not 01H
0810-0812
LD HL,4124HLD HL,FAC21 24 41
Load Register Pair HL with the address of the exponent in the ACCumulator
0813
LD A,(HL)7E
Load Register A with the exponent of the single precision value in the ACCumulator (held at the location of the memory pointer in Register Pair HL)
The next two instructions are commented in the original ROM source code as: Get SQR(.5)
0814-0816
LD BC,8035H01 35 80
Load Register BC with the exponent and the MSB of a single precision constant (which is 32821)
0817-0819
LD DE,04F3H11 F3 04
Load Register DE with the NMSB and the LSB of a single precision constant (which is 1267). Register Pairs BC and DE are now equal to the single precision constant of .707107
081A
SUB B90
Remove the excess 80H (held in Register B) from the exponent of the n-value (of LOG (n)) held in Register A
081B
PUSH AFF5
Save the modified exponent to the the STACK for later
081C
LD (HL),B70
Set the exponent to 80H
The next two instructions save SQR(.5) to the STACK
081D
PUSH DED5
Save the NMSB and the LSB of the single precision value in Register Pair DE on the STACK
081E
PUSH BCC5
Save the exponent and the MSB of the single value in Register Pair BC on the STACK
081F-0831
Calculate (F-SQR(.5))/(F+SQR(.5)) where F = the number in the ACCumulator by GOSUBing to FADD which will add the x-value to the single precision constant in Register Pairs BC and DE and return with the result in the ACCumulator, by calling the SINGLE PRECISION ADD routine at 0716H (which adds the single precision value in (BC/DE) to the single precision value in the ACCumulator. The sum is left in the ACCumulator)
The next two instructions restore SQR(.5) from the STACK
0822
POP BCC1
Get the exponent and the MSB of the single precision value from the STACK and put it in Register Pair BC
0823
POP DED1
Get the NMSB and the LSB of the single precision value from the STACK and put it in Register Pair DE
The next two instructions get SQR(2)
0824
INC B04
Multiply the single precision value in Register Pairs BC and DE by two by bumping the exponent in Register B
0825-0827
Go divide the single precision value in Register Pairs BC and DE by the x-value in the ACCumulator and return with the result in the ACCumulator
0828-082A
LD HL,07F8HLD HL,FONE21 F8 07
Load Register Pair HL with the starting address of a single precision constant (which is at 2040)
082B-082D
Go subtract the x-value in the ACCumulator from the single precision constant of 1. 0 at the location of the memory pointer in Register Pair HL and return with the result in the ACCumulator
082E-0830
LD HL,07FCHLD HL,LOGCN221 FC 07
Load Register Pair HL with the starting address of a storage location for the single precision constants of a “approximation polynomial” to be used.
0831-0833
Go do a series of computations and return with the result in the ACCumulator
The next two instructions are commented in the original ROM source code as: Get -1/2
0834-0836
LD BC,8080H01 80 80
Load Register BC with the exponent and the MSB of a single precision constant
0837-0839
LD DE,0000H11 00 00
Load Register Pair DE with the NMSB and the LSB of a single precision. Register Pairs BC and DE are now equal to a single precision of -0.5
083A-083C
Add in the last constant via a GOSUB to FADD which will add the x-value in the ACCumulator to the single precision constant in Register Pairs BC and DE and return with the result in the ACCumulator, by calling the SINGLE PRECISION ADD routine at 0716H (which adds the single precision value in (BC/DE) to the single precision value in the ACCumulator. The sum is left in the ACCumulator)
083D
POP AFF1
Retrieve the original exponent from the STACK and put it in Register A
083E-0840
Go convert the value in Register A to a single precision number and add it to the x-value in the ACCumulator. Return with the result in the ACCumulator
The instructions are commented in the original ROM source code as: Get LN(2)
0841-0843
 ↳ MULLN2
LD BC,8031H01 31 80
Load Register Pair BC with the exponent and the MSB of a single precision constant
0844-0846
LD DE,7218H11 18 72
Load Register Pair DE with the NMSB and the LSB of a single precision constant. Register Pairs BC and DE are now equal to a single precision value of 0.693147
The original ROM source code had a jump to the muptlication routine; but to save bytes, the ROM was restructured to just fall into the MULTiplocation routine instead.

0847H-0891H – SINGLE PRECISION MULTIPLICATION, – “FMULT”

Single-precision multiplication (ACCumulator=BCDE*ACC or ACC = ARG * FAC)).
Multiplies the current value in the ACCumulator by the value in (BC/DE). the product is left in the ACCumulator.

Note: If you wanted to multiply two single precision numbers store one operand in the BCDE registers, the other in 4121H-4124H CALL 0847H. The result (in single precision format) is in 4121H-4124H in approximately 2.2 milliseconds.

Single Precision Multiply Multiplies the current value in the ACCumulator by the value in (BC/DE). the product is left in the ACCumulator

This routine alters every Register.
0847-0849
 ↳ FMULT
Go check to see if the single precision value in the ACCumulator is equal to zero
084A
RET ZC8
Return if the single precision value in the ACCumulator is equal to zero
084B-084C
LD L,00H2E 00
Since we don’t have a zero, the next step is to add the two exponents using L as a flag, so load Register L with 0
084D-084F
Next we need to fix up the exponents and save the numbers in the registers for faster addition.
0850
LD A,C79
Load Register A with the single precision value’s High Order/MSB in Register C
0851-0853
LD (414FH),ALD (FMLTT1),A32 4F 41
Save the MSB of the single precision value in Register A at memory location 414FH
0854
EX DE,HLEB
Load Register Pair HL with the NMSB and the LSB of the single precision value in Register Pair DE
0855-0857
LD (4150H),HLLD (FMLTT2),HL22 50 41
Save the NMSB and the LSB of the single precision value in Register Pair HL at memory locations 4150H and 4151H
0858-085A
LD BC,0000H01 00 00
Load Register Pair BC with a zero, which we will also put into Register D and Register E
085B
LD D,B50
Load Register D with the value in Register B
085C
LD E,B58
Load Register E with the value in Register B
085D-085F
LD HL,0765HLD HL,NORMAL21 65 07
Load Register Pair HL with the return address
0860
PUSH HLE5
Save the return address in Register Pair HL on the STACK
0861-0863
LD HL,0869HLD HL,FMULT221 69 08
Load Register Pair HL with the return address
0864
PUSH HLE5
Save the return address in Register Pair HL on the STACK
0865
PUSH HLE5
Save the return address in Register Pair HL on the STACK
0866-0868
LD HL,4121HLD HL,FACLO21 21 41
Load Register Pair HL with the low order/LSB address of the single precision value in the ACCumulator
0869
 ↳ FMULT2
LD A,(HL)7E
Load Register A with the byte to multiply by (on entry its the LSB of the single precision value in the ACCumulator)
086A
INC HL23
Increment the memory pointer in Register Pair HL to point to the next byte of the number in the ACCumulator
086B
OR AB7
Check to see if the LSB of the single precision value in the ACCumulator in Register A is equal to zero
086C-086D
Jump if the LSB of the single precision value in the ACCumulator is equal to zero
086E
PUSH HLE5
Save the memory pointer to the number in the ACCumulator (tracked by Register HL) on the STACK
086F-0870
LD L,08H2E 08
Load Register L with the bit shift counter
The original source code explains what is being done next. The product will be formed in C/D/E/B. This will be in C/H/L/B part of the time in order to use the “DAD” instruction. At FMULT2, we get the next byte of the mantissa in the ACCumulator to multiply by, which is tracked by HL and unchanged by FMULT2. If the byte is zero, we just shift the product 8 bits to the right. This byte is then shifted right and saved in Register D. The CARRY FLAG determines if we should add in the second factor, and, if we do, we add it to C/H/L. Register B is only used to determine which way we round. We then shift C/H/L/B right one to get ready for the next time through the loop. Note: The CARRY is shifted into the MSB of Register C. Register E has the count to determine when we have looked at all the bits of Register D.
0871
 ↳ FMULT4
RRA1F
Shift the LSB of the single precision value in the ACCumulator in Register A one place to the right. RRA rotates the contents of Register A right one bit position, with Bit 0 going to the CARRY FLAG, and the CARRY FLAG going to Bit 7. RRA also can be used to divide a number in 2.
0872
LD H,A67
Load Register H with the adjusted LSB in Register A
0873
LD A,C79
Load Register A with the MSB of the single precision value in Register C
0874-0875
If the bit was zero, don’t add in any numbers and, instead, jump forward to 0881H
0876
PUSH HLE5
Save the counters (tracked in Register Pair HL) to the STACK
0877-0879
LD HL,(4150H)LD HL,(FMLTT2)2A 50 41
Load Register Pair HL with the NMSB and the LSB of the original value in Register Pairs BC and DE
087A
ADD HL,DE19
Add the NMSB and the LSB of the total figured so far in Register Pair DE to the NMSB and the LSB of the original value in Register Pair HL
087B
EX DE,HLEB
Load Register Pair DE with the adjusted total in Register Pair HL
087C
POP HLE1
Get the counters back from the STACK and put it in Register Pair HL
087D-087F
LD A,(414FH)LD A,(FMLTT1)3A 4F 41
Load Register A with the MSB of the original value in Register Pairs BC and DE
0880
ADC A,C89
Add the MSB of the original value in Register A to the MSB of the total figured so far in Register C
0881
 ↳ FMULT5
RRA1F
Shift the adjusted MSB of the total in Register A one place to the right. RRA rotates the contents of Register A right one bit position, with Bit 0 going to the CARRY FLAG, and the CARRY FLAG going to Bit 7. RRA also can be used to divide a number in 2.
0882
LD C,A4F
Load Register C with the adjusted MSB of the total in Register A
0883
LD A,D7A
Load Register A with the NMSB of the total in Register D
0884
RRA1F
Shift the NMSB of the total in Register A one place to the right. RRA rotates the contents of Register A right one bit position, with Bit 0 going to the CARRY FLAG, and the CARRY FLAG going to Bit 7. RRA also can be used to divide a number in 2.
0885
LD D,A57
Load Register D with the adjusted NMSB of the total in Register A
0886
LD A,E7B
Load Register A with the LSB of the total in Register E
0887
RRA1F
Shift the LSB of the total in Register A one place to the right. RRA rotates the contents of Register A right one bit position, with Bit 0 going to the CARRY FLAG, and the CARRY FLAG going to Bit 7. RRA also can be used to divide a number in 2.
0888
LD E,A5F
Load Register E with the adjusted LSB of the total in Register A
0889
LD A,B78
Load Register A with the value in Register B
088A
RRA1F
Shift the value in Register A one place to the right. RRA rotates the contents of Register A right one bit position, with Bit 0 going to the CARRY FLAG, and the CARRY FLAG going to Bit 7. RRA also can be used to divide a number in 2.
088B
LD B,A47
Load Register B with the adjusted value in Register A
088C
DEC L2D
Decrement the bit counter in Register L and set the flags accordingly
088D
LD A,H7C
Load Register A with the LSB of the number we are multiplying
088E-088F
Loop until 8 bits have been shifted
0890
 ↳ POPHRT
POP HLE1
Get the memory pointer to the number to multiply by from the STACK and put it in Register Pair HL
0891
RETC9
RETurn to CALLer

0892H-0896H – SINGLE PRECISION MATH ROUTINE – “FMULT3”

This is accomplished by a circular shift of BC/DE one byte – B is lost, C is replaced by A

This is a multiply by zero, where we just shift everything 8 bits to the right.

0892
 ↳ FMULT3
LD B,E43
Load Register B with the LSB of the single precision value in Register E
0893
LD E,D5A
Load Register E with the NMSB of the single precision value in Register D
0894
LD D,C51
Load Register D with the MSB of the single precision value in Register C
0895
LD C,A4F
Load Register C with the value in Register A (which should be all 0’s, which will now be on the left)
0896
RETC9
RETurn to CALLer

0897H-08A1H – SINGLE PRECISION MATH ROUTINE
– “DIV10”

This routine divides the ACCumulator by 10. Every Register is used.
0897-0899
 ↳ DIV10
Save the number via a GOSUB to 09A4 which moves the SINGLE PRECISION value in the ACCumulator to the STACK (stored in LSB/MSB/Exponent order)
089A-089C
LD HL,0DD8HLD HL,FTEN21 D8 0D
Load Register Pair HL with the starting address of a single precision constant equal to 10
089D-089F
Move the “10” into the ACCUulator via a call to 09B1H (which moves a SINGLE PRECISION number pointed to by HL to ACCumulator)
08A0
 ↳ FDIVT
POP BCC1
Get the exponent and the MSB of the single precision value on the STACK and put it in Register Pair BC
08A1
POP DED1
Get the NMSB and the LSB of the single precision value from the STACK and put it in Register Pair DE
With the numbers in their places, we now just fall into the floating division routine.

08A2H-0903H – SINGLE PRECISION DIVISION – “FDIV”

Single-precision division (ACCumulator=BCDE/ACCumulator or ACC = ARG / ACC). If ACCumulator=0 a ” /0 ERROR ” will result.

This routine will divide the SINGLE PRECISION value in Register Pairs BC and DE by the single precision value in the ACCumulator. The result is returned in the ACCumulator. Every register is used.

To use a ROM call to divide two single precision numbers, store the dividend in registers BCDE, and the divisor in 4121H-4124H and then CALL 08A2H. The result (in single precision format) is in 4121H-4124H and then pproximately 4.8 milliseconds. Overflow or /0 will error out and return to Level II.
08A2-08A4
 ↳ FDIV
Go check to see if the single precision value in the ACCumulator is equal to zero so as to process that error.
08A5-08A7
If the SIGN routine retuns Z FLAG set, then we have a division by zero problem so JUMP to the Level II BASIC error routine and display an /0 ERROR message
08A8-08A9
LD L,FFH2E FF
Load Register L with a flag for use when subtracting the two exponents.
08AA-08AC
Go adjust the exponent in the ACCumulator for division
08AD
08AE
INC (HL)
INC (HL)34
Add two to the exponent pointed to by (HL) to correct scaling
08AF
DEC HL2B
Decrement the value of the memory pointer in Register Pair HL to now point to the High Order/MSB of the single precision number in the ACCumulator
08B0
LD A,(HL)7E
Load Register A with the MSB of the single precision value in the ACCumulator
08B1-08B3
LD (4089H),ALD (FDIVA+1),A32 89 40
Save the MSB of the single precision value in the ACCumulator in Register A at memory location 4089H
08B4
DEC HL2B
Decrement the value of the memory pointer in Register Pair HL to point to the Middle Order/NMSB of the single precision number in the ACCumulator
08B5
LD A,(HL)7E
Load Register A with the NMSB of the single precision value in the ACCumulator at the location of the memory pointer in Register Pair HL
08B6-08B8
LD (4085H),ALD (FDIVB+1),A32 85 40
Save the NMSB of the single precision value in the ACCumulator in Register A at memory location 4085H
08B9
DEC HL2B
Decrement the value of the memory pointer in Register Pair HL to point to the Low Order/LSB of the single precision number in the ACCumulator
08BA
LD A,(HL)7E
Load Register A with the LSB of the single precision value in the ACCumulator at the location of the memory pointer in Register Pair HL
08BB-08BD
LD (4081H),ALD (FDIVC+1),A32 81 40
Save the LSB of the single precision value in the ACCumulator in Register A at memory location 4081H
At this point, the memory locations are set up, and it’s time to get to work. According to the original ROM source:

The numerator will be kept in Registers B/H/L. The quotient will be formed in Registers C/D/E. To get a bit of the quotient, we first save Registers B/H/L on the stack, and then subtract the denominator that we saved in memory. The CARRY FLAG will indicate whether or not Registers B/H/L was bigger than the denominator. If Registers B/H/L are bigger, the next bit of the quotient is a one. To get the old Registers B/H/L off the stack, they are POPped into the PSW. If the denominator was bigger, the next bit of the quotient is zero, and we get the old Registers B/H/L back by POPping them off the stack. We have to keep an extra bit of the quotient in FDIVG+1 in case the denominator was bigger, in which case Registers B/H/L will get shifted left. If the MSB of Register B is one, it has to be stored somewhere, so we store it in FDIVG+1. Then the next time through the loop Registers B/H/L will look bigger because it has an extra High Order bit in FDIVG+1. We are done dividing when the MSB of Register C is a one, which occurs when we have calculated 24 bits of the quotient. When we jump to ROUND, the 25th bit of the quotient (whcih is in the MSB of Register A) determines whether we round or not. If initially the denominator is bigger than the numerator, the first bit of the quotient will be zero. This means we will go through the divide loop 26 times, since it stops on the 25th bit after the first non-zero bit of the exponent. So, this quotient will look shifted left one from the quotient of two numbers in which the numerator is bigger. This can only occur on the first time through the loop, so Registers C/D/E are all zero. So, if we finish the loop and Registers C/D/E are all zero, then we must decrement the exponent to correct for this.
08BE
LD B,C41
First, we need to get the number into B/H/L. First, load Register B with the MSB of the single precision dividend (held in in Register C)
08BF
EX DE,HLEB
Then, get the NMSB and LSB of the dividend from DE into Register Pair HL
08C0
XOR AAF
Next, we need to zero out C, D, E, and the Highest Order
08C1
LD C,A4F
Zero the MSB of the total by loading Register C with the value in Register A
08C2
LD D,A57
Zero the NMSB of the total by loading Register D with the value in Register A
08C3
LD E,A5F
Zero the LSB of the total by loading Register E with the value in Register A
08C4-08C6
LD (408CH),ALD (FDIVG+1),A32 8C 40
Zero memory location 408CH (which is holding the highest order)
08C7
 ↳ FDIV1
PUSH HLE5
Save the NMSB and LSB of the single precision dividend (held in Register Pair HL) on the STACK
08C8
PUSH BCC5
Save the MSB of the dividend in Register B on the STACK
08C9
LD A,L7D
Next we will need to subtract the number that was in the ACCumulator, so load Register A with the LSB of the dividend in Register L
08CA-08CC
Go to the Level II BASIC division routine. Note: Per the original ROM source code, this division routine was moved to RAM for speed; it didn’t HAVE to be moved!
08CD-08CE
SBC 00HDE 00
Subtract the CARRY FLAG from it
08CF
CCF3F
Set the CARRY FLAG to correspond to the next quotient bit
08D0-08D1
If we subtracted too much then the NC flag will be set, in which case we need to get the old number back! To do this, JUMP down to 08D9H (which is a mid-instruction Z-80 trick)
08D2-08D4
LD (408CH),ALD (FDIVG+1),A32 8C 40
Update the highest order number held at FDIVG+1
08D5
POP AFF1
We want to clear the previous number off the stack since the subtraction didn’t cause an error
08D6
POP AFF1
And again
08D7
SCF37
Set the CARRY FLAG so that the next bit in the quotient is a 1 to indicate that the subtraction was good
08D8
D2D2 C1 E1
Z-80 Trick – See the note at 0134H for an explanation
08D9
 ↳ FDIV2
POP BCC1
If we JUMP here, then the subtraction was too much and we need to get the old number from the STACK and put it in Register Pair BC
08DA
POP HLE1
… and get the old number back into Register Pair HL
08DB
LD A,C79
We want to see if we are done by testing Register C, so load Register A with the MSB of the total in Register C
08DC
08DD
INC A
DEC A3C
Increment and then Decrement the MSB of the total in Register A. This will set the SIGN FLAG without affecting the CARRY FLAG
08DE
RRA1F
Shift the CARRY into the MSB (held in Register A). RRA rotates the contents of Register A right one bit position, with Bit 0 going to the CARRY FLAG, and the CARRY FLAG going to Bit 7. RRA also can be used to divide a number in 2.
08DF-08E1
If we are done, JUMP back to ROUNDB
08E2
RLA17
If we are here, then we aren’t done. First, we need to get the old CARRY FLAG back via a RLA
08E3
LD A,E7B
Next, we are going to rotate EVERYTHING left 1 bit. Load Register A with the LSB of the total in Register E
08E4
RLA17
Rotate the next bit of the quotient in
08E5
LD E,A5F
Load Register E with the adjusted LSB of the total in Register A
08E6
LD A,D7A
Load Register A with the NMSB of the total in Register D
08E7
RLA17
Rotate the next bit of the quotient in
08E8
LD D,A57
Load Register D with the adjusted NMSB of the total in Register A
08E9
LD A,C79
Load Register A with the MSB of the total in Register C
08EA
RLA17
Rotate the next bit of the quotient in
08EB
LD C,A4F
Load Register C with the adjusted MSB of the total in Register A
08EC
ADD HL,HL29
Almost done! Rotate a zero into the right end of the number
08ED
LD A,B78
Next, rotate the High Order/MSB of the dividend in Register B
08EE
RLA17
Rotate the next bit of the quotient in
08EF
LD B,A47
Load Register B with the adjusted MSB of the dividend in Register A
08F0-08F2
LD A,(408CH)LD A,(FDIVG+1)3A 8C 40
Next, rotate the HIGHEST order. Load Register A with the value at memory location 408CH
08F3
RLA17
Rotate the next bit of the quotient in
08F4-08F6
LD (408CH),ALD (FDIVG+1),A32 8C 40
Save the adjusted value in Register A at memory location 408CH
08F7
LD A,C79
Next we need to add one to the exponent if the first subtraction didn’t work. To do so, first load Register A with the MSB of the total in Register C
08F8
OR DB2
Combine the NMSB of the total in Register D with the value in Register A
08F9
OR EB3
Combine the LSB of the total in Register E with the value in Register A
08FA-08FB
Jump back to 08C7H if the total isn’t equal to zero
08FC
PUSH HLE5
Save the NMSB and the LSB of the dividend in Register Pair HL on the STACK
08FD-08FF
LD HL,4124HLD HL,FAC21 24 41
Load Register Pair HL with the address of the exponent in the ACCumulator
0900
DEC (HL)35
Decrement the exponent in the ACCumulator at the location of the memory pointer in Register Pair HL
0901
POP HLE1
Get the NMSB and the LSB of the dividend from the STACK and put it in Register Pair HL
0902-0903
Keep dividing if there was no overflow by JUMPING back to 08C7H if the exponent in the ACCumulator isn’t equal to zero
0904-0906
Display an ?OV ERROR

0907H-0913H – DOUBLE PRECISION MATH ROUTINE – “MULDVS”

This routine is to check for special cases and to add exponents for the FMULT and FDIV routines. Registers A, B, H and L are modified.
0907-0908
 ↳ MULDVS
LD A,FFH3E FF
This is the entry point from the DDIV routine. With this, we need to set up to subtract exponents. To do this we load Register A with an appropriate bit mask
0909
LD L,0AFH2E AF
Z-80 Trick. If we are passing through, the Register L will change, but the XOR in 090A will not trigger.
090A
 ↳ MULDVA
XOR AAF
This is the entry point from the DMULT routine. With this, we need to set up to ADD exponents. To do this we load Register A with an appropriate bit mask
090B-090D
LD HL,412DHLD HL,ARG-121 2D 41
Load Register Pair HL with the address of the SIGN and the High/Order MSB in ARG (a/k/a REG 2) (a/k/a ARG)
090E
LD C,(HL)4E
Load Register C with the High Order/MSB and the sign of the value in ARG (a/k/a REG 2) for unpacking
090F
INC HL23
Increment the value of the memory pointer in Register Pair HL to now point to the exponent
0910
XOR (HL)AE
Get the exponent by XORing the mask in Register A (which varied based on where this routine was entered from)
0911
LD B,A47
Save the adjusted exponent into Register B for processing below
0912-0913
LD L,00H2E 00
Load Register L with a 00H which will indicate that the below routine needs to ADD the exponents and then pass through to the MULDIV routine

0914H-0930H – SINGLE PRECISION MATH ROUTINE – “MULDIV”

0914
 ↳ MULDIV
LD A,B78
First we should test to make sure that the number isn’t zero, so Load Register A with the exponent in Register B
0915
OR AB7
Check to see if the exponent in Register A is equal to zero
0916-0917
If the exponent in Register A is equal to zero then we just need to ZERO out the ACCumulator and we are done. Do that by JUMPing to 0937H
0918
LD A,L7D
Next, we need to determine if we are ADDing or SUBtracting, which is held in Register L. So load Register A with the bit mask in Register L
0919-091B
LD HL,4124HLD HL,FAC21 24 41
Load Register Pair HL with the address of the exponent in the ACCumulator
091C
XOR (HL)AE
Combine the value of the exponent at the location of the memory pointer in Register Pair HL with the bit mask in Register A (formerly of Register L)
091D
ADD A,B80
Add the value of the exponent in Register B to the value of the exponent in Register A
091E
LD B,A47
Load Register B with the combined exponents (currently held in Register A)
091F
RRA1F
Shift the value of the combined exponents in Register A one place to the right so that we can check for an overflow. RRA rotates the contents of Register A right one bit position, with Bit 0 going to the CARRY FLAG, and the CARRY FLAG going to Bit 7. RRA also can be used to divide a number in 2.
0920
XOR BA8
Check to see if the Carry flag was set by combining the two exponents. This will cause an overflow if the sign is the same as the carry.
0921
LD A,B78
Load Register A with the combined/summed exponents value in Register B
0922-0924
If we have an overflow, Jump away to 0936H
0925-0926
ADD 80HC6 80
If we don’t have an overflow, then we need to make an exponent in excess of 80H (and turn on bit 8)
0927
LD (HL),A77
Save the value of the combined exponent in Register A as the exponent in the ACCumulator at the location of the memory pointer in Register Pair HL
0928-092A
If the ADD 80H triggered a ZERO FLAG, then we have an underflow! Jump to POPHRT to put the numbers back and RETurn
092B-092D
Unpack the arguments by a GODUB to UNPACK, which will turn on the sign bit of the MSB in the ACCumulator and Register B and save the sign bits
092E
LD (HL),A77
Save the new sign (held in Register A) to the ACCumulator at the location of the memory pointer in Register Pair HL
092F
 ↳ DCXHRT
DEC HL2B
Decrement the memory pointer in Register Pair HL so that it points to the exponent in the ACCumulator
0930
RETC9
RETurn to CALLer, with the HIGH ORDER/MSB in Register A

0931H-093DH – SINGLE PRECISION MATH ROUTINE – “MLDVEX”

This routine is called from EXP. If jumped here will checks if ACC=0. If so, the Z flag will be set
0931-0933
 ↳ MLDVEX
Go check the value of the sign bit for the value in the ACCumulator and choose UNDERFLOW if negative
0934
CPL2F
Pick OVERFLOW if it was positive
0935
POP HLE1
Get the value from the STACK and put it in Register HL
0936
 ↳ MULDV1
OR AB7
Weneed to test to see if the error was an OVERFLOW or an UNDERFLOW, so set the flags according to the value of the sign bit test
0937
 ↳ MULDV2
POP HLE1
Clean the old RETurn address off the stack
0938-093A
If the value in the ACCumulator is negative, JUMP to 0778H to handle the underflow
093B-093D
If its not negative, jump to 07B2H to throw an error because we have an overflow

093EH-0954H – SINGLE PRECISION MATH ROUTINE – “MUL10”

This routine multiplies the ACCumulator by 10. Every register is modified.
093E-0940
 ↳ MUL10
Call 09BF which loads the SINGLE PRECISION value in the ACCumulator into Register Pair BC/DE
0941
LD A,B78
Load Register A with the value of the exponent (from Register B)
0942
OR AB7
Check to see if the exponent in Register A is equal to zero, because if the exponent is 0 then so is the number!
0943
RET ZC8
If the single precision value in Register Pairs BC and DE is equal to zero, then RETurn
0944-0945
ADD 02HC6 02
Multiply the value of the exponent in Register A by four (by adding 2 to the exponent)
0946-0948
Display an ?OV ERROR if the adjusted exponent in Register A is too large
0949
LD B,A47
Put the exponent back into Register B
094A-094C
Multiply the number by 5 by adding the original value in the ACCumulator to the adjusted value in Register Pairs BC and DE and return with the original result in the ACCumulator by calling the SINGLE PRECISION ADD routine at 0716H (which adds the single precision value in (BC/DE) to the single precision value in the ACCumulator. The sum is left in the ACCumulator)
094D-094F
LD HL,4124HLD HL,FAC21 24 41
Prepare to add 1 to the expenent (to thus multiply it by 2, which is then 10 times the original number). First, load Register Pair HL with the address of the exponent in the ACCumulator
0950
INC (HL)34
Increment the value of the exponent in the ACCumulator at the location of the memory pointer in Register Pair HL. ACCumulator now holds the original value times ten
0951
RET NZC0
Return if the new value in the ACCumulator is in an acceptable range
0952-0954
Display an ?OV ERROR if the value of the exponent at the location of the memory pointer in Register Pair HL is too large

0955H-0963H – SINGLE PRECISION MATH ROUTINE – “SIGN”

Puts the SIGN of the ACCumulator into Register A. Only Register A is modified by this routine; the ACCumulator is left untouched.

To take advantage of the RST instructions to save bytes, FSIGN is defined to be an RST. “FSIGN” is equivalent to “call sign” the first few instructions of SIGN (the ones before SIGNC) are done in the 8 bytes at the RST location.
0955-0957
 ↳ SIGN
LD A,(4124H)LD A,(FAC)3A 24 41
Prepare to check to see if the number in the ACCumulator is ZERO by loading Register A with the value of the exponent in the ACCumulator
0958
OR AB7
Check to see if the exponent in Register A is equal to zero
0959
RET ZC8
Return if the single precision value in the ACCumulator is equal to zero
095A-095C
 ↳ SIGNC
LD A,(4123H)LD A,(FAC-1)3A 23 41
Load Register A with the SIGN of the ACCumulator
095D-095E
CP 2FHFE 2F
Z-80 Trick. If passing through, this will check the value of Register A and skip the next CPL instruction.
095E
 ↳ FCOMPS
CPL2F
Complement the sign. This is ignored if passing through and proceesed only if specifically jumped to.
095F
 ↳ ICOMPS
RLA17
Put the value of the sign bit in Register A into the CARRY FLAG
0960
“SIGNS”
SBC A,A9F
If the CARRY FLAG is 0 (i.e., POSITIVE), then make Register A = 0. If the CARRY FLAG is 1 (i.e., NEGATIVE), make Register A = FFH
0961
RET NZC0
If the CARRY FLAG was 1, then the number is negative, and we want to RETurn
0962
 ↳ INRART
INC A3C
Increment the value in Register A so that Register A will be equal to 1 if the single precision value in the ACCumulator is positive
0963
RETC9
RETurn to CALLer

0964H-0976H – SINGLE PRECISION MATH ROUTINE – “FLOAT”

This routine will take a signed integer held in Register A and turn it into a floating point number. All registers are modified.
0964-0965
 ↳ FLOAT
LD B,88H06 88
Load Register B with an exponent for an integer value
0966-0968
LD DE,0000H11 00 00
Load Register Pair DE with zero
This routine will float the singed number in B/A/D/E. All registers are modified.
0969-096B
 ↳ FLOATR
LD HL,4124HLD HL,FAC21 24 41
Load Register Pair HL with the address of the exponent in the ACCumulator
096C
LD C,A4F
Load Register C with the High Order/MSB of the integer value
096D
LD (HL),B70
Save the exponent in Register B into the ACCumulator at the location of the memory pointer in Register Pair HL
096E-096F
LD B,00H06 00
Load Register B with zero to zero the overflow byte
0970
INC HL23
Increment the memory pointer in Register Pair HL to now point to the sign of the number in the ACCumulator
0971-0972
LD (HL),80H36 80
Assume a positive number by putting an 80H there
0973
RLA17
Shift the value of the sign bit into the CARRY FLAG
0974-0976
Jump to 0762H to float the number

0977H-0989H – LEVEL II BASIC ABS() ROUTINE – “ABS”

ABS routine (ACCumulator=ABS(ACCumulator)) input and output can be integer, single-precision or double-precision, depending on what is placed in the NTF (NTF=2, 4 or 8).
A call to 0977H converts the value in Working Register Area 1 (the ACCumulator) to its positive equivalent. The result is left in the ACCumulator. If a negative integer greater than 2** 15 is encountered, it is converted to a single precision value. The data type or mode flag (40AFH) will be updated to reflect any change in mode. All registers are modified.

NOTE: To use a ROM call to find ABS(X),store the value of X in 4121H-4122H (integer), in 4121H-4124H (single precision), or in 411DH and then H (double precision), and store the variable type (2, 4, or 8, respectively) in 40AFH. Then CALL 0977H. The result (in the same format as the input variable) is in the same locations in which the input variable was stored. If the input was an integer, the result is also in the HL Register Pair.

ABS routine (ACC=ABS(ACC)) input and output can be integer, single-precision or double-precision, depending on what is placed in the NTF (NTF=2, 4 or 8). (For a definition of NTF, see Part 2.)

Absolute Value: Converts the value in Working Register Area 1 (ACCumulator) to its positive equivalent. The result is left in the ACCumulator. If a negative integer greater than 2**15 is encountered, it is converted to a single precision value. The data type or mode flag (40AF) will be updated to reflect any change in mode
0977-0979
 ↳ ABS
GOSUB to VSIGN to get the SGN of the ACCumulator into Register A
097A
RET PF0
If that sign is POSITIVE, then I guess we are done, so just RETurn
This routine will negate any value in the ACCumulator. Every Register is affected.
097B
 ↳ VNEG
We need to check the value of the current number type flag, so we call the TEST DATA MODE routine at RST 20H which determines the type of the current value in the ACCumulator and returns a combination of STATUS flags and unique numeric values in the register A according to the data mode flag (40AFH). The results are returned as follows:
Variable
Type
FlagsRegister A
IntegerNZ/C/M/E-1
StringZ/C/P/E0
Single PrecisionNZ/C/P/O1
Double PrecisionNZ/NC/P/E5
097C-097E
If that test showed INTEGER, JUMP to 0C5BH to negate an integer
097F-0981
If that test showed STRING, Display a ?TM ERROR message
This routine will negate the single or double precision number in the ACCumulator. Registers A, H, and L are affected.
  • To use this routine, the number must already be PACKed.
  • 0982-0984
     ↳ NEG
    LD HL,4123HLD HL,FAC-121 23 41
    Load Register Pair HL with the address of the MSB (which holds the SIGN bit) in the ACCumulator.
    0985
    LD A,(HL)7E
    Load Register A with the MSB (which holds the SIGN bit) in the ACCumulator at the location of the memory pointer in Register Pair HL
    0986-0987
    XOR 80HXOR 1000 0000EE 80
    Complement the sign bit in the MSB in Register A. Since we know the number is negative, this is really just switching it to positive.
    0988
    LD (HL),A77
    Save the adjusted MSB (which holds the SIGN bit) in Register A in the ACCumulator at the location of the memory pointer in Register Pair HL
    0989
    RETC9
    RETurn to CALLer

    098AH-0993H – LEVEL II BASIC SGN() ROUTINE – “SGN”

    SGN function (ACCumulator=SGN(ACCumulator)). After execution, NTF=2 and ACCumulator=-l, 0 or 1 depending on sign and value of ACC before execution. Registers A, H, and L are affected.

    NOTE: To use a ROM call to find SGN(X), store the value of X in 4121H-4122H (integer), in 4121H-4124H (single precision), or in, s-4124H (double precision) and then store the variable type (2, 4, or 8, respectively) in 40AFH and then CALL 098AH. The result (in integer format) is in 4121H-4122H and in the HL Register Pair.

    SGN function (ACC=SGN(ACC)). After execution, NTF=2 and ACC=-l, 0 or 1 depending on sign and value of ACC be fore execution. 0994 This routine checks the sign of the ACC. NTF must be set. After execution A register=00 if ACC=0, A=01 if ACC > 0 or A=FFH if A < 1. The Flags are also valid
    098A-098C
     ↳ SGN
    Get the sign of the ACCumulator into Register A
    This routine will convert a signed number (held in Register A) into an integer.
    098D
     ↳ CONIA
    LD L,A6F
    Load Register L with the result of the sign test in Register A
    098E
    RLA17
    Shift the sign bit in Register A into the Carry flag
    098F
    SBC A,A9F
    Adjust the value in Register A so that it will be equal to zero if the current value in the ACCumulator is positive and equal to -1 if the current value in the ACCumulator is negative
    0990
    LD H,A67
    Save the adjusted value in Register A in Register H
    0991-0993
    Jump to 0A9AH to return the result and set the VALTYP

    0994H-09A3H – LEVEL II BASIC MATH ROUTINE – “VSIGN”

    This routine checks the sign of the ACCumulator. NTF must be set. After execution A register=00 if ACCumulator=0, A=01 if ACC > 0 or A=FFH if A < 1. The Flags are also valid.
    0994
     ↳ VSIGN
    We need to check the value of the current number type flag, so we call the TEST DATA MODE routine at RST 20H which determines the type of the current value in the ACCumulator and returns a combination of STATUS flags and unique numeric values in the register A according to the data mode flag (40AFH). The results are returned as follows:
    Variable TypeFlagsRegister A
    IntegerNZ/C/M/E-1
    StringZ/C/P/E0
    Single PrecisionNZ/C/P/O1
    Double PrecisionNZ/NC/P/E5
    0995-0997
    If that test showed STRING, Display a ?TM ERROR message
    0998-099A
    Since P means string, single precision, or double precision; and if it was a string it would have jumped already, this line says jump to 0955H if the current value in the ACCumulator is single precision or double precision, as those are processed the same way
    099B-099D
    LD HL,(4121H)LD HL,(FACLO)2A 21 41
    At this point, we know we have an integer. Load Register Pair HL with the integer value in the ACCumulator
    This routine finds the sign of the value held at (HL). Only Register A is altered.
    099E
     ↳ ISIGN
    LD A,H7C
    Load Register A with the MSB (which holds the SIGN bit) of the integer value in Register H
    099F
    OR LB5
    Check to see if the integer value in the ACCumulator is equal to zero
    09A0
    RET ZC8
    Return if the integer value in the ACCumulator is equal to zero
    09A1
    LD A,H7C
    If its not zero, then the sign of the number is the same as the sign of Register H so load Register A with the MSB (which holds the SIGN bit) of the integer value in Register H
    09A2-09A3
    Jump to 095FH to set Register A accordingly

    09A4H-09B0H – SINGLE PRECISION MATH ROUTINE – “PUSHF”

    Move ACCumulator To STACK: Moves the single precision value in the ACCumulator to the STACK. It is stored in LSB/MSB/Exponent order. Registers D and E are affected. Note, the mode flag is not tested by the move routine, it is simply assumed that ACCumulator contains a single precision value

    Loads Single-precision value from ACC to STACK ((SP)=ACC). To retrieve this value, POP BC followed by POP DE. A, BC and HL are unchanged by this function.
    09A4
     ↳ PUSHF
    EX DE,HLEB
    Preserve (HL) by swapping HL and DE
    09A5-09A7
    LD HL,(4121H)LD HL,(FACLO)2A 21 41
    Load Register Pair HL with the LSB and the NMSB of the single precision value in the ACCumulator
    09A8
    EX (SP),HLE3
    Swap (SP) and HL so that the return address is now in HL and the NMSB and the LSB of the single precision value are now at the top of the STACK
    09A9
    PUSH HLE5
    Save the return address in Register Pair HL on the STACK
    09AA-09AC
    LD HL,(4123H)LD HL,(FAC-1)2A 23 41
    Load Register Pair HL with the exponent and the High Order/MSB of the single precision value in the ACCumulator
    09AD
    EX (SP),HLE3
    Swap (SP) and HL so that the return address is now in HL and the MSB of the single precision value is now at the top of the STACK
    09AE
    PUSH HLE5
    Save the return address in Register Pair HL on the STACK
    09AF
    EX DE,HLEB
    Restore the original Register Pair HL from DE
    09B0
    RETC9
    RETurn to CALLer

    09B1H-09BEH – SINGLE PRECISION MATH ROUTINE – “MOVFM”

    This routine moves a number from memory (pointed to by HL) into the ACCumulator — (ACCumulator=(HL)). All registers except Register A are affected, with HL = HL + 4 on exit.
    09B1-09B3
     ↳ MOVFM
    Load the SINGLE PRECISION value pointed to by Register Pair HL into Register Pairs BC/DE via a CALL to 09C2H Then fall into the MOVFR routine.
    This routine loads the ACC with the contents of the BC and DE Register Pairs. (ACC=BCDE). Only Registers D and E are modified.

    Move SP Value In BC/DC Into ACCumulator: Moves the single precision value in BC/DE into ACCumulator. HL is destroyed BC/DE is left intact. Note – the mode flag is not updated!
    09B4
     ↳ MOVFR
    EX DE,HLEB
    Load Register Pair HL with the NMSB and the LSB of the single precision value in Register Pair DE.
    09B5-09B7
    LD (4121H),HLLD (FACLO),HL22 21 41
    Save the NMSB and the LSB of the single precision value into the ACCumulator (at the locations pointed to by Register Pair HL)
    09B8
    LD H,B60
    Let HL = BC (so the High Orders/MSB + Exponent) … part 1 …
    09B9
    LD L,C59
    … part 2
    09BA-09BC
    LD (4123H),HLLD (FAC-1),HL22 23 41
    Save the exponent and the MSB of the single precision value into the ACCumulator pointed to by Register Pair HL
    09BD
    EX DE,HLEB
    Restore the original HL from DE
    09BE
    RETC9
    RETurn to CALLer

    09BFH-09CAH – SINGLE PRECISION MATH ROUTINE – “MOVRF”

    This routine is the opposite of the 09B4H routine. It loads four bytes from ACCumulator (single-precision) into the BC/DE Register Pairs. Only Register A is unchanged.

  • Loads A SP Value From ACCumulator Into BC/DE: Loads a single precision value from ACCumulator into BC/DE. Note, the mode flag is not tested by the move routine. It is up to the caller to insure that ACCumulator actually contains a single precision value

    This routine is the opposite of the 9B4H routine. It loads four bytes from the ACC (single-precision) into the BC and DE Register Pairs. (BCDE=ACC). A is unchanged
  • Load A SP Value Into BC/DE: Loads a single precision value pointed to by HL into BC/DE. Uses all registers

    On Exit, HL = HL + 4

    This routine will load the BCDE Register Pairs with four bytes from the location pointed to by HL. (BCDE=(HL)). With these types of data movements, the E Register is loaded with the LSB and the B register. with the MSB

    09C2
     ↳ MOVRM
    LD E,(HL)5E
    Load Register E with the LSB of the single precision value in the ACCumulator at the location of the memory pointer in Register Pair HL.

    This routine will load the BCDE Register Pairs with four bytes from the location pointed to by HL. (BCDE=(HL)). With these types of data movements, the E Register is loaded with the LSB and the B register. with the MSB
    09C3
    INC HL23
    Increment the value of the memory pointer in Register Pair HL to point to the middle order/NMSB number
    09C4
     ↳ GETBCD
    LD D,(HL)56
    Load Register D with the NMSB of the single precision value in the ACCumulator at the location of the memory pointer in Register Pair HL
    09C5
    INC HL23
    Increment the value of the memory pointer in Register Pair HL to point to the high order/MSB number
    09C6
    LD C,(HL)4E
    Load Register C with the MSB of the single precision value in the ACCumulator at the location of the memory pointer in Register Pair HL
    09C7
    INC HL23
    Increment the value of the memory pointer in Register Pair HL to point to the exponent
    09C8
    LD B,(HL)46
    Load Register B with the exponent of the single precision value in the ACCumulator at the location of the memory pointer in Register Pair HL
    09C9
     ↳ INXHRT
    INC HL23
    Increment the value of the memory pointer in Register Pair HL so that it points to the beginning of the next number
    09CA
    RETC9
    RETurn to CALLer

    09CBH-09D1H – SINGLE PRECISION MATH ROUTINE – “MOVMF”

    This routine is the opposite of the 09B1H routine. It loads the number from the ACCumulator to the memory location pointed to by HL. ((HL)=ACC). Modifies all Registers except for Register C
    09CB-09CD
     ↳ MOVMF
    LD DE,4121HLD DE,FACLO11 21 41
    Load Register Pair DE with the starting address for a single precision value in the ACCumulator. Then pass throgh to the following routine.
    Data move routine. This moves four bytes from the location pointed to by DE into the location pointed to by HL. ((HL)=(DE)). Modifies all Registers except for Register C
    09CE-09CF
     ↳ MOVE
    LD B,04H06 04
    Load Register B with the number of bytes to be moved for a single precision value so that B will act as a counter.
    09D0-09D1
    Jump to 09D7H (which is the GENERAL PURPOSE MOVE routine and moves the contents of the B Register Bytes from the address in DE to the address in HL)

    09D2H-09DEH – MOVE VALUE POINTED TO BY HL TO THE LOCATION POINTED TO BY DE – “MOVVFM”

    This is the VARIABLE MOVE routine which moves the number of bytes specified in the variable type flag (40AFH) from the address in DE to the address in HL. Uses A, B, DE and HL.

    Data move routine. The location pointed to by DE is loaded with bytes from the location pointed to by HL. The number of bytes moved is determined by the value in the NTF. ((DE)=(HL))
    09D2
     ↳ MOVVFM
    EX DE,HLEB
    Exchange the value in Register Pair HL with the value in Register Pair DE, and then fall through to the VMOVE routine.
    This routine is similar to 9D2H above. The only difference is that it moves data in the opposite direction. ((HL) = (DE))
    09D3-09D5
     ↳ VMOVE
    LD A,(40AFH)LD A,(VALTYP)3A AF 40
    Load Register A with the current value of the number type flag (which is in 40AFH). This, not coincidentally, is also the length of the number being worked on!
    09D6
    LD B,A47
    Load Register B with the number of bytes to be moved in Register A.
    This routine is the same as 9D6H except that the number of bytes shifted is determined by the value in the B Register ((HL)=(DE))
  • Moves contents of B-register bytes from the address in DE to the address given in HL. Uses all registers except C
  • 09D7
     ↳ MOVE1
    LD A,(DE)1A
    Top of a loop to move (DE)’s content into (HL). First, load Register A with the value at the location of the memory pointer in Register Pair DE.

    This routine is the same as 9D6H except that the number of bytes shifted is determined by the value in the B Register ((HL)=(DE)).
    This is the GENERAL PURPOSE MOVE routine and moves the contents of the B Register Bytes from the address in DE to the address in HL)
    09D8
    LD (HL),A77
    and then Save the value in Register A at the location of the memory pointer in Register Pair HL
    09D9
    INC DE13
    Increment the value of the memory pointer in Register Pair DE
    09DA
    INC HL23
    Increment the value of the memory pointer in Register Pair HL
    09DB
    DEC B05
    Decrement the value of the byte counter in Register B
    09DC-09DD
    Loop until all of the bytes have been moved
    09DE
    RETC9
    RETurn to CALLer

    09DFH-09F3H – SINGLE PRECISION MATH ROUTINE – “UNPACK”

    This routine “UNPACKS” the ACCumulator and the Registers. Registers A, C, H, and L are altered.

    When the number in the ACCumulator is unpacked, the assumed one in the mantissa is restored, and the complement of the sign is placed in ACCumulator+1.
    09DF-09E1
     ↳ UNPACK
    LD HL,4123HLD HL,FAC-121 23 41
    Load Register Pair HL with the address of the MSB (including the SIGN) of the value in the ACCumulator
    09E2
    LD A,(HL)7E
    Load Register A with the MSB (and SIGN) of the value in the ACCumulator at the location of the memory pointer in Register Pair HL
    09E3
    RLCA07
    Duplicate the sign into the CARRY and the LSB
    09E4
    SCF37
    Set the Carry flag to restore the hidden “1” for the mantissa
    09E5
    RRA1F
    Turn off the sign bit in Register A by moving the value of the Carry flag into Register A and moving the previous value of the sign bit from bit 0 of Register A into the Carry flag. RRA rotates the contents of Register A right one bit position, with Bit 0 going to the CARRY FLAG, and the CARRY FLAG going to Bit 7. RRA also can be used to divide a number in 2.
    09E6
    LD (HL),A77
    Save the adjusted High Order/MSB+Sign in Register A in the ACCumulator at the location of the memory pointer in Register Pair HL
    09E7
    CCF3F
    Invert the value of the sign bit in the Carry flag
    09E8
    RRA1F
    Move the inverted sign bit from the Carry flag into Register A. RRA rotates the contents of Register A right one bit position, with Bit 0 going to the CARRY FLAG, and the CARRY FLAG going to Bit 7. RRA also can be used to divide a number in 2.
    09E9
    INC HL
    INC HL23
    Increment the value of the memory pointer in Register Pair HL twice to now point to the temporary sign byte
    09EB
    LD (HL),A77
    Save the complemented sign (in Register A) to the location of the memory pointer in Register Pair HL
    09EC
    LD A,C79
    Load Register A with the MSB+SIGN of the single precision value in Register C
    09ED
    RLCA07
    Duplicate the sign in both the CARRY FLAG and the LSB
    09EE
    SCF37
    Set the Carry flag to restore the hidden “1” for the mantissa
    09EF
    RRA1F
    Restore the High Order (MSB+Sign) in A. RRA rotates the contents of Register A right one bit position, with Bit 0 going to the CARRY FLAG, and the CARRY FLAG going to Bit 7. RRA also can be used to divide a number in 2.
    09F0
    LD C,A4F
    Load Register C with the adjusted High Order (MSB+Sign) in Register A
    09F1
    RRA1F
    Move the value of the sign bit from the Carry flag into Register A. RRA rotates the contents of Register A right one bit position, with Bit 0 going to the CARRY FLAG, and the CARRY FLAG going to Bit 7. RRA also can be used to divide a number in 2.
    09F2
    XOR (HL)AE
    Combine the value of the sign bit of the ACCUMULATOR and the SIGN BIT of the Registers
    09F3
    RETC9
    RETurn to CALLer

    09F4H-09FBH – LEVEL II BASIC MATH ROUTINE – “VMOVFA”

    This routine moves a number of bytes (the number depending on the value stored in the VALTYPE) from (HL) to the ACCumulator. All Registers except C are affected.
    09F4-09F6
     ↳ VMOVFA
    LD HL,4127HLD HL,ARGLO21 27 41
    This is the entry point from the DADD routine. To facilitate, we need to set HL to point to ARG (a/k/a REG 2)) instead of the ACCumulator
    09F7-09F9
     ↳ VMOVFM
    LD DE,09D2HLD DE,MOVVFM11 D2 09
    Load Register Pair DE with the return address of the routine that does an exchange and then falls into the MOVE1 routine.
    09FA-09FB
    Jump to 0A02

    09FCH-0A0BH – LEVEL II BASIC MATH ROUTINE – “VMOVAF”

    This is the opposite of 9F4H. This routine moves a number of bytes (the number depending on the value stored in the VALTYPE) from the ACCumulator to (HL). All Registers except C are affected.
    09FC-09FE
     ↳ VMOVAF
    LD HL,4127HLD HL,ARGLO21 27 41
    Entered here from FIN, DMUL10, and DDIV10. They require that Register Pair HL to point to ARG (a/k/a REG 2) instead of the ACCumulator
    09FF-0A01
     ↳ VMOVMF
    LD DE,09D3HLD DE,VMOVE11 D3 09
    When entered from here, we need to load Register Pair DE with the return address of the MOVE routine.
    0A02
     ↳ VMVVFM
    PUSH DED5
    When entered here, save Register Pair DE (which, if passed through, is a return address) on the STACK
    0A03-0A05
     ↳ VDFACS
    LD DE,4121HLD DE,FACLO11 21 41
    Entered here from INT, STR, and SNG. In that case, we must load Register Pair DE with the starting address for a single precision value in the ACCumulator
    0A06
    We need to check the value of the current number type flag, so we call the TEST DATA MODE routine at RST 20H which determines the type of the current value in the ACCumulator and returns a combination of STATUS flags and unique numeric values in the register A according to the data mode flag (40AFH). The results are returned as follows:
    Variable TypeFlagsRegister A
    IntegerNZ/C/M/E-1
    StringZ/C/P/E0
    Single PrecisionNZ/C/P/O1
    Double PrecisionNZ/NC/P/E5
    0A07
    RET CD8
    If that test is anything other than double precision, return out of this subroutine to the address which was fed in
    0A08-0A0A
    LD DE,411DHLD DE,DFACLO11 1D 41
    If we are here, then we have a double precision number, so set Register Pair DE to point to the the LSB of a double precision number.
    0A0B
    RETC9
    RETurn to the place we set up to return to

    0A0CH-0A25H – SINGLE PRECISION COMPARE – “FCOMP”

    According to the original ROM source code, this routine will compare two single precision numbers. On Exit, A=1 if ARG < ACCumulator, A=0 if ARG=Accmulator, and A=-1 if ARG > ACCumulator. This routine exits with the CARRY FLAG on. Alters Registers A, H, and L.

    Single-precision compare. Compares ACCumulator with the contents of BCDE registers. After execution of this routine, the A Register will contain: A=0 if ACCumulator=BCDE, A=1 if ACC>BCDE or A=FFH if ACC<BCDE.

    Single Precision Comparison: Algebraically compares the single precision value in (BC/DE) to the single precision value ACCumulator. The result of the comparison is returned in the A and status as: IF (BC/DE) > ACCumulator A = -1, IF (BC/DE) < ACCumulator A = +1, IF (BC/DE) = ACCumulator A = 0

    NOTE: To use a ROM call to compare two single precision numbers, store the first input in registers BCDE, the second input in 4121H-4124H and then CALL 0A0CH. If the numbers are equal, the Z (zero) flag will be set. If they are not equal, the Z flag will be turned off. If the first input number is the smaller, the S (sign) and C (carry) flags will also be turned off. If the second input number is the smaller, the S and C flags will both be set.

    0A0C
     ↳ FCOMP
    LD A,B78
    First we need to check to see if ARG is zero, so load Register A with the value of the exponent in Register B
    0A0D
    OR AB7
    Set the flags based on Register A
    0A0E-0A10
    If the exponent in Register A is equal to zero, then JUMP to SIGN
    0A11-0A13
    LD HL,095EHLD HL,FCOMPS21 5E 09
    Set up the destination address to use on a RETurn by first loading Register Pair HL with the address to the FCOMPS routine
    0A14
    PUSH HLE5
    Save the return address in Register Pair HL on the STACK
    0A15-0A17
    Check to see if the ACCumulator is zero via a GOSUB to SIGN
    0A18
    LD A,C79
    If the ACCumulator is ZERO, then the result is simply the NEGative of ARG, so, to prepare for that, load Register A with the MSB of the single precision value in Register C
    0A19
    RET ZC8
    If the ACCumulator was zero, RETurn with Register A holding Register C
    0A1A-0A1C
    LD HL,4123HLD HL,FAC-121 23 41
    Load Register Pair HL with the address of the MSB+SIGN in Register A
    0A1D
    XOR (HL)AE
    Check to see if the signs of the ACCumulator and the ARG are the same via a XOR
    0A1E
    LD A,C79
    If they are different, then the result of that XOR will be the sign of the number in ARG, so load Register A with the MSB+SIGN of Register C
    0A1F
    RET MF8
    If the signs are different, RETurn
    0A20-0A22
    Now that we have resolved the signs, JUMP to FCOMP2 to check the rest of the numbers
    0A23
     ↳ FCOMPD
    RRA1F
    If are are here, then the numbers are different, so the next step is to change the signs if both numbers are negative. To do this, first move the value of the Carry flag from the comparison into Register A. RRA rotates the contents of Register A right one bit position, with Bit 0 going to the CARRY FLAG, and the CARRY FLAG going to Bit 7. RRA also can be used to divide a number in 2.
    0A24
    XOR CA9
    Combine the value of the MSB/SIGN of the single precision value in Register C with the value in Register A
    0A25
    RETC9
    With Register A now set, RETurn to CALLer

    0A26H-0A38H – Part of the SINGLE PRECISION COMPARISON ROUTINE – “FCOMP2”

    0A26
    INC HL23
    Increment the value of the memory pointer in Register Pair HL so that it points to the exponent of the single precision number in the ACCumulator
    0A27
    LD A,B78
    Load Register A with the value of the exponent for the single precision value held in ARG (stored in Register B)
    0A28
    CP (HL)BE
    Check to see if the exponent for the single precision value in the ACCumulator at the location of the memory pointer in Register Pair HL is the same as the value of the exponent for the single precision value in Register A
    0A29
    RET NZC0
    If the value of the exponent for the single precision number in the ACCumulator isn’t the same as the value of the exponent for the single precision number in ARG (held in Register A), RETurn
    0A2A
    DEC HL2B
    Decrement the value of the memory pointer in Register Pair HL so it now will point to the HIGH ORDER/MSB of the single precision number in the ACCumulator
    0A2B
    LD A,C79
    Load Register A with the HIGH ORDER/MSB of the single precision number in ARG (stored in Register C)
    0A2C
    CP (HL)BE
    Check to see if the HIGH ORDER/MSB for the single precision value in the ACCumulator at the location of the memory pointer in Register Pair HL is the same as the value of the MSB for the single precision value in Register A
    0A2D
    RET NZC0
    If the value of the HIGH ORDER/MSB for the single precision number in the ACCumulator isn’t the same as the value of the HIGH ORDER/MSB for the single precision number in ARG (held in Register A), RETurn
    0A2E
    DEC HL2B
    Decrement the value of the memory pointer in Register Pair HL so it now will point to the MIDDLE ORDER/NMSB of the single precision number in the ACCumulator
    0A2F
    LD A,D7A
    Load Register A with the MIDDLE ORDER/NMSB of the single precision number in ARG (stored in Register D)
    0A30
    CP (HL)BE
    Check to see if the MIDDLE ORDER/NMSB for the single precision value in the ACCumulator at the location of the memory pointer in Register Pair HL is the same as the value of the MIDDLE ORDER/NMSB for the single precision value in Register A
    0A31
    RET NZC0
    If the value of the MIDDLE ORDER/NMSB for the single precision number in the ACCumulator isn’t the same as the value of the MIDDLE ORDER/NMSB for the single precision number in ARG (held in Register A), RETurn
    0A32
    DEC HL2B
    Decrement the value of the memory pointer in Register Pair HL so it now will point to the LOW ORDER/LSB of the single precision number in the ACCumulator
    0A33
    LD A,E7B
    Load Register A with the LOW ORDER/LSB of the single precision number in ARG (stored in Register E)
    0A34
    SUB (HL)96
    Use subtraction to check to see if the LOW ORDER/LSB for the single precision value in the ACCumulator at the location of the memory pointer in Register Pair HL is the same as the value of the LOW ORDER/LSB for the single precision value in Register A. We use subtraction so that if the numbers are the same then Register A will be filled with the 0 response.
    0A35
    RET NZC0
    Return if the value of the LSB in the ACCumulator isn’t the same as the value of the LSB in Register A
    0A36
    POP HLE1
    If we are here then the numbers are the same so we need to get the extra data off of the STACK
    0A37
    POP HLE1
    Clear the stack
    0A3B
    RET7C
    All done, so RET to CALLer

    0A39H-0A48H – INTEGER COMPARISON ROUTINE
    – “ICOMP”

    According to the original ROM source code, this routine will compare two integers. On Exit, A=1 if (DE) < (HL), A=0 if (DE)=(HL), and A=-1 if (DE) > (HL). Alters only Register A.

    Integer compare. Compares HL with DE. After execution, A=0 if HL=DE, A=1 if HL>DE or A=FFH if HL<DE. The S and Z flags are valid.

    NOTE: To use a ROM call to compare two integers, store the first input in DE, the second in HL and then CALL 0A39H. If the numbers are equal, the Z (zero) flag will be set. If they are not equal, the Z flag will be turned off. If the first input number is the smaller, the S (sign) and C (carry) flags will also be turned off. If the second input number is the smaller, the S and C flags will both be set.

    Compares HL with DE. After execution, A=0 if HL=DE, A=1 if HL>DE or A=FFH if HL<DE. The S and Z flags are valid

    Algebraically compares two integer values in DE and HL. The contents of DE and HL are left intact. The result of the comparison is left in the A Register and status register: If DE > HL A = -1, IF DE < HL A = +1, IF DE = HL A = 0

    0A39
     ↳ ICOMP
    LD A,D7A
    First we test the signs, so load Register A with the SIGN of the integer value in Register D
    0A3A
    XOR HAC
    Check to see if the sign bit for the MSB of the integer value in Register H is the same as the sign bit for the SIGN for the integer value in Register A
    0A3B
    LD A,H7C
    If the signs are NOT the same, then the result is the sign of (HL), so put the SIGN of the number in (HL) into the response register of Register A.
    0A3C-0A3E
    If the sign bits are NOT the same, JUMP to ICOMPS to check the numbers
    0A3F
    CP DBA
    If we are here, then the signs are the same, so now check to see if the HIGH ORDER/MSB for the integer value in Register D is the same as the HIGH ORDER/MSB for the integer value in Register A
    0A40-0A42
    if the HIGH ORDER/MSB for the integer value in Register D isn’t the same as the HIGH ORDER/MSB for the integer value in Register A, JUMP to SIGNS to set up the appropriate response in Register A
    0A33
    LD A,L7B
    Next, check to see if the LOW ORDER/LSB’s are the same by first loading Register A with the LOW ORDER/LSB of the integer value in Register L
    0A44
    SUB E93
    Use subtraction to check to see if the LOW ORDER/LSB for the integer value in Register E is the same as the LOW ORDER/LSB for the integer value in Register A
    0A45-0A47
    If the LSB for the integer value in Register E isn’t the same as the LSB for the integer value in Register A, JUMP to SIGNS to set up the appropriate response in Register A
    0A48
    RETC9
    If we are here, then two things. First, they are the same. Second, A is zero. So RETurn to CALLer

    0A49H-0A77H – DOUBLE PRECISION COMPARISON ROUTINE – “DCOMPD”

    According to the original ROM source code, this routine will compare two double precision numbers. On Exit, A=1 if ARG < ACCumulator, A=0 if ARG=Accmulator, and A=-1 if ARG > ACCumulator. Every register is affected.

    Double-precision compare. Compares ACCumulator with the ARG (a/k/a REG 2). After execution the A Register will contain: A=0 if ACCumulator=ARG (a/k/a REG 2), A=1 if ACC > ARG (a/k/a REG 2) or A=FFH if ACC < ARG (a/k/a REG 2). S and Z flags are valid.
    0A49-0A4B
     ↳ DCOMPD
    LD HL,4127HLD HL,ARGLO21 27 41
    Load Register Pair HL with the starting address of ARG (a/k/a REG 2). If entering here, then (DE) already needs to be set with the pointer to ARG.
    Note: 4127H-412EH holds ARG (a/k/a REG 2)
    0A4C-0A4E
    Go move the double precision value pointed to by Register Pair DE to ARG (a/k/a REG 2)
    0A4F-0A51
     ↳ XDCOMP
    LD DE,412EHLD DE,ARG11 2E 41
    Load Register Pair DE with the address of the exponent in ARG (a/k/a REG 2)
    0A52
    LD A,(DE)1A
    Load Register A with the exponent for the double precision value in ARG (a/k/a REG 2) at the location of the memory pointer in Register Pair DE
    0A53
    OR AB7
    Check to see if the double precision value in ARG (a/k/a REG 2) is equal to zero
    0A54-0A56
    If the double precision value in ARG (a/k/a REG 2) is equal to zero, then we are done, so JUMP to SIGN to set up Register A with the appropriate response.
    0A57-0A59
    LD HL,095EHLD HL,FCOMPS21 5E 09
    Load Register Pair HL with a return address to the FCOMPS routine
    0A5A
    PUSH HLE5
    Save the return address in Register Pair HL on the STACK
    0A5B-0A5D
    Go check to see if the double precision value in the ACCumulator is equal to zero
    0A5E
    DEC DE1B
    Decrement the value of the memory pointer in Register Pair DE so that DE now points to the MSB+SIGN of the number in ARG (a/k/a REG 2)
    0A5F
    LD A,(DE)1A
    Load Register A with the MSB+SIGN of the double precision value in ARG (a/k/a REG 2) at the location of the memory pointer in Register Pair DE
    0A60
    LD C,A4F
    Presetve the MSB+SIGN of the double precision value in ARG (a/k/a REG 2) into Register C
    0A61
    RET ZC8
    If the number in the ACCumulator = 0, then the sign of the result is the sign of ARG, so RETurn wto FCOMPS
    0A62-0A64
    LD HL,4123HLD HL,FAC-121 23 41
    Load Register Pair HL with the address of the SIGN of the double precision value in the ACCumulator
    0A65
    XOR (HL)AE
    Check to see if the sign bit the double precision value in the ACCumulator at the location of the memory pointer in Register Pair HL is the same as the sign bit of the double precision value in ARG (a/k/a REG 2) in Register A
    0A66
    LD A,C79
    In case they are the same, get the sign from C into Register A.
    0A67
    RET MF8
    If they are NOT the same, RETurn to FCOMPS to set set Register A
    0A68
    INC DE13
    Increment the value of the memory pointer in Register Pair DE so that DE now points to the exponent of ARG
    0A69
    INC HL23
    Increment the value of the memory pointer in Register Pair HL so that HL now points to the exponent of the ACCumulator
    0A6A-OA6B
    LD B,08H06 08
    Load Register B with the number of bytes to be compared, as B will act as a counter
    0A6C
     ↳ DCOMP1
    LD A,(DE)1A
    Load Register A with a byte from the double precision number in ARG (pointed to by Register Pair DE)
    0A6D
    SUB (HL)96
    Use subtraction to compare that byte from ARG with the correspondible byte from the ACCumulator (pointed to by Register Pair HL)
    0A6E-0A70
    If the NZ is set, then the numbers are different o JUMP to FCOMPD to set up Register A
    0A71
    DEC DE1B
    If we are here, then they are the same, so we need to move to the next byte of ARG (so decrement the value of the memory pointer in Register Pair DE)
    0A72
    DEC HL2B
    and the next byte of the ACCumulator (so decrement the value of the memory pointer in Register Pair HL)
    0A73
    DEC B05
    and to decrease the byte counter (so Decrement the number of bytes remaining to be compared in Register B)
    0A74-0A75
    The DEC of B will set the flags. If B is NOT ZERO, then Loop back to DCOMP1 until all of the bytes have been compared
    0A76
    POP BCC1
    If we are here, then the numbers are the same, so we need to clean the RETurn to FCOMPS off the stack, as that is not where we want to RETurn to
    0A77
    RETC9
    RETurn to the actual CALLer

    0A78H-0A7EH – DOUBLE PRECISION COMPARE – “DCOMP”

    According to the original ROM source code, this routine will compare two double precision numbers, but is the opposite of the ICOMP, FCOMP, and XDCOMP routines. This one swaps ARC and ACC, so on Exit, A=1 if ARG > ACCumulator, A=0 if ARG=Accmulator, and A=-1 if ARG < ACCumulator. Every register is affected.

    Double-precision compare. This compare is the opposite of the A4FH compare. It compares the ARG (a/k/a REG 2) with the ACC. (Remember that a compare is actually a subtraction that is never executed therefore a compare can be done in two ways with the same values. (A-B and B-A)). The results are the same as the A4FH routine.

    Double Precision Compare: Compares the double precision value in the ACCumulator to the value in ARG (a/k/a REG 2). Both Register areas are left intact. The result of the comparison is left in the A and status registers as: IF ACCumulator > ARG (a/k/a REG 2) A = -1, IF ACCumulator < ARG (a/k/a REG 2) A = +1, IF ACCumulator = ARG (a/k/a REG 2) A = 0

    NOTE: To use a ROM call to compare two double precision number, store the first input in 411DH-4124H, and store the second input in 4127H-412EH and then CALL 0A78H. If the numbers are equal, the Z (zero) flag will be set. If they are not equal, the Z flag will be turned off. If the first input number is the smaller, the S (sign) and C (carry) flags will also be turned off. If the second input number is the smaller, the S and C flags will both be set.
    0A78-0A7A
     ↳ DCOMP
    GOSUB to compare the double precision value in ARG (a/k/a REG 2) to the double precision value in the ACCumulator
    0A7B-0A7D
    If the double precision value in the ACCumulator and the double precision value in ARG (a/k/a REG 2) aren’t the same then JUMP to FCOMPS to negate the answer and set up the CARRY FLAG for the DOCMP routine
    0A7E
    RETC9
    RETurn to CALLer

    0A7FH-0AB0H – LEVEL II BASIC CINT ROUTINE – “FRCINT”

    CINT routine. Takes a value from ACC, converts it to an integer value and puts it back into the ACC. On completion, the HL Register Pair contains the LSB of the integer value, and the NTF contains 2 (Integer=2). If NTF=3 (string) a TM ERROR will be generated and control will be passed to BASIC. Every register is affected. No rounding is performed

    NOTE: To use a ROM call to call the CINT routine, store the single precision input variable in 4121H-4124H and then call to 0A8AH and bypass all the foregoing. After the call, the integer result would be in 4121H-4122H and in the HL Register Pair. Too big a number will generate a ?OV Error.
    0A7F
     ↳ FRCINT
    We need to check the value of the current number type flag, so we call the TEST DATA MODE routine at RST 20H which determines the type of the current value in the ACCumulator and returns a combination of STATUS flags and unique numeric values in the register A according to the data mode flag (40AFH). The results are returned as follows:
    Variable TypeFlagsRegister A
    IntegerNZ/C/M/E-1
    StringZ/C/P/E0
    Single PrecisionNZ/C/P/O1
    Double PrecisionNZ/NC/P/E5
    0A80-0A82
    LD HL,(4121H)LD HL,(FACLO)2A 21 41
    Just in acse we already have an integer, Load Register Pair HL with the integer value in the ACCumulator (which is stored at FACLO and FACLO+1)
    0A83
    RET MF8
    If that test showed we have an INTEGER, then return out of this subroutine
    0A84-0A86
    If that test showed we have a STRING, Display a ?TM ERROR message
    0A87-0A89
    If that test shows we have DOUBLE PRECISION, call 0AB9H to convert the number to single precision
    0A8A-0A8C
    LD HL,07B2HLD HL,OVERR21 B2 07
    Just in case the number is too big, pre-load HL with the RETurn address to the ?OV ERROR routine
    0A8D
    PUSH HLE5
    Save the return address in Register Pair HL on the STACK and fall into the “CONIS” routine to continue.

    0A8EH – LEVEL II BASIC CONVERSION ROUTINE – “CONIS”

    This routine will convert a single precision number to an integer. Every register is affected.
    0A8E-0A90
     ↳ CONIS
    LD A,(4124H)LD A,(FAC)3A 24 41
    Load Register A with the exponent for the single precision value in the ACCumulator
    0A91-0A92
    CP 90HFE 90
    Check to see if the exponent for the single precision value in the ACCumulator in Register A indicates more than 16 bits of precision
    0A93-0A94
    If the exponent for the single precision value in the ACCumulator in Register A indicates more than 16 bits of precision, JUMP to CONIS2 to make sure that the reason it is “too big” isn’t because it is -32768
    0A95-0A97
    If we are here then the number isn’t too big, so GOSUB to QINT to convert the single precision value in the ACCumulator to an integer and return with the integer value in Register Pair DE
    0A98
    EX DE,HLEB
    Load Register Pair HL with the integer value that was put into Register Pair DE by QINT
    0A99
     ↳ CONIS1
    POP DED1
    Get the error address from the STACK and put it in Register Pair DE

    0A9AH – LEVEL II BASIC CONVERSION ROUTINE – “MAKINT”

    This is the routine that returns the value in the HL Register Pair to the BASIC program that called it. In effect it moves the content of HL into the ACCumulator so it is ACCumulator = (HL) with VALTYPE set accordingly
    0A9A-0A9C
     ↳ MAKINT
    LD (4121H),HLLD (FACLO),HL22 21 41
    Save the integer value in Register Pair HL as the current value in the ACCumulator.
    0A9D-0A9E
     ↳ VALINT
    LD A,02H3E 02
    Load Register A with an integer number type flag.
    0A9F-0AA1
     ↳ CONISD
    LD (40AFH),ALD (VALTYP),A32 AF 40
    Save the integer number type flag in Register A as the current value of the number type flag.
    Note: 40AFH holds Current number type flag. This is the entry point from the CONDS routine
    0AA2
    RETC9
    RETurn to CALLer

    0AA3H – LEVEL II BASIC CONVERSION ROUTINE – “CONIS2”

    0AA3-0AA5
     ↳ CONIS2
    LD BC,9080H01 80 90
    This routine’s purpose is to check to see if a number from the FIN routine is -32768. First, load up the register paird BCDE with 9080H/0000H for purposes of using FCOMP to test
    0AA6-0AA8
    LD DE,0000H11 00 00
    Load Register Pair DE with the NMSB and the LSB of a single precision value. Register Pairs BC and DE now hold a single precision value equal to -32768
    0AA9-0AAB
    Call the SINGLE PRECISION COMPARISON routine at 0A0CH.

    NOTE: The routine at 0A0CH algebraically compares the single precision value in BC/DE to the single precision value ACCumulator.
    The results are stored in A as follows:
    • A=0 if ACCumulator = BCDE
    • A=1 if ACCumulator>BCDE; and
    • A=FFH if ACCumulator<BCDE.
    0AAC
    RET NZC0
    If FCOMP returns a NZ, then there was an error and the number could NOT be converted into an integer. In this case, display an ?OV ERROR
    0AAD
    LD H,C61
    If we are here, then the value is -32768, so we need to put that into (HL). First, load Register H with the MSB of the single precision value in Register C
    0AAE
    LD L,D6A
    Load Register L with the NMSB of the single precision value in Register D
    0AAF-0AB0
    Jump to 0A99H to store (HL) into the ACCumulator and set the VALTYPE accordingly.

    0AB1H-0ACBH – LEVEL II BASIC CSNG ROUTINE – “FRCSNG”

    Force the number in the ACCumulator to be a single-precision number. Every register is affected.

    CSNG routine. Takes value from ACC and converts it to single-precision. The result is put in ACC and NTF contains 4.

    CSNG routine. Takes value from ACC and converts it to single-precision. The result is put in ACC and NTF contains 4

    Integer To Single: The contents of ACCumulator are converted from integer or double precision to single precision. All registers are used

    0AB1
     ↳ FRCSNG
    We need to check the value of the current number type flag, so we call the TEST DATA MODE routine at RST 20H which determines the type of the current value in the ACCumulator and returns a combination of STATUS flags and unique numeric values in the register A according to the data mode flag (40AFH). The results are returned as follows:
    Variable TypeFlagsRegister A
    IntegerNZ/C/M/E-1
    StringZ/C/P/E0
    Single PrecisionNZ/C/P/O1
    Double PrecisionNZ/NC/P/E5
    0AB2
    RET POE0
    IF PO is set, then we have SINGLE PRECISION number already, so nothing to do! RETurn out of this subroutine
    0AB3-0AB5
    If that test shows we have an INTEGER, jump to 0ACCH to convert it
    0AB6-0AB8
    If that test shows we have a STRING, display a ?TM ERROR. Otherwise, fall into the DOUBLE PRECISION routine, located just after to avoid a JUMP to it.

    0AB9 – LEVEL II BASIC NUMBER CONVERSION ROUTINE – “CONSD”

    Convert a double-prevision number to single-precision. Every register is affected.
    0AB9-0ABB
     ↳ CONSD
    Move the HIGH ORDER/MSB’s into the registers via a call to MOVRF which loads the SINGLE PRECISION value in the ACCumulator (which is currently the most significant four bytes of the double precision value in the ACCumulator) into Register Pair BC/DE
    0ABC-0ABE
    Go set the current number type flag to single precision
    0ABF
    LD A,B78
    Next we need to see if the number is zero, so load Register A with the exponent of the double precision value in Register B
    0AC0
    OR AB7
    Check to see if the exponent in the ACCumulator is equal to zero
    0AC1
    RET ZC8
    If the exponent is zero, then the number is zero, so RETurn
    0AC2-0AC4
    We now know the number isn’t zero, so we need to unpack the number via a CALL to UNPACK which will turn on the most significant bit of the single precision value in the ACCumulator
    0AC5-0AC7
    LD HL,4120HLD HL,FACLO-121 20 41
    Load Register Pair HL with the address of the first byte below a single-prevision value (i.e., chop off the MSB of a double double precision value)
    0AC8
    LD B,(HL)46
    Loaded Register B with the chopped number, as that is where the ROUND routine expects the number to be
    0AC9-0ACB
    Jump to 0796H to round the chopped number up and RETurn

    0ACCH-0ADAH -LEVEL II BASIC NUMBER CONVERSION ROUTINE – “CONSI”

    Convert Integer to Single Precision. Every register is affected.

    Note: If you wanted to convert integer to single precision via a ROM call, you would store the integer input variable in 4121H-4122H and then call to 0ACCH. The result (as a single precision number) will be in 4121H-4124H.
    0ACC-0ACE
     ↳ CONSI
    LD HL,(4121H)LD HL,(FACLO)2A 21 41
    Load Register Pair HL with the integer value from the ACCumulator
    0ACF-0AD1
     ↳ CONSIH
    Go set the current number type flag to single precision
    0AD2
    LD A,H7C
    Now we need to prepare the registers for the FLOATR routine. First, load Register A with the MSB of the integer value in Register H
    0AD3
    LD D,L55
    Load Register D with the LSB of the integer value in Register L
    0AD4-0AD5
    LD E,00H1E 00
    Zero Register E
    0AD6-0AD7
    LD B,90H06 90
    Load Register B with the initial maximum exponent
    0AD8-0ADA
    Jump to 0969H to float the integer into single precision

    0ADBH-0AEDH – LEVEL II BASIC CDBL ROUTINE – “FRCDBL”

    CDBL routine. Takes a value from ACCumulator (regardless of integer or single precision) and convert it to double-precision. The result will be in ACC and NTF will be 8.
    0ADB
     ↳ FRCDBL
    We need to check the value of the current number type flag, so we call the TEST DATA MODE routine at RST 20H which determines the type of the current value in the ACCumulator and returns a combination of STATUS flags and unique numeric values in the register A according to the data mode flag (40AFH). The results are returned as follows:
    Variable TypeFlagsRegister A
    IntegerNZ/C/M/E-1
    StringZ/C/P/E0
    Single PrecisionNZ/C/P/O1
    Double PrecisionNZ/NC/P/E5
    0ADC
    RET NCD0
    If that test shows we have already a DOUBLE PRECISION number, then we are done, so RETurn out of the subroutine
    0ADD-0ADF
    If that test shows we have a STRING, Display a TM ERROR message
    0AE0-0AE2
    If that test shows we have an INTEGER, then go to 0ACCH to convert that integer to SINGLE PRECISION and then fall into the CONDS routine to convert a single precision number into double precision.

    0AE3H – LEVEL II BASIC CDBL ROUTINE – “CONDS”

    Convert a single precision number to double precisions. Modifies Registers A, H, and L.
    0AE3-0AE5
     ↳ CONDS
    LD HL,0000H21 00 00
    Load Register Pair HL with zero so we can zero out the ACCumulator
    0AE6-0AE8
    LD (411DH),HLLD (DFACLO),HL22 1D 41
    Zero out the first and second bytes of the double precision number in the ACCumulator.
    Note: 411DH-4124H holds ACCumulator
    0AE9-0AEB
    LD (411FH),HLLD (DFACLO+2),HL22 1F 41
    Zero out the third and fourth bytes of the double precision number in the ACCumulator
    0AEC-0AED
     ↳ VALDBL
    LD A,08H3E 08
    Load Register A with a double precision number type flag

    0AEEH-0AF3H – LEVEL II BASIC MATH ROUTINE – “VALSNG”

    0AEE
    LD BC,043EH01 3E 04
    Z-80 Trick. If passing through to this routine, BC will be modified but the next instruction will be skipped.
    0AEF-0AF0
     ↳ VALSNG
    LD A,04H
    3E 04
    Load Register A with a single precision number type flag (of 4)
    0AF1-0AF3
    However we got here, Register A now holds the desired VALTYPE, so jump away to 0A9FH to save the value in Register A as the VALTYPE and RETurn

    0AF4H-0AFAH – LEVEL II BASIC MATH ROUTINE – “CHKSTR”

    This routine will force the ACCUmlator to be a STRING. Only Register A is modified.
    0AF4
     ↳ CHKSTR
    We need to check the value of the current number type flag, so we call the TEST DATA MODE routine at RST 20H which determines the type of the current value in the ACCumulator and returns a combination of STATUS flags and unique numeric values in the register A according to the data mode flag (40AFH). The results are returned as follows:
    Variable TypeFlagsRegister A
    IntegerNZ/C/M/E-1
    StringZ/C/P/E0
    Single PrecisionNZ/C/P/O1
    Double PrecisionNZ/NC/P/E5
    0AF5
    RET ZC8
    If that test shows we already have a STRING then we are done, so RETturn out of the subroutine. Otherwise, fall into the ?TM ERROR routine, placed here to save bytes and avoid a JUMP.

    0AF6 – ?TM Error Routine – “TMERR”

    0AF6-0AF7
     ↳ TMERR
    LD E,18H1E 18
    Load Register E with a ?TM ERROR code.

    This is the entry point for the TM ERROR
    0AF8-0AFA
    Display a TM ERROR message if the current value in the ACCumulator isn’t a string

    0AFBH-0B1EH – LEVEL II BASIC MATH ROUTINE – “QINT”

    This routine is a quick “Greatest Integer” function. Registers A-E are affected.

    The result of INT(ACCumulator) is left in C/D/E as a signed number

    This routine assumes that the number in the ACCumulator is less than 8,388,608 (i.e., 2^23) and that the exponent of the ACCumulator is held in Register A on entry.

    This routine can also be used to reset the BC and DE Register Pairs if the A Register contains 0. (XOR A before calling this routine).
    0AFB
     ↳ QINT
    LD B,A47
    Load Register B with the exponent of the single precision number in Register A. If a XOR A was executed before calling this routine, then the following will zero all of the registers.
    0AFC
    LD C,A4F
    Load Register C with the exponent of the single precision number in Register A
    0AFD
    LD D,A57
    Load Register D with the exponent of the single precision number in Register A
    0AFE
    LD E,A5F
    Load Register E with the exponent of the single precision number in Register A
    0AFF
    OR AB7
    Check to see if the single precision number in the ACCumulator is equal to zero
    0B00
    RET ZC8
    If Register A was 0 on entry (meaning that the exponent of the number is 0), then RETurn the same way but with C/D/E = 0, as any number whose exponent is 0 is 0.
    The original ROM source code has this to say about the next set of instructions:

    The hard case in QINT is negative non-integers. To handle this, if the number is negative, we regard the 3-byte mantissa as a 3-byte integer and subtract one. Then all the fractional bits are shifted out by shifting the mantissa right. Then, if the number was negative, we add one.

    So, if we had a negative integer, all the bits to the right of the binary point were zero and the net effect is we have the original number in C/D/E.

    If the number was a negative non-integer, there is at least one non-zero bit to the right of the binary point and the net effect is that we get the absolute value of int(fac) in C/D/E. C/D/E is then negated if the original number was negative so the result will be signed.
    0B01
    PUSH HLE5
    Save the value in Register Pair HL on the STACK
    0B02-0B04
    Call 09BF which loads the SINGLE PRECISION value in the ACCumulator into Register Pair BC/DE
    0B05-0B07
    Go turn on the sign bit of the single precision value in Register Pairs BC and DE
    0B08
    XOR (HL)AE
    Set the sign bit according to the sign of the value at the location of the memory pointer in Register Pair HL
    0B09
    LD H,A67
    Preserve the sign of the numbers into Register H
    0B0A-0B0C
    If the number was negative, we need to substract 1 from the LOW ORDER/LSB and to do that we GOSUB to QINTA
    0B0D-0B0E
    LD A,98H3E 98
    Next we need to see how many number of bits we need to shift to change the number to an integer, so start that calculation by loading Register A with the maximum exponent
    0B0F
    SUB B90
    and then subtract the exponent in Register B from the exponent in Register A
    0B10-0B12
    Shift the single precision value in Register Pairs BC and DE to get rid of any fractional bits via a GOSUB to SHIFTR.
    0B13
    LD A,H7C
    Restore the SIGN back into Register A from Register H
    0B14
    RLA17
    Put the sign bit into the Carry flag so that it won’t get changed.
    0B15-0B17
    If the original number was negative (and thus the CARRY FLAG is set), GOSUB to ROUNDA to bump the value in Register Pairs BC and DE by 1
    0B18-0B19
    LD B,00H06 00
    Clear our Register B
    0B1A-0B1C
    If the original number was negative, we need to negate the number because we need a signed mantissa
    0B1D
    POP HLE1
    Restore HL from the STACK where it was saved at the top of this routine
    0B1E
    RETC9
    RETurn to CALLer

    0BlFH-0B25H – LEVEL II BASIC MATH ROUTINE – “QINTA”

    0B1F
     ↳ QINTA
    DEC DE1B
    Decrement C/D/E by 1
    0B20
    LD A,D7A
    Now we need to see if we need to carry that further and subtract one from C, so load Register A with the value of the NMSB for the single precision value which is held in Register D
    0B21
    AND EA3
    Combine the LSB of the single precision value in Register E with the NMSB of the single precision value in Register A
    0B22
    INC A3C
    Increment the combined value in Register A
    0B23
    RET NZC0
    If both D and E were -1 (i.e., DE was FFFFH) then RETurn
    0B24
     ↳ DCXBRT
    DEC BC0B
    Decrement the value of the exponent and the MSB of the single precision value in Register Pair BC. A note in the original ROM source said that this was put in specifically at the request of Bill Gates and that Register C would never be ZERO, so DEC BC and DEC C would be functionally equivalent.
    0B25
    RETC9
    RETurn to CALLer

    0B26H-0B58H – LEVEL II BASIC FIX ROUTINE
    – “FIX”

    This is the FIX(n) routine. It returns SGN(n)*INT(ABS(n))

    Takes a value from ACC and converts it to an integer value. The result will be in ACC. NTF will be 2 if value is smaller than 32767 else it will be 4. An error will be generated if NTF=3 (string).
    A call to 0B26H unconditionally truncates the fractional part of a floating point number in the ACCumulator. The result is stored in the ACCumulator and the type flag is set to integer.

    Note: If you wanted to call the FIX routine via a ROM call, you would store the single-precision input variable in 4121H-4124H, then put a 4 into 40AFH to flag as single precision, and then call to 0B26H. If the result can be an integer, it will be in 4121H-4122H and in the HL Register Pair. If single precision, the result will be in 4121H-4124H. If double precision, in 411DH-4124H. In all cases 40AFH will have the data mode flag as 2, 4, or 8, accordingly.

    FIX routine. Takes a value from ACC and converts it to an integer value. The result will be in ACC. NTF will be 2 if value is smaller than 32767 else it will be 4. An error will be generated if NTF=3 (string)

    Floating To Integer: Unconditionally truncates the fractional part of a floating point number in the ACCumulator. The result is stored in the ACCumulator and the type flag is set to integer
    0B26
     ↳ FIX
    We need to check the value of the current number type flag, so we call the TEST DATA MODE routine at RST 20H which determines the type of the current value in the ACCumulator and returns a combination of STATUS flags and unique numeric values in the register A according to the data mode flag (40AFH). The results are returned as follows:
    Variable TypeFlagsRegister A
    IntegerNZ/C/M/E-1
    StringZ/C/P/E0
    Single PrecisionNZ/C/P/O1
    Double PrecisionNZ/NC/P/E5
    0B27
    RET MF8
    If that test shows we have an INTEGER then we are all done, so RETurn to the caller
    0B28-0B2A
    Go check the sign of the current value in the ACCumulator
    0B2B-0B2D
    If the current value in the ACCumulator is positive, then we only need to do a regular INT(n), so JUMP to 0B37H (which returns the integer portion of a floating point number. If the value is positive, the integer portion is returned. If the value is negative with a fractional part, it is rounded up before truncation. The integer portion is left in the ACCumulator. The mode flag is updated.)
    0B2E-0B30
    If we are here then the number was negative, and we need it to be positive, so GOSUB the NEG routine to convert the current value in the ACCumulator to positive
    0B31-0B33
    Now that ACCumulator is positive, GOSUB to do a regular INT(n), so JUMP to 0B37H (which returns the integer portion of a floating point number. If the value is positive, the integer portion is returned. If the value is negative with a fractional part, it is rounded up before truncation. The integer portion is left in the ACCumulator. The mode flag is updated.)
    0B34-0B36
    Since it was negative, we now need to make it negative again so JUMP to 097BH to re-NEGate the number and RETurn to the caller of this routine

    0B37H – LEVEL II BASIC INT( ROUTINE – “VINT”

    Return Integer: Returns the integer portion of a floating point number. Every flag is affected. If the value is positive, the integer portion is returned. If the value is negative with a fractional part, it is rounded up before truncation. The integer portion is left in the ACCumulator

    Note: If you wanted to call the INT routine via a ROM call, you would store the single precision input variable in 4121H-4124H, put a 4 into 40AFH (to flag as single precision), and then call 0B3DH and bypass all the foregoing. After the call, the integer result would be in 4121H-4122H and in the HL Register Pair IF the absolute value of the input did not exceed 32767. Otherwise it will be in 4121H-4124H in single precision format, and 40AF will be a 2 for integer or 4 for single precision
    0B37
     ↳ VINT
    We need to check the value of the current number type flag, so we call the TEST DATA MODE routine at RST 20H which determines the type of the current value in the ACCumulator and returns a combination of STATUS flags and unique numeric values in the register A according to the data mode flag (40AFH). The results are returned as follows:
    Variable TypeFlagsRegister A
    IntegerNZ/C/M/E-1
    StringZ/C/P/E0
    Single PrecisionNZ/C/P/O1
    Double PrecisionNZ/NC/P/E5
    0B38
    RET MF8
    If that test shows we have an INTEGER then we are done, so RETurn to CALLer.
    0B39-0B3A
    If the NC FLAG is set, then we have a double density number, so JUMP to DINT to handle the conversion.
    0B3B-0B3C
    Display a ?TM ERROR if the current value in the ACCumulator isa string
    0B3D-0B3F
    Now we try to use the CONIS routine to convert the single precision value in the ACCumulator to an integer. If we can’t we will return here to give a single precision result instead.
    0B40-0B42
     ↳ INT
    LD HL,4124HLD HL,FAC21 24 41
    Load Register Pair HL with the address of the exponent in the ACCumulator
    0B43
    LD A,(HL)7E
    Load Register A with the value of the exponent in the ACCumulator (held at the location of the memory pointer in Register Pair HL)
    0B44-0B45
    CP 98HFE 98
    Check to see if there are fractional bits used by the current value in the ACCumulator. If are none, then the NC CARRY flag will be set.
    0B46-0B48
    LD A,(4121H)LD A,(FACLO)3A 21 41
    Load Register A with the LSB of the single precision number in the ACCumulator
    0B49
    RET NCD0
    If there are no fractional bits, then we are done, so RETurn with Register A holding the single precision value in the ACCumulator
    0B4A
    LD A,(HL)7E
    Load Register A with the exponent of the single precision number in the ACCumulator
    0B4B-0B4D
    If we are here, then there were fractional bits, so GOSUB to QINT to convert the single precision number in the ACCumulator to an integer
    0B4E-0B4F
    LD (HL),98H36 98
    Adjust the exponent to be a correct one post-normalization
    0B50
    LD A,E7B
    Load Register A with the LSB of the integer value in Register E
    0B51
    PUSH AFF5
    Save the LSB of the integer value in Register A on the STACK
    0B52
    LD A,C79
    If the number was negative then we need to negate it, so first load Register A with the value in Register C
    0B53
    RLA17
    Move the sign bit in Register A into the CARRY FLAG
    0B54-0B56
    GOSUB to FADLT to re-float the number
    0B57
    POP AFF1
    Get the LSB of the single precision value from the STACK and put it in Register A
    0B58
    RETC9
    RETurn to CALLer

    0B59H-0B9DH – LEVEL II BASIC MATH ROUTINE – “DINT”

    Greated Integer function for double-precision numbers. All registers are affected.
    0B59-0B5B
     ↳ DINT
    LD HL,4124HLD HL,FAC21 24 41
    Load Register Pair HL with the address of the exponent in the ACCumulator
    0B5C
    LD A,(HL)7E
    Load Register A with the value of the exponent for the double precision number in the ACCumulator (stored at the location of the memory pointer in Register Pair HL)
    0B5D-0B5E
    CP 90HFE 90
    Check to see if the double precision number in the ACCumulator uses more or less than 16 bits of precision
    0B5F-0B61
    If the double precision value in the ACCumulator uses less than 16 bits of precision, then we can use the FRCINT routine to do it, so JUMP to the CONVERT TO INTEGER routine at 0A7F (where the contents of ACCumulator are converted from single or double precision to integer and stored in HL)
    0B62-0B63
    If the NZ flag was set, we still need to make sure we didn’t have the special case number of -32768, so JUMP to DINT2
    0B64
    LD C,A4F
    If we’re here then we have to do it the hard way. First, load Register C with the exponent for the double precision number in Register A
    0B65
    DEC HL2B
    Decrement the value of the memory pointer in Register Pair HL to point to the HIGH ORDER (MSB+SIGN) portion of the double precision number
    0B66
    LD A,(HL)7E
    Load Register A with the HIGH ORDER (MSB+SIGN) of the double precision value in the ACCumulator at the location of the memory pointer in Register Pair HL
    0B67-0B68
    XOR 80HXOR 1000 0000EE 80
    Complement the value of the sign bit in Register A (which is 1000 0000)
    0B69-0B6A
    LD B,06H06 06
    Next we need to check to see if the rest of the number is ZERO, so load Register B with the number of bytes to be checked
    0B6B
     ↳ DINT1
    DEC HL2B
    Top of a loop. Decrement the value of the memory pointer in Register Pair HL to point to the next byte of the number
    0B6C
    OR (HL)B6
    Combine the value at the location of the memory pointer in Register HL with the value in Register A. If any of the bits are non-zero, then A will then be non-zero
    0B6D
    DEC B05
    Decrement the byte counter in Register B
    0B6E-0B6F
    Loop until all of the bytes have been checked
    0B70
    OR AB7
    The above loop kept ORing A with bits, so now we need to see what A actually holds, so set the flag.
    0B71-0B73
    LD HL,8000H21 00 80
    Just in case, put -32768 into Register Pair HL. Note that -32768 is negative 0 in double precision
    0B74-0B76
    If the P flag is set, then Register A was zero (-32768), so JUMP to 0A9AH to deal with it
    0B77
    LD A,C79
    Register A wasn’t zero, so let’s keep calcuating. Load Register A with the exponent for the double precision value in Register C
    0B78-0B79
     ↳ DINT2
    CP B8HFE B8
    Check to see if there are fractional bits in for the double precision value in the ACCumulator
    0B7A
    RET NCD0
    If the NO CARRY FLAG is set, then there are no fractional bits so we already have an integer! With this, RETurn
    0B7B
     ↳ DINTFO

     ↳ “DINTFO”
    PUSH AFF5
    Save the exponent in Register A on the STACK. This is the entry point from FOUT, and if that’s the case, the CARRY FLAG will be set.
    0B7C-0B7E
    Gosub to 09BF which loads the HIGH ORDER (the most significant four bytes) of the double precision value in the ACCumulator into Register Pair BC/DE
    0B7F-0B81
    Gosub to 09DFH to turn on the sign bit and return with the value of the sign
    0B82
    XOR (HL)AE
    Get the sign back by XORing A against (HL)
    0B83
    DEC HL2B
    Decrement the value of the memory pointer in Register Pair HL to point to the exponent of the double-precision number
    0B84-0B85
    LD (HL),B8H36 B8
    Save an exponent at the location of the memory pointer in Register HL for post-normalization
    0B86
    PUSH AFF5
    Save the value of the sign test in Register A on the STACK
    0B87-0B89
    If the number was negative, then the M FLAG will be set, in which case GOSUB to DINTA to subtract 1 from the LSB of the ACCumulato
    0B8A-0B8C
    LD HL,4123HLD HL,FAC-121 23 41
    Load Register Pair HL with the address of the HIGH ORDER/MSB in the ACCumulator
    0B8D-0B8E
    LD A,B8H3E B8
    Next we need to see how many bits we need to shift, so start off with Register A being the the maximum value of an exponent
    0B8F
    SUB B90
    Subtract the value of the exponent at the location of the memory pointer in Register Pair HL from the value in Register A
    0B90-0B92
    Shift the ACCumulator bits B times
    0B93
    POP AFF1
    Get the value of the sign test from the STACK and put it in Register A
    0B94-0B96
    If the sign is negative, GOSUB to DROUNA to add 1 to the value in the ACCumulator
    0B97
    XOR AAF
    Zero Register A so we can put a zero into the extra LOW ORDER byte, so that when we normalize it, we shift in zeroes
    0B98-0B9A
    LD (411CH),ALD (DFACLO-1),A32 1C 41
    Put a ZERO into the starting address of ACCumulator minus one
    0B9B
    POP AFF1
    Get the value of the original exponent test from the STACK and put it in Register Pair AF. This also will restore the CARRY FLAG if we entered here FOUT to indicate that we should NOT re-float the number
    0B9C
    RET NCD0
    IF called from FOUT, then RETurn to skip re-floating the number.
    0B9D-0B9F
    Jump to 0CD8H to re-float (“normalize”) the number

    0BA0H-0BA9H – LEVEL II BASIC MATH ROUTINE – “DINTA”

    0BA0-0BA2
     ↳ DINTA
    LD HL,411DHLD HL,DFACLO21 1D 41
    If we enter from DINTA, our purpose is to subtract 1 from the ACCumulator, so we point HL to the LSB of the ACCumulator.
    0BA3
     ↳ DINTA1
    LD A,(HL)7E
    Regardless of how we enter this routine, the purpose now is to subtract 1 from (HL). To do that, first get the value into A
    0BA4
    DEC (HL)35
    Decrement the value at the location of the memory pointer in Register Pair HL by 1
    0BA5
    OR AB7
    We really only want to continue if the byte uCheck to see if the byte used to be ZERO, so test the byte
    0BA6
    INC HL23
    Increment the value of the memory pointer in Register Pair HL to point to the next byte
    0BA7-0BA8
    Loop until the value at the location of the memory pointer in Register Pair HL is equal to a nonzero value
    0BA9
    RETC9
    RETurn to CALLer

    0BAAH-0BC6H – LEVEL II BASIC MATH ROUTINE – “UMULT”

    This is the integer multiply routine for multiplying dimensioned array. It will calculate DE = BC * DE. If there is an overflow, a ?BS ERROR will get thrown. Every register except HL is affected.
    0BAA
     ↳ UMULT
    PUSH HLE5
    Save the value in Register Pair HL on the STACK
    0BAB-0BAD
    LD HL,0000H21 00 00
    Load Register Pair HL with zero to zero the product registers
    0BAE
    0BAF
    LD A,B
    OR C78 B1
    First let’s see if (BC) is zero by loading Register A with the MSB of the integer value in Register B and then ORing the LSB held in Register C
    0BB0-0BB1
    If BC is already zero, then just return, since HL is already zero
    0BB2-0BB3
    LD A,10H3E 10
    Load Register A with the counter value (which is 16)
    0BB4
     ↳ UMULT1
    ADD HL,HL29
    Top of a loop. Multiply the result in Register Pair HL by two
    0BB5-0BB7
    If the CARRY FLAG was set, then we have an overflow, which we handle by displaying a ?BS ERROR message
    0BB8
    EX DE,HLEB
    Save the product so far into Register Pair DE
    0BB9
    ADD HL,HL29
    Multiply the integer value in Register Pair HL by two
    0BBA
    EX DE,HLEB
    Swap DE and HL so DE now holds HL * 4 and HL holds HL * 2
    0BBB-0BBC
    If the HIGH ORDER/MSB from the HL addition was 1, then we need to add in (BC) so JUMP to UMULT2 to do that
    0BBD
    ADD HL,BC09
    Add the integer value in Register Pair BC to the result in Register Pair HL
    0BBE-0BC0
    Display a BS ERROR message if the result in Register Pair HL has overflowed
    0BC1
     ↳ UMULT2
    DEC A3D
    Decrement the counter in Register A
    0BC2-0BC3
    Loop until the multiplication has been completed
    0BC2-0BC3
    Loop until the multiplication has been completed
    0BC4
     ↳ MULRET
    EX DE,HLEB
    Swap so that the return result is in DE. We don’t care about HL because …
    0BC5
    POP HLE1
    … restore the original HL from the STACK
    0BC6
    RETC9
    RETurn to CALLer
    The next bunch of routines are the integer arithmetic routines. According to the original ROM source code, the conventions are.
    • Integer variables are 2 byte signed numbers, with the LSB coming first
    • For one argument functions, the argument is in (HL) and the results are put into (HL)
    • For two argument operations, the first argument is in (DE), the second in (HL), and the restuls are left in the ACCumulator and, if there was no overflow, (HL). If there was an overflow, then the arguments are converted to single precision.
    • When integers are stored in the ACCumulator, they are stored at FACLO+0 and FACLO+1, with VALTYPE=2

    0BC7H-0BD1H – INTEGER SUBTRACTION – “ISUB”

    Integer subtract. (ACCumulator=DE-HL) The result is returned in both ACCumulator and, if there was no overflow, the HL Register Pair.
    Subtracts the value in DE from the value in HL. The difference is left in the HL Register Pair. DE is preserved. In the event of underflow, both values are converted to single precision and the subtraction is repeated. The result is left in the ACCumulator and the mode flag is updated accordingly.

    Note: If you wanted to subtract 2 integers via a ROM call, store one into DE and the subtrahend in HL (i.e., to do 26-17, DE gets 26), and then call 0BC7H. The integer result will be stored in 4121H-4122H approximately 210 microseconds later, and 40AFH will be set to 2 (to flag it as an integer). If there is an overflow, it will be converted to single precision (with 40AFH being a 4 in that case) and will be stored in 4121H-4124H.

    Every register is affected.

    Integer Subtraction: Subtracts the value in DE from the value in HL. The difference is left in the HL Register Pair. DE is preserved. In the event of underflow, both values are converted to single precision and the subtraction is repeated. The result is left in the ACCumulator and the mode flag is updated accordingly
    0BC7
     ↳ ISUB
    LD A,H7C
    The first thing we need to do is to extend the sign of (HL) into Register B. That’s the next 4 instructions. First, load Register A with the MSB+SIGN of the integer value in Register H
    0BC8
    RLA17
    Rotate the value of the sign bit into the CARRY FLAG
    0BC9
    SBC A,A9F
    Adjust Register A according to the value of the sign bit
    0BCA
    LD B,A47
    Load Register B with the result of the sign test
    0BCB-0BCD
    Negate (HL) via a GOSUB to INEGHL
    0BCE
    LD A,C79
    Load Register A with zero
    0BCF
    SBC A,B98
    Negate the sign
    0BD0-0BD1
    Jump to 0BD5H to add the numbers

    0BD2H-0BF1H – INTEGER ADDITION – “IADD”

    Integer addition (ACCumulator=DE+HL), where ACCumulator = 4121H-4122H. After execution NTF=2, or 4 if overflow has occurred, in which case the result in the ACCumulator will be single-precision. The result is returned in both ACCumulator and the HL Register Pair.

    Adds the integer value in DE to the integer in HL. The sum is left in HL and the orginal contents of DE are preserved. If overflow occurs (sum exceeds 2**15), both values are converted to single precision and then added. The result would be left in the ACCumulator and the mode flag would be updated.

    Every register is affected.

    Note: If you wanted to add 2 integers via a ROM call, store one input into DE and the other into HL, and then call 0BD2H. The result will be in 4121H-4122H and in HL, with a 2 in 40AFH, and will take about 130 microseconds. If there is an overflow, the result will be converted to Single Precision and put into 4121H-4124H (with a 4 in 40AFH).
    0BD2
     ↳ IADD
    LD A,H7C
    The first thing we need to do is to extend the sign of (HL) into Register B. That’s the next 4 instructions. First, load Register A with the MSB+SIGN of the integer value in Register H
    0BD3
    RLA17
    Rotate the value of the sign bit into the CARRY FLAG
    0BD4
    SBC A,A9F
    Adjust Register A according to the value of the sign bit
    0BD5
     ↳ IADDS
    LD B,A47
    Load Register B with the result of the sign test
    0BD6
    PUSH HLE5
    Save the second argument (held in Register Pair HL) to the the STACK in case we have an overflow
    0BD7
    LD A,D7A
    The next 4 instructions extend the sign of (DE) into Register A. First, load Register A with the MSB+SIGN of the integer value in Register Pair DE
    0BD8
    RLA17
    Rotate the value of the sign bit into the CARRY FLAG
    0BD9
    SBC A,A9F
    Adjust Register A according to the value of the sign bit
    0BDA
    ADD HL,DE19
    Add the two LSBs, result in Register Pair HL
    0BDB
    ADC A,B88
    Add the extra HIGH ORDER (held in Register B) to the value of the sign test for the integer value in Register Pair DE in Register A
    0BDC
    RRCA0F
    The next 2 instructions are to see if the LSB of A is different from the MSB of H, in which ase an overflow occurred. So, put the value of the Carry flag in Register A
    0BDD
    XOR HAC
    Combine the value of the sign bit for the result in Register H with the value in Register A
    0BDE-0BE0
    If the P FLAG is set, then we had no overflow. In this case, we need to restore the original (HL) from the stack and we are done. So JUMP to CONIS1 to do all that AND put (HL) into the ACCumulator as well.
    0BE1
    PUSH BCC5
    If we are here then we have an overflow. First, save the extended sign of (HL) (held in Register B) to the STACK
    0BE2
    EX DE,HLEB
    Load Register Pair HL with the integer value in Register Pair DE
    0BE3-0BE5
    Go float the Register value in Register Pair HL to single precision and return with the result in the ACCumulator
    0BE6
    POP AFF1
    Get the sign of (HL) from the STACK and put it in Register A
    0BE7
    POP HLE1
    Get the old (HL) back from the STACK
    0BE8-0BEA
    Call 09A4 which moves the SINGLE PRECISION value in the ACCumulator to the STACK (stored in LSB/MSB/Exponent order)
    0BEB
    EX DE,HLEB
    Load Register Pair DE with the integer value in Register Pair HL, as FLOATR needs DE to hold the value
    0BEC-0BEE
    Go float the integer value in Register Pair DE to single precision and return with the result in the ACCumulator
    0BEF-0BF1
    At this point the basic values are good enough to be added via single precision, so JUMP to FADDT to do that

    0BF2H-0C1EH – INTEGER MULTIPLICATION – “IMULT”

    Integer multiply. (ACCumulator (and HL) =DE*HL). Multiplies HL by DE. The product is left in HL and DE is preserved. If overflow occurs, both values are converted to single precision and the operation is restarted. The product would be left in the ACCumulator.

    Note: If you wanted to multiply two integers, store one input in DE, the other in HL CALL 0BF2H. The result is in 4121H-4122H and in HL, with a 2 in 40AFH (but in an overflow the result is converted to single precision format and stored in 4121H-4124H, with a 4 in 40AFH. Process takes approximately 900 microseconds.
    0BF2
     ↳ IMULT
    LD A,H7C
    Load Register A with the MSB of the integer value in Register H
    0BF3
    OR LB5
    Combine the LSB of the integer value in Register L with the MSB of the integer value in Register A
    0BF4-0BF6
    If the ZERO flag is set, then HL is zero, and if so, just return
    0BF7
    PUSH HLE5
    In case of an overflow, we are going to need our original arguments. Save the integer value in Register Pair HL on the STACK
    0BF8
    PUSH DED5
    Save the integer value in Register Pair DE on the STACK
    0BF9-0BFB
    Go convert any negative integer values to positive and return with Register B set according to the value of the sign bits
    0BFC
    PUSH BCC5
    Save the value of the sign bit test in Register B on the STACK
    0BFD
    0BFE
    LD B,H
    LD C,L44
    Copy the second argument from HL into BC
    0BFF-0C01
    LD HL,0000H21 00 00
    Start Register Pair HL at zero, as the result will go into HL
    0C02-0C03
    LD A,10H3E 10
    Load Register A with the counter value (which is 16)
    0C04
     ↳ IMULT1
    ADD HL,HL29
    Multiply the result in Register Pair HL by two
    0C05-0C06
    If that caused an overflow, then JUMP to IMULT5
    The next 6 instruction are to roate the first argument left one to see if we need to add BC to it or not. If the NC FLAG is set, then we don’t add in BC. Otherwise we do.
    0C07
    EX DE,HLEB
    Exchange the integer value in Register Pair DE with the integer result in Register Pair HL
    0C08
    ADD HL,HL29
    Multiply the integer value in Register Pair HL by two
    0C09
    EX DE,HLEB
    Exchange the integer result in Register Pair DE with the integer value in Register Pair HL
    0C0A-0C0B
    If the NC FLAG is set, then skip the next instructions which add in BC
    0C0C
    ADD HL,BC09
    Add the integer value in Register Pair BC to the integer result in Register Pair HL
    0C0D-0C0F
    If we have overflowed by adding in BC, then JUMP to IMULT5
    0C10
     ↳ IMULT2
    DEC A3D
    Decrement the value of the counter in Register A
    0C11-0C12
    Loop until the multiplication has been completed
    0C13
    POP BCC1
    At this point we are done, so we need to finish up. First, get the value of the sign test from the STACK and put it in Register B
    0C14
    POP DED1
    Get the original FIRST argument from the STACK and put it in Register Pair DE
    This is the entry from IDIV. The next instructions test to see if the result is => 32768 or is -32768.
    0C15
     ↳ IMLDIV
    LD A,H7C
    Load Register A with the MSB of the result in Register H
    0C16
    OR AB7
    Test Register H
    0C17-0C19
    If the M FLAG is set, then the result is =gt; 32768, so JUMP to IMULT3 to make sure it isn’t -32768.
    0C1A
    POP DED1
    If we are here, then the number is OK, so get the SECOND argument off the stack and into Register Pair DE
    0C1B
    LD A,B78
    Load Register A with the value of the sign test in Register B
    0C1C-0C1E
    Jump to 0C4DH to NEGate the number, if needed and RETurn

    0C1FH-0C34H – LEVEL II BASIC MATH ROUTINE – “IMULT3”

    0C1F-0C20
     ↳ IMULT3
    XOR 80HEE 80
    Clear the sign bit for the MSB of the integer value in Register A which is 1000 0000
    0C21
    OR LB5
    Combine the value of the LSB for the integer value in Register L with the adjusted MSB of the integer value in Register A
    0C22-0C23
    If the Z FLAG is set, then the result is 32768, so JUMP to IMULT4
    0C24
    EX DE,HLEB
    If we are hre, then it is > 32768 giving us an overflow, so Load Register Pair HL with the integer value in Register Pair DE
    0C25-0C28
    LD BC,E1C1H01 C1 E1
    Z-80 Trick – See the note at 0134H for an explanation
    0C26
     ↳ IMULT5
    POP BCCF
    Get the value of the sign test from the STACK and put it in Register B
    0C27
    POP HL0A
    Get the original FIRST argument from the STACK and put it in Register Pair HL
    0C28-0C2A
    Go float the FIRST argument (held in in Register Pair HL) to single precision and return with the result in the ACCumulator
    0C2B
    POP HLE1
    Get the original SECOND argument from the STACK and put it in Register Pair HL
    0C2C-0C2E
    Save the floated FIRST agument via a GOSUB to 09A4 which moves the SINGLE PRECISION value in the ACCumulator to the STACK (stored in LSB/MSB/Exponent order)
    0C2F-0C31
    Go float the SECOND argument (held in in Register Pair HL) to single precision and return with the result in the ACCumulator
    0C32
     ↳ FMULTT
    POP BCC1
    Get the FIRST argument off the stack and put it in Register Pair BC. POLYX jumps here.
    0C33
    POP DED1
    Get the NMSB and the LSB of the single precision value from the STACK and put it in Register Pair DE
    0C34-0C36
    Multiply the arguments via regular old FMULT – the SINGLE PRECISION MULTIPLY routine at 0847H (which multiplies the current value in the ACCumulator by the value in (BC/DE). The product is left in the ACCumulator

    0C37H-0C44H – LEVEL II BASIC MATH ROUTINE – “IMULT4”

    0C37
     ↳ IMULT4
    LD A,B78
    We need to see if the result is +/- 32768. First, load Register A with the result of the sign test in Register B
    0C38
    OR AB7
    Check the result
    0C39
    POP BCC1
    Discard the original SECOND argument from the STACK
    0C3A-0CJC
    Jump if the result is supposed to be negative
    0C3D
    PUSH DED5
    If we are here, then the result is positive. Save the remainder for MOD to the STACK
    0C3E-0C40
    Go float -32768 and return with the result in the ACCumulator
    0C41
    POP DED1
    Get the MOD’s remainder from the STACK and put it in Register Pair DE
    0C42-0C44
    Jump to 0982H to turn -32768 into 32768 and finish up

    0C45H-0C5AH – LEVEL II BASIC MATH ROUTINE – “IMULDV”

    This is the integer division routine HL = DE / HL. The remainder will be left in DE and the quotient will be left in HL. Every register is affected.
    0C45
     ↳ IMULDV
    LD A,H7C
    Load Register A with the MSB+SIGN of the integer value in Register H
    0C46
    XOR DAA
    Combine the MSB of the integer value in Register D with the MSB+SIGN of the integer value in Register A
    0C47
    LD B,A47
    Save the result of the combined signs in Register A into Register B
    0C48-0C4A
    If necessary, NEGate the SECOND argument (i.e., the value in Register Pair HL) to positive
    0C4B
    EX DE,HLEB
    Presetve the contents of Register DE into Register Pair HL, and fall through to the negation routine below.
    0C4C
     ↳ INEGH
    LD A,H7C
    Load Register A with the MSB+SIGN of the integer value in Register H
    0C4D
     ↳ INEGA
    OR AB7
    Set the condition codes so we can see the sign of HL
    0C4E-0C50
    If the P FLAG is set, then the integer value in Register Pair HL is positive and we don’t need to NEGate it. So we JUMP to MAKINT to save the result into the ACCumulator for when the operators come back tt this routine.
    Negate HL routine. This routine changes the sign of the HL Register Pair and stores it in the ACC. (HL=ACCumulator=-HL) The result is returned in both the HL Register Pair and the ACC.
    0C51
     ↳ INEGHL
    XOR AAF
    Zero Register A.
    0C52
    LD C,A4F
    Load Register C with the ZERO held in Register A
    0C53
    SUB L95
    Subtract the LSB of the integer value in Register L from the ZERO in Register A
    0C54
    LD L,A6F
    Save the adjusted value in Register A in Register L
    0C55
    LD A,C79
    Load Register A with a ZERO
    0C56
    SBC A,H9C
    Subtract the HIGH ORDER (MSB+SIGN) of the integer value in Register H from the value in Register A
    0C57
    LD H,A67
    Save the adjusted value in Register A into Register H
    0C58-0C5A
    Jump to 0A9AH to save the result into the ACCumulator for when the operations jump back here.

    0C5BH-0C6FH – LEVEL II BASIC MATH ROUTINE – “INEG”

    Integer Negation Routine. All registers are altered.
    0C5B-0C5D
     ↳ INEG
    LD HL,(4121H)LD HL,(FACLO)2A 21 41
    Load Register Pair HL with the integer value in the ACCumulator
    0C5E-0C60
    Go convert the integer value in Register Pair HL to positive if it’s negative
    0C61
    LD A,H7C
    Load Register A with the HIGH ORDER (i.e., MSB+SIGN) of the integer value in Register H
    0C62-0C63
    XOR 80HXOR 1000 0000EE 80
    Invert the value of the sign bit in Register A which is 1000 0000 so that we can check for the special case of -32768
    0C64
    OR LB5
    Combine the LSB of the integer value in Register L with the adjusted MSB of the integer value in Register A
    0C65
    RET NZC0
    Return if the integer value in the ACCumulator isn’t equal to -32768
    0C66
     ↳ INEG2
    EX DE,HLEB
    If we are here, the magic -32768 was found, so we need to float it. First, load Register Pair DE with the integer value in Register Pair HL
    0C67-0C69
    Go set the number type flag to single precision
    0C6A
    XOR AAF
    Zero Register A, which we will use for the HIGH ORDER
    0C6B-0C6C
     ↳ INEGAD
    LD B,98H06 98
    Load Register B with an exponent. IADD jumps here.
    0C6D-0C6F
    Float the number via a JUMP to 0969H

    DOUBLE PRECISION ROUTINES

    The next bunch of routines are the double precision arithmetic routines. According to the original ROM source code, the conventions are.
    • Double prevision numbers are 8 bytes long: The first 4 bytes are 32 low order bits of precision and the last 4 bytes are are in the same format as single precision numbers. The lowest order byte comes first in RAM.
    • For one argument gunctions: The argument is in the ACCumulator, and the results is put there too.
    • For two argument operations, the first argument is in the ACCumulator and the second argument is in ARG-7,-6,-5,-4,-3,-2,-1,-0. ARGLO=ARG-7. The result is left in the ACCumulator.
    • Note that the order of the numbers is reversed from integers and single precisions values

    0C70H-0C76H – DOUBLE PRECISION SUBTRACTION – “DSUB”

    Double-precision subtraction (ACCumulator = ACCumulator – ARG).
    Subtracts the double precision value in ARG (a/k/a REG 2) from the value in the ACCumulator. The difference is left in the ACCumulator.

    Note: If you wanted to subtract two double precision numbers, store the minuend in 411DH-4124H and the subtrahend in 4127H-412EH, and CALL 0C70H. The result (in double precision format) is in 411DH-4124H in approximately 1.3 milliseconds.
    0C70-0C72
     ↳ DSUB
    LD HL,412DHLD HL,ARG-121 2D 41
    Since addition is easier than subtraction, first we need to negate the SECOND argument by first loading Register Pair HL with the address of the MSB in ARG (a/k/a REG 2)
    0C73
    LD A,(HL)7E
    Load Register A with the HIGH ORDER (i.e., MSB+SIGN) of the double precision value in ARG (a/k/a REG 2) at the location of the memory pointer in Register Pair HL
    0C74-0C75
    XOR 80HXOR 1000 0000EE 80
    Invert the value of the sign bit for the MSB of the double precision value in Register A which is 1000 0000
    0C76
    LD (HL),A77
    Save the adjusted HIGH ORDER (i.e., MSB+SIGN) of the double precision value in Register A in ARG (a/k/a REG 2) at the location of the memory pointer in Register Pair HL. To save RAM we now fall into the DADD addition routine.

    0C77H-0CCEH -DOUBLE PRECISION ADDITION – “DADD”

    Double-precision addition (ACCumulator=ACCumulator+ARG (a/k/a REG 2)).
    Adds the double precision value in ARG (a/k/a REG 2) to the value in the ACCumulator. Sum is left in the ACCumulator. All registers are affected.

    Note: If you wanted to add 2 double precision numbers via a ROM call, store one input into 411DH-4124H and the other in 4127H-412EH. Then call 0C77H. The double precision result will be stored in 411DH-4124H approximately 1.3 milliseconds later.
    0C77-0C79
     ↳ DADD
    LD HL,412EHLD HL,ARG21 2E 41
    Load Register Pair HL with the address of the exponent in the FIRST argument held at ARG (a/k/a REG 2)
    0C7A
    LD A,(HL)7E
    Prepare to test that for ZERO by first loading Register A with the exponent of the double precision value in ARG (a/k/a REG 2) at the location of the memory pointer in Register Pair HL
    0C7B
    OR AB7
    Check to see if the double precision value in ARG (a/k/a REG 2) is equal to zero
    0C7C
    RET ZC8
    Return if the double precision value in ARG (a/k/a REG 2) is equal to zero, since that means that the ACCumulator (i.e., the FIRST argument) is actually the sum
    0C7D
    LD B,A47
    Preserve the exponent for the double precision value in Register A into Register C
    0C7E
    DEC HL2B
    Decrement the value of the memory pointer in Register Pair HL to now point to the HIGH ORDER (i.e., MSB + SIGN) for unpacking
    0C7F
    LD C,(HL)4E
    Load Register C with the value of the HIGH ORDER (i.e., MSB + SIGN) of the double precision value in ARG (a/k/a REG 2) at the location of the memory pointer in Register Pair HL
    0C80-0C82
    LD DE,4124HLD DE,FAC11 24 41
    Load Register Pair DE with the address of the exponent of the SECOND argument (held in the ACCumulator)
    0C83
    LD A,(DE)1A
    Fetch the value of the exponent of the double precision value in the ACCumulator at the location of the memory pointer in Register Pair DE
    0C84
    OR AB7
    Set the flags to see if the double precision value in the ACCumulator is equal to zero
    0C85-0C87
    If the exponent is zero, then the number is zero, so we are once again adding 0 to a number. In this case, the non-zero number is in the wrong reghister, so JUMP to VMOVFA to move ARG to the ACCumulator and exit.
    0C88
    SUB B90
    Now we know we do not have any zero’s, so we next need to get the shift count by subtracting the exponents. First, subtract the value of the exponent for the double precision value in ARG (a/k/a REG 2) in Register B from the value of the exponent for the double precision value in the ACCumulator in Register A
    0C89-0C8A
    If the NC FLAG is set, then the we need to put the smaller number into the ACCumulator, so JUMP to DADD2 to do that
    0C8B
    CPL2F
    Negate the shift count held in Register A
    0C8C
    INC A3C
    Increment the value of the difference for the exponents in Register A so that Register A will hold the positive difference
    0C8D
    PUSH AFF5
    Save the shift count (i.e., the difference for the exponents, held in Register A) to the STACK
    Next we are going to switch ARG and the ACCumulator.
    0C8E-0C8F
    LD C,08H0E 08
    Load Register C with a counter value which is 8
    0C90
    INC HL23
    Increment the value of the memory pointer in Register Pair HL so that it will be pointing to the exponent of the double precision value in ARG (a/k/a REG 2)
    0C91
    PUSH HLE5
    Save the value of the memory pointer in Register Pair HL (which is pointing to ARG) on the STACK
    0C92
     ↳ DADD1
    LD A,(DE)1A
    Top of a loop. Load Register A with the value of the ACCumulator pointed to by Register Pair DE
    0C93
    LD B,(HL)46
    Load Register B with the value of ARG (a/k/a REG 2) pointed to by Register Pair DE
    0C94
    LD (HL),A77
    Save the ACCumulator value into the corresponding ARG (a/k/a REG 2) byte.
    0C95
    LD A,B78
    Load Register A with the ARG byte (held in Register B)
    0C96
    LD (DE),A12
    Save the ARG byte (held in Register A) into the corresopnding ACCumulator byte (pointed to by Register Pair DE)
    0C97
    DEC DE1B
    Decrement the value of the memory pointer in Register Pair DE to the next lower byte of the ACCumulator
    0C98
    DEC HL2B
    Decrement the value of the memory pointer in Register Pair HL to the next lower byte in ARG
    0C99
    DEC C0D
    Decrement the value of the counter in Register C
    0C9A-0C9B
    Loop until the double precision values in the ACCumulator and ARG (a/k/a REG 2) have been exchanged
    0C9C
    POP HLE1
    Get the HIGH ORDER back from the stack into Register Pair HL
    0C9D
    LD B,(HL)46
    Fetch the exponent for the double precision value in ARG (a/k/a REG 2) pointed to by Register Pair HL
    0C9E
    DEC HL2B
    Decrement the value of the memory pointer in Register Pair HL to now point to the HIGH ORDER (MSB + SIGN)
    0C9F
    LD C,(HL)4E
    Load Register C with the value of the MSB+SIGN for the double precision value in ARG (a/k/a REG 2) at the location of the memory pointer in Register Pair HL
    0CA0
    POP AFF1
    Get the shift count (i.e., difference for the exponents) back into Register A
    0CA1-0CA2
     ↳ DADD2
    CP 39HFE 39
    Check to see if the difference between the two exponents is greater than 56 bits
    0CA3
    RET NCD0
    Return if the difference between the two exponents is greater than 56 bits
    0CA4
    PUSH AFF5
    Save the shift count (i.e., difference for the exponents) from Register A onto the STACK
    0CA5-0CA7
    Go turn on the sign bits for the double precision numbers
    0CA8
    INC HL23
    Increment the value of the memory pointer in Register Pair HL to now point to ARGLO-1
    0CA9-0CAA
    LD (HL),00H36 00
    Zero the temporary LSB (held at the location of the memory pointer in Register Pair HL)
    0CAB
    LD B,A47
    Preserve the sign test into Register B
    0CAC
    POP AFF1
    Restore the shift count (i.e., difference for the exponents) from the STACK into Register A
    0CAD-0CAF
    LD HL,412DHLD HL,ARG-121 2D 41
    Load Register Pair HL with the address of the HIGH ORDER in ARG (a/k/a REG 2)
    0CB0-0CB2
    Go shift the double precision value in ARG (a/k/a REG 2) until it lines up with the double precision value in the ACCumulator
    0CB3-0CB5
    LD A,(4126H)LD A,(ARGLO-1)3A 26 41
    We next need to transfer the OVERFLOW byte from ARG to ACCumulator, so first put it in Register A
    0CB6-0CB8
    LD (411CH),ALD (DFACLO-1),A32 1C 41
    Save the value in Register A to ARG
    0CB9
    LD A,B78
    Load Register A with the value of the sign test in Register B
    0CBA
    OR AB7
    Check to see if the signs are equal
    0CBB-0CBD
    If the P FLAG is set, then the signs of the numbers are different, so JUMP to DADD3 to subtract the values
    0CBE-0CC0
    Otherwise (i.e., the signs are the same) GOSUB to DADDAA to add the numbers
    0CC1-0CC3
    If that didn’t trigger a NC FLAG, then JUMP to 0D0EH to ROUND the result and continue on.
    0CC4
    EX DE,HLEB
    If that DID trigger the NC FLAG, then put the pointer to the exponent of the ACCumulator into HL
    0CC5
    INC (HL)34
    Add one to the exponent (since we had an overflow)
    0CC6-0CC8
    Check for OVERFLOW because of that too! If the Z FLAG is set, then display an ?OV ERROR if the exponent for the double precision result in the ACCumulator is too large
    0CC9-0CCB
    If we still have no overflow, then we need to shift the number right one so as to shift in the CARRY FLAG. To do this we GOSUB to DSHFRB
    0CCC-0CCE
    JUMP to 0D0EH to ROUND the result and continue on.

    0CCFH-0D1FH – DOUBLE PRECISION MATH ROUTINE – “DADD3”

    0CCF-0CD1
     ↳ DADD3
    Go subtract the double precision values
    0CD2-0CD4
    LD HL,4125HLD HL,FAC+121 25 41
    Right now HL isn’t pointing where we need it to point for a call to DNEGR, so load Register Pair HL with the address of the sign, and then, to save RAM, fall through into DNORML.
    0CD5-0CD7
    Go complement the result in the ACCumulator if the Carry flag is set

    0CD8H – DOUBLE PRECISION MATH ROUTINE – “DNORML” and “DNORM1”

    0CD8
     ↳ DNORML
    XOR AAF
    Zero Register A, which will act as a byte shift counter
    0CD9
     ↳ DNORM1
    LD B,A47
    Preserve the shift counter into Register Register B
    0CDA-0CDC
    LD A,(4123H)LD A,(FAC-1)3A 23 41
    Load Register A with the value of the HIGH ORDER (i.e., MSB+SIGN) of the double precision result in the ACCumulator
    0CDD
    OR AB7
    Check to see if we can shift 8 numbers to the left
    0CDE-0CDF
    If the NZ FLAG is set, then we cannot shift 8 numbers left, so we need to JUMP to see if the number is already normalized.
    0CE0-0CE2
    LD HL,411CHLD HL,DFACLO-121 1C 41
    If we are here then we CAN shift 8 numbers left, so first load Register Pair HL with the starting address of ACCumulator minus one.
    0CE3-0CE4
    LD C,08H0E 08
    Load Register C with the number of bytes to be shifted (i.e., 8)
    0CE5
     ↳ DNORM2
    LD D,(HL)56
    Top of a loop. Load Register D with a byte from the ACCumulator (pointed to by Register Pair HL)
    0CE6
    LD (HL),A77
    Save the value in Register A to the newly vacated location at the memory pointer in Register Pair HL. Note that on the FIRST loop, this is a zero.
    0CE7
    LD A,D7A
    Put the current byte from the ACCumulator (preserved in D) into Register A for writing on the next iteration
    0CE8
    INC HL23
    Increment the value of the memory pointer in registerpair HL
    0CE9
    DEC C0D
    Decrement the number of bytes to be shifted in Register C
    0CEA-0CEB
    Loop until all of the bytes in the double precision value have been shifted
    0CEC
    LD A,B78
    Now that we did an 8 byte shift, we need to subtract 8 from the shift counter. First, load Register A with the number of bits shifted in Register B
    0CED-0CEE
    SUB 08HD6 08
    Subtract the number of bits just shifted from the shift counter in Register A
    0CEF-0CF0
    CP C0HFE C0
    Check to see if the whole of the double precision value has been shifted
    0CF1-0CF2
    If the whole of the double precision value hasn’t been shifted, JUMP back to DNORM1 to shift some more
    0CF3-0CF5
    If we are here, then we have shifted all the bytes so JUMP to ZERO

    0CF6H – Part of the “DNORML” and “DNORM1” Routine

    0CF6
     ↳ DNORM3
    DEC B05
    Decrement the shift counter held in Register B
    0CF7-0CF9
    LD HL,411CHLD HL,DFACLO-121 1C 41
    Load Register Pair HL with the starting address of ACCumulator minus one.
    0CFA-0CFC
    Shift the double precision value in the ACCumulator once to the left
    0CFD
    OR AB7
    Check to see if the number has been normalized yet
    0CFE-0D00
     ↳ DNORM5
    If the P FLAG is set, then we are not yet normalized, so LOOP back to DNORM3 and keep shifting
    0D01
    LD A,B78
    Load Register A with the value of the shift counter from Register B
    0D02
    OR AB7
    Check to see if the shift counter in Register A is equal to zero
    0D03-0D04
    If the shift counter is zero, then proceed to round the number and finish up by JUMPing to DROUND
    0D05-0D07
    LD HL,4124HLD HL,FAC21 24 41
    Load Register Pair HL with the address of the exponent in the ACCumulator
    0D08
    ADD A,(HL)86
    Add the value of the exponent for the double precision value in the ACCumulator at the location of the memory pointer in Register Pair HL to the value of the shift counter in Register A
    0D09
    LD (HL),A77
    Save the adjusted exponent for the double precision value in Register A at the location of the memory pointer in Register Pair HL
    0D0A-0D0C
    If the NC FLAG was triggered, then we have an UNDERFLOW, so JUMP to ZERO
    0D0D
    RET ZC8
    If the Z FLAG is set, then the result is already zero and we are done, so FALL into the DROUND routine and round the result.

    0D0EH – DOUBLE PRECISION MATH ROUTINE – “DROUND” and “DROUNB”

    This routine will round the ACCumulator. Registers A, B, H, and L are affected.
    0D0E-0D10
     ↳ DROUND
    LD A,(411CH)LD A,(DFACLO-1)3A 1C 41
    Load Register A with the value of the rounding byte at the location of the starting address of ACCumulator minus one.
    0D11
     ↳ DROUNB
    OR AB7
    Check to see if there is a bit to be shifted into the double precision value in the ACCumulator
    0D12-0D14
    Go move the bit into the double precision value if necessary
    0D15-0D17
    LD HL,4125HLD HL,FAC+121 25 41
    Load Register Pair HL with the address of the unpacked sign for the result.
    0D18
    LD A,(HL)7E
    Load Register A with the value of the sign for the result at the location of the memory pointer in Register Pair HL
    0D19-0D1A
    AND 80HAND 1000 0000E6 80
    Turn off some bits so we can mask the value of the sign for the result in Register A which is 1000 0000 to isolate the sign bit.
    0D1B
    0D1C
    DEC HL
    DEC HL2B
    Decrement the value of the memory pointer in Register Pair HL twice so that it points to HIGH ORDER (MSB) byte in the ACCumulator
    0D1D
    XOR (HL)AE
    Pack the SIGN and the MSB together
    0D1E
    LD (HL),A77
    Save the packed sign and MSB combination byte to the ACCumulator at the location of the memory pointer in Register Pair HL
    0D1F
    RETC9
    RETurn to CALLer

    0D20H-0D32H – DOUBLE PRECISION MATH support routine – “DROUNA”

    0D20-0D22
     ↳ DROUNA
    LD HL,411DHLD HL,DFACLO21 1D 41
    Set up HL to point to the LSB of the the ACCumulator.
    Note: 411DH-4124H holds ACCumulator
    0D23-0D24
    LD B,07H06 07
    Load Register B with the number of bytes to be bumped for the double precision value in the ACCumulator
    0D25
     ↳ DRON1
    INC (HL)34
    Top of a loop. Increment a byte of the ACCumulator at the location of the memory pointer in Register Pair HL
    0D26
    RET NZC0
    Return if the value at the location of the memory pointer in Register Pair HL isn’t equal to zero
    0D27
    INC HL23
    Increment the value of the memory pointer in Register Pair HL to point to the next highest order in the ACCumulator
    0D28
    DEC B05
    Decrement the value of the byte counter in Register B
    0D29-0D2A
    Loop until all of the necessary bytes have been bumped
    0D2B
    INC (HL)34
    We have bumped all the bytes, so now we need to increment the value of the exponent at the location of the memory pointer in Register Pair HL
    0D2C-0D2E
    Check for overflow. If the Z FLAG is set, then JUMP to the Level II BASIC error routine and display an OV ERROR message if the exponent for the double precision value in the ACCumulator is too large
    0D2F
    DEC HL2B
    Decrement the value of the memory pointer in Register Pair HL to point to the HIGH ORDER
    0D30-0D31
    LD (HL),80H36 80
    Save a new MSB+SIGN at the location of the memory pointer in Register Pair HL
    0D32
    RETC9
    RETurn to CALLer

    0D33H-0D44H – DOUBLE PRECISION MATH ROUTINE – “DADDAA” and “DADDA”

    0D33-0D35
     ↳ DADDAA
    LD HL,4127HLD HL,ARGLO21 27 41
    DADD enters here, so we need to set both HL and DE. In that case, set HL to point to ARG (a/k/a REG 2).
    Note: 4127H-412EH holds ARG (a/k/a REG 2)
    0D36-0D38
     ↳ DADDFO
    LD DE,411DHLD DE,DFACLO11 1D 41
    FOUT enters here, and DADD passes through to here. Load Register Pair DE with the starting address of ACCumulator.
    Note: 411DH-4124H holds ACCumulator
    0D39-0D3A
     ↳ DADDS
    LD C,07H0E 07
    Load Register C with the number of bytes to be added
    0D3B
    XOR AAF
    Clear the Carry flag
    0D3C
     ↳ DADDLS
    LD A,(DE)1A
    Top of a loop. Load Register A with the value in the ACCumulator at the location of the memory pointer in Register Pair DE
    0D3D
    ADC A,(HL)8E
    Add the value in ARG (a/k/a REG 2) at the location of the memory value in Register A
    0D3E
    LD (DE),A12
    Save the result of that addition into the ACCumulator at the location of the memory pointer in Register Pair DE
    0D3F
    INC DE13
    Increment the value of the memory pointer in Register Pair DE
    0D40
    INC HL23
    Increment the value of the memory pointer in Register Pair HL
    0D41
    DEC C0D
    Decrement the number of bytes to be added in Register C
    0D42-0D43
    Loop until all of the bytes for the double precision values have been added
    0D44
    RETC9
    RETurn to CALLer

    0D45H-0D56H – DOUBLE PRECISION MATH ROUTINE – “DADDAS”

    This routine subtracts numbers in the pure version. This needs to be done in two subroutines since the ROM cannot be modified.
    0D45-0D47
     ↳ DADDAS
    LD HL,4127HLD HL,ARGLO21 27 41
    DADD enters here, so we need to set both HL and DE. In that case, set Register Pair HL with the starting address of ARG (a/k/a REG 2).
    Note: 4127H-412EH holds ARG (a/k/a REG 2)
    0D48-0D4A
     ↳ DADDFS
    LD DE,411DHLD DE,DFACLO11 1D 41
    FOUT enters here, and DADD passes through to here. Load Register Pair DE with the starting address of ACCumulator.
    Note: 411DH-4124H holds ACCumulator
    0D4B-0D4C
     ↳ DADDSS
    LD C,07H0E 07
    Load Register C with the number of bytes to be subtracted
    0D4D
    XOR AAF
    Clear the Carry flag
    0D4E
     ↳ DADDLS
    LD A,(DE)1A
    Top of a loop. Load Register A with the value in the ACCumulator at the location of the memory pointer in Register Pair DE
    0D4F
    SBC A,(HL)9E
    Subtract the value in ARG (a/k/a REG 2) at the location of the memory pointer in Register Pair HL from the value in Register A
    0D50
    LD (DE),A12
    Save the result in Register A in the ACCumulator at the location of the memory pointer in Register Pair DE
    0D51
    INC DE13
    Increment the value of the memory pointer in Register Pair DE
    0D52
    INC HL23
    Increment the value of the memory pointer in Register Pair HL
    0D53
    DEC C0D
    Decrement the number of bytes to be subtracted for the double precision values in Register C
    0D54-0D55
    Loop until all of the bytes for the double precision values have been subtracted
    0D56
    RETC9
    RETurn to CALLer

    0D57H-0D68H – DOUBLE PRECISION MATH ROUTINE – “DNEGR”

    This routine will negate the signed number held in the ACCumulator. Registers A, B, C, H, and L are affected. This routine is called by DADD and DINT.
    0D57
     ↳ DNEGR
    LD A,(HL)7E
    Load Register A with the value of the sign from the ACCumulator at the location of the memory pointer in Register Pair HL
    0D58
    CPL2F
    Complement the value of the sign in Register A
    0D59
    LD (HL),A77
    Save the value of the sign in Register A at the location of the memory pointer in Register Pair HL
    0D5A-0D5C
    LD HL,411CHLD HL,DFACLO-121 1C 41
    Load Register Pair HL with the starting address of ACCumulator minus one.
    0D5D-0D5E
    LD B,08H06 08
    Load Register B with the number of bytes to be reversed
    0D5F
    XOR AAF
    Zero Register A and clear the CARRY FLAG
    0D60
    LD C,A4F
    Load Register C with the ZERO
    0D61
     ↳ DNEGR1
    LD A,C79
    Top of a loop. Load Register A with the value in Register C
    0D62
    SBC A,(HL)9E
    NEGate a byte to the ACCumulator
    0D63
    LD (HL),A77
    Save the NEGated value in Register A back to the location of the memory pointer in Register Pair HL
    0D64
    INC HL23
    Increment the value of the memory pointer in Register Pair HL
    0D65
    DEC B05
    Decrement the number of bytes to be reversed in Register B
    0D66-0D67
    Loop until all of the bytes for the double precision number in the ACCumulator have been reversed
    0D68
    RETC9
    RETurn to CALLer

    0D69H-0D8FH – DOUBLE PRECISION MATH ROUTINE – “DSHFTR”

    This routine wwill shift the double precision value held in the ACCumulator to the right once.
    0D69
     ↳ DSHFTR
    LD (HL),C71
    Save the unpacked MSB of the double precision value in Register C at the location of the memory pointer in Register Pair HL
    0D6A
    PUSH HLE5
    Save the value of the memory pointer in Register Pair HL on the STACK
    0D6B-0D6C
     ↳ DSHFR1
    SUB 08HD6 08
    Subtract 8 from the number of bits to be shifted from the number of bits to be shifted in Register A
    0D6D-0D6E
    If we can shift 8 bits at once (which is then shifting a byte at a time) the NC FLAG will be set. If not, the CARRY FLAG will be sent and we need to JUMP to DSHFR3 to do it one byte at a time.
    0D6F
    POP HLE1
    Get the value of the memory pointer from the STACK and put it in Register Pair HL
    0D70
     ↳ DSHFRM
    PUSH HLE5
    Save the value of the memory pointer in Register Pair HL on the STACK. This is the entry point from DMULT.
    0D71-0D73
    LD DE,0800H11 00 08
    This LD command shifts a zero into the HIGH ORDER byte and sets up a counter
    0D74
     ↳ DSHFR2
    LD C,(HL)4E
    Top of a loop. Preserve a byte of the ACCumulator into Register C
    0D75
    LD (HL),E73
    Overwrite that location with the last byte (held in Register E)
    0D76
    LD E,C59
    Load Register E with the value in Register C so that THIS is the byte to write next.
    0D77
    DEC HL2B
    Decrement the value of the memory pointer in Register Pair HL to point to the next lower order byte
    0D78
    DEC D15
    Decrement the number of bits shifted in Register D
    0D79-0D7A
    Loop until all of the bits have been shifted
    0D7B-0D7C
    LOOP back to the top to see we can shift another 8 bits.

    0D69H-0D8FH – DOUBLE PRECISION MATH ROUTINE – “DSHFR3”

    0D7D-0D7E
     ↳ DSHFR3
    ADD 09HC6 09
    At this point, we cannot shift 8 bytes at once and need to do them individually. First, set a corrected shift counter
    0D7F
    LD D,A57
    Preserve the adjusted shift counter into Register D
    0D80
     ↳ DSHFR4
    XOR AAF
    Clear the CARRY FLAG
    0D81
    POP HLE1
    Restore the pointer to the HIGH ORDER byte into Register Pair HL
    0D82
    DEC D15
    Decrement the number of bits to be shifted in Register D
    0D83
    RET ZC8
    Return if all of the bits have been shifted
    0D84
     ↳ DSHFRA
    PUSH HLE5
    If all the bits have not been shifted, first save the pointer to the LOW ORDER byte. This is the entry from DADD and DMULT.
    0D85-0D86
    LD E,08H1E 08
    Load Register E with the counter of the number of bytes to be shifted
    0D87
     ↳ DSHFR5
    LD A,(HL)7E
    Top of a loop. Load Register A with a byte from the ACCumulator pointed to by HL
    0D88
    RRA1F
    Shift the value in Register A. RRA rotates the contents of Register A right one bit position, with Bit 0 going to the CARRY FLAG, and the CARRY FLAG going to Bit 7. RRA also can be used to divide a number in 2.
    0D89
    LD (HL),A77
    Put the rotated byte back
    0D8A
    DEC HL2B
    Decrement the value of the memory pointer in Register Pair HL so we deal with the next lower order byte
    0D8B
    DEC E1D
    Decrement the number of bytes to be shifted in Register E
    0D8C-0D8D
    Loop until all of the bits have been shifted
    0D8E-0D8F
    Loop until all of the bits have been shifted

    0D90H-0D96H – DOUBLE PRECISION MATH ROUTINE – “DSHFRB”

    This is the entry from DADD and DMULT.
    0D90-0D92
     ↳ DSHFRB
    LD HL,4123HLD HL,FAC-121 23 41
    Load Register Pair HL with the address of the HIGH PRDER (MSB) of the double precision value in the ACCumulator
    0D93-0D94
    LD D,01H16 01
    Load Register D with the number of bits to be shifted
    0D95-0D96
    Jump to 0D84H to shift HL D bits to the right

    0D97H-0DA0H – DOUBLE PRECISION MATH ROUTINE – “DSHFLC”

    This routine will rotate the ACCumulator left one. Register A, C, H, and L are affected.
    0D97-0D98
     ↳ DSHFLC
    LD C,08H0E 08
    Load Register C with the number of bytes to be shifted
    0D99
     ↳ DSHFTL
    LD A,(HL)7E
    Top of a loop. Load Register A with a byte from the ACCumulator pointed to by HL
    0D9A
    RLA17
    Rotate that byte left one bit
    0D9B
    LD (HL),A77
    Save the shifted byte (held in Register A) back to the ACCumulator at the location of the memory pointer in Register Pair HL
    0D9C
    INC HL23
    Increment the value of the memory pointer in Register Pair HL to point to the next higher order byte
    0D9D
    DEC C0D
    Decrement the byte counter in Register C
    0D9E-0D9F
    Loop until all of the bytes have been shifted
    0DA0
    RETC9
    RETurn to CALLer

    0DA1H-0DD3H – DOUBLE PRECISION MULTIPLICATION – “DMULT”

    Double-precision multiplication (ACCumulator=ACC*ARG (a/k/a REG 2)).
    Multiplies the double precision value in the ACCumulator by the value in ARG (a/k/a REG 2). The product is left in the ACCumulator.

    Note: If you wanted to multiply two double precision numbers store one operand in 411DH-4124H, and store the other in 4127H-412EH and then CALL 0DA1H. The result (in double precision format) is in 411DH-4124H in approximately 22 milliseconds.
    0DA1-0DA3
     ↳ DMULT
    As always, we first start by checking to see if we are operating with any ZEROes. First, go check to see if the value in the ACCumulator is equal to zero
    0DA4
    RET ZC8
    If the double precision value in the ACCumulator is equal to zero then we already have our answer (i.e., 0) in the ACCumulator, so RETurn
    0DA5-0DA7
    Add the exponents and take care of processing the signs of the numbers via a GOSUB to MULDVA
    0DA8-0DAA
    Zero out the ACCumulator and put the move the double precision value in the ACCumulator to a temporary work area via a GOSUB to DMULDV
    0DAB
    LD (HL),C71
    Put the unpacked HIGH ORDER byte (pointed to by Register Pair HL in ARG) into Register C
    0DAC
    INC DE13
    Increment Register Pair DE so that it points to the LSB of the double precision value in ARG
    0DAD-0DAE
    LD B,07H06 07
    Load Register B with the number of bytes to be figured
    0DAF
     ↳ DMULT2
    LD A,(DE)1A
    Top of a big loop. Fetch a byte of ARG (at the location pointed to by DE) to multiply by into Register A
    0DB0
    INC DE13
    Increment the value of the memory pointer in Register Pair DE to point to the next higher byte.
    0DB1
    OR AB7
    Check to see if the value in Register A is equal to zero
    0DB2
    PUSH DED5
    Save the value of the memory pointer to ARG (held in Register Pair DE) to the STACK
    0DB3-0DB4
    If Register A is zero, then we are multiplying by ZERO, so JUMP to DMULT5
    0DB5-0DB6
    LD C,08H0E 08
    Otherwise, we need to set up for another loop for bit rotation. First, load Register C with the numberof bits to be shifted
    0DB7
     ↳ DMULT3
    PUSH BCC5
    Top of a loop. Save the counters (held in Register Pair BC) to the STACK
    0DB8
    RRA1F
    Shift the multiplier value (held in Register A) one place to the right. RRA rotates the contents of Register A right one bit position, with Bit 0 going to the CARRY FLAG, and the CARRY FLAG going to Bit 7. RRA also can be used to divide a number in 2.
    0DB9
    LD B,A47
    Preserve the shifted multiplier byte into Register B
    0DBA-0DBC
    If the bit of the multiplier that got shifted into the CARRY FLAG was a 1, we need to add in the old ACCumulator value via a GOSUB to DADDAA which adds the value in ARG (a/k/a REG 2) to the total in the ACCumulator
    0DBD-0DBF
    Rotate the production right one bit via a GOSUB to DSHFRB which shifts the value of the total in the ACCumulator
    0DC0
    LD A,B78
    Restore the shifted multiplier byte (held in Register B) back into Register A
    0DC1
    POP BCC1
    Restore the counters from the STACK into Register Pair BC
    0DC2
    DEC C0D
    Decrement the number of bits to be shifted from the ARG (tracked in Register C)
    0DC3-0DC4
    If we have not hit 0 on the counter, LOOP back to 0DB7H to multiply by the next bit of the multiplier until all of the bits have been shifted
    0DC5
     ↳ DMULT4
    POP DED1
    If we are here, then we finished rotating that one byte. Top of a loop. First, get the pointer to ARG back from the STACK and put it in Register Pair DE
    0DC6
    DEC B05
    Decrement the number of bytes to be figured (tracked in Register B)
    0DC7-0DC8
    Loop back to 0DAFH to multiply by the next higher order byte in ARG until all of the bytes in ARG have been figured
    0DC9-0DCB
    If we are here then we are done (first, all the bits in each number were rotated, then that was done by all the bytes). Jump to 0CD8H to normalize and round the result.

    0DCCH – DOUBLE PRECISION MULTIPLICATION Support Routine – “DMULT5”

    This routine handles multiplying by zero.
    0DCC-0DCE
     ↳ DMULT5
    LD HL,4123HLD HL,FAC-121 23 41
    Load Register Pair HL with the address of the HIGH ORDER/MSB of the ACCumulator
    0DCF-0DD1
    Go shift the double precision total in the ACCumulator right one byte
    0DD2-0DD3
    Jump back into DMULT at the point where we finalize the number

    0DD4H-0DDBH – DOUBLE PRECISION CONSTANT STORAGE AREA – “DTEN” and “FTEN”

    0DD4-0DDB
     ↳ DTEN
    00 00 00 00 00 00 20 8400
    A double precision constant equal to 10 is stored here. Note: 0DD8 is also a reference point.
    0DD8-0DDB
     ↳ FTEN
    00 00 20 8400
    A double precision constant equal to 10.0 is stored here. Note: 0DD8 is also a reference point.

    0DDCH-0DE4H – DOUBLE PRECISION MATH ROUTINE – “DDIV10”

    Double precision divide routine. Divides the ACCumulator by 10. All registers are affected.
    0DDC-0DDE
     ↳ DDIV10
    LD DE,0DD4HLD DE,DTEN11 D4 0D
    Load Register Pair DE with the starting address of the double precision constant for 10
    0DDF-0DE1
    LD HL,4127HLD HL,ARGLO21 27 41
    Load Register Pair HL with the starting address of ARG (a/k/a REG 2).
    Note: 4127H-412EH holds ARG (a/k/a REG 2)
    0DE2-0DE4
    GOSUB to VMOVE to move the 10 into ARG and then fall through to the DDIV routine to divide by 10.

    0DE5H-0E38H – DOUBLE PRECISION DIVISION – “DDIV”

    Double-precision division (ACCumulator=ACC / ARG).

    Divides the double precision value in the ACCumulator by the value in ARG (a/k/a REG 2). The quotient is left in the ACCumulator. All registers are affected
    To use a ROM call to divide two double precision numbers, store the dividend in 411DH-4124H, and the divisor in 4127H-412EH and then CALL 0DE5H. The result (in double precision format) is in 411DH-4124H and then pproximately 42 milliseconds. Overflow or /0 will error out and return to Level II.
    0DE5-0DE7
     ↳ DDIV
    LD A,(412EH)LD A,(ARG)3A 2E 41
    As always, start by checking to see if we are dealing with a ZERO. First, load Register A with the value of the exponent for the double precision value in ARG (a/k/a REG 2)
    0DE8
    OR AB7
    Check to see if the double precision value in ARG (a/k/a REG 2) is equal to zero
    0DE9-0DEB
    Display a ?/0 ERROR message if the double precision value in ARG (a/k/a REG 2) is equal to zero
    0DEC-0DEE
    Subtract the exponents and check the signs via a GOSUB to MULDVS
    0DEF
    0DF0
    INC (HL)
    INC (HL)34
    Increment the value of the exponent in the ACCumulator TWICE to correct the scaling
    0DF1-0DF3
    Zero the ACCumulator and move the double precision value from ACCumulator into ARG (a/k/a REG 2)
    0DF4-0DF6
    LD HL,4151HLD HL,FBUFFR+3421 51 41
    Load Register Pair HL with the address of the extra HIGH ORDER byte we will use in ARG
    0DF7
    LD (HL),C71
    Zero the that byte
    0DF8
    LD B,C41
    Zero Register B, which will be the flag that tells us when start dividing
    0DF9-0DFB
     ↳ DDIV1
    LD DE,414AHLD DE,FBUFFR+2711 4A 41
    Top of a large loop. First, get the pointer to the end of the BUFFR into Register Pair DE
    0DFC-0DFE
    LD HL,4127HLD HL,ARGLO21 27 41
    Load Register Pair HL with the address of the END of the double precision value in ARG (a/k/a REG 2)
    0DFF-0E01
    Go subtract the those two double precision values
    0E02
    LD A,(DE)1A
    Prepare to subtract from the extra HIGH ORDER byte by first loading Register A with the value at the location of the memory pointer in Register Pair DE
    0E03
    SBC A,C99
    Subtract the value in Register C from the value in Register A
    0E04
    CCF3F
    If the subtraction was good then the CARRY FLAG will be set, so complement the value of the CARRY FLAG so that NC FLAG will mean good
    0E05-0E06
    If the subtraction was bad, meaning that the double precision value in ARG (a/k/a REG 2) is greater than the double precision value in FBUFFER, then JUMP to DDIV2
    0E07-0E09
    LD DE,414AHLD DE,FBUFFR+2711 4A 41
    Put the pointer to the end of the BUFFR into Register Pair DE
    0E0A-0E0C
    LD HL,4127HLD HL,ARGLO21 27 41
    Load Register Pair HL with the address of the END of the double precision value in ARG (a/k/a REG 2)
    0E0D-0E0F
    Go add the double precision value in ARG (a/k/a REG 2) to the double precision value in FBUFFR
    0E10
    XOR AAF
    Clear the CARRY FLAG for the Z-80 trick in the next instruction.
    0E11-0E13
    JP C,0412HDA 12 04
    Z-80 TRICK. Since the CARRY was just cleared, this cannot ever execute and it won’t even see the next instruction. It is designed to allow for passing through but not running the next 2 instructions.
    0E12
     ↳ DDIV2
    LD (DE),A12
    If this line is executed (i.e., JUMPed to, but not passed down to), put the new highest order byte into Register A
    0E13
    INC B04
    If this line is executed (i.e., JUMPed to, but not passed down to), increment the flag in Register B to show that we can do the division
    0E14-0E16
    LD A,(4123H)LD A,(FAC-1)3A 23 41
    Prepare the check to see if we are finished dividing. First, getch the byte at FAC-1
    0E17
    0E18
    INC A
    DEC A3C
    INCrement and DECrement Register A so that the SIGN FLAG will be set without chaning the status of the CARRY FLAG
    0E19
    RRA1F
    In preparation for DROUNB, put the CARRY FLAG into the MSB via a RRA rotation. RRA rotates the contents of Register A right one bit position, with Bit 0 going to the CARRY FLAG, and the CARRY FLAG going to Bit 7. RRA also can be used to divide a number in 2.
    0E1A-0E1C
    If the M FLAG is set, then we are done and have 57 bits of accuracy, so JUMP to DROUNB to finish up.
    0E1D
    RLA17
    Restore the CARRY BIT to where it belongs
    0E1E-0E20
    LD HL,411DHLD HL,DFACLO21 1D 41
    Load Register Pair HL with the starting address of the LOW ORDER/LSB byte of the double precision result in the ACCumulator.
    Note: 411DH-4124H holds ACCumulator
    0E21-0E22
    LD C,07H0E 07
    Load Register C with the number of bytes to be shifted
    0E23-0E25
    GOSUB to DSHFTL to shit in the next bit in the quotient (held in the ACCumulator)
    0E26-0E28
    LD HL,414AHLD HL,FBUFFR+2721 4A 41
    Load Register Pair HL with the pointo the LOW ORDER byte in FBUFFR
    0E29R-0E2BH
    Go shift the double precision value dividend (in FBUFFR) one to the left
    0E2C
    LD A,B78
    Test to see if this was the first time by first loading Register A with the value of the counter in Register B. Note that B will get changed on the first or second subtraction
    0E2D
    OR AB7
    Set the flags based on Register B
    0E2E-0E2F
    If Register B is not ZERO, then we have more to go so LOOP back up to DDIV1
    0E30-0E32
    LD HL,4124HLD HL,FAC21 24 41
    If we are here, then this was the first iteration, so we need to subtract one from the exponent to correct scaling. To do that, first load Register Pair HL with the address of the exponent for the double precision result in the ACCumulator
    0E33
    DEC (HL)35
    Decrement the value of the exponent for the double precision result in the ACCumulator at the location of the memory pointer in Register Pair HL. If (HL) is reduced to zero then we have a problem!
    0E34-0E35
    Continue dividing so long as we don’t have an overflow by LOOPING back to DDIV
    0E36-0E38
    Display an ?OV ERROR if the exponent for the result in the ACCumulator is too small

    0E39H-0E4CH – DOUBLE PRECISION MATH ROUTINE – “DMULDV”

    This routine will transfer the double prevision number held in the ACCumulator to FBUFFR for the DMULT and DDIV routines. All registers are affected.
    0E39
     ↳ DMULDV
    LD A,C79
    We need to put the unpacked HIGH ORDER back into ARG, so first load Register A with the HIGH ORDER of the double precision value in ARG (a/k/a REG 2) in Register C
    0E3A-0E3C
    LD (412DH),ALD (ARG-1),A32 2D 41
    Save the MSB of the double precision value in ARG (a/k/a REG 2) in Register A
    0E3D
    DEC HL2B
    Decrement the value of the memory pointer in Register Pair HL to now point to the HIGH ORDER of the ACCumulator
    0E3E-0E40
    LD DE,4150HLD DE,FMLTT211 50 41
    Load Register Pair DE with the end of FBUFFR
    0E41-0E43
    LD BC,0700H01 00 07
    Load Register B with the number of bytes to be moved (which is 7) and put a zero into Register C
    0E44
     ↳ DMLDV1
    LD A,(HL)7E
    Top of a loop. Fetch a byte from the ACCumulator (tracked by Register Pair HL) into Register A
    0E45
    LD (DE),A12
    Save that byte into FBUFFR (tracked by Register Pair DE)
    0E46
    LD (HL),C71
    Zero out that location in the ACCumulator
    0E47
    DEC DE1B
    Decrement the value of the memory pointer to FBUFFR (tracked by Register Pair DE)
    0E48
    DEC HL2B
    Decrement the value of the memory pointer to the ACCumulator (tracked by Register Pair HL)
    0E49
    DEC B05
    Decrement the value of the byte counter in Register B to see if we are done
    0E4A-0E4B
    Loop until the double precision value has been moved from the ACCumulator to FBUFFR
    0E4C
    RETC9
    RETurn to CALLer

    0E4DH-0E64H – LEVEL II BASIC MATH ROUTINE – “DMUL10”

    This routine multiplies the current double-precision value by 10 by adding it to itself. First the current value is moved to a saved location, and then DP add routine adds the current value to that saved value. All registers are affected
    0E4D-0E4F
     ↳ DMUL10
    Go move the value in the ACCumulator to ARG (a/k/a REG 2)
    0E50
    EX DE,HLEB
    Since VMOVAF exits with DE pointing to ACCumulator + 1 we need to swap those so that HL points to the ACCumulator
    0E51
    DEC HL2B
    As always, the first thing we need to do is see if we are deadling with a 0. First, decrement the value of the memory pointer in Register Pair HL to point to the exponent of the number in the ACCumulator
    0E52
    LD A,(HL)7E
    Fetch the exponent from the ACCumulator
    0E53
    OR AB7
    Check to see if the value in the ACCumulator is equal to zero
    0E54
    RET ZC8
    Return if the value in the ACCumulator is equal to zero
    0E55-0E56
    ADD 02HC6 02
    Add two to the exponent which is the same as multiplying the ACCumulator by 4
    0E57-0E59
    Display an ?OV ERROR if the adjusted exponent in Register A is too large
    0E5A
    LD (HL),A77
    Save the adjusted exponent back into the ACCumulator at the location of the memory pointer in Register Pair HL
    0E5B
    PUSH HLE5
    Save pointer to the ACCumulator onto the STACK
    0E5C-0E5E
    Add in that number one more time (so now it is time 5) by GOSUBing to the DOUBLE PRECISION ADD function (whcih adds the double precision value in ARG (a/k/a REG 2) to the value in the ACCumulator. Result is left in the ACCumulator)
    0E5F
    POP HLE1
    Get the memory pointer to the ACCumulator from the STACK and put it in Register Pair HL
    0E60
    INC (HL)34
    Add 1 to the exponent, thus doubling the number.
    0E61
    RET NZC0
    Return if overflow didn’t occur
    0E62-0E64
    Display an ?OV ERROR if the exponent in the ACCumulator at the location of the memory pointer in Register Pair HL is too large

    0E65H-0F88H – ASCII to Double Precision Converter – “FINDBL”

    This routine converts an ASCII string (pointed to by HL) to a double-precision value and stores it in the ACCumulator. The NTF is fixed accordingly. The string must be terminated with a , or zero byte. Note that the ARG (a/k/a REG 2) is destroyed in the process and that HL will point to the delimiter at the end of the string. The string formats must follow the same rules as in BASIC. All registers are affected

    On entry (HL) must point to the first character in the string buffer, with the first character being in A. On exit, the the double precision number is left in the ACCumulator.

    In processing, the digits are packed into the ACCumulator as an integer, with tracking for the decimal point. C=80H if we have not seen a decimal point, and 00H if we have. Register B holds the number of digits after the decimal point.

    At the end, Register B and the exponent (held in Register E) are used to determine how many times we multiply or divide the number by 10 to get the correct number.
    0E65-0E67
     ↳ FINDBL
    GOSUB to ZERO to zero the ACCumulator
    0E68-0E6A
    GOSUB to VALDBL to force the VALTYP to to double precision
    0E6B
    OR 0AFH
    F6 AFF6 AF
    Part of a Z-80 Trick. If passing through, the next instruction of XOR A will not execute. This is done so that if passing through, the XOR A doesn’t cause us to CALL MAKINT. If the next instruction is JUMPed to, and executes, MAKINT will be CALLed

    0E6CH – ASCII to Binary Converter – “FIN”

    A call to 0E6CH converts the ASCII string pointed to by HL to binary. If the value is less than 2** 16 and does not contain a decimal point or an E or D descriptor (exponent), the string will be converted to its integer equivalent. If the string contains a decimal point or an E, or D descriptor or if it exceeds 2** 16 it will be converted to single or double precision. The binary value will be left in the ACCumulator and the mode flag will be to the proper value.

    Evaluate a numeric string that begins at the address pointed to by the HL Register Pair, store it in ACCUM and set the NTF. This routine stops as soon as it encounters a character that is not part of the number (it will return a value of zero if no valid numeric characters are found). It will accept signed values in Integer, Real or Scientific Notation. Number returned will be in integer format if possible, else single precision unless the string has over seven digits (not including exponent), in which case number will be returned as double precision.

    This routine will convert the ASCII string pointed to by register pair HL to binary. The result will be returned in the ACCumulator, and the number type flag will be updated accordingly. The routine will convert the ASCII string to the least amount of precision required.

    Note: If you wanted to do this conversion via a ROM call, first have the characters assembled in consecutive memory locations, with either a comma or a 00H at the end. Load HL with the address of the first character. Call 0E6CH. If the output can be an integer, it will be in 4121H-4122H (with 40AFH being a 2). If the output has to be single precision, it will be in 4121H-4124H (with 40AFH being a 4). If the output has to be double precision, it will be in 411DH-4124H (with 40AFH being an 8).
    0E6C
     ↳ FIN
    XOR AAF
    Zero Register A for the purpose of triggering the GOSUB to MAKINT at 0E73H
    0E6D
     ↳ FINCHR
    EX DE,HLEB
    Load Register Pair DE with the pointer to the current BASIC line being interpreted
    0E6E-0E70
    LD BC,00FFH01 FF 00
    Load Register Pair BC with a zero and a negative one. Register B will track the decimal point location and C will be a flag.
    0E71
    LD H,B60
    Load Register H with zero
    0E72
    LD L,B68
    Load Register L with zero. Now HL is zero.
    0E73-0E75
    A CALL to MAKINT will clear the ACCumulator and force VALTYP into Integer
    0E76
    EX DE,HLEB
    Restore the pointer to the BASIC line being interpreted into HL and zero out Register Pair DE
    0E77
    LD A,(HL)7E
    Retrieve the first character at at the location of the current input buffer pointer in Register Pair HL
    0E78-0E79
    CP 2DHCP “-“FE 2D
    Check to see if the character at the current position in the string being interpreted is a
    0E7A
    PUSH AFF5
    Save the sign in Register Pair AF on the STACK
    0E7B-0E7D
    If the character at the current position in the string being interpreted is a then JUMP to FINC to ignore it
    0E7E-0E7F
    CP 2BHCP “+”FE 2B
    Check to see if the character at the current position in the string being interpreted is a +
    0E80-0E81
    If the character at the current position in the string being interpreted is a + then JUMP to FINC to process it
    0E82
    DEC HL2B
    Decrement the value of the current input buffer pointer in Register Pair HL to point to the first character in the string being interpreted
    0E83
     ↳ FINC
    Since we need to bump the current input buffer pointer in Register Pair HL until it points to the next character, call the EXAMINE NEXT SYMBOL routine at RST 10H (which Loads the next character from the string pointed to by the HL Register set into the A-Register And clears the CARRY flag if it is alphabetic, or sets it if is alphanumeric. Blanks and control codes 09 and OB are ignored causing the following character to be loaded and tested. The HL Register will be incremented before loading any character therfore on the first call the HL Register should contain the string address minus one. The string must be terminated by a byte of zeros)
    0E84-0E86
    If the character at the location of the current input buffer pointer in Register A is numeric then JUMP to FINDIG
    0E87-0E88
    CP 2EHCP “.”FE 2E
    Check to see if the character at the location of the current input buffer pointer in Register A is a .
    0E89-0E8B
    Jump if the character at the location of the current input buffer pointer in Register A is a .
    0E8C-0E8D
    CP 45HCP “E”FE 45
    Check to see if the character at the location of the current input buffer pointer in Register A is an E (which is a single precision exponent)
    0E8E-0E8F
    Jump if the character at the location of the current input buffer pointer in Register A is an E
    0E90-0E91
    CP 25HCP “%”FE 25
    Check to see if the character at the location of the current input buffer pointer in Register A is a %
    0E92-0E94
    Jump to FININT (since this HAS to be an integer) if the character at the location of the current input buffer pointer in Register A is a %
    0E95-0E96
    CP 23HCP “#”FE 23
    Check to see if the character at the location of the current input buffer pointer in Register A is a #
    0E97-0E99
    Jump to FINDBF (since this needs to be forced into double precision) if the character at the location of the current input buffer pointer in Register A is a #
    0E9A-0E9B
    CP 21HCP “!”FE 21
    Check to see if the character at the location of the current input buffer pointer in Register A is a !
    0E9C-0E9E
    Jump to FINSNF (since this needs to be forced into single precision) if the character at the location of the current input buffer pointer in Register A is a !
    0E9F-0EA0
    CP 44HCP “D”FE 44
    Check to see if the character at the location of the current input buffer pointer in Register A is a D
    0EA1-0EA2
    If the character ISN’T a D, then we must be finished with the number, so JUMP to FINE
    0EA3
     ↳ FINEX1
    OR AB7
    Set the flags according to the value of the character at the location of the current input buffer pointer in Register A
    0EA4-0EA6
     ↳ FINEX
    Convert the current value in the ACCumulator to either single precision or double precision
    0EA7
    PUSH HLE5
    Save the current input buffer pointer to the string being processed (tracked in Register Pair HL) to the STACK
    0EA8-0EAA
    LD HL,0EBDHLD HL,FINEC21 BD 0E
    Load Register Pair HL with the return address to the FINEC routine
    0EAB
    EX (SP),HLE3
    Swap (SP) and HL, so that the return address goes into Register Pair HL and the current input buffer pointer to the text string goes to the top of the STACK
    0EAC
    Next we need the first character of the exponent. Since we need to bump the current input buffer pointer in Register Pair HL until it points to the next character, call the EXAMINE NEXT SYMBOL routine at RST 10H.

    The RST 10H routine parses the characters starting at HL+1 for the first non-SPACE,non-09H,non-0BH character it finds. On exit, Register A will hold that character, and the C FLAG is set if its alphabetic, and NC FLAG if its alphanumeric. All strings must have a 00H at the end.
    0EAD
    DEC D15
    Decrement the value in Register D to turn the sign of the exponent to NEGATIVE
    0EAE-0EAF
    CP 0CEHCP “-“FE CE
    Check to see if the character at the location of the current input buffer pointer in Register A is a token
    0EB0
    RET ZC8
    If the character at the location of the current input buffer pointer in Register A is a minus sign token then RET
    0EB1-0EB2
    CP 2DHCP “-“FE 2D
    Check to see if the character at the location of the current input buffer pointer in Register A is a sign (not token)
    0EB3
    RET ZC8
    If the character at the location of the current input buffer pointer in Register A is a minus sign then RET
    0EB4
    INC D14
    If we are here then the exponent is still positive, so increment the value in Register D to re-set that flag, as we are now going to process the notations for positive
    0EB5-0EB6
    CP 0CDHCP “+”FE CD
    Check to see if the character at the location of the current input buffer pointer in Register A is a + token (0CDH)
    0EB7
    RET ZC8
    Return if the character at the location of the current input buffer pointer in Register A is a + token (CDH)
    0EB8-0EB9
    CP 2BHCP “+”FE 2B
    Check to see if the character at the location of the current input buffer pointer in Register A is a +
    0EBB
    RET Z2B
    Return if the character at the location of the current input buffer pointer in Register A is a +
    0EBA
    DEC HLC8
    If we are still here then the first character wasn’t a sign, so we are going to need to check it for a digit. Since CHARGET INC’s HL, we need to DEC HL
    0EBC
    POP AFF1
    Discard the FINCE return address as we no longer need it … we are now passing right to it!
    0EBC
     ↳ FINEC
    Since we need to bump the current input buffer pointer in Register Pair HL until it points to the next character, call the EXAMINE NEXT SYMBOL routine at RST 10H.

    The RST 10H routine parses the characters starting at HL+1 for the first non-SPACE,non-09H,non-0BH character it finds. On exit, Register A will hold that character, and the C FLAG is set if its alphabetic, and NC FLAG if its alphanumeric. All strings must have a 00H at the end.
    0EBE-0EC0
    If the character at the location of the input buffer pointer in Register A is numeric, then JUMP to FINEDG to pack the digit into the exponent
    0EC1
    INC D14
    If we didn’t JUMP away to FINEDG, then we didn’t get a digit, so we need to adjust the sign of the exponent again … to positive by INCrementing the value in Register D
    0EC2-0EC3
    So long as the exponent isn’t a ZERO, JUMP to FINE to skip over the handling of a negative exponent
    0EC4
    XOR AAF
    If we are here, then the exponent is negative. Zero Register A
    0EC5
    SUB E93
    NEGate the value of the exponent in Register E (i.e., A = 0 – E)
    0EC6
    LD E,A5F
    Load Register E with the negated version of itself
    0EC7
     ↳ FINE
    PUSH HLE5
    Save the current input buffer pointer to the string being converted (tracked in Register Pair HL) to the STACK
    0EC8
    LD A,E7B
    Load Register A with the value of the exponent in Register E
    0EC9
    SUB B90
    Subtract the value in Register B from the exponent in Register A to get the number of times we have to multiply or divide by 10
    This “FINE2” routine will multiply or divide by 10 the correct number of times. If A=0 the number is an integer.
    0ECA-0ECC
     ↳ FINE2
    If the P FLAG is set, then we need to multiply. So multiply the current value by ten
    0ECD-0ECF
    If the M FLAG is set, then we need to divide. So multiply the current value by ten
    0ED0-0ED1
    Whichever of those two routines applied, if they returned a NZ then we need to do it again … so Loop until the value is adjusted correctly
    Next we need to put the correct sign on the number.
    0ED2
    POP HLE1
    Get the value of the current input buffer pointer of the string being parsed from the STACK and put it in Register Pair HL
    0ED3
    POP AFF1
    Get the sign value from the STACK and put it in Register A
    0ED4
    PUSH HLE5
    Save the value of the current input buffer pointer of the string being parsed in Register Pair HL on the STACK
    0ED5-0ED7
    If the Z FLAG is set, then convert the current value to negative
    0ED8
    POP HLE1
    Get the value of the current input buffer pointer of the string being parsed from the STACK and put it in Register Pair HL
    Next we want -32768 to be an integer (it would be single precision at this point)
    0ED9
    We need to check the value of the current number type flag, so we call the TEST DATA MODE routine at RST 20H which determines the type of the current value in the ACCumulator and returns a combination of STATUS flags and unique numeric values in the register A according to the data mode flag (40AFH). The results are returned as follows:
    IntegerNZ/C/M/E and A is -1
    StringZ/C/P/E and A is 0
    Single PrecisionNZ/C/P/O and A is 1
    Double PrecisionNZ/NC/P/E and A is 5.
    0EDA
    RET PEE8
    If that test shows we have anything other than a SINGLE PRECISION number, then we do no thave -32678, so RETurn
    0EDB
    PUSH HLE5
    If we are here, then we have a single preciosin number. Save the value of the current input buffer pointer of the string being parsed in Register Pair HL to the STACK
    0EDC-0EDE
    LD HL,0890HLD HL,POPHRT21 90 08
    Load Register Pair HL with the return address of the POPHRT routine because CONIS2 does funny things to the stack.
    0EDF
    PUSH HLE5
    Save the value of the return address in Register Pair HL on the STACK
    0EE0-0EE2
    Check to see if we have -32768 via a GOSUB to CONIS2 which will convert the current value in the ACCumulator to an integer if possible
    0EE3
    RETC9
    RETurn to CALLer. If we didn’t have -32768 then this will RETurn to POPHRT

    0EE4 – Math Routine – “FINDP”

    This routine checks to see if we have seen TWO decimal points and to set the decimal point flag. We jumped here when we found a single decimal point.
    0EE4
     ↳ FINDP
    We need to check the value of the current number type flag, so we call the TEST DATA MODE routine at RST 20H which determines the type of the current value in the ACCumulator and returns a combination of STATUS flags and unique numeric values in the register A according to the data mode flag (40AFH). The results are returned as follows:
    Variable TypeFlagsRegister A
    IntegerNZ/C/M/E-1
    StringZ/C/P/E0
    Single PrecisionNZ/C/P/O1
    Double PrecisionNZ/NC/P/E5
    0EE5
    INC C0C
    Increment the value in Register C to adjust the flag
    0EE6-0EE7
    If the INC C is NOT ZERO then we have 2 decimal points, so we are DONE.
    0EE8-0EEA
    If we are still here, then we have 1 decimal point, so convert the ACCumulator to single prevision via a GOSUB to 0EFBH to convert the current value in the ACCumulator to single precision
    0EEB-0EED
    Jump to 0E83H to continue looking for digits

    0EEE – Math Routine – “FININT”

    0EEE
     ↳ FININT
    We need to check the value of the current number type flag, so we call the TEST DATA MODE routine at RST 20H which determines the type of the current value in the ACCumulator and returns a combination of STATUS flags and unique numeric values in the register A according to the data mode flag (40AFH). The results are returned as follows:
    Variable TypeFlagsRegister A
    IntegerNZ/C/M/E-1
    StringZ/C/P/E0
    Single PrecisionNZ/C/P/O1
    Double PrecisionNZ/NC/P/E5
    0EEF-0EF1
    If that test shows anything but an INTEGER, jump to the Level II BASIC error routine and display a ?SN ERROR message
    0EF2
     ↳ INFINE
    INC HL23
    Top of a loop. If we are here, then we have something other than a single precision number. Next we move past the % character at the input buffer pointer to the sting being processed (tracked in Register Pair HL). We know this is the last character (a trailing %).
    0EF3-0EF4
    We are now done, so Jump to 0EC7H to finish.
    0EF5
     ↳ FINDBF
    OR AB7
    If we are here then we need to force double precision, so set the NZ FLAG
    0EF6-0EF8
     ↳ FINSNF
    Force a type conversion via a GOSUB to FINFRC to convert the current value in the ACCumulator to either single precision or double precision, based on the concents of Register A (Z=Force to Single or NZ=Force to Double)
    0EF9-0EFA
    Bump the pointer in HL and go to FINE via a JUMP to INFINE

    0EFB – Math Routine – “FINFRC”

    This routine will force the ACCumulator to be either single precision or double precision based on the Z FLAG. Z FLAG = Force to single precision; NZ FLAG = Force to double precision.
    0EFB
     ↳ FINFRC
    PUSH HLE5
    Save the value of the current input buffer pointer of the string being parsed in Register Pair HL on the STACK
    0EFC
    PUSH DED5
    Save the exponent (held in Register Pair DE) to the STACK
    0EFD
    PUSH BCC5
    Save the decimal point information (held in Register Pair BC) to the STACK
    0EFE
    PUSH AFF5
    Save the sp/dp value flag for the conversion (held in Register A) to the STACK
    0EFF-0F01
    If the Z FLAG is set, call the CONVERT TO SINGLE PRECISION routine at 0AB1H (which converts the contents of ACCumulator from integer or double precision into single precision)
    0F02
    POP AFF1
    Restore the sp/dp value flag for the conversion from the STACK and put it in Register Pair AF
    0F03-0F05
    If the NZ FLAG is set, Call the CONVERT TO DOUBLE PRECISION routine at 0ADBH (where the contents of ACCumulator are converted from integer or single precision to double precision)
    0F06
    POP BCC1
    Restore the decimal point information from the STACK and put it in Register Pair BC
    0F07
    POP DED1
    Restore the exponent from the STACK and put it in Register Pair DE
    0F08
    POP HLE1
    Restore the value of the current input buffer pointer of the string being parsed from the STACK and put it in Register Pair HL
    0F09
    RETC9
    RETurn to CALLer

    0EE4 – Math Routine – “FINMUL” and “FINMLT”

    This subroutine multiplies a number by 10 once. The original ROM source notes that the reason this is a subroutine is that it can also double as a check to see if A is ZERO, thus saving bytes. All registers are affected.
    0F0A
     ↳ FINMUL
    RET ZC8
    If the exponent is ZERO then exit right back out
    0F0B
     ↳ FINMLT
    PUSH AFF5
    Save the exponent (held in Register Pair AF) to the STACK. FOUT enters the routine here.
    0F0C
    We need to check the value of the current number type flag, so we call the TEST DATA MODE routine at RST 20H which determines the type of the current value in the ACCumulator and returns a combination of STATUS flags and unique numeric values in the register A according to the data mode flag (40AFH). The results are returned as follows:
    Variable TypeFlagsRegister A
    IntegerNZ/C/M/E-1
    StringZ/C/P/E0
    Single PrecisionNZ/C/P/O1
    Double PrecisionNZ/NC/P/E5
    0F0D
    PUSH AFF5
    Save exponent and the value type from AF to the STACK
    0F0E-0F10
    If that test shows SINGLE PRECISION, go to 093EH to multiply the current value in the ACCumulator by “10.0”
    0F11
    POP AFF1
    Get the exponent and the value type from the STACK back into AF
    0F12-0F14
    If that test shows DOUBLE PRECISION, go to 0E4DH to multiply the current value in the ACCumulator by “10D0”
    0F15
    POP AFF1
    Get the exponent and the value type from the STACK and put it in Register Pair AF
    0F16
     ↳ DCRART
    DEC A3D
    Decrement the exponent (held in Register A) since we have now multiplied by 1 since (x^10 = 10x^9).
    0F17
    RETC9
    RETurn to CALLer

    0F18 – Math Routine – “FINDIV”

    This subroutine divides a number by 10 once. FIN and FOUT use this routine. Registers A, B, and C are affected.
    0F18
     ↳ FINDIV
    PUSH DED5
    Preserve DE to the STACK for POPing at the end
    0F19
    PUSH HLE5
    Preserve HL to the STACK for POPing at the end
    0F1A
    PUSH AFF5
    Since we need to divide we need to preserve the exponent, so save the value in Register A on the STACK
    0F1B
    We need to check the value of the current number type flag, so we call the TEST DATA MODE routine at RST 20H which determines the type of the current value in the ACCumulator and returns a combination of STATUS flags and unique numeric values in the register A according to the data mode flag (40AFH). The results are returned as follows:
    Variable TypeFlagsRegister A
    IntegerNZ/C/M/E-1
    StringZ/C/P/E0
    Single PrecisionNZ/C/P/O1
    Double PrecisionNZ/NC/P/E5
    0F1C
    PUSH AFF5
    Save the value of the FLAGS from the RST 20H call to the STACK
    0F1D-0F1F
    If that test shows SINGLE PRECISION, go to 0897H to divide the current value in the ACCumulator by “10.0”
    0F20
    POP AFF1
    Get the value from the STACK and put it in Register Pair AF
    0F21-0F23
    If that test shows DOUBLE PRECISION, go to 0DDCH to divide the current value in the ACCumulator by “10D0”
    0F24
    POP AFF1
    Restore the flags from the STACK and put it in Register Pair F
    0F25
    POP HLE1
    Get the value from the STACK and put it in Register Pair HL
    0F26
    POP DED1
    Get the value from the STACK and put it in Register Pair DE
    0F27
    INC A3C
    Increment the exponent (stored in Register A) since 10x^9 = x^10
    0F28
    RETC9
    RETurn to CALLer

    0F29 – Math Routine – “FINDIG”

    This routine will pack the next digit of the number into the ACCumulator. To do this, the ACCumulator is multipled by ten to shift everything over and make room for the digit, and then the digit is added in.
    0F29
     ↳ FINDIG
    PUSH DED5
    Save the exponent (held in Register Pair DE) on the STACK
    0F2A
    LD A,B78
    We need to check where the decimal point is, so load Register A with the value in Register B
    0F2B
    ADC A,C89
    Increement the decimal place count if we are past the decimal point by adding the value in Register C to the value in Register A
    0F2C
    LD B,A47
    Save the revised decimal point location (tracked in Register B)
    0F2D
    PUSH BCC5
    Save the decimal point information (tracked in Register Pair BC) on the STACK
    0F2E
    PUSH HLE5
    Save the value of the current input buffer pointer of the string being parsed in Register Pair HL on the STACK
    0F2F
    LD A,(HL)7E
    Fetch the digit we want to pack at the location of the current input buffer pointer in Register Pair HL
    0F30-0F31
    SUB 30HSUB “0”D6 30
    Subtract 30H from the ASCII value in Register A so that it will be binary
    0F32
    PUSH AFF5
    Save the adjusted value in the digit (held in Register A) to the STACK
    0F33
    We need to check the value of the current number type flag, so we call the TEST DATA MODE routine at RST 20H which determines the type of the current value in the ACCumulator and returns a combination of STATUS flags and unique numeric values in the register A according to the data mode flag (40AFH). The results are returned as follows:
    Variable TypeFlagsRegister A
    IntegerNZ/C/M/E-1
    StringZ/C/P/E0
    Single PrecisionNZ/C/P/O1
    Double PrecisionNZ/NC/P/E5
    0F34-0FJ6
    If that test shows we have anything but an INTEGER, jump to FINDGV to handle the cases of a a single precision or double precision number
    If we are here, then we re packing the next digit of an integer.
    0F37-0FJ9
    LD HL,(4121H)LD HL,(FACLO)2A 21 41
    Now that we know we have an integer, put it into the ACCumulator at (HL)
    0F3A-0FJC
    LD DE,0CCDH11 CD 0C
    Load Register Pair DE with 3277 to see if we will overflow
    0F3D
    Now we need to check to see if the integer value in HL is greater than or equal to 0CCDH (in DE), so we call the COMPARE DE:HL routine, which numerically compares DE and HL. Will not work for signed integers (except positive ones). Uses the A-register only. The result of the comparison is returned in the status Register As: CARRY SET=HL<DE; NO CARRY=HL>DE; NZ=Unequal; Z=Equal)
    0F3E-0F3F
    If the NC FLAG is set then HL (the number we are working on) > DE (an overflow value), so the number is too big. JUMP to FING2
    0F40
    0F41
    LD D,H
    LD E,L54
    Let DE = HL
    0F42
    ADD HL,HL29
    Multiply the integer value in Register Pair HL by two
    0F43
    ADD HL,HL29
    Multiply the integer value in Register Pair HL by two. Register Pair HL now holds the original integer value times four
    0F44
    ADD HL,DE19
    Add the original integer value in Register Pair DE to the integer value in Register Pair HL. Register Pair HL now holds the original integer value times five
    0F45
    ADD HL,HL29
    Multiply the integer value in Register Pair HL by two. Register Pair HL now holds the origmal integer value times ten
    At this point, the number has shifted over to make room for the new digit in the ones place.
    0F46
    POP AFF1
    Get the binary value for the number we want to pack in from the STACK and put it in Register A
    0F47
    LD C,A4F
    Load Register C with the value of the character in Register A. Why C? The DAD routine needs it there and B is already zero.
    0F48
    ADD HL,BC09
    Add the value of the character in Register Pair BC to the newly shifted integer value in Register Pair HL
    0F49
    LD A,H7C
    We next need to test for an overflow, so load Register A with the MSB of the integer value in Register H
    0F4A
    OR AB7
    Set the flags based on the MSB
    0F4B-0F4D
    If the M FLAG is set, then we have overflowed, so JUMP to FINDG1
    0F4E-0F50
    LD (4121H),HLLD (FACLO),HL22 21 41
    If we are here, then we did not overflow so save the new integer back into the ACCumulator
    0F51
     ↳ FINDGE
    POP HLE1
    Restore the value of the current input buffer pointer of the string being parsed into Register Pair HL
    0F52
    POP BCC1
    Restore the the decimal point information (tracked in Register Pair BC) from the STACK
    0F53
    POP DED1
    Restore the exponent (held in Register Pair DE) from the STACK
    0F54-0F56
    Jump to 0E83H to process the next character

    0F57 – Math Routine – “FINDG1”

    This routine handles 32768 and 32769
    0F57
     ↳ FINDG1
    LD A,C79
    Load Register A with the binary value of the character in Register C
    0F58
    PUSH AFF5
    Save the value in Register A on the STACK

    0F59 – Math Routine – “FINDG2”

    Convert integer digits into single precision digits
    0F59-0F5B
     ↳ FINDG2
    Go convert the current value in the ACCumulator to single precision
    0F5C
    SCF37
    Set the Carry flag to avoid the next instruction jumping away

    0F5D – Math Routine – “FINDGV”

    Determine if we have a single precision or a double prevision number
    0F5D-0F5E
     ↳ FINDGV
    If the current value in the ACCumulator is double precision, then JUMP to FINGD to use the double precision routine to pack in the next digit
    These next 2 instruction set up BCDE to hold “1000000”
    0F5F-0F61
    LD BC,9474H01 74 94
    Load Register Pair BC with the exponent and the MSB of a single precision constant
    0F62-0F64
    LD DE,2400H11 00 24
    Load Register Pair DE with the NMSB and the LSB of a single precision constant. Register Pairs BC and DE now hold a single precision constant equal to 1E6
    0F65-0F67
    Call the SINGLE PRECISION COMPARISON routine at 0A0CH to algebraically compare the single precision value in BC/DE (which is 1000000) to the single precision value ACCumulator. The results are stored in A as follows:
    • A=0 if ACCumulator = BCDE
    • A=1 if ACCumulator>BCDE; and
    • A=FFH if ACCumulator<BCDE.
    0F68-0F6A
    If the single precision value in the ACCumulator is greater than or equal to 1000000 then we need to change from single precision to double precision, so JUMP to FINDG3 to covert the number to double precision.
    0F6B-0F6D
    Go multiply the single precision value in the ACCumulator by 10
    0F6E
    POP AFF1
    Get the binary value of the number we want to pack in from the STACK and put it in Register A
    0F6F-0F71
    Add the value in Register A to the single precision value in the ACCumulator
    0F72-0F33
    Jump to 0F51H to get the flags off of the stack and finish.

    0F74 – Math Routine – “FINDG3” and “FINDGD”

    The routine will convert a 7 digit single precision number into a double precision number
    0F74-0F76
     ↳ FINDG3
    Go convert the single precision value in the ACCumulator to double precision
    This routine will pack in a digit into a double precision number
    0F77-0F79
     ↳ FINDGD
    Go multiply the double precision value in the ACCumulator by ten
    0F7A-0F7C
    Go move the double precision value in the ACCumulator to ARG (a/k/a REG 2)
    0F7D
    POP AFF1
    Get the binary value for the number to pack in from the STACK and put it in Register A
    0F7E-0F80
    Go convert that binary value to single precision
    0F81-0F83
    Go convert that single precision value to double precision
    0F84-0F86
    Call the DOUBLE PRECISION ADD function (whcih adds the double precision value in ARG (a/k/a REG 2) to the value in the ACCumulator. Result is left in the ACCumulator)
    0F87-0F88
    Jump to 0F51H to get the flags off of the stack and finish.

    0F89H-0F93H – SINGLE PRECISION MATH ROUTINE – “FINLOG”

    This is a subroutine for FIN and for LOG
    0F89-0F8B
     ↳ FINLOG
    Call 09A4 which moves the SINGLE PRECISION value in the ACCumulator to the STACK (stored in LSB/MSB/Exponent order)
    0F8C-0F8E
    Go convert the value in Register A to a single precision floating number and return with the result in the ACCumulator
    0F8F
    POP BCC1
    Clear off the stack
    0F90
    POP DED1
    Clear off the stack
    0F91-0F93
    Jump to the SINGLE PRECISION ADD routine at 0716H (which adds the single precision value in (BC/DE) to the single precision value in the ACCumulator. The sum is left in the ACCumulator)

    0F94H-0FA6H – LEVEL II BASIC MATH ROUTINE – “FINEDG”

    Pack in a digit of the exponent. This is done by multiplying the old exponent by 10 and then adding in the desired digit. Note: This routine does NOT check for overflow.
    0F94
     ↳ FINEDG
    LD A,E7B
    Load Register A with the value of the exponent in Register E
    0F95-0F96
    CP 0AHFE 0A
    Test for overfly by checking to see if the value of the exponent in Register A is greater than or equal to 10. This is necessary because if it overflows the Register E will be corrupted
    0F97-0F98
    If the value of the exponent in Register A is greater than or equal to 10 then we already have two digits, so JUMP to FINEDO to keep processing
    0F99
    RLCA07
    Multiply the value in Register A by two
    0F9A
    RLCA07
    Multiply the value in Register A by two. Register A now holds the original value of the exponent times four
    0F9B
    ADD A,E83
    Add the original value of the exponent in Register E to the adjusted value of the exponent in Register A
    0F9C
    RLCA07
    Multiply the value in Register A by two. Register A now holds the original value of the exponent times ten
    0F9D
    ADD A,(HL)86
    Add the value of the number at the location of the input buffer pointer in Register Pair HL to the adjusted value in Register A
    0F9E-0F9F
    SUB 30HSUB “0”D6 30
    Convert the adjusted value in Register A to it’s binary equivalent (which is subtracting 0011 0000)
    0FA0
    LD E,A5F
    Save the adjusted exponent into Register E
    0FA1-0FA3
    JP M,321EHFA 1E 32
    Z-80 TRICK. If passing through, this sill never trigger, but neither will the next instruction!
    0FA2-0FA3
     ↳ FINEDO
    LD E,32H1E 32
    If JUMPed here, E will be reset (from Register A’s value) to 50 Decimal, which, as an exponent, will SAFELY cause an overflow or underflow. If passing through, this will not be seen
    0FA4-0FA6
    Jump to 0EBDH to continue the routine

    0FA7H-0FAEH – DISPLAY MESSAGE ROUTINE – “INPRT”

    This routine is to output a floating point number.
    0FA7
     ↳ INPRT
    PUSH HLE5
    Save the line number (held in Register Pair HL) to the STACK
    0FA8-0FAA
    LD HL,1924HLD HL,INTXT21 24 19
    Load Register Pair HL with the starting address of the ” IN ” +00H message (which is 1924H)
    0FAB-0FAD
    Call the WRITE MESSAGE routine at 28A7H..
    NOTE:
    • The routine at 28A7 displays the message pointed to by HL on current system output device (usually video).
    • The string to be displayed must be terminated by a byte of machine zeros or a carriage return code 0D.
    • If terminated with a carriage return, control is returned to the caller after taking the DOS exit at 41D0H (JP 5B99H).
    0FAE
    POP HLE1
    Get the value from the STACK and put it in Register Pair HL and then pass through to the LINPRT routine.

    0FAFH-0FBCH – CONVERT BINARY TO ASCII AND DISPLAY RESULT – “LINPRT”

    This routine converts the two byte number in the HL Register Pair (which is assumed to be an integer) to ASCII and displays it at the current cursor position on the video screen. The space for the sign at the beginning of a line is removed. All registers are affected.

    0FAF-0FB1
     ↳ LINPRT
    Go save the line number (held in the ACCumulator) as an integer into Register Pair HL
    0FB2
    XOR AAF
    Zero Register A to indicate that the output should be a free format
    0FB3-0FB5
    Go initialize the input buffer for the ASCII conversion. This will set up the sign.
    0FB6
    OR (HL)B6
    Turn off the Z FLAG.
    0FB7-0FB9
    Go convert the integer value in the ACCumulator to an ASCII string. Return with Register Pair HL pointing to the result
    0FBA-0FBC
    Go display the message pointed to by Register Pair HL

    0FBDH-1363H – BINARY TO ASCII CONVERSION ROUTINE – “FOUT”

    According to the original ROM source code:
    This routine will output the value held in the ACCumulator according to the format specifications held in Registers A, B, and C. The ACCumulator contents are lost and all registers are affected.

    The format codes are as follows:
    • Register A:
      • Bit 7:
        • 0 means free format output, i.e. the other bits of a must be zero, trailing zeros are suppressed, a number is printed in fixed or floating point notation according to its magnitude, the number is left justified in its field, and Registers B and C are ignored.
        • 1 means fixed format output, i.e. the other bits of a are checked for formatting information, the number is right justified in its field, trailing zeros are not suppressed. this is used for print using.
      • Bit 6:
        • 0 means means don’t print the number with commas.
        • 1 means group the digits in the integer part of the number into groups of three and separate the groups by commas.
      • Bit 5: 1 means fill the leading spaces in the field with asterisks (“*”)
      • Bit 4: 1 means output the number with a floating dollar sign (“$”)
      • Bit 3: 1 means print the sign of a positive number as a plus sign (“+”) instead of a space
      • Bit 2: 1 means print the sign of the number after the number
      • Bit 1: Unused
      • Bit 0:
        • 1 means print the number in floating point notation i.e. “e notation”. If this bit is on, the comma specification (bit 6) is ignored.
        • 0 means print the number in fixed point notation. Numbers > 1e16 cannot be printed in fixed point notation.
      • Register B: The number of places in the field to the left of the decimal point (B does not include the decimal point)
      • Register C: The number of places in the field to the right of the decimal point (C includes the decimal point)
      • Note 1: B and C do not include the 4 positions for the exponent. If bit 0 is on FOUT assumes b+c <= 24 (decimal)
      • Note 2: If the number is too big to fit in the field, a percent sign (“%”) is printed and the field is extended to hold the number.
    According to other sources:
    Conversion routine. Converts the value from ACCumulator to an ASCII string delimited with a zero byte. The number type can be any of Integer, single or double-precision. After execution HL will be pointing to the start of the string. ACCumulator and ARG (a/k/a REG 2) are destroyed by the process.

    To use a ROM call to convert a number to a string of digits, and to display the latter on the video screen starting at the current cursor position, store the number in 4121H-4122H (if it’s an integer), or in 4121H-4124H (if it’s single precision), or in 411DH-4124H (if it’s double precision). Then store the variable type (2, 4, or 8, respectively) in 40AFH. Call 0FBDH and then call the WRITE MESSAGE routine at 28A7H.
    • NOTE 1: The subroutine at 28A7H is a general program for displaying a string of characters and updating the cursor position. The string to be displayed must be terminated by a zero byte, and the HL Register Pair must contain the address of the first character of the string before 28A7H is called. (The routine at 0FBDH effects this setup automatically.)
    • NOTE 2: DISK SYSTEM CAUTION: The subroutine at 28A7H has two exits to DISK BASIC, with RAM transfer points at 41C1H and 41D0H. To use this routine safely, either be certain that DISK BASIC is in place or have your assembly language program fill locations 41C1H and 41D0H with RET’s (C9H), before calling the routine.
    0FBD
     ↳ FOUT
    XOR AAF
    Zero Register A so that the format is set for free output

    0FBEH-0FC0H – FLOATING to ASCII Conversion Routine – “PUFOUT”

    This routine converts a single or double precision number in the ACCumulator to its ASCII equivalent. The ASCII value is stored at the buffer pointed to by the HL Register Pair. As the value is converted from binary to ASCII, it is formatted as it would be if a PRINT USING statement had been invoked. The format modes that can be specified are selected by loading the following values into the A, B, and C registers as follows:
    • A=0 means do not edit; this is a binary to ASCII conversion
    • A=X means edit as follows: Bit 7=1 means edit the value, Bit 6=Print commas every third digit, Bit 5=Include leading asterisks, Bit 4=Print a leading $, Bit 3=Sign Follows Value, and Bit 1=Exponential Notation
    • B = The number of digits to the left of the decimal point.
    • C = The number of digits after the decimal point.
    Note: If you wanted to convert any integer/single/double into its character string, store the variable in 4121H-4122H for integer, 4121H-4124H for single, or in 411DH-4124H for double. Then load 40AFH with a 2, 4, or 8 depending on whether that variable was integer, single, or double. Then call 0FBDH. Upon return, the character string is stored in 4130H and on, ending with a 00H.
    0FBE-0FC0
     ↳ PUFOUT
    Save the formt specification in Register A and put a space for positive numbers into the buffer and loads HL with the starting address of the input buffer
    0FC1-0FC2
    AND 08HE6 08
    Turn off some bits so we can check the value of Register A to see if a plus sign is required to be included for positive numbers
    0FC3-0FC4
    If a plus sign is NOT required to be added to the ASCII output string, then Jump to 0FC7H
    0FC5-0FC6
    LD (HL),2BHLD (HL),”+”36 2B
    If we are here, then it is required, so put a + into the buffer pointed to by Register Pair HL
    0FC7
     ↳ FOUT1
    EX DE,HLEB
    Load Register Pair DE with the value of the buffer pointer (held in Register Pair HL)
    0FC8-0FCA
    Go determine the value of the sign for the current value in the ACCumulator
    0FCB
    EX DE,HLEB
    Restore the buffer pointer back to HL
    0FCC-0FCE
    If the P FLAG is set then we have a negative number, so we need to negate it by JUMPing to FOUT2
    0FCF-0FD0
    LD (HL),2DHLD (HL),”-“36 2D
    Save a minus sign (-) at the location of the buffer pointer in Register Pair HL
    0FD1
    PUSH BCC5
    Save the field length specifications held in B and C to the STACK
    0FD2
    PUSH HLE5
    Save the buffer pointer to the STACK
    0FD3-0FD5
    GOSUB to 097BH to convert the negative value in the ACCumulator to its positive equivalent
    0FD6
    POP HLE1
    Restore the buffer pointer from the STACK into HL
    0FD7
    POP BCC1
    Restore the field length specifications from the STACK into B and C
    0FD8
    OR HB4
    Turn off the Z FLAG. This relies on the fact that FBUFR is never on page 0
    0FD9
     ↳ FOUT2
    INC HL23
    Increment the buffer pointer in Register Pair HL to where the next character will be placed
    0FDA-0FDB
    LD (HL),30H36 30
    Save an ASCII zero (0) at the location of the input buffer pointer in Register Pair HL EITHER because a “0” will ultimately go there (if we are processing in free format) OR to to reserve a space fro a floating dollar sign (if we are processing in fixed format)
    0FDC-0FDE
    LD A,(40D8H)LD A,(TEMP3)3A D8 40
    Load Register A with the format specification (held in a temporary storage location)
    0FDF
    LD D,A57
    Preserve the format specification into Register D
    0FE0
    RLA17
    Move the “free format” or “fixed format” bit into the Carry flag
    0FE1-0FE3
    LD A,(40AFH)LD A,(VALTYP)3A AF 40
    Since VNEG may have changed VALTYP, re-fetch it (as -32768 is and integer but 32768 is single-precision).
    0FE4-0FE6
    The comment in the original source says to JUMP to FOUTFX because “the man wants fixed formatted output here to print numbers in free format”
    0FE7-0FE9
    If the Z FLAG is set, then JUMP to FOUTZR to finish it up
    0FEA-0FEB
    CP 04HFE 04
    Check to see if the current value in the ACCumulator is single or double precision
    0FEC-0FEE
    If the current value in the ACCumulator is single or double precision JUMP to FOUFRV
    0FEF-0FF1
    LD BC,0000H01 00 00
    If we are here (and didn’t jump away) then we are dealing with an INTEGER. First, set the decimal point counter and comma counter to ZERO
    0FF2-0FF4
    Call the INTEGER TO ASCII routine at 1232F to convert the integer in the ACCumulator to ASCII and stores the ASCII string in the buffer pointed to in HL. We then fall through to FOUTZS
    This routine will zero suppress the digits in FBUFFR and asterisk fill and zero suppress if necessary.
    0FF5-0FF7
     ↳ FOUTZS
    LD HL,4130HLD HL,FBUFFR+121 30 41
    Load Register Pair HL with the starting address of the buffer, which will hold the SIGN
    0FF8
    LD B,(HL)46
    Load Register B with the sign (i.e., the character at the location of the buffer pointer in Register Pair HL)
    0FF9-0FFA
    LD C,20HLD C,” “0E 20
    Load Register C with a SPACE
    0FFB-0FFD
    LD A,(40D8H)LD A,(TEMP3)3A D8 40
    Load Register A with format specifications
    0FFE
    LD E,A5F
    Put the format specifications into Register E
    0FFF-1000
    AND 20HAND 0010 0000E6 20
    MASK the format specifications (by AND against 0010 0000) to see an asterisk fill is required