Model 4 ROM Explained

Page Customization



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

0000-0002 Disables the interrupts, clears the A register, then jumps to initialisation routine at 674H

0000–  ↳ START
DIF3
Disables the interrupts and turns off clock
0001
XOR AAF
Clears the A Register and status
0002
Go to the Boostrap/Initialization routine at 3015H (which just jumps to 3455H which is the BOOTSTRAP (sets interrupts, clears ports, checks for a BREAK key and tries to work with the disk controller).
0005-0007
JP 4000HJP RST1$C3 00 40
Go to RST 0008H code via 4000H

0008 (RST 8H) Jumps to 4000H. 4000H passes control to 1C96H. This routine is used for scanning strings. It compares the character pointed to by the HL Register Pair with the character pointed to by the return address on the top of the STACK (Note that a RST instruction is in effect a CALL and places a return address on the STACK) formula: (HL)=((SP))? If they are not equal an SN ERROR will result; if they are equal then the return address on the STACK will be incremented to bypass the test character and control will be passed to RST 10H logic. RST 8H is used to look for expected characters in a string and then return with (HL) pointing to the next non-blank character. (see RST l0H) (BC and DE registers unaffected.). This routine can be used by CALLing 1C96H or RST 8H

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

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

000B–  ↳ WHERE
POP HLE1
Get the address from the STACK and put it in Register Pair HL

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

000C
JP (HL)E9
Jump to the location of the address in Register Pair HL
000D
JUMP to the disk load and run sector routine at 069FH.

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

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

0010
(RST 010H)
JP 4003HJP RST2$C3 03 40
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
JR 0046HJR CIOJ18 2E
Jump to the Level II BASIC driver entry routine at 0046H

0018 (RST 18H) Jumps to 1C90H through 4006H. This routine can be called by using RST 18H or CALL 1C90H. It compares two 16 bit values in HL and DE and sets the S and Z flags accordingly (they are set in the same way as for a normal 8 bit CP). All registers are unchanged except for A

0018–  ↳ RST18
JP 4006HJP RST3$C3 06 40
Jumps to 1C90H through 4006H. This routine can be called by using RST 18H or CALL lC90H. It compares two 16 bit values in HL and DE and sets the S and Z flags accordingly (they are set in the same way as for a normal 8 bit CP). All registers are unchanged except for A.
This is the COMPARE DE:HL routine, which numerically compares DE and HL. Will not work for signed integers (except positive ones). Uses the A-register only. The result of the comparison is returned in the status Register as: CARRY SET=HL<DE; NO CARRY=HL>DE; NZ=Unequal; Z=Equal)

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

This routine outputs a byte to a device. When calling, A = output byte, DE = starting address of DCB of device. On exit, Z set if device ready. Uses AF.

001B–  ↳ PUT
PUSH BCC5
Save the value in Register Pair BC on the STACK
001C-001D
LD B,02H06 02
Load Register B with the device type entry code of 02H
001E-001F
JR 0046HJR CIOJ18 26
Jump to the Level II BASIC driver entry routine at 0046H

0020 (RST 20H) This routine jumps to 25D9H through 4009H. If the NTF=8 then C=RESET or else C=SET, Z flag will be SET if NTF=3 (S flag is valid also.). After execution of RST 20H or CALL 25D9H, A will contain the value NTF-3, all other registers are unchanged. (The NTF will be discussed in the arithmetic section.)

0020
(RST 020H)
JP 4009HJP RST4$C3 09 40
This routine jumps to 25D9H through 4009H. If the NTF=8 then C=RESET or else C=SET, Z flag will be SET if NTF=3 (S flag is valid also.). After execution of RST 20H or CALL 25D9H, A will contain the value NTF-3, all other registers are unchanged.
Returns a combination of STATUS flags and unique numeric values in the A Register according to the data mode flag (40AFH). Integer = NZ/C/M/E and A is -1; String = Z/C/P/E and A is 0; Single Precision = NZ/C/P/O and A is 1; and Double Precision is NZ/NC/P/E and A is 5. This CALL is usually made to determine the type of the current value in the ACCumulator (i.e., 4121H-4122H). It should be used with caution, however since the mode flag and ACCumulator can get out of phase particularly if some of the CALLS described here are used to load ACCumulator

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

0023–  ↳ $CTL
PUSH BCC5
Save the value in Register Pair BC on the STACK
0024-0025
LD B,04H06 04
Load Register B with the device type entry code of 04H
0026-0027
JR 0046HJR CIOJ18 1E
Jump to the Level II BASIC driver entry routine at 0046H

0028 (RST 28H) Jumps to 400CH which contains C9H (RET) under Level II BASIC. This vector is only used by Disk BASIC. It is called by the BREAK key routine, and can be used to intercept the BREAK key logic

0028
(RST 028H)
JP 400CHJP RST5$C3 0C 40
Jumps to 400CH which contains C9H (RET) under Level II BASIC. This vector is only used by Disk BASIC. It is called by the BREAK key routine, and can be used to intercept the BREAK key logic.
This is the DOS FUNCTION CALL routine at RST 28 (which passes request code in A-register to DOS for processing. Returns for non-disk system. For disk systems, the A Register must contain a legitimate DOS function code. If the code is positive, the CALL is ignored and control returns to the caller. Note that the DOS routine discards the return address stored on the STACK by the RST instruction. After processing control will be returned to the previous address on the STACK)

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

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

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

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

Scan Keyboard: Performs an instantaneous scan of the keyboard. If no key is depressed control is returned to the caller with in Register A and status Register set to zero. If any key (except the BREAK key) is active the ASCII value for that character is returned in the A-register. If the BREAK key is active, a RST 28 with a system request code of 01 is executed. The RST instruction results in a JUMP to the DOS Exit 400C. On non-disk Systems the Exit returns, on disk systems control is passed to SYS0 where the request code will be inspected and ignored, because system request codes must have bit 8 on. After inspection of the code, control is returned to the caller of 002B. Characters detected at 002B are not displayed. Uses DE, status, and A register

002B-002D–  ↳ $KBD
LD DE,4015HLD DE,KDCB$11 15 40
Load Register Pair DE with the starting address of the keyboard device control block.
Note: 4015H holds Keyboard DCB – Device type
002E-002F
JR 0013HJR $GET18 E3
Jump to the Level II BASIC driver entry routine
0030
(RST 030H)
JP 400FHJP RST6$C3 0F 40
This location passes control to 400FH which contains a RET (C9H) under Level II. This location is only used by a Disk system.
This is the LOAD DEBUG routine, and loads the DEBUG program and transfers control to it. When DEBUG processing is complete, control is returned to the orginal caller. For non-disk systems control is returned immediately

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

Character print routine. A CALL 33H will print a character at the current cursor position. The A Register must contain the ASCII code for the character or graphics figure that is to be printed before CALLing this routine. The DE Register Pair is used by the routine.
A call to 0033H displays the character in the A-register on the video. Control codes are permitted. All registers are used.

0033-0035–  ↳ $DSP
LD DE,401DHLD DE,DDCB$11 1D 40
Load Register Pair DE with the starting address of the video display device control block.
Note: 401DH holds Video DCB – Device type
0036-0037
JR 001BHJR $PUT18 E3
Jump to the Level II BASIC driver entry routine at 001BH

0038 (RST 38H) This location will pass control to 4012H. This location is only used by a Disk system

0038
(RST 038H)
JP 4012HJP RST7$C3 12 40
This location will pass control to 4012H. This location is only used by a Disk system.
This is the INTERRUPT ENTRY POINT routine at RST 38H which is the system entry point for all interrupts. It contains a jump to a section of code in the Communications Region designed to field interrupts. That section of code consists of a DI (disables further interrupts) followed by a RET (returns to the point of interrupt) for non-disk systems, or a jump to an interrupt processor in SYSO if it is a DOS system. For DOS systems the interrupt handler consists of a task scheduler, where the exact cause of the interrupt is determined (usually a clock interrupt) and the next task from the task control block is executed. After task completion, control returns to the point of interrupt

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

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

A call to 003BH causes the character contained in the 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.

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

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

0040-0042–  ↳ $KEYIN
JP 05D9HJP KEYINC3 D9 05
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)

*0043H-0045H – Model 4 Gen 1 Routine – Unused Code

*0043
RET
*0044
NOP
*0045
NOP

*0043H-0045H – Model 4 Gen 2 Routine – Called from new routine at 3790H

*0043-0045
JP 0434HC3 34 04
Jump into the new PRINT SCREEN routine

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

0046
JUMP to 0674H which is the is the Level II BASIC keyboard driver entry routine.

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

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

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

Wait For Keyboard Input: Returns as soon as any key on keyboard is pressed. ASCII value for character entered is returned in A- register. Uses A, status and DE registers

0049-004B–  ↳ $KEY
CALL 002BHCALL $KBDCD 2B 00
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
JR 0049HJR $KEY18 F9
Loop until a key is pressed

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

This is a table of control characters used by BASIC.

0050
Load DE with the RS-232 Input DCB of 41E5H.
0053
Input a Byte from the RS-232
0055
Load DE with the RS-232 Output DCB of 41EDH
0058
Output a Byte to the RS-232
005A
Load DE with the RS-232 Controller DCB of 41F5H
005D
Set up the RS-232
005F00
NOP

*0060H – Model 4 Gen 1 – DELAY ROUTINE – “$PAUSE”

This is a delay loop. The BC Register Pair is used as the loop counter. The duration of the delay, in microseconds, is the value of BC times 14.66. Register A is used.

*0060
JUMP to the delay routine at 01FBH, which uses BC as a loop counter. It RETs when done, so the next instruction is NOT a pass-through!
0063-0064
JR NZ,0060HJR NZ,PAUSE20 FB
Loop until the counter in Register Pair BC is equal to zero
0065
RETC9
RETurn to CALLer

*0060H – Model 4 Gen 2 – DELAY ROUTINE – “$PAUSE”

This is a delay loop. The BC Register Pair is used as the loop counter. The duration of the delay, in microseconds, is the value of BC times 14.66. Register A is used.

*0060
JUMP to the delay routine at 01FBH, which uses BC as a loop counter. It RETs when done, so the next instruction is NOT a pass-through!
0063-0065
JP 041FHC3 1F 04
Jump to the printer routine

0066H – Program control jumps when the RESET button is pressed (Non Maskable Interrupt address)

0066
Go to the non-maskable interrupt routine at 3039H.

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
Initialize all I/O Drivers by jumping to 0452

*006C-0074H – Model 4 Gen 1 – Unused Code

*006C
Load DE with the $ Routine DCB at 421DH
*006F
Set the I/O Routine by jumping to 001BH
*0071
NOP
*0072-0074
JP 06CCHJP RESETRC3 CC 06
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

*006C-0074H – Model 4 Gen 2

*006C
RETC9
RETurn to Caller

*006D-0070H – Model 4 Gen 2 – New Code for Gen 2 – Called from 02C3H when the BREAK key is hit

*006D
LD BC,1A18H01 18 1A
Set Register Pair BC to point to the STPRDY Routine
*0070
JP 19AEHC3 AE 19
JUMP to 19AEH to reset the stack and reinitialize the system variables, including reinitializing the STACK to the location now held in SAVSTK
*0073
NOP00
No Operation
*0074
NOP00
Operation

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
Continue with the communication region initialization by loading register pair HL with 42E5H.
0083-0084
LD (HL),3AHLD (HL),”:”36 3A
Save a 3AH (which a “:”) at the location of the memory pointer in Register Pair HL (which is 41E5H)
0085
INC HL23
Increment the memory pointer in Register Pair HL from 41E5H to 41E6H
0086
LD (HL),B70
Zero out 41E6H (the location of the memory pointer in Register Pair HL)
0087
INC HL23
Increment the memory pointer in Register Pair HL from 41E6H to 41E7H
0088-0089
LD (HL),2CHLD (HL),”,”36 2C
Save a 2CH (which is a “,”) at 41E7H (the location of the memory pointer in Register Pair HL)
008A
INC HL23
Increment the memory pointer in Register Pair HL from 41E7H to 41E8H, which is the input/output buffer BUFINI

This loads 40A7H with the I/O buffer location address 41E8H. (40A7H is the I/O buffer pointer and can be changed to relocate the buffer.)

008B-008D
LD (40A7H),HLLD (BUFPNT),HL22 A7 40
This loads the input buffer pointer (held at 40A7H) with the keyboard buffer location address of 41E8H. (40A7H is the I/O buffer pointer and can be changed to relocate the buffer.). Save the value in Register Pair HL as the starting address of the keyboard input buffer area.
Note: 40A7H-40A8H holds the input Buffer pointer
008E-0090
LD DE,012DHLD DE,NAVERR11 2D 01
In prepartaion for a jump, load Register Pair DE with the starting address of the ?L3 ERROR routine

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

The rest of the initialization routine. Asks MEMORY SIZE ?, sets the memory pointers accordingly and prints RADIO SHACK LEVEL II BASIC , then it jumps to lAl9H which is the entry point for the BASIC command mode

0091-0092
LD B,1CHLD B,ERCNT06 1C
Since there are 28 pre-defined DOS BASIC commands in ROM, load Register B with the number of times (=28) to save the jump to the ?L3 ERROR routine
0093-0095
LD HL,4152HLD HL,ERCALL21 52 41
Load Register Pair HL with the starting address of the Disk Basic links (which is 4152H) in preparation for generating an error if disk basic commands are attempted.
Note: 4152H-41A3H holds Disk Basic links
0096
LD (HL),C3H
Save a C3H to the location of the memory pointer in register pair HL.
NOTE: C3H is the first byte of a 3 byte JUMP xxxxH command.
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
DJNZ 0096HDJNZ ERLOPS10 F7
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
LD (HL),C9H
Save a C9H to the location of the memory pointer in register pair HL.
NOTE: C9H is a RET instruction.
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
DJNZ 00A1HDJNZ LOPRTS10 F9
Loop from 4156H until all of the DOS links have been set to RETs

00A8H – VIDEO AND PRINTER ROUTINE

00A8
Load register pair HL with the starting address of user RAM (which is 42E8H).
00AB
LD (HL),B70
Zero the end of the buffer (i.e., 42E8H, the location of the memory pointer in Register Pair HL)
00AC
Set the current STACK pointer to 42F8H.
00AF-00B1
CALL 1B8FHCALL STKINICD 8F 1B
Go initialize the Level II BASIC variables and pointers
00B2
NOP
00B3
NOP
00B4
NOP
00B5-00B7–  ↳ MEMGET
LD HL,0105HLD HL,MEMMSG21 05 01
Load Register Pair HL with the starting address of the MEMORY SIZE? message in ROM. In the original ROM source, this address was set as “LD HL,MEMMSG”
00B8-00BA
CALL 28A7HCALL STROUTCD A7 28
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
CALL 1BB3HCALL QINLINCD B3 1B
Print a “?” and get input from the keyboard
00BE-00BF
JR C,00B5HJR C,MEMGET38 F5
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
RST 10HCHRGETD7
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
JR NZ,00D6HJR NZ,TYPMEM20 12
Jump forward to 00D6H if there was a response to the MEMORY SIZE? question
00C4
If we are here then just an ENTER was hit in response to the MEMORY SIZE? question, so we need to figure it out dynamically, so load register pair HL with the starting address for the memory size check.Difference between M1 and M3: The instruction starting at 00C4H loads HL with 434CH in the Model I, 444CH in the Model III. If only “ENTER” was pressed in response to the “MEMORY SIZE?” prompt, a memory test is initiated starting at the location pointed to by HL, and continuing upward until the end of memory (or a bad memory location) is reached.
00C7–  ↳ LOOPMM
INC HL23
We are going to start testing RAM at 17229 (i.e., 434DH) toward 65535, so increment the memory pointer in Register Pair HL
00C8-00C9
LD A,H
OR L7C
There is no way to COMPARE HL against anything, so the common “trick” is to load Register A with Register H and then OR it against Register L. If you do this, Register A can only be zero if both Registers H and L are zero
00CA-00CB
JR Z,00E7HJR Z,USEDEF28 1B
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
JR Z,00C7HJR Z,LOOPMM28 F3
If the address exists, loop back to 00C7H until the end of memory is found
00D4-00D5
JR 00E7HJR USEDEF18 11
If the address didn’t exist, jump to 00E7H (which goes to he next address and tries again)
00D6-00D8–  ↳ TYPMEM
CALL 1E5AHCALL LINGETCD 5A 1E
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
JP NZ,1997HJP NZ,SNERRC2 97 19
Display a ?SN ERROR if Register A is not equal to zero
00DD
EX DE,HLEB
Swap DE (where the integer version of the MEMORY SIZE? answer is located) and HL, so that Register Pair HL now has with the MEMORY SIZE answer again, but in integer format
00DE
DEC HL2B
Decrement the MEMORY SIZE? in Register Pair HL
00DF-00E0
LD A,8FH3E 8F
Load Register A with a memory test value of 8F or 10001111
00E1
LD B,(HL)46
Load Register B with the value at the location of the MEMORY SIZE? pointer in Register Pair HL (to save the data thats there)
00E2
LD (HL),A77
Put the test pattern (in A which is 8FH) into that the location of the MEMORY SIZE? pointer in Register Pair HL
00E3
CP (HL)BE
Check to see if the value in the memory location set in HL matches the test pattern in A
00E4
LD (HL),B70
Restore the old memory contents back
00E5-00E6
JR NZ,00B5HJR NZ,MEMGET20 CE
The test at MEMORY SIZE? -1 failed so we have to ask MEMORY SIZE again by jumping to 00B5H
00E7–  ↳ USEDEF
DEC HL2B
Decrement the memory size pointer in Register Pair HL, so it is the amount of memory – 2
00E8
Load register pair DE with the minimum MEMORY SIZE? response (held at 4514H).
00EB
RST 18HCOMPARDF
Now we need to check to see if the MEMORY SIZE? pointer (in HL) is less than the minimum MEMORY SIZE? response (in DE), so we call the COMPARE DE:HL routine, which numerically compares DE and HL. Will not work for signed integers (except positive ones). Uses the A-register only. The result of the comparison is returned in the status Register as: CARRY SET=HL<DE; NO CARRY=HL>DE; NZ=Unequal; Z=Equal)
00EC-00EE
JP C,197AHJR C,OMERRDA 7A 19
If C is set, then the amount of actual memory (in HL) is less than the minimum memory required (in DE), so we have to go to the Level II BASIC error routine and display an OM ERROR
00EF-00F1–  ↳ STRSZD
LD DE,FFCEHLD DE,65536-STRSZD11 CE FF
Load Register Pair DE with the default size of the string area (i.e., negative fifty)
00F2-00F4
LD (40B1H),HLLD (MEMSIZ),HL22 B1 40
Save the MEMORY SIZE? amount (which is in HL) to 40B1H (which holds the MEMORY SIZE? pointer)
00F5
ADD HL,DE19
Subtract the size of the string data (which was -50) from the highest memory address (stored in HE)
00F6-00F8
LD (40A0H),HLLD (STKTOP),HL22 A0 40
Save the start of string space pointer (which is now held Register Pair HL) to 40A0H.
Note: 40A0H-40A1H holds the start of string space pointer
00F9-00FB
CALL 1B4DHCALL SCRTCHCD 4D 1B
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

*00FFH – Model 4 Gen 1 Code

*00FF
Different ROM Versions handle this differently. Both will display the message pointed to by HL.
*0102-0104
JP 1A19HJP READYC3 19 1A
Go to the Level II BASIC READY routine

*00FFH – Model 4 Gen 2 Code

*00FF
CALL 021BHCD 1B 02
GOSUB to 021BH. Note; 021BH will display the character at (HL) until a 03H is found.
*0102
JP 01E6HC3 E6 01
JUMP to 01E6H to keep processing. In this case, next, display the COPYRIGHT message

0105H-0110H – MESSAGE STORAGE

The “MEMORY SIZE” message is located here

0105
“MEMORY SIZE” + 00H
MEMORY SIZE message storage area.
0111
“RADIO SHACK MODEL-III BASIC” + 0DH
RADIO SHACK MODEL-III BASIC message storage area.

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

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

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

0132–  ↳ GRPHCS
RST 10HCHRGETD7
Since we need to bump the current BASIC program pointer until it points to the next character, call the EXAMINE NEXT SYMBOL routine at RST 10H.

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

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

0135
LD A,80H
Load register A with 80H (Decimal:128) which is SET.
0137
LD BC,013EH01 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

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

0138
LD A,01H
Load register A with 01H which is RESET.

013AH-019CH GRAPHICS ROUTINE

Common code for SET/RESET/POINT – A will be 0 if POINT, 80H if SET and 1 for RESET.

013A
PUSH AFF5
Save the flag which indicates which command was requested (held in Register A) to the STACK
013B
RST 08H + 28H
Since SET/RESET/POINT all need a “(” to start with, call the COMPARE SYMBOL routine which comparess the symbol in the input string pointed to by HL register to the value in the location following the RST 08 call. If there is a match, control is returned to address of the RST 08 instruction 2 with the next symbol in the 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
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
JP NC,1E4AHJP NC,FCERRD2 4A 1E
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
RST 08H + “,”
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 the Aregister 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
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 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
JP NC,1E4AHJP NC,FCERRD2 4A 1E
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–  ↳ SETRES
LD D,FFH
Prepare to divide Y coordinate by 3 … load register D with starting quotient of FFH (which will turn into 00 once it enters the loop).
0152–  ↳ LOPMD3
INC D14
Increment the quotient in Register D
0153-0154
SUB 03HD6 03
Divide by subtraction; in this case subtract 3 from Register A
0155-0156
JR NC,0152HJP NC,LOPMD330 FB
Loop until Register D equals the Y value divided by 3
0157-0158
ADD 03HC6 03
Make the remainder positive by adjust the remainder in Register A by adding back 3
0159
LD C,A4F
Save the remainder in Register C
015A
POP AFF1
Get the requested coordindate’s X value from the STACK and put it in Register A
015B
ADD A,A87
Multiply the X value in Register A by two, storing the result in Register A
015C
LD E,A5F
Load Register E with the newly doubled Register A
015D-015E
LD B,02H06 02
Load Register B with the number of times to shift Register Pair DE right (which is 2)
015F–  ↳ SHFTW
LD A,D7A
Load Register A with the adjusted Y value in Register D
0160
RRA1F
Divide the adjusted Y value in Register A by two. RRA rotates the contents of Register A right one bit position, with Bit 0 going to the CARRY FLAG, and the CARRY FLAG going to Bit 7. RRA also can be used to divide a number in 2.
0161
LD D,A57
Save the new Y value in Register A in Register D
0162
LD A,E7B
Load Register A with the adjusted X value in Register E
0163
RRA1F
Divide the adjusted X value in Register A by two. RRA rotates the contents of Register A right one bit position, with Bit 0 going to the CARRY FLAG, and the CARRY FLAG going to Bit 7. RRA also can be used to divide a number in 2.
0164
LD E,A5F
Load Register E with the new X value in Register A
0165-0166
DJNZ 015FHDJNZ DHFTW10 F8
Loop until the memory offset in Register Pair DE has been figured
0167
LD A,C79
Now we need to computer the position of the point so load Register A with the value in Register C
0168
ADC A,A8F
Multiply the value in Register A by two and add the value of the Carry flag to Register A
0169
INC A3C
Increment the value in Register A
016A
LD B,A47
Save the bit position in Register A in Register B
016B
XOR AAF
Zero Register A and reset the Carry flag
016C
SCF37
Set the Carry flag
016D–  ↳ PWR2
ADC A,A8F
Add the value of the Carry flag to Register A
016E-016F
DJNZ 016DHDJNZ PWR210 FD
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
JP M,017CHJP M,FND4FA 7C 01
Skip over the next instruction if the character in Register A is a graphic character
017A-017B
LD A,80H3E 80
Since the character at the screen location turned out not to be a graphics character, we need to set it to a blank graphics character, so load Register A with a blank graphic character which is CHR$(128)
017C–  ↳ FND4
LD B,A47
Save the character which is being modified by the SET/RESET (held in Register A) into Register B
017D
POP AFF1
Get the graphic character and the flags from the STACK and put it in Register A
017E
OR AB7
Set the flags according to the graphic mode in Register A
017F
LD A,B78
Get the existing graphic character on the screen (held in Register B) and put it in Register A
0180-0181
JR Z,0192HJR Z,TBIT28 10
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
JP M,018FHJP M,SBITFA 8F 01
Jump forward to 018FH if the graphic mode is SET
0186
LD A,C79
Load Register A with the graphic mask in Register C
0187
CPL2F
Reverse the graphic mask in Register A
0188
LD C,A4F
Load Register C with the adjusted graphic mask in Register A
0189
LD A,(DE)1A
Load Register A with the character at the location of the video memory pointer in Register Pair DE
018A
AND CA1
RESET the graphic bit by combining the graphic mask in Register C with the graphic character in Register A
018B–  ↳ FINSTB
LD (DE),A12
Save the adjusted graphic character in Register A at the location of the video memory pointer in Register Pair DE
018C
RST 08H + “)”
We need to check HL against 29H (ASCII: )) so we 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 the Aregister 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).
018E
RETC9
RETurn to CALLer
018F–  ↳ SBIT
OR CB1
SET the graphic bit by combining the graphic mask in Register C with the graphic character in Register A
0190-0191
JR 018BHJP FINSTB18 F9
Jump back a few bytes to 018BH
0192–  ↳ TBIT
AND CA1
POINT the graphic bit by combining the graphic mask in Register C with the graphic character in Register A
0193
ADD FFH
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
CALL 098DHCALL CONIACD 8D 09
Save the value in Register A as the current result in the ACCumulator (i.e., 4121H-4122H)
019A
POP HLE1
Get the current BASIC program pointer from the STACK and put it in Register Pair HL
019B-019C
JR 018CHJR FINPTB18 EF
Jump to 018CH

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

019D–  ↳ INKEY
RST 10HCHRGETD7
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
JR NZ,01ABHJR NZ,BUFCIN20 06
Jump to 01ABH (to skip the next character scan) if a key has been pressed
01A5-01A7–  ↳ MRCHRI
CALL 0358HCALL ISCHARCD 58 03
Go scan the keyboard
01A8
OR AB7
Set the status flags
01A9-01AA
JR Z,01BCHJR Z,NULRT28 11
Jump to 01BCH if a key wasn’t pressed
01AB–  ↳ BUFCIN
PUSH AFF5
Save the key pressed (held in Register A) to the top of STACK
01AC
XOR AAF
Clear the buffered character by zeroing Register A
01AD-01AF
LD (4099H),ALD (CHARC),A32 99 40
Save the value in Register A as the last key pressed (which is kept at 16537).
Note: 4099H holds the last key pressed
01B0
INC A3C
Increment the value in Register A (which is going to represet the size of the character string to be built)
01B1-01B3
CALL 2857HCALL STRINICD 57 28
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
JP 2884HJP PUTNEWC3 84 28
Go save the string’s VARPTR as the current result
01BC-01BE–  ↳ NULRT
LD HL,1928HLD HL,REDDY-121 28 19
Load Register Pair HL with the starting address of the “READY” message (which is 6440)
01BF-01C1
LD (4121H),HLLD (FACLO),HL22 21 41
Save the address in Register Pair HL as the current result in the ACCumulator (i.e., 4121H-4122H)
01C2-01C3
LD A,03H3E 03
Load Register A with a string number type flag
01C4-01C6
LD (40AFH),ALD (VALTYP),A32 AF 40
Save the value in Register A as the current number type (which is at 16559).
Note: 40AFH holds current variable’s type flag
01C7
POP HLE1
Get the current BASIC program pointer from the STACK and put it in Register Pair HL
01C8
RETC9
RETurn to CALLer

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

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

To use a ROM call to clear the screen, CALL 01C9H. The cursor is reset to the home position, which is 3C00H.

01C9-01CA–  ↳ CLS
LD A,1CH3E 1C
Load Register A with the ASCII character to home the cursor
01CB-01CD
CALL 033AHCALL OUT2DCD 3A 03
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
JP 033AHJP OUT2DC3 3A 03
Go send the character in Register A (i.e., the ASCII character to clear to the end of the screen) to the video display

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

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

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

NOTE: To run a RANDOM (seed the random number generator) via a ROM call just call CALL 01D3H. This causes the contents of R (memory refresh) to be stored in 40ABH. The entire 24 bit seed is stored in 40AAH-40ACH.

01D3–  ↳ RANDOM
LD A,RED 5F
Load Register A with the current value of the refresh register
01D5-01D7
LD (40ABH),ALD (RNDX+1),A32 AB 40
Save the pseudi-random value in Register A to 40ABH (the random number seed)
01D8
RETC9
RETurn to CALLer

*01D9-01F7H – Model 4 Gen 1 Routine – Print Screen Routine – “$PRSCN”
This routine copies all 1024 characters from the screen to the printer. If the printer is unavailable, this routine waits until the printer becomes available. If BREAK is pressed, this routine returns to the caller.

*01D9
“PRSCN”
Load HL with the memory location for the beginning of the video RAM.
Difference between M1 and M3: The routine to print the contents of the screen on the line printer is located from 01D9H to 01F4H on the Model III. On the Model I, 01D9H – 01F7H contains the routine to output one bit to the cassette.
*01DC
LD A,(HL)
Put the character at the screen location stored in HL into A.
*01DD
CP 80H
Check A against 80H, which represets the lowest graphic character. If A < 80H then the character is NOT graphic and the the CARRY will be set. Otherwise NC is set to show that the character is a graphic character.
*01DF
If the CARRY FLAG is set, we have a non-graphic characters, so skip the next instruction.
*01E1
LD A,2EH
Overwrite the current character held in Register A with a ., so that all graphic characters are printed as .‘s.
*01E3
Call the PRINT CHARACTER routine at 003BH (which sends the character in the A register to the printer).
*01E6
INC HL
Bump HL to the next character on the screen.
*01E7
BIT 6,H
Check the 6th Bit in H to see if we are at the end of the line (meaning that H is now 64; 1 character beyond the 63 maximum per lime).
*01E9
If we are at 64, then JUMP to 0214H for a new line.
*01EB
LD A,L
Prepare to test of end of line by loading Register A with Register L.
*01EC
AND 03FH
AND the contents of A with 3FH (Binary: 00111111) to turn off Bits 7 and 6, making the maximum number A can be 3FH (Decimal: 63).
*01EE
If any of the bits 5-0 are still “1”, then we are not at the end of the line. With this, loop back to 01DCH for the next character.
*01F0
GOSUB to 0214H for a new line.
Difference between M1 and M3: 01F0H contains CALL 0221H instruction on Model I, and CALL 0214H instruction on Model III.
*01F3
Loop back to 01DCH for the next character.
Difference between M1 and M3: Contains LD B,5CH instruction on Model I, JR 01DCH instruction on Model III.
*01F5-01F6–  ↳ CT3
DJNZ 01F5HDJNZ CT310 FE
Loop for delay
*01F7
RETC9
RETurn to CALLer

*01D9 – Model 4 Gen 2 Routine

*01D9
JP 3027HC3 27 30
Jump to the new PRINT SCREEN by first jumping to 3027H, which is just a JUMP to 37A5H.

*01DC-01E5H – Model 4 Gen 2 Routine to wait for either PRINTER READY or a BREAK key

*01DC
CALL 044BHCD 4B 04
GOSUB to 044BH which will check to see if PRINTER READY by polling port F8H
*01DF
RET ZC8
If it is ready (because Z is set) then RETURN.
*01E0
CALL 028DHCD 8D 02
GOSUB to the $KBBRK routine to check for a BREAK key only
*01E3
Loop back to 01DCH to retry the printer if the BREAK wasn’t pressed
*01E5
RETC9
If the BREAK key was pressed, then RETurn to caller

*01E6-01F7H – Model 4 Gen 2 Routine – Called from 0102H after displaying the CASS, MEMORY SIZE, and RADIO SHACK messages

*01E6
LD HL,0202H21 02 02
Point HL to 0202H which is where the copyright message is stored.
*01E9
NOP00
No Operation
*01EA
CALL 021BHCD 1B 02
GOSUB to 021BH. Note; 021BH will display the character at (HL) until a 03H is found.
*01ED
LD HL,3030H213030
Set HL to 3030H, which is a JUMP to 37EAH for STRING=DATE$+””+TIME$.
*01F0
LD (4177H),HL
Load the memory location held at 4177H with HL.

NOTE: 4177H is the TIME$ vector.
*01F3
JP 022EHC3 2E 02
JUMP to 022EH to do the final cleanup before entering BASIC … Enable Interrupts and JUMP to READY prompt
*01F6
NOP00
No Operation
*01F7
NOP00
No Operation

01F8 – Turn Off The Cassette Motor – “$CSOFF”
After writing data to the cassette, this routine should be called to turn off the cassette drive. There are no entry conditions and no registers are modified.

01F8
“CSOFF”
JUMP to 300CH to turn the cassette off.
Difference between M1 and M3: In the Model I, the routine to turn off the cassette recorder is located from 01F8H to 0211H. In the Model III, 01F8H contains a jump to 300CH ( the location of a vector to the “turn off cassette” routine in the Model III). 01FBH through 0201H contain the time delay routine (see notes on 0060H), and 0202H through 020FH contains the text “(c) ’80 Tandy” and a carriage return.

01FB-0201 – DELAY ROUTINE – “$DELAY”

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.65. Register A is used.

01FB
LD A,A
Top of the loop – Load A with A … just a waste of some cycles.
01FC
DEC BC
Decrement the counter in register pair BC
01FD-01FE
LD A,B
OR C
The easiest way to test a 2 byte register for zero is to load the MSB into A and then OR it with the LSB. If the MSB was 0 and the LSB was 0, then A will be 0.
01FF
Loop until the counter in register pair BC is equal to zero.
0201
RET
Return.

0202 – Message Storage Location

0202
(C) “80 Tandy” + 0DH
Copyright message storage area.

*0210 – Model 4 Gen 1 – These instructions are never called or used.

*0210-0211
LD E,3DH
I do not see that this command is ever executed as it is never called. However, it loads E with 3DH, most likely to toss off an error.

*0210 – Model 4 Gen 2 – These instructions are never called or used.

*0211
NOP
No Operation
*0211
NOP
No Operation

0212H – This continues a subroutine and was JUMPed to from 022CH. It zeroes A and all flags everything and RETURNs.
Difference between M1 and M3: In the Model I, routines to define cassette drive (0212H – 021DH), reset the cassette input port FFH (021EH – 022BH), and to blink the asterisk while reading a cassette (022CH – 0234H). In the Model III, a routine to insure compatibility with programs that define the cassette drive (XOR A followed by RET, located at 0212H & 0213H), a subroutine used by the routine that begins at 01D9H (0214H 0227H), a couple of cassette-related segments (0228H – 022DH), and an EI instruction followed by JP 1Al9H (enable interrupts and return to BASIC “READY”, located at 022EH – 0231H).

0212
XOR A
Set A to ZERO and clear all status flags.
0213
RET
Return.

0216-021A – Display a Carriage Return

0214
LD A,0DH
Load A with 0DH (ASCII: Carriage Return).
0216
Call the PRINT CHARACTER routine at 003B (which sends the character in the A register to the printer).
0219
XOR A
Set A to ZERO and clear all status flags.
021A
RET
Return.

021B-0227 – “$VDLINE” – Display a Line Until 03H or 0DH Reached.
This subroutine displays a line. The line must be terminated with an ASCII ETX (X’03’) or carriage return (X’0D’). If the terminator is a carriage return, it will be printed; if it is an ETX, it will not be printed. This allows VDLINE to position the cursor to the beginning of the next line or leave it at the position after the last text character. On entry (HL) shuold contain the output text, terminated by a 03H or a 0DH.

021B
“VIDLINE”
LD A,(HL)
Put the memory contents of (HL) into Register A.
021C
INC HL
Bump HL.
021D
CP 03H
Check those memory contents against 03H to see if it is the end of message.
021F
RET Z
If it was the end of message, RETURN.
0220
If it was NOT the end of message, call the PRINT CHARACTER routine at 0033 (which sends the character in the A register to the printer).
0223
CP 0DH
Check to see if it was a carriage return.
0225
If it was NOT a carriage return, loop back to load A with the next character.
0227
RET
If it WAS a carriage return, RETURN.

0228H – This continues a subroutine and was JUMPed to from 023DH. It puts 3000H into the memory location pointed to by the stack pointer, and JUMPs to 302AH.

0228
EX (SP),HL
Put HL (which presumably has a return address in it) into the memory location of the STACK pointer.
0229
JUMP to 302AH.

NOTE: 302AH is an entry in a jump vector table that JUMPs to 31F7H. 31F7H checks to see if we have a PRINT #.

022CH – BLINK ASTERISK routine – This routine is CALLED from 02E7 and alternatively displays and clears an asterisk in the upper right hand corner of the video display.

022C
JUMP to 0212H to zero the flags and RETURN.

022EH – Final cleanup before entering BASIC … Enable Interrupts and JUMP to READY prompt

022E
EI
Enable Interrupts.
022F
Show READY prompt by jumping to 1A19H.

*0232 – Model 1 Gen 1 – These instructions are never called or used.

*0232
CCF
*0233
INC A
*0234
RET

*0232 – Model 1 Gen 2 – These instructions are never called or used.

*0232
NOP
*0233
NOP
*0234
NOP

0235-0240 – CASSETTE ROUTINE – Read a Byte from Cassette – “CSIN”
After the completion of a $CSHIN call, this $CSIN routine begins inputting data, one byte at a time. This routine MUST be called often enough to keep up with the baud rate. There are no entry conditions. A is modified to hold the data byte.
Difference between M1 and M3: In the Model I, 0235H – 0240H contains the routine to read one byte from the cassette, and 0241H – 0260H contains the routine to get one bit from the cassette. In the Model III, 0235H – 023CH contain the start of the Model III routine to read one byte from cassette, 023DH – 0242H is part of the routine that begins at 0287H (writes cassette leader and sync byte), 0243H – 024CH is the actual start of the routine to search for the cassette leader and aync byte, 024DH – 0252H is the actual start of the routine to write a byte to tape, and 0253H – 025EH is a subroutine used by the system to select 500 or 1500 baud tape speed.

0235
“CSIN”
PUSH DE
Save the value in register pair DE on the STACK.
0236
PUSH BC
Save the value in register pair BC on the STACK.
0237
PUSH HL
Save the value in register pair HL on the STACK.
0238
LD HL,(420EH)
Put the TAPE READ VECTOR (stored at 420EH) into HL.
023B
EX (SP),HL
Replace the value at the top of the stack with the TAPE READ VECTOR (stored at 420EH), and what used to HL at the top of this routine into Register Pair HL.
023C
RET
Go to the TAPE READ VECTOR.
NOTE: When a routine is CALLed, the RETurn address is put at the top of the stack; so RET jumps to the value at the top of the STACK.

023D – This continues a subroutine and was JUMPed to from 028BH to to set the cassette write vector. It just PUSHes HL, puts 3000H into HL, and JUMPs out to 0228H.

023D
PUSH HL
Save the value in register pair HL on the STACK.
023E
Load HL with 3000H.
0241
JUMP back to 0228H.

0243-024B – CASSETTE ROUTINE – Read a Byte from Cassette

0243
DI
Disable interrupts.
0244
GOSUB to 300FH to start the cassette.
0247
PUSH HL
Save the value in register pair HL on the STACK.
0248-024A
HL = 3006H
024B
JUMP back to 0228H.

024D-0252 – CASSETTE ROUTINE – Write a Byte to Cassette

024D
PUSH HL
Save the value in register pair HL on the STACK.
024E
LD HL,(420CH)
Load HL with the memory contents of the TAPE WRITE VECTOR.
0251
EX (SP),HL
Put the TAPE WRITE VECTOR (stored at 420CH) into the memory location pointed to by the STACK, and the memory location pointed to the STACK into HL.
0252
RET
RETURN to the memory address held in the TAPE WRITE VECTOR.
NOTE: When a routine is CALLed, the RETurn address is put at the top of the stack; so RET jumps to the value at the top of the STACK.
0253
EX (SP),HL
Take the RETURN ADDRESS of whoever called this routine and put it into HL.
0254
LD A,(4211H)
Load A with the contents of memory location 4211H. Memory location 4211H is the Cassette Baud Rate Select. It will be Z for 500 baud, and NZ for 1500 baud.
0257
OR A
Set flags for A.
0258
If A was a zero, jump to 025DH to leave the routine with HL as is.
025A
INC HL
If A was not zero, bump HL 3 times to move to the fast vector.
025B
INC HL
(Bump 2).
025C
INC HL
(Bump 3).
025D
EX (SP),HL
Put HL as the RETURN ADDRESS and restore HL.
025E
RET
RETURN.
025F
POP BCC1
Clear out the STACK
0260
RETC9
RETurn to CALLer
0261
GOSUB to the very next instruction.

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

0261-0263–  ↳ TWOCSO
CALL 0264HCALL CASOUTCD 64 02
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)

0264 – “$CSOUT” – Output a byte to cassette.
After writing the header with $CSHWR, use this $CSOUT to write the data, one byte at a time. You MUST call $CSOUT often enough to keep up with the baud rate. Register A needs to hold the data byte on entry.
Difference between M1 and M3: In the Model I, 0264H – 0283H contains the routine to output one byte to the cassette. In the Model III, 0264H – 0266H contains a jump to 024DH (the start of the Model III routine to output one byte to cassette), followed by time data (60 seconds, 60 minutes, 24 hours) at 0266H – 0268H, followed by twelve bytes which contain the length of each of the twelve months (0264H – 0274H). This is followed by two NOPs, then starting at 0277H is a 1DH byte, a 1EH byte, the message “Diskette?”, and finally a 03H byte (at 0282H).

0264
JUMP to 024DH to write a byte to cassette.

0266 – Storage location for the maximum number of seconds in a minute, minutes in an hour, hours in a day, and days in a month.

0266
3C 3C 18
Time Data (60, 60, 24).
0269
1F 1C 1F 1E
Month Lengths.
026D
1F 1E 1F 1F
For DATE$.
0271
1E 1F 1E 1F
0275
00 00
Unused.
0277
1D
Group Separator.
0278
1E
Record Separator.
0279
“DISKETTE?” + 03H
Message Space.
0283
F2
UNUSED.

0284 – This subroutine is called by 2076H to turn the tape on, no header – it jumps out to 023DH.
Difference between M1 and M3: In the Model I, this area contains several cassette I/O routines, including turn on cassette, write leader and sync byte (0284H); write leader and sync byte (0287H); turn on cassette, search for leader and sync byte (0293H); search for leader and sync byte (0296H), put 2 asterisks in upper right corner of video ( part of previous routines, begins at 029FH). In the Model III, 0284H contains a JP 0287H instruction (faster than three NOPs), while 0287H is the start of the routine to turn on the cassette, write leader and sync byte. 028DH – 0292H contains the fast routine to check if BREAK is depressed. 0293H contains a JP 0243H instruction, while 0296H contains a JR 0243H (0243H is the actual start of the routine to turn on the cassette, search for leader and sync byte). 0298H – 02A0H is the machine language routine to turn on the built-in clock display (in the upper right hand corner of the video display), while 02A1H – 02A8H is the location of the corresponding routine to turn the clock display back off.

0284
JUMP to 0287H (the very next instruction anyway!), which was the the “$CSHWR” routine in the Model I ROM. That routine writes tape leader and the A5H sync byte. DE and HL are unchanged.

Load register B with the number of bytes to be written.

0287 – Write Leader and Sync Byte – “$CSHWR”
Each cassette record begins with a header consisting of a leader sequence and a synchronization byte. This $CSHWR routine turns on the cassette and writes out this header. There are no entry conditions. A is altered by this routine.

0287
“CSHWR”
DI
Disable Interrupts.
0288
GOSUB to 300FH to turn on the cassette.
028B
JUMP to 023DH to set the CASSETTE WRITE VECTOR.

028DH – “$KBBRK” -Check for a BREAK key only. This is a fast key scan routine which looks solely for the BREAK key. Use this routine if you want to minimize the keyboard scan time without totally locking out the keyboard. On exit NZ will be set if BREAK was set. This subroutine is called by 0444H (in the middle of the PRINTER ROUTINE) to check for a BREAK key.

028D
“KBBRK”
LD A,(3840H)
Check for BREAK Key. First, load A with the memory contents of 3840H (which is the keyboard scan of 14400, the 7th keyboard line), to check for a BREAK. 14400 is ENTER (01) CLEAR (02) BREAK (04) RIGHT ARROW (08) LINE FEED (16) LEFT ARROW (32) SPACE (64)
0290
AND 4
AND the memory contents of 3840H with 04H (Binary: 0000 0100) to isolate only Bit 3. This a precursor to a future test to see if it was a BREAK key.
0292
RET
RETURN.

0293 – CASSETTE ROUTINE – Read the Header and Sync Bytes

0293
JUMP to 0243H to read the cassette header.

0296 – CASSETTE ROUTINE – “CSHIN” – Search for Cassette Header and Sync Byte
Each cassette record begins with a header consisting of a leader sequence and synchronization byte. $CSHIN turns on the cassette drive and begins searching for this header information. The subroutine returns to the calling program after the sync-byte has been read. There are no entry conditions. Register A is altered by the routine.

0296
JUMP to 0243H to read the cassette header.

0298 – Enable the Clock Display – “CLKON”
No entry conditions. A is altered by this routine.

0298
“CLKON”
LD A,(4210H)
Put the contents of memory location 4210H into A.

NOTE: 4210H holds the bit mask for port ECH. Port ECH stores miscellaneous controls.
029B
SET 0,A
Set BIT 0 of A.
029D
LD (4210H),A
Put the modified Clock Bit (stored in A) into the memory location 4210H.

NOTE: 4210H holds the bit mask for port ECH. Port ECH stores miscellaneous controls.
02A0
RET
RETURN.

02A1 – Disable the Clock Display – “CLKOFF”
No entry conditions. A is altered by this routine.

02A1
“CLKOFF”
LD A,(4210H)
Put the clock bit stored in memory location 4210H into A.

NOTE: 4210H holds the bit mask for port ECH. Port ECH stores miscellaneous controls.
02A4
RES 0,A
Reset BIT 0 of A.
02A6
JUMP to 029DH.

*02A8 – Model 4 Gen 1 – These instructions are never called or used.

*02A8
RETC9
RETurn to CALLer

*02A8 – Model 4 Gen 2 – These instructions are never called or used.

*02A8
NOP

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

02A9-02AB–  ↳ ENBLK
CALL 0314HCALL CADRINCD 14 03
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
CALL 01F8HCALL CTOFFCD F8 01
Go turn off the cassette motor
02B2-02B4–  ↳ SYSTEM
CALL 41E2HCALL SYSOUTCD E2 41
Go call the DOS link at 41E2H.
In NEWDOS 2.1, this is called during a SYSTEM operation

*02B5-02B7 – Model 4 Gen 1 – Set the STACK Pointer.

*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

*02B5-02B7 – Model 4 Gen 2 – Set the STACK Pointer.

*02B5-02B7
LD SP,42E8HLD SP,BUFINI+25631 E8 42
Set the STACK pointer to 42E8H (which is the assumed load address). This location passes control to the routine used by the BASIC command SYSTEM
02B8-02BA
CALL 20FEHCALL CRDOCD FE 20
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
CALL 032AHCALL OUTDOCD 2A 03
Go display the character in Register A on the video display
02C0-02C2
CALL 1BB3HCALL QINLINCD B3 1B
We need a filename now, so go get the input from the keyboard

*02C3-02C5 – Deal with the BREAK Key.

*02C3-02C5
JP C,06CCHJP C,RESETRDA CC 06
ROM GEN 1 – If a BREAK key was hit (because the Carry flag is now on), go to the Level II BASIC READY routine
*02C3
JP C,006DHDA 6D 00
ROM GEN 2 – If a BREAK key was hit (because the Carry flag is now on), go to new routine at 006DH
02C6
RST 10HCHRGETD7
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
JP Z,1997HJP Z,SNERRCA 97 19
Display a ?SN ERROR if there wasn’t any input
02CA-02CB
CP 2FHLD A,”/”FE 2F
Check to see if the character at the location of the input buffer pointer in Register A is a / character
02CC-02CD
JR Z,031DHJR Z, GOOD28 4F
Jump to 031DH if the character at the location of the input buffer pointer in Register A is a /
02CE-02D0
CALL 0293HCALL CSRDONCALL CSRDONCD 93 02
Go turn on the cassette motor
02D1-02D3– LOPHD
CALL 0235HCALL CASINCD 35 02
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
JR NZ,02D1HJR NZ,LOPHD20 F9
Loop until the header byte is found
02D8-02D9
LD B,06H06 06
If were here, we got the header byte, so load Register B with the length of the filename to read from the cassette (which is 6 characters)
02DA–  ↳ CHKBYT
LD A,(HL)7E
Load Register A with the character at the location of the current input buffer pointer in Register Pair HL
02DB
OR AB7
Check to see if the character at the location of the current input buffer pointer in Register A is an end of input character
02DC-02DD
JR Z,02E7HJR Z,GETDT28 09
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
CALL 0235HCALL CASINCD 35 02
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
JR NZ,02D1HJR NZ,LOPHD20 EC
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
02E5-02E6
DJNZ 02DAHDJNZ CHKBYT10 F3
Loop until the whole of the filename has been read from the cassette and checked against the user response
02E7-02E9– GETDT
CALL 022CHCALL BCASINCD 2C 02
Call the BLINK ASTERISK routine at 022CH which alternatively displays and clears an asterisk in the upper right hand corner of the video display
02EA-02EC–  ↳ GETDT2
CALL 0235HCALL CASINCD 35 02
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
JR Z,02A9HJR Z,ENBLK28 B8
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
JR NZ,02EAHJR NZ,GETDT220 F5
Loop until either an execution address header byte or a file block header byte is read from the cassette
02F5-02F7
CALL 0235HCALL CASINCD 35 02
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
CALL 0314HCALL CADRINCD 14 03
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
CALL 0235HCALL CASINCD 35 02
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
DJNZ 02FEHDJNZ LDATIN10 F7
Loop until the whole file block has been read
0307-0309
CALL 0235HCALL CASINCD 35 02
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
JR Z,02E7HJR Z,GETDT28 DA
If its the same, jump to 02E7H because the next instructions are for bad checksums
030D-030E
LD A,43H3E 43
Load Register A with a C character
030F-0311
LD (3C3EH),A32 3E 3C
Display the C character in Register A on the video display (at 15422)
0312-0313
JR 02EAHJR GETDT218 D6
Jump to 02EAH and keep reading bytes

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

0314–  ↳ CADRIN
CALL 0235HCALL CASINCD 35 02
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
CALL 0235HCALL CASINCD 35 02
Calls the READ ONE BYTE FROM CASSETTE routine at 0235H (whichh reads one byte from the cassette drive specified in Register A, and returns the byte in Register A)
031B
LD H,A67
Load Register H with the byte read from the cassette in Register A (which is the MSB of the 16 bit value)
031C
RETC9
RETurn to CALLer

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

031D–  ↳ GODO
EX DE,HLEB
Load Register Pair DE with the pointer to the BASIC command line being processed (held in Register Pair HL)
031E-0320
LD HL,(40DFH)LD HL,(TEMP)2A DF 40
Load Register Pair HL with the execution address (which is stored at 40DFH).
Note: 40DFH-40E0H is also used by DOS
0321
EX DE,HLEB
So that we can run a RST 10H in the next instruction, we need to exchange the execution address in Register Pair HL with the input buffer pointer in Register Pair DE
0322
RST 10HCHRGETD7
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 NZ,1E5AHCALL NZ,LINGETC4 5A 1E
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
JR NZ,02B2HJR NZ,SYSTEM20 8A
Jump if it turns out there weren’t any digits (i.e., bad input) in the input
0328
EX DE,HLEB
Since there were digits (or else we would have jumped in the prior instruction), exchange the input buffer pointer in Register Pair HL with the execution address in Register Pair DE
0329
JP (HL)E9
Jump to the execution address (i.e. “/xxxx”) which is in Register Pair HL

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

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

This routine outputs a byte to device determined by byte stored at (409CH) – FFH=Tape, 0=Video, l=Printer. When calling, A = output byte. Uses AF. Warning: This routine CALLs a Disk BASIC link at address 41ClH which may have to be “plugged” with a RETurn (C9H) instruction.

032A–  ↳ OUTDO
PUSH BCC5
We are going to need to use Register C, so push Register Pair BC into the STACK
032B
LD C,A4F
Load Register C with the character to be output in Register A
032C-032E
CALL 41C1HCALL EXOUTCCD C1 41
Go call the DOS link at 41ClH.
In NEWDOS 2.1, this writes to the system output device
032F-0331
LD A,(409CH)LD A,(PRTFLG)3A 9C 40
Load Register A with the current output device number stored in 409CH.
Note: 409CH holds the current output device flag: -1=cassette, 0=video and 1=printer
0332
OR AB7
Since LD doesn’t set flags, in order to be able to test Register A using flags we need to execute an OR A first. This will enable us to set the flags according to the current output device number in Register A
0333
LD A,C79
Load Register A with the character to be output in Register C
0334
POP BCC1
Get the value from the STACK and put it in Register Pair BC
0335-0337
JP M,0264HJP M,CASOUTFA 64 02
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
JR NZ,039CHJR NZ,OUTLPT20 62
Jump to 039CH if the character in Register A is to be sent to the printer

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

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

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

To display special functions using a ROM call, load the A Register with the value given below for the special function and then CALL 033AH.

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

Save the value in Register Pair DE on the STACK
033B-033D
CALL 0033HCALL $DSPCD 33 00
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
CALL 0348HCALL DSPPOSCD 48 03
Go update the current cursor position and test to see if the display memory is full
0342-0344
LD (40A6H),ALD (TTYPOS),A32 A6 40
Save the current cursor line position stored in 40A6H to Register A.
Note: 40A6H holds the current cursor line position
0345
POP AFF1
Get the character from the STACK and put it in Register A
0346
POP DED1
Get the value from the STACK and put it in Register Pair DE
0347
RETC9
RETurn to CALLer

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

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

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

0358-035A–  ↳ ISCHAR
CALL 41C4HCALL EXINCCD C4 41
Go call the DOS link at 41C4H
035B
PUSH DED5
Since the next routine uses DE, save the value in Register Pair DE on the STACK
035C-035E
CALL 002BHCALL $KBDCD 2B 00
Call the SCAN KEYBOARD routine at 002BH
035F
POP DED1
Get the value from the STACK and put it in Register Pair DE
0360
RETC9
RETurn to CALLer

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

This is one of the general purpose input routines (see 5D9 and 1BB3 also). This routine inputs a string from the keyboard, up to a maximum of 240 characters (F0H), and echoes them to the screen. It puts this data into a buffer located at the address pointed to by the buffer pointer at 40A7H. (e.g. If 40A7H contains 5000H the data will be stored from 5000H onwards). The string is terminated with a zero byte. The program returns from this routine as soon as the ENTER key has been pressed. When it does so, HL contains the start address of the input string and B contains the length of the string. (RST 10H can be used to make HL point to the first character of the string, if required.).
Note: 40A7H-40A8H holds the input Buffer pointer.

0361–  ↳ INLIN
XOR AAF
Zero Register A to clear the buffered character
0362-0364
LD (4099H),ALD (CHARC),A32 99 40
Save the value in Register A as the last key pressed (which is stored in 4099H).
Note: 4099H holds the Last key pressed
0365-0367
LD (40A6H),ALD (TTYPOS),A32 A6 40
Save the value in Register A as the current cursor line position (which is stored in 40A6H).
Note: 40A6H holds the current cursor line position
0368-036A
CALL 41AFHCALL INLINECD AF 41
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
LD B,0F0H
Load register B with the length of the input buffer (which is 240).
0371-0373
CALL 05D9HCALL KEYINCD D9 05
“WAIT FOR NEXT LINE” keyboard input routine at 05D9H (which takes keyboard entry until a carriage return, a break, or buffer overrun occurs)
0374
PUSH AFF5
Save the flags on the STACK
0375
LD C,B48
Load Register C with the length of the input in Register B
0376-0377
LD B,00H06 00
Zero Register B so that Register Pair BC will have the length of the input
0378
ADD HL,BC09
Add the length of the input in Register Pair BC to the starting address of the input buffer in Register Pair HL
0379-037A
LD (HL),00H36 00
Save an end of the input character at the location of the end of input pointer in Register Pair HL
037B-037D
LD HL,(40A7H)LD HL,(BUFPNT)2A A7 40
Load Register Pair HL with the starting address of the input buffer (which is 40A7H).
Note: 40A7H-40A8H holds the input Buffer pointer
037E
POP AFF1
Get the flags from the STACK
037F
POP BCC1
Get the value from the STACK and put it in Register Pair BC
0380
DEC HL2B
Decrement the input buffer pointer in Register Pair HL (so that HL is the input area pointer – 1)
0381
RET CD8
Return if the BREAK key was pressed
0382
XOR AAF
Otherwise (i.e., the BREAK key was not pressed), zero all the status flags
0383
RETC9
RETurn to CALLer

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

Waits for keypress

0384-0386–  ↳ INCHR
CALL 0358HCALL ISCHARCD 58 03
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
JR 0384HJR INCHR18 F9
Loop until a key is pressed

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

038B–  ↳ FINLPT
XOR AAF
Zero Register A, which then means it contains the device code for VIDEO
038C-038E
LD (409CH),ALD (PRTFLG),A32 9C 40
Save the value in Register A (the current output device code of video) to 409CH.
Note: 409CH holds the current output device flag: -1=cassette, 0=video and 1=printer
038F-0391
LD A,(409BH)LD A,(LPTPOS)3A 9B 40
Load Register A with the current printer carriage position (which is stored at 409BH).
Note: 409BH holds the printer carriage position
0392
OR AB7
Set the flags for the carriage position in Register A
0393
RET ZC8
Return if the carriage position in Register A is equal to zero
0394-0395
LD A,0DHLD A,ENTER3E 0D
Load Register A with a “CARRIAGE RETURN
0396
PUSH DED5
Save the value in Register Pair DE on the STACK
0397-0399
CALL 039CHCALL OUTPUTCD 9C 03
Go send the carriage return character in Register A to the printer
039A
POP DED1
Get the value from the STACK and put it in Register Pair DE
039B
RETC9
RETurn to CALLer

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

This is the LPRINT routine. All registers are saved. The byte to be printed should be in the A register.

039C–  ↳ OUTLPT
PUSH AFF5
Save the value in Register Pair AF on the STACK
039D
PUSH DED5
Save the value in Register Pair DE on the STACK
039E
PUSH BCC5
Save the value in Register Pair BC on the STACK
039F
LD C,A4F
Load Register C with the character to be sent to the printer in Register A
03A0-03A1
LD E,00H1E 00
Zero Register E (which will ultimately hold the new character/line count of 0CH, 0DH, or 0AH)
03A2-03A3–  ↳ OUTDO
CP 0CHFE 0C
Check to see if the character to be sent to the printer in Register A is equal to 0CH (which is ‘skip to next line’)
03A4-03A5
JR Z,03B6HJR Z,LZRPOS28 10
Jump to 03B6H if the character to be sent to the printer in Register A is equal to 0CH
03A6-03A7
CP 0AHLD A,LINE FEEDFE 0A
Check to see if the character to be sent to the printer in Register A is a line feed character (i.e., 0AH)
03A8-03A9
JR NZ,03ADHJR NZ,LZRNOT20 03
Jump to 03ADH if the character to be sent to the printer in Register A isn’t a line feed character
03AA-03AB
LD A,0DHLD A,CARRIAGE RETURN3E 0D
Load Register A with a carriage return character (i.e., 0DH)
03AC
LD C,A4F
Load Register C with the character to be sent to printer in Register A
03AD-03AE–  ↳ LZRNOT
CP 0DHFE 0D
Check to see if the character to be sent to the printer in Register A is a carriage return character
03AF-03B0
JR Z,03B6HJR Z,LZRPOS28 05
Jump to 03B6H if the character to be sent to the printer in Register A is a carriage return character
03B1-03B3
LD A,(409BH)LD A,(LPTPOS)3A 9B 40
Load Register A with the current printer carriage position (stored in 409BH).
Note: 409BH holds the printer carriage position
03B4
INC A3C
Increment the current carriage position in Register A
03B5
LD E,A5F
Load Register E with the current carriage position in Register A
03B6–  ↳ LZRPOS
LD A,E7B
Load Register A with the current carriage position in Register E. Why do this since its obviously already done? Becasuse this is a jump point!
03B7-03B9
LD (409BH),ALD (LPTPOS),A32 9B 40
Save the current carriage position (which is stored in 409BH) in Register A.
Note: 409BH holds the printer carriage position
03BA
LD A,C79
Load Register A with the character to be sent to the printer in Register C
03BB-03BD
CALL 003BHCALL $PRTCD 3B 00
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-0451H – Model 4 Gen 1 PRINTER ROUTINE

In the Model III, 03C2H – 0451H is the line printer driver routine, 0452H – 0468H is the actual location of the routine to initialize all I/O drivers, 046BH – 0472H is a routine used by the RUN/EDIT/NEW commands to unprotect the video display and to load HL with the start of BASIC program pointer at 40A4H-40A5H, and 0473H-05D0H is the video driver routine and the keyboard driver begins at 3024H in the Model III).

*03C2
LD A,C
A = C (the current character).
*03C3
CP 20H
Check to see if the character is a control character by testing A – 20H. Results:
  • If A=20H it sets the ZERO FLAG
  • If A<20H then the CARRY FLAG will be set
  • if A>=20H then the NO CARRY FLAG will be set.
If A is a CONTROL CHARACTER then C will be set.
*03C5
If A was >= a SPACE, the NC would be set meaning A is a control character, jump to 03E5H to skip a bunch of needless tests.
*03C7
CP 0DH
Check to see if the character is a carriage return.
*03C9
If it is a carriage return, jump to 03F5H.

NOTE: 03F5H prints a character while maintaining page height and width.
*03CB
CP 0CH
Check to see if the character is a FORM FEED.
*03CD
JUMP to 03FFH if it is not a FORM FEED.
*03CF
LD A,(IX+03H)
If we are here then the character must be a printable one, so we need to get the number of lines left in the page and put them into B for a DJNZ countdown.
*03D2
SUB (IX+04H)
Subtract the number of lines printed from A.

NOTE: IX+4 is the number of lines printed.
*03D5
LD B,A
LET B = A
*03D6
GOSUB to 0440H to wait until the printer is ready (honoring BREAK, if hit).
*03D9
LD A,0AH
Put a LINE FEED character into A.
*03DB
OUT (0F8H),A
Output the LINE FEED character to port 0F8H.
NOTE: 0F8H is the printer port. If you put data to it, it prints it. Otherwise, Bits 4-7 hold printer status.
*03DD
JUMP back to 03D6H until the number of lines left in the page is zero.
*03DF
LD (IX+05H),00H
Load the memory location pointed to by IX+5 with a zero. NOTE: IX+5 is the number of characters printed.
*03E3
JUMP to 0439H to set the number of lines printed to 01 and exit

03E5H – Model 4 Gen 1 Inside the PRINTER ROUTINE – If we are here, the characters to be sent to the printer are NOT control characters, so test for graphics (and jump away), and if not, put the character from the PRINTER LOOKUP TABLE into C.

*03E5
CP 80H
Test for a graphics character by comparing A to 80H. If it is a graphics character than NC will be set.
*03E7
JUMP to 0419H to handle the graphics character.
*03E9
LD B,00H
Load B with a 00 (to set a MSB = 0).
*03EB
SUB 20H
Subtract 20H from A to adjust the character to the table.
*03ED
LD C,A
Load C with A. Now BC has the adjusted character value.
*03EE
Load HL with 3145H.

NOTE: 3145H is the PRINTER CHARACTER LOOKUP TABLE.
*03F1
ADD HL,BC
Add BC to HL so that HL will have the character location in the character table.
*03F2
LD C,(HL)
Load C with the character at the position of HL in the character table.
*03F3
JUMP to 0403H to continue.

03F5-0424 – Model 4 Gen 1 Inside the PRINTER ROUTINE – Print A Character Honoring Page Height and Width

*03F5
LD A,(IX+05H)
Load the A with the number of characters printed.

NOTE: IX+5 is the number of characters printed.
*03F8
OR A
Set the flags for A, including a test for zero/none.
*03F9
LD A,C
Put the character held in C into A.
*03FA
If there were ANY characters printed (so A is not zero), jump to 033FH.
*03FC
LD A,0AH
If there weren’t sny characters printed, the load A with 0AH.
*03FE
LD C,A
Load C with 0AH.
*03FF
CP 20H
Check to see if the character is a control character by testing A – 20H. If A=20H it sets the ZERO FLAG. If A<20H then the CARRY FLAG will be set and if A>=20H then the NO CARRY FLAG will be set. If A is a CONTROL CHARACTER then C will be set.
*0401
If it is a control character, jump to 0419H.

0403 – Model 4 Gen 1 Inside the PRINTER ROUTINE – If we are here, then C holds the printable character to be printed as determined by the PRINTER CHARACTER TABLE.

*0403
LD A,(IX+06H)
Load A with the MAXIMUM PRINT WIDTH.

NOTE: IX+06H holds the MAXIMUM PRINT WIDTH.
*0406
INC A
Bump A to one beyond that (i.e., unlimited).
*0407
If the maximum print width is unlimited, jump to 0419H.
*0409
CP (IX+05H)
Check to see if the line is full by comparing A with IX+5.

NOTE: IX+5 is the number of characters printed.
*040C
If the line is NOT full, jump to 0419H.
*040E
GOSUB to 0440H to wait until the printer is ready (honoring BREAK, if hit).
*0411
LD A,0DH
Load A with a carriage return.
*0413
OUT (0F8H),A
Send the carriage return to port 0F8H.

NOTE: 0F8H is the printer port. If you put data to it, it prints it. Otherwise, Bits 4-7 hold printer status.
*0415
LD (IX+05H),00H
Set the number of characters printed to zero.

NOTE: IX+5 is the number of characters printed.
*0419
GOSUB to 0440H to wait until the printer is ready (honoring BREAK, if hit).
*041C
LD A,C
Restore the character held in C back into A.
*041D
OUT (F8H),A
Send the character to port F8H.

NOTE: F8H is the printer port. If you put data to it, it prints it. Otherwise, Bits 4-7 hold printer status.
*041F
INC (IX+05H)
Bump the number of characters printed.

NOTE: IX+5 is the number of characters printed.
*0422
CP 0DH
Check A for a carriage return.
*0424
If A was a carriage return, skip the next few instructions and jump to 042AH.
*0426
CP 0AH
Check to see if the character in register A is 0AH (ASCII: LINE FEED character).
*0428
IF A is not a LINE FEED then jump to 043DH.

042A – Model 4 Gen 1 Inside the PRINTER ROUTINE – If we are here, then we have a LINE FEED or a CARRIAGE RETURN in A.

*042A
LD (IX+05H),00H
Reset the number of characters printed.

NOTE: IX+5 is the number of characters printed.
*042E
INC (IX+04H)
Bump IX+04.

NOTE: IX+4 is the number of lines printed.
*0431
LD A,(IX+04H)
Load A with the number of lines printed.
*0434
CP (IX+03H)
Compare that to the maximum number of lines per page.

NOTE: IX+3 is the maximum number of lines per page.
*0437
Skip the next instruction by JUMPing to 043DH if the number of lines printed is less than maximum number of lines per page.
*0439
LD (IX+04H),01H
We must be at top of page so set the number of lines printed to 01.

NOTE: IX+4 is the number of lines printed.
*043D
XOR A
Clear A and the status bits.
*043E
LD A,C
Load the character into A.
*043F
RET
RETURN.

0440-044A – Model 4 Gen 1 – Inside the PRINTER ROUTINE – Subroutine to wait for PRINTER READY, but Honor a BREAK Key

*0440
GOSUB to 044BH to check the printer.
*0443
RET Z
If it is ready (because Z is set) then RETURN.
*0444
if we are here, the printer is not ready. GOSUB to 028DH to check for a BREAK key being pressed.
*0447
Loop back to 0440H if BREAK wasn’t pressed.
*0449
POP AF
Restore AF from the STACK.
*044A
RET
RETURN.

03C2H-044AH – Model 4 Gen 2 PRINTER ROUTINE

*03C2
LD A,C
A = C (the current character).
*03C3
CP 20H
Check to see if the character is a control character by testing A – 20H. Results:
  • If A=20H it sets the ZERO FLAG
  • If A<20H then the CARRY FLAG will be set
  • if A>=20H then the NO CARRY FLAG will be set.
If A is a CONTROL CHARACTER then C will be set.
*03C5
If A was >= a SPACE, the NC would be set meaning A is a control character, jump to 03E9H to skip a bunch of needless tests.
*03C7
CP 0DH
Check to see if the character is a carriage return.
*03C9
JR Z,0414H28 49
If it is a carriage return, jump to 0414H.

NOTE: 0414H will process a carriage return.
*03CB
CP 0CH
Check to see if the character is a FORM FEED.
*03CD
If it is not a FORM FEED, JUMP to 041DH
*03CF
LD A,(IX+03H)DD 7E 03
If we are here then the character must be a printable one, so we need to get the number of lines left in the page and put them into B for a DJNZ countdown.
*03D2
SUB (IX+04H)DD 96 04
Subtract the number of lines printed from A.

NOTE: IX+4 is the number of lines printed.
*03D5
LD B,A47
Preserve Register A into Register B
*03D6
CALL 01DCHCD DC 01
GOSUB to 01DCH to wait for either PRINTER READY or BREAK key
*03D9
LD A,0AH3E 0A
Put a LINE FEED character into A.
*03DB
OUT (F8H),AD3 F8
Output the LINE FEED character to port 0F8H.
NOTE: 0F8H is the printer port. If you put data to it, it prints it. Otherwise, Bits 4-7 hold printer status.
*03DD
JUMP back to 03D6H until the number of lines left in the page is zero.
*03DF
LD (IX+05H),05HDD 36 05 00
Load the memory location pointed to by IX+5 with a 05H.
NOTE: IX+5 is the number of characters printed.
*03E3
LD (IX+04H),04HDD 36 04 01
Load the memory location pointed to by IX+4 with a 04H.
NOTE: IX+4 is the number of number of lines printed.
*03E7
JUMP to 0448H will will XOR A, Load C into A, and RETurn

03C2H-044AH – Model 4 Gen 2 PRINTER ROUTINE – Jumped here from 03C5 if the character in A was >= a SPACE

*03E9
LD A,(41FBH)3A FB 41
Put the character stored at (41FBH) into Register A
*03EC
OR AB7
Set the FLAGS based on Register A
*03ED
If that character was NULL, then skip the rest of this routine and pick up at 03F6H
*03EF
CP 01HFE 01
If the character in 41FBH was not a 00H, then let’s check to see if it was an 01H
*03F1
JP Z,3045HCA 45 30
If the character in 41FBH was an 01H, JUMP 3045H which simply JUMPs to 378DH to a new routine for Rom GEN 2 which processes printing when a 01H or Line Feed or Carriage Return is the current character being printed
*03F4
JR 041FH18 29
If the character in 41FBH was not a 00H or 01H then JUMP to 041FH to check for exceeding a printer line, advancing if needed, and sending the character to the printer

03F6 – Model 4 Gen 2 PRINTER ROUTINE – Jumped here from 03EDH if the character stored at 41FBH is a ZERO

*03F6
LD A,(41FCH)3A FC 41
Put the character stored at (41FCH) into Register A
*03F9
OR AB7
Set the FLAGS based on Register A
*03FA
If the character stored at (41FCH) was NOT 0, then JUMP to 040AH to check for SPECIAL CHARACTERS, CONTROL CHARACTERS, or TABs
*03FC
LD A,C79
Copy the character held in Register C into Register A for testing
*03FD
CP A0HFE A0
Check to see if the character which was held in Register A is LESS than A0H.
*03FF
If the character is LESS than A0H then the CARRY FLAG will be sent, so JUMP to 041FH to check for exceeding a printer line, advancing if needed, and sending the character to the printer
*0401
CP C0HFE C0
Check to see if the character is a control character by comparing A to C0H
*0403
If A >=C0H then the NO CARRY FLAG will be set and we have either a TAB or a SPECIAL CHARACTER.
*0405
ADD 40HC6 40
If we are here then the character is > A0H but < than C0H, so add 40H to it.
*0407
LD C,A4F
Store the adjusted character back into Register C
*0408
JR 041FH18 15
JUMP to 041FH to check for exceeding a printer line, advancing if needed, and sending the character to the printer

040A – Model 4 Gen 2 PRINTER ROUTINE – Jumped here from 03FAH if the character stored at 41FCH is NOT a ZERO

*040A
LD A,C79
Copy the character held in Register C into Register A for testing
*040B
CP C0HFE C0
Check to see if the character is a control character by comparing A to C0H
*040D
If the character held in Register C was < C0H then the CARRY FLAG will be set and we have a TAB or SPECIAL CHARACTER so JUMP 041FH to check for exceeding a printer line, advancing if needed, and sending the character to the printer
*040F
SUB 20HD6 20
Subtract 20H from the TAB or SPECIAL CHARACTER
*0411
LD C,A4F
Store the adjusted character back into Register C
*0412
JUMP to 041FH to check for exceeding a printer line, advancing if needed, and sending the character to the printer

0414 – Model 4 Gen 2 PRINTER ROUTINE – Jumped here from 03C9 if the character held in REGISTER C (the current character) is a CARRIAGE RETURN

*0414
LD A,(IX+05H)DD 7E 05
Load the Register A with the contents of (IX+5), which is the number of characters printed.
*0417
OR AB7
Set the FLAGS based on the contents of (IX+5)
*0418
If there were NO characters printed, then JUMP to 0434H to print a character and check to see if the line needs to be advanced
*041A
LD A,0AH3E 0A
If there were characters printed, then put a LINE FEED character into Register A
*041C
LD C,A4F
Store the LINE FEED character held in Register A into Register C (which tracks the current character)
*041D
JUMP to 0434H to print a character and check to see if the line needs to be advanced

041F – Model 4 Gen 2 PRINTER ROUTINE – Checks to see if we are at the end of a ilne, advances if needed, and sends the character to the printer

*041F
LD A,(IX+06H)DD 7E 06
Load A with the value stored at (IX+06H) which is the MAXIMUM PRINT WIDTH.
*0422
INC A3C
Bump Register A by 1
*0423
If the MAXIMUM PRINT WIDTH had been reached (meaning that it rolled to 0 when A was bumped), JUMP to 0434H to print a character and check to see if the line needs to be advanced
*0425
CP (IX+05H)DD BE 05
Check to see if the line is full by comparing A with (IX+05H) which holds the number of characters printed.
*0428
If the line is NOT full, then JUMP to 0434H to print a character and check to see if the line needs to be advanced
*042A
CALL 01DCHCD DC 01
GOSUB to 01DCH to wait for either PRINTER READY or BREAK key
*042D
LD A,0DH3E 0D
Load A with a carriage return.
*042F
OUT (F8H),AD3 F8
Send the carriage return to port 0F8H.
NOTE: 0F8H is the printer port. If you put data to it, it prints it. Otherwise, Bits 4-7 hold printer status.
*0431
CALL 3048HCD 48 30
GOSUB to 3048H which just JUMPs to 377AH to check to see if we are on a new printable page and set the pointers accordingly.
*0434
CALL 01DCHCD DC 01
GOSUB to 01DCH to wait for either PRINTER READY or BREAK key
*0437
LD A,C79
Put the character held in C back into A so it can be sent to the PRINTER
*0438
OUT (F8H),AD3 F8
Send the character to port F8H.
NOTE: F8H is the printer port. If you put data to it, it prints it. Otherwise, Bits 4-7 hold printer status.
*043A
INC (IX+05H)DD 34 05
Bump the number of characters printed.
NOTE: IX+5 is the number of characters printed.
*043D
CP 0DHFE 0D
Check A for a carriage return.
*043F
If A was a carriage return, skip the next few instructions and jump to 0445H to GOSUB to 3048H to JUMP to 377AH to check to see if we are on a new printable page and set the pointers accordingly.
*0441
CP 0AHFE 0A
Check to see if the character in register A is 0AH (ASCII: LINE FEED character).
*0443
IF A is not a LINE FEED then jump to 0448H to skip the next instruction.
*0445
CALL 3048HCD 48 30
GOSUB to 3048H which just JUMPs to 377AH to check to see if we are on a new printable page and set the pointers accordingly.
*0448
XOR AAF
Clear Register A and RESET all FLAGS
*0449
LD A,C79
Put the character held in Register C into Register A
*044A
RETC9
RETurn to CALLER

044B-0451 – Inside the PRINTER ROUTINE – Subroutine to check to see if PRINTER READY by polling port F8H

044B
IN A,(F8H)
Set A with the Printer Status Byte.

NOTE: F8H is the printer port. If Bit 7 is set, the printer is not busy. If Bit 6 is set the printer is not out of paper. If bit 5 is set, the device is selected. If Bit 4 is set, no printer fault.
044D
AND 0F0H
AND A against F0H (Binary: 11110000) to strip off BITS 3-0, leaving BITS 7-4 intact.
044F
CP 30H
Check the already masked A against 30H (Binary: 00110000) to see if the printer is ready.

NOTE: This translates to PRINTER NOT BUSY (Bit 7=0), PRINTER NOT OUT OF PAPER (Bit 6=0), PRINTER SELECTED (Bit 5=1), and NO PRINTER FAULT (Bit 4=1).
0451
RET
Return.

0452-0468 – Initialize KB, DI, PR, RI, RO and RN

0452
Initialize to Keyboard, Display Drive, and Printer …
0455
… by moving the 24 bytes starting at 36BFH …
0458
… to 4015H-402DH.
045B
LDIR
045D
Initialize RI, RO, and RN …
0460
… by moving the 24 bytes starting at 36F9H …
0463
… to 41E5H-41FDH.
0466
LDIR
0468
RET
RETURN.

*0469-046A – Model 4 Gen 1 – These instructions are never called or used.

*0469-046A
JUMP to 00DA to JUMP to display a ?SN ERROR.

*0469-046A – Model 4 Gen 2 – These instructions are never called or used.

*0469
NOP
*046A
NOP

046B-0472 – This subroutine zeroes out the PROTECTED SCREEN LINES (if any) and point HL to the start of data

046B
XOR A
Clear A and all Status Bits.
046C
LD (4214H),A
Set memory location 4214H to zero.
NOTE: 4214H is the number of protected video lines.
046F
LD HL,(40A4H)
Load HL with the memory contents of 40A4H.
NOTE: 40A4 is the DATA POINTER.
0472
RET
Return.

0473H-04B1H – Video Display DCB.

0473
DI
Disable Interrupts.
0474-0479
LD L,(IX+03H)
LD H,(IX+04H)
Load HL with the MSB and LSB of the current cursor position (Held in IX+3 and IX+4).
047A
LD A,(IX+05H)
Load A with the character at the current cursor position.

NOTE: IX+05H holds the character at the cursor position.
047D
OR A
Set flags. It will be Z if the cursor is off.
047E
Skip the next instruction (i.e., JUMP to 0481H) if the cursor is off.
0480
LD (HL),A
If we are here, the cursor is on so display the character held in A at the current cursor position held in HL.
0481
LD A,C
Load A with C (which should be the character to display).
0482
CP 20H
Check to see if the character is a control character by comparing A to 20H. Results:
  • If A=20H it sets the ZERO FLAG.
  • If A<20H then the CARRY FLAG will be set
  • If A>=20H then the NO CARRY FLAG will be set.
If A is a CONTROL CHARACTER then the CARRY FLAG will be set.
0484
If the CARRY FLAG is set (i.e., we have a control character), JUMP to 0521H.
0487
CP C0H
Check to see if the character is a control character by comparing A to 20H. Results:
  • If A=C0H it sets the ZERO FLAG.
  • If A<C0H then the CARRY FLAG will be set
  • If A>=C0H then the NO CARRY FLAG will be set.
If A is a TAB or SPECIAL CHARACTER then NC will be set.
0489
If the CARRY FLAG is NOT set (i.e., we have a TAB or SPECIAL CHARACTER), JUMP to 04B7H.

048B – Inside the CURSOR MANAGEMENT ROUTINE – If we are here, the character is not a control character, tab, or special characters.

048B
GOSUB to 0576H to display the character on the screen.
048E
LD A,H
Now we need to make sure the cursor is still on the screen. First Load A with H (which is the MSB of the screen location).
048F
AND 03H
Mask A against 03H (0000 0011), so that only the last 2 bits are live (so it can be only 0, 1, 2 or 3).
0491
OR 3CH
OR it against 3CH (0011 1100), so that it is 0011 11xx where xx are those 2 bits (so it can be only 60, 61, 62, or 63).
0493
LD H,A
Load H with the masked A.
0494
LD D,(HL)
Get the character at the cursor position (held in the memory location pointed to by HL) and put it in D.
0495
LD A,(IX+05H)
Load A with IX+5 to see if the cursor is on.

NOTE: IX+05H holds the character at the cursor position.
0498
OR A
Set flags for A.
0499
If the cursor is NOT on (A is Zero), then jump to 04A8H.
049B
LD (IX+05H),D
The cursor is on so put the character which is supposed to be there, there.

NOTE: IX+05H holds the character at the cursor position.
049E
LD A,(IX+06H)
Load A with the cursor character.

NOTE: IX+6 holds the cursor character.
04A1
CP 20H
Check to see if the character is a control character by testing A – 20H. If A=20H it sets the ZERO FLAG. If A<20H then the CARRY FLAG will be set and if A>=20H then the NO CARRY FLAG will be set. If A is a CONTROL CHARACTER then C will be set.
04A3
If it is not a control character, us it by jumping to 04A7H.
04A5
LD A,0B0H
If it is a control character, then load A with the default cursor of B0H.

NOTE: B0H is a two pixel wide graphic character located below the letter line.
04A7
LD (HL),A
Display the character held in A into the memory location pointed to be HL. This should display the cursor.
04A8
LD (IX+03H),L
Save the cursor position by loading IX+3 with L and …
04AB
LD (IX+04H),H
… by loading IX+4 with H.
04AE
XOR A
Zero A and clear all status flags.
04AF
LD A,C
Load A with the character.
04B0
EI
Enable Interrupts.
04B1
RET
RETURN.

04B2 – Cursor Management – Move to the start of the line.

04B2
LD A,L
Load register A with the LSB of the current position in register L.
04B3-04A3
AND C0H
Point to the beginning of the line by ANDing it against 1100 0000 to keep only Bits 6 and 7 (so it will be XX00H, XX40H, XX80H, or XXC0H).
04B5
LD L,A
Load register L with the updated value in register A.
04B6
RET
Return with the new video buffer address stored in HL.

04B7 – Cursor Management – We have EITHER a TAB or SPECIAL CHARACTER, so figure it out, and proceed accordingly.

04B7
LD A,(IX+07H)
Load A with IX+7 to check for TABS or SPECIAL CHARACTERS.
04BA
OR A
Set the Flags for A.
04BB
LD A,C
Put the current character into A.
04BC
If A is Not Zero, jump to 048BH to display the special character set.
04BE
SUB C0H
Subtract C0H (Binary: 1100 0000) to compute a TAB.
04C0
If TAB(0) then jump to 048EH.
04C2
LD B,A
Load B with the number of spaces needed.
04C3
LD A,20H
Load A with a SPACE.
04C5
GOSUB to 0576H to display the character on screen.
04C8
Loop back 2 Instructions until B is exhausted.
04CA
JUMP to 048EH.

04CC – Cursor Management – CURSOR ON.

04CC
LD A,(HL)
Store the character at the cursor into A.
04CD
LD (IX+05H),A
Put the character held in A at the cursor position.

NOTE: IX+05H holds the character at the cursor position.
04D0
RET
RETURN.

04D1 – Cursor Management – CURSOR OFF (Jumped to from 0539H)

04D1
XOR A
Zero A and all Flags.
04D2
JUMP to 04CDH to put the character held in A at the cursor position.

04D4 – Cursor Management – HOME CURSOR

04D4
Getting ready to HOME the cursor, so load HL with 3C00H.

NOTE: 3C00H is the start of the video display RAM.
04D7
LD A,(4210H)
Load A with the memory contents of 4210H.

NOTE: 4210H holds the bit mask for port ECH. Port ECH stores miscellaneous controls. In this case, we are looking for the bit which holds whether we are in LARGE characters or SMALL characters.
04DA
AND FBH
Mask A with FBH (Binary: 1111 1011) to turn off Bit 2.
04DC
GOSUB to 0570H Put A into memory location 4210H (4210H holds the bit mask for port ECH) and then output A to Port ECH.
04DF
LD A,(4214H)
Load A with the memory contents of 4214H.

NOTE: 4214H is the number of protected video lines.
04E2
AND 07H
AND A with 07H (Binary: 0000 0111) to keep only Bits 0, 1, and 2. This means that the only possibilities for A are 0-7.
04E4
RET Z
If A is ZERO (no lines to protect) then RETURN.
04E5
Since A is not ZERO, we have to protect some lines. First, GOSUB to 0504H to move the cursor down.
04E8
DEC A
Decrement A.
04E9
Loop back to 04E4H to either RETURN if we are at zero, or move down another line and try again.

04EB – Cursor Management – BACKSPACE

04EB
DEC HL
Decrement HL to back up the cursor.
04EC
LD A,(4210H)
Load A with the memory contents of 4210H.

NOTE: 4210H holds the bit mask for port ECH. Port ECH stores miscellaneous controls. In this case, we are looking for the bit which holds whether we are in LARGE characters or SMALL characters.
04EF
AND 04H
Mask A with 04H (0000 0100) to leave only bit 3 live, allowing Z to be set if Bit 3 is high, and NZ to be set if Bit 3 is low.
04F1
If it is Z is set, then we have small characters, so jump to skip the next instruction.
04F3
DEC HL
Decrement HL to back up the cursor another space.
04F4
LD (HL),20H
Put a space in the current cursor position.
04F6
RET
RETURN.

04F7 – Cursor Management – CURSOR BACK

04F7
LD A,(4210H)
Load A with the memory contents of 4210H.

NOTE: 4210H holds the bit mask for port ECH. Port ECH stores miscellaneous controls. In this case, we are looking for the bit which holds whether we are in LARGE characters or SMALL characters.
04FA
AND 04H
Mask A with 04H (0000 0100) to leave only bit 3 live, allowing Z to be set if Bit 3 is high, and NZ to be set if Bit 3 is low.
04FC
If A is not zero (which means A is 4), then GOSUB to the next instruction, which is a clever way to run that routine twice since we are in LARGE type.
04FF
LD A,L
Put the contents of L into A.
0500
AND 3FH
Mask A with 3F (0011 1111) to strip off Bits 6 and 7. A can now be no higher than 3F (Decimal: 63).
0502
DEC HL
Decrement HL to back up the cursor.
0503
RET NZ
RETURN if we are not at the start of the screen.

0504 – Cursor Management – CURSOR DOWN

0504
(If we are at the start of the screen) we need to move down one line, so load DE with 40H (64).
0507
ADD HL,DE
Add DE (64 characters) to HL (current cursor position).
0508
RET
RETURN.

0509 – Cursor Management – CURSOR FORWARD.

0509
INC HL
HL should be holding the current cursor position. Bump HL one forward.
050A
LD A,L
Load A with L to check the position in the line.
050B
AND 3FH
Mask A with 3F (0011 1111) to keep only Bits 0-5, so that it will be no higher than 3F/63.
050D
RET NZ
If A is not zero, then we are not at the end of the line, so RETURN.

050E – Cursor Management – CURSOR UP

050E
If we are here, then we are at the end of the line, so we need to move up one line. Start by putting FFC0H into DE.

NOTE: FFC0H is -64, or 1 line length.
0511
ADD HL,DE
Subtract 64 (the length a line on screen) from HL to move it to the previous line.
0512
RET
RETURN.

0513-0520 – Cursor Management – Turn on DOUBLE SIZE and put the cursor on EVEN columns only.

0513
LD A,(4210H)
Load A with the memory contents of 4210H.

NOTE: 4210H holds the bit mask for port ECH. Port ECH stores miscellaneous controls. In this case, we are looking for the bit which holds whether we are in LARGE characters or SMALL characters.
0516
OR 04H
OR A against 04 (0000 0100) to turn on the 3rd bit. This will turn on DOUBLE SIZE characters.
0518
GOSUB to 0570H to put A into memory location 4210H (4210H holds the bit mask for port ECH) and then output A to Port ECH.
051B
INC HL
Bump HL to move the cursor + 1.
051C
LD A,L
Load A with the LSB of the cursor position.
051D
AND FEH
Mask A (the LSB of the cursor position) with FE (1111 1110) which turns off BIT 0. This is to set to an even position.
051F
LD L,A
Put the newly “evened” A into L. This will then make HL only be on every other column.
0520
RET
RETURN.

0521-055F – Cursor Management – Process Special Characters

0521
Put 048EH into DE. This will eventually act as the RETURN location and is the routine that makes sure the cursor is still on the screen.
0524
PUSH DE
Put that RETurn address into the STACK.
0525
CP 08H
Compare A with 08H.

NOTE: 08H is a BACKSPACE.
0527
If A is BACKSPACE, jump to 04EBH to deal with it.
0529
CP 0AH
Compare A with 0AH.

NOTE: 0AH is a LINE FEED.
052B
If A is LINE FEED, jump to 05AFH to deal with it.
052E
CP 0DH
Compare A with 0DH.

NOTE: 0DH is a CARRIAGE RETURN.
0530
If A is CARRIAGE RETURN, jump to 05AFH to deal with it.
0533
CP 0EH
Compare A with 0EH.

NOTE: 0EH is a CURSOR ON.
0535
If A is CURSOR ON, jump to 04CCH to deal with it.
0537
CP 0FH
Compare A with 0FH.

NOTE: 0FH is a CURSOR OFF.
0539
If A is CURSOR OFF, jump to 04D1H to deal with it.
053B
SUB 15H
Subtract 15H (Decimal: 21) from A to bring it down into the control character range.
053D
If A is 0, jump to 0560H to deal with it.
053F
DEC A
Decrement A by 1. This would test for special and alternative characters.
0540
If A is 0, jump to 056BH to deal with it.
0542
DEC A
Decrement A by 1. This would test for DOUBLE SIZE.
0543
If A is DOUBLE SIZE, jump to 0513H to deal with it.
0545
DEC A
Decrement A by 1. This would test for CURSOR BACK.
0546
If A is CURSOR BACK, jump to 04F7H to deal with it.
0548
DEC A
Decrement A by 1. This would test for CURSOR FORWARD.
0549
If A is CURSOR FORWARD, jump to 0509H to deal with it.
054B
DEC A
Decrement A by 1. This would test for CURSOR DOWN.
054C
If A is CURSOR DOWN, jump to 0504H to deal with it.
054E
DEC A
Decrement A by 1. This would test for CURSOR UP.
054F
If A is CURSOR UP, jump to 050EH to deal with it.
0551
DEC A
Decrement A by 1. This would test for HOME CURSOR.
0552
If A is HOME CURSOR, jump to 04D4H to deal with it.
0555
DEC A
Decrement A by 1. This would test for RESTART LINE.
0556
If A is RESTART LINE, jump to 04B2H to deal with it.
0559
DEC A
Decrement A by 1. This would test for CLEAR TO END OF LINE.
055A
If A is CLEAR TO END OF LINE, jump to 05BCH to deal with it.
055C
DEC A
Decrement A by 1. This would test for CLEAR TO END OF SCREEN.
055D
If A is CLEAR TO END OF SCREEN, jump to 05C5H to deal with it.
055F
RET
RETURN (to 048EH to makes sure the cursor is still on the screen).

0560 – Cursor Management – Control Characters.

0560
LD A,(IX+07H)
Load A with the contents of IX+07H, which toggles TAB and ALTERNATIVE.
0563
AND 01H
MASK A with 0000 0001, to keep only the character flag bit.
0565
XOR 01H
XOR A with 0000 0001 to toggle the character flag bit.
0567
LD (IX+07H),A
Put the MASKED and XORed value back into IX+07H, which toggles TAB and ALTERNATIVE.
056A
RET
RETURN.

056B – Cursor Management – Special and Alternative Characters

056B
LD A,(4210H)
Put the contents of memory location 4210H into A.

NOTE: 4210H holds the bit mask for port ECH. Port ECH stores miscellaneous controls.
056E
XOR 08H
XOR A with 08H (0000 1000). This will toggle bit 3 to deal with special/alternative characters.

NOTE: Bit 3 of ECH is the SPECIAL CHARACTER SELECT. It will be 0 for KANA and 1 for MISC.
0570
LD (4210H),A
Put the toggled A back into memory location 4210H.

NOTE: 4210H holds the bit mask for port ECH. Port ECH stores miscellaneous controls.
0573
OUT (ECH),A
Output A to Port ECH.
0575
RET
RETURN.

0576 – This routine displays a character, moves forward either 1 or 2 spaces depending on if we are double size or not, and advances the screen if that character pushed the cursor beyond the end of the screen.

0576
LD (HL),A
Display the character on screen.

NOTE: HL should be the current screen location and A should be the character.
0577
INC HL
Bump HL to advance the cursor.
0578
LD A,(4210H)
Put the contents of memory location 4210H into A.

NOTE: 4210H holds the bit mask for port ECH. Port ECH stores miscellaneous controls.
057B
AND 04H
Mask A with 04H (0000 0100), so the only possibilties are 4 (0000 0100) or 0 (0000 0000).
057D
If it is zero, then we are SMALL SIZE, and JUMP to 0580H.
057F
INC HL
If we are here, then we are DOUBLE SIZE, so need to bump HL again to advance the cursor (even columns only).
0580
LD A,H
We need to test to see if we just fell off the screen, so load A with the MSB of the cursor location.
0581
CP 40H
Compare the MSB of the cursor location held in A against 40H (Binary: 0100 0000, Decimal: 64).
0583
RET NZ
If it is not zero then we did not fall off the screen, then RETURN.
0584
If we are here then we fell off the screen so first GOSUB to 050EH to move up a line.
0587
PUSH HL
Save the cursor location held in HL to the STACK.

0588 – Cursor Management – Scroll the Screen

0588
LD A,(4214H)
Load A with the memory contents of 4214H.

NOTE: 4214H is the number of protected video lines.
058B
AND 07H
AND A with 07H (0000 0111) to turn off all bits except for Bits 0, 1, and 2. This means that the only possibilities for A are 0-7.
058D
Load HL with the start of the screen.
0590
Load DE with the size of the screen (1024 characters).
0593
PUSH BC
Save the value in Register Pair BC to the STACK.
0594
Load BC with the number of characters per line (64 characters).
0597
INC A
Increase A which is holding the number of lines to protect.
0598
ADD HL,BC
Add BC (the number of characers per line) to HL (the current cursor position), which then moves us down one line.
0599
EX DE,HL
Swap DE and HL which will then reduce the screen size by one line.
059A
OR A
Set the flags for A.
059B
SBC HL,BC
Subtract, with carry, BC from HL.
059D
EX DE,HL
Swap DE and HL.
059E
DEC A
Reduce A by one, so that we have one less line to protect.
059F
Loop back to 0598H until we have finished this for all protectable lines.
05A1
PUSH DE
Save DE to the STACK.
05A2
PUSH HL
Save HL to the STACK.
05A3
OR A
Set the flags for A, as we prepare to move the start back up.
05A4
SBC HL,BC
Subtract, with carry, BC from HL to move up one line.
05A6
EX DE,HL
Swap DE and HL so that the source is now the start of screen plus one line.
05A7
POP HL
Restore HL from the STACK. HL will be the START OF SCREEN.
05A8
POP BC
Restore BC from the STACK. BC will be the COUNT = SCREEN SIZE – ONE LINE.
05A9
LDIR
Scroll the unprotected portions of the screen.
05AB
POP BC
Restore BC from the STACK (it was pushed in 0593H).
05AC
EX DE,HL
Swap DE and HL, so now HL = CURSOR POSITION.
05AD
JUMP to 05C6H to clear to the end of screen without changing HL.

05AF – Cursor Management – CARRIAGE RETURN or LINE FEED

05AF
GOSUB to 04B2H to move to the start of the line.
05B2
PUSH HL
Save HL (the cursor position) to the STACK.
05B3
GOSUB to 0504H to move the cursor down one line.
05B6
LD A,H
We need to test to see if we just fell off the screen, so load A with the MSB of the cursor location.
05B7
CP 40H
Compare the MSB of the cursor location held in A against 40H (64).
05B9
If we fell off the screen the JUMP to 0588H to scroll the screen.
05BB
POP DE
Otherwise restore DE from the STACK to get the old cursor position.

05BC – Cursor Management – CLEAR TO END OF LINE

05BC
PUSH HL
Save HL (contaning the NEW CURSOR POSITION) to the STACK.
05BD
LD D,H
DE currently holds the END OF LINE. Put the MSB into H.
05BE
LD A,L
Put the LSB into A.
05BF
OR 3FH
MASK the LSB of the END OF THE LINE with 3F (63).
05C1
LD E,A
Load E with the masked value of the END OF THE LINE.
05C2
INC DE
Bump DE by one so it now points to the start of the next line.
05C3
JUMP to 05C9H to clear to the end of the line.

05C5 – Cursor Management – CLEAR TO END OF SCREEN

05C5
PUSH HL
Save HL (containing the CURSOR POSITION) to the STACK.
05C6
Set DE to 4000H which is 1 character off the screen.
05C9
LD (HL),20H
Put a BLANK into the current cursor position.
05CB
INC HL
Bump the current cursor position by one.
05CC
RST 18H
We need to check to see if the integer value in HL is greater than or equal to DE (which is 1 character off the screen) so we call the COMPARE DE:HL routine, which numerically compares DE and HL. Will not work for signed integers (except positive ones). Uses the A-register only. The result of the comparison is returned in the status register as: CARRY SET=HL<DE; NO CARRY=HL>DE; NZ=Unequal; Z=Equal).
05CD
If that RST 18H call is not zero, then we are not off the screen, so loop back to 05C9H until we are done.
05CF
POP HL
We have cleared the screen so now restore the cursor position back to HL from the STACK.
05D0
RETC9
RETurn to CALLer

*05D1-05D8 – Model 4 Gen 1 Message Storage Area

*05D1-05D3
“RON”52 4F 4E
*05D4
AND F0HE6 F0
I don’t think this is used
*05D6
CP 30HFE 30
I don’t think this is used

*05D1-05D8 – Model 4 Gen 2 Message Storage Area

*05D1-05D8
0EH + “Cass ? + 03H”

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

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

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

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

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

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

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


According to the original ROM comments, on entry, HL to point to the input line address in RAM and Register B to hold the maximum number of input characters to fetch. On exit, Register A should hold the number of characters entered

05D9–  ↳ KEYIN
PUSH HLE5
Save the start of the input buffer area pointer in Register Pair HL on the STACK
05DA-05DB
LD A,0EH3E 0E
Load Register A with a turn on the cursor character (which is 14)
05DC-05DE
CALL 0033HCALL $DSPCD 33 00
Display a cursor by calling the DISPLAY A CHARACTER routine at 0033H (which puts the character in Register A on the video screen)
05DF
LD C,B48
Load Register C with the size of the input buffer in Register B
05E0-05E2–  ↳ KLNNXT
CALL 0049HCALL $KEYCD 49 00
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
JR NC,060CHJR NC,KLNCHR30 25
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
JP Z,0662HJP Z,KLNCRCA 62 06
Jump if the key that was pressed in Register A is a CARRIAGE RETURN
05EC-05ED
CP 1FHFE 1F
Check to see if the key that was pressed in Register A is the CLEAR key
05EE-05EF
JR Z,0619HJR Z,KLNCLR28 29
Jump if the key that was pressed in Register A is the CLEAR key
05F0-05F1
CP 01HFE 01
Check to see if the key that was pressed in Register A is the BREAK key
05F2-05F3
JR Z,0661HJR Z,KLNBRK28 6D
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
JR Z,0630HJR Z,KLNBSP28 34
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
JR Z,062BHJR Z,KLNCAN28 2B
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
JR Z,0646HJR Z,KLNHT28 42
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
JR Z,0641HJR Z,KLNETB28 39
Jump if the key that was pressed in Register A is a turn on the 32 character per line mode character
0608-0609
CP 0AHFE 0A
Check to see if the key that was pressed in Register A is a line feed character of CHR$(10)
060A
RET NZC0
Return (to 05E0H) if the key that was pressed in Register A isn’t a line feed character
060B
POP DED1
Get the return address from the STACK and put it in Register Pair DE (so that it isn’t 05E0H anymore)
060C–  ↳ KLNCHR
LD (HL),A77
We now know that the key pressed is a printable character so save the key that was pressed in Register A at the location of the input buffer pointer in Register Pair HL
060D
LD A,B78
Load Register A with the length of the buffer remaining in Register B
060E
OR AB7
Check to see if there is any more of the input buffer remaining (and set status)
060F-0610
JR Z,05E0HJR Z,KLNNXT28 CF
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
CALL 0033HCALL $DSPCD 33 00
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
JR 05E0HJR KLNNXT18 C7
Jump to 05E0H to get the next character

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

0619H-061B–  ↳ KLNCLR
CALL 01C9HCALL CLSCD C9 01
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
JUMP to 05E0H (to get the next character, which is now the first character in the buffer).

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

Cancel the accumulated line

0622-0624–  ↳ KLNCNL
CALL 0630HCALL KLNBSPCD 30 06
Gosub to wait for the next key and back up the input buffer pointer in Register Pair HL if necessary
0625
DEC HL2B
Backup to the previous character (the one before the CARRIAGE RETURN) by decrementing the input buffer pointer in Register Pair HL
0626
LD A,(HL)7E
Load Register A with the character at the location of the input buffer pointer in Register Pair HL
0627
INC HL23
Increment the input buffer pointer in Register Pair HL to the net availabile position
0628-0629
CP 0AHFE 0A
Check to see if the character in Register A is the line feed character of CHR$(10)
062A
RET ZC8
Return if the character in Register A is a line feed character
062B–  ↳ KLNCAN
LD A,B78
Now we know that character wasn’t a line feed, so we need to test for a buffer full. This loads Register A with the number of bytes remaining in the input buffer area in Register B
062C
CP CB9
Check to see if the number of characters remaining in the input buffer area in Register A is the same as the length of the input buffer area in Register C
062D-062E
JR NZ,0622HJR NZ,KLNCNL20 F3
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
CALL 0033HCALL $DSPCD 33 00
Effectuate the backspace by calling the DISPLAY A CHARACTER routine at 0033H (which puts the character in Register A on the video screen)
063F
INC B04
Increment the number of characters remaining in the input buffer area in Register B
0640
RETC9
RETurn to CALLer

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

Turn on 32 Character Mode

0641-0642–  ↳ KLNETB
LD A,17HLD A,0001 01113E 17
Load Register A with mask of 00010111 so as to turn on the 32 character per line mode character
0643-0645
JP 0033HJP $DSPC3 33 00
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
GOSUB to 0348H to get the cursor line position and return with it in register A.
0649-064A
AND 07H<ADD 0000 0111span class=”opcode2″>E6 07
Turn off some bits so we can mask the cursor line position in Register A by ANDing it against 0000 0111
064B
CPL2F
Inverse the value in Register A
064C
INC A3C
Increment the value in Register A so that it is 1 <= A <= 8
064D-064E
ADD 08HC6 08
Clear the upper bits of the counter
064F
LD E,A5F
Load Register E with the number of spaces to be added in Register A
0650–  ↳ KLNHTL
LD A,B78
Load Register A with the number of bytes remaining in the input buffer area in Register B
0651
OR AB7
Check to see if the buffer is full
0652
RET ZC8
Return if the input buffer is full
0653-0654
LD A,20HLD A,” “3E 20
Load Register A with a space character
0655
LD (HL),A77
Save the space character in Register A at the location of the input buffer area pointer in Register Pair HL
0656
INC HL23
Increment the input buffer area pointer in Register Pair HL
0657
PUSH DED5
Save the value in Register Pair DE on the STACK
0658-065A
CALL 0033HCALL $DSPCD 33 00
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
Loop back to 0650H until all the spaces have been added to the input buffer.

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

Process a Carriage Return and Automatic Line Feed

0661–  ↳ KLNBRK
SCF37
Set the Carry flag. This is done because the routine is going to exit with the CARRY flag set as an indication that BREAK was hit
0662–  ↳ KLNCR
PUSH AFF5
Save the value in Register Pair AF on the STACK, which saves the CARRY flag
0663-0664
LD A,0DH3E 0D
Load Register A with a carriage return character
0665
LD (HL),A77
Save the carriage return character in Register A at the location of the input buffer area pointer in Register Pair HL
0666-0668
CALL 0033HCALL $DSPCD 33 00
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
CALL 0033HCALL $DSPCD 33 00
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

0674 – KEYBOARD DRIVER ENTRY ROUTINE

0674
PUSH HL
Save register pair HL to the STACK.
0675
PUSH IX
Save Special Index Register IX to the STACK.
0677
PUSH DE
Save register pair DE (= the starting address of the device control block) to the STACK.
0678
POP IX
Get the starting address of the device control block from the STACK and put it in Special Index Register IX.
067A
PUSH DE
Save register pair DE (= the starting address of the device control block) to the STACK.
067B
Load register pair HL with a return address of 0694H (to restore all registers and RETurn).
067E
PUSH HL
Save the return address in register pair HL on the STACK.
067F
LD C,A
Save the character to output (current held in register A) to register C.
0680
LD A,(DE)
Load register A with the device type code at the location of the device control block pointer in register pair DE.
0681
BIT 7,A
Test Bit 7 of A, which is the bit for a DISK FILE.
0683
If not a disk file, then skip the next 3 instructions and JUMP to 068AH.
0685
AND B
Isolate the device code bits in A by AND’ing with the device codes in B.
0686
CP B
Check to see if the updated device type code in register A is the same as the driver entry code in register B.
0687
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.
068A
AND B
Masking A against B also sets Z for WRITE.
068B
CP 02H
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.
068D
LD L,(IX+01H)
Load register L with the LSB of the driver entry address at the location of the device control block pointer in Special Index Register IX plus one.
0690
LD H,(IX+02H)
Load register H with the MSB of the driver entry address at the location of the device control block pointer in Special Index Register IX plus one.
0693
JP (HL)
JUMP to the driver entry address in register pair HL.
0694
POP DE
Get the value from the STACK and put it in register pair DE.
0695
POP IX
Get the value from the STACK and put it in Special Index Register IX.
0697
POP HL
Get the value from the STACK and put it in register pair HL.
0698
POP BC
Get the value from the STACK and put it in register pair BC.
0699
RET
Return.

069AH – This subroutine CLEARS the DATA FLAG, sets up a buffer of 255 bytes (held in D) and JUMPS to 2B8DH.

069A
XOR A
Clear A and all status flags.
069B
LD (409FH),A
Load the DATA FLAG in 409FH with 0 (since A was just XOR’d against itself).
069E
LD D,FFH
Load D with 255, which will represent a buffer of 255 bytes.
06A0
JUMP to 2B8DH which is in the middle of the TOKENize routine. This address loads A with the current character at the BASIC line pointer, tests for end of line, puts it into the memory location pointed to by BC, and exits.
06A3
AND FDH
Mask A with FDH (1111 1101) to turn off Bit 1.
06A5
LD (409FH),A
Put A into the DATA FLAG held in 409FH. Note: Bit 0 HIGH means inside a quote. Bit 1 HIGH means inside a DATA. Bit 2 HIGH means inside a REM.
06A8
LD A,3AH
Load A with 3AH (which is a :).
06AA
OR A
Set the flags, to start a check for a reserved word.
06AB
JUMP to 06E2H (to exit this routine back to 2B89H) if it is not a reserved word.
06AE
LD A,(409FH)
Load A with the DATA FLAG in 409FH. This is to see if we are currently in a quote string.
06B1
RRA
Rotate A right one bit, with the bit that falls off (BIT 0) being moved to the CARRY FLAG, and the CARRY FLAG is moved to BIT 7.
06B2
If CARRY is set then we are in quoted string so JUMP to 06E2H which then JUMPs to 2B89H which is in the middle of the TOKENize routing. This address bumps BC (the input buffer pointer), reduces D (the buffer counter), moves the BASIC line pointer forward, and continues.
06B4
RRA
Rotate A right one bit, with the bit that falls off (BIT 0) being moved to the CARRY FLAG, and the CARRY FLAG is moved to BIT 7.
06B5
RRA
Rotate A right one bit, with the bit that falls off (BIT 0) being moved to the CARRY FLAG, and the CARRY FLAG is moved to BIT 7. This tests for a REM.
06B6
If C is set, we are in a REM, so JUMP to 06F6H if we are NOT in a REM.
06B8
LD A,(HL)
At this point, we assume this is a TOKEN. So load A with the contents of HL to get the token.
06B9
CP FBH
Check A against FBH (Binary: 1111 1011) to see if it is a REM TOKEN.
06BB
PUSH HL
Save HL (Position in Text) to the STACK.
06BC
PUSH BC
Save BC (Position in Buffer) to the STACK.
06BD
Set HL to 06DFH, which will act as a RETURN.
06C0
PUSH HL
Save HL (the return) to the STACK.
06C1
RET NZ
RETURN if this is NOT a REM TOKEN.

The next set of instructions tests the buffer backwards for M, E, and R, and RETURNS out if those are not found.

06C2
DEC BC
Decrement the Buffer to back up one character.
06C3
LD A,(BC)
Put the character in the buffer into A.
06C4
CP 4DH
Test A for a 4DH (ASCII: M).
06C6
RET NZ
If it is not a M then RETURN.
06C7
DEC BC
Decrement the Buffer to back up one character.
06C8
LD A,(BC)
Put the character in the buffer into A.
06C9
CP 45H
Test A for a E.
06CB
RET NZ
If it is not a E then RETURN.
06CC
DEC BC
Decrement the Buffer to back up one character.
06CD
LD A,(BC)
Put the character in the buffer into A.
06CE
CP 52H
Test A for a R.
06D0
RET NZ
If it is not a R then RETURN.

At this point BC, BC+1, and BC+2 were REM, so check backwards again for a : and if not, RETURN.

06D1
DEC BC
Decrement the Buffer to back up one character.
06D2
LD A,(BC)
Put the character in the buffer into A.
06D3
CP 3AH
Test A for a :.
06D5
RET NZ
If it is not a : then RETURN.

At this point BC, BC+1, BC+2, and BC+3 were :REM.

06D6
POP AF
Restore AF from the STACK (to clear the STACK).
06D7
POP AF
Restore AF from the STACK (to clear the STACK).
06D8
POP HL
Restore HL from the STACK (to get the position).
06D9
INC D
INC D
INC D
INC D
We need to decrease the buffer size by 4.
06DD
JUMP to 0704H to load the next character held in (HL) into A and JUMP to 2BA0H to see if the current token is ELSE and then keep processing.
06DF
POP BC
Restore BC (the buffer position) from the STACK.
06E0
POP HL
Restore HL (the text position) from the STACK.
06E1
LD A,(HL)
Put the character at the current text position into A.
06E2
JUMP to 2B89H which is in the middle of the TOKENize routing. This address bumps BC (the input buffer pointer), reduces D (the buffer counter), moves the BASIC line pointer forward, and continues.

06E5H – This subroutine sets the DATA FLAG to “BIT 1 HIGH” to indicate that we are in a DATA command.

06E5
LD A,(409FH)
Load A with the DATA FLAG in 409FH.
06E8
OR 02H
OR A against 02H (0000 0010) to set BIT 1, the DATA bit.
06EA
LD (409FH),A
Put the revised A into the DATA FLAG.
06ED
XOR A
Clear A and all flags.
06EE
RET
RETURN.

06EFH – This subroutine sets the DATA FLAG to “BIT 2 HIGH” to indicate that we are in a REM command.

06EF
LD A,(409FH)
Load A with the DATA FLAG in 409FH.
06F2
OR 04H
OR A against 04H (Binary: 0000 0100) to turn on Bit 2, the REM bit.
06F4
RETURN.
06F6
RLA
Rotate A left one bit, with the bit that falls off (BIT 7) being moved to the CARRY FLAG, and the CARRY FLAG is moved to BIT 0. If this results in the CARRY FLAG being set, then we are in a DATA statement.
06F7
If A had Bit 7 high (which was rotated into CARRY for thsi test), then we are in a DATA statement, JUMP to 06E2H which then JUMPs to 2B89H which is in the middle of the TOKENize routing. This address bumps BC (the input buffer pointer), reduces D (the buffer counter), moves the BASIC line pointer forward, and continues.
06F9
LD A,(HL)
Load the next character into A.
06FA
CP 88H
Compare A to 88H to see if it is DATA.
06FC
If it is DATA then GOSUB to 06E5H to set the flag accordingly.
06FF
CP 93H
Compare A to 93H to see if it is REM.
0701
If it is REM then GOSUB to 06EFH to set the DATA FLAG to indicate that we are inside a REM.
0704
LD A,(HL)
Load the next character into A.
0705
JUMP to 2BA0H to see if the current token is ELSE and then keep processing.

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

Single-precision addition (ACCumulator=(HL)+ACC) involving a buffer pointed to by the HL Register Pair and ACCumulator (i.e., 4121H-4122H). This part of the program loads the BCDE registers with the value from the buffer, then passes control to 716H.

0708-070A–  ↳ FADDH21 80 13
LD HL,1380HLD HL,FHALF
Load Register Pair HL address of the single precision value 1/2, which is stored in ROM at 1380H. This would be applicable if the entry jump was to this address (FADDH). If the entry is to FADDS, then this wouldn’t occur.
070B-070D–  ↳ FADDS
CALL 09C2HCALL MOVRMCD C2 09
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
JR 0716HJR FADD18 06
Actually do the addition via a JUMP to the SINGLE PRECISION ADD routine at 0716H (which adds the single precision value in (BC/DE) to the single precision value in the ACCumulator (i.e., 4121H-4122H). The sum is left in the ACCumulator

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

Single-precision subtraction (ACC=(HL)-ACC). This loads the BCDE registers with the value from (HL), then passes control to 713H.

0710-0712–  ↳ FSUBS
CALL 09C2HCALL MOVRMCD C2 09
Load a SINGLE PRECISION value pointed to by Register Pair HL into Register Pairs BC and DE via a GOSUB to MOVRM

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

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

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

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

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

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

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

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

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

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

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

Formula: FAC:=ARG+FAC

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

If INTFSF=1 the format of floating point numbers will be:

  • Reg B – SIGN AND BITS 1-7 OF EXPONENT
  • Reg C – Bit 8 of exponent ;and bits 2-8 of mantissa
  • Reg D – Bits 9-16 of mantissa
  • Reg E – Bits 17-24 of mantissa, and likewise for the ACCumulator format
  • Note: The exponent for intel will be 7FH
0716–  ↳ FADD
LD A,B78
First we need to check to see if the first argument is zero, so we load Register A with the exponent of the single precision value in Register B
0717
OR AB7
Set the flags based on Register B to check to see if the single precision value in Register Pairs BC and DE is equal to zero
0718
RET ZC8
Return if the single precision value in Register Pairs BC and DE is equal to zero because the result is already in the ACCumulator.
0719-071B
LD A,(4124H)LD A,(FAC)3A 24 41
Next, we want to test to see if the exponent is zero, because if it is, then the answer is already in the registers. First, load Register A with the exponent of the single precision value in the ACCumulator (i.e., 4121H-4122H)
071C
OR AB7
Set the flags based on the exponent (now in A) is equal to zero
071D-071F
JP Z,09B4HJP Z,MOVFRCA B4 09
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
JR NC,072FHJR NC,FADD130 0C
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 09A4HCALL PUSHFCD A4 09
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 09B4HCALL MOVFRCD B4 09
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
CALL 09DFHCALL UNPACKCD DF 09
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

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.

0738-073A
CALL 07D7HCALL SHIFTRCD D7 07
Shift the single precision value in Register Pairs BC and DE until it lines up with the single precision value in 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
JP P,0754HJP P,FADD3F2 54 07
If the signs were differet, then we move to subtractions
0742-0744
CALL 07B7HCALL FADDACD B7 07
Otherwise, we add the single precision value in BCDE to the single precision value in the ACCumulator
0745-0747
JP NC,0796HJP NC,ROUNDD2 96 07
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
JP Z,07B2HJP Z,OVERRCA B2 07
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
CALL 07EBHCALL SHRADDCD EB 07
Go shift the single precision result in Register Pairs BC and DE
0752-0753
JR 0796HJR ROUND18 42
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.

0754–  ↳ FADD3
XOR AAF
Zero Register A to negate the unflow byte and subtract the numbers.
0755
SUB B90
Subtract the 8-bit value in Register B from the value in Register A
0756
LD B,A47
Save the result into Register A
0757
LD A,(HL)7E
Prepare to subtract the low order numbers. First, load Register A with the value at the memory pointer in Register Pair HL
0758
SBC A,E9B
Subtract the value in Register E from the value in Register A
0759
LD E,A5F
Load Register E with the result in Register A
075A
INC HL23
Increment the memory pointer in Register Pair HL to point to the next byte (the middle order numbers) to deal with
075B
LD A,(HL)7E
Prepare to subtract the middle order numbers. First, load Register A with the value at the location of the memory pointer in Register Pair HL
075C
SBC A,D9A
Subtract the value in Register D from the value in Register A
075D
LD D,A57
Load Register D with the result in Register A
075E
INC HL23
Increment the memory pointer in Register Pair HL to point to the next byte (the high order numbers) to deal with
075F
LD A,(HL)7E
Load Register A with the value at the location of the memory pointer in Register Pair HL
0760
SBC A,C99
Subtract the value in Register C from the value in Register A
0761
LD C,A4F
Load Register C with the result in Register A

With that out the way, we need to make sure we have a positive mantissa (or else we will need to negate the number).

0762-0764–  ↳ FADFLT
CALL C,07C3HCALL C,NEGRDC C3 07
If the Carry flag is set (which is to indicate that the number was negative), go convert the single precision value to a positive number

This next routine normalizes CDEB. In doing so, ABCDE and HL are all modified. This routine shifts the mantissa left until the MSB is a 1.

0765–  ↳ NORMAL
LD L,B68
Put the lowest two bytes into (HL)
0766
LD H,E63
Load Register H with the LSB of the single precision value in Register E
0767
XOR AAF
Zero Register A so that Register B can track the shift count.
0768–  ↳ NORM1
LD B,A47
Save the shift count from Register A into Register B.
0769
LD A,C79
Load Register A with the MSB of the single precision value in Register C
076A
OR AB7
Check to see if the value in Register A is equal to zero
076B-076C
JR NZ,0785HJR NZ,NORM320 18
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
JR NZ,0768HJR NZ,NORM120 F0
If we did not shift 4 bytes of ZERO’es, shift 8 more via a loop until shift is completed

This routine will ZERO out the ACCumulator, changing only Register A in the process. A will exit as 0.

0778–  ↳ ZERO
XOR AAF
Zero Register A
0779-077B–  ↳ ZERO0
LD (4124H),ALD (FAC),A32 24 41
Make the ACCUmulator’s exponent = whatever is in A. If entered from above, then it will be 0. This is done because Level II treats a number as zero if its exponent is zero.
077C
RETC9
Return with a single precision value of zero in the ACCumulator

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

077D–  ↳ NORM2
DEC B05
Decrement the shift count (exponent counter) in Register B
077E
ADD HL,HL29
Rotate (HL) left by 1 and shift in a 0
077F
LD A,D7A
Rotate the next higher order (NMSB) left 1 as well.
0780
RLA17
Shift the NMSB in Register A left one bit and shift a bit from Register Pair HL if necessary
0781
LD D,A57
Save the adjusted NMSB in Register A into Register D
0782
LD A,C79
Load Register A with the MSB in Register C
0783
ADC A,A8F
Shift the MSB in Register A left one bit and shift a bit from Register D if necessary. The flags will get set as well.
0784
LD C,A4F
Load Register C with the adjusted value in Register A
0785-0787–  ↳ NORM3
JP P,077DHJP P,NORM2F2 7D 07
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
JR Z,0796HJR Z,ROUND28 08
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
JR NC,0778HR NC,ZERO30 E3
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.

Vernon 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

0796–  ↳ ROUND
LD A,B78
Load Register A with the LSB of the single precision value in Register B
0797-0799–  ↳ ROUNDB
LD HL,4124HLD HL,FAC21 24 41
Load Register Pair HL with the address of the exponent in the ACCumulator. Note: The FDIV ROM Routine enters the routine here at ROUNDB with Register A set differently.
079A
OR AB7
Set the status flags to enable us to see if we need if we need to round up. If the M FLAG is set (the most significant bit of the value in Register A), we will need to round up.
079B-079D
CALL M,07A8HCALL M,ROUNDAFC A8 07
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
JP 09B4HJP MOVFRC3 B4 09
Save the number into the ACCumulator via a JUMP to 09B4H which moves the SINGLE PRECISION value in BC/DE into ACCumulator

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

This is a subroutine within the ROUND round. This will add one to C/D/E.

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

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

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

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

This routine adds (HL+2),(HL+1),(HL+0) to C,D,E. This is called by FADD and FOUT.

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

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

This routine negates the number in C/D/E/B. CALLd by FADD and QUINT. Alters everything except Register H.

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

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

This routine will shift the number in C/D/E right the number of times held in Register A. The general idea is to shift right 8 places as many times as is possible within the number of times in A, and then jump out to shift single bits once you can’t shift 8 at a time anymore. Alters everything except Register H.

07D7-07D8–  ↳ SHIFTR
LD B,00H06 00
Load Register B, which will hold the overflow byte, with zero to reset the overflow byte
07D9-07DA–  ↳ SHFTR1
SUB 08HD6 08
Top of a loop. For speed, first check to see if the shift counter in Register A still indicates at least 8 bits have to be shifted right
07DB-07DC
JR C,07E4HJR C,SHFTR238 07
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
JR 07D9HJR SHFTR118 F5
Loop back to see if we can keep shifting

07E4 – SINGLE PRECISION MATH ROUTINE – “SHFTR2”

This routine will shift the number in C/D/E right the number of times held in Register A, but one byte at a time.

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

07F8-07FB – SINGLE PRECISION CONSTANT STORAGE LOCATION

07F8-07FB
00 00 00 81
A single precision constant equal to 1.0 is stored here.

07FC-0808 – SINGLE PRECISION CONSTANTS STORAGE LOCATION2

07FC
03
The number of single precision constants which follows is stored here.
07FD-0800
AA 56 19 80
A single precision constant equal to .598978 is stored here.
0801-0804
F1 22 76 80
A single precision constant equal to .961471 is stored here.
0805-0808
45 AA 38 82
A single precision constant equal to 2.88539 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 ROM’s LOG function produces a positive value. e.g., 10 PRINT LOG(.99999994)

0809-080B–  ↳ LOG
CALL 0955HCALL SIGNCD 55 09
Go check the sign (or zero value) of the single precision value in the ACCumulator
080C
OR AB7
Set the flags.
080D-080F
JP PE,1E4AHJP PE,FCERREA 4A 1E
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
CALL 0716HCALL FADDCD 16 07
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
CALL 08A2HCALL FDIVCD A2 08
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
CALL 0710HCALL FSUBSCD 10 07
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
CALL 149AHCALL POLYXCD 9A 14
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
CALL 0716HCALL FADDCD 16 07
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
CALL 0F89HCALL FINLOGCD 89 0F
Go convert the value in Register A to a single precision number and add it to the x-value in the ACCumulator. Return with the result in the ACCumulator

The instructions are commented in the original ROM source code as: Get LN(2)

0841-0843–  ↳ MULLN2
LD BC,8031H01 31 80
Load Register Pair BC with the exponent and the MSB of a single precision constant
0844-0846
LD DE,7218H11 18 72
Load Register Pair DE with the NMSB and the LSB of a single precision constant. Register Pairs BC and DE are now equal to a single precision value of 0.693147

The original ROM source code had a jump to the muptlication routine; but to save bytes, the ROM was restructured to just fall into the MULTiplocation routine instead.


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

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

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

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

This routine alters every Register.

0847-0849–  ↳ FMULT
CALL 0955HCALL SIGNCD 55 09
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
CALL 0914HCALL MULDIVCD 14 09
Next we need to fix up the exponents and save the numbers in the registers for faster addition.
0850
LD A,C79
Load Register A with the single precision value’s High Order/MSB in Register C
0851-0853
LD (414FH),ALD (FMLTT1),A32 4F 41
Save the MSB of the single precision value in Register A at memory location 414FH
0854
EX DE,HLEB
Load Register Pair HL with the NMSB and the LSB of the single precision value in Register Pair DE
0855-0857
LD (4150H),HLLD (FMLTT2),HL22 50 41
Save the NMSB and the LSB of the single precision value in Register Pair HL at memory locations 4150H and 4151H
0858-085A
LD BC,0000H01 00 00
Load Register Pair BC with a zero, which we will also put into Register D and Register E
085B
LD D,B50
Load Register D with the value in Register B
085C
LD E,B58
Load Register E with the value in Register B
085D-085F
LD HL,0765HLD HL,NORMAL21 65 07
Load Register Pair HL with the return address
0860
PUSH HLE5
Save the return address in Register Pair HL on the STACK
0861-0863
LD HL,0869HLD HL,FMULT221 69 08
Load Register Pair HL with the return address
0864
PUSH HLE5
Save the return address in Register Pair HL on the STACK
0865
PUSH HLE5
Save the return address in Register Pair HL on the STACK
0866-0868
LD HL,4121HLD HL,FACLO21 21 41
Load Register Pair HL with the low order/LSB address of the single precision value in the ACCumulator
0869–  ↳ FMULT2
LD A,(HL)7E
Load Register A with the byte to multiply by (on entry its the LSB of the single precision value in the ACCumulator)
086A
INC HL23
Increment the memory pointer in Register Pair HL to point to the next byte of the number in the ACCumulator
086B
OR AB7
Check to see if the LSB of the single precision value in the ACCumulator in Register A is equal to zero
086C-086D
JR Z,0892HJR Z,FMULT328 24
Jump if the LSB of the single precision value in the ACCumulator is equal to zero
086E
PUSH HLE5
Save the memory pointer to the number in the ACCumulator (tracked by Register HL) on the STACK
086F-0870
LD L,08H2E 08
Load Register L with the bit shift counter

The original source code explains what is being done next. The product will be formed in C/D/E/B. This will be in C/H/L/B part of the time in order to use the “DAD” instruction. At FMULT2, we get the next byte of the mantissa in the ACCumulator to multiply by, which is tracked by HL and unchanged by FMULT2. If the byte is zero, we just shift the product 8 bits to the right. This byte is then shifted right and saved in Register D. The CARRY FLAG determines if we should add in the second factor, and, if we do, we add it to C/H/L. Register B is only used to determine which way we round. We then shift C/H/L/B right one to get ready for the next time through the loop. Note: The CARRY is shifted into the MSB of Register C. Register E has the count to determine when we have looked at all the bits of Register D.

0871–  ↳ FMULT4
RRA1F
Shift the LSB of the single precision value in the ACCumulator in Register A one place to the right. RRA rotates the contents of Register A right one bit position, with Bit 0 going to the CARRY FLAG, and the CARRY FLAG going to Bit 7. RRA also can be used to divide a number in 2.
0872
LD H,A67
Load Register H with the adjusted LSB in Register A
0873
LD A,C79
Load Register A with the MSB of the single precision value in Register C
0874-0875
JR NC,0881HJR NC,FMULT530 0B
If the bit was zero, don’t add in any numbers and, instead, jump forward to 0881H
0876
PUSH HLE5
Save the counters (tracked in Register Pair HL) to the STACK
0877-0879
LD HL,(4150H)LD HL,(FMLTT2)2A 50 41
Load Register Pair HL with the NMSB and the LSB of the original value in Register Pairs BC and DE
087A
ADD HL,DE19
Add the NMSB and the LSB of the total figured so far in Register Pair DE to the NMSB and the LSB of the original value in Register Pair HL
087B
EX DE,HLEB
Load Register Pair DE with the adjusted total in Register Pair HL
087C
POP HLE1
Get the counters back from the STACK and put it in Register Pair HL
087D-087F
LD A,(414FH)LD A,(FMLTT1)3A 4F 41
Load Register A with the MSB of the original value in Register Pairs BC and DE
0880
ADC A,C89
Add the MSB of the original value in Register A to the MSB of the total figured so far in Register C
0881–  ↳ FMULT5
RRA1F
Shift the adjusted MSB of the total in Register A one place to the right. RRA rotates the contents of Register A right one bit position, with Bit 0 going to the CARRY FLAG, and the CARRY FLAG going to Bit 7. RRA also can be used to divide a number in 2.
0882
LD C,A4F
Load Register C with the adjusted MSB of the total in Register A
0883
LD A,D7A
Load Register A with the NMSB of the total in Register D
0884
RRA1F
Shift the NMSB of the total in Register A one place to the right. RRA rotates the contents of Register A right one bit position, with Bit 0 going to the CARRY FLAG, and the CARRY FLAG going to Bit 7. RRA also can be used to divide a number in 2.
0885
LD D,A57
Load Register D with the adjusted NMSB of the total in Register A
0886
LD A,E7B
Load Register A with the LSB of the total in Register E
0887
RRA1F
Shift the LSB of the total in Register A one place to the right. RRA rotates the contents of Register A right one bit position, with Bit 0 going to the CARRY FLAG, and the CARRY FLAG going to Bit 7. RRA also can be used to divide a number in 2.
0888
LD E,A5F
Load Register E with the adjusted LSB of the total in Register A
0889
LD A,B78
Load Register A with the value in Register B
088A
RRA1F
Shift the value in Register A one place to the right. RRA rotates the contents of Register A right one bit position, with Bit 0 going to the CARRY FLAG, and the CARRY FLAG going to Bit 7. RRA also can be used to divide a number in 2.
088B
LD B,A47
Load Register B with the adjusted value in Register A
088C
DEC L2D
Decrement the bit counter in Register L and set the flags accordingly
088D
LD A,H7C
Load Register A with the LSB of the number we are multiplying
088E-088F
JR NZ,0871HJR NZ,FMULT420 E1
Loop until 8 bits have been shifted
0890–  ↳ POPHRT
POP HLE1
Get the memory pointer to the number to multiply by from the STACK and put it in Register Pair HL
0891
RETC9
RETurn to CALLer

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

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

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

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

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

This routine divides the ACCumulator by 10. Every Register is used.

0897-0899–  ↳ DIV10
CALL 09A4HCALL PUSHFCD A4 09
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
CALL 09B1HCALL MOVFMCD B1 09
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

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.

08A1
POP DED1
Get the NMSB and the LSB of the single precision value from the STACK and put it in Register Pair DE
08A2-08A4–  ↳ FDIV
CALL 0955HCALL SIGNCD 55 09
Go check to see if the single precision value in the ACCumulator is equal to zero so as to process that error.
08A5-08A7
JP Z,199AHJP Z,DV0ERRCA 9A 19
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
CALL 0914HCALL MULDIVCD 14 09
Go adjust the exponent in the ACCumulator for division
08AD
08AE
INC (HL)
INC (HL)34
Add two to the exponent pointed to by (HL) to correct scaling
08AF
DEC HL2B
Decrement the value of the memory pointer in Register Pair HL to now point to the High Order/MSB of the single precision number in the ACCumulator
08B0
LD A,(HL)7E
Load Register A with the MSB of the single precision value in the ACCumulator
08B1-08B3
LD (4089H),ALD (FDIVA+1),A32 89 40
Save the MSB of the single precision value in the ACCumulator in Register A at memory location 4089H
08B4
DEC HL2B
Decrement the value of the memory pointer in Register Pair HL to point to the Middle Order/NMSB of the single precision number in the ACCumulator
08B5
LD A,(HL)7E
Load Register A with the NMSB of the single precision value in the ACCumulator at the location of the memory pointer in Register Pair HL
08B6-08B8
LD (4085H),ALD (FDIVB+1),A32 85 40
Save the NMSB of the single precision value in the ACCumulator in Register A at memory location 4085H
08B9
DEC HL2B
Decrement the value of the memory pointer in Register Pair HL to point to the Low Order/LSB of the single precision number in the ACCumulator
08BA
LD A,(HL)7E
Load Register A with the LSB of the single precision value in the ACCumulator at the location of the memory pointer in Register Pair HL
08BB-08BD
LD (4081H),ALD (FDIVC+1),A32 81 40
Save the LSB of the single precision value in the ACCumulator in Register A at memory location 4081H

At this point, the memory locations are set up, and it’s time to get to work. According to the original ROM source:

The numerator will be kept in Registers B/H/L. The quotient will be formed in Registers C/D/E. To get a bit of the quotient, we first save Registers B/H/L on the stack, and then subtract the denominator that we saved in memory. The CARRY FLAG will indicate whether or not Registers B/H/L was bigger than the denominator. If Registers B/H/L are bigger, the next bit of the quotient is a one. To get the old Registers B/H/L off the stack, they are POPped into the PSW. If the denominator was bigger, the next bit of the quotient is zero, and we get the old Registers B/H/L back by POPping them off the stack. We have to keep an extra bit of the quotient in FDIVG+1 in case the denominator was bigger, in which case Registers B/H/L will get shifted left. If the MSB of Register B is one, it has to be stored somewhere, so we store it in FDIVG+1. Then the next time through the loop Registers B/H/L will look bigger because it has an extra High Order bit in FDIVG+1. We are done dividing when the MSB of Register C is a one, which occurs when we have calculated 24 bits of the quotient. When we jump to ROUND, the 25th bit of the quotient (whcih is in the MSB of Register A) determines whether we round or not. If initially the denominator is bigger than the numerator, the first bit of the quotient will be zero. This means we will go through the divide loop 26 times, since it stops on the 25th bit after the first non-zero bit of the exponent. So, this quotient will look shifted left one from the quotient of two numbers in which the numerator is bigger. This can only occur on the first time through the loop, so Registers C/D/E are all zero. So, if we finish the loop and Registers C/D/E are all zero, then we must decrement the exponent to correct for this.

08BE
LD B,C41
First, we need to get the number into B/H/L. First, load Register B with the MSB of the single precision dividend (held in in Register C)
08BF
EX DE,HLEB
Then, get the NMSB and LSB of the dividend from DE into Register Pair HL
08C0
XOR AAF
Next, we need to zero out C, D, E, and the Highest Order
08C1
LD C,A4F
Zero the MSB of the total by loading Register C with the value in Register A
08C2
LD D,A57
Zero the NMSB of the total by loading Register D with the value in Register A
08C3
LD E,A5F
Zero the LSB of the total by loading Register E with the value in Register A
08C4-08C6
LD (408CH),ALD (FDIVG+1),A32 8C 40
Zero memory location 408CH (which is holding the highest order)
08C7–  ↳ FDIV1
PUSH HLE5
Save the NMSB and LSB of the single precision dividend (held in Register Pair HL) on the STACK
08C8
PUSH BCC5
Save the MSB of the dividend in Register B on the STACK
08C9
LD A,L7D
Next we will need to subtract the number that was in the ACCumulator, so load Register A with the LSB of the dividend in Register L
08CA-08CC
CALL 4080HCALL FDIVCCD 80 40
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
JR NC,08D9HJR NC,FDIV230 07
If we subtracted too much then the NC flag will be set, in which case we need to get the old number back! To do this, JUMP down to 08D9H (which is a mid-instruction Z-80 trick)
08D2-08D4
LD (408CH),ALD (FDIVG+1),A32 8C 40
Update the highest order number held at FDIVG+1
08D5
POP AFF1
We want to clear the previous number off the stack since the subtraction didn’t cause an error
08D6
POP AFF1
And again
08D7
SCF37
Set the CARRY FLAG so that the next bit in the quotient is a 1 to indicate that the subtraction was good
08D8
D2
Z-80 Trick – See the note at 0134H for an explanation.
08D9
POP BC
Get the value from the STACK and put it in register pair BC.
08DA
POP HL
Get the value from the STACK and put it in 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
JP M,0797HJP M,ROUNDBFA 97 07
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
JR NZ,08C7HJR NZ,FDIV120 CB
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
JR NZ,08C7HJR NZ,FDIV120 C3
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
JP 07B2HJP OVERRC3 B2 07
Display an ?OV ERROR

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

This routine is to check for special cases and to add exponents for the FMULT and FDIV routines. Registers A, B, H and L are modified.

0907-0908–  ↳ MULDVS
LD A,FFH3E FF
This is the entry point from the DDIV routine. With this, we need to set up to subtract exponents. To do this we load Register A with an appropriate bit mask
0909
2E
Z-80 Trick – See the note at 0134H for an explanation.
090A
XOR A
Zero A and clear the flags.
090B-090D
LD HL,412DHLD HL,ARG-121 2D 41
Load Register Pair HL with the address of the SIGN and the High/Order MSB in ARG (a/k/a REG 2) (a/k/a ARG)
090E
LD C,(HL)4E
Load Register C with the High Order/MSB and the sign of the value in ARG (a/k/a REG 2) for unpacking
090F
INC HL23
Increment the value of the memory pointer in Register Pair HL to now point to the exponent
0910
XOR (HL)AE
Get the exponent by XORing the mask in Register A (which varied based on where this routine was entered from)
0911
LD B,A47
Save the adjusted exponent into Register B for processing below
0912-0913
LD L,00H2E 00
Load Register L with a 00H which will indicate that the below routine needs to ADD the exponents and then pass through to the MULDIV routine

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

0914–  ↳ MULDIV
LD A,B78
First we should test to make sure that the number isn’t zero, so Load Register A with the exponent in Register B
0915
OR AB7
Check to see if the exponent in Register A is equal to zero
0916-0917
JR Z,0937HJR Z,MULDV228 1F
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
JP P,0936HJP P,MULDV1F2 36 09
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
JP Z,0890HJP Z,POPHRTCA 90 08
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
CALL 09DFHCALL UNPACKCD DF 09
Unpack the arguments by a GODUB to UNPACK, which will turn on the sign bit of the MSB in the ACCumulator and Register B and save the sign bits
092E
LD (HL),A77
Save the new sign (held in Register A) to the ACCumulator at the location of the memory pointer in Register Pair HL
092F–  ↳ DCXHRT
DEC HL2B
Decrement the memory pointer in Register Pair HL so that it points to the exponent in the ACCumulator
0930
RETC9
RETurn to CALLer, with the HIGH ORDER/MSB in Register A

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

This routine is called from EXP. If jumped here will checks if ACC=0. If so, the Z flag will be set

0931-0933–  ↳ MLDVEX
CALL 0955HCALL SIGNCD 55 09
Go check the value of the sign bit for the value in the ACCumulator and choose UNDERFLOW if negative
0934
CPL2F
Pick OVERFLOW if it was positive
0935
POP HLE1
Get the value from the STACK and put it in Register HL
0936–  ↳ MULDV1
OR AB7
Weneed to test to see if the error was an OVERFLOW or an UNDERFLOW, so set the flags according to the value of the sign bit test
0937–  ↳ MULDV2
POP HLE1
Clean the old RETurn address off the stack
0938-093A
JP P,0778HJP P,ZEROF2 78 07
If the value in the ACCumulator is negative, JUMP to 0778H to handle the underflow
093B-093D
JP 07B2HJP OVERRC3 B2 07
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 09BFHCALL MOVRFCD BF 09
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
JP C,07B2HJP C,OVERRDA B2 07
Display an ?OV ERROR if the adjusted exponent in Register A is too large
0949
LD B,A47
Put the exponent back into Register B
094A-094C
CALL 0716HCALL FADDCD 16 07
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
JP 07B2HJP OVERRC3 B2 07
Display an ?OV ERROR if the value of the exponent at the location of the memory pointer in Register Pair HL is too large

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

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

To take advantage of the RST instructions to save bytes, FSIGN is defined to be an RST. “FSIGN” is equivalent to “call sign” the first few instructions of SIGN (the ones before SIGNC) are done in the 8 bytes at the RST location.

0955-0957–  ↳ SIGN
LD A,(4124H)LD A,(FAC)3A 24 41
Prepare to check to see if the number in the ACCumulator is ZERO by loading Register A with the value of the exponent in the ACCumulator
0958
OR AB7
Check to see if the exponent in Register A is equal to zero
0959
RET ZC8
Return if the single precision value in the ACCumulator is equal to zero
095A-095C–  ↳ SIGNC
LD A,(4123H)LD A,(FAC-1)3A 23 41
Load Register A with the SIGN of the ACCumulator
095D-095E
CP 2FHFE 2F
Z-80 Trick. If passing through, this will check the value of Register A and skip the next CPL instruction.
095F–  ↳ ICOMPS
RLA17
Put the value of the sign bit in Register A into the CARRY FLAG
0960
“SIGNS”
SBC A,A9F
If the CARRY FLAG is 0 (i.e., POSITIVE), then make Register A = 0. If the CARRY FLAG is 1 (i.e., NEGATIVE), make Register A = FFH
0961
RET NZC0
If the CARRY FLAG was 1, then the number is negative, and we want to RETurn
0962–  ↳ INRART
INC A3C
Increment the value in Register A so that Register A will be equal to 1 if the single precision value in the ACCumulator is positive
0963
RETC9
RETurn to CALLer

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

This routine will take a signed integer held in Register A and turn it into a floating point number. All registers are modified.

0964-0965–  ↳ FLOAT
LD B,88H06 88
Load Register B with an exponent for an integer value
0966-0968
LD DE,0000H11 00 00
Load Register Pair DE with zero

This routine will float the singed number in B/A/D/E. All registers are modified.

0969-096B–  ↳ FLOATR
LD HL,4124HLD HL,FAC21 24 41
Load Register Pair HL with the address of the exponent in the ACCumulator
096C
LD C,A4F
Load Register C with the High Order/MSB of the integer value
096D
LD (HL),B70
Save the exponent in Register B into the ACCumulator at the location of the memory pointer in Register Pair HL
096E-096F
LD B,00H06 00
Load Register B with zero to zero the overflow byte
0970
INC HL23
Increment the memory pointer in Register Pair HL to now point to the sign of the number in the ACCumulator
0971-0972
LD (HL),80H36 80
Assume a positive number by putting an 80H there
0973
RLA17
Shift the value of the sign bit into the CARRY FLAG
0974-0976
JP 0762HJP FADFLTC3 62 07
Jump to 0762H to float the number

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

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

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

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

Absolute Value: Converts the value in Working Register Area 1 (ACCumulator) to its positive equivalent. The result is left in the ACCumulator. If a negative integer greater than 2**15 is encountered, it is converted to a single precision value. The data type or mode flag (40AF) will be updated to reflect any change in mode

0977-0979–  ↳ ABS
CALL 0994HCALL VSIGNCD 94 09
GOSUB to VSIGN to get the SGN of the ACCumulator into Register A
097A
RET PF0
If that sign is POSITIVE, then I guess we are done, so just RETurn

This routine will negate any value in the ACCumulator. Every Register is affected.

097B–  ↳ VNEG
RST 20HGETYPEE7
We need to check the value of the current number type flag, so we call the TEST DATA MODE routine at RST 20H which determines the type of the current value in the ACCumulator and returns a combination of STATUS flags and unique numeric values in the register A according to the data mode flag (40AFH). The results are returned as follows:
Variable
Type
FlagsRegister A
IntegerNZ/C/M/E-1
StringZ/C/P/E0
Single PrecisionNZ/C/P/O1
Double PrecisionNZ/NC/P/E5
097C-097E
JP M,0C5BHJP M,INEGFA 5B 0C
If that test showed INTEGER, JUMP to 0C5BH to negate an integer
097F-0981
JP Z,0AF6HJP Z,TMERRCA F6 0A
If that test showed STRING, Display a ?TM ERROR message

This routine will negate the single or double precision number in the ACCumulator. Registers A, H, and L are affected.

  • To use this routine, the number must already be PACKed.

    0982-0984–  ↳ NEG
    LD HL,4123HLD HL,FAC-121 23 41
    Load Register Pair HL with the address of the MSB (which holds the SIGN bit) in the ACCumulator.
    0985
    LD A,(HL)7E
    Load Register A with the MSB (which holds the SIGN bit) in the ACCumulator at the location of the memory pointer in Register Pair HL
    0986-0987
    XOR 80HXOR 1000 0000EE 80
    Complement the sign bit in the MSB in Register A. Since we know the number is negative, this is really just switching it to positive.
    0988
    LD (HL),A77
    Save the adjusted MSB (which holds the SIGN bit) in Register A in the ACCumulator at the location of the memory pointer in Register Pair HL
    0989
    RETC9
    RETurn to CALLer

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

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

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

    SGN function (ACC=SGN(ACC)). After execution, NTF=2 and ACC=-l, 0 or 1 depending on sign and value of ACC be fore execution. 0994 This routine checks the sign of the ACC. NTF must be set. After execution A register=00 if ACC=0, A=01 if ACC > 0 or A=FFH if A < 1. The Flags are also valid

    098A-098C–  ↳ SGN
    CALL 0994HCALL VSIGNCD 94 09
    Get the sign of the ACCumulator into Register A

    This routine will convert a signed number (held in Register A) into an integer.

    098D–  ↳ CONIA
    LD L,A6F
    Load Register L with the result of the sign test in Register A
    098E
    RLA17
    Shift the sign bit in Register A into the Carry flag
    098F
    SBC A,A9F
    Adjust the value in Register A so that it will be equal to zero if the current value in the ACCumulator is positive and equal to -1 if the current value in the ACCumulator is negative
    0990
    LD H,A67
    Save the adjusted value in Register A in Register H
    0991-0993
    JP 0A9AHJP MAKINTC3 9A 0A
    Jump to 0A9AH to return the result and set the VALTYP

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

    This routine checks the sign of the ACCumulator. NTF must be set. After execution A register=00 if ACCumulator=0, A=01 if ACC > 0 or A=FFH if A < 1. The Flags are also valid.

    0994–  ↳ VSIGN
    RST 20HGETYPEE7
    We need to check the value of the current number type flag, so we call the TEST DATA MODE routine at RST 20H which determines the type of the current value in the ACCumulator and returns a combination of STATUS flags and unique numeric values in the register A according to the data mode flag (40AFH). The results are returned as follows:
    Variable TypeFlagsRegister A
    IntegerNZ/C/M/E-1
    StringZ/C/P/E0
    Single PrecisionNZ/C/P/O1
    Double PrecisionNZ/NC/P/E5
    0995-0997
    JP Z,0AF6HJP Z,TMERRCA F6 0A
    If that test showed STRING, Display a ?TM ERROR message
    0998-099A
    JP P,0955HJP P,SIGNF2 55 09
    Since P means string, single precision, or double precision; and if it was a string it would have jumped already, this line says jump to 0955H if the current value in the ACCumulator is single precision or double precision, as those are processed the same way
    099B-099D
    LD HL,(4121H)LD HL,(FACLO)2A 21 41
    At this point, we know we have an integer. Load Register Pair HL with the integer value in the ACCumulator

    This routine finds the sign of the value held at (HL). Only Register A is altered.

    099E–  ↳ ISIGN
    LD A,H7C
    Load Register A with the MSB (which holds the SIGN bit) of the integer value in Register H
    099F
    OR LB5
    Check to see if the integer value in the ACCumulator is equal to zero
    09A0
    RET ZC8
    Return if the integer value in the ACCumulator is equal to zero
    09A1
    LD A,H7C
    If its not zero, then the sign of the number is the same as the sign of Register H so load Register A with the MSB (which holds the SIGN bit) of the integer value in Register H
    09A2-09A3
    JR 095FHJR ICOMPS18 BB
    Jump to 095FH to set Register A accordingly

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

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

    Loads Single-precision value from ACC to STACK ((SP)=ACC). To retrieve this value, POP BC followed by POP DE. A, BC and HL are unchanged by this function.

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

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

    This routine moves a number from memory (pointed to by HL) into the ACCumulator — (ACCumulator=(HL)). All registers except Register A are affected, with HL = HL + 4 on exit.

    09B1-09B3–  ↳ MOVFM
    CALL 09C2HCALL MOVRMCD C2 09
    Load the SINGLE PRECISION value pointed to by Register Pair HL into Register Pairs BC/DE via a CALL to 09C2H Then fall into the MOVFR routine.

    This routine loads the ACC with the contents of the BC and DE Register Pairs. (ACC=BCDE). Only Registers D and E are modified.

    Move SP Value In BC/DC Into ACCumulator: Moves the single precision value in BC/DE into ACCumulator. HL is destroyed BC/DE is left intact. Note – the mode flag is not updated!

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

    09BF-09CA – SINGLE PRECISION MATH ROUTINE
    “LDRASA”

    This routine is the opposite of the 09B4H routine. It loads four bytes from REG 1 (single-precision) into the BC and DE register pairs. (BCDE=ACC). A is unchanged.

    Move FAC to registers (B,C,D,E). Alters B,C,D,E,H,L

    09BF-09C1
    Load register pair HL with the starting address for a single precision value in REG 1.
    09C2–  ↳ MOVRM
    LD E,(HL)5E
    Load Register E with the LSB of the single precision value in the ACCumulator at the location of the memory pointer in Register Pair HL.

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

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

    This routine is the opposite of the 09B1H routine. It loads the number from the ACCumulator to the memory location pointed to by HL. ((HL)=ACC). Modifies all Registers except for Register C

    09CB-09CD–  ↳ MOVMF
    LD DE,4121HLD DE,FACLO11 21 41
    Load Register Pair DE with the starting address for a single precision value in the ACCumulator. Then pass throgh to the following routine.

    Data move routine. This moves four bytes from the location pointed to by DE into the location pointed to by HL. ((HL)=(DE)). Modifies all Registers except for Register C

    09CE-09CF–  ↳ MOVE
    LD B,04H06 04
    Load Register B with the number of bytes to be moved for a single precision value so that B will act as a counter.
    09D0-09D1
    JR 09D7HJR MOVE118 05
    Jump to 09D7H (which is the GENERAL PURPOSE MOVE routine and moves the contents of the B Register Bytes from the address in DE to the address in HL)

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

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

    Data move routine. The location pointed to by DE is loaded with bytes from the location pointed to by HL. The number of bytes moved is determined by the value in the NTF. ((DE)=(HL))

    09D2–  ↳ MOVVFM
    EX DE,HLEB
    Exchange the value in Register Pair HL with the value in Register Pair DE, and then fall through to the VMOVE routine.

    This routine is similar to 9D2H above. The only difference is that it moves data in the opposite direction. ((HL) = (DE))

    09D3-09D5–  ↳ VMOVE
    LD A,(40AFH)LD A,(VALTYP)3A AF 40
    Load Register A with the current value of the number type flag (which is in 40AFH). This, not coincidentally, is also the length of the number being worked on!
    09D6
    LD B,A47
    Load Register B with the number of bytes to be moved in Register A.

    This routine is the same as 9D6H except that the number of bytes shifted is determined by the value in the B Register ((HL)=(DE))

  • Moves contents of B-register bytes from the address in DE to the address given in HL. Uses all registers except C

    09D7–  ↳ MOVE1
    LD A,(DE)1A
    Top of a loop to move (DE)’s content into (HL). First, load Register A with the value at the location of the memory pointer in Register Pair DE.

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

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

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

    When the number in the ACCumulator is unpacked, the assumed one in the mantissa is restored, and the complement of the sign is placed in ACCumulator+1.

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

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

    This routine moves a number of bytes (the number depending on the value stored in the VALTYPE) from (HL) to the ACCumulator. All Registers except C are affected.

    09F4-09F6–  ↳ VMOVFA
    LD HL,4127HLD HL,ARGLO21 27 41
    This is the entry point from the DADD routine. To facilitate, we need to set HL to point to ARG (a/k/a REG 2)) instead of the ACCumulator
    09F7-09F9–  ↳ VMOVFM
    LD DE,09D2HLD DE,MOVVFM11 D2 09
    Load Register Pair DE with the return address of the routine that does an exchange and then falls into the MOVE1 routine.
    09FA-09FB
    JR 0A02HJR VMVVFM18 06
    Jump to 0A02

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

    This is the opposite of 9F4H. This routine moves a number of bytes (the number depending on the value stored in the VALTYPE) from the ACCumulator to (HL). All Registers except C are affected.

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

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

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

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

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

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

    0A0C–  ↳ FCOMP
    LD A,B78
    First we need to check to see if ARG is zero, so load Register A with the value of the exponent in Register B
    0A0D
    OR AB7
    Set the flags based on Register A
    0A0E-0A10
    JP Z,0955HJP Z,SIGNCA 55 09
    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
    CALL 0955HCALL SIGNCD 55 09
    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
    CALL 0A26HCALL FCOMP2CD 26 0A
    Now that we have resolved the signs, JUMP to FCOMP2 to check the rest of the numbers
    0A23–  ↳ FCOMPD
    RRA1F
    If are are here, then the numbers are different, so the next step is to change the signs if both numbers are negative. To do this, first move the value of the Carry flag from the comparison into Register A. RRA rotates the contents of Register A right one bit position, with Bit 0 going to the CARRY FLAG, and the CARRY FLAG going to Bit 7. RRA also can be used to divide a number in 2.
    0A24
    XOR CA9
    Combine the value of the MSB/SIGN of the single precision value in Register C with the value in Register A
    0A25
    RETC9
    With Register A now set, RETurn to CALLer

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

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

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

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

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

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

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

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

    0A39–  ↳ ICOMP
    LD A,D7A
    First we test the signs, so load Register A with the SIGN of the integer value in Register D
    0A3A
    XOR HAC
    Check to see if the sign bit for the MSB of the integer value in Register H is the same as the sign bit for the SIGN for the integer value in Register A
    0A3B
    LD A,H7C
    If the signs are NOT the same, then the result is the sign of (HL), so put the SIGN of the number in (HL) into the response register of Register A.
    0A3C-0A3E
    JP M,095FHJP M,ICOMPSFA 5F 09
    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
    JP NZ,0960HJP NZ,SIGNSC2 60 09
    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
    0A43
    LD A,L
    Load register A with the 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
    JP NZ,0960HJP NZ,SIGNSC2 60 09
    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
    0A49-0A4B–  ↳ DCOMPD
    LD HL,4127HLD HL,ARGLO21 27 41
    Load Register Pair HL with the starting address of ARG (a/k/a REG 2). If entering here, then (DE) already needs to be set with the pointer to ARG.
    Note: 4127H-412EH holds ARG (a/k/a REG 2)
    0A4C-0A4E
    CALL 09D3HCALL VMOVECD D3 09
    Go move the double precision value pointed to by Register Pair DE to ARG (a/k/a REG 2)
    0A4F-0A51–  ↳ XDCOMP
    LD DE,412EHLD DE,ARG11 2E 41
    Load Register Pair DE with the address of the exponent in ARG (a/k/a REG 2)
    0A52
    LD A,(DE)1A
    Load Register A with the exponent for the double precision value in ARG (a/k/a REG 2) at the location of the memory pointer in Register Pair DE
    0A53
    OR AB7
    Check to see if the double precision value in ARG (a/k/a REG 2) is equal to zero
    0A54-0A56
    JP Z,0955HJP Z,SIGNCA 55 09
    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
    CALL 0955HCALL SIGNCD 55 09
    Go check to see if the double precision value in the ACCumulator is equal to zero
    0A5E
    DEC DE1B
    Decrement the value of the memory pointer in Register Pair DE so that DE now points to the MSB+SIGN of the number in ARG (a/k/a REG 2)
    0A5F
    LD A,(DE)1A
    Load Register A with the MSB+SIGN of the double precision value in ARG (a/k/a REG 2) at the location of the memory pointer in Register Pair DE
    0A60
    LD C,A4F
    Presetve the MSB+SIGN of the double precision value in ARG (a/k/a REG 2) into Register C
    0A61
    RET ZC8
    If the number in the ACCumulator = 0, then the sign of the result is the sign of ARG, so RETurn wto FCOMPS
    0A62-0A64
    LD HL,4123HLD HL,FAC-121 23 41
    Load Register Pair HL with the address of the SIGN of the double precision value in the ACCumulator
    0A65
    XOR (HL)AE
    Check to see if the sign bit the double precision value in the ACCumulator at the location of the memory pointer in Register Pair HL is the same as the sign bit of the double precision value in ARG (a/k/a REG 2) in Register A
    0A66
    LD A,C79
    In case they are the same, get the sign from C into Register A.
    0A67
    RET MF8
    If they are NOT the same, RETurn to FCOMPS to set set Register A
    0A68
    INC DE13
    Increment the value of the memory pointer in Register Pair DE so that DE now points to the exponent of ARG
    0A69
    INC HL23
    Increment the value of the memory pointer in Register Pair HL so that HL now points to the exponent of the ACCumulator
    0A6A-OA6B
    LD B,08H06 08
    Load Register B with the number of bytes to be compared, as B will act as a counter
    0A6C–  ↳ DCOMP1
    LD A,(DE)1A
    Load Register A with a byte from the double precision number in ARG (pointed to by Register Pair DE)
    0A6D
    SUB (HL)96
    Use subtraction to compare that byte from ARG with the correspondible byte from the ACCumulator (pointed to by Register Pair HL)
    0A6E-0A70
    JP NZ,0A23HJP NZ,FCOMPDC2 23 0A
    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
    JR NZ,0A6CHJR NZ,DCOMP120 F6
    The DEC of B will set the flags. If B is NOT ZERO, then Loop back to DCOMP1 until all of the bytes have been compared
    0A76
    POP BCC1
    If we are here, then the numbers are the same, so we need to clean the RETurn to FCOMPS off the stack, as that is not where we want to RETurn to
    0A77
    RETC9
    RETurn to the actual CALLer

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

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

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

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

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

    0A78-0A7A–  ↳ DCOMP
    CALL 0A4FHCALL XDCOMPCD 4F 0A
    GOSUB to compare the double precision value in ARG (a/k/a REG 2) to the double precision value in the ACCumulator
    0A7B-0A7D
    JP NZ,095EHJP NZ,FCOMPSC2 5E 09
    If the double precision value in the ACCumulator and the double precision value in ARG (a/k/a REG 2) aren’t the same then JUMP to FCOMPS to negate the answer and set up the CARRY FLAG for the DOCMP routine
    0A7E
    RETC9
    RETurn to CALLer

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

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

    NOTE: To use a ROM call to call the CINT routine, store the single precision input variable in 4121H-4124H and then call to 0A8AH and bypass all the foregoing. After the call, the integer result would be in 4121H-4122H and in the HL Register Pair. Too big a number will generate a ?OV Error.

    0A7F–  ↳ FRCINT
    RST 20HGETYPEE7
    We need to check the value of the current number type flag, so we call the TEST DATA MODE routine at RST 20H which determines the type of the current value in the ACCumulator and returns a combination of STATUS flags and unique numeric values in the register A according to the data mode flag (40AFH). The results are returned as follows:
    Variable TypeFlagsRegister A
    IntegerNZ/C/M/E-1
    StringZ/C/P/E0
    Single PrecisionNZ/C/P/O1
    Double PrecisionNZ/NC/P/E5
    0A80-0A82
    LD HL,(4121H)LD HL,(FACLO)2A 21 41
    Just in acse we already have an integer, Load Register Pair HL with the integer value in the ACCumulator (which is stored at FACLO and FACLO+1)
    0A83
    RET MF8
    If that test showed we have an INTEGER, then return out of this subroutine
    0A84-0A86
    JP Z,0AF6HJP Z,TMERRCA F6 0A
    If that test showed we have a STRING, Display a ?TM ERROR message
    0A87-0A89
    CALL NC,0AB9HCALL NC,CONSDD4 B9 0A
    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
    JR NC,0AA3HJR NC,CONIS230 0E
    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
    CALL 0AFBHCALL QINTCD FB 0A
    If we are here then the number isn’t too big, so GOSUB to QINT to convert the single precision value in the ACCumulator to an integer and return with the integer value in Register Pair DE
    0A98
    EX DE,HLEB
    Load Register Pair HL with the integer value that was put into Register Pair DE by QINT
    0A99–  ↳ CONIS1
    POP DED1
    Get the error address from the STACK and put it in Register Pair DE

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

    This is the routine that returns the value in the HL Register Pair to the BASIC program that called it. In effect it moves the content of HL into the ACCumulator so it is ACCumulator = (HL) with VALTYPE set accordingly

    0A9A-0A9C–  ↳ MAKINT
    LD (4121H),HLLD (FACLO),HL22 21 41
    Save the integer value in Register Pair HL as the current value in the ACCumulator.
    0A9D-0A9E–  ↳ VALINT
    LD A,02H3E 02
    Load Register A with an integer number type flag.
    0A9F-0AA1–  ↳ CONISD
    LD (40AFH),ALD (VALTYP),A32 AF 40
    Save the integer number type flag in Register A as the current value of the number type flag.
    Note: 40AFH holds Current number type flag. This is the entry point from the CONDS routine
    0AA2
    RETC9
    RETurn to CALLer

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

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

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

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

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

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

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

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

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

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

    Convert a double-prevision number to single-precision. Every register is affected.

    0AB9-0ABB–  ↳ CONSD
    CALL 09BFHCALL MOVRFCD BF 09
    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
    CALL 0AEFHCALL VALSNGCD EF 0A
    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
    CALL 09DFHCALL UNPACKCD DF 09
    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
    JP 0796HJP ROUNDC3 96 07
    Jump to 0796H to round the chopped number up and RETurn

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

    Convert Integer to Single Precision. Every register is affected.

    Note: If you wanted to convert integer to single precision via a ROM call, you would store the integer input variable in 4121H-4122H and then call to 0ACCH. The result (as a single precision number) will be in 4121H-4124H.

    0ACC-0ACE–  ↳ CONSI
    LD HL,(4121H)LD HL,(FACLO)2A 21 41
    Load Register Pair HL with the integer value from the ACCumulator
    0ACF-0AD1–  ↳ CONSIH
    CALL 0AEFHCALL VALSNGCD EF 0A
    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
    JP 0969HJP FLOATRC3 69 09
    Jump to 0969H to float the integer into single precision

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

    CDBL routine. Takes a value from ACCumulator (regardless of integer or single precision) and convert it to double-precision. The result will be in ACC and NTF will be 8.

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

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

    Convert a single precision number to double precisions. Modifies Registers A, H, and L.

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

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

    0AEE
    LD BC,043EH01 3E 04
    Z-80 Trick. If passing through to this routine, BC will be modified but the next instruction will be skipped.
    0AF1-0AF3
    JP 0A9FHJP CONISDC3 9F 0A
    However we got here, Register A now holds the desired VALTYPE, so jump away to 0A9FH to save the value in Register A as the VALTYPE and RETurn

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

    This routine will force the ACCUmlator to be a STRING. Only Register A is modified.

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

    0AF6 – ?TM Error Routine – “TMERR”

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

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

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

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

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

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

    This routine can also be used to reset the BC and DE Register Pairs if the A Register contains 0. (XOR A before calling this routine).

    0AFB–  ↳ QINT
    LD B,A47
    Load Register B with the exponent of the single precision number in Register A. If a XOR A was executed before calling this routine, then the following will zero all of the registers.
    0AFC
    LD C,A4F
    Load Register C with the exponent of the single precision number in Register A
    0AFD
    LD D,A57
    Load Register D with the exponent of the single precision number in Register A
    0AFE
    LD E,A5F
    Load Register E with the exponent of the single precision number in Register A
    0AFF
    OR AB7
    Check to see if the single precision number in the ACCumulator is equal to zero
    0B00
    RET ZC8
    If Register A was 0 on entry (meaning that the exponent of the number is 0), then RETurn the same way but with C/D/E = 0, as any number whose exponent is 0 is 0.

    The original ROM source code has this to say about the next set of instructions:

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

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

    If the number was a negative non-integer, there is at least one non-zero bit to the right of the binary point and the net effect is that we get the absolute value of int(fac) in C/D/E. C/D/E is then negated if the original number was negative so the result will be signed.

    0B01
    PUSH HLE5
    Save the value in Register Pair HL on the STACK
    0B02-0B04
    CALL 09BFHCALL MOVRFCD BF 09
    Call 09BF which loads the SINGLE PRECISION value in the ACCumulator into Register Pair BC/DE
    0B05-0B07
    CALL 09DFHCALL UNPACKCD DF 09
    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
    CALL M,0B1FHCALL M,QINTAFC 1F 0B
    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
    CALL 07D7HCALL SHIFTRCD D7 07
    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
    CALL C,07A8HCALL C,ROUNDADC A8 07
    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
    CALL C,07C3HCALL C,NEGRDC C3 07
    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

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

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

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

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

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

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

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

    Floating To Integer: Unconditionally truncates the fractional part of a floating point number in the ACCumulator. The result is stored in the ACCumulator and the type flag is set to integer

    0B26–  ↳ FIX
    RST 20HGETYPEE7
    We need to check the value of the current number type flag, so we call the TEST DATA MODE routine at RST 20H which determines the type of the current value in the ACCumulator and returns a combination of STATUS flags and unique numeric values in the register A according to the data mode flag (40AFH). The results are returned as follows:
    Variable TypeFlagsRegister A
    IntegerNZ/C/M/E-1
    StringZ/C/P/E0
    Single PrecisionNZ/C/P/O1
    Double PrecisionNZ/NC/P/E5
    0B27
    RET MF8
    If that test shows we have an INTEGER then we are all done, so RETurn to the caller
    0B28-0B2A
    CALL 0955HCALL SIGNCD 55 09
    Go check the sign of the current value in the ACCumulator
    0B2B-0B2D
    JP P,0B37HJP P,VINTF2 37 0B
    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
    CALL 0982HCALL NEGCD 82 09
    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
    CALL 0B37HCALL VINTCD 37 0B
    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
    JP 097BHJP VNEGC3 7B 09
    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

    0B37–  ↳ VINT
    RST 20HGETYPEE7
    We need to check the value of the current number type flag, so we call the TEST DATA MODE routine at RST 20H which determines the type of the current value in the ACCumulator and returns a combination of STATUS flags and unique numeric values in the register A according to the data mode flag (40AFH). The results are returned as follows:
    Variable TypeFlagsRegister A
    IntegerNZ/C/M/E-1
    StringZ/C/P/E0
    Single PrecisionNZ/C/P/O1
    Double PrecisionNZ/NC/P/E5
    0B38
    RET MF8
    If that test shows we have an INTEGER then we are done, so RETurn to CALLer.
    0B39-0B3A
    JR NC,0B59HJR NC,DINT30 1E
    If the NC FLAG is set, then we have a double density number, so JUMP to DINT to handle the conversion.
    0B3B-0B3C
    JR Z,0AF6HJP Z,TMERR28 B9
    Display a ?TM ERROR if the current value in the ACCumulator isa string
    0B3D-0B3F
    CALL 0A8EHCALL CONISCD 8E 0A
    Now we try to use the CONIS routine to convert the single precision value in the ACCumulator to an integer. If we can’t we will return here to give a single precision result instead.
    0B40-0B42–  ↳ INT
    LD HL,4124HLD HL,FAC21 24 41
    Load Register Pair HL with the address of the exponent in the ACCumulator
    0B43
    LD A,(HL)7E
    Load Register A with the value of the exponent in the ACCumulator (held at the location of the memory pointer in Register Pair HL)
    0B44-0B45
    CP 98HFE 98
    Check to see if there are fractional bits used by the current value in the ACCumulator. If are none, then the NC CARRY flag will be set.
    0B46-0B48
    LD A,(4121H)LD A,(FACLO)3A 21 41
    Load Register A with the LSB of the single precision number in the ACCumulator
    0B49
    RET NCD0
    If there are no fractional bits, then we are done, so RETurn with Register A holding the single precision value in the ACCumulator
    0B4A
    LD A,(HL)7E
    Load Register A with the exponent of the single precision number in the ACCumulator
    0B4B-0B4D
    CALL 0AFBHCALL QINTCD FB 0A
    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
    CALL 0762HCALL FADFLTCD 62 07
    GOSUB to FADLT to re-float the number
    0B57
    POP AFF1
    Get the LSB of the single precision value from the STACK and put it in Register A
    0B58
    RETC9
    RETurn to CALLer

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

    Greated Integer function for double-precision numbers. All registers are affected.

    0B59-0B5B–  ↳ DINT
    LD HL,4124HLD HL,FAC21 24 41
    Load Register Pair HL with the address of the exponent in the ACCumulator
    0B5C
    LD A,(HL)7E
    Load Register A with the value of the exponent for the double precision number in the ACCumulator (stored at the location of the memory pointer in Register Pair HL)
    0B5D-0B5E
    CP 90HFE 90
    Check to see if the double precision number in the ACCumulator uses more or less than 16 bits of precision
    0B5F-0B61
    JP C,0A7FHJP C,FRCINTDA 7F 0A
    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
    JR NZ,0B78HJR NZ,DINT220 14
    If the NZ flag was set, we still need to make sure we didn’t have the special case number of -32768, so JUMP to DINT2
    0B64
    LD C,A4F
    If we’re here then we have to do it the hard way. First, load Register C with the exponent for the double precision number in Register A
    0B65
    DEC HL2B
    Decrement the value of the memory pointer in Register Pair HL to point to the HIGH ORDER (MSB+SIGN) portion of the double precision number
    0B66
    LD A,(HL)7E
    Load Register A with the HIGH ORDER (MSB+SIGN) of the double precision value in the ACCumulator at the location of the memory pointer in Register Pair HL
    0B67-0B68
    XOR 80HXOR 1000 0000EE 80
    Complement the value of the sign bit in Register A (which is 1000 0000)
    0B69-0B6A
    LD B,06H06 06
    Next we need to check to see if the rest of the number is ZERO, so load Register B with the number of bytes to be checked
    0B6B–  ↳ DINT1
    DEC HL2B
    Top of a loop. Decrement the value of the memory pointer in Register Pair HL to point to the next byte of the number
    0B6C
    OR (HL)B6
    Combine the value at the location of the memory pointer in Register HL with the value in Register A. If any of the bits are non-zero, then A will then be non-zero
    0B6D
    DEC B05
    Decrement the byte counter in Register B
    0B6E-0B6F
    JR NZ,0B6BHJR NZ,DINT120 FB
    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
    JP Z,0A9AHJP Z,MAKINTCA 9A 0A
    If the P flag is set, then Register A was zero (-32768), so JUMP to 0A9AH to deal with it
    0B77
    LD A,C79
    Register A wasn’t zero, so let’s keep calcuating. Load Register A with the exponent for the double precision value in Register C
    0B78-0B79–  ↳ DINT2
    CP B8HFE B8
    Check to see if there are fractional bits in for the double precision value in the ACCumulator
    0B7A
    RET NCD0
    If the NO CARRY FLAG is set, then there are no fractional bits so we already have an integer! With this, RETurn
    0B7B–  ↳ DINTFO–  ↳ “DINTFO”
    PUSH AFF5
    Save the exponent in Register A on the STACK. This is the entry point from FOUT, and if that’s the case, the CARRY FLAG will be set.
    0B7C-0B7E
    CALL 09BFHCALL MOVRFCD BF 09
    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
    CALL 09DFHCALL UNPACKCD DF 09
    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
    CALL M,0BA0HCALL M,DINTAFC A0 0B
    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
    CALL 0D69HCALL DSHFTRCD 69 0D
    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
    CALL M,0D20HCALL M,DROUNAFC 20 0D
    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
    JP 0CD8HJP DNORMLC3 D8 0C
    Jump to 0CD8H to re-float (“normalize”) the number

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

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

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

    This is the integer multiply routine for multiplying dimensioned array. It will calculate DE = BC * DE. If there is an overflow, a ?BS ERROR will get thrown. Every register except HL is affected.

    0BAA–  ↳ UMULT
    PUSH HLE5
    Save the value in Register Pair HL on the STACK
    0BAB-0BAD
    LD HL,0000H21 00 00
    Load Register Pair HL with zero to zero the product registers
    0BAE
    0BAF
    LD A,B
    OR C78 B1
    First let’s see if (BC) is zero by loading Register A with the MSB of the integer value in Register B and then ORing the LSB held in Register C
    0BB0-0BB1
    JR Z,0BC4HJR Z,MULRET28 12
    If BC is already zero, then just return, since HL is already zero
    0BB2-0BB3
    LD A,10H3E 10
    Load Register A with the counter value (which is 16)
    0BB4–  ↳ UMULT1
    ADD HL,HL29
    Top of a loop. Multiply the result in Register Pair HL by two
    0BB5-0BB7
    JP C,273DHJP C,BSERRDA 3D 27
    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
    JR NC,0BC1HJR NC,UMULT230 04
    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
    JP C,273DHJP C,BSERRDA 3D 27
    Display a BS ERROR message if the result in Register Pair HL has overflowed
    0BC1–  ↳ UMULT2
    DEC A3D
    Decrement the counter in Register A
    0BC2-0BC3
    Loop until the multiplication has been completed
    0BC4–  ↳ MULRET
    EX DE,HLEB
    Swap so that the return result is in DE. We don’t care about HL because …
    0BC5
    POP HLE1
    … restore the original HL from the STACK
    0BC6
    RETC9
    RETurn to CALLer

    The next bunch of routines are the integer arithmetic routines. According to the original ROM source code, the conventions are.

    • Integer variables are 2 byte signed numbers, with the LSB coming first
    • For one argument functions, the argument is in (HL) and the results are put into (HL)
    • For two argument operations, the first argument is in (DE), the second in (HL), and the restuls are left in the ACCumulator and, if there was no overflow, (HL). If there was an overflow, then the arguments are converted to single precision.
    • When integers are stored in the ACCumulator, they are stored at FACLO+0 and FACLO+1, with VALTYPE=2

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

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

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

    Every register is affected.

    Integer Subtraction: Subtracts the value in DE from the value in HL. The difference is left in the HL Register Pair. DE is preserved. In the event of underflow, both values are converted to single precision and the subtraction is repeated. The result is left in the ACCumulator and the mode flag is updated accordingly

    0BC7–  ↳ ISUB
    LD A,H7C
    The first thing we need to do is to extend the sign of (HL) into Register B. That’s the next 4 instructions. First, load Register A with the MSB+SIGN of the integer value in Register H
    0BC8
    RLA17
    Rotate the value of the sign bit into the CARRY FLAG
    0BC9
    SBC A,A9F
    Adjust Register A according to the value of the sign bit
    0BCA
    LD B,A47
    Load Register B with the result of the sign test
    0BCB-0BCD
    CALL 0C51HCALL INEGHLCD 51 0C
    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
    JR 0BD5HJR IADDS18 03
    Jump to 0BD5H to add the numbers

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

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

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

    Every register is affected.

    Note: If you wanted to add 2 integers via a ROM call, store one input into DE and the other into HL, and then call 0BD2H. The result will be in 4121H-4122H and in HL, with a 2 in 40AFH, and will take about 130 microseconds. If there is an overflow, the result will be converted to Single Precision and put into 4121H-4124H (with a 4 in 40AFH).

    0BD2–  ↳ IADD
    LD A,H7C
    The first thing we need to do is to extend the sign of (HL) into Register B. That’s the next 4 instructions. First, load Register A with the MSB+SIGN of the integer value in Register H
    0BD3
    RLA17
    Rotate the value of the sign bit into the CARRY FLAG
    0BD4
    SBC A,A9F
    Adjust Register A according to the value of the sign bit
    0BD5–  ↳ IADDS
    LD B,A47
    Load Register B with the result of the sign test
    0BD6
    PUSH HLE5
    Save the second argument (held in Register Pair HL) to the the STACK in case we have an overflow
    0BD7
    LD A,D7A
    The next 4 instructions extend the sign of (DE) into Register A. First, load Register A with the MSB+SIGN of the integer value in Register Pair DE
    0BD8
    RLA17
    Rotate the value of the sign bit into the CARRY FLAG
    0BD9
    SBC A,A9F
    Adjust Register A according to the value of the sign bit
    0BDA
    ADD HL,DE19
    Add the two LSBs, result in Register Pair HL
    0BDB
    ADC A,B88
    Add the extra HIGH ORDER (held in Register B) to the value of the sign test for the integer value in Register Pair DE in Register A
    0BDC
    RRCA0F
    The next 2 instructions are to see if the LSB of A is different from the MSB of H, in which ase an overflow occurred. So, put the value of the Carry flag in Register A
    0BDD
    XOR HAC
    Combine the value of the sign bit for the result in Register H with the value in Register A
    0BDE-0BE0
    JP P,0A99HJP P,CONIS1F2 99 0A
    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
    CALL 0ACFHCALL CONSIHCD CF 0A
    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 09A4HCALL PUSHFCD A4 09
    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
    CALL 0C6BHCALL INEGADCD 6B 0C
    Go float the integer value in Register Pair DE to single precision and return with the result in the ACCumulator
    0BEF-0BF1
    JP 0F8FHJP FADDTC3 8F 0F
    At this point the basic values are good enough to be added via single precision, so JUMP to FADDT to do that

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

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

    Note: If you wanted to multiply two integers, store one input in DE, the other in HL CALL 0BF2H. The result is in 4121H-4122H and in HL, with a 2 in 40AFH (but in an overflow the result is converted to single precision format and stored in 4121H-4124H, with a 4 in 40AFH. Process takes approximately 900 microseconds.

    0BF2–  ↳ IMULT
    LD A,H7C
    Load Register A with the MSB of the integer value in Register H
    0BF3
    OR LB5
    Combine the LSB of the integer value in Register L with the MSB of the integer value in Register A
    0BF4-0BF6
    JP Z,0A9AHJP Z,MAKINTCA 9A 0A
    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
    CALL 0C45HCALL IMULDVCD 45 0C
    Go convert any negative integer values to positive and return with Register B set according to the value of the sign bits
    0BFC
    PUSH BCC5
    Save the value of the sign bit test in Register B on the STACK
    0BFD
    0BFE
    LD B,H
    LD C,L44
    Copy the second argument from HL into BC
    0BFF-0C01
    LD HL,0000H21 00 00
    Start Register Pair HL at zero, as the result will go into HL
    0C02-0C03
    LD A,10H3E 10
    Load Register A with the counter value (which is 16)
    0C04–  ↳ IMULT1
    ADD HL,HL29
    Multiply the result in Register Pair HL by two
    0C05-0C06
    JR C,0C26HJR C,IMULT538 1F
    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
    JR NC,0C10HJR NC,IMULT230 04
    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
    JP C,0C26HJP C,IMULT5DA 26 0C
    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
    JR NZ,0C04HJR NZ,IMULT120 F1
    Loop until the multiplication has been completed
    0C13
    POP BCC1
    At this point we are done, so we need to finish up. First, get the value of the sign test from the STACK and put it in Register B
    0C14
    POP DED1
    Get the original FIRST argument from the STACK and put it in Register Pair DE

    This is the entry from IDIV. The next instructions test to see if the result is => 32768 or is -32768.

    0C15–  ↳ IMLDIV
    LD A,H7C
    Load Register A with the MSB of the result in Register H
    0C16
    OR AB7
    Test Register H
    0C17-0C19
    JP M,0C1FHJP M,IMULT3FA 1F 0C
    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
    JP 0C4DHJP INEGAC3 4D 0C
    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
    JR Z,0C37HJR Z,IMULT428 13
    If the Z FLAG is set, then the result is 32768, so JUMP to IMULT4
    0C24
    EX DE,HLEB
    If we are hre, then it is > 32768 giving us an overflow, so Load Register Pair HL with the integer value in Register Pair DE
    0C25-0C28
    LD BC,E1C1H01 C1 E1
    Z-80 Trick – See the note at 0134H for an explanation
    0C28-0C2A
    CALL 0ACFHCALL CONSIHCD CF 0A
    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
    CALL 09A4HCALL PUSHFCD A4 09
    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
    CALL 0ACFHCALL CONSIHCD CF 0A
    Go float the SECOND argument (held in in Register Pair HL) to single precision and return with the result in the ACCumulator
    0C32–  ↳ FMULTT
    POP BCC1
    Get the FIRST argument off the stack and put it in Register Pair BC. POLYX jumps here.
    0C33
    POP DED1
    Get the NMSB and the LSB of the single precision value from the STACK and put it in Register Pair DE
    0C34-0C36
    JP 0847HJP FMULTC3 47 08
    Multiply the arguments via regular old FMULT – the SINGLE PRECISION MULTIPLY routine at 0847H (which multiplies the current value in the ACCumulator by the value in (BC/DE). The product is left in the ACCumulator

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

    0C37–  ↳ IMULT4
    LD A,B78
    We need to see if the result is +/- 32768. First, load Register A with the result of the sign test in Register B
    0C38
    OR AB7
    Check the result
    0C39
    POP BCC1
    Discard the original SECOND argument from the STACK
    0C3A-0CJC
    JP M,0A9AHJP M,MAKINTFA 9A 0A
    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
    CALL 0ACFHCALL CONSIHCD CF 0A
    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
    JP 0982HJP NEGC3 82 09
    Jump to 0982H to turn -32768 into 32768 and finish up

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

    This is the integer division routine HL = DE / HL. The remainder will be left in DE and the quotient will be left in HL. Every register is affected.

    0C45–  ↳ IMULDV
    LD A,H7C
    Load Register A with the MSB+SIGN of the integer value in Register H
    0C46
    XOR DAA
    Combine the MSB of the integer value in Register D with the MSB+SIGN of the integer value in Register A
    0C47
    LD B,A47
    Save the result of the combined signs in Register A into Register B
    0C48-0C4A
    CALL 0C4CHCALL INEGHCD 4C 0C
    If necessary, NEGate the SECOND argument (i.e., the value in Register Pair HL) to positive
    0C4B
    EX DE,HLEB
    Presetve the contents of Register DE into Register Pair HL, and fall through to the negation routine below.
    0C4C–  ↳ INEGH
    LD A,H7C
    Load Register A with the MSB+SIGN of the integer value in Register H
    0C4D–  ↳ INEGA
    OR AB7
    Set the condition codes so we can see the sign of HL
    0C4E-0C50
    JP P,0A9AHJP P,MAKINTF2 9A 0A
    If the P FLAG is set, then the integer value in Register Pair HL is positive and we don’t need to NEGate it. So we JUMP to MAKINT to save the result into the ACCumulator for when the operators come back tt this routine.

    Negate HL routine. This routine changes the sign of the HL Register Pair and stores it in the ACC. (HL=ACCumulator=-HL) The result is returned in both the HL Register Pair and the ACC.

    0C51–  ↳ INEGHL
    XOR AAF
    Zero Register A.
    0C52
    LD C,A4F
    Load Register C with the ZERO held in Register A
    0C53
    SUB L95
    Subtract the LSB of the integer value in Register L from the ZERO in Register A
    0C54
    LD L,A6F
    Save the adjusted value in Register A in Register L
    0C55
    LD A,C79
    Load Register A with a ZERO
    0C56
    SBC A,H9C
    Subtract the HIGH ORDER (MSB+SIGN) of the integer value in Register H from the value in Register A
    0C57
    LD H,A67
    Save the adjusted value in Register A into Register H
    0C58-0C5A
    JP 0A9AHJP MAKINTC3 9A 0A
    Jump to 0A9AH to save the result into the ACCumulator for when the operations jump back here.

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

    Integer Negation Routine. All registers are altered.

    0C5B-0C5D–  ↳ INEG
    LD HL,(4121H)LD HL,(FACLO)2A 21 41
    Load Register Pair HL with the integer value in the ACCumulator
    0C5E-0C60
    CALL 0C51HCALL INEGHLCD 51 0C
    Go convert the integer value in Register Pair HL to positive if it’s negative
    0C61
    LD A,H7C
    Load Register A with the HIGH ORDER (i.e., MSB+SIGN) of the integer value in Register H
    0C62-0C63
    XOR 80HXOR 1000 0000EE 80
    Invert the value of the sign bit in Register A which is 1000 0000 so that we can check for the special case of -32768
    0C64
    OR LB5
    Combine the LSB of the integer value in Register L with the adjusted MSB of the integer value in Register A
    0C65
    RET NZC0
    Return if the integer value in the ACCumulator isn’t equal to -32768
    0C66–  ↳ INEG2
    EX DE,HLEB
    If we are here, the magic -32768 was found, so we need to float it. First, load Register Pair DE with the integer value in Register Pair HL
    0C67-0C69
    CALL 0AEFHCALL VALSNGCD EF 0A
    Go set the number type flag to single precision
    0C6A
    XOR AAF
    Zero Register A, which we will use for the HIGH ORDER
    0C6B-0C6C–  ↳ INEGAD
    LD B,98H06 98
    Load Register B with an exponent. IADD jumps here.
    0C6D-0C6F
    JP 0969HJP FLOATRC3 69 09
    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-0C72–  ↳ DSUB
    LD HL,412DHLD HL,ARG-121 2D 41
    Since addition is easier than subtraction, first we need to negate the SECOND argument by first loading Register Pair HL with the address of the MSB in ARG (a/k/a REG 2)
    0C73
    LD A,(HL)7E
    Load Register A with the HIGH ORDER (i.e., MSB+SIGN) of the double precision value in ARG (a/k/a REG 2) at the location of the memory pointer in Register Pair HL
    0C74-0C75
    XOR 80HXOR 1000 0000EE 80
    Invert the value of the sign bit for the MSB of the double precision value in Register A which is 1000 0000
    0C76
    LD (HL),A77
    Save the adjusted HIGH ORDER (i.e., MSB+SIGN) of the double precision value in Register A in ARG (a/k/a REG 2) at the location of the memory pointer in Register Pair HL. To save RAM we now fall into the DADD addition routine.

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

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

    Note: If you wanted to add 2 double precision numbers via a ROM call, store one input into 411DH-4124H and the other in 4127H-412EH. Then call 0C77H. The double precision result will be stored in 411DH-4124H approximately 1.3 milliseconds later.

    0C77-0C79–  ↳ DADD
    LD HL,412EHLD HL,ARG21 2E 41
    Load Register Pair HL with the address of the exponent in the FIRST argument held at ARG (a/k/a REG 2)
    0C7A
    LD A,(HL)7E
    Prepare to test that for ZERO by first loading Register A with the exponent of the double precision value in ARG (a/k/a REG 2) at the location of the memory pointer in Register Pair HL
    0C7B
    OR AB7
    Check to see if the double precision value in ARG (a/k/a REG 2) is equal to zero
    0C7C
    RET ZC8
    Return if the double precision value in ARG (a/k/a REG 2) is equal to zero, since that means that the ACCumulator (i.e., the FIRST argument) is actually the sum
    0C7D
    LD B,A47
    Preserve the exponent for the double precision value in Register A into Register C
    0C7E
    DEC HL2B
    Decrement the value of the memory pointer in Register Pair HL to now point to the HIGH ORDER (i.e., MSB + SIGN) for unpacking
    0C7F
    LD C,(HL)4E
    Load Register C with the value of the HIGH ORDER (i.e., MSB + SIGN) of the double precision value in ARG (a/k/a REG 2) at the location of the memory pointer in Register Pair HL
    0C80-0C82
    LD DE,4124HLD DE,FAC11 24 41
    Load Register Pair DE with the address of the exponent of the SECOND argument (held in the ACCumulator)
    0C83
    LD A,(DE)1A
    Fetch the value of the exponent of the double precision value in the ACCumulator at the location of the memory pointer in Register Pair DE
    0C84
    OR AB7
    Set the flags to see if the double precision value in the ACCumulator is equal to zero
    0C85-0C87
    JP Z,09F4HJP Z,MOVFACA F4 09
    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
    JR NC,0CA1HJR NC,DADD230 16
    If the NC FLAG is set, then the we need to put the smaller number into the ACCumulator, so JUMP to DADD2 to do that
    0C8B
    CPL2F
    Negate the shift count held in Register A
    0C8C
    INC A3C
    Increment the value of the difference for the exponents in Register A so that Register A will hold the positive difference
    0C8D
    PUSH AFF5
    Save the shift count (i.e., the difference for the exponents, held in Register A) to the STACK

    Next we are going to switch ARG and the ACCumulator.

    0C8E-0C8F
    LD C,08H0E 08
    Load Register C with a counter value which is 8
    0C90
    INC HL23
    Increment the value of the memory pointer in Register Pair HL so that it will be pointing to the exponent of the double precision value in ARG (a/k/a REG 2)
    0C91
    PUSH HLE5
    Save the value of the memory pointer in Register Pair HL (which is pointing to ARG) on the STACK
    0C92–  ↳ DADD1
    LD A,(DE)1A
    Top of a loop. Load Register A with the value of the ACCumulator pointed to by Register Pair DE
    0C93
    LD B,(HL)46
    Load Register B with the value of ARG (a/k/a REG 2) pointed to by Register Pair DE
    0C94
    LD (HL),A77
    Save the ACCumulator value into the corresponding ARG (a/k/a REG 2) byte.
    0C95
    LD A,B78
    Load Register A with the ARG byte (held in Register B)
    0C96
    LD (DE),A12
    Save the ARG byte (held in Register A) into the corresopnding ACCumulator byte (pointed to by Register Pair DE)
    0C97
    DEC DE1B
    Decrement the value of the memory pointer in Register Pair DE to the next lower byte of the ACCumulator
    0C98
    DEC HL2B
    Decrement the value of the memory pointer in Register Pair HL to the next lower byte in ARG
    0C99
    DEC C0D
    Decrement the value of the counter in Register C
    0C9A-0C9B
    JR NZ,0C92HJR NZ,DADD120 F6
    Loop until the double precision values in the ACCumulator and ARG (a/k/a REG 2) have been exchanged
    0C9C
    POP HLE1
    Get the HIGH ORDER back from the stack into Register Pair HL
    0C9D
    LD B,(HL)46
    Fetch the exponent for the double precision value in ARG (a/k/a REG 2) pointed to by Register Pair HL
    0C9E
    DEC HL2B
    Decrement the value of the memory pointer in Register Pair HL to now point to the HIGH ORDER (MSB + SIGN)
    0C9F
    LD C,(HL)4E
    Load Register C with the value of the MSB+SIGN for the double precision value in ARG (a/k/a REG 2) at the location of the memory pointer in Register Pair HL
    0CA0
    POP AFF1
    Get the shift count (i.e., difference for the exponents) back into Register A
    0CA1-0CA2–  ↳ DADD2
    CP 39HFE 39
    Check to see if the difference between the two exponents is greater than 56 bits
    0CA3
    RET NCD0
    Return if the difference between the two exponents is greater than 56 bits
    0CA4
    PUSH AFF5
    Save the shift count (i.e., difference for the exponents) from Register A onto the STACK
    0CA5-0CA7
    CALL 09DFHCALL UNPACKCD DF 09
    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
    CALL 0D69HCALL DSHFTRCD 69 0D
    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
    JP P,0CCFHJP P,DADD3F2 CF 0C
    If the P FLAG is set, then the signs of the numbers are different, so JUMP to DADD3 to subtract the values
    0CBE-0CC0
    CALL 0D33HCALL DADDAACD 33 0D
    Otherwise (i.e., the signs are the same) GOSUB to DADDAA to add the numbers
    0CC1-0CC3
    JP NC,0D0EHJP NC,DROUNDD2 0E 0D
    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
    JP Z,07B2HJP Z,OVERRCA B2 07
    Check for OVERFLOW because of that too! If the Z FLAG is set, then display an ?OV ERROR if the exponent for the double precision result in the ACCumulator is too large
    0CC9-0CCB
    CALL 0D90HCALL DSHFRBCD 90 0D
    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
    JP 0D0EHJP DROUNDC3 0E 0D
    JUMP to 0D0EH to ROUND the result and continue on.

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

    0CCF-0CD1–  ↳ DADD3
    CALL 0D45HCALL DADDASCD 45 0D
    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
    CALL C,0D57HCALL C,DNEGRDC 57 0D
    Go complement the result in the ACCumulator if the Carry flag is set

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

    0CD8–  ↳ DNORML
    XOR AAF
    Zero Register A, which will act as a byte shift counter
    0CD9–  ↳ DNORM1
    LD B,A47
    Preserve the shift counter into Register Register B
    0CDA-0CDC
    LD A,(4123H)LD A,(FAC-1)3A 23 41
    Load Register A with the value of the HIGH ORDER (i.e., MSB+SIGN) of the double precision result in the ACCumulator
    0CDD
    OR AB7
    Check to see if we can shift 8 numbers to the left
    0CDE-0CDF
    JR NZ,0CFEHJR NZ,DNORM520 1E
    If the NZ FLAG is set, then we cannot shift 8 numbers left, so we need to JUMP to see if the number is already normalized.
    0CE0-0CE2
    LD HL,411CHLD HL,DFACLO-121 1C 41
    If we are here then we CAN shift 8 numbers left, so first load Register Pair HL with the starting address of ACCumulator minus one.
    0CE3-0CE4
    LD C,08H0E 08
    Load Register C with the number of bytes to be shifted (i.e., 8)
    0CE5–  ↳ DNORM2
    LD D,(HL)56
    Top of a loop. Load Register D with a byte from the ACCumulator (pointed to by Register Pair HL)
    0CE6
    LD (HL),A77
    Save the value in Register A to the newly vacated location at the memory pointer in Register Pair HL. Note that on the FIRST loop, this is a zero.
    0CE7
    LD A,D7A
    Put the current byte from the ACCumulator (preserved in D) into Register A for writing on the next iteration
    0CE8
    INC HL23
    Increment the value of the memory pointer in registerpair HL
    0CE9
    DEC C0D
    Decrement the number of bytes to be shifted in Register C
    0CEA-0CEB
    JR NZ,0CE5HJR NZ,DNORM220 F9
    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
    JR NZ,0CD9HJR NZ,DNORM120 E6
    If the whole of the double precision value hasn’t been shifted, JUMP back to DNORM1 to shift some more
    0CF3-0CF5
    JP 0778HJP ZEROC3 78 07
    If we are here, then we have shifted all the bytes so JUMP to ZERO

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

    0CF6–  ↳ DNORM3
    DEC B05
    Decrement the shift counter held in Register B
    0CF7-0CF9
    LD HL,411CHLD HL,DFACLO-121 1C 41
    Load Register Pair HL with the starting address of ACCumulator minus one.
    0CFA-0CFC
    CALL 0D97HCALL DSHFLCCD 97 0D
    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
    JP P,0CF6HJP P,DNORM3F2 F6 0C
    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
    JR Z,0D0EHJR Z,DROUND28 09
    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
    JP NC,0778HP NC,ZEROD2 78 07
    If the NC FLAG was triggered, then we have an UNDERFLOW, so JUMP to ZERO
    0D0D
    RET ZC8
    If the Z FLAG is set, then the result is already zero and we are done, so FALL into the DROUND routine and round the result.

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

    This routine will round the ACCumulator. Registers A, B, H, and L are affected.

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

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

    0D20-0D22–  ↳ DROUNA
    LD HL,411DHLD HL,DFACLO21 1D 41
    Set up HL to point to the LSB of the the ACCumulator.
    Note: 411DH-4124H holds ACCumulator
    0D23-0D24
    LD B,07H06 07
    Load Register B with the number of bytes to be bumped for the double precision value in the ACCumulator
    0D25–  ↳ DRON1
    INC (HL)34
    Top of a loop. Increment a byte of the ACCumulator at the location of the memory pointer in Register Pair HL
    0D26
    RET NZC0
    Return if the value at the location of the memory pointer in Register Pair HL isn’t equal to zero
    0D27
    INC HL23
    Increment the value of the memory pointer in Register Pair HL to point to the next highest order in the ACCumulator
    0D28
    DEC B05
    Decrement the value of the byte counter in Register B
    0D29-0D2A
    JR NZ,0D25HJR NZ,DRONA120 FA
    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
    JP Z,07B2HJP Z,OVERRCA B2 07
    Check for overflow. If the Z FLAG is set, then JUMP to the Level II BASIC error routine and display an OV ERROR message if the exponent for the double precision value in the ACCumulator is too large
    0D2F
    DEC HL2B
    Decrement the value of the memory pointer in Register Pair HL to point to the HIGH ORDER
    0D30-0D31
    LD (HL),80H36 80
    Save a new MSB+SIGN at the location of the memory pointer in Register Pair HL
    0D32
    RETC9
    RETurn to CALLer

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

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

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

    This routine subtracts numbers in the pure version. This needs to be done in two subroutines since the ROM cannot be modified.

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

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

    This routine will negate the signed number held in the ACCumulator. Registers A, B, C, H, and L are affected. This routine is called by DADD and DINT.

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

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

    This routine wwill shift the double precision value held in the ACCumulator to the right once.

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

    0D7D-0D7EH – DOUBLE PRECISION MATH ROUTINE – “DSHFR3”

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

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

    This is the entry from DADD and DMULT.

    0D90-0D92–  ↳ DSHFRB
    LD HL,4123HLD HL,FAC-121 23 41
    Load Register Pair HL with the address of the HIGH PRDER (MSB) of the double precision value in the ACCumulator
    0D93-0D94
    LD D,01H16 01
    Load Register D with the number of bits to be shifted
    0D95-0D96
    JR 0D84HJR DSHFRA18 ED
    Jump to 0D84H to shift HL D bits to the right

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

    This routine will rotate the ACCumulator left one. Register A, C, H, and L are affected.

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

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

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

    Note: If you wanted to multiply two double precision numbers store one operand in 411DH-4124H, and store the other in 4127H-412EH and then CALL 0DA1H. The result (in double precision format) is in 411DH-4124H in approximately 22 milliseconds.

    0DA1-0DA3–  ↳ DMULT
    CALL 0955HCALL SIGNCD 55 09
    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
    CALL 090AHCALL MULDVACD 0A 09
    Add the exponents and take care of processing the signs of the numbers via a GOSUB to MULDVA
    0DA8-0DAA
    CALL 0E39HCALL DMULDVCD 39 0E
    Zero out the ACCumulator and put the move the double precision value in the ACCumulator to a temporary work area via a GOSUB to DMULDV
    0DAB
    LD (HL),C71
    Put the unpacked HIGH ORDER byte (pointed to by Register Pair HL in ARG) into Register C
    0DAC
    INC DE13
    Increment Register Pair DE so that it points to the LSB of the double precision value in ARG
    0DAD-0DAE
    LD B,07H06 07
    Load Register B with the number of bytes to be figured
    0DAF–  ↳ DMULT2
    LD A,(DE)1A
    Top of a big loop. Fetch a byte of ARG (at the location pointed to by DE) to multiply by into Register A
    0DB0
    INC DE13
    Increment the value of the memory pointer in Register Pair DE to point to the next higher byte.
    0DB1
    OR AB7
    Check to see if the value in Register A is equal to zero
    0DB2
    PUSH DED5
    Save the value of the memory pointer to ARG (held in Register Pair DE) to the STACK
    0DB3-0DB4
    JR Z,0DCCHJR Z,DMULT528 17
    If Register A is zero, then we are multiplying by ZERO, so JUMP to DMULT5
    0DB5-0DB6
    LD C,08H0E 08
    Otherwise, we need to set up for another loop for bit rotation. First, load Register C with the numberof bits to be shifted
    0DB7–  ↳ DMULT3
    PUSH BCC5
    Top of a loop. Save the counters (held in Register Pair BC) to the STACK
    0DB8
    RRA1F
    Shift the multiplier value (held in Register A) one place to the right. RRA rotates the contents of Register A right one bit position, with Bit 0 going to the CARRY FLAG, and the CARRY FLAG going to Bit 7. RRA also can be used to divide a number in 2.
    0DB9
    LD B,A47
    Preserve the shifted multiplier byte into Register B
    0DBA-0DBC
    CALL C,0D33HCALL C,DADDAADC 33 0D
    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
    CALL 0D90HCALL DSHFRBCD 90 0D
    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
    JR NZ,0DB7HJR NZ,DMULT320 F2
    If we have not hit 0 on the counter, LOOP back to 0DB7H to multiply by the next bit of the multiplier until all of the bits have been shifted
    0DC5–  ↳ DMULT4
    POP DED1
    If we are here, then we finished rotating that one byte. Top of a loop. First, get the pointer to ARG back from the STACK and put it in Register Pair DE
    0DC6
    DEC B05
    Decrement the number of bytes to be figured (tracked in Register B)
    0DC7-0DC8
    JR NZ,0DAFHJR NZ,DMULT220 E6
    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
    JP 0CD8HJP DNORMLC3 D8 0C
    If we are here then we are done (first, all the bits in each number were rotated, then that was done by all the bytes). Jump to 0CD8H to normalize and round the result.

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

    This routine handles multiplying by zero.

    0DCC-0DCE–  ↳ DMULT5
    LD HL,4123HLD HL,FAC-121 23 41
    Load Register Pair HL with the address of the HIGH ORDER/MSB of the ACCumulator
    0DCF-0DD1
    CALL 0D70HCALL DSHFRMCD 70 0D
    Go shift the double precision total in the ACCumulator right one byte
    0DD2-0DD3
    JR 0DC5HJR DMULT418 F1
    Jump back into DMULT at the point where we finalize the number

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

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

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

    Double precision divide routine. Divides the ACCumulator by 10. All registers are affected.

    0DDC-0DDE–  ↳ DDIV10
    LD DE,0DD4HLD DE,DTEN11 D4 0D
    Load Register Pair DE with the starting address of the double precision constant for 10
    0DDF-0DE1
    LD HL,4127HLD HL,ARGLO21 27 41
    Load Register Pair HL with the starting address of ARG (a/k/a REG 2).
    Note: 4127H-412EH holds ARG (a/k/a REG 2)
    0DE2-0DE4
    CALL 09D3HCALL VMOVECD D3 09
    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-0DE7–  ↳ DDIV
    LD A,(412EH)LD A,(ARG)3A 2E 41
    As always, start by checking to see if we are dealing with a ZERO. First, load Register A with the value of the exponent for the double precision value in ARG (a/k/a REG 2)
    0DE8
    OR AB7
    Check to see if the double precision value in ARG (a/k/a REG 2) is equal to zero
    0DE9-0DEB
    JP Z,199AHJP Z,DV0ERRCA 9A 19
    Display a ?/0 ERROR message if the double precision value in ARG (a/k/a REG 2) is equal to zero
    0DEC-0DEE
    CALL 0907HCALL MULDVSCD 07 09
    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
    CALL 0E39HCALL DMULDVCD 39 0E
    Zero the ACCumulator and move the double precision value from ACCumulator into ARG (a/k/a REG 2)
    0DF4-0DF6
    LD HL,4151HLD HL,FBUFFR+3421 51 41
    Load Register Pair HL with the address of the extra HIGH ORDER byte we will use in ARG
    0DF7
    LD (HL),C71
    Zero the that byte
    0DF8
    LD B,C41
    Zero Register B, which will be the flag that tells us when start dividing
    0DF9-0DFB–  ↳ DDIV1
    LD DE,414AHLD DE,FBUFFR+2711 4A 41
    Top of a large loop. First, get the pointer to the end of the BUFFR into Register Pair DE
    0DFC-0DFE
    LD HL,4127HLD HL,ARGLO21 27 41
    Load Register Pair HL with the address of the END of the double precision value in ARG (a/k/a REG 2)
    0DFF-0E01
    CALL 0D4BHCALL DADDSSCD 4B 0D
    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
    JR C,0E12HJR C,DDIV238 0B
    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
    CALL 0D39HCALL DADDSCD 39 0D
    Go add the double precision value in ARG (a/k/a REG 2) to the double precision value in FBUFFR
    0E10
    XOR AAF
    Clear the CARRY FLAG for the Z-80 trick in the next instruction.
    0E11-0E13
    JP C,0412HDA 12 04
    Z-80 TRICK. Since the CARRY was just cleared, this cannot ever execute and it won’t even see the next instruction. It is designed to allow for passing through but not running the next 2 instructions.
    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
    JP M,0D11HJP M,DROUNBFA 11 0D
    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
    CALL 0D99HCALL DSHFTLCD 99 0D
    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
    CALL 0D97HCALL DSHFLCCD 97 0D
    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
    JR NZ,0DF9HJR NZ,DDIV120 C9
    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
    JR NZ,0DF9HJR NZ,DDIV120 C3
    Continue dividing so long as we don’t have an overflow by LOOPING back to DDIV
    0E36-0E38
    JP 07B2HJP OVERRC3 B2 07
    Display an ?OV ERROR if the exponent for the result in the ACCumulator is too small

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

    This routine will transfer the double prevision number held in the ACCumulator to FBUFFR for the DMULT and DDIV routines. All registers are affected.

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

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

    This routine multiplies the current double-precision value by 10 by adding it to itself. First the current value is moved to a saved location, and then DP add routine adds the current value to that saved value. All registers are affected

    0E4D-0E4F–  ↳ DMUL10
    CALL 09FCHCALL VMOVAFCD FC 09
    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
    JP C,07B2HJP C,OVERRDA B2 07
    Display an ?OV ERROR if the adjusted exponent in Register A is too large
    0E5A
    LD (HL),A77
    Save the adjusted exponent back into the ACCumulator at the location of the memory pointer in Register Pair HL
    0E5B
    PUSH HLE5
    Save pointer to the ACCumulator onto the STACK
    0E5C-0E5E
    CALL 0C77HCALL DADDCD 77 0C
    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
    JP 07B2HJP OVERRC3 B2 07
    Display an ?OV ERROR if the exponent in the ACCumulator at the location of the memory pointer in Register Pair HL is too large

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

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

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

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

    At the end, Register B and the exponent (held in Register E) are used to determine how many times we multiply or divide the number by 10 to get the correct number.

    0E65-0E67–  ↳ FINDBL
    CALL 0778HCALL ZEROCD 78 07
    GOSUB to ZERO to zero the ACCumulator
    0E68-0E6A
    CALL 0AECHCALL VALDBLCD EC 0A
    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).

    0E6B
    “ASTOR”
    OR AFH
    Zero register A (by issuing an OR against 1010 1111).
    This routine is the same as E65H above, except that it fixes REG 1 and NTF to the smallest possible number type.
    0E6D–  ↳ FINCHR
    EX DE,HLEB
    Load Register Pair DE with the pointer to the current BASIC line being interpreted
    0E6E-0E70
    LD BC,00FFH01 FF 00
    Load Register Pair BC with a zero and a negative one. Register B will track the decimal point location and C will be a flag.
    0E71
    LD H,B60
    Load Register H with zero
    0E72
    LD L,B68
    Load Register L with zero. Now HL is zero.
    0E73-0E75
    CALL Z,0A9AHCALL Z,MAKINTCC 9A 0A
    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
    JP Z,0E83HJP Z,FINCCA 83 0E
    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
    JR Z,0E83HJR Z,FINC28 01
    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

    0E83H – Process a + or at the location of the current input buffer.

    0E83–  ↳ FINC
    RST 10HCHRGETD7
    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
    JP C,0F29HJP C,FINDIGDA 29 0F
    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
    JP Z,0EE4HJP Z,FINDPCA E4 0E
    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
    JR Z,0EA4HJR Z,FINEX28 14
    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
    JP Z,0EEEHJP Z,FININTCA EE 0E
    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
    JP Z,0EF5HJP Z,FINDBFCA F5 0E
    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
    JP Z,0EF6HJP Z,FINSNFCA F6 0E
    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
    JR NZ,0EC7HJR NZ,FINE20 24
    If the character ISN’T a D, then we must be finished with the number, so JUMP to FINE
    0EA3–  ↳ FINEX1
    OR AB7
    Set the flags according to the value of the character at the location of the current input buffer pointer in Register A

    0EA4H – Inside the ASCII TO BINARY CONVERTER routine. Process a E at the location of the current input buffer.

    0EA4-0EA6–  ↳ FINEX
    CALL 0EFBHCALL FINFRCCD FB 0E
    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
    RST 10HCHRGETD7
    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!
    0EBD
    RST 10H
    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.
    NOTE:
    • The RST 10H routine loads the next character from the string pointed to by the HL register into the A-register and clears the CARRY FLAG if it is alphabetic, or sets it if is alphanumeric.
    • Blanks and control codes 09H and 0BH 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.
    0EBC–  ↳ FINEC
    RST 10HCHRGETF1
    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
    JP C,0F94HJP C,FINEDGDA 94 0F
    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
    JR NZ,0EC7HJR NZ,FINE20 03
    So long as the exponent isn’t a ZERO, JUMP to FINE to skip over the handling of a negative exponent
    0EC4
    XOR AAF
    If we are here, then the exponent is negative. Zero Register A
    0EC5
    SUB E93
    NEGate the value of the exponent in Register E (i.e., A = 0 – E)
    0EC6
    LD E,A5F
    Load Register E with the negated version of itself
    0EC7–  ↳ FINE
    PUSH HLE5
    Save the current input buffer pointer to the string being converted (tracked in Register Pair HL) to the STACK
    0EC8
    LD A,E7B
    Load Register A with the value of the exponent in Register E
    0EC9
    SUB B90
    Subtract the value in Register B from the exponent in Register A to get the number of times we have to multiply or divide by 10

    This “FINE2” routine will multiply or divide by 10 the correct number of times. If A=0 the number is an integer.

    0ECA-0ECC–  ↳ FINE2
    CALL P,0F0AHCALL P,FINMLTF4 0A 0F
    If the P FLAG is set, then we need to multiply. So multiply the current value by ten
    0ECD-0ECF
    CALL M,0F18HCALL M,FINDIVFC 18 0F
    If the M FLAG is set, then we need to divide. So multiply the current value by ten
    0ED0-0ED1
    JR NZ,0ECAHJR NZ,FINE220 F8
    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
    CALL Z,097BHCALL Z,VNEGCC 7B 09
    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
    RST 20HGETYPEE7
    We need to check the value of the current number type flag, so we call the TEST DATA MODE routine at RST 20H which determines the type of the current value in the ACCumulator and returns a combination of STATUS flags and unique numeric values in the register A according to the data mode flag (40AFH). The results are returned as follows:
    IntegerNZ/C/M/E and A is -1
    StringZ/C/P/E and A is 0
    Single PrecisionNZ/C/P/O and A is 1
    Double PrecisionNZ/NC/P/E and A is 5.
    0EDA
    RET PEE8
    If that test shows we have anything other than a SINGLE PRECISION number, then we do no thave -32678, so RETurn
    0EDB
    PUSH HLE5
    If we are here, then we have a single preciosin number. Save the value of the current input buffer pointer of the string being parsed in Register Pair HL to the STACK
    0EDC-0EDE
    LD HL,0890HLD HL,POPHRT21 90 08
    Load Register Pair HL with the return address of the POPHRT routine because CONIS2 does funny things to the stack.
    0EDF
    PUSH HLE5
    Save the value of the return address in Register Pair HL on the STACK
    0EE0-0EE2
    CALL 0AA3HCALL CONIS2CD A3 0A
    Check to see if we have -32768 via a GOSUB to CONIS2 which will convert the current value in the ACCumulator to an integer if possible
    0EE3
    RETC9
    RETurn to CALLer. If we didn’t have -32768 then this will RETurn to POPHRT

    0EE4 – Math Routine – “FINDP”

    This routine checks to see if we have seen TWO decimal points and to set the decimal point flag. We jumped here when we found a single decimal point.

    0EE4–  ↳ FINDP
    RST 20HGETYPEE7
    We need to check the value of the current number type flag, so we call the TEST DATA MODE routine at RST 20H which determines the type of the current value in the ACCumulator and returns a combination of STATUS flags and unique numeric values in the register A according to the data mode flag (40AFH). The results are returned as follows:
    Variable TypeFlagsRegister A
    IntegerNZ/C/M/E-1
    StringZ/C/P/E0
    Single PrecisionNZ/C/P/O1
    Double PrecisionNZ/NC/P/E5
    0EE5
    INC C0C
    Increment the value in Register C to adjust the flag
    0EE6-0EE7
    JR NZ,0EC7HJR NZ,FINE20 DF
    If the INC C is NOT ZERO then we have 2 decimal points, so we are DONE.
    0EE8-0EEA
    CALL C,0EFBHCALL C,FINFRCDC FB 0E
    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
    JP 0E83HJP FINCC3 83 0E
    Jump to 0E83H to continue looking for digits

    0EEE – Math Routine – “FININT”

    0EEE–  ↳ FININT
    RST 20HGETYPEE7
    We need to check the value of the current number type flag, so we call the TEST DATA MODE routine at RST 20H which determines the type of the current value in the ACCumulator and returns a combination of STATUS flags and unique numeric values in the register A according to the data mode flag (40AFH). The results are returned as follows:
    Variable TypeFlagsRegister A
    IntegerNZ/C/M/E-1
    StringZ/C/P/E0
    Single PrecisionNZ/C/P/O1
    Double PrecisionNZ/NC/P/E5
    0EEF-0EF1
    JP P,1997HJP P,SNERRF2 97 19
    If that test shows anything but an INTEGER, jump to the Level II BASIC error routine and display a ?SN ERROR message
    0EF2–  ↳ INFINE
    INC HL23
    Top of a loop. If we are here, then we have something other than a single precision number. Next we move past the % character at the input buffer pointer to the sting being processed (tracked in Register Pair HL). We know this is the last character (a trailing %).
    0EF3-0EF4
    JR 0EC7HJR FINE18 D2
    We are now done, so Jump to 0EC7H to finish.
    0EF5–  ↳ FINDBF
    OR AB7
    If we are here then we need to force double precision, so set the NZ FLAG
    0EF6-0EF8–  ↳ FINSNF
    CALL 0EFBHCALL FINFRCCD FB 0E
    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
    JR 0EF2HJR INFINE18 F7
    Bump the pointer in HL and go to FINE via a JUMP to INFINE

    0EFB – Math Routine – “FINFRC”

    This routine will force the ACCumulator to be either single precision or double precision based on the Z FLAG. Z FLAG = Force to single precision; NZ FLAG = Force to double precision.

    0EFB–  ↳ FINFRC
    PUSH HLE5
    Save the value of the current input buffer pointer of the string being parsed in Register Pair HL on the STACK
    0EFC
    PUSH DED5
    Save the exponent (held in Register Pair DE) to the STACK
    0EFD
    PUSH BCC5
    Save the decimal point information (held in Register Pair BC) to the STACK
    0EFE
    PUSH AFF5
    Save the sp/dp value flag for the conversion (held in Register A) to the STACK
    0EFF-0F01
    CALL Z,0AB1HCALL Z,FRCSNGCC B1 0A
    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
    CALL NZ,0ADBHCALL NZ,FRCDBLC4 DB 0A
    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

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

    This subroutine multiplies a number by 10 once. The original ROM source notes that the reason this is a subroutine is that it can also double as a check to see if A is ZERO, thus saving bytes. All registers are affected.

    0F0A–  ↳ FINMUL
    RET ZC8
    If the exponent is ZERO then exit right back out
    0F0B–  ↳ FINMLT
    PUSH AFF5
    Save the exponent (held in Register Pair AF) to the STACK. FOUT enters the routine here.
    0F0C
    RST 20HGETYPEE7
    We need to check the value of the current number type flag, so we call the TEST DATA MODE routine at RST 20H which determines the type of the current value in the ACCumulator and returns a combination of STATUS flags and unique numeric values in the register A according to the data mode flag (40AFH). The results are returned as follows:
    Variable TypeFlagsRegister A
    IntegerNZ/C/M/E-1
    StringZ/C/P/E0
    Single PrecisionNZ/C/P/O1
    Double PrecisionNZ/NC/P/E5
    0F0D
    PUSH AFF5
    Save exponent and the value type from AF to the STACK
    0F0E-0F10
    CALL PO,093EHCALL PO,MUL10E4 3E 09
    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
    CALL PE,0E4DHCALL PE,DMUL10EC 4D 0E
    If that test shows DOUBLE PRECISION, go to 0E4DH to multiply the current value in the ACCumulator by “10D0”
    0F15
    POP AFF1
    Get the exponent and the value type from the STACK and put it in Register Pair AF
    0F16–  ↳ DCRART
    DEC A3D
    Decrement the exponent (held in Register A) since we have now multiplied by 1 since (x^10 = 10x^9).
    0F17
    RETC9
    RETurn to CALLer

    0F18 – Math Routine – “FINDIV”

    This subroutine divides a number by 10 once. FIN and FOUT use this routine. Registers A, B, and C are affected.

    0F18–  ↳ FINDIV
    PUSH DED5
    Preserve DE to the STACK for POPing at the end
    0F19
    PUSH HLE5
    Preserve HL to the STACK for POPing at the end
    0F1A
    PUSH AFF5
    Since we need to divide we need to preserve the exponent, so save the value in Register A on the STACK
    0F1B
    RST 20HGETYPEE7
    We need to check the value of the current number type flag, so we call the TEST DATA MODE routine at RST 20H which determines the type of the current value in the ACCumulator and returns a combination of STATUS flags and unique numeric values in the register A according to the data mode flag (40AFH). The results are returned as follows:
    Variable TypeFlagsRegister A
    IntegerNZ/C/M/E-1
    StringZ/C/P/E0
    Single PrecisionNZ/C/P/O1
    Double PrecisionNZ/NC/P/E5
    0F1C
    PUSH AFF5
    Save the value of the FLAGS from the RST 20H call to the STACK
    0F1D-0F1F
    CALL PO,0897HCALL PO,DIV10E4 97 08
    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
    CALL PE,0DDCHCALL PE,DDIV10EC DC 0D
    If that test shows DOUBLE PRECISION, go to 0DDCH to divide the current value in the ACCumulator by “10D0”
    0F24
    POP AFF1
    Restore the flags from the STACK and put it in Register Pair F
    0F25
    POP HLE1
    Get the value from the STACK and put it in Register Pair HL
    0F26
    POP DED1
    Get the value from the STACK and put it in Register Pair DE
    0F27
    INC A3C
    Increment the exponent (stored in Register A) since 10x^9 = x^10
    0F28
    RETC9
    RETurn to CALLer

    0F29 – Math Routine – “FINDIG”

    This routine will pack the next digit of the number into the ACCumulator. To do this, the ACCumulator is multipled by ten to shift everything over and make room for the digit, and then the digit is added in.

    0F29–  ↳ FINDIG
    PUSH DED5
    Save the exponent (held in Register Pair DE) on the STACK
    0F2A
    LD A,B78
    We need to check where the decimal point is, so load Register A with the value in Register B
    0F2B
    ADC A,C89
    Increement the decimal place count if we are past the decimal point by adding the value in Register C to the value in Register A
    0F2C
    LD B,A47
    Save the revised decimal point location (tracked in Register B)
    0F2D
    PUSH BCC5
    Save the decimal point information (tracked in Register Pair BC) on the STACK
    0F2E
    PUSH HLE5
    Save the value of the current input buffer pointer of the string being parsed in Register Pair HL on the STACK
    0F2F
    LD A,(HL)7E
    Fetch the digit we want to pack at the location of the current input buffer pointer in Register Pair HL
    0F30-0F31
    SUB 30HSUB “0”D6 30
    Subtract 30H from the ASCII value in Register A so that it will be binary
    0F32
    PUSH AFF5
    Save the adjusted value in the digit (held in Register A) to the STACK
    0F33
    RST 20HGETYPEE7
    We need to check the value of the current number type flag, so we call the TEST DATA MODE routine at RST 20H which determines the type of the current value in the ACCumulator and returns a combination of STATUS flags and unique numeric values in the register A according to the data mode flag (40AFH). The results are returned as follows:
    Variable TypeFlagsRegister A
    IntegerNZ/C/M/E-1
    StringZ/C/P/E0
    Single PrecisionNZ/C/P/O1
    Double PrecisionNZ/NC/P/E5
    0F34-0FJ6
    JP P,0F5DHJP P,FINDGVF2 5D 0F
    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
    RST 18HCOMPARDF
    Now we need to check to see if the integer value in HL is greater than or equal to 0CCDH (in DE), so we call the COMPARE DE:HL routine, which numerically compares DE and HL. Will not work for signed integers (except positive ones). Uses the A-register only. The result of the comparison is returned in the status Register As: CARRY SET=HL<DE; NO CARRY=HL>DE; NZ=Unequal; Z=Equal)
    0F3E-0F3F
    JR NC,0F59HJR NC,FINDG230 19
    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
    JP M,0F57HJP M,FINDG1FA 57 0F
    If the M FLAG is set, then we have overflowed, so JUMP to FINDG1
    0F4E-0F50
    LD (4121H),HLLD (FACLO),HL22 21 41
    If we are here, then we did not overflow so save the new integer back into the ACCumulator
    0F51–  ↳ FINDGE
    POP HLE1
    Restore the value of the current input buffer pointer of the string being parsed into Register Pair HL
    0F52
    POP BCC1
    Restore the the decimal point information (tracked in Register Pair BC) from the STACK
    0F53
    POP DED1
    Restore the exponent (held in Register Pair DE) from the STACK
    0F54-0F56
    JP 0E83HJP FINCC3 83 0E
    Jump to 0E83H to process the next character

    0F57 – Math Routine – “FINDG1”

    This routine handles 32768 and 32769

    0F57–  ↳ FINDG1
    LD A,C79
    Load Register A with the binary value of the character in Register C
    0F58
    PUSH AFF5
    Save the value in Register A on the STACK

    0F59 – Math Routine – “FINDG2”

    Convert integer digits into single precision digits

    0F59-0F5B–  ↳ FINDG2
    CALL 0ACCHCALL CONSICD CC 0A
    Go convert the current value in the ACCumulator to single precision
    0F5C
    SCF37
    Set the Carry flag to avoid the next instruction jumping away

    0F5D – Math Routine – “FINDGV”

    Determine if we have a single precision or a double prevision number

    0F5D-0F5E–  ↳ FINDGV
    JR NC,0F77HJR NC,FINDGD30 18
    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 0A0CHCALL FCOMPCD 0C 0A
    Call the SINGLE PRECISION COMPARISON routine at 0A0CH to algebraically compare the single precision value in BC/DE (which is 1000000) to the single precision value ACCumulator. The results are stored in A as follows:
    • A=0 if ACCumulator = BCDE
    • A=1 if ACCumulator>BCDE; and
    • A=FFH if ACCumulator<BCDE.
    0F68-0F6A
    JP P,0F74HJP P,FINDG3F2 74 0F
    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
    CALL 093EHCALL MUL10CD 3E 09
    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
    CALL 0F89HCALL FINLOGCD 89 0F
    Add the value in Register A to the single precision value in the ACCumulator
    0F72-0F33
    JR 0F51HJR FINDGE18 DD
    Jump to 0F51H to get the flags off of the stack and finish.

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

    The routine will convert a 7 digit single precision number into a double precision number

    0F74-0F76–  ↳ FINDG3
    CALL 0AE3HCALL CONDSCD E3 0A
    Go convert the single precision value in the ACCumulator to double precision

    This routine will pack in a digit into a double precision number

    0F77-0F79–  ↳ FINDGD
    CALL 0E4DHCALL DMUL10CD 4D 0E
    Go multiply the double precision value in the ACCumulator by ten
    0F7A-0F7C
    CALL 09FCHCALL VMOVAFCD FC 09
    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
    CALL 0964HCALL FLOATCD 64 09
    Go convert that binary value to single precision
    0F81-0F83
    CALL 0AE3HCALL CONDSCD E3 0A
    Go convert that single precision value to double precision
    0F84-0F86
    CALL 0C77HCALL DADDCD 77 0C
    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
    JR 0F51HJR FINDGE18 C8
    Jump to 0F51H to get the flags off of the stack and finish.

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

    This is a subroutine for FIN and for LOG

    0F89-0F8B–  ↳ FINLOG
    CALL 09A4HCALL PUSHFCD A4 09
    Call 09A4 which moves the SINGLE PRECISION value in the ACCumulator to the STACK (stored in LSB/MSB/Exponent order)
    0F8C-0F8E
    CALL 0964HCALL FLOATCD 64 09
    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
    JP 0716HJP FADDC3 16 07
    Jump to the SINGLE PRECISION ADD routine at 0716H (which adds the single precision value in (BC/DE) to the single precision value in the ACCumulator. The sum is left in the ACCumulator)

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

    Pack in a digit of the exponent. This is done by multiplying the old exponent by 10 and then adding in the desired digit. Note: This routine does NOT check for overflow.

    0F94–  ↳ FINEDG
    LD A,E7B
    Load Register A with the value of the exponent in Register E
    0F95-0F96
    CP 0AHFE 0A
    Test for overfly by checking to see if the value of the exponent in Register A is greater than or equal to 10. This is necessary because if it overflows the Register E will be corrupted
    0F97-0F98
    JR NC,0FA2HJR NC,FINEDO30 09
    If the value of the exponent in Register A is greater than or equal to 10 then we already have two digits, so JUMP to FINEDO to keep processing
    0F99
    RLCA07
    Multiply the value in Register A by two
    0F9A
    RLCA07
    Multiply the value in Register A by two. Register A now holds the original value of the exponent times four
    0F9B
    ADD A,E83
    Add the original value of the exponent in Register E to the adjusted value of the exponent in Register A
    0F9C
    RLCA07
    Multiply the value in Register A by two. Register A now holds the original value of the exponent times ten
    0F9D
    ADD A,(HL)86
    Add the value of the number at the location of the input buffer pointer in Register Pair HL to the adjusted value in Register A
    0F9E-0F9F
    SUB 30HSUB “0”D6 30
    Convert the adjusted value in Register A to it’s binary equivalent (which is subtracting 0011 0000)
    0FA0
    LD E,A5F
    Save the adjusted exponent into Register E
    0FA1-0FA3
    JP M,321EHFA 1E 32
    Z-80 TRICK. If passing through, this sill never trigger, but neither will the next instruction!
    0FA4-0FA6
    JP 0EBDHJP FINECC3 BD 0E
    Jump to 0EBDH to continue the routine

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

    This routine is to output a floating point number.

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

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

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

    0FAF-0FB1–  ↳ LINPRT
    CALL 0A9AHCALL MAKINTCD 9A 0A
    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
    CALL 1034HCALL FOUINICD 34 10
    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
    CALL 0FD9HCALL FOUT2CD D9 0F
    Go convert the integer value in the ACCumulator to an ASCII string. Return with Register Pair HL pointing to the result
    0FBA-0FBC
    JP 28A6HJP STROUIC3 A6 28
    Go display the message pointed to by Register Pair HL

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

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

    The format codes are as follows:

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

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

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

    This routine converts a single or double precision number in the ACCumulator to its ASCII equivalent. The ASCII value is stored at the buffer pointed to by the HL Register Pair. As the value is converted from binary to ASCII, it is formatted as it would be if a PRINT USING statement had been invoked. The format modes that can be specified are selected by loading the following values into the A, B, and C registers as follows:

    • A=0 means do not edit; this is a binary to ASCII conversion
    • A=X means edit as follows: Bit 7=1 means edit the value, Bit 6=Print commas every third digit, Bit 5=Include leading asterisks, Bit 4=Print a leading $, Bit 3=Sign Follows Value, and Bit 1=Exponential Notation
    • B = The number of digits to the left of the decimal point.
    • C = The number of digits after the decimal point.
    Note: If you wanted to convert any integer/single/double into its character string, store the variable in 4121H-4122H for integer, 4121H-4124H for single, or in 411DH-4124H for double. Then load 40AFH with a 2, 4, or 8 depending on whether that variable was integer, single, or double. Then call 0FBDH. Upon return, the character string is stored in 4130H and on, ending with a 00H.
    0FBE-0FC0–  ↳ PUFOUT
    CALL 1034HCALL FOUINICD 34 10
    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
    JR Z,0FC7HJR Z,FOUT128 02
    If a plus sign is NOT required to be added to the ASCII output string, then Jump to 0FC7H
    0FC5-0FC6
    LD (HL),2BHLD (HL),”+”36 2B
    If we are here, then it is required, so put a into the buffer pointed to by Register Pair HL
    0FC7–  ↳ FOUT1
    EX DE,HLEB
    Load Register Pair DE with the value of the buffer pointer (held in Register Pair HL)
    0FC8-0FCA
    CALL 0994HCALL VSIGNCD 94 09
    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
    JP P,0FD9HJP P,FOUT2F2 D9 0F
    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
    CALL 097BHCALL VNEGCD 7B 09
    GOSUB to 097BH to convert the negative value in the ACCumulator to its positive equivalent
    0FD6
    POP HLE1
    Restore the buffer pointer from the STACK into HL
    0FD7
    POP BCC1
    Restore the field length specifications from the STACK into B and C
    0FD8
    OR HB4
    Turn off the Z FLAG. This relies on the fact that FBUFR is never on page 0
    0FD9–  ↳ FOUT2
    INC HL23
    Increment the buffer pointer in Register Pair HL to where the next character will be placed
    0FDA-0FDB
    LD (HL),30H36 30
    Save an ASCII zero (0) at the location of the input buffer pointer in Register Pair HL EITHER because a “0” will ultimately go there (if we are processing in free format) OR to to reserve a space fro a floating dollar sign (if we are processing in fixed format)
    0FDC-0FDE
    LD A,(40D8H)LD A,(TEMP3)3A D8 40
    Load Register A with the format specification (held in a temporary storage location)
    0FDF
    LD D,A57
    Preserve the format specification into Register D
    0FE0
    RLA17
    Move the “free format” or “fixed format” bit into the Carry flag
    0FE1-0FE3
    LD A,(40AFH)LD A,(VALTYP)3A AF 40
    Since VNEG may have changed VALTYP, re-fetch it (as -32768 is and integer but 32768 is single-precision).
    0FE4-0FE6
    JP C,109AHJP C,FOUTFXDA 9A 10
    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
    JP Z,1092HJP Z,FOUTZRCA 92 10
    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
    JP NC,103DHJP NC,FOUFRVD2 3D 10
    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 132FHCALL FOUTCICD 2F 13
    Call the INTEGER TO ASCII routine at 1232F to convert the integer in the ACCumulator to ASCII and stores the ASCII string in the buffer pointed to in HL. We then fall through to FOUTZS

    This routine will zero suppress the digits in FBUFFR and asterisk fill and zero suppress if necessary.

    0FF5-0FF7–  ↳ FOUTZS
    LD HL,4130HLD HL,FBUFFR+121 30 41
    Load Register Pair HL with the starting address of the buffer, which will hold the SIGN
    0FF8
    LD B,(HL)46
    Load Register B with the sign (i.e., the character at the location of the buffer pointer in Register Pair HL)
    0FF9-0FFA
    LD C,20HLD C,” “0E 20
    Load Register C with a SPACE
    0FFB-0FFD
    LD A,(40D8H)LD A,(TEMP3)3A D8 40
    Load Register A with format specifications
    0FFE
    LD E,A5F
    Put the format specifications into Register E
    0FFF-1000
    AND 20HAND 0010 0000E6 20
    MASK the format specifications (by AND against 0010 0000) to see an asterisk fill is required
    1001-1002
    JR Z,100AHJR Z,FOTZS128 07
    If we do NOT need to do an asterisk fill in the ASCII string, JUMP to FOTZS1.
    1003
    LD A,B78
    If we’re here, then we do need to do the asterisk fill, so first lets see what the sign was. Load Register A with the character at the location of the input buffer pointer in Register B
    1004
    CP CB9
    Check to see if the character at the location of the input buffer pointer in Register A is a SPACE. The Z FLAG will be set if it was a SPACE
    1005-1006
    LD C,2AHLD C,”*”0E 2A
    Load Register C with a the fill character, which, in this case, will be a
    1007-1008
    JR NZ,100AHJR NZ,FOTZS120 01
    If the character at the location of the input buffer pointer in Register A isn’t SPACEthen JUMP to FOTS1 to change the SPACEwhere the sign would be into a .
    1009
    LD B,C41
    Load Register B with the character in Register C
    1001-1002
    JR Z,100AHJR Z,FOTZS128 07
    If we do NOT need to do an asterisk fill in the ASCII string, JUMP to FOTZS1.
    1003
    LD A,B78
    If we’re here, then we do need to do the asterisk fill, so first lets see what the sign was. Load Register A with the character at the location of the input buffer pointer in Register B
    1004
    CP CB9
    Check to see if the character at the location of the input buffer pointer in Register A is a SPACE. The Z FLAG will be set if it was a SPACE
    1005-1006
    LD C,2AHLD C,”*”0E 2A
    Load Register C with a the fill character, which, in this case, will be a
    1007-1008
    JR NZ,100AHJR NZ,FOTZS120 01
    If the character at the location of the input buffer pointer in Register A isn’t SPACEthen JUMP to FOTS1 to change the SPACEwhere the sign would be into a .
    1009
    LD B,C41
    Load Register B with the character in Register C
    100A–  ↳ FOTZS1
    LD (HL),C71
    Fill the zero or the sign with the filler character held in Register C at the location of the input buffer pointer in Register Pair HL
    100B
    RST 10HCHRGETD7
    We need the next character from the buffer. Using CHRGET is, however, a RAM saving method since there is no SPACEto skip, so the RST 10H really just does INC HL and LD A,(HL)
    100C-100D
    JR Z,1022HJR Z,FOTZS428 14
    If the character at the location of the input buffer pointer in Register Pair HL is the end of the input buffer character (00H) then we are done with the number. In this casse, we need to back up and put in a ZERO, so JUMP to FOTZS4. CHRGET would have set the ZERO FLAG on any 00H or :, but there are no :going to be found.
    100E-100F
    CP 45HCP “E”FE 45
    Check to see if the character at the location of the input buffer pointer in Register A is an E
    1010-1011
    JR Z,1022HJR Z,FOTZS428 10
    If the character is an Ewe need to put a 0in the floating point nontation with the C format 0, so JUMP to FOTZS4 to put into that ZERO.
    1012-1013
    CP 44HCP “D”FE 44
    Check to see if the character at the location of the input buffer pointer in Register A is a D
    1014-1015
    JR Z,1022HJR Z,FOTZS428 0C
    If the character is an Dwe need to put a 0in the floating point nontation with the C format 0, so JUMP to FOTZS4 to put into that ZERO.
    1016-1017
    CP 30HCP “0”FE 30
    Check to see if the character at the location of the input buffer pointer in Register A is a 0
    1018-1019
    JR Z,100AHJR Z,FOTZS128 F0
    If the character is a 0then we need to suppress it, so JUMP to FOTZS1.
    101A-101B
    CP 2CHCP “,”FE 2C
    Check to see if the character at the location of the input buffer pointer in Register A is a ,
    101C-101D
    JR Z,100AHJR Z,FOTZS128 EC
    If the character is a ,then we need to suppress it, so JUMP to FOTZS1.
    101E-101F
    CP 2EHCP “.”FE 2E
    Check to see if the character at the location of the input buffer pointer in Register A is a .
    1020-1021
    JR NZ,1025HJR NZ,FOTZS220 03
    If we do not have a ., then JUMP to FOTZS2
    1022–  ↳ FOTZS4
    DEC HL2B
    If we are here then we need to back up the string and put a 0before it. First, step back one location in the string
    1023-1024
    LD (HL),30HLD (HL),”0″36 30
    Save a 0at the location of the input buffer pointer in Register Pair HL
    1025–  ↳ FOTZS2
    LD A,E7B
    Next we need to check to see if we need a floating dollar sign. First, load the format specs into Register A
    1026-1027
    AND 10HAND 0001 0000E6 10
    Check to see if a $is to be included in the ASCII string
    1028-1029
    JR Z,102DHJR Z,FOTZS328 03
    If the Z FLAG is set, then we don’t have a dollar sign, so skip the next 2 instructions (which puts in a $) if a $isn’t to be included in the ASCII string
    102A
    DEC HL2B
    Need to add a $, so first we decrement the value of the input buffer pointer in Register Pair HL …
    102B-102C
    LD (HL),24HLD (HL),”$”36 24
    … and then put a $there
    102D–  ↳ FOTZS3
    LD A,E7B
    Next we need to check to see if we need a trailing sign. First, load the format specs into Register A
    102E-102F
    AND 04HAND 0000 0100E6 04
    Turn off every bit except Bit 2 (by ANDing against 00000100). If Bit 2 was on, then NZ will be set. If Bit 2 was off, then Z will be set. So this checks to see if the sign is to follow the ASCII string
    1030
    RET NZC0
    If the sign isn’t to follow the ASCII string, then we are done so RETurn
    1031
    DEC HL2B
    Decrement the value of the input buffer pointer in Register Pair HL
    1032
    LD (HL),B70
    Save the sign (in Register B) to the location of the input buffer pointer in Register Pair HL
    1033
    RETC9
    RETurn to CALLer

    1034 – LEVEL II BASIC MATH ROUTINE– “FOUINI”

    Initially set up the format specs and put in a SPACEfor the sign of a positive number. This routine gets called by the FLOATING to ASCII Conversion Routine (at 0FBEH) and by the BINARY to ASCII Conversion Routine (at 0FAFH)

    1034-1036–  ↳ FOUINI
    LD (40D8H),ALD (TEMP3),A32 D8 40
    Save the format specification (in Register A) to 40D8H.
    Note: 40D8H-40D9H holds the temporary storage location
    1037-1039
    LD HL,4130HLD HL,FBUFFR+121 30 41
    Set up a pointer into FBUFFR, starting at FBUFFR+1 just in case the number will overflow its field, in which case there is still room in FBUFFR for the %character.
    103A-103B
    LD (HL),” “36 20
    Save a SPACEat the location of the input buffer pointer in Register Pair HL
    103C
    RETC9
    RETurn to CALLer

    103D – LEVEL II BASIC MATH ROUTINE– “FOUFRV”

    This routine gets called by the FLOATING to ASCII Conversion Routine (0FBEH-0FC0H) if the value being converted is either Single Precision or Double Precision. This will print a single or double precision number in free format

    103D-103E–  ↳ FOUFRV
    CP 05HFE 05
    Company Register A against 05H and if A < 05H, set the C FLAG. With this, the CARRY FLAG will be set if we are dealing with a double precision number.
    103F
    PUSH HLE5
    Save the pointer to the buffer (held in Register Pair HL) to the STACK

    OK, this is fun. The next instructions are supposed to set Register D to be the counter for the number of digits to display. There is no agreement on what the next two instructions do:

    “Microsoft BASIC Decoded & Other Mysteries” says it turns 04 (SP) and 08 (SP) into 08 (SP) and 10 (DP) into 09 (SP) and 0B (DP)

    “Model III ROM Commented” says it turns D into 07 (SP) and 17 (DP)

    The original ROM Source Code comment says it turns D into 04 to 06 (SP) and 10 to 20 (DP)

    1040-1041
    SBC A,00HDE 00
    Adjust the value of the number type in Register A. It will be 04H if SINGLE PRECISION and it will be 08H if DOUBLE precision
    1042
    RLA17
    Multiply the value of the number type in Register A by two, so now A will be 08H if SINGLE precision and 0AH if DOUBLE precision
    1043
    LD D,A57
    Load Register D with the adjusted value of the number type in Register A
    1044
    INC D14
    Bump the value of the number type in Register D (so D will be 09H if SINGLE precision and 0BH if DOUBLE precision)
    1045-1047
    CALL 1201HCALL FOUTNVCD 01 12
    Go scale (normalize) the current value in ACCumulator so that all the significant digits will be in the integer portion (i.e., 99,999 <= X <= 999,999). Returns wihth A being the number of times the DOUBLE precision value was scaled up or down
    1048-104A
    LD BC,0300H01 00 03
    Load Register B to be the decimal point count of 3 (as we will assume it will come in E Notation), and Register C to be the comma count (currently 0).
    104B
    ADD A,D82
    Test to see if we are going to actually need E Notation by first adding the value in Register D to the value in Register A
    104C-104E
    JP M,1057HJP M,FOFRS1FA 57 10
    If D is less than .01 then we will need E Notation, so JUMP to FOFRS1
    104F
    INC D14
    Now we need to see if the number is too big. Bump the value in Register D
    1050
    CP DBA
    Compare the bumped Register D to Register A
    1051-1052
    JR NC,1057HJR NC,FOFRS130 04
    If the number is too big (i.e., greater than 10^D-1), JUMP to FOFRS1
    1053
    INC A3C
    If we are here, then we are able to display the number in fixed point notation, so we must bump the number of decimal point count
    1054
    LD B,A47
    Load Register B with the decimal point count (stored in Register A)
    1055-1056
    LD A,02H3E 02
    Set up for fixed point output. Fixed point notation has no exponent, so loading Register A with a two so that the next instruction will turn A (which is tracking the exponent) to 0.
    1057-1058–  ↳ FOFRS1
    SUB A,02HD6 02
    Compute the exponent value (which will be a zero if we were passing through), so now D-2 will be added to it
    1059
    POP HLE1
    Get the pointer to the string buffer from the STACK and put it in Register Pair HL
    105A
    PUSH AFF5
    Save the exponent value (currently in Register A) to the STACK
    105B-105D
    CALL 1291HCALL FOUTEDCD 91 12
    GOSUB to FOUTED to test to see if the number is .01 < number < .1 for purposes of putting a comma or decimal point in the input buffer if necessary
    105E-105F
    LD (HL),30HLD (HL),”0″36 30
    If the number is within that range, then add a 0at the location of the buffer pointer in Register Pair HL
    1060-1062
    CALL Z,09C9HCALL Z,INXHRTCC C9 09
    If there was no scaling, GOSUB to 09C9H to bump HL and return
    1063-1065
    CALL 12A4HCALL FOUTCVCD A4 12
    Next we need to convert the number to decimal digits by a GOSUB to FOUTCV which will convert the binary value in ACCumulator to ASCII, the result being stored in the input buffer pointer

    The FOFRS2 routine will suppress trailing zeroes.

    1066–  ↳ FOFRS2
    DEC HL2B
    Backspace to the last character by decrementing the value of the input buffer pointer in Register Pair HL
    1067
    LD A,(HL)7E
    Fetch the last character (at the location of the input buffer pointer in Register Pair HL)
    1068-1069
    CP 30HCP “0”FE 30
    Check to see if the value in Register A is a 0
    106A-106B
    JR Z,1066HJR Z,FOFRS228 FA
    If it is a zero, then we want to suppress it, so loop back to FOFRS2 to decrement again and keep suppressing ending zeroes.

    At this point, all trailing zeroes are now gone and HL points to the last non-zero character.

    106C-106D
    CP 2EHCP “.”FE 2E
    Check to see if the last character in the buffer (now that all ending zeroes have been supressed) is is a .
    106E-1070
    CALL NZ,09C9HCALL NZ,INXHRTC4 C9 09
    If its NOT a decimal point, GOSUB to bump the value of the input buffer pointer in Register Pair HL. Otherwise, HL is now sitting at the decimal point to suppress that character too.
    1071
    POP AFF1
    Restore the exponent from the STACK into Register A
    1072-10731
    JR Z,1093HJR Z,FOUTDN28 1F
    If the exponent is zero then we are done, so jump to 1093H. Otherwise, pass down to FOFLDN.

    1074 – LEVEL II BASIC MATH ROUTINE– “FOFLDN”

    This routine will put the exponent and a Dor Einto the buffer. On entry, Register A holds the exponent and it is assumed that all FLAGs are set correctly.

    1074–  ↳ FOFLDN
    PUSH AFF5
    Save the exponent (stored in A) to the STACK
    1075
    RST 20HGETYPEE7
    Determine the precision by checking the value of the current number type flag. In this case, this is a really cool trick. The purpose is to load a bit into the CARRY flag if we are going to display an Einstead of a D. We start off with 1/2 of the ascii value for the “D”, then, in 1 instruction, multiply it by 2 and add in the carry bit. So if the CARRY FLAG is off, then it is a “D” and if the CARRY FLAG is on, then it is an “E”
    1076-1077
    LD A,22H3E 22
    Load Register A with the starting value for a D or E character. In this case, A is set for 1/2 of the ASCII code for D
    1078
    ADC A,A8F
    Multiply the value of the character in Register A by two and add in the value of the Carry flag from the number type flag test. This will result with A it being a Dif the value is SINGLE precision and an Eif the value is DOUBLE precision
    1079
    LD (HL),A77
    Save the exponent designation (the Dor Ein Register A) at the location of the input buffer pointer in Register Pair HL
    107A
    INC HL23
    Bump the value of the buffer pointer in Register Pair HL, which is the first position of the exponent in the buffer
    107B
    POP AFF1
    Get the value of the exponent from the STACK and put it in Register A
    107C-107D
    LD (HL),2BHLD (HL),”+”36 2B
    Save a +at the location of the input buffer pointer in Register Pair HL. This is done to save bytes. Instead of testing for + or – and then putting in the appropriate character, a +is put in, and then it is overwritten if a should be there.
    107E-1080
    JP P,1085HJP P,FOUCE1F2 85 10
    If the exponent is positive, then the +we just put into the buffer (and HL is still pointing to that location) is good, so skip the next 3 instructions (by jumping to 1085H) if the exponent is positive
    1081-1082
    LD (HL),2DHLD (HL),”-“36 2D
    Save a (which is 2DH) at the location of the input buffer pointer in Register Pair HL, thus overwriting the initially placed +in that same location
    1083
    CPL2F
    If we are here then we have a negative exponent (or we would have jumped to 1085H back in 107EH), so convert the negative exponent to positive by reversing the value of the exponent in Register A
    1084
    INC A3C
    We also need to bump the value of the exponent in Register A by 1 when switching from negative to positive. We then pass through and rejoin where we would have jumped if the number had been positive.

    1085 – LEVEL II BASIC MATH ROUTINE– “FOUCE1” and “FOUCE2”

    This routine will calculate the two digit exponent.

    1085-1086–  ↳ FOUCE1
    LD B,2FHLD B,”0″-106 2F
    At this point, the exponent is positive. Next step is to load Register B with a 0minus one. This is because the next instruction, which is the top of a loop, bumps it by one.
    1087–  ↳ FOUCE2
    INC B04
    Top of a loop. Bump the value of the ASCII character in Register B. This is the start of a 3 Opcode routine to divide by 10 using compound subtraction
    1088-1089
    SUB A,0AHD6 0A
    Subtract ten from the value of the exponent in Register A
    108A-108B
    JR NC,1087HJR NC,FOUCE230 FB
    Loop until the value of the exponent in Register A is less than ten. B holds the quotient (e.g., the number of times the subtraction had to occur to get to a remainder less than 10)
    108C-108D
    ADD A,3AHC6 3A
    Since A is holding the remainder of the ‘divide-by-10’ routine above, add 3AH to it so that it will be an ASCII digit + 10
    108E
    INC HL23
    Bump the value of the buffer pointer in Register Pair HL
    108F
    LD (HL),B70
    Save the ASCII character in Register B (which is the first digit of the exponent in ASCII – the 10’s digit) at the location of the input buffer pointer in Register Pair HL
    1090
    INC HL23
    Bump the value of the buffer pointer in Register Pair HL
    1091
    LD (HL),A77
    Save the value of the ASCII character in Register A (which is the second digit of the exponent in ASCII – the 1’s digit) at the location of the input buffer pointer in Register Pair HL
    1092–  ↳ FOUTZR
    INC HL23
    Bump the value of the buffer pointer in Register Pair HL

    1093 – LEVEL II BASIC MATH ROUTINE– “FOUTDN”

    This routine will print a free format zero.

    1093-1094–  ↳ FOUTDN
    LD (HL),00H36 00
    Save an end of the ASCII string character (designated as 00H) at the location of the input buffer pointer in Register Pair HL
    1095
    EX DE,HLEB
    Since the FFXFLV routine will need the buffer pointer in DE instead of HL, swap those registers
    1096-1098
    LD HL,4130HLD HL,FBUFFR+121 30 41
    Load Register Pair HL with the starting address of the buffer pointer.
    Note: 4130H-4149H holds an internal print buffer
    1099
    RETC9
    DONE! RETurn to CALLer

    109A- LEVEL II BASIC MATH ROUTINE– “FOUTFX”

    This routine will print a number in fixed format.

    109A–  ↳ FOUTFX
    INC HL23
    Bump the value of the buffer pointer in Register Pair HL
    109B
    PUSH BCC5
    Save the field length specifiers (B has the number of #‘s before the current vale of the input buffer pointer and C has the number of #’s after) to the STACK
    109C-109D
    CP 04HFE 04
    Check to see if the current number type in ACCumulator is single or double precision
    109E
    LD A,D7A
    Load Register A with the format specifiers (held in Register D)
    109F-10A1
    JP NC,1109HP NC,FOUFXVD2 09 11
    If the current value in ACCumulator is either single precision or double precision then JUMP away to FOUFXV. If its an integer we will pass through.
    10A2
    RRA1F
    Rotate Register A so that we can check to see if this has to be printed in floating format or not. RRA rotates Register A right one bit, with Bit 0 going to CARRY and CARRY going to Bit 7.
    10A3-10A5
    JP C,11A3HJP C,FFXIFLDA A3 11
    If we need to print it in floating point (exponential) notation, JUMP TO FFXIFL.

    If we are here then we are going to print an integer in fixed format/fixed point notation.

    10A6-10A8
    LD BC,0603H01 03 06
    Load Register B to a decimal counte of 6 and Load Register C with a comma count of 3
    10A9-10AB
    CALL 1289HCALL FOUICCCD 89 12
    Go check to see if commas are needed. If no comma is needed, set C to zero
    10AC
    POP DED1
    Restore the field lengths (the number of #’s to the left and right of the decimal point) from the STACK into Register DE
    10AD
    LD A,D7A
    Load Register A with the number of digits requested to the left of the decimal point in Register D
    10AE-10AF
    SUB A,05HD6 05
    Since the maximim number of digits allowed for an integer to the left of the decimal point is 5, subtract 5 from the number of digits to the left of the decimal point requested. This will test to see if we have to print extra spaces because the field is too big.
    10B0-10B2
    CALL P,1269HCALL P,FOTZERF4 69 12
    If the field is too big, and we have to print extra spaces, we GOSUB to FOTZER to put in zeroes which will later be converted to either SPACEor by FOUTZS
    10B3-10B5
    CALL 132FHCALL FOUTCICD 2F 13
    Convert the number to decimal digits by GOSUBing to the INTEGER TO ASCII routine at 1232F (which converts the integer in ACCumulator to ASCII and stores the ASCII string in the buffer pointed to in HL)
    10B6–  ↳ FOUTTD
    LD A,E7B
    Next we need to test to see if we need a decimal point. First, load Register A with the number of digits to the right of the decimal point requested (which is stored in Register E)
    10B7
    OR AB7
    Check to see if there are any digits to the right of the decimal point requested and set the status flags accordingly
    10B8-10BA
    CALL Z,092FHCALL Z,DCXHRTCC 2F 09
    If the Z FLAG is set, then we do NOT need a decimal point, and need to backspace over it, so GOSUB to DCXHRT to decrement the value of the buffer pointer in Register Pair HL
    10BB
    DEC A3D
    Next we need to test to see how many trailing zeroes we need to print. Decrement the number of digits to the right of the decimal point in Register A.
    10BC-10BE
    CALL P,1269HCALL P,FOTZERF4 69 12
    If the POSITIVE flag is set, then print the trailing zeroes via GOSUB to 1269H

    10BF – LEVEL II BASIC MATH ROUTINE– “FOUTTS”

    This routine will finish up the printing of a fixed format number.

    10BF–  ↳ FOUTTS
    PUSH HLE5
    Save the current buffer pointer (stored in Register Pair HL) to the STACK. We then pss through to the FOUTTS routine to finish up the number.
    10C0-10C2
    CALL 0FF5HCALL FOUTZSCD F5 0F
    Go edit the ASCII string in the input buffer to suppress any zeroes, if needed.
    10C3
    POP HLE1
    Get the saved buffer pointer value from the STACK and put it in Register Pair HL
    10C4-10C5
    JR Z,10C8HJR Z,FFXIX128 02
    If the Z FLAG is set, then we do NOT have a trailing sign, so we JUMP away to 10C8H
    10C6
    LD (HL),B70
    So now we know a sign does follow the value so we save sign (held in Register B) at the location of the buffer pointer in Register Pair HL
    10C7
    INC HL23
    Bump the value of the input buffer pointer in Register Pair HL
    10C8-10C9–  ↳ FFXIX1
    LD (HL),00H36 00
    Terminate the buffer by saving an end of the ASCII string character (=00H) at the location of the input buffer pointer in Register Pair HL

    Now we need to check to see if the fixed format/fixed point number overflowed its field length. The location if the decimal point needs to be in TEMP2.

    10CA-10CC
    LD HL,412FHLD HL,FBUFFR21 2F 41
    Load Register Pair HL with the starting address of the input buffer pointer (which is 412FH) minus 1 (because the first instruction of the following common code is add 1 to HL)
    10CD–  ↳ FOUBE1
    INC HL23
    Bump the value of the buffer pointer in Register Pair HL
    10CE-10D0–  ↳ FOUBE5
    LD A,(40F3H)LD A,(TEMP2)3A F3 40
    Load Register A with the LSB of the address of the decimal point for the ASCII string. Why just the LSB? FBUFFR is only 35 bytes long, so we only need to check the LSB to see if the field is big enough.
    10D1
    SUB A,L95
    First, subtract the LSB of the input buffer pointer address in Register L from the value in Register A to see how much space we have taken up
    10D2
    SUB A,D92
    Next, set the flags by subtract the number of digits to the left of the decimal point in Register D from the adjusted value in Register A to determine if we have taken the right amount of space. Z FLAG will mean we did!
    10D3
    RET ZC8
    If we have taken the right amount of space, then we are done, so we RETurn
    10D4
    LD A,(HL)7E
    If we are here, then we took too much space. How do we know it is too much instead of just “different”? Well, we started checking from the beginning of the buffer, and the field must be small enough to fit into the buffer. With this, we need to fetch the next character from the buffer into Register A
    10D5-10D6
    CP 20HCP ” “FE 20
    Check to see if the character at the location of the buffer pointer in Register A is a space, meaning we can just ignore the character to make the field shorter
    10D7-10D8
    JR Z,10CDHJR Z,FOUBE128 F4
    If it is a space, then LOOP back to FOUBE1 to ignore it
    10D9-10DA
    CP 2AHCP “*”FE 2A
    Check to see if the character at the location of the input buffer pointer in Register A is a , meaning we can just ignore the character to make the field shorter
    10DD
    DEC HL2B
    Since we want to ignore ‘s we decrement the value of the input buffer pointer in Register Pair HL so it will get re-tested
    10DE
    PUSH HLE5
    Save the value of the buffer pointer in Register Pair HL to the STACK

    10DF – LEVEL II BASIC MATH ROUTINE– “FOUBE2”

    In this routine, we check to see if we can ignore the leading zero before a decimal point. We can do this if if we see the following: (in order)

    +,-a sign (either “-” or “+”)[optional]
    $a dollar sign[optional]
    0a zero[mandatory]
    .a decimal point[mandatory]
    0-9another digit[mandatory]

    If we see a leading zero, it must be the one before a decimal point or else FOUTZS would have akready suppressed it. In that case, we just INC HLover the character following the zero, and not have to check for the decimal point explicitly.
    10DF–  ↳ FOUBE2
    PUSH AFF5
    Save the the current character (which is the value in Register Pair AF) to the STACK. This also saves the ZERO FLAG.
    10E0-10E2
    LD BC,10DFHLD BC,FOUBE201 DF 10
    Load Register Pair BC with the return address for use in case we have a , a +, or a $
    10E3
    PUSH BCC5
    Save the return address in Register Pair BC to the STACK
    10E4
    RST 10HCHRGETD7
    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.
    10E5-10E6
    CP 2DHCP “-“FE 2D
    Check to see if the character at the location of the input buffer pointer in Register A is a
    10E7
    RET ZC8
    Return (to 10DFH) if the character at the location of the input buffer pointer in Register A is a
    10E8-10E9
    CP 2BHCP “+”FE 2B
    Check to see if the character at the location of the input buffer pointer in Register A is a +
    10EA
    RET ZC8
    Return (to 10DFH) if the character at the location of the input buffer pointer in Register A is a +
    10EB-10EC
    CP 24HCP “$”FE 24
    Check to see if the character at the location of the input buffer pointer in Register A is a $
    10ED
    RET ZC8
    Return (to 10DFH) if the character at the location of the input buffer pointer in Register A is a $
    10EE
    POP BCC1
    We don’t need a shortcut to jump to 10DFH anymore, so let’s get rid of the now unneeded return address from the STACK
    10EF-10F0
    CP 30HCP “0”FE 30
    Check to see if the character at the location of the input buffer pointer in Register A is a 0
    10F1-10F2
    JR NZ,1102HJR NZ,FOUBE420 0F
    If the character at the location of the input buffer pointer in Register A isn’t a 0then we can no longer just get rid of the characters, so JUMP to FOUBE4 to continue
    10F3
    INC HL23
    Bump the value of the input buffer pointer in Register Pair HL so that we skip over the decimal point to the next character
    10F4
    RST 10HCHRGETD7
    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.
    10F5-10F6
    JR NC,1102HJR NC,FOUBE430 0B
    If the character after the decimal point is not a digit then we can’t shorten the field anymore, so JUMP to FOUBE4
    10F7
    DEC HL2B
    If we didn’t jump away, then we can shorten the field by one, so DECrement the value of the buffer pointer in Register Pair HL to backspace
    10F8
    LD BC,772BH01 2B 77
    Z-80 Trick! The byte at this memory location, 01H, is there to turn the real instruction that follows in 10F9H into a harmless LD BC,xxxx. This way, if you are processing straight down in order, it skips the next command at 10F9H (in this case a DEC HL) because it wasn’t a command, it was a hex number to be loaded into BC! Instead, if you jump to 10F9H, you skip this byte and it is an DEC HL

    10F9 – LEVEL II BASIC MATH ROUTINE– “FOUBE3”

    If we can get rid of the zero, we put the characters on the STACK back into the buffer one position in front of where they originally were.

    Note that the maximum number of STACK levels this uses is three — one for the last entry flag, one for a possible sign, and one for a possible dollar sign.

    We don’t have to worry about the first character being in the buffer twice because the pointer when FOUT exits will be pointing to the second occurance.

    10F9–  ↳ FOUBE3
    DEC HL2B
    If passing through, this instruction won’t get executed. If JUMPed to, decrement the value of the buffer pointer in Register Pair HL (we needed that Z-80 trick to avoid a double backspace if passing through)
    10FA
    LD (HL),A77
    If passing through, this instruction won’t get executed. If JUMPed to, save the character in Register A at the location of the input buffer pointer in Register Pair HL
    10FB
    POP AFF1
    Get the character from the STACK and put it in Register Pair AF
    10FC-10FD
    JR Z,10F9HJR Z,FOUBE328 FB
    If the Z FLAG is set, then we LOOP back to FOUBE3 to put the character back into the buffer
    10FE
    POP BCC1
    Restore the buffer pointer from the top of the STACK into Register Pair BC
    10FF-1101
    JP 10CEHJP FOUBE5C3 CE 10
    LOOP back to 10CEH to see if the field is NOW small enough.

    1102 – LEVEL II BASIC MATH ROUTINE– “FOUBE4”

    If the number is too big for the field, we wind up here to deal with that.

    1102–  ↳ FOUBE4
    POP AFF1
    Restore the character from the STACK and put it in Register Pair AF
    1103-1104
    JR Z,1102HJR Z,FOUBE428 FD
    If the Z FLAG is set, then LOOP back 1 instruction to leave the number in the buffer alone
    1105
    POP HLE1
    Get the starting address of the field from the STACK and put it in Register Pair HL. This will be the pointer to the beginning of the number – 1
    1106-1107
    LD (HL),25HLD (HL),”%”36 25
    Show that we have overflowed the field by putting a %character at the front.
    1108
    RETC9
    All done! RETurn to CALLer

    1109 – LEVEL II BASIC MATH ROUTINE– “FOUFXV”

    This is where the PRINT USING routine will print a single or double precision number in a fixed format

    1109–  ↳ FOUFXV
    PUSH HLE5
    Save the buffer pointer in Register Pair HL to the STACK
    110A
    RRA1F
    Rotate Register A so that the “fixed notation” or “floating notation” flag bit moves into the CARRY FLAG for testing. RRA rotates Register A right one bit, with Bit 0 going to CARRY and CARRY going to Bit 7.
    110B-110D
    JP C,11AAHJP C,FFXFLVDA AA 11
    If the CARRY FLAG was set, then we know we are printing the number in “E” notation, so JUMP to FFXFLV
    110E-110F
    JR Z,1124HJR Z,FFXSFX28 14
    If the Z FLAG was set, the we have a SINGLE PRECISION number to print, so JUMP to FFXSFC to do that

    If we are here, then we are printing a DOUBLE PRECISION number in fixed format/fixed point notation

    1110-1112
    LD DE,1384HLD DE,FFXDXM11 84 13
    Load Register Pair DE with the address of the DOUBLE PRECISION value to be compared to the current value in ACCumulator. Register Pair DE points to a double precision constant equal to 1D16
    1113-1115
    CALL 0A49HCALL DCOMPDCD 49 0A
    Since we can’t print a number which is greater than 10^16 in fixed format, GOSUB to compare the double precision constant pointed to by Register Pair DE (which is 1Dl6) to the double precision value in ACCumulator
    1116-1117
    LD D,10H16 10
    Load Register D with the maximum length of a double precision value (which is 16 in decimal)
    1118-111A
    JP M,1132HJP M,FFXSDCFA 32 11
    If the M FLAG is set, then the number in the ACCumulator is small enough to print (i.e., less than or equal to 1Dl6), so JUMP to 1132H

    111B – LEVEL II BASIC MATH ROUTINE– “FFXSDO”

    This routine will print a number which is greaster than 10^16 in free format with a percent sign

    111B–  ↳ FFXSDO
    POP HLE1
    Get the current buffer pointer from the STACK and put it in Register Pair HL
    111C
    POP BCC1
    Get the field specifier from the STACK and put it in Register Pair BC, resulting in B containing the number of #‘s before and C containing the number of #‘s after
    111D-111F
    CALL 0FBDHCALL FOUTCD BD 0F
    Print the number in free format via a GOSUB to FOUT (which will convert a double precision value in ACCumulator to an ASCII string)
    1120
    DEC HL2B
    Decrement the input buffer pointer in Register Pair HL to point in front of the number
    1121-1122
    LD (HL),25HLD (HL),”%”36 25
    Save a %character at the location of the input buffer pointer in Register Pair HL
    1123
    RETC9
    All done! RETurn to CALLer

    1124 – LEVEL II BASIC MATH ROUTINE– “FFXSFX”

    This routine will print a SINGLE PRECISION number in fixed format/fixed point notation

    1124-1126–  ↳ FFXSFX
    LD BC,B60EH01 0E B6
    Load Register Pair BC/DE with 1E16
    1127-1129
    LD DE,1BCAH11 CA 1B
    Register Pairs BC and DE now hold a single precision constant equal to 1×10^16
    112A-112C
    CALL 0A0CHCALL FCOMPCD 0C 0A
    Call the SINGLE PRECISION COMPARISON routine at routine at 0A0CH which algebraically compares the single precision value in BC/DE to the single precision value ACCumulator.
    The results are stored in A as follows:
    If ACCumulator = BCDEA=00
    If ACCumulator > BCDEA=01
    If ACCumulator < BCDEA=FF
    112D-112F
    JP P,111BHJP P,FFXSDOF2 1B 11
    If the P FLAG is set then the number is too big, so we need to JUMP to FFXSDO to print it in free format with a %overflow symbol
    1130-1131
    LD D,06H16 06
    Now we know that the SINGLE precision value in ACCumulator is less than 1×10^16. Load Register D with the maximum length of a single precision value (which is 6) and then fall through to the FFXSDC routine

    1124 – LEVEL II BASIC MATH ROUTINE– “FFXSDC”

    This routine will print a SINGLE PRECISION or DOUBLE PRECISION number in fixed format/fixed point notation

    1132-1134–  ↳ FFXSDC
    CALL 0955HCALL SIGNCD 55 09
    GOSUB to SIGN to see if we have a ZERO in the ACCumulator
    1135-1137
    CALL NZ,1201HCALL NZ,FOUTNVC4 01 12
    If we do NOT have a ZERO, then GOSUB to FOUTNV to normalize the number so that all digits to be printed are located in the initeger part
    1138
    POP HLE1
    Get the buffer pointer from the STACK and put it in Register Pair HL
    1139
    POP BCC1
    Get the value from the STACK and put it in Register Pair BC, resulting in B containing the number of #‘s before and C containing the number of #‘s after
    113A-113C
    JP M,1157HJP M,FFXXVSFA 57 11
    If the exponent is negative, JUMP to FFXXVS to handle that

    This routine will print a number that has no fractional digits

    113D
    PUSH BCC5
    Save the value in Register Pair BC (B was the number of #‘s before and C is the number of #‘s after) to the STACK
    113E
    LD E,A5F
    Load Register E with the exponent

    If the field length is higher than the number of characters we actually have, we are going to need to put in that number of leading zeroes.

    113F
    LD A,B78
    Load Register A with the number of digits before the decimal point requested
    1140
    SUB A,D92
    Subtract the maximum length for the current number type in Register D (which is 6) from the number of digits requested in Register A
    1141
    SUB A,E93
    Subtract the number of times the current value in ACCumulator was divided in Register E from the adjusted value in Register A
    1142-1144
    CALL P,1269HCALL P,FOTZERF4 69 12
    If B-D-E is still POSITIVE, then we have to fill with some zeroes so GOSUB to 1269H to put leading zeros into the input buffer if necessary
    1145-1147
    CALL 127DHCALL FOUTCDCD 7D 12
    Next, set up the decimal point and comma counts via a GOSUB to DOUTCD
    1148-114A
    CALL 12A4HCALL FOUTCVCD A4 12
    Then, convert the number to decimal digits via a GOSUB to FOUTCV to convert the integer portion of the current value in ACCumulator to an ASCII string
    114B
    OR EB3
    Merge in the number of digits after the number, if the field is big enough of course.
    114C-114E
    CALL NZ,1277HCALL NZ,FOTZECC4 77 12
    If there are number to be put there (i.e., merging in E leaves a number greater than Zero), then GOSUB to FOTZEC to put trailing zeros into the input buffer if necessary
    114F
    OR EB3
    Check to see if commas or the decimal point is needed
    1150-1152
    CALL NZ,1291HCALL NZ,FOUTEDC4 91 12
    Go put commas and the decimal point into the input buffer if necessary
    1153
    POP DED1
    Retrieve the field length specs from the STACK and put it in Register Pair DE
    1154-1156
    JP 10B6HJP FOUTTDC3 B6 10
    Jump to 10B6H to check the size, run zero suppression, and convert the fractional portion of the number to ASCII to finish up

    1157 – LEVEL II BASIC MATH ROUTINE– “FFXXVS”

    This routine will print a SINGLE PRECISION or DOUBLE PREVISION number that has fractional digits

    1157–  ↳ FFXXVS
    LD E,A5F
    Preserve the exponent into Register E
    1158
    LD A,C79
    Prepare to divide by 10 the right number of times so that the result will be rounded correctly and have the correct number of significant digits. First, load Register A with the number of digits requested to the right of the decimal point
    1159
    OR AB7
    Check to see if any digits to the right of the decimal point was requested
    115A-115C
    CALL NZ,0F16HCALL NZ,DCRARTC4 16 0F
    Go decrement the number of digits requested to the right of the decimal point if necessary
    115D
    ADD A,E83
    Add the number of times the current value was multiplied in Register E to the number of digits to the right of the decimal point requested in Register A
    115E-1160
    JP M,1162HJP M,FFXXV8FA 62 11
    If the value in ACCumulator must be scaled down then skip the next instruction, as we want a ZERO FLAG only if the result was not negative
    1161
    XOR AAF
    Zero Register A
    1162–  ↳ FFXXV8
    PUSH BCC5
    Save the field specifications held in Register Pair BC (B was the number of #‘s before and C is the number of #‘s after) to the STACK
    1163
    PUSH AFF5
    Save the the scale count (held in Register Pair AF) to the STACK
    1164-1166–  ↳ FFXXV2
    CALL M,0F18HCALL M,FINDIVFC 18 0F
    Top of a divide loop. GOSUB to 0F18H to divide the value in ACCumulator by ten, A times, if necessary
    1167-1169
    JP M,1164HJP M,FFXXV2FA 64 11
    Loop until the value in ACCumulator is properly adjusted. When this is done, A will hold the number of times it was divided by 10
    116A
    POP BCC1
    Get the original scale count from the STACK and put it in Register Pair BC
    116B
    LD A,E7B
    We now need to test as to whether the number has integer digits or not. First, load Register A with the number of times the value in ACCumulator was multiplied in Register E
    116C
    SUB A,B90
    Subtract the value in Register B from the value in Register A
    116D
    POP BCC1
    Get the value from the STACK and put it in Register Pair BC, resulting in B containing the number of #‘s before and C containing the number of #‘s after
    116E
    LD E,A5F
    Calculate the number of decimal places before the number ends by first loading Register E with the adjusted scale factor value in Register A …
    116F
    ADD A,D82
    … and then adding the length of the maximum size for the current value in Register D to the adjusted scale factor value in Register A. This will set the sign flag
    1170
    LD A,B78
    Load Register A with the number of #‘s before (stored in B)
    1171-1173
    JP M,117FHJP M,FFXXV3FA 7F 11
    Jump to 117FH if there are no digits to the left of the decimal point

    This routine will print numbers with integer digits, and will print some leading zeroes if the field is bigger than the number of digits we need to print.

    1174
    SUB A,D92
    We now know there are leading digits so, subtract the maximum length for the current value in Register D (6 for SINGLE precision and 10 for DOUBLE precision) from the adjusted value in Register A
    1175
    SUB A,E93
    Then, subtract the adjusted scale value in Register E from the adjusted value in Register A
    1176-1178
    CALL P,1269HCALL P,FOTZERF4 69 12
    If that subtraction leads to a positive number, go put leading zeros into the input buffer
    1179
    PUSH BCC5
    Save the field specs held in Register Pair BC (B was the number of #‘s before and C is the number of #‘s after) to the STACK
    117A-117C
    CALL 127DHCALL FOUTCDCD 7D 12
    GOSUB to set up BC for decimal point and comma counters
    117D-117E
    JR 1190HJR FFXXV618 11
    Jump to 1190H to convert the digits before the decimal point and trim the number

    117F – LEVEL II BASIC MATH ROUTINE– “FFXXV3”

    This routine will print a number without integer digits.

    117F-1181–  ↳ FFXXV3
    CALL 1269HCALL FOTZERCD 69 12
    Go put leading zeros (as needed) into the input buffer
    1182
    LD A,C79
    Load Register A with the number of bytes requested to the right of the decimal point (in Register C) because C is about to get wiped
    1183-1185
    CALL 1294HCALL FOUTDPCD 94 12
    GOSUB to 1294H to put a decimal point into the input buffer
    1186
    LD C,A4F
    Reload Register C with the number of digits requested to the right of the decimal point in Register A
    1187
    XOR AAF
    Next we need to calculate how many zeroes to put between the decimal point and the first digit, so start by zeroing Register A
    1188
    SUB A,D92
    Then – subtract the maximum length for the current value in Register D from the value in Register A
    1189
    SUB A,E93
    Then – subtract the value in Register E from the adjusted value in Register A
    118A-118C
    CALL 1269HCALL FOTZERCD 69 12
    GOSUB to put that many zeroes into the buffer
    118D
    PUSH BCC5
    Save the value in Register Pair BC (B is the exponent and C is the number of #‘s after) to the STACK
    118E
    LD B,A47
    Load Register B (i.e., the decimal place count) with the value in Register A (which is 0)
    118F
    LD C,A4F
    Load Register C (i..e, the comma count) with the value in Register A (which is 0)
    1190-1192–  ↳ FFXXV6
    CALL 12A4HCALL FOUTCVCD A4 12
    GOSUB to 12A4H to convert the integer portion of the SINGLE precision value in ACCumulator to an ASCII string. These will be the decimal digits.
    1193
    POP BCC1
    Get the number of #‘s before and number of #‘s after and put it back in Register Pair BC
    1194
    OR CB1
    Check to see if we need to print any zeroes after the last digit (i.e., if there are any digits to the right of the decimal point requested) and set the status accordingly
    1195-1196
    JR NZ,119AHJR NZ,FFXXV720 03
    If the NZ FLAG is set, then there are digits to the right of the decimal point to fill, so JUMP to 119AH to do that
    1197-1199
    LD HL,(40F3H)LD HL,(TEMP2)2A F3 40
    Now we know that there are no digits to the right of the decimal point. Load Register Pair HL with the position of the decimal point (which is stored in 40F3H).
    Note: 40F3H-40F4H is a temporary storage location

    119A – LEVEL II BASIC MATH ROUTINE– “FFXXV7”

    This routine will print trailing zeroes.

    119A–  ↳ FFXXV7
    ADD A,E83
    Add the value in Register E to the value in Register A to get the number of digits before the decimal point
    119B
    DEC A3D
    Decrement the adjusted value in Register A
    119C-119E
    CALL P,1269HCALL P,FOTZERF4 69 12
    If dropping A by 1 still results in a positive number, GOSUB 1269H to put that number of zeros into the input buffer
    119F
    LD D,B50
    Load Register D with the number of digits to the left of the decimal point requested (from Register B)
    11A0-11A2
    JP 10BFHJP FOUTTSC3 BF 10
    Jump to 10BFH to finish up

    11A3 – LEVEL II BASIC MATH ROUTINE– “FFXIFL”

    This routine will print an integer in fixed format/floating point notation.

    11A3–  ↳ FFXIFL
    PUSH HLE5
    Save the current position of the buffer (in Register Pair HL) to the STACK
    11A4
    PUSH DED5
    Generally save Register Pair DE to be POPped after the CALL. DE currently holds the format specs
    11A5-11A7
    CALL 0ACCHCALL CONSICD CC 0A
    GOSUB 0ACCH to convert the integer value in ACCumulator to a SINGLE precision value
    11A8
    POP DED1
    Restore DE from the STACK
    11A9
    XOR AAF
    Zero Register A, clear the status flags. This will denote to the next routine that we are printing a number as a SINGLE PRECISION number, and then fall into the FFXFLV routine

    11AA – LEVEL II BASIC MATH ROUTINE– “FFXFLV”

    This routine will print a SINGLE or DOUBLE PRECISION number in fixed format/floating point notation.

    11AA-11AC–  ↳ FFXFLV
    JP Z,11B0HJP Z,FFXSFLCA B0 11
    If we have a SINGLE PRECISION number (because the Z FLAG is set), Jump to 11B0H to set the flags appropriately
    11AD-11AE
    LD E,10H1E 10
    We know we have a DOUBLE PRECISION so load Register E with the maximum length of a double precision value (which is 16)
    11AF-11B2
    LD BC,1E06H01 1E 06
    Z-80 Trick! If passing through then this just modifies the Register Pair BC. However, if JUMPing to 11B0, a LD E,06H occurs, changing E
    11B0-11B1–  ↳ FFXSFL
    LD E,06H1E 06
    Load Register E with the maximum length of a single precision value (which is 6)
    11B2-11B4
    CALL 0955HCALL SIGNCD 55 09
    GOSUB 0955H to check to see if we have a zero in the ACCumulator
    11B5
    SCF37
    Set the Carry flag to determine if we are printing a zero or not. This works because FOUTNV exits with the NC FLAG set
    11B6-11B8
    CALL NZ,1201HCALL NZ,FOUTNVC4 01 12
    If we do not have a zero, then we need to normalize the number so that all digits to be printed are in the integer portion, so GOSUB to 1201H to scale the current value in ACCumulator
    11B9
    POP HLE1
    Get the buffer position from the STACK and put it in Register Pair HL
    11BA
    POP BCC1
    Get the number of #‘s before and the number of #‘s after from the STACK and put it in Register Pair BC
    11BB
    PUSH AFF5
    Save the exponent in Register Pair AF to the STACK
    11BC
    LD A,C79
    We need to calculate how many significant digits we must print, so load Register A with the number of digits to the right of the decimal point requested (stored in Register C)
    11BD
    OR AB7
    Set the status so we can see if there are any digits to the right of the decimal point requested through a zero register
    11BE
    PUSH AFF5
    Save the original trailing digit count (in Register Pair AF) to the STACK
    11BF-11C1
    CALL NZ,0F16HCALL NZ,DCRARTC4 16 0F
    If the trail count is not zero, then GOSUB to 0F16H to decrement the number of digits requested to the right of the decimal point in Register A
    11C2
    ADD A,B80
    Add the number of digits requested for the left of the decimal point in Register B to the number of digits requested to the right of the decimal point in Register A
    11C3
    LD C,A4F
    Load Register C with the total digit count (held in Register A)
    11C4
    LD A,D7A
    Load Register A with the value of the edit flag in Register D
    11C5-11C6
    AND 04HAND 0000 0100E6 04
    Check to see if the sign follows the ASCII string (i.e., is a “trailing” sign)
    11C7-11C8
    CP 01HFE 01
    Set the Carry flag according to the sign following the ASCII string test (it will be No Carry if a sign follows, and will be CARRY if A=0)
    11C9
    SBC A,A9F
    If we have a trailing sign, this will set Register D to 0. Otherwise, D will be FFH if we don’t have a trailing sign.
    11CA
    LD D,A57
    Load Register D with those results
    11CB
    ADD A,C81
    Add the value in Register C to the value in Register A so as to set the number of significant digits to print
    11CC
    LD C,A4F
    Load Register C with the adjusted value in Register A
    11CD
    SUB A,E93
    If the number of significant digits to print is less than E, then we have to get rid of some numbers! Subtract the value in Register E from the adjusted value in Register A so that A will now contain the number of times to divide by 10
    11CE
    PUSH AFF5
    Save the divisor count (from Register Pair AF) to the STACK. This is the result of the comparison of the number of significant digits and the number of digits we will actually print.
    11CF
    PUSH BCC5
    Save the “B” field spec and the number of significant digits (from Register Pair BC) to the STACK
    11D0-11D2–  ↳ FFXLV1
    CALL M,0F18HCALL M,FINDIVFC 18 0F
    GOSUB 0F18H to divide the current value in ACCumulator by ten, Register A number of times
    11D3-11D5
    JP M,11D0HJP M,FFXLV1FA D0 11
    Loop back 1 instruction (divide by 10) until the division has been completed
    11D6
    POP BCC1
    Retrieve the “B” field spec and the number of significant digits from the STACK back into Register Pair BC
    11D7
    POP AFF1
    Get the number of trailing zeroes to print from the STACK and put it in Register Pair A
    11D8
    PUSH BCC5
    Save the “B” field spec and the number of significant digits (from Register Pair BC) to the STACK
    11D9
    PUSH AFF5
    Save the number of trailing zeroes to print to the STACK
    11DA-11DC
    JP M,11DEHJP M,FFXLV3FA DE 11
    Skip the next instruction (i.e., jump to 11DEH) if there are any trailing zeroes
    11DD
    XOR AAF
    Zero Register A and all status flags
    11DE–  ↳ FFXLV3
    CPL2F
    Make the trailing zero count posivite by inverting the value in Register A
    11DF
    INC A3C
    Bump the value in Register A so that it will be positive
    11E0
    ADD A,B80
    Set the decimal place count by adding the number of digits requested to the left of the decimal point in Register B to the adjusted value in Register A
    11E1
    INC A3C
    Bump the adjusted value in Register A
    11E2
    ADD A,D82
    Take into account if the sign is trailing by adding the value of the maximum length for the current number type in Register D (6 for single precision, 16 fo double precision) to the adjusted value in Register A
    11E3
    LD B,A47
    Copy Register A into Register B so that B holds the number of digits before the decimal point
    11E4-11E5
    LD C,00H0E 00
    Set the comma count to zero by loading Register C with zero (so that there are no commas)
    11E6-11E8
    CALL 12A4HCALL FOUTCVCD A4 12
    GOSUB to 12A4H to convert the current value in ACCumulator to decimal digits
    11E9
    POP AFF1
    Get the number of #’s before from the STACK and put it in Register Pair AF
    11EA-11EC
    CALL P,1271HCALL P,FOTZNCF4 71 12
    GOSUB 1271H to put zeros into the trailing input buffer
    11ED
    POP BCC1
    Get the number of #’s before and the number of #‘s after from the STACK and put it in Register Pair BC
    11EE
    POP AFF1
    Get the C count (numbers before the decimal point) from the STACK and put it in Register Pair A and restore the FLAGS
    11EF-11F1
    CALL Z,092FHCALL Z,DCXHRTCC 2F 09
    If C = 0 then the last character was a decimal point, so ignore it via a GOSUB to 092FH to decrement the input buffer pointer in Register Pair HL if there are none
    11F2
    POP AFF1
    Get the exponent back from from the STACK and put it in Register Pair AF
    11F3-11F4
    JR C,11F8HJR C,FFXLV238 03
    If the number is zero, then the exponent is zero, so JUMP to 11F8H (to add the exponent) if the carry flag was set
    11F5
    ADD A,E83
    Otherwise, we need to scale the number – so first add the value in Register E to the value in Register A
    11F6
    SUB A,B90
    Subtract the number of digits to the left of the decimal point requested in Register B from the adjusted value in Register A
    11F7
    SUB A,D92
    Subtract the value in Register D from the value in Register A to get the size of the exponent
    11F8–  ↳ FFXLV2
    PUSH BCC5
    Save the “B” field spec to the STACK
    11F9-11FB
    CALL 1074HCALL FOFLDNCD 74 10
    Put the exponent into the buffer via a GOSUB to 1074H to figure the value of the exponent for the current value in ACCumulator
    11FC
    EX DE,HLEB
    Swap DE and HL so that the pointer to the end of the buffer is put into Register Pair HL just in case we have a trailing sign we need to add.
    11FD
    POP DED1
    Restore the “B” field spec into Register D in case we need to put on a trailing sign.
    11FE-1200
    JP 10BFHJP FOUTTSC3 BF 10
    Jump to 10BFH to put on the trailing sign and finish up

    1201 – Test the magnitude of SP and DP numbers, and clear the times the value was scaled– “FOUTNV”

    This routine will scale (normalize) the number in the accumulator so that all the digits are in the integer part (i.e., between 99,999 and 999,999). The signed base 10 exponent is returned in Register A. Registers D and E are unchanged.

    1201–  ↳ FOUTNV
    PUSH DED5
    Save the value in Register Pair DE to the STACK. We are going to pop this back at the end of the routine.
    1202
    XOR AAF
    Zero Register A, which will be the exponent
    1203
    PUSH AFF5
    Save the exponent in Register Pair A to the STACK
    1204
    RST 20HGETYPEE7
    We need to check the value of the current number type flag, so we call the TEST DATA MODE routine at RST 20H.
    NOTE:The RST 20H routine determines the type of the current value in ACCumulator and returns a combination of STATUS flags and unique numeric values in the A Register according to the data mode flag (40AFH). The results are returned as follows:
    • 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.
    1205-1207
    JP PO,1222HJP PO,FOUNDBE2 22 12
    If that test shows we have a SINGLE PRECISION number (through getting a Parity-Odd flag), jump to 1222H to handle. Otherwise, pass through
    1208-120A–  ↳ FORBIG
    LD A,(4124H)LD A,(FAC)3A 24 41
    At this point we know we have a DOUBLE precision value. Load Register A with the value of the exponent for the double precision value in ACCumulator
    120B-120C
    CP 91HFE 91
    Check to see if the double precision value in ACCumulator uses is less than 1D5 (i.e., the integer portion of the double precision value)
    120D-120F
    JP NC,1222HP NC,FOUNDBD2 22 12
    If the double precision value in ACCumulator is not less than 1D5, then ship over the following multiplcation code and go to FOUNDB.
    1210-1212
    LD DE,1364HLD DE,TENTEN11 64 13
    Load Register Pair DE with 1D10
    1213-1215
    LD HL,4127HLD HL,ARGLO21 27 41
    In praration for VMOVE and DMULT, point HL to REG2
    1216-1218
    CALL 09D3HCALL VMOVECD D3 09
    GOSUB to 09D3H to move the double precision constant into REG2
    1219-121B
    CALL 0DA1HCALL DMULTCD A1 0D
    GOSUB to 0DA1H to call the DOUBLE PRECISION MULTIPLY routine at 0DA1H (which multiplies the double precision value in ACCumulator by the value in REG 2. The product is left in ACCumulator)
    121C
    POP AFF1
    Retrieve the original exponent from the STACK into Register A
    121D-121E
    SUB A,0AHD6 0A
    Subtract ten from the value in Register A to do a proper offset for an exponent
    121F
    PUSH AFF5
    Save the adjusted exponent (held in Register A) to the STACK
    1220-1221
    JR 1208HJR FORBIG18 E6
    Force it to be bigger via a JUMP to 1208H so as to loop until the integer portion exceeds 2e16

    1222 – LEVEL II BASIC MATH ROUTINE– “FOUNDB”

    There is a big bug in this routine which was fixed in v1.2 of the ROM. The fixing of that bug caused a renumbering from 1228H-124CH. The numbering here will show both.

    1222-1224–  ↳ FOUNDB
    CALL 124FHCALL FOUNVCCD 4F 12
    Check to see if the number in the ACCumulator is too big or too small via a GOSUB to 124FH to compare the current value in ACCumulator to 999999.5
    1225–  ↳ FOUNV1
    RST 20HGETYPEE7
    In order to determine if the ACCumulator is big enough, we need to know what kind of value we have in the ACCumulator so call the TEST DATA MODE routine at RST 20H.
    NOTE:The RST 20H routine determines the type of the current value in ACCumulator and returns a combination of STATUS flags and unique numeric values in the A Register according to the data mode flag (40AFH). The results are returned as follows:
    • 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.
    1226-1228
    JP PE,1234HJP PE,FOUNV4EA 34 12
    If that test shows we have a DOUBLE PRECISION or a STRING, jump to 1234H
    1226-1228
    In ROM 1.2 this is a big bug fix. Now if the shows we do NOT have a STRING, jump to 1233H

    The next two instructions load BCDE with 99999.95 so as to check to see if the number in FAC is too big.

    1229-122B
    1228-122A
    LD BC,9143H01 43 91
    Load Register Pair BC with the exponent and the MSB of a single precision constant
    122C-122E
    122B-122D
    LD DE,4FF9H11 F9 4F
    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 constant of 99,999.945
    122F-1231
    122E-1230
    CALL 0A0CHCALL FCOMPCD 0C 0A
    GOSUB to routine at 0A0CH which algebraically compares the single precision value in BC/DE to the single precision value ACCumulator.
    The results are stored in A as follows:
    If ACCumulator = BCDEA=00
    If ACCumulator > BCDEA=01
    If ACCumulator < BCDEA=FF
    1232-1233
    JR 123AHJR FOUNV518 06
    Jump down two instructions to 123AH to test the results of the comparison
    1231-1232
    JR 1239HJR FOUNV5
    Jump down two instructions to 123AH to test the results of the comparison
    1234-1236–  ↳ FOUNV4
    1233-1235
    LD DE,136CHLD DE,FOUTDL11 6C 13
    Load Register Pair DE with the starting address of a double precision constant equal to 999,999,999,999,999.95
    1237-1239
    1236-1238
    CALL 0A49HCALL DCOMPDCD 49 0A
    Go compare the double precision constant pointed to by Register Pair DE to the double precision value in ACCumulator to see if the number is still too small
    123A-123C–  ↳ FOUNV5
    1239-123B
    JP P,124CHJP P,FOUNV3F2 4C 12
    If the number isn’t too small anymore then we are done so JUMP to 124CH
    1239-123B–  ↳ FOUNV5
    If the number isn’t too small anymore then we are done so JUMP to 124BH
    123D
    123C
    POP AFF1
    If we are here then the number is still too small so we will need to multiply it by 10. Get the value of the scaled counter from the STACK and put it in Register Pair AF
    123E-1240
    123D-123F
    CALL 0F0BHCALL FINMLTCD 0B 0F
    GOSUB to 0F0BH to multiply the current value in ACCumulator by ten
    1241
    1240
    PUSH AFF5
    Save the exponent value (the negative of the number of times the value was multiplied) in Register Pair AF to the STACK.
    1242-1243
    1241-1242
    JR 1225HJR FOUNV118 E1
    Keep looping back to see if the number is big enough (i.e., between 999,999 and 99,999)
    1244
    1243
    POP AFF1
    At this point, the ACCumulator is too big. First, fetch the exponent (i.e., the scaled count) from the STACK and put it in Register A
    1245-1247
    1244-1246
    CALL 0F18HCALL FINDIVCD 18 0F
    GOSUB to 0F18H to divide the current value in ACCumulator by ten
    1248
    1247
    PUSH AFF5
    Save the exponent to the STACK. A is the count of the number of times it was divided
    1249-124B
    1248-124A
    CALL 124FHCALL FOUNVCCD 4F 12
    We need to see if the ACCumulator is small enough so GOSUB to 124FH to loop until the value in ACCumulator is < 999,999
    124C–  ↳ FOUNV3
    124B
    POP AFF1
    At this point, we are done scaling, so restore the exponent into Register A. A = + times divided or – times multiplied
    124D
    124C
    POP DED1
    Restore DE from where it was preserved at the top of this routine
    N/A
    124D
    OR A
    In ROM v1.2 sets the status flags. This also realigns the memory addresses from changes to v1.2 ROM
    124E
    RETC9
    RETurn to CALLer

    124F – LEVEL II BASIC MATH ROUTINE– “FOUNVC”

    This routine will see if the number in the ACCumulator is small enough yet

    124F–  ↳ FOUNVC
    RST 20HGETYPEE7
    We need to check the value of the current number type flag, so we call the TEST DATA MODE routine at RST 20H.
    NOTE:The RST 20H routine determines the type of the current value in ACCumulator and returns a combination of STATUS flags and unique numeric values in the A Register according to the data mode flag (40AFH). The results are returned as follows:
    • 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.
    1250-1252
    JP PE,125EHJP PE,FONVC1EA 5E 12
    If that test shows we have a DOUBLE PRECISION number, jump to 125EH

    The next two instructions load BCDE with 999999.5 to see if the number in the FAC is too large.

    1253-1255
    LD BC,9474H01 74 94
    Now that we know we have a single precision number, load Register Pair BC with the exponent and the MSB of a single precision constant
    1256-1258
    LD DE,23F8H11 F8 23
    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 constant of 999,999.5
    1259-125B
    CALL 0A0CHCALL FCOMPCD 0C 0A
    Call the SINGLE PRECISION COMPARISON routine at routine at 0A0CH which algebraically compares the single precision value in BC/DE to the single precision value ACCumulator.
    The results are stored in A as follows:
    If ACCumulator = BCDEA=00
    If ACCumulator > BCDEA=01
    If ACCumulator < BCDEA=FF
    125C-125D
    JR 1264HJR FONVC218 06
    Jump to 1264H to test the result of the comparison
    125E-1260–  ↳ FONVC1
    LD DE,1374HLD DE,FOUTDU11 74 13
    If we are here, then we have a DOUBLE PRECISION number to deal with, so start by loading Register Pair DE with the starting address of a double precision constant equal to 9,999,999,999,999,999.5
    1261-1263
    CALL 0A49HCALL DCOMPDCD 49 0A
    Check to see if the number is too big via a GOSUB to 0A49H to compare the double precision constant pointed to by Register Pair DE to the double precision value in ACCumulator
    1264–  ↳ FONVC2
    POP HLE1
    Get the return address from the STACK and put it in Register Pair HL so we can go to 1244H
    1265-1267
    JP P,1244HJP P,FOUNV2F2 44 12
    If the P FLAG is set, then the number is still too big (i.e., the number in the ACCumulator has more than 6 digits in the integer portion), so JUMP to 1244H
    1265-1267
    In ROM v1.2 the ROM addresses had moved 1 byte
    1268
    JP (HL)E9
    If the number isn’t too big, then just RETurn by JUMPing to (HL)

    1269H – LEVEL II BASIC MATH ROUTINE– “FOTZER”

    This routine puts leading zeroes into the input buffer. The count is held in Register A and it can be zero, but the Z FLAG needs to be set in that case. Only (HL) and Register A are affected.

    1269–  ↳ FOTZER
    OR AB7
    This is the entry point from FFXXV3 where the flags have not yet been set, so set the flags, particularly the Z FLAG
    126A–  ↳ FOTZR1
    RET ZC8
    Top of a loop. If the number of 0’s we need to display is zero, then just RETurn
    126B
    DEC A3D
    Decrement the value in Register A to show that an ASCII zero was moved to the print buffer
    126C-126D
    LD (HL),30HLD (HL),”0″36 30
    Save a 0at the location of the input buffer pointer in Register Pair HL
    126E
    INC HL23
    Bump the input buffer pointer in Register Pair HL
    126F-1270
    JR 126AHJR FOTZR118 F9
    Jump back to 126AH until the number in Register A of ASCII zeroes were moved

    1271 – LEVEL II BASIC MATH ROUTINE– “FOTZNC”

    This routine will put zeroes in the buffer along with commans or a decimal point in the middle. The count is held in Register A and it can be zero, but the Z FLAG needs to be set in that case. Registers B (decimal point count) and C (comma count) are updated accordingly. Everything but DE is affected.

    1271-1272–  ↳ FOTZNC
    JR NZ,1277HJR NZ,FOTZEC20 04
    So long as we are adding zeroes, Jump to 1277H
    1273–  ↳ FOTZRC
    RET ZC8
    Top of a loop. If there are no more zeroes to add, RETurn
    1274-1276
    CALL 1291HCALL FOUTEDCD 91 12
    Check to see if we need to insert a comma or a decimal prior to the zero at the current positiuon via a GOSUB to FOUTED
    1277-1278–  ↳ FOTZEC
    LD (HL),30HLD (HL),”0″36 30
    Save a 0at the location of the input buffer pointer in Register Pair HL
    1279
    INC HL23
    Bump the input buffer pointer in Register Pair HL
    127A
    DEC A3D
    Decrement the counter of trailing zeroes to add in Register A
    127B-127C
    JR 1273HJR FOTZRC18 F6
    Loop back and keep looping until the number of zeroes to add is 0.

    127D – LEVEL II BASIC MATH ROUTINE– “FOUTCD”

    This routine will put a possible comma count into Register C and will zero Register C if we are not using commas in the specification.

    127D–  ↳ FOUTCD
    LD A,E7B
    The next bunch of math is to set up the decimal point count. First, load Register A with the value in Register E so that A holds the decimal point countcount of the times the value was scaled up or down
    127E
    ADD A,D82
    Add the number of digits to print (from Register D) to the value in Register A
    127F
    INC A3C
    Bump the adjusted value in Register A so now A holds the number of digits before the decimal point
    1280
    LD B,A47
    Load Register B with the leading digit count (from Register A)
    1281
    INC A3C
    Next, we are going to set up the comma count. First bump the value in Register A so not A holds the leading digits + 2
    1282-1283–  ↳ FOTCD1
    SUB A,03HD6 03
    Subtract three from the adjusted value in Register A which, when combined with the next instruction as a loop, divides modulo 3
    1284-1285
    JR NC,1282HJR NC,FOTCD130 FC
    Loop back 1 instruction until the value in Register A is -1, -2, or -3
    1286-1287
    ADD A,05HC6 05
    Add 5 (which is 3 back plus 2 more for scaling) to A to get a positive remainder. This will give 4, 3, or 2 as the comma count
    1288
    LD C,A4F
    Save the possible comma count into Register A
    1289-128B–  ↳ FOUICC
    LD A,(40D8H)LD A,(TEMP3)3A D8 40
    Load Register A with the format specs from the temporary storage location
    128C-128D
    AND 40HAND 0100 0000E6 40
    Mask against 0100 0000 to isolate the comma bit to see if commas are requested
    128E
    RET NZC0
    If the NZ FLAG is set then we are using commas, so just RETurn
    128F
    LD C,A4F
    If we are here, then we aren’t using commas, so Zero the comma counter in Register C
    1290
    RETC9
    RETurn to CALLer

    1291 – LEVEL II BASIC MATH ROUTINE– “FOUTED”

    This routine will put decimal points and commas in their correct places. This subroutine should be called before the next digit is put in the buffer. Register B = the decimal point count and Register C = the comma count.

    The counts tell how many more digits have to go in before the comma ;or decimal point go in.

    The comma or decimal point then goes before the last digit in the count. For example, if the decimal point should come after the first digit, the decimal point count should be 2.

    1291–  ↳ FOUTED
    DEC B05
    First we need to test to see if it is time to put in a decimal point. To do this, we DECrement the decimal point counter in Register B to see if the zero flag sets or not
    1292-1293
    JR NZ,129CHJR NZ,FOUED120 08
    If the decimal point position hasn’t been reached then JUMP to FOUED1 to see if a comma needs to go there.
    1294-1295–  ↳ FOUTDP
    LD (HL),2EHLD (HL),”.”36 2E
    If we are here, then the decimal point time has come. Save a decimal point at the location of the input buffer pointer in Register Pair HL
    1296-1298
    LD (40F3H),HLLD (TEMP2),HL22 F3 40
    Save the address of the decimal point position (held in Register Pair HL).
    Note: 40F3H-40F4H is a temporary storage location
    1299
    INC HL23
    Bump the buffer pointer in Register Pair HL
    129A
    LD C,B48
    We just put in a decimal point, so we KNOW we don’t need to put a comma here, so ZERO out the comma counter
    129B
    RETC9
    RETurn to CALLer

    129C – LEVEL II BASIC MATH ROUTINE– “FOUED1”

    Part of the above routine, jumped here to test to see if a comma needs to be placed at (HL).

    129C–  ↳ FOUED1
    DEC C0D
    First, we need to test to see if it is time to put in a comma by DECrementing the comma counter in Register C
    129D
    RET NZC0
    If the NZ FLAG is set, then we are not putting in a comma, so RETurn
    129E-129F
    LD (HL),2CHLD (HL),”,”36 2C
    If didn’t jump out, then we need a comma here so put a comma (which is ASCII code 2CH) at the location of the input buffer pointer in Register Pair HL
    12A0
    INC HL23
    Bump the input buffer pointer (to account for the new comma) in Register Pair HL
    12A1-12A2
    LD C,03H0E 03
    Reset the comma counter by setting it to 3 (since commas come after units of 3 numbers)
    12A3
    RETC9
    RETurn to CALLer

    12A4 – LEVEL II BASIC MATH ROUTINE– “FOUTCV”

    This routine will convert a SINGLE PRECISION or a DOUBLE PRECISION number that has been normalized to decimal digits. The decimal point count is in Register B and the comma count is in Register C. (HL) points to where the first digit will go. Routine will exit with A=0.

    12A4–  ↳ FOUTCV
    PUSH DED5
    Generally preserve Register Pair DE. This will get POPped when the subroutine is done.
    12A5
    RST 20HGETYPEE7
    We need to check the value of the current number type flag, so we call the TEST DATA MODE routine at RST 20H.
    NOTE:The RST 20H routine determines the type of the current value in ACCumulator and returns a combination of STATUS flags and unique numeric values in the A Register according to the data mode flag (40AFH). The results are returned as follows:
    • 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.
    12A6-12A8
    JP PO,12EAHJP PO,FOUTCSE2 EA 12
    If we have a single precision number (by the Parity Odd flag being set) JUMP to 12EAH to convert a SINGLE precision number into its INTEGER equivalent)
    12A9
    PUSH BCC5
    Now that we know we have a DOUBLE PRECISION number, save decimal/comma count (in Register Pair BC) to the STACK
    12AA
    PUSH HLE5
    Save the buffer address (in Register Pair HL) to the STACK
    12AB-12AD
    CALL 09FCHCALL VMOVAFCD FC 09
    GOSUB to 09FCH to mmove the double precision value in ACCumulator to REG2
    12AE-12B0
    LD HL,137CHLD HL,DHALF21 7C 13
    Load Register Pair HL with the starting address of a double precision constant equal to 0.5D0
    12B1-12B3
    CALL 09F7HCALL VMOVFMCD F7 09
    GOSUB to 09F7H to move 0.5D0 to the ACCumulator
    12B4-12B6
    CALL 0C77HCALL DADDCD 77 0C
    Call the DOUBLE PRECISION ADD function (which adds the double precision value in REG 2 to the value in ACCumulator (which is the constant 0.5D0). Result is left in ACCumulator)
    12B7
    XOR AAF
    Zero Register A and clear the status flags; particularly the CARRY FLAG
    12B8-12BA
    CALL 0B7BHCALL DINTFOCD 7B 0B
    Isolate the integer part of the double precision number via a GOSUB to 0B7BH
    12BB
    POP HLE1
    Restore the buffer address from the STACK and put it in Register Pair HL
    12BC
    POP BCC1
    Restore the decimal and comma counters from the STACK and put it in Register Pair BC
    12BD-12BF
    LD DE,138CHLD DE,FODTBL11 8C 13
    Load Register Pair DE with the starting address of a series of double precision constants (i.e., a table of powers of 10 from 1.0x10E15 – 1.0x10E6) for the binary to ASCII conversion
    12C0-12C1
    LD A,0AH3E 0A
    We are going to want to convert ten digits, so load Register A with the number of times to divide the double precision value in ACCumulator by a power of 10

    Top of a loop to convert the next digit. It is executed “A” times.

    12C2-12C4–  ↳ FOUCD1
    CALL 1291HCALL FOUTEDCD 91 12
    Check to see if we need to put in a decimal point or a comma at the location pointed to by HL via a GOSUB to 1291H
    12C5
    PUSH BCC5
    Save the count of digits before the decimal point and the count of digts after the decimal point (stored in Register Pair BC) to the STACK
    12C6
    PUSH AFF5
    Save the number of digits to process / division count (stored in Register Pair A) to the STACK
    12C7
    PUSH HLE5
    Save the current buffer address (stored in Register Pair HL) to the STACK
    12C8
    PUSH DED5
    Save the address of the power of 10 table (stored in Register Pair DE) to the STACK
    12C9-12CA
    LD B,2FHLD B,”0″-106 2F
    Load Register B (which will be the quotient in ASCII for each division) with the ASCII value for a zero character minus one since the loop which follows starts by INCrementing the value
    12CB–  ↳ FOUCD2
    INC B04
    Top of a loop. Bump the ASCII value for the digit in Register B so as to start with ASCII “0”
    12CC
    POP HLE1
    Get the address of the power of 10 table (i.e., the divisor) from the STACK and put it in Register Pair HL and
    12CD
    PUSH HLE5
    …. put it right back into the STACK so that it can be restored during the loop
    12CE-12D0
    CALL 0D48HCALL DADDFSCD 48 0D
    GOSUB to 0D48H to subtract the double precision value pointed to by Register Pair HL from the double precision value in REG l. This is to divide the current integer value by of a power of 10 starting at 10e15 working its way down to 10e6 in a loop until the remainder is less than the current power)
    12D1-12D2
    JR NC,12CBHJR NC,FOUCD230 F8
    Jump back to do another subtraction and keep looping until the Carry flag gets set by the subtraction (meaning that the remainder is now less than the current power)
    12D3
    POP HLE1
    If we are here because the C FLAG fired, then we have subtracted once too many times. So we need to un-subtract once. To do that we first need to get the address of the power table from the STACK and put it in Register Pair HL
    12D4-12D6
    CALL 0D36HCALL DADDFOCD 36 0D
    GOSUB to 0D36H to add the double precision value pointed to by Register Pair HL (which is the table of powers of 10) to the double precision remainder in ACCumulator to make it a positive value. Return with the correct remainder in ACCumulator
    12D7
    EX DE,HLEB
    Swap DE and HL so that th eopoert of ten pointer is now in DE.
    12D8
    POP HLE1
    Get the current buffer address from the STACK and put it in Register Pair HL
    12D9
    LD (HL),B70
    Save the ASCII value for the digit in Register B at the location of the input buffer pointer (stored in Register Pair HL)
    12DA
    INC HL23
    Bump the buffer pointer in Register Pair HL since we have just put a digit there
    12DB
    POP AFF1
    Get the loop counter back into Register A
    12DC
    POP BCC1
    Get the decimal point and comma counter from the STACK and put it in Register Pair BC
    12DD
    DEC A3D
    Decrement the loop counter value in Register A (we are going to loop 10 times)
    12DE-12DF
    JR NZ,12C2HJR NZ,FOUCD120 E2
    Loop 10 times until the ASCII string has been figured
    12E0
    PUSH BCC5
    At this point, we have finished printing the last digit, so now we want to convert the remaining digits using single precision routines (which are faster). First, save the decimal and comma counters (stored in Register Pair BC) to the STACK
    12E1
    PUSH HLE5
    Save the input buffer pointer (stored in Register Pair HL) to the STACK
    12E2-12E4
    LD HL,411DHLD HL,DFACLO21 1D 41
    Point HL to the remaining digits for processing as a single precision number
    12E5-12E7
    CALL 09B1HCALL MOVRMCD B1 09
    Move the numbers that are left to ACCumulator via a GOSUB to MOVRM
    12E8-12E9
    JR 12F6HJR FOUCDC18 0C
    Jump to 12F6H to convert that last half to ASCII

    12EA – LEVEL II BASIC MATH ROUTINE– “FOUTCS”

    This routine is to convert a SINGLE precision value to an INTEGER which will be the decimal digits. Divide the integer equivalent by 100,000 and 10,000. Use the code at 1335H to convert the last 1000 to ASCII.

    12EA–  ↳ FOUTCS
    PUSH BCC5
    Save the decimal/comma count (in Register Pair BC) to the STACK
    12EB
    PUSH HLE5
    Save the buffer pointer (stored in Register Pair HL) to the STACK
    12EC-12EE
    CALL 0708HCALL FADDHCD 08 07
    Round the number to the nearest integer via a GOSUB to 0708H which will add a single precision value of 0.5 to the single precision value in ACCumulator. The result is stored in BC/DE
    12EF
    INC A3C
    When a number is positive and non-zero, a FADDH call to round will always exit with the HIGH ORDER of 0 in Register A. So we add 1 to force A to be non-zero.
    12F0-12F2
    CALL 0AFBHCALL QINTCD FB 0A
    GOSUB to 0AFBH to convert the positive single precision value in ACCumulator to an integer. The result is stored in C/D/E
    12F3-12F5
    CALL 09B4HCALL MOVFRCD B4 09
    Save the C/D/E number into the ACCumulator via a GOSUB to MOVFR
    12F6–  ↳ FOUCDC
    POP HLE1
    Get the current buffer pointer value from the STACK and put it in Register Pair HL
    12F7
    POP BCC1
    Get the decimal/comma count value from the STACK and put it in Register Pair BC
    12F8
    XOR AAF
    Clear the CARRY FLAG, which is our flag to calculate two digits
    12F9-12FB
    LD DE,13D2HLD DE,FOSTBL11 D2 13
    Load Register Pair DE with the starting address for a series of integer values (in this case, 100,000) and then fall through to FOUCS1

    12FC – LEVEL II BASIC MATH ROUTINE– “FOUCS1”

    This routine is to calculate the next digit of the number.

    12FC–  ↳ FOUCS1
    CCF3F
    Complement the Carry flag, which tracks when we are done with the division loop of 12FC-1327H
    12FD-12FF
    CALL 1291HCALL FOUTEDCD 91 12
    Check to see if we need to put a decimal point or a comma before the current number via GOSUB to FOUTED
    1300
    PUSH BCC5
    Save the decimal and comma counter (stored in Register Pair BC) to the STACK
    1301
    PUSH AFF5
    Save the carry flag (which acts as our digit count for the count of the number of times through this loop) to the STACK
    1302
    PUSH HLE5
    Save the current buffer pointer value (stored in Register Pair HL) to the STACK
    1303
    PUSH DED5
    Save the power of 10 table pointer (stored in Register Pair DE) to the STACK
    1304-1306
    CALL 09BFHCALL MOVRFCD BF 09
    Loads the SINGLE PRECISION value in ACCumulator into Register Pair BC/DE via A GOSUB to MOVRF.
    1307
    POP HLE1
    Get the power of 10 table address (the integer value for 100,000) from the STACK and put it in Register Pair HL
    1308-1309
    LD B,2FHLD B,”0″-106 2F
    Set B to be the next digit to print. Since the next step INCremenets B, we need to start off with B one too low.

    130A – LEVEL II BASIC MATH ROUTINE– “FOUCS2”

    This routine divides the integer portion of the current value by 100,000 using compound subtraction. The quotient is kept in Register B as an ASCII value.

    130A–  ↳ FOUCS2
    INC B04
    Bump the ASCII value from the digit in Register B to increase the ASCII value from 0 and upward
    130B
    LD A,E7B
    Load Register A with the Low Order/LSB of the single precision value in Register E
    130C
    SUB (HL)96
    Subtract the value at the location of the memory pointer in Register Pair HL (the LSB of 100,000) from the value of the LSB of the single precision value in Register A
    130D
    LD E,A5F
    Load Register E with the adjusted LSB of the single precision value in Register A
    130E
    INC HL23
    Bump the value of the memory pointer in Register Pair HL to the next digit of 100,000
    130F
    LD A,D7A
    Load Register A with the Middle Order/NMSB of the single precision value in Register D
    1310
    SBC A,(HL)9E
    Subtract the value at the location of the memory pointer in Register Pair HL (the middle byte of 100,000) from the value of the NMSB of the single precision value in Register A
    1311
    LD D,A57
    Load Register D with the adjusted NMSB of the single precision value in Register A
    1312
    INC HL23
    Bump the value of the memory pointer in Register Pair HL to the MSB of 100,000
    1313
    LD A,C79
    Load Register A with the High Order/MSB of the single precision value in Register C
    1314
    SBC A,(HL)9E
    Subtract the value at the location of the memory pointer in Register Pair HL from the value of the MSB of 100,000 (a single precision value in Register A)
    1315
    LD C,A4F
    Load Register C with the adjusted MSB of the single precision value in Register A
    1316
    DEC HL2B
    Decrement the value of the memory pointer in Register Pair HL to the NMSB of 100,000
    1317
    DEC HL2B
    Decrement the value of the memory pointer in Register Pair HL again, now to the LSB of 100,000
    1318-1319
    JR NC,130AHJR NC,FOUCS230 F0
    Loop until the ASCII value for the digit under 100,000 has been figured
    131A-131C
    CALL 07B7HCALL FADDACD B7 07
    We need to add 100,000 to C/D/E and make it positive so we GOSUB to 07B7H to add the value at the location of the memory pointer in Register Pair HL to the value in Register Pairs BC and DE
    131D
    INC HL23
    Bump the value of the memory pointer in Register Pair HL to now point to the 10,000 constant
    131E-1320
    CALL 09B4HCALL MOVFRCD B4 09
    Save the remainder as a current value by GOSUB to 09B4H (which moves the SINGLE PRECISION value in DC/DE into ACCumulator)
    1321
    EX DE,HLEB
    Load Register Pair DE with the address of the next value to divide the current value in ACCumulator by (which is the constant of 10,000)
    1322
    POP HLE1
    Get the value of the memory pointer from the STACK and put it in Register Pair HL
    1323
    LD (HL),B70
    Save the ASCII value for the digit in Register B at the location of the input buffer pointer in Register Pair HL
    1324
    INC HL23
    Bump the value of the input buffer pointer in Register Pair HL
    1325
    POP AFF1
    Get the carry flag from the STACK and put it in Register Pair AF
    1326
    POP BCC1
    Get the value from the STACK and put it in Register Pair BC so it can be saved later
    1327-1328
    JR C,12FCHJR C,FOUCS138 D3
    If the carry flag is set, then reset it and loop the dividing by 10,000 until the integer portion is found
    1329
    INC DE13
    If we fall through to here, we have divided the integer part of the single precision variable by 100,000 and then by 10,000 with the remainder being positive and saved as the current value. With this we bump the value of the memory pointer in Register Pair DE
    132A
    INC DE13
    and again bump the value of the memory pointer in Register Pair DE, so now DE points to the constant 1,000
    132B-132C
    LD A,04H3E 04
    Load Register A with the number of digits for the ASCII string to be figured
    132D-132E
    JR 1335HJR FOUCI118 06
    Jump to 1335H to convert the remainder to 4 ASCII digits. Note that the CARRY FLAG will be off.

    132F – This routine will convert an INTEGER to ASCII– “FOUTCI”

    This routine converts an integer into decimal digits by dividing the integer portion of the current value by 100,000 using compound subtraction. The quotient is kept in Register B as an ASCII value and A=0 on exit.

    132F–  ↳ FOUTCI
    PUSH DED5
    Generally preserve DE. This will be POPped just before the RETurn
    1330-1332
    LD DE,13D8HLD DE,FOITBL11 D8 13
    Load Register Pair DE with the starting address of the descending powers of 10 starting at 10,000
    1333-1334
    LD A,05H3E 05
    Load Register A with the number of digits for the ASCII string to be built (i.e., 5 since the maximum positive integer is 32768)
    1335-1337–  ↳ FOUCI1
    CALL 1291HCALL FOUTEDCD 91 12
    Top of the big loop. Check to see if a decimal point or comma needs to be placed before the digit being processed via a GOSUB to FOUTED
    1338
    PUSH BCC5
    Save the decimal and comma counter (stored in Register Pair BC) to the STACK
    1339
    PUSH AFF5
    Save the number of digits-to-process counter (stored in Register A) to the STACK
    133A
    PUSH HLE5
    Save the address of the power table (stored in Register Pair HL) to the STACK
    133B
    EX DE,HLEB
    Load Register Pair HL with the starting address of the descending powers of 10 starting at 10,000 (stored in Register Pair DE)
    133C
    LD C,(HL)4E
    Load Register C with the LSB for the power of 10 stored in Register Pair HL
    133D
    INC HL23
    Bump the value of the memory pointer in Register Pair HL to be the MDB of the power of 10
    133E
    LD B,(HL)46
    Load Register B with the MSB for the integer value at the location of the memory pointer in Register Pair HL
    133F
    PUSH BCC5
    Save the integer value of the power of 10 in Register Pair BC to the STACK
    1340
    INC HL23
    Bump the value of the memory pointer in Register Pair HL to the next value in the power of 10 table
    1341
    EX (SP),HLE3
    Swap (SP) and HL so that the pointer to the power of 10 table is in the STACK and the power of ten is in HL
    1342
    EX DE,HLEB
    Put the power of ten into DE
    1343-1345
    LD HL,(4121H)LD HL,(FACLO)2A 21 41
    Load Register Pair HL with the integer value in ACCumulator
    1346-1347
    LD B,2FHLD B,”0″ – 106 2F
    Since we are about to start a loop which starts with an INC, compensate by loading Register B with the ASCII value for a zero character minus one

    This loop divides the current value by a power of 10 starting at 10,000 and working down to 10. The remainder frome ach division is added to the division and the sum becomes the dividend for the next division until done. The quotient is +2FH (which is the ASCII equivalent of a quotient).

    1348–  ↳ FOUCI2
    INC B04
    Bump the ASCII value for the digit in Register B (so it starts at 0 and moves up each loop)
    1349
    LD A,L7D
    Load Register A with the LSB of the integer value in Register L
    134A
    SUB E93
    Subtract the value of the LSB of the integer value in Register E from the value of the LSB of the integer value in Register A
    134B
    LD L,A6F
    Load Register L with the adjusted value of the LSB of the integer value in Register A
    134C
    LD A,H7C
    Load Register A with the value of the MSB of the integer value in Register H
    134D
    SBC A,D9A
    Subtract the MSB of the integer value in Register D from the value of the MSB of the integer value in Register A
    134E
    LD H,A67
    Load Register H with the adjusted value of the MSB of the integer value in Register A
    134F-1350
    JR NC,1348HJR NC,FOUCI230 F7
    If the quotient (stored in HL) >= the current power of 10 (stored in DE) then we need to loop back to 1348H
    1351
    ADD HL,DE19
    The problem with using the CARRY FLAG as a trigger is that it triggers once you have already gone too far. So we need to go back 1. To do this, add the remainder (stored as an integer in Register Pair DE) to the quotient (stored in Register Pair HL as an integer)
    1352-1354
    LD (4121H),HLLD (FACLO),HL22 21 41
    Save the integer remainder (stored in Register Pair HL) in ACCumulator
    1355
    POP DED1
    Get the address of the next power of 10 from the STACK and put it in Register Pair DE
    1356
    POP HLE1
    Get the memory pointer for the buffer from the STACK and put it in Register Pair HL
    1357
    LD (HL),B70
    Save the ASCII value for the digit (from Register B that tracked the number of divisions) to the location of the output buffer pointer (stored in Register Pair HL)
    1358
    INC HL23
    Bump the value of the buffer pointer in Register Pair HL since we just filled that spot with an ASCII value
    1359
    POP AFF1
    Get the number of digits to convert (i.e., the digit counter) from the STACK and put it in A
    135A
    POP BCC1
    Get the decimal/comma counts from the STACK and put it into Register Pair BC
    135B
    DEC A3D
    Decrement the value of the counter in Register A (which is a countdown from 5)
    135C-135D
    JR NZ,1335HJR NZ,FOUCI120 D7
    If the counter of the number of digits (from 5) is still not zero, jump back to 1335H until all of the digits have been figured
    135E-1360
    CALL 1291HCALL FOUTEDCD 91 12
    So now all the digits have been calculated in ASCII, so GOSUB 1291H to put a decimal point or comma into the input buffer if necessary
    1361
    LD (HL),A77
    Save a zero (the value in Register A which hit zero when the loop from 5 finished) to the input buffer, pointed to by Register Pair HL. Note that we do not advance HL, so we can overwrite this trailing zero if necessary.
    1362
    POP DED1
    Get the value from the STACK (which was whatever value was in DE when this routine started) and put it in Register Pair DE
    1363
    RETC9
    RETurn to CALLer with A=0

    1364-136B – DOUBLE PRECISION CONSTANT STORAGE LOCATION– “TENTEN”

    1364-136B–  ↳ TENTEN
    00 00 00 00 F9 02 15 A2
    A double precision constant equal to 10000000000 is stored here

    136C-1373 – DOUBLE PRECISION CONSTANT STORAGE LOCATION– “FOUTDL”

    136C-1373–  ↳ FOUTDL
    FD FF 9F 31 A9 5F 63 B2
    A double precision constant equal to 999,999,999,999,999.95 is stored here

    1374-137B – DOUBLE PRECISION CONSTANT STORAGE LOCATION– “FOUTDU”

    1374-137B–  ↳ FOUTDU
    FE FF 03 BF C9 1B 0E B6
    A double precision constant equal to 9,999,999,999,999,999.5 is stored here

    137C-1383 – DOUBLE PRECISION CONSTANT STORAGE LOCATION– “DHALF”

    137C-137F–  ↳ DHALF
    00 00 00 00
    A double precision constant equal to 0.5D0 is stored here.
    BYTE SAVING NOTE: Referencing 1380H, which is half-way through this double precision value of .5, results in a single precision value of 0.5
    1380-1383–  ↳ FHALF
    00 00 00 80
    A double precision constant equal to 0.5E0 is stored here.
    BYTE SAVING NOTE: Referencing 1380H, which is half-way through this double precision value of .5, results in a single precision value of 0.5

    1384-138B – DOUBLE PRECISION CONSTANT STORAGE LOCATION– “FFXDXM”

    1384-138B–  ↳ FFXDXM
    00 00 04 BF C9 1B 0E B6
    A double precision constant equal to 1D16

    138C-13D1 – DOUBLE PRECISION INTEGER CONSTANT STORAGE LOCATION– “FODTBL”

    138C-1392–  ↳ FODTBL
    00 80 C6 A5 7E 8D 03
    1D15
    1393-1399
    00 40 7A 10 F3 5A 00
    1D14
    139A-13A0
    00 A0 72 4E 18 09 00
    1D13
    13A1-13A7
    00 10 A5 D5 E8 00 00
    1D12
    13A1-13A7
    00 10 A5 D5 E8 00 00
    1D11
    13A8-13AE
    00 E8 76 48 17 00 00
    1D10
    13AF-13B5
    00 E4 0B 54 02 00 00
    1D9
    13B6-13BC
    00 CA 9A 3B 00 00 00
    1D8
    13BD-13C3
    00 E1 F4 05 00 00 00
    1D7
    13C4-13CA
    80 96 98 00 00 00 00
    1D6
    13CB-13D1
    40 42 0F 00 00 00 00
    1D5

    13D2-13D9 – SINGLE PRECISION POWER OF TEN TABLE LOCATION– “FOSTBL

    13D2-13D4–  ↳ FOSTBL
    A0 86 01
    1E5
    13D5-13D7
    10 27 00
    1E4

    13D8 – SINGLE PRECISION POWER OF TEN TABLE LOCATION– “FOITBL

    13D8-13D9–  ↳ FOITBL
    10 27
    10,000
    13DA-13DB
    E8 03
    1,000
    13DC-13DD
    64 00
    100
    13DE-13DF
    0A 00
    10
    13E0-13E1
    01 00
    1

    13E2-13E6 – LEVEL II BASIC MATH ROUTINE– “PSHNEG”

    13E2-13E4–  ↳ PSHNEG
    LD HL,0982HLD HL,NEG21 82 09
    Load Register Pair HL with the address of the routine for conversion of floating point numbers from negative to positive
    13E5
    EX (SP),HLE3
    Exchange the value of that routines jump address to the STACK with the value of the return address in Register Pair HL
    135E-1360
    CALL 1291HCALL FOUTEDCD 91 12
    So now all the digits have been calculated in ASCII, so GOSUB 1291H to put a decimal point or comma into the input buffer if necessary

    13E7-13F1 – LEVEL II BASIC SQR(n)– “SQR”

    This routine computes the square root of any value in ACCumulator. It processes it by raising n to the power of 0.5. The root is left in ACCumulator as a single precision value. Single-precision values only should be used

    13E7-13F1–  ↳ SQR
    CALL 09A4HCALL PUSHFCD A4 09
    GOSUB 09A4 which moves the SINGLE PRECISION value in ACCumulator to the STACK (stored in LSB/MSB/Exponent order)
    13EA-13EC
    LD HL,1380HLD HL,FHALF21 80 13
    Load Register Pair HL with the starting address of a single precision constant equal to 0.5 (which will be the exponent)
    13ED-13EF
    CALL 09B1HCALL MOVRMCD B1 09
    GOSUB 09B1H (which moves a SINGLE PRECISION number pointed to by HL to ACCumulator)
    13F0-13F1
    JR 13F5HJR FPWRT18 03
    Jump to the EXP(n)routine at 13F5H (which will be using a .5 exponent to do the square root) skipping 13F2H since the exponent is already single precision

    13F2-1478H LEVEL II BASIC X to the Y Power (X^Y) ROUTINE– “FPWRQ”

    A call to 13F2H raises the single precision value which has been saved to the STACK to the power specified in ACCumulator. The result will be returned in ACCumulator. The method of computation is e ** (y ln x).

    13F2-13F4–  ↳ FPWRQ
    CALL 0AB1HCALL FRCSNGCD B1 0A
    Make sure that the exponent is single precision by GOSUB to 0AB1H which is the CONVERT TO SINGLE PRECISION routine at 0AB1H (which converts the contents of ACCumulator from integer or double precision into single precision)
    13F5–  ↳ FPWRT
    POP BCC1
    Get the MSB of the single precision value from the STACK and put it in Register Pair BC
    13F6
    POP DED1
    Get the NMSB and the LSB of the single precision value from the STACK and put it in Register Pair DE

    13F7 – LEVEL II BASIC Exponentiation routine– “FPWR”

    This routine handles the exponentiation routine of X^Y. To do so, first Y is checked for 0 and, if so, then the answer is simply 1. Then we check X for 0 and, if so, then the answer is simply 0.

    If neither of those scenarios is the case, then must check to see if X is positive and, if not, check to see if Y is negative and if it is even or odd.

    If Y is negative, the we negate it to avoid the LOG routine giving a ?FC ERROR when we call it.

    If X is negative and Y is odd, the NEG routine is pushed to the STACK as the exit rouine so that the result will be negative.

    The actual math here is X^Y = EXP(Y*LOG(X)).

    13F7-13F9–  ↳ FPWR
    CALL 0955HCALL SIGNCD 55 09
    First, check Y to see if Y is zero via a GOSUB 0955H to check the sign for the single precision value in ACCumulator (the exponent)
    13FA
    LD A,B78
    Next, check to see if X is zero by first loading Register A with the MSB of the number to be raised (stored as a single precision value in Register B)
    13FB-13FC
    JR Z,1439HJR Z,EXP28 3C
    If it is zero then we already know our ansder which is, mathematically, a 1 so JUMP to the EXP(n)routine at 1439H
    13FD-13FF
    JP P,1404HJP P,POSEXPF2 04 14
    After knowing that X isn’t 0, we must check the sign of Y. If it is positive, then JUMP to POSEXP to skip the next 2 opcodes (which check to see if zero is involved) if the exponent (the single precision value in ACCumulator) is positive
    1400
    OR AB7
    Check to see if this is a ZERO raised to the minus power.
    1401-1403
    JP Z,199AHJP Z,DV0ERRCA 9A 19
    If it is 0 raised to a minus power, display a ?/0 ERRORmessage since the single precision value in ACCumulator is negative and the single precision value in Register Pairs BC and DE is equal to zero.
    /0 ERROR entry point
    1404–  ↳ POSEXP
    OR AB7
    ANOTHER check to see if the value to be raised (i.e., the single precision value in Register Pairs BC and DE) is equal to zero
    1405-1407
    JP Z,0779HJP Z,DV0ERRCA 79 07
    If the value to be raised (i.e., the single precision value in Register Pairs BC and DE) is equal to zero, then we already know the result will be zero, so JUMP to ZERO0.
    1408
    PUSH DED5
    At this point we know that none of the values are zero, and we are raising the number to a positive power. Save the value to be raised (the NMSB and the LSB of the single precision value in Register Pair DE) to the STACK
    1409
    PUSH BCC5
    Save the exponent and the MSB of the single precision value in Register Pair BC to the STACK
    140A
    LD A,C79
    Now we want to check the sign of X. First, load Register A with the value of the MSB of the single precision value to be raised (which is stored in Register C)
    140B-140C
    OR 7FHF6 7F
    Turn the Z FLAG off by ORing against 7FH (0111 1111) in Register A
    140D-140F
    CALL 09BFHCALL MOVRFCD BF 09
    Load the Y (the power) into BC/DE by GOSUB to 09BF which loads the SINGLE PRECISION value in ACCumulator (the exponent) into Register Pair BC/DE
    1410-1412
    JP P,1421HJP P,FPWR1F2 21 14
    If X is positive, then jump down to FPWR1 as we have nothing advanced to process
    1413
    PUSH DED5
    Otherwise, we need to do some more math. Save the Y value to the STACK first by saving the NMSB and the LSB of the exponent (the single precision value in Register Pair DE) to the STACK
    1414
    PUSH BCC5
    and then Save the exponent and the LSB of the single precision value in Register Pair BC to the STACK
    1415-1417
    CALL 0B40HCALL INTCD 40 0B
    Check to see if the Y is an integer via a GOSUB to 0B40H to figure the integer portion of the exponent (i.e., the single precision value in ACCumulator) into A with the truncated floating point portion into ACCumulator
    1418
    POP BCC1
    Restore the exponent and the MSB of the Y value from the STACK and put it in Register Pair BC
    1419
    POP DED1
    Restore the NMSB and the LSB of the Y value from the STACK and put it in Register Pair DE
    141A
    PUSH AFF5
    Save the LSB of the integer to the STACK for even and odd information
    141B-141D
    CALL 0A0CHCALL FCOMPCD 0C 0A
    Make sure we have an integer by GOSUBing to FCOMP which will compare the original exponent to the truncated one by GOSUB to routine at 0A0CH which algebraically compares the single precision value in BC/DE to the single precision value ACCumulator.
    The results are stored in A as follows:
    If ACCumulator = BCDEA=00
    If ACCumulator > BCDEA=01
    If ACCumulator < BCDEA=FF
    141E
    POP HLE1
    Get the exponent as an integer from the STACK and put it in Register H. This will help us determine if it is even or odd.
    141F
    LD A,H7C
    Load Register A with the exponent as an integer (as stored in Register H)
    1420
    RRA1F
    Rotate that exponent right by one, so we can tell if it is even or odd. If the exponent (as an integer) is odd, set the CARRY FLAG. RRA rotates Register A right one bit, with Bit 0 going to CARRY and CARRY going to Bit 7.
    1421–  ↳ FPWR1
    POP HLE1
    Prepare to get the X back into the ACCumulator by fetching the number from the top of the STACK into Register Pair HL
    1422-1424
    LD (4123H),HLLD (FAC-1),HL22 23 41
    Save the HIGH ORDERs of X to the ACCumulator
    1425
    POP HLE1
    Get the LOW ORDERS of X from the STACK and put it in Register Pair HL
    1426-1428
    LD (4121H),HLLD (FACLO),HL22 21 41
    Save the rest of the exponent (i.e., as stored in Register Pair HL as the NMSB and the LSB of the single precision value) in ACCumulator
    1429-142B
    CALL C,13E2HCALL C,PSHNEGDC E2 13
    If the exponent is odd then we need to negate the final result, so GOSUB to 13E2H to PUSH the address of the NEG routine into the STACK
    142C-142E
    CALL Z,0982HCALL Z,NEGCC 82 09
    If the exponent is an integer and the base is negative, GOSUB to 0983H to invert the value of the exponent
    142F
    PUSH DED5
    Save the NMSB and the LSB of the Y/exponent (i.e., the single precision value in Register Pair DE) to the STACK
    1430
    PUSH BCC5
    Save the MSB of the Y/exponent (i.e., the single precision value in Register Pair BC) to the STACK
    1431-1433
    CALL 0809HCALL LOGCD 09 08
    Now we want to compute EXP(Y*LOG(X)) so we CALL the LOG(N) routine at 0809H (which computes the natural log (base E) of the single precision value in ACCumulator. The result is returned as a single precision value in ACCumulator. Can give an ILLEGAL FUNCTION CALL erro if a negative base is raised to a power with a fraction)
    1434
    POP BCC1
    Get the exponent and the MSB of the single precision value from the STACK and put it in Register Pair BC
    1435
    POP DED1
    Get the NMSB and the LSB of the single precision from the STACK and put it in Register Pair DE
    1436-1438
    CALL 0847HCALL FMULTCD 47 08
    We need to multiply the ln(value) * the exponent so we have to GOSUB to 0847H to SINGLE PRECISION MULTIPLY routine (which multiplies the current value in ACCumulator by the value in (BC/DE). The product is left in ACCumulator

    1439 – LEVEL II ROM EXPROUTINE.

    Single-precision only. (ACCumulator = EXP(REG1)).

    To process this function we first save the original argument and multiply the ACCumulator by log2(e). The result of that is then used to determine if we will get overflow, since exp(x)=2^(x*log2(e)) where log2(e)=log(e) base 2.

    We then save the integer part of this to scale the answer at the end, since 2^y=2^int(y)*2^(y-int(y)) and 2^int(y) is easy to compute.

    So in the end we compute 2^(x*log2(e)-int(x*log2(e))) by p(ln(2)*(int(x*log2(e))+1)-x) where p is an approximation polynomial.

    The result is then scaled by the power of 2 we previously saved.

    A call to 1439H raises E (natural base) to the value in ACCumulator which must be a single precision value. The result will be returned in ACCumulator as a single precision number.

    1439-143B–  ↳ EXP
    CALL 09A4HCALL PUSHFCD A4 09
    Save the argument via a GOSUB to 09A4 to move the SINGLE PRECISION value in ACCumulator (the exponent) to the STACK (stored in LSB/MSB/Exponent order)
    143C-143E
    LD BC,8138H01 38 81
    Next we want to do a LOG(E) in base 2, so load Register Pair BC with the exponent and MSB of a single precision constant
    143F-1441
    LD DE,AA3BH11 3B AA
    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 constant of 1.442695 (which is approximately 2 + ln 2)
    1442-1444
    CALL 0847HCALL FMULTCD 47 08
    We next want to calculate INT(ARG/LN(2)) = INT(ARG*LOG2(E)). So we will need to multiply the exponent value by 2 ln 2 so we call the SINGLE PRECISION MULTIPLY routine at 0847H (which multiplies the current value in ACCumulator by the value in (BC/DE). The product is left in ACCumulator
    1445-1447
    LD A,(4124H)LD A,(FAC)3A 24 41
    Load Register A with the result of the math just done (i.e., which was multiplying the exponent value by 2 ln 2) which was stored in ACCumulator.
    1448-1449
    CP 88HFE 88
    Next we want to see if ABS(ACCumulator) is >= 128 (i.e., if the integer portion of the single precision value in ACCumulator uses more than 7 bits of precision) by comparing it against a mask of 1000 1000
    144A-144C
    JP NC,0931HP NC,MLDVEXD2 31 09
    If the single precision value in ACCumulator uses more than 7 bits of precision for its integer portion then it is too big and we need to JUMP to MLDVEX to deal with that.
    144D-144F
    CALL 0B40HCALL INTCD 40 0B
    So now that we know the integer portion is not too big, but we need to see if the argument is too big as well so we GOSUB to 0B40H to get the integer portion of the value in ACCumulator and return with it in Register A
    1450-1451
    ADD A,80HC6 80
    Adjust the value in Register A by masking it against 1000 0000
    1452-1453
    ADD A,02HC6 02
    Adjust the value in Register A by adding 2 more. We will either get an overflow (C FLAG) or we wont (NC FLAG)
    1454-1456
    JP C,0931HJP C,MLDVEXDA 31 09
    If (exponent * 2 ln 2) is => 126 (meaning when 2 was added it it, it overflowed with a 128), jump to 0931H
    1457
    PUSH AFF5
    So now neither has overflowed, so save the scale factor +82H (as stored in Register Pair AF) to the STACK
    1458-145A
    LD HL,07F8HLD HL,FONE21 F8 07
    Load Register Pair HL with a single precision constant equal to 1.0 (as found at 1458H)
    145B-145D
    CALL 070BHCALL FADDSCD 0B 07
    Go add the single precision constant 1.0 (as pointed to by Register Pair HL) to the current value in ACCumulator which is EXP * 2 ln 2
    145E-1460
    CALL 0841HCALL MULLN2CD 41 08
    Need to multiply that by ln 2, so GOSUB to 0841H to multiply (1 + [EXP * 2 ln 2]) (as stored in ACCumulator) by 0.693147
    1461
    POP AFF1
    Get the scale factor (as stored in the STACK) and put it in Register Pair AF
    1462
    POP BCC1
    Get the original exponent into BC/DE in 2 steps fist get the exponent and the MSB of the single precision value from the STACK and put it in Register Pair BC
    1463
    POP DED1
    and then get the NMSB and the LSB of the single precision value from the STACK and put it in Register Pair DE
    1464
    PUSH AFF5
    Put the scale factor (the integerized EXP * 2 ln 2) as stored in Register Pair AF onto the STACK
    1465-1467
    CALL 0713HCALL FSUBCD 13 07
    Now we need to subtract the original exponent from the integerized exponent so we GOSUB to 0713H which is the SINGLE PRECISION SUBTRACT routine (which subtracts the single precision value in BC/DE from the single precision value in ACCumulator. The difference is left in ACCumulator)
    1468-146A
    CALL 0982HCALL NEGCD 82 09
    To force that difference to be a positive number we GOSUB to 0982H which makes the current single precision value in ACCumulator positive
    146B-146D
    LD HL,1479HLD HL,EXPCON21 79 14
    Load Register Pair HL with the starting address for a series of 8 coefficients so as to enable us to evaluate the approximation polynomial in the next instruction
    146E-1470
    CALL 14A9HCALL POLYCD A9 14
    GOSUB to 14A9H to do that series of computations
    1471-1473
    LD DE,0000H11 00 00
    We want to make sure that FMULT will check for an exponent overflow at the end of this routine, so we can’t just add it to the exponent. Rather, we will multiply it by 2^(B-1) so that FMULT will check. So first, load the integerized equivalent of EXP * 2 lnt 2 into BC/DE so first we load Register Pair DE with zero …
    1474
    POP BCC1
    … and then get the value from the STACK and put it in Register Pair BC
    1475
    LD C,D4A
    Load Register C with zero (since Register D was filled with a zero in 1471H)
    1476-1478
    JP 0847HJP FMULTC3 47 08
    We need to multiply by the sum from the series and return so we jump to 0847H which is the the SINGLE PRECISION MULTIPLY routine at 0847H (which multiplies the current value in ACCumulator by the value in (BC/DE). The product is left in ACCumulator

    1479-1499 – SINGLE PRECISION CONSTANT STORAGE LOCATION
    This represents 1/6, -1/5, 1/4, -1/3, 1/2, -1, and 1– “EXPCON”

    1479–  ↳ EXPCON
    09
    The number of single precision constants (9) which follow are stored here
    147A-147D
    40 2E 94 74
    A single precision constant equal to -0.00014171607 (-1.413165 * 10e-4) is stored here
    147E-1481
    70 4F 2E 77
    A single precision constant equal to 0.00132988204 (1.32988 * 10e-3, roughly -1/6) is stored here
    1482-1485
    6E 02 88 7A
    A single precision constant equal to -0.00830136052 (-8.30136 * 10e-3, roughly -1/5) is stored here
    1486-1489
    E7 A0 2A 7C
    A single precision constant equal to 0.04165735095 (roughly 1/4) is stored here
    148A-148D
    50 AA AA 7E
    A single precision constant equal to -0.16666531543 (roughly -1/3) is stored here
    148E-1491
    FF FF 7F 7F
    A single precision constant equal to 0.49999996981 (roughly 1/2) is stored here
    1492-1495
    00 00 80 81
    A single precision constant equal to -1.0 is stored here
    1496-1499
    00 00 00 81
    A single precision constant equal to 1.0 is stored here

    149A-14C8 – LEVEL II BASIC MATH ROUTINE– “POLYX”

    This is a general purpose summation routine which computes the series C0*X+C1*X^3+C2*X^5+C3*X^7+…+C(N)*X^(2*N+1) for I=0 to N when entered at 149AH If entered at 14A9H the series changes to SUM ((((x*c0+c1)x*c2)x+c3)x+.cN. On entry, the x is held in BC/DE and HL points to a list containing the number of terms followed by the coefficients.

    The pointer to degree+1 is in (HL) and the constants should follow the egree, stored in reverse order. X is in the ACCumulator.

    149A-149C–  ↳ POLYX
    CALL 09A4HCALL PUSHFCD A4 09
    Save X to the STACK via a GOSUB to 09A4 to which move the SINGLE PRECISION value in ACCumulator to the STACK (stored in LSB/MSB/Exponent order)
    149D-149F
    LD DE,0C32HLD DE,FMULTT11 32 0C
    Load Register Pair DE with the return address of the FMULTT routine …
    14A0
    PUSH DED5
    … and push it to the STACK, so that once this routine ends, it will be multiplied by X
    14A1
    PUSH HLE5
    Save pointer to the constant (as stored in Register Pair HL) to the STACK
    14A2-14A4
    CALL 09BFHCALL MOVRFCD BF 09
    We need to square X, so we do that in the next two steps. First, GOSUB to 09BFH which loads the SINGLE PRECISION value in ACCumulator into Register Pair BC/DE
    14A5-14A7
    CALL 0847HCALL FMULTCD 47 08
    Since ACCumulator and BC/DE now hold the same number, you can square that by a GOSUB to 0847H which is the SINGLE PRECISION MULTIPLY routine (which multiplies the current value in ACCumulator by the value in (BC/DE). The product is left in ACCumulator)
    14A8
    POP HLE1
    Restore the consatnt pointer from the STACK and put it in Register Pair HL, and then fall through into the POLY routine

    14A9 – LEVEL II BASIC MATH ROUTINE– “POLY”

    General polynomial evaluator routine. Pointer to degree+1 is in (HL), and that gets updated through the computation. The Constants follow the degree and should be stored in reverse order. The ACCumulator has the X. The formula is c0+c1*x+c2*x^2+c3*x^3+…+c(n-1)*x^(n-1)+c(n)*x^n

    14A9-14AB–  ↳ POLY
    CALL 09A4HCALL PUSHFCD A4 09
    Save the “X” to the STACK. We need to move either x or x**2 (depending on the routine entry point) to the STACK so we GOSUB to 09A4 which moves the SINGLE PRECISION value in ACCumulator to the STACK (stored in LSB/MSB/Exponent order)
    14AC
    LD A,(HL)7E
    Fetch the degree (i.e., the number of values to be figured at the location of the memory pointer in Register Pair HL) into Register A
    14AD
    INC HL23
    Bump the value of the memory pointer in Register Pair HL so that it points to the first constant/coefficient
    14AE-14B0
    CALL 09B1HCALL MOVRMCD B1 09
    Now load that constant/coefficient (stored in HL) and move it to ACCumulator by GOSUB to 09B1H (which moves a SINGLE PRECISION number pointed to by HL to ACCumulator)
    14B1
    LD B,0F106 F1
    Z-80 Trick! If passing through, this will simply alter Register B and the next instruction of POP AF will not be processed.
    14B2–  ↳ POLY1
    POP AFF1
    Get the degree (count of coefficients left) from the STACK and put it in Register A
    14B3
    POP BCC1
    Get the value of “X” from the STACK and put it in Register Pair BC/DE – Step 1 and …
    14B4
    POP DED1
    … Step 2
    14B5
    DEC A3D
    Count 1 of the terms as computed by decrementing the counter in Register A
    14B6
    RET ZC8
    If that decrement results in a zero (meaning the series of computations has been completed) return out of the subroutine
    14B7-14B8
    PUSH DE
    PUSH BCD5
    Save the NMSB and the LSB of “X” from DE to the STACK and save the MSB of “X” from BC to the STACK
    14B9
    PUSH AFF5
    Save counter of the remaining degrees (terms to compute) as tracked by Register A into the STACK
    14BA
    PUSH HLE5
    Save the value of the memory pointer to the next constant/coefficient (stored in Register Pair HL) to the STACK
    14BB-14BD
    CALL 0847HCALL FMULTCD 47 08
    Compute C(I)*x by GOSUB to 0847H which is the SINGLE PRECISION MULTIPLY routine (which multiplies the current value in ACCumulator by the value in (BC/DE). The product is left in ACCumulator
    14BE
    POP HLE1
    Restore the coefficient table address (from the STACK) to Register Pair HL
    14BF-14C1
    CALL 09C2HCALL MOVRMCD C2 09
    Get the next coefficient from HL into BC/DE by GOSUB to 09C2H (which loads a SINGLE PRECISION value pointed to by Register Pair HL into Register Pairs BC and DE)
    14C2
    PUSH HLE5
    Save the next coefficient (stored in Register Pair HL) to the STACK
    14C3-14C5
    CALL 0716HCALL FADDCD 16 07
    Compute C(I)*x+C(I+1) by GOSUB to 0716H which is the SINGLE PRECISION ADD routine (which adds the single precision value in (BC/DE) to the single precision value in ACCumulator. The sum is left in ACCumulator)
    14C6
    POP HLE1
    Restore the coefficient table address (from the STACK) to Register Pair HL
    14C7-14C8
    JR 14B2HJR POLY118 E9
    Jump back to 14B2H to continue the series. ACCumulator contains the current term

    14C9-1540 – LEVEL II BASIC RND(n)ROUTINE– “RND”.

    If the passed argument is 0, the last random number generated is returned. If the argument is < 0, a new sequence of random numbers is started using the argument.

    To form the next random number in the sequence, we multiply the previous random number by a random constant, and add in another random constant. Then the HIGH ORDER and LOW ORDER bytes are switched, the exponent is put where it will be shifted in by normal, and the exponent in the ACCUMULATOR is set to 80H so the result will be less than 1. This is then normalized and saved for the next time.

    The reason we switch the HIGH ORDER and LOW ORDER bytes is so we have a random chance of getting a number less than or greater than .5

    Integer, single or double-precision. Output will be single-precision. (ACC=RND (ACC))

    A call to 14C9H Generates a random number between 0 and 1, or 1 and n depending on the parameter passed in ACCumulator, The random value is returned in ACCumulator as an integer with the mode flag set. The parameter passed will determine the range of the random number returned. A parameter of 0 will return an interger between 0 and 1. A parameter greater than 0 will have any fraction portion truncated and will cause a value between 1 and the integer portion of the parameter to be returned.

    There is a bug in the operation of this command. According to Vernon Hester RND(n) where n is an integer from 1 to 32767 is supposed to return an integer from 1 to n. However, when n is a power of two raised to a positive integer exponent from 0 to 14 sometimes returns n+1

    14C9-14CB–  ↳ RND
    CALL 0A7FHCALL FRCINTCD 7F 0A
    First convert the argument to an integer via a GOSUB to the CONVERT TO INTEGER routine at 0A7FH (where the contents of ACCumulator are converted from single or double precision to integer and deposited into HL)
    14CC
    LD A,H7C
    Load Register A with the value of the MSB for the integer value in Register H
    14CD
    OR AB7
    Check to see if the integer value in Register Pair HL is negative
    14CE-14D0
    JP M,1E4AHJP M,FCERRFA 4A 1E
    Since we won’t accept a negative number, display a ?FC ERRORmessage if the integer value in Register Pair HL is negative
    14D1
    OR LB5
    Combine the MSB and LSB and set status flags so we can see if the integer value in Register Pair HL is equal to zero
    14D2-14D4
    JP Z,14F0HJP Z,RND0CA F0 14
    If it is zero, we don’t need the rest of the below which functions to generate RND(n)so we just jump to 14F0H (which generates RND(0), which is a number between 0 and 1)
    14D5
    PUSH HLE5
    Since it wasn’t zero, we need to save the argument (as stored in Register Pair HL) to the STACK
    14D6-14D8
    CALL 14F0HCALL RND0CD F0 14
    Generate a random number between 0 and 1 via a call to GOSUB to 14F0H (which generates RND(0)) and return with the single precision result in ACCumulator
    14D9-14DB
    CALL 09BFHCALL MOVRFCD BF 09
    Move the random number we just generated into BC/DE via a GOSUB to 09BFH which loads the SINGLE PRECISION value in ACCumulator into Register Pair BC/DE
    14DC
    EX DE,HLEB
    Swap some registers so that the random number is now in B/C/H/L
    14DD
    EX (SP),HLE3
    Swap (SP) and HL so that the LOW ORDER bytes of the random number are at the top of the STACK, and HL now holds the integer argument
    14DE
    PUSH BCC5
    Save the HIGH ORDER bytes of the random number value to the STACK
    14DF-14E1
    CALL 0ACFHCALL CONSIHCD CF 0A
    Convert the original x of RND(x) to single precision by GOSUB to 0ACFH which converts the integer value in Register Pair HL to single precision and return with the result in ACCumulator
    14E2-14E3
    POP BC
    POP DEC1
    Restore the RND(0) value from the STACK and put it into Register Pair BC/DE
    14E4-14E6
    CALL 0847HCALL FMULTCD 47 08
    Multiply the RND(0) value (currently in BC/DE) by the n of RND(n)(currently in ACCumulator) by GOSUB to 0847H which is the SINGLE PRECISION MULTIPLY routine (which multiplies the current value in ACCumulator by the value in (BC/DE). The product is left in ACCumulator
    14E7-14E9
    LD HL,07F8HLD HL,FONE21 F8 07
    Load Register Pair HL with the starting address of a single precision constant equal to 1.0
    14EA-14EC
    CALL 070BHCALL FADDSCD 0B 07
    Increase the random number by one by GOSUB to 070BH which adds the single precision constant pointed to by Register Pair HL (which is 1.0) to the single precision value in ACCumulator (which is the random number). Return with the single precision result in ACCumulator
    14ED-14EF
    JP 0B40HJP INTC3 40 0B
    With the random number now in ACCumulator, jump to 0B40H (which will convert it to an integer and RETurn to the subroutine caller, thus exiting out of this routine)

    14F0 – This routine calculates RND(0)– “RND0”.

    14F0-14F2
    LD HL,4090HLD HL,MULTR21 90 40
    Load Register Pair HL with the starting address for a multiplier table used for figuring random numbers
    14F3
    PUSH HLE5
    Save the starting address for the table used for figuring random numbers (stored in HL) to the STACK
    14F4-14F6
    LD DE,0000H11 00 00
    Load Register Pair DE with zero (which will be the NMLSB and LSB of the starting value)
    14F7
    LD C,E4B
    Load Register C with zero (C will be the MSB of the starting value). Now C/D/E is zero.
    14F8-14F9
    LD H,03H26 03
    Load Register H with the counter value for the multiplication loop (which will be 3)
    14FA-14FB–  ↳ RNDO0
    LD L,08H2E 08
    Load Register L with a counter value of 8 bits
    14FC–  ↳ RND1
    EX DE,HLEB
    Swap DE and HL so that the counters are now in DE and the NMSB and the LSB of the random number is in Register Pair HL
    14FD
    ADD HL,HL29
    Multiply the NMSB and the LSB of the random number in Register Pair HL by two
    14FE
    EX DE,HLEB
    Exchange the newly doubled NMSB and the LSB of the random number to DE and the counters to Register Pair HL
    14FF
    LD A,C79
    Next we want to shift the HIGH ORDER byte (Register C), so first load Register A with the MSB of the random number in Register C
    1500
    RLA17
    Multiply the HIGH ORDER byte of the random number in Register A by two
    1501
    LD C,A4F
    Load Register C with the adjusted MSB of the random number in Register A
    1502
    EX (SP),HLE3
    Swap (SP) and HL so that the counters are now at the top of the STACK and the pointer to the multiplier is in Register Pair HL
    1503
    LD A,(HL)7E
    Fetch a multiplier from the the table value (held at the location of the memory pointer in Register Pair HL) into Register A
    1504
    RLCA07
    Rotate the bits of Register A
    1505
    LD (HL),A77
    Save the doubled value (stored in Register A) at the location of the memory pointer in Register Pair HL
    1506
    EX (SP),HLE3
    Swap (SP) and HL so that the counters are now in HL and the pointer to the multiplication table is at the top of the STACK
    1507-1509
    JP NC,1516HJP NC,RND2D2 16 15
    If that rotation set a NC FLAG, JUMP forward to 1516H
    150A
    PUSH HLE5
    Save the counter values in Register Pair HL to the STACK
    150B-150D
    LD HL,(40AAH)LD HL,(RNDX)2A AA 40
    Load Register Pair HL with the NMSB and the LSB of the random number seed.
    Note: 40AAH-40ADH holds the random number seed
    150E
    ADD HL,DE19
    Add the NMSB and the LSB of the random number in Register Pair DE to the NMSB and the LSB of the random number seed in Register Pair HL
    150F
    EX DE,HLEB
    Load Register Pair DE with the adjusted NMSB and LSB of the random number in Register Pair HL
    1510-1512
    LD A,(40ACH)LD A,(RNDX+2)3A AC 40
    Load Register A with the MSB of the random number seed
    1513
    ADC A,C89
    Add the MSB of the random number in Register C to the MSB of the random number seed in Register A
    1514
    LD C,A4F
    Load Register C with the adjusted MSB of the random number in Register A
    1515
    POP HLE1
    Get the counter values from the STACK and put it in Register Pair HL
    1516–  ↳ RND2
    DEC L2D
    Decrement the loop counter in Register L
    1517-1519
    JP NZ,14FCHJP NZ,RND1C2 FC 14
    Loop back to 14FCH until the above has been done eight times
    151A
    EX (SP),HLE3
    Swap (SP) and HL so that HL will now point to the table of multipliers
    151B
    INC HL23
    Bump to the next table of multipliers value
    151C
    EX (SP),HLE3
    Exchange the bumped pointer to the table value in Register Pair HL with the counter value to the STACK
    151D
    DEC H25
    Decrement the counter value of the outer loop (in Register H) to see if there are more bytes to deal with
    151E-1520
    JP NZ,14FAHJP NZ,RNDO0C2 FA 14
    Loop back to 14FAH three times until the random number has been figured
    1521
    POP HLE1
    Clear the flag table address from the STACK. The fact that it is going into HL is not important
    1522-1524
    LD HL,B065H21 65 B0
    Load Register Pair HL with the value to re-seed the random number seed
    1525
    ADD HL,DE19
    Add the seed (from Register Pair HL) to the NMSB and the LSB of the random number in Register Pair DE
    1526-1528
    LD (40AAH),HLLD (RNDX),HL22 AA 40
    Save the adjusted value in Register Pair HL as the NMSB and the LSB of the random number seed.
    Note: 40AAH-40ADH holds the random number seed
    1529-152B
    CALL 0AEFHCALL VALSNGCD EF 0A
    Go set the current number type to single precision
    152C-152D
    LD A,05H3E 05
    Load Register A with a 5
    152E
    ADC A,C89
    Add 5 (the value held in A) and the MSB of the random number in Register C
    152F-1531
    LD (40ACH),ALD (RNDX+2),A32 AC 40
    Save the adjusted value in Register A as the MSB of the random number seed, so now the result is in A/H/L
    1532
    EX DE,HLEB
    Swap DE and HL so that the result is now in A/D/E
    1533-1534
    LD B,80H06 80
    Load Register B with a value for the sign flag and the exponent (i.e, 1000 0000)
    1535-1537
    LD HL,4125HLD HL,FAC+121 25 41
    Load Register Pair HL with the address for the sign value storage location.
    Note: 4125H-4126H is used by floating point routines
    1538
    LD (HL),B70
    Save the sign result (1000 0000) in Register B at the location of the memory pointer in Register Pair HL
    1539
    DEC HL2B
    Decrement to exponent (held in Register Pair HL)
    153A
    LD (HL),B70
    Set the exponent to (1000 0000) so that the value will be < 1
    153B
    LD C,A4F
    Now we just want to normalize C/D/E. First, load Register C with the value of the MSB for the single precision random number in Register A
    153C-153D
    LD B,00H06 00
    Zero the value of any overflow in Register B
    153E-1540
    JP 0765HJP NORMALC3 65 07
    Jump to 0765H which will normalize the value and then jump to 14D9H unless RND(0)was called in which case return to caller

    1541-1546 – LEVEL II BASIC COS
    ROUTINE– “COS”.

    Single-precision only.(ACCumulator = COS(ACCumulator)). A call to 1541H computes the cosine for an angle given in radians. The angle must be a floating point value in ACCumulator; the cosine will be returned in ACCumulator as a floating point value.

    The formula being used is COS(X) = SIN(X+PI/2)

    1541-1543–  ↳ COS
    LD HL,158BHLD HL,PI221 8B 15
    Load Register Pair HL with the starting address of a single precision constant equal to 1.57079637029 (which is pi / 2)
    1544-1546
    CALL 070BHCALL FADDSCD 0B 07
    GOSUB to 070BH to add 1.57079637029 (stored in HL) to the single precision value in ACCumulator and then pass through to the SIN() routine which is next

    1547-158A – LEVEL II BASIC SINROUTINE– “SIN”

    Single-precision only.(ACCumulator = SIN(ACCumulator)).

    A call to 1549H returns the sine as a single precision value in ACCumulator. The sine must be given in radians in ACCumulator.

    The actual calculation routine is:

    1. Assume X <= 360 degrees.
    2. Recompute x as x=x/360 so that x=< 1.
    3. If x <= 90 degrees go to step 7.
    4. If x <= 180 degrees then x=0.5-x and then go to step 7.
    5. If x <= 270 degrees then x=0.5-x.
    6. Recompute x as x=x-1.0.
    7. Compute SIN using the power series.
    1547-1549H
    “SIN”
    CALL 09A4HCALL PUSHFCD A4 09
    First we want to divide the ACCumulator by 2*PI. First, GOSUB to 09A4 which moves the SINGLE PRECISION value in ACCumulator (the x in a SIN(x) call) to the STACK (stored in LSB/MSB/Exponent order)
    154A-154C
    LD BC,8349H01 49 83
    Load Register Pair BC with the exponent and the MSB of a single precision constant
    154D-154F
    LD DE,0FDBH11 DB 0F
    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 6.2831855 (which is pi * 2)
    1550-1552
    CALL 09B4HCALL MOVFRCD B4 09
    Move 2 x pi value (held in DC/BE) into ACCumulator by GOSUB to 09B4H (which moves the SINGLE PRECISION value in DC/DE into ACCumulator)
    1553-1554
    POP BC
    POP DEC1
    Put the x from a SIN(x) call into BC/DE
    1555-1557
    CALL 08A2HCALL FDIVCD A2 08
    To divide the x from a SIN(x) call (held in BC/DE) by pi*2 (held in ACCumulator) we must GOSUB 80A2H to divide the single precision value in Register Pairs BC and DE by the single precision value in ACCumulator. Return with the single precision result in ACCumulator
    1558-155A
    CALL 09A4HCALL PUSHFCD A4 09
    Move that value (x / 2*pi) from ACCumulator to the STACK by GOSUB to 09A4H which moves the SINGLE PRECISION value in ACCumulator to the STACK (stored in LSB/MSB/Exponent order)
    155B-155D
    CALL 0B40HCALL INTCD 40 0B
    Go figure the integer portion for the single precision value in ACCumulator by calling 0B40H. We need to do this so we can isolate the remainder
    155E,155F
    POP BC
    POP DEC1
    Put the quotient and remainder of x/2*pi into BC/DE
    1560-1562
    CALL 0713HCALL FSUBCD 13 07
    To get the remainder we need to subtract the integer portion from the full portion so we GOSUB 0713H (the SINGLE PRECISION SUBTRACT routine) to subtract the single precision value in BC/DE (the entire result) from the single precision value in ACCumulator (the integer part of the result). The difference is left in ACCumulator)
    1563-1564
    LD HL,158FHLD HL,FR421 8F 15
    Load Register Pair HL with the starting address of a single precision constant equal to 0.25
    1566-1568
    CALL 0710HCALL FSUBSCD 10 07
    Next in calculating a SIN we would need to subtract .25 (held in HL) from the fractional part (held in ACCumulator) so as to see if it is <= to 90 degrees. To do this we GOSUB 0710H to subtract the single precision value in ACCumulator from the single precision constant pointed to by Register Pair HL. Return with the result in ACCumulator
    1569-156B
    CALL 0955HCALL SIGNCD 55 09
    Go check the sign of the result of that (.25 – fractional part) subtraction which is held in ACCumulator
    156C
    SCF37
    Set the Carry flag
    156D-156F
    JP P,1577HJP P,SIN1F2 77 15
    Jump to 1577H if the single precision value in ACCumulator is positive (meaning it is < than 90 degrees)
    1570-1572
    CALL 0708HCALL FADDHCD 08 07
    If we are here, it is => 90 degrees, so we need to add .5 to the single precision value in ACCumulator. Return with the result in ACCumulator
    1573-1575
    CALL 0955HCALL SIGNCD 55 09
    Go check the sign for the single precision value in ACCumulator which basically checks to see if it is > 0.75 (meaning < 270 degrees)
    1576
    OR AB7
    Test the value of the sign test in Register A
    1577–  ↳ SIN1
    PUSH AFF5
    Save the sign indicator (+ or -1) in Register Pair AF to the STACK
    1578-157A
    CALL P,0982HCALL P,NEGF4 82 09
    If it is positive, make it negative by GOSUB to 0982H
    157B-157D
    LD HL,158FHLD HL,FR421 8F 15
    Load Register Pair HL with the starting address of a single precision constant equal to 0.25
    157E-1580
    CALL 070BHCALL FADDSCD 0B 07
    Add .25 (stored in HL) to the current value in ACCumulator by GOSUB to 070BH (result is saved in ACCumulator)
    1581
    POP AFF1
    Get the sign reversal flag from the STACK and put it in Register Pair AF
    1582-1584
    CALL NC,0982HCALL NC,NEGD4 82 09
    Set the sign of the x term according to the quadrant by GOSUB to 0982H if if the Carry flag wasn’t set from above
    1585-1587
    LD HL,1593HLD HL,SINCON21 93 15
    Load Register Pair HL with 1593H (which is the starting address for a series of single precision values for a set of computations)
    1588-158A
    JP 149AHJP POLYXC3 9A 14
    Go to 149AH to compute the series and then RETURN

    158B-158E – SINGLE PRECISION CONSTANT STORAGE LOCATION– “PI2”

    158B-158EH–  ↳ PI2
    DB 0F 49 81DB 0F
    A single precision constant equal to 1.57079637029 is stored here

    158F-1592 – SINGLE PRECISION CONSTANT STORAGE LOCATION– “FR4”

    158F-1592H–  ↳ FR4
    00 00 00 7F
    A single precision constant equal to 0.25 is stored here

    1593-15A7 – SINGLE PRECISION CONSTANTS STORAGE LOCATION– “SINCON”

    1593–  ↳ SINCON
    05H
    The number of single precision constants (05) which follows is stored here. These are the coefficients used in the power series to compute SIN(x)
    1594-1597H
    BAH D7H 1EH 86H
    A single precision constant equal to 39.7106704708 is stored here
    1598-159BH
    64H 26H 99H 87H
    A single precision constant equal to -76.5749816893 is stored here
    159C-159FH
    58H 34H 23H 87H
    A single precision constant equal to 81.6022338865 is stored here
    15A0-15A3H
    E0H 6DH A5H 86H
    A single precision constant equal to -41.3416748045 is stored here
    15A4-15A7H
    DAH 0FH 49H 83H
    A single precision constant equal to 6.28318500497 is stored here

    15A8-15BC – LEVEL II BASIC TAN(n)ROUTINE– “TAN”

    Single-precision only.(ACCumulator = TAN(ACCumulator)).
    A call to 15A8H computes the tangent of an angle in radians. The angle must be specified as a single precision value in ACCumulator. The tangent will be left in ACCumulator.
    Uses the fact that TAN(x) = SIN(x) / COS(x)

    15A8-15AA–  ↳ TAN
    CALL 09A4HCALL PUSHFCD A4 09
    Save the argument via a GOSUB to 09A4 which moves the SINGLE PRECISION value in ACCumulator to the STACK (stored in LSB/MSB/Exponent order)
    15AB-15AD
    CALL 1547HCALL SINCD 47 15
    Call the SIN(n)routine at 1547H (which returns the sine as a single precision value in ACCumulator. The sine must be given in radians in ACCumulator
    15AE,15AF
    POP BC
    POP HLC1
    Get the original exponent and value from the STACK and put it in Register Pair BC/HL
    15B0-15B2
    CALL 09A4HCALL PUSHFCD A4 09
    Call 09A4 which moves the SIN(x) single precision value (stored in REG) 1 to the STACK (stored in LSB/MSB/Exponent order)
    15B3
    EX DE,HLEB
    Load Register Pair DE with the NMSB and the LSB of the single precision value in Register Pair HL
    15B4-15B6H 
    CALL 09B4HCALL MOVFRCD B4 09
    Call 09B4H (which moves the original value in BC/DE into ACCumulator)
    15B7-15B9
    CALL 1541HCALL COSCD 41 15
    Call the COSINE routine at 1541H (which computes the cosine for an angle given in radians. The angle must be a floating point value; the cosine will be returned in ACCumulator as a floating point value
    15BA-15BC
    JP 08A0HJP FDIVTC3 A0 08
    Jump to 08A0H to compute SIN(n)/ COS(n)and return the value as TAN(n)

    15BD-15E2 – LEVEL II BASIC ATN(n)ROUTINE– “ATN”.

    Single-precision only.(ACCumulator = ATN(ACCumulator)).
    A call to 15BD returns the angle in radians, for the floating point tangent value in ACCumulator. The angle will be left as a single precision value in ACCumulator.

    The method of computation used in this routine is:

    1. Test the sign of the tangent to see if a negative angle is in the 2nd or 4th quadrant. Set the flag to force the result to positive on exit. If the value is negative, invert the sign.
    2. Test magnitude of tangent. If it is < 1 go to step 3. Otherwise, compute its reciprocal and put the return address on the STACK that will calculate pi/2 – series value.
    3. Evaluate the series: (((x^2*c0+c1) x^2+c2) . c8)x
    4. If the flag from step 1 is not set, then invert the sign of the series result.
    5. If the original value is < 1 then return to the caller. Otherwise, compute pi/2-value from step 4 and then return.
    15BD-15BF–  ↳ ATN
    CALL 0955HCALL SIGNCD 55 09
    Go check the sign of argument (i.e., the single precision value in the ACCumulator)
    15C0-15C2
    CALL M,13E2HCALL M,PSHNEGFC E2 13
    If the single precision value in ACCumulator is negative then put a return address of 13E2H to the STACK
    15C3-15C5
    CALL M,0982HCALL M,NEGFC 82 09
    Go convert the negative number in ACCumulator to positive if necessary
    15C6-15C8
    LD A,(4124H)LD A,(FAC)3A 24 41
    We next want to see if the ACCumulator is > 1, so load Register A with the exponent of the tangent (which is a single precision value in ACCumulator)
    15C9-15CA
    CP 81HFE 81
    Check to see if the the exponent of the tangent (which is a single precision value in ACCumulator) is less than one
    15CB-15CC
    JR C,15D9HJR C,ATN238 0C
    Jump to forward 15D9H if the carry flag is set (i.e., the single precision value in ACCumulator is less than one)
    15CD-15CF
    LD BC,8100H01 00 81
    Load Register Pair BC with an exponent and a MSB for a single precision value
    15D0
    LD D,C51
    Zero the NMSB of the single precision value in Register D
    15D1
    LD E,C59
    Zero the LSB of the single precision value in Register E
    15D2-15D4
    CALL 08A2HCALL FDIVCD A2 08
    GOSUB 082AH to get the reciprocal of the tangent. This routine divides the single precision value in ACCumulator into the single precision constant in Register Pairs BC and DE
    15D5-15D7
    LD HL,0710HLD HL,FSUBS21 10 07
    Load Register Pair HL with a return address of 0710H (which is the subtract routine to be called once the series is calculated)
    15D8
    PUSH HLE5
    Save the value of the return address in Register Pair HL to the STACK
    15D9-15DB–  ↳ ATN2
    LD HL,15E3HLD HL,ATNCON21 E3 15
    Load Register Pair HL with the starting address for a series of single precision numbers for a set of computations
    15DC-15DE
    CALL 149AHCALL POLYXCD 9A 14
    Go do the set of computations
    15DF-15E1
    LD HL,158BHLD HL,PI221 8B 15
    Load Register Pair HL with the starting address of a single precision constant equal to 1.57079637029 (which is pi/2)
    15E2
    RETC9
    Return. The return address was set to 0710H above, which will then subtract the last term from pi/2 and then return

    15E3-1607 – SINGLE PRECISION CONSTANTS STORAGE LOCATION– “ATNCON”

    15E3–  ↳ ATNCON
    09
    The number of single precision constants (9) which follows is stored here
    15E4-15E7
    4A D7 3B 78
    A single precision constant equal to 0.00286622549 is stored here
    15E8-15EB
    02 6E 84 7B
    A single precision constant equal to -0.01616573699 is stored here
    15EC-15EF
    FE C1 2F 7C
    A single precision constant equal to 0.04290961441 is stored here
    15F0-15F3
    74 31 9A 3D
    A single precision constant equal to 0.07528963666 is stored here
    15F4-15F7
    84 3D 5A 7D
    A single precision constant equal to 0.10656264407 is stored here
    15F8-15FB
    C8 7F 91 7E
    A single precision constant equal to -0.14208900905 is stored here
    15FC-15FF
    E4 BB 4C 7E
    A single precision constant equal to 0.19993549561 is stored here
    1600-1603
    6C AA AA 7F
    A single precision constant equal to -0.33333146561 is stored here
    1604-1607
    00 00 00 01
    A single precision constant equal to 1.0 is stored here

    1608-18C8 – LIST OF BASIC RESERVED WORDS, TOKENS, AND ENTRY LOCATIONS AS FOLLOWS:

    The original ROM source code makes an interesting note about the order of these reserved words. Some reserved words are contained in other reserved words, which will cause a problem. They given examples of:

    • IF J=F OR T=5will process a FOR
    • INPis part of INPUT
    • IF T OR Q THENwill process a TO
    SO, the smaller word always has to appear later in the reserved word table.
    ABSD90977|ANDD225FD
    ASCF62A0F|ATNE415BD
    AUTOB72008|CDBLF10ADB
    CHR$(F72A1F|CINTEF0A7F
    CLEARB81E7A|CLOADB92C1F
    CLOSEA64185|CLS8401C9
    CMD854173|CONTB31DE4
    COSEl1541|CSAVEBA2BF5
    CSNGF00ABl|CVDE8415E
    CVIE64152|CVSE74158
    DATA881F05|DEFDD415B
    DEFDBL9B1E09|DEFINT991E03
    DEFSNG9A1E06|DEFSTR981E00
    DELETEB62BC6|DIM8A2608
    EDIT9D2E60|ELSE951F07
    END801DAE|EOFE94161
    ERLC224DD|ERRC324CF
    ERROR9E1FF4|EXPE01439
    FIELDA3417C|FIXF20B26
    FNBE4155|FOR811CA1
    FREDA27D4|GETA44174
    GOSUB911EB1|GOTO5D1EC2
    IF8F2039|INKEY$C9019D
    INPDB2AEF|INPUT89219A
    INSTRC5419D|INTD80B37
    KILLAA4191|LEFT$F82A61
    LENF32A03|LET8C1F21
    LINE9C41A3|LISTB42B2E
    LLISTB52B29|LOADA74188
    LOCEA4164|LOFEB4167
    LOGDF0809|LPRINTAF2067
    LSETAB4197|MEMC827C9
    MERGEA8418B|MID$FA2A9A
    MKD$EE4170|NAMEA9418E
    NEWBB1B49H|NEXT8722B6
    NOTCB25C4|ONA11FC6
    OPENA24179|ORD325F7
    OUTAO2AFB|PEEKE52CAA
    POINTC60132|POKEB12CB1
    POSDC27F5|PRINTB2206F
    PUTA54182|RANDOM8601D3
    READ8B21EF|REM931F07
    RESET820138|RESTORE901D91
    RESUME9F1FAFH|RETURN921EDEH
    RIGHT$F92A91|RNDDE14C9
    RSETAC419A|RUN8E1EA3
    SAVEAD41A0|SET830135
    SGND7098A|SINE21547
    SQRCD13E7|STEPcc2B01
    STOP941DA9|STR$F42836
    STRING$C42A2F|SYSTEMAE02B2
    TAB(BC2137|TANE315A8
    THENCA|TIME$C74176
    TOBD|TROFF971DF8
    TRON961DF8|USINGBF2CBD
    USRC127FE|VALFF2AC5
    VARPTRC024EB|+CD249F
    CE2532|CF
    /D0|?D1
    >D4|=D5
    <D6|&26
    FB3A93

    18C9-18F6 – STORAGE LOCATION FOR LEVEL II BASIC ERROR MESSAGES– “ERRTAB”

    18C9
    “NF”
    NEXT without FOR Error Message (Error 00H)
    18CB
    “SN”
    Syntax Error Error Message (Error 02H)
    18CD
    “RG”
    RETURN without GOSUB Error Message (Error 04H)
    18CF
    “OD”
    Out of DATA) Error Message (Error 06H)
    18D1
    “FC”
    Illegal Function Call Error Message (Error 08H)
    18D3
    “OV”
    Overflow Error Message (Error 0AH)
    18D5
    “OM”
    Out of Memory Error Message (Error 0CH)
    18D7
    “UL”
    Underfined Line Number Error Message (Error 0EH)
    18D9
    “BS”
    Subscript out of Range Error Message (Error 10H)
    18DB
    “DD”
    Redimensioned Array Error Message (Error 12H)
    18DD
    “/0”
    Division by Zero Error Message (Error 14H)
    18DF
    “ID”>
    Illegal Direct Operation Error Message (Error 16H)
    18E1
    “TM”
    Type Mismatch Error Message (Error 18H)
    18E3
    “OS”
    Out of String Message (Error 1AH)
    18E5
    “LS”
    Out of Memory Error Message (Error 1CH)
    18E7
    “ST”
    String Too Long Error Message (Error 1EH)
    18E9
    “CN”
    Can’t Continue Error Message (Error 20H)
    18EB
    “NR”
    No RESUME Error Message (Error 22H)
    18ED
    “RW”
    RESUME Without Error Error Message (Error 24H)
    18EF
    “UE”
    Unprintable Error Error Message (Error 26H)
    18F1
    “HO”
    Missing Operand Error Message (Error 28H)
    18F3
    “FD”
    Bad file Data Error Message (Error 2AH)
    18F5
    “L3”
    Disk BASIC Command Error Message (Error 2CH)

    18F7-1904 – STORAGE LOCATION FOR THE SINGLE PRECISION DIVISION ROUTINE
    This code is moved from 18F7-191DH to 4080H-40A5H during non-disk initial setup.

    18F7
    SUB 00HD6 00
    Subtract the LSB
    18F9
    LD L,A6F
    Restore the value to L
    18FA
    LD A,H7C
    Get the middle byte
    18FB
    SBC A,00HDE 00
    Subtract the middle byte
    18FD
    LD H,A67
    Move the difference to H
    18FE
    LD A,B78
    Get the MSB
    18FF
    SBC A,00HDE 00
    Subtract the MSB
    1901
    LD B,A47
    Move it back to A
    1902
    LD A,00H3E 00
    Clear A
    1904
    RETC9
    RETurn to CALLer

    1905-191C – STORAGE LOCATION FOR VALUES PLACED IN RAM UPON INITIALIZATION.

    This code is moved to 408E during non-disk initial setup.


    191D-1923 – MESSAGE STORAGE LOCATION– “ERR”

    191D-1923–  ↳ ERR
    “Error” + 00H20 45
    The word “ERROR”
    1924-1928–  ↳ INTX
    ” in ” + 00H
    The word ” IN “

    1929-192F – MESSAGE STORAGE LOCATION– “REDDY”

    1929-192F–  ↳ REDDY
    “READY” + 0DH + 00H
    The Level II BASIC READY message is stored here

    1930-1935 – MESSAGE STORAGE LOCATION– “BRKTXT”

    1930-1935–  ↳ BNKTXT
    “Break” + 00H
    The Level II BASIC BREAK message is stored here

    1936-1954 – SCAN STACK ROUTINE– “FNDFOR”

    This routine is called with DE as the address of the NEXT index. It scans the STACK backwards looking for a FORpush. If one is found, it gets the address of the index and compares with the DE that was in place when this routine was called. If it is equal, then it exits with A=0 and HL=Address of the variable. If it is not equal it will keep scanning until no FOR push is found and then exit with A<>0.

    According to the original ROM source, this routine is part of the general storage management routines, and if designed to find a FORentry on the STACK with the variable pointer passed in Register Pair DE.

    1936-1938–  ↳ FNDFOR
    LD HL,0004H21 04 00
    Load Register Pair HL with 4 so that we can backspace
    1939
    ADD HL,SP39
    Add the 4 (held in HL) to the current value of the STACK pointer. HL will hold the current STACK pointer, the STACK point will bump forward 4
    193A–  ↳ LOOPER
    LD A,(HL)7E
    Load Register A with the value held at the current STACK point MINUS 4. This is to enable seeing what type of data is on the STACK
    193B
    INC HL23
    Bump the value of the memory pointer in Register Pair HL so as to backspace one more byte in case a FORtoken is located
    193C-193D
    CP 81HFE 81
    Check to see if the value in Register A (which is the current STACK pointer – 4) is a FORtoken to make sure that the item in the STACK was associated with a FOR loop
    193E
    RET NZC0
    If the item isn’t associated with a FOR statement (as the value in Register A isn’t a FORtoken), exit out of this routine. This returns with A being non-zero because there was no FORpush
    193F
    LD C,(HL)4E
    If we are here, then the entry on the STACK is associated with a FOR statement. Load Register C with the LSB of the FOR‘s variable address
    1940
    INC HL23
    Bump the value of the memory pointer in Register Pair HL so as to backspace the current STACK pointer by yet another byte
    1941
    LD B,(HL)46
    Load Register B with the MSB of the FOR‘s variable address
    1942
    INC HL23
    Bump the value of the memory pointer in Register Pair HL so now HL will be the address of the FORvariable on the STACK
    1943
    PUSH HLE5
    Save the value in Register Pair HL (which is the address of the FORvariable) to the STACK
    1944
    LD L,C69
    Load Register L with the LSB of the FORvariable’s address in Register C
    1945
    LD H,B60
    Load Register H with the MSB of the FORvariable’s address in Register B
    1946-1947
    LD A,D
    OR E7A
    Z-80 Trick to check a Register Pair (in this case DE) for zero. Load D into A and then OR that against E. If both D and E are zero, then A will be zero. This sets up to handle a NEXT statement that doesn’t have a variable argument
    1948
    EX DE,HLEB
    Exchange the variable address in Register Pair HL with the variable address in Register Pair DE so that DE will now hold the address of the FORvariable from the STACK. This is to ensure that we return with DE pointing to the variable
    1949-194A
    JR Z,194DHJR Z,POPGOF28 02
    Skip the next 2 opcodes if the variable address in Register Pair DE was equal to zero, meaning that DE was not, in fact, pointing to the variable as we needed it to be
    194B
    EX DE,HLEB
    Exchange the variable address in Register Pair HL with the variable address in Register Pair DE so that HL will now have the address of the FORvariable from the STACK
    194C
    RST 18HCOMPARDF
    This routine was entered with DE being the address of the NEXTindex, so we need to compare that against the index from the STACK. To do this, we RST 18 to see if the variable address in HL is the same as in DE, so we call the COMPARE DE:HL routine, which numerically compares DE and HL. Will not work for signed integers (except positive ones). Uses the A-register only. The result of the comparison is returned in the status Register as: CARRY SET=HL<DE; NO CARRY=HL>DE; NZ=Unequal; Z=Equal)
    194D-194F–  ↳ POPGOF
    LD BC,000EH
    LD BC,FORSIZ01 0E 00
    Load Register Pair BC with the value to backspace (i.e., erase) the FORtoken (which is 10)
    1950
    POP HLE1
    Get the memory pointer from the STACK of the sign of the increment flag and put it in Register Pair HL
    1951
    RET ZC8
    If the variable in the FORblock matched the NEXTindex block, then RETURN with HL pointing to the bottom of the entry
    1952
    ADD HL,BC09
    If it didn’t match, execute that 10 byte stepback in BC for the next possible FORpush
    1953-1954
    JR 193AHJR LOOPER18 E5
    At this point, we should be pointing to the start of the NEXTentry, so JUMP to keep looking until the appropriate FORblock has been located

    1955-1962 – DATA MOVEMENT ROUTINE– “BLTU”

    This routine moves a variable into another area specified by the caller. On entry BC is set as the end address of the list to move (which is the upper limit); DE is set as the start address of the list to move; and HL is the end of the area to move it to.

    According to the original ROM source, this routine is part of the general storage management routines, and if designed to make space by shoving everything forward and to check to make sure a reasonable amount of space remains between the top of the STACK and the highest location transferred to. On Entry, HL should be the destination of the high address, DE should be the low address to be transferred there, and BC should be the high address to be transferred there. On exit, HL=DE=Low BC=The location LOW was moved to.

    1955-1957–  ↳ BLTU
    CALL 196CH
    CALL REASONCD 6C 19
    GOSUB to 196CH to make sure there’s enough room in memory for the string area and make sure the STACK won’t be overrun
    1958–  ↳ BLTUC
    PUSH BCC5
    The next 3 instructions are really just to exchange HL and BC. First, save the end address of the list to move (stored in BC) to the STACK
    1959
    EX (SP),HLE3
    Save the end address of the list to move (stored in the STACK now) to Register Pair HL
    195A
    POP BCC1
    Get the end address of the move from the STACK and put it in Register Pair BC
    195B–  ↳ BLTLOP
    RST 18HCOMPARDF
    Check is we are done by checking to see if the memory pointer in HL is the same as the memory pointer in DE (to see if the move is finished), so we call the COMPARE DE:HL routine, which numerically compares DE and HL. Will not work for signed integers (except positive ones). Uses the A-register only. The result of the comparison is returned in the status Register as: CARRY SET=HL<DE; NO CARRY=HL>DE; NZ=Unequal; Z=Equal)
    195C
    LD A,(HL)7E
    Get a byte from the source list to transfer and put it in Register A
    195D
    LD (BC),A02
    Transfer it by saving the byte into wherever BC is pointing
    195E
    RET ZC8
    Return if finished with the move (i.e., the memory pointer in Register Pair HL is the same as the value of the memory pointer in Register Pair DE)
    195F
    DEC BC0B
    Decrement the source address (in Register Pair BC)
    1960
    DEC HL2B
    Decrement the destination address (in Register Pair HL)
    1961-1962
    JR 195BHJR BLTLOP18 F8
    Loop until the entire block was moved

    1963-197D – MEMORY CHECK ROUTINE– “GETSTK”

    This routine computes the amount of space between HL and the end of memory at FFC6. On entry, Register C should hold the number of desired bytes.

    According to the original ROM source, this routine is part of the general storage management routines, and if designed to make sure that a certain number of locations remain available for the STACK. To use this routine, Register C needs to hold the number of two byte entries needed, and then do a CALL GETSTK. This routine must be called by any reoutine which puts an arbitrary amount of stuff into the STACK (such as a recursive routine like FRMEVL). It is also called by routines such as GOSUBand FORwhich make permanent entries in the STACK.

    1963–  ↳ GETSTK
    PUSH HLE5
    Save the value in Register Pair HL to the STACK
    1964-1966
    LD HL,(40FDH)LD HL,(STREND)2A FD 40
    Load Register Pair HL with the starting address of free memory (which is stored at 40FDH).
    Note: 40FDH-40FEH holds Free memory pointer
    1967-1968
    LD B,00H06 00
    Load Register B with zero
    1969
    ADD HL,BC09
    Add 2 times the number of bytes required to start of free area (held in Register Pair BC) to the value in Register Pair HL
    196A
    ADD HL,BC09
    Add the value in Register Pair BC to the value in Register Pair HL leaving HL to now contain the end of the free area
    196B-196C
    LD A,0E5H3E E5
    Z-80 Trick! See the general explanation at 10F8H
    196C–  ↳ REASON
    PUSH HLE5
    Now we check t omake sure there is at least “NUMLEV” bytes between the address and the top of the STACK. First, save the new free area pointer (which is the start) to the STACK
    196D-196E
    LD A,C6H
    LD A,256-(2*NUMLEV)3E C6
    Load Register A with C6H (which is the the LSB of FFC6H; the top of memory)
    196F
    SUB L95
    Subtract the LSB of the value of the new memory pointer in Register L from the value in Register A
    1970
    LD L,A6F
    Load Register L with the adjusted value in Register A (i.e., the free memory pointer resulting from subtracting the new starting address)
    1971-1972
    LD A,FFH3E FF
    Load Register A with the MSB of the top of memory
    1973
    SBC A,H9C
    Subtract the MSB of the new memory pointer in Register H from the value in Register A. If the free space list exceeds 7FFC6 then the Carry flag gets set, meaning memory overflowed
    1974-1975
    JR C,197AHJR C,OMERR38 04
    If the CARRY flag is set then HL was simply too big. So we need to display a ?OM ERRORsince we are out of memory
    1976
    LD H,A67
    Next we need to determine if the free space list has overflowed the STACK area so first we load Register H with the adjusted value in Register A
    1977
    ADD HL,SP39
    Add the value of the STACK pointer to the adjusted value in Register Pair HL. If we are OK, the CARRY FLAG will be set
    1978
    POP HLE1
    Restore the original HL on entry back into Register Pair HL
    1979
    RET CD8
    If the carry flag is set, then we have no overflow – so RETURN to CALLer

    197A-197B – ?OM ERROR ENTRY POINT– “OMERR”

    197A-197B–  ↳ OMERR
    LD E,0CHLD E,ERROM1E 0C
    Load Register E with the ?OM ERROR
    197C-197D
    JR 19A2HJR ERROR18 24
    Display an ?OM ERRORmessage

    197E-1AF7 – LEVEL II BASIC COMMAND MODE ERROR HANDLING– “PRGEND”

    197E-1980–  ↳ PRGEND
    LD HL,(40A2H)LD HL,(CURLIN)2A A2 40
    Load Register Pair HL with the value of the current BASIC line number.
    Note: 40A2H-40A3H holds the current BASIC line number
    1981
    LD A,H7C
    Test to see if this was a direct command instead a line number by first loading Register A with the MSB of the current BASIC line number in Register H
    1982
    AND LA5
    Combine the LSB of the current BASIC line number in Register L with the MSB of the current line number in Register A
    1983
    INC A3C
    Bump the value of the combined BASIC line number in Register A. If the current line is FFFFH then we have not started execution of a BASIC program yet (meaning we are still in the inputting phase)
    1984-1985
    JR Z,198EHJR Z,ENDCNJ28 08
    Jump to 198EH if Level II BASIC is still in the command mode (rather than being in execution mode)
    1986-1988
    LD A,(40F2H)LD A,(ONEFLG)3A F2 40
    Load Register A with the error override flag (i.e., if there is an ON ERROR GOTO active)
    1989
    OR AB7
    Check to see if the error flag is set
    198A-198B
    LD E,22HLD E,ERRNR1E 22
    Load Register E with a ?NR ERRORcode
    198C-198D
    JR NZ,19A2HJR NZ,ERROR20 14
    Jump to 19A2H if the error flag is set (meaning there was no RESUMEaddress)
    198E-1990–  ↳ ENDCNJ
    JP 1DC1HJP ENDCONC3 C1 1D
    Otherwise, jump to 1DC1H (to END) because there was an error in the input phase
    1991-1993–  ↳ DATSNE
    LD HL,(40DAH)LD HL,(DATLIN)2A DA 40
    Load Register Pair HL with the DATAline number (which is stored at 16602).
    Note: 40DAH-40DBH holds DATA line number
    1994-1996
    LD (40A2H),HLLD (CURLIN),HL22 A2 40
    Make the DATA line number into the CURRENT line number by saving it in Register Pair HL.
    Note: 40A2H-40A3H holds the current BASIC line number
    1997-1998–  ↳ SNERR
    LD E,02HLD E,ERRSN1E 02
    Load Register E with a ?SN ERRORcode.
    SN ERROR entry point

    The next few instructions are all Z-80 tricks to allow Register E to hold its value while passing through them all.

    1999-199B–  ↳ DV0ERR
    LD BC,141EH01 1E 14
    Z-80 Trick. JUMPing here will load BC but skip the reload of Register E as follows
    199A-199B
    LD E,14HLD E,ERRDV001 1E 14
    Load Register E with a ?/0 ERRORcode
    199C-199E–  ↳ NFERR
    LD BC,001EH01 1E 00
    Z-80 Trick. JUMPing here will load BC but skip the reload of Register E as follows
    199D-199E
    LD E,00HLD E,ERRNF1E 00
    Load Register E with a ?NF ERRORcode.
    ?NF ERRORentry point
    199F-19A1–  ↳ REERR
    LD BC,241EHLD E,ERRRE01 1E 24
    Z-80 Trick. JUMPing here will load BC but skip the reload of Register E as follows
    19A0-19A1
    LD E,24HLD E,ERRRE1E 24
    Load Register E with a ?RW ERRORcode.
    ?RW ERRORentry point
    19A2-19A4–  ↳ ERROR
    LD HL,(40A2H)LD HL,(CURLIN)2A A2 40
    Load Register Pair HL with the value of the current BASIC line number which has the error. Note: 40A2H-40A3H holds the current BASIC line number
    19A5-19A7
    LD (40EAH),HLLD (ERRLIN),HL22 EA 40
    Save the value of the current BASIC line number with the error into the RAM Location which tracks the ERL variable.
    Note: 40EAH-40EBH holds Line number with error
    19A8-19AA
    LD (40ECH),HLLD (DOT),HL22 EC 40
    Save the value of the current BASIC line number with the error into the RAM location used for EDIT or LIST
    19AB-19AD–  ↳ ERRESM
    LD BC,19B4HLD BC,ERRMOR01 B4 19
    Load Register Pair BC with the return address of 19B4H which is the continuation address after a reinitialization

    19AEH – Routine used when a catastropic BREAK key occurs

    19AE-19B0–  ↳ ERESET
    LD HL,(40E8H)LD HL,(SAVSTK)2A E8 40
    Load Register Pair HL with the value of the STACK pointer (which is stored at 40E8H).
    Note: 40E8H-40E9H holds STACK pointer pointer
    19B1-19B3
    JP 1B9AHJP STKERRC3 9A 1B
    Jump to 1B9AH to reinitialize the system variables, including reinitializing the STACK to the location now held in SAVSTK

    19B4 – LEVEL II BASIC COMMAND MODE ERROR HANDLING– “ERRMOR”

    19B4–  ↳ ERRMOR
    POP BCC1
    Discard the entry at the top of the STACK (which is the FNDFOR Stopper)
    19B5
    LD A,E7B
    Load Register A with the value of the error code in Register E
    19B6
    LD C,E4B
    Load Register C with the value of the error code in Register E, as we will need to restore it later as well
    19B7-19B9
    LD (409AH),ALD (ERRFLG),A32 9A 40
    Save the value of the error code (from in Register A) into 409AH.
    Note: 409AH holds the RESUME flag
    19BA-19BC
    LD HL,(40E6H)LD (SAVTXT),A2A E6 40
    Load Register Pair HL with the value of the current BASIC program pointer (which is stored in 40E6H) (i.e., the address of the last byte executed in the current line).
    Note: 40E6H-40E7H holds the temporary storage location
    19BD-19BF
    LD (40EEH),HLLD (ERRTXT),HL22 EE 40
    Save the value of the current BASIC program pointer (which is stored in 40EEH) in Register Pair HL.
    Note: 40EEH-40EFH is used by RESUME
    19C0
    EX DE,HLEB
    Load Register Pair DE with the value of the current BASIC program pointer in Register Pair HL so that the SAVTXT is preserved in Register Pair DE
    19C1-19C3
    LD HL,(40EAH)LD HL,(ERRLIN)2A EA 40
    Load Register Pair HL with the line number where the error occurred.
    Note: 40EAH-40EBH holds Line number with error
    19C4,19C4
    LD A,H
    AND L7C
    Z-80 Trick to test if HL is zero or not – If H AND L are 0, then they are each zero
    19C6
    INC A3C
    Bump the value of the combined current BASIC line number. If the line with the error was a direct command, this would have been FFFF, so the INC A will then set the ZERO flag if it was direct
    19C7-19C8
    JR Z,19D0HJR Z,NTMDCN28 07
    If this was a direct command (and ZERO FLAG is set), then we do not want to modify OLDTXT or OLDLIN, so jump to 19D0H
    19C9-19CB
    LD (40F5H),HLLD (OLDLIN),HL22 F5 40
    Let OLDLIN = ERRLIN by saving the value of the current BASIC line number in Register Pair HL to (40F5H).
    Note: 40F5H-40F6H holds the last line number executed
    19CC
    EX DE,HLEB
    Get bacK SAVTXT by swapping HL and DE
    19CD-19CF
    LD (40F7H),HLLD (OLDTXT),HL22 F7 40
    Let OLDTXT = SAVTXT by saving the value of the current BASIC program pointer in Register Pair HL.
    Note: 40F7H-40F8H holds Last byte executed
    19D0-19D2–  ↳ NTMDCN
    LD HL,(40F0H)LD HL,(ONELIN)2A F0 40
    See if we are trapping errors by first loading Register Pair HL with the current ON ERRORaddress.
    Note: 40F0H-40F1H is used by ON ERROR
    19D3,19D4
    LD A,H
    OR L7C
    Z-80 Trick to test if HL is zero or not – If H OR L are 0, then they are each zero
    19D5
    EX DE,HLEB
    Load Register Pair DE with the ON ERRORaddress to go to if there’s an error which is currently in Register Pair HL
    19D6-19D8
    LD HL,40F2HLD HL,ONEFLG21 F2 40
    Load Register Pair HL with the address of the error flag (which is 40F2H).
    Note: 40F2H holds Error flag
    19D9-19DA
    JR Z,19E3HJR Z,NOTRAP28 08
    Jump to 19E3H (to error out) if we aren’t otherwise trapping errors (because there isn’t an ON ERRORaddress)
    19DB
    AND (HL)A6
    Since Register A is currently non-zero (or we would have jumped away in the prior instruction), combining that number with with the ONEFLG will result in either a 0 or non-zero based on whether whether the flag was already set
    19DC-19DD
    JR NZ,19E3HJR NZ,NOTRAP20 05
    If the NZ FLAG is set, then it was already set, so JUMP to 19E3H (to error out)
    19DE
    DEC (HL)35
    Force an error
    19DF
    EX DE,HLEB
    Load Register Pair HL with the ON ERRORline address pointer held in Register Pair DE
    19E0-19E2
    JP 1D36HJP GONE4C3 36 1D
    Jump to 1D36H to make that happen

    19E3 – LEVEL II BASIC COMMAND MODE ERROR HANDLING– “NOTRAP”

    19E3–  ↳ NOTRAP
    XOR AAF
    Zero Register A
    19E4
    LD (HL),A77
    Reset ONMEFLG. Clear the error override flag by save a zero (from Register A) as the current error flag at the location of the memory pointer in Register Pair HL
    19E5
    LD E,C59
    Restore the error code (which was tucked away in Register C at 19B6H) into Register E
    19E6-19E8
    CALL 20F9HCALL CRDONZCD F9 20
    We need to position the video to the next line, so go display a carriage return on the video display if necessary
    19E9-19EB
    LD HL,18C9HLD HL,ERRTAB21 C9 18
    Load Register Pair HL with the starting address for the table of error messages
    19E6-19E8
    CALL 41A6HCALL EXDSKRCD F9 20
    Check to see if DOS should be handling this
    19EF
    LD D,A57
    Since we are non-DOS, we continue by loading Register D with zero
    19F0-19F1
    LD A,3FHLD A,”?”3E 3F
    Load Register A with a ?
    19F2-19F4
    CALL 032AHCALL OUTDOCD 2A 03
    GOSUB to 032AH to display the question mark in Register A
    19F5
    ADD HL,DE19
    Add the value of the error code in Register Pair DE to the starting address of the table of error messages in Register Pair HL
    19F6
    LD A,(HL)7E
    Load Register A with the first character of the error message at the location of the table pointer in Register Pair HL
    19F7-19F9
    CALL 032AHCALL OUTDOCD 2A 03
    GOSUB to 032AH to display the first character of the error message in Register A
    19FA
    RST 10HCHRGETD7
    Error codes are 2 characters so we need to set the second character of the error in Register A, 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.
    19FB-19FD
    CALL 032AHCALL OUTDOCD 2A 03
    GOSUB to 032AH to display the second character of the error message in Register A
    19FE-1A00
    LD HL,191DHLD HL,ERR21 1D 19
    Load Register Pair HL with 191DH which is the starting address of the word “ERROR” message
    1A01
    PUSH HLE5
    Save the starting address of the word “ERROR” (held in HL) to the STACK
    1A02-1A04
    LD HL,(40EAH)LD HL,(ERRLIN)2A EA 40
    Load Register Pair HL with the value of the BASIC line number causing the error.
    Note: 40EAH-40EBH holds Line number with error
    1A05
    EX (SP),HLE3
    Exchange the value of the current BASIC line number in Register Pair HL with the starting address of the Level II BASIC ERROR message to the STACK
    1A06–  ↳ ERRFIN
    CALL 28A7HCALL STROUTCD A7 28
    GOSUB to 28A7 to display the entire word ERROR on screen
    1A09
    POP HLE1
    Get the value of the BASIC line number with the error from the STACK and put it in Register Pair HL
    1A0A-1A0C
    LD DE,FFFEHLD DE,0 + 6553411 FE FF
    Load Register Pair DE with FFFEH.

    This basically reserves the line number 65534 as a trigger for the next few steps
    1A0D
    RST 18HCOMPARDF
    Now we need to compare the BASIC line number causing the error (held in HL) with FFFEH (held in DE) so as to see if we are in the initialization routine, so we call the COMPARE DE:HL routine, which numerically compares DE and HL. Will not work for signed integers (except positive ones). Uses the A-register only. The result of the comparison is returned in the status Register as: CARRY SET=HL<DE; NO CARRY=HL>DE; NZ=Unequal; Z=Equal)
    1A0E-1A10
    JP Z,0674HJP Z,INITCA 74 06
    Jump to 0674H if we are in the initialization routine because the error line number was FFFEH
    1A11
    LD A,H7C
    Next, let’s see if we were in direct mode (i.e., entered from the command line). Load Register A with the MSB of the current BASIC line number in Register H
    1A12
    AND LA5
    Combine the LSB of the current BASIC line number in Register L with the MSB of the current BASIC line number in Register A
    1A13
    INC A3C
    Bump the combined value of the current BASIC line number in Register A to test to see if the line number is 00H (meaning command mode)
    1A14-1A16
    CALL NZ,0FA7HCALL NZ,INPRTC4 A7 0F
    GOSUB to 0FA7H to display the current BASIC line number in Register Pair HL if Level II BASIC isn’t in the command mode

    The original ROM has this note: The following code is for “LIST” command stopping and for returning from a failed “CVER” and to correct a direct GOSUB which does input.

    1A17
    LD A,0C1H3E C1
    Z-80 Trick! If passing through from the above routine, then A will loaded and the instruction at 1A18 will be skipped.
    1A18–  ↳ STPRDY
    POP BCC1
    Get the value from the STACK and put it in Register Pair BC
    1A19-1A1BH
    “READY”
    CALL 038BHCALL FINLPTCD 8B 03
    Go set the current output device to the video display.
    Re-entry into BASIC command mode entry point. (see 6CCH also)
    1A1C-1A1EH
    CALL 41ACHCALL PRGFINCD AC 41
    1A1F-1A21
    CALL 01F8HCALL CTOFFCD F8 01
    Go turn off the cassette recorder
    1A22-1A24
    CALL 20F9HCALL CRDONZCD F9 20
    Go display a carriage return if necessary
    1A25-1A27
    LD HL,1929HLD HL,REDDY21 29 19
    Load Register Pair HL with the starting address of word “READY”
    1A28-1A2A–  ↳ REPINI
    CALL 28A7HCALL STROUTCD A7 28
    Display the word “READY”
    1A2B-1A2D
    LD A,(409AH)LD A,(ERRFLG)3A 9A 40
    Load Register A with the value of the current error code
    1A2E-1A2F
    SUB 02HD6 02
    Check to see if the current error code is a ?SN ERRORcode by subtracting 2 from the error code, resulting in a zero if its a SN ERROR and any other number if it isn’t
    1A30-1A32
    CALL Z,2E53HCALL Z,ERREDTCC 53 2E
    If the current error code is a SN ERROR code, then automatically enter EDIT mode on that line via this CALL

    1A33 – MAIN LEVEL II BASIC INTERPRETER ENTRY– “MAIN”

    If the jump here was from an AUTOcall, (40E4H) will have the increment number, (40E1H) will be 0 if no AUTOand non-zero if AUTO, and (40E2H) will have the starting line number.

    While not for this specific routine, this is the best place to mention it. Vernon Hester has pointed out that while BASIC is supposed to ignore spaces in commands, it fails to properly handle some commands because of spaces

    If you have a statement with a type declaration tag after a number and a space before an add or subtract arithmetic operator, the ROM applies the operator as a unary operator for the following argument
      Example: PRINT 2% + N will display two numbers. PRINT 2%+N will display one number.
    Also, if you have a statement with a type declaration tag after a number and a space before a multiply or divide arithmetic operator, the ROM willl throw a ?SN ERROR
      Example: PRINT 2%*N will display a number. PRINT 2% * N will display a ?SN ERROR.

    1A33-1A35–  ↳ MAIN
    LD HL,FFFFH21 FF FF
    Load Register Pair HL with the command mode line number
    1A36-1A38
    LD (40A2H),HLLD (CURLIN),HL22 A2 40
    Set CURLIN up for direct command mode by saving FFFFH as the current BASIC line number.
    Note: 40A2H-40A3H holds the current BASIC line number
    1A39-1A3B
    LD A,(40E1H)LD A,(AUTFLG)3A E1 40
    Load Register A with the value of the AUTOflag. It will be zero if not in AUTO, and anything else if in AUTO
    1A3C
    OR AB7
    Check to see if in the AUTOmode
    1A3D-1A3E
    JR Z,1A76HJR Z,NTAUTO28 37
    Jump to 1A76H if not in the AUTOmode
    1A3F-1A41
    LD HL,(40E2H)LD HL,(AUTLIN)2A E2 40
    We are in AUTOmode so load Register Pair HL with the current AUTOline number.
    Note: 40E2H-40E3H holds Current BASIC line number
    1A42
    PUSH HLE5
    Save the current AUTOline number (stored in Register Pair HL) to the STACK
    1A43-1A45
    CALL 0FAFHCALL LINPRTCD AF 0F
    Display the current AUTO line number on the screen via a GOSUB to 0FAFH to call the HL TO ASCII routine at 0FAFH (which converts the value in the HL Register Pair (assumed to be an integer) to ASCII
    1A46
    POP DED1
    Get the current AUTOline number from the STACK and put it in Register Pair DE
    1A47
    PUSH DED5
    Save the current AUTOline number in Register Pair DE to the STACK
    1A48-1A4A
    CALL 1B2CHCALL FNDLINCD 2C 1B
    See if the line number already exists by CALLing the SEARCH FOR LINE NUMBER routine at 1B2CH which looks for the line number specified in DE. Returns C/Z with the line found in BC, NC/Z with line number is too large and HL/BC having the next available location, or NC/NZ with line number not found, and BC has the first available one after that
    1A4B-1A4C
    LD A,2AH3E 2A
    Load Register A with a (which will be code for a matching line number)
    1A4D-1A4E
    JR C,1A51HJR C,AUTELN38 02
    If the call to 1B2CH shows a matching line number was found in the BASIC program (by returning a C), skip the next instruction so that Register A keeps the
    1A4F-1A50
    LD A,20HLD A,” “3E 20
    If we are here, then there was no matching line number so we need to change the next character from a (which is loaded into Register A which but is not applicable) to a SPACE
    1A51-1A53–  ↳ AUTELN
    CALL 032AHCALL OUTDOCD 2A 03
    Go display the character in Register A on the video display (which will be a “*” if a matching line number was found)
    1A54-1A56
    CALL 0361HCALL INLINCD 61 03
    GOSUB to 0361H to read a line into the buffer
    1A57
    POP DED1
    Get the current line number from the STACK and put it in Register Pair DE
    1A58-1A59
    JR NC,1A60HJR NC,AUTGOD30 06
    Skip the next 3 opcodes if the BREAKkey wasn’t pressed
    1A5A–  ↳ AUTRES
    XOR AAF
    The BREAKkey was pressed so we need to zero Register A to clear the AUTO increment flag
    1A5B-1A5D
    LD (40E1H),ALD (AUTFLG),A32 E1 40
    Save the value in Register A as the current AUTO flag (to turn off AUTO)
    1A5E-1A5F
    JR 1A19HJR READY18 B9
    Jump to the normal command mode “READY” routine at 1A19H

    1A60H – Part of the AUTO command– “AUTGOD”

    1A60-1A62–  ↳ AUTGOD
    LD HL,(40E4H)LD HL,(AUTINC)2A E4 40
    Load Register Pair HL with the value of the AUTOincrement.
    Note: 40E4H-40E5H holds AUTO increment
    1A63
    ADD HL,DE19
    Since we didn’t BREAK out of the routine we need to keep processing the AUTOso we add the value of the AUTO line number in Register Pair DE with the AUTO increment value in Register Pair HL
    1A64-1A65
    JR C,1A5AHJR C,AUTRES38 F4
    If that addition to the next AUTOline number causes an overflow (by triggering the Carry flag), jump to 1A5AH which is the same as if the BREAKkey was hit
    1A66
    PUSH DED5
    Save the current AUTOline number in Register Pair DE to the STACK
    1A67-1A69
    LD DE,FFF9H11 F9 FF
    Load Register Pair DE with the maximum BASIC line number of FFF9H (=65529).

    There is an explanation at 1E5AH as to why 65529 is the highest possible line number (vs 65535 which would make more sense)
    1A6A
    RST 18HCOMPARDF
    Now we need to compare the adjusted AUTOline number (in HL) with the maximum BASIC line number (in DE), so we call the COMPARE DE:HL routine, which numerically compares DE and HL. Will not work for signed integers (except positive ones). Uses the A-register only. The result of the comparison is returned in the status Register as: CARRY SET=HL<DE; NO CARRY=HL>DE; NZ=Unequal; Z=Equal)
    1A6B
    POP DED1
    Get the current AUTOline number from the STACK and put it in Register Pair DE
    1A6C-1A6D
    JR NC,1A5AHJR NC,AUTRES30 EC
    If the adjusted AUTO line number in Register Pair HL is too large, jump to 1A5AH which is the same as if the BREAKkey was hit
    1A6E-1A70
    LD (40E2H),HLLD (AUTLIN),HL22 E2 40
    Save the adjusted AUTOline number in Register Pair HL as the current AUTOline number.
    Note: 40E2H-40E3H holds Current BASIC line number
    1A71-1A72
    OR 0FFHF6 FF
    Set all non-zero condition codes by ORing Register A with FFH
    1A73-1A75
    JP 2FEBHJP EDITRTC3 EB 2F
    Jump to the EDITroutine to save the current BASIC line

    1A76H – Part of the AUTO command– “NTAUTO”

    1A76-1A77–  ↳ NTAUTO
    LD A,3EHLD A,”>”3E 3E
    Load Register A with a >symbol (i.e., the prompt that follows READY)
    1A78-1A7A
    CALL 032AHCALL OUTDOCD 2A 03
    Go display the Level II BASIC prompt in Register A on the video display
    1A7B-1A7D
    CALL 0361HCALL INLINCD 61 03
    GOSUB to 0361H to accept input. HL will hold the buffer address for that input
    1A7E-1A80
    JP C,1A33HJP C,MAINDA 33 1A
    Jump to 1A33H if the BREAKkey was pressed
    1A81
    RST 10HCHRGETD7
    Since we need to bump the current input buffer pointer in Register Pair HL until it points to the first 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.
    1A82
    INC A3C
    Bump the value of the character in Register A. This sets the status flags but saves the carry flag
    1A83
    DEC A3D
    Decrement the value of the character in Register A so we can test for an end of statement
    1A84-1A86
    JP Z,1A33HJP Z,MAINCA 33 1A
    Jump to 1A33H if we have an end of statement or a blank statement
    1A87
    PUSH AFF5
    Save the value in Register Pair AF to the STACK (including the carry flag)
    1A88-1A8A
    CALL 1E5AHCALL LINGETCD 5A 1E
    Call the ASCII TO INTEGER routine at 1E5AH which 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
    1A8B–  ↳ BAKSP
    DEC HL2B
    Top of a loop. Decrement the value of the input buffer pointer in Register Pair HL so that it will point to the previous character
    1A8C
    LD A,(HL)7E
    Fetch the character at the location of the input buffer pointer in Register Pair HL
    1A8D-1A8E
    CP 20HCP ” “FE 20
    Check to see if the character at the location of the input buffer pointer in Register A is a SPACE
    1A8F-1A90
    JR Z,1A8BHJR Z,BAKSP28 FA
    Loop back to 1A8BH if the character at the location of the input buffer pointer in Register A is a space
    1A91
    INC HL23
    Now HL points to the last non-space character, so we need to advance one — Bump the value of the input buffer pointer in Register Pair HL so that it points to the first character following a line number
    1A92
    LD A,(HL)7E
    Load Register A with that character
    1A93-1A94
    CP 20HCP ” “FE 20
    Check to see if the character at the location of the input buffer pointer in Register A is a space
    1A95-1A97
    CALL Z,09C9HCALL Z,INXHRTCC C9 09
    If it is a space then skip it by a GOSUB to 09C9H which bumps the value of the input buffer pointer in Register Pair HL if necessary
    1A98–  ↳ EDENT
    PUSH DED5
    Save the BASIC line number in Register Pair DE (which was converted from ASCII to an integer in 1A88H through a call to 1E5AH) to the STACK
    1A99-1A9B
    CALL 1BC0HCALL CRUNCHCD C0 1B
    Tokenize the input via a GOSUB to 1BC0H. BC will equal the length of the encoded statement when its done
    1A9C
    POP DED1
    Restore the BASIC line number (which is an integer) from the STACK
    1A9D
    POP AFF1
    Get the carry flag (from the 1A81H character fetch) and put it in Register Pair AF to aid in determining if there even was a line number
    1A9E-1AA0
    LD (40E6H),HLLD (SAVTXT),HL22 E6 40
    Save the input buffer pointer in Register Pair HL t the temporary storage area for use in RESUMEing a direct statement.
    Note: 40E6H-40E7H holds the temporary storage location
    1A99-1A9B
    CALL 41B2HCALL DIRDOCD C0 1B
    Check to see if DOS should be taking this over
    1AA4-1AA6
    JP NC,1D5AHJP NC,GONED2 5A 1D
    If NC is set then this was a direct statement, so JUMP to 1D5AH as there wasn’t a line number with the input
    1AA7
    PUSH DED5
    Save the BASIC line number (as an integer stored in Register Pair DE) to the STACK
    1AA8
    PUSH BCC5
    Save the length (i.e., character count) of the tokenized input (stored in Register Pair BC) to the STACK
    1AA9
    XOR AAF
    Zero Register A
    1AAA-1AAC
    LD (40DDH),ALD (BFKLFL),A32 DD 40
    Save the value in Register A (which is a 00H) as the input flag.
    Note: 40DDH holds the BUFFER KILLED flag
    1AAD
    RST 10HCHRGETD7
    Since we need find the first token, we have to bump the current input buffer pointer in Register Pair HL until it points to the next character by calling 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.
    1AAE
    OR AB7
    Set the flags
    1AAF
    PUSH AFF5
    Save the status flag to the STACK
    1AB0
    EX DE,HLEB
    Load Register Pair HL with the integer value of the BASIC line number (held in Register Pair DE)
    1AB1-1AB3
    LD (40ECH),HLLD (DOT),HL22 EC 40
    Save the integer value of the line number (held in Register Pair HL) to 40ECH.
    Note: 40ECH-40EDH holds EDIT line number
    1AB4
    EX DE,HLEB
    Exchange the value of the input buffer pointer in Register Pair DE with the integer value of the BASIC line number in Register Pair HL. This will fill DE with the line number for the search routine in the next instruction
    1AB5-1AB7
    CALL 1B2CHCALL FNDLINCD 2C 1B
    Call the SEARCH FOR LINE NUMBER routine at 1B2CH which looks for the line number specified in DE. Returns C/Z with the line found in BC, NC/Z with line number is too large and HL/BC having the next available location, or NC/NZ with line number not found, and BC has the first available one after that
    1AB8–  ↳ LEXIST
    PUSH BCC5
    Save the address of the line number in the BASIC program (if it exists) in Register Pair BC to the STACK
    1AB9-1ABB
    CALL C,2BE4HCALL C,DELDC E4 2B
    Delete the line since there wasn’t a matching line number in the BASIC program. The GOSUB to 2BE4H will move the closest line number up in memory to make room for another line
    1ABC–  ↳ NODEL
    POP DED1
    REstore the pointer to the place to insert the line from the STACK into Register Pair DE
    1ABD
    POP AFF1
    Restore the status from the 1AADH token scan into Register Pair AF to see if the line had anything on it
    1ABE
    PUSH DED5
    Save the integer address of the BASIC line number where we need to start fixing links to the STACK
    1ABF-1AC0
    JR Z,1AE8HJR Z,FINI28 27
    Jump to 1AE8H if there was a matching line number in the BASIC program; otherwise we move on and a new line has to be added as follows
    1AC1
    POP DED1
    Clear the STACK from the start of the fix links
    1AC2-1AC4
    LD HL,(40F9H)LD HL,(VARTAB)2A F9 40
    Load Register Pair HL with the end of the BASIC program pointer.
    Note: 40F9H-40FAH holds the starting address of the simple variable storage area
    1AC5
    EX (SP),HLE3
    Exchange the length of the tokenized input to the STACK with the end of the BASIC program pointer (VARTAB) in Register Pair HL
    1AC6
    POP BCC1
    Get the end of the BASIC program pointer (VARTAB) from the STACK and put it in Register Pair BC
    1AC7
    ADD HL,BC09
    Add the end of the BASIC program pointer in Register Pair BC to the value of the length of the tokenized input in Register Pair HL
    1AC8
    PUSH HLE5
    Save the adjusted end of the BASIC program pointer (VARTAB) in Register Pair HL to the STACK
    1AC9-1ACB
    CALL 1955HCALL BLTUCD 55 19
    Go check to see if there is enough room in memory for the new BASIC line by GOSUB to 1955H
    1ACC
    POP HLE1
    Get the new end of the BASIC program pointer from the STACK and put it in Register Pair HL
    1ACD-1ACF
    LD (40F9H),HLLD (VARTAB),HL22 F9 40
    Save the new end of the BASIC program pointer in Register Pair HL.
    Note: 40F9H-40FAH holds the starting address of the simple variable storage area
    1AD0
    EX DE,HLEB
    Load Register Pair HL with the address of the BASIC line
    1AD1
    LD (HL),H74
    Save the MSB of the address of the BASIC line in Register H
    1AD2
    POP DED1
    Get the value of the BASIC line number from the STACK and put it in Register Pair DE
    1AD3
    PUSH HLE5
    Save the value of the memory pointer in Register Pair HL to the STACK. This will be the place to start to fix links
    1AD4
    INC HL23
    Bump the value of the memory pointer in Register Pair HL (which bumps it to the LSB of the line number entry). This is so that the ROM doesn’t think that this link is the end of the program
    1AD5
    INC HL23
    Bump the value of the memory pointer in Register Pair HL (which bumps it to the MSB of the line number entry)
    1AD6
    LD (HL),E73
    Save the LSB of the BASIC line number in Register E at the location of the memory pointer in Register Pair HL
    1AD7
    INC HL23
    Bump the value of the line number memory pointer in Register Pair HL
    1AD8
    LD (HL),D72
    Save the MSB of the BASIC line number in Register D at the location of the memory pointer in Register Pair HL. At this point DE should have the binary value for the new line number
    1AD9
    INC HL23
    Bump the value of the BASIC line number in Register Pair HL
    1ADA
    EX DE,HLEB
    Load Register Pair DE with first data byte address following the line number (held in Register Pair HL)
    1ADB-1ADD
    LD HL,(40A7H)LD HL,(BUFPNT)2A A7 40
    Load Register Pair HL with the value of the tokenized input pointer
    Note: 40A7H-40A8H holds Input Buffer pointer
    1ADE
    EX DE,HLEB
    Exchange the value of the memory pointer in Register Pair DE with the value of the tokenized input pointer in Register Pair HL
    1ADF
    DEC DE1B
    Decrement the value of the tokenized input buffer pointer in Register Pair DE
    1AE0
    DEC DE1B
    Decrement the value of the tokenized input buffer pointer in Register Pair DE
    1AE1–  ↳ MLOOPR
    LD A,(DE)1A
    Top of a loop to transfer the line. Load Register A with the value at the location of the tokenized input buffer pointer in Register Pair DE
    1AE2
    LD (HL),A77
    Save the value in Register A at the location of the memory pointer in Register Pair HL
    1AE3
    INC HL23
    Bump the store address (held in Register Pair HL)
    1AE4
    INC DE13
    Bump the fetch address (held in Register Pair DE)
    1AE5
    OR AB7
    Check to see if the character in Register A is an end of the line character
    1AE6-1AE7
    JR NZ,1AE1HJR NZ,MLOOPR20 F9
    Since a 0 will mark the end of the line, so long as NZ is set we need to keep looping, so loop backup until the whole of the new BASIC line has been stored in memory
    1AE8–  ↳ FINI
    POP DED1
    Get the address of the line in the program table from the STACK and put it in Register Pair DE. This would be the start of link fixing area
    1AE9-1AEB
    CALL 1AFCHCALL CHEADCD FC 1A
    Go fix/update line pointers for all lines following the new line
    1AEC-1AEA
    CALL 41B5HCALL EXFINDCD B5 41
    Check to see if DOS should be handling something
    1AEF-1AF1
    CALL 1B5DHCALL RUNCCD 5D 1B
    Do a clear, set up the STACK, and update all the BASIC line pointers
    1AF2-1AF4
    CALL 41B8HCALL EXFIN2CD B8 41
    Check to see if DOS should be handling something
    1AF5-1AF7
    JP 1A33HJP MAINC3 33 1A
    Go get the input again

    1AF8-1B0F – LINE POINTERS ROUTINE– “LINKER”

    This routine fixes the line pointers in a BASIC program. This is useful, for instance for a renumber program which has to move BASIC program lines from one location in memory to an other, which means that the line pointers would no longer be valid. This routine will fix them. Registers A, HL and DE are used.

    1AF8-1AFA–  ↳ LINKER
    LD HL,(40A4H)LD HL,(TXTTAB)2A A4 40
    Load Register Pair HL with the start of the BASIC program pointer (called the PST).
    NOTE: 40A4H-40A5H holds the starting address of BASIC program text also known as the PROGRAM STATEMENT TABLE (PST)
    1AFB
    EX DE,HLEB
    Move the PST address to DE

    A note in the original rom source code says that CHEAD goes through the program storage area in RAM and fixes up all the links. The end of each line is found by searching for a zero. The double zero link is used to detect the end of the program.

    1AFC,1AFD–  ↳ CHEAD
    LD H,D
    LD L,E62
    LET HL = DE
    1AFE
    LD A,(HL)7E
    Load Register A with the value at the location of the memory pointer in Register Pair HL so that we can see if we are at the end of a chain
    1AFF
    INC HL23
    Bump the value of the memory pointer in Register Pair HL
    1B00
    OR (HL)B6
    Since the first 2 bytes of each line contains the address of the next line, a 0000H would signify the end byte. With this, check to see if the character at the location of the memory pointer in Register Pair HL is an end of the BASIC program character
    1B01
    RET ZC8
    Return if done

    If we are here, we did not get a 00 end of program, so we continue

    1B02
    INC HL23
    We need HL to be the start of the text. Since HL is the ‘beginning of statment pointer’, we need to skip over the 3rd and 4th bytes, so bump the value of the memory pointer in Register Pair HL
    1B03
    INC HL23
    Bump the value of the memory pointer in Register Pair HL
    1B04
    INC HL23
    Bump the value of the memory pointer in Register Pair HL
    1B05
    XOR AAF
    Zero Register A and clear the flags
    1B06–  ↳ CZLOOP
    CP (HL)BE
    Top of a loop. Check to see if the character at the location of the memory pointer in Register HL is an end of the BASIC line character
    1B07
    INC HL23
    Bump the value of the memory pointer in Register Pair HL
    1B08-1B09
    JR NZ,1B06HJR NZ,CZLOOP20 FC
    Loop until the end of the BASIC line character is found
    1B0A
    EX DE,HLEB
    Exchange the starting address of the current BASIC line in Register Pair DE with the starting address of the next BASIC line in Register Pair HL
    1B0B
    LD (HL),E73
    Save the LSB of the next BASIC line’s starting address in Register E at the location of the memory pointer in Register Pair HL
    1B0C
    INC HL23
    Bump the value of the memory pointer in Register Pair HL
    1B0D
    LD (HL),D72
    Save the MSB of the next BASIC line’s starting address in Register D at the location of the memory pointer in Register Pair HL
    1B0E-1B0F
    JR 1AFCHJR CHEAD18 EC
    Loop until the end of the program has been found

    1B10-1B48 – EVALUATE LINE NUMBERS– “SCNLINE”

    This is called by LIST and DELETE. It converts the starting and ending linbers (X-Y) to binary and saves the ending line number on the STACK. Then the code locates the program table address for the starting line. The routine leaves the address of the starting line in BC and the ending line number in the STACK.

    According to the original ROM source, SCNLIN scans a line range of the form of #-# or #- or -# or blank, and then finds the first line in the range.

    1B10-1B12–  ↳ SCNLIN
    LD DE,0000H11 00 00
    Load Register Pair DE (which will be the list found value) with zero
    1B13
    PUSH DED5
    Save the initial assumption of start-of-list to the STACK
    1B14-1B15
    JR Z,1B1FHJR Z,ALLLST28 09
    If we are finished (i.e., there aren’t any line numbers to be evaluated), and the Z FLAG is therefore set, then jump to 1B1FH so list it all
    1B16
    POP DED1
    Get the line number from the STACK and put it in Register Pair DE
    1B17-1B19
    CALL 1E4FHCALL LINSPCCD 4F 1E
    Go evaluate the line number at the location of the current BASIC program pointer in Register Pair HL and return with the line number’s binary value in Register Pair DE
    1B1A
    PUSH DED5
    Save the first line number’s value (stored in Register Pair DE) to the STACK
    1B1B-1B1C
    JR Z,1B28HJR Z,SNGLIN28 0B
    Jump if there isn’t a second line number to be evaluated given (i.e, LIST 3000-)
    1B1D-1B1E
    RST 08H CEHCF CE
    If we are here, there is a second line number. That that would have to be preceded by a at the location of the current BASIC program pointer in Register Pair HL, 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)
    1B1F-1B21–  ↳ ALLLST
    LD DE,FFFAH11 FA FF
    Load Register Pair DE with the maximum end of range of FFAFH as the second line number, as FFAH is the last permitted line number. Note: FFFFH means that the BASIC instruction being interpreted was entered from the command line instead of being part of a program
    1B22-1B24
    CALL NZ,1E4FHCALL NZ,LINSPCC4 4F 1E
    Go evaluate the second line number at the location of the current BASIC program pointer in Register Pair HL and return with the line number’s binary value in Register Pair DE. Will return with Z flag set if it was a number
    1B25-1B27
    JP NZ,1997HJP NZ,SNERRC2 97 19
    Go to the Level II BASIC error routine and display a ?SN ERROR message if the data which followed the token wasn’t a line number
    1B28–  ↳ SNGLIN
    EX DE,HLEB
    Set HL to be the FINAL line number by Loading Register Pair HL with the value of the second line number in Register Pair DE and load Register Pair DE with the value of the current BASIC program pointer in Register Pair HL
    1B29
    POP DED1
    Get the value of the FIRST line number from the STACK and put it in Register Pair DE
    1B2A–  ↳ FNDLN1
    EX (SP),HLE3
    Exchange the return address to the STACK with the value of the second line number in Register Pair HL
    1B2B
    PUSH HLE5
    Save the value of the return address in Register Pair HL to the STACK so we can properly exit later

    1B2CH – SEARCH FOR A LINE NUMBER– “FNDLIN”

    According to the original ROM source, the FNDLIN routine searches the program text for the line whose line number is held in Register Pair DE. DE is preserved. There are three possible returns:

    • If there is no line in the program which is greater than the one sought, then Z/NC and HL=BC.
    • If the line which was searched for was actually found, Z and BC=the link field in the line and HL=the link field in the next line.
    • If the line was not found, but there is still more lines in the program, NZ/NC, BC=the line in the porgram greater than the one searched for, and HL = the link field in the next line.
    This routine searches a BASIC program for a BASIC line with a line number matching the value in the DE Register Pair. To use this routine, the required line number must be placed in the DE Register Pair. When a match is found, this routine sets the carry flag; the BC Register Pair points to the start of the required line, and the HL Register points to the start of the next line. HL, AF and BC are used.

    This is the the SEARCH FOR LINE NUMBER routine at 1B2C, which searches the Program Statement Table (PST) for a BASIC statement with the line number specified in the DE Register Pair. All registers are used. The exit conditions are:
    • C/Z=Line Found and BC is the starting address of the line in the PST and HL is the address following;
    • NC/Z=Line not found or too large and HL/BC will have the address of the next available PST location; and
    • NC/NZ=Line not found and BC=Address of the first line number greater than the one specified and HL will be the address of the following line.
    1B2C-1B2E–  ↳ FNDLIN
    LD HL,(40A4H)LD HL,(TXTTAB)2A A4 40
    Load Register Pair HL with the value of the start of the BASIC program pointer.
    NOTE: 40A4H-40A5H holds the starting address of BASIC program text also known as the PROGRAM STATEMENT TABLE (PST)
    1B2F,1B30–  ↳ LOOP
    LD B,H
    LD C,L44
    LET BC = HL so that BC will also hold the address of the current line in the PST
    1B31
    LD A,(HL)7E
    Load Register A with the first byte of the word pointer (held in Register Pair HL)
    1B32
    INC HL23
    Bump the value of the memory pointer in Register Pair HL to the MSB of the address of the next line
    1B33
    OR (HL)B6
    Combine the first byte of the word pointer with the second byte of the word pointer with the result in Register A and set the status flags
    1B34
    DEC HL2B
    Restore HL to the start of the current line
    1B35
    RET ZC8
    Return if this is the end of PST (=the end of the BASIC program)
    1B36
    INC HL23
    It’s not the end so we need HL to point the line number for the current line which means it must advance by 2. Bump the value of the memory pointer in Register Pair HL
    1B37
    INC HL23
    Bump the value of the memory pointer in Register Pair HL
    1B38
    LD A,(HL)7E
    Load Register A with the LSB of the current BASIC line number at the location of the memory pointer in Register Pair HL
    1B39
    INC HL23
    Bump the value of the memory pointer in Register Pair HL to point to the MSB of the current BASIC line number
    1B3A
    LD H,(HL)66
    Load Register H with the MSB of the current BASIC line number at the location of the memory pointer in Register Pair HL
    1B3B
    LD L,A6F
    Load Register L with the LSB of the current BASIC line number in Register A. HL should now hold the line number in the PST
    1B3C
    RST 18HCOMPARDF
    Now we need to compare the value of the current BASIC line number in HL and the first line number (in DE), so we call the COMPARE DE:HL routine, which numerically compares DE and HL. Will not work for signed integers (except positive ones). Uses the A-register only. The result of the comparison is returned in the status Register as: CARRY SET=HL<DE; NO CARRY=HL>DE; NZ=Unequal; Z=Equal)
    1B3D
    LD H,B60
    Next we need to set HL to the start of the line before the current one, and that is held in BC
    1B3E
    LD L,C69
    Load Register L with the LSB of the memory pointer in Register C
    1B3F
    LD A,(HL)7E
    Load Register A with the value at the location of the memory pointer in Register Pair HL
    1B40
    INC HL23
    Bump the value of the memory pointer in Register Pair HL
    1B41
    LD H,(HL)66
    Load Register H with the MSB of the next BASIC line pointer at the location of the memory pointer in Register Pair HL
    1B42
    LD L,A6F
    Load Register L with the LSB of the next BASIC line pointer in Register A to form the address of the next line in HL
    1B43
    CCF3F
    Complement the value of the Carry flag. The carry will be set if the current line number is < the value in DE. This will clear the Carry flag.
    1B44
    RET ZC8
    Return if the first line number in Register Pair DE is the same as the current BASIC line number. The line numbers match, so exit C, Z, BC=address of the current line, and HL=address of the next line
    1B45
    CCF3F
    We have no match, so complement (reverse) the Carry flag and exit
    1B46
    RET NCD0
    if the first line number in Register Pair DE is less than the current BASIC line number. BC will be the address of the current line and HL will be the address of the next line
    1B47-1B48
    JR 1B2FHJR LOOP18 E6
    Loop until the location of the line number has been found in the BASIC program

    1B49-1B5C – LEVEL II BASIC NEWROUTINE– “SCRATH”

    1B49–  ↳ SCRATH
    RET NZC0
    Go to the Level II BASIC error routine and display a ?SN ERRORmessage if there is any input following the NEWtoken
    1B4A-1B4C
    CALL 01C9HCALL CLSCD C9 01
    Call the CLEAR SCREEN routine at 01C9 (which cleanrts the screen, changes to 64 characters, and homes the screen)
    1B4D-1B4F–  ↳ SCRTCH
    LD HL,(40A4H)LD HL,(TXTTAB)2A A4 40
    Load Register Pair HL with the start of the PST (the start of the BASIC program).
    NOTE: 40A4H-40A5H holds the starting address of BASIC program text also known as the PROGRAM STATEMENT TABLE (PST)
    1B50-1B52
    CALL 1DF8HCALL TOFFCD F8 1D
    GOSUB to the TROFFroutine at 1DF8H (which just loads A with a zero for TROFF, and puts that 0 into 411BH)
    1B53-1B55
    LD (40E1H),ALD (AUTFLG),A32 E1 40
    Reset the AUTOflag by putting a zero (which is in A due to the GOSUB to the TROFFstatement in the above instruction) into 40E1H
    1B56
    LD (HL),A77
    We need to initialize the PST, and the way to do that is to zero the first two bytes so . save a zero at the location of the memory pointer in Register Pair HL
    1B57
    INC HL23
    . bump the value of the memory pointer in Register Pair HL
    1B58
    LD (HL),A77
    . and save a zero at the location of the memory pointer in Register Pair HL
    1B59
    INC HL23
    Bump the value of the memory pointer in Register Pair HL
    1B5A-1B5C
    LD (40F9H),HLLD (VARTAB),HL22 F9 40
    Save the value in Register Pair HL at 40F9H to initialize the start of the variable list table as the end of the PST.
    Note: 40F9H-40FAH holds the starting address of the simple variable storage area

    1B5D-1BB2 – LEVEL II BASIC RUNROUTINE – “RUNC”

    This routine does a lot of variable resets and other things that are common to NEWas well, so NEWjust does the special NEWstuff and than passes right through to here to reset the rest.

    To use a ROM call to RUN a BASIC program, starting with its first line, execute the following instructions:
       LD HL,1D1EH
       PUSH HL
       JP 1B5DH.

    1B5D
    GOSUB to 046BH to unprotect the screen and point HL to the start of data.
    1B60
    DEC HL2B
    Decrement the value in Register Pair HL to backspace

    1B61H – Subroutine which initializes a lot of stuff– “CLEARC”

    Initialize he variable and array space by resetting ARYTAB (which is the end of the the simple variable spac) and STREND (which is the end of the array storage). It then falls into STKINI which resets the STACK. HL is preserved.

    1B61-1B63–  ↳ CLEARC
    LD (40DFH),HLLD (TEMP),HL22 DF 40
    Save the adjusted value in Register Pair HL into 40DFH.
    Note: 40DFH-40E0H is used by DOS
    1B64-1B65
    LD B,1AH06 1A
    Load Register B with the number of variable names to be initialized (which is 26)
    1B66-1B68
    LD HL,4101HLD HL,DEFTBL21 01 41
    Load Register Pair HL with the starting address of the variable declaration table (which is 4101H).
    Note: 4101H-411AH holds Variable Declaration Table
    1B69-1B6A–  ↳ LOPDFT
    LD (HL),04H36 04
    Top of a DJNZ loop. Set the variable at the location of the memory pointer in Register Pair HL to a single precision variable
    1B6B
    INC HL23
    Bump the value of the memory pointer in Register Pair HL to the next variable
    1B6C-1B6D
    DJNZ 1B69HDJNZ LOPDFT10 FB
    Loop until all 26 variables have been set to single precision
    1B6E
    XOR AAF
    Zero Register A
    1B6F-1B71
    LD (40F2H),ALD (ONEFLG),A32 F2 40
    Save the value in Register A (which is a zero) as the current value of the RESUMEflag to say that there is no error for RESUMEto handle.
    Note: 40F2H holds Error flag
    1B72
    LD L,A6F
    Zero Register L
    1B73
    LD H,A67
    Zero Register H
    1B74-1B76
    LD (40F0H),HLLD (ONELIN),HL22 F0 40
    Save a zero (held in Register Pair HL) as the current ON ERRORaddress.
    Note: 40F0H-40F1H is used by ON ERROR
    1B77-1B79
    LD (40F7H),HLLD (OLDTXT),HL22 F7 40
    Save a zero (held in Register Pair HL) as the current BREAK, STOP, or ENDaddress.
    Note: 40F7H-40F8H holds Last byte executed
    1B7A-1B7C
    LD HL,(40B1H)LD HL,(MEMSIZ)2A B1 40
    Load Register Pair HL with the top of the memory pointer held in 40B1H.
    Note: 40B1H-40B2H holds MEMORY SIZE? pointer
    1B7D-1B7F
    LD (40D6H),HLLD (FRETOP),HL22 D6 40
    Save the top of memory pointer (held in Register Pair HL) as the next available address in the string space pointer.
    Note: 40D6H-40D7H holds the next available location in string space pointer
    1B80-1B82
    CALL 1D91HCALL RESTORECD 91 1D
    Go do a RESTORE(which will mess with DE and HL)
    1B83-1B85
    LD HL,(40F9H)LD HL,(VARTAB)2A F9 40
    Load Register Pair HL with the end of the BASIC program pointer.
    Note: 40F9H-40FAH holds the starting address of the simple variable storage area
    1B86-1B88
    LD (40FBH),HLLD (ARYTAB),HL22 FB 40
    Save the value in Register Pair HL as the new simple variables pointer. 40FBH-40FCH holds the starting address of the BASIC array variable storage area
    1B89-1B8B
    LD (40FDH),HLLD (STREND),HL22 FD 40
    Save the value in Register Pair HL as the new array variables pointer.
    Note: 40FDH-40FEH holds Free memory pointer
    1B80-1B82
    CALL 1D91HCALL RESTORECD 91 1D
    Go do a RESTORE(which will mess with DE and HL)

    1B8F – Subroutine which initializes a lot of stuff– “STKINI”

    According to the notes in the original ROM source code, this routine resets the STACK point, which will also destroy all GOSUBs and FORs. String temporaries are freed, SUBFLG is reset, CONT is forbidden, and a dummy entry is put on the STACK, so that FNDFOR will always find a NON-“FOR” entry at the bottom of the STACK. A will be reset to 0 and Register Pair DE is preserved.

    1B8F–  ↳ STKINI
    POP BCC1
    Get the return address from the STACK because we are about to change the STACK pointer
    1B90-1B92
    LD HL,(40A0H)LD HL,(STKTOP)2A A0 40
    Load Register Pair HL with the start of string space pointer (which is also the end of RAM). 40A0H-40A1H holds the start of string space pointer
    1B93
    DEC HL2B
    Decrement the value of the memory pointer in Register Pair HL
    1B94
    DEC HL2B
    Decrement the value of the memory pointer in Register Pair HL (so now HL has the start of the string space pointer – 2) so that there is now room for a FNDFOR stopper value to be put on the STACK
    1B95-1B97
    LD (40E8H),HLLD (SAVSTK),HL22 E8 40
    Save the string space pointer – 2 (in Register Pair HL) as the STACK pointer
    Note: 40E8H-40E9H holds STACK pointer pointer
    1B98
    INC HL23
    Bump the value of the memory pointer in Register Pair HL
    1B99
    INC HL23
    Bump the value of the memory pointer in Register Pair HL so it is back to being the start of the string space pointer
    1B9A–  ↳ STKERR
    LD SP,HLF9
    Initialize the STACK by loading the STACK pointer with the start of the string space pointer (held in Register Pair HL)
    1B9B-1B9D
    LD HL,40B5HLD HL,TEMPST21 B5 40
    Load Register Pair HL with the start of the string work area (which is 40B5H).
    Note: 40B5H-40D2H holds Temporary string work area
    1B9E-1BA0
    LD (40B3H),HLLD (TEMPPT),HL22 B3 40
    Initialize the string temporaries by saving the value in Register Pair HL as the next available location in the string work area pointer.
    Note: 40B3H-40B4H holds the next available location in the temporary string work area pointer
    1BA1-1BA3
    CALL 038BHCALL FINLPTCD 8B 03
    GOSUB 038BH to set the current output device to the video display
    1BA4-1BA6
    CALL 2169HCALL FINPRTCD 69 21
    Go turn off the cassette recorder
    1BA7
    XOR AAF
    Zero Register A
    1BA8
    LD H,A67
    Zero Register H
    1BA9
    LD L,A6F
    Zero Register L
    1BAA-1BAC–  ↳ WASDIR
    LD (40DCH),ALD (SUBFLG),A32 DC 40
    Clear the FORstatement flag.
    Note: 40DCH holds FOR flag
    1BAD
    PUSH HLE5
    Save the value in Register Pair HL to the STACK to deal with FOR and GOSUB
    1BAE
    PUSH BCC5
    Save the value in Register Pair BC (which is the RETURN ADDRESS) back on the STACK
    1BAF-1BB1–  ↳ GTMPRT
    LD HL,(40DFH)LD HL,(TEMP)2A DF 40
    Restore Register Pair HL so it is preserved
    1BB2
    RETC9
    RETurn to CALLer

    1BB3-1BBF – KEYBOARD INPUT ROUTINE– “QINLIN”

    This is the last of the general purpose input routines. This routine functions identically to the 0361H routine with the exception that it prints a ?on the screen (like INPUT does with BASIC) before allowing input from the keyboard.

    1BB3-1BB4–  ↳ QINLIN
    LD A,3FHLD A,”?”3E 3F
    Load Register A with a ?
    1BB5-1BB7
    CALL 032AHCALL OUTDOCD 2A 03
    Go display the ?(stored in Register A) on the video display
    1BB8-1BB9
    LD A,20H3E 20
    Load Register A with a SPACE
    1BBA-1BBC
    CALL 032AHCALL OUTDOCD 2A 03
    Go display the space in Register A on the video display
    1BBD-1BBF
    JP 0361HJP INLINC3 61 03
    Jump to the keyboard input routine at 0361H. Note that this skips the “CRUNCHING” of tokens

    1BC0-1C8F – TOKENIZE INPUT ROUTINE– “CRUNCH”

    The original ROM source code says that this routine translates all “reserved words” into single bytes with the MSB on. This saves space and time by allowing for table dispatch during execution, and, as such, all statements appear together in the ; reserved word list in the same ; order they appear in in STMDSP.

    1BC0–  ↳ CRUNCH
    XOR AAF
    Zero Register A

    *1BC1-1BC3 – Model 4 Gen 1

    *1BC1-1BC3
    LD (40B0H),ALD (DORES),A32 B0 40
    Save the value in Register A as the current value of the tokenization flag.
    Note: 40B0H holds the temporary storage location

    *1BC1-1BC3 – Model 4 Gen 2

    *1BC1-1BC3
    LD (409FH),A32 9F 40
    Put A into the DATA FLAG held in 409FH. Note: Bit 0 HIGH means inside a quote. Bit 1 HIGH means inside a DATA. Bit 2 HIGH means inside a REM.
    1BC4
    LD C,A4F
    Zero Register C
    1BC5
    EX DE,HLEB
    Load Register Pair DE with the address of the first character after the line number (as stored in Register Pair HL)
    1BC6-1BC8
    LD HL,(40A7H)LD HL,(BUFPNT)2A A7 40
    Load Register Pair HL with the starting address of the input buffer.
    Note: 40A7H-40A8H holds Input Buffer pointer
    1BC9
    DEC HL2B
    We need to backspace twice so … decrement the value of the input buffer pointer in Register Pair HL
    1BCA
    DEC HL2B
    … and again decrement the value of the input buffer pointer in Register Pair HL
    1BCB
    EX DE,HLEB
    Exchange the string address – 2 from HL into DE, and the current input string address from DE into HL
    1BCC–  ↳ KLOOP
    LD A,(HL)7E
    Load Register A with the character at the current location in the buffer (in Register Pair HL)
    1BCD-1BCE
    CP 20HFE 20
    Check to see if that character in Register A is a SPACEthat we would want to keep
    1BCF-1BD1
    JP Z,1C5BHJP Z,STUFFHCA 5B 1C
    If that character is a SPACEwe would want to keep, Jump to 1C5BH to “STUFF” it into the destination line
    1BD2
    LD B,A47
    Copy the current character into Register B
    1BD3-1BD4
    CP 22HFE 22
    Check to see if the current character is a
    1BD5-1BD7
    JP Z,1C77HJP Z,STRNGCA 77 1C
    If the current character is a , Jump to 1C77H to will move the entire field between the quotes into the a code string
    1BD8
    OR AB7
    Now we want to check to see if the current character is an END OF LINE, so we need to set up the status flags
    1BD9-1BDB
    JP Z,1C7DHJP Z,CRDONECA 7D 1C
    If the current character is an END OF LINE then we are done, so jump to 1C7DH

    *1BDC-1BDE – Model 4 Gen 1

    *1BDC-1BDE
    LD A,(40B0H)LD A,(DORES)3A B0 40
    Load Register A with the value of the tokenization flag for DATA.
    Note: 40B0H holds the temporary storage location

    *1BDC-1BDE – Model 4 Gen 2

    *1BDC-1BDE
    LD A,(409FH)3A 9F 40
    Put the contents of the DATA FLAG held in 409FH into A. Note: Bit 0 HIGH means inside a quote. Bit 1 HIGH means inside a DATA. Bit 2 HIGH means inside a REM.
    1BDF
    OR AB7
    Check to see if a DATA statement is being processed
    1BE0
    LD A,(HL)7E
    Re-fetch the current character (from the memory location of the input buffer pointer in Register Pair HL) and put it into Register A
    1BE1-1BE3
    JP NZ,1C5BHJP NZ,STUFFHC2 5B 1C
    Jump to 1C5BH if a DATAstatement is being processed, as we don’t want to crunch that
    1BE4-1BE5
    CP 3FHFE 3F
    Check to see if the character in Register A is a ?(meaning a PRINTstatement)
    1BE6-1BE7
    LD A,B2HLD A,$PRINT3E B2
    Load Register A with a PRINTtoken
    1BE8-1BEA
    JP Z,1C5BHJP Z,STUFFHCA 5B 1C
    If we have a ?then make believe it is a “PRINT” token, and jump to 1C5BH to STUFF it into the destination line
    1BEB
    LD A,(HL)7E
    Re-fetch the current character (from the memory location of the input buffer pointer in Register Pair HL) and put it into Register A
    1BEC-1BED
    CP 30HFE 30
    Since the crunching routine is slow, these next instructions look for characters that will not need crunching. First, check to see if the character in Register A is less than a zero character (alpha numeric)
    1BEE-1BEF
    JR C,1BF5HJR C,MUSTCR38 05
    Jump to 1BF5H if the character in Register A is less than a zero character, meaning it is not a digit or letter
    1BF0-1BF1
    CP 3CHFE 3C
    Next, check to see if the character in Register A is less than < character (which is a test to see if it is 09, :, ;, <, constant or special character
    1BF2-1BF4
    JP C,1C5BHJP C,STUFFHDA 5B 1C
    Jump to 1C5BH if the character in Register A is 09, :, ;, <, constant or special character
    1BF5–  ↳ MUSTCR
    PUSH DED5
    Save the value of the input buffer pointer in Register Pair DE to the STACK
    1BF6-1BF8
    LD DE,164FHLD D,RESLST-111 4F 16
    Load Register Pair DE with the starting address of the reserved words list, minus 1 (since the first instruction in the LOPSKP routine is to INC the value)
    1BF9
    PUSH BCC5
    Save the character count (held in Register Pair BC) to the STACK
    1BFA-1BFC
    LD BC,1C3DHLD BC,NOTRES01 3D 1C
    Load Register Pair BC with a return address after matching the reserved word list
    1BFD
    PUSH BCC5
    Save the return address in Register Pair BC to the STACK
    1BFE-1BFF
    LD B,7FH06 7F
    Load Register B with a value to initialize the reserved words counter
    1C00
    LD A,(HL)7E
    Load Register A with the character at the location of the input buffer pointer in Register Pair HL
    1C01-1C02
    CP 61HCP “A”+20HFE 61
    Check to see if the character in Register A is lower-case
    1C03-1C04
    JR C,1C0CHJR C,TRYAGA38 07
    Jump to 1C0CH if the character in Register A isn’t lowercase
    1C05-1C06
    CP 7BHCP “Z”+21HFE 7B
    Check to see if the character in Register A is within the lower-case range
    1C07-1C08
    JR NC,1C0CHJR NC,TRYAGA30 03
    Jump down 2 instructions to 1C0CH if the character in Register A isn’t lowercase
    1C09-1C0A
    AND 5FHAND 0101 1111E6 5F
    Covert the lowercase character in Register A to upper-case
    1C0B
    LD (HL),A77
    Replace the current character in the input buffer (tracked by Register Pair HL) with the adjusted character (held in Register A)
    1C0C–  ↳ TRYAGA
    LD C,(HL)4E
    Load Register C with the character at the location of the input buffer pointer in Register Pair HL
    1C0D
    EX DE,HLEB
    Exchange DE and HL so that DE will now hold the input buffer pointer (TXTPTR) and HL will hold the reserved words list pointer (“RESLST”)
    1C0E–  ↳ LOPSKP
    INC HL23
    Bump the value of the reserved words list pointer to the next reserved word and put that in Register Pair HL
    1C0F
    OR (HL)B6
    We need to find the start of a reserved word which is done by searching the list for a character with its MSB bit on. So first, check to see if bit 7 of the character at the location of the reserved words list pointer in Register Pair HL is set
    1C10-1C12
    JP P,1C0EHJP P,LOPSKPF2 0E 1C
    Jump to 1C0EH if the character at the location of the reserved words list pointer in Register Pair HL doesn’t have bit 7 set

    If we are here, the the character in the reserved word list had its MSB on, and is a reserved word.

    1C13
    INC B04
    Bump the reserved words counter being tracked in Register B
    1C14
    LD A,(HL)7E
    Load Register A with the character at the location of the reserved words list pointer in Register Pair HL
    1C15-1C16
    AND 7FHAND 0111 1111E6 7F
    Reset the MSB (i.e., Bit 7 aka the Sign Bit) of the character in Register A
    1C17
    RET ZC8
    If that set the Z FLAG then we are at the end of ther reserved word list, so, if so RETurn to CALLer
    1C18
    CP CB9
    Check to see if the character in Register C is the same as the character at the location of the reserved words list pointer in Register A
    1C19-1C1A
    JR NZ,1C0EHJR NZ,LOPSKP20 F3
    Jump to 1C0EH if the characters don’t match
    1C1B
    EX DE,HLEB
    Exchange the value of the reserved words list pointer (“RESLST”) in Register Pair HL with the value of the input buffer pointer (“TXTPTR”) in Register Pair DE
    1C1C
    PUSH HLE5
    Save the value of the input buffer pointer to the STACK in case it turns out that this isn’t a match after all
    1C1D–  ↳ LOPPSI
    INC DE13
    Top of a loop. Bump the value of the reserved words list pointer (held in Register Pair DE) to the next character of the reserved word being checked
    1C1E
    LD A,(DE)1A
    Load Register A with the character at the location of the reserved words list pointer in Register Pair DE
    1C1F
    OR AB7
    Check to see if Bit 7 of the character in Register A is set, meaning we just hit a different reserved word
    1C20-1C22
    JP M,1C39HJP M,FOUNDFA 39 1C
    If Bit 7 of that character was set, and we are at a new reserved word, then we have finished matching, so jump to 1C39H

    If we are here then we are in the middle of checking against the reserved word list, and we are still in the middle of a reserved word that might be a potential match.

    1C23
    LD C,A4F
    Load Register C with the current reserved word list character (held in Register A)
    1C24
    LD A,B78
    Load Register A with the value of the reserved words counter in Register B

    The GOTOreserved word is the only one which allows for spaces to be inside it, so . if we find that we have GOso far, we will call the RST 10H to strip out any intevening spaces before continuing.

    1C25-1C26
    CP 8DHCP $GOTOFE 8D
    Check to see if the current reserved word being checked is GOTO
    1C27-1C28
    JR NZ,1C2BHJR NZ,NTGOTO20 02
    Skip the next 2 instructions if the current reserved word being checked isn’t GOTO
    1C29
    RST 10HCHRGETD7
    Since we need to skip spaces 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.
    1C2A
    DEC HL2B
    Decrement the value of the input buffer pointer in Register Pair HL because the next instruction increments it
    1C2B–  ↳ NTGOTO
    INC HL23
    Bump the value of the input buffer pointer in Register Pair HL to point to the next character
    1C2C
    LD A,(HL)7E
    Load Register A with the next character at the location of the input buffer pointer in Register Pair HL
    1C2D-1C2E
    CP 61HCP “A”+20HFE 61
    Check to see if the character in Register A is lower-case
    1C2F-1C30
    JR C,1C33HJR C,NOTLW138 02
    If the character in Register A isn’t lowercase, skip the next instruction
    1C31-1C32
    AND 5FHAND 0101 1111E6 5F
    AND the character in Register A against 0101 1111 to get rid of the lowercase bit and make it uppercase
    1C33–  ↳ NOTLW1
    CP CB9
    Check to see if the character in Register A (the input element) matches the character in Register C (the next letter in the reserved word list)
    1C34-1C35
    JR Z,1C1DHJR Z,LOPPSI28 E7
    If that character is ALSO a match, loop back to 1C1DH to keep checking that reserved word
    1C36
    POP HLE1
    If we are here, then the matching process failed, so we need to get back the original input buffer pointer into HL and move on to the next reserved word
    1C37-1C38
    JR 1C0CHJR TRYAGA18 D3
    Jump back to 1C0CH to process the next reserved word against the character at the input buffer
    1C39–  ↳ FOUND
    LD C,B48
    Load Register C with the value of the reserved words counter in Register B
    1C3A
    POP AFF1
    Clear the old TXTPTR text pointer from the STACK (disposing of the HL push from 1C1CH)
    1C3B
    EX DE,HLEB
    Exchange the TXTPTR and RESPTR because the NOTRES routine starts with the same instruction flipping them back
    1C3C
    RETC9
    RETurn to CALLer (which, by the way, will just pass through, the the RET was reprogrammed to be the next instruction)

    1C3D – Part of the tokeninzing routine– “NOTRES”

    1C3D–  ↳ NOTRES
    EX DE,HLEB
    Exchange the reserved words list pointer in Register Pair HL with the input buffer pointer in Register Pair DE
    1C3E
    LD A,C79
    Load Register A with the value of the reserved words counter in Register C
    1C3F
    POP BCC1
    Restore the character count into Register Pair BC
    1C40
    POP DED1
    Restore the “STUFF” pointer into Register Pair DE
    1C41
    EX DE,HLEB
    Exchange DE and HL so that DE will now hold the input buffer pointer (TXTPTR) and HL will hold the “STUFF” pointer

    The ELSEtoken needs to be treated differently as it is a reserved word which is followed by a reserved word. To deal with this, we put in a fake colon!

    1C42-1C43
    CP 95HFE 95
    Check to see if the token in Register A is an ELSEtoken
    1C44-1C45
    LD (HL),3AHLD (HL),”:”36 3A
    Save a colon at the “STUFF” pointer location (held in Register Pair HL)
    1C46-1C47
    JR NZ,1C4AHJR NZ,CKSNGQ20 02
    If the token in Register A isn’t an ELSEtoken, skip the next two instructions
    1C48
    INC C0C
    Bump the value of the crunched character counter
    1C49
    INC HL23
    Bump the value of the “STUFF” pointer (held in Register Pair HL)

    The token needs to be treated differently as it doesn’t require a colon. To deal with this, we put in a fake colon!

    1C4A-1C4B–  ↳ CKSNGQ
    CP 0FBHCP SNGQTKFE FB
    Next we check to see if the token in Register A is a token
    1C4C-1C4D
    JR NZ,1C5AHJR NZ,NTSNGT20 0C
    Jump to 1C5AH if the token in Register A isn’t a
    1C4E-1C4F
    LD (HL),3AHLD (HL),”:”36 3A
    Save a “:” at the “STUFF” pointer (held in Register Pair HL)
    1C50
    INC HL23
    Bump the value of the “STUFF” pointer (held in Register Pair HL) because we just inserted a :
    1C51-1C52
    LD B,93HLD B,$REM06 93
    Load Register B with a REMtoken
    1C53
    LD (HL),B70
    Save the REMtoken in Register B at the location of the input buffer pointer in Register Pair HL
    1C54
    INC HL23
    Bump the value of the “STUFF” pointer (held in Register Pair HL) because we just inserted a REMtoken
    1C55
    EX DE,HLEB
    Exchange DE and HL so that DE will now hold the “STUFF” pointer and HL will hold the input buffer pointer
    1C56
    INC C0C
    Bump the character counter in Register C
    1C57
    INC C0C
    Bump the character counter in Register C
    1C58-1C59
    JR 1C77HJR STRNG18 1D
    Jump to 1C77H to move all the rest of the text of the line to the “STUFF” buffer

    1C5A – Part of the tokeninzing routine– “NTSNGT” and “STUFFH”

    1C5A–  ↳ NTSNGT
    EX DE,HLEB
    Exchange DE and HL so that DE will now hold the “STUFF” pointer
    1C5B–  ↳ STUFFH
    INC HL23
    Bump the value of the input buffer pointer in Register Pair HL
    1C5C
    LD (DE),A12
    Save the value of the token in Register A at the location of the input buffer pointer in Register Pair DE
    1C5D
    INC DE13
    Bump the value of the “STUFF” pointer (held in Register Pair DE)
    1C5E
    INC C0C
    Bump the value of the crunched character count
    1C5F-1C60
    SUB 3AHD6 3A
    Check to see if the character in Register A is a :to flag a multi-statement line
    1C61-1C62
    JR Z,1C67HJR Z,COLIS28 04
    If we found a “:” then skip the next two instructions
    1C63-1C64
    CP 4EHCP $DATAFE 4E
    Check to see if the token in Register A is a DATAtoken
    1C65-1C66
    JR NZ,1C6AHJR NZ,NODATT20 03
    If the token in Register A isn’t a DATAtoken then skip the next instruction

    *1C67-1C69 – Model 4 Gen 1

    *1C67-1C69–  ↳ COLIS
    LD (40B0H),ALD (DORES),A32 B0 40
    Save the value in Register A as the tokenization flag for DATA.
    Note: 40B0H holds the temporary storage location

    *1C67-1C69 – Model 4 Gen 2

    *1C67-1C69
    LD (409FH),A32 9F 40
    Put A into the DATA FLAG held in 409FH. Note: Bit 0 HIGH means inside a quote. Bit 1 HIGH means inside a DATA. Bit 2 HIGH means inside a REM.
    1C6A-1C6B–  ↳ NODATT
    SUB 59HD6 59
    Check to see if the token in Register A is a REMtoken
    1C6C-1C6E
    JP NZ,1BCCHJP NZ,KLOOPC2 CC 1B
    Jump to 1BCCH if the token in Register A isn’t a REMtoken
    1C6F
    LD B,A47
    Load Register B with a zero, since processing a REM doesn’t end with a :, only an END OF LINE (i.e., 0)
    1C70–  ↳ STR1
    LD A,(HL)7E
    Top of a loop. Load Register A with the character at the location of the input buffer pointer in Register Pair HL
    1C71
    OR AB7
    Check to see if the character in Register A is an END OF LINE delimiter of 0
    1C72-1C73
    JR Z,1C7DHJR Z,CRDONE28 09
    Jump out of this loop to 1C7DH if the character in Register A is an END OF LINE delimeter
    1C74
    CP BB8
    Check to see if the character in Register B matches the character in Register A
    1C75-1C76
    JR Z,1C5BHJR Z,STUFFH28 E4
    If they match, then we are completely done with gobbling the whole rest of line without checking, so Jump to 1C5BH
    1C77–  ↳ STRNG
    INC HL23
    Bump the value of the input buffer pointer in Register Pair HL
    1C78
    LD (DE),A12
    Save the character in Register A to the location of the “STUFF” pointer (in Register Pair DE)
    1C79
    INC C0C
    Bump the value of the crunched character count
    1C7A
    INC DE13
    Bump the value of the “STUFF” pointer (held in Register Pair DE)
    1C7B-1C7C
    JR 1C70HJR STR118 F3
    Loop until an END OF LINE delimiter or a ending quote is found

    1C7D – Part of the tokeninzing routine – Jumped here when an EOL is found– “CRDONE”

    1C7D-1C7F–  ↳ CRDONE
    LD HL,0005H21 05 00
    We are going to need to add 5 bytes to the tokenized character count
    1C80
    LD B,H44
    Load Register B with zero
    1C81
    ADD HL,BC09
    Add 5 to the length of the tokenized character count so far
    1C82,1C83
    LD B,H
    LD C,L44
    Let BC = HL
    1C84-1C86
    LD HL,(40A7H)LD HL,(BUFPNT)2A A7 40
    Load Register Pair HL with the start address of the input buffer.
    Note: 40A7H-40A8H holds Input Buffer pointer
    1C87
    DEC HL2B
    Decrement the value of the input buffer pointer in Register Pair HL
    1C88
    DEC HL2B
    Decrement the value of the input buffer pointer in Register Pair HL
    1C89
    DEC HL2B
    Decrement the value of the input buffer pointer in Register Pair HL
    1C8A
    LD (DE),A12
    We need THREE zeroes at the end, so . zero the location of the input buffer pointer in Register Pair DE to denote END OF LINE
    1C8B
    INC DE13
    Bump the value of the input buffer pointer in Register Pair DE
    1C8C
    LD (DE),A12
    Zero the next two locations of the input buffer pointer in Register Pair DE to denote no further link (since this was a direct statement)
    1C8D
    INC DE13
    Bump the value of the input buffer pointer in Register Pair DE
    1C8E
    LD (DE),A12
    Zero the location of the input buffer pointer in Register Pair DE
    1C8F
    RETC9
    RETURN t o CALLer since we are done with the CRUNCHing

    1C90-1C95 – RST 0018H CODE– “DCOMPR”

    The RST 18H code is located here. Unsigned compare (HL-DE), which numerically compares DE and HL. Will not work for signed integers (except positive ones). Uses the A-register only. The result of the comparison is returned in the status Register as: CARRY SET=HL<DE; NO CARRY=HL>DE; NZ=Unequal; Z=Equal).

    1C90–  ↳ DCOMPR
    LD A,H7C
    Load Register A with the MSB of the value in Register H
    1C91
    SUB D92
    Subtract the value of the MSB of the value in Register D from the MSB of the value in Register A
    1C92
    RET NZC0
    Return if the MSB of the value in Register D doesn’t equal the MSB of the value in Register H
    1C93
    LD A,L7D
    Load Register A with the LSB of the value in Register L
    1C94
    SUB E93
    Subtract the LSB of the value in Register E from the LSB of the value in Register A
    1C95
    RETC9
    RETurn to CALLer

    1C96-1CA0 – RST 0008H CODE– “SYNCHR”

    The RST 8H code is located here. This is the COMPARE SYMBOL routine which comparess the symbol in the input string pointed to by HL Register to the value in the location following the RST 08 call. If there is a match, control is returned to address of the RST 08 instruction 2 with the next symbol in in Register A and HL incremented by one. If the two characters do not match, a syntax error message is given and control returns to the Input Phase).

    1C96–  ↳ SYNCHR
    LD A,(HL)7E
    Load Register A with the character at the location of the current BASIC program pointer in Register Pair HL
    1C97
    EX (SP),HLE3
    Save the return address of the routine by exchanging the value of the current BASIC program pointer in Register Pair HL with the value of the return address to the STACK
    1C98
    CP (HL)BE
    Check to see if the character at the location following the RST 08H call (stored in Register Pair HL) is the same as the character in Register A
    1C99
    INC HL23
    Bump the value of the return address in Register Pair HL
    1C9A
    EX (SP),HLE3
    Restore the return address (meaning the RST 08H plus 1 byte plus 1 byte) to the STACK pointer
    1C9B-1C9D
    JP Z,1D78HJP CHRGTRCA 78 1D
    Jump to the RST 0010H code if the characters match
    1C9E-1C90
    JP 1997HJP SNERRC3 97 19
    If they don’t match, jump to the ?SN ERRORroutine

    1CA1-1D1D – Level II BASIC FORROUTINE– “FOR”

    According to the comments to the actual ROM source code, the FOR entry in the STACK has 16 bytes, as follows:

    • 1 Byte – The FOR token
    • 2 Bytes – A pointer to the loop’s variable
    • 1 Byte – A byte reflecting the sign of the increment
    • 4 Bytes – The value of the STEP
    • 4 Bytes – The upper limit of the loop
    • 2 Bytes – The line number of the FOR statement
    • 2 Bytes – A text pointo into the FOR statement

    Vernon Hester has flagged a bug in the FOR…NEXT routines. FOR-NEXT loops with valid integer values should complete, but FOR J% = 0 TO 30000 STEP 5000 : PRINT J%, : NEXT J%will trigger an ?OV ERROR.

    1CA1-1CA2–  ↳ FOR
    LD A,64H3E 64
    Load Register A with the value for the FORflag
    1CA3-1CA5
    LD (40DCH),ALD (SUBFLG),A32 DC 40
    Save the value in Register A as the current value of the FOR flag.
    Note: 40DCH holds FORflag
    1CA6-1CA8
    CALL 1F21HCALL LETCD 21 1F
    GOSUB to the “LET” routine at 1F21H to read the variable, assign it the correct initial value, and store a pointer to the RAM variable location “TEMP”
    1CA9
    EX (SP),HLE3
    Exchange the value of the current BASIC program pointer in Register Pair HL with the value to the STACK
    1CAA-1CAC
    CALL 1936HCALL FNDFORCD 36 19
    Go check to see if there is a FORstatement to the STACK already using the same variable name (called the index)
    1CAD
    POP DED1
    Get the current BASIC program pointer from the STACK (which should be the TOtoken and put it in Register Pair DE
    1CAE-1CAF
    JR NZ,1CB5HJR NZ,NOTOL20 05
    If there isn’t a matching FORstatement to the STACK, skip the next 3 instructions (which are preparing for a NEXT WITHOUT FOR error) and jump to 1CB5H. If one is found, on exit HL will equal the starting address of the FORpush
    1CB0
    ADD HL,BC09
    Add the value in Register Pair BC (which is the offset to the end of the STACK frame) to the value in Register Pair HL. In the case where we had a matching FOR, we elimiate the matchin entry as well as evertying after it by doing this addition. After this addition, we should be pointing to the end of the first FORframe push
    1CB1
    LD SP,HLF9
    Remove all the rest by resetting the STACK pointer to end of the first FORframe push. This also frees up the STACK space and prepares for a NF error
    1CB2-1CB4
    LD (40E8H),HLLD (SAVSTK),HL22 E8 40
    Save the value in Register Pair HL as the STACK pointer, get ready for a NF error.
    Note: 40E8H-40E9H holds STACK pointer pointer
    1CB5–  ↳ NOTOL
    EX DE,HLEB
    Exchange Register Pairs DE and HL, so that HL now points to the current BASIC program pointer and DE will be the STACK pointer address
    1CB6-1CB7
    LD C,08H0E 08
    Load Register C with the 1/2 the amount of space (i.e. 16 bytes) needed for a FOR entry
    1CB8-1CBA
    CALL 1963HCALL GETSTKCD 63 19
    Go check to see if there is enough memory (i.e., 16 bytes) left by calling 1963H (which is the MEMORY CHECK ROUTINE)
    1CBB
    PUSH HLE5
    Save the value of the current BASIC program pointer (which is the code string address before the TO) in Register Pair HL to the STACK
    1CBC-1CBE
    CALL 1F05HCALL DATACD 05 1F
    Keep scanning the current BASIC program (pointer is in Register Pair HL) until it points to the end of the BASIC statement. HL should then point to the END OF LINE terminator
    1CBF
    EX (SP),HLE3
    Exchange the adjusted value of the current BASIC program pointer in Register Pair HL with the value of the current BASIC program pointer to the STACK
    1CC0
    PUSH HLE5
    Save the value of the current BASIC program pointer in Register Pair HL to the STACK. This should be pointing to the TOtoken
    1CC1-1CC3
    LD HL,(40A2H)LD HL,(CURLIN)2A A2 40
    Load Register Pair HL with the value (in binary) of the current BASIC line number.
    Note: 40A2H-40A3H holds the current BASIC line number
    1CC4
    EX (SP),HLE3
    Exchange the value of the current BASIC line number in Register Pair HL with the value of the current BASIC program pointer to the STACK. Once again, HL will point to the current location in the BASIC program
    1CC5-1CC6
    RST 08H BDHRST 08H TOCF
    Since the character at the location of the current BASIC program pointer in Register Pair HL must be TO token (BDH) so 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)
    1CC7
    RST 20HGETYPEE7
    We need to check the value of the current number type flag, so we call the TEST DATA MODE routine at RST 20H.
    NOTE:The RST 20H routine determines the type of the current value in ACCumulator and returns a combination of STATUS flags and unique numeric values in the A Register according to the data mode flag (40AFH). The results are returned as follows:
    • 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.
    1CC8-1CCA
    JP Z,0AF6HJP Z,TMERRCA F6 0A
    If that test shows we have a STRING, go to the Level II BASIC error routine and display a TM ERROR message
    1CCB-1CCD
    JP NC,0AF6HJP NC,TMERRD2 F6 0A
    If that test shows we have a DOUBLE PRECISION number, go display a ?TM ERRORmessage
    1CCE
    PUSH AFF5
    We have an integer, so let’s keep going and save the resulting integer value in Register Pair AF (the type flags) to the STACK
    1CCF-1CD1
    CALL 2337HCALL FRMEVLCD 37 23
    Go evaluate the expression at the location of the current BASIC program pointer in Register Pair HL (which should be the TOside) and return with the result in ACCumulator
    1CD2
    POP AFF1
    Restore the index type flags from the from the STACK and put it in Register Pair AF
    1CD3
    PUSH HLE5
    Save the value of the current BASIC program pointer (which is the code string after the TOpointer) in Register Pair HL to the STACK
    1CD4-1CD6
    JP P,1CECHJP P,SNGFORF2 EC 1C
    If the flag is positive, then we have a SINGLE PRECISION “FOR” loop
    1CD7-1CD9
    CALL 0A7FHCALL FRCINTCD 7F 0A
    Call the CONVERT TO INTEGER routine at 0A7FH (where the contents of ACCumulator are converted from single or double precision to integer and deposited into HL)
    1CDA
    EX (SP),HLE3
    Exchange the integer value in Register Pair HL from that conversion (the current TOvalue) with the value of the current BASIC program pointer to the STACK
    1CDB-1CCD
    LD DE,0001H11 01 00
    Load Register Pair DE with a default STEPvalue of 1
    1CDE
    LD A,(HL)7E
    Fetch the next character from the current BASIC program line (pointer in Register Pair HL) into Register A
    1CDF-1CE0
    CP 0CCHCP STEPTKFE CC
    Check to see if the character in Register A is a STEPtoken
    1CE1-1CE3
    CALL Z,2B01HCALL Z,GETINTCC 01 2B
    So now we have a STEPtoken so we have to get the step value into DE. To do this, GOSUB to 2B01H to evaluate the expression at the location of the current BASIC program pointer in Register Pair HL and return with the integer value in Register Pair DE if the character in Register A is a STEP token
    1CE4
    PUSH DED5
    Save the STEPindex value (in Register Pair DE) to the STACK
    1CE5
    PUSH HLE5
    Save the current BASIC program pointer (in Register Pair HL) to the STACK
    1CE6
    EX DE,HLEB
    Exchange the STEPvalue (from DE) with the value of the current BASIC program pointer (in Register Pair HL). Now HL will have the value so the next call can test its size
    1CE7-1CE9
    CALL 099EHCALL ISIGNCD 9E 09
    GOSUB to 099EH to get the sign of the STEPvalue into A. It will be A=+1 if positive and A=-1 if negative
    1CEA-1CEB
    JR 1D0EHJR STPSGN18 22
    Jump down to 1D0EH to finish the entry by putting the sign of the STEP and the dummy entries into the STACK

    1CECH – Part of the Level II BASIC FORROUTINE– “SNGFOR”

    1CEC-1CEE–  ↳ SNGFOR
    CALL 0AB1HCALL FRCSNGCD B1 0A
    Need the TOvalue to be integer so GOSUB to the CONVERT TO SINGLE PRECISION routine at 0AB1H (which converts the contents of ACCumulator from integer or double precision into single precision)
    1CEF-1CF1
    CALL 09BFHCALL MOVRFCD BF 09
    Call 09BF which loads the SINGLE PRECISION value in ACCumulator (the TOvalue in integer) into Register Pair BC/DE
    1CF2
    POP HLE1
    Get the value of the current BASIC program pointer from the STACK (which should be the end of the TOexpression) and put it in Register Pair HL
    1CF3,1CF4
    PUSH BC
    PUSH DEC5
    Save all 4 bytes of the TOvalue to the STACK
    1CF5-1CF7
    LD BC,8100H01 00 81
    Load Register Pair BC with the exponent and the MSB for a single precision constant
    1CF8
    LD D,C51
    Zero the NMSB for the single precision constant in Register D
    1CF9
    LD E,D5A
    Zero the LSB for the single precision constant in Register E. Register Pairs BC and DE now hold a single precision constant equal to one
    1CFA
    LD A,(HL)7E
    Load Register A with the character at the location of the current BASIC program pointer in Register Pair HL
    1CFB-1CFC
    CP 0CCHFE CC
    Check to see if the character in Register A is a STEPtoken
    1CFD-1CFE
    LD A,01H3E 01
    Load Register A with the default STEPvalue (in this case, 1)
    1CFF-1D00
    JR NZ,1D0FHJR NZ,ONEON20 0E
    Skip over the next unstructions by jumping down to 1D0FH if the character at the location of the current BASIC program pointer in Register A isn’t a STEP token
    1D01-1D03
    CALL 2338HCALL FRMCHKCD 38 23
    Go evaluate the expression at the location of the current BASIC program pointer (which is the STEPinstruction and return with the result in ACCumulator
    1D04
    PUSH HLE5
    Save the value of the current BASIC program pointer in Register Pair HL to the STACK
    1D05-1D07
    CALL 0AB1HCALL FRCSNGCD B1 0A
    Convert the STEPvalue to single precision by calling the CONVERT TO SINGLE PRECISION routine at 0AB1H (which converts the contents of ACCumulator from integer or double precision into single precision)
    1D08-1D0A
    CALL 09BFHCALL MOVRFCD BF 09
    Load the STEPvalue into BC/DE by calling 09BF which loads the SINGLE PRECISION value in ACCumulator into Register Pair BC/DE
    1D0B-1D0D
    CALL 0955HCALL SIGNCD 55 09
    Go get the sign for the STEPincrement value (held in ACCumulator) into Register A. A will be +1 if positive, and -1 if negative
    1D0E–  ↳ STPSGN
    POP HLE1
    Get the value of the current BASIC program pointer from the STACK and put it in Register Pair HL
    1D0F–  ↳ ONEON
    PUSH BCC5
    Save the exponent and the NMSB for the single precision value in Register Pair BC to the STACK
    1D10
    PUSH DED5
    Save the NMSB and the LSB for the single precision value in Register Pair DE to the STACK
    1D11
    LD C,A4F
    Load Register C with the sign value for the STEPvalue in Register A
    1D12
    RST 20HGETYPEE7
    We need to check the value of the current number type flag, so we call the TEST DATA MODE routine at RST 20H.
    NOTE:The RST 20H routine determines the type of the current value in ACCumulator and returns a combination of STATUS flags and unique numeric values in the A Register according to the data mode flag (40AFH). The results are returned as follows:
    • 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.
    1D13
    LD B,A47
    Load Register B with type-adjusted and sign value of the number type flag test in Register A
    1D14
    PUSH BCC5
    Save the type-adjusted and sign value in Register Pair BC to the STACK
    1D15
    PUSH HLE5
    Save the value of the current BASIC program pointer in Register Pair HL to the STACK
    1D16-1D18
    LD HL,(40DFH)LD HL,(TEMP)2A DF 40
    Load Register B with a FOR x=ytoken
    1D19
    EX (SP),HLE3
    Put the pointer to the variable onto the STACK and restore the pointer to the BASIC program line being examined into HL
    1D1A-1D1B–  ↳ NXTCON
    LD B,81HLD B,$FOR06 81
    Load Register B with the FORtoken
    1D1C
    PUSH BCC5
    Save the FORand token and the sign of the STEPincrement BC to the STACK
    1D1D
    INC SP33
    Since a token only takes one byte of space, bump the value of the STACK pointer to leave a one byte gap. By continuing onward, we will wind up continuing the execution of the code string

    1D1E-1D77 – LEVEL II BASIC INTERPRETER– “NEWSTT”

    According to the original ROM source code, this is where we go for a new statement. The character on the BASIC program line pointed to by Register Pair HL should be either a “:” or an END OF LINE. The address of this routine is left on the STACK so that when a statement is executed and done, the RETurn comes back here.

    1D1E-1D20–  ↳ NEWSTT
    CALL 0358HCALL ISCHARCD 58 03
    Go check to see if a key has been pressed
    1D21
    OR AB7
    Set the flags. If a key was hit, test for SHIFT+@
    1D22-1D24
    CALL NZ,1DA0HCALL NZ,CNTCCNC4 A0 1D
    GOSUB to 1DA0H if the key pressed was a SHIFT+@. This will save the address of the last byte executed in the current line
    1D25-1D27
    LD (40E6H),HLLD (SAVTXT),HL22 E6 40
    Save the value of the current BASIC program pointer. This would be used by CONT, INPUT, CLEAR, and PRINT USING
    Note: 40E6H-40E7H holds the temporary storage location
    1D28-1D2B
    LD (40E8H),SPLD (SAVSTK),SPED 73 E8 40
    Save the value of the STACK pointer.
    Note: 40E8H-40E9H holds STACK pointer pointer
    1D2C
    LD A,(HL)7E
    Load Register A with the value at the location of the current BASIC program pointer in Register Pair HL (which SHOULD be the character which terminated the last statement)
    1D2D-1D2E
    CP 3AHFE 3A
    Check to see if the character in Register A is a :
    1D2F-1D30
    JR Z,1D5AHJR Z,GONE28 29
    If the character is a :, jump to 1D5AH since that means this is a brand new statement on this line
    1D31
    OR AB7
    There wasn’t a colon, so the only valid thing we can find now is an END OF LINE character (i.e., a NULL). This will check to see if the character in Register A is an end of the BASIC line character
    1D32-1D34
    JP NZ,1997HJP NZ,SNERRC2 97 19
    Go to the Level II BASIC error routine and display a ?SN ERRORmessage if the character in Register A isn’t an end of the BASIC line character since COLON and NULL are the only two valid characters
    1D35
    INC HL23
    So now we know that we have another line number to check, so bump the value of the current BASIC program pointer in Register Pair HL
    1D36–  ↳ GONE4
    LD A,(HL)7E
    Load Register A with the LSB of the next BASIC line pointer at the location of the current BASIC program pointer in Register Pair HL
    1D37
    INC HL23
    Bump the value of the current BASIC program pointer in Register Pair HL
    1D38
    OR (HL)B6
    Check to see if the next BASIC line pointer is equal to zero
    1D39-1D3B
    JP Z,197EHJP Z,PRGENDCA 7E 19
    If that OR matched, then we had two NULL NULL, meaning END OF PROGRAM, so JUMP to 197EH since we are at the end of the BASIC program
    1D3C
    INC HL23
    If we are here, it was not the end of the BASIC program so we need to bump the value of the current BASIC program pointer in Register Pair HL
    1D3D
    LD E,(HL)5E
    Load Register E with the LSB of the BASIC line number at the location of the current BASIC program pointer in Register Pair HL
    1D3E
    INC HL23
    Bump the value of the current BASIC program pointer in Register Pair HL
    1D3F
    LD D,(HL)56
    Load Register D with the MSB of the BASIC line number at the location of the current BASIC program pointer in Register Pair HL
    1D40
    EX DE,HLEB
    Exchange the value of the BASIC line number in Register Pair DE with the value of the current BASIC program pointer in Register Pair HL. HL will now hold the line number
    1D41-1D43
    LD (40A2H),HLLD (CURLIN),HL22 A2 40
    Save the value of the BASIC line number in Register Pair HL into the memory location devoted to tracking that sort of thing

    Honor a TRONby showing the line number if it is in effect.

    1D44-1D46
    LD A,(411BH)LD A,(TRCFLG)3A 1B 41
    Before we move on, we need to honor a TRON, if its in effect so first we load Register A with the value of the TRONflag.
    Note: 411BH holds TRON/TROFF flag
    1D47
    OR AB7
    Check for TRON. NZ means that TRACE is on
    1D48-1D49
    JR Z,1D59HJR Z,NOTTRC28 0F
    Jump out of this routine to 1D59H if TROFF
    1D4A
    PUSH DED5
    If we are here, we have to process the code to show the “<nnnn>” of a TRON. First, save the value of the current BASIC program pointer in Register Pair DE to the STACK
    1D4B-1D4C
    LD A,3CH3E 3C
    Load Register A with a <
    1D4D-1D4F
    CALL 032AHCALL OUTDOCD 2A 03
    Go display the < that preceeds the line number in TRONoutput
    1D50-1D52
    CALL 0FAFHCALL LINPRTCD AF 0F
    Call the HL TO ASCII routine at 0FAFH (which converts the value in the HL Register Pair (assumed to be an integer) to ASCII and display it at the current cursor position on the video screen) to display the current BASIC line number
    1D53-1D54
    LD A,3EH3E 3E
    Load Register A with a >
    1D55-1D57
    CALL 032AHCALL OUTDOCD 2A 03
    Go display the > that follows the line number in TRONoutput
    1D58
    POP DED1
    Get the value of the current BASIC program pointer from the STACK and put it in Register Pair DE

    That finishes the TRON routine where we display <nnnn> if it is in effect.

    1D59–  ↳ NOTTRC
    EX DE,HLEB
    Load Register Pair HL with the value of the current BASIC program pointer in Register Pair DE
    1D5A–  ↳ GONE
    RST 10HCHRGETD7
    We need to get the next token. We 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.
    1D5B-1D5D
    LD DE,1D1EHLD DE,NEWSTT11 1E 1D
    Load Register Pair DE with the return address to go to after executing one verb
    1D5E
    PUSH DED5
    Save that return address in Register Pair DE to the STACK
    1D5F–  ↳ GONE3
    RET ZC8
    Return (back to 1D1EH) if the character at the location of the current BASIC program pointer (in Register Pair HL) is an END OF LINE delimiter
    1D60-1D61–  ↳ GONE2
    SUB 80HSUB $ENDD6 80
    Check to see if the character at the location of the current BASIC program pointer in Register A is a token. This is accomplished because tokens range from 80H-FBH so this would give an index of the current token. This is looking for an ON GOTO and ON GOSUB
    1D62-1D64
    JP C,1F21HJP C,LETDA 21 1F
    If the C FLAG is set, then this must be a LETso jump to 1F21H
    1D65-1D66–  ↳ NUMCMD
    CP 3CHFE 3C
    Check to see if the token in Register A is below the TAB(token
    1D67-1D69
    JP NC,2AE7HJP NZ,ISMID$D2 E7 2A
    Jump out of here to 2AE7H if the token in Register A is greater than or equal to a TAB(token, meaning TAB(to MID$(
    1D6A
    RLCA07
    Multiply the token value in Register A by two. This doubles the remainder of the routine address offset
    1D6B
    LD C,A4F
    Load the adjusted token value in Register C from Register A
    1D6C-1D6D
    LD B,00H06 00
    Load Register B with zero so that BC now holds “00” and (2 x the token)
    1D6E
    EX DE,HLEB
    Load Register Pair DE with the value of the current BASIC program pointer in Register Pair HL
    1D6F-1D71
    LD HL,1822HLD HL,STMDSP21 22 18
    Load Register Pair HL with the list of BASIC execution addresses (called the Statement Dispatch Table in the original ROM source code)
    1D72
    ADD HL,BC09
    Add the value of the token offset in Register Pair BC to the starting address of the list of BASIC execution addresses in Register Pair HL
    1D73
    LD C,(HL)4E
    Load Register C with the LSB of the execution address at the location of the memory pointer in Register Pair HL
    1D74
    INC HL23
    Bump the value of the memory pointer in Register Pair HL
    1D75
    LD B,(HL)46
    Load Register B with the MSB of the execution address at the location of the memory pointer in Register Pair HL
    1D76
    PUSH BCC5
    Save the value of the execution address in Register Pair BC to the STACK
    1D77
    EX DE,HLEB
    Load Register Pair HL with the value of the current BASIC program pointer in Register Pair DE (i.e., restore the code string address)

    1D78-1D90 – RST 0010H CODE– “CHRGTR”

    The RST 10H code is located here. This is the EXAMINE NEXT SYMBOL routine 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).

    1D78–  ↳ CHRGTR
    INC HL23
    Bump the value of the current BASIC program pointer in Register Pair HL to the next character
    1D79–  ↳ CHRGT2
    LD A,(HL)7E
    Load Register A with the value of the character at the location of the current BASIC program pointer in Register Pair HL
    1D7A-1D7B
    CP 3AHFE 3A
    Check to see if the character at the location of the current BASIC program pointer in Register A is greater than or equal to a :
    1D7C
    RET NCD0
    Return if the character at the location of the current BASIC program pointer in Register A is greater than or equal to a :(meaning :, ;, <. Y, Z
    1D7D-1D7E–  ↳ CHRCON
    CP 20HCP ” “FE 20
    Check to see if the character at the location of the current BASIC program pointer in Register A is a SPACE
    1D7F-1D81
    JP Z,1D78HJP Z,CHRGTRCA 78 1D
    Loop if the character at the location of the current BASIC program pointer in Register A is a SPACE
    1D82-1D83
    CP 0BHFE 0B
    Check to see if the character at the location of the current BASIC program pointer in Register A is greater than or equal to 0BH (meaning it is not a control code)
    1D84-1D85
    JR NC,1D8BHJR NC,NOTLFT30 05
    Jump if the character at the location of the current BASIC program pointer in Register A if greater than or equal to 0BH
    1D86-1D87
    CP 09HFE 09
    Check to see if the character at the location of the current BASIC program pointer in Register A is greater than or equal to 09H (meaning a line feed or tab)
    1D88-1D8A
    JP NC,1D78HJP NC,CHRGTRD2 78 1D
    Loop back up to get the next character if if the character at the location of the current BASIC program pointer in Register A is greater than or equal to 09H
    1D8B-1D8C–  ↳ NOTLFT
    CP 30HCP “0”FE 30
    Check to see if the character at the location of the current BASIC program pointer in Register A is greater than or equal to a zero character
    1D8D
    CCF3F
    Set the carry flag if that ASCII character is numeric (i.e., greater than or equal to 30H)
    1D8E
    INC A3C
    Clear the CARRY FLAG if it is not numeric (i.e., it is less than 30)
    1D8F
    DEC A3D
    Set the status flags (except for the CARRY FLAG) according to the character at hand
    1D90
    RETC9
    RETurn to CALLer

    1D91-1D9A – LEVEL II BASIC RESTORE ROUTINE– “RESTORE”

    1D91–  ↳ RESTORE
    EX DE,HLEB
    Save the contents of HL by loading its contents into DE
    1D92-1D94
    LD HL,(40A4H)LD HL,(TXTTAB)2A A4 40
    Load Register Pair HL with the start of the BASIC program pointer.
    NOTE: 40A4H-40A5H holds the starting address of BASIC program text also known as the PROGRAM STATEMENT TABLE (PST)
    1D95
    DEC HL2B
    Backspace from the start of the BASIC program pointer, so that it is now TXTTAB – 1
    1D96-1D98–  ↳ RESFIN
    LD (40FFH),HLLD (DATPTR),HL22 FF 40
    Save the start of the program pointer -1 into 40FFH.
    Note: 40FFH-4100H holds READ pointer
    1D99
    EX DE,HLEB
    Restore the poiner to the current character on the BASIC program line being examined back into Register Pair HL
    1D9A
    RETC9
    RETurn to CALLer

    1D9B-1DAD – SCAN KEYBOARD ROUTINE– “ISCNTC”

    1D9B-1D9D–  ↳ ISCNTC
    CALL 0358HCALL ISCHARCD 58 03
    Go scan the keyboard
    1D9E
    OR AB7
    Check to see if a key was pressed
    1D9F
    RET ZC8
    Return if a key wasn’t pressed
    1DA0-1DA1–  ↳ CNTCCN
    CP 60HFE 60
    Check to see if the key pressed in Register A is a SHIFT+@
    1DA2-1DA4
    CALL Z,0384HCALL Z,INCHRCC 84 03
    If you got a SHIFT+@we now need to honor that by waiting for yet another key to be pressed
    1DA5-1DA7
    LD (4099H),ALD (CHARC),A32 99 40
    Save the key pressed in Register A as the value of the last key pressed.
    Note: 4099H holds the Last key pressed
    1DA8
    DEC A3D
    Check to see if the BREAKkey was pressed

    1DA9-1DAD – STOPROUTINE– “STOP”

    1DA9–  ↳ STOP
    RET NZC0
    Return if the BREAKkey wasn’t pressed.
    This is the STOPentry point
    1DAA
    INC A3C
    Readjust the value of the key pressed in Register A
    1DAB-1DAD
    JP 1DB4HJP CONSTPC3 B4 1D
    Jump into the applicable portion of the code that processes END

    1DAE-1DE3 – LEVEL II BASIC ENDROUTINE– “END”

    1DAE–  ↳ END

    RET NZC0
    Return and display a ?SN ERRORmessage if there is anything following the ENDtoken -or- if we did not get a BREAK
    1DAF
    PUSH AFF5
    Save all the condition codes and the value in Register Pair AF to the STACK
    1DB0-1DB2
    CALL Z,41BBHCALL Z,CLSALLCC BB 41
    Jump into DOS to see if it wants to do anything here
    1DB3
    POP AFF1
    Restore the condition codes and Register A
    1DB4-1DB6–  ↳ CONSTP
    LD (40E6H),HLLD (SAVTXT),HL22 E6 40
    Save the value of the current BASIC program pointer in Register Pair HL. This will be for a CONTinue.
    Note: 40E6H-40E7H holds the temporary storage location
    1DB7-1DB9
    LD HL,40B5HLD HL,TEMPST21 B5 40
    We next want to reset the String temp pointer, so load Register Pair HL with the starting address of the temporary string work area.
    Note: 40B5H-40D2H holds Temporary string work area
    1DBA-1DBC
    LD (40B3H),HLLD (TEMPPT),HL22 B3 40
    Save the value in Register Pair HL as the new temporary string work area pointer.
    Note: 40B3H-40B4H holds the next available location in the temporary string work area pointer
    1DBD-1DBFH
    LD HL,FFF6H21 F6 FF
    Z-80 space saving trick. If passing down from the prior instruction, the line is run and the next OR FFH line is NOT!
    1DBE-1DBF–  ↳ STPEND
    OR FFHF6 FF
    Force A to be non-zero so as to force the printing of the BREAK message
    1DC0
    POP BCC1
    Clears out the NEWSTT return address from the STACK, as we won’t be returning
    1DC1-1DC3–  ↳ ENDCON
    LD HL,(40A2H)LD HL,(CURLIN)2A A2 40
    Load Register Pair HL with the value of the current BASIC line number in binary.
    Note: 40A2H-40A3H holds the current BASIC line number
    1DC4
    PUSH HLE5
    Save the value of the current BASIC line number (in binary) in Register Pair HL to the STACK
    1DC5
    PUSH AFF5
    Save the value in Register Pair AF to the STACK. A will be 0 if it is an ENDand A will be a 1 if it is a STOP. If A is 0 then it should NOT print the BREAK message
    1DC6
    LD A,L7D
    These 3 instructions test to see if we are in command mode by testing for an uninitialized line (meaning a line number of FFFF). First, load Register A with the LSB of the current BASIC line number in Register L
    1DC7
    AND HA4
    Combine the MSB of the current BASIC line number in Register H with the LSB of the current BASIC line number in Register A
    1DC8
    INC A3C
    Bump the combined value of the current BASIC line number in Register A
    1DC9-1DCA
    JR Z,1DD4HJR Z,DIRIS28 09
    Increasing FFFF by 1 would flip the Z flag on, meaning there was no line number. If there was no line number, skip the next 3 instructions and go to 1DD4H

    If we are here, then there was a line number, so we need to set some locations to enable a CONT command to work.

    1DCB-1DCD
    LD (40F5H),HLLD (OLDLIN),HL22 F5 40
    Save the line number we ended on in Register Pair HL.
    Note: 40F5H-40F6H holds the last line number executed
    1DCE-1DD0
    LD HL,(40E6H)LD HL,(SAVTXT)2A E6 40
    Load Register Pair HL with the value of the current BASIC program pointer .
    Note: 40E6H-40E7H holds the temporary storage location
    1DD1-1DD3
    LD (40F7H),HLLD (OLDTXT),HL22 F7 40
    Save the value of the current BASIC program pointer in Register Pair HL.
    Note: 40F7H-40F8H holds Last byte executed
    1DD4-1DD6–  ↳ DIRIS
    CALL 038BHCALL FINLPTCD 8B 03
    Go set the current output device to the video display
    1DD7-1DD9
    CALL 20F9HCALL CRDONZCD F9 20
    Go display a carriage return if necessary
    1DDA
    POP AFF1
    Restore A so we can see if this is an END(where A=0) or a STOP(where A=1)
    1DDB-1DDD
    LD HL,1930HLD HL,BRKTXT21 30 19
    Load Register Pair HL with the starting address of the BREAKmessage
    1DDE-1DE0
    JP NZ,1A06HJP NZ,ERRFINC2 06 1A
    Jump to 1A06H if it was a BREAKor a STOPthat halted program execution
    1DE1-1DE3
    JP 1A18HJP STPRDYC3 18 1A
    At this point it was either due to an ENDstatement or an error while in command mode so jump to 1A18H (note: This uses a Z-80 trick as 1A18H is in the middle of a 2 Opcode instruction starting at 1A17H)

    1DE4-1DF6 – LEVEL II BASIC CONT ROUTINE– “CONT”

    1DE4-1DE6–  ↳ CONT
    LD HL,(40F7H)LD HL,(OLDTXT)2A F7 40
    Load Register Pair HL with the value of the continuation address. A stored text pointer of 0000 is set up by STKINI if there is nothing to CONTinue
    Note: 40F7H-40F8H holds Last byte executed
    1DE7
    LD A,H7C
    Load Register A with the MSB of the continuation address in Register H
    1DE8
    OR LB5
    Combine the LSB of the continuation address in Register L with the MSB of the continuation address in Register A. If there was nothing in HL, this would trigger a Z flag. There should be nothing in the HL flag if there was a STOP, END, hitting ENTERin response to an INPUT request, and hitting BREAK
    1DE9-1DEA-  ↳ RESERR
    LD E,20HLD E,ERRCN1E 20
    Load Register E with a ?CN ERRORcode
    1DEB-1DED
    JP Z,19A2HJP Z,ERRORCA A2 19
    Go to the ?CN ERRORroutine if CONTisn't possible (meaning the line number was zero)
    1DEE
    EX DE,HLEB
    If we are here, there was a valid return line number, so load Register Pair DE with the value of the continuation line number in Register Pair HL
    1DEF-1DF1
    LD HL,(40F5H)LD HL,(OLDLIN)2A F5 40
    Load HL with the value of the last BASIC line number executed.
    Note: 40F5H-40F6H holds the last line number executed
    1DF2-1DF4
    LD (40A2H),HLLD (CURLIN),HL22 A2 40
    Save the last line number executed in Register Pair HL (which had the error) as the current BASIC line number.
    Note: 40A2H-40A3H holds the current BASIC line number
    1DF5
    EX DE,HLEB
    Swap so that HL will be the address of the continuation line
    1DF6
    RETC9
    Return (continue running but from the CONTline number)

    1DF7-1DF8 - TRON ENTRY POINT- "TON"

    Turns TRONfeature on. Causes line numbers for each BASIC statement executed to be displayed. Uses Register A.

    1DF7-1DF8-  ↳ TON
    LD A,0AFH3E AF
    Load Register A with a nonzero value. Note that this value was chosen simply because this is also a Z-80 trick. If 1DF8 is jumped into, that is XOR A which will set A to zero instead of this value

    1DF8 - TROFF ENTRY POINT- "TOFF"

    1DF8-  ↳ TOFF
    XOR AAF
    Z-80 trick. This command is only visible if jumped to. This zeros Register A

    The following code is common to both TRON and TROFF.

    1DF9-1DFB
    LD (411BH),ALD (TRCFLG),A32 1B 41
    Save the value in Register A as the current value of the TRON/TROFF flag.
    Note: 411BH holds TRON/TROFF flag
    1DFC
    RETC9
    RETurn to CALLer

    1DFD-1DFF - DISK ROUTINE NOT USED BY LEVEL II BASIC- "POPAHT".

    1DFD-  ↳ POPAHT
    POP AF
    1DFEE1
    POP HL
    Restore the pointer to the BASIC program line being parsed into Register Pair HL
    1DFF
    RET

    1E00-1E02 - DEFSTRENTRY POINT- "DEFSTR"

    1E00-1E01-  ↳ DEFSTR1E 03
    LD E,03H
    Load Register E with the DEFSTRstring number type flag (03H)
    1E02-1E04
    LD BC,021EH01 1E 02
    Z-80 Trick! If this line is found via pass through, the BC is just loaded with this number, and then others, until it hits 1E0BH with E still holding 3

    1E03-1E05 - DEFINT ENTRY POINT- "DEFINT"

    1E03-1E04-  ↳ DEFINT
    LD E,02H1E 02
    Load Register E with the DEFINTinteger number type flag (02H)
    1E05
    LD BC,041EH01 1E 04
    Z-80 Trick! If this line is found via pass through, the BC is just loaded with this number, and then others, until it hits 1E0BH with E still holding 2

    1E06-1E08 - DEFSNG ENTRY POINT- "DEFREA"

    1E06-1E07-  ↳ DEFREA
    LD E,04H1E 04
    Load Register E with the DEFSNGsingle precision number type flag (04H)
    1E08
    LD BC,081EH01 1E 08
    Z-80 Trick! If this line is found via pass through, the BC is just loaded with this number, and then others, until it hits 1E0BH with E still holding 4

    1E09-1E0A - DEFDBL ENTRY POINT- "DEFDBL"

    1E09-1E0A-  ↳ DEFDBL
    LD E,08H1E 08
    Load Register E with the DEFDBLdouble precision number type flag (08H)

    1E0B-1E3C - COMMON CODE SHARED BY DEFSTR/DEFINT/DEFSNG/DEFDBL- "DEFCON".

    All of those can either have a -for a range of values or be separated by ,. This code needs to figure out the variables that followed the DEF???instruction and then set the variable type (which is currently sitting in Register E) in the variable table

    1E0B-1E0D-  ↳ DEFCON
    CALL 1E3DHCALL ISLETCD 3D 1E
    A call to 1E3DH checks the value at memory location (HL), which would be the current BASIC program pointer, to see if it is a letter. If its not, CARRY is set. If it is, CARRY is clear
    1E0E-1E10
    LD BC,1997HLD BC,SNERR01 97 19
    Load Register Pair BC with a return address which will return to the ?SN ERRORroutine
    1E11
    PUSH BCC5
    Save the ?SN ERROR address (in Register Pair BC) to the STACK
    1E12
    RET CD8
    Return if the character at the location of the current BASIC program pointer in Register A isn't alphabetic (meaning that DEF???wasn't followed by a letter)
    1E13-1E14
    SUB 41HD6 41
    Subtract 41H from the letter's value in Register A so that it will be in the range of 0-25
    1E15
    LD C,A4F
    Load Register C with the offset (i.e., adjusted value in Register A)
    1E16
    LD B,A47
    Load Register B with the same, under the assumption that it will get updated at some point
    1E17
    RST 10HCHRGETD7
    Since we now need bump the value of 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.
    1E18-1E19
    CP 0CEHCP MINUTKFE CE
    Check to see if the character at the location of the current BASIC program pointer in Register A is a -
    1E1A-1E1B
    JR NZ,1E25HJR NZ,NOTRNG20 09
    If it's not a -we know this isn't a range, so jump to 1E25H because we don't need to get any more variables
    1E1C
    RST 10HCHRGETD7
    If we are here, then we know the DEF???has a range, so we need another character. We bump the value of 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.
    1E1D-1E1F
    CALL 1E3DHCALL ISLETCD 3D 1E
    A call to 1E3DH checks the value at memory location (HL), which would be the current BASIC program pointer, to see if it is a letter. If its not, CARRY is set. If it is, CARRY is clear
    1E20
    RET CD8
    Return with a ?SN ERROR if the character at the location of the current BASIC program pointer in Register A isn't alphabetic
    1E21-1E22
    SUB 41HD6 41
    Subtract 41H from the letter's value in Register A so that it will be in the range of 0-25
    1E23
    LD B,A47
    Overwrite Register B with the offset for the end of range letter variable
    1E24
    RST 10HCHRGETD7
    Since we now need bump the value of 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.
    1E25-  ↳ NOTRNG
    LD A,B78
    Load Register A with the value of the second letter in Register B
    1E26
    SUB C91
    Subtract the value of the first letter in Register C from the value of the second letter in Register A
    1E27
    RET CD8
    If the varible names are not in ascending order (the one in C is earlier than the one in A), the CARRY FLAG will ahve been set, so RETurn to a ?SN ERROR
    1E28
    INC A3C
    Bump the value in Register A so that it holds the number of variable names to be changed
    1E29
    EX (SP),HLE3
    Save the pointer to the next character to be parsed in the BASIC program to the STACK, and clear the error address
    1E2A-1E2C
    LD HL,4101HLD HL,DEFTBL21 01 41
    Load Register Pair HL with the starting address of the variable declaration table.
    Note: 4101H-411AH holds Variable Declaration Table
    1E2D-1E2E
    LD B,00H06 00
    Load Register B with zero so that the starting offset in Register C can be utilized in 16 bit math
    1E2F
    ADD HL,BC09
    Make HL point to the first entry in the variable table to be modified by adding the value of the first letter in Register Pair BC to the value of the starting address of the variable declaration table in Register Pair HL
    1E30-  ↳ LPDCHG
    LD (HL),E73
    Top of a loop. E was set on entry to string, integer, single precision, or double precision as applicable. Save the number type flag in Register E at the location of the memory pointer in Register Pair HL
    1E31
    INC HL23
    Bump the value of the memory pointer in Register Pair HL
    1E32
    DEC A3D
    Decrement the count of the number of variables to be changed in Register A so as to track the number of changes remaining to be made
    1E33-1E34
    JR NZ,1E30HJR NZ,LPDCHG20 FB
    Loop until all of the variables in the DEFxxxrange have been changed
    1E35
    POP HLE1
    Restore the pointer to the BASIC program line being processed into Register Pair HL
    1E36
    LD A,(HL)7E
    Fetch the last character in the BASIC program line being processed into Register A
    1E37-1E38
    CP 2CHFE 2C
    Check to see if the character at the location of the current BASIC program pointer in Register A is a ,
    1E39
    RET NZC0
    Return if the character at the location of the current BASIC program pointer in Register A isn't a ,
    1E3A
    RST 10HCHRGETD7
    Since we now need bump the value of 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.
    1E3B-1E3C
    JR 1E0BHJR DEFCON18 CE
    Loop until done with all the variables

    1E3D-1E44 - EXAMINE VARIABLE- "ISLET"

    This routine tests the value pointed to by the HL Register Pair and sets the C FLAG if it is an ASCII letter value; and otherwise the NC FLAG is set.

    1E3D-  ↳ ISLET
    LD A,(HL)7E
    Load Register A with the character at the location of the current BASIC program pointer in Register Pair HL
    1E3E-1E3F-  ↳ ISLET2
    CP 41HCP "A"FE 41
    Check to see if the character at the location of the current BASIC program pointer in Register A is less than an "A"
    1E40
    RET CD8
    Return early if the character at the location of the current BASIC program pointer in Register A is less than an A
    1E41-1E42
    CP 5BHCP "Z"+1FE 5B
    Check to see if the character at the location of the current BASIC program pointer in Register A is greater than a "Z"
    1E43
    CCF3F
    Complement the value of the Carry flag. On exit this routine will have the Carry flag set if the character at the location of the current BASIC program pointer in Register A isn't alphabetic and will have the Carry flag cleared if the character at the location of the current BASIC program pointer in Register A is alphabetic
    1E44
    RETC9
    RETurn to CALLer

    1E45-1E4E - EXAMINE VARIABLE- "INTIDX"

    This is called when evaluating a subscript for a variable reference. It will evaluate if the value is positive or negative.

    According to the original ROM source code, this routine reads a formula from the current position and turns it into a positive integer, with the result put into Register Pair DE. Negative arguments are not allowed. On exit, Register Pair HL wil point to the terminating character of the formula on the BASIC program line being examined.

    1E45-  ↳ INTIDX
    RST 10HCHRGETD7
    Get the next symbol from the input. Bump the value of 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.
    1E46-1E48-  ↳ INTID2
    CALL 2B02HCALL GETIN2CD 02 2B
    Go evaluate the expression at the current location of the BASIC program pointer in Register Pair HL and return with the integer result in Register Pair DE and the condition codes set based on the high order of the result
    1E49
    RET PF0
    Return if the integer result in Register Pair DE is positive. If it is negative, flow down to the ?FC ERROR

    1E4A - ?FC ERROR entry point- "FCERR"

    1E4A-1E4B-  ↳ FCERR

    LD E,08H1E 08
    Load Register E with an ?FC ERRORcode
    1E4C-1E4E
    JP 19A2HJP ERRORC3 A2 19
    Display a ?FC ERRORmessage. If this is from a pass through, the error wll show if the integer result in Register Pair DE is negative

    1E4F-1E79 - Line Number Conversion Routine 1 - "LINSPC"

    According to the original ROM source code, LINSPC and LINGET are identical except t hat LINSPC also permits the use of a "." to act as the current line number. Otherwise, They read the line number from the current position in the BASIC program. Possible line numbers are 00000-65529. On exit, DE holds the line number, and HL is updated to point to the terminating character, and Register A will contain the terminating character with the FLAGs set based on Register A's value.

    1E4F-  ↳ LINSPC
    LD A,(HL)7E
    Load Register A with the character at the location of the current BASIC program pointer in Register Pair HL
    1E50-1E51
    CP 2EHCP "."FE 2E
    Check to see if the character at the location of the current BASIC program pointer in Register A is a .
    1E52
    EX DE,HLEB
    Load Register Pair DE with the value of the current BASIC program pointer in Register Pair HL
    1E53-1E55
    LD HL,(40ECH)LD HL,(DOT)2A EC 40
    Load Register Pair HL with the current BASIC line number.
    Note: 40ECH-40EDH holds current line number
    1E56
    EX DE,HLEB
    Exchange the value of the current BASIC program pointer in Register Pair DE with the current BASIC line number in Register Pair HL
    1E57-1E59
    JP Z,1D78HJP Z,CHRGTRCA 78 1D
    Jump to the RST 10H (EXAMINE NEXT SYMBOL) routine if the character at the location of the current BASIC program pointer in Register A is a .

    1E5A - Line Number Conversion Routine 2- "LINGET"

    Converts numeric ASCII string pointed to by the HL Register Pair, to HEX and places the result in the DE Register Pair. After execution HL points to the delimiter and the A Register contains the delimiter value. The Z flag is set if the delimiter equals 00 or 3A. Z is reset if any other delimiter is used. If there is no string at the location pointed to by HL the routine will return a MO ERROR (missing operand). If the result in the DE Register Pair exceeds FFFFH an OV ERROR (overflow) results.

    1E5A-  ↳ LINGET
    DEC HL2B
    Backspace HL (the current BASIC program pointer) to the current character
    1E5B-1E5D-  ↳ LINGT2
    LD DE,0000H11 00 00
    Since Register Pair DE will be the "accumulator" for the result, start it off at zero
    1E5E-  ↳ MORLIN
    RST 10HCHRGETD7
    Top of a loop. Re-process that previous character through a RST 10H call.

    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.
    1E5F
    RET NCD0
    Return if the character at the location of the current BASIC program pointer in Register A isn't numeric
    1E60
    PUSH HLE5
    Save the value of the current BASIC program pointer in Register Pair HL to the STACK
    1E61
    PUSH AFF5
    Save the value in Register Pair AF (which is the digit plus flags resulting from the RST 10H call) to the STACK
    1E62-1E64
    LD HL,1998H21 98 19
    Load Register Pair HL with 6552.

    Why 6552? Well, since the Z-80 has no multiply function, checking for any possible arbitrary number would require 4 branches for each step in the 'add to itself until it hits * 10' plus another 4 when adding the last digit. The TRS-80 ROM didn't have that kind of room, nor the time to do all that, so they needed a cheat and that cheat was to let it go as high 65529. After all, 6552 + 1 more digit can NEVER exceed 65535, but 6553 + 1 digit sure can!

    So with this trick, the ROM just needs to first check the number against 6552, which, when multiplied by 10, and adding one more digit, will never exceed 65529 (because 9 is the highest one number can go).

    By using this trick, only 1 comparison is needed (is it greater or less than 6552) . at the cost of 4 usable line numbers/memory size setting in the 6553x range.

    Wait, you say. 65535-65529 is 6 numbers, so why do you say 4. Well, another shortcut the ROM uses is that it assumes anything at line number 65535 is being entered in direct mode (i.e., no line number; just a command), so 65535 couldn't be a line number. Similarly, in 1A09, 65534 is reserved to trigger the BASIC interpreter to jump to the initial powerup routine in the ROM (i.e., a reboot) so you couldn't use that line number either
    1E65
    RST 18HCOMPARDF
    Now we need to check to see if the integer value in DE is greater than 6552, so we call the COMPARE DE:HL routine, which numerically compares DE and HL. Will not work for signed integers (except positive ones). Uses the A-register only. The result of the comparison is returned in the status Register as: CARRY SET=HL<DE; NO CARRY=HL>DE; NZ=Unequal; Z=Equal)
    1E66-1E68
    JP C,1997HJP C,SNERRDA 97 19
    Go to the Level II BASIC error routine and display a SN ERROR message if the value in Register Pair DE is greater than 6552

    Now we need HL = DE * 10

    1E69
    LD H,D62
    Load Register H with the MSB of the integer total in Register D.

    This is so we can multiply HL (which should hold the number 6552) by 10
    1E6A
    LD L,E6B
    Load Register L with the LSB of the integer total in Register E.

    This is so we can multiply HL (which should hold the number 6552) by 10
    1E6B
    ADD HL,DE19
    Multiply the integer value in Register Pair HL by two
    1E6C
    ADD HL,HL29
    Add HL to itself, which is the same as multiplying the integer value in Register Pair HL by two. The integer result in Register Pair HL is now equal to the integer value in Register Pair DE times four
    1E6D
    ADD HL,DE19
    Add the integer value in Register Pair DE to the integer value in Register Pair HL. The integer result in Register Pair HL is now equal to the integer value in Register Pair DE times five
    1E6E
    ADD HL,HL29
    Add HL to itself, which is the same as multiplying the integer value in Register Pair HL by two. The integer result in Register Pair HL is now equal to the integer value in Register Pair DE times ten. Now HL is 65520
    1E6F
    POP AFF1
    Put the last ASCII digit (from the STACK) into AF
    1E70-1E71
    SUB 30HSUB "0"D6 30
    Convert the ASCII digit in Register A to binary
    1E72
    LD E,A5F
    Load Register E with the binary value of the character in Register A
    1E73-1E74
    LD D,00H16 00
    Load Register D with zero so that DE will be 0000 through 0009 (the binary equivalent of the digit)
    1E75
    ADD HL,DE19
    Add the binary value of the character in Register Pair DE to the integer value in Register Pair HL

    As noted above, adding in any digit can only result in HL going to 65529
    1E76
    EX DE,HLEB
    Swap DE and HL. This will have the effect of setting DE to be 10(base 10) * DE + A
    1E77
    POP HLE1
    Restore the pointer to the next digit (from the STACK) into HL
    1E78-1E79
    JR 1E5EHJR MORLIN18 E4
    Loop until the ASCII to binary conversion has been completed

    1E7A-1EA0 - LEVEL II BASIC CLEARROUTINE- "CLEAR"

    According to the original ROM source code, this will change the amount of string space allowed. If no formula is given, the amount of string space will remain unchanged. On entry, if the Z flag is set, there was no parameter present

    1E7A-1E7C-  ↳ CLEAR
    JP Z,1B61HJP Z,CLEARCCA 61 1B
    Jump to 1B61H (which will set all 26 variables to single precision) if there isn't a number of bytes specified to clear for string space
    1E7D-1D7F
    CALL 1E46HCALL INTID2CD 46 1E
    Go evaluate the number of bytes to be reserved for string space and return with the integer result in Register Pair DE
    1E80
    DEC HL2B
    Decrement the current BASIC program pointer in Register Pair HL
    1E81
    RST 10HCHRGETD7
    Evaluate the next instruction through a RST 10H call. This will bump the value of 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.
    1E82
    RET NZC0
    Return out of the routine if the character at the location of the current BASIC program pointer isn't an end of the BASIC statement character
    1E83
    PUSH HLE5
    Save the value of the current BASIC program pointer in Register Pair HL to the STACK
    1E84-1E86
    LD HL,(40B1H)LD HL,(MEMSIZ)2A B1 40
    Load the top of memory pointer into Register Pair HL.
    Note: 40B1H-40B2H holds MEMORY SIZE? pointer
    1E87
    LD A,L7D
    Load Register A with the LSB of the top of memory pointer in Register L
    1E88
    SUB E93
    Subtract the LSB of the number of bytes for string space in Register E from the LSB of the top of memory pointer in Register A
    1E89
    LD E,A5F
    Load Register E with the LSB of the start of string space in Register A
    1E8A
    LD A,H7C
    Load Register A with the MSB of the top of memory pointer in Register H
    1E8B
    SBC A,D9A
    Subtract the MSB of the number of bytes for string space in Register D from the MSB of the top of memory pointer in Register A
    1E8C
    LD D,A57
    Load Register D with the MSB of the start of string space pointer in Register A
    1E8D-1E8F
    JP C,197AHJR C,OMERRDA 7A 19
    If the CARRY flag was triggered there isn't enough memory for the amount of string space specified, so go show a ?OM ERROR message
    1E90-1E92
    LD HL,(40F9H)LD HL,(VARTAB)2A F9 40
    Load Register Pair HL with the end of the BASIC program pointer.
    Note: 40F9H-40FAH holds the starting address of the simple variable storage area
    1E93-1E95
    LD BC,0028H01 28 00
    Load Register Pair BC with the least amount of space needed for BASIC program variables just so that we have some breathing room
    1E96
    ADD HL,BC09
    Add the value in Register Pair BC to the end of BASIC program pointer in Register Pair HL
    1E97
    RST 18HCOMPARDF
    Now we need to check to the adjusted end of the BASIC program pointer (in HL) with the start of the string space pointer (in DE), so we call the COMPARE DE:HL routine, which numerically compares DE and HL. Will not work for signed integers (except positive ones). Uses the A-register only. The result of the comparison is returned in the status Register as: CARRY SET=HL<DE; NO CARRY=HL>DE; NZ=Unequal; Z=Equal)
    1E98-1E9A
    JP NC,197AHJP NC,OMERRD2 7A 19
    Display an ?OM ERRORmessage if the start of string space pointer in Register Pair DE is less than the adjusted end of the BASIC program pointer in Register Pair HL
    1E9B
    EX DE,HLEB
    Put the new start of the string area address into HL
    1E9C-1E9E
    LD (40A0H),HLLD (STKTOP),HL22 A0 40
    Load the start of string space pointer with HL. 40A0H-40A1H holds the start of string space pointer
    1E9F
    POP HLE1
    Restore the code string pointer back into HL
    1EA0-1EA2
    JP 1B61HJP CLEARCC3 61 1B
    Jump to 1B61H

    1EA3-1EB0 - LEVEL II BASIC RUNROUTINE- "RUN"

    On entry, if the Z flag is set, there was no parameter present

    1EA3-1EA5-  ↳ RUN
    JP Z,1B5DHJP Z,RUNCCA 5D 1B
    Jump to 1B5DH if there isn't a line number specified after the RUNtoken
    1EA6-1EA9
    CALL 41C7HCALL LRUNCD C7 41
    GOSUB to 41C7H to see if DOS wants to do anything here - if so clean up the BASIC variables and pointer, set HL=TXTTAB-1 and RETurn to NEWSTT
    1EA9-1EAB
    CALL 1B61HCALL CLEARCCD 61 1B
    Clean up, reset the STACK, CDATPTR, and variables. Only HL is preserved
    1EAC-1EAE
    LD BC,1D1EHLD BC,NEWSTT01 1E 1D
    Load Register Pair BC with the continuation address in the execution driver
    1EAF-1EB0
    JR 1EC1HJR RUNC218 10
    Put NEWSTT at the top of STACK (so that a RETurn will jump to it) and then pass through to the GOTOroutine via a jump to 1EC1H to begin execution at the nnnn line

    1EB1-1EC1 - LEVEL II BASIC GOSUBROUTINE- "GOSUB"

    According to the original ROM source code, the 5 byte GOSUB entry on the STACK is as follows:

    • LOW ADDRESS
      • 1 Byte - The GOSUB token.
      • 2 Bytes - The line number of the GOSUB statement.
      • 2 Bytes - A pointer to the text of the GOSUB in the BASIC program being executed.
    • HIGH ADDRESS
    Can be used to execute the equivalent of a GOSUBstatement from an assembly program. It allows a BASIC subroutine to be called from an assembly subroutine. After the BASIC subroutine executes, control returns to the next statement in the assembly program. All registers are used. On entry, the HL must contain an ASCII string with the starting line number of the subroutine.
    1EB1-1EB2-  ↳ GOSUB
    LD C,03H0E 03
    Load Register B with half the number of bytes needed for the GOSUBpush
    1EB3-1EB5
    CALL 1963HCALL GETSTKCD 63 19
    Go make sure that there's enough memory for the GOSUBpush
    1EB6
    POP BCC1
    Get the NEWSTT return address from the STACK and put it in Register Pair BC
    1EB7
    PUSH HLE5
    Save the value of the current BASIC program pointer in Register Pair HL to the STACK
    1EB8
    PUSH HLE5
    Create a hole to be filled in later by once again saving the value of the current BASIC program pointer to the STACK
    1EB9-1EBB
    LD HL,(40A2H)LD HL,(CURLIN)2A A2 40
    Load Register Pair HL with the value of the current BASIC line number in binary.
    Note: 40A2H-40A3H holds the current BASIC line number
    1EBC
    EX (SP),HLE3
    Put the binary value for the current line number into the hole in the STACK AND restore the code string pointer
    1EBD-1EBE
    LD A,91HLD A,$GOSUB3E 91
    Load Register A with a GOSUBtoken
    1EBF
    PUSH AFF5
    Save the GOSUBtoken (in Register Pair AF) to the STACK
    1EC0
    INC SP33
    Bump the value of the STACK pointer since we just used one space for the GOSUB token
    1EC1-  ↳ RUNC2
    PUSH BCC5
    Save the "NEWSTT" return address in Register Pair BC to the STACK, and now spill down to the GOTOroutine

    1EC2-1EDD - LEVEL II BASIC GOTOROUTINE- "GOTO"

    1EC2-1EC4-  ↳ GOTO
    CALL 1E5AHCALL LINGETCD 5A 1E
    We need to put the line number to branch to into DE so we call ASCII TO INTEGER routine at 1E5A which 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
    1EC5-1EC7-  ↳ GOTO2
    CALL 1F07HCALL REMCD 07 1F
    Go bump the current BASIC program pointer in Register Pair HL until it points to the end of the current BASIC line
    1EC8
    PUSH HLE5
    Save the value of the current BASIC program pointer in Register Pair HL to the STACK
    1EC9-1ECB
    LD HL,(40A2H)LD (CURLIN),HL2A A2 40
    Load Register Pair HL with the binary equivalent of the last line number.
    Note: 40A2H-40A3H holds the current BASIC line number
    1ECC
    RST 18HCOMPARDF
    Now we need to compare the value of the current BASIC line number (in HL) with the value of the line number to branch to (in DE), so we call the COMPARE DE:HL routine, which numerically compares DE and HL. Will not work for signed integers (except positive ones). Uses the A-register only. The result of the comparison is returned in the status Register as: CARRY SET=HL<DE; NO CARRY=HL>DE; NZ=Unequal; Z=Equal)
    1ECD
    POP HLE1
    Get the value of the current BASIC program pointer from the STACK and put it in Register Pair HL
    1ECE
    INC HL23
    Bump the value of the current BASIC program pointer in Register Pair HL so as to restore the code string address. DE holds the line number specified
    1ECF-1ED1
    CALL C,1B2FHCALL C,LOOPDC 2F 1B
    If the line number to branch to (in Register Pair DE) is greater than the current BASIC line number then go find where the line number is located, starting with the current line
    1ED2-1ED4
    CALL NC,1B2CHCALL NC,FNDLIND4 2C 1B
    If the line number to branch to (in Register Pair DE) is less than the current BASIC line number, then go find where the line number is located by calling the SEARCH FOR LINE NUMBER routine at 1B2CH which looks for the line number specified in DE. Returns C/Z with the line found in BC, NC/Z with line number is too large and HL/BC having the next available location, or NC/NZ with line number not found, and BC has the first available one after that.
    Yes, this is a second search that kicks in if the prior search (which started at the current line number) failed, as this one will start at the beginning of the program
    1ED5,1ED6
    LD H,B
    LD L,C60
    Let HL = BC (which is holding the address of the requested line number
    1ED7
    DEC HL2B
    Decrement the value of the current BASIC program pointer in Register Pair HL to now point to the End of Line terminator from the previous line
    1ED8
    RET CD8
    If a line number was found then the CARRY FLAG would have been set. If so, RETURN from this routine to the execution driver (to start execution at the new line number), otherwise pass through to the ?UL ERROR routine

    1ED9-1EDD - LEVEL II BASIC ?UL ERRORROUTINE- "USERR"

    1ED9-1EDA-  ↳ USERR
    LD E,0EHLD E,ERRUS1E 0E
    Load Register E with an ?UL ERRORcode
    1EDB-1EDD
    JP 19A2HJP ERRORC3 A2 19
    Since the line number wasn't found, display a ?UL ERRORmessage

    1EDE-1E04 - LEVEL II BASIC RETURNROUTINE- "RETURN"

    Returns control to the BASIC statement following the last GOSUBcall. An assembly program called by a BASIC subroutine may wish to return directly to the orginal caller without returning through the subroutine entry point. This exit can be used for that return. The return address to the STACK for the call to the assembly program must be cleared before returning via 1EDFH. On entry, if the Z flag is set, there was no parameter present.

    1EDE-  ↳ RETURN
    RET NZC0
    Display a ?SN ERRORmessage if there is anything following the RETURNtoken
    1EDF-1EE0
    LD D,0FFH16 FF
    Load Register D with FFH (a dummy address for the search routine to ensure that the variable pointer in Register Pair DE does not find a match)
    1EE1-1EE3
    CALL 1936HCALL FNDFORCD 36 19
    Go past all the FORentries in the STACK via this CALL which backspaces the STACK pointer by four and return with the value at the location of the STACK pointer in Register A
    1EE4
    LD SP,HLF9
    Update the STACK by loading the STACK pointer with the new value in Register Pair HL
    1EE5-1EE7HS
    LD (40E8H),HLLD (SAVSTK),HL22 E8 40
    Save the STACK pointer position in Register Pair HL.
    Note: 40E8H-40E9H holds STACK pointer pointer
    1EE8-1EE9
    CP 91HCP $GOSUBFE 91
    Check to see if the value in Register A is a GOSUBtoken
    1EEA-1EEB
    LD E,04HLD E,ERRRG1E 04
    Load Register E with a ?RG ERRORcode

    1EEC - RG ERROR entry point.

    1EEC-1EEE
    JP NZ,19A2HJP NZ,ERRORC2 A2 19
    Display a ?RG ERRORmessage if there isn't a GOSUBpush to the STACK
    1EEF
    POP HLE1
    Otherwise, get the line number that the GOSUBwas from from the STACK and put it in Register Pair HL
    1EF0-1EF2
    LD (40A2H),HLLD (CURLIN),HL22 A2 40
    Save the GOSUBline number in Register Pair HL as the current BASIC line number.
    Note: 40A2H-40A3H holds the current BASIC line number

    The next few instructions set up to see if there was GOSUB from the command line instead of insiude a program.

    1EF3
    INC HL23
    Bump the value of the current BASIC line number in Register Pair HL
    1EF4
    LD A,H7C
    Load Register A with the MSB of the adjusted current BASIC 1ine number in Register Pair HL
    1EF5
    OR LB5
    Combine the LSB of the adjusted current BASIC line number in Register L with the adjusted MSB of the current BASIC line number in Register A. This is a test for an overflow or direct command mode
    1EF6-1EF7
    JR NZ,1EFFHJR NZ,GOBACK20 07
    If the NZ FLAG is set, then this was NOT in direct command mode, so JUMP to 1EFF to do the RETURN
    1EF8-1EFA
    LD A,(40DDH)LD A,(BFKLFL)3A DD 40
    If we are here, we may have a one liner! Load Register A with the command mode flag.
    Note: 40DDH holds INPUT flag
    1EFB
    OR AB7
    Set the flags
    1EFC-1EFE
    JP NZ,1A18HJP NZ,STPRDYC2 18 1A
    Jump to 1A18H if Level II BASIC, instead of doing a RETURN, since we are in direct command mode
    1EFF-1F01-  ↳ GOBACK
    LD HL,1D1EHLD HL,NEWSTT21 1E 1D
    Load Register Pair HL with the return address of NEWSTT
    1F02
    EX (SP),HLE3
    Put the "NEWSTT" return address into the top of the STACK and put the current location on the program line being parsed back into Register Pair HL
    1F03-1F04
    LD A,0E1H3E E1
    Z-80 Trick! If passing through will just change Register A to this value, but NOT do a POP HL
    1F04-  ↳ DATAH
    POP HLE1
    If this is jumped to, then process a POP HL, which would NOT be processed if this is passed through. If this line is processed, then its purpose is to restore the pointer to the current character on the BASIC program line being processed back into Register Pair HL

    1F05-1F20 - SCAN ROUTINE- "DATA"

    1F05-  ↳ DATA
    LD BC,0E3AH01 3A 0E
    This part of a Z-80 Trick. If this line is hit by passing through, then BC is simply changed and the next instruction at 1F07H is never executed
    1F07-1F08-  ↳ REM
    LD C,00H0E 00
    Load Register C with zero
    1F09-1F0A
    LD B,00H06 00
    Load Register B with zero, as the only terminator inside quotes or a REM is a NULL terminator

    The notes in the original ROM source code explain that when a quote is seen, the second terminator is traded, so in a DATA statement a colon inside quotations will have no effect.

    1F0B-  ↳ EXCHQT
    LD A,C79
    Top of a loop. Load Register A with the stop scan character in Register C
    1F0C
    LD C,B48
    Load Register C with the stop scan character in Register B
    1F0D
    LD B,A47
    Load Register B with the stop scan character in Register A
    1F0E-  ↳ REMER
    LD A,(HL)7E
    Top of a loop. Load Register A with the character at the location of the current BASIC program pointer in Register Pair HL
    1F0F
    OR AB7
    Check to see if the character at the location of the current BASIC program pointer in Register A is an END OF LINE terminator
    1F10
    RET ZC8
    Return if the character at the location of the current BASIC program pointer in Register A is an END OF LINE terminator, because an END OF LINE terminator will stop everything, including quote things or a REM
    1F11
    CP BB8
    Check to see if the character at the location of the current BASIC program pointer in Register A is the same as the stop scan character in Register B
    1F12
    RET ZC8
    Return if the character at the location of the current BASIC program pointer in Register A is the same as the stop scan character in Register B
    1F13
    INC HL23
    Bump the value of the current BASIC program pointer in Register Pair HL
    1F14-1F15
    CP 22HCP """FE 22
    Check to see if the character at the location of the current BASIC program pointer in Register A is a quote
    1F16-1F17
    JR Z,1F0BHJR Z,EXCHQT28 F3
    Loop back if the character at the location of the current BASIC program pointer in Register A is a quote, as we need to trade

    The notes in the original ROM source code say that when an IFtakes a false branch, it must find the appropriate ELSEto start execution at. "DATA" counts the number of IF's it sees so that the ELSEcode can matche ELSE's with IF's. This count is kept in Register D.

    1F18-1F19
    SUB 8FHSUB $IFD6 8F
    Check to see if the character at the location of the current BASIC program pointer in Register A is a IFtoken
    1F1A-1F1B
    JR NZ,1F0EHJR NZ,REMER20 F2
    Loop back to 1F0EH if the character at the location of the current BASIC program pointer in Register A isn't an IFtoken
    1F1C
    CP BB8
    Since a REM cannot be SMASHed, we only increment Register D if Register B does not equal zero. So . Check to see if the character at the location of the current BASIC program pointer in Register A is the same as the character in Register B
    1F1D
    ADC A,D8A
    As long as Regisister B is not zero, add the value in Register D to the value in Register A
    1F1E
    LD D,A57
    Load Register D with the value in Register A
    1F1F-1F20
    JR 1F0EHJR REMER18 ED
    Loop back to 1F0EH until scan is completed

    1F21-1F6B - LEVEL II BASIC LET ROUTINE- "LET"

    1F21-1F23-  ↳ LET
    CALL 260DHCALL PTRGETCD 0D 26
    Call the FIND ADDRESS OF VARIABLE routine at 260DH which searches the Variable List Table for a variable name which matches the name in the string pointed to in HL, and return the address of that variable in DE (and if there is no variable, it creates it, zeroes it, and returns THAT location)
    1F24-1F25
    RST 08H D5HRST 08H EQULTKCF
    Test if the variable name is followed by a =. Since the character at the location of the current BASIC program pointer in Register Pair HL must be an EQUAL sign (D5) so 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)
    1F26
    EX DE,HLEB
    Exchange the address of the variable in Register Pair DE with the value of the current BASIC program pointer in Register Pair HL

    A note in the original ROM source code explains that the following sets up "TEMP" for the FORcommand so when user-functions call REDINP, the "TEMP" doesn't get changed.

    1F27-1F29
    LD (40DFH),HLLD (TEMP),HL22 DF 40
    Save the addres of the variable in Register Pair HL.
    Note: 40DFH-40E0H is a common temporary storage area
    1F2A
    EX DE,HLEB
    Exchange the value of the current BASIC program pointer in Register Pair DE with the address of the variable in Register Pair HL
    1F2B-  ↳ REDINP
    PUSH DED5
    Save the address of the variable in Register Pair DE to the STACK
    1F2C
    RST 20HGETYPEE7
    We need to check the value of the current number type flag, so we call the TEST DATA MODE routine at RST 20H.
    NOTE:The RST 20H routine determines the type of the current value in ACCumulator and returns a combination of STATUS flags and unique numeric values in the A Register according to the data mode flag (40AFH). The results are returned as follows:
    • 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.
    1F2D
    PUSH AFF5
    Save the value in Register Pair AF to the STACK. A will be -1 for an integer, 0 for a string, 1 for single precision, and 5 for double precision
    1F2E-1F30-  ↳ LETCN3
    CALL 2337HCALL FRMEVLCD 37 23
    Go evaluate the expression at the location of the current BASIC program pointer in Register Pair HL and return with the result in ACCumulator
    1F31-  ↳ LETCON
    POP AFF1
    Get the variable type of the variable into Register A
    1F32-  ↳ LETCN2
    EX (SP),HLE3
    Exchange (SP) and HL so that the address of the variable is now held in Register Pair HL and the pointer to the current character in the BASIC program being processed goes to the top of the STACK
    1F33-1F34-  ↳ INPCOM
    ADD A,03HC6 03
    Top of a loop. Adjust the value in Register A so that it will hold the correct number type flag (i.e., A will be 2 for integer, 3 for string, 4 for single precision, and 8 for double precision)
    1F35-1F37
    CALL 2819HCALL DOCNVFCD 19 28
    Go to 2819H to convert the result in ACCumulator to the same number type for the variable held in Register A
    1F38-1F3A
    CALL 0A03HCALL VDFACSCD 03 0A
    Set Register Pair DE to equal the start position of good data in ACCumulator and call the GETYPE
    1F3B
    PUSH HLE5
    Save the address of the variable in Register Pair HL to the STACK
    1F3C-1F3D
    JR NZ,1F66HJR NZ,COPNUM20 28
    If the result is anumber, JUMP to force it and copy
    1F3E-1F40
    LD HL,(4121H)LD HL,(FACLO)2A 21 41
    Load Register Pair HL with the starting address of the string's VARPTR in ACCumulator
    1F41
    PUSH HLE5
    Save the VARPTR for the string result in Register Pair HL to the STACK
    1F42
    INC HL23
    Bump the value of the VARPTR for the string result in Register Pair HL
    1F43
    LD E,(HL)5E
    Load Register E with the LSB of the address for the string at the location of the VARPTR for the string in Register Pair HL
    1F44
    INC HL23
    Bump the value of the VARPTR for the string result in Register Pair HL
    1F45
    LD D,(HL)56
    Load Register D with the MSB of the address for the string at the location of the VARPTR for the string in Register Pair HL
    1F46-1F48
    LD HL,(40A4H)LD HL,(TXTTAB)2A A4 40
    Load Register Pair HL with the start of the BASIC program.
    NOTE: 40A4H-40A5H holds the starting address of BASIC program text also known as the PROGRAM STATEMENT TABLE (PST)
    1F49
    RST 18HCOMPARDF
    Now we need to compare the start of the BASIC program area (in HL) with the address of the string result in DE, so we call the COMPARE DE:HL routine, which numerically compares DE and HL. Will not work for signed integers (except positive ones). Uses the A-register only. The result of the comparison is returned in the status Register as: CARRY SET=HL<DE; NO CARRY=HL>DE; NZ=Unequal; Z=Equal)
    1F4A-1F4B
    JR NC,1F5AHJR NC,INBUFC30 0E
    If the address of the string result in Register Pair DE is less than the start of the BASIC program area in Register Pair HL, then the data is really in the buffer, so JUMP to do the copy
    1F4C-1F4E
    LD HL,(40A0H)LD HL,(STKTOP)2A A0 40
    We want to see if it points into string space, so load Register Pair HL with the start of the string space pointer. 40A0H-40A1H holds the start of string space pointer
    1F4F
    RST 18HCOMPARDF
    Now we need to compare the start of the string space pointer (in HL) with the address of the string result in DE, so we call the COMPARE DE:HL routine, which numerically compares DE and HL. Will not work for signed integers (except positive ones). Uses the A-register only. The result of the comparison is returned in the status Register as: CARRY SET=HL<DE; NO CARRY=HL>DE; NZ=Unequal; Z=Equal)
    1F50
    POP DED1
    Get the VARPTR for the string result from the STACK and put it in Register Pair DE
    1F51-1F52
    JR NC,1F62HJR NC,DNTCPY30 0F
    If NC is set, then we have a literal, which we do not want to copy, so JUMP if the address of the string result in Register Pair DE was less than the start of the string space pointer in Register Pair HL

    Next, we need to see if it is a variable by checking the descriptor. If it is not a variable, we do not want to copy it.

    1F53-1F55
    LD HL,(40F9H)LD HL,(VARTAB)2A F9 40
    Load Register Pair HL with the simple variables pointer.
    Note: 40F9H-40FAH holds the starting address of the simple variable storage area
    1F56
    RST 18HCOMPARDF
    Now we need to compare the address of the VARPTR for the string result in Register Pair DE with the simple variables pointer in Register Pair HL, so we call the COMPARE DE:HL routine, which numerically compares DE and HL. Will not work for signed integers (except positive ones). Uses the A-register only. The result of the comparison is returned in the status Register as: CARRY SET=HL<DE; NO CARRY=HL>DE; NZ=Unequal; Z=Equal)
    1F57-1F58
    JR NC,1F62HJR NC,DNTCPY30 09
    Jump if the address of the VARPTR for the string result in Register Pair DE is less than the simple variables pointer in Register Pair HL
    1F59-1F5AH
    LD A,D1H3E D1
    Z-80 Trick. If we are passing through to this location, then A will get changed and the next instruction (i.e., the POP DE) will not get executed
    1F5A-  ↳ INBUFC
    POP DED1
    Get the VARPTR for the string result from the STACK and put it in Register Pair DE
    1F5B-1F5D
    CALL 29F5HCALL FRETMSCD F5 29
    Free up the TEMP without freeing up any string space
    1F5E
    EX DE,HLEB
    Exchange the VARPTR for the string in Register Pair DE with the string's address in Register Pair HL, because the next instruction copies where HL points to
    1F5F-1F61
    CALL 2843HCALL STRCPYCD 43 28
    Copy the variable in the string space OR strings with data in the buffer into string space
    1F62-1F64-  ↳ DNTCPY
    CALL 29F5HCALL FRETMSCD F5 29
    Free up the TEMP without freeing up any string space
    1F65
    EX (SP),HLE3
    Exchange HL and (SP). HL will contain the place to store the VARPTR. (SP) will have nothing important
    1F66-1F68-  ↳ COPNUM
    CALL 09D3HCALL VMOVECD D3 09
    Copy a descriptor or value to its proper location in memory
    1F69
    POP DED1
    Get the top entry of the STACK. It is either garbage or information for a "FOR"
    1F6A
    POP HLE1
    Get the value of the current BASIC program pointer from the STACK and put it in Register Pair HL
    1F6B
    RETC9
    RETurn to CALLer

    1F6C-1FAE - LEVEL II BASIC ERROR ONROUTINE- "ONGOTO"

    1F6C-1F6D-  ↳ ONGOTO
    CP 9EHCP $ERRORFE 9E
    Check to see if the character at the location of the current BASIC program pointer in Register A is an ERRORtoken (since this is supposed to be ON ERROR
    1F6E-1F6F
    JR NZ,1F95HJR NZ,NTOERR20 25
    Jump down to 1F95H if the character at the location of the current BASIC program pointer in Register A isn't an ERRORtoken
    1F70
    RST 10HCHRGETD7
    If we are here, we have ON ERRORbut we do need to see what's next. Since we now need bump the value of 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.
    1F71-1F72
    RST 08H 8DHRST 08H $GOTOCF
    The next valid character at the location of the current BASIC program pointer in Register Pair HL must be a GOTOtoken (=8DH) so 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)
    1F73-1F75
    CALL 1E5AHCALL LINGETCD 5A 1E
    The next thing in the program line needs to be a line number, so convert the next constant into binary (with the result in DE). To do this, call the ASCII TO INTEGER routine at 1E5A which 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
    1F76
    LD A,D7A
    We need to test to see if the line number in DE is a zero. So . load Register A with the MSB of the line number in Register D
    1F77
    OR EB3
    Combine the LSB of the line number in Register E with the MSB of the line number in Register A to see if we have a zero as in ON ERROR GOTO 0
    1F78-1F79
    JR Z,1F83HJR Z,RESTRP28 09
    If we have an ON ERROR GOTO 0, we need to reset the error trap, so JUMP to 1F83H
    1F7A-1F7C
    CALL 1B2AHCALL FNDLN1CD 2A 1B
    Next we need to see if the line number actuall exists! Go find the location of the line number in Register Pair DE and return with the location of the line number in Register Pair BC
    1F7D
    1F7E
    LD D,B
    LD E,C50
    Put the pointer to the line number (held in BC) into DE
    1F7F
    POP HLE1
    Restore the location of the current position in the BASIC program being processed from the top of the STACK into Register Pair HL
    1F80-1F82
    JP NC,1ED9HJP NC,USERRD2 D9 1E
    If the NC FLAG is set, then the BASIC line number was not found, so we need to JUMP to display a ?UL ERROR
    1F83-  ↳ RESTRP
    EX DE,HLEB
    Exchange DE and HL, so that HL will now hold the pointer to the line number and DE will hold the location of the current BASIC program pointer
    1F84-1F86
    LD (40F0H),HLLD (ONELIN),HL22 F0 40
    Save the pointer to the line number held in in Register Pair HL.
    Note: 40F0H-40F1H is used by ON ERROR
    1F87
    EX DE,HLEB
    Exchange the value of the current BASIC program pointer in Register Pair DE with the location of the BASIC line in Register Pair HL
    1F88
    RET CD8
    RETURN out of this routine to the execution driver if it was not ON ERROR GOTO 0000
    1F89-1F8B
    LD A,(40F2H)LD A,(ONEFLG)3A F2 40
    Load Register A with the value of the error flag so we can see if we are in an ON . ERROR routine.
    Note: 40F2H holds Error flag
    1F8C
    OR AB7
    Check to see if the error flag is set
    1F8D
    RET ZC8
    If the Z FLAG is set, then we are not in an ON . ERROR routine and we can just RETURN to CALLer because we have already disabled the trapping
    1F8E-1F90
    LD A,(409AH)LD A,(ERRFLG)3A 9A 40
    Load Register A with the error code.
    Note: 409AH holds the RESUME flag
    1F91
    LD E,A5F
    Load Register E with the value of the error code in Register A
    1F92-1F94
    JP 19ABHJP ERRESMC3 AB 19
    Force the error to occur via a JUMP to the Level II error routine and display the error message for the error code in Register E

    1F95 - Still in the ONroutine- "NTOERR"

    We know it isn't ON ERROR. We now need to deal with the possibility that it was an ON n GOTOor ON n GOSUB.

    1F95-1F97-  ↳ NTOERR
    CALL 2B1CHCALL GETBYTCD 1C 2B
    First we need to get the nso go evaluate the expression at the location of the current BASIC program pointer and return with the result in Register Pair DE
    1F98
    LD A,(HL)7E
    Load Register A with the next character (which should be a token or terminator) at the location of the current BASIC program pointer in Register Pair HL
    1F99
    LD B,A47
    Save that character into Register B
    1F9A-1F9B
    CP 91HCP $GOSUBFE 91
    Check to see if the character at the location of the current BASIC program pointer in Register A is a GOSUBtoken
    1F9C-1F9D
    JR Z,1FA1HJR Z,ISGOSU28 03
    Skip the next 2 opcodes if the character at the location of current BASIC program pointer in Register A is a GOSUBtoken
    1F9E-1F9F
    RST 08H 8DHRST 08H $GOTOCF
    Now let's test to see if the next character is the token for a GOTO, 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)
    1FA0
    DEC HL2B
    Decrement the value of the current BASIC program pointer in Register Pair HL to point to the GOTOtoken
    1FA1-  ↳ ISGOSU
    LD C,E4B
    Load Register C with the character count of the expression after the ONtoken in Register E (i.e., the "n" from ON n GOTO
    1FA2-  ↳ LOOPON
    DEC C0D
    Top of a loop. Decrement the line number counter in Register C to test to make sure there are enough skips
    1FA3
    LD A,B78
    Load Register A with the token in Register B (which is either a GOSUBor GOTOtoken)
    1FA4-1FA6
    JP Z,1D60HJP Z,GONE2CA 60 1D
    If we are done (i.e., C hit ZERO) then Jump to 1D60H
    1FA7-1FA9
    CALL 1E5BHCALL LINGT2CD 5B 1E
    We need to slip over the line number so we GOSUB to 1E5BH to evaluate the line number at the location of the current BASIC program pointer and return with the result in Register Pair DE
    1FAA-1FAB
    CP 2CHCP ","FE 2C
    Check to see if the character at the location of the current BASIC program pointer in Register A is a COMMA. If not, it is the ent of the statement
    1FAC
    RET NZC0
    If the character at the location of the current BASIC program pointer in Register A isn't a ,then we must be at the end of the line
    1FAD-1FAE
    JR 1FA2HJR LOOPON18 F3
    Otherwise, keep grabbing and processing line numbers

    1FAF-1FF3 - LEVEL II BASIC RESUMEROUTINE- "RESUME"

    1FAF-1FB1-  ↳ RESUME
    LD DE,40F2HLD DE,ONEFLG11 F2 40
    Load Register Pair DE with the address of the Level II BASIC error flag.
    Note: 40F2H holds Error flag
    1FB2
    LD A,(DE)1A
    Load Register A with the error flag at the location of the memory pointer in Register Pair DE. It will be FF if there is an error, and a zero otherwise
    1FB3
    OR AB7
    Check for an error
    1FB4-1FB6
    JP Z,19A0HJP Z,REERRCA A0 19
    If we processed a RESUME but yet there was no error, then display an ?RW ERRORmessage
    1FB7
    INC A3C
    Clear the error flag in Register A
    1FB8-1FBA
    LD (409AH),ALD (ERRFLG),A32 9A 40
    Clear the error flag (by putting a 0 into 409AH) so that way if a BREAKis hit, it won't give an error
    1FBB
    LD (DE),A12
    Reset the error flag at the location of the memory pointer in Register Pair DE
    1FBC
    LD A,(HL)7E
    Load Register A with the character at the location of the current BASIC program pointer in Register Pair HL
    1FBD-1FBE
    CP 87HCP $NEXTFE 87
    Check to see if the character at the location of the current BASIC program pointer in Register A is a NEXTtoken
    1FBF-1FC0
    JR Z,1FCDHJR Z,RESNXT28 0C
    Jump down to 1FCDH if the character at the location of the current BASIC program pointer in Register A is a NEXTtoken (as in RESUME NEXT
    1FC1-1FC3
    CALL 1E5AHCALL LINGETCD 5A 1E
    Get the binary equivalent of the line number into DE by calling the ASCII TO INTEGER routine at 1E5A which 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
    1FC4
    RET NZC0
    Exit to the execution driver if there wasn't a line number at the location of the current BASIC program pointer
    1FC5
    1FC6
    LD A,D7A
    Prepare to test DE for Zero using the Z-80 trick of LD A,MSB and OR LSB
    1FC7-1FC9
    JP NZ,1EC5HJP NZ,GOTO2C2 C5 1E
    If the line number was NOT zero (i.e., it was RESUME nnnn), then JUMP there!
    1FCA
    INC A3C
    Prepare to set a non-Zero condition code so that it will indicate RESUME 0
    1FCB-1FCC
    JR 1FCFHJR RESTXT18 02
    Jump to the RESUME 0at 1FCFH

    1FCDH - Part of the RESUMEROUTINE- "RESNXT"

    1FCD-  ↳ RESNXT
    RST 10HCHRGETD7
    Since we now need bump the value of 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.
    1FCE
    RET NZC0
    Return if this is the end of the BASIC statement

    1FCF - This is the RESUME 0routine- "RESTXT"

    1FCF-1FD1-  ↳ RESTXT
    LD HL,(40EEH)LD HL,(ERRTXT)2A EE 40
    Get the value of the current BASIC program pointer and put it in Register Pair HL.
    Note: 40EEH-40EFH is used by RESUME
    1FD2
    EX DE,HLEB
    Load Register Pair DE with the value of the current BASIC program pointer in Register Pair HL
    1FD3-1FD5
    LD HL,(40EAH)LD HL,(ERRLIN)2A EA 40
    Load Register Pair HL with the current BASIC line number.
    Note: 40EAH-40EBH holds Line number with error
    1FD6-1FD8
    LD (40A2H),HLLD (CURLIN),HL22 A2 40
    Save the value of the current BASIC line number in Register Pair HL. This is where we will resume execution.
    Note: 40A2H-40A3H holds the current BASIC line number
    1FD9
    EX DE,HLEB
    Load Register Pair HL with the value of the current BASIC program pointer in Register Pair DE
    1FDA
    RET NZC0
    Return to the execution driver if this is RESUME
    1FDB
    LD A,(HL)7E
    Get the character at the location of the current BASIC program pointer in Register Pair HL and put it in Register A
    1FDC
    OR AB7
    Check the character at the location of the current BASIC program pointer in Register A to see if its an end of the BASIC line character
    1FDD-1FDE
    JR NZ,1FE3HJR NZ,NOTBGL20 04
    If A was ZERO, then the character would be :or an end of the BASIC line character. Jump forward to 1FE3H if the character at the location of the current BASIC program pointer in Register A isn't an end of the BASIC line character or a ":"
    1FDF
    INC HL23
    We need to skip the header, so we are going to move HL down 4 positions. Bump the value of the current BASIC program pointer in Register Pair HL to skip over the zero byte terminator
    1FE0
    INC HL23
    Bump the value of the current BASIC program pointer in Register Pair HL to skip over the LSB of the pointer to the next statement
    1FE1
    INC HL23
    Bump the value of the current BASIC program pointer in Register Pair HL to skip over the MSB of the pointer to the next statement
    1FE2
    INC HL23
    Bump the value of the current BASIC program pointer in Register Pair HL to skip over the LSB of the line number in binary for the line following the error
    1FE3-  ↳ NOTBGL
    INC HL23
    Bump the value of the current BASIC program pointer in Register Pair HL to skip over the MSB of the line number in binary for the line following the error so that it points to the start of the next statement
    1FE4
    LD A,D7A
    Load Register A with the MSB of the line number with the error in Register D
    1FE5
    AND EA3
    Combine the LSB of the line number with the error in Register E with the MSB of the line number with the error in Register A. This begins the test for the end of program marker
    1FE6
    INC A3C
    Bump the combined value of the line number with the error in Register A. If this was a direct statement, rather than a program line, this will set the Z FLAG
    1FE7-1FE9
    JP NZ,1F05HJP NZ,DATAC2 05 1F
    Jump to NEWSTT and stop if this was part of a program (and not a direct command)
    1FEA-1FEC
    LD A,(40DDH)LD A,(BFKLFL)3A DD 40
    Load Register A with the buffer kill flag
    1FED
    DEC A3D
    Check to see if the command mode flag in Register A is set
    1FEE-1FF0
    JP Z,1DBEHJP Z,STPENDCA BE 1D
    Jump to 1DBEH if Level II BASIC is in the command mode because you cant CONTinue/RESUME if you are in command mode
    1FF1-1FF3
    JP 1F05HJP DATAC3 05 1F
    Exit the routine by jumping to 1F05H to get the next statement

    1FF4H-2007 - LEVEL II BASIC ERROR ROUTINE - "ERRORS"

    This evaluates n for ERROR n

    1FF4-1FF6-  ↳ ERRORS
    CALL 2B1CHCALL GETBYTCD 1C 2B
    Get the error code parameter which was passed by a GOSUB to 2B1CH which evaluates the expression at the location of the current BASIC program pointer in Register Pair HL and return with the result in A
    1FF7
    RET NZC0
    Return if this isn't the end of the BASIC statement
    1FF8
    OR AB7
    Set up to check to see if the error number in Register A is equal to zero
    1FF9-1FFB
    JP Z,1E4AHJP Z,FCERRCA 4A 1E
    If the error code was zero, then that's an error in itself, so display an ?FC ERRORmessage
    1FFC
    DEC A3D
    Subtract one from the error code in Register A
    1FFD
    ADD A,A87
    Multiply the error code in Register A by two (so now A = 2(A-1))
    1FFE
    LD E,A5F
    Load Register E with 2(A-1)
    1FFF-2000
    CP 2DHCP LSTERRFE 2D
    Check to see if the error code in Register A is less than 45
    2001-2002
    JR C,2005HJR C,GOERR38 02
    Jump forward 1 instruction (i.e., skip the line that sets up for an error of ?UE ERROR) if the error code (in Register A) is in range (i.e., less than 45)

    2003 - UE ERROR entry point- "GOERR"

    2003-2004-  ↳ GOERR
    LD E,26HLD E,ERRUE1E 26
    Load Register E with the ERROR code for a ?UE ERROR
    2005-2007
    JP 19A2HJP ERRORC3 A2 19
    Go to the Level II BASIC error routine and display the appropriate error message

    2008-2038 - LEVEL II BASIC AUTOROUTINE- "AUTO"

    According to the original ROM source code, the AUTO [beginning line,[increment]] command is used to automatically generate line number for lines to be inserted. The beginning line is used to specify the initial line (and if omitted, defaults to 10) and the increment is used to specify the increment used to generate the next line number. If only a comma is used after the beginning line, the old increment is used.

    On entry, Z will be set if there was nothing following the command.

    2008-200A-  ↳ AUTO
    LD DE,000AH11 0A 00
    Load DE with a default starting line number of ten
    200B
    PUSH DED5
    Save the default STACK line number in DE to the STACK
    200C-200D
    JR Z,2025HJR Z,SNGAUT28 17
    Jump down to 2025H if this is the end of the AUTOstatement (meaning that no parameters were provided). This will default to AUTO 10,10
    200E-2010
    CALL 1E4FHCALL LINSPCCD 4F 1E
    If we are here, then there were parameters after the AUTOstatement so we need to GOSUB to 1E4FH to evaluate the line number at the current location of the BASIC program pointer in HL and return with the result in DE. The Z flag will be set if that was the end of the command
    2011
    EX DE,HLEB
    Since there is no such Z-80 command as EX (SP),DEwe now need to do some swapping to get DE into (SP). First, exchange the value of the line number in DE with the value of the current BASIC program pointer in HL
    2012
    EX (SP),HLE3
    Next, exchange the default line number (to the STACK from the above push of DE) with the line number that the user provided (in HL)

    At this point, DE points to the current character in the BASIC line being processed, "10" is in HL, and the initial number is on the STACK.

    2013-2014
    JR Z,2026HJR Z,SNGAU128 11
    Jump down to 2026H if this is the end of the AUTOstatement (meaning that only 1 parameter was provided). This will default to 10
    2015
    EX DE,HLEB
    Load HL with the value of the current BASIC program pointer in DE
    2016-2017
    RST 08H ⇒ 2CSYNCHK ,CF 2C
    If we are here, then we have an AUTOcommand with one parameter (starting line number) and we have not finished with the command. Since the only valid addition to that would be a trailing ,, we need to test the current BASIC program pointer in HL for a COMMA (=2CH), by calling the COMPARE SYMBOL at RST 08H.

    NOTE:The RST 08H routine 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 the next execution address (i.e, the RST 08H instruction + 2) with the next symbol in the A Register 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).
    2018
    EX DE,HLEB
    If we are here, then it matched (a ?SN ERRORwould have occurred otherwise) so load DE with the value of the current BASIC program pointer in HL
    2019-201B
    LD HL,(40E4H)LD HL,(AUTINC)2A E4 40
    Load HL with the last provided AUTOincrement value.
    Note: 40E4H-40E5H holds AUTOincrement
    201C
    EX DE,HLEB
    Exchange the value of the current BASIC program pointer in DE with the last AUTOincrement value in HL
    201D-201E
    JR Z,2025HJR Z,SNGAUT28 06
    Jump to 2025H if this is the end of the BASIC statement (meaning, we got AUTO nn,but no further parameter). This will then use the last provided increment
    201F-2021
    CALL 1E5AHCALL LINGETCD 5A 1E
    If we are here, there was a second parameter so we GOSUB to 1E5AH to get the second parameter as 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
    2022-2024
    JP NZ,1997HJP NZ,SNERRC2 97 19
    If that prior GOSUB found a non-numeric character it aborted with a NZ, so if there is a NZ, jump to 1997H to show a ?SN ERROR
    2025-  ↳ SNGAUT
    EX DE,HLEB
    Exchange the AUTOincrement number in DE with the value of the current BASIC program pointer in HL. HL should now hold the AUTO increment
    2026-  ↳ SNGAU1
    LD A,H7C
    Prepare to test to see if it is zero. First, load Register A with the MSB of the AUTOincrement number in Register H. (This is step 1 of a test to see if HL is zero)
    2027
    OR LB5
    Combine the LSB of the AUTOincrement number in Register L with the MSB of the AUTOincrement number in Register A. (This is step 2 of a test to see if HL is zero)
    2028-202A
    JP Z,1E4AHJP Z,FCERRCA 4A 1E
    That all tested the value of HL to see if it was zero. If it was, jupm to 1E4AH to show a ?FC ERRORmessage
    202B-202D
    LD (40E4H),HLLD (AUTINC),HL22 E4 40
    Save the value of the AUTOincrement number in HL.
    Note: 40E4H-40E5H holds AUTOincrement
    202E-2030
    LD (40E1H),ALD (AUTFLG),A32 E1 40
    Set the AUTOflag to non-zero. We know A is non-zero because we would have jumped 2 instruction ago if it was
    2031
    POP HLE1
    Get the starting line number from the STACK and put it in HL
    2032-2034
    LD (40E2H),HLLD (AUTLIN),HL22 E2 40
    Save the line number in HL as the next AUTO line number.
    Note: 40E2H-40E3H holds Current BASIC line number
    2035
    POP BCC1
    Clean up the return address of NEWSTT from the STACK
    2036-2038
    JP 1A33HJP MAINC3 33 1A
    Jump to the Level II BASIC command mode

    2039-2066 - LEVEL II BASIC IFROUTINE- "IF"

    2039-203B-  ↳ IF
    CALL 2337HCALL FRMEVLCD 37 23
    GOSUB to 2337H to evaluate the BASIC expression pointed to by HL and return with the result in ACCumulator
    203C
    LD A,(HL)7E
    Load Register A with the character at the location of the current BASIC program pointer in HL
    203D-203E
    CP 2CHFE 2C
    Check to see if the character at the location of the current BASIC program pointer in Register A is a ,. If they match, the Z FLAG is set, and otherwise the NZ FLAG is set. If A < the checked value, then the C FLAG is set. If A >= the checked value, the NC FLAG is set.
    203F-2041
    CALL Z,1D78HJP Z,CHRGTRCC 78 1D
    If the character at the location of the current BASIC program pointer in Register A is a ,then go bump the value of the current BASIC program pointer in HL until it points to the next character
    2042-2043
    CP 0CAHFE CA
    Check to see if the character at the location of the current BASIC program pointer in Register A is a THENtoken
    . If they match, the Z FLAG is set, and otherwise the NZ FLAG is set. If A < the checked value, then the C FLAG is set. If A >= the checked value, the NC FLAG is set.
    2044-2046
    CALL Z,1D78HJP Z,CHRGTRCC 78 1D
    If Z flag is set, the character at the location of the current BASIC program pointer in Register A is a THENtoken, so we need to bump the value of the current BASIC program pointer until it points to the next character
    2047
    DEC HL2B
    Decrement the value of the current BASIC program pointer in HL so that we are still positioned at the THENtoken
    2048-  ↳ OKGOTO
    PUSH HLE5
    Save the value of the current BASIC program pointer to the STACK
    2049-204B
    CALL 0994HCALL VSIGNCD 94 09
    GOSUB to 0994H to see if the expression after the IFtoken was true or false
    204C
    POP HLE1
    Restore the address of the current position in the current statement (from the STACK) into HL
    204D-204E
    JR Z,2056HJR Z,FALSIF28 07
    Jump down to 2056H if the expression was false
    204F-  ↳ DOCOND
    RST 10HCHRGETD7
    We need the next character in the BASIC program so 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.
    2050-2052
    JP C,1EC2HJP C,GOTODA C2 1E
    Jump to the GOTOroutine if the character at the location of the current BASIC program pointer in HL is numeric
    2053-2055
    JP 1D5FHJP GONEC3 5F 1D
    Jump to the execution driver at 1D5FH to evaluate the rest of the statement string

    2056H - LEVEL II BASIC ELSEROUTINE- "FALSIF"

    2056-2057-  ↳ FALSIF
    LD D,01H16 01
    Load Register D with the scan counter as we will need to count the number of ELSEs. The "DATA" routine increments this counter ever time it finds an IFstatement
    2058-205A-  ↳ SKPMRF
    CALL 1F05HCALL DATACD 05 1F
    Go scan the BASIC statement to skip a statement. A ":" is placed in front of each ELSE so that the DATA routine will stop before an ELSE clause
    205B
    OR AB7
    Since a LD command does not affect the flags, OR A is commonly used to set the flags based on A. This is to check to see if this is the end of the BASIC line
    205C
    RET ZC8
    If this is the end of the BASIC statement then there isn't going to be an ELSE, so RETurn to CALLer
    205D
    RST 10HCHRGETD7
    We need the next character in the BASIC program so 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.
    205E-205F
    CP 95HCP $ELSEFE 95
    Check to see if the character at the location of the current BASIC program pointer in Register A is an ELSEtoken. If they match, the Z FLAG is set, and otherwise the NZ FLAG is set. If A < the checked value, then the C FLAG is set. If A >= the checked value, the NC FLAG is set.
    2060-2061
    JR NZ,2058HJR NZ,SKPMRF20 F6
    If this wasn't an ELSE then we are still in the THEN clause, so loop back to 2058H until an ELSEtoken is found
    2062
    DEC D15
    Decrement the number of ELSE statements that have been found
    2063-2064
    JR NZ,2058HJR NZ,SKPMRF20 F3
    If that DEC didn't hit ZERO, then we haven't found them all, so loop back to 2058H until all of the ELSEtokens have been found
    2065-2066
    JR 204FHJR DOCOND18 E8
    If we are here, then we have found the right ELSE statement! Jump to 204FH to evaluate the rest of the expression

    2067-206E - LEVEL II BASIC LPRINT ROUTINE- "LPRINT"

    2067-2068-  ↳ LPRINT
    LD A,01H3E 01
    Load Register A with the output device code for the printer
    2069-206B
    LD (409CH),ALD (PRTFLG),A32 9C 40
    Save the value in Register A as the current output device type number (-1=cassette, 0=video; or 1=printer)
    206C
    Skip a few instructions and pick up at 207CH.

    206F-2177 - LEVEL II BASIC PRINT@ ROUTINE- "PRINT"

    206F-2071-  ↳ PRINT
    CALL 41CAHCALL FILGETCD CA 41
    Do a DOS Vector call in case this is to go to a disk file
    2072-2073
    CP '#'
    Check the character at the location of the current BASIC program pointer in register A to see if it's an #.
    2074-2075
    Jump to 207CH if it is not a #.
    2076-2078
    GOSUB to 0284H to [Turn the tape on, no header].
    2079-207B
    LD (409CH),A
    Load the memory location at 409CH with A, to route output to cassette.
    207C
    DEC HL
    Decrement HL to back up.
    207D
    RST 10H
    We need to check for the end of the statement as the next character in the BASIC program so call the EXAMINE NEXT SYMBOL routine at RST 10H.
    NOTE:
    • The RST 10H routine loads the next character from the string pointed to by the HL register into the A-register and clears the CARRY flag if it is alphabetic, or sets it if is alphanumeric.
    • Blanks and control codes 09H and 0BH 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.
    207E-2080
    GOSUB to 20FEH if it was the end of statement, so we can process a new line.
    2081-2083
    And if it was the end of the statement then JUMP to 2169H to finish up.
    2084
    OR 20H
    With this MASK, we are getting readh to check to see if it was a @.
    2086
    CP 60H
    Compare against a "'" (which is actually a @on the TRS-80).
    2088-2089
    JUMP to 20A5H if it is not a @.
    208A
    GOSUB to 2B01H to evaluate the PRINT@expression (which is at the location of the current BASIC program pointer in HL and return with the result in DE).
    208D
    CP 04
    Check to see if the location in DE is greater than 1023H. A CP will return Z if there is a match against Register A, and NZ if not a match against Register A.
    208F
    If that check resulted in no CARRY flag, jump to 1E4AH to show a ?FC error.
    2092
    PUSH HL
    Push the current code string address (held in HL) to the stack.
    2093
    Load HL with the start of the display area.
    2096
    ADD HL,DE
    Add DE to HL so that HL now will be the start of the diplay area PLUS the PRINT@offset position.
    2097
    LD (4020H),HL
    Store the cursor position of the screen address matching the PRINT@position into the display DCB held in memory location 4020H.
    • 4020H-4021H: Holds the video memory address of the current cursor position.
    209A
    LD A,E
    E is the current position within the line.
    209B
    AND 3FH
    Make it not exceed 63 and then save it ....
    209D
    LD (40A6H),A
    ... into the current cursor offset (which is held in 40A6H).
    20A0
    POP HL
    Restore the code string address to HL
    20A2
    RST 08H
    2C
    At this point we have a PRINT@nnnnso the only valid next character is a ,so we need to call RST 08H to check against 2C, the code for comma.

    NOTE:The RST 08H routine 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 the next execution address (i.e, the RST 08H instruction + 2) with the next symbol in the A Register 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).
    20A3-20A4
    JUMP to 206CH to [NEXT ITEM].
    20A5
    LD A,(HL)
    2C
    Put the next token (held in the memory location pointed to by HL) into A.
    20A6-20A7
    CP 0BFH
    Compare the device flag.
    • NOTE: A CPwill return Z if there is a match against Register A, and NZ if not a match against Register A.)
    20A8-20AA
    ... and return to the execution driver.
    20AB-20AC
    CP BCH
    Test for a TABtoken.

    Notes:

    • BCH is a TABin ASCII
    • A CP will return Z if there is a match against Register A, and NZ if not a match against Register A.
    20AD-20AF
    and if it is a TABtoken (meaning the command is PRINT TAB, jump down to 2137H.
    20B0
    PUSH HL
    Continuing on a PRINT#, save the current position in the input stream into the stack.
    20B1-20B2
    CP 2CH
    Test for a ,.

    Notes:

    • 2CH is a ,in ASCII
    • A CP will return Z if there is a match against Register A, and NZ if not a match against Register A.
    20B3-20B4
    If there is a comma, jump down to 2108H to get the next item.
    20B5-20B6
    CP 3BH
    Since its not a comma, next we need to check for a ;.

    Notes:

    • 3BH is a ;in ASCII
    • A CP will return Z if there is a match against Register A, and NZ if not a match against Register A.
    20B7-20B8
    If it is a ;, jump down to 2117H to process.
    20B9
    CALL 2337HCALL FRMEVLCD 37 23
    Get the address or value of the next item to be printed by calling the formula evaluator at 2337H
    20BC
    EX (SP),HL
    Swap (SP) for HL to restore the position.
    20BD
    RST 20HGETYPEE7
    Check its data type with a call to RST 20H.
    NOTE:The RST 20H routine determines the type of the current value in ACCumulator and returns a combination of STATUS flags and unique numeric values in the A Register according to the data mode flag (40AFH). The results are returned as follows:
    • 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.
    20BE
    JR Z,20F2HJR Z,STRDON28 32
    If it is a string, jump down to 20F2H to handle that case
    20C0
    CALL 0FBDHCALL FOUTCD BD 0F
    GOSUB to the routine at 0FBDH to convert the number to an ASCII string and move to the print buffer
    20C3
    CALL 2865HCALL STRLITCD 65 28
    GOSUB to the routine at 2865H to build a literal string pool entry for the ASCII number
    20C6
    CALL 41CDHCALL EXDSKLCD CD 41
    GOSUB to DOS to see if DOS wants to do anything here
    20C9
    LD HL,(4121H)LD HL,(FACLO)2A 21 41
    Put the address of the pointer to the current print string into HL
    20CC
    LD A,(409CH)LD A,(PRTFLG)3A 9C 40
    Get the output device type flag and put it into A.
    • Note: 409CH holds the current output device type number (-1=cassette, 0=video; or 1=printer)
    20CF
    OR AB7
    Since a LD command does not affect the flags, OR A is commonly used to set the flags based on A. This is to test the output device type flag
    20D0
    JP M,20E9HJP M,LINCH2FA E9 20
    If we writing to a cassette (i.e., PRINT#) jump to 20E9H
    20D3
    JR Z,20DDHJP Z,ISTTY28 08
    If, instead, we do NOT have a LPRINT, jump down to 20DDH
    20D5-20D7
    LD A,(409BH)LD A,(LPTPOS)3A 9B 40
    If we are here, we have a LPRINTso load A with the current carriage position.
    Note: 409BH holds the printer carriage position
    20D8-  ↳ LPTCD2
    ADD A,(HL)86
    Add the length of the string to be sent to the printer at the location of the memory pointer in HL to the current carriage position in Register A
    20D9-20DA
    CP 84HCP LPTLENFE 84
    Check to see if the adjusted length in Register A is greater than 132. If they match, the Z FLAG is set, and otherwise the NZ FLAG is set. If A < the checked value, then the C FLAG is set. If A >= the checked value, the NC FLAG is set.
    20DB-20DC
    JR 20E6HJR LINCHK18 09
    Jump forward to 20E6H

    20DDH - LEVEL II BASIC PRINT@ ROUTINE - Jumped here if we are sure we are using the display- "ISTTY"

    20DD-20DF-  ↳ ISTTY
    LD A,(409DH)LD A,(LINLEN)3A 9D 40
    Load Register A with the video line size.
    Note: 409DH holds the size of line on the video display
    20E0
    LD B,A47
    Load Register B with the video line size in Register A
    20E1-20E3
    LD A,(40A6H)LD A,(TTYPOS)3A A6 40
    Load Register A with the current video line position.
    Note: 40A6H holds the current cursor line position
    20E4
    ADD A,(HL)86
    Add the length of the string to be sent to the video display at the location of the memory pointer in HL to the value of the current video line position in Register A
    20E5-  ↳ LINPT3
    CP BB8
    Check to see if the length in Register A is greater than the video line length. If they match, the Z FLAG is set, and otherwise the NZ FLAG is set. If A < the checked value, then the C FLAG is set. If A >= the checked value, the NC FLAG is set.
    20E6-20E8-  ↳ LINCHK
    CALL NC,20FEHCALL NC,CRDOD4 FE 20
    If NC is set, the new line will overflow the buffer so send a carriage return to the current output device
    20E9-20EB-  ↳ LINCH2
    CALL 28AAHCALL STRPRTCD AA 28
    Go send the string to the current output device
    20EC-20ED
    LD A,20H3E 20
    Load Register A with a SPACE
    20EE-20F0
    CALL 032AHCALL OUTDOCD 2A 03
    Go send the space in Register A to the current output device
    20F1
    OR AB7
    Since a LD command does not affect the flags, OR A is commonly used to set the flags based on A. This is to check to see if Register A is equal to zero
    20F2-20F4-  ↳ STRDON
    CALL Z,28AAHCALL Z,STRPRTCC AA 28
    If necessary go send the string to the current output device
    20F5
    POP HLE1
    Restore the current code string to HL
    20F6-20F8
    Loop to 207CH until an end of statement code is found. Going all the way back to 207CH will also test for a PRINT @, thus permitting a PRINT @to appear other than first
    20F9-20FB-  ↳ CRDONZ
    LD A,(40A6H)LD A,(TTYPOS)3A A6 40
    Load Register A with the number of characters printed on the current line.
    Note: 40A6H holds the current cursor line position
    20FC
    OR AB7
    Since a LD command does not affect the flags, OR A is commonly used to set the flags based on A. This is to check to see if any characters have been printed on the current line
    20FD
    RET ZC8
    Return out of this routine if we are at the start of a line

    20FE - This routine outputs a carriage return (0DH) to a device determined by flag stored at (409CH)- "CRDO"

    NOTE: This routine may be CALLed at 20F9H, in which case it will not perform the above action if the video display cursor is already positioned at the beginning of a line, as determined by checking the contents of the cursor position flag at 40A6H (if zero, cursor is at start of line). This routine CALLs the routine at 032AH and also CALLs a Disk BASIC link at 41D0H.

    20FE-20FF-  ↳ CRDO
    LD A,0DH3E 0D
    Otherwise, we need to skip to the next line so load Register A with a carriage return
    2100-2102
    CALL 032AHCALL OUTDOCD 2A 03
    Go send the carriage return in Register A to the current output device
    2103-2105-  ↳ CRFIN
    CALL 41D0HCALL EXDSCRCD D0 41
    GOSUB to the DOS routine at 41D0H to deal with screen wrap
    2106
    XOR AAF
    Zero Register A and the carry flags
    2107
    RETC9
    Return back to the calling routine

    2108 - This is the jump point for a continuation of the PRINT#code- "COMPRT".

    2108-210A-  ↳ COMPRT
    CALL 41D3HCALL EXPDOSCD D3 41
    Jump to DOS to see if DOS wants to modify the behavior
    210B-210D
    LD A,(409CH)LD A,(PRTFLG)3A 9C 40
    Load Register A with the value of the current output device flag.
    • Note: 409CH holds the current output device type number (-1=cassette, 0=video; or 1=printer)
    210E
    OR AB7
    Test the value of the current output device flag in Register A.
    • M will be set if the value in A is negative.
    • P will be set if the value in A is positive or zero.
    210F-2111
    JP P,2119HJP P,NTCASF2 19 21
    Jump down a few instructions to 2119H if the printer or the video display is the current output device
    2112-2113
    LD A,2CH3E 2C
    So now that we know the current device is cassette, we will load Register A with a ,
    2114-2116
    CALL 032AHCALL OUTDOCD 2A 03
    Send the ,in Register A to current output device. In this case, it is the cassette recorder because we otherwise would have jumped away at 210FH
    2117-2118
    JR 2164HCALL NOTABR18 4B
    Jump forward to 2164H to fetch the next character from the code string
    2119-211A-  ↳ NTCAS
    JR Z,2123HJR Z,ISCTTY28 08
    We landed here from 210FH knowing that the current output device flag was either video or printer. Now we need to break that down too, so if the device flag is the video display, jump down to 2123H
    211B-211D-  ↳ LPTCD3
    LD A,(409BH)LD A,(LPTPOS)3A 9B 40
    We're here because the output device is a printer. First, load Register A with the current carriage position.
    Note: 409BH holds the printer carriage position
    211E-211F-  ↳ NLPPOS
    CP 70HCP NLPPOSFE 70
    Check to see if the current carriage position in Register A is greater than 112. If they match, the Z FLAG is set, and otherwise the NZ FLAG is set. If A < the checked value, then the C FLAG is set. If A >= the checked value, the NC FLAG is set.
    2120-2122
    JP 212BHJP CHKCOMC3 2B 21
    Jump forward a few instructions to 212BH
    2123-2125-  ↳ ISCTTY
    LD A,(409EH)LD A,(CLMLST)3A 9E 40
    We were jumped to this point because the output device is the video display. So load Register A with the video line length.
    Note: 409EH holds the size of line on the printer
    2126
    LD B,A47
    Load Register B with the video line length in Register A
    2127-2129
    LD A,(40A6H)LD A,(TTYPOS)3A A6 40
    Load Register A with the current video line position.
    Note: 40A6H holds the current cursor line position
    212A-  ↳ NCMPOS
    CP BB8
    Test to see if there is room on this line by checking to see if the current video line position in Register A is equal to the video line length in Register B. If they match, the Z FLAG is set, and otherwise the NZ FLAG is set. If A < the checked value, then the C FLAG is set. If A >= the checked value, the NC FLAG is set.
    212B-212D-  ↳ CHKCOM
    CALL NC,20FEHCALL NC,CRDOD4 FE 20
    If the NC is set, there is no room on the current line, so we need to GOSUB to 20FEH to print a carriage return on the current output device
    212E-212F
    JR NC,2164HJR NC,NOTABR30 34
    If we are beyond the last comma field, quit via a JUMP to 2164H
    2130-2131-  ↳ MORCOM
    SUB 10HSUB CLMWIDD6 10
    Calculate A MOD CLDMWID to see if there are at least 16 spaces left on the current line for the current output device
    2132-2133
    JR NC,2130HJR NC,MORCOM30 FC
    Loop back until there are at least 16 spaces left on the current line
    2134
    CPL2F
    Figure the number of spaces to be sent to the current output device so that we have an even CLMWID
    2135-2136
    JR 215AHJR NC,ASPA218 23
    Jump to 215AH to print Register A + 1 number of spaces/blanks

    2137 - TAB logic- "TABER"

    This routine is the TAB function for video or printer (determined by flag at 409CH). On entry: E Register contains desired TAB position, HL points to start of message to be displayed (or zero byte if no message). This routine does extensive string processing and may not be the most efficient method of achieving the desired result, particularly if it is desired only to tab over a number of spaces. Also, this routine CALLs several Disk BASIC links which may have to be "plugged". 2169 - Reset device type flag at 409CH to zero (output to video display), also turns off cassette drive if necessary. CALLs Disk BASIC link at 41BEH prior to return.

    2137-2139-  ↳ TABER
    CALL 2B1BHCALL GTBYTCCD 1B 2B
    GOSUB forward to 2B1BH to evaluate the tab number at the location of the current BASIC program pointer in HL and return with the result in Register A
    213A
    AND 7FH
    The results are in A so mask the tab number in register A so that it doesn’t exceed 127.
    213C
    LD E,A5F
    Load Register E with the value of the tab number from Register A
    213D-213E
    RST 08H ⇒ 29SNCHK ")"CF 29
    At this point, we have a TAB(nn, so the next character needs to be a ). Since the character at the location of the current BASIC program pointer in HL must be a ")", call the COMPARE SYMBOL at RST 08H.

    NOTE:The RST 08H routine 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 the next execution address (i.e, the RST 08H instruction + 2) with the next symbol in the A Register 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).
    213F
    DEC HL2B
    Decrement the value of the current BASIC program pointer in HL so that it points to the (

    NOTE 1: The cursor position cannot be moved backward by this procedure. If n is not greater than the current cursor position on the line, no change will occur.

    NOTE 2: To locate the cursor at a given position on the screen (the function of the PRINT@ command in BASIC), the simplest procedure is to modify the cursor position bytes, which are located at 4020H-4021H. The address contained in these memory cells is that of the position in video memory (3C00H-3FFFH) at which the (abstract) cursor resides. This cursor position controls subsequent printing via the subroutine at 28A7H

    DISK SYSTEM CAUTION: The subroutine at 213FH has three exits to DISK BASIC, with RAM transfer points at 41BEH, 41C1H, and 41D3H. To use this routine safely, either be certain that DISK BASIC is in place, or have your assembly language program fill locations 41BEH, 41C1H, and 41D3H with RET's (C9H), before calling the routine.

    2140
    PUSH HLE5
    Save the value of the current BASIC program pointer in HL to the STACK
    2141
    CALL 41D3HCALL EXPDOSCD D3 41
    Jump to DOS to see if DOS wants to modify the behavior
    2144-2146
    LD A,(409CH)LD A,(PRTFLG)3A 9C 40
    Load Register A with the value of the current output device flag.
    • Note: 409CH holds the current output device type number (-1=cassette, 0=video; or 1=printer)
    2147
    OR AB7
    Test the value of the current output device flag in Register A. A NZ means line printer
    2148-214A
    JP M,1E4AHJP M,FCERRFA 4A 1E
    Since you cannot send a tab to the cassette, display a ?FC ERRORmessage if the current output device is the cassette recorder
    214B-214D
    JP Z,2153HJP Z,TTYISTCA 53 21
    Jump forward a few instructions to 2153H if the current output device is the video display
    214E-2150
    LD A,(409BH)LD A,(LPTPOS)3A 9B 40
    Since we have already jumped away if the current output device is to cassette or screen, if we are here we know that this is to go to the printer, so load Register A with the current carriage position.
    Note: 409BH holds the printer carriage position
    2151-2152
    JR 2156HJR DOSIZT18 03
    Jump over the next instruction (which is a video instruction)

    2153H - Displaying to Screen - "TTYIST"

    2153-2155-  ↳ TTYIST
    LD A,(40A6H)LD A,(TTYPOS)3A A6 40
    Load Register A with the current video line position.
    Note: 40A6H holds the current cursor line position
    2156-  ↳ DOSIZT
    CPL2F
    When we land here, A holds either the current carriage position (printer) or the current line position (video). Regardless, complement (make negative) that value in preparation to determine how many spaces to print (which would be Register E - Register A)
    2157
    ADD A,E83
    Add the tab number in Register B to the adjusted line position in Register A so that A=-current position + tab
    2158-2159
    JR NC,2164HJR NC,NOTABR30 0A
    If we we have a negative number of spaces to print, then we aren't going to print any, so jump forward a few instructions to 2164H to skip over the actual TAB effectuation routine
    215A-  ↳ ASPA2
    INC A3C
    Bump the number of spaces to be printed in Register A
    215B-  ↳ ASPAC
    LD B,A47
    Load Register B with the number of spaces to be printed in Register A
    215C-215D
    LD A,20H3E 20
    Load Register A with a SPACE
    215E-2160-  ↳ REPOUT
    CALL 032AHCALL OUTDOCD 2A 03
    Send the space in Register A to the current output device
    2161
    DEC B05
    Decrement the number of SPACE's to be displayed (counter in Register B)
    2162-2163
    JR NZ,215EHJR NZ,REPOUT20 FA
    Loop back to 215EH until all of the spaces have been displayed/printed
    2164-  ↳ NOTABR
    POP HLE1
    Restore the position in of the current BASIC program pointer (from the STACK) into HL
    2164-  ↳ NOTABR
    POP HLE1
    Restore the position in of the current BASIC program pointer (from the STACK) into HL
    2165
    RST 10HCHRGETD7
    We need the next character in the BASIC program so 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.
    2166
    Jump back to 2081H to process the rest of the PRINT TAB statement.

    2169 - This routine resets the device type flag at 409CH to zero (output to video display), also turns off cassette drive if necessary. CALLs Disk BASIC link at 41BEH prior to return- "FINPRT".

    2169-216B-  ↳ FINPRT
    LD A,(409CH)LD A,(PRTFLG)3A 9C 40
    If we land here, we need to turn off the cassette and reset the current output device to be video. To do this we first load Register A with the current output device flag.
    • Note: 409CH holds the current output device type number (-1=cassette, 0=video; or 1=printer)
    216C
    OR AB7
    Test the value of the current output device flag in Register A. If the M flag is set, A was negative
    216D-216F
    CALL M,01F8HCALL M,CTOFFFC F8 01
    If the current output device flag is the cassette recorder, GOSUB to 01F8H to turn it off
    2170
    XOR AAF
    Clear Register A (which will set A to 0) and clear the status flags
    2171-2173
    LD (409CH),ALD (PRTFLG),A32 9C 40
    Save the value in Register A (which is 0) as the current value of the output device flag.
    • Note: 409CH holds the current output device type number (-1=cassette, 0=video; or 1=printer)
    2174
    CALL 41BEHCALL FINDRTCD BE 41
    GOSUB to DOS to see if DOS wants to do anything here
    2177
    RETC9
    RETurn to CALLer

    2178-217E - MESSAGE STORAGE LOCATION FOR REDO MESSAGE- - "TRYAGN"

    2178-217E-  ↳ TRYAGN
    "?REDO" + CRLF + 00H
    The ?REDO message is stored here

    217F-2285 - LEVEL II BASIC INPUT AND READ ROUTINES- "TRMNOK"

    This is a multi-purpose processing routine. We can wind up here because DATA was typed in or DATA statements were improperly formatted. We can also wind up here where we want an INPUT to start again. If we are here because of a READ, throw a ?SN ERROR at the data line.

    217F-2181-  ↳ TRMNOK
    LD A,(40DEH)LD A,(FLGINP)3A DE 40
    Load Register A with the READ flag to help us try to figure out if this was a READ or an INPUT.
    Note: 40DEH holds READ flag
    2182
    OR AB7
    Check to see if the read flag is set. If Z FLAG then it was INPUT
    2183-2185
    JP NZ,1991HJP NZ.DATSNEC2 91 19
    If the read flag is set, go give a ?SN ERROR
    2186-2188
    LD A,(40A9H)LD A,(CASFLG)3A A9 40
    Now we know the read flag is NOT set, so we need to load Register A with the INPUT type flag.
    Note: 40A9H holds Cassette input flag
    2189
    OR AB7
    Check to see if the cassette recorder is the current input device, as we will need to give a FILE DATA error
    218A-218B
    LD E,2AH1E 2A
    Load Register E with the ?FD ERRORcode
    218C-218E
    JP Z,19A2HJP Z,ERRORCA A2 19
    If the current input device is the cassette recorder, go give a ?FD ERROR
    218F
    POP BCC1
    Clean up the STACK (discards the pointer into the variable list)
    2190-2192-  ↳ RDOINP
    LD HL,2178HLD HL,TRYAGN21 78 21
    Load HL with the starting address of the ?REDOmessage
    2193-2195
    CALL 28A7HCALL STROUTCD A7 28
    Display the "?REDO FROM START" message
    2196-2198
    LD HL,(40E6H)LD HL,(SAVTXT)2A E6 40
    Get the value of the current BASIC program pointer in HL.
    Note: 40E6H-40E7H is a common temporary storage location
    2199
    RETC9
    Return out of this routine back to NEWSTT of the INPUT statement

    219A - INPUT logic- "INPUT"

    219A-219C-  ↳ INPUT
    CALL 2828HCALL ERRDIRCD 28 28
    Check to see if there is an illegal direct in the input statement
    219D
    LD A,(HL)7E
    Load Register A with the character at the location of the current BASIC program pointer in HL
    219E
    Call the DOS link at 41D6H. In NEWDOS 2.1 this is called at the beginning of INPUT processing.
    21A1-21A2
    SUB 23HD6 23
    Check to see if the character at the location of the current BASIC program pointer in Register A is a #
    21A3-21A5
    LD (40A9H),ALD (CASFLG),A32 A9 40
    Set the current input device flag for the cassette recorder.
    Note: 40A9H holds cassette input flag
    21A6
    LD A,(HL)7E
    Load Register A with the character at the location of the current BASIC program pointer in HL
    21A7-21A8
    JR NZ,21C9HJR NZ,INTCAS20 20
    Jump to 21C9H if there is keyboard input
    21A9-21AB
    CALL 0293HCALL CSRDONCD 93 02
    If we are here, then the input is coming from the cassette, so GOSUB to 0293H to read the cassette leader and find the sync byte
    21AC
    PUSH HLE5
    Save the current BASIC program pointer in HL to the STACK
    21AD-21AE
    LD B,FAH06 FA
    Load Register B with 250, which is the maximum number of characters which can be read
    21AF-21B1
    LD HL,(40A7H)LD HL,(BUFPNT)2A A7 40
    Load HL with the starting address of the input buffer.
    Note: 40A7H-40A8H holds the input Buffer pointer
    21B2-21B4-  ↳ FILBUF
    CALL 0235HCALL CASINCD 35 02
    Top of a DJNZ Loop. 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)
    21B5
    LD (HL),A77
    Save the byte read from the cassette recorder in Register A at the location of the input buffer pointer in HL
    21B6
    INC HL23
    Bump the value of the input buffer pointer in HL
    21B7-21B8
    CP 0DHFE 0D
    Check to see if the character read from the cassette recorder in Register A is a carriage return. If they match, the Z FLAG is set, and otherwise the NZ FLAG is set. If A < the checked value, then the C FLAG is set. If A >= the checked value, the NC FLAG is set.
    21B9-21BA
    JR Z,21BDHJR Z,ENDREC28 02
    Jump out of this routine if the character read from the cassette recorder in Register A is a carriage return
    21BB-21BC
    DJNZ 21B2HDJNZ FILBUF10 F5
    Loop back to 21B2H until the input buffer is full
    21BD-  ↳ ENDREC
    DEC HL2B
    Decrement the value of the input buffer pointer in HL to make room in the buffer for a terminator character
    21BD-  ↳ ENDREC
    DEC HL2B
    Decrement the value of the input buffer pointer in HL to make room in the buffer for a terminator character
    21BE-21BF
    LD (HL),00H36 00
    Save a zero (which is a terminator)( at the location of the input buffer pointer in HL
    21C0-21C2
    CALL 01F8HCALL CTOFFCD F8 01
    GOSUB to 01F8H to put a 00H at the end of the tape and turn the cassette recorder off
    21C3-21C5
    LD HL,(40A7H)LD HL,(BUFPNT)2A A7 40
    Load HL with the starting address of the input buffer.
    Note: 40A7H-40A8H holds the input Buffer pointer
    21C6
    DEC HL2B
    Decrement the value of the input buffer pointer in HL
    21C7-21C8
    JR 21EBHJR INPCN318 22
    Jump to 21EBH to store a comma there so we can use the READ processing
    21C9-21CB-  ↳ INTCAS
    LD BC,21DBHLD BC,NOTQTI01 DB 21
    Load BC with a return address of 21DBH for where to go when done dealing with a quoted string
    21CC
    PUSH BCC5
    Save the value of the return address in BC to the STACK
    21CD-21CE-  ↳ QTINP
    CP 22HFE 22
    Check to see if the character at the location of the current BASIC program pointer in Register A is a quote. If they match, the Z FLAG is set, and otherwise the NZ FLAG is set. If A < the checked value, then the C FLAG is set. If A >= the checked value, the NC FLAG is set.
    21CF
    RET NZC0
    Return to 21DBH if the character at the location of the current BASIC program pointer in Register A isn't a quote
    21D0-21D2
    CALL 2866HCALL STRLTICD 66 28
    Go set up pointers for the prompting message in the temporary string work area
    21D3-21D4
    RST 08H ⇒ 3BSYNCHK ;CF 3B
    Since the character at the location of the current BASIC program pointer in HL must be a ";", call the COMPARE SYMBOL routine at RST 08H.

    NOTE:The RST 08H routine 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 the next execution address (i.e, the RST 08H instruction + 2) with the next symbol in the A Register 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).
    21D5
    PUSH HLE5
    Save the value of the current BASIC program pointer in HL to the STACK
    21D6-21D8
    CALL 28AAHCALL STRPRTCD AA 28
    Go display the prompting message
    21D9
    POP HLE1
    Restore the code string address (from the STACK) into HL
    21DA
    RETC9
    Return to 21DBH
    21C9-21CB-  ↳ INTCAS
    LD BC,21DBHLD BC,NOTQTI01 DB 21
    Load BC with a return address of 21DBH for where to go when done dealing with a quoted string
    21DB-  ↳ NOTQTI
    PUSH HLE5
    Save the value of the current BASIC program pointer in HL to the STACK
    21DC-21DE-  ↳ GETAGN
    CALL 1BB3HCALL QINLINCD B3 1B
    Print the ? prompt and get the input from the keyboard
    21DF
    POP BCC1
    Remove the value of the current BASIC program pointer from the STACK, as we may be exiting
    21E0-21E2
    JP C,1DBEHJP C,STPENDDA BE 1D
    The CARRY FLAG will be set if a BREAKwas hit (meaning we got no input). Jump back to 1DBEH if the BREAKkey was pressed
    21E3
    INC HL23
    Bump the value of the input buffer pointer in HL
    21E4
    LD A,(HL)7E
    Load Register A with the character at the location of the input buffer pointer in HL
    21E5
    OR AB7
    Test the value of the character at the location of the input buffer pointer in Register A
    21E6
    DEC HL2B
    Decrement the value of the input buffer pointer in HL
    21E7
    PUSH BCC5
    Since it turns out we didn't exit, put the return address back onto to the STACK
    21E8-21EA
    JP Z,1F04HJP Z,DATAHCA 04 1F
    Skip to the next statement if the character at the location of the input buffer pointer in Register A is an end of the input character (i.e., a CARRIAGE RETURN
    21EB-21EC-  ↳ INPCN3
    LD (HL),2CH36 2C
    Save a "," at the location of the current input buffer pointer in HL
    21ED-21EE
    JR 21F4HJR INPCON18 05
    Jump to 21F4H (which address uses a Z-80 trick)

    21EF - READlogic- "READ"

    21EF-  ↳ READ
    PUSH HLE5
    Save the current BASIC program pointer in HL
    21F0-21F2
    LD HL,(40FFH)LD HL,(DATPTR)2A FF 40
    Load HL with the location of the last DATA statement read.
    Note: 40FFH-4100H holds READpointer
    21F3-21F4
    OR 0AFHOR 1010 1111F6 AF
    Turn on some bits in Register A to set Register A to a non-zero value if entered from the READroutine and, due to a Z-80 trick, set to zero if entered from the INPUTroutine
    21F4-  ↳ INPCON
    XOR AAF
    Set the flag to indicate that this is an INPUT
    21F5-21F7
    LD (40DEH),ALD (FLGINP),A32 DE 40
    Save the value of the input type flag in Register A.
    Note: 40DEH holds READ flag

    A note in the original ROM source code indicates that when processing DATA and READ, we keep one pointer which points to the data being fetched, and another pointer which points to the lists of variables. The data pointer will always start by pointing to a terminator (either a "," a ":" or an END OF LINE).

    21F8
    EX (SP),HLE3
    Swap HL and the top of STACK, so that the DATA POINTER goes to the top of the STACK, and HL will now hold the variable list pointer
    21F9-21FA
    JR 21FDHJR LOPDAT18 02
    Skip over the next instruction
    21FB-21FC-  ↳ LOPDT2
    RST 08H ⇒ 2CSYNCHK ","CF 2C
    Since the character at the location of the current BASIC program pointer in HL must be a ,, call the COMPARE SYMBOL at RST 08H.

    NOTE:The RST 08H routine 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 the next execution address (i.e, the RST 08H instruction + 2) with the next symbol in the A Register 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).
    21FD-21FF-  ↳ LOPDAT
    CALL 260DHCALL PTRGETCD 0D 26
    Call the FIND ADDRESS OF VARIABLE routine at 260DH which searches the Variable List Table for a variable name which matches the name in the string pointed to in HL, and return the address of that variable in DE (and if there is no variable, it creates it, zeroes it, and returns THAT location)
    2200
    EX (SP),HLE3
    Swap HL and the top of STACK, so that the Variable List Pointer goes to the top of the STACK and HL will hold the DATA LIST POINTER

    A note in the original ROM source code indicates that if we are here, we have a variable which is expecting data, so we only have two choices - get it some data or complain about it not getting expected data.

    2201
    PUSH DED5
    Save the pointer to the variable we are about to load with a value (held in Register Pair DE)
    2202
    LD A,(HL)7E
    Load Register A with the character at the location of the DATA LIST POINTER. This could be a terminator if its the first read
    2203-2204
    CP 2CHFE 2C
    Check to see if the character at the location of the input buffer pointer Register A is a ,. If they match, the Z FLAG is set, and otherwise the NZ FLAG is set. If A < the checked value, then the C FLAG is set. If A >= the checked value, the NC FLAG is set.
    2205-2206
    JR Z,222DHJR Z,DATBK28 26
    Jump to 222DH to fetch the impending data if we know that the character at the location of the input buffer pointer in Register A is a ,
    2207-2209
    LD A,(40DEH)LD A,(FLGINP)3A DE 40
    Load Register A with the input type flag so we can see what kind of statement called this routine (READ or INPUT).
    Note: 40DEH holds READ flag
    220A
    OR AB7
    Since a LD command does not affect the flags, OR A is commonly used to set the flags based on A. This is to Check for READ or INPUT
    220B-220D
    JP NZ,2296HJP NZ,DATLOPC2 96 22
    Jump to 2296H if the input type flag in Register A indicates READ meaning we need to go search for another data statement
    220E-2210
    LD A,(40A9H)LD A,(CASFLG)3A A9 40
    Load Register A with the value of the cassette input flag.
    Note: 40A9H holds Cassette input flag
    2211
    OR AB7
    Check to see if the input is from the cassette recorder, because if it is, then we have run out of data when we needed it
    2212-2213
    LD E,06HLD E,ERROD1E 06
    Load Register E with an ?OD ERRORcode
    2214-2216
    JP Z,19A2HJP Z,ERRORCA A2 19
    Go to the Level II BASIC error routine and display an ?OD ERRORmessage if the input is from the cassette recorder
    2217-2218
    LD A,3FH3E 3F
    Load Register A with a "?"
    2219-221B
    CALL 032AHCALL OUTDOCD 2A 03
    GOSUB to 032AH which is a general purpose output routine that outputs a byte from the A Register to video, tape or printer (based on what is in 409CH)
    221C-221E
    CALL 1BB3HCALL QINLINCD B3 1B
    Go get the keyboard line input using the routine that will also print a "?" as we want "??" when we need more input
    221F
    POP DED1
    Get the address of VARIABLE POINT (the variable to be set) from the STACK and put it in DE
    2220
    POP BCC1
    Get the return address from the STACK and put it in BC because we might be exiting this routine
    2221-2223
    JP C,1DBEHJP C,STPENDDA BE 1D
    Exit this routine via a jump to 1DBEH if we have an empty input (such as if the BREAKkey was pressed)
    2224
    INC HL23
    Since we got no input, get ready to exit. First, bump the value of the input buffer pointer in HL
    2225
    LD A,(HL)7E
    Load Register A with the character at the location of the input buffer pointer in HL
    2226
    OR AB7
    Check to see if the character at the location of the input buffer pointer in Register A is an end of the input character
    2227
    DEC HL2B
    Decrement the value of the input buffer pointer in HL
    2228
    PUSH BCC5
    Save the RETurn address back to the top of the STACK because we didn't actually exit
    2229-222B
    JP Z,1F04HJP Z,DATAHCA 04 1F
    Jump if the character at the location of the input buffer pointer in Register A is an end of the input character (i.e., a CARRIAGE RETURN)
    222C
    PUSH DED5
    Save the variable pointer (the variable's address) in DE to the STACK. At this point, the DATA will now start at the beginning of the buffer and QINLIN will leave HL set to point to that buffer
    222D
    Call the DOS link at 41DCH. In NEWDOS 2.1, this is called during READ processing when a variable has been read.
    2230
    RST 20HGETYPEE7
    We need to know if this is a string, so check the value of the current number type flag by calling the TEST DATA MODE routine at RST 20H.
    NOTE:The RST 20H routine determines the type of the current value in ACCumulator and returns a combination of STATUS flags and unique numeric values in the A Register according to the data mode flag (40AFH). The results are returned as follows:
    • 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.
    2231
    PUSH AFF5
    Save the number type of the variable to the STACK
    2232-2233
    JR NZ,224DHJR NZ,NUMINS20 19
    If that test shows we do NOT have a STRING, jump to 224DH. Note that we do not check the contents, so an unquoted string could contain all digits
    2234
    RST 10HCHRGETD7
    We need the next character in the BASIC program so 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.
    2235
    LD D,A57
    We are going to assume we have a quoted string here. First, load Register D with the character at the location of the input buffer pointer in Register A
    2236
    LD B,A47
    Load Register B with the character at the location of the input buffer pointer in Register A
    2237-2238
    CP 22HFE 22
    Check to see if the character at the location of the input buffer pointer in Register A is a quote. If they match, the Z FLAG is set, and otherwise the NZ FLAG is set. If A < the checked value, then the C FLAG is set. If A >= the checked value, the NC FLAG is set.
    2239-223A
    JR Z,2240HJR Z,NOWGETE7
    Jump to 2240H if the character at the location of the input buffer pointer in Register A is a quote
    223B-223C
    LD D,3AH16 3A
    Load D with the character :, which could act as an unquoted string terminator
    223D-223E
    LD B,2CH06 2C
    Load Register B with the character ,which could ALSO act as an unquoted string terminator
    223F
    DEC HL2B
    Decrement the pointer to the BASIC line being interpreted since we need this starting character to be included in the quoted string
    2240-2242-  ↳ NOWGETR
    CALL 2869HCALL STRLT2CD 69 28
    Set up a string descriptor for the value and copy it if necessary
    2243-  ↳ DOASIG
    POP AFF1
    Discard the number type for the variable from the STACK and put it in Register A
    2244
    EX DE,HLEB
    Load DE with the pointer to the BASIC line being processed
    2245-2247
    LD HL,225AHLD HL,STRDN221 5A 22
    Load HL with the value of a RETurn address
    2248
    EX (SP),HLE3
    Exchange HL and the top of the STACK, so that HL now points to the place to store the variable value
    2249
    PUSH DED5
    Save the pointer to the BASIC line being processed to the STACK
    224A-224C
    JP 1F33HJP INPCOMC3 33 1F
    Go set the variable to the value of the string
    224D-  ↳ NUMINS
    RST 10HCHRGETD7
    We need the next character in the BASIC program so 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.
    224D-  ↳ NUMINS
    RST 10HCHRGETD7
    We need the next character in the BASIC program so 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.
    224E
    POP AFF1
    Load Register A with the number type for the variable to be set
    224F
    PUSH AFF5
    Save it back to to the STACK
    2250-2252
    LD BC,2243HLD BC,DOASIG01 43 22
    Load BC with the value of the return address so that the assignment will jump to the LET routine
    2253
    PUSH BCC5
    Save the return address in BC to the STACK
    2254-2256
    JP C,0E6CHJP C,FINDA 6C 0E
    If the current number type is integer or single precision (i.e., NOT double precision), call the ASCII TO BINARY routine at 0E6C (which converts the ASCII string pointed to by HL to binary with the result in ACCumulator and the mode flag will have changed)
    2257-2259
    JP NC,0E65HJP NC,FINDBLD2 65 0E
    If the current number type is double precision, jump to the ASCII TO DOUBLE routine at 0E65H.
    • NOTE: 0E65H converts the ASCII string pointed to by HL to its double precision equivalent; with output left in ACCumulator).
    225A-  ↳ STRDN2
    DEC HL2B
    Decrement the value of the pointer to the BASIC line being processed by 1 character
    225B
    RST 10HCHRGETD7
    We need the next character in the BASIC program so 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.
    225C-225D
    JR Z,2263HJR Z,TRMOK28 05
    Jump to 2263H if the character at the location of the input buffer pointer in HL is an end of the input character
    225E-225F
    CP 2CHFE 2C
    Check to see if the character at the location of the input buffer pointer in Register A is a comma. If they match, the Z FLAG is set, and otherwise the NZ FLAG is set. If A < the checked value, then the C FLAG is set. If A >= the checked value, the NC FLAG is set.
    2260-2262
    JP NZ,217FHJR NZ,TRMNOKC2 7F 21
    Jump to 217FH if the character at the location of the input buffer pointer in Register A isn't a comma
    2263-  ↳ TRMOK
    EX (SP),HLE3
    Exchange the value of the input buffer pointer in HL with the value of the current BASIC program pointer to the STACK
    2264
    DEC HL2B
    Decrement the value of the current BASIC program pointer in HL to point to the terminator character
    2265
    RST 10HCHRGETD7
    We need the next character in the BASIC program so 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.
    2266-2268
    JP NZ,21FBHJP NZ,LOPDT2C2 FB 21
    If Z FLAG is set, then we are not ending, so we will need to check for a "," and get another variable to fill with data
    2269
    POP DED1
    Remove the pointer to the data location from the STACK
    226A
    NOP
    226B
    NOP
    226C
    NOP
    226D
    NOP
    226E
    NOP
    226F-2271
    LD A,(40DEH)LD A,(FLGINP)3A DE 40
    Load Register A with the value of the input type flag to see if we are here because of READ or because of INPUT.
    Note: 40DEH holds READ flag
    2272
    OR AB7
    Check to see if the input type is READor INPUT
    2273
    EX DE,HLEB
    Load DE with the value of the current BASIC program pointer in HL
    2274-2276
    JP NZ,1D96HJP NZ,RESFINC2 96 1D
    Jump if the input type flag is set for READ so we can set the DATA POINTER
    2277
    PUSH DED5
    Save the current BASIC program pointer in DE to the STACK
    2278-227A
    CALL 41DFHCALL EXCHDSCD DF 41
    GOSUB to DOS to see if DOS wants to modify any behavior
    227B
    OR (HL)B6
    Check to see if this is the end of the input (which could be a "," or a ":")
    227C-227E
    LD HL,2286HLD HL,EXIGNT21 86 22
    Load HL with the starting address of the ?EXTRA IGNOREDmessage
    227F-2281
    CALL NZ,28A7HCALL NZ,STROUTC4 A7 28
    If the Z FLAG was set, then we weren't really at the end, so We need to display the ?EXTRA IGNOREDmessage, so we 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).
    2282-  ↳ FINPRG
    POP HLE1
    Get the value of the pointer to the BASIC line being processed from the STACK and put it in HL
    2283-2285
    JP 2169HJP FINPRTC3 69 21
    Go turn off the cassette recorder and return to the BASIC interpreter

    2286-2295 - MESSAGE STORAGE LOCATION- "EXIGNT"

    2286-2295-  ↳ EXIGNT
    "?Extra ignored" + 0DH + 00H3F
    The EXTRA IGNORED message is stored here

    2296-22B5 - FIND THE NEXT DATA STATEMENT ROUTINE - "DATLOP"

    The original ROM source notes that the search is mad by uising the execution code for DATA to skp over statements. The start word of each statement is compared against $DATA. Each new line number is stored in DATLIN so that if an error occurs while reading data, the error message can give the line number of the bad formatted data.

    2296-2298-  ↳ DATLOP
    CALL 1F05HCALL DATACD 05 1F
    Go find the next DATA statement
    2299-  ↳ DATFND
    OR AB7
    Check to see if this is the end of the BASIC line
    229A-229B
    JR NZ,22AEHJR NZ.NOWLIN20 12
    Jump to 22AEH if the BASIC statement is terminated with a :
    229C
    INC HL23
    Bump the value of the current BASIC program pointer in HL
    229D
    LD A,(HL)7E
    Load Register A with the LSB of the line address at the location of the current BASIC program pointer in HL
    229E
    INC HL23
    Bump the value of the current BASIC program pointer in HL
    229F
    OR (HL)B6
    Combine the MSB of the line address at the location of the current BASIC program in HL with the LSB of the line address in Register A
    22A0-22A1
    LD E,06H1E 06
    Load Register E with an ?OD ERRORcode
    22A2-22A4
    JP Z,19A2HJP Z,ERRORCA A2 19
    Go to the Level II BASIC error routine and display an OD ERROR message if this is the end of the BASIC program
    22A5
    INC HL23
    Bump the value of the current BASIC program pointer in HL
    22A6
    LD E,(HL)5E
    Load Register E with the LSB of the BASIC line number at the location of the current BASIC program pointer in HL
    22A7
    INC HL23
    Bump the value of the current BASIC program pointer in HL
    22A8
    LD D,(HL)56
    Load Register D with the MSB of the BASIC line number at the location of the current BASIC program pointer in HL
    22A9
    EX DE,HLEB
    Exchange the value of the current BASIC program pointer in HL with the value of the BASIC line number in DE
    22AA-22AC
    LD (40DAH),HLLD (DATLIN),HL22 DA 40
    Save the BASIC line number in HL.
    Note: 40DAH-40DBH holds DATA line number
    22AD
    EX DE,HLEB
    Exchange the value of the current BASIC program pointer in DE with the value of the BASIC line number in HL
    22AE-  ↳ NOWLIN
    RST 10HCHRGETD7
    We need the next character in the BASIC program so 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.
    22AF-22B0
    CP 88HCP $DATAFE 88
    Check to see if the character at the location of the current BASIC program pointer in Register A is a DATA token. If they match, the Z FLAG is set, and otherwise the NZ FLAG is set. If A < the checked value, then the C FLAG is set. If A >= the checked value, the NC FLAG is set.
    22B1-22B2
    JR NZ,2296HJR NZ,DATLOP20 E3
    Jump to 2296H (=find the next DATA statement routine) if the character at the location of the current BASIC program pointer in Register A isn't a DATA token
    22B3-22B5
    JP 222DHJP DATBKC3 2D 22
    Now we know that the current BASIC program pointer is pointing to a DATA token, so jump to 222DH to read it

    22B6-2336 - LEVEL II BASIC NEXTROUTINE- "NEXT"

    The original ROM source notes that a FOR entry on the STACK has the following format:

    • LOW ADDRESS
      • Token ($FOR in high byte) - 1 Byte
      • Pointer to the loop variable - 2 Bytes
      • A Byte reflecting the sign of the increment - 1 Byte
      • Step value - 4 bytes
      • Upper limit of FOR loop - 4 bytes
      • Line number of the FOR statement - 2 bytes
      • Text pointer to the FOR statement - 2 bytes
    • HIGH ADDRESS
    22B6-22B8-  ↳ NEXT
    LD DE,0000H11 00 00
    Load DE with the default for cases where NEXT doesn't include a variable name (such as "NEXT X"). By doing this, FINDFOR will be called with DE = 0
    22B9-22BB-  ↳ NEXTC
    CALL NZ,260DHCALL NZ,PTRGETC4 0D 26
    Get the pointer to variable which follows the NEXTtoken, call the FIND ADDRESS OF VARIABLE routine at 260DH which searches the Variable List Table for a variable name which matches the name in the string pointed to in HL, and return the address of that variable in DE (and if there is no variable, it creates it, zeroes it, and returns THAT location)
    22BC-22BE
    LD (40DFH),HLLD (TEMP),HL22 DF 40
    Save the value of the current BASIC program pointer (contained in 40DFH) in H into a common temporary storage area
    22BF-22C1
    CALL 1936HCALL FNDFORCD 36 19
    Go search the STACK for the appropriate FOR entry that matches the variable name being used here
    22C2-22C4
    JP NZ,199DHJP NZ,NFERRC2 9D 19
    If FNDFOR found nothing, then display a NF ERROR message if the appropriate FOR push wasn't found
    22C5
    LD SP,HLF9
    Clean up the STACK. First set the STACK pointer with the value of the memory pointer in HL
    22C6-22C8
    LD (40E8H),HLLD (SAVSTK),HL22 E8 40
    Save the value in HL to the STACK pointer pointer
    22C9
    PUSH DED5
    Save the pointer to the variables address (in DE) to the STACK
    22CA
    LD A,(HL)7E
    Load Register A with the value of the sign for the STEPvalue
    22CB
    INC HL23
    Bump the value of the memory pointer in HL
    22CC
    PUSH AFF5
    Save the value of the sign for the STEPvalue in Register A to the STACK
    22CD
    PUSH DED5
    Save the pointer to the loop variable (in DE) to the STACK
    22CE
    LD A,(HL)7E
    Load Register A with the number type flag for the STEPvalue to help determine if it is an integer
    22CF
    INC HL23
    Bump the value of "FOR" entry pointer in HL
    22D0
    OR AB7
    Check the value of the number type flag for the STEPflag in Register A. The MINUS FLAG will be set if it is an integer
    22D1-22D3
    JP M,22EAHJP M,INTNXTFA EA 22
    Jump to 22EAH if the STEPvalue is an integer
    22D4-22D6
    CALL 09B1HCALL MOVFMCD B1 09
    Move the step value into the ACC via a GOSUB to 09B1H (which moves a SINGLE PRECISION number pointed to by HL to ACCumulator)
    22D7
    EX (SP),HLE3
    Exchange the top of the STACK with HL, so that the pointer for the LOOP variable goes into HL and the pointer for the FOR entry goes to the top of the STACK
    22D8
    PUSH HLE5
    Save the pointer to the loop variable's address in HL to the STACK
    22D9-22DB
    CALL 070BHCALL FADDSCD 0B 07
    Add the single precision value at the location of the memory pointer in HL to the single precision STEPvalue in Register Pairs BC and DE. Return with the result in ACCumulator
    22DC
    POP HLE1
    Get the pointer to the loop variable from the STACK and put it in HL
    22DD-22DF
    CALL 09CBHCALL MOVMFCD CB 09
    Go move the single precision result from ACCumulator to the loop variable's address in HL
    22E0
    POP HLE1
    Get the ENTRY POINTER from the STACK and put it in HL
    22E1-22E3
    CALL 09C2HCALL MOVRMCALL MOVRMCD C2 09
    Get the final looop number into the registers via a GOSUB to 09C2H (which loads a SINGLE PRECISION value pointed to by HL into Register Pairs BC and DE)
    22E4
    PUSH HLE5
    Save the ENTRY POINTER to the STACK
    22E5-22E7
    CALL 0A0CHCALL FCOMPCD 0C 0A
    Compare the numbers returning 377 if the ACC is less than the registers, 0 if equal, and otherwise 1. This is done by a GOSUB to the SINGLE PRECISION COMPARISON routine at 0A0CH.

    NOTE:The routine at 0A0CH algebraically compares the single precision value in BC/DE to the single precision value ACCumulator.
    The results are stored in A as follows:
    • A=0 if ACCumulator = BCDE
    • A=1 if ACCumulator>BCDE; and
    • A=FFH if ACCumulator<BCDE.
    22E8-22E9
    JR 2313HJR FINNXT18 29
    Jump forward to to 2313H to skip over the integer processing code

    22EAH - Part of the NEXTcode, where we process the variable as an integer- "INTNXT"

    22EA-  ↳ INTNXT
    INC HL23
    Since we are dealing with an integer, and not a single precision number, we need to skip 4 bytes of the TOvalue
    22EB
    INC HL23
    Bump the value of the memory pointer in HL
    22EC
    INC HL23
    Bump the value of the memory pointer in HL
    22ED
    INC HL23
    Bump the value of the memory pointer in HL
    22EE
    LD C,(HL)4E
    Load Register C with the LSB of the STEPvalue (held at the location of the memory pointer in HL)
    22EF
    INC HL23
    Bump the value of the memory pointer in HL to be the MSB of the STEPvalue
    22F0
    LD B,(HL)46
    Load Register B with the MSB of the STEPvalue at the location of the memory pointer in HL
    22F1
    INC HL23
    Bump the value of the memory pointer in HL so that it is the STACK address of the TOlimit
    22F2
    EX (SP),HLE3
    Exchange HL and the value at the top of the STACK, so that HL will point to the loop's variable and the ENTRY POINTER will be at the top of the STACK
    22F3
    LD E,(HL)5E
    Next we need to get thee loop's variable value into Register Pair DE. First, load Register E with the LSB of the loop variable
    22F4
    INC HL23
    Bump the value of the memory pointer in HL to point to the MSB of the index
    22F5
    LD D,(HL)56
    Load Register D with the MSB of the loop variable
    22F6
    PUSH HLE5
    Save the pointer to the loop variable value to the STACK
    22F7
    LD L,C69
    Next, we are going to need to add DE to HL. First, load Register L with the LSB of the STEPvalue in Register C
    22F8
    LD H,B60
    Load Register H with the MSB of the STEPvalue in Register B
    22F9-22FB
    CALL 0BD2HCALL IADDCD D2 0B
    With the STEPvalue in HL, call the INTEGER ADD routine at 0BD2H (which 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 ACCumulator and the mode flag would be updated)
    22FC-22FE
    LD A,(40AFH)LD A,(VALTYP)3A AF 40
    Load Register A with the current value of the data type flag, as we want to check for an overflow.
    Note: 40AFH holds Current number type flag
    22FF
    CP 04HFE 04
    Check to see if the current value in ACCumulator was turned into single precision. If they match, the Z FLAG is set, and otherwise the NZ FLAG is set. If A < the checked value, then the C FLAG is set. If A >= the checked value, the NC FLAG is set.
    2301-2303
    JP Z,07B2HJP Z,OVERRCA B2 07
    Show an ?OV ERRORmessage if the index (i.e., the current value in ACCumulator) has overflowed to be a single precision number instead of an integer
    2304
    EX DE,HLEB
    Swap DE and HL so that DE will now hold the new loop variable value
    2305
    POP HLE1
    Restore the pointer to the loop variable back into HL
    2306
    LD (HL),D72
    Save the MSB of the result in Register D at the location of the memory pointer in HL
    2307
    DEC HL2B
    Decrement the value of the memory pointer in reg? ister pair HL
    2308
    LD (HL),E73
    Save the LSB of the result in Register E at the location of the memory pointer in HL
    2309
    POP HLE1
    Restore the pointer for the FOR entry back into HL
    230A
    PUSH DED5
    Save the value of the loop's variable (the index) in DE to the STACK
    230B
    LD E,(HL)5E
    We need DE to hold the final value, so first load Register E with the LSB of the TOvalue at the location of the memory pointer in HL
    230C
    INC HL23
    Bump the value of the memory pointer in HL to point to the MSB of the TOvalue
    230D
    LD D,(HL)56
    Load Register D with the MSB of the TOvalue at the location of the memory pointer in HL
    230E
    INC HL23
    Bump the value of the memory pointer in HL so now it points to the line number
    230F
    EX (SP),HLE3
    Swap the TOvalue at the memory location pointed to by the STACK with the address of the binary line number for the FORstatement (now in HL)
    2310-2312
    CALL 0A39HCALL ICOMPCD 39 0A
    We need to compare the new index to the limit so we call the INTEGER COMPARISON routine at 0A39H (which 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 as:
    If DE > HL then A will be -1;
    If DE < HL then A will b +1; and
    If DE = HL then A will be 0

    2313H - Part of the NEXTcode, jumped to continue after skipping over the integer processing- "FINNXT"

    2313-  ↳ FINNXT
    POP HLE1
    Restore the "FOR" entry pointer (which is now pointing past the final value) into HL
    2314
    POP BCC1
    Get the value of the sign from the STACK and put it in BC
    2315
    SUB B90
    We need to see if the sign is the sign we expected so we subtract the value of the sign in Register B from the value in Register A (which is currently set to CURRENT VALUE - FINAL VALUE)
    2316-2318
    CALL 09C2HCALL MOVRMCD C2 09
    Call 09C2H (which loads a SINGLE PRECISION value pointed to by HL into Register Pairs BC and DE)
    2319-231A
    JR Z,2324HJR Z,LOOPDN28 09
    Jump to 2324H if the index does not equal the TOlimit (meaning the FOR . NEXT loop has not been completed)
    231B
    EX DE,HLEB
    Load HL with the BASIC line number of the FORstatement in DE
    231C-231E
    LD (40A2H),HLLD (CURLIN),HL22 A2 40
    Save the BASIC line number in HL as the current BASIC line number (which is stored at 40A2H-40A3H).
    231F
    LD L,C69
    Set up the pointer to the current character in the BASIC program being processed by first loading Register L with the LSB of the current BASIC program pointer in Register C
    2320
    LD H,B60
    Load Register H with the MSB of the current BASIC program pointer in Register B
    2321-2323
    JP 1D1AHJP NXTCONC3 1A 1D
    Jump to 1D1AH to continue execution and restore the FORtoken and GAP for FOR

    2313H - Part of the NEXTcode, jumped if we haven't hit the TO counter yet- "LOOPDN"

    2324-  ↳ LOOPDN
    LD SP,HLF9
    We are going to need to eliminate the FOR entry since HL already moved all the way down the entry
    2325-2327
    LD (40E8H),HLLD (SAVSTK),HL22 E8 40
    Reset the STACK pointer pointer
    2328-232A
    LD HL,(40DFH)LD HL,(TEMP)2A DF 40
    Save the value of the current BASIC program pointer in HL into a common temporary storage area
    232B
    LD A,(HL)7E
    We are going to need to check for a "," so first load Register A with the next token (i.e., the character at the location of the current BASIC program pointer in HL)
    232C-232D
    CP 2CHFE 2C
    Check to see if the character at the location of the current BASIC program pointer in Register A is a ,. If they match, the Z FLAG is set, and otherwise the NZ FLAG is set. If A < the checked value, then the C FLAG is set. If A >= the checked value, the NC FLAG is set.
    232E-2330
    JP NZ,1D1EHJP NZ,NEWSTTC2 1E 1D
    If the character at the location of the current BASIC program pointer in Register A isn't a ,then we need to look at another variable name for the NEXT, so JUMP to NEWSTT
    2331
    RST 10HCHRGETD7
    We need the next character in the BASIC program so 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.
    2332-2334
    CALL 22B9HCALL NEXTCCD B9 22
    GOSUB to 22B9H to process NEXT, but do not allow a blank variable name

    2335-27C8 - EVALUATE EXPRESSION- "FRMPRN" and "FRMEVL"

    According to the original ROM source, this routine starts with HL pointing to the first character of a formula. At the end of the routine HL points to the terminator, and ACC holds the result. Important to note that on exit Register A does not necessarily reflect the terminating character.

    The formula evaluator uses the operation table ("OPTAB") to determine the precedent and to dispatch addresses for each operator.

  • During operation, the STACK has the following format:
    • The RETURN location on completion (RETAOP)
    • The floating point temporary result
    • The address of the operator routine
    • The precedence of the operator
    Another description of this routine is that it evaluates a BASIC expression pointed to by the HL and stores the result in the ACC. The expression must be terminated with zero byte, comma, right bracket or colon. After execution, HL will point to the delimiter and, in the case of string expressions, the ACC will contain the address of the first of three bytes that contain string length and string address. Note that the STACK is used frequently and the machine should be formatted for RUN mode in order to use this routine.
    2335-  ↳ FRMPRN
    RST 08H ⇒ 28SYNCHK "("CF 28
    If we chose THIS entry point, we require a parenthesis to start. Since the character at the location of the current BASIC program pointer in HL must be a (, call the COMPARE SYMBOL routine at RST 08H.

    NOTE:The RST 08H routine 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 the next execution address (i.e, the RST 08H instruction + 2) with the next symbol in the A Register 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).
    2337-  ↳ FRMEVL
    DEC HL2B
    This would be the entry point if we don't need a lead-in "(". First, decrement the value of the current BASIC program pointer in HL
    2338-2339-  ↳ FRMCHK
    LD D,00H16 00
    Load Register D with zero as a dummy precedence for the formula
    233A-  ↳ LPOPER
    PUSH DED5
    Save the value in DE to the STACK
    233B-233C
    LD C,01H0E 01
    Load Register C with the number of bytes of memory required for a return address -1 (so 2 bytes)
    233D-233F
    CALL 1963HCALL GETSTKCD 63 19
    Since we must make sure that there is enough room for recursive calls, we GOSUB to 1963H to compute the amount of space between HL and the end of memory at FFC6H
    2340-2342
    CALL 249FHCALL EVALCD 9F 24
    Go get the value of the next part of the expression at the location of the current BASIC program pointer in HL
    2343-2345
    LD (40F3H),HLLD (TEMP2),HL22 F3 40
    Save the value of the current BASIC program pointer (i.e., the next token) in HL.
    Note: 40F3H-40F4H is a temporary storage location
    2346-2348-  ↳ RETAOP
    LD HL,(40F3H)LD HL,(TEMP2)2A F3 40
    Load HL with the value of the current BASIC program pointer.
    Note: 40F3H-40F4H is a temporary storage location
    2349-  ↳ TSTOP
    POP BCC1
    Get the last PRECEDENCE value from the STACK and put it in BC
    234A-  ↳ NOTSTV
    LD A,(HL)7E
    Load Register A with the character at the location of the current BASIC program pointer in HL
    234B-234C
    LD D,00H16 00
    Load Register D with zero as an assumption that there are no relation operations AND to set up the high order of the index into OPTAB
    234D-234E-  ↳ LOPREL
    SUB D4HSUB GREATKD6 D4
    Check to see if the character at the location of the current BASIC program pointer in Register A is an arithmetic or logical (i.e., relational) token (by subtracting 212 from it)
    234F-2350
    JR C,2364HJR C,ENDREL38 13
    Jump to 2364H if the character at the location of the current BASIC program pointer in Register A is "relational" (i.e., +, -, *, /, ?, ANDor OR
    2351-2352-  ↳ NMREL
    CP 03HCP NMRELFE 03
    Check to see if the character at the location of the current BASIC program pointer in Register A is really relational (i.e., a >, =, or <. If they match, the Z FLAG is set, and otherwise the NZ FLAG is set. If A < the checked value, then the C FLAG is set. If A >= the checked value, the NC FLAG is set.
    2353-2354
    JR NC,2364HJR NC,ENDREL30 0F
    If the character at the location of the current BASIC program pointer in Register A isn't a >, =, or <, and it's jsut BIG, JUMP to ENDREL
    2355-2356
    CP 01HFE 01
    Set the Carry flag if >. Then test for <=or >=
    2357
    RLA17
    Adjust the value in Register A to give >as 1, =as 2, or <as 4
    2358
    XOR DAA
    Combine the current value in Register A with the value of the last token examined to see if this is a legal combination of <=or =>or illegal combination of <<, ==, or >>
    2359
    CP DBA
    Check to make sure that the result is bigger and to avoid an illegal combination of operators. If they match, the Z FLAG is set, and otherwise the NZ FLAG is set. If A < the checked value, then the C FLAG is set. If A >= the checked value, the NC FLAG is set.
    235A
    LD D,A57
    Load Register D with the mask in Register A
    235B-235D
    JP C,1997HJP C,SNERRDA 97 19
    If we have two of the same operators then display a ?SN ERROR
    235E-2360
    LD (40D8H),HLLD (TEMP3),HLLD (TEMP3),HL22 D8 40
    Save the address of the >, =, or <token (held in HL) to 40D8H, which is another temporary storage location
    2361
    RST 10HCHRGETD7
    We need the next token so 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.
    2362-2363
    JR 234DHJR LOPREL18 E9
    Jump back to 234DH
    2364-  ↳ ENDREL
    LD A,D7A
    Load Register A with the mask from Register D
    2365
    OR AB7
    Test the value of the operator in Register A against the mask to see if any of the masked operators are present
    2366-2368
    JP NZ,23ECHJP NZ,FNDRELC2 EC 23
    If the NZ FLAG is set, then one of the masked operators (>, =, or <) is present, so JUMP to FINDREL to handle those
    2369
    LD A,(HL)7E
    Load Register A with the character at the location of the current BASIC program pointer in HL
    236A-236C
    LD (40D8H),HLLD (TEMP3),HL22 D8 40
    Save the address of the current BASIC program pointer in HL (which is an arithmetic operator) to 40D8H, which is another temporary storage location
    236D-236E
    SUB 0CDHSUB PLUSTKD6 CD
    Check to see if the operator at the location of the current BASIC program pointer in Register A is an arithmetic token
    236F
    RET CD8
    Return if the character at the location of the current BASIC program pointer in Register A isn't an arithmetic token
    2370-2371
    CP 07HCP LSTOPKFE 07
    Check to see if the character at the location of the current BASIC program pointer in Register A is a +to ORtoken. If they match, the Z FLAG is set, and otherwise the NZ FLAG is set. If A < the checked value, then the C FLAG is set. If A >= the checked value, the NC FLAG is set.
    2372
    RET NCD0
    Return if the character at the location of the current BASIC program pointer in Register A isn't a +to ORtoken
    2373
    LD E,A5F
    Load Register E with the operator value in Register A (which would be between 0 and 7). This also sets up for a MULTIPLY BY 3 since the table entries are 3 bytes each.
    E   Token
    0   +
    1   -
    2   *
    3   /
    4   @@
    5   AND
    6   OR
    2374-2376
    LD A,(40AFH)LD A,(VALTYP)3A AF 40
    Load Register A with the current value of the number type flag.
    Note: 40AFH holds Current number type flag
    2377-2378
    SUB 03HD6 03
    Test for a string by adjusting the value of the number type flag to get a -1 if integer, 0 for a string, 1 for single precision, and 5 for double precision
    2379
    OR EB3
    Combine the operator value in Register E with the adjusted number type flag in Register A. If it turns out to be a "+" then the Z FLAG will be set
    237A-237C
    JP Z,298FHJP Z,CATCA 8F 29
    Jump down to 298FH if the combination of the adjusted number type flag in Register A and the operator value in Register E indicates string addition
    237D-237F
    LD HL,189AHLD HL,OPTAB21 9A 18
    Load HL with the starting address of the table of precedence operator values
    2380
    ADD HL,DE19
    Add the value of the offset in DE to the table of precedence values pointer in HL
    2381
    LD A,B78
    Load Register A with the old precedence (i.e., the precedence value for the last operator) in Register B
    2382
    LD D,(HL)56
    Load Register D with the precedence value for the current operator
    2383
    CP DBA
    Let A = OLD PRECEDENCE minus NEW PRECEDENCE so as to compare the precedence value for the current operator in Register D with the precedence value for the last operator in Register A. If they match, the Z FLAG is set, and otherwise the NZ FLAG is set. If A < the checked value, then the C FLAG is set. If A >= the checked value, the NC FLAG is set.
    2384
    RET NCD0
    We need to apply the old precedence if it has greater or equal precedence to the new one, so if the NC flag is set, RETurn out of this routine
    2385
    PUSH BCC5
    Save the precedence value and the token for the last operator in BC to the STACK
    2386-2388
    LD BC,2346HLD BC,RETAOP01 46 23
    Load BC with the return address in case there is a break in precedence
    2389
    PUSH BCC5
    Save the return address in BC to the STACK
    238A
    LD A,D7A
    Load Register A with the precedence value for the current operator in Register D so that we can test for an exponent
    238B-238C
    CP 7FHCP EXPSTKFE 7F
    Check to see if the precedence value for the current operator in Register A indicates an exponential operator. If so, then we need FRCSNG and a special STACK entry
    238D-238F
    JP Z,23D4HCA D4 23
    Jump down to 23D4H if the precedence value for the current operator in Register A indicates an exponential operator
    238D-238F
    JP Z,23D4HJP Z,EPSTKCA D4 23
    Jump down to 23D4H if the precedence value for the current operator in Register A indicates an exponential operator
    2390-2391
    CP 51HFE 51
    Check to see if the precedence value for the current operator in Register A indicates an "AND" or an "OR" logical operator. If they match, the Z FLAG is set, and otherwise the NZ FLAG is set. If A < the checked value, then the C FLAG is set. If A >= the checked value, the NC FLAG is set.
    2392-2394
    JP C,23E1HJP C,ANDORDDA E1 23
    Jump if the precedence value for the current operator in Register A indicates a logical operator

    According to the original ROM source code, the following will push the current value in the ACC onto the STACK EXCEPT in the case of a string, in which case it will throw a TYPE MISMATCH error. Registers D and E are preserved. This routine is also used in the user-defined function value savings

    2395-2397-  ↳ NUMREL
    LD HL,4121HLD HL,FACLO21 21 41
    Load HL with the address of the ACCumulator
    2398
    OR AB7
    Ensure the CARRY FLAG is off
    2399-239B-  ↳ PUSVAL
    LD A,(40AFH)LD A,(VALTYP)3A AF 40
    Load Register A with the type of value we are dealing with.
    Note: 40AFH holds Current number type flag
    239C
    DEC A3D
    Next we need to set the flags without setting CARRY ... Adjust the current number type flag in Register A
    239D
    DEC A3D
    Adjust the current number type flag in Register A
    239E
    DEC A3D
    Adjust the current number type flag in Register A. Now A will be -1=Integer, 0=String, 1=Single Precision, 5=Double Precision
    239F-23A1
    JP Z,0AF6HJP Z,TMERRCA F6 0A
    Display a ?TM ERRORif the current value in ACCumulator is a string
    23A2
    LD C,(HL)4E
    Get the value at the location of the memory pointer in HL and put it in Register C
    23A3
    INC HL23
    Bump the value of the memory pointer in HL
    23A4
    LD B,(HL)46
    Load Register B with the value at the location of the memory pointer in HL
    23A5
    PUSH BCC5
    Save the FACLO (held in B) to the STACK
    23A6-23A8
    JP M,23C5HJP M,VPUSHDFA C5 23
    If the data was an integer, then we are done, so in that case JUMP to 23C5H
    23A9
    INC HL23
    It's not an integer, so lets get the rest of the value by bumping the value of the memory pointer in HL
    23AA
    LD C,(HL)4E
    Load Register C with the value at the location of the memory pointer in HL
    23AB
    INC HL23
    Bump the value of the memory pointer in HL
    23AC
    LD B,(HL)46
    Load Register B with the value at the location of the memory pointer in HL
    23AD
    PUSH BCC5
    Save the value in BC (the rest of the digit) to the STACK
    23AE
    PUSH AFF5
    Save the variable flag (held in value in AF as the type - 3) to the STACK
    23AF
    OR AB7
    Reset the status flags so that we can test if the current number type is double precision
    23B0-23B2
    JP PO,23C4HJP PO,VPSHD1E2 C4 23
    Jump down to 23C4H if the current number type is single precision
    23B3
    POP AFF1
    Restore the variable flag into Register Pair AF from the STACK
    23B4
    INC HL23
    Bump the value of the memory pointer in HL
    23B5-23B6
    JR C,23BAHJR C,PUSDVR38 03
    Skip the next instruction if the current number type is single precision
    23B7-23B9
    LD HL,411DHLD HL,DFACLO21 1D 41
    Reset HL to start of ACCumulator for a double density number.
    Note: 411DH-4124H holds REG l
    23BA-  ↳ PUSDVR
    LD C,(HL)4E
    Load Register C with the rest of the double precision value (held at the location of the memory pointer in HL)
    23BB
    INC HL23
    Bump the value of the memory pointer in HL
    23BC
    LD B,(HL)46
    Load Register B with the next digit LSB (at the location of the memory pointer in HL)
    23BD
    INC HL23
    Bump the value of the memory pointer in HL to the next digit
    23BE
    PUSH BCC5
    Save the LSB/NMSB of the double precision value (held in BC) to the STACK
    23BF
    LD C,(HL)4E
    Load Register C with the value at the location of the memory pointer in HL
    23C0
    INC HL23
    Bump the value of the memory pointer in HL
    23C1
    LD B,(HL)46
    Load Register B with the value at the location of the memory pointer in HL
    23C2
    PUSH BCC5
    Save the value in BC to the STACK
    23C3
    LD B,0F1H06 F1
    Z-80 Trick to nullify the next opcode if passing through
    23C4-  ↳ VPSHD1
    POP AFF1
    Restore the variable flag (which is actually the variable flag - 3)
    23C5-23C6-  ↳ VPUSHD
    ADD A,03HC6 03
    Adjust the number type in Register A up 3
    23C7
    LD C,E4B
    Load Register C with the value of the current operator token in Register E (0-7)
    23C8
    LD B,A47
    Load Register B with the number type flag in Register A
    23C9
    PUSH BCC5
    Save these two new things (held in BC) to the STACK
    23CA-23CC
    LD BC,2406HLD BC,APPLOP01 06 24
    Load BC with the return address of 2406H which is the APPLOP general operator application routine to do type conversions
    23CD-  ↳ FINTMP
    PUSH BCC5
    Save the return address in BC to the STACK
    23CE-23D0
    LD HL,(40D8H)LD HL,(TEMP3)2A D8 40
    Load HL with the value of the current BASIC program pointer.
    Note: 40D8H-40D9H holds Temporary storage location
    23D1-23D3
    JP 233AHJP LPOPERC3 3A 23
    Jump back to 233AH to continue reading the formula

  • 23D4-23D6 - Part of the Evaluation Routine- "EPSTK"

    Per the original ROM source, for exponentiation, we want to force the current value in the ACCumulator to be single precision. When application time comes, we force the right hand operand to single precision as well.

    23D4-23D6-  ↳ EXPSTK
    CALL 0AB1HCALL FRCSNGCD B1 0A
    Call the CONVERT TO SINGLE PRECISION routine at 0AB1H (which converts the contents of ACCumulator from integer or double precision into single precision)
    23D7-23D9
    CALL 09A4HCALL PUSHFCD A4 09
    Call 09A4 which moves the SINGLE PRECISION value in the ACCumulator to the STACK (stored in LSB/MSB/Exponent order)
    23DA-23DC
    LD BC,13F2HLD BC,FPWRQ01 F2 13
    Load BC with the address of the exponential X^Y routine at 13F2H
    23DD-23DE
    LD D,7FH16 7F
    Load Register D with the precedence value for an exponential operator
    23DF-23E0
    JR 23CDHJR FINTMP18 EC
    Jump back to 23CDH to continue expression evaluation

    23D4-23D6 - Part of the Evaluation Routine- "ANDORD"

    According to the original ROM source, for ANDand ORand \and MODwe want to force the current value in the ACCumulator to be an integer, and at application time force the right hand operator to be an integer as well.

    23E1-  ↳ ANDORD
    PUSH DED5
    Save the precedence value and the operator token in DE to the STACK
    23E2-23E4
    CALL 0A7FHCALL FRCINTCD 7F 0A
    Call the CONVERT TO INTEGER routine at 0A7FH (where the contents of ACCumulator are converted from single or double precision to integer and deposited into HL)
    23E5
    POP DED1
    Get the precedence value and the operator token from the STACK and put it in DE
    23E6
    PUSH HLE5
    Save the left hand operand in HL to the STACK
    23E7-23E9
    LD BC,25E9HLD BC,DANDOR01 E9 25
    Load BC with a return address of 25E9H to handle AND and OR
    23EA-23EB
    JR 23CDHJR FINTMP18 E1
    Jump back to 23CDH to push this address, push precedence, and then keep processing the expression.

    23D4-23D6 - Part of the Evaluation Routine- "FINREL"

    According to the original ROM source, this routine will build an entry for a relational operator strings are treated specially. Numeric compares are different from most operator entries only in the fact that at the bottom instead of having RETAOP, DOCMP and the relational bits are stored. Strings have STRCMP, the pointer at the string descriptor, DOCMP and the relational bits.

    23EC-  ↳ FINREL
    LD A,B78
    Load Register A with the precedence value for the PRIOR operator in Register B
    23ED-23EE
    CP 64HFE 64
    Check to see if the last operator was a logical/relational operator, as those have a value of 100. If they match, the Z FLAG is set, and otherwise the NZ FLAG is set. If A < the checked value, then the C FLAG is set. If A >= the checked value, the NC FLAG is set.
    23EF
    RET NCD0
    If the PRIOR operator has a higher precedentnce then apply that one instead via a RETurn
    23F0
    PUSH BCC5
    Save the precedence value and the operator token for the PRIOR operator in BC to the STACK
    23F1
    PUSH DED5
    Save the precedence value (D) and the token (E) for the current operator from DE (either a 6, 5, or 3) to the STACK
    23F2-23F4
    LD DE,6404HLD DE,256 * 100 + OPCNT11 04 64
    Load DE with the precedence value (of 100) and the displatch offset of the token for the new operator
    23F5-23F7
    LD HL,25B8HLD HL,DOCMP21 B8 25
    LD HL with the routine to take compare routine results and relation bits and return the answer; routine will do a JUMP to DOCMP when complete
    23F8
    PUSH HLE5
    and push it to the STACK
    23F9
    RST 20HGETYPEE7
    We need to check the value of the current number type flag, so we call the TEST DATA MODE routine at RST 20H.
    NOTE:The RST 20H routine determines the type of the current value in ACCumulator and returns a combination of STATUS flags and unique numeric values in the A Register according to the data mode flag (40AFH). The results are returned as follows:
    • 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.
    23FA-23FC
    JP NZ,2395HJP NZ,NUMRELC2 95 23
    If that test shows we do NOT have a STRING (i.e., we have numbers), jump back to 2395H to build an APPLOP entry
    23FD-23FF
    LD HL,(4121H)LD HL,(FACLO)2A 21 41
    If we are here then we have a string, so first load HL with the string address held at the memory location pointed to by ACCumulator
    2400
    PUSH HLE5
    Save the string's address in HL to the STACK so that STRCMP can use it.
    2401-2303
    LD BC,258CHLD BC,STRCMP01 8C 25
    Load BC with the address of the string comparison routine
    2404-2405
    JR 23CDHJR FINTMP18 C7
    Jump back to 23CDH which will push BC, re-get the text pointer, save the precedence, and then scan more of the expression.

    2406 - Part of the Evaluation Routine- "APPLOP"

    According to the original ROM source code, APPLOP is returned to when it is time to apply an arithmetic or numeric comparison operation. The STACK has a double byte entry with the operator number and the VALTYP of the value on the STACK. APPLOP decides what value level the operation will occur at, and converts the arguments. APPLOP uses different calling conventions for each value type:

    • integers: left in [d,e] right in [h,l]
    • singles: left in [b,c,d,e] right in the fac
    • doubles: left in fac right in arg

    2406-  ↳ APPLOP
    POP BCC1
    Get the operand value type into B and the Operator Offset (i.e., token) for the last operator from the STACK and put it in C
    2407
    LD A,C79
    Load Register A with the operator token in Register C
    2408-240A
    LD (40B0H),ALD (DORES),A32 B0 40
    Save the operator token in Register A.
    Note: 40B0H holds Temporary storage location
    240B
    LD A,B78
    Load Register A with the operand value type for the value held in the STACK (held in Register B)
    240C-240D
    CP 08HFE 08
    Check the precision type of the number in the STACK against 08H. If they match, the Z FLAG is set, and otherwise the NZ FLAG is set. If A < the checked value, then the C FLAG is set. If A >= the checked value, the NC FLAG is set.
    240E-240F
    JR Z,2438HJR Z,STKDBL28 28
    Jump forward to 2438H to force the number in the STACK to double precision
    2410-2412
    LD A,(40AFH)LD A,(VALTYP)3A AF 40
    Load Register A with the number type for the current result in ACCumulator.
    Note: 40AFH holds Current number type flag
    2413-2414
    CP 08HFE 08
    Check to see if the current result in ACCumulator is double precision. If they match, the Z FLAG is set, and otherwise the NZ FLAG is set. If A < the checked value, then the C FLAG is set. If A >= the checked value, the NC FLAG is set.
    2415-2417
    JP Z,2460HJP Z,FACDBLCA 60 24
    Jump forward to 2460H if the current value in ACCumulator is double precision so that we can convert the STACK operand to double density
    2418
    LD D,A57
    Load Register D with the current data type flag for the value in the ACCumulator held in Register A
    2419
    LD A,B78
    Load Register A with value type of the value in the STACK entry (looking for single precision)
    241A-241B
    CP 04HFE 04
    Check to see if the number type in the STACK is single precision. If they match, the Z FLAG is set, and otherwise the NZ FLAG is set. If A < the checked value, then the C FLAG is set. If A >= the checked value, the NC FLAG is set.
    241C-241E
    JP Z,2472HJP Z,STKSNGCA 72 24
    If so, jump forward to 2472H to convert he ACCumulator number to single precision
    241F
    LD A,D7A
    Load Register A with the number type flag for the value in ACCumulator for single precision.
    2420-2421
    CP 03HFE 03
    Check to see if the current value in ACCumulator is a string. If they match, the Z FLAG is set, and otherwise the NZ FLAG is set. If A < the checked value, then the C FLAG is set. If A >= the checked value, the NC FLAG is set.
    2422-2424
    JP Z,0AF6HJP Z,TMERRCA F6 0A
    Display a ?TM ERRORmessage if the current value in ACCumulator is a string
    2425-2427
    JP NC,247CHJP NC,FACSNG22 E8 40
    Jump forward to 247CH if the current value in ACCumulator is single precision to convert the value in the STACK to single precision.

    At this point, the number in the STACK MUST be an integer.

    2428-242A
    LD HL,18BFHLD HL,INTDSP21 BF 18
    Load HL with the starting address of the arithmetic jump table
    242B-242C
    LD B,00H06 00
    Load Register B with zero
    242D
    ADD HL,BC09
    Add the operator's token in BC to the value of the arithmetic jump table pointer in HL
    242E
    ADD HL,BC09
    Add the operator's token in BC to the value of the arithmetic jump table pointer in HL. HL will now point to where to go to process.
    242F
    LD C,(HL)4E
    Now that HL points to the token, we need to get the address from the lookup table. Load Register C with the LSB of the jump address at the location of the arithmetic jump table pointer in HL
    2430
    INC HL23
    Bump the value of the arithmetic jump table pointer in HL
    2431
    LD B,(HL)46
    Load Register B with the MSB of the jump address at the location of the arithmetic jump table pointer in HL
    2432
    POP DED1
    Get the left hand operand from the STACK and put it into Register Pair DE
    2433-2435
    LD HL,(4121H)LD HL,(FACLO)2A 21 41
    Get the right hand operand from (FACLO) and put it into Register Pair DE
    2436
    PUSH BCC5
    Save the arithmetic routine's jump address in register pair BC to the STACK
    2437
    RETC9
    RETurn to the arithmetic routine of choice.

    2438 - Part of the Evaluation Routine- "STKDBL"

    According to the original ROM source code, at this point we know the STACK operand is double precision, so the number in the ACC must be forced into double precision, then moved into ARG and the STACK value POPped into ACC.

    2438-243A-  ↳ STKDBL
    CALL 0ADBHCALL FRCDBLCD DB 0A
    Make the ACCumulator double precision via a call to the CONVERT TO DOUBLE PRECISION routine at 0ADBH (where the contents of ACCumulator are converted from integer or single precision to double precision)
    243B-243D
    CALL 09FCHCALL VMOVAFCD FC 09
    Move the ACC into the ARG
    243E
    POP HLE1
    Get the STACK operand into the ACCumulator ...POP the STACK and put it in HL
    243F-2441
    LD (411FH),HLLD (DFACLO+2),HL22 1F 41
    Save the value in HL near the end of the ACCumulator
    2442
    POP HLE1
    Get the value from the STACK and put it in HL
    2443-2445
    LD (411DH),HLLD (DFACLO),HL22 1D 41
    Save the value in HL in the ACCumulator.
    Note: 411DH-4124H holds the ACCumulator
    2446-  ↳ SNGDBL
    POP BCC1
    Next we need 4 bytes from the STACK so ... get the value from the STACK and put it in BC
    2447
    POP DED1
    Get the value from the STACK and put it in DE
    2448-244A
    CALL 09B4HCALL MOVFRCD B4 09
    Put those 4 bytes into the ACCumulator via a call 09B4H (which moves the SINGLE PRECISION value in DC/DE into ACCumulator). This moves DE to (4121H) and BC to (4123H)
    244B-244D-  ↳ SETDBL
    CALL 0ADBHCALL FRCDBLCD DB 0A
    Convert the first value to double precision by calling the CONVERT TO DOUBLE PRECISION routine at 0ADBH (where the contents of ACCumulator are converted from integer or single precision to double precision)
    244E-2450
    LD HL,18ABHLD HL,DBLDSP21 AB 18
    Load HL with the double precision arithmetic jump table's starting address
    2451-2453-  ↳ DODSP
    LD A,(40B0H)
    LD A,(DORES)3A B0 40
    Load Register A with the operator token in Register A.
    Note: 40B0H holds Temporary storage location
    2454
    RLCA07
    Multiply the value of the operator token in Register A by two since each byte of the table is 2 bytes.
    2455
    PUSH BCC5
    Save the value in BC to the STACK so we can use it for 16 bit arithmetic.
    2456
    LD C,A4F
    Load Register C with the adjusted value of the operator token in Register A (i.e., 2 x token)
    2457-2458
    LD B,00H06 00
    Load Register B with zero
    2459
    ADD HL,BC09
    Add the value of the adjusted operator token in BC (which is token value * 2) to 18ABH (which is the double precision arithmetic jump table's starting address) in HL
    245A
    POP BCC1
    Restore BC from the STACK and put it in BC in preparation for single precision math
    245B
    LD A,(HL)7E
    Load Register A with the LSB of the jump address at the location of the arithmetic jump table pointer in HL
    245C
    INC HL23
    Bump the value of the arithmetic jump table pointer in HL
    245D
    LD H,(HL)66
    Load Register H with the MSB of the jump address at the location of the arithmetic jump table pointer in HL
    245E
    LD L,A6F
    Load Register L with the LSB of the jump address in Register A
    245F
    JP (HL)E9
    Jump to the routine held in (HL).

    2460H - Part of the Evaluation Routine- "FACDBL"

    According to the original ROM source code, at this point the ACCumulator holds a double precision numbe, and the STACK holds either an integer or a single precision number, so we need to convert it.

    2460-  ↳ FACDBL
    PUSH BCC5
    Save the number type of the value held in the STACK
    2461-2463
    CALL 09FCHCALL VMOVAFCD FC 09
    Move the value in the ACCumulator into ARG
    2464
    POP AFF1
    Load Register A with the value type of the number in the STACK
    2465-2467
    LD (40AFH),ALD (VALTYP),A32 AF 40
    Save the value of the current number type flag in Register A.
    Note: 40AFH holds Current number type flag
    2468-2469
    CP 04HFE 04
    Check to see if the current result in ACCumulator is single precision. If they match, the Z FLAG is set, and otherwise the NZ FLAG is set. If A < the checked value, then the C FLAG is set. If A >= the checked value, the NC FLAG is set.
    246A-246B
    JR Z,2446HJR Z,SNGDBL28 DA
    Jump back to 2446H if the current result in ACCumulator is single precision. That will POP BC and DE, and then CALL MOVFR to continue
    246C
    POP HLE1
    Get the integer value from the STACK and put it in HL
    246D-246F
    LD (4121H),HLLD (FACLO),HL22 21 41
    Save the integer value in HL in the ACCumulator
    2470-2471
    JR 244BHJR SETDBL18 D9
    Jump back to 244BH to set it up

    2472H - Part of the Evaluation Routine- "STKSNG"

    According to the original ROM source code, at this point the STACK holds a single precision number, we know that the ACCumulator holds either an integer or a single precision number, so we need to convert it.

    2472-2474-  ↳ STKSNG
    CALL 0AB1HCALL FRCSNGCD B1 0A
    Convert the ACCumulator if necessary via a call to the CONVERT TO SINGLE PRECISION routine at 0AB1H (which converts the contents of ACCumulator from integer or double precision into single precision)
    2475
    POP BCC1
    Get the left hand operand into the registers. First POP the value from the STACK and put it in BC
    2476
    POP DED1
    and then get the value from the STACK and put it in DE
    2477-2479-  ↳ SNGDO
    LD HL,18B5HLD HL,SNGDSP21 B5 18
    Load HL with the starting address of the single precision arithmetic jump table
    247A-247B
    JR 2451HJR DODSP18 D5
    Jump back to 2451H to perform the operation held in Registe Pair HL

    247CH - Part of the Evaluation Routine- "FACSNG"

    According to the original ROM source code, at this point the ACCuumulator holds a single precision number and the STACK holds an integer.

    247C-  ↳ FACSNG
    POP HLE1
    Get the integer value from the STACK and put it in HL
    247D-247F
    CALL 09A4HCALL PUSHFCD A4 09
    Call 09A4 which moves the SINGLE PRECISION value in ACCumulator to the STACK (stored in LSB/MSB/Exponent order)
    2480-2482
    CALL 0ACFHCALL CONSIHCD CF 0A
    Go convert the integer value in HL to single precision
    2483-2485
    CALL 09BFHCALL MOVRFCD BF 09
    Call 09BF which loads the SINGLE PRECISION value (tyhe left hand operator) in ACCumulator into BC/DE
    2486
    POP HLE1
    Get the ACCumulator LSB/NMSB from the STACK and put it in HL
    2487-2489
    LD (4123H),HLLD (FAC-1),HL22 23 41
    Save the value in HL in ACCumulator
    248A
    POP HLE1
    Get the MSB and exponent from the STACK and put it in HL
    248B-248D
    LD (4121H),HLLD (FACLO),HL22 21 41
    Save the value in HL in ACCumulator
    248E-248F
    JR 2477HJR SNGDO18 E7
    Jump back to 2477H to perform the operation

    2490 - Integer divide- "INTDIV"

    (ACC=DE / HL) Result will be in single-precision (NTF=4) and will be in the ACC.
    Divides DE by HL. Both values are converted to single precision before the division is started. The quotient is left in REG l; the mode flag is updated. The orginal contents of the DE and HL Register sets are lost

    The original ROM source code points that we can't just live in integer world here, because we want 1/3 to be .3333 and not 0! So all arguments must be single precision even if they are integer

    Integer Division: Divides DE by HL. Both values are converted to single precision before the division is started. The quotient is left in the ACCumulator; the mode flag is updated. The orginal contents of the DE and HL Register sets are lost

    Single Precision Divide Divides the single precision value in (BC/DE) by the single precision value in the ACCumulator. The quotient is left in the ACCumulator.

    2490-  ↳ INTDIV
    PUSH HLE5
    Save the right hand argument (in HL) to the STACK
    2491
    EX DE,HLEB
    Exchange the left hand argument into HL and the right hand argument into DE
    2492-2494
    CALL 0ACFHCALL CONSIHCD CF 0A
    Go convert the integer value in HL to single precision and return with the result in the ACCumulator
    2495
    POP HLE1
    Get the right hand argument from the STACK and put it in HL
    2496-2498
    CALL 09A4HCALL PUSHFCD A4 09
    Call 09A4 which moves the SINGLE PRECISION value the ACCumulator (i.e., the converted left hand argument) to the STACK (stored in LSB/MSB/Exponent order)
    2499-249B
    CALL 0ACFHCALL CONSIHCD CF 0A
    Go convert the integer value in HL (i.e., the right hand argument) to single precision and return with the result the ACCumulator
    249C-249E
    JP 08A0HJP FDIVTC3 A0 08
    Go do a single precision divide

    249F - Evaluate a Variable, Constant, or Function Call- "EVAL"

    249F-  ↳ EVAL
    RST 10HCHRGETD7
    We need the next character in the BASIC program so 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.
    24A0-24A1
    LD E,28H1E 28
    Load Register E with a ?MO ERRORcode
    24A2-24A4
    JP Z,19A2HJP Z,ERRORCA A2 19
    Display a ?MO ERRORif we are at the end of the string
    24A5-24A7
    JP C,0E6CHJP C,FINDA 6C 0E
    If the character at the location of the current BASIC program pointer in HL is numeric, call the ASCII TO BINARY routine at 0E6C (which converts the ASCII string pointed to by HL to binary with the result in ACCumulator and the mode flag will have changed)
    24A8-24AA
    CALL 1E3DHCALL ISLETCD 3D 1E
    Check to see if the character at the location of the current BASIC program pointer in HL is alphabetic (as we are also looking to see if we have a variable name)
    24AB-24AD
    JP NC,2540HJP NC,ISVARD2 40 25
    If the character at the location of the current BASIC program pointer in HL is alphabetic then we have a variable, so JUMP to 1E3DH to deal with that.
    24AE-24AF
    CP 0CDHCP PLUSTKFE CD
    Check to see if the character at the location of the current BASIC program pointer in Register A is a +token. If they match, the Z FLAG is set, and otherwise the NZ FLAG is set. If A < the checked value, then the C FLAG is set. If A >= the checked value, the NC FLAG is set.
    24B0-24B1
    JR Z,249FHJR Z,EVAL28 ED
    We want to ignore any +token here, so if we have one, JUMP back to the top of this EVAL routine and keep parsing.
    24B2-24B3
    CP 2EHFE 2E
    Check to see if the character at the location of the current BASIC program pointer in Register A is a decimal point. If they match, the Z FLAG is set, and otherwise the NZ FLAG is set. If A < the checked value, then the C FLAG is set. If A >= the checked value, the NC FLAG is set.
    24B4-24B6
    JP Z,0E6CHJP Z,FINCA 6C 0E
    If the character at the location of the current BASIC program pointer in HL is a decimal point, call the ASCII TO BINARY routine at 0E6C (which converts the ASCII string pointed to by HL to binary with the result in ACCumulator and the mode flag will have changed)
    24B7-24B8
    CP 0CEHCP MINUTKFE CE
    Check to see if the character at the location of the current BASIC program pointer in Register A is a -token. If they match, the Z FLAG is set, and otherwise the NZ FLAG is set. If A < the checked value, then the C FLAG is set. If A >= the checked value, the NC FLAG is set.
    24B9-24BB
    JP Z,2532HJP Z,DOMINCA 32 25
    Process a negation via a jump to 2532H if the character at the location of the current BASIC program pointer in Register A is a -token
    24BC-24BD
    CP 22HFE 22
    Check to see if the character at the location of the current BASIC program pointer in Register A is a ".

    Notes:

    • 22H is a ". If they match, the Z FLAG is set, and otherwise the NZ FLAG is set. If A < the checked value, then the C FLAG is set. If A >= the checked value, the NC FLAG is set.
    24BE-24C0
    JP Z,2866HJP Z,STRLTICA 66 28
    If the character is a quote then we have a string constant, so jump to 2866H if the character at the location of the current BASIC program pointer in Register A is a "
    24C1-24C2
    CP 0CBHCP NOTTKFE CB
    Check to see if the character at the location of the current BASIC program pointer in Register A is a NOTtoken. If they match, the Z FLAG is set, and otherwise the NZ FLAG is set. If A < the checked value, then the C FLAG is set. If A >= the checked value, the NC FLAG is set.
    24C3-24C5
    JP Z,25C4HJP Z,NOTERCA C4 25
    Jump to 25C4H if the character at the location of the current BASIC program pointer in Register A is a NOTtoken
    24C6-24C7
    CP 26HFE 26
    Check to see if the character at the location of the current BASIC program pointer in Register A is a &. If they match, the Z FLAG is set, and otherwise the NZ FLAG is set. If A < the checked value, then the C FLAG is set. If A >= the checked value, the NC FLAG is set.
    24C8-24CA
    JP Z,4194HJP Z,OCTCNSCA 94 41
    Just to DOS to handle a &O; and &H;
    24CB-24CC
    CP 0C3HCP ERCTKFE C3
    Check to see if the character at the location of the current BASIC program pointer in Register A is an ERRtoken.

    Notes:

    • C3H is a ERRtoken.
    • A CP will return Z if there is a match against Register A, and NZ if not a match against Register A.
    24CD-24CE
    JR NZ,24D9HJP NZ,NTERC20 0A
    Jump to 24D9H if the character at the location of the current BASIC program pointer in Register A isn't an ERRtoken
    24CF
    RST 10HCHRGETD7
    We now need bump the value of 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.
    24D0-24D2
    LD A,(409AH)LD A,(ERRFLG)3A 9A 40
    Load Register A with the value of the current error number.
    Note: 409AH holds the RESUMEflag
    24D3-  ↳ NTDERC
    PUSH HLE5
    Save the value of the current BASIC program pointer in HL to the STACK
    24D4-24D6
    CALL 27F8HCALL SNGFLTCD F8 27
    Go save the value of the current error number in Register A (as an integer) as the current result in REG l
    24D7
    POP HLE1
    Get the value of the current BASIC program pointer from the STACK and put it in HL
    24D8
    RETC9
    RETurn to CALLer
    24D9-24DA-  ↳ NTERC
    CP C2HCP ERLTKFE C2
    Check to see if the character at the location of the current BASIC program pointer in Register A is a ERLtoken
    24DB-24DC
    JR NZ,24E7HR NZ,NTERL20 0A
    Jump to 24E7H if the character at the location of the current BASIC program pointer in Register A isn't an ERLtoken
    24DD
    RST 10HCHRGETD7
    We need the next character in the BASIC program so 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.
    24DE
    PUSH HLE5
    Save the value of the current BASIC program pointer in HL to the STACK
    24DF-24E1
    LD HL,(40EAH)LD HL,(ERRLIN)2A EA 40
    Load HL with the current error line number.
    Note: 40EAH-40EBH holds the line number with error
    24E2-24E3
    CALL 0C66HCALL INEG2CD 66 0C
    Go save the error line number in HL as the current result in REG1
    24E5
    POP HLE1
    Get the value of the current BASIC program pointer from the STACK and put it in HL
    24E6
    RETC9
    RETurn to CALLer

    24E7-24FEVARPTRlogic- "NTERL"
    24E7-24E8-  ↳ NTERL
    CP 0C0HCP $VARPTRFE C0
    Check to see if the character at the location of the current BASIC program pointer in Register A is a VARPTRtoken
    24E9-24EA
    JR NZ,24FFHJR NZ,NTVARP20 14
    Jump back to 24FFH if the character at the location of the current BASIC program pointer in Register A isn't a VARPTRtoken
    24EB
    RST 10HCHRGETD7
    We need the next character in the BASIC program so 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.
    24EC-24ED
    RST 08H ⇒ 28SYNCHK "("CF 28
    Since the character at the location of the current BASIC program pointer in HL must be a (, call the COMPARE SYMBOL routine at RST 08H.

    NOTE:The RST 08H routine 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 the next execution address (i.e, the RST 08H instruction + 2) with the next symbol in the A Register 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).
    24EE-24F0
    CALL 260DHCALL PTRGETCD 0D 26
    Call the FIND ADDRESS OF VARIABLE routine at 260DH which searches the Variable List Table for a variable name which matches the name in the string pointed to in HL, and return the address of that variable in DE (and if there is no variable, it creates it, zeroes it, and returns THAT location)
    24F1-24F2-  ↳ VARRET
    RST 08H ⇒ 29SYNCHK ")"CF 29
    Since the character at the location of the current BASIC program pointer in HL must be a ), call 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 the A Register 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)
    24F3
    PUSH HLE5
    Save the value of the current BASIC program pointer in HL to the STACK
    24F4
    EX DE,HLEB
    Swap DE and HL so that HL now holds the value to return
    24F5
    LD A,H7C
    Load Register A with MSB of the variable's address in Register H to make sure that it isn't undefined.
    24F6
    OR LB5
    Combine the LSB of the variable's address in Register L with the MSB of the variable's address in Register A. This type of pattern is used to check for something being zero
    24F7-24F9
    JP Z,1E4AHJP Z,FCERRCA 4A 1E
    Display a ?FC ERRORif the variable's address in HL is equal to zero, meaning that the variable is undefined
    24FA-24FC
    CALL 0A9AHCALL MAKINTCD 9A 0A
    Save the variable's address in HL as an integer into ACCumulator
    24FD
    POP HLE1
    Get the value of the current BASIC program pointer from the STACK and put it in HL
    24FE
    RETC9
    Return to the execution driver

    24FF - Other Function Routine - Jumped here if it wasn't VARPTR to see what else it might have been- "NTVARP"

    24FF-2500-  ↳ NTVARP
    CP 0C1HCP USRTKFE C1
    Check to see if the character at the location of the current BASIC program pointer in Register A is a USRtoken. If they match, the Z FLAG is set, and otherwise the NZ FLAG is set. If A < the checked value, then the C FLAG is set. If A >= the checked value, the NC FLAG is set.
    2501-2503
    JP Z,27FEHJP Z,USRFNCA FE 27
    Jump to 27FEH if the character at the location of the current BASIC program pointer in Register A is a USRtoken
    2504-2505
    CP 0C5HCP INSRTKFE C5
    Check to see if the character at the location of the current BASIC program pointer in Register A is a INSTRtoken. If they match, the Z FLAG is set, and otherwise the NZ FLAG is set. If A < the checked value, then the C FLAG is set. If A >= the checked value, the NC FLAG is set.
    2501-2503
    JP Z,419DHJP Z,INSTRCA FE 27
    If the character at the location of the current BASIC program pointer in Register A is a INSTRtoken, jump to DOS to deal with it.
    2509-250A
    CP 0C8HCP $MEMFE C8
    Check to see if the character at the location of the current BASIC program pointer in Register A is a MEMtoken. If they match, the Z FLAG is set, and otherwise the NZ FLAG is set. If A < the checked value, then the C FLAG is set. If A >= the checked value, the NC FLAG is set.
    250B-250D
    JP Z,27C9HJP Z,MEMCA C9 27
    If the character at the location of the current BASIC program pointer in Register A is a MEMtoken, jump to the RETURN AMOUNT OF FREE MEMORY routine at 27C9H which computes the amount of memory remaining between the end of the variable list and the end of the STACK and puts the result in ACCumulator as a SINGLE PRECISION number
    250E-250F
    CP 0C7HCP $TIMEFE C7
    Check to see if the character at the location of the current BASIC program pointer in Register A is a TIME$token. If they match, the Z FLAG is set, and otherwise the NZ FLAG is set. If A < the checked value, then the C FLAG is set. If A >= the checked value, the NC FLAG is set.
    250B-250D
    JP Z,417DHJP Z,TIMECA C9 27
    If the character at the location of the current BASIC program pointer in Register A is a MEMtoken, jump to DOS to deal with it.
    2513-2514
    CP 0C6HCP $POINTFE C6
    Check to see if the character at the location of the current BASIC program pointer in Register A is a POINTtoken. If they match, the Z FLAG is set, and otherwise the NZ FLAG is set. If A < the checked value, then the C FLAG is set. If A >= the checked value, the NC FLAG is set.
    2515-2517
    JP Z,0132HJP Z,POINTCA 32 01
    Jump to 0132H if the character at the location of the current BASIC program pointer in Register A is a POINTtoken
    2518-2519
    CP 0C9HCP $INKEYFE C9
    Check to see if the character at the location of the current BASIC program pointer in Register A is an INKEY$token. If they match, the Z FLAG is set, and otherwise the NZ FLAG is set. If A < the checked value, then the C FLAG is set. If A >= the checked value, the NC FLAG is set.
    251A-251C
    JP Z,019DHJP Z,INKEYCA 9D 01
    Jump to 019DH if the character at the location of the current BASIC program pointer in Register A is an INKEY$token
    251D-251E
    CP 0C4HCP $STRINGFE C4
    Check to see if the character at the location of the current BASIC program pointer in Register A is a STRING$token. If they match, the Z FLAG is set, and otherwise the NZ FLAG is set. If A < the checked value, then the C FLAG is set. If A >= the checked value, the NC FLAG is set.
    241F-2421
    JP Z,2A2FHJP Z,STRNG$7A
    Jump to 2A2FH if the character at the location of the current BASIC program pointer in Register A is a STRING$token
    2522-2523
    CP 0BEHCP FNTKFE BE
    Check to see if the character at the location of the current BASIC program pointer in Register A is a FNtoken. If they match, the Z FLAG is set, and otherwise the NZ FLAG is set. If A < the checked value, then the C FLAG is set. If A >= the checked value, the NC FLAG is set.
    241F-2421
    JP Z,4155HJP Z,FNDOER7A
    If the character at the location of the current BASIC program pointer in Register A is a FNtoken, jump to DOS to deal with it.
    2527-2528
    SUB 0D7HSUB ONEFUND6 D7
    Check to see if the character at the location of the current BASIC program pointer in Register A is a function name between the SGNand MID$token
    2529-252B
    JP NC,254EHJP NC,ISFUND2 4E 25
    Jump to 254EH if the character at the location of the current BASIC program pointer in Register A is a SGNto MID$token. The original ROM source notes that there is no need to check for an upper bound because functions are the highest allowed characters

    252CH - Other Function Routine - If we pass through to here, the only other possibility is that it is a function in parenthesis- "PARCHK"
    252C-252E-  ↳ PARCHK
    CALL 2335HCALL FRMPRNCD 35 23
    GOSUB to 2335H to recursively evaluate the expression at the location of the current BASIC program pointer in HL
    252F-2530
    RST 08H ⇒ 29SYNCHK ")"CF 29
    Since the character at the location of the current BASIC program pointer in HL must be a ), call the COMPARE SYMBOL routine at RST 08H.

    NOTE:The RST 08H routine 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 the next execution address (i.e, the RST 08H instruction + 2) with the next symbol in the A Register 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).
    2531
    RETC9
    Return out of this routine

    2532 - Binary Minus Routine- "DOMIN"

    2532-  ↳ DOMIN
    LD D,7DH16 7D
    Load Register D with a precedence value below "^" but above everything else since its a uniary minus.
    2534-2536
    CALL 233AHCALL LPOPERCD 3A 23
    GOSUB to 233AH to evaluate the variable at the location of the current BASIC program pointer in HL
    2537-2539
    LD HL,(40F3H)LD HL,(TEMP2)2A F3 40
    Load HL with the value of the current BASIC program pointer.
    Note: 40F3H-40F4H is a temporary storage location
    253A
    PUSH HLE5
    Save the value of the current BASIC program pointer in HL to the STACK so we know where to continue
    253B-253D
    CALL 097BHCALL VNEGCD 7B 09
    Invert the sign of the current value in ACCumulator
    253E-  ↳ LABBCK
    POP HLE1
    According to the original ROM source, this is where functions that don't return string values return. Restore the code string address from the STACK and put it in HL. We need this because we return here after executing functions SNG(to MID$(
    253F
    RETC9
    Return to the expression evaluation

    2540 - Math Routine- "ISVAR"

    According to the original ROM source code, this routine loads a variable to the ACC and sets the NTF. The HL must point to the ASCII variable name. After execution the HL will point to the character following the last character of the variable used. The value of the variable will be loaded in the ACC. For strings however (NTF=3), the ACC will contain the address of the first three bytes which contain the string length and string address (see Level II BASIC manual). Also note that if the variable cannot be found it will be created and given a value of zero.

    2540-2542-  ↳ ISVAR
    CALL 260DHCALL PTRGETCD 0D 26
    Get the pointer to the variable held in Register Pair DE by calling the FIND ADDRESS OF VARIABLE routine at 260DH which searches the Variable List Table for a variable name which matches the name in the string pointed to in HL, and return the address of that variable in DE (and if there is no variable, it creates it, zeroes it, and returns THAT location)
    2543-  ↳ RETVAR
    PUSH HLE5
    Save the value of the current BASIC program pointer in HL to the STACK
    2544
    EX DE,HLEB
    Swap DE and HL so that the pointer to the variable value is now held in Register Pair HL. This is the pointer to a descriptor, not the actual variable.
    2545-2547
    LD (4121H),HLLD (FACLO),HL22 21 41
    In case it is a string, we will store the pointer to the descriptor in FACLO.
    2548
    RST 20HGETYPEE7
    We need to check the value of the current number type flag, so we call the TEST DATA MODE routine at RST 20H.
    NOTE:The RST 20H routine determines the type of the current value in ACCumulator and returns a combination of STATUS flags and unique numeric values in the A Register according to the data mode flag (40AFH). The results are returned as follows:
    • 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.
    2549-254B
    CALL NZ,09F7HCALL NZ,VMOVFMC4 F7 09
    If we had a string, then we are just going to leave a pointer in the ACCumulator. If not, the we need to actually transfer the value into th ACCumulator using the pointer in Register Pair HL. With this, if that test shows we do NOT have a STRING, call 09F7H to move data
    254C
    POP HLE1
    Restore the value of the current BASIC program pointer to Register Pair HL
    254D
    RETC9
    Return to the caller

    254E - This routine processes an expression for SNG(to MID$(- "ISFUN"

    254E-254F-  ↳ ISFUN
    LD B,00H06 00
    Load Register B with zero
    2550
    RLCA07
    Set A to be 2 * (token - D7H)
    2551
    LD C,A4F
    Save the new token
    2552
    PUSH BCC5
    Save 0/2*(token-D7) on STACK
    2553
    RST 10HCHRGETD7
    Get the next character from the tokenized string by calling 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.
    2554
    LD A,C79
    Prepare to look for the function number
    2555-  ↳ NUMGFN
    CP 41HCP NUMGFNFE 41
    Test the adjusted token to see if it is past 2 * LASNUM - 2 * ONEFUN + 1. If they match, the Z FLAG is set, and otherwise the NZ FLAG is set. If A < the checked value, then the C FLAG is set. If A >= the checked value, the NC FLAG is set.
    2557
    JR C,256FHJR C,OKNORM38 16
    If the CARRY FLAG is set, then the function is past that last number, so JUMP to 256FH if the token is SGN(to CHR$(. If not, it is a LEFT$- MID$
    2559
    CALL 2335HCALL FRMPRNCD 35 23
    Otherwise, it must be a normal function so GOSUB to 2335H to capture the "(" and the first argument. This routine will evaluate the expression part of the calling sequence (which requires 2 or parameters)

    The original source code has this to say about being here:

    Most functions take a single argument. The return address of these functions is a small routine that checks to make sure valtyp is 0 (numeric) and pops off the text pointer. so normal functions that return string results (i.e. chr$) must pop off the return address of labbck, and pop off the text pointer and then return to FRMEVL.

    The so called "funny" functions can take more than one argument, the first of which must be string and the second of which must be a number between 0 and 256. The text pointer is passed to these functions so additional arguments can be read. The text pointer is passed in Register Pair DE. The close parenthesis must be checked and return is directly to FRMEVL with Register Pair HL setup as the text pointer pointing beyond the ")".

    The pointer to the descriptor of the string argument is stored on the STACK underneath the value of the integer argument (2 bytes).

    The first argument is ALWAY a string. The second is always an integer.

    255C
    RST 08H ⇒ 2CSYNCHK ","CF 2C
    We need TWO arguments, so there needs to be a ",". With this we use RST 08H to test for a ,
    255E
    CALL 0AF4HCALL CHKSTRCD F4 0A
    GOSUB to 0AF4H to ensure the current variable is a string, otherwise it is an error
    2561
    EX DE,HLEB
    Swap DE and HL so that DE will now point to the position in the current BASIC program being evaluated and HL is the address of the current variable (which MUST be a string)
    2562
    LD HL,(4121H)LD HL,(FACLO)2A 21 41
    Load HL with the string descriptor address held at the memory location pointed to by ACCumulator
    2565
    EX (SP),HLE3
    Put the pointer to the string descriptor onto the STACK and put the function number into Register Pair HL
    2566
    PUSH HLE5
    Save function number (i.e., 00 / 2*(token-D7H)) to the STACK
    2567
    EX DE,HLEB
    Swap DE and HL so that HL will now point to the position in the current BASIC program being evaluated.
    2568
    CALL 2B1CHCALL GETBYTCD 1C 2B
    Evaluate n portion of the string function. Register E will contain the value of the formula.
    256B
    EX DE,HLEB
    Swap DE and HL so that HL will now hold the integer value of the second argument and DE will point to the position in the current BASIC program being evaluated.
    256C
    EX (SP),HLE3
    Swap (SP) and HL so that HL will now hold the function number, and the integer value of the second argument will be at the top of the STACK
    256D
    JR 2583HJR FINGO18 14
    Jump down to 2583H to process the token
    256F-2571-  ↳ OKNORM
    CALL 252CHCALL PARCHKCD 2C 25
    Next we need to check out the argument (and make sure it is followed by a ")") via a single variable parameter call. First, GOSUB to 252CH to evaluate the expression at the location of the current BASIC program pointer in HL
    2572
    EX (SP),HLE3
    Swap (SP) and HL so that HL will now hold the function number [0 + 2 * (token - D7H)], and the pointer to the position in the current BASIC program being evaluated will be at the top of the STACK.

    The original source code has this to say about being here:

    We next have to check to see if a special coercion must be done for one of the transcendental functions (RND, SQR, COS, SIN, TAN, ATN, LOG, and EXP).

    Since these functions do not look at VALTYP, but rather assume the argument passed in the ACCumulator is single precision, we MUST call FRCSNG before dispatching to them.

    2573
    LD A,L7D
    Load Register A with the function number [i.e., 2 * (token - D7H)]
    2574-2575-  ↳ BOTCON
    CP 0CHCP (SQRTK-ONEFUN)*2FE 0C
    Check to see if the operator token in Register A is one less than SQR. If they match, the Z FLAG is set, and otherwise the NZ FLAG is set. If A < the checked value, then the C FLAG is set. If A >= the checked value, the NC FLAG is set.
    2576-2577
    JR C,257FHJR C,NOTFRF38 07
    Jump down to 257FH to avoid forcing the argument if the operator token in Register A is SGNto SQR
    2578-2579-  ↳ TOPCON
    CP 1BHCP (ATNTK-ONEFUN)*2+1FE 1B
    Check to see if the operator token in Register A is bigger than ATN(). If they match, the Z FLAG is set, and otherwise the NZ FLAG is set. If A < the checked value, then the C FLAG is set. If A >= the checked value, the NC FLAG is set.
    257A
    PUSH HLE5
    Save the function number (i.e., 0 + 2*(token - D7H)) to the stop of the STACK
    257B-257D
    CALL C,0AB1HCALL C,FRCSNGDC B1 0A
    If we are still here, then we need to force the ACCumulator into Single Precision, so CALL the CONVERT TO SINGLE PRECISION routine at 0AB1H (which converts the contents of ACCumulator [4121H] from integer or double precision into single precision)
    257F-2581-  ↳ NOTFRF
    LD DE,253EHLD DE,LABBCK11 3E 25
    Load DE with a return address of 253EH for once the function is executed
    2582
    PUSH DED5
    Save the value of the return address in DE to the STACK so it will act as the return adddress
    2583-2585-  ↳ FINGO
    LD BC,1608HLD BC,FUNDSP01 08 16
    Load BC with the function dispatch/jump table address
    2586-  ↳ DISPAT
    ADD HL,BC09
    Add the jump table pointer in BC (i.e., the offset) with the value of the operator token in HL
    2587
    LD C,(HL)4E
    Load Register C with the LSB of the jump address at the location of the jump table pointer in HL
    2588
    INC HL23
    Bump the value of the jump table pointer in HL
    2589
    LD H,(HL)66
    Load Register H with the MSB of the jump address at the location of the jump table pointer in HL
    258A
    LD L,C69
    Load Register L with the LSB of the jump address in Register C
    258B
    JP (HL)E9
    Perform the function.

    258C - Part of the Formula Evaluation Code- "STRCMP"

    According to the original ROM source, this routine will compare two strings, one with the description in Register Pair DE and the other in FACLO/FACLO+1. On exit:

    • A = 0 if the strings are equal
    • A = 377 if BCDE > FACLO
    • A = 1 if BCDE < FACLO
    This routine will do a relational comparison of two strings.
    It will load A with the length of the first string and BC with the string's address. Then it will load D with the length of the second string and HL with the string's address.
    258C-258E-  ↳ STRCMP
    CALL 29D7HCALL FRESTRCD D7 29
    First we need to free up the FAC string and get the pointer to the FAC descriptor into Register Pair HL. We do this via a GOSUB to 29D7H to check to see if there is enough memory for the string
    258F
    LD A,(HL)7E
    Load Register A with the LSB of the length of the string in the FACLO
    2590
    INC HL23
    Bump the value of the string's VARPTR in HL so that HL points to the LSB of the string address
    2591
    LD C,(HL)4E
    Load Register C with the LSB of the string in the FACLO
    2592
    INC HL23
    Bump the value of the string's VARPTR in HL so that HL points to the MSB of the string address
    2593
    LD B,(HL)46
    Load Register B with the MSB of the string in the FACLO. Register Pair BC now points to the FACLO string.
    2594
    POP DED1
    Put the STACK string pointer into Register Pair DE
    2595
    PUSH BCC5
    Save the pointer to the FACLO string data
    2596
    PUSH AFF5
    Save the FACLO string's length in Register A to the STACK
    2597-2599
    CALL 29DEHCALL FRETMPCD DE 29
    Free up the STACK string and RETURN with the pointer to the STACK string descriptor in Register Pair HL.
    259A
    POP DED1
    Get the length of the FACLO string from the STACK and put it in Register D
    259B
    LD E,(HL)5E
    Load Register E with the STACK / BCDE string's length
    259C
    INC HL23
    Bump the pointer to the STACK / BCDE string's entry in HL
    259D
    LD C,(HL)4E
    Load Register C with the LSB of the STACK / BCDE string's address
    259E
    INC HL23
    Bump the pointer to the STACK / BCDE string's entry in HL
    259F
    LD B,(HL)46
    Load Register B with the MSB of the STACK / BCDE string's address
    25A0
    POP HLE1
    Get the second character pointer from the STACK and put it in HL
    25A1-  ↳ CSLOOP
    LD A,E7B
    Load Register A with the length of the STACK / BCDE string in Register E
    25A2
    OR DB2
    Combine the FACLO string's length in Register D with the STACK / BCDE string's length in Register A
    25A3
    RET ZC8
    If both string lengths are the same, then we are done, so return out of the routine since there aren't any characters left in either string to be compared
    25A4
    LD A,D7A
    Load Register A with the FACLO string's length in Register D
    25A5-25A6
    SUB 01HD6 01
    Check to see if the FACLO string's length in Register A is equal to zero, by setting the CARRY FLAG and turning Register A into 255 if Register D was 0.
    25A7
    RET CD8
    If the CARRY FLAG is set, then the FACLO string has run out of characters, so RETurn
    25A8
    XOR AAF
    If we are here, then FACLO wasn't out of characters, so we next need to see if the STACK / BCDE string ended first. First we load Register A with zero
    25A9
    CP EBB
    Check to see if the STACK / BCDE string's length in Register E is equal to zero. If they match, the Z FLAG is set, and otherwise the NZ FLAG is set. If A < the checked value, then the C FLAG is set. If A >= the checked value, the NC FLAG is set.
    25AA
    INC A3C
    Bump the value in Register A for a return code of A = 1
    25AB
    RET NCD0
    Return if there aren't any more characters in the STACK / BCDE string to be compared
    25AC
    DEC D15
    If we are STILL here, then neither string has ended. First, decrement the value of the FACLO string's length in Register D
    25AD
    DEC E1D
    Decrement the value of the STACK / BCDE string's length in Register E
    25AE
    LD A,(BC)0A
    Load Register A with the character at the location of the STACK / BCDE string pointer in BC
    25AF
    CP (HL)BE
    Compare the character at the location of the STACK / BCDE string pointer in Register A with the character at the location of the FACLO string pointer in HL. If they match, the Z FLAG is set, and otherwise the NZ FLAG is set. If A < the checked value, then the C FLAG is set. If A >= the checked value, the NC FLAG is set.
    25B0
    INC HL23
    Bump the value of the first string pointer in HL
    25B1
    INC BC03
    Bump the value of the second string pointer in BC
    25B2-25B3
    JR Z,25A1HJR Z,CSLOOP28 ED
    Loop back to 25A1H to keep going if the characters match
    25B4
    CCF3F
    If we are here, then the NZ FLAG was set and the strings are NOT equal. Since they are not equal, reverse the CARRY flag
    25B5-25B7
    JP 0960HJP SIGNSC3 60 09
    Jump back to 0960H to set up Register A based on the CARRY FLAG.
    25B8-  ↳ DOCMP
    INC A3C
    Bump the value of the current precedence value in Register A
    25B9
    ADC A,A8F
    Adjust the value of the current precedence value in Register A. Depending on which routine called this this will give either (A) a 1 with a NC if 0 or a 0 with C if FF or (B) 4=Less, 2=Equal, 1=Greater
    25BA
    POP BCC1
    Get the last operator value from the STACK and put it in BC
    25BB
    AND BA0
    Combine the precedence value in Register B with the precedence value in Register A to see if any of the bits match.
    25BC-25BD
    ADD A,FFHC6 FF
    Adjust the value in Register A. This will give a 0 if both are equal and a CARRY if they are unequal
    25BE
    SBC A,A9F
    Check to see if the precedence values in registers A and B match. This will set A=0 if equal, and, depending on the routine which called this, either an A+1 or a A=377 if unequal
    25BF-25C1
    CALL 098DHCALL CONIACD 8D 09
    Convert Register A to a signed integer via a CALL to 098DH. This will set the current value to 00 if A is positive, and to FF if A is negative.
    If the values match, set the current result in zero. If they do not match, set the current result to -1
    25C2-25C3
    JR 25D6HJR RETAPG18 12
    At this point we want to return from the operator application place so the text pointer will get set up to what it was when LPOPER returned. To do this we jump forward to 25D6H to continue with the expression evaluation

    25C4 - NOTRoutine- "NOTER"

    25C4-25C5-  ↳ NOTER
    LD D,5AH16 5A
    NOT has a precedence value of 90, so we need a dummy entry of 90 on the STACK
    25C6-25C8
    CALL 233AHCALL LPOPERCD 3A 23
    Go evaluate the expression with a dummy entry of 90 on the STACK
    25C9-25CB
    CALL 0A7FHCALL FRCINTCD 7F 0A
    We need the argument to be an integer so CALL the CONVERT TO INTEGER routine at 0A7FH (where the contents of ACCumulator are converted from single or double precision to integer and deposited into HL)
    25CC
    LD A,L7D
    The next bunch of instructions are to complement Register Pair HL. First, load Register A with the LSB of the integer value
    25CD
    CPL2F
    Compliment the LSB of the integer value in Register A
    25CE
    LD L,A6F
    Load Register L with the adjusted LSB of the integer value in Register A
    25CF
    LD A,H7C
    Load Register A with the MSB of the integer value in Register H
    25D0
    CPL2F
    Compliment the MSB of the integer value in Register A
    25D1
    LD H,A67
    Load Register H with the adjusted MSB of the integer value in Register A
    25D2-25D4
    LD (4121H),HLLD (FACLO),HL22 21 41
    Save the complimented integer value in HL as the current result in ACCumulator
    25D5
    POP BCC1
    Clean up the STACK
    25D6-25D8-  ↳ RETAPG
    JP 2346HJP RETAOPC3 46 23
    Jump back to 2346H to continue with the expression evaluation. We need to do this because FRMEVL, after seeing a precedence level of 90, thinks it is applying an operator, which it isn't. It has the text pointer stored in TEXT 2 so we need to JUMP to RETAOP to re-fetch that pointer.

    25D9 - The RST 20H code- "GETYPR"

    This is the TEST DATA MODE, which determines the type of the current value in ACCumulator and returns a combination of STATUS flags and unique numeric values in the A Register according to the data mode flag (40AFH). TYPE   CODE   ZERO   CARRY   NEG   PARITY   A-Register
    INT     02     NZ      C      N      E         -1
    STR     03      Z      C      P      E          0
    SNG     04     NZ      C      P      O          1
    DBL     08     NZ     NC      P      E          5

    25D9-25DBH-  ↳ GETPYR
    LD A,(40AFH)LD A,(VALTYP)3A AF 40
    Load Register A with the current value of the number type flag.
    Note: 40AFH holds Current number type flag
    25DC-25DD
    CP 08HFE 08
    Check to see if the current value in ACCumulator is double precision (02=INT, 03=STR, 04=SNG, 08=DBL). If they match, the Z FLAG is set, and otherwise the NZ FLAG is set. If A < the checked value, then the C FLAG is set. If A >= the checked value, the NC FLAG is set.
    25DE-25DF-  ↳ CGETYP
    JR NC,25E5HJR NC,NCASE30 05
    If that test shows that we have a DOUBLE PRECISION number, jump forward to 25E5H
    25E0-25E1
    SUB 03HD6 03
    If the number is not double precision, subtract 3
    25E2
    OR AB7
    Set the status flags of the adjusted number type flag in Register A
    25E3
    SCF37
    Set the Carry flag
    25E4
    RETC9
    RETurn to CALLer
    25E5-25E6-  ↳ NCASE
    SUB 03HD6 03
    We are dealing with a double precision number so adjust the value of the current number type flag in Register A
    25E7
    OR AB7
    Test the value of the current number type flag in Register A, which will exit without the CARRY flag set
    25E8
    RETC9
    RETurn to CALLer

    25E9 - ANDand ORRoutines- "DANDOR"

    According to the original ROM source, this routine applies the ANDand ORoperators and should be used to implement all logical operators

    Whenever an operator is applied, its precedence is in Register B

    This fact is used to distinguish between ANDand OR

    The right hand argument is coerced to integer, just as the left hand one was when it was pushed on the STACK

    25E9-  ↳ DANDOR
    PUSH BCC5
    B has he precedence value, so save BC to the STACK. The precedence value for OR is 70.
    25EA-25EC
    CALL 0A7FHCALL FRCINTCD 7F 0A
    We need the right hand argument to be an integer, so GOSUB the CONVERT TO INTEGER routine at 0A7FH (where the contents of ACCumulator are converted from single or double precision to integer and deposited into HL)
    25ED
    POP AFF1
    Get the precedence value from the STACK and put it in Register A so that we can detemined between span class="code">ANDand OR
    25EE
    POP DED1
    Get the left hand argument from the STACK and put it in DE
    25EF-25F1
    LD BC,27FAHLD BC,GIVINT01 FA 27
    Load BC with a return address of 27FAH
    25F2
    PUSH BCC5
    Save the value of the return address in BC to the STACK
    25F3-25F4
    CP 46HFE 46
    Check to see if the operator value in Register A is an ORtoken. If they match, the Z FLAG is set, and otherwise the NZ FLAG is set. If A < the checked value, then the C FLAG is set. If A >= the checked value, the NC FLAG is set.
    25F5-25F6
    JR NZ,25FDHJR NZ,NOTOR20 06
    If Register A was not 46H then we have an AND, so jump forward a few instructions to 25FDH (to the ANDcode) if the operator value in Register A isn't an ORtoken

    25F7 - ORlogic.

    25F7
    LD A,E7B
    Load Register A with the LSB of the first value in Register E
    25F8
    OR LB5
    Combine the LSB of the first value in Register A with the LSB of the second value in Register L
    25F9
    LD L,A6F
    Load Register L with the ORed value in Register A
    25FA
    LD A,H7C
    Load Register A with the MSB of the second value in Register H
    25FB
    OR DB2
    Combine the MSB of the first value in Register D with the MSB of the second value in Register A
    25FC
    RETC9
    Return to 27FAH (=convert the result to integer and return that integer calue to 2346H)

    25FD - ANDlogic- "NOTOR"

    25FD-  ↳ NOTOR
    LD A,E7B
    Load Register A with the LSB of the first value in Register E
    25FE
    AND LA5
    Combine the LSB of the first value in Register A with the LSB of the second value in Register L
    25FF
    LD L,A6F
    Load Register L with the ANDed value in Register A
    2600
    LD A,H7C
    Load Register A with the MSB of the second value in Register H
    2601
    AND DA2
    Combine the MSB of the first value in Register D with the MSB of the second value in Register A
    2602
    RETC9
    Return to 27FAH (=Make the result an integer and return to 2346H)

    2603 - Dimension and Variable Searching Routine- "DIMCON"

    2603-  ↳ DIMCON
    DEC HL2B
    Decrement the value of the BASIC program pointer in HL so that we can see what the prior character was
    2604
    RST 10HCHRGETD7
    We need the next character in the BASIC program so 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.
    2605
    RET ZC8
    If the CHGRGET routine returned a Z FLAG, then we have a terminator; so RETurn since this is the end of the BASIC statement
    2606-2607
    RST 08H ⇒ 2CSYNCHK ","CF 2C
    Since the character at the location of the current BASIC program pointer in HL must be a ,, call the COMPARE SYMBOL routine at RST 08H.

    NOTE:The RST 08H routine 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 the next execution address (i.e, the RST 08H instruction + 2) with the next symbol in the A Register 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).

    2608 - DIMlogic- "DIM"

    The original ROM source code states that this DIM code sets DIMFLG and then falls into the variable search routine, which then looks at DIMFLG at three different points:

    1. If an entry is found, dimflg being on indicates a "doubly dimensioned" variable
    2. When a new entry is being built dimflg's being on indicates the indices should be used for the size of each indice. otherwise the default of ten is used.
    3. When the build entry code finishes, only if dimflg is off will indexing be done
    2608-260A-  ↳ DIM
    LD BC,2603HD BC,DIMCON01 03 26
    Load BC with a return address of 2603H
    260B
    PUSH BCC5
    Save the return address of 2603H (in BC) to the STACK
    260C-260D
    OR AFHF6 AF
    This is part of a Z-80 trick. If passed through, then this will just OR A to force A to be non-zero. It will then skip the next instruction

    260D - Variable location and creation logic- "PTRGET".

    The original ROM source code states that this routine will read the variable name at the current text position and put a pointer to its value in Register Pair DE. Register Pair HL is then updated to point to the character after the variable name and VALTYP is set up. Evaluating subscripts in a variable name can cause recursive calls to PTRGET so at that point all values must be stored on the STACK. On RETurn, [a] does not reflect the value of the terminating character

    This routine will return the address of a variable in memory or create it if it is not found. In order to use this routine, the HL must point to the variable name (ASCII). Then, after execution, HL will point to the character following the variable name and the location of the variable will be returned in the DE Register Pair. For integer, single or doubleprecision (NTF=2, 4 or 8) the address returned in DE will be the same as for the VARPTR command under BASIC. (see Level II BASIC manual on VARPTR) For strings (NTF=3) however the address returned in DE will point to the first of three bytes containing the string length and string address.
    This entry point searches the Variable List Table (VLT) for a variable name which matches the name in the string pointed to by HL. If the variable exists, its address is returned in DE. If it is not defined, then it is created with an initial value ofzero and its address is returned in DE. Dimensioned and non-dimensioned variables may be located, and suffixs for data mode may be included in the name string. A byte of machine zeros must terminate the name string. All registers are used.

    260D-  ↳ PTRGET
    XOR AAF
    If JUMPed here, then A is set to ZERO. As a reminder, if passed through from the above routine, A will be NOT ZERO.
    260E-2610
    LD (40AEH),ALD (DIMFLG),A32 AE 40
    Save the value in Register A as the current variable location/creation flag.
    Note: 40AEH holds LOCATE/CREATE variable flag
    2611
    LD B,(HL)46
    Load Register B with the first character of the variable name
    2612-2614-  ↳ PTRGT2
    CALL 1E3DHCALL ISLETCD 3D 1E
    GOSUB to 1E3DH to make sure the first character of the variable name is a letter
    2615-2617
    JP C,1997HJP C,SNERRDA 97 19
    Display a ?SN ERRORif the first character of the variable name isn't a letter
    2618
    XOR AAF
    Zero Register A
    2619
    LD C,A4F
    Set up to assume that there is no second character by zeroing Register C
    261A
    RST 10HCHRGETD7
    We need the next character in the BASIC program so 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.
    261B-261C
    JR C,2622HJR C,ISSEC38 05
    Jump to 2622H if the character at the location of the current BASIC program pointer in Register A is numeric
    261D-261F
    CALL 1E3DHCALL ISLETCD 3D 1E
    GOSUB to 1E3DH to check to see if the character at the location of the current BASIC program pointer is a letter. This will set the CARRY FLAG.
    2620-2621
    JR C,262BHJR C,NOSEC38 09
    Jump to 262BH if the character at the location of the current BASIC program pointer in Register A isn't a letter
    2622-  ↳ ISSEC
    LD C,A4F
    If we are here, then the second character was a number, so save it in Register C
    2623-  ↳ EATEM
    RST 10HCHRGETD7
    We now need bump the value of 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.
    2624-2625
    JR C,2623HJR C,EATEM38 FD
    Loop back one OPCODE to keep eating characters until a non-numeric character is found
    2626-2628
    CALL 1E3DHCALL ISLETCD 3D 1E
    Go check to see if the character at the location of the current BASIC program pointer in HL is alphabetic
    2629-262A
    JR NC,2623HJR NC,EATEM30 F8
    Jump back to 2623H if the character at the location of the current BASIC program pointer in HL is alphabetic
    262B-262D-  ↳ NOSEC
    LD DE,2652HLD DE,HAVTYP11 52 26
    Load DE with a return address of 2652H. Done to save time/RAM from using JUMPs instead.
    262E
    PUSH DED5
    Save the value of the return address in DE to the STACK
    262F-2630
    LD D,02H16 02
    Load Register D with an integer number type flag
    2631-2632
    CP 25HFE 25
    Check to see if the character at the location of the current BASIC program pointer in Register A is a %. If they match, the Z FLAG is set, and otherwise the NZ FLAG is set. If A < the checked value, then the C FLAG is set. If A >= the checked value, the NC FLAG is set.
    2633
    RET ZC8
    Return if the character at the location of the current BASIC program pointer in Register A is a %
    2634
    INC D14
    Bump Register D so that it will be equal to a string number type flag (02=INT, 03=STR, 04=SNG, 08=DBL)
    2635-2636
    CP 24HFE 24
    Check to see if the character at the location of the current BASIC program pointer in Register A is a $. If they match, the Z FLAG is set, and otherwise the NZ FLAG is set. If A < the checked value, then the C FLAG is set. If A >= the checked value, the NC FLAG is set.
    2637
    RET ZC8
    Return if the character at the location of current BASIC program pointer in Register A is a $
    2638
    INC D14
    Bump Register D so that it will be equal to a single precision number type flag (02=INT, 03=STR, 04=SNG, 08=DBL)
    2639-263A
    CP 21HFE 21
    Check to see if the character at the location of the current BASIC program pointer in Register A is a !. If they match, the Z FLAG is set, and otherwise the NZ FLAG is set. If A < the checked value, then the C FLAG is set. If A >= the checked value, the NC FLAG is set.
    263B
    RET ZC8
    Return if the character at the location of the current BASIC program pointer in Register A is a !
    263C-263D
    LD D,08H16 08
    Load Register D with a double precision number type flag. We have to do this because 04H (SNG) would bump to 05H if we just did another INC, but we need 08H for DBL
    263E-263F
    CP 23HFE 23
    Check to see if the character at the location of the current BASIC program pointer in Register A is a #. If they match, the Z FLAG is set, and otherwise the NZ FLAG is set. If A < the checked value, then the C FLAG is set. If A >= the checked value, the NC FLAG is set.
    2640
    RET ZC8
    Return if the character at the location of the current BASIC program pointer in Register A is a #
    2641
    LD A,B78
    Load Register A with the first character of the variable name from Register B
    2642-2643
    SUB 41HD6 41
    Adjust the value of the first character of the variable name in Register A so that it is in the range of 0 to 25 for a table offset
    2644-2645
    AND 7FHE6 7F
    Get rid of the user-defined function bit in Register B by ANDing it against 0111 1111
    2646
    LD E,A5F
    Next we need to build a two byte offset so first load Register E with the adjusted first character of the variable name in Register A
    2647-2648
    LD D,00H16 00
    Load Register D with zero
    2649
    PUSH HLE5
    Save the value of the current BASIC program pointer in HL to the STACK
    264A-264C
    LD HL,4101HLD HL,DEFTBL21 01 41
    Load HL with the starting address of the variable declaration table.
    NOTE:4101H-411AH holds Variable Declaration Table
    264D
    ADD HL,DE19
    Add the offset to the top of the table
    264E
    LD D,(HL)56
    Load Register D with the number type value from the variable declaration table pointer in HL
    264F
    POP HLE1
    Get the value of the current BASIC program pointer from the STACK and put it in HL
    2650
    DEC HL2B
    Decrement the value of the current BASIC program pointer in HL as there was no marking character
    2651
    RETC9
    Return with data type in D

    2652-  ↳ HAVTYP
    LD A,D7A
    Load Register A with the value of the number type flag in Register D
    2653-2655
    LD (40AFH),ALD (VALTYP),A32 AF 40
    Save the number type flag for the current variable name from Register A.
    NOTE:40AFH holds Current number type flag
    2656
    RST 10HCHRGETD7
    We want to skip the "type marker" so we need the next character in the BASIC program so 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.
    2657-2659
    LD A,(40DCH)LD A,(SUBFLG)3A DC 40
    Load Register A with the FORflag. Why the FOR flag? It doubles as a "should we allow arrays here" flag!
    265A
    OR AB7
    Test the value of the FORflag in Register A
    265B-265D
    JP NZ,2664HJP NZ,NOARYSC2 64 26
    Jump to 2664H if a arrays are not permitted
    265E
    LD A,(HL)7E
    Re-fetch the next element of the code string by loading Register A with the character at the location of the current BASIC program pointer in HL
    265F-2660
    SUB 28HD6 28
    Next, test for an array by checking to see if the character at the location of the current BASIC program pointer in Register A is a (
    2661-2663
    JP Z,26E9HJP Z,ISARYCA E9 26
    If the Z FLAG is set then we have an array (meaning, it is a subscripted variable), so JUMP to 26E9H
    2664-  ↳ NOARYS
    XOR AAF
    Zero Register A so that we can allow for parenthesis now
    2665-2667
    LD (40DCH),ALD (SUBFLG),A32 DC 40
    Set the "permit arrays" array flag to 'no subscript'.
    Note: 40DCH holds FORflag
    2668
    PUSH HLE5
    Save the value of the current BASIC program pointer in HL to the STACK
    2669
    PUSH DED5
    Save the number type flag for the variable in DE to the STACK
    266A-266C
    LD HL,(40F9H)LD HL,(VARTAB)2A F9 40
    Load HL with the value of the simple variables pointer, which will be t he place to start the search.
    • Note: 40F9H-40FAH holds the starting address of the simple variable storage area.
    266D-  ↳ LOPFND
    EX DE,HLEB
    Swap DE and HL so that DE will not point to the place to start the search. We don't care what happens to HL
    266E-2670
    LD HL,(40FBH)LD HL,(ARYTAB)2A FB 40
    Load HL with the pointer to the end of simple variables. 40FBH-40FCH holds the starting address of the BASIC array variable storage area
    2671
    RST 18HCOMPARDF
    Now we need to see if we have reached the end of the table so we compare the value of the simple variables pointer in DE with the array variables pointer in HL, so we call the COMPARE DE:HL 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.
    2672
    POP HLE1
    Get the number type flag for the variable from STACK and put it in HL
    2673-2674
    JR Z,268EHJR Z,NOTFNS28 19
    If the Z FLAG is set (because the simple variables pointer in DE is greater than or equal to the array variables pointer) then the variable was not found, and so we need to create a new variable. To do this we JUMP to 268EH
    2675
    LD A,(DE)1A
    Load Register A with the number type flag for the variable at the location of the simple variables pointer in DE
    2676
    LD L,A6F
    Preserve Register A into Register L so we know how many entries to skip.
    2677
    CP HBC
    Compare the number type flag for the variable in Register H to the number type flag in Register A. If they match, the Z FLAG is set, and otherwise the NZ FLAG is set. If A < the checked value, then the C FLAG is set. If A >= the checked value, the NC FLAG is set.
    2678
    INC DE13
    Bump the value of the current simple variables pointer in DE to the 2nd character name for this entry
    2679-267A
    JR NZ,2686HJR NZ,NOTIT120 0B
    If the NZ FLAG is set then we did not have the right type of variable and need to skip it VIA a JUMP to 2686H
    267B
    LD A,(DE)1A
    Since the type matches, compare the 1st characters by loading Register A with the first character of variable name at the location of the simple variables pointer in DE
    267C
    CP CB9
    Compare the character at the location of the simple variables pointer in Register A with the first character of the variable name in Register C. If they match, the Z FLAG is set, and otherwise the NZ FLAG is set. If A < the checked value, then the C FLAG is set. If A >= the checked value, the NC FLAG is set.
    267D-2637
    JR NZ,2686HJR NZ,NOTIT120 07
    Jump to 2686H if the first characters of the variable names don't match
    267F
    INC DE13
    Bump the value of the current simple variables pointer in DE
    2680
    LD A,(DE)1A
    Load Register A with the second character of the variable name at the location of the simple variables pointer in DE
    2681
    CP BB8
    Compare the character at the location of the simple variables pointer in Register A with the first character of the variable name in Register B. If they match, the Z FLAG is set, and otherwise the NZ FLAG is set. If A < the checked value, then the C FLAG is set. If A >= the checked value, the NC FLAG is set.
    2682-2684
    JP Z,26CCHJP Z,FINPTRCA CC 26
    Jump to 26CCH if the character at the location of the simple variables pointer in Register A matches the first character of the variable name in Register B
    2685-2686
    LD A,13H3E 13
    Z-80 Trick to skip the next INC DE if continuing through
    2686-  ↳ NOTIT1
    INC DE13
    Bump to the next entry in the simple variable list part 1
    2687
    INC DE13
    Bump the value of the simple variables pointer in DE part 2
    2688
    PUSH HLE5
    Save the number type flag for the variable in HL to the STACK so that it can be re-loaded at 2672H
    2689-268A
    LD H,00H26 00
    Load Register H with zero so that Register Pair HL is the number of bytes to skip, but in 16 bits.
    268B
    ADD HL,DE19
    Add the value of the simple variables pointer in DE to the value of the number type flag in HL
    268C-268D
    JR 266DHJR LOPFND18 DF
    Loop back to 266DH to keep searching
    268E-  ↳ NOTFNS
    LD A,H7C
    Load Register A with the length for the variable in Register H
    268F
    POP HLE1
    Get the value of the current BASIC program pointer from the STACK and put it in HL
    2690
    EX (SP),HLE3
    Exchange (SP) and HL so that Register Pair HL now holds the return address and the value of the current BASIC program pointer is now at the top of the STACK
    2691
    PUSH AFF5
    Save length of the variable in Register A to the STACK
    2692
    PUSH DED5
    Save the current variable table position from DE to the STACK
    2693-2695
    LD DE,24F1HLD DE,VARRET11 F1 24
    Load DE with a VARPTR locator return address of 24F1H
    2696
    RST 18HCOMPARDF
    Now we need to check to see if this was a VARPTR or not, so we compare the return address in DE with the return address in HL by calling the COMPARE DE:HL 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.
    2697-2698
    JR Z,26CFHJR Z,VARNOT28 36
    If the Z FLAG is set, then this was a VARPTR call, so JUMP forward to 26CFH.
    2699-269B
    LD DE,2543HLD DE,RETVAR11 43 25
    Next we need to see if EVAL called this routine. Load DE with a return address of the find address of variables routine at 2543H
    269C
    RST 18HCOMPARDF
    We need to see if we were called from the 'find address of variable' routine so we need to compare the return address in DE with the return address in HL, so we call the COMPARE DE:HL 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.
    269D
    POP DED1
    Restore the current variable table position from the STACK and put it in DE
    269E-269F
    JR Z,26D5HJR Z,FINZER28 35
    If this routine is called to locate the variables address, we JUMP to 26D5H as we do not need to create a new variable, and instead we zero out the FAC and skip the RETurn.
    26A0
    POP AFF1
    Clear the STACK and put the value of the number type flag for the variable from the STACK and put it in Register A
    26A1
    EX (SP),HLE3
    Swap (SP) and HL so that the value of the current BASIC program pointer is now in Register Pair HL
    26A2
    PUSH HLE5
    Save the value of the current BASIC program pointer in HL to the STACK
    26A3
    PUSH BCC5
    Save the variable's address in BC to the STACK as we are about to use both Register B and Register C
    26A4
    LD C,A4F
    Load Register C with the value of the number type flag for the variable in Register A
    26A5-26A6
    LD B,00H06 00
    Load Register B with zero so that the number type flag for the variable can be represented in 16 bits
    26A7
    PUSH BCC5
    Save the variable's number type flag in BC to the STACK
    26A8
    INC BC03
    Bump the value of the variable's number type flag in BC
    26A9
    INC BC03
    Bump the value of the variable's number type flag in BC
    26AA
    INC BC03
    Bump the value of the variable's number type flag in BC. Now the variable's length includes room for the addresses as well.
    26AB-26AD
    LD HL,(40FDH)LD HL,(STREND)2A FD 40
    Load HL with the value of the free memory pointer.
    Note: 40FDH-40FEH holds Free memory pointer
    26AE
    PUSH HLE5
    Save the value of the free memory pointer in HL to the STACK
    26AF
    ADD HL,BC09
    Add the value of the variable's number type flag in BC to the value of the free memory pointer in HL
    26B0
    POP BCC1
    Restore the high address from the STACK and put it in BC
    26B1
    PUSH HLE5
    Save the value of the high address pointer in HL to the STACK
    26B2-26B4
    CALL 1955HCALL BLTUCD 55 19
    Block transfer the variable information and make sure we do not overflow the STACK space via a GOSUB to BLTU.
    26B5
    POP HLE1
    Get the value of the new free memory pointer (i.e., STREND) from the STACK and put it in HL
    26B6-26B8
    LD (40FDH),HLLD (STREND),HL22 FD 40
    Save the value of the new free memory pointer in HL to lock in that variable space.
    NOTE:40FDH-40FEH holds Free memory pointer
    26B9
    LD H,B60
    Load Register H with the MSB of the new array variables pointer in Register B
    26BA
    LD L,C69
    Load Register L with the LSB of the new array variables pointer in Register C. HL will now point to the end of the new variable.
    26BB-26BD
    LD (40FBH),HLLD (ARYTAB),HL22 FB 40
    Save the value of the new array variables pointer in HL. 40FBH-40FCH holds the starting address of the BASIC array variable storage area
    26BE-  ↳ ZEROER
    DEC HL2B
    At this point, HL is retuned pointing to the end of the variable, so we need to zero backwards to DE which points to the start of the variable table. First, decrement the value of the array variables pointer in HL
    26BF-26C0
    LD (HL),00H36 00
    Zero the location of the memory pointer in HL
    26C1
    RST 18HCOMPARDF
    Now we need to check to see if the variable has been completely zeroed, so we call the COMPARE DE:HL 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.
    26C2-26CJ
    JR NZ,26BEHR NZ,ZEROER20 FA
    Loop back to 26BEH until the variable has been cleared
    26C4
    POP DED1
    Get the value of the variable's number type flag from the STACK and put it in DE
    26C5
    LD (HL),E73
    Save the value of the number type flag in Register E at the location of the memory pointer in HL
    26C6
    INC HL23
    Bump the value of the memory pointer in HL
    26C7
    POP DED1
    Get the 2nd character of the variable's name from the STACK and put it in DE
    26C8
    LD (HL),E73
    Save the first character of the variable's name in Register E at the location of the memory pointer in HL
    26C9
    INC HL23
    Bump the value of the memory pointer in HL
    26CA
    LD (HL),D72
    Save the first character of the variable's name in Register D at the location of the memory pointer in HL
    26CB
    EX DE,HLEB
    Load DE with the value of the variable pointer in from HL
    26CC-  ↳ FINPTR
    INC DE13
    Bump the value of the variable pointer in DE so that it points to the value
    26CD
    POP HLE1
    Restore the value of the current BASIC program pointer from the STACK into Register Pair HL
    26CE
    RETC9
    RETurn to CALLer
    26CF-  ↳ VARNOT
    LD D,A57
    On entry, the Z FLAG was set, meaning that A=0. Zero out Register D with the value of Register AA
    26D0
    LD E,A5F
    Zero out Register E
    26D1
    POP AFF1
    Clean up the STACK (which was the PUSHed DE)
    26D2
    POP AFF1
    Clean up the STACK (which was the length)
    26D3
    EX (SP),HLE3
    Swap (SP) and HL so that the return return is now at the top of the STACK and the pointer in current BASIC program pointer is restored to HL
    26D4
    RETC9
    Return to the VARPTRroutine

    26D5 - This routine is ZERO out all variable types and skip any RETurn- "FINZER"
    26D5-26D7-  ↳ FINZER
    LD (4124H),ALD (FAC),A32 24 41
    Zero ACCumulator so that all single-precision and double-precision variables become zero
    26D8
    POP BCC1
    Clean up the STACK (i.e., remove the length of the variable)
    26D9
    LD H,A67
    Zero Register H to clear out integers as well
    26DA
    LD L,A6F
    Zero Register L to clear out integers as well
    26DB-26DD
    LD (4121H),HLLD (FACLO),HL22 21 41
    Zero the string pointer location in ACCumulator
    26DE
    RST 20HGETYPEE7
    We need to check the value of the current number type flag, so we call the TEST DATA MODE routine at RST 20H.
    NOTE:The RST 20H routine determines the type of the current value in ACCumulator and returns a combination of STATUS flags and unique numeric values in the A Register according to the data mode flag (40AFH). The results are returned as follows:
    • 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.
    26DF-26E0
    JR NZ,26E7HJR NZ,POPHR220 06
    If the NZ FLAG is set then we do NOT have a string, in which case we are done because we have zeroed out all the number types. JUMP to 26E7H
    26E1-26E3
    LD HL,1928HLD HL,REDDY-121 28 19
    If we are here, then we have a string, and need to zero that out. First, load HL with the character before the starting address of the Level II BASIC READY message, which is a 00H!
    26E4-26E6
    LD (4121H),HLLD (FACLO),HL22 21 41
    Save the value in HL as the current string pointer, which is now null.
    26E7-  ↳ POPHR2
    POP HLE1
    Get the value of the current BASIC program pointer from the STACK and put it in HL
    26E8
    RETC9
    RETurn (from the EVAL routine) to CALLer

    26E9 - This routine handles a subscripted variables- "ISARY"

    According to the original ROM source, ARRAYs have the following format:

    • Descriptor - Low Byte - Second Character (200 bit is string)
    • Descriptor - High Byte - First character
    • Length of array in core in bytes
    • Number of dimensions (1 byte)
    • Then, for each dimension starting with the first, a list of the max index+1 (2 bytes each)
    • The associated value
    On entry D = the type of variable, B = the 1st character of the variable name, C = the 2nd character of the variable name, and HL = the current position in the input string.
    26E9-  ↳ ISARY
    PUSH HLE5
    Save the DIMFLG and VALTYP for recursion
    26EA-26EC
    LD HL,(40AEH)LD HL,(DIMFLG)2A AE 40
    Load HL with the value of the locate/create flag.
    Note: 40AEH holds LOCATE/CREATE variable flag and will be a 0 if in locate mode and anything other than zero if in create mode
    26ED
    EX (SP),HLE3
    Swap (SP) and HL so that the the value of the current BASIC program pointer is back into Register Pair HL, and the DIMFLG and VALTYP are moved to the top of the STACK
    26EE
    LD D,A57
    Zero Register D (which will hold the number of dimension)
    26EF-  ↳ INDLOP
    PUSH DED5
    Save the number of dimension (held in Register D) to the STACK
    26F0
    PUSH BCC5
    Save the variable's name in BC to the STACK
    26F1-26F3
    CALL 1E45HCALL INTIDXCD 45 1E
    Go evaluate the array subscript/index at the location of the current BASIC program pointer in HL up to a )or ,. Return with the binary value in DE
    26F4
    POP BCC1
    Get the variable's name from the STACK (1st and 2nd character) and put it in BC
    26F5
    POP AFF1
    Get the variable's number of dimension so far from the STACK and put it in Register A
    26F6
    EX DE,HLEB
    Swap DE and HL, so that DE will now be the value of the current BASIC program pointer and HL will now hold the array subscript
    26F7
    EX (SP),HLE3
    Swap (SP) and HL so that HL will now hold DIMFLG and VALTYP and the array subscript will be at the stop of the STACK
    26F8
    PUSH HLE5
    Save the DIMGFLG and VALTYP (in HL) to the STACK
    26F9
    EX DE,HLEB
    Swap DE and HL so that the value of the current BASIC program pointer is now in Register Pair HL, and the DIMFLG and VALTYP are now in DE.
    26FA
    INC A3C
    Bump the number of dimensions evaluated in Register A
    26FB
    LD D,A57
    Load Register D with the number of dimensions evaluated in Register A
    26FC
    LD A,(HL)7E
    Load Register A with the character at the location of the current BASIC program pointer in HL
    26FD-26FE
    CP 2CHFE 2C
    Check to see if the character at the location of the current BASIC program pointer in Register A is a ,. If they match, the Z FLAG is set, and otherwise the NZ FLAG is set. If A < the checked value, then the C FLAG is set. If A >= the checked value, the NC FLAG is set.
    26FF-2700
    JR Z,26EFHJR Z,INDLOP28 EE
    If the character at the location of the current BASIC program pointer in Register A is a ,, then we have more dimensions to process, so JUMP back to INDLOP to process again
    2701-2702
    RST 08H ⇒ 29SYNCHK ")"CF 29
    Since the character at the location of the current BASIC program pointer in HL must be a ), call the COMPARE SYMBOL routine at RST 08H.

    NOTE:The RST 08H routine 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 the next execution address (i.e, the RST 08H instruction + 2) with the next symbol in the A Register 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).
    2703-2705-  ↳ SUBSOK
    LD (40F3H),HLLD (TEMP2),HL22 F3 40
    Save the value of the current BASIC program pointer in HL into the TEMP2 storage area.
    2706
    POP HLE1
    Get the DIMFLG and VALTYP from the STACK and put it in HL.
    2707-2709
    LD (40AEH),HLLD (DIMFLG),HL22 AE 40
    Save the value of the DIMFLG and VALTYP into the DIMFLG location in RAM.
    NOTE:40AEH holds LOCATE/CREATE variable flag
    270A
    PUSH DED5
    Save the number of subscripts/dimensions evaluated (held in DE) to the STACK

    At this point, Register BC holds the variable name, the pointer to the BASIC program is in TEMP2, all of the indexes are on the STACK, as is the number of dimensions.

    270B-270D
    LD HL,(40FBH)LD HL,(ARYTAB)2A FB 40
    We are now going to start the serach! First, load HL with the value of the array variables pointer as the starting point. 40FBH-40FCH holds the starting address of the BASIC array variable storage area
    270E-270F
    LD A,19H3E 19
    Z-80 Trick to skip the next command of ADD HL,DE if falling through
    270F-  ↳ LOPFDA
    ADD HL,DE19
    Advance past the current array as we know it isn't the correct one. Note: 40FBH-40FCH holds the array variables pointer
    2710
    EX DE,HLEB
    Swap DE and HL so that DE holds the current search point. We don't care about HL.
    2711-2713
    LD HL,(40FDH)LD HL,(STREND)2A FD 40
    Load HL with the place to STOP the search (i.e., the value of the free memory pointer).
    Note: 40FDH-40FEH holds Free memory pointer
    2714
    EX DE,HLEB
    Swap DE and HL so that DE now holds the place to stop the search and HL holds the current search point.
    2715
    RST 18HCOMPARDF
    Now we need to see if we have reached the end of the search by comparing the END point to the CURRENT point, so we call the COMPARE DE:HL 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.
    2716-2718
    LD A,(40AFH)LD A,(VALTYP)3A AF 40
    Load Register A with the value of the current number type flag.
    Note: 40AFH holds Current number type flag
    2719-271A
    JR Z,2742HJR Z,NOTFDD28 27
    If the Z FLAG is set, then we have run out of places to search and are finished, without finding the array, so JUMP out of this loop to 2742H
    271B
    CP (HL)BE
    Compare the value of the variable's number type flag in Register A with the value of the number type flag at the location of the array variables pointer in HL. If they match, the Z FLAG is set, and otherwise the NZ FLAG is set. If A < the checked value, then the C FLAG is set. If A >= the checked value, the NC FLAG is set.
    271C
    INC HL23
    Bump the value of the array variables pointer in HL
    271D-271E
    JR NZ,2727HR NZ,NMARY220 08
    Jump forward (but still in this loop) to 2727H if the number type flags don't match
    271F
    LD A,(HL)7E
    Load Register A with the first character of the variable name at the location of the array variables pointer in HL
    2720
    CP CB9
    Check to see if the first character of the variable at the location of the array variable pointer in Register A matches the first character of the variable name in Register C. If they match, the Z FLAG is set, and otherwise the NZ FLAG is set. If A < the checked value, then the C FLAG is set. If A >= the checked value, the NC FLAG is set.
    2721
    INC HL23
    Bump the value of the array variables pointer in HL
    2722-2723
    JR NZ,2728HR NZ,NMARY120 04
    Jump forward (but still in this loop) to 2728H if the first characters of the variable names don't match
    2724
    LD A,(HL)7E
    Load Register A with the second character of the variable name at the location of the array variables pointer in HL
    2725
    CP BB8
    Compare the second character of the variable name at the location of the array variables pointer in Register A matches the second character of the variable name in Register B. If they match, the Z FLAG is set, and otherwise the NZ FLAG is set. If A < the checked value, then the C FLAG is set. If A >= the checked value, the NC FLAG is set.
    2726-2727
    LD A,23H3E 23
    This part of a Z-80 Trick. Will load A with 23H if passing through, and not do the following INC HL.
    2727-  ↳ NMARY2
    INC HL23
    Bump the value of the array variables pointer in HL
    2728-  ↳ NMARY1
    INC HL23
    Bump the value of the array variables pointer in HL. HL should now point to the LENGTH entry of the array being looked at
    2729
    LD E,(HL)5E
    Load Register E with the LSB of the LENGTH of the array being looked at
    272A
    INC HL23
    Bump the value of the array variables pointer in HL
    272B
    LD D,(HL)56
    Load Register D with the MSB of the LENGTH of the array being looked at
    272C
    INC HL23
    Bump the value of the array variables pointer in HL
    272D-272E
    JR NZ,270FHR NZ,LOPFDA20 E0
    If the NZ FLAG is set, then we do not have a match ,and we need to skuip this entry and try again via a JUMP back to 270FH
    272F-2731
    LD A,(40AEH)LD A,(DIMFLG)3A AE 40
    Load Register A with the value of the locate/create flag to see if this was a "DIM" instruction.
    Note: 40AEH holds LOCATE/CREATE variable flag
    2732
    OR AB7
    Since a LD command does not set any flags, we must OR A to set the flags against A. In this case, to check the status of the locate/create flag in Register A
    2733-2734
    LD E,12H1E 12
    Prepare for an error if this routine was NOT called by "DIM" by loading Register E with the ?DD ERRORcode
    2735-2737
    JP NZ,19A2HJP NZ,ERRORC2 A2 19
    If the NZ FLAG is set, then this was not called from DIM, and we need to throw a REDIMENTIONED ARRAY error (i.e., ?DD ERROR) since that variable already exists

    At this point TEMP2 still holds the pointer to the position in the BASIC line being evaluated AND we have located the variable we were looking or. HL will point beyond the LENGTH to the number of dimensions. All indices are on the STACK, followed by the number of dimensions.

    2738
    POP AFF1
    Get the number of dimension evaluated from the STACK and put it in Register A
    2739
    SUB (HL)96
    To do the actual erasure we need to make suyre that the number given now and when the array was set up are the same so we compare the number of subscripts evaluated in Register A with the number of subscripts for the array at the location of the array variables pointer in HL (meaning, the number which was DIMmed)
    273A-273C
    JP Z,2795HJP Z,GETDEFCA 95 27
    If they match then we are done so JUMP down to 2795H to read the indices

    273D - ?BS ERROR entry point- "BSER"
    273D-273E-  ↳ BSER
    LD E,10H1E 10
    Load Register E with a ?BS ERRORcode
    273F-2741
    JP 19A2HJP ERRORC3 A2 19
    Display a ?BS ERRORmessage if the number of subscripts evaluated in Register A doesn't match the number of subscripts for the array at the location of the array variable pointer in HL

    2742 - Part of the ARRAY routines. Jumped here when a variable isn't found in the ARRAY table- "NOTFDD"

    The original ROM source lays out the steps which the ROM takes to build an entry when a variable isn't found in the array table:

  • Put down the descriptor
  • Set up the number of dimensions
  • Make sure there is room for the new entry
  • Remember the VARPTR
  • Set the VALTYP
  • Hold 2 bytes for the size
  • Loop
    1. Get an index
    2. Put number+1 down at the VARPTR
    3. Increase the VARPTR
    4. Decmrent the number of DIMs
    5. Go back to the LOOP until the number of DIMs hits Zero
  • Call REASON with Register Pair HL holding the last location of the variable
  • Update STREND
  • Zero out backwards
  • Make the tally include MAXDIMS
  • Put down TALLY
  • If called by DIM, RETurn
  • If not called by DIM, then index into the variable as if it was found when initially searched for.
  • 2742-  ↳ NOTFDD
    LD (HL),A77
    Save the variable type for the array in Register A at the location of the array variables pointer in HL
    2743
    INC HL23
    Bump the value of the array variables pointer in HL so it points to the 2nd character in the variable name (since they are saved in last, first order)
    2744
    LD E,A5F
    Load Register E with the variable type flag for the current variable
    2745-2746
    LD D,00H16 00
    Zero Register D so that Register Pair DE can be the size of one value of the type VALTYP
    2747
    POP AFF1
    Get the number of dimensions evaluated from the STACK and put it in Register A
    2748
    LD (HL),C71
    Save the second character of the variable's name in Register C at the location of the array variables pointer in HL
    2749
    INC HL23
    Bump the value of the array variables pointer in HL to now point to the 1st character in the variable name (since they are saved in last, first order)
    274A
    LD (HL),B70
    Save the first character of the variable's name in Register B at the location of the array variables pointer in HL
    274B
    INC HL23
    Bump the value of the array variables pointer in HL to the LSB of the offset to the next entry
    274C
    LD C,A4F
    In preparation for the next CALL, load Register C with the number of two byte entries needed to store the size of each dimension
    274D-274F
    CALL 1963HCALL GETSTKCD 63 19
    Figure the amount of memory space left between HL and the free memory and get the space needed as set in Register C
    2750
    INC HL23
    Next we need to make room (i.e., skip over) the size of each dimension so ... Bump the value of the array variables pointer in HL
    2751
    INC HL23
    Bump the value of the array variables pointer in HL. These 2 INC's skip over the offset entry
    2752-2754
    LD (40D8H),HLLD (TEMP3),HL22 D8 40
    Next we need to secure space for the dimenion entries by saving the location in which to put the size (which is the address of the maximum number of indices) into a temporary ram location.
    Note: 40D8H-40D9H holds temporary storage location
    2755
    LD (HL),C71
    Save the number of dimension at the location of the array variables pointer in HL
    2756
    INC HL23
    Bump the value of the array variables pointer in HL to point to the first subscript entry in the array table
    2757-2759
    LD A,(40AEH)LD A,(DIMFLG)3A AE 40
    Load Register A with the value of the locate/create flag so we can check to see if this routine was called from a DIM function.
    Note: 40AEH holds LOCATE/CREATE variable flag
    275A
    RLA17
    Set the CARRY flag accordingly
    275B
    LD A,C79
    Load Register A with the number of dimension evaluated in Register C
    275C-275E-  ↳ LOPPTA
    LD BC,000BH01 0B 00
    Top of a loop assuming we did not get here from "DIM". Load BC with the default number of 11, which is the most entries an array can have without a DIM
    275F-2760
    JR NC,2763HJR NC,NOTDIM30 02
    If the NC flag is set, then we did not arrive here from DIM, so JUMP forward to 2763H if the array is being created because it certainly wasn't found
    2761
    POP BCC1
    Get a subscript/index from the STACK and put it in BC
    2762
    INC BC03
    Bump the value of the subscript in BC by one for the ZERO entry.
    2763-  ↳ NOTDIM
    LD (HL),C71
    Top of a loop. Save the LSB of the subscript's value in Register C at the location of the array variables pointer in HL
    2764
    INC HL23
    Bump the value of the array variables pointer in HL
    2765
    LD (HL),B70
    Save the MSB of the subscript's value in Register B at the location of the array variables pointer in HL
    2766
    INC HL23
    Bump the value of the array variables pointer in HL
    2767
    PUSH AFF5
    Save the number of dimensions evaluated in Register A (and the CARRY aflag results from DIMFLG) to the STACK
    2768-276A
    CALL 0BAAHCALL UMULTCD AA 0B
    Go multiply the size of the subscript by the value of the number type flag to determine the amount of memory necessary for the subscript
    276B
    POP AFF1
    Get the number of domensions that the CARRY FLAG (DIMFLG) from the STACK and put it in Register A
    276C
    DEC A3D
    Decrement the counter of the number of dimensions to check by one
    276D-276E
    JR NZ,275CHJR NZ,LOPPTA20 ED
    Jump back to 275CH if there are anymore subscripts to be evaluated
    276F
    PUSH AFF5
    If we are here, then all dimensions have been processed and allocation. Next, save the number of subscripts evaluated (and the DIMFLG) in Register Pair AF to the STACK
    2770
    LD B,D42
    Load Register B with the MSB of the array's length in Register D
    2771
    LD C,E4B
    Load Register C with the LSB of the array's length in Register E. Now BC = size of the array in bytes
    2772
    EX DE,HLEB
    Swap DE and HL so that DE now has the start of the values and HL has the end of the values
    2773
    ADD HL,DE19
    Add the length of the array in HL to the value of the array variable pointer in DE
    2774-2775
    JR C,273DHJR C,BSER38 C7
    If that addition triggered the CARRY FLAG then we are out of RAM so JUMP back to 273DH and throw a ?BS ERROR
    2776-2778
    CALL 196CH
    CALL REASONCD 6C 19
    We now know there is room in RAM, so GOSUB to "REASON" to make sure there is room for the values
    2779-277B
    LD (40FDH),HLLD (STREND),HL22 FD 40
    Update the end of storage pointer with the end of the array (held in HL).
    Note: 40FDH-40FEH holds free memory pointer
    277C-  ↳ ZERITA
    DEC HL2B
    Now we need to zero the new array. First, decrement the value of the array pointer in HL
    277D-277E
    LD (HL),00H36 00
    Zero the location of the array pointer in HL
    277F
    RST 18HCOMPARDF
    Now we need to compare the array pointer in HL with the array variables pointer in DE, so we call the COMPARE DE:HL 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.
    2780-2781
    JR NZ,277CHJR NZ,ZERITA20 FA
    If the NZ FLAG is set, then we have more entries to ZERO, so loop until the array has been cleared
    2782
    INC BC03
    Load BC with the array's length in bytes plus one so as to make room for the byte which holds the number of dimensions for this array variable
    2783
    LD D,A57
    Zero Register D
    2784-2786
    LD HL,(40D8H)HL,(TEMP3)2A D8 40
    Load HL with the array variables pointer (=the number of indices).
    Note: 40D8H-40D9H holds Temporary storage location
    2787
    LD E,(HL)5E
    Load Register E with the number of dimension for the array at the location of the array variables pointer in HL
    2788
    EX DE,HLEB
    Swap DE and HL so that HL now holds the number of dimensions and DE holds the value of the array variables pointer
    2789
    ADD HL,HL29
    Multiply the number of subscripts for the array in HL by two
    278A
    ADD HL,BC09
    Add the length of the array in BC (i.e., the size) to the number of subscripts times two in HL so that we have the total number of bytes used
    278B
    EX DE,HLEB
    Swap DE and HL so that DE now holds the total number of bytes to be used for the array and HL holds the value of the array variables pointer
    278C
    DEC HL2B
    We now need to insert the size of the array in bytes into the array holding area, but that is 2 bytes back so ... decrement the value of the array variables pointer in HL
    278D
    DEC HL2B
    Decrement the value of the array variables pointer in HL
    278E
    LD (HL),E73
    Save the LSB of the size of the array in Register E at the location of the array variables pointer in HL
    278F
    INC HL23
    Bump the value of the array variables pointer in HL
    2790
    LD (HL),D72
    Save the MSB of size of the array array in Register D at the location of the array variables pointer in HL
    2791
    INC HL23
    Bump the value of the array variables pointer in HL
    2792
    POP AFF1
    Get the value of the DIMFLG (i.e., the CARRY BIT) and a 0 into Register A
    2793-2794
    JR C,27C5HJR C,FINNNOW38 30
    Jump forward to 27C5H if the array is being created

    At this point, HL points beyond the SIZE of the array to the NUMBER OF DIMENSIONS in the array. So what we need to do next is

    • We need NUMDIM to equal the number of dimensions and CURTOL to be 0
    • Start a loop:
      • Get a new index value
      • Pop the new maximum into CURMAX
      • Make sure the index value isn't too big
      • Multiply CURTOL by CURMAX
      • Add the index to CURTOL
      • Reduce NUMDIM by 1
      • LOOP BACK if NUMDIM isn't yet 0
    • Set an OFFSET as CURTOL*4 (which is the VALTYP for extended).
    2795-  ↳ GETDEF
    LD B,A47
    Zero Register B
    2796
    LD C,A4F
    Zero Register C. Now BC = 0 = CURTOL
    2797
    LD A,(HL)7E
    Load Register A with the number of dimensions for the array at the location of the array variables pointer in HL
    2798
    INC HL23
    Bump the value of the array variables pointer in HL to one entry past the number of dimensions
    2799
    LD D,0E1H16 E1
    Z-80 Trick to hide the next instruction (POP HL) if proceeding downward
    279A-  ↳ INLPNM
    POP HLE1
    Get the array variables pointer from the STACK and put it in HL
    279B
    LD E,(HL)5E
    Next, we want DE to be the maximum for the current index entry, so first load Register E with the LSB of the subscript limit at the location of the array variables pointer in HL
    279C
    INC HL23
    Bump the value of the array variables pointer in HL
    279D
    LD D,(HL)56
    Load Register D with the MSB of the subscript limit at the location of the array variables pointer in HL
    279E
    INC HL23
    Bump the value of the array variables pointer in HL
    279F
    EX (SP),HLE3
    Swap HL and (SP) so that HL now points to the currrent index, and the pointer to the array variable goes to the top of the STACK
    27A0
    PUSH AFF5
    Save the number of dimensions for the array in Register A to the STACK
    27A1
    RST 18HCOMPARDF
    Now we need to compare the subscript limit in DE with the subscript for the array in HL, so we call the COMPARE DE:HL 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.
    27A2-27A4
    JP NC,273DHJP NC,BSERD2 3D 27
    If the current inex is too big, then we need to throw a ?BS ERROR via a JUMP to 273DH
    27A5-27A7
    CALL 0BAAHCALL UMULTCD AA 0B
    CURTOL = CURTOL * the current maximum subscript
    27A8
    ADD HL,DE19
    Add the index to CURTOL
    27A9
    POP AFF1
    Get the number of dimensions for the array from the STACK and put it in Register A
    27AA
    DEC A3D
    We checked one, so cross one off the list and see if there are anymore dimensions to be evaluated
    27AB
    LD B,H44
    Load Register B with the MSB of the subscript pointer in Register H
    27AC
    LD C,L4D
    Load Register C with the LSB of the subscript pointer in Register L. Now BC = CURTOL for the start of the next loop.
    27AD-27AE
    JR NZ,279AHJR NZ,INLPNM20 EB
    Loop back to 279AH until all of the subscripts have been evaluated
    27AF-27B1
    LD A,(40AFH)LD A,(VALTYP)3A AF 40
    Get the number type flag for the current array; which also doubles for how big the values are
    NOTE:40AFH holds Current number type flag
    27B2
    27B3
    LD B,H
    LD C,L44
    We are going to need to multiply by THREE, so we need to save the original value into BC as well.
    27B4
    ADD HL,HL29
    Multiply the subscript pointer in HL by two
    27B5-27B6
    SUB 04HD6 04
    Check the value of the number type flag in Register A because we would be done if we have an integer or a string.
    27B7-27B8
    JR C,27BDHJR C,DMLVAL38 04
    Jump forward to 27BDH if the current number type is an integer or string
    27B9
    ADD HL,HL29
    It isn't an integer or a string so once again multiply the subscript pointer in HL by two (so now it is multiplied by 4)
    27BA-27BB
    JR Z,27C2HJR Z,DONMUL28 06
    Jump forward to 27C2H if the current number type is single precision as we then have enough bytes.
    27BC
    ADD HL,HL29
    It must be double precision, so once again multiply the subscript pointer in HL by two (so now it is multiplied by 8)
    27BD-  ↳ DMLVAL
    OR AB7
    Set the flags
    27BE-27C0
    JP PO,27C2HJP PO,DONMULE2 C2 27
    Jump forward to 27C2H if the current number type isn't a string
    27C1
    ADD HL,BC09
    The current number type is a string so add the value of the original subscript pointer in BC to the subscript pointer in HL
    27C2-  ↳ DONMUL
    POP BCC1
    Get the value of the array variables pointer from the STACK and put it in BC
    27C3
    ADD HL,BC09
    Add the value of the array variables pointer in BC to the subscript pointer in HL to get the place where the size value needs to be stored
    27C4
    EX DE,HLEB
    Load DE with the variable pointer in HL
    27C5-27C7-  ↳ FINNNOW
    LD HL,(40F3H)LD HL,(TEMP2)2A F3 40
    Get the value of the current BASIC program pointer and put it in HL.
    Note: 40F3H-40F4H is a temporary storage location
    27C8
    RETC9
    RETurn to CALLer

    27C9-27D3 - LEVEL II BASIC MEM ROUTINE- "MEM"

    This is the RETURN AMOUNT OF FREE MEMORY routine at 27C9H which computes the amount of memory remaining between the end of the variable list and the end of the STACK and puts the result in ACCumulator as a SINGLE PRECISION number.

    27C9-  ↳ MEM
    XOR AAF
    Zero Register A and the status flags
    27CA
    PUSH HLE5
    Save the value of the current BASIC program pointer in HL to the STACK
    27CB-27CD
    LD (40AFH),ALD (VALTYP),A32 AF 40
    Zero the number type flag.
    NOTE:40AFH holds Current number type flag
    27CE-27D0
    CALL 27D4HCALL FRECD D4 27
    Determine how much space there is via a GOSUB to the FREroutine at 27D4H
    27D1
    POP HLE1
    Get the value of the current BASIC program pointer from the STACK and put it in HL
    27D2
    RST 10HCHRGETD7
    We need the next character in the BASIC program so 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.
    27D3
    RETC9
    Return to BASIC

    27D4-27F4 - LEVEL II BASIC FREROUTINE- "FRE"
    27D4-27D6-  ↳ FRE
    LD HL,(40FDH)LD HL,(STREND)2A FD 40
    Load HL with the start of free memory pointer (which is also the end of the variable and text space).
    NOTE:40FDH-40FEH holds Free memory pointer
    27D7
    EX DE,HLEB
    Load DE with the value of the free memory pointer in HL for subtraction
    27D8-27DA
    LD HL,0000H21 00 00
    Zero HL
    27DB
    ADD HL,SP39
    Add the value in HL (which is zero) to the current value of the STACK pointer so that the STACK pointer is now in HL
    27DC
    RST 20HGETYPEE7
    We need to check the value of the current number type flag, so we call the TEST DATA MODE routine at RST 20H.
    NOTE:The RST 20H routine determines the type of the current value in ACCumulator and returns a combination of STATUS flags and unique numeric values in the A Register according to the data mode flag (40AFH). The results are returned as follows:
    • 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.
    27DD-27DE
    JR NZ,27ECHJR NZ,GIVDBL20 0D
    If that test shows we do NOT have a STRING (meaning this was really a MEMcall, jump to forward to 27ECH
    27DF-27E1
    CALL 29DAHCALL FREFACCD DA 29
    Free up the argument and set up to give some free string space
    27E2-27E1
    CALL 28E6HCALL GARBA2CD E6 28
    Do garbage collection to free up space
    27E5-27E7
    LD HL,(40A0H)LD HL,(STKTOP)2A A0 40
    Load HL with the start of string space pointer / bottom of free space.
    NOTE:40A0H-40A1H holds the start of string space pointer
    27E8
    EX DE,HLEB
    Load DE with the start of string space pointer in HL
    27E9-27EB
    LD HL,(40D6H)LD HL,(FRETOP)2A D6 40
    Load HL with the next available location in string space pointer / top of free space.
    NOTE:40D6H-40D7H holds the next available location in string space pointer

    The next routine subtracts DE from HL and then floats the result leaving it in FAC.

    27EC-  ↳ GIVDBL
    LD A,L7D
    Prepare to do HL = HL - DE. First, load Register A with the LSB of the next available location in string space pointer in Register L
    27ED
    SUB E93
    Subtract the LSB of the start of string space pointer in Register E from the LSB of the next available location in string space pointer in Register A
    27EE
    LD L,A6F
    Load Register L with the LSB of the amount of string space remaining in Register A
    27EF
    LD A,H7C
    Load Register A with the MSB of the next available location in string space pointer in Register H
    27F0
    SBC A,D9A
    Subtract the MSB of the string space pointer in Register D from the MSB of the next available location of string space pointer in Register A
    27F1
    LD H,A67
    Load Register H with the MSB of the amount of string space remaining in Register A
    27F2-27F4
    JP 0C66HJP INEG2C3 66 0C
    Jump to 0C66H to convert the difference between HL and DE to single precision and then RETurn out of the routine

    27F5-27FD - LEVEL II BASIC POS(ROUTINE- "POS"

    27F5-27F7-  ↳ POS
    LD A,(40A6H)LD A,(TTYPOS)3A A6 40
    Load Register A with the current cursor line position.
    Note: 40A6H holds the current cursor line position
    27F8-  ↳ SNGFLT
    LD L,A6F
    Load Register L with the value of the current cursor line position in Register A.
    27F9
    XOR AAF
    Zero Register A
    27FA-  ↳ GIVINT
    LD H,A67
    Load Register H with zero, so now HL is 00 + cursor position
    27FB-27FD
    JP 0A9AHJP MAKINTC3 9A 0A
    Jump to 0A9AH to make A an unsigned integer

    27FE-2818 - LEVEL II BASIC USR(x)ROUTINE- "USRFN"

    27FE-2780-  ↳ USRFN
    CALL 41A9HCALL USROUTCD A9 41
    GOSUB to DOS to see if DOS wants to deal with this
    2801
    RST 10HCHRGETD7
    We need the next character so 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.
    2802-2804
    CALL 252CHCALL PARCHKCD 2C 25
    GOSUB to 252CH to evaluate the expression at the location of the current BASIC program pointer in HL and return with the result in ACCumulator
    2805
    PUSH HLE5
    Save the value of the current BASIC program pointer in HL (=the address of the next element in the code string) to the STACK
    2806-2808
    LD HL,0890HLD HL,POPHRT21 90 08
    Load HL with the return address of 0890H which will clear the STACK before returning to BASIC
    2809
    PUSH HLE5
    Save the value of the return address in HL to the STACK
    280A-280C
    LD A,(40AFH)LD A,(VALTYP)3A AF 40
    Load Register A with the value of the current number type flag for the argument provided.
    Note: 40AFH holds Current number type flag
    280D
    PUSH AFF5
    Save the value of the current number type flag in Register A.
    (02=INT, 03=STR, 04=SNG, 08=DBL)
    280E-280F
    CP 03HFE 03
    Check to see if the current value is a string. If they match, the Z FLAG is set, and otherwise the NZ FLAG is set. If A < the checked value, then the C FLAG is set. If A >= the checked value, the NC FLAG is set.
    2810-2812
    CALL Z,29DAHCALL Z,FREFACCC DA 29
    If the current result is a string then GOSUB to 29DAH to get the free the space and get the string address into HL
    2813
    POP AFF1
    Restore the number type flag into Register A
    2814
    EX DE,HLEB
    Load DE with the (possible) value of the pointer to the string (in HL)
    2815-2817
    LD HL,(408EH)2A 8E 40
    Load HL with the starting address of the machine language subroutine. In TRSDOS, 408EH is a variable called MAXFIL which holds the value the user entered when answering the prompted "FILES?" question. This would definitely NOT be the right address under DOS.
    2818
    JP (HL)E9
    Jump to (HL) to run the routine

    2819-2827 - CONVERSION ROUTINE- "DOCNVF"

    Usually called by LETto convert the result of arithmetic routines to the proper destination type.

    2819-  ↳ DOCNVF
    PUSH HLE5
    Save the pointer to the current character in the BASIC program being evaluated to the STACK
    281A-281B
    AND 07HE6 07
    Mask the value of the current number type flag in Register A to force the formula type to conform to the variable type that it is assigned to
    281C-281E
    LD HL,18A1HLD HL,FRCTBL21 A1 18
    Load HL with the address of the arithmetic conversion routines
    281F
    LD C,A4F
    Load Register C with the value of the number type flag in Register A.
    (02=INT, 03=STR, 04=SNG, 08=DBL)
    2820-2821
    LD B,00H06 00
    Zero Register B. Now BC holds 00 + type and will be the two byte offset into the table
    2822
    ADD HL,BC09
    Add the offset (of BC) to the base arithmetic conversion routines, to find the right jump point
    2823-2825
    CALL 2586HCALL DISPATCD 86 25
    GOSUB to 2586H to convert the current result in REG l to its proper number type
    2826
    POP HLE1
    Restore the pointer to the current character in the BASIC program being evaluated to HL
    2827
    RETC9
    RETurn to CALLer

    2828-2835 - Routine to see if we are in DIRECT MODE and ERROR OUT if so- "ERRDIR"

    Usually called from the INPUTroutine. On entry HL has the current line number in binary.

    2828-  ↳ ERRDIR
    PUSH HLE5
    Save whatever was in HL to the STACK
    2829-282B
    LD HL,(40A2H)LD HL,(CURLIN)2A A2 40
    Load HL with the value of the current BASIC line number (which is stored at 40A2H-40A3H).
    282C
    INC HL23
    Bump the value of the current BASIC line number in HL to enable us to test for a direct statement. Direct is 65535 so bumping by 1 will give us a ZERo
    282D
    LD A,H7C
    Load Register A with the MSB of the current BASIC line number in Register H
    282E
    OR LB5
    Combine the LSB of the current BASIC line number in Register L with the MSB of the current BASIC line number in Register A. If H and L are both ZERO then the Z FLAG will be set.
    282F
    POP HLE1
    Restore whatever was in HL on entry back into HL
    2830
    RET NZC0
    Return if there is a line number (i.e., this isn't the command mode) and otherwise fall through to the ?ID ERROR routine

    2831 - ID ERROR entry point.

    2831-2832
    LD E,16H1E 16
    Load Register E with the ?ID ERRORcode
    2833-2835
    JP 19A2HJP ERRORC3 A2 19
    Display an ?ID ERRORif this is the command mode

    2836-2856 - STRING ROUTINE - STR$logic- "STR$"

    2836-2838-  ↳ STR$
    CALL 0FBDHCALL FOUTCD BD 0F
    GOSUB to 0FBDH to convert the current result in ACCumulator to an ASCII string
    2839-283B-  ↳ STR$1
    CALL 2865HCALL STRLITCD 65 28
    Scan it and turn it into a string (make a temporary string work area entry)
    283C-283E
    CALL 29DAHCALL FREFACCD DA 29
    Load HL with the string's VARPTR and free up the temp
    283F-2841
    LD BC,2A2BHLD BC,FINBCK01 2B 2A
    Load BC with a return address of 2A2BH (which cleans the STACK and then jumps to 2884H)
    2842
    PUSH BCC5
    Save the value of the return address in BC to the STACK

    The next routine, STRCPY, creates a copy of the string pointed to by Register Pair HL. On exit, DE points to DSCTMP which has the string information.

    2843-  ↳ STRCPY
    LD A,(HL)7E
    Load Register A with the string's length at the location of the string's VARPTR in HL
    2844
    INC HL23
    Bump the value of the string's VARPTR in HL
    2845
    PUSH HLE5
    Save the value of the string's VARPTR in HL to the STACK
    2846-2848
    CALL 28BFHCALL GETSPACD BF 28
    GOSUB to 28BFH to test the remaining string area to make sure that the new string will fit
    2849
    POP HLE1
    Reload HL with the string's VARPTR. This is the destination to where the string should be copied.
    284A
    LD C,(HL)4E
    Load Register C with the LSB of the string's address at the location of the string's VARPTR in HL
    284B
    INC HL23
    Bump the value of the string's VARPTR in HL
    284C
    LD B,(HL)46
    Load Register B with the MSB of the string's address at the location of the string's VARPTR in HL
    284D-284F
    CALL 285AHCALL STRAD2CD 5A 28
    GOSUB to 285AH to save the string's length and the string's address at 40D3H, so as to set up DSCTMP
    2850
    PUSH HLE5
    Save the pointer to STRAD2 (which is 40D3H) to the STACK
    2851
    LD L,A6F
    Load Register L with the string's length (from Register A)
    2852-2854
    CALL 29CEHCALL MOVSTRCD CE 29
    GOSUB to 29CEH to move L characters from the temp area (of BC) to the string data area (in DE)
    2855
    POP DED1
    Restore the pointer to DSCTMP (40D3H) into Register Pair DE
    2856
    RETC9
    RETurn to CALLer

    2857-2864 - STRING ROUTINE- "STRINI"

    2857-2859-  ↳ STRINI
    CALL 28BFHCALL GETSPACD BF 28
    GOSUB to 28BFH to make sure that there is enough string space remaining for the string length of Register A characters. Get the address of the next string area in DE. Then save A and DE at 40D3H-40D5H
    285A-  ↳ STRAD2
    LD HL,40D3HLD HL,DSCTMP21 D3 40
    Load HL with the address of the temporary string parameter storage area.
    Note: 40D3H-40D5H holds Used for temporary string VARPTR's
    285D-  ↳ STRAD1
    PUSH HLE5
    Save the address of the temporary string parameter area in HL to the STACK
    285E
    LD (HL),A77
    Save the string's length in Register A at the location of the temporary string parameter storage pointer in HL
    285F-  ↳ PUTDEI
    INC HL23
    The next instructions are to set up DE to be the pointer to free space. First, bump the value of the temporary string parameter storage pointer in HL
    2860
    LD (HL),E73
    Save the LSB of the string's address in Register E at the location of the temporary string parameter storage pointer in HL
    2861
    INC HL23
    Bump the value of the temporary string parameter storage pointer in HL
    2862
    LD (HL),D72
    Save the MSB of the string's address in Register D at the location of the temporary string parameter storage pointer in HL
    2863
    POP HLE1
    Get the address of the temporary string parameter storage area from the STACK and put it in HL
    2864
    RETC9
    RETurn to CALLer

    2865-28A5 - STRING ROUTINE- "STRLIT"

    STRLT2 takes the string literal whose first character is pointed by HL+1 and builds a descriptor for it. Leading quotes should be skipped before the CALL to this routine.

    The descriptor is initially built in DSCTMP, but PUTNEW transfers it into a temporary RAM area and leaves a pointer at the temporary in FACLO.

    All characters other than zero (that terminate the string) should be set up in Registers B and D. If the terminator is a quote, the quote is skipped over.

    On EXIT, the character after the string literal is pointed to by Register Pair HL and is in Register A. No flags are set.

    2865-  ↳ STRLIT
    DEC HL2B
    Decrement the value of the current BASIC program pointer in HL
    2866-2867-  ↳ STRLTI
    LD B,22H06 22
    Load Register B with a "(which is really the end of the quote search character)
    2868-  ↳ STRLT3
    LD D,B50
    Load Register D with a "(which is really the ending search character)
    2869-  ↳ STRLT2
    PUSH HLE5
    Save the address of the current BASIC program pointer in HL to the STACK. This is ALSO the pointer to the start of the literal string being worked on.
    286A-286B
    LD C,0FFH0E FF
    Load Register C with a -1
    286C-  ↳ STRGET
    INC HL23
    Bump the value of the current BASIC program pointer in HL to skip over that initial "
    286D
    LD A,(HL)7E
    Load Register A with the character at the location of the current BASIC program pointer in HL
    286E
    INC C0C
    Bump the counter in Register C
    286F
    OR AB7
    Check to see if the character at the location of the current BASIC program pointer in Register A is an end of the BASIC line character
    2870-2871
    JR Z,2878HJR Z,STRFIN28 06
    Jump to 2878H if the character at the location of the current BASIC program pointer in Register A is an end of the BASIC line character
    2872
    CP DBA
    Check to see if the character at the location of the current BASIC program pointer in Register A is the same as the terminating character in Register D. If they match, the Z FLAG is set, and otherwise the NZ FLAG is set. If A < the checked value, then the C FLAG is set. If A >= the checked value, the NC FLAG is set.
    2873-2874
    JR Z,2878HJR Z,STRFIN28 03
    Jump to 2878H if the character at the location of the current BASIC program pointer in Register A is the same as the terminating character in Register D
    2875
    CP BB8
    Check to see if the character at the location of the current BASIC program pointer in Register A is the same at the terminating character in Register B. If they match, the Z FLAG is set, and otherwise the NZ FLAG is set. If A < the checked value, then the C FLAG is set. If A >= the checked value, the NC FLAG is set.
    2876-2877
    JR NZ,286CHJR NZ,STRGET20 F4
    Loop back to 286CH if the character at the location of the current BASIC program pointer in Register A isn't the same as the terminating character in Register B or D
    2878-2879-  ↳ STRFIN
    CP 22HFE 22
    Check to see if the character at the location of the current BASIC program pointer in Register A is a quote. If they match, the Z FLAG is set, and otherwise the NZ FLAG is set. If A < the checked value, then the C FLAG is set. If A >= the checked value, the NC FLAG is set.
    287A-287C
    CALL Z,1D78HCALL Z,CHRGTRCC 78 1D
    If it was a quote, then GOSUB to 1D78H to bump the value of the current BASIC program pointer in HL until it points to the next character (i.e., skip the quote)
    287D
    EX (SP),HLE3
    Exchange the value of the current BASIC program pointer in HL with the string's address to the STACK
    287E
    INC HL23
    Bump the string's address in HL until it points to the first character of the string
    287F
    EX DE,HLEB
    Load DE with the temporary pointer string's address in HL
    2880
    LD A,C79
    Load Register A with the string's length from Register C
    2881-2883
    CALL 285AHCALL STRAD2CD 5A 28
    GOSUB to 285AH to save the string's length and the string's address into 40D3H (i.e., DSCTMP)
    2884-2886-  ↳ PUTNEW
    LD DE,40D3HLD DE,DSCTMP11 D3 40
    Load DE with the address of the string parameter storage area.
    Note: 40D3H-40D5H holds Used for temporary string VARPTR's
    2887-2888
    LD A,D5H3E D5
    This seems to be garbage, but 2888H is a JUMP point in DOS Basic to process a new string in the DEF FN routine. When JUMPed to 2888H, a PUSH DE is processed
    2888-  ↳ PUTTMP
    PUSH DED5
    Save a pointer to the stat of the string to the STACK
    2889-288B
    LD HL,(40B3H)LD HL,(TEMPPT)2A B3 40
    Load HL with the first avaialble free location in the temporary string work area in HL. This will serve as the string's VARPTR in ACCumulator.
    Note: 40B3H-40B4H holds the next available location in the temporary string work area pointer
    288C-288E
    LD (4121H),HLLD (FACLO),HL22 21 41
    Save the value of the next available location in the temporary string work area in HL. This is where the result string descriptor will be
    288F-2890
    LD A,03H3E 03
    Load Register A with the string number type flag
    2891-2893
    LD (40AFH),ALD (VALTYP),A32 AF 40
    Save the value in Register A as the current number type flag.
    Note: 40AFH holds current number type flag
    2894-2896
    CALL 09D3HCALL VMOVECD D3 09
    Move the value into the temporary string work area in HL
    2897-2899
    LD DE,40D6HLD DE,FRETOP11 D6 40
    Depending on how we got here, DE will be different. If the jump was into PUTTMP, then DE will NOT equal FRETOP.
    289A
    RST 18HCOMPARDF
    We need to do some checking, as FRETOP is just beyond the temporary string storage areas, so if TEMPPT points to it, then there are no free temporary storage areas left! To do this we check to see if the updated temporary string work area location in HL isn't greater than the ending address of the temporary string work area in DE, so we call the COMPARE DE:HL 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.
    289B-289D
    LD (40B3H),HLLD (TEMPPT),HL22 B3 40
    Save the new temporary string pointer in HL as the next available location in the temporary string work area.
    Note: 40B3H-40B4H holds the next available location in the temporary string work area pointer
    289E
    POP HLE1
    Get the value of the current BASIC program pointer from the STACK and put it in HL
    289F
    LD A,(HL)7E
    Load Register A with the character at the location of the current BASIC program pointer in HL
    28A0
    RET NZC0
    Return if the updated temporary string work area location wasn't beyond the end of the temporary string work area (meaning it overflowed, because if it overflowed .)

    28A1 - ST ERROR entry point.

    28A1-28A2
    LD E,1EH1E 1E
    Load Register E with a ?ST ERRORcode
    28A3-28A5
    JP 19A2HJP ERRORC3 A2 19
    Display a ?ST ERRORmessage if the temporary string work area has overflowed

    28A6-28BE - DISPLAY MESSAGE ROUTINE- "STROUI"

    According to the original ROM source, this routine will print the string pointed to by Register Pair HL. The string MUST be terminated by a 00H. If the string exists below DSCTMP, then it is copied into string space first.

    28A6-  ↳ STROUI

    INC HL23
    Bump the value of the current BASIC program pointer in HL

    EXAMPLE: Suppose that we have the following symbolic setup:
    TITL DEFM 'INSIDE LEVEL II'
    DEFB 0
    Then, the instructions:
    LD HL,TITL
    CALL 28A7H- CALL STROUT
    will cause "INSIDE LEVEL II" to be displayed at the current cursor position and the cursor position to be updated.

  • NOTE: If the subroutine at 28A7H is used by an assembly language program that is itself entered by a USR call, the return from the assembly language program may encounter the embarrassment of a TM error, with control passing to the Level II monitor. This occurs because the subroutine at 28A7H leaves a 3 in location 40AFH, while the USR structure requires a 2 in 40AFH upon returning. The malady is cured by storing a 2 in 40AFH before returning, or by jumping to 0A9AH instead of executing the simple RET. The problem would not occur in the first place if the assembly language program returns the value of an integer variable to the BASIC program, and it might not occur if some other ROM routine is called after the subroutine at 28A7H and before returning - if the other subroutine produces an integer output. DISK SYSTEM CAUTION: See the DISK SYSTEM CAUTION of Section 8.1 regarding the exits to DISK BASIC from the subroutine at 28A7H.
    28A7-28A9H
    "STROUT"
    CALL 2865HCALL STRLITCD 65 28
    Go build a temporary string work area entry for the message at FACLOthe location of the current BASIC program pointer in HL

    If the routine entry is at STRPRT, then it just prints the string whose descriptor is held in FACLO

    28AA-28AC-  ↳ STRPRT
    CALL 29DAHCALL FREFACCD DA 29
    GOSUB to 29DAH to build a temporary string work area entry for the message pointed to by FACLO
    28AD-28AF
    CALL 09C4HCALL GETBCDCD C4 09
    Go get the string's length in Register D and the string's address in BC
    28B0
    INC D14
    Bump the value of the string's length in Register D in preparation for the following loop which starts with a DEC D
    28B1-  ↳ STRPR2
    DEC D15
    Top of a loop. Decrement the value of the string's length in Register D
    28B2
    RET ZC8
    Return if all of the characters in the string have been sent to the current output device.
    28B3
    LD A,(BC)0A
    Load Register A with the character at the location of the string pointer in BC
    28B4-28B6
    CALL 032AHCALL OUTDOCD 2A 03
    Go send the character in Register A to the current output device
    28B7-28B8
    CP 0DHFE 0D
    Check to see if the character in Register A is a CARRIAGE RETURN. If they match, the Z FLAG is set, and otherwise the NZ FLAG is set. If A < the checked value, then the C FLAG is set. If A >= the checked value, the NC FLAG is set.
    28B9-28BB
    CALL Z,2103HCALL Z,CRFINCC 03 21
    Jump to 2103H if the character in Register A is a carriage return
    28BC
    INC BC03
    Bump the value of the string pointer in BC
    28BD-28BE
    JR 28B1HJR STRPR218 F2
    Loop until all of the characters in the string have been sent to the current output device

  • 28BF-28D9 - STRING ROUTINE- "GETSPA"

    This routine will get space for a character string, and it might force garbage collection as well. The number of characters is in Register A. On exit, DE will point to the string, but if it could not allocate space, then an ?OS ERROR is thrown instead.

    28BF-  ↳ GETSPA
    OR AB7
    Make sure A is not zero. A ZERO FLAG will signal that garbage collection has happened
    28C0-28C1
    LD C,0F1H0E F1
    Z-80 Trick. If passing through, the C just changes and the POP AF which follows is ignored.
    28C1-  ↳ TRYGI2
    POP AFF1
    Get the string's length from the STACK and put it in Register A
    28C2
    PUSH AFF5
    Save the length of the string in Register A to the STACK
    28C3-28C5
    LD HL,(40A0H)LD HL,(STKTOP)2A A0 40
    Load HL with the poinmter to the bottom of the string space. 40A0H-40A1H holds the start of string space pointer
    28C6
    EX DE,HLEB
    Move the bottom of the string space pointer into DE. We don't care what happens to HL
    28C7-28C9
    LD HL,(40D6H)LD HL,(FRETOP)2A D6 40
    Load HL with the pointer to the TOP of free space.
    Note: 40D6H-40D7H holds the next available location in string space pointer
    28CA
    CPL2F
    Complement the string's length in Register A so that it is negative
    28CB
    LD C,A4F
    The next two instructions put the negative of the number of characters into Register Pair BC. First, load Register C with the negative string's length in Register A
    28CC-28CD
    LD B,0FFH06 FF
    Load Register B with a -1 so that BC will be the negative length of the string
    28CE
    ADD HL,BC09
    Add the negative string's length in BC to the top of free space pointer in HL
    28CF
    INC HL23
    Bump the value of the adjusted next available location in string space pointer in HL
    28D0
    RST 18HCOMPARDF
    We need to make sure there is enough room for the string, so we need to compare these by checking to see if the adjusted next available location in string space pointer in HL is less than the start of string space pointer in DE, so we call the COMPARE DE:HL 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.
    28D1-28D2
    JR C,28DAHJR C,GARBAG38 07
    If the CARRY FLAG is set then we do not have enough room for the string, so lets JUMP forward to 28DAH to do some garbae collection.
    28D3-28D5
    LD (40D6H),HLLD (FRETOP),HL22 D6 40
    Save the value in HL as new bottom of MEMORY
    28D6
    INC HL23
    Bump the value of HL to point to the string
    28D7
    EX DE,HLEB
    Load DE with the pointer to the string
    28D8-  ↳ PPSWRT
    POP AFF1
    Get the string's length from the STACK and put it in Register A
    28D9
    RETC9
    RETurn to CALLer

    28DA-298E - STRING ROUTINE- "GARBAG"

    28DA-  ↳ GARBAG
    POP AFF1
    Get the garbage collection code which was PUSHed
    28DB-28DC
    LD E,1AH1E 1A
    Load Register E with an ?OS ERRORcode
    28DD-28DF
    JP Z,19A2HJP Z,ERRORCA A2 19
    If the Z FLAG is set, then we already tried garbage collection and still have no RAM left for the string, so display a ?OS ERRORif there isn't enough string space available for the string and we had already reorganized string space
    28E0
    CP ABF
    Otherwise, set the flags to say that we have already done the garbage collection. If they match, the Z FLAG is set, and otherwise the NZ FLAG is set. If A < the checked value, then the C FLAG is set. If A >= the checked value, the NC FLAG is set.
    28E1
    PUSH AFF5
    Save the garbage collection flag to the STACK
    28E2-28E4
    LD BC,28C1HLD BC,TRYGI201 C1 28
    Load BC with a return address of 28C1H (which would retry allocation)
    28E5
    PUSH BCC5
    Save that return address to the STACK
    28E6-28E8-  ↳ GARBA2
    LD HL,(40B1H)LD HL,(MEMSIZ)2A B1 40
    Load HL with the top of memory pointer.
    Note: 40B1H-40B2H holds MEMORY SIZE? pointer
    28E9-28EB-  ↳ FNDVAR
    LD (40D6H),HLLD (FRETOP),HL22 D6 40
    Save the top of BASIC memory pointer in HL as the next available location in string space pointer.
    Note: 40D6H-40D7H holds the next available location in string space pointer
    28EC-28EE
    LD HL,0000H21 00 00
    Zero HL
    28EF
    PUSH HLE5
    Save the value in HL to the STACK to indicate that we didn't find a variable on this pass
    28F0-28F2
    LD HL,(40A0H)LD HL,(STKTOP)2A A0 40
    Load HL with the start of string space pointer to force DVARS to ignore strings in the program text (literals and data).
    NOTE:40A0H-40A1H holds the start of string space pointer
    28F3
    PUSH HLE5
    Save high address (start of string space pointer in HL) to the STACK
    28F4-28F6
    LD HL,40B5HLD HL,TEMPST21 B5 40
    Load HL with the start of the temporary string work area pointer.
    Note: 40B5H-40D2H holds Temporary string work area
    28F7
    "TVAR"
    EX DE,HLEB
    Load DE with the start of the temporary string work area pointer in HL
    28F8-28FA
    LD HL,(40B3H)LD HL,(TEMPPT)2A B3 40
    Load HL with the next available location in the temporary string work area pointer.
    NOTE:40B3H-40B4H holds the next available location in the temporary string work area pointer
    28FB
    EX DE,HLEB
    Exchange the start of the temporary string work area pointer in DE with the next available location in the temporary string work area pointer in HL
    28FC
    RST 18HCOMPARDF
    We need to see if 40B3H is pointing to the first entry (40B5H) - if the start of the temporary string work area pointer in HL is the same as the next available location in the temporary string work area pointer, so we call the COMPARE DE:HL 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.
    28FD-28FF
    LD BC,28F7HLD BC,TVAR01 F7 28
    Load BC with a return address of 28F7H
    2900-2902
    JP NZ,294AHJP NZ,DVAR2C2 4A 29
    Jump to 294AH to do the temporary variable garbage collection if the temporary string work area isn't empty
    2903-  ↳ SVARS
    LD HL,(40F9H)LD HL,(VARTAB)2A F9 40
    Load HL with the start of the simple variables pointer.
    NOTE:40F9H-40FAH holds the starting address of the simple variable storage area
    2906-  ↳ SVAR
    EX DE,HLEB
    Load DE with the simple variables pointer in HL
    2907-2909
    LD HL,(40FBH)LD HL,(ARYTAB)2A FB 40
    Load HL with the start of the array variables pointer (which is also the end of the simple variables). 40FBH-40FCH holds the starting address of the BASIC array variable storage area
    290A
    EX DE,HLEB
    Swap DE and HL so that DE holds the end and HL holds the start.
    290B
    RST 18HCOMPARDF
    Now we need to check to see if the simple variables pointer in HL is the same as the array variables pointer in DE, so we call the COMPARE DE:HL 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.
    290C-290D
    JR Z,2921HJR Z,ARYVAR28 13
    If the Z FLAG is set then we are at the end of the simple variable. If so, we then need to do the string type variables so we jump forward to 2921H.
    290E
    LD A,(HL)7E
    Load Register A with second character of the variable name
    290F
    INC HL23
    Bump the value of the simple variables pointer in HL
    2910
    INC HL23
    Bump the simple variables pointer in HL
    2911
    INC HL23
    Bump the value of the simple variables pointer in HL. HL should now point to the value of the variable.
    2912-2913
    CP 03HFE 03
    Check to see if the variable at the location of the simple variables pointer is a string. If they match, the Z FLAG is set, and otherwise the NZ FLAG is set. If A < the checked value, then the C FLAG is set. If A >= the checked value, the NC FLAG is set.
    2914-2915
    JR NZ,291AHJR NZ,SKPVAR20 04
    Jump to 291AH if the variable at the location of the simple variables pointer in HL isn't a string
    2916-2918
    CALL 294BHCALL DVARSCD 4B 29
    GOSUB to 294BH to do collect the string
    2919
    XOR AAF
    Zero Register A to indicate that we should not be skipping anything else.
    291A-  ↳ SKPVAR
    LD E,A5F
    Zero Register E
    291B-291C
    LD D,00H16 00
    Zero Register D. Now DE should be the number of characters to skip.
    291D
    ADD HL,DE19
    Add the number of characters to skip (held in DE) to the simple variables pointer in HL
    291E-291F
    JR 2906HJR SVAR18 E6
    Loop back to 2906H until all of the simple variables have been checked
    2920-  ↳ ARYVA2
    POP BCC1
    Clean up the STACK
    2921-  ↳ ARYVAR
    EX DE,HLEB
    Load DE with the value of the array variables pointer (ARTVAR) in HL
    2922-2924
    LD HL,(40FDH)LD HL,(STREND)2A FD 40
    Load HL with the end of the arrays / start of free memory pointer.
    Note: 40FDH-40FEH holds Free memory pointer
    2925
    EX DE,HLEB
    Exchange the value of the array variables pointer in DE with the value of the free memory pointer in HL
    2926
    RST 18HCOMPARDF
    We need to see if we are donee with arrays, so we check to see if the array variables pointer in HL is the same as the free memory pointer in DE, so we call the COMPARE DE:HL 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.
    2927-2929
    JP Z,296BHJP Z,GRBPASCA 6B 29
    Jump if the array variables pointer in Register HL is the same as the start of the free memory pointer in DE
    292A
    LD A,(HL)7E
    Load Register A with the number type flag at the location of the array variables pointer in HL
    292B
    INC HL23
    Bump the value of the array variables pointer in HL
    292C-292E
    CALL 09C2HCALL MOVRMCD C2 09
    Get the length of the array into Register Pair BC via a CALL to 09C2H (which loads a SINGLE PRECISION value pointed to by HL into Register Pairs BC and DE)
    292F
    PUSH HLE5
    Save the pointer to the DIMS to the STACK
    2930
    ADD HL,BC09
    Add the value of the offset to the next array in BC to the value of the array variables pointer in HL
    2931-2932
    CP 03HFE 03
    Check to see if the array being examined is a string. If they match, the Z FLAG is set, and otherwise the NZ FLAG is set. If A < the checked value, then the C FLAG is set. If A >= the checked value, the NC FLAG is set.
    2933-2934
    JR NZ,2920HJR NZ,ARYVA220 EB
    If the array being examined isn't a string then skip it via a JUMP to 2920H
    2935-2937
    LD (40D8H),HLLD (TEMP3),HL22 D8 40
    Save the address of the end of the array being examined.
    Note: 40D8H-40D9H holds Temporary storage location
    2938
    POP HLE1
    Get the value of the array variables pointer from the STACK and put it in HL
    2939
    LD C,(HL)4E
    Load Register C with the number of subscripts for the array at the location of the array variables pointer in HL
    293A-293B
    LD B,00H06 00
    Zero Register B so that BC holds the number of dimensions
    293C
    ADD HL,BC09
    Add the number of subscripts in the array in BC to the value of the array variables pointer in HL
    293D
    ADD HL,BC09
    Add the number of subscripts in the array in BC to the value of the array variables pointer in HL. Now we should be past the DIMs because we added twice the DIMs and they are 2 bytes each.
    293E
    INC HL23
    Bump the value of the array variables pointer in HL one more time to account for #DIMs.
    293F-  ↳ ARYSTR
    EX DE,HLEB
    Load DE with the value of the current position in the array variables pointer in HL
    2940-2942
    LD HL,(40D8H)LD HL,(TEMP3)2A D8 40
    Load HL with the address of the end of this array.
    Note: 40D8H-40D9H holds Temporary storage location
    2943
    EX DE,HLEB
    Swap DE and HL so that HL now points to the current position
    2944
    RST 18HCOMPARDF
    We need to test for the end of the array space so we need to check to see if the array variables pointer in HL is the same as the address of the next array in DE, so we call the COMPARE DE:HL 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.
    2945-2946
    JR Z,2921HJR Z,ARYVAR28 DA
    If the Z FLAG is set then we are at the end of an array so JUMP to 2921H to try the next array
    2947-2948
    LD BC,293FHLD BC,ARYSTR01 3F 29
    Load BC with a return address of 293FH
    294A-  ↳ DVAR2
    PUSH BCC5
    Save the value in BC to the STACK
    294B-  ↳ DVAR and DVARS
    XOR AAF
    Zero Register A so we can test for a null string
    294C
    OR (HL)B6
    Load Register A with the length of the string at the location of the array variables pointer in HL
    294D
    INC HL23
    Bump the value of the array variables pointer in HL
    294E
    LD E,(HL)5E
    Load Register E with the LSB of the string's address at the location of the array variables pointer in HL
    294F
    INC HL23
    Bump the value of the array variables pointer in HL
    2950
    LD D,(HL)56
    Load Register D with the MSB of the string's address at the location of the array variables pointer in HL. DE should now point to the value of the array.
    2951
    INC HL23
    Bump the value of the array variables pointer in HL
    2952
    RET ZC8
    Return if the string's length in Register A is equal to zero
    2953
    2954
    LD B,H
    LD C,L44
    Let BC = HL
    2955-2957
    LD HL,(40D6H)LD HL,(FRETOP)2A D6 40
    Load HL with the location of the the top of string free space.
    NOTE:40D6H-40D7H holds the next available location in string space pointer
    2958
    RST 18HCOMPARDF
    We need to see if this string's pointer is LESS than the top of free string space by checking to see if the string's address in DE is in string space, so we call the COMPARE DE:HL 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.
    2959
    295A
    LD H,B
    LD L,C60
    Let HL = BC
    295B
    RET CD8
    If this string's pointer is NOT less than the top of string free space, then there is no need to continue working on it, so RETurn
    295C
    POP HLE1
    Get the return address from the STACK and put it in HL
    295D
    EX (SP),HLE3
    Swap (SP) and HL so that the return address is back on the STACK and HL holds the maximum number seen
    295E
    RST 18HCOMPARDF
    Now we need to check to see if the string's address in DE is below the start of string space pointer in HL, so we call the COMPARE DE:HL 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.
    295F
    EX (SP),HLE3
    Swap (SP) and HL so that the return address is back in HL and the STACK holds the maximum number seen
    2960
    PUSH HLE5
    Save the return address in HL to the STACK
    2961
    2962
    LD H,B
    LD L,C60
    Let HL = BC
    2963
    RET NCD0
    Return if the string's address in DE is below the string space pointer
    2964
    POP BCC1
    Get the return address from the STACK and put it in BC
    2965
    POP AFF1
    Clean up the STACK (remove the MAX SEEN)
    2966
    POP AFF1
    Clean up the STACK (remove the VARIABLE POINTER)
    2967
    PUSH HLE5
    Save the value of the array variables pointer in HL to the STACK
    2968
    PUSH DED5
    Save the new MAX pointer in DE to the STACK
    2969
    PUSH BCC5
    Save the value of the return address in BC to the STACK
    296A
    RETC9
    RETurn to CALLer

    If we are here, we have made one complete pass through the string variables.

    296B-  ↳ GRBPAS
    POP DED1
    Load DE with the address of the last string put into the temporary string work area (aka the MAX pointer)
    296C
    POP HLE1
    Get the variable pointer from the STACK and put it in HL
    296D
    LD A,L7D
    Load Register A with the LSB of the variable pointer in Register L
    296E
    OR HB4
    Combine the MSB of the temporary string work area pointer in Register H with the LSB of the temporary string work area pointer in Register A
    296F
    RET ZC8
    If HL=0 then we are at the end of the garbage collection, so return
    2970
    DEC HL2B
    Decrement the value of the temporary string work area pointer in HL, currently just past the string descriptor
    2971
    LD B,(HL)46
    Load Register B with the MSB of the string's address at the location of the temporary string work area pointer in HL
    2972
    DEC HL2B
    Decrement the value of the temporary string work area pointer in HL
    2973
    LD C,(HL)4E
    Load Register C with the LSB of the string's address at the location of the temporary string work area pointer in HL. BC will now point to the string data.
    2974
    PUSH HLE5
    Save the value of the temporary string work area pointer in HL to the STACK. This will be needed to update the pointer after the string is moved.
    2975
    DEC HL2B
    Decrement the value of the temporary string work area pointer in HL
    2976
    LD L,(HL)6E
    Load Register L with the string's length at the location of the temporary string work area pointer in HL
    2977-2978
    LD H,00H26 00
    Zero Register H so that HL is now the string's character count
    2979
    ADD HL,BC09
    Add the length of the string in HL to the string's address in BC. HL now points just beyond the string.
    297A
    LD D,B50
    Load Register D with the MSB of the string's address in Register B
    297B
    LD E,C59
    Load Register E with the LSB of the string's address in Register C. DE now is the original pointer to the string.
    297C
    DEC HL2B
    Decrement the value of the string's ending address in HL to avoid moving one beyond the string
    297D
    LD B,H44
    Load Register B with the MSB of the string's ending address in Register H
    297E
    LD C,L4D
    Load Register C with the LSB of the string's ending address in Register L. BC now points to the top of the string
    297F-2981
    LD HL,(40D6H)LD HL,(FRETOP)2A D6 40
    Load HL with the top of free space.
    NOTE:40D6H-40D7H holds the next available location in string space pointer
    2982-2984
    CALL 1958HCALL BLTUCCD 58 19
    Move the string from the temporary storage location to string space
    2985
    POP HLE1
    Get back the pointer to the description of the variable from the STACK and put it in HL
    2986
    LD (HL),C71
    Save the LSB of the string's permanent address (held in Register C) to (HL)
    2987
    INC HL23
    Bump the value of the temporary string work area pointer in HL
    2988
    LD (HL),B70
    Save the MSB of the string's permanent address (held in Register C) to (HL)
    2989
    LD L,C69
    Load Register L with the LSB of the string's address in Register C
    298A
    LD H,B60
    Load Register H with the MSB of the string's address in Register B. Register Pair HL will now be the new pointer to the string.
    298B
    DEC HL2B
    Decrement the string's address in HL to adjust FRETOP
    298C-298E
    JP 28E9HJP FNDVARC3 E9 28
    Jump to 28E9H to try to find the high again.

    298F-29C5 - STRING ADDITION ROUTINE - Concatenate two strings- "CAT"

    This routine concatenates two strings. The first is pointed to by FACLO and HL points to the character after the "+" sign in on the command line.

    298F-  ↳ CAT
    PUSH BCC5
    Save the precedence/operator value in BC to the STACK
    2990
    PUSH HLE5
    Save the value of the current BASIC program pointer in HL to the STACK
    2991-2993
    LD HL,(4121H)LD HL,(FACLO)2A 21 41
    Load HL with the first string's VARPTR (from ACCumulator)
    2994
    EX (SP),HLE3
    Exchange the value of the first string's VARPTR in HL with the value of the current BASIC program to the STACK
    2995-2997
    CALL 249FHCALL EVALCD 9F 24
    GOSUB to 249FH to evaluate the expression at the location of the current BASIC program pointer in HL
    2998
    EX (SP),HLE3
    Exchange the value of the current BASIC program pointer in HL with the value of the first string's VARPTR to the STACK
    2999-299B
    CALL 0AF4HCALL CHKSTRCD F4 0A
    Go make sure the current result in ACCumulator is a string
    299C
    LD A,(HL)7E
    Load Register A with the first string's length at the location of the first string's VARPTR in HL
    299D
    PUSH HLE5
    Save the value of the first string's descriptor (VARPTR) in HL to the STACK
    299E-29A0
    LD HL,(4121H)LD HL,(FACLO)2A 21 41
    Load HL with the second string's VARPTR in ACCumulator
    29A1
    PUSH HLE5
    Save the second string's VARPTR in HL to the STACK
    29A2
    ADD A,(HL)86
    Add the length of the second string at the location of the second string's VARPTR in HL to the length of the first string in Register A
    29A3-29A4
    LD E,1CH1E 1C
    Load Register E with a ?LS ERRORcode
    in case the two string lengths are not less than 256.
    29A5-29A7
    JP C,19A2HJP C,ERRORDA A2 19
    Display a ?LS ERRORmessage if the combined lengths of the strings is greater than 255
    29A8-29AA
    CALL 2857HCALL STRINICD 57 28
    Go make sure that there is enough string space for the new string
    29AB
    POP DED1
    Get the second string's VARPTR from the STACK and put it in DE
    29AC-29AE
    CALL 29DEHCALL FRETMPCD DE 29
    Go update the temporary string work area
    29AF
    EX (SP),HLE3
    Exchange the second string's VARPTR in HL with the first string's VARPTR to the STACK
    29B0-29B2
    CALL 29DDHCALL FRETM2CD DD 29
    Go update the temporary string work area
    29B3
    PUSH HLE5
    Save the value of the first string's VARPTR in HL to the STACK
    29B4-29B6-  ↳ INCSTR
    LD HL,(40D4H)LD HL,(DSCTMP+1)2A D4 40
    Load HL with the pointer to the first string's address
    29B7
    EX DE,HLEB
    Load DE with the first string's address in HL
    29B8-29BA
    CALL 29C6HCALL MOVINSCD C6 29
    Go move the first string to its temporary storage location
    29BB-29BD
    CALL 29C6HCALL MOVINSCD C6 29
    Go move the second string to its temporary storage location
    29BE-29C0
    LD HL,2349HLD HL,TSTOP21 49 23
    Load HL with the return address
    29C1
    EX (SP),HLE3
    Exchange the value of the return address in HL with the value of the current BASIC program pointer to the STACK
    29C2
    PUSH HLE5
    Save the value of the current BASIC program pointer in HL to the STACK
    29C3-29C5
    JP 2884HJP PUTNEWC3 84 28
    Jump to 2884H to RETurn to TSTOP

    29C6-29D6 - STRING ROUTINE - This will move strings using the STACK- "MOVINS"

    On entry, the STACK should have the count/source address and DE should have the destination address

    29C6-  ↳ MOVINS
    POP HLE1
    Load HL with the value of the return address to the STACK
    29C7
    EX (SP),HLE3
    Swap (SP) and HL so that the return address is now at the top of the STACK and the string's descriptor/VARPTR is in Register Pair HL.

    29C8 - STRING MOVE ROUTINE
    On entry HL points to the string control block for the string to be moved, and DE contains the destination address. All registers are used. The string length and address are not moved. String control blocks have the format: X=String Length; ADDR = String Address.

    29C8
    LD A,(HL)7E
    Load Register A with the string's length at the location of the string's VARPTR in HL
    29C9
    INC HL23
    Bump the value of the string's VARPTR in HL
    29CA
    LD C,(HL)4E
    Load Register C with the LSB of the string's address at the location of the string's VARPTR in HL
    29CB
    INC HL23
    Bump the value of the string's VARPTR in HL
    29CC
    LD B,(HL)46
    Load Register B with the MSB of the string's address at the location of the string's VARPTR in HL. BC now points to the string data.
    29CD
    LD L,A6F
    Load Register L with the string's length in Register A
    29CE-  ↳ MOVSTR
    INC L2C
    Increment the value of the string's length in Register L in preparation for the next instruction which is a loop.
    29CF-  ↳ MOVLP
    DEC L2D
    Decrement the value of the string's length in Register L
    29D0
    RET ZC8
    If L hits zero then we have moved all the characters, so RETurn
    29D1
    LD A,(BC)0A
    If we are here then we still have characters to move. First, load Register A with the character at the location of the string pointer in BC
    29D2
    LD (DE),A12
    Save the character in Register A at the location of the string storage pointer in DE
    29D3
    INC BC03
    Bump the value of the string pointer in BC
    29D4
    INC DE13
    Bump the value of the string storage pointer in DE
    29D5-29D6
    JR 29CFHJR MOVLP18 F8
    Loop until the string has been completely moved

    29D7-29F4 - STRING ROUTINE- "FRESTR"

    According to the original ROM source code, FRETMP is passed a pointer to a string descriptor in Register Pair DE and is returned in Register Pair HL. All the other registers are modified. A check to is made to see if the string descriptor in Register Pair DE points to is the last temporary descriptor allocated by PUTNEW. If so, the temporary is freed up by the updating of TEMPPT. If a temporary is freed up, a further check is made to see if the string data that that string temporary pointed to is the the lowest part of string space in use. If so, FRETMP is updated to reflect the fact that that space is no longer in use.

    This routine is a contination of VAL, FRE, and PRINTprocessing. A jump to here would include the need to get a string's VARPTR and put it in HL.

    29D7-29D9-  ↳ FRESTR
    CALL 0AF4HCALL CHKSTRCD F4 0A
    GOSUB to 0AF4H to make sure that the current result in REG l is a string
    29DA-29DC-  ↳ FREFAC
    LD HL,(4121H)LD HL,(FACLO)2A 21 41
    Load HL with the string's VARPTR in ACCumulator
    29DD-  ↳ FRETM2
    EX DE,HLEB
    Load DE with the value of the string's VARPTR in HL
    29DE-29E0-  ↳ FRETMP
    CALL 29F5HCALL FRETMSCD F5 29
    Check to see if the string is the last entry in the temporary string work area
    29E1
    EX DE,HLEB
    Load HL with the value of the string's VARPTR in DE
    29E2
    RET NZC0
    Return if the string isn't the last entry in the temporary string work area
    29E3
    PUSH DED5
    Save the value of the string's VARPTR in DE to the STACK
    29E4
    LD D,B50
    Load Register D with the MSB of the string's address in Register B
    29E5
    LD E,C59
    Load Register E with the LSB of the string's address in Register C. DE now points to the string.
    29E6
    DEC DE1B
    Decrement the value of the string's address in DE
    29E7
    LD C,(HL)4E
    Load Register C with the string's length at the location of the string's VARPTR in HL
    29E8-29EA
    LD HL,(40D6H)LD HL,(FRETOP)2A D6 40
    Load HL with the next available location in string space pointer for the purpose of testing to see if this is the FIRST one in the string space.
    Note: 40D6H-40D7H holds the next available location in string space pointer
    29EB
    RST 18HCOMPARDF
    We need to see if the current string is the last one defined in the string area by seeing if the string's address in DE is the same as the next available location in string space pointer in HL, so we call the COMPARE DE:HL 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.
    29EC-29ED
    JR NZ,29F3HJR NZ,NOTLST20 05
    Jump forward to 29F3H if the string's address in DE isn't the same as the next available location in string space pointer in HL
    29EE
    LD B,A47
    If is the last one defined then we need to update the current string pointer so first we load Register B with the value in Register A
    29EF
    ADD HL,BC09
    Add the length of the string in BC to the next available location in string space pointer in HL
    29F0-29F2
    LD (40D6H),HLLD (FRETOP),HL22 D6 40
    Save the adjusted next available location in string space pointer in HL.
    Note: 40D6H-40D7H holds the next available location in string space pointer
    29F3-  ↳ NOTLST
    POP HLE1
    Get the string's VARPTR from the STACK and put it in HL
    29F4
    RETC9
    RETurn to CALLer

    29F5-2A02 - STRING ROUTINE - - "FRETMS"
    Test to see if the string in DE is the last string used in the temporary string work area.

    29F5-29F7-  ↳ FRETMS
    LD HL,(40B3H)LD HL,(TEMPPT)2A B3 40
    Load HL with the temporary string work area pointer.
    Note: 40B3H-40B4H holds the next available location in the temporary string work area pointer
    29F8
    DEC HL2B
    Decrement the value of the temporary string work area pointer in HL which backs up two words
    29F9
    LD B,(HL)46
    Load Register B with the MSB of the string's address at the location of the temporary string work area pointer in HL
    29FA
    DEC HL2B
    Decrement the value of the temporary string work area pointer in HL
    29FB
    LD C,(HL)4E
    Load Register C with the LSB of the string's address at the location of the temporary string work area pointer in HL
    29FC
    DEC HL2B
    Decrement the value of the temporary string work area pointer in HL
    29FD
    RST 18HCOMPARDF
    We want to see if Register Pairr DE points to the last by checking to see if the string's VARPTR in DE matches the temporary string work area pointer in HL, so we call the COMPARE DE:HL routine at RST 18H.
    NOTE:
    • The RST 18H routine The result of the comparison is returned in the status Register as: CARRY SET=HL<DE; NO CARRY=HL>DE; NZ=Unequal; Z=Equal.
    29FE
    RET NZC0
    If the Z FLAG is set then we are done freeing space, so RETURN
    29FF-2A01
    LD (40B3H),HLLD (TEMPPT),HL22 B3 40
    Save the value of the temporary string work area pointer in HL.
    Note: 40B3H-40B4H holds the next available location in the temporary string work area pointer
    2A02
    RETC9
    RETurn to CALLer

    2A03-2A0E - LEVEL II BASIC LENROUTINE- "LEN"

    2A03-2A05-  ↳ LEN
    LD BC,27F8HLD BC,SNGFLT01 F8 27
    Load BC with the return address of 27F8H
    2A06
    PUSH BCC5
    Save the return address of 27F8H (in BC) to the STACK
    2A07-2A09-  ↳ LEN1
    CALL 29D7HCALL FRESTRCD D7 29
    GOSUB to 29D7H to free up the temporary variable pointed to by FACLO
    2A0A
    XOR AAF
    Zero Register A so as to force a numeric flag
    2A0B
    LD D,A57
    Zero Register D so that DE can be used when E is set.
    2A0C
    LD A,(HL)7E
    Load Register A with the string's length at the location of the string's VARPTR in HL
    2A0D
    OR AB7
    Set the flags according to the string's length in Register A
    2A0E
    RETC9
    RETurn to CALLer

    2A0F-2A1E - LEVEL II BASIC ASCROUTINE- "ASC"

    2A0F-2A11-  ↳ ASC
    LD BC,27F8HLD BC,SNGFLT01 F8 27
    Load BC with the return address of 27F8H
    2A12
    PUSH BCC5
    Save the return address of 27F8H (in BC) to the STACK
    2A13-2A15-  ↳ ASC2
    CALL 2A07HCALL LEN1CD 07 2A
    GOSUB to 2A07H (which itself is a GOSUB to 29D7H) to get string's VARPTR into HL and the string's length into Register A
    2A16-2A18
    JP Z,1E4AHJP Z,FCERRCA 4A 1E
    If the Z FLAG is set, then this was a null string (bad argument), so display a ?FC ERROR
    2A19
    INC HL23
    Bump the value of the string's VARPTR in HL
    2A1A
    LD E,(HL)5E
    Load Register E with the LSB of the address of the string's data at the location of the string's VARPTR in HL
    2A1B
    INC HL23
    Bump the value of the string's VARPTR in HL
    2A1C
    LD D,(HL)56
    Load Register D with the MSB of the address of the string's data at the location of the string's VARPTR in HL
    2A1D
    LD A,(DE)1A
    Load Register A with the first character at the location of the string pointer in DE
    2A1E
    RETC9
    RETurn to CALLer
    2A1F-2A20-  ↳ CHR$
    LD A,01H3E 01
    Load Register A with the length of the string to be created
    2A21-2A23
    CALL 2857HCALL STRINICD 57 28
    GOSUB to 2857H to save the string's length in Register A and value and set up the string's address
    2A24-2A26
    CALL 2B1FHCALL CONINTCD 1F 2B
    GOSUB to 2B1FH to evaluate the expression at the location of the current BASIC program pointer in HL and return with the integer result in DE
    2A27-2A29-  ↳ SETSTR
    LD HL,(40D4H)LD HL,(DSCTMP+1)2A D4 40
    Load HL with the temporary string's address
    2A2A
    LD (HL),E73
    Save the character in Register E at the location of the string pointer in HL
    2A2B-  ↳ FINBCK
    POP BCC1
    Clean up the STACK so that the RETURN address is another one that is next in the STACK.
    2A2C-2A2E
    JP 2884HJP PUTNEWC3 84 28
    Jump to 2884H

    2A2F-2A60 - LEVEL II BASIC STRING$ROUTINE- "STRNG$"

    2A2F
    RST 10HCHRGETD7
    We have the STRING$command, but now we need the next character so we call a RST 10H to bump the value of 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.
    2A30-2A31
    RST 08H ⇒ 28SYNCHK "("CF 28
    Since the character at the location of the current BASIC program pointer in HL must be a (, call the COMPARE SYMBOL routine at RST 08H.

    NOTE:The RST 08H routine 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 the next execution address (i.e, the RST 08H instruction + 2) with the next symbol in the A Register 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).
    2A32-2A34
    CALL 2B1CHCALL GETBYTCD 1C 2B
    This will get the string length required (which we will call "N") via a GOSUB to 2B1CH to evaluate the expression at the location of the current BASIC program pointer in HL and return with the integer result in DE
    2A34
    DEC HL2B
    Backspace the code string. This is a Z-80 trick because this code was part of the above call instruction
    2A35
    PUSH DED5
    Save the string's length ("N") (currently in DE) to the STACK
    2A36-2A37
    RST 08 ⇒ 2CSYNCHK ","CF 2C
    Now we have STRING$(nnnso the next character needs to be a ,. To test for the character at the location of the current BASIC program pointer in HL being a ,, call 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 the A Register 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)
    2A38-2A3A
    CALL 2337HCALL FRMEVLCD 37 23
    Now we have STRING$(xxx,and an expression so GOSUB to 2337H to evaluate the expression at the location of the current BASIC program pointer in HL and return with the result in ACCumulator
    2A3B-2A3C
    RST 08H ⇒ 29SYNCHK ")"CF 29
    Now we have STRING$(nnn,X. Since the character at the location of the current BASIC program pointer in HL must be a ), call the COMPARE SYMBOL routine at RST 08H.

    NOTE:The RST 08H routine 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 the next execution address (i.e, the RST 08H instruction + 2) with the next symbol in the A Register 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).
    2A3D
    EX (SP),HLE3
    We are now done with getting STRING$(nnn,X). Next we must exchange the value of the current BASIC program pointer in HL with the string's length ("N") in the STACK
    2A3E
    PUSH HLE5
    Save the string's length ("N") in HL to the STACK so that we can test it to make sure it is an integer
    2A3F
    RST 20HGETYPEE7
    We need to check the value of the current data type flag, so we call the TEST DATA MODE routine at RST 20H.
    NOTE:The RST 20H routine determines the type of the current value in ACCumulator and returns a combination of STATUS flags and unique numeric values in the A Register according to the data mode flag (40AFH). The results are returned as follows:
    • 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.
    2A40-2A41
    JR Z,2A47HJR Z,STRSTR28 05
    If that test shows "N" is a a STRING, jump to down to 2A47H
    2A42-2A44
    CALL 2B1FHCALL CONINTCD 1F 2B
    We now know that "N" is at least a number, so we GOSUB to 2B1FH to convert the current result in ACCumulator to an integer and return with the 8-bit result in Register A
    2A45-2A46
    JR 2A4AHJR CALSPA18 03
    Skip the next instruction (which would load the string address and 1st character) by jumping to 2A4AH
    2A47-2A49-  ↳ STRSTR
    CALL 2A13HCALL ASC2CD 13 2A
    Go get the first character in the string and return with it in Register A. This is the character that will be repeated
    2A4A-  ↳ CALSPA
    POP DED1
    Get the "N" from the STACK and put it in DE (well, actually, Register E)
    2A4B
    PUSH AFF5
    Save the character for the string (held in Register A) to the STACK
    2A4C-  ↳ SPACE2
    PUSH AFF5
    and then save it to the STACK again
    2A4D
    LD A,E7B
    Load Register A with "N" (held in Register E)
    2A4E-2A4F
    CALL 2857HCALL STRINICD 57 28
    GOSUB to 2857H to allocate N bytes in the temporary string work area
    2A51
    LD E,A5F
    Load Register E with "N" (held in Register A)
    2A52
    POP AFF1
    Get the character for the string ("X") from the STACK and put it in Register A
    2A53
    INC E1C
    To set the status flags we need to increase and then decrease E. First, bump the value of the string's length in Register E
    2A54
    DEC E1D
    . and then decrement the string's length in Register E
    2A55-2A56
    JR Z,2A2BHJR Z,FINBCK28 D4
    If "N" was zero (so that the string is now complete), jump back to 2A2BH
    2A57-2A59
    LD HL,(40D4H)LD HL,(DSCTMP+1)2A D4 40
    If we are here, we have not finished building the STRING$string so now we load HL with the string's address and get ready to loop
    2A5A-  ↳ SPLP$
    LD (HL),A77
    Save the character in Register A ("X") at the location of the string pointer in HL
    2A5B
    INC HL23
    Bump the value of the string pointer in HL
    2A5C
    DEC E1D
    Decrement the string's length in Register E
    2A5D-2A5E
    Loop back to 2A5AH to keep filling (HL) until the string of X's has been completed
    2A5F-2A60
    JR 2A2BHJR FINBCK18 CA
    Jump back to 2A2BH to put the temp description to finish

    2A61-2A90 - LEVEL II BASIC LEFT$(ROUTINE- "LEFT$"

    On entry, HL=address of LEFT$$, the STACK = string address, STACK+1 = n, and DE = code string address.

    2A61-2A63-  ↳ LEFT$
    CALL 2ADFHCALL PREAMCD DF 2A
    Go check the syntax. The character at the location of the current BASIC program pointer in HL must be a )
    2A64
    XOR AAF
    Zero Register A because the string pointer never changes
    2A65-  ↳ LEFT3
    EX (SP),HLE3
    Exchange the value of the current BASIC program pointer in HL with the string's VARPTR to the STACK
    2A66
    LD C,A4F
    Zero Register C
    2A67-2A68
    LD A,0E5H3E E5
    Z-80 Trick. By adding a 3E at 2A67, it masks out 2A68H if passing through by making the Z-80 think the instruction is LD A,0E5H
    2A69-  ↳ LEFT2
    PUSH HLE5
    Save the value of the string's VARPTR in HL to the STACK
    2A6A
    LD A,(HL)7E
    Load Register A with the string's length at the location of the string's VARPTR in HL
    2A6B
    CP BB8
    Check to see if the new string's length in Register B is greater than the string's length in Register A. If they match, the Z FLAG is set, and otherwise the NZ FLAG is set. If A < the checked value, then the C FLAG is set. If A >= the checked value, the NC FLAG is set.
    2A6C-2A6D
    JR C,2A70HJR C,ALLSTR38 02
    Jump to 2A70H if the new string's length in Register B is greater than the string's length in Register A
    2A6E
    LD A,B78
    Load Register A with the new string's TRUNCATED length in Register B
    2A6F-2A71
    LD DE,000EH11 0E 00
    Z-80 Trick. This is a LD DE,000EHwhich is irrelevant and only executed if continuing through. If, however, one was to jump to 2A70H instead, a proper opcode would occur
    2A72
    PUSH BCC5
    Save the offset in BC to the STACK
    2A73-2A75
    CALL 28BFHCALL GETSPACD BF 28
    Go see if there is enough string space for the new string
    2A76
    POP BCC1
    Get the offset from the STACK and put it in BC
    2A77
    POP HLE1
    Get the string's VARPTR from the STACK and put it in HL
    2A78
    PUSH HLE5
    Save the string's VARPTR in HL to the STACK
    2A79
    INC HL23
    Bump HL to now point to the address of the string
    2A7A
    LD B,(HL)46
    Load Register B with the LSB of the string's address at the location of the string's VARPTR in HL
    2A7B
    INC HL23
    Bump the value of the string's VARPTR in HL
    2A7C
    LD H,(HL)66
    Load Register H with the MSB of the string's address at the location of the string's VARPTR in HL
    2A7D
    LD L,B68
    Load Register L with the LSB of the string's address in Register B
    2A7E-2A7F
    LD B,00H06 00
    Zero Register B so that BC can be used
    2A80
    ADD HL,BC09
    Add the string's length in BC to the string's address in HL
    2A81
    LD B,H44
    Load Register B with the MSB of the string's ending address in Register H
    2A82
    LD C,L4D
    Load Register C with the LSB of the string's ending address in Register L
    2A83-2A85
    CALL 285AHCALL STRAD2CD 5A 28
    Go save the string's length (held in A) and the string's starting address (held in DE)
    2A86
    LD L,A6F
    Load Register L with the string's length (i.e., the number of characters to move) held in Register A
    2A87-2A89
    CALL 29CEHCALL MOVSTRCD CE 29
    Go move L characers frm BC to DE
    2A8A
    POP DED1
    Clean up the STACK
    2A8B-2A8D
    CALL 29DEHCALL FRETMPCD DE 29
    Go update the temporary string work area
    2A8E-2A90
    JP 2884HJP PUTNEWC3 84 28
    Jump to 2884H to put the temp variable in the temp list

    2A91-2A99 - LEVEL II BASIC RIGHT$ ROUTINE- "RIGHT$"

    2A91-2A93
    CALL 2ADFHCALL PREAMCD DF 2A
    Go check the syntax. The character at the location of the current BASIC program pointer in HL must be a )
    2A94
    POP DED1
    Get the string's VARPTR from the STACK and put it in DE
    2A95
    PUSH DED5
    Save the string's VARPTR in DE to the STACK
    2A96
    LD A,(DE)1A
    Load Register A with the string's length (held at the location of the string's VARPTR in DE)
    2A97
    SUB B90
    Subtract the new string's length in Register B from the string's length in Register A to isolate the number of bytes
    2A98-2A99
    JR 2A65HJR LEFT318 CB
    Jump to the LEFT$(code

    2A9A-2AC4 - LEVEL II BASIC MID$ ROUTINE- "MID$"

    The original ROM source code clarifies that if the number provided is greater than the actual length of the string, a NULL value is returned. If the second number (the number of characters) exceeds the end of the string, the maximum amount of available characters are returned.

    2A9A-  ↳ MID$
    EX DE,HLEB
    Load HL with the value of the current BASIC program pointer (held in DE)
    2A9B
    LD A,(HL)7E
    Load Register A with the terminal character, currently held at the location of the current BASIC program pointer in HL
    2A9C-2A9E
    CALL 2AE2HCALL PREAM2CD E2 2A
    GOSUB to 2AE2H to get the offset in Register B and the string's VARPTR in DE
    2A9F
    INC B04
    We need to set the status flags to correspond to the offset position value so we first bump the value of the string's position in Register B
    2AA0
    DEC B05
    ... and then we decrement the value of the string's offset position in Register B
    2AA1-2AAJ
    JP Z,1E4AHJP Z,FCERRCA 4A 1E
    If the starting position (held in B) is 0, display ?FC ERROR
    2AA4
    PUSH BCC5
    Save the value of the offset position (held in Register B) to the STACK
    2AA5-2AA6
    LD E,0FFH1E FF
    Load Register E with the default string's length of 256 in case no number of bytes are given
    2AA7-2AA8
    CP 29HCP ")"FE 29
    More syntax checking. Here we need to test for a )at the location of the current BASIC program pointer. If they match, the Z FLAG is set, and otherwise the NZ FLAG is set. If A < the checked value, then the C FLAG is set. If A >= the checked value, the NC FLAG is set.
    2AA9-2AAA
    JR Z,2AB0HJR Z,MID228 05
    Jump to 2AB0H if the character at the location of the current BASIC program pointer in Register A is a )
    2AAB-2AAC
    RST 08H ⇒ 2CSYNCHK ","CF 2C
    If it wasn't a ), it had best be a ,so we need to test the character at the location of the current BASIC program pointer in HL by calling the COMPARE SYMBOL routine at RST 08H.

    NOTE:The RST 08H routine 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 the next execution address (i.e, the RST 08H instruction + 2) with the next symbol in the A Register 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).
    2AAD-2AAF
    CALL 2B1CHCALL GETBYTCD 1C 2B
    Go evaluate the expression at the location of the current BASIC program pointer in HL and return with the integer result in DE. Now the byte count is in DE as an integer
    2AB0-2AB1
    RST 08H ⇒ 29SYNCHK ")"CD 29
    Since the character at the location of the current BASIC program pointer in HL must be a ), call the COMPARE SYMBOL routine at RST 08H.

    NOTE:The RST 08H routine 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 the next execution address (i.e, the RST 08H instruction + 2) with the next symbol in the A Register 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).
    2AB2
    POP AFF1
    Get the offset from the STACK and put it in Register A
    2AB3
    EX (SP),HLE3
    Exchange the value of the current BASIC program pointer in HL with the value of the string's VARPTR to the STACK
    2AB4-2AB6
    LD BC,2A69HLD BC,LEFT201 69 2A
    Load BC with 2A69H as the return address (which is in the LEFT$routine)
    2AB7
    PUSH BCC5
    Save the return address in BC to the STACK
    2AB8
    DEC A3D
    Decrement the value of the requested offset in Register A so that we have a starting position minus 1
    2AB9
    CP (HL)BE
    Compare the string's length at the location of the string's VARPTR in HL with the value of the offset in Register A. If they match, the Z FLAG is set, and otherwise the NZ FLAG is set. If A < the checked value, then the C FLAG is set. If A >= the checked value, the NC FLAG is set.
    2ABA-2ABB
    LD B,00H06 00
    Zero Register B
    2ABC
    RET NCD0
    If the offset pointer ispast the end of the string we are going to return a null
    2ABD
    LD C,A4F
    Load Register C with the offset in Register A
    2ABE
    LD A,(HL)7E
    Load Register A with the string's length at the location of the string's VARPTR in HL
    2ABF
    SUB C91
    Subtract the index (the second argument) in Register C from the string's length in Register A
    2AC0
    CP EBB
    Compare the new string's length in Register E with the adjusted string's length in Register A to see if we are going to truncate. If they match, the Z FLAG is set, and otherwise the NZ FLAG is set. If A < the checked value, then the C FLAG is set. If A >= the checked value, the NC FLAG is set.
    2AC1
    LD B,A47
    Load Register B with the calculated string's length in Register A
    2AC2
    RET CD8
    If we are not going to truncate, then just use the partial string we already have and Return to 2A69H
    2AC3
    LD B,E43
    Load Register B with the new string's truncated length in Register E
    2AC4
    RETC9
    Return to 2A69H aka LEFT2

    2AC5-2ADE - LEVEL II BASIC VALROUTINE- "VAL"

    2AC5-2AC7-  ↳ VAL
    CALL 2A07HCALL LEN1CD 07 2A
    Go get the string's length in Register A and the string's VARPTR in HL
    2AC8-2ACA
    JP Z,27F8HJP Z,SNGFLTCA F8 27
    Jump to 27F8H if the string's length in Register A is equal to zero
    2ACB
    LD E,A5F
    Load Register E with the string's length at the location of the string's VARPTR in HL.

    The original ROM source explains that we need to do some fancy footwork here to handle the case where the two strings are stored next to each other.

    2ACC
    INC HL23
    Bump the value of the string's VARPTR in HL
    2ACD
    LD A,(HL)7E
    Load Register A with the LSB of the string's address at the location of the string's VARPTR in HL
    2ACE
    INC HL23
    Bump the value of the string's VARPTR in HL
    2ACF
    LD H,(HL)66
    Load Register H with the MSB of the string's address at the location of the string's VARPTR in HL
    2AD0
    LD L,A6F
    Load Register L with the LSB of the string's address in Register A
    2AD1
    PUSH HLE5
    Save the value of the string's address in HL to the STACK
    2AD2
    ADD HL,DE19
    Add the string's length in DE to the string's address in HL
    2AD3
    LD B,(HL)46
    Load Register B with the last character of the string at the location of the string pointer in HL
    2AD4
    LD (HL),D72
    Save the zero in Register D at the location of the string pointer in HL
    2AD5
    EX (SP),HLE3
    Exchange the string's ending address in HL with the string's address to the STACK
    2AD6
    PUSH BCC5
    Save the last character of the string in Register B to the STACK
    2AD7
    LD A,(HL)7E
    Load Register A with the first character of the argument
    2AD8-2ADA
    CALL 0E65HCALL FINDBLCD 65 0E
    Call the ASCII TO DOUBLE routine at 0E65H (which converts the ASCII string pointed to by HL to its double precision equivalent; with output left in ACCumulator)
    2ADB
    POP BCC1
    Get the modified character of the next string into Register B
    2ADC
    POP HLE1
    Get the pointer to the modified character back into HL
    2ADD
    LD (HL),B70
    Restore the character.
    2ADE
    RETC9
    RETurn to CALLer

    2ADF-2A6 - STRING ROUTINE- "PREAM"

    This is called by LEFT$, MID$, and RIGHT$to test for the ending ")" character. On entry, the STACK has the string address, byte count, and return address. On exit the STACK has the string address, DE and B each have the byte count.

    2ADF-  ↳ PREAM
    EX DE,HLEB
    Load HL with the value of the current BASIC program pointer in DE
    2AE0-2AE1
    RST 08H ⇒ 29SYNCHK ")"CF 29
    Since the character at the location of the current BASIC program pointer in HL must be a ), call the COMPARE SYMBOL routine at RST 08H.

    NOTE:The RST 08H routine 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 the next execution address (i.e, the RST 08H instruction + 2) with the next symbol in the A Register 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).
    2AE2-  ↳ PREAM2
    POP BCC1
    Get the return address from the STACK and put it in BC
    2AE3
    POP DED1
    Get the number of bytes to isolate from the string (from the STACK) and put it in DE
    2AE4
    PUSH BCC5
    Save the return address in BC to the STACK
    2AE5
    LD B,E43
    Load Register B with the number of bytes in Register E
    2AE6
    RETC9
    RETurn to CALLer

    2AE7H-2AEE - Process a LEFT-HAND-SIDE MID$- "ISMID$"

    2AE7-2AE8-  ↳ ISMID$
    CP 7AHCP MIDTK-$ENDFE 7A
    This routine is designed to handle a left-size MID$ call. So check to see if the character at the location of the current BASIC program pointer in Register A is trying to process a left hand side MID$. If they match, the Z FLAG is set, and otherwise the NZ FLAG is set. If A < the checked value, then the C FLAG is set. If A >= the checked value, the NC FLAG is set.
    2AE9-2AEB
    JP NZ,1997HJP NZ,SNERRC2 97 19
    Display a ?SN ERRORmessage if that is not what is going on.
    2AEC-2AEE
    JP 41D9HJP DLHSMDC3 D9 41
    Jump to the DOS link at 41D9H to let disk BASIC handle TAB–MID$.

    2AEF-2AF7 - LEVEL II BASIC INPROUTINE- "FNINP"

    2AEF-2AF1-  ↳ FNINP

    CALL 2B1FHCALL CONINTCD 1F 2B
    Go evaluate the expression at the location of the current BASIC program pointer in HL and return with the port number in Register A
    2AF2-2AF4
    LD (4094H),ALD (STAINP+1),A32 94 40
    Save the value of the port number (from Register A) into 4094H, which is in the middle of a routine.
    2AF5-2AF1
    CALL 4093HCALL STAINPCD 1F 2B
    Perform in INP on the channel held in Register A

    2AF8-2B00 - LEVEL II BASIC OUTROUTINE- "FNOUT"

    2AF8-2AFA
    JP 27F8HJP SNGFLTC3 F8 27
    Evaluate the results and return them
    2AFB-2AFD-  ↳ FNOUT

    CALL 2B0EHCALL SETIOCD 0E 2B
    Get the I/O Port Ready
    2AFB-2AFD
    JP 4096HJP OUTWRDCD 0E 2B
    Do the OUT and RETurn

    2B01-2B0D - EVALUATE EXPRESSION ROUTINE- "GETINT"

    This evaluates an expression and leaves the result in DE as an integer.

    2B01-  ↳ GETINT
    RST 10HCHRGETD7
    We need the next character in the BASIC program so 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.
    2B02-2B04-  ↳ GETIN2
    CALL 2337HCALL FRMEVLCD 37 23
    Go evaluate the expression at the location of the current BASIC program pointer in HL and return with the result in ACCumulator

    2B05 - This routine takes the value from the ACC, converts it to an integer value and places the result in the DE Register Pair. The Z flag will be set if the result in DE is smaller than or equal to 255 (FFH). (DE = INT (ACC)).

    2B05-  ↳ INTFR2
    PUSH HLE5
    Save the value of the current BASIC program pointer in HL to the STACK
    2B06-2B08
    CALL 0A7FHCALL FRCINTCD 7F 0A
    Call the CONVERT TO INTEGER routine at 0A7FH (where the contents of ACCumulator are converted from single or double precision to integer and deposited into HL)
    2B09
    EX DE,HLEB
    Load DE with the integer result in HL
    2B0A
    POP HLE1
    Get the value of the current BASIC program pointer from the STACK and put it in HL
    2B0B
    LD A,D7A
    Load Register A with the MSB of the integer result in Register D
    2B0C
    OR AB7
    Test the value of the MSB in Register A
    2B0D
    RETC9
    RETurn to CALLer

    2B0E-2B16 - EVALUATE EXPRESSION ROUTINE - OUTcontinues here- "SETIO"

    2B0E-2B10-  ↳ SETIO
    CALL 2B1CHCALL GETBYTCD 1C 2B
    GOSUB to 2B1CH to evaluate the expression at the location of the current BASIC program pointer in HL and return with the 8-bit value in Register A
    2B11-2B13
    LD (4094H),ALD (STAINP+1),A32 94 40
    Save the 8-bit value in Register A in the DOS address of 4094H to set up for WAIT
    2B14-2B16
    LD (4097H),ALD (OUTWRD+1),A32 97 40
    Save the 8-bit value in Register A in the DOS address of 4097H to set up for OUT

    2B17-2B1A - CHECK SYNTAX ROUTINE - This checks to see if the next character is a "and contnues on to 2B1CH if it is, and errors out if it isn't.

    2B17-2B18
    RST 08H ⇒ 2ESYNCHK ","CF 2E
    Since the character at the location of the current BASIC program pointer in HL must be a ",", call the COMPARE SYMBOL routine at RST 08H.

    NOTE:The RST 08H routine 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 the next execution address (i.e, the RST 08H instruction + 2) with the next symbol in the A Register 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).
    2B19-2B1A
    JR 2B1CHJR GETBYT18 01
    Jump forward to 2B1CH

    2B1B-2B28 - EVALUATE EXPRESSION ROUTINE - This is called by PRINT TAB- "GTBYTC".

    2B1B-  ↳ GTBYTC
    RST 10HCHRGETD7
    We need the next character in the BASIC program so 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.

    2B1C - Common Conversion Routine- "GETBYT"

    This routine converts a numeric ASCII string pointed to by the HL into a hexadecimal value and places the result in the A register. If the result is larger than 255 (FFH) then an FC ERROR (Illegal function call) will be generated. After execution the HL will point to the delimiter. If the delimiter is a zero byte or a colon (3AH) then the Z flag will be set. Any other delimiter will cause the Z flag to be reset.

    2B1C-2B1E-  ↳ GETBYT
    CALL 2337HCALL FRMEVLCD 37 23
    GOSUB to 2337H to evaluate the formula/expression at the location of the current BASIC program pointer in HL and return with the result in REG l
    2B1F-2B21-  ↳ CONINT
    CALL 2B05HCALL INTFR2CD 05 2B
    GOSUB to 2B05H to convert the result in ACCumulator to an integer and return with the integer result in DE. Flags are set based on Register D.
    2B22-2B24
    JP NZ,1E4AHJP NZ,FCERRC2 4A 1E
    If the result is greater than 255, display a ?FC ERRORmessage
    2B25
    DEC HL2B
    Decrement the value of the current BASIC program pointer in HL
    2B26
    RST 10HCHRGETD7
    We need the next character in the BASIC program so 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.
    2B27
    LD A,E7B
    Load Register A with the 8-bit result in Register E so that both A and E have the result.
    2B28
    RETC9
    RETurn to CALLer

    2B29-2B2D - LEVEL II BASIC LLISTROUTINE- "LLIST"

    This routine sets the output device flag to PRINTER and then flows through to the LISTcommand.

    2B29-2B2A-  ↳ LLIST
    LD A,01H3E 01
    Load Register A with the printer output device code
    2B2B-2B2D
    LD (409CH),ALD (PRTFLG),A32 9C 40
    Save the value in Register A as the current output device flag.
    Note: 409CH holds the current output device flag: -1=cassette, 0=video and 1=printer

    2B2E-2B74 - LEVEL II BASIC LISTROUTINE- "LIST"

    On entry the STACK has the return address, then the first basic line number to be listed, then the last basic line number to be listed.

    2B2E-  ↳ LIST
    POP BCC1
    Get rid of the the return address on the STACK
    2B2F-2B31
    CALL 1B10HCALL SCNLINCD 10 1B
    Go evaluate the range of line numbers given at the location of the current BASIC program pointer in HL
    2B32
    PUSH BCC5
    Save the address of the first BASIC line (held in BC) to the STACK
    2B33-2B35-  ↳ LIST4
    LD HL,FFFFH21 FF FF
    Load HL with a -1. This is because the below loop starts with a INC HL, so as to turn the first line number into 0
    2B36-2B38
    LD (40A2H),HLLD (CURLIN),HL22 A2 40
    Save the value in HL as the current BASIC line number (which is stored at 40A2H-40A3H).
    2B39
    POP HLE1
    Get the address of the first BASIC line to be listed (from the STACK) and put it in HL
    2B3A
    POP DED1
    Get the value of the last BASIC line number to be listed (from the STACK) and put it in DE
    2B3B
    LD C,(HL)4E
    Load Register C with the LSB of the next BASIC line pointer at the location of the memory pointer in HL
    2B3C
    INC HL23
    Bump the value of the memory pointer in HL
    2B3D
    LD B,(HL)46
    Load Register B with the MSB of the next BASIC line pointer at the location of the memory pointer in HL
    2B3E
    INC HL23
    Bump the value of the memory pointer in HL
    2B3F
    LD A,B78
    Load Register A with the MSB of the next BASIC line pointer in Register B
    2B40
    OR CB1
    Combine the LSB of the next BASIC line pointer in Register C with the MSB of the next BASIC line pointer in Register A. This will let us test for the end of the BASIC program
    2B41-2B43
    JP Z,1A19HJP Z,READYCA 19 1A
    If we are at the elast line, then STOP and JUMP to 1A19H to the READY PROMPT.
    2B44-2B46
    CALL 41DFHCALL EXCHDSCD DF 41
    GOSUB to DOS to see if DOS wants to do anything here.
    2B47-2B49
    CALL 1D9BHCALL ISCNTCCD 9B 1D
    Go scan the keyboard to see if the BREAKkey or the shift-@key was pressed
    2B4A
    PUSH BCC5
    Save the address of the next BASIC line in BC to the STACK
    2B4B
    LD C,(HL)4E
    We now want to push the line number, but we have to load BC with it first. Load Register C with the LSB of the BASIC line number at the location of the memory pointer in HL
    2B4C
    INC HL23
    Bump the value of the memory pointer in HL
    2B4D
    LD B,(HL)46
    Load Register B with the MSB of the BASIC line number at the location of the memory pointer in HL
    2B4E
    INC HL23
    Bump the value of the memory pointer in HL
    2B4F
    PUSH BCC5
    Save the BASIC line number in BC to the STACK
    2B50
    EX (SP),HLE3
    Swap (SP) and HL so that the line number is now in HL
    2B51
    EX DE,HLEB
    Swap DE and HL so that the last BASIC line number is now in HL
    2B52
    RST 18HCOMPARDF
    We need to see if we are outside the last number in the specified range so compare the BASIC line number in DE with the last BASIC line number in HL, so we call the COMPARE DE:HL 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.
    2B53
    POP BCC1
    Get the pointer to the location on the BASIC program line being processed and put it in BC
    2B54-2B56
    JP C,1A18HJP C,STPRDYDA 18 1A
    If the BASIC line number in DE is greater than the last BASIC line number in HL then we have gone past the end, so we are done processing!
    2B57
    EX (SP),HLE3
    Swap (SP) and HL so that the last BASIC line number is now on the STACK
    2B58
    PUSH HLE5
    Save the address of the next BASIC line in HL to the STACK
    2B59
    PUSH BCC5
    Save the pointer to the location on the BASIC program line being processed to the STACK
    2B5A
    EX DE,HLEB
    Load HL with the BASIC line number (from DE)
    2B5B-2B5D
    LD (40ECH),HLLD (DOT),HL22 EC 40
    Save the BASIC line number in HL into DOT for use later in EDIT or LIST.
    Note: 40ECH-40EDH holds EDIT line number
    2B5E-2B60
    CALL 0FAFHCALL LINPRTCD AF 0F
    Call the HL TO ASCII routine at 0FAFH (which converts the value in the HL (assumed to be an integer) to ASCII and display it at the current cursor position on the video screen) to display the current BASIC line number
    2B61-2B62
    LD A,20H3E 20
    Load Register A with a space
    2B63
    POP HLE1
    Get the value of the memory pointer from the STACK and put it in HL
    2B64-2B66
    CALL 032AHCALL OUTDOCD 2A 03
    Go send the space in Register A to the current output device
    2B67-2B69
    CALL 2B7EHCALL BUFLINCD 7E 2B
    GOSUB to 2B7EH move the BASIC line at the location of the memory pointer in HL into the input buffer and untokenize the BASIC line
    2B6A-2B6C
    LD HL,(40A7H)LD HL,(BUFPNT)2A A7 40
    Load HL with the starting address of the input buffer.
    Note: 40A7H-40A8H holds the input Buffer pointer
    2B6D-2B6F
    CALL 2B75HCALL LISPRTCD 75 2B
    Since we need to send the BASIC line in the input buffer to the current output device we call the PRINT MESSAGE routine at 2B75H which writes string pointed to by HL to the current output device
    2B70-2B72
    CALL 20FEHCALL CRDOCD FE 20
    Go send a carriage return to the current output device
    2B73-2B74
    JR 2B33HJR LIST418 BE
    Loop back to 2B33H until the listing is complete

    2B75-2B7D - DISPLAY MESSAGE ROUTINE- "LISPRT"

    This is the PRINT MESSAGE routine which writes string pointed to by HL to the current output device. String must be terminated by a byte of zeros. This call is different from 28A7H because it does not use the literal string pool area, but it does use the same display routine and it takes the same DOS Exit at 41C1H. Uses all registers. This routine can be called without loading the BASIC utility, if a C9H (RET) is stored in 41ClH.

    This routine outputs a string to device indicated by device type flag stored at 409CH. String must end with zero byte. On entry, HL registers must point to address of start of string. Calls routine at 032AH.

    2B75"LISPRT"

    LD A,(HL)7E
    Load Register A with the character at the location of the memory pointer in HL
    2B76
    OR AB7
    Check to see if the character in Register A is an end of the string character (00H)
    2B77
    RET ZC8
    Return if the character in Register A is an end of the string character
    2B78-2B7A
    CALL 032AHCALL OUTDOCD 2A 03
    Go send the character in Register A to the current output device
    2B7B
    INC HL23
    Bump the value of the memory pointer in HL
    2B7C-2B7D
    JR 2B75HJR LISPRT18 F7
    Loop back to 2B75H until all of the characters have been sent to the current output device

    2B7E-2BC5 - UNTOKENIZE ROUTINE- "BUFLIN"

    This routine is called by LISTand EDIT. It moves the line pointed to by HL to the input buffer area and then expands each token into the appropriate key word.

    2B7E-  ↳ BUFLIN

    PUSH HLE5
    Save the BASIC line pointer in HL to the STACK
    2B7F-2B81
    LD HL,(40A7H)LD HL,(BUFPNT)2A A7 40
    Load HL with the starting address of the input buffer.
    Note: 40A7H-40A8H holds the input Buffer pointer
    2B82
    2B83
    LD B,H
    LD C,L44
    LET Register Pair BC = Register Pair HL
    2B84
    POP HLE1
    Get the value of the BASIC line pointer from the STACK and put it in HL
    2B85
    JUMP to 069AH to clear A and all flags, load the data flag (at 409FH) with 0, load D with 255 (a buffer), and JUMP to 2B8DH.
    2B88
    NOP
    2B89-  ↳ PLOOP
    INC BC03
    Top of a loop. Bump the value of the input buffer pointer in BC
    2B8A
    DEC D15
    Decrement the character count in Register D
    2B8B
    RET ZC8
    Return if 256 characters have been moved into the input buffer
    2B8C
    INC HL
    Move one byte forward in the text.
    2B8D
    LD A,(HL)
    Load register A with the character at the location of the BASIC line pointer in HL.
    2B8E
    OR A
    Set the status flags to enable us to check to see if the character in register A is an end of the BASIC line character.
    2B8F
    LD (BC),A02
    Save the character at the location of the input buffer pointer (held in Register A)to the memory location held by BC. If the character was a 00H terminator, then the terminator will also be copied.
    2B90
    RET ZC8
    Return if the character in Register A is an end of the BASIC line character
    2B91
    Jump to 302DH, which JUMPS to 377B.
    2B94
    CP FBH
    Check to see if the character in register A is a ' token.
    2B96-2B97
    JR NZ,2BA0HJR NZ,NTQTTK20 08
    Jump forward to 2BA0H if the character in Register A isn't a 'token

    This is where the infamous ROM bug which can crash Level II sits. It assumes that a 'has room to move backwards 4 characters, which it might not!

    2B98-2B9B
    DEC BC
    DEC BC
    DEC BC
    DEC BC0B
    First, backspace 4 characters to compensat for ":REM" which is otherwise hidden from the user's view. Decrement the value of the input buffer pointer in BC
    2B9C-2B9F
    INC D
    INC D
    INC D
    INC D14
    Then, bump the value of the character counter in Register D 4 times

    A REM isn't the only TOKEN with a hidden add-on. ELSE also has a hidden colon in front of it. So let's now deal with that.

    2BA0-2BA1-  ↳ NRQTTK
    CP 95HCP $ELSEFE 95
    Check to see if the character in Register A is an ELSEtoken. If they match, the Z FLAG is set, and otherwise the NZ FLAG is set. If A < the checked value, then the C FLAG is set. If A >= the checked value, the NC FLAG is set.
    2BA2-2BA4
    CALL Z,0B24HCALL Z,DCXVBRTCC 24 0B
    If it was an ELSEwe need to backspace the expanded buffer pointer to NOT print the hidden ":". To do this, go back to 0B24H to decrement the value of the input buffer pointer if the character in Register A is an ELSE token
    2BA5-2BA6
    SUB 7FHD6 7F
    Next, we need to get rid of the SIGN BIT and add one, so subtract 7F to get the number of the entry we are looking for in token list
    2BA7
    PUSH HLE5
    Save the value of the BASIC line pointer in HL to the STACK. Register L holds the reserved word number at this point.
    2BA8
    LD E,A5F
    Load Register E with the character in Register A
    2BA9-2BAB
    LD HL,1650HLD HL,RESLST21 50 16
    Load HL with the starting address of the reserved words list
    2BAC-  ↳ LOPRES
    LD A,(HL)7E
    Load Register A with the character at the location of the reserved words list pointer in HL
    2BAD
    OR AB7
    Test the value of the character in Register A. The P FLAG will be set on the first character of each TOKEN because it has the high bit set.
    2BAE
    INC HL23
    Bump the value of the reserved words list pointer in HL
    2BAF-2BB1
    JP P,2BACHJP P,LOPRESF2 AC 2B
    If the character at the location of the reserved words pointer in Register A doesn't have bit 7 set then Jump back to 2BACH.
    2BB2
    DEC E1D
    Decrement the counter
    2BB3-2BB4
    JR NZ,2BACHJR NZ,LOPRES20 F7
    Jump back to 2BACH if this isn't the reserved word for the token
    2BB5-2BB6
    AND 7FHE6 7F
    Reset bit 7 of the character in Register A by ANDing it against 0111 1111. This is to eliminate the MSB for "EDIT" and for disk I/O
    2BB7-  ↳ MORPUR
    LD (BC),A02
    Save the character in Register A at the location of the input buffer pointer in BC
    2BB8
    INC BC03
    Bump the value of the input buffer pointer in BC
    2BB9
    DEC D15
    Decrement the value of the character counter in Register D
    2BBA-2BBC
    JP Z,28D8HJP Z,PPSWRTCA D8 28
    If the Z FLAG has been set, then the character counter for the buffer has been exhausted and the buffer is now full, so JUMP back to 28D8H
    2BBD
    LD A,(HL)7E
    Load Register A with the character at the location of the reserved words pointer in HL
    2BBE
    INC HL23
    Bump the reserved words pointer in HL
    2BBF
    OR AB7
    Test the value of the character in Register A
    2BC0-2BC2
    JP P,2BB7HJP P,MORPURF2 B7 2B
    Keep getting characters in this reserved word until we hit the next reserved word
    2BC3
    POP HLE1
    Get the value of the BASIC line pointer from the STACK and put it in HL
    2BC4-2BC5
    JR 2B8CHJR PLOOP218 C6
    Jump back to 2B8CH to continue processing the BASIC line being interpreted

    2BC6-2BF4 - LEVEL II BASIC DELETE ROUTINE- "DELETE"

    This routine is called by LISTand EDIT. It moves the line pointed to by HL to the input buffer area and then expands each token into the appropriate key word.

    2BC6-2BC8-  ↳ DELETE
    CALL 1B10HCALL SCNLINCD 10 1B
    GOSUB to 1B10H to evaluate the line numbers at the location of the current BASIC program pointer in HL
    2BC9
    POP DED1
    Get the value of the last BASIC line number to be deleted (in binary) from the STACK and put it in DE
    2BCA
    PUSH BCC5
    Save the address of the first BASIC line to be deleted in BC to the STACK
    2BCB
    PUSH BCC5
    Save the address of the first BASIC line to be deleted in BC to the STACK AGAIN!
    2BCC-2BCE
    CALL 1B2CHCALL FNDLINCD 2C 1B
    GOSUB to 1B2CH to the SEARCH FOR LINE NUMBER routine which looks for the line number specified in DE so as to get the address of the last line to be deleted.Returns C/Z with the line found in BC, NC/Z with line number is too large and HL/BC having the next available location, or NC/NZ with line number not found, and BC has the first available one after that
    2BCF-2BD0
    JR NC,2BD6HJR NC,FCERRG30 05
    Since the first line number provided MUST be found, if FNDLIN returns with the NC FLAG set (i.e., not found) we must JUMP to 2BD6H to show a ?FC ERROR
    2BD1
    2BD2
    LD D,H
    LD E,L54
    Let Register Pair DE = Register Pair HL
    2BD3
    EX (SP),HLE3
    Exchange the last BASIC line's address in HL with the first BASIC line's address to the STACK
    2BD4
    PUSH HLE5
    Save the pointer to the first line in range to the STACK
    2BD5
    RST 18HCOMPARDF
    We need to check to see if the first BASIC line's address in HL is greater than or equal to the last BASIC line's address in DE, so we call the COMPARE DE:HL 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.
    2BD6-2BD8-  ↳ FCERRG
    JP NC,1E4AHJP NC,FCERRD2 4A 1E
    Display a ?FC ERRORmessage if the first BASIC lines address in HL is greater than or equal to the last BASIC line's address in DE
    2BD9-2BDB
    LD HL,1929HLD HL,REDDY21 29 19
    Load HL with the starting address of the BASIC READY message
    2BDC-2BDE
    CALL 28A7HCALL STROUTCD A7 28
    Call the WRITE MESSAGE routine at 28A7H to print the message pointed to by HL.
    2BDF
    POP BCC1
    Get the first BASIC line's address from the STACK and put it in BC
    2BE0-2BE2
    LD HL,1AE8HLD HL,FINI21 E8 1A
    Load HL with the return address
    2BE3
    EX (SP),HLE3
    Swap (SP) and HL so that HL now points to the next BASIC line's address ...
    2BE4-  ↳ DEL
    EX DE,HLEB
    and then put it into DE
    2BE5-2BE7
    LD HL,(40F9H)LD HL,(VARTAB)2A F9 40
    Load HL with the end of the BASIC program pointer.
    • Note: 40F9H-40FAH holds the starting address of the simple variable storage area.
    2BE8-  ↳ MLOOP
    LD A,(DE)1A
    Load Register A with the character at the location of the memory pointer in DE
    2BE9
    LD (BC),A02
    Save the character in Register A at the location of the memory pointer in BC
    2BEA
    INC BC03
    Bump the value of the memory pointer in BC
    2BEB
    INC DE13
    Bump the value of the memory pointer in DE
    2BEC
    RST 18HCOMPARDF
    Now we need to check to see if the memory pointer in DE equals the end of the BASIC program pointer in HL, so we call the COMPARE DE:HL 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.
    2BED-2BEE
    JR NZ,2BE8HJR NZ,MLOOP20 F9
    Loop back to 2BE8H until the memory pointer in DE equals the end of the BASIC program pointer in HL
    2BEF
    LD H,B60
    Load Register H with the MSB of the memory pointer in Register B
    2BF0
    LD L,C69
    Load Register L with the LSB of the memory pointer in Register C
    2BF1-2BF3
    LD (40F9H),HLLD (VARTAB),HL22 F9 40
    Save the value in HL as the new end of the BASIC program pointer.
    • Note: 40F9H-40FAH holds the starting address of the simple variable storage area.
    2BF4
    RETC9
    RETurn to CALLer

    2BF5-2C1E - LEVEL II BASIC CSAVE ROUTINE- "CSAVE"

    The original ROM source code says that the CSAVE command dump's BASIC's core. Three D3H's are written, followed by a 1 character filename. At the end 3 zeros in a row are written.

    2BF5-2BF7-  ↳ CSAVE
    CALL 0284HCALL CWRTONCD 84 02
    Calls the WRITE LEADER routine at 0284H (which writes a Level II leader on the cassette unit set in Register A)
    2BF8
    CALL 2337HCALL FRMEVLCD 37 23
    2BFAH Go evaluate the rest of the CSAVEexpression at the location of the current BASIC program pointer in HL and return with the result in REG l
    2BFB
    PUSH HLE5
    Save the value of the current BASIC program pointer in HL to the STACK so we can get it back at the end of the routine
    2BFC-2BFE
    CALL 2A13HCALL ASC2CD 13 2A
    Go get the starting address of the filename into DE
    2BFF-2C00
    LD A,D3H3E D3
    Load Register A with the filename header byte (=D3H which is a "S" with the sign bit on)
    2C01-2C03
    CALL 0264HCALL CASOUTCD 64 02
    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), which in this case the filename header byte
    2C04-2C06
    CALL 0261HCALL TWOCSOCD 61 02
    Go write the filename header byte in Register A twice more
    2C07
    LD A,(DE)1A
    Load Register A with the first character of the filename at the location of the filename pointer in DE
    2C08-2C0A
    CALL 0264HCALL CASOUTCD 64 02
    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), which in this case is the filename
    2C0B-2C0D
    LD HL,(40A4H)LD HL,(TXTTAB)2A A4 40
    Load HL with the start of the BASIC program pointer.
    • Note: 40A4H-40A5H holds the starting address of BASIC program text also known as the PROGRAM STATEMENT TABLE (PST).
    2C0E
    EX DE,HLEB
    Load DE with the start of the BASIC program pointer in HL
    2C0F-2C11
    LD HL,(40F9H)LD HL,(VARTAB)2A F9 40
    Load HL with the end of the BASIC program pointer.
    • Note: 40F9H-40FAH holds the starting address of the simple variable storage area.
    2C12-  ↳ LOPSCO
    LD A,(DE)1A
    Top of a loop. We are going to loop from DE (start of program) to HL (end of program) now. Load Register A with the character at the location of the memory pointer in DE
    2C13
    INC DE13
    Bump the value of the memory pointer in DE
    2C14-2C16
    CALL 0264HCALL CASOUTCD 64 02
    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)
    2C17
    RST 18HCOMPARDF
    Now we need to check to see if the memory pointer in DE is equal to the end of the BASIC program pointer in HL, so we call the COMPARE DE:HL 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.
    2C18-2C19
    JR NZ,2C12HJR NZ,LOPCSO20 F8
    Loop back to 2C12H until the memory pointer in DE is equal to the end of the BASIC program pointer in HL
    2C1A-2C1C
    CALL 01F8HCALL CTOFFCD F8 01
    All done! GOSUB 01F8H to turn the cassette recorder off
    2C1D
    POP HLE1
    Get the value of the current BASIC program pointer from the STACK and put it in HL
    2C1E
    RETC9
    RETurn to CALLer

    2C1F-2CA4 - LEVEL II BASIC CLOAD ROUTINE - ROM v1.0- "CLOAD"

    2C1F-2C21-  ↳ CLOAD
    CALL 0293HCALL CSRDONCD 93 02
    Go turn on the cassette recorder
    2C22
    LD A,(HL)7E
    Load Register A with the character at the location of the current BASIC program pointer in HL
    2C23-2C24
    SUB 0B2HSUB $PRINTD6 B2
    Check to see if the character at the location of the current BASIC program pointer in Register A is a ?, meaning that CLOAD?was requested.
    2C25-2C26
    JR Z,2C29HJR Z,CLOADP28 02
    Jump to the CLOAD?routine at 2C29H if the character at the location of the current BASIC program pointer in Register A is a ?
    2C27
    XOR AAF
    OK - So this is now a straight CLOAD. First, zero Register A
    2C28
    LD BC,232F01 2F 23
    Z-80 Trick! The next instruction would set the flag for a CLOAD?which we don't want if we are passing through because we need A to be 0, so 2C28H starts with a 01H which would be a meaningless LOAD statement and assumes that the following instruction at 2C29H is what to load it with, effectively skipping 2C29H. BUT, if you jump to 2C29H you get the actual XOR command and keep going
    2C29-  ↳ CLOADP
    CPL2F
    Load Register A with a -1 for CLOAD?. It will still be a 0 if this is CLOAD
    2C2A
    INC HL23
    Bump the value of the current BASIC program pointer in HL until it points to the next character after the ?in CLOAD?
    2C2B
    PUSH AFF5
    Save the CLOAD/CLOAD?flag in Register A to the STACK
    2C2C
    DEC HL2B
    Decrement the value of the current BASIC program pointer in HL so we can see if we are at the end
    2C2D
    RST 10HCHRGETD7
    We need the next character in the BASIC program so 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.
    2C2E-2C2F
    LD A,00H3E 00
    Zero Register A to allow for any filename
    2C30-2C31
    JR Z,2C39HJR Z,CLNONM28 07
    Jump if the character at the location of the current BASIC program pointer in HL is an end of the BASIC statement character
    2C32-2C34
    CALL 2337HCALL FRMEVLCD 37 23
    To get the filename we need to GOSUB to 2337H to evaluate the expression at the location of the current BASIC program pointer in HL and return with the result in ACCumulator
    2C35-2C37
    CALL 2A13HCALL ASC2CD 13 2A
    Make sure the length is good, and save the pointer to the filename to Register Pair DE
    2C38
    LD A,(DE)1A
    Load Register A with the first character of the filename at the location of the filename pointer in DE
    2C39-  ↳ CLNONM
    LD L,A6F
    Load Register L with the filename in Register A
    2C3A
    POP AFF1
    Get the value of the CLOAD/CLOAD?flag from the STACK and put it in Register A
    2C3B
    OR AB7
    Test the value of the CLOAD/CLOAD?flag in Register A (since CPL doesn't set any flags)
    2C3C
    LD H,A67
    Load Register H with the value of the CLOAD/CLOAD?flag in Register A
    2C3D-2C3F
    LD (4121H),HLLD (FACLO),HL22 21 41
    Save the value of the CLOAD/CLOAD?flag and the filename in HL in ACCumulator
    2C40-2C42
    CALL Z,1B4DHCALL Z,SCRTCHCC 4D 1B
    Call the NEWroutine at 1B4D if it is a CLOAD
    2C43-2C45
    LD HL,(4121H)LD HL,(FACLO)2A 21 41
    Load HL with the CLOAD/CLOAD?flag and the filename in ACCumulator
    2C46
    EX DE,HLEB
    Load D with the CLOAD/CLOAD? flag and load Register E with the filename
    2C47-2C48-  ↳ LOPCLK
    LD B,03H06 03
    Load Register B with the number of header bytes
    2C49-2C4B-  ↳ LOPCL2
    CALL 0235HCALL CASINCD 35 02
    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)
    2C4C-2C4D
    SUB 0D3HD6 D3
    Check to see if the character in Register A is a filename header byte
    2C4E-2C4F
    JR NZ,2C47HJR NZ,LOPCLK20 F7
    Loop if the character in Register A isn't a filename header byte
    2C50-2C51
    DJNZ 2C49HDJNZ LOPCL210 F7
    Loop back to 2C49H until three filename header bytes have been read
    2C52-2C54
    CALL 0235HCALL CASINCD 35 02
    Now that the header is out of the way, let's start working on the filename. GOSUB to 0235H to the READ ONE BYTE FROM CASSETTE (which reads one byte from the cassette drive specified in Register A, and returns the byte in Register A)
    2C55
    INC E1C
    We need to test to see if a filename was even given so we have to increase and decrease E to set flags . Bump the value of the filename in Register E
    2C56
    DEC E1D
    Decrement the value of the filename in Register E
    2C57-2C58
    JR Z,2C5CHJR Z,NONAMC28 03
    Jump to 2C5CH (to pretend the filename matched) if no filename was specified
    2C59
    CP EBB
    If we are here, then the user has supplied a filename which is held in Register E AND we have the first byte from the tape in Register A, so we need to compare the filename specified in Register E with the character in Register A. If they match, the Z FLAG is set, and otherwise the NZ FLAG is set. If A < the checked value, then the C FLAG is set. If A >= the checked value, the NC FLAG is set.
    2C5A-2C5B
    JR NZ,2C93HJR NZ,SKPFIL20 37
    Jump to 2C93H (to skip to the end of that file) if the filename specified in Register E doesn't match the byte read from tape in Register A
    2C5C-2C5E-  ↳ NONAMC
    LD HL,(40A4H)LD HL,(TXTTAB)2A A4 40
    If we are here, the filename on tape matches the filename given so lets start loading. Load HL with the start of the BASIC program pointer.
    • Note: 40A4H-40A5H holds the starting address of BASIC program text also known as the PROGRAM STATEMENT TABLE (PST).

    This loop is going to read a byte, compare it to the next byte in the program memory, jump away if it doesn't match AND CLOAD? was chosen, write (or overwrite) that byte to memory, check for a zero, and loop back if no zero was found.

    2C5F-2C60-  ↳ DOCRS
    LD B,03H06 03
    Load Register B with the number of zeros to look for to stop the load (which is 3)
    2C61-2C63-  ↳ DOCSMR
    CALL 0235HCALL CASINCD 35 02
    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)
    2C64
    LD E,A5F
    Preserve the character that was just read from tape into Register E
    2C65
    SUB (HL)96
    Compare the character we just read from the tape (held in Register A) with the character at the location of the memory pointer in HL by subtracting them
    2C66
    AND DA2
    Combine the subtracted result in Register A with the value of the CLOAD/CLOAD?flag in Register D. Why is this tricky? Because D is always 0 for a CLOAD, so when you AND against 0, you always get 0. If this was CLOAD?, nothing would happen as a result of this.
    2C67-2C68
    JR NZ,2C8AHJR NZ,NOGOOD20 21
    Jump to 2C8AH if CLOAD?was selected but the bytes don't match
    2C69
    LD (HL),E73
    At this point either CLOAD?was selected and the bytes match, or CLOADwas selected. Either way, save the character we read from the tape (held in Register E) to the location of the memory pointer in HL
    2C6A-2C6C
    CALL 196CH
    CALL REASONCD 6C 19
    Make sure there is more room, and toss a ?OM ERRORif there isn't.
    2C6D
    LD A,(HL)7E
    Load Register A with the character at the location of the memory pointer in HL
    2C6E
    OR AB7
    Check to see if the byte just read in Register A is equal to zero
    2C6F
    INC HL23
    Bump the value of the memory pointer in HL
    2C70-2C71
    JR NZ,2C5FHJR NZ,DOCRS20 ED
    Loop if the byte in Register A isn't equal to zero (meaning that it isn't end of program or end of statement)
    2C72-2C74
    CALL 022CHCALL BCASINCD 2C 02
    Call the BLINK ASTERISK routine at 022CH which alternatively displays and clears an asterisk in the upper right hand corner of the video display
    2C75-2C76
    DJNZ 2C61HDJNZ DOCSMR10 EA
    Do that loop until three zeros in a row have been read from the cassette recorder
    2C77-2C79
    LD (40F9H),HLLD (VARTAB),HL22 F9 40
    By this point, HL will have been incremented all the way through the program. Save the value of the memory pointer in HL as the new end of the BASIC program pointer.
    • Note: 40F9H-40FAH holds the starting address of the simple variable storage area.
    2C7A-2C7C
    GOSUB to 01F8H to turn off the tape.
    Difference between M1 and M3 ROMs: Also part of CLOAD, this change turns off the tape before printing READY on the video display. In the Model I, the code from 2C7AH - 2C82H consisted of the instructions LD HL,1929H; CALL 28A7H, CALL 01F8H. In the Model III the CALL to 01F8H has been moved to the beginning of these instructions.
    2C7D-2C7F
    Load HL with the starting address of the BASIC READY message.
    2C80-2C82
    We need to display the Level II BASIC READY message, so we 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).
    2C83-2C85
    LD HL,(40A4H)LD HL,(TXTTAB)2A A4 40
    Load HL with the start of the BASIC program pointer.
    • Note: 40A4H-40A5H holds the starting address of BASIC program text also known as the PROGRAM STATEMENT TABLE (PST).
    2C86
    PUSH HLE5
    Save the start of the BASIC program pointer in HL to the STACK. FINI will need this value there.
    2C87-2C89
    JP 1AE8HJP FINIC3 E8 1A
    Jump to 1AE8H to reinitialize the BASIC interpreter and continue
    2C8A-2C8C
    GOSUB to 31BDH to reset the cassette I/O.
    Difference between M1 and M3 ROMs: This is part of the CLOAD? routine used when a bad byte has been read from the tape. In the Model I, a LD HL, 2CA5H instruction is found here (loads HL with the starting address of the "BAD" message), while the Model III uses a CALL 31BDH instruction, which does some housekeeping in addition to pointing HL to the "BAD" message.
    2C8D-2C8F
    CALL 28A7HCALL STROUTCD A7 28
    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).
    2C90-2C92
    JP 1A18JP STPRDYC3 18 1A
    JUMP to STPRDY to pop NEWSTT from the STACK and then fall into the READY routine
    2C93-2C95-  ↳ SKPFIL
    LD (3C3EH),A32 3E 3C
    Go display the filename on the video display
    2C96-2C97-  ↳ ZERSRF
    LD B,03H06 03
    Load Register B with the number of zeros to be found to stop the search
    2C98-2C9A-  ↳ GETCHZ
    CALL 0235HCALL CASINCD 35 02
    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)
    2C9B
    OR AB7
    Check to see if the character in Register A is equal to zero
    2C9C-2C9D
    JR NZ,2C96HJR NZ,ZERSRF20 F8
    Loop if the character in Register A isn't equal to zero
    2C9E-2C9F
    DJNZ 2C98HDJNZ GETCHZ10 F8
    Loop until three zeros in a row have been read from the cassette recorder
    2CA0-2CA2
    CALL 0296HCALL CSRDON + 3CD 96 02
    Calls the READ CASSETTE LEADER routine at 0296 (which reads from the cassette recorder selected in Register A until the end-of-leader marker of A5H is found; and flashes the cursor while doing this)
    2CA3-2CA4
    JR 2C47HJR LOPCLK18 A2
    Jump back to 2C47H

    2CA5-2CA9 - MESSAGE STORAGE LOCATION- "NOOKCS"

    2CA5-2CA9-  ↳ NOOKCS
    "BAD" + 0DH + 00H42
    The BAD message is stored here

    2CAA-2CB0 - LEVEL II BASIC PEEKROUTINE- "PEEK"

    The original ROM source code says that PEEK only accepts positive numbers up to 32767 and POKE will only take an address up to 32767. Negative numbers can be used to refer to locations higher than 32767, the correspondence is given by subtracting 65536 from locations higher than 32767 or by specifying a positive number up to 65535

  • On entry, ACCumulator to have the peek location, and on exit ACCumulator to have the peeked value.
  • 2CAA-2CAC-  ↳ PEEK
    CALL 0A7FHCALL FRCINTCD 7F 0A
    Call the CONVERT TO INTEGER routine at 0A7FH (where the contents of ACCumulator are converted from single or double precision to integer and deposited into HL)
    2CAD
    LD A,(HL)7E
    Load Register A with the value at the location of the memory pointer in HL
    2CAE-2CB0
    JP 27F8HJP SNGFLTC3 F8 27
    Go save the 8-bit value in Register A as the current result in ACCumulator

    2CB1-2CBC - LEVEL II BASIC POKEROUTINE- "POKE"

    2CB1-2CB3-  ↳ POKE
    CALL 2B02HCALL GETIN2CD 02 2B
    Go evaluate the expression at the location of the current BASIC program pointer in HL and return with the integer result in DE
    2CB4
    PUSH DED5
    Save the address the user wants to POKE to (held in DE) to the STACK
    2CB5-2CB6
    RST 08H ⇒ 2ESYNCHK ","CF 2E
    Since the character at the location of the current BASIC program pointer in HL must be a ,, call the COMPARE SYMBOL routine at RST 08H.

    NOTE:The RST 08H routine 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 the next execution address (i.e, the RST 08H instruction + 2) with the next symbol in the A Register 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).
    2CB7-2CB9
    CALL 2B1CHCALL GETBYTCD 1C 2B
    GOSUB to 2B1CH to evaluate the expression at the location of the current BASIC program pointer in HL and return with the 8-bit value in Register A
    2CBA
    POP DED1
    Get the address the user wants to POKE to from the STACK and put it in DE
    2CBB
    LD (DE),A12
    Save the value the user wanted to poke (held in Register A) in the location that the user wants to POKE to (held in DE)
    2CBC
    RETC9
    RETurn to CALLer

    2CBD-2E52 - LEVEL II BASIC USINGROUTINE- "PRINUS"

    The original ROM source code says that we wind up here after the "USING" clause in a PRINT statement is recognized. The idea is to scan the using string until the value list is exhausted, finding string and numeric fields to print values out of the list in, and just outputing any characters that aren't part of a print field

    Vernon Hester has reported an error in the PRINT USING routine. A PRINT USING statement with a negative sign at the end of the field prints a negative sign after negative numbers and prints a space for positive numbers. However, if the field specifiers in the string also has two asterisks at the beginning of the field, the ROM prints an asterisk instead of a space after a positive number.

       Example: PRINT USING "**####-";1234 will display **1234* instead of **1234SPACE

    2CBD-2CBF-  ↳ PRINUS
    CALL 2338HCALL FRMCHKCD 38 23
    Go evaluate the string expression at the location of the current BASIC program pointer in HL
    2CC0-2CC2
    CALL 0AF4HCALL CHKSTRCD F4 0A
    Go make sure the expression that was just evaluated was a string
    2CC3-2CC4
    RST 08H ⇒ 3BSYNCHK ";"CF 3B
    Since the character at the location of the current BASIC program pointer in HL must be a ";", call the COMPARE SYMBOL routine at RST 08H.

    NOTE:The RST 08H routine 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 the next execution address (i.e, the RST 08H instruction + 2) with the next symbol in the A Register 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).
    2CC5
    EX DE,HLEB
    Swap DE and HL so that DE now holds the current BASIC program pointer
    2CC6-2CC8
    LD HL,(4121H)LD HL,(FACLO)2A 21 41
    Load HL with the USING string's VARPTR
    2CC9-2CCA
    JR 2CD3HJR INIUS18 08
    Jump down to 2CD3H to continue
    2CCB-2CCD-  ↳ REUSST
    LD A,(40DEH)LD A,(FLGINP)3A DE 40
    Load Register A with the value of the READ/INPUT flag, which is being used here to track if we printed out a value on the prior scan.
    2CCE
    OR AB7
    Check to see if that flag indivates that we did, or did not, print out a value last time.
    2CCF-2CD0
    JR Z,2CDDHJR Z,FCERR328 0C
    If we did not print out a value last time, we have an error, so JUMP down to 2CDDH
    2CD1
    POP DED1
    Restore the pointer to the "USING" string decription from the STACK into DE
    2CD2
    EX DE,HLEB
    Swap DE and HL so that HL will hold the pointer to the "USING" string descriptor and DE will hold the pointer to the position on the BASIC line being evaluated.
    2CD3-  ↳ INIUS
    PUSH HLE5
    Save the pointer to the "USING" string descriptor (i.e., the USING string's VARPTR) in HL to the STACK
    2CD4
    XOR AAF
    Zero Register A and all the flags.
    2CD5-2CD7
    LD (40DEH),ALD (FLGINP),A32 DE 40
    Clear the flag we are using to see if we printed the values or not.
    2CD8
    CP DBA
    Turn the Z FLAG off so as to indicate the value list has not ended yet. This is accomplished by checking to see if the value in D is equal to zero by checking it against A which was XOR'd to 0 above. If they match, the Z FLAG is set, and otherwise the NZ FLAG is set. If A < the checked value, then the C FLAG is set. If A >= the checked value, the NC FLAG is set.
    2CD9
    PUSH AFF5
    Save the flag indicating if the value list has ended or not to the STACK
    2CDA
    PUSH DED5
    Save the pointer into the value list to the STACK
    2CDB
    LD B,(HL)46
    Load Register B with the USING string's length
    2CDC
    OR BB0
    Check to see if the USING string's length in Register B is equal to zero
    2CDD-2CDF-  ↳ FCERR3
    JP Z,1E4AHJP Z,FCERRCA 4A 1E
    If the USING string is NULL then display a ?FC ERROR
    2CE0
    INC HL23
    Bump the pointer to the USING string's data in HL by 1
    2CE1
    LD C,(HL)4E
    Load Register C with the LSB of the USING string's address at the location of the USING string's VARPTR in HL
    2CE2
    INC HL23
    Bump the value of the USING string's VARPTR in HL
    2CE3
    LD H,(HL)66
    Load Register H with the MSB of the USING string's address at the location of the USING string's VARPTR in HL
    2CE4
    LD L,C69
    Load Register L with the LSB of the USING string's address in Register C
    2CE5-2CE6
    JR 2D03HJR PRCCHR18 1C
    Jump down to 2D03H to loop to scan the USING string

    2CE7-  ↳ BGSTRF
    LD E,B58
    Load Register E with the USING string's length
    2CE8
    PUSH HLE5
    Save the pointer to the USING string pointer in HL to the top of the STACK
    2CE9-2CEA
    LD C,02H0E 02
    Since the \\ string field length is two plus number of enclosed spaces, add two
    2CEB-  ↳ LPSTRF
    LD A,(HL)7E
    Load Register A with the character at the location of the USING string pointer in HL
    2CEC
    INC HL23
    Bump the value of the USING string data pointer in HL
    2CED-2CEE
    CP 25HCP CSTRNGFE 25
    Check to see if the character in Register A is a %, which acts as a field terminator. If they match, the Z FLAG is set, and otherwise the NZ FLAG is set. If A < the checked value, then the C FLAG is set. If A >= the checked value, the NC FLAG is set.
    2CEF-2CF1
    JP Z,2E17HJP Z,ISSTRFCA 17 2E
    If it is a "%" then JUMP to 2E17H to evaluate a string and print
    2CF2-2CF3
    CP 20HFE 20
    Check to see if the character in Register A is a " ", which acts as a field extender. If they match, the Z FLAG is set, and otherwise the NZ FLAG is set. If A < the checked value, then the C FLAG is set. If A >= the checked value, the NC FLAG is set.
    2CF4-2CF5
    JR NZ,2CF9HJR NZ,NOSTRF20 03
    If the character is not a field extender, then it isn't a string field, so JUMP down a few opcodes to 2CF9H
    2CF6
    INC C0C
    Increment the field width (tracked in in Register C)
    2CF7-2CF8
    DJNZ 2CEBHDJNZ LPSTRF10 F2
    Decrement the USING string's length in Register B and loop back to keep scanning for the field terminator or more characters

    If we are here, then a string field was not found. The "USING" string character count and the pointer into its data MUST be restored and the "\" printed.

    2CF9-  ↳ NOSTRF
    POP HLE1
    Restore the pointer to the "USING" string's data into Register Pair HL
    2CFA
    LD B,E43
    Load Register B with the USING string's length
    2CFB-2CFC
    LD A,25HLD A,CSTRNG3E 25
    Restore the character into Register Adiv>

    At this point we need to print the character held in Register A since it wasn't part of any field

    2CFD-2CFF-  ↳ NEWUCH
    CALL 2E49HCALL PLSPRTCD 49 2E
    If a +came before the character, make sure to print it
    2D00-2D02
    CALL 032AHCALL OUTDOCD 2A 03
    Once that has been printed, now we print the character in Register A since we know it isn't part of a field
    2D03-  ↳ PRCCHR
    XOR AAF
    We need to set Register Pair DE to 0 so that if we jump away, some of the flags are already ZERO, thus preventing us from printing a second "+". To do this, first zero Register A and clear the flags
    2D04
    LD E,A5F
    Zero Register E
    2D05
    LD D,A57
    Zero Register D
    2D06-2D08-  ↳ PLSFIN
    CALL 2E49HCALL PLSPRTCD 49 2E
    Go print a leading +if necessary (i.e., to allow for multiple plusses)
    2D09
    LD D,A57
    Set the "plus flag" in Register D based on Register A. Note, since this is a loop, A could (and is) set to different values below.
    2D0A
    LD A,(HL)7E
    Load Register A with the next field description character in the USING string
    2D0B
    INC HL23
    Bump the value of the USING string pointer in HL
    2D0C-2D0D
    CP 21HCP "!"FE 21
    Check to see if the character in Register A is a !(which represents a single string character). If they match, the Z FLAG is set, and otherwise the NZ FLAG is set. If A < the checked value, then the C FLAG is set. If A >= the checked value, the NC FLAG is set.
    2D0E-2D10
    JP Z,2E14HJP Z,SMSTRFCA 14 2E
    Jump if the character in Register A is a !
    2D11-2D12
    CP 23HCP "#"FE 23
    Check to see if the character in Register A is a #(which represents the start of a numeric field). If they match, the Z FLAG is set, and otherwise the NZ FLAG is set. If A < the checked value, then the C FLAG is set. If A >= the checked value, the NC FLAG is set.
    2D13-2D14
    JR Z,2D4CHJR Z,NUMNUM28 37
    Jump if the character in Register A is a #
    2D15
    DEC B05
    Since every other possibility is actually a two character field, decrement the value of the string's length in Register B onem ore time
    2D16-2D18
    JP Z,2DFEHJP Z,REUSINCA FE 2D
    If the USING list is exhausted (because we have a Z from that DEC), JUMP to REUSIN to reuse the USING string.

    Now we parse all the 2 character USING fields.

    2D19-2D1A
    CP 2BHCP "+"FE 2B
    Check to see if the character in Register A is a +(i.e., a leading PLUS). If they match, the Z FLAG is set, and otherwise the NZ FLAG is set. If A < the checked value, then the C FLAG is set. If A >= the checked value, the NC FLAG is set.
    2D1B-2D1C
    LD A,08H3E 08
    Set Register A to feed Register D (at the top of the loop) with an 08H to force a leading +in case a numeric field starts
    2D1D-2D1E
    JR Z,2D06HJR Z,PLSFIN28 E7
    Jump if the character in Register A was a +
    2D1F
    DEC HL2B
    Decrement the value of the USING string pointer so we can re-get the character.
    2D20
    LD A,(HL)7E
    Load Register A with the (current) character at the location of the USING string pointer in HL
    2D21
    INC HL23
    Bump the value of the USING string pointer in HL
    2D22-2D23
    CP 2EHCP "."FE 2E
    Check to see if the character in Register A is a .(i.e., a numeric field with trailing digits). If they match, the Z FLAG is set, and otherwise the NZ FLAG is set. If A < the checked value, then the C FLAG is set. If A >= the checked value, the NC FLAG is set.
    2D24-2D25
    JR Z,2D66HJR Z,DOTNUM28 40
    Jump if the character in Register A is a .to scan with Register E holding the number of digits before the "." as 0
    2D26-2D27
    CP 25HCP CSTRNGFE 25
    Check to see if the character in Register A is a %(i.e., a really big string field). If they match, the Z FLAG is set, and otherwise the NZ FLAG is set. If A < the checked value, then the C FLAG is set. If A >= the checked value, the NC FLAG is set.
    2D28-2D29
    JR Z,2CE7HJR Z,BGSTRF28 BD
    Jump to see if it is really a string field if the character in Register A is a %
    2D2A
    CP (HL)BE
    Check to see if the next character matches the current character in the the USING string. If they match, the Z FLAG is set, and otherwise the NZ FLAG is set. If A < the checked value, then the C FLAG is set. If A >= the checked value, the NC FLAG is set.
    2D2B-2D2C
    JR NZ,2CFDHJR NZ,NEWUCH20 D0
    If the NZ flag is set, then we can't have a $$or a **, so all remaining possibilities are exhausted, so JUMP to NEWUCH
    2D2D-2D2E
    CP 24HCP "$"FE 24
    Check to see if the double character is a $. If they match, the Z FLAG is set, and otherwise the NZ FLAG is set. If A < the checked value, then the C FLAG is set. If A >= the checked value, the NC FLAG is set.
    2D2F-2D30
    JR Z,2D45HJR Z,DOLRNM28 14
    Jump to set up the flag bit if the match is $$
    2D31-2D32
    CP 2AHCP "*"FE 2A
    Check to see if the double character is a **. If they match, the Z FLAG is set, and otherwise the NZ FLAG is set. If A < the checked value, then the C FLAG is set. If A >= the checked value, the NC FLAG is set.
    2D33-2D34
    JR NZ,2CFDHJR NZ,NEWUCH20 C8
    If the NZ FLAG is set, then the character is simply not part of a field since all the possibilties have been tested. If so, JUMP
    2D35
    LD A,B78
    Prepare to test to see if the "USING" string is long enough for a **$by first loading Register A with the USING string's length
    2D36-2D37
    CP 02HFE 02
    Check to see if the USING string's length in Register A is at least two. If they match, the Z FLAG is set, and otherwise the NZ FLAG is set. If A < the checked value, then the C FLAG is set. If A >= the checked value, the NC FLAG is set.
    2D38
    INC HL23
    Bump the value of the USING string pointer in HL
    2D39-2D3A
    JR C,2D3EHJR C,NOTSPC38 03
    Jump to 2D3EH if the USING string's length in Register A isn't at least two
    2D3B
    LD A,(HL)7E
    Load Register A with the character at the location of the USING string pointer in HL
    2D3C-2D3D
    CP 24HCP "$"FE 24
    Check to see if the character in Register A is a $. If they match, the Z FLAG is set, and otherwise the NZ FLAG is set. If A < the checked value, then the C FLAG is set. If A >= the checked value, the NC FLAG is set.
    2D3E-2D3F- NOTSPC
    LD A,20H3E 20
    Set the *bit in Register A
    2D40-2D41
    JR NZ,2D49HJR NZ,SPCNUM20 07
    If we did not ultimately get a **$then JUMP (noting we do NOT set the dollar sign flag)
    2D42
    DEC B05
    Decrement the value of the USING string's length to take the $into account
    2D43
    INC E1C
    Bump the field width tracker to account for the floating dollar sign
    2D44-2D45
    CP 0AFHFE AF
    Z-80 Trick to skip over a XOR Aif passing through by processing it as a CP AFH. If they match, the Z FLAG is set, and otherwise the NZ FLAG is set. If A < the checked value, then the C FLAG is set. If A >= the checked value, the NC FLAG is set.
    2D45-  ↳ DOLRNM
    XOR AAF
    This is $processing for PRINT USING. Clear Register A.
    2D46-2D47
    ADD A,10HC6 10
    Mask Register A to set the bit for a floating dollar sign flag.
    2D48
    INC HL23
    Bump the value of the USING string pointer in HL to go past the special characters
    2D49-  ↳ SPCNUM
    INC E1C
    Since two characters specify the field size, start off with E=1
    2D4A
    ADD A,D82
    Combine the bits in Register D into the flag tracker
    2D4B
    LD D,A57
    Preserve the modified flag tracker into Register D.
    2D4C-  ↳ NUMNUM
    INC E1C
    Bump the number of characters to the left of the decimal point in Register E
    2D4D-2D4E
    LD C,00H0E 00
    Set the number of digits to the right of the decimal point (tracked in Register C) to 0
    2D4F
    DEC B05
    Check to see if there are any more characters by decrementing the value of the string's length in Register B
    2D50-2D51
    JR Z,2D99HJR Z,ENDNUS28 47
    If the Z FLAG is set because we ran out of the characters to scan, then JUMP to ENDNUS because we are done scanning this particular numeric field.
    2D52
    LD A,(HL)7E
    Load Register A with the next character at the location of the USING string pointer in HL
    2D53
    INC HL23
    Bump the value of the USING string pointer in HL
    2D54-2D55
    CP 2EHCP "."FE 2E
    Check to see if the character in Register A is a .(i.e., a trailing digit). If they match, the Z FLAG is set, and otherwise the NZ FLAG is set. If A < the checked value, then the C FLAG is set. If A >= the checked value, the NC FLAG is set.
    2D56-2D57
    JR Z,2D70HJR Z,AFTDOT28 18
    If yes, then need to use a special scan loop to scan after the decimal point, so JUMP to AFTDOT
    2D58-2D59
    CP 23HCP "#"FE 23
    Check to see if the character in Register A is a #(i.e., a leading digit). If they match, the Z FLAG is set, and otherwise the NZ FLAG is set. If A < the checked value, then the C FLAG is set. If A >= the checked value, the NC FLAG is set.
    2D5A-2D
    JR Z,2D4CHJR Z,NUMNUM28 F0
    If yes, increment the count and keep scanning via a JUMP to NUMNUM
    2D5C-2D5D
    CP 2CHCP ","FE 2C
    Check to see if the character in Register A is a ,. If they match, the Z FLAG is set, and otherwise the NZ FLAG is set. If A < the checked value, then the C FLAG is set. If A >= the checked value, the NC FLAG is set.
    2D5E-2D5F
    JR NZ,2D7AHJR NZ,FINNUM20 1A
    If there is no comma, then JUMP to FINNUM because there are no more leading digits and we need to check for "^^^"
    2D60
    LD A,D7A
    If we are here, then a comma was requested. Turn on the COMMA bit
    2D61-2D62
    OR 40HF6 40
    Mask the flag in Register A for ,
    2D63
    LD D,A57
    Load Register D with the value of the flag in Register A
    2D64-2D65
    JR 2D4CHJR NUMNUM18 E6
    Jump to 2D4CH to keep scanning

    2D66 - Part of the PRINT USING Routine- "DOTNUM"

    Jumped here when a "." is seen in the USING string. THis means that we are starting a numeric field ONLY if it is followed by a "#"

    2D66-  ↳ DOTNUM
    LD A,(HL)7E
    Load Register A with the next character of the USING string
    2D67-2D68
    CP 23HFE 23
    Check to see if the character in Register A is a #(i.e., a numeric field following a "."). If they match, the Z FLAG is set, and otherwise the NZ FLAG is set. If A < the checked value, then the C FLAG is set. If A >= the checked value, the NC FLAG is set.
    2D69-2D6A
    LD A,2EHLD A,"."3E 2E
    Load Register A with a decimal point
    2D6B-2D6C
    JR NZ,2CFDHJR NZ,NEWUCH20 90
    If it isn't a "." then JUMP AWAY to NEWUCH with A holding a "." so that a "." will get printed
    2D6D-2D6E
    LD C,01H0E 01
    If it was a "." then we have a numeric field to process. First, set C with the number of characters to the right of the decimal point
    2D6F
    INC HL23
    Bump the value of the USING string pointer in HL
    2D70-  ↳ AFTDOT
    INC C0C
    Bump the number of digits to the right of the decimal point (tracked in Register C)
    2D71
    DEC B05
    Decrement the value of the USING STRING's length to test to see if there are more characters
    2D72-2D73
    JR Z,2D99HJR Z,ENDNUS28 25
    If the USING string length is now ZERO, JUMP to ENDNUS to stop scanning
    2D74
    LD A,(HL)7E
    Load Register A with the character at the location of the USING string pointer in HL
    2D75
    INC HL23
    Bump the value of the USING string pointer in HL
    2D76-2D77
    CP 23HFE 23
    Check to see if the character in Register A is a #; meaning that there are more digits after the decimal point. If they match, the Z FLAG is set, and otherwise the NZ FLAG is set. If A < the checked value, then the C FLAG is set. If A >= the checked value, the NC FLAG is set.
    2D78-2D79
    JR Z,2D70HJR Z,AFTDOT28 F6
    If there are more digits, JUMP to AFTDOT to increment the count and keep scanning

    2D7A - Part of the PRINT USING Routine- "FINNUM"

    Now we move on to check the "^^^^" that indicates scientific notation

    2D7A-  ↳ FINNUM
    PUSH DED5
    Save the value of the flag (tracked in D) and the number of leading digits (tracked in E) to the STACK
    2D7B-2D7D
    LD DE,2D97HLD DE,NOTSCI11 97 2D
    Load DE with the return address in case this is not a scientific notation
    2D7E
    PUSH DED5
    Save the value of the return address in DE to the STACK
    2D7F
    2D80
    LD D,H
    LD E,L54
    Let DE = HL in case we need to rememer HL
    2D81-2D82
    CP 5BHFE 5B
    Check to see if the character in Register A is an up arrow. If they match, the Z FLAG is set, and otherwise the NZ FLAG is set. If A < the checked value, then the C FLAG is set. If A >= the checked value, the NC FLAG is set.
    2D83
    RET NZC0
    Return if the character in Register A isn't an up arrow
    2D84
    CP (HL)BE
    Check to see if the character at the location of the USING string pointer in HL is an up arrow. If they match, the Z FLAG is set, and otherwise the NZ FLAG is set. If A < the checked value, then the C FLAG is set. If A >= the checked value, the NC FLAG is set.
    2D85
    RET NZC0
    Return to 2D97 if the character at the location of the USING string pointer in HL isn't an up arrow (meaning we do not have a ^^format)
    2D86
    INC HL23
    Bump the value of the USING string pointer in HL
    2D87
    CP (HL)BE
    Check to see if there is a third up arrow at the location of the USING string pointer in HL. If they match, the Z FLAG is set, and otherwise the NZ FLAG is set. If A < the checked value, then the C FLAG is set. If A >= the checked value, the NC FLAG is set.
    2D88
    RET NZC0
    Return to 2D97 if the character at the location of the USING string pointer in HL isn't an up arrow (meaning we do not have a ^^^format)
    2D89
    INC HL23
    Bump the value of the USING string pointer in HL
    2D8A
    CP (HL)BE
    Check to see if the character at the location of the USING string pointer in HL is a fourth up arrow. If they match, the Z FLAG is set, and otherwise the NZ FLAG is set. If A < the checked value, then the C FLAG is set. If A >= the checked value, the NC FLAG is set.
    2D8B
    RET NZC0
    Return to 2D97 if the character at the location of the USING string pointer in HL isn't an up arrow (meaning we do not have a ^^^^format)
    2D8C
    INC HL23
    Bump the value of the USING string pointer in HL. If we are here we have a #.^^^^format
    2D8D
    LD A,B78
    Now we need to check if there were enough characters for a ^^^^. First load Register A with the value of the USING string's length in Register B
    2D8E-2D8F
    SUB 04HD6 04
    Check to see if there are at least 4 characters left in the USING string
    2D90
    RET CD8
    Return to 2D97 if there aren't at least four characters left in the USING string
    2D91
    POP DED1
    If there are at least 4 characters left, then clean up the STACK by removing the NOTSCI return address
    2D92
    POP DED1
    Get the flag and the count of the characters to the left of the decimal point from the STACK and put it in DE
    2D93
    LD B,A47
    Load Register B with the new USING string's length in Register A
    2D94
    INC D14
    Set the exponential notation flag (tracked in Register D)
    2D95
    INC HL23
    Bump the value of the USING string pointer in HL
    2D96
    JP Z,0D1EBHCA EB D1
    Z-80 Trick! If passing through this won't do anything because the Z FLAG won't be set AND the EX DE,HL won't be executed because it doesn't see that instruction.
    2D97"NOTSCI"
    EX DE,HLEB
    (Ignored if passing through) Restore the old HL into HL
    2D98
    POP DED1
    (Ignored if passing through) Restore the flags into Register D and the number of leading digits into Register E
    2D99-  ↳ ENDNUS
    LD A,D7A
    We need to test to see if the 'leading plus' flag is on, so we load Register A with the value of the edit flag in Register D
    2D9A
    DEC HL2B
    Decrement the value of the USING string pointer in HL
    2D9B
    INC E1C
    Bump the number of characters to the left of the decimal point in Register E to take into account the leading plus
    2D9C-2D9D
    AND 08HE6 08
    Mask Register A to NOT check for a trailing sign
    2D9E-2D9F
    JR NZ,2DB5HJR NZ,ENDNUM20 15
    If that AND leaves us with a NZ, then we are all done with the field, so JUMP to ENDNUM
    2DA0
    DEC E1D
    Otherwise, since we don't have a leading plus, we don't increment the number of digits before the decimal point ... so decrement the number of characters to the left of the decimal point in Register E
    2DA1
    LD A,B78
    Check to see if there are more characters by first loading Register A with the USING string's length from Register B
    2DA2
    OR AB7
    Check to see if this is the end of the USING string
    2DA3-2DA4
    JR Z,2DB5HJR Z,ENDNUM28 10
    If we are out of characters, then we are all done, so JUMP to ENDNUM
    2DA5
    LD A,(HL)7E
    If there ARE more characters, then fill Register A with the character at the location of the USING string pointer in HL
    2DA6-2DA7
    SUB 2DHSUB "-"D6 2D
    Check to see if the character in Register A is a -(i.e., a trailing minus)
    2DA8-2DA9
    JR Z,2DB0HJR Z,SGNTRL28 06
    If it is, then JUMP to SGNTRL to set the trailing sign flag
    2DAA-2DAB
    CP 0FEHCP "+" - "-"FE FE
    Check to see if the character in Register A is a +(i.e., a trailing plus). If they match, the Z FLAG is set, and otherwise the NZ FLAG is set. If A < the checked value, then the C FLAG is set. If A >= the checked value, the NC FLAG is set.
    2DAC-2DAD
    JR NZ,2DB5HJR NZ,ENDNUM20 07
    If its NOT, then we are done scanning, so JUMP to ENDNUM
    2DAE-2DAF
    LD A,08H3E 08
    If we are here then we did have a trailing "+" so first set the flag for a POSITIVE "+"
    2DB0-2DB1-  ↳ SGNTRL
    ADD A,04HC6 04
    Then set the flag for a trailing sign
    2DB2
    ADD A,D82
    Combine the value of the flag in Register D with the value of the flag in Register A
    2DB3
    LD D,A57
    Load Register D with the current flags
    2DB4
    DEC B05
    Decrement the value of the USING string's length in Register B by 1 to account for the trailing sign

    2DB5 - Part of the PRINT USING Routine- "ENDNUM"

    Jump point for when we figure out that we are at the end of a string of digits within a USING string

    2DB5-  ↳ ENDNUM
    POP HLE1
    Get the value of the current BASIC program pointer from the STACK and put it in HL
    2DB6
    POP AFF1
    Load Register A with the flag that tells us whether there are more values to process in the value list.
    2DB7-2DB8
    JR Z,2E09HJR Z,FLDFIN28 50
    If there are no more values in the value list to process, then JUMP to FLDFIN because we are done with the PRINT
    2DB9
    PUSH BCC5
    Save the number of characters remaining to be processed in the USING string (held in B) and the trailing digits (held in C) to the STACK
    2DBA
    PUSH DED5
    Save the flags (held in D) and the number of leading digits (held in E) to the STACK
    2DBB-2DBD
    CALL 2337HCALL FRMEVLCD 37 23
    Read a value from the value list by CALLING the routine to evaluate the expression at the location of the current BASIC program pointer and return with the result in ACCumulator
    2DBE
    POP DED1
    Restore the flags (held in D) and the number of leading digits (held in E) from the STACK
    2DBF
    POP BCC1
    Restore the number of characters remaining to be processed in the USING string (held in B) and the trailing digits (held in C) from the STACK
    2DC0
    PUSH BCC5
    Save the number of characters remaining to be processed in the USING string (held in B) and the trailing digits (held in C) to the STACK
    2DC1
    PUSH HLE5
    Save the value of the current BASIC program pointer in HL to the STACK
    2DC2
    LD B,E43
    Set Register B to hold the number of leading digits (i.e., the number of characters to the left of the decimal point)
    2DC3
    LD A,B78
    We need to test to make sure the total number if digits does not exceed 24, so first load Register A with the number of characters to the left of the decimal point in Register B
    2DC4
    ADD A,C81
    Then add the number of characters to the right of the decimal point in Register C to the number of characters to the left of the decimal point in Register A
    2DC5-2DC6
    CP 19HFE 19
    Check to see if the total number of characters in Register A is greater than 24. If they match, the Z FLAG is set, and otherwise the NZ FLAG is set. If A < the checked value, then the C FLAG is set. If A >= the checked value, the NC FLAG is set.
    2DC7-2DC9
    JP NC,1E4AHJP NC,FCERRD2 4A 1E
    Display a FC ERROR message if the total number of digits is greater than 24
    2DCA
    LD A,D7A
    Load Register A with the flags (held in Register D)
    2DCB-2DCC
    OR 80HF6 80
    Turn on the "USING" bit in the flags
    2DCD-2DCF
    CALL 0FBEHCALL PUFOUTCD BE 0F
    Prepare to print by calling the FLOATING TO ASCII routine at 0FBEH (whcih converts a single or double precision number in ACCumulator to its ASCII equivalent which will be stored at the buffer pointed to by HL using the format codes in the A, B, and C registers
    2DD0-2DD2
    CALL 28A7HCALL STROUTCD A7 28
    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).
    2DD3-  ↳ FNSTRF
    POP HLE1
    Top of a loop. Get the value of the current BASIC program pointer from the STACK and put it in HL
    2DD4
    DEC HL2B
    Decrement the value of the current BASIC program pointer in HL so we can test to see what the terminator was
    2DD5
    RST 10HCHRGETD7
    We need the next character in the BASIC program so 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.
    2DD6
    SCF37
    Set the Carry flag to indicate that a CRLF is desired
    2DD7-2DD8
    JR Z,2DE6HJR Z,CRDNUS28 0D
    If the character at the location of the current BASIC program pointer in Register A is an end of the BASIC statement character, then we need to print a CRLF, so JUMP to CRDNUS
    2DD9-2DDB
    LD (40DEH),ALD (FLGINP),A32 DE 40
    Set the flag that the value HAS been printed!
    2DDC-2DDD
    CP 3BHCP ";"FE 3B
    Check to see if the character at the location of the current BASIC program pointer in Register A is a semicolon. If they match, the Z FLAG is set, and otherwise the NZ FLAG is set. If A < the checked value, then the C FLAG is set. If A >= the checked value, the NC FLAG is set.
    2DDE-2DDF
    JR Z,2DE5HJR Z,SEMUSN28 05
    If so, then we have a legal delimiter, so JUMP to SEMUSN
    2DE0-2DE1
    CP 2CHCP ","FE 2C
    Check to see if the character at the location of the current BASIC program pointer in Register A is a comma. If they match, the Z FLAG is set, and otherwise the NZ FLAG is set. If A < the checked value, then the C FLAG is set. If A >= the checked value, the NC FLAG is set.
    2DE2-2DE4
    JP NZ,1997HJP NZ,SNERRC2 97 19
    If not a comma, then we have no more valid delimiters (it wasnt a ";" or a ",") so go to the Level II BASIC error routine and display an SN ERROR message if the character at the location of the current BASIC program pointer in Register A isn't a comma
    2DE5-  ↳ SEMUSN
    RST 10HCHRGETD7
    We need the next character in the BASIC program so 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.
    2DE6-  ↳ CRDNUS
    POP BCC1
    Restore the number of characters remaining to be procesed in the USING string into Register B
    2DE7
    EX DE,HLEB
    Swap DE and HL so that DE will point to the location of the current BASIC program pointer. We don't care about HL.
    2DE8
    POP HLE1
    Restore the position in the USING string from the STACK and put it in HL
    2DE9
    PUSH HLE5
    Save the position in the USING string (held in HL) to the STACK
    2DEA
    PUSH AFF5
    Save the flag that indicates whether or not the value list has terminated to the STACK
    2DEB
    PUSH DED5
    Save the value of the current BASIC program pointer (held in DE) to the STACK

    The original ROM source code indicates that since FRMEVL may have forced some garbage collection, we cannot rely on the pointer of characters remaining to be scanned. Instead, we have to use the number of characters scanned prior to calling FRMEVL as an offset to the "USING" string's data after FRMEVL.

    2DEC
    LD A,(HL)7E
    Load Register A with the USING string's length at the location of the USING string's VARPTR in HL
    2DED
    SUB B90
    Subtract the number of characers which were already scanned
    2DEE
    INC HL23
    Bump the pointer to the "USING" strings string data
    2DEF
    LD C,(HL)4E
    Load Register C with the LSB of the USING string's address at the location of the USING string's VARPTR in HL
    2DF0
    INC HL23
    Bump the value of the USING string's VARPTR in HL
    2DF1
    LD H,(HL)66
    Load Register H with the MSB of the USING string's address at the location of the USING string's VARPTR in HL
    2DF2
    LD L,C69
    Load Register L with the LSB of the USING string's address in Register C
    2DF3-2DF4
    LD D,00H16 00
    Zero Register D so that Register Pair DE can be a 16 bit offset of whatever is held in A.
    2DF5
    LD E,A5F
    Load Register E with the USING string's offset in Register A
    2DF6
    ADD HL,DE19
    Add the USING string's offset in DE to the USING string's address in HL to get us the new pointer into the USING string's string data into HL
    2DF7
    LD A,B78
    Load Register A with the number of characters left to scan
    2DF8
    OR AB7
    Check to see if this is the end of the USING string
    2DF9-2DFB
    JP NZ,2D03HJP NZ,PRCCHRC2 03 2D
    If there are still more string characters to scan, JUMP to PRCCHR to do so
    2DFC-2DFD
    JR 2E04HJR FINUSI18 06
    Jump to 2E04H if this is the actual end of the entire USING string

    2DFE-2E00 - Part of the PRINT USING Routine- "REUSIN"

    We will wind up here when we are done processing a numeric field

    2DFE-2E00-  ↳ REUSIN
    CALL 2E49HCALL PLSPRTCD 49 2E
    GOSUB to 2E49H print a +if necessary
    2E01-2E03
    CALL 032AHCALL OUTDOCD 2A 03
    Go send the FINAL character (held in Register A) to the current output device
    2E04-  ↳ FINUSI
    POP HLE1
    Get the value of the current BASIC program pointer from the STACK and put it in HL
    2E05
    POP AFF1
    Restore the flag which indicates whether or not the value list has ended into Register A
    2E06-2E08
    JP NZ,2CCBHJP NZ,REUSSTC2 CB 2C
    If the value list has NOT ended, JUMP back to REUSST to reuse the USING string
    2E09-2E0B-  ↳ FLDFIN
    CALL C,20FEHCALL C,CRDODC FE 20
    If we are here, then we didn't have a ,or ;after the PRINT USING, so we GOSUB to 20FEH to send a carriage return to the current output device if necessary
    2E0C
    EX (SP),HLE3
    Swap (SP) with HL so that HL will now point to the "USING" string's descriptor and (SP) will hold the value of the current BASIC program pointer
    2E0D-2E0F
    CALL 29DDHCALL FRETM2CD DD 29
    Free the RAM holding the USING string
    2E10
    POP HLE1
    Get the value of the current BASIC program pointer from the STACK and put it in HL
    2E11-2E13
    JP 2169HJP FINPRTC3 69 21
    Return to 2169H

    2E14-2E15 - Part of the PRINT USING Routine- "SMSTRF"

    We will wind up here when the "!" indicating a single character string field has been scanned

    2E14-2E15-  ↳ SMSTRF
    LD C,01H0E 01
    Set the field width to 1
    2E16-2E17
    LD A,0F1H3E F1
    Z-80 Trick. By putting a 3E in front of the F1 (which is POP AF, to clear the STACK) that POP AFgets skipped if flowing down in the code
    2E17-  ↳ ISSTRF
    POP AFF1
    (Skipped if passing down) Clear the STACK *dumping the HL that was being saved in case it turned out that this wasn't actually a string)
    2E18
    DEC B05
    Decrement the USING string character count (tracked in Register B)
    2E19-2E1B
    CALL 2E49HCALL PLSPRTCD 49 2E
    If there was a "+" before the field, then print it
    2E1C
    POP HLE1
    Get the value of the current BASIC program pointer from the STACK and put it in HL
    2E1D
    POP AFF1
    Get the flag which indicates whether there are more values in the value list into Register A
    2E1E-2E1F
    JR Z,2E09HJR Z,FLDFIN28 E9
    If there are no more values in the value list, then we are done so JUMP back to 2E09H
    2E20
    PUSH BCC5
    Save the number of characters still to be scanned from the USING string (tracked in B) to the STACK
    2E21-2E23
    CALL 2337HCALL FRMEVLCD 37 23
    Read a value by GOSUBing to FRMEVL which will evaluate the expression at the location of the current BASIC program pointer in HL and return with the result in ACCumulator
    2E24-2E26
    CALL 0AF4HCALL CHKSTRCD F4 0A
    Go make sure the current result in ACCumulator is a string
    2E27
    POP BCC1
    Restore the field width (a/k/a the number of characters to be printed) into Register C
    2E28
    PUSH BCC5
    Save the USING string's length and the number of characters to be printed in BC to the STACK
    2E29
    PUSH HLE5
    Save the value of the current BASIC program pointer in HL to the STACK
    2E2A-2E2C
    LD HL,(4121H)LD HL,(FACLO)2A 21 41
    Load HL with the string's VARPTR in ACCumulator
    2E2D
    LD B,C41
    Load Register B with field width (a/k/a the number of characters to be printed)
    2E2E-2E2F
    LD C,00H0E 00
    Zero Register C so that we can use the LEFT$ routine
    2E30
    PUSH BCC5
    Save the length of the string to be printed in Register B to the STACK (as we will need that for space padding)
    2E31-2E33
    CALL 2A68HCALL LEFTUSCD 68 2A
    Truncate the string to B characters via a call to the LEFT$ routine
    2E34-2E36
    CALL 28AAHCALL STRPRTCD AA 28
    Print the string to the current output device
    2E37-2E39
    LD HL,(4121H)LD HL,(FACLO)2A 21 41
    Load HL with the string's VARPTR in ACCumulator so we can see if we need to pad the string
    2E3A
    POP AFF1
    Get the field width (a/k/a the length of the string to be printed) from the STACK and put it in Register A
    2E3B
    SUB (HL)96
    Determine the amount of padding needed into Register A by subtracting the string's length at the location of the string's VARPTR in HL from the length of the string to be printed in Register A
    2E3C
    LD B,A47
    Save the amount of padding needed into Register B
    2E3D-2E3E
    LD A,20H3E 20
    Load Register A with a SPACE
    2E3F
    INC B04
    Bump the number of spaces in Register B because the loop startes with a DEC B

    This loop will print all the spaces needed and then jump to 2DD3H.

    2E40-  ↳ UPRTSP
    DEC B05
    Top of a loop. Decrement the number of spaces in Register B
    2E41-2E43
    JP Z,2DD3HJP Z,FNSTRFCA D3 2D
    If all of the spaces have been printed, Jump back to 2DD3H to see if the value list ended and to resume scanning
    2E44-2E46
    CALL 032AHCALL OUTDOCD 2A 03
    Go send a space to the current output device
    2E47-2E48
    JR 2E40HJR UPRTSP18 F7
    LOOP back to 2E40H to print all the spaces

    2E49 - Part of the PRINT USING Routine- "PLSPRT"

    When a "+" is detected in the "USING" string and a numeric field follows, a bit in Register D should be set, otherwise +should be printed. Since deciding whether a numeric field follows is very difficult, the bit is always set in Register D. At the point it is decided a character is not part of a numeric field, this routine is called to see if the bit in Register D is set, which means a plus preceded the character and should be printed

    2E49-  ↳ PLSPRT
    PUSH AFF5
    Save the current character (held in Register A) to the STACK
    2E4A
    LD A,D7A
    We need to test the PLUS BIT in D, so first load Register A with the value in Register D
    2E4B
    OR AB7
    Check to see if Register A is equal to zero as that would be the ONLY bit which could be turned on at this particular point in the routine.
    2E4C-2E4D
    LD A,2BHLD A,"+"3E 2B
    Prepare to print the +by loading Register A with a +
    2E4E-2E50
    CALL NZ,032AHCALL NZ,OUTDOC4 2A 03
    If the bit was set (i.e., A was non-zero), then send a +to the current output device
    2E51
    POP AFF1
    Get the current character from the STACK and put it in Register A
    2E52
    RETC9
    RETurn to CALLer

    2E53-2FFA - LEVEL II BASIC EDITROUTINE- "ERREDT"

    According to the original ROM source, the EDIT command takes a single line number as its argument. If that line doesn't exist, and error is thrown. If the line does exist, the line number is then typed, and the system waits for the user to enter any of the valid commands.

  • Register C holds the number of characters in the line, Register B holds the current character position (with 0 being the first character) and Register Pair HL points to the current character
    2E53-2E55-  ↳ ERREDT
    LD (409AH),ALD (ERRFLG),A32 9A 40
    Reset the EDIT flag.
    Note: 409AH holds the ERROR/RESUME flag
    2E56-2E58
    LD HL,(40EAH)LD HL,(ERRLIN)2A EA 40
    Load HL with the line number to be edited.
    Note: 40EAH-40EBH holds the line number with error
    2E59
    OR HB4
    OR Register A with the MSB of the error line number in Register H
    2E5A
    AND LA5
    Combine the LSB of the error line number in Register L with the MSB of the line number in Register A. It will be FFH if this was a direct command rather than being part of a program
    2E5B
    INC A3C
    Bump the combined value of the error line number in Register A. If this was a direct call from the command line, this will turn A from FFH into 00H
    2E5C
    EX DE,HLEB
    Swap DE and HL so that DE now holds the line number to edit.
    2E5D
    RET ZC8
    If there was no line number, return if Level II BASIC
    2E5E-2E5F
    JR 2E64HJR EREDIT18 04
    otherwise, continue via a JUMP to 2E64H

    Now that the above code is out of the way (it was the code which would enter EDIT if there was an error in a line number), let us actually process the EDITcommand

    2E60-2E62-  ↳ EDIT
    CALL 1E4FHCALL LINSPCCD 4F 1E
    Get the first line number by calling 1E4F - returns in in DE
    2E63
    RET NZC0
    If the zero flag got set, there was no line number, so return
    2E64-  ↳ EREDIT
    POP HLE1
    Clean up the STACK (i.e., discard the NEWSTT return address)
    2E65-  ↳ EEDITS
    EX DE,HLEB
    Load HL with the line number to be edited in DE
    2E66-2E68
    LD (40ECH),HLLD (DOT),HL22 EC 40
    Save the value of the line number to be edited (in HL) to the memory location that cares about such things.
    Note: 40ECH-40EDH holds EDIT/LIST line number
    2E69
    EX DE,HLEB
    Load HL with the line number to be edited
    2E6A-2E6C
    CALL 1B2CHCALL FNDLINCD 2C 1B
    Find that line number via a GOSUB to the SEARCH FOR LINE NUMBER routine at 1B2CH which looks for the line number specified in DE. Returns C/Z with the line found in BC, NC/Z with line number is too large and HL/BC having the next available location, or NC/NZ with line number not found, and BC has the first available one after that
    2E6D-2E6F
    JP NC,1ED9HJP NC,USERRD2 D9 1E
    If the BASIC line number doesn't exist, display a ?UL ERROR
    2E70
    2E71
    LD H,B
    LD L,C60
    At this point, the line number has been found. Let HL=BC so that HL also points to the location in RAM of the line number being edited
    2E72
    2E73
    INC HL
    INC HL23
    Bump the value of the memory pointer in HL twice to now point to the first byte of the line.
    2E74
    LD C,(HL)4E
    Load Register C with first byte of the line number being edited
    2E75
    INC HL23
    Bump the value of the memory pointer in HL to point to the second byte of the line being edited
    2E76
    LD B,(HL)46
    Load Register B with second byte of the line number being edited
    2E77
    INC HL23
    Bump the value of the memory pointer in HL to now point to the first byte of the actual line
    2E78
    PUSH BCC5
    Save the line number to the STACK
    2E79-2E7B
    CALL 2B7EHCALL BUFLINCD 7E 2B
    GOSUB to 2B7EH move the BASIC line at the location of the memory pointer in HL into a memory buffer and untokenize the BASIC line
    2E7C-  ↳ LLED
    POP HLE1
    Get the value of the line number from the STACK and put it in HL
    2E7D-  ↳ INLED
    PUSH HLE5
    Save the value of the line number in HL to the STACK
    2E7E-2E80
    CALL 0FAFHCALL LINPRTCD AF 0F
    Convert the line number to ASCII and print it out by calling the HL TO ASCII routine at 0FAFH (which converts the value in the HL (assumed to be an integer) to ASCII and display it at the current cursor position on the video screen)
    2E81-2E82
    LD A,20H3E 20
    Load Register A with a space
    2E83-2E85
    CALL 032AHCALL OUTDOCD 2A 03
    Go display the space in Register A
    2E86-2E88
    LD HL,(40A7H)LD HL,(BUFPNT)2A A7 40
    Load HL with the starting address of the expanded version of the current line from the input buffer.
    Note: 40A7H-40A8H holds the input Buffer pointer
    2E89-2E8A
    LD A,0EH3E 0E
    Load Register A with the "turn on the cursor" character
    2E8B-2E8D
    CALL 032AHCALL OUTDOCD 2A 03
    Go turn on the cursor
    2E8E
    PUSH HLE5
    Save the value of the input buffer pointer (in HL) to the STACK
    2E8F-2E90
    LD C,FFH0E FF
    Load Register C with the number of characters examined so far with FFH because the next line is going to INC it by 1 to make it 0
    2E91-  ↳ LENLP
    INC C0C
    Bump the number of characters examined so far in Register C
    2E92
    LD A,(HL)7E
    Load Register A with the character at the location of the input buffer pointer in HL
    2E93
    OR AB7
    Check to see if the character in Register A is an end of the BASIC line character
    2E94
    INC HL23
    Bump the value of the input buffer pointer in HL
    2E95-2E96
    JR NZ,2E91HJR NZ,LENLP20 FA
    Loop back to 2E91H until the end of the BASIC line has been found
    2E97
    POP HLE1
    At this point, C will be the maximum number of characters in the line at issue. Put the start of the expanded buffer into HL
    2E98
    LD B,A47
    Set the current position in the BASIC line being edited (tracked by Register B) to ZERO
    2E99-2E9A-  ↳ DISPED
    LD D,00H16 00
    Assume the repetition count for the upcoming command (tracked by Register D) is zero
    2E9B-2E9D-  ↳ DISPI
    CALL 0384HCALL INCHRCD 84 03
    Go scan the keyboard to wait for the user command
    2D9E-2E9F-  ↳ DISP
    SUB 30H20 15
    We need to test to see if the character was alphabetic or alphanumeric so we subtract 30H from it
    2EA0-2EA1
    JR C,2EB0HJR C,NOTDGI38 0E
    Jump down to 2EB0H if the character in Register A is alphabetic
    2EA2-2EA3
    CP 0AHFE 0A
    Check to see if the character is Register A is numeric. If they match, the Z FLAG is set, and otherwise the NZ FLAG is set. If A < the checked value, then the C FLAG is set. If A >= the checked value, the NC FLAG is set.
    2EA4-2EA5
    JR NC,2EB0HJR NC,NOTDGI30 0A
    Jump to 2EB0H if the character in Register A isn't numeric
    2EA6
    LD E,A5F
    Load Register E with the binary value of the character in Register A
    2EA7
    LD A,D7A
    Put the repetition value into Register A
    2EA8
    RLCA07
    Multiply the value in Register A by two (so now A has multiplied by 2)
    2EA9
    RLCA07
    Multiply the value in Register A by two (so now A has multiplied by 4)
    2EAA
    ADD A,D82
    Add the value in Register D to the value in Register A (so now A has multiplied by 5)
    2EAB
    RLCA07
    Multiply the value in Register A by two (so now A has multiplied by 10). Now the "ones place" is empty.
    2EAC
    ADD A,E83
    Add the value in Register E to the value in Register A in the "ones place"
    2EAD
    LD D,A57
    Load Register D with the value in Register A
    2EAE-2EAF
    JR 2E9BHJR DISPI18 EB
    Loop until a nonnumeric character is pressed

  • 2EB0H - LEVEL II BASIC EDITROUTINE- "NOTDGI"

    While getting user command input within an edit, we wind up here if the user enters a non-numeric character (i.e., the actual command, and not just the repetition number which precedes it)

    2EB0-  ↳ NOTDGI
    PUSH HLE5
    Save the value of the input buffer pointer in HL to the STACK
    2EB1-2EB3
    LD HL,2E99HLD HL,DISPED21 99 2E
    Load HL with the return address of 2E99H
    2EB4
    EX (SP),HLE3
    Exchange the value of the return address in HL with the value of the input buffer pointer to the STACK
    2EB5
    DEC D15
    We need to test if the command was preceded by a number so we need to set the flags by first decrementing the numeric value in Register D
    2EB6
    INC D14
    ... and then incrementing the numeric value in Register D to set the flags
    2EB7-2EB9
    JP NZ,2EBBHJP NZ,NTZERDC2 BB 2E
    If we had a received a repetition count already, then JUMP to 2EBBH
    2EBA
    INC D14
    Otherwise, set the repetition count (held in Register D) to be one
    2EBB-2EBC-  ↳ NTZERD
    CP 0D8HFE D8
    Check to see if the character in Register A is a BACKSPACEcharacter. If they match, the Z FLAG is set, and otherwise the NZ FLAG is set. If A < the checked value, then the C FLAG is set. If A >= the checked value, the NC FLAG is set.
    2EBD-2EBF
    JP Z,2FD2HJP Z,DELEDCA D2 2F
    If the character in Register A is a BACKSPACEcharacter, JUMP to DELED
    2EC0-2EC1
    CP 0DDHFE DD
    Check to see if the character in Register A is a CARRIAGE RETURN. If they match, the Z FLAG is set, and otherwise the NZ FLAG is set. If A < the checked value, then the C FLAG is set. If A >= the checked value, the NC FLAG is set.
    2EC2-2EC4
    JP Z,2FE0HJP Z,CREDCA E0 2F
    If the character in Register A is a CARRIAGE RETURN, JUMP to CRED
    2EC5-2EC6
    CP 0F0HFE F0
    Check to see if the character in Register A is a SPACE. If they match, the Z FLAG is set, and otherwise the NZ FLAG is set. If A < the checked value, then the C FLAG is set. If A >= the checked value, the NC FLAG is set.
    2EC7-2EC8
    JR Z,2F0AHJR Z,SPED28 41
    If the character in Register A is a SPACE, JUMP to SPED

    That's it for non-alphabetic instructions, so we need to need to convert a lower case command to upper case

    2EC9-2ECA
    CP 31HFE 31
    Check to see if the character in Register A is lowercase. If they match, the Z FLAG is set, and otherwise the NZ FLAG is set. If A < the checked value, then the C FLAG is set. If A >= the checked value, the NC FLAG is set.
    2ECB-2ECC
    JR C,2ECFHJR C,NOTLW438 02
    Jump if the character in Register A isn't lowercase
    2ECD-2ECE
    SUB 20HD6 20
    Convert the lowercase character in Register A to uppercase
    2ECF-2ED0-  ↳ NOTLW4
    CP 21HFE 21
    Check to see if the character in Register A is a Q(i.e., QUIT the edit). If they match, the Z FLAG is set, and otherwise the NZ FLAG is set. If A < the checked value, then the C FLAG is set. If A >= the checked value, the NC FLAG is set.
    2ED1-2ED3
    JP Z,2FF6HJP Z,QEDCA F6 2F
    Jump if the character in Register A is a Q(i.e., QUIT)
    2ED4-2ED5
    CP 1CHFE 1C
    Check to see if the character in Register A is an L. If they match, the Z FLAG is set, and otherwise the NZ FLAG is set. If A < the checked value, then the C FLAG is set. If A >= the checked value, the NC FLAG is set.
    2ED6-2ED7
    JP Z,2F40HJP Z,LEDCA 40 2F
    Jump if the character in Register A is an L(i.e., BRANCH)
    2ED9-2EDA
    CP 23HFE 23
    Check to see if the character in Register A is an S. If they match, the Z FLAG is set, and otherwise the NZ FLAG is set. If A < the checked value, then the C FLAG is set. If A >= the checked value, the NC FLAG is set.
    2EDB-2EDC
    JR Z,2F1CHJR Z,SED28 3F
    Jump if the character in Register A is an S(i.e., SEARCH)
    2EDD-2EDE
    CP 19HFE 19
    Check to see if the character in Register A is an I. If they match, the Z FLAG is set, and otherwise the NZ FLAG is set. If A < the checked value, then the C FLAG is set. If A >= the checked value, the NC FLAG is set.
    2EDF-2EE1
    JP Z,2F7DHJP Z,IEDCA 7D 2F
    Jump if the character in Register A is an I(i.e., INSERT)
    2EE2-2EE3
    CP 14HFE 14
    Check to see if the character in Register A is a D. If they match, the Z FLAG is set, and otherwise the NZ FLAG is set. If A < the checked value, then the C FLAG is set. If A >= the checked value, the NC FLAG is set.
    2EE4-2EE6
    JP Z,2F4AHJP Z,DEDCA 4A 2F
    Jump if the character in Register A is a D(i.e., DELETE)
    2EE7-2EE8
    CP 13HFE 13
    Check to see if the character in Register A is a C. If they match, the Z FLAG is set, and otherwise the NZ FLAG is set. If A < the checked value, then the C FLAG is set. If A >= the checked value, the NC FLAG is set.
    2EE9-2EEB
    JP Z,2F65HJP Z,CEDCA 65 2F
    Jump if the character in Register A is a C(i.e., CHANGE)
    2EEC-2EED
    CP 15HFE 15
    Check to see if the character in Register A is an E. If they match, the Z FLAG is set, and otherwise the NZ FLAG is set. If A < the checked value, then the C FLAG is set. If A >= the checked value, the NC FLAG is set.
    2EEE-2EF0
    JP Z,2FE3HJP Z,EEDCA E3 2F
    Jump if the character in Register A is an E(i.e., END)
    2EF1-2EF2
    CP 28HFE 28
    Check to see if the character in Register A is an X. If they match, the Z FLAG is set, and otherwise the NZ FLAG is set. If A < the checked value, then the C FLAG is set. If A >= the checked value, the NC FLAG is set.
    2EF3-2EF5
    JP Z,2F78HJP Z,XEDCA 78 2F
    Jump if the character in Register A is an X(i.e., EXTEND)
    2EF6-2EF7
    CP 1BHFE 1B
    Check to see if the character in Register A is a K. If they match, the Z FLAG is set, and otherwise the NZ FLAG is set. If A < the checked value, then the C FLAG is set. If A >= the checked value, the NC FLAG is set.
    2EF8-2EF9
    JR Z,2F16HJR Z,KED28 1C
    Jump if the character in Register A is a K(i.e., KILL)
    2EFA-2EFB
    CP 18HFE 18
    Check to see if the character in Register A is an H. If they match, the Z FLAG is set, and otherwise the NZ FLAG is set. If A < the checked value, then the C FLAG is set. If A >= the checked value, the NC FLAG is set.
    2EFC-2EFE
    JP Z,2F75HJP Z,HEDCA 75 2F
    Jump if the character in Register A is an H(i.e., HACK off the rest of the line and then enter INSERT mode)
    2EFF-2F00
    CP 11HFE 11
    Check to see if the character in Register A is an A(i.e., AGAIN). If they match, the Z FLAG is set, and otherwise the NZ FLAG is set. If A < the checked value, then the C FLAG is set. If A >= the checked value, the NC FLAG is set.
    2F01
    RET NZC0
    Return if the character in Register A isn't an A

    2F02 - EDITCommand - Cancel and Restore Logic.

    2F02
    POP BCC1
    Clean up the STACK (i.e., remove the DISPI return address)
    2F03
    POP DED1
    Get the BASIC line number from the STACK and put it in DE
    2F04-2F06
    CALL 20FEHCALL CRDOCD FE 20
    Go print a carriage return on the video display if necessary
    2F07-2F09
    JP 2E65HJP EEDITSC3 65 2E
    Jump back to 2E65H to re-enter the EDITroutine

    2F0A - This routine prints a string of text to the display, printer or tape- "SPED"

    This routine it uses 032AH to do this. HL must point to the first character of the string. (409CH must be set before calling this routine, see 32AH). String must be delimited with a zero byte.
    Note: 409CH holds the current output device flag: -1=cassette, 0=video and 1=printer

    2F0A-  ↳ SPED
    LD A,(HL)7E
    Load Register A with the character at the location of the input buffer pointer in HL
    2F0B
    OR AB7
    Check to see if the character in Register A is an end of the BASIC line character
    2F0C
    RET ZC8
    Return if the character in Register A is an end of the BASIC line character
    2F0D
    INC B04
    Bump the character position in Register B
    2F0E-2F10
    CALL 032AHCALL OUTDOCD 2A 03
    Go display the character in Register A
    2F11
    INC HL23
    Bump the value of the pointer in HL
    2F12
    DEC D15
    Decrement the number of times to perform the operation in Register D
    2F13-2F14
    JR NZ,2F0AHJR NZ,SPED20 F5
    Loop until done
    2F15
    RETC9
    RETurn to CALLer

    2F16 - EDITCommand - KILL Logic- "KED".

    2F16-  ↳ KED
    PUSH HLE5
    Save the current character position in the buffer in HL to the STACK
    2F17-2F19
    LD HL,2F5FHLD HL,TYPSLH21 5F 2F
    Load HL with the return address of 2F5FH (which will print the final !
    2F1A
    EX (SP),HLE3
    Exchange the value of the return address in HL with the value of the input buffer pointer to the STACK
    2F1B
    SCF37
    Set the KILL/SEARCH flag for KILL since CARRY flag signals KILL
    2F1C-  ↳ SED
    PUSH AFF5
    Save the KILL/SEARCH flag to the STACK
    2F1D-2F1F
    CALL 0384HCALL INCHRCD 84 03
    Go scan the keyboard for the character the user wants to SEARCH for
    2F20
    LD E,A5F
    Save the character the user wants to SEARCH for into Register E
    2F21
    POP AFF1
    Get the KILL/SEARCH flag from the STACK
    2F22
    PUSH AFF5
    Save the KILL/SEARCH flag to the STACK
    2F23-2F25
    CALL C,2F5FHCALL C,TYPSLHDC 5F 2F
    If KILL (because the CARRY flag was set) then GOSUB to 2F5FH to print a !
    2F26-  ↳ SRCALP
    LD A,(HL)7E
    Load Register A with the character at the location of the input buffer pointer in HL
    2F27
    OR AB7
    Check to see if the character in Register A is an end of the BASIC line character
    2F28-2F2A
    JP Z,2F3EHJP Z,POPARTCA 3E 2F
    Jump down to 2F3EH if the character in Register A is an end of the BASIC line character
    2F2B-2F2D
    CALL 032AHCALL OUTDOCD 2A 03
    Go display the character in Register A
    2F2E
    POP AFF1
    Get the KILL/SEARCH flag from the STACK
    2F2F
    PUSH AFF5
    Save the KILL/SEARCH flag to the STACK
    2F30-2F32
    CALL C,2FA1HCALL C,DELCHRDC A1 2F
    If the CARRY flag is set, that means we are in KILL mode so GOSUB to 2FA1H to delete the character from the input buffer
    2F33-2F34
    JR C,2F37HJR C,NOTSRC38 02
    Jump to 2F37H if KILL. Note, we do not move the HL pointer in this case because DELCHR already moved it.
    2F35
    INC HL23
    If we are here, it must be SEARCH! So bump to the next character
    2F36
    INC B04
    Bump the value of the character position in Register B
    2F37-  ↳ NOTSRC
    LD A,(HL)7E
    Regardless of whether we are SEARCH or KILL, load Register A with the character at the location of the current input buffer pointer in HL
    2F38
    CP EBB
    Check to see if the character in Register A is the same as the character to be located (i.e., the one specified by the user) in Register E. If they match, the Z FLAG is set, and otherwise the NZ FLAG is set. If A < the checked value, then the C FLAG is set. If A >= the checked value, the NC FLAG is set.
    2F39-2F3A
    JR NZ,2F26HJR NZ,SRCALP20 EB
    Loop back to 2F26H until the character to be located is found
    2F3B
    DEC D15
    Decrement the number of times to perform the operation in Register D (as initially specified by the user by entering a number before the command)
    2F3C-2F3D
    JR NZ,2F26HJR NZ,SRCALP20 E8
    Loop until done
    2F3E-  ↳ POPART
    POP AFF1
    Get the KILL/SEARCH flag from the STACK
    2F3F
    RETC9
    RETurn to CALLer

    2F40 - EDITCommand - LIST Logic- "LED".

    2F40-2F42-  ↳ LED
    CALL 2B75HCALL LISPRTCD 75 2B
    Since we need to display the line being edited we call the PRINT MESSAGE routine at 2B75H which writes string pointed to by HL to the current output device
    2F43-2F45
    CALL 20FEHCALL CRDOCD FE 20
    Go display a carriage return if necessary
    2F46
    POP BCC1
    Clear off the RETURN address to DISPED
    2F47-2F49
    JP 2E7CHJP LLEDC3 7C 2E
    Jump to 2E7CH (to display the current line number and await the next EDIT command)

    2F4A - EDITCommand - DELETE Logic- "DED"

    2F4A-  ↳ DED
    LD A,(HL)7E
    Load Register A with the character at the location of the input buffer pointer in HL
    2F4B
    OR AB7
    Check to see if the character in Register A is an end of the BASIC line character
    2F5C
    RET Z15
    Return if the character in Register A is an end of the BASIC line character
    2F4D-2F4E
    LD A,21HLD A,"!"3E 21
    Load Register A with an !
    2F4F-2F51
    CALL 032AHCALL OUTDOCD 2A 03
    Go display the !in Register A
    2F52-  ↳ DELLP
    LD A,(HL)7E
    Load Register A with the character at the location of the input buffer pointer in HL
    2F53
    OR AB7
    Check to see if the character in Register A is an end of the BASIC line character
    2F54-2F5B
    JR Z,2F5FHJR Z,TYPSLH28 09
    Jump to 2F5FH if the character in Register A is an end of the BASIC line character
    2F56-2F58
    CALL 032AHCALL OUTDOCD 2A 03
    Go display the character in Register A
    2F59-2F5B
    CALL 2FA1HCALL DELCHRCD A1 2F
    Go delete the character from the input buffer
    2F5C
    DEC D15
    Decrement the number of times to perform the operation in Register D (as initially specified by the user by entering a number before the command)
    2F5D-2F5E
    JR NZ,2F52HJR NZ,DELLP20 F3
    Loop until done
    2F5F-2F60-  ↳ TYPSLH
    LD A,21HLD A,"!"3E 21
    Load Register A with an !
    2F61-2F63
    CALL 032AHCALL OUTDOCD 2A 03
    Go display the ! in Register A
    2F64
    RETC9
    RETurn to CALLer

    2F65 - EDITCommand - CHANGE Logic- "CED".

    2F65-  ↳ CED
    LD A,(HL)7E
    Load Register A with the character at the location of the input buffer pointer in HL
    2F66
    OR AB7
    Check to see if the character in Register A is an end of the BASIC line character
    2F67
    RET ZC8
    Return if the character in Register A is an end of the BASIC line character
    2F68-2F6A
    CALL 0384HCALL INCHRCD 84 03
    Go get the character to put in the input buffer from the keyboard
    2F6B
    LD (HL),A77
    Save the character in Register A at the memory location of the input buffer pointer in HL
    2F6C-2F6E
    CALL 032AHCALL OUTDOCD 2A 03
    Go display the character in Register A
    2F6F
    INC HL23
    Bump the value of the input buffer pointer in HL
    2F70
    INC B04
    Bump the character position in Register B
    2F71
    DEC D15
    Decrement the number of times to perform the operation in Register D (as initially specified by the user by entering a number before the command)
    2F72-2F73
    JR NZ,2F65HJR NZ,CED20 F1
    Loop until done
    2F74
    RETC9
    RETurn to CALLer

    2F75 - EDITCommand - HACK/INSERT Logic- "HED"
    2F75-2F76-  ↳ HED
    LD (HL),00H36 00
    Set the line end to be the current position.
    2F77
    LD C,B48
    Load Register C with the character position in Register B which will now be the line length
    2F78-2F79-  ↳ XED
    LD D,0FFH16 FF
    Prepare for the next CALL to find the end of the line by loading Register D with the number of times to perform the operation
    2F7A-2F7C
    CALL 2F0AHCALL SPEDCD 0A 2F
    GOSUB to 2F0AH to display the Register BASIC line if necessary
    2F7D-2F7F-  ↳ IED
    CALL 0384HCALL INCHRCD 84 03
    Go get the character to be inserted from the keyboard
    2F80
    OR AB7
    Check to see if a key was pressed
    2F81-2F83
    JP Z,2F7DHJP Z,IEDCA 7D 2F
    Loop back to 2F7DH until a key is pressed
    2F84-2F85
    CP 08HFE 08
    Check to see if the character in Register A is a backspace character. If they match, the Z FLAG is set, and otherwise the NZ FLAG is set. If A < the checked value, then the C FLAG is set. If A >= the checked value, the NC FLAG is set.
    2F86-2F87
    JR Z,2F92HJR Z,TYPARW28 0A
    Jump to 2F92H if the character in Register A is a backspace character
    2F88-2F89
    CP 0DHFE 0D
    Check to see if the character in Register A is a carriage return. If they match, the Z FLAG is set, and otherwise the NZ FLAG is set. If A < the checked value, then the C FLAG is set. If A >= the checked value, the NC FLAG is set.
    2F8A-2F8C
    JP Z,2FE0HJP Z,CREDCA E0 2F
    Jump to 2FE0H if the character in Register A is a carriage return
    2F8D-2F8E
    CP 1BHFE 1B
    Check to see if the character in Register A is a shift up arrow(also known as an ESCape). If they match, the Z FLAG is set, and otherwise the NZ FLAG is set. If A < the checked value, then the C FLAG is set. If A >= the checked value, the NC FLAG is set.
    2F8F
    RET ZC8
    Return if the character in Register A is shift up arrow
    2F90-2F91
    JR NZ,2FB0HJR NZ,NTARRW20 1E
    Jump to 2FB0H to add the new character to the current line

    2F92 - EDITCommand - BACKSPACE CURSOR Logic- "TYPARW".

    2F92-2F93-  ↳ TYPARW
    LD A,08H3E 08
    Load Register A with a backspace the cursor character
    2F94-  ↳ TYPAR1
    DEC B05
    Decrement the character position in Register B
    2F95
    INC B04
    Bump the character position in Register B
    2F96-2F97
    JR Z,2FB7HJR Z,DINGI28 1F
    If this is the first character of the BASIC line Jump forward to 2FB7H
    2F98-2F9A
    CALL 032AHCALL OUTDOCD 2A 03
    Go backspace the cursor on the video display
    2F9B
    DEC HL2B
    Decrement the value of the input buffer pointer in HL
    2F9C
    DEC B05
    Decrement the character position in Register B
    2F9D-2F9F
    LD DE,2F7DHLD DE,IED11 7D 2F
    Load DE with a return address of 2F7DH
    2FA0
    PUSH DED5
    Save the value of the return address in DE to the STACK

    2FA1 - LEVEL II BASIC EDITROUTINE- "DELCHR"

    This subroutine will delete the character pointed to by Register Pair HL and will correct Register C

    2FA1-  ↳ DELCHR
    PUSH HLE5
    Save the value of the input buffer pointer in HL to the STACK
    2FA2
    DEC C0D
    Decrement the character position in Register C
    2FA3-  ↳ CMPRSS
    LD A,(HL)7E
    Load Register A with the character at the location of the input buffer pointer in HL
    2FA4
    OR AB7
    Check to see if the character in Register A is an end of the BASIC line character
    2FA5
    SCF37
    Set the Carry flag to signal that DELCHR was called
    2FA6-2FA8
    JP Z,0890HJP Z,POPHRTCA 90 08
    If the character in Register A is an end of the BASIC line character then we are done compressing so Jump to 0890H
    2FA9
    INC HL23
    Bump the value of the input buffer pointer in HL
    2FAA
    LD A,(HL)7E
    Load Register A with the character at the location of the input buffer pointer in HL
    2FAB
    DEC HL2B
    Decrement the value of the input buffer pointer in HL
    2FAC
    LD (HL),A77
    Save the character in Register A at the location of the current input buffer pointer in HL
    2FAD
    INC HL23
    Bump the value of the input buffer pointer in HL
    2FAE-2FAF
    JR 2FA3HJR CMPRSS18 F3
    Loop back to 2FA3H to keep crunching the line

    2FB0 - EDITCommand - ADD A CHARACTER Logic- "NTARRW".

    2FB0-  ↳ NTARRW
    PUSH AFF5
    Save the character to be inserted in Register A to the STACK
    2FB1
    LD A,C79
    Load Register A with the number of characters in the input buffer (i.e., the length of the line) in Register C
    2FB2-2FB3
    CP FFHCP BUFLENFE FF
    We need to make sure we aren't trying to make the line too long, so check for the maximum BASIC line length. If they match, the Z FLAG is set, and otherwise the NZ FLAG is set. If A < the checked value, then the C FLAG is set. If A >= the checked value, the NC FLAG is set.
    2FB4-2FB5
    JR C,2FB9HJR C,OKINS38 03
    Jump forward to 2FB9H if the maximum BASIC line length hasn't been reached
    2FB6
    POP AFF1
    Get the character to be inserted from the STACK and put it in Register A
    2FB7-2FB8-  ↳ DINGI
    JR 2F7DHJR IED18 C4
    The character needs to be ESCape, so jump back to 2F7DH
    2FB9-  ↳ OKINS
    SUB B90
    Subtract the character position in Register B from the number of characters in the input buffer in Register A. This should give the current byte position
    2FBA
    INC C0C
    Bump the number of characters in the input buffer in Register C
    2FBB
    INC B04
    Bump the character position in Register B
    2FBC
    PUSH BCC5
    Save the character position and the number of characters in the input buffer in BC to the STACK
    2FBD
    EX DE,HLEB
    Load DE with the input buffer pointer in HL
    2FBE
    LD L,A6F
    Load Register L with the number of bytes to move
    2FBF-2FC0
    LD H,00H26 00
    Zero Register H so that the number of bytes to move can be done in a 16 bit Register Pair.
    2FC1
    ADD HL,DE19
    Add the value of the input buffer pointer in DE to the character count in HL
    2FC2
    LD B,H44
    Load Register B with the MSB of the end of the BASIC line pointer in Register H
    2FC3
    LD C,L4D
    Load Register C with the LSB of the end of the BASIC line pointer in Register L
    2FC4
    INC HL23
    Bump the value of the end of the BASIC line pointer in HL
    2FC5-2FC7
    CALL 1958HCALL BLTUCCD 58 19
    Go move the line up out character
    2FC8
    POP BCC1
    Get the character position and the number of characters in the input buffer from the STACK and put it in BC
    2FC9
    POP AFF1
    Get the character to be inserted from the STACK and put it in Register A
    2FCA
    LD (HL),A77
    Save the character in Register A at the location of the current input buffer pointer in HL
    2FCB-2FCD
    CALL 032AHCALL OUTDOCD 2A 03
    Go display the character in Register A
    2FCE
    INC HL23
    Bump the value of the input buffer pointer in HL
    2FCF-2FD1
    JP 2F7DHJP IEDC3 7D 2F
    Jump back to 2F7DH to get more characters

    2FD2 - EDITCommand - BACKSPACE Logic- "DELED".

    2FD2-  ↳ DELED
    LD A,B78
    Top of a loop. Test to see if we are moving back past the first character by first loading Register A with the number of times to backspace in Register B
    2FD3
    OR AB7
    Check to see if this is the start of the BASIC line
    2FD4
    RET ZC8
    Return if this is the start of the BASIC line
    2FD5
    DEC B05
    Decrement the character position in Register B
    2FD6
    DEC HL2B
    Decrement the value of the buffer pointer in HL
    2FD7-2FD8
    LD A,08H3E 08
    Load Register A with a backspace the cursor character
    2FD9-2FDB
    CALL 032AHCALL OUTDOCD 2A 03
    Backspace the cursor on the video display
    2FDC
    DEC D15
    Decrement the number of times to perform the operation in Register D
    2FDD-2FDE
    JR NZ,2FD2HJR NZ,DELED20 F3
    Loop until done
    2FDF
    RETC9
    RETurn to CALLer
    2FE0-2FE2-  ↳ CRED
    CALL 2B75HCALL LISPRTCD 75 2B
    Since we need to display the rest of the BASIC line, we call the PRINT MESSAGE routine at 2B75H which writes string pointed to by HL to the current output device
    2FE3-2FE5-  ↳ EED
    CALL 20FEHCALL CRDOCD FE 20
    Go display a carriage return if necessary
    2FE6
    POP BCC1
    Clean up the STACK (to remove the DISPED return address)
    2FE7
    POP DED1
    Get the BASIC line number (in binary) from the STACK and put it in DE
    2FE8
    LD A,D7A
    Load Register A with the MSB of the BASIC line number in Register D
    2FE9
    AND EA3
    Combine the LSB of the BASIC line number in Register E with the MSB of the BASIC line number in Register A
    2FEA
    INC A3C
    Bump the combined BASIC line number in Register A
    2FEB-2FED-  ↳ EDITRT
    LD HL,(40A7H)LD HL,(BUFPNT)2A A7 40
    Load HL with the starting address of the input buffer.
    Note: 40A7H-40A8H holds the input Buffer pointer
    2FEE
    DEC HL2B
    Decrement the value of the input buffer pointer in HL
    2FEF
    RET ZC8
    Return if this is the Level II BASIC command mode
    2FF0
    SCF37
    Set the Carry flag to to fool the INSERT code; this flags the line number has having been seen
    2FF1
    INC HL23
    Bump the value of the input buffer pointer in HL
    2FF2
    PUSH AFF5
    Save the command mode flag in AF to the STACK
    2FF3-2FF5
    JP 1A98HJP EDENTC3 98 1A
    Jump to entry point in the main Level II processing code

    2FF6 - EDITCommand - QUIT Logic- "QED".

    2FF6-  ↳ QED
    POP BCC1
    Get rid of the DISPED return address
    2FF7
    POP DED1
    Get the line number off of the stack
    2FF8-2FFA
    JP 1A19HJP READYC3 19 1A
    Jump to the Level II BASIC READY routine
    2FFB
    SBC C3H
    This is just garbage
    2FFD
    This is just garbage

    3000 - Jump Table.

    3000
    Jump to 325EH for a SLOW tape header write.
    3003
    Jump to 329BH for a FAST tape header write.
    3006
    Jump to 3274H for a SLOW tape header read.
    3009
    Jump to 32DAH for a FAST tape header read.
    300C
    Jump to 31C0H for Cassette OFF.
    300F
    Jump to 31D1H for Cassette ON.

    3012 - Model 4 ROM Gen 1

    *3012
    Jump to 3461H for Warm Boot.
    *3015
    Jump to 3401H for Bootstrap.

    3012 - Model 4 ROM Gen 2

    *3012
    JP 3486HC3 86 34
    Jump to 3486H for Warm Boot.
    *3015
    JP 3426HC3 26 34
    Jump to 3426H for Bootstrap.
    3018
    Jump to 35C2H for Maskable Interrupt Handler.
    301B
    Jump to 35FBH for RS-232 Initialization.
    301E
    Jump to 365AH for RS-232 Input.
    3021
    Jump to 3680H for RS-232 Output.

    *3024 - Model 4 ROM Gen 1

    *3024
    Jump to 338EH for Keyboard Input.
    *3027
    RET
    NOP
    NOP
    I/O Re-Router was removed from the Model 4 ROM.

    *3024 - Model 4 ROM Gen 2

    *3024
    Jump to 338EH for Keyboard Input.
    *3027
    JUMP to 37A5H which is the new print screen routine
    302A
    Jump to 31F7H for part of cassette header routine.

    *302D - Model 4 ROM Gen 1

    *302D
    Jump to 37A4H for a routine which parses whether the current instruction on a the current line is in quotes.
    *3030
    Jump to 37C2H for STRING=DATE$+""+TIME$.

    *302D - Model 4 ROM Gen 2

    *302D
    JP 375CHC3 5C 37
    Jump to 375CH for a routine which parses whether the current instruction on a the current line is in quotes.
    *3030
    JP 37EAHC3 EA 37
    Jump to 37EAH for STRING=DATE$+""+TIME$.
    3033
    "$DATE"
    Jump to 35BBH to put the DATE onto the upper right hand corner of the screen.
    3036
    Jump to 35A0H to put the TIME 10 characters from the upper right hand corner of the screen.
    3039
    IN A,(0E4H)
    Poll Port E4H into A.

    NOTE: Port E4H is the Non-Maskable Interrupt Latch.
    303B
    BIT 5,A
    Test Bit 5 of Register A. Bit 5 of the NMI on an Input test is the RESET STATUS, with 0=False, and 1=True

    *303DH-303FH - Model 4 ROM Gen 1

    *303DH-303FH
    Jump to 34CEH.

    *303DH-303FH - Model 4 ROM Gen 2

    *303DH-303FH
    JP 350BHC3 0B 35
    Jump to 350BH.
    3040
    XOR D
    3041
    XOR D

    *3042 - Model 4 ROM Gen 1 - Prompt the User to set the cassette baud rate.

    *3042
    Jump to 310BH

    *3042 - Model 4 ROM Gen 2 - Prompt the User to set the cassette baud rate.

    *3042
    JP 33FFHC3 FF 33
    Jump to 33FFH to process the CASS? question

    *3045-3064 - Model 4 ROM Gen 1

    *3045-305F
    @abcdefghijklmnopqrstuvwxyz
    Keyboard Rows 0-3, Unshifted, No Caps Lock
    *3060
    NOP
    Computer version number, which is always 1 for a Model III and 0 for a Model 4
    *3061
    NOP
    *3062
    NOP
    *3063
    NOP
    *3064
    NOP
    *3065-307D
    30 31 32 33 34 35 36 37 38 39 3A 3B 2C 2D 2E 2F 0D 1F 01 5B 0A 08 09 20 00
    Keyboard Rows 4-6, Unshifted, No Caps Lock
    *307E
    NOP
    *307F
    NOP
    *3080
    NOP
    *3081
    NOP
    *3082
    NOP
    *3083
    NOP
    *3084
    NOP
    *3085-309F
    60 41 42 43 44 45 46 47 48 49 4A 4B 4C 4D 4E 4F 50 51 52 53 54 55 56 57 58 59 5A
    Keyboard Rows 0-3, shifted, No Caps Lock
    *30A0
    NOP
    *30A1
    NOP
    *30A2
    NOP
    *30A3
    NOP
    *30A4
    NOP
    *30A5
    NOP
    *30A6-30BC
    21 22 23 24 25 26 27 28 29 2A 2B 3C 3D 3E 3F 0D 1F 01 1B 1A 18 19 20
    Keyboard Rows 4-6, shifted, No Caps Lock
    *30BD
    NOP
    *30BE
    NOP
    *30BF
    NOP
    *30C0
    NOP
    *30C1
    NOP
    *30C2
    NOP
    *30C3
    NOP
    *30C4
    NOP
    *30C5-30DF
    40 41 42 43 44 45 46 47 48 49 4A 4B 4C 4D 4E 4F 50 51 52 53 54 55 56 57 58 59 5A
    Keyboard Rows 0-3, UNshifted, Caps Lock.
    *30E0
    NOP
    *30E1
    NOP
    *30E2
    NOP
    *30E3
    NOP
    *30E4
    NOP
    *30E5-30FC
    30 31 32 33 34 35 36 37 38 39 3A 3B 2C 2D 2E 2F 0D 1F 01 5B 0A 08 09 20
    Keyboard Rows 4-6, UNshifted, Caps Lock
    *30FD
    NOP
    *30FE
    NOP
    *30FF
    NOP
    *3100
    NOP
    *3101
    NOP
    *3102
    NOP
    *3103
    NOP
    *3104
    NOP

    *3105H - Model 4 Gen 1 jump to NON-Disk BASIC

    *3105
    GOSUB to the $SETCAS routine which prompts the user to set the cassette baud rate (310BH - 313AH)
    *3108
    JUMP to 0075H to go to non-disk initialization.

    *310BH - Model 4 Gen 1 Set the TAPE BAUD RATE ($SETCAS).

    *310B
    EI
    Enable Interrupts.
    *310C
    GOSUB to 312DH which loads A with a carrage return, and jumps to 0033H to display it.
    *310F
    Load HL with the address of the "CASS?" message.
    *3112
    GOSUB to 021BH. Note; 021BH will display the character at (HL) until a 03H is found.
    *3115
    GOSUB to 0049H.

    NOTE: 0049H is the $KBWAIT routine which scans the keyboard and returns with the key pressed, if any, in register A.
    *3118
    CP 0DH
    Compare A and 0D (a CARRIAGE RETURN).
    *311A
    If the CARRIAGE RETURNis hit, then JUMP to 312AH to default to HIGH SPEED.
    *311C
    PUSH AF
    Save AF to the STACK.

    NOTE: A currently holds the character pressed in response to the "CASS?" message.
    *311D
    GOSUB to 0033H.

    NOTE: 0033H is the character print routine, to put the character held in Register A at the current cursor position.
    *3120
    POP AF
    Restore AF from the STACK.

    NOTE: A will then hold the character pressed in response to the "CASS?" message.
    *3121
    CP 48H
    Compare A with 48H (ASCII: H).
    *3123
    If A = Hthen JUMP to 312AH to select HIGH SPEED.
    *3125
    CP 4CH
    Compare A with a 4CH (ASCII: L).
    *3127
    If A was NOT a "L" then JUMP back to 310BH to try again.

    3129H - Model 4 Gen 1 Set the Selected Cassette Baud Rate as LOW SPEED

    *3129
    XOR A
    Set A to 0.

    NOTE: Storing a 0 at 4211H will select low speed.

    312AH - Model 4 Gen 1 Write the Byte to Select the Selected "CASS?" Cassette Baud Rate

    *312A
    LD (4211H),A
    Save A (which is either "H" or 0 at this point) into (4211H).

    NOTE: 4211H holds the CASSETTE BAUD RATE SELECT as:
    • 0: 500 Baud
    • Anything Else: 1500 baud
    *312D
    LD A,0DH
    A = CARRIAGE RETURN
    *312F
    Display the CARRIAGE RETURNvia a JUMP to 0033H.

    NOTE: 0033H is the character print routine, to put the character held in Register A at the current cursor position.

    3132H - Model 4 Gen 1 MESSAGE STORAGE LOCATION

    *3132-313A
    0EH + Cass ? +03H
    *313B
    Finish the cassette setup by calling a portion of the keyboard scan routine which CALLS the screen print routine at 34FDH
    *313E
    XOR A
    Clear Register A and All Flags
    *313F
    RET
    Return a null character.
    *3140
    NOP
    *3141
    NOP
    *3142
    NOP
    *3143
    NOP
    *3144
    NOP

    *3145 - Model 4 Gen 1 Printer Character Table Codes 32-127.

    *3145-31A4
    20 21 22 23 24 25 26 27 28 29 2A 2B 2C 2D 2E 2F 30 31 32 33 34 35 36 37 38 39 3A 3B 3C 3D 3E 3F 40 41 42 43 44 45 46 47 48 49 4A 4B 4C 4D 4E 4F 50 51 52 53 54 55 56 57 58 59 5A 5B 5C 5D 5E 5F 40 61 62 63 64 65 66 67 68 69 6A 6B 6C 6D 6E 6F 70 71 72 73 74 75 76 77 78 79 7A 7B 7C 7D 7E 7F

    *3045 - Model 4 ROM Gen 2.

    *3045
    JP 378DHC3 8D 37
    Jump to 378DH to a new routine for Rom GEN 2 which processes printing when a 01H or Line Feed or Carriage Return is the current character being printed
    *3048
    JP 377AHC3 7A 37
    Jump to 377AH to check to see if we are on a new printable page and set the pointers accordingly. CALLED from 0431 and 0445
    *304B
    JP 3179HC3 79 31
    Jump to 3179H to continuing initialization routine by setting up the RS-232

    *3045 - Model 4 ROM Gen 2 Keyboard Rows 0-3, Unshifted, No Caps Lock.

    *304E-3068
    @abcdefghijklmnopqrstuvwxyz
    *3069
    NOP
    *306A
    NOP
    *306B
    NOP
    *306C
    NOP
    *306D
    NOP
    *306E-3085
    30 31 32 33 34 35 36 37 38 39 3A 3B 2C 2D 2E 2F 0D 1F 01 5B 0A 08 09 20

    *3086 - Model 4 ROM Gen 2 - Snuck in a code snippet from the RS-232 Initialization Routine - Poll the UART and wait for the P FLAG to not be set and then CONTINUE at 306CH.

    *3086
    IN A,(0EAH)DB EA
    Poll the RS-232 UART Control Register and Status Register (at Port EAH) and put the results into Register A
    *3087
    OR AB7
    Set the FLAGS based on the RS-232 UART Control Register and Status Register
    *3089-308B
    JP P,3086HF2 86 30
    If the P FLAG is set, then LOOP back 2 instructions and poll again
    *308C-308D
    If the P FLAG is NOT set, then CONTINUE processing at 306CH which polls the RS-232 Register at Port EBH, puts the results into Register A, and RETurns to CALLer

    **3086 - Model 4 ROM Student Network Edition - A little Network 4 Boot Code - Poll the keyboard and mask for a "4" key.

    This routine will do a little of the work to check for the ‘4’ key being pressed. It will poll the keyboard and process the results masked against the "4" key, but then RETurn without doing anything about it.

    **3086
    LD A,(3810H)3A 10 38
    Fetch the Keyboard Matrix 0-7 into Register A to check for certain keys.
    **3089
    AND 10HE6 10
    MASK that keyboard matrix against 10H (Binary: 0001 0000) to leave only Bit 5 so as to check to see if a 4 key was pressed.
    **308B
    RETC9
    RETurn to CALLer
    **306C
    NOP
    **306D
    NOP
    **306D
    NOP

    *308E-30A8 - Model 4 ROM Gen 2 - Continuing with the Keyboard Table

    *308E-30A8
    60 41 42 43 44 45 46 47 48 49 4A 4B 4C 4D 4E 4F 50 51 52 53 54 55 56 57 58 59 5A
    *30A9
    NOP
    *30AA
    NOP
    *30AB
    NOP
    *30AC
    NOP
    *30AD
    NOP
    *30AE
    NOP
    *30AF-30C5
    21 22 23 24 25 26 27 28 29 2A 2B 3C 3D 3E 3F 0D 1F 01 1B 1A 18 19 20

    *30C6-30C8 - Model 4 ROM Gen 2 - A little code snippet

    *30C6-30C7
    IN A,(0EBH)DB EB
    Poll the RS-232 Register at Port EBH and put the results into Register A.
    *30C8
    RETC9

    **30C6-30C8 - Model 4 ROM Student Network Edition - A little code snippet

    *30C6
    NOP
    *30C7
    NOP
    *30C8
    NOP

    *3089 - Model 4 ROM Gen 2 - Continuing with the Keyboard Table

    *30C9
    NOP
    *30CA-30CD
    PUSH BC
    POP BC
    NOP
    RET
    Standard code for a short delay
    *30CE
    @ABCDEFGHIJKLMNOPQRSTUVWXYZ
    *30E9
    NOP
    *30EA
    NOP
    *30EB
    NOP
    *30EC
    NOP
    *30ED
    NOP
    *30EE
    30 31 32 33 34 35 36 37 38 39 3A 3B 2C 2D 2E 2F 0D 1F 01 5B 0A 08 09 20
    Keyboard Rows 4-6, UNshifted, Caps Lock
    *3106
    LD A,(3880H)3A 80 38
    Load A with the value held at 3880H (which are the SHIFT KEYS)
    *3109
    LD HL,414FH21 F4 41
    Point HL to 414FH
    *310C
    AND 7CHE6 7C
    MASK the value held in the SHIFT KEY RAM location against 0111 1100 to keep only bits 2, 3, 4, 5, and 6 live
    *310E
    OR AB7
    OR A against itself to reset the flags
    *310F
    JR Z,3142H28 31
    If the Z FLAG is set, JUMP to 3142H to continue checking fro special keys
    *3111
    LD E,A5F
    Put the masked A into E.
    *3112
    XOR (HL)AE
    Toggle against the old image.
    *3113
    LD (HL),E73
    Save the new image into (HL).
    *3114
    AND EA3
    Mask Register E against Register A.
    *3115
    JR Z,3157H28 40
    If ZERO then the LEFT SHIFT PRESSED was JUMP to 3157H to restart parsing the keyboard
    *3117
    LD BC,05C4H01 C4 05
    Load BC with 05C4H to set up a 1/50 second delay for de-bounce.
    *311A
    CALL 0060HCD 60 00
    GOSUB to 0060H which jumps to the delay routine at 01FBH (which uses BC as a loop counter). It RETs when done so it doesn't come back here.
    *311D
    LD A,(3880H)3A 80 38
    Load A with the value held at 3880H (which are the SHIFT KEYS)
    *3120
    AND 7CHE6 7C
    MASK the value held in the SHIFT KEY RAM location against 0111 1100 to keep only bits 2, 3, 4, 5, and 6 live
    *3122
    CP EBB
    Compare A with E.
    *3123
    JP NZ,37D3HC2 D3 37
    If The MASKED Shift Key Value does not match Register E then JUMP to 37D3H.
    *3126
    RLA17
    Rotate A left one bit, with the contents of BIT 7 being put into the CARRY FLAG and the CARRY FLAG are put into BIT 0. This puts the read data into the CARRY FLAG.
    *3127
    RLA17
    Rotate A left one bit, with the contents of BIT 7 being put into the CARRY FLAG and the CARRY FLAG are put into BIT 0. This puts the read data into the CARRY FLAG.
    *3128
    We have a function key, but which one. If the NC FLAG is set, it isn't the F3, so JUMP to 312EH to test the F2 Key
    *312A
    LD A,(41F3H)3A F3 41
    Load Register A with the value of the character to be returned when the F3 key is pressed
    *312D
    RETC9
    RETurn to Caller

    *312E - Model 4 Gen 2 - KEYBOARD Routine - Check and Process the F2 Key or Jump Away.

    *312E
    RLA17
    Rotate A left one bit, with the contents of BIT 7 being put into the CARRY FLAG and the CARRY FLAG are put into BIT 0. This puts the read data into the CARRY FLAG.
    *312F
    If the NC FLAG is set, it isn't the F2 key, so JUMP to 3135H to test the F1 key
    *3131
    LD A,(41ECH)3A EC 41
    Load Register A with the value of the character to be returned when the F2 key is pressed
    *3134
    RETC9
    RETurn to Caller

    *3135 - Model 4 Gen 2 - KEYBOARD Routine - Check and Process the F1 Key or Jump Away.

    *3135
    RLA17
    Rotate A left one bit, with the contents of BIT 7 being put into the CARRY FLAG and the CARRY FLAG are put into BIT 0. This puts the read data into the CARRY FLAG.
    *3136
    If the NC FLAG is set, it isn't the F1 key, so JUMP to 313CH to keep checking special keys
    *3138
    LD A,(41EBH)3A EB 41
    Load Register A with the value of the character to be returned when the F1 key is pressed
    *313B
    RETC9
    RETurn to Caller

    *33C4 - Model 4 Gen 2 - KEYBOARD Routine - Part of the Keyboard Scan Routine. Keep Checking Special Keys.

    *313C
    RLA17
    Rotate A left one bit, with the contents of BIT 7 being put into the CARRY FLAG and the CARRY FLAG are put into BIT 0. This puts the read data into the CARRY FLAG.
    *313D
    JP C,37CCHDA CC 37
    If the CARRY FLAG is high, then JUMP to 37CCH to toggle the CAPS LOCK
    *3140
    JUMP to 3152H to deal with the CONTROL KEY flag

    *33CA - Model 4 Gen 1 - KEYBOARD Routine - Part of the Keyboard Scan Routine.

    *3142
    LD (HL),A77
    Save the character held in Register A into the memory location pointed to by Register Pair HL
    *3143
    LD A,FFH3E FF
    Load A with FF to set up for a FLAG = 0FFH = NO CONTROL.
    *3145
    LD HL,3840H21 40 38
    Load HL with 3840H to start a check for a DOWN ARROW.
    *3148
    BIT 4,(HL)CB 66
    Test BIT 4 of (HL) to check for a DOWN ARROW.
    *314A
    JR Z,3154H28 08
    JUMP to 3154H if the a DOWN ARROW was NOT pressed.
    *314C
    SLA LCB 25
    Next we need to check for a a LEFT SHIFT so shift L left.
    *314E
    BIT 0,(HL)CB 46
    Test BIT 0 of (HL) to check for a LEFT SHIFT.
    *3150
    JUMP to 33DCH if the a LEFT SHIFT was NOT pressed.
    *3152
    LD A,1FH3E 1F
    Load A with 1F to set up for FLAG = CONTROL KEY.
    *3154
    LD (4224H),A32 24 42
    Save the CONTROL FLAG into (4224H).
    NOTE: 4224H Holds the CONTROL KEY flag.
    *3157
    CALL 338EHCD 8E 33
    GOSUB to 338EH to start parsing the keyboard from row 0
    *315A
    RET NCD0
    If that routine exited with NC FLAG set, RETurn
    *315B
    CALL 3739HCD 39 37
    GOSUB to 3739H to deal with a SHIFT key
    *315E
    CP 1AHFE 1A
    Check A against 1AH to see if we have a SHIFT+DOWN ARROW.
    *3160
    JP Z,37D3HCA D3 37
    If we have a SHIFT+DOWN ARROW then JUMP to 37D3H.
    *3163
    OR AB7
    Set the flags based on Register A
    *3164
    JP Z,37CCHCA CC 37
    If Register A = 0 the JUMP to 37CCH to toggle the CAPS LOCK
    *3167
    LD HL,4224H21 24 42
    Set Register Pair HL to 4224H, which is the CONTROL KEY flag.
    *316A
    BIT 7,(HL)CB 7E
    Test Bit 7 of the CONTROL KEY flag in RAM.
    *316C
    If Bit 7 of the CONTROL KEY flag is OFF, the JUMP to 3174H
    *316E
    CP 2AHFE 2A
    Check A against a * key.
    *3170
    JP Z,37C7HCA C7 37
    If A is a * then JUMP to 313BH which is a new screen print routine for CTRL+*. This differs from the regular SCREEN PRINT routine in that it processes a XOR A before returning.
    *3173
    AND (HL)A6
    Prepare to check for a BREAK key by masking A against the memory contents of HL ...
    *3174
    CP 01HFE 01
    ... and COMPARING it to 01H.
    *3176
    RET NZC0
    If the result of the compare is NOT zero, then RETURN.
    *3177
    RST 28HEF
    If we are here, then a BREAK key was hit, so call RST 28H to handle the BREAK key.
    *3178
    RETC9
    RETURN to Caller

    *3179 - Model 4 Gen 2 - Continuing Initialization Routine by setting up the RS-232.

    *3179
    XOR AAF
    Set Register A to 0
    *317A
    OUT (E8H),AD3 E8
    Output A to port E8H.
    NOTE: Port E8H is the RS-232 Status Register & Master Reset. Outputting ANYTHING to Port E8H resets the RS-232.
    *317C
    LD A,EEH3E EE
    Set Register A to EEH for outputting to the RS-232 Baud Rate Select and Sense Switches: (Port E9H)
    *317E
    OUT (E9H),AD3 E9
    Initialize the RS-232 to 9,600 baud by outputting EEH (held in Register A) to Port E9H.
    NOTE: Port E9H is the RS-232 Baud Rate Selects and Sense Switches. Outputting to Port E9H will set the Baud Rate as follows: Bits 0-3 - Select the Receive Rate and Bits 4-7 - Select the Transmit Rate
    *3180
    LD A,6DH3E 6D
    Set Register A to 6DH for outputting to the RS-232 UART Control Register and Status Registe (Port EAH)
    *3182
    OUT (EAH),AD3 EA
    Sent 6DH (0110 1101) to the RS-232 UART Control Register and Status Register at Port EAH. When OUTPUTTINg to EAH, the following is handled:
    • Bit 0: Data Terminal Ready (1=DTR Off) (Pin 20 of the DB-25)
    • Bit 1: Request to Send (1=RTS Off) (Pin 4 of the DB-25)
    • Bit 2: Break (1=Send Break Signal)
    • Bit 3: Parity Enable (0 = Enable Parity, 1 = Disable Parity)
    • Bit 4: Stop Bits (0 = 1 Stop Bit, 1 = 2 Stop Bits
    • Bits 5-6: Select Word Length (00 = 5, 01 = 7, 10 = 6, 11 = 8)
    • Bit 7: Parity (0 = Odd, 1 = Even)
    *3184
    IN A,(E8H)DB E8
    Poll the RS-232 Status Register and Master Reset of Port E8H and put the value into Register A.
    *3186
    BIT 6,ACB 77
    Test Bit 6 of the RS-232 Status Register to check Data Set Ready (Pin 6 of the DB-25).
    *3188
    If the DATA SET READY is ZERO then LOP back 2 instructions to 3184 to poll again
    *318A
    LD A,6CH3E 6C
    Set Register A to 6CH (0110 1100) for outputting to the RS-232 UART Control Register and Status Registe (Port EAH)
    *318C
    OUT (EAH),AD3 EA
    Send 01101100 to the RS-232 UART Control Register and Status Register. This sets: DTR on, RTS on, Send BREAK, Disable Parity, 1 Stop Bit, 8 Bit Word Length, and Odd Parity
    *318E
    IN A,(E8H)DB E8
    Poll the RS-232 Status Register and Master Reset of Port E8H and put the value into Register A.
    *3190
    BIT 6,ACB 77
    Test Bit 6 of the RS-232 Status Register to check Data Set Ready (Pin 6 of the DB-25).
    *3192
    If the DATA SET READY is ZERO then LOOP back 2 instructions to 318EH to poll again
    *3194
    LD A,0FH3E 0F
    Set Register A to 0FH
    *3196
    CALL 37D5HCD D5 37
    GOSUB to 37D5H to send the Character in Register A to the RS-232, once the RS-232 shows ready to accept that character.
    *3199
    CALL 3086HCD 86 30
    GOSUB to 3086 to Poll the UART and wait for the P FLAG to not be set and then CONTINUE at 306CH
    *319C
    CALL 3086HCD 86 30
    GOSUB to 3086 to Poll the UART and wait for the P FLAG to not be set and then CONTINUE at 306CH
    *319F
    CALL 37D5HCD D5 37
    GOSUB to 37D5H to send the Character in Register A to the RS-232, once the RS-232 shows ready to accept that character.
    *31A2-31A4
    JP 3517HC3 17 35
    JUMP to 3517H to finish up initialization by filling 256 bytes into 4300H and then JUMP there

    **3179 - Model 4 ROM Student Network Edition - Replacement for the above RS-232 Initialization

    **3179
    LD A,(4210H)3A 10 42
    Get the status of the status I/O by setting Register A to hold the contents of memory location 4210H into A.

    NOTE: 4210H holds the bit mask for port ECH. Port ECH stores miscellaneous controls.
    **317C
    OR 10HF6 10
    OR against 10H (0001 0000) to turn all I/O ports on
    **317E
    LD (4210H),A32 10 42
    Put the masked status back, first by loading it into 4210H
    **3181
    OUT (ECH),AD3 EC
    ... and then by sending it to 0ECH which is the same as 04210.
    **3183
    LD A,08H3E 08
    Put an 08H into Register A
    **3185
    OUT (D3H),AD3 D3
    Send 08H to Port D3H, which is the Network 4 Omninet MSB pointer
    **3187
    XOR AAF
    Put an 00H into Register A
    **3188
    OUT (D1H),AD3 D1
    Send 08H to Port D1H, which is the Network 4 Omninet LSB pointer
    **318A
    LD BC,00D0H01 D0 00
    In preparation for INIR commands, we must set up B, C, and HL. This will set B as 00H and C as D0H, which is the Network 4 INPUT Port Number
    **318D
    LD HL,7000H21 00 70
    In further preparation for the INIR commands, point HL to the BUFFER for the code, which is 7000H
    **3190
    PUSH HLE5
    Save the 7000H Buffer start point to the top of the stack
    **3191
    INIRED B2
    Write a byte from port C to the memory location pointed to by HL, and then increase HL and decrease B.
    **3193
    INIRED B2
    Write a byte from port C to the memory location pointed to by HL, and then increase HL and decrease B.
    **3195
    INIRED B2
    Write a byte from port C to the memory location pointed to by HL, and then increase HL and decrease B.
    **3197
    INIRED B2
    Write a byte from port C to the memory location pointed to by HL, and then increase HL and decrease B.
    **3199
    INIRED B2
    Write a byte from port C to the memory location pointed to by HL, and then increase HL and decrease B.
    **319B
    INIRED B2
    Write a byte from port C to the memory location pointed to by HL, and then increase HL and decrease B.
    **319D
    INIRED B2
    Write a byte from port C to the memory location pointed to by HL, and then increase HL and decrease B.
    **319F
    INIRED B2
    Write a byte from port C to the memory location pointed to by HL, and then increase HL and decrease B.
    **31A1
    RETC9
    RETurn to CALLer
    **31A2
    NOP00
    **31A3
    NOP00
    **31A4
    NOP00

    31A5 - Output the TIMING MARK to the cassette

    31A5
    LD A,01H
    Load A with 01H. This is to prepare to send 0.46V to tape.
    31A7
    OUT (0FFH),A
    Load Port FFH with A.

    NOTE: Port FFH is the cassette port. When outputting to FFH, Bits Zero and 1 set to: 00 is .85V, 01 is .46V, and 10 is 0.0V.
    31A9
    LD B,0DH
    Load B with 0DH in as a loop counter.
    31AB
    Loop this instruction until B hits ZERO.
    31AD
    LD A,02H
    Load A with 01H. This is to prepare to send 0.0V to tape.
    31AF
    OUT (0FFH),A
    Load Port FFH with A.

    NOTE: Port FFH is the cassette port. When outputting to FFH, Bits Zero and 1 set to: 00 is .85V, 01 is .46V, and 10 is 0.0V.
    31B1
    LD B,0DH
    Load B with 0DH in as a delay.
    31B3
    Loop this instruction until B hits ZERO.
    31B5
    GOSUB to 31F3 to send 0.46V to tape.

    NOTE: 31F3H resets the cassette port, and then output a 0 to the Cassette Port FFH.
    31B8
    LD B,78H
    Load B with 78H in as a delay.
    31BA
    Loop this instruction until B hits ZERO.
    31BC
    RET
    RETURN.
    31BD
    Load HL with 2CA5H.

    NOTE: 2CA5H is the ?BAD? message string.

    31C0 - Turn Off The Cassette

    31C0
    LD A,(4213H)
    Load A with the memory contents of 4213H.

    NOTE: 4213H is the default interrupt vector setting for the cassette.
    31C3
    OUT (0E0H),A
    Output A to Port E0H.

    NOTE: Port E0H is the maskable interrupt latch, which directs jumps.
    Jump Table:
    • xxxxxxx1 jumps to 3365H
    • xxxxxx1x jumps to 3369H
    • xxxxx1xx jumps to 4046H
    • xxxx1xxx jumps to 403DH
    • xxx1xxxx jumps to 4206H
    • xx1xxxxx jumps to 4209H
    • x1xxxxxx jumps to 44040H
    • 1xxxxxxx jumps to 44043H
    31C5
    IN A,(FFH)
    INPut the contents of Port FFH and store the result in A.

    NOTE: Port FFH is the cassertte port read status. If the 7th bit is 0 then it is low, and if 7th bith is 1 then it is high.
    31C7
    LD A,(4210H)
    Load A with the memory contents of 4210H.

    NOTE: 4210H is the bit mask for Port ECH. Port ECH is the Miscellaneous Controls port, which covers clock on/off (Bit 0), cassette motor on or off (Bit 1), double size video on or off (Bit 2), and special character set select of Kana or misc (Bit 3). Higher bits are used for the Model 4 only.
    31CA
    AND FDH
    Mask A against FDH (1111 1101) to zero bit 1.
    31CC
    GOSUB to 31EDH to save the mask into 4210H, output it to Port ECH, and return here.
    31CF
    EI
    Enable Interrupts.
    31D0
    RET
    RETURN.

    31D1 - Turn On The Cassette - Part 1. This will remove the return address, save DE and BC, restore the return address, and than blank the "**"

    31D1
    EX DE,HL
    Swap DE and HL to remove the return address.
    31D2
    EX (SP),HL
    Swap the memory contents pointed to by the STACK POINTER and HL (which is now what DE was).
    31D3
    PUSH BC
    Save BC to the STACK.
    31D4
    PUSH HL
    Save HL to the STACK.
    31D5
    EX DE,HL
    Swap DE and HL back.
    31D6
    IN A,(ECH)
    Poll Port ECH and put the results into A.

    NOTE: Port ECH is the Miscellaneous Controls port, which covers clock on/off (Bit 0), cassette motor on or off (Bit 1), double size video on or off (Bit 2), and special character set select of Kana or misc (Bit 3). Higher bits are used for the Model 4 only.
    31D8
    LD DE," "
    Load DE with SPACESPACE.
    31DB
    LD (3C3EH),DE
    Load the screen memory location of 3C3EH with DE.
    31DF
    GOSUB to 31E8H to turn on the cassette.
    31E2
    Load a delay count of 7D00 (Decimal: 32,000) into BC.
    31E5
    Jump to 0060H, which JUMPs to the delay routine at 01FBH and RETURNS.

    31E8 - Turn On The Cassette - Actually Set the Bit Mask and Output the Command

    31E8
    LD A,(4210H)
    Load A with the memory contents of 4210H.

    NOTE: 4210H is the bit mask for Port ECH. Port ECH is the Miscellaneous Controls port, which covers clock on/off (Bit 0), cassette motor on or off (Bit 1), double size video on or off (Bit 2), and special character set select of Kana or misc (Bit 3). Higher bits are used for the Model 4 only.
    31EB
    OR 02H
    OR A with 02H (0000 0010) to set Bit 1.
    31ED
    LD (4210H),A
    Save revised Bit Mask back to 4210H.

    NOTE: 4210H is the bit mask for Port ECH. Port ECH is the Miscellaneous Controls port, which covers clock on/off (Bit 0), cassette motor on or off (Bit 1), double size video on or off (Bit 2), and special character set select of Kana or misc (Bit 3). Higher bits are used for the Model 4 only.
    31F0
    OUT (0ECH),A
    Output that Bit Mask to port 0ECH.
    31F2
    RET
    RETURN.

    31F3 - Reset the Cassette Port. This routine OUTputs a 0 to the Cassette Port FFH

    31F3
    XOR A
    We want to reset the cassette port so we zero A.
    31F4
    OUT (0FFH),A
    Load Port FFH with A.

    NOTE: Port FFH is the cassette port. When outputting to FFH, Bits Zero and 1 set to: 00 is .85V, 01 is .46V, and 10 is 0.0V.
    31F6
    RET
    RETURN.

    31F7 - Check to see if we have a PRINT #command and, if so, get the port number, validate that the next character is a ,and return.

    31F7
    LD A,(HL)
    Load Register A with the memory contents pointed to by Register Pair HL.
    31F8
    SUB 23H
    Subtract 23H so that we can test to see if the caller was a PRINT #command.
    31FA
    If that subtraction didn't result in a ZERO (a match), the JUMP to 0253H.

    NOTE: 0253H is in the middle of the "Write a Byte to Cassette" Routine. It ends in a RETURN.
    31FD
    If it was a PRINT #command, then GOSUB to 2B01H to get the device number.
    3200
    RST 08H
    ","
    So now we have PRINT #nand the next character needs to be a ,, so call RST 08 to check for the next character against a "," and generate a SYNTAX ERRORif it wasn't.
    3202
    RET
    If we are here, then we have PRINT #n,so we RETURN.

    3203 - Vector for a SLOW cassette read

    3203
    LD B,08H
    Load B with 8, representing the need to LOOP for 8 bits.
    3205
    GOSUB to 3220H.

    NOTE: 3220H reads the tape until it finds a timing mark or the BREAKis hit.
    3208
    Loop that GOSUB 8 times.
    320A
    LD A,(4212H)
    Put the contents of 4212H into A.

    NOTE: 4212H holds the cassette blinker counter.
    320D
    INC A
    Bump A.
    320E
    AND 5FH
    Mask A against 5F (0101 1111) to turn Bit 7 and Bit 5 off.
    3210
    LD (4212H),A
    Put A into 4212H.

    NOTE: 4212H holds the cassette blinker counter.
    3213
    If the masked count is NOT ZERO, then skip the next few instructions.
    3215
    LD A,(3C3FH)
    Load A with the screen contents at position 3C3FH.
    3218
    XOR 0AH
    XOR A against 0AH (00001010). This turns a "*" into a " " and vice versa as a a "*" is 0010 1010 and when you XOR that against 0000 1010 you get 0010 0000 which is a " " (and vice versa).
    321A
    LD (3C3FH),A
    Put the revised A onto the screen at position 3C3FH.
    321D
    LD A,D
    Put D (the byte) into A.
    321E
    JUMP to 3298H to restore the registers and RETURN.

    3220 - Cassette - Keep reading tape looking for a timing mark or BREAK.

    3220
    PUSH BC
    Save BC to the STACK.
    3221
    IN A,(FFH)
    Poll Port FFH with the results into Register A.

    NOTE: FFH is the Cassette Port.
    3223
    RLA
    Put the level received in A into the Carry Bit by rotating A left 1 bit (putting bit 7 into the CARRY FLAG and the old CARRY FLAG into bit 0).
    3224
    If the CARRY FLAG is set then the timing mark was found, so JUMP to 322EH.
    3226
    GOSUB to 028DH to check for a BREAKkey.
    3229
    Loop back to 3221H until we get either a timing mark or a BREAKkey.
    322B
    If we are here, then we got a BREAKkey, so JUMP to 335CH.

    322E - Cassette - Wait for the timing mark to pass and the next data to show up. Put that data into Bit 0 of D.

    First, wait for 6EH Units (the length of the timing mark)

    322E
    LD B,6EH
    Load B with 6EH, which is the length of the timing mark.
    3230
    Loop until B is 0.

    Reset the cassette port

    3232
    GOSUB to 31F3H to RESET the cassette port.

    Next, wait for 98H Units (the length until a data pulse is expected)

    3235
    LD B,98H
    Load B with 98H, which is when the next data pulse should be available.
    3237
    Loop until B is 0.

    ... continue

    3239
    IN A,(FFH)
    Poll Port FFH and put the results into A.

    NOTE: Port FFH is the Cassette Port.
    323B
    POP BC
    Restore BC from the STACK.
    323C
    RLA
    Rotate A left one bit, with the contents of BIT 7 being put into the CARRY FLAG and the CARRY FLAG are put into BIT 0. This puts the read data into the CARRY FLAG.
    323D
    RL D
    Rotate D left one bit, with the contents of BIT 7 being put into the CARRY FLAG and the CARRY FLAG are put into BIT 0. This puts the read data into BIT 0 of D.
    323F
    JUMP to 31F3H to reset the cassette port and RETURN.

    3241 - Vector for a SLOW cassette write. On entry A is the byte to output.

    3241
    PUSH AF
    Save AF to the STACK.
    3242
    PUSH BC
    Save BC to the STACK.
    3243
    PUSH DE
    Save DE to the STACK.
    3244
    LD C,08H
    Load C with an 8, representing 8 bits to be written.
    3246
    LD D,A
    Load D with A.

    NOTE: D will be the DATA BYTE.
    3247
    GOSUB to 31A5H.

    NOTE: 31A5H outputs the timing mark.
    324A
    RLC D
    Rotate D left one bit, with the contents of BIT 7 being put into BOTH the CARRY FLAG and BIT 0. This puts the DATA BIT into CARRY.
    324C
    If there is no DATA BIT (because CARRY is 0) then JUMP to 3258H to wait the appropriate amount of time for a 0 byte to be written.
    324E
    If we are here, then there was a PULSE, so GOSUB to 31A5H to output the timing bit.
    3251
    DEC C
    Reduce the counter holding the number of bits to deal with by 1.
    3252
    Loop back to 3247H until the counter in C hits 0.
    3254
    POP DE
    Restore DE from the STACK.
    3255
    POP BC
    Restore BC from the STACK.
    3256
    POP AF
    Restore AF from the STACK.
    3257
    RET
    RETURN.

    3258 - "Write a 0 Bit" by simply waiting the appropriate amount of time and doing nothing.

    3258
    LD B,9AH
    Set a delay of 9A.
    325A
    Loop until that hits zero.
    325C
    JUMP to 3251H.

    325E - SLOW tape header write

    325E
    PUSH HL
    Save HL to the STACK.
    325F
    Load HL with 3241H.

    NOTE: 3241H is the Vector for a SLOW cassette write.
    3262
    LD (420CH),HL
    Load the memory contents of 420CH with HL.

    NOTE: 420CH is the TAPE WRITE VECTOR.
    3265
    LD B,53H
    Load B with 53H (Decimal: 83) in prepartion to output 83 ZEROes.
    3267
    XOR A
    Clear A and all flags.
    3268
    GOSUB to 3241H.

    NOTE: 3241H is the Vector for a SLOW cassette write.
    326B
    Loop back to 3268H until 63 ZEROes have been written.
    326D
    LD A,0A5H
    Load A with A5H.

    NOTE: A5H is the OUTPUT SYNC BYTE.
    326F
    GOSUB to 3241H.

    NOTE: 3241H is the Vector for a SLOW cassette write.
    3272
    JUMP to 3297H to restore all the registers and RETURN.

    3274 - SLOW tape header read

    3274
    PUSH HL
    Save HL to the STACK.
    3275
    Load HL with 3203H.

    NOTE: 3203H is the vector for a SLOW cassette read.
    3278
    LD (420EH),HL
    Put the vector for a slow cassette read into the memory location at 420EH.

    NOTE: 420EH is the TAPE READ VECTOR.
    327B
    LD B,40H
    Load B with 40H to set up a loop of 64 to try to find 64 zeroes.
    327D
    LD D,00H
    Load D with 0.
    327F
    GOSUB to 3220H.

    NOTE: 3220H will keep reading tape looking for a timing mark or BREAK.
    3282
    LD A,D
    Load A with the D (the data byte) to begin to check the current data byte.
    3283
    OR A
    Set up the flags.
    3284
    LOOP back to 327BH until A is ZERO.
    3286
    Loop back to 327FH 64 times.
    3288
    GOSUB to 3220H.

    NOTE: 3220H reads the tape until it finds a timing mark or the BREAKis hit.
    328B
    LD A,D
    Load A with the D (the data byte) to begin to check the current data byte.
    328C
    CP 0A5H
    Compare A to A5H looking for a SYNC BYTE.
    328E
    JUMP back to to 3288H if a SYNC BYTE wasn't found.
    3290
    LD HL,"**"
    In preparation to display a "**", load HL with **.
    3293
    LD (3C3EH),HL
    Put HL onto the screen at location 3C3EH.
    3296
    LD A,H
    Load A with H (which is a *.
    3297
    POP HL
    Restore HL from the STACK.
    3298
    POP BC
    Restore BC from the STACK.
    3299
    POP DE
    Restore DE from the STACK.
    329A
    RET
    RETURN.

    329B - FAST tape header write.

    329B
    PUSH HL
    Save HL to the STACK.
    329C
    Load HL with 32BAH.

    NOTE: 32BAH is the VECTOR TO FAST WRITE.
    329F
    LD (420CH),HL
    Load the memory contents of 420CH with HL.

    NOTE: 420CH is the TAPE WRITE VECTOR.
    32A2
    LD B,00H
    Load B with 00H to set up a loop of 256 to output 256 "55H" bytes.
    32A4
    LD A,55H
    Load A with "55H".
    32A6
    GOSUB to 32B4H.

    NOTE: 32B4 restore all registers from the STACK, and Fill C with A, and JUMP to cassette write
    32A9
    LOOP back to 32A4H 256 times.
    32AB
    LD A,7FH
    Load A with 7FH.

    NOTE: 7FH is the OUTPUT SYNC BYTE.
    32AD
    GOSUB to 32B4H.

    NOTE: 32B4 restore all registers from the STACK, and Fill C with A, and JUMP to cassette write
    32B0
    LD A,A5H
    Load A with A5H.

    NOTE: A5H is the SLOW SYNC BYTE.
    32B2
    JUMP to 3297H to restore all the registers and RETURN.

    32B4 - Restore all registers from the STACK, and Fill C with A, and JUMP to cassette write.

    32B4
    PUSH AF
    Save AF to the STACK.
    32B5
    PUSH BC
    Save BC to the STACK.
    32B6
    PUSH DE
    Save DE to the STACK.
    32B7
    LD C,A
    Load C with A.
    32B8
    JUMP to 32C1H.

    NOTE: 32C1H will write to cassette without a start bit.

    32BA - Save all registers to the STACK, and Fill C with A, GOSBUB to write out the START BIT ...

    32BA
    PUSH AF
    Save AF to the STACK.
    32BB
    PUSH BC
    Save BC to the STACK.
    32BC
    PUSH DE
    Save DE to the STACK.
    32BD
    LD C,A
    Load C with A.
    32BE
    GOSUB to 333EH to write the START BIT.

    32C1 - Call 3335H to Output a Bit 8 Times

    32C1
    LD B,08H
    Load B with an 8 to set up a loop for 8 bits.
    32C3
    GOSUB to 3335H to output the bit.
    32C6
    Loop back one instruction for all 8 bits.
    32C8
    JUMP to 3254H to restore all the registers and RETURN.

    32CA - Read the start bit, read 8 bits, check for error, and flash the star

    32CA
    GOSUB to 3350H to READ START BIT.
    32CD
    LD B,08H
    Load B with an 8 to set up a loop for 8 bits.
    32CF
    GOSUB to 3350H to READ BIT.
    32D2
    GOSUB to 337CH to CHECK FOR DATA ERROR.
    32D5
    Loop back 2 instructions for all 8 bits.
    32D7
    JUMP to 320AH to flash the *.

    32DA - FAST tape header read.

    32DA
    PUSH HL
    Save HL to the STACK.
    32DB
    Load HL with 32CAH.

    NOTE: 32CAH reads an verifies a byte.
    32DE
    LD (420EH),HL
    Load the TAPE READ VECTOR (memory location of 420EH) with the 32CAH.
    32E1
    LD A,01H
    Load A with a 1 to set the interrupt.
    32E3
    OUT (0E0H),A
    OUTPUT A to Port E0H.

    NOTE: Port E0H is the maskable interrupt latch, which directs jumps.
    Jump Table:
    • xxxxxxx1 jumps to 3365H
    • xxxxxx1x jumps to 3369H
    • xxxxx1xx jumps to 4046H
    • xxxx1xxx jumps to 403DH
    • xxx1xxxx jumps to 4206H
    • xx1xxxxx jumps to 4209H
    • x1xxxxxx jumps to 44040H
    • 1xxxxxxx jumps to 44043H
    32E5
    LD B,80H
    Set up a loop of 80H (128) to try to read 128 bits.
    32E7
    GOSUB to 3350H to READ a BIT.
    32EA
    LD A,C
    Load A with C (which is the pulse width).
    32EB
    CP 0FH
    Compare A to 0FH to see if the pulse width was too short.
    32ED
    Loop back to 32E5H if the pulse width was too short.
    32EF
    CP 3EH
    Compare A to 3EH to see if the pulse width was too long.
    32F1
    Loop back to 32E5H if the pulse width was too long.
    32F3
    Loop back to 32E7H 128 times.
    32F5
    Load HL with 0000.
    32F8
    LD B,40H
    Set up a loop of 40H (Decimal: 64) to try to read 64 bits.
    32FA
    GOSUB to 3350H to READ BIT.
    32FD
    GOSUB to 3350H to READ BIT.
    3300
    LD D,C
    Load D with C (which holds the delay count).
    3301
    GOSUB to 3350H to READ BIT.
    3304
    LD A,D
    Load A with D to set up to find the difference in the delays.
    3305
    SUB C
    Subtract C (the delay count) from A (which holds D).
    3306
    JUMP to 330AH (to GET ABSOLUTE) if the carry flag is NOT set.
    3308
    NEG
    A = 0 - A.
    330A
    CP 0DH
    Compare the NEGated A against 0DH. This has the effect of checking A-0DH, so if A < 0DH then the CARRY will be set and if A >= 0DH then the NO CARRY will be set.
    330C
    If the CARRY FLAG is set, JUMP to 3313H which will bump L and continue the 64 bit loop.
    330E
    INC H
    Bump HL since we have one more zero bit.
    330F
    Loop back to 32FAH until 64 bits read.
    3311
    JUMP out of the loop to 3316H.
    3313
    INC L
    Bump L since we have one more bit.
    3314
    Loop back to 32FAH until 64 bits read.
    3316
    LD A,40H
    Load A with 40H (64).
    3318
    CP H
    Compare A with H to check for bits.
    3319
    If they match, then JUMP forward to 3325H.
    331B
    CP L
    Compare with A to check for one bits.
    331C
    If not, JUMP forward to 32F5H.
    331E
    LD A,02H
    Load A with 2 to set the interrupt vector.
    3320
    OUT (0E0H),A
    Output the 2 to Port E0H.

    NOTE: Port E0H is the maskable interrupt latch, which directs jumps.
    Jump Table:
    • xxxxxxx1 jumps to 3365H
    • xxxxxx1x jumps to 3369H
    • xxxxx1xx jumps to 4046H
    • xxxx1xxx jumps to 403DH
    • xxx1xxxx jumps to 4206H
    • xx1xxxxx jumps to 4209H
    • x1xxxxxx jumps to 44040H
    • 1xxxxxxx jumps to 44043H
    3322
    GOSUB to 3350H to READ BIT.
    3325
    LD D,00H
    Zero D.
    3327
    GOSUB to 3350H to READ BIT.
    332A
    GOSUB to 337CH to check for a data error.
    332D
    LD A,D
    Load A with D (the read byte).
    332E
    CP 7FH
    Compare A against 7F to check for a MARKER BYTE.
    3330
    If no marker byte, then JUMP to 3327H.
    3332
    If it was a marker byte, then JP to 3290H to flash the *and continue.
    3335
    RLC C
    We need to shift the bit into CARRY so we rotate C left one bit, copying BIT 7 to the CARRY FLAG and the CARRY FLAG to BIT 0.
    3337
    If the bit was 0, JUMP forward 2 instructions to 333EH.
    3339
    Load DE with 1217H to set up a delay for 1 BIT.
    333C
    Skip the next instruction.
    333E
    Load DE with 2B2F to set the delay for a 0 BIT.
    3341
    DEC D
    Decrement D as DELAY #1.
    3342
    JUMP back to the prior instruction until D is 0.
    3344
    LD A,02H
    Load A with a 2 to set up for a write of 0 VOLTS to TAPE.
    3346
    OUT (0FFH),A
    Load Port FFH with A.

    NOTE: Port FFH is the cassette port. When outputting to FFH, Bits Zero and 1 set to: 00 is .85V, 01 is .46V, and 10 is 0.0V.
    3348
    DEC E
    Decrement E as DELAY #2.
    3349
    JUMP back to the prior instruction until E is 0.
    334B
    LD A,01H
    Load A with 01H to prepare to send 0.85 VOLTS to TAPE.
    334D
    OUT (0FFH),A
    Load Port FFH with A.

    NOTE: Port FFH is the cassette port. When outputting to FFH, Bits Zero and 1 set to: 00 is .85V, 01 is .46V, and 10 is 0.0V.
    334F
    RET
    RETURN.

    3350H - READ a BIT

    3350
    EI
    Enable Interrupts.
    3351
    LD C,00H
    Load C with 0.
    3353
    INC C
    Bump C.
    3354
    LD A,(3840H)
    Load A with the contents of 3840H so as to check for a BREAK.
    3357
    AND 04H
    Mask A with 4 (0000 0100).
    3359
    If the Masked A is 0, then LOOP back to 3353H.
    335B
    DI
    Disable Interrupts.
    335C
    Load HL with "BK" to set up to display "BK" over the "**".
    335F
    LD (3C3EH),HL
    Put the "BK" in HL onto the video screen at 3C3EH.
    3362
    JUMP to 4203H, which then jumps to 022EH, which is the BREAK VECTOR for cassette and RS-232.

    3365H - This is a Port E0H Masked Jump. If the MASKABLE INTERRUPT is xxxxxxx1, it jumps here. This is a Cassette Routine with E set to HIGH

    3365
    LD E,01H
    Load E with 01H (Binary: 0000 0001) to make the bit go high.
    3367
    Skip the next instruction.
    3369
    LD E,00H
    Load E with 0 to make the bit go low.
    336B
    LD A,06H
    Load A with 6.
    336D
    ADD A,C
    Add A (6) to C, so COUNT = COUNT + 6.
    336E
    LD C,A
    Put the count held in A back into C.
    336F
    IN A,(FFH)
    Poll FFH (to get the level) into A.

    NOTE: FFH is the Cassette Port.
    3371
    AND 01H
    Mask A with 1 (0000 0001) to keep only Bit 0.
    3373
    CP E
    Compare A with E (which was the set level).
    3374
    If A does NOT match E then skip the next 3 instructions.
    3376
    POP AF
    Restore AF from the STACK.
    3377
    POP AF
    Restore AF (the REMOTE CALLER'S ADDRESS) from the STACK.
    3378
    RET
    RETURN to the caller's caller.
    3379
    POP AF
    Restore AF from the STACK.
    337A
    EI
    Enable Interrupts.
    337B
    RET
    RETURN back top the loop.

    337CH - Check for a Data Error.

    337C
    LD A,C
    Load A with C (the count).
    337D
    CP 22H
    Compare A with 22H. Results:
    • If A=22H it sets the ZERO FLAG
    • If A<22H then the CARRY FLAG will be set
    • If A>=22H then the NO CARRY FLAG will be set
    337F
    RL D
    We need to put the data bit into D so we rotate D left one bit, with the contents of BIT 7 being put into the CARRY FLAG and the CARRY FLAG are put into BIT 0. This puts the read data into BIT 0 of D.
    3381
    CP 0FH
    Make sure it was not too quick by checking A against 0FH. This has the effect of checking A-0FH, so if A < 0FH then the CARRY will be set and if A >= 0FH then the NO CARRY will be set.
    3383
    If A < 0FH then it was too quick and we have a data error so JUMP forward a few instructions 3388H.
    3385
    CP 3EH
    Compare A against 3EH to make sure it was not too slow.
    3387
    RET C
    If it wasn't too slow, RETURN.
    3388
    LD A,44H
    It was too slow, so load A with a D.
    338A
    LD (3C3EH),A
    Put the "D" on the screen at video location 3C3EH.
    338D
    RET
    RETURN.

    *338EH - Model 4 Gen 1

    In the Model 4 this area contains part of the keyboard scan routine (338EH-3400H), the bootstrap routine (3401H-34CDH), the Non-Maskable Interrupt handler routine (34CEH-34O9H), more of the keyboard scan routine (37DAH-34FCH), a new screen print routine used when the control and asterisk keys are pressed (34FDH-351EH), and ten NOPs (zero bytes at 351FH through 3528H).

    *338E
    Set Register Pair HL to 41F4H which is the storage location used to store the "image" (current status) of the CAPS, CTRL, and function keys (F1, F2, and F3).
    *3391
    LD A,(3880H)
    Load A with the value held at 3880H (which are the SHIFT KEYS)
    *3394
    AND 7CH
    MASK the value held in the SHIFT KEY RAM location against 0111 1100 to keep only bits 2, 3, 4, 5, and 6 live
    *3396
    OR A
    OR A against itself to reset the flags
    *3397
    If the Z FLAG is set, JUMP to 33CAH to re-scan the keybaord
    *3399
    LD E,A
    Put the masked A into E.
    *339A
    XOR (HL)
    Toggle against the old image.
    *339B
    LD (HL),E
    Save the new image into (HL).
    *339C
    AND E
    Mask Register E against Rgister A.
    *339D
    If NOT ZERO then RIGHT SHIFT PRESSED and we JUMP to 33DFH.
    *339F
    Load BC with 05C4H to set up a 1/50 second delay for de-bounce.
    *33A2
    GOSUB to 0060H which jumps to the delay routine at 01FBH (which uses BC as a loop counter). It RETs when done so it doesn't come back here.
    *33A5
    LD A,(3880H)
    Load A with the value held at 3880H (which are the SHIFT KEYS)
    *33A8
    AND 7CH
    MASK the value held in the SHIFT KEY RAM location against 0111 1100 to keep only bits 2, 3, 4, 5, and 6 live
    *33AA
    CP E
    Compare A with E.
    *3374
    If The MASKED Shift Key Value does not match Register E then JUMP to 37DFH.
    *33AE
    RLA
    Rotate A left one bit, with the contents of BIT 7 being put into the CARRY FLAG and the CARRY FLAG are put into BIT 0. This puts the read data into the CARRY FLAG.
    *33AF
    RLA
    Rotate A left one bit, with the contents of BIT 7 being put into the CARRY FLAG and the CARRY FLAG are put into BIT 0. This puts the read data into the CARRY FLAG.
    *33B0
    We have a function key, but which one. If the NC FLAG is set, it isn't the F3, so JUMP to 33B6 to test the F2 Key
    *33B2
    LD A,(41F3H)
    Load Register A with the value of the character to be returned when the F3key is pressed
    *33B5
    RET
    RETurn to Caller

    *33B6 - Model 4 Gen 1 - KEYBOARD Routine - Check and Process the F2 Key or Jump Away.

    *33B6
    RLA
    Rotate A left one bit, with the contents of BIT 7 being put into the CARRY FLAG and the CARRY FLAG are put into BIT 0. This puts the read data into the CARRY FLAG.
    *33B7
    If the NC FLAG is set, it isn't the F2, so JUMP to 33BDH to test the F1 Key
    *33B9
    LD A,(41ECH)
    Load Register A with the value of the character to be returned when the F2key is pressed
    *33BC
    RET
    RETurn to Caller

    *33B6 - Model 4 Gen 1 - KEYBOARD Routine - Check and Process the F1 Key or Jump Away.

    *33BD
    RLA
    Rotate A left one bit, with the contents of BIT 7 being put into the CARRY FLAG and the CARRY FLAG are put into BIT 0. This puts the read data into the CARRY FLAG.
    *33BE
    If the NC FLAG is set, it isn't the F1, so JUMP to 33C4 to keep checking special keys
    *33C0
    LD A,(41EBH)
    Load Register A with the value of the character to be returned when the F1key is pressed
    *33C3
    RET
    RETurn to Caller

    *33C4 - Model 4 Gen 1 - KEYBOARD Routine - Part of the Keyboard Scan Routine. Keep Checking Special Keys.

    *33C4
    RLA
    Rotate A left one bit, with the contents of BIT 7 being put into the CARRY FLAG and the CARRY FLAG are put into BIT 0. This puts the read data into the CARRY FLAG.
    *33C5
    If the CARRY FLAG is high, then JUMP to 37D8 to toggle the CAPS LOCK
    *33C8
    JUMP to 33DAH to deal with the CONTROL KEY flag

    *33CA - Model 4 Gen 1 - KEYBOARD Routine - Part of the Keyboard Scan Routine.

    *33CA
    LD (HL),A
    Save the character held in Register A into the memory location pointed to by Register Pair HL
    33CB
    LD A,FFH
    Load A with FF to set up for a FLAG = 0FFH = NO CONTROL.
    33CD
    Load HL with 3840H to start a check for a DOWN ARROW.
    33D0
    BIT 4,(HL)
    Test BIT 4 of (HL) to check for a DOWN ARROW.
    33D2
    JUMP to 33DCH if the a DOWN ARROWwas NOT pressed.
    33D4
    SLA L
    Next we need to check for a a LEFT SHIFTso shift L left.
    33D6
    BIT 00H,(HL)
    Test BIT 0 of (HL) to check for a LEFT SHIFT.
    33D8
    JUMP to 33DCH if the a LEFT SHIFTwas NOT pressed.
    33DA
    LD A,1FH
    Load A with 1F to set up for FLAG = CONTROL KEY.
    33DC
    LD (4224H),A
    Save the CONTROL FLAG into (4224H).

    NOTE: 4224H Holds the CONTROL KEY flag.
    *33DF
    GOSUB to 3739H to start parsing the keyboard from row 0
    *33E2
    RET NC
    If that routine exited with NC FLAG set, RETurn
    *33E3
    GOSUB to 34DAH to deal with a SHIFT key
    33E6
    CP 1AH
    Check A against 1AH to see if we have a SHIFT DOWN ARROW.
    33E8
    If we have a SHIFT DOWN ARROWthen JUMP to 37DFH.
    *33EB
    OR A
    Set the flags based on Register A
    *33EC
    If Register A = 0 the JUMP to 37D8 to toggle the CAPS LOCK
    *33EF
    Set Register Pair HL to 4224H, which is the CONTROL KEY flag.
    *33F2
    BIT 7,(HL)
    Test Bit 7 of the CONTROL KEY flag in RAM.
    *33F4
    If Bit 7 of the CONTROL KEY flag is OFF, the JUMP to 33FCH
    *33F6
    CP 2AH
    Check A against a *.
    *33F8
    If A is a *then JUMP to 313BH whcih is a new screen print routine for CTRL-*.
    *33FB
    AND (HL)
    Prepare to check for a BREAKby masking A against the memory contents of HL ...
    *33FC
    CP 01H
    ... and COMPARING it to 01H.
    *33FE
    RET NZ
    If the result of the compare is NOT zero, then RETURN.
    *33FF
    RST 28H
    If we are here, then a BREAKwas hit, so call RST 28H to handle the BREAK.
    *3400
    RET
    RETURN to Caller

    *3401 - Model 4 Gen 1 - This is the BOOTSTRAP. Clears ports, checks for a BREAKkey and a Floppy Controller.

    3401
    IM 1
    Set the INTERRUPT MODE to 1.
    3403
    Load the STACK POINTER with 407DH.
    *3406
    LD B,0FH
    Let Register B = 0FH
    *3408
    LD C,88H
    Let Register C = the CRT Controller Control Register Port
    *340A
    OUT (C),B
    Set the CRT Controller Control Register Port to 0FH
    *340C
    OUT (89H),A
    Send Register A to the CRT Controller Data Register
    *340E
    Loop back two instructions until all 16 data registers have been set
    3410
    OUT (0E4H),A
    Output A to Port E4H.

    NOTE: Port E4H is the non-maskable interrupt latch. This is to clear the non-maskable interrupt status.
    3412
    OR 20H
    OR 20H (0010 0000) against A to turn on Bit 5.
    3414
    OUT (0ECH),A
    Output the modified A to Port ECH.

    NOTE: Port ECH is the control port. In this case, this is turning on bit 5 which ENABLEs the Video Waits. This bit is mirrored into a chip called a latch which has a wire running to both the Z-80 and the video circuity that mediates access to video RAM.
    3416
    LD A,81H
    Load A with 81H (Decimal 129, Binary 10000001).
    3418
    OUT (0F4H),A
    Output A to Port F4H to select drive 0 and double-density.

    NOTE: Port F4H is the Disk Drive and Disk Density Select. Bits 0-3 are the drive select of 0-3 and Bit 7 is 0 for single density and 1 for double density.
    341A
    LD A,D0H
    Load A with D0H (Decimal: 208, Binary: 1101 0000).
    341C
    OUT (0F0H),A
    Output "D0H" to Port F0H.

    NOTE: Port F0H is the FDC Status Register. Output Commands:
    • 00H - restore
    • 80H - read sector
    • A0H - write normal sector
    • A1H - write read protect sector
    • C0H - read address
    • D0H - reset; puts FDC in mode 1
    • E0H - read track
    • F0H - write track
    *341E
    PUSH BC
    Undertake a short delay of PUSHING BC, POPPING BC, and NOPing
    *341F
    POP BC
    *3420
    NOP
    3421
    LD A,04H
    Load A with a 4 (Binary: 0000 0100).
    3423
    OUT (0E0H),A
    Output 4 to E0H to JUMP to 4046H to Set the Maskable Interrupt.

    NOTE: Port E0H is the maskable interrupt latch, which directs jumps.
    Jump Table:
    • xxxxxxx1 jumps to 3365H
    • xxxxxx1x jumps to 3369H
    • xxxxx1xx jumps to 4046H
    • xxxx1xxx jumps to 403DH
    • xxx1xxxx jumps to 4206H
    • xx1xxxxx jumps to 4209H
    • x1xxxxxx jumps to 44040H
    • 1xxxxxxx jumps to 44043H
    3425
    LD A,0BH
    Load A with 0BH (Binary: 00001011).
    3427
    OUT (0F0H),A
    Output 0BH to Port F0H to to RESTORE TO TRACK.

    NOTE: Port F0H is the FDC Status Register. A B0H (Decimal: 00001011) sent to Port F0H is the command Restore (0000), Load Head at Beginning (1), No Verify (0), 30ms step rate (11).
    3429
    Next we need to initialize some ports via a LDIR. The next 4 commands will move the 76 (4C) bytes from 36AAH to 4000H.
    3432
    LDIR
    3434
    Next we need to initialize more ports via a LDIR. The next 4 commands will move the 64 (40) bytes from 36F9H to 41E5H.
    343D
    LDIR
    343F
    GOSUB to 01C9H to clear the screen.
    3442
    GOSUB to 028DH to check for a BREAKkey.
    3445
    If we have a BREAKkey then jump to Non-Disk BASIC at 3105H.
    3448
    IN A,(F0H)
    If we did not get a BREAK, then we next need to verify the disk controller by polling the Floppy Disk Controller Status Register at Port F0H and put the result into Register A. Results:
    • 1 = Busy
    • 2 = Index/DRQ
    • 4 = Track 0/Lost Data
    • 8 = CRC Error
    • 16 = Seek Error/Record Not Found
    • 32 = Record Type/Write Fault/Head Loaded
    • 64 = Write Protected
    • 128 = Not Ready
      344A
      INC A
      Bump the FDC Status by 1 (which would turn a FFH (128) into a 00H), making a Z flag mean "Disk Controller Not Ready".
      344B
      If the FDC Stats + 1 is zero, then we have no disk controller, so jump to Non-Disk BASIC at 3105H.
      344E
      If we are here, then we have a floppy controller, so set up for a loop of 65,535 times.
      3451
      DEC BC
      Decrease BC by 1.
      3452
      LD A,81H
      Set A to 81H (Decimal: 10000001).
      3454
      OUT (0F4H),A
      Send 1000 0001 to Port F4H to set double density.

      NOTE: Port F4H is the Disk Drive and Disk Density Select. Bits 0-3 are the drive select of 0-3 and Bit 7 is 0 for single density and 1 for double density.
      3456
      LD A,B
      OR C
      To test a BC against 0, LOAD A with B and then OR C. A will be 0 only if both B and C were zero.
      3458
      If we have finished the 65,535 loop then jump to Non-Disk BASIC at 3105H.
      345B
      IN A,(F0H)
      We are still in the loop, so poll the FLOPPPY STATUS REGISTER at Port F0H into A. Results:
      • xxxx xxx1 = Busy
      • xxxx xx1x = Index/DRQ
      • xxxx x1xx = Track 0/Lost Data
      • xxxx 1xxx = CRC Error
      • xxx1 xxxx = Seek Error/Record Not Found
      • xx1x xxxx = Record Type/Write Fault/Head Loaded
      • x1xx xxxx = Write Protected
      • 1xxx xxxx = Not Ready
      345D
      BIT 2,A
      Test Bit 2 of A (the Floppy Status). If it is 0 then we haven't made it to track 0, otherwise we have.
      345F
      Still haven't found track 0, so jump back to 3451H to continue the drive polling loop of 65,535 times.

      *3461 - Model 4 Gen 1 - Warm Boot Routine.

      3461
      LD E,05H
      Set up for a loop of 5 using Register E.
      3463
      Set up for a loop of 65,536 using Register Pair BC.
      3466
      IN A,(F0H)
      Poll the FLOPPPY STATUS REGISTER at Port F0H into A. Results:
      • xxxx xxx1 = Busy
      • xxxx xx1x = Index/DRQ
      • xxxx x1xx = Track 0/Lost Data
      • xxxx 1xxx = CRC Error
      • xxx1 xxxx = Seek Error/Record Not Found
      • xx1x xxxx = Record Type/Write Fault/Head Loaded
      • x1xx xxxx = Write Protected
      • 1xxx xxxx = Not Ready
      3468
      BIT 01H,A
      Check A to see if bit 1 (meaning "Drive Busy") is set.
      346A
      If bit 1 is set, jump to 347DH.
      346C
      DEC BC
      If bit 1 is not set (meaning, the drive is NOT busy), reduce the counter (BC) by 1.
      346D
      LD A,81H
      Load A with 81H (Decimal: 129, Binary: 1000 0001).
      346F
      OUT (0F4H),A
      Output A to Port F4H to select drive 0, double density.

      NOTE: Port F4H is the Disk Drive and Disk Density Select. Bits 0-3 are the drive select of 0-3 and Bit 7 is 0 for single density and 1 for double density.
      3471
      LD A,B
      OR C
      To test a BC against 0, LOAD A with B and then OR C. A will be 0 only if both B and C were zero.
      3473
      If BC is not 0, then loop back to 3466H.
      3475
      If we are here, then we received no BIT 1 of FLOPPY STATUS REGISTER despite 65,536 tries, so we need to deal with that by printing "DISKETTE?" on the screen. Point HL to the "DISKETTE?" message at 0279H.
      3478
      GOSUB to 021BH. Note; 021BH will display the character at (HL) until a 03H is found.
      347B
      If we are here, there is no disk in drive and we displayed "DISKETTE?" so loop back to 3461H to poll for a disk all over again.
      347D
      DEC E
      If we are here, it found a diskette so next we need to find the index mark. First decrement E by 1.
      347E
      Jump back to the top of this WARM BOOT ROUTINE to check the FDC up to 5 times.
      3463H
      LD A,81H
      Load A with 81H (Decimal: 129, Binary: 1000 0001).
      3482
      OUT (0F4H),A
      Output A to Port F4H to select drive 0, double density.

      NOTE: Port F4H is the Disk Drive and Disk Density Select. Bits 0-3 are the drive select of 0-3 and Bit 7 is 0 for single density and 1 for double density.
      3484
      We need to set the Non-Maskable Interrupt Vector to be a JUMP to 34B8H so we do the next 4 steps. Sets 34B8H as the jump point for a NMI hit, which is what occurs once a sector has been completely read.
      3487
      LD (404AH),HL
      348A
      LD A,C3H
      Load A with C3H (Decimal: 195, Binary 1100 0011).
      348C
      LD (4049H),A
      Put the C3H into 4049H.

      NOTE: 4049H is the Non-Maskable Interrupt Vector.
      348F
      LD A,80H
      Load A with 80H (Decimal: 128, Binary 1000 0000).
      3491
      OUT (0E4H),A
      Output "1000 0000" to Port E4H.

      NOTE: Port E4H is the non-maskable interrupt latch.
      3493
      Set BC to F3H.

      NOTE: Port F3H is the Floppy Disk Controller Data Register.
      3496
      Set HL to 4300H, which is where the data is going to go.
      3499
      LD A,01H
      Prepare to read Sector 1 by loading A with a 1.
      349B
      OUT (0F2H),A
      Output 1 to Port F2H.

      NOTE: Port F2H is the Floppy Disk Controller Track Register.
      349D
      LD A,80H
      Prepare to read a single sector by loading A with 80H (Decimal: 128, Binary: 1000 0000).
      349F
      OUT (0F0H),A
      Output "1000 0000" to Port F0H.

      NOTE: Port F0H is the FDC Status Register. Outputting 1000 0000 is issuing the command READ SECTOR (100), SINGLE RECORD (0), SIDE 0 (0), NO 15MS DELAY (0), DISABLE SIDE COMPARE (0).
      34A1
      GOSUB to 37E1H to delay 3 instruction cycles, arguably the time it will take for the FDC to respond to the port command.
      34A4
      IN A,(F0H)
      Poll the FLOPPPY STATUS REGISTER at Port F0H into A. Results:
      • xxxx xxx1 = Busy
      • xxxx xx1x = Index/DRQ
      • xxxx x1xx = Track 0/Lost Data
      • xxxx 1xxx = CRC Error
      • xxx1 xxxx = Seek Error/Record Not Found
      • xx1x xxxx = Record Type/Write Fault/Head Loaded
      • x1xx xxxx = Write Protected
      • 1xxx xxxx = Not Ready
      34A6
      AND 02H
      Mask A against (0000 0010) which will leave only bit 1 active. This will test for the index mark, and Z will indicate that the index mark has NOT been detected, and NZ will indicate the index mark has been detected.
      34A8
      Loop back 2 instructions to keep polling F0H until the index is found.
      34AB
      INI
      Input the data byte.
      34AD
      LD A,81H
      Load A with 81H (Decimal: 129, Binary: 1000 0001) to select disk 0.
      34AF
      OR 40H
      OR A with 40H (Binary: 0100 0000) to set A to double-density.
      34B1
      OUT (0F4H),A
      Output A to Port F4H to select drive 0, double density.

      NOTE: Port F4H is the Disk Drive and Disk Density Select. Bits 0-3 are the drive select of 0-3 and Bit 7 is 0 for single density and 1 for double density.
      34B3
      INI
      Input the data byte.
      34B5
      Loop back to 34ADH to set drive and density.

      NOTE: This appears to be an infinite loop in the code, but it isn't in actuality. Once the disk controller has loaded an entire sector, it will trigger a non-maskable interrupt and will exit to 3502H. That was set up at 34CE.

      *34B8H - Model 4 Gen 1 - Non-Maskable Interrupt Jump Point to verify the disk sector read wasn't in error.

      34B8
      XOR A
      Clear A and all flags.
      34B9
      OUT (0E4H),A
      Send a 0 to Port E4H.

      NOTE: Port E4H is the non-maskable interrupt latch. This is to clear the non-maskable interrupt status.
      34BB
      Now that the NMI jumped here, we need to set a new NMI jump, this time to 45EDH.
      34BE
      LD (4049H),HL
      Load "45EDH" into 4049H.

      NOTE: 4049H is the Non-Maskable Interrupt Vector.
      34C1
      GOSUB to 37E1H to delay 3 instruction cycles, arguably the time it will take for the FDC to respond to the port command.
      34C4
      IN A,(F0H)
      Poll the FLOPPPY STATUS REGISTER at Port F0H into A. Results:
      • xxxx xxx1 = Busy
      • xxxx xx1x = Index/DRQ
      • xxxx x1xx = Track 0/Lost Data
      • xxxx 1xxx = CRC Error
      • xxx1 xxxx = Seek Error/Record Not Found
      • xx1x xxxx = Record Type/Write Fault/Head Loaded
      • x1xx xxxx = Write Protected
      • 1xxx xxxx = Not Ready
      34C6
      POP HL
      Clean up the Stack.
      34C7
      AND 1CH
      Mask A with 1CH (Binary: 0001 1100). This keep only bit 3 (Track 0/Lost Data), bit 4 (CRC Error) and bit 5 (Seek Error/Record Not Found). If NONE of these are present, then Z will be set.
      34C9
      If those 3 errors aren't generated, then JUMP to 4300H.
      34CC
      If we are here at least one of those errors were present, so JUMP back to 3480H to try to read the sector again.

      *34CEH - Model 4 Gen 1 - NMI handler. On entry, A holds Bit 5 of the Non-Maskable Interrupt Latch at port 0E4H. 303DH jumped here.

      34CE
      If Bit 5 of the Non-Maskable Interrupt Latch was NOT zero (meaning, disk interrupt), then jump to 4049H which, in TRSDOS, is a jump to 42BDH which, at least in TRSDOS, in a RETurn.
      34D1
      IN A,(E4H)
      Poll Port E4H into A.

      NOTE: Port E4H is the Non-Maskable Interrupt Latch.
      34D3
      BIT 5,A
      Test Bit 5 of Port E4 against A.

      NOTE: Port E4H is the Non-Maskable Interrupt Latch.
      34D5
      Loop back 2 instructions until it is set (i.e., not a zero).
      34D7
      Jump to 0000H to restart the computer.

      *34DA - Model 4 Gen 1 - Part of the Keyboard routine.

      *34DA
      LD A,(3880H)
      Put the contents of memory location 3880H into A to GET SHIFT(S).
      *34DD
      AND 03H
      Mask the SHIFT Register against 03H (Binary: 0000 0011) to keep only Bits 0 and 1 to check for shifts.
      *34DF
      If the masked A is 0, then we have no shifts, and skip the next instruction (to 34E3H).
      *34E1
      SET 6,D
      Set BIT 6 of D to offset D for shifts.
      *34E3
      LD A,(4019H)
      Load A with the contents of memory location 4019H to check for CAPS LOCK.

      NOTE: 4019H is the CAPS LOCK TOGGLE.
      *34E6
      OR A
      Set the flags.
      *34E7
      If the ZERO flag is set then there is NO CAPS LOCK so skip the next intruction.
      *34E9
      SET 7,D
      Set BIT 7 of D to offset D for CAPS LOCK.

      NOTE: BIT 7 is the difference between UPPER CASE and LOWER CASE versions of the same letter.
      *34EB
      LD A,(3880H)
      Put the contents of memory location 3880H into A to GET SHIFT(S).
      *34EE
      AND 03H
      Mask the SHIFT Register against 03H (Binary: 0000 0011) to keep only Bits 0 and 1 to check for shifts.
      *34F0
      If the masked A is 0, then we have no shifts, and skip the next instruction (to 34F4H).
      *34F2
      RES 7,D
      RESET the bit 7 of Register D to offset D for CAPS LOCK.

      NOTE: BIT 7 is the difference between UPPER CASE and LOWER CASE versions of the same letter.
      34F4
      Load HL with 3045H (the KEYBOARD TABLES).
      34F7
      LD E,D
      We need DE to be the OFFSET, so load E with D and ...
      34F8
      LD D,00H
      ... load D with 00.
      34FA
      ADD HL,DE
      Add DE (the offset over the keyboard table) to HL (the keyboard table).
      34FB
      LD A,(HL)
      Get the character pointed to by (HL) and put it into A.
      *34FC
      RET
      RETurn to Caller

      *34FD - Model 4 Gen 1 - Print Screen Routine - "$PRSCN"
      This routine copies all 1024 characters from the screen to the printer. If the printer is unavailable, this routine waits until the printer becomes available. If BREAKis pressed, this routine returns to the caller.

      *34FD
      "PRSCN"
      Load HL with the memory location for the beginning of the video RAM.
      Difference between M1 and M3: The routine to print the contents of the screen on the line printer is located from 01D9H to 01F4H on the Model III. On the Model I, 01D9H - 01F7H contains the routine to output one bit to the cassette.
      *3500
      LD A,(HL)
      Put the character at the screen location stored in HL into A.
      *3501
      RLCA
      Rotate the contents of A left one bit position. The contents of bit 7 are copied to the carry flag and bit 0.
      *3502
      If the high bit was off, JUMP to 350BH
      *3504
      RLCA
      Rotate the contents of A left one bit position. The contents of bit 7 are copied to the carry flag and bit 0.
      *3505
      If the CARRY FLAG is set, we have a non-graphic characters, so skip the next instructions.
      *3507
      LD A,2EH
      Overwrite the current character held in Register A with a ., so that all graphic characters are printed as .'s.
      *3509
      Skip the next instruction (whch changes the character to a ".")
      *350B
      LD A,(HL)
      Change the (forbidden) character on the screen into a "."
      *350C
      Call the PRINT CHARACTER routine at 003BH (which sends the character in the A register to the printer).
      *350F
      INC HL
      Bump HL to the next character on the screen.
      *3510
      BIT 6,H
      Check the 6th Bit in H to see if we are at the end of the line (meaning that H is now 64; 1 character beyond the 63 maximum per lime).
      *3512
      If we are at 64, then JUMP to 0214H for a new line.
      *3515
      LD A,L
      Prepare to test of end of line by loading Register A with Register L.
      *3516
      AND 03FH
      AND the contents of A with 3FH (Binary: 00111111) to turn off Bits 7 and 6, making the maximum number A can be 3FH (Decimal: 63).
      *3518
      If any of the bits 5-0 are still "1", then we are not at the end of the line. With this, loop back to 3500H for the next character.
      *351A
      GOSUB to 0214H for a new line.
      Difference between M1 and M3: 01F0H contains CALL 0221H instruction on Model I, and CALL 0214H instruction on Model III.
      *351D
      Loop back to 3500H for the next character.
      Difference between M1 and M3: Contains LD B,5CH instruction on Model I, JR 01DCH instruction on Model III.

      *351F - Model 4 Gen 1 - Unused Code

      *351F
      NOP
      *3520
      NOP
      *3521
      NOP
      *3522
      NOP
      *3523
      NOP
      *3524
      NOP
      *3525
      NOP
      *3526
      NOP
      *3527
      NOP
      *3528
      NOP

      *338EH - Model 4 Gen 2 - Jump Point for Keyboard Input.

      *338E
      LD BC,3801H010138
      Load BC with 3801H (KEYBOARD ROW 0).
      *3391
      LD HL,4036H213640
      Load HL with 4036H (BUFFER ROW 0).
      *3394
      LD D,00H1600
      Load D with 0 (so D = ROW 0).
      *3396
      LD A,(BC)0A
      oad A with the contents held in (BC) to check the keyboard row.
      *3397
      LD E,A5F
      Load E with the contents held in (BC) to check the keyboard row.
      *3398
      XOR (HL)AE
      XOR (HL) to set changed bits.
      *3399
      LD (HL),E73
      Save the scan back into (HL).
      *339A
      AND EA3
      Mask A with E (to mask the released keys).
      *339B
      JUMP to 33BCH if any keys are pressed.

      Go to the next row

      *339D
      INC D14
      Bump D so that D holds the NEXT row number.
      *339E
      INC HL23
      Bump HL so that HL holds the NEXT buffer location.
      *339F
      RLC CCB01
      We need C to point to the next row of keys, so we rotate C left one bit, copying BIT 7 to the CARRY FLAG and the CARRY FLAG to BIT 0.
      *33A1
      If the ROTATE caused the P FLAG to trigger (by having the number of 1 bits being even), JUMP back to 3396H to check the keyboard row
      *33A4
      LD A,(41FDH)3AFD41
      If the ZERO FLAG is not set, then load A with the memory contents of 41FDH.
      NOTE: 41FDH is the saved position in the keyboard scan.
      *33A7
      LD L,A6F
      Load L with the contents of Register A.
      *33A8
      LD A,(41FEH)3AFE41
      Load A with the memory contents of 41FEH.
      NOTE: 41FEH is the saved IMAGE at the saved position in the keyboard scan data.
      *33AB
      AND (HL)A6
      MASK A against (HL) to see if the previous keys are still pressed.
      *33AC
      JUMP to 33DAH if the previous keys are still pressed.
      *33AF
      SBC HL,HLED62
      Zero HL by subtracting HL from HL.
      *33B1
      LD (4201H),HL220142
      Load the memory location at 4201H with HL to clear the repeat counter.
      NOTE: 4201H is the REPEAT DELAY COUNTER.

      Set the Keyboard Repeat Delay Count to 1500.

      *33B4
      Load HL with 05DCH (=1500)
      *33B7
      LD (41FFH),HL22FF41
      Load the memory location at 41FFH with 05DCH.
      NOTE: 41FFH holds the keyboard scan repeat delay count.
      *33BA
      XOR AAF
      Clear A and all flags.
      *33BB
      RETC9
      RETurn to Caller

      *33BC - Model 4 Gen 2 - Keyboard Routine - If the same keys are still pressed then we need to deal with debounce.

      *33BC
      LD E,A5F
      Load E with A.
      *33BD
      PUSH BCC5
      Save BC to the STACK.
      *33BE
      LD BC,05C4H01 C4 05
      Load BC with 05C4H to set up a 1/50 second delay for de-bounce.
      *33C1
      CALL 0060HCD 60 00
      GOSUB to 0060H which jumps to the delay routine at 01FBH (which uses BC as a loop counter). It RETs when done so it doesn't come back here.
      *33C4
      POP BCC1
      Restore BC from the STACK.
      *33C5
      LD A,(BC)0A
      Load A with the memory contents pointed to by BC to re-check the keyboard.
      *33C6
      AND EA3
      Compare A against E to check the pattern.
      *33C7
      RET ZC8
      If not the same pattern then RETURN.
      *33C8
      LD (41FEH),A32 FE 41
      If it is the same pattern then save A into (41FEH).
      NOTE: 41FE is the SAVED IMAGE AT POSITION.
      *33CB
      LD A,L7D
      Load A with L (the scan position).
      *33CC
      LD (41FDH),A32 FD 41
      Save A into (41FDH).
      NOTE: 41FDH is the SAVED POSITION IN SCAN.
      *33CF
      LD A,D7A
      Load A with D (8 * ROW #).
      *33D0
      RLA17
      Rotate A left one bit, with the contents of BIT 7 being put into the CARRY FLAG and the CARRY FLAG are put into BIT 0. This puts the read data into the CARRY FLAG.
      *33D1
      RLA17
      Rotate A left one bit, with the contents of BIT 7 being put into the CARRY FLAG and the CARRY FLAG are put into BIT 0. This puts the read data into the CARRY FLAG.
      *33D2
      RLA17
      Rotate A left one bit, with the contents of BIT 7 being put into the CARRY FLAG and the CARRY FLAG are put into BIT 0. This puts the read data into the CARRY FLAG.
      *33D3
      LD D,A57
      Copy Register A into Register D
      *33D4
      LD A,E7B
      Copy Register E into Register A
      *33D5
      RRCA0F
      Rotate A right one bit, with the contents of BIT 0 being put into BOTH the CARRY FLAG and BIT 7. D = 8* ROW # + KEY #.
      *33D6
      RET CD8
      If the contents of BIT 0 of A was SET, RETurn
      *33D7
      INC D14
      Bump D so that D holds the NEXT row number.
      *33D8
      Loop back to 33D5H (to keep rotating A right one bit and bumping D).

      *33DA - Model 4 Gen 2 - Keyboard Routine.

      *33DA
      PUSH HLE5
      Save HL to the top of the STACK.
      *33DB
      LD HL,(4201H)2A0142
      Load HL with the repeat delay counter.
      NOTE: 4201H is the REPEAT DELAY COUNTER.
      *33DE
      INC HL23
      Bump HL.
      *33DF
      LD (4201H),HL220142
      Load the memory location at 4201H with HL to clear the repeat counter.
      NOTE: 4201H is the REPEAT DELAY COUNTER.
      *33E2
      LD DE,(41FFH)ED5BFF41
      Load DE with the byte stored at 41FFH.
      NOTE: 41FFH is the REPEAT DELAY COUNT.
      *33E6
      SBC HL,DEED52
      Subtract with CARRY DE from HL.
      *33E8
      POP DED1
      Restore old HL (which is what is in the stack) into DE.
      *33E9
      If we haven't scanned enough then JUMP to 37D3H to clear flags and RETURN.
      *33EC
      XOR AAF
      Clear A and all flags.
      *33ED
      LD (DE),A12
      Put a 0 into the memory location pointed to (DE) to let the key be re-read.
      *33EE
      LD (4201H),HL220142
      Load the memory location at 4201H with HL to clear the repeat counter.
      NOTE: 4201H is the REPEAT DELAY COUNTER.
      *33F1
      LD L,96H2E96
      Load L with 96H to set a fast repeat count.
      *33F3
      LD (41FFH),HL22FF41
      Save HL into the memory location at 41FFH.
      NOTE: 41FFH is the REPEAT DELAY COUNT.
      *33F6
      JP 3142HC34231
      JUMP to 3142H to re-scan the keyboard.

      *33F9 - Model 4 Gen 2 - Enter NON-Disk BASIC.

      *33F9
      CALL 33FFHCD FF 33
      GOSUB to 33FFH to Process the CASS? Question
      *33FC
      JP 0075HC37500
      Continue initializing by JUMPing to 0075H

      *33FF - Model 4 Gen 2 Routine - Initialization - Process the CASS? Question

      *33FF
      EIFB
      Enable Interrupts.
      *3400
      CALL 3421HCD 21 34
      GOSUB to 3421H which loads A with a carrage return, and jumps to 0033H to display it.
      *3403
      LD HL,05D1H21 D1 05
      Load HL with the address of the "CASS?" message.
      *3406
      CALL 021BHCD 1B 02
      GOSUB to 021BH.
      NOTE: 021BH will display the character at (HL) until a 03H is found.
      *3409
      CALL 0049HCD 49 00
      GOSUB to 0049H.
      NOTE: 0049H is the $KBWAIT routine which scans the keyboard and returns with the key pressed, if any, in register A.
      *340C
      CP 0DHFE 0D
      Check to see if Register A is holding a ENTER
      *340E
      If the ENTER is hit, then JUMP to 341EH to default to HIGH SPEED.
      *3410
      PUSH AFF5
      Save AF to the STACK.
      NOTE: Register A currently holds the character pressed in response to the "CASS?" message.
      *3411
      CALL 0033HCD 33 00
      GOSUB to 0033H pt display the character held in Register A at the current cursor position.
      *3414
      POP AFF1
      Restore the answer to CASS? from the STACK into Register A.
      *3415
      CP 48HFE 48
      Compare the answer to the CASS? Prompt held in Register A against with 48H (ASCII: H).
      *3417
      If the answer to CASS? was H then JUMP to 341EH to select HIGH SPEED.
      *3419
      CP 4CHFE 4C
      Compare the answer to the CASS? Prompt held in Register A against with 4CH (ASCII: L).
      *341B
      If the answer to CASS? was NOT L then JUMP to 33FFH to get another response.

      Set the flag for LOW SPEED CASSETTE

      *341D
      XOR AAF
      Set A to 0.
      *341E
      LD (4211H),A32 11 42
      Save Register A (which is either "H" or 0 at this point) into (4211H).
      NOTE: 4211H holds the CASSETTE BAUD RATE SELECT as:
      • 0: 500 Baud
      • Anything Else: 1500 baud
      *3421
      LD A,0DH3E 0D
      Put a CARRIAGE RETURN into Register A
      *3423
      JP 0033HC3 33 00
      JUMP to 0033H to put the character held in Register A at the current cursor position.

      *3426 - Model 4 Gen 2 Routine - This is the BOOTSTRAP. Turns on Interrupts, sets the stack, sets the CRT Controller, sets the FDC.

      *3426
      IM 1ED56
      Set the INTERRUPT MODE to 1.
      *3428
      LD SP,407DH317D40
      Load the STACK POINTER with 407DH.
      *342B
      LD B,0FH060F
      Let Register B = 0FH
      *342D
      LD C,88H0E88
      Let Register C = the CRT Controller Control Register Port
      *342F
      OUT (C),BED41
      Set the CRT Controller Control Register Port to the contents of B (which are decreasing as the loop progresses)
      *3431
      OUT (89H),AD389
      Send Register A to the CRT Controller Data Register
      *3433
      Loop back two instructions until all 16 data registers have been set
      *3435
      OUT (E4H),AD3E4
      Clear the Non-Maskable Interrupt Latch by sending the contents of Register A to Port E4H
      *3437
      OR 20HF620
      OR 20H (0010 0000) against A to turn on Bit 5.
      *3439
      OUT (ECH),AD3EC
      Output the modified A to Port ECH.
      NOTE: Port ECH is the control port. In this case, this is turning on bit 5 which ENABLEs the Video Waits. This bit is mirrored into a chip called a latch which has a wire running to both the Z-80 and the video circuity that mediates access to video RAM.
      *343B
      LD A,81H3E81
      Load A with 81H (Decimal 129, Binary 10000001).
      *343D
      OUT (F4H),AD3F4
      Output A to Port F4H to select drive 0 and double-density.
      NOTE: Port F4H is the Disk Drive and Disk Density Select. Bits 0-3 are the drive select of 0-3 and Bit 7 is 0 for single density and 1 for double density.
      *343F
      LD A,D0H3ED0
      Load A with D0H (Decimal: 208, Binary: 1101 0000).
      *3441
      OUT (F0H),AD3F0
      Output "D0H" to the FDC Status Register at Port F0H. This resets the FDC and puts it in mode 1
      *3443
      PUSH BCC5
      Undertake a short delay of PUSHING BC, POPPING BC, and NOPing
      *3444
      POP BCC1
      Undertake a short delay of PUSHING BC, POPPING BC, and NOPing
      *3445
      NOP00
      Undertake a short delay of PUSHING BC, POPPING BC, and NOPing
      *3446
      LD A,04H3E04
      Load A with a 4 (Binary: 0000 0100).
      *3448
      OUT (E0H),AD3E0
      Output 4 to E0H to JUMP to 4046H to Set the Maskable Interrupt.
      NOTE: Port E0H is the maskable interrupt latch, which directs jumps. In the case of 0100, to 4046H.
      *344A
      LD A,0BH3E0B
      Load A with 0BH (Binary: 00001011).
      *344C
      OUT (F0H),AD3F0
      Output 0BH to the FDC Status Register at Port F0H to to RESTORE TO TRACK. A B0H (Decimal: 00001011) sent to Port F0H is the command Restore (0000), Load Head at Beginning (1), No Verify (0), 30ms step rate (11).
      *344E
      LD HL,36AAH21AA36
      Next we need to initialize some ports via a LDIR. The next 4 commands will move the 76 (4C) bytes from 36AAH to 4000H.
      *3451
      LD DE,4000H110040
      Set the LDIR Destination to 4000H
      *3454
      LD BC,004CH014C00
      Set the number of bytes to move to 4CH
      *3457
      LDIREDB0
      Move the 76 (4C) bytes from 36AAH to 4000H
      *3459
      LD HL,36F9H21F936
      Next we need to initialize more ports via a LDIR. The next 4 commands will move the 64 (40) bytes from 36F9H to 41E5H.
      *345C
      LD DE,41E5H11E541
      Set the LDIR Destination to 41E5H
      *345F
      LD BC,0040H014000
      Set the number of bytes to move to 40H
      *3462
      LDIREDB0
      Move the 64 (40) bytes from 36F9H to 41E5H.
      *3464
      GOSUB to 01C9H to clear the screen.
      *3467
      GOSUB to 028DH to check for a BREAK key.

      The Model 4 ROM Student Network Edition ROM changes that to ...

      **3467
      GOSUB to 3517H to check ??????????????????????????
      *346A
      JP NZ,33F9HC2F933
      If we have a BREAK key then jump to Non-Disk BASIC at 33F9H.
      *346D
      IN A,(F0H)DBF0
      We didn't get a BREAK, so we are going to be expecting a disk system. Poll the FLOPPPY STATUS REGISTER at Port F0H into A. Results:
      • xxxx xxx1 = Busy
      • xxxx xx1x = Index/DRQ
      • xxxx x1xx = Track 0/Lost Data
      • xxxx 1xxx = CRC Error
      • xxx1 xxxx = Seek Error/Record Not Found
      • xx1x xxxx = Record Type/Write Fault/Head Loaded
      • x1xx xxxx = Write Protected
      • 1xxx xxxx = Not Ready
      *346F
      INC A3C
      Bump the FDC Status by 1 (which would turn a FFH (128) into a 00H), making a Z flag mean "Disk Controller Not Ready".
      *3470
      JP Z,33F9HCAF933
      If the FDC Stats + 1 is zero, then we have no disk controller, so jump to Non-Disk BASIC at 33F9H.
      *3473
      LD BC,0000H010000
      If we are here, then we have a floppy controller, so set up for a loop of 65,535 times.
      *3476
      DEC BC0B
      Decrease BC by 1.
      *3477
      LD A,81H3E81
      Set A to 81H (Decimal: 10000001).
      *3479
      OUT (F4H),AD3F4
      Send 1000 0001 to the Floppy Disk Controller at Port F4H to set double density.
      NOTE: Port F4H is the Disk Drive and Disk Density Select. Bits 0-3 are the drive select of 0-3 and Bit 7 is 0 for single density and 1 for double density.
      *347B
      LD A,B78
      To test a BC against 0, LOAD A with B and then OR C. A will be 0 only if both B and C were zero.
      *347C
      OR CB1
      *347D
      If we have finished the 65,535 loop then jump to Non-Disk BASIC at 33F9H.
      *3480
      IN A,(F0H)DBF0
      We are still in the loop, so Poll the FLOPPPY STATUS REGISTER at Port F0H into A. Results:
      • xxxx xxx1 = Busy
      • xxxx xx1x = Index/DRQ
      • xxxx x1xx = Track 0/Lost Data
      • xxxx 1xxx = CRC Error
      • xxx1 xxxx = Seek Error/Record Not Found
      • xx1x xxxx = Record Type/Write Fault/Head Loaded
      • x1xx xxxx = Write Protected
      • 1xxx xxxx = Not Ready
      *3482
      BIT 2,ACB57
      Test Bit 2 of A (the Floppy Status). If it is 0 then we haven't made it to track 0, otherwise we have
      *3484
      JR Z,3476H28F0
      Still haven't found track 0, so jump back to 3476H to continue the drive polling loop of 65,535 times.

      If we are here, then we hit track 0, so proceed to process a Warm Boot.

      *3486
      LD E,0AH1E0A
      Set up for a loop of 10 using Register E; this will be the number of tries to find an index mark.
      *3488
      LD BC,0000H010000
      Set up for a loop of 65,536 using Register Pair BC.
      *348B
      IN A,(F0H)DBF0
      Poll the FLOPPPY STATUS REGISTER at Port F0H into A. Results:
      • xxxx xxx1 = Busy
      • xxxx xx1x = Index/DRQ
      • xxxx x1xx = Track 0/Lost Data
      • xxxx 1xxx = CRC Error
      • xxx1 xxxx = Seek Error/Record Not Found
      • xx1x xxxx = Record Type/Write Fault/Head Loaded
      • x1xx xxxx = Write Protected
      • 1xxx xxxx = Not Ready
      *348D
      BIT 1,ACB4F
      Check A to see if bit 1 (meaning "Drive Busy") is set.
      *348F
      If bit 1 is set, JUMP to 3496H to keep processing.
      *3491
      CALL 349BHCD9B34
      If Bit 1 is NOT Set, meaning that the Drive is NOT Busy, we have exhausted our tries, so GOSUB to 349BH to display DISKETTE and keep trying.
      *3494
      Loop back to 348BH to keep checking the drive 65,536 x 10 times

      *3496 - Model 4 Gen 2 Routine - Continue Warm Boot now that we know the drive is working; next we try to find the Index Mark.

      *3496
      DEC E1D
      If we are here, it found a diskette so next we need to find the index mark. First decrement E (the big loop counter of 10) by 1.
      *3497
      So long as we have not run out of the loop of 10 tries, JUMP to 34ACH to find the index mark.
      *3499
      JUMP to 34BDH to try to read the sector.

      *349B - Model 4 Gen 2 Routine - We have exhausted our tries, so display DISKETTE and keep trying.

      *349B
      DEC BC0B
      If bit 1 is not set (meaning, the drive is NOT busy), reduce the counter (BC) by 1.
      *349C
      LD A,81H3E 81
      Load A with 81H (Decimal: 129, Binary: 1000 0001).
      *349E
      OUT (F4H),AD3 F4
      Output A to Port F4H to select drive 0, double density.
      NOTE: Port F4H is the Disk Drive and Disk Density Select. Bits 0-3 are the drive select of 0-3 and Bit 7 is 0 for single density and 1 for double density.
      *34A0
      LD A,B78
      To test a BC against 0, LOAD A with B and then OR C. A will be 0 only if both B and C were zero.
      *34A1
      OR CB1
      *34A2
      RET NZC0
      If BC is not 0, then RETurn
      *34A3
      POP HLE1
      Clear the STACK
      *34A4
      LD HL,0277H21 77 02
      If we are here, then we received no BIT 1 of FLOPPY STATUS REGISTER despite 65,536 tries, so we need to deal with that by printing "DISKETTE?" on the screen. Point HL to the "DISKETTE?" message at 0279H.
      *34A7
      CALL 021BHCD 1B 02
      GOSUB to 021BH. Note; 021BH will display the character at (HL) until a 03H is found.
      *34AA
      If we are here, there is no disk in drive and we displayed "DISKETTE?" so loop back to 3486H to poll for a disk all over again.

      *34AC - Model 4 Gen 2 Routine - Look for the INDEX Mark on the diskette.

      *34AC
      LD BC,0000H010000
      Set up for a loop of 65,536 using Register Pair BC.
      *34AF
      IN A,(F0H)DBF0
      Poll the FLOPPPY STATUS REGISTER at Port F0H into A. Results:
      • xxxx xxx1 = Busy
      • xxxx xx1x = Index/DRQ
      • xxxx x1xx = Track 0/Lost Data
      • xxxx 1xxx = CRC Error
      • xxx1 xxxx = Seek Error/Record Not Found
      • xx1x xxxx = Record Type/Write Fault/Head Loaded
      • x1xx xxxx = Write Protected
      • 1xxx xxxx = Not Ready
      *34B1
      BIT 1,ACB4F
      Check the Floppy Disk Controller Status (held in Register A) to see if bit 1 (meaning "Drive Busy") is set.
      *34B3
      If bit 1 is not set (meaning, the drive is NOT busy), JUMP to 34BAH to restart the read attempts.
      *34B5
      If we are here, then the drive was busy, so GOSUB to 349BH to display DISKETTE? and try again
      *34B8
      JR 34AFH18F5
      JUMP back to the top of this routine and keep looking

      *34BA - Model 4 Gen 2 Routine - Finish initializing the floppy disk boot.

      *34BA
      DEC E1D
      If we are here, it found a diskette so next we need to find the index mark. First decrement E by 1.
      *34BB
      Jump back to 3488H in this WARM BOOT ROUTINE to check the FDC up to 10 times.

      We are going to try to read a sector

      *34BD
      LD A,81H3E 81
      Load A with 81H (Decimal: 129, Binary: 1000 0001).
      *34BF
      OUT (F4H),AD3 F4
      Output A to Port F4H to select drive 0, double density.
      NOTE: Port F4H is the Disk Drive and Disk Density Select. Bits 0-3 are the drive select of 0-3 and Bit 7 is 0 for single density and 1 for double density.
      *34C1
      LD HL,34F5H21 F5 34
      We need to set the Non-Maskable Interrupt Vector to be a JUMP to 34F5H so we do the next 4 steps. Sets 34F5H as the jump point for a NMI hit, which is what occurs once a sector has been completely read.
      *34C4
      LD (404AH),HL22 4A 40
      Put the desired JUMP address of 34F5H into (404AH)
      *34C7
      LD A,C3H3E C3
      Load A with C3H (Decimal: 195, Binary 1100 0011).
      *34C9
      LD (4049H),A32 49 40
      Put the C3H into 4049H.
      NOTE: 4049H is the Non-Maskable Interrupt Vector.
      *34CC
      LD A,80H3E 80
      Load A with 80H (Decimal: 128, Binary 1000 0000).
      *34CE
      OUT (E4H),AD3 E4
      Output "1000 0000" to the the non-maskable interrupt latch via Port E4H.
      *34D0
      LD BC,00F3H01 F3 00
      Set BC to 0F3H.
      NOTE: Port F3H is the Floppy Disk Controller Data Register.
      *34D3
      LD HL,4300H21 00 43
      Set HL to 4300H, which is where the data is going to go.
      *34D6
      LD A,01H3E 01
      Prepare to read Sector 1 by loading A with a 1.
      *34D8
      OUT (F2H),AD3 F2
      Output 1 to the Floppy Disk Controller Track Register at Port F2H.
      *34DA
      LD A,80H3E 80
      Prepare to read a single sector by loading A with 80H (Decimal: 128, Binary: 1000 0000).
      *34DC
      OUT (F0H),AD3 F0
      Output "1000 0000" to Port F0H.
      NOTE: Port F0H is the FDC Status Register. Outputting 1000 0000 is issuing the command READ SECTOR (100), SINGLE RECORD (0), SIDE 0 (0), NO 15MS DELAY (0), DISABLE SIDE COMPARE (0).
      *34DE
      CALL 30CAHCD CA 30
      GOSUB to 30CAH to delay 3 instruction cycles, arguably the time it will take for the FDC to respond to the port command.
      *34E1
      IN A,(F0H)DB F0
      Poll the FLOPPPY STATUS REGISTER at Port F0H into A. Results:
      • xxxx xxx1 = Busy
      • xxxx xx1x = Index/DRQ
      • xxxx x1xx = Track 0/Lost Data
      • xxxx 1xxx = CRC Error
      • xxx1 xxxx = Seek Error/Record Not Found
      • xx1x xxxx = Record Type/Write Fault/Head Loaded
      • x1xx xxxx = Write Protected
      • 1xxx xxxx = Not Ready
      *34E3
      AND 02HE6 02
      Mask A against (0000 0010) which will leave only bit 1 active. This will test for the index mark, and Z will indicate that the index mark has NOT been detected, and NZ will indicate the index mark has been detected.
      *34E5
      JP Z,34E1HCA E1 34
      Loop back 2 instructions to keep polling F0H until the index is found.
      *34E8
      INIEDA2
      Input the data byte.
      *34EA
      LD A,81H3E81
      Load A with 81H (Decimal: 129, Binary: 1000 0001) to select disk 0.
      *34EC
      OR 40HF640
      OR A with 40H (Binary: 0100 0000) to set A to double-density.
      *34EE
      OUT (F4H),AD3F4
      Output A to Port F4H to select drive 0, double density.
      NOTE: Port F4H is the Disk Drive and Disk Density Select. Bits 0-3 are the drive select of 0-3 and Bit 7 is 0 for single density and 1 for double density.
      *34F0
      INIEDA2
      Input the data byte.
      *34F2
      JP 34EAHC3 EA 34
      Loop back to 34EAH to set drive and density.
      NOTE: This appears to be an infinite loop in the code, but it isn't in actuality. Once the disk controller has loaded an entire sector, it will trigger a non-maskable interrupt and will exit to 3502H. That was set up at 34CE.

      *34F5 - Model 4 Gen 2 Routine - Non-Maskable Interrupt Jump Point to verify the disk sector read wasn't in error.

      *34F5
      XOR AAF
      Clear A and all flags.
      *34F6
      OUT (E4H),AD3 E4
      Send a 0 to the non-maskable interrupt latch via Port E4H. This is to clear the non-maskable interrupt status.
      *34F8
      LD HL,45EDH21 ED 45
      Now that the NMI jumped here, we need to set a new NMI jump, this time to 45EDH.
      *34FB
      LD (4049H),HL22 49 40
      Load the destination location into (4049H) which is the Non-Maskable Interrupt Vector.
      *34FE
      CALL 30CAHCD CA 30
      GOSUB to 30CAH to delay 3 instruction cycles, arguably the time it will take for the FDC to respond to the port command.
      *3501
      IN A,(F0H)DB F0
      Poll the FLOPPPY STATUS REGISTER at Port F0H into A. Results:
      • xxxx xxx1 = Busy
      • xxxx xx1x = Index/DRQ
      • xxxx x1xx = Track 0/Lost Data
      • xxxx 1xxx = CRC Error
      • xxx1 xxxx = Seek Error/Record Not Found
      • xx1x xxxx = Record Type/Write Fault/Head Loaded
      • x1xx xxxx = Write Protected
      • 1xxx xxxx = Not Ready
      *3503
      POP HLE1
      Clean up the Stack.
      *3504
      AND 1CHE61C
      Mask A with 1CH (Binary: 0001 1100). This keep only bit 3 (Track 0/Lost Data), bit 4 (CRC Error) and bit 5 (Seek Error/Record Not Found). If NONE of these are present, then Z will be set.
      *3506
      If those 3 errors aren't generated, then JUMP to 4300H.
      *3509
      If we are here at least one of those errors were present, so JUMP back to 34BDH to try to read the sector again.

      *350B - Model 4 Gen 2 Routine - NMI handler. On entry, A holds Bit 5 of the Non-Maskable Interrupt Latch at port 0E4H.

      *350B
      If Bit 5 of the Non-Maskable Interrupt Latch was NOT zero (meaning, disk interrupt), then jump to 4049H which, in TRSDOS, is a jump to 42BDH which, at least in TRSDOS, in a RETurn.
      *350E
      IN A,(E4H)DBE4
      Poll the Non-Maskable Interrupt Latch at Port E4H into A.
      *3510
      BIT 5,ACB6F
      Test Bit 5 of Port E4 against A.
      *3512
      JR Z,350EH28FA
      Loop back 2 instructions until it is set (i.e., not a zero).
      *3514
      JP 0000HC30000
      Jump to 0000H to restart the computer.

      *3517 - Model 4 Gen 2 Routine - Finish up initialization by filling 256 bytes into 4300H and then JUMPing there

      *3517
      OR AB7
      Set the FLAGS based on Register A
      *3518
      JP NZ,3179HC27931
      If A is not ZERO then Jump to 3179H to continuing initialization routine by setting up the RS-232
      *351B
      LD HL,4300H210043
      Set HL to 4300H, which is where the data is going to go.
      *351E
      GOSUB to 3086 to Poll the UART and wait for the P FLAG to not be set and then CONTINUE at 306CH
      *3521
      LD (HL),A77
      Store Register A into the memory location pointed to by Register Pair HL
      *3522
      INC L2C
      Bump Register L by 1
      *3523
      JR NZ,351EH20F9
      So long as we have not overflowed L, LOOP back to 351EH
      *3525
      JP 37E0HC3E037
      If we had filled 256 bytes, continue via a JUMP to 37E0H to set up the RS-232 to DTR On, RTS Off, No parity, 9600 Baud, 1 Stop Bit, and then JUMP to (HL).
      *3528
      NOP00
      No Operation

      **3517 - Model 4 ROM Student Network Edition - Finish up initialization

      **3517
      CALL 3086HCD8630
      GOSUB to 3086 to simply poll the keyboard and mask for a "4" key
      **351A
      JR NZ,3520H2004
      If the key isn't a "4" then restore BC and return to processing as a regular Model 4 would
      **351C
      CALL 028DHCD8D02
      GOSUB to 028DH to check for a BREAK key.
      **351F
      RETC9
      RETurn to CALLer
      **3520
      POP BCC1
      Restore the contents at the top of the STACK into Register Pair BC
      **3521
      JP 3179HC37931
      Jump to 3179H to continuing initialization routine by setting up the RS-232
      **3524
      NOP00
      **3525
      NOP00
      **3526
      NOP00
      **3527
      NOP00
      **3528
      NOP00

      3529H - Deal with the cursor.

      3529
      Set the return address to 3591H and put that into DE.
      352C
      PUSH DE
      Push that return address onto the Stack.
      352D
      IN A,(0ECH)
      Poll Port ECH and put the results into A.

      NOTE: Port ECH is the Miscellaneous Controls port, which covers clock on/off (Bit 0), cassette motor on or off (Bit 1), double size video on or off (Bit 2), and special character set select of Kana or misc (Bit 3). Higher bits are used for the Model 4 only.
      352F
      LD A,(4022H)
      Check to see if the Cursor is on by loading (4022H) into A.

      NOTE: 4022H holds the Cursor ON/OFF Flag and will be 0 if the cursor is off, or the the underscore character otherwise.
      3532
      OR A
      Set the flags.
      3533
      If A was zero, then the cursor is off and we JUMP down to 3557H.
      3535
      LD A,(401CH)
      Check to see if the cursor is to blink by loading (401CH) into A.

      NOTE: 401CH holds the Cursor Blink Switch and will be 0 for Blink, and anything else for No Blink.
      3538
      OR A
      Test A.
      3539
      If the system is set for No Blink, then JUMP to 3557H.
      353B
      Load HL with 401AH.

      NOTE: 401AH is the memory location that stores the cursor blink count.
      353E
      DEC (HL)
      Reduce the memory contents of (401AH) by one.

      NOTE: 401AH is the memory location that stores the cursor blink count.
      353F
      If that reduced count is still not zero, JUMP to 3557H.
      3541
      LD (HL),07H
      Set the cursor blink count to 7.

      NOTE: 401AH is the memory location that stores the cursor blink count.
      3543
      INC HL
      Bump HL. This will increase HL from 401AH to 401BH.

      NOTE: 401BH holds the cursor blink status - 0 = Off, Anything Else = On.
      3544
      LD A,(HL)
      Poll the cursor blink status memory location and put the results into A.
      3545
      AND 01H
      Mask A against 0000 0001, to have only Bit 0 active.
      3547
      XOR 01H
      XOR A with 01H.
      3549
      LD (HL),A
      Put the toggled cursor blink status into the appropriate memory location.
      354A
      LD HL,(4020H)
      Poll (4020H) and put the result into HL.

      NOTE: 4020H holds the current cursor position.
      354D
      If the current cursor position is 0, then it is off, so JUMP down 2 instructions to 3554H to make the cursor a blank (space).
      354F
      LD A,(4023H)
      Load A with the memory contents of (4023H).

      NOTE: 4023H holds the cursor character.
      3552
      JUMP down to 3556H to skip the next instruction and continue this routine by displaying the character in A.
      3554
      LD A,20H
      Load A with 20H which is the ASCII equivalent of a SPACE.

      3556H - Update the heartbeat and deal with the time, including rollover to the next day, month, and year.

      3556
      LD (HL),A
      Put the character held in A into the memory location pointed to by (HL).

      NOTE: (HL) will hold the current cursor position.
      3557
      LET Register Pair HL = 4216H.

      NOTE: 4216H is the heartbeat counter.
      355A
      DEC (HL)
      Decrease the number held at (4216H) by 1.
      355B
      RET NZ
      If the number held at (4216H) is not zero, then RETURN.
      355C
      LD (HL),1EH
      Put a 1EH (Decimal: 30) into (4216H).

      NOTE: 4216H is the heartbeat counter.
      355E
      INC HL
      Bump HL by 1. HL will now point to 4217H, which is the memory location that holds the SECONDS.
      355F
      Load DE with 0266H.

      NOTE: 0266H points to the TIME DATA of the number of seconds in a minute, the number of mnutes in an hour, and the number of hours in a day.
      3562
      LD B,03H
      Load B with a 03H, to set up a loop where we test seconds, minutes, and hours against their maximums.
      3564
      INC (HL)
      Bump the number currently held in HL to increase the number stored there 1.

      NOTE: If HL is 4217H then it is seconds, 4218H then it is minutes, 4217H then it is hours.
      3565
      LD A,(DE)
      Poll the memory contents held at (DE) and put the result into A to get the maximum possible units for each time measure.

      NOTE: 0266H points to the TIME DATA of the number of seconds in a minute, 0267H points to the number of mnutes in an hour, and 0268H points to the number of hours in a day.
      3566
      SUB (HL)
      Compare the maximum to what we have by subtracting that maximum from the value pointed at in (HL).
      3567
      RET NZ
      If there is no difference between what we have and the maximum then RETURN.
      3568
      LD (HL),A
      If there is a difference, then put the difference into (HL) because we have to bump the next highest thing (seconds to minutes, minutes to hours, hours to days).
      3569
      INC HL
      Bump HL.

      NOTE: If HL is 4217H then it is seconds, 4218H then it is minutes, 4219H then it is hours, and 421AH then it is YEARS.
      356A
      INC DE
      Bump DE.

      NOTE: 0266H points to the TIME DATA of the number of seconds in a minute, 0267H points to the number of mnutes in an hour, and 0268H points to the number of hours in a day.
      356B
      Loop back to 3564H until the loop of 3 has been met, meaning that we have processed seconds, minutes, and hours.
      356D
      INC HL
      Bump HL one more time, to 421BH.

      NOTE: 421BH holds the current DAY portion of the date.
      356E
      INC (HL)
      Bump the DAY portion of the date.
      356F
      INC HL
      Bump HL one more time, to 421CH.

      NOTE: 421BH holds the current MONTH portion of the date.
      3570
      LD A,(HL)
      Get the month and put it into A.
      3571
      DEC HL
      Decrease HL back to to 421BH.

      NOTE: 421BH holds the DAY portion of the date.
      3572
      DEC A
      Decrease A by one (to the previous month).

      NOTE: DE currently points to the memory locations housing the of days in each month.
      3573
      ADD A,E
      Add E to A.
      3574
      LD E,A
      Load E with the result. E = E + A.
      3575
      LD A,(DE)
      Poll the number of days in a month and put the result into A.
      3576
      CP (HL)
      Compare A against (HL).

      NOTE: If this is not a loop, the HL holds the day portion of the date.
      3577
      RET NC
      If all of this shows that the current day of the month is less than the maximum number of days in a month, then RETURN because we don't need to bump anything more.
      3578
      LD A,(HL)
      Load A with the current day of the month, as HL was pushed back to point to the day of the month at instruction 3571H.
      3579
      CP 1EH
      Compare A to 1E (Decimal: 30).

      NOTE: A CP actually subtracts 1E from A without modifying A, but the flags are set accordingly. Here, if A is < 1E then the CARRY FLAG will be set.
      357B
      If the carry flag isn't set then JUMP to 3583H to update the MONTH, but not the YEAR.
      357D
      DEC HL
      Decrement HL to now point to 421AH.

      NOTE: 421AH points to the current YEAR.
      357E
      LD A,(HL)
      Load the YEAR into A.
      357F
      INC HL
      Bump HL to 421BH.

      NOTE: 421BH points to the current DAY.
      3580
      AND 03H
      Mask A (which is holding the year) with 03H (Binary: 00000011) to test for a leap year.
      3582
      RET Z
      RETURN if that mask showed that we are in the 4th year of a cycle (because 04 and higher are turned off).
      3583
      LD (HL),01H
      Put a 1 into the memory location pointed to by HL (which is DAY).
      3585
      INC HL
      Bump HL to 421CH.

      NOTE: 421BH points to the current MONTH.
      3586
      INC (HL)
      Increase the current MONTH by 1.
      3587
      LD A,(HL)
      Put the current MONTH into A.
      3588
      SUB 0DH
      Subract 13 from A.

      NOTE: This will test against a month 13. If A is < 13, then the CARRY FLAG will be set.
      358A
      RET C
      If it is NOT month 13 then RETURN to skip the next code which increases the YEAR.
      358B
      LD (HL),01H
      If we are here, then MONTH = 13, so set MONTH to 1.
      358D
      DEC HL
      Decrement HL to 421BH.

      NOTE: 421BH points to the current DAY.
      358E
      DEC HL
      Decrement HL to 421AH.

      NOTE: 421BH points to the current YEAR.
      358F
      INC (HL)
      Bump the current year.
      3590
      RET
      RETURN.

      3591H - Check to see if the clock is on and exit back out if it is off OR the heartbeat shows that the clock was just updated. Pass through otherwise.

      3591
      LD A,(4210H)
      Test to see if the clock is on by loading A with the memory contents of 4210H.

      NOTE: 4210H is the bit mask for Port ECH. Port ECH is the Miscellaneous Controls port, which covers clock on/off (Bit 0), cassette motor on or off (Bit 1), double size video on or off (Bit 2), and special character set select of Kana or misc (Bit 3). Higher bits are used for the Model 4 only.
      3594
      BIT 0,A
      Test Bit 0 of A to see if the clock is on or off.
      3596
      RET Z
      If Bit 0 of A is ZERO, then return.
      3597
      LD A,(4216H)
      If we are here, then the clock is on, so load A with the memory contents of (4216H) to see if the clock was just updated.

      NOTE: 4216H is the heartbeat counter.
      359A
      CP 1EH
      Compare the heartbeat counter against 1EH.

      NOTE: A CP actually subtracts 1E from A without modifying A, but the flags are set accordingly.
      359C
      RET NZ
      If the clock was not just updated, then RETURN.
      359D
      If the clock was just updated, then we need to display to screen so we set HL to the screen location of 3C35H which is the top line of the screen, 10 characters from the end of first line.

      35A0H - Put the Clock 10 characters from the end of the first line. We enter this routine with HL pointing to the screen location 10 characters from the end of the first line.

      35A0
      Set DE to 4219H.

      NOTE: 4219H is the current HOUR.
      35A3
      LD C,3AH
      Load C with a :.

      NOTE: This routine is also used to convert the date, and C will be swapped out to a /for that routine.
      35A5
      LD B,03H
      Load B with a 3.

      NOTE: This is because we need to convert 3 numbers, so we will loop 3 times.
      35A7
      LD A,(DE)
      Put the memory contents of DE into A.

      NOTE: This will be HOUR (4219H) on the first pass, MINUTE (4218H) on the second pass, and SECOND (4219H) on the third pass.
      35A8
      DEC DE
      Decrement DE to point to the next unit to be dealt with.
      35A9
      LD (HL),2FH
      Load the memory location pointed to by (HL) with 2F.

      NOTE: 2F is a /which is also 1 character below a 0.
      35AB
      INC (HL)
      Increase whatever is held in (HL). On the first iteration, this change the character at the screen location to a 0.
      35AC
      SUB 0AH
      A = A - 10. If A is < 10, then the CARRY FLAG will be set and if A is > 10, the NOT CARRY FLAG will be set.
      35AE
      Loop back 2 instructions if A > 10 [V!!!].
      35B0
      ADD A,3AH
      If we are here, then A was less than 10, so we need to increase A by 3A (Binary: 0011 1010) to point to the ASCII number of the remainder.
      35B2
      INC HL
      Bump HL to point to the next location on the video screen. On the first iteration, this will be the 2nd digit of the HOUR.
      35B3
      LD (HL),A
      Put the ASCII value of the remainder onto the screen. On the first iteration, this will be the 2nd digit of the HOUR put into 4220H.
      35B4
      INC HL
      Bump HL to point to the next location on the video screen. On the first iteration, this will be the 3rd character.
      35B5
      DEC B
      Decrement B to the next unit. On the first iteration, this will move from 3 to 2.
      35B6
      RET Z
      If we have processed all passes in the loop, RETURN.
      35B7
      LD (HL),C
      If we are here, then the routine has not yet looped 3 times, so put a :onto the screen.
      35B8
      INC HL
      Bump HL to point to the next location on the video screen.
      35B9
      JUMP back to 35A7 to continue the loop.

      35BBH - Put the DATE 8 characters from the end of the first line. We enter this routine with HL pointing to the screen location and we just jump into the prior routine with a different pointer to the DATE and a change in the delimeter to a /.

      35BB
      Load DE with 421CH.

      NOTE: 421CH holds the current MONTH.
      35BE
      LD C,2FH
      Load C with a /.
      35C0
      Jump into the above routine to convert the Month, Day, and Year.

      35C2H - Maskable Interrupt Handler.

      35C2
      PUSH AF
      Save AF to the STACK.
      35C3
      IN A,(E0H)
      Poll Port E0H which is the MASKABLE INTERRUPT LATCH and put the results into A.
      35C5
      RRA
      Rotate the contents of A one bit to the right, with the contents of the carry bit being moved to bit 7 and the contents of bit 0 being moved to the carry bit. If Bit 0 is low then NC will be set.
      35C6
      If Bit 0 is low then JUMP to 3365H (which is a cassette routine with E set to HIGH).
      35C9
      RRA
      Rotate the contents of A one bit to the right, with the contents of the carry bit being moved to bit 7 and the contents of bit 0 being moved to the carry bit. If Bit 0 is low then NC will be set.
      35CA
      If Bit 1 (which is now in the carry) is low then JUMP to 3369H (which is a cassette routine with E set to LOW).
      35CD
      PUSH BC
      Save all the registers.
      35CE
      PUSH DE
      35CF
      PUSH HL
      35D0
      PUSH IX
      35D2
      PUSH IY
      35D4
      Load HL with 35F1H to set the return address.
      35D7
      PUSH HL
      Push HL to the STACK.
      35D8
      RRA
      Rotate the contents of A one bit to the right, with the contents of the carry bit being moved to bit 7 and the contents of bit 0 being moved to the carry bit. If Bit 2 is low then NC will be set.
      35D9
      If Bit 2 (which is now in the carry) is low then JUMP to 4046H.

      NOTE: 4046H is Interrupt Vector 2 and contains a JUMP to 4046H which, in at least in TRSDOS, is just a JUMP to 3529H and is used by the clock.
      35DC
      RRA
      Rotate the contents of A one bit to the right, with the contents of the carry bit being moved to bit 7 and the contents of bit 0 being moved to the carry bit. If Bit 3 is low then NC will be set.
      35DD
      If Bit 3 (which is now in the carry) is low then JUMP to 403DH.

      NOTE: 403DH is Interrupt Vector 3 and contains a JUMP to 403DH which, in at least in TRSDOS, is just a JUMP to 35FAH which is a RETurn.
      35E0
      RRA
      Rotate the contents of A one bit to the right, with the contents of the carry bit being moved to bit 7 and the contents of bit 0 being moved to the carry bit. If Bit 4 is low then NC will be set.
      35E1
      If Bit 4 (which is now in the carry) is low then JUMP to 4206H.

      NOTE: 403DH is Interrupt Vector 4 and contains a JUMP to 4206H which, in at least in TRSDOS, is just a JUMP to 35FAH which is a RETurn.
      35E4
      RRA
      Rotate the contents of A one bit to the right, with the contents of the carry bit being moved to bit 7 and the contents of bit 0 being moved to the carry bit. If Bit 5 is low then NC will be set.
      35E5
      If Bit 5 (which is now in the carry) is low then JUMP to 4209H.

      NOTE: 4209H is Interrupt Vector 5 and contains a JUMP to 4209H which, in at least in TRSDOS, is just a JUMP to 35FAH which is a RETurn.
      35E8
      RRA
      Rotate the contents of A one bit to the right, with the contents of the carry bit being moved to bit 7 and the contents of bit 0 being moved to the carry bit. If Bit 6 is low then NC will be set.
      35E9
      If Bit 6 (which is now in the carry) is low then JUMP to 4040H.

      NOTE: 4209H is Interrupt Vector 6 and contains a JUMP to 4040H which, in at least in TRSDOS, is just a JUMP to 35FAH which is a RETurn.
      35EC
      RRA
      Rotate the contents of A one bit to the right, with the contents of the carry bit being moved to bit 7 and the contents of bit 0 being moved to the carry bit. If Bit 7 is low then NC will be set.
      35ED
      If Bit 7 (which is now in the carry) is low then JUMP to 4043H.

      NOTE: 4043H is Interrupt Vector 7 and contains a JUMP to 4043H which, in at least in TRSDOS, is just a JUMP to 35FAH which is a RETurn.
      35F0
      POP HL
      Restore all registers.
      35F1
      POP IY
      35F3
      POP IX
      35F5
      POP HL
      35F6
      POP DE
      35F7
      POP BC
      35F8
      POP AF
      35F9
      EI
      Enable Interrupts.
      35FA
      RET
      RETURN.

      35FBH - RS-232 Initialization Routine. I'm [guessing] that IX is set to 41F5H

      35FB
      DI
      Disable Interrupts so they don't interrupt this routine.
      35FC
      IN A,(EAH)
      Poll Port EAH into A.

      NOTE: Port EAH is the RS-232 UART Control Register/Status Register. Input:
      • Bit 3: Parity error (1=True)
      • Bit 4: Framing Error (1=True)
      • Bit 5: Overrun (1=True)
      • Bit 6: Data Sent (1=True)
      • Bit 7: Data Ready (1=True)
      35FE
      CP FFH
      Compare A with FFH to see if the RS-232 exists.
      3600
      If the RS-232 does NOT exist, JUMP down to 363AH.
      3602
      XOR A
      Flip the RS-232 Port Results, just to get a non-zero result.
      3603
      OUT (0E8H),A
      Output A to port E8H.

      NOTE: Port E8H is the RS-232 Status Register & Master Reset. Outputting ANYTHING to Port E8H resets the RS-232.
      3605
      LD A,(IX+03H)
      Load the BAUD RATE CODE into A.

      NOTE: 41F8H holds the baud rate code.
      3608
      OUT (0E9H),A
      Output A to Port E9H.

      NOTE: Port E9H is the RS-232 Baud Rate Selects and Sense Switches. Outputting to Port E9H will set the Baud Rate as follows:
      • Bits 0-3 - Select the Receive Rate
      • Bits 4-7 - Select the Transmit Rate
      360A
      LD A,(IX+04H)
      Load the CONFIURATION CODE into A.

      NOTE: 41F9 holds the RS-232 Configuration Code.
      360D
      OR A
      Set the flags.
      360E
      If the CONFIGURATION CODE in A is 0, then JUMP down to 363AH.
      3610
      OUT (0EAH),A
      Output the CONFIGURATION CODE to Port EAH.

      NOTE: Port EAH is the RS-232 UART Control Register/Status Register. For output:
      • Bit 0: Data Terminal Ready
      • Bit 1: Request to End
      • Bit 2: Break
      • Bit 3: Parity Enable
      • Bit 4: Stop Bits
      • Bit 5: Select
      • Bit 6: Word Length
      • Bit 7: Parity (0=Odd, 1=Even)
      3612
      Load IY with 41E5H.

      NOTE: 41E5H Is the RS-232 Input DCB. 1=READ ONLY.
      3616
      GOSUB to 3644H [to CLEAR OPTIONS].
      3619
      LD A,(IX+05H)
      Load the WAIT SWITCH into A.

      NOTE: 41FAH holds the RS-232 Wait Switch.
      361C
      OR A
      Set the flags.
      361D
      If the WAIT SWITCH was ZERO, skip the next instruction and pick up at 3623H.
      361F
      SET 01H,(IY+04H)
      If we are here, the WAIT SWITCH was NOT zero, so SET BIT 1 of (41E9H) to SET the WAIT FLAG.

      NOTE: 41E9H is the RS-232 Input DCB:
      • Bit 2: Driver On/Off
      • Bit 1: Wait/No Wait
      3623
      SET 02H,(IY+04H)
      and SET BIT 2 of (41E9H) to SET the ACTIVE FLAG.

      NOTE: 41E9H is the RS-232 Input DCB:
      • Bit 2: Driver On/Off
      • Bit 1: Wait/No Wait
      3627
      Load IY with 41EDH.

      NOTE: 41EDH is the RS-232 Output DCB. Type = 2 = Write Only.
      362B
      OR A
      Set flags.
      362C
      If the ZERO flag is set, we have NO WAIT, so JUMP to 3632H.
      362E
      SET 01H,(IY+04H)
      Set Bit 1 of 41F1H to set the WAIT FLAG.

      NOTE: 41F1H is part of the RS-232 Output DCB:
      • Bit 2: Driver ON/OFF
      • Bit 1: Wait/No Wait
      3632
      SET 02H,(IY+04H)
      Set Bit 2 of 41F1H to set the ACTIVE FLAG.
      3636
      IN A,(E8H)
      Poll Port E8H and put the result into A.

      NOTE: Port E8H is the RS-232 Status Register & Master Reset. Input:
      • Bit 4: Ring Indicator
      • Bit 5: Carrier Detect
      • Bit 6: Data Set Ready
      • Bit 7: Clear to Send
      3638
      EI
      Re-Enable Interrupts.
      3639
      RET
      RETURN.

      363A - This will zero out a bunch of RS-232 Related Ports and Memory Addresses. We wind up here if there is no RS-232 or the RS-232 CONFIGURATION CODE is 0.

      363A
      XOR A
      Clear A and all flags.
      363B
      LD B,04H
      Load B with 4 as a counter. 4 is because there are 4 ports - E8H-EBH:
      • Port E8H: RS-232 Status Register & Master Reset
      • Port E9H: RS-232 Baud Rate Select and Sense Switches
      • Port EAH: RS-232 UART Control Register and Status Register
      • Port EBH: RS-232 Data Register
      363D
      LD C,0E8H
      Load C with E8H.
      363F
      OUT (C),A
      Send a 0 to the current port. On the first iteration, this is E8H (the RS-232 Status Register & Master Reset).
      3641
      INC C
      BUMP C to the next port.
      3642
      Jump back to 363FH until all 4 ports have been reset.
      3644
      Load HL with 41E8H.

      NOTE: 41E8 is the Input Buffer of the RS-232 Input DCB.
      3647
      LD B,03H
      Load B with 3 as a counter. 3 is for 3 bytes - 41E8H, 41E9H, and 41EAH.
      3649
      LD (HL),00H
      Load (HL) with 00H.
      364B
      INC HL
      Bump HL.
      364C
      Loop back to 3649H until 3 bytes have been zeroed.
      364E
      Load HL with 41F0H.

      NOTE: 41F0H is the 1 characer output buffer for the RS-232 Output DCB.
      3651
      LD B,03H
      Load B with 3 as a counter. 3 is for 3 bytes - 41F0H, 41F1H, and 41F2H.
      3653
      LD (HL),00H
      Load (HL) with 00H.
      3655
      INC HL
      Bump HL.
      3656
      Loop back to 3653H until 3 bytes have been zeroed.
      3658
      RETURN.

      365AH - RS-232 Input Routine.

      365A
      Load IX with 41E5H.

      NOTE: 41E5H is the DCB for RS-232 Input. 41E8H is the 1 Character RS-232 Input.
      365E
      XOR A
      Clear A and all Flags.
      365F
      LD (IX+03H),A
      Load (41E8H) with a Zero.

      NOTE: 41E8H is the 1 Character RS-232 Input.
      3662
      BIT 2,(IX+04H)
      Test Bit 2 of 41E9H to see if the RS-232 is active.

      NOTE: Bit 2 of 41E9 contains the DRIVER ON/OFF.
      3666
      RET Z
      If the Driver is OFF, RETURN.
      3667
      IN A,(EAH)
      If the Driver is ON, Poll Port EAH into A.

      NOTE: Port EAH is the RS-232 UART Control Register/Status Register. Input:
      • Bit 3: Parity error (1=True)
      • Bit 4: Framing Error (1=True)
      • Bit 5: Overrun (1=True)
      • Bit 6: Data Sent (1=True)
      • Bit 7: Data Ready (1=True)
      3669
      BIT 7,A
      Test Bit 7 of A.

      NOTE: Bit 7 will be 1 if DATA READY (1=True).
      366B
      If NOT ZERO, then DATA is READY, so JUMP out of this loop to 367AH.
      366D
      BIT 1,(IX+04H)
      Test Bit 1 of 41E9H.

      NOTE: Bit 1 of 41E9 contains the WAIT/NO WAIT of the RS-232 Input DCB.
      3671
      RET Z
      If its NO WAIT then RETURN, otherwise continue (to keep polling).
      3672
      GOSUB to 028DH to check for a BREAKkey.
      3675
      JUMP to 3667H to poll again if there was NO BREAK.
      3677
      JUMP to 4203H if the BREAKkey was pressed.

      NOTE: 4203H JUMPS to 022EH and is the break vector for tape and RS-232.
      367A
      IN A,(EBH)
      Poll Port EBH to A.

      NOTE: Port EBH is the RS-232C Data Register. It contains the data received from the RS-232C.
      367C
      LD (IX+03H),A
      Load (41E8H) with a A (the data from Port EBH).

      NOTE: 41E8H is the 1 Character RS-232 Input.
      367F
      RET
      RETURN.

      3680H - RS-232 Output Routine.

      3680
      Note. 41EDH is the RS-232 Output DCB, and 41F1H holds DRIVER ON/OFF in BIT 2, and WAIT/NO WAIT in BIT 1.
      3684
      BIT 2,(IX+04H)
      Test Bit 2 of 41F1H to see if the RS-232 is active.
      3688
      RET Z
      If the RS-232 is NOT active, RETURN.
      3689
      IN A,(EAH)
      If the RS-232 IS active, then Poll Port EAH into A.

      NOTE: Port EAH is the RS-232 UART Control Register/Status Register. Input:
      • Bit 3: Parity error (1=True)
      • Bit 4: Framing Error (1=True)
      • Bit 5: Overrun (1=True)
      • Bit 6: Data Sent (1=True)
      • Bit 7: Data Ready (1=True)
      368B
      BIT 6,A
      Test Bit 6 of Port EAH to see READY TO SEND.
      368D
      If READY TO SEND then skip forward to 369CH.
      368F
      BIT 1,(IX+04H)
      Test Bit 1 of 41F1H to see if the RS-232 is active.

      NOTE: 41F1H Hholds DRIVER ON/OFF in BIT 2, and WAIT/NO WAIT in BIT 1.
      3693
      RET Z
      If RS-232 is NOT active, RETURN.
      3694
      GOSUB to 028DH to check for a BREAKkey.
      3697
      JUMP to 3689H to poll again if there was NO BREAK.
      3699
      JUMP to 4203H if the BREAKkey was pressed.

      NOTE: 4203H JUMPS to 022EH and is the break vector for tape and RS-232.
      369C
      LD A,(IX+03H)
      Load A with the memory contents of 41F0H.

      NOTE: 41F0H is RS-232 output buffer byte.
      369F
      OR A
      Test A and Set Flags.
      36A0
      If not zero, then there is a character in the buffer, so skip the next instruction and leave that byte in A.
      36A2
      LD A,C
      Load A with C [GET CHAR FROM DISPATCHER].
      36A3
      OUT (0EBH),A
      Send A out of Port EBH.

      NOTE: Port EBH is the RS-232C Data Register. It contains the data to be sent to the RS-232C.
      36A5
      LD (IX+03H),00H
      Load memory contents of the RS-232 output byte (at 41F0H) with a 0.
      36A9
      RET
      RETURN.

      36AAH - Initial Vectors and DCBs for RAM 4000H-404BH.

      36AA
       
      C3 96 1C C3 78 1D C3 90 1C C3 D9 25 C9 00 00 C9 00 00 C3 18 30 01 24 30 00 01 07 00 00 07 73 04 00 3C 00 B0 00 06 C2 03 43 01 00 FF 52 C3 00 50 C7 00 00 AF C9 00 AA AA AA AA AA AA AA C3 FA 35 C3 FA 35 C3 FA 35 C3 29 35 C7 00
      36D5
      FFH
      36D6
      00H
      This byte is moved to reserved RAM location 402CH (part of the printer Device Control Block) during power up. This location formerly contained the letter "R" (probably leftover garbage from the Model I "PR" designator) but now contains a zero byte.
      36DA
      RST 00H
      36DB
      NOP
      36DC
      NOP
      36DD
      XOR A
      36DE
      RET
      36DF
      NOP
      36E0-36E6H
       
      These seven bytes are moved to reserved RAM locations 4036H - 403CH during power up. These locations are used by the keyboard scan routine to store an "image" of the first seven rows of the keyboard matrix. They are all initialized to contain AAH bytes in the Model III (for some unknown reason), but in the Model 4 they are correctly initialized with zero bytes.
      36E7-36F3
       
      C3 FA 35 C3 FA 35 C3 FA 35 C3 FA 35 C7
      36F4
      NOP

      36F5H - UNUSED.

      36F5-36F8
       
      00 00 00 00

      36F9H - Initial Vectors and DCBs for RAM 41E5H-4224H.

      36F9-36FE
       
      01 1E 30 00 00 00
      36FF-3700
      06 1B
      Two bytes that are moved to RS-232 Input Device Control Block (locations 41EBH & 41ECH) during power up. These bytes formerly contained the ASCII characters "RI" (designator for "RS-232 input"), but now contain the ASCII character codes that will be returned when the F1or F2keys are pressed. 36FFH contains 60H (a SHIFT+ @character), which will be loaded into 41EBH as the character returned when the F1key is pressed, while 3700H contains lBH (a shift-up arrow character), which will be loaded into 41ECH as the character returned when the F2key is pressed.
      3700-3706
      1B 02 21 30 00 00 00
      3707-3708
      08 00
      Two bytes that are moved to RS-232 Output Device Control Block (locations 41F3H and 41F4H) during power up. These bytes formerly contained the ASCII characters "RO" (designator for "RS-232 Output"), but now contain the ASCII character code that will be returned when the F3key is pressed and the "image" for the eighth keyboard row (the row mapped to 3880H). 3707H contains 08H (a SHIFT+ @character), which will be loaded into 41F3H as the character returned when the "F3" key is pressed, while 3708H contains a zero byte, which will be loaded into 41F4H to initialize the storage location used to store the "image" (current status) of the CAPS, CTRL, and function keys (F1, F2, and F3).
      3709-370E
      02 1B 30 55 6C FF
      370F-3710
      00 00
      Two bytes that are moved to RS-232 Initialization Device Control Block (locations 41FBH and 41FCH) during power up. These bytes formerly contained the ASCII characters "RN" (designator for "RS-232 iNitialization"), but now contain two zero (00H) bytes.
      3711-371F
      00 00 FF FF 00 00 C3 2E 02 C3 FA 35 C3 FA 35
      3720-3730
      41 32 03 32 28 03 3C 04 00 00 1E 00 00 00 00 00 00
      3731-3733
      00 00 00
      Three bytes that are moved to 421DH - 421FH during power up. These bytes formerly contained the device type flag and the driver address, and were part of the I/O re-router Device Control Block. As previously mentioned, the I/O re-router routine has been eliminated from the Model 4 ROM.
      3734-3738
      00 00 00 00 FF

      *3739H - Model 4 Gen 1

      *3739
      Load BC with 3801H (KEYBOARD ROW 0).
      *373C
      Load HL with 4036H (BUFFER ROW 0).
      *373F
      LD D,00H
      Load D with 0 (so D = ROW 0).
      *3741
      LD A,(BC)
      Load A with the contents held in (BC) to check the keyboard row.
      *3742
      LD E,A
      Load E with the contents held in (BC) to check the keyboard row.
      *3743
      XOR (HL)
      XOR (HL) to set changed bits.
      *3744
      LD (HL),E
      Save the scan back into (HL).
      *3745
      AND E
      Mask A with E (to mask the released keys).
      *3746
      JUMP to 3767H if any keys are pressed.

      *3748 - Model 4 Gen 1 routine to Go to the next Keyboard row

      *3748
      INC D
      Bump D so that D holds the NEXT row number.
      *3749
      INC HL
      Bump HL so that HL holds the NEXT buffer location.
      *374A
      RLC C
      We need C to point to the next row of keys, so we rotate C left one bit, copying BIT 7 to the CARRY FLAG and the CARRY FLAG to BIT 0.
      *374C
      If the ROTATE caused the P FLAG to trigger (by having the numebr of 1 bits being even), JUMP back to 3741H to check the keyboard row
      *374F
      LD A,(41FDH)
      If the ZERO FLAG is not set, then load A with the memory contents of 41FDH.

      NOTE: 41FDH is the saved position in the keyboard scan.
      *3752
      LD L,A
      Load L with the A.
      *3753
      LD A,(41FEH)
      Load A with the memory contents of 41FEH.

      NOTE: 41FEH is the saved IMAGE at the saved position in the keyboard scan data.
      *3756
      AND (HL)
      MASK A against (HL) to see if the previous keys are still pressed.
      *3757
      JUMP to 3785H if the previous keys are still pressed.
      *375A
      SBC HL,HL
      Zero HL by subtracting HL from HL.
      *375C
      LD (4201H),HL
      Load the memory location at 4201H with HL to clear the repeat counter.

      NOTE: 4201H is the REPEAT DELAY COUNTER.

      *375F - Model 4 Gen 1 routine to Set the Keyboard Repeat Delay Count to 1500.

      *375F
      Load HL with 05DCH.

      NOTE: 05DCH is 1500.
      *3762
      LD (41FFH),HL
      Load the memory location at 41FFH with 05DCH.

      NOTE: 41FFH holds the keyboard scan repeat delay count.
      *3765
      XOR A
      Clear A and all flags.
      *3766
      RET
      RETURN.

      *3767 - Model 4 Gen 1 routine to Keyboard Repeat - Jumps Here if the same keys are still pressed.

      *3767
      LD E,A
      Load E with A.
      *3768
      PUSH BC
      Save BC to the STACK.
      *3769
      Load BC with 05C4H to set up a 1/50 second delay for de-bounce.
      *376C
      GOSUB to 0060H which jumps to the delay routine at 01FBH (which uses BC as a loop counter). It RETs when done so it doesn't come back here.
      *376F
      POP BC
      Restore BC from the STACK.
      *3770
      LD A,(BC)
      Load A with the memory contents pointed to by BC to re-check the keyboard.
      *3771
      AND E
      Compare A against E to check the pattern.
      *3772
      RET Z
      If not the same pattern then RETURN.
      *3773
      LD (41FEH),A
      If it is the same pattern then save A into (41FEH).

      NOTE: 41FE is the SAVED IMAGE AT POSITION.
      *3776
      LD A,L
      Load A with L (the scan position).
      *3777
      LD (41FDH),A
      Save A into (41FDH).

      NOTE: 41FDH is the SAVED POSITION IN SCAN.
      *377A
      LD A,D
      Load A with D (8 * ROW #).
      *377B
      RLA
      Rotate A left one bit, with the contents of BIT 7 being put into the CARRY FLAG and the CARRY FLAG are put into BIT 0. This puts the read data into the CARRY FLAG.
      *377C
      RLA
      Rotate A left one bit, with the contents of BIT 7 being put into the CARRY FLAG and the CARRY FLAG are put into BIT 0. This puts the read data into the CARRY FLAG.
      *377D
      RLA
      Rotate A left one bit, with the contents of BIT 7 being put into the CARRY FLAG and the CARRY FLAG are put into BIT 0. This puts the read data into the CARRY FLAG.
      *377E
      LD D,A
      D = A
      *377F
      LD A,E
      A = E
      *3780
      RRCA
      Rotate A right one bit, with the contents of BIT 0 being put into BOTH the CARRY FLAG and BIT 7. D = 8* ROW # + KEY #.
      *3781
      RET C
      If the contents of BIT 0 of A was SET, RETurn
      *3782
      INC D
      INC D
      Bump D so that D holds the NEXT row number.
      *3783
      Loop back to 3780H (to keep rotating A right one bit and bumping D).

      *3785 - Model 4 Gen 1 routine to Keyboard Repeat - Jumps Here if the same keys are still pressed.

      *3785
      PUSH HL
      Save HL to STACK.
      *3786
      LD HL,(4201H)
      Load HL with the repeat delay counter.

      NOTE: 4201H is the REPEAT DELAY COUNTER.
      *3789
      INC HL
      Bump HL.
      *378A
      LD (4201H),HL
      Load the memory location at 4201H with HL to clear the repeat counter.

      NOTE: 4201H is the REPEAT DELAY COUNTER.
      *378D
      LD DE,(41FFH)
      Load DE with the byte stored at 41FFH.

      NOTE: 41FFH is the REPEAT DELAY COUNT.
      *3791
      SBC HL,DE
      Subtract with CARRY DE from HL.
      *3793
      POP DE
      Restore old HL (which is what is in the stack) into DE.
      *3794
      If we haven't scanned enough then JUMP to 37DFH to clear flags and RETURN.
      *3797
      XOR A
      Clear A and all flags.
      *3798
      LD (DE),A
      Put a 0 into the memory location pointed to (DE) to let the key be re-read.
      *3799
      LD (4201H),HL
      Load the memory location at 4201H with HL to clear the repeat counter.

      NOTE: 4201H is the REPEAT DELAY COUNTER.
      *379C
      LD L,96H
      Load L with 96H to set a fast repeat count.
      *379E
      LD (41FFH),HL
      Save HL into the memory location at 41FFH.

      NOTE: 41FFH is the REPEAT DELAY COUNT.
      *37A1
      JUMP to 33CAH to re-scan the keybaord.

      *37A4 - Model 4 Gen 1 routine jumped to from the the middle of the tokenize routine.

      *37A4
      CP 22H
      If the character in register A is not an end of the BASIC line character, then test the character against 22H to see if it is a ".
      *37A6
      If the character in register A is not a ", then JUMP forward a few instructions to 37B2H.
      *37A8
      LD A,(409FH)
      Load A with the memory contents of 409FH.

      NOTE: 409FH is the DATA FLAG.
      *37AB
      XOR 01H
      XOR A against 1 (Binary: 0000 0001).

      NOTE: XOR is an exclusive OR, meaning that if the 2 things are the SAME the result is 0, and if the 2 things are DIFFERENT, the result is 1.
      *37AD
      LD (409FH),A
      Load memory location 409FH with a the XOR'd results.

      NOTE: 409FH is the DATA FLAG.
      *37B0
      LD A,22H
      Load A with 22H, which is a ".
      *37B2
      CP 3AH
      Compare A against 3AH (which is a :) doing A - 3AH. If A < 3AH, for UNSIGNED items, then the C FLAG is set. If A => 3AH, for UNSIGNED items, then the C FLAG is reset. If A = 3AH then the Z flag is set.
      *37B4
      If A is 3AH then JUMP to 06AAH.

      NOTE: 06AAH is in the middle of the KEYBOARD DRIVER ENTRY ROUTINE.
      *37B7
      LD A,(409FH)
      Otherwise, Load A with the memory contents of 409FH.

      NOTE: 409FH is the DATA FLAG.
      *37BB
      RRA
      Rotate the contents of A one bit to the right, with the contents of the carry bit being moved to bit 7 and the contents of bit 0 being moved to the carry bit. If Bit 0 is low then NC will be set.
      *3792
      If Bit 7 of the DATA FLAG was set, then JUMP to 06A8H.

      NOTE: 06A8H is in the middle of the KEYBOARD DRIVER ENTRY ROUTINE.
      *37BE
      RLA
      Rotate A left one bit, with the contents of BIT 7 being put into the CARRY FLAG and the CARRY FLAG are put into BIT 0. This puts the read data into the CARRY FLAG.
      *37BF
      JUMP to 06A3H.

      NOTE: 06A3H is the REM processor in the middle of the KEYBOARD DRIVER ENTRY ROUTINE.

      *37C2 - Model 4 Gen 1 routine to BASIC TIMES (DATE$+" "+TIME$)

      *37C2
      RST 10H
      Call the EXAMINE NEXT SYMBOL routine at RST 10H.
      NOTE:
      • The RST 10H routine loads the next character from the string pointed to by the HL register into the A-register and clears the CARRY FLAG if it is alphabetic, or sets it if is alphanumeric.
      • Blanks and control codes 09H and 0BH 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.
      *37C3
      PUSH HL
      Save HL (the current position) to the STACK.
      *37C4
      LD A,11H
      Load A with 11H (Decimal: 17).

      NOTE: This is to set up for a 17 Byte String.
      *37C6
      GOSUB to 2857H to create the string.
      *37C9
      LD HL,(40D4H)
      Load HL with the memory contents of (40D4H).

      NOTE: 40D4 is the string pointer.
      *37CC
      GOSUB to 35BBH.

      NOTE: 35BBH populates the DATE$ and puts it on the screen.
      *37CF
      LD (HL),20H
      Load the memory location pointed to by HL with a SPACE.
      *37D1
      INC HL
      Increment HL to move 1 character over.
      *37D2
      GOSUB to 35A0H.

      NOTE: 35A0H populates the TIME$ and puts it on the screen.
      *37D5
      JUMP to 2884H, which is in the middle of the STRING routine.

      *37D8 - Model 4 Gen 1 routine to Toggle Caps Lock.

      *37D8
      LD A,01H
      Load A with 01H.
      *37DA
      Load HL with 4019H.

      NOTE: 4019H holds the caps lock toggle in the keyboard DCB.
      *37DD
      XOR (HL)
      Invert the contents of the (4019H).
      *37DE
      LD (HL),A
      Store the inverted (i.e., toggled) contents of (4019H) back into (4019H)
      *37DF
      XOR A
      Clear A and all flags.
      *37E0
      RET
      RETurn to Caller.

      *37E1 - Model 4 Gen 1 routine to do a very short delay routine

      *37E1
      PUSH BC
      Push the contents of Register Pair BC to the top of the STACK
      *37E2
      POP BC
      Restore the value held at the top of the STACK into Register Pair BC
      *37E3
      NOP
      No Operation
      *37E4
      RET
      RETurn to Caller

      *37E5 - Model 4 Gen 1 routine Unused Code

      *37E5
      NOP
      *37E6
      NOP
      *37E7
      NOP
      *37E8
      RST 38H
      *37E9
      RST 38H
      *37EA
      NOP

      *37EBH - Model 4 Gen 1 routine to display the Copyright Message

      *37EB
      GOSUB to 021BH. Note; 021BH will display the character at (HL) until a 03H is found.
      *37EE
      Load HL with 0202H.

      NOTE: 0202H points to the message '(C) "80 Tandy"'.
      *37F1
      GOSUB to 021BH. Note; 021BH will display the character at (HL) until a 03H is found.

      *37F4 - Model 4 Gen 1 routine to enable the TIME$ Command

      *37F4
      Load HL with 3030H.

      NOTE: 3030H is the STRING=DATE$+""+TIME$ routine.
      *37F7
      LD (4177H),HL
      Load the memory location held at 4177H with HL.

      NOTE: 4176H is the TIME$ vector as is currently set to be JP ?L3 ERRORas the M1/M3 ROM considered that to be a DOS command. It is not a DOS command for a Model III so that jump needs to be changed. In this case, it is changed from JP ?L3 ERROR to JP 3030H.
      *37F7
      JUMP to 022EH to continue.

      NOTE: 022EH will enable interrupts, show the READY prompt, and RETURN.

      *37FD - Model 4 Gen 1 unused Code

      *37FD
      PUSH HL
      *37FE
      PUSH HL
      *37FF
      RST 38H

      *3739H - Model 4 Gen 2 - Part of the Keyboard Routine to check for SHIFT and CAPS LOCK and deal with them accordingly.

      *3739
      LD A,(3880H)3A 80 38
      Load A with the value held at 3880H (which are the SHIFT keys)
      *373C
      AND 03HE6 03
      Mask the SHIFT Register against 03H (Binary: 0000 0011) to keep only Bits 0 and 1 to check for shifts.
      *373E
      If the masked A is 0, then we have no shifts, and skip the next instruction (to 3742H).
      *3740
      SET 6,DCB F2
      Set BIT 6 of D to offset D for shifts.
      *3742
      LD A,(4019H)3A 19 40
      Load A with the contents of memory location 4019H to check for CAPS LOCK.
      NOTE: 4019H is the CAPS LOCK TOGGLE.
      *3745
      OR AB7
      Set the flags.
      *3746
      If the ZERO flag is set then there is NO CAPS LOCK so JUMP to 3753H to skip the next intructions.
      *3748
      SET 7,DCB FA
      Set BIT 7 of D to offset D for CAPS LOCK.
      NOTE: Bit 7 is the difference between UPPER CASE and LOWER CASE versions of the same letter.
      *374A
      LD A,(3880H)3A 80 38
      Put the contents of memory location 3880H into A to GET SHIFT(S).
      *374D
      AND 03HE6 03
      Mask the SHIFT Register against 03H (Binary: 0000 0011) to keep only Bits 0 and 1 to check for shifts.
      *374F
      JR Z,3753H28 02
      If the masked A is 0, then we have no shifts, and skip the next instruction (to 3753H).
      *3751
      RES 7,DCB BA
      RESET the bit 7 of Register D to offset D for CAPS LOCK.
      NOTE: BIT 7 is the difference between UPPER CASE and LOWER CASE versions of the same letter.
      *3753
      LD HL,304EH21 4E 30
      Load HL with 304EH (the KEYBOARD TABLES).
      *3756
      LD E,D5A
      We need DE to be the OFFSET, so load E with D and ...
      *3757
      LD D,00H16 00
      ... load D with 00.
      *3759
      ADD HL,DE19
      Add DE (the offset over the keyboard table) to HL (the keyboard table).
      *375A
      LD A,(HL)7E
      Get the character pointed to by (HL) and put it into A.
      *375B
      RETC9
      RETurn to Caller

      *375C - Model 4 Gen 2 - Part of the TOKENIZE Routine and KEYBOARD shared routine. Same as Gen 1 but with different JUMP points

      *375C
      CP 22HFE 22
      If the character in register A is not an end of the BASIC line character, then test the character against 22H to see if it is a ".
      *375E
      If the character in register A is not a ", then JUMP forward a few instructions to 376AH.
      *3760
      LD A,(409FH)3A 9F 40
      Load A with the memory contents of 409FH.
      NOTE: 409FH is the DATA FLAG.
      *3763
      XOR 01HEE 01
      XOR A against 1 (Binary: 0000 0001)
      NOTE: XOR is an exclusive OR, meaning that if the 2 things are the SAME the result is 0, and if the 2 things are DIFFERENT, the result is 1.
      *3765
      LD (409FH),A32 9F 40
      Load memory location 409FH with a the XOR'd results.
      NOTE: 409FH is the DATA FLAG.
      *3768
      LD A,22H3E 22
      Load A with 22H, which is a ".
      *376A
      CP 3AHFE 3A
      Compare A against 3AH (which is a :) doing A - 3AH. If A < 3AH, for UNSIGNED items, then the C FLAG is set. If A => 3AH, for UNSIGNED items, then the C FLAG is reset. If A = 3AH then the Z flag is set.
      *376C
      JP NZ,06AAHC2 AA 06
      If A is NOT a : then JUMP to 06AAH.
      NOTE: 06AAH is in the middle of the KEYBOARD DRIVER ENTRY ROUTINE.
      *376F
      LD A,(409FH)3A 9F 40
      Otherwise, Load A with the memory contents of 409FH.
      NOTE: 409FH is the DATA FLAG.
      *3772
      RRA1F
      Rotate the contents of A one bit to the right, with the contents of the carry bit being moved to bit 7 and the contents of bit 0 being moved to the carry bit. If Bit 0 is low then NC will be set.
      *3773
      JP C,06A8HDA A8 06
      If Bit 7 of the DATA FLAG was set, then JUMP to 06A8H.
      NOTE: 06A8H is in the middle of the KEYBOARD DRIVER ENTRY ROUTINE.
      *3776
      RLA17
      Rotate A left one bit, with the contents of BIT 7 being put into the CARRY FLAG and the CARRY FLAG are put into BIT 0. This puts the read data into the CARRY FLAG.
      *3777
      JP 06A3HC3 A3 06
      JUMP to 06A3H.
      NOTE: 06A3H is the REM processor in the middle of the KEYBOARD DRIVER ENTRY ROUTINE.

      *377A - Model 4 Gen 2 - Part of the PRINTER routine - Check to see if we are on a new printable page

      *377A
      LD (IX+05H),05HDD 36 05 00
      Load the memory location pointed to by IX+5 with a 05H.
      NOTE: IX+5 is the number of characters printed.
      *377E
      INC (IX+04H)DD 34 04
      Bump the number of lines already printed (which is tracked in IX+04H)
      *3781
      LD A,(IX+04H)DD 7E 04
      Fetch the number of lines already printed (which is tracked in IX+04H) into Register A
      *3784
      CP (IX+03H)DD BE 03
      Compare the number of lines already printed (held in Register A) against the number of lines left in the page (tracked in IX+03H)
      *3787
      RET NZC0
      If the number of lines already printed doesn't match the number of lines left in the page then RETURN
      *3788
      LD (IX+04H),04HDD 36 04 01
      If they DO match, then set the number of lines already printed to 04H
      *378C
      RETC9
      RETurn to Caller

      *378D - Model 4 Gen 2 - Routine called by the PRINT routine when a 01H or Line Feed or Carriage Return is the current character being printed. This is the same code Frank Durda had used in his XDROM

      *378D
      LD A,C79
      LET Register A = Register C.
      *378E
      CP E0HFE E0
      Compare A against E0H (Binary:1110 0000). Results:
      • If A=E0H it sets the ZERO FLAG
      • If A<E0H then the CARRY FLAG will be set
      • if A>=E0H then the NO CARRY FLAG will be set
      *3790
      JP NC,0043HD2 43 00
      If A>=E0H then JUMP back to 0043H (which USED to be the KBLINE routine, but now just JUMPs to 0434H which is the routine which prints the character held in C).
      *3793
      CP C0HFE C0
      If we are here then A>=E0H, so lets further test to see if A is C0H or higher. Results:
      • If A=C0H it sets the ZERO FLAG
      • If A<C0H then the CARRY FLAG will be set
      • if A>=C0H then the NO CARRY FLAG will be set
      *3795
      JP C,0063HDA 63 00
      If A<C0H then JUMP to 0063H (which jumps to 041FH which is inside the PRINTER ROUTINE; C holds the printable character to be printed as determined by the PRINTER CHARACTER TABLE).
      *3798
      SUB C0HD6 C0
      If we are here then A<E0H, but A=>C0H (i.e., betweem 160 and 191), so SUBTRACT C0H (Decimal: 64; or 1 character below “A”) to Register A.
      *379A
      LD B,00H06 00
      LET Register B = 00H
      *379C
      LD C,A4F
      LET Register C = Register A.
      *379D
      LD HL,(4220H)2A 20 42
      LET Register Pair HL the memory contents of 4220H and 4221H.

      NOTE: 4220H-4221H is the destination device nName for ROUTE Routine. 2 Bytes
      *37A0
      ADD HL,BC09
      LET HL = HL + BC
      *37A1
      LD C,(HL)4E
      LET Register C = the contents of the memory location pointed to by Register Pair HL.
      *37A2
      JP 0063HC3 63 00
      JUMP to 0063H (which jumps to 041FH which is inside the PRINTER ROUTINE; C holds the printable character to be printed as determined by the PRINTER CHARACTER TABLE).

      *37A5 - Model 4 Gen 2 - Screen Print Routine

      *37A5
      LD HL,3C00H21 00 3C
      Load HL with the memory location for the beginning of the video RAM.
      *37A8
      LD A,(HL)7E
      Put the character at the screen location stored in HL into A.
      *37A9
      RLCA07
      Rotate the bits in A left (with Bit 7 going into both the CARRY and Bit 0). This will double as a test of Bit 7 since that is now sitting in the CARRY.
      *37AA
      If Bit 7 of A was HIGH, then A is a graphics character, so skip to 37B3H
      *37AC
      RLCA07
      Rotate the bits in A left (with Bit 7 going into both the CARRY and Bit 0). This will double as a test of Bit 7 since that is now sitting in the CARRY.
      *37AD
      If Bit 7 was high, then A is a non-printable but also not-graphics character, so JUMP to 37B3H to swap in the ROTATED character
      *37AF
      LD A,2EH3E 2E
      Load Register A with a . which is the character which will print instead of non-printable characters.
      *37B1
      Skip the next instruction which replaces the non-printable character with a .
      *37B3
      LD A,(HL)7E
      Replace the non-printable character with a .
      *37B4
      CALL 003BHCD 3B 00
      Call the PRINT CHARACTER routine at 003B (which sends the character in the A register to the printer).
      *37B7
      INC HL23
      Bump HL to the next character on the screen.
      *37B8
      BIT 6,HCB 74
      Check the 6th Bit in H to see if we are at the end of the line (meaning that H is now 64; 1 character beyond the 63 maximum per lime).
      *37BA
      JP NZ,0214HC2 14 02
      If we are at 64, then JUMP to 0214H for a new line.
      *37BD
      LD A,L7D
      Test of end of line by loading A with L.
      *37BE
      AND 3FHE6 3F
      AND the contents of A with 3FH (Binary: 00111111) to turn off Bits 7 and 6, making the maximum number A can be 3FH (Decimal: 63).
      *37C0
      If A hadn't turned to ZERO, then we are NOT at the end of a line, so LOOP back to the top of this routine at 37A8H to process the next character
      *37C2
      CALL 0214HCD 14 02
      GOSUB to 0214H for a new line
      *37C5
      Jump back to top of this routine at 37A8H to process the next character

      *37A5 - Model 4 Gen 2 - Screen Print Routine BUT exits witH Register A at 0 and all FLAGS cleared

      *37C7
      CALL 37A5HCD A5 37
      GOSUB to 37A5H for the screen print
      *37CA
      XOR AAF
      Clear Register A and all the flags
      *37CB
      RETC9
      RETurn to CALLer

      *37CC - Model 4 Gen 2 - TOGGLE the CAPS LOCK

      *37CC
      LD A,01H3E 01
      Load Register A with an 01H
      *37CE
      LD HL,4019H21 19 40
      Load HL with 4019H, which is the CAPS LOCK TOGGLE in the keyboard DCB.
      *37D1
      XOR (HL)AE
      XOR the contents of the CAPS LOCK TOGGLE in RAM against 01H; this effectively toggles it
      *37D2
      LD (HL),A77
      Put the toggled CAPS LOCK TOGGLE back into RAM where such thing is stored
      *37D3
      XOR AAF
      Clear Register A and Clear all FLAGs
      *37D4
      RETC9
      RETurn to CALLER

      *37D5 - Model 4 Gen 2 - Send the Character in Register A to the RS-232, once the RS-232 shows ready to accept that character.

      *37D5
      PUSH AFF5
      Preserve Registers A and F to the top of the STACK
      *37D6
      IN A,(EAH)DB EA
      Poll the RS-232 UART Control Register/Status Register (via Port EAH) into A.
      *37D8
      BIT 6,ACB 77
      Test Bit 6 of the RS-232 Register. It will be 1 if READY TO SEND
      *37DA
      If the UART reported that READY TO SEND is FALSE, LOOP back to 37D6 and poll again.
      *37DC
      POP AFF1
      Restore Registers A and F from the top of the STACK
      *37DD
      OUT (EBH),AD3 EB
      Send the contents held in Register A to the RS-232C Data Register (via Port E8H) so as to send A to the RS-232C.
      *37DF
      RETC9
      RETurn to CALLer

      *37E0 - Model 4 Gen 2 - Set up the RS-232 to DTR On, RTS Off, No parity, 9600 Baud, 1 Stop Bit, and then JUMP to (HL).

      *37E0
      LD A,6DH3E 6D
      Load Register A with a 6DH (Binary: 0110 1101)
      *37E2
      OUT (EAH),AD3 EA
      Send the 01101101 out the RS-232 UART Control Register at Port 0EAH. This sends DTR on, RTS Off, Send BREAK, Disable Parity, 1 Stop Bit, 9600 Baud, and Odd Parity as
      • Bit 0: Data Terminal Ready (1=DTR Off) (Pin 20 of the DB-25)
      • Bit 1: Request to Send (1=RTS Off) (Pin 4 of the DB-25)
      • Bit 2: Break (1=Send Break Signal)
      • Bit 3: Parity Enable (0 = Enable Parity, 1 = Disable Parity)
      • Bit 4: Stop Bits (0 = 1 Stop Bit, 1 = 2 Stop Bits)
      • Bits 5-6: Select Word Length (00 = 5, 01 = 7, 10 = 6, 11 = 8)
      • Bit 7: Parity (0 = Odd, 1 = Even)
      *37E4
      JP (HL)E9
      Jump to whatever (HL) is pointing to.

      *37E5 - Model 4 Gen 2 - UNUSED CODE.

      *37E5
      NOP00
      *37E6
      NOP00
      *37E7
      NOP00
      *37E8
      RST 38HFF
      *37E9
      RST 38HFF

      **37D5-37E9 - Model 4 ROM Student Network Edition - Replace all the above with NOPs


      *37EA - Model 4 Gen 2 - BASIC TIMES (DATE$+" "+TIME$)

      *37EA
      RST 10HD7
      Call the EXAMINE NEXT SYMBOL routine at RST 10H.
      *37EB
      PUSH HLE5
      Save HL (the current position) to the STACK.
      *37EC
      LD A,11H3E 11
      Load A with 11H (Decimal: 17) to set up for a 17 Byte String.
      *37EE
      CALL 2857HCD 57 28
      GOSUB to 2857H to create a 17 byte string.
      *37F1
      LD HL,(40D4H)2A D4 40
      Load HL with the memory contents of (40D4H), which is the string pointer.
      *37F4
      CALL 35BBHCD BB 35
      GOSUB to 35BCH to populate the DATE$ and put it on the screen.
      *37F7
      LD (HL),20H36 20
      Load the memory location pointed to by HL with a SPACE.
      *37F9
      INC HL23
      Increment HL to move 1 character over.
      *37FA
      CALL 35A0HCD A0 35
      GOSUB to 35A0H to populate the TIME$ and put it on the screen.
      *37FD
      JP 2884HC3 84 28
      JUMP to 2884H, which is in the middle of the STRING routine.