0000H-0004H - Power Up Routine- "START"
0000-0002 Disables the interrupts, clears the A register, then jumps to initialisation routine at 674H
0000START
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
0008H - RST 08H - Syntax Check- "SYNTAX"
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. 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.
0008SYNTAX
(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 compares 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 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.
000BH-000CH - Disk Routine- "WHERE"
000BWHERE
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
0010H-0012H - RST 10H - Get a Character from the Buffer
This routine increments HL and tests the characters pointed to by the HL Register Pair. It will bypass any spaces and characters 9 and 10 (shifted left and down arrows respectively). Upon return from this routine HL will point to the next non-blank character.
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
0018H - RST 18H - Compare DE:HL- "RST18"
This routine 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.
0018RST18
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:
| Condition | Flag |
| If HL < DE | CARRY SET |
| If HL > DE | NO CARRY |
| If HL ≠ DE | NZ |
| If HL = DE | Z |
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.
001BPUT
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
0020H - RST 20H - Data Mode Check
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.
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).
| If the Variable Type is ... | and the Flags are set ... | ... then Register A will be set ... |
| Integer | NZ/C/M/E | -1 |
| String | Z/C/P/E | 0 |
| Single Precision | NZ/C/P/O | 1 |
| Double Precision | NZ/NC/P/E | 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
0028H - RST 28H - DOS Function Call
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. 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.
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 0049Hor you would write a routine that jumps back to the call if A is 0.
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
0030H - RST 30H - Load Debug
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.
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
0038H - RST 38H - Interrupt Entry Point
This 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.
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. A call to 003BH causes the character contained in the A-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.
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)
0046H-0048H - Driver Entry Routine - Part 2- "CIOJ"
0046-0048CIOJC3 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 002BHinstead.
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 more useful than 2BH. Character is returned in the A Register (AF and DE used).
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.
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
005B
18H
Shift Left Arrow (18H)
005D
19H
Shift Right Arrow (19H)
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.
0060PAUSE
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
0066H-0068H - NMI Vector
0066 is the location to which program control jumps when the RESET button is pressed (Non Maskable Interrupt address).
0066-0068NMI
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)
| 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 |
Not Ready | Protected | Head Loaded | Seek Error | CRC Error | Track 00 | Index | Busy |
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 preparation 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 point to ?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.
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-0097ERLOPS
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-00A2LOPRTS
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
00A8H - 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-00B7MEMGET
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-00C6MEMSIZ
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
00C7LOOPMM
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-00D8TYPMEM
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 ERRORif 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 8For 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
00E7USEDEF
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:
| Condition | Flag |
| If HL < DE | CARRY SET |
| If HL > DE | NO CARRY |
| If HL ≠ DE | NZ |
| If HL = DE | Z |
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-00F1STRSZD
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 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-012ENAVERR
LD E,2CHLD E,ERRNAV1E 2C
Load Register E with the ?L3 ERRORcode of 2CH
012F-0131
Go to the Level II BASIC error routine with 2CH loaded into Register E
0132H-0134H - LEVEL II BASIC POINTCOMMAND 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
0132GRPHCS
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 POINTcommand 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 SETCOMMAND- "SET"
0135-0136SET
LD A,80H3E 80
Load Register A with 80H (which is 128) which is SET
0138H-0139H - LEVEL II BASIC RESETCOMMAND ENTRY POINT- "RESET"
0138-0139RESET
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 SETand 1 for RESET.
013A
PUSH AFF5
Save the flag which indicates which command was requested (held in Register A) to the STACK
013B-013C
Since SET/RESET/POINTall 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
0152LOPMD3
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)
015FSHFTW
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
016DPWR2
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)
017CFND4
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
018BFINSTB
LD (DE),A12
Save the adjusted graphic character in Register A at the location of the video memory pointer in Register Pair DE
018C-018DFINPTB
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
018FSBIT
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
0192TBIT
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
019DH-01C8H - LEVEL II BASIC INKEY$ routine- "INKEY"
019DINKEY
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-01A7MRCHRI
Go scan the keyboard
01A8
OR AB7
Set the status flags
01A9-01AA
Jump to 01BCH if a key wasn't pressed
01ABBUFCIN
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-01BENULRT
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-01CACLS
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.
01D3RANDOM
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-01DBCTPULS
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-01E2CT1
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-01ECCT2
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-01F6CT3
Loop for delay
01F7
RETC9
RETurn to CALLer
01F8H-01FDH - CASSETTE ROUTINE (TURN OFF CASSETTE)- "CTOFF"
01F8CTOFF
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"
01FECTON
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
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,0and then CALL 0212H
0212-0214DEFDRV
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-021BCTCHG2
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-0223CTCHG
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-022EBCASIN
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.
0235CASIN
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
0241CTBIT
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
Loop (don't jump unless B is zero; with a decrement of B each time) for delay
024C-024E
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-0263TWOCSO
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.
0264CASOUT
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-027FBYT2
LD B,87H06 87
Load Register B with the delay count of 87H (Decimal: 135)
0280-0281BYT3
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-0286CWONWL
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-0295CSRDON
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
0296CSRDON+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-02ABENBLK
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-02B4SYSTEM
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 ERRORif 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)
02DACHKBYT
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-02ECGETDT2
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 Ccharacter
030F-0311
LD (3C3EH),A32 3E 3C
Display the Ccharacter 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
0314CADRIN
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"
031DGODO
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.
032AOUTDO
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 LDdoesn't set flags, in order to be able to test Register A using flags we need to execute an OR Afirst. 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.
- Backspace and erase previous character - 08H
- Carriage return and linefeed - 0DH
- Turn on cursor - 0EH
- Turn off cursor - 0FH
- Convert to 32 characters per line mode - 17H
- Backspace cursor - 18H
- Advance cursor one position - 19H
- Downward line feed - 1AH
- Upward line feed - 1BH
- Home (cursor to upper left corner) - 1CH
- Move cursor to beginning of current line - 1DH
- Erase from cursor position to end of line - 1EH
- Erase from cursor position to end of screen - 1FH
033AOUT2D
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-034ADSPPOS
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-035AISCHAR
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.
0361INLIN
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 BREAKkey 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-0386INCHR
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.
038BFINLPT
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,ENTER 3E 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.
039COUTLPT
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-03A3OUTDO
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 FEED FE 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 RETURN 3E 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-03AELZRNOT
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
03B6LZRPOS
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.
03C2CIO
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
03DDCIORTN
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-03E5KEY
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
03EBKEYLP
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"
03FAKEYDWN
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, RCLAinstructions 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)
0402KEYDLP
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-040DKEYFND
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
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-042AKEYNAL
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-0436KEYINV
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
043DKEYSPL
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-0445KEYNSF
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
044BKEYRTN
LD D,A57
Load Register D with the ASCII value for the key pressed in Register A
044C-044EKEYRT2
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-045ADSP
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
0467DSPGRP
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-047FDSPCHR
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-0494DSPRTN
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-049CDSPRD
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"
04A1DSPBOL
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-04A7DSPGRC
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"
04B8DSPCON
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-04BBDSPCN2
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"
04BDDSPCOF
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-04C2DSPHOM
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"
04CEDSPBSP
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-04D8DSPBS2
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-04DCDSPLFT
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
04E2DSPLF2
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-04E9DSPDWN
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"
04ECDSPRHT
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-04F3DSPUP
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-04F8DSPETB
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-0508DSPCTL
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
0541DSPOUT
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
054BDSPOT2
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-0556DSPROL
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
0564DSPCR
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
057CDSPEOF
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-057FDSPERF
LD DE,4000HLD DE,DSPAD$+102411 00 40
Load Register Pair DE with the end of video memory plus one (which is 4000H)
0580-0581DSPERA
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"
058DPRT
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-05A4PRTVT
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-05A8PRTFF
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
05B4PRTIT
PUSH AFF5
Save the character to be sent to the printer in Register A on the STACK
05B5-05B7PRTIT2
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-05CFPRTOP
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
05D9KEYIN
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-05E2KLNNXT
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 CLEARkey
05EE-05EF
Jump if the key that was pressed in Register A is the CLEARkey
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)
060CKLNCHR
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
0619H - Part of the Display routine- "KLNCLR"
Clear the screen
0619H-061BKLNCLR
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-0624KLNCNL
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
062BKLNCAN
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-0642KLNETB
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-0648KLNHT
Go get the cursor line position and return with it in Register A
0649-064A
AND 07H<ADD 0000 0111E6 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
0661KLNBRK
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
0662KLNCR
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-0675INIT
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)
0687CLRAM
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-068DCHKMAN
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 04HAND 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)
| 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 |
Not Ready | Protected | Head Loaded | Seek Error | CRC Error | Track 00 | Index | Busy |
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),03H0000 001136 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)
| 0 | 0 | 0 | 0 | h | V | r1 | r0 | | Command=Restore Bit 7-4: Command Code (0000) h: 1=Enable Head Load/Settle, 0=No delay V: 1=Verify Track 0 ID, 0=No verification r1, r0: Stepping Motor Rate (00=3ms, 01=6ms, 10=10ms, 11=20ms) |
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-06B3BOOTDL
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
| 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 |
Not Ready | Protected | Head Loaded | Seek Error | CRC Error | Track 00 | Index | Busy |
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
| 1 | 0 | 0 | m | b | E | 0 | 0 | | Command=Read Sector Bit 7-5: Read Command (100) m: 1=Multiple Records, 0=Single Record b: 1=IBM format, 0=Non-IBM Format E: 1=Enable HLD, HLT, and 10ms Delay, 0=Assume Head Already Engaged, no Delay Remainder: Unused (00) |
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-06C1BOOTLP
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 1A19H as the latter sometimes does strange things to any resident BASIC program
06CC-06CERESETR
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"
06D2INITR
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)
06E1
RETC9
This will be 400FH - it is a RETurn from RST 30H (which is a jump to 44B4H for DOS)
06E300
NOP
This will be 4012H - RST 38H vector DI/RET (JP 4518H for DOS)
06E4
EIFB
This is the interrupt entry point vector
This is the keyboard DCB
06E8
03 E3
Keyboard DCB + 1 - Driver Address
This is the display DCB
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
This is the printer DCB
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
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
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
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
| DFACLO | 4 | Four lowest orders for double precision |
| FACLO | 3 | Low order of Mantissa, Middle Order of Mantissa, High Order of Mantissa |
| FAC | 2 | Exponent, Temporary Complement of the Sign in the MSB |
| ARGLO | 7 | Temporary location of second argument for double precision |
| ARG | 1 | |
| FBUFFR | | Buffer 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).
According to Vernon Hester, there are a whole lot of errors in ROM code for processing math. I have no hope of finding the code so I will put them here:
When a number is just under specific decimal magnitudes, the ROM prints a colon instead of 10
Example: 9999999999999999 / 1D12 + 3D-13 returns :000
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-070AFADDH21 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-070DFADDS
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-0712FSUBS
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-0715FSUB
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
0716FADD
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 rounding 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.
0754FADD3
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-0764FADFLT
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.
0765NORMAL
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.
0768NORM1
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.
0778ZERO
XOR AAF
Zero Register A
0779-077BZERO0
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"
077DNORM2
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-0787NORM3
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.
Vernong Hester has flagged an error in the rounding of math routines. In base 10, rounding to k-digits examines digit k+1. If digit k+1 is 5 through 9, then digit k is adjusted up by one and carries to the most significant digit, if necessary. If digit k+1 is less than 5, then digit k is not adjusted. This should not get muddled with the conversion of base 2 to base 10. Nevertheless, four divided by nine should be: .444444 and not .444445
0796ROUND
LD A,B78
Load Register A with the LSB of the single precision value in Register B
0797-0799ROUNDB
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.
07A8ROUNDA
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-07B3OVERR
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.
07B7FADDA
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-07C5NEGR
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-07D8SHIFTR
LD B,00H06 00
Load Register B, which will hold the overflow byte, with zero to reset the overflow byte
07D9-07DASHFTR1
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-07E5SHFTR2
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
07E7SHFTR3
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
07EBSHRADD
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-07FBFONE
00 00 00 8100
A single precision constant equal to 1.0 is stored here
07FCH-0808H - SINGLE PRECISION CONSTANTS STORAGE LOCATION 2- "LOGCN2"
07FCLOGCN2
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.
Vernon Hester has identified a bug in the LOG() routine. Regardless of the base, if the argument is 1 then the logarithm is zero, if the argument is >1 then the logarithm is positive, and if the argument is >0 and < 1 then the logarithm is negative. However, if the argument is just under 1, the ROMs LOG function produces a positive value. e.g., 10 PRINT LOG(.99999994)
0809-080BLOG
Go check the sign (or zero value) of the single precision value in the ACCumulator
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-0843MULLN2
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-0849FMULT
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
0869FMULT2
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.
0871FMULT4
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
0881FMULT5
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.
0892FMULT3
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-0899DIV10
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-08A4FDIV
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)
08C7FDIV1
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
08D7
SCF37
Set the CARRY FLAG so that the next bit in the quotient is a 1 to indicate that the subtraction was good
08D9FDIV2
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-0908MULDVS
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.
090AMULDVA
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"
0914MULDIV
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
092FDCXHRT
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-0933MLDVEX
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
0936MULDV1
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
0937MULDV2
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 ERRORif 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 ERRORif 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-0957SIGN
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-095CSIGNC
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.
095EFCOMPS
CPL2F
Complement the sign. This is ignored if passing through and proceesed only if specifically jumped to.
095FICOMPS
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
0962INRART
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-0965FLOAT
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-096BFLOATR
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-0979ABS
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.
097BVNEG
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:
| If the Variable Type is ... | and the Flags are set ... | ... then Register A will be set ... |
| Integer | NZ/C/M/E | -1 |
| String | Z/C/P/E | 0 |
| Single Precision | NZ/C/P/O | 1 |
| Double Precision | NZ/NC/P/E | 5 |
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-0984NEG
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"
SGNfunction (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-098CSGN
Get the sign of the ACCumulator into Register A
This routine will convert a signed number (held in Register A) into an integer.
098DCONIA
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.
0994VSIGN
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:
| If the Variable Type is ... | and the Flags are set ... | ... then Register A will be set ... |
| Integer | NZ/C/M/E | -1 |
| String | Z/C/P/E | 0 |
| Single Precision | NZ/C/P/O | 1 |
| Double Precision | NZ/NC/P/E | 5 |
0995-0997
If that test showed STRING, Display a ?TM ERRORmessage
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.
099EISIGN
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.
09A4PUSHF
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-09B3MOVFM
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!
09B4MOVFR
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 ...
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
09BF?????
LD HL,4121H21 21 41
Load Register Pair HL with the address of the ACCumulator
09C2MOVRM
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
09C4GETBCD
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
09C9INXHRT
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-09CDMOVMF
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-09CFMOVE
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))
09D2MOVVFM
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-09D5VMOVE
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
09D7MOVE1
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-09E1UNPACK
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-09F6VMOVFA
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-09F9VMOVFM
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.
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-09FEVMOVAF
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-0A01VMOVMF
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.
0A02VMVVFM
PUSH DED5
When entered here, save Register Pair DE (which, if passed through, is a return address) on the STACK
0A03-0A05VDFACS
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:
| If the Variable Type is ... | and the Flags are set ... | ... then Register A will be set ... |
| Integer | NZ/C/M/E | -1 |
| String | Z/C/P/E | 0 |
| Single Precision | NZ/C/P/O | 1 |
| Double Precision | NZ/NC/P/E | 5 |
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.
0A0CFCOMP
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
0A23FCOMPD
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
0A39ICOMP
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-0A4BDCOMPD
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-0A51XDCOMP
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
0A6CDCOMP1
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-0A7ADCOMP
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.
0A7FFRCINT
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:
| If the Variable Type is ... | and the Flags are set ... | ... then Register A will be set ... |
| Integer | NZ/C/M/E | -1 |
| String | Z/C/P/E | 0 |
| Single Precision | NZ/C/P/O | 1 |
| Double Precision | NZ/NC/P/E | 5 |
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 ERRORmessage
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
0A99CONIS1
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-0A9CMAKINT
LD (4121H),HLLD (FACLO),HL22 21 41
Save the integer value in Register Pair HL as the current value in the ACCumulator.
0A9D-0A9EVALINT
LD A,02H3E 02
Load Register A with an integer number type flag.
0A9F-0AA1CONISD
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-0AA5CONIS2
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:
| Condition | Register A |
| If ACCumulator = BCDE | 00 |
| If ACCumulator > BCDE | 01 |
| If ACCumulator < BCDE | FF |
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
0AB1FRCSNG
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:
| If the Variable Type is ... | and the Flags are set ... | ... then Register A will be set ... |
| Integer | NZ/C/M/E | -1 |
| String | Z/C/P/E | 0 |
| Single Precision | NZ/C/P/O | 1 |
| Double Precision | NZ/NC/P/E | 5 |
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-0ABBCONSD
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-0ACECONSI
LD HL,(4121H)LD HL,(FACLO)2A 21 41
Load Register Pair HL with the integer value from the ACCumulator
0ACF-0AD1CONSIH
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.
0ADBFRCDBL
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:
| If the Variable Type is ... | and the Flags are set ... | ... then Register A will be set ... |
| Integer | NZ/C/M/E | -1 |
| String | Z/C/P/E | 0 |
| Single Precision | NZ/C/P/O | 1 |
| Double Precision | NZ/NC/P/E | 5 |
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-0AE5CONDS
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-0AEDVALDBL
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.
0AF4CHKSTR
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:
| If the Variable Type is ... | and the Flags are set ... | ... then Register A will be set ... |
| Integer | NZ/C/M/E | -1 |
| String | Z/C/P/E | 0 |
| Single Precision | NZ/C/P/O | 1 |
| Double Precision | NZ/NC/P/E | 5 |
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-0AF7TMERR
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).
0AFBQINT
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"
0B1FQINTA
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
0B24DCXBRT
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
0B26FIX
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:
| If the Variable Type is ... | and the Flags are set ... | ... then Register A will be set ... |
| Integer | NZ/C/M/E | -1 |
| String | Z/C/P/E | 0 |
| Single Precision | NZ/C/P/O | 1 |
| Double Precision | NZ/NC/P/E | 5 |
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
According to Vernon Hester, there is are a number of bugs in this routine.
First, INT(value) should produce a result equal to or less than value. However, if the value is double-precision (by definition), the ROM rounds value to single-precision first, then performs the INT function. e.g., PRINT INT(2.9999999) produces 3 instead of 2.
Next, INT(value) should never overflow. However, if the value is double-precision 32767.9999#, the ROM overflows.
Next, INT(value) should produce a result equal to or less than value. However, if the value is double-precision equal to ?2"n+2"(n-7) where n is an integer >14, the ROM produces an incorrect value. e.g., PRINT INT(?44800#) produces ?45056
0B37VINT
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:
| If the Variable Type is ... | and the Flags are set ... | ... then Register A will be set ... |
| Integer | NZ/C/M/E | -1 |
| String | Z/C/P/E | 0 |
| Single Precision | NZ/C/P/O | 1 |
| Double Precision | NZ/NC/P/E | 5 |
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 ERRORif 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-0B42INT
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-0B5BDINT
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
0B6BDINT1
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-0B79DINT2
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
0B7BDINTFO
↳ "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-0BA2DINTA
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.
0BA3DINTA1
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.
0BAAUMULT
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)
0BB4UMULT1
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
0BC1UMULT2
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
0BC4MULRET
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
0BC7ISUB
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).
0BD2IADD
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
0BD5IADDS
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.
0BF2IMULT
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)
0C04IMULT1
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.
0C15IMLDIV
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
0C26IMULT5
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
0C32FMULTT
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"
0C37IMULT4
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.
0C45IMULDV
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.
0C4CINEGH
LD A,H7C
Load Register A with the MSB+SIGN of the integer value in Register H
0C4DINEGA
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.
0C51INEGHL
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-0C5DINEG
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
0C66INEG2
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-0C6CINEGAD
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.
Vernon Hester has flagged a bug. Double-precision subtraction should produce an difference accurate to 16 digits. However, the difference resulting from doubleprecision subtraction is erroneous when the smaller operand's value is significantly less than the larger operand's value
Example: In the code Y# = .20# : X# = 1D16 : J# = X# - Y# : PRINT J# - X#J# is incorrect and J#-X# shows a positive result when it is negative.
0C70-0C72DSUB
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-0C79DADD
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
0C92DADD1
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-0CA2DADD2
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 ERRORif 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-0CD1DADD3
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"
0CD8DNORML
XOR AAF
Zero Register A, which will act as a byte shift counter
0CD9DNORM1
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)
0CE5DNORM2
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
0CF6DNORM3
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.
0D11DROUNB
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-0D22DROUNA
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
0D25DRON1
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-0D35DADDAA
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-0D38DADDFO
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-0D3ADADDS
LD C,07H0E 07
Load Register C with the number of bytes to be added
0D3B
XOR AAF
Clear the Carry flag
0D3CDADDLS
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-0D47DADDAS
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-0D4ADADDFS
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-0D4CDADDSS
LD C,07H0E 07
Load Register C with the number of bytes to be subtracted
0D4D
XOR AAF
Clear the Carry flag
0D4EDADDLS
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.
0D57DNEGR
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
0D61DNEGR1
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.
0D69DSHFTR
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-0D6CDSHFR1
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
0D74DSHFR2
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-0D7EDSHFR3
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
0D84DSHFRA
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
0D87DSHFR5
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-0D92DSHFRB
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-0D98DSHFLC
LD C,08H0E 08
Load Register C with the number of bytes to be shifted
0D99DSHFTL
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-0DA3DMULT
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
0DAFDMULT2
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
0DB7DMULT3
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
0DC5DMULT4
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-0DCEDMULT5
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-0DDBDTEN
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-0DDBFTEN
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-0DDEDDIV10
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.
According to Vernon Hester, there is a bug this routine. Double-precision division should return a zero quotient when the dividend is zero. However, when the dividend is zero and the divisor is less than .25#, the ROM's double-precision division produces an non-zero quotient. e.g., PRINT 0 / .24# produces a quotient of 1.171859195766034D-38. If the divisor is 2.938735877055719D-39 then the quotient is .5
Another bug is that double-precision division should perform correctly for absolute values that are from 2.938735877055719D-39 to 1.701411834604692D+38. If the divisor is the minimum magnitude or the minimum magnitude times 2, then double-precision division errors.
10 Z# = 1 / (2^125 + 2^125) * .25 'This values Z# with 2.938735877055719D-39
20 PRINT 1 / Z# 'displays 2.938735877055719D-39 instead of overflow
0DE5-0DE7DDIV
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-0DFBDDIV1
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
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.
0E12DDIV2
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 ERRORif 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.
0E39DMULDV
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
0E44DMLDV1
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-0E4FDMUL10
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 ERRORif 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 ERRORif 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-0E67FINDBL
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).
0E6CFIN
XOR AAF
Zero Register A for the purpose of triggering the GOSUB to MAKINT at 0E73H
0E6DFINCHR
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
0E83FINC
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
0EA3FINEX1
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-0EA6FINEX
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!
0EBCFINEC
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
0EC7FINE
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-0ECCFINE2
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:
| If the Variable Type is ... | and the Flags are set ... | ... then Register A will be set ... |
| Integer | NZ/C/M/E | -1 |
| String | Z/C/P/E | 0 |
| Single Precision | NZ/C/P/O | 1 |
| Double Precision | NZ/NC/P/E | 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.
0EE4FINDP
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:
| If the Variable Type is ... | and the Flags are set ... | ... then Register A will be set ... |
| Integer | NZ/C/M/E | -1 |
| String | Z/C/P/E | 0 |
| Single Precision | NZ/C/P/O | 1 |
| Double Precision | NZ/NC/P/E | 5 |
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"
0EEEFININT
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:
| If the Variable Type is ... | and the Flags are set ... | ... then Register A will be set ... |
| Integer | NZ/C/M/E | -1 |
| String | Z/C/P/E | 0 |
| Single Precision | NZ/C/P/O | 1 |
| Double Precision | NZ/NC/P/E | 5 |
0EEF-0EF1
If that test shows anything but an INTEGER, jump to the Level II BASIC error routine and display a ?SN ERROR message
0EF2INFINE
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.
0EF5FINDBF
OR AB7
If we are here then we need to force double precision, so set the NZ FLAG
0EF6-0EF8FINSNF
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.
0EFBFINFRC
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.
0F0AFINMUL
RET ZC8
If the exponent is ZERO then exit right back out
0F0BFINMLT
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:
| If the Variable Type is ... | and the Flags are set ... | ... then Register A will be set ... |
| Integer | NZ/C/M/E | -1 |
| String | Z/C/P/E | 0 |
| Single Precision | NZ/C/P/O | 1 |
| Double Precision | NZ/NC/P/E | 5 |
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
0F16DCRART
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.
0F18FINDIV
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:
| If the Variable Type is ... | and the Flags are set ... | ... then Register A will be set ... |
| Integer | NZ/C/M/E | -1 |
| String | Z/C/P/E | 0 |
| Single Precision | NZ/C/P/O | 1 |
| Double Precision | NZ/NC/P/E | 5 |
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.
0F29FINDIG
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:
| If the Variable Type is ... | and the Flags are set ... | ... then Register A will be set ... |
| Integer | NZ/C/M/E | -1 |
| String | Z/C/P/E | 0 |
| Single Precision | NZ/C/P/O | 1 |
| Double Precision | NZ/NC/P/E | 5 |
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:
| Condition | Flag |
| If HL < DE | CARRY SET |
| If HL > DE | NO CARRY |
| If HL ≠ DE | NZ |
| If HL = DE | Z |
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
0F51FINDGE
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
0F57FINDG1
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-0F5BFINDG2
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-0F5EFINDGV
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:
| Condition | Register A |
| If ACCumulator = BCDE | 00 |
| If ACCumulator > BCDE | 01 |
| If ACCumulator < BCDE | FF |
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-0F76FINDG3
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-0F79FINDGD
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-0F8BFINLOG
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.
0F94FINEDG
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 will never trigger, but neither will the next instruction!
0FA2-0FA3FINEDO
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.
0FA7INPRT
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-0FB1LINPRT
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.
0FBDFOUT
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
0FC7FOUT1
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
0FD9FOUT2
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-0FF7FOUTZS
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