Page Customization
0000H-0004H – POWER UP ROUTINE – “START”
0000-0002 Disables the interrupts, clears the A register, then jumps to initialisation routine at 674H
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
(RST 008H)
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”
000DH-000FH – DISK BOOTSTRAP – “$BOOT”
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
(RST 010H)
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.
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
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.
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.)
(RST 020H)
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”
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
(RST 028H)
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
Note: 4015H holds Keyboard DCB – Device type
(RST 030H)
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.
Note: 401DH holds Video DCB – Device type
0038 (RST 38H) This location will pass control to 4012H. This location is only used by a Disk system
(RST 038H)
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.)
Note: 4025H holds Printer DCB – Device type
0040H-0042H – INPUT ROUTINE – “$KEYIN”
*0043H-0045H – Model 4 Gen 1 Routine – Unused Code
*0043H-0045H – Model 4 Gen 2 Routine – Called from new routine at 3790H
0046H-0048H – DRIVER ENTRY ROUTINE – Part 2 – “CIOJ”
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
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
0050H-005FH – KEYBOARD LOOKUP TABLE – “KEYTAB”
This is a table of control characters used by BASIC.
*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.
*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.
0066H – Program control jumps when the RESET button is pressed (Non Maskable Interrupt address)
0069H-0074H NMI INTERRUPT ROUTINE (RESET) – “$INITIO”
*This part of the initialization routine checks to see if a disk drive is connected. If so, it will jump to 00H. (This is why the reset button will reinitialize DOS.)
*006C-0074H – Model 4 Gen 1 – Unused Code
*006C-0074H – Model 4 Gen 2
*006D-0070H – Model 4 Gen 2 – New Code for Gen 2 – Called from 02C3H when the BREAK key is hit
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.
Note: 4080H-408DH is a division support routine
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.)
Note: 40A7H-40A8H holds the input Buffer pointer
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
Note: 4152H-41A3H holds Disk Basic links
NOTE: C3H is the first byte of a 3 byte JUMP xxxxH command.
NOTE: C9H is a RET instruction.
00A8H – VIDEO AND PRINTER ROUTINE
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).
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.
OR L7C
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
Note: 40A0H-40A1H holds the start of string space pointer
*00FFH – Model 4 Gen 1 Code
*00FFH – Model 4 Gen 2 Code
0105H-0110H – MESSAGE STORAGE
The “MEMORY SIZE” message is located here
012DH-0131H – ?L3 ERROR ROUTINE – “NAVERR”
0132H-0134H – LEVEL II BASIC POINT COMMAND ENTRY POINT – “GRPHCS” or “POINT”
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.
0135H-0137H – LEVEL II BASIC SET COMMAND – “SET”
0138H-0139H – LEVEL II BASIC RESET COMMAND ENTRY POINT – “RESET”
013AH-019CH GRAPHICS ROUTINE
Common code for SET/RESET/POINT – A will be 0 if POINT, 80H if SET and 1 for RESET.
This is a suitable entry point for the graphics routines. (see Part 2)
019DH-01C8H – LEVEL II BASIC INKEY$ ROUTINE – “INKEY”
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.
Note: 4099H holds the last key pressed
Note: 40AFH holds current variable’s type flag
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.
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.
*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.
“PRSCN”
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.
Difference between M1 and M3: 01F0H contains CALL 0221H instruction on Model I, and CALL 0214H instruction on Model III.
Difference between M1 and M3: Contains LD B,5CH instruction on Model I, JR 01DCH instruction on Model III.
*01D9 – Model 4 Gen 2 Routine
*01DC-01E5H – Model 4 Gen 2 Routine to wait for either PRINTER READY or a BREAK key
*01E6-01F7H – Model 4 Gen 2 Routine – Called from 0102H after displaying the CASS, MEMORY SIZE, and RADIO SHACK messages
NOTE: 4177H is the TIME$ vector.
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.
“CSOFF”
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.
OR C
0202 – Message Storage Location
*0210 – Model 4 Gen 1 – These instructions are never called or used.
*0210 – Model 4 Gen 2 – These instructions are never called or used.
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).
0216-021A – Display a Carriage 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.
“VIDLINE”
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.
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.
022EH – Final cleanup before entering BASIC … Enable Interrupts and JUMP to READY prompt
*0232 – Model 1 Gen 1 – These instructions are never called or used.
*0232 – Model 1 Gen 2 – These instructions are never called or used.
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.
“CSIN”
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.
0243-024B – CASSETTE ROUTINE – Read a Byte from Cassette
024D-0252 – CASSETTE ROUTINE – Write a Byte to Cassette
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.
0261H-0263H – CASSETTE ROUTINE – “TWOCSO”
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).
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.
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.
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.
“CSHWR”
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.
“KBBRK”
0293 – CASSETTE ROUTINE – Read the Header and Sync Bytes
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.
0298 – Enable the Clock Display – “CLKON”
No entry conditions. A is altered by this routine.
“CLKON”
NOTE: 4210H holds the bit mask for port ECH. Port ECH stores miscellaneous controls.
NOTE: 4210H holds the bit mask for port ECH. Port ECH stores miscellaneous controls.
02A1 – Disable the Clock Display – “CLKOFF”
No entry conditions. A is altered by this routine.
“CLKOFF”
NOTE: 4210H holds the bit mask for port ECH. Port ECH stores miscellaneous controls.
*02A8 – Model 4 Gen 1 – These instructions are never called or used.
*02A8 – Model 4 Gen 2 – These instructions are never called or used.
02A9H-0329H – LEVEL II SYSTEM ROUTINE-ENTRY POINT – “ENBLK”
Note: 40DFH-40E0H is also used by DOS
In NEWDOS 2.1, this is called during a SYSTEM operation
*02B5-02B7 – Model 4 Gen 1 – Set the STACK Pointer.
*02B5-02B7 – Model 4 Gen 2 – Set the STACK Pointer.
*02C3-02C5 – Deal with the BREAK Key.
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.
0314H – Read 2 bytes from the tape into Register Pair HL – “CADRIN”
031DH – Execute the Cassette Program which was Loaded – “GODO”
Note: 40DFH-40E0H is also used by DOS
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.
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.
In NEWDOS 2.1, this writes to the system output device
Note: 409CH holds the current output device flag: -1=cassette, 0=video and 1=printer
033AH-0347H – OUTPUT ROUTINE – “OUT2D”
A Print routine which performs the same function as 33H except that it doesn’t destroy the contents of the DE Register Pair. This means that all the general purpose registers are saved, which is often desirable
To use a ROM call to print a single character at the current cursor position, and to update the cursor position, load the ASCII value of the character into the A Register And then CALL 033AH.
To display special functions using a ROM call, load the A Register with the value given below for the special function and then CALL 033AH.
- Backspace and erase previous character – 08H
- Carriage return and linefeed – 0DH
- Turn on cursor – 0EH
- Turn off cursor – 0FH
- Convert to 32 characters per line mode – 17H
- Backspace cursor – 18H
- Advance cursor one position – 19H
- Downward line feed – 1AH
- Upward line feed – 1BH
- Home (cursor to upper left corner) – 1CH
- Move cursor to beginning of current line – 1DH
- Erase from cursor position to end of line – 1EH
- Erase from cursor position to end of screen – 1FH
Save the value in Register Pair DE on the STACK
Note: 40A6H holds the current cursor line position
0348H-0357H – VIDEO ROUTINE – “DSPPOS”
Note: 403DH-4040H is used by DOS
Note: 4020H-4021H holds Video DCB – Cursor location
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
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.
Note: 4099H holds the Last key pressed
Note: 40A6H holds the current cursor line position
In NEWDOS 2.1, this is the satrt of keyboard input
Note: 40A7H-40A8H holds the input Buffer pointer
Note: 40A7H-40A8H holds the input Buffer pointer
0384H-038AH – KEYBOARD ROUTINE – “INCHR”
Waits for keypress
038BH-039BH – PRINTER ROUTINE – “FINLPT”
Note: 409CH holds the current output device flag: -1=cassette, 0=video and 1=printer
Note: 409BH holds the printer carriage position
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.
Note: 409BH holds the printer carriage position
Note: 409BH holds the printer carriage position
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).
- 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.
NOTE: 03F5H prints a character while maintaining page height and width.
NOTE: IX+4 is the number of lines printed.
NOTE: 0F8H is the printer port. If you put data to it, it prints it. Otherwise, Bits 4-7 hold printer status.
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.
03F5-0424 – Model 4 Gen 1 Inside the PRINTER ROUTINE – Print A Character Honoring Page Height and Width
NOTE: IX+5 is the number of characters printed.
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.
NOTE: IX+5 is the number of characters printed.
NOTE: 0F8H is the printer port. If you put data to it, it prints it. Otherwise, Bits 4-7 hold printer status.
NOTE: IX+5 is the number of characters printed.
NOTE: F8H is the printer port. If you put data to it, it prints it. Otherwise, Bits 4-7 hold printer status.
NOTE: IX+5 is the number of characters printed.
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.
NOTE: IX+5 is the number of characters printed.
NOTE: IX+4 is the number of lines printed.
NOTE: IX+3 is the maximum number of lines per page.
NOTE: IX+4 is the number of lines printed.
0440-044A – Model 4 Gen 1 – Inside the PRINTER ROUTINE – Subroutine to wait for PRINTER READY, but Honor a BREAK Key
03C2H-044AH – Model 4 Gen 2 PRINTER ROUTINE
- 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.
NOTE: 0414H will process a carriage return.
NOTE: IX+4 is the number of lines printed.
NOTE: 0F8H is the printer port. If you put data to it, it prints it. Otherwise, Bits 4-7 hold printer status.
NOTE: IX+5 is the number of characters printed.
NOTE: IX+4 is the number of number of lines printed.
03C2H-044AH – Model 4 Gen 2 PRINTER ROUTINE – Jumped here from 03C5 if the character in A was >= a SPACE
03F6 – Model 4 Gen 2 PRINTER ROUTINE – Jumped here from 03EDH if the character stored at 41FBH is a ZERO
040A – Model 4 Gen 2 PRINTER ROUTINE – Jumped here from 03FAH if the character stored at 41FCH is NOT a ZERO
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
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
NOTE: 0F8H is the printer port. If you put data to it, it prints it. Otherwise, Bits 4-7 hold printer status.
NOTE: F8H is the printer port. If you put data to it, it prints it. Otherwise, Bits 4-7 hold printer status.
NOTE: IX+5 is the number of characters printed.
044B-0451 – Inside the PRINTER ROUTINE – Subroutine to check to see if PRINTER READY by polling port F8H
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.
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).
0452-0468 – Initialize KB, DI, PR, RI, RO and RN
*0469-046A – Model 4 Gen 1 – These instructions are never called or used.
*0469-046A – Model 4 Gen 2 – These instructions are never called or used.
046B-0472 – This subroutine zeroes out the PROTECTED SCREEN LINES (if any) and point HL to the start of data
NOTE: 4214H is the number of protected video lines.
NOTE: 40A4 is the DATA POINTER.
0473H-04B1H – Video Display DCB.
LD H,(IX+04H)
NOTE: IX+05H holds the character at the cursor position.
- 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=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.
048B – Inside the CURSOR MANAGEMENT ROUTINE – If we are here, the character is not a control character, tab, or special characters.
NOTE: IX+05H holds the character at the cursor position.
NOTE: IX+05H holds the character at the cursor position.
NOTE: IX+6 holds the cursor character.
NOTE: B0H is a two pixel wide graphic character located below the letter line.
04B2 – Cursor Management – Move to the start of the line.
04B7 – Cursor Management – We have EITHER a TAB or SPECIAL CHARACTER, so figure it out, and proceed accordingly.
04CC – Cursor Management – CURSOR ON.
NOTE: IX+05H holds the character at the cursor position.
04D1 – Cursor Management – CURSOR OFF (Jumped to from 0539H)
04D4 – Cursor Management – HOME CURSOR
NOTE: 3C00H is the start of the video display RAM.
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.
NOTE: 4214H is the number of protected video lines.
04EB – Cursor Management – BACKSPACE
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.
04F7 – Cursor Management – CURSOR BACK
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.
0504 – Cursor Management – CURSOR DOWN
0509 – Cursor Management – CURSOR FORWARD.
050E – Cursor Management – CURSOR UP
NOTE: FFC0H is -64, or 1 line length.
0513-0520 – Cursor Management – Turn on DOUBLE SIZE and put the cursor on EVEN columns only.
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.
0521-055F – Cursor Management – Process Special Characters
NOTE: 08H is a BACKSPACE.
NOTE: 0AH is a LINE FEED.
NOTE: 0DH is a CARRIAGE RETURN.
NOTE: 0EH is a CURSOR ON.
NOTE: 0FH is a CURSOR OFF.
0560 – Cursor Management – Control Characters.
056B – Cursor Management – Special and Alternative Characters
NOTE: 4210H holds the bit mask for port ECH. Port ECH stores miscellaneous controls.
NOTE: Bit 3 of ECH is the SPECIAL CHARACTER SELECT. It will be 0 for KANA and 1 for MISC.
NOTE: 4210H holds the bit mask for port ECH. Port ECH stores miscellaneous controls.
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.
NOTE: HL should be the current screen location and A should be the character.
NOTE: 4210H holds the bit mask for port ECH. Port ECH stores miscellaneous controls.
0588 – Cursor Management – Scroll the Screen
NOTE: 4214H is the number of protected video lines.
05AF – Cursor Management – CARRIAGE RETURN or LINE FEED
05BC – Cursor Management – CLEAR TO END OF LINE
05C5 – Cursor Management – CLEAR TO END OF SCREEN
*05D1-05D8 – Model 4 Gen 1 Message Storage Area
*05D1-05D8 – Model 4 Gen 2 Message Storage Area
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
0619H – Part of the Display routine – “KLNCLR”
Clear the screen
0622H – Part of the Display routine – “KLNCNL”
Cancel the accumulated line
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
0641H – Part of the Display routine – “KLNETB”
Turn on 32 Character Mode
0646H – Part of the Display routine – “KLNHT”
Process a horizontal tab
0661H – Part of the Display routine – “KLNBRK”
Process a Carriage Return and Automatic Line Feed
0674 – KEYBOARD DRIVER ENTRY ROUTINE
069AH – This subroutine CLEARS the DATA FLAG, sets up a buffer of 255 bytes (held in D) and JUMPS to 2B8DH.
The next set of instructions tests the buffer backwards for M, E, and R, and RETURNS out if those are not found.
At this point BC, BC+1, and BC+2 were REM, so check backwards again for a : and if not, RETURN.
At this point BC, BC+1, BC+2, and BC+3 were :REM.
INC D
INC D
INC D
06E5H – This subroutine sets the DATA FLAG to “BIT 1 HIGH” to indicate that we are in a DATA command.
06EFH – This subroutine sets the DATA FLAG to “BIT 2 HIGH” to indicate that we are in a REM command.
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.
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.
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.
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
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.
At this point, the smaller number is in ABCD, so we proceed with the math.
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.
0754H-077CH – SINGLE PRECISION MATH ROUTINE – “FADD3”
This routine will subtract CDEB from ((HL)+0,1,2),0.
With that out the way, we need to make sure we have a positive mantissa (or else we will need to negate the 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.
This routine will ZERO out the ACCumulator, changing only Register A in the process. A will exit as 0.
077DH-07A7H – SINGLE PRECISION MATH SUPPORT ROUTINE – “NORM2”
If we are here, then we have a fully normalized result, so let us continue.
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
07A8H-07B6H – SINGLE PRECISION MATH SUPPORT ROUTINE – “ROUNDA”
This is a subroutine within the ROUND round. This will add one to C/D/E.
07B2H – ?OV ERROR entry point – “OVERR”
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.
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.
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.
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 …
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.
07F8-07FB – SINGLE PRECISION CONSTANT STORAGE LOCATION
07FC-0808 – SINGLE PRECISION CONSTANTS STORAGE LOCATION2
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)
The next two instructions are commented in the original ROM source code as: Get SQR(.5)
The next two instructions save SQR(.5) to the STACK
The next two instructions restore SQR(.5) from the STACK
The next two instructions get SQR(2)
The next two instructions are commented in the original ROM source code as: Get -1/2
The instructions are commented in the original ROM source code as: Get LN(2)
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.
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.
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.
0897H-08A1H – SINGLE PRECISION MATH ROUTINE– – “DIV10”
This routine divides the ACCumulator by 10. Every Register is used.
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.
08AE
INC (HL)34
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.
08DD
DEC A3C
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.
0914H-0930H – SINGLE PRECISION MATH ROUTINE – “MULDIV”
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
093EH-0954H – SINGLE PRECISION MATH ROUTINE – “MUL10”
This routine multiplies the ACCumulator by 10. Every register is modified.
093E-0940– ↳ MUL10Call 09BF which loads the SINGLE PRECISION value in the ACCumulator into Register Pair BC/DE
0941LD A,B78Load Register A with the value of the exponent (from Register B)
0942OR AB7Check to see if the exponent in Register A is equal to zero, because if the exponent is 0 then so is the number!
0943RET ZC8If the single precision value in Register Pairs BC and DE is equal to zero, then RETurn
0944-0945ADD 02HC6 02Multiply the value of the exponent in Register A by four (by adding 2 to the exponent)
0946-0948Display an ?OV ERROR if the adjusted exponent in Register A is too large
0949LD B,A47Put the exponent back into Register B
094A-094CMultiply 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-094FPrepare 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
0950INC (HL)34Increment 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
0951RET NZC0Return if the new value in the ACCumulator is in an acceptable range
0952-0954Display 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– ↳ SIGNLD A,(4124H)LD A,(FAC)3A 24 41Prepare 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
0958OR AB7Check to see if the exponent in Register A is equal to zero
0959RET ZC8Return if the single precision value in the ACCumulator is equal to zero
095A-095C– ↳ SIGNCLD A,(4123H)LD A,(FAC-1)3A 23 41Load Register A with the SIGN of the ACCumulator
095D-095ECP 2FHFE 2FZ-80 Trick. If passing through, this will check the value of Register A and skip the next CPL instruction.
095F– ↳ ICOMPSRLA17Put the value of the sign bit in Register A into the CARRY FLAG
0960
“SIGNS”SBC A,A9FIf 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
0961RET NZC0If the CARRY FLAG was 1, then the number is negative, and we want to RETurn
0962– ↳ INRARTINC A3CIncrement the value in Register A so that Register A will be equal to 1 if the single precision value in the ACCumulator is positive
0963RETC9RETurn 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– ↳ FLOATLD B,88H06 88Load Register B with an exponent for an integer value
This routine will float the singed number in B/A/D/E. All registers are modified.
0969-096B– ↳ FLOATRLoad Register Pair HL with the address of the exponent in the ACCumulator
096CLD C,A4FLoad Register C with the High Order/MSB of the integer value
096DLD (HL),B70Save the exponent in Register B into the ACCumulator at the location of the memory pointer in Register Pair HL
096E-096FLD B,00H06 00Load Register B with zero to zero the overflow byte
0970INC HL23Increment the memory pointer in Register Pair HL to now point to the sign of the number in the ACCumulator
0971-0972LD (HL),80H36 80Assume a positive number by putting an 80H there
0973RLA17Shift the value of the sign bit into the CARRY FLAG
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– ↳ ABSGOSUB to VSIGN to get the SGN of the ACCumulator into Register A
097ARET PF0If 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– ↳ VNEGRST 20HGETYPEE7We 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 Flags Register A Integer NZ/C/M/E -1 String Z/C/P/E 0 Single Precision NZ/C/P/O 1 Double Precision NZ/NC/P/E 5
This 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– ↳ NEGLoad Register Pair HL with the address of the MSB (which holds the SIGN bit) in the ACCumulator.
0985LD A,(HL)7ELoad 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-0987XOR 80HXOR 1000 0000EE 80Complement the sign bit in the MSB in Register A. Since we know the number is negative, this is really just switching it to positive.
0988LD (HL),A77Save 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
0989RETC9RETurn 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
This routine will convert a signed number (held in Register A) into an integer.
098D– ↳ CONIALD L,A6FLoad Register L with the result of the sign test in Register A
098ERLA17Shift the sign bit in Register A into the Carry flag
098FSBC A,A9FAdjust 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
0990LD H,A67Save the adjusted value in Register A in Register H
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– ↳ VSIGNRST 20HGETYPEE7We 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 Flags Register A Integer NZ/C/M/E -1 String Z/C/P/E 0 Single Precision NZ/C/P/O 1 Double Precision NZ/NC/P/E 5
0998-099ASince 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-099DLD HL,(4121H)LD HL,(FACLO)2A 21 41At 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– ↳ ISIGNLD A,H7CLoad Register A with the MSB (which holds the SIGN bit) of the integer value in Register H
099FOR LB5Check to see if the integer value in the ACCumulator is equal to zero
09A0RET ZC8Return if the integer value in the ACCumulator is equal to zero
09A1LD A,H7CIf 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
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– ↳ PUSHFEX DE,HLEBPreserve (HL) by swapping HL and DE
09A5-09A7LD HL,(4121H)LD HL,(FACLO)2A 21 41Load Register Pair HL with the LSB and the NMSB of the single precision value in the ACCumulator
09A8EX (SP),HLE3Swap (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
09A9PUSH HLE5Save the return address in Register Pair HL on the STACK
09AA-09ACLD HL,(4123H)LD HL,(FAC-1)2A 23 41Load Register Pair HL with the exponent and the High Order/MSB of the single precision value in the ACCumulator
09ADEX (SP),HLE3Swap (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
09AEPUSH HLE5Save the return address in Register Pair HL on the STACK
09AFEX DE,HLEBRestore the original Register Pair HL from DE
09B0RETC9RETurn 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– ↳ MOVFMLoad 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– ↳ MOVFREX DE,HLEBLoad Register Pair HL with the NMSB and the LSB of the single precision value in Register Pair DE.
09B5-09B7LD (4121H),HLLD (FACLO),HL22 21 41Save the NMSB and the LSB of the single precision value into the ACCumulator (at the locations pointed to by Register Pair HL)
09B8LD H,B60Let HL = BC (so the High Orders/MSB + Exponent) … part 1 …
09B9LD L,C59… part 2
09BA-09BCLD (4123H),HLLD (FAC-1),HL22 23 41Save the exponent and the MSB of the single precision value into the ACCumulator pointed to by Register Pair HL
09BDEX DE,HLEBRestore the original HL from DE
09BERETC9RETurn 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-09C1Load register pair HL with the starting address for a single precision value in REG 1.
09C2– ↳ MOVRMLD E,(HL)5ELoad 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
09C3INC HL23Increment the value of the memory pointer in Register Pair HL to point to the middle order/NMSB number
09C4– ↳ GETBCDLD D,(HL)56Load Register D with the NMSB of the single precision value in the ACCumulator at the location of the memory pointer in Register Pair HL
09C5INC HL23Increment the value of the memory pointer in Register Pair HL to point to the high order/MSB number
09C6LD C,(HL)4ELoad Register C with the MSB of the single precision value in the ACCumulator at the location of the memory pointer in Register Pair HL
09C7INC HL23Increment the value of the memory pointer in Register Pair HL to point to the exponent
09C8LD B,(HL)46Load 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– ↳ INXHRTINC HL23Increment the value of the memory pointer in Register Pair HL so that it points to the beginning of the next number
09CARETC9RETurn 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– ↳ MOVMFLoad 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– ↳ MOVELD B,04H06 04Load Register B with the number of bytes to be moved for a single precision value so that B will act as a counter.
09D0-09D1Jump 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– ↳ MOVVFMEX DE,HLEBExchange 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– ↳ VMOVELD A,(40AFH)LD A,(VALTYP)3A AF 40Load 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!
09D6LD B,A47Load 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– ↳ MOVE1LD A,(DE)1ATop 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)
09D8LD (HL),A77and then Save the value in Register A at the location of the memory pointer in Register Pair HL
09D9INC DE13Increment the value of the memory pointer in Register Pair DE
09DAINC HL23Increment the value of the memory pointer in Register Pair HL
09DBDEC B05Decrement the value of the byte counter in Register B
09DERETC9RETurn 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– ↳ UNPACKLoad Register Pair HL with the address of the MSB (including the SIGN) of the value in the ACCumulator
09E2LD A,(HL)7ELoad Register A with the MSB (and SIGN) of the value in the ACCumulator at the location of the memory pointer in Register Pair HL
09E3RLCA07Duplicate the sign into the CARRY and the LSB
09E4SCF37Set the Carry flag to restore the hidden “1” for the mantissa
09E5RRA1FTurn 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.
09E6LD (HL),A77Save the adjusted High Order/MSB+Sign in Register A in the ACCumulator at the location of the memory pointer in Register Pair HL
09E7CCF3FInvert the value of the sign bit in the Carry flag
09E8RRA1FMove 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 HL23Increment the value of the memory pointer in Register Pair HL twice to now point to the temporary sign byte
09EBLD (HL),A77Save the complemented sign (in Register A) to the location of the memory pointer in Register Pair HL
09ECLD A,C79Load Register A with the MSB+SIGN of the single precision value in Register C
09EDRLCA07Duplicate the sign in both the CARRY FLAG and the LSB
09EESCF37Set the Carry flag to restore the hidden “1” for the mantissa
09EFRRA1FRestore 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.
09F0LD C,A4FLoad Register C with the adjusted High Order (MSB+Sign) in Register A
09F1RRA1FMove 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.
09F2XOR (HL)AECombine the value of the sign bit of the ACCUMULATOR and the SIGN BIT of the Registers
09F3RETC9RETurn 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– ↳ VMOVFAThis 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– ↳ VMOVFMLoad Register Pair DE with the return address of the routine that does an exchange and then falls into the MOVE1 routine.
09FCH-0A0BH – LEVEL II BASIC MATH ROUTINE – “VMOVAF”
This is the opposite of 9F4H. This routine moves a number of bytes (the number depending on the value stored in the VALTYPE) from the ACCumulator to (HL). All Registers except C are affected.
09FC-09FE– ↳ VMOVAFEntered 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– ↳ VMOVMFWhen entered from here, we need to load Register Pair DE with the return address of the MOVE routine.
0A02– ↳ VMVVFMPUSH DED5When entered here, save Register Pair DE (which, if passed through, is a return address) on the STACK
0A03-0A05– ↳ VDFACSEntered 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
0A06RST 20HGETYPEE7We 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 Flags Register A Integer NZ/C/M/E -1 String Z/C/P/E 0 Single Precision NZ/C/P/O 1 Double Precision NZ/NC/P/E 5
0A07RET CD8If that test is anything other than double precision, return out of this subroutine to the address which was fed in
0A08-0A0AIf 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.
0A0BRETC9RETurn 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– ↳ FCOMPLD A,B78First we need to check to see if ARG is zero, so load Register A with the value of the exponent in Register B
0A0DOR AB7Set the flags based on Register A
0A0E-0A10If the exponent in Register A is equal to zero, then JUMP to SIGN
0A11-0A13Set up the destination address to use on a RETurn by first loading Register Pair HL with the address to the FCOMPS routine
0A14PUSH HLE5Save the return address in Register Pair HL on the STACK
0A18LD A,C79If 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
0A19RET ZC8If the ACCumulator was zero, RETurn with Register A holding Register C
0A1A-0A1CLoad Register Pair HL with the address of the MSB+SIGN in Register A
0A1DXOR (HL)AECheck to see if the signs of the ACCumulator and the ARG are the same via a XOR
0A1ELD A,C79If 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
0A1FRET MF8If the signs are different, RETurn
0A20-0A22Now that we have resolved the signs, JUMP to FCOMP2 to check the rest of the numbers
0A23– ↳ FCOMPDRRA1FIf 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.
0A24XOR CA9Combine the value of the MSB/SIGN of the single precision value in Register C with the value in Register A
0A25RETC9With Register A now set, RETurn to CALLer
0A26H-0A38H – Part of the SINGLE PRECISION COMPARISON ROUTINE – “FCOMP2”
0A26INC HL23Increment 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
0A27LD A,B78Load Register A with the value of the exponent for the single precision value held in ARG (stored in Register B)
0A28CP (HL)BECheck 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
0A29RET NZC0If 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
0A2ADEC HL2BDecrement 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
0A2BLD A,C79Load Register A with the HIGH ORDER/MSB of the single precision number in ARG (stored in Register C)
0A2CCP (HL)BECheck 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
0A2DRET NZC0If 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
0A2EDEC HL2BDecrement 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
0A2FLD A,D7ALoad Register A with the MIDDLE ORDER/NMSB of the single precision number in ARG (stored in Register D)
0A30CP (HL)BECheck 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
0A31RET NZC0If 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
0A32DEC HL2BDecrement 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
0A33LD A,E7BLoad Register A with the LOW ORDER/LSB of the single precision number in ARG (stored in Register E)
0A34SUB (HL)96Use 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.
0A35RET NZC0Return if the value of the LSB in the ACCumulator isn’t the same as the value of the LSB in Register A
0A36POP HLE1If we are here then the numbers are the same so we need to get the extra data off of the STACK
0A37POP HLE1Clear the stack
0A38RETReturn 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– ↳ ICOMPLD A,D7AFirst we test the signs, so load Register A with the SIGN of the integer value in Register D
0A3AXOR HACCheck 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
0A3BLD A,H7CIf 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-0A3EIf the sign bits are NOT the same, JUMP to ICOMPS to check the numbers
0A3FCP DBAIf 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-0A42if 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
0A43LD A,LLoad register A with the LSB of the integer value in register L.
0A44SUB E93Use 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-0A47If 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
0A48RETC9If we are here, then two things. First, they are the same. Second, A is zero. So RETurn to CALLer
0A49-0A4B– ↳ DCOMPDLoad 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-0A4EGo move the double precision value pointed to by Register Pair DE to ARG (a/k/a REG 2)
0A4F-0A51– ↳ XDCOMPLoad Register Pair DE with the address of the exponent in ARG (a/k/a REG 2)
0A52LD A,(DE)1ALoad 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
0A53OR AB7Check to see if the double precision value in ARG (a/k/a REG 2) is equal to zero
0A54-0A56If 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-0A59Load Register Pair HL with a return address to the FCOMPS routine
0A5APUSH HLE5Save the return address in Register Pair HL on the STACK
0A5B-0A5DGo check to see if the double precision value in the ACCumulator is equal to zero
0A5EDEC DE1BDecrement 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)
0A5FLD A,(DE)1ALoad 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
0A60LD C,A4FPresetve the MSB+SIGN of the double precision value in ARG (a/k/a REG 2) into Register C
0A61RET ZC8If the number in the ACCumulator = 0, then the sign of the result is the sign of ARG, so RETurn wto FCOMPS
0A62-0A64Load Register Pair HL with the address of the SIGN of the double precision value in the ACCumulator
0A65XOR (HL)AECheck 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
0A66LD A,C79In case they are the same, get the sign from C into Register A.
0A67RET MF8If they are NOT the same, RETurn to FCOMPS to set set Register A
0A68INC DE13Increment the value of the memory pointer in Register Pair DE so that DE now points to the exponent of ARG
0A69INC HL23Increment the value of the memory pointer in Register Pair HL so that HL now points to the exponent of the ACCumulator
0A6A-OA6BLD B,08H06 08Load Register B with the number of bytes to be compared, as B will act as a counter
0A6C– ↳ DCOMP1LD A,(DE)1ALoad Register A with a byte from the double precision number in ARG (pointed to by Register Pair DE)
0A6DSUB (HL)96Use subtraction to compare that byte from ARG with the correspondible byte from the ACCumulator (pointed to by Register Pair HL)
0A6E-0A70If the NZ is set, then the numbers are different o JUMP to FCOMPD to set up Register A
0A71DEC DE1BIf 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)
0A72DEC HL2Band the next byte of the ACCumulator (so decrement the value of the memory pointer in Register Pair HL)
0A73DEC B05and to decrease the byte counter (so Decrement the number of bytes remaining to be compared in Register B)
0A74-0A75The 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
0A76POP BCC1If 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
0A77RETC9RETurn 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– ↳ DCOMPGOSUB to compare the double precision value in ARG (a/k/a REG 2) to the double precision value in the ACCumulator
0A7B-0A7DIf 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
0A7ERETC9RETurn 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– ↳ FRCINTRST 20HGETYPEE7We 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 Flags Register A Integer NZ/C/M/E -1 String Z/C/P/E 0 Single Precision NZ/C/P/O 1 Double Precision NZ/NC/P/E 5
0A80-0A82LD HL,(4121H)LD HL,(FACLO)2A 21 41Just 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)
0A83RET MF8If that test showed we have an INTEGER, then return out of this subroutine
0A84-0A86If that test showed we have a STRING, Display a ?TM ERROR message
0A87-0A89If that test shows we have DOUBLE PRECISION, call 0AB9H to convert the number to single precision
0A8A-0A8CJust in case the number is too big, pre-load HL with the RETurn address to the ?OV ERROR routine
0A8DPUSH HLE5Save 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– ↳ CONISLD A,(4124H)LD A,(FAC)3A 24 41Load Register A with the exponent for the single precision value in the ACCumulator
0A91-0A92CP 90HFE 90Check to see if the exponent for the single precision value in the ACCumulator in Register A indicates more than 16 bits of precision
0A93-0A94If 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-0A97If 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
0A98EX DE,HLEBLoad Register Pair HL with the integer value that was put into Register Pair DE by QINT
0A99– ↳ CONIS1POP DED1Get 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– ↳ MAKINTLD (4121H),HLLD (FACLO),HL22 21 41Save the integer value in Register Pair HL as the current value in the ACCumulator.
0A9D-0A9E– ↳ VALINTLD A,02H3E 02Load Register A with an integer number type flag.
0A9F-0AA1– ↳ CONISDLD (40AFH),ALD (VALTYP),A32 AF 40Save 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
0AA2RETC9RETurn to CALLer
0AA3H – LEVEL II BASIC CONVERSION ROUTINE – “CONIS2”
0AA3-0AA5– ↳ CONIS2LD BC,9080H01 80 90This 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-0AA8LD DE,0000H11 00 00Load 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-0AABCall 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.
0AACRET NZC0If 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
0AADLD H,C61If 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
0AAELD L,D6ALoad Register L with the NMSB of the single precision value in Register D
0AAF-0AB0Jump 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– ↳ FRCSNGRST 20HGETYPEE7We 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 Flags Register A Integer NZ/C/M/E -1 String Z/C/P/E 0 Single Precision NZ/C/P/O 1 Double Precision NZ/NC/P/E 5
0AB2RET POE0IF PO is set, then we have SINGLE PRECISION number already, so nothing to do! RETurn out of this subroutine
0AB3-0AB5If that test shows we have an INTEGER, jump to 0ACCH to convert it
0AB6-0AB8If 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– ↳ CONSDMove 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
0ABFLD A,B78Next we need to see if the number is zero, so load Register A with the exponent of the double precision value in Register B
0AC0OR AB7Check to see if the exponent in the ACCumulator is equal to zero
0AC1RET ZC8If the exponent is zero, then the number is zero, so RETurn
0AC2-0AC4We 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-0AC7Load 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)
0AC8LD B,(HL)46Loaded Register B with the chopped number, as that is where the ROUND routine expects the number to be
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– ↳ CONSILD HL,(4121H)LD HL,(FACLO)2A 21 41Load Register Pair HL with the integer value from the ACCumulator
0ACF-0AD1– ↳ CONSIHGo set the current number type flag to single precision
0AD2LD A,H7CNow we need to prepare the registers for the FLOATR routine. First, load Register A with the MSB of the integer value in Register H
0AD3LD D,L55Load Register D with the LSB of the integer value in Register L
0AD4-0AD5LD E,00H1E 00Zero Register E
0AD6-0AD7LD B,90H06 90Load Register B with the initial maximum exponent
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– ↳ FRCDBLRST 20HGETYPEE7We 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 Flags Register A Integer NZ/C/M/E -1 String Z/C/P/E 0 Single Precision NZ/C/P/O 1 Double Precision NZ/NC/P/E 5
0ADCRET NCD0If that test shows we have already a DOUBLE PRECISION number, then we are done, so RETurn out of the subroutine
0ADD-0ADFIf that test shows we have a STRING, Display a TM ERROR message
0AE0-0AE2If 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– ↳ CONDSLD HL,0000H21 00 00Load Register Pair HL with zero so we can zero out the ACCumulator
0AE6-0AE8LD (411DH),HLLD (DFACLO),HL22 1D 41Zero out the first and second bytes of the double precision number in the ACCumulator.
Note: 411DH-4124H holds ACCumulator
0AE9-0AEBLD (411FH),HLLD (DFACLO+2),HL22 1F 41Zero out the third and fourth bytes of the double precision number in the ACCumulator
0AEC-0AED– ↳ VALDBLLD A,08H3E 08Load Register A with a double precision number type flag
0AEEH-0AF3H – LEVEL II BASIC MATH ROUTINE – “VALSNG”
0AEELD BC,043EH01 3E 04Z-80 Trick. If passing through to this routine, BC will be modified but the next instruction will be skipped.
0AF1-0AF3However 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– ↳ CHKSTRRST 20HGETYPEE7We 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 Flags Register A Integer NZ/C/M/E -1 String Z/C/P/E 0 Single Precision NZ/C/P/O 1 Double Precision NZ/NC/P/E 5
0AF5RET ZC8If 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– ↳ TMERRLD E,18H1E 18Load Register E with a ?TM ERROR code.
This is the entry point for the TM ERROR
0AF8-0AFADisplay 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– ↳ QINTLD B,A47Load 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.
0AFCLD C,A4FLoad Register C with the exponent of the single precision number in Register A
0AFDLD D,A57Load Register D with the exponent of the single precision number in Register A
0AFELD E,A5FLoad Register E with the exponent of the single precision number in Register A
0AFFOR AB7Check to see if the single precision number in the ACCumulator is equal to zero
0B00RET ZC8If 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.
0B01PUSH HLE5Save the value in Register Pair HL on the STACK
0B02-0B04Call 09BF which loads the SINGLE PRECISION value in the ACCumulator into Register Pair BC/DE
0B05-0B07Go turn on the sign bit of the single precision value in Register Pairs BC and DE
0B08XOR (HL)AESet the sign bit according to the sign of the value at the location of the memory pointer in Register Pair HL
0B09LD H,A67Preserve the sign of the numbers into Register H
0B0A-0B0CIf the number was negative, we need to substract 1 from the LOW ORDER/LSB and to do that we GOSUB to QINTA
0B0D-0B0ELD A,98H3E 98Next 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
0B0FSUB B90and then subtract the exponent in Register B from the exponent in Register A
0B10-0B12Shift the single precision value in Register Pairs BC and DE to get rid of any fractional bits via a GOSUB to SHIFTR.
0B13LD A,H7CRestore the SIGN back into Register A from Register H
0B14RLA17Put the sign bit into the Carry flag so that it won’t get changed.
0B15-0B17If 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-0B19LD B,00H06 00Clear our Register B
0B1A-0B1CIf the original number was negative, we need to negate the number because we need a signed mantissa
0B1DPOP HLE1Restore HL from the STACK where it was saved at the top of this routine
0B1ERETC9RETurn to CALLer
0B1FH-0B25H – LEVEL II BASIC MATH ROUTINE – “QINTA”
0B1F– ↳ QINTADEC DE1BDecrement C/D/E by 1
0B20LD A,D7ANow 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
0B21AND EA3Combine the LSB of the single precision value in Register E with the NMSB of the single precision value in Register A
0B22INC A3CIncrement the combined value in Register A
0B23RET NZC0If both D and E were -1 (i.e., DE was FFFFH) then RETurn
0B24– ↳ DCXBRTDEC BC0BDecrement 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.
0B25RETC9RETurn 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– ↳ FIXRST 20HGETYPEE7We 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 Flags Register A Integer NZ/C/M/E -1 String Z/C/P/E 0 Single Precision NZ/C/P/O 1 Double Precision NZ/NC/P/E 5
0B27RET MF8If that test shows we have an INTEGER then we are all done, so RETurn to the caller
0B2B-0B2DIf 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-0B30If 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-0B33Now 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-0B36Since 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– ↳ VINTRST 20HGETYPEE7We 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 Flags Register A Integer NZ/C/M/E -1 String Z/C/P/E 0 Single Precision NZ/C/P/O 1 Double Precision NZ/NC/P/E 5
0B38RET MF8If that test shows we have an INTEGER then we are done, so RETurn to CALLer.
0B39-0B3AIf the NC FLAG is set, then we have a double density number, so JUMP to DINT to handle the conversion.
0B3B-0B3CDisplay a ?TM ERROR if the current value in the ACCumulator isa string
0B3D-0B3FNow 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– ↳ INTLoad Register Pair HL with the address of the exponent in the ACCumulator
0B43LD A,(HL)7ELoad Register A with the value of the exponent in the ACCumulator (held at the location of the memory pointer in Register Pair HL)
0B44-0B45CP 98HFE 98Check 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-0B48LD A,(4121H)LD A,(FACLO)3A 21 41Load Register A with the LSB of the single precision number in the ACCumulator
0B49RET NCD0If there are no fractional bits, then we are done, so RETurn with Register A holding the single precision value in the ACCumulator
0B4ALD A,(HL)7ELoad Register A with the exponent of the single precision number in the ACCumulator
0B4B-0B4DIf 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-0B4FLD (HL),98H36 98Adjust the exponent to be a correct one post-normalization
0B50LD A,E7BLoad Register A with the LSB of the integer value in Register E
0B51PUSH AFF5Save the LSB of the integer value in Register A on the STACK
0B52LD A,C79If the number was negative then we need to negate it, so first load Register A with the value in Register C
0B53RLA17Move the sign bit in Register A into the CARRY FLAG
0B57POP AFF1Get the LSB of the single precision value from the STACK and put it in Register A
0B58RETC9RETurn to CALLer
0B59H-0B9DH – LEVEL II BASIC MATH ROUTINE – “DINT”
Greated Integer function for double-precision numbers. All registers are affected.
0B59-0B5B– ↳ DINTLoad Register Pair HL with the address of the exponent in the ACCumulator
0B5CLD A,(HL)7ELoad 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-0B5ECP 90HFE 90Check to see if the double precision number in the ACCumulator uses more or less than 16 bits of precision
0B5F-0B61If 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-0B63If 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
0B64LD C,A4FIf 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
0B65DEC HL2BDecrement the value of the memory pointer in Register Pair HL to point to the HIGH ORDER (MSB+SIGN) portion of the double precision number
0B66LD A,(HL)7ELoad 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-0B68XOR 80HXOR 1000 0000EE 80Complement the value of the sign bit in Register A (which is 1000 0000)
0B69-0B6ALD B,06H06 06Next 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– ↳ DINT1DEC HL2BTop of a loop. Decrement the value of the memory pointer in Register Pair HL to point to the next byte of the number
0B6COR (HL)B6Combine 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
0B6DDEC B05Decrement the byte counter in Register B
0B70OR AB7The above loop kept ORing A with bits, so now we need to see what A actually holds, so set the flag.
0B71-0B73LD HL,8000H21 00 80Just in case, put -32768 into Register Pair HL. Note that -32768 is negative 0 in double precision
0B74-0B76If the P flag is set, then Register A was zero (-32768), so JUMP to 0A9AH to deal with it
0B77LD A,C79Register 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– ↳ DINT2CP B8HFE B8Check to see if there are fractional bits in for the double precision value in the ACCumulator
0B7ARET NCD0If 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 AFF5Save 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-0B7EGosub 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-0B81Gosub to 09DFH to turn on the sign bit and return with the value of the sign
0B82XOR (HL)AEGet the sign back by XORing A against (HL)
0B83DEC HL2BDecrement the value of the memory pointer in Register Pair HL to point to the exponent of the double-precision number
0B84-0B85LD (HL),B8H36 B8Save an exponent at the location of the memory pointer in Register HL for post-normalization
0B86PUSH AFF5Save the value of the sign test in Register A on the STACK
0B87-0B89If 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-0B8CLoad Register Pair HL with the address of the HIGH ORDER/MSB in the ACCumulator
0B8D-0B8ELD A,B8H3E B8Next 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
0B8FSUB B90Subtract the value of the exponent at the location of the memory pointer in Register Pair HL from the value in Register A
0B93POP AFF1Get the value of the sign test from the STACK and put it in Register A
0B94-0B96If the sign is negative, GOSUB to DROUNA to add 1 to the value in the ACCumulator
0B97XOR AAFZero 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-0B9ALD (411CH),ALD (DFACLO-1),A32 1C 41Put a ZERO into the starting address of ACCumulator minus one
0B9BPOP AFF1Get 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
0B9CRET NCD0IF called from FOUT, then RETurn to skip re-floating the number.
0BA0H-0BA9H – LEVEL II BASIC MATH ROUTINE – “DINTA”
0BA0-0BA2– ↳ DINTAIf we enter from DINTA, our purpose is to subtract 1 from the ACCumulator, so we point HL to the LSB of the ACCumulator.
0BA3– ↳ DINTA1LD A,(HL)7ERegardless of how we enter this routine, the purpose now is to subtract 1 from (HL). To do that, first get the value into A
0BA4DEC (HL)35Decrement the value at the location of the memory pointer in Register Pair HL by 1
0BA5OR AB7We really only want to continue if the byte uCheck to see if the byte used to be ZERO, so test the byte
0BA6INC HL23Increment the value of the memory pointer in Register Pair HL to point to the next byte
0BA7-0BA8Loop until the value at the location of the memory pointer in Register Pair HL is equal to a nonzero value
0BA9RETC9RETurn 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– ↳ UMULTPUSH HLE5Save the value in Register Pair HL on the STACK
0BAE
0BAFLD A,B
OR C78 B1First 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-0BB1If BC is already zero, then just return, since HL is already zero
0BB2-0BB3LD A,10H3E 10Load Register A with the counter value (which is 16)
0BB4– ↳ UMULT1ADD HL,HL29Top of a loop. Multiply the result in Register Pair HL by two
0BB5-0BB7If the CARRY FLAG was set, then we have an overflow, which we handle by displaying a ?BS ERROR message
0BB8EX DE,HLEBSave the product so far into Register Pair DE
0BB9ADD HL,HL29Multiply the integer value in Register Pair HL by two
0BBAEX DE,HLEBSwap DE and HL so DE now holds HL * 4 and HL holds HL * 2
0BBB-0BBCIf the HIGH ORDER/MSB from the HL addition was 1, then we need to add in (BC) so JUMP to UMULT2 to do that
0BBDADD HL,BC09Add the integer value in Register Pair BC to the result in Register Pair HL
0BBE-0BC0Display a BS ERROR message if the result in Register Pair HL has overflowed
0BC1– ↳ UMULT2DEC A3DDecrement the counter in Register A
0BC4– ↳ MULRETEX DE,HLEBSwap so that the return result is in DE. We don’t care about HL because …
0BC5POP HLE1… restore the original HL from the STACK
0BC6RETC9RETurn 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– ↳ ISUBLD A,H7CThe 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
0BC8RLA17Rotate the value of the sign bit into the CARRY FLAG
0BC9SBC A,A9FAdjust Register A according to the value of the sign bit
0BCALD B,A47Load Register B with the result of the sign test
0BCELD A,C79Load Register A with zero
0BCFSBC A,B98Negate the sign
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– ↳ IADDLD A,H7CThe 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
0BD3RLA17Rotate the value of the sign bit into the CARRY FLAG
0BD4SBC A,A9FAdjust Register A according to the value of the sign bit
0BD5– ↳ IADDSLD B,A47Load Register B with the result of the sign test
0BD6PUSH HLE5Save the second argument (held in Register Pair HL) to the the STACK in case we have an overflow
0BD7LD A,D7AThe 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
0BD8RLA17Rotate the value of the sign bit into the CARRY FLAG
0BD9SBC A,A9FAdjust Register A according to the value of the sign bit
0BDAADD HL,DE19Add the two LSBs, result in Register Pair HL
0BDBADC A,B88Add 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
0BDCRRCA0FThe 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
0BDDXOR HACCombine the value of the sign bit for the result in Register H with the value in Register A
0BDE-0BE0If 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.
0BE1PUSH BCC5If we are here then we have an overflow. First, save the extended sign of (HL) (held in Register B) to the STACK
0BE2EX DE,HLEBLoad Register Pair HL with the integer value in Register Pair DE
0BE3-0BE5Go float the Register value in Register Pair HL to single precision and return with the result in the ACCumulator
0BE6POP AFF1Get the sign of (HL) from the STACK and put it in Register A
0BE7POP HLE1Get the old (HL) back from the STACK
0BE8-0BEACall 09A4 which moves the SINGLE PRECISION value in the ACCumulator to the STACK (stored in LSB/MSB/Exponent order)
0BEBEX DE,HLEBLoad Register Pair DE with the integer value in Register Pair HL, as FLOATR needs DE to hold the value
0BEC-0BEEGo float the integer value in Register Pair DE to single precision and return with the result in the ACCumulator
0BEF-0BF1At 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– ↳ IMULTLD A,H7CLoad Register A with the MSB of the integer value in Register H
0BF3OR LB5Combine the LSB of the integer value in Register L with the MSB of the integer value in Register A
0BF4-0BF6If the ZERO flag is set, then HL is zero, and if so, just return
0BF7PUSH HLE5In case of an overflow, we are going to need our original arguments. Save the integer value in Register Pair HL on the STACK
0BF8PUSH DED5Save the integer value in Register Pair DE on the STACK
0BF9-0BFBGo convert any negative integer values to positive and return with Register B set according to the value of the sign bits
0BFCPUSH BCC5Save the value of the sign bit test in Register B on the STACK
0BFD
0BFELD B,H
LD C,L44Copy the second argument from HL into BC
0C02-0C03LD A,10H3E 10Load Register A with the counter value (which is 16)
0C04– ↳ IMULT1ADD HL,HL29Multiply the result in Register Pair HL by two
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.
0C07EX DE,HLEBExchange the integer value in Register Pair DE with the integer result in Register Pair HL
0C08ADD HL,HL29Multiply the integer value in Register Pair HL by two
0C09EX DE,HLEBExchange the integer result in Register Pair DE with the integer value in Register Pair HL
0C0A-0C0BIf the NC FLAG is set, then skip the next instructions which add in BC
0C0CADD HL,BC09Add the integer value in Register Pair BC to the integer result in Register Pair HL
0C10– ↳ IMULT2DEC A3DDecrement the value of the counter in Register A
0C13POP BCC1At 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
0C14POP DED1Get 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– ↳ IMLDIVLD A,H7CLoad Register A with the MSB of the result in Register H
0C16OR AB7Test Register H
0C17-0C19If the M FLAG is set, then the result is =gt; 32768, so JUMP to IMULT3 to make sure it isn’t -32768.
0C1APOP DED1If we are here, then the number is OK, so get the SECOND argument off the stack and into Register Pair DE
0C1BLD A,B78Load Register A with the value of the sign test in Register B
0C1FH-0C34H – LEVEL II BASIC MATH ROUTINE – “IMULT3”
0C1F-0C20– ↳ IMULT3XOR 80HEE 80Clear the sign bit for the MSB of the integer value in Register A which is 1000 0000
0C21OR LB5Combine the value of the LSB for the integer value in Register L with the adjusted MSB of the integer value in Register A
0C22-0C23If the Z FLAG is set, then the result is 32768, so JUMP to IMULT4
0C24EX DE,HLEBIf we are hre, then it is > 32768 giving us an overflow, so Load Register Pair HL with the integer value in Register Pair DE
0C28-0C2AGo float the FIRST argument (held in in Register Pair HL) to single precision and return with the result in the ACCumulator
0C2BPOP HLE1Get the original SECOND argument from the STACK and put it in Register Pair HL
0C2C-0C2ESave 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-0C31Go float the SECOND argument (held in in Register Pair HL) to single precision and return with the result in the ACCumulator
0C32– ↳ FMULTTPOP BCC1Get the FIRST argument off the stack and put it in Register Pair BC. POLYX jumps here.
0C33POP DED1Get the NMSB and the LSB of the single precision value from the STACK and put it in Register Pair DE
0C34-0C36Multiply 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– ↳ IMULT4LD A,B78We need to see if the result is +/- 32768. First, load Register A with the result of the sign test in Register B
0C38OR AB7Check the result
0C39POP BCC1Discard the original SECOND argument from the STACK
0C3DPUSH DED5If we are here, then the result is positive. Save the remainder for MOD to the STACK
0C41POP DED1Get the MOD’s remainder from the STACK and put it in Register Pair DE
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– ↳ IMULDVLD A,H7CLoad Register A with the MSB+SIGN of the integer value in Register H
0C46XOR DAACombine the MSB of the integer value in Register D with the MSB+SIGN of the integer value in Register A
0C47LD B,A47Save the result of the combined signs in Register A into Register B
0C48-0C4AIf necessary, NEGate the SECOND argument (i.e., the value in Register Pair HL) to positive
0C4BEX DE,HLEBPresetve the contents of Register DE into Register Pair HL, and fall through to the negation routine below.
0C4C– ↳ INEGHLD A,H7CLoad Register A with the MSB+SIGN of the integer value in Register H
0C4D– ↳ INEGAOR AB7Set the condition codes so we can see the sign of HL
0C4E-0C50If 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– ↳ INEGHLXOR AAFZero Register A.
0C52LD C,A4FLoad Register C with the ZERO held in Register A
0C53SUB L95Subtract the LSB of the integer value in Register L from the ZERO in Register A
0C54LD L,A6FSave the adjusted value in Register A in Register L
0C55LD A,C79Load Register A with a ZERO
0C56SBC A,H9CSubtract the HIGH ORDER (MSB+SIGN) of the integer value in Register H from the value in Register A
0C57LD H,A67Save the adjusted value in Register A into Register H
0C58-0C5AJump 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– ↳ INEGLD HL,(4121H)LD HL,(FACLO)2A 21 41Load Register Pair HL with the integer value in the ACCumulator
0C5E-0C60Go convert the integer value in Register Pair HL to positive if it’s negative
0C61LD A,H7CLoad Register A with the HIGH ORDER (i.e., MSB+SIGN) of the integer value in Register H
0C62-0C63XOR 80HXOR 1000 0000EE 80Invert the value of the sign bit in Register A which is 1000 0000 so that we can check for the special case of -32768
0C64OR LB5Combine the LSB of the integer value in Register L with the adjusted MSB of the integer value in Register A
0C65RET NZC0Return if the integer value in the ACCumulator isn’t equal to -32768
0C66– ↳ INEG2EX DE,HLEBIf 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
0C6AXOR AAFZero Register A, which we will use for the HIGH ORDER
0C6B-0C6C– ↳ INEGADLD B,98H06 98Load Register B with an exponent. IADD jumps here.
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– ↳ DSUBSince 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) 0C73LD A,(HL)7ELoad 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-0C75XOR 80HXOR 1000 0000EE 80Invert the value of the sign bit for the MSB of the double precision value in Register A which is 1000 0000 0C76LD (HL),A77Save 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– ↳ DADDLoad Register Pair HL with the address of the exponent in the FIRST argument held at ARG (a/k/a REG 2) 0C7ALD A,(HL)7EPrepare 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 0C7BOR AB7Check to see if the double precision value in ARG (a/k/a REG 2) is equal to zero 0C7CRET ZC8Return 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 0C7DLD B,A47Preserve the exponent for the double precision value in Register A into Register C 0C7EDEC HL2BDecrement the value of the memory pointer in Register Pair HL to now point to the HIGH ORDER (i.e., MSB + SIGN) for unpacking 0C7FLD C,(HL)4ELoad 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-0C82Load Register Pair DE with the address of the exponent of the SECOND argument (held in the ACCumulator) 0C83LD A,(DE)1AFetch the value of the exponent of the double precision value in the ACCumulator at the location of the memory pointer in Register Pair DE 0C84OR AB7Set the flags to see if the double precision value in the ACCumulator is equal to zero 0C85-0C87If 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. 0C88SUB B90Now 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-0C8AIf the NC FLAG is set, then the we need to put the smaller number into the ACCumulator, so JUMP to DADD2 to do that 0C8BCPL2FNegate the shift count held in Register A 0C8CINC A3CIncrement the value of the difference for the exponents in Register A so that Register A will hold the positive difference 0C8DPUSH AFF5Save 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-0C8FLD C,08H0E 08Load Register C with a counter value which is 8
0C90INC HL23Increment 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)
0C91PUSH HLE5Save the value of the memory pointer in Register Pair HL (which is pointing to ARG) on the STACK
0C92– ↳ DADD1LD A,(DE)1ATop of a loop. Load Register A with the value of the ACCumulator pointed to by Register Pair DE
0C93LD B,(HL)46Load Register B with the value of ARG (a/k/a REG 2) pointed to by Register Pair DE
0C94LD (HL),A77Save the ACCumulator value into the corresponding ARG (a/k/a REG 2) byte.
0C95LD A,B78Load Register A with the ARG byte (held in Register B)
0C96LD (DE),A12Save the ARG byte (held in Register A) into the corresopnding ACCumulator byte (pointed to by Register Pair DE)
0C97DEC DE1BDecrement the value of the memory pointer in Register Pair DE to the next lower byte of the ACCumulator
0C98DEC HL2BDecrement the value of the memory pointer in Register Pair HL to the next lower byte in ARG
0C99DEC C0DDecrement the value of the counter in Register C
0C9A-0C9BLoop until the double precision values in the ACCumulator and ARG (a/k/a REG 2) have been exchanged
0C9CPOP HLE1Get the HIGH ORDER back from the stack into Register Pair HL
0C9DLD B,(HL)46Fetch the exponent for the double precision value in ARG (a/k/a REG 2) pointed to by Register Pair HL
0C9EDEC HL2BDecrement the value of the memory pointer in Register Pair HL to now point to the HIGH ORDER (MSB + SIGN)
0C9FLD C,(HL)4ELoad 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
0CA0POP AFF1Get the shift count (i.e., difference for the exponents) back into Register A
0CA1-0CA2– ↳ DADD2CP 39HFE 39Check to see if the difference between the two exponents is greater than 56 bits
0CA3RET NCD0Return if the difference between the two exponents is greater than 56 bits
0CA4PUSH AFF5Save the shift count (i.e., difference for the exponents) from Register A onto the STACK
0CA8INC HL23Increment the value of the memory pointer in Register Pair HL to now point to ARGLO-1
0CA9-0CAALD (HL),00H36 00Zero the temporary LSB (held at the location of the memory pointer in Register Pair HL)
0CABLD B,A47Preserve the sign test into Register B
0CACPOP AFF1Restore the shift count (i.e., difference for the exponents) from the STACK into Register A
0CAD-0CAFLoad Register Pair HL with the address of the HIGH ORDER in ARG (a/k/a REG 2)
0CB0-0CB2Go 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-0CB5LD A,(4126H)LD A,(ARGLO-1)3A 26 41We next need to transfer the OVERFLOW byte from ARG to ACCumulator, so first put it in Register A
0CB6-0CB8LD (411CH),ALD (DFACLO-1),A32 1C 41Save the value in Register A to ARG
0CB9LD A,B78Load Register A with the value of the sign test in Register B
0CBAOR AB7Check to see if the signs are equal
0CBB-0CBDIf the P FLAG is set, then the signs of the numbers are different, so JUMP to DADD3 to subtract the values
0CBE-0CC0Otherwise (i.e., the signs are the same) GOSUB to DADDAA to add the numbers
0CC1-0CC3If that didn’t trigger a NC FLAG, then JUMP to 0D0EH to ROUND the result and continue on.
0CC4EX DE,HLEBIf that DID trigger the NC FLAG, then put the pointer to the exponent of the ACCumulator into HL
0CC5INC (HL)34Add one to the exponent (since we had an overflow)
0CC6-0CC8Check 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-0CCBIf 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
0CCFH-0D1FH – DOUBLE PRECISION MATH ROUTINE – “DADD3”
0CD2-0CD4Right 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-0CD7Go complement the result in the ACCumulator if the Carry flag is set
0CD8H – DOUBLE PRECISION MATH ROUTINE – “DNORML” and “DNORM1”
0CD8– ↳ DNORMLXOR AAFZero Register A, which will act as a byte shift counter
0CD9– ↳ DNORM1LD B,A47Preserve the shift counter into Register Register B
0CDA-0CDCLD A,(4123H)LD A,(FAC-1)3A 23 41Load Register A with the value of the HIGH ORDER (i.e., MSB+SIGN) of the double precision result in the ACCumulator
0CDDOR AB7Check to see if we can shift 8 numbers to the left
0CDE-0CDFIf 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-0CE2If 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-0CE4LD C,08H0E 08Load Register C with the number of bytes to be shifted (i.e., 8)
0CE5– ↳ DNORM2LD D,(HL)56Top of a loop. Load Register D with a byte from the ACCumulator (pointed to by Register Pair HL)
0CE6LD (HL),A77Save 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.
0CE7LD A,D7APut the current byte from the ACCumulator (preserved in D) into Register A for writing on the next iteration
0CE8INC HL23Increment the value of the memory pointer in registerpair HL
0CE9DEC C0DDecrement the number of bytes to be shifted in Register C
0CEA-0CEBLoop until all of the bytes in the double precision value have been shifted
0CECLD A,B78Now 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-0CEESUB 08HD6 08Subtract the number of bits just shifted from the shift counter in Register A
0CEF-0CF0CP C0HFE C0Check to see if the whole of the double precision value has been shifted
0CF1-0CF2If the whole of the double precision value hasn’t been shifted, JUMP back to DNORM1 to shift some more
0CF6H – Part of the “DNORML” and “DNORM1” Routine
0CF6– ↳ DNORM3DEC B05Decrement the shift counter held in Register B
0CF7-0CF9Load Register Pair HL with the starting address of ACCumulator minus one.
0CFA-0CFCShift the double precision value in the ACCumulator once to the left
0CFDOR AB7Check to see if the number has been normalized yet
0CFE-0D00– ↳ DNORM5If the P FLAG is set, then we are not yet normalized, so LOOP back to DNORM3 and keep shifting
0D01LD A,B78Load Register A with the value of the shift counter from Register B
0D02OR AB7Check to see if the shift counter in Register A is equal to zero
0D03-0D04If the shift counter is zero, then proceed to round the number and finish up by JUMPing to DROUND
0D05-0D07Load Register Pair HL with the address of the exponent in the ACCumulator
0D08ADD A,(HL)86Add 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
0D09LD (HL),A77Save the adjusted exponent for the double precision value in Register A at the location of the memory pointer in Register Pair HL
0D0A-0D0CIf the NC FLAG was triggered, then we have an UNDERFLOW, so JUMP to ZERO
0D0DRET ZC8If 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– ↳ DROUNDLD A,(411CH)LD A,(DFACLO-1)3A 1C 41Load Register A with the value of the rounding byte at the location of the starting address of ACCumulator minus one.
0D11– ↳ DROUNBOR AB7Check to see if there is a bit to be shifted into the double precision value in the ACCumulator
0D12-0D14Go move the bit into the double precision value if necessary
0D15-0D17Load Register Pair HL with the address of the unpacked sign for the result.
0D18LD A,(HL)7ELoad Register A with the value of the sign for the result at the location of the memory pointer in Register Pair HL
0D19-0D1AAND 80HAND 1000 0000E6 80Turn 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
0D1CDEC HL
DEC HL2BDecrement the value of the memory pointer in Register Pair HL twice so that it points to HIGH ORDER (MSB) byte in the ACCumulator
0D1DXOR (HL)AEPack the SIGN and the MSB together
0D1ELD (HL),A77Save the packed sign and MSB combination byte to the ACCumulator at the location of the memory pointer in Register Pair HL
0D1FRETC9RETurn to CALLer
0D20H-0D32H – DOUBLE PRECISION MATH support routine – “DROUNA”
0D20-0D22– ↳ DROUNASet up HL to point to the LSB of the the ACCumulator.
Note: 411DH-4124H holds ACCumulator
0D23-0D24LD B,07H06 07Load Register B with the number of bytes to be bumped for the double precision value in the ACCumulator
0D25– ↳ DRON1INC (HL)34Top of a loop. Increment a byte of the ACCumulator at the location of the memory pointer in Register Pair HL
0D26RET NZC0Return if the value at the location of the memory pointer in Register Pair HL isn’t equal to zero
0D27INC HL23Increment the value of the memory pointer in Register Pair HL to point to the next highest order in the ACCumulator
0D28DEC B05Decrement the value of the byte counter in Register B
0D2BINC (HL)34We 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-0D2ECheck 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
0D2FDEC HL2BDecrement the value of the memory pointer in Register Pair HL to point to the HIGH ORDER
0D30-0D31LD (HL),80H36 80Save a new MSB+SIGN at the location of the memory pointer in Register Pair HL
0D32RETC9RETurn to CALLer
0D33H-0D44H – DOUBLE PRECISION MATH ROUTINE – “DADDAA” and “DADDA”
0D33-0D35– ↳ DADDAADADD 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– ↳ DADDFOFOUT 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– ↳ DADDSLD C,07H0E 07Load Register C with the number of bytes to be added
0D3BXOR AAFClear the Carry flag
0D3C– ↳ DADDLSLD A,(DE)1ATop of a loop. Load Register A with the value in the ACCumulator at the location of the memory pointer in Register Pair DE
0D3DADC A,(HL)8EAdd the value in ARG (a/k/a REG 2) at the location of the memory value in Register A
0D3ELD (DE),A12Save the result of that addition into the ACCumulator at the location of the memory pointer in Register Pair DE
0D3FINC DE13Increment the value of the memory pointer in Register Pair DE
0D40INC HL23Increment the value of the memory pointer in Register Pair HL
0D41DEC C0DDecrement the number of bytes to be added in Register C
0D42-0D43Loop until all of the bytes for the double precision values have been added
0D44RETC9RETurn 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– ↳ DADDASDADD 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– ↳ DADDFSFOUT 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– ↳ DADDSSLD C,07H0E 07Load Register C with the number of bytes to be subtracted
0D4DXOR AAFClear the Carry flag
0D4E– ↳ DADDLSLD A,(DE)1ATop of a loop. Load Register A with the value in the ACCumulator at the location of the memory pointer in Register Pair DE
0D4FSBC A,(HL)9ESubtract 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
0D50LD (DE),A12Save the result in Register A in the ACCumulator at the location of the memory pointer in Register Pair DE
0D51INC DE13Increment the value of the memory pointer in Register Pair DE
0D52INC HL23Increment the value of the memory pointer in Register Pair HL
0D53DEC C0DDecrement the number of bytes to be subtracted for the double precision values in Register C
0D54-0D55Loop until all of the bytes for the double precision values have been subtracted
0D56RETC9RETurn 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– ↳ DNEGRLD A,(HL)7ELoad Register A with the value of the sign from the ACCumulator at the location of the memory pointer in Register Pair HL
0D58CPL2FComplement the value of the sign in Register A
0D59LD (HL),A77Save the value of the sign in Register A at the location of the memory pointer in Register Pair HL
0D5A-0D5CLoad Register Pair HL with the starting address of ACCumulator minus one.
0D5D-0D5ELD B,08H06 08Load Register B with the number of bytes to be reversed
0D5FXOR AAFZero Register A and clear the CARRY FLAG
0D60LD C,A4FLoad Register C with the ZERO
0D61– ↳ DNEGR1LD A,C79Top of a loop. Load Register A with the value in Register C
0D62SBC A,(HL)9ENEGate a byte to the ACCumulator
0D63LD (HL),A77Save the NEGated value in Register A back to the location of the memory pointer in Register Pair HL
0D64INC HL23Increment the value of the memory pointer in Register Pair HL
0D65DEC B05Decrement the number of bytes to be reversed in Register B
0D66-0D67Loop until all of the bytes for the double precision number in the ACCumulator have been reversed
0D68RETC9RETurn 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– ↳ DSHFTRLD (HL),C71Save the unpacked MSB of the double precision value in Register C at the location of the memory pointer in Register Pair HL
0D6APUSH HLE5Save the value of the memory pointer in Register Pair HL on the STACK
0D6B-0D6C– ↳ DSHFR1SUB 08HD6 08Subtract 8 from the number of bits to be shifted from the number of bits to be shifted in Register A
0D6D-0D6EIf 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.
0D6FPOP HLE1Get the value of the memory pointer from the STACK and put it in Register Pair HL
0D70– ↳ DSHFRMPUSH HLE5Save the value of the memory pointer in Register Pair HL on the STACK. This is the entry point from DMULT.
0D71-0D73LD DE,0800H11 00 08This LD command shifts a zero into the HIGH ORDER byte and sets up a counter
0D74– ↳ DSHFR2LD C,(HL)4ETop of a loop. Preserve a byte of the ACCumulator into Register C
0D75LD (HL),E73Overwrite that location with the last byte (held in Register E)
0D76LD E,C59Load Register E with the value in Register C so that THIS is the byte to write next.
0D77DEC HL2BDecrement the value of the memory pointer in Register Pair HL to point to the next lower order byte
0D78DEC D15Decrement the number of bits shifted in Register D
0D7D-0D7EH – DOUBLE PRECISION MATH ROUTINE – “DSHFR3”
0D7D-0D7E– ↳ DSHFR3ADD 09HC6 09At this point, we cannot shift 8 bytes at once and need to do them individually. First, set a corrected shift counter
0D7FLD D,A57Preserve the adjusted shift counter into Register D
0D80– ↳ DSHFR4XOR AAFClear the CARRY FLAG
0D81POP HLE1Restore the pointer to the HIGH ORDER byte into Register Pair HL
0D82DEC D15Decrement the number of bits to be shifted in Register D
0D83RET ZC8Return if all of the bits have been shifted
0D84– ↳ DSHFRAPUSH HLE5If 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-0D86LD E,08H1E 08Load Register E with the counter of the number of bytes to be shifted
0D87– ↳ DSHFR5LD A,(HL)7ETop of a loop. Load Register A with a byte from the ACCumulator pointed to by HL
0D88RRA1FShift 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.
0D89LD (HL),A77Put the rotated byte back
0D8ADEC HL2BDecrement the value of the memory pointer in Register Pair HL so we deal with the next lower order byte
0D8BDEC E1DDecrement the number of bytes to be shifted in Register E
0D90H-0D96H – DOUBLE PRECISION MATH ROUTINE – “DSHFRB”
This is the entry from DADD and DMULT.
0D90-0D92– ↳ DSHFRBLoad Register Pair HL with the address of the HIGH PRDER (MSB) of the double precision value in the ACCumulator
0D93-0D94LD D,01H16 01Load Register D with the number of bits to be shifted
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– ↳ DSHFLCLD C,08H0E 08Load Register C with the number of bytes to be shifted
0D99– ↳ DSHFTLLD A,(HL)7ETop of a loop. Load Register A with a byte from the ACCumulator pointed to by HL
0D9ARLA17Rotate that byte left one bit
0D9BLD (HL),A77Save the shifted byte (held in Register A) back to the ACCumulator at the location of the memory pointer in Register Pair HL
0D9CINC HL23Increment the value of the memory pointer in Register Pair HL to point to the next higher order byte
0D9DDEC C0DDecrement the byte counter in Register C
0DA0RETC9RETurn 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– ↳ DMULTAs 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
0DA4RET ZC8If 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-0DA7Add the exponents and take care of processing the signs of the numbers via a GOSUB to MULDVA
0DA8-0DAAZero out the ACCumulator and put the move the double precision value in the ACCumulator to a temporary work area via a GOSUB to DMULDV
0DABLD (HL),C71Put the unpacked HIGH ORDER byte (pointed to by Register Pair HL in ARG) into Register C
0DACINC DE13Increment Register Pair DE so that it points to the LSB of the double precision value in ARG
0DAD-0DAELD B,07H06 07Load Register B with the number of bytes to be figured
0DAF– ↳ DMULT2LD A,(DE)1ATop of a big loop. Fetch a byte of ARG (at the location pointed to by DE) to multiply by into Register A
0DB0INC DE13Increment the value of the memory pointer in Register Pair DE to point to the next higher byte.
0DB1OR AB7Check to see if the value in Register A is equal to zero
0DB2PUSH DED5Save the value of the memory pointer to ARG (held in Register Pair DE) to the STACK
0DB3-0DB4If Register A is zero, then we are multiplying by ZERO, so JUMP to DMULT5
0DB5-0DB6LD C,08H0E 08Otherwise, we need to set up for another loop for bit rotation. First, load Register C with the numberof bits to be shifted
0DB7– ↳ DMULT3PUSH BCC5Top of a loop. Save the counters (held in Register Pair BC) to the STACK
0DB8RRA1FShift 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.
0DB9LD B,A47Preserve the shifted multiplier byte into Register B
0DBA-0DBCIf 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-0DBFRotate the production right one bit via a GOSUB to DSHFRB which shifts the value of the total in the ACCumulator
0DC0LD A,B78Restore the shifted multiplier byte (held in Register B) back into Register A
0DC1POP BCC1Restore the counters from the STACK into Register Pair BC
0DC2DEC C0DDecrement the number of bits to be shifted from the ARG (tracked in Register C)
0DC3-0DC4If 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– ↳ DMULT4POP DED1If 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
0DC6DEC B05Decrement the number of bytes to be figured (tracked in Register B)
0DC7-0DC8Loop back to 0DAFH to multiply by the next higher order byte in ARG until all of the bytes in ARG have been figured
0DC9-0DCBIf 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– ↳ DMULT5Load Register Pair HL with the address of the HIGH ORDER/MSB of the ACCumulator
0DCF-0DD1Go shift the double precision total in the ACCumulator right one byte
0DD4H-0DDBH – DOUBLE PRECISION CONSTANT STORAGE AREA – “DTEN” and “FTEN”
0DD4-0DDB– ↳ DTEN00 00 00 00 00 00 20 8400A double precision constant equal to 10 is stored here. Note: 0DD8 is also a reference point.
0DD8-0DDB– ↳ FTEN00 00 20 8400A 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– ↳ DDIV10Load Register Pair DE with the starting address of the double precision constant for 10
0DDF-0DE1Load 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-0DE4GOSUB 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– ↳ DDIVLD A,(412EH)LD A,(ARG)3A 2E 41As 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) 0DE8OR AB7Check to see if the double precision value in ARG (a/k/a REG 2) is equal to zero 0DE9-0DEBDisplay a ?/0 ERROR message if the double precision value in ARG (a/k/a REG 2) is equal to zero 0DEC-0DEESubtract the exponents and check the signs via a GOSUB to MULDVS 0DEF
0DF0INC (HL)
INC (HL)34Increment the value of the exponent in the ACCumulator TWICE to correct the scaling 0DF1-0DF3Zero the ACCumulator and move the double precision value from ACCumulator into ARG (a/k/a REG 2) 0DF4-0DF6Load Register Pair HL with the address of the extra HIGH ORDER byte we will use in ARG 0DF7LD (HL),C71Zero the that byte 0DF8LD B,C41Zero Register B, which will be the flag that tells us when start dividing 0DF9-0DFB– ↳ DDIV1Top of a large loop. First, get the pointer to the end of the BUFFR into Register Pair DE 0DFC-0DFELoad Register Pair HL with the address of the END of the double precision value in ARG (a/k/a REG 2) 0E02LD A,(DE)1APrepare 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 0E03SBC A,C99Subtract the value in Register C from the value in Register A 0E04CCF3FIf 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-0E06If 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-0E09Put the pointer to the end of the BUFFR into Register Pair DE 0E0A-0E0CLoad Register Pair HL with the address of the END of the double precision value in ARG (a/k/a REG 2) 0E0D-0E0FGo add the double precision value in ARG (a/k/a REG 2) to the double precision value in FBUFFR 0E10XOR AAFClear the CARRY FLAG for the Z-80 trick in the next instruction. 0E11-0E13JP C,0412HDA 12 04Z-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-0E16LD A,(4123H)LD A,(FAC-1)3A 23 41Prepare the check to see if we are finished dividing. First, getch the byte at FAC-1 0E17
0E18INC A
DEC A3CINCrement and DECrement Register A so that the SIGN FLAG will be set without chaning the status of the CARRY FLAG 0E19RRA1FIn 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-0E1CIf the M FLAG is set, then we are done and have 57 bits of accuracy, so JUMP to DROUNB to finish up. 0E1DRLA17Restore the CARRY BIT to where it belongs 0E1E-0E20Load 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-0E22LD C,07H0E 07Load Register C with the number of bytes to be shifted 0E23-0E25GOSUB to DSHFTL to shit in the next bit in the quotient (held in the ACCumulator) 0E26-0E28Load Register Pair HL with the pointo the LOW ORDER byte in FBUFFR 0E29R-0E2BHGo shift the double precision value dividend (in FBUFFR) one to the left 0E2CLD A,B78Test 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 0E2DOR AB7Set the flags based on Register B 0E2E-0E2FIf Register B is not ZERO, then we have more to go so LOOP back up to DDIV1 0E30-0E32If 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 0E33DEC (HL)35Decrement 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-0E35Continue dividing so long as we don’t have an overflow by LOOPING back to DDIV 0E36-0E38Display 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– ↳ DMULDVLD A,C79We 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-0E3CLD (412DH),ALD (ARG-1),A32 2D 41Save the MSB of the double precision value in ARG (a/k/a REG 2) in Register A
0E3DDEC HL2BDecrement the value of the memory pointer in Register Pair HL to now point to the HIGH ORDER of the ACCumulator
0E41-0E43LD BC,0700H01 00 07Load Register B with the number of bytes to be moved (which is 7) and put a zero into Register C
0E44– ↳ DMLDV1LD A,(HL)7ETop of a loop. Fetch a byte from the ACCumulator (tracked by Register Pair HL) into Register A
0E45LD (DE),A12Save that byte into FBUFFR (tracked by Register Pair DE)
0E46LD (HL),C71Zero out that location in the ACCumulator
0E47DEC DE1BDecrement the value of the memory pointer to FBUFFR (tracked by Register Pair DE)
0E48DEC HL2BDecrement the value of the memory pointer to the ACCumulator (tracked by Register Pair HL)
0E49DEC B05Decrement the value of the byte counter in Register B to see if we are done
0E4A-0E4BLoop until the double precision value has been moved from the ACCumulator to FBUFFR
0E4CRETC9RETurn 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– ↳ DMUL10Go move the value in the ACCumulator to ARG (a/k/a REG 2)
0E50EX DE,HLEBSince VMOVAF exits with DE pointing to ACCumulator + 1 we need to swap those so that HL points to the ACCumulator
0E51DEC HL2BAs 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
0E52LD A,(HL)7EFetch the exponent from the ACCumulator
0E53OR AB7Check to see if the value in the ACCumulator is equal to zero
0E54RET ZC8Return if the value in the ACCumulator is equal to zero
0E55-0E56ADD 02HC6 02Add two to the exponent which is the same as multiplying the ACCumulator by 4
0E57-0E59Display an ?OV ERROR if the adjusted exponent in Register A is too large
0E5ALD (HL),A77Save the adjusted exponent back into the ACCumulator at the location of the memory pointer in Register Pair HL
0E5BPUSH HLE5Save pointer to the ACCumulator onto the STACK
0E5C-0E5EAdd 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)
0E5FPOP HLE1Get the memory pointer to the ACCumulator from the STACK and put it in Register Pair HL
0E60INC (HL)34Add 1 to the exponent, thus doubling the number.
0E61RET NZC0Return if overflow didn’t occur
0E62-0E64Display 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.
0E6BOR 0AFH
F6 AFF6 AFPart 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 AFHZero 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– ↳ FINCHREX DE,HLEBLoad Register Pair DE with the pointer to the current BASIC line being interpreted
0E6E-0E70LD BC,00FFH01 FF 00Load Register Pair BC with a zero and a negative one. Register B will track the decimal point location and C will be a flag.
0E71LD H,B60Load Register H with zero
0E72LD L,B68Load Register L with zero. Now HL is zero.
0E73-0E75A CALL to MAKINT will clear the ACCumulator and force VALTYP into Integer
0E76EX DE,HLEBRestore the pointer to the BASIC line being interpreted into HL and zero out Register Pair DE
0E77LD A,(HL)7ERetrieve the first character at at the location of the current input buffer pointer in Register Pair HL
0E78-0E79CP 2DHCP “-“FE 2DCheck to see if the character at the current position in the string being interpreted is a –
0E7APUSH AFF5Save the sign in Register Pair AF on the STACK
0E7B-0E7DIf the character at the current position in the string being interpreted is a – then JUMP to FINC to ignore it
0E7E-0E7FCP 2BHCP “+”FE 2BCheck to see if the character at the current position in the string being interpreted is a
0E80-0E81If the character at the current position in the string being interpreted is a then JUMP to FINC to process it
0E82DEC HL2BDecrement 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– ↳ FINCRST 10HCHRGETD7Since 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-0E86If the character at the location of the current input buffer pointer in Register A is numeric then JUMP to FINDIG
0E87-0E88CP 2EHCP “.”FE 2ECheck to see if the character at the location of the current input buffer pointer in Register A is a .
0E89-0E8BJump if the character at the location of the current input buffer pointer in Register A is a .
0E8C-0E8DCP 45HCP “E”FE 45Check 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-0E8FJump if the character at the location of the current input buffer pointer in Register A is an E
0E90-0E91CP 25HCP “%”FE 25Check to see if the character at the location of the current input buffer pointer in Register A is a %
0E92-0E94Jump 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-0E96CP 23HCP “#”FE 23Check to see if the character at the location of the current input buffer pointer in Register A is a #
0E97-0E99Jump 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-0E9BCP 21HCP “!”FE 21Check to see if the character at the location of the current input buffer pointer in Register A is a !
0E9C-0E9EJump 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-0EA0CP 44HCP “D”FE 44Check to see if the character at the location of the current input buffer pointer in Register A is a D
0EA1-0EA2If the character ISN’T a D, then we must be finished with the number, so JUMP to FINE
0EA3– ↳ FINEX1OR AB7Set 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– ↳ FINEXConvert the current value in the ACCumulator to either single precision or double precision
0EA7PUSH HLE5Save the current input buffer pointer to the string being processed (tracked in Register Pair HL) to the STACK
0EA8-0EAALoad Register Pair HL with the return address to the FINEC routine
0EABEX (SP),HLE3Swap (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
0EACRST 10HCHRGETD7Next 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.
0EADDEC D15Decrement the value in Register D to turn the sign of the exponent to NEGATIVE
0EAE-0EAFCP 0CEHCP “-“FE CECheck to see if the character at the location of the current input buffer pointer in Register A is a – token
0EB0RET ZC8If the character at the location of the current input buffer pointer in Register A is a minus sign token then RET
0EB1-0EB2CP 2DHCP “-“FE 2DCheck to see if the character at the location of the current input buffer pointer in Register A is a – sign (not token)
0EB3RET ZC8If the character at the location of the current input buffer pointer in Register A is a minus sign then RET
0EB4INC D14If 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-0EB6CP 0CDHCP “+”FE CDCheck to see if the character at the location of the current input buffer pointer in Register A is a + token (0CDH)
0EB7RET ZC8Return if the character at the location of the current input buffer pointer in Register A is a + token (CDH)
0EB8-0EB9CP 2BHCP “+”FE 2BCheck to see if the character at the location of the current input buffer pointer in Register A is a +
0EBBRET Z2BReturn if the character at the location of the current input buffer pointer in Register A is a +
0EBADEC HLC8If 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
0EBCPOP AFF1Discard the FINCE return address as we no longer need it … we are now passing right to it!
0EBDRST 10HSince 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– ↳ FINECRST 10HCHRGETF1Since 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-0EC0If 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
0EC1INC D14If 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-0EC3So long as the exponent isn’t a ZERO, JUMP to FINE to skip over the handling of a negative exponent
0EC4XOR AAFIf we are here, then the exponent is negative. Zero Register A
0EC5SUB E93NEGate the value of the exponent in Register E (i.e., A = 0 – E)
0EC6LD E,A5FLoad Register E with the negated version of itself
0EC7– ↳ FINEPUSH HLE5Save the current input buffer pointer to the string being converted (tracked in Register Pair HL) to the STACK
0EC8LD A,E7BLoad Register A with the value of the exponent in Register E
0EC9SUB B90Subtract 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– ↳ FINE2If the P FLAG is set, then we need to multiply. So multiply the current value by ten
0ECD-0ECFIf the M FLAG is set, then we need to divide. So multiply the current value by ten
0ED0-0ED1Whichever 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.
0ED2POP HLE1Get the value of the current input buffer pointer of the string being parsed from the STACK and put it in Register Pair HL
0ED3POP AFF1Get the sign value from the STACK and put it in Register A
0ED4PUSH HLE5Save the value of the current input buffer pointer of the string being parsed in Register Pair HL on the STACK
0ED5-0ED7If the Z FLAG is set, then convert the current value to negative
0ED8POP HLE1Get 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)
0ED9RST 20HGETYPEE7We 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:
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 Double Precision NZ/NC/P/E and A is 5.
0EDARET PEE8If that test shows we have anything other than a SINGLE PRECISION number, then we do no thave -32678, so RETurn
0EDBPUSH HLE5If 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-0EDELoad Register Pair HL with the return address of the POPHRT routine because CONIS2 does funny things to the stack.
0EDFPUSH HLE5Save the value of the return address in Register Pair HL on the STACK
0EE0-0EE2Check 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
0EE3RETC9RETurn 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– ↳ FINDPRST 20HGETYPEE7We 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 Flags Register A Integer NZ/C/M/E -1 String Z/C/P/E 0 Single Precision NZ/C/P/O 1 Double Precision NZ/NC/P/E 5
0EE5INC C0CIncrement the value in Register C to adjust the flag
0EE6-0EE7If the INC C is NOT ZERO then we have 2 decimal points, so we are DONE.
0EE8-0EEAIf 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
0EEE – Math Routine – “FININT”
0EEE– ↳ FININTRST 20HGETYPEE7We 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 Flags Register A Integer NZ/C/M/E -1 String Z/C/P/E 0 Single Precision NZ/C/P/O 1 Double Precision NZ/NC/P/E 5
0EEF-0EF1If that test shows anything but an INTEGER, jump to the Level II BASIC error routine and display a ?SN ERROR message
0EF2– ↳ INFINEINC HL23Top 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 %).
0EF5– ↳ FINDBFOR AB7If we are here then we need to force double precision, so set the NZ FLAG
0EF6-0EF8– ↳ FINSNFForce 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)
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– ↳ FINFRCPUSH HLE5Save the value of the current input buffer pointer of the string being parsed in Register Pair HL on the STACK
0EFCPUSH DED5Save the exponent (held in Register Pair DE) to the STACK
0EFDPUSH BCC5Save the decimal point information (held in Register Pair BC) to the STACK
0EFEPUSH AFF5Save the sp/dp value flag for the conversion (held in Register A) to the STACK
0EFF-0F01If 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)
0F02POP AFF1Restore the sp/dp value flag for the conversion from the STACK and put it in Register Pair AF
0F03-0F05If 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)
0F06POP BCC1Restore the decimal point information from the STACK and put it in Register Pair BC
0F07POP DED1Restore the exponent from the STACK and put it in Register Pair DE
0F08POP HLE1Restore the value of the current input buffer pointer of the string being parsed from the STACK and put it in Register Pair HL
0F09RETC9RETurn 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– ↳ FINMULRET ZC8If the exponent is ZERO then exit right back out
0F0B– ↳ FINMLTPUSH AFF5Save the exponent (held in Register Pair AF) to the STACK. FOUT enters the routine here.
0F0CRST 20HGETYPEE7We 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 Flags Register A Integer NZ/C/M/E -1 String Z/C/P/E 0 Single Precision NZ/C/P/O 1 Double Precision NZ/NC/P/E 5
0F0DPUSH AFF5Save exponent and the value type from AF to the STACK
0F0E-0F10If that test shows SINGLE PRECISION, go to 093EH to multiply the current value in the ACCumulator by “10.0”
0F11POP AFF1Get the exponent and the value type from the STACK back into AF
0F12-0F14If that test shows DOUBLE PRECISION, go to 0E4DH to multiply the current value in the ACCumulator by “10D0”
0F15POP AFF1Get the exponent and the value type from the STACK and put it in Register Pair AF
0F16– ↳ DCRARTDEC A3DDecrement the exponent (held in Register A) since we have now multiplied by 1 since (x^10 = 10x^9).
0F17RETC9RETurn 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– ↳ FINDIVPUSH DED5Preserve DE to the STACK for POPing at the end
0F19PUSH HLE5Preserve HL to the STACK for POPing at the end
0F1APUSH AFF5Since we need to divide we need to preserve the exponent, so save the value in Register A on the STACK
0F1BRST 20HGETYPEE7We 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 Flags Register A Integer NZ/C/M/E -1 String Z/C/P/E 0 Single Precision NZ/C/P/O 1 Double Precision NZ/NC/P/E 5
0F1CPUSH AFF5Save the value of the FLAGS from the RST 20H call to the STACK
0F1D-0F1FIf that test shows SINGLE PRECISION, go to 0897H to divide the current value in the ACCumulator by “10.0”
0F20POP AFF1Get the value from the STACK and put it in Register Pair AF
0F21-0F23If that test shows DOUBLE PRECISION, go to 0DDCH to divide the current value in the ACCumulator by “10D0”
0F24POP AFF1Restore the flags from the STACK and put it in Register Pair F
0F25POP HLE1Get the value from the STACK and put it in Register Pair HL
0F26POP DED1Get the value from the STACK and put it in Register Pair DE
0F27INC A3CIncrement the exponent (stored in Register A) since 10x^9 = x^10
0F28RETC9RETurn 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– ↳ FINDIGPUSH DED5Save the exponent (held in Register Pair DE) on the STACK
0F2ALD A,B78We need to check where the decimal point is, so load Register A with the value in Register B
0F2BADC A,C89Increement the decimal place count if we are past the decimal point by adding the value in Register C to the value in Register A
0F2CLD B,A47Save the revised decimal point location (tracked in Register B)
0F2DPUSH BCC5Save the decimal point information (tracked in Register Pair BC) on the STACK
0F2EPUSH HLE5Save the value of the current input buffer pointer of the string being parsed in Register Pair HL on the STACK
0F2FLD A,(HL)7EFetch the digit we want to pack at the location of the current input buffer pointer in Register Pair HL
0F30-0F31SUB 30HSUB “0”D6 30Subtract 30H from the ASCII value in Register A so that it will be binary
0F32PUSH AFF5Save the adjusted value in the digit (held in Register A) to the STACK
0F33RST 20HGETYPEE7We 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 Flags Register A Integer NZ/C/M/E -1 String Z/C/P/E 0 Single Precision NZ/C/P/O 1 Double Precision NZ/NC/P/E 5
0F34-0FJ6If 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-0FJ9LD HL,(4121H)LD HL,(FACLO)2A 21 41Now that we know we have an integer, put it into the ACCumulator at (HL)
0F3DRST 18HCOMPARDFNow 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-0F3FIf 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
0F41LD D,H
LD E,L54Let DE = HL
0F42ADD HL,HL29Multiply the integer value in Register Pair HL by two
0F43ADD HL,HL29Multiply the integer value in Register Pair HL by two. Register Pair HL now holds the original integer value times four
0F44ADD HL,DE19Add 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
0F45ADD HL,HL29Multiply 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.
0F46POP AFF1Get the binary value for the number we want to pack in from the STACK and put it in Register A
0F47LD C,A4FLoad Register C with the value of the character in Register A. Why C? The DAD routine needs it there and B is already zero.
0F48ADD HL,BC09Add the value of the character in Register Pair BC to the newly shifted integer value in Register Pair HL
0F49LD A,H7CWe next need to test for an overflow, so load Register A with the MSB of the integer value in Register H
0F4AOR AB7Set the flags based on the MSB
0F4B-0F4DIf the M FLAG is set, then we have overflowed, so JUMP to FINDG1
0F4E-0F50LD (4121H),HLLD (FACLO),HL22 21 41If we are here, then we did not overflow so save the new integer back into the ACCumulator
0F51– ↳ FINDGEPOP HLE1Restore the value of the current input buffer pointer of the string being parsed into Register Pair HL
0F52POP BCC1Restore the the decimal point information (tracked in Register Pair BC) from the STACK
0F53POP DED1Restore the exponent (held in Register Pair DE) from the STACK
0F57 – Math Routine – “FINDG1”
This routine handles 32768 and 32769
0F57– ↳ FINDG1LD A,C79Load Register A with the binary value of the character in Register C
0F58PUSH AFF5Save the value in Register A on the STACK
0F59 – Math Routine – “FINDG2”
Convert integer digits into single precision digits
0F59-0F5B– ↳ FINDG2Go convert the current value in the ACCumulator to single precision
0F5CSCF37Set 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– ↳ FINDGVIf 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-0F61LD BC,9474H01 74 94Load Register Pair BC with the exponent and the MSB of a single precision constant
0F62-0F64LD DE,2400H11 00 24Load 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-0F67Call 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-0F6AIf 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-0F6DGo multiply the single precision value in the ACCumulator by 10
0F6EPOP AFF1Get the binary value of the number we want to pack in from the STACK and put it in Register A
0F6F-0F71Add the value in Register A to the single precision value in the ACCumulator
0F74 – Math Routine – “FINDG3” and “FINDGD”
The routine will convert a 7 digit single precision number into a double precision number
0F74-0F76– ↳ FINDG3Go 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– ↳ FINDGDGo multiply the double precision value in the ACCumulator by ten
0F7A-0F7CGo move the double precision value in the ACCumulator to ARG (a/k/a REG 2)
0F7DPOP AFF1Get the binary value for the number to pack in from the STACK and put it in Register A
0F84-0F86Call 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)
0F89H-0F93H – SINGLE PRECISION MATH ROUTINE – “FINLOG”
This is a subroutine for FIN and for LOG
0F89-0F8B– ↳ FINLOGCall 09A4 which moves the SINGLE PRECISION value in the ACCumulator to the STACK (stored in LSB/MSB/Exponent order)
0F8C-0F8EGo convert the value in Register A to a single precision floating number and return with the result in the ACCumulator
0F8FPOP BCC1Clear off the stack
0F90POP DED1Clear off the stack
0F91-0F93Jump 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– ↳ FINEDGLD A,E7BLoad Register A with the value of the exponent in Register E
0F95-0F96CP 0AHFE 0ATest 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-0F98If 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
0F99RLCA07Multiply the value in Register A by two
0F9ARLCA07Multiply the value in Register A by two. Register A now holds the original value of the exponent times four
0F9BADD A,E83Add the original value of the exponent in Register E to the adjusted value of the exponent in Register A
0F9CRLCA07Multiply the value in Register A by two. Register A now holds the original value of the exponent times ten
0F9DADD A,(HL)86Add 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-0F9FSUB 30HSUB “0”D6 30Convert the adjusted value in Register A to it’s binary equivalent (which is subtracting 0011 0000)
0FA0LD E,A5FSave the adjusted exponent into Register E
0FA1-0FA3JP M,321EHFA 1E 32Z-80 TRICK. If passing through, this sill never trigger, but neither will the next instruction!
0FA7H-0FAEH – DISPLAY MESSAGE ROUTINE – “INPRT”
This routine is to output a floating point number.
0FA7– ↳ INPRTPUSH HLE5Save the line number (held in Register Pair HL) to the STACK
0FA8-0FAALoad Register Pair HL with the starting address of the ” IN ” +00H message (which is 1924H)
0FAB-0FADCall 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).
0FAEPOP HLE1Get 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– ↳ LINPRTGo save the line number (held in the ACCumulator) as an integer into Register Pair HL
0FB2XOR AAFZero Register A to indicate that the output should be a free format
0FB3-0FB5Go initialize the input buffer for the ASCII conversion. This will set up the sign.
0FB6OR (HL)B6Turn off the Z FLAG.
0FB7-0FB9Go convert the integer value in the ACCumulator to an ASCII string. Return with Register Pair HL pointing to the result
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– ↳ FOUTXOR AAFZero 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– ↳ PUFOUTSave 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-0FC2AND 08HE6 08Turn 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-0FC4If a plus sign is NOT required to be added to the ASCII output string, then Jump to 0FC7H
0FC5-0FC6LD (HL),2BHLD (HL),”+”36 2BIf we are here, then it is required, so put a into the buffer pointed to by Register Pair HL
0FC7– ↳ FOUT1EX DE,HLEBLoad Register Pair DE with the value of the buffer pointer (held in Register Pair HL)
0FC8-0FCAGo determine the value of the sign for the current value in the ACCumulator
0FCBEX DE,HLEBRestore the buffer pointer back to HL
0FCC-0FCEIf the P FLAG is set then we have a negative number, so we need to negate it by JUMPing to FOUT2
0FCF-0FD0LD (HL),2DHLD (HL),”-“36 2DSave a minus sign (-) at the location of the buffer pointer in Register Pair HL
0FD1PUSH BCC5Save the field length specifications held in B and C to the STACK
0FD2PUSH HLE5Save the buffer pointer to the STACK
0FD3-0FD5GOSUB to 097BH to convert the negative value in the ACCumulator to its positive equivalent
0FD6POP HLE1Restore the buffer pointer from the STACK into HL
0FD7POP BCC1Restore the field length specifications from the STACK into B and C
0FD8OR HB4Turn off the Z FLAG. This relies on the fact that FBUFR is never on page 0
0FD9– ↳ FOUT2INC HL23Increment the buffer pointer in Register Pair HL to where the next character will be placed
0FDA-0FDBLD (HL),30H36 30Save 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-0FDELD A,(40D8H)LD A,(TEMP3)3A D8 40Load Register A with the format specification (held in a temporary storage location)
0FDFLD D,A57Preserve the format specification into Register D
0FE0RLA17Move the “free format” or “fixed format” bit into the Carry flag
0FE1-0FE3LD A,(40AFH)LD A,(VALTYP)3A AF 40Since VNEG may have changed VALTYP, re-fetch it (as -32768 is and integer but 32768 is single-precision).
0FE4-0FE6The comment in the original source says to JUMP to FOUTFX because “the man wants fixed formatted output here to print numbers in free format”
0FEA-0FEBCP 04HFE 04Check to see if the current value in the ACCumulator is single or double precision
0FEC-0FEEIf the current value in the ACCumulator is single or double precision JUMP to FOUFRV
0FEF-0FF1LD BC,0000H01 00 00If 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-0FF4Call 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– ↳ FOUTZSLoad Register Pair HL with the starting address of the buffer, which will hold the SIGN
0FF8LD B,(HL)46Load Register B with the sign (i.e., the character at the location of the buffer pointer in Register Pair HL)
0FF9-0FFALD C,20HLD C,” “0E 20Load Register C with a SPACE
0FFB-0FFDLD A,(40D8H)LD A,(TEMP3)3A D8 40Load Register A with format specifications
0FFELD E,A5FPut the format specifications into Register E
0FFF-1000AND 20HAND 0010 0000E6 20MASK the format specifications (by AND against 0010 0000) to see an asterisk fill is required
1001-1002If we do NOT need to do an asterisk fill in the ASCII string, JUMP to FOTZS1.
1003LD A,B78If 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
1004CP CB9Check 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-1006LD C,2AHLD C,”*”0E 2ALoad Register C with a the fill character, which, in this case, will be a
1007-1008If 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 .
1009LD B,C41Load Register B with the character in Register C
1001-1002If we do NOT need to do an asterisk fill in the ASCII string, JUMP to FOTZS1.
1003LD A,B78If 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
1004CP CB9Check 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-1006LD C,2AHLD C,”*”0E 2ALoad Register C with a the fill character, which, in this case, will be a
1007-1008If 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 .
1009LD B,C41Load Register B with the character in Register C
100A– ↳ FOTZS1LD (HL),C71Fill 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
100BRST 10HCHRGETD7We 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-100DIf 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-100FCP 45HCP “E”FE 45Check to see if the character at the location of the input buffer pointer in Register A is an E
1010-1011If 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-1013CP 44HCP “D”FE 44Check to see if the character at the location of the input buffer pointer in Register A is a D
1014-1015If 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-1017CP 30HCP “0”FE 30Check to see if the character at the location of the input buffer pointer in Register A is a 0
1018-1019If the character is a 0then we need to suppress it, so JUMP to FOTZS1.
101A-101BCP 2CHCP “,”FE 2CCheck to see if the character at the location of the input buffer pointer in Register A is a ,
101C-101DIf the character is a ,then we need to suppress it, so JUMP to FOTZS1.
101E-101FCP 2EHCP “.”FE 2ECheck to see if the character at the location of the input buffer pointer in Register A is a .
1022– ↳ FOTZS4DEC HL2BIf 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-1024LD (HL),30HLD (HL),”0″36 30Save a 0at the location of the input buffer pointer in Register Pair HL
1025– ↳ FOTZS2LD A,E7BNext we need to check to see if we need a floating dollar sign. First, load the format specs into Register A
1026-1027AND 10HAND 0001 0000E6 10Check to see if a $is to be included in the ASCII string
1028-1029If 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
102ADEC HL2BNeed to add a $, so first we decrement the value of the input buffer pointer in Register Pair HL …
102B-102CLD (HL),24HLD (HL),”$”36 24… and then put a $there
102D– ↳ FOTZS3LD A,E7BNext we need to check to see if we need a trailing sign. First, load the format specs into Register A
102E-102FAND 04HAND 0000 0100E6 04Turn 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
1030RET NZC0If the sign isn’t to follow the ASCII string, then we are done so RETurn
1031DEC HL2BDecrement the value of the input buffer pointer in Register Pair HL
1032LD (HL),B70Save the sign (in Register B) to the location of the input buffer pointer in Register Pair HL
1033RETC9RETurn 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– ↳ FOUINILD (40D8H),ALD (TEMP3),A32 D8 40Save the format specification (in Register A) to 40D8H.
Note: 40D8H-40D9H holds the temporary storage location
1037-1039Set 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-103BLD (HL),” “36 20Save a SPACEat the location of the input buffer pointer in Register Pair HL
103CRETC9RETurn 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– ↳ FOUFRVCP 05HFE 05Company 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.
103FPUSH HLE5Save 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-1041SBC A,00HDE 00Adjust the value of the number type in Register A. It will be 04H if SINGLE PRECISION and it will be 08H if DOUBLE precision
1042RLA17Multiply 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
1043LD D,A57Load Register D with the adjusted value of the number type in Register A
1044INC D14Bump the value of the number type in Register D (so D will be 09H if SINGLE precision and 0BH if DOUBLE precision)
1045-1047Go 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-104ALD BC,0300H01 00 03Load 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).
104BADD A,D82Test 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-104EIf D is less than .01 then we will need E Notation, so JUMP to FOFRS1
104FINC D14Now we need to see if the number is too big. Bump the value in Register D
1050CP DBACompare the bumped Register D to Register A
1051-1052If the number is too big (i.e., greater than 10^D-1), JUMP to FOFRS1
1053INC A3CIf 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
1054LD B,A47Load Register B with the decimal point count (stored in Register A)
1055-1056LD A,02H3E 02Set 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– ↳ FOFRS1SUB A,02HD6 02Compute the exponent value (which will be a zero if we were passing through), so now D-2 will be added to it
1059POP HLE1Get the pointer to the string buffer from the STACK and put it in Register Pair HL
105APUSH AFF5Save the exponent value (currently in Register A) to the STACK
105B-105DGOSUB 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-105FLD (HL),30HLD (HL),”0″36 30If the number is within that range, then add a 0at the location of the buffer pointer in Register Pair HL
1060-1062If there was no scaling, GOSUB to 09C9H to bump HL and return
1063-1065Next 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– ↳ FOFRS2DEC HL2BBackspace to the last character by decrementing the value of the input buffer pointer in Register Pair HL
1067LD A,(HL)7EFetch the last character (at the location of the input buffer pointer in Register Pair HL)
1068-1069CP 30HCP “0”FE 30Check to see if the value in Register A is a 0
106A-106BIf 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-106DCP 2EHCP “.”FE 2ECheck to see if the last character in the buffer (now that all ending zeroes have been supressed) is is a .
106E-1070If 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.
1071POP AFF1Restore the exponent from the STACK into Register A
1072-10731If 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– ↳ FOFLDNPUSH AFF5Save the exponent (stored in A) to the STACK
1075RST 20HGETYPEE7Determine 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-1077LD A,22H3E 22Load 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
1078ADC A,A8FMultiply 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
1079LD (HL),A77Save the exponent designation (the Dor Ein Register A) at the location of the input buffer pointer in Register Pair HL
107AINC HL23Bump the value of the buffer pointer in Register Pair HL, which is the first position of the exponent in the buffer
107BPOP AFF1Get the value of the exponent from the STACK and put it in Register A
107C-107DLD (HL),2BHLD (HL),”+”36 2BSave 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-1080If 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-1082LD (HL),2DHLD (HL),”-“36 2DSave 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
1083CPL2FIf 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
1084INC A3CWe 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– ↳ FOUCE1LD B,2FHLD B,”0″-106 2FAt 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– ↳ FOUCE2INC B04Top 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-1089SUB A,0AHD6 0ASubtract ten from the value of the exponent in Register A
108A-108BLoop 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-108DADD A,3AHC6 3ASince 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
108EINC HL23Bump the value of the buffer pointer in Register Pair HL
108FLD (HL),B70Save 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
1090INC HL23Bump the value of the buffer pointer in Register Pair HL
1091LD (HL),A77Save 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– ↳ FOUTZRINC HL23Bump 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– ↳ FOUTDNLD (HL),00H36 00Save an end of the ASCII string character (designated as 00H) at the location of the input buffer pointer in Register Pair HL
1095EX DE,HLEBSince the FFXFLV routine will need the buffer pointer in DE instead of HL, swap those registers
1096-1098Load Register Pair HL with the starting address of the buffer pointer.
Note: 4130H-4149H holds an internal print buffer
1099RETC9DONE! RETurn to CALLer
109A- LEVEL II BASIC MATH ROUTINE– “FOUTFX”
This routine will print a number in fixed format.
109A– ↳ FOUTFXINC HL23Bump the value of the buffer pointer in Register Pair HL
109BPUSH BCC5Save 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-109DCP 04HFE 04Check to see if the current number type in ACCumulator is single or double precision
109ELD A,D7ALoad Register A with the format specifiers (held in Register D)
109F-10A1If 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.
10A2RRA1FRotate 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-10A5If 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-10A8LD BC,0603H01 03 06Load Register B to a decimal counte of 6 and Load Register C with a comma count of 3
10A9-10ABGo check to see if commas are needed. If no comma is needed, set C to zero
10ACPOP DED1Restore the field lengths (the number of #’s to the left and right of the decimal point) from the STACK into Register DE
10ADLD A,D7ALoad Register A with the number of digits requested to the left of the decimal point in Register D
10AE-10AFSUB A,05HD6 05Since 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-10B2If 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-10B5Convert 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– ↳ FOUTTDLD A,E7BNext 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)
10B7OR AB7Check to see if there are any digits to the right of the decimal point requested and set the status flags accordingly
10B8-10BAIf 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
10BBDEC A3DNext 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-10BEIf 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– ↳ FOUTTSPUSH HLE5Save 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-10C2Go edit the ASCII string in the input buffer to suppress any zeroes, if needed.
10C3POP HLE1Get the saved buffer pointer value from the STACK and put it in Register Pair HL
10C4-10C5If the Z FLAG is set, then we do NOT have a trailing sign, so we JUMP away to 10C8H
10C6LD (HL),B70So 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
10C7INC HL23Bump the value of the input buffer pointer in Register Pair HL
10C8-10C9– ↳ FFXIX1LD (HL),00H36 00Terminate 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-10CCLoad 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– ↳ FOUBE1INC HL23Bump the value of the buffer pointer in Register Pair HL
10CE-10D0– ↳ FOUBE5LD A,(40F3H)LD A,(TEMP2)3A F3 40Load 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.
10D1SUB A,L95First, 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
10D2SUB A,D92Next, 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!
10D3RET ZC8If we have taken the right amount of space, then we are done, so we RETurn
10D4LD A,(HL)7EIf 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-10D6CP 20HCP ” “FE 20Check 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
10D9-10DACP 2AHCP “*”FE 2ACheck 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
10DDDEC HL2BSince we want to ignore ‘s we decrement the value of the input buffer pointer in Register Pair HL so it will get re-tested
10DEPUSH HLE5Save 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] 0 a zero [mandatory] . a decimal point [mandatory] 0-9 another 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– ↳ FOUBE2PUSH AFF5Save the the current character (which is the value in Register Pair AF) to the STACK. This also saves the ZERO FLAG.
10E0-10E2Load Register Pair BC with the return address for use in case we have a –, a +, or a $
10E3PUSH BCC5Save the return address in Register Pair BC to the STACK
10E4RST 10HCHRGETD7Since 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-10E6CP 2DHCP “-“FE 2DCheck to see if the character at the location of the input buffer pointer in Register A is a –
10E7RET ZC8Return (to 10DFH) if the character at the location of the input buffer pointer in Register A is a –
10E8-10E9CP 2BHCP “+”FE 2BCheck to see if the character at the location of the input buffer pointer in Register A is a +
10EARET ZC8Return (to 10DFH) if the character at the location of the input buffer pointer in Register A is a +
10EB-10ECCP 24HCP “$”FE 24Check to see if the character at the location of the input buffer pointer in Register A is a $
10EDRET ZC8Return (to 10DFH) if the character at the location of the input buffer pointer in Register A is a $
10EEPOP BCC1We 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-10F0CP 30HCP “0”FE 30Check to see if the character at the location of the input buffer pointer in Register A is a 0
10F1-10F2If 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
10F3INC HL23Bump the value of the input buffer pointer in Register Pair HL so that we skip over the decimal point to the next character
10F4RST 10HCHRGETD7Since 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-10F6If the character after the decimal point is not a digit then we can’t shorten the field anymore, so JUMP to FOUBE4
10F7DEC HL2BIf 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
10F8LD BC,772BH01 2B 77Z-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– ↳ FOUBE3DEC HL2BIf 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)
10FALD (HL),A77If 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
10FBPOP AFF1Get the character from the STACK and put it in Register Pair AF
10FC-10FDIf the Z FLAG is set, then we LOOP back to FOUBE3 to put the character back into the buffer
10FEPOP BCC1Restore the buffer pointer from the top of the STACK into Register Pair BC
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– ↳ FOUBE4POP AFF1Restore the character from the STACK and put it in Register Pair AF
1103-1104If the Z FLAG is set, then LOOP back 1 instruction to leave the number in the buffer alone
1105POP HLE1Get 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-1107LD (HL),25HLD (HL),”%”36 25Show that we have overflowed the field by putting a %character at the front.
1108RETC9All 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– ↳ FOUFXVPUSH HLE5Save the buffer pointer in Register Pair HL to the STACK
110ARRA1FRotate 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-110DIf the CARRY FLAG was set, then we know we are printing the number in “E” notation, so JUMP to FFXFLV
110E-110FIf 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-1112Load 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-1115Since 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-1117LD D,10H16 10Load Register D with the maximum length of a double precision value (which is 16 in decimal)
1118-111AIf 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– ↳ FFXSDOPOP HLE1Get the current buffer pointer from the STACK and put it in Register Pair HL
111CPOP BCC1Get 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-111FPrint the number in free format via a GOSUB to FOUT (which will convert a double precision value in ACCumulator to an ASCII string)
1120DEC HL2BDecrement the input buffer pointer in Register Pair HL to point in front of the number
1121-1122LD (HL),25HLD (HL),”%”36 25Save a %character at the location of the input buffer pointer in Register Pair HL
1123RETC9All 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
1127-1129LD DE,1BCAH11 CA 1BRegister Pairs BC and DE now hold a single precision constant equal to 1×10^16
112A-112CCall 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 = BCDE A=00 If ACCumulator > BCDE A=01 If ACCumulator < BCDE A=FF
112D-112FIf 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-1131LD D,06H16 06Now 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– ↳ FFXSDCGOSUB to SIGN to see if we have a ZERO in the ACCumulator
1135-1137If 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
1138POP HLE1Get the buffer pointer from the STACK and put it in Register Pair HL
1139POP BCC1Get 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
This routine will print a number that has no fractional digits
113DPUSH BCC5Save the value in Register Pair BC (B was the number of #‘s before and C is the number of #‘s after) to the STACK
113ELD E,A5FLoad 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.
113FLD A,B78Load Register A with the number of digits before the decimal point requested
1140SUB A,D92Subtract the maximum length for the current number type in Register D (which is 6) from the number of digits requested in Register A
1141SUB A,E93Subtract the number of times the current value in ACCumulator was divided in Register E from the adjusted value in Register A
1142-1144If 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-1147Next, set up the decimal point and comma counts via a GOSUB to DOUTCD
1148-114AThen, 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
114BOR EB3Merge in the number of digits after the number, if the field is big enough of course.
114C-114EIf 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
114FOR EB3Check to see if commas or the decimal point is needed
1150-1152Go put commas and the decimal point into the input buffer if necessary
1153POP DED1Retrieve the field length specs from the STACK and put it in Register Pair DE
1154-1156Jump 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– ↳ FFXXVSLD E,A5FPreserve the exponent into Register E
1158LD A,C79Prepare 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
1159OR AB7Check to see if any digits to the right of the decimal point was requested
115A-115CGo decrement the number of digits requested to the right of the decimal point if necessary
115DADD A,E83Add 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-1160If 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
1161XOR AAFZero Register A
1162– ↳ FFXXV8PUSH BCC5Save 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
1163PUSH AFF5Save the the scale count (held in Register Pair AF) to the STACK
1164-1166– ↳ FFXXV2Top of a divide loop. GOSUB to 0F18H to divide the value in ACCumulator by ten, A times, if necessary
1167-1169Loop until the value in ACCumulator is properly adjusted. When this is done, A will hold the number of times it was divided by 10
116APOP BCC1Get the original scale count from the STACK and put it in Register Pair BC
116BLD A,E7BWe 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
116CSUB A,B90Subtract the value in Register B from the value in Register A
116DPOP BCC1Get 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
116ELD E,A5FCalculate the number of decimal places before the number ends by first loading Register E with the adjusted scale factor value in Register A …
116FADD 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
1170LD A,B78Load Register A with the number of #‘s before (stored in B)
1171-1173Jump 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.
1174SUB A,D92We 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
1175SUB A,E93Then, subtract the adjusted scale value in Register E from the adjusted value in Register A
1176-1178If that subtraction leads to a positive number, go put leading zeros into the input buffer
1179PUSH BCC5Save 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
117D-117EJump 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– ↳ FFXXV3Go put leading zeros (as needed) into the input buffer
1182LD A,C79Load 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
1186LD C,A4FReload Register C with the number of digits requested to the right of the decimal point in Register A
1187XOR AAFNext we need to calculate how many zeroes to put between the decimal point and the first digit, so start by zeroing Register A
1188SUB A,D92Then – subtract the maximum length for the current value in Register D from the value in Register A
1189SUB A,E93Then – subtract the value in Register E from the adjusted value in Register A
118DPUSH BCC5Save the value in Register Pair BC (B is the exponent and C is the number of #‘s after) to the STACK
118ELD B,A47Load Register B (i.e., the decimal place count) with the value in Register A (which is 0)
118FLD C,A4FLoad Register C (i..e, the comma count) with the value in Register A (which is 0)
1190-1192– ↳ FFXXV6GOSUB to 12A4H to convert the integer portion of the SINGLE precision value in ACCumulator to an ASCII string. These will be the decimal digits.
1193POP BCC1Get the number of #‘s before and number of #‘s after and put it back in Register Pair BC
1194OR CB1Check 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-1196If 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-1199LD HL,(40F3H)LD HL,(TEMP2)2A F3 40Now 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– ↳ FFXXV7ADD A,E83Add the value in Register E to the value in Register A to get the number of digits before the decimal point
119BDEC A3DDecrement the adjusted value in Register A
119C-119EIf dropping A by 1 still results in a positive number, GOSUB 1269H to put that number of zeros into the input buffer
119FLD D,B50Load Register D with the number of digits to the left of the decimal point requested (from Register B)
11A3 – LEVEL II BASIC MATH ROUTINE– “FFXIFL”
This routine will print an integer in fixed format/floating point notation.
11A3– ↳ FFXIFLPUSH HLE5Save the current position of the buffer (in Register Pair HL) to the STACK
11A4PUSH DED5Generally save Register Pair DE to be POPped after the CALL. DE currently holds the format specs
11A5-11A7GOSUB 0ACCH to convert the integer value in ACCumulator to a SINGLE precision value
11A8POP DED1Restore DE from the STACK
11A9XOR AAFZero 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– ↳ FFXFLVIf we have a SINGLE PRECISION number (because the Z FLAG is set), Jump to 11B0H to set the flags appropriately
11AD-11AELD E,10H1E 10We know we have a DOUBLE PRECISION so load Register E with the maximum length of a double precision value (which is 16)
11AF-11B2LD BC,1E06H01 1E 06Z-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– ↳ FFXSFLLD E,06H1E 06Load Register E with the maximum length of a single precision value (which is 6)
11B2-11B4GOSUB 0955H to check to see if we have a zero in the ACCumulator
11B5SCF37Set the Carry flag to determine if we are printing a zero or not. This works because FOUTNV exits with the NC FLAG set
11B6-11B8If 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
11B9POP HLE1Get the buffer position from the STACK and put it in Register Pair HL
11BAPOP BCC1Get the number of #‘s before and the number of #‘s after from the STACK and put it in Register Pair BC
11BBPUSH AFF5Save the exponent in Register Pair AF to the STACK
11BCLD A,C79We 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)
11BDOR AB7Set the status so we can see if there are any digits to the right of the decimal point requested through a zero register
11BEPUSH AFF5Save the original trailing digit count (in Register Pair AF) to the STACK
11BF-11C1If 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
11C2ADD A,B80Add 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
11C3LD C,A4FLoad Register C with the total digit count (held in Register A)
11C4LD A,D7ALoad Register A with the value of the edit flag in Register D
11C5-11C6AND 04HAND 0000 0100E6 04Check to see if the sign follows the ASCII string (i.e., is a “trailing” sign)
11C7-11C8CP 01HFE 01Set 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)
11C9SBC A,A9FIf 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.
11CALD D,A57Load Register D with those results
11CBADD A,C81Add the value in Register C to the value in Register A so as to set the number of significant digits to print
11CCLD C,A4FLoad Register C with the adjusted value in Register A
11CDSUB A,E93If 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
11CEPUSH AFF5Save 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.
11CFPUSH BCC5Save the “B” field spec and the number of significant digits (from Register Pair BC) to the STACK
11D0-11D2– ↳ FFXLV1GOSUB 0F18H to divide the current value in ACCumulator by ten, Register A number of times
11D3-11D5Loop back 1 instruction (divide by 10) until the division has been completed
11D6POP BCC1Retrieve the “B” field spec and the number of significant digits from the STACK back into Register Pair BC
11D7POP AFF1Get the number of trailing zeroes to print from the STACK and put it in Register Pair A
11D8PUSH BCC5Save the “B” field spec and the number of significant digits (from Register Pair BC) to the STACK
11D9PUSH AFF5Save the number of trailing zeroes to print to the STACK
11DA-11DCSkip the next instruction (i.e., jump to 11DEH) if there are any trailing zeroes
11DDXOR AAFZero Register A and all status flags
11DE– ↳ FFXLV3CPL2FMake the trailing zero count posivite by inverting the value in Register A
11DFINC A3CBump the value in Register A so that it will be positive
11E0ADD A,B80Set 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
11E1INC A3CBump the adjusted value in Register A
11E2ADD A,D82Take 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
11E3LD B,A47Copy Register A into Register B so that B holds the number of digits before the decimal point
11E4-11E5LD C,00H0E 00Set the comma count to zero by loading Register C with zero (so that there are no commas)
11E6-11E8GOSUB to 12A4H to convert the current value in ACCumulator to decimal digits
11E9POP AFF1Get the number of #’s before from the STACK and put it in Register Pair AF
11EDPOP BCC1Get the number of #’s before and the number of #‘s after from the STACK and put it in Register Pair BC
11EEPOP AFF1Get the C count (numbers before the decimal point) from the STACK and put it in Register Pair A and restore the FLAGS
11EF-11F1If 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
11F2POP AFF1Get the exponent back from from the STACK and put it in Register Pair AF
11F3-11F4If the number is zero, then the exponent is zero, so JUMP to 11F8H (to add the exponent) if the carry flag was set
11F5ADD A,E83Otherwise, we need to scale the number – so first add the value in Register E to the value in Register A
11F6SUB A,B90Subtract the number of digits to the left of the decimal point requested in Register B from the adjusted value in Register A
11F7SUB A,D92Subtract the value in Register D from the value in Register A to get the size of the exponent
11F8– ↳ FFXLV2PUSH BCC5Save the “B” field spec to the STACK
11F9-11FBPut the exponent into the buffer via a GOSUB to 1074H to figure the value of the exponent for the current value in ACCumulator
11FCEX DE,HLEBSwap 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.
11FDPOP DED1Restore the “B” field spec into Register D in case we need to put on a trailing sign.
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– ↳ FOUTNVPUSH DED5Save the value in Register Pair DE to the STACK. We are going to pop this back at the end of the routine.
1202XOR AAFZero Register A, which will be the exponent
1203PUSH AFF5Save the exponent in Register Pair A to the STACK
1204RST 20HGETYPEE7We 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-1207If 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– ↳ FORBIGLD A,(4124H)LD A,(FAC)3A 24 41At 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-120CCP 91HFE 91Check 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-120FIf the double precision value in ACCumulator is not less than 1D5, then ship over the following multiplcation code and go to FOUNDB.
1219-121BGOSUB 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)
121CPOP AFF1Retrieve the original exponent from the STACK into Register A
121D-121ESUB A,0AHD6 0ASubtract ten from the value in Register A to do a proper offset for an exponent
121FPUSH AFF5Save the adjusted exponent (held in Register A) to the STACK
1220-1221Force 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– ↳ FOUNDBCheck 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– ↳ FOUNV1RST 20HGETYPEE7In 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-1228If that test shows we have a DOUBLE PRECISION or a STRING, jump to 1234H
1226-1228In 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-122ALD BC,9143H01 43 91Load Register Pair BC with the exponent and the MSB of a single precision constant
122C-122E
122B-122DLD DE,4FF9H11 F9 4FLoad 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-1230GOSUB 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 = BCDE A=00 If ACCumulator > BCDE A=01 If ACCumulator < BCDE A=FF
1232-1233Jump down two instructions to 123AH to test the results of the comparison
1234-1236– ↳ FOUNV4
1233-1235Load Register Pair DE with the starting address of a double precision constant equal to 999,999,999,999,999.95
1237-1239
1236-1238Go 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-123BIf the number isn’t too small anymore then we are done so JUMP to 124CH
1239-123B– ↳ FOUNV5If the number isn’t too small anymore then we are done so JUMP to 124BH
123D
123CPOP AFF1If 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-123FGOSUB to 0F0BH to multiply the current value in ACCumulator by ten
1241
1240PUSH AFF5Save the exponent value (the negative of the number of times the value was multiplied) in Register Pair AF to the STACK.
1242-1243
1241-1242Keep looping back to see if the number is big enough (i.e., between 999,999 and 99,999)
1244
1243POP AFF1At 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-1246GOSUB to 0F18H to divide the current value in ACCumulator by ten
1248
1247PUSH AFF5Save the exponent to the STACK. A is the count of the number of times it was divided
1249-124B
1248-124AWe 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
124BPOP AFF1At this point, we are done scaling, so restore the exponent into Register A. A = + times divided or – times multiplied
124D
124CPOP DED1Restore DE from where it was preserved at the top of this routine
N/A
124DOR AIn ROM v1.2 sets the status flags. This also realigns the memory addresses from changes to v1.2 ROM
124ERETC9RETurn to CALLer
124F – LEVEL II BASIC MATH ROUTINE– “FOUNVC”
This routine will see if the number in the ACCumulator is small enough yet
124F– ↳ FOUNVCRST 20HGETYPEE7We 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-1252If 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-1255LD BC,9474H01 74 94Now 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-1258LD DE,23F8H11 F8 23Load 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-125BCall 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 = BCDE A=00 If ACCumulator > BCDE A=01 If ACCumulator < BCDE A=FF
125E-1260– ↳ FONVC1If 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-1263Check 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– ↳ FONVC2POP HLE1Get the return address from the STACK and put it in Register Pair HL so we can go to 1244H
1265-1267If 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
1268JP (HL)E9If 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– ↳ FOTZEROR AB7This is the entry point from FFXXV3 where the flags have not yet been set, so set the flags, particularly the Z FLAG
126A– ↳ FOTZR1RET ZC8Top of a loop. If the number of 0’s we need to display is zero, then just RETurn
126BDEC A3DDecrement the value in Register A to show that an ASCII zero was moved to the print buffer
126C-126DLD (HL),30HLD (HL),”0″36 30Save a 0at the location of the input buffer pointer in Register Pair HL
126EINC HL23Bump the input buffer pointer in Register Pair HL
126F-1270Jump 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.
1273– ↳ FOTZRCRET ZC8Top of a loop. If there are no more zeroes to add, RETurn
1274-1276Check 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– ↳ FOTZECLD (HL),30HLD (HL),”0″36 30Save a 0at the location of the input buffer pointer in Register Pair HL
1279INC HL23Bump the input buffer pointer in Register Pair HL
127ADEC A3DDecrement the counter of trailing zeroes to add in Register A
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– ↳ FOUTCDLD A,E7BThe 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
127EADD A,D82Add the number of digits to print (from Register D) to the value in Register A
127FINC A3CBump the adjusted value in Register A so now A holds the number of digits before the decimal point
1280LD B,A47Load Register B with the leading digit count (from Register A)
1281INC A3CNext, 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– ↳ FOTCD1SUB A,03HD6 03Subtract three from the adjusted value in Register A which, when combined with the next instruction as a loop, divides modulo 3
1284-1285Loop back 1 instruction until the value in Register A is -1, -2, or -3
1286-1287ADD A,05HC6 05Add 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
1288LD C,A4FSave the possible comma count into Register A
1289-128B– ↳ FOUICCLD A,(40D8H)LD A,(TEMP3)3A D8 40Load Register A with the format specs from the temporary storage location
128C-128DAND 40HAND 0100 0000E6 40Mask against 0100 0000 to isolate the comma bit to see if commas are requested
128ERET NZC0If the NZ FLAG is set then we are using commas, so just RETurn
128FLD C,A4FIf we are here, then we aren’t using commas, so Zero the comma counter in Register C
1290RETC9RETurn 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– ↳ FOUTEDDEC B05First 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-1293If the decimal point position hasn’t been reached then JUMP to FOUED1 to see if a comma needs to go there.
1294-1295– ↳ FOUTDPLD (HL),2EHLD (HL),”.”36 2EIf 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-1298LD (40F3H),HLLD (TEMP2),HL22 F3 40Save the address of the decimal point position (held in Register Pair HL).
Note: 40F3H-40F4H is a temporary storage location
1299INC HL23Bump the buffer pointer in Register Pair HL
129ALD C,B48We just put in a decimal point, so we KNOW we don’t need to put a comma here, so ZERO out the comma counter
129BRETC9RETurn 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– ↳ FOUED1DEC C0DFirst, we need to test to see if it is time to put in a comma by DECrementing the comma counter in Register C
129DRET NZC0If the NZ FLAG is set, then we are not putting in a comma, so RETurn
129E-129FLD (HL),2CHLD (HL),”,”36 2CIf 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
12A0INC HL23Bump the input buffer pointer (to account for the new comma) in Register Pair HL
12A1-12A2LD C,03H0E 03Reset the comma counter by setting it to 3 (since commas come after units of 3 numbers)
12A3RETC9RETurn 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– ↳ FOUTCVPUSH DED5Generally preserve Register Pair DE. This will get POPped when the subroutine is done.
12A5RST 20HGETYPEE7We 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-12A8If 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)
12A9PUSH BCC5Now that we know we have a DOUBLE PRECISION number, save decimal/comma count (in Register Pair BC) to the STACK
12AAPUSH HLE5Save the buffer address (in Register Pair HL) to the STACK
12AB-12ADGOSUB to 09FCH to mmove the double precision value in ACCumulator to REG2
12AE-12B0Load Register Pair HL with the starting address of a double precision constant equal to 0.5D0
12B4-12B6Call 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)
12B7XOR AAFZero Register A and clear the status flags; particularly the CARRY FLAG
12B8-12BAIsolate the integer part of the double precision number via a GOSUB to 0B7BH
12BBPOP HLE1Restore the buffer address from the STACK and put it in Register Pair HL
12BCPOP BCC1Restore the decimal and comma counters from the STACK and put it in Register Pair BC
12BD-12BFLoad 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-12C1LD A,0AH3E 0AWe 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– ↳ FOUCD1Check 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
12C5PUSH BCC5Save 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
12C6PUSH AFF5Save the number of digits to process / division count (stored in Register Pair A) to the STACK
12C7PUSH HLE5Save the current buffer address (stored in Register Pair HL) to the STACK
12C8PUSH DED5Save the address of the power of 10 table (stored in Register Pair DE) to the STACK
12C9-12CALD B,2FHLD B,”0″-106 2FLoad 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– ↳ FOUCD2INC B04Top of a loop. Bump the ASCII value for the digit in Register B so as to start with ASCII “0”
12CCPOP HLE1Get the address of the power of 10 table (i.e., the divisor) from the STACK and put it in Register Pair HL and
12CDPUSH HLE5…. put it right back into the STACK so that it can be restored during the loop
12CE-12D0GOSUB 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-12D2Jump 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)
12D3POP HLE1If 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-12D6GOSUB 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
12D7EX DE,HLEBSwap DE and HL so that th eopoert of ten pointer is now in DE.
12D8POP HLE1Get the current buffer address from the STACK and put it in Register Pair HL
12D9LD (HL),B70Save the ASCII value for the digit in Register B at the location of the input buffer pointer (stored in Register Pair HL)
12DAINC HL23Bump the buffer pointer in Register Pair HL since we have just put a digit there
12DBPOP AFF1Get the loop counter back into Register A
12DCPOP BCC1Get the decimal point and comma counter from the STACK and put it in Register Pair BC
12DDDEC A3DDecrement the loop counter value in Register A (we are going to loop 10 times)
12E0PUSH BCC5At 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
12E1PUSH HLE5Save the input buffer pointer (stored in Register Pair HL) to the STACK
12E2-12E4Point HL to the remaining digits for processing as a single precision number
12E5-12E7Move the numbers that are left to ACCumulator via a GOSUB to MOVRM
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– ↳ FOUTCSPUSH BCC5Save the decimal/comma count (in Register Pair BC) to the STACK
12EBPUSH HLE5Save the buffer pointer (stored in Register Pair HL) to the STACK
12EC-12EERound 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
12EFINC A3CWhen 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-12F2GOSUB to 0AFBH to convert the positive single precision value in ACCumulator to an integer. The result is stored in C/D/E
12F3-12F5Save the C/D/E number into the ACCumulator via a GOSUB to MOVFR
12F6– ↳ FOUCDCPOP HLE1Get the current buffer pointer value from the STACK and put it in Register Pair HL
12F7POP BCC1Get the decimal/comma count value from the STACK and put it in Register Pair BC
12F8XOR AAFClear the CARRY FLAG, which is our flag to calculate two digits
12F9-12FBLoad 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– ↳ FOUCS1CCF3FComplement the Carry flag, which tracks when we are done with the division loop of 12FC-1327H
12FD-12FFCheck to see if we need to put a decimal point or a comma before the current number via GOSUB to FOUTED
1300PUSH BCC5Save the decimal and comma counter (stored in Register Pair BC) to the STACK
1301PUSH AFF5Save the carry flag (which acts as our digit count for the count of the number of times through this loop) to the STACK
1302PUSH HLE5Save the current buffer pointer value (stored in Register Pair HL) to the STACK
1303PUSH DED5Save the power of 10 table pointer (stored in Register Pair DE) to the STACK
1304-1306Loads the SINGLE PRECISION value in ACCumulator into Register Pair BC/DE via A GOSUB to MOVRF.
1307POP HLE1Get the power of 10 table address (the integer value for 100,000) from the STACK and put it in Register Pair HL
1308-1309LD B,2FHLD B,”0″-106 2FSet 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– ↳ FOUCS2INC B04Bump the ASCII value from the digit in Register B to increase the ASCII value from 0 and upward
130BLD A,E7BLoad Register A with the Low Order/LSB of the single precision value in Register E
130CSUB (HL)96Subtract 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
130DLD E,A5FLoad Register E with the adjusted LSB of the single precision value in Register A
130EINC HL23Bump the value of the memory pointer in Register Pair HL to the next digit of 100,000
130FLD A,D7ALoad Register A with the Middle Order/NMSB of the single precision value in Register D
1310SBC A,(HL)9ESubtract 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
1311LD D,A57Load Register D with the adjusted NMSB of the single precision value in Register A
1312INC HL23Bump the value of the memory pointer in Register Pair HL to the MSB of 100,000
1313LD A,C79Load Register A with the High Order/MSB of the single precision value in Register C
1314SBC A,(HL)9ESubtract 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)
1315LD C,A4FLoad Register C with the adjusted MSB of the single precision value in Register A
1316DEC HL2BDecrement the value of the memory pointer in Register Pair HL to the NMSB of 100,000
1317DEC HL2BDecrement the value of the memory pointer in Register Pair HL again, now to the LSB of 100,000
1318-1319Loop until the ASCII value for the digit under 100,000 has been figured
131A-131CWe 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
131DINC HL23Bump the value of the memory pointer in Register Pair HL to now point to the 10,000 constant
131E-1320Save the remainder as a current value by GOSUB to 09B4H (which moves the SINGLE PRECISION value in DC/DE into ACCumulator)
1321EX DE,HLEBLoad 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)
1322POP HLE1Get the value of the memory pointer from the STACK and put it in Register Pair HL
1323LD (HL),B70Save the ASCII value for the digit in Register B at the location of the input buffer pointer in Register Pair HL
1324INC HL23Bump the value of the input buffer pointer in Register Pair HL
1325POP AFF1Get the carry flag from the STACK and put it in Register Pair AF
1326POP BCC1Get the value from the STACK and put it in Register Pair BC so it can be saved later
1327-1328If the carry flag is set, then reset it and loop the dividing by 10,000 until the integer portion is found
1329INC DE13If 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
132AINC DE13and again bump the value of the memory pointer in Register Pair DE, so now DE points to the constant 1,000
132B-132CLD A,04H3E 04Load Register A with the number of digits for the ASCII string to be figured
132D-132EJump 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– ↳ FOUTCIPUSH DED5Generally preserve DE. This will be POPped just before the RETurn
1330-1332Load Register Pair DE with the starting address of the descending powers of 10 starting at 10,000
1333-1334LD A,05H3E 05Load 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– ↳ FOUCI1Top 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
1338PUSH BCC5Save the decimal and comma counter (stored in Register Pair BC) to the STACK
1339PUSH AFF5Save the number of digits-to-process counter (stored in Register A) to the STACK
133APUSH HLE5Save the address of the power table (stored in Register Pair HL) to the STACK
133BEX DE,HLEBLoad Register Pair HL with the starting address of the descending powers of 10 starting at 10,000 (stored in Register Pair DE)
133CLD C,(HL)4ELoad Register C with the LSB for the power of 10 stored in Register Pair HL
133DINC HL23Bump the value of the memory pointer in Register Pair HL to be the MDB of the power of 10
133ELD B,(HL)46Load Register B with the MSB for the integer value at the location of the memory pointer in Register Pair HL
133FPUSH BCC5Save the integer value of the power of 10 in Register Pair BC to the STACK
1340INC HL23Bump the value of the memory pointer in Register Pair HL to the next value in the power of 10 table
1341EX (SP),HLE3Swap (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
1342EX DE,HLEBPut the power of ten into DE
1343-1345LD HL,(4121H)LD HL,(FACLO)2A 21 41Load Register Pair HL with the integer value in ACCumulator
1346-1347LD B,2FHLD B,”0″ – 106 2FSince 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– ↳ FOUCI2INC B04Bump the ASCII value for the digit in Register B (so it starts at 0 and moves up each loop)
1349LD A,L7DLoad Register A with the LSB of the integer value in Register L
134ASUB E93Subtract 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
134BLD L,A6FLoad Register L with the adjusted value of the LSB of the integer value in Register A
134CLD A,H7CLoad Register A with the value of the MSB of the integer value in Register H
134DSBC A,D9ASubtract the MSB of the integer value in Register D from the value of the MSB of the integer value in Register A
134ELD H,A67Load Register H with the adjusted value of the MSB of the integer value in Register A
134F-1350If the quotient (stored in HL) >= the current power of 10 (stored in DE) then we need to loop back to 1348H
1351ADD HL,DE19The 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-1354LD (4121H),HLLD (FACLO),HL22 21 41Save the integer remainder (stored in Register Pair HL) in ACCumulator
1355POP DED1Get the address of the next power of 10 from the STACK and put it in Register Pair DE
1356POP HLE1Get the memory pointer for the buffer from the STACK and put it in Register Pair HL
1357LD (HL),B70Save 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)
1358INC HL23Bump the value of the buffer pointer in Register Pair HL since we just filled that spot with an ASCII value
1359POP AFF1Get the number of digits to convert (i.e., the digit counter) from the STACK and put it in A
135APOP BCC1Get the decimal/comma counts from the STACK and put it into Register Pair BC
135BDEC A3DDecrement the value of the counter in Register A (which is a countdown from 5)
135C-135DIf 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-1360So 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
1361LD (HL),A77Save 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.
1362POP DED1Get the value from the STACK (which was whatever value was in DE when this routine started) and put it in Register Pair DE
1363RETC9RETurn to CALLer with A=0
1364-136B – DOUBLE PRECISION CONSTANT STORAGE LOCATION– “TENTEN”
1364-136B– ↳ TENTEN00 00 00 00 F9 02 15 A2A double precision constant equal to 10000000000 is stored here
136C-1373 – DOUBLE PRECISION CONSTANT STORAGE LOCATION– “FOUTDL”
136C-1373– ↳ FOUTDLFD FF 9F 31 A9 5F 63 B2A double precision constant equal to 999,999,999,999,999.95 is stored here
1374-137B – DOUBLE PRECISION CONSTANT STORAGE LOCATION– “FOUTDU”
1374-137B– ↳ FOUTDUFE FF 03 BF C9 1B 0E B6A 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– ↳ DHALF00 00 00 00A 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– ↳ FHALF00 00 00 80A 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– ↳ FFXDXM00 00 04 BF C9 1B 0E B6A double precision constant equal to 1D16
138C-13D1 – DOUBLE PRECISION INTEGER CONSTANT STORAGE LOCATION– “FODTBL”
138C-1392– ↳ FODTBL00 80 C6 A5 7E 8D 031D15
1393-139900 40 7A 10 F3 5A 001D14
139A-13A000 A0 72 4E 18 09 001D13
13A1-13A700 10 A5 D5 E8 00 001D12
13A1-13A700 10 A5 D5 E8 00 001D11
13A8-13AE00 E8 76 48 17 00 001D10
13AF-13B500 E4 0B 54 02 00 001D9
13B6-13BC00 CA 9A 3B 00 00 001D8
13BD-13C300 E1 F4 05 00 00 001D7
13C4-13CA80 96 98 00 00 00 001D6
13CB-13D140 42 0F 00 00 00 001D5
13D2-13D9 – SINGLE PRECISION POWER OF TEN TABLE LOCATION– “FOSTBL
13D2-13D4– ↳ FOSTBLA0 86 011E5
13D5-13D710 27 001E4
13D8 – SINGLE PRECISION POWER OF TEN TABLE LOCATION– “FOITBL
13D8-13D9– ↳ FOITBL10 2710,000
13DA-13DBE8 031,000
13DC-13DD64 00100
13DE-13DF0A 0010
13E0-13E101 001
13E2-13E6 – LEVEL II BASIC MATH ROUTINE– “PSHNEG”
13E2-13E4– ↳ PSHNEGLoad Register Pair HL with the address of the routine for conversion of floating point numbers from negative to positive
13E5EX (SP),HLE3Exchange the value of that routines jump address to the STACK with the value of the return address in Register Pair HL
135E-1360So 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– ↳ SQRGOSUB 09A4 which moves the SINGLE PRECISION value in ACCumulator to the STACK (stored in LSB/MSB/Exponent order)
13EA-13ECLoad Register Pair HL with the starting address of a single precision constant equal to 0.5 (which will be the exponent)
13ED-13EFGOSUB 09B1H (which moves a SINGLE PRECISION number pointed to by HL to ACCumulator)
13F0-13F1Jump 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– ↳ FPWRQMake 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– ↳ FPWRTPOP BCC1Get the MSB of the single precision value from the STACK and put it in Register Pair BC
13F6POP DED1Get 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– ↳ FPWRFirst, 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)
13FALD A,B78Next, 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-13FCIf it is zero then we already know our ansder which is, mathematically, a 1 so JUMP to the EXP(n)routine at 1439H
13FD-13FFAfter 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
1400OR AB7Check to see if this is a ZERO raised to the minus power.
1401-1403If 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– ↳ POSEXPOR AB7ANOTHER 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-1407If 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.
1408PUSH DED5At 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
1409PUSH BCC5Save the exponent and the MSB of the single precision value in Register Pair BC to the STACK
140ALD A,C79Now 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-140COR 7FHF6 7FTurn the Z FLAG off by ORing against 7FH (0111 1111) in Register A
140D-140FLoad 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-1412If X is positive, then jump down to FPWR1 as we have nothing advanced to process
1413PUSH DED5Otherwise, 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
1414PUSH BCC5and then Save the exponent and the LSB of the single precision value in Register Pair BC to the STACK
1415-1417Check 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
1418POP BCC1Restore the exponent and the MSB of the Y value from the STACK and put it in Register Pair BC
1419POP DED1Restore the NMSB and the LSB of the Y value from the STACK and put it in Register Pair DE
141APUSH AFF5Save the LSB of the integer to the STACK for even and odd information
141B-141DMake 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 = BCDE A=00 If ACCumulator > BCDE A=01 If ACCumulator < BCDE A=FF
141EPOP HLE1Get 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.
141FLD A,H7CLoad Register A with the exponent as an integer (as stored in Register H)
1420RRA1FRotate 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– ↳ FPWR1POP HLE1Prepare to get the X back into the ACCumulator by fetching the number from the top of the STACK into Register Pair HL
1422-1424LD (4123H),HLLD (FAC-1),HL22 23 41Save the HIGH ORDERs of X to the ACCumulator
1425POP HLE1Get the LOW ORDERS of X from the STACK and put it in Register Pair HL
1426-1428LD (4121H),HLLD (FACLO),HL22 21 41Save 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-142BIf 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-142EIf the exponent is an integer and the base is negative, GOSUB to 0983H to invert the value of the exponent
142FPUSH DED5Save the NMSB and the LSB of the Y/exponent (i.e., the single precision value in Register Pair DE) to the STACK
1430PUSH BCC5Save the MSB of the Y/exponent (i.e., the single precision value in Register Pair BC) to the STACK
1431-1433Now 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)
1434POP BCC1Get the exponent and the MSB of the single precision value from the STACK and put it in Register Pair BC
1435POP DED1Get the NMSB and the LSB of the single precision from the STACK and put it in Register Pair DE
1436-1438We 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– ↳ EXPSave 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-143ELD BC,8138H01 38 81Next 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-1441LD DE,AA3BH11 3B AALoad 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-1444We 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-1447LD A,(4124H)LD A,(FAC)3A 24 41Load 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-1449CP 88HFE 88Next 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-144CIf 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-144FSo 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-1451ADD A,80HC6 80Adjust the value in Register A by masking it against 1000 0000
1452-1453ADD A,02HC6 02Adjust the value in Register A by adding 2 more. We will either get an overflow (C FLAG) or we wont (NC FLAG)
1454-1456If (exponent * 2 ln 2) is => 126 (meaning when 2 was added it it, it overflowed with a 128), jump to 0931H
1457PUSH AFF5So now neither has overflowed, so save the scale factor +82H (as stored in Register Pair AF) to the STACK
1458-145ALoad Register Pair HL with a single precision constant equal to 1.0 (as found at 1458H)
145B-145DGo 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-1460Need to multiply that by ln 2, so GOSUB to 0841H to multiply (1 + [EXP * 2 ln 2]) (as stored in ACCumulator) by 0.693147
1461POP AFF1Get the scale factor (as stored in the STACK) and put it in Register Pair AF
1462POP BCC1Get 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
1463POP DED1and then get the NMSB and the LSB of the single precision value from the STACK and put it in Register Pair DE
1464PUSH AFF5Put the scale factor (the integerized EXP * 2 ln 2) as stored in Register Pair AF onto the STACK
1465-1467Now 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-146ATo force that difference to be a positive number we GOSUB to 0982H which makes the current single precision value in ACCumulator positive
146B-146DLoad 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
1471-1473LD DE,0000H11 00 00We 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 …
1474POP BCC1… and then get the value from the STACK and put it in Register Pair BC
1475LD C,D4ALoad Register C with zero (since Register D was filled with a zero in 1471H)
1476-1478We 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– ↳ EXPCON09The number of single precision constants (9) which follow are stored here
147A-147D40 2E 94 74A single precision constant equal to -0.00014171607 (-1.413165 * 10e-4) is stored here
147E-148170 4F 2E 77A single precision constant equal to 0.00132988204 (1.32988 * 10e-3, roughly -1/6) is stored here
1482-14856E 02 88 7AA single precision constant equal to -0.00830136052 (-8.30136 * 10e-3, roughly -1/5) is stored here
1486-1489E7 A0 2A 7CA single precision constant equal to 0.04165735095 (roughly 1/4) is stored here
148A-148D50 AA AA 7EA single precision constant equal to -0.16666531543 (roughly -1/3) is stored here
148E-1491FF FF 7F 7FA single precision constant equal to 0.49999996981 (roughly 1/2) is stored here
1492-149500 00 80 81A single precision constant equal to -1.0 is stored here
1496-149900 00 00 81A 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– ↳ POLYXSave 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-149FLoad Register Pair DE with the return address of the FMULTT routine …
14A0PUSH DED5… and push it to the STACK, so that once this routine ends, it will be multiplied by X
14A1PUSH HLE5Save pointer to the constant (as stored in Register Pair HL) to the STACK
14A2-14A4We 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-14A7Since 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)
14A8POP HLE1Restore 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– ↳ POLYSave 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)
14ACLD A,(HL)7EFetch 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
14ADINC HL23Bump the value of the memory pointer in Register Pair HL so that it points to the first constant/coefficient
14AE-14B0Now 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)
14B1LD B,0F106 F1Z-80 Trick! If passing through, this will simply alter Register B and the next instruction of POP AF will not be processed.
14B2– ↳ POLY1POP AFF1Get the degree (count of coefficients left) from the STACK and put it in Register A
14B3POP BCC1Get the value of “X” from the STACK and put it in Register Pair BC/DE – Step 1 and …
14B4POP DED1… Step 2
14B5DEC A3DCount 1 of the terms as computed by decrementing the counter in Register A
14B6RET ZC8If that decrement results in a zero (meaning the series of computations has been completed) return out of the subroutine
14B7-14B8PUSH DE
PUSH BCD5Save the NMSB and the LSB of “X” from DE to the STACK and save the MSB of “X” from BC to the STACK
14B9PUSH AFF5Save counter of the remaining degrees (terms to compute) as tracked by Register A into the STACK
14BAPUSH HLE5Save the value of the memory pointer to the next constant/coefficient (stored in Register Pair HL) to the STACK
14BB-14BDCompute 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
14BEPOP HLE1Restore the coefficient table address (from the STACK) to Register Pair HL
14BF-14C1Get 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)
14C2PUSH HLE5Save the next coefficient (stored in Register Pair HL) to the STACK
14C3-14C5Compute 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)
14C6POP HLE1Restore the coefficient table address (from the STACK) to Register Pair HL
14C7-14C8Jump 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– ↳ RNDFirst 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)
14CCLD A,H7CLoad Register A with the value of the MSB for the integer value in Register H
14CDOR AB7Check to see if the integer value in Register Pair HL is negative
14CE-14D0Since we won’t accept a negative number, display a ?FC ERRORmessage if the integer value in Register Pair HL is negative
14D1OR LB5Combine 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-14D4If 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)
14D5PUSH HLE5Since it wasn’t zero, we need to save the argument (as stored in Register Pair HL) to the STACK
14D6-14D8Generate 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-14DBMove 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
14DCEX DE,HLEBSwap some registers so that the random number is now in B/C/H/L
14DDEX (SP),HLE3Swap (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
14DEPUSH BCC5Save the HIGH ORDER bytes of the random number value to the STACK
14DF-14E1Convert 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-14E3POP BC
POP DEC1Restore the RND(0) value from the STACK and put it into Register Pair BC/DE
14E4-14E6Multiply 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-14E9Load Register Pair HL with the starting address of a single precision constant equal to 1.0
14EA-14ECIncrease 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-14EFWith 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-14F2Load Register Pair HL with the starting address for a multiplier table used for figuring random numbers
14F3PUSH HLE5Save the starting address for the table used for figuring random numbers (stored in HL) to the STACK
14F4-14F6LD DE,0000H11 00 00Load Register Pair DE with zero (which will be the NMLSB and LSB of the starting value)
14F7LD C,E4BLoad Register C with zero (C will be the MSB of the starting value). Now C/D/E is zero.
14F8-14F9LD H,03H26 03Load Register H with the counter value for the multiplication loop (which will be 3)
14FA-14FB– ↳ RNDO0LD L,08H2E 08Load Register L with a counter value of 8 bits
14FC– ↳ RND1EX DE,HLEBSwap 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
14FDADD HL,HL29Multiply the NMSB and the LSB of the random number in Register Pair HL by two
14FEEX DE,HLEBExchange the newly doubled NMSB and the LSB of the random number to DE and the counters to Register Pair HL
14FFLD A,C79Next 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
1500RLA17Multiply the HIGH ORDER byte of the random number in Register A by two
1501LD C,A4FLoad Register C with the adjusted MSB of the random number in Register A
1502EX (SP),HLE3Swap (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
1503LD A,(HL)7EFetch a multiplier from the the table value (held at the location of the memory pointer in Register Pair HL) into Register A
1504RLCA07Rotate the bits of Register A
1505LD (HL),A77Save the doubled value (stored in Register A) at the location of the memory pointer in Register Pair HL
1506EX (SP),HLE3Swap (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
150APUSH HLE5Save the counter values in Register Pair HL to the STACK
150B-150DLD HL,(40AAH)LD HL,(RNDX)2A AA 40Load Register Pair HL with the NMSB and the LSB of the random number seed.
Note: 40AAH-40ADH holds the random number seed
150EADD HL,DE19Add 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
150FEX DE,HLEBLoad Register Pair DE with the adjusted NMSB and LSB of the random number in Register Pair HL
1510-1512LD A,(40ACH)LD A,(RNDX+2)3A AC 40Load Register A with the MSB of the random number seed
1513ADC A,C89Add the MSB of the random number in Register C to the MSB of the random number seed in Register A
1514LD C,A4FLoad Register C with the adjusted MSB of the random number in Register A
1515POP HLE1Get the counter values from the STACK and put it in Register Pair HL
1516– ↳ RND2DEC L2DDecrement the loop counter in Register L
151AEX (SP),HLE3Swap (SP) and HL so that HL will now point to the table of multipliers
151BINC HL23Bump to the next table of multipliers value
151CEX (SP),HLE3Exchange the bumped pointer to the table value in Register Pair HL with the counter value to the STACK
151DDEC H25Decrement the counter value of the outer loop (in Register H) to see if there are more bytes to deal with
151E-1520Loop back to 14FAH three times until the random number has been figured
1521POP HLE1Clear the flag table address from the STACK. The fact that it is going into HL is not important
1525ADD HL,DE19Add the seed (from Register Pair HL) to the NMSB and the LSB of the random number in Register Pair DE
1526-1528LD (40AAH),HLLD (RNDX),HL22 AA 40Save 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
152C-152DLD A,05H3E 05Load Register A with a 5
152EADC A,C89Add 5 (the value held in A) and the MSB of the random number in Register C
152F-1531LD (40ACH),ALD (RNDX+2),A32 AC 40Save the adjusted value in Register A as the MSB of the random number seed, so now the result is in A/H/L
1532EX DE,HLEBSwap DE and HL so that the result is now in A/D/E
1533-1534LD B,80H06 80Load Register B with a value for the sign flag and the exponent (i.e, 1000 0000)
1535-1537Load Register Pair HL with the address for the sign value storage location.
Note: 4125H-4126H is used by floating point routines
1538LD (HL),B70Save the sign result (1000 0000) in Register B at the location of the memory pointer in Register Pair HL
1539DEC HL2BDecrement to exponent (held in Register Pair HL)
153ALD (HL),B70Set the exponent to (1000 0000) so that the value will be < 1
153BLD C,A4FNow 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-153DLD B,00H06 00Zero the value of any overflow in Register B
153E-1540Jump 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– ↳ COSLoad Register Pair HL with the starting address of a single precision constant equal to 1.57079637029 (which is pi / 2)
1544-1546GOSUB 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:
- Assume X <= 360 degrees.
- Recompute x as x=x/360 so that x=< 1.
- If x <= 90 degrees go to step 7.
- If x <= 180 degrees then x=0.5-x and then go to step 7.
- If x <= 270 degrees then x=0.5-x.
- Recompute x as x=x-1.0.
- Compute SIN using the power series.
1547-1549H
“SIN”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-154CLD BC,8349H01 49 83Load Register Pair BC with the exponent and the MSB of a single precision constant
154D-154FLD DE,0FDBH11 DB 0FLoad 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-1552Move 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-1554POP BC
POP DEC1Put the x from a SIN(x) call into BC/DE
1555-1557To 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-155AMove 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-155DGo 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,155FPOP BC
POP DEC1Put the quotient and remainder of x/2*pi into BC/DE
1560-1562To 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-1564Load Register Pair HL with the starting address of a single precision constant equal to 0.25
1566-1568Next 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-156BGo check the sign of the result of that (.25 – fractional part) subtraction which is held in ACCumulator
156CSCF37Set the Carry flag
156D-156FJump to 1577H if the single precision value in ACCumulator is positive (meaning it is < than 90 degrees)
1570-1572If 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-1575Go check the sign for the single precision value in ACCumulator which basically checks to see if it is > 0.75 (meaning < 270 degrees)
1576OR AB7Test the value of the sign test in Register A
1577– ↳ SIN1PUSH AFF5Save the sign indicator (+ or -1) in Register Pair AF to the STACK
157B-157DLoad Register Pair HL with the starting address of a single precision constant equal to 0.25
157E-1580Add .25 (stored in HL) to the current value in ACCumulator by GOSUB to 070BH (result is saved in ACCumulator)
1581POP AFF1Get the sign reversal flag from the STACK and put it in Register Pair AF
1582-1584Set 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-1587Load Register Pair HL with 1593H (which is the starting address for a series of single precision values for a set of computations)
158B-158E – SINGLE PRECISION CONSTANT STORAGE LOCATION– “PI2”
158B-158EH– ↳ PI2DB 0F 49 81DB 0FA single precision constant equal to 1.57079637029 is stored here
158F-1592 – SINGLE PRECISION CONSTANT STORAGE LOCATION– “FR4”
158F-1592H– ↳ FR400 00 00 7FA single precision constant equal to 0.25 is stored here
1593-15A7 – SINGLE PRECISION CONSTANTS STORAGE LOCATION– “SINCON”
1593– ↳ SINCON05HThe 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-1597HBAH D7H 1EH 86HA single precision constant equal to 39.7106704708 is stored here
1598-159BH64H 26H 99H 87HA single precision constant equal to -76.5749816893 is stored here
159C-159FH58H 34H 23H 87HA single precision constant equal to 81.6022338865 is stored here
15A0-15A3HE0H 6DH A5H 86HA single precision constant equal to -41.3416748045 is stored here
15A4-15A7HDAH 0FH 49H 83HA 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– ↳ TANSave 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-15ADCall 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,15AFPOP BC
POP HLC1Get the original exponent and value from the STACK and put it in Register Pair BC/HL
15B0-15B2Call 09A4 which moves the SIN(x) single precision value (stored in REG) 1 to the STACK (stored in LSB/MSB/Exponent order)
15B3EX DE,HLEBLoad Register Pair DE with the NMSB and the LSB of the single precision value in Register Pair HL
15B4-15B6H Call 09B4H (which moves the original value in BC/DE into ACCumulator)
15B7-15B9Call 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-15BCJump 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:
- 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.
- 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.
- Evaluate the series: (((x^2*c0+c1) x^2+c2) . c8)x
- If the flag from step 1 is not set, then invert the sign of the series result.
- If the original value is < 1 then return to the caller. Otherwise, compute pi/2-value from step 4 and then return.
15BD-15BF– ↳ ATNGo check the sign of argument (i.e., the single precision value in the ACCumulator)
15C0-15C2If the single precision value in ACCumulator is negative then put a return address of 13E2H to the STACK
15C3-15C5Go convert the negative number in ACCumulator to positive if necessary
15C6-15C8LD A,(4124H)LD A,(FAC)3A 24 41We 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-15CACP 81HFE 81Check to see if the the exponent of the tangent (which is a single precision value in ACCumulator) is less than one
15CB-15CCJump to forward 15D9H if the carry flag is set (i.e., the single precision value in ACCumulator is less than one)
15CD-15CFLD BC,8100H01 00 81Load Register Pair BC with an exponent and a MSB for a single precision value
15D0LD D,C51Zero the NMSB of the single precision value in Register D
15D1LD E,C59Zero the LSB of the single precision value in Register E
15D2-15D4GOSUB 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-15D7Load Register Pair HL with a return address of 0710H (which is the subtract routine to be called once the series is calculated)
15D8PUSH HLE5Save the value of the return address in Register Pair HL to the STACK
15D9-15DB– ↳ ATN2Load Register Pair HL with the starting address for a series of single precision numbers for a set of computations
15DF-15E1Load Register Pair HL with the starting address of a single precision constant equal to 1.57079637029 (which is pi/2)
15E2RETC9Return. 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– ↳ ATNCON09The number of single precision constants (9) which follows is stored here
15E4-15E74A D7 3B 78A single precision constant equal to 0.00286622549 is stored here
15E8-15EB02 6E 84 7BA single precision constant equal to -0.01616573699 is stored here
15EC-15EFFE C1 2F 7CA single precision constant equal to 0.04290961441 is stored here
15F0-15F374 31 9A 3DA single precision constant equal to 0.07528963666 is stored here
15F4-15F784 3D 5A 7DA single precision constant equal to 0.10656264407 is stored here
15F8-15FBC8 7F 91 7EA single precision constant equal to -0.14208900905 is stored here
15FC-15FFE4 BB 4C 7EA single precision constant equal to 0.19993549561 is stored here
1600-16036C AA AA 7FA single precision constant equal to -0.33333146561 is stored here
1604-160700 00 00 01A 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.
ABS D9 0977 | AND D2 25FD
ASC F6 2A0F | ATN E4 15BD
AUTO B7 2008 | CDBL F1 0ADB
CHR$( F7 2A1F | CINT EF 0A7F
CLEAR B8 1E7A | CLOAD B9 2C1F
CLOSE A6 4185 | CLS 84 01C9
CMD 85 4173 | CONT B3 1DE4
COS El 1541 | CSAVE BA 2BF5
CSNG F0 0ABl | CVD E8 415E
CVI E6 4152 | CVS E7 4158
DATA 88 1F05 | DEF DD 415B
DEFDBL 9B 1E09 | DEFINT 99 1E03
DEFSNG 9A 1E06 | DEFSTR 98 1E00
DELETE B6 2BC6 | DIM 8A 2608
EDIT 9D 2E60 | ELSE 95 1F07
END 80 1DAE | EOF E9 4161
ERL C2 24DD | ERR C3 24CF
ERROR 9E 1FF4 | EXP E0 1439
FIELD A3 417C | FIX F2 0B26
FN BE 4155 | FOR 81 1CA1
FRE DA 27D4 | GET A4 4174
GOSUB 91 1EB1 | GOTO 5D 1EC2
IF 8F 2039 | INKEY$ C9 019D
INP DB 2AEF | INPUT 89 219A
INSTR C5 419D | INT D8 0B37
KILL AA 4191 | LEFT$ F8 2A61
LEN F3 2A03 | LET 8C 1F21
LINE 9C 41A3 | LIST B4 2B2E
LLIST B5 2B29 | LOAD A7 4188
LOC EA 4164 | LOF EB 4167
LOG DF 0809 | LPRINT AF 2067
LSET AB 4197 | MEM C8 27C9
MERGE A8 418B | MID$ FA 2A9A
MKD$ EE 4170 | NAME A9 418E
NEW BB 1B49H | NEXT 87 22B6
NOT CB 25C4 | ON A1 1FC6
OPEN A2 4179 | OR D3 25F7
OUT AO 2AFB | PEEK E5 2CAA
POINT C6 0132 | POKE B1 2CB1
POS DC 27F5 | PRINT B2 206F
PUT A5 4182 | RANDOM 86 01D3
READ 8B 21EF | REM 93 1F07
RESET 82 0138 | RESTORE 90 1D91
RESUME 9F 1FAFH | RETURN 92 1EDEH
RIGHT$ F9 2A91 | RND DE 14C9
RSET AC 419A | RUN 8E 1EA3
SAVE AD 41A0 | SET 83 0135
SGN D7 098A | SIN E2 1547
SQR CD 13E7 | STEP cc 2B01
STOP 94 1DA9 | STR$ F4 2836
STRING$ C4 2A2F | SYSTEM AE 02B2
TAB( BC 2137 | TAN E3 15A8
THEN CA | TIME$ C7 4176
TO BD | TROFF 97 1DF8
TRON 96 1DF8 | USING BF 2CBD
USR C1 27FE | VAL FF 2AC5
VARPTR C0 24EB | + CD 249F
– CE 2532 | CF
/ D0 | ? D1
> D4 | = D5
< D6 | & 26
‘ FB 3A93
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.
18F7SUB 00HD6 00Subtract the LSB
18F9LD L,A6FRestore the value to L
18FALD A,H7CGet the middle byte
18FBSBC A,00HDE 00Subtract the middle byte
18FDLD H,A67Move the difference to H
18FELD A,B78Get the MSB
18FFSBC A,00HDE 00Subtract the MSB
1901LD B,A47Move it back to A
1902LD A,00H3E 00Clear A
1904RETC9RETurn 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 45The word “ERROR”
1924-1928– ↳ INTX” in ” + 00HThe word ” IN “
1929-192F – MESSAGE STORAGE LOCATION– “REDDY”
1929-192F– ↳ REDDY“READY” + 0DH + 00HThe Level II BASIC READY message is stored here
1930-1935 – MESSAGE STORAGE LOCATION– “BRKTXT”
1930-1935– ↳ BNKTXT“Break” + 00HThe 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.
1939ADD HL,SP39Add 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– ↳ LOOPERLD A,(HL)7ELoad 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
193BINC HL23Bump the value of the memory pointer in Register Pair HL so as to backspace one more byte in case a FORtoken is located
193C-193DCP 81HFE 81Check 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
193ERET NZC0If 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
193FLD C,(HL)4EIf 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
1940INC HL23Bump the value of the memory pointer in Register Pair HL so as to backspace the current STACK pointer by yet another byte
1941LD B,(HL)46Load Register B with the MSB of the FOR‘s variable address
1942INC HL23Bump the value of the memory pointer in Register Pair HL so now HL will be the address of the FORvariable on the STACK
1943PUSH HLE5Save the value in Register Pair HL (which is the address of the FORvariable) to the STACK
1944LD L,C69Load Register L with the LSB of the FORvariable’s address in Register C
1945LD H,B60Load Register H with the MSB of the FORvariable’s address in Register B
1946-1947LD A,D
OR E7AZ-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
1948EX DE,HLEBExchange 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-194ASkip 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
194BEX DE,HLEBExchange 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
194CRST 18HCOMPARDFThis 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– ↳ POPGOFLoad Register Pair BC with the value to backspace (i.e., erase) the FORtoken (which is 10)
1950POP HLE1Get the memory pointer from the STACK of the sign of the increment flag and put it in Register Pair HL
1951RET ZC8If the variable in the FORblock matched the NEXTindex block, then RETURN with HL pointing to the bottom of the entry
1952ADD HL,BC09If it didn’t match, execute that 10 byte stepback in BC for the next possible FORpush
1953-1954At 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– ↳ BLTUGOSUB 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– ↳ BLTUCPUSH BCC5The 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
1959EX (SP),HLE3Save the end address of the list to move (stored in the STACK now) to Register Pair HL
195APOP BCC1Get the end address of the move from the STACK and put it in Register Pair BC
195B– ↳ BLTLOPRST 18HCOMPARDFCheck 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)
195CLD A,(HL)7EGet a byte from the source list to transfer and put it in Register A
195DLD (BC),A02Transfer it by saving the byte into wherever BC is pointing
195ERET ZC8Return 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)
195FDEC BC0BDecrement the source address (in Register Pair BC)
1960DEC HL2BDecrement the destination address (in Register Pair HL)
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– ↳ GETSTKPUSH HLE5Save the value in Register Pair HL to the STACK
1964-1966LD HL,(40FDH)LD HL,(STREND)2A FD 40Load Register Pair HL with the starting address of free memory (which is stored at 40FDH).
Note: 40FDH-40FEH holds Free memory pointer
1967-1968LD B,00H06 00Load Register B with zero
1969ADD HL,BC09Add 2 times the number of bytes required to start of free area (held in Register Pair BC) to the value in Register Pair HL
196AADD HL,BC09Add 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-196CLD A,0E5H3E E5Z-80 Trick! See the general explanation at 10F8H
196C– ↳ REASONPUSH HLE5Now 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-196ELD A,C6H
LD A,256-(2*NUMLEV)3E C6Load Register A with C6H (which is the the LSB of FFC6H; the top of memory)
196FSUB L95Subtract the LSB of the value of the new memory pointer in Register L from the value in Register A
1970LD L,A6FLoad Register L with the adjusted value in Register A (i.e., the free memory pointer resulting from subtracting the new starting address)
1971-1972LD A,FFH3E FFLoad Register A with the MSB of the top of memory
1973SBC A,H9CSubtract 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-1975If the CARRY flag is set then HL was simply too big. So we need to display a ?OM ERRORsince we are out of memory
1976LD H,A67Next 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
1977ADD HL,SP39Add the value of the STACK pointer to the adjusted value in Register Pair HL. If we are OK, the CARRY FLAG will be set
1978POP HLE1Restore the original HL on entry back into Register Pair HL
1979RET CD8If the carry flag is set, then we have no overflow – so RETURN to CALLer
197A-197B – ?OM ERROR ENTRY POINT– “OMERR”
197A-197B– ↳ OMERRLD E,0CHLD E,ERROM1E 0CLoad Register E with the ?OM ERROR
197E-1AF7 – LEVEL II BASIC COMMAND MODE ERROR HANDLING– “PRGEND”
197E-1980– ↳ PRGENDLD HL,(40A2H)LD HL,(CURLIN)2A A2 40Load Register Pair HL with the value of the current BASIC line number.
Note: 40A2H-40A3H holds the current BASIC line number
1981LD A,H7CTest 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
1982AND LA5Combine the LSB of the current BASIC line number in Register L with the MSB of the current line number in Register A
1983INC A3CBump 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-1985Jump to 198EH if Level II BASIC is still in the command mode (rather than being in execution mode)
1986-1988LD A,(40F2H)LD A,(ONEFLG)3A F2 40Load Register A with the error override flag (i.e., if there is an ON ERROR GOTO active)
1989OR AB7Check to see if the error flag is set
198A-198BLD E,22HLD E,ERRNR1E 22Load Register E with a ?NR ERRORcode
198C-198DJump to 19A2H if the error flag is set (meaning there was no RESUMEaddress)
198E-1990– ↳ ENDCNJOtherwise, jump to 1DC1H (to END) because there was an error in the input phase
1991-1993– ↳ DATSNELD HL,(40DAH)LD HL,(DATLIN)2A DA 40Load Register Pair HL with the DATAline number (which is stored at 16602).
Note: 40DAH-40DBH holds DATA line number
1994-1996LD (40A2H),HLLD (CURLIN),HL22 A2 40Make 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– ↳ SNERRLD E,02HLD E,ERRSN1E 02Load 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– ↳ DV0ERRLD BC,141EH01 1E 14Z-80 Trick. JUMPing here will load BC but skip the reload of Register E as follows
199A-199BLD E,14HLD E,ERRDV001 1E 14Load Register E with a ?/0 ERRORcode
199C-199E– ↳ NFERRLD BC,001EH01 1E 00Z-80 Trick. JUMPing here will load BC but skip the reload of Register E as follows
199D-199ELD E,00HLD E,ERRNF1E 00Load Register E with a ?NF ERRORcode.
?NF ERRORentry point
199F-19A1– ↳ REERRZ-80 Trick. JUMPing here will load BC but skip the reload of Register E as follows
19A0-19A1LD E,24HLD E,ERRRE1E 24Load Register E with a ?RW ERRORcode.
?RW ERRORentry point
19A2-19A4– ↳ ERRORLD HL,(40A2H)LD HL,(CURLIN)2A A2 40Load 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-19A7LD (40EAH),HLLD (ERRLIN),HL22 EA 40Save 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-19AALD (40ECH),HLLD (DOT),HL22 EC 40Save the value of the current BASIC line number with the error into the RAM location used for EDIT or LIST
19AB-19AD– ↳ ERRESMLoad 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– ↳ ERESETLD HL,(40E8H)LD HL,(SAVSTK)2A E8 40Load Register Pair HL with the value of the STACK pointer (which is stored at 40E8H).
Note: 40E8H-40E9H holds STACK pointer pointer
19B1-19B3Jump 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– ↳ ERRMORPOP BCC1Discard the entry at the top of the STACK (which is the FNDFOR Stopper)
19B5LD A,E7BLoad Register A with the value of the error code in Register E
19B6LD C,E4BLoad Register C with the value of the error code in Register E, as we will need to restore it later as well
19B7-19B9LD (409AH),ALD (ERRFLG),A32 9A 40Save the value of the error code (from in Register A) into 409AH.
Note: 409AH holds the RESUME flag
19BA-19BCLD HL,(40E6H)LD (SAVTXT),A2A E6 40Load 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-19BFLD (40EEH),HLLD (ERRTXT),HL22 EE 40Save the value of the current BASIC program pointer (which is stored in 40EEH) in Register Pair HL.
Note: 40EEH-40EFH is used by RESUME
19C0EX DE,HLEBLoad 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-19C3LD HL,(40EAH)LD HL,(ERRLIN)2A EA 40Load Register Pair HL with the line number where the error occurred.
Note: 40EAH-40EBH holds Line number with error
19C4,19C4LD A,H
AND L7CZ-80 Trick to test if HL is zero or not – If H AND L are 0, then they are each zero
19C6INC A3CBump 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-19C8If 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-19CBLD (40F5H),HLLD (OLDLIN),HL22 F5 40Let 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
19CCEX DE,HLEBGet bacK SAVTXT by swapping HL and DE
19CD-19CFLD (40F7H),HLLD (OLDTXT),HL22 F7 40Let 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– ↳ NTMDCNLD HL,(40F0H)LD HL,(ONELIN)2A F0 40See 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,19D4LD A,H
OR L7CZ-80 Trick to test if HL is zero or not – If H OR L are 0, then they are each zero
19D5EX DE,HLEBLoad Register Pair DE with the ON ERRORaddress to go to if there’s an error which is currently in Register Pair HL
19D6-19D8Load Register Pair HL with the address of the error flag (which is 40F2H).
Note: 40F2H holds Error flag
19D9-19DAJump to 19E3H (to error out) if we aren’t otherwise trapping errors (because there isn’t an ON ERRORaddress)
19DBAND (HL)A6Since 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-19DDIf the NZ FLAG is set, then it was already set, so JUMP to 19E3H (to error out)
19DEDEC (HL)35Force an error
19DFEX DE,HLEBLoad Register Pair HL with the ON ERRORline address pointer held in Register Pair DE
19E3 – LEVEL II BASIC COMMAND MODE ERROR HANDLING– “NOTRAP”
19E3– ↳ NOTRAPXOR AAFZero Register A
19E4LD (HL),A77Reset 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
19E5LD E,C59Restore the error code (which was tucked away in Register C at 19B6H) into Register E
19E6-19E8We need to position the video to the next line, so go display a carriage return on the video display if necessary
19E9-19EBLoad Register Pair HL with the starting address for the table of error messages
19EFLD D,A57Since we are non-DOS, we continue by loading Register D with zero
19F0-19F1LD A,3FHLD A,”?”3E 3FLoad Register A with a ?
19F5ADD HL,DE19Add the value of the error code in Register Pair DE to the starting address of the table of error messages in Register Pair HL
19F6LD A,(HL)7ELoad Register A with the first character of the error message at the location of the table pointer in Register Pair HL
19F7-19F9GOSUB to 032AH to display the first character of the error message in Register A
19FARST 10HCHRGETD7Error 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-19FDGOSUB to 032AH to display the second character of the error message in Register A
19FE-1A00Load Register Pair HL with 191DH which is the starting address of the word “ERROR” message
1A01PUSH HLE5Save the starting address of the word “ERROR” (held in HL) to the STACK
1A02-1A04LD HL,(40EAH)LD HL,(ERRLIN)2A EA 40Load Register Pair HL with the value of the BASIC line number causing the error.
Note: 40EAH-40EBH holds Line number with error
1A05EX (SP),HLE3Exchange 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– ↳ ERRFINGOSUB to 28A7 to display the entire word ERROR on screen
1A09POP HLE1Get the value of the BASIC line number with the error from the STACK and put it in Register Pair HL
1A0A-1A0CLoad Register Pair DE with FFFEH.
This basically reserves the line number 65534 as a trigger for the next few steps
1A0DRST 18HCOMPARDFNow 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-1A10Jump to 0674H if we are in the initialization routine because the error line number was FFFEH
1A11LD A,H7CNext, 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
1A12AND LA5Combine the LSB of the current BASIC line number in Register L with the MSB of the current BASIC line number in Register A
1A13INC A3CBump 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-1A16GOSUB 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.
1A17LD A,0C1H3E C1Z-80 Trick! If passing through from the above routine, then A will loaded and the instruction at 1A18 will be skipped.
1A18– ↳ STPRDYPOP BCC1Get the value from the STACK and put it in Register Pair BC
1A19-1A1BH
“READY”Go set the current output device to the video display.
Re-entry into BASIC command mode entry point. (see 6CCH also)
1A1C-1A1EH
1A25-1A27Load Register Pair HL with the starting address of word “READY”
1A2B-1A2DLD A,(409AH)LD A,(ERRFLG)3A 9A 40Load Register A with the value of the current error code
1A2E-1A2FSUB 02HD6 02Check 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-1A32If 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.
1A36-1A38LD (40A2H),HLLD (CURLIN),HL22 A2 40Set 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-1A3BLD A,(40E1H)LD A,(AUTFLG)3A E1 40Load Register A with the value of the AUTOflag. It will be zero if not in AUTO, and anything else if in AUTO 1A3COR AB7Check to see if in the AUTOmode 1A3F-1A41LD HL,(40E2H)LD HL,(AUTLIN)2A E2 40We are in AUTOmode so load Register Pair HL with the current AUTOline number.
Note: 40E2H-40E3H holds Current BASIC line number 1A42PUSH HLE5Save the current AUTOline number (stored in Register Pair HL) to the STACK 1A43-1A45Display 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 1A46POP DED1Get the current AUTOline number from the STACK and put it in Register Pair DE 1A47PUSH DED5Save the current AUTOline number in Register Pair DE to the STACK 1A48-1A4ASee 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-1A4CLD A,2AH3E 2ALoad Register A with a (which will be code for a matching line number) 1A4D-1A4EIf 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-1A50LD A,20HLD A,” “3E 20If 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– ↳ AUTELNGo display the character in Register A on the video display (which will be a “*” if a matching line number was found) 1A57POP DED1Get the current line number from the STACK and put it in Register Pair DE 1A5A– ↳ AUTRESXOR AAFThe BREAKkey was pressed so we need to zero Register A to clear the AUTO increment flag 1A5B-1A5DLD (40E1H),ALD (AUTFLG),A32 E1 40Save the value in Register A as the current AUTO flag (to turn off AUTO)
1A60H – Part of the AUTO command– “AUTGOD”
1A60-1A62– ↳ AUTGODLD HL,(40E4H)LD HL,(AUTINC)2A E4 40Load Register Pair HL with the value of the AUTOincrement.
Note: 40E4H-40E5H holds AUTO increment 1A63ADD HL,DE19Since 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-1A65If 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 1A66PUSH DED5Save the current AUTOline number in Register Pair DE to the STACK 1A67-1A69LD DE,FFF9H11 F9 FFLoad 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) 1A6ARST 18HCOMPARDFNow 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) 1A6BPOP DED1Get the current AUTOline number from the STACK and put it in Register Pair DE 1A6C-1A6DIf 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-1A70LD (40E2H),HLLD (AUTLIN),HL22 E2 40Save the adjusted AUTOline number in Register Pair HL as the current AUTOline number.
Note: 40E2H-40E3H holds Current BASIC line number 1A71-1A72OR 0FFHF6 FFSet all non-zero condition codes by ORing Register A with FFH
1A76H – Part of the AUTO command– “NTAUTO”
1A76-1A77– ↳ NTAUTOLD A,3EHLD A,”>”3E 3ELoad Register A with a >symbol (i.e., the prompt that follows READY) 1A78-1A7AGo display the Level II BASIC prompt in Register A on the video display 1A7B-1A7DGOSUB to 0361H to accept input. HL will hold the buffer address for that input 1A81RST 10HCHRGETD7Since 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. 1A82INC A3CBump the value of the character in Register A. This sets the status flags but saves the carry flag 1A83DEC A3DDecrement the value of the character in Register A so we can test for an end of statement 1A84-1A86Jump to 1A33H if we have an end of statement or a blank statement 1A87PUSH AFF5Save the value in Register Pair AF to the STACK (including the carry flag) 1A88-1A8ACall 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– ↳ BAKSPDEC HL2BTop of a loop. Decrement the value of the input buffer pointer in Register Pair HL so that it will point to the previous character 1A8CLD A,(HL)7EFetch the character at the location of the input buffer pointer in Register Pair HL 1A8D-1A8ECP 20HCP ” “FE 20Check to see if the character at the location of the input buffer pointer in Register A is a SPACE 1A8F-1A90Loop back to 1A8BH if the character at the location of the input buffer pointer in Register A is a space 1A91INC HL23Now 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 1A92LD A,(HL)7ELoad Register A with that character 1A93-1A94CP 20HCP ” “FE 20Check to see if the character at the location of the input buffer pointer in Register A is a space 1A95-1A97If 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– ↳ EDENTPUSH DED5Save 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-1A9BTokenize the input via a GOSUB to 1BC0H. BC will equal the length of the encoded statement when its done 1A9CPOP DED1Restore the BASIC line number (which is an integer) from the STACK 1A9DPOP AFF1Get 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-1AA0LD (40E6H),HLLD (SAVTXT),HL22 E6 40Save 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 1AA4-1AA6If NC is set then this was a direct statement, so JUMP to 1D5AH as there wasn’t a line number with the input 1AA7PUSH DED5Save the BASIC line number (as an integer stored in Register Pair DE) to the STACK 1AA8PUSH BCC5Save the length (i.e., character count) of the tokenized input (stored in Register Pair BC) to the STACK 1AA9XOR AAFZero Register A 1AAA-1AACLD (40DDH),ALD (BFKLFL),A32 DD 40Save the value in Register A (which is a 00H) as the input flag.
Note: 40DDH holds the BUFFER KILLED flag 1AADRST 10HCHRGETD7Since 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. 1AAEOR AB7Set the flags 1AAFPUSH AFF5Save the status flag to the STACK 1AB0EX DE,HLEBLoad Register Pair HL with the integer value of the BASIC line number (held in Register Pair DE) 1AB1-1AB3LD (40ECH),HLLD (DOT),HL22 EC 40Save the integer value of the line number (held in Register Pair HL) to 40ECH.
Note: 40ECH-40EDH holds EDIT line number 1AB4EX DE,HLEBExchange 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-1AB7Call 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– ↳ LEXISTPUSH BCC5Save the address of the line number in the BASIC program (if it exists) in Register Pair BC to the STACK 1AB9-1ABBDelete 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– ↳ NODELPOP DED1REstore the pointer to the place to insert the line from the STACK into Register Pair DE 1ABDPOP AFF1Restore the status from the 1AADH token scan into Register Pair AF to see if the line had anything on it 1ABEPUSH DED5Save the integer address of the BASIC line number where we need to start fixing links to the STACK 1ABF-1AC0Jump 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 1AC1POP DED1Clear the STACK from the start of the fix links 1AC2-1AC4LD HL,(40F9H)LD HL,(VARTAB)2A F9 40Load Register Pair HL with the end of the BASIC program pointer.
Note: 40F9H-40FAH holds the starting address of the simple variable storage area 1AC5EX (SP),HLE3Exchange the length of the tokenized input to the STACK with the end of the BASIC program pointer (VARTAB) in Register Pair HL 1AC6POP BCC1Get the end of the BASIC program pointer (VARTAB) from the STACK and put it in Register Pair BC 1AC7ADD HL,BC09Add 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 1AC8PUSH HLE5Save the adjusted end of the BASIC program pointer (VARTAB) in Register Pair HL to the STACK 1AC9-1ACBGo check to see if there is enough room in memory for the new BASIC line by GOSUB to 1955H 1ACCPOP HLE1Get the new end of the BASIC program pointer from the STACK and put it in Register Pair HL 1ACD-1ACFLD (40F9H),HLLD (VARTAB),HL22 F9 40Save 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 1AD0EX DE,HLEBLoad Register Pair HL with the address of the BASIC line 1AD1LD (HL),H74Save the MSB of the address of the BASIC line in Register H 1AD2POP DED1Get the value of the BASIC line number from the STACK and put it in Register Pair DE 1AD3PUSH HLE5Save the value of the memory pointer in Register Pair HL to the STACK. This will be the place to start to fix links 1AD4INC HL23Bump 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 1AD5INC HL23Bump the value of the memory pointer in Register Pair HL (which bumps it to the MSB of the line number entry) 1AD6LD (HL),E73Save the LSB of the BASIC line number in Register E at the location of the memory pointer in Register Pair HL 1AD7INC HL23Bump the value of the line number memory pointer in Register Pair HL 1AD8LD (HL),D72Save 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 1AD9INC HL23Bump the value of the BASIC line number in Register Pair HL 1ADAEX DE,HLEBLoad Register Pair DE with first data byte address following the line number (held in Register Pair HL) 1ADB-1ADDLD HL,(40A7H)LD HL,(BUFPNT)2A A7 40Load Register Pair HL with the value of the tokenized input pointer
Note: 40A7H-40A8H holds Input Buffer pointer 1ADEEX DE,HLEBExchange the value of the memory pointer in Register Pair DE with the value of the tokenized input pointer in Register Pair HL 1ADFDEC DE1BDecrement the value of the tokenized input buffer pointer in Register Pair DE 1AE0DEC DE1BDecrement the value of the tokenized input buffer pointer in Register Pair DE 1AE1– ↳ MLOOPRLD A,(DE)1ATop 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 1AE2LD (HL),A77Save the value in Register A at the location of the memory pointer in Register Pair HL 1AE3INC HL23Bump the store address (held in Register Pair HL) 1AE4INC DE13Bump the fetch address (held in Register Pair DE) 1AE5OR AB7Check to see if the character in Register A is an end of the line character 1AE6-1AE7Since 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– ↳ FINIPOP DED1Get 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-1AEBGo fix/update line pointers for all lines following the new line 1AEF-1AF1Do a clear, set up the STACK, and update all the BASIC line pointers
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– ↳ LINKERLD HL,(40A4H)LD HL,(TXTTAB)2A A4 40Load 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)
1AFBEX DE,HLEBMove 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– ↳ CHEADLD H,D
LD L,E62LET HL = DE
1AFELD A,(HL)7ELoad 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
1AFFINC HL23Bump the value of the memory pointer in Register Pair HL
1B00OR (HL)B6Since 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
1B01RET ZC8Return if done
If we are here, we did not get a 00 end of program, so we continue
1B02INC HL23We 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
1B03INC HL23Bump the value of the memory pointer in Register Pair HL
1B04INC HL23Bump the value of the memory pointer in Register Pair HL
1B05XOR AAFZero Register A and clear the flags
1B06– ↳ CZLOOPCP (HL)BETop 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
1B07INC HL23Bump the value of the memory pointer in Register Pair HL
1B0AEX DE,HLEBExchange 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
1B0BLD (HL),E73Save the LSB of the next BASIC line’s starting address in Register E at the location of the memory pointer in Register Pair HL
1B0CINC HL23Bump the value of the memory pointer in Register Pair HL
1B0DLD (HL),D72Save the MSB of the next BASIC line’s starting address in Register D at the location of the memory pointer in Register Pair HL
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– ↳ SCNLINLD DE,0000H11 00 00Load Register Pair DE (which will be the list found value) with zero
1B13PUSH DED5Save the initial assumption of start-of-list to the STACK
1B14-1B15If 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
1B16POP DED1Get the line number from the STACK and put it in Register Pair DE
1B17-1B19Go 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
1B1APUSH DED5Save the first line number’s value (stored in Register Pair DE) to the STACK
1B1B-1B1CJump if there isn’t a second line number to be evaluated given (i.e, LIST 3000-)
1B1D-1B1ERST 08H CEHCF CEIf 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– ↳ ALLLSTLD DE,FFFAH11 FA FFLoad 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-1B24Go 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-1B27Go 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– ↳ SNGLINEX DE,HLEBSet 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
1B29POP DED1Get the value of the FIRST line number from the STACK and put it in Register Pair DE
1B2A– ↳ FNDLN1EX (SP),HLE3Exchange the return address to the STACK with the value of the second line number in Register Pair HL
1B2BPUSH HLE5Save 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– ↳ FNDLINLD HL,(40A4H)LD HL,(TXTTAB)2A A4 40Load 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– ↳ LOOPLD B,H
LD C,L44LET BC = HL so that BC will also hold the address of the current line in the PST
1B31LD A,(HL)7ELoad Register A with the first byte of the word pointer (held in Register Pair HL)
1B32INC HL23Bump the value of the memory pointer in Register Pair HL to the MSB of the address of the next line
1B33OR (HL)B6Combine 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
1B34DEC HL2BRestore HL to the start of the current line
1B35RET ZC8Return if this is the end of PST (=the end of the BASIC program)
1B36INC HL23It’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
1B37INC HL23Bump the value of the memory pointer in Register Pair HL
1B38LD A,(HL)7ELoad Register A with the LSB of the current BASIC line number at the location of the memory pointer in Register Pair HL
1B39INC HL23Bump the value of the memory pointer in Register Pair HL to point to the MSB of the current BASIC line number
1B3ALD H,(HL)66Load Register H with the MSB of the current BASIC line number at the location of the memory pointer in Register Pair HL
1B3BLD L,A6FLoad Register L with the LSB of the current BASIC line number in Register A. HL should now hold the line number in the PST
1B3CRST 18HCOMPARDFNow 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)
1B3DLD H,B60Next we need to set HL to the start of the line before the current one, and that is held in BC
1B3ELD L,C69Load Register L with the LSB of the memory pointer in Register C
1B3FLD A,(HL)7ELoad Register A with the value at the location of the memory pointer in Register Pair HL
1B40INC HL23Bump the value of the memory pointer in Register Pair HL
1B41LD H,(HL)66Load Register H with the MSB of the next BASIC line pointer at the location of the memory pointer in Register Pair HL
1B42LD L,A6FLoad Register L with the LSB of the next BASIC line pointer in Register A to form the address of the next line in HL
1B43CCF3FComplement 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.
1B44RET ZC8Return 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
1B45CCF3FWe have no match, so complement (reverse) the Carry flag and exit
1B46RET NCD0if 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-1B48Loop until the location of the line number has been found in the BASIC program
1B49-1B5C – LEVEL II BASIC NEWROUTINE– “SCRATH”
1B49– ↳ SCRATHRET NZC0Go to the Level II BASIC error routine and display a ?SN ERRORmessage if there is any input following the NEWtoken
1B4A-1B4CCall the CLEAR SCREEN routine at 01C9 (which cleanrts the screen, changes to 64 characters, and homes the screen)
1B4D-1B4F– ↳ SCRTCHLD HL,(40A4H)LD HL,(TXTTAB)2A A4 40Load 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-1B52GOSUB to the TROFFroutine at 1DF8H (which just loads A with a zero for TROFF, and puts that 0 into 411BH)
1B53-1B55LD (40E1H),ALD (AUTFLG),A32 E1 40Reset the AUTOflag by putting a zero (which is in A due to the GOSUB to the TROFFstatement in the above instruction) into 40E1H
1B56LD (HL),A77We 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
1B57INC HL23. bump the value of the memory pointer in Register Pair HL
1B58LD (HL),A77. and save a zero at the location of the memory pointer in Register Pair HL
1B59INC HL23Bump the value of the memory pointer in Register Pair HL
1B5A-1B5CLD (40F9H),HLLD (VARTAB),HL22 F9 40Save 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.
1B60DEC HL2BDecrement 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– ↳ CLEARCLD (40DFH),HLLD (TEMP),HL22 DF 40Save the adjusted value in Register Pair HL into 40DFH.
Note: 40DFH-40E0H is used by DOS
1B64-1B65LD B,1AH06 1ALoad Register B with the number of variable names to be initialized (which is 26)
1B66-1B68Load Register Pair HL with the starting address of the variable declaration table (which is 4101H).
Note: 4101H-411AH holds Variable Declaration Table
1B69-1B6A– ↳ LOPDFTLD (HL),04H36 04Top of a DJNZ loop. Set the variable at the location of the memory pointer in Register Pair HL to a single precision variable
1B6BINC HL23Bump the value of the memory pointer in Register Pair HL to the next variable
1B6EXOR AAFZero Register A
1B6F-1B71LD (40F2H),ALD (ONEFLG),A32 F2 40Save 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
1B72LD L,A6FZero Register L
1B73LD H,A67Zero Register H
1B74-1B76LD (40F0H),HLLD (ONELIN),HL22 F0 40Save a zero (held in Register Pair HL) as the current ON ERRORaddress.
Note: 40F0H-40F1H is used by ON ERROR
1B77-1B79LD (40F7H),HLLD (OLDTXT),HL22 F7 40Save a zero (held in Register Pair HL) as the current BREAK, STOP, or ENDaddress.
Note: 40F7H-40F8H holds Last byte executed
1B7A-1B7CLD HL,(40B1H)LD HL,(MEMSIZ)2A B1 40Load Register Pair HL with the top of the memory pointer held in 40B1H.
Note: 40B1H-40B2H holds MEMORY SIZE? pointer
1B7D-1B7FLD (40D6H),HLLD (FRETOP),HL22 D6 40Save 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
1B83-1B85LD HL,(40F9H)LD HL,(VARTAB)2A F9 40Load 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-1B88LD (40FBH),HLLD (ARYTAB),HL22 FB 40Save 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-1B8BLD (40FDH),HLLD (STREND),HL22 FD 40Save the value in Register Pair HL as the new array variables pointer.
Note: 40FDH-40FEH holds Free memory pointer
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– ↳ STKINIPOP BCC1Get the return address from the STACK because we are about to change the STACK pointer
1B90-1B92LD HL,(40A0H)LD HL,(STKTOP)2A A0 40Load 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
1B93DEC HL2BDecrement the value of the memory pointer in Register Pair HL
1B94DEC HL2BDecrement 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-1B97LD (40E8H),HLLD (SAVSTK),HL22 E8 40Save the string space pointer – 2 (in Register Pair HL) as the STACK pointer
Note: 40E8H-40E9H holds STACK pointer pointer
1B98INC HL23Bump the value of the memory pointer in Register Pair HL
1B99INC HL23Bump the value of the memory pointer in Register Pair HL so it is back to being the start of the string space pointer
1B9A– ↳ STKERRLD SP,HLF9Initialize the STACK by loading the STACK pointer with the start of the string space pointer (held in Register Pair HL)
1B9B-1B9DLoad Register Pair HL with the start of the string work area (which is 40B5H).
Note: 40B5H-40D2H holds Temporary string work area
1B9E-1BA0LD (40B3H),HLLD (TEMPPT),HL22 B3 40Initialize 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-1BA3GOSUB 038BH to set the current output device to the video display
1BA7XOR AAFZero Register A
1BA8LD H,A67Zero Register H
1BA9LD L,A6FZero Register L
1BAA-1BAC– ↳ WASDIRLD (40DCH),ALD (SUBFLG),A32 DC 40Clear the FORstatement flag.
Note: 40DCH holds FOR flag
1BADPUSH HLE5Save the value in Register Pair HL to the STACK to deal with FOR and GOSUB
1BAEPUSH BCC5Save the value in Register Pair BC (which is the RETURN ADDRESS) back on the STACK
1BAF-1BB1– ↳ GTMPRTLD HL,(40DFH)LD HL,(TEMP)2A DF 40Restore Register Pair HL so it is preserved
1BB2RETC9RETurn 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– ↳ QINLINLD A,3FHLD A,”?”3E 3FLoad Register A with a ?
1BB8-1BB9LD A,20H3E 20Load Register A with a SPACE
1BBD-1BBFJump 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– ↳ CRUNCHXOR AAFZero Register A
*1BC1-1BC3 – Model 4 Gen 1
*1BC1-1BC3LD (40B0H),ALD (DORES),A32 B0 40Save 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-1BC3LD (409FH),A32 9F 40Put 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.
1BC4LD C,A4FZero Register C
1BC5EX DE,HLEBLoad Register Pair DE with the address of the first character after the line number (as stored in Register Pair HL)
1BC6-1BC8LD HL,(40A7H)LD HL,(BUFPNT)2A A7 40Load Register Pair HL with the starting address of the input buffer.
Note: 40A7H-40A8H holds Input Buffer pointer
1BC9DEC HL2BWe need to backspace twice so … decrement the value of the input buffer pointer in Register Pair HL
1BCADEC HL2B… and again decrement the value of the input buffer pointer in Register Pair HL
1BCBEX DE,HLEBExchange the string address – 2 from HL into DE, and the current input string address from DE into HL
1BCC– ↳ KLOOPLD A,(HL)7ELoad Register A with the character at the current location in the buffer (in Register Pair HL)
1BCD-1BCECP 20HFE 20Check to see if that character in Register A is a SPACEthat we would want to keep
1BCF-1BD1If that character is a SPACEwe would want to keep, Jump to 1C5BH to “STUFF” it into the destination line
1BD2LD B,A47Copy the current character into Register B
1BD3-1BD4CP 22HFE 22Check to see if the current character is a “
1BD5-1BD7If the current character is a “, Jump to 1C77H to will move the entire field between the quotes into the a code string
1BD8OR AB7Now 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-1BDBIf the current character is an END OF LINE then we are done, so jump to 1C7DH
*1BDC-1BDE – Model 4 Gen 1
*1BDC-1BDELD A,(40B0H)LD A,(DORES)3A B0 40Load 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-1BDELD A,(409FH)3A 9F 40Put 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.
1BDFOR AB7Check to see if a DATA statement is being processed
1BE0LD A,(HL)7ERe-fetch the current character (from the memory location of the input buffer pointer in Register Pair HL) and put it into Register A
1BE1-1BE3Jump to 1C5BH if a DATAstatement is being processed, as we don’t want to crunch that
1BE4-1BE5CP 3FHFE 3FCheck to see if the character in Register A is a ?(meaning a PRINTstatement)
1BE6-1BE7LD A,B2HLD A,$PRINT3E B2Load Register A with a PRINTtoken
1BE8-1BEAIf we have a ?then make believe it is a “PRINT” token, and jump to 1C5BH to STUFF it into the destination line
1BEBLD A,(HL)7ERe-fetch the current character (from the memory location of the input buffer pointer in Register Pair HL) and put it into Register A
1BEC-1BEDCP 30HFE 30Since 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-1BEFJump to 1BF5H if the character in Register A is less than a zero character, meaning it is not a digit or letter
1BF0-1BF1CP 3CHFE 3CNext, check to see if the character in Register A is less than < character (which is a test to see if it is 0–9, :, ;, <, constant or special character
1BF2-1BF4Jump to 1C5BH if the character in Register A is 0–9, :, ;, <, constant or special character
1BF5– ↳ MUSTCRPUSH DED5Save the value of the input buffer pointer in Register Pair DE to the STACK
1BF6-1BF8Load 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)
1BF9PUSH BCC5Save the character count (held in Register Pair BC) to the STACK
1BFA-1BFCLoad Register Pair BC with a return address after matching the reserved word list
1BFDPUSH BCC5Save the return address in Register Pair BC to the STACK
1BFE-1BFFLD B,7FH06 7FLoad Register B with a value to initialize the reserved words counter
1C00LD A,(HL)7ELoad Register A with the character at the location of the input buffer pointer in Register Pair HL
1C01-1C02CP 61HCP “A”+20HFE 61Check to see if the character in Register A is lower-case
1C05-1C06CP 7BHCP “Z”+21HFE 7BCheck to see if the character in Register A is within the lower-case range
1C07-1C08Jump down 2 instructions to 1C0CH if the character in Register A isn’t lowercase
1C09-1C0AAND 5FHAND 0101 1111E6 5FCovert the lowercase character in Register A to upper-case
1C0BLD (HL),A77Replace the current character in the input buffer (tracked by Register Pair HL) with the adjusted character (held in Register A)
1C0C– ↳ TRYAGALD C,(HL)4ELoad Register C with the character at the location of the input buffer pointer in Register Pair HL
1C0DEX DE,HLEBExchange 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– ↳ LOPSKPINC HL23Bump the value of the reserved words list pointer to the next reserved word and put that in Register Pair HL
1C0FOR (HL)B6We 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-1C12Jump 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.
1C13INC B04Bump the reserved words counter being tracked in Register B
1C14LD A,(HL)7ELoad Register A with the character at the location of the reserved words list pointer in Register Pair HL
1C15-1C16AND 7FHAND 0111 1111E6 7FReset the MSB (i.e., Bit 7 aka the Sign Bit) of the character in Register A
1C17RET ZC8If that set the Z FLAG then we are at the end of ther reserved word list, so, if so RETurn to CALLer
1C18CP CB9Check 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
1C1BEX DE,HLEBExchange 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
1C1CPUSH HLE5Save the value of the input buffer pointer to the STACK in case it turns out that this isn’t a match after all
1C1D– ↳ LOPPSIINC DE13Top 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
1C1ELD A,(DE)1ALoad Register A with the character at the location of the reserved words list pointer in Register Pair DE
1C1FOR AB7Check to see if Bit 7 of the character in Register A is set, meaning we just hit a different reserved word
1C20-1C22If 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.
1C23LD C,A4FLoad Register C with the current reserved word list character (held in Register A)
1C24LD A,B78Load 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-1C26CP 8DHCP $GOTOFE 8DCheck to see if the current reserved word being checked is GOTO
1C27-1C28Skip the next 2 instructions if the current reserved word being checked isn’t GOTO
1C29RST 10HCHRGETD7Since 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.
1C2ADEC HL2BDecrement the value of the input buffer pointer in Register Pair HL because the next instruction increments it
1C2B– ↳ NTGOTOINC HL23Bump the value of the input buffer pointer in Register Pair HL to point to the next character
1C2CLD A,(HL)7ELoad Register A with the next character at the location of the input buffer pointer in Register Pair HL
1C2D-1C2ECP 61HCP “A”+20HFE 61Check to see if the character in Register A is lower-case
1C2F-1C30If the character in Register A isn’t lowercase, skip the next instruction
1C31-1C32AND 5FHAND 0101 1111E6 5FAND the character in Register A against 0101 1111 to get rid of the lowercase bit and make it uppercase
1C33– ↳ NOTLW1CP CB9Check 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-1C35If that character is ALSO a match, loop back to 1C1DH to keep checking that reserved word
1C36POP HLE1If 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-1C38Jump back to 1C0CH to process the next reserved word against the character at the input buffer
1C39– ↳ FOUNDLD C,B48Load Register C with the value of the reserved words counter in Register B
1C3APOP AFF1Clear the old TXTPTR text pointer from the STACK (disposing of the HL push from 1C1CH)
1C3BEX DE,HLEBExchange the TXTPTR and RESPTR because the NOTRES routine starts with the same instruction flipping them back
1C3CRETC9RETurn 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– ↳ NOTRESEX DE,HLEBExchange the reserved words list pointer in Register Pair HL with the input buffer pointer in Register Pair DE
1C3ELD A,C79Load Register A with the value of the reserved words counter in Register C
1C3FPOP BCC1Restore the character count into Register Pair BC
1C40POP DED1Restore the “STUFF” pointer into Register Pair DE
1C41EX DE,HLEBExchange 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-1C43CP 95HFE 95Check to see if the token in Register A is an ELSEtoken
1C44-1C45LD (HL),3AHLD (HL),”:”36 3ASave a colon at the “STUFF” pointer location (held in Register Pair HL)
1C46-1C47If the token in Register A isn’t an ELSEtoken, skip the next two instructions
1C48INC C0CBump the value of the crunched character counter
1C49INC HL23Bump 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– ↳ CKSNGQCP 0FBHCP SNGQTKFE FBNext we check to see if the token in Register A is a ‘token
1C4E-1C4FLD (HL),3AHLD (HL),”:”36 3ASave a “:” at the “STUFF” pointer (held in Register Pair HL)
1C50INC HL23Bump the value of the “STUFF” pointer (held in Register Pair HL) because we just inserted a :
1C51-1C52LD B,93HLD B,$REM06 93Load Register B with a REMtoken
1C53LD (HL),B70Save the REMtoken in Register B at the location of the input buffer pointer in Register Pair HL
1C54INC HL23Bump the value of the “STUFF” pointer (held in Register Pair HL) because we just inserted a REMtoken
1C55EX DE,HLEBExchange DE and HL so that DE will now hold the “STUFF” pointer and HL will hold the input buffer pointer
1C56INC C0CBump the character counter in Register C
1C57INC C0CBump the character counter in Register C
1C58-1C59Jump 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– ↳ NTSNGTEX DE,HLEBExchange DE and HL so that DE will now hold the “STUFF” pointer
1C5B– ↳ STUFFHINC HL23Bump the value of the input buffer pointer in Register Pair HL
1C5CLD (DE),A12Save the value of the token in Register A at the location of the input buffer pointer in Register Pair DE
1C5DINC DE13Bump the value of the “STUFF” pointer (held in Register Pair DE)
1C5EINC C0CBump the value of the crunched character count
1C5F-1C60SUB 3AHD6 3ACheck to see if the character in Register A is a :to flag a multi-statement line
1C63-1C64CP 4EHCP $DATAFE 4ECheck to see if the token in Register A is a DATAtoken
1C65-1C66If the token in Register A isn’t a DATAtoken then skip the next instruction
*1C67-1C69 – Model 4 Gen 1
*1C67-1C69– ↳ COLISLD (40B0H),ALD (DORES),A32 B0 40Save 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-1C69LD (409FH),A32 9F 40Put 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– ↳ NODATTSUB 59HD6 59Check to see if the token in Register A is a REMtoken
1C6FLD B,A47Load Register B with a zero, since processing a REM doesn’t end with a :, only an END OF LINE (i.e., 0)
1C70– ↳ STR1LD A,(HL)7ETop of a loop. Load Register A with the character at the location of the input buffer pointer in Register Pair HL
1C71OR AB7Check to see if the character in Register A is an END OF LINE delimiter of 0
1C72-1C73Jump out of this loop to 1C7DH if the character in Register A is an END OF LINE delimeter
1C74CP BB8Check to see if the character in Register B matches the character in Register A
1C75-1C76If they match, then we are completely done with gobbling the whole rest of line without checking, so Jump to 1C5BH
1C77– ↳ STRNGINC HL23Bump the value of the input buffer pointer in Register Pair HL
1C78LD (DE),A12Save the character in Register A to the location of the “STUFF” pointer (in Register Pair DE)
1C79INC C0CBump the value of the crunched character count
1C7AINC DE13Bump the value of the “STUFF” pointer (held in Register Pair DE)
1C7D – Part of the tokeninzing routine – Jumped here when an EOL is found– “CRDONE”
1C7D-1C7F– ↳ CRDONELD HL,0005H21 05 00We are going to need to add 5 bytes to the tokenized character count
1C80LD B,H44Load Register B with zero
1C81ADD HL,BC09Add 5 to the length of the tokenized character count so far
1C82,1C83LD B,H
LD C,L44Let BC = HL
1C84-1C86LD HL,(40A7H)LD HL,(BUFPNT)2A A7 40Load Register Pair HL with the start address of the input buffer.
Note: 40A7H-40A8H holds Input Buffer pointer
1C87DEC HL2BDecrement the value of the input buffer pointer in Register Pair HL
1C88DEC HL2BDecrement the value of the input buffer pointer in Register Pair HL
1C89DEC HL2BDecrement the value of the input buffer pointer in Register Pair HL
1C8ALD (DE),A12We need THREE zeroes at the end, so . zero the location of the input buffer pointer in Register Pair DE to denote END OF LINE
1C8BINC DE13Bump the value of the input buffer pointer in Register Pair DE
1C8CLD (DE),A12Zero the next two locations of the input buffer pointer in Register Pair DE to denote no further link (since this was a direct statement)
1C8DINC DE13Bump the value of the input buffer pointer in Register Pair DE
1C8ELD (DE),A12Zero the location of the input buffer pointer in Register Pair DE
1C8FRETC9RETURN 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– ↳ DCOMPRLD A,H7CLoad Register A with the MSB of the value in Register H
1C91SUB D92Subtract the value of the MSB of the value in Register D from the MSB of the value in Register A
1C92RET NZC0Return if the MSB of the value in Register D doesn’t equal the MSB of the value in Register H
1C93LD A,L7DLoad Register A with the LSB of the value in Register L
1C94SUB E93Subtract the LSB of the value in Register E from the LSB of the value in Register A
1C95RETC9RETurn 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– ↳ SYNCHRLD A,(HL)7ELoad Register A with the character at the location of the current BASIC program pointer in Register Pair HL
1C97EX (SP),HLE3Save 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
1C98CP (HL)BECheck 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
1C99INC HL23Bump the value of the return address in Register Pair HL
1C9AEX (SP),HLE3Restore the return address (meaning the RST 08H plus 1 byte plus 1 byte) to the STACK pointer
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– ↳ FORLD A,64H3E 64Load Register A with the value for the FORflag
1CA3-1CA5LD (40DCH),ALD (SUBFLG),A32 DC 40Save the value in Register A as the current value of the FOR flag.
Note: 40DCH holds FORflag
1CA6-1CA8GOSUB 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”
1CA9EX (SP),HLE3Exchange the value of the current BASIC program pointer in Register Pair HL with the value to the STACK
1CAA-1CACGo check to see if there is a FORstatement to the STACK already using the same variable name (called the index)
1CADPOP DED1Get the current BASIC program pointer from the STACK (which should be the TOtoken and put it in Register Pair DE
1CAE-1CAFIf 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
1CB0ADD HL,BC09Add 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
1CB1LD SP,HLF9Remove 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-1CB4LD (40E8H),HLLD (SAVSTK),HL22 E8 40Save the value in Register Pair HL as the STACK pointer, get ready for a NF error.
Note: 40E8H-40E9H holds STACK pointer pointer
1CB5– ↳ NOTOLEX DE,HLEBExchange 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-1CB7LD C,08H0E 08Load Register C with the 1/2 the amount of space (i.e. 16 bytes) needed for a FOR entry
1CB8-1CBAGo check to see if there is enough memory (i.e., 16 bytes) left by calling 1963H (which is the MEMORY CHECK ROUTINE)
1CBBPUSH HLE5Save 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-1CBEKeep 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
1CBFEX (SP),HLE3Exchange 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
1CC0PUSH HLE5Save the value of the current BASIC program pointer in Register Pair HL to the STACK. This should be pointing to the TOtoken
1CC1-1CC3LD HL,(40A2H)LD HL,(CURLIN)2A A2 40Load Register Pair HL with the value (in binary) of the current BASIC line number.
Note: 40A2H-40A3H holds the current BASIC line number
1CC4EX (SP),HLE3Exchange 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-1CC6RST 08H BDHRST 08H TOCFSince 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)
1CC7RST 20HGETYPEE7We 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-1CCAIf that test shows we have a STRING, go to the Level II BASIC error routine and display a TM ERROR message
1CCB-1CCDIf that test shows we have a DOUBLE PRECISION number, go display a ?TM ERRORmessage
1CCEPUSH AFF5We 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-1CD1Go 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
1CD2POP AFF1Restore the index type flags from the from the STACK and put it in Register Pair AF
1CD3PUSH HLE5Save the value of the current BASIC program pointer (which is the code string after the TOpointer) in Register Pair HL to the STACK
1CD4-1CD6If the flag is positive, then we have a SINGLE PRECISION “FOR” loop
1CD7-1CD9Call 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)
1CDAEX (SP),HLE3Exchange 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
1CDELD A,(HL)7EFetch the next character from the current BASIC program line (pointer in Register Pair HL) into Register A
1CDF-1CE0CP 0CCHCP STEPTKFE CCCheck to see if the character in Register A is a STEPtoken
1CE1-1CE3So 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
1CE4PUSH DED5Save the STEPindex value (in Register Pair DE) to the STACK
1CE5PUSH HLE5Save the current BASIC program pointer (in Register Pair HL) to the STACK
1CE6EX DE,HLEBExchange 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-1CE9GOSUB to 099EH to get the sign of the STEPvalue into A. It will be A=+1 if positive and A=-1 if negative
1CEA-1CEBJump 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– ↳ SNGFORNeed 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-1CF1Call 09BF which loads the SINGLE PRECISION value in ACCumulator (the TOvalue in integer) into Register Pair BC/DE
1CF2POP HLE1Get 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,1CF4PUSH BC
PUSH DEC5Save all 4 bytes of the TOvalue to the STACK
1CF5-1CF7LD BC,8100H01 00 81Load Register Pair BC with the exponent and the MSB for a single precision constant
1CF8LD D,C51Zero the NMSB for the single precision constant in Register D
1CF9LD E,D5AZero the LSB for the single precision constant in Register E. Register Pairs BC and DE now hold a single precision constant equal to one
1CFALD A,(HL)7ELoad Register A with the character at the location of the current BASIC program pointer in Register Pair HL
1CFB-1CFCCP 0CCHFE CCCheck to see if the character in Register A is a STEPtoken
1CFD-1CFELD A,01H3E 01Load Register A with the default STEPvalue (in this case, 1)
1CFF-1D00Skip 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-1D03Go evaluate the expression at the location of the current BASIC program pointer (which is the STEPinstruction and return with the result in ACCumulator
1D04PUSH HLE5Save the value of the current BASIC program pointer in Register Pair HL to the STACK
1D05-1D07Convert 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-1D0ALoad the STEPvalue into BC/DE by calling 09BF which loads the SINGLE PRECISION value in ACCumulator into Register Pair BC/DE
1D0B-1D0DGo get the sign for the STEPincrement value (held in ACCumulator) into Register A. A will be +1 if positive, and -1 if negative
1D0E– ↳ STPSGNPOP HLE1Get the value of the current BASIC program pointer from the STACK and put it in Register Pair HL
1D0F– ↳ ONEONPUSH BCC5Save the exponent and the NMSB for the single precision value in Register Pair BC to the STACK
1D10PUSH DED5Save the NMSB and the LSB for the single precision value in Register Pair DE to the STACK
1D11LD C,A4FLoad Register C with the sign value for the STEPvalue in Register A
1D12RST 20HGETYPEE7We 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.
1D13LD B,A47Load Register B with type-adjusted and sign value of the number type flag test in Register A
1D14PUSH BCC5Save the type-adjusted and sign value in Register Pair BC to the STACK
1D15PUSH HLE5Save the value of the current BASIC program pointer in Register Pair HL to the STACK
1D16-1D18LD HL,(40DFH)LD HL,(TEMP)2A DF 40Load Register B with a FOR x=ytoken
1D19EX (SP),HLE3Put the pointer to the variable onto the STACK and restore the pointer to the BASIC program line being examined into HL
1D1A-1D1B– ↳ NXTCONLD B,81HLD B,$FOR06 81Load Register B with the FORtoken
1D1CPUSH BCC5Save the FORand token and the sign of the STEPincrement BC to the STACK
1D1DINC SP33Since 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.
1D21OR AB7Set the flags. If a key was hit, test for SHIFT+@
1D22-1D24GOSUB to 1DA0H if the key pressed was a SHIFT+@. This will save the address of the last byte executed in the current line
1D25-1D27LD (40E6H),HLLD (SAVTXT),HL22 E6 40Save 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-1D2BLD (40E8H),SPLD (SAVSTK),SPED 73 E8 40Save the value of the STACK pointer.
Note: 40E8H-40E9H holds STACK pointer pointer
1D2CLD A,(HL)7ELoad 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-1D2ECP 3AHFE 3ACheck to see if the character in Register A is a :
1D2F-1D30If the character is a :, jump to 1D5AH since that means this is a brand new statement on this line
1D31OR AB7There 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-1D34Go 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
1D35INC HL23So 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– ↳ GONE4LD A,(HL)7ELoad Register A with the LSB of the next BASIC line pointer at the location of the current BASIC program pointer in Register Pair HL
1D37INC HL23Bump the value of the current BASIC program pointer in Register Pair HL
1D38OR (HL)B6Check to see if the next BASIC line pointer is equal to zero
1D39-1D3BIf 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
1D3CINC HL23If 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
1D3DLD E,(HL)5ELoad Register E with the LSB of the BASIC line number at the location of the current BASIC program pointer in Register Pair HL
1D3EINC HL23Bump the value of the current BASIC program pointer in Register Pair HL
1D3FLD D,(HL)56Load Register D with the MSB of the BASIC line number at the location of the current BASIC program pointer in Register Pair HL
1D40EX DE,HLEBExchange 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-1D43LD (40A2H),HLLD (CURLIN),HL22 A2 40Save 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-1D46LD A,(411BH)LD A,(TRCFLG)3A 1B 41Before 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
1D47OR AB7Check for TRON. NZ means that TRACE is on
1D4APUSH DED5If 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-1D4CLD A,3CH3E 3CLoad Register A with a <
1D50-1D52Call 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-1D54LD A,3EH3E 3ELoad Register A with a >
1D58POP DED1Get 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– ↳ NOTTRCEX DE,HLEBLoad Register Pair HL with the value of the current BASIC program pointer in Register Pair DE
1D5A– ↳ GONERST 10HCHRGETD7We 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-1D5DLoad Register Pair DE with the return address to go to after executing one verb
1D5EPUSH DED5Save that return address in Register Pair DE to the STACK
1D5F– ↳ GONE3RET ZC8Return (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– ↳ GONE2SUB 80HSUB $ENDD6 80Check 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
1D65-1D66– ↳ NUMCMDCP 3CHFE 3CCheck to see if the token in Register A is below the TAB(token
1D67-1D69Jump out of here to 2AE7H if the token in Register A is greater than or equal to a TAB(token, meaning TAB(to MID$(
1D6ARLCA07Multiply the token value in Register A by two. This doubles the remainder of the routine address offset
1D6BLD C,A4FLoad the adjusted token value in Register C from Register A
1D6C-1D6DLD B,00H06 00Load Register B with zero so that BC now holds “00” and (2 x the token)
1D6EEX DE,HLEBLoad Register Pair DE with the value of the current BASIC program pointer in Register Pair HL
1D6F-1D71Load Register Pair HL with the list of BASIC execution addresses (called the Statement Dispatch Table in the original ROM source code)
1D72ADD HL,BC09Add 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
1D73LD C,(HL)4ELoad Register C with the LSB of the execution address at the location of the memory pointer in Register Pair HL
1D74INC HL23Bump the value of the memory pointer in Register Pair HL
1D75LD B,(HL)46Load Register B with the MSB of the execution address at the location of the memory pointer in Register Pair HL
1D76PUSH BCC5Save the value of the execution address in Register Pair BC to the STACK
1D77EX DE,HLEBLoad 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– ↳ CHRGTRINC HL23Bump the value of the current BASIC program pointer in Register Pair HL to the next character
1D79– ↳ CHRGT2LD A,(HL)7ELoad Register A with the value of the character at the location of the current BASIC program pointer in Register Pair HL
1D7A-1D7BCP 3AHFE 3ACheck to see if the character at the location of the current BASIC program pointer in Register A is greater than or equal to a :
1D7CRET NCD0Return 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– ↳ CHRCONCP 20HCP ” “FE 20Check to see if the character at the location of the current BASIC program pointer in Register A is a SPACE
1D7F-1D81Loop if the character at the location of the current BASIC program pointer in Register A is a SPACE
1D82-1D83CP 0BHFE 0BCheck 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-1D85Jump if the character at the location of the current BASIC program pointer in Register A if greater than or equal to 0BH
1D86-1D87CP 09HFE 09Check 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-1D8ALoop 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– ↳ NOTLFTCP 30HCP “0”FE 30Check 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
1D8DCCF3FSet the carry flag if that ASCII character is numeric (i.e., greater than or equal to 30H)
1D8EINC A3CClear the CARRY FLAG if it is not numeric (i.e., it is less than 30)
1D8FDEC A3DSet the status flags (except for the CARRY FLAG) according to the character at hand
1D90RETC9RETurn to CALLer
1D91-1D9A – LEVEL II BASIC RESTORE ROUTINE– “RESTORE”
1D91– ↳ RESTOREEX DE,HLEBSave the contents of HL by loading its contents into DE
1D92-1D94LD HL,(40A4H)LD HL,(TXTTAB)2A A4 40Load 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)
1D95DEC HL2BBackspace from the start of the BASIC program pointer, so that it is now TXTTAB – 1
1D96-1D98– ↳ RESFINLD (40FFH),HLLD (DATPTR),HL22 FF 40Save the start of the program pointer -1 into 40FFH.
Note: 40FFH-4100H holds READ pointer
1D99EX DE,HLEBRestore the poiner to the current character on the BASIC program line being examined back into Register Pair HL
1D9ARETC9RETurn to CALLer
1D9B-1DAD – SCAN KEYBOARD ROUTINE– “ISCNTC”
1D9EOR AB7Check to see if a key was pressed
1D9FRET ZC8Return if a key wasn’t pressed
1DA0-1DA1– ↳ CNTCCNCP 60HFE 60Check to see if the key pressed in Register A is a SHIFT+@
1DA2-1DA4If you got a SHIFT+@we now need to honor that by waiting for yet another key to be pressed
1DA5-1DA7LD (4099H),ALD (CHARC),A32 99 40Save the key pressed in Register A as the value of the last key pressed.
Note: 4099H holds the Last key pressed
1DA8DEC A3DCheck to see if the BREAKkey was pressed
1DA9-1DAD – STOPROUTINE– “STOP”
1DA9– ↳ STOPRET NZC0Return if the BREAKkey wasn’t pressed.
This is the STOPentry point
1DAAINC A3CReadjust the value of the key pressed in Register A
1DAE-1DE3 – LEVEL II BASIC ENDROUTINE– “END”
1DAE– ↳ ENDRET NZC0Return and display a ?SN ERRORmessage if there is anything following the ENDtoken -or- if we did not get a BREAK
1DAFPUSH AFF5Save all the condition codes and the value in Register Pair AF to the STACK
1DB3POP AFF1Restore the condition codes and Register A
1DB4-1DB6– ↳ CONSTPLD (40E6H),HLLD (SAVTXT),HL22 E6 40Save 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-1DB9We 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-1DBCLD (40B3H),HLLD (TEMPPT),HL22 B3 40Save 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-1DBFHLD HL,FFF6H21 F6 FFZ-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– ↳ STPENDOR FFHF6 FFForce A to be non-zero so as to force the printing of the BREAK message
1DC0POP BCC1Clears out the NEWSTT return address from the STACK, as we won’t be returning
1DC1-1DC3– ↳ ENDCONLD HL,(40A2H)LD HL,(CURLIN)2A A2 40Load Register Pair HL with the value of the current BASIC line number in binary.
Note: 40A2H-40A3H holds the current BASIC line number
1DC4PUSH HLE5Save the value of the current BASIC line number (in binary) in Register Pair HL to the STACK
1DC5PUSH AFF5Save 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
1DC6LD A,L7DThese 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
1DC7AND HA4Combine the MSB of the current BASIC line number in Register H with the LSB of the current BASIC line number in Register A
1DC8INC A3CBump the combined value of the current BASIC line number in Register A
1DC9-1DCAIncreasing 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-1DCDLD (40F5H),HLLD (OLDLIN),HL22 F5 40Save the line number we ended on in Register Pair HL.
Note: 40F5H-40F6H holds the last line number executed
1DCE-1DD0LD HL,(40E6H)LD HL,(SAVTXT)2A E6 40Load Register Pair HL with the value of the current BASIC program pointer .
Note: 40E6H-40E7H holds the temporary storage location
1DD1-1DD3LD (40F7H),HLLD (OLDTXT),HL22 F7 40Save the value of the current BASIC program pointer in Register Pair HL.
Note: 40F7H-40F8H holds Last byte executed
1DD4-1DD6– ↳ DIRISGo set the current output device to the video display
1DDAPOP AFF1Restore A so we can see if this is an END(where A=0) or a STOP(where A=1)
1DDB-1DDDLoad Register Pair HL with the starting address of the BREAKmessage
1DDE-1DE0JP NZ,1A06HJP NZ,ERRFINC2 06 1AJump to 1A06H if it was a BREAKor a STOPthat halted program execution
1DE1-1DE3At 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– ↳ CONTLD HL,(40F7H)LD HL,(OLDTXT)2A F7 40Load 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
1DE7LD A,H7CLoad Register A with the MSB of the continuation address in Register H
1DE8OR LB5Combine 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- ↳ RESERRLD E,20HLD E,ERRCN1E 20Load Register E with a ?CN ERRORcode
1DEB-1DEDGo to the ?CN ERRORroutine if CONTisn't possible (meaning the line number was zero)
1DEEEX DE,HLEBIf 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-1DF1LD HL,(40F5H)LD HL,(OLDLIN)2A F5 40Load HL with the value of the last BASIC line number executed.
Note: 40F5H-40F6H holds the last line number executed
1DF2-1DF4LD (40A2H),HLLD (CURLIN),HL22 A2 40Save 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
1DF5EX DE,HLEBSwap so that HL will be the address of the continuation line
1DF6RETC9Return (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- ↳ TONLD A,0AFH3E AFLoad 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- ↳ TOFFXOR AAFZ-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-1DFBLD (411BH),ALD (TRCFLG),A32 1B 41Save the value in Register A as the current value of the TRON/TROFF flag.
Note: 411BH holds TRON/TROFF flag
1DFCRETC9RETurn to CALLer
1DFD-1DFF - DISK ROUTINE NOT USED BY LEVEL II BASIC- "POPAHT".
1DFD- ↳ POPAHTPOP AF
1DFEE1POP HLRestore the pointer to the BASIC program line being parsed into Register Pair HL
1DFFRET
1E00-1E02 - DEFSTRENTRY POINT- "DEFSTR"
1E00-1E01- ↳ DEFSTR1E 03LD E,03HLoad Register E with the DEFSTRstring number type flag (03H)
1E02-1E04LD BC,021EH01 1E 02Z-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- ↳ DEFINTLD E,02H1E 02Load Register E with the DEFINTinteger number type flag (02H)
1E05LD BC,041EH01 1E 04Z-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- ↳ DEFREALD E,04H1E 04Load Register E with the DEFSNGsingle precision number type flag (04H)
1E08LD BC,081EH01 1E 08Z-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- ↳ DEFDBLLD E,08H1E 08Load 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- ↳ DEFCONA 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-1E10Load Register Pair BC with a return address which will return to the ?SN ERRORroutine
1E11PUSH BCC5Save the ?SN ERROR address (in Register Pair BC) to the STACK
1E12RET CD8Return 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-1E14SUB 41HD6 41Subtract 41H from the letter's value in Register A so that it will be in the range of 0-25
1E15LD C,A4FLoad Register C with the offset (i.e., adjusted value in Register A)
1E16LD B,A47Load Register B with the same, under the assumption that it will get updated at some point
1E17RST 10HCHRGETD7Since 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-1E19CP 0CEHCP MINUTKFE CECheck to see if the character at the location of the current BASIC program pointer in Register A is a -
1E1A-1E1BIf 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
1E1CRST 10HCHRGETD7If 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-1E1FA 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
1E20RET CD8Return with a ?SN ERROR if the character at the location of the current BASIC program pointer in Register A isn't alphabetic
1E21-1E22SUB 41HD6 41Subtract 41H from the letter's value in Register A so that it will be in the range of 0-25
1E23LD B,A47Overwrite Register B with the offset for the end of range letter variable
1E24RST 10HCHRGETD7Since 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- ↳ NOTRNGLD A,B78Load Register A with the value of the second letter in Register B
1E26SUB C91Subtract the value of the first letter in Register C from the value of the second letter in Register A
1E27RET CD8If 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
1E28INC A3CBump the value in Register A so that it holds the number of variable names to be changed
1E29EX (SP),HLE3Save the pointer to the next character to be parsed in the BASIC program to the STACK, and clear the error address
1E2A-1E2CLoad Register Pair HL with the starting address of the variable declaration table.
Note: 4101H-411AH holds Variable Declaration Table
1E2D-1E2ELD B,00H06 00Load Register B with zero so that the starting offset in Register C can be utilized in 16 bit math
1E2FADD HL,BC09Make 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- ↳ LPDCHGLD (HL),E73Top 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
1E31INC HL23Bump the value of the memory pointer in Register Pair HL
1E32DEC A3DDecrement 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-1E34Loop until all of the variables in the DEFxxxrange have been changed
1E35POP HLE1Restore the pointer to the BASIC program line being processed into Register Pair HL
1E36LD A,(HL)7EFetch the last character in the BASIC program line being processed into Register A
1E37-1E38CP 2CHFE 2CCheck to see if the character at the location of the current BASIC program pointer in Register A is a ,
1E39RET NZC0Return if the character at the location of the current BASIC program pointer in Register A isn't a ,
1E3ARST 10HCHRGETD7Since 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.
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- ↳ ISLETLD A,(HL)7ELoad Register A with the character at the location of the current BASIC program pointer in Register Pair HL
1E3E-1E3F- ↳ ISLET2CP 41HCP "A"FE 41Check to see if the character at the location of the current BASIC program pointer in Register A is less than an "A"
1E40RET CD8Return early if the character at the location of the current BASIC program pointer in Register A is less than an A
1E41-1E42CP 5BHCP "Z"+1FE 5BCheck to see if the character at the location of the current BASIC program pointer in Register A is greater than a "Z"
1E43CCF3FComplement 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
1E44RETC9RETurn 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- ↳ INTIDXRST 10HCHRGETD7Get 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- ↳ INTID2Go 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
1E49RET PF0Return 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- ↳ FCERRLD E,08H1E 08Load Register E with an ?FC ERRORcode
1E4C-1E4EDisplay 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- ↳ LINSPCLD A,(HL)7ELoad Register A with the character at the location of the current BASIC program pointer in Register Pair HL
1E50-1E51CP 2EHCP "."FE 2ECheck to see if the character at the location of the current BASIC program pointer in Register A is a .
1E52EX DE,HLEBLoad Register Pair DE with the value of the current BASIC program pointer in Register Pair HL
1E53-1E55LD HL,(40ECH)LD HL,(DOT)2A EC 40Load Register Pair HL with the current BASIC line number.
Note: 40ECH-40EDH holds current line number
1E56EX DE,HLEBExchange the value of the current BASIC program pointer in Register Pair DE with the current BASIC line number in Register Pair HL
1E57-1E59Jump 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- ↳ LINGETDEC HL2BBackspace HL (the current BASIC program pointer) to the current character
1E5B-1E5D- ↳ LINGT2LD DE,0000H11 00 00Since Register Pair DE will be the "accumulator" for the result, start it off at zero
1E5E- ↳ MORLINRST 10HCHRGETD7Top 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.
1E5FRET NCD0Return if the character at the location of the current BASIC program pointer in Register A isn't numeric
1E60PUSH HLE5Save the value of the current BASIC program pointer in Register Pair HL to the STACK
1E61PUSH AFF5Save the value in Register Pair AF (which is the digit plus flags resulting from the RST 10H call) to the STACK
1E62-1E64LD HL,1998H21 98 19Load 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
1E65RST 18HCOMPARDFNow 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-1E68Go 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
1E69LD H,D62Load 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
1E6ALD L,E6BLoad 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
1E6BADD HL,DE19Multiply the integer value in Register Pair HL by two
1E6CADD HL,HL29Add 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
1E6DADD HL,DE19Add 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
1E6EADD HL,HL29Add 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
1E6FPOP AFF1Put the last ASCII digit (from the STACK) into AF
1E70-1E71SUB 30HSUB "0"D6 30Convert the ASCII digit in Register A to binary
1E72LD E,A5FLoad Register E with the binary value of the character in Register A
1E73-1E74LD D,00H16 00Load Register D with zero so that DE will be 0000 through 0009 (the binary equivalent of the digit)
1E75ADD HL,DE19Add 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
1E76EX DE,HLEBSwap DE and HL. This will have the effect of setting DE to be 10(base 10) * DE + A
1E77POP HLE1Restore the pointer to the next digit (from the STACK) into HL
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- ↳ CLEARJump 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-1D7FGo evaluate the number of bytes to be reserved for string space and return with the integer result in Register Pair DE
1E80DEC HL2BDecrement the current BASIC program pointer in Register Pair HL
1E81RST 10HCHRGETD7Evaluate 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.
1E82RET NZC0Return 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
1E83PUSH HLE5Save the value of the current BASIC program pointer in Register Pair HL to the STACK
1E84-1E86LD HL,(40B1H)LD HL,(MEMSIZ)2A B1 40Load the top of memory pointer into Register Pair HL.
Note: 40B1H-40B2H holds MEMORY SIZE? pointer
1E87LD A,L7DLoad Register A with the LSB of the top of memory pointer in Register L
1E88SUB E93Subtract 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
1E89LD E,A5FLoad Register E with the LSB of the start of string space in Register A
1E8ALD A,H7CLoad Register A with the MSB of the top of memory pointer in Register H
1E8BSBC A,D9ASubtract 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
1E8CLD D,A57Load Register D with the MSB of the start of string space pointer in Register A
1E8D-1E8FIf 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-1E92LD HL,(40F9H)LD HL,(VARTAB)2A F9 40Load 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-1E95LD BC,0028H01 28 00Load Register Pair BC with the least amount of space needed for BASIC program variables just so that we have some breathing room
1E96ADD HL,BC09Add the value in Register Pair BC to the end of BASIC program pointer in Register Pair HL
1E97RST 18HCOMPARDFNow 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-1E9ADisplay 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
1E9BEX DE,HLEBPut the new start of the string area address into HL
1E9C-1E9ELD (40A0H),HLLD (STKTOP),HL22 A0 40Load the start of string space pointer with HL. 40A0H-40A1H holds the start of string space pointer
1E9FPOP HLE1Restore the code string pointer back into HL
1EA3-1EB0 - LEVEL II BASIC RUNROUTINE- "RUN"
On entry, if the Z flag is set, there was no parameter present
1EA3-1EA5- ↳ RUNJump to 1B5DH if there isn't a line number specified after the RUNtoken
1EA6-1EA9GOSUB 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-1EABClean up, reset the STACK, CDATPTR, and variables. Only HL is preserved
1EAC-1EAELoad Register Pair BC with the continuation address in the execution driver
1EAF-1EB0Put 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- ↳ GOSUBLD C,03H0E 03Load Register B with half the number of bytes needed for the GOSUBpush
1EB6POP BCC1Get the NEWSTT return address from the STACK and put it in Register Pair BC
1EB7PUSH HLE5Save the value of the current BASIC program pointer in Register Pair HL to the STACK
1EB8PUSH HLE5Create a hole to be filled in later by once again saving the value of the current BASIC program pointer to the STACK
1EB9-1EBBLD HL,(40A2H)LD HL,(CURLIN)2A A2 40Load Register Pair HL with the value of the current BASIC line number in binary.
Note: 40A2H-40A3H holds the current BASIC line number
1EBCEX (SP),HLE3Put the binary value for the current line number into the hole in the STACK AND restore the code string pointer
1EBD-1EBELD A,91HLD A,$GOSUB3E 91Load Register A with a GOSUBtoken
1EBFPUSH AFF5Save the GOSUBtoken (in Register Pair AF) to the STACK
1EC0INC SP33Bump the value of the STACK pointer since we just used one space for the GOSUB token
1EC1- ↳ RUNC2PUSH BCC5Save 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- ↳ GOTOWe 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- ↳ GOTO2Go bump the current BASIC program pointer in Register Pair HL until it points to the end of the current BASIC line
1EC8PUSH HLE5Save the value of the current BASIC program pointer in Register Pair HL to the STACK
1EC9-1ECBLD HL,(40A2H)LD (CURLIN),HL2A A2 40Load Register Pair HL with the binary equivalent of the last line number.
Note: 40A2H-40A3H holds the current BASIC line number
1ECCRST 18HCOMPARDFNow 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)
1ECDPOP HLE1Get the value of the current BASIC program pointer from the STACK and put it in Register Pair HL
1ECEINC HL23Bump 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-1ED1If 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-1ED4If 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,1ED6LD H,B
LD L,C60Let HL = BC (which is holding the address of the requested line number
1ED7DEC HL2BDecrement 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
1ED8RET CD8If 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- ↳ USERRLD E,0EHLD E,ERRUS1E 0ELoad Register E with an ?UL ERRORcode
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- ↳ RETURNRET NZC0Display a ?SN ERRORmessage if there is anything following the RETURNtoken
1EDF-1EE0LD D,0FFH16 FFLoad 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-1EE3Go 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
1EE4LD SP,HLF9Update the STACK by loading the STACK pointer with the new value in Register Pair HL
1EE5-1EE7HSLD (40E8H),HLLD (SAVSTK),HL22 E8 40Save the STACK pointer position in Register Pair HL.
Note: 40E8H-40E9H holds STACK pointer pointer
1EE8-1EE9CP 91HCP $GOSUBFE 91Check to see if the value in Register A is a GOSUBtoken
1EEA-1EEBLD E,04HLD E,ERRRG1E 04Load Register E with a ?RG ERRORcode
1EEC - RG ERROR entry point.
1EEC-1EEEDisplay a ?RG ERRORmessage if there isn't a GOSUBpush to the STACK
1EEFPOP HLE1Otherwise, get the line number that the GOSUBwas from from the STACK and put it in Register Pair HL
1EF0-1EF2LD (40A2H),HLLD (CURLIN),HL22 A2 40Save 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.
1EF3INC HL23Bump the value of the current BASIC line number in Register Pair HL
1EF4LD A,H7CLoad Register A with the MSB of the adjusted current BASIC 1ine number in Register Pair HL
1EF5OR LB5Combine 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-1EF7If the NZ FLAG is set, then this was NOT in direct command mode, so JUMP to 1EFF to do the RETURN
1EF8-1EFALD A,(40DDH)LD A,(BFKLFL)3A DD 40If we are here, we may have a one liner! Load Register A with the command mode flag.
Note: 40DDH holds INPUT flag
1EFBOR AB7Set the flags
1EFC-1EFEJump to 1A18H if Level II BASIC, instead of doing a RETURN, since we are in direct command mode
1EFF-1F01- ↳ GOBACKLoad Register Pair HL with the return address of NEWSTT
1F02EX (SP),HLE3Put 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-1F04LD A,0E1H3E E1Z-80 Trick! If passing through will just change Register A to this value, but NOT do a POP HL
1F04- ↳ DATAHPOP HLE1If 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- ↳ DATALD BC,0E3AH01 3A 0EThis 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- ↳ REMLD C,00H0E 00Load Register C with zero
1F09-1F0ALD B,00H06 00Load 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- ↳ EXCHQTLD A,C79Top of a loop. Load Register A with the stop scan character in Register C
1F0CLD C,B48Load Register C with the stop scan character in Register B
1F0DLD B,A47Load Register B with the stop scan character in Register A
1F0E- ↳ REMERLD A,(HL)7ETop of a loop. Load Register A with the character at the location of the current BASIC program pointer in Register Pair HL
1F0FOR AB7Check to see if the character at the location of the current BASIC program pointer in Register A is an END OF LINE terminator
1F10RET ZC8Return 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
1F11CP BB8Check 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
1F12RET ZC8Return 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
1F13INC HL23Bump the value of the current BASIC program pointer in Register Pair HL
1F14-1F15CP 22HCP """FE 22Check to see if the character at the location of the current BASIC program pointer in Register A is a quote
1F16-1F17Loop 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-1F19SUB 8FHSUB $IFD6 8FCheck to see if the character at the location of the current BASIC program pointer in Register A is a IFtoken
1F1A-1F1BLoop back to 1F0EH if the character at the location of the current BASIC program pointer in Register A isn't an IFtoken
1F1CCP BB8Since 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
1F1DADC A,D8AAs long as Regisister B is not zero, add the value in Register D to the value in Register A
1F1ELD D,A57Load Register D with the value in Register A
1F21-1F6B - LEVEL II BASIC LET ROUTINE- "LET"
1F21-1F23- ↳ LETCall 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-1F25RST 08H D5HRST 08H EQULTKCFTest 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)
1F26EX DE,HLEBExchange 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-1F29LD (40DFH),HLLD (TEMP),HL22 DF 40Save the addres of the variable in Register Pair HL.
Note: 40DFH-40E0H is a common temporary storage area
1F2AEX DE,HLEBExchange the value of the current BASIC program pointer in Register Pair DE with the address of the variable in Register Pair HL
1F2B- ↳ REDINPPUSH DED5Save the address of the variable in Register Pair DE to the STACK
1F2CRST 20HGETYPEE7We 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.
1F2DPUSH AFF5Save 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- ↳ LETCN3Go evaluate the expression at the location of the current BASIC program pointer in Register Pair HL and return with the result in ACCumulator
1F31- ↳ LETCONPOP AFF1Get the variable type of the variable into Register A
1F32- ↳ LETCN2EX (SP),HLE3Exchange (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- ↳ INPCOMADD A,03HC6 03Top 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-1F37Go to 2819H to convert the result in ACCumulator to the same number type for the variable held in Register A
1F38-1F3ASet Register Pair DE to equal the start position of good data in ACCumulator and call the GETYPE
1F3BPUSH HLE5Save the address of the variable in Register Pair HL to the STACK
1F3E-1F40LD HL,(4121H)LD HL,(FACLO)2A 21 41Load Register Pair HL with the starting address of the string's VARPTR in ACCumulator
1F41PUSH HLE5Save the VARPTR for the string result in Register Pair HL to the STACK
1F42INC HL23Bump the value of the VARPTR for the string result in Register Pair HL
1F43LD E,(HL)5ELoad Register E with the LSB of the address for the string at the location of the VARPTR for the string in Register Pair HL
1F44INC HL23Bump the value of the VARPTR for the string result in Register Pair HL
1F45LD D,(HL)56Load 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-1F48LD HL,(40A4H)LD HL,(TXTTAB)2A A4 40Load 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)
1F49RST 18HCOMPARDFNow 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-1F4BIf 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-1F4ELD HL,(40A0H)LD HL,(STKTOP)2A A0 40We 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
1F4FRST 18HCOMPARDFNow 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)
1F50POP DED1Get the VARPTR for the string result from the STACK and put it in Register Pair DE
1F51-1F52If 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-1F55LD HL,(40F9H)LD HL,(VARTAB)2A F9 40Load Register Pair HL with the simple variables pointer.
Note: 40F9H-40FAH holds the starting address of the simple variable storage area
1F56RST 18HCOMPARDFNow 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-1F58Jump 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-1F5AHLD A,D1H3E D1Z-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- ↳ INBUFCPOP DED1Get the VARPTR for the string result from the STACK and put it in Register Pair DE
1F5EEX DE,HLEBExchange 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-1F61Copy the variable in the string space OR strings with data in the buffer into string space
1F62-1F64- ↳ DNTCPYFree up the TEMP without freeing up any string space
1F65EX (SP),HLE3Exchange HL and (SP). HL will contain the place to store the VARPTR. (SP) will have nothing important
1F66-1F68- ↳ COPNUMCopy a descriptor or value to its proper location in memory
1F69POP DED1Get the top entry of the STACK. It is either garbage or information for a "FOR"
1F6APOP HLE1Get the value of the current BASIC program pointer from the STACK and put it in Register Pair HL
1F6BRETC9RETurn to CALLer
1F6C-1FAE - LEVEL II BASIC ERROR ONROUTINE- "ONGOTO"
1F6C-1F6D- ↳ ONGOTOCP 9EHCP $ERRORFE 9ECheck 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-1F6FJump down to 1F95H if the character at the location of the current BASIC program pointer in Register A isn't an ERRORtoken
1F70RST 10HCHRGETD7If 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-1F72RST 08H 8DHRST 08H $GOTOCFThe 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-1F75The 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
1F76LD A,D7AWe 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
1F77OR EB3Combine 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-1F79If we have an ON ERROR GOTO 0, we need to reset the error trap, so JUMP to 1F83H
1F7A-1F7CNext 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
1F7ELD D,B
LD E,C50Put the pointer to the line number (held in BC) into DE
1F7FPOP HLE1Restore the location of the current position in the BASIC program being processed from the top of the STACK into Register Pair HL
1F80-1F82If the NC FLAG is set, then the BASIC line number was not found, so we need to JUMP to display a ?UL ERROR
1F83- ↳ RESTRPEX DE,HLEBExchange 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-1F86LD (40F0H),HLLD (ONELIN),HL22 F0 40Save the pointer to the line number held in in Register Pair HL.
Note: 40F0H-40F1H is used by ON ERROR
1F87EX DE,HLEBExchange the value of the current BASIC program pointer in Register Pair DE with the location of the BASIC line in Register Pair HL
1F88RET CD8RETURN out of this routine to the execution driver if it was not ON ERROR GOTO 0000
1F89-1F8BLD A,(40F2H)LD A,(ONEFLG)3A F2 40Load 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
1F8COR AB7Check to see if the error flag is set
1F8DRET ZC8If 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-1F90LD A,(409AH)LD A,(ERRFLG)3A 9A 40Load Register A with the error code.
Note: 409AH holds the RESUME flag
1F91LD E,A5FLoad Register E with the value of the error code in Register A
1F92-1F94Force 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- ↳ NTOERRFirst 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
1F98LD A,(HL)7ELoad 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
1F99LD B,A47Save that character into Register B
1F9A-1F9BCP 91HCP $GOSUBFE 91Check to see if the character at the location of the current BASIC program pointer in Register A is a GOSUBtoken
1F9C-1F9DJR Z,1FA1HJR Z,ISGOSU28 03Skip the next 2 opcodes if the character at the location of current BASIC program pointer in Register A is a GOSUBtoken
1F9E-1F9FRST 08H 8DHRST 08H $GOTOCFNow 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)
1FA0DEC HL2BDecrement the value of the current BASIC program pointer in Register Pair HL to point to the GOTOtoken
1FA1- ↳ ISGOSULD C,E4BLoad Register C with the character count of the expression after the ONtoken in Register E (i.e., the "n" from ON n GOTO
1FA2- ↳ LOOPONDEC C0DTop of a loop. Decrement the line number counter in Register C to test to make sure there are enough skips
1FA3LD A,B78Load Register A with the token in Register B (which is either a GOSUBor GOTOtoken)
1FA7-1FA9We 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-1FABCP 2CHCP ","FE 2CCheck 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
1FACRET NZC0If 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
1FAF-1FF3 - LEVEL II BASIC RESUMEROUTINE- "RESUME"
1FAF-1FB1- ↳ RESUMELoad Register Pair DE with the address of the Level II BASIC error flag.
Note: 40F2H holds Error flag
1FB2LD A,(DE)1ALoad 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
1FB3OR AB7Check for an error
1FB4-1FB6If we processed a RESUME but yet there was no error, then display an ?RW ERRORmessage
1FB7INC A3CClear the error flag in Register A
1FB8-1FBALD (409AH),ALD (ERRFLG),A32 9A 40Clear the error flag (by putting a 0 into 409AH) so that way if a BREAKis hit, it won't give an error
1FBBLD (DE),A12Reset the error flag at the location of the memory pointer in Register Pair DE
1FBCLD A,(HL)7ELoad Register A with the character at the location of the current BASIC program pointer in Register Pair HL
1FBD-1FBECP 87HCP $NEXTFE 87Check to see if the character at the location of the current BASIC program pointer in Register A is a NEXTtoken
1FBF-1FC0Jump 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-1FC3Get 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
1FC4RET NZC0Exit to the execution driver if there wasn't a line number at the location of the current BASIC program pointer
1FC5
1FC6LD A,D7APrepare to test DE for Zero using the Z-80 trick of LD A,MSB and OR LSB
1FC7-1FC9If the line number was NOT zero (i.e., it was RESUME nnnn), then JUMP there!
1FCAINC A3CPrepare to set a non-Zero condition code so that it will indicate RESUME 0
1FCDH - Part of the RESUMEROUTINE- "RESNXT"
1FCD- ↳ RESNXTRST 10HCHRGETD7Since 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.
1FCERET NZC0Return if this is the end of the BASIC statement
1FCF - This is the RESUME 0routine- "RESTXT"
1FCF-1FD1- ↳ RESTXTLD HL,(40EEH)LD HL,(ERRTXT)2A EE 40Get the value of the current BASIC program pointer and put it in Register Pair HL.
Note: 40EEH-40EFH is used by RESUME
1FD2EX DE,HLEBLoad Register Pair DE with the value of the current BASIC program pointer in Register Pair HL
1FD3-1FD5LD HL,(40EAH)LD HL,(ERRLIN)2A EA 40Load Register Pair HL with the current BASIC line number.
Note: 40EAH-40EBH holds Line number with error
1FD6-1FD8LD (40A2H),HLLD (CURLIN),HL22 A2 40Save 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
1FD9EX DE,HLEBLoad Register Pair HL with the value of the current BASIC program pointer in Register Pair DE
1FDARET NZC0Return to the execution driver if this is RESUME
1FDBLD A,(HL)7EGet the character at the location of the current BASIC program pointer in Register Pair HL and put it in Register A
1FDCOR AB7Check 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-1FDEIf 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 ":"
1FDFINC HL23We 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
1FE0INC HL23Bump the value of the current BASIC program pointer in Register Pair HL to skip over the LSB of the pointer to the next statement
1FE1INC HL23Bump the value of the current BASIC program pointer in Register Pair HL to skip over the MSB of the pointer to the next statement
1FE2INC HL23Bump 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- ↳ NOTBGLINC HL23Bump 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
1FE4LD A,D7ALoad Register A with the MSB of the line number with the error in Register D
1FE5AND EA3Combine 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
1FE6INC A3CBump 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-1FE9Jump to NEWSTT and stop if this was part of a program (and not a direct command)
1FEA-1FECLD A,(40DDH)LD A,(BFKLFL)3A DD 40Load Register A with the buffer kill flag
1FEDDEC A3DCheck to see if the command mode flag in Register A is set
1FEE-1FF0Jump to 1DBEH if Level II BASIC is in the command mode because you cant CONTinue/RESUME if you are in command mode
1FF4H-2007 - LEVEL II BASIC ERROR ROUTINE - "ERRORS"
This evaluates n for ERROR n
1FF4-1FF6- ↳ ERRORSGet 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
1FF7RET NZC0Return if this isn't the end of the BASIC statement
1FF8OR AB7Set up to check to see if the error number in Register A is equal to zero
1FF9-1FFBIf the error code was zero, then that's an error in itself, so display an ?FC ERRORmessage
1FFCDEC A3DSubtract one from the error code in Register A
1FFDADD A,A87Multiply the error code in Register A by two (so now A = 2(A-1))
1FFELD E,A5FLoad Register E with 2(A-1)
1FFF-2000CP 2DHCP LSTERRFE 2DCheck to see if the error code in Register A is less than 45
2001-2002Jump 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- ↳ GOERRLD E,26HLD E,ERRUE1E 26Load Register E with the ERROR code for a ?UE ERROR
2005-2007Go 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.
200BPUSH DED5Save the default STACK line number in DE to the STACK
200C-200DJump 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-2010If 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
2011EX DE,HLEBSince 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
2012EX (SP),HLE3Next, 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-2014Jump down to 2026H if this is the end of the AUTOstatement (meaning that only 1 parameter was provided). This will default to 10
2015EX DE,HLEBLoad HL with the value of the current BASIC program pointer in DE
2016-2017RST 08H ⇒ 2CSYNCHK ,CF 2CIf 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).
2018EX DE,HLEBIf 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-201BLD HL,(40E4H)LD HL,(AUTINC)2A E4 40Load HL with the last provided AUTOincrement value.
Note: 40E4H-40E5H holds AUTOincrement
201CEX DE,HLEBExchange the value of the current BASIC program pointer in DE with the last AUTOincrement value in HL
201D-201EJump 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-2021If 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-2024If 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- ↳ SNGAUTEX DE,HLEBExchange the AUTOincrement number in DE with the value of the current BASIC program pointer in HL. HL should now hold the AUTO increment
2026- ↳ SNGAU1LD A,H7CPrepare 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)
2027OR LB5Combine 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-202AThat all tested the value of HL to see if it was zero. If it was, jupm to 1E4AH to show a ?FC ERRORmessage
202B-202DLD (40E4H),HLLD (AUTINC),HL22 E4 40Save the value of the AUTOincrement number in HL.
Note: 40E4H-40E5H holds AUTOincrement
202E-2030LD (40E1H),ALD (AUTFLG),A32 E1 40Set the AUTOflag to non-zero. We know A is non-zero because we would have jumped 2 instruction ago if it was
2031POP HLE1Get the starting line number from the STACK and put it in HL
2032-2034LD (40E2H),HLLD (AUTLIN),HL22 E2 40Save the line number in HL as the next AUTO line number.
Note: 40E2H-40E3H holds Current BASIC line number
2035POP BCC1Clean up the return address of NEWSTT from the STACK
2039-2066 - LEVEL II BASIC IFROUTINE- "IF"
2039-203B- ↳ IFGOSUB to 2337H to evaluate the BASIC expression pointed to by HL and return with the result in ACCumulator
203CLD A,(HL)7ELoad Register A with the character at the location of the current BASIC program pointer in HL
203D-203ECP 2CHFE 2CCheck 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-2041If 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-2043CP 0CAHFE CACheck 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-2046If 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
2047DEC HL2BDecrement the value of the current BASIC program pointer in HL so that we are still positioned at the THENtoken
2048- ↳ OKGOTOPUSH HLE5Save the value of the current BASIC program pointer to the STACK
2049-204BGOSUB to 0994H to see if the expression after the IFtoken was true or false
204CPOP HLE1Restore the address of the current position in the current statement (from the STACK) into HL
204F- ↳ DOCONDRST 10HCHRGETD7We 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-2052Jump to the GOTOroutine if the character at the location of the current BASIC program pointer in HL is numeric
2053-2055Jump to the execution driver at 1D5FH to evaluate the rest of the statement string
2056H - LEVEL II BASIC ELSEROUTINE- "FALSIF"
2056-2057- ↳ FALSIFLD D,01H16 01Load 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- ↳ SKPMRFGo 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
205BOR AB7Since 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
205CRET ZC8If this is the end of the BASIC statement then there isn't going to be an ELSE, so RETurn to CALLer
205DRST 10HCHRGETD7We 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-205FCP 95HCP $ELSEFE 95Check 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-2061If this wasn't an ELSE then we are still in the THEN clause, so loop back to 2058H until an ELSEtoken is found
2062DEC D15Decrement the number of ELSE statements that have been found
2063-2064If 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-2066If 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- ↳ LPRINTLD A,01H3E 01Load Register A with the output device code for the printer
2069-206BLD (409CH),ALD (PRTFLG),A32 9C 40Save the value in Register A as the current output device type number (-1=cassette, 0=video; or 1=printer)
206F-2177 - LEVEL II BASIC PRINT@ ROUTINE- "PRINT"
206F-2071- ↳ PRINTDo a DOS Vector call in case this is to go to a disk file
2072-2073CP '#'Check the character at the location of the current BASIC program pointer in register A to see if it's an #.
2079-207BLD (409CH),ALoad the memory location at 409CH with A, to route output to cassette.
207CDEC HLDecrement HL to back up.
207DRST 10HWe 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.
2084OR 20HWith this MASK, we are getting readh to check to see if it was a @.
2086CP 60HCompare against a "'" (which is actually a @on the TRS-80).
208AGOSUB 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).
208DCP 04Check 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.
2092PUSH HLPush the current code string address (held in HL) to the stack.
2096ADD HL,DEAdd DE to HL so that HL now will be the start of the diplay area PLUS the PRINT@offset position.
2097LD (4020H),HLStore 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.
209ALD A,EE is the current position within the line.
209BAND 3FHMake it not exceed 63 and then save it ....
209DLD (40A6H),A... into the current cursor offset (which is held in 40A6H).
20A0POP HLRestore the code string address to HL
20A2RST 08H
2CAt 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).
20A5LD A,(HL)
2CPut the next token (held in the memory location pointed to by HL) into A.
20A6-20A7CP 0BFHCompare 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.)
20AB-20ACCP BCHTest 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.
20B0PUSH HLContinuing on a PRINT#, save the current position in the input stream into the stack.
20B1-20B2CP 2CHTest 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.
20B5-20B6CP 3BHSince 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.
20B9Get the address or value of the next item to be printed by calling the formula evaluator at 2337H
20BCEX (SP),HLSwap (SP) for HL to restore the position.
20BDRST 20HGETYPEE7Check 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.
20C0GOSUB to the routine at 0FBDH to convert the number to an ASCII string and move to the print buffer
20C3GOSUB to the routine at 2865H to build a literal string pool entry for the ASCII number
20C9LD HL,(4121H)LD HL,(FACLO)2A 21 41Put the address of the pointer to the current print string into HL
20CCLD A,(409CH)LD A,(PRTFLG)3A 9C 40Get 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)
20CFOR AB7Since 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
20D5-20D7LD A,(409BH)LD A,(LPTPOS)3A 9B 40If we are here, we have a LPRINTso load A with the current carriage position.
Note: 409BH holds the printer carriage position
20D8- ↳ LPTCD2ADD A,(HL)86Add 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-20DACP 84HCP LPTLENFE 84Check 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.
20DDH - LEVEL II BASIC PRINT@ ROUTINE - Jumped here if we are sure we are using the display- "ISTTY"
20DD-20DF- ↳ ISTTYLD A,(409DH)LD A,(LINLEN)3A 9D 40Load Register A with the video line size.
Note: 409DH holds the size of line on the video display
20E0LD B,A47Load Register B with the video line size in Register A
20E1-20E3LD A,(40A6H)LD A,(TTYPOS)3A A6 40Load Register A with the current video line position.
Note: 40A6H holds the current cursor line position
20E4ADD A,(HL)86Add 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- ↳ LINPT3CP BB8Check 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- ↳ LINCHKIf NC is set, the new line will overflow the buffer so send a carriage return to the current output device
20E9-20EB- ↳ LINCH2Go send the string to the current output device
20EC-20EDLD A,20H3E 20Load Register A with a SPACE
20F1OR AB7Since 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- ↳ STRDONIf necessary go send the string to the current output device
20F5POP HLE1Restore the current code string to HL
20F6-20F8Loop 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- ↳ CRDONZLD A,(40A6H)LD A,(TTYPOS)3A A6 40Load Register A with the number of characters printed on the current line.
Note: 40A6H holds the current cursor line position
20FCOR AB7Since 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
20FDRET ZC8Return 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- ↳ CRDOLD A,0DH3E 0DOtherwise, we need to skip to the next line so load Register A with a carriage return
2100-2102Go send the carriage return in Register A to the current output device
2103-2105- ↳ CRFINGOSUB to the DOS routine at 41D0H to deal with screen wrap
2106XOR AAFZero Register A and the carry flags
2107RETC9Return back to the calling routine
2108 - This is the jump point for a continuation of the PRINT#code- "COMPRT".
2108-210A- ↳ COMPRTJump to DOS to see if DOS wants to modify the behavior
210B-210DLD A,(409CH)LD A,(PRTFLG)3A 9C 40Load 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)
210EOR AB7Test 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-2111Jump down a few instructions to 2119H if the printer or the video display is the current output device
2112-2113LD A,2CH3E 2CSo now that we know the current device is cassette, we will load Register A with a ,
2114-2116Send 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-2118Jump forward to 2164H to fetch the next character from the code string
2119-211A- ↳ NTCASWe 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- ↳ LPTCD3LD A,(409BH)LD A,(LPTPOS)3A 9B 40We'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- ↳ NLPPOSCP 70HCP NLPPOSFE 70Check 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.
2123-2125- ↳ ISCTTYLD A,(409EH)LD A,(CLMLST)3A 9E 40We 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
2126LD B,A47Load Register B with the video line length in Register A
2127-2129LD A,(40A6H)LD A,(TTYPOS)3A A6 40Load Register A with the current video line position.
Note: 40A6H holds the current cursor line position
212A- ↳ NCMPOSCP BB8Test 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- ↳ CHKCOMIf 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-212FIf we are beyond the last comma field, quit via a JUMP to 2164H
2130-2131- ↳ MORCOMSUB 10HSUB CLMWIDD6 10Calculate A MOD CLDMWID to see if there are at least 16 spaces left on the current line for the current output device
2132-2133Loop back until there are at least 16 spaces left on the current line
2134CPL2FFigure the number of spaces to be sent to the current output device so that we have an even CLMWID
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- ↳ TABERGOSUB 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
213AAND 7FHThe results are in A so mask the tab number in register A so that it doesn’t exceed 127.
213CLD E,A5FLoad Register E with the value of the tab number from Register A
213D-213ERST 08H ⇒ 29SNCHK ")"CF 29At 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).
213FDEC HL2BDecrement 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.
2140PUSH HLE5Save the value of the current BASIC program pointer in HL to the STACK
2144-2146LD A,(409CH)LD A,(PRTFLG)3A 9C 40Load 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)
2147OR AB7Test the value of the current output device flag in Register A. A NZ means line printer
2148-214ASince you cannot send a tab to the cassette, display a ?FC ERRORmessage if the current output device is the cassette recorder
214B-214DJump forward a few instructions to 2153H if the current output device is the video display
214E-2150LD A,(409BH)LD A,(LPTPOS)3A 9B 40Since 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
2153H - Displaying to Screen - "TTYIST"
2153-2155- ↳ TTYISTLD A,(40A6H)LD A,(TTYPOS)3A A6 40Load Register A with the current video line position.
Note: 40A6H holds the current cursor line position
2156- ↳ DOSIZTCPL2FWhen 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)
2157ADD A,E83Add the tab number in Register B to the adjusted line position in Register A so that A=-current position + tab
2158-2159If 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- ↳ ASPA2INC A3CBump the number of spaces to be printed in Register A
215B- ↳ ASPACLD B,A47Load Register B with the number of spaces to be printed in Register A
215C-215DLD A,20H3E 20Load Register A with a SPACE
215E-2160- ↳ REPOUTSend the space in Register A to the current output device
2161DEC B05Decrement the number of SPACE's to be displayed (counter in Register B)
2162-2163Loop back to 215EH until all of the spaces have been displayed/printed
2164- ↳ NOTABRPOP HLE1Restore the position in of the current BASIC program pointer (from the STACK) into HL
2164- ↳ NOTABRPOP HLE1Restore the position in of the current BASIC program pointer (from the STACK) into HL
2165RST 10HCHRGETD7We 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.
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- ↳ FINPRTLD A,(409CH)LD A,(PRTFLG)3A 9C 40If 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)
216COR AB7Test the value of the current output device flag in Register A. If the M flag is set, A was negative
216D-216FIf the current output device flag is the cassette recorder, GOSUB to 01F8H to turn it off
2170XOR AAFClear Register A (which will set A to 0) and clear the status flags
2171-2173LD (409CH),ALD (PRTFLG),A32 9C 40Save 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)
2177RETC9RETurn to CALLer
2178-217E - MESSAGE STORAGE LOCATION FOR REDO MESSAGE- - "TRYAGN"
2178-217E- ↳ TRYAGN"?REDO" + CRLF + 00HThe ?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- ↳ TRMNOKLD A,(40DEH)LD A,(FLGINP)3A DE 40Load 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
2182OR AB7Check to see if the read flag is set. If Z FLAG then it was INPUT
2186-2188LD A,(40A9H)LD A,(CASFLG)3A A9 40Now 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
2189OR AB7Check to see if the cassette recorder is the current input device, as we will need to give a FILE DATA error
218A-218BLD E,2AH1E 2ALoad Register E with the ?FD ERRORcode
218C-218EIf the current input device is the cassette recorder, go give a ?FD ERROR
218FPOP BCC1Clean up the STACK (discards the pointer into the variable list)
2190-2192- ↳ RDOINPLoad HL with the starting address of the ?REDOmessage
2196-2198LD HL,(40E6H)LD HL,(SAVTXT)2A E6 40Get the value of the current BASIC program pointer in HL.
Note: 40E6H-40E7H is a common temporary storage location
2199RETC9Return out of this routine back to NEWSTT of the INPUT statement
219A - INPUT logic- "INPUT"
219A-219C- ↳ INPUTCheck to see if there is an illegal direct in the input statement
219DLD A,(HL)7ELoad Register A with the character at the location of the current BASIC program pointer in HL
219ECall the DOS link at 41D6H. In NEWDOS 2.1 this is called at the beginning of INPUT processing.
21A1-21A2SUB 23HD6 23Check to see if the character at the location of the current BASIC program pointer in Register A is a #
21A3-21A5LD (40A9H),ALD (CASFLG),A32 A9 40Set the current input device flag for the cassette recorder.
Note: 40A9H holds cassette input flag
21A6LD A,(HL)7ELoad Register A with the character at the location of the current BASIC program pointer in HL
21A9-21ABIf 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
21ACPUSH HLE5Save the current BASIC program pointer in HL to the STACK
21AD-21AELD B,FAH06 FALoad Register B with 250, which is the maximum number of characters which can be read
21AF-21B1LD HL,(40A7H)LD HL,(BUFPNT)2A A7 40Load HL with the starting address of the input buffer.
Note: 40A7H-40A8H holds the input Buffer pointer
21B2-21B4- ↳ FILBUFTop 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)
21B5LD (HL),A77Save the byte read from the cassette recorder in Register A at the location of the input buffer pointer in HL
21B6INC HL23Bump the value of the input buffer pointer in HL
21B7-21B8CP 0DHFE 0DCheck 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-21BAJump out of this routine if the character read from the cassette recorder in Register A is a carriage return
21BD- ↳ ENDRECDEC HL2BDecrement the value of the input buffer pointer in HL to make room in the buffer for a terminator character
21BD- ↳ ENDRECDEC HL2BDecrement the value of the input buffer pointer in HL to make room in the buffer for a terminator character
21BE-21BFLD (HL),00H36 00Save a zero (which is a terminator)( at the location of the input buffer pointer in HL
21C0-21C2GOSUB to 01F8H to put a 00H at the end of the tape and turn the cassette recorder off
21C3-21C5LD HL,(40A7H)LD HL,(BUFPNT)2A A7 40Load HL with the starting address of the input buffer.
Note: 40A7H-40A8H holds the input Buffer pointer
21C6DEC HL2BDecrement the value of the input buffer pointer in HL
21C7-21C8Jump to 21EBH to store a comma there so we can use the READ processing
21C9-21CB- ↳ INTCASLoad BC with a return address of 21DBH for where to go when done dealing with a quoted string
21CCPUSH BCC5Save the value of the return address in BC to the STACK
21CD-21CE- ↳ QTINPCP 22HFE 22Check 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.
21CFRET NZC0Return to 21DBH if the character at the location of the current BASIC program pointer in Register A isn't a quote
21D0-21D2Go set up pointers for the prompting message in the temporary string work area
21D3-21D4RST 08H ⇒ 3BSYNCHK ;CF 3BSince 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).
21D5PUSH HLE5Save the value of the current BASIC program pointer in HL to the STACK
21D9POP HLE1Restore the code string address (from the STACK) into HL
21DARETC9Return to 21DBH
21C9-21CB- ↳ INTCASLoad BC with a return address of 21DBH for where to go when done dealing with a quoted string
21DB- ↳ NOTQTIPUSH HLE5Save the value of the current BASIC program pointer in HL to the STACK
21DC-21DE- ↳ GETAGNPrint the ? prompt and get the input from the keyboard
21DFPOP BCC1Remove the value of the current BASIC program pointer from the STACK, as we may be exiting
21E0-21E2The CARRY FLAG will be set if a BREAKwas hit (meaning we got no input). Jump back to 1DBEH if the BREAKkey was pressed
21E3INC HL23Bump the value of the input buffer pointer in HL
21E4LD A,(HL)7ELoad Register A with the character at the location of the input buffer pointer in HL
21E5OR AB7Test the value of the character at the location of the input buffer pointer in Register A
21E6DEC HL2BDecrement the value of the input buffer pointer in HL
21E7PUSH BCC5Since it turns out we didn't exit, put the return address back onto to the STACK
21E8-21EASkip 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- ↳ INPCN3LD (HL),2CH36 2CSave a "," at the location of the current input buffer pointer in HL
21EF - READlogic- "READ"
21EF- ↳ READPUSH HLE5Save the current BASIC program pointer in HL
21F0-21F2LD HL,(40FFH)LD HL,(DATPTR)2A FF 40Load HL with the location of the last DATA statement read.
Note: 40FFH-4100H holds READpointer
21F3-21F4OR 0AFHOR 1010 1111F6 AFTurn 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- ↳ INPCONXOR AAFSet the flag to indicate that this is an INPUT
21F5-21F7LD (40DEH),ALD (FLGINP),A32 DE 40Save 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).
21F8EX (SP),HLE3Swap 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
21FB-21FC- ↳ LOPDT2RST 08H ⇒ 2CSYNCHK ","CF 2CSince 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- ↳ LOPDATCall 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)
2200EX (SP),HLE3Swap 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.
2201PUSH DED5Save the pointer to the variable we are about to load with a value (held in Register Pair DE)
2202LD A,(HL)7ELoad Register A with the character at the location of the DATA LIST POINTER. This could be a terminator if its the first read
2203-2204CP 2CHFE 2CCheck 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-2206Jump 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-2209LD A,(40DEH)LD A,(FLGINP)3A DE 40Load 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
220AOR AB7Since 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-220DJump to 2296H if the input type flag in Register A indicates READ meaning we need to go search for another data statement
220E-2210LD A,(40A9H)LD A,(CASFLG)3A A9 40Load Register A with the value of the cassette input flag.
Note: 40A9H holds Cassette input flag
2211OR AB7Check 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-2213LD E,06HLD E,ERROD1E 06Load Register E with an ?OD ERRORcode
2214-2216Go to the Level II BASIC error routine and display an ?OD ERRORmessage if the input is from the cassette recorder
2217-2218LD A,3FH3E 3FLoad Register A with a "?"
2219-221BGOSUB 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-221EGo get the keyboard line input using the routine that will also print a "?" as we want "??" when we need more input
221FPOP DED1Get the address of VARIABLE POINT (the variable to be set) from the STACK and put it in DE
2220POP BCC1Get the return address from the STACK and put it in BC because we might be exiting this routine
2221-2223Exit this routine via a jump to 1DBEH if we have an empty input (such as if the BREAKkey was pressed)
2224INC HL23Since we got no input, get ready to exit. First, bump the value of the input buffer pointer in HL
2225LD A,(HL)7ELoad Register A with the character at the location of the input buffer pointer in HL
2226OR AB7Check to see if the character at the location of the input buffer pointer in Register A is an end of the input character
2227DEC HL2BDecrement the value of the input buffer pointer in HL
2228PUSH BCC5Save the RETurn address back to the top of the STACK because we didn't actually exit
2229-222BJump 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)
222CPUSH DED5Save 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
222DCall the DOS link at 41DCH. In NEWDOS 2.1, this is called during READ processing when a variable has been read.
2230RST 20HGETYPEE7We 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.
2231PUSH AFF5Save the number type of the variable to the STACK
2232-2233If 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
2234RST 10HCHRGETD7We 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.
2235LD D,A57We 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
2236LD B,A47Load Register B with the character at the location of the input buffer pointer in Register A
2237-2238CP 22HFE 22Check 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-223AJump to 2240H if the character at the location of the input buffer pointer in Register A is a quote
223B-223CLD D,3AH16 3ALoad D with the character :, which could act as an unquoted string terminator
223D-223ELD B,2CH06 2CLoad Register B with the character ,which could ALSO act as an unquoted string terminator
223FDEC HL2BDecrement the pointer to the BASIC line being interpreted since we need this starting character to be included in the quoted string
2240-2242- ↳ NOWGETRSet up a string descriptor for the value and copy it if necessary
2243- ↳ DOASIGPOP AFF1Discard the number type for the variable from the STACK and put it in Register A
2244EX DE,HLEBLoad DE with the pointer to the BASIC line being processed
2248EX (SP),HLE3Exchange HL and the top of the STACK, so that HL now points to the place to store the variable value
2249PUSH DED5Save the pointer to the BASIC line being processed to the STACK
224D- ↳ NUMINSRST 10HCHRGETD7We 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- ↳ NUMINSRST 10HCHRGETD7We 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.
224EPOP AFF1Load Register A with the number type for the variable to be set
224FPUSH AFF5Save it back to to the STACK
2250-2252Load BC with the value of the return address so that the assignment will jump to the LET routine
2253PUSH BCC5Save the return address in BC to the STACK
2254-2256If 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-2259If 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- ↳ STRDN2DEC HL2BDecrement the value of the pointer to the BASIC line being processed by 1 character
225BRST 10HCHRGETD7We 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-225DJump to 2263H if the character at the location of the input buffer pointer in HL is an end of the input character
225E-225FCP 2CHFE 2CCheck 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-2262Jump to 217FH if the character at the location of the input buffer pointer in Register A isn't a comma
2263- ↳ TRMOKEX (SP),HLE3Exchange the value of the input buffer pointer in HL with the value of the current BASIC program pointer to the STACK
2264DEC HL2BDecrement the value of the current BASIC program pointer in HL to point to the terminator character
2265RST 10HCHRGETD7We 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-2268If 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
2269POP DED1Remove the pointer to the data location from the STACK
226ANOP
226BNOP
226CNOP
226DNOP
226ENOP
226F-2271LD A,(40DEH)LD A,(FLGINP)3A DE 40Load 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
2272OR AB7Check to see if the input type is READor INPUT
2273EX DE,HLEBLoad DE with the value of the current BASIC program pointer in HL
2274-2276Jump if the input type flag is set for READ so we can set the DATA POINTER
2277PUSH DED5Save the current BASIC program pointer in DE to the STACK
227BOR (HL)B6Check to see if this is the end of the input (which could be a "," or a ":")
227C-227ELoad HL with the starting address of the ?EXTRA IGNOREDmessage
227F-2281If 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- ↳ FINPRGPOP HLE1Get the value of the pointer to the BASIC line being processed from the STACK and put it in HL
2283-2285Go turn off the cassette recorder and return to the BASIC interpreter
2286-2295 - MESSAGE STORAGE LOCATION- "EXIGNT"
2286-2295- ↳ EXIGNT"?Extra ignored" + 0DH + 00H3FThe 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.
2299- ↳ DATFNDOR AB7Check to see if this is the end of the BASIC line
229CINC HL23Bump the value of the current BASIC program pointer in HL
229DLD A,(HL)7ELoad Register A with the LSB of the line address at the location of the current BASIC program pointer in HL
229EINC HL23Bump the value of the current BASIC program pointer in HL
229FOR (HL)B6Combine 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-22A1LD E,06H1E 06Load Register E with an ?OD ERRORcode
22A2-22A4Go to the Level II BASIC error routine and display an OD ERROR message if this is the end of the BASIC program
22A5INC HL23Bump the value of the current BASIC program pointer in HL
22A6LD E,(HL)5ELoad Register E with the LSB of the BASIC line number at the location of the current BASIC program pointer in HL
22A7INC HL23Bump the value of the current BASIC program pointer in HL
22A8LD D,(HL)56Load Register D with the MSB of the BASIC line number at the location of the current BASIC program pointer in HL
22A9EX DE,HLEBExchange the value of the current BASIC program pointer in HL with the value of the BASIC line number in DE
22AA-22ACLD (40DAH),HLLD (DATLIN),HL22 DA 40Save the BASIC line number in HL.
Note: 40DAH-40DBH holds DATA line number
22ADEX DE,HLEBExchange the value of the current BASIC program pointer in DE with the value of the BASIC line number in HL
22AE- ↳ NOWLINRST 10HCHRGETD7We 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-22B0CP 88HCP $DATAFE 88Check 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-22B2Jump 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-22B5Now 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- ↳ NEXTLD DE,0000H11 00 00Load 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- ↳ NEXTCGet 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-22BELD (40DFH),HLLD (TEMP),HL22 DF 40Save the value of the current BASIC program pointer (contained in 40DFH) in H into a common temporary storage area
22BF-22C1Go search the STACK for the appropriate FOR entry that matches the variable name being used here
22C2-22C4If FNDFOR found nothing, then display a NF ERROR message if the appropriate FOR push wasn't found
22C5LD SP,HLF9Clean up the STACK. First set the STACK pointer with the value of the memory pointer in HL
22C6-22C8LD (40E8H),HLLD (SAVSTK),HL22 E8 40Save the value in HL to the STACK pointer pointer
22C9PUSH DED5Save the pointer to the variables address (in DE) to the STACK
22CALD A,(HL)7ELoad Register A with the value of the sign for the STEPvalue
22CBINC HL23Bump the value of the memory pointer in HL
22CCPUSH AFF5Save the value of the sign for the STEPvalue in Register A to the STACK
22CDPUSH DED5Save the pointer to the loop variable (in DE) to the STACK
22CELD A,(HL)7ELoad Register A with the number type flag for the STEPvalue to help determine if it is an integer
22CFINC HL23Bump the value of "FOR" entry pointer in HL
22D0OR AB7Check the value of the number type flag for the STEPflag in Register A. The MINUS FLAG will be set if it is an integer
22D4-22D6Move the step value into the ACC via a GOSUB to 09B1H (which moves a SINGLE PRECISION number pointed to by HL to ACCumulator)
22D7EX (SP),HLE3Exchange 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
22D8PUSH HLE5Save the pointer to the loop variable's address in HL to the STACK
22D9-22DBAdd 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
22DCPOP HLE1Get the pointer to the loop variable from the STACK and put it in HL
22DD-22DFGo move the single precision result from ACCumulator to the loop variable's address in HL
22E0POP HLE1Get the ENTRY POINTER from the STACK and put it in HL
22E1-22E3Get 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)
22E4PUSH HLE5Save the ENTRY POINTER to the STACK
22E5-22E7Compare 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.
22EAH - Part of the NEXTcode, where we process the variable as an integer- "INTNXT"
22EA- ↳ INTNXTINC HL23Since we are dealing with an integer, and not a single precision number, we need to skip 4 bytes of the TOvalue
22EBINC HL23Bump the value of the memory pointer in HL
22ECINC HL23Bump the value of the memory pointer in HL
22EDINC HL23Bump the value of the memory pointer in HL
22EELD C,(HL)4ELoad Register C with the LSB of the STEPvalue (held at the location of the memory pointer in HL)
22EFINC HL23Bump the value of the memory pointer in HL to be the MSB of the STEPvalue
22F0LD B,(HL)46Load Register B with the MSB of the STEPvalue at the location of the memory pointer in HL
22F1INC HL23Bump the value of the memory pointer in HL so that it is the STACK address of the TOlimit
22F2EX (SP),HLE3Exchange 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
22F3LD E,(HL)5ENext we need to get thee loop's variable value into Register Pair DE. First, load Register E with the LSB of the loop variable
22F4INC HL23Bump the value of the memory pointer in HL to point to the MSB of the index
22F5LD D,(HL)56Load Register D with the MSB of the loop variable
22F6PUSH HLE5Save the pointer to the loop variable value to the STACK
22F7LD L,C69Next, we are going to need to add DE to HL. First, load Register L with the LSB of the STEPvalue in Register C
22F8LD H,B60Load Register H with the MSB of the STEPvalue in Register B
22F9-22FBWith 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-22FELD A,(40AFH)LD A,(VALTYP)3A AF 40Load 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
22FFCP 04HFE 04Check 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-2303Show 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
2304EX DE,HLEBSwap DE and HL so that DE will now hold the new loop variable value
2305POP HLE1Restore the pointer to the loop variable back into HL
2306LD (HL),D72Save the MSB of the result in Register D at the location of the memory pointer in HL
2307DEC HL2BDecrement the value of the memory pointer in reg? ister pair HL
2308LD (HL),E73Save the LSB of the result in Register E at the location of the memory pointer in HL
2309POP HLE1Restore the pointer for the FOR entry back into HL
230APUSH DED5Save the value of the loop's variable (the index) in DE to the STACK
230BLD E,(HL)5EWe 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
230CINC HL23Bump the value of the memory pointer in HL to point to the MSB of the TOvalue
230DLD D,(HL)56Load Register D with the MSB of the TOvalue at the location of the memory pointer in HL
230EINC HL23Bump the value of the memory pointer in HL so now it points to the line number
230FEX (SP),HLE3Swap 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-2312We 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- ↳ FINNXTPOP HLE1Restore the "FOR" entry pointer (which is now pointing past the final value) into HL
2314POP BCC1Get the value of the sign from the STACK and put it in BC
2315SUB B90We 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-2318Call 09C2H (which loads a SINGLE PRECISION value pointed to by HL into Register Pairs BC and DE)
2319-231AJump to 2324H if the index does not equal the TOlimit (meaning the FOR . NEXT loop has not been completed)
231BEX DE,HLEBLoad HL with the BASIC line number of the FORstatement in DE
231C-231ELD (40A2H),HLLD (CURLIN),HL22 A2 40Save the BASIC line number in HL as the current BASIC line number (which is stored at 40A2H-40A3H).
231FLD L,C69Set 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
2320LD H,B60Load Register H with the MSB of the current BASIC program pointer in Register B
2321-2323Jump 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- ↳ LOOPDNLD SP,HLF9We are going to need to eliminate the FOR entry since HL already moved all the way down the entry
2325-2327LD (40E8H),HLLD (SAVSTK),HL22 E8 40Reset the STACK pointer pointer
2328-232ALD HL,(40DFH)LD HL,(TEMP)2A DF 40Save the value of the current BASIC program pointer in HL into a common temporary storage area
232BLD A,(HL)7EWe 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-232DCP 2CHFE 2CCheck 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-2330If 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
2331RST 10HCHRGETD7We 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-2334GOSUB 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- ↳ FRMPRNRST 08H ⇒ 28SYNCHK "("CF 28If 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- ↳ FRMEVLDEC HL2BThis 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- ↳ FRMCHKLD D,00H16 00Load Register D with zero as a dummy precedence for the formula
233A- ↳ LPOPERPUSH DED5Save the value in DE to the STACK
233B-233CLD C,01H0E 01Load Register C with the number of bytes of memory required for a return address -1 (so 2 bytes)
233D-233FSince 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-2342Go get the value of the next part of the expression at the location of the current BASIC program pointer in HL
2343-2345LD (40F3H),HLLD (TEMP2),HL22 F3 40Save 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- ↳ RETAOPLD HL,(40F3H)LD HL,(TEMP2)2A F3 40Load HL with the value of the current BASIC program pointer.
Note: 40F3H-40F4H is a temporary storage location
2349- ↳ TSTOPPOP BCC1Get the last PRECEDENCE value from the STACK and put it in BC
234A- ↳ NOTSTVLD A,(HL)7ELoad Register A with the character at the location of the current BASIC program pointer in HL
234B-234CLD D,00H16 00Load 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- ↳ LOPRELSUB D4HSUB GREATKD6 D4Check 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-2350Jump 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- ↳ NMRELCP 03HCP NMRELFE 03Check 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-2354If 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-2356CP 01HFE 01Set the Carry flag if >. Then test for <=or >=
2357RLA17Adjust the value in Register A to give >as 1, =as 2, or <as 4
2358XOR DAACombine 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 >>
2359CP DBACheck 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.
235ALD D,A57Load Register D with the mask in Register A
235E-2360LD (40D8H),HLLD (TEMP3),HLLD (TEMP3),HL22 D8 40Save the address of the >, =, or <token (held in HL) to 40D8H, which is another temporary storage location
2361RST 10HCHRGETD7We 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.
2364- ↳ ENDRELLD A,D7ALoad Register A with the mask from Register D
2365OR AB7Test the value of the operator in Register A against the mask to see if any of the masked operators are present
2366-2368If the NZ FLAG is set, then one of the masked operators (>, =, or <) is present, so JUMP to FINDREL to handle those
2369LD A,(HL)7ELoad Register A with the character at the location of the current BASIC program pointer in HL
236A-236CLD (40D8H),HLLD (TEMP3),HL22 D8 40Save the address of the current BASIC program pointer in HL (which is an arithmetic operator) to 40D8H, which is another temporary storage location
236D-236ESUB 0CDHSUB PLUSTKD6 CDCheck to see if the operator at the location of the current BASIC program pointer in Register A is an arithmetic token
236FRET CD8Return if the character at the location of the current BASIC program pointer in Register A isn't an arithmetic token
2370-2371CP 07HCP LSTOPKFE 07Check 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.
2372RET NCD0Return if the character at the location of the current BASIC program pointer in Register A isn't a +to ORtoken
2373LD E,A5FLoad 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-2376LD A,(40AFH)LD A,(VALTYP)3A AF 40Load Register A with the current value of the number type flag.
Note: 40AFH holds Current number type flag
2377-2378SUB 03HD6 03Test 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
2379OR EB3Combine 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-237CJump 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-237FLoad HL with the starting address of the table of precedence operator values
2380ADD HL,DE19Add the value of the offset in DE to the table of precedence values pointer in HL
2381LD A,B78Load Register A with the old precedence (i.e., the precedence value for the last operator) in Register B
2382LD D,(HL)56Load Register D with the precedence value for the current operator
2383CP DBALet 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.
2384RET NCD0We 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
2385PUSH BCC5Save the precedence value and the token for the last operator in BC to the STACK
2386-2388Load BC with the return address in case there is a break in precedence
2389PUSH BCC5Save the return address in BC to the STACK
238ALD A,D7ALoad Register A with the precedence value for the current operator in Register D so that we can test for an exponent
238B-238CCP 7FHCP EXPSTKFE 7FCheck 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-238FJP Z,23D4HCA D4 23Jump down to 23D4H if the precedence value for the current operator in Register A indicates an exponential operator
238D-238FJump down to 23D4H if the precedence value for the current operator in Register A indicates an exponential operator
2390-2391CP 51HFE 51Check 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-2394Jump 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
2398OR AB7Ensure the CARRY FLAG is off
2399-239B- ↳ PUSVALLD A,(40AFH)LD A,(VALTYP)3A AF 40Load Register A with the type of value we are dealing with.
Note: 40AFH holds Current number type flag
239CDEC A3DNext we need to set the flags without setting CARRY ... Adjust the current number type flag in Register A
239DDEC A3DAdjust the current number type flag in Register A
239EDEC A3DAdjust the current number type flag in Register A. Now A will be -1=Integer, 0=String, 1=Single Precision, 5=Double Precision
239F-23A1Display a ?TM ERRORif the current value in ACCumulator is a string
23A2LD C,(HL)4EGet the value at the location of the memory pointer in HL and put it in Register C
23A3INC HL23Bump the value of the memory pointer in HL
23A4LD B,(HL)46Load Register B with the value at the location of the memory pointer in HL
23A5PUSH BCC5Save the FACLO (held in B) to the STACK
23A6-23A8If the data was an integer, then we are done, so in that case JUMP to 23C5H
23A9INC HL23It's not an integer, so lets get the rest of the value by bumping the value of the memory pointer in HL
23AALD C,(HL)4ELoad Register C with the value at the location of the memory pointer in HL
23ABINC HL23Bump the value of the memory pointer in HL
23ACLD B,(HL)46Load Register B with the value at the location of the memory pointer in HL
23ADPUSH BCC5Save the value in BC (the rest of the digit) to the STACK
23AEPUSH AFF5Save the variable flag (held in value in AF as the type - 3) to the STACK
23AFOR AB7Reset the status flags so that we can test if the current number type is double precision
23B0-23B2Jump down to 23C4H if the current number type is single precision
23B3POP AFF1Restore the variable flag into Register Pair AF from the STACK
23B4INC HL23Bump the value of the memory pointer in HL
23B5-23B6Skip the next instruction if the current number type is single precision
23B7-23B9Reset HL to start of ACCumulator for a double density number.
Note: 411DH-4124H holds REG l
23BA- ↳ PUSDVRLD C,(HL)4ELoad Register C with the rest of the double precision value (held at the location of the memory pointer in HL)
23BBINC HL23Bump the value of the memory pointer in HL
23BCLD B,(HL)46Load Register B with the next digit LSB (at the location of the memory pointer in HL)
23BDINC HL23Bump the value of the memory pointer in HL to the next digit
23BEPUSH BCC5Save the LSB/NMSB of the double precision value (held in BC) to the STACK
23BFLD C,(HL)4ELoad Register C with the value at the location of the memory pointer in HL
23C0INC HL23Bump the value of the memory pointer in HL
23C1LD B,(HL)46Load Register B with the value at the location of the memory pointer in HL
23C2PUSH BCC5Save the value in BC to the STACK
23C3LD B,0F1H06 F1Z-80 Trick to nullify the next opcode if passing through
23C4- ↳ VPSHD1POP AFF1Restore the variable flag (which is actually the variable flag - 3)
23C5-23C6- ↳ VPUSHDADD A,03HC6 03Adjust the number type in Register A up 3
23C7LD C,E4BLoad Register C with the value of the current operator token in Register E (0-7)
23C8LD B,A47Load Register B with the number type flag in Register A
23C9PUSH BCC5Save these two new things (held in BC) to the STACK
23CA-23CCLoad BC with the return address of 2406H which is the APPLOP general operator application routine to do type conversions
23CD- ↳ FINTMPPUSH BCC5Save the return address in BC to the STACK
23CE-23D0LD HL,(40D8H)LD HL,(TEMP3)2A D8 40Load HL with the value of the current BASIC program pointer.
Note: 40D8H-40D9H holds Temporary storage location
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- ↳ EXPSTKCall the CONVERT TO SINGLE PRECISION routine at 0AB1H (which converts the contents of ACCumulator from integer or double precision into single precision)
23D7-23D9Call 09A4 which moves the SINGLE PRECISION value in the ACCumulator to the STACK (stored in LSB/MSB/Exponent order)
23DA-23DCLoad BC with the address of the exponential X^Y routine at 13F2H
23DD-23DELD D,7FH16 7FLoad Register D with the precedence value for an exponential operator
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- ↳ ANDORDPUSH DED5Save the precedence value and the operator token in DE to the STACK
23E2-23E4Call 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)
23E5POP DED1Get the precedence value and the operator token from the STACK and put it in DE
23E6PUSH HLE5Save the left hand operand in HL to the STACK
23EA-23EBJump 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- ↳ FINRELLD A,B78Load Register A with the precedence value for the PRIOR operator in Register B
23ED-23EECP 64HFE 64Check 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.
23EFRET NCD0If the PRIOR operator has a higher precedentnce then apply that one instead via a RETurn
23F0PUSH BCC5Save the precedence value and the operator token for the PRIOR operator in BC to the STACK
23F1PUSH DED5Save the precedence value (D) and the token (E) for the current operator from DE (either a 6, 5, or 3) to the STACK
23F2-23F4Load DE with the precedence value (of 100) and the displatch offset of the token for the new operator
23F5-23F7LD 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
23F8PUSH HLE5and push it to the STACK
23F9RST 20HGETYPEE7We 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-23FCIf that test shows we do NOT have a STRING (i.e., we have numbers), jump back to 2395H to build an APPLOP entry
23FD-23FFLD HL,(4121H)LD HL,(FACLO)2A 21 41If 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
2400PUSH HLE5Save the string's address in HL to the STACK so that STRCMP can use it.
2404-2405Jump 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- ↳ APPLOPPOP BCC1Get 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
2407LD A,C79Load Register A with the operator token in Register C
2408-240ALD (40B0H),ALD (DORES),A32 B0 40Save the operator token in Register A.
Note: 40B0H holds Temporary storage location
240BLD A,B78Load Register A with the operand value type for the value held in the STACK (held in Register B)
240C-240DCP 08HFE 08Check 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-240FJump forward to 2438H to force the number in the STACK to double precision
2410-2412LD A,(40AFH)LD A,(VALTYP)3A AF 40Load Register A with the number type for the current result in ACCumulator.
Note: 40AFH holds Current number type flag
2413-2414CP 08HFE 08Check 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-2417Jump forward to 2460H if the current value in ACCumulator is double precision so that we can convert the STACK operand to double density
2418LD D,A57Load Register D with the current data type flag for the value in the ACCumulator held in Register A
2419LD A,B78Load Register A with value type of the value in the STACK entry (looking for single precision)
241A-241BCP 04HFE 04Check 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-241EIf so, jump forward to 2472H to convert he ACCumulator number to single precision
241FLD A,D7ALoad Register A with the number type flag for the value in ACCumulator for single precision.
2420-2421CP 03HFE 03Check 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-2424Display a ?TM ERRORmessage if the current value in ACCumulator is a string
2425-2427Jump 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-242ALoad HL with the starting address of the arithmetic jump table
242B-242CLD B,00H06 00Load Register B with zero
242DADD HL,BC09Add the operator's token in BC to the value of the arithmetic jump table pointer in HL
242EADD HL,BC09Add 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.
242FLD C,(HL)4ENow 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
2430INC HL23Bump the value of the arithmetic jump table pointer in HL
2431LD B,(HL)46Load Register B with the MSB of the jump address at the location of the arithmetic jump table pointer in HL
2432POP DED1Get the left hand operand from the STACK and put it into Register Pair DE
2433-2435LD HL,(4121H)LD HL,(FACLO)2A 21 41Get the right hand operand from (FACLO) and put it into Register Pair DE
2436PUSH BCC5Save the arithmetic routine's jump address in register pair BC to the STACK
2437RETC9RETurn 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- ↳ STKDBLMake 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)
243EPOP HLE1Get the STACK operand into the ACCumulator ...POP the STACK and put it in HL
243F-2441LD (411FH),HLLD (DFACLO+2),HL22 1F 41Save the value in HL near the end of the ACCumulator
2442POP HLE1Get the value from the STACK and put it in HL
2443-2445LD (411DH),HLLD (DFACLO),HL22 1D 41Save the value in HL in the ACCumulator.
Note: 411DH-4124H holds the ACCumulator
2446- ↳ SNGDBLPOP BCC1Next we need 4 bytes from the STACK so ... get the value from the STACK and put it in BC
2447POP DED1Get the value from the STACK and put it in DE
2448-244APut 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- ↳ SETDBLConvert 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-2450Load HL with the double precision arithmetic jump table's starting address
2451-2453- ↳ DODSPLD A,(40B0H)
LD A,(DORES)3A B0 40Load Register A with the operator token in Register A.
Note: 40B0H holds Temporary storage location
2454RLCA07Multiply the value of the operator token in Register A by two since each byte of the table is 2 bytes.
2455PUSH BCC5Save the value in BC to the STACK so we can use it for 16 bit arithmetic.
2456LD C,A4FLoad Register C with the adjusted value of the operator token in Register A (i.e., 2 x token)
2457-2458LD B,00H06 00Load Register B with zero
2459ADD HL,BC09Add 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
245APOP BCC1Restore BC from the STACK and put it in BC in preparation for single precision math
245BLD A,(HL)7ELoad Register A with the LSB of the jump address at the location of the arithmetic jump table pointer in HL
245CINC HL23Bump the value of the arithmetic jump table pointer in HL
245DLD H,(HL)66Load Register H with the MSB of the jump address at the location of the arithmetic jump table pointer in HL
245ELD L,A6FLoad Register L with the LSB of the jump address in Register A
245FJP (HL)E9Jump 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- ↳ FACDBLPUSH BCC5Save the number type of the value held in the STACK
2464POP AFF1Load Register A with the value type of the number in the STACK
2465-2467LD (40AFH),ALD (VALTYP),A32 AF 40Save the value of the current number type flag in Register A.
Note: 40AFH holds Current number type flag
2468-2469CP 04HFE 04Check 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-246BJump back to 2446H if the current result in ACCumulator is single precision. That will POP BC and DE, and then CALL MOVFR to continue
246CPOP HLE1Get the integer value from the STACK and put it in HL
246D-246FLD (4121H),HLLD (FACLO),HL22 21 41Save the integer value in HL in the ACCumulator
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- ↳ STKSNGConvert 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)
2475POP BCC1Get the left hand operand into the registers. First POP the value from the STACK and put it in BC
2476POP DED1and then get the value from the STACK and put it in DE
2477-2479- ↳ SNGDOLoad HL with the starting address of the single precision arithmetic jump table
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- ↳ FACSNGPOP HLE1Get the integer value from the STACK and put it in HL
247D-247FCall 09A4 which moves the SINGLE PRECISION value in ACCumulator to the STACK (stored in LSB/MSB/Exponent order)
2483-2485Call 09BF which loads the SINGLE PRECISION value (tyhe left hand operator) in ACCumulator into BC/DE
2486POP HLE1Get the ACCumulator LSB/NMSB from the STACK and put it in HL
2487-2489LD (4123H),HLLD (FAC-1),HL22 23 41Save the value in HL in ACCumulator
248APOP HLE1Get the MSB and exponent from the STACK and put it in HL
248B-248DLD (4121H),HLLD (FACLO),HL22 21 41Save the value in HL in ACCumulator
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- ↳ INTDIVPUSH HLE5Save the right hand argument (in HL) to the STACK
2491EX DE,HLEBExchange the left hand argument into HL and the right hand argument into DE
2492-2494Go convert the integer value in HL to single precision and return with the result in the ACCumulator
2495POP HLE1Get the right hand argument from the STACK and put it in HL
2496-2498Call 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-249BGo convert the integer value in HL (i.e., the right hand argument) to single precision and return with the result the ACCumulator
249F - Evaluate a Variable, Constant, or Function Call- "EVAL"
249F- ↳ EVALRST 10HCHRGETD7We 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-24A1LD E,28H1E 28Load Register E with a ?MO ERRORcode
24A5-24A7If 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-24AACheck 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-24ADIf 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-24AFCP 0CDHCP PLUSTKFE CDCheck 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-24B1We want to ignore any +token here, so if we have one, JUMP back to the top of this EVAL routine and keep parsing.
24B2-24B3CP 2EHFE 2ECheck 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-24B6If 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-24B8CP 0CEHCP MINUTKFE CECheck 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-24BBProcess 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-24BDCP 22HFE 22Check 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-24C0If 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-24C2CP 0CBHCP NOTTKFE CBCheck 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-24C5Jump to 25C4H if the character at the location of the current BASIC program pointer in Register A is a NOTtoken
24C6-24C7CP 26HFE 26Check 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.
24CB-24CCCP 0C3HCP ERCTKFE C3Check 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-24CEJump to 24D9H if the character at the location of the current BASIC program pointer in Register A isn't an ERRtoken
24CFRST 10HCHRGETD7We 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-24D2LD A,(409AH)LD A,(ERRFLG)3A 9A 40Load Register A with the value of the current error number.
Note: 409AH holds the RESUMEflag
24D3- ↳ NTDERCPUSH HLE5Save the value of the current BASIC program pointer in HL to the STACK
24D4-24D6Go save the value of the current error number in Register A (as an integer) as the current result in REG l
24D7POP HLE1Get the value of the current BASIC program pointer from the STACK and put it in HL
24D8RETC9RETurn to CALLer
24D9-24DA- ↳ NTERCCP C2HCP ERLTKFE C2Check to see if the character at the location of the current BASIC program pointer in Register A is a ERLtoken
24DB-24DCJump to 24E7H if the character at the location of the current BASIC program pointer in Register A isn't an ERLtoken
24DDRST 10HCHRGETD7We 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.
24DEPUSH HLE5Save the value of the current BASIC program pointer in HL to the STACK
24DF-24E1LD HL,(40EAH)LD HL,(ERRLIN)2A EA 40Load HL with the current error line number.
Note: 40EAH-40EBH holds the line number with error
24E2-24E3Go save the error line number in HL as the current result in REG1
24E5POP HLE1Get the value of the current BASIC program pointer from the STACK and put it in HL
24E6RETC9RETurn to CALLer
24E7-24FEVARPTRlogic- "NTERL"
24E7-24E8- ↳ NTERLCP 0C0HCP $VARPTRFE C0Check to see if the character at the location of the current BASIC program pointer in Register A is a VARPTRtoken
24E9-24EAJump back to 24FFH if the character at the location of the current BASIC program pointer in Register A isn't a VARPTRtoken
24EBRST 10HCHRGETD7We 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-24EDRST 08H ⇒ 28SYNCHK "("CF 28Since 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-24F0Call 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- ↳ VARRETRST 08H ⇒ 29SYNCHK ")"CF 29Since 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)
24F3PUSH HLE5Save the value of the current BASIC program pointer in HL to the STACK
24F4EX DE,HLEBSwap DE and HL so that HL now holds the value to return
24F5LD A,H7CLoad Register A with MSB of the variable's address in Register H to make sure that it isn't undefined.
24F6OR LB5Combine 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-24F9Display a ?FC ERRORif the variable's address in HL is equal to zero, meaning that the variable is undefined
24FA-24FCSave the variable's address in HL as an integer into ACCumulator
24FDPOP HLE1Get the value of the current BASIC program pointer from the STACK and put it in HL
24FERETC9Return 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- ↳ NTVARPCP 0C1HCP USRTKFE C1Check 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-2503Jump to 27FEH if the character at the location of the current BASIC program pointer in Register A is a USRtoken
2504-2505CP 0C5HCP INSRTKFE C5Check 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-2503If 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-250ACP 0C8HCP $MEMFE C8Check 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-250DIf 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-250FCP 0C7HCP $TIMEFE C7Check 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-250DIf 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-2514CP 0C6HCP $POINTFE C6Check 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-2517Jump to 0132H if the character at the location of the current BASIC program pointer in Register A is a POINTtoken
2518-2519CP 0C9HCP $INKEYFE C9Check 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-251CJump to 019DH if the character at the location of the current BASIC program pointer in Register A is an INKEY$token
251D-251ECP 0C4HCP $STRINGFE C4Check 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-2421Jump to 2A2FH if the character at the location of the current BASIC program pointer in Register A is a STRING$token
2522-2523CP 0BEHCP FNTKFE BECheck 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-2421If 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-2528SUB 0D7HSUB ONEFUND6 D7Check 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-252BJump 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- ↳ PARCHKGOSUB to 2335H to recursively evaluate the expression at the location of the current BASIC program pointer in HL
252F-2530RST 08H ⇒ 29SYNCHK ")"CF 29Since 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).
2531RETC9Return out of this routine
2532 - Binary Minus Routine- "DOMIN"
2532- ↳ DOMINLD D,7DH16 7DLoad Register D with a precedence value below "^" but above everything else since its a uniary minus.
2534-2536GOSUB to 233AH to evaluate the variable at the location of the current BASIC program pointer in HL
2537-2539LD HL,(40F3H)LD HL,(TEMP2)2A F3 40Load HL with the value of the current BASIC program pointer.
Note: 40F3H-40F4H is a temporary storage location
253APUSH HLE5Save the value of the current BASIC program pointer in HL to the STACK so we know where to continue
253E- ↳ LABBCKPOP HLE1According 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$(
253FRETC9Return 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- ↳ ISVARGet 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- ↳ RETVARPUSH HLE5Save the value of the current BASIC program pointer in HL to the STACK
2544EX DE,HLEBSwap 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-2547LD (4121H),HLLD (FACLO),HL22 21 41In case it is a string, we will store the pointer to the descriptor in FACLO.
2548RST 20HGETYPEE7We 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-254BIf 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
254CPOP HLE1Restore the value of the current BASIC program pointer to Register Pair HL
254DRETC9Return to the caller
254E - This routine processes an expression for SNG(to MID$(- "ISFUN"
254E-254F- ↳ ISFUNLD B,00H06 00Load Register B with zero
2550RLCA07Set A to be 2 * (token - D7H)
2551LD C,A4FSave the new token
2552PUSH BCC5Save 0/2*(token-D7) on STACK
2553RST 10HCHRGETD7Get 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.
2554LD A,C79Prepare to look for the function number
2555- ↳ NUMGFNCP 41HCP NUMGFNFE 41Test 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.
2557If 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$
2559Otherwise, 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.
255CRST 08H ⇒ 2CSYNCHK ","CF 2CWe need TWO arguments, so there needs to be a ",". With this we use RST 08H to test for a ,
255EGOSUB to 0AF4H to ensure the current variable is a string, otherwise it is an error
2561EX DE,HLEBSwap 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)
2562LD HL,(4121H)LD HL,(FACLO)2A 21 41Load HL with the string descriptor address held at the memory location pointed to by ACCumulator
2565EX (SP),HLE3Put the pointer to the string descriptor onto the STACK and put the function number into Register Pair HL
2566PUSH HLE5Save function number (i.e., 00 / 2*(token-D7H)) to the STACK
2567EX DE,HLEBSwap DE and HL so that HL will now point to the position in the current BASIC program being evaluated.
2568Evaluate n portion of the string function. Register E will contain the value of the formula.
256BEX DE,HLEBSwap 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.
256CEX (SP),HLE3Swap (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
256F-2571- ↳ OKNORMNext 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
2572EX (SP),HLE3Swap (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.
2573LD A,L7DLoad Register A with the function number [i.e., 2 * (token - D7H)]
2574-2575- ↳ BOTCONCP 0CHCP (SQRTK-ONEFUN)*2FE 0CCheck 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-2577Jump down to 257FH to avoid forcing the argument if the operator token in Register A is SGNto SQR
2578-2579- ↳ TOPCONCP 1BHCP (ATNTK-ONEFUN)*2+1FE 1BCheck 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.
257APUSH HLE5Save the function number (i.e., 0 + 2*(token - D7H)) to the stop of the STACK
257B-257DIf 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- ↳ NOTFRFLoad DE with a return address of 253EH for once the function is executed
2582PUSH DED5Save the value of the return address in DE to the STACK so it will act as the return adddress
2583-2585- ↳ FINGOLoad BC with the function dispatch/jump table address
2586- ↳ DISPATADD HL,BC09Add the jump table pointer in BC (i.e., the offset) with the value of the operator token in HL
2587LD C,(HL)4ELoad Register C with the LSB of the jump address at the location of the jump table pointer in HL
2588INC HL23Bump the value of the jump table pointer in HL
2589LD H,(HL)66Load Register H with the MSB of the jump address at the location of the jump table pointer in HL
258ALD L,C69Load Register L with the LSB of the jump address in Register C
258BJP (HL)E9Perform 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- ↳ STRCMPFirst 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
258FLD A,(HL)7ELoad Register A with the LSB of the length of the string in the FACLO
2590INC HL23Bump the value of the string's VARPTR in HL so that HL points to the LSB of the string address
2591LD C,(HL)4ELoad Register C with the LSB of the string in the FACLO
2592INC HL23Bump the value of the string's VARPTR in HL so that HL points to the MSB of the string address
2593LD B,(HL)46Load Register B with the MSB of the string in the FACLO. Register Pair BC now points to the FACLO string.
2594POP DED1Put the STACK string pointer into Register Pair DE
2595PUSH BCC5Save the pointer to the FACLO string data
2596PUSH AFF5Save the FACLO string's length in Register A to the STACK
2597-2599Free up the STACK string and RETURN with the pointer to the STACK string descriptor in Register Pair HL.
259APOP DED1Get the length of the FACLO string from the STACK and put it in Register D
259BLD E,(HL)5ELoad Register E with the STACK / BCDE string's length
259CINC HL23Bump the pointer to the STACK / BCDE string's entry in HL
259DLD C,(HL)4ELoad Register C with the LSB of the STACK / BCDE string's address
259EINC HL23Bump the pointer to the STACK / BCDE string's entry in HL
259FLD B,(HL)46Load Register B with the MSB of the STACK / BCDE string's address
25A0POP HLE1Get the second character pointer from the STACK and put it in HL
25A1- ↳ CSLOOPLD A,E7BLoad Register A with the length of the STACK / BCDE string in Register E
25A2OR DB2Combine the FACLO string's length in Register D with the STACK / BCDE string's length in Register A
25A3RET ZC8If 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
25A4LD A,D7ALoad Register A with the FACLO string's length in Register D
25A5-25A6SUB 01HD6 01Check 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.
25A7RET CD8If the CARRY FLAG is set, then the FACLO string has run out of characters, so RETurn
25A8XOR AAFIf 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
25A9CP EBBCheck 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.
25AAINC A3CBump the value in Register A for a return code of A = 1
25ABRET NCD0Return if there aren't any more characters in the STACK / BCDE string to be compared
25ACDEC D15If we are STILL here, then neither string has ended. First, decrement the value of the FACLO string's length in Register D
25ADDEC E1DDecrement the value of the STACK / BCDE string's length in Register E
25AELD A,(BC)0ALoad Register A with the character at the location of the STACK / BCDE string pointer in BC
25AFCP (HL)BECompare 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.
25B0INC HL23Bump the value of the first string pointer in HL
25B1INC BC03Bump the value of the second string pointer in BC
25B4CCF3FIf we are here, then the NZ FLAG was set and the strings are NOT equal. Since they are not equal, reverse the CARRY flag
25B8- ↳ DOCMPINC A3CBump the value of the current precedence value in Register A
25B9ADC A,A8FAdjust 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
25BAPOP BCC1Get the last operator value from the STACK and put it in BC
25BBAND BA0Combine the precedence value in Register B with the precedence value in Register A to see if any of the bits match.
25BC-25BDADD A,FFHC6 FFAdjust the value in Register A. This will give a 0 if both are equal and a CARRY if they are unequal
25BESBC A,A9FCheck 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-25C1Convert 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-25C3At 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- ↳ NOTERLD D,5AH16 5ANOT has a precedence value of 90, so we need a dummy entry of 90 on the STACK
25C6-25C8Go evaluate the expression with a dummy entry of 90 on the STACK
25C9-25CBWe 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)
25CCLD A,L7DThe next bunch of instructions are to complement Register Pair HL. First, load Register A with the LSB of the integer value
25CDCPL2FCompliment the LSB of the integer value in Register A
25CELD L,A6FLoad Register L with the adjusted LSB of the integer value in Register A
25CFLD A,H7CLoad Register A with the MSB of the integer value in Register H
25D0CPL2FCompliment the MSB of the integer value in Register A
25D1LD H,A67Load Register H with the adjusted MSB of the integer value in Register A
25D2-25D4LD (4121H),HLLD (FACLO),HL22 21 41Save the complimented integer value in HL as the current result in ACCumulator
25D5POP BCC1Clean up the STACK
25D6-25D8- ↳ RETAPGJump 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- ↳ GETPYRLD A,(40AFH)LD A,(VALTYP)3A AF 40Load Register A with the current value of the number type flag.
Note: 40AFH holds Current number type flag
25DC-25DDCP 08HFE 08Check 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- ↳ CGETYPIf that test shows that we have a DOUBLE PRECISION number, jump forward to 25E5H
25E0-25E1SUB 03HD6 03If the number is not double precision, subtract 3
25E2OR AB7Set the status flags of the adjusted number type flag in Register A
25E3SCF37Set the Carry flag
25E4RETC9RETurn to CALLer
25E5-25E6- ↳ NCASESUB 03HD6 03We are dealing with a double precision number so adjust the value of the current number type flag in Register A
25E7OR AB7Test the value of the current number type flag in Register A, which will exit without the CARRY flag set
25E8RETC9RETurn 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- ↳ DANDORPUSH BCC5B has he precedence value, so save BC to the STACK. The precedence value for OR is 70.
25EA-25ECWe 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)
25EDPOP AFF1Get the precedence value from the STACK and put it in Register A so that we can detemined between span class="code">ANDand OR
25EEPOP DED1Get the left hand argument from the STACK and put it in DE
25F2PUSH BCC5Save the value of the return address in BC to the STACK
25F3-25F4CP 46HFE 46Check 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-25F6If 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.
25F7LD A,E7BLoad Register A with the LSB of the first value in Register E
25F8OR LB5Combine the LSB of the first value in Register A with the LSB of the second value in Register L
25F9LD L,A6FLoad Register L with the ORed value in Register A
25FALD A,H7CLoad Register A with the MSB of the second value in Register H
25FBOR DB2Combine the MSB of the first value in Register D with the MSB of the second value in Register A
25FCRETC9Return to 27FAH (=convert the result to integer and return that integer calue to 2346H)
25FD - ANDlogic- "NOTOR"
25FD- ↳ NOTORLD A,E7BLoad Register A with the LSB of the first value in Register E
25FEAND LA5Combine the LSB of the first value in Register A with the LSB of the second value in Register L
25FFLD L,A6FLoad Register L with the ANDed value in Register A
2600LD A,H7CLoad Register A with the MSB of the second value in Register H
2601AND DA2Combine the MSB of the first value in Register D with the MSB of the second value in Register A
2602RETC9Return to 27FAH (=Make the result an integer and return to 2346H)
2603 - Dimension and Variable Searching Routine- "DIMCON"
2603- ↳ DIMCONDEC HL2BDecrement the value of the BASIC program pointer in HL so that we can see what the prior character was
2604RST 10HCHRGETD7We 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.
2605RET ZC8If the CHGRGET routine returned a Z FLAG, then we have a terminator; so RETurn since this is the end of the BASIC statement
2606-2607RST 08H ⇒ 2CSYNCHK ","CF 2CSince 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:
- If an entry is found, dimflg being on indicates a "doubly dimensioned" variable
- 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.
- When the build entry code finishes, only if dimflg is off will indexing be done
260BPUSH BCC5Save the return address of 2603H (in BC) to the STACK
260C-260DOR AFHF6 AFThis 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- ↳ PTRGETXOR AAFIf JUMPed here, then A is set to ZERO. As a reminder, if passed through from the above routine, A will be NOT ZERO.
260E-2610LD (40AEH),ALD (DIMFLG),A32 AE 40Save the value in Register A as the current variable location/creation flag.
Note: 40AEH holds LOCATE/CREATE variable flag
2611LD B,(HL)46Load Register B with the first character of the variable name
2612-2614- ↳ PTRGT2GOSUB to 1E3DH to make sure the first character of the variable name is a letter
2615-2617Display a ?SN ERRORif the first character of the variable name isn't a letter
2618XOR AAFZero Register A
2619LD C,A4FSet up to assume that there is no second character by zeroing Register C
261ARST 10HCHRGETD7We 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-261CJump to 2622H if the character at the location of the current BASIC program pointer in Register A is numeric
261D-261FGOSUB 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-2621Jump to 262BH if the character at the location of the current BASIC program pointer in Register A isn't a letter
2622- ↳ ISSECLD C,A4FIf we are here, then the second character was a number, so save it in Register C
2623- ↳ EATEMRST 10HCHRGETD7We 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-2625Loop back one OPCODE to keep eating characters until a non-numeric character is found
2626-2628Go check to see if the character at the location of the current BASIC program pointer in HL is alphabetic
2629-262AJump back to 2623H if the character at the location of the current BASIC program pointer in HL is alphabetic
262B-262D- ↳ NOSECLoad DE with a return address of 2652H. Done to save time/RAM from using JUMPs instead.
262EPUSH DED5Save the value of the return address in DE to the STACK
262F-2630LD D,02H16 02Load Register D with an integer number type flag
2631-2632CP 25HFE 25Check 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.
2633RET ZC8Return if the character at the location of the current BASIC program pointer in Register A is a %
2634INC D14Bump Register D so that it will be equal to a string number type flag (02=INT, 03=STR, 04=SNG, 08=DBL)
2635-2636CP 24HFE 24Check 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.
2637RET ZC8Return if the character at the location of current BASIC program pointer in Register A is a $
2638INC D14Bump Register D so that it will be equal to a single precision number type flag (02=INT, 03=STR, 04=SNG, 08=DBL)
2639-263ACP 21HFE 21Check 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.
263BRET ZC8Return if the character at the location of the current BASIC program pointer in Register A is a !
263C-263DLD D,08H16 08Load 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-263FCP 23HFE 23Check 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.
2640RET ZC8Return if the character at the location of the current BASIC program pointer in Register A is a #
2641LD A,B78Load Register A with the first character of the variable name from Register B
2642-2643SUB 41HD6 41Adjust 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-2645AND 7FHE6 7FGet rid of the user-defined function bit in Register B by ANDing it against 0111 1111
2646LD E,A5FNext 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-2648LD D,00H16 00Load Register D with zero
2649PUSH HLE5Save the value of the current BASIC program pointer in HL to the STACK
264A-264CLoad HL with the starting address of the variable declaration table.
NOTE:4101H-411AH holds Variable Declaration Table
264DADD HL,DE19Add the offset to the top of the table
264ELD D,(HL)56Load Register D with the number type value from the variable declaration table pointer in HL
264FPOP HLE1Get the value of the current BASIC program pointer from the STACK and put it in HL
2650DEC HL2BDecrement the value of the current BASIC program pointer in HL as there was no marking character
2651RETC9Return with data type in D
2652- ↳ HAVTYPLD A,D7ALoad Register A with the value of the number type flag in Register D
2653-2655LD (40AFH),ALD (VALTYP),A32 AF 40Save the number type flag for the current variable name from Register A.
NOTE:40AFH holds Current number type flag
2656RST 10HCHRGETD7We 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-2659LD A,(40DCH)LD A,(SUBFLG)3A DC 40Load Register A with the FORflag. Why the FOR flag? It doubles as a "should we allow arrays here" flag!
265AOR AB7Test the value of the FORflag in Register A
265ELD A,(HL)7ERe-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-2660SUB 28HD6 28Next, 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-2663If the Z FLAG is set then we have an array (meaning, it is a subscripted variable), so JUMP to 26E9H
2664- ↳ NOARYSXOR AAFZero Register A so that we can allow for parenthesis now
2665-2667LD (40DCH),ALD (SUBFLG),A32 DC 40Set the "permit arrays" array flag to 'no subscript'.
Note: 40DCH holds FORflag
2668PUSH HLE5Save the value of the current BASIC program pointer in HL to the STACK
2669PUSH DED5Save the number type flag for the variable in DE to the STACK
266A-266CLD HL,(40F9H)LD HL,(VARTAB)2A F9 40Load 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- ↳ LOPFNDEX DE,HLEBSwap 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-2670LD HL,(40FBH)LD HL,(ARYTAB)2A FB 40Load HL with the pointer to the end of simple variables. 40FBH-40FCH holds the starting address of the BASIC array variable storage area
2671RST 18HCOMPARDFNow 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.
2672POP HLE1Get the number type flag for the variable from STACK and put it in HL
2673-2674If 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
2675LD A,(DE)1ALoad Register A with the number type flag for the variable at the location of the simple variables pointer in DE
2676LD L,A6FPreserve Register A into Register L so we know how many entries to skip.
2677CP HBCCompare 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.
2678INC DE13Bump the value of the current simple variables pointer in DE to the 2nd character name for this entry
2679-267AIf 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
267BLD A,(DE)1ASince 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
267CCP CB9Compare 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-2637Jump to 2686H if the first characters of the variable names don't match
267FINC DE13Bump the value of the current simple variables pointer in DE
2680LD A,(DE)1ALoad Register A with the second character of the variable name at the location of the simple variables pointer in DE
2681CP BB8Compare 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-2684Jump 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-2686LD A,13H3E 13Z-80 Trick to skip the next INC DE if continuing through
2686- ↳ NOTIT1INC DE13Bump to the next entry in the simple variable list part 1
2687INC DE13Bump the value of the simple variables pointer in DE part 2
2688PUSH HLE5Save the number type flag for the variable in HL to the STACK so that it can be re-loaded at 2672H
2689-268ALD H,00H26 00Load Register H with zero so that Register Pair HL is the number of bytes to skip, but in 16 bits.
268BADD HL,DE19Add the value of the simple variables pointer in DE to the value of the number type flag in HL
268E- ↳ NOTFNSLD A,H7CLoad Register A with the length for the variable in Register H
268FPOP HLE1Get the value of the current BASIC program pointer from the STACK and put it in HL
2690EX (SP),HLE3Exchange (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
2691PUSH AFF5Save length of the variable in Register A to the STACK
2692PUSH DED5Save the current variable table position from DE to the STACK
2696RST 18HCOMPARDFNow 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-2698If the Z FLAG is set, then this was a VARPTR call, so JUMP forward to 26CFH.
2699-269BNext we need to see if EVAL called this routine. Load DE with a return address of the find address of variables routine at 2543H
269CRST 18HCOMPARDFWe 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.
269DPOP DED1Restore the current variable table position from the STACK and put it in DE
269E-269FIf 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.
26A0POP AFF1Clear the STACK and put the value of the number type flag for the variable from the STACK and put it in Register A
26A1EX (SP),HLE3Swap (SP) and HL so that the value of the current BASIC program pointer is now in Register Pair HL
26A2PUSH HLE5Save the value of the current BASIC program pointer in HL to the STACK
26A3PUSH BCC5Save the variable's address in BC to the STACK as we are about to use both Register B and Register C
26A4LD C,A4FLoad Register C with the value of the number type flag for the variable in Register A
26A5-26A6LD B,00H06 00Load Register B with zero so that the number type flag for the variable can be represented in 16 bits
26A7PUSH BCC5Save the variable's number type flag in BC to the STACK
26A8INC BC03Bump the value of the variable's number type flag in BC
26A9INC BC03Bump the value of the variable's number type flag in BC
26AAINC BC03Bump the value of the variable's number type flag in BC. Now the variable's length includes room for the addresses as well.
26AB-26ADLD HL,(40FDH)LD HL,(STREND)2A FD 40Load HL with the value of the free memory pointer.
Note: 40FDH-40FEH holds Free memory pointer
26AEPUSH HLE5Save the value of the free memory pointer in HL to the STACK
26AFADD HL,BC09Add the value of the variable's number type flag in BC to the value of the free memory pointer in HL
26B0POP BCC1Restore the high address from the STACK and put it in BC
26B1PUSH HLE5Save the value of the high address pointer in HL to the STACK
26B2-26B4Block transfer the variable information and make sure we do not overflow the STACK space via a GOSUB to BLTU.
26B5POP HLE1Get the value of the new free memory pointer (i.e., STREND) from the STACK and put it in HL
26B6-26B8LD (40FDH),HLLD (STREND),HL22 FD 40Save the value of the new free memory pointer in HL to lock in that variable space.
NOTE:40FDH-40FEH holds Free memory pointer
26B9LD H,B60Load Register H with the MSB of the new array variables pointer in Register B
26BALD L,C69Load 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-26BDLD (40FBH),HLLD (ARYTAB),HL22 FB 40Save the value of the new array variables pointer in HL. 40FBH-40FCH holds the starting address of the BASIC array variable storage area
26BE- ↳ ZEROERDEC HL2BAt 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-26C0LD (HL),00H36 00Zero the location of the memory pointer in HL
26C1RST 18HCOMPARDFNow 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.
26C4POP DED1Get the value of the variable's number type flag from the STACK and put it in DE
26C5LD (HL),E73Save the value of the number type flag in Register E at the location of the memory pointer in HL
26C6INC HL23Bump the value of the memory pointer in HL
26C7POP DED1Get the 2nd character of the variable's name from the STACK and put it in DE
26C8LD (HL),E73Save the first character of the variable's name in Register E at the location of the memory pointer in HL
26C9INC HL23Bump the value of the memory pointer in HL
26CALD (HL),D72Save the first character of the variable's name in Register D at the location of the memory pointer in HL
26CBEX DE,HLEBLoad DE with the value of the variable pointer in from HL
26CC- ↳ FINPTRINC DE13Bump the value of the variable pointer in DE so that it points to the value
26CDPOP HLE1Restore the value of the current BASIC program pointer from the STACK into Register Pair HL
26CERETC9RETurn to CALLer
26CF- ↳ VARNOTLD D,A57On entry, the Z FLAG was set, meaning that A=0. Zero out Register D with the value of Register AA
26D0LD E,A5FZero out Register E
26D1POP AFF1Clean up the STACK (which was the PUSHed DE)
26D2POP AFF1Clean up the STACK (which was the length)
26D3EX (SP),HLE3Swap (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
26D4RETC9Return to the VARPTRroutine
26D5 - This routine is ZERO out all variable types and skip any RETurn- "FINZER"
26D5-26D7- ↳ FINZERLD (4124H),ALD (FAC),A32 24 41Zero ACCumulator so that all single-precision and double-precision variables become zero
26D8POP BCC1Clean up the STACK (i.e., remove the length of the variable)
26D9LD H,A67Zero Register H to clear out integers as well
26DALD L,A6FZero Register L to clear out integers as well
26DB-26DDLD (4121H),HLLD (FACLO),HL22 21 41Zero the string pointer location in ACCumulator
26DERST 20HGETYPEE7We 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-26E0If 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-26E3If 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-26E6LD (4121H),HLLD (FACLO),HL22 21 41Save the value in HL as the current string pointer, which is now null.
26E7- ↳ POPHR2POP HLE1Get the value of the current BASIC program pointer from the STACK and put it in HL
26E8RETC9RETurn (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- ↳ ISARYPUSH HLE5Save the DIMFLG and VALTYP for recursion
26EA-26ECLD HL,(40AEH)LD HL,(DIMFLG)2A AE 40Load 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
26EDEX (SP),HLE3Swap (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
26EELD D,A57Zero Register D (which will hold the number of dimension)
26EF- ↳ INDLOPPUSH DED5Save the number of dimension (held in Register D) to the STACK
26F0PUSH BCC5Save the variable's name in BC to the STACK
26F1-26F3Go 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
26F4POP BCC1Get the variable's name from the STACK (1st and 2nd character) and put it in BC
26F5POP AFF1Get the variable's number of dimension so far from the STACK and put it in Register A
26F6EX DE,HLEBSwap 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
26F7EX (SP),HLE3Swap (SP) and HL so that HL will now hold DIMFLG and VALTYP and the array subscript will be at the stop of the STACK
26F8PUSH HLE5Save the DIMGFLG and VALTYP (in HL) to the STACK
26F9EX DE,HLEBSwap 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.
26FAINC A3CBump the number of dimensions evaluated in Register A
26FBLD D,A57Load Register D with the number of dimensions evaluated in Register A
26FCLD A,(HL)7ELoad Register A with the character at the location of the current BASIC program pointer in HL
26FD-26FECP 2CHFE 2CCheck 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-2700If 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-2702RST 08H ⇒ 29SYNCHK ")"CF 29Since 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- ↳ SUBSOKLD (40F3H),HLLD (TEMP2),HL22 F3 40Save the value of the current BASIC program pointer in HL into the TEMP2 storage area.
2706POP HLE1Get the DIMFLG and VALTYP from the STACK and put it in HL.
2707-2709LD (40AEH),HLLD (DIMFLG),HL22 AE 40Save the value of the DIMFLG and VALTYP into the DIMFLG location in RAM.
NOTE:40AEH holds LOCATE/CREATE variable flag
270APUSH DED5Save 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-270DLD HL,(40FBH)LD HL,(ARYTAB)2A FB 40We 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-270FLD A,19H3E 19Z-80 Trick to skip the next command of ADD HL,DE if falling through
270F- ↳ LOPFDAADD HL,DE19Advance past the current array as we know it isn't the correct one. Note: 40FBH-40FCH holds the array variables pointer
2710EX DE,HLEBSwap DE and HL so that DE holds the current search point. We don't care about HL.
2711-2713LD HL,(40FDH)LD HL,(STREND)2A FD 40Load HL with the place to STOP the search (i.e., the value of the free memory pointer).
Note: 40FDH-40FEH holds Free memory pointer
2714EX DE,HLEBSwap DE and HL so that DE now holds the place to stop the search and HL holds the current search point.
2715RST 18HCOMPARDFNow 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-2718LD A,(40AFH)LD A,(VALTYP)3A AF 40Load Register A with the value of the current number type flag.
Note: 40AFH holds Current number type flag
2719-271AIf 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
271BCP (HL)BECompare 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.
271CINC HL23Bump the value of the array variables pointer in HL
271D-271EJump forward (but still in this loop) to 2727H if the number type flags don't match
271FLD A,(HL)7ELoad Register A with the first character of the variable name at the location of the array variables pointer in HL
2720CP CB9Check 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.
2721INC HL23Bump the value of the array variables pointer in HL
2722-2723Jump forward (but still in this loop) to 2728H if the first characters of the variable names don't match
2724LD A,(HL)7ELoad Register A with the second character of the variable name at the location of the array variables pointer in HL
2725CP BB8Compare 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-2727LD A,23H3E 23This part of a Z-80 Trick. Will load A with 23H if passing through, and not do the following INC HL.
2727- ↳ NMARY2INC HL23Bump the value of the array variables pointer in HL
2728- ↳ NMARY1INC HL23Bump the value of the array variables pointer in HL. HL should now point to the LENGTH entry of the array being looked at
2729LD E,(HL)5ELoad Register E with the LSB of the LENGTH of the array being looked at
272AINC HL23Bump the value of the array variables pointer in HL
272BLD D,(HL)56Load Register D with the MSB of the LENGTH of the array being looked at
272CINC HL23Bump the value of the array variables pointer in HL
272D-272EIf 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-2731LD A,(40AEH)LD A,(DIMFLG)3A AE 40Load 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
2732OR AB7Since 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-2734LD E,12H1E 12Prepare for an error if this routine was NOT called by "DIM" by loading Register E with the ?DD ERRORcode
2735-2737If 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.
2738POP AFF1Get the number of dimension evaluated from the STACK and put it in Register A
2739SUB (HL)96To 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-273CIf they match then we are done so JUMP down to 2795H to read the indices
273D - ?BS ERROR entry point- "BSER"
273D-273E- ↳ BSERLD E,10H1E 10Load Register E with a ?BS ERRORcode
273F-2741Display 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 - Get an index
- Put number+1 down at the VARPTR
- Increase the VARPTR
- Decmrent the number of DIMs
- 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- ↳ NOTFDDLD (HL),A77Save the variable type for the array in Register A at the location of the array variables pointer in HL
2743INC HL23Bump 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)
2744LD E,A5FLoad Register E with the variable type flag for the current variable
2745-2746LD D,00H16 00Zero Register D so that Register Pair DE can be the size of one value of the type VALTYP
2747POP AFF1Get the number of dimensions evaluated from the STACK and put it in Register A
2748LD (HL),C71Save the second character of the variable's name in Register C at the location of the array variables pointer in HL
2749INC HL23Bump 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)
274ALD (HL),B70Save the first character of the variable's name in Register B at the location of the array variables pointer in HL
274BINC HL23Bump the value of the array variables pointer in HL to the LSB of the offset to the next entry
274CLD C,A4FIn preparation for the next CALL, load Register C with the number of two byte entries needed to store the size of each dimension
274D-274FFigure the amount of memory space left between HL and the free memory and get the space needed as set in Register C
2750INC HL23Next 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
2751INC HL23Bump the value of the array variables pointer in HL. These 2 INC's skip over the offset entry
2752-2754LD (40D8H),HLLD (TEMP3),HL22 D8 40Next 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
2755LD (HL),C71Save the number of dimension at the location of the array variables pointer in HL
2756INC HL23Bump the value of the array variables pointer in HL to point to the first subscript entry in the array table
2757-2759LD A,(40AEH)LD A,(DIMFLG)3A AE 40Load 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
275ARLA17Set the CARRY flag accordingly
275BLD A,C79Load Register A with the number of dimension evaluated in Register C
275C-275E- ↳ LOPPTALD BC,000BH01 0B 00Top 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-2760If 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
2761POP BCC1Get a subscript/index from the STACK and put it in BC
2762INC BC03Bump the value of the subscript in BC by one for the ZERO entry.
2763- ↳ NOTDIMLD (HL),C71Top of a loop. Save the LSB of the subscript's value in Register C at the location of the array variables pointer in HL
2764INC HL23Bump the value of the array variables pointer in HL
2765LD (HL),B70Save the MSB of the subscript's value in Register B at the location of the array variables pointer in HL
2766INC HL23Bump the value of the array variables pointer in HL
2767PUSH AFF5Save the number of dimensions evaluated in Register A (and the CARRY aflag results from DIMFLG) to the STACK
2768-276AGo multiply the size of the subscript by the value of the number type flag to determine the amount of memory necessary for the subscript
276BPOP AFF1Get the number of domensions that the CARRY FLAG (DIMFLG) from the STACK and put it in Register A
276CDEC A3DDecrement the counter of the number of dimensions to check by one
276D-276EJump back to 275CH if there are anymore subscripts to be evaluated
276FPUSH AFF5If 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
2770LD B,D42Load Register B with the MSB of the array's length in Register D
2771LD C,E4BLoad Register C with the LSB of the array's length in Register E. Now BC = size of the array in bytes
2772EX DE,HLEBSwap DE and HL so that DE now has the start of the values and HL has the end of the values
2773ADD HL,DE19Add the length of the array in HL to the value of the array variable pointer in DE
2774-2775If that addition triggered the CARRY FLAG then we are out of RAM so JUMP back to 273DH and throw a ?BS ERROR
2776-2778We now know there is room in RAM, so GOSUB to "REASON" to make sure there is room for the values
2779-277BLD (40FDH),HLLD (STREND),HL22 FD 40Update the end of storage pointer with the end of the array (held in HL).
Note: 40FDH-40FEH holds free memory pointer
277C- ↳ ZERITADEC HL2BNow we need to zero the new array. First, decrement the value of the array pointer in HL
277D-277ELD (HL),00H36 00Zero the location of the array pointer in HL
277FRST 18HCOMPARDFNow 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-2781If the NZ FLAG is set, then we have more entries to ZERO, so loop until the array has been cleared
2782INC BC03Load 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
2783LD D,A57Zero Register D
2784-2786LD HL,(40D8H)HL,(TEMP3)2A D8 40Load HL with the array variables pointer (=the number of indices).
Note: 40D8H-40D9H holds Temporary storage location
2787LD E,(HL)5ELoad Register E with the number of dimension for the array at the location of the array variables pointer in HL
2788EX DE,HLEBSwap DE and HL so that HL now holds the number of dimensions and DE holds the value of the array variables pointer
2789ADD HL,HL29Multiply the number of subscripts for the array in HL by two
278AADD HL,BC09Add 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
278BEX DE,HLEBSwap 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
278CDEC HL2BWe 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
278DDEC HL2BDecrement the value of the array variables pointer in HL
278ELD (HL),E73Save the LSB of the size of the array in Register E at the location of the array variables pointer in HL
278FINC HL23Bump the value of the array variables pointer in HL
2790LD (HL),D72Save the MSB of size of the array array in Register D at the location of the array variables pointer in HL
2791INC HL23Bump the value of the array variables pointer in HL
2792POP AFF1Get the value of the DIMFLG (i.e., the CARRY BIT) and a 0 into Register A
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- ↳ GETDEFLD B,A47Zero Register B
2796LD C,A4FZero Register C. Now BC = 0 = CURTOL
2797LD A,(HL)7ELoad Register A with the number of dimensions for the array at the location of the array variables pointer in HL
2798INC HL23Bump the value of the array variables pointer in HL to one entry past the number of dimensions
2799LD D,0E1H16 E1Z-80 Trick to hide the next instruction (POP HL) if proceeding downward
279A- ↳ INLPNMPOP HLE1Get the array variables pointer from the STACK and put it in HL
279BLD E,(HL)5ENext, 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
279CINC HL23Bump the value of the array variables pointer in HL
279DLD D,(HL)56Load Register D with the MSB of the subscript limit at the location of the array variables pointer in HL
279EINC HL23Bump the value of the array variables pointer in HL
279FEX (SP),HLE3Swap 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
27A0PUSH AFF5Save the number of dimensions for the array in Register A to the STACK
27A1RST 18HCOMPARDFNow 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-27A4If the current inex is too big, then we need to throw a ?BS ERROR via a JUMP to 273DH
27A8ADD HL,DE19Add the index to CURTOL
27A9POP AFF1Get the number of dimensions for the array from the STACK and put it in Register A
27AADEC A3DWe checked one, so cross one off the list and see if there are anymore dimensions to be evaluated
27ABLD B,H44Load Register B with the MSB of the subscript pointer in Register H
27ACLD C,L4DLoad Register C with the LSB of the subscript pointer in Register L. Now BC = CURTOL for the start of the next loop.
27AD-27AELoop back to 279AH until all of the subscripts have been evaluated
27AF-27B1LD A,(40AFH)LD A,(VALTYP)3A AF 40Get 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
27B3LD B,H
LD C,L44We are going to need to multiply by THREE, so we need to save the original value into BC as well.
27B4ADD HL,HL29Multiply the subscript pointer in HL by two
27B5-27B6SUB 04HD6 04Check the value of the number type flag in Register A because we would be done if we have an integer or a string.
27B7-27B8Jump forward to 27BDH if the current number type is an integer or string
27B9ADD HL,HL29It 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-27BBJump forward to 27C2H if the current number type is single precision as we then have enough bytes.
27BCADD HL,HL29It must be double precision, so once again multiply the subscript pointer in HL by two (so now it is multiplied by 8)
27BD- ↳ DMLVALOR AB7Set the flags
27BE-27C0Jump forward to 27C2H if the current number type isn't a string
27C1ADD HL,BC09The current number type is a string so add the value of the original subscript pointer in BC to the subscript pointer in HL
27C2- ↳ DONMULPOP BCC1Get the value of the array variables pointer from the STACK and put it in BC
27C3ADD HL,BC09Add 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
27C4EX DE,HLEBLoad DE with the variable pointer in HL
27C5-27C7- ↳ FINNNOWLD HL,(40F3H)LD HL,(TEMP2)2A F3 40Get the value of the current BASIC program pointer and put it in HL.
Note: 40F3H-40F4H is a temporary storage location
27C8RETC9RETurn 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- ↳ MEMXOR AAFZero Register A and the status flags
27CAPUSH HLE5Save the value of the current BASIC program pointer in HL to the STACK
27CB-27CDLD (40AFH),ALD (VALTYP),A32 AF 40Zero the number type flag.
NOTE:40AFH holds Current number type flag
27CE-27D0Determine how much space there is via a GOSUB to the FREroutine at 27D4H
27D1POP HLE1Get the value of the current BASIC program pointer from the STACK and put it in HL
27D2RST 10HCHRGETD7We 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.
27D3RETC9Return to BASIC
27D4-27F4 - LEVEL II BASIC FREROUTINE- "FRE"
27D4-27D6- ↳ FRELD HL,(40FDH)LD HL,(STREND)2A FD 40Load 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
27D7EX DE,HLEBLoad DE with the value of the free memory pointer in HL for subtraction
27DBADD HL,SP39Add the value in HL (which is zero) to the current value of the STACK pointer so that the STACK pointer is now in HL
27DCRST 20HGETYPEE7We 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-27DEIf that test shows we do NOT have a STRING (meaning this was really a MEMcall, jump to forward to 27ECH
27DF-27E1Free up the argument and set up to give some free string space
27E5-27E7LD HL,(40A0H)LD HL,(STKTOP)2A A0 40Load HL with the start of string space pointer / bottom of free space.
NOTE:40A0H-40A1H holds the start of string space pointer
27E8EX DE,HLEBLoad DE with the start of string space pointer in HL
27E9-27EBLD HL,(40D6H)LD HL,(FRETOP)2A D6 40Load 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- ↳ GIVDBLLD A,L7DPrepare to do HL = HL - DE. First, load Register A with the LSB of the next available location in string space pointer in Register L
27EDSUB E93Subtract 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
27EELD L,A6FLoad Register L with the LSB of the amount of string space remaining in Register A
27EFLD A,H7CLoad Register A with the MSB of the next available location in string space pointer in Register H
27F0SBC A,D9ASubtract 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
27F1LD H,A67Load Register H with the MSB of the amount of string space remaining in Register A
27F2-27F4Jump 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- ↳ POSLD A,(40A6H)LD A,(TTYPOS)3A A6 40Load Register A with the current cursor line position.
Note: 40A6H holds the current cursor line position
27F8- ↳ SNGFLTLD L,A6FLoad Register L with the value of the current cursor line position in Register A.
27F9XOR AAFZero Register A
27FA- ↳ GIVINTLD H,A67Load Register H with zero, so now HL is 00 + cursor position
27FE-2818 - LEVEL II BASIC USR(x)ROUTINE- "USRFN"
27FE-2780- ↳ USRFNGOSUB to DOS to see if DOS wants to deal with this
2801RST 10HCHRGETD7We 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-2804GOSUB to 252CH to evaluate the expression at the location of the current BASIC program pointer in HL and return with the result in ACCumulator
2805PUSH HLE5Save the value of the current BASIC program pointer in HL (=the address of the next element in the code string) to the STACK
2806-2808Load HL with the return address of 0890H which will clear the STACK before returning to BASIC
2809PUSH HLE5Save the value of the return address in HL to the STACK
280A-280CLD A,(40AFH)LD A,(VALTYP)3A AF 40Load Register A with the value of the current number type flag for the argument provided.
Note: 40AFH holds Current number type flag
280DPUSH AFF5Save the value of the current number type flag in Register A.
(02=INT, 03=STR, 04=SNG, 08=DBL)
280E-280FCP 03HFE 03Check 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-2812If the current result is a string then GOSUB to 29DAH to get the free the space and get the string address into HL
2813POP AFF1Restore the number type flag into Register A
2814EX DE,HLEBLoad DE with the (possible) value of the pointer to the string (in HL)
2815-2817LD HL,(408EH)2A 8E 40Load 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.
2818JP (HL)E9Jump 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- ↳ DOCNVFPUSH HLE5Save the pointer to the current character in the BASIC program being evaluated to the STACK
281A-281BAND 07HE6 07Mask 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-281ELoad HL with the address of the arithmetic conversion routines
281FLD C,A4FLoad Register C with the value of the number type flag in Register A.
(02=INT, 03=STR, 04=SNG, 08=DBL)
2820-2821LD B,00H06 00Zero Register B. Now BC holds 00 + type and will be the two byte offset into the table
2822ADD HL,BC09Add the offset (of BC) to the base arithmetic conversion routines, to find the right jump point
2823-2825GOSUB to 2586H to convert the current result in REG l to its proper number type
2826POP HLE1Restore the pointer to the current character in the BASIC program being evaluated to HL
2827RETC9RETurn 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- ↳ ERRDIRPUSH HLE5Save whatever was in HL to the STACK
2829-282BLD HL,(40A2H)LD HL,(CURLIN)2A A2 40Load HL with the value of the current BASIC line number (which is stored at 40A2H-40A3H).
282CINC HL23Bump 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
282DLD A,H7CLoad Register A with the MSB of the current BASIC line number in Register H
282EOR LB5Combine 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.
282FPOP HLE1Restore whatever was in HL on entry back into HL
2830RET NZC0Return 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-2832LD E,16H1E 16Load Register E with the ?ID ERRORcode
2836-2856 - STRING ROUTINE - STR$logic- "STR$"
2836-2838- ↳ STR$GOSUB to 0FBDH to convert the current result in ACCumulator to an ASCII string
2839-283B- ↳ STR$1Scan it and turn it into a string (make a temporary string work area entry)
283F-2841Load BC with a return address of 2A2BH (which cleans the STACK and then jumps to 2884H)
2842PUSH BCC5Save 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- ↳ STRCPYLD A,(HL)7ELoad Register A with the string's length at the location of the string's VARPTR in HL
2844INC HL23Bump the value of the string's VARPTR in HL
2845PUSH HLE5Save the value of the string's VARPTR in HL to the STACK
2846-2848GOSUB to 28BFH to test the remaining string area to make sure that the new string will fit
2849POP HLE1Reload HL with the string's VARPTR. This is the destination to where the string should be copied.
284ALD C,(HL)4ELoad Register C with the LSB of the string's address at the location of the string's VARPTR in HL
284BINC HL23Bump the value of the string's VARPTR in HL
284CLD B,(HL)46Load Register B with the MSB of the string's address at the location of the string's VARPTR in HL
284D-284FGOSUB to 285AH to save the string's length and the string's address at 40D3H, so as to set up DSCTMP
2850PUSH HLE5Save the pointer to STRAD2 (which is 40D3H) to the STACK
2851LD L,A6FLoad Register L with the string's length (from Register A)
2852-2854GOSUB to 29CEH to move L characters from the temp area (of BC) to the string data area (in DE)
2855POP DED1Restore the pointer to DSCTMP (40D3H) into Register Pair DE
2856RETC9RETurn to CALLer
2857-2864 - STRING ROUTINE- "STRINI"
2857-2859- ↳ STRINIGOSUB 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- ↳ STRAD2Load HL with the address of the temporary string parameter storage area.
Note: 40D3H-40D5H holds Used for temporary string VARPTR's
285D- ↳ STRAD1PUSH HLE5Save the address of the temporary string parameter area in HL to the STACK
285ELD (HL),A77Save the string's length in Register A at the location of the temporary string parameter storage pointer in HL
285F- ↳ PUTDEIINC HL23The 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
2860LD (HL),E73Save the LSB of the string's address in Register E at the location of the temporary string parameter storage pointer in HL
2861INC HL23Bump the value of the temporary string parameter storage pointer in HL
2862LD (HL),D72Save the MSB of the string's address in Register D at the location of the temporary string parameter storage pointer in HL
2863POP HLE1Get the address of the temporary string parameter storage area from the STACK and put it in HL
2864RETC9RETurn 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- ↳ STRLITDEC HL2BDecrement the value of the current BASIC program pointer in HL
2866-2867- ↳ STRLTILD B,22H06 22Load Register B with a "(which is really the end of the quote search character)
2868- ↳ STRLT3LD D,B50Load Register D with a "(which is really the ending search character)
2869- ↳ STRLT2PUSH HLE5Save 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-286BLD C,0FFH0E FFLoad Register C with a -1
286C- ↳ STRGETINC HL23Bump the value of the current BASIC program pointer in HL to skip over that initial "
286DLD A,(HL)7ELoad Register A with the character at the location of the current BASIC program pointer in HL
286EINC C0CBump the counter in Register C
286FOR AB7Check 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-2871Jump 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
2872CP DBACheck 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-2874Jump 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
2875CP BB8Check 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-2877Loop 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- ↳ STRFINCP 22HFE 22Check 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-287CIf 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)
287DEX (SP),HLE3Exchange the value of the current BASIC program pointer in HL with the string's address to the STACK
287EINC HL23Bump the string's address in HL until it points to the first character of the string
287FEX DE,HLEBLoad DE with the temporary pointer string's address in HL
2880LD A,C79Load Register A with the string's length from Register C
2881-2883GOSUB to 285AH to save the string's length and the string's address into 40D3H (i.e., DSCTMP)
2884-2886- ↳ PUTNEWLoad DE with the address of the string parameter storage area.
Note: 40D3H-40D5H holds Used for temporary string VARPTR's
2887-2888LD A,D5H3E D5This 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- ↳ PUTTMPPUSH DED5Save a pointer to the stat of the string to the STACK
2889-288BLD HL,(40B3H)LD HL,(TEMPPT)2A B3 40Load 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-288ELD (4121H),HLLD (FACLO),HL22 21 41Save 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-2890LD A,03H3E 03Load Register A with the string number type flag
2891-2893LD (40AFH),ALD (VALTYP),A32 AF 40Save the value in Register A as the current number type flag.
Note: 40AFH holds current number type flag
2897-2899Depending on how we got here, DE will be different. If the jump was into PUTTMP, then DE will NOT equal FRETOP.
289ARST 18HCOMPARDFWe 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-289DLD (40B3H),HLLD (TEMPPT),HL22 B3 40Save 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
289EPOP HLE1Get the value of the current BASIC program pointer from the STACK and put it in HL
289FLD A,(HL)7ELoad Register A with the character at the location of the current BASIC program pointer in HL
28A0RET NZC0Return 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-28A2LD E,1EH1E 1ELoad Register E with a ?ST ERRORcode
28A3-28A5Display 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- ↳ STROUIINC HL23Bump 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"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- ↳ STRPRTGOSUB to 29DAH to build a temporary string work area entry for the message pointed to by FACLO
28AD-28AFGo get the string's length in Register D and the string's address in BC
28B0INC D14Bump the value of the string's length in Register D in preparation for the following loop which starts with a DEC D
28B1- ↳ STRPR2DEC D15Top of a loop. Decrement the value of the string's length in Register D
28B2RET ZC8Return if all of the characters in the string have been sent to the current output device.
28B3LD A,(BC)0ALoad Register A with the character at the location of the string pointer in BC
28B4-28B6Go send the character in Register A to the current output device
28B7-28B8CP 0DHFE 0DCheck 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-28BBJump to 2103H if the character in Register A is a carriage return
28BCINC BC03Bump the value of the string pointer in BC
28BD-28BELoop 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- ↳ GETSPAOR AB7Make sure A is not zero. A ZERO FLAG will signal that garbage collection has happened
28C0-28C1LD C,0F1H0E F1Z-80 Trick. If passing through, the C just changes and the POP AF which follows is ignored.
28C1- ↳ TRYGI2POP AFF1Get the string's length from the STACK and put it in Register A
28C2PUSH AFF5Save the length of the string in Register A to the STACK
28C3-28C5LD HL,(40A0H)LD HL,(STKTOP)2A A0 40Load HL with the poinmter to the bottom of the string space. 40A0H-40A1H holds the start of string space pointer
28C6EX DE,HLEBMove the bottom of the string space pointer into DE. We don't care what happens to HL
28C7-28C9LD HL,(40D6H)LD HL,(FRETOP)2A D6 40Load HL with the pointer to the TOP of free space.
Note: 40D6H-40D7H holds the next available location in string space pointer
28CACPL2FComplement the string's length in Register A so that it is negative
28CBLD C,A4FThe 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-28CDLD B,0FFH06 FFLoad Register B with a -1 so that BC will be the negative length of the string
28CEADD HL,BC09Add the negative string's length in BC to the top of free space pointer in HL
28CFINC HL23Bump the value of the adjusted next available location in string space pointer in HL
28D0RST 18HCOMPARDFWe 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-28D2If 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-28D5LD (40D6H),HLLD (FRETOP),HL22 D6 40Save the value in HL as new bottom of MEMORY
28D6INC HL23Bump the value of HL to point to the string
28D7EX DE,HLEBLoad DE with the pointer to the string
28D8- ↳ PPSWRTPOP AFF1Get the string's length from the STACK and put it in Register A
28D9RETC9RETurn to CALLer
28DA-298E - STRING ROUTINE- "GARBAG"
28DA- ↳ GARBAGPOP AFF1Get the garbage collection code which was PUSHed
28DB-28DCLD E,1AH1E 1ALoad Register E with an ?OS ERRORcode
28DD-28DFIf 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
28E0CP ABFOtherwise, 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.
28E1PUSH AFF5Save the garbage collection flag to the STACK
28E2-28E4Load BC with a return address of 28C1H (which would retry allocation)
28E5PUSH BCC5Save that return address to the STACK
28E6-28E8- ↳ GARBA2LD HL,(40B1H)LD HL,(MEMSIZ)2A B1 40Load HL with the top of memory pointer.
Note: 40B1H-40B2H holds MEMORY SIZE? pointer
28E9-28EB- ↳ FNDVARLD (40D6H),HLLD (FRETOP),HL22 D6 40Save 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
28EFPUSH HLE5Save the value in HL to the STACK to indicate that we didn't find a variable on this pass
28F0-28F2LD HL,(40A0H)LD HL,(STKTOP)2A A0 40Load 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
28F3PUSH HLE5Save high address (start of string space pointer in HL) to the STACK
28F4-28F6Load HL with the start of the temporary string work area pointer.
Note: 40B5H-40D2H holds Temporary string work area
28F7
"TVAR"EX DE,HLEBLoad DE with the start of the temporary string work area pointer in HL
28F8-28FALD HL,(40B3H)LD HL,(TEMPPT)2A B3 40Load 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
28FBEX DE,HLEBExchange 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
28FCRST 18HCOMPARDFWe 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.
2900-2902Jump to 294AH to do the temporary variable garbage collection if the temporary string work area isn't empty
2903- ↳ SVARSLD HL,(40F9H)LD HL,(VARTAB)2A F9 40Load HL with the start of the simple variables pointer.
NOTE:40F9H-40FAH holds the starting address of the simple variable storage area
2906- ↳ SVAREX DE,HLEBLoad DE with the simple variables pointer in HL
2907-2909LD HL,(40FBH)LD HL,(ARYTAB)2A FB 40Load 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
290AEX DE,HLEBSwap DE and HL so that DE holds the end and HL holds the start.
290BRST 18HCOMPARDFNow 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-290DIf 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.
290ELD A,(HL)7ELoad Register A with second character of the variable name
290FINC HL23Bump the value of the simple variables pointer in HL
2910INC HL23Bump the simple variables pointer in HL
2911INC HL23Bump the value of the simple variables pointer in HL. HL should now point to the value of the variable.
2912-2913CP 03HFE 03Check 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-2915Jump to 291AH if the variable at the location of the simple variables pointer in HL isn't a string
2919XOR AAFZero Register A to indicate that we should not be skipping anything else.
291A- ↳ SKPVARLD E,A5FZero Register E
291B-291CLD D,00H16 00Zero Register D. Now DE should be the number of characters to skip.
291DADD HL,DE19Add the number of characters to skip (held in DE) to the simple variables pointer in HL
2920- ↳ ARYVA2POP BCC1Clean up the STACK
2921- ↳ ARYVAREX DE,HLEBLoad DE with the value of the array variables pointer (ARTVAR) in HL
2922-2924LD HL,(40FDH)LD HL,(STREND)2A FD 40Load HL with the end of the arrays / start of free memory pointer.
Note: 40FDH-40FEH holds Free memory pointer
2925EX DE,HLEBExchange the value of the array variables pointer in DE with the value of the free memory pointer in HL
2926RST 18HCOMPARDFWe 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-2929Jump if the array variables pointer in Register HL is the same as the start of the free memory pointer in DE
292ALD A,(HL)7ELoad Register A with the number type flag at the location of the array variables pointer in HL
292BINC HL23Bump the value of the array variables pointer in HL
292C-292EGet 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)
292FPUSH HLE5Save the pointer to the DIMS to the STACK
2930ADD HL,BC09Add the value of the offset to the next array in BC to the value of the array variables pointer in HL
2931-2932CP 03HFE 03Check 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-2934If the array being examined isn't a string then skip it via a JUMP to 2920H
2935-2937LD (40D8H),HLLD (TEMP3),HL22 D8 40Save the address of the end of the array being examined.
Note: 40D8H-40D9H holds Temporary storage location
2938POP HLE1Get the value of the array variables pointer from the STACK and put it in HL
2939LD C,(HL)4ELoad Register C with the number of subscripts for the array at the location of the array variables pointer in HL
293A-293BLD B,00H06 00Zero Register B so that BC holds the number of dimensions
293CADD HL,BC09Add the number of subscripts in the array in BC to the value of the array variables pointer in HL
293DADD HL,BC09Add 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.
293EINC HL23Bump the value of the array variables pointer in HL one more time to account for #DIMs.
293F- ↳ ARYSTREX DE,HLEBLoad DE with the value of the current position in the array variables pointer in HL
2940-2942LD HL,(40D8H)LD HL,(TEMP3)2A D8 40Load HL with the address of the end of this array.
Note: 40D8H-40D9H holds Temporary storage location
2943EX DE,HLEBSwap DE and HL so that HL now points to the current position
2944RST 18HCOMPARDFWe 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-2946If the Z FLAG is set then we are at the end of an array so JUMP to 2921H to try the next array
294A- ↳ DVAR2PUSH BCC5Save the value in BC to the STACK
294B- ↳ DVAR and DVARSXOR AAFZero Register A so we can test for a null string
294COR (HL)B6Load Register A with the length of the string at the location of the array variables pointer in HL
294DINC HL23Bump the value of the array variables pointer in HL
294ELD E,(HL)5ELoad Register E with the LSB of the string's address at the location of the array variables pointer in HL
294FINC HL23Bump the value of the array variables pointer in HL
2950LD D,(HL)56Load 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.
2951INC HL23Bump the value of the array variables pointer in HL
2952RET ZC8Return if the string's length in Register A is equal to zero
2953
2954LD B,H
LD C,L44Let BC = HL
2955-2957LD HL,(40D6H)LD HL,(FRETOP)2A D6 40Load HL with the location of the the top of string free space.
NOTE:40D6H-40D7H holds the next available location in string space pointer
2958RST 18HCOMPARDFWe 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
295ALD H,B
LD L,C60Let HL = BC
295BRET CD8If 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
295CPOP HLE1Get the return address from the STACK and put it in HL
295DEX (SP),HLE3Swap (SP) and HL so that the return address is back on the STACK and HL holds the maximum number seen
295ERST 18HCOMPARDFNow 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.
295FEX (SP),HLE3Swap (SP) and HL so that the return address is back in HL and the STACK holds the maximum number seen
2960PUSH HLE5Save the return address in HL to the STACK
2961
2962LD H,B
LD L,C60Let HL = BC
2963RET NCD0Return if the string's address in DE is below the string space pointer
2964POP BCC1Get the return address from the STACK and put it in BC
2965POP AFF1Clean up the STACK (remove the MAX SEEN)
2966POP AFF1Clean up the STACK (remove the VARIABLE POINTER)
2967PUSH HLE5Save the value of the array variables pointer in HL to the STACK
2968PUSH DED5Save the new MAX pointer in DE to the STACK
2969PUSH BCC5Save the value of the return address in BC to the STACK
296ARETC9RETurn to CALLer
If we are here, we have made one complete pass through the string variables.
296B- ↳ GRBPASPOP DED1Load DE with the address of the last string put into the temporary string work area (aka the MAX pointer)
296CPOP HLE1Get the variable pointer from the STACK and put it in HL
296DLD A,L7DLoad Register A with the LSB of the variable pointer in Register L
296EOR HB4Combine 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
296FRET ZC8If HL=0 then we are at the end of the garbage collection, so return
2970DEC HL2BDecrement the value of the temporary string work area pointer in HL, currently just past the string descriptor
2971LD B,(HL)46Load Register B with the MSB of the string's address at the location of the temporary string work area pointer in HL
2972DEC HL2BDecrement the value of the temporary string work area pointer in HL
2973LD C,(HL)4ELoad 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.
2974PUSH HLE5Save 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.
2975DEC HL2BDecrement the value of the temporary string work area pointer in HL
2976LD L,(HL)6ELoad Register L with the string's length at the location of the temporary string work area pointer in HL
2977-2978LD H,00H26 00Zero Register H so that HL is now the string's character count
2979ADD HL,BC09Add the length of the string in HL to the string's address in BC. HL now points just beyond the string.
297ALD D,B50Load Register D with the MSB of the string's address in Register B
297BLD E,C59Load Register E with the LSB of the string's address in Register C. DE now is the original pointer to the string.
297CDEC HL2BDecrement the value of the string's ending address in HL to avoid moving one beyond the string
297DLD B,H44Load Register B with the MSB of the string's ending address in Register H
297ELD C,L4DLoad Register C with the LSB of the string's ending address in Register L. BC now points to the top of the string
297F-2981LD HL,(40D6H)LD HL,(FRETOP)2A D6 40Load HL with the top of free space.
NOTE:40D6H-40D7H holds the next available location in string space pointer
2982-2984Move the string from the temporary storage location to string space
2985POP HLE1Get back the pointer to the description of the variable from the STACK and put it in HL
2986LD (HL),C71Save the LSB of the string's permanent address (held in Register C) to (HL)
2987INC HL23Bump the value of the temporary string work area pointer in HL
2988LD (HL),B70Save the MSB of the string's permanent address (held in Register C) to (HL)
2989LD L,C69Load Register L with the LSB of the string's address in Register C
298ALD H,B60Load 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.
298BDEC HL2BDecrement the string's address in HL to adjust FRETOP
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- ↳ CATPUSH BCC5Save the precedence/operator value in BC to the STACK
2990PUSH HLE5Save the value of the current BASIC program pointer in HL to the STACK
2991-2993LD HL,(4121H)LD HL,(FACLO)2A 21 41Load HL with the first string's VARPTR (from ACCumulator)
2994EX (SP),HLE3Exchange the value of the first string's VARPTR in HL with the value of the current BASIC program to the STACK
2995-2997GOSUB to 249FH to evaluate the expression at the location of the current BASIC program pointer in HL
2998EX (SP),HLE3Exchange the value of the current BASIC program pointer in HL with the value of the first string's VARPTR to the STACK
299CLD A,(HL)7ELoad Register A with the first string's length at the location of the first string's VARPTR in HL
299DPUSH HLE5Save the value of the first string's descriptor (VARPTR) in HL to the STACK
299E-29A0LD HL,(4121H)LD HL,(FACLO)2A 21 41Load HL with the second string's VARPTR in ACCumulator
29A1PUSH HLE5Save the second string's VARPTR in HL to the STACK
29A2ADD A,(HL)86Add 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-29A4LD E,1CH1E 1CLoad Register E with a ?LS ERRORcodein case the two string lengths are not less than 256.
29A5-29A7Display a ?LS ERRORmessage if the combined lengths of the strings is greater than 255
29A8-29AAGo make sure that there is enough string space for the new string
29ABPOP DED1Get the second string's VARPTR from the STACK and put it in DE
29AFEX (SP),HLE3Exchange the second string's VARPTR in HL with the first string's VARPTR to the STACK
29B3PUSH HLE5Save the value of the first string's VARPTR in HL to the STACK
29B4-29B6- ↳ INCSTRLD HL,(40D4H)LD HL,(DSCTMP+1)2A D4 40Load HL with the pointer to the first string's address
29B7EX DE,HLEBLoad DE with the first string's address in HL
29C1EX (SP),HLE3Exchange the value of the return address in HL with the value of the current BASIC program pointer to the STACK
29C2PUSH HLE5Save the value of the current BASIC program pointer in HL to the STACK
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- ↳ MOVINSPOP HLE1Load HL with the value of the return address to the STACK
29C7EX (SP),HLE3Swap (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.
29C8LD A,(HL)7ELoad Register A with the string's length at the location of the string's VARPTR in HL
29C9INC HL23Bump the value of the string's VARPTR in HL
29CALD C,(HL)4ELoad Register C with the LSB of the string's address at the location of the string's VARPTR in HL
29CBINC HL23Bump the value of the string's VARPTR in HL
29CCLD B,(HL)46Load 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.
29CDLD L,A6FLoad Register L with the string's length in Register A
29CE- ↳ MOVSTRINC L2CIncrement the value of the string's length in Register L in preparation for the next instruction which is a loop.
29CF- ↳ MOVLPDEC L2DDecrement the value of the string's length in Register L
29D0RET ZC8If L hits zero then we have moved all the characters, so RETurn
29D1LD A,(BC)0AIf 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
29D2LD (DE),A12Save the character in Register A at the location of the string storage pointer in DE
29D3INC BC03Bump the value of the string pointer in BC
29D4INC DE13Bump the value of the string storage pointer in DE
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- ↳ FRESTRGOSUB to 0AF4H to make sure that the current result in REG l is a string
29DA-29DC- ↳ FREFACLD HL,(4121H)LD HL,(FACLO)2A 21 41Load HL with the string's VARPTR in ACCumulator
29DD- ↳ FRETM2EX DE,HLEBLoad DE with the value of the string's VARPTR in HL
29DE-29E0- ↳ FRETMPCheck to see if the string is the last entry in the temporary string work area
29E1EX DE,HLEBLoad HL with the value of the string's VARPTR in DE
29E2RET NZC0Return if the string isn't the last entry in the temporary string work area
29E3PUSH DED5Save the value of the string's VARPTR in DE to the STACK
29E4LD D,B50Load Register D with the MSB of the string's address in Register B
29E5LD E,C59Load Register E with the LSB of the string's address in Register C. DE now points to the string.
29E6DEC DE1BDecrement the value of the string's address in DE
29E7LD C,(HL)4ELoad Register C with the string's length at the location of the string's VARPTR in HL
29E8-29EALD HL,(40D6H)LD HL,(FRETOP)2A D6 40Load 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
29EBRST 18HCOMPARDFWe 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-29EDJump 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
29EELD B,A47If 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
29EFADD HL,BC09Add the length of the string in BC to the next available location in string space pointer in HL
29F0-29F2LD (40D6H),HLLD (FRETOP),HL22 D6 40Save the adjusted next available location in string space pointer in HL.
Note: 40D6H-40D7H holds the next available location in string space pointer
29F3- ↳ NOTLSTPOP HLE1Get the string's VARPTR from the STACK and put it in HL
29F4RETC9RETurn 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- ↳ FRETMSLD HL,(40B3H)LD HL,(TEMPPT)2A B3 40Load HL with the temporary string work area pointer.
Note: 40B3H-40B4H holds the next available location in the temporary string work area pointer
29F8DEC HL2BDecrement the value of the temporary string work area pointer in HL which backs up two words
29F9LD B,(HL)46Load Register B with the MSB of the string's address at the location of the temporary string work area pointer in HL
29FADEC HL2BDecrement the value of the temporary string work area pointer in HL
29FBLD C,(HL)4ELoad Register C with the LSB of the string's address at the location of the temporary string work area pointer in HL
29FCDEC HL2BDecrement the value of the temporary string work area pointer in HL
29FDRST 18HCOMPARDFWe 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.
29FERET NZC0If the Z FLAG is set then we are done freeing space, so RETURN
29FF-2A01LD (40B3H),HLLD (TEMPPT),HL22 B3 40Save 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
2A02RETC9RETurn to CALLer
2A03-2A0E - LEVEL II BASIC LENROUTINE- "LEN"
2A06PUSH BCC5Save the return address of 27F8H (in BC) to the STACK
2A07-2A09- ↳ LEN1GOSUB to 29D7H to free up the temporary variable pointed to by FACLO
2A0AXOR AAFZero Register A so as to force a numeric flag
2A0BLD D,A57Zero Register D so that DE can be used when E is set.
2A0CLD A,(HL)7ELoad Register A with the string's length at the location of the string's VARPTR in HL
2A0DOR AB7Set the flags according to the string's length in Register A
2A0ERETC9RETurn to CALLer
2A0F-2A1E - LEVEL II BASIC ASCROUTINE- "ASC"
2A12PUSH BCC5Save the return address of 27F8H (in BC) to the STACK
2A13-2A15- ↳ ASC2GOSUB 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-2A18If the Z FLAG is set, then this was a null string (bad argument), so display a ?FC ERROR
2A19INC HL23Bump the value of the string's VARPTR in HL
2A1ALD E,(HL)5ELoad Register E with the LSB of the address of the string's data at the location of the string's VARPTR in HL
2A1BINC HL23Bump the value of the string's VARPTR in HL
2A1CLD D,(HL)56Load Register D with the MSB of the address of the string's data at the location of the string's VARPTR in HL
2A1DLD A,(DE)1ALoad Register A with the first character at the location of the string pointer in DE
2A1ERETC9RETurn to CALLer
2A1F-2A20- ↳ CHR$LD A,01H3E 01Load Register A with the length of the string to be created
2A21-2A23GOSUB to 2857H to save the string's length in Register A and value and set up the string's address
2A24-2A26GOSUB 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- ↳ SETSTRLD HL,(40D4H)LD HL,(DSCTMP+1)2A D4 40Load HL with the temporary string's address
2A2ALD (HL),E73Save the character in Register E at the location of the string pointer in HL
2A2B- ↳ FINBCKPOP BCC1Clean up the STACK so that the RETURN address is another one that is next in the STACK.
2A2F-2A60 - LEVEL II BASIC STRING$ROUTINE- "STRNG$"
2A2FRST 10HCHRGETD7We 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-2A31RST 08H ⇒ 28SYNCHK "("CF 28Since 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-2A34This 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
2A34DEC HL2BBackspace the code string. This is a Z-80 trick because this code was part of the above call instruction
2A35PUSH DED5Save the string's length ("N") (currently in DE) to the STACK
2A36-2A37RST 08 ⇒ 2CSYNCHK ","CF 2CNow 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-2A3ANow 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-2A3CRST 08H ⇒ 29SYNCHK ")"CF 29Now 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).
2A3DEX (SP),HLE3We 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
2A3EPUSH HLE5Save the string's length ("N") in HL to the STACK so that we can test it to make sure it is an integer
2A3FRST 20HGETYPEE7We 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.
2A42-2A44We 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-2A46Skip the next instruction (which would load the string address and 1st character) by jumping to 2A4AH
2A47-2A49- ↳ STRSTRGo get the first character in the string and return with it in Register A. This is the character that will be repeated
2A4A- ↳ CALSPAPOP DED1Get the "N" from the STACK and put it in DE (well, actually, Register E)
2A4BPUSH AFF5Save the character for the string (held in Register A) to the STACK
2A4C- ↳ SPACE2PUSH AFF5and then save it to the STACK again
2A4DLD A,E7BLoad Register A with "N" (held in Register E)
2A4E-2A4FGOSUB to 2857H to allocate N bytes in the temporary string work area
2A51LD E,A5FLoad Register E with "N" (held in Register A)
2A52POP AFF1Get the character for the string ("X") from the STACK and put it in Register A
2A53INC E1CTo set the status flags we need to increase and then decrease E. First, bump the value of the string's length in Register E
2A54DEC E1D. and then decrement the string's length in Register E
2A55-2A56If "N" was zero (so that the string is now complete), jump back to 2A2BH
2A57-2A59LD HL,(40D4H)LD HL,(DSCTMP+1)2A D4 40If 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),A77Save the character in Register A ("X") at the location of the string pointer in HL
2A5BINC HL23Bump the value of the string pointer in HL
2A5CDEC E1DDecrement the string's length in Register E
2A5D-2A5EJR NZ,2A5AH20 FBLoop back to 2A5AH to keep filling (HL) until the string of X's has been completed
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$Go check the syntax. The character at the location of the current BASIC program pointer in HL must be a )
2A64XOR AAFZero Register A because the string pointer never changes
2A65- ↳ LEFT3EX (SP),HLE3Exchange the value of the current BASIC program pointer in HL with the string's VARPTR to the STACK
2A66LD C,A4FZero Register C
2A67-2A68LD A,0E5H3E E5Z-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- ↳ LEFT2PUSH HLE5Save the value of the string's VARPTR in HL to the STACK
2A6ALD A,(HL)7ELoad Register A with the string's length at the location of the string's VARPTR in HL
2A6BCP BB8Check 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-2A6DJump to 2A70H if the new string's length in Register B is greater than the string's length in Register A
2A6ELD A,B78Load Register A with the new string's TRUNCATED length in Register B
2A6F-2A71LD DE,000EH11 0E 00Z-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
2A72PUSH BCC5Save the offset in BC to the STACK
2A76POP BCC1Get the offset from the STACK and put it in BC
2A77POP HLE1Get the string's VARPTR from the STACK and put it in HL
2A78PUSH HLE5Save the string's VARPTR in HL to the STACK
2A79INC HL23Bump HL to now point to the address of the string
2A7ALD B,(HL)46Load Register B with the LSB of the string's address at the location of the string's VARPTR in HL
2A7BINC HL23Bump the value of the string's VARPTR in HL
2A7CLD H,(HL)66Load Register H with the MSB of the string's address at the location of the string's VARPTR in HL
2A7DLD L,B68Load Register L with the LSB of the string's address in Register B
2A7E-2A7FLD B,00H06 00Zero Register B so that BC can be used
2A80ADD HL,BC09Add the string's length in BC to the string's address in HL
2A81LD B,H44Load Register B with the MSB of the string's ending address in Register H
2A82LD C,L4DLoad Register C with the LSB of the string's ending address in Register L
2A83-2A85Go save the string's length (held in A) and the string's starting address (held in DE)
2A86LD L,A6FLoad Register L with the string's length (i.e., the number of characters to move) held in Register A
2A8APOP DED1Clean up the STACK
2A91-2A99 - LEVEL II BASIC RIGHT$ ROUTINE- "RIGHT$"
2A91-2A93Go check the syntax. The character at the location of the current BASIC program pointer in HL must be a )
2A94POP DED1Get the string's VARPTR from the STACK and put it in DE
2A95PUSH DED5Save the string's VARPTR in DE to the STACK
2A96LD A,(DE)1ALoad Register A with the string's length (held at the location of the string's VARPTR in DE)
2A97SUB B90Subtract the new string's length in Register B from the string's length in Register A to isolate the number of bytes
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,HLEBLoad HL with the value of the current BASIC program pointer (held in DE)
2A9BLD A,(HL)7ELoad Register A with the terminal character, currently held at the location of the current BASIC program pointer in HL
2A9C-2A9EGOSUB to 2AE2H to get the offset in Register B and the string's VARPTR in DE
2A9FINC B04We 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
2AA0DEC B05... and then we decrement the value of the string's offset position in Register B
2AA4PUSH BCC5Save the value of the offset position (held in Register B) to the STACK
2AA5-2AA6LD E,0FFH1E FFLoad Register E with the default string's length of 256 in case no number of bytes are given
2AA7-2AA8CP 29HCP ")"FE 29More 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-2AAAJump to 2AB0H if the character at the location of the current BASIC program pointer in Register A is a )
2AAB-2AACRST 08H ⇒ 2CSYNCHK ","CF 2CIf 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-2AAFGo 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-2AB1RST 08H ⇒ 29SYNCHK ")"CD 29Since 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).
2AB2POP AFF1Get the offset from the STACK and put it in Register A
2AB3EX (SP),HLE3Exchange the value of the current BASIC program pointer in HL with the value of the string's VARPTR to the STACK
2AB4-2AB6Load BC with 2A69H as the return address (which is in the LEFT$routine)
2AB7PUSH BCC5Save the return address in BC to the STACK
2AB8DEC A3DDecrement the value of the requested offset in Register A so that we have a starting position minus 1
2AB9CP (HL)BECompare 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-2ABBLD B,00H06 00Zero Register B
2ABCRET NCD0If the offset pointer ispast the end of the string we are going to return a null
2ABDLD C,A4FLoad Register C with the offset in Register A
2ABELD A,(HL)7ELoad Register A with the string's length at the location of the string's VARPTR in HL
2ABFSUB C91Subtract the index (the second argument) in Register C from the string's length in Register A
2AC0CP EBBCompare 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.
2AC1LD B,A47Load Register B with the calculated string's length in Register A
2AC2RET CD8If we are not going to truncate, then just use the partial string we already have and Return to 2A69H
2AC3LD B,E43Load Register B with the new string's truncated length in Register E
2AC4RETC9Return to 2A69H aka LEFT2
2AC5-2ADE - LEVEL II BASIC VALROUTINE- "VAL"
2AC5-2AC7- ↳ VALGo get the string's length in Register A and the string's VARPTR in HL
2AC8-2ACAJump to 27F8H if the string's length in Register A is equal to zero
2ACBLD E,A5FLoad 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.
2ACCINC HL23Bump the value of the string's VARPTR in HL
2ACDLD A,(HL)7ELoad Register A with the LSB of the string's address at the location of the string's VARPTR in HL
2ACEINC HL23Bump the value of the string's VARPTR in HL
2ACFLD H,(HL)66Load Register H with the MSB of the string's address at the location of the string's VARPTR in HL
2AD0LD L,A6FLoad Register L with the LSB of the string's address in Register A
2AD1PUSH HLE5Save the value of the string's address in HL to the STACK
2AD2ADD HL,DE19Add the string's length in DE to the string's address in HL
2AD3LD B,(HL)46Load Register B with the last character of the string at the location of the string pointer in HL
2AD4LD (HL),D72Save the zero in Register D at the location of the string pointer in HL
2AD5EX (SP),HLE3Exchange the string's ending address in HL with the string's address to the STACK
2AD6PUSH BCC5Save the last character of the string in Register B to the STACK
2AD7LD A,(HL)7ELoad Register A with the first character of the argument
2AD8-2ADACall 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)
2ADBPOP BCC1Get the modified character of the next string into Register B
2ADCPOP HLE1Get the pointer to the modified character back into HL
2ADDLD (HL),B70Restore the character.
2ADERETC9RETurn 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- ↳ PREAMEX DE,HLEBLoad HL with the value of the current BASIC program pointer in DE
2AE0-2AE1RST 08H ⇒ 29SYNCHK ")"CF 29Since 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- ↳ PREAM2POP BCC1Get the return address from the STACK and put it in BC
2AE3POP DED1Get the number of bytes to isolate from the string (from the STACK) and put it in DE
2AE4PUSH BCC5Save the return address in BC to the STACK
2AE5LD B,E43Load Register B with the number of bytes in Register E
2AE6RETC9RETurn to CALLer
2AE7H-2AEE - Process a LEFT-HAND-SIDE MID$- "ISMID$"
2AE7-2AE8- ↳ ISMID$CP 7AHCP MIDTK-$ENDFE 7AThis 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.
2AEF-2AF7 - LEVEL II BASIC INPROUTINE- "FNINP"
2AEF-2AF1- ↳ FNINPGo evaluate the expression at the location of the current BASIC program pointer in HL and return with the port number in Register A
2AF2-2AF4LD (4094H),ALD (STAINP+1),A32 94 40Save the value of the port number (from Register A) into 4094H, which is in the middle of a routine.
2AF8-2B00 - LEVEL II BASIC OUTROUTINE- "FNOUT"
2B01-2B0D - EVALUATE EXPRESSION ROUTINE- "GETINT"
This evaluates an expression and leaves the result in DE as an integer.
2B01- ↳ GETINTRST 10HCHRGETD7We 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- ↳ GETIN2Go 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- ↳ INTFR2PUSH HLE5Save the value of the current BASIC program pointer in HL to the STACK
2B06-2B08Call 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)
2B09EX DE,HLEBLoad DE with the integer result in HL
2B0APOP HLE1Get the value of the current BASIC program pointer from the STACK and put it in HL
2B0BLD A,D7ALoad Register A with the MSB of the integer result in Register D
2B0COR AB7Test the value of the MSB in Register A
2B0DRETC9RETurn to CALLer
2B0E-2B16 - EVALUATE EXPRESSION ROUTINE - OUTcontinues here- "SETIO"
2B0E-2B10- ↳ SETIOGOSUB 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-2B13LD (4094H),ALD (STAINP+1),A32 94 40Save the 8-bit value in Register A in the DOS address of 4094H to set up for WAIT
2B14-2B16LD (4097H),ALD (OUTWRD+1),A32 97 40Save 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-2B18RST 08H ⇒ 2ESYNCHK ","CF 2ESince 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).
2B1B-2B28 - EVALUATE EXPRESSION ROUTINE - This is called by PRINT TAB- "GTBYTC".
2B1B- ↳ GTBYTCRST 10HCHRGETD7We 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- ↳ GETBYTGOSUB 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- ↳ CONINTGOSUB 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-2B24If the result is greater than 255, display a ?FC ERRORmessage
2B25DEC HL2BDecrement the value of the current BASIC program pointer in HL
2B26RST 10HCHRGETD7We 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.
2B27LD A,E7BLoad Register A with the 8-bit result in Register E so that both A and E have the result.
2B28RETC9RETurn 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- ↳ LLISTLD A,01H3E 01Load Register A with the printer output device code
2B2B-2B2DLD (409CH),ALD (PRTFLG),A32 9C 40Save 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- ↳ LISTPOP BCC1Get rid of the the return address on the STACK
2B2F-2B31Go evaluate the range of line numbers given at the location of the current BASIC program pointer in HL
2B32PUSH BCC5Save the address of the first BASIC line (held in BC) to the STACK
2B33-2B35- ↳ LIST4LD HL,FFFFH21 FF FFLoad 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-2B38LD (40A2H),HLLD (CURLIN),HL22 A2 40Save the value in HL as the current BASIC line number (which is stored at 40A2H-40A3H).
2B39POP HLE1Get the address of the first BASIC line to be listed (from the STACK) and put it in HL
2B3APOP DED1Get the value of the last BASIC line number to be listed (from the STACK) and put it in DE
2B3BLD C,(HL)4ELoad Register C with the LSB of the next BASIC line pointer at the location of the memory pointer in HL
2B3CINC HL23Bump the value of the memory pointer in HL
2B3DLD B,(HL)46Load Register B with the MSB of the next BASIC line pointer at the location of the memory pointer in HL
2B3EINC HL23Bump the value of the memory pointer in HL
2B3FLD A,B78Load Register A with the MSB of the next BASIC line pointer in Register B
2B40OR CB1Combine 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-2B43If we are at the elast line, then STOP and JUMP to 1A19H to the READY PROMPT.
2B47-2B49Go scan the keyboard to see if the BREAKkey or the shift-@key was pressed
2B4APUSH BCC5Save the address of the next BASIC line in BC to the STACK
2B4BLD C,(HL)4EWe 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
2B4CINC HL23Bump the value of the memory pointer in HL
2B4DLD B,(HL)46Load Register B with the MSB of the BASIC line number at the location of the memory pointer in HL
2B4EINC HL23Bump the value of the memory pointer in HL
2B4FPUSH BCC5Save the BASIC line number in BC to the STACK
2B50EX (SP),HLE3Swap (SP) and HL so that the line number is now in HL
2B51EX DE,HLEBSwap DE and HL so that the last BASIC line number is now in HL
2B52RST 18HCOMPARDFWe 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.
2B53POP BCC1Get the pointer to the location on the BASIC program line being processed and put it in BC
2B54-2B56If 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!
2B57EX (SP),HLE3Swap (SP) and HL so that the last BASIC line number is now on the STACK
2B58PUSH HLE5Save the address of the next BASIC line in HL to the STACK
2B59PUSH BCC5Save the pointer to the location on the BASIC program line being processed to the STACK
2B5AEX DE,HLEBLoad HL with the BASIC line number (from DE)
2B5B-2B5DLD (40ECH),HLLD (DOT),HL22 EC 40Save the BASIC line number in HL into DOT for use later in EDIT or LIST.
Note: 40ECH-40EDH holds EDIT line number
2B5E-2B60Call 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-2B62LD A,20H3E 20Load Register A with a space
2B63POP HLE1Get the value of the memory pointer from the STACK and put it in HL
2B67-2B69GOSUB 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-2B6CLD HL,(40A7H)LD HL,(BUFPNT)2A A7 40Load HL with the starting address of the input buffer.
Note: 40A7H-40A8H holds the input Buffer pointer
2B6D-2B6FSince 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
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)7ELoad Register A with the character at the location of the memory pointer in HL
2B76OR AB7Check to see if the character in Register A is an end of the string character (00H)
2B77RET ZC8Return if the character in Register A is an end of the string character
2B78-2B7AGo send the character in Register A to the current output device
2B7BINC HL23Bump the value of the memory pointer in HL
2B7C-2B7DLoop 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- ↳ BUFLINPUSH HLE5Save the BASIC line pointer in HL to the STACK
2B7F-2B81LD HL,(40A7H)LD HL,(BUFPNT)2A A7 40Load HL with the starting address of the input buffer.
Note: 40A7H-40A8H holds the input Buffer pointer
2B82
2B83LD B,H
LD C,L44LET Register Pair BC = Register Pair HL
2B84POP HLE1Get the value of the BASIC line pointer from the STACK and put it in HL
2B85JUMP 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.
2B88NOP
2B89- ↳ PLOOPINC BC03Top of a loop. Bump the value of the input buffer pointer in BC
2B8ADEC D15Decrement the character count in Register D
2B8BRET ZC8Return if 256 characters have been moved into the input buffer
2B8CINC HLMove one byte forward in the text.
2B8DLD A,(HL)Load register A with the character at the location of the BASIC line pointer in HL.
2B8EOR ASet the status flags to enable us to check to see if the character in register A is an end of the BASIC line character.
2B8FLD (BC),A02Save 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.
2B90RET ZC8Return if the character in Register A is an end of the BASIC line character
2B94CP FBHCheck to see if the character in register A is a ' token.
2B96-2B97Jump 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-2B9BDEC BC
DEC BC
DEC BC
DEC BC0BFirst, 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-2B9FINC D
INC D
INC D
INC D14Then, 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- ↳ NRQTTKCP 95HCP $ELSEFE 95Check 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-2BA4If 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-2BA6SUB 7FHD6 7FNext, 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
2BA7PUSH HLE5Save the value of the BASIC line pointer in HL to the STACK. Register L holds the reserved word number at this point.
2BA8LD E,A5FLoad Register E with the character in Register A
2BA9-2BABLoad HL with the starting address of the reserved words list
2BAC- ↳ LOPRESLD A,(HL)7ELoad Register A with the character at the location of the reserved words list pointer in HL
2BADOR AB7Test 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.
2BAEINC HL23Bump the value of the reserved words list pointer in HL
2BAF-2BB1If the character at the location of the reserved words pointer in Register A doesn't have bit 7 set then Jump back to 2BACH.
2BB2DEC E1DDecrement the counter
2BB3-2BB4Jump back to 2BACH if this isn't the reserved word for the token
2BB5-2BB6AND 7FHE6 7FReset 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- ↳ MORPURLD (BC),A02Save the character in Register A at the location of the input buffer pointer in BC
2BB8INC BC03Bump the value of the input buffer pointer in BC
2BB9DEC D15Decrement the value of the character counter in Register D
2BBA-2BBCIf 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
2BBDLD A,(HL)7ELoad Register A with the character at the location of the reserved words pointer in HL
2BBEINC HL23Bump the reserved words pointer in HL
2BBFOR AB7Test the value of the character in Register A
2BC0-2BC2Keep getting characters in this reserved word until we hit the next reserved word
2BC3POP HLE1Get the value of the BASIC line pointer from the STACK and put it in HL
2BC4-2BC5Jump 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- ↳ DELETEGOSUB to 1B10H to evaluate the line numbers at the location of the current BASIC program pointer in HL
2BC9POP DED1Get the value of the last BASIC line number to be deleted (in binary) from the STACK and put it in DE
2BCAPUSH BCC5Save the address of the first BASIC line to be deleted in BC to the STACK
2BCBPUSH BCC5Save the address of the first BASIC line to be deleted in BC to the STACK AGAIN!
2BCC-2BCEGOSUB 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-2BD0Since 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
2BD2LD D,H
LD E,L54Let Register Pair DE = Register Pair HL
2BD3EX (SP),HLE3Exchange the last BASIC line's address in HL with the first BASIC line's address to the STACK
2BD4PUSH HLE5Save the pointer to the first line in range to the STACK
2BD5RST 18HCOMPARDFWe 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- ↳ FCERRGDisplay 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
2BDC-2BDECall the WRITE MESSAGE routine at 28A7H to print the message pointed to by HL.
2BDFPOP BCC1Get the first BASIC line's address from the STACK and put it in BC
2BE3EX (SP),HLE3Swap (SP) and HL so that HL now points to the next BASIC line's address ...
2BE4- ↳ DELEX DE,HLEBand then put it into DE
2BE5-2BE7LD HL,(40F9H)LD HL,(VARTAB)2A F9 40Load HL with the end of the BASIC program pointer.
- Note: 40F9H-40FAH holds the starting address of the simple variable storage area.
2BE8- ↳ MLOOPLD A,(DE)1ALoad Register A with the character at the location of the memory pointer in DE
2BE9LD (BC),A02Save the character in Register A at the location of the memory pointer in BC
2BEAINC BC03Bump the value of the memory pointer in BC
2BEBINC DE13Bump the value of the memory pointer in DE
2BECRST 18HCOMPARDFNow 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-2BEELoop back to 2BE8H until the memory pointer in DE equals the end of the BASIC program pointer in HL
2BEFLD H,B60Load Register H with the MSB of the memory pointer in Register B
2BF0LD L,C69Load Register L with the LSB of the memory pointer in Register C
2BF1-2BF3LD (40F9H),HLLD (VARTAB),HL22 F9 40Save 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.
2BF4RETC9RETurn 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- ↳ CSAVECalls the WRITE LEADER routine at 0284H (which writes a Level II leader on the cassette unit set in Register A)
2BF82BFAH 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
2BFBPUSH HLE5Save 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
2BFF-2C00LD A,D3H3E D3Load Register A with the filename header byte (=D3H which is a "S" with the sign bit on)
2C01-2C03the 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
2C07LD A,(DE)1ALoad Register A with the first character of the filename at the location of the filename pointer in DE
2C08-2C0Athe 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-2C0DLD HL,(40A4H)LD HL,(TXTTAB)2A A4 40Load 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).
2C0EEX DE,HLEBLoad DE with the start of the BASIC program pointer in HL
2C0F-2C11LD HL,(40F9H)LD HL,(VARTAB)2A F9 40Load HL with the end of the BASIC program pointer.
- Note: 40F9H-40FAH holds the starting address of the simple variable storage area.
2C12- ↳ LOPSCOLD A,(DE)1ATop 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
2C13INC DE13Bump the value of the memory pointer in DE
2C14-2C16the 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)
2C17RST 18HCOMPARDFNow 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-2C19Loop back to 2C12H until the memory pointer in DE is equal to the end of the BASIC program pointer in HL
2C1DPOP HLE1Get the value of the current BASIC program pointer from the STACK and put it in HL
2C1ERETC9RETurn to CALLer
2C1F-2CA4 - LEVEL II BASIC CLOAD ROUTINE - ROM v1.0- "CLOAD"
2C22LD A,(HL)7ELoad Register A with the character at the location of the current BASIC program pointer in HL
2C23-2C24SUB 0B2HSUB $PRINTD6 B2Check 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-2C26Jump to the CLOAD?routine at 2C29H if the character at the location of the current BASIC program pointer in Register A is a ?
2C27XOR AAFOK - So this is now a straight CLOAD. First, zero Register A
2C28LD BC,232F01 2F 23Z-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- ↳ CLOADPCPL2FLoad Register A with a -1 for CLOAD?. It will still be a 0 if this is CLOAD
2C2AINC HL23Bump the value of the current BASIC program pointer in HL until it points to the next character after the ?in CLOAD?
2C2BPUSH AFF5Save the CLOAD/CLOAD?flag in Register A to the STACK
2C2CDEC HL2BDecrement the value of the current BASIC program pointer in HL so we can see if we are at the end
2C2DRST 10HCHRGETD7We 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-2C2FLD A,00H3E 00Zero Register A to allow for any filename
2C30-2C31Jump if the character at the location of the current BASIC program pointer in HL is an end of the BASIC statement character
2C32-2C34To 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-2C37Make sure the length is good, and save the pointer to the filename to Register Pair DE
2C38LD A,(DE)1ALoad Register A with the first character of the filename at the location of the filename pointer in DE
2C39- ↳ CLNONMLD L,A6FLoad Register L with the filename in Register A
2C3APOP AFF1Get the value of the CLOAD/CLOAD?flag from the STACK and put it in Register A
2C3BOR AB7Test the value of the CLOAD/CLOAD?flag in Register A (since CPL doesn't set any flags)
2C3CLD H,A67Load Register H with the value of the CLOAD/CLOAD?flag in Register A
2C3D-2C3FLD (4121H),HLLD (FACLO),HL22 21 41Save the value of the CLOAD/CLOAD?flag and the filename in HL in ACCumulator
2C43-2C45LD HL,(4121H)LD HL,(FACLO)2A 21 41Load HL with the CLOAD/CLOAD?flag and the filename in ACCumulator
2C46EX DE,HLEBLoad D with the CLOAD/CLOAD? flag and load Register E with the filename
2C47-2C48- ↳ LOPCLKLD B,03H06 03Load Register B with the number of header bytes
2C49-2C4B- ↳ LOPCL2Calls 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-2C4DSUB 0D3HD6 D3Check to see if the character in Register A is a filename header byte
2C4E-2C4FLoop if the character in Register A isn't a filename header byte
2C50-2C51Loop back to 2C49H until three filename header bytes have been read
2C52-2C54Now 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)
2C55INC E1CWe 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
2C56DEC E1DDecrement the value of the filename in Register E
2C57-2C58Jump to 2C5CH (to pretend the filename matched) if no filename was specified
2C59CP EBBIf 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-2C5BJump 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- ↳ NONAMCLD HL,(40A4H)LD HL,(TXTTAB)2A A4 40If 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- ↳ DOCRSLD B,03H06 03Load Register B with the number of zeros to look for to stop the load (which is 3)
2C61-2C63- ↳ DOCSMRCalls 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)
2C64LD E,A5FPreserve the character that was just read from tape into Register E
2C65SUB (HL)96Compare 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
2C66AND DA2Combine 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.
2C69LD (HL),E73At 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-2C6CMake sure there is more room, and toss a ?OM ERRORif there isn't.
2C6DLD A,(HL)7ELoad Register A with the character at the location of the memory pointer in HL
2C6EOR AB7Check to see if the byte just read in Register A is equal to zero
2C6FINC HL23Bump the value of the memory pointer in HL
2C70-2C71Loop if the byte in Register A isn't equal to zero (meaning that it isn't end of program or end of statement)
2C72-2C74Call the BLINK ASTERISK routine at 022CH which alternatively displays and clears an asterisk in the upper right hand corner of the video display
2C75-2C76Do that loop until three zeros in a row have been read from the cassette recorder
2C77-2C79LD (40F9H),HLLD (VARTAB),HL22 F9 40By 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-2C7CGOSUB 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.
2C80-2C82We 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-2C85LD HL,(40A4H)LD HL,(TXTTAB)2A A4 40Load 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).
2C86PUSH HLE5Save the start of the BASIC program pointer in HL to the STACK. FINI will need this value there.
2C8A-2C8CGOSUB 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-2C8FCall 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-2C92JP 1A18JP STPRDYC3 18 1AJUMP to STPRDY to pop NEWSTT from the STACK and then fall into the READY routine
2C93-2C95- ↳ SKPFILLD (3C3EH),A32 3E 3CGo display the filename on the video display
2C96-2C97- ↳ ZERSRFLD B,03H06 03Load Register B with the number of zeros to be found to stop the search
2C98-2C9A- ↳ GETCHZCalls 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)
2C9BOR AB7Check to see if the character in Register A is equal to zero
2C9E-2C9FLoop until three zeros in a row have been read from the cassette recorder
2CA0-2CA2Calls 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)
2CA5-2CA9 - MESSAGE STORAGE LOCATION- "NOOKCS"
2CA5-2CA9- ↳ NOOKCS"BAD" + 0DH + 00H42The 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- ↳ PEEKCall 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)
2CADLD A,(HL)7ELoad Register A with the value at the location of the memory pointer in HL
2CAE-2CB0Go save the 8-bit value in Register A as the current result in ACCumulator
2CB1-2CBC - LEVEL II BASIC POKEROUTINE- "POKE"
2CB1-2CB3- ↳ POKEGo evaluate the expression at the location of the current BASIC program pointer in HL and return with the integer result in DE
2CB4PUSH DED5Save the address the user wants to POKE to (held in DE) to the STACK
2CB5-2CB6RST 08H ⇒ 2ESYNCHK ","CF 2ESince 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-2CB9GOSUB 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
2CBAPOP DED1Get the address the user wants to POKE to from the STACK and put it in DE
2CBBLD (DE),A12Save the value the user wanted to poke (held in Register A) in the location that the user wants to POKE to (held in DE)
2CBCRETC9RETurn 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- ↳ PRINUSGo evaluate the string expression at the location of the current BASIC program pointer in HL 2CC0-2CC2Go make sure the expression that was just evaluated was a string 2CC3-2CC4RST 08H ⇒ 3BSYNCHK ";"CF 3BSince 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).
2CC5EX DE,HLEBSwap DE and HL so that DE now holds the current BASIC program pointer 2CC6-2CC8LD HL,(4121H)LD HL,(FACLO)2A 21 41Load HL with the USING string's VARPTR 2CCB-2CCD- ↳ REUSSTLD A,(40DEH)LD A,(FLGINP)3A DE 40Load 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. 2CCEOR AB7Check to see if that flag indivates that we did, or did not, print out a value last time. 2CCF-2CD0If we did not print out a value last time, we have an error, so JUMP down to 2CDDH 2CD1POP DED1Restore the pointer to the "USING" string decription from the STACK into DE 2CD2EX DE,HLEBSwap 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- ↳ INIUSPUSH HLE5Save the pointer to the "USING" string descriptor (i.e., the USING string's VARPTR) in HL to the STACK 2CD4XOR AAFZero Register A and all the flags. 2CD5-2CD7LD (40DEH),ALD (FLGINP),A32 DE 40Clear the flag we are using to see if we printed the values or not. 2CD8CP DBATurn 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. 2CD9PUSH AFF5Save the flag indicating if the value list has ended or not to the STACK 2CDAPUSH DED5Save the pointer into the value list to the STACK 2CDBLD B,(HL)46Load Register B with the USING string's length 2CDCOR BB0Check to see if the USING string's length in Register B is equal to zero 2CDD-2CDF- ↳ FCERR3If the USING string is NULL then display a ?FC ERROR 2CE0INC HL23Bump the pointer to the USING string's data in HL by 1 2CE1LD C,(HL)4ELoad Register C with the LSB of the USING string's address at the location of the USING string's VARPTR in HL 2CE2INC HL23Bump the value of the USING string's VARPTR in HL 2CE3LD H,(HL)66Load Register H with the MSB of the USING string's address at the location of the USING string's VARPTR in HL 2CE4LD L,C69Load Register L with the LSB of the USING string's address in Register C
2CE7- ↳ BGSTRFLD E,B58Load Register E with the USING string's length 2CE8PUSH HLE5Save the pointer to the USING string pointer in HL to the top of the STACK 2CE9-2CEALD C,02H0E 02Since the \\ string field length is two plus number of enclosed spaces, add two 2CEB- ↳ LPSTRFLD A,(HL)7ELoad Register A with the character at the location of the USING string pointer in HL 2CECINC HL23Bump the value of the USING string data pointer in HL 2CED-2CEECP 25HCP CSTRNGFE 25Check 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-2CF1If it is a "%" then JUMP to 2E17H to evaluate a string and print 2CF2-2CF3CP 20HFE 20Check 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-2CF5If the character is not a field extender, then it isn't a string field, so JUMP down a few opcodes to 2CF9H 2CF6INC C0CIncrement the field width (tracked in in Register C) 2CF7-2CF8Decrement 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- ↳ NOSTRFPOP HLE1Restore the pointer to the "USING" string's data into Register Pair HL
2CFALD B,E43Load Register B with the USING string's length
2CFB-2CFCLD A,25HLD A,CSTRNG3E 25Restore 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- ↳ NEWUCHIf a +came before the character, make sure to print it
2D00-2D02Once that has been printed, now we print the character in Register A since we know it isn't part of a field
2D03- ↳ PRCCHRXOR AAFWe 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
2D04LD E,A5FZero Register E
2D05LD D,A57Zero Register D
2D06-2D08- ↳ PLSFINGo print a leading +if necessary (i.e., to allow for multiple plusses)
2D09LD D,A57Set 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.
2D0ALD A,(HL)7ELoad Register A with the next field description character in the USING string
2D0BINC HL23Bump the value of the USING string pointer in HL
2D0C-2D0DCP 21HCP "!"FE 21Check 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.
2D11-2D12CP 23HCP "#"FE 23Check 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.
2D15DEC B05Since every other possibility is actually a two character field, decrement the value of the string's length in Register B onem ore time
2D16-2D18If 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-2D1ACP 2BHCP "+"FE 2BCheck 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-2D1CLD A,08H3E 08Set 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
2D1FDEC HL2BDecrement the value of the USING string pointer so we can re-get the character.
2D20LD A,(HL)7ELoad Register A with the (current) character at the location of the USING string pointer in HL
2D21INC HL23Bump the value of the USING string pointer in HL
2D22-2D23CP 2EHCP "."FE 2ECheck 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-2D25Jump if the character in Register A is a .to scan with Register E holding the number of digits before the "." as 0
2D26-2D27CP 25HCP CSTRNGFE 25Check 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-2D29Jump to see if it is really a string field if the character in Register A is a %
2D2ACP (HL)BECheck 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-2D2CIf the NZ flag is set, then we can't have a $$or a **, so all remaining possibilities are exhausted, so JUMP to NEWUCH
2D2D-2D2ECP 24HCP "$"FE 24Check 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.
2D31-2D32CP 2AHCP "*"FE 2ACheck 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-2D34If 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
2D35LD A,B78Prepare 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-2D37CP 02HFE 02Check 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.
2D38INC HL23Bump the value of the USING string pointer in HL
2D39-2D3AJump to 2D3EH if the USING string's length in Register A isn't at least two
2D3BLD A,(HL)7ELoad Register A with the character at the location of the USING string pointer in HL
2D3C-2D3DCP 24HCP "$"FE 24Check 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- NOTSPCLD A,20H3E 20Set the *bit in Register A
2D40-2D41If we did not ultimately get a **$then JUMP (noting we do NOT set the dollar sign flag)
2D42DEC B05Decrement the value of the USING string's length to take the $into account
2D43INC E1CBump the field width tracker to account for the floating dollar sign
2D44-2D45CP 0AFHFE AFZ-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- ↳ DOLRNMXOR AAFThis is $processing for PRINT USING. Clear Register A.
2D46-2D47ADD A,10HC6 10Mask Register A to set the bit for a floating dollar sign flag.
2D48INC HL23Bump the value of the USING string pointer in HL to go past the special characters
2D49- ↳ SPCNUMINC E1CSince two characters specify the field size, start off with E=1
2D4AADD A,D82Combine the bits in Register D into the flag tracker
2D4BLD D,A57Preserve the modified flag tracker into Register D.
2D4C- ↳ NUMNUMINC E1CBump the number of characters to the left of the decimal point in Register E
2D4D-2D4ELD C,00H0E 00Set the number of digits to the right of the decimal point (tracked in Register C) to 0
2D4FDEC B05Check to see if there are any more characters by decrementing the value of the string's length in Register B
2D50-2D51If 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.
2D52LD A,(HL)7ELoad Register A with the next character at the location of the USING string pointer in HL
2D53INC HL23Bump the value of the USING string pointer in HL
2D54-2D55CP 2EHCP "."FE 2ECheck 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-2D57If yes, then need to use a special scan loop to scan after the decimal point, so JUMP to AFTDOT
2D58-2D59CP 23HCP "#"FE 23Check 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.
2D5C-2D5DCP 2CHCP ","FE 2CCheck 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-2D5FIf there is no comma, then JUMP to FINNUM because there are no more leading digits and we need to check for "^^^"
2D60LD A,D7AIf we are here, then a comma was requested. Turn on the COMMA bit
2D61-2D62OR 40HF6 40Mask the flag in Register A for ,
2D63LD D,A57Load Register D with the value of the flag in Register A
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- ↳ DOTNUMLD A,(HL)7ELoad Register A with the next character of the USING string
2D67-2D68CP 23HFE 23Check 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-2D6ALD A,2EHLD A,"."3E 2ELoad Register A with a decimal point
2D6B-2D6CIf it isn't a "." then JUMP AWAY to NEWUCH with A holding a "." so that a "." will get printed
2D6D-2D6ELD C,01H0E 01If 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
2D6FINC HL23Bump the value of the USING string pointer in HL
2D70- ↳ AFTDOTINC C0CBump the number of digits to the right of the decimal point (tracked in Register C)
2D71DEC B05Decrement the value of the USING STRING's length to test to see if there are more characters
2D72-2D73If the USING string length is now ZERO, JUMP to ENDNUS to stop scanning
2D74LD A,(HL)7ELoad Register A with the character at the location of the USING string pointer in HL
2D75INC HL23Bump the value of the USING string pointer in HL
2D76-2D77CP 23HFE 23Check 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-2D79If 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- ↳ FINNUMPUSH DED5Save the value of the flag (tracked in D) and the number of leading digits (tracked in E) to the STACK
2D7B-2D7DLoad DE with the return address in case this is not a scientific notation
2D7EPUSH DED5Save the value of the return address in DE to the STACK
2D7F
2D80LD D,H
LD E,L54Let DE = HL in case we need to rememer HL
2D81-2D82CP 5BHFE 5BCheck 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.
2D83RET NZC0Return if the character in Register A isn't an up arrow
2D84CP (HL)BECheck 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.
2D85RET NZC0Return 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)
2D86INC HL23Bump the value of the USING string pointer in HL
2D87CP (HL)BECheck 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.
2D88RET NZC0Return 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)
2D89INC HL23Bump the value of the USING string pointer in HL
2D8ACP (HL)BECheck 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.
2D8BRET NZC0Return 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)
2D8CINC HL23Bump the value of the USING string pointer in HL. If we are here we have a #.^^^^format
2D8DLD A,B78Now 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-2D8FSUB 04HD6 04Check to see if there are at least 4 characters left in the USING string
2D90RET CD8Return to 2D97 if there aren't at least four characters left in the USING string
2D91POP DED1If there are at least 4 characters left, then clean up the STACK by removing the NOTSCI return address
2D92POP DED1Get the flag and the count of the characters to the left of the decimal point from the STACK and put it in DE
2D93LD B,A47Load Register B with the new USING string's length in Register A
2D94INC D14Set the exponential notation flag (tracked in Register D)
2D95INC HL23Bump the value of the USING string pointer in HL
2D96JP Z,0D1EBHCA EB D1Z-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
2D98POP DED1(Ignored if passing through) Restore the flags into Register D and the number of leading digits into Register E
2D99- ↳ ENDNUSLD A,D7AWe 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
2D9ADEC HL2BDecrement the value of the USING string pointer in HL
2D9BINC E1CBump the number of characters to the left of the decimal point in Register E to take into account the leading plus
2D9C-2D9DAND 08HE6 08Mask Register A to NOT check for a trailing sign
2D9E-2D9FIf that AND leaves us with a NZ, then we are all done with the field, so JUMP to ENDNUM
2DA0DEC E1DOtherwise, 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
2DA1LD A,B78Check to see if there are more characters by first loading Register A with the USING string's length from Register B
2DA2OR AB7Check to see if this is the end of the USING string
2DA3-2DA4If we are out of characters, then we are all done, so JUMP to ENDNUM
2DA5LD A,(HL)7EIf there ARE more characters, then fill Register A with the character at the location of the USING string pointer in HL
2DA6-2DA7SUB 2DHSUB "-"D6 2DCheck to see if the character in Register A is a -(i.e., a trailing minus)
2DAA-2DABCP 0FEHCP "+" - "-"FE FECheck 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.
2DAE-2DAFLD A,08H3E 08If we are here then we did have a trailing "+" so first set the flag for a POSITIVE "+"
2DB0-2DB1- ↳ SGNTRLADD A,04HC6 04Then set the flag for a trailing sign
2DB2ADD A,D82Combine the value of the flag in Register D with the value of the flag in Register A
2DB3LD D,A57Load Register D with the current flags
2DB4DEC B05Decrement 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- ↳ ENDNUMPOP HLE1Get the value of the current BASIC program pointer from the STACK and put it in HL
2DB6POP AFF1Load Register A with the flag that tells us whether there are more values to process in the value list.
2DB7-2DB8If there are no more values in the value list to process, then JUMP to FLDFIN because we are done with the PRINT
2DB9PUSH BCC5Save 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
2DBAPUSH DED5Save the flags (held in D) and the number of leading digits (held in E) to the STACK
2DBB-2DBDRead 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
2DBEPOP DED1Restore the flags (held in D) and the number of leading digits (held in E) from the STACK
2DBFPOP BCC1Restore 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
2DC0PUSH BCC5Save 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
2DC1PUSH HLE5Save the value of the current BASIC program pointer in HL to the STACK
2DC2LD B,E43Set Register B to hold the number of leading digits (i.e., the number of characters to the left of the decimal point)
2DC3LD A,B78We 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
2DC4ADD A,C81Then 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-2DC6CP 19HFE 19Check 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-2DC9Display a FC ERROR message if the total number of digits is greater than 24
2DCALD A,D7ALoad Register A with the flags (held in Register D)
2DCB-2DCCOR 80HF6 80Turn on the "USING" bit in the flags
2DCD-2DCFPrepare 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-2DD2Call 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- ↳ FNSTRFPOP HLE1Top of a loop. Get the value of the current BASIC program pointer from the STACK and put it in HL
2DD4DEC HL2BDecrement the value of the current BASIC program pointer in HL so we can test to see what the terminator was
2DD5RST 10HCHRGETD7We 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.
2DD6SCF37Set the Carry flag to indicate that a CRLF is desired
2DD7-2DD8If 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-2DDBLD (40DEH),ALD (FLGINP),A32 DE 40Set the flag that the value HAS been printed!
2DDC-2DDDCP 3BHCP ";"FE 3BCheck 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.
2DE0-2DE1CP 2CHCP ","FE 2CCheck 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-2DE4If 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- ↳ SEMUSNRST 10HCHRGETD7We 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- ↳ CRDNUSPOP BCC1Restore the number of characters remaining to be procesed in the USING string into Register B
2DE7EX DE,HLEBSwap DE and HL so that DE will point to the location of the current BASIC program pointer. We don't care about HL.
2DE8POP HLE1Restore the position in the USING string from the STACK and put it in HL
2DE9PUSH HLE5Save the position in the USING string (held in HL) to the STACK
2DEAPUSH AFF5Save the flag that indicates whether or not the value list has terminated to the STACK
2DEBPUSH DED5Save 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.
2DECLD A,(HL)7ELoad Register A with the USING string's length at the location of the USING string's VARPTR in HL
2DEDSUB B90Subtract the number of characers which were already scanned
2DEEINC HL23Bump the pointer to the "USING" strings string data
2DEFLD C,(HL)4ELoad Register C with the LSB of the USING string's address at the location of the USING string's VARPTR in HL
2DF0INC HL23Bump the value of the USING string's VARPTR in HL
2DF1LD H,(HL)66Load Register H with the MSB of the USING string's address at the location of the USING string's VARPTR in HL
2DF2LD L,C69Load Register L with the LSB of the USING string's address in Register C
2DF3-2DF4LD D,00H16 00Zero Register D so that Register Pair DE can be a 16 bit offset of whatever is held in A.
2DF5LD E,A5FLoad Register E with the USING string's offset in Register A
2DF6ADD HL,DE19Add 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
2DF7LD A,B78Load Register A with the number of characters left to scan
2DF8OR AB7Check to see if this is the end of the USING string
2DF9-2DFBIf there are still more string characters to scan, JUMP to PRCCHR to do so
2DFE-2E00 - Part of the PRINT USING Routine- "REUSIN"
We will wind up here when we are done processing a numeric field
2E01-2E03Go send the FINAL character (held in Register A) to the current output device
2E04- ↳ FINUSIPOP HLE1Get the value of the current BASIC program pointer from the STACK and put it in HL
2E05POP AFF1Restore the flag which indicates whether or not the value list has ended into Register A
2E06-2E08If the value list has NOT ended, JUMP back to REUSST to reuse the USING string
2E09-2E0B- ↳ FLDFINIf 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
2E0CEX (SP),HLE3Swap (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
2E10POP HLE1Get the value of the current BASIC program pointer from the STACK and put it in HL
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- ↳ SMSTRFLD C,01H0E 01Set the field width to 1
2E16-2E17LD A,0F1H3E F1Z-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- ↳ ISSTRFPOP 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)
2E18DEC B05Decrement the USING string character count (tracked in Register B)
2E1CPOP HLE1Get the value of the current BASIC program pointer from the STACK and put it in HL
2E1DPOP AFF1Get the flag which indicates whether there are more values in the value list into Register A
2E1E-2E1FIf there are no more values in the value list, then we are done so JUMP back to 2E09H
2E20PUSH BCC5Save the number of characters still to be scanned from the USING string (tracked in B) to the STACK
2E21-2E23Read 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
2E27POP BCC1Restore the field width (a/k/a the number of characters to be printed) into Register C
2E28PUSH BCC5Save the USING string's length and the number of characters to be printed in BC to the STACK
2E29PUSH HLE5Save the value of the current BASIC program pointer in HL to the STACK
2E2A-2E2CLD HL,(4121H)LD HL,(FACLO)2A 21 41Load HL with the string's VARPTR in ACCumulator
2E2DLD B,C41Load Register B with field width (a/k/a the number of characters to be printed)
2E2E-2E2FLD C,00H0E 00Zero Register C so that we can use the LEFT$ routine
2E30PUSH BCC5Save the length of the string to be printed in Register B to the STACK (as we will need that for space padding)
2E31-2E33Truncate the string to B characters via a call to the LEFT$ routine
2E37-2E39LD HL,(4121H)LD HL,(FACLO)2A 21 41Load HL with the string's VARPTR in ACCumulator so we can see if we need to pad the string
2E3APOP AFF1Get the field width (a/k/a the length of the string to be printed) from the STACK and put it in Register A
2E3BSUB (HL)96Determine 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
2E3CLD B,A47Save the amount of padding needed into Register B
2E3D-2E3ELD A,20H3E 20Load Register A with a SPACE
2E3FINC B04Bump 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- ↳ UPRTSPDEC B05Top of a loop. Decrement the number of spaces in Register B
2E41-2E43If all of the spaces have been printed, Jump back to 2DD3H to see if the value list ended and to resume scanning
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- ↳ PLSPRTPUSH AFF5Save the current character (held in Register A) to the STACK
2E4ALD A,D7AWe need to test the PLUS BIT in D, so first load Register A with the value in Register D
2E4BOR AB7Check 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-2E4DLD A,2BHLD A,"+"3E 2BPrepare to print the +by loading Register A with a +
2E4E-2E50If the bit was set (i.e., A was non-zero), then send a +to the current output device
2E51POP AFF1Get the current character from the STACK and put it in Register A
2E52RETC9RETurn 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- ↳ ERREDTLD (409AH),ALD (ERRFLG),A32 9A 40Reset the EDIT flag.
Note: 409AH holds the ERROR/RESUME flag
2E56-2E58LD HL,(40EAH)LD HL,(ERRLIN)2A EA 40Load HL with the line number to be edited.
Note: 40EAH-40EBH holds the line number with error
2E59OR HB4OR Register A with the MSB of the error line number in Register H
2E5AAND LA5Combine 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
2E5BINC A3CBump 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
2E5CEX DE,HLEBSwap DE and HL so that DE now holds the line number to edit.
2E5DRET ZC8If there was no line number, return if Level II BASIC
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- ↳ EDITGet the first line number by calling 1E4F - returns in in DE
2E63RET NZC0If the zero flag got set, there was no line number, so return
2E64- ↳ EREDITPOP HLE1Clean up the STACK (i.e., discard the NEWSTT return address)
2E65- ↳ EEDITSEX DE,HLEBLoad HL with the line number to be edited in DE
2E66-2E68LD (40ECH),HLLD (DOT),HL22 EC 40Save 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
2E69EX DE,HLEBLoad HL with the line number to be edited
2E6A-2E6CFind 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
2E70
2E71LD H,B
LD L,C60At 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
2E73INC HL
INC HL23Bump the value of the memory pointer in HL twice to now point to the first byte of the line.
2E74LD C,(HL)4ELoad Register C with first byte of the line number being edited
2E75INC HL23Bump the value of the memory pointer in HL to point to the second byte of the line being edited
2E76LD B,(HL)46Load Register B with second byte of the line number being edited
2E77INC HL23Bump the value of the memory pointer in HL to now point to the first byte of the actual line
2E78PUSH BCC5Save the line number to the STACK
2E79-2E7BGOSUB 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- ↳ LLEDPOP HLE1Get the value of the line number from the STACK and put it in HL
2E7D- ↳ INLEDPUSH HLE5Save the value of the line number in HL to the STACK
2E7E-2E80Convert 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-2E82LD A,20H3E 20Load Register A with a space
2E86-2E88LD HL,(40A7H)LD HL,(BUFPNT)2A A7 40Load 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-2E8ALD A,0EH3E 0ELoad Register A with the "turn on the cursor" character
2E8EPUSH HLE5Save the value of the input buffer pointer (in HL) to the STACK
2E8F-2E90LD C,FFH0E FFLoad 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- ↳ LENLPINC C0CBump the number of characters examined so far in Register C
2E92LD A,(HL)7ELoad Register A with the character at the location of the input buffer pointer in HL
2E93OR AB7Check to see if the character in Register A is an end of the BASIC line character
2E94INC HL23Bump the value of the input buffer pointer in HL
2E95-2E96Loop back to 2E91H until the end of the BASIC line has been found
2E97POP HLE1At this point, C will be the maximum number of characters in the line at issue. Put the start of the expanded buffer into HL
2E98LD B,A47Set the current position in the BASIC line being edited (tracked by Register B) to ZERO
2E99-2E9A- ↳ DISPEDLD D,00H16 00Assume the repetition count for the upcoming command (tracked by Register D) is zero
2E9B-2E9D- ↳ DISPIGo scan the keyboard to wait for the user command
2D9E-2E9F- ↳ DISPSUB 30H20 15We need to test to see if the character was alphabetic or alphanumeric so we subtract 30H from it
2EA2-2EA3CP 0AHFE 0ACheck 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.
2EA6LD E,A5FLoad Register E with the binary value of the character in Register A
2EA7LD A,D7APut the repetition value into Register A
2EA8RLCA07Multiply the value in Register A by two (so now A has multiplied by 2)
2EA9RLCA07Multiply the value in Register A by two (so now A has multiplied by 4)
2EAAADD A,D82Add the value in Register D to the value in Register A (so now A has multiplied by 5)
2EABRLCA07Multiply the value in Register A by two (so now A has multiplied by 10). Now the "ones place" is empty.
2EACADD A,E83Add the value in Register E to the value in Register A in the "ones place"
2EADLD D,A57Load Register D with the value in Register A
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- ↳ NOTDGIPUSH HLE5Save the value of the input buffer pointer in HL to the STACK
2EB4EX (SP),HLE3Exchange the value of the return address in HL with the value of the input buffer pointer to the STACK
2EB5DEC D15We 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
2EB6INC D14... and then incrementing the numeric value in Register D to set the flags
2EB7-2EB9If we had a received a repetition count already, then JUMP to 2EBBH
2EBAINC D14Otherwise, set the repetition count (held in Register D) to be one
2EBB-2EBC- ↳ NTZERDCP 0D8HFE D8Check 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-2EBFIf the character in Register A is a BACKSPACEcharacter, JUMP to DELED
2EC0-2EC1CP 0DDHFE DDCheck 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-2EC4If the character in Register A is a CARRIAGE RETURN, JUMP to CRED
2EC5-2EC6CP 0F0HFE F0Check 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.
That's it for non-alphabetic instructions, so we need to need to convert a lower case command to upper case
2EC9-2ECACP 31HFE 31Check 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.
2ECD-2ECESUB 20HD6 20Convert the lowercase character in Register A to uppercase
2ECF-2ED0- ↳ NOTLW4CP 21HFE 21Check 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.
2ED4-2ED5CP 1CHFE 1CCheck 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.
2ED9-2EDACP 23HFE 23Check 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.
2EDD-2EDECP 19HFE 19Check 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.
2EE2-2EE3CP 14HFE 14Check 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.
2EE7-2EE8CP 13HFE 13Check 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.
2EEC-2EEDCP 15HFE 15Check 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.
2EF1-2EF2CP 28HFE 28Check 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.
2EF6-2EF7CP 1BHFE 1BCheck 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.
2EFA-2EFBCP 18HFE 18Check 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-2EFEJump if the character in Register A is an H(i.e., HACK off the rest of the line and then enter INSERT mode)
2EFF-2F00CP 11HFE 11Check 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.
2F01RET NZC0Return if the character in Register A isn't an A
2F02 - EDITCommand - Cancel and Restore Logic.
2F02POP BCC1Clean up the STACK (i.e., remove the DISPI return address)
2F03POP DED1Get the BASIC line number from the STACK and put it in DE
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- ↳ SPEDLD A,(HL)7ELoad Register A with the character at the location of the input buffer pointer in HL
2F0BOR AB7Check to see if the character in Register A is an end of the BASIC line character
2F0CRET ZC8Return if the character in Register A is an end of the BASIC line character
2F0DINC B04Bump the character position in Register B
2F11INC HL23Bump the value of the pointer in HL
2F12DEC D15Decrement the number of times to perform the operation in Register D
2F15RETC9RETurn to CALLer
2F16 - EDITCommand - KILL Logic- "KED".
2F16- ↳ KEDPUSH HLE5Save the current character position in the buffer in HL to the STACK
2F17-2F19Load HL with the return address of 2F5FH (which will print the final !
2F1AEX (SP),HLE3Exchange the value of the return address in HL with the value of the input buffer pointer to the STACK
2F1BSCF37Set the KILL/SEARCH flag for KILL since CARRY flag signals KILL
2F1C- ↳ SEDPUSH AFF5Save the KILL/SEARCH flag to the STACK
2F1D-2F1FGo scan the keyboard for the character the user wants to SEARCH for
2F20LD E,A5FSave the character the user wants to SEARCH for into Register E
2F21POP AFF1Get the KILL/SEARCH flag from the STACK
2F22PUSH AFF5Save the KILL/SEARCH flag to the STACK
2F23-2F25If KILL (because the CARRY flag was set) then GOSUB to 2F5FH to print a !
2F26- ↳ SRCALPLD A,(HL)7ELoad Register A with the character at the location of the input buffer pointer in HL
2F27OR AB7Check to see if the character in Register A is an end of the BASIC line character
2F28-2F2AJump down to 2F3EH if the character in Register A is an end of the BASIC line character
2F2EPOP AFF1Get the KILL/SEARCH flag from the STACK
2F2FPUSH AFF5Save the KILL/SEARCH flag to the STACK
2F30-2F32If 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-2F34Jump to 2F37H if KILL. Note, we do not move the HL pointer in this case because DELCHR already moved it.
2F35INC HL23If we are here, it must be SEARCH! So bump to the next character
2F36INC B04Bump the value of the character position in Register B
2F37- ↳ NOTSRCLD A,(HL)7ERegardless of whether we are SEARCH or KILL, load Register A with the character at the location of the current input buffer pointer in HL
2F38CP EBBCheck 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.
2F3BDEC D15Decrement the number of times to perform the operation in Register D (as initially specified by the user by entering a number before the command)
2F3E- ↳ POPARTPOP AFF1Get the KILL/SEARCH flag from the STACK
2F3FRETC9RETurn to CALLer
2F40 - EDITCommand - LIST Logic- "LED".
2F40-2F42- ↳ LEDSince 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
2F46POP BCC1Clear off the RETURN address to DISPED
2F47-2F49Jump to 2E7CH (to display the current line number and await the next EDIT command)
2F4A - EDITCommand - DELETE Logic- "DED"
2F4A- ↳ DEDLD A,(HL)7ELoad Register A with the character at the location of the input buffer pointer in HL
2F4BOR AB7Check to see if the character in Register A is an end of the BASIC line character
2F5CRET Z15Return if the character in Register A is an end of the BASIC line character
2F4D-2F4ELD A,21HLD A,"!"3E 21Load Register A with an !
2F52- ↳ DELLPLD A,(HL)7ELoad Register A with the character at the location of the input buffer pointer in HL
2F53OR AB7Check to see if the character in Register A is an end of the BASIC line character
2F54-2F5BJump to 2F5FH if the character in Register A is an end of the BASIC line character
2F5CDEC D15Decrement the number of times to perform the operation in Register D (as initially specified by the user by entering a number before the command)
2F5F-2F60- ↳ TYPSLHLD A,21HLD A,"!"3E 21Load Register A with an !
2F64RETC9RETurn to CALLer
2F65 - EDITCommand - CHANGE Logic- "CED".
2F65- ↳ CEDLD A,(HL)7ELoad Register A with the character at the location of the input buffer pointer in HL
2F66OR AB7Check to see if the character in Register A is an end of the BASIC line character
2F67RET ZC8Return if the character in Register A is an end of the BASIC line character
2F68-2F6AGo get the character to put in the input buffer from the keyboard
2F6BLD (HL),A77Save the character in Register A at the memory location of the input buffer pointer in HL
2F6FINC HL23Bump the value of the input buffer pointer in HL
2F70INC B04Bump the character position in Register B
2F71DEC D15Decrement the number of times to perform the operation in Register D (as initially specified by the user by entering a number before the command)
2F74RETC9RETurn to CALLer
2F75 - EDITCommand - HACK/INSERT Logic- "HED"
2F75-2F76- ↳ HEDLD (HL),00H36 00Set the line end to be the current position.
2F77LD C,B48Load Register C with the character position in Register B which will now be the line length
2F78-2F79- ↳ XEDLD D,0FFH16 FFPrepare for the next CALL to find the end of the line by loading Register D with the number of times to perform the operation
2F7D-2F7F- ↳ IEDGo get the character to be inserted from the keyboard
2F80OR AB7Check to see if a key was pressed
2F84-2F85CP 08HFE 08Check 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-2F87Jump to 2F92H if the character in Register A is a backspace character
2F88-2F89CP 0DHFE 0DCheck 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-2F8CJump to 2FE0H if the character in Register A is a carriage return
2F8D-2F8ECP 1BHFE 1BCheck 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.
2F8FRET ZC8Return if the character in Register A is shift up arrow
2F92 - EDITCommand - BACKSPACE CURSOR Logic- "TYPARW".
2F92-2F93- ↳ TYPARWLD A,08H3E 08Load Register A with a backspace the cursor character
2F94- ↳ TYPAR1DEC B05Decrement the character position in Register B
2F95INC B04Bump the character position in Register B
2F96-2F97If this is the first character of the BASIC line Jump forward to 2FB7H
2F9BDEC HL2BDecrement the value of the input buffer pointer in HL
2F9CDEC B05Decrement the character position in Register B
2FA0PUSH DED5Save 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- ↳ DELCHRPUSH HLE5Save the value of the input buffer pointer in HL to the STACK
2FA2DEC C0DDecrement the character position in Register C
2FA3- ↳ CMPRSSLD A,(HL)7ELoad Register A with the character at the location of the input buffer pointer in HL
2FA4OR AB7Check to see if the character in Register A is an end of the BASIC line character
2FA5SCF37Set the Carry flag to signal that DELCHR was called
2FA6-2FA8If the character in Register A is an end of the BASIC line character then we are done compressing so Jump to 0890H
2FA9INC HL23Bump the value of the input buffer pointer in HL
2FAALD A,(HL)7ELoad Register A with the character at the location of the input buffer pointer in HL
2FABDEC HL2BDecrement the value of the input buffer pointer in HL
2FACLD (HL),A77Save the character in Register A at the location of the current input buffer pointer in HL
2FADINC HL23Bump the value of the input buffer pointer in HL
2FB0 - EDITCommand - ADD A CHARACTER Logic- "NTARRW".
2FB0- ↳ NTARRWPUSH AFF5Save the character to be inserted in Register A to the STACK
2FB1LD A,C79Load Register A with the number of characters in the input buffer (i.e., the length of the line) in Register C
2FB2-2FB3CP FFHCP BUFLENFE FFWe 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-2FB5Jump forward to 2FB9H if the maximum BASIC line length hasn't been reached
2FB6POP AFF1Get the character to be inserted from the STACK and put it in Register A
2FB9- ↳ OKINSSUB B90Subtract 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
2FBAINC C0CBump the number of characters in the input buffer in Register C
2FBBINC B04Bump the character position in Register B
2FBCPUSH BCC5Save the character position and the number of characters in the input buffer in BC to the STACK
2FBDEX DE,HLEBLoad DE with the input buffer pointer in HL
2FBELD L,A6FLoad Register L with the number of bytes to move
2FBF-2FC0LD H,00H26 00Zero Register H so that the number of bytes to move can be done in a 16 bit Register Pair.
2FC1ADD HL,DE19Add the value of the input buffer pointer in DE to the character count in HL
2FC2LD B,H44Load Register B with the MSB of the end of the BASIC line pointer in Register H
2FC3LD C,L4DLoad Register C with the LSB of the end of the BASIC line pointer in Register L
2FC4INC HL23Bump the value of the end of the BASIC line pointer in HL
2FC8POP BCC1Get the character position and the number of characters in the input buffer from the STACK and put it in BC
2FC9POP AFF1Get the character to be inserted from the STACK and put it in Register A
2FCALD (HL),A77Save the character in Register A at the location of the current input buffer pointer in HL
2FCEINC HL23Bump the value of the input buffer pointer in HL
2FD2 - EDITCommand - BACKSPACE Logic- "DELED".
2FD2- ↳ DELEDLD A,B78Top 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
2FD3OR AB7Check to see if this is the start of the BASIC line
2FD4RET ZC8Return if this is the start of the BASIC line
2FD5DEC B05Decrement the character position in Register B
2FD6DEC HL2BDecrement the value of the buffer pointer in HL
2FD7-2FD8LD A,08H3E 08Load Register A with a backspace the cursor character
2FDCDEC D15Decrement the number of times to perform the operation in Register D
2FDFRETC9RETurn to CALLer
2FE0-2FE2- ↳ CREDSince 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
2FE6POP BCC1Clean up the STACK (to remove the DISPED return address)
2FE7POP DED1Get the BASIC line number (in binary) from the STACK and put it in DE
2FE8LD A,D7ALoad Register A with the MSB of the BASIC line number in Register D
2FE9AND EA3Combine the LSB of the BASIC line number in Register E with the MSB of the BASIC line number in Register A
2FEAINC A3CBump the combined BASIC line number in Register A
2FEB-2FED- ↳ EDITRTLD HL,(40A7H)LD HL,(BUFPNT)2A A7 40Load HL with the starting address of the input buffer.
Note: 40A7H-40A8H holds the input Buffer pointer
2FEEDEC HL2BDecrement the value of the input buffer pointer in HL
2FEFRET ZC8Return if this is the Level II BASIC command mode
2FF0SCF37Set the Carry flag to to fool the INSERT code; this flags the line number has having been seen
2FF1INC HL23Bump the value of the input buffer pointer in HL
2FF2PUSH AFF5Save the command mode flag in AF to the STACK
2FF6 - EDITCommand - QUIT Logic- "QED".
3000 - Jump Table.
3012 - Model 4 ROM Gen 1
3012 - Model 4 ROM Gen 2
*3024 - Model 4 ROM Gen 1
*3027RET
NOP
NOPI/O Re-Router was removed from the Model 4 ROM.
*3024 - Model 4 ROM Gen 2
*302D - Model 4 ROM Gen 1
*302D - Model 4 ROM Gen 2
*302DJP 375CHC3 5C 37Jump to 375CH for a routine which parses whether the current instruction on a the current line is in quotes.
3036Jump to 35A0H to put the TIME 10 characters from the upper right hand corner of the screen.
3039IN A,(0E4H)Poll Port E4H into A.
NOTE: Port E4H is the Non-Maskable Interrupt Latch.
303BBIT 5,ATest Bit 5 of Register A. Bit 5 of the NMI on an Input test is the RESET STATUS, with 0=False, and 1=True
*3042 - Model 4 ROM Gen 1 - Prompt the User to set the cassette baud rate.
*3042 - Model 4 ROM Gen 2 - Prompt the User to set the cassette baud rate.
*3045-3064 - Model 4 ROM Gen 1
*3045-305F@abcdefghijklmnopqrstuvwxyzKeyboard Rows 0-3, Unshifted, No Caps Lock
*3060NOPComputer version number, which is always 1 for a Model III and 0 for a Model 4
*3061NOP
*3062NOP
*3063NOP
*3064NOP
*3065-307D30 31 32 33 34 35 36 37 38 39 3A 3B 2C 2D 2E 2F 0D 1F 01 5B 0A 08 09 20 00Keyboard Rows 4-6, Unshifted, No Caps Lock
*307ENOP
*307FNOP
*3080NOP
*3081NOP
*3082NOP
*3083NOP
*3084NOP
*3085-309F60 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 5AKeyboard Rows 0-3, shifted, No Caps Lock
*30A0NOP
*30A1NOP
*30A2NOP
*30A3NOP
*30A4NOP
*30A5NOP
*30A6-30BC21 22 23 24 25 26 27 28 29 2A 2B 3C 3D 3E 3F 0D 1F 01 1B 1A 18 19 20Keyboard Rows 4-6, shifted, No Caps Lock
*30BDNOP
*30BENOP
*30BFNOP
*30C0NOP
*30C1NOP
*30C2NOP
*30C3NOP
*30C4NOP
*30C5-30DF40 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 5AKeyboard Rows 0-3, UNshifted, Caps Lock.
*30E0NOP
*30E1NOP
*30E2NOP
*30E3NOP
*30E4NOP
*30E5-30FC30 31 32 33 34 35 36 37 38 39 3A 3B 2C 2D 2E 2F 0D 1F 01 5B 0A 08 09 20Keyboard Rows 4-6, UNshifted, Caps Lock
*30FDNOP
*30FENOP
*30FFNOP
*3100NOP
*3101NOP
*3102NOP
*3103NOP
*3104NOP
*3105H - Model 4 Gen 1 jump to NON-Disk BASIC
GOSUB to the $SETCAS routine which prompts the user to set the cassette baud rate (310BH - 313AH)
*310BH - Model 4 Gen 1 Set the TAPE BAUD RATE ($SETCAS).
*310CGOSUB to 312DH which loads A with a carrage return, and jumps to 0033H to display it.
*3115GOSUB to 0049H.
NOTE: 0049H is the $KBWAIT routine which scans the keyboard and returns with the key pressed, if any, in register A.
*3118CP 0DHCompare A and 0D (a CARRIAGE RETURN).
*311CPUSH AFSave AF to the STACK.
NOTE: A currently holds the character pressed in response to the "CASS?" message.
*311DGOSUB to 0033H.
NOTE: 0033H is the character print routine, to put the character held in Register A at the current cursor position.
*3120POP AFRestore AF from the STACK.
NOTE: A will then hold the character pressed in response to the "CASS?" message.
*3121CP 48HCompare A with 48H (ASCII: H).
*3125CP 4CHCompare A with a 4CH (ASCII: L).
3129H - Model 4 Gen 1 Set the Selected Cassette Baud Rate as LOW SPEED
*3129XOR ASet 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
LD (4211H),ASave 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
*312FDisplay 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
Finish the cassette setup by calling a portion of the keyboard scan routine which CALLS the screen print routine at 34FDH
*313EXOR AClear Register A and All Flags
*313FRETReturn a null character.
*3140NOP
*3141NOP
*3142NOP
*3143NOP
*3144NOP
*3145 - Model 4 Gen 1 Printer Character Table Codes 32-127.
*3145-31A420 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.
*3045JP 378DHC3 8D 37Jump 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
*3048JP 377AHC3 7A 37Jump to 377AH to check to see if we are on a new printable page and set the pointers accordingly. CALLED from 0431 and 0445
*3045 - Model 4 ROM Gen 2 Keyboard Rows 0-3, Unshifted, No Caps Lock.
*304E-3068@abcdefghijklmnopqrstuvwxyz
*3069NOP
*306ANOP
*306BNOP
*306CNOP
*306DNOP
*306E-308530 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.
*3086IN A,(0EAH)DB EAPoll the RS-232 UART Control Register and Status Register (at Port EAH) and put the results into Register A
*3087OR AB7Set the FLAGS based on the RS-232 UART Control Register and Status Register
*308C-308DJR 30C6H18 38If 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.
**3086LD A,(3810H)3A 10 38Fetch the Keyboard Matrix 0-7 into Register A to check for certain keys.
**3089AND 10HE6 10MASK 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.
**306CNOP
**306DNOP
**306DNOP
*308E-30A8 - Model 4 ROM Gen 2 - Continuing with the Keyboard Table
*308E-30A860 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
*30A9NOP
*30AANOP
*30ABNOP
*30ACNOP
*30ADNOP
*30AENOP
*30AF-30C521 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-30C7IN A,(0EBH)DB EBPoll the RS-232 Register at Port EBH and put the results into Register A.
*30C8RETC9
**30C6-30C8 - Model 4 ROM Student Network Edition - A little code snippet
*30C6NOP
*30C7NOP
*30C8NOP
*3089 - Model 4 ROM Gen 2 - Continuing with the Keyboard Table
*30C9NOP
*30CA-30CDPUSH BC
POP BC
NOP
RETStandard code for a short delay
*30CE@ABCDEFGHIJKLMNOPQRSTUVWXYZ
*30E9NOP
*30EANOP
*30EBNOP
*30ECNOP
*30EDNOP
*30EE30 31 32 33 34 35 36 37 38 39 3A 3B 2C 2D 2E 2F 0D 1F 01 5B 0A 08 09 20Keyboard Rows 4-6, UNshifted, Caps Lock
*3106LD A,(3880H)3A 80 38Load A with the value held at 3880H (which are the SHIFT KEYS)
*3109LD HL,414FH21 F4 41Point HL to 414FH
*310CAND 7CHE6 7CMASK the value held in the SHIFT KEY RAM location against 0111 1100 to keep only bits 2, 3, 4, 5, and 6 live
*310EOR AB7OR A against itself to reset the flags
*310FJR Z,3142H28 31If the Z FLAG is set, JUMP to 3142H to continue checking fro special keys
*3111LD E,A5FPut the masked A into E.
*3112XOR (HL)AEToggle against the old image.
*3113LD (HL),E73Save the new image into (HL).
*3114AND EA3Mask Register E against Register A.
*3115JR Z,3157H28 40If ZERO then the LEFT SHIFT PRESSED was JUMP to 3157H to restart parsing the keyboard
*3117LD BC,05C4H01 C4 05Load BC with 05C4H to set up a 1/50 second delay for de-bounce.
*311ACALL 0060HCD 60 00GOSUB 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.
*311DLD A,(3880H)3A 80 38Load A with the value held at 3880H (which are the SHIFT KEYS)
*3120AND 7CHE6 7CMASK the value held in the SHIFT KEY RAM location against 0111 1100 to keep only bits 2, 3, 4, 5, and 6 live
*3122CP EBBCompare A with E.
*3126RLA17Rotate 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.
*3127RLA17Rotate 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.
*3128JR NC,312EH30 04We 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
*312ALD A,(41F3H)3A F3 41Load Register A with the value of the character to be returned when the F3 key is pressed
*312DRETC9RETurn to Caller
*312E - Model 4 Gen 2 - KEYBOARD Routine - Check and Process the F2 Key or Jump Away.
*312ERLA17Rotate 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.
*312FJR NC,3135H30 04If the NC FLAG is set, it isn't the F2 key, so JUMP to 3135H to test the F1 key
*3131LD A,(41ECH)3A EC 41Load Register A with the value of the character to be returned when the F2 key is pressed
*3134RETC9RETurn to Caller
*3135 - Model 4 Gen 2 - KEYBOARD Routine - Check and Process the F1 Key or Jump Away.
*3135RLA17Rotate 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.
*3136JR NC,313CH30 04If the NC FLAG is set, it isn't the F1 key, so JUMP to 313CH to keep checking special keys
*3138LD A,(41EBH)3A EB 41Load Register A with the value of the character to be returned when the F1 key is pressed
*313BRETC9RETurn to Caller
*33C4 - Model 4 Gen 2 - KEYBOARD Routine - Part of the Keyboard Scan Routine. Keep Checking Special Keys.
*313CRLA17Rotate 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.
*33CA - Model 4 Gen 1 - KEYBOARD Routine - Part of the Keyboard Scan Routine.
*3142LD (HL),A77Save the character held in Register A into the memory location pointed to by Register Pair HL
*3143LD A,FFH3E FFLoad A with FF to set up for a FLAG = 0FFH = NO CONTROL.
*3145LD HL,3840H21 40 38Load HL with 3840H to start a check for a DOWN ARROW.
*3148BIT 4,(HL)CB 66Test BIT 4 of (HL) to check for a DOWN ARROW.
*314AJR Z,3154H28 08JUMP to 3154H if the a DOWN ARROW was NOT pressed.
*314CSLA LCB 25Next we need to check for a a LEFT SHIFT so shift L left.
*314EBIT 0,(HL)CB 46Test BIT 0 of (HL) to check for a LEFT SHIFT.
*3152LD A,1FH3E 1FLoad A with 1F to set up for FLAG = CONTROL KEY.
*3154LD (4224H),A32 24 42Save the CONTROL FLAG into (4224H).
NOTE: 4224H Holds the CONTROL KEY flag.
*315ARET NCD0If that routine exited with NC FLAG set, RETurn
*315ECP 1AHFE 1ACheck A against 1AH to see if we have a SHIFT+DOWN ARROW.
*3163OR AB7Set the flags based on Register A
*3167LD HL,4224H21 24 42Set Register Pair HL to 4224H, which is the CONTROL KEY flag.
*316ABIT 7,(HL)CB 7ETest Bit 7 of the CONTROL KEY flag in RAM.
*316ECP 2AHFE 2ACheck A against a * key.
*3170JP Z,37C7HCA C7 37If 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.
*3173AND (HL)A6Prepare to check for a BREAK key by masking A against the memory contents of HL ...
*3174CP 01HFE 01... and COMPARING it to 01H.
*3176RET NZC0If the result of the compare is NOT zero, then RETURN.
*3177RST 28HEFIf we are here, then a BREAK key was hit, so call RST 28H to handle the BREAK key.
*3178RETC9RETURN to Caller
*3179 - Model 4 Gen 2 - Continuing Initialization Routine by setting up the RS-232.
*3179XOR AAFSet Register A to 0
*317AOUT (E8H),AD3 E8Output A to port E8H.
NOTE: Port E8H is the RS-232 Status Register & Master Reset. Outputting ANYTHING to Port E8H resets the RS-232.
*317CLD A,EEH3E EESet Register A to EEH for outputting to the RS-232 Baud Rate Select and Sense Switches: (Port E9H)
*317EOUT (E9H),AD3 E9Initialize 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
*3180LD A,6DH3E 6DSet Register A to 6DH for outputting to the RS-232 UART Control Register and Status Registe (Port EAH)
*3182OUT (EAH),AD3 EASent 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)
*3184IN A,(E8H)DB E8Poll the RS-232 Status Register and Master Reset of Port E8H and put the value into Register A.
*3186BIT 6,ACB 77Test Bit 6 of the RS-232 Status Register to check Data Set Ready (Pin 6 of the DB-25).
*3188JR Z,3184H28 FAIf the DATA SET READY is ZERO then LOP back 2 instructions to 3184 to poll again
*318ALD A,6CH3E 6CSet Register A to 6CH (0110 1100) for outputting to the RS-232 UART Control Register and Status Registe (Port EAH)
*318COUT (EAH),AD3 EASend 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
*318EIN A,(E8H)DB E8Poll the RS-232 Status Register and Master Reset of Port E8H and put the value into Register A.
*3190BIT 6,ACB 77Test Bit 6 of the RS-232 Status Register to check Data Set Ready (Pin 6 of the DB-25).
*3192JR NZ,318EH20 FAIf the DATA SET READY is ZERO then LOOP back 2 instructions to 318EH to poll again
*3194LD A,0FH3E 0FSet Register A to 0FH
*3196CALL 37D5HCD D5 37GOSUB to 37D5H to send the Character in Register A to the RS-232, once the RS-232 shows ready to accept that character.
*3199CALL 3086HCD 86 30GOSUB to 3086 to Poll the UART and wait for the P FLAG to not be set and then CONTINUE at 306CH
*319CCALL 3086HCD 86 30GOSUB to 3086 to Poll the UART and wait for the P FLAG to not be set and then CONTINUE at 306CH
*319FCALL 37D5HCD D5 37GOSUB to 37D5H to send the Character in Register A to the RS-232, once the RS-232 shows ready to accept that character.
*31A2-31A4JP 3517HC3 17 35JUMP 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
**3179LD A,(4210H)3A 10 42Get 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.
**317COR 10HF6 10OR against 10H (0001 0000) to turn all I/O ports on
**317ELD (4210H),A32 10 42Put the masked status back, first by loading it into 4210H
**3181OUT (ECH),AD3 EC... and then by sending it to 0ECH which is the same as 04210.
**3183LD A,08H3E 08Put an 08H into Register A
**3185OUT (D3H),AD3 D3Send 08H to Port D3H, which is the Network 4 Omninet MSB pointer
**3187XOR AAFPut an 00H into Register A
**3188OUT (D1H),AD3 D1Send 08H to Port D1H, which is the Network 4 Omninet LSB pointer
**318ALD BC,00D0H01 D0 00In 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
**318DLD HL,7000H21 00 70In further preparation for the INIR commands, point HL to the BUFFER for the code, which is 7000H
**3190PUSH HLE5Save the 7000H Buffer start point to the top of the stack
**3191INIRED B2Write a byte from port C to the memory location pointed to by HL, and then increase HL and decrease B.
**3193INIRED B2Write a byte from port C to the memory location pointed to by HL, and then increase HL and decrease B.
**3195INIRED B2Write a byte from port C to the memory location pointed to by HL, and then increase HL and decrease B.
**3197INIRED B2Write a byte from port C to the memory location pointed to by HL, and then increase HL and decrease B.
**3199INIRED B2Write a byte from port C to the memory location pointed to by HL, and then increase HL and decrease B.
**319BINIRED B2Write a byte from port C to the memory location pointed to by HL, and then increase HL and decrease B.
**319DINIRED B2Write a byte from port C to the memory location pointed to by HL, and then increase HL and decrease B.
**319FINIRED B2Write a byte from port C to the memory location pointed to by HL, and then increase HL and decrease B.
**31A1RETC9RETurn to CALLer
**31A2NOP00
**31A3NOP00
**31A4NOP00
31A5 - Output the TIMING MARK to the cassette
31A5LD A,01HLoad A with 01H. This is to prepare to send 0.46V to tape.
31A7OUT (0FFH),ALoad 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.
31A9LD B,0DHLoad B with 0DH in as a loop counter.
31ADLD A,02HLoad A with 01H. This is to prepare to send 0.0V to tape.
31AFOUT (0FFH),ALoad 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.
31B1LD B,0DHLoad B with 0DH in as a delay.
31B5GOSUB to 31F3 to send 0.46V to tape.
NOTE: 31F3H resets the cassette port, and then output a 0 to the Cassette Port FFH.
31B8LD B,78HLoad B with 78H in as a delay.
31BCRETRETURN.
31C0 - Turn Off The Cassette
31C0LD A,(4213H)Load A with the memory contents of 4213H.
NOTE: 4213H is the default interrupt vector setting for the cassette.
31C3OUT (0E0H),AOutput 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
31C5IN 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.
31C7LD 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.
31CAAND FDHMask A against FDH (1111 1101) to zero bit 1.
31CFEIEnable Interrupts.
31D0RETRETURN.
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 "**"
31D1EX DE,HLSwap DE and HL to remove the return address.
31D2EX (SP),HLSwap the memory contents pointed to by the STACK POINTER and HL (which is now what DE was).
31D3PUSH BCSave BC to the STACK.
31D4PUSH HLSave HL to the STACK.
31D5EX DE,HLSwap DE and HL back.
31D6IN 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.
31D8LD DE," "Load DE with SPACESPACE.
31DBLD (3C3EH),DELoad the screen memory location of 3C3EH with DE.
31E8 - Turn On The Cassette - Actually Set the Bit Mask and Output the Command
31E8LD 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.
31EBOR 02HOR A with 02H (0000 0010) to set Bit 1.
31EDLD (4210H),ASave 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.
31F0OUT (0ECH),AOutput that Bit Mask to port 0ECH.
31F2RETRETURN.
31F3 - Reset the Cassette Port. This routine OUTputs a 0 to the Cassette Port FFH
31F3XOR AWe want to reset the cassette port so we zero A.
31F4OUT (0FFH),ALoad 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.
31F6RETRETURN.
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.
31F7LD A,(HL)Load Register A with the memory contents pointed to by Register Pair HL.
31F8SUB 23HSubtract 23H so that we can test to see if the caller was a PRINT #command.
31FAIf 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.
3200RST 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.
3202RETIf we are here, then we have PRINT #n,so we RETURN.
3203 - Vector for a SLOW cassette read
3203LD B,08HLoad B with 8, representing the need to LOOP for 8 bits.
3205GOSUB to 3220H.
NOTE: 3220H reads the tape until it finds a timing mark or the BREAKis hit.
320ALD A,(4212H)Put the contents of 4212H into A.
NOTE: 4212H holds the cassette blinker counter.
320DINC ABump A.
320EAND 5FHMask A against 5F (0101 1111) to turn Bit 7 and Bit 5 off.
3210LD (4212H),APut A into 4212H.
NOTE: 4212H holds the cassette blinker counter.
3215LD A,(3C3FH)Load A with the screen contents at position 3C3FH.
3218XOR 0AHXOR 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).
321ALD (3C3FH),APut the revised A onto the screen at position 3C3FH.
321DLD A,DPut D (the byte) into A.
3220 - Cassette - Keep reading tape looking for a timing mark or BREAK.
3220PUSH BCSave BC to the STACK.
3221IN A,(FFH)Poll Port FFH with the results into Register A.
NOTE: FFH is the Cassette Port.
3223RLAPut 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).
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)
322ELD B,6EHLoad B with 6EH, which is the length of the timing mark.
Reset the cassette port
Next, wait for 98H Units (the length until a data pulse is expected)
3235LD B,98HLoad B with 98H, which is when the next data pulse should be available.
... continue
3239IN A,(FFH)Poll Port FFH and put the results into A.
NOTE: Port FFH is the Cassette Port.
323BPOP BCRestore BC from the STACK.
323CRLARotate 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.
323DRL DRotate 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.
3241 - Vector for a SLOW cassette write. On entry A is the byte to output.
3241PUSH AFSave AF to the STACK.
3242PUSH BCSave BC to the STACK.
3243PUSH DESave DE to the STACK.
3244LD C,08HLoad C with an 8, representing 8 bits to be written.
3246LD D,ALoad D with A.
NOTE: D will be the DATA BYTE.
324ARLC DRotate 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.
324CIf 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.
3251DEC CReduce the counter holding the number of bits to deal with by 1.
3254POP DERestore DE from the STACK.
3255POP BCRestore BC from the STACK.
3256POP AFRestore AF from the STACK.
3257RETRETURN.
3258 - "Write a 0 Bit" by simply waiting the appropriate amount of time and doing nothing.
325E - SLOW tape header write
325EPUSH HLSave HL to the STACK.
3262LD (420CH),HLLoad the memory contents of 420CH with HL.
NOTE: 420CH is the TAPE WRITE VECTOR.
3265LD B,53HLoad B with 53H (Decimal: 83) in prepartion to output 83 ZEROes.
3267XOR AClear A and all flags.
326DLD A,0A5HLoad A with A5H.
NOTE: A5H is the OUTPUT SYNC BYTE.
3274 - SLOW tape header read
3274PUSH HLSave HL to the STACK.
3278LD (420EH),HLPut the vector for a slow cassette read into the memory location at 420EH.
NOTE: 420EH is the TAPE READ VECTOR.
327BLD B,40HLoad B with 40H to set up a loop of 64 to try to find 64 zeroes.
327DLD D,00HLoad D with 0.
3282LD A,DLoad A with the D (the data byte) to begin to check the current data byte.
3283OR ASet up the flags.
3288GOSUB to 3220H.
NOTE: 3220H reads the tape until it finds a timing mark or the BREAKis hit.
328BLD A,DLoad A with the D (the data byte) to begin to check the current data byte.
328CCP 0A5HCompare A to A5H looking for a SYNC BYTE.
3290LD HL,"**"In preparation to display a "**", load HL with **.
3293LD (3C3EH),HLPut HL onto the screen at location 3C3EH.
3296LD A,HLoad A with H (which is a *.
3297POP HLRestore HL from the STACK.
3298POP BCRestore BC from the STACK.
3299POP DERestore DE from the STACK.
329ARETRETURN.
329B - FAST tape header write.
329BPUSH HLSave HL to the STACK.
329FLD (420CH),HLLoad the memory contents of 420CH with HL.
NOTE: 420CH is the TAPE WRITE VECTOR.
32A2LD B,00HLoad B with 00H to set up a loop of 256 to output 256 "55H" bytes.
32A4LD A,55HLoad A with "55H".
32A6GOSUB to 32B4H.
NOTE: 32B4 restore all registers from the STACK, and Fill C with A, and JUMP to cassette write
32ABLD A,7FHLoad A with 7FH.
NOTE: 7FH is the OUTPUT SYNC BYTE.
32ADGOSUB to 32B4H.
NOTE: 32B4 restore all registers from the STACK, and Fill C with A, and JUMP to cassette write
32B0LD A,A5HLoad A with A5H.
NOTE: A5H is the SLOW SYNC BYTE.
32B4 - Restore all registers from the STACK, and Fill C with A, and JUMP to cassette write.
32B4PUSH AFSave AF to the STACK.
32B5PUSH BCSave BC to the STACK.
32B6PUSH DESave DE to the STACK.
32B7LD C,ALoad C with A.
32BA - Save all registers to the STACK, and Fill C with A, GOSBUB to write out the START BIT ...
32BAPUSH AFSave AF to the STACK.
32BBPUSH BCSave BC to the STACK.
32BCPUSH DESave DE to the STACK.
32BDLD C,ALoad C with A.
32C1 - Call 3335H to Output a Bit 8 Times
32C1LD B,08HLoad B with an 8 to set up a loop for 8 bits.
32CA - Read the start bit, read 8 bits, check for error, and flash the star
32CDLD B,08HLoad B with an 8 to set up a loop for 8 bits.
32DA - FAST tape header read.
32DAPUSH HLSave HL to the STACK.
32DELD (420EH),HLLoad the TAPE READ VECTOR (memory location of 420EH) with the 32CAH.
32E1LD A,01HLoad A with a 1 to set the interrupt.
32E3OUT (0E0H),AOUTPUT 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
32E5LD B,80HSet up a loop of 80H (128) to try to read 128 bits.
32EALD A,CLoad A with C (which is the pulse width).
32EBCP 0FHCompare A to 0FH to see if the pulse width was too short.
32EFCP 3EHCompare A to 3EH to see if the pulse width was too long.
32F8LD B,40HSet up a loop of 40H (Decimal: 64) to try to read 64 bits.
3300LD D,CLoad D with C (which holds the delay count).
3304LD A,DLoad A with D to set up to find the difference in the delays.
3305SUB CSubtract C (the delay count) from A (which holds D).
3308NEGA = 0 - A.
330ACP 0DHCompare 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.
330CIf the CARRY FLAG is set, JUMP to 3313H which will bump L and continue the 64 bit loop.
330EINC HBump HL since we have one more zero bit.
3313INC LBump L since we have one more bit.
3316LD A,40HLoad A with 40H (64).
3318CP HCompare A with H to check for bits.
331BCP LCompare with A to check for one bits.
331ELD A,02HLoad A with 2 to set the interrupt vector.
3320OUT (0E0H),AOutput 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
3325LD D,00HZero D.
332DLD A,DLoad A with D (the read byte).
332ECP 7FHCompare A against 7F to check for a MARKER BYTE.
3335RLC CWe 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.
3341DEC DDecrement D as DELAY #1.
3344LD A,02HLoad A with a 2 to set up for a write of 0 VOLTS to TAPE.
3346OUT (0FFH),ALoad 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.
3348DEC EDecrement E as DELAY #2.
334BLD A,01HLoad A with 01H to prepare to send 0.85 VOLTS to TAPE.
334DOUT (0FFH),ALoad 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.
334FRETRETURN.
3350H - READ a BIT
3350EIEnable Interrupts.
3351LD C,00HLoad C with 0.
3353INC CBump C.
3354LD A,(3840H)Load A with the contents of 3840H so as to check for a BREAK.
3357AND 04HMask A with 4 (0000 0100).
335BDIDisable Interrupts.
335FLD (3C3EH),HLPut the "BK" in HL onto the video screen at 3C3EH.
3362JUMP 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
3365LD E,01HLoad E with 01H (Binary: 0000 0001) to make the bit go high.
3369LD E,00HLoad E with 0 to make the bit go low.
336BLD A,06HLoad A with 6.
336DADD A,CAdd A (6) to C, so COUNT = COUNT + 6.
336ELD C,APut the count held in A back into C.
336FIN A,(FFH)Poll FFH (to get the level) into A.
NOTE: FFH is the Cassette Port.
3371AND 01HMask A with 1 (0000 0001) to keep only Bit 0.
3373CP ECompare A with E (which was the set level).
3376POP AFRestore AF from the STACK.
3377POP AFRestore AF (the REMOTE CALLER'S ADDRESS) from the STACK.
3378RETRETURN to the caller's caller.
3379POP AFRestore AF from the STACK.
337AEIEnable Interrupts.
337BRETRETURN back top the loop.
337CH - Check for a Data Error.
337CLD A,CLoad A with C (the count).
337DCP 22HCompare 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
337FRL DWe 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.
3381CP 0FHMake 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.
3383If A < 0FH then it was too quick and we have a data error so JUMP forward a few instructions 3388H.
3385CP 3EHCompare A against 3EH to make sure it was not too slow.
3387RET CIf it wasn't too slow, RETURN.
3388LD A,44HIt was too slow, so load A with a D.
338ALD (3C3EH),APut the "D" on the screen at video location 3C3EH.
338DRETRETURN.
*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).
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).
*3391LD A,(3880H)Load A with the value held at 3880H (which are the SHIFT KEYS)
*3394AND 7CHMASK the value held in the SHIFT KEY RAM location against 0111 1100 to keep only bits 2, 3, 4, 5, and 6 live
*3396OR AOR A against itself to reset the flags
*3399LD E,APut the masked A into E.
*339AXOR (HL)Toggle against the old image.
*339BLD (HL),ESave the new image into (HL).
*339CAND EMask Register E against Rgister A.
*33A2GOSUB 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.
*33A5LD A,(3880H)Load A with the value held at 3880H (which are the SHIFT KEYS)
*33A8AND 7CHMASK the value held in the SHIFT KEY RAM location against 0111 1100 to keep only bits 2, 3, 4, 5, and 6 live
*33AACP ECompare A with E.
*33AERLARotate 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.
*33AFRLARotate 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.
*33B0We 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
*33B2LD A,(41F3H)Load Register A with the value of the character to be returned when the F3key is pressed
*33B5RETRETurn to Caller
*33B6 - Model 4 Gen 1 - KEYBOARD Routine - Check and Process the F2 Key or Jump Away.
RLARotate 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.
*33B9LD A,(41ECH)Load Register A with the value of the character to be returned when the F2key is pressed
*33BCRETRETurn to Caller
*33B6 - Model 4 Gen 1 - KEYBOARD Routine - Check and Process the F1 Key or Jump Away.
RLARotate 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.
*33BEIf the NC FLAG is set, it isn't the F1, so JUMP to 33C4 to keep checking special keys
*33C0LD A,(41EBH)Load Register A with the value of the character to be returned when the F1key is pressed
*33C3RETRETurn to Caller
*33C4 - Model 4 Gen 1 - KEYBOARD Routine - Part of the Keyboard Scan Routine. Keep Checking Special Keys.
RLARotate 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.
*33CA - Model 4 Gen 1 - KEYBOARD Routine - Part of the Keyboard Scan Routine.
LD (HL),ASave the character held in Register A into the memory location pointed to by Register Pair HL
33CBLD A,FFHLoad A with FF to set up for a FLAG = 0FFH = NO CONTROL.
33D0BIT 4,(HL)Test BIT 4 of (HL) to check for a DOWN ARROW.
33D4SLA LNext we need to check for a a LEFT SHIFTso shift L left.
33D6BIT 00H,(HL)Test BIT 0 of (HL) to check for a LEFT SHIFT.
33DALD A,1FHLoad A with 1F to set up for FLAG = CONTROL KEY.
33DCLD (4224H),ASave the CONTROL FLAG into (4224H).
NOTE: 4224H Holds the CONTROL KEY flag.
*33E2RET NCIf that routine exited with NC FLAG set, RETurn
33E6CP 1AHCheck A against 1AH to see if we have a SHIFT DOWN ARROW.
*33EBOR ASet the flags based on Register A
*33F2BIT 7,(HL)Test Bit 7 of the CONTROL KEY flag in RAM.
*33F6CP 2AHCheck A against a *.
*33FBAND (HL)Prepare to check for a BREAKby masking A against the memory contents of HL ...
*33FERET NZIf the result of the compare is NOT zero, then RETURN.
*33FFRST 28HIf we are here, then a BREAKwas hit, so call RST 28H to handle the BREAK.
*3400RETRETURN to Caller
*3401 - Model 4 Gen 1 - This is the BOOTSTRAP. Clears ports, checks for a BREAKkey and a Floppy Controller.
3401IM 1Set the INTERRUPT MODE to 1.
*3406LD B,0FHLet Register B = 0FH
*3408LD C,88HLet Register C = the CRT Controller Control Register Port
*340COUT (89H),ASend Register A to the CRT Controller Data Register
3410OUT (0E4H),AOutput A to Port E4H.
NOTE: Port E4H is the non-maskable interrupt latch. This is to clear the non-maskable interrupt status.
3412OR 20HOR 20H (0010 0000) against A to turn on Bit 5.
3414OUT (0ECH),AOutput 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.
3416LD A,81HLoad A with 81H (Decimal 129, Binary 10000001).
3418OUT (0F4H),AOutput 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.
341ALD A,D0HLoad A with D0H (Decimal: 208, Binary: 1101 0000).
341COUT (0F0H),AOutput "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
*341EPUSH BCUndertake a short delay of PUSHING BC, POPPING BC, and NOPing
*341FPOP BC
*3420NOP
3421LD A,04HLoad A with a 4 (Binary: 0000 0100).
3423OUT (0E0H),AOutput 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
3425LD A,0BHLoad A with 0BH (Binary: 00001011).
3427OUT (0F0H),AOutput 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).
3429Next we need to initialize some ports via a LDIR. The next 4 commands will move the 76 (4C) bytes from 36AAH to 4000H.
342C
342F
3432LDIR
3434Next we need to initialize more ports via a LDIR. The next 4 commands will move the 64 (40) bytes from 36F9H to 41E5H.
3437
343A
343DLDIR
3448IN 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
344AINC ABump the FDC Status by 1 (which would turn a FFH (128) into a 00H), making a Z flag mean "Disk Controller Not Ready".
344BIf the FDC Stats + 1 is zero, then we have no disk controller, so jump to Non-Disk BASIC at 3105H.
344EIf we are here, then we have a floppy controller, so set up for a loop of 65,535 times.
3451DEC BCDecrease BC by 1.
3452LD A,81HSet A to 81H (Decimal: 10000001).
3454OUT (0F4H),ASend 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.
3456LD A,B
OR CTo 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.
345BIN 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
345DBIT 2,ATest Bit 2 of A (the Floppy Status). If it is 0 then we haven't made it to track 0, otherwise we have.
345FStill 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.
3461LD E,05HSet up for a loop of 5 using Register E.
3466IN 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
3468BIT 01H,ACheck A to see if bit 1 (meaning "Drive Busy") is set.
346CDEC BCIf bit 1 is not set (meaning, the drive is NOT busy), reduce the counter (BC) by 1.
346DLD A,81HLoad A with 81H (Decimal: 129, Binary: 1000 0001).
346FOUT (0F4H),AOutput 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.
3471LD A,B
OR CTo 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.
3475If 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.
347BIf 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.
347DDEC EIf we are here, it found a diskette so next we need to find the index mark. First decrement E by 1.
3463HLD A,81HLoad A with 81H (Decimal: 129, Binary: 1000 0001).
3482OUT (0F4H),AOutput 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.
3484We 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.
3487LD (404AH),HL
348ALD A,C3HLoad A with C3H (Decimal: 195, Binary 1100 0011).
348CLD (4049H),APut the C3H into 4049H.
NOTE: 4049H is the Non-Maskable Interrupt Vector.
348FLD A,80HLoad A with 80H (Decimal: 128, Binary 1000 0000).
3491OUT (0E4H),AOutput "1000 0000" to Port E4H.
NOTE: Port E4H is the non-maskable interrupt latch.
3499LD A,01HPrepare to read Sector 1 by loading A with a 1.
349BOUT (0F2H),AOutput 1 to Port F2H.
NOTE: Port F2H is the Floppy Disk Controller Track Register.
349DLD A,80HPrepare to read a single sector by loading A with 80H (Decimal: 128, Binary: 1000 0000).
349FOUT (0F0H),AOutput "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).
34A1GOSUB to 37E1H to delay 3 instruction cycles, arguably the time it will take for the FDC to respond to the port command.
34A4IN 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
34A6AND 02HMask 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.
34ABINIInput the data byte.
34ADLD A,81HLoad A with 81H (Decimal: 129, Binary: 1000 0001) to select disk 0.
34AFOR 40HOR A with 40H (Binary: 0100 0000) to set A to double-density.
34B1OUT (0F4H),AOutput 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.
34B3INIInput the data byte.
34B5Loop 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.
34B8XOR AClear A and all flags.
34B9OUT (0E4H),ASend a 0 to Port E4H.
NOTE: Port E4H is the non-maskable interrupt latch. This is to clear the non-maskable interrupt status.
34BELD (4049H),HLLoad "45EDH" into 4049H.
NOTE: 4049H is the Non-Maskable Interrupt Vector.
34C1GOSUB to 37E1H to delay 3 instruction cycles, arguably the time it will take for the FDC to respond to the port command.
34C4IN 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
34C6POP HLClean up the Stack.
34C7AND 1CHMask 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.
34CCIf 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.
34CEIf 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.
34D1IN A,(E4H)Poll Port E4H into A.
NOTE: Port E4H is the Non-Maskable Interrupt Latch.
34D3BIT 5,ATest Bit 5 of Port E4 against A.
NOTE: Port E4H is the Non-Maskable Interrupt Latch.
*34DA - Model 4 Gen 1 - Part of the Keyboard routine.
*34DDAND 03HMask the SHIFT Register against 03H (Binary: 0000 0011) to keep only Bits 0 and 1 to check for shifts.
*34DFIf the masked A is 0, then we have no shifts, and skip the next instruction (to 34E3H).
*34E1SET 6,DSet BIT 6 of D to offset D for shifts.
LD A,(4019H)Load A with the contents of memory location 4019H to check for CAPS LOCK.
NOTE: 4019H is the CAPS LOCK TOGGLE.
*34E6OR ASet the flags.
*34E9SET 7,DSet 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.
*34EBLD A,(3880H)Put the contents of memory location 3880H into A to GET SHIFT(S).
*34EEAND 03HMask the SHIFT Register against 03H (Binary: 0000 0011) to keep only Bits 0 and 1 to check for shifts.
*34F0If the masked A is 0, then we have no shifts, and skip the next instruction (to 34F4H).
*34F2RES 7,DRESET 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.
34F7LD E,DWe need DE to be the OFFSET, so load E with D and ...
34F8LD D,00H... load D with 00.
34FAADD HL,DEAdd DE (the offset over the keyboard table) to HL (the keyboard table).
34FBLD A,(HL)Get the character pointed to by (HL) and put it into A.
*34FCRETRETurn 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.
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.
*3501RLCARotate the contents of A left one bit position. The contents of bit 7 are copied to the carry flag and bit 0.
*3504RLCARotate the contents of A left one bit position. The contents of bit 7 are copied to the carry flag and bit 0.
*3505If the CARRY FLAG is set, we have a non-graphic characters, so skip the next instructions.
*3507LD A,2EHOverwrite the current character held in Register A with a ., so that all graphic characters are printed as .'s.
Call the PRINT CHARACTER routine at 003BH (which sends the character in the A register to the printer).
*350FINC HLBump HL to the next character on the screen.
*3510BIT 6,HCheck 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).
*3515LD A,LPrepare to test of end of line by loading Register A with Register L.
*3516AND 03FHAND 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).
*3518If 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.
*351AGOSUB 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.
*351DLoop 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
*351FNOP
*3520NOP
*3521NOP
*3522NOP
*3523NOP
*3524NOP
*3525NOP
*3526NOP
*3527NOP
*3528NOP
*338EH - Model 4 Gen 2 - Jump Point for Keyboard Input.
*338ELD BC,3801H010138Load BC with 3801H (KEYBOARD ROW 0).
*3391LD HL,4036H213640Load HL with 4036H (BUFFER ROW 0).
*3394LD D,00H1600Load D with 0 (so D = ROW 0).
*3396LD A,(BC)0Aoad A with the contents held in (BC) to check the keyboard row.
*3397LD E,A5FLoad E with the contents held in (BC) to check the keyboard row.
*3398XOR (HL)AEXOR (HL) to set changed bits.
*3399LD (HL),E73Save the scan back into (HL).
*339AAND EA3Mask A with E (to mask the released keys).
Go to the next row
*339DINC D14Bump D so that D holds the NEXT row number.
*339EINC HL23Bump HL so that HL holds the NEXT buffer location.
*339FRLC CCB01We 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.
*33A1JP P,3396HF29633If 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
*33A4LD A,(41FDH)3AFD41If 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.
*33A7LD L,A6FLoad L with the contents of Register A.
*33A8LD A,(41FEH)3AFE41Load A with the memory contents of 41FEH.
NOTE: 41FEH is the saved IMAGE at the saved position in the keyboard scan data.
*33ABAND (HL)A6MASK A against (HL) to see if the previous keys are still pressed.
*33AFSBC HL,HLED62Zero HL by subtracting HL from HL.
*33B1LD (4201H),HL220142Load 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.
*33B7LD (41FFH),HL22FF41Load the memory location at 41FFH with 05DCH.
NOTE: 41FFH holds the keyboard scan repeat delay count.
*33BAXOR AAFClear A and all flags.
*33BBRETC9RETurn to Caller
*33BC - Model 4 Gen 2 - Keyboard Routine - If the same keys are still pressed then we need to deal with debounce.
*33BCLD E,A5FLoad E with A.
*33BDPUSH BCC5Save BC to the STACK.
*33C1CALL 0060HCD 60 00GOSUB 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.
*33C4POP BCC1Restore BC from the STACK.
*33C5LD A,(BC)0ALoad A with the memory contents pointed to by BC to re-check the keyboard.
*33C6AND EA3Compare A against E to check the pattern.
*33C7RET ZC8If not the same pattern then RETURN.
*33C8LD (41FEH),A32 FE 41If it is the same pattern then save A into (41FEH).
NOTE: 41FE is the SAVED IMAGE AT POSITION.
*33CBLD A,L7DLoad A with L (the scan position).
*33CCLD (41FDH),A32 FD 41Save A into (41FDH).
NOTE: 41FDH is the SAVED POSITION IN SCAN.
*33CFLD A,D7ALoad A with D (8 * ROW #).
*33D0RLA17Rotate 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.
*33D1RLA17Rotate 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.
*33D2RLA17Rotate 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.
*33D3LD D,A57Copy Register A into Register D
*33D4LD A,E7BCopy Register E into Register A
*33D5RRCA0FRotate A right one bit, with the contents of BIT 0 being put into BOTH the CARRY FLAG and BIT 7. D = 8* ROW # + KEY #.
*33D6RET CD8If the contents of BIT 0 of A was SET, RETurn
*33D7INC D14Bump D so that D holds the NEXT row number.
*33DA - Model 4 Gen 2 - Keyboard Routine.
*33DAPUSH HLE5Save HL to the top of the STACK.
*33DBLD HL,(4201H)2A0142Load HL with the repeat delay counter.
NOTE: 4201H is the REPEAT DELAY COUNTER.
*33DEINC HL23Bump HL.
*33DFLD (4201H),HL220142Load the memory location at 4201H with HL to clear the repeat counter.
NOTE: 4201H is the REPEAT DELAY COUNTER.
*33E2LD DE,(41FFH)ED5BFF41Load DE with the byte stored at 41FFH.
NOTE: 41FFH is the REPEAT DELAY COUNT.
*33E6SBC HL,DEED52Subtract with CARRY DE from HL.
*33E8POP DED1Restore old HL (which is what is in the stack) into DE.
*33ECXOR AAFClear A and all flags.
*33EDLD (DE),A12Put a 0 into the memory location pointed to (DE) to let the key be re-read.
*33EELD (4201H),HL220142Load the memory location at 4201H with HL to clear the repeat counter.
NOTE: 4201H is the REPEAT DELAY COUNTER.
*33F1LD L,96H2E96Load L with 96H to set a fast repeat count.
*33F3LD (41FFH),HL22FF41Save HL into the memory location at 41FFH.
NOTE: 41FFH is the REPEAT DELAY COUNT.
*33F9 - Model 4 Gen 2 - Enter NON-Disk BASIC.
*33FF - Model 4 Gen 2 Routine - Initialization - Process the CASS? Question
*33FFEIFBEnable Interrupts.
*3400CALL 3421HCD 21 34GOSUB to 3421H which loads A with a carrage return, and jumps to 0033H to display it.
*3403LD HL,05D1H21 D1 05Load HL with the address of the "CASS?" message.
*3406CALL 021BHCD 1B 02GOSUB to 021BH.
NOTE: 021BH will display the character at (HL) until a 03H is found.
*3409CALL 0049HCD 49 00GOSUB to 0049H.
NOTE: 0049H is the $KBWAIT routine which scans the keyboard and returns with the key pressed, if any, in register A.
*340CCP 0DHFE 0DCheck to see if Register A is holding a ENTER
*3410PUSH AFF5Save AF to the STACK.
NOTE: Register A currently holds the character pressed in response to the "CASS?" message.
*3411CALL 0033HCD 33 00GOSUB to 0033H pt display the character held in Register A at the current cursor position.
*3414POP AFF1Restore the answer to CASS? from the STACK into Register A.
*3415CP 48HFE 48Compare the answer to the CASS? Prompt held in Register A against with 48H (ASCII: H).
*3419CP 4CHFE 4CCompare the answer to the CASS? Prompt held in Register A against with 4CH (ASCII: L).
Set the flag for LOW SPEED CASSETTE
*341DXOR AAFSet A to 0.
*341ELD (4211H),A32 11 42Save 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
*3421LD A,0DH3E 0DPut a CARRIAGE RETURN into Register A
*3423JP 0033HC3 33 00JUMP 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.
*3426IM 1ED56Set the INTERRUPT MODE to 1.
*3428LD SP,407DH317D40Load the STACK POINTER with 407DH.
*342BLD B,0FH060FLet Register B = 0FH
*342DLD C,88H0E88Let Register C = the CRT Controller Control Register Port
*342FOUT (C),BED41Set the CRT Controller Control Register Port to the contents of B (which are decreasing as the loop progresses)
*3431OUT (89H),AD389Send Register A to the CRT Controller Data Register
*3435OUT (E4H),AD3E4Clear the Non-Maskable Interrupt Latch by sending the contents of Register A to Port E4H
*3437OR 20HF620OR 20H (0010 0000) against A to turn on Bit 5.
*3439OUT (ECH),AD3ECOutput 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.
*343BLD A,81H3E81Load A with 81H (Decimal 129, Binary 10000001).
*343DOUT (F4H),AD3F4Output 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.
*343FLD A,D0H3ED0Load A with D0H (Decimal: 208, Binary: 1101 0000).
*3441OUT (F0H),AD3F0Output "D0H" to the FDC Status Register at Port F0H. This resets the FDC and puts it in mode 1
*3443PUSH BCC5Undertake a short delay of PUSHING BC, POPPING BC, and NOPing
*3444POP BCC1Undertake a short delay of PUSHING BC, POPPING BC, and NOPing
*3445NOP00Undertake a short delay of PUSHING BC, POPPING BC, and NOPing
*3446LD A,04H3E04Load A with a 4 (Binary: 0000 0100).
*3448OUT (E0H),AD3E0Output 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.
*344ALD A,0BH3E0BLoad A with 0BH (Binary: 00001011).
*344COUT (F0H),AD3F0Output 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).
*344ELD HL,36AAH21AA36Next we need to initialize some ports via a LDIR. The next 4 commands will move the 76 (4C) bytes from 36AAH to 4000H.
*3451LD DE,4000H110040Set the LDIR Destination to 4000H
*3454LD BC,004CH014C00Set the number of bytes to move to 4CH
*3457LDIREDB0Move the 76 (4C) bytes from 36AAH to 4000H
*3459LD HL,36F9H21F936Next we need to initialize more ports via a LDIR. The next 4 commands will move the 64 (40) bytes from 36F9H to 41E5H.
*345CLD DE,41E5H11E541Set the LDIR Destination to 41E5H
*345FLD BC,0040H014000Set the number of bytes to move to 40H
*3462LDIREDB0Move the 64 (40) bytes from 36F9H to 41E5H.
The Model 4 ROM Student Network Edition ROM changes that to ...
*346AJP NZ,33F9HC2F933If we have a BREAK key then jump to Non-Disk BASIC at 33F9H.
*346DIN A,(F0H)DBF0We 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
*346FINC A3CBump the FDC Status by 1 (which would turn a FFH (128) into a 00H), making a Z flag mean "Disk Controller Not Ready".
*3470JP Z,33F9HCAF933If the FDC Stats + 1 is zero, then we have no disk controller, so jump to Non-Disk BASIC at 33F9H.
*3473LD BC,0000H010000If we are here, then we have a floppy controller, so set up for a loop of 65,535 times.
*3476DEC BC0BDecrease BC by 1.
*3477LD A,81H3E81Set A to 81H (Decimal: 10000001).
*3479OUT (F4H),AD3F4Send 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.
*347BLD A,B78To 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.
*347COR CB1
*3480IN A,(F0H)DBF0We 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
*3482BIT 2,ACB57Test Bit 2 of A (the Floppy Status). If it is 0 then we haven't made it to track 0, otherwise we have
*3484JR Z,3476H28F0Still 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.
*3486LD E,0AH1E0ASet up for a loop of 10 using Register E; this will be the number of tries to find an index mark.
*3488LD BC,0000H010000Set up for a loop of 65,536 using Register Pair BC.
*348BIN A,(F0H)DBF0Poll 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
*348DBIT 1,ACB4FCheck A to see if bit 1 (meaning "Drive Busy") is set.
*3491CALL 349BHCD9B34If 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.
*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.
*3496DEC E1DIf 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.
*3497JR NZ,34ACH2013So long as we have not run out of the loop of 10 tries, JUMP to 34ACH to find the index mark.
*349B - Model 4 Gen 2 Routine - We have exhausted our tries, so display DISKETTE and keep trying.
*349BDEC BC0BIf bit 1 is not set (meaning, the drive is NOT busy), reduce the counter (BC) by 1.
*349CLD A,81H3E 81Load A with 81H (Decimal: 129, Binary: 1000 0001).
*349EOUT (F4H),AD3 F4Output 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.
*34A0LD A,B78To 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.
*34A1OR CB1
*34A2RET NZC0If BC is not 0, then RETurn
*34A3POP HLE1Clear the STACK
*34A4LD HL,0277H21 77 02If 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.
*34A7CALL 021BHCD 1B 02GOSUB to 021BH. Note; 021BH will display the character at (HL) until a 03H is found.
*34AAJR 3486H18 DAIf 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.
*34ACLD BC,0000H010000Set up for a loop of 65,536 using Register Pair BC.
*34AFIN A,(F0H)DBF0Poll 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
*34B1BIT 1,ACB4FCheck the Floppy Disk Controller Status (held in Register A) to see if bit 1 (meaning "Drive Busy") is set.
*34B3JR Z,34BAH2805If bit 1 is not set (meaning, the drive is NOT busy), JUMP to 34BAH to restart the read attempts.
*34B5CALL 349BHCD9B34If we are here, then the drive was busy, so GOSUB to 349BH to display DISKETTE? and try again
*34B8JR 34AFH18F5JUMP back to the top of this routine and keep looking
*34BA - Model 4 Gen 2 Routine - Finish initializing the floppy disk boot.
*34BADEC E1DIf we are here, it found a diskette so next we need to find the index mark. First decrement E by 1.
We are going to try to read a sector
*34BDLD A,81H3E 81Load A with 81H (Decimal: 129, Binary: 1000 0001).
*34BFOUT (F4H),AD3 F4Output 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.
*34C1LD HL,34F5H21 F5 34We 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.
*34C4LD (404AH),HL22 4A 40Put the desired JUMP address of 34F5H into (404AH)
*34C7LD A,C3H3E C3Load A with C3H (Decimal: 195, Binary 1100 0011).
*34C9LD (4049H),A32 49 40Put the C3H into 4049H.
NOTE: 4049H is the Non-Maskable Interrupt Vector.
*34CCLD A,80H3E 80Load A with 80H (Decimal: 128, Binary 1000 0000).
*34CEOUT (E4H),AD3 E4Output "1000 0000" to the the non-maskable interrupt latch via Port E4H.
*34D0LD BC,00F3H01 F3 00Set BC to 0F3H.
NOTE: Port F3H is the Floppy Disk Controller Data Register.
*34D3LD HL,4300H21 00 43Set HL to 4300H, which is where the data is going to go.
*34D6LD A,01H3E 01Prepare to read Sector 1 by loading A with a 1.
*34D8OUT (F2H),AD3 F2Output 1 to the Floppy Disk Controller Track Register at Port F2H.
*34DALD A,80H3E 80Prepare to read a single sector by loading A with 80H (Decimal: 128, Binary: 1000 0000).
*34DCOUT (F0H),AD3 F0Output "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).
*34DECALL 30CAHCD CA 30GOSUB to 30CAH to delay 3 instruction cycles, arguably the time it will take for the FDC to respond to the port command.
*34E1IN A,(F0H)DB F0Poll 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
*34E3AND 02HE6 02Mask 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.
*34E8INIEDA2Input the data byte.
*34EALD A,81H3E81Load A with 81H (Decimal: 129, Binary: 1000 0001) to select disk 0.
*34ECOR 40HF640OR A with 40H (Binary: 0100 0000) to set A to double-density.
*34EEOUT (F4H),AD3F4Output 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.
*34F0INIEDA2Input the data byte.
*34F2JP 34EAHC3 EA 34Loop 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.
*34F5XOR AAFClear A and all flags.
*34F6OUT (E4H),AD3 E4Send a 0 to the non-maskable interrupt latch via Port E4H. This is to clear the non-maskable interrupt status.
*34F8LD HL,45EDH21 ED 45Now that the NMI jumped here, we need to set a new NMI jump, this time to 45EDH.
*34FBLD (4049H),HL22 49 40Load the destination location into (4049H) which is the Non-Maskable Interrupt Vector.
*34FECALL 30CAHCD CA 30GOSUB to 30CAH to delay 3 instruction cycles, arguably the time it will take for the FDC to respond to the port command.
*3501IN A,(F0H)DB F0Poll 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
*3503POP HLE1Clean up the Stack.
*3504AND 1CHE61CMask 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.
*3509JR 34BDH18B2If 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.
*350BJP NZ,4049HC24940If 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.
*350EIN A,(E4H)DBE4Poll the Non-Maskable Interrupt Latch at Port E4H into A.
*3510BIT 5,ACB6FTest Bit 5 of Port E4 against A.
*3512JR Z,350EH28FALoop back 2 instructions until it is set (i.e., not a zero).
*3517 - Model 4 Gen 2 Routine - Finish up initialization by filling 256 bytes into 4300H and then JUMPing there
*3517OR AB7Set the FLAGS based on Register A
*3518JP NZ,3179HC27931If A is not ZERO then Jump to 3179H to continuing initialization routine by setting up the RS-232
*351BLD HL,4300H210043Set HL to 4300H, which is where the data is going to go.
*351ECALL 3086HCD8630GOSUB to 3086 to Poll the UART and wait for the P FLAG to not be set and then CONTINUE at 306CH
*3521LD (HL),A77Store Register A into the memory location pointed to by Register Pair HL
*3522INC L2CBump Register L by 1
*3523JR NZ,351EH20F9So long as we have not overflowed L, LOOP back to 351EH
*3525JP 37E0HC3E037If 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).
*3528NOP00No Operation
**3517 - Model 4 ROM Student Network Edition - Finish up initialization
**3517CALL 3086HCD8630GOSUB to 3086 to simply poll the keyboard and mask for a "4" key
**351AJR NZ,3520H2004If the key isn't a "4" then restore BC and return to processing as a regular Model 4 would
**351CCALL 028DHCD8D02GOSUB to 028DH to check for a BREAK key.
**351FRETC9RETurn to CALLer
**3520POP BCC1Restore the contents at the top of the STACK into Register Pair BC
**3521JP 3179HC37931Jump to 3179H to continuing initialization routine by setting up the RS-232
**3524NOP00
**3525NOP00
**3526NOP00
**3527NOP00
**3528NOP00
3529H - Deal with the cursor.
352CPUSH DEPush that return address onto the Stack.
352DIN 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.
352FLD 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.
3532OR ASet the flags.
3535LD 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.
3538OR ATest A.
353BLoad HL with 401AH.
NOTE: 401AH is the memory location that stores the cursor blink count.
353EDEC (HL)Reduce the memory contents of (401AH) by one.
NOTE: 401AH is the memory location that stores the cursor blink count.
3541LD (HL),07HSet the cursor blink count to 7.
NOTE: 401AH is the memory location that stores the cursor blink count.
3543INC HLBump HL. This will increase HL from 401AH to 401BH.
NOTE: 401BH holds the cursor blink status - 0 = Off, Anything Else = On.
3544LD A,(HL)Poll the cursor blink status memory location and put the results into A.
3545AND 01HMask A against 0000 0001, to have only Bit 0 active.
3547XOR 01HXOR A with 01H.
3549LD (HL),APut the toggled cursor blink status into the appropriate memory location.
354ALD HL,(4020H)Poll (4020H) and put the result into HL.
NOTE: 4020H holds the current cursor position.
354DIf the current cursor position is 0, then it is off, so JUMP down 2 instructions to 3554H to make the cursor a blank (space).
354FLD A,(4023H)Load A with the memory contents of (4023H).
NOTE: 4023H holds the cursor character.
3552JUMP down to 3556H to skip the next instruction and continue this routine by displaying the character in A.
3554LD A,20HLoad 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.
3556LD (HL),APut the character held in A into the memory location pointed to by (HL).
NOTE: (HL) will hold the current cursor position.
355ADEC (HL)Decrease the number held at (4216H) by 1.
355BRET NZIf the number held at (4216H) is not zero, then RETURN.
355CLD (HL),1EHPut a 1EH (Decimal: 30) into (4216H).
NOTE: 4216H is the heartbeat counter.
355EINC HLBump HL by 1. HL will now point to 4217H, which is the memory location that holds the SECONDS.
355FLoad 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.
3562LD B,03HLoad B with a 03H, to set up a loop where we test seconds, minutes, and hours against their maximums.
3564INC (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.
3565LD 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.
3566SUB (HL)Compare the maximum to what we have by subtracting that maximum from the value pointed at in (HL).
3567RET NZIf there is no difference between what we have and the maximum then RETURN.
3568LD (HL),AIf 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).
3569INC HLBump 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.
356AINC DEBump 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.
356BLoop back to 3564H until the loop of 3 has been met, meaning that we have processed seconds, minutes, and hours.
356DINC HLBump HL one more time, to 421BH.
NOTE: 421BH holds the current DAY portion of the date.
356EINC (HL)Bump the DAY portion of the date.
356FINC HLBump HL one more time, to 421CH.
NOTE: 421BH holds the current MONTH portion of the date.
3570LD A,(HL)Get the month and put it into A.
3571DEC HLDecrease HL back to to 421BH.
NOTE: 421BH holds the DAY portion of the date.
3572DEC ADecrease A by one (to the previous month).
NOTE: DE currently points to the memory locations housing the of days in each month.
3573ADD A,EAdd E to A.
3574LD E,ALoad E with the result. E = E + A.
3575LD A,(DE)Poll the number of days in a month and put the result into A.
3576CP (HL)Compare A against (HL).
NOTE: If this is not a loop, the HL holds the day portion of the date.
3577RET NCIf 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.
3578LD 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.
3579CP 1EHCompare 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.
357BIf the carry flag isn't set then JUMP to 3583H to update the MONTH, but not the YEAR.
357DDEC HLDecrement HL to now point to 421AH.
NOTE: 421AH points to the current YEAR.
357ELD A,(HL)Load the YEAR into A.
357FINC HLBump HL to 421BH.
NOTE: 421BH points to the current DAY.
3580AND 03HMask A (which is holding the year) with 03H (Binary: 00000011) to test for a leap year.
3582RET ZRETURN if that mask showed that we are in the 4th year of a cycle (because 04 and higher are turned off).
3583LD (HL),01HPut a 1 into the memory location pointed to by HL (which is DAY).
3585INC HLBump HL to 421CH.
NOTE: 421BH points to the current MONTH.
3586INC (HL)Increase the current MONTH by 1.
3587LD A,(HL)Put the current MONTH into A.
3588SUB 0DHSubract 13 from A.
NOTE: This will test against a month 13. If A is < 13, then the CARRY FLAG will be set.
358ARET CIf it is NOT month 13 then RETURN to skip the next code which increases the YEAR.
358BLD (HL),01HIf we are here, then MONTH = 13, so set MONTH to 1.
358DDEC HLDecrement HL to 421BH.
NOTE: 421BH points to the current DAY.
358EDEC HLDecrement HL to 421AH.
NOTE: 421BH points to the current YEAR.
358FINC (HL)Bump the current year.
3590RETRETURN.
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.
3591LD 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.
3594BIT 0,ATest Bit 0 of A to see if the clock is on or off.
3596RET ZIf Bit 0 of A is ZERO, then return.
3597LD 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.
359ACP 1EHCompare the heartbeat counter against 1EH.
NOTE: A CP actually subtracts 1E from A without modifying A, but the flags are set accordingly.
359CRET NZIf the clock was not just updated, then RETURN.
359DIf 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.
35A3LD C,3AHLoad C with a :.
NOTE: This routine is also used to convert the date, and C will be swapped out to a /for that routine.
35A5LD B,03HLoad B with a 3.
NOTE: This is because we need to convert 3 numbers, so we will loop 3 times.
35A7LD 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.
35A8DEC DEDecrement DE to point to the next unit to be dealt with.
35A9LD (HL),2FHLoad the memory location pointed to by (HL) with 2F.
NOTE: 2F is a /which is also 1 character below a 0.
35ABINC (HL)Increase whatever is held in (HL). On the first iteration, this change the character at the screen location to a 0.
35ACSUB 0AHA = 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.
35B0ADD A,3AHIf 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.
35B2INC HLBump HL to point to the next location on the video screen. On the first iteration, this will be the 2nd digit of the HOUR.
35B3LD (HL),APut 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.
35B4INC HLBump HL to point to the next location on the video screen. On the first iteration, this will be the 3rd character.
35B5DEC BDecrement B to the next unit. On the first iteration, this will move from 3 to 2.
35B6RET ZIf we have processed all passes in the loop, RETURN.
35B7LD (HL),CIf we are here, then the routine has not yet looped 3 times, so put a :onto the screen.
35B8INC HLBump HL to point to the next location on the video screen.
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 /.
35BELD C,2FHLoad C with a /.
35C2H - Maskable Interrupt Handler.
35C2PUSH AFSave AF to the STACK.
35C3IN A,(E0H)Poll Port E0H which is the MASKABLE INTERRUPT LATCH and put the results into A.
35C5RRARotate 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.
35C9RRARotate 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.
35CAIf Bit 1 (which is now in the carry) is low then JUMP to 3369H (which is a cassette routine with E set to LOW).
35CDPUSH BCSave all the registers.
35CEPUSH DE
35CFPUSH HL
35D0PUSH IX
35D2PUSH IY
35D7PUSH HLPush HL to the STACK.
35D8RRARotate 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.
35D9If 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.
35DCRRARotate 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.
35DDIf 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.
35E0RRARotate 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.
35E1If 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.
35E4RRARotate 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.
35E5If 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.
35E8RRARotate 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.
35E9If 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.
35ECRRARotate 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.
35EDIf 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.
35F0POP HLRestore all registers.
35F1POP IY
35F3POP IX
35F5POP HL
35F6POP DE
35F7POP BC
35F8POP AF
35F9EIEnable Interrupts.
35FARETRETURN.
35FBH - RS-232 Initialization Routine. I'm [guessing] that IX is set to 41F5H
35FBDIDisable Interrupts so they don't interrupt this routine.
35FCIN 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)
35FECP FFHCompare A with FFH to see if the RS-232 exists.
3602XOR AFlip the RS-232 Port Results, just to get a non-zero result.
3603OUT (0E8H),AOutput A to port E8H.
NOTE: Port E8H is the RS-232 Status Register & Master Reset. Outputting ANYTHING to Port E8H resets the RS-232.
3605LD A,(IX+03H)Load the BAUD RATE CODE into A.
NOTE: 41F8H holds the baud rate code.
3608OUT (0E9H),AOutput 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
360ALD A,(IX+04H)Load the CONFIURATION CODE into A.
NOTE: 41F9 holds the RS-232 Configuration Code.
360DOR ASet the flags.
3610OUT (0EAH),AOutput 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)
3619LD A,(IX+05H)Load the WAIT SWITCH into A.
NOTE: 41FAH holds the RS-232 Wait Switch.
361COR ASet the flags.
361FSET 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
3623SET 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
362BOR ASet flags.
362ESET 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
3632SET 02H,(IY+04H)Set Bit 2 of 41F1H to set the ACTIVE FLAG.
3636IN 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
3638EIRe-Enable Interrupts.
3639RETRETURN.
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.
363AXOR AClear A and all flags.
363BLD B,04HLoad 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
363DLD C,0E8HLoad C with E8H.
363FOUT (C),ASend a 0 to the current port. On the first iteration, this is E8H (the RS-232 Status Register & Master Reset).
3641INC CBUMP C to the next port.
3647LD B,03HLoad B with 3 as a counter. 3 is for 3 bytes - 41E8H, 41E9H, and 41EAH.
3649LD (HL),00HLoad (HL) with 00H.
364BINC HLBump HL.
364ELoad HL with 41F0H.
NOTE: 41F0H is the 1 characer output buffer for the RS-232 Output DCB.
3651LD B,03HLoad B with 3 as a counter. 3 is for 3 bytes - 41F0H, 41F1H, and 41F2H.
3653LD (HL),00HLoad (HL) with 00H.
3655INC HLBump HL.
365AH - RS-232 Input Routine.
365ALoad IX with 41E5H.
NOTE: 41E5H is the DCB for RS-232 Input. 41E8H is the 1 Character RS-232 Input.
365EXOR AClear A and all Flags.
365FLD (IX+03H),ALoad (41E8H) with a Zero.
NOTE: 41E8H is the 1 Character RS-232 Input.
3662BIT 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.
3666RET ZIf the Driver is OFF, RETURN.
3667IN 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)
3669BIT 7,ATest Bit 7 of A.
NOTE: Bit 7 will be 1 if DATA READY (1=True).
366DBIT 1,(IX+04H)Test Bit 1 of 41E9H.
NOTE: Bit 1 of 41E9 contains the WAIT/NO WAIT of the RS-232 Input DCB.
3671RET ZIf its NO WAIT then RETURN, otherwise continue (to keep polling).
3677JUMP to 4203H if the BREAKkey was pressed.
NOTE: 4203H JUMPS to 022EH and is the break vector for tape and RS-232.
367AIN 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.
367CLD (IX+03H),ALoad (41E8H) with a A (the data from Port EBH).
NOTE: 41E8H is the 1 Character RS-232 Input.
367FRETRETURN.
3680H - RS-232 Output Routine.
3680Note. 41EDH is the RS-232 Output DCB, and 41F1H holds DRIVER ON/OFF in BIT 2, and WAIT/NO WAIT in BIT 1.
3684BIT 2,(IX+04H)Test Bit 2 of 41F1H to see if the RS-232 is active.
3688RET ZIf the RS-232 is NOT active, RETURN.
3689IN 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)
368BBIT 6,ATest Bit 6 of Port EAH to see READY TO SEND.
368FBIT 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.
3693RET ZIf RS-232 is NOT active, RETURN.
3699JUMP to 4203H if the BREAKkey was pressed.
NOTE: 4203H JUMPS to 022EH and is the break vector for tape and RS-232.
369CLD A,(IX+03H)Load A with the memory contents of 41F0H.
NOTE: 41F0H is RS-232 output buffer byte.
369FOR ATest A and Set Flags.
36A0If not zero, then there is a character in the buffer, so skip the next instruction and leave that byte in A.
36A2LD A,CLoad A with C [GET CHAR FROM DISPATCHER].
36A3OUT (0EBH),ASend 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.
36A5LD (IX+03H),00HLoad memory contents of the RS-232 output byte (at 41F0H) with a 0.
36A9RETRETURN.
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
36D5FFH
36D600HThis 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.
36D7
36DARST 00H
36DBNOP
36DCNOP
36DDXOR A
36DERET
36DFNOP
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
36F4NOP
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-370006 1BTwo 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-37061B 02 21 30 00 00 00
3707-370808 00Two 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-370E02 1B 30 55 6C FF
370F-371000 00Two 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-371F00 00 FF FF 00 00 C3 2E 02 C3 FA 35 C3 FA 35
3720-373041 32 03 32 28 03 3C 04 00 00 1E 00 00 00 00 00 00
3731-373300 00 00Three 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-373800 00 00 00 FF
*3739H - Model 4 Gen 1
*373FLD D,00HLoad D with 0 (so D = ROW 0).
*3742LD E,ALoad E with the contents held in (BC) to check the keyboard row.
*3743XOR (HL)XOR (HL) to set changed bits.
*3744LD (HL),ESave the scan back into (HL).
*3745AND EMask A with E (to mask the released keys).
*3748 - Model 4 Gen 1 routine to Go to the next Keyboard row
*3748INC DBump D so that D holds the NEXT row number.
*3749INC HLBump HL so that HL holds the NEXT buffer location.
*374ARLC CWe 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.
*374CIf 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
*374FLD 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.
*3752LD L,ALoad L with the A.
*3753LD 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.
*3756AND (HL)MASK A against (HL) to see if the previous keys are still pressed.
*375ASBC HL,HLZero HL by subtracting HL from HL.
*375CLD (4201H),HLLoad 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.
*3762LD (41FFH),HLLoad the memory location at 41FFH with 05DCH.
NOTE: 41FFH holds the keyboard scan repeat delay count.
*3765XOR AClear A and all flags.
*3766RETRETURN.
*3767 - Model 4 Gen 1 routine to Keyboard Repeat - Jumps Here if the same keys are still pressed.
*3768PUSH BCSave BC to the STACK.
*376CGOSUB 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.
*376FPOP BCRestore BC from the STACK.
*3770LD A,(BC)Load A with the memory contents pointed to by BC to re-check the keyboard.
*3771AND ECompare A against E to check the pattern.
*3772RET ZIf not the same pattern then RETURN.
*3773LD (41FEH),AIf it is the same pattern then save A into (41FEH).
NOTE: 41FE is the SAVED IMAGE AT POSITION.
*3776LD A,LLoad A with L (the scan position).
*3777LD (41FDH),ASave A into (41FDH).
NOTE: 41FDH is the SAVED POSITION IN SCAN.
*377ALD A,DLoad A with D (8 * ROW #).
*377BRLARotate 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.
*377CRLARotate 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.
*377DRLARotate 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.
*377ELD D,AD = A
*377FLD A,EA = E
RRCARotate A right one bit, with the contents of BIT 0 being put into BOTH the CARRY FLAG and BIT 7. D = 8* ROW # + KEY #.
*3781RET CIf the contents of BIT 0 of A was SET, RETurn
*3782INC DINC DBump D so that D holds the NEXT row number.
*3785 - Model 4 Gen 1 routine to Keyboard Repeat - Jumps Here if the same keys are still pressed.
*3786LD HL,(4201H)Load HL with the repeat delay counter.
NOTE: 4201H is the REPEAT DELAY COUNTER.
*3789INC HLBump HL.
*378ALD (4201H),HLLoad the memory location at 4201H with HL to clear the repeat counter.
NOTE: 4201H is the REPEAT DELAY COUNTER.
*378DLD DE,(41FFH)Load DE with the byte stored at 41FFH.
NOTE: 41FFH is the REPEAT DELAY COUNT.
*3791SBC HL,DESubtract with CARRY DE from HL.
*3793POP DERestore old HL (which is what is in the stack) into DE.
*3797XOR AClear A and all flags.
*3798LD (DE),APut a 0 into the memory location pointed to (DE) to let the key be re-read.
*3799LD (4201H),HLLoad the memory location at 4201H with HL to clear the repeat counter.
NOTE: 4201H is the REPEAT DELAY COUNTER.
*379CLD L,96HLoad L with 96H to set a fast repeat count.
*379ELD (41FFH),HLSave HL into the memory location at 41FFH.
NOTE: 41FFH is the REPEAT DELAY COUNT.
*37A4 - Model 4 Gen 1 routine jumped to from the the middle of the tokenize routine.
CP 22HIf 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 ".
*37A6If the character in register A is not a ", then JUMP forward a few instructions to 37B2H.
*37A8LD A,(409FH)Load A with the memory contents of 409FH.
NOTE: 409FH is the DATA FLAG.
*37ABXOR 01HXOR 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.
*37ADLD (409FH),ALoad memory location 409FH with a the XOR'd results.
NOTE: 409FH is the DATA FLAG.
*37B0LD A,22HLoad A with 22H, which is a ".
CP 3AHCompare 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.
*37B4If A is 3AH then JUMP to 06AAH.
NOTE: 06AAH is in the middle of the KEYBOARD DRIVER ENTRY ROUTINE.
*37B7LD A,(409FH)Otherwise, Load A with the memory contents of 409FH.
NOTE: 409FH is the DATA FLAG.
*37BBRRARotate 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.
*3792If Bit 7 of the DATA FLAG was set, then JUMP to 06A8H.
NOTE: 06A8H is in the middle of the KEYBOARD DRIVER ENTRY ROUTINE.
*37BERLARotate 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.
*37BFJUMP 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$)
RST 10HCall 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.
*37C3PUSH HLSave HL (the current position) to the STACK.
*37C4LD A,11HLoad A with 11H (Decimal: 17).
NOTE: This is to set up for a 17 Byte String.
*37C9LD HL,(40D4H)Load HL with the memory contents of (40D4H).
NOTE: 40D4 is the string pointer.
*37CFLD (HL),20HLoad the memory location pointed to by HL with a SPACE.
*37D1INC HLIncrement HL to move 1 character over.
*37D8 - Model 4 Gen 1 routine to Toggle Caps Lock.
*37DDXOR (HL)Invert the contents of the (4019H).
*37DELD (HL),AStore the inverted (i.e., toggled) contents of (4019H) back into (4019H)
*37E0RETRETurn to Caller.
*37E1 - Model 4 Gen 1 routine to do a very short delay routine
*37E5 - Model 4 Gen 1 routine Unused Code
*37E5NOP
*37E6NOP
*37E7NOP
*37E8RST 38H
*37E9RST 38H
*37EANOP
*37EBH - Model 4 Gen 1 routine to display the Copyright Message
*37F4 - Model 4 Gen 1 routine to enable the TIME$ Command
*37F7LD (4177H),HLLoad 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.
*37F7JUMP to 022EH to continue.
NOTE: 022EH will enable interrupts, show the READY prompt, and RETURN.
*37FD - Model 4 Gen 1 unused Code
*37FDPUSH HL
*37FEPUSH HL
*37FFRST 38H
*3739H - Model 4 Gen 2 - Part of the Keyboard Routine to check for SHIFT and CAPS LOCK and deal with them accordingly.
*3739LD A,(3880H)3A 80 38Load A with the value held at 3880H (which are the SHIFT keys)
*373CAND 03HE6 03Mask the SHIFT Register against 03H (Binary: 0000 0011) to keep only Bits 0 and 1 to check for shifts.
*373EJR Z,3742H28 02If the masked A is 0, then we have no shifts, and skip the next instruction (to 3742H).
*3740SET 6,DCB F2Set BIT 6 of D to offset D for shifts.
*3742LD A,(4019H)3A 19 40Load A with the contents of memory location 4019H to check for CAPS LOCK.
NOTE: 4019H is the CAPS LOCK TOGGLE.
*3745OR AB7Set the flags.
*3746JR Z,3753H28 0BIf the ZERO flag is set then there is NO CAPS LOCK so JUMP to 3753H to skip the next intructions.
*3748SET 7,DCB FASet 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.
*374ALD A,(3880H)3A 80 38Put the contents of memory location 3880H into A to GET SHIFT(S).
*374DAND 03HE6 03Mask the SHIFT Register against 03H (Binary: 0000 0011) to keep only Bits 0 and 1 to check for shifts.
*374FJR Z,3753H28 02If the masked A is 0, then we have no shifts, and skip the next instruction (to 3753H).
*3751RES 7,DCB BARESET 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.
*3753LD HL,304EH21 4E 30Load HL with 304EH (the KEYBOARD TABLES).
*3756LD E,D5AWe need DE to be the OFFSET, so load E with D and ...
*3757LD D,00H16 00... load D with 00.
*3759ADD HL,DE19Add DE (the offset over the keyboard table) to HL (the keyboard table).
*375ALD A,(HL)7EGet the character pointed to by (HL) and put it into A.
*375BRETC9RETurn 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
*375CCP 22HFE 22If 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 ".
*375EJR NZ,376AH20 0AIf the character in register A is not a ", then JUMP forward a few instructions to 376AH.
*3760LD A,(409FH)3A 9F 40Load A with the memory contents of 409FH.
NOTE: 409FH is the DATA FLAG.
*3763XOR 01HEE 01XOR 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.
*3765LD (409FH),A32 9F 40Load memory location 409FH with a the XOR'd results.
NOTE: 409FH is the DATA FLAG.
*3768LD A,22H3E 22Load A with 22H, which is a ".
*376ACP 3AHFE 3ACompare 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.
*376CJP NZ,06AAHC2 AA 06If A is NOT a : then JUMP to 06AAH.
NOTE: 06AAH is in the middle of the KEYBOARD DRIVER ENTRY ROUTINE.
*376FLD A,(409FH)3A 9F 40Otherwise, Load A with the memory contents of 409FH.
NOTE: 409FH is the DATA FLAG.
*3772RRA1FRotate 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.
*3773JP C,06A8HDA A8 06If Bit 7 of the DATA FLAG was set, then JUMP to 06A8H.
NOTE: 06A8H is in the middle of the KEYBOARD DRIVER ENTRY ROUTINE.
*3776RLA17Rotate 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.
*3777JP 06A3HC3 A3 06JUMP 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
*377ALD (IX+05H),05HDD 36 05 00Load the memory location pointed to by IX+5 with a 05H.
NOTE: IX+5 is the number of characters printed.
*377EINC (IX+04H)DD 34 04Bump the number of lines already printed (which is tracked in IX+04H)
*3781LD A,(IX+04H)DD 7E 04Fetch the number of lines already printed (which is tracked in IX+04H) into Register A
*3784CP (IX+03H)DD BE 03Compare the number of lines already printed (held in Register A) against the number of lines left in the page (tracked in IX+03H)
*3787RET NZC0If the number of lines already printed doesn't match the number of lines left in the page then RETURN
*3788LD (IX+04H),04HDD 36 04 01If they DO match, then set the number of lines already printed to 04H
*378CRETC9RETurn 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
*378DLD A,C79LET Register A = Register C.
*378ECP E0HFE E0Compare 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
*3790JP NC,0043HD2 43 00If 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).
*3793CP C0HFE C0If 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
*3795JP C,0063HDA 63 00If 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).
*3798SUB C0HD6 C0If 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.
*379ALD B,00H06 00LET Register B = 00H
*379CLD C,A4FLET Register C = Register A.
*379DLD HL,(4220H)2A 20 42LET Register Pair HL the memory contents of 4220H and 4221H.
NOTE: 4220H-4221H is the destination device nName for ROUTE Routine. 2 Bytes
*37A0ADD HL,BC09LET HL = HL + BC
*37A1LD C,(HL)4ELET Register C = the contents of the memory location pointed to by Register Pair HL.
*37A2JP 0063HC3 63 00JUMP 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
*37A5LD HL,3C00H21 00 3CLoad HL with the memory location for the beginning of the video RAM.
*37A8LD A,(HL)7EPut the character at the screen location stored in HL into A.
*37A9RLCA07Rotate 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.
*37ACRLCA07Rotate 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.
*37ADJR C,37B3H38 04If 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
*37AFLD A,2EH3E 2ELoad Register A with a . which is the character which will print instead of non-printable characters.
*37B3LD A,(HL)7EReplace the non-printable character with a .
*37B4CALL 003BHCD 3B 00Call the PRINT CHARACTER routine at 003B (which sends the character in the A register to the printer).
*37B7INC HL23Bump HL to the next character on the screen.
*37B8BIT 6,HCB 74Check 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).
*37BDLD A,L7DTest of end of line by loading A with L.
*37BEAND 3FHE6 3FAND 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).
*37C0JR NZ,37A8H20 E6If 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
*37A5 - Model 4 Gen 2 - Screen Print Routine BUT exits witH Register A at 0 and all FLAGS cleared
*37C7CALL 37A5HCD A5 37GOSUB to 37A5H for the screen print
*37CAXOR AAFClear Register A and all the flags
*37CBRETC9RETurn to CALLer
*37CC - Model 4 Gen 2 - TOGGLE the CAPS LOCK
*37CCLD A,01H3E 01Load Register A with an 01H
*37CELD HL,4019H21 19 40Load HL with 4019H, which is the CAPS LOCK TOGGLE in the keyboard DCB.
*37D1XOR (HL)AEXOR the contents of the CAPS LOCK TOGGLE in RAM against 01H; this effectively toggles it
*37D2LD (HL),A77Put the toggled CAPS LOCK TOGGLE back into RAM where such thing is stored
*37D3XOR AAFClear Register A and Clear all FLAGs
*37D4RETC9RETurn 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.
*37D5PUSH AFF5Preserve Registers A and F to the top of the STACK
*37D6IN A,(EAH)DB EAPoll the RS-232 UART Control Register/Status Register (via Port EAH) into A.
*37D8BIT 6,ACB 77Test Bit 6 of the RS-232 Register. It will be 1 if READY TO SEND
*37DAJR Z,37D6H28 FAIf the UART reported that READY TO SEND is FALSE, LOOP back to 37D6 and poll again.
*37DCPOP AFF1Restore Registers A and F from the top of the STACK
*37DDOUT (EBH),AD3 EBSend the contents held in Register A to the RS-232C Data Register (via Port E8H) so as to send A to the RS-232C.
*37DFRETC9RETurn 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).
*37E0LD A,6DH3E 6DLoad Register A with a 6DH (Binary: 0110 1101)
*37E2OUT (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)
*37E4JP (HL)E9Jump to whatever (HL) is pointing to.
*37E5 - Model 4 Gen 2 - UNUSED CODE.
*37E5NOP00
*37E6NOP00
*37E7NOP00
*37E8RST 38HFF
*37E9RST 38HFF
**37D5-37E9 - Model 4 ROM Student Network Edition - Replace all the above with NOPs
*37EA - Model 4 Gen 2 - BASIC TIMES (DATE$+" "+TIME$)
*37EARST 10HD7Call the EXAMINE NEXT SYMBOL routine at RST 10H.
*37EBPUSH HLE5Save HL (the current position) to the STACK.
*37ECLD A,11H3E 11Load A with 11H (Decimal: 17) to set up for a 17 Byte String.
*37F1LD HL,(40D4H)2A D4 40Load HL with the memory contents of (40D4H), which is the string pointer.
*37F7LD (HL),20H36 20Load the memory location pointed to by HL with a SPACE.
*37F9INC HL23Increment HL to move 1 character over.
This routine multiplies the ACCumulator by 10. Every register is modified.
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.
“SIGNS”
This routine will take a signed integer held in Register A and turn it into a floating point number. All registers are modified.
This routine will float the singed number in B/A/D/E. All registers are modified.
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
This routine will negate any value in the ACCumulator. Every Register is affected.
| Variable Type | Flags | Register A |
| Integer | NZ/C/M/E | -1 |
| String | Z/C/P/E | 0 |
| Single Precision | NZ/C/P/O | 1 |
| Double Precision | NZ/NC/P/E | 5 |
This routine will negate the single or double precision number in the ACCumulator. Registers A, H, and L are affected.
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
This routine will convert a signed number (held in Register A) into an integer.
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.
| Variable Type | Flags | Register A |
| Integer | NZ/C/M/E | -1 |
| String | Z/C/P/E | 0 |
| Single Precision | NZ/C/P/O | 1 |
| Double Precision | NZ/NC/P/E | 5 |
This routine finds the sign of the value held at (HL). Only Register A is altered.
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.
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.
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!
“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
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
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
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
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))
This routine is similar to 9D2H above. The only difference is that it moves data in the opposite direction. ((HL) = (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 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)
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.
INC HL23
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.
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.
| Variable Type | Flags | Register A |
| Integer | NZ/C/M/E | -1 |
| String | Z/C/P/E | 0 |
| Single Precision | NZ/C/P/O | 1 |
| Double Precision | NZ/NC/P/E | 5 |
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.
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
Note: 4127H-412EH holds ARG (a/k/a REG 2)
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.
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.
| Variable Type | Flags | Register A |
| Integer | NZ/C/M/E | -1 |
| String | Z/C/P/E | 0 |
| Single Precision | NZ/C/P/O | 1 |
| Double Precision | NZ/NC/P/E | 5 |
This routine will convert a single precision number to an integer. Every register is affected.
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
Note: 40AFH holds Current number type flag. This is the entry point from the CONDS routine
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.
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
| Variable Type | Flags | Register A |
| Integer | NZ/C/M/E | -1 |
| String | Z/C/P/E | 0 |
| Single Precision | NZ/C/P/O | 1 |
| Double Precision | NZ/NC/P/E | 5 |
Convert a double-prevision number to single-precision. Every register is affected.
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.
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.
| Variable Type | Flags | Register A |
| Integer | NZ/C/M/E | -1 |
| String | Z/C/P/E | 0 |
| Single Precision | NZ/C/P/O | 1 |
| Double Precision | NZ/NC/P/E | 5 |
Convert a single precision number to double precisions. Modifies Registers A, H, and L.
Note: 411DH-4124H holds ACCumulator
This routine will force the ACCUmlator to be a STRING. Only Register A is modified.
| Variable Type | Flags | Register A |
| Integer | NZ/C/M/E | -1 |
| String | Z/C/P/E | 0 |
| Single Precision | NZ/C/P/O | 1 |
| Double Precision | NZ/NC/P/E | 5 |
This is the entry point for the TM ERROR
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).
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.
– “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
| Variable Type | Flags | Register A |
| Integer | NZ/C/M/E | -1 |
| String | Z/C/P/E | 0 |
| Single Precision | NZ/C/P/O | 1 |
| Double Precision | NZ/NC/P/E | 5 |
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
| Variable Type | Flags | Register A |
| Integer | NZ/C/M/E | -1 |
| String | Z/C/P/E | 0 |
| Single Precision | NZ/C/P/O | 1 |
| Double Precision | NZ/NC/P/E | 5 |
Greated Integer function for double-precision numbers. All registers are affected.
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.
0BAF
OR C78 B1
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
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
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).
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.
0BFE
LD C,L44
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.
This is the entry from IDIV. The next instructions test to see if the result is => 32768 or is -32768.
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.
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.
Integer Negation Routine. All registers are altered.
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
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
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.
Next we are going to switch ARG and the ACCumulator.
This routine will round the ACCumulator. Registers A, B, H, and L are affected.
0D1C
DEC HL2B
Note: 411DH-4124H holds ACCumulator
Note: 4127H-412EH holds ARG (a/k/a REG 2)
Note: 411DH-4124H holds ACCumulator
This routine subtracts numbers in the pure version. This needs to be done in two subroutines since the ROM cannot be modified.
Note: 4127H-412EH holds ARG (a/k/a REG 2)
Note: 411DH-4124H holds ACCumulator
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.
This routine wwill shift the double precision value held in the ACCumulator to the right once.
This is the entry from DADD and DMULT.
This routine will rotate the ACCumulator left one. Register A, C, H, and L are affected.
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.
This routine handles multiplying by zero.
Double precision divide routine. Divides the ACCumulator by 10. All registers are affected.
Note: 4127H-412EH holds ARG (a/k/a REG 2)
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
0DF0
INC (HL)34
0E18
DEC A3C
Note: 411DH-4124H holds ACCumulator
This routine will transfer the double prevision number held in the ACCumulator to FBUFFR for the DMULT and DDIV routines. All registers are affected.
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
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.
F6 AFF6 AF
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).
“ASTOR”
This routine is the same as E65H above, except that it fixes REG 1 and NTF to the smallest possible number type.
0E83H – Process a + or – at the location of the current input buffer.
0EA4H – Inside the ASCII TO BINARY CONVERTER routine. Process a E at the location of the current input buffer.
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.
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.
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.
This “FINE2” routine will multiply or divide by 10 the correct number of times. If A=0 the number is an integer.
Next we need to put the correct sign on the number.
Next we want -32768 to be an integer (it would be single precision at this point)
| 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 |
| Double Precision | NZ/NC/P/E and A is 5. |
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.
| Variable Type | Flags | Register A |
| Integer | NZ/C/M/E | -1 |
| String | Z/C/P/E | 0 |
| Single Precision | NZ/C/P/O | 1 |
| Double Precision | NZ/NC/P/E | 5 |
| Variable Type | Flags | Register A |
| Integer | NZ/C/M/E | -1 |
| String | Z/C/P/E | 0 |
| Single Precision | NZ/C/P/O | 1 |
| Double Precision | NZ/NC/P/E | 5 |
This 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.
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.
| Variable Type | Flags | Register A |
| Integer | NZ/C/M/E | -1 |
| String | Z/C/P/E | 0 |
| Single Precision | NZ/C/P/O | 1 |
| Double Precision | NZ/NC/P/E | 5 |
This subroutine divides a number by 10 once. FIN and FOUT use this routine. Registers A, B, and C are affected.
| Variable Type | Flags | Register A |
| Integer | NZ/C/M/E | -1 |
| String | Z/C/P/E | 0 |
| Single Precision | NZ/C/P/O | 1 |
| Double Precision | NZ/NC/P/E | 5 |
This 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.
| Variable Type | Flags | Register A |
| Integer | NZ/C/M/E | -1 |
| String | Z/C/P/E | 0 |
| Single Precision | NZ/C/P/O | 1 |
| Double Precision | NZ/NC/P/E | 5 |
If we are here, then we re packing the next digit of an integer.
0F41
LD E,L54
At this point, the number has shifted over to make room for the new digit in the ones place.
This routine handles 32768 and 32769
Convert integer digits into single precision digits
Determine if we have a single precision or a double prevision number
These next 2 instruction set up BCDE to hold “1000000”
- A=0 if ACCumulator = BCDE
- A=1 if ACCumulator>BCDE; and
- A=FFH if ACCumulator<BCDE.
The routine will convert a 7 digit single precision number into a double precision number
This routine will pack in a digit into a double precision number
This is a subroutine for FIN and for LOG
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.
This routine is to output a floating point number.
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).
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.
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.
- Bit 7:
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.
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.
This routine will zero suppress the digits in FBUFFR and asterisk fill and zero suppress if necessary.
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)
Note: 40D8H-40D9H holds the temporary storage location
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
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)
The FOFRS2 routine will suppress trailing zeroes.
At this point, all trailing zeroes are now gone and HL points to the last non-zero character.
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.
This routine will calculate the two digit exponent.
This routine will print a free format zero.
Note: 4130H-4149H holds an internal print buffer
This routine will print a number in fixed format.
If we are here then we are going to print an integer in fixed format/fixed point notation.
This routine will finish up the printing of a fixed format number.
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.
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] |
| 0 | a zero | [mandatory] |
| . | a decimal point | [mandatory] |
| 0-9 | another 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.
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.
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.
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.
If the number is too big for the field, we wind up here to deal with that.
This is where the PRINT USING routine will print a single or double precision number in a fixed format
If we are here, then we are printing a DOUBLE PRECISION number in fixed format/fixed point notation
This routine will print a number which is greaster than 10^16 in free format with a percent sign
This routine will print a SINGLE PRECISION number in fixed format/fixed point notation
The results are stored in A as follows:
| If ACCumulator = BCDE | A=00 |
| If ACCumulator > BCDE | A=01 |
| If ACCumulator < BCDE | A=FF |
This routine will print a SINGLE PRECISION or DOUBLE PRECISION number in fixed format/fixed point notation
This routine will print a number that has no fractional digits
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.
This routine will print a SINGLE PRECISION or DOUBLE PREVISION number that has fractional digits
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.
This routine will print a number without integer digits.
Note: 40F3H-40F4H is a temporary storage location
This routine will print trailing zeroes.
This routine will print an integer in fixed format/floating point notation.
This routine will print a SINGLE or DOUBLE PRECISION number in fixed format/floating point notation.
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.
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.
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.
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.
The next two instructions load BCDE with 99999.95 so as to check to see if the number in FAC is too big.
1228-122A
122B-122D
122E-1230
The results are stored in A as follows:
| If ACCumulator = BCDE | A=00 |
| If ACCumulator > BCDE | A=01 |
| If ACCumulator < BCDE | A=FF |
1233-1235
1236-1238
1239-123B
123C
123D-123F
1240
1241-1242
1243
1244-1246
1247
1248-124A
124B
124C
124D
This routine will see if the number in the ACCumulator is small enough yet
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.
The next two instructions load BCDE with 999999.5 to see if the number in the FAC is too large.
The results are stored in A as follows:
| If ACCumulator = BCDE | A=00 |
| If ACCumulator > BCDE | A=01 |
| If ACCumulator < BCDE | A=FF |
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.
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.
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.
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.
Note: 40F3H-40F4H is a temporary storage location
Part of the above routine, jumped here to test to see if a comma needs to be placed at (HL).
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.
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.
Top of a loop to convert the next digit. It is executed “A” times.
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.
This routine is to calculate the next digit of the number.
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.
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.
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).
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
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
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
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).
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)).
/0 ERROR entry point
The results are stored in A as follows:
| If ACCumulator = BCDE | A=00 |
| If ACCumulator > BCDE | A=01 |
| If ACCumulator < BCDE | A=FF |
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.
This represents 1/6, -1/5, 1/4, -1/3, 1/2, -1, and 1– “EXPCON”
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.
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
PUSH BCD5
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
POP DEC1
Note: 40AAH-40ADH holds the random number seed
Note: 40AAH-40ADH holds the random number seed
Note: 4125H-4126H is used by floating point routines
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)
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:
- Assume X <= 360 degrees.
- Recompute x as x=x/360 so that x=< 1.
- If x <= 90 degrees go to step 7.
- If x <= 180 degrees then x=0.5-x and then go to step 7.
- If x <= 270 degrees then x=0.5-x.
- Recompute x as x=x-1.0.
- Compute SIN using the power series.
“SIN”
POP DEC1
POP DEC1
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)
POP HLC1
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:
- 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.
- 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.
- Evaluate the series: (((x^2*c0+c1) x^2+c2) . c8)x
- If the flag from step 1 is not set, then invert the sign of the series result.
- If the original value is < 1 then return to the caller. Otherwise, compute pi/2-value from step 4 and then return.
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
| ABS | D9 | 0977 | | | AND | D2 | 25FD |
| ASC | F6 | 2A0F | | | ATN | E4 | 15BD |
| AUTO | B7 | 2008 | | | CDBL | F1 | 0ADB |
| CHR$( | F7 | 2A1F | | | CINT | EF | 0A7F |
| CLEAR | B8 | 1E7A | | | CLOAD | B9 | 2C1F |
| CLOSE | A6 | 4185 | | | CLS | 84 | 01C9 |
| CMD | 85 | 4173 | | | CONT | B3 | 1DE4 |
| COS | El | 1541 | | | CSAVE | BA | 2BF5 |
| CSNG | F0 | 0ABl | | | CVD | E8 | 415E |
| CVI | E6 | 4152 | | | CVS | E7 | 4158 |
| DATA | 88 | 1F05 | | | DEF | DD | 415B |
| DEFDBL | 9B | 1E09 | | | DEFINT | 99 | 1E03 |
| DEFSNG | 9A | 1E06 | | | DEFSTR | 98 | 1E00 |
| DELETE | B6 | 2BC6 | | | DIM | 8A | 2608 |
| EDIT | 9D | 2E60 | | | ELSE | 95 | 1F07 |
| END | 80 | 1DAE | | | EOF | E9 | 4161 |
| ERL | C2 | 24DD | | | ERR | C3 | 24CF |
| ERROR | 9E | 1FF4 | | | EXP | E0 | 1439 |
| FIELD | A3 | 417C | | | FIX | F2 | 0B26 |
| FN | BE | 4155 | | | FOR | 81 | 1CA1 |
| FRE | DA | 27D4 | | | GET | A4 | 4174 |
| GOSUB | 91 | 1EB1 | | | GOTO | 5D | 1EC2 |
| IF | 8F | 2039 | | | INKEY$ | C9 | 019D |
| INP | DB | 2AEF | | | INPUT | 89 | 219A |
| INSTR | C5 | 419D | | | INT | D8 | 0B37 |
| KILL | AA | 4191 | | | LEFT$ | F8 | 2A61 |
| LEN | F3 | 2A03 | | | LET | 8C | 1F21 |
| LINE | 9C | 41A3 | | | LIST | B4 | 2B2E |
| LLIST | B5 | 2B29 | | | LOAD | A7 | 4188 |
| LOC | EA | 4164 | | | LOF | EB | 4167 |
| LOG | DF | 0809 | | | LPRINT | AF | 2067 |
| LSET | AB | 4197 | | | MEM | C8 | 27C9 |
| MERGE | A8 | 418B | | | MID$ | FA | 2A9A |
| MKD$ | EE | 4170 | | | NAME | A9 | 418E |
| NEW | BB | 1B49H | | | NEXT | 87 | 22B6 |
| NOT | CB | 25C4 | | | ON | A1 | 1FC6 |
| OPEN | A2 | 4179 | | | OR | D3 | 25F7 |
| OUT | AO | 2AFB | | | PEEK | E5 | 2CAA |
| POINT | C6 | 0132 | | | POKE | B1 | 2CB1 |
| POS | DC | 27F5 | | | B2 | 206F | |
| PUT | A5 | 4182 | | | RANDOM | 86 | 01D3 |
| READ | 8B | 21EF | | | REM | 93 | 1F07 |
| RESET | 82 | 0138 | | | RESTORE | 90 | 1D91 |
| RESUME | 9F | 1FAFH | | | RETURN | 92 | 1EDEH |
| RIGHT$ | F9 | 2A91 | | | RND | DE | 14C9 |
| RSET | AC | 419A | | | RUN | 8E | 1EA3 |
| SAVE | AD | 41A0 | | | SET | 83 | 0135 |
| SGN | D7 | 098A | | | SIN | E2 | 1547 |
| SQR | CD | 13E7 | | | STEP | cc | 2B01 |
| STOP | 94 | 1DA9 | | | STR$ | F4 | 2836 |
| STRING$ | C4 | 2A2F | | | SYSTEM | AE | 02B2 |
| TAB( | BC | 2137 | | | TAN | E3 | 15A8 |
| THEN | CA | | | TIME$ | C7 | 4176 | |
| TO | BD | | | TROFF | 97 | 1DF8 | |
| TRON | 96 | 1DF8 | | | USING | BF | 2CBD |
| USR | C1 | 27FE | | | VAL | FF | 2AC5 |
| VARPTR | C0 | 24EB | | | + | CD | 249F |
| – | CE | 2532 | | | CF | ||
| / | D0 | | | ? | D1 | ||
| > | D4 | | | = | D5 | ||
| < | D6 | | | & | 26 | ||
| ‘ | FB | 3A93 |
This code is moved from 18F7-191DH to 4080H-40A5H during non-disk initial setup.
This code is moved to 408E during non-disk initial setup.
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.
OR E7A
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.
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.
Note: 40FDH-40FEH holds Free memory pointer
LD A,256-(2*NUMLEV)3E C6
Note: 40A2H-40A3H holds the current BASIC line number
Note: 40DAH-40DBH holds DATA line number
Note: 40A2H-40A3H holds the current BASIC line number
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.
?NF ERRORentry point
?RW ERRORentry point
Note: 40EAH-40EBH holds Line number with error
Note: 40E8H-40E9H holds STACK pointer pointer
Note: 409AH holds the RESUME flag
Note: 40E6H-40E7H holds the temporary storage location
Note: 40EEH-40EFH is used by RESUME
Note: 40EAH-40EBH holds Line number with error
AND L7C
Note: 40F5H-40F6H holds the last line number executed
Note: 40F7H-40F8H holds Last byte executed
Note: 40F0H-40F1H is used by ON ERROR
OR L7C
Note: 40F2H holds Error flag
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.
Note: 40EAH-40EBH holds Line number with error
This basically reserves the line number 65534 as a trigger for the next few steps
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.
“READY”
Re-entry into BASIC command mode entry point. (see 6CCH also)
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
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.
Note: 40A2H-40A3H holds the current BASIC line number
Note: 40E2H-40E3H holds Current BASIC line number
Note: 40E4H-40E5H holds AUTO increment
There is an explanation at 1E5AH as to why 65529 is the highest possible line number (vs 65535 which would make more sense)
Note: 40E2H-40E3H holds Current BASIC line number
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.
Note: 40E6H-40E7H holds the temporary storage location
Note: 40DDH holds the BUFFER KILLED flag
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.
Note: 40ECH-40EDH holds EDIT line number
Note: 40F9H-40FAH holds the starting address of the simple variable storage area
Note: 40F9H-40FAH holds the starting address of the simple variable storage area
Note: 40A7H-40A8H holds Input Buffer pointer
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.
NOTE: 40A4H-40A5H holds the starting address of BASIC program text also known as the PROGRAM STATEMENT TABLE (PST)
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.
LD L,E62
If we are here, we did not get a 00 end of program, so we continue
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.
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 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.
NOTE: 40A4H-40A5H holds the starting address of BASIC program text also known as the PROGRAM STATEMENT TABLE (PST)
LD C,L44
NOTE: 40A4H-40A5H holds the starting address of BASIC program text also known as the PROGRAM STATEMENT TABLE (PST)
Note: 40F9H-40FAH holds the starting address of the simple variable storage area
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.
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.
Note: 40DFH-40E0H is used by DOS
Note: 4101H-411AH holds Variable Declaration Table
Note: 40F2H holds Error flag
Note: 40F0H-40F1H is used by ON ERROR
Note: 40F7H-40F8H holds Last byte executed
Note: 40B1H-40B2H holds MEMORY SIZE? pointer
Note: 40D6H-40D7H holds the next available location in string space pointer
Note: 40F9H-40FAH holds the starting address of the simple variable storage area
Note: 40FDH-40FEH holds Free memory pointer
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.
Note: 40E8H-40E9H holds STACK pointer pointer
Note: 40B5H-40D2H holds Temporary string work area
Note: 40B3H-40B4H holds the next available location in the temporary string work area pointer
Note: 40DCH holds FOR flag
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.
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.
*1BC1-1BC3 – Model 4 Gen 1
Note: 40B0H holds the temporary storage location
*1BC1-1BC3 – Model 4 Gen 2
Note: 40A7H-40A8H holds Input Buffer pointer
*1BDC-1BDE – Model 4 Gen 1
Note: 40B0H holds the temporary storage location
*1BDC-1BDE – Model 4 Gen 2
If we are here, the the character in the reserved word list had its MSB on, and is a reserved word.
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.
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.
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.
1C3D – Part of the tokeninzing routine– “NOTRES”
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!
The ‘token needs to be treated differently as it doesn’t require a colon. To deal with this, we put in a fake colon!
1C5A – Part of the tokeninzing routine– “NTSNGT” and “STUFFH”
*1C67-1C69 – Model 4 Gen 1
Note: 40B0H holds the temporary storage location
*1C67-1C69 – Model 4 Gen 2
1C7D – Part of the tokeninzing routine – Jumped here when an EOL is found– “CRDONE”
LD C,L44
Note: 40A7H-40A8H holds Input Buffer pointer
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).
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).
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.
Note: 40DCH holds FORflag
Note: 40E8H-40E9H holds STACK pointer pointer
Note: 40A2H-40A3H holds the current BASIC line number
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.
1CECH – Part of the Level II BASIC FORROUTINE– “SNGFOR”
PUSH DEC5
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.
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.
Note: 40E6H-40E7H holds the temporary storage location
Note: 40E8H-40E9H holds STACK pointer pointer
Honor a TRONby showing the line number if it is in effect.
Note: 411BH holds TRON/TROFF flag
That finishes the TRON routine where we display <nnnn> if it is in effect.
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.
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).
1D91-1D9A – LEVEL II BASIC RESTORE ROUTINE– “RESTORE”
NOTE: 40A4H-40A5H holds the starting address of BASIC program text also known as the PROGRAM STATEMENT TABLE (PST)
Note: 40FFH-4100H holds READ pointer
1D9B-1DAD – SCAN KEYBOARD ROUTINE– “ISCNTC”
Note: 4099H holds the Last key pressed
1DA9-1DAD – STOPROUTINE– “STOP”
This is the STOPentry point
1DAE-1DE3 – LEVEL II BASIC ENDROUTINE– “END”
Note: 40E6H-40E7H holds the temporary storage location
Note: 40B5H-40D2H holds Temporary string work area
Note: 40B3H-40B4H holds the next available location in the temporary string work area pointer
Note: 40A2H-40A3H holds the current BASIC line number
If we are here, then there was a line number, so we need to set some locations to enable a CONT command to work.
Note: 40F5H-40F6H holds the last line number executed
Note: 40E6H-40E7H holds the temporary storage location
Note: 40F7H-40F8H holds Last byte executed
1DE4-1DF6 – LEVEL II BASIC CONT ROUTINE– “CONT”
Note: 40F7H-40F8H holds Last byte executed
Note: 40F5H-40F6H holds the last line number executed
Note: 40A2H-40A3H holds the current BASIC line number
1DF7-1DF8 - TRON ENTRY POINT- "TON"
Turns TRONfeature on. Causes line numbers for each BASIC statement executed to be displayed. Uses Register A.
1DF8 - TROFF ENTRY POINT- "TOFF"
The following code is common to both TRON and TROFF.
Note: 411BH holds TRON/TROFF flag
1DFD-1DFF - DISK ROUTINE NOT USED BY LEVEL II BASIC- "POPAHT".
1E00-1E02 - DEFSTRENTRY POINT- "DEFSTR"
1E03-1E05 - DEFINT ENTRY POINT- "DEFINT"
1E06-1E08 - DEFSNG ENTRY POINT- "DEFREA"
1E09-1E0A - DEFDBL ENTRY POINT- "DEFDBL"
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
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.
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.
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.
Note: 4101H-411AH holds Variable Declaration Table
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.
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.
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.
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.
1E4A - ?FC ERROR entry point- "FCERR"
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.
Note: 40ECH-40EDH holds current line number
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.
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.
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
Now we need HL = DE * 10
This is so we can multiply HL (which should hold the number 6552) by 10
This is so we can multiply HL (which should hold the number 6552) by 10
As noted above, adding in any digit can only result in HL going to 65529
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
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.
Note: 40B1H-40B2H holds MEMORY SIZE? pointer
Note: 40F9H-40FAH holds the starting address of the simple variable storage area
1EA3-1EB0 - LEVEL II BASIC RUNROUTINE- "RUN"
On entry, if the Z flag is set, there was no parameter present
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
Note: 40A2H-40A3H holds the current BASIC line number
1EC2-1EDD - LEVEL II BASIC GOTOROUTINE- "GOTO"
Note: 40A2H-40A3H holds the current BASIC line number
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
LD L,C60
1ED9-1EDD - LEVEL II BASIC ?UL ERRORROUTINE- "USERR"
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.
Note: 40E8H-40E9H holds STACK pointer pointer
1EEC - RG ERROR entry point.
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.
Note: 40DDH holds INPUT flag
1F05-1F20 - SCAN ROUTINE- "DATA"
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.
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.
1F21-1F6B - LEVEL II BASIC LET ROUTINE- "LET"
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.
Note: 40DFH-40E0H is a common temporary storage area
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.
NOTE: 40A4H-40A5H holds the starting address of BASIC program text also known as the PROGRAM STATEMENT TABLE (PST)
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.
Note: 40F9H-40FAH holds the starting address of the simple variable storage area
1F6C-1FAE - LEVEL II BASIC ERROR ONROUTINE- "ONGOTO"
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.
1F7E
LD E,C50
Note: 40F0H-40F1H is used by ON ERROR
Note: 40F2H holds Error flag
Note: 409AH holds the RESUME flag
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.
1FAF-1FF3 - LEVEL II BASIC RESUMEROUTINE- "RESUME"
Note: 40F2H holds Error flag
1FC6
1FCDH - Part of the RESUMEROUTINE- "RESNXT"
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.
1FCF - This is the RESUME 0routine- "RESTXT"
Note: 40EEH-40EFH is used by RESUME
Note: 40EAH-40EBH holds Line number with error
Note: 40A2H-40A3H holds the current BASIC line number
1FF4H-2007 - LEVEL II BASIC ERROR ROUTINE - "ERRORS"
This evaluates n for ERROR n
2003 - UE ERROR entry point- "GOERR"
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.
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.
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).
Note: 40E4H-40E5H holds AUTOincrement
Note: 40E4H-40E5H holds AUTOincrement
Note: 40E2H-40E3H holds Current BASIC line number
2039-2066 - LEVEL II BASIC IFROUTINE- "IF"
. 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.
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.
2056H - LEVEL II BASIC ELSEROUTINE- "FALSIF"
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.
2067-206E - LEVEL II BASIC LPRINT ROUTINE- "LPRINT"
206F-2177 - LEVEL II BASIC PRINT@ ROUTINE- "PRINT"
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.
- 4020H-4021H: Holds the video memory address of the current cursor position.
2C
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).
2C
- NOTE: A CPwill return Z if there is a match against Register A, and NZ if not a match against Register A.)
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.
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.
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.
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.
- Note: 409CH holds the current output device type number (-1=cassette, 0=video; or 1=printer)
Note: 409BH holds the printer carriage position
20DDH - LEVEL II BASIC PRINT@ ROUTINE - Jumped here if we are sure we are using the display- "ISTTY"
Note: 409DH holds the size of line on the video display
Note: 40A6H holds the current cursor line position
Note: 40A6H holds the current cursor line position
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.
2108 - This is the jump point for a continuation of the PRINT#code- "COMPRT".
- Note: 409CH holds the current output device type number (-1=cassette, 0=video; or 1=printer)
- M will be set if the value in A is negative.
- P will be set if the value in A is positive or zero.
Note: 409BH holds the printer carriage position
Note: 409EH holds the size of line on the printer
Note: 40A6H holds the current cursor line position
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.
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).
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.
- Note: 409CH holds the current output device type number (-1=cassette, 0=video; or 1=printer)
Note: 409BH holds the printer carriage position
2153H - Displaying to Screen - "TTYIST"
Note: 40A6H holds the current cursor line position
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.
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".
- Note: 409CH holds the current output device type number (-1=cassette, 0=video; or 1=printer)
- Note: 409CH holds the current output device type number (-1=cassette, 0=video; or 1=printer)
2178-217E - MESSAGE STORAGE LOCATION FOR REDO MESSAGE- - "TRYAGN"
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.
Note: 40DEH holds READ flag
Note: 40A9H holds Cassette input flag
Note: 40E6H-40E7H is a common temporary storage location
219A - INPUT logic- "INPUT"
Note: 40A9H holds cassette input flag
Note: 40A7H-40A8H holds the input Buffer pointer
Note: 40A7H-40A8H holds the input Buffer pointer
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).
21EF - READlogic- "READ"
Note: 40FFH-4100H holds READpointer
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).
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).
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.
Note: 40DEH holds READ flag
Note: 40A9H holds Cassette input flag
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.
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.
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.
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.
- NOTE: 0E65H converts the ASCII string pointed to by HL to its double precision equivalent; with output left in ACCumulator).
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.
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.
Note: 40DEH holds READ flag
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).
2286-2295 - MESSAGE STORAGE LOCATION- "EXIGNT"
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.
Note: 40DAH-40DBH holds DATA line number
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.
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
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.
22EAH - Part of the NEXTcode, where we process the variable as an integer- "INTNXT"
Note: 40AFH holds Current number type flag
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"
2313H - Part of the NEXTcode, jumped if we haven't hit the TO counter yet- "LOOPDN"
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.
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.
- The RETURN location on completion (RETAOP)
- The floating point temporary result
- The address of the operator routine
- The precedence of the operator
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).
Note: 40F3H-40F4H is a temporary storage location
Note: 40F3H-40F4H is a temporary storage location
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.
E Token
0 +
1 -
2 *
3 /
4 @@
5 AND
6 OR
Note: 40AFH holds Current number type flag
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
Note: 40AFH holds Current number type flag
Note: 411DH-4124H holds REG l
Note: 40D8H-40D9H holds Temporary storage location
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 - 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.
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.
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.
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
Note: 40B0H holds Temporary storage location
Note: 40AFH holds Current number type flag
At this point, the number in the STACK MUST be an integer.
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.
Note: 411DH-4124H holds the ACCumulator
LD A,(DORES)3A B0 40
Note: 40B0H holds Temporary storage location
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.
Note: 40AFH holds Current number type flag
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.
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.
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.
249F - Evaluate a Variable, Constant, or Function Call- "EVAL"
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.
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.
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.
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.
Note: 409AH holds the RESUMEflag
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.
Note: 40EAH-40EBH holds the line number with error
24E7-24FEVARPTRlogic- "NTERL"
24E7-24E8- ↳ NTERLCP 0C0HCP $VARPTRFE C0Check to see if the character at the location of the current BASIC program pointer in Register A is a VARPTRtoken
24E9-24EAJump back to 24FFH if the character at the location of the current BASIC program pointer in Register A isn't a VARPTRtoken
24EBRST 10HCHRGETD7We 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-24EDRST 08H ⇒ 28SYNCHK "("CF 28Since 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-24F0Call 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- ↳ VARRETRST 08H ⇒ 29SYNCHK ")"CF 29Since 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)
24F3PUSH HLE5Save the value of the current BASIC program pointer in HL to the STACK
24F4EX DE,HLEBSwap DE and HL so that HL now holds the value to return
24F5LD A,H7CLoad Register A with MSB of the variable's address in Register H to make sure that it isn't undefined.
24F6OR LB5Combine 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-24F9Display a ?FC ERRORif the variable's address in HL is equal to zero, meaning that the variable is undefined
24FA-24FCSave the variable's address in HL as an integer into ACCumulator
24FDPOP HLE1Get the value of the current BASIC program pointer from the STACK and put it in HL
24FERETC9Return 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- ↳ NTVARPCP 0C1HCP USRTKFE C1Check 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-2503Jump to 27FEH if the character at the location of the current BASIC program pointer in Register A is a USRtoken
2504-2505CP 0C5HCP INSRTKFE C5Check 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-2503If 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-250ACP 0C8HCP $MEMFE C8Check 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-250DIf 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-250FCP 0C7HCP $TIMEFE C7Check 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-250DIf 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-2514CP 0C6HCP $POINTFE C6Check 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-2517Jump to 0132H if the character at the location of the current BASIC program pointer in Register A is a POINTtoken
2518-2519CP 0C9HCP $INKEYFE C9Check 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-251CJump to 019DH if the character at the location of the current BASIC program pointer in Register A is an INKEY$token
251D-251ECP 0C4HCP $STRINGFE C4Check 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-2421Jump to 2A2FH if the character at the location of the current BASIC program pointer in Register A is a STRING$token
2522-2523CP 0BEHCP FNTKFE BECheck 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-2421If 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-2528SUB 0D7HSUB ONEFUND6 D7Check 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-252BJump 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- ↳ PARCHKGOSUB to 2335H to recursively evaluate the expression at the location of the current BASIC program pointer in HL
252F-2530RST 08H ⇒ 29SYNCHK ")"CF 29Since 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).
2531RETC9Return out of this routine
2532 - Binary Minus Routine- "DOMIN"
2532- ↳ DOMINLD D,7DH16 7DLoad Register D with a precedence value below "^" but above everything else since its a uniary minus.
2534-2536GOSUB to 233AH to evaluate the variable at the location of the current BASIC program pointer in HL
2537-2539LD HL,(40F3H)LD HL,(TEMP2)2A F3 40Load HL with the value of the current BASIC program pointer.
Note: 40F3H-40F4H is a temporary storage location
253APUSH HLE5Save the value of the current BASIC program pointer in HL to the STACK so we know where to continue
253E- ↳ LABBCKPOP HLE1According 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$(
253FRETC9Return 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- ↳ ISVARGet 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- ↳ RETVARPUSH HLE5Save the value of the current BASIC program pointer in HL to the STACK
2544EX DE,HLEBSwap 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-2547LD (4121H),HLLD (FACLO),HL22 21 41In case it is a string, we will store the pointer to the descriptor in FACLO.
2548RST 20HGETYPEE7We 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-254BIf 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
254CPOP HLE1Restore the value of the current BASIC program pointer to Register Pair HL
254DRETC9Return to the caller
254E - This routine processes an expression for SNG(to MID$(- "ISFUN"
254E-254F- ↳ ISFUNLD B,00H06 00Load Register B with zero
2550RLCA07Set A to be 2 * (token - D7H)
2551LD C,A4FSave the new token
2552PUSH BCC5Save 0/2*(token-D7) on STACK
2553RST 10HCHRGETD7Get 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.
2554LD A,C79Prepare to look for the function number
2555- ↳ NUMGFNCP 41HCP NUMGFNFE 41Test 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.
2557If 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$
2559Otherwise, 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.
255CRST 08H ⇒ 2CSYNCHK ","CF 2CWe need TWO arguments, so there needs to be a ",". With this we use RST 08H to test for a ,
255EGOSUB to 0AF4H to ensure the current variable is a string, otherwise it is an error
2561EX DE,HLEBSwap 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)
2562LD HL,(4121H)LD HL,(FACLO)2A 21 41Load HL with the string descriptor address held at the memory location pointed to by ACCumulator
2565EX (SP),HLE3Put the pointer to the string descriptor onto the STACK and put the function number into Register Pair HL
2566PUSH HLE5Save function number (i.e., 00 / 2*(token-D7H)) to the STACK
2567EX DE,HLEBSwap DE and HL so that HL will now point to the position in the current BASIC program being evaluated.
2568Evaluate n portion of the string function. Register E will contain the value of the formula.
256BEX DE,HLEBSwap 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.
256CEX (SP),HLE3Swap (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
256F-2571- ↳ OKNORMNext 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
2572EX (SP),HLE3Swap (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.
2573LD A,L7DLoad Register A with the function number [i.e., 2 * (token - D7H)]
2574-2575- ↳ BOTCONCP 0CHCP (SQRTK-ONEFUN)*2FE 0CCheck 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-2577Jump down to 257FH to avoid forcing the argument if the operator token in Register A is SGNto SQR
2578-2579- ↳ TOPCONCP 1BHCP (ATNTK-ONEFUN)*2+1FE 1BCheck 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.
257APUSH HLE5Save the function number (i.e., 0 + 2*(token - D7H)) to the stop of the STACK
257B-257DIf 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- ↳ NOTFRFLoad DE with a return address of 253EH for once the function is executed
2582PUSH DED5Save the value of the return address in DE to the STACK so it will act as the return adddress
2583-2585- ↳ FINGOLoad BC with the function dispatch/jump table address
2586- ↳ DISPATADD HL,BC09Add the jump table pointer in BC (i.e., the offset) with the value of the operator token in HL
2587LD C,(HL)4ELoad Register C with the LSB of the jump address at the location of the jump table pointer in HL
2588INC HL23Bump the value of the jump table pointer in HL
2589LD H,(HL)66Load Register H with the MSB of the jump address at the location of the jump table pointer in HL
258ALD L,C69Load Register L with the LSB of the jump address in Register C
258BJP (HL)E9Perform 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- ↳ STRCMPFirst 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
258FLD A,(HL)7ELoad Register A with the LSB of the length of the string in the FACLO
2590INC HL23Bump the value of the string's VARPTR in HL so that HL points to the LSB of the string address
2591LD C,(HL)4ELoad Register C with the LSB of the string in the FACLO
2592INC HL23Bump the value of the string's VARPTR in HL so that HL points to the MSB of the string address
2593LD B,(HL)46Load Register B with the MSB of the string in the FACLO. Register Pair BC now points to the FACLO string.
2594POP DED1Put the STACK string pointer into Register Pair DE
2595PUSH BCC5Save the pointer to the FACLO string data
2596PUSH AFF5Save the FACLO string's length in Register A to the STACK
2597-2599Free up the STACK string and RETURN with the pointer to the STACK string descriptor in Register Pair HL.
259APOP DED1Get the length of the FACLO string from the STACK and put it in Register D
259BLD E,(HL)5ELoad Register E with the STACK / BCDE string's length
259CINC HL23Bump the pointer to the STACK / BCDE string's entry in HL
259DLD C,(HL)4ELoad Register C with the LSB of the STACK / BCDE string's address
259EINC HL23Bump the pointer to the STACK / BCDE string's entry in HL
259FLD B,(HL)46Load Register B with the MSB of the STACK / BCDE string's address
25A0POP HLE1Get the second character pointer from the STACK and put it in HL
25A1- ↳ CSLOOPLD A,E7BLoad Register A with the length of the STACK / BCDE string in Register E
25A2OR DB2Combine the FACLO string's length in Register D with the STACK / BCDE string's length in Register A
25A3RET ZC8If 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
25A4LD A,D7ALoad Register A with the FACLO string's length in Register D
25A5-25A6SUB 01HD6 01Check 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.
25A7RET CD8If the CARRY FLAG is set, then the FACLO string has run out of characters, so RETurn
25A8XOR AAFIf 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
25A9CP EBBCheck 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.
25AAINC A3CBump the value in Register A for a return code of A = 1
25ABRET NCD0Return if there aren't any more characters in the STACK / BCDE string to be compared
25ACDEC D15If we are STILL here, then neither string has ended. First, decrement the value of the FACLO string's length in Register D
25ADDEC E1DDecrement the value of the STACK / BCDE string's length in Register E
25AELD A,(BC)0ALoad Register A with the character at the location of the STACK / BCDE string pointer in BC
25AFCP (HL)BECompare 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.
25B0INC HL23Bump the value of the first string pointer in HL
25B1INC BC03Bump the value of the second string pointer in BC
25B4CCF3FIf we are here, then the NZ FLAG was set and the strings are NOT equal. Since they are not equal, reverse the CARRY flag
25B8- ↳ DOCMPINC A3CBump the value of the current precedence value in Register A
25B9ADC A,A8FAdjust 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
25BAPOP BCC1Get the last operator value from the STACK and put it in BC
25BBAND BA0Combine the precedence value in Register B with the precedence value in Register A to see if any of the bits match.
25BC-25BDADD A,FFHC6 FFAdjust the value in Register A. This will give a 0 if both are equal and a CARRY if they are unequal
25BESBC A,A9FCheck 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-25C1Convert 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-25C3At 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- ↳ NOTERLD D,5AH16 5ANOT has a precedence value of 90, so we need a dummy entry of 90 on the STACK
25C6-25C8Go evaluate the expression with a dummy entry of 90 on the STACK
25C9-25CBWe 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)
25CCLD A,L7DThe next bunch of instructions are to complement Register Pair HL. First, load Register A with the LSB of the integer value
25CDCPL2FCompliment the LSB of the integer value in Register A
25CELD L,A6FLoad Register L with the adjusted LSB of the integer value in Register A
25CFLD A,H7CLoad Register A with the MSB of the integer value in Register H
25D0CPL2FCompliment the MSB of the integer value in Register A
25D1LD H,A67Load Register H with the adjusted MSB of the integer value in Register A
25D2-25D4LD (4121H),HLLD (FACLO),HL22 21 41Save the complimented integer value in HL as the current result in ACCumulator
25D5POP BCC1Clean up the STACK
25D6-25D8- ↳ RETAPGJump 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- ↳ GETPYRLD A,(40AFH)LD A,(VALTYP)3A AF 40Load Register A with the current value of the number type flag.
Note: 40AFH holds Current number type flag
25DC-25DDCP 08HFE 08Check 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- ↳ CGETYPIf that test shows that we have a DOUBLE PRECISION number, jump forward to 25E5H
25E0-25E1SUB 03HD6 03If the number is not double precision, subtract 3
25E2OR AB7Set the status flags of the adjusted number type flag in Register A
25E3SCF37Set the Carry flag
25E4RETC9RETurn to CALLer
25E5-25E6- ↳ NCASESUB 03HD6 03We are dealing with a double precision number so adjust the value of the current number type flag in Register A
25E7OR AB7Test the value of the current number type flag in Register A, which will exit without the CARRY flag set
25E8RETC9RETurn 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- ↳ DANDORPUSH BCC5B has he precedence value, so save BC to the STACK. The precedence value for OR is 70.
25EA-25ECWe 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)
25EDPOP AFF1Get the precedence value from the STACK and put it in Register A so that we can detemined between span class="code">ANDand OR
25EEPOP DED1Get the left hand argument from the STACK and put it in DE
25F2PUSH BCC5Save the value of the return address in BC to the STACK
25F3-25F4CP 46HFE 46Check 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-25F6If 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.
25F7LD A,E7BLoad Register A with the LSB of the first value in Register E
25F8OR LB5Combine the LSB of the first value in Register A with the LSB of the second value in Register L
25F9LD L,A6FLoad Register L with the ORed value in Register A
25FALD A,H7CLoad Register A with the MSB of the second value in Register H
25FBOR DB2Combine the MSB of the first value in Register D with the MSB of the second value in Register A
25FCRETC9Return to 27FAH (=convert the result to integer and return that integer calue to 2346H)
25FD - ANDlogic- "NOTOR"
25FD- ↳ NOTORLD A,E7BLoad Register A with the LSB of the first value in Register E
25FEAND LA5Combine the LSB of the first value in Register A with the LSB of the second value in Register L
25FFLD L,A6FLoad Register L with the ANDed value in Register A
2600LD A,H7CLoad Register A with the MSB of the second value in Register H
2601AND DA2Combine the MSB of the first value in Register D with the MSB of the second value in Register A
2602RETC9Return to 27FAH (=Make the result an integer and return to 2346H)
2603 - Dimension and Variable Searching Routine- "DIMCON"
2603- ↳ DIMCONDEC HL2BDecrement the value of the BASIC program pointer in HL so that we can see what the prior character was
2604RST 10HCHRGETD7We 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.
2605RET ZC8If the CHGRGET routine returned a Z FLAG, then we have a terminator; so RETurn since this is the end of the BASIC statement
2606-2607RST 08H ⇒ 2CSYNCHK ","CF 2CSince 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:
- If an entry is found, dimflg being on indicates a "doubly dimensioned" variable
- 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.
- When the build entry code finishes, only if dimflg is off will indexing be done
260BPUSH BCC5Save the return address of 2603H (in BC) to the STACK
260C-260DOR AFHF6 AFThis 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- ↳ PTRGETXOR AAFIf JUMPed here, then A is set to ZERO. As a reminder, if passed through from the above routine, A will be NOT ZERO.
260E-2610LD (40AEH),ALD (DIMFLG),A32 AE 40Save the value in Register A as the current variable location/creation flag.
Note: 40AEH holds LOCATE/CREATE variable flag
2611LD B,(HL)46Load Register B with the first character of the variable name
2612-2614- ↳ PTRGT2GOSUB to 1E3DH to make sure the first character of the variable name is a letter
2615-2617Display a ?SN ERRORif the first character of the variable name isn't a letter
2618XOR AAFZero Register A
2619LD C,A4FSet up to assume that there is no second character by zeroing Register C
261ARST 10HCHRGETD7We 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-261CJump to 2622H if the character at the location of the current BASIC program pointer in Register A is numeric
261D-261FGOSUB 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-2621Jump to 262BH if the character at the location of the current BASIC program pointer in Register A isn't a letter
2622- ↳ ISSECLD C,A4FIf we are here, then the second character was a number, so save it in Register C
2623- ↳ EATEMRST 10HCHRGETD7We 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-2625Loop back one OPCODE to keep eating characters until a non-numeric character is found
2626-2628Go check to see if the character at the location of the current BASIC program pointer in HL is alphabetic
2629-262AJump back to 2623H if the character at the location of the current BASIC program pointer in HL is alphabetic
262B-262D- ↳ NOSECLoad DE with a return address of 2652H. Done to save time/RAM from using JUMPs instead.
262EPUSH DED5Save the value of the return address in DE to the STACK
262F-2630LD D,02H16 02Load Register D with an integer number type flag
2631-2632CP 25HFE 25Check 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.
2633RET ZC8Return if the character at the location of the current BASIC program pointer in Register A is a %
2634INC D14Bump Register D so that it will be equal to a string number type flag (02=INT, 03=STR, 04=SNG, 08=DBL)
2635-2636CP 24HFE 24Check 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.
2637RET ZC8Return if the character at the location of current BASIC program pointer in Register A is a $
2638INC D14Bump Register D so that it will be equal to a single precision number type flag (02=INT, 03=STR, 04=SNG, 08=DBL)
2639-263ACP 21HFE 21Check 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.
263BRET ZC8Return if the character at the location of the current BASIC program pointer in Register A is a !
263C-263DLD D,08H16 08Load 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-263FCP 23HFE 23Check 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.
2640RET ZC8Return if the character at the location of the current BASIC program pointer in Register A is a #
2641LD A,B78Load Register A with the first character of the variable name from Register B
2642-2643SUB 41HD6 41Adjust 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-2645AND 7FHE6 7FGet rid of the user-defined function bit in Register B by ANDing it against 0111 1111
2646LD E,A5FNext 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-2648LD D,00H16 00Load Register D with zero
2649PUSH HLE5Save the value of the current BASIC program pointer in HL to the STACK
264A-264CLoad HL with the starting address of the variable declaration table.
NOTE:4101H-411AH holds Variable Declaration Table
264DADD HL,DE19Add the offset to the top of the table
264ELD D,(HL)56Load Register D with the number type value from the variable declaration table pointer in HL
264FPOP HLE1Get the value of the current BASIC program pointer from the STACK and put it in HL
2650DEC HL2BDecrement the value of the current BASIC program pointer in HL as there was no marking character
2651RETC9Return with data type in D
2652- ↳ HAVTYPLD A,D7ALoad Register A with the value of the number type flag in Register D
2653-2655LD (40AFH),ALD (VALTYP),A32 AF 40Save the number type flag for the current variable name from Register A.
NOTE:40AFH holds Current number type flag
2656RST 10HCHRGETD7We 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-2659LD A,(40DCH)LD A,(SUBFLG)3A DC 40Load Register A with the FORflag. Why the FOR flag? It doubles as a "should we allow arrays here" flag!
265AOR AB7Test the value of the FORflag in Register A
265ELD A,(HL)7ERe-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-2660SUB 28HD6 28Next, 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-2663If the Z FLAG is set then we have an array (meaning, it is a subscripted variable), so JUMP to 26E9H
2664- ↳ NOARYSXOR AAFZero Register A so that we can allow for parenthesis now
2665-2667LD (40DCH),ALD (SUBFLG),A32 DC 40Set the "permit arrays" array flag to 'no subscript'.
Note: 40DCH holds FORflag
2668PUSH HLE5Save the value of the current BASIC program pointer in HL to the STACK
2669PUSH DED5Save the number type flag for the variable in DE to the STACK
266A-266CLD HL,(40F9H)LD HL,(VARTAB)2A F9 40Load 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- ↳ LOPFNDEX DE,HLEBSwap 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-2670LD HL,(40FBH)LD HL,(ARYTAB)2A FB 40Load HL with the pointer to the end of simple variables. 40FBH-40FCH holds the starting address of the BASIC array variable storage area
2671RST 18HCOMPARDFNow 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.
2672POP HLE1Get the number type flag for the variable from STACK and put it in HL
2673-2674If 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
2675LD A,(DE)1ALoad Register A with the number type flag for the variable at the location of the simple variables pointer in DE
2676LD L,A6FPreserve Register A into Register L so we know how many entries to skip.
2677CP HBCCompare 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.
2678INC DE13Bump the value of the current simple variables pointer in DE to the 2nd character name for this entry
2679-267AIf 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
267BLD A,(DE)1ASince 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
267CCP CB9Compare 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-2637Jump to 2686H if the first characters of the variable names don't match
267FINC DE13Bump the value of the current simple variables pointer in DE
2680LD A,(DE)1ALoad Register A with the second character of the variable name at the location of the simple variables pointer in DE
2681CP BB8Compare 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-2684Jump 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-2686LD A,13H3E 13Z-80 Trick to skip the next INC DE if continuing through
2686- ↳ NOTIT1INC DE13Bump to the next entry in the simple variable list part 1
2687INC DE13Bump the value of the simple variables pointer in DE part 2
2688PUSH HLE5Save the number type flag for the variable in HL to the STACK so that it can be re-loaded at 2672H
2689-268ALD H,00H26 00Load Register H with zero so that Register Pair HL is the number of bytes to skip, but in 16 bits.
268BADD HL,DE19Add the value of the simple variables pointer in DE to the value of the number type flag in HL
268E- ↳ NOTFNSLD A,H7CLoad Register A with the length for the variable in Register H
268FPOP HLE1Get the value of the current BASIC program pointer from the STACK and put it in HL
2690EX (SP),HLE3Exchange (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
2691PUSH AFF5Save length of the variable in Register A to the STACK
2692PUSH DED5Save the current variable table position from DE to the STACK
2696RST 18HCOMPARDFNow 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-2698If the Z FLAG is set, then this was a VARPTR call, so JUMP forward to 26CFH.
2699-269BNext we need to see if EVAL called this routine. Load DE with a return address of the find address of variables routine at 2543H
269CRST 18HCOMPARDFWe 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.
269DPOP DED1Restore the current variable table position from the STACK and put it in DE
269E-269FIf 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.
26A0POP AFF1Clear the STACK and put the value of the number type flag for the variable from the STACK and put it in Register A
26A1EX (SP),HLE3Swap (SP) and HL so that the value of the current BASIC program pointer is now in Register Pair HL
26A2PUSH HLE5Save the value of the current BASIC program pointer in HL to the STACK
26A3PUSH BCC5Save the variable's address in BC to the STACK as we are about to use both Register B and Register C
26A4LD C,A4FLoad Register C with the value of the number type flag for the variable in Register A
26A5-26A6LD B,00H06 00Load Register B with zero so that the number type flag for the variable can be represented in 16 bits
26A7PUSH BCC5Save the variable's number type flag in BC to the STACK
26A8INC BC03Bump the value of the variable's number type flag in BC
26A9INC BC03Bump the value of the variable's number type flag in BC
26AAINC BC03Bump the value of the variable's number type flag in BC. Now the variable's length includes room for the addresses as well.
26AB-26ADLD HL,(40FDH)LD HL,(STREND)2A FD 40Load HL with the value of the free memory pointer.
Note: 40FDH-40FEH holds Free memory pointer
26AEPUSH HLE5Save the value of the free memory pointer in HL to the STACK
26AFADD HL,BC09Add the value of the variable's number type flag in BC to the value of the free memory pointer in HL
26B0POP BCC1Restore the high address from the STACK and put it in BC
26B1PUSH HLE5Save the value of the high address pointer in HL to the STACK
26B2-26B4Block transfer the variable information and make sure we do not overflow the STACK space via a GOSUB to BLTU.
26B5POP HLE1Get the value of the new free memory pointer (i.e., STREND) from the STACK and put it in HL
26B6-26B8LD (40FDH),HLLD (STREND),HL22 FD 40Save the value of the new free memory pointer in HL to lock in that variable space.
NOTE:40FDH-40FEH holds Free memory pointer
26B9LD H,B60Load Register H with the MSB of the new array variables pointer in Register B
26BALD L,C69Load 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-26BDLD (40FBH),HLLD (ARYTAB),HL22 FB 40Save the value of the new array variables pointer in HL. 40FBH-40FCH holds the starting address of the BASIC array variable storage area
26BE- ↳ ZEROERDEC HL2BAt 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-26C0LD (HL),00H36 00Zero the location of the memory pointer in HL
26C1RST 18HCOMPARDFNow 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.
26C4POP DED1Get the value of the variable's number type flag from the STACK and put it in DE
26C5LD (HL),E73Save the value of the number type flag in Register E at the location of the memory pointer in HL
26C6INC HL23Bump the value of the memory pointer in HL
26C7POP DED1Get the 2nd character of the variable's name from the STACK and put it in DE
26C8LD (HL),E73Save the first character of the variable's name in Register E at the location of the memory pointer in HL
26C9INC HL23Bump the value of the memory pointer in HL
26CALD (HL),D72Save the first character of the variable's name in Register D at the location of the memory pointer in HL
26CBEX DE,HLEBLoad DE with the value of the variable pointer in from HL
26CC- ↳ FINPTRINC DE13Bump the value of the variable pointer in DE so that it points to the value
26CDPOP HLE1Restore the value of the current BASIC program pointer from the STACK into Register Pair HL
26CERETC9RETurn to CALLer
26CF- ↳ VARNOTLD D,A57On entry, the Z FLAG was set, meaning that A=0. Zero out Register D with the value of Register AA
26D0LD E,A5FZero out Register E
26D1POP AFF1Clean up the STACK (which was the PUSHed DE)
26D2POP AFF1Clean up the STACK (which was the length)
26D3EX (SP),HLE3Swap (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
26D4RETC9Return to the VARPTRroutine
26D5 - This routine is ZERO out all variable types and skip any RETurn- "FINZER"
26D5-26D7- ↳ FINZERLD (4124H),ALD (FAC),A32 24 41Zero ACCumulator so that all single-precision and double-precision variables become zero
26D8POP BCC1Clean up the STACK (i.e., remove the length of the variable)
26D9LD H,A67Zero Register H to clear out integers as well
26DALD L,A6FZero Register L to clear out integers as well
26DB-26DDLD (4121H),HLLD (FACLO),HL22 21 41Zero the string pointer location in ACCumulator
26DERST 20HGETYPEE7We 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-26E0If 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-26E3If 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-26E6LD (4121H),HLLD (FACLO),HL22 21 41Save the value in HL as the current string pointer, which is now null.
26E7- ↳ POPHR2POP HLE1Get the value of the current BASIC program pointer from the STACK and put it in HL
26E8RETC9RETurn (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- ↳ ISARYPUSH HLE5Save the DIMFLG and VALTYP for recursion
26EA-26ECLD HL,(40AEH)LD HL,(DIMFLG)2A AE 40Load 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
26EDEX (SP),HLE3Swap (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
26EELD D,A57Zero Register D (which will hold the number of dimension)
26EF- ↳ INDLOPPUSH DED5Save the number of dimension (held in Register D) to the STACK
26F0PUSH BCC5Save the variable's name in BC to the STACK
26F1-26F3Go 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
26F4POP BCC1Get the variable's name from the STACK (1st and 2nd character) and put it in BC
26F5POP AFF1Get the variable's number of dimension so far from the STACK and put it in Register A
26F6EX DE,HLEBSwap 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
26F7EX (SP),HLE3Swap (SP) and HL so that HL will now hold DIMFLG and VALTYP and the array subscript will be at the stop of the STACK
26F8PUSH HLE5Save the DIMGFLG and VALTYP (in HL) to the STACK
26F9EX DE,HLEBSwap 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.
26FAINC A3CBump the number of dimensions evaluated in Register A
26FBLD D,A57Load Register D with the number of dimensions evaluated in Register A
26FCLD A,(HL)7ELoad Register A with the character at the location of the current BASIC program pointer in HL
26FD-26FECP 2CHFE 2CCheck 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-2700If 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-2702RST 08H ⇒ 29SYNCHK ")"CF 29Since 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- ↳ SUBSOKLD (40F3H),HLLD (TEMP2),HL22 F3 40Save the value of the current BASIC program pointer in HL into the TEMP2 storage area.
2706POP HLE1Get the DIMFLG and VALTYP from the STACK and put it in HL.
2707-2709LD (40AEH),HLLD (DIMFLG),HL22 AE 40Save the value of the DIMFLG and VALTYP into the DIMFLG location in RAM.
NOTE:40AEH holds LOCATE/CREATE variable flag
270APUSH DED5Save 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-270DLD HL,(40FBH)LD HL,(ARYTAB)2A FB 40We 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-270FLD A,19H3E 19Z-80 Trick to skip the next command of ADD HL,DE if falling through
270F- ↳ LOPFDAADD HL,DE19Advance past the current array as we know it isn't the correct one. Note: 40FBH-40FCH holds the array variables pointer
2710EX DE,HLEBSwap DE and HL so that DE holds the current search point. We don't care about HL.
2711-2713LD HL,(40FDH)LD HL,(STREND)2A FD 40Load HL with the place to STOP the search (i.e., the value of the free memory pointer).
Note: 40FDH-40FEH holds Free memory pointer
2714EX DE,HLEBSwap DE and HL so that DE now holds the place to stop the search and HL holds the current search point.
2715RST 18HCOMPARDFNow 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-2718LD A,(40AFH)LD A,(VALTYP)3A AF 40Load Register A with the value of the current number type flag.
Note: 40AFH holds Current number type flag
2719-271AIf 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
271BCP (HL)BECompare 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.
271CINC HL23Bump the value of the array variables pointer in HL
271D-271EJump forward (but still in this loop) to 2727H if the number type flags don't match
271FLD A,(HL)7ELoad Register A with the first character of the variable name at the location of the array variables pointer in HL
2720CP CB9Check 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.
2721INC HL23Bump the value of the array variables pointer in HL
2722-2723Jump forward (but still in this loop) to 2728H if the first characters of the variable names don't match
2724LD A,(HL)7ELoad Register A with the second character of the variable name at the location of the array variables pointer in HL
2725CP BB8Compare 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-2727LD A,23H3E 23This part of a Z-80 Trick. Will load A with 23H if passing through, and not do the following INC HL.
2727- ↳ NMARY2INC HL23Bump the value of the array variables pointer in HL
2728- ↳ NMARY1INC HL23Bump the value of the array variables pointer in HL. HL should now point to the LENGTH entry of the array being looked at
2729LD E,(HL)5ELoad Register E with the LSB of the LENGTH of the array being looked at
272AINC HL23Bump the value of the array variables pointer in HL
272BLD D,(HL)56Load Register D with the MSB of the LENGTH of the array being looked at
272CINC HL23Bump the value of the array variables pointer in HL
272D-272EIf 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-2731LD A,(40AEH)LD A,(DIMFLG)3A AE 40Load 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
2732OR AB7Since 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-2734LD E,12H1E 12Prepare for an error if this routine was NOT called by "DIM" by loading Register E with the ?DD ERRORcode
2735-2737If 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.
2738POP AFF1Get the number of dimension evaluated from the STACK and put it in Register A
2739SUB (HL)96To 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-273CIf they match then we are done so JUMP down to 2795H to read the indices
273D - ?BS ERROR entry point- "BSER"
273D-273E- ↳ BSERLD E,10H1E 10Load Register E with a ?BS ERRORcode
273F-2741Display 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 - Get an index
- Put number+1 down at the VARPTR
- Increase the VARPTR
- Decmrent the number of DIMs
- 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- ↳ NOTFDDLD (HL),A77Save the variable type for the array in Register A at the location of the array variables pointer in HL
2743INC HL23Bump 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)
2744LD E,A5FLoad Register E with the variable type flag for the current variable
2745-2746LD D,00H16 00Zero Register D so that Register Pair DE can be the size of one value of the type VALTYP
2747POP AFF1Get the number of dimensions evaluated from the STACK and put it in Register A
2748LD (HL),C71Save the second character of the variable's name in Register C at the location of the array variables pointer in HL
2749INC HL23Bump 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)
274ALD (HL),B70Save the first character of the variable's name in Register B at the location of the array variables pointer in HL
274BINC HL23Bump the value of the array variables pointer in HL to the LSB of the offset to the next entry
274CLD C,A4FIn preparation for the next CALL, load Register C with the number of two byte entries needed to store the size of each dimension
274D-274FFigure the amount of memory space left between HL and the free memory and get the space needed as set in Register C
2750INC HL23Next 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
2751INC HL23Bump the value of the array variables pointer in HL. These 2 INC's skip over the offset entry
2752-2754LD (40D8H),HLLD (TEMP3),HL22 D8 40Next 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
2755LD (HL),C71Save the number of dimension at the location of the array variables pointer in HL
2756INC HL23Bump the value of the array variables pointer in HL to point to the first subscript entry in the array table
2757-2759LD A,(40AEH)LD A,(DIMFLG)3A AE 40Load 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
275ARLA17Set the CARRY flag accordingly
275BLD A,C79Load Register A with the number of dimension evaluated in Register C
275C-275E- ↳ LOPPTALD BC,000BH01 0B 00Top 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-2760If 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
2761POP BCC1Get a subscript/index from the STACK and put it in BC
2762INC BC03Bump the value of the subscript in BC by one for the ZERO entry.
2763- ↳ NOTDIMLD (HL),C71Top of a loop. Save the LSB of the subscript's value in Register C at the location of the array variables pointer in HL
2764INC HL23Bump the value of the array variables pointer in HL
2765LD (HL),B70Save the MSB of the subscript's value in Register B at the location of the array variables pointer in HL
2766INC HL23Bump the value of the array variables pointer in HL
2767PUSH AFF5Save the number of dimensions evaluated in Register A (and the CARRY aflag results from DIMFLG) to the STACK
2768-276AGo multiply the size of the subscript by the value of the number type flag to determine the amount of memory necessary for the subscript
276BPOP AFF1Get the number of domensions that the CARRY FLAG (DIMFLG) from the STACK and put it in Register A
276CDEC A3DDecrement the counter of the number of dimensions to check by one
276D-276EJump back to 275CH if there are anymore subscripts to be evaluated
276FPUSH AFF5If 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
2770LD B,D42Load Register B with the MSB of the array's length in Register D
2771LD C,E4BLoad Register C with the LSB of the array's length in Register E. Now BC = size of the array in bytes
2772EX DE,HLEBSwap DE and HL so that DE now has the start of the values and HL has the end of the values
2773ADD HL,DE19Add the length of the array in HL to the value of the array variable pointer in DE
2774-2775If that addition triggered the CARRY FLAG then we are out of RAM so JUMP back to 273DH and throw a ?BS ERROR
2776-2778We now know there is room in RAM, so GOSUB to "REASON" to make sure there is room for the values
2779-277BLD (40FDH),HLLD (STREND),HL22 FD 40Update the end of storage pointer with the end of the array (held in HL).
Note: 40FDH-40FEH holds free memory pointer
277C- ↳ ZERITADEC HL2BNow we need to zero the new array. First, decrement the value of the array pointer in HL
277D-277ELD (HL),00H36 00Zero the location of the array pointer in HL
277FRST 18HCOMPARDFNow 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-2781If the NZ FLAG is set, then we have more entries to ZERO, so loop until the array has been cleared
2782INC BC03Load 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
2783LD D,A57Zero Register D
2784-2786LD HL,(40D8H)HL,(TEMP3)2A D8 40Load HL with the array variables pointer (=the number of indices).
Note: 40D8H-40D9H holds Temporary storage location
2787LD E,(HL)5ELoad Register E with the number of dimension for the array at the location of the array variables pointer in HL
2788EX DE,HLEBSwap DE and HL so that HL now holds the number of dimensions and DE holds the value of the array variables pointer
2789ADD HL,HL29Multiply the number of subscripts for the array in HL by two
278AADD HL,BC09Add 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
278BEX DE,HLEBSwap 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
278CDEC HL2BWe 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
278DDEC HL2BDecrement the value of the array variables pointer in HL
278ELD (HL),E73Save the LSB of the size of the array in Register E at the location of the array variables pointer in HL
278FINC HL23Bump the value of the array variables pointer in HL
2790LD (HL),D72Save the MSB of size of the array array in Register D at the location of the array variables pointer in HL
2791INC HL23Bump the value of the array variables pointer in HL
2792POP AFF1Get the value of the DIMFLG (i.e., the CARRY BIT) and a 0 into Register A
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- ↳ GETDEFLD B,A47Zero Register B
2796LD C,A4FZero Register C. Now BC = 0 = CURTOL
2797LD A,(HL)7ELoad Register A with the number of dimensions for the array at the location of the array variables pointer in HL
2798INC HL23Bump the value of the array variables pointer in HL to one entry past the number of dimensions
2799LD D,0E1H16 E1Z-80 Trick to hide the next instruction (POP HL) if proceeding downward
279A- ↳ INLPNMPOP HLE1Get the array variables pointer from the STACK and put it in HL
279BLD E,(HL)5ENext, 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
279CINC HL23Bump the value of the array variables pointer in HL
279DLD D,(HL)56Load Register D with the MSB of the subscript limit at the location of the array variables pointer in HL
279EINC HL23Bump the value of the array variables pointer in HL
279FEX (SP),HLE3Swap 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
27A0PUSH AFF5Save the number of dimensions for the array in Register A to the STACK
27A1RST 18HCOMPARDFNow 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-27A4If the current inex is too big, then we need to throw a ?BS ERROR via a JUMP to 273DH
27A8ADD HL,DE19Add the index to CURTOL
27A9POP AFF1Get the number of dimensions for the array from the STACK and put it in Register A
27AADEC A3DWe checked one, so cross one off the list and see if there are anymore dimensions to be evaluated
27ABLD B,H44Load Register B with the MSB of the subscript pointer in Register H
27ACLD C,L4DLoad Register C with the LSB of the subscript pointer in Register L. Now BC = CURTOL for the start of the next loop.
27AD-27AELoop back to 279AH until all of the subscripts have been evaluated
27AF-27B1LD A,(40AFH)LD A,(VALTYP)3A AF 40Get 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
27B3LD B,H
LD C,L44We are going to need to multiply by THREE, so we need to save the original value into BC as well.
27B4ADD HL,HL29Multiply the subscript pointer in HL by two
27B5-27B6SUB 04HD6 04Check the value of the number type flag in Register A because we would be done if we have an integer or a string.
27B7-27B8Jump forward to 27BDH if the current number type is an integer or string
27B9ADD HL,HL29It 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-27BBJump forward to 27C2H if the current number type is single precision as we then have enough bytes.
27BCADD HL,HL29It must be double precision, so once again multiply the subscript pointer in HL by two (so now it is multiplied by 8)
27BD- ↳ DMLVALOR AB7Set the flags
27BE-27C0Jump forward to 27C2H if the current number type isn't a string
27C1ADD HL,BC09The current number type is a string so add the value of the original subscript pointer in BC to the subscript pointer in HL
27C2- ↳ DONMULPOP BCC1Get the value of the array variables pointer from the STACK and put it in BC
27C3ADD HL,BC09Add 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
27C4EX DE,HLEBLoad DE with the variable pointer in HL
27C5-27C7- ↳ FINNNOWLD HL,(40F3H)LD HL,(TEMP2)2A F3 40Get the value of the current BASIC program pointer and put it in HL.
Note: 40F3H-40F4H is a temporary storage location
27C8RETC9RETurn 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- ↳ MEMXOR AAFZero Register A and the status flags
27CAPUSH HLE5Save the value of the current BASIC program pointer in HL to the STACK
27CB-27CDLD (40AFH),ALD (VALTYP),A32 AF 40Zero the number type flag.
NOTE:40AFH holds Current number type flag
27CE-27D0Determine how much space there is via a GOSUB to the FREroutine at 27D4H
27D1POP HLE1Get the value of the current BASIC program pointer from the STACK and put it in HL
27D2RST 10HCHRGETD7We 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.
27D3RETC9Return to BASIC
27D4-27F4 - LEVEL II BASIC FREROUTINE- "FRE"
27D4-27D6- ↳ FRELD HL,(40FDH)LD HL,(STREND)2A FD 40Load 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
27D7EX DE,HLEBLoad DE with the value of the free memory pointer in HL for subtraction
27DBADD HL,SP39Add the value in HL (which is zero) to the current value of the STACK pointer so that the STACK pointer is now in HL
27DCRST 20HGETYPEE7We 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-27DEIf that test shows we do NOT have a STRING (meaning this was really a MEMcall, jump to forward to 27ECH
27DF-27E1Free up the argument and set up to give some free string space
27E5-27E7LD HL,(40A0H)LD HL,(STKTOP)2A A0 40Load HL with the start of string space pointer / bottom of free space.
NOTE:40A0H-40A1H holds the start of string space pointer
27E8EX DE,HLEBLoad DE with the start of string space pointer in HL
27E9-27EBLD HL,(40D6H)LD HL,(FRETOP)2A D6 40Load 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- ↳ GIVDBLLD A,L7DPrepare to do HL = HL - DE. First, load Register A with the LSB of the next available location in string space pointer in Register L
27EDSUB E93Subtract 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
27EELD L,A6FLoad Register L with the LSB of the amount of string space remaining in Register A
27EFLD A,H7CLoad Register A with the MSB of the next available location in string space pointer in Register H
27F0SBC A,D9ASubtract 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
27F1LD H,A67Load Register H with the MSB of the amount of string space remaining in Register A
27F2-27F4Jump 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- ↳ POSLD A,(40A6H)LD A,(TTYPOS)3A A6 40Load Register A with the current cursor line position.
Note: 40A6H holds the current cursor line position
27F8- ↳ SNGFLTLD L,A6FLoad Register L with the value of the current cursor line position in Register A.
27F9XOR AAFZero Register A
27FA- ↳ GIVINTLD H,A67Load Register H with zero, so now HL is 00 + cursor position
27FE-2818 - LEVEL II BASIC USR(x)ROUTINE- "USRFN"
27FE-2780- ↳ USRFNGOSUB to DOS to see if DOS wants to deal with this
2801RST 10HCHRGETD7We 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-2804GOSUB to 252CH to evaluate the expression at the location of the current BASIC program pointer in HL and return with the result in ACCumulator
2805PUSH HLE5Save the value of the current BASIC program pointer in HL (=the address of the next element in the code string) to the STACK
2806-2808Load HL with the return address of 0890H which will clear the STACK before returning to BASIC
2809PUSH HLE5Save the value of the return address in HL to the STACK
280A-280CLD A,(40AFH)LD A,(VALTYP)3A AF 40Load Register A with the value of the current number type flag for the argument provided.
Note: 40AFH holds Current number type flag
280DPUSH AFF5Save the value of the current number type flag in Register A.
(02=INT, 03=STR, 04=SNG, 08=DBL)
280E-280FCP 03HFE 03Check 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-2812If the current result is a string then GOSUB to 29DAH to get the free the space and get the string address into HL
2813POP AFF1Restore the number type flag into Register A
2814EX DE,HLEBLoad DE with the (possible) value of the pointer to the string (in HL)
2815-2817LD HL,(408EH)2A 8E 40Load 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.
2818JP (HL)E9Jump 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- ↳ DOCNVFPUSH HLE5Save the pointer to the current character in the BASIC program being evaluated to the STACK
281A-281BAND 07HE6 07Mask 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-281ELoad HL with the address of the arithmetic conversion routines
281FLD C,A4FLoad Register C with the value of the number type flag in Register A.
(02=INT, 03=STR, 04=SNG, 08=DBL)
2820-2821LD B,00H06 00Zero Register B. Now BC holds 00 + type and will be the two byte offset into the table
2822ADD HL,BC09Add the offset (of BC) to the base arithmetic conversion routines, to find the right jump point
2823-2825GOSUB to 2586H to convert the current result in REG l to its proper number type
2826POP HLE1Restore the pointer to the current character in the BASIC program being evaluated to HL
2827RETC9RETurn 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- ↳ ERRDIRPUSH HLE5Save whatever was in HL to the STACK
2829-282BLD HL,(40A2H)LD HL,(CURLIN)2A A2 40Load HL with the value of the current BASIC line number (which is stored at 40A2H-40A3H).
282CINC HL23Bump 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
282DLD A,H7CLoad Register A with the MSB of the current BASIC line number in Register H
282EOR LB5Combine 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.
282FPOP HLE1Restore whatever was in HL on entry back into HL
2830RET NZC0Return 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-2832LD E,16H1E 16Load Register E with the ?ID ERRORcode
2836-2856 - STRING ROUTINE - STR$logic- "STR$"
2836-2838- ↳ STR$GOSUB to 0FBDH to convert the current result in ACCumulator to an ASCII string
2839-283B- ↳ STR$1Scan it and turn it into a string (make a temporary string work area entry)
283F-2841Load BC with a return address of 2A2BH (which cleans the STACK and then jumps to 2884H)
2842PUSH BCC5Save 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- ↳ STRCPYLD A,(HL)7ELoad Register A with the string's length at the location of the string's VARPTR in HL
2844INC HL23Bump the value of the string's VARPTR in HL
2845PUSH HLE5Save the value of the string's VARPTR in HL to the STACK
2846-2848GOSUB to 28BFH to test the remaining string area to make sure that the new string will fit
2849POP HLE1Reload HL with the string's VARPTR. This is the destination to where the string should be copied.
284ALD C,(HL)4ELoad Register C with the LSB of the string's address at the location of the string's VARPTR in HL
284BINC HL23Bump the value of the string's VARPTR in HL
284CLD B,(HL)46Load Register B with the MSB of the string's address at the location of the string's VARPTR in HL
284D-284FGOSUB to 285AH to save the string's length and the string's address at 40D3H, so as to set up DSCTMP
2850PUSH HLE5Save the pointer to STRAD2 (which is 40D3H) to the STACK
2851LD L,A6FLoad Register L with the string's length (from Register A)
2852-2854GOSUB to 29CEH to move L characters from the temp area (of BC) to the string data area (in DE)
2855POP DED1Restore the pointer to DSCTMP (40D3H) into Register Pair DE
2856RETC9RETurn to CALLer
2857-2864 - STRING ROUTINE- "STRINI"
2857-2859- ↳ STRINIGOSUB 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- ↳ STRAD2Load HL with the address of the temporary string parameter storage area.
Note: 40D3H-40D5H holds Used for temporary string VARPTR's
285D- ↳ STRAD1PUSH HLE5Save the address of the temporary string parameter area in HL to the STACK
285ELD (HL),A77Save the string's length in Register A at the location of the temporary string parameter storage pointer in HL
285F- ↳ PUTDEIINC HL23The 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
2860LD (HL),E73Save the LSB of the string's address in Register E at the location of the temporary string parameter storage pointer in HL
2861INC HL23Bump the value of the temporary string parameter storage pointer in HL
2862LD (HL),D72Save the MSB of the string's address in Register D at the location of the temporary string parameter storage pointer in HL
2863POP HLE1Get the address of the temporary string parameter storage area from the STACK and put it in HL
2864RETC9RETurn 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- ↳ STRLITDEC HL2BDecrement the value of the current BASIC program pointer in HL
2866-2867- ↳ STRLTILD B,22H06 22Load Register B with a "(which is really the end of the quote search character)
2868- ↳ STRLT3LD D,B50Load Register D with a "(which is really the ending search character)
2869- ↳ STRLT2PUSH HLE5Save 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-286BLD C,0FFH0E FFLoad Register C with a -1
286C- ↳ STRGETINC HL23Bump the value of the current BASIC program pointer in HL to skip over that initial "
286DLD A,(HL)7ELoad Register A with the character at the location of the current BASIC program pointer in HL
286EINC C0CBump the counter in Register C
286FOR AB7Check 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-2871Jump 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
2872CP DBACheck 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-2874Jump 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
2875CP BB8Check 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-2877Loop 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- ↳ STRFINCP 22HFE 22Check 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-287CIf 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)
287DEX (SP),HLE3Exchange the value of the current BASIC program pointer in HL with the string's address to the STACK
287EINC HL23Bump the string's address in HL until it points to the first character of the string
287FEX DE,HLEBLoad DE with the temporary pointer string's address in HL
2880LD A,C79Load Register A with the string's length from Register C
2881-2883GOSUB to 285AH to save the string's length and the string's address into 40D3H (i.e., DSCTMP)
2884-2886- ↳ PUTNEWLoad DE with the address of the string parameter storage area.
Note: 40D3H-40D5H holds Used for temporary string VARPTR's
2887-2888LD A,D5H3E D5This 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- ↳ PUTTMPPUSH DED5Save a pointer to the stat of the string to the STACK
2889-288BLD HL,(40B3H)LD HL,(TEMPPT)2A B3 40Load 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-288ELD (4121H),HLLD (FACLO),HL22 21 41Save 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-2890LD A,03H3E 03Load Register A with the string number type flag
2891-2893LD (40AFH),ALD (VALTYP),A32 AF 40Save the value in Register A as the current number type flag.
Note: 40AFH holds current number type flag
2897-2899Depending on how we got here, DE will be different. If the jump was into PUTTMP, then DE will NOT equal FRETOP.
289ARST 18HCOMPARDFWe 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-289DLD (40B3H),HLLD (TEMPPT),HL22 B3 40Save 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
289EPOP HLE1Get the value of the current BASIC program pointer from the STACK and put it in HL
289FLD A,(HL)7ELoad Register A with the character at the location of the current BASIC program pointer in HL
28A0RET NZC0Return 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-28A2LD E,1EH1E 1ELoad Register E with a ?ST ERRORcode
28A3-28A5Display 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- ↳ STROUIINC HL23Bump 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"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- ↳ STRPRTGOSUB to 29DAH to build a temporary string work area entry for the message pointed to by FACLO
28AD-28AFGo get the string's length in Register D and the string's address in BC
28B0INC D14Bump the value of the string's length in Register D in preparation for the following loop which starts with a DEC D
28B1- ↳ STRPR2DEC D15Top of a loop. Decrement the value of the string's length in Register D
28B2RET ZC8Return if all of the characters in the string have been sent to the current output device.
28B3LD A,(BC)0ALoad Register A with the character at the location of the string pointer in BC
28B4-28B6Go send the character in Register A to the current output device
28B7-28B8CP 0DHFE 0DCheck 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-28BBJump to 2103H if the character in Register A is a carriage return
28BCINC BC03Bump the value of the string pointer in BC
28BD-28BELoop 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- ↳ GETSPAOR AB7Make sure A is not zero. A ZERO FLAG will signal that garbage collection has happened
28C0-28C1LD C,0F1H0E F1Z-80 Trick. If passing through, the C just changes and the POP AF which follows is ignored.
28C1- ↳ TRYGI2POP AFF1Get the string's length from the STACK and put it in Register A
28C2PUSH AFF5Save the length of the string in Register A to the STACK
28C3-28C5LD HL,(40A0H)LD HL,(STKTOP)2A A0 40Load HL with the poinmter to the bottom of the string space. 40A0H-40A1H holds the start of string space pointer
28C6EX DE,HLEBMove the bottom of the string space pointer into DE. We don't care what happens to HL
28C7-28C9LD HL,(40D6H)LD HL,(FRETOP)2A D6 40Load HL with the pointer to the TOP of free space.
Note: 40D6H-40D7H holds the next available location in string space pointer
28CACPL2FComplement the string's length in Register A so that it is negative
28CBLD C,A4FThe 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-28CDLD B,0FFH06 FFLoad Register B with a -1 so that BC will be the negative length of the string
28CEADD HL,BC09Add the negative string's length in BC to the top of free space pointer in HL
28CFINC HL23Bump the value of the adjusted next available location in string space pointer in HL
28D0RST 18HCOMPARDFWe 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-28D2If 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-28D5LD (40D6H),HLLD (FRETOP),HL22 D6 40Save the value in HL as new bottom of MEMORY
28D6INC HL23Bump the value of HL to point to the string
28D7EX DE,HLEBLoad DE with the pointer to the string
28D8- ↳ PPSWRTPOP AFF1Get the string's length from the STACK and put it in Register A
28D9RETC9RETurn to CALLer
28DA-298E - STRING ROUTINE- "GARBAG"
28DA- ↳ GARBAGPOP AFF1Get the garbage collection code which was PUSHed
28DB-28DCLD E,1AH1E 1ALoad Register E with an ?OS ERRORcode
28DD-28DFIf 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
28E0CP ABFOtherwise, 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.
28E1PUSH AFF5Save the garbage collection flag to the STACK
28E2-28E4Load BC with a return address of 28C1H (which would retry allocation)
28E5PUSH BCC5Save that return address to the STACK
28E6-28E8- ↳ GARBA2LD HL,(40B1H)LD HL,(MEMSIZ)2A B1 40Load HL with the top of memory pointer.
Note: 40B1H-40B2H holds MEMORY SIZE? pointer
28E9-28EB- ↳ FNDVARLD (40D6H),HLLD (FRETOP),HL22 D6 40Save 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
28EFPUSH HLE5Save the value in HL to the STACK to indicate that we didn't find a variable on this pass
28F0-28F2LD HL,(40A0H)LD HL,(STKTOP)2A A0 40Load 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
28F3PUSH HLE5Save high address (start of string space pointer in HL) to the STACK
28F4-28F6Load HL with the start of the temporary string work area pointer.
Note: 40B5H-40D2H holds Temporary string work area
28F7
"TVAR"EX DE,HLEBLoad DE with the start of the temporary string work area pointer in HL
28F8-28FALD HL,(40B3H)LD HL,(TEMPPT)2A B3 40Load 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
28FBEX DE,HLEBExchange 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
28FCRST 18HCOMPARDFWe 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.
2900-2902Jump to 294AH to do the temporary variable garbage collection if the temporary string work area isn't empty
2903- ↳ SVARSLD HL,(40F9H)LD HL,(VARTAB)2A F9 40Load HL with the start of the simple variables pointer.
NOTE:40F9H-40FAH holds the starting address of the simple variable storage area
2906- ↳ SVAREX DE,HLEBLoad DE with the simple variables pointer in HL
2907-2909LD HL,(40FBH)LD HL,(ARYTAB)2A FB 40Load 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
290AEX DE,HLEBSwap DE and HL so that DE holds the end and HL holds the start.
290BRST 18HCOMPARDFNow 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-290DIf 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.
290ELD A,(HL)7ELoad Register A with second character of the variable name
290FINC HL23Bump the value of the simple variables pointer in HL
2910INC HL23Bump the simple variables pointer in HL
2911INC HL23Bump the value of the simple variables pointer in HL. HL should now point to the value of the variable.
2912-2913CP 03HFE 03Check 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-2915Jump to 291AH if the variable at the location of the simple variables pointer in HL isn't a string
2919XOR AAFZero Register A to indicate that we should not be skipping anything else.
291A- ↳ SKPVARLD E,A5FZero Register E
291B-291CLD D,00H16 00Zero Register D. Now DE should be the number of characters to skip.
291DADD HL,DE19Add the number of characters to skip (held in DE) to the simple variables pointer in HL
2920- ↳ ARYVA2POP BCC1Clean up the STACK
2921- ↳ ARYVAREX DE,HLEBLoad DE with the value of the array variables pointer (ARTVAR) in HL
2922-2924LD HL,(40FDH)LD HL,(STREND)2A FD 40Load HL with the end of the arrays / start of free memory pointer.
Note: 40FDH-40FEH holds Free memory pointer
2925EX DE,HLEBExchange the value of the array variables pointer in DE with the value of the free memory pointer in HL
2926RST 18HCOMPARDFWe 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-2929Jump if the array variables pointer in Register HL is the same as the start of the free memory pointer in DE
292ALD A,(HL)7ELoad Register A with the number type flag at the location of the array variables pointer in HL
292BINC HL23Bump the value of the array variables pointer in HL
292C-292EGet 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)
292FPUSH HLE5Save the pointer to the DIMS to the STACK
2930ADD HL,BC09Add the value of the offset to the next array in BC to the value of the array variables pointer in HL
2931-2932CP 03HFE 03Check 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-2934If the array being examined isn't a string then skip it via a JUMP to 2920H
2935-2937LD (40D8H),HLLD (TEMP3),HL22 D8 40Save the address of the end of the array being examined.
Note: 40D8H-40D9H holds Temporary storage location
2938POP HLE1Get the value of the array variables pointer from the STACK and put it in HL
2939LD C,(HL)4ELoad Register C with the number of subscripts for the array at the location of the array variables pointer in HL
293A-293BLD B,00H06 00Zero Register B so that BC holds the number of dimensions
293CADD HL,BC09Add the number of subscripts in the array in BC to the value of the array variables pointer in HL
293DADD HL,BC09Add 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.
293EINC HL23Bump the value of the array variables pointer in HL one more time to account for #DIMs.
293F- ↳ ARYSTREX DE,HLEBLoad DE with the value of the current position in the array variables pointer in HL
2940-2942LD HL,(40D8H)LD HL,(TEMP3)2A D8 40Load HL with the address of the end of this array.
Note: 40D8H-40D9H holds Temporary storage location
2943EX DE,HLEBSwap DE and HL so that HL now points to the current position
2944RST 18HCOMPARDFWe 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-2946If the Z FLAG is set then we are at the end of an array so JUMP to 2921H to try the next array
294A- ↳ DVAR2PUSH BCC5Save the value in BC to the STACK
294B- ↳ DVAR and DVARSXOR AAFZero Register A so we can test for a null string
294COR (HL)B6Load Register A with the length of the string at the location of the array variables pointer in HL
294DINC HL23Bump the value of the array variables pointer in HL
294ELD E,(HL)5ELoad Register E with the LSB of the string's address at the location of the array variables pointer in HL
294FINC HL23Bump the value of the array variables pointer in HL
2950LD D,(HL)56Load 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.
2951INC HL23Bump the value of the array variables pointer in HL
2952RET ZC8Return if the string's length in Register A is equal to zero
2953
2954LD B,H
LD C,L44Let BC = HL
2955-2957LD HL,(40D6H)LD HL,(FRETOP)2A D6 40Load HL with the location of the the top of string free space.
NOTE:40D6H-40D7H holds the next available location in string space pointer
2958RST 18HCOMPARDFWe 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
295ALD H,B
LD L,C60Let HL = BC
295BRET CD8If 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
295CPOP HLE1Get the return address from the STACK and put it in HL
295DEX (SP),HLE3Swap (SP) and HL so that the return address is back on the STACK and HL holds the maximum number seen
295ERST 18HCOMPARDFNow 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.
295FEX (SP),HLE3Swap (SP) and HL so that the return address is back in HL and the STACK holds the maximum number seen
2960PUSH HLE5Save the return address in HL to the STACK
2961
2962LD H,B
LD L,C60Let HL = BC
2963RET NCD0Return if the string's address in DE is below the string space pointer
2964POP BCC1Get the return address from the STACK and put it in BC
2965POP AFF1Clean up the STACK (remove the MAX SEEN)
2966POP AFF1Clean up the STACK (remove the VARIABLE POINTER)
2967PUSH HLE5Save the value of the array variables pointer in HL to the STACK
2968PUSH DED5Save the new MAX pointer in DE to the STACK
2969PUSH BCC5Save the value of the return address in BC to the STACK
296ARETC9RETurn to CALLer
If we are here, we have made one complete pass through the string variables.
296B- ↳ GRBPASPOP DED1Load DE with the address of the last string put into the temporary string work area (aka the MAX pointer)
296CPOP HLE1Get the variable pointer from the STACK and put it in HL
296DLD A,L7DLoad Register A with the LSB of the variable pointer in Register L
296EOR HB4Combine 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
296FRET ZC8If HL=0 then we are at the end of the garbage collection, so return
2970DEC HL2BDecrement the value of the temporary string work area pointer in HL, currently just past the string descriptor
2971LD B,(HL)46Load Register B with the MSB of the string's address at the location of the temporary string work area pointer in HL
2972DEC HL2BDecrement the value of the temporary string work area pointer in HL
2973LD C,(HL)4ELoad 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.
2974PUSH HLE5Save 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.
2975DEC HL2BDecrement the value of the temporary string work area pointer in HL
2976LD L,(HL)6ELoad Register L with the string's length at the location of the temporary string work area pointer in HL
2977-2978LD H,00H26 00Zero Register H so that HL is now the string's character count
2979ADD HL,BC09Add the length of the string in HL to the string's address in BC. HL now points just beyond the string.
297ALD D,B50Load Register D with the MSB of the string's address in Register B
297BLD E,C59Load Register E with the LSB of the string's address in Register C. DE now is the original pointer to the string.
297CDEC HL2BDecrement the value of the string's ending address in HL to avoid moving one beyond the string
297DLD B,H44Load Register B with the MSB of the string's ending address in Register H
297ELD C,L4DLoad Register C with the LSB of the string's ending address in Register L. BC now points to the top of the string
297F-2981LD HL,(40D6H)LD HL,(FRETOP)2A D6 40Load HL with the top of free space.
NOTE:40D6H-40D7H holds the next available location in string space pointer
2982-2984Move the string from the temporary storage location to string space
2985POP HLE1Get back the pointer to the description of the variable from the STACK and put it in HL
2986LD (HL),C71Save the LSB of the string's permanent address (held in Register C) to (HL)
2987INC HL23Bump the value of the temporary string work area pointer in HL
2988LD (HL),B70Save the MSB of the string's permanent address (held in Register C) to (HL)
2989LD L,C69Load Register L with the LSB of the string's address in Register C
298ALD H,B60Load 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.
298BDEC HL2BDecrement the string's address in HL to adjust FRETOP
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- ↳ CATPUSH BCC5Save the precedence/operator value in BC to the STACK
2990PUSH HLE5Save the value of the current BASIC program pointer in HL to the STACK
2991-2993LD HL,(4121H)LD HL,(FACLO)2A 21 41Load HL with the first string's VARPTR (from ACCumulator)
2994EX (SP),HLE3Exchange the value of the first string's VARPTR in HL with the value of the current BASIC program to the STACK
2995-2997GOSUB to 249FH to evaluate the expression at the location of the current BASIC program pointer in HL
2998EX (SP),HLE3Exchange the value of the current BASIC program pointer in HL with the value of the first string's VARPTR to the STACK
299CLD A,(HL)7ELoad Register A with the first string's length at the location of the first string's VARPTR in HL
299DPUSH HLE5Save the value of the first string's descriptor (VARPTR) in HL to the STACK
299E-29A0LD HL,(4121H)LD HL,(FACLO)2A 21 41Load HL with the second string's VARPTR in ACCumulator
29A1PUSH HLE5Save the second string's VARPTR in HL to the STACK
29A2ADD A,(HL)86Add 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-29A4LD E,1CH1E 1CLoad Register E with a ?LS ERRORcodein case the two string lengths are not less than 256.
29A5-29A7Display a ?LS ERRORmessage if the combined lengths of the strings is greater than 255
29A8-29AAGo make sure that there is enough string space for the new string
29ABPOP DED1Get the second string's VARPTR from the STACK and put it in DE
29AFEX (SP),HLE3Exchange the second string's VARPTR in HL with the first string's VARPTR to the STACK
29B3PUSH HLE5Save the value of the first string's VARPTR in HL to the STACK
29B4-29B6- ↳ INCSTRLD HL,(40D4H)LD HL,(DSCTMP+1)2A D4 40Load HL with the pointer to the first string's address
29B7EX DE,HLEBLoad DE with the first string's address in HL
29C1EX (SP),HLE3Exchange the value of the return address in HL with the value of the current BASIC program pointer to the STACK
29C2PUSH HLE5Save the value of the current BASIC program pointer in HL to the STACK
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- ↳ MOVINSPOP HLE1Load HL with the value of the return address to the STACK
29C7EX (SP),HLE3Swap (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.
29C8LD A,(HL)7ELoad Register A with the string's length at the location of the string's VARPTR in HL
29C9INC HL23Bump the value of the string's VARPTR in HL
29CALD C,(HL)4ELoad Register C with the LSB of the string's address at the location of the string's VARPTR in HL
29CBINC HL23Bump the value of the string's VARPTR in HL
29CCLD B,(HL)46Load 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.
29CDLD L,A6FLoad Register L with the string's length in Register A
29CE- ↳ MOVSTRINC L2CIncrement the value of the string's length in Register L in preparation for the next instruction which is a loop.
29CF- ↳ MOVLPDEC L2DDecrement the value of the string's length in Register L
29D0RET ZC8If L hits zero then we have moved all the characters, so RETurn
29D1LD A,(BC)0AIf 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
29D2LD (DE),A12Save the character in Register A at the location of the string storage pointer in DE
29D3INC BC03Bump the value of the string pointer in BC
29D4INC DE13Bump the value of the string storage pointer in DE
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- ↳ FRESTRGOSUB to 0AF4H to make sure that the current result in REG l is a string
29DA-29DC- ↳ FREFACLD HL,(4121H)LD HL,(FACLO)2A 21 41Load HL with the string's VARPTR in ACCumulator
29DD- ↳ FRETM2EX DE,HLEBLoad DE with the value of the string's VARPTR in HL
29DE-29E0- ↳ FRETMPCheck to see if the string is the last entry in the temporary string work area
29E1EX DE,HLEBLoad HL with the value of the string's VARPTR in DE
29E2RET NZC0Return if the string isn't the last entry in the temporary string work area
29E3PUSH DED5Save the value of the string's VARPTR in DE to the STACK
29E4LD D,B50Load Register D with the MSB of the string's address in Register B
29E5LD E,C59Load Register E with the LSB of the string's address in Register C. DE now points to the string.
29E6DEC DE1BDecrement the value of the string's address in DE
29E7LD C,(HL)4ELoad Register C with the string's length at the location of the string's VARPTR in HL
29E8-29EALD HL,(40D6H)LD HL,(FRETOP)2A D6 40Load 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
29EBRST 18HCOMPARDFWe 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-29EDJump 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
29EELD B,A47If 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
29EFADD HL,BC09Add the length of the string in BC to the next available location in string space pointer in HL
29F0-29F2LD (40D6H),HLLD (FRETOP),HL22 D6 40Save the adjusted next available location in string space pointer in HL.
Note: 40D6H-40D7H holds the next available location in string space pointer
29F3- ↳ NOTLSTPOP HLE1Get the string's VARPTR from the STACK and put it in HL
29F4RETC9RETurn 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- ↳ FRETMSLD HL,(40B3H)LD HL,(TEMPPT)2A B3 40Load HL with the temporary string work area pointer.
Note: 40B3H-40B4H holds the next available location in the temporary string work area pointer
29F8DEC HL2BDecrement the value of the temporary string work area pointer in HL which backs up two words
29F9LD B,(HL)46Load Register B with the MSB of the string's address at the location of the temporary string work area pointer in HL
29FADEC HL2BDecrement the value of the temporary string work area pointer in HL
29FBLD C,(HL)4ELoad Register C with the LSB of the string's address at the location of the temporary string work area pointer in HL
29FCDEC HL2BDecrement the value of the temporary string work area pointer in HL
29FDRST 18HCOMPARDFWe 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.
29FERET NZC0If the Z FLAG is set then we are done freeing space, so RETURN
29FF-2A01LD (40B3H),HLLD (TEMPPT),HL22 B3 40Save 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
2A02RETC9RETurn to CALLer
2A03-2A0E - LEVEL II BASIC LENROUTINE- "LEN"
2A06PUSH BCC5Save the return address of 27F8H (in BC) to the STACK
2A07-2A09- ↳ LEN1GOSUB to 29D7H to free up the temporary variable pointed to by FACLO
2A0AXOR AAFZero Register A so as to force a numeric flag
2A0BLD D,A57Zero Register D so that DE can be used when E is set.
2A0CLD A,(HL)7ELoad Register A with the string's length at the location of the string's VARPTR in HL
2A0DOR AB7Set the flags according to the string's length in Register A
2A0ERETC9RETurn to CALLer
2A0F-2A1E - LEVEL II BASIC ASCROUTINE- "ASC"
2A12PUSH BCC5Save the return address of 27F8H (in BC) to the STACK
2A13-2A15- ↳ ASC2GOSUB 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-2A18If the Z FLAG is set, then this was a null string (bad argument), so display a ?FC ERROR
2A19INC HL23Bump the value of the string's VARPTR in HL
2A1ALD E,(HL)5ELoad Register E with the LSB of the address of the string's data at the location of the string's VARPTR in HL
2A1BINC HL23Bump the value of the string's VARPTR in HL
2A1CLD D,(HL)56Load Register D with the MSB of the address of the string's data at the location of the string's VARPTR in HL
2A1DLD A,(DE)1ALoad Register A with the first character at the location of the string pointer in DE
2A1ERETC9RETurn to CALLer
2A1F-2A20- ↳ CHR$LD A,01H3E 01Load Register A with the length of the string to be created
2A21-2A23GOSUB to 2857H to save the string's length in Register A and value and set up the string's address
2A24-2A26GOSUB 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- ↳ SETSTRLD HL,(40D4H)LD HL,(DSCTMP+1)2A D4 40Load HL with the temporary string's address
2A2ALD (HL),E73Save the character in Register E at the location of the string pointer in HL
2A2B- ↳ FINBCKPOP BCC1Clean up the STACK so that the RETURN address is another one that is next in the STACK.
2A2F-2A60 - LEVEL II BASIC STRING$ROUTINE- "STRNG$"
2A2FRST 10HCHRGETD7We 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-2A31RST 08H ⇒ 28SYNCHK "("CF 28Since 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-2A34This 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
2A34DEC HL2BBackspace the code string. This is a Z-80 trick because this code was part of the above call instruction
2A35PUSH DED5Save the string's length ("N") (currently in DE) to the STACK
2A36-2A37RST 08 ⇒ 2CSYNCHK ","CF 2CNow 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-2A3ANow 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-2A3CRST 08H ⇒ 29SYNCHK ")"CF 29Now 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).
2A3DEX (SP),HLE3We 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
2A3EPUSH HLE5Save the string's length ("N") in HL to the STACK so that we can test it to make sure it is an integer
2A3FRST 20HGETYPEE7We 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.
2A42-2A44We 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-2A46Skip the next instruction (which would load the string address and 1st character) by jumping to 2A4AH
2A47-2A49- ↳ STRSTRGo get the first character in the string and return with it in Register A. This is the character that will be repeated
2A4A- ↳ CALSPAPOP DED1Get the "N" from the STACK and put it in DE (well, actually, Register E)
2A4BPUSH AFF5Save the character for the string (held in Register A) to the STACK
2A4C- ↳ SPACE2PUSH AFF5and then save it to the STACK again
2A4DLD A,E7BLoad Register A with "N" (held in Register E)
2A4E-2A4FGOSUB to 2857H to allocate N bytes in the temporary string work area
2A51LD E,A5FLoad Register E with "N" (held in Register A)
2A52POP AFF1Get the character for the string ("X") from the STACK and put it in Register A
2A53INC E1CTo set the status flags we need to increase and then decrease E. First, bump the value of the string's length in Register E
2A54DEC E1D. and then decrement the string's length in Register E
2A55-2A56If "N" was zero (so that the string is now complete), jump back to 2A2BH
2A57-2A59LD HL,(40D4H)LD HL,(DSCTMP+1)2A D4 40If 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),A77Save the character in Register A ("X") at the location of the string pointer in HL
2A5BINC HL23Bump the value of the string pointer in HL
2A5CDEC E1DDecrement the string's length in Register E
2A5D-2A5EJR NZ,2A5AH20 FBLoop back to 2A5AH to keep filling (HL) until the string of X's has been completed
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$Go check the syntax. The character at the location of the current BASIC program pointer in HL must be a )
2A64XOR AAFZero Register A because the string pointer never changes
2A65- ↳ LEFT3EX (SP),HLE3Exchange the value of the current BASIC program pointer in HL with the string's VARPTR to the STACK
2A66LD C,A4FZero Register C
2A67-2A68LD A,0E5H3E E5Z-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- ↳ LEFT2PUSH HLE5Save the value of the string's VARPTR in HL to the STACK
2A6ALD A,(HL)7ELoad Register A with the string's length at the location of the string's VARPTR in HL
2A6BCP BB8Check 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-2A6DJump to 2A70H if the new string's length in Register B is greater than the string's length in Register A
2A6ELD A,B78Load Register A with the new string's TRUNCATED length in Register B
2A6F-2A71LD DE,000EH11 0E 00Z-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
2A72PUSH BCC5Save the offset in BC to the STACK
2A76POP BCC1Get the offset from the STACK and put it in BC
2A77POP HLE1Get the string's VARPTR from the STACK and put it in HL
2A78PUSH HLE5Save the string's VARPTR in HL to the STACK
2A79INC HL23Bump HL to now point to the address of the string
2A7ALD B,(HL)46Load Register B with the LSB of the string's address at the location of the string's VARPTR in HL
2A7BINC HL23Bump the value of the string's VARPTR in HL
2A7CLD H,(HL)66Load Register H with the MSB of the string's address at the location of the string's VARPTR in HL
2A7DLD L,B68Load Register L with the LSB of the string's address in Register B
2A7E-2A7FLD B,00H06 00Zero Register B so that BC can be used
2A80ADD HL,BC09Add the string's length in BC to the string's address in HL
2A81LD B,H44Load Register B with the MSB of the string's ending address in Register H
2A82LD C,L4DLoad Register C with the LSB of the string's ending address in Register L
2A83-2A85Go save the string's length (held in A) and the string's starting address (held in DE)
2A86LD L,A6FLoad Register L with the string's length (i.e., the number of characters to move) held in Register A
2A8APOP DED1Clean up the STACK
2A91-2A99 - LEVEL II BASIC RIGHT$ ROUTINE- "RIGHT$"
2A91-2A93Go check the syntax. The character at the location of the current BASIC program pointer in HL must be a )
2A94POP DED1Get the string's VARPTR from the STACK and put it in DE
2A95PUSH DED5Save the string's VARPTR in DE to the STACK
2A96LD A,(DE)1ALoad Register A with the string's length (held at the location of the string's VARPTR in DE)
2A97SUB B90Subtract the new string's length in Register B from the string's length in Register A to isolate the number of bytes
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,HLEBLoad HL with the value of the current BASIC program pointer (held in DE)
2A9BLD A,(HL)7ELoad Register A with the terminal character, currently held at the location of the current BASIC program pointer in HL
2A9C-2A9EGOSUB to 2AE2H to get the offset in Register B and the string's VARPTR in DE
2A9FINC B04We 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
2AA0DEC B05... and then we decrement the value of the string's offset position in Register B
2AA4PUSH BCC5Save the value of the offset position (held in Register B) to the STACK
2AA5-2AA6LD E,0FFH1E FFLoad Register E with the default string's length of 256 in case no number of bytes are given
2AA7-2AA8CP 29HCP ")"FE 29More 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-2AAAJump to 2AB0H if the character at the location of the current BASIC program pointer in Register A is a )
2AAB-2AACRST 08H ⇒ 2CSYNCHK ","CF 2CIf 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-2AAFGo 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-2AB1RST 08H ⇒ 29SYNCHK ")"CD 29Since 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).
2AB2POP AFF1Get the offset from the STACK and put it in Register A
2AB3EX (SP),HLE3Exchange the value of the current BASIC program pointer in HL with the value of the string's VARPTR to the STACK
2AB4-2AB6Load BC with 2A69H as the return address (which is in the LEFT$routine)
2AB7PUSH BCC5Save the return address in BC to the STACK
2AB8DEC A3DDecrement the value of the requested offset in Register A so that we have a starting position minus 1
2AB9CP (HL)BECompare 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-2ABBLD B,00H06 00Zero Register B
2ABCRET NCD0If the offset pointer ispast the end of the string we are going to return a null
2ABDLD C,A4FLoad Register C with the offset in Register A
2ABELD A,(HL)7ELoad Register A with the string's length at the location of the string's VARPTR in HL
2ABFSUB C91Subtract the index (the second argument) in Register C from the string's length in Register A
2AC0CP EBBCompare 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.
2AC1LD B,A47Load Register B with the calculated string's length in Register A
2AC2RET CD8If we are not going to truncate, then just use the partial string we already have and Return to 2A69H
2AC3LD B,E43Load Register B with the new string's truncated length in Register E
2AC4RETC9Return to 2A69H aka LEFT2
2AC5-2ADE - LEVEL II BASIC VALROUTINE- "VAL"
2AC5-2AC7- ↳ VALGo get the string's length in Register A and the string's VARPTR in HL
2AC8-2ACAJump to 27F8H if the string's length in Register A is equal to zero
2ACBLD E,A5FLoad 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.
2ACCINC HL23Bump the value of the string's VARPTR in HL
2ACDLD A,(HL)7ELoad Register A with the LSB of the string's address at the location of the string's VARPTR in HL
2ACEINC HL23Bump the value of the string's VARPTR in HL
2ACFLD H,(HL)66Load Register H with the MSB of the string's address at the location of the string's VARPTR in HL
2AD0LD L,A6FLoad Register L with the LSB of the string's address in Register A
2AD1PUSH HLE5Save the value of the string's address in HL to the STACK
2AD2ADD HL,DE19Add the string's length in DE to the string's address in HL
2AD3LD B,(HL)46Load Register B with the last character of the string at the location of the string pointer in HL
2AD4LD (HL),D72Save the zero in Register D at the location of the string pointer in HL
2AD5EX (SP),HLE3Exchange the string's ending address in HL with the string's address to the STACK
2AD6PUSH BCC5Save the last character of the string in Register B to the STACK
2AD7LD A,(HL)7ELoad Register A with the first character of the argument
2AD8-2ADACall 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)
2ADBPOP BCC1Get the modified character of the next string into Register B
2ADCPOP HLE1Get the pointer to the modified character back into HL
2ADDLD (HL),B70Restore the character.
2ADERETC9RETurn 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- ↳ PREAMEX DE,HLEBLoad HL with the value of the current BASIC program pointer in DE
2AE0-2AE1RST 08H ⇒ 29SYNCHK ")"CF 29Since 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- ↳ PREAM2POP BCC1Get the return address from the STACK and put it in BC
2AE3POP DED1Get the number of bytes to isolate from the string (from the STACK) and put it in DE
2AE4PUSH BCC5Save the return address in BC to the STACK
2AE5LD B,E43Load Register B with the number of bytes in Register E
2AE6RETC9RETurn to CALLer
2AE7H-2AEE - Process a LEFT-HAND-SIDE MID$- "ISMID$"
2AE7-2AE8- ↳ ISMID$CP 7AHCP MIDTK-$ENDFE 7AThis 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.
2AEF-2AF7 - LEVEL II BASIC INPROUTINE- "FNINP"
2AEF-2AF1- ↳ FNINPGo evaluate the expression at the location of the current BASIC program pointer in HL and return with the port number in Register A
2AF2-2AF4LD (4094H),ALD (STAINP+1),A32 94 40Save the value of the port number (from Register A) into 4094H, which is in the middle of a routine.
2AF8-2B00 - LEVEL II BASIC OUTROUTINE- "FNOUT"
2B01-2B0D - EVALUATE EXPRESSION ROUTINE- "GETINT"
This evaluates an expression and leaves the result in DE as an integer.
2B01- ↳ GETINTRST 10HCHRGETD7We 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- ↳ GETIN2Go 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- ↳ INTFR2PUSH HLE5Save the value of the current BASIC program pointer in HL to the STACK
2B06-2B08Call 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)
2B09EX DE,HLEBLoad DE with the integer result in HL
2B0APOP HLE1Get the value of the current BASIC program pointer from the STACK and put it in HL
2B0BLD A,D7ALoad Register A with the MSB of the integer result in Register D
2B0COR AB7Test the value of the MSB in Register A
2B0DRETC9RETurn to CALLer
2B0E-2B16 - EVALUATE EXPRESSION ROUTINE - OUTcontinues here- "SETIO"
2B0E-2B10- ↳ SETIOGOSUB 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-2B13LD (4094H),ALD (STAINP+1),A32 94 40Save the 8-bit value in Register A in the DOS address of 4094H to set up for WAIT
2B14-2B16LD (4097H),ALD (OUTWRD+1),A32 97 40Save 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-2B18RST 08H ⇒ 2ESYNCHK ","CF 2ESince 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).
2B1B-2B28 - EVALUATE EXPRESSION ROUTINE - This is called by PRINT TAB- "GTBYTC".
2B1B- ↳ GTBYTCRST 10HCHRGETD7We 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- ↳ GETBYTGOSUB 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- ↳ CONINTGOSUB 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-2B24If the result is greater than 255, display a ?FC ERRORmessage
2B25DEC HL2BDecrement the value of the current BASIC program pointer in HL
2B26RST 10HCHRGETD7We 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.
2B27LD A,E7BLoad Register A with the 8-bit result in Register E so that both A and E have the result.
2B28RETC9RETurn 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- ↳ LLISTLD A,01H3E 01Load Register A with the printer output device code
2B2B-2B2DLD (409CH),ALD (PRTFLG),A32 9C 40Save 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- ↳ LISTPOP BCC1Get rid of the the return address on the STACK
2B2F-2B31Go evaluate the range of line numbers given at the location of the current BASIC program pointer in HL
2B32PUSH BCC5Save the address of the first BASIC line (held in BC) to the STACK
2B33-2B35- ↳ LIST4LD HL,FFFFH21 FF FFLoad 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-2B38LD (40A2H),HLLD (CURLIN),HL22 A2 40Save the value in HL as the current BASIC line number (which is stored at 40A2H-40A3H).
2B39POP HLE1Get the address of the first BASIC line to be listed (from the STACK) and put it in HL
2B3APOP DED1Get the value of the last BASIC line number to be listed (from the STACK) and put it in DE
2B3BLD C,(HL)4ELoad Register C with the LSB of the next BASIC line pointer at the location of the memory pointer in HL
2B3CINC HL23Bump the value of the memory pointer in HL
2B3DLD B,(HL)46Load Register B with the MSB of the next BASIC line pointer at the location of the memory pointer in HL
2B3EINC HL23Bump the value of the memory pointer in HL
2B3FLD A,B78Load Register A with the MSB of the next BASIC line pointer in Register B
2B40OR CB1Combine 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-2B43If we are at the elast line, then STOP and JUMP to 1A19H to the READY PROMPT.
2B47-2B49Go scan the keyboard to see if the BREAKkey or the shift-@key was pressed
2B4APUSH BCC5Save the address of the next BASIC line in BC to the STACK
2B4BLD C,(HL)4EWe 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
2B4CINC HL23Bump the value of the memory pointer in HL
2B4DLD B,(HL)46Load Register B with the MSB of the BASIC line number at the location of the memory pointer in HL
2B4EINC HL23Bump the value of the memory pointer in HL
2B4FPUSH BCC5Save the BASIC line number in BC to the STACK
2B50EX (SP),HLE3Swap (SP) and HL so that the line number is now in HL
2B51EX DE,HLEBSwap DE and HL so that the last BASIC line number is now in HL
2B52RST 18HCOMPARDFWe 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.
2B53POP BCC1Get the pointer to the location on the BASIC program line being processed and put it in BC
2B54-2B56If 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!
2B57EX (SP),HLE3Swap (SP) and HL so that the last BASIC line number is now on the STACK
2B58PUSH HLE5Save the address of the next BASIC line in HL to the STACK
2B59PUSH BCC5Save the pointer to the location on the BASIC program line being processed to the STACK
2B5AEX DE,HLEBLoad HL with the BASIC line number (from DE)
2B5B-2B5DLD (40ECH),HLLD (DOT),HL22 EC 40Save the BASIC line number in HL into DOT for use later in EDIT or LIST.
Note: 40ECH-40EDH holds EDIT line number
2B5E-2B60Call 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-2B62LD A,20H3E 20Load Register A with a space
2B63POP HLE1Get the value of the memory pointer from the STACK and put it in HL
2B67-2B69GOSUB 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-2B6CLD HL,(40A7H)LD HL,(BUFPNT)2A A7 40Load HL with the starting address of the input buffer.
Note: 40A7H-40A8H holds the input Buffer pointer
2B6D-2B6FSince 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
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)7ELoad Register A with the character at the location of the memory pointer in HL
2B76OR AB7Check to see if the character in Register A is an end of the string character (00H)
2B77RET ZC8Return if the character in Register A is an end of the string character
2B78-2B7AGo send the character in Register A to the current output device
2B7BINC HL23Bump the value of the memory pointer in HL
2B7C-2B7DLoop 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- ↳ BUFLINPUSH HLE5Save the BASIC line pointer in HL to the STACK
2B7F-2B81LD HL,(40A7H)LD HL,(BUFPNT)2A A7 40Load HL with the starting address of the input buffer.
Note: 40A7H-40A8H holds the input Buffer pointer
2B82
2B83LD B,H
LD C,L44LET Register Pair BC = Register Pair HL
2B84POP HLE1Get the value of the BASIC line pointer from the STACK and put it in HL
2B85JUMP 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.
2B88NOP
2B89- ↳ PLOOPINC BC03Top of a loop. Bump the value of the input buffer pointer in BC
2B8ADEC D15Decrement the character count in Register D
2B8BRET ZC8Return if 256 characters have been moved into the input buffer
2B8CINC HLMove one byte forward in the text.
2B8DLD A,(HL)Load register A with the character at the location of the BASIC line pointer in HL.
2B8EOR ASet the status flags to enable us to check to see if the character in register A is an end of the BASIC line character.
2B8FLD (BC),A02Save 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.
2B90RET ZC8Return if the character in Register A is an end of the BASIC line character
2B94CP FBHCheck to see if the character in register A is a ' token.
2B96-2B97Jump 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-2B9BDEC BC
DEC BC
DEC BC
DEC BC0BFirst, 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-2B9FINC D
INC D
INC D
INC D14Then, 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- ↳ NRQTTKCP 95HCP $ELSEFE 95Check 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-2BA4If 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-2BA6SUB 7FHD6 7FNext, 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
2BA7PUSH HLE5Save the value of the BASIC line pointer in HL to the STACK. Register L holds the reserved word number at this point.
2BA8LD E,A5FLoad Register E with the character in Register A
2BA9-2BABLoad HL with the starting address of the reserved words list
2BAC- ↳ LOPRESLD A,(HL)7ELoad Register A with the character at the location of the reserved words list pointer in HL
2BADOR AB7Test 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.
2BAEINC HL23Bump the value of the reserved words list pointer in HL
2BAF-2BB1If the character at the location of the reserved words pointer in Register A doesn't have bit 7 set then Jump back to 2BACH.
2BB2DEC E1DDecrement the counter
2BB3-2BB4Jump back to 2BACH if this isn't the reserved word for the token
2BB5-2BB6AND 7FHE6 7FReset 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- ↳ MORPURLD (BC),A02Save the character in Register A at the location of the input buffer pointer in BC
2BB8INC BC03Bump the value of the input buffer pointer in BC
2BB9DEC D15Decrement the value of the character counter in Register D
2BBA-2BBCIf 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
2BBDLD A,(HL)7ELoad Register A with the character at the location of the reserved words pointer in HL
2BBEINC HL23Bump the reserved words pointer in HL
2BBFOR AB7Test the value of the character in Register A
2BC0-2BC2Keep getting characters in this reserved word until we hit the next reserved word
2BC3POP HLE1Get the value of the BASIC line pointer from the STACK and put it in HL
2BC4-2BC5Jump 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- ↳ DELETEGOSUB to 1B10H to evaluate the line numbers at the location of the current BASIC program pointer in HL
2BC9POP DED1Get the value of the last BASIC line number to be deleted (in binary) from the STACK and put it in DE
2BCAPUSH BCC5Save the address of the first BASIC line to be deleted in BC to the STACK
2BCBPUSH BCC5Save the address of the first BASIC line to be deleted in BC to the STACK AGAIN!
2BCC-2BCEGOSUB 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-2BD0Since 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
2BD2LD D,H
LD E,L54Let Register Pair DE = Register Pair HL
2BD3EX (SP),HLE3Exchange the last BASIC line's address in HL with the first BASIC line's address to the STACK
2BD4PUSH HLE5Save the pointer to the first line in range to the STACK
2BD5RST 18HCOMPARDFWe 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- ↳ FCERRGDisplay 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
2BDC-2BDECall the WRITE MESSAGE routine at 28A7H to print the message pointed to by HL.
2BDFPOP BCC1Get the first BASIC line's address from the STACK and put it in BC
2BE3EX (SP),HLE3Swap (SP) and HL so that HL now points to the next BASIC line's address ...
2BE4- ↳ DELEX DE,HLEBand then put it into DE
2BE5-2BE7LD HL,(40F9H)LD HL,(VARTAB)2A F9 40Load HL with the end of the BASIC program pointer.
- Note: 40F9H-40FAH holds the starting address of the simple variable storage area.
2BE8- ↳ MLOOPLD A,(DE)1ALoad Register A with the character at the location of the memory pointer in DE
2BE9LD (BC),A02Save the character in Register A at the location of the memory pointer in BC
2BEAINC BC03Bump the value of the memory pointer in BC
2BEBINC DE13Bump the value of the memory pointer in DE
2BECRST 18HCOMPARDFNow 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-2BEELoop back to 2BE8H until the memory pointer in DE equals the end of the BASIC program pointer in HL
2BEFLD H,B60Load Register H with the MSB of the memory pointer in Register B
2BF0LD L,C69Load Register L with the LSB of the memory pointer in Register C
2BF1-2BF3LD (40F9H),HLLD (VARTAB),HL22 F9 40Save 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.
2BF4RETC9RETurn 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- ↳ CSAVECalls the WRITE LEADER routine at 0284H (which writes a Level II leader on the cassette unit set in Register A)
2BF82BFAH 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
2BFBPUSH HLE5Save 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
2BFF-2C00LD A,D3H3E D3Load Register A with the filename header byte (=D3H which is a "S" with the sign bit on)
2C01-2C03the 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
2C07LD A,(DE)1ALoad Register A with the first character of the filename at the location of the filename pointer in DE
2C08-2C0Athe 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-2C0DLD HL,(40A4H)LD HL,(TXTTAB)2A A4 40Load 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).
2C0EEX DE,HLEBLoad DE with the start of the BASIC program pointer in HL
2C0F-2C11LD HL,(40F9H)LD HL,(VARTAB)2A F9 40Load HL with the end of the BASIC program pointer.
- Note: 40F9H-40FAH holds the starting address of the simple variable storage area.
2C12- ↳ LOPSCOLD A,(DE)1ATop 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
2C13INC DE13Bump the value of the memory pointer in DE
2C14-2C16the 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)
2C17RST 18HCOMPARDFNow 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-2C19Loop back to 2C12H until the memory pointer in DE is equal to the end of the BASIC program pointer in HL
2C1DPOP HLE1Get the value of the current BASIC program pointer from the STACK and put it in HL
2C1ERETC9RETurn to CALLer
2C1F-2CA4 - LEVEL II BASIC CLOAD ROUTINE - ROM v1.0- "CLOAD"
2C22LD A,(HL)7ELoad Register A with the character at the location of the current BASIC program pointer in HL
2C23-2C24SUB 0B2HSUB $PRINTD6 B2Check 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-2C26Jump to the CLOAD?routine at 2C29H if the character at the location of the current BASIC program pointer in Register A is a ?
2C27XOR AAFOK - So this is now a straight CLOAD. First, zero Register A
2C28LD BC,232F01 2F 23Z-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- ↳ CLOADPCPL2FLoad Register A with a -1 for CLOAD?. It will still be a 0 if this is CLOAD
2C2AINC HL23Bump the value of the current BASIC program pointer in HL until it points to the next character after the ?in CLOAD?
2C2BPUSH AFF5Save the CLOAD/CLOAD?flag in Register A to the STACK
2C2CDEC HL2BDecrement the value of the current BASIC program pointer in HL so we can see if we are at the end
2C2DRST 10HCHRGETD7We 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-2C2FLD A,00H3E 00Zero Register A to allow for any filename
2C30-2C31Jump if the character at the location of the current BASIC program pointer in HL is an end of the BASIC statement character
2C32-2C34To 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-2C37Make sure the length is good, and save the pointer to the filename to Register Pair DE
2C38LD A,(DE)1ALoad Register A with the first character of the filename at the location of the filename pointer in DE
2C39- ↳ CLNONMLD L,A6FLoad Register L with the filename in Register A
2C3APOP AFF1Get the value of the CLOAD/CLOAD?flag from the STACK and put it in Register A
2C3BOR AB7Test the value of the CLOAD/CLOAD?flag in Register A (since CPL doesn't set any flags)
2C3CLD H,A67Load Register H with the value of the CLOAD/CLOAD?flag in Register A
2C3D-2C3FLD (4121H),HLLD (FACLO),HL22 21 41Save the value of the CLOAD/CLOAD?flag and the filename in HL in ACCumulator
2C43-2C45LD HL,(4121H)LD HL,(FACLO)2A 21 41Load HL with the CLOAD/CLOAD?flag and the filename in ACCumulator
2C46EX DE,HLEBLoad D with the CLOAD/CLOAD? flag and load Register E with the filename
2C47-2C48- ↳ LOPCLKLD B,03H06 03Load Register B with the number of header bytes
2C49-2C4B- ↳ LOPCL2Calls 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-2C4DSUB 0D3HD6 D3Check to see if the character in Register A is a filename header byte
2C4E-2C4FLoop if the character in Register A isn't a filename header byte
2C50-2C51Loop back to 2C49H until three filename header bytes have been read
2C52-2C54Now 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)
2C55INC E1CWe 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
2C56DEC E1DDecrement the value of the filename in Register E
2C57-2C58Jump to 2C5CH (to pretend the filename matched) if no filename was specified
2C59CP EBBIf 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-2C5BJump 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- ↳ NONAMCLD HL,(40A4H)LD HL,(TXTTAB)2A A4 40If 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- ↳ DOCRSLD B,03H06 03Load Register B with the number of zeros to look for to stop the load (which is 3)
2C61-2C63- ↳ DOCSMRCalls 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)
2C64LD E,A5FPreserve the character that was just read from tape into Register E
2C65SUB (HL)96Compare 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
2C66AND DA2Combine 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.
2C69LD (HL),E73At 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-2C6CMake sure there is more room, and toss a ?OM ERRORif there isn't.
2C6DLD A,(HL)7ELoad Register A with the character at the location of the memory pointer in HL
2C6EOR AB7Check to see if the byte just read in Register A is equal to zero
2C6FINC HL23Bump the value of the memory pointer in HL
2C70-2C71Loop if the byte in Register A isn't equal to zero (meaning that it isn't end of program or end of statement)
2C72-2C74Call the BLINK ASTERISK routine at 022CH which alternatively displays and clears an asterisk in the upper right hand corner of the video display
2C75-2C76Do that loop until three zeros in a row have been read from the cassette recorder
2C77-2C79LD (40F9H),HLLD (VARTAB),HL22 F9 40By 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-2C7CGOSUB 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.
2C80-2C82We 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-2C85LD HL,(40A4H)LD HL,(TXTTAB)2A A4 40Load 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).
2C86PUSH HLE5Save the start of the BASIC program pointer in HL to the STACK. FINI will need this value there.
2C8A-2C8CGOSUB 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-2C8FCall 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-2C92JP 1A18JP STPRDYC3 18 1AJUMP to STPRDY to pop NEWSTT from the STACK and then fall into the READY routine
2C93-2C95- ↳ SKPFILLD (3C3EH),A32 3E 3CGo display the filename on the video display
2C96-2C97- ↳ ZERSRFLD B,03H06 03Load Register B with the number of zeros to be found to stop the search
2C98-2C9A- ↳ GETCHZCalls 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)
2C9BOR AB7Check to see if the character in Register A is equal to zero
2C9E-2C9FLoop until three zeros in a row have been read from the cassette recorder
2CA0-2CA2Calls 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)
2CA5-2CA9 - MESSAGE STORAGE LOCATION- "NOOKCS"
2CA5-2CA9- ↳ NOOKCS"BAD" + 0DH + 00H42The 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- ↳ PEEKCall 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)
2CADLD A,(HL)7ELoad Register A with the value at the location of the memory pointer in HL
2CAE-2CB0Go save the 8-bit value in Register A as the current result in ACCumulator
2CB1-2CBC - LEVEL II BASIC POKEROUTINE- "POKE"
2CB1-2CB3- ↳ POKEGo evaluate the expression at the location of the current BASIC program pointer in HL and return with the integer result in DE
2CB4PUSH DED5Save the address the user wants to POKE to (held in DE) to the STACK
2CB5-2CB6RST 08H ⇒ 2ESYNCHK ","CF 2ESince 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-2CB9GOSUB 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
2CBAPOP DED1Get the address the user wants to POKE to from the STACK and put it in DE
2CBBLD (DE),A12Save the value the user wanted to poke (held in Register A) in the location that the user wants to POKE to (held in DE)
2CBCRETC9RETurn 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- ↳ PRINUSGo evaluate the string expression at the location of the current BASIC program pointer in HL 2CC0-2CC2Go make sure the expression that was just evaluated was a string 2CC3-2CC4RST 08H ⇒ 3BSYNCHK ";"CF 3BSince 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).
2CC5EX DE,HLEBSwap DE and HL so that DE now holds the current BASIC program pointer 2CC6-2CC8LD HL,(4121H)LD HL,(FACLO)2A 21 41Load HL with the USING string's VARPTR 2CCB-2CCD- ↳ REUSSTLD A,(40DEH)LD A,(FLGINP)3A DE 40Load 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. 2CCEOR AB7Check to see if that flag indivates that we did, or did not, print out a value last time. 2CCF-2CD0If we did not print out a value last time, we have an error, so JUMP down to 2CDDH 2CD1POP DED1Restore the pointer to the "USING" string decription from the STACK into DE 2CD2EX DE,HLEBSwap 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- ↳ INIUSPUSH HLE5Save the pointer to the "USING" string descriptor (i.e., the USING string's VARPTR) in HL to the STACK 2CD4XOR AAFZero Register A and all the flags. 2CD5-2CD7LD (40DEH),ALD (FLGINP),A32 DE 40Clear the flag we are using to see if we printed the values or not. 2CD8CP DBATurn 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. 2CD9PUSH AFF5Save the flag indicating if the value list has ended or not to the STACK 2CDAPUSH DED5Save the pointer into the value list to the STACK 2CDBLD B,(HL)46Load Register B with the USING string's length 2CDCOR BB0Check to see if the USING string's length in Register B is equal to zero 2CDD-2CDF- ↳ FCERR3If the USING string is NULL then display a ?FC ERROR 2CE0INC HL23Bump the pointer to the USING string's data in HL by 1 2CE1LD C,(HL)4ELoad Register C with the LSB of the USING string's address at the location of the USING string's VARPTR in HL 2CE2INC HL23Bump the value of the USING string's VARPTR in HL 2CE3LD H,(HL)66Load Register H with the MSB of the USING string's address at the location of the USING string's VARPTR in HL 2CE4LD L,C69Load Register L with the LSB of the USING string's address in Register C
2CE7- ↳ BGSTRFLD E,B58Load Register E with the USING string's length 2CE8PUSH HLE5Save the pointer to the USING string pointer in HL to the top of the STACK 2CE9-2CEALD C,02H0E 02Since the \\ string field length is two plus number of enclosed spaces, add two 2CEB- ↳ LPSTRFLD A,(HL)7ELoad Register A with the character at the location of the USING string pointer in HL 2CECINC HL23Bump the value of the USING string data pointer in HL 2CED-2CEECP 25HCP CSTRNGFE 25Check 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-2CF1If it is a "%" then JUMP to 2E17H to evaluate a string and print 2CF2-2CF3CP 20HFE 20Check 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-2CF5If the character is not a field extender, then it isn't a string field, so JUMP down a few opcodes to 2CF9H 2CF6INC C0CIncrement the field width (tracked in in Register C) 2CF7-2CF8Decrement 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- ↳ NOSTRFPOP HLE1Restore the pointer to the "USING" string's data into Register Pair HL
2CFALD B,E43Load Register B with the USING string's length
2CFB-2CFCLD A,25HLD A,CSTRNG3E 25Restore 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- ↳ NEWUCHIf a +came before the character, make sure to print it
2D00-2D02Once that has been printed, now we print the character in Register A since we know it isn't part of a field
2D03- ↳ PRCCHRXOR AAFWe 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
2D04LD E,A5FZero Register E
2D05LD D,A57Zero Register D
2D06-2D08- ↳ PLSFINGo print a leading +if necessary (i.e., to allow for multiple plusses)
2D09LD D,A57Set 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.
2D0ALD A,(HL)7ELoad Register A with the next field description character in the USING string
2D0BINC HL23Bump the value of the USING string pointer in HL
2D0C-2D0DCP 21HCP "!"FE 21Check 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.
2D11-2D12CP 23HCP "#"FE 23Check 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.
2D15DEC B05Since every other possibility is actually a two character field, decrement the value of the string's length in Register B onem ore time
2D16-2D18If 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-2D1ACP 2BHCP "+"FE 2BCheck 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-2D1CLD A,08H3E 08Set 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
2D1FDEC HL2BDecrement the value of the USING string pointer so we can re-get the character.
2D20LD A,(HL)7ELoad Register A with the (current) character at the location of the USING string pointer in HL
2D21INC HL23Bump the value of the USING string pointer in HL
2D22-2D23CP 2EHCP "."FE 2ECheck 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-2D25Jump if the character in Register A is a .to scan with Register E holding the number of digits before the "." as 0
2D26-2D27CP 25HCP CSTRNGFE 25Check 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-2D29Jump to see if it is really a string field if the character in Register A is a %
2D2ACP (HL)BECheck 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-2D2CIf the NZ flag is set, then we can't have a $$or a **, so all remaining possibilities are exhausted, so JUMP to NEWUCH
2D2D-2D2ECP 24HCP "$"FE 24Check 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.
2D31-2D32CP 2AHCP "*"FE 2ACheck 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-2D34If 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
2D35LD A,B78Prepare 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-2D37CP 02HFE 02Check 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.
2D38INC HL23Bump the value of the USING string pointer in HL
2D39-2D3AJump to 2D3EH if the USING string's length in Register A isn't at least two
2D3BLD A,(HL)7ELoad Register A with the character at the location of the USING string pointer in HL
2D3C-2D3DCP 24HCP "$"FE 24Check 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- NOTSPCLD A,20H3E 20Set the *bit in Register A
2D40-2D41If we did not ultimately get a **$then JUMP (noting we do NOT set the dollar sign flag)
2D42DEC B05Decrement the value of the USING string's length to take the $into account
2D43INC E1CBump the field width tracker to account for the floating dollar sign
2D44-2D45CP 0AFHFE AFZ-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- ↳ DOLRNMXOR AAFThis is $processing for PRINT USING. Clear Register A.
2D46-2D47ADD A,10HC6 10Mask Register A to set the bit for a floating dollar sign flag.
2D48INC HL23Bump the value of the USING string pointer in HL to go past the special characters
2D49- ↳ SPCNUMINC E1CSince two characters specify the field size, start off with E=1
2D4AADD A,D82Combine the bits in Register D into the flag tracker
2D4BLD D,A57Preserve the modified flag tracker into Register D.
2D4C- ↳ NUMNUMINC E1CBump the number of characters to the left of the decimal point in Register E
2D4D-2D4ELD C,00H0E 00Set the number of digits to the right of the decimal point (tracked in Register C) to 0
2D4FDEC B05Check to see if there are any more characters by decrementing the value of the string's length in Register B
2D50-2D51If 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.
2D52LD A,(HL)7ELoad Register A with the next character at the location of the USING string pointer in HL
2D53INC HL23Bump the value of the USING string pointer in HL
2D54-2D55CP 2EHCP "."FE 2ECheck 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-2D57If yes, then need to use a special scan loop to scan after the decimal point, so JUMP to AFTDOT
2D58-2D59CP 23HCP "#"FE 23Check 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.
2D5C-2D5DCP 2CHCP ","FE 2CCheck 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-2D5FIf there is no comma, then JUMP to FINNUM because there are no more leading digits and we need to check for "^^^"
2D60LD A,D7AIf we are here, then a comma was requested. Turn on the COMMA bit
2D61-2D62OR 40HF6 40Mask the flag in Register A for ,
2D63LD D,A57Load Register D with the value of the flag in Register A
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- ↳ DOTNUMLD A,(HL)7ELoad Register A with the next character of the USING string
2D67-2D68CP 23HFE 23Check 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-2D6ALD A,2EHLD A,"."3E 2ELoad Register A with a decimal point
2D6B-2D6CIf it isn't a "." then JUMP AWAY to NEWUCH with A holding a "." so that a "." will get printed
2D6D-2D6ELD C,01H0E 01If 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
2D6FINC HL23Bump the value of the USING string pointer in HL
2D70- ↳ AFTDOTINC C0CBump the number of digits to the right of the decimal point (tracked in Register C)
2D71DEC B05Decrement the value of the USING STRING's length to test to see if there are more characters
2D72-2D73If the USING string length is now ZERO, JUMP to ENDNUS to stop scanning
2D74LD A,(HL)7ELoad Register A with the character at the location of the USING string pointer in HL
2D75INC HL23Bump the value of the USING string pointer in HL
2D76-2D77CP 23HFE 23Check 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-2D79If 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- ↳ FINNUMPUSH DED5Save the value of the flag (tracked in D) and the number of leading digits (tracked in E) to the STACK
2D7B-2D7DLoad DE with the return address in case this is not a scientific notation
2D7EPUSH DED5Save the value of the return address in DE to the STACK
2D7F
2D80LD D,H
LD E,L54Let DE = HL in case we need to rememer HL
2D81-2D82CP 5BHFE 5BCheck 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.
2D83RET NZC0Return if the character in Register A isn't an up arrow
2D84CP (HL)BECheck 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.
2D85RET NZC0Return 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)
2D86INC HL23Bump the value of the USING string pointer in HL
2D87CP (HL)BECheck 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.
2D88RET NZC0Return 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)
2D89INC HL23Bump the value of the USING string pointer in HL
2D8ACP (HL)BECheck 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.
2D8BRET NZC0Return 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)
2D8CINC HL23Bump the value of the USING string pointer in HL. If we are here we have a #.^^^^format
2D8DLD A,B78Now 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-2D8FSUB 04HD6 04Check to see if there are at least 4 characters left in the USING string
2D90RET CD8Return to 2D97 if there aren't at least four characters left in the USING string
2D91POP DED1If there are at least 4 characters left, then clean up the STACK by removing the NOTSCI return address
2D92POP DED1Get the flag and the count of the characters to the left of the decimal point from the STACK and put it in DE
2D93LD B,A47Load Register B with the new USING string's length in Register A
2D94INC D14Set the exponential notation flag (tracked in Register D)
2D95INC HL23Bump the value of the USING string pointer in HL
2D96JP Z,0D1EBHCA EB D1Z-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
2D98POP DED1(Ignored if passing through) Restore the flags into Register D and the number of leading digits into Register E
2D99- ↳ ENDNUSLD A,D7AWe 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
2D9ADEC HL2BDecrement the value of the USING string pointer in HL
2D9BINC E1CBump the number of characters to the left of the decimal point in Register E to take into account the leading plus
2D9C-2D9DAND 08HE6 08Mask Register A to NOT check for a trailing sign
2D9E-2D9FIf that AND leaves us with a NZ, then we are all done with the field, so JUMP to ENDNUM
2DA0DEC E1DOtherwise, 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
2DA1LD A,B78Check to see if there are more characters by first loading Register A with the USING string's length from Register B
2DA2OR AB7Check to see if this is the end of the USING string
2DA3-2DA4If we are out of characters, then we are all done, so JUMP to ENDNUM
2DA5LD A,(HL)7EIf there ARE more characters, then fill Register A with the character at the location of the USING string pointer in HL
2DA6-2DA7SUB 2DHSUB "-"D6 2DCheck to see if the character in Register A is a -(i.e., a trailing minus)
2DAA-2DABCP 0FEHCP "+" - "-"FE FECheck 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.
2DAE-2DAFLD A,08H3E 08If we are here then we did have a trailing "+" so first set the flag for a POSITIVE "+"
2DB0-2DB1- ↳ SGNTRLADD A,04HC6 04Then set the flag for a trailing sign
2DB2ADD A,D82Combine the value of the flag in Register D with the value of the flag in Register A
2DB3LD D,A57Load Register D with the current flags
2DB4DEC B05Decrement 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- ↳ ENDNUMPOP HLE1Get the value of the current BASIC program pointer from the STACK and put it in HL
2DB6POP AFF1Load Register A with the flag that tells us whether there are more values to process in the value list.
2DB7-2DB8If there are no more values in the value list to process, then JUMP to FLDFIN because we are done with the PRINT
2DB9PUSH BCC5Save 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
2DBAPUSH DED5Save the flags (held in D) and the number of leading digits (held in E) to the STACK
2DBB-2DBDRead 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
2DBEPOP DED1Restore the flags (held in D) and the number of leading digits (held in E) from the STACK
2DBFPOP BCC1Restore 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
2DC0PUSH BCC5Save 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
2DC1PUSH HLE5Save the value of the current BASIC program pointer in HL to the STACK
2DC2LD B,E43Set Register B to hold the number of leading digits (i.e., the number of characters to the left of the decimal point)
2DC3LD A,B78We 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
2DC4ADD A,C81Then 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-2DC6CP 19HFE 19Check 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-2DC9Display a FC ERROR message if the total number of digits is greater than 24
2DCALD A,D7ALoad Register A with the flags (held in Register D)
2DCB-2DCCOR 80HF6 80Turn on the "USING" bit in the flags
2DCD-2DCFPrepare 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-2DD2Call 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- ↳ FNSTRFPOP HLE1Top of a loop. Get the value of the current BASIC program pointer from the STACK and put it in HL
2DD4DEC HL2BDecrement the value of the current BASIC program pointer in HL so we can test to see what the terminator was
2DD5RST 10HCHRGETD7We 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.
2DD6SCF37Set the Carry flag to indicate that a CRLF is desired
2DD7-2DD8If 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-2DDBLD (40DEH),ALD (FLGINP),A32 DE 40Set the flag that the value HAS been printed!
2DDC-2DDDCP 3BHCP ";"FE 3BCheck 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.
2DE0-2DE1CP 2CHCP ","FE 2CCheck 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-2DE4If 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- ↳ SEMUSNRST 10HCHRGETD7We 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- ↳ CRDNUSPOP BCC1Restore the number of characters remaining to be procesed in the USING string into Register B
2DE7EX DE,HLEBSwap DE and HL so that DE will point to the location of the current BASIC program pointer. We don't care about HL.
2DE8POP HLE1Restore the position in the USING string from the STACK and put it in HL
2DE9PUSH HLE5Save the position in the USING string (held in HL) to the STACK
2DEAPUSH AFF5Save the flag that indicates whether or not the value list has terminated to the STACK
2DEBPUSH DED5Save 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.
2DECLD A,(HL)7ELoad Register A with the USING string's length at the location of the USING string's VARPTR in HL
2DEDSUB B90Subtract the number of characers which were already scanned
2DEEINC HL23Bump the pointer to the "USING" strings string data
2DEFLD C,(HL)4ELoad Register C with the LSB of the USING string's address at the location of the USING string's VARPTR in HL
2DF0INC HL23Bump the value of the USING string's VARPTR in HL
2DF1LD H,(HL)66Load Register H with the MSB of the USING string's address at the location of the USING string's VARPTR in HL
2DF2LD L,C69Load Register L with the LSB of the USING string's address in Register C
2DF3-2DF4LD D,00H16 00Zero Register D so that Register Pair DE can be a 16 bit offset of whatever is held in A.
2DF5LD E,A5FLoad Register E with the USING string's offset in Register A
2DF6ADD HL,DE19Add 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
2DF7LD A,B78Load Register A with the number of characters left to scan
2DF8OR AB7Check to see if this is the end of the USING string
2DF9-2DFBIf there are still more string characters to scan, JUMP to PRCCHR to do so
2DFE-2E00 - Part of the PRINT USING Routine- "REUSIN"
We will wind up here when we are done processing a numeric field
2E01-2E03Go send the FINAL character (held in Register A) to the current output device
2E04- ↳ FINUSIPOP HLE1Get the value of the current BASIC program pointer from the STACK and put it in HL
2E05POP AFF1Restore the flag which indicates whether or not the value list has ended into Register A
2E06-2E08If the value list has NOT ended, JUMP back to REUSST to reuse the USING string
2E09-2E0B- ↳ FLDFINIf 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
2E0CEX (SP),HLE3Swap (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
2E10POP HLE1Get the value of the current BASIC program pointer from the STACK and put it in HL
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- ↳ SMSTRFLD C,01H0E 01Set the field width to 1
2E16-2E17LD A,0F1H3E F1Z-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- ↳ ISSTRFPOP 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)
2E18DEC B05Decrement the USING string character count (tracked in Register B)
2E1CPOP HLE1Get the value of the current BASIC program pointer from the STACK and put it in HL
2E1DPOP AFF1Get the flag which indicates whether there are more values in the value list into Register A
2E1E-2E1FIf there are no more values in the value list, then we are done so JUMP back to 2E09H
2E20PUSH BCC5Save the number of characters still to be scanned from the USING string (tracked in B) to the STACK
2E21-2E23Read 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
2E27POP BCC1Restore the field width (a/k/a the number of characters to be printed) into Register C
2E28PUSH BCC5Save the USING string's length and the number of characters to be printed in BC to the STACK
2E29PUSH HLE5Save the value of the current BASIC program pointer in HL to the STACK
2E2A-2E2CLD HL,(4121H)LD HL,(FACLO)2A 21 41Load HL with the string's VARPTR in ACCumulator
2E2DLD B,C41Load Register B with field width (a/k/a the number of characters to be printed)
2E2E-2E2FLD C,00H0E 00Zero Register C so that we can use the LEFT$ routine
2E30PUSH BCC5Save the length of the string to be printed in Register B to the STACK (as we will need that for space padding)
2E31-2E33Truncate the string to B characters via a call to the LEFT$ routine
2E37-2E39LD HL,(4121H)LD HL,(FACLO)2A 21 41Load HL with the string's VARPTR in ACCumulator so we can see if we need to pad the string
2E3APOP AFF1Get the field width (a/k/a the length of the string to be printed) from the STACK and put it in Register A
2E3BSUB (HL)96Determine 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
2E3CLD B,A47Save the amount of padding needed into Register B
2E3D-2E3ELD A,20H3E 20Load Register A with a SPACE
2E3FINC B04Bump 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- ↳ UPRTSPDEC B05Top of a loop. Decrement the number of spaces in Register B
2E41-2E43If all of the spaces have been printed, Jump back to 2DD3H to see if the value list ended and to resume scanning
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- ↳ PLSPRTPUSH AFF5Save the current character (held in Register A) to the STACK
2E4ALD A,D7AWe need to test the PLUS BIT in D, so first load Register A with the value in Register D
2E4BOR AB7Check 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-2E4DLD A,2BHLD A,"+"3E 2BPrepare to print the +by loading Register A with a +
2E4E-2E50If the bit was set (i.e., A was non-zero), then send a +to the current output device
2E51POP AFF1Get the current character from the STACK and put it in Register A
2E52RETC9RETurn 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- ↳ ERREDTLD (409AH),ALD (ERRFLG),A32 9A 40Reset the EDIT flag.
Note: 409AH holds the ERROR/RESUME flag
2E56-2E58LD HL,(40EAH)LD HL,(ERRLIN)2A EA 40Load HL with the line number to be edited.
Note: 40EAH-40EBH holds the line number with error
2E59OR HB4OR Register A with the MSB of the error line number in Register H
2E5AAND LA5Combine 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
2E5BINC A3CBump 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
2E5CEX DE,HLEBSwap DE and HL so that DE now holds the line number to edit.
2E5DRET ZC8If there was no line number, return if Level II BASIC
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- ↳ EDITGet the first line number by calling 1E4F - returns in in DE
2E63RET NZC0If the zero flag got set, there was no line number, so return
2E64- ↳ EREDITPOP HLE1Clean up the STACK (i.e., discard the NEWSTT return address)
2E65- ↳ EEDITSEX DE,HLEBLoad HL with the line number to be edited in DE
2E66-2E68LD (40ECH),HLLD (DOT),HL22 EC 40Save 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
2E69EX DE,HLEBLoad HL with the line number to be edited
2E6A-2E6CFind 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
2E70
2E71LD H,B
LD L,C60At 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
2E73INC HL
INC HL23Bump the value of the memory pointer in HL twice to now point to the first byte of the line.
2E74LD C,(HL)4ELoad Register C with first byte of the line number being edited
2E75INC HL23Bump the value of the memory pointer in HL to point to the second byte of the line being edited
2E76LD B,(HL)46Load Register B with second byte of the line number being edited
2E77INC HL23Bump the value of the memory pointer in HL to now point to the first byte of the actual line
2E78PUSH BCC5Save the line number to the STACK
2E79-2E7BGOSUB 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- ↳ LLEDPOP HLE1Get the value of the line number from the STACK and put it in HL
2E7D- ↳ INLEDPUSH HLE5Save the value of the line number in HL to the STACK
2E7E-2E80Convert 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-2E82LD A,20H3E 20Load Register A with a space
2E86-2E88LD HL,(40A7H)LD HL,(BUFPNT)2A A7 40Load 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-2E8ALD A,0EH3E 0ELoad Register A with the "turn on the cursor" character
2E8EPUSH HLE5Save the value of the input buffer pointer (in HL) to the STACK
2E8F-2E90LD C,FFH0E FFLoad 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- ↳ LENLPINC C0CBump the number of characters examined so far in Register C
2E92LD A,(HL)7ELoad Register A with the character at the location of the input buffer pointer in HL
2E93OR AB7Check to see if the character in Register A is an end of the BASIC line character
2E94INC HL23Bump the value of the input buffer pointer in HL
2E95-2E96Loop back to 2E91H until the end of the BASIC line has been found
2E97POP HLE1At this point, C will be the maximum number of characters in the line at issue. Put the start of the expanded buffer into HL
2E98LD B,A47Set the current position in the BASIC line being edited (tracked by Register B) to ZERO
2E99-2E9A- ↳ DISPEDLD D,00H16 00Assume the repetition count for the upcoming command (tracked by Register D) is zero
2E9B-2E9D- ↳ DISPIGo scan the keyboard to wait for the user command
2D9E-2E9F- ↳ DISPSUB 30H20 15We need to test to see if the character was alphabetic or alphanumeric so we subtract 30H from it
2EA2-2EA3CP 0AHFE 0ACheck 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.
2EA6LD E,A5FLoad Register E with the binary value of the character in Register A
2EA7LD A,D7APut the repetition value into Register A
2EA8RLCA07Multiply the value in Register A by two (so now A has multiplied by 2)
2EA9RLCA07Multiply the value in Register A by two (so now A has multiplied by 4)
2EAAADD A,D82Add the value in Register D to the value in Register A (so now A has multiplied by 5)
2EABRLCA07Multiply the value in Register A by two (so now A has multiplied by 10). Now the "ones place" is empty.
2EACADD A,E83Add the value in Register E to the value in Register A in the "ones place"
2EADLD D,A57Load Register D with the value in Register A
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- ↳ NOTDGIPUSH HLE5Save the value of the input buffer pointer in HL to the STACK
2EB4EX (SP),HLE3Exchange the value of the return address in HL with the value of the input buffer pointer to the STACK
2EB5DEC D15We 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
2EB6INC D14... and then incrementing the numeric value in Register D to set the flags
2EB7-2EB9If we had a received a repetition count already, then JUMP to 2EBBH
2EBAINC D14Otherwise, set the repetition count (held in Register D) to be one
2EBB-2EBC- ↳ NTZERDCP 0D8HFE D8Check 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-2EBFIf the character in Register A is a BACKSPACEcharacter, JUMP to DELED
2EC0-2EC1CP 0DDHFE DDCheck 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-2EC4If the character in Register A is a CARRIAGE RETURN, JUMP to CRED
2EC5-2EC6CP 0F0HFE F0Check 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.
That's it for non-alphabetic instructions, so we need to need to convert a lower case command to upper case
2EC9-2ECACP 31HFE 31Check 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.
2ECD-2ECESUB 20HD6 20Convert the lowercase character in Register A to uppercase
2ECF-2ED0- ↳ NOTLW4CP 21HFE 21Check 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.
2ED4-2ED5CP 1CHFE 1CCheck 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.
2ED9-2EDACP 23HFE 23Check 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.
2EDD-2EDECP 19HFE 19Check 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.
2EE2-2EE3CP 14HFE 14Check 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.
2EE7-2EE8CP 13HFE 13Check 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.
2EEC-2EEDCP 15HFE 15Check 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.
2EF1-2EF2CP 28HFE 28Check 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.
2EF6-2EF7CP 1BHFE 1BCheck 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.
2EFA-2EFBCP 18HFE 18Check 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-2EFEJump if the character in Register A is an H(i.e., HACK off the rest of the line and then enter INSERT mode)
2EFF-2F00CP 11HFE 11Check 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.
2F01RET NZC0Return if the character in Register A isn't an A
2F02 - EDITCommand - Cancel and Restore Logic.
2F02POP BCC1Clean up the STACK (i.e., remove the DISPI return address)
2F03POP DED1Get the BASIC line number from the STACK and put it in DE
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- ↳ SPEDLD A,(HL)7ELoad Register A with the character at the location of the input buffer pointer in HL
2F0BOR AB7Check to see if the character in Register A is an end of the BASIC line character
2F0CRET ZC8Return if the character in Register A is an end of the BASIC line character
2F0DINC B04Bump the character position in Register B
2F11INC HL23Bump the value of the pointer in HL
2F12DEC D15Decrement the number of times to perform the operation in Register D
2F15RETC9RETurn to CALLer
2F16 - EDITCommand - KILL Logic- "KED".
2F16- ↳ KEDPUSH HLE5Save the current character position in the buffer in HL to the STACK
2F17-2F19Load HL with the return address of 2F5FH (which will print the final !
2F1AEX (SP),HLE3Exchange the value of the return address in HL with the value of the input buffer pointer to the STACK
2F1BSCF37Set the KILL/SEARCH flag for KILL since CARRY flag signals KILL
2F1C- ↳ SEDPUSH AFF5Save the KILL/SEARCH flag to the STACK
2F1D-2F1FGo scan the keyboard for the character the user wants to SEARCH for
2F20LD E,A5FSave the character the user wants to SEARCH for into Register E
2F21POP AFF1Get the KILL/SEARCH flag from the STACK
2F22PUSH AFF5Save the KILL/SEARCH flag to the STACK
2F23-2F25If KILL (because the CARRY flag was set) then GOSUB to 2F5FH to print a !
2F26- ↳ SRCALPLD A,(HL)7ELoad Register A with the character at the location of the input buffer pointer in HL
2F27OR AB7Check to see if the character in Register A is an end of the BASIC line character
2F28-2F2AJump down to 2F3EH if the character in Register A is an end of the BASIC line character
2F2EPOP AFF1Get the KILL/SEARCH flag from the STACK
2F2FPUSH AFF5Save the KILL/SEARCH flag to the STACK
2F30-2F32If 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-2F34Jump to 2F37H if KILL. Note, we do not move the HL pointer in this case because DELCHR already moved it.
2F35INC HL23If we are here, it must be SEARCH! So bump to the next character
2F36INC B04Bump the value of the character position in Register B
2F37- ↳ NOTSRCLD A,(HL)7ERegardless of whether we are SEARCH or KILL, load Register A with the character at the location of the current input buffer pointer in HL
2F38CP EBBCheck 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.
2F3BDEC D15Decrement the number of times to perform the operation in Register D (as initially specified by the user by entering a number before the command)
2F3E- ↳ POPARTPOP AFF1Get the KILL/SEARCH flag from the STACK
2F3FRETC9RETurn to CALLer
2F40 - EDITCommand - LIST Logic- "LED".
2F40-2F42- ↳ LEDSince 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
2F46POP BCC1Clear off the RETURN address to DISPED
2F47-2F49Jump to 2E7CH (to display the current line number and await the next EDIT command)
2F4A - EDITCommand - DELETE Logic- "DED"
2F4A- ↳ DEDLD A,(HL)7ELoad Register A with the character at the location of the input buffer pointer in HL
2F4BOR AB7Check to see if the character in Register A is an end of the BASIC line character
2F5CRET Z15Return if the character in Register A is an end of the BASIC line character
2F4D-2F4ELD A,21HLD A,"!"3E 21Load Register A with an !
2F52- ↳ DELLPLD A,(HL)7ELoad Register A with the character at the location of the input buffer pointer in HL
2F53OR AB7Check to see if the character in Register A is an end of the BASIC line character
2F54-2F5BJump to 2F5FH if the character in Register A is an end of the BASIC line character
2F5CDEC D15Decrement the number of times to perform the operation in Register D (as initially specified by the user by entering a number before the command)
2F5F-2F60- ↳ TYPSLHLD A,21HLD A,"!"3E 21Load Register A with an !
2F64RETC9RETurn to CALLer
2F65 - EDITCommand - CHANGE Logic- "CED".
2F65- ↳ CEDLD A,(HL)7ELoad Register A with the character at the location of the input buffer pointer in HL
2F66OR AB7Check to see if the character in Register A is an end of the BASIC line character
2F67RET ZC8Return if the character in Register A is an end of the BASIC line character
2F68-2F6AGo get the character to put in the input buffer from the keyboard
2F6BLD (HL),A77Save the character in Register A at the memory location of the input buffer pointer in HL
2F6FINC HL23Bump the value of the input buffer pointer in HL
2F70INC B04Bump the character position in Register B
2F71DEC D15Decrement the number of times to perform the operation in Register D (as initially specified by the user by entering a number before the command)
2F74RETC9RETurn to CALLer
2F75 - EDITCommand - HACK/INSERT Logic- "HED"
2F75-2F76- ↳ HEDLD (HL),00H36 00Set the line end to be the current position.
2F77LD C,B48Load Register C with the character position in Register B which will now be the line length
2F78-2F79- ↳ XEDLD D,0FFH16 FFPrepare for the next CALL to find the end of the line by loading Register D with the number of times to perform the operation
2F7D-2F7F- ↳ IEDGo get the character to be inserted from the keyboard
2F80OR AB7Check to see if a key was pressed
2F84-2F85CP 08HFE 08Check 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-2F87Jump to 2F92H if the character in Register A is a backspace character
2F88-2F89CP 0DHFE 0DCheck 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-2F8CJump to 2FE0H if the character in Register A is a carriage return
2F8D-2F8ECP 1BHFE 1BCheck 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.
2F8FRET ZC8Return if the character in Register A is shift up arrow
2F92 - EDITCommand - BACKSPACE CURSOR Logic- "TYPARW".
2F92-2F93- ↳ TYPARWLD A,08H3E 08Load Register A with a backspace the cursor character
2F94- ↳ TYPAR1DEC B05Decrement the character position in Register B
2F95INC B04Bump the character position in Register B
2F96-2F97If this is the first character of the BASIC line Jump forward to 2FB7H
2F9BDEC HL2BDecrement the value of the input buffer pointer in HL
2F9CDEC B05Decrement the character position in Register B
2FA0PUSH DED5Save 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- ↳ DELCHRPUSH HLE5Save the value of the input buffer pointer in HL to the STACK
2FA2DEC C0DDecrement the character position in Register C
2FA3- ↳ CMPRSSLD A,(HL)7ELoad Register A with the character at the location of the input buffer pointer in HL
2FA4OR AB7Check to see if the character in Register A is an end of the BASIC line character
2FA5SCF37Set the Carry flag to signal that DELCHR was called
2FA6-2FA8If the character in Register A is an end of the BASIC line character then we are done compressing so Jump to 0890H
2FA9INC HL23Bump the value of the input buffer pointer in HL
2FAALD A,(HL)7ELoad Register A with the character at the location of the input buffer pointer in HL
2FABDEC HL2BDecrement the value of the input buffer pointer in HL
2FACLD (HL),A77Save the character in Register A at the location of the current input buffer pointer in HL
2FADINC HL23Bump the value of the input buffer pointer in HL
2FB0 - EDITCommand - ADD A CHARACTER Logic- "NTARRW".
2FB0- ↳ NTARRWPUSH AFF5Save the character to be inserted in Register A to the STACK
2FB1LD A,C79Load Register A with the number of characters in the input buffer (i.e., the length of the line) in Register C
2FB2-2FB3CP FFHCP BUFLENFE FFWe 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-2FB5Jump forward to 2FB9H if the maximum BASIC line length hasn't been reached
2FB6POP AFF1Get the character to be inserted from the STACK and put it in Register A
2FB9- ↳ OKINSSUB B90Subtract 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
2FBAINC C0CBump the number of characters in the input buffer in Register C
2FBBINC B04Bump the character position in Register B
2FBCPUSH BCC5Save the character position and the number of characters in the input buffer in BC to the STACK
2FBDEX DE,HLEBLoad DE with the input buffer pointer in HL
2FBELD L,A6FLoad Register L with the number of bytes to move
2FBF-2FC0LD H,00H26 00Zero Register H so that the number of bytes to move can be done in a 16 bit Register Pair.
2FC1ADD HL,DE19Add the value of the input buffer pointer in DE to the character count in HL
2FC2LD B,H44Load Register B with the MSB of the end of the BASIC line pointer in Register H
2FC3LD C,L4DLoad Register C with the LSB of the end of the BASIC line pointer in Register L
2FC4INC HL23Bump the value of the end of the BASIC line pointer in HL
2FC8POP BCC1Get the character position and the number of characters in the input buffer from the STACK and put it in BC
2FC9POP AFF1Get the character to be inserted from the STACK and put it in Register A
2FCALD (HL),A77Save the character in Register A at the location of the current input buffer pointer in HL
2FCEINC HL23Bump the value of the input buffer pointer in HL
2FD2 - EDITCommand - BACKSPACE Logic- "DELED".
2FD2- ↳ DELEDLD A,B78Top 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
2FD3OR AB7Check to see if this is the start of the BASIC line
2FD4RET ZC8Return if this is the start of the BASIC line
2FD5DEC B05Decrement the character position in Register B
2FD6DEC HL2BDecrement the value of the buffer pointer in HL
2FD7-2FD8LD A,08H3E 08Load Register A with a backspace the cursor character
2FDCDEC D15Decrement the number of times to perform the operation in Register D
2FDFRETC9RETurn to CALLer
2FE0-2FE2- ↳ CREDSince 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
2FE6POP BCC1Clean up the STACK (to remove the DISPED return address)
2FE7POP DED1Get the BASIC line number (in binary) from the STACK and put it in DE
2FE8LD A,D7ALoad Register A with the MSB of the BASIC line number in Register D
2FE9AND EA3Combine the LSB of the BASIC line number in Register E with the MSB of the BASIC line number in Register A
2FEAINC A3CBump the combined BASIC line number in Register A
2FEB-2FED- ↳ EDITRTLD HL,(40A7H)LD HL,(BUFPNT)2A A7 40Load HL with the starting address of the input buffer.
Note: 40A7H-40A8H holds the input Buffer pointer
2FEEDEC HL2BDecrement the value of the input buffer pointer in HL
2FEFRET ZC8Return if this is the Level II BASIC command mode
2FF0SCF37Set the Carry flag to to fool the INSERT code; this flags the line number has having been seen
2FF1INC HL23Bump the value of the input buffer pointer in HL
2FF2PUSH AFF5Save the command mode flag in AF to the STACK
2FF6 - EDITCommand - QUIT Logic- "QED".
3000 - Jump Table.
3012 - Model 4 ROM Gen 1
3012 - Model 4 ROM Gen 2
*3024 - Model 4 ROM Gen 1
*3027RET
NOP
NOPI/O Re-Router was removed from the Model 4 ROM.
*3024 - Model 4 ROM Gen 2
*302D - Model 4 ROM Gen 1
*302D - Model 4 ROM Gen 2
*302DJP 375CHC3 5C 37Jump to 375CH for a routine which parses whether the current instruction on a the current line is in quotes.
3036Jump to 35A0H to put the TIME 10 characters from the upper right hand corner of the screen.
3039IN A,(0E4H)Poll Port E4H into A.
NOTE: Port E4H is the Non-Maskable Interrupt Latch.
303BBIT 5,ATest Bit 5 of Register A. Bit 5 of the NMI on an Input test is the RESET STATUS, with 0=False, and 1=True
*3042 - Model 4 ROM Gen 1 - Prompt the User to set the cassette baud rate.
*3042 - Model 4 ROM Gen 2 - Prompt the User to set the cassette baud rate.
*3045-3064 - Model 4 ROM Gen 1
*3045-305F@abcdefghijklmnopqrstuvwxyzKeyboard Rows 0-3, Unshifted, No Caps Lock
*3060NOPComputer version number, which is always 1 for a Model III and 0 for a Model 4
*3061NOP
*3062NOP
*3063NOP
*3064NOP
*3065-307D30 31 32 33 34 35 36 37 38 39 3A 3B 2C 2D 2E 2F 0D 1F 01 5B 0A 08 09 20 00Keyboard Rows 4-6, Unshifted, No Caps Lock
*307ENOP
*307FNOP
*3080NOP
*3081NOP
*3082NOP
*3083NOP
*3084NOP
*3085-309F60 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 5AKeyboard Rows 0-3, shifted, No Caps Lock
*30A0NOP
*30A1NOP
*30A2NOP
*30A3NOP
*30A4NOP
*30A5NOP
*30A6-30BC21 22 23 24 25 26 27 28 29 2A 2B 3C 3D 3E 3F 0D 1F 01 1B 1A 18 19 20Keyboard Rows 4-6, shifted, No Caps Lock
*30BDNOP
*30BENOP
*30BFNOP
*30C0NOP
*30C1NOP
*30C2NOP
*30C3NOP
*30C4NOP
*30C5-30DF40 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 5AKeyboard Rows 0-3, UNshifted, Caps Lock.
*30E0NOP
*30E1NOP
*30E2NOP
*30E3NOP
*30E4NOP
*30E5-30FC30 31 32 33 34 35 36 37 38 39 3A 3B 2C 2D 2E 2F 0D 1F 01 5B 0A 08 09 20Keyboard Rows 4-6, UNshifted, Caps Lock
*30FDNOP
*30FENOP
*30FFNOP
*3100NOP
*3101NOP
*3102NOP
*3103NOP
*3104NOP
*3105H - Model 4 Gen 1 jump to NON-Disk BASIC
GOSUB to the $SETCAS routine which prompts the user to set the cassette baud rate (310BH - 313AH)
*310BH - Model 4 Gen 1 Set the TAPE BAUD RATE ($SETCAS).
*310CGOSUB to 312DH which loads A with a carrage return, and jumps to 0033H to display it.
*3115GOSUB to 0049H.
NOTE: 0049H is the $KBWAIT routine which scans the keyboard and returns with the key pressed, if any, in register A.
*3118CP 0DHCompare A and 0D (a CARRIAGE RETURN).
*311CPUSH AFSave AF to the STACK.
NOTE: A currently holds the character pressed in response to the "CASS?" message.
*311DGOSUB to 0033H.
NOTE: 0033H is the character print routine, to put the character held in Register A at the current cursor position.
*3120POP AFRestore AF from the STACK.
NOTE: A will then hold the character pressed in response to the "CASS?" message.
*3121CP 48HCompare A with 48H (ASCII: H).
*3125CP 4CHCompare A with a 4CH (ASCII: L).
3129H - Model 4 Gen 1 Set the Selected Cassette Baud Rate as LOW SPEED
*3129XOR ASet 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
LD (4211H),ASave 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
*312FDisplay 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
Finish the cassette setup by calling a portion of the keyboard scan routine which CALLS the screen print routine at 34FDH
*313EXOR AClear Register A and All Flags
*313FRETReturn a null character.
*3140NOP
*3141NOP
*3142NOP
*3143NOP
*3144NOP
*3145 - Model 4 Gen 1 Printer Character Table Codes 32-127.
*3145-31A420 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.
*3045JP 378DHC3 8D 37Jump 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
*3048JP 377AHC3 7A 37Jump to 377AH to check to see if we are on a new printable page and set the pointers accordingly. CALLED from 0431 and 0445
*3045 - Model 4 ROM Gen 2 Keyboard Rows 0-3, Unshifted, No Caps Lock.
*304E-3068@abcdefghijklmnopqrstuvwxyz
*3069NOP
*306ANOP
*306BNOP
*306CNOP
*306DNOP
*306E-308530 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.
*3086IN A,(0EAH)DB EAPoll the RS-232 UART Control Register and Status Register (at Port EAH) and put the results into Register A
*3087OR AB7Set the FLAGS based on the RS-232 UART Control Register and Status Register
*308C-308DJR 30C6H18 38If 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.
**3086LD A,(3810H)3A 10 38Fetch the Keyboard Matrix 0-7 into Register A to check for certain keys.
**3089AND 10HE6 10MASK 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.
**306CNOP
**306DNOP
**306DNOP
*308E-30A8 - Model 4 ROM Gen 2 - Continuing with the Keyboard Table
*308E-30A860 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
*30A9NOP
*30AANOP
*30ABNOP
*30ACNOP
*30ADNOP
*30AENOP
*30AF-30C521 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-30C7IN A,(0EBH)DB EBPoll the RS-232 Register at Port EBH and put the results into Register A.
*30C8RETC9
**30C6-30C8 - Model 4 ROM Student Network Edition - A little code snippet
*30C6NOP
*30C7NOP
*30C8NOP
*3089 - Model 4 ROM Gen 2 - Continuing with the Keyboard Table
*30C9NOP
*30CA-30CDPUSH BC
POP BC
NOP
RETStandard code for a short delay
*30CE@ABCDEFGHIJKLMNOPQRSTUVWXYZ
*30E9NOP
*30EANOP
*30EBNOP
*30ECNOP
*30EDNOP
*30EE30 31 32 33 34 35 36 37 38 39 3A 3B 2C 2D 2E 2F 0D 1F 01 5B 0A 08 09 20Keyboard Rows 4-6, UNshifted, Caps Lock
*3106LD A,(3880H)3A 80 38Load A with the value held at 3880H (which are the SHIFT KEYS)
*3109LD HL,414FH21 F4 41Point HL to 414FH
*310CAND 7CHE6 7CMASK the value held in the SHIFT KEY RAM location against 0111 1100 to keep only bits 2, 3, 4, 5, and 6 live
*310EOR AB7OR A against itself to reset the flags
*310FJR Z,3142H28 31If the Z FLAG is set, JUMP to 3142H to continue checking fro special keys
*3111LD E,A5FPut the masked A into E.
*3112XOR (HL)AEToggle against the old image.
*3113LD (HL),E73Save the new image into (HL).
*3114AND EA3Mask Register E against Register A.
*3115JR Z,3157H28 40If ZERO then the LEFT SHIFT PRESSED was JUMP to 3157H to restart parsing the keyboard
*3117LD BC,05C4H01 C4 05Load BC with 05C4H to set up a 1/50 second delay for de-bounce.
*311ACALL 0060HCD 60 00GOSUB 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.
*311DLD A,(3880H)3A 80 38Load A with the value held at 3880H (which are the SHIFT KEYS)
*3120AND 7CHE6 7CMASK the value held in the SHIFT KEY RAM location against 0111 1100 to keep only bits 2, 3, 4, 5, and 6 live
*3122CP EBBCompare A with E.
*3126RLA17Rotate 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.
*3127RLA17Rotate 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.
*3128JR NC,312EH30 04We 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
*312ALD A,(41F3H)3A F3 41Load Register A with the value of the character to be returned when the F3 key is pressed
*312DRETC9RETurn to Caller
*312E - Model 4 Gen 2 - KEYBOARD Routine - Check and Process the F2 Key or Jump Away.
*312ERLA17Rotate 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.
*312FJR NC,3135H30 04If the NC FLAG is set, it isn't the F2 key, so JUMP to 3135H to test the F1 key
*3131LD A,(41ECH)3A EC 41Load Register A with the value of the character to be returned when the F2 key is pressed
*3134RETC9RETurn to Caller
*3135 - Model 4 Gen 2 - KEYBOARD Routine - Check and Process the F1 Key or Jump Away.
*3135RLA17Rotate 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.
*3136JR NC,313CH30 04If the NC FLAG is set, it isn't the F1 key, so JUMP to 313CH to keep checking special keys
*3138LD A,(41EBH)3A EB 41Load Register A with the value of the character to be returned when the F1 key is pressed
*313BRETC9RETurn to Caller
*33C4 - Model 4 Gen 2 - KEYBOARD Routine - Part of the Keyboard Scan Routine. Keep Checking Special Keys.
*313CRLA17Rotate 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.
*33CA - Model 4 Gen 1 - KEYBOARD Routine - Part of the Keyboard Scan Routine.
*3142LD (HL),A77Save the character held in Register A into the memory location pointed to by Register Pair HL
*3143LD A,FFH3E FFLoad A with FF to set up for a FLAG = 0FFH = NO CONTROL.
*3145LD HL,3840H21 40 38Load HL with 3840H to start a check for a DOWN ARROW.
*3148BIT 4,(HL)CB 66Test BIT 4 of (HL) to check for a DOWN ARROW.
*314AJR Z,3154H28 08JUMP to 3154H if the a DOWN ARROW was NOT pressed.
*314CSLA LCB 25Next we need to check for a a LEFT SHIFT so shift L left.
*314EBIT 0,(HL)CB 46Test BIT 0 of (HL) to check for a LEFT SHIFT.
*3152LD A,1FH3E 1FLoad A with 1F to set up for FLAG = CONTROL KEY.
*3154LD (4224H),A32 24 42Save the CONTROL FLAG into (4224H).
NOTE: 4224H Holds the CONTROL KEY flag.
*315ARET NCD0If that routine exited with NC FLAG set, RETurn
*315ECP 1AHFE 1ACheck A against 1AH to see if we have a SHIFT+DOWN ARROW.
*3163OR AB7Set the flags based on Register A
*3167LD HL,4224H21 24 42Set Register Pair HL to 4224H, which is the CONTROL KEY flag.
*316ABIT 7,(HL)CB 7ETest Bit 7 of the CONTROL KEY flag in RAM.
*316ECP 2AHFE 2ACheck A against a * key.
*3170JP Z,37C7HCA C7 37If 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.
*3173AND (HL)A6Prepare to check for a BREAK key by masking A against the memory contents of HL ...
*3174CP 01HFE 01... and COMPARING it to 01H.
*3176RET NZC0If the result of the compare is NOT zero, then RETURN.
*3177RST 28HEFIf we are here, then a BREAK key was hit, so call RST 28H to handle the BREAK key.
*3178RETC9RETURN to Caller
*3179 - Model 4 Gen 2 - Continuing Initialization Routine by setting up the RS-232.
*3179XOR AAFSet Register A to 0
*317AOUT (E8H),AD3 E8Output A to port E8H.
NOTE: Port E8H is the RS-232 Status Register & Master Reset. Outputting ANYTHING to Port E8H resets the RS-232.
*317CLD A,EEH3E EESet Register A to EEH for outputting to the RS-232 Baud Rate Select and Sense Switches: (Port E9H)
*317EOUT (E9H),AD3 E9Initialize 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
*3180LD A,6DH3E 6DSet Register A to 6DH for outputting to the RS-232 UART Control Register and Status Registe (Port EAH)
*3182OUT (EAH),AD3 EASent 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)
*3184IN A,(E8H)DB E8Poll the RS-232 Status Register and Master Reset of Port E8H and put the value into Register A.
*3186BIT 6,ACB 77Test Bit 6 of the RS-232 Status Register to check Data Set Ready (Pin 6 of the DB-25).
*3188JR Z,3184H28 FAIf the DATA SET READY is ZERO then LOP back 2 instructions to 3184 to poll again
*318ALD A,6CH3E 6CSet Register A to 6CH (0110 1100) for outputting to the RS-232 UART Control Register and Status Registe (Port EAH)
*318COUT (EAH),AD3 EASend 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
*318EIN A,(E8H)DB E8Poll the RS-232 Status Register and Master Reset of Port E8H and put the value into Register A.
*3190BIT 6,ACB 77Test Bit 6 of the RS-232 Status Register to check Data Set Ready (Pin 6 of the DB-25).
*3192JR NZ,318EH20 FAIf the DATA SET READY is ZERO then LOOP back 2 instructions to 318EH to poll again
*3194LD A,0FH3E 0FSet Register A to 0FH
*3196CALL 37D5HCD D5 37GOSUB to 37D5H to send the Character in Register A to the RS-232, once the RS-232 shows ready to accept that character.
*3199CALL 3086HCD 86 30GOSUB to 3086 to Poll the UART and wait for the P FLAG to not be set and then CONTINUE at 306CH
*319CCALL 3086HCD 86 30GOSUB to 3086 to Poll the UART and wait for the P FLAG to not be set and then CONTINUE at 306CH
*319FCALL 37D5HCD D5 37GOSUB to 37D5H to send the Character in Register A to the RS-232, once the RS-232 shows ready to accept that character.
*31A2-31A4JP 3517HC3 17 35JUMP 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
**3179LD A,(4210H)3A 10 42Get 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.
**317COR 10HF6 10OR against 10H (0001 0000) to turn all I/O ports on
**317ELD (4210H),A32 10 42Put the masked status back, first by loading it into 4210H
**3181OUT (ECH),AD3 EC... and then by sending it to 0ECH which is the same as 04210.
**3183LD A,08H3E 08Put an 08H into Register A
**3185OUT (D3H),AD3 D3Send 08H to Port D3H, which is the Network 4 Omninet MSB pointer
**3187XOR AAFPut an 00H into Register A
**3188OUT (D1H),AD3 D1Send 08H to Port D1H, which is the Network 4 Omninet LSB pointer
**318ALD BC,00D0H01 D0 00In 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
**318DLD HL,7000H21 00 70In further preparation for the INIR commands, point HL to the BUFFER for the code, which is 7000H
**3190PUSH HLE5Save the 7000H Buffer start point to the top of the stack
**3191INIRED B2Write a byte from port C to the memory location pointed to by HL, and then increase HL and decrease B.
**3193INIRED B2Write a byte from port C to the memory location pointed to by HL, and then increase HL and decrease B.
**3195INIRED B2Write a byte from port C to the memory location pointed to by HL, and then increase HL and decrease B.
**3197INIRED B2Write a byte from port C to the memory location pointed to by HL, and then increase HL and decrease B.
**3199INIRED B2Write a byte from port C to the memory location pointed to by HL, and then increase HL and decrease B.
**319BINIRED B2Write a byte from port C to the memory location pointed to by HL, and then increase HL and decrease B.
**319DINIRED B2Write a byte from port C to the memory location pointed to by HL, and then increase HL and decrease B.
**319FINIRED B2Write a byte from port C to the memory location pointed to by HL, and then increase HL and decrease B.
**31A1RETC9RETurn to CALLer
**31A2NOP00
**31A3NOP00
**31A4NOP00
31A5 - Output the TIMING MARK to the cassette
31A5LD A,01HLoad A with 01H. This is to prepare to send 0.46V to tape.
31A7OUT (0FFH),ALoad 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.
31A9LD B,0DHLoad B with 0DH in as a loop counter.
31ADLD A,02HLoad A with 01H. This is to prepare to send 0.0V to tape.
31AFOUT (0FFH),ALoad 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.
31B1LD B,0DHLoad B with 0DH in as a delay.
31B5GOSUB to 31F3 to send 0.46V to tape.
NOTE: 31F3H resets the cassette port, and then output a 0 to the Cassette Port FFH.
31B8LD B,78HLoad B with 78H in as a delay.
31BCRETRETURN.
31C0 - Turn Off The Cassette
31C0LD A,(4213H)Load A with the memory contents of 4213H.
NOTE: 4213H is the default interrupt vector setting for the cassette.
31C3OUT (0E0H),AOutput 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
31C5IN 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.
31C7LD 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.
31CAAND FDHMask A against FDH (1111 1101) to zero bit 1.
31CFEIEnable Interrupts.
31D0RETRETURN.
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 "**"
31D1EX DE,HLSwap DE and HL to remove the return address.
31D2EX (SP),HLSwap the memory contents pointed to by the STACK POINTER and HL (which is now what DE was).
31D3PUSH BCSave BC to the STACK.
31D4PUSH HLSave HL to the STACK.
31D5EX DE,HLSwap DE and HL back.
31D6IN 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.
31D8LD DE," "Load DE with SPACESPACE.
31DBLD (3C3EH),DELoad the screen memory location of 3C3EH with DE.
31E8 - Turn On The Cassette - Actually Set the Bit Mask and Output the Command
31E8LD 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.
31EBOR 02HOR A with 02H (0000 0010) to set Bit 1.
31EDLD (4210H),ASave 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.
31F0OUT (0ECH),AOutput that Bit Mask to port 0ECH.
31F2RETRETURN.
31F3 - Reset the Cassette Port. This routine OUTputs a 0 to the Cassette Port FFH
31F3XOR AWe want to reset the cassette port so we zero A.
31F4OUT (0FFH),ALoad 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.
31F6RETRETURN.
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.
31F7LD A,(HL)Load Register A with the memory contents pointed to by Register Pair HL.
31F8SUB 23HSubtract 23H so that we can test to see if the caller was a PRINT #command.
31FAIf 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.
3200RST 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.
3202RETIf we are here, then we have PRINT #n,so we RETURN.
3203 - Vector for a SLOW cassette read
3203LD B,08HLoad B with 8, representing the need to LOOP for 8 bits.
3205GOSUB to 3220H.
NOTE: 3220H reads the tape until it finds a timing mark or the BREAKis hit.
320ALD A,(4212H)Put the contents of 4212H into A.
NOTE: 4212H holds the cassette blinker counter.
320DINC ABump A.
320EAND 5FHMask A against 5F (0101 1111) to turn Bit 7 and Bit 5 off.
3210LD (4212H),APut A into 4212H.
NOTE: 4212H holds the cassette blinker counter.
3215LD A,(3C3FH)Load A with the screen contents at position 3C3FH.
3218XOR 0AHXOR 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).
321ALD (3C3FH),APut the revised A onto the screen at position 3C3FH.
321DLD A,DPut D (the byte) into A.
3220 - Cassette - Keep reading tape looking for a timing mark or BREAK.
3220PUSH BCSave BC to the STACK.
3221IN A,(FFH)Poll Port FFH with the results into Register A.
NOTE: FFH is the Cassette Port.
3223RLAPut 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).
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)
322ELD B,6EHLoad B with 6EH, which is the length of the timing mark.
Reset the cassette port
Next, wait for 98H Units (the length until a data pulse is expected)
3235LD B,98HLoad B with 98H, which is when the next data pulse should be available.
... continue
3239IN A,(FFH)Poll Port FFH and put the results into A.
NOTE: Port FFH is the Cassette Port.
323BPOP BCRestore BC from the STACK.
323CRLARotate 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.
323DRL DRotate 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.
3241 - Vector for a SLOW cassette write. On entry A is the byte to output.
3241PUSH AFSave AF to the STACK.
3242PUSH BCSave BC to the STACK.
3243PUSH DESave DE to the STACK.
3244LD C,08HLoad C with an 8, representing 8 bits to be written.
3246LD D,ALoad D with A.
NOTE: D will be the DATA BYTE.
324ARLC DRotate 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.
324CIf 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.
3251DEC CReduce the counter holding the number of bits to deal with by 1.
3254POP DERestore DE from the STACK.
3255POP BCRestore BC from the STACK.
3256POP AFRestore AF from the STACK.
3257RETRETURN.
3258 - "Write a 0 Bit" by simply waiting the appropriate amount of time and doing nothing.
325E - SLOW tape header write
325EPUSH HLSave HL to the STACK.
3262LD (420CH),HLLoad the memory contents of 420CH with HL.
NOTE: 420CH is the TAPE WRITE VECTOR.
3265LD B,53HLoad B with 53H (Decimal: 83) in prepartion to output 83 ZEROes.
3267XOR AClear A and all flags.
326DLD A,0A5HLoad A with A5H.
NOTE: A5H is the OUTPUT SYNC BYTE.
3274 - SLOW tape header read
3274PUSH HLSave HL to the STACK.
3278LD (420EH),HLPut the vector for a slow cassette read into the memory location at 420EH.
NOTE: 420EH is the TAPE READ VECTOR.
327BLD B,40HLoad B with 40H to set up a loop of 64 to try to find 64 zeroes.
327DLD D,00HLoad D with 0.
3282LD A,DLoad A with the D (the data byte) to begin to check the current data byte.
3283OR ASet up the flags.
3288GOSUB to 3220H.
NOTE: 3220H reads the tape until it finds a timing mark or the BREAKis hit.
328BLD A,DLoad A with the D (the data byte) to begin to check the current data byte.
328CCP 0A5HCompare A to A5H looking for a SYNC BYTE.
3290LD HL,"**"In preparation to display a "**", load HL with **.
3293LD (3C3EH),HLPut HL onto the screen at location 3C3EH.
3296LD A,HLoad A with H (which is a *.
3297POP HLRestore HL from the STACK.
3298POP BCRestore BC from the STACK.
3299POP DERestore DE from the STACK.
329ARETRETURN.
329B - FAST tape header write.
329BPUSH HLSave HL to the STACK.
329FLD (420CH),HLLoad the memory contents of 420CH with HL.
NOTE: 420CH is the TAPE WRITE VECTOR.
32A2LD B,00HLoad B with 00H to set up a loop of 256 to output 256 "55H" bytes.
32A4LD A,55HLoad A with "55H".
32A6GOSUB to 32B4H.
NOTE: 32B4 restore all registers from the STACK, and Fill C with A, and JUMP to cassette write
32ABLD A,7FHLoad A with 7FH.
NOTE: 7FH is the OUTPUT SYNC BYTE.
32ADGOSUB to 32B4H.
NOTE: 32B4 restore all registers from the STACK, and Fill C with A, and JUMP to cassette write
32B0LD A,A5HLoad A with A5H.
NOTE: A5H is the SLOW SYNC BYTE.
32B4 - Restore all registers from the STACK, and Fill C with A, and JUMP to cassette write.
32B4PUSH AFSave AF to the STACK.
32B5PUSH BCSave BC to the STACK.
32B6PUSH DESave DE to the STACK.
32B7LD C,ALoad C with A.
32BA - Save all registers to the STACK, and Fill C with A, GOSBUB to write out the START BIT ...
32BAPUSH AFSave AF to the STACK.
32BBPUSH BCSave BC to the STACK.
32BCPUSH DESave DE to the STACK.
32BDLD C,ALoad C with A.
32C1 - Call 3335H to Output a Bit 8 Times
32C1LD B,08HLoad B with an 8 to set up a loop for 8 bits.
32CA - Read the start bit, read 8 bits, check for error, and flash the star
32CDLD B,08HLoad B with an 8 to set up a loop for 8 bits.
32DA - FAST tape header read.
32DAPUSH HLSave HL to the STACK.
32DELD (420EH),HLLoad the TAPE READ VECTOR (memory location of 420EH) with the 32CAH.
32E1LD A,01HLoad A with a 1 to set the interrupt.
32E3OUT (0E0H),AOUTPUT 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
32E5LD B,80HSet up a loop of 80H (128) to try to read 128 bits.
32EALD A,CLoad A with C (which is the pulse width).
32EBCP 0FHCompare A to 0FH to see if the pulse width was too short.
32EFCP 3EHCompare A to 3EH to see if the pulse width was too long.
32F8LD B,40HSet up a loop of 40H (Decimal: 64) to try to read 64 bits.
3300LD D,CLoad D with C (which holds the delay count).
3304LD A,DLoad A with D to set up to find the difference in the delays.
3305SUB CSubtract C (the delay count) from A (which holds D).
3308NEGA = 0 - A.
330ACP 0DHCompare 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.
330CIf the CARRY FLAG is set, JUMP to 3313H which will bump L and continue the 64 bit loop.
330EINC HBump HL since we have one more zero bit.
3313INC LBump L since we have one more bit.
3316LD A,40HLoad A with 40H (64).
3318CP HCompare A with H to check for bits.
331BCP LCompare with A to check for one bits.
331ELD A,02HLoad A with 2 to set the interrupt vector.
3320OUT (0E0H),AOutput 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
3325LD D,00HZero D.
332DLD A,DLoad A with D (the read byte).
332ECP 7FHCompare A against 7F to check for a MARKER BYTE.
3335RLC CWe 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.
3341DEC DDecrement D as DELAY #1.
3344LD A,02HLoad A with a 2 to set up for a write of 0 VOLTS to TAPE.
3346OUT (0FFH),ALoad 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.
3348DEC EDecrement E as DELAY #2.
334BLD A,01HLoad A with 01H to prepare to send 0.85 VOLTS to TAPE.
334DOUT (0FFH),ALoad 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.
334FRETRETURN.
3350H - READ a BIT
3350EIEnable Interrupts.
3351LD C,00HLoad C with 0.
3353INC CBump C.
3354LD A,(3840H)Load A with the contents of 3840H so as to check for a BREAK.
3357AND 04HMask A with 4 (0000 0100).
335BDIDisable Interrupts.
335FLD (3C3EH),HLPut the "BK" in HL onto the video screen at 3C3EH.
3362JUMP 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
3365LD E,01HLoad E with 01H (Binary: 0000 0001) to make the bit go high.
3369LD E,00HLoad E with 0 to make the bit go low.
336BLD A,06HLoad A with 6.
336DADD A,CAdd A (6) to C, so COUNT = COUNT + 6.
336ELD C,APut the count held in A back into C.
336FIN A,(FFH)Poll FFH (to get the level) into A.
NOTE: FFH is the Cassette Port.
3371AND 01HMask A with 1 (0000 0001) to keep only Bit 0.
3373CP ECompare A with E (which was the set level).
3376POP AFRestore AF from the STACK.
3377POP AFRestore AF (the REMOTE CALLER'S ADDRESS) from the STACK.
3378RETRETURN to the caller's caller.
3379POP AFRestore AF from the STACK.
337AEIEnable Interrupts.
337BRETRETURN back top the loop.
337CH - Check for a Data Error.
337CLD A,CLoad A with C (the count).
337DCP 22HCompare 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
337FRL DWe 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.
3381CP 0FHMake 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.
3383If A < 0FH then it was too quick and we have a data error so JUMP forward a few instructions 3388H.
3385CP 3EHCompare A against 3EH to make sure it was not too slow.
3387RET CIf it wasn't too slow, RETURN.
3388LD A,44HIt was too slow, so load A with a D.
338ALD (3C3EH),APut the "D" on the screen at video location 3C3EH.
338DRETRETURN.
*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).
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).
*3391LD A,(3880H)Load A with the value held at 3880H (which are the SHIFT KEYS)
*3394AND 7CHMASK the value held in the SHIFT KEY RAM location against 0111 1100 to keep only bits 2, 3, 4, 5, and 6 live
*3396OR AOR A against itself to reset the flags
*3399LD E,APut the masked A into E.
*339AXOR (HL)Toggle against the old image.
*339BLD (HL),ESave the new image into (HL).
*339CAND EMask Register E against Rgister A.
*33A2GOSUB 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.
*33A5LD A,(3880H)Load A with the value held at 3880H (which are the SHIFT KEYS)
*33A8AND 7CHMASK the value held in the SHIFT KEY RAM location against 0111 1100 to keep only bits 2, 3, 4, 5, and 6 live
*33AACP ECompare A with E.
*33AERLARotate 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.
*33AFRLARotate 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.
*33B0We 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
*33B2LD A,(41F3H)Load Register A with the value of the character to be returned when the F3key is pressed
*33B5RETRETurn to Caller
*33B6 - Model 4 Gen 1 - KEYBOARD Routine - Check and Process the F2 Key or Jump Away.
RLARotate 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.
*33B9LD A,(41ECH)Load Register A with the value of the character to be returned when the F2key is pressed
*33BCRETRETurn to Caller
*33B6 - Model 4 Gen 1 - KEYBOARD Routine - Check and Process the F1 Key or Jump Away.
RLARotate 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.
*33BEIf the NC FLAG is set, it isn't the F1, so JUMP to 33C4 to keep checking special keys
*33C0LD A,(41EBH)Load Register A with the value of the character to be returned when the F1key is pressed
*33C3RETRETurn to Caller
*33C4 - Model 4 Gen 1 - KEYBOARD Routine - Part of the Keyboard Scan Routine. Keep Checking Special Keys.
RLARotate 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.
*33CA - Model 4 Gen 1 - KEYBOARD Routine - Part of the Keyboard Scan Routine.
LD (HL),ASave the character held in Register A into the memory location pointed to by Register Pair HL
33CBLD A,FFHLoad A with FF to set up for a FLAG = 0FFH = NO CONTROL.
33D0BIT 4,(HL)Test BIT 4 of (HL) to check for a DOWN ARROW.
33D4SLA LNext we need to check for a a LEFT SHIFTso shift L left.
33D6BIT 00H,(HL)Test BIT 0 of (HL) to check for a LEFT SHIFT.
33DALD A,1FHLoad A with 1F to set up for FLAG = CONTROL KEY.
33DCLD (4224H),ASave the CONTROL FLAG into (4224H).
NOTE: 4224H Holds the CONTROL KEY flag.
*33E2RET NCIf that routine exited with NC FLAG set, RETurn
33E6CP 1AHCheck A against 1AH to see if we have a SHIFT DOWN ARROW.
*33EBOR ASet the flags based on Register A
*33F2BIT 7,(HL)Test Bit 7 of the CONTROL KEY flag in RAM.
*33F6CP 2AHCheck A against a *.
*33FBAND (HL)Prepare to check for a BREAKby masking A against the memory contents of HL ...
*33FERET NZIf the result of the compare is NOT zero, then RETURN.
*33FFRST 28HIf we are here, then a BREAKwas hit, so call RST 28H to handle the BREAK.
*3400RETRETURN to Caller
*3401 - Model 4 Gen 1 - This is the BOOTSTRAP. Clears ports, checks for a BREAKkey and a Floppy Controller.
3401IM 1Set the INTERRUPT MODE to 1.
*3406LD B,0FHLet Register B = 0FH
*3408LD C,88HLet Register C = the CRT Controller Control Register Port
*340COUT (89H),ASend Register A to the CRT Controller Data Register
3410OUT (0E4H),AOutput A to Port E4H.
NOTE: Port E4H is the non-maskable interrupt latch. This is to clear the non-maskable interrupt status.
3412OR 20HOR 20H (0010 0000) against A to turn on Bit 5.
3414OUT (0ECH),AOutput 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.
3416LD A,81HLoad A with 81H (Decimal 129, Binary 10000001).
3418OUT (0F4H),AOutput 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.
341ALD A,D0HLoad A with D0H (Decimal: 208, Binary: 1101 0000).
341COUT (0F0H),AOutput "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
*341EPUSH BCUndertake a short delay of PUSHING BC, POPPING BC, and NOPing
*341FPOP BC
*3420NOP
3421LD A,04HLoad A with a 4 (Binary: 0000 0100).
3423OUT (0E0H),AOutput 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
3425LD A,0BHLoad A with 0BH (Binary: 00001011).
3427OUT (0F0H),AOutput 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).
3429Next we need to initialize some ports via a LDIR. The next 4 commands will move the 76 (4C) bytes from 36AAH to 4000H.
342C
342F
3432LDIR
3434Next we need to initialize more ports via a LDIR. The next 4 commands will move the 64 (40) bytes from 36F9H to 41E5H.
3437
343A
343DLDIR
3448IN 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
344AINC ABump the FDC Status by 1 (which would turn a FFH (128) into a 00H), making a Z flag mean "Disk Controller Not Ready".
344BIf the FDC Stats + 1 is zero, then we have no disk controller, so jump to Non-Disk BASIC at 3105H.
344EIf we are here, then we have a floppy controller, so set up for a loop of 65,535 times.
3451DEC BCDecrease BC by 1.
3452LD A,81HSet A to 81H (Decimal: 10000001).
3454OUT (0F4H),ASend 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.
3456LD A,B
OR CTo 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.
345BIN 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
345DBIT 2,ATest Bit 2 of A (the Floppy Status). If it is 0 then we haven't made it to track 0, otherwise we have.
345FStill 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.
3461LD E,05HSet up for a loop of 5 using Register E.
3466IN 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
3468BIT 01H,ACheck A to see if bit 1 (meaning "Drive Busy") is set.
346CDEC BCIf bit 1 is not set (meaning, the drive is NOT busy), reduce the counter (BC) by 1.
346DLD A,81HLoad A with 81H (Decimal: 129, Binary: 1000 0001).
346FOUT (0F4H),AOutput 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.
3471LD A,B
OR CTo 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.
3475If 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.
347BIf 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.
347DDEC EIf we are here, it found a diskette so next we need to find the index mark. First decrement E by 1.
3463HLD A,81HLoad A with 81H (Decimal: 129, Binary: 1000 0001).
3482OUT (0F4H),AOutput 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.
3484We 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.
3487LD (404AH),HL
348ALD A,C3HLoad A with C3H (Decimal: 195, Binary 1100 0011).
348CLD (4049H),APut the C3H into 4049H.
NOTE: 4049H is the Non-Maskable Interrupt Vector.
348FLD A,80HLoad A with 80H (Decimal: 128, Binary 1000 0000).
3491OUT (0E4H),AOutput "1000 0000" to Port E4H.
NOTE: Port E4H is the non-maskable interrupt latch.
3499LD A,01HPrepare to read Sector 1 by loading A with a 1.
349BOUT (0F2H),AOutput 1 to Port F2H.
NOTE: Port F2H is the Floppy Disk Controller Track Register.
349DLD A,80HPrepare to read a single sector by loading A with 80H (Decimal: 128, Binary: 1000 0000).
349FOUT (0F0H),AOutput "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).
34A1GOSUB to 37E1H to delay 3 instruction cycles, arguably the time it will take for the FDC to respond to the port command.
34A4IN 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
34A6AND 02HMask 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.
34ABINIInput the data byte.
34ADLD A,81HLoad A with 81H (Decimal: 129, Binary: 1000 0001) to select disk 0.
34AFOR 40HOR A with 40H (Binary: 0100 0000) to set A to double-density.
34B1OUT (0F4H),AOutput 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.
34B3INIInput the data byte.
34B5Loop 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.
34B8XOR AClear A and all flags.
34B9OUT (0E4H),ASend a 0 to Port E4H.
NOTE: Port E4H is the non-maskable interrupt latch. This is to clear the non-maskable interrupt status.
34BELD (4049H),HLLoad "45EDH" into 4049H.
NOTE: 4049H is the Non-Maskable Interrupt Vector.
34C1GOSUB to 37E1H to delay 3 instruction cycles, arguably the time it will take for the FDC to respond to the port command.
34C4IN 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
34C6POP HLClean up the Stack.
34C7AND 1CHMask 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.
34CCIf 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.
34CEIf 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.
34D1IN A,(E4H)Poll Port E4H into A.
NOTE: Port E4H is the Non-Maskable Interrupt Latch.
34D3BIT 5,ATest Bit 5 of Port E4 against A.
NOTE: Port E4H is the Non-Maskable Interrupt Latch.
*34DA - Model 4 Gen 1 - Part of the Keyboard routine.
*34DDAND 03HMask the SHIFT Register against 03H (Binary: 0000 0011) to keep only Bits 0 and 1 to check for shifts.
*34DFIf the masked A is 0, then we have no shifts, and skip the next instruction (to 34E3H).
*34E1SET 6,DSet BIT 6 of D to offset D for shifts.
LD A,(4019H)Load A with the contents of memory location 4019H to check for CAPS LOCK.
NOTE: 4019H is the CAPS LOCK TOGGLE.
*34E6OR ASet the flags.
*34E9SET 7,DSet 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.
*34EBLD A,(3880H)Put the contents of memory location 3880H into A to GET SHIFT(S).
*34EEAND 03HMask the SHIFT Register against 03H (Binary: 0000 0011) to keep only Bits 0 and 1 to check for shifts.
*34F0If the masked A is 0, then we have no shifts, and skip the next instruction (to 34F4H).
*34F2RES 7,DRESET 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.
34F7LD E,DWe need DE to be the OFFSET, so load E with D and ...
34F8LD D,00H... load D with 00.
34FAADD HL,DEAdd DE (the offset over the keyboard table) to HL (the keyboard table).
34FBLD A,(HL)Get the character pointed to by (HL) and put it into A.
*34FCRETRETurn 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.
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.
*3501RLCARotate the contents of A left one bit position. The contents of bit 7 are copied to the carry flag and bit 0.
*3504RLCARotate the contents of A left one bit position. The contents of bit 7 are copied to the carry flag and bit 0.
*3505If the CARRY FLAG is set, we have a non-graphic characters, so skip the next instructions.
*3507LD A,2EHOverwrite the current character held in Register A with a ., so that all graphic characters are printed as .'s.
Call the PRINT CHARACTER routine at 003BH (which sends the character in the A register to the printer).
*350FINC HLBump HL to the next character on the screen.
*3510BIT 6,HCheck 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).
*3515LD A,LPrepare to test of end of line by loading Register A with Register L.
*3516AND 03FHAND 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).
*3518If 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.
*351AGOSUB 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.
*351DLoop 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
*351FNOP
*3520NOP
*3521NOP
*3522NOP
*3523NOP
*3524NOP
*3525NOP
*3526NOP
*3527NOP
*3528NOP
*338EH - Model 4 Gen 2 - Jump Point for Keyboard Input.
*338ELD BC,3801H010138Load BC with 3801H (KEYBOARD ROW 0).
*3391LD HL,4036H213640Load HL with 4036H (BUFFER ROW 0).
*3394LD D,00H1600Load D with 0 (so D = ROW 0).
*3396LD A,(BC)0Aoad A with the contents held in (BC) to check the keyboard row.
*3397LD E,A5FLoad E with the contents held in (BC) to check the keyboard row.
*3398XOR (HL)AEXOR (HL) to set changed bits.
*3399LD (HL),E73Save the scan back into (HL).
*339AAND EA3Mask A with E (to mask the released keys).
Go to the next row
*339DINC D14Bump D so that D holds the NEXT row number.
*339EINC HL23Bump HL so that HL holds the NEXT buffer location.
*339FRLC CCB01We 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.
*33A1JP P,3396HF29633If 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
*33A4LD A,(41FDH)3AFD41If 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.
*33A7LD L,A6FLoad L with the contents of Register A.
*33A8LD A,(41FEH)3AFE41Load A with the memory contents of 41FEH.
NOTE: 41FEH is the saved IMAGE at the saved position in the keyboard scan data.
*33ABAND (HL)A6MASK A against (HL) to see if the previous keys are still pressed.
*33AFSBC HL,HLED62Zero HL by subtracting HL from HL.
*33B1LD (4201H),HL220142Load 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.
*33B7LD (41FFH),HL22FF41Load the memory location at 41FFH with 05DCH.
NOTE: 41FFH holds the keyboard scan repeat delay count.
*33BAXOR AAFClear A and all flags.
*33BBRETC9RETurn to Caller
*33BC - Model 4 Gen 2 - Keyboard Routine - If the same keys are still pressed then we need to deal with debounce.
*33BCLD E,A5FLoad E with A.
*33BDPUSH BCC5Save BC to the STACK.
*33C1CALL 0060HCD 60 00GOSUB 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.
*33C4POP BCC1Restore BC from the STACK.
*33C5LD A,(BC)0ALoad A with the memory contents pointed to by BC to re-check the keyboard.
*33C6AND EA3Compare A against E to check the pattern.
*33C7RET ZC8If not the same pattern then RETURN.
*33C8LD (41FEH),A32 FE 41If it is the same pattern then save A into (41FEH).
NOTE: 41FE is the SAVED IMAGE AT POSITION.
*33CBLD A,L7DLoad A with L (the scan position).
*33CCLD (41FDH),A32 FD 41Save A into (41FDH).
NOTE: 41FDH is the SAVED POSITION IN SCAN.
*33CFLD A,D7ALoad A with D (8 * ROW #).
*33D0RLA17Rotate 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.
*33D1RLA17Rotate 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.
*33D2RLA17Rotate 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.
*33D3LD D,A57Copy Register A into Register D
*33D4LD A,E7BCopy Register E into Register A
*33D5RRCA0FRotate A right one bit, with the contents of BIT 0 being put into BOTH the CARRY FLAG and BIT 7. D = 8* ROW # + KEY #.
*33D6RET CD8If the contents of BIT 0 of A was SET, RETurn
*33D7INC D14Bump D so that D holds the NEXT row number.
*33DA - Model 4 Gen 2 - Keyboard Routine.
*33DAPUSH HLE5Save HL to the top of the STACK.
*33DBLD HL,(4201H)2A0142Load HL with the repeat delay counter.
NOTE: 4201H is the REPEAT DELAY COUNTER.
*33DEINC HL23Bump HL.
*33DFLD (4201H),HL220142Load the memory location at 4201H with HL to clear the repeat counter.
NOTE: 4201H is the REPEAT DELAY COUNTER.
*33E2LD DE,(41FFH)ED5BFF41Load DE with the byte stored at 41FFH.
NOTE: 41FFH is the REPEAT DELAY COUNT.
*33E6SBC HL,DEED52Subtract with CARRY DE from HL.
*33E8POP DED1Restore old HL (which is what is in the stack) into DE.
*33ECXOR AAFClear A and all flags.
*33EDLD (DE),A12Put a 0 into the memory location pointed to (DE) to let the key be re-read.
*33EELD (4201H),HL220142Load the memory location at 4201H with HL to clear the repeat counter.
NOTE: 4201H is the REPEAT DELAY COUNTER.
*33F1LD L,96H2E96Load L with 96H to set a fast repeat count.
*33F3LD (41FFH),HL22FF41Save HL into the memory location at 41FFH.
NOTE: 41FFH is the REPEAT DELAY COUNT.
*33F9 - Model 4 Gen 2 - Enter NON-Disk BASIC.
*33FF - Model 4 Gen 2 Routine - Initialization - Process the CASS? Question
*33FFEIFBEnable Interrupts.
*3400CALL 3421HCD 21 34GOSUB to 3421H which loads A with a carrage return, and jumps to 0033H to display it.
*3403LD HL,05D1H21 D1 05Load HL with the address of the "CASS?" message.
*3406CALL 021BHCD 1B 02GOSUB to 021BH.
NOTE: 021BH will display the character at (HL) until a 03H is found.
*3409CALL 0049HCD 49 00GOSUB to 0049H.
NOTE: 0049H is the $KBWAIT routine which scans the keyboard and returns with the key pressed, if any, in register A.
*340CCP 0DHFE 0DCheck to see if Register A is holding a ENTER
*3410PUSH AFF5Save AF to the STACK.
NOTE: Register A currently holds the character pressed in response to the "CASS?" message.
*3411CALL 0033HCD 33 00GOSUB to 0033H pt display the character held in Register A at the current cursor position.
*3414POP AFF1Restore the answer to CASS? from the STACK into Register A.
*3415CP 48HFE 48Compare the answer to the CASS? Prompt held in Register A against with 48H (ASCII: H).
*3419CP 4CHFE 4CCompare the answer to the CASS? Prompt held in Register A against with 4CH (ASCII: L).
Set the flag for LOW SPEED CASSETTE
*341DXOR AAFSet A to 0.
*341ELD (4211H),A32 11 42Save 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
*3421LD A,0DH3E 0DPut a CARRIAGE RETURN into Register A
*3423JP 0033HC3 33 00JUMP 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.
*3426IM 1ED56Set the INTERRUPT MODE to 1.
*3428LD SP,407DH317D40Load the STACK POINTER with 407DH.
*342BLD B,0FH060FLet Register B = 0FH
*342DLD C,88H0E88Let Register C = the CRT Controller Control Register Port
*342FOUT (C),BED41Set the CRT Controller Control Register Port to the contents of B (which are decreasing as the loop progresses)
*3431OUT (89H),AD389Send Register A to the CRT Controller Data Register
*3435OUT (E4H),AD3E4Clear the Non-Maskable Interrupt Latch by sending the contents of Register A to Port E4H
*3437OR 20HF620OR 20H (0010 0000) against A to turn on Bit 5.
*3439OUT (ECH),AD3ECOutput 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.
*343BLD A,81H3E81Load A with 81H (Decimal 129, Binary 10000001).
*343DOUT (F4H),AD3F4Output 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.
*343FLD A,D0H3ED0Load A with D0H (Decimal: 208, Binary: 1101 0000).
*3441OUT (F0H),AD3F0Output "D0H" to the FDC Status Register at Port F0H. This resets the FDC and puts it in mode 1
*3443PUSH BCC5Undertake a short delay of PUSHING BC, POPPING BC, and NOPing
*3444POP BCC1Undertake a short delay of PUSHING BC, POPPING BC, and NOPing
*3445NOP00Undertake a short delay of PUSHING BC, POPPING BC, and NOPing
*3446LD A,04H3E04Load A with a 4 (Binary: 0000 0100).
*3448OUT (E0H),AD3E0Output 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.
*344ALD A,0BH3E0BLoad A with 0BH (Binary: 00001011).
*344COUT (F0H),AD3F0Output 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).
*344ELD HL,36AAH21AA36Next we need to initialize some ports via a LDIR. The next 4 commands will move the 76 (4C) bytes from 36AAH to 4000H.
*3451LD DE,4000H110040Set the LDIR Destination to 4000H
*3454LD BC,004CH014C00Set the number of bytes to move to 4CH
*3457LDIREDB0Move the 76 (4C) bytes from 36AAH to 4000H
*3459LD HL,36F9H21F936Next we need to initialize more ports via a LDIR. The next 4 commands will move the 64 (40) bytes from 36F9H to 41E5H.
*345CLD DE,41E5H11E541Set the LDIR Destination to 41E5H
*345FLD BC,0040H014000Set the number of bytes to move to 40H
*3462LDIREDB0Move the 64 (40) bytes from 36F9H to 41E5H.
The Model 4 ROM Student Network Edition ROM changes that to ...
*346AJP NZ,33F9HC2F933If we have a BREAK key then jump to Non-Disk BASIC at 33F9H.
*346DIN A,(F0H)DBF0We 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
*346FINC A3CBump the FDC Status by 1 (which would turn a FFH (128) into a 00H), making a Z flag mean "Disk Controller Not Ready".
*3470JP Z,33F9HCAF933If the FDC Stats + 1 is zero, then we have no disk controller, so jump to Non-Disk BASIC at 33F9H.
*3473LD BC,0000H010000If we are here, then we have a floppy controller, so set up for a loop of 65,535 times.
*3476DEC BC0BDecrease BC by 1.
*3477LD A,81H3E81Set A to 81H (Decimal: 10000001).
*3479OUT (F4H),AD3F4Send 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.
*347BLD A,B78To 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.
*347COR CB1
*3480IN A,(F0H)DBF0We 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
*3482BIT 2,ACB57Test Bit 2 of A (the Floppy Status). If it is 0 then we haven't made it to track 0, otherwise we have
*3484JR Z,3476H28F0Still 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.
*3486LD E,0AH1E0ASet up for a loop of 10 using Register E; this will be the number of tries to find an index mark.
*3488LD BC,0000H010000Set up for a loop of 65,536 using Register Pair BC.
*348BIN A,(F0H)DBF0Poll 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
*348DBIT 1,ACB4FCheck A to see if bit 1 (meaning "Drive Busy") is set.
*3491CALL 349BHCD9B34If 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.
*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.
*3496DEC E1DIf 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.
*3497JR NZ,34ACH2013So long as we have not run out of the loop of 10 tries, JUMP to 34ACH to find the index mark.
*349B - Model 4 Gen 2 Routine - We have exhausted our tries, so display DISKETTE and keep trying.
*349BDEC BC0BIf bit 1 is not set (meaning, the drive is NOT busy), reduce the counter (BC) by 1.
*349CLD A,81H3E 81Load A with 81H (Decimal: 129, Binary: 1000 0001).
*349EOUT (F4H),AD3 F4Output 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.
*34A0LD A,B78To 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.
*34A1OR CB1
*34A2RET NZC0If BC is not 0, then RETurn
*34A3POP HLE1Clear the STACK
*34A4LD HL,0277H21 77 02If 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.
*34A7CALL 021BHCD 1B 02GOSUB to 021BH. Note; 021BH will display the character at (HL) until a 03H is found.
*34AAJR 3486H18 DAIf 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.
*34ACLD BC,0000H010000Set up for a loop of 65,536 using Register Pair BC.
*34AFIN A,(F0H)DBF0Poll 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
*34B1BIT 1,ACB4FCheck the Floppy Disk Controller Status (held in Register A) to see if bit 1 (meaning "Drive Busy") is set.
*34B3JR Z,34BAH2805If bit 1 is not set (meaning, the drive is NOT busy), JUMP to 34BAH to restart the read attempts.
*34B5CALL 349BHCD9B34If we are here, then the drive was busy, so GOSUB to 349BH to display DISKETTE? and try again
*34B8JR 34AFH18F5JUMP back to the top of this routine and keep looking
*34BA - Model 4 Gen 2 Routine - Finish initializing the floppy disk boot.
*34BADEC E1DIf we are here, it found a diskette so next we need to find the index mark. First decrement E by 1.
We are going to try to read a sector
*34BDLD A,81H3E 81Load A with 81H (Decimal: 129, Binary: 1000 0001).
*34BFOUT (F4H),AD3 F4Output 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.
*34C1LD HL,34F5H21 F5 34We 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.
*34C4LD (404AH),HL22 4A 40Put the desired JUMP address of 34F5H into (404AH)
*34C7LD A,C3H3E C3Load A with C3H (Decimal: 195, Binary 1100 0011).
*34C9LD (4049H),A32 49 40Put the C3H into 4049H.
NOTE: 4049H is the Non-Maskable Interrupt Vector.
*34CCLD A,80H3E 80Load A with 80H (Decimal: 128, Binary 1000 0000).
*34CEOUT (E4H),AD3 E4Output "1000 0000" to the the non-maskable interrupt latch via Port E4H.
*34D0LD BC,00F3H01 F3 00Set BC to 0F3H.
NOTE: Port F3H is the Floppy Disk Controller Data Register.
*34D3LD HL,4300H21 00 43Set HL to 4300H, which is where the data is going to go.
*34D6LD A,01H3E 01Prepare to read Sector 1 by loading A with a 1.
*34D8OUT (F2H),AD3 F2Output 1 to the Floppy Disk Controller Track Register at Port F2H.
*34DALD A,80H3E 80Prepare to read a single sector by loading A with 80H (Decimal: 128, Binary: 1000 0000).
*34DCOUT (F0H),AD3 F0Output "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).
*34DECALL 30CAHCD CA 30GOSUB to 30CAH to delay 3 instruction cycles, arguably the time it will take for the FDC to respond to the port command.
*34E1IN A,(F0H)DB F0Poll 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
*34E3AND 02HE6 02Mask 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.
*34E8INIEDA2Input the data byte.
*34EALD A,81H3E81Load A with 81H (Decimal: 129, Binary: 1000 0001) to select disk 0.
*34ECOR 40HF640OR A with 40H (Binary: 0100 0000) to set A to double-density.
*34EEOUT (F4H),AD3F4Output 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.
*34F0INIEDA2Input the data byte.
*34F2JP 34EAHC3 EA 34Loop 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.
*34F5XOR AAFClear A and all flags.
*34F6OUT (E4H),AD3 E4Send a 0 to the non-maskable interrupt latch via Port E4H. This is to clear the non-maskable interrupt status.
*34F8LD HL,45EDH21 ED 45Now that the NMI jumped here, we need to set a new NMI jump, this time to 45EDH.
*34FBLD (4049H),HL22 49 40Load the destination location into (4049H) which is the Non-Maskable Interrupt Vector.
*34FECALL 30CAHCD CA 30GOSUB to 30CAH to delay 3 instruction cycles, arguably the time it will take for the FDC to respond to the port command.
*3501IN A,(F0H)DB F0Poll 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
*3503POP HLE1Clean up the Stack.
*3504AND 1CHE61CMask 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.
*3509JR 34BDH18B2If 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.
*350BJP NZ,4049HC24940If 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.
*350EIN A,(E4H)DBE4Poll the Non-Maskable Interrupt Latch at Port E4H into A.
*3510BIT 5,ACB6FTest Bit 5 of Port E4 against A.
*3512JR Z,350EH28FALoop back 2 instructions until it is set (i.e., not a zero).
*3517 - Model 4 Gen 2 Routine - Finish up initialization by filling 256 bytes into 4300H and then JUMPing there
*3517OR AB7Set the FLAGS based on Register A
*3518JP NZ,3179HC27931If A is not ZERO then Jump to 3179H to continuing initialization routine by setting up the RS-232
*351BLD HL,4300H210043Set HL to 4300H, which is where the data is going to go.
*351ECALL 3086HCD8630GOSUB to 3086 to Poll the UART and wait for the P FLAG to not be set and then CONTINUE at 306CH
*3521LD (HL),A77Store Register A into the memory location pointed to by Register Pair HL
*3522INC L2CBump Register L by 1
*3523JR NZ,351EH20F9So long as we have not overflowed L, LOOP back to 351EH
*3525JP 37E0HC3E037If 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).
*3528NOP00No Operation
**3517 - Model 4 ROM Student Network Edition - Finish up initialization
**3517CALL 3086HCD8630GOSUB to 3086 to simply poll the keyboard and mask for a "4" key
**351AJR NZ,3520H2004If the key isn't a "4" then restore BC and return to processing as a regular Model 4 would
**351CCALL 028DHCD8D02GOSUB to 028DH to check for a BREAK key.
**351FRETC9RETurn to CALLer
**3520POP BCC1Restore the contents at the top of the STACK into Register Pair BC
**3521JP 3179HC37931Jump to 3179H to continuing initialization routine by setting up the RS-232
**3524NOP00
**3525NOP00
**3526NOP00
**3527NOP00
**3528NOP00
3529H - Deal with the cursor.
352CPUSH DEPush that return address onto the Stack.
352DIN 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.
352FLD 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.
3532OR ASet the flags.
3535LD 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.
3538OR ATest A.
353BLoad HL with 401AH.
NOTE: 401AH is the memory location that stores the cursor blink count.
353EDEC (HL)Reduce the memory contents of (401AH) by one.
NOTE: 401AH is the memory location that stores the cursor blink count.
3541LD (HL),07HSet the cursor blink count to 7.
NOTE: 401AH is the memory location that stores the cursor blink count.
3543INC HLBump HL. This will increase HL from 401AH to 401BH.
NOTE: 401BH holds the cursor blink status - 0 = Off, Anything Else = On.
3544LD A,(HL)Poll the cursor blink status memory location and put the results into A.
3545AND 01HMask A against 0000 0001, to have only Bit 0 active.
3547XOR 01HXOR A with 01H.
3549LD (HL),APut the toggled cursor blink status into the appropriate memory location.
354ALD HL,(4020H)Poll (4020H) and put the result into HL.
NOTE: 4020H holds the current cursor position.
354DIf the current cursor position is 0, then it is off, so JUMP down 2 instructions to 3554H to make the cursor a blank (space).
354FLD A,(4023H)Load A with the memory contents of (4023H).
NOTE: 4023H holds the cursor character.
3552JUMP down to 3556H to skip the next instruction and continue this routine by displaying the character in A.
3554LD A,20HLoad 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.
3556LD (HL),APut the character held in A into the memory location pointed to by (HL).
NOTE: (HL) will hold the current cursor position.
355ADEC (HL)Decrease the number held at (4216H) by 1.
355BRET NZIf the number held at (4216H) is not zero, then RETURN.
355CLD (HL),1EHPut a 1EH (Decimal: 30) into (4216H).
NOTE: 4216H is the heartbeat counter.
355EINC HLBump HL by 1. HL will now point to 4217H, which is the memory location that holds the SECONDS.
355FLoad 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.
3562LD B,03HLoad B with a 03H, to set up a loop where we test seconds, minutes, and hours against their maximums.
3564INC (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.
3565LD 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.
3566SUB (HL)Compare the maximum to what we have by subtracting that maximum from the value pointed at in (HL).
3567RET NZIf there is no difference between what we have and the maximum then RETURN.
3568LD (HL),AIf 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).
3569INC HLBump 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.
356AINC DEBump 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.
356BLoop back to 3564H until the loop of 3 has been met, meaning that we have processed seconds, minutes, and hours.
356DINC HLBump HL one more time, to 421BH.
NOTE: 421BH holds the current DAY portion of the date.
356EINC (HL)Bump the DAY portion of the date.
356FINC HLBump HL one more time, to 421CH.
NOTE: 421BH holds the current MONTH portion of the date.
3570LD A,(HL)Get the month and put it into A.
3571DEC HLDecrease HL back to to 421BH.
NOTE: 421BH holds the DAY portion of the date.
3572DEC ADecrease A by one (to the previous month).
NOTE: DE currently points to the memory locations housing the of days in each month.
3573ADD A,EAdd E to A.
3574LD E,ALoad E with the result. E = E + A.
3575LD A,(DE)Poll the number of days in a month and put the result into A.
3576CP (HL)Compare A against (HL).
NOTE: If this is not a loop, the HL holds the day portion of the date.
3577RET NCIf 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.
3578LD 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.
3579CP 1EHCompare 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.
357BIf the carry flag isn't set then JUMP to 3583H to update the MONTH, but not the YEAR.
357DDEC HLDecrement HL to now point to 421AH.
NOTE: 421AH points to the current YEAR.
357ELD A,(HL)Load the YEAR into A.
357FINC HLBump HL to 421BH.
NOTE: 421BH points to the current DAY.
3580AND 03HMask A (which is holding the year) with 03H (Binary: 00000011) to test for a leap year.
3582RET ZRETURN if that mask showed that we are in the 4th year of a cycle (because 04 and higher are turned off).
3583LD (HL),01HPut a 1 into the memory location pointed to by HL (which is DAY).
3585INC HLBump HL to 421CH.
NOTE: 421BH points to the current MONTH.
3586INC (HL)Increase the current MONTH by 1.
3587LD A,(HL)Put the current MONTH into A.
3588SUB 0DHSubract 13 from A.
NOTE: This will test against a month 13. If A is < 13, then the CARRY FLAG will be set.
358ARET CIf it is NOT month 13 then RETURN to skip the next code which increases the YEAR.
358BLD (HL),01HIf we are here, then MONTH = 13, so set MONTH to 1.
358DDEC HLDecrement HL to 421BH.
NOTE: 421BH points to the current DAY.
358EDEC HLDecrement HL to 421AH.
NOTE: 421BH points to the current YEAR.
358FINC (HL)Bump the current year.
3590RETRETURN.
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.
3591LD 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.
3594BIT 0,ATest Bit 0 of A to see if the clock is on or off.
3596RET ZIf Bit 0 of A is ZERO, then return.
3597LD 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.
359ACP 1EHCompare the heartbeat counter against 1EH.
NOTE: A CP actually subtracts 1E from A without modifying A, but the flags are set accordingly.
359CRET NZIf the clock was not just updated, then RETURN.
359DIf 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.
35A3LD C,3AHLoad C with a :.
NOTE: This routine is also used to convert the date, and C will be swapped out to a /for that routine.
35A5LD B,03HLoad B with a 3.
NOTE: This is because we need to convert 3 numbers, so we will loop 3 times.
35A7LD 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.
35A8DEC DEDecrement DE to point to the next unit to be dealt with.
35A9LD (HL),2FHLoad the memory location pointed to by (HL) with 2F.
NOTE: 2F is a /which is also 1 character below a 0.
35ABINC (HL)Increase whatever is held in (HL). On the first iteration, this change the character at the screen location to a 0.
35ACSUB 0AHA = 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.
35B0ADD A,3AHIf 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.
35B2INC HLBump HL to point to the next location on the video screen. On the first iteration, this will be the 2nd digit of the HOUR.
35B3LD (HL),APut 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.
35B4INC HLBump HL to point to the next location on the video screen. On the first iteration, this will be the 3rd character.
35B5DEC BDecrement B to the next unit. On the first iteration, this will move from 3 to 2.
35B6RET ZIf we have processed all passes in the loop, RETURN.
35B7LD (HL),CIf we are here, then the routine has not yet looped 3 times, so put a :onto the screen.
35B8INC HLBump HL to point to the next location on the video screen.
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 /.
35BELD C,2FHLoad C with a /.
35C2H - Maskable Interrupt Handler.
35C2PUSH AFSave AF to the STACK.
35C3IN A,(E0H)Poll Port E0H which is the MASKABLE INTERRUPT LATCH and put the results into A.
35C5RRARotate 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.
35C9RRARotate 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.
35CAIf Bit 1 (which is now in the carry) is low then JUMP to 3369H (which is a cassette routine with E set to LOW).
35CDPUSH BCSave all the registers.
35CEPUSH DE
35CFPUSH HL
35D0PUSH IX
35D2PUSH IY
35D7PUSH HLPush HL to the STACK.
35D8RRARotate 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.
35D9If 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.
35DCRRARotate 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.
35DDIf 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.
35E0RRARotate 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.
35E1If 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.
35E4RRARotate 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.
35E5If 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.
35E8RRARotate 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.
35E9If 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.
35ECRRARotate 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.
35EDIf 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.
35F0POP HLRestore all registers.
35F1POP IY
35F3POP IX
35F5POP HL
35F6POP DE
35F7POP BC
35F8POP AF
35F9EIEnable Interrupts.
35FARETRETURN.
35FBH - RS-232 Initialization Routine. I'm [guessing] that IX is set to 41F5H
35FBDIDisable Interrupts so they don't interrupt this routine.
35FCIN 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)
35FECP FFHCompare A with FFH to see if the RS-232 exists.
3602XOR AFlip the RS-232 Port Results, just to get a non-zero result.
3603OUT (0E8H),AOutput A to port E8H.
NOTE: Port E8H is the RS-232 Status Register & Master Reset. Outputting ANYTHING to Port E8H resets the RS-232.
3605LD A,(IX+03H)Load the BAUD RATE CODE into A.
NOTE: 41F8H holds the baud rate code.
3608OUT (0E9H),AOutput 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
360ALD A,(IX+04H)Load the CONFIURATION CODE into A.
NOTE: 41F9 holds the RS-232 Configuration Code.
360DOR ASet the flags.
3610OUT (0EAH),AOutput 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)
3619LD A,(IX+05H)Load the WAIT SWITCH into A.
NOTE: 41FAH holds the RS-232 Wait Switch.
361COR ASet the flags.
361FSET 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
3623SET 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
362BOR ASet flags.
362ESET 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
3632SET 02H,(IY+04H)Set Bit 2 of 41F1H to set the ACTIVE FLAG.
3636IN 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
3638EIRe-Enable Interrupts.
3639RETRETURN.
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.
363AXOR AClear A and all flags.
363BLD B,04HLoad 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
363DLD C,0E8HLoad C with E8H.
363FOUT (C),ASend a 0 to the current port. On the first iteration, this is E8H (the RS-232 Status Register & Master Reset).
3641INC CBUMP C to the next port.
3647LD B,03HLoad B with 3 as a counter. 3 is for 3 bytes - 41E8H, 41E9H, and 41EAH.
3649LD (HL),00HLoad (HL) with 00H.
364BINC HLBump HL.
364ELoad HL with 41F0H.
NOTE: 41F0H is the 1 characer output buffer for the RS-232 Output DCB.
3651LD B,03HLoad B with 3 as a counter. 3 is for 3 bytes - 41F0H, 41F1H, and 41F2H.
3653LD (HL),00HLoad (HL) with 00H.
3655INC HLBump HL.
365AH - RS-232 Input Routine.
365ALoad IX with 41E5H.
NOTE: 41E5H is the DCB for RS-232 Input. 41E8H is the 1 Character RS-232 Input.
365EXOR AClear A and all Flags.
365FLD (IX+03H),ALoad (41E8H) with a Zero.
NOTE: 41E8H is the 1 Character RS-232 Input.
3662BIT 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.
3666RET ZIf the Driver is OFF, RETURN.
3667IN 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)
3669BIT 7,ATest Bit 7 of A.
NOTE: Bit 7 will be 1 if DATA READY (1=True).
366DBIT 1,(IX+04H)Test Bit 1 of 41E9H.
NOTE: Bit 1 of 41E9 contains the WAIT/NO WAIT of the RS-232 Input DCB.
3671RET ZIf its NO WAIT then RETURN, otherwise continue (to keep polling).
3677JUMP to 4203H if the BREAKkey was pressed.
NOTE: 4203H JUMPS to 022EH and is the break vector for tape and RS-232.
367AIN 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.
367CLD (IX+03H),ALoad (41E8H) with a A (the data from Port EBH).
NOTE: 41E8H is the 1 Character RS-232 Input.
367FRETRETURN.
3680H - RS-232 Output Routine.
3680Note. 41EDH is the RS-232 Output DCB, and 41F1H holds DRIVER ON/OFF in BIT 2, and WAIT/NO WAIT in BIT 1.
3684BIT 2,(IX+04H)Test Bit 2 of 41F1H to see if the RS-232 is active.
3688RET ZIf the RS-232 is NOT active, RETURN.
3689IN 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)
368BBIT 6,ATest Bit 6 of Port EAH to see READY TO SEND.
368FBIT 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.
3693RET ZIf RS-232 is NOT active, RETURN.
3699JUMP to 4203H if the BREAKkey was pressed.
NOTE: 4203H JUMPS to 022EH and is the break vector for tape and RS-232.
369CLD A,(IX+03H)Load A with the memory contents of 41F0H.
NOTE: 41F0H is RS-232 output buffer byte.
369FOR ATest A and Set Flags.
36A0If not zero, then there is a character in the buffer, so skip the next instruction and leave that byte in A.
36A2LD A,CLoad A with C [GET CHAR FROM DISPATCHER].
36A3OUT (0EBH),ASend 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.
36A5LD (IX+03H),00HLoad memory contents of the RS-232 output byte (at 41F0H) with a 0.
36A9RETRETURN.
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
36D5FFH
36D600HThis 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.
36D7
36DARST 00H
36DBNOP
36DCNOP
36DDXOR A
36DERET
36DFNOP
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
36F4NOP
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-370006 1BTwo 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-37061B 02 21 30 00 00 00
3707-370808 00Two 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-370E02 1B 30 55 6C FF
370F-371000 00Two 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-371F00 00 FF FF 00 00 C3 2E 02 C3 FA 35 C3 FA 35
3720-373041 32 03 32 28 03 3C 04 00 00 1E 00 00 00 00 00 00
3731-373300 00 00Three 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-373800 00 00 00 FF
*3739H - Model 4 Gen 1
*373FLD D,00HLoad D with 0 (so D = ROW 0).
*3742LD E,ALoad E with the contents held in (BC) to check the keyboard row.
*3743XOR (HL)XOR (HL) to set changed bits.
*3744LD (HL),ESave the scan back into (HL).
*3745AND EMask A with E (to mask the released keys).
*3748 - Model 4 Gen 1 routine to Go to the next Keyboard row
*3748INC DBump D so that D holds the NEXT row number.
*3749INC HLBump HL so that HL holds the NEXT buffer location.
*374ARLC CWe 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.
*374CIf 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
*374FLD 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.
*3752LD L,ALoad L with the A.
*3753LD 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.
*3756AND (HL)MASK A against (HL) to see if the previous keys are still pressed.
*375ASBC HL,HLZero HL by subtracting HL from HL.
*375CLD (4201H),HLLoad 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.
*3762LD (41FFH),HLLoad the memory location at 41FFH with 05DCH.
NOTE: 41FFH holds the keyboard scan repeat delay count.
*3765XOR AClear A and all flags.
*3766RETRETURN.
*3767 - Model 4 Gen 1 routine to Keyboard Repeat - Jumps Here if the same keys are still pressed.
*3768PUSH BCSave BC to the STACK.
*376CGOSUB 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.
*376FPOP BCRestore BC from the STACK.
*3770LD A,(BC)Load A with the memory contents pointed to by BC to re-check the keyboard.
*3771AND ECompare A against E to check the pattern.
*3772RET ZIf not the same pattern then RETURN.
*3773LD (41FEH),AIf it is the same pattern then save A into (41FEH).
NOTE: 41FE is the SAVED IMAGE AT POSITION.
*3776LD A,LLoad A with L (the scan position).
*3777LD (41FDH),ASave A into (41FDH).
NOTE: 41FDH is the SAVED POSITION IN SCAN.
*377ALD A,DLoad A with D (8 * ROW #).
*377BRLARotate 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.
*377CRLARotate 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.
*377DRLARotate 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.
*377ELD D,AD = A
*377FLD A,EA = E
RRCARotate A right one bit, with the contents of BIT 0 being put into BOTH the CARRY FLAG and BIT 7. D = 8* ROW # + KEY #.
*3781RET CIf the contents of BIT 0 of A was SET, RETurn
*3782INC DINC DBump D so that D holds the NEXT row number.
*3785 - Model 4 Gen 1 routine to Keyboard Repeat - Jumps Here if the same keys are still pressed.
*3786LD HL,(4201H)Load HL with the repeat delay counter.
NOTE: 4201H is the REPEAT DELAY COUNTER.
*3789INC HLBump HL.
*378ALD (4201H),HLLoad the memory location at 4201H with HL to clear the repeat counter.
NOTE: 4201H is the REPEAT DELAY COUNTER.
*378DLD DE,(41FFH)Load DE with the byte stored at 41FFH.
NOTE: 41FFH is the REPEAT DELAY COUNT.
*3791SBC HL,DESubtract with CARRY DE from HL.
*3793POP DERestore old HL (which is what is in the stack) into DE.
*3797XOR AClear A and all flags.
*3798LD (DE),APut a 0 into the memory location pointed to (DE) to let the key be re-read.
*3799LD (4201H),HLLoad the memory location at 4201H with HL to clear the repeat counter.
NOTE: 4201H is the REPEAT DELAY COUNTER.
*379CLD L,96HLoad L with 96H to set a fast repeat count.
*379ELD (41FFH),HLSave HL into the memory location at 41FFH.
NOTE: 41FFH is the REPEAT DELAY COUNT.
*37A4 - Model 4 Gen 1 routine jumped to from the the middle of the tokenize routine.
CP 22HIf 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 ".
*37A6If the character in register A is not a ", then JUMP forward a few instructions to 37B2H.
*37A8LD A,(409FH)Load A with the memory contents of 409FH.
NOTE: 409FH is the DATA FLAG.
*37ABXOR 01HXOR 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.
*37ADLD (409FH),ALoad memory location 409FH with a the XOR'd results.
NOTE: 409FH is the DATA FLAG.
*37B0LD A,22HLoad A with 22H, which is a ".
CP 3AHCompare 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.
*37B4If A is 3AH then JUMP to 06AAH.
NOTE: 06AAH is in the middle of the KEYBOARD DRIVER ENTRY ROUTINE.
*37B7LD A,(409FH)Otherwise, Load A with the memory contents of 409FH.
NOTE: 409FH is the DATA FLAG.
*37BBRRARotate 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.
*3792If Bit 7 of the DATA FLAG was set, then JUMP to 06A8H.
NOTE: 06A8H is in the middle of the KEYBOARD DRIVER ENTRY ROUTINE.
*37BERLARotate 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.
*37BFJUMP 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$)
RST 10HCall 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.
*37C3PUSH HLSave HL (the current position) to the STACK.
*37C4LD A,11HLoad A with 11H (Decimal: 17).
NOTE: This is to set up for a 17 Byte String.
*37C9LD HL,(40D4H)Load HL with the memory contents of (40D4H).
NOTE: 40D4 is the string pointer.
*37CFLD (HL),20HLoad the memory location pointed to by HL with a SPACE.
*37D1INC HLIncrement HL to move 1 character over.
*37D8 - Model 4 Gen 1 routine to Toggle Caps Lock.
*37DDXOR (HL)Invert the contents of the (4019H).
*37DELD (HL),AStore the inverted (i.e., toggled) contents of (4019H) back into (4019H)
*37E0RETRETurn to Caller.
*37E1 - Model 4 Gen 1 routine to do a very short delay routine
*37E5 - Model 4 Gen 1 routine Unused Code
*37E5NOP
*37E6NOP
*37E7NOP
*37E8RST 38H
*37E9RST 38H
*37EANOP
*37EBH - Model 4 Gen 1 routine to display the Copyright Message
*37F4 - Model 4 Gen 1 routine to enable the TIME$ Command
*37F7LD (4177H),HLLoad 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.
*37F7JUMP to 022EH to continue.
NOTE: 022EH will enable interrupts, show the READY prompt, and RETURN.
*37FD - Model 4 Gen 1 unused Code
*37FDPUSH HL
*37FEPUSH HL
*37FFRST 38H
*3739H - Model 4 Gen 2 - Part of the Keyboard Routine to check for SHIFT and CAPS LOCK and deal with them accordingly.
*3739LD A,(3880H)3A 80 38Load A with the value held at 3880H (which are the SHIFT keys)
*373CAND 03HE6 03Mask the SHIFT Register against 03H (Binary: 0000 0011) to keep only Bits 0 and 1 to check for shifts.
*373EJR Z,3742H28 02If the masked A is 0, then we have no shifts, and skip the next instruction (to 3742H).
*3740SET 6,DCB F2Set BIT 6 of D to offset D for shifts.
*3742LD A,(4019H)3A 19 40Load A with the contents of memory location 4019H to check for CAPS LOCK.
NOTE: 4019H is the CAPS LOCK TOGGLE.
*3745OR AB7Set the flags.
*3746JR Z,3753H28 0BIf the ZERO flag is set then there is NO CAPS LOCK so JUMP to 3753H to skip the next intructions.
*3748SET 7,DCB FASet 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.
*374ALD A,(3880H)3A 80 38Put the contents of memory location 3880H into A to GET SHIFT(S).
*374DAND 03HE6 03Mask the SHIFT Register against 03H (Binary: 0000 0011) to keep only Bits 0 and 1 to check for shifts.
*374FJR Z,3753H28 02If the masked A is 0, then we have no shifts, and skip the next instruction (to 3753H).
*3751RES 7,DCB BARESET 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.
*3753LD HL,304EH21 4E 30Load HL with 304EH (the KEYBOARD TABLES).
*3756LD E,D5AWe need DE to be the OFFSET, so load E with D and ...
*3757LD D,00H16 00... load D with 00.
*3759ADD HL,DE19Add DE (the offset over the keyboard table) to HL (the keyboard table).
*375ALD A,(HL)7EGet the character pointed to by (HL) and put it into A.
*375BRETC9RETurn 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
*375CCP 22HFE 22If 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 ".
*375EJR NZ,376AH20 0AIf the character in register A is not a ", then JUMP forward a few instructions to 376AH.
*3760LD A,(409FH)3A 9F 40Load A with the memory contents of 409FH.
NOTE: 409FH is the DATA FLAG.
*3763XOR 01HEE 01XOR 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.
*3765LD (409FH),A32 9F 40Load memory location 409FH with a the XOR'd results.
NOTE: 409FH is the DATA FLAG.
*3768LD A,22H3E 22Load A with 22H, which is a ".
*376ACP 3AHFE 3ACompare 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.
*376CJP NZ,06AAHC2 AA 06If A is NOT a : then JUMP to 06AAH.
NOTE: 06AAH is in the middle of the KEYBOARD DRIVER ENTRY ROUTINE.
*376FLD A,(409FH)3A 9F 40Otherwise, Load A with the memory contents of 409FH.
NOTE: 409FH is the DATA FLAG.
*3772RRA1FRotate 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.
*3773JP C,06A8HDA A8 06If Bit 7 of the DATA FLAG was set, then JUMP to 06A8H.
NOTE: 06A8H is in the middle of the KEYBOARD DRIVER ENTRY ROUTINE.
*3776RLA17Rotate 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.
*3777JP 06A3HC3 A3 06JUMP 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
*377ALD (IX+05H),05HDD 36 05 00Load the memory location pointed to by IX+5 with a 05H.
NOTE: IX+5 is the number of characters printed.
*377EINC (IX+04H)DD 34 04Bump the number of lines already printed (which is tracked in IX+04H)
*3781LD A,(IX+04H)DD 7E 04Fetch the number of lines already printed (which is tracked in IX+04H) into Register A
*3784CP (IX+03H)DD BE 03Compare the number of lines already printed (held in Register A) against the number of lines left in the page (tracked in IX+03H)
*3787RET NZC0If the number of lines already printed doesn't match the number of lines left in the page then RETURN
*3788LD (IX+04H),04HDD 36 04 01If they DO match, then set the number of lines already printed to 04H
*378CRETC9RETurn 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
*378DLD A,C79LET Register A = Register C.
*378ECP E0HFE E0Compare 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
*3790JP NC,0043HD2 43 00If 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).
*3793CP C0HFE C0If 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
*3795JP C,0063HDA 63 00If 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).
*3798SUB C0HD6 C0If 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.
*379ALD B,00H06 00LET Register B = 00H
*379CLD C,A4FLET Register C = Register A.
*379DLD HL,(4220H)2A 20 42LET Register Pair HL the memory contents of 4220H and 4221H.
NOTE: 4220H-4221H is the destination device nName for ROUTE Routine. 2 Bytes
*37A0ADD HL,BC09LET HL = HL + BC
*37A1LD C,(HL)4ELET Register C = the contents of the memory location pointed to by Register Pair HL.
*37A2JP 0063HC3 63 00JUMP 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
*37A5LD HL,3C00H21 00 3CLoad HL with the memory location for the beginning of the video RAM.
*37A8LD A,(HL)7EPut the character at the screen location stored in HL into A.
*37A9RLCA07Rotate 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.
*37ACRLCA07Rotate 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.
*37ADJR C,37B3H38 04If 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
*37AFLD A,2EH3E 2ELoad Register A with a . which is the character which will print instead of non-printable characters.
*37B3LD A,(HL)7EReplace the non-printable character with a .
*37B4CALL 003BHCD 3B 00Call the PRINT CHARACTER routine at 003B (which sends the character in the A register to the printer).
*37B7INC HL23Bump HL to the next character on the screen.
*37B8BIT 6,HCB 74Check 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).
*37BDLD A,L7DTest of end of line by loading A with L.
*37BEAND 3FHE6 3FAND 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).
*37C0JR NZ,37A8H20 E6If 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
*37A5 - Model 4 Gen 2 - Screen Print Routine BUT exits witH Register A at 0 and all FLAGS cleared
*37C7CALL 37A5HCD A5 37GOSUB to 37A5H for the screen print
*37CAXOR AAFClear Register A and all the flags
*37CBRETC9RETurn to CALLer
*37CC - Model 4 Gen 2 - TOGGLE the CAPS LOCK
*37CCLD A,01H3E 01Load Register A with an 01H
*37CELD HL,4019H21 19 40Load HL with 4019H, which is the CAPS LOCK TOGGLE in the keyboard DCB.
*37D1XOR (HL)AEXOR the contents of the CAPS LOCK TOGGLE in RAM against 01H; this effectively toggles it
*37D2LD (HL),A77Put the toggled CAPS LOCK TOGGLE back into RAM where such thing is stored
*37D3XOR AAFClear Register A and Clear all FLAGs
*37D4RETC9RETurn 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.
*37D5PUSH AFF5Preserve Registers A and F to the top of the STACK
*37D6IN A,(EAH)DB EAPoll the RS-232 UART Control Register/Status Register (via Port EAH) into A.
*37D8BIT 6,ACB 77Test Bit 6 of the RS-232 Register. It will be 1 if READY TO SEND
*37DAJR Z,37D6H28 FAIf the UART reported that READY TO SEND is FALSE, LOOP back to 37D6 and poll again.
*37DCPOP AFF1Restore Registers A and F from the top of the STACK
*37DDOUT (EBH),AD3 EBSend the contents held in Register A to the RS-232C Data Register (via Port E8H) so as to send A to the RS-232C.
*37DFRETC9RETurn 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).
*37E0LD A,6DH3E 6DLoad Register A with a 6DH (Binary: 0110 1101)
*37E2OUT (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)
*37E4JP (HL)E9Jump to whatever (HL) is pointing to.
*37E5 - Model 4 Gen 2 - UNUSED CODE.
*37E5NOP00
*37E6NOP00
*37E7NOP00
*37E8RST 38HFF
*37E9RST 38HFF
**37D5-37E9 - Model 4 ROM Student Network Edition - Replace all the above with NOPs
*37EA - Model 4 Gen 2 - BASIC TIMES (DATE$+" "+TIME$)
*37EARST 10HD7Call the EXAMINE NEXT SYMBOL routine at RST 10H.
*37EBPUSH HLE5Save HL (the current position) to the STACK.
*37ECLD A,11H3E 11Load A with 11H (Decimal: 17) to set up for a 17 Byte String.
*37F1LD HL,(40D4H)2A D4 40Load HL with the memory contents of (40D4H), which is the string pointer.
*37F7LD (HL),20H36 20Load the memory location pointed to by HL with a SPACE.
*37F9INC HL23Increment HL to move 1 character over.
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.
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).
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).
2532 - Binary Minus Routine- "DOMIN"
Note: 40F3H-40F4H is a temporary storage location
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.
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.
254E - This routine processes an expression for SNG(to MID$(- "ISFUN"
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.
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.
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.
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
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.
If the values match, set the current result in zero. If they do not match, set the current result to -1
25C4 - NOTRoutine- "NOTER"
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
Note: 40AFH holds Current number type flag
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
25F7 - ORlogic.
25FD - ANDlogic- "NOTOR"
2603 - Dimension and Variable Searching Routine- "DIMCON"
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.
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:
- If an entry is found, dimflg being on indicates a "doubly dimensioned" variable
- 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.
- When the build entry code finishes, only if dimflg is off will indexing be done
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.
Note: 40AEH holds LOCATE/CREATE variable flag
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.
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.
NOTE:4101H-411AH holds Variable Declaration Table
2652- ↳ HAVTYP
NOTE:40AFH holds Current number type flag
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.
Note: 40DCH holds FORflag
- Note: 40F9H-40FAH holds the starting address of the simple variable storage area.
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.
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.
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.
Note: 40FDH-40FEH holds Free memory pointer
NOTE:40FDH-40FEH holds Free memory pointer
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.
26D5 - This routine is ZERO out all variable types and skip any RETurn- "FINZER"
26D5-26D7- ↳ FINZERLD (4124H),ALD (FAC),A32 24 41Zero ACCumulator so that all single-precision and double-precision variables become zero
26D8POP BCC1Clean up the STACK (i.e., remove the length of the variable)
26D9LD H,A67Zero Register H to clear out integers as well
26DALD L,A6FZero Register L to clear out integers as well
26DB-26DDLD (4121H),HLLD (FACLO),HL22 21 41Zero the string pointer location in ACCumulator
26DERST 20HGETYPEE7We 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-26E0If 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-26E3If 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-26E6LD (4121H),HLLD (FACLO),HL22 21 41Save the value in HL as the current string pointer, which is now null.
26E7- ↳ POPHR2POP HLE1Get the value of the current BASIC program pointer from the STACK and put it in HL
26E8RETC9RETurn (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- ↳ ISARYPUSH HLE5Save the DIMFLG and VALTYP for recursion
26EA-26ECLD HL,(40AEH)LD HL,(DIMFLG)2A AE 40Load 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
26EDEX (SP),HLE3Swap (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
26EELD D,A57Zero Register D (which will hold the number of dimension)
26EF- ↳ INDLOPPUSH DED5Save the number of dimension (held in Register D) to the STACK
26F0PUSH BCC5Save the variable's name in BC to the STACK
26F1-26F3Go 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
26F4POP BCC1Get the variable's name from the STACK (1st and 2nd character) and put it in BC
26F5POP AFF1Get the variable's number of dimension so far from the STACK and put it in Register A
26F6EX DE,HLEBSwap 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
26F7EX (SP),HLE3Swap (SP) and HL so that HL will now hold DIMFLG and VALTYP and the array subscript will be at the stop of the STACK
26F8PUSH HLE5Save the DIMGFLG and VALTYP (in HL) to the STACK
26F9EX DE,HLEBSwap 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.
26FAINC A3CBump the number of dimensions evaluated in Register A
26FBLD D,A57Load Register D with the number of dimensions evaluated in Register A
26FCLD A,(HL)7ELoad Register A with the character at the location of the current BASIC program pointer in HL
26FD-26FECP 2CHFE 2CCheck 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-2700If 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-2702RST 08H ⇒ 29SYNCHK ")"CF 29Since 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- ↳ SUBSOKLD (40F3H),HLLD (TEMP2),HL22 F3 40Save the value of the current BASIC program pointer in HL into the TEMP2 storage area.
2706POP HLE1Get the DIMFLG and VALTYP from the STACK and put it in HL.
2707-2709LD (40AEH),HLLD (DIMFLG),HL22 AE 40Save the value of the DIMFLG and VALTYP into the DIMFLG location in RAM.
NOTE:40AEH holds LOCATE/CREATE variable flag
270APUSH DED5Save 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-270DLD HL,(40FBH)LD HL,(ARYTAB)2A FB 40We 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-270FLD A,19H3E 19Z-80 Trick to skip the next command of ADD HL,DE if falling through
270F- ↳ LOPFDAADD HL,DE19Advance past the current array as we know it isn't the correct one. Note: 40FBH-40FCH holds the array variables pointer
2710EX DE,HLEBSwap DE and HL so that DE holds the current search point. We don't care about HL.
2711-2713LD HL,(40FDH)LD HL,(STREND)2A FD 40Load HL with the place to STOP the search (i.e., the value of the free memory pointer).
Note: 40FDH-40FEH holds Free memory pointer
2714EX DE,HLEBSwap DE and HL so that DE now holds the place to stop the search and HL holds the current search point.
2715RST 18HCOMPARDFNow 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-2718LD A,(40AFH)LD A,(VALTYP)3A AF 40Load Register A with the value of the current number type flag.
Note: 40AFH holds Current number type flag
2719-271AIf 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
271BCP (HL)BECompare 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.
271CINC HL23Bump the value of the array variables pointer in HL
271D-271EJump forward (but still in this loop) to 2727H if the number type flags don't match
271FLD A,(HL)7ELoad Register A with the first character of the variable name at the location of the array variables pointer in HL
2720CP CB9Check 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.
2721INC HL23Bump the value of the array variables pointer in HL
2722-2723Jump forward (but still in this loop) to 2728H if the first characters of the variable names don't match
2724LD A,(HL)7ELoad Register A with the second character of the variable name at the location of the array variables pointer in HL
2725CP BB8Compare 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-2727LD A,23H3E 23This part of a Z-80 Trick. Will load A with 23H if passing through, and not do the following INC HL.
2727- ↳ NMARY2INC HL23Bump the value of the array variables pointer in HL
2728- ↳ NMARY1INC HL23Bump the value of the array variables pointer in HL. HL should now point to the LENGTH entry of the array being looked at
2729LD E,(HL)5ELoad Register E with the LSB of the LENGTH of the array being looked at
272AINC HL23Bump the value of the array variables pointer in HL
272BLD D,(HL)56Load Register D with the MSB of the LENGTH of the array being looked at
272CINC HL23Bump the value of the array variables pointer in HL
272D-272EIf 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-2731LD A,(40AEH)LD A,(DIMFLG)3A AE 40Load 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
2732OR AB7Since 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-2734LD E,12H1E 12Prepare for an error if this routine was NOT called by "DIM" by loading Register E with the ?DD ERRORcode
2735-2737If 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.
2738POP AFF1Get the number of dimension evaluated from the STACK and put it in Register A
2739SUB (HL)96To 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-273CIf they match then we are done so JUMP down to 2795H to read the indices
273D - ?BS ERROR entry point- "BSER"
273D-273E- ↳ BSERLD E,10H1E 10Load Register E with a ?BS ERRORcode
273F-2741Display 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 - Get an index
- Put number+1 down at the VARPTR
- Increase the VARPTR
- Decmrent the number of DIMs
- 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- ↳ NOTFDDLD (HL),A77Save the variable type for the array in Register A at the location of the array variables pointer in HL
2743INC HL23Bump 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)
2744LD E,A5FLoad Register E with the variable type flag for the current variable
2745-2746LD D,00H16 00Zero Register D so that Register Pair DE can be the size of one value of the type VALTYP
2747POP AFF1Get the number of dimensions evaluated from the STACK and put it in Register A
2748LD (HL),C71Save the second character of the variable's name in Register C at the location of the array variables pointer in HL
2749INC HL23Bump 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)
274ALD (HL),B70Save the first character of the variable's name in Register B at the location of the array variables pointer in HL
274BINC HL23Bump the value of the array variables pointer in HL to the LSB of the offset to the next entry
274CLD C,A4FIn preparation for the next CALL, load Register C with the number of two byte entries needed to store the size of each dimension
274D-274FFigure the amount of memory space left between HL and the free memory and get the space needed as set in Register C
2750INC HL23Next 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
2751INC HL23Bump the value of the array variables pointer in HL. These 2 INC's skip over the offset entry
2752-2754LD (40D8H),HLLD (TEMP3),HL22 D8 40Next 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
2755LD (HL),C71Save the number of dimension at the location of the array variables pointer in HL
2756INC HL23Bump the value of the array variables pointer in HL to point to the first subscript entry in the array table
2757-2759LD A,(40AEH)LD A,(DIMFLG)3A AE 40Load 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
275ARLA17Set the CARRY flag accordingly
275BLD A,C79Load Register A with the number of dimension evaluated in Register C
275C-275E- ↳ LOPPTALD BC,000BH01 0B 00Top 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-2760If 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
2761POP BCC1Get a subscript/index from the STACK and put it in BC
2762INC BC03Bump the value of the subscript in BC by one for the ZERO entry.
2763- ↳ NOTDIMLD (HL),C71Top of a loop. Save the LSB of the subscript's value in Register C at the location of the array variables pointer in HL
2764INC HL23Bump the value of the array variables pointer in HL
2765LD (HL),B70Save the MSB of the subscript's value in Register B at the location of the array variables pointer in HL
2766INC HL23Bump the value of the array variables pointer in HL
2767PUSH AFF5Save the number of dimensions evaluated in Register A (and the CARRY aflag results from DIMFLG) to the STACK
2768-276AGo multiply the size of the subscript by the value of the number type flag to determine the amount of memory necessary for the subscript
276BPOP AFF1Get the number of domensions that the CARRY FLAG (DIMFLG) from the STACK and put it in Register A
276CDEC A3DDecrement the counter of the number of dimensions to check by one
276D-276EJump back to 275CH if there are anymore subscripts to be evaluated
276FPUSH AFF5If 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
2770LD B,D42Load Register B with the MSB of the array's length in Register D
2771LD C,E4BLoad Register C with the LSB of the array's length in Register E. Now BC = size of the array in bytes
2772EX DE,HLEBSwap DE and HL so that DE now has the start of the values and HL has the end of the values
2773ADD HL,DE19Add the length of the array in HL to the value of the array variable pointer in DE
2774-2775If that addition triggered the CARRY FLAG then we are out of RAM so JUMP back to 273DH and throw a ?BS ERROR
2776-2778We now know there is room in RAM, so GOSUB to "REASON" to make sure there is room for the values
2779-277BLD (40FDH),HLLD (STREND),HL22 FD 40Update the end of storage pointer with the end of the array (held in HL).
Note: 40FDH-40FEH holds free memory pointer
277C- ↳ ZERITADEC HL2BNow we need to zero the new array. First, decrement the value of the array pointer in HL
277D-277ELD (HL),00H36 00Zero the location of the array pointer in HL
277FRST 18HCOMPARDFNow 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-2781If the NZ FLAG is set, then we have more entries to ZERO, so loop until the array has been cleared
2782INC BC03Load 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
2783LD D,A57Zero Register D
2784-2786LD HL,(40D8H)HL,(TEMP3)2A D8 40Load HL with the array variables pointer (=the number of indices).
Note: 40D8H-40D9H holds Temporary storage location
2787LD E,(HL)5ELoad Register E with the number of dimension for the array at the location of the array variables pointer in HL
2788EX DE,HLEBSwap DE and HL so that HL now holds the number of dimensions and DE holds the value of the array variables pointer
2789ADD HL,HL29Multiply the number of subscripts for the array in HL by two
278AADD HL,BC09Add 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
278BEX DE,HLEBSwap 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
278CDEC HL2BWe 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
278DDEC HL2BDecrement the value of the array variables pointer in HL
278ELD (HL),E73Save the LSB of the size of the array in Register E at the location of the array variables pointer in HL
278FINC HL23Bump the value of the array variables pointer in HL
2790LD (HL),D72Save the MSB of size of the array array in Register D at the location of the array variables pointer in HL
2791INC HL23Bump the value of the array variables pointer in HL
2792POP AFF1Get the value of the DIMFLG (i.e., the CARRY BIT) and a 0 into Register A
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- ↳ GETDEFLD B,A47Zero Register B
2796LD C,A4FZero Register C. Now BC = 0 = CURTOL
2797LD A,(HL)7ELoad Register A with the number of dimensions for the array at the location of the array variables pointer in HL
2798INC HL23Bump the value of the array variables pointer in HL to one entry past the number of dimensions
2799LD D,0E1H16 E1Z-80 Trick to hide the next instruction (POP HL) if proceeding downward
279A- ↳ INLPNMPOP HLE1Get the array variables pointer from the STACK and put it in HL
279BLD E,(HL)5ENext, 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
279CINC HL23Bump the value of the array variables pointer in HL
279DLD D,(HL)56Load Register D with the MSB of the subscript limit at the location of the array variables pointer in HL
279EINC HL23Bump the value of the array variables pointer in HL
279FEX (SP),HLE3Swap 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
27A0PUSH AFF5Save the number of dimensions for the array in Register A to the STACK
27A1RST 18HCOMPARDFNow 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-27A4If the current inex is too big, then we need to throw a ?BS ERROR via a JUMP to 273DH
27A8ADD HL,DE19Add the index to CURTOL
27A9POP AFF1Get the number of dimensions for the array from the STACK and put it in Register A
27AADEC A3DWe checked one, so cross one off the list and see if there are anymore dimensions to be evaluated
27ABLD B,H44Load Register B with the MSB of the subscript pointer in Register H
27ACLD C,L4DLoad Register C with the LSB of the subscript pointer in Register L. Now BC = CURTOL for the start of the next loop.
27AD-27AELoop back to 279AH until all of the subscripts have been evaluated
27AF-27B1LD A,(40AFH)LD A,(VALTYP)3A AF 40Get 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
27B3LD B,H
LD C,L44We are going to need to multiply by THREE, so we need to save the original value into BC as well.
27B4ADD HL,HL29Multiply the subscript pointer in HL by two
27B5-27B6SUB 04HD6 04Check the value of the number type flag in Register A because we would be done if we have an integer or a string.
27B7-27B8Jump forward to 27BDH if the current number type is an integer or string
27B9ADD HL,HL29It 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-27BBJump forward to 27C2H if the current number type is single precision as we then have enough bytes.
27BCADD HL,HL29It must be double precision, so once again multiply the subscript pointer in HL by two (so now it is multiplied by 8)
27BD- ↳ DMLVALOR AB7Set the flags
27BE-27C0Jump forward to 27C2H if the current number type isn't a string
27C1ADD HL,BC09The current number type is a string so add the value of the original subscript pointer in BC to the subscript pointer in HL
27C2- ↳ DONMULPOP BCC1Get the value of the array variables pointer from the STACK and put it in BC
27C3ADD HL,BC09Add 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
27C4EX DE,HLEBLoad DE with the variable pointer in HL
27C5-27C7- ↳ FINNNOWLD HL,(40F3H)LD HL,(TEMP2)2A F3 40Get the value of the current BASIC program pointer and put it in HL.
Note: 40F3H-40F4H is a temporary storage location
27C8RETC9RETurn 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- ↳ MEMXOR AAFZero Register A and the status flags
27CAPUSH HLE5Save the value of the current BASIC program pointer in HL to the STACK
27CB-27CDLD (40AFH),ALD (VALTYP),A32 AF 40Zero the number type flag.
NOTE:40AFH holds Current number type flag
27CE-27D0Determine how much space there is via a GOSUB to the FREroutine at 27D4H
27D1POP HLE1Get the value of the current BASIC program pointer from the STACK and put it in HL
27D2RST 10HCHRGETD7We 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.
27D3RETC9Return to BASIC
27D4-27F4 - LEVEL II BASIC FREROUTINE- "FRE"
27D4-27D6- ↳ FRELD HL,(40FDH)LD HL,(STREND)2A FD 40Load 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
27D7EX DE,HLEBLoad DE with the value of the free memory pointer in HL for subtraction
27DBADD HL,SP39Add the value in HL (which is zero) to the current value of the STACK pointer so that the STACK pointer is now in HL
27DCRST 20HGETYPEE7We 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-27DEIf that test shows we do NOT have a STRING (meaning this was really a MEMcall, jump to forward to 27ECH
27DF-27E1Free up the argument and set up to give some free string space
27E5-27E7LD HL,(40A0H)LD HL,(STKTOP)2A A0 40Load HL with the start of string space pointer / bottom of free space.
NOTE:40A0H-40A1H holds the start of string space pointer
27E8EX DE,HLEBLoad DE with the start of string space pointer in HL
27E9-27EBLD HL,(40D6H)LD HL,(FRETOP)2A D6 40Load 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- ↳ GIVDBLLD A,L7DPrepare to do HL = HL - DE. First, load Register A with the LSB of the next available location in string space pointer in Register L
27EDSUB E93Subtract 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
27EELD L,A6FLoad Register L with the LSB of the amount of string space remaining in Register A
27EFLD A,H7CLoad Register A with the MSB of the next available location in string space pointer in Register H
27F0SBC A,D9ASubtract 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
27F1LD H,A67Load Register H with the MSB of the amount of string space remaining in Register A
27F2-27F4Jump 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- ↳ POSLD A,(40A6H)LD A,(TTYPOS)3A A6 40Load Register A with the current cursor line position.
Note: 40A6H holds the current cursor line position
27F8- ↳ SNGFLTLD L,A6FLoad Register L with the value of the current cursor line position in Register A.
27F9XOR AAFZero Register A
27FA- ↳ GIVINTLD H,A67Load Register H with zero, so now HL is 00 + cursor position
27FE-2818 - LEVEL II BASIC USR(x)ROUTINE- "USRFN"
27FE-2780- ↳ USRFNGOSUB to DOS to see if DOS wants to deal with this
2801RST 10HCHRGETD7We 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-2804GOSUB to 252CH to evaluate the expression at the location of the current BASIC program pointer in HL and return with the result in ACCumulator
2805PUSH HLE5Save the value of the current BASIC program pointer in HL (=the address of the next element in the code string) to the STACK
2806-2808Load HL with the return address of 0890H which will clear the STACK before returning to BASIC
2809PUSH HLE5Save the value of the return address in HL to the STACK
280A-280CLD A,(40AFH)LD A,(VALTYP)3A AF 40Load Register A with the value of the current number type flag for the argument provided.
Note: 40AFH holds Current number type flag
280DPUSH AFF5Save the value of the current number type flag in Register A.
(02=INT, 03=STR, 04=SNG, 08=DBL)
280E-280FCP 03HFE 03Check 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-2812If the current result is a string then GOSUB to 29DAH to get the free the space and get the string address into HL
2813POP AFF1Restore the number type flag into Register A
2814EX DE,HLEBLoad DE with the (possible) value of the pointer to the string (in HL)
2815-2817LD HL,(408EH)2A 8E 40Load 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.
2818JP (HL)E9Jump 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- ↳ DOCNVFPUSH HLE5Save the pointer to the current character in the BASIC program being evaluated to the STACK
281A-281BAND 07HE6 07Mask 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-281ELoad HL with the address of the arithmetic conversion routines
281FLD C,A4FLoad Register C with the value of the number type flag in Register A.
(02=INT, 03=STR, 04=SNG, 08=DBL)
2820-2821LD B,00H06 00Zero Register B. Now BC holds 00 + type and will be the two byte offset into the table
2822ADD HL,BC09Add the offset (of BC) to the base arithmetic conversion routines, to find the right jump point
2823-2825GOSUB to 2586H to convert the current result in REG l to its proper number type
2826POP HLE1Restore the pointer to the current character in the BASIC program being evaluated to HL
2827RETC9RETurn 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- ↳ ERRDIRPUSH HLE5Save whatever was in HL to the STACK
2829-282BLD HL,(40A2H)LD HL,(CURLIN)2A A2 40Load HL with the value of the current BASIC line number (which is stored at 40A2H-40A3H).
282CINC HL23Bump 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
282DLD A,H7CLoad Register A with the MSB of the current BASIC line number in Register H
282EOR LB5Combine 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.
282FPOP HLE1Restore whatever was in HL on entry back into HL
2830RET NZC0Return 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-2832LD E,16H1E 16Load Register E with the ?ID ERRORcode
2836-2856 - STRING ROUTINE - STR$logic- "STR$"
2836-2838- ↳ STR$GOSUB to 0FBDH to convert the current result in ACCumulator to an ASCII string
2839-283B- ↳ STR$1Scan it and turn it into a string (make a temporary string work area entry)
283F-2841Load BC with a return address of 2A2BH (which cleans the STACK and then jumps to 2884H)
2842PUSH BCC5Save 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- ↳ STRCPYLD A,(HL)7ELoad Register A with the string's length at the location of the string's VARPTR in HL
2844INC HL23Bump the value of the string's VARPTR in HL
2845PUSH HLE5Save the value of the string's VARPTR in HL to the STACK
2846-2848GOSUB to 28BFH to test the remaining string area to make sure that the new string will fit
2849POP HLE1Reload HL with the string's VARPTR. This is the destination to where the string should be copied.
284ALD C,(HL)4ELoad Register C with the LSB of the string's address at the location of the string's VARPTR in HL
284BINC HL23Bump the value of the string's VARPTR in HL
284CLD B,(HL)46Load Register B with the MSB of the string's address at the location of the string's VARPTR in HL
284D-284FGOSUB to 285AH to save the string's length and the string's address at 40D3H, so as to set up DSCTMP
2850PUSH HLE5Save the pointer to STRAD2 (which is 40D3H) to the STACK
2851LD L,A6FLoad Register L with the string's length (from Register A)
2852-2854GOSUB to 29CEH to move L characters from the temp area (of BC) to the string data area (in DE)
2855POP DED1Restore the pointer to DSCTMP (40D3H) into Register Pair DE
2856RETC9RETurn to CALLer
2857-2864 - STRING ROUTINE- "STRINI"
2857-2859- ↳ STRINIGOSUB 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- ↳ STRAD2Load HL with the address of the temporary string parameter storage area.
Note: 40D3H-40D5H holds Used for temporary string VARPTR's
285D- ↳ STRAD1PUSH HLE5Save the address of the temporary string parameter area in HL to the STACK
285ELD (HL),A77Save the string's length in Register A at the location of the temporary string parameter storage pointer in HL
285F- ↳ PUTDEIINC HL23The 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
2860LD (HL),E73Save the LSB of the string's address in Register E at the location of the temporary string parameter storage pointer in HL
2861INC HL23Bump the value of the temporary string parameter storage pointer in HL
2862LD (HL),D72Save the MSB of the string's address in Register D at the location of the temporary string parameter storage pointer in HL
2863POP HLE1Get the address of the temporary string parameter storage area from the STACK and put it in HL
2864RETC9RETurn 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- ↳ STRLITDEC HL2BDecrement the value of the current BASIC program pointer in HL
2866-2867- ↳ STRLTILD B,22H06 22Load Register B with a "(which is really the end of the quote search character)
2868- ↳ STRLT3LD D,B50Load Register D with a "(which is really the ending search character)
2869- ↳ STRLT2PUSH HLE5Save 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-286BLD C,0FFH0E FFLoad Register C with a -1
286C- ↳ STRGETINC HL23Bump the value of the current BASIC program pointer in HL to skip over that initial "
286DLD A,(HL)7ELoad Register A with the character at the location of the current BASIC program pointer in HL
286EINC C0CBump the counter in Register C
286FOR AB7Check 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-2871Jump 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
2872CP DBACheck 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-2874Jump 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
2875CP BB8Check 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-2877Loop 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- ↳ STRFINCP 22HFE 22Check 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-287CIf 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)
287DEX (SP),HLE3Exchange the value of the current BASIC program pointer in HL with the string's address to the STACK
287EINC HL23Bump the string's address in HL until it points to the first character of the string
287FEX DE,HLEBLoad DE with the temporary pointer string's address in HL
2880LD A,C79Load Register A with the string's length from Register C
2881-2883GOSUB to 285AH to save the string's length and the string's address into 40D3H (i.e., DSCTMP)
2884-2886- ↳ PUTNEWLoad DE with the address of the string parameter storage area.
Note: 40D3H-40D5H holds Used for temporary string VARPTR's
2887-2888LD A,D5H3E D5This 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- ↳ PUTTMPPUSH DED5Save a pointer to the stat of the string to the STACK
2889-288BLD HL,(40B3H)LD HL,(TEMPPT)2A B3 40Load 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-288ELD (4121H),HLLD (FACLO),HL22 21 41Save 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-2890LD A,03H3E 03Load Register A with the string number type flag
2891-2893LD (40AFH),ALD (VALTYP),A32 AF 40Save the value in Register A as the current number type flag.
Note: 40AFH holds current number type flag
2897-2899Depending on how we got here, DE will be different. If the jump was into PUTTMP, then DE will NOT equal FRETOP.
289ARST 18HCOMPARDFWe 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-289DLD (40B3H),HLLD (TEMPPT),HL22 B3 40Save 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
289EPOP HLE1Get the value of the current BASIC program pointer from the STACK and put it in HL
289FLD A,(HL)7ELoad Register A with the character at the location of the current BASIC program pointer in HL
28A0RET NZC0Return 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-28A2LD E,1EH1E 1ELoad Register E with a ?ST ERRORcode
28A3-28A5Display 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- ↳ STROUIINC HL23Bump 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"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- ↳ STRPRTGOSUB to 29DAH to build a temporary string work area entry for the message pointed to by FACLO
28AD-28AFGo get the string's length in Register D and the string's address in BC
28B0INC D14Bump the value of the string's length in Register D in preparation for the following loop which starts with a DEC D
28B1- ↳ STRPR2DEC D15Top of a loop. Decrement the value of the string's length in Register D
28B2RET ZC8Return if all of the characters in the string have been sent to the current output device.
28B3LD A,(BC)0ALoad Register A with the character at the location of the string pointer in BC
28B4-28B6Go send the character in Register A to the current output device
28B7-28B8CP 0DHFE 0DCheck 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-28BBJump to 2103H if the character in Register A is a carriage return
28BCINC BC03Bump the value of the string pointer in BC
28BD-28BELoop 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- ↳ GETSPAOR AB7Make sure A is not zero. A ZERO FLAG will signal that garbage collection has happened
28C0-28C1LD C,0F1H0E F1Z-80 Trick. If passing through, the C just changes and the POP AF which follows is ignored.
28C1- ↳ TRYGI2POP AFF1Get the string's length from the STACK and put it in Register A
28C2PUSH AFF5Save the length of the string in Register A to the STACK
28C3-28C5LD HL,(40A0H)LD HL,(STKTOP)2A A0 40Load HL with the poinmter to the bottom of the string space. 40A0H-40A1H holds the start of string space pointer
28C6EX DE,HLEBMove the bottom of the string space pointer into DE. We don't care what happens to HL
28C7-28C9LD HL,(40D6H)LD HL,(FRETOP)2A D6 40Load HL with the pointer to the TOP of free space.
Note: 40D6H-40D7H holds the next available location in string space pointer
28CACPL2FComplement the string's length in Register A so that it is negative
28CBLD C,A4FThe 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-28CDLD B,0FFH06 FFLoad Register B with a -1 so that BC will be the negative length of the string
28CEADD HL,BC09Add the negative string's length in BC to the top of free space pointer in HL
28CFINC HL23Bump the value of the adjusted next available location in string space pointer in HL
28D0RST 18HCOMPARDFWe 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-28D2If 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-28D5LD (40D6H),HLLD (FRETOP),HL22 D6 40Save the value in HL as new bottom of MEMORY
28D6INC HL23Bump the value of HL to point to the string
28D7EX DE,HLEBLoad DE with the pointer to the string
28D8- ↳ PPSWRTPOP AFF1Get the string's length from the STACK and put it in Register A
28D9RETC9RETurn to CALLer
28DA-298E - STRING ROUTINE- "GARBAG"
28DA- ↳ GARBAGPOP AFF1Get the garbage collection code which was PUSHed
28DB-28DCLD E,1AH1E 1ALoad Register E with an ?OS ERRORcode
28DD-28DFIf 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
28E0CP ABFOtherwise, 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.
28E1PUSH AFF5Save the garbage collection flag to the STACK
28E2-28E4Load BC with a return address of 28C1H (which would retry allocation)
28E5PUSH BCC5Save that return address to the STACK
28E6-28E8- ↳ GARBA2LD HL,(40B1H)LD HL,(MEMSIZ)2A B1 40Load HL with the top of memory pointer.
Note: 40B1H-40B2H holds MEMORY SIZE? pointer
28E9-28EB- ↳ FNDVARLD (40D6H),HLLD (FRETOP),HL22 D6 40Save 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
28EFPUSH HLE5Save the value in HL to the STACK to indicate that we didn't find a variable on this pass
28F0-28F2LD HL,(40A0H)LD HL,(STKTOP)2A A0 40Load 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
28F3PUSH HLE5Save high address (start of string space pointer in HL) to the STACK
28F4-28F6Load HL with the start of the temporary string work area pointer.
Note: 40B5H-40D2H holds Temporary string work area
28F7
"TVAR"EX DE,HLEBLoad DE with the start of the temporary string work area pointer in HL
28F8-28FALD HL,(40B3H)LD HL,(TEMPPT)2A B3 40Load 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
28FBEX DE,HLEBExchange 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
28FCRST 18HCOMPARDFWe 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.
2900-2902Jump to 294AH to do the temporary variable garbage collection if the temporary string work area isn't empty
2903- ↳ SVARSLD HL,(40F9H)LD HL,(VARTAB)2A F9 40Load HL with the start of the simple variables pointer.
NOTE:40F9H-40FAH holds the starting address of the simple variable storage area
2906- ↳ SVAREX DE,HLEBLoad DE with the simple variables pointer in HL
2907-2909LD HL,(40FBH)LD HL,(ARYTAB)2A FB 40Load 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
290AEX DE,HLEBSwap DE and HL so that DE holds the end and HL holds the start.
290BRST 18HCOMPARDFNow 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-290DIf 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.
290ELD A,(HL)7ELoad Register A with second character of the variable name
290FINC HL23Bump the value of the simple variables pointer in HL
2910INC HL23Bump the simple variables pointer in HL
2911INC HL23Bump the value of the simple variables pointer in HL. HL should now point to the value of the variable.
2912-2913CP 03HFE 03Check 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-2915Jump to 291AH if the variable at the location of the simple variables pointer in HL isn't a string
2919XOR AAFZero Register A to indicate that we should not be skipping anything else.
291A- ↳ SKPVARLD E,A5FZero Register E
291B-291CLD D,00H16 00Zero Register D. Now DE should be the number of characters to skip.
291DADD HL,DE19Add the number of characters to skip (held in DE) to the simple variables pointer in HL
2920- ↳ ARYVA2POP BCC1Clean up the STACK
2921- ↳ ARYVAREX DE,HLEBLoad DE with the value of the array variables pointer (ARTVAR) in HL
2922-2924LD HL,(40FDH)LD HL,(STREND)2A FD 40Load HL with the end of the arrays / start of free memory pointer.
Note: 40FDH-40FEH holds Free memory pointer
2925EX DE,HLEBExchange the value of the array variables pointer in DE with the value of the free memory pointer in HL
2926RST 18HCOMPARDFWe 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-2929Jump if the array variables pointer in Register HL is the same as the start of the free memory pointer in DE
292ALD A,(HL)7ELoad Register A with the number type flag at the location of the array variables pointer in HL
292BINC HL23Bump the value of the array variables pointer in HL
292C-292EGet 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)
292FPUSH HLE5Save the pointer to the DIMS to the STACK
2930ADD HL,BC09Add the value of the offset to the next array in BC to the value of the array variables pointer in HL
2931-2932CP 03HFE 03Check 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-2934If the array being examined isn't a string then skip it via a JUMP to 2920H
2935-2937LD (40D8H),HLLD (TEMP3),HL22 D8 40Save the address of the end of the array being examined.
Note: 40D8H-40D9H holds Temporary storage location
2938POP HLE1Get the value of the array variables pointer from the STACK and put it in HL
2939LD C,(HL)4ELoad Register C with the number of subscripts for the array at the location of the array variables pointer in HL
293A-293BLD B,00H06 00Zero Register B so that BC holds the number of dimensions
293CADD HL,BC09Add the number of subscripts in the array in BC to the value of the array variables pointer in HL
293DADD HL,BC09Add 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.
293EINC HL23Bump the value of the array variables pointer in HL one more time to account for #DIMs.
293F- ↳ ARYSTREX DE,HLEBLoad DE with the value of the current position in the array variables pointer in HL
2940-2942LD HL,(40D8H)LD HL,(TEMP3)2A D8 40Load HL with the address of the end of this array.
Note: 40D8H-40D9H holds Temporary storage location
2943EX DE,HLEBSwap DE and HL so that HL now points to the current position
2944RST 18HCOMPARDFWe 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-2946If the Z FLAG is set then we are at the end of an array so JUMP to 2921H to try the next array
294A- ↳ DVAR2PUSH BCC5Save the value in BC to the STACK
294B- ↳ DVAR and DVARSXOR AAFZero Register A so we can test for a null string
294COR (HL)B6Load Register A with the length of the string at the location of the array variables pointer in HL
294DINC HL23Bump the value of the array variables pointer in HL
294ELD E,(HL)5ELoad Register E with the LSB of the string's address at the location of the array variables pointer in HL
294FINC HL23Bump the value of the array variables pointer in HL
2950LD D,(HL)56Load 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.
2951INC HL23Bump the value of the array variables pointer in HL
2952RET ZC8Return if the string's length in Register A is equal to zero
2953
2954LD B,H
LD C,L44Let BC = HL
2955-2957LD HL,(40D6H)LD HL,(FRETOP)2A D6 40Load HL with the location of the the top of string free space.
NOTE:40D6H-40D7H holds the next available location in string space pointer
2958RST 18HCOMPARDFWe 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
295ALD H,B
LD L,C60Let HL = BC
295BRET CD8If 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
295CPOP HLE1Get the return address from the STACK and put it in HL
295DEX (SP),HLE3Swap (SP) and HL so that the return address is back on the STACK and HL holds the maximum number seen
295ERST 18HCOMPARDFNow 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.
295FEX (SP),HLE3Swap (SP) and HL so that the return address is back in HL and the STACK holds the maximum number seen
2960PUSH HLE5Save the return address in HL to the STACK
2961
2962LD H,B
LD L,C60Let HL = BC
2963RET NCD0Return if the string's address in DE is below the string space pointer
2964POP BCC1Get the return address from the STACK and put it in BC
2965POP AFF1Clean up the STACK (remove the MAX SEEN)
2966POP AFF1Clean up the STACK (remove the VARIABLE POINTER)
2967PUSH HLE5Save the value of the array variables pointer in HL to the STACK
2968PUSH DED5Save the new MAX pointer in DE to the STACK
2969PUSH BCC5Save the value of the return address in BC to the STACK
296ARETC9RETurn to CALLer
If we are here, we have made one complete pass through the string variables.
296B- ↳ GRBPASPOP DED1Load DE with the address of the last string put into the temporary string work area (aka the MAX pointer)
296CPOP HLE1Get the variable pointer from the STACK and put it in HL
296DLD A,L7DLoad Register A with the LSB of the variable pointer in Register L
296EOR HB4Combine 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
296FRET ZC8If HL=0 then we are at the end of the garbage collection, so return
2970DEC HL2BDecrement the value of the temporary string work area pointer in HL, currently just past the string descriptor
2971LD B,(HL)46Load Register B with the MSB of the string's address at the location of the temporary string work area pointer in HL
2972DEC HL2BDecrement the value of the temporary string work area pointer in HL
2973LD C,(HL)4ELoad 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.
2974PUSH HLE5Save 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.
2975DEC HL2BDecrement the value of the temporary string work area pointer in HL
2976LD L,(HL)6ELoad Register L with the string's length at the location of the temporary string work area pointer in HL
2977-2978LD H,00H26 00Zero Register H so that HL is now the string's character count
2979ADD HL,BC09Add the length of the string in HL to the string's address in BC. HL now points just beyond the string.
297ALD D,B50Load Register D with the MSB of the string's address in Register B
297BLD E,C59Load Register E with the LSB of the string's address in Register C. DE now is the original pointer to the string.
297CDEC HL2BDecrement the value of the string's ending address in HL to avoid moving one beyond the string
297DLD B,H44Load Register B with the MSB of the string's ending address in Register H
297ELD C,L4DLoad Register C with the LSB of the string's ending address in Register L. BC now points to the top of the string
297F-2981LD HL,(40D6H)LD HL,(FRETOP)2A D6 40Load HL with the top of free space.
NOTE:40D6H-40D7H holds the next available location in string space pointer
2982-2984Move the string from the temporary storage location to string space
2985POP HLE1Get back the pointer to the description of the variable from the STACK and put it in HL
2986LD (HL),C71Save the LSB of the string's permanent address (held in Register C) to (HL)
2987INC HL23Bump the value of the temporary string work area pointer in HL
2988LD (HL),B70Save the MSB of the string's permanent address (held in Register C) to (HL)
2989LD L,C69Load Register L with the LSB of the string's address in Register C
298ALD H,B60Load 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.
298BDEC HL2BDecrement the string's address in HL to adjust FRETOP
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- ↳ CATPUSH BCC5Save the precedence/operator value in BC to the STACK
2990PUSH HLE5Save the value of the current BASIC program pointer in HL to the STACK
2991-2993LD HL,(4121H)LD HL,(FACLO)2A 21 41Load HL with the first string's VARPTR (from ACCumulator)
2994EX (SP),HLE3Exchange the value of the first string's VARPTR in HL with the value of the current BASIC program to the STACK
2995-2997GOSUB to 249FH to evaluate the expression at the location of the current BASIC program pointer in HL
2998EX (SP),HLE3Exchange the value of the current BASIC program pointer in HL with the value of the first string's VARPTR to the STACK
299CLD A,(HL)7ELoad Register A with the first string's length at the location of the first string's VARPTR in HL
299DPUSH HLE5Save the value of the first string's descriptor (VARPTR) in HL to the STACK
299E-29A0LD HL,(4121H)LD HL,(FACLO)2A 21 41Load HL with the second string's VARPTR in ACCumulator
29A1PUSH HLE5Save the second string's VARPTR in HL to the STACK
29A2ADD A,(HL)86Add 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-29A4LD E,1CH1E 1CLoad Register E with a ?LS ERRORcodein case the two string lengths are not less than 256.
29A5-29A7Display a ?LS ERRORmessage if the combined lengths of the strings is greater than 255
29A8-29AAGo make sure that there is enough string space for the new string
29ABPOP DED1Get the second string's VARPTR from the STACK and put it in DE
29AFEX (SP),HLE3Exchange the second string's VARPTR in HL with the first string's VARPTR to the STACK
29B3PUSH HLE5Save the value of the first string's VARPTR in HL to the STACK
29B4-29B6- ↳ INCSTRLD HL,(40D4H)LD HL,(DSCTMP+1)2A D4 40Load HL with the pointer to the first string's address
29B7EX DE,HLEBLoad DE with the first string's address in HL
29C1EX (SP),HLE3Exchange the value of the return address in HL with the value of the current BASIC program pointer to the STACK
29C2PUSH HLE5Save the value of the current BASIC program pointer in HL to the STACK
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- ↳ MOVINSPOP HLE1Load HL with the value of the return address to the STACK
29C7EX (SP),HLE3Swap (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.
29C8LD A,(HL)7ELoad Register A with the string's length at the location of the string's VARPTR in HL
29C9INC HL23Bump the value of the string's VARPTR in HL
29CALD C,(HL)4ELoad Register C with the LSB of the string's address at the location of the string's VARPTR in HL
29CBINC HL23Bump the value of the string's VARPTR in HL
29CCLD B,(HL)46Load 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.
29CDLD L,A6FLoad Register L with the string's length in Register A
29CE- ↳ MOVSTRINC L2CIncrement the value of the string's length in Register L in preparation for the next instruction which is a loop.
29CF- ↳ MOVLPDEC L2DDecrement the value of the string's length in Register L
29D0RET ZC8If L hits zero then we have moved all the characters, so RETurn
29D1LD A,(BC)0AIf 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
29D2LD (DE),A12Save the character in Register A at the location of the string storage pointer in DE
29D3INC BC03Bump the value of the string pointer in BC
29D4INC DE13Bump the value of the string storage pointer in DE
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- ↳ FRESTRGOSUB to 0AF4H to make sure that the current result in REG l is a string
29DA-29DC- ↳ FREFACLD HL,(4121H)LD HL,(FACLO)2A 21 41Load HL with the string's VARPTR in ACCumulator
29DD- ↳ FRETM2EX DE,HLEBLoad DE with the value of the string's VARPTR in HL
29DE-29E0- ↳ FRETMPCheck to see if the string is the last entry in the temporary string work area
29E1EX DE,HLEBLoad HL with the value of the string's VARPTR in DE
29E2RET NZC0Return if the string isn't the last entry in the temporary string work area
29E3PUSH DED5Save the value of the string's VARPTR in DE to the STACK
29E4LD D,B50Load Register D with the MSB of the string's address in Register B
29E5LD E,C59Load Register E with the LSB of the string's address in Register C. DE now points to the string.
29E6DEC DE1BDecrement the value of the string's address in DE
29E7LD C,(HL)4ELoad Register C with the string's length at the location of the string's VARPTR in HL
29E8-29EALD HL,(40D6H)LD HL,(FRETOP)2A D6 40Load 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
29EBRST 18HCOMPARDFWe 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-29EDJump 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
29EELD B,A47If 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
29EFADD HL,BC09Add the length of the string in BC to the next available location in string space pointer in HL
29F0-29F2LD (40D6H),HLLD (FRETOP),HL22 D6 40Save the adjusted next available location in string space pointer in HL.
Note: 40D6H-40D7H holds the next available location in string space pointer
29F3- ↳ NOTLSTPOP HLE1Get the string's VARPTR from the STACK and put it in HL
29F4RETC9RETurn 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- ↳ FRETMSLD HL,(40B3H)LD HL,(TEMPPT)2A B3 40Load HL with the temporary string work area pointer.
Note: 40B3H-40B4H holds the next available location in the temporary string work area pointer
29F8DEC HL2BDecrement the value of the temporary string work area pointer in HL which backs up two words
29F9LD B,(HL)46Load Register B with the MSB of the string's address at the location of the temporary string work area pointer in HL
29FADEC HL2BDecrement the value of the temporary string work area pointer in HL
29FBLD C,(HL)4ELoad Register C with the LSB of the string's address at the location of the temporary string work area pointer in HL
29FCDEC HL2BDecrement the value of the temporary string work area pointer in HL
29FDRST 18HCOMPARDFWe 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.
29FERET NZC0If the Z FLAG is set then we are done freeing space, so RETURN
29FF-2A01LD (40B3H),HLLD (TEMPPT),HL22 B3 40Save 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
2A02RETC9RETurn to CALLer
2A03-2A0E - LEVEL II BASIC LENROUTINE- "LEN"
2A06PUSH BCC5Save the return address of 27F8H (in BC) to the STACK
2A07-2A09- ↳ LEN1GOSUB to 29D7H to free up the temporary variable pointed to by FACLO
2A0AXOR AAFZero Register A so as to force a numeric flag
2A0BLD D,A57Zero Register D so that DE can be used when E is set.
2A0CLD A,(HL)7ELoad Register A with the string's length at the location of the string's VARPTR in HL
2A0DOR AB7Set the flags according to the string's length in Register A
2A0ERETC9RETurn to CALLer
2A0F-2A1E - LEVEL II BASIC ASCROUTINE- "ASC"
2A12PUSH BCC5Save the return address of 27F8H (in BC) to the STACK
2A13-2A15- ↳ ASC2GOSUB 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-2A18If the Z FLAG is set, then this was a null string (bad argument), so display a ?FC ERROR
2A19INC HL23Bump the value of the string's VARPTR in HL
2A1ALD E,(HL)5ELoad Register E with the LSB of the address of the string's data at the location of the string's VARPTR in HL
2A1BINC HL23Bump the value of the string's VARPTR in HL
2A1CLD D,(HL)56Load Register D with the MSB of the address of the string's data at the location of the string's VARPTR in HL
2A1DLD A,(DE)1ALoad Register A with the first character at the location of the string pointer in DE
2A1ERETC9RETurn to CALLer
2A1F-2A20- ↳ CHR$LD A,01H3E 01Load Register A with the length of the string to be created
2A21-2A23GOSUB to 2857H to save the string's length in Register A and value and set up the string's address
2A24-2A26GOSUB 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- ↳ SETSTRLD HL,(40D4H)LD HL,(DSCTMP+1)2A D4 40Load HL with the temporary string's address
2A2ALD (HL),E73Save the character in Register E at the location of the string pointer in HL
2A2B- ↳ FINBCKPOP BCC1Clean up the STACK so that the RETURN address is another one that is next in the STACK.
2A2F-2A60 - LEVEL II BASIC STRING$ROUTINE- "STRNG$"
2A2FRST 10HCHRGETD7We 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-2A31RST 08H ⇒ 28SYNCHK "("CF 28Since 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-2A34This 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
2A34DEC HL2BBackspace the code string. This is a Z-80 trick because this code was part of the above call instruction
2A35PUSH DED5Save the string's length ("N") (currently in DE) to the STACK
2A36-2A37RST 08 ⇒ 2CSYNCHK ","CF 2CNow 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-2A3ANow 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-2A3CRST 08H ⇒ 29SYNCHK ")"CF 29Now 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).
2A3DEX (SP),HLE3We 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
2A3EPUSH HLE5Save the string's length ("N") in HL to the STACK so that we can test it to make sure it is an integer
2A3FRST 20HGETYPEE7We 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.
2A42-2A44We 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-2A46Skip the next instruction (which would load the string address and 1st character) by jumping to 2A4AH
2A47-2A49- ↳ STRSTRGo get the first character in the string and return with it in Register A. This is the character that will be repeated
2A4A- ↳ CALSPAPOP DED1Get the "N" from the STACK and put it in DE (well, actually, Register E)
2A4BPUSH AFF5Save the character for the string (held in Register A) to the STACK
2A4C- ↳ SPACE2PUSH AFF5and then save it to the STACK again
2A4DLD A,E7BLoad Register A with "N" (held in Register E)
2A4E-2A4FGOSUB to 2857H to allocate N bytes in the temporary string work area
2A51LD E,A5FLoad Register E with "N" (held in Register A)
2A52POP AFF1Get the character for the string ("X") from the STACK and put it in Register A
2A53INC E1CTo set the status flags we need to increase and then decrease E. First, bump the value of the string's length in Register E
2A54DEC E1D. and then decrement the string's length in Register E
2A55-2A56If "N" was zero (so that the string is now complete), jump back to 2A2BH
2A57-2A59LD HL,(40D4H)LD HL,(DSCTMP+1)2A D4 40If 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),A77Save the character in Register A ("X") at the location of the string pointer in HL
2A5BINC HL23Bump the value of the string pointer in HL
2A5CDEC E1DDecrement the string's length in Register E
2A5D-2A5EJR NZ,2A5AH20 FBLoop back to 2A5AH to keep filling (HL) until the string of X's has been completed
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$Go check the syntax. The character at the location of the current BASIC program pointer in HL must be a )
2A64XOR AAFZero Register A because the string pointer never changes
2A65- ↳ LEFT3EX (SP),HLE3Exchange the value of the current BASIC program pointer in HL with the string's VARPTR to the STACK
2A66LD C,A4FZero Register C
2A67-2A68LD A,0E5H3E E5Z-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- ↳ LEFT2PUSH HLE5Save the value of the string's VARPTR in HL to the STACK
2A6ALD A,(HL)7ELoad Register A with the string's length at the location of the string's VARPTR in HL
2A6BCP BB8Check 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-2A6DJump to 2A70H if the new string's length in Register B is greater than the string's length in Register A
2A6ELD A,B78Load Register A with the new string's TRUNCATED length in Register B
2A6F-2A71LD DE,000EH11 0E 00Z-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
2A72PUSH BCC5Save the offset in BC to the STACK
2A76POP BCC1Get the offset from the STACK and put it in BC
2A77POP HLE1Get the string's VARPTR from the STACK and put it in HL
2A78PUSH HLE5Save the string's VARPTR in HL to the STACK
2A79INC HL23Bump HL to now point to the address of the string
2A7ALD B,(HL)46Load Register B with the LSB of the string's address at the location of the string's VARPTR in HL
2A7BINC HL23Bump the value of the string's VARPTR in HL
2A7CLD H,(HL)66Load Register H with the MSB of the string's address at the location of the string's VARPTR in HL
2A7DLD L,B68Load Register L with the LSB of the string's address in Register B
2A7E-2A7FLD B,00H06 00Zero Register B so that BC can be used
2A80ADD HL,BC09Add the string's length in BC to the string's address in HL
2A81LD B,H44Load Register B with the MSB of the string's ending address in Register H
2A82LD C,L4DLoad Register C with the LSB of the string's ending address in Register L
2A83-2A85Go save the string's length (held in A) and the string's starting address (held in DE)
2A86LD L,A6FLoad Register L with the string's length (i.e., the number of characters to move) held in Register A
2A8APOP DED1Clean up the STACK
2A91-2A99 - LEVEL II BASIC RIGHT$ ROUTINE- "RIGHT$"
2A91-2A93Go check the syntax. The character at the location of the current BASIC program pointer in HL must be a )
2A94POP DED1Get the string's VARPTR from the STACK and put it in DE
2A95PUSH DED5Save the string's VARPTR in DE to the STACK
2A96LD A,(DE)1ALoad Register A with the string's length (held at the location of the string's VARPTR in DE)
2A97SUB B90Subtract the new string's length in Register B from the string's length in Register A to isolate the number of bytes
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,HLEBLoad HL with the value of the current BASIC program pointer (held in DE)
2A9BLD A,(HL)7ELoad Register A with the terminal character, currently held at the location of the current BASIC program pointer in HL
2A9C-2A9EGOSUB to 2AE2H to get the offset in Register B and the string's VARPTR in DE
2A9FINC B04We 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
2AA0DEC B05... and then we decrement the value of the string's offset position in Register B
2AA4PUSH BCC5Save the value of the offset position (held in Register B) to the STACK
2AA5-2AA6LD E,0FFH1E FFLoad Register E with the default string's length of 256 in case no number of bytes are given
2AA7-2AA8CP 29HCP ")"FE 29More 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-2AAAJump to 2AB0H if the character at the location of the current BASIC program pointer in Register A is a )
2AAB-2AACRST 08H ⇒ 2CSYNCHK ","CF 2CIf 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-2AAFGo 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-2AB1RST 08H ⇒ 29SYNCHK ")"CD 29Since 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).
2AB2POP AFF1Get the offset from the STACK and put it in Register A
2AB3EX (SP),HLE3Exchange the value of the current BASIC program pointer in HL with the value of the string's VARPTR to the STACK
2AB4-2AB6Load BC with 2A69H as the return address (which is in the LEFT$routine)
2AB7PUSH BCC5Save the return address in BC to the STACK
2AB8DEC A3DDecrement the value of the requested offset in Register A so that we have a starting position minus 1
2AB9CP (HL)BECompare 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-2ABBLD B,00H06 00Zero Register B
2ABCRET NCD0If the offset pointer ispast the end of the string we are going to return a null
2ABDLD C,A4FLoad Register C with the offset in Register A
2ABELD A,(HL)7ELoad Register A with the string's length at the location of the string's VARPTR in HL
2ABFSUB C91Subtract the index (the second argument) in Register C from the string's length in Register A
2AC0CP EBBCompare 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.
2AC1LD B,A47Load Register B with the calculated string's length in Register A
2AC2RET CD8If we are not going to truncate, then just use the partial string we already have and Return to 2A69H
2AC3LD B,E43Load Register B with the new string's truncated length in Register E
2AC4RETC9Return to 2A69H aka LEFT2
2AC5-2ADE - LEVEL II BASIC VALROUTINE- "VAL"
2AC5-2AC7- ↳ VALGo get the string's length in Register A and the string's VARPTR in HL
2AC8-2ACAJump to 27F8H if the string's length in Register A is equal to zero
2ACBLD E,A5FLoad 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.
2ACCINC HL23Bump the value of the string's VARPTR in HL
2ACDLD A,(HL)7ELoad Register A with the LSB of the string's address at the location of the string's VARPTR in HL
2ACEINC HL23Bump the value of the string's VARPTR in HL
2ACFLD H,(HL)66Load Register H with the MSB of the string's address at the location of the string's VARPTR in HL
2AD0LD L,A6FLoad Register L with the LSB of the string's address in Register A
2AD1PUSH HLE5Save the value of the string's address in HL to the STACK
2AD2ADD HL,DE19Add the string's length in DE to the string's address in HL
2AD3LD B,(HL)46Load Register B with the last character of the string at the location of the string pointer in HL
2AD4LD (HL),D72Save the zero in Register D at the location of the string pointer in HL
2AD5EX (SP),HLE3Exchange the string's ending address in HL with the string's address to the STACK
2AD6PUSH BCC5Save the last character of the string in Register B to the STACK
2AD7LD A,(HL)7ELoad Register A with the first character of the argument
2AD8-2ADACall 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)
2ADBPOP BCC1Get the modified character of the next string into Register B
2ADCPOP HLE1Get the pointer to the modified character back into HL
2ADDLD (HL),B70Restore the character.
2ADERETC9RETurn 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- ↳ PREAMEX DE,HLEBLoad HL with the value of the current BASIC program pointer in DE
2AE0-2AE1RST 08H ⇒ 29SYNCHK ")"CF 29Since 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- ↳ PREAM2POP BCC1Get the return address from the STACK and put it in BC
2AE3POP DED1Get the number of bytes to isolate from the string (from the STACK) and put it in DE
2AE4PUSH BCC5Save the return address in BC to the STACK
2AE5LD B,E43Load Register B with the number of bytes in Register E
2AE6RETC9RETurn to CALLer
2AE7H-2AEE - Process a LEFT-HAND-SIDE MID$- "ISMID$"
2AE7-2AE8- ↳ ISMID$CP 7AHCP MIDTK-$ENDFE 7AThis 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.
2AEF-2AF7 - LEVEL II BASIC INPROUTINE- "FNINP"
2AEF-2AF1- ↳ FNINPGo evaluate the expression at the location of the current BASIC program pointer in HL and return with the port number in Register A
2AF2-2AF4LD (4094H),ALD (STAINP+1),A32 94 40Save the value of the port number (from Register A) into 4094H, which is in the middle of a routine.
2AF8-2B00 - LEVEL II BASIC OUTROUTINE- "FNOUT"
2B01-2B0D - EVALUATE EXPRESSION ROUTINE- "GETINT"
This evaluates an expression and leaves the result in DE as an integer.
2B01- ↳ GETINTRST 10HCHRGETD7We 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- ↳ GETIN2Go 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- ↳ INTFR2PUSH HLE5Save the value of the current BASIC program pointer in HL to the STACK
2B06-2B08Call 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)
2B09EX DE,HLEBLoad DE with the integer result in HL
2B0APOP HLE1Get the value of the current BASIC program pointer from the STACK and put it in HL
2B0BLD A,D7ALoad Register A with the MSB of the integer result in Register D
2B0COR AB7Test the value of the MSB in Register A
2B0DRETC9RETurn to CALLer
2B0E-2B16 - EVALUATE EXPRESSION ROUTINE - OUTcontinues here- "SETIO"
2B0E-2B10- ↳ SETIOGOSUB 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-2B13LD (4094H),ALD (STAINP+1),A32 94 40Save the 8-bit value in Register A in the DOS address of 4094H to set up for WAIT
2B14-2B16LD (4097H),ALD (OUTWRD+1),A32 97 40Save 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-2B18RST 08H ⇒ 2ESYNCHK ","CF 2ESince 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).
2B1B-2B28 - EVALUATE EXPRESSION ROUTINE - This is called by PRINT TAB- "GTBYTC".
2B1B- ↳ GTBYTCRST 10HCHRGETD7We 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- ↳ GETBYTGOSUB 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- ↳ CONINTGOSUB 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-2B24If the result is greater than 255, display a ?FC ERRORmessage
2B25DEC HL2BDecrement the value of the current BASIC program pointer in HL
2B26RST 10HCHRGETD7We 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.
2B27LD A,E7BLoad Register A with the 8-bit result in Register E so that both A and E have the result.
2B28RETC9RETurn 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- ↳ LLISTLD A,01H3E 01Load Register A with the printer output device code
2B2B-2B2DLD (409CH),ALD (PRTFLG),A32 9C 40Save 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- ↳ LISTPOP BCC1Get rid of the the return address on the STACK
2B2F-2B31Go evaluate the range of line numbers given at the location of the current BASIC program pointer in HL
2B32PUSH BCC5Save the address of the first BASIC line (held in BC) to the STACK
2B33-2B35- ↳ LIST4LD HL,FFFFH21 FF FFLoad 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-2B38LD (40A2H),HLLD (CURLIN),HL22 A2 40Save the value in HL as the current BASIC line number (which is stored at 40A2H-40A3H).
2B39POP HLE1Get the address of the first BASIC line to be listed (from the STACK) and put it in HL
2B3APOP DED1Get the value of the last BASIC line number to be listed (from the STACK) and put it in DE
2B3BLD C,(HL)4ELoad Register C with the LSB of the next BASIC line pointer at the location of the memory pointer in HL
2B3CINC HL23Bump the value of the memory pointer in HL
2B3DLD B,(HL)46Load Register B with the MSB of the next BASIC line pointer at the location of the memory pointer in HL
2B3EINC HL23Bump the value of the memory pointer in HL
2B3FLD A,B78Load Register A with the MSB of the next BASIC line pointer in Register B
2B40OR CB1Combine 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-2B43If we are at the elast line, then STOP and JUMP to 1A19H to the READY PROMPT.
2B47-2B49Go scan the keyboard to see if the BREAKkey or the shift-@key was pressed
2B4APUSH BCC5Save the address of the next BASIC line in BC to the STACK
2B4BLD C,(HL)4EWe 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
2B4CINC HL23Bump the value of the memory pointer in HL
2B4DLD B,(HL)46Load Register B with the MSB of the BASIC line number at the location of the memory pointer in HL
2B4EINC HL23Bump the value of the memory pointer in HL
2B4FPUSH BCC5Save the BASIC line number in BC to the STACK
2B50EX (SP),HLE3Swap (SP) and HL so that the line number is now in HL
2B51EX DE,HLEBSwap DE and HL so that the last BASIC line number is now in HL
2B52RST 18HCOMPARDFWe 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.
2B53POP BCC1Get the pointer to the location on the BASIC program line being processed and put it in BC
2B54-2B56If 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!
2B57EX (SP),HLE3Swap (SP) and HL so that the last BASIC line number is now on the STACK
2B58PUSH HLE5Save the address of the next BASIC line in HL to the STACK
2B59PUSH BCC5Save the pointer to the location on the BASIC program line being processed to the STACK
2B5AEX DE,HLEBLoad HL with the BASIC line number (from DE)
2B5B-2B5DLD (40ECH),HLLD (DOT),HL22 EC 40Save the BASIC line number in HL into DOT for use later in EDIT or LIST.
Note: 40ECH-40EDH holds EDIT line number
2B5E-2B60Call 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-2B62LD A,20H3E 20Load Register A with a space
2B63POP HLE1Get the value of the memory pointer from the STACK and put it in HL
2B67-2B69GOSUB 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-2B6CLD HL,(40A7H)LD HL,(BUFPNT)2A A7 40Load HL with the starting address of the input buffer.
Note: 40A7H-40A8H holds the input Buffer pointer
2B6D-2B6FSince 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
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)7ELoad Register A with the character at the location of the memory pointer in HL
2B76OR AB7Check to see if the character in Register A is an end of the string character (00H)
2B77RET ZC8Return if the character in Register A is an end of the string character
2B78-2B7AGo send the character in Register A to the current output device
2B7BINC HL23Bump the value of the memory pointer in HL
2B7C-2B7DLoop 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- ↳ BUFLINPUSH HLE5Save the BASIC line pointer in HL to the STACK
2B7F-2B81LD HL,(40A7H)LD HL,(BUFPNT)2A A7 40Load HL with the starting address of the input buffer.
Note: 40A7H-40A8H holds the input Buffer pointer
2B82
2B83LD B,H
LD C,L44LET Register Pair BC = Register Pair HL
2B84POP HLE1Get the value of the BASIC line pointer from the STACK and put it in HL
2B85JUMP 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.
2B88NOP
2B89- ↳ PLOOPINC BC03Top of a loop. Bump the value of the input buffer pointer in BC
2B8ADEC D15Decrement the character count in Register D
2B8BRET ZC8Return if 256 characters have been moved into the input buffer
2B8CINC HLMove one byte forward in the text.
2B8DLD A,(HL)Load register A with the character at the location of the BASIC line pointer in HL.
2B8EOR ASet the status flags to enable us to check to see if the character in register A is an end of the BASIC line character.
2B8FLD (BC),A02Save 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.
2B90RET ZC8Return if the character in Register A is an end of the BASIC line character
2B94CP FBHCheck to see if the character in register A is a ' token.
2B96-2B97Jump 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-2B9BDEC BC
DEC BC
DEC BC
DEC BC0BFirst, 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-2B9FINC D
INC D
INC D
INC D14Then, 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- ↳ NRQTTKCP 95HCP $ELSEFE 95Check 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-2BA4If 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-2BA6SUB 7FHD6 7FNext, 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
2BA7PUSH HLE5Save the value of the BASIC line pointer in HL to the STACK. Register L holds the reserved word number at this point.
2BA8LD E,A5FLoad Register E with the character in Register A
2BA9-2BABLoad HL with the starting address of the reserved words list
2BAC- ↳ LOPRESLD A,(HL)7ELoad Register A with the character at the location of the reserved words list pointer in HL
2BADOR AB7Test 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.
2BAEINC HL23Bump the value of the reserved words list pointer in HL
2BAF-2BB1If the character at the location of the reserved words pointer in Register A doesn't have bit 7 set then Jump back to 2BACH.
2BB2DEC E1DDecrement the counter
2BB3-2BB4Jump back to 2BACH if this isn't the reserved word for the token
2BB5-2BB6AND 7FHE6 7FReset 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- ↳ MORPURLD (BC),A02Save the character in Register A at the location of the input buffer pointer in BC
2BB8INC BC03Bump the value of the input buffer pointer in BC
2BB9DEC D15Decrement the value of the character counter in Register D
2BBA-2BBCIf 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
2BBDLD A,(HL)7ELoad Register A with the character at the location of the reserved words pointer in HL
2BBEINC HL23Bump the reserved words pointer in HL
2BBFOR AB7Test the value of the character in Register A
2BC0-2BC2Keep getting characters in this reserved word until we hit the next reserved word
2BC3POP HLE1Get the value of the BASIC line pointer from the STACK and put it in HL
2BC4-2BC5Jump 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- ↳ DELETEGOSUB to 1B10H to evaluate the line numbers at the location of the current BASIC program pointer in HL
2BC9POP DED1Get the value of the last BASIC line number to be deleted (in binary) from the STACK and put it in DE
2BCAPUSH BCC5Save the address of the first BASIC line to be deleted in BC to the STACK
2BCBPUSH BCC5Save the address of the first BASIC line to be deleted in BC to the STACK AGAIN!
2BCC-2BCEGOSUB 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-2BD0Since 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
2BD2LD D,H
LD E,L54Let Register Pair DE = Register Pair HL
2BD3EX (SP),HLE3Exchange the last BASIC line's address in HL with the first BASIC line's address to the STACK
2BD4PUSH HLE5Save the pointer to the first line in range to the STACK
2BD5RST 18HCOMPARDFWe 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- ↳ FCERRGDisplay 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
2BDC-2BDECall the WRITE MESSAGE routine at 28A7H to print the message pointed to by HL.
2BDFPOP BCC1Get the first BASIC line's address from the STACK and put it in BC
2BE3EX (SP),HLE3Swap (SP) and HL so that HL now points to the next BASIC line's address ...
2BE4- ↳ DELEX DE,HLEBand then put it into DE
2BE5-2BE7LD HL,(40F9H)LD HL,(VARTAB)2A F9 40Load HL with the end of the BASIC program pointer.
- Note: 40F9H-40FAH holds the starting address of the simple variable storage area.
2BE8- ↳ MLOOPLD A,(DE)1ALoad Register A with the character at the location of the memory pointer in DE
2BE9LD (BC),A02Save the character in Register A at the location of the memory pointer in BC
2BEAINC BC03Bump the value of the memory pointer in BC
2BEBINC DE13Bump the value of the memory pointer in DE
2BECRST 18HCOMPARDFNow 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-2BEELoop back to 2BE8H until the memory pointer in DE equals the end of the BASIC program pointer in HL
2BEFLD H,B60Load Register H with the MSB of the memory pointer in Register B
2BF0LD L,C69Load Register L with the LSB of the memory pointer in Register C
2BF1-2BF3LD (40F9H),HLLD (VARTAB),HL22 F9 40Save 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.
2BF4RETC9RETurn 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- ↳ CSAVECalls the WRITE LEADER routine at 0284H (which writes a Level II leader on the cassette unit set in Register A)
2BF82BFAH 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
2BFBPUSH HLE5Save 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
2BFF-2C00LD A,D3H3E D3Load Register A with the filename header byte (=D3H which is a "S" with the sign bit on)
2C01-2C03the 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
2C07LD A,(DE)1ALoad Register A with the first character of the filename at the location of the filename pointer in DE
2C08-2C0Athe 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-2C0DLD HL,(40A4H)LD HL,(TXTTAB)2A A4 40Load 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).
2C0EEX DE,HLEBLoad DE with the start of the BASIC program pointer in HL
2C0F-2C11LD HL,(40F9H)LD HL,(VARTAB)2A F9 40Load HL with the end of the BASIC program pointer.
- Note: 40F9H-40FAH holds the starting address of the simple variable storage area.
2C12- ↳ LOPSCOLD A,(DE)1ATop 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
2C13INC DE13Bump the value of the memory pointer in DE
2C14-2C16the 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)
2C17RST 18HCOMPARDFNow 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-2C19Loop back to 2C12H until the memory pointer in DE is equal to the end of the BASIC program pointer in HL
2C1DPOP HLE1Get the value of the current BASIC program pointer from the STACK and put it in HL
2C1ERETC9RETurn to CALLer
2C1F-2CA4 - LEVEL II BASIC CLOAD ROUTINE - ROM v1.0- "CLOAD"
2C22LD A,(HL)7ELoad Register A with the character at the location of the current BASIC program pointer in HL
2C23-2C24SUB 0B2HSUB $PRINTD6 B2Check 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-2C26Jump to the CLOAD?routine at 2C29H if the character at the location of the current BASIC program pointer in Register A is a ?
2C27XOR AAFOK - So this is now a straight CLOAD. First, zero Register A
2C28LD BC,232F01 2F 23Z-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- ↳ CLOADPCPL2FLoad Register A with a -1 for CLOAD?. It will still be a 0 if this is CLOAD
2C2AINC HL23Bump the value of the current BASIC program pointer in HL until it points to the next character after the ?in CLOAD?
2C2BPUSH AFF5Save the CLOAD/CLOAD?flag in Register A to the STACK
2C2CDEC HL2BDecrement the value of the current BASIC program pointer in HL so we can see if we are at the end
2C2DRST 10HCHRGETD7We 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-2C2FLD A,00H3E 00Zero Register A to allow for any filename
2C30-2C31Jump if the character at the location of the current BASIC program pointer in HL is an end of the BASIC statement character
2C32-2C34To 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-2C37Make sure the length is good, and save the pointer to the filename to Register Pair DE
2C38LD A,(DE)1ALoad Register A with the first character of the filename at the location of the filename pointer in DE
2C39- ↳ CLNONMLD L,A6FLoad Register L with the filename in Register A
2C3APOP AFF1Get the value of the CLOAD/CLOAD?flag from the STACK and put it in Register A
2C3BOR AB7Test the value of the CLOAD/CLOAD?flag in Register A (since CPL doesn't set any flags)
2C3CLD H,A67Load Register H with the value of the CLOAD/CLOAD?flag in Register A
2C3D-2C3FLD (4121H),HLLD (FACLO),HL22 21 41Save the value of the CLOAD/CLOAD?flag and the filename in HL in ACCumulator
2C43-2C45LD HL,(4121H)LD HL,(FACLO)2A 21 41Load HL with the CLOAD/CLOAD?flag and the filename in ACCumulator
2C46EX DE,HLEBLoad D with the CLOAD/CLOAD? flag and load Register E with the filename
2C47-2C48- ↳ LOPCLKLD B,03H06 03Load Register B with the number of header bytes
2C49-2C4B- ↳ LOPCL2Calls 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-2C4DSUB 0D3HD6 D3Check to see if the character in Register A is a filename header byte
2C4E-2C4FLoop if the character in Register A isn't a filename header byte
2C50-2C51Loop back to 2C49H until three filename header bytes have been read
2C52-2C54Now 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)
2C55INC E1CWe 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
2C56DEC E1DDecrement the value of the filename in Register E
2C57-2C58Jump to 2C5CH (to pretend the filename matched) if no filename was specified
2C59CP EBBIf 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-2C5BJump 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- ↳ NONAMCLD HL,(40A4H)LD HL,(TXTTAB)2A A4 40If 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- ↳ DOCRSLD B,03H06 03Load Register B with the number of zeros to look for to stop the load (which is 3)
2C61-2C63- ↳ DOCSMRCalls 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)
2C64LD E,A5FPreserve the character that was just read from tape into Register E
2C65SUB (HL)96Compare 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
2C66AND DA2Combine 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.
2C69LD (HL),E73At 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-2C6CMake sure there is more room, and toss a ?OM ERRORif there isn't.
2C6DLD A,(HL)7ELoad Register A with the character at the location of the memory pointer in HL
2C6EOR AB7Check to see if the byte just read in Register A is equal to zero
2C6FINC HL23Bump the value of the memory pointer in HL
2C70-2C71Loop if the byte in Register A isn't equal to zero (meaning that it isn't end of program or end of statement)
2C72-2C74Call the BLINK ASTERISK routine at 022CH which alternatively displays and clears an asterisk in the upper right hand corner of the video display
2C75-2C76Do that loop until three zeros in a row have been read from the cassette recorder
2C77-2C79LD (40F9H),HLLD (VARTAB),HL22 F9 40By 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-2C7CGOSUB 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.
2C80-2C82We 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-2C85LD HL,(40A4H)LD HL,(TXTTAB)2A A4 40Load 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).
2C86PUSH HLE5Save the start of the BASIC program pointer in HL to the STACK. FINI will need this value there.
2C8A-2C8CGOSUB 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-2C8FCall 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-2C92JP 1A18JP STPRDYC3 18 1AJUMP to STPRDY to pop NEWSTT from the STACK and then fall into the READY routine
2C93-2C95- ↳ SKPFILLD (3C3EH),A32 3E 3CGo display the filename on the video display
2C96-2C97- ↳ ZERSRFLD B,03H06 03Load Register B with the number of zeros to be found to stop the search
2C98-2C9A- ↳ GETCHZCalls 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)
2C9BOR AB7Check to see if the character in Register A is equal to zero
2C9E-2C9FLoop until three zeros in a row have been read from the cassette recorder
2CA0-2CA2Calls 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)
2CA5-2CA9 - MESSAGE STORAGE LOCATION- "NOOKCS"
2CA5-2CA9- ↳ NOOKCS"BAD" + 0DH + 00H42The 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- ↳ PEEKCall 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)
2CADLD A,(HL)7ELoad Register A with the value at the location of the memory pointer in HL
2CAE-2CB0Go save the 8-bit value in Register A as the current result in ACCumulator
2CB1-2CBC - LEVEL II BASIC POKEROUTINE- "POKE"
2CB1-2CB3- ↳ POKEGo evaluate the expression at the location of the current BASIC program pointer in HL and return with the integer result in DE
2CB4PUSH DED5Save the address the user wants to POKE to (held in DE) to the STACK
2CB5-2CB6RST 08H ⇒ 2ESYNCHK ","CF 2ESince 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-2CB9GOSUB 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
2CBAPOP DED1Get the address the user wants to POKE to from the STACK and put it in DE
2CBBLD (DE),A12Save the value the user wanted to poke (held in Register A) in the location that the user wants to POKE to (held in DE)
2CBCRETC9RETurn 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- ↳ PRINUSGo evaluate the string expression at the location of the current BASIC program pointer in HL 2CC0-2CC2Go make sure the expression that was just evaluated was a string 2CC3-2CC4RST 08H ⇒ 3BSYNCHK ";"CF 3BSince 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).
2CC5EX DE,HLEBSwap DE and HL so that DE now holds the current BASIC program pointer 2CC6-2CC8LD HL,(4121H)LD HL,(FACLO)2A 21 41Load HL with the USING string's VARPTR 2CCB-2CCD- ↳ REUSSTLD A,(40DEH)LD A,(FLGINP)3A DE 40Load 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. 2CCEOR AB7Check to see if that flag indivates that we did, or did not, print out a value last time. 2CCF-2CD0If we did not print out a value last time, we have an error, so JUMP down to 2CDDH 2CD1POP DED1Restore the pointer to the "USING" string decription from the STACK into DE 2CD2EX DE,HLEBSwap 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- ↳ INIUSPUSH HLE5Save the pointer to the "USING" string descriptor (i.e., the USING string's VARPTR) in HL to the STACK 2CD4XOR AAFZero Register A and all the flags. 2CD5-2CD7LD (40DEH),ALD (FLGINP),A32 DE 40Clear the flag we are using to see if we printed the values or not. 2CD8CP DBATurn 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. 2CD9PUSH AFF5Save the flag indicating if the value list has ended or not to the STACK 2CDAPUSH DED5Save the pointer into the value list to the STACK 2CDBLD B,(HL)46Load Register B with the USING string's length 2CDCOR BB0Check to see if the USING string's length in Register B is equal to zero 2CDD-2CDF- ↳ FCERR3If the USING string is NULL then display a ?FC ERROR 2CE0INC HL23Bump the pointer to the USING string's data in HL by 1 2CE1LD C,(HL)4ELoad Register C with the LSB of the USING string's address at the location of the USING string's VARPTR in HL 2CE2INC HL23Bump the value of the USING string's VARPTR in HL 2CE3LD H,(HL)66Load Register H with the MSB of the USING string's address at the location of the USING string's VARPTR in HL 2CE4LD L,C69Load Register L with the LSB of the USING string's address in Register C
2CE7- ↳ BGSTRFLD E,B58Load Register E with the USING string's length 2CE8PUSH HLE5Save the pointer to the USING string pointer in HL to the top of the STACK 2CE9-2CEALD C,02H0E 02Since the \\ string field length is two plus number of enclosed spaces, add two 2CEB- ↳ LPSTRFLD A,(HL)7ELoad Register A with the character at the location of the USING string pointer in HL 2CECINC HL23Bump the value of the USING string data pointer in HL 2CED-2CEECP 25HCP CSTRNGFE 25Check 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-2CF1If it is a "%" then JUMP to 2E17H to evaluate a string and print 2CF2-2CF3CP 20HFE 20Check 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-2CF5If the character is not a field extender, then it isn't a string field, so JUMP down a few opcodes to 2CF9H 2CF6INC C0CIncrement the field width (tracked in in Register C) 2CF7-2CF8Decrement 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- ↳ NOSTRFPOP HLE1Restore the pointer to the "USING" string's data into Register Pair HL
2CFALD B,E43Load Register B with the USING string's length
2CFB-2CFCLD A,25HLD A,CSTRNG3E 25Restore 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- ↳ NEWUCHIf a +came before the character, make sure to print it
2D00-2D02Once that has been printed, now we print the character in Register A since we know it isn't part of a field
2D03- ↳ PRCCHRXOR AAFWe 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
2D04LD E,A5FZero Register E
2D05LD D,A57Zero Register D
2D06-2D08- ↳ PLSFINGo print a leading +if necessary (i.e., to allow for multiple plusses)
2D09LD D,A57Set 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.
2D0ALD A,(HL)7ELoad Register A with the next field description character in the USING string
2D0BINC HL23Bump the value of the USING string pointer in HL
2D0C-2D0DCP 21HCP "!"FE 21Check 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.
2D11-2D12CP 23HCP "#"FE 23Check 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.
2D15DEC B05Since every other possibility is actually a two character field, decrement the value of the string's length in Register B onem ore time
2D16-2D18If 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-2D1ACP 2BHCP "+"FE 2BCheck 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-2D1CLD A,08H3E 08Set 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
2D1FDEC HL2BDecrement the value of the USING string pointer so we can re-get the character.
2D20LD A,(HL)7ELoad Register A with the (current) character at the location of the USING string pointer in HL
2D21INC HL23Bump the value of the USING string pointer in HL
2D22-2D23CP 2EHCP "."FE 2ECheck 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-2D25Jump if the character in Register A is a .to scan with Register E holding the number of digits before the "." as 0
2D26-2D27CP 25HCP CSTRNGFE 25Check 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-2D29Jump to see if it is really a string field if the character in Register A is a %
2D2ACP (HL)BECheck 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-2D2CIf the NZ flag is set, then we can't have a $$or a **, so all remaining possibilities are exhausted, so JUMP to NEWUCH
2D2D-2D2ECP 24HCP "$"FE 24Check 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.
2D31-2D32CP 2AHCP "*"FE 2ACheck 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-2D34If 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
2D35LD A,B78Prepare 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-2D37CP 02HFE 02Check 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.
2D38INC HL23Bump the value of the USING string pointer in HL
2D39-2D3AJump to 2D3EH if the USING string's length in Register A isn't at least two
2D3BLD A,(HL)7ELoad Register A with the character at the location of the USING string pointer in HL
2D3C-2D3DCP 24HCP "$"FE 24Check 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- NOTSPCLD A,20H3E 20Set the *bit in Register A
2D40-2D41If we did not ultimately get a **$then JUMP (noting we do NOT set the dollar sign flag)
2D42DEC B05Decrement the value of the USING string's length to take the $into account
2D43INC E1CBump the field width tracker to account for the floating dollar sign
2D44-2D45CP 0AFHFE AFZ-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- ↳ DOLRNMXOR AAFThis is $processing for PRINT USING. Clear Register A.
2D46-2D47ADD A,10HC6 10Mask Register A to set the bit for a floating dollar sign flag.
2D48INC HL23Bump the value of the USING string pointer in HL to go past the special characters
2D49- ↳ SPCNUMINC E1CSince two characters specify the field size, start off with E=1
2D4AADD A,D82Combine the bits in Register D into the flag tracker
2D4BLD D,A57Preserve the modified flag tracker into Register D.
2D4C- ↳ NUMNUMINC E1CBump the number of characters to the left of the decimal point in Register E
2D4D-2D4ELD C,00H0E 00Set the number of digits to the right of the decimal point (tracked in Register C) to 0
2D4FDEC B05Check to see if there are any more characters by decrementing the value of the string's length in Register B
2D50-2D51If 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.
2D52LD A,(HL)7ELoad Register A with the next character at the location of the USING string pointer in HL
2D53INC HL23Bump the value of the USING string pointer in HL
2D54-2D55CP 2EHCP "."FE 2ECheck 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-2D57If yes, then need to use a special scan loop to scan after the decimal point, so JUMP to AFTDOT
2D58-2D59CP 23HCP "#"FE 23Check 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.
2D5C-2D5DCP 2CHCP ","FE 2CCheck 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-2D5FIf there is no comma, then JUMP to FINNUM because there are no more leading digits and we need to check for "^^^"
2D60LD A,D7AIf we are here, then a comma was requested. Turn on the COMMA bit
2D61-2D62OR 40HF6 40Mask the flag in Register A for ,
2D63LD D,A57Load Register D with the value of the flag in Register A
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- ↳ DOTNUMLD A,(HL)7ELoad Register A with the next character of the USING string
2D67-2D68CP 23HFE 23Check 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-2D6ALD A,2EHLD A,"."3E 2ELoad Register A with a decimal point
2D6B-2D6CIf it isn't a "." then JUMP AWAY to NEWUCH with A holding a "." so that a "." will get printed
2D6D-2D6ELD C,01H0E 01If 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
2D6FINC HL23Bump the value of the USING string pointer in HL
2D70- ↳ AFTDOTINC C0CBump the number of digits to the right of the decimal point (tracked in Register C)
2D71DEC B05Decrement the value of the USING STRING's length to test to see if there are more characters
2D72-2D73If the USING string length is now ZERO, JUMP to ENDNUS to stop scanning
2D74LD A,(HL)7ELoad Register A with the character at the location of the USING string pointer in HL
2D75INC HL23Bump the value of the USING string pointer in HL
2D76-2D77CP 23HFE 23Check 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-2D79If 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- ↳ FINNUMPUSH DED5Save the value of the flag (tracked in D) and the number of leading digits (tracked in E) to the STACK
2D7B-2D7DLoad DE with the return address in case this is not a scientific notation
2D7EPUSH DED5Save the value of the return address in DE to the STACK
2D7F
2D80LD D,H
LD E,L54Let DE = HL in case we need to rememer HL
2D81-2D82CP 5BHFE 5BCheck 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.
2D83RET NZC0Return if the character in Register A isn't an up arrow
2D84CP (HL)BECheck 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.
2D85RET NZC0Return 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)
2D86INC HL23Bump the value of the USING string pointer in HL
2D87CP (HL)BECheck 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.
2D88RET NZC0Return 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)
2D89INC HL23Bump the value of the USING string pointer in HL
2D8ACP (HL)BECheck 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.
2D8BRET NZC0Return 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)
2D8CINC HL23Bump the value of the USING string pointer in HL. If we are here we have a #.^^^^format
2D8DLD A,B78Now 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-2D8FSUB 04HD6 04Check to see if there are at least 4 characters left in the USING string
2D90RET CD8Return to 2D97 if there aren't at least four characters left in the USING string
2D91POP DED1If there are at least 4 characters left, then clean up the STACK by removing the NOTSCI return address
2D92POP DED1Get the flag and the count of the characters to the left of the decimal point from the STACK and put it in DE
2D93LD B,A47Load Register B with the new USING string's length in Register A
2D94INC D14Set the exponential notation flag (tracked in Register D)
2D95INC HL23Bump the value of the USING string pointer in HL
2D96JP Z,0D1EBHCA EB D1Z-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
2D98POP DED1(Ignored if passing through) Restore the flags into Register D and the number of leading digits into Register E
2D99- ↳ ENDNUSLD A,D7AWe 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
2D9ADEC HL2BDecrement the value of the USING string pointer in HL
2D9BINC E1CBump the number of characters to the left of the decimal point in Register E to take into account the leading plus
2D9C-2D9DAND 08HE6 08Mask Register A to NOT check for a trailing sign
2D9E-2D9FIf that AND leaves us with a NZ, then we are all done with the field, so JUMP to ENDNUM
2DA0DEC E1DOtherwise, 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
2DA1LD A,B78Check to see if there are more characters by first loading Register A with the USING string's length from Register B
2DA2OR AB7Check to see if this is the end of the USING string
2DA3-2DA4If we are out of characters, then we are all done, so JUMP to ENDNUM
2DA5LD A,(HL)7EIf there ARE more characters, then fill Register A with the character at the location of the USING string pointer in HL
2DA6-2DA7SUB 2DHSUB "-"D6 2DCheck to see if the character in Register A is a -(i.e., a trailing minus)
2DAA-2DABCP 0FEHCP "+" - "-"FE FECheck 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.
2DAE-2DAFLD A,08H3E 08If we are here then we did have a trailing "+" so first set the flag for a POSITIVE "+"
2DB0-2DB1- ↳ SGNTRLADD A,04HC6 04Then set the flag for a trailing sign
2DB2ADD A,D82Combine the value of the flag in Register D with the value of the flag in Register A
2DB3LD D,A57Load Register D with the current flags
2DB4DEC B05Decrement 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- ↳ ENDNUMPOP HLE1Get the value of the current BASIC program pointer from the STACK and put it in HL
2DB6POP AFF1Load Register A with the flag that tells us whether there are more values to process in the value list.
2DB7-2DB8If there are no more values in the value list to process, then JUMP to FLDFIN because we are done with the PRINT
2DB9PUSH BCC5Save 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
2DBAPUSH DED5Save the flags (held in D) and the number of leading digits (held in E) to the STACK
2DBB-2DBDRead 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
2DBEPOP DED1Restore the flags (held in D) and the number of leading digits (held in E) from the STACK
2DBFPOP BCC1Restore 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
2DC0PUSH BCC5Save 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
2DC1PUSH HLE5Save the value of the current BASIC program pointer in HL to the STACK
2DC2LD B,E43Set Register B to hold the number of leading digits (i.e., the number of characters to the left of the decimal point)
2DC3LD A,B78We 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
2DC4ADD A,C81Then 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-2DC6CP 19HFE 19Check 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-2DC9Display a FC ERROR message if the total number of digits is greater than 24
2DCALD A,D7ALoad Register A with the flags (held in Register D)
2DCB-2DCCOR 80HF6 80Turn on the "USING" bit in the flags
2DCD-2DCFPrepare 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-2DD2Call 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- ↳ FNSTRFPOP HLE1Top of a loop. Get the value of the current BASIC program pointer from the STACK and put it in HL
2DD4DEC HL2BDecrement the value of the current BASIC program pointer in HL so we can test to see what the terminator was
2DD5RST 10HCHRGETD7We 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.
2DD6SCF37Set the Carry flag to indicate that a CRLF is desired
2DD7-2DD8If 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-2DDBLD (40DEH),ALD (FLGINP),A32 DE 40Set the flag that the value HAS been printed!
2DDC-2DDDCP 3BHCP ";"FE 3BCheck 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.
2DE0-2DE1CP 2CHCP ","FE 2CCheck 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-2DE4If 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- ↳ SEMUSNRST 10HCHRGETD7We 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- ↳ CRDNUSPOP BCC1Restore the number of characters remaining to be procesed in the USING string into Register B
2DE7EX DE,HLEBSwap DE and HL so that DE will point to the location of the current BASIC program pointer. We don't care about HL.
2DE8POP HLE1Restore the position in the USING string from the STACK and put it in HL
2DE9PUSH HLE5Save the position in the USING string (held in HL) to the STACK
2DEAPUSH AFF5Save the flag that indicates whether or not the value list has terminated to the STACK
2DEBPUSH DED5Save 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.
2DECLD A,(HL)7ELoad Register A with the USING string's length at the location of the USING string's VARPTR in HL
2DEDSUB B90Subtract the number of characers which were already scanned
2DEEINC HL23Bump the pointer to the "USING" strings string data
2DEFLD C,(HL)4ELoad Register C with the LSB of the USING string's address at the location of the USING string's VARPTR in HL
2DF0INC HL23Bump the value of the USING string's VARPTR in HL
2DF1LD H,(HL)66Load Register H with the MSB of the USING string's address at the location of the USING string's VARPTR in HL
2DF2LD L,C69Load Register L with the LSB of the USING string's address in Register C
2DF3-2DF4LD D,00H16 00Zero Register D so that Register Pair DE can be a 16 bit offset of whatever is held in A.
2DF5LD E,A5FLoad Register E with the USING string's offset in Register A
2DF6ADD HL,DE19Add 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
2DF7LD A,B78Load Register A with the number of characters left to scan
2DF8OR AB7Check to see if this is the end of the USING string
2DF9-2DFBIf there are still more string characters to scan, JUMP to PRCCHR to do so
2DFE-2E00 - Part of the PRINT USING Routine- "REUSIN"
We will wind up here when we are done processing a numeric field
2E01-2E03Go send the FINAL character (held in Register A) to the current output device
2E04- ↳ FINUSIPOP HLE1Get the value of the current BASIC program pointer from the STACK and put it in HL
2E05POP AFF1Restore the flag which indicates whether or not the value list has ended into Register A
2E06-2E08If the value list has NOT ended, JUMP back to REUSST to reuse the USING string
2E09-2E0B- ↳ FLDFINIf 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
2E0CEX (SP),HLE3Swap (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
2E10POP HLE1Get the value of the current BASIC program pointer from the STACK and put it in HL
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- ↳ SMSTRFLD C,01H0E 01Set the field width to 1
2E16-2E17LD A,0F1H3E F1Z-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- ↳ ISSTRFPOP 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)
2E18DEC B05Decrement the USING string character count (tracked in Register B)
2E1CPOP HLE1Get the value of the current BASIC program pointer from the STACK and put it in HL
2E1DPOP AFF1Get the flag which indicates whether there are more values in the value list into Register A
2E1E-2E1FIf there are no more values in the value list, then we are done so JUMP back to 2E09H
2E20PUSH BCC5Save the number of characters still to be scanned from the USING string (tracked in B) to the STACK
2E21-2E23Read 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
2E27POP BCC1Restore the field width (a/k/a the number of characters to be printed) into Register C
2E28PUSH BCC5Save the USING string's length and the number of characters to be printed in BC to the STACK
2E29PUSH HLE5Save the value of the current BASIC program pointer in HL to the STACK
2E2A-2E2CLD HL,(4121H)LD HL,(FACLO)2A 21 41Load HL with the string's VARPTR in ACCumulator
2E2DLD B,C41Load Register B with field width (a/k/a the number of characters to be printed)
2E2E-2E2FLD C,00H0E 00Zero Register C so that we can use the LEFT$ routine
2E30PUSH BCC5Save the length of the string to be printed in Register B to the STACK (as we will need that for space padding)
2E31-2E33Truncate the string to B characters via a call to the LEFT$ routine
2E37-2E39LD HL,(4121H)LD HL,(FACLO)2A 21 41Load HL with the string's VARPTR in ACCumulator so we can see if we need to pad the string
2E3APOP AFF1Get the field width (a/k/a the length of the string to be printed) from the STACK and put it in Register A
2E3BSUB (HL)96Determine 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
2E3CLD B,A47Save the amount of padding needed into Register B
2E3D-2E3ELD A,20H3E 20Load Register A with a SPACE
2E3FINC B04Bump 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- ↳ UPRTSPDEC B05Top of a loop. Decrement the number of spaces in Register B
2E41-2E43If all of the spaces have been printed, Jump back to 2DD3H to see if the value list ended and to resume scanning
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- ↳ PLSPRTPUSH AFF5Save the current character (held in Register A) to the STACK
2E4ALD A,D7AWe need to test the PLUS BIT in D, so first load Register A with the value in Register D
2E4BOR AB7Check 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-2E4DLD A,2BHLD A,"+"3E 2BPrepare to print the +by loading Register A with a +
2E4E-2E50If the bit was set (i.e., A was non-zero), then send a +to the current output device
2E51POP AFF1Get the current character from the STACK and put it in Register A
2E52RETC9RETurn 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- ↳ ERREDTLD (409AH),ALD (ERRFLG),A32 9A 40Reset the EDIT flag.
Note: 409AH holds the ERROR/RESUME flag
2E56-2E58LD HL,(40EAH)LD HL,(ERRLIN)2A EA 40Load HL with the line number to be edited.
Note: 40EAH-40EBH holds the line number with error
2E59OR HB4OR Register A with the MSB of the error line number in Register H
2E5AAND LA5Combine 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
2E5BINC A3CBump 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
2E5CEX DE,HLEBSwap DE and HL so that DE now holds the line number to edit.
2E5DRET ZC8If there was no line number, return if Level II BASIC
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- ↳ EDITGet the first line number by calling 1E4F - returns in in DE
2E63RET NZC0If the zero flag got set, there was no line number, so return
2E64- ↳ EREDITPOP HLE1Clean up the STACK (i.e., discard the NEWSTT return address)
2E65- ↳ EEDITSEX DE,HLEBLoad HL with the line number to be edited in DE
2E66-2E68LD (40ECH),HLLD (DOT),HL22 EC 40Save 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
2E69EX DE,HLEBLoad HL with the line number to be edited
2E6A-2E6CFind 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
2E70
2E71LD H,B
LD L,C60At 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
2E73INC HL
INC HL23Bump the value of the memory pointer in HL twice to now point to the first byte of the line.
2E74LD C,(HL)4ELoad Register C with first byte of the line number being edited
2E75INC HL23Bump the value of the memory pointer in HL to point to the second byte of the line being edited
2E76LD B,(HL)46Load Register B with second byte of the line number being edited
2E77INC HL23Bump the value of the memory pointer in HL to now point to the first byte of the actual line
2E78PUSH BCC5Save the line number to the STACK
2E79-2E7BGOSUB 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- ↳ LLEDPOP HLE1Get the value of the line number from the STACK and put it in HL
2E7D- ↳ INLEDPUSH HLE5Save the value of the line number in HL to the STACK
2E7E-2E80Convert 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-2E82LD A,20H3E 20Load Register A with a space
2E86-2E88LD HL,(40A7H)LD HL,(BUFPNT)2A A7 40Load 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-2E8ALD A,0EH3E 0ELoad Register A with the "turn on the cursor" character
2E8EPUSH HLE5Save the value of the input buffer pointer (in HL) to the STACK
2E8F-2E90LD C,FFH0E FFLoad 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- ↳ LENLPINC C0CBump the number of characters examined so far in Register C
2E92LD A,(HL)7ELoad Register A with the character at the location of the input buffer pointer in HL
2E93OR AB7Check to see if the character in Register A is an end of the BASIC line character
2E94INC HL23Bump the value of the input buffer pointer in HL
2E95-2E96Loop back to 2E91H until the end of the BASIC line has been found
2E97POP HLE1At this point, C will be the maximum number of characters in the line at issue. Put the start of the expanded buffer into HL
2E98LD B,A47Set the current position in the BASIC line being edited (tracked by Register B) to ZERO
2E99-2E9A- ↳ DISPEDLD D,00H16 00Assume the repetition count for the upcoming command (tracked by Register D) is zero
2E9B-2E9D- ↳ DISPIGo scan the keyboard to wait for the user command
2D9E-2E9F- ↳ DISPSUB 30H20 15We need to test to see if the character was alphabetic or alphanumeric so we subtract 30H from it
2EA2-2EA3CP 0AHFE 0ACheck 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.
2EA6LD E,A5FLoad Register E with the binary value of the character in Register A
2EA7LD A,D7APut the repetition value into Register A
2EA8RLCA07Multiply the value in Register A by two (so now A has multiplied by 2)
2EA9RLCA07Multiply the value in Register A by two (so now A has multiplied by 4)
2EAAADD A,D82Add the value in Register D to the value in Register A (so now A has multiplied by 5)
2EABRLCA07Multiply the value in Register A by two (so now A has multiplied by 10). Now the "ones place" is empty.
2EACADD A,E83Add the value in Register E to the value in Register A in the "ones place"
2EADLD D,A57Load Register D with the value in Register A
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- ↳ NOTDGIPUSH HLE5Save the value of the input buffer pointer in HL to the STACK
2EB4EX (SP),HLE3Exchange the value of the return address in HL with the value of the input buffer pointer to the STACK
2EB5DEC D15We 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
2EB6INC D14... and then incrementing the numeric value in Register D to set the flags
2EB7-2EB9If we had a received a repetition count already, then JUMP to 2EBBH
2EBAINC D14Otherwise, set the repetition count (held in Register D) to be one
2EBB-2EBC- ↳ NTZERDCP 0D8HFE D8Check 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-2EBFIf the character in Register A is a BACKSPACEcharacter, JUMP to DELED
2EC0-2EC1CP 0DDHFE DDCheck 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-2EC4If the character in Register A is a CARRIAGE RETURN, JUMP to CRED
2EC5-2EC6CP 0F0HFE F0Check 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.
That's it for non-alphabetic instructions, so we need to need to convert a lower case command to upper case
2EC9-2ECACP 31HFE 31Check 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.
2ECD-2ECESUB 20HD6 20Convert the lowercase character in Register A to uppercase
2ECF-2ED0- ↳ NOTLW4CP 21HFE 21Check 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.
2ED4-2ED5CP 1CHFE 1CCheck 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.
2ED9-2EDACP 23HFE 23Check 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.
2EDD-2EDECP 19HFE 19Check 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.
2EE2-2EE3CP 14HFE 14Check 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.
2EE7-2EE8CP 13HFE 13Check 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.
2EEC-2EEDCP 15HFE 15Check 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.
2EF1-2EF2CP 28HFE 28Check 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.
2EF6-2EF7CP 1BHFE 1BCheck 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.
2EFA-2EFBCP 18HFE 18Check 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-2EFEJump if the character in Register A is an H(i.e., HACK off the rest of the line and then enter INSERT mode)
2EFF-2F00CP 11HFE 11Check 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.
2F01RET NZC0Return if the character in Register A isn't an A
2F02 - EDITCommand - Cancel and Restore Logic.
2F02POP BCC1Clean up the STACK (i.e., remove the DISPI return address)
2F03POP DED1Get the BASIC line number from the STACK and put it in DE
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- ↳ SPEDLD A,(HL)7ELoad Register A with the character at the location of the input buffer pointer in HL
2F0BOR AB7Check to see if the character in Register A is an end of the BASIC line character
2F0CRET ZC8Return if the character in Register A is an end of the BASIC line character
2F0DINC B04Bump the character position in Register B
2F11INC HL23Bump the value of the pointer in HL
2F12DEC D15Decrement the number of times to perform the operation in Register D
2F15RETC9RETurn to CALLer
2F16 - EDITCommand - KILL Logic- "KED".
2F16- ↳ KEDPUSH HLE5Save the current character position in the buffer in HL to the STACK
2F17-2F19Load HL with the return address of 2F5FH (which will print the final !
2F1AEX (SP),HLE3Exchange the value of the return address in HL with the value of the input buffer pointer to the STACK
2F1BSCF37Set the KILL/SEARCH flag for KILL since CARRY flag signals KILL
2F1C- ↳ SEDPUSH AFF5Save the KILL/SEARCH flag to the STACK
2F1D-2F1FGo scan the keyboard for the character the user wants to SEARCH for
2F20LD E,A5FSave the character the user wants to SEARCH for into Register E
2F21POP AFF1Get the KILL/SEARCH flag from the STACK
2F22PUSH AFF5Save the KILL/SEARCH flag to the STACK
2F23-2F25If KILL (because the CARRY flag was set) then GOSUB to 2F5FH to print a !
2F26- ↳ SRCALPLD A,(HL)7ELoad Register A with the character at the location of the input buffer pointer in HL
2F27OR AB7Check to see if the character in Register A is an end of the BASIC line character
2F28-2F2AJump down to 2F3EH if the character in Register A is an end of the BASIC line character
2F2EPOP AFF1Get the KILL/SEARCH flag from the STACK
2F2FPUSH AFF5Save the KILL/SEARCH flag to the STACK
2F30-2F32If 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-2F34Jump to 2F37H if KILL. Note, we do not move the HL pointer in this case because DELCHR already moved it.
2F35INC HL23If we are here, it must be SEARCH! So bump to the next character
2F36INC B04Bump the value of the character position in Register B
2F37- ↳ NOTSRCLD A,(HL)7ERegardless of whether we are SEARCH or KILL, load Register A with the character at the location of the current input buffer pointer in HL
2F38CP EBBCheck 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.
2F3BDEC D15Decrement the number of times to perform the operation in Register D (as initially specified by the user by entering a number before the command)
2F3E- ↳ POPARTPOP AFF1Get the KILL/SEARCH flag from the STACK
2F3FRETC9RETurn to CALLer
2F40 - EDITCommand - LIST Logic- "LED".
2F40-2F42- ↳ LEDSince 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
2F46POP BCC1Clear off the RETURN address to DISPED
2F47-2F49Jump to 2E7CH (to display the current line number and await the next EDIT command)
2F4A - EDITCommand - DELETE Logic- "DED"
2F4A- ↳ DEDLD A,(HL)7ELoad Register A with the character at the location of the input buffer pointer in HL
2F4BOR AB7Check to see if the character in Register A is an end of the BASIC line character
2F5CRET Z15Return if the character in Register A is an end of the BASIC line character
2F4D-2F4ELD A,21HLD A,"!"3E 21Load Register A with an !
2F52- ↳ DELLPLD A,(HL)7ELoad Register A with the character at the location of the input buffer pointer in HL
2F53OR AB7Check to see if the character in Register A is an end of the BASIC line character
2F54-2F5BJump to 2F5FH if the character in Register A is an end of the BASIC line character
2F5CDEC D15Decrement the number of times to perform the operation in Register D (as initially specified by the user by entering a number before the command)
2F5F-2F60- ↳ TYPSLHLD A,21HLD A,"!"3E 21Load Register A with an !
2F64RETC9RETurn to CALLer
2F65 - EDITCommand - CHANGE Logic- "CED".
2F65- ↳ CEDLD A,(HL)7ELoad Register A with the character at the location of the input buffer pointer in HL
2F66OR AB7Check to see if the character in Register A is an end of the BASIC line character
2F67RET ZC8Return if the character in Register A is an end of the BASIC line character
2F68-2F6AGo get the character to put in the input buffer from the keyboard
2F6BLD (HL),A77Save the character in Register A at the memory location of the input buffer pointer in HL
2F6FINC HL23Bump the value of the input buffer pointer in HL
2F70INC B04Bump the character position in Register B
2F71DEC D15Decrement the number of times to perform the operation in Register D (as initially specified by the user by entering a number before the command)
2F74RETC9RETurn to CALLer
2F75 - EDITCommand - HACK/INSERT Logic- "HED"
2F75-2F76- ↳ HEDLD (HL),00H36 00Set the line end to be the current position.
2F77LD C,B48Load Register C with the character position in Register B which will now be the line length
2F78-2F79- ↳ XEDLD D,0FFH16 FFPrepare for the next CALL to find the end of the line by loading Register D with the number of times to perform the operation
2F7D-2F7F- ↳ IEDGo get the character to be inserted from the keyboard
2F80OR AB7Check to see if a key was pressed
2F84-2F85CP 08HFE 08Check 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-2F87Jump to 2F92H if the character in Register A is a backspace character
2F88-2F89CP 0DHFE 0DCheck 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-2F8CJump to 2FE0H if the character in Register A is a carriage return
2F8D-2F8ECP 1BHFE 1BCheck 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.
2F8FRET ZC8Return if the character in Register A is shift up arrow
2F92 - EDITCommand - BACKSPACE CURSOR Logic- "TYPARW".
2F92-2F93- ↳ TYPARWLD A,08H3E 08Load Register A with a backspace the cursor character
2F94- ↳ TYPAR1DEC B05Decrement the character position in Register B
2F95INC B04Bump the character position in Register B
2F96-2F97If this is the first character of the BASIC line Jump forward to 2FB7H
2F9BDEC HL2BDecrement the value of the input buffer pointer in HL
2F9CDEC B05Decrement the character position in Register B
2FA0PUSH DED5Save 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- ↳ DELCHRPUSH HLE5Save the value of the input buffer pointer in HL to the STACK
2FA2DEC C0DDecrement the character position in Register C
2FA3- ↳ CMPRSSLD A,(HL)7ELoad Register A with the character at the location of the input buffer pointer in HL
2FA4OR AB7Check to see if the character in Register A is an end of the BASIC line character
2FA5SCF37Set the Carry flag to signal that DELCHR was called
2FA6-2FA8If the character in Register A is an end of the BASIC line character then we are done compressing so Jump to 0890H
2FA9INC HL23Bump the value of the input buffer pointer in HL
2FAALD A,(HL)7ELoad Register A with the character at the location of the input buffer pointer in HL
2FABDEC HL2BDecrement the value of the input buffer pointer in HL
2FACLD (HL),A77Save the character in Register A at the location of the current input buffer pointer in HL
2FADINC HL23Bump the value of the input buffer pointer in HL
2FB0 - EDITCommand - ADD A CHARACTER Logic- "NTARRW".
2FB0- ↳ NTARRWPUSH AFF5Save the character to be inserted in Register A to the STACK
2FB1LD A,C79Load Register A with the number of characters in the input buffer (i.e., the length of the line) in Register C
2FB2-2FB3CP FFHCP BUFLENFE FFWe 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-2FB5Jump forward to 2FB9H if the maximum BASIC line length hasn't been reached
2FB6POP AFF1Get the character to be inserted from the STACK and put it in Register A
2FB9- ↳ OKINSSUB B90Subtract 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
2FBAINC C0CBump the number of characters in the input buffer in Register C
2FBBINC B04Bump the character position in Register B
2FBCPUSH BCC5Save the character position and the number of characters in the input buffer in BC to the STACK
2FBDEX DE,HLEBLoad DE with the input buffer pointer in HL
2FBELD L,A6FLoad Register L with the number of bytes to move
2FBF-2FC0LD H,00H26 00Zero Register H so that the number of bytes to move can be done in a 16 bit Register Pair.
2FC1ADD HL,DE19Add the value of the input buffer pointer in DE to the character count in HL
2FC2LD B,H44Load Register B with the MSB of the end of the BASIC line pointer in Register H
2FC3LD C,L4DLoad Register C with the LSB of the end of the BASIC line pointer in Register L
2FC4INC HL23Bump the value of the end of the BASIC line pointer in HL
2FC8POP BCC1Get the character position and the number of characters in the input buffer from the STACK and put it in BC
2FC9POP AFF1Get the character to be inserted from the STACK and put it in Register A
2FCALD (HL),A77Save the character in Register A at the location of the current input buffer pointer in HL
2FCEINC HL23Bump the value of the input buffer pointer in HL
2FD2 - EDITCommand - BACKSPACE Logic- "DELED".
2FD2- ↳ DELEDLD A,B78Top 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
2FD3OR AB7Check to see if this is the start of the BASIC line
2FD4RET ZC8Return if this is the start of the BASIC line
2FD5DEC B05Decrement the character position in Register B
2FD6DEC HL2BDecrement the value of the buffer pointer in HL
2FD7-2FD8LD A,08H3E 08Load Register A with a backspace the cursor character
2FDCDEC D15Decrement the number of times to perform the operation in Register D
2FDFRETC9RETurn to CALLer
2FE0-2FE2- ↳ CREDSince 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
2FE6POP BCC1Clean up the STACK (to remove the DISPED return address)
2FE7POP DED1Get the BASIC line number (in binary) from the STACK and put it in DE
2FE8LD A,D7ALoad Register A with the MSB of the BASIC line number in Register D
2FE9AND EA3Combine the LSB of the BASIC line number in Register E with the MSB of the BASIC line number in Register A
2FEAINC A3CBump the combined BASIC line number in Register A
2FEB-2FED- ↳ EDITRTLD HL,(40A7H)LD HL,(BUFPNT)2A A7 40Load HL with the starting address of the input buffer.
Note: 40A7H-40A8H holds the input Buffer pointer
2FEEDEC HL2BDecrement the value of the input buffer pointer in HL
2FEFRET ZC8Return if this is the Level II BASIC command mode
2FF0SCF37Set the Carry flag to to fool the INSERT code; this flags the line number has having been seen
2FF1INC HL23Bump the value of the input buffer pointer in HL
2FF2PUSH AFF5Save the command mode flag in AF to the STACK
2FF6 - EDITCommand - QUIT Logic- "QED".
3000 - Jump Table.
3012 - Model 4 ROM Gen 1
3012 - Model 4 ROM Gen 2
*3024 - Model 4 ROM Gen 1
*3027RET
NOP
NOPI/O Re-Router was removed from the Model 4 ROM.
*3024 - Model 4 ROM Gen 2
*302D - Model 4 ROM Gen 1
*302D - Model 4 ROM Gen 2
*302DJP 375CHC3 5C 37Jump to 375CH for a routine which parses whether the current instruction on a the current line is in quotes.
3036Jump to 35A0H to put the TIME 10 characters from the upper right hand corner of the screen.
3039IN A,(0E4H)Poll Port E4H into A.
NOTE: Port E4H is the Non-Maskable Interrupt Latch.
303BBIT 5,ATest Bit 5 of Register A. Bit 5 of the NMI on an Input test is the RESET STATUS, with 0=False, and 1=True
*3042 - Model 4 ROM Gen 1 - Prompt the User to set the cassette baud rate.
*3042 - Model 4 ROM Gen 2 - Prompt the User to set the cassette baud rate.
*3045-3064 - Model 4 ROM Gen 1
*3045-305F@abcdefghijklmnopqrstuvwxyzKeyboard Rows 0-3, Unshifted, No Caps Lock
*3060NOPComputer version number, which is always 1 for a Model III and 0 for a Model 4
*3061NOP
*3062NOP
*3063NOP
*3064NOP
*3065-307D30 31 32 33 34 35 36 37 38 39 3A 3B 2C 2D 2E 2F 0D 1F 01 5B 0A 08 09 20 00Keyboard Rows 4-6, Unshifted, No Caps Lock
*307ENOP
*307FNOP
*3080NOP
*3081NOP
*3082NOP
*3083NOP
*3084NOP
*3085-309F60 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 5AKeyboard Rows 0-3, shifted, No Caps Lock
*30A0NOP
*30A1NOP
*30A2NOP
*30A3NOP
*30A4NOP
*30A5NOP
*30A6-30BC21 22 23 24 25 26 27 28 29 2A 2B 3C 3D 3E 3F 0D 1F 01 1B 1A 18 19 20Keyboard Rows 4-6, shifted, No Caps Lock
*30BDNOP
*30BENOP
*30BFNOP
*30C0NOP
*30C1NOP
*30C2NOP
*30C3NOP
*30C4NOP
*30C5-30DF40 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 5AKeyboard Rows 0-3, UNshifted, Caps Lock.
*30E0NOP
*30E1NOP
*30E2NOP
*30E3NOP
*30E4NOP
*30E5-30FC30 31 32 33 34 35 36 37 38 39 3A 3B 2C 2D 2E 2F 0D 1F 01 5B 0A 08 09 20Keyboard Rows 4-6, UNshifted, Caps Lock
*30FDNOP
*30FENOP
*30FFNOP
*3100NOP
*3101NOP
*3102NOP
*3103NOP
*3104NOP
*3105H - Model 4 Gen 1 jump to NON-Disk BASIC
GOSUB to the $SETCAS routine which prompts the user to set the cassette baud rate (310BH - 313AH)
*310BH - Model 4 Gen 1 Set the TAPE BAUD RATE ($SETCAS).
*310CGOSUB to 312DH which loads A with a carrage return, and jumps to 0033H to display it.
*3115GOSUB to 0049H.
NOTE: 0049H is the $KBWAIT routine which scans the keyboard and returns with the key pressed, if any, in register A.
*3118CP 0DHCompare A and 0D (a CARRIAGE RETURN).
*311CPUSH AFSave AF to the STACK.
NOTE: A currently holds the character pressed in response to the "CASS?" message.
*311DGOSUB to 0033H.
NOTE: 0033H is the character print routine, to put the character held in Register A at the current cursor position.
*3120POP AFRestore AF from the STACK.
NOTE: A will then hold the character pressed in response to the "CASS?" message.
*3121CP 48HCompare A with 48H (ASCII: H).
*3125CP 4CHCompare A with a 4CH (ASCII: L).
3129H - Model 4 Gen 1 Set the Selected Cassette Baud Rate as LOW SPEED
*3129XOR ASet 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
LD (4211H),ASave 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
*312FDisplay 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
Finish the cassette setup by calling a portion of the keyboard scan routine which CALLS the screen print routine at 34FDH
*313EXOR AClear Register A and All Flags
*313FRETReturn a null character.
*3140NOP
*3141NOP
*3142NOP
*3143NOP
*3144NOP
*3145 - Model 4 Gen 1 Printer Character Table Codes 32-127.
*3145-31A420 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.
*3045JP 378DHC3 8D 37Jump 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
*3048JP 377AHC3 7A 37Jump to 377AH to check to see if we are on a new printable page and set the pointers accordingly. CALLED from 0431 and 0445
*3045 - Model 4 ROM Gen 2 Keyboard Rows 0-3, Unshifted, No Caps Lock.
*304E-3068@abcdefghijklmnopqrstuvwxyz
*3069NOP
*306ANOP
*306BNOP
*306CNOP
*306DNOP
*306E-308530 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.
*3086IN A,(0EAH)DB EAPoll the RS-232 UART Control Register and Status Register (at Port EAH) and put the results into Register A
*3087OR AB7Set the FLAGS based on the RS-232 UART Control Register and Status Register
*308C-308DJR 30C6H18 38If 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.
**3086LD A,(3810H)3A 10 38Fetch the Keyboard Matrix 0-7 into Register A to check for certain keys.
**3089AND 10HE6 10MASK 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.
**306CNOP
**306DNOP
**306DNOP
*308E-30A8 - Model 4 ROM Gen 2 - Continuing with the Keyboard Table
*308E-30A860 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
*30A9NOP
*30AANOP
*30ABNOP
*30ACNOP
*30ADNOP
*30AENOP
*30AF-30C521 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-30C7IN A,(0EBH)DB EBPoll the RS-232 Register at Port EBH and put the results into Register A.
*30C8RETC9
**30C6-30C8 - Model 4 ROM Student Network Edition - A little code snippet
*30C6NOP
*30C7NOP
*30C8NOP
*3089 - Model 4 ROM Gen 2 - Continuing with the Keyboard Table
*30C9NOP
*30CA-30CDPUSH BC
POP BC
NOP
RETStandard code for a short delay
*30CE@ABCDEFGHIJKLMNOPQRSTUVWXYZ
*30E9NOP
*30EANOP
*30EBNOP
*30ECNOP
*30EDNOP
*30EE30 31 32 33 34 35 36 37 38 39 3A 3B 2C 2D 2E 2F 0D 1F 01 5B 0A 08 09 20Keyboard Rows 4-6, UNshifted, Caps Lock
*3106LD A,(3880H)3A 80 38Load A with the value held at 3880H (which are the SHIFT KEYS)
*3109LD HL,414FH21 F4 41Point HL to 414FH
*310CAND 7CHE6 7CMASK the value held in the SHIFT KEY RAM location against 0111 1100 to keep only bits 2, 3, 4, 5, and 6 live
*310EOR AB7OR A against itself to reset the flags
*310FJR Z,3142H28 31If the Z FLAG is set, JUMP to 3142H to continue checking fro special keys
*3111LD E,A5FPut the masked A into E.
*3112XOR (HL)AEToggle against the old image.
*3113LD (HL),E73Save the new image into (HL).
*3114AND EA3Mask Register E against Register A.
*3115JR Z,3157H28 40If ZERO then the LEFT SHIFT PRESSED was JUMP to 3157H to restart parsing the keyboard
*3117LD BC,05C4H01 C4 05Load BC with 05C4H to set up a 1/50 second delay for de-bounce.
*311ACALL 0060HCD 60 00GOSUB 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.
*311DLD A,(3880H)3A 80 38Load A with the value held at 3880H (which are the SHIFT KEYS)
*3120AND 7CHE6 7CMASK the value held in the SHIFT KEY RAM location against 0111 1100 to keep only bits 2, 3, 4, 5, and 6 live
*3122CP EBBCompare A with E.
*3126RLA17Rotate 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.
*3127RLA17Rotate 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.
*3128JR NC,312EH30 04We 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
*312ALD A,(41F3H)3A F3 41Load Register A with the value of the character to be returned when the F3 key is pressed
*312DRETC9RETurn to Caller
*312E - Model 4 Gen 2 - KEYBOARD Routine - Check and Process the F2 Key or Jump Away.
*312ERLA17Rotate 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.
*312FJR NC,3135H30 04If the NC FLAG is set, it isn't the F2 key, so JUMP to 3135H to test the F1 key
*3131LD A,(41ECH)3A EC 41Load Register A with the value of the character to be returned when the F2 key is pressed
*3134RETC9RETurn to Caller
*3135 - Model 4 Gen 2 - KEYBOARD Routine - Check and Process the F1 Key or Jump Away.
*3135RLA17Rotate 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.
*3136JR NC,313CH30 04If the NC FLAG is set, it isn't the F1 key, so JUMP to 313CH to keep checking special keys
*3138LD A,(41EBH)3A EB 41Load Register A with the value of the character to be returned when the F1 key is pressed
*313BRETC9RETurn to Caller
*33C4 - Model 4 Gen 2 - KEYBOARD Routine - Part of the Keyboard Scan Routine. Keep Checking Special Keys.
*313CRLA17Rotate 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.
*33CA - Model 4 Gen 1 - KEYBOARD Routine - Part of the Keyboard Scan Routine.
*3142LD (HL),A77Save the character held in Register A into the memory location pointed to by Register Pair HL
*3143LD A,FFH3E FFLoad A with FF to set up for a FLAG = 0FFH = NO CONTROL.
*3145LD HL,3840H21 40 38Load HL with 3840H to start a check for a DOWN ARROW.
*3148BIT 4,(HL)CB 66Test BIT 4 of (HL) to check for a DOWN ARROW.
*314AJR Z,3154H28 08JUMP to 3154H if the a DOWN ARROW was NOT pressed.
*314CSLA LCB 25Next we need to check for a a LEFT SHIFT so shift L left.
*314EBIT 0,(HL)CB 46Test BIT 0 of (HL) to check for a LEFT SHIFT.
*3152LD A,1FH3E 1FLoad A with 1F to set up for FLAG = CONTROL KEY.
*3154LD (4224H),A32 24 42Save the CONTROL FLAG into (4224H).
NOTE: 4224H Holds the CONTROL KEY flag.
*315ARET NCD0If that routine exited with NC FLAG set, RETurn
*315ECP 1AHFE 1ACheck A against 1AH to see if we have a SHIFT+DOWN ARROW.
*3163OR AB7Set the flags based on Register A
*3167LD HL,4224H21 24 42Set Register Pair HL to 4224H, which is the CONTROL KEY flag.
*316ABIT 7,(HL)CB 7ETest Bit 7 of the CONTROL KEY flag in RAM.
*316ECP 2AHFE 2ACheck A against a * key.
*3170JP Z,37C7HCA C7 37If 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.
*3173AND (HL)A6Prepare to check for a BREAK key by masking A against the memory contents of HL ...
*3174CP 01HFE 01... and COMPARING it to 01H.
*3176RET NZC0If the result of the compare is NOT zero, then RETURN.
*3177RST 28HEFIf we are here, then a BREAK key was hit, so call RST 28H to handle the BREAK key.
*3178RETC9RETURN to Caller
*3179 - Model 4 Gen 2 - Continuing Initialization Routine by setting up the RS-232.
*3179XOR AAFSet Register A to 0
*317AOUT (E8H),AD3 E8Output A to port E8H.
NOTE: Port E8H is the RS-232 Status Register & Master Reset. Outputting ANYTHING to Port E8H resets the RS-232.
*317CLD A,EEH3E EESet Register A to EEH for outputting to the RS-232 Baud Rate Select and Sense Switches: (Port E9H)
*317EOUT (E9H),AD3 E9Initialize 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
*3180LD A,6DH3E 6DSet Register A to 6DH for outputting to the RS-232 UART Control Register and Status Registe (Port EAH)
*3182OUT (EAH),AD3 EASent 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)
*3184IN A,(E8H)DB E8Poll the RS-232 Status Register and Master Reset of Port E8H and put the value into Register A.
*3186BIT 6,ACB 77Test Bit 6 of the RS-232 Status Register to check Data Set Ready (Pin 6 of the DB-25).
*3188JR Z,3184H28 FAIf the DATA SET READY is ZERO then LOP back 2 instructions to 3184 to poll again
*318ALD A,6CH3E 6CSet Register A to 6CH (0110 1100) for outputting to the RS-232 UART Control Register and Status Registe (Port EAH)
*318COUT (EAH),AD3 EASend 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
*318EIN A,(E8H)DB E8Poll the RS-232 Status Register and Master Reset of Port E8H and put the value into Register A.
*3190BIT 6,ACB 77Test Bit 6 of the RS-232 Status Register to check Data Set Ready (Pin 6 of the DB-25).
*3192JR NZ,318EH20 FAIf the DATA SET READY is ZERO then LOOP back 2 instructions to 318EH to poll again
*3194LD A,0FH3E 0FSet Register A to 0FH
*3196CALL 37D5HCD D5 37GOSUB to 37D5H to send the Character in Register A to the RS-232, once the RS-232 shows ready to accept that character.
*3199CALL 3086HCD 86 30GOSUB to 3086 to Poll the UART and wait for the P FLAG to not be set and then CONTINUE at 306CH
*319CCALL 3086HCD 86 30GOSUB to 3086 to Poll the UART and wait for the P FLAG to not be set and then CONTINUE at 306CH
*319FCALL 37D5HCD D5 37GOSUB to 37D5H to send the Character in Register A to the RS-232, once the RS-232 shows ready to accept that character.
*31A2-31A4JP 3517HC3 17 35JUMP 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
**3179LD A,(4210H)3A 10 42Get 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.
**317COR 10HF6 10OR against 10H (0001 0000) to turn all I/O ports on
**317ELD (4210H),A32 10 42Put the masked status back, first by loading it into 4210H
**3181OUT (ECH),AD3 EC... and then by sending it to 0ECH which is the same as 04210.
**3183LD A,08H3E 08Put an 08H into Register A
**3185OUT (D3H),AD3 D3Send 08H to Port D3H, which is the Network 4 Omninet MSB pointer
**3187XOR AAFPut an 00H into Register A
**3188OUT (D1H),AD3 D1Send 08H to Port D1H, which is the Network 4 Omninet LSB pointer
**318ALD BC,00D0H01 D0 00In 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
**318DLD HL,7000H21 00 70In further preparation for the INIR commands, point HL to the BUFFER for the code, which is 7000H
**3190PUSH HLE5Save the 7000H Buffer start point to the top of the stack
**3191INIRED B2Write a byte from port C to the memory location pointed to by HL, and then increase HL and decrease B.
**3193INIRED B2Write a byte from port C to the memory location pointed to by HL, and then increase HL and decrease B.
**3195INIRED B2Write a byte from port C to the memory location pointed to by HL, and then increase HL and decrease B.
**3197INIRED B2Write a byte from port C to the memory location pointed to by HL, and then increase HL and decrease B.
**3199INIRED B2Write a byte from port C to the memory location pointed to by HL, and then increase HL and decrease B.
**319BINIRED B2Write a byte from port C to the memory location pointed to by HL, and then increase HL and decrease B.
**319DINIRED B2Write a byte from port C to the memory location pointed to by HL, and then increase HL and decrease B.
**319FINIRED B2Write a byte from port C to the memory location pointed to by HL, and then increase HL and decrease B.
**31A1RETC9RETurn to CALLer
**31A2NOP00
**31A3NOP00
**31A4NOP00
31A5 - Output the TIMING MARK to the cassette
31A5LD A,01HLoad A with 01H. This is to prepare to send 0.46V to tape.
31A7OUT (0FFH),ALoad 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.
31A9LD B,0DHLoad B with 0DH in as a loop counter.
31ADLD A,02HLoad A with 01H. This is to prepare to send 0.0V to tape.
31AFOUT (0FFH),ALoad 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.
31B1LD B,0DHLoad B with 0DH in as a delay.
31B5GOSUB to 31F3 to send 0.46V to tape.
NOTE: 31F3H resets the cassette port, and then output a 0 to the Cassette Port FFH.
31B8LD B,78HLoad B with 78H in as a delay.
31BCRETRETURN.
31C0 - Turn Off The Cassette
31C0LD A,(4213H)Load A with the memory contents of 4213H.
NOTE: 4213H is the default interrupt vector setting for the cassette.
31C3OUT (0E0H),AOutput 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
31C5IN 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.
31C7LD 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.
31CAAND FDHMask A against FDH (1111 1101) to zero bit 1.
31CFEIEnable Interrupts.
31D0RETRETURN.
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 "**"
31D1EX DE,HLSwap DE and HL to remove the return address.
31D2EX (SP),HLSwap the memory contents pointed to by the STACK POINTER and HL (which is now what DE was).
31D3PUSH BCSave BC to the STACK.
31D4PUSH HLSave HL to the STACK.
31D5EX DE,HLSwap DE and HL back.
31D6IN 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.
31D8LD DE," "Load DE with SPACESPACE.
31DBLD (3C3EH),DELoad the screen memory location of 3C3EH with DE.
31E8 - Turn On The Cassette - Actually Set the Bit Mask and Output the Command
31E8LD 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.
31EBOR 02HOR A with 02H (0000 0010) to set Bit 1.
31EDLD (4210H),ASave 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.
31F0OUT (0ECH),AOutput that Bit Mask to port 0ECH.
31F2RETRETURN.
31F3 - Reset the Cassette Port. This routine OUTputs a 0 to the Cassette Port FFH
31F3XOR AWe want to reset the cassette port so we zero A.
31F4OUT (0FFH),ALoad 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.
31F6RETRETURN.
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.
31F7LD A,(HL)Load Register A with the memory contents pointed to by Register Pair HL.
31F8SUB 23HSubtract 23H so that we can test to see if the caller was a PRINT #command.
31FAIf 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.
3200RST 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.
3202RETIf we are here, then we have PRINT #n,so we RETURN.
3203 - Vector for a SLOW cassette read
3203LD B,08HLoad B with 8, representing the need to LOOP for 8 bits.
3205GOSUB to 3220H.
NOTE: 3220H reads the tape until it finds a timing mark or the BREAKis hit.
320ALD A,(4212H)Put the contents of 4212H into A.
NOTE: 4212H holds the cassette blinker counter.
320DINC ABump A.
320EAND 5FHMask A against 5F (0101 1111) to turn Bit 7 and Bit 5 off.
3210LD (4212H),APut A into 4212H.
NOTE: 4212H holds the cassette blinker counter.
3215LD A,(3C3FH)Load A with the screen contents at position 3C3FH.
3218XOR 0AHXOR 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).
321ALD (3C3FH),APut the revised A onto the screen at position 3C3FH.
321DLD A,DPut D (the byte) into A.
3220 - Cassette - Keep reading tape looking for a timing mark or BREAK.
3220PUSH BCSave BC to the STACK.
3221IN A,(FFH)Poll Port FFH with the results into Register A.
NOTE: FFH is the Cassette Port.
3223RLAPut 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).
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)
322ELD B,6EHLoad B with 6EH, which is the length of the timing mark.
Reset the cassette port
Next, wait for 98H Units (the length until a data pulse is expected)
3235LD B,98HLoad B with 98H, which is when the next data pulse should be available.
... continue
3239IN A,(FFH)Poll Port FFH and put the results into A.
NOTE: Port FFH is the Cassette Port.
323BPOP BCRestore BC from the STACK.
323CRLARotate 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.
323DRL DRotate 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.
3241 - Vector for a SLOW cassette write. On entry A is the byte to output.
3241PUSH AFSave AF to the STACK.
3242PUSH BCSave BC to the STACK.
3243PUSH DESave DE to the STACK.
3244LD C,08HLoad C with an 8, representing 8 bits to be written.
3246LD D,ALoad D with A.
NOTE: D will be the DATA BYTE.
324ARLC DRotate 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.
324CIf 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.
3251DEC CReduce the counter holding the number of bits to deal with by 1.
3254POP DERestore DE from the STACK.
3255POP BCRestore BC from the STACK.
3256POP AFRestore AF from the STACK.
3257RETRETURN.
3258 - "Write a 0 Bit" by simply waiting the appropriate amount of time and doing nothing.
325E - SLOW tape header write
325EPUSH HLSave HL to the STACK.
3262LD (420CH),HLLoad the memory contents of 420CH with HL.
NOTE: 420CH is the TAPE WRITE VECTOR.
3265LD B,53HLoad B with 53H (Decimal: 83) in prepartion to output 83 ZEROes.
3267XOR AClear A and all flags.
326DLD A,0A5HLoad A with A5H.
NOTE: A5H is the OUTPUT SYNC BYTE.
3274 - SLOW tape header read
3274PUSH HLSave HL to the STACK.
3278LD (420EH),HLPut the vector for a slow cassette read into the memory location at 420EH.
NOTE: 420EH is the TAPE READ VECTOR.
327BLD B,40HLoad B with 40H to set up a loop of 64 to try to find 64 zeroes.
327DLD D,00HLoad D with 0.
3282LD A,DLoad A with the D (the data byte) to begin to check the current data byte.
3283OR ASet up the flags.
3288GOSUB to 3220H.
NOTE: 3220H reads the tape until it finds a timing mark or the BREAKis hit.
328BLD A,DLoad A with the D (the data byte) to begin to check the current data byte.
328CCP 0A5HCompare A to A5H looking for a SYNC BYTE.
3290LD HL,"**"In preparation to display a "**", load HL with **.
3293LD (3C3EH),HLPut HL onto the screen at location 3C3EH.
3296LD A,HLoad A with H (which is a *.
3297POP HLRestore HL from the STACK.
3298POP BCRestore BC from the STACK.
3299POP DERestore DE from the STACK.
329ARETRETURN.
329B - FAST tape header write.
329BPUSH HLSave HL to the STACK.
329FLD (420CH),HLLoad the memory contents of 420CH with HL.
NOTE: 420CH is the TAPE WRITE VECTOR.
32A2LD B,00HLoad B with 00H to set up a loop of 256 to output 256 "55H" bytes.
32A4LD A,55HLoad A with "55H".
32A6GOSUB to 32B4H.
NOTE: 32B4 restore all registers from the STACK, and Fill C with A, and JUMP to cassette write
32ABLD A,7FHLoad A with 7FH.
NOTE: 7FH is the OUTPUT SYNC BYTE.
32ADGOSUB to 32B4H.
NOTE: 32B4 restore all registers from the STACK, and Fill C with A, and JUMP to cassette write
32B0LD A,A5HLoad A with A5H.
NOTE: A5H is the SLOW SYNC BYTE.
32B4 - Restore all registers from the STACK, and Fill C with A, and JUMP to cassette write.
32B4PUSH AFSave AF to the STACK.
32B5PUSH BCSave BC to the STACK.
32B6PUSH DESave DE to the STACK.
32B7LD C,ALoad C with A.
32BA - Save all registers to the STACK, and Fill C with A, GOSBUB to write out the START BIT ...
32BAPUSH AFSave AF to the STACK.
32BBPUSH BCSave BC to the STACK.
32BCPUSH DESave DE to the STACK.
32BDLD C,ALoad C with A.
32C1 - Call 3335H to Output a Bit 8 Times
32C1LD B,08HLoad B with an 8 to set up a loop for 8 bits.
32CA - Read the start bit, read 8 bits, check for error, and flash the star
32CDLD B,08HLoad B with an 8 to set up a loop for 8 bits.
32DA - FAST tape header read.
32DAPUSH HLSave HL to the STACK.
32DELD (420EH),HLLoad the TAPE READ VECTOR (memory location of 420EH) with the 32CAH.
32E1LD A,01HLoad A with a 1 to set the interrupt.
32E3OUT (0E0H),AOUTPUT 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
32E5LD B,80HSet up a loop of 80H (128) to try to read 128 bits.
32EALD A,CLoad A with C (which is the pulse width).
32EBCP 0FHCompare A to 0FH to see if the pulse width was too short.
32EFCP 3EHCompare A to 3EH to see if the pulse width was too long.
32F8LD B,40HSet up a loop of 40H (Decimal: 64) to try to read 64 bits.
3300LD D,CLoad D with C (which holds the delay count).
3304LD A,DLoad A with D to set up to find the difference in the delays.
3305SUB CSubtract C (the delay count) from A (which holds D).
3308NEGA = 0 - A.
330ACP 0DHCompare 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.
330CIf the CARRY FLAG is set, JUMP to 3313H which will bump L and continue the 64 bit loop.
330EINC HBump HL since we have one more zero bit.
3313INC LBump L since we have one more bit.
3316LD A,40HLoad A with 40H (64).
3318CP HCompare A with H to check for bits.
331BCP LCompare with A to check for one bits.
331ELD A,02HLoad A with 2 to set the interrupt vector.
3320OUT (0E0H),AOutput 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
3325LD D,00HZero D.
332DLD A,DLoad A with D (the read byte).
332ECP 7FHCompare A against 7F to check for a MARKER BYTE.
3335RLC CWe 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.
3341DEC DDecrement D as DELAY #1.
3344LD A,02HLoad A with a 2 to set up for a write of 0 VOLTS to TAPE.
3346OUT (0FFH),ALoad 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.
3348DEC EDecrement E as DELAY #2.
334BLD A,01HLoad A with 01H to prepare to send 0.85 VOLTS to TAPE.
334DOUT (0FFH),ALoad 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.
334FRETRETURN.
3350H - READ a BIT
3350EIEnable Interrupts.
3351LD C,00HLoad C with 0.
3353INC CBump C.
3354LD A,(3840H)Load A with the contents of 3840H so as to check for a BREAK.
3357AND 04HMask A with 4 (0000 0100).
335BDIDisable Interrupts.
335FLD (3C3EH),HLPut the "BK" in HL onto the video screen at 3C3EH.
3362JUMP 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
3365LD E,01HLoad E with 01H (Binary: 0000 0001) to make the bit go high.
3369LD E,00HLoad E with 0 to make the bit go low.
336BLD A,06HLoad A with 6.
336DADD A,CAdd A (6) to C, so COUNT = COUNT + 6.
336ELD C,APut the count held in A back into C.
336FIN A,(FFH)Poll FFH (to get the level) into A.
NOTE: FFH is the Cassette Port.
3371AND 01HMask A with 1 (0000 0001) to keep only Bit 0.
3373CP ECompare A with E (which was the set level).
3376POP AFRestore AF from the STACK.
3377POP AFRestore AF (the REMOTE CALLER'S ADDRESS) from the STACK.
3378RETRETURN to the caller's caller.
3379POP AFRestore AF from the STACK.
337AEIEnable Interrupts.
337BRETRETURN back top the loop.
337CH - Check for a Data Error.
337CLD A,CLoad A with C (the count).
337DCP 22HCompare 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
337FRL DWe 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.
3381CP 0FHMake 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.
3383If A < 0FH then it was too quick and we have a data error so JUMP forward a few instructions 3388H.
3385CP 3EHCompare A against 3EH to make sure it was not too slow.
3387RET CIf it wasn't too slow, RETURN.
3388LD A,44HIt was too slow, so load A with a D.
338ALD (3C3EH),APut the "D" on the screen at video location 3C3EH.
338DRETRETURN.
*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).
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).
*3391LD A,(3880H)Load A with the value held at 3880H (which are the SHIFT KEYS)
*3394AND 7CHMASK the value held in the SHIFT KEY RAM location against 0111 1100 to keep only bits 2, 3, 4, 5, and 6 live
*3396OR AOR A against itself to reset the flags
*3399LD E,APut the masked A into E.
*339AXOR (HL)Toggle against the old image.
*339BLD (HL),ESave the new image into (HL).
*339CAND EMask Register E against Rgister A.
*33A2GOSUB 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.
*33A5LD A,(3880H)Load A with the value held at 3880H (which are the SHIFT KEYS)
*33A8AND 7CHMASK the value held in the SHIFT KEY RAM location against 0111 1100 to keep only bits 2, 3, 4, 5, and 6 live
*33AACP ECompare A with E.
*33AERLARotate 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.
*33AFRLARotate 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.
*33B0We 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
*33B2LD A,(41F3H)Load Register A with the value of the character to be returned when the F3key is pressed
*33B5RETRETurn to Caller
*33B6 - Model 4 Gen 1 - KEYBOARD Routine - Check and Process the F2 Key or Jump Away.
RLARotate 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.
*33B9LD A,(41ECH)Load Register A with the value of the character to be returned when the F2key is pressed
*33BCRETRETurn to Caller
*33B6 - Model 4 Gen 1 - KEYBOARD Routine - Check and Process the F1 Key or Jump Away.
RLARotate 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.
*33BEIf the NC FLAG is set, it isn't the F1, so JUMP to 33C4 to keep checking special keys
*33C0LD A,(41EBH)Load Register A with the value of the character to be returned when the F1key is pressed
*33C3RETRETurn to Caller
*33C4 - Model 4 Gen 1 - KEYBOARD Routine - Part of the Keyboard Scan Routine. Keep Checking Special Keys.
RLARotate 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.
*33CA - Model 4 Gen 1 - KEYBOARD Routine - Part of the Keyboard Scan Routine.
LD (HL),ASave the character held in Register A into the memory location pointed to by Register Pair HL
33CBLD A,FFHLoad A with FF to set up for a FLAG = 0FFH = NO CONTROL.
33D0BIT 4,(HL)Test BIT 4 of (HL) to check for a DOWN ARROW.
33D4SLA LNext we need to check for a a LEFT SHIFTso shift L left.
33D6BIT 00H,(HL)Test BIT 0 of (HL) to check for a LEFT SHIFT.
33DALD A,1FHLoad A with 1F to set up for FLAG = CONTROL KEY.
33DCLD (4224H),ASave the CONTROL FLAG into (4224H).
NOTE: 4224H Holds the CONTROL KEY flag.
*33E2RET NCIf that routine exited with NC FLAG set, RETurn
33E6CP 1AHCheck A against 1AH to see if we have a SHIFT DOWN ARROW.
*33EBOR ASet the flags based on Register A
*33F2BIT 7,(HL)Test Bit 7 of the CONTROL KEY flag in RAM.
*33F6CP 2AHCheck A against a *.
*33FBAND (HL)Prepare to check for a BREAKby masking A against the memory contents of HL ...
*33FERET NZIf the result of the compare is NOT zero, then RETURN.
*33FFRST 28HIf we are here, then a BREAKwas hit, so call RST 28H to handle the BREAK.
*3400RETRETURN to Caller
*3401 - Model 4 Gen 1 - This is the BOOTSTRAP. Clears ports, checks for a BREAKkey and a Floppy Controller.
3401IM 1Set the INTERRUPT MODE to 1.
*3406LD B,0FHLet Register B = 0FH
*3408LD C,88HLet Register C = the CRT Controller Control Register Port
*340COUT (89H),ASend Register A to the CRT Controller Data Register
3410OUT (0E4H),AOutput A to Port E4H.
NOTE: Port E4H is the non-maskable interrupt latch. This is to clear the non-maskable interrupt status.
3412OR 20HOR 20H (0010 0000) against A to turn on Bit 5.
3414OUT (0ECH),AOutput 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.
3416LD A,81HLoad A with 81H (Decimal 129, Binary 10000001).
3418OUT (0F4H),AOutput 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.
341ALD A,D0HLoad A with D0H (Decimal: 208, Binary: 1101 0000).
341COUT (0F0H),AOutput "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
*341EPUSH BCUndertake a short delay of PUSHING BC, POPPING BC, and NOPing
*341FPOP BC
*3420NOP
3421LD A,04HLoad A with a 4 (Binary: 0000 0100).
3423OUT (0E0H),AOutput 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
3425LD A,0BHLoad A with 0BH (Binary: 00001011).
3427OUT (0F0H),AOutput 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).
3429Next we need to initialize some ports via a LDIR. The next 4 commands will move the 76 (4C) bytes from 36AAH to 4000H.
342C
342F
3432LDIR
3434Next we need to initialize more ports via a LDIR. The next 4 commands will move the 64 (40) bytes from 36F9H to 41E5H.
3437
343A
343DLDIR
3448IN 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
344AINC ABump the FDC Status by 1 (which would turn a FFH (128) into a 00H), making a Z flag mean "Disk Controller Not Ready".
344BIf the FDC Stats + 1 is zero, then we have no disk controller, so jump to Non-Disk BASIC at 3105H.
344EIf we are here, then we have a floppy controller, so set up for a loop of 65,535 times.
3451DEC BCDecrease BC by 1.
3452LD A,81HSet A to 81H (Decimal: 10000001).
3454OUT (0F4H),ASend 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.
3456LD A,B
OR CTo 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.
345BIN 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
345DBIT 2,ATest Bit 2 of A (the Floppy Status). If it is 0 then we haven't made it to track 0, otherwise we have.
345FStill 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.
3461LD E,05HSet up for a loop of 5 using Register E.
3466IN 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
3468BIT 01H,ACheck A to see if bit 1 (meaning "Drive Busy") is set.
346CDEC BCIf bit 1 is not set (meaning, the drive is NOT busy), reduce the counter (BC) by 1.
346DLD A,81HLoad A with 81H (Decimal: 129, Binary: 1000 0001).
346FOUT (0F4H),AOutput 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.
3471LD A,B
OR CTo 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.
3475If 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.
347BIf 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.
347DDEC EIf we are here, it found a diskette so next we need to find the index mark. First decrement E by 1.
3463HLD A,81HLoad A with 81H (Decimal: 129, Binary: 1000 0001).
3482OUT (0F4H),AOutput 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.
3484We 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.
3487LD (404AH),HL
348ALD A,C3HLoad A with C3H (Decimal: 195, Binary 1100 0011).
348CLD (4049H),APut the C3H into 4049H.
NOTE: 4049H is the Non-Maskable Interrupt Vector.
348FLD A,80HLoad A with 80H (Decimal: 128, Binary 1000 0000).
3491OUT (0E4H),AOutput "1000 0000" to Port E4H.
NOTE: Port E4H is the non-maskable interrupt latch.
3499LD A,01HPrepare to read Sector 1 by loading A with a 1.
349BOUT (0F2H),AOutput 1 to Port F2H.
NOTE: Port F2H is the Floppy Disk Controller Track Register.
349DLD A,80HPrepare to read a single sector by loading A with 80H (Decimal: 128, Binary: 1000 0000).
349FOUT (0F0H),AOutput "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).
34A1GOSUB to 37E1H to delay 3 instruction cycles, arguably the time it will take for the FDC to respond to the port command.
34A4IN 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
34A6AND 02HMask 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.
34ABINIInput the data byte.
34ADLD A,81HLoad A with 81H (Decimal: 129, Binary: 1000 0001) to select disk 0.
34AFOR 40HOR A with 40H (Binary: 0100 0000) to set A to double-density.
34B1OUT (0F4H),AOutput 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.
34B3INIInput the data byte.
34B5Loop 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.
34B8XOR AClear A and all flags.
34B9OUT (0E4H),ASend a 0 to Port E4H.
NOTE: Port E4H is the non-maskable interrupt latch. This is to clear the non-maskable interrupt status.
34BELD (4049H),HLLoad "45EDH" into 4049H.
NOTE: 4049H is the Non-Maskable Interrupt Vector.
34C1GOSUB to 37E1H to delay 3 instruction cycles, arguably the time it will take for the FDC to respond to the port command.
34C4IN 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
34C6POP HLClean up the Stack.
34C7AND 1CHMask 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.
34CCIf 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.
34CEIf 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.
34D1IN A,(E4H)Poll Port E4H into A.
NOTE: Port E4H is the Non-Maskable Interrupt Latch.
34D3BIT 5,ATest Bit 5 of Port E4 against A.
NOTE: Port E4H is the Non-Maskable Interrupt Latch.
*34DA - Model 4 Gen 1 - Part of the Keyboard routine.
*34DDAND 03HMask the SHIFT Register against 03H (Binary: 0000 0011) to keep only Bits 0 and 1 to check for shifts.
*34DFIf the masked A is 0, then we have no shifts, and skip the next instruction (to 34E3H).
*34E1SET 6,DSet BIT 6 of D to offset D for shifts.
LD A,(4019H)Load A with the contents of memory location 4019H to check for CAPS LOCK.
NOTE: 4019H is the CAPS LOCK TOGGLE.
*34E6OR ASet the flags.
*34E9SET 7,DSet 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.
*34EBLD A,(3880H)Put the contents of memory location 3880H into A to GET SHIFT(S).
*34EEAND 03HMask the SHIFT Register against 03H (Binary: 0000 0011) to keep only Bits 0 and 1 to check for shifts.
*34F0If the masked A is 0, then we have no shifts, and skip the next instruction (to 34F4H).
*34F2RES 7,DRESET 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.
34F7LD E,DWe need DE to be the OFFSET, so load E with D and ...
34F8LD D,00H... load D with 00.
34FAADD HL,DEAdd DE (the offset over the keyboard table) to HL (the keyboard table).
34FBLD A,(HL)Get the character pointed to by (HL) and put it into A.
*34FCRETRETurn 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.
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.
*3501RLCARotate the contents of A left one bit position. The contents of bit 7 are copied to the carry flag and bit 0.
*3504RLCARotate the contents of A left one bit position. The contents of bit 7 are copied to the carry flag and bit 0.
*3505If the CARRY FLAG is set, we have a non-graphic characters, so skip the next instructions.
*3507LD A,2EHOverwrite the current character held in Register A with a ., so that all graphic characters are printed as .'s.
Call the PRINT CHARACTER routine at 003BH (which sends the character in the A register to the printer).
*350FINC HLBump HL to the next character on the screen.
*3510BIT 6,HCheck 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).
*3515LD A,LPrepare to test of end of line by loading Register A with Register L.
*3516AND 03FHAND 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).
*3518If 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.
*351AGOSUB 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.
*351DLoop 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
*351FNOP
*3520NOP
*3521NOP
*3522NOP
*3523NOP
*3524NOP
*3525NOP
*3526NOP
*3527NOP
*3528NOP
*338EH - Model 4 Gen 2 - Jump Point for Keyboard Input.
*338ELD BC,3801H010138Load BC with 3801H (KEYBOARD ROW 0).
*3391LD HL,4036H213640Load HL with 4036H (BUFFER ROW 0).
*3394LD D,00H1600Load D with 0 (so D = ROW 0).
*3396LD A,(BC)0Aoad A with the contents held in (BC) to check the keyboard row.
*3397LD E,A5FLoad E with the contents held in (BC) to check the keyboard row.
*3398XOR (HL)AEXOR (HL) to set changed bits.
*3399LD (HL),E73Save the scan back into (HL).
*339AAND EA3Mask A with E (to mask the released keys).
Go to the next row
*339DINC D14Bump D so that D holds the NEXT row number.
*339EINC HL23Bump HL so that HL holds the NEXT buffer location.
*339FRLC CCB01We 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.
*33A1JP P,3396HF29633If 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
*33A4LD A,(41FDH)3AFD41If 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.
*33A7LD L,A6FLoad L with the contents of Register A.
*33A8LD A,(41FEH)3AFE41Load A with the memory contents of 41FEH.
NOTE: 41FEH is the saved IMAGE at the saved position in the keyboard scan data.
*33ABAND (HL)A6MASK A against (HL) to see if the previous keys are still pressed.
*33AFSBC HL,HLED62Zero HL by subtracting HL from HL.
*33B1LD (4201H),HL220142Load 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.
*33B7LD (41FFH),HL22FF41Load the memory location at 41FFH with 05DCH.
NOTE: 41FFH holds the keyboard scan repeat delay count.
*33BAXOR AAFClear A and all flags.
*33BBRETC9RETurn to Caller
*33BC - Model 4 Gen 2 - Keyboard Routine - If the same keys are still pressed then we need to deal with debounce.
*33BCLD E,A5FLoad E with A.
*33BDPUSH BCC5Save BC to the STACK.
*33C1CALL 0060HCD 60 00GOSUB 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.
*33C4POP BCC1Restore BC from the STACK.
*33C5LD A,(BC)0ALoad A with the memory contents pointed to by BC to re-check the keyboard.
*33C6AND EA3Compare A against E to check the pattern.
*33C7RET ZC8If not the same pattern then RETURN.
*33C8LD (41FEH),A32 FE 41If it is the same pattern then save A into (41FEH).
NOTE: 41FE is the SAVED IMAGE AT POSITION.
*33CBLD A,L7DLoad A with L (the scan position).
*33CCLD (41FDH),A32 FD 41Save A into (41FDH).
NOTE: 41FDH is the SAVED POSITION IN SCAN.
*33CFLD A,D7ALoad A with D (8 * ROW #).
*33D0RLA17Rotate 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.
*33D1RLA17Rotate 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.
*33D2RLA17Rotate 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.
*33D3LD D,A57Copy Register A into Register D
*33D4LD A,E7BCopy Register E into Register A
*33D5RRCA0FRotate A right one bit, with the contents of BIT 0 being put into BOTH the CARRY FLAG and BIT 7. D = 8* ROW # + KEY #.
*33D6RET CD8If the contents of BIT 0 of A was SET, RETurn
*33D7INC D14Bump D so that D holds the NEXT row number.
*33DA - Model 4 Gen 2 - Keyboard Routine.
*33DAPUSH HLE5Save HL to the top of the STACK.
*33DBLD HL,(4201H)2A0142Load HL with the repeat delay counter.
NOTE: 4201H is the REPEAT DELAY COUNTER.
*33DEINC HL23Bump HL.
*33DFLD (4201H),HL220142Load the memory location at 4201H with HL to clear the repeat counter.
NOTE: 4201H is the REPEAT DELAY COUNTER.
*33E2LD DE,(41FFH)ED5BFF41Load DE with the byte stored at 41FFH.
NOTE: 41FFH is the REPEAT DELAY COUNT.
*33E6SBC HL,DEED52Subtract with CARRY DE from HL.
*33E8POP DED1Restore old HL (which is what is in the stack) into DE.
*33ECXOR AAFClear A and all flags.
*33EDLD (DE),A12Put a 0 into the memory location pointed to (DE) to let the key be re-read.
*33EELD (4201H),HL220142Load the memory location at 4201H with HL to clear the repeat counter.
NOTE: 4201H is the REPEAT DELAY COUNTER.
*33F1LD L,96H2E96Load L with 96H to set a fast repeat count.
*33F3LD (41FFH),HL22FF41Save HL into the memory location at 41FFH.
NOTE: 41FFH is the REPEAT DELAY COUNT.
*33F9 - Model 4 Gen 2 - Enter NON-Disk BASIC.
*33FF - Model 4 Gen 2 Routine - Initialization - Process the CASS? Question
*33FFEIFBEnable Interrupts.
*3400CALL 3421HCD 21 34GOSUB to 3421H which loads A with a carrage return, and jumps to 0033H to display it.
*3403LD HL,05D1H21 D1 05Load HL with the address of the "CASS?" message.
*3406CALL 021BHCD 1B 02GOSUB to 021BH.
NOTE: 021BH will display the character at (HL) until a 03H is found.
*3409CALL 0049HCD 49 00GOSUB to 0049H.
NOTE: 0049H is the $KBWAIT routine which scans the keyboard and returns with the key pressed, if any, in register A.
*340CCP 0DHFE 0DCheck to see if Register A is holding a ENTER
*3410PUSH AFF5Save AF to the STACK.
NOTE: Register A currently holds the character pressed in response to the "CASS?" message.
*3411CALL 0033HCD 33 00GOSUB to 0033H pt display the character held in Register A at the current cursor position.
*3414POP AFF1Restore the answer to CASS? from the STACK into Register A.
*3415CP 48HFE 48Compare the answer to the CASS? Prompt held in Register A against with 48H (ASCII: H).
*3419CP 4CHFE 4CCompare the answer to the CASS? Prompt held in Register A against with 4CH (ASCII: L).
Set the flag for LOW SPEED CASSETTE
*341DXOR AAFSet A to 0.
*341ELD (4211H),A32 11 42Save 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
*3421LD A,0DH3E 0DPut a CARRIAGE RETURN into Register A
*3423JP 0033HC3 33 00JUMP 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.
*3426IM 1ED56Set the INTERRUPT MODE to 1.
*3428LD SP,407DH317D40Load the STACK POINTER with 407DH.
*342BLD B,0FH060FLet Register B = 0FH
*342DLD C,88H0E88Let Register C = the CRT Controller Control Register Port
*342FOUT (C),BED41Set the CRT Controller Control Register Port to the contents of B (which are decreasing as the loop progresses)
*3431OUT (89H),AD389Send Register A to the CRT Controller Data Register
*3435OUT (E4H),AD3E4Clear the Non-Maskable Interrupt Latch by sending the contents of Register A to Port E4H
*3437OR 20HF620OR 20H (0010 0000) against A to turn on Bit 5.
*3439OUT (ECH),AD3ECOutput 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.
*343BLD A,81H3E81Load A with 81H (Decimal 129, Binary 10000001).
*343DOUT (F4H),AD3F4Output 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.
*343FLD A,D0H3ED0Load A with D0H (Decimal: 208, Binary: 1101 0000).
*3441OUT (F0H),AD3F0Output "D0H" to the FDC Status Register at Port F0H. This resets the FDC and puts it in mode 1
*3443PUSH BCC5Undertake a short delay of PUSHING BC, POPPING BC, and NOPing
*3444POP BCC1Undertake a short delay of PUSHING BC, POPPING BC, and NOPing
*3445NOP00Undertake a short delay of PUSHING BC, POPPING BC, and NOPing
*3446LD A,04H3E04Load A with a 4 (Binary: 0000 0100).
*3448OUT (E0H),AD3E0Output 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.
*344ALD A,0BH3E0BLoad A with 0BH (Binary: 00001011).
*344COUT (F0H),AD3F0Output 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).
*344ELD HL,36AAH21AA36Next we need to initialize some ports via a LDIR. The next 4 commands will move the 76 (4C) bytes from 36AAH to 4000H.
*3451LD DE,4000H110040Set the LDIR Destination to 4000H
*3454LD BC,004CH014C00Set the number of bytes to move to 4CH
*3457LDIREDB0Move the 76 (4C) bytes from 36AAH to 4000H
*3459LD HL,36F9H21F936Next we need to initialize more ports via a LDIR. The next 4 commands will move the 64 (40) bytes from 36F9H to 41E5H.
*345CLD DE,41E5H11E541Set the LDIR Destination to 41E5H
*345FLD BC,0040H014000Set the number of bytes to move to 40H
*3462LDIREDB0Move the 64 (40) bytes from 36F9H to 41E5H.
The Model 4 ROM Student Network Edition ROM changes that to ...
*346AJP NZ,33F9HC2F933If we have a BREAK key then jump to Non-Disk BASIC at 33F9H.
*346DIN A,(F0H)DBF0We 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
*346FINC A3CBump the FDC Status by 1 (which would turn a FFH (128) into a 00H), making a Z flag mean "Disk Controller Not Ready".
*3470JP Z,33F9HCAF933If the FDC Stats + 1 is zero, then we have no disk controller, so jump to Non-Disk BASIC at 33F9H.
*3473LD BC,0000H010000If we are here, then we have a floppy controller, so set up for a loop of 65,535 times.
*3476DEC BC0BDecrease BC by 1.
*3477LD A,81H3E81Set A to 81H (Decimal: 10000001).
*3479OUT (F4H),AD3F4Send 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.
*347BLD A,B78To 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.
*347COR CB1
*3480IN A,(F0H)DBF0We 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
*3482BIT 2,ACB57Test Bit 2 of A (the Floppy Status). If it is 0 then we haven't made it to track 0, otherwise we have
*3484JR Z,3476H28F0Still 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.
*3486LD E,0AH1E0ASet up for a loop of 10 using Register E; this will be the number of tries to find an index mark.
*3488LD BC,0000H010000Set up for a loop of 65,536 using Register Pair BC.
*348BIN A,(F0H)DBF0Poll 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
*348DBIT 1,ACB4FCheck A to see if bit 1 (meaning "Drive Busy") is set.
*3491CALL 349BHCD9B34If 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.
*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.
*3496DEC E1DIf 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.
*3497JR NZ,34ACH2013So long as we have not run out of the loop of 10 tries, JUMP to 34ACH to find the index mark.
*349B - Model 4 Gen 2 Routine - We have exhausted our tries, so display DISKETTE and keep trying.
*349BDEC BC0BIf bit 1 is not set (meaning, the drive is NOT busy), reduce the counter (BC) by 1.
*349CLD A,81H3E 81Load A with 81H (Decimal: 129, Binary: 1000 0001).
*349EOUT (F4H),AD3 F4Output 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.
*34A0LD A,B78To 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.
*34A1OR CB1
*34A2RET NZC0If BC is not 0, then RETurn
*34A3POP HLE1Clear the STACK
*34A4LD HL,0277H21 77 02If 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.
*34A7CALL 021BHCD 1B 02GOSUB to 021BH. Note; 021BH will display the character at (HL) until a 03H is found.
*34AAJR 3486H18 DAIf 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.
*34ACLD BC,0000H010000Set up for a loop of 65,536 using Register Pair BC.
*34AFIN A,(F0H)DBF0Poll 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
*34B1BIT 1,ACB4FCheck the Floppy Disk Controller Status (held in Register A) to see if bit 1 (meaning "Drive Busy") is set.
*34B3JR Z,34BAH2805If bit 1 is not set (meaning, the drive is NOT busy), JUMP to 34BAH to restart the read attempts.
*34B5CALL 349BHCD9B34If we are here, then the drive was busy, so GOSUB to 349BH to display DISKETTE? and try again
*34B8JR 34AFH18F5JUMP back to the top of this routine and keep looking
*34BA - Model 4 Gen 2 Routine - Finish initializing the floppy disk boot.
*34BADEC E1DIf we are here, it found a diskette so next we need to find the index mark. First decrement E by 1.
We are going to try to read a sector
*34BDLD A,81H3E 81Load A with 81H (Decimal: 129, Binary: 1000 0001).
*34BFOUT (F4H),AD3 F4Output 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.
*34C1LD HL,34F5H21 F5 34We 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.
*34C4LD (404AH),HL22 4A 40Put the desired JUMP address of 34F5H into (404AH)
*34C7LD A,C3H3E C3Load A with C3H (Decimal: 195, Binary 1100 0011).
*34C9LD (4049H),A32 49 40Put the C3H into 4049H.
NOTE: 4049H is the Non-Maskable Interrupt Vector.
*34CCLD A,80H3E 80Load A with 80H (Decimal: 128, Binary 1000 0000).
*34CEOUT (E4H),AD3 E4Output "1000 0000" to the the non-maskable interrupt latch via Port E4H.
*34D0LD BC,00F3H01 F3 00Set BC to 0F3H.
NOTE: Port F3H is the Floppy Disk Controller Data Register.
*34D3LD HL,4300H21 00 43Set HL to 4300H, which is where the data is going to go.
*34D6LD A,01H3E 01Prepare to read Sector 1 by loading A with a 1.
*34D8OUT (F2H),AD3 F2Output 1 to the Floppy Disk Controller Track Register at Port F2H.
*34DALD A,80H3E 80Prepare to read a single sector by loading A with 80H (Decimal: 128, Binary: 1000 0000).
*34DCOUT (F0H),AD3 F0Output "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).
*34DECALL 30CAHCD CA 30GOSUB to 30CAH to delay 3 instruction cycles, arguably the time it will take for the FDC to respond to the port command.
*34E1IN A,(F0H)DB F0Poll 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
*34E3AND 02HE6 02Mask 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.
*34E8INIEDA2Input the data byte.
*34EALD A,81H3E81Load A with 81H (Decimal: 129, Binary: 1000 0001) to select disk 0.
*34ECOR 40HF640OR A with 40H (Binary: 0100 0000) to set A to double-density.
*34EEOUT (F4H),AD3F4Output 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.
*34F0INIEDA2Input the data byte.
*34F2JP 34EAHC3 EA 34Loop 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.
*34F5XOR AAFClear A and all flags.
*34F6OUT (E4H),AD3 E4Send a 0 to the non-maskable interrupt latch via Port E4H. This is to clear the non-maskable interrupt status.
*34F8LD HL,45EDH21 ED 45Now that the NMI jumped here, we need to set a new NMI jump, this time to 45EDH.
*34FBLD (4049H),HL22 49 40Load the destination location into (4049H) which is the Non-Maskable Interrupt Vector.
*34FECALL 30CAHCD CA 30GOSUB to 30CAH to delay 3 instruction cycles, arguably the time it will take for the FDC to respond to the port command.
*3501IN A,(F0H)DB F0Poll 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
*3503POP HLE1Clean up the Stack.
*3504AND 1CHE61CMask 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.
*3509JR 34BDH18B2If 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.
*350BJP NZ,4049HC24940If 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.
*350EIN A,(E4H)DBE4Poll the Non-Maskable Interrupt Latch at Port E4H into A.
*3510BIT 5,ACB6FTest Bit 5 of Port E4 against A.
*3512JR Z,350EH28FALoop back 2 instructions until it is set (i.e., not a zero).
*3517 - Model 4 Gen 2 Routine - Finish up initialization by filling 256 bytes into 4300H and then JUMPing there
*3517OR AB7Set the FLAGS based on Register A
*3518JP NZ,3179HC27931If A is not ZERO then Jump to 3179H to continuing initialization routine by setting up the RS-232
*351BLD HL,4300H210043Set HL to 4300H, which is where the data is going to go.
*351ECALL 3086HCD8630GOSUB to 3086 to Poll the UART and wait for the P FLAG to not be set and then CONTINUE at 306CH
*3521LD (HL),A77Store Register A into the memory location pointed to by Register Pair HL
*3522INC L2CBump Register L by 1
*3523JR NZ,351EH20F9So long as we have not overflowed L, LOOP back to 351EH
*3525JP 37E0HC3E037If 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).
*3528NOP00No Operation
**3517 - Model 4 ROM Student Network Edition - Finish up initialization
**3517CALL 3086HCD8630GOSUB to 3086 to simply poll the keyboard and mask for a "4" key
**351AJR NZ,3520H2004If the key isn't a "4" then restore BC and return to processing as a regular Model 4 would
**351CCALL 028DHCD8D02GOSUB to 028DH to check for a BREAK key.
**351FRETC9RETurn to CALLer
**3520POP BCC1Restore the contents at the top of the STACK into Register Pair BC
**3521JP 3179HC37931Jump to 3179H to continuing initialization routine by setting up the RS-232
**3524NOP00
**3525NOP00
**3526NOP00
**3527NOP00
**3528NOP00
3529H - Deal with the cursor.
352CPUSH DEPush that return address onto the Stack.
352DIN 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.
352FLD 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.
3532OR ASet the flags.
3535LD 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.
3538OR ATest A.
353BLoad HL with 401AH.
NOTE: 401AH is the memory location that stores the cursor blink count.
353EDEC (HL)Reduce the memory contents of (401AH) by one.
NOTE: 401AH is the memory location that stores the cursor blink count.
3541LD (HL),07HSet the cursor blink count to 7.
NOTE: 401AH is the memory location that stores the cursor blink count.
3543INC HLBump HL. This will increase HL from 401AH to 401BH.
NOTE: 401BH holds the cursor blink status - 0 = Off, Anything Else = On.
3544LD A,(HL)Poll the cursor blink status memory location and put the results into A.
3545AND 01HMask A against 0000 0001, to have only Bit 0 active.
3547XOR 01HXOR A with 01H.
3549LD (HL),APut the toggled cursor blink status into the appropriate memory location.
354ALD HL,(4020H)Poll (4020H) and put the result into HL.
NOTE: 4020H holds the current cursor position.
354DIf the current cursor position is 0, then it is off, so JUMP down 2 instructions to 3554H to make the cursor a blank (space).
354FLD A,(4023H)Load A with the memory contents of (4023H).
NOTE: 4023H holds the cursor character.
3552JUMP down to 3556H to skip the next instruction and continue this routine by displaying the character in A.
3554LD A,20HLoad 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.
3556LD (HL),APut the character held in A into the memory location pointed to by (HL).
NOTE: (HL) will hold the current cursor position.
355ADEC (HL)Decrease the number held at (4216H) by 1.
355BRET NZIf the number held at (4216H) is not zero, then RETURN.
355CLD (HL),1EHPut a 1EH (Decimal: 30) into (4216H).
NOTE: 4216H is the heartbeat counter.
355EINC HLBump HL by 1. HL will now point to 4217H, which is the memory location that holds the SECONDS.
355FLoad 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.
3562LD B,03HLoad B with a 03H, to set up a loop where we test seconds, minutes, and hours against their maximums.
3564INC (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.
3565LD 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.
3566SUB (HL)Compare the maximum to what we have by subtracting that maximum from the value pointed at in (HL).
3567RET NZIf there is no difference between what we have and the maximum then RETURN.
3568LD (HL),AIf 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).
3569INC HLBump 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.
356AINC DEBump 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.
356BLoop back to 3564H until the loop of 3 has been met, meaning that we have processed seconds, minutes, and hours.
356DINC HLBump HL one more time, to 421BH.
NOTE: 421BH holds the current DAY portion of the date.
356EINC (HL)Bump the DAY portion of the date.
356FINC HLBump HL one more time, to 421CH.
NOTE: 421BH holds the current MONTH portion of the date.
3570LD A,(HL)Get the month and put it into A.
3571DEC HLDecrease HL back to to 421BH.
NOTE: 421BH holds the DAY portion of the date.
3572DEC ADecrease A by one (to the previous month).
NOTE: DE currently points to the memory locations housing the of days in each month.
3573ADD A,EAdd E to A.
3574LD E,ALoad E with the result. E = E + A.
3575LD A,(DE)Poll the number of days in a month and put the result into A.
3576CP (HL)Compare A against (HL).
NOTE: If this is not a loop, the HL holds the day portion of the date.
3577RET NCIf 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.
3578LD 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.
3579CP 1EHCompare 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.
357BIf the carry flag isn't set then JUMP to 3583H to update the MONTH, but not the YEAR.
357DDEC HLDecrement HL to now point to 421AH.
NOTE: 421AH points to the current YEAR.
357ELD A,(HL)Load the YEAR into A.
357FINC HLBump HL to 421BH.
NOTE: 421BH points to the current DAY.
3580AND 03HMask A (which is holding the year) with 03H (Binary: 00000011) to test for a leap year.
3582RET ZRETURN if that mask showed that we are in the 4th year of a cycle (because 04 and higher are turned off).
3583LD (HL),01HPut a 1 into the memory location pointed to by HL (which is DAY).
3585INC HLBump HL to 421CH.
NOTE: 421BH points to the current MONTH.
3586INC (HL)Increase the current MONTH by 1.
3587LD A,(HL)Put the current MONTH into A.
3588SUB 0DHSubract 13 from A.
NOTE: This will test against a month 13. If A is < 13, then the CARRY FLAG will be set.
358ARET CIf it is NOT month 13 then RETURN to skip the next code which increases the YEAR.
358BLD (HL),01HIf we are here, then MONTH = 13, so set MONTH to 1.
358DDEC HLDecrement HL to 421BH.
NOTE: 421BH points to the current DAY.
358EDEC HLDecrement HL to 421AH.
NOTE: 421BH points to the current YEAR.
358FINC (HL)Bump the current year.
3590RETRETURN.
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.
3591LD 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.
3594BIT 0,ATest Bit 0 of A to see if the clock is on or off.
3596RET ZIf Bit 0 of A is ZERO, then return.
3597LD 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.
359ACP 1EHCompare the heartbeat counter against 1EH.
NOTE: A CP actually subtracts 1E from A without modifying A, but the flags are set accordingly.
359CRET NZIf the clock was not just updated, then RETURN.
359DIf 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.
35A3LD C,3AHLoad C with a :.
NOTE: This routine is also used to convert the date, and C will be swapped out to a /for that routine.
35A5LD B,03HLoad B with a 3.
NOTE: This is because we need to convert 3 numbers, so we will loop 3 times.
35A7LD 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.
35A8DEC DEDecrement DE to point to the next unit to be dealt with.
35A9LD (HL),2FHLoad the memory location pointed to by (HL) with 2F.
NOTE: 2F is a /which is also 1 character below a 0.
35ABINC (HL)Increase whatever is held in (HL). On the first iteration, this change the character at the screen location to a 0.
35ACSUB 0AHA = 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.
35B0ADD A,3AHIf 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.
35B2INC HLBump HL to point to the next location on the video screen. On the first iteration, this will be the 2nd digit of the HOUR.
35B3LD (HL),APut 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.
35B4INC HLBump HL to point to the next location on the video screen. On the first iteration, this will be the 3rd character.
35B5DEC BDecrement B to the next unit. On the first iteration, this will move from 3 to 2.
35B6RET ZIf we have processed all passes in the loop, RETURN.
35B7LD (HL),CIf we are here, then the routine has not yet looped 3 times, so put a :onto the screen.
35B8INC HLBump HL to point to the next location on the video screen.
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 /.
35BELD C,2FHLoad C with a /.
35C2H - Maskable Interrupt Handler.
35C2PUSH AFSave AF to the STACK.
35C3IN A,(E0H)Poll Port E0H which is the MASKABLE INTERRUPT LATCH and put the results into A.
35C5RRARotate 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.
35C9RRARotate 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.
35CAIf Bit 1 (which is now in the carry) is low then JUMP to 3369H (which is a cassette routine with E set to LOW).
35CDPUSH BCSave all the registers.
35CEPUSH DE
35CFPUSH HL
35D0PUSH IX
35D2PUSH IY
35D7PUSH HLPush HL to the STACK.
35D8RRARotate 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.
35D9If 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.
35DCRRARotate 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.
35DDIf 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.
35E0RRARotate 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.
35E1If 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.
35E4RRARotate 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.
35E5If 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.
35E8RRARotate 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.
35E9If 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.
35ECRRARotate 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.
35EDIf 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.
35F0POP HLRestore all registers.
35F1POP IY
35F3POP IX
35F5POP HL
35F6POP DE
35F7POP BC
35F8POP AF
35F9EIEnable Interrupts.
35FARETRETURN.
35FBH - RS-232 Initialization Routine. I'm [guessing] that IX is set to 41F5H
35FBDIDisable Interrupts so they don't interrupt this routine.
35FCIN 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)
35FECP FFHCompare A with FFH to see if the RS-232 exists.
3602XOR AFlip the RS-232 Port Results, just to get a non-zero result.
3603OUT (0E8H),AOutput A to port E8H.
NOTE: Port E8H is the RS-232 Status Register & Master Reset. Outputting ANYTHING to Port E8H resets the RS-232.
3605LD A,(IX+03H)Load the BAUD RATE CODE into A.
NOTE: 41F8H holds the baud rate code.
3608OUT (0E9H),AOutput 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
360ALD A,(IX+04H)Load the CONFIURATION CODE into A.
NOTE: 41F9 holds the RS-232 Configuration Code.
360DOR ASet the flags.
3610OUT (0EAH),AOutput 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)
3619LD A,(IX+05H)Load the WAIT SWITCH into A.
NOTE: 41FAH holds the RS-232 Wait Switch.
361COR ASet the flags.
361FSET 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
3623SET 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
362BOR ASet flags.
362ESET 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
3632SET 02H,(IY+04H)Set Bit 2 of 41F1H to set the ACTIVE FLAG.
3636IN 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
3638EIRe-Enable Interrupts.
3639RETRETURN.
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.
363AXOR AClear A and all flags.
363BLD B,04HLoad 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
363DLD C,0E8HLoad C with E8H.
363FOUT (C),ASend a 0 to the current port. On the first iteration, this is E8H (the RS-232 Status Register & Master Reset).
3641INC CBUMP C to the next port.
3647LD B,03HLoad B with 3 as a counter. 3 is for 3 bytes - 41E8H, 41E9H, and 41EAH.
3649LD (HL),00HLoad (HL) with 00H.
364BINC HLBump HL.
364ELoad HL with 41F0H.
NOTE: 41F0H is the 1 characer output buffer for the RS-232 Output DCB.
3651LD B,03HLoad B with 3 as a counter. 3 is for 3 bytes - 41F0H, 41F1H, and 41F2H.
3653LD (HL),00HLoad (HL) with 00H.
3655INC HLBump HL.
365AH - RS-232 Input Routine.
365ALoad IX with 41E5H.
NOTE: 41E5H is the DCB for RS-232 Input. 41E8H is the 1 Character RS-232 Input.
365EXOR AClear A and all Flags.
365FLD (IX+03H),ALoad (41E8H) with a Zero.
NOTE: 41E8H is the 1 Character RS-232 Input.
3662BIT 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.
3666RET ZIf the Driver is OFF, RETURN.
3667IN 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)
3669BIT 7,ATest Bit 7 of A.
NOTE: Bit 7 will be 1 if DATA READY (1=True).
366DBIT 1,(IX+04H)Test Bit 1 of 41E9H.
NOTE: Bit 1 of 41E9 contains the WAIT/NO WAIT of the RS-232 Input DCB.
3671RET ZIf its NO WAIT then RETURN, otherwise continue (to keep polling).
3677JUMP to 4203H if the BREAKkey was pressed.
NOTE: 4203H JUMPS to 022EH and is the break vector for tape and RS-232.
367AIN 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.
367CLD (IX+03H),ALoad (41E8H) with a A (the data from Port EBH).
NOTE: 41E8H is the 1 Character RS-232 Input.
367FRETRETURN.
3680H - RS-232 Output Routine.
3680Note. 41EDH is the RS-232 Output DCB, and 41F1H holds DRIVER ON/OFF in BIT 2, and WAIT/NO WAIT in BIT 1.
3684BIT 2,(IX+04H)Test Bit 2 of 41F1H to see if the RS-232 is active.
3688RET ZIf the RS-232 is NOT active, RETURN.
3689IN 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)
368BBIT 6,ATest Bit 6 of Port EAH to see READY TO SEND.
368FBIT 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.
3693RET ZIf RS-232 is NOT active, RETURN.
3699JUMP to 4203H if the BREAKkey was pressed.
NOTE: 4203H JUMPS to 022EH and is the break vector for tape and RS-232.
369CLD A,(IX+03H)Load A with the memory contents of 41F0H.
NOTE: 41F0H is RS-232 output buffer byte.
369FOR ATest A and Set Flags.
36A0If not zero, then there is a character in the buffer, so skip the next instruction and leave that byte in A.
36A2LD A,CLoad A with C [GET CHAR FROM DISPATCHER].
36A3OUT (0EBH),ASend 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.
36A5LD (IX+03H),00HLoad memory contents of the RS-232 output byte (at 41F0H) with a 0.
36A9RETRETURN.
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
36D5FFH
36D600HThis 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.
36D7
36DARST 00H
36DBNOP
36DCNOP
36DDXOR A
36DERET
36DFNOP
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
36F4NOP
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-370006 1BTwo 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-37061B 02 21 30 00 00 00
3707-370808 00Two 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-370E02 1B 30 55 6C FF
370F-371000 00Two 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-371F00 00 FF FF 00 00 C3 2E 02 C3 FA 35 C3 FA 35
3720-373041 32 03 32 28 03 3C 04 00 00 1E 00 00 00 00 00 00
3731-373300 00 00Three 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-373800 00 00 00 FF
*3739H - Model 4 Gen 1
*373FLD D,00HLoad D with 0 (so D = ROW 0).
*3742LD E,ALoad E with the contents held in (BC) to check the keyboard row.
*3743XOR (HL)XOR (HL) to set changed bits.
*3744LD (HL),ESave the scan back into (HL).
*3745AND EMask A with E (to mask the released keys).
*3748 - Model 4 Gen 1 routine to Go to the next Keyboard row
*3748INC DBump D so that D holds the NEXT row number.
*3749INC HLBump HL so that HL holds the NEXT buffer location.
*374ARLC CWe 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.
*374CIf 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
*374FLD 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.
*3752LD L,ALoad L with the A.
*3753LD 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.
*3756AND (HL)MASK A against (HL) to see if the previous keys are still pressed.
*375ASBC HL,HLZero HL by subtracting HL from HL.
*375CLD (4201H),HLLoad 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.
*3762LD (41FFH),HLLoad the memory location at 41FFH with 05DCH.
NOTE: 41FFH holds the keyboard scan repeat delay count.
*3765XOR AClear A and all flags.
*3766RETRETURN.
*3767 - Model 4 Gen 1 routine to Keyboard Repeat - Jumps Here if the same keys are still pressed.
*3768PUSH BCSave BC to the STACK.
*376CGOSUB 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.
*376FPOP BCRestore BC from the STACK.
*3770LD A,(BC)Load A with the memory contents pointed to by BC to re-check the keyboard.
*3771AND ECompare A against E to check the pattern.
*3772RET ZIf not the same pattern then RETURN.
*3773LD (41FEH),AIf it is the same pattern then save A into (41FEH).
NOTE: 41FE is the SAVED IMAGE AT POSITION.
*3776LD A,LLoad A with L (the scan position).
*3777LD (41FDH),ASave A into (41FDH).
NOTE: 41FDH is the SAVED POSITION IN SCAN.
*377ALD A,DLoad A with D (8 * ROW #).
*377BRLARotate 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.
*377CRLARotate 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.
*377DRLARotate 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.
*377ELD D,AD = A
*377FLD A,EA = E
RRCARotate A right one bit, with the contents of BIT 0 being put into BOTH the CARRY FLAG and BIT 7. D = 8* ROW # + KEY #.
*3781RET CIf the contents of BIT 0 of A was SET, RETurn
*3782INC DINC DBump D so that D holds the NEXT row number.
*3785 - Model 4 Gen 1 routine to Keyboard Repeat - Jumps Here if the same keys are still pressed.
*3786LD HL,(4201H)Load HL with the repeat delay counter.
NOTE: 4201H is the REPEAT DELAY COUNTER.
*3789INC HLBump HL.
*378ALD (4201H),HLLoad the memory location at 4201H with HL to clear the repeat counter.
NOTE: 4201H is the REPEAT DELAY COUNTER.
*378DLD DE,(41FFH)Load DE with the byte stored at 41FFH.
NOTE: 41FFH is the REPEAT DELAY COUNT.
*3791SBC HL,DESubtract with CARRY DE from HL.
*3793POP DERestore old HL (which is what is in the stack) into DE.
*3797XOR AClear A and all flags.
*3798LD (DE),APut a 0 into the memory location pointed to (DE) to let the key be re-read.
*3799LD (4201H),HLLoad the memory location at 4201H with HL to clear the repeat counter.
NOTE: 4201H is the REPEAT DELAY COUNTER.
*379CLD L,96HLoad L with 96H to set a fast repeat count.
*379ELD (41FFH),HLSave HL into the memory location at 41FFH.
NOTE: 41FFH is the REPEAT DELAY COUNT.
*37A4 - Model 4 Gen 1 routine jumped to from the the middle of the tokenize routine.
CP 22HIf 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 ".
*37A6If the character in register A is not a ", then JUMP forward a few instructions to 37B2H.
*37A8LD A,(409FH)Load A with the memory contents of 409FH.
NOTE: 409FH is the DATA FLAG.
*37ABXOR 01HXOR 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.
*37ADLD (409FH),ALoad memory location 409FH with a the XOR'd results.
NOTE: 409FH is the DATA FLAG.
*37B0LD A,22HLoad A with 22H, which is a ".
CP 3AHCompare 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.
*37B4If A is 3AH then JUMP to 06AAH.
NOTE: 06AAH is in the middle of the KEYBOARD DRIVER ENTRY ROUTINE.
*37B7LD A,(409FH)Otherwise, Load A with the memory contents of 409FH.
NOTE: 409FH is the DATA FLAG.
*37BBRRARotate 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.
*3792If Bit 7 of the DATA FLAG was set, then JUMP to 06A8H.
NOTE: 06A8H is in the middle of the KEYBOARD DRIVER ENTRY ROUTINE.
*37BERLARotate 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.
*37BFJUMP 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$)
RST 10HCall 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.
*37C3PUSH HLSave HL (the current position) to the STACK.
*37C4LD A,11HLoad A with 11H (Decimal: 17).
NOTE: This is to set up for a 17 Byte String.
*37C9LD HL,(40D4H)Load HL with the memory contents of (40D4H).
NOTE: 40D4 is the string pointer.
*37CFLD (HL),20HLoad the memory location pointed to by HL with a SPACE.
*37D1INC HLIncrement HL to move 1 character over.
*37D8 - Model 4 Gen 1 routine to Toggle Caps Lock.
*37DDXOR (HL)Invert the contents of the (4019H).
*37DELD (HL),AStore the inverted (i.e., toggled) contents of (4019H) back into (4019H)
*37E0RETRETurn to Caller.
*37E1 - Model 4 Gen 1 routine to do a very short delay routine
*37E5 - Model 4 Gen 1 routine Unused Code
*37E5NOP
*37E6NOP
*37E7NOP
*37E8RST 38H
*37E9RST 38H
*37EANOP
*37EBH - Model 4 Gen 1 routine to display the Copyright Message
*37F4 - Model 4 Gen 1 routine to enable the TIME$ Command
*37F7LD (4177H),HLLoad 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.
*37F7JUMP to 022EH to continue.
NOTE: 022EH will enable interrupts, show the READY prompt, and RETURN.
*37FD - Model 4 Gen 1 unused Code
*37FDPUSH HL
*37FEPUSH HL
*37FFRST 38H
*3739H - Model 4 Gen 2 - Part of the Keyboard Routine to check for SHIFT and CAPS LOCK and deal with them accordingly.
*3739LD A,(3880H)3A 80 38Load A with the value held at 3880H (which are the SHIFT keys)
*373CAND 03HE6 03Mask the SHIFT Register against 03H (Binary: 0000 0011) to keep only Bits 0 and 1 to check for shifts.
*373EJR Z,3742H28 02If the masked A is 0, then we have no shifts, and skip the next instruction (to 3742H).
*3740SET 6,DCB F2Set BIT 6 of D to offset D for shifts.
*3742LD A,(4019H)3A 19 40Load A with the contents of memory location 4019H to check for CAPS LOCK.
NOTE: 4019H is the CAPS LOCK TOGGLE.
*3745OR AB7Set the flags.
*3746JR Z,3753H28 0BIf the ZERO flag is set then there is NO CAPS LOCK so JUMP to 3753H to skip the next intructions.
*3748SET 7,DCB FASet 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.
*374ALD A,(3880H)3A 80 38Put the contents of memory location 3880H into A to GET SHIFT(S).
*374DAND 03HE6 03Mask the SHIFT Register against 03H (Binary: 0000 0011) to keep only Bits 0 and 1 to check for shifts.
*374FJR Z,3753H28 02If the masked A is 0, then we have no shifts, and skip the next instruction (to 3753H).
*3751RES 7,DCB BARESET 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.
*3753LD HL,304EH21 4E 30Load HL with 304EH (the KEYBOARD TABLES).
*3756LD E,D5AWe need DE to be the OFFSET, so load E with D and ...
*3757LD D,00H16 00... load D with 00.
*3759ADD HL,DE19Add DE (the offset over the keyboard table) to HL (the keyboard table).
*375ALD A,(HL)7EGet the character pointed to by (HL) and put it into A.
*375BRETC9RETurn 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
*375CCP 22HFE 22If 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 ".
*375EJR NZ,376AH20 0AIf the character in register A is not a ", then JUMP forward a few instructions to 376AH.
*3760LD A,(409FH)3A 9F 40Load A with the memory contents of 409FH.
NOTE: 409FH is the DATA FLAG.
*3763XOR 01HEE 01XOR 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.
*3765LD (409FH),A32 9F 40Load memory location 409FH with a the XOR'd results.
NOTE: 409FH is the DATA FLAG.
*3768LD A,22H3E 22Load A with 22H, which is a ".
*376ACP 3AHFE 3ACompare 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.
*376CJP NZ,06AAHC2 AA 06If A is NOT a : then JUMP to 06AAH.
NOTE: 06AAH is in the middle of the KEYBOARD DRIVER ENTRY ROUTINE.
*376FLD A,(409FH)3A 9F 40Otherwise, Load A with the memory contents of 409FH.
NOTE: 409FH is the DATA FLAG.
*3772RRA1FRotate 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.
*3773JP C,06A8HDA A8 06If Bit 7 of the DATA FLAG was set, then JUMP to 06A8H.
NOTE: 06A8H is in the middle of the KEYBOARD DRIVER ENTRY ROUTINE.
*3776RLA17Rotate 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.
*3777JP 06A3HC3 A3 06JUMP 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
*377ALD (IX+05H),05HDD 36 05 00Load the memory location pointed to by IX+5 with a 05H.
NOTE: IX+5 is the number of characters printed.
*377EINC (IX+04H)DD 34 04Bump the number of lines already printed (which is tracked in IX+04H)
*3781LD A,(IX+04H)DD 7E 04Fetch the number of lines already printed (which is tracked in IX+04H) into Register A
*3784CP (IX+03H)DD BE 03Compare the number of lines already printed (held in Register A) against the number of lines left in the page (tracked in IX+03H)
*3787RET NZC0If the number of lines already printed doesn't match the number of lines left in the page then RETURN
*3788LD (IX+04H),04HDD 36 04 01If they DO match, then set the number of lines already printed to 04H
*378CRETC9RETurn 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
*378DLD A,C79LET Register A = Register C.
*378ECP E0HFE E0Compare 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
*3790JP NC,0043HD2 43 00If 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).
*3793CP C0HFE C0If 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
*3795JP C,0063HDA 63 00If 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).
*3798SUB C0HD6 C0If 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.
*379ALD B,00H06 00LET Register B = 00H
*379CLD C,A4FLET Register C = Register A.
*379DLD HL,(4220H)2A 20 42LET Register Pair HL the memory contents of 4220H and 4221H.
NOTE: 4220H-4221H is the destination device nName for ROUTE Routine. 2 Bytes
*37A0ADD HL,BC09LET HL = HL + BC
*37A1LD C,(HL)4ELET Register C = the contents of the memory location pointed to by Register Pair HL.
*37A2JP 0063HC3 63 00JUMP 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
*37A5LD HL,3C00H21 00 3CLoad HL with the memory location for the beginning of the video RAM.
*37A8LD A,(HL)7EPut the character at the screen location stored in HL into A.
*37A9RLCA07Rotate 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.
*37ACRLCA07Rotate 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.
*37ADJR C,37B3H38 04If 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
*37AFLD A,2EH3E 2ELoad Register A with a . which is the character which will print instead of non-printable characters.
*37B3LD A,(HL)7EReplace the non-printable character with a .
*37B4CALL 003BHCD 3B 00Call the PRINT CHARACTER routine at 003B (which sends the character in the A register to the printer).
*37B7INC HL23Bump HL to the next character on the screen.
*37B8BIT 6,HCB 74Check 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).
*37BDLD A,L7DTest of end of line by loading A with L.
*37BEAND 3FHE6 3FAND 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).
*37C0JR NZ,37A8H20 E6If 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
*37A5 - Model 4 Gen 2 - Screen Print Routine BUT exits witH Register A at 0 and all FLAGS cleared
*37C7CALL 37A5HCD A5 37GOSUB to 37A5H for the screen print
*37CAXOR AAFClear Register A and all the flags
*37CBRETC9RETurn to CALLer
*37CC - Model 4 Gen 2 - TOGGLE the CAPS LOCK
*37CCLD A,01H3E 01Load Register A with an 01H
*37CELD HL,4019H21 19 40Load HL with 4019H, which is the CAPS LOCK TOGGLE in the keyboard DCB.
*37D1XOR (HL)AEXOR the contents of the CAPS LOCK TOGGLE in RAM against 01H; this effectively toggles it
*37D2LD (HL),A77Put the toggled CAPS LOCK TOGGLE back into RAM where such thing is stored
*37D3XOR AAFClear Register A and Clear all FLAGs
*37D4RETC9RETurn 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.
*37D5PUSH AFF5Preserve Registers A and F to the top of the STACK
*37D6IN A,(EAH)DB EAPoll the RS-232 UART Control Register/Status Register (via Port EAH) into A.
*37D8BIT 6,ACB 77Test Bit 6 of the RS-232 Register. It will be 1 if READY TO SEND
*37DAJR Z,37D6H28 FAIf the UART reported that READY TO SEND is FALSE, LOOP back to 37D6 and poll again.
*37DCPOP AFF1Restore Registers A and F from the top of the STACK
*37DDOUT (EBH),AD3 EBSend the contents held in Register A to the RS-232C Data Register (via Port E8H) so as to send A to the RS-232C.
*37DFRETC9RETurn 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).
*37E0LD A,6DH3E 6DLoad Register A with a 6DH (Binary: 0110 1101)
*37E2OUT (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)
*37E4JP (HL)E9Jump to whatever (HL) is pointing to.
*37E5 - Model 4 Gen 2 - UNUSED CODE.
*37E5NOP00
*37E6NOP00
*37E7NOP00
*37E8RST 38HFF
*37E9RST 38HFF
**37D5-37E9 - Model 4 ROM Student Network Edition - Replace all the above with NOPs
*37EA - Model 4 Gen 2 - BASIC TIMES (DATE$+" "+TIME$)
*37EARST 10HD7Call the EXAMINE NEXT SYMBOL routine at RST 10H.
*37EBPUSH HLE5Save HL (the current position) to the STACK.
*37ECLD A,11H3E 11Load A with 11H (Decimal: 17) to set up for a 17 Byte String.
*37F1LD HL,(40D4H)2A D4 40Load HL with the memory contents of (40D4H), which is the string pointer.
*37F7LD (HL),20H36 20Load the memory location pointed to by HL with a SPACE.
*37F9INC HL23Increment HL to move 1 character over.
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.
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
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
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).
NOTE:40AEH holds LOCATE/CREATE variable flag
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.
Note: 40FDH-40FEH holds Free memory pointer
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.
Note: 40AFH holds Current number type flag
Note: 40AEH holds LOCATE/CREATE variable flag
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.
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:
- Get an index
- Put number+1 down at the VARPTR
- Increase the VARPTR
- Decmrent the number of DIMs
- Go back to the LOOP until the number of DIMs hits Zero
Note: 40D8H-40D9H holds temporary storage location
Note: 40AEH holds LOCATE/CREATE variable flag
Note: 40FDH-40FEH holds free memory pointer
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.
Note: 40D8H-40D9H holds Temporary storage location
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
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.
NOTE:40AFH holds Current number type flag
27B3
LD C,L44
Note: 40F3H-40F4H is a temporary storage location
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- ↳ MEMXOR AAFZero Register A and the status flags
27CAPUSH HLE5Save the value of the current BASIC program pointer in HL to the STACK
27CB-27CDLD (40AFH),ALD (VALTYP),A32 AF 40Zero the number type flag.
NOTE:40AFH holds Current number type flag
27CE-27D0Determine how much space there is via a GOSUB to the FREroutine at 27D4H
27D1POP HLE1Get the value of the current BASIC program pointer from the STACK and put it in HL
27D2RST 10HCHRGETD7We 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.
27D3RETC9Return to BASIC
27D4-27F4 - LEVEL II BASIC FREROUTINE- "FRE"
27D4-27D6- ↳ FRELD HL,(40FDH)LD HL,(STREND)2A FD 40Load 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
27D7EX DE,HLEBLoad DE with the value of the free memory pointer in HL for subtraction
27DBADD HL,SP39Add the value in HL (which is zero) to the current value of the STACK pointer so that the STACK pointer is now in HL
27DCRST 20HGETYPEE7We 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-27DEIf that test shows we do NOT have a STRING (meaning this was really a MEMcall, jump to forward to 27ECH
27DF-27E1Free up the argument and set up to give some free string space
27E5-27E7LD HL,(40A0H)LD HL,(STKTOP)2A A0 40Load HL with the start of string space pointer / bottom of free space.
NOTE:40A0H-40A1H holds the start of string space pointer
27E8EX DE,HLEBLoad DE with the start of string space pointer in HL
27E9-27EBLD HL,(40D6H)LD HL,(FRETOP)2A D6 40Load 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- ↳ GIVDBLLD A,L7DPrepare to do HL = HL - DE. First, load Register A with the LSB of the next available location in string space pointer in Register L
27EDSUB E93Subtract 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
27EELD L,A6FLoad Register L with the LSB of the amount of string space remaining in Register A
27EFLD A,H7CLoad Register A with the MSB of the next available location in string space pointer in Register H
27F0SBC A,D9ASubtract 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
27F1LD H,A67Load Register H with the MSB of the amount of string space remaining in Register A
27F2-27F4Jump 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- ↳ POSLD A,(40A6H)LD A,(TTYPOS)3A A6 40Load Register A with the current cursor line position.
Note: 40A6H holds the current cursor line position
27F8- ↳ SNGFLTLD L,A6FLoad Register L with the value of the current cursor line position in Register A.
27F9XOR AAFZero Register A
27FA- ↳ GIVINTLD H,A67Load Register H with zero, so now HL is 00 + cursor position
27FE-2818 - LEVEL II BASIC USR(x)ROUTINE- "USRFN"
27FE-2780- ↳ USRFNGOSUB to DOS to see if DOS wants to deal with this
2801RST 10HCHRGETD7We 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-2804GOSUB to 252CH to evaluate the expression at the location of the current BASIC program pointer in HL and return with the result in ACCumulator
2805PUSH HLE5Save the value of the current BASIC program pointer in HL (=the address of the next element in the code string) to the STACK
2806-2808Load HL with the return address of 0890H which will clear the STACK before returning to BASIC
2809PUSH HLE5Save the value of the return address in HL to the STACK
280A-280CLD A,(40AFH)LD A,(VALTYP)3A AF 40Load Register A with the value of the current number type flag for the argument provided.
Note: 40AFH holds Current number type flag
280DPUSH AFF5Save the value of the current number type flag in Register A.
(02=INT, 03=STR, 04=SNG, 08=DBL)
280E-280FCP 03HFE 03Check 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-2812If the current result is a string then GOSUB to 29DAH to get the free the space and get the string address into HL
2813POP AFF1Restore the number type flag into Register A
2814EX DE,HLEBLoad DE with the (possible) value of the pointer to the string (in HL)
2815-2817LD HL,(408EH)2A 8E 40Load 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.
2818JP (HL)E9Jump 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- ↳ DOCNVFPUSH HLE5Save the pointer to the current character in the BASIC program being evaluated to the STACK
281A-281BAND 07HE6 07Mask 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-281ELoad HL with the address of the arithmetic conversion routines
281FLD C,A4FLoad Register C with the value of the number type flag in Register A.
(02=INT, 03=STR, 04=SNG, 08=DBL)
2820-2821LD B,00H06 00Zero Register B. Now BC holds 00 + type and will be the two byte offset into the table
2822ADD HL,BC09Add the offset (of BC) to the base arithmetic conversion routines, to find the right jump point
2823-2825GOSUB to 2586H to convert the current result in REG l to its proper number type
2826POP HLE1Restore the pointer to the current character in the BASIC program being evaluated to HL
2827RETC9RETurn 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- ↳ ERRDIRPUSH HLE5Save whatever was in HL to the STACK
2829-282BLD HL,(40A2H)LD HL,(CURLIN)2A A2 40Load HL with the value of the current BASIC line number (which is stored at 40A2H-40A3H).
282CINC HL23Bump 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
282DLD A,H7CLoad Register A with the MSB of the current BASIC line number in Register H
282EOR LB5Combine 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.
282FPOP HLE1Restore whatever was in HL on entry back into HL
2830RET NZC0Return 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-2832LD E,16H1E 16Load Register E with the ?ID ERRORcode
2836-2856 - STRING ROUTINE - STR$logic- "STR$"
2836-2838- ↳ STR$GOSUB to 0FBDH to convert the current result in ACCumulator to an ASCII string
2839-283B- ↳ STR$1Scan it and turn it into a string (make a temporary string work area entry)
283F-2841Load BC with a return address of 2A2BH (which cleans the STACK and then jumps to 2884H)
2842PUSH BCC5Save 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- ↳ STRCPYLD A,(HL)7ELoad Register A with the string's length at the location of the string's VARPTR in HL
2844INC HL23Bump the value of the string's VARPTR in HL
2845PUSH HLE5Save the value of the string's VARPTR in HL to the STACK
2846-2848GOSUB to 28BFH to test the remaining string area to make sure that the new string will fit
2849POP HLE1Reload HL with the string's VARPTR. This is the destination to where the string should be copied.
284ALD C,(HL)4ELoad Register C with the LSB of the string's address at the location of the string's VARPTR in HL
284BINC HL23Bump the value of the string's VARPTR in HL
284CLD B,(HL)46Load Register B with the MSB of the string's address at the location of the string's VARPTR in HL
284D-284FGOSUB to 285AH to save the string's length and the string's address at 40D3H, so as to set up DSCTMP
2850PUSH HLE5Save the pointer to STRAD2 (which is 40D3H) to the STACK
2851LD L,A6FLoad Register L with the string's length (from Register A)
2852-2854GOSUB to 29CEH to move L characters from the temp area (of BC) to the string data area (in DE)
2855POP DED1Restore the pointer to DSCTMP (40D3H) into Register Pair DE
2856RETC9RETurn to CALLer
2857-2864 - STRING ROUTINE- "STRINI"
2857-2859- ↳ STRINIGOSUB 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- ↳ STRAD2Load HL with the address of the temporary string parameter storage area.
Note: 40D3H-40D5H holds Used for temporary string VARPTR's
285D- ↳ STRAD1PUSH HLE5Save the address of the temporary string parameter area in HL to the STACK
285ELD (HL),A77Save the string's length in Register A at the location of the temporary string parameter storage pointer in HL
285F- ↳ PUTDEIINC HL23The 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
2860LD (HL),E73Save the LSB of the string's address in Register E at the location of the temporary string parameter storage pointer in HL
2861INC HL23Bump the value of the temporary string parameter storage pointer in HL
2862LD (HL),D72Save the MSB of the string's address in Register D at the location of the temporary string parameter storage pointer in HL
2863POP HLE1Get the address of the temporary string parameter storage area from the STACK and put it in HL
2864RETC9RETurn 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- ↳ STRLITDEC HL2BDecrement the value of the current BASIC program pointer in HL
2866-2867- ↳ STRLTILD B,22H06 22Load Register B with a "(which is really the end of the quote search character)
2868- ↳ STRLT3LD D,B50Load Register D with a "(which is really the ending search character)
2869- ↳ STRLT2PUSH HLE5Save 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-286BLD C,0FFH0E FFLoad Register C with a -1
286C- ↳ STRGETINC HL23Bump the value of the current BASIC program pointer in HL to skip over that initial "
286DLD A,(HL)7ELoad Register A with the character at the location of the current BASIC program pointer in HL
286EINC C0CBump the counter in Register C
286FOR AB7Check 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-2871Jump 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
2872CP DBACheck 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-2874Jump 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
2875CP BB8Check 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-2877Loop 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- ↳ STRFINCP 22HFE 22Check 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-287CIf 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)
287DEX (SP),HLE3Exchange the value of the current BASIC program pointer in HL with the string's address to the STACK
287EINC HL23Bump the string's address in HL until it points to the first character of the string
287FEX DE,HLEBLoad DE with the temporary pointer string's address in HL
2880LD A,C79Load Register A with the string's length from Register C
2881-2883GOSUB to 285AH to save the string's length and the string's address into 40D3H (i.e., DSCTMP)
2884-2886- ↳ PUTNEWLoad DE with the address of the string parameter storage area.
Note: 40D3H-40D5H holds Used for temporary string VARPTR's
2887-2888LD A,D5H3E D5This 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- ↳ PUTTMPPUSH DED5Save a pointer to the stat of the string to the STACK
2889-288BLD HL,(40B3H)LD HL,(TEMPPT)2A B3 40Load 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-288ELD (4121H),HLLD (FACLO),HL22 21 41Save 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-2890LD A,03H3E 03Load Register A with the string number type flag
2891-2893LD (40AFH),ALD (VALTYP),A32 AF 40Save the value in Register A as the current number type flag.
Note: 40AFH holds current number type flag
2897-2899Depending on how we got here, DE will be different. If the jump was into PUTTMP, then DE will NOT equal FRETOP.
289ARST 18HCOMPARDFWe 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-289DLD (40B3H),HLLD (TEMPPT),HL22 B3 40Save 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
289EPOP HLE1Get the value of the current BASIC program pointer from the STACK and put it in HL
289FLD A,(HL)7ELoad Register A with the character at the location of the current BASIC program pointer in HL
28A0RET NZC0Return 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-28A2LD E,1EH1E 1ELoad Register E with a ?ST ERRORcode
28A3-28A5Display 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- ↳ STROUIINC HL23Bump 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"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- ↳ STRPRTGOSUB to 29DAH to build a temporary string work area entry for the message pointed to by FACLO
28AD-28AFGo get the string's length in Register D and the string's address in BC
28B0INC D14Bump the value of the string's length in Register D in preparation for the following loop which starts with a DEC D
28B1- ↳ STRPR2DEC D15Top of a loop. Decrement the value of the string's length in Register D
28B2RET ZC8Return if all of the characters in the string have been sent to the current output device.
28B3LD A,(BC)0ALoad Register A with the character at the location of the string pointer in BC
28B4-28B6Go send the character in Register A to the current output device
28B7-28B8CP 0DHFE 0DCheck 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-28BBJump to 2103H if the character in Register A is a carriage return
28BCINC BC03Bump the value of the string pointer in BC
28BD-28BELoop 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- ↳ GETSPAOR AB7Make sure A is not zero. A ZERO FLAG will signal that garbage collection has happened
28C0-28C1LD C,0F1H0E F1Z-80 Trick. If passing through, the C just changes and the POP AF which follows is ignored.
28C1- ↳ TRYGI2POP AFF1Get the string's length from the STACK and put it in Register A
28C2PUSH AFF5Save the length of the string in Register A to the STACK
28C3-28C5LD HL,(40A0H)LD HL,(STKTOP)2A A0 40Load HL with the poinmter to the bottom of the string space. 40A0H-40A1H holds the start of string space pointer
28C6EX DE,HLEBMove the bottom of the string space pointer into DE. We don't care what happens to HL
28C7-28C9LD HL,(40D6H)LD HL,(FRETOP)2A D6 40Load HL with the pointer to the TOP of free space.
Note: 40D6H-40D7H holds the next available location in string space pointer
28CACPL2FComplement the string's length in Register A so that it is negative
28CBLD C,A4FThe 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-28CDLD B,0FFH06 FFLoad Register B with a -1 so that BC will be the negative length of the string
28CEADD HL,BC09Add the negative string's length in BC to the top of free space pointer in HL
28CFINC HL23Bump the value of the adjusted next available location in string space pointer in HL
28D0RST 18HCOMPARDFWe 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-28D2If 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-28D5LD (40D6H),HLLD (FRETOP),HL22 D6 40Save the value in HL as new bottom of MEMORY
28D6INC HL23Bump the value of HL to point to the string
28D7EX DE,HLEBLoad DE with the pointer to the string
28D8- ↳ PPSWRTPOP AFF1Get the string's length from the STACK and put it in Register A
28D9RETC9RETurn to CALLer
28DA-298E - STRING ROUTINE- "GARBAG"
28DA- ↳ GARBAGPOP AFF1Get the garbage collection code which was PUSHed
28DB-28DCLD E,1AH1E 1ALoad Register E with an ?OS ERRORcode
28DD-28DFIf 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
28E0CP ABFOtherwise, 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.
28E1PUSH AFF5Save the garbage collection flag to the STACK
28E2-28E4Load BC with a return address of 28C1H (which would retry allocation)
28E5PUSH BCC5Save that return address to the STACK
28E6-28E8- ↳ GARBA2LD HL,(40B1H)LD HL,(MEMSIZ)2A B1 40Load HL with the top of memory pointer.
Note: 40B1H-40B2H holds MEMORY SIZE? pointer
28E9-28EB- ↳ FNDVARLD (40D6H),HLLD (FRETOP),HL22 D6 40Save 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
28EFPUSH HLE5Save the value in HL to the STACK to indicate that we didn't find a variable on this pass
28F0-28F2LD HL,(40A0H)LD HL,(STKTOP)2A A0 40Load 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
28F3PUSH HLE5Save high address (start of string space pointer in HL) to the STACK
28F4-28F6Load HL with the start of the temporary string work area pointer.
Note: 40B5H-40D2H holds Temporary string work area
28F7
"TVAR"EX DE,HLEBLoad DE with the start of the temporary string work area pointer in HL
28F8-28FALD HL,(40B3H)LD HL,(TEMPPT)2A B3 40Load 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
28FBEX DE,HLEBExchange 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
28FCRST 18HCOMPARDFWe 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.
2900-2902Jump to 294AH to do the temporary variable garbage collection if the temporary string work area isn't empty
2903- ↳ SVARSLD HL,(40F9H)LD HL,(VARTAB)2A F9 40Load HL with the start of the simple variables pointer.
NOTE:40F9H-40FAH holds the starting address of the simple variable storage area
2906- ↳ SVAREX DE,HLEBLoad DE with the simple variables pointer in HL
2907-2909LD HL,(40FBH)LD HL,(ARYTAB)2A FB 40Load 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
290AEX DE,HLEBSwap DE and HL so that DE holds the end and HL holds the start.
290BRST 18HCOMPARDFNow 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-290DIf 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.
290ELD A,(HL)7ELoad Register A with second character of the variable name
290FINC HL23Bump the value of the simple variables pointer in HL
2910INC HL23Bump the simple variables pointer in HL
2911INC HL23Bump the value of the simple variables pointer in HL. HL should now point to the value of the variable.
2912-2913CP 03HFE 03Check 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-2915Jump to 291AH if the variable at the location of the simple variables pointer in HL isn't a string
2919XOR AAFZero Register A to indicate that we should not be skipping anything else.
291A- ↳ SKPVARLD E,A5FZero Register E
291B-291CLD D,00H16 00Zero Register D. Now DE should be the number of characters to skip.
291DADD HL,DE19Add the number of characters to skip (held in DE) to the simple variables pointer in HL
2920- ↳ ARYVA2POP BCC1Clean up the STACK
2921- ↳ ARYVAREX DE,HLEBLoad DE with the value of the array variables pointer (ARTVAR) in HL
2922-2924LD HL,(40FDH)LD HL,(STREND)2A FD 40Load HL with the end of the arrays / start of free memory pointer.
Note: 40FDH-40FEH holds Free memory pointer
2925EX DE,HLEBExchange the value of the array variables pointer in DE with the value of the free memory pointer in HL
2926RST 18HCOMPARDFWe 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-2929Jump if the array variables pointer in Register HL is the same as the start of the free memory pointer in DE
292ALD A,(HL)7ELoad Register A with the number type flag at the location of the array variables pointer in HL
292BINC HL23Bump the value of the array variables pointer in HL
292C-292EGet 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)
292FPUSH HLE5Save the pointer to the DIMS to the STACK
2930ADD HL,BC09Add the value of the offset to the next array in BC to the value of the array variables pointer in HL
2931-2932CP 03HFE 03Check 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-2934If the array being examined isn't a string then skip it via a JUMP to 2920H
2935-2937LD (40D8H),HLLD (TEMP3),HL22 D8 40Save the address of the end of the array being examined.
Note: 40D8H-40D9H holds Temporary storage location
2938POP HLE1Get the value of the array variables pointer from the STACK and put it in HL
2939LD C,(HL)4ELoad Register C with the number of subscripts for the array at the location of the array variables pointer in HL
293A-293BLD B,00H06 00Zero Register B so that BC holds the number of dimensions
293CADD HL,BC09Add the number of subscripts in the array in BC to the value of the array variables pointer in HL
293DADD HL,BC09Add 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.
293EINC HL23Bump the value of the array variables pointer in HL one more time to account for #DIMs.
293F- ↳ ARYSTREX DE,HLEBLoad DE with the value of the current position in the array variables pointer in HL
2940-2942LD HL,(40D8H)LD HL,(TEMP3)2A D8 40Load HL with the address of the end of this array.
Note: 40D8H-40D9H holds Temporary storage location
2943EX DE,HLEBSwap DE and HL so that HL now points to the current position
2944RST 18HCOMPARDFWe 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-2946If the Z FLAG is set then we are at the end of an array so JUMP to 2921H to try the next array
294A- ↳ DVAR2PUSH BCC5Save the value in BC to the STACK
294B- ↳ DVAR and DVARSXOR AAFZero Register A so we can test for a null string
294COR (HL)B6Load Register A with the length of the string at the location of the array variables pointer in HL
294DINC HL23Bump the value of the array variables pointer in HL
294ELD E,(HL)5ELoad Register E with the LSB of the string's address at the location of the array variables pointer in HL
294FINC HL23Bump the value of the array variables pointer in HL
2950LD D,(HL)56Load 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.
2951INC HL23Bump the value of the array variables pointer in HL
2952RET ZC8Return if the string's length in Register A is equal to zero
2953
2954LD B,H
LD C,L44Let BC = HL
2955-2957LD HL,(40D6H)LD HL,(FRETOP)2A D6 40Load HL with the location of the the top of string free space.
NOTE:40D6H-40D7H holds the next available location in string space pointer
2958RST 18HCOMPARDFWe 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
295ALD H,B
LD L,C60Let HL = BC
295BRET CD8If 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
295CPOP HLE1Get the return address from the STACK and put it in HL
295DEX (SP),HLE3Swap (SP) and HL so that the return address is back on the STACK and HL holds the maximum number seen
295ERST 18HCOMPARDFNow 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.
295FEX (SP),HLE3Swap (SP) and HL so that the return address is back in HL and the STACK holds the maximum number seen
2960PUSH HLE5Save the return address in HL to the STACK
2961
2962LD H,B
LD L,C60Let HL = BC
2963RET NCD0Return if the string's address in DE is below the string space pointer
2964POP BCC1Get the return address from the STACK and put it in BC
2965POP AFF1Clean up the STACK (remove the MAX SEEN)
2966POP AFF1Clean up the STACK (remove the VARIABLE POINTER)
2967PUSH HLE5Save the value of the array variables pointer in HL to the STACK
2968PUSH DED5Save the new MAX pointer in DE to the STACK
2969PUSH BCC5Save the value of the return address in BC to the STACK
296ARETC9RETurn to CALLer
If we are here, we have made one complete pass through the string variables.
296B- ↳ GRBPASPOP DED1Load DE with the address of the last string put into the temporary string work area (aka the MAX pointer)
296CPOP HLE1Get the variable pointer from the STACK and put it in HL
296DLD A,L7DLoad Register A with the LSB of the variable pointer in Register L
296EOR HB4Combine 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
296FRET ZC8If HL=0 then we are at the end of the garbage collection, so return
2970DEC HL2BDecrement the value of the temporary string work area pointer in HL, currently just past the string descriptor
2971LD B,(HL)46Load Register B with the MSB of the string's address at the location of the temporary string work area pointer in HL
2972DEC HL2BDecrement the value of the temporary string work area pointer in HL
2973LD C,(HL)4ELoad 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.
2974PUSH HLE5Save 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.
2975DEC HL2BDecrement the value of the temporary string work area pointer in HL
2976LD L,(HL)6ELoad Register L with the string's length at the location of the temporary string work area pointer in HL
2977-2978LD H,00H26 00Zero Register H so that HL is now the string's character count
2979ADD HL,BC09Add the length of the string in HL to the string's address in BC. HL now points just beyond the string.
297ALD D,B50Load Register D with the MSB of the string's address in Register B
297BLD E,C59Load Register E with the LSB of the string's address in Register C. DE now is the original pointer to the string.
297CDEC HL2BDecrement the value of the string's ending address in HL to avoid moving one beyond the string
297DLD B,H44Load Register B with the MSB of the string's ending address in Register H
297ELD C,L4DLoad Register C with the LSB of the string's ending address in Register L. BC now points to the top of the string
297F-2981LD HL,(40D6H)LD HL,(FRETOP)2A D6 40Load HL with the top of free space.
NOTE:40D6H-40D7H holds the next available location in string space pointer
2982-2984Move the string from the temporary storage location to string space
2985POP HLE1Get back the pointer to the description of the variable from the STACK and put it in HL
2986LD (HL),C71Save the LSB of the string's permanent address (held in Register C) to (HL)
2987INC HL23Bump the value of the temporary string work area pointer in HL
2988LD (HL),B70Save the MSB of the string's permanent address (held in Register C) to (HL)
2989LD L,C69Load Register L with the LSB of the string's address in Register C
298ALD H,B60Load 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.
298BDEC HL2BDecrement the string's address in HL to adjust FRETOP
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- ↳ CATPUSH BCC5Save the precedence/operator value in BC to the STACK
2990PUSH HLE5Save the value of the current BASIC program pointer in HL to the STACK
2991-2993LD HL,(4121H)LD HL,(FACLO)2A 21 41Load HL with the first string's VARPTR (from ACCumulator)
2994EX (SP),HLE3Exchange the value of the first string's VARPTR in HL with the value of the current BASIC program to the STACK
2995-2997GOSUB to 249FH to evaluate the expression at the location of the current BASIC program pointer in HL
2998EX (SP),HLE3Exchange the value of the current BASIC program pointer in HL with the value of the first string's VARPTR to the STACK
299CLD A,(HL)7ELoad Register A with the first string's length at the location of the first string's VARPTR in HL
299DPUSH HLE5Save the value of the first string's descriptor (VARPTR) in HL to the STACK
299E-29A0LD HL,(4121H)LD HL,(FACLO)2A 21 41Load HL with the second string's VARPTR in ACCumulator
29A1PUSH HLE5Save the second string's VARPTR in HL to the STACK
29A2ADD A,(HL)86Add 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-29A4LD E,1CH1E 1CLoad Register E with a ?LS ERRORcodein case the two string lengths are not less than 256.
29A5-29A7Display a ?LS ERRORmessage if the combined lengths of the strings is greater than 255
29A8-29AAGo make sure that there is enough string space for the new string
29ABPOP DED1Get the second string's VARPTR from the STACK and put it in DE
29AFEX (SP),HLE3Exchange the second string's VARPTR in HL with the first string's VARPTR to the STACK
29B3PUSH HLE5Save the value of the first string's VARPTR in HL to the STACK
29B4-29B6- ↳ INCSTRLD HL,(40D4H)LD HL,(DSCTMP+1)2A D4 40Load HL with the pointer to the first string's address
29B7EX DE,HLEBLoad DE with the first string's address in HL
29C1EX (SP),HLE3Exchange the value of the return address in HL with the value of the current BASIC program pointer to the STACK
29C2PUSH HLE5Save the value of the current BASIC program pointer in HL to the STACK
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- ↳ MOVINSPOP HLE1Load HL with the value of the return address to the STACK
29C7EX (SP),HLE3Swap (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.
29C8LD A,(HL)7ELoad Register A with the string's length at the location of the string's VARPTR in HL
29C9INC HL23Bump the value of the string's VARPTR in HL
29CALD C,(HL)4ELoad Register C with the LSB of the string's address at the location of the string's VARPTR in HL
29CBINC HL23Bump the value of the string's VARPTR in HL
29CCLD B,(HL)46Load 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.
29CDLD L,A6FLoad Register L with the string's length in Register A
29CE- ↳ MOVSTRINC L2CIncrement the value of the string's length in Register L in preparation for the next instruction which is a loop.
29CF- ↳ MOVLPDEC L2DDecrement the value of the string's length in Register L
29D0RET ZC8If L hits zero then we have moved all the characters, so RETurn
29D1LD A,(BC)0AIf 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
29D2LD (DE),A12Save the character in Register A at the location of the string storage pointer in DE
29D3INC BC03Bump the value of the string pointer in BC
29D4INC DE13Bump the value of the string storage pointer in DE
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- ↳ FRESTRGOSUB to 0AF4H to make sure that the current result in REG l is a string
29DA-29DC- ↳ FREFACLD HL,(4121H)LD HL,(FACLO)2A 21 41Load HL with the string's VARPTR in ACCumulator
29DD- ↳ FRETM2EX DE,HLEBLoad DE with the value of the string's VARPTR in HL
29DE-29E0- ↳ FRETMPCheck to see if the string is the last entry in the temporary string work area
29E1EX DE,HLEBLoad HL with the value of the string's VARPTR in DE
29E2RET NZC0Return if the string isn't the last entry in the temporary string work area
29E3PUSH DED5Save the value of the string's VARPTR in DE to the STACK
29E4LD D,B50Load Register D with the MSB of the string's address in Register B
29E5LD E,C59Load Register E with the LSB of the string's address in Register C. DE now points to the string.
29E6DEC DE1BDecrement the value of the string's address in DE
29E7LD C,(HL)4ELoad Register C with the string's length at the location of the string's VARPTR in HL
29E8-29EALD HL,(40D6H)LD HL,(FRETOP)2A D6 40Load 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
29EBRST 18HCOMPARDFWe 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-29EDJump 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
29EELD B,A47If 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
29EFADD HL,BC09Add the length of the string in BC to the next available location in string space pointer in HL
29F0-29F2LD (40D6H),HLLD (FRETOP),HL22 D6 40Save the adjusted next available location in string space pointer in HL.
Note: 40D6H-40D7H holds the next available location in string space pointer
29F3- ↳ NOTLSTPOP HLE1Get the string's VARPTR from the STACK and put it in HL
29F4RETC9RETurn 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- ↳ FRETMSLD HL,(40B3H)LD HL,(TEMPPT)2A B3 40Load HL with the temporary string work area pointer.
Note: 40B3H-40B4H holds the next available location in the temporary string work area pointer
29F8DEC HL2BDecrement the value of the temporary string work area pointer in HL which backs up two words
29F9LD B,(HL)46Load Register B with the MSB of the string's address at the location of the temporary string work area pointer in HL
29FADEC HL2BDecrement the value of the temporary string work area pointer in HL
29FBLD C,(HL)4ELoad Register C with the LSB of the string's address at the location of the temporary string work area pointer in HL
29FCDEC HL2BDecrement the value of the temporary string work area pointer in HL
29FDRST 18HCOMPARDFWe 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.
29FERET NZC0If the Z FLAG is set then we are done freeing space, so RETURN
29FF-2A01LD (40B3H),HLLD (TEMPPT),HL22 B3 40Save 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
2A02RETC9RETurn to CALLer
2A03-2A0E - LEVEL II BASIC LENROUTINE- "LEN"
2A06PUSH BCC5Save the return address of 27F8H (in BC) to the STACK
2A07-2A09- ↳ LEN1GOSUB to 29D7H to free up the temporary variable pointed to by FACLO
2A0AXOR AAFZero Register A so as to force a numeric flag
2A0BLD D,A57Zero Register D so that DE can be used when E is set.
2A0CLD A,(HL)7ELoad Register A with the string's length at the location of the string's VARPTR in HL
2A0DOR AB7Set the flags according to the string's length in Register A
2A0ERETC9RETurn to CALLer
2A0F-2A1E - LEVEL II BASIC ASCROUTINE- "ASC"
2A12PUSH BCC5Save the return address of 27F8H (in BC) to the STACK
2A13-2A15- ↳ ASC2GOSUB 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-2A18If the Z FLAG is set, then this was a null string (bad argument), so display a ?FC ERROR
2A19INC HL23Bump the value of the string's VARPTR in HL
2A1ALD E,(HL)5ELoad Register E with the LSB of the address of the string's data at the location of the string's VARPTR in HL
2A1BINC HL23Bump the value of the string's VARPTR in HL
2A1CLD D,(HL)56Load Register D with the MSB of the address of the string's data at the location of the string's VARPTR in HL
2A1DLD A,(DE)1ALoad Register A with the first character at the location of the string pointer in DE
2A1ERETC9RETurn to CALLer
2A1F-2A20- ↳ CHR$LD A,01H3E 01Load Register A with the length of the string to be created
2A21-2A23GOSUB to 2857H to save the string's length in Register A and value and set up the string's address
2A24-2A26GOSUB 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- ↳ SETSTRLD HL,(40D4H)LD HL,(DSCTMP+1)2A D4 40Load HL with the temporary string's address
2A2ALD (HL),E73Save the character in Register E at the location of the string pointer in HL
2A2B- ↳ FINBCKPOP BCC1Clean up the STACK so that the RETURN address is another one that is next in the STACK.
2A2F-2A60 - LEVEL II BASIC STRING$ROUTINE- "STRNG$"
2A2FRST 10HCHRGETD7We 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-2A31RST 08H ⇒ 28SYNCHK "("CF 28Since 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-2A34This 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
2A34DEC HL2BBackspace the code string. This is a Z-80 trick because this code was part of the above call instruction
2A35PUSH DED5Save the string's length ("N") (currently in DE) to the STACK
2A36-2A37RST 08 ⇒ 2CSYNCHK ","CF 2CNow 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-2A3ANow 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-2A3CRST 08H ⇒ 29SYNCHK ")"CF 29Now 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).
2A3DEX (SP),HLE3We 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
2A3EPUSH HLE5Save the string's length ("N") in HL to the STACK so that we can test it to make sure it is an integer
2A3FRST 20HGETYPEE7We 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.
2A42-2A44We 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-2A46Skip the next instruction (which would load the string address and 1st character) by jumping to 2A4AH
2A47-2A49- ↳ STRSTRGo get the first character in the string and return with it in Register A. This is the character that will be repeated
2A4A- ↳ CALSPAPOP DED1Get the "N" from the STACK and put it in DE (well, actually, Register E)
2A4BPUSH AFF5Save the character for the string (held in Register A) to the STACK
2A4C- ↳ SPACE2PUSH AFF5and then save it to the STACK again
2A4DLD A,E7BLoad Register A with "N" (held in Register E)
2A4E-2A4FGOSUB to 2857H to allocate N bytes in the temporary string work area
2A51LD E,A5FLoad Register E with "N" (held in Register A)
2A52POP AFF1Get the character for the string ("X") from the STACK and put it in Register A
2A53INC E1CTo set the status flags we need to increase and then decrease E. First, bump the value of the string's length in Register E
2A54DEC E1D. and then decrement the string's length in Register E
2A55-2A56If "N" was zero (so that the string is now complete), jump back to 2A2BH
2A57-2A59LD HL,(40D4H)LD HL,(DSCTMP+1)2A D4 40If 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),A77Save the character in Register A ("X") at the location of the string pointer in HL
2A5BINC HL23Bump the value of the string pointer in HL
2A5CDEC E1DDecrement the string's length in Register E
2A5D-2A5EJR NZ,2A5AH20 FBLoop back to 2A5AH to keep filling (HL) until the string of X's has been completed
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$Go check the syntax. The character at the location of the current BASIC program pointer in HL must be a )
2A64XOR AAFZero Register A because the string pointer never changes
2A65- ↳ LEFT3EX (SP),HLE3Exchange the value of the current BASIC program pointer in HL with the string's VARPTR to the STACK
2A66LD C,A4FZero Register C
2A67-2A68LD A,0E5H3E E5Z-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- ↳ LEFT2PUSH HLE5Save the value of the string's VARPTR in HL to the STACK
2A6ALD A,(HL)7ELoad Register A with the string's length at the location of the string's VARPTR in HL
2A6BCP BB8Check 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-2A6DJump to 2A70H if the new string's length in Register B is greater than the string's length in Register A
2A6ELD A,B78Load Register A with the new string's TRUNCATED length in Register B
2A6F-2A71LD DE,000EH11 0E 00Z-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
2A72PUSH BCC5Save the offset in BC to the STACK
2A76POP BCC1Get the offset from the STACK and put it in BC
2A77POP HLE1Get the string's VARPTR from the STACK and put it in HL
2A78PUSH HLE5Save the string's VARPTR in HL to the STACK
2A79INC HL23Bump HL to now point to the address of the string
2A7ALD B,(HL)46Load Register B with the LSB of the string's address at the location of the string's VARPTR in HL
2A7BINC HL23Bump the value of the string's VARPTR in HL
2A7CLD H,(HL)66Load Register H with the MSB of the string's address at the location of the string's VARPTR in HL
2A7DLD L,B68Load Register L with the LSB of the string's address in Register B
2A7E-2A7FLD B,00H06 00Zero Register B so that BC can be used
2A80ADD HL,BC09Add the string's length in BC to the string's address in HL
2A81LD B,H44Load Register B with the MSB of the string's ending address in Register H
2A82LD C,L4DLoad Register C with the LSB of the string's ending address in Register L
2A83-2A85Go save the string's length (held in A) and the string's starting address (held in DE)
2A86LD L,A6FLoad Register L with the string's length (i.e., the number of characters to move) held in Register A
2A8APOP DED1Clean up the STACK
2A91-2A99 - LEVEL II BASIC RIGHT$ ROUTINE- "RIGHT$"
2A91-2A93Go check the syntax. The character at the location of the current BASIC program pointer in HL must be a )
2A94POP DED1Get the string's VARPTR from the STACK and put it in DE
2A95PUSH DED5Save the string's VARPTR in DE to the STACK
2A96LD A,(DE)1ALoad Register A with the string's length (held at the location of the string's VARPTR in DE)
2A97SUB B90Subtract the new string's length in Register B from the string's length in Register A to isolate the number of bytes
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,HLEBLoad HL with the value of the current BASIC program pointer (held in DE)
2A9BLD A,(HL)7ELoad Register A with the terminal character, currently held at the location of the current BASIC program pointer in HL
2A9C-2A9EGOSUB to 2AE2H to get the offset in Register B and the string's VARPTR in DE
2A9FINC B04We 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
2AA0DEC B05... and then we decrement the value of the string's offset position in Register B
2AA4PUSH BCC5Save the value of the offset position (held in Register B) to the STACK
2AA5-2AA6LD E,0FFH1E FFLoad Register E with the default string's length of 256 in case no number of bytes are given
2AA7-2AA8CP 29HCP ")"FE 29More 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-2AAAJump to 2AB0H if the character at the location of the current BASIC program pointer in Register A is a )
2AAB-2AACRST 08H ⇒ 2CSYNCHK ","CF 2CIf 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-2AAFGo 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-2AB1RST 08H ⇒ 29SYNCHK ")"CD 29Since 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).
2AB2POP AFF1Get the offset from the STACK and put it in Register A
2AB3EX (SP),HLE3Exchange the value of the current BASIC program pointer in HL with the value of the string's VARPTR to the STACK
2AB4-2AB6Load BC with 2A69H as the return address (which is in the LEFT$routine)
2AB7PUSH BCC5Save the return address in BC to the STACK
2AB8DEC A3DDecrement the value of the requested offset in Register A so that we have a starting position minus 1
2AB9CP (HL)BECompare 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-2ABBLD B,00H06 00Zero Register B
2ABCRET NCD0If the offset pointer ispast the end of the string we are going to return a null
2ABDLD C,A4FLoad Register C with the offset in Register A
2ABELD A,(HL)7ELoad Register A with the string's length at the location of the string's VARPTR in HL
2ABFSUB C91Subtract the index (the second argument) in Register C from the string's length in Register A
2AC0CP EBBCompare 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.
2AC1LD B,A47Load Register B with the calculated string's length in Register A
2AC2RET CD8If we are not going to truncate, then just use the partial string we already have and Return to 2A69H
2AC3LD B,E43Load Register B with the new string's truncated length in Register E
2AC4RETC9Return to 2A69H aka LEFT2
2AC5-2ADE - LEVEL II BASIC VALROUTINE- "VAL"
2AC5-2AC7- ↳ VALGo get the string's length in Register A and the string's VARPTR in HL
2AC8-2ACAJump to 27F8H if the string's length in Register A is equal to zero
2ACBLD E,A5FLoad 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.
2ACCINC HL23Bump the value of the string's VARPTR in HL
2ACDLD A,(HL)7ELoad Register A with the LSB of the string's address at the location of the string's VARPTR in HL
2ACEINC HL23Bump the value of the string's VARPTR in HL
2ACFLD H,(HL)66Load Register H with the MSB of the string's address at the location of the string's VARPTR in HL
2AD0LD L,A6FLoad Register L with the LSB of the string's address in Register A
2AD1PUSH HLE5Save the value of the string's address in HL to the STACK
2AD2ADD HL,DE19Add the string's length in DE to the string's address in HL
2AD3LD B,(HL)46Load Register B with the last character of the string at the location of the string pointer in HL
2AD4LD (HL),D72Save the zero in Register D at the location of the string pointer in HL
2AD5EX (SP),HLE3Exchange the string's ending address in HL with the string's address to the STACK
2AD6PUSH BCC5Save the last character of the string in Register B to the STACK
2AD7LD A,(HL)7ELoad Register A with the first character of the argument
2AD8-2ADACall 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)
2ADBPOP BCC1Get the modified character of the next string into Register B
2ADCPOP HLE1Get the pointer to the modified character back into HL
2ADDLD (HL),B70Restore the character.
2ADERETC9RETurn 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- ↳ PREAMEX DE,HLEBLoad HL with the value of the current BASIC program pointer in DE
2AE0-2AE1RST 08H ⇒ 29SYNCHK ")"CF 29Since 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- ↳ PREAM2POP BCC1Get the return address from the STACK and put it in BC
2AE3POP DED1Get the number of bytes to isolate from the string (from the STACK) and put it in DE
2AE4PUSH BCC5Save the return address in BC to the STACK
2AE5LD B,E43Load Register B with the number of bytes in Register E
2AE6RETC9RETurn to CALLer
2AE7H-2AEE - Process a LEFT-HAND-SIDE MID$- "ISMID$"
2AE7-2AE8- ↳ ISMID$CP 7AHCP MIDTK-$ENDFE 7AThis 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.
2AEF-2AF7 - LEVEL II BASIC INPROUTINE- "FNINP"
2AEF-2AF1- ↳ FNINPGo evaluate the expression at the location of the current BASIC program pointer in HL and return with the port number in Register A
2AF2-2AF4LD (4094H),ALD (STAINP+1),A32 94 40Save the value of the port number (from Register A) into 4094H, which is in the middle of a routine.
2AF8-2B00 - LEVEL II BASIC OUTROUTINE- "FNOUT"
2B01-2B0D - EVALUATE EXPRESSION ROUTINE- "GETINT"
This evaluates an expression and leaves the result in DE as an integer.
2B01- ↳ GETINTRST 10HCHRGETD7We 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- ↳ GETIN2Go 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- ↳ INTFR2PUSH HLE5Save the value of the current BASIC program pointer in HL to the STACK
2B06-2B08Call 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)
2B09EX DE,HLEBLoad DE with the integer result in HL
2B0APOP HLE1Get the value of the current BASIC program pointer from the STACK and put it in HL
2B0BLD A,D7ALoad Register A with the MSB of the integer result in Register D
2B0COR AB7Test the value of the MSB in Register A
2B0DRETC9RETurn to CALLer
2B0E-2B16 - EVALUATE EXPRESSION ROUTINE - OUTcontinues here- "SETIO"
2B0E-2B10- ↳ SETIOGOSUB 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-2B13LD (4094H),ALD (STAINP+1),A32 94 40Save the 8-bit value in Register A in the DOS address of 4094H to set up for WAIT
2B14-2B16LD (4097H),ALD (OUTWRD+1),A32 97 40Save 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-2B18RST 08H ⇒ 2ESYNCHK ","CF 2ESince 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).
2B1B-2B28 - EVALUATE EXPRESSION ROUTINE - This is called by PRINT TAB- "GTBYTC".
2B1B- ↳ GTBYTCRST 10HCHRGETD7We 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- ↳ GETBYTGOSUB 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- ↳ CONINTGOSUB 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-2B24If the result is greater than 255, display a ?FC ERRORmessage
2B25DEC HL2BDecrement the value of the current BASIC program pointer in HL
2B26RST 10HCHRGETD7We 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.
2B27LD A,E7BLoad Register A with the 8-bit result in Register E so that both A and E have the result.
2B28RETC9RETurn 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- ↳ LLISTLD A,01H3E 01Load Register A with the printer output device code
2B2B-2B2DLD (409CH),ALD (PRTFLG),A32 9C 40Save 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- ↳ LISTPOP BCC1Get rid of the the return address on the STACK
2B2F-2B31Go evaluate the range of line numbers given at the location of the current BASIC program pointer in HL
2B32PUSH BCC5Save the address of the first BASIC line (held in BC) to the STACK
2B33-2B35- ↳ LIST4LD HL,FFFFH21 FF FFLoad 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-2B38LD (40A2H),HLLD (CURLIN),HL22 A2 40Save the value in HL as the current BASIC line number (which is stored at 40A2H-40A3H).
2B39POP HLE1Get the address of the first BASIC line to be listed (from the STACK) and put it in HL
2B3APOP DED1Get the value of the last BASIC line number to be listed (from the STACK) and put it in DE
2B3BLD C,(HL)4ELoad Register C with the LSB of the next BASIC line pointer at the location of the memory pointer in HL
2B3CINC HL23Bump the value of the memory pointer in HL
2B3DLD B,(HL)46Load Register B with the MSB of the next BASIC line pointer at the location of the memory pointer in HL
2B3EINC HL23Bump the value of the memory pointer in HL
2B3FLD A,B78Load Register A with the MSB of the next BASIC line pointer in Register B
2B40OR CB1Combine 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-2B43If we are at the elast line, then STOP and JUMP to 1A19H to the READY PROMPT.
2B47-2B49Go scan the keyboard to see if the BREAKkey or the shift-@key was pressed
2B4APUSH BCC5Save the address of the next BASIC line in BC to the STACK
2B4BLD C,(HL)4EWe 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
2B4CINC HL23Bump the value of the memory pointer in HL
2B4DLD B,(HL)46Load Register B with the MSB of the BASIC line number at the location of the memory pointer in HL
2B4EINC HL23Bump the value of the memory pointer in HL
2B4FPUSH BCC5Save the BASIC line number in BC to the STACK
2B50EX (SP),HLE3Swap (SP) and HL so that the line number is now in HL
2B51EX DE,HLEBSwap DE and HL so that the last BASIC line number is now in HL
2B52RST 18HCOMPARDFWe 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.
2B53POP BCC1Get the pointer to the location on the BASIC program line being processed and put it in BC
2B54-2B56If 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!
2B57EX (SP),HLE3Swap (SP) and HL so that the last BASIC line number is now on the STACK
2B58PUSH HLE5Save the address of the next BASIC line in HL to the STACK
2B59PUSH BCC5Save the pointer to the location on the BASIC program line being processed to the STACK
2B5AEX DE,HLEBLoad HL with the BASIC line number (from DE)
2B5B-2B5DLD (40ECH),HLLD (DOT),HL22 EC 40Save the BASIC line number in HL into DOT for use later in EDIT or LIST.
Note: 40ECH-40EDH holds EDIT line number
2B5E-2B60Call 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-2B62LD A,20H3E 20Load Register A with a space
2B63POP HLE1Get the value of the memory pointer from the STACK and put it in HL
2B67-2B69GOSUB 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-2B6CLD HL,(40A7H)LD HL,(BUFPNT)2A A7 40Load HL with the starting address of the input buffer.
Note: 40A7H-40A8H holds the input Buffer pointer
2B6D-2B6FSince 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
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)7ELoad Register A with the character at the location of the memory pointer in HL
2B76OR AB7Check to see if the character in Register A is an end of the string character (00H)
2B77RET ZC8Return if the character in Register A is an end of the string character
2B78-2B7AGo send the character in Register A to the current output device
2B7BINC HL23Bump the value of the memory pointer in HL
2B7C-2B7DLoop 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- ↳ BUFLINPUSH HLE5Save the BASIC line pointer in HL to the STACK
2B7F-2B81LD HL,(40A7H)LD HL,(BUFPNT)2A A7 40Load HL with the starting address of the input buffer.
Note: 40A7H-40A8H holds the input Buffer pointer
2B82
2B83LD B,H
LD C,L44LET Register Pair BC = Register Pair HL
2B84POP HLE1Get the value of the BASIC line pointer from the STACK and put it in HL
2B85JUMP 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.
2B88NOP
2B89- ↳ PLOOPINC BC03Top of a loop. Bump the value of the input buffer pointer in BC
2B8ADEC D15Decrement the character count in Register D
2B8BRET ZC8Return if 256 characters have been moved into the input buffer
2B8CINC HLMove one byte forward in the text.
2B8DLD A,(HL)Load register A with the character at the location of the BASIC line pointer in HL.
2B8EOR ASet the status flags to enable us to check to see if the character in register A is an end of the BASIC line character.
2B8FLD (BC),A02Save 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.
2B90RET ZC8Return if the character in Register A is an end of the BASIC line character
2B94CP FBHCheck to see if the character in register A is a ' token.
2B96-2B97Jump 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-2B9BDEC BC
DEC BC
DEC BC
DEC BC0BFirst, 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-2B9FINC D
INC D
INC D
INC D14Then, 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- ↳ NRQTTKCP 95HCP $ELSEFE 95Check 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-2BA4If 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-2BA6SUB 7FHD6 7FNext, 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
2BA7PUSH HLE5Save the value of the BASIC line pointer in HL to the STACK. Register L holds the reserved word number at this point.
2BA8LD E,A5FLoad Register E with the character in Register A
2BA9-2BABLoad HL with the starting address of the reserved words list
2BAC- ↳ LOPRESLD A,(HL)7ELoad Register A with the character at the location of the reserved words list pointer in HL
2BADOR AB7Test 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.
2BAEINC HL23Bump the value of the reserved words list pointer in HL
2BAF-2BB1If the character at the location of the reserved words pointer in Register A doesn't have bit 7 set then Jump back to 2BACH.
2BB2DEC E1DDecrement the counter
2BB3-2BB4Jump back to 2BACH if this isn't the reserved word for the token
2BB5-2BB6AND 7FHE6 7FReset 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- ↳ MORPURLD (BC),A02Save the character in Register A at the location of the input buffer pointer in BC
2BB8INC BC03Bump the value of the input buffer pointer in BC
2BB9DEC D15Decrement the value of the character counter in Register D
2BBA-2BBCIf 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
2BBDLD A,(HL)7ELoad Register A with the character at the location of the reserved words pointer in HL
2BBEINC HL23Bump the reserved words pointer in HL
2BBFOR AB7Test the value of the character in Register A
2BC0-2BC2Keep getting characters in this reserved word until we hit the next reserved word
2BC3POP HLE1Get the value of the BASIC line pointer from the STACK and put it in HL
2BC4-2BC5Jump 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- ↳ DELETEGOSUB to 1B10H to evaluate the line numbers at the location of the current BASIC program pointer in HL
2BC9POP DED1Get the value of the last BASIC line number to be deleted (in binary) from the STACK and put it in DE
2BCAPUSH BCC5Save the address of the first BASIC line to be deleted in BC to the STACK
2BCBPUSH BCC5Save the address of the first BASIC line to be deleted in BC to the STACK AGAIN!
2BCC-2BCEGOSUB 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-2BD0Since 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
2BD2LD D,H
LD E,L54Let Register Pair DE = Register Pair HL
2BD3EX (SP),HLE3Exchange the last BASIC line's address in HL with the first BASIC line's address to the STACK
2BD4PUSH HLE5Save the pointer to the first line in range to the STACK
2BD5RST 18HCOMPARDFWe 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- ↳ FCERRGDisplay 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
2BDC-2BDECall the WRITE MESSAGE routine at 28A7H to print the message pointed to by HL.
2BDFPOP BCC1Get the first BASIC line's address from the STACK and put it in BC
2BE3EX (SP),HLE3Swap (SP) and HL so that HL now points to the next BASIC line's address ...
2BE4- ↳ DELEX DE,HLEBand then put it into DE
2BE5-2BE7LD HL,(40F9H)LD HL,(VARTAB)2A F9 40Load HL with the end of the BASIC program pointer.
- Note: 40F9H-40FAH holds the starting address of the simple variable storage area.
2BE8- ↳ MLOOPLD A,(DE)1ALoad Register A with the character at the location of the memory pointer in DE
2BE9LD (BC),A02Save the character in Register A at the location of the memory pointer in BC
2BEAINC BC03Bump the value of the memory pointer in BC
2BEBINC DE13Bump the value of the memory pointer in DE
2BECRST 18HCOMPARDFNow 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-2BEELoop back to 2BE8H until the memory pointer in DE equals the end of the BASIC program pointer in HL
2BEFLD H,B60Load Register H with the MSB of the memory pointer in Register B
2BF0LD L,C69Load Register L with the LSB of the memory pointer in Register C
2BF1-2BF3LD (40F9H),HLLD (VARTAB),HL22 F9 40Save 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.
2BF4RETC9RETurn 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- ↳ CSAVECalls the WRITE LEADER routine at 0284H (which writes a Level II leader on the cassette unit set in Register A)
2BF82BFAH 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
2BFBPUSH HLE5Save 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
2BFF-2C00LD A,D3H3E D3Load Register A with the filename header byte (=D3H which is a "S" with the sign bit on)
2C01-2C03the 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
2C07LD A,(DE)1ALoad Register A with the first character of the filename at the location of the filename pointer in DE
2C08-2C0Athe 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-2C0DLD HL,(40A4H)LD HL,(TXTTAB)2A A4 40Load 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).
2C0EEX DE,HLEBLoad DE with the start of the BASIC program pointer in HL
2C0F-2C11LD HL,(40F9H)LD HL,(VARTAB)2A F9 40Load HL with the end of the BASIC program pointer.
- Note: 40F9H-40FAH holds the starting address of the simple variable storage area.
2C12- ↳ LOPSCOLD A,(DE)1ATop 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
2C13INC DE13Bump the value of the memory pointer in DE
2C14-2C16the 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)
2C17RST 18HCOMPARDFNow 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-2C19Loop back to 2C12H until the memory pointer in DE is equal to the end of the BASIC program pointer in HL
2C1DPOP HLE1Get the value of the current BASIC program pointer from the STACK and put it in HL
2C1ERETC9RETurn to CALLer
2C1F-2CA4 - LEVEL II BASIC CLOAD ROUTINE - ROM v1.0- "CLOAD"
2C22LD A,(HL)7ELoad Register A with the character at the location of the current BASIC program pointer in HL
2C23-2C24SUB 0B2HSUB $PRINTD6 B2Check 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-2C26Jump to the CLOAD?routine at 2C29H if the character at the location of the current BASIC program pointer in Register A is a ?
2C27XOR AAFOK - So this is now a straight CLOAD. First, zero Register A
2C28LD BC,232F01 2F 23Z-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- ↳ CLOADPCPL2FLoad Register A with a -1 for CLOAD?. It will still be a 0 if this is CLOAD
2C2AINC HL23Bump the value of the current BASIC program pointer in HL until it points to the next character after the ?in CLOAD?
2C2BPUSH AFF5Save the CLOAD/CLOAD?flag in Register A to the STACK
2C2CDEC HL2BDecrement the value of the current BASIC program pointer in HL so we can see if we are at the end
2C2DRST 10HCHRGETD7We 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-2C2FLD A,00H3E 00Zero Register A to allow for any filename
2C30-2C31Jump if the character at the location of the current BASIC program pointer in HL is an end of the BASIC statement character
2C32-2C34To 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-2C37Make sure the length is good, and save the pointer to the filename to Register Pair DE
2C38LD A,(DE)1ALoad Register A with the first character of the filename at the location of the filename pointer in DE
2C39- ↳ CLNONMLD L,A6FLoad Register L with the filename in Register A
2C3APOP AFF1Get the value of the CLOAD/CLOAD?flag from the STACK and put it in Register A
2C3BOR AB7Test the value of the CLOAD/CLOAD?flag in Register A (since CPL doesn't set any flags)
2C3CLD H,A67Load Register H with the value of the CLOAD/CLOAD?flag in Register A
2C3D-2C3FLD (4121H),HLLD (FACLO),HL22 21 41Save the value of the CLOAD/CLOAD?flag and the filename in HL in ACCumulator
2C43-2C45LD HL,(4121H)LD HL,(FACLO)2A 21 41Load HL with the CLOAD/CLOAD?flag and the filename in ACCumulator
2C46EX DE,HLEBLoad D with the CLOAD/CLOAD? flag and load Register E with the filename
2C47-2C48- ↳ LOPCLKLD B,03H06 03Load Register B with the number of header bytes
2C49-2C4B- ↳ LOPCL2Calls 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-2C4DSUB 0D3HD6 D3Check to see if the character in Register A is a filename header byte
2C4E-2C4FLoop if the character in Register A isn't a filename header byte
2C50-2C51Loop back to 2C49H until three filename header bytes have been read
2C52-2C54Now 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)
2C55INC E1CWe 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
2C56DEC E1DDecrement the value of the filename in Register E
2C57-2C58Jump to 2C5CH (to pretend the filename matched) if no filename was specified
2C59CP EBBIf 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-2C5BJump 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- ↳ NONAMCLD HL,(40A4H)LD HL,(TXTTAB)2A A4 40If 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- ↳ DOCRSLD B,03H06 03Load Register B with the number of zeros to look for to stop the load (which is 3)
2C61-2C63- ↳ DOCSMRCalls 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)
2C64LD E,A5FPreserve the character that was just read from tape into Register E
2C65SUB (HL)96Compare 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
2C66AND DA2Combine 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.
2C69LD (HL),E73At 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-2C6CMake sure there is more room, and toss a ?OM ERRORif there isn't.
2C6DLD A,(HL)7ELoad Register A with the character at the location of the memory pointer in HL
2C6EOR AB7Check to see if the byte just read in Register A is equal to zero
2C6FINC HL23Bump the value of the memory pointer in HL
2C70-2C71Loop if the byte in Register A isn't equal to zero (meaning that it isn't end of program or end of statement)
2C72-2C74Call the BLINK ASTERISK routine at 022CH which alternatively displays and clears an asterisk in the upper right hand corner of the video display
2C75-2C76Do that loop until three zeros in a row have been read from the cassette recorder
2C77-2C79LD (40F9H),HLLD (VARTAB),HL22 F9 40By 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-2C7CGOSUB 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.
2C80-2C82We 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-2C85LD HL,(40A4H)LD HL,(TXTTAB)2A A4 40Load 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).
2C86PUSH HLE5Save the start of the BASIC program pointer in HL to the STACK. FINI will need this value there.
2C8A-2C8CGOSUB 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-2C8FCall 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-2C92JP 1A18JP STPRDYC3 18 1AJUMP to STPRDY to pop NEWSTT from the STACK and then fall into the READY routine
2C93-2C95- ↳ SKPFILLD (3C3EH),A32 3E 3CGo display the filename on the video display
2C96-2C97- ↳ ZERSRFLD B,03H06 03Load Register B with the number of zeros to be found to stop the search
2C98-2C9A- ↳ GETCHZCalls 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)
2C9BOR AB7Check to see if the character in Register A is equal to zero
2C9E-2C9FLoop until three zeros in a row have been read from the cassette recorder
2CA0-2CA2Calls 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)
2CA5-2CA9 - MESSAGE STORAGE LOCATION- "NOOKCS"
2CA5-2CA9- ↳ NOOKCS"BAD" + 0DH + 00H42The 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- ↳ PEEKCall 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)
2CADLD A,(HL)7ELoad Register A with the value at the location of the memory pointer in HL
2CAE-2CB0Go save the 8-bit value in Register A as the current result in ACCumulator
2CB1-2CBC - LEVEL II BASIC POKEROUTINE- "POKE"
2CB1-2CB3- ↳ POKEGo evaluate the expression at the location of the current BASIC program pointer in HL and return with the integer result in DE
2CB4PUSH DED5Save the address the user wants to POKE to (held in DE) to the STACK
2CB5-2CB6RST 08H ⇒ 2ESYNCHK ","CF 2ESince 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-2CB9GOSUB 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
2CBAPOP DED1Get the address the user wants to POKE to from the STACK and put it in DE
2CBBLD (DE),A12Save the value the user wanted to poke (held in Register A) in the location that the user wants to POKE to (held in DE)
2CBCRETC9RETurn 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- ↳ PRINUSGo evaluate the string expression at the location of the current BASIC program pointer in HL 2CC0-2CC2Go make sure the expression that was just evaluated was a string 2CC3-2CC4RST 08H ⇒ 3BSYNCHK ";"CF 3BSince 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).
2CC5EX DE,HLEBSwap DE and HL so that DE now holds the current BASIC program pointer 2CC6-2CC8LD HL,(4121H)LD HL,(FACLO)2A 21 41Load HL with the USING string's VARPTR 2CCB-2CCD- ↳ REUSSTLD A,(40DEH)LD A,(FLGINP)3A DE 40Load 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. 2CCEOR AB7Check to see if that flag indivates that we did, or did not, print out a value last time. 2CCF-2CD0If we did not print out a value last time, we have an error, so JUMP down to 2CDDH 2CD1POP DED1Restore the pointer to the "USING" string decription from the STACK into DE 2CD2EX DE,HLEBSwap 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- ↳ INIUSPUSH HLE5Save the pointer to the "USING" string descriptor (i.e., the USING string's VARPTR) in HL to the STACK 2CD4XOR AAFZero Register A and all the flags. 2CD5-2CD7LD (40DEH),ALD (FLGINP),A32 DE 40Clear the flag we are using to see if we printed the values or not. 2CD8CP DBATurn 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. 2CD9PUSH AFF5Save the flag indicating if the value list has ended or not to the STACK 2CDAPUSH DED5Save the pointer into the value list to the STACK 2CDBLD B,(HL)46Load Register B with the USING string's length 2CDCOR BB0Check to see if the USING string's length in Register B is equal to zero 2CDD-2CDF- ↳ FCERR3If the USING string is NULL then display a ?FC ERROR 2CE0INC HL23Bump the pointer to the USING string's data in HL by 1 2CE1LD C,(HL)4ELoad Register C with the LSB of the USING string's address at the location of the USING string's VARPTR in HL 2CE2INC HL23Bump the value of the USING string's VARPTR in HL 2CE3LD H,(HL)66Load Register H with the MSB of the USING string's address at the location of the USING string's VARPTR in HL 2CE4LD L,C69Load Register L with the LSB of the USING string's address in Register C
2CE7- ↳ BGSTRFLD E,B58Load Register E with the USING string's length 2CE8PUSH HLE5Save the pointer to the USING string pointer in HL to the top of the STACK 2CE9-2CEALD C,02H0E 02Since the \\ string field length is two plus number of enclosed spaces, add two 2CEB- ↳ LPSTRFLD A,(HL)7ELoad Register A with the character at the location of the USING string pointer in HL 2CECINC HL23Bump the value of the USING string data pointer in HL 2CED-2CEECP 25HCP CSTRNGFE 25Check 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-2CF1If it is a "%" then JUMP to 2E17H to evaluate a string and print 2CF2-2CF3CP 20HFE 20Check 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-2CF5If the character is not a field extender, then it isn't a string field, so JUMP down a few opcodes to 2CF9H 2CF6INC C0CIncrement the field width (tracked in in Register C) 2CF7-2CF8Decrement 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- ↳ NOSTRFPOP HLE1Restore the pointer to the "USING" string's data into Register Pair HL
2CFALD B,E43Load Register B with the USING string's length
2CFB-2CFCLD A,25HLD A,CSTRNG3E 25Restore 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- ↳ NEWUCHIf a +came before the character, make sure to print it
2D00-2D02Once that has been printed, now we print the character in Register A since we know it isn't part of a field
2D03- ↳ PRCCHRXOR AAFWe 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
2D04LD E,A5FZero Register E
2D05LD D,A57Zero Register D
2D06-2D08- ↳ PLSFINGo print a leading +if necessary (i.e., to allow for multiple plusses)
2D09LD D,A57Set 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.
2D0ALD A,(HL)7ELoad Register A with the next field description character in the USING string
2D0BINC HL23Bump the value of the USING string pointer in HL
2D0C-2D0DCP 21HCP "!"FE 21Check 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.
2D11-2D12CP 23HCP "#"FE 23Check 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.
2D15DEC B05Since every other possibility is actually a two character field, decrement the value of the string's length in Register B onem ore time
2D16-2D18If 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-2D1ACP 2BHCP "+"FE 2BCheck 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-2D1CLD A,08H3E 08Set 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
2D1FDEC HL2BDecrement the value of the USING string pointer so we can re-get the character.
2D20LD A,(HL)7ELoad Register A with the (current) character at the location of the USING string pointer in HL
2D21INC HL23Bump the value of the USING string pointer in HL
2D22-2D23CP 2EHCP "."FE 2ECheck 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-2D25Jump if the character in Register A is a .to scan with Register E holding the number of digits before the "." as 0
2D26-2D27CP 25HCP CSTRNGFE 25Check 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-2D29Jump to see if it is really a string field if the character in Register A is a %
2D2ACP (HL)BECheck 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-2D2CIf the NZ flag is set, then we can't have a $$or a **, so all remaining possibilities are exhausted, so JUMP to NEWUCH
2D2D-2D2ECP 24HCP "$"FE 24Check 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.
2D31-2D32CP 2AHCP "*"FE 2ACheck 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-2D34If 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
2D35LD A,B78Prepare 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-2D37CP 02HFE 02Check 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.
2D38INC HL23Bump the value of the USING string pointer in HL
2D39-2D3AJump to 2D3EH if the USING string's length in Register A isn't at least two
2D3BLD A,(HL)7ELoad Register A with the character at the location of the USING string pointer in HL
2D3C-2D3DCP 24HCP "$"FE 24Check 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- NOTSPCLD A,20H3E 20Set the *bit in Register A
2D40-2D41If we did not ultimately get a **$then JUMP (noting we do NOT set the dollar sign flag)
2D42DEC B05Decrement the value of the USING string's length to take the $into account
2D43INC E1CBump the field width tracker to account for the floating dollar sign
2D44-2D45CP 0AFHFE AFZ-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- ↳ DOLRNMXOR AAFThis is $processing for PRINT USING. Clear Register A.
2D46-2D47ADD A,10HC6 10Mask Register A to set the bit for a floating dollar sign flag.
2D48INC HL23Bump the value of the USING string pointer in HL to go past the special characters
2D49- ↳ SPCNUMINC E1CSince two characters specify the field size, start off with E=1
2D4AADD A,D82Combine the bits in Register D into the flag tracker
2D4BLD D,A57Preserve the modified flag tracker into Register D.
2D4C- ↳ NUMNUMINC E1CBump the number of characters to the left of the decimal point in Register E
2D4D-2D4ELD C,00H0E 00Set the number of digits to the right of the decimal point (tracked in Register C) to 0
2D4FDEC B05Check to see if there are any more characters by decrementing the value of the string's length in Register B
2D50-2D51If 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.
2D52LD A,(HL)7ELoad Register A with the next character at the location of the USING string pointer in HL
2D53INC HL23Bump the value of the USING string pointer in HL
2D54-2D55CP 2EHCP "."FE 2ECheck 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-2D57If yes, then need to use a special scan loop to scan after the decimal point, so JUMP to AFTDOT
2D58-2D59CP 23HCP "#"FE 23Check 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.
2D5C-2D5DCP 2CHCP ","FE 2CCheck 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-2D5FIf there is no comma, then JUMP to FINNUM because there are no more leading digits and we need to check for "^^^"
2D60LD A,D7AIf we are here, then a comma was requested. Turn on the COMMA bit
2D61-2D62OR 40HF6 40Mask the flag in Register A for ,
2D63LD D,A57Load Register D with the value of the flag in Register A
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- ↳ DOTNUMLD A,(HL)7ELoad Register A with the next character of the USING string
2D67-2D68CP 23HFE 23Check 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-2D6ALD A,2EHLD A,"."3E 2ELoad Register A with a decimal point
2D6B-2D6CIf it isn't a "." then JUMP AWAY to NEWUCH with A holding a "." so that a "." will get printed
2D6D-2D6ELD C,01H0E 01If 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
2D6FINC HL23Bump the value of the USING string pointer in HL
2D70- ↳ AFTDOTINC C0CBump the number of digits to the right of the decimal point (tracked in Register C)
2D71DEC B05Decrement the value of the USING STRING's length to test to see if there are more characters
2D72-2D73If the USING string length is now ZERO, JUMP to ENDNUS to stop scanning
2D74LD A,(HL)7ELoad Register A with the character at the location of the USING string pointer in HL
2D75INC HL23Bump the value of the USING string pointer in HL
2D76-2D77CP 23HFE 23Check 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-2D79If 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- ↳ FINNUMPUSH DED5Save the value of the flag (tracked in D) and the number of leading digits (tracked in E) to the STACK
2D7B-2D7DLoad DE with the return address in case this is not a scientific notation
2D7EPUSH DED5Save the value of the return address in DE to the STACK
2D7F
2D80LD D,H
LD E,L54Let DE = HL in case we need to rememer HL
2D81-2D82CP 5BHFE 5BCheck 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.
2D83RET NZC0Return if the character in Register A isn't an up arrow
2D84CP (HL)BECheck 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.
2D85RET NZC0Return 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)
2D86INC HL23Bump the value of the USING string pointer in HL
2D87CP (HL)BECheck 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.
2D88RET NZC0Return 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)
2D89INC HL23Bump the value of the USING string pointer in HL
2D8ACP (HL)BECheck 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.
2D8BRET NZC0Return 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)
2D8CINC HL23Bump the value of the USING string pointer in HL. If we are here we have a #.^^^^format
2D8DLD A,B78Now 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-2D8FSUB 04HD6 04Check to see if there are at least 4 characters left in the USING string
2D90RET CD8Return to 2D97 if there aren't at least four characters left in the USING string
2D91POP DED1If there are at least 4 characters left, then clean up the STACK by removing the NOTSCI return address
2D92POP DED1Get the flag and the count of the characters to the left of the decimal point from the STACK and put it in DE
2D93LD B,A47Load Register B with the new USING string's length in Register A
2D94INC D14Set the exponential notation flag (tracked in Register D)
2D95INC HL23Bump the value of the USING string pointer in HL
2D96JP Z,0D1EBHCA EB D1Z-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
2D98POP DED1(Ignored if passing through) Restore the flags into Register D and the number of leading digits into Register E
2D99- ↳ ENDNUSLD A,D7AWe 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
2D9ADEC HL2BDecrement the value of the USING string pointer in HL
2D9BINC E1CBump the number of characters to the left of the decimal point in Register E to take into account the leading plus
2D9C-2D9DAND 08HE6 08Mask Register A to NOT check for a trailing sign
2D9E-2D9FIf that AND leaves us with a NZ, then we are all done with the field, so JUMP to ENDNUM
2DA0DEC E1DOtherwise, 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
2DA1LD A,B78Check to see if there are more characters by first loading Register A with the USING string's length from Register B
2DA2OR AB7Check to see if this is the end of the USING string
2DA3-2DA4If we are out of characters, then we are all done, so JUMP to ENDNUM
2DA5LD A,(HL)7EIf there ARE more characters, then fill Register A with the character at the location of the USING string pointer in HL
2DA6-2DA7SUB 2DHSUB "-"D6 2DCheck to see if the character in Register A is a -(i.e., a trailing minus)
2DAA-2DABCP 0FEHCP "+" - "-"FE FECheck 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.
2DAE-2DAFLD A,08H3E 08If we are here then we did have a trailing "+" so first set the flag for a POSITIVE "+"
2DB0-2DB1- ↳ SGNTRLADD A,04HC6 04Then set the flag for a trailing sign
2DB2ADD A,D82Combine the value of the flag in Register D with the value of the flag in Register A
2DB3LD D,A57Load Register D with the current flags
2DB4DEC B05Decrement 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- ↳ ENDNUMPOP HLE1Get the value of the current BASIC program pointer from the STACK and put it in HL
2DB6POP AFF1Load Register A with the flag that tells us whether there are more values to process in the value list.
2DB7-2DB8If there are no more values in the value list to process, then JUMP to FLDFIN because we are done with the PRINT
2DB9PUSH BCC5Save 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
2DBAPUSH DED5Save the flags (held in D) and the number of leading digits (held in E) to the STACK
2DBB-2DBDRead 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
2DBEPOP DED1Restore the flags (held in D) and the number of leading digits (held in E) from the STACK
2DBFPOP BCC1Restore 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
2DC0PUSH BCC5Save 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
2DC1PUSH HLE5Save the value of the current BASIC program pointer in HL to the STACK
2DC2LD B,E43Set Register B to hold the number of leading digits (i.e., the number of characters to the left of the decimal point)
2DC3LD A,B78We 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
2DC4ADD A,C81Then 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-2DC6CP 19HFE 19Check 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-2DC9Display a FC ERROR message if the total number of digits is greater than 24
2DCALD A,D7ALoad Register A with the flags (held in Register D)
2DCB-2DCCOR 80HF6 80Turn on the "USING" bit in the flags
2DCD-2DCFPrepare 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-2DD2Call 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- ↳ FNSTRFPOP HLE1Top of a loop. Get the value of the current BASIC program pointer from the STACK and put it in HL
2DD4DEC HL2BDecrement the value of the current BASIC program pointer in HL so we can test to see what the terminator was
2DD5RST 10HCHRGETD7We 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.
2DD6SCF37Set the Carry flag to indicate that a CRLF is desired
2DD7-2DD8If 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-2DDBLD (40DEH),ALD (FLGINP),A32 DE 40Set the flag that the value HAS been printed!
2DDC-2DDDCP 3BHCP ";"FE 3BCheck 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.
2DE0-2DE1CP 2CHCP ","FE 2CCheck 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-2DE4If 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- ↳ SEMUSNRST 10HCHRGETD7We 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- ↳ CRDNUSPOP BCC1Restore the number of characters remaining to be procesed in the USING string into Register B
2DE7EX DE,HLEBSwap DE and HL so that DE will point to the location of the current BASIC program pointer. We don't care about HL.
2DE8POP HLE1Restore the position in the USING string from the STACK and put it in HL
2DE9PUSH HLE5Save the position in the USING string (held in HL) to the STACK
2DEAPUSH AFF5Save the flag that indicates whether or not the value list has terminated to the STACK
2DEBPUSH DED5Save 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.
2DECLD A,(HL)7ELoad Register A with the USING string's length at the location of the USING string's VARPTR in HL
2DEDSUB B90Subtract the number of characers which were already scanned
2DEEINC HL23Bump the pointer to the "USING" strings string data
2DEFLD C,(HL)4ELoad Register C with the LSB of the USING string's address at the location of the USING string's VARPTR in HL
2DF0INC HL23Bump the value of the USING string's VARPTR in HL
2DF1LD H,(HL)66Load Register H with the MSB of the USING string's address at the location of the USING string's VARPTR in HL
2DF2LD L,C69Load Register L with the LSB of the USING string's address in Register C
2DF3-2DF4LD D,00H16 00Zero Register D so that Register Pair DE can be a 16 bit offset of whatever is held in A.
2DF5LD E,A5FLoad Register E with the USING string's offset in Register A
2DF6ADD HL,DE19Add 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
2DF7LD A,B78Load Register A with the number of characters left to scan
2DF8OR AB7Check to see if this is the end of the USING string
2DF9-2DFBIf there are still more string characters to scan, JUMP to PRCCHR to do so
2DFE-2E00 - Part of the PRINT USING Routine- "REUSIN"
We will wind up here when we are done processing a numeric field
2E01-2E03Go send the FINAL character (held in Register A) to the current output device
2E04- ↳ FINUSIPOP HLE1Get the value of the current BASIC program pointer from the STACK and put it in HL
2E05POP AFF1Restore the flag which indicates whether or not the value list has ended into Register A
2E06-2E08If the value list has NOT ended, JUMP back to REUSST to reuse the USING string
2E09-2E0B- ↳ FLDFINIf 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
2E0CEX (SP),HLE3Swap (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
2E10POP HLE1Get the value of the current BASIC program pointer from the STACK and put it in HL
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- ↳ SMSTRFLD C,01H0E 01Set the field width to 1
2E16-2E17LD A,0F1H3E F1Z-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- ↳ ISSTRFPOP 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)
2E18DEC B05Decrement the USING string character count (tracked in Register B)
2E1CPOP HLE1Get the value of the current BASIC program pointer from the STACK and put it in HL
2E1DPOP AFF1Get the flag which indicates whether there are more values in the value list into Register A
2E1E-2E1FIf there are no more values in the value list, then we are done so JUMP back to 2E09H
2E20PUSH BCC5Save the number of characters still to be scanned from the USING string (tracked in B) to the STACK
2E21-2E23Read 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
2E27POP BCC1Restore the field width (a/k/a the number of characters to be printed) into Register C
2E28PUSH BCC5Save the USING string's length and the number of characters to be printed in BC to the STACK
2E29PUSH HLE5Save the value of the current BASIC program pointer in HL to the STACK
2E2A-2E2CLD HL,(4121H)LD HL,(FACLO)2A 21 41Load HL with the string's VARPTR in ACCumulator
2E2DLD B,C41Load Register B with field width (a/k/a the number of characters to be printed)
2E2E-2E2FLD C,00H0E 00Zero Register C so that we can use the LEFT$ routine
2E30PUSH BCC5Save the length of the string to be printed in Register B to the STACK (as we will need that for space padding)
2E31-2E33Truncate the string to B characters via a call to the LEFT$ routine
2E37-2E39LD HL,(4121H)LD HL,(FACLO)2A 21 41Load HL with the string's VARPTR in ACCumulator so we can see if we need to pad the string
2E3APOP AFF1Get the field width (a/k/a the length of the string to be printed) from the STACK and put it in Register A
2E3BSUB (HL)96Determine 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
2E3CLD B,A47Save the amount of padding needed into Register B
2E3D-2E3ELD A,20H3E 20Load Register A with a SPACE
2E3FINC B04Bump 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- ↳ UPRTSPDEC B05Top of a loop. Decrement the number of spaces in Register B
2E41-2E43If all of the spaces have been printed, Jump back to 2DD3H to see if the value list ended and to resume scanning
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- ↳ PLSPRTPUSH AFF5Save the current character (held in Register A) to the STACK
2E4ALD A,D7AWe need to test the PLUS BIT in D, so first load Register A with the value in Register D
2E4BOR AB7Check 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-2E4DLD A,2BHLD A,"+"3E 2BPrepare to print the +by loading Register A with a +
2E4E-2E50If the bit was set (i.e., A was non-zero), then send a +to the current output device
2E51POP AFF1Get the current character from the STACK and put it in Register A
2E52RETC9RETurn 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- ↳ ERREDTLD (409AH),ALD (ERRFLG),A32 9A 40Reset the EDIT flag.
Note: 409AH holds the ERROR/RESUME flag
2E56-2E58LD HL,(40EAH)LD HL,(ERRLIN)2A EA 40Load HL with the line number to be edited.
Note: 40EAH-40EBH holds the line number with error
2E59OR HB4OR Register A with the MSB of the error line number in Register H
2E5AAND LA5Combine 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
2E5BINC A3CBump 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
2E5CEX DE,HLEBSwap DE and HL so that DE now holds the line number to edit.
2E5DRET ZC8If there was no line number, return if Level II BASIC
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- ↳ EDITGet the first line number by calling 1E4F - returns in in DE
2E63RET NZC0If the zero flag got set, there was no line number, so return
2E64- ↳ EREDITPOP HLE1Clean up the STACK (i.e., discard the NEWSTT return address)
2E65- ↳ EEDITSEX DE,HLEBLoad HL with the line number to be edited in DE
2E66-2E68LD (40ECH),HLLD (DOT),HL22 EC 40Save 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
2E69EX DE,HLEBLoad HL with the line number to be edited
2E6A-2E6CFind 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
2E70
2E71LD H,B
LD L,C60At 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
2E73INC HL
INC HL23Bump the value of the memory pointer in HL twice to now point to the first byte of the line.
2E74LD C,(HL)4ELoad Register C with first byte of the line number being edited
2E75INC HL23Bump the value of the memory pointer in HL to point to the second byte of the line being edited
2E76LD B,(HL)46Load Register B with second byte of the line number being edited
2E77INC HL23Bump the value of the memory pointer in HL to now point to the first byte of the actual line
2E78PUSH BCC5Save the line number to the STACK
2E79-2E7BGOSUB 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- ↳ LLEDPOP HLE1Get the value of the line number from the STACK and put it in HL
2E7D- ↳ INLEDPUSH HLE5Save the value of the line number in HL to the STACK
2E7E-2E80Convert 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-2E82LD A,20H3E 20Load Register A with a space
2E86-2E88LD HL,(40A7H)LD HL,(BUFPNT)2A A7 40Load 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-2E8ALD A,0EH3E 0ELoad Register A with the "turn on the cursor" character
2E8EPUSH HLE5Save the value of the input buffer pointer (in HL) to the STACK
2E8F-2E90LD C,FFH0E FFLoad 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- ↳ LENLPINC C0CBump the number of characters examined so far in Register C
2E92LD A,(HL)7ELoad Register A with the character at the location of the input buffer pointer in HL
2E93OR AB7Check to see if the character in Register A is an end of the BASIC line character
2E94INC HL23Bump the value of the input buffer pointer in HL
2E95-2E96Loop back to 2E91H until the end of the BASIC line has been found
2E97POP HLE1At this point, C will be the maximum number of characters in the line at issue. Put the start of the expanded buffer into HL
2E98LD B,A47Set the current position in the BASIC line being edited (tracked by Register B) to ZERO
2E99-2E9A- ↳ DISPEDLD D,00H16 00Assume the repetition count for the upcoming command (tracked by Register D) is zero
2E9B-2E9D- ↳ DISPIGo scan the keyboard to wait for the user command
2D9E-2E9F- ↳ DISPSUB 30H20 15We need to test to see if the character was alphabetic or alphanumeric so we subtract 30H from it
2EA2-2EA3CP 0AHFE 0ACheck 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.
2EA6LD E,A5FLoad Register E with the binary value of the character in Register A
2EA7LD A,D7APut the repetition value into Register A
2EA8RLCA07Multiply the value in Register A by two (so now A has multiplied by 2)
2EA9RLCA07Multiply the value in Register A by two (so now A has multiplied by 4)
2EAAADD A,D82Add the value in Register D to the value in Register A (so now A has multiplied by 5)
2EABRLCA07Multiply the value in Register A by two (so now A has multiplied by 10). Now the "ones place" is empty.
2EACADD A,E83Add the value in Register E to the value in Register A in the "ones place"
2EADLD D,A57Load Register D with the value in Register A
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- ↳ NOTDGIPUSH HLE5Save the value of the input buffer pointer in HL to the STACK
2EB4EX (SP),HLE3Exchange the value of the return address in HL with the value of the input buffer pointer to the STACK
2EB5DEC D15We 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
2EB6INC D14... and then incrementing the numeric value in Register D to set the flags
2EB7-2EB9If we had a received a repetition count already, then JUMP to 2EBBH
2EBAINC D14Otherwise, set the repetition count (held in Register D) to be one
2EBB-2EBC- ↳ NTZERDCP 0D8HFE D8Check 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-2EBFIf the character in Register A is a BACKSPACEcharacter, JUMP to DELED
2EC0-2EC1CP 0DDHFE DDCheck 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-2EC4If the character in Register A is a CARRIAGE RETURN, JUMP to CRED
2EC5-2EC6CP 0F0HFE F0Check 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.
That's it for non-alphabetic instructions, so we need to need to convert a lower case command to upper case
2EC9-2ECACP 31HFE 31Check 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.
2ECD-2ECESUB 20HD6 20Convert the lowercase character in Register A to uppercase
2ECF-2ED0- ↳ NOTLW4CP 21HFE 21Check 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.
2ED4-2ED5CP 1CHFE 1CCheck 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.
2ED9-2EDACP 23HFE 23Check 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.
2EDD-2EDECP 19HFE 19Check 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.
2EE2-2EE3CP 14HFE 14Check 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.
2EE7-2EE8CP 13HFE 13Check 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.
2EEC-2EEDCP 15HFE 15Check 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.
2EF1-2EF2CP 28HFE 28Check 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.
2EF6-2EF7CP 1BHFE 1BCheck 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.
2EFA-2EFBCP 18HFE 18Check 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-2EFEJump if the character in Register A is an H(i.e., HACK off the rest of the line and then enter INSERT mode)
2EFF-2F00CP 11HFE 11Check 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.
2F01RET NZC0Return if the character in Register A isn't an A
2F02 - EDITCommand - Cancel and Restore Logic.
2F02POP BCC1Clean up the STACK (i.e., remove the DISPI return address)
2F03POP DED1Get the BASIC line number from the STACK and put it in DE
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- ↳ SPEDLD A,(HL)7ELoad Register A with the character at the location of the input buffer pointer in HL
2F0BOR AB7Check to see if the character in Register A is an end of the BASIC line character
2F0CRET ZC8Return if the character in Register A is an end of the BASIC line character
2F0DINC B04Bump the character position in Register B
2F11INC HL23Bump the value of the pointer in HL
2F12DEC D15Decrement the number of times to perform the operation in Register D
2F15RETC9RETurn to CALLer
2F16 - EDITCommand - KILL Logic- "KED".
2F16- ↳ KEDPUSH HLE5Save the current character position in the buffer in HL to the STACK
2F17-2F19Load HL with the return address of 2F5FH (which will print the final !
2F1AEX (SP),HLE3Exchange the value of the return address in HL with the value of the input buffer pointer to the STACK
2F1BSCF37Set the KILL/SEARCH flag for KILL since CARRY flag signals KILL
2F1C- ↳ SEDPUSH AFF5Save the KILL/SEARCH flag to the STACK
2F1D-2F1FGo scan the keyboard for the character the user wants to SEARCH for
2F20LD E,A5FSave the character the user wants to SEARCH for into Register E
2F21POP AFF1Get the KILL/SEARCH flag from the STACK
2F22PUSH AFF5Save the KILL/SEARCH flag to the STACK
2F23-2F25If KILL (because the CARRY flag was set) then GOSUB to 2F5FH to print a !
2F26- ↳ SRCALPLD A,(HL)7ELoad Register A with the character at the location of the input buffer pointer in HL
2F27OR AB7Check to see if the character in Register A is an end of the BASIC line character
2F28-2F2AJump down to 2F3EH if the character in Register A is an end of the BASIC line character
2F2EPOP AFF1Get the KILL/SEARCH flag from the STACK
2F2FPUSH AFF5Save the KILL/SEARCH flag to the STACK
2F30-2F32If 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-2F34Jump to 2F37H if KILL. Note, we do not move the HL pointer in this case because DELCHR already moved it.
2F35INC HL23If we are here, it must be SEARCH! So bump to the next character
2F36INC B04Bump the value of the character position in Register B
2F37- ↳ NOTSRCLD A,(HL)7ERegardless of whether we are SEARCH or KILL, load Register A with the character at the location of the current input buffer pointer in HL
2F38CP EBBCheck 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.
2F3BDEC D15Decrement the number of times to perform the operation in Register D (as initially specified by the user by entering a number before the command)
2F3E- ↳ POPARTPOP AFF1Get the KILL/SEARCH flag from the STACK
2F3FRETC9RETurn to CALLer
2F40 - EDITCommand - LIST Logic- "LED".
2F40-2F42- ↳ LEDSince 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
2F46POP BCC1Clear off the RETURN address to DISPED
2F47-2F49Jump to 2E7CH (to display the current line number and await the next EDIT command)
2F4A - EDITCommand - DELETE Logic- "DED"
2F4A- ↳ DEDLD A,(HL)7ELoad Register A with the character at the location of the input buffer pointer in HL
2F4BOR AB7Check to see if the character in Register A is an end of the BASIC line character
2F5CRET Z15Return if the character in Register A is an end of the BASIC line character
2F4D-2F4ELD A,21HLD A,"!"3E 21Load Register A with an !
2F52- ↳ DELLPLD A,(HL)7ELoad Register A with the character at the location of the input buffer pointer in HL
2F53OR AB7Check to see if the character in Register A is an end of the BASIC line character
2F54-2F5BJump to 2F5FH if the character in Register A is an end of the BASIC line character
2F5CDEC D15Decrement the number of times to perform the operation in Register D (as initially specified by the user by entering a number before the command)
2F5F-2F60- ↳ TYPSLHLD A,21HLD A,"!"3E 21Load Register A with an !
2F64RETC9RETurn to CALLer
2F65 - EDITCommand - CHANGE Logic- "CED".
2F65- ↳ CEDLD A,(HL)7ELoad Register A with the character at the location of the input buffer pointer in HL
2F66OR AB7Check to see if the character in Register A is an end of the BASIC line character
2F67RET ZC8Return if the character in Register A is an end of the BASIC line character
2F68-2F6AGo get the character to put in the input buffer from the keyboard
2F6BLD (HL),A77Save the character in Register A at the memory location of the input buffer pointer in HL
2F6FINC HL23Bump the value of the input buffer pointer in HL
2F70INC B04Bump the character position in Register B
2F71DEC D15Decrement the number of times to perform the operation in Register D (as initially specified by the user by entering a number before the command)
2F74RETC9RETurn to CALLer
2F75 - EDITCommand - HACK/INSERT Logic- "HED"
2F75-2F76- ↳ HEDLD (HL),00H36 00Set the line end to be the current position.
2F77LD C,B48Load Register C with the character position in Register B which will now be the line length
2F78-2F79- ↳ XEDLD D,0FFH16 FFPrepare for the next CALL to find the end of the line by loading Register D with the number of times to perform the operation
2F7D-2F7F- ↳ IEDGo get the character to be inserted from the keyboard
2F80OR AB7Check to see if a key was pressed
2F84-2F85CP 08HFE 08Check 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-2F87Jump to 2F92H if the character in Register A is a backspace character
2F88-2F89CP 0DHFE 0DCheck 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-2F8CJump to 2FE0H if the character in Register A is a carriage return
2F8D-2F8ECP 1BHFE 1BCheck 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.
2F8FRET ZC8Return if the character in Register A is shift up arrow
2F92 - EDITCommand - BACKSPACE CURSOR Logic- "TYPARW".
2F92-2F93- ↳ TYPARWLD A,08H3E 08Load Register A with a backspace the cursor character
2F94- ↳ TYPAR1DEC B05Decrement the character position in Register B
2F95INC B04Bump the character position in Register B
2F96-2F97If this is the first character of the BASIC line Jump forward to 2FB7H
2F9BDEC HL2BDecrement the value of the input buffer pointer in HL
2F9CDEC B05Decrement the character position in Register B
2FA0PUSH DED5Save 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- ↳ DELCHRPUSH HLE5Save the value of the input buffer pointer in HL to the STACK
2FA2DEC C0DDecrement the character position in Register C
2FA3- ↳ CMPRSSLD A,(HL)7ELoad Register A with the character at the location of the input buffer pointer in HL
2FA4OR AB7Check to see if the character in Register A is an end of the BASIC line character
2FA5SCF37Set the Carry flag to signal that DELCHR was called
2FA6-2FA8If the character in Register A is an end of the BASIC line character then we are done compressing so Jump to 0890H
2FA9INC HL23Bump the value of the input buffer pointer in HL
2FAALD A,(HL)7ELoad Register A with the character at the location of the input buffer pointer in HL
2FABDEC HL2BDecrement the value of the input buffer pointer in HL
2FACLD (HL),A77Save the character in Register A at the location of the current input buffer pointer in HL
2FADINC HL23Bump the value of the input buffer pointer in HL
2FB0 - EDITCommand - ADD A CHARACTER Logic- "NTARRW".
2FB0- ↳ NTARRWPUSH AFF5Save the character to be inserted in Register A to the STACK
2FB1LD A,C79Load Register A with the number of characters in the input buffer (i.e., the length of the line) in Register C
2FB2-2FB3CP FFHCP BUFLENFE FFWe 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-2FB5Jump forward to 2FB9H if the maximum BASIC line length hasn't been reached
2FB6POP AFF1Get the character to be inserted from the STACK and put it in Register A
2FB9- ↳ OKINSSUB B90Subtract 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
2FBAINC C0CBump the number of characters in the input buffer in Register C
2FBBINC B04Bump the character position in Register B
2FBCPUSH BCC5Save the character position and the number of characters in the input buffer in BC to the STACK
2FBDEX DE,HLEBLoad DE with the input buffer pointer in HL
2FBELD L,A6FLoad Register L with the number of bytes to move
2FBF-2FC0LD H,00H26 00Zero Register H so that the number of bytes to move can be done in a 16 bit Register Pair.
2FC1ADD HL,DE19Add the value of the input buffer pointer in DE to the character count in HL
2FC2LD B,H44Load Register B with the MSB of the end of the BASIC line pointer in Register H
2FC3LD C,L4DLoad Register C with the LSB of the end of the BASIC line pointer in Register L
2FC4INC HL23Bump the value of the end of the BASIC line pointer in HL
2FC8POP BCC1Get the character position and the number of characters in the input buffer from the STACK and put it in BC
2FC9POP AFF1Get the character to be inserted from the STACK and put it in Register A
2FCALD (HL),A77Save the character in Register A at the location of the current input buffer pointer in HL
2FCEINC HL23Bump the value of the input buffer pointer in HL
2FD2 - EDITCommand - BACKSPACE Logic- "DELED".
2FD2- ↳ DELEDLD A,B78Top 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
2FD3OR AB7Check to see if this is the start of the BASIC line
2FD4RET ZC8Return if this is the start of the BASIC line
2FD5DEC B05Decrement the character position in Register B
2FD6DEC HL2BDecrement the value of the buffer pointer in HL
2FD7-2FD8LD A,08H3E 08Load Register A with a backspace the cursor character
2FDCDEC D15Decrement the number of times to perform the operation in Register D
2FDFRETC9RETurn to CALLer
2FE0-2FE2- ↳ CREDSince 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
2FE6POP BCC1Clean up the STACK (to remove the DISPED return address)
2FE7POP DED1Get the BASIC line number (in binary) from the STACK and put it in DE
2FE8LD A,D7ALoad Register A with the MSB of the BASIC line number in Register D
2FE9AND EA3Combine the LSB of the BASIC line number in Register E with the MSB of the BASIC line number in Register A
2FEAINC A3CBump the combined BASIC line number in Register A
2FEB-2FED- ↳ EDITRTLD HL,(40A7H)LD HL,(BUFPNT)2A A7 40Load HL with the starting address of the input buffer.
Note: 40A7H-40A8H holds the input Buffer pointer
2FEEDEC HL2BDecrement the value of the input buffer pointer in HL
2FEFRET ZC8Return if this is the Level II BASIC command mode
2FF0SCF37Set the Carry flag to to fool the INSERT code; this flags the line number has having been seen
2FF1INC HL23Bump the value of the input buffer pointer in HL
2FF2PUSH AFF5Save the command mode flag in AF to the STACK
2FF6 - EDITCommand - QUIT Logic- "QED".
3000 - Jump Table.
3012 - Model 4 ROM Gen 1
3012 - Model 4 ROM Gen 2
*3024 - Model 4 ROM Gen 1
*3027RET
NOP
NOPI/O Re-Router was removed from the Model 4 ROM.
*3024 - Model 4 ROM Gen 2
*302D - Model 4 ROM Gen 1
*302D - Model 4 ROM Gen 2
*302DJP 375CHC3 5C 37Jump to 375CH for a routine which parses whether the current instruction on a the current line is in quotes.
3036Jump to 35A0H to put the TIME 10 characters from the upper right hand corner of the screen.
3039IN A,(0E4H)Poll Port E4H into A.
NOTE: Port E4H is the Non-Maskable Interrupt Latch.
303BBIT 5,ATest Bit 5 of Register A. Bit 5 of the NMI on an Input test is the RESET STATUS, with 0=False, and 1=True
*3042 - Model 4 ROM Gen 1 - Prompt the User to set the cassette baud rate.
*3042 - Model 4 ROM Gen 2 - Prompt the User to set the cassette baud rate.
*3045-3064 - Model 4 ROM Gen 1
*3045-305F@abcdefghijklmnopqrstuvwxyzKeyboard Rows 0-3, Unshifted, No Caps Lock
*3060NOPComputer version number, which is always 1 for a Model III and 0 for a Model 4
*3061NOP
*3062NOP
*3063NOP
*3064NOP
*3065-307D30 31 32 33 34 35 36 37 38 39 3A 3B 2C 2D 2E 2F 0D 1F 01 5B 0A 08 09 20 00Keyboard Rows 4-6, Unshifted, No Caps Lock
*307ENOP
*307FNOP
*3080NOP
*3081NOP
*3082NOP
*3083NOP
*3084NOP
*3085-309F60 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 5AKeyboard Rows 0-3, shifted, No Caps Lock
*30A0NOP
*30A1NOP
*30A2NOP
*30A3NOP
*30A4NOP
*30A5NOP
*30A6-30BC21 22 23 24 25 26 27 28 29 2A 2B 3C 3D 3E 3F 0D 1F 01 1B 1A 18 19 20Keyboard Rows 4-6, shifted, No Caps Lock
*30BDNOP
*30BENOP
*30BFNOP
*30C0NOP
*30C1NOP
*30C2NOP
*30C3NOP
*30C4NOP
*30C5-30DF40 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 5AKeyboard Rows 0-3, UNshifted, Caps Lock.
*30E0NOP
*30E1NOP
*30E2NOP
*30E3NOP
*30E4NOP
*30E5-30FC30 31 32 33 34 35 36 37 38 39 3A 3B 2C 2D 2E 2F 0D 1F 01 5B 0A 08 09 20Keyboard Rows 4-6, UNshifted, Caps Lock
*30FDNOP
*30FENOP
*30FFNOP
*3100NOP
*3101NOP
*3102NOP
*3103NOP
*3104NOP
*3105H - Model 4 Gen 1 jump to NON-Disk BASIC
GOSUB to the $SETCAS routine which prompts the user to set the cassette baud rate (310BH - 313AH)
*310BH - Model 4 Gen 1 Set the TAPE BAUD RATE ($SETCAS).
*310CGOSUB to 312DH which loads A with a carrage return, and jumps to 0033H to display it.
*3115GOSUB to 0049H.
NOTE: 0049H is the $KBWAIT routine which scans the keyboard and returns with the key pressed, if any, in register A.
*3118CP 0DHCompare A and 0D (a CARRIAGE RETURN).
*311CPUSH AFSave AF to the STACK.
NOTE: A currently holds the character pressed in response to the "CASS?" message.
*311DGOSUB to 0033H.
NOTE: 0033H is the character print routine, to put the character held in Register A at the current cursor position.
*3120POP AFRestore AF from the STACK.
NOTE: A will then hold the character pressed in response to the "CASS?" message.
*3121CP 48HCompare A with 48H (ASCII: H).
*3125CP 4CHCompare A with a 4CH (ASCII: L).
3129H - Model 4 Gen 1 Set the Selected Cassette Baud Rate as LOW SPEED
*3129XOR ASet 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
LD (4211H),ASave 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
*312FDisplay 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
Finish the cassette setup by calling a portion of the keyboard scan routine which CALLS the screen print routine at 34FDH
*313EXOR AClear Register A and All Flags
*313FRETReturn a null character.
*3140NOP
*3141NOP
*3142NOP
*3143NOP
*3144NOP
*3145 - Model 4 Gen 1 Printer Character Table Codes 32-127.
*3145-31A420 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.
*3045JP 378DHC3 8D 37Jump 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
*3048JP 377AHC3 7A 37Jump to 377AH to check to see if we are on a new printable page and set the pointers accordingly. CALLED from 0431 and 0445
*3045 - Model 4 ROM Gen 2 Keyboard Rows 0-3, Unshifted, No Caps Lock.
*304E-3068@abcdefghijklmnopqrstuvwxyz
*3069NOP
*306ANOP
*306BNOP
*306CNOP
*306DNOP
*306E-308530 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.
*3086IN A,(0EAH)DB EAPoll the RS-232 UART Control Register and Status Register (at Port EAH) and put the results into Register A
*3087OR AB7Set the FLAGS based on the RS-232 UART Control Register and Status Register
*308C-308DJR 30C6H18 38If 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.
**3086LD A,(3810H)3A 10 38Fetch the Keyboard Matrix 0-7 into Register A to check for certain keys.
**3089AND 10HE6 10MASK 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.
**306CNOP
**306DNOP
**306DNOP
*308E-30A8 - Model 4 ROM Gen 2 - Continuing with the Keyboard Table
*308E-30A860 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
*30A9NOP
*30AANOP
*30ABNOP
*30ACNOP
*30ADNOP
*30AENOP
*30AF-30C521 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-30C7IN A,(0EBH)DB EBPoll the RS-232 Register at Port EBH and put the results into Register A.
*30C8RETC9
**30C6-30C8 - Model 4 ROM Student Network Edition - A little code snippet
*30C6NOP
*30C7NOP
*30C8NOP
*3089 - Model 4 ROM Gen 2 - Continuing with the Keyboard Table
*30C9NOP
*30CA-30CDPUSH BC
POP BC
NOP
RETStandard code for a short delay
*30CE@ABCDEFGHIJKLMNOPQRSTUVWXYZ
*30E9NOP
*30EANOP
*30EBNOP
*30ECNOP
*30EDNOP
*30EE30 31 32 33 34 35 36 37 38 39 3A 3B 2C 2D 2E 2F 0D 1F 01 5B 0A 08 09 20Keyboard Rows 4-6, UNshifted, Caps Lock
*3106LD A,(3880H)3A 80 38Load A with the value held at 3880H (which are the SHIFT KEYS)
*3109LD HL,414FH21 F4 41Point HL to 414FH
*310CAND 7CHE6 7CMASK the value held in the SHIFT KEY RAM location against 0111 1100 to keep only bits 2, 3, 4, 5, and 6 live
*310EOR AB7OR A against itself to reset the flags
*310FJR Z,3142H28 31If the Z FLAG is set, JUMP to 3142H to continue checking fro special keys
*3111LD E,A5FPut the masked A into E.
*3112XOR (HL)AEToggle against the old image.
*3113LD (HL),E73Save the new image into (HL).
*3114AND EA3Mask Register E against Register A.
*3115JR Z,3157H28 40If ZERO then the LEFT SHIFT PRESSED was JUMP to 3157H to restart parsing the keyboard
*3117LD BC,05C4H01 C4 05Load BC with 05C4H to set up a 1/50 second delay for de-bounce.
*311ACALL 0060HCD 60 00GOSUB 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.
*311DLD A,(3880H)3A 80 38Load A with the value held at 3880H (which are the SHIFT KEYS)
*3120AND 7CHE6 7CMASK the value held in the SHIFT KEY RAM location against 0111 1100 to keep only bits 2, 3, 4, 5, and 6 live
*3122CP EBBCompare A with E.
*3126RLA17Rotate 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.
*3127RLA17Rotate 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.
*3128JR NC,312EH30 04We 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
*312ALD A,(41F3H)3A F3 41Load Register A with the value of the character to be returned when the F3 key is pressed
*312DRETC9RETurn to Caller
*312E - Model 4 Gen 2 - KEYBOARD Routine - Check and Process the F2 Key or Jump Away.
*312ERLA17Rotate 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.
*312FJR NC,3135H30 04If the NC FLAG is set, it isn't the F2 key, so JUMP to 3135H to test the F1 key
*3131LD A,(41ECH)3A EC 41Load Register A with the value of the character to be returned when the F2 key is pressed
*3134RETC9RETurn to Caller
*3135 - Model 4 Gen 2 - KEYBOARD Routine - Check and Process the F1 Key or Jump Away.
*3135RLA17Rotate 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.
*3136JR NC,313CH30 04If the NC FLAG is set, it isn't the F1 key, so JUMP to 313CH to keep checking special keys
*3138LD A,(41EBH)3A EB 41Load Register A with the value of the character to be returned when the F1 key is pressed
*313BRETC9RETurn to Caller
*33C4 - Model 4 Gen 2 - KEYBOARD Routine - Part of the Keyboard Scan Routine. Keep Checking Special Keys.
*313CRLA17Rotate 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.
*33CA - Model 4 Gen 1 - KEYBOARD Routine - Part of the Keyboard Scan Routine.
*3142LD (HL),A77Save the character held in Register A into the memory location pointed to by Register Pair HL
*3143LD A,FFH3E FFLoad A with FF to set up for a FLAG = 0FFH = NO CONTROL.
*3145LD HL,3840H21 40 38Load HL with 3840H to start a check for a DOWN ARROW.
*3148BIT 4,(HL)CB 66Test BIT 4 of (HL) to check for a DOWN ARROW.
*314AJR Z,3154H28 08JUMP to 3154H if the a DOWN ARROW was NOT pressed.
*314CSLA LCB 25Next we need to check for a a LEFT SHIFT so shift L left.
*314EBIT 0,(HL)CB 46Test BIT 0 of (HL) to check for a LEFT SHIFT.
*3152LD A,1FH3E 1FLoad A with 1F to set up for FLAG = CONTROL KEY.
*3154LD (4224H),A32 24 42Save the CONTROL FLAG into (4224H).
NOTE: 4224H Holds the CONTROL KEY flag.
*315ARET NCD0If that routine exited with NC FLAG set, RETurn
*315ECP 1AHFE 1ACheck A against 1AH to see if we have a SHIFT+DOWN ARROW.
*3163OR AB7Set the flags based on Register A
*3167LD HL,4224H21 24 42Set Register Pair HL to 4224H, which is the CONTROL KEY flag.
*316ABIT 7,(HL)CB 7ETest Bit 7 of the CONTROL KEY flag in RAM.
*316ECP 2AHFE 2ACheck A against a * key.
*3170JP Z,37C7HCA C7 37If 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.
*3173AND (HL)A6Prepare to check for a BREAK key by masking A against the memory contents of HL ...
*3174CP 01HFE 01... and COMPARING it to 01H.
*3176RET NZC0If the result of the compare is NOT zero, then RETURN.
*3177RST 28HEFIf we are here, then a BREAK key was hit, so call RST 28H to handle the BREAK key.
*3178RETC9RETURN to Caller
*3179 - Model 4 Gen 2 - Continuing Initialization Routine by setting up the RS-232.
*3179XOR AAFSet Register A to 0
*317AOUT (E8H),AD3 E8Output A to port E8H.
NOTE: Port E8H is the RS-232 Status Register & Master Reset. Outputting ANYTHING to Port E8H resets the RS-232.
*317CLD A,EEH3E EESet Register A to EEH for outputting to the RS-232 Baud Rate Select and Sense Switches: (Port E9H)
*317EOUT (E9H),AD3 E9Initialize 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
*3180LD A,6DH3E 6DSet Register A to 6DH for outputting to the RS-232 UART Control Register and Status Registe (Port EAH)
*3182OUT (EAH),AD3 EASent 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)
*3184IN A,(E8H)DB E8Poll the RS-232 Status Register and Master Reset of Port E8H and put the value into Register A.
*3186BIT 6,ACB 77Test Bit 6 of the RS-232 Status Register to check Data Set Ready (Pin 6 of the DB-25).
*3188JR Z,3184H28 FAIf the DATA SET READY is ZERO then LOP back 2 instructions to 3184 to poll again
*318ALD A,6CH3E 6CSet Register A to 6CH (0110 1100) for outputting to the RS-232 UART Control Register and Status Registe (Port EAH)
*318COUT (EAH),AD3 EASend 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
*318EIN A,(E8H)DB E8Poll the RS-232 Status Register and Master Reset of Port E8H and put the value into Register A.
*3190BIT 6,ACB 77Test Bit 6 of the RS-232 Status Register to check Data Set Ready (Pin 6 of the DB-25).
*3192JR NZ,318EH20 FAIf the DATA SET READY is ZERO then LOOP back 2 instructions to 318EH to poll again
*3194LD A,0FH3E 0FSet Register A to 0FH
*3196CALL 37D5HCD D5 37GOSUB to 37D5H to send the Character in Register A to the RS-232, once the RS-232 shows ready to accept that character.
*3199CALL 3086HCD 86 30GOSUB to 3086 to Poll the UART and wait for the P FLAG to not be set and then CONTINUE at 306CH
*319CCALL 3086HCD 86 30GOSUB to 3086 to Poll the UART and wait for the P FLAG to not be set and then CONTINUE at 306CH
*319FCALL 37D5HCD D5 37GOSUB to 37D5H to send the Character in Register A to the RS-232, once the RS-232 shows ready to accept that character.
*31A2-31A4JP 3517HC3 17 35JUMP 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
**3179LD A,(4210H)3A 10 42Get 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.
**317COR 10HF6 10OR against 10H (0001 0000) to turn all I/O ports on
**317ELD (4210H),A32 10 42Put the masked status back, first by loading it into 4210H
**3181OUT (ECH),AD3 EC... and then by sending it to 0ECH which is the same as 04210.
**3183LD A,08H3E 08Put an 08H into Register A
**3185OUT (D3H),AD3 D3Send 08H to Port D3H, which is the Network 4 Omninet MSB pointer
**3187XOR AAFPut an 00H into Register A
**3188OUT (D1H),AD3 D1Send 08H to Port D1H, which is the Network 4 Omninet LSB pointer
**318ALD BC,00D0H01 D0 00In 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
**318DLD HL,7000H21 00 70In further preparation for the INIR commands, point HL to the BUFFER for the code, which is 7000H
**3190PUSH HLE5Save the 7000H Buffer start point to the top of the stack
**3191INIRED B2Write a byte from port C to the memory location pointed to by HL, and then increase HL and decrease B.
**3193INIRED B2Write a byte from port C to the memory location pointed to by HL, and then increase HL and decrease B.
**3195INIRED B2Write a byte from port C to the memory location pointed to by HL, and then increase HL and decrease B.
**3197INIRED B2Write a byte from port C to the memory location pointed to by HL, and then increase HL and decrease B.
**3199INIRED B2Write a byte from port C to the memory location pointed to by HL, and then increase HL and decrease B.
**319BINIRED B2Write a byte from port C to the memory location pointed to by HL, and then increase HL and decrease B.
**319DINIRED B2Write a byte from port C to the memory location pointed to by HL, and then increase HL and decrease B.
**319FINIRED B2Write a byte from port C to the memory location pointed to by HL, and then increase HL and decrease B.
**31A1RETC9RETurn to CALLer
**31A2NOP00
**31A3NOP00
**31A4NOP00
31A5 - Output the TIMING MARK to the cassette
31A5LD A,01HLoad A with 01H. This is to prepare to send 0.46V to tape.
31A7OUT (0FFH),ALoad 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.
31A9LD B,0DHLoad B with 0DH in as a loop counter.
31ADLD A,02HLoad A with 01H. This is to prepare to send 0.0V to tape.
31AFOUT (0FFH),ALoad 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.
31B1LD B,0DHLoad B with 0DH in as a delay.
31B5GOSUB to 31F3 to send 0.46V to tape.
NOTE: 31F3H resets the cassette port, and then output a 0 to the Cassette Port FFH.
31B8LD B,78HLoad B with 78H in as a delay.
31BCRETRETURN.
31C0 - Turn Off The Cassette
31C0LD A,(4213H)Load A with the memory contents of 4213H.
NOTE: 4213H is the default interrupt vector setting for the cassette.
31C3OUT (0E0H),AOutput 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
31C5IN 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.
31C7LD 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.
31CAAND FDHMask A against FDH (1111 1101) to zero bit 1.
31CFEIEnable Interrupts.
31D0RETRETURN.
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 "**"
31D1EX DE,HLSwap DE and HL to remove the return address.
31D2EX (SP),HLSwap the memory contents pointed to by the STACK POINTER and HL (which is now what DE was).
31D3PUSH BCSave BC to the STACK.
31D4PUSH HLSave HL to the STACK.
31D5EX DE,HLSwap DE and HL back.
31D6IN 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.
31D8LD DE," "Load DE with SPACESPACE.
31DBLD (3C3EH),DELoad the screen memory location of 3C3EH with DE.
31E8 - Turn On The Cassette - Actually Set the Bit Mask and Output the Command
31E8LD 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.
31EBOR 02HOR A with 02H (0000 0010) to set Bit 1.
31EDLD (4210H),ASave 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.
31F0OUT (0ECH),AOutput that Bit Mask to port 0ECH.
31F2RETRETURN.
31F3 - Reset the Cassette Port. This routine OUTputs a 0 to the Cassette Port FFH
31F3XOR AWe want to reset the cassette port so we zero A.
31F4OUT (0FFH),ALoad 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.
31F6RETRETURN.
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.
31F7LD A,(HL)Load Register A with the memory contents pointed to by Register Pair HL.
31F8SUB 23HSubtract 23H so that we can test to see if the caller was a PRINT #command.
31FAIf 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.
3200RST 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.
3202RETIf we are here, then we have PRINT #n,so we RETURN.
3203 - Vector for a SLOW cassette read
3203LD B,08HLoad B with 8, representing the need to LOOP for 8 bits.
3205GOSUB to 3220H.
NOTE: 3220H reads the tape until it finds a timing mark or the BREAKis hit.
320ALD A,(4212H)Put the contents of 4212H into A.
NOTE: 4212H holds the cassette blinker counter.
320DINC ABump A.
320EAND 5FHMask A against 5F (0101 1111) to turn Bit 7 and Bit 5 off.
3210LD (4212H),APut A into 4212H.
NOTE: 4212H holds the cassette blinker counter.
3215LD A,(3C3FH)Load A with the screen contents at position 3C3FH.
3218XOR 0AHXOR 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).
321ALD (3C3FH),APut the revised A onto the screen at position 3C3FH.
321DLD A,DPut D (the byte) into A.
3220 - Cassette - Keep reading tape looking for a timing mark or BREAK.
3220PUSH BCSave BC to the STACK.
3221IN A,(FFH)Poll Port FFH with the results into Register A.
NOTE: FFH is the Cassette Port.
3223RLAPut 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).
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)
322ELD B,6EHLoad B with 6EH, which is the length of the timing mark.
Reset the cassette port
Next, wait for 98H Units (the length until a data pulse is expected)
3235LD B,98HLoad B with 98H, which is when the next data pulse should be available.
... continue
3239IN A,(FFH)Poll Port FFH and put the results into A.
NOTE: Port FFH is the Cassette Port.
323BPOP BCRestore BC from the STACK.
323CRLARotate 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.
323DRL DRotate 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.
3241 - Vector for a SLOW cassette write. On entry A is the byte to output.
3241PUSH AFSave AF to the STACK.
3242PUSH BCSave BC to the STACK.
3243PUSH DESave DE to the STACK.
3244LD C,08HLoad C with an 8, representing 8 bits to be written.
3246LD D,ALoad D with A.
NOTE: D will be the DATA BYTE.
324ARLC DRotate 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.
324CIf 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.
3251DEC CReduce the counter holding the number of bits to deal with by 1.
3254POP DERestore DE from the STACK.
3255POP BCRestore BC from the STACK.
3256POP AFRestore AF from the STACK.
3257RETRETURN.
3258 - "Write a 0 Bit" by simply waiting the appropriate amount of time and doing nothing.
325E - SLOW tape header write
325EPUSH HLSave HL to the STACK.
3262LD (420CH),HLLoad the memory contents of 420CH with HL.
NOTE: 420CH is the TAPE WRITE VECTOR.
3265LD B,53HLoad B with 53H (Decimal: 83) in prepartion to output 83 ZEROes.
3267XOR AClear A and all flags.
326DLD A,0A5HLoad A with A5H.
NOTE: A5H is the OUTPUT SYNC BYTE.
3274 - SLOW tape header read
3274PUSH HLSave HL to the STACK.
3278LD (420EH),HLPut the vector for a slow cassette read into the memory location at 420EH.
NOTE: 420EH is the TAPE READ VECTOR.
327BLD B,40HLoad B with 40H to set up a loop of 64 to try to find 64 zeroes.
327DLD D,00HLoad D with 0.
3282LD A,DLoad A with the D (the data byte) to begin to check the current data byte.
3283OR ASet up the flags.
3288GOSUB to 3220H.
NOTE: 3220H reads the tape until it finds a timing mark or the BREAKis hit.
328BLD A,DLoad A with the D (the data byte) to begin to check the current data byte.
328CCP 0A5HCompare A to A5H looking for a SYNC BYTE.
3290LD HL,"**"In preparation to display a "**", load HL with **.
3293LD (3C3EH),HLPut HL onto the screen at location 3C3EH.
3296LD A,HLoad A with H (which is a *.
3297POP HLRestore HL from the STACK.
3298POP BCRestore BC from the STACK.
3299POP DERestore DE from the STACK.
329ARETRETURN.
329B - FAST tape header write.
329BPUSH HLSave HL to the STACK.
329FLD (420CH),HLLoad the memory contents of 420CH with HL.
NOTE: 420CH is the TAPE WRITE VECTOR.
32A2LD B,00HLoad B with 00H to set up a loop of 256 to output 256 "55H" bytes.
32A4LD A,55HLoad A with "55H".
32A6GOSUB to 32B4H.
NOTE: 32B4 restore all registers from the STACK, and Fill C with A, and JUMP to cassette write
32ABLD A,7FHLoad A with 7FH.
NOTE: 7FH is the OUTPUT SYNC BYTE.
32ADGOSUB to 32B4H.
NOTE: 32B4 restore all registers from the STACK, and Fill C with A, and JUMP to cassette write
32B0LD A,A5HLoad A with A5H.
NOTE: A5H is the SLOW SYNC BYTE.
32B4 - Restore all registers from the STACK, and Fill C with A, and JUMP to cassette write.
32B4PUSH AFSave AF to the STACK.
32B5PUSH BCSave BC to the STACK.
32B6PUSH DESave DE to the STACK.
32B7LD C,ALoad C with A.
32BA - Save all registers to the STACK, and Fill C with A, GOSBUB to write out the START BIT ...
32BAPUSH AFSave AF to the STACK.
32BBPUSH BCSave BC to the STACK.
32BCPUSH DESave DE to the STACK.
32BDLD C,ALoad C with A.
32C1 - Call 3335H to Output a Bit 8 Times
32C1LD B,08HLoad B with an 8 to set up a loop for 8 bits.
32CA - Read the start bit, read 8 bits, check for error, and flash the star
32CDLD B,08HLoad B with an 8 to set up a loop for 8 bits.
32DA - FAST tape header read.
32DAPUSH HLSave HL to the STACK.
32DELD (420EH),HLLoad the TAPE READ VECTOR (memory location of 420EH) with the 32CAH.
32E1LD A,01HLoad A with a 1 to set the interrupt.
32E3OUT (0E0H),AOUTPUT 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
32E5LD B,80HSet up a loop of 80H (128) to try to read 128 bits.
32EALD A,CLoad A with C (which is the pulse width).
32EBCP 0FHCompare A to 0FH to see if the pulse width was too short.
32EFCP 3EHCompare A to 3EH to see if the pulse width was too long.
32F8LD B,40HSet up a loop of 40H (Decimal: 64) to try to read 64 bits.
3300LD D,CLoad D with C (which holds the delay count).
3304LD A,DLoad A with D to set up to find the difference in the delays.
3305SUB CSubtract C (the delay count) from A (which holds D).
3308NEGA = 0 - A.
330ACP 0DHCompare 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.
330CIf the CARRY FLAG is set, JUMP to 3313H which will bump L and continue the 64 bit loop.
330EINC HBump HL since we have one more zero bit.
3313INC LBump L since we have one more bit.
3316LD A,40HLoad A with 40H (64).
3318CP HCompare A with H to check for bits.
331BCP LCompare with A to check for one bits.
331ELD A,02HLoad A with 2 to set the interrupt vector.
3320OUT (0E0H),AOutput 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
3325LD D,00HZero D.
332DLD A,DLoad A with D (the read byte).
332ECP 7FHCompare A against 7F to check for a MARKER BYTE.
3335RLC CWe 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.
3341DEC DDecrement D as DELAY #1.
3344LD A,02HLoad A with a 2 to set up for a write of 0 VOLTS to TAPE.
3346OUT (0FFH),ALoad 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.
3348DEC EDecrement E as DELAY #2.
334BLD A,01HLoad A with 01H to prepare to send 0.85 VOLTS to TAPE.
334DOUT (0FFH),ALoad 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.
334FRETRETURN.
3350H - READ a BIT
3350EIEnable Interrupts.
3351LD C,00HLoad C with 0.
3353INC CBump C.
3354LD A,(3840H)Load A with the contents of 3840H so as to check for a BREAK.
3357AND 04HMask A with 4 (0000 0100).
335BDIDisable Interrupts.
335FLD (3C3EH),HLPut the "BK" in HL onto the video screen at 3C3EH.
3362JUMP 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
3365LD E,01HLoad E with 01H (Binary: 0000 0001) to make the bit go high.
3369LD E,00HLoad E with 0 to make the bit go low.
336BLD A,06HLoad A with 6.
336DADD A,CAdd A (6) to C, so COUNT = COUNT + 6.
336ELD C,APut the count held in A back into C.
336FIN A,(FFH)Poll FFH (to get the level) into A.
NOTE: FFH is the Cassette Port.
3371AND 01HMask A with 1 (0000 0001) to keep only Bit 0.
3373CP ECompare A with E (which was the set level).
3376POP AFRestore AF from the STACK.
3377POP AFRestore AF (the REMOTE CALLER'S ADDRESS) from the STACK.
3378RETRETURN to the caller's caller.
3379POP AFRestore AF from the STACK.
337AEIEnable Interrupts.
337BRETRETURN back top the loop.
337CH - Check for a Data Error.
337CLD A,CLoad A with C (the count).
337DCP 22HCompare 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
337FRL DWe 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.
3381CP 0FHMake 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.
3383If A < 0FH then it was too quick and we have a data error so JUMP forward a few instructions 3388H.
3385CP 3EHCompare A against 3EH to make sure it was not too slow.
3387RET CIf it wasn't too slow, RETURN.
3388LD A,44HIt was too slow, so load A with a D.
338ALD (3C3EH),APut the "D" on the screen at video location 3C3EH.
338DRETRETURN.
*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).
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).
*3391LD A,(3880H)Load A with the value held at 3880H (which are the SHIFT KEYS)
*3394AND 7CHMASK the value held in the SHIFT KEY RAM location against 0111 1100 to keep only bits 2, 3, 4, 5, and 6 live
*3396OR AOR A against itself to reset the flags
*3399LD E,APut the masked A into E.
*339AXOR (HL)Toggle against the old image.
*339BLD (HL),ESave the new image into (HL).
*339CAND EMask Register E against Rgister A.
*33A2GOSUB 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.
*33A5LD A,(3880H)Load A with the value held at 3880H (which are the SHIFT KEYS)
*33A8AND 7CHMASK the value held in the SHIFT KEY RAM location against 0111 1100 to keep only bits 2, 3, 4, 5, and 6 live
*33AACP ECompare A with E.
*33AERLARotate 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.
*33AFRLARotate 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.
*33B0We 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
*33B2LD A,(41F3H)Load Register A with the value of the character to be returned when the F3key is pressed
*33B5RETRETurn to Caller
*33B6 - Model 4 Gen 1 - KEYBOARD Routine - Check and Process the F2 Key or Jump Away.
RLARotate 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.
*33B9LD A,(41ECH)Load Register A with the value of the character to be returned when the F2key is pressed
*33BCRETRETurn to Caller
*33B6 - Model 4 Gen 1 - KEYBOARD Routine - Check and Process the F1 Key or Jump Away.
RLARotate 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.
*33BEIf the NC FLAG is set, it isn't the F1, so JUMP to 33C4 to keep checking special keys
*33C0LD A,(41EBH)Load Register A with the value of the character to be returned when the F1key is pressed
*33C3RETRETurn to Caller
*33C4 - Model 4 Gen 1 - KEYBOARD Routine - Part of the Keyboard Scan Routine. Keep Checking Special Keys.
RLARotate 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.
*33CA - Model 4 Gen 1 - KEYBOARD Routine - Part of the Keyboard Scan Routine.
LD (HL),ASave the character held in Register A into the memory location pointed to by Register Pair HL
33CBLD A,FFHLoad A with FF to set up for a FLAG = 0FFH = NO CONTROL.
33D0BIT 4,(HL)Test BIT 4 of (HL) to check for a DOWN ARROW.
33D4SLA LNext we need to check for a a LEFT SHIFTso shift L left.
33D6BIT 00H,(HL)Test BIT 0 of (HL) to check for a LEFT SHIFT.
33DALD A,1FHLoad A with 1F to set up for FLAG = CONTROL KEY.
33DCLD (4224H),ASave the CONTROL FLAG into (4224H).
NOTE: 4224H Holds the CONTROL KEY flag.
*33E2RET NCIf that routine exited with NC FLAG set, RETurn
33E6CP 1AHCheck A against 1AH to see if we have a SHIFT DOWN ARROW.
*33EBOR ASet the flags based on Register A
*33F2BIT 7,(HL)Test Bit 7 of the CONTROL KEY flag in RAM.
*33F6CP 2AHCheck A against a *.
*33FBAND (HL)Prepare to check for a BREAKby masking A against the memory contents of HL ...
*33FERET NZIf the result of the compare is NOT zero, then RETURN.
*33FFRST 28HIf we are here, then a BREAKwas hit, so call RST 28H to handle the BREAK.
*3400RETRETURN to Caller
*3401 - Model 4 Gen 1 - This is the BOOTSTRAP. Clears ports, checks for a BREAKkey and a Floppy Controller.
3401IM 1Set the INTERRUPT MODE to 1.
*3406LD B,0FHLet Register B = 0FH
*3408LD C,88HLet Register C = the CRT Controller Control Register Port
*340COUT (89H),ASend Register A to the CRT Controller Data Register
3410OUT (0E4H),AOutput A to Port E4H.
NOTE: Port E4H is the non-maskable interrupt latch. This is to clear the non-maskable interrupt status.
3412OR 20HOR 20H (0010 0000) against A to turn on Bit 5.
3414OUT (0ECH),AOutput 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.
3416LD A,81HLoad A with 81H (Decimal 129, Binary 10000001).
3418OUT (0F4H),AOutput 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.
341ALD A,D0HLoad A with D0H (Decimal: 208, Binary: 1101 0000).
341COUT (0F0H),AOutput "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
*341EPUSH BCUndertake a short delay of PUSHING BC, POPPING BC, and NOPing
*341FPOP BC
*3420NOP
3421LD A,04HLoad A with a 4 (Binary: 0000 0100).
3423OUT (0E0H),AOutput 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
3425LD A,0BHLoad A with 0BH (Binary: 00001011).
3427OUT (0F0H),AOutput 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).
3429Next we need to initialize some ports via a LDIR. The next 4 commands will move the 76 (4C) bytes from 36AAH to 4000H.
342C
342F
3432LDIR
3434Next we need to initialize more ports via a LDIR. The next 4 commands will move the 64 (40) bytes from 36F9H to 41E5H.
3437
343A
343DLDIR
3448IN 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
344AINC ABump the FDC Status by 1 (which would turn a FFH (128) into a 00H), making a Z flag mean "Disk Controller Not Ready".
344BIf the FDC Stats + 1 is zero, then we have no disk controller, so jump to Non-Disk BASIC at 3105H.
344EIf we are here, then we have a floppy controller, so set up for a loop of 65,535 times.
3451DEC BCDecrease BC by 1.
3452LD A,81HSet A to 81H (Decimal: 10000001).
3454OUT (0F4H),ASend 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.
3456LD A,B
OR CTo 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.
345BIN 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
345DBIT 2,ATest Bit 2 of A (the Floppy Status). If it is 0 then we haven't made it to track 0, otherwise we have.
345FStill 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.
3461LD E,05HSet up for a loop of 5 using Register E.
3466IN 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
3468BIT 01H,ACheck A to see if bit 1 (meaning "Drive Busy") is set.
346CDEC BCIf bit 1 is not set (meaning, the drive is NOT busy), reduce the counter (BC) by 1.
346DLD A,81HLoad A with 81H (Decimal: 129, Binary: 1000 0001).
346FOUT (0F4H),AOutput 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.
3471LD A,B
OR CTo 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.
3475If 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.
347BIf 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.
347DDEC EIf we are here, it found a diskette so next we need to find the index mark. First decrement E by 1.
3463HLD A,81HLoad A with 81H (Decimal: 129, Binary: 1000 0001).
3482OUT (0F4H),AOutput 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.
3484We 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.
3487LD (404AH),HL
348ALD A,C3HLoad A with C3H (Decimal: 195, Binary 1100 0011).
348CLD (4049H),APut the C3H into 4049H.
NOTE: 4049H is the Non-Maskable Interrupt Vector.
348FLD A,80HLoad A with 80H (Decimal: 128, Binary 1000 0000).
3491OUT (0E4H),AOutput "1000 0000" to Port E4H.
NOTE: Port E4H is the non-maskable interrupt latch.
3499LD A,01HPrepare to read Sector 1 by loading A with a 1.
349BOUT (0F2H),AOutput 1 to Port F2H.
NOTE: Port F2H is the Floppy Disk Controller Track Register.
349DLD A,80HPrepare to read a single sector by loading A with 80H (Decimal: 128, Binary: 1000 0000).
349FOUT (0F0H),AOutput "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).
34A1GOSUB to 37E1H to delay 3 instruction cycles, arguably the time it will take for the FDC to respond to the port command.
34A4IN 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
34A6AND 02HMask 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.
34ABINIInput the data byte.
34ADLD A,81HLoad A with 81H (Decimal: 129, Binary: 1000 0001) to select disk 0.
34AFOR 40HOR A with 40H (Binary: 0100 0000) to set A to double-density.
34B1OUT (0F4H),AOutput 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.
34B3INIInput the data byte.
34B5Loop 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.
34B8XOR AClear A and all flags.
34B9OUT (0E4H),ASend a 0 to Port E4H.
NOTE: Port E4H is the non-maskable interrupt latch. This is to clear the non-maskable interrupt status.
34BELD (4049H),HLLoad "45EDH" into 4049H.
NOTE: 4049H is the Non-Maskable Interrupt Vector.
34C1GOSUB to 37E1H to delay 3 instruction cycles, arguably the time it will take for the FDC to respond to the port command.
34C4IN 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
34C6POP HLClean up the Stack.
34C7AND 1CHMask 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.
34CCIf 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.
34CEIf 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.
34D1IN A,(E4H)Poll Port E4H into A.
NOTE: Port E4H is the Non-Maskable Interrupt Latch.
34D3BIT 5,ATest Bit 5 of Port E4 against A.
NOTE: Port E4H is the Non-Maskable Interrupt Latch.
*34DA - Model 4 Gen 1 - Part of the Keyboard routine.
*34DDAND 03HMask the SHIFT Register against 03H (Binary: 0000 0011) to keep only Bits 0 and 1 to check for shifts.
*34DFIf the masked A is 0, then we have no shifts, and skip the next instruction (to 34E3H).
*34E1SET 6,DSet BIT 6 of D to offset D for shifts.
LD A,(4019H)Load A with the contents of memory location 4019H to check for CAPS LOCK.
NOTE: 4019H is the CAPS LOCK TOGGLE.
*34E6OR ASet the flags.
*34E9SET 7,DSet 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.
*34EBLD A,(3880H)Put the contents of memory location 3880H into A to GET SHIFT(S).
*34EEAND 03HMask the SHIFT Register against 03H (Binary: 0000 0011) to keep only Bits 0 and 1 to check for shifts.
*34F0If the masked A is 0, then we have no shifts, and skip the next instruction (to 34F4H).
*34F2RES 7,DRESET 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.
34F7LD E,DWe need DE to be the OFFSET, so load E with D and ...
34F8LD D,00H... load D with 00.
34FAADD HL,DEAdd DE (the offset over the keyboard table) to HL (the keyboard table).
34FBLD A,(HL)Get the character pointed to by (HL) and put it into A.
*34FCRETRETurn 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.
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.
*3501RLCARotate the contents of A left one bit position. The contents of bit 7 are copied to the carry flag and bit 0.
*3504RLCARotate the contents of A left one bit position. The contents of bit 7 are copied to the carry flag and bit 0.
*3505If the CARRY FLAG is set, we have a non-graphic characters, so skip the next instructions.
*3507LD A,2EHOverwrite the current character held in Register A with a ., so that all graphic characters are printed as .'s.
Call the PRINT CHARACTER routine at 003BH (which sends the character in the A register to the printer).
*350FINC HLBump HL to the next character on the screen.
*3510BIT 6,HCheck 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).
*3515LD A,LPrepare to test of end of line by loading Register A with Register L.
*3516AND 03FHAND 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).
*3518If 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.
*351AGOSUB 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.
*351DLoop 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
*351FNOP
*3520NOP
*3521NOP
*3522NOP
*3523NOP
*3524NOP
*3525NOP
*3526NOP
*3527NOP
*3528NOP
*338EH - Model 4 Gen 2 - Jump Point for Keyboard Input.
*338ELD BC,3801H010138Load BC with 3801H (KEYBOARD ROW 0).
*3391LD HL,4036H213640Load HL with 4036H (BUFFER ROW 0).
*3394LD D,00H1600Load D with 0 (so D = ROW 0).
*3396LD A,(BC)0Aoad A with the contents held in (BC) to check the keyboard row.
*3397LD E,A5FLoad E with the contents held in (BC) to check the keyboard row.
*3398XOR (HL)AEXOR (HL) to set changed bits.
*3399LD (HL),E73Save the scan back into (HL).
*339AAND EA3Mask A with E (to mask the released keys).
Go to the next row
*339DINC D14Bump D so that D holds the NEXT row number.
*339EINC HL23Bump HL so that HL holds the NEXT buffer location.
*339FRLC CCB01We 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.
*33A1JP P,3396HF29633If 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
*33A4LD A,(41FDH)3AFD41If 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.
*33A7LD L,A6FLoad L with the contents of Register A.
*33A8LD A,(41FEH)3AFE41Load A with the memory contents of 41FEH.
NOTE: 41FEH is the saved IMAGE at the saved position in the keyboard scan data.
*33ABAND (HL)A6MASK A against (HL) to see if the previous keys are still pressed.
*33AFSBC HL,HLED62Zero HL by subtracting HL from HL.
*33B1LD (4201H),HL220142Load 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.
*33B7LD (41FFH),HL22FF41Load the memory location at 41FFH with 05DCH.
NOTE: 41FFH holds the keyboard scan repeat delay count.
*33BAXOR AAFClear A and all flags.
*33BBRETC9RETurn to Caller
*33BC - Model 4 Gen 2 - Keyboard Routine - If the same keys are still pressed then we need to deal with debounce.
*33BCLD E,A5FLoad E with A.
*33BDPUSH BCC5Save BC to the STACK.
*33C1CALL 0060HCD 60 00GOSUB 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.
*33C4POP BCC1Restore BC from the STACK.
*33C5LD A,(BC)0ALoad A with the memory contents pointed to by BC to re-check the keyboard.
*33C6AND EA3Compare A against E to check the pattern.
*33C7RET ZC8If not the same pattern then RETURN.
*33C8LD (41FEH),A32 FE 41If it is the same pattern then save A into (41FEH).
NOTE: 41FE is the SAVED IMAGE AT POSITION.
*33CBLD A,L7DLoad A with L (the scan position).
*33CCLD (41FDH),A32 FD 41Save A into (41FDH).
NOTE: 41FDH is the SAVED POSITION IN SCAN.
*33CFLD A,D7ALoad A with D (8 * ROW #).
*33D0RLA17Rotate 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.
*33D1RLA17Rotate 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.
*33D2RLA17Rotate 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.
*33D3LD D,A57Copy Register A into Register D
*33D4LD A,E7BCopy Register E into Register A
*33D5RRCA0FRotate A right one bit, with the contents of BIT 0 being put into BOTH the CARRY FLAG and BIT 7. D = 8* ROW # + KEY #.
*33D6RET CD8If the contents of BIT 0 of A was SET, RETurn
*33D7INC D14Bump D so that D holds the NEXT row number.
*33DA - Model 4 Gen 2 - Keyboard Routine.
*33DAPUSH HLE5Save HL to the top of the STACK.
*33DBLD HL,(4201H)2A0142Load HL with the repeat delay counter.
NOTE: 4201H is the REPEAT DELAY COUNTER.
*33DEINC HL23Bump HL.
*33DFLD (4201H),HL220142Load the memory location at 4201H with HL to clear the repeat counter.
NOTE: 4201H is the REPEAT DELAY COUNTER.
*33E2LD DE,(41FFH)ED5BFF41Load DE with the byte stored at 41FFH.
NOTE: 41FFH is the REPEAT DELAY COUNT.
*33E6SBC HL,DEED52Subtract with CARRY DE from HL.
*33E8POP DED1Restore old HL (which is what is in the stack) into DE.
*33ECXOR AAFClear A and all flags.
*33EDLD (DE),A12Put a 0 into the memory location pointed to (DE) to let the key be re-read.
*33EELD (4201H),HL220142Load the memory location at 4201H with HL to clear the repeat counter.
NOTE: 4201H is the REPEAT DELAY COUNTER.
*33F1LD L,96H2E96Load L with 96H to set a fast repeat count.
*33F3LD (41FFH),HL22FF41Save HL into the memory location at 41FFH.
NOTE: 41FFH is the REPEAT DELAY COUNT.
*33F9 - Model 4 Gen 2 - Enter NON-Disk BASIC.
*33FF - Model 4 Gen 2 Routine - Initialization - Process the CASS? Question
*33FFEIFBEnable Interrupts.
*3400CALL 3421HCD 21 34GOSUB to 3421H which loads A with a carrage return, and jumps to 0033H to display it.
*3403LD HL,05D1H21 D1 05Load HL with the address of the "CASS?" message.
*3406CALL 021BHCD 1B 02GOSUB to 021BH.
NOTE: 021BH will display the character at (HL) until a 03H is found.
*3409CALL 0049HCD 49 00GOSUB to 0049H.
NOTE: 0049H is the $KBWAIT routine which scans the keyboard and returns with the key pressed, if any, in register A.
*340CCP 0DHFE 0DCheck to see if Register A is holding a ENTER
*3410PUSH AFF5Save AF to the STACK.
NOTE: Register A currently holds the character pressed in response to the "CASS?" message.
*3411CALL 0033HCD 33 00GOSUB to 0033H pt display the character held in Register A at the current cursor position.
*3414POP AFF1Restore the answer to CASS? from the STACK into Register A.
*3415CP 48HFE 48Compare the answer to the CASS? Prompt held in Register A against with 48H (ASCII: H).
*3419CP 4CHFE 4CCompare the answer to the CASS? Prompt held in Register A against with 4CH (ASCII: L).
Set the flag for LOW SPEED CASSETTE
*341DXOR AAFSet A to 0.
*341ELD (4211H),A32 11 42Save 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
*3421LD A,0DH3E 0DPut a CARRIAGE RETURN into Register A
*3423JP 0033HC3 33 00JUMP 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.
*3426IM 1ED56Set the INTERRUPT MODE to 1.
*3428LD SP,407DH317D40Load the STACK POINTER with 407DH.
*342BLD B,0FH060FLet Register B = 0FH
*342DLD C,88H0E88Let Register C = the CRT Controller Control Register Port
*342FOUT (C),BED41Set the CRT Controller Control Register Port to the contents of B (which are decreasing as the loop progresses)
*3431OUT (89H),AD389Send Register A to the CRT Controller Data Register
*3435OUT (E4H),AD3E4Clear the Non-Maskable Interrupt Latch by sending the contents of Register A to Port E4H
*3437OR 20HF620OR 20H (0010 0000) against A to turn on Bit 5.
*3439OUT (ECH),AD3ECOutput 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.
*343BLD A,81H3E81Load A with 81H (Decimal 129, Binary 10000001).
*343DOUT (F4H),AD3F4Output 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.
*343FLD A,D0H3ED0Load A with D0H (Decimal: 208, Binary: 1101 0000).
*3441OUT (F0H),AD3F0Output "D0H" to the FDC Status Register at Port F0H. This resets the FDC and puts it in mode 1
*3443PUSH BCC5Undertake a short delay of PUSHING BC, POPPING BC, and NOPing
*3444POP BCC1Undertake a short delay of PUSHING BC, POPPING BC, and NOPing
*3445NOP00Undertake a short delay of PUSHING BC, POPPING BC, and NOPing
*3446LD A,04H3E04Load A with a 4 (Binary: 0000 0100).
*3448OUT (E0H),AD3E0Output 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.
*344ALD A,0BH3E0BLoad A with 0BH (Binary: 00001011).
*344COUT (F0H),AD3F0Output 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).
*344ELD HL,36AAH21AA36Next we need to initialize some ports via a LDIR. The next 4 commands will move the 76 (4C) bytes from 36AAH to 4000H.
*3451LD DE,4000H110040Set the LDIR Destination to 4000H
*3454LD BC,004CH014C00Set the number of bytes to move to 4CH
*3457LDIREDB0Move the 76 (4C) bytes from 36AAH to 4000H
*3459LD HL,36F9H21F936Next we need to initialize more ports via a LDIR. The next 4 commands will move the 64 (40) bytes from 36F9H to 41E5H.
*345CLD DE,41E5H11E541Set the LDIR Destination to 41E5H
*345FLD BC,0040H014000Set the number of bytes to move to 40H
*3462LDIREDB0Move the 64 (40) bytes from 36F9H to 41E5H.
The Model 4 ROM Student Network Edition ROM changes that to ...
*346AJP NZ,33F9HC2F933If we have a BREAK key then jump to Non-Disk BASIC at 33F9H.
*346DIN A,(F0H)DBF0We 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
*346FINC A3CBump the FDC Status by 1 (which would turn a FFH (128) into a 00H), making a Z flag mean "Disk Controller Not Ready".
*3470JP Z,33F9HCAF933If the FDC Stats + 1 is zero, then we have no disk controller, so jump to Non-Disk BASIC at 33F9H.
*3473LD BC,0000H010000If we are here, then we have a floppy controller, so set up for a loop of 65,535 times.
*3476DEC BC0BDecrease BC by 1.
*3477LD A,81H3E81Set A to 81H (Decimal: 10000001).
*3479OUT (F4H),AD3F4Send 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.
*347BLD A,B78To 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.
*347COR CB1
*3480IN A,(F0H)DBF0We 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
*3482BIT 2,ACB57Test Bit 2 of A (the Floppy Status). If it is 0 then we haven't made it to track 0, otherwise we have
*3484JR Z,3476H28F0Still 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.
*3486LD E,0AH1E0ASet up for a loop of 10 using Register E; this will be the number of tries to find an index mark.
*3488LD BC,0000H010000Set up for a loop of 65,536 using Register Pair BC.
*348BIN A,(F0H)DBF0Poll 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
*348DBIT 1,ACB4FCheck A to see if bit 1 (meaning "Drive Busy") is set.
*3491CALL 349BHCD9B34If 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.
*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.
*3496DEC E1DIf 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.
*3497JR NZ,34ACH2013So long as we have not run out of the loop of 10 tries, JUMP to 34ACH to find the index mark.
*349B - Model 4 Gen 2 Routine - We have exhausted our tries, so display DISKETTE and keep trying.
*349BDEC BC0BIf bit 1 is not set (meaning, the drive is NOT busy), reduce the counter (BC) by 1.
*349CLD A,81H3E 81Load A with 81H (Decimal: 129, Binary: 1000 0001).
*349EOUT (F4H),AD3 F4Output 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.
*34A0LD A,B78To 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.
*34A1OR CB1
*34A2RET NZC0If BC is not 0, then RETurn
*34A3POP HLE1Clear the STACK
*34A4LD HL,0277H21 77 02If 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.
*34A7CALL 021BHCD 1B 02GOSUB to 021BH. Note; 021BH will display the character at (HL) until a 03H is found.
*34AAJR 3486H18 DAIf 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.
*34ACLD BC,0000H010000Set up for a loop of 65,536 using Register Pair BC.
*34AFIN A,(F0H)DBF0Poll 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
*34B1BIT 1,ACB4FCheck the Floppy Disk Controller Status (held in Register A) to see if bit 1 (meaning "Drive Busy") is set.
*34B3JR Z,34BAH2805If bit 1 is not set (meaning, the drive is NOT busy), JUMP to 34BAH to restart the read attempts.
*34B5CALL 349BHCD9B34If we are here, then the drive was busy, so GOSUB to 349BH to display DISKETTE? and try again
*34B8JR 34AFH18F5JUMP back to the top of this routine and keep looking
*34BA - Model 4 Gen 2 Routine - Finish initializing the floppy disk boot.
*34BADEC E1DIf we are here, it found a diskette so next we need to find the index mark. First decrement E by 1.
We are going to try to read a sector
*34BDLD A,81H3E 81Load A with 81H (Decimal: 129, Binary: 1000 0001).
*34BFOUT (F4H),AD3 F4Output 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.
*34C1LD HL,34F5H21 F5 34We 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.
*34C4LD (404AH),HL22 4A 40Put the desired JUMP address of 34F5H into (404AH)
*34C7LD A,C3H3E C3Load A with C3H (Decimal: 195, Binary 1100 0011).
*34C9LD (4049H),A32 49 40Put the C3H into 4049H.
NOTE: 4049H is the Non-Maskable Interrupt Vector.
*34CCLD A,80H3E 80Load A with 80H (Decimal: 128, Binary 1000 0000).
*34CEOUT (E4H),AD3 E4Output "1000 0000" to the the non-maskable interrupt latch via Port E4H.
*34D0LD BC,00F3H01 F3 00Set BC to 0F3H.
NOTE: Port F3H is the Floppy Disk Controller Data Register.
*34D3LD HL,4300H21 00 43Set HL to 4300H, which is where the data is going to go.
*34D6LD A,01H3E 01Prepare to read Sector 1 by loading A with a 1.
*34D8OUT (F2H),AD3 F2Output 1 to the Floppy Disk Controller Track Register at Port F2H.
*34DALD A,80H3E 80Prepare to read a single sector by loading A with 80H (Decimal: 128, Binary: 1000 0000).
*34DCOUT (F0H),AD3 F0Output "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).
*34DECALL 30CAHCD CA 30GOSUB to 30CAH to delay 3 instruction cycles, arguably the time it will take for the FDC to respond to the port command.
*34E1IN A,(F0H)DB F0Poll 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
*34E3AND 02HE6 02Mask 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.
*34E8INIEDA2Input the data byte.
*34EALD A,81H3E81Load A with 81H (Decimal: 129, Binary: 1000 0001) to select disk 0.
*34ECOR 40HF640OR A with 40H (Binary: 0100 0000) to set A to double-density.
*34EEOUT (F4H),AD3F4Output 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.
*34F0INIEDA2Input the data byte.
*34F2JP 34EAHC3 EA 34Loop 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.
*34F5XOR AAFClear A and all flags.
*34F6OUT (E4H),AD3 E4Send a 0 to the non-maskable interrupt latch via Port E4H. This is to clear the non-maskable interrupt status.
*34F8LD HL,45EDH21 ED 45Now that the NMI jumped here, we need to set a new NMI jump, this time to 45EDH.
*34FBLD (4049H),HL22 49 40Load the destination location into (4049H) which is the Non-Maskable Interrupt Vector.
*34FECALL 30CAHCD CA 30GOSUB to 30CAH to delay 3 instruction cycles, arguably the time it will take for the FDC to respond to the port command.
*3501IN A,(F0H)DB F0Poll 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
*3503POP HLE1Clean up the Stack.
*3504AND 1CHE61CMask 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.
*3509JR 34BDH18B2If 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.
*350BJP NZ,4049HC24940If 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.
*350EIN A,(E4H)DBE4Poll the Non-Maskable Interrupt Latch at Port E4H into A.
*3510BIT 5,ACB6FTest Bit 5 of Port E4 against A.
*3512JR Z,350EH28FALoop back 2 instructions until it is set (i.e., not a zero).
*3517 - Model 4 Gen 2 Routine - Finish up initialization by filling 256 bytes into 4300H and then JUMPing there
*3517OR AB7Set the FLAGS based on Register A
*3518JP NZ,3179HC27931If A is not ZERO then Jump to 3179H to continuing initialization routine by setting up the RS-232
*351BLD HL,4300H210043Set HL to 4300H, which is where the data is going to go.
*351ECALL 3086HCD8630GOSUB to 3086 to Poll the UART and wait for the P FLAG to not be set and then CONTINUE at 306CH
*3521LD (HL),A77Store Register A into the memory location pointed to by Register Pair HL
*3522INC L2CBump Register L by 1
*3523JR NZ,351EH20F9So long as we have not overflowed L, LOOP back to 351EH
*3525JP 37E0HC3E037If 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).
*3528NOP00No Operation
**3517 - Model 4 ROM Student Network Edition - Finish up initialization
**3517CALL 3086HCD8630GOSUB to 3086 to simply poll the keyboard and mask for a "4" key
**351AJR NZ,3520H2004If the key isn't a "4" then restore BC and return to processing as a regular Model 4 would
**351CCALL 028DHCD8D02GOSUB to 028DH to check for a BREAK key.
**351FRETC9RETurn to CALLer
**3520POP BCC1Restore the contents at the top of the STACK into Register Pair BC
**3521JP 3179HC37931Jump to 3179H to continuing initialization routine by setting up the RS-232
**3524NOP00
**3525NOP00
**3526NOP00
**3527NOP00
**3528NOP00
3529H - Deal with the cursor.
352CPUSH DEPush that return address onto the Stack.
352DIN 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.
352FLD 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.
3532OR ASet the flags.
3535LD 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.
3538OR ATest A.
353BLoad HL with 401AH.
NOTE: 401AH is the memory location that stores the cursor blink count.
353EDEC (HL)Reduce the memory contents of (401AH) by one.
NOTE: 401AH is the memory location that stores the cursor blink count.
3541LD (HL),07HSet the cursor blink count to 7.
NOTE: 401AH is the memory location that stores the cursor blink count.
3543INC HLBump HL. This will increase HL from 401AH to 401BH.
NOTE: 401BH holds the cursor blink status - 0 = Off, Anything Else = On.
3544LD A,(HL)Poll the cursor blink status memory location and put the results into A.
3545AND 01HMask A against 0000 0001, to have only Bit 0 active.
3547XOR 01HXOR A with 01H.
3549LD (HL),APut the toggled cursor blink status into the appropriate memory location.
354ALD HL,(4020H)Poll (4020H) and put the result into HL.
NOTE: 4020H holds the current cursor position.
354DIf the current cursor position is 0, then it is off, so JUMP down 2 instructions to 3554H to make the cursor a blank (space).
354FLD A,(4023H)Load A with the memory contents of (4023H).
NOTE: 4023H holds the cursor character.
3552JUMP down to 3556H to skip the next instruction and continue this routine by displaying the character in A.
3554LD A,20HLoad 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.
3556LD (HL),APut the character held in A into the memory location pointed to by (HL).
NOTE: (HL) will hold the current cursor position.
355ADEC (HL)Decrease the number held at (4216H) by 1.
355BRET NZIf the number held at (4216H) is not zero, then RETURN.
355CLD (HL),1EHPut a 1EH (Decimal: 30) into (4216H).
NOTE: 4216H is the heartbeat counter.
355EINC HLBump HL by 1. HL will now point to 4217H, which is the memory location that holds the SECONDS.
355FLoad 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.
3562LD B,03HLoad B with a 03H, to set up a loop where we test seconds, minutes, and hours against their maximums.
3564INC (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.
3565LD 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.
3566SUB (HL)Compare the maximum to what we have by subtracting that maximum from the value pointed at in (HL).
3567RET NZIf there is no difference between what we have and the maximum then RETURN.
3568LD (HL),AIf 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).
3569INC HLBump 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.
356AINC DEBump 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.
356BLoop back to 3564H until the loop of 3 has been met, meaning that we have processed seconds, minutes, and hours.
356DINC HLBump HL one more time, to 421BH.
NOTE: 421BH holds the current DAY portion of the date.
356EINC (HL)Bump the DAY portion of the date.
356FINC HLBump HL one more time, to 421CH.
NOTE: 421BH holds the current MONTH portion of the date.
3570LD A,(HL)Get the month and put it into A.
3571DEC HLDecrease HL back to to 421BH.
NOTE: 421BH holds the DAY portion of the date.
3572DEC ADecrease A by one (to the previous month).
NOTE: DE currently points to the memory locations housing the of days in each month.
3573ADD A,EAdd E to A.
3574LD E,ALoad E with the result. E = E + A.
3575LD A,(DE)Poll the number of days in a month and put the result into A.
3576CP (HL)Compare A against (HL).
NOTE: If this is not a loop, the HL holds the day portion of the date.
3577RET NCIf 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.
3578LD 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.
3579CP 1EHCompare 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.
357BIf the carry flag isn't set then JUMP to 3583H to update the MONTH, but not the YEAR.
357DDEC HLDecrement HL to now point to 421AH.
NOTE: 421AH points to the current YEAR.
357ELD A,(HL)Load the YEAR into A.
357FINC HLBump HL to 421BH.
NOTE: 421BH points to the current DAY.
3580AND 03HMask A (which is holding the year) with 03H (Binary: 00000011) to test for a leap year.
3582RET ZRETURN if that mask showed that we are in the 4th year of a cycle (because 04 and higher are turned off).
3583LD (HL),01HPut a 1 into the memory location pointed to by HL (which is DAY).
3585INC HLBump HL to 421CH.
NOTE: 421BH points to the current MONTH.
3586INC (HL)Increase the current MONTH by 1.
3587LD A,(HL)Put the current MONTH into A.
3588SUB 0DHSubract 13 from A.
NOTE: This will test against a month 13. If A is < 13, then the CARRY FLAG will be set.
358ARET CIf it is NOT month 13 then RETURN to skip the next code which increases the YEAR.
358BLD (HL),01HIf we are here, then MONTH = 13, so set MONTH to 1.
358DDEC HLDecrement HL to 421BH.
NOTE: 421BH points to the current DAY.
358EDEC HLDecrement HL to 421AH.
NOTE: 421BH points to the current YEAR.
358FINC (HL)Bump the current year.
3590RETRETURN.
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.
3591LD 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.
3594BIT 0,ATest Bit 0 of A to see if the clock is on or off.
3596RET ZIf Bit 0 of A is ZERO, then return.
3597LD 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.
359ACP 1EHCompare the heartbeat counter against 1EH.
NOTE: A CP actually subtracts 1E from A without modifying A, but the flags are set accordingly.
359CRET NZIf the clock was not just updated, then RETURN.
359DIf 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.
35A3LD C,3AHLoad C with a :.
NOTE: This routine is also used to convert the date, and C will be swapped out to a /for that routine.
35A5LD B,03HLoad B with a 3.
NOTE: This is because we need to convert 3 numbers, so we will loop 3 times.
35A7LD 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.
35A8DEC DEDecrement DE to point to the next unit to be dealt with.
35A9LD (HL),2FHLoad the memory location pointed to by (HL) with 2F.
NOTE: 2F is a /which is also 1 character below a 0.
35ABINC (HL)Increase whatever is held in (HL). On the first iteration, this change the character at the screen location to a 0.
35ACSUB 0AHA = 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.
35B0ADD A,3AHIf 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.
35B2INC HLBump HL to point to the next location on the video screen. On the first iteration, this will be the 2nd digit of the HOUR.
35B3LD (HL),APut 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.
35B4INC HLBump HL to point to the next location on the video screen. On the first iteration, this will be the 3rd character.
35B5DEC BDecrement B to the next unit. On the first iteration, this will move from 3 to 2.
35B6RET ZIf we have processed all passes in the loop, RETURN.
35B7LD (HL),CIf we are here, then the routine has not yet looped 3 times, so put a :onto the screen.
35B8INC HLBump HL to point to the next location on the video screen.
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 /.
35BELD C,2FHLoad C with a /.
35C2H - Maskable Interrupt Handler.
35C2PUSH AFSave AF to the STACK.
35C3IN A,(E0H)Poll Port E0H which is the MASKABLE INTERRUPT LATCH and put the results into A.
35C5RRARotate 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.
35C9RRARotate 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.
35CAIf Bit 1 (which is now in the carry) is low then JUMP to 3369H (which is a cassette routine with E set to LOW).
35CDPUSH BCSave all the registers.
35CEPUSH DE
35CFPUSH HL
35D0PUSH IX
35D2PUSH IY
35D7PUSH HLPush HL to the STACK.
35D8RRARotate 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.
35D9If 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.
35DCRRARotate 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.
35DDIf 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.
35E0RRARotate 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.
35E1If 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.
35E4RRARotate 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.
35E5If 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.
35E8RRARotate 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.
35E9If 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.
35ECRRARotate 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.
35EDIf 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.
35F0POP HLRestore all registers.
35F1POP IY
35F3POP IX
35F5POP HL
35F6POP DE
35F7POP BC
35F8POP AF
35F9EIEnable Interrupts.
35FARETRETURN.
35FBH - RS-232 Initialization Routine. I'm [guessing] that IX is set to 41F5H
35FBDIDisable Interrupts so they don't interrupt this routine.
35FCIN 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)
35FECP FFHCompare A with FFH to see if the RS-232 exists.
3602XOR AFlip the RS-232 Port Results, just to get a non-zero result.
3603OUT (0E8H),AOutput A to port E8H.
NOTE: Port E8H is the RS-232 Status Register & Master Reset. Outputting ANYTHING to Port E8H resets the RS-232.
3605LD A,(IX+03H)Load the BAUD RATE CODE into A.
NOTE: 41F8H holds the baud rate code.
3608OUT (0E9H),AOutput 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
360ALD A,(IX+04H)Load the CONFIURATION CODE into A.
NOTE: 41F9 holds the RS-232 Configuration Code.
360DOR ASet the flags.
3610OUT (0EAH),AOutput 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)
3619LD A,(IX+05H)Load the WAIT SWITCH into A.
NOTE: 41FAH holds the RS-232 Wait Switch.
361COR ASet the flags.
361FSET 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
3623SET 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
362BOR ASet flags.
362ESET 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
3632SET 02H,(IY+04H)Set Bit 2 of 41F1H to set the ACTIVE FLAG.
3636IN 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
3638EIRe-Enable Interrupts.
3639RETRETURN.
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.
363AXOR AClear A and all flags.
363BLD B,04HLoad 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
363DLD C,0E8HLoad C with E8H.
363FOUT (C),ASend a 0 to the current port. On the first iteration, this is E8H (the RS-232 Status Register & Master Reset).
3641INC CBUMP C to the next port.
3647LD B,03HLoad B with 3 as a counter. 3 is for 3 bytes - 41E8H, 41E9H, and 41EAH.
3649LD (HL),00HLoad (HL) with 00H.
364BINC HLBump HL.
364ELoad HL with 41F0H.
NOTE: 41F0H is the 1 characer output buffer for the RS-232 Output DCB.
3651LD B,03HLoad B with 3 as a counter. 3 is for 3 bytes - 41F0H, 41F1H, and 41F2H.
3653LD (HL),00HLoad (HL) with 00H.
3655INC HLBump HL.
365AH - RS-232 Input Routine.
365ALoad IX with 41E5H.
NOTE: 41E5H is the DCB for RS-232 Input. 41E8H is the 1 Character RS-232 Input.
365EXOR AClear A and all Flags.
365FLD (IX+03H),ALoad (41E8H) with a Zero.
NOTE: 41E8H is the 1 Character RS-232 Input.
3662BIT 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.
3666RET ZIf the Driver is OFF, RETURN.
3667IN 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)
3669BIT 7,ATest Bit 7 of A.
NOTE: Bit 7 will be 1 if DATA READY (1=True).
366DBIT 1,(IX+04H)Test Bit 1 of 41E9H.
NOTE: Bit 1 of 41E9 contains the WAIT/NO WAIT of the RS-232 Input DCB.
3671RET ZIf its NO WAIT then RETURN, otherwise continue (to keep polling).
3677JUMP to 4203H if the BREAKkey was pressed.
NOTE: 4203H JUMPS to 022EH and is the break vector for tape and RS-232.
367AIN 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.
367CLD (IX+03H),ALoad (41E8H) with a A (the data from Port EBH).
NOTE: 41E8H is the 1 Character RS-232 Input.
367FRETRETURN.
3680H - RS-232 Output Routine.
3680Note. 41EDH is the RS-232 Output DCB, and 41F1H holds DRIVER ON/OFF in BIT 2, and WAIT/NO WAIT in BIT 1.
3684BIT 2,(IX+04H)Test Bit 2 of 41F1H to see if the RS-232 is active.
3688RET ZIf the RS-232 is NOT active, RETURN.
3689IN 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)
368BBIT 6,ATest Bit 6 of Port EAH to see READY TO SEND.
368FBIT 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.
3693RET ZIf RS-232 is NOT active, RETURN.
3699JUMP to 4203H if the BREAKkey was pressed.
NOTE: 4203H JUMPS to 022EH and is the break vector for tape and RS-232.
369CLD A,(IX+03H)Load A with the memory contents of 41F0H.
NOTE: 41F0H is RS-232 output buffer byte.
369FOR ATest A and Set Flags.
36A0If not zero, then there is a character in the buffer, so skip the next instruction and leave that byte in A.
36A2LD A,CLoad A with C [GET CHAR FROM DISPATCHER].
36A3OUT (0EBH),ASend 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.
36A5LD (IX+03H),00HLoad memory contents of the RS-232 output byte (at 41F0H) with a 0.
36A9RETRETURN.
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
36D5FFH
36D600HThis 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.
36D7
36DARST 00H
36DBNOP
36DCNOP
36DDXOR A
36DERET
36DFNOP
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
36F4NOP
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-370006 1BTwo 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-37061B 02 21 30 00 00 00
3707-370808 00Two 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-370E02 1B 30 55 6C FF
370F-371000 00Two 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-371F00 00 FF FF 00 00 C3 2E 02 C3 FA 35 C3 FA 35
3720-373041 32 03 32 28 03 3C 04 00 00 1E 00 00 00 00 00 00
3731-373300 00 00Three 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-373800 00 00 00 FF
*3739H - Model 4 Gen 1
*373FLD D,00HLoad D with 0 (so D = ROW 0).
*3742LD E,ALoad E with the contents held in (BC) to check the keyboard row.
*3743XOR (HL)XOR (HL) to set changed bits.
*3744LD (HL),ESave the scan back into (HL).
*3745AND EMask A with E (to mask the released keys).
*3748 - Model 4 Gen 1 routine to Go to the next Keyboard row
*3748INC DBump D so that D holds the NEXT row number.
*3749INC HLBump HL so that HL holds the NEXT buffer location.
*374ARLC CWe 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.
*374CIf 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
*374FLD 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.
*3752LD L,ALoad L with the A.
*3753LD 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.
*3756AND (HL)MASK A against (HL) to see if the previous keys are still pressed.
*375ASBC HL,HLZero HL by subtracting HL from HL.
*375CLD (4201H),HLLoad 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.
*3762LD (41FFH),HLLoad the memory location at 41FFH with 05DCH.
NOTE: 41FFH holds the keyboard scan repeat delay count.
*3765XOR AClear A and all flags.
*3766RETRETURN.
*3767 - Model 4 Gen 1 routine to Keyboard Repeat - Jumps Here if the same keys are still pressed.
*3768PUSH BCSave BC to the STACK.
*376CGOSUB 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.
*376FPOP BCRestore BC from the STACK.
*3770LD A,(BC)Load A with the memory contents pointed to by BC to re-check the keyboard.
*3771AND ECompare A against E to check the pattern.
*3772RET ZIf not the same pattern then RETURN.
*3773LD (41FEH),AIf it is the same pattern then save A into (41FEH).
NOTE: 41FE is the SAVED IMAGE AT POSITION.
*3776LD A,LLoad A with L (the scan position).
*3777LD (41FDH),ASave A into (41FDH).
NOTE: 41FDH is the SAVED POSITION IN SCAN.
*377ALD A,DLoad A with D (8 * ROW #).
*377BRLARotate 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.
*377CRLARotate 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.
*377DRLARotate 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.
*377ELD D,AD = A
*377FLD A,EA = E
RRCARotate A right one bit, with the contents of BIT 0 being put into BOTH the CARRY FLAG and BIT 7. D = 8* ROW # + KEY #.
*3781RET CIf the contents of BIT 0 of A was SET, RETurn
*3782INC DINC DBump D so that D holds the NEXT row number.
*3785 - Model 4 Gen 1 routine to Keyboard Repeat - Jumps Here if the same keys are still pressed.
*3786LD HL,(4201H)Load HL with the repeat delay counter.
NOTE: 4201H is the REPEAT DELAY COUNTER.
*3789INC HLBump HL.
*378ALD (4201H),HLLoad the memory location at 4201H with HL to clear the repeat counter.
NOTE: 4201H is the REPEAT DELAY COUNTER.
*378DLD DE,(41FFH)Load DE with the byte stored at 41FFH.
NOTE: 41FFH is the REPEAT DELAY COUNT.
*3791SBC HL,DESubtract with CARRY DE from HL.
*3793POP DERestore old HL (which is what is in the stack) into DE.
*3797XOR AClear A and all flags.
*3798LD (DE),APut a 0 into the memory location pointed to (DE) to let the key be re-read.
*3799LD (4201H),HLLoad the memory location at 4201H with HL to clear the repeat counter.
NOTE: 4201H is the REPEAT DELAY COUNTER.
*379CLD L,96HLoad L with 96H to set a fast repeat count.
*379ELD (41FFH),HLSave HL into the memory location at 41FFH.
NOTE: 41FFH is the REPEAT DELAY COUNT.
*37A4 - Model 4 Gen 1 routine jumped to from the the middle of the tokenize routine.
CP 22HIf 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 ".
*37A6If the character in register A is not a ", then JUMP forward a few instructions to 37B2H.
*37A8LD A,(409FH)Load A with the memory contents of 409FH.
NOTE: 409FH is the DATA FLAG.
*37ABXOR 01HXOR 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.
*37ADLD (409FH),ALoad memory location 409FH with a the XOR'd results.
NOTE: 409FH is the DATA FLAG.
*37B0LD A,22HLoad A with 22H, which is a ".
CP 3AHCompare 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.
*37B4If A is 3AH then JUMP to 06AAH.
NOTE: 06AAH is in the middle of the KEYBOARD DRIVER ENTRY ROUTINE.
*37B7LD A,(409FH)Otherwise, Load A with the memory contents of 409FH.
NOTE: 409FH is the DATA FLAG.
*37BBRRARotate 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.
*3792If Bit 7 of the DATA FLAG was set, then JUMP to 06A8H.
NOTE: 06A8H is in the middle of the KEYBOARD DRIVER ENTRY ROUTINE.
*37BERLARotate 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.
*37BFJUMP 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$)
RST 10HCall 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.
*37C3PUSH HLSave HL (the current position) to the STACK.
*37C4LD A,11HLoad A with 11H (Decimal: 17).
NOTE: This is to set up for a 17 Byte String.
*37C9LD HL,(40D4H)Load HL with the memory contents of (40D4H).
NOTE: 40D4 is the string pointer.
*37CFLD (HL),20HLoad the memory location pointed to by HL with a SPACE.
*37D1INC HLIncrement HL to move 1 character over.
*37D8 - Model 4 Gen 1 routine to Toggle Caps Lock.
*37DDXOR (HL)Invert the contents of the (4019H).
*37DELD (HL),AStore the inverted (i.e., toggled) contents of (4019H) back into (4019H)
*37E0RETRETurn to Caller.
*37E1 - Model 4 Gen 1 routine to do a very short delay routine
*37E5 - Model 4 Gen 1 routine Unused Code
*37E5NOP
*37E6NOP
*37E7NOP
*37E8RST 38H
*37E9RST 38H
*37EANOP
*37EBH - Model 4 Gen 1 routine to display the Copyright Message
*37F4 - Model 4 Gen 1 routine to enable the TIME$ Command
*37F7LD (4177H),HLLoad 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.
*37F7JUMP to 022EH to continue.
NOTE: 022EH will enable interrupts, show the READY prompt, and RETURN.
*37FD - Model 4 Gen 1 unused Code
*37FDPUSH HL
*37FEPUSH HL
*37FFRST 38H
*3739H - Model 4 Gen 2 - Part of the Keyboard Routine to check for SHIFT and CAPS LOCK and deal with them accordingly.
*3739LD A,(3880H)3A 80 38Load A with the value held at 3880H (which are the SHIFT keys)
*373CAND 03HE6 03Mask the SHIFT Register against 03H (Binary: 0000 0011) to keep only Bits 0 and 1 to check for shifts.
*373EJR Z,3742H28 02If the masked A is 0, then we have no shifts, and skip the next instruction (to 3742H).
*3740SET 6,DCB F2Set BIT 6 of D to offset D for shifts.
*3742LD A,(4019H)3A 19 40Load A with the contents of memory location 4019H to check for CAPS LOCK.
NOTE: 4019H is the CAPS LOCK TOGGLE.
*3745OR AB7Set the flags.
*3746JR Z,3753H28 0BIf the ZERO flag is set then there is NO CAPS LOCK so JUMP to 3753H to skip the next intructions.
*3748SET 7,DCB FASet 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.
*374ALD A,(3880H)3A 80 38Put the contents of memory location 3880H into A to GET SHIFT(S).
*374DAND 03HE6 03Mask the SHIFT Register against 03H (Binary: 0000 0011) to keep only Bits 0 and 1 to check for shifts.
*374FJR Z,3753H28 02If the masked A is 0, then we have no shifts, and skip the next instruction (to 3753H).
*3751RES 7,DCB BARESET 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.
*3753LD HL,304EH21 4E 30Load HL with 304EH (the KEYBOARD TABLES).
*3756LD E,D5AWe need DE to be the OFFSET, so load E with D and ...
*3757LD D,00H16 00... load D with 00.
*3759ADD HL,DE19Add DE (the offset over the keyboard table) to HL (the keyboard table).
*375ALD A,(HL)7EGet the character pointed to by (HL) and put it into A.
*375BRETC9RETurn 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
*375CCP 22HFE 22If 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 ".
*375EJR NZ,376AH20 0AIf the character in register A is not a ", then JUMP forward a few instructions to 376AH.
*3760LD A,(409FH)3A 9F 40Load A with the memory contents of 409FH.
NOTE: 409FH is the DATA FLAG.
*3763XOR 01HEE 01XOR 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.
*3765LD (409FH),A32 9F 40Load memory location 409FH with a the XOR'd results.
NOTE: 409FH is the DATA FLAG.
*3768LD A,22H3E 22Load A with 22H, which is a ".
*376ACP 3AHFE 3ACompare 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.
*376CJP NZ,06AAHC2 AA 06If A is NOT a : then JUMP to 06AAH.
NOTE: 06AAH is in the middle of the KEYBOARD DRIVER ENTRY ROUTINE.
*376FLD A,(409FH)3A 9F 40Otherwise, Load A with the memory contents of 409FH.
NOTE: 409FH is the DATA FLAG.
*3772RRA1FRotate 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.
*3773JP C,06A8HDA A8 06If Bit 7 of the DATA FLAG was set, then JUMP to 06A8H.
NOTE: 06A8H is in the middle of the KEYBOARD DRIVER ENTRY ROUTINE.
*3776RLA17Rotate 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.
*3777JP 06A3HC3 A3 06JUMP 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
*377ALD (IX+05H),05HDD 36 05 00Load the memory location pointed to by IX+5 with a 05H.
NOTE: IX+5 is the number of characters printed.
*377EINC (IX+04H)DD 34 04Bump the number of lines already printed (which is tracked in IX+04H)
*3781LD A,(IX+04H)DD 7E 04Fetch the number of lines already printed (which is tracked in IX+04H) into Register A
*3784CP (IX+03H)DD BE 03Compare the number of lines already printed (held in Register A) against the number of lines left in the page (tracked in IX+03H)
*3787RET NZC0If the number of lines already printed doesn't match the number of lines left in the page then RETURN
*3788LD (IX+04H),04HDD 36 04 01If they DO match, then set the number of lines already printed to 04H
*378CRETC9RETurn 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
*378DLD A,C79LET Register A = Register C.
*378ECP E0HFE E0Compare 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
*3790JP NC,0043HD2 43 00If 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).
*3793CP C0HFE C0If 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
*3795JP C,0063HDA 63 00If 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).
*3798SUB C0HD6 C0If 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.
*379ALD B,00H06 00LET Register B = 00H
*379CLD C,A4FLET Register C = Register A.
*379DLD HL,(4220H)2A 20 42LET Register Pair HL the memory contents of 4220H and 4221H.
NOTE: 4220H-4221H is the destination device nName for ROUTE Routine. 2 Bytes
*37A0ADD HL,BC09LET HL = HL + BC
*37A1LD C,(HL)4ELET Register C = the contents of the memory location pointed to by Register Pair HL.
*37A2JP 0063HC3 63 00JUMP 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
*37A5LD HL,3C00H21 00 3CLoad HL with the memory location for the beginning of the video RAM.
*37A8LD A,(HL)7EPut the character at the screen location stored in HL into A.
*37A9RLCA07Rotate 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.
*37ACRLCA07Rotate 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.
*37ADJR C,37B3H38 04If 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
*37AFLD A,2EH3E 2ELoad Register A with a . which is the character which will print instead of non-printable characters.
*37B3LD A,(HL)7EReplace the non-printable character with a .
*37B4CALL 003BHCD 3B 00Call the PRINT CHARACTER routine at 003B (which sends the character in the A register to the printer).
*37B7INC HL23Bump HL to the next character on the screen.
*37B8BIT 6,HCB 74Check 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).
*37BDLD A,L7DTest of end of line by loading A with L.
*37BEAND 3FHE6 3FAND 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).
*37C0JR NZ,37A8H20 E6If 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
*37A5 - Model 4 Gen 2 - Screen Print Routine BUT exits witH Register A at 0 and all FLAGS cleared
*37C7CALL 37A5HCD A5 37GOSUB to 37A5H for the screen print
*37CAXOR AAFClear Register A and all the flags
*37CBRETC9RETurn to CALLer
*37CC - Model 4 Gen 2 - TOGGLE the CAPS LOCK
*37CCLD A,01H3E 01Load Register A with an 01H
*37CELD HL,4019H21 19 40Load HL with 4019H, which is the CAPS LOCK TOGGLE in the keyboard DCB.
*37D1XOR (HL)AEXOR the contents of the CAPS LOCK TOGGLE in RAM against 01H; this effectively toggles it
*37D2LD (HL),A77Put the toggled CAPS LOCK TOGGLE back into RAM where such thing is stored
*37D3XOR AAFClear Register A and Clear all FLAGs
*37D4RETC9RETurn 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.
*37D5PUSH AFF5Preserve Registers A and F to the top of the STACK
*37D6IN A,(EAH)DB EAPoll the RS-232 UART Control Register/Status Register (via Port EAH) into A.
*37D8BIT 6,ACB 77Test Bit 6 of the RS-232 Register. It will be 1 if READY TO SEND
*37DAJR Z,37D6H28 FAIf the UART reported that READY TO SEND is FALSE, LOOP back to 37D6 and poll again.
*37DCPOP AFF1Restore Registers A and F from the top of the STACK
*37DDOUT (EBH),AD3 EBSend the contents held in Register A to the RS-232C Data Register (via Port E8H) so as to send A to the RS-232C.
*37DFRETC9RETurn 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).
*37E0LD A,6DH3E 6DLoad Register A with a 6DH (Binary: 0110 1101)
*37E2OUT (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)
*37E4JP (HL)E9Jump to whatever (HL) is pointing to.
*37E5 - Model 4 Gen 2 - UNUSED CODE.
*37E5NOP00
*37E6NOP00
*37E7NOP00
*37E8RST 38HFF
*37E9RST 38HFF
**37D5-37E9 - Model 4 ROM Student Network Edition - Replace all the above with NOPs
*37EA - Model 4 Gen 2 - BASIC TIMES (DATE$+" "+TIME$)
*37EARST 10HD7Call the EXAMINE NEXT SYMBOL routine at RST 10H.
*37EBPUSH HLE5Save HL (the current position) to the STACK.
*37ECLD A,11H3E 11Load A with 11H (Decimal: 17) to set up for a 17 Byte String.
*37F1LD HL,(40D4H)2A D4 40Load HL with the memory contents of (40D4H), which is the string pointer.
*37F7LD (HL),20H36 20Load the memory location pointed to by HL with a SPACE.
*37F9INC HL23Increment HL to move 1 character over.
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.
NOTE:40AFH holds Current number type flag
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.
NOTE:40FDH-40FEH holds Free memory pointer
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.
NOTE:40A0H-40A1H holds the start of string space pointer
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.
27F5-27FD - LEVEL II BASIC POS(ROUTINE- "POS"
Note: 40A6H holds the current cursor line position
27FE-2818 - LEVEL II BASIC USR(x)ROUTINE- "USRFN"
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.
Note: 40AFH holds Current number type flag
(02=INT, 03=STR, 04=SNG, 08=DBL)
2819-2827 - CONVERSION ROUTINE- "DOCNVF"
Usually called by LETto convert the result of arithmetic routines to the proper destination type.
(02=INT, 03=STR, 04=SNG, 08=DBL)
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.
2831 - ID ERROR entry point.
2836-2856 - STRING ROUTINE - STR$logic- "STR$"
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.
2857-2864 - STRING ROUTINE- "STRINI"
Note: 40D3H-40D5H holds Used for temporary string VARPTR's
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.
Note: 40D3H-40D5H holds Used for temporary string VARPTR's
Note: 40B3H-40B4H holds the next available location in the temporary string work area pointer
Note: 40AFH holds current number type flag
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.
Note: 40B3H-40B4H holds the next available location in the temporary string work area pointer
28A1 - ST ERROR entry point.
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.
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.
"STROUT"
If the routine entry is at STRPRT, then it just prints the string whose descriptor is held in FACLO
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.
Note: 40D6H-40D7H holds the next available location in string space pointer
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.
28DA-298E - STRING ROUTINE- "GARBAG"
Note: 40B1H-40B2H holds MEMORY SIZE? pointer
Note: 40D6H-40D7H holds the next available location in string space pointer
NOTE:40A0H-40A1H holds the start of string space pointer
Note: 40B5H-40D2H holds Temporary string work area
"TVAR"
NOTE:40B3H-40B4H holds the next available location in the temporary string work area pointer
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.
NOTE:40F9H-40FAH holds the starting address of the simple variable storage area
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.
Note: 40FDH-40FEH holds Free memory pointer
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.
Note: 40D8H-40D9H holds Temporary storage location
Note: 40D8H-40D9H holds Temporary storage location
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.
2954
LD C,L44
NOTE:40D6H-40D7H holds the next available location in string space pointer
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.
295A
LD L,C60
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.
2962
LD L,C60
If we are here, we have made one complete pass through the string variables.
NOTE:40D6H-40D7H holds the next available location in string space pointer
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.
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
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.
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.
Note: 40D6H-40D7H holds the next available location in string space pointer
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.
Note: 40D6H-40D7H holds the next available location in string space pointer
29F5-2A02 - STRING ROUTINE - - "FRETMS"
Test to see if the string in DE is the last string used in the temporary string work area.
Note: 40B3H-40B4H holds the next available location in the temporary string work area pointer
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.
Note: 40B3H-40B4H holds the next available location in the temporary string work area pointer
2A03-2A0E - LEVEL II BASIC LENROUTINE- "LEN"
2A0F-2A1E - LEVEL II BASIC ASCROUTINE- "ASC"
2A2F-2A60 - LEVEL II BASIC STRING$ROUTINE- "STRNG$"
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.
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).
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).
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.
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$Go check the syntax. The character at the location of the current BASIC program pointer in HL must be a )
2A64XOR AAFZero Register A because the string pointer never changes
2A65- ↳ LEFT3EX (SP),HLE3Exchange the value of the current BASIC program pointer in HL with the string's VARPTR to the STACK
2A66LD C,A4FZero Register C
2A67-2A68LD A,0E5H3E E5Z-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- ↳ LEFT2PUSH HLE5Save the value of the string's VARPTR in HL to the STACK
2A6ALD A,(HL)7ELoad Register A with the string's length at the location of the string's VARPTR in HL
2A6BCP BB8Check 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-2A6DJump to 2A70H if the new string's length in Register B is greater than the string's length in Register A
2A6ELD A,B78Load Register A with the new string's TRUNCATED length in Register B
2A6F-2A71LD DE,000EH11 0E 00Z-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
2A72PUSH BCC5Save the offset in BC to the STACK
2A76POP BCC1Get the offset from the STACK and put it in BC
2A77POP HLE1Get the string's VARPTR from the STACK and put it in HL
2A78PUSH HLE5Save the string's VARPTR in HL to the STACK
2A79INC HL23Bump HL to now point to the address of the string
2A7ALD B,(HL)46Load Register B with the LSB of the string's address at the location of the string's VARPTR in HL
2A7BINC HL23Bump the value of the string's VARPTR in HL
2A7CLD H,(HL)66Load Register H with the MSB of the string's address at the location of the string's VARPTR in HL
2A7DLD L,B68Load Register L with the LSB of the string's address in Register B
2A7E-2A7FLD B,00H06 00Zero Register B so that BC can be used
2A80ADD HL,BC09Add the string's length in BC to the string's address in HL
2A81LD B,H44Load Register B with the MSB of the string's ending address in Register H
2A82LD C,L4DLoad Register C with the LSB of the string's ending address in Register L
2A83-2A85Go save the string's length (held in A) and the string's starting address (held in DE)
2A86LD L,A6FLoad Register L with the string's length (i.e., the number of characters to move) held in Register A
2A8APOP DED1Clean up the STACK
2A91-2A99 - LEVEL II BASIC RIGHT$ ROUTINE- "RIGHT$"
2A91-2A93Go check the syntax. The character at the location of the current BASIC program pointer in HL must be a )
2A94POP DED1Get the string's VARPTR from the STACK and put it in DE
2A95PUSH DED5Save the string's VARPTR in DE to the STACK
2A96LD A,(DE)1ALoad Register A with the string's length (held at the location of the string's VARPTR in DE)
2A97SUB B90Subtract the new string's length in Register B from the string's length in Register A to isolate the number of bytes
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,HLEBLoad HL with the value of the current BASIC program pointer (held in DE)
2A9BLD A,(HL)7ELoad Register A with the terminal character, currently held at the location of the current BASIC program pointer in HL
2A9C-2A9EGOSUB to 2AE2H to get the offset in Register B and the string's VARPTR in DE
2A9FINC B04We 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
2AA0DEC B05... and then we decrement the value of the string's offset position in Register B
2AA4PUSH BCC5Save the value of the offset position (held in Register B) to the STACK
2AA5-2AA6LD E,0FFH1E FFLoad Register E with the default string's length of 256 in case no number of bytes are given
2AA7-2AA8CP 29HCP ")"FE 29More 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-2AAAJump to 2AB0H if the character at the location of the current BASIC program pointer in Register A is a )
2AAB-2AACRST 08H ⇒ 2CSYNCHK ","CF 2CIf 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-2AAFGo 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-2AB1RST 08H ⇒ 29SYNCHK ")"CD 29Since 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).
2AB2POP AFF1Get the offset from the STACK and put it in Register A
2AB3EX (SP),HLE3Exchange the value of the current BASIC program pointer in HL with the value of the string's VARPTR to the STACK
2AB4-2AB6Load BC with 2A69H as the return address (which is in the LEFT$routine)
2AB7PUSH BCC5Save the return address in BC to the STACK
2AB8DEC A3DDecrement the value of the requested offset in Register A so that we have a starting position minus 1
2AB9CP (HL)BECompare 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-2ABBLD B,00H06 00Zero Register B
2ABCRET NCD0If the offset pointer ispast the end of the string we are going to return a null
2ABDLD C,A4FLoad Register C with the offset in Register A
2ABELD A,(HL)7ELoad Register A with the string's length at the location of the string's VARPTR in HL
2ABFSUB C91Subtract the index (the second argument) in Register C from the string's length in Register A
2AC0CP EBBCompare 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.
2AC1LD B,A47Load Register B with the calculated string's length in Register A
2AC2RET CD8If we are not going to truncate, then just use the partial string we already have and Return to 2A69H
2AC3LD B,E43Load Register B with the new string's truncated length in Register E
2AC4RETC9Return to 2A69H aka LEFT2
2AC5-2ADE - LEVEL II BASIC VALROUTINE- "VAL"
2AC5-2AC7- ↳ VALGo get the string's length in Register A and the string's VARPTR in HL
2AC8-2ACAJump to 27F8H if the string's length in Register A is equal to zero
2ACBLD E,A5FLoad 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.
2ACCINC HL23Bump the value of the string's VARPTR in HL
2ACDLD A,(HL)7ELoad Register A with the LSB of the string's address at the location of the string's VARPTR in HL
2ACEINC HL23Bump the value of the string's VARPTR in HL
2ACFLD H,(HL)66Load Register H with the MSB of the string's address at the location of the string's VARPTR in HL
2AD0LD L,A6FLoad Register L with the LSB of the string's address in Register A
2AD1PUSH HLE5Save the value of the string's address in HL to the STACK
2AD2ADD HL,DE19Add the string's length in DE to the string's address in HL
2AD3LD B,(HL)46Load Register B with the last character of the string at the location of the string pointer in HL
2AD4LD (HL),D72Save the zero in Register D at the location of the string pointer in HL
2AD5EX (SP),HLE3Exchange the string's ending address in HL with the string's address to the STACK
2AD6PUSH BCC5Save the last character of the string in Register B to the STACK
2AD7LD A,(HL)7ELoad Register A with the first character of the argument
2AD8-2ADACall 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)
2ADBPOP BCC1Get the modified character of the next string into Register B
2ADCPOP HLE1Get the pointer to the modified character back into HL
2ADDLD (HL),B70Restore the character.
2ADERETC9RETurn 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- ↳ PREAMEX DE,HLEBLoad HL with the value of the current BASIC program pointer in DE
2AE0-2AE1RST 08H ⇒ 29SYNCHK ")"CF 29Since 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- ↳ PREAM2POP BCC1Get the return address from the STACK and put it in BC
2AE3POP DED1Get the number of bytes to isolate from the string (from the STACK) and put it in DE
2AE4PUSH BCC5Save the return address in BC to the STACK
2AE5LD B,E43Load Register B with the number of bytes in Register E
2AE6RETC9RETurn to CALLer
2AE7H-2AEE - Process a LEFT-HAND-SIDE MID$- "ISMID$"
2AE7-2AE8- ↳ ISMID$CP 7AHCP MIDTK-$ENDFE 7AThis 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.
2AEF-2AF7 - LEVEL II BASIC INPROUTINE- "FNINP"
2AEF-2AF1- ↳ FNINPGo evaluate the expression at the location of the current BASIC program pointer in HL and return with the port number in Register A
2AF2-2AF4LD (4094H),ALD (STAINP+1),A32 94 40Save the value of the port number (from Register A) into 4094H, which is in the middle of a routine.
2AF8-2B00 - LEVEL II BASIC OUTROUTINE- "FNOUT"
2B01-2B0D - EVALUATE EXPRESSION ROUTINE- "GETINT"
This evaluates an expression and leaves the result in DE as an integer.
2B01- ↳ GETINTRST 10HCHRGETD7We 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- ↳ GETIN2Go 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- ↳ INTFR2PUSH HLE5Save the value of the current BASIC program pointer in HL to the STACK
2B06-2B08Call 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)
2B09EX DE,HLEBLoad DE with the integer result in HL
2B0APOP HLE1Get the value of the current BASIC program pointer from the STACK and put it in HL
2B0BLD A,D7ALoad Register A with the MSB of the integer result in Register D
2B0COR AB7Test the value of the MSB in Register A
2B0DRETC9RETurn to CALLer
2B0E-2B16 - EVALUATE EXPRESSION ROUTINE - OUTcontinues here- "SETIO"
2B0E-2B10- ↳ SETIOGOSUB 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-2B13LD (4094H),ALD (STAINP+1),A32 94 40Save the 8-bit value in Register A in the DOS address of 4094H to set up for WAIT
2B14-2B16LD (4097H),ALD (OUTWRD+1),A32 97 40Save 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-2B18RST 08H ⇒ 2ESYNCHK ","CF 2ESince 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).
2B1B-2B28 - EVALUATE EXPRESSION ROUTINE - This is called by PRINT TAB- "GTBYTC".
2B1B- ↳ GTBYTCRST 10HCHRGETD7We 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- ↳ GETBYTGOSUB 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- ↳ CONINTGOSUB 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-2B24If the result is greater than 255, display a ?FC ERRORmessage
2B25DEC HL2BDecrement the value of the current BASIC program pointer in HL
2B26RST 10HCHRGETD7We 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.
2B27LD A,E7BLoad Register A with the 8-bit result in Register E so that both A and E have the result.
2B28RETC9RETurn 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- ↳ LLISTLD A,01H3E 01Load Register A with the printer output device code
2B2B-2B2DLD (409CH),ALD (PRTFLG),A32 9C 40Save 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- ↳ LISTPOP BCC1Get rid of the the return address on the STACK
2B2F-2B31Go evaluate the range of line numbers given at the location of the current BASIC program pointer in HL
2B32PUSH BCC5Save the address of the first BASIC line (held in BC) to the STACK
2B33-2B35- ↳ LIST4LD HL,FFFFH21 FF FFLoad 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-2B38LD (40A2H),HLLD (CURLIN),HL22 A2 40Save the value in HL as the current BASIC line number (which is stored at 40A2H-40A3H).
2B39POP HLE1Get the address of the first BASIC line to be listed (from the STACK) and put it in HL
2B3APOP DED1Get the value of the last BASIC line number to be listed (from the STACK) and put it in DE
2B3BLD C,(HL)4ELoad Register C with the LSB of the next BASIC line pointer at the location of the memory pointer in HL
2B3CINC HL23Bump the value of the memory pointer in HL
2B3DLD B,(HL)46Load Register B with the MSB of the next BASIC line pointer at the location of the memory pointer in HL
2B3EINC HL23Bump the value of the memory pointer in HL
2B3FLD A,B78Load Register A with the MSB of the next BASIC line pointer in Register B
2B40OR CB1Combine 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-2B43If we are at the elast line, then STOP and JUMP to 1A19H to the READY PROMPT.
2B47-2B49Go scan the keyboard to see if the BREAKkey or the shift-@key was pressed
2B4APUSH BCC5Save the address of the next BASIC line in BC to the STACK
2B4BLD C,(HL)4EWe 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
2B4CINC HL23Bump the value of the memory pointer in HL
2B4DLD B,(HL)46Load Register B with the MSB of the BASIC line number at the location of the memory pointer in HL
2B4EINC HL23Bump the value of the memory pointer in HL
2B4FPUSH BCC5Save the BASIC line number in BC to the STACK
2B50EX (SP),HLE3Swap (SP) and HL so that the line number is now in HL
2B51EX DE,HLEBSwap DE and HL so that the last BASIC line number is now in HL
2B52RST 18HCOMPARDFWe 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.
2B53POP BCC1Get the pointer to the location on the BASIC program line being processed and put it in BC
2B54-2B56If 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!
2B57EX (SP),HLE3Swap (SP) and HL so that the last BASIC line number is now on the STACK
2B58PUSH HLE5Save the address of the next BASIC line in HL to the STACK
2B59PUSH BCC5Save the pointer to the location on the BASIC program line being processed to the STACK
2B5AEX DE,HLEBLoad HL with the BASIC line number (from DE)
2B5B-2B5DLD (40ECH),HLLD (DOT),HL22 EC 40Save the BASIC line number in HL into DOT for use later in EDIT or LIST.
Note: 40ECH-40EDH holds EDIT line number
2B5E-2B60Call 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-2B62LD A,20H3E 20Load Register A with a space
2B63POP HLE1Get the value of the memory pointer from the STACK and put it in HL
2B67-2B69GOSUB 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-2B6CLD HL,(40A7H)LD HL,(BUFPNT)2A A7 40Load HL with the starting address of the input buffer.
Note: 40A7H-40A8H holds the input Buffer pointer
2B6D-2B6FSince 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
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)7ELoad Register A with the character at the location of the memory pointer in HL
2B76OR AB7Check to see if the character in Register A is an end of the string character (00H)
2B77RET ZC8Return if the character in Register A is an end of the string character
2B78-2B7AGo send the character in Register A to the current output device
2B7BINC HL23Bump the value of the memory pointer in HL
2B7C-2B7DLoop 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- ↳ BUFLINPUSH HLE5Save the BASIC line pointer in HL to the STACK
2B7F-2B81LD HL,(40A7H)LD HL,(BUFPNT)2A A7 40Load HL with the starting address of the input buffer.
Note: 40A7H-40A8H holds the input Buffer pointer
2B82
2B83LD B,H
LD C,L44LET Register Pair BC = Register Pair HL
2B84POP HLE1Get the value of the BASIC line pointer from the STACK and put it in HL
2B85JUMP 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.
2B88NOP
2B89- ↳ PLOOPINC BC03Top of a loop. Bump the value of the input buffer pointer in BC
2B8ADEC D15Decrement the character count in Register D
2B8BRET ZC8Return if 256 characters have been moved into the input buffer
2B8CINC HLMove one byte forward in the text.
2B8DLD A,(HL)Load register A with the character at the location of the BASIC line pointer in HL.
2B8EOR ASet the status flags to enable us to check to see if the character in register A is an end of the BASIC line character.
2B8FLD (BC),A02Save 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.
2B90RET ZC8Return if the character in Register A is an end of the BASIC line character
2B94CP FBHCheck to see if the character in register A is a ' token.
2B96-2B97Jump 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-2B9BDEC BC
DEC BC
DEC BC
DEC BC0BFirst, 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-2B9FINC D
INC D
INC D
INC D14Then, 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- ↳ NRQTTKCP 95HCP $ELSEFE 95Check 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-2BA4If 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-2BA6SUB 7FHD6 7FNext, 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
2BA7PUSH HLE5Save the value of the BASIC line pointer in HL to the STACK. Register L holds the reserved word number at this point.
2BA8LD E,A5FLoad Register E with the character in Register A
2BA9-2BABLoad HL with the starting address of the reserved words list
2BAC- ↳ LOPRESLD A,(HL)7ELoad Register A with the character at the location of the reserved words list pointer in HL
2BADOR AB7Test 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.
2BAEINC HL23Bump the value of the reserved words list pointer in HL
2BAF-2BB1If the character at the location of the reserved words pointer in Register A doesn't have bit 7 set then Jump back to 2BACH.
2BB2DEC E1DDecrement the counter
2BB3-2BB4Jump back to 2BACH if this isn't the reserved word for the token
2BB5-2BB6AND 7FHE6 7FReset 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- ↳ MORPURLD (BC),A02Save the character in Register A at the location of the input buffer pointer in BC
2BB8INC BC03Bump the value of the input buffer pointer in BC
2BB9DEC D15Decrement the value of the character counter in Register D
2BBA-2BBCIf 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
2BBDLD A,(HL)7ELoad Register A with the character at the location of the reserved words pointer in HL
2BBEINC HL23Bump the reserved words pointer in HL
2BBFOR AB7Test the value of the character in Register A
2BC0-2BC2Keep getting characters in this reserved word until we hit the next reserved word
2BC3POP HLE1Get the value of the BASIC line pointer from the STACK and put it in HL
2BC4-2BC5Jump 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- ↳ DELETEGOSUB to 1B10H to evaluate the line numbers at the location of the current BASIC program pointer in HL
2BC9POP DED1Get the value of the last BASIC line number to be deleted (in binary) from the STACK and put it in DE
2BCAPUSH BCC5Save the address of the first BASIC line to be deleted in BC to the STACK
2BCBPUSH BCC5Save the address of the first BASIC line to be deleted in BC to the STACK AGAIN!
2BCC-2BCEGOSUB 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-2BD0Since 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
2BD2LD D,H
LD E,L54Let Register Pair DE = Register Pair HL
2BD3EX (SP),HLE3Exchange the last BASIC line's address in HL with the first BASIC line's address to the STACK
2BD4PUSH HLE5Save the pointer to the first line in range to the STACK
2BD5RST 18HCOMPARDFWe 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- ↳ FCERRGDisplay 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
2BDC-2BDECall the WRITE MESSAGE routine at 28A7H to print the message pointed to by HL.
2BDFPOP BCC1Get the first BASIC line's address from the STACK and put it in BC
2BE3EX (SP),HLE3Swap (SP) and HL so that HL now points to the next BASIC line's address ...
2BE4- ↳ DELEX DE,HLEBand then put it into DE
2BE5-2BE7LD HL,(40F9H)LD HL,(VARTAB)2A F9 40Load HL with the end of the BASIC program pointer.
- Note: 40F9H-40FAH holds the starting address of the simple variable storage area.
2BE8- ↳ MLOOPLD A,(DE)1ALoad Register A with the character at the location of the memory pointer in DE
2BE9LD (BC),A02Save the character in Register A at the location of the memory pointer in BC
2BEAINC BC03Bump the value of the memory pointer in BC
2BEBINC DE13Bump the value of the memory pointer in DE
2BECRST 18HCOMPARDFNow 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-2BEELoop back to 2BE8H until the memory pointer in DE equals the end of the BASIC program pointer in HL
2BEFLD H,B60Load Register H with the MSB of the memory pointer in Register B
2BF0LD L,C69Load Register L with the LSB of the memory pointer in Register C
2BF1-2BF3LD (40F9H),HLLD (VARTAB),HL22 F9 40Save 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.
2BF4RETC9RETurn 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- ↳ CSAVECalls the WRITE LEADER routine at 0284H (which writes a Level II leader on the cassette unit set in Register A)
2BF82BFAH 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
2BFBPUSH HLE5Save 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
2BFF-2C00LD A,D3H3E D3Load Register A with the filename header byte (=D3H which is a "S" with the sign bit on)
2C01-2C03the 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
2C07LD A,(DE)1ALoad Register A with the first character of the filename at the location of the filename pointer in DE
2C08-2C0Athe 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-2C0DLD HL,(40A4H)LD HL,(TXTTAB)2A A4 40Load 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).
2C0EEX DE,HLEBLoad DE with the start of the BASIC program pointer in HL
2C0F-2C11LD HL,(40F9H)LD HL,(VARTAB)2A F9 40Load HL with the end of the BASIC program pointer.
- Note: 40F9H-40FAH holds the starting address of the simple variable storage area.
2C12- ↳ LOPSCOLD A,(DE)1ATop 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
2C13INC DE13Bump the value of the memory pointer in DE
2C14-2C16the 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)
2C17RST 18HCOMPARDFNow 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-2C19Loop back to 2C12H until the memory pointer in DE is equal to the end of the BASIC program pointer in HL
2C1DPOP HLE1Get the value of the current BASIC program pointer from the STACK and put it in HL
2C1ERETC9RETurn to CALLer
2C1F-2CA4 - LEVEL II BASIC CLOAD ROUTINE - ROM v1.0- "CLOAD"
2C22LD A,(HL)7ELoad Register A with the character at the location of the current BASIC program pointer in HL
2C23-2C24SUB 0B2HSUB $PRINTD6 B2Check 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-2C26Jump to the CLOAD?routine at 2C29H if the character at the location of the current BASIC program pointer in Register A is a ?
2C27XOR AAFOK - So this is now a straight CLOAD. First, zero Register A
2C28LD BC,232F01 2F 23Z-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- ↳ CLOADPCPL2FLoad Register A with a -1 for CLOAD?. It will still be a 0 if this is CLOAD
2C2AINC HL23Bump the value of the current BASIC program pointer in HL until it points to the next character after the ?in CLOAD?
2C2BPUSH AFF5Save the CLOAD/CLOAD?flag in Register A to the STACK
2C2CDEC HL2BDecrement the value of the current BASIC program pointer in HL so we can see if we are at the end
2C2DRST 10HCHRGETD7We 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-2C2FLD A,00H3E 00Zero Register A to allow for any filename
2C30-2C31Jump if the character at the location of the current BASIC program pointer in HL is an end of the BASIC statement character
2C32-2C34To 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-2C37Make sure the length is good, and save the pointer to the filename to Register Pair DE
2C38LD A,(DE)1ALoad Register A with the first character of the filename at the location of the filename pointer in DE
2C39- ↳ CLNONMLD L,A6FLoad Register L with the filename in Register A
2C3APOP AFF1Get the value of the CLOAD/CLOAD?flag from the STACK and put it in Register A
2C3BOR AB7Test the value of the CLOAD/CLOAD?flag in Register A (since CPL doesn't set any flags)
2C3CLD H,A67Load Register H with the value of the CLOAD/CLOAD?flag in Register A
2C3D-2C3FLD (4121H),HLLD (FACLO),HL22 21 41Save the value of the CLOAD/CLOAD?flag and the filename in HL in ACCumulator
2C43-2C45LD HL,(4121H)LD HL,(FACLO)2A 21 41Load HL with the CLOAD/CLOAD?flag and the filename in ACCumulator
2C46EX DE,HLEBLoad D with the CLOAD/CLOAD? flag and load Register E with the filename
2C47-2C48- ↳ LOPCLKLD B,03H06 03Load Register B with the number of header bytes
2C49-2C4B- ↳ LOPCL2Calls 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-2C4DSUB 0D3HD6 D3Check to see if the character in Register A is a filename header byte
2C4E-2C4FLoop if the character in Register A isn't a filename header byte
2C50-2C51Loop back to 2C49H until three filename header bytes have been read
2C52-2C54Now 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)
2C55INC E1CWe 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
2C56DEC E1DDecrement the value of the filename in Register E
2C57-2C58Jump to 2C5CH (to pretend the filename matched) if no filename was specified
2C59CP EBBIf 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-2C5BJump 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- ↳ NONAMCLD HL,(40A4H)LD HL,(TXTTAB)2A A4 40If 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- ↳ DOCRSLD B,03H06 03Load Register B with the number of zeros to look for to stop the load (which is 3)
2C61-2C63- ↳ DOCSMRCalls 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)
2C64LD E,A5FPreserve the character that was just read from tape into Register E
2C65SUB (HL)96Compare 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
2C66AND DA2Combine 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.
2C69LD (HL),E73At 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-2C6CMake sure there is more room, and toss a ?OM ERRORif there isn't.
2C6DLD A,(HL)7ELoad Register A with the character at the location of the memory pointer in HL
2C6EOR AB7Check to see if the byte just read in Register A is equal to zero
2C6FINC HL23Bump the value of the memory pointer in HL
2C70-2C71Loop if the byte in Register A isn't equal to zero (meaning that it isn't end of program or end of statement)
2C72-2C74Call the BLINK ASTERISK routine at 022CH which alternatively displays and clears an asterisk in the upper right hand corner of the video display
2C75-2C76Do that loop until three zeros in a row have been read from the cassette recorder
2C77-2C79LD (40F9H),HLLD (VARTAB),HL22 F9 40By 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-2C7CGOSUB 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.
2C80-2C82We 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-2C85LD HL,(40A4H)LD HL,(TXTTAB)2A A4 40Load 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).
2C86PUSH HLE5Save the start of the BASIC program pointer in HL to the STACK. FINI will need this value there.
2C8A-2C8CGOSUB 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-2C8FCall 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-2C92JP 1A18JP STPRDYC3 18 1AJUMP to STPRDY to pop NEWSTT from the STACK and then fall into the READY routine
2C93-2C95- ↳ SKPFILLD (3C3EH),A32 3E 3CGo display the filename on the video display
2C96-2C97- ↳ ZERSRFLD B,03H06 03Load Register B with the number of zeros to be found to stop the search
2C98-2C9A- ↳ GETCHZCalls 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)
2C9BOR AB7Check to see if the character in Register A is equal to zero
2C9E-2C9FLoop until three zeros in a row have been read from the cassette recorder
2CA0-2CA2Calls 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)
2CA5-2CA9 - MESSAGE STORAGE LOCATION- "NOOKCS"
2CA5-2CA9- ↳ NOOKCS"BAD" + 0DH + 00H42The 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- ↳ PEEKCall 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)
2CADLD A,(HL)7ELoad Register A with the value at the location of the memory pointer in HL
2CAE-2CB0Go save the 8-bit value in Register A as the current result in ACCumulator
2CB1-2CBC - LEVEL II BASIC POKEROUTINE- "POKE"
2CB1-2CB3- ↳ POKEGo evaluate the expression at the location of the current BASIC program pointer in HL and return with the integer result in DE
2CB4PUSH DED5Save the address the user wants to POKE to (held in DE) to the STACK
2CB5-2CB6RST 08H ⇒ 2ESYNCHK ","CF 2ESince 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-2CB9GOSUB 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
2CBAPOP DED1Get the address the user wants to POKE to from the STACK and put it in DE
2CBBLD (DE),A12Save the value the user wanted to poke (held in Register A) in the location that the user wants to POKE to (held in DE)
2CBCRETC9RETurn 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- ↳ PRINUSGo evaluate the string expression at the location of the current BASIC program pointer in HL 2CC0-2CC2Go make sure the expression that was just evaluated was a string 2CC3-2CC4RST 08H ⇒ 3BSYNCHK ";"CF 3BSince 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).
2CC5EX DE,HLEBSwap DE and HL so that DE now holds the current BASIC program pointer 2CC6-2CC8LD HL,(4121H)LD HL,(FACLO)2A 21 41Load HL with the USING string's VARPTR 2CCB-2CCD- ↳ REUSSTLD A,(40DEH)LD A,(FLGINP)3A DE 40Load 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. 2CCEOR AB7Check to see if that flag indivates that we did, or did not, print out a value last time. 2CCF-2CD0If we did not print out a value last time, we have an error, so JUMP down to 2CDDH 2CD1POP DED1Restore the pointer to the "USING" string decription from the STACK into DE 2CD2EX DE,HLEBSwap 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- ↳ INIUSPUSH HLE5Save the pointer to the "USING" string descriptor (i.e., the USING string's VARPTR) in HL to the STACK 2CD4XOR AAFZero Register A and all the flags. 2CD5-2CD7LD (40DEH),ALD (FLGINP),A32 DE 40Clear the flag we are using to see if we printed the values or not. 2CD8CP DBATurn 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. 2CD9PUSH AFF5Save the flag indicating if the value list has ended or not to the STACK 2CDAPUSH DED5Save the pointer into the value list to the STACK 2CDBLD B,(HL)46Load Register B with the USING string's length 2CDCOR BB0Check to see if the USING string's length in Register B is equal to zero 2CDD-2CDF- ↳ FCERR3If the USING string is NULL then display a ?FC ERROR 2CE0INC HL23Bump the pointer to the USING string's data in HL by 1 2CE1LD C,(HL)4ELoad Register C with the LSB of the USING string's address at the location of the USING string's VARPTR in HL 2CE2INC HL23Bump the value of the USING string's VARPTR in HL 2CE3LD H,(HL)66Load Register H with the MSB of the USING string's address at the location of the USING string's VARPTR in HL 2CE4LD L,C69Load Register L with the LSB of the USING string's address in Register C
2CE7- ↳ BGSTRFLD E,B58Load Register E with the USING string's length 2CE8PUSH HLE5Save the pointer to the USING string pointer in HL to the top of the STACK 2CE9-2CEALD C,02H0E 02Since the \\ string field length is two plus number of enclosed spaces, add two 2CEB- ↳ LPSTRFLD A,(HL)7ELoad Register A with the character at the location of the USING string pointer in HL 2CECINC HL23Bump the value of the USING string data pointer in HL 2CED-2CEECP 25HCP CSTRNGFE 25Check 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-2CF1If it is a "%" then JUMP to 2E17H to evaluate a string and print 2CF2-2CF3CP 20HFE 20Check 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-2CF5If the character is not a field extender, then it isn't a string field, so JUMP down a few opcodes to 2CF9H 2CF6INC C0CIncrement the field width (tracked in in Register C) 2CF7-2CF8Decrement 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- ↳ NOSTRFPOP HLE1Restore the pointer to the "USING" string's data into Register Pair HL
2CFALD B,E43Load Register B with the USING string's length
2CFB-2CFCLD A,25HLD A,CSTRNG3E 25Restore 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- ↳ NEWUCHIf a +came before the character, make sure to print it
2D00-2D02Once that has been printed, now we print the character in Register A since we know it isn't part of a field
2D03- ↳ PRCCHRXOR AAFWe 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
2D04LD E,A5FZero Register E
2D05LD D,A57Zero Register D
2D06-2D08- ↳ PLSFINGo print a leading +if necessary (i.e., to allow for multiple plusses)
2D09LD D,A57Set 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.
2D0ALD A,(HL)7ELoad Register A with the next field description character in the USING string
2D0BINC HL23Bump the value of the USING string pointer in HL
2D0C-2D0DCP 21HCP "!"FE 21Check 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.
2D11-2D12CP 23HCP "#"FE 23Check 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.
2D15DEC B05Since every other possibility is actually a two character field, decrement the value of the string's length in Register B onem ore time
2D16-2D18If 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-2D1ACP 2BHCP "+"FE 2BCheck 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-2D1CLD A,08H3E 08Set 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
2D1FDEC HL2BDecrement the value of the USING string pointer so we can re-get the character.
2D20LD A,(HL)7ELoad Register A with the (current) character at the location of the USING string pointer in HL
2D21INC HL23Bump the value of the USING string pointer in HL
2D22-2D23CP 2EHCP "."FE 2ECheck 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-2D25Jump if the character in Register A is a .to scan with Register E holding the number of digits before the "." as 0
2D26-2D27CP 25HCP CSTRNGFE 25Check 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-2D29Jump to see if it is really a string field if the character in Register A is a %
2D2ACP (HL)BECheck 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-2D2CIf the NZ flag is set, then we can't have a $$or a **, so all remaining possibilities are exhausted, so JUMP to NEWUCH
2D2D-2D2ECP 24HCP "$"FE 24Check 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.
2D31-2D32CP 2AHCP "*"FE 2ACheck 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-2D34If 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
2D35LD A,B78Prepare 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-2D37CP 02HFE 02Check 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.
2D38INC HL23Bump the value of the USING string pointer in HL
2D39-2D3AJump to 2D3EH if the USING string's length in Register A isn't at least two
2D3BLD A,(HL)7ELoad Register A with the character at the location of the USING string pointer in HL
2D3C-2D3DCP 24HCP "$"FE 24Check 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- NOTSPCLD A,20H3E 20Set the *bit in Register A
2D40-2D41If we did not ultimately get a **$then JUMP (noting we do NOT set the dollar sign flag)
2D42DEC B05Decrement the value of the USING string's length to take the $into account
2D43INC E1CBump the field width tracker to account for the floating dollar sign
2D44-2D45CP 0AFHFE AFZ-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- ↳ DOLRNMXOR AAFThis is $processing for PRINT USING. Clear Register A.
2D46-2D47ADD A,10HC6 10Mask Register A to set the bit for a floating dollar sign flag.
2D48INC HL23Bump the value of the USING string pointer in HL to go past the special characters
2D49- ↳ SPCNUMINC E1CSince two characters specify the field size, start off with E=1
2D4AADD A,D82Combine the bits in Register D into the flag tracker
2D4BLD D,A57Preserve the modified flag tracker into Register D.
2D4C- ↳ NUMNUMINC E1CBump the number of characters to the left of the decimal point in Register E
2D4D-2D4ELD C,00H0E 00Set the number of digits to the right of the decimal point (tracked in Register C) to 0
2D4FDEC B05Check to see if there are any more characters by decrementing the value of the string's length in Register B
2D50-2D51If 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.
2D52LD A,(HL)7ELoad Register A with the next character at the location of the USING string pointer in HL
2D53INC HL23Bump the value of the USING string pointer in HL
2D54-2D55CP 2EHCP "."FE 2ECheck 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-2D57If yes, then need to use a special scan loop to scan after the decimal point, so JUMP to AFTDOT
2D58-2D59CP 23HCP "#"FE 23Check 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.
2D5C-2D5DCP 2CHCP ","FE 2CCheck 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-2D5FIf there is no comma, then JUMP to FINNUM because there are no more leading digits and we need to check for "^^^"
2D60LD A,D7AIf we are here, then a comma was requested. Turn on the COMMA bit
2D61-2D62OR 40HF6 40Mask the flag in Register A for ,
2D63LD D,A57Load Register D with the value of the flag in Register A
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- ↳ DOTNUMLD A,(HL)7ELoad Register A with the next character of the USING string
2D67-2D68CP 23HFE 23Check 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-2D6ALD A,2EHLD A,"."3E 2ELoad Register A with a decimal point
2D6B-2D6CIf it isn't a "." then JUMP AWAY to NEWUCH with A holding a "." so that a "." will get printed
2D6D-2D6ELD C,01H0E 01If 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
2D6FINC HL23Bump the value of the USING string pointer in HL
2D70- ↳ AFTDOTINC C0CBump the number of digits to the right of the decimal point (tracked in Register C)
2D71DEC B05Decrement the value of the USING STRING's length to test to see if there are more characters
2D72-2D73If the USING string length is now ZERO, JUMP to ENDNUS to stop scanning
2D74LD A,(HL)7ELoad Register A with the character at the location of the USING string pointer in HL
2D75INC HL23Bump the value of the USING string pointer in HL
2D76-2D77CP 23HFE 23Check 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-2D79If 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- ↳ FINNUMPUSH DED5Save the value of the flag (tracked in D) and the number of leading digits (tracked in E) to the STACK
2D7B-2D7DLoad DE with the return address in case this is not a scientific notation
2D7EPUSH DED5Save the value of the return address in DE to the STACK
2D7F
2D80LD D,H
LD E,L54Let DE = HL in case we need to rememer HL
2D81-2D82CP 5BHFE 5BCheck 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.
2D83RET NZC0Return if the character in Register A isn't an up arrow
2D84CP (HL)BECheck 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.
2D85RET NZC0Return 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)
2D86INC HL23Bump the value of the USING string pointer in HL
2D87CP (HL)BECheck 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.
2D88RET NZC0Return 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)
2D89INC HL23Bump the value of the USING string pointer in HL
2D8ACP (HL)BECheck 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.
2D8BRET NZC0Return 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)
2D8CINC HL23Bump the value of the USING string pointer in HL. If we are here we have a #.^^^^format
2D8DLD A,B78Now 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-2D8FSUB 04HD6 04Check to see if there are at least 4 characters left in the USING string
2D90RET CD8Return to 2D97 if there aren't at least four characters left in the USING string
2D91POP DED1If there are at least 4 characters left, then clean up the STACK by removing the NOTSCI return address
2D92POP DED1Get the flag and the count of the characters to the left of the decimal point from the STACK and put it in DE
2D93LD B,A47Load Register B with the new USING string's length in Register A
2D94INC D14Set the exponential notation flag (tracked in Register D)
2D95INC HL23Bump the value of the USING string pointer in HL
2D96JP Z,0D1EBHCA EB D1Z-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
2D98POP DED1(Ignored if passing through) Restore the flags into Register D and the number of leading digits into Register E
2D99- ↳ ENDNUSLD A,D7AWe 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
2D9ADEC HL2BDecrement the value of the USING string pointer in HL
2D9BINC E1CBump the number of characters to the left of the decimal point in Register E to take into account the leading plus
2D9C-2D9DAND 08HE6 08Mask Register A to NOT check for a trailing sign
2D9E-2D9FIf that AND leaves us with a NZ, then we are all done with the field, so JUMP to ENDNUM
2DA0DEC E1DOtherwise, 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
2DA1LD A,B78Check to see if there are more characters by first loading Register A with the USING string's length from Register B
2DA2OR AB7Check to see if this is the end of the USING string
2DA3-2DA4If we are out of characters, then we are all done, so JUMP to ENDNUM
2DA5LD A,(HL)7EIf there ARE more characters, then fill Register A with the character at the location of the USING string pointer in HL
2DA6-2DA7SUB 2DHSUB "-"D6 2DCheck to see if the character in Register A is a -(i.e., a trailing minus)
2DAA-2DABCP 0FEHCP "+" - "-"FE FECheck 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.
2DAE-2DAFLD A,08H3E 08If we are here then we did have a trailing "+" so first set the flag for a POSITIVE "+"
2DB0-2DB1- ↳ SGNTRLADD A,04HC6 04Then set the flag for a trailing sign
2DB2ADD A,D82Combine the value of the flag in Register D with the value of the flag in Register A
2DB3LD D,A57Load Register D with the current flags
2DB4DEC B05Decrement 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- ↳ ENDNUMPOP HLE1Get the value of the current BASIC program pointer from the STACK and put it in HL
2DB6POP AFF1Load Register A with the flag that tells us whether there are more values to process in the value list.
2DB7-2DB8If there are no more values in the value list to process, then JUMP to FLDFIN because we are done with the PRINT
2DB9PUSH BCC5Save 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
2DBAPUSH DED5Save the flags (held in D) and the number of leading digits (held in E) to the STACK
2DBB-2DBDRead 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
2DBEPOP DED1Restore the flags (held in D) and the number of leading digits (held in E) from the STACK
2DBFPOP BCC1Restore 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
2DC0PUSH BCC5Save 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
2DC1PUSH HLE5Save the value of the current BASIC program pointer in HL to the STACK
2DC2LD B,E43Set Register B to hold the number of leading digits (i.e., the number of characters to the left of the decimal point)
2DC3LD A,B78We 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
2DC4ADD A,C81Then 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-2DC6CP 19HFE 19Check 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-2DC9Display a FC ERROR message if the total number of digits is greater than 24
2DCALD A,D7ALoad Register A with the flags (held in Register D)
2DCB-2DCCOR 80HF6 80Turn on the "USING" bit in the flags
2DCD-2DCFPrepare 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-2DD2Call 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- ↳ FNSTRFPOP HLE1Top of a loop. Get the value of the current BASIC program pointer from the STACK and put it in HL
2DD4DEC HL2BDecrement the value of the current BASIC program pointer in HL so we can test to see what the terminator was
2DD5RST 10HCHRGETD7We 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.
2DD6SCF37Set the Carry flag to indicate that a CRLF is desired
2DD7-2DD8If 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-2DDBLD (40DEH),ALD (FLGINP),A32 DE 40Set the flag that the value HAS been printed!
2DDC-2DDDCP 3BHCP ";"FE 3BCheck 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.
2DE0-2DE1CP 2CHCP ","FE 2CCheck 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-2DE4If 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- ↳ SEMUSNRST 10HCHRGETD7We 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- ↳ CRDNUSPOP BCC1Restore the number of characters remaining to be procesed in the USING string into Register B
2DE7EX DE,HLEBSwap DE and HL so that DE will point to the location of the current BASIC program pointer. We don't care about HL.
2DE8POP HLE1Restore the position in the USING string from the STACK and put it in HL
2DE9PUSH HLE5Save the position in the USING string (held in HL) to the STACK
2DEAPUSH AFF5Save the flag that indicates whether or not the value list has terminated to the STACK
2DEBPUSH DED5Save 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.
2DECLD A,(HL)7ELoad Register A with the USING string's length at the location of the USING string's VARPTR in HL
2DEDSUB B90Subtract the number of characers which were already scanned
2DEEINC HL23Bump the pointer to the "USING" strings string data
2DEFLD C,(HL)4ELoad Register C with the LSB of the USING string's address at the location of the USING string's VARPTR in HL
2DF0INC HL23Bump the value of the USING string's VARPTR in HL
2DF1LD H,(HL)66Load Register H with the MSB of the USING string's address at the location of the USING string's VARPTR in HL
2DF2LD L,C69Load Register L with the LSB of the USING string's address in Register C
2DF3-2DF4LD D,00H16 00Zero Register D so that Register Pair DE can be a 16 bit offset of whatever is held in A.
2DF5LD E,A5FLoad Register E with the USING string's offset in Register A
2DF6ADD HL,DE19Add 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
2DF7LD A,B78Load Register A with the number of characters left to scan
2DF8OR AB7Check to see if this is the end of the USING string
2DF9-2DFBIf there are still more string characters to scan, JUMP to PRCCHR to do so
2DFE-2E00 - Part of the PRINT USING Routine- "REUSIN"
We will wind up here when we are done processing a numeric field
2E01-2E03Go send the FINAL character (held in Register A) to the current output device
2E04- ↳ FINUSIPOP HLE1Get the value of the current BASIC program pointer from the STACK and put it in HL
2E05POP AFF1Restore the flag which indicates whether or not the value list has ended into Register A
2E06-2E08If the value list has NOT ended, JUMP back to REUSST to reuse the USING string
2E09-2E0B- ↳ FLDFINIf 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
2E0CEX (SP),HLE3Swap (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
2E10POP HLE1Get the value of the current BASIC program pointer from the STACK and put it in HL
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- ↳ SMSTRFLD C,01H0E 01Set the field width to 1
2E16-2E17LD A,0F1H3E F1Z-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- ↳ ISSTRFPOP 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)
2E18DEC B05Decrement the USING string character count (tracked in Register B)
2E1CPOP HLE1Get the value of the current BASIC program pointer from the STACK and put it in HL
2E1DPOP AFF1Get the flag which indicates whether there are more values in the value list into Register A
2E1E-2E1FIf there are no more values in the value list, then we are done so JUMP back to 2E09H
2E20PUSH BCC5Save the number of characters still to be scanned from the USING string (tracked in B) to the STACK
2E21-2E23Read 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
2E27POP BCC1Restore the field width (a/k/a the number of characters to be printed) into Register C
2E28PUSH BCC5Save the USING string's length and the number of characters to be printed in BC to the STACK
2E29PUSH HLE5Save the value of the current BASIC program pointer in HL to the STACK
2E2A-2E2CLD HL,(4121H)LD HL,(FACLO)2A 21 41Load HL with the string's VARPTR in ACCumulator
2E2DLD B,C41Load Register B with field width (a/k/a the number of characters to be printed)
2E2E-2E2FLD C,00H0E 00Zero Register C so that we can use the LEFT$ routine
2E30PUSH BCC5Save the length of the string to be printed in Register B to the STACK (as we will need that for space padding)
2E31-2E33Truncate the string to B characters via a call to the LEFT$ routine
2E37-2E39LD HL,(4121H)LD HL,(FACLO)2A 21 41Load HL with the string's VARPTR in ACCumulator so we can see if we need to pad the string
2E3APOP AFF1Get the field width (a/k/a the length of the string to be printed) from the STACK and put it in Register A
2E3BSUB (HL)96Determine 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
2E3CLD B,A47Save the amount of padding needed into Register B
2E3D-2E3ELD A,20H3E 20Load Register A with a SPACE
2E3FINC B04Bump 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- ↳ UPRTSPDEC B05Top of a loop. Decrement the number of spaces in Register B
2E41-2E43If all of the spaces have been printed, Jump back to 2DD3H to see if the value list ended and to resume scanning
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- ↳ PLSPRTPUSH AFF5Save the current character (held in Register A) to the STACK
2E4ALD A,D7AWe need to test the PLUS BIT in D, so first load Register A with the value in Register D
2E4BOR AB7Check 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-2E4DLD A,2BHLD A,"+"3E 2BPrepare to print the +by loading Register A with a +
2E4E-2E50If the bit was set (i.e., A was non-zero), then send a +to the current output device
2E51POP AFF1Get the current character from the STACK and put it in Register A
2E52RETC9RETurn 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- ↳ ERREDTLD (409AH),ALD (ERRFLG),A32 9A 40Reset the EDIT flag.
Note: 409AH holds the ERROR/RESUME flag
2E56-2E58LD HL,(40EAH)LD HL,(ERRLIN)2A EA 40Load HL with the line number to be edited.
Note: 40EAH-40EBH holds the line number with error
2E59OR HB4OR Register A with the MSB of the error line number in Register H
2E5AAND LA5Combine 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
2E5BINC A3CBump 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
2E5CEX DE,HLEBSwap DE and HL so that DE now holds the line number to edit.
2E5DRET ZC8If there was no line number, return if Level II BASIC
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- ↳ EDITGet the first line number by calling 1E4F - returns in in DE
2E63RET NZC0If the zero flag got set, there was no line number, so return
2E64- ↳ EREDITPOP HLE1Clean up the STACK (i.e., discard the NEWSTT return address)
2E65- ↳ EEDITSEX DE,HLEBLoad HL with the line number to be edited in DE
2E66-2E68LD (40ECH),HLLD (DOT),HL22 EC 40Save 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
2E69EX DE,HLEBLoad HL with the line number to be edited
2E6A-2E6CFind 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
2E70
2E71LD H,B
LD L,C60At 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
2E73INC HL
INC HL23Bump the value of the memory pointer in HL twice to now point to the first byte of the line.
2E74LD C,(HL)4ELoad Register C with first byte of the line number being edited
2E75INC HL23Bump the value of the memory pointer in HL to point to the second byte of the line being edited
2E76LD B,(HL)46Load Register B with second byte of the line number being edited
2E77INC HL23Bump the value of the memory pointer in HL to now point to the first byte of the actual line
2E78PUSH BCC5Save the line number to the STACK
2E79-2E7BGOSUB 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- ↳ LLEDPOP HLE1Get the value of the line number from the STACK and put it in HL
2E7D- ↳ INLEDPUSH HLE5Save the value of the line number in HL to the STACK
2E7E-2E80Convert 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-2E82LD A,20H3E 20Load Register A with a space
2E86-2E88LD HL,(40A7H)LD HL,(BUFPNT)2A A7 40Load 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-2E8ALD A,0EH3E 0ELoad Register A with the "turn on the cursor" character
2E8EPUSH HLE5Save the value of the input buffer pointer (in HL) to the STACK
2E8F-2E90LD C,FFH0E FFLoad 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- ↳ LENLPINC C0CBump the number of characters examined so far in Register C
2E92LD A,(HL)7ELoad Register A with the character at the location of the input buffer pointer in HL
2E93OR AB7Check to see if the character in Register A is an end of the BASIC line character
2E94INC HL23Bump the value of the input buffer pointer in HL
2E95-2E96Loop back to 2E91H until the end of the BASIC line has been found
2E97POP HLE1At this point, C will be the maximum number of characters in the line at issue. Put the start of the expanded buffer into HL
2E98LD B,A47Set the current position in the BASIC line being edited (tracked by Register B) to ZERO
2E99-2E9A- ↳ DISPEDLD D,00H16 00Assume the repetition count for the upcoming command (tracked by Register D) is zero
2E9B-2E9D- ↳ DISPIGo scan the keyboard to wait for the user command
2D9E-2E9F- ↳ DISPSUB 30H20 15We need to test to see if the character was alphabetic or alphanumeric so we subtract 30H from it
2EA2-2EA3CP 0AHFE 0ACheck 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.
2EA6LD E,A5FLoad Register E with the binary value of the character in Register A
2EA7LD A,D7APut the repetition value into Register A
2EA8RLCA07Multiply the value in Register A by two (so now A has multiplied by 2)
2EA9RLCA07Multiply the value in Register A by two (so now A has multiplied by 4)
2EAAADD A,D82Add the value in Register D to the value in Register A (so now A has multiplied by 5)
2EABRLCA07Multiply the value in Register A by two (so now A has multiplied by 10). Now the "ones place" is empty.
2EACADD A,E83Add the value in Register E to the value in Register A in the "ones place"
2EADLD D,A57Load Register D with the value in Register A
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- ↳ NOTDGIPUSH HLE5Save the value of the input buffer pointer in HL to the STACK
2EB4EX (SP),HLE3Exchange the value of the return address in HL with the value of the input buffer pointer to the STACK
2EB5DEC D15We 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
2EB6INC D14... and then incrementing the numeric value in Register D to set the flags
2EB7-2EB9If we had a received a repetition count already, then JUMP to 2EBBH
2EBAINC D14Otherwise, set the repetition count (held in Register D) to be one
2EBB-2EBC- ↳ NTZERDCP 0D8HFE D8Check 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-2EBFIf the character in Register A is a BACKSPACEcharacter, JUMP to DELED
2EC0-2EC1CP 0DDHFE DDCheck 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-2EC4If the character in Register A is a CARRIAGE RETURN, JUMP to CRED
2EC5-2EC6CP 0F0HFE F0Check 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.
That's it for non-alphabetic instructions, so we need to need to convert a lower case command to upper case
2EC9-2ECACP 31HFE 31Check 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.
2ECD-2ECESUB 20HD6 20Convert the lowercase character in Register A to uppercase
2ECF-2ED0- ↳ NOTLW4CP 21HFE 21Check 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.
2ED4-2ED5CP 1CHFE 1CCheck 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.
2ED9-2EDACP 23HFE 23Check 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.
2EDD-2EDECP 19HFE 19Check 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.
2EE2-2EE3CP 14HFE 14Check 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.
2EE7-2EE8CP 13HFE 13Check 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.
2EEC-2EEDCP 15HFE 15Check 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.
2EF1-2EF2CP 28HFE 28Check 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.
2EF6-2EF7CP 1BHFE 1BCheck 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.
2EFA-2EFBCP 18HFE 18Check 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-2EFEJump if the character in Register A is an H(i.e., HACK off the rest of the line and then enter INSERT mode)
2EFF-2F00CP 11HFE 11Check 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.
2F01RET NZC0Return if the character in Register A isn't an A
2F02 - EDITCommand - Cancel and Restore Logic.
2F02POP BCC1Clean up the STACK (i.e., remove the DISPI return address)
2F03POP DED1Get the BASIC line number from the STACK and put it in DE
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- ↳ SPEDLD A,(HL)7ELoad Register A with the character at the location of the input buffer pointer in HL
2F0BOR AB7Check to see if the character in Register A is an end of the BASIC line character
2F0CRET ZC8Return if the character in Register A is an end of the BASIC line character
2F0DINC B04Bump the character position in Register B
2F11INC HL23Bump the value of the pointer in HL
2F12DEC D15Decrement the number of times to perform the operation in Register D
2F15RETC9RETurn to CALLer
2F16 - EDITCommand - KILL Logic- "KED".
2F16- ↳ KEDPUSH HLE5Save the current character position in the buffer in HL to the STACK
2F17-2F19Load HL with the return address of 2F5FH (which will print the final !
2F1AEX (SP),HLE3Exchange the value of the return address in HL with the value of the input buffer pointer to the STACK
2F1BSCF37Set the KILL/SEARCH flag for KILL since CARRY flag signals KILL
2F1C- ↳ SEDPUSH AFF5Save the KILL/SEARCH flag to the STACK
2F1D-2F1FGo scan the keyboard for the character the user wants to SEARCH for
2F20LD E,A5FSave the character the user wants to SEARCH for into Register E
2F21POP AFF1Get the KILL/SEARCH flag from the STACK
2F22PUSH AFF5Save the KILL/SEARCH flag to the STACK
2F23-2F25If KILL (because the CARRY flag was set) then GOSUB to 2F5FH to print a !
2F26- ↳ SRCALPLD A,(HL)7ELoad Register A with the character at the location of the input buffer pointer in HL
2F27OR AB7Check to see if the character in Register A is an end of the BASIC line character
2F28-2F2AJump down to 2F3EH if the character in Register A is an end of the BASIC line character
2F2EPOP AFF1Get the KILL/SEARCH flag from the STACK
2F2FPUSH AFF5Save the KILL/SEARCH flag to the STACK
2F30-2F32If 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-2F34Jump to 2F37H if KILL. Note, we do not move the HL pointer in this case because DELCHR already moved it.
2F35INC HL23If we are here, it must be SEARCH! So bump to the next character
2F36INC B04Bump the value of the character position in Register B
2F37- ↳ NOTSRCLD A,(HL)7ERegardless of whether we are SEARCH or KILL, load Register A with the character at the location of the current input buffer pointer in HL
2F38CP EBBCheck 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.
2F3BDEC D15Decrement the number of times to perform the operation in Register D (as initially specified by the user by entering a number before the command)
2F3E- ↳ POPARTPOP AFF1Get the KILL/SEARCH flag from the STACK
2F3FRETC9RETurn to CALLer
2F40 - EDITCommand - LIST Logic- "LED".
2F40-2F42- ↳ LEDSince 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
2F46POP BCC1Clear off the RETURN address to DISPED
2F47-2F49Jump to 2E7CH (to display the current line number and await the next EDIT command)
2F4A - EDITCommand - DELETE Logic- "DED"
2F4A- ↳ DEDLD A,(HL)7ELoad Register A with the character at the location of the input buffer pointer in HL
2F4BOR AB7Check to see if the character in Register A is an end of the BASIC line character
2F5CRET Z15Return if the character in Register A is an end of the BASIC line character
2F4D-2F4ELD A,21HLD A,"!"3E 21Load Register A with an !
2F52- ↳ DELLPLD A,(HL)7ELoad Register A with the character at the location of the input buffer pointer in HL
2F53OR AB7Check to see if the character in Register A is an end of the BASIC line character
2F54-2F5BJump to 2F5FH if the character in Register A is an end of the BASIC line character
2F5CDEC D15Decrement the number of times to perform the operation in Register D (as initially specified by the user by entering a number before the command)
2F5F-2F60- ↳ TYPSLHLD A,21HLD A,"!"3E 21Load Register A with an !
2F64RETC9RETurn to CALLer
2F65 - EDITCommand - CHANGE Logic- "CED".
2F65- ↳ CEDLD A,(HL)7ELoad Register A with the character at the location of the input buffer pointer in HL
2F66OR AB7Check to see if the character in Register A is an end of the BASIC line character
2F67RET ZC8Return if the character in Register A is an end of the BASIC line character
2F68-2F6AGo get the character to put in the input buffer from the keyboard
2F6BLD (HL),A77Save the character in Register A at the memory location of the input buffer pointer in HL
2F6FINC HL23Bump the value of the input buffer pointer in HL
2F70INC B04Bump the character position in Register B
2F71DEC D15Decrement the number of times to perform the operation in Register D (as initially specified by the user by entering a number before the command)
2F74RETC9RETurn to CALLer
2F75 - EDITCommand - HACK/INSERT Logic- "HED"
2F75-2F76- ↳ HEDLD (HL),00H36 00Set the line end to be the current position.
2F77LD C,B48Load Register C with the character position in Register B which will now be the line length
2F78-2F79- ↳ XEDLD D,0FFH16 FFPrepare for the next CALL to find the end of the line by loading Register D with the number of times to perform the operation
2F7D-2F7F- ↳ IEDGo get the character to be inserted from the keyboard
2F80OR AB7Check to see if a key was pressed
2F84-2F85CP 08HFE 08Check 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-2F87Jump to 2F92H if the character in Register A is a backspace character
2F88-2F89CP 0DHFE 0DCheck 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-2F8CJump to 2FE0H if the character in Register A is a carriage return
2F8D-2F8ECP 1BHFE 1BCheck 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.
2F8FRET ZC8Return if the character in Register A is shift up arrow
2F92 - EDITCommand - BACKSPACE CURSOR Logic- "TYPARW".
2F92-2F93- ↳ TYPARWLD A,08H3E 08Load Register A with a backspace the cursor character
2F94- ↳ TYPAR1DEC B05Decrement the character position in Register B
2F95INC B04Bump the character position in Register B
2F96-2F97If this is the first character of the BASIC line Jump forward to 2FB7H
2F9BDEC HL2BDecrement the value of the input buffer pointer in HL
2F9CDEC B05Decrement the character position in Register B
2FA0PUSH DED5Save 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- ↳ DELCHRPUSH HLE5Save the value of the input buffer pointer in HL to the STACK
2FA2DEC C0DDecrement the character position in Register C
2FA3- ↳ CMPRSSLD A,(HL)7ELoad Register A with the character at the location of the input buffer pointer in HL
2FA4OR AB7Check to see if the character in Register A is an end of the BASIC line character
2FA5SCF37Set the Carry flag to signal that DELCHR was called
2FA6-2FA8If the character in Register A is an end of the BASIC line character then we are done compressing so Jump to 0890H
2FA9INC HL23Bump the value of the input buffer pointer in HL
2FAALD A,(HL)7ELoad Register A with the character at the location of the input buffer pointer in HL
2FABDEC HL2BDecrement the value of the input buffer pointer in HL
2FACLD (HL),A77Save the character in Register A at the location of the current input buffer pointer in HL
2FADINC HL23Bump the value of the input buffer pointer in HL
2FB0 - EDITCommand - ADD A CHARACTER Logic- "NTARRW".
2FB0- ↳ NTARRWPUSH AFF5Save the character to be inserted in Register A to the STACK
2FB1LD A,C79Load Register A with the number of characters in the input buffer (i.e., the length of the line) in Register C
2FB2-2FB3CP FFHCP BUFLENFE FFWe 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-2FB5Jump forward to 2FB9H if the maximum BASIC line length hasn't been reached
2FB6POP AFF1Get the character to be inserted from the STACK and put it in Register A
2FB9- ↳ OKINSSUB B90Subtract 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
2FBAINC C0CBump the number of characters in the input buffer in Register C
2FBBINC B04Bump the character position in Register B
2FBCPUSH BCC5Save the character position and the number of characters in the input buffer in BC to the STACK
2FBDEX DE,HLEBLoad DE with the input buffer pointer in HL
2FBELD L,A6FLoad Register L with the number of bytes to move
2FBF-2FC0LD H,00H26 00Zero Register H so that the number of bytes to move can be done in a 16 bit Register Pair.
2FC1ADD HL,DE19Add the value of the input buffer pointer in DE to the character count in HL
2FC2LD B,H44Load Register B with the MSB of the end of the BASIC line pointer in Register H
2FC3LD C,L4DLoad Register C with the LSB of the end of the BASIC line pointer in Register L
2FC4INC HL23Bump the value of the end of the BASIC line pointer in HL
2FC8POP BCC1Get the character position and the number of characters in the input buffer from the STACK and put it in BC
2FC9POP AFF1Get the character to be inserted from the STACK and put it in Register A
2FCALD (HL),A77Save the character in Register A at the location of the current input buffer pointer in HL
2FCEINC HL23Bump the value of the input buffer pointer in HL
2FD2 - EDITCommand - BACKSPACE Logic- "DELED".
2FD2- ↳ DELEDLD A,B78Top 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
2FD3OR AB7Check to see if this is the start of the BASIC line
2FD4RET ZC8Return if this is the start of the BASIC line
2FD5DEC B05Decrement the character position in Register B
2FD6DEC HL2BDecrement the value of the buffer pointer in HL
2FD7-2FD8LD A,08H3E 08Load Register A with a backspace the cursor character
2FDCDEC D15Decrement the number of times to perform the operation in Register D
2FDFRETC9RETurn to CALLer
2FE0-2FE2- ↳ CREDSince 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
2FE6POP BCC1Clean up the STACK (to remove the DISPED return address)
2FE7POP DED1Get the BASIC line number (in binary) from the STACK and put it in DE
2FE8LD A,D7ALoad Register A with the MSB of the BASIC line number in Register D
2FE9AND EA3Combine the LSB of the BASIC line number in Register E with the MSB of the BASIC line number in Register A
2FEAINC A3CBump the combined BASIC line number in Register A
2FEB-2FED- ↳ EDITRTLD HL,(40A7H)LD HL,(BUFPNT)2A A7 40Load HL with the starting address of the input buffer.
Note: 40A7H-40A8H holds the input Buffer pointer
2FEEDEC HL2BDecrement the value of the input buffer pointer in HL
2FEFRET ZC8Return if this is the Level II BASIC command mode
2FF0SCF37Set the Carry flag to to fool the INSERT code; this flags the line number has having been seen
2FF1INC HL23Bump the value of the input buffer pointer in HL
2FF2PUSH AFF5Save the command mode flag in AF to the STACK
2FF6 - EDITCommand - QUIT Logic- "QED".
3000 - Jump Table.
3012 - Model 4 ROM Gen 1
3012 - Model 4 ROM Gen 2
*3024 - Model 4 ROM Gen 1
*3027RET
NOP
NOPI/O Re-Router was removed from the Model 4 ROM.
*3024 - Model 4 ROM Gen 2
*302D - Model 4 ROM Gen 1
*302D - Model 4 ROM Gen 2
*302DJP 375CHC3 5C 37Jump to 375CH for a routine which parses whether the current instruction on a the current line is in quotes.
3036Jump to 35A0H to put the TIME 10 characters from the upper right hand corner of the screen.
3039IN A,(0E4H)Poll Port E4H into A.
NOTE: Port E4H is the Non-Maskable Interrupt Latch.
303BBIT 5,ATest Bit 5 of Register A. Bit 5 of the NMI on an Input test is the RESET STATUS, with 0=False, and 1=True
*3042 - Model 4 ROM Gen 1 - Prompt the User to set the cassette baud rate.
*3042 - Model 4 ROM Gen 2 - Prompt the User to set the cassette baud rate.
*3045-3064 - Model 4 ROM Gen 1
*3045-305F@abcdefghijklmnopqrstuvwxyzKeyboard Rows 0-3, Unshifted, No Caps Lock
*3060NOPComputer version number, which is always 1 for a Model III and 0 for a Model 4
*3061NOP
*3062NOP
*3063NOP
*3064NOP
*3065-307D30 31 32 33 34 35 36 37 38 39 3A 3B 2C 2D 2E 2F 0D 1F 01 5B 0A 08 09 20 00Keyboard Rows 4-6, Unshifted, No Caps Lock
*307ENOP
*307FNOP
*3080NOP
*3081NOP
*3082NOP
*3083NOP
*3084NOP
*3085-309F60 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 5AKeyboard Rows 0-3, shifted, No Caps Lock
*30A0NOP
*30A1NOP
*30A2NOP
*30A3NOP
*30A4NOP
*30A5NOP
*30A6-30BC21 22 23 24 25 26 27 28 29 2A 2B 3C 3D 3E 3F 0D 1F 01 1B 1A 18 19 20Keyboard Rows 4-6, shifted, No Caps Lock
*30BDNOP
*30BENOP
*30BFNOP
*30C0NOP
*30C1NOP
*30C2NOP
*30C3NOP
*30C4NOP
*30C5-30DF40 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 5AKeyboard Rows 0-3, UNshifted, Caps Lock.
*30E0NOP
*30E1NOP
*30E2NOP
*30E3NOP
*30E4NOP
*30E5-30FC30 31 32 33 34 35 36 37 38 39 3A 3B 2C 2D 2E 2F 0D 1F 01 5B 0A 08 09 20Keyboard Rows 4-6, UNshifted, Caps Lock
*30FDNOP
*30FENOP
*30FFNOP
*3100NOP
*3101NOP
*3102NOP
*3103NOP
*3104NOP
*3105H - Model 4 Gen 1 jump to NON-Disk BASIC
GOSUB to the $SETCAS routine which prompts the user to set the cassette baud rate (310BH - 313AH)
*310BH - Model 4 Gen 1 Set the TAPE BAUD RATE ($SETCAS).
*310CGOSUB to 312DH which loads A with a carrage return, and jumps to 0033H to display it.
*3115GOSUB to 0049H.
NOTE: 0049H is the $KBWAIT routine which scans the keyboard and returns with the key pressed, if any, in register A.
*3118CP 0DHCompare A and 0D (a CARRIAGE RETURN).
*311CPUSH AFSave AF to the STACK.
NOTE: A currently holds the character pressed in response to the "CASS?" message.
*311DGOSUB to 0033H.
NOTE: 0033H is the character print routine, to put the character held in Register A at the current cursor position.
*3120POP AFRestore AF from the STACK.
NOTE: A will then hold the character pressed in response to the "CASS?" message.
*3121CP 48HCompare A with 48H (ASCII: H).
*3125CP 4CHCompare A with a 4CH (ASCII: L).
3129H - Model 4 Gen 1 Set the Selected Cassette Baud Rate as LOW SPEED
*3129XOR ASet 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
LD (4211H),ASave 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
*312FDisplay 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
Finish the cassette setup by calling a portion of the keyboard scan routine which CALLS the screen print routine at 34FDH
*313EXOR AClear Register A and All Flags
*313FRETReturn a null character.
*3140NOP
*3141NOP
*3142NOP
*3143NOP
*3144NOP
*3145 - Model 4 Gen 1 Printer Character Table Codes 32-127.
*3145-31A420 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.
*3045JP 378DHC3 8D 37Jump 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
*3048JP 377AHC3 7A 37Jump to 377AH to check to see if we are on a new printable page and set the pointers accordingly. CALLED from 0431 and 0445
*3045 - Model 4 ROM Gen 2 Keyboard Rows 0-3, Unshifted, No Caps Lock.
*304E-3068@abcdefghijklmnopqrstuvwxyz
*3069NOP
*306ANOP
*306BNOP
*306CNOP
*306DNOP
*306E-308530 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.
*3086IN A,(0EAH)DB EAPoll the RS-232 UART Control Register and Status Register (at Port EAH) and put the results into Register A
*3087OR AB7Set the FLAGS based on the RS-232 UART Control Register and Status Register
*308C-308DJR 30C6H18 38If 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.
**3086LD A,(3810H)3A 10 38Fetch the Keyboard Matrix 0-7 into Register A to check for certain keys.
**3089AND 10HE6 10MASK 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.
**306CNOP
**306DNOP
**306DNOP
*308E-30A8 - Model 4 ROM Gen 2 - Continuing with the Keyboard Table
*308E-30A860 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
*30A9NOP
*30AANOP
*30ABNOP
*30ACNOP
*30ADNOP
*30AENOP
*30AF-30C521 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-30C7IN A,(0EBH)DB EBPoll the RS-232 Register at Port EBH and put the results into Register A.
*30C8RETC9
**30C6-30C8 - Model 4 ROM Student Network Edition - A little code snippet
*30C6NOP
*30C7NOP
*30C8NOP
*3089 - Model 4 ROM Gen 2 - Continuing with the Keyboard Table
*30C9NOP
*30CA-30CDPUSH BC
POP BC
NOP
RETStandard code for a short delay
*30CE@ABCDEFGHIJKLMNOPQRSTUVWXYZ
*30E9NOP
*30EANOP
*30EBNOP
*30ECNOP
*30EDNOP
*30EE30 31 32 33 34 35 36 37 38 39 3A 3B 2C 2D 2E 2F 0D 1F 01 5B 0A 08 09 20Keyboard Rows 4-6, UNshifted, Caps Lock
*3106LD A,(3880H)3A 80 38Load A with the value held at 3880H (which are the SHIFT KEYS)
*3109LD HL,414FH21 F4 41Point HL to 414FH
*310CAND 7CHE6 7CMASK the value held in the SHIFT KEY RAM location against 0111 1100 to keep only bits 2, 3, 4, 5, and 6 live
*310EOR AB7OR A against itself to reset the flags
*310FJR Z,3142H28 31If the Z FLAG is set, JUMP to 3142H to continue checking fro special keys
*3111LD E,A5FPut the masked A into E.
*3112XOR (HL)AEToggle against the old image.
*3113LD (HL),E73Save the new image into (HL).
*3114AND EA3Mask Register E against Register A.
*3115JR Z,3157H28 40If ZERO then the LEFT SHIFT PRESSED was JUMP to 3157H to restart parsing the keyboard
*3117LD BC,05C4H01 C4 05Load BC with 05C4H to set up a 1/50 second delay for de-bounce.
*311ACALL 0060HCD 60 00GOSUB 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.
*311DLD A,(3880H)3A 80 38Load A with the value held at 3880H (which are the SHIFT KEYS)
*3120AND 7CHE6 7CMASK the value held in the SHIFT KEY RAM location against 0111 1100 to keep only bits 2, 3, 4, 5, and 6 live
*3122CP EBBCompare A with E.
*3126RLA17Rotate 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.
*3127RLA17Rotate 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.
*3128JR NC,312EH30 04We 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
*312ALD A,(41F3H)3A F3 41Load Register A with the value of the character to be returned when the F3 key is pressed
*312DRETC9RETurn to Caller
*312E - Model 4 Gen 2 - KEYBOARD Routine - Check and Process the F2 Key or Jump Away.
*312ERLA17Rotate 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.
*312FJR NC,3135H30 04If the NC FLAG is set, it isn't the F2 key, so JUMP to 3135H to test the F1 key
*3131LD A,(41ECH)3A EC 41Load Register A with the value of the character to be returned when the F2 key is pressed
*3134RETC9RETurn to Caller
*3135 - Model 4 Gen 2 - KEYBOARD Routine - Check and Process the F1 Key or Jump Away.
*3135RLA17Rotate 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.
*3136JR NC,313CH30 04If the NC FLAG is set, it isn't the F1 key, so JUMP to 313CH to keep checking special keys
*3138LD A,(41EBH)3A EB 41Load Register A with the value of the character to be returned when the F1 key is pressed
*313BRETC9RETurn to Caller
*33C4 - Model 4 Gen 2 - KEYBOARD Routine - Part of the Keyboard Scan Routine. Keep Checking Special Keys.
*313CRLA17Rotate 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.
*33CA - Model 4 Gen 1 - KEYBOARD Routine - Part of the Keyboard Scan Routine.
*3142LD (HL),A77Save the character held in Register A into the memory location pointed to by Register Pair HL
*3143LD A,FFH3E FFLoad A with FF to set up for a FLAG = 0FFH = NO CONTROL.
*3145LD HL,3840H21 40 38Load HL with 3840H to start a check for a DOWN ARROW.
*3148BIT 4,(HL)CB 66Test BIT 4 of (HL) to check for a DOWN ARROW.
*314AJR Z,3154H28 08JUMP to 3154H if the a DOWN ARROW was NOT pressed.
*314CSLA LCB 25Next we need to check for a a LEFT SHIFT so shift L left.
*314EBIT 0,(HL)CB 46Test BIT 0 of (HL) to check for a LEFT SHIFT.
*3152LD A,1FH3E 1FLoad A with 1F to set up for FLAG = CONTROL KEY.
*3154LD (4224H),A32 24 42Save the CONTROL FLAG into (4224H).
NOTE: 4224H Holds the CONTROL KEY flag.
*315ARET NCD0If that routine exited with NC FLAG set, RETurn
*315ECP 1AHFE 1ACheck A against 1AH to see if we have a SHIFT+DOWN ARROW.
*3163OR AB7Set the flags based on Register A
*3167LD HL,4224H21 24 42Set Register Pair HL to 4224H, which is the CONTROL KEY flag.
*316ABIT 7,(HL)CB 7ETest Bit 7 of the CONTROL KEY flag in RAM.
*316ECP 2AHFE 2ACheck A against a * key.
*3170JP Z,37C7HCA C7 37If 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.
*3173AND (HL)A6Prepare to check for a BREAK key by masking A against the memory contents of HL ...
*3174CP 01HFE 01... and COMPARING it to 01H.
*3176RET NZC0If the result of the compare is NOT zero, then RETURN.
*3177RST 28HEFIf we are here, then a BREAK key was hit, so call RST 28H to handle the BREAK key.
*3178RETC9RETURN to Caller
*3179 - Model 4 Gen 2 - Continuing Initialization Routine by setting up the RS-232.
*3179XOR AAFSet Register A to 0
*317AOUT (E8H),AD3 E8Output A to port E8H.
NOTE: Port E8H is the RS-232 Status Register & Master Reset. Outputting ANYTHING to Port E8H resets the RS-232.
*317CLD A,EEH3E EESet Register A to EEH for outputting to the RS-232 Baud Rate Select and Sense Switches: (Port E9H)
*317EOUT (E9H),AD3 E9Initialize 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
*3180LD A,6DH3E 6DSet Register A to 6DH for outputting to the RS-232 UART Control Register and Status Registe (Port EAH)
*3182OUT (EAH),AD3 EASent 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)
*3184IN A,(E8H)DB E8Poll the RS-232 Status Register and Master Reset of Port E8H and put the value into Register A.
*3186BIT 6,ACB 77Test Bit 6 of the RS-232 Status Register to check Data Set Ready (Pin 6 of the DB-25).
*3188JR Z,3184H28 FAIf the DATA SET READY is ZERO then LOP back 2 instructions to 3184 to poll again
*318ALD A,6CH3E 6CSet Register A to 6CH (0110 1100) for outputting to the RS-232 UART Control Register and Status Registe (Port EAH)
*318COUT (EAH),AD3 EASend 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
*318EIN A,(E8H)DB E8Poll the RS-232 Status Register and Master Reset of Port E8H and put the value into Register A.
*3190BIT 6,ACB 77Test Bit 6 of the RS-232 Status Register to check Data Set Ready (Pin 6 of the DB-25).
*3192JR NZ,318EH20 FAIf the DATA SET READY is ZERO then LOOP back 2 instructions to 318EH to poll again
*3194LD A,0FH3E 0FSet Register A to 0FH
*3196CALL 37D5HCD D5 37GOSUB to 37D5H to send the Character in Register A to the RS-232, once the RS-232 shows ready to accept that character.
*3199CALL 3086HCD 86 30GOSUB to 3086 to Poll the UART and wait for the P FLAG to not be set and then CONTINUE at 306CH
*319CCALL 3086HCD 86 30GOSUB to 3086 to Poll the UART and wait for the P FLAG to not be set and then CONTINUE at 306CH
*319FCALL 37D5HCD D5 37GOSUB to 37D5H to send the Character in Register A to the RS-232, once the RS-232 shows ready to accept that character.
*31A2-31A4JP 3517HC3 17 35JUMP 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
**3179LD A,(4210H)3A 10 42Get 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.
**317COR 10HF6 10OR against 10H (0001 0000) to turn all I/O ports on
**317ELD (4210H),A32 10 42Put the masked status back, first by loading it into 4210H
**3181OUT (ECH),AD3 EC... and then by sending it to 0ECH which is the same as 04210.
**3183LD A,08H3E 08Put an 08H into Register A
**3185OUT (D3H),AD3 D3Send 08H to Port D3H, which is the Network 4 Omninet MSB pointer
**3187XOR AAFPut an 00H into Register A
**3188OUT (D1H),AD3 D1Send 08H to Port D1H, which is the Network 4 Omninet LSB pointer
**318ALD BC,00D0H01 D0 00In 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
**318DLD HL,7000H21 00 70In further preparation for the INIR commands, point HL to the BUFFER for the code, which is 7000H
**3190PUSH HLE5Save the 7000H Buffer start point to the top of the stack
**3191INIRED B2Write a byte from port C to the memory location pointed to by HL, and then increase HL and decrease B.
**3193INIRED B2Write a byte from port C to the memory location pointed to by HL, and then increase HL and decrease B.
**3195INIRED B2Write a byte from port C to the memory location pointed to by HL, and then increase HL and decrease B.
**3197INIRED B2Write a byte from port C to the memory location pointed to by HL, and then increase HL and decrease B.
**3199INIRED B2Write a byte from port C to the memory location pointed to by HL, and then increase HL and decrease B.
**319BINIRED B2Write a byte from port C to the memory location pointed to by HL, and then increase HL and decrease B.
**319DINIRED B2Write a byte from port C to the memory location pointed to by HL, and then increase HL and decrease B.
**319FINIRED B2Write a byte from port C to the memory location pointed to by HL, and then increase HL and decrease B.
**31A1RETC9RETurn to CALLer
**31A2NOP00
**31A3NOP00
**31A4NOP00
31A5 - Output the TIMING MARK to the cassette
31A5LD A,01HLoad A with 01H. This is to prepare to send 0.46V to tape.
31A7OUT (0FFH),ALoad 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.
31A9LD B,0DHLoad B with 0DH in as a loop counter.
31ADLD A,02HLoad A with 01H. This is to prepare to send 0.0V to tape.
31AFOUT (0FFH),ALoad 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.
31B1LD B,0DHLoad B with 0DH in as a delay.
31B5GOSUB to 31F3 to send 0.46V to tape.
NOTE: 31F3H resets the cassette port, and then output a 0 to the Cassette Port FFH.
31B8LD B,78HLoad B with 78H in as a delay.
31BCRETRETURN.
31C0 - Turn Off The Cassette
31C0LD A,(4213H)Load A with the memory contents of 4213H.
NOTE: 4213H is the default interrupt vector setting for the cassette.
31C3OUT (0E0H),AOutput 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
31C5IN 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.
31C7LD 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.
31CAAND FDHMask A against FDH (1111 1101) to zero bit 1.
31CFEIEnable Interrupts.
31D0RETRETURN.
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 "**"
31D1EX DE,HLSwap DE and HL to remove the return address.
31D2EX (SP),HLSwap the memory contents pointed to by the STACK POINTER and HL (which is now what DE was).
31D3PUSH BCSave BC to the STACK.
31D4PUSH HLSave HL to the STACK.
31D5EX DE,HLSwap DE and HL back.
31D6IN 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.
31D8LD DE," "Load DE with SPACESPACE.
31DBLD (3C3EH),DELoad the screen memory location of 3C3EH with DE.
31E8 - Turn On The Cassette - Actually Set the Bit Mask and Output the Command
31E8LD 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.
31EBOR 02HOR A with 02H (0000 0010) to set Bit 1.
31EDLD (4210H),ASave 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.
31F0OUT (0ECH),AOutput that Bit Mask to port 0ECH.
31F2RETRETURN.
31F3 - Reset the Cassette Port. This routine OUTputs a 0 to the Cassette Port FFH
31F3XOR AWe want to reset the cassette port so we zero A.
31F4OUT (0FFH),ALoad 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.
31F6RETRETURN.
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.
31F7LD A,(HL)Load Register A with the memory contents pointed to by Register Pair HL.
31F8SUB 23HSubtract 23H so that we can test to see if the caller was a PRINT #command.
31FAIf 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.
3200RST 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.
3202RETIf we are here, then we have PRINT #n,so we RETURN.
3203 - Vector for a SLOW cassette read
3203LD B,08HLoad B with 8, representing the need to LOOP for 8 bits.
3205GOSUB to 3220H.
NOTE: 3220H reads the tape until it finds a timing mark or the BREAKis hit.
320ALD A,(4212H)Put the contents of 4212H into A.
NOTE: 4212H holds the cassette blinker counter.
320DINC ABump A.
320EAND 5FHMask A against 5F (0101 1111) to turn Bit 7 and Bit 5 off.
3210LD (4212H),APut A into 4212H.
NOTE: 4212H holds the cassette blinker counter.
3215LD A,(3C3FH)Load A with the screen contents at position 3C3FH.
3218XOR 0AHXOR 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).
321ALD (3C3FH),APut the revised A onto the screen at position 3C3FH.
321DLD A,DPut D (the byte) into A.
3220 - Cassette - Keep reading tape looking for a timing mark or BREAK.
3220PUSH BCSave BC to the STACK.
3221IN A,(FFH)Poll Port FFH with the results into Register A.
NOTE: FFH is the Cassette Port.
3223RLAPut 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).
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)
322ELD B,6EHLoad B with 6EH, which is the length of the timing mark.
Reset the cassette port
Next, wait for 98H Units (the length until a data pulse is expected)
3235LD B,98HLoad B with 98H, which is when the next data pulse should be available.
... continue
3239IN A,(FFH)Poll Port FFH and put the results into A.
NOTE: Port FFH is the Cassette Port.
323BPOP BCRestore BC from the STACK.
323CRLARotate 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.
323DRL DRotate 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.
3241 - Vector for a SLOW cassette write. On entry A is the byte to output.
3241PUSH AFSave AF to the STACK.
3242PUSH BCSave BC to the STACK.
3243PUSH DESave DE to the STACK.
3244LD C,08HLoad C with an 8, representing 8 bits to be written.
3246LD D,ALoad D with A.
NOTE: D will be the DATA BYTE.
324ARLC DRotate 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.
324CIf 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.
3251DEC CReduce the counter holding the number of bits to deal with by 1.
3254POP DERestore DE from the STACK.
3255POP BCRestore BC from the STACK.
3256POP AFRestore AF from the STACK.
3257RETRETURN.
3258 - "Write a 0 Bit" by simply waiting the appropriate amount of time and doing nothing.
325E - SLOW tape header write
325EPUSH HLSave HL to the STACK.
3262LD (420CH),HLLoad the memory contents of 420CH with HL.
NOTE: 420CH is the TAPE WRITE VECTOR.
3265LD B,53HLoad B with 53H (Decimal: 83) in prepartion to output 83 ZEROes.
3267XOR AClear A and all flags.
326DLD A,0A5HLoad A with A5H.
NOTE: A5H is the OUTPUT SYNC BYTE.
3274 - SLOW tape header read
3274PUSH HLSave HL to the STACK.
3278LD (420EH),HLPut the vector for a slow cassette read into the memory location at 420EH.
NOTE: 420EH is the TAPE READ VECTOR.
327BLD B,40HLoad B with 40H to set up a loop of 64 to try to find 64 zeroes.
327DLD D,00HLoad D with 0.
3282LD A,DLoad A with the D (the data byte) to begin to check the current data byte.
3283OR ASet up the flags.
3288GOSUB to 3220H.
NOTE: 3220H reads the tape until it finds a timing mark or the BREAKis hit.
328BLD A,DLoad A with the D (the data byte) to begin to check the current data byte.
328CCP 0A5HCompare A to A5H looking for a SYNC BYTE.
3290LD HL,"**"In preparation to display a "**", load HL with **.
3293LD (3C3EH),HLPut HL onto the screen at location 3C3EH.
3296LD A,HLoad A with H (which is a *.
3297POP HLRestore HL from the STACK.
3298POP BCRestore BC from the STACK.
3299POP DERestore DE from the STACK.
329ARETRETURN.
329B - FAST tape header write.
329BPUSH HLSave HL to the STACK.
329FLD (420CH),HLLoad the memory contents of 420CH with HL.
NOTE: 420CH is the TAPE WRITE VECTOR.
32A2LD B,00HLoad B with 00H to set up a loop of 256 to output 256 "55H" bytes.
32A4LD A,55HLoad A with "55H".
32A6GOSUB to 32B4H.
NOTE: 32B4 restore all registers from the STACK, and Fill C with A, and JUMP to cassette write
32ABLD A,7FHLoad A with 7FH.
NOTE: 7FH is the OUTPUT SYNC BYTE.
32ADGOSUB to 32B4H.
NOTE: 32B4 restore all registers from the STACK, and Fill C with A, and JUMP to cassette write
32B0LD A,A5HLoad A with A5H.
NOTE: A5H is the SLOW SYNC BYTE.
32B4 - Restore all registers from the STACK, and Fill C with A, and JUMP to cassette write.
32B4PUSH AFSave AF to the STACK.
32B5PUSH BCSave BC to the STACK.
32B6PUSH DESave DE to the STACK.
32B7LD C,ALoad C with A.
32BA - Save all registers to the STACK, and Fill C with A, GOSBUB to write out the START BIT ...
32BAPUSH AFSave AF to the STACK.
32BBPUSH BCSave BC to the STACK.
32BCPUSH DESave DE to the STACK.
32BDLD C,ALoad C with A.
32C1 - Call 3335H to Output a Bit 8 Times
32C1LD B,08HLoad B with an 8 to set up a loop for 8 bits.
32CA - Read the start bit, read 8 bits, check for error, and flash the star
32CDLD B,08HLoad B with an 8 to set up a loop for 8 bits.
32DA - FAST tape header read.
32DAPUSH HLSave HL to the STACK.
32DELD (420EH),HLLoad the TAPE READ VECTOR (memory location of 420EH) with the 32CAH.
32E1LD A,01HLoad A with a 1 to set the interrupt.
32E3OUT (0E0H),AOUTPUT 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
32E5LD B,80HSet up a loop of 80H (128) to try to read 128 bits.
32EALD A,CLoad A with C (which is the pulse width).
32EBCP 0FHCompare A to 0FH to see if the pulse width was too short.
32EFCP 3EHCompare A to 3EH to see if the pulse width was too long.
32F8LD B,40HSet up a loop of 40H (Decimal: 64) to try to read 64 bits.
3300LD D,CLoad D with C (which holds the delay count).
3304LD A,DLoad A with D to set up to find the difference in the delays.
3305SUB CSubtract C (the delay count) from A (which holds D).
3308NEGA = 0 - A.
330ACP 0DHCompare 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.
330CIf the CARRY FLAG is set, JUMP to 3313H which will bump L and continue the 64 bit loop.
330EINC HBump HL since we have one more zero bit.
3313INC LBump L since we have one more bit.
3316LD A,40HLoad A with 40H (64).
3318CP HCompare A with H to check for bits.
331BCP LCompare with A to check for one bits.
331ELD A,02HLoad A with 2 to set the interrupt vector.
3320OUT (0E0H),AOutput 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
3325LD D,00HZero D.
332DLD A,DLoad A with D (the read byte).
332ECP 7FHCompare A against 7F to check for a MARKER BYTE.
3335RLC CWe 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.
3341DEC DDecrement D as DELAY #1.
3344LD A,02HLoad A with a 2 to set up for a write of 0 VOLTS to TAPE.
3346OUT (0FFH),ALoad 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.
3348DEC EDecrement E as DELAY #2.
334BLD A,01HLoad A with 01H to prepare to send 0.85 VOLTS to TAPE.
334DOUT (0FFH),ALoad 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.
334FRETRETURN.
3350H - READ a BIT
3350EIEnable Interrupts.
3351LD C,00HLoad C with 0.
3353INC CBump C.
3354LD A,(3840H)Load A with the contents of 3840H so as to check for a BREAK.
3357AND 04HMask A with 4 (0000 0100).
335BDIDisable Interrupts.
335FLD (3C3EH),HLPut the "BK" in HL onto the video screen at 3C3EH.
3362JUMP 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
3365LD E,01HLoad E with 01H (Binary: 0000 0001) to make the bit go high.
3369LD E,00HLoad E with 0 to make the bit go low.
336BLD A,06HLoad A with 6.
336DADD A,CAdd A (6) to C, so COUNT = COUNT + 6.
336ELD C,APut the count held in A back into C.
336FIN A,(FFH)Poll FFH (to get the level) into A.
NOTE: FFH is the Cassette Port.
3371AND 01HMask A with 1 (0000 0001) to keep only Bit 0.
3373CP ECompare A with E (which was the set level).
3376POP AFRestore AF from the STACK.
3377POP AFRestore AF (the REMOTE CALLER'S ADDRESS) from the STACK.
3378RETRETURN to the caller's caller.
3379POP AFRestore AF from the STACK.
337AEIEnable Interrupts.
337BRETRETURN back top the loop.
337CH - Check for a Data Error.
337CLD A,CLoad A with C (the count).
337DCP 22HCompare 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
337FRL DWe 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.
3381CP 0FHMake 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.
3383If A < 0FH then it was too quick and we have a data error so JUMP forward a few instructions 3388H.
3385CP 3EHCompare A against 3EH to make sure it was not too slow.
3387RET CIf it wasn't too slow, RETURN.
3388LD A,44HIt was too slow, so load A with a D.
338ALD (3C3EH),APut the "D" on the screen at video location 3C3EH.
338DRETRETURN.
*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).
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).
*3391LD A,(3880H)Load A with the value held at 3880H (which are the SHIFT KEYS)
*3394AND 7CHMASK the value held in the SHIFT KEY RAM location against 0111 1100 to keep only bits 2, 3, 4, 5, and 6 live
*3396OR AOR A against itself to reset the flags
*3399LD E,APut the masked A into E.
*339AXOR (HL)Toggle against the old image.
*339BLD (HL),ESave the new image into (HL).
*339CAND EMask Register E against Rgister A.
*33A2GOSUB 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.
*33A5LD A,(3880H)Load A with the value held at 3880H (which are the SHIFT KEYS)
*33A8AND 7CHMASK the value held in the SHIFT KEY RAM location against 0111 1100 to keep only bits 2, 3, 4, 5, and 6 live
*33AACP ECompare A with E.
*33AERLARotate 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.
*33AFRLARotate 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.
*33B0We 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
*33B2LD A,(41F3H)Load Register A with the value of the character to be returned when the F3key is pressed
*33B5RETRETurn to Caller
*33B6 - Model 4 Gen 1 - KEYBOARD Routine - Check and Process the F2 Key or Jump Away.
RLARotate 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.
*33B9LD A,(41ECH)Load Register A with the value of the character to be returned when the F2key is pressed
*33BCRETRETurn to Caller
*33B6 - Model 4 Gen 1 - KEYBOARD Routine - Check and Process the F1 Key or Jump Away.
RLARotate 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.
*33BEIf the NC FLAG is set, it isn't the F1, so JUMP to 33C4 to keep checking special keys
*33C0LD A,(41EBH)Load Register A with the value of the character to be returned when the F1key is pressed
*33C3RETRETurn to Caller
*33C4 - Model 4 Gen 1 - KEYBOARD Routine - Part of the Keyboard Scan Routine. Keep Checking Special Keys.
RLARotate 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.
*33CA - Model 4 Gen 1 - KEYBOARD Routine - Part of the Keyboard Scan Routine.
LD (HL),ASave the character held in Register A into the memory location pointed to by Register Pair HL
33CBLD A,FFHLoad A with FF to set up for a FLAG = 0FFH = NO CONTROL.
33D0BIT 4,(HL)Test BIT 4 of (HL) to check for a DOWN ARROW.
33D4SLA LNext we need to check for a a LEFT SHIFTso shift L left.
33D6BIT 00H,(HL)Test BIT 0 of (HL) to check for a LEFT SHIFT.
33DALD A,1FHLoad A with 1F to set up for FLAG = CONTROL KEY.
33DCLD (4224H),ASave the CONTROL FLAG into (4224H).
NOTE: 4224H Holds the CONTROL KEY flag.
*33E2RET NCIf that routine exited with NC FLAG set, RETurn
33E6CP 1AHCheck A against 1AH to see if we have a SHIFT DOWN ARROW.
*33EBOR ASet the flags based on Register A
*33F2BIT 7,(HL)Test Bit 7 of the CONTROL KEY flag in RAM.
*33F6CP 2AHCheck A against a *.
*33FBAND (HL)Prepare to check for a BREAKby masking A against the memory contents of HL ...
*33FERET NZIf the result of the compare is NOT zero, then RETURN.
*33FFRST 28HIf we are here, then a BREAKwas hit, so call RST 28H to handle the BREAK.
*3400RETRETURN to Caller
*3401 - Model 4 Gen 1 - This is the BOOTSTRAP. Clears ports, checks for a BREAKkey and a Floppy Controller.
3401IM 1Set the INTERRUPT MODE to 1.
*3406LD B,0FHLet Register B = 0FH
*3408LD C,88HLet Register C = the CRT Controller Control Register Port
*340COUT (89H),ASend Register A to the CRT Controller Data Register
3410OUT (0E4H),AOutput A to Port E4H.
NOTE: Port E4H is the non-maskable interrupt latch. This is to clear the non-maskable interrupt status.
3412OR 20HOR 20H (0010 0000) against A to turn on Bit 5.
3414OUT (0ECH),AOutput 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.
3416LD A,81HLoad A with 81H (Decimal 129, Binary 10000001).
3418OUT (0F4H),AOutput 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.
341ALD A,D0HLoad A with D0H (Decimal: 208, Binary: 1101 0000).
341COUT (0F0H),AOutput "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
*341EPUSH BCUndertake a short delay of PUSHING BC, POPPING BC, and NOPing
*341FPOP BC
*3420NOP
3421LD A,04HLoad A with a 4 (Binary: 0000 0100).
3423OUT (0E0H),AOutput 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
3425LD A,0BHLoad A with 0BH (Binary: 00001011).
3427OUT (0F0H),AOutput 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).
3429Next we need to initialize some ports via a LDIR. The next 4 commands will move the 76 (4C) bytes from 36AAH to 4000H.
342C
342F
3432LDIR
3434Next we need to initialize more ports via a LDIR. The next 4 commands will move the 64 (40) bytes from 36F9H to 41E5H.
3437
343A
343DLDIR
3448IN 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
344AINC ABump the FDC Status by 1 (which would turn a FFH (128) into a 00H), making a Z flag mean "Disk Controller Not Ready".
344BIf the FDC Stats + 1 is zero, then we have no disk controller, so jump to Non-Disk BASIC at 3105H.
344EIf we are here, then we have a floppy controller, so set up for a loop of 65,535 times.
3451DEC BCDecrease BC by 1.
3452LD A,81HSet A to 81H (Decimal: 10000001).
3454OUT (0F4H),ASend 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.
3456LD A,B
OR CTo 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.
345BIN 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
345DBIT 2,ATest Bit 2 of A (the Floppy Status). If it is 0 then we haven't made it to track 0, otherwise we have.
345FStill 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.
3461LD E,05HSet up for a loop of 5 using Register E.
3466IN 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
3468BIT 01H,ACheck A to see if bit 1 (meaning "Drive Busy") is set.
346CDEC BCIf bit 1 is not set (meaning, the drive is NOT busy), reduce the counter (BC) by 1.
346DLD A,81HLoad A with 81H (Decimal: 129, Binary: 1000 0001).
346FOUT (0F4H),AOutput 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.
3471LD A,B
OR CTo 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.
3475If 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.
347BIf 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.
347DDEC EIf we are here, it found a diskette so next we need to find the index mark. First decrement E by 1.
3463HLD A,81HLoad A with 81H (Decimal: 129, Binary: 1000 0001).
3482OUT (0F4H),AOutput 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.
3484We 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.
3487LD (404AH),HL
348ALD A,C3HLoad A with C3H (Decimal: 195, Binary 1100 0011).
348CLD (4049H),APut the C3H into 4049H.
NOTE: 4049H is the Non-Maskable Interrupt Vector.
348FLD A,80HLoad A with 80H (Decimal: 128, Binary 1000 0000).
3491OUT (0E4H),AOutput "1000 0000" to Port E4H.
NOTE: Port E4H is the non-maskable interrupt latch.
3499LD A,01HPrepare to read Sector 1 by loading A with a 1.
349BOUT (0F2H),AOutput 1 to Port F2H.
NOTE: Port F2H is the Floppy Disk Controller Track Register.
349DLD A,80HPrepare to read a single sector by loading A with 80H (Decimal: 128, Binary: 1000 0000).
349FOUT (0F0H),AOutput "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).
34A1GOSUB to 37E1H to delay 3 instruction cycles, arguably the time it will take for the FDC to respond to the port command.
34A4IN 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
34A6AND 02HMask 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.
34ABINIInput the data byte.
34ADLD A,81HLoad A with 81H (Decimal: 129, Binary: 1000 0001) to select disk 0.
34AFOR 40HOR A with 40H (Binary: 0100 0000) to set A to double-density.
34B1OUT (0F4H),AOutput 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.
34B3INIInput the data byte.
34B5Loop 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.
34B8XOR AClear A and all flags.
34B9OUT (0E4H),ASend a 0 to Port E4H.
NOTE: Port E4H is the non-maskable interrupt latch. This is to clear the non-maskable interrupt status.
34BELD (4049H),HLLoad "45EDH" into 4049H.
NOTE: 4049H is the Non-Maskable Interrupt Vector.
34C1GOSUB to 37E1H to delay 3 instruction cycles, arguably the time it will take for the FDC to respond to the port command.
34C4IN 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
34C6POP HLClean up the Stack.
34C7AND 1CHMask 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.
34CCIf 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.
34CEIf 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.
34D1IN A,(E4H)Poll Port E4H into A.
NOTE: Port E4H is the Non-Maskable Interrupt Latch.
34D3BIT 5,ATest Bit 5 of Port E4 against A.
NOTE: Port E4H is the Non-Maskable Interrupt Latch.
*34DA - Model 4 Gen 1 - Part of the Keyboard routine.
*34DDAND 03HMask the SHIFT Register against 03H (Binary: 0000 0011) to keep only Bits 0 and 1 to check for shifts.
*34DFIf the masked A is 0, then we have no shifts, and skip the next instruction (to 34E3H).
*34E1SET 6,DSet BIT 6 of D to offset D for shifts.
LD A,(4019H)Load A with the contents of memory location 4019H to check for CAPS LOCK.
NOTE: 4019H is the CAPS LOCK TOGGLE.
*34E6OR ASet the flags.
*34E9SET 7,DSet 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.
*34EBLD A,(3880H)Put the contents of memory location 3880H into A to GET SHIFT(S).
*34EEAND 03HMask the SHIFT Register against 03H (Binary: 0000 0011) to keep only Bits 0 and 1 to check for shifts.
*34F0If the masked A is 0, then we have no shifts, and skip the next instruction (to 34F4H).
*34F2RES 7,DRESET 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.
34F7LD E,DWe need DE to be the OFFSET, so load E with D and ...
34F8LD D,00H... load D with 00.
34FAADD HL,DEAdd DE (the offset over the keyboard table) to HL (the keyboard table).
34FBLD A,(HL)Get the character pointed to by (HL) and put it into A.
*34FCRETRETurn 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.
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.
*3501RLCARotate the contents of A left one bit position. The contents of bit 7 are copied to the carry flag and bit 0.
*3504RLCARotate the contents of A left one bit position. The contents of bit 7 are copied to the carry flag and bit 0.
*3505If the CARRY FLAG is set, we have a non-graphic characters, so skip the next instructions.
*3507LD A,2EHOverwrite the current character held in Register A with a ., so that all graphic characters are printed as .'s.
Call the PRINT CHARACTER routine at 003BH (which sends the character in the A register to the printer).
*350FINC HLBump HL to the next character on the screen.
*3510BIT 6,HCheck 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).
*3515LD A,LPrepare to test of end of line by loading Register A with Register L.
*3516AND 03FHAND 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).
*3518If 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.
*351AGOSUB 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.
*351DLoop 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
*351FNOP
*3520NOP
*3521NOP
*3522NOP
*3523NOP
*3524NOP
*3525NOP
*3526NOP
*3527NOP
*3528NOP
*338EH - Model 4 Gen 2 - Jump Point for Keyboard Input.
*338ELD BC,3801H010138Load BC with 3801H (KEYBOARD ROW 0).
*3391LD HL,4036H213640Load HL with 4036H (BUFFER ROW 0).
*3394LD D,00H1600Load D with 0 (so D = ROW 0).
*3396LD A,(BC)0Aoad A with the contents held in (BC) to check the keyboard row.
*3397LD E,A5FLoad E with the contents held in (BC) to check the keyboard row.
*3398XOR (HL)AEXOR (HL) to set changed bits.
*3399LD (HL),E73Save the scan back into (HL).
*339AAND EA3Mask A with E (to mask the released keys).
Go to the next row
*339DINC D14Bump D so that D holds the NEXT row number.
*339EINC HL23Bump HL so that HL holds the NEXT buffer location.
*339FRLC CCB01We 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.
*33A1JP P,3396HF29633If 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
*33A4LD A,(41FDH)3AFD41If 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.
*33A7LD L,A6FLoad L with the contents of Register A.
*33A8LD A,(41FEH)3AFE41Load A with the memory contents of 41FEH.
NOTE: 41FEH is the saved IMAGE at the saved position in the keyboard scan data.
*33ABAND (HL)A6MASK A against (HL) to see if the previous keys are still pressed.
*33AFSBC HL,HLED62Zero HL by subtracting HL from HL.
*33B1LD (4201H),HL220142Load 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.
*33B7LD (41FFH),HL22FF41Load the memory location at 41FFH with 05DCH.
NOTE: 41FFH holds the keyboard scan repeat delay count.
*33BAXOR AAFClear A and all flags.
*33BBRETC9RETurn to Caller
*33BC - Model 4 Gen 2 - Keyboard Routine - If the same keys are still pressed then we need to deal with debounce.
*33BCLD E,A5FLoad E with A.
*33BDPUSH BCC5Save BC to the STACK.
*33C1CALL 0060HCD 60 00GOSUB 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.
*33C4POP BCC1Restore BC from the STACK.
*33C5LD A,(BC)0ALoad A with the memory contents pointed to by BC to re-check the keyboard.
*33C6AND EA3Compare A against E to check the pattern.
*33C7RET ZC8If not the same pattern then RETURN.
*33C8LD (41FEH),A32 FE 41If it is the same pattern then save A into (41FEH).
NOTE: 41FE is the SAVED IMAGE AT POSITION.
*33CBLD A,L7DLoad A with L (the scan position).
*33CCLD (41FDH),A32 FD 41Save A into (41FDH).
NOTE: 41FDH is the SAVED POSITION IN SCAN.
*33CFLD A,D7ALoad A with D (8 * ROW #).
*33D0RLA17Rotate 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.
*33D1RLA17Rotate 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.
*33D2RLA17Rotate 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.
*33D3LD D,A57Copy Register A into Register D
*33D4LD A,E7BCopy Register E into Register A
*33D5RRCA0FRotate A right one bit, with the contents of BIT 0 being put into BOTH the CARRY FLAG and BIT 7. D = 8* ROW # + KEY #.
*33D6RET CD8If the contents of BIT 0 of A was SET, RETurn
*33D7INC D14Bump D so that D holds the NEXT row number.
*33DA - Model 4 Gen 2 - Keyboard Routine.
*33DAPUSH HLE5Save HL to the top of the STACK.
*33DBLD HL,(4201H)2A0142Load HL with the repeat delay counter.
NOTE: 4201H is the REPEAT DELAY COUNTER.
*33DEINC HL23Bump HL.
*33DFLD (4201H),HL220142Load the memory location at 4201H with HL to clear the repeat counter.
NOTE: 4201H is the REPEAT DELAY COUNTER.
*33E2LD DE,(41FFH)ED5BFF41Load DE with the byte stored at 41FFH.
NOTE: 41FFH is the REPEAT DELAY COUNT.
*33E6SBC HL,DEED52Subtract with CARRY DE from HL.
*33E8POP DED1Restore old HL (which is what is in the stack) into DE.
*33ECXOR AAFClear A and all flags.
*33EDLD (DE),A12Put a 0 into the memory location pointed to (DE) to let the key be re-read.
*33EELD (4201H),HL220142Load the memory location at 4201H with HL to clear the repeat counter.
NOTE: 4201H is the REPEAT DELAY COUNTER.
*33F1LD L,96H2E96Load L with 96H to set a fast repeat count.
*33F3LD (41FFH),HL22FF41Save HL into the memory location at 41FFH.
NOTE: 41FFH is the REPEAT DELAY COUNT.
*33F9 - Model 4 Gen 2 - Enter NON-Disk BASIC.
*33FF - Model 4 Gen 2 Routine - Initialization - Process the CASS? Question
*33FFEIFBEnable Interrupts.
*3400CALL 3421HCD 21 34GOSUB to 3421H which loads A with a carrage return, and jumps to 0033H to display it.
*3403LD HL,05D1H21 D1 05Load HL with the address of the "CASS?" message.
*3406CALL 021BHCD 1B 02GOSUB to 021BH.
NOTE: 021BH will display the character at (HL) until a 03H is found.
*3409CALL 0049HCD 49 00GOSUB to 0049H.
NOTE: 0049H is the $KBWAIT routine which scans the keyboard and returns with the key pressed, if any, in register A.
*340CCP 0DHFE 0DCheck to see if Register A is holding a ENTER
*3410PUSH AFF5Save AF to the STACK.
NOTE: Register A currently holds the character pressed in response to the "CASS?" message.
*3411CALL 0033HCD 33 00GOSUB to 0033H pt display the character held in Register A at the current cursor position.
*3414POP AFF1Restore the answer to CASS? from the STACK into Register A.
*3415CP 48HFE 48Compare the answer to the CASS? Prompt held in Register A against with 48H (ASCII: H).
*3419CP 4CHFE 4CCompare the answer to the CASS? Prompt held in Register A against with 4CH (ASCII: L).
Set the flag for LOW SPEED CASSETTE
*341DXOR AAFSet A to 0.
*341ELD (4211H),A32 11 42Save 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
*3421LD A,0DH3E 0DPut a CARRIAGE RETURN into Register A
*3423JP 0033HC3 33 00JUMP 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.
*3426IM 1ED56Set the INTERRUPT MODE to 1.
*3428LD SP,407DH317D40Load the STACK POINTER with 407DH.
*342BLD B,0FH060FLet Register B = 0FH
*342DLD C,88H0E88Let Register C = the CRT Controller Control Register Port
*342FOUT (C),BED41Set the CRT Controller Control Register Port to the contents of B (which are decreasing as the loop progresses)
*3431OUT (89H),AD389Send Register A to the CRT Controller Data Register
*3435OUT (E4H),AD3E4Clear the Non-Maskable Interrupt Latch by sending the contents of Register A to Port E4H
*3437OR 20HF620OR 20H (0010 0000) against A to turn on Bit 5.
*3439OUT (ECH),AD3ECOutput 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.
*343BLD A,81H3E81Load A with 81H (Decimal 129, Binary 10000001).
*343DOUT (F4H),AD3F4Output 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.
*343FLD A,D0H3ED0Load A with D0H (Decimal: 208, Binary: 1101 0000).
*3441OUT (F0H),AD3F0Output "D0H" to the FDC Status Register at Port F0H. This resets the FDC and puts it in mode 1
*3443PUSH BCC5Undertake a short delay of PUSHING BC, POPPING BC, and NOPing
*3444POP BCC1Undertake a short delay of PUSHING BC, POPPING BC, and NOPing
*3445NOP00Undertake a short delay of PUSHING BC, POPPING BC, and NOPing
*3446LD A,04H3E04Load A with a 4 (Binary: 0000 0100).
*3448OUT (E0H),AD3E0Output 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.
*344ALD A,0BH3E0BLoad A with 0BH (Binary: 00001011).
*344COUT (F0H),AD3F0Output 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).
*344ELD HL,36AAH21AA36Next we need to initialize some ports via a LDIR. The next 4 commands will move the 76 (4C) bytes from 36AAH to 4000H.
*3451LD DE,4000H110040Set the LDIR Destination to 4000H
*3454LD BC,004CH014C00Set the number of bytes to move to 4CH
*3457LDIREDB0Move the 76 (4C) bytes from 36AAH to 4000H
*3459LD HL,36F9H21F936Next we need to initialize more ports via a LDIR. The next 4 commands will move the 64 (40) bytes from 36F9H to 41E5H.
*345CLD DE,41E5H11E541Set the LDIR Destination to 41E5H
*345FLD BC,0040H014000Set the number of bytes to move to 40H
*3462LDIREDB0Move the 64 (40) bytes from 36F9H to 41E5H.
The Model 4 ROM Student Network Edition ROM changes that to ...
*346AJP NZ,33F9HC2F933If we have a BREAK key then jump to Non-Disk BASIC at 33F9H.
*346DIN A,(F0H)DBF0We 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
*346FINC A3CBump the FDC Status by 1 (which would turn a FFH (128) into a 00H), making a Z flag mean "Disk Controller Not Ready".
*3470JP Z,33F9HCAF933If the FDC Stats + 1 is zero, then we have no disk controller, so jump to Non-Disk BASIC at 33F9H.
*3473LD BC,0000H010000If we are here, then we have a floppy controller, so set up for a loop of 65,535 times.
*3476DEC BC0BDecrease BC by 1.
*3477LD A,81H3E81Set A to 81H (Decimal: 10000001).
*3479OUT (F4H),AD3F4Send 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.
*347BLD A,B78To 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.
*347COR CB1
*3480IN A,(F0H)DBF0We 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
*3482BIT 2,ACB57Test Bit 2 of A (the Floppy Status). If it is 0 then we haven't made it to track 0, otherwise we have
*3484JR Z,3476H28F0Still 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.
*3486LD E,0AH1E0ASet up for a loop of 10 using Register E; this will be the number of tries to find an index mark.
*3488LD BC,0000H010000Set up for a loop of 65,536 using Register Pair BC.
*348BIN A,(F0H)DBF0Poll 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
*348DBIT 1,ACB4FCheck A to see if bit 1 (meaning "Drive Busy") is set.
*3491CALL 349BHCD9B34If 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.
*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.
*3496DEC E1DIf 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.
*3497JR NZ,34ACH2013So long as we have not run out of the loop of 10 tries, JUMP to 34ACH to find the index mark.
*349B - Model 4 Gen 2 Routine - We have exhausted our tries, so display DISKETTE and keep trying.
*349BDEC BC0BIf bit 1 is not set (meaning, the drive is NOT busy), reduce the counter (BC) by 1.
*349CLD A,81H3E 81Load A with 81H (Decimal: 129, Binary: 1000 0001).
*349EOUT (F4H),AD3 F4Output 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.
*34A0LD A,B78To 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.
*34A1OR CB1
*34A2RET NZC0If BC is not 0, then RETurn
*34A3POP HLE1Clear the STACK
*34A4LD HL,0277H21 77 02If 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.
*34A7CALL 021BHCD 1B 02GOSUB to 021BH. Note; 021BH will display the character at (HL) until a 03H is found.
*34AAJR 3486H18 DAIf 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.
*34ACLD BC,0000H010000Set up for a loop of 65,536 using Register Pair BC.
*34AFIN A,(F0H)DBF0Poll 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
*34B1BIT 1,ACB4FCheck the Floppy Disk Controller Status (held in Register A) to see if bit 1 (meaning "Drive Busy") is set.
*34B3JR Z,34BAH2805If bit 1 is not set (meaning, the drive is NOT busy), JUMP to 34BAH to restart the read attempts.
*34B5CALL 349BHCD9B34If we are here, then the drive was busy, so GOSUB to 349BH to display DISKETTE? and try again
*34B8JR 34AFH18F5JUMP back to the top of this routine and keep looking
*34BA - Model 4 Gen 2 Routine - Finish initializing the floppy disk boot.
*34BADEC E1DIf we are here, it found a diskette so next we need to find the index mark. First decrement E by 1.
We are going to try to read a sector
*34BDLD A,81H3E 81Load A with 81H (Decimal: 129, Binary: 1000 0001).
*34BFOUT (F4H),AD3 F4Output 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.
*34C1LD HL,34F5H21 F5 34We 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.
*34C4LD (404AH),HL22 4A 40Put the desired JUMP address of 34F5H into (404AH)
*34C7LD A,C3H3E C3Load A with C3H (Decimal: 195, Binary 1100 0011).
*34C9LD (4049H),A32 49 40Put the C3H into 4049H.
NOTE: 4049H is the Non-Maskable Interrupt Vector.
*34CCLD A,80H3E 80Load A with 80H (Decimal: 128, Binary 1000 0000).
*34CEOUT (E4H),AD3 E4Output "1000 0000" to the the non-maskable interrupt latch via Port E4H.
*34D0LD BC,00F3H01 F3 00Set BC to 0F3H.
NOTE: Port F3H is the Floppy Disk Controller Data Register.
*34D3LD HL,4300H21 00 43Set HL to 4300H, which is where the data is going to go.
*34D6LD A,01H3E 01Prepare to read Sector 1 by loading A with a 1.
*34D8OUT (F2H),AD3 F2Output 1 to the Floppy Disk Controller Track Register at Port F2H.
*34DALD A,80H3E 80Prepare to read a single sector by loading A with 80H (Decimal: 128, Binary: 1000 0000).
*34DCOUT (F0H),AD3 F0Output "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).
*34DECALL 30CAHCD CA 30GOSUB to 30CAH to delay 3 instruction cycles, arguably the time it will take for the FDC to respond to the port command.
*34E1IN A,(F0H)DB F0Poll 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
*34E3AND 02HE6 02Mask 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.
*34E8INIEDA2Input the data byte.
*34EALD A,81H3E81Load A with 81H (Decimal: 129, Binary: 1000 0001) to select disk 0.
*34ECOR 40HF640OR A with 40H (Binary: 0100 0000) to set A to double-density.
*34EEOUT (F4H),AD3F4Output 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.
*34F0INIEDA2Input the data byte.
*34F2JP 34EAHC3 EA 34Loop 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.
*34F5XOR AAFClear A and all flags.
*34F6OUT (E4H),AD3 E4Send a 0 to the non-maskable interrupt latch via Port E4H. This is to clear the non-maskable interrupt status.
*34F8LD HL,45EDH21 ED 45Now that the NMI jumped here, we need to set a new NMI jump, this time to 45EDH.
*34FBLD (4049H),HL22 49 40Load the destination location into (4049H) which is the Non-Maskable Interrupt Vector.
*34FECALL 30CAHCD CA 30GOSUB to 30CAH to delay 3 instruction cycles, arguably the time it will take for the FDC to respond to the port command.
*3501IN A,(F0H)DB F0Poll 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
*3503POP HLE1Clean up the Stack.
*3504AND 1CHE61CMask 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.
*3509JR 34BDH18B2If 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.
*350BJP NZ,4049HC24940If 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.
*350EIN A,(E4H)DBE4Poll the Non-Maskable Interrupt Latch at Port E4H into A.
*3510BIT 5,ACB6FTest Bit 5 of Port E4 against A.
*3512JR Z,350EH28FALoop back 2 instructions until it is set (i.e., not a zero).
*3517 - Model 4 Gen 2 Routine - Finish up initialization by filling 256 bytes into 4300H and then JUMPing there
*3517OR AB7Set the FLAGS based on Register A
*3518JP NZ,3179HC27931If A is not ZERO then Jump to 3179H to continuing initialization routine by setting up the RS-232
*351BLD HL,4300H210043Set HL to 4300H, which is where the data is going to go.
*351ECALL 3086HCD8630GOSUB to 3086 to Poll the UART and wait for the P FLAG to not be set and then CONTINUE at 306CH
*3521LD (HL),A77Store Register A into the memory location pointed to by Register Pair HL
*3522INC L2CBump Register L by 1
*3523JR NZ,351EH20F9So long as we have not overflowed L, LOOP back to 351EH
*3525JP 37E0HC3E037If 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).
*3528NOP00No Operation
**3517 - Model 4 ROM Student Network Edition - Finish up initialization
**3517CALL 3086HCD8630GOSUB to 3086 to simply poll the keyboard and mask for a "4" key
**351AJR NZ,3520H2004If the key isn't a "4" then restore BC and return to processing as a regular Model 4 would
**351CCALL 028DHCD8D02GOSUB to 028DH to check for a BREAK key.
**351FRETC9RETurn to CALLer
**3520POP BCC1Restore the contents at the top of the STACK into Register Pair BC
**3521JP 3179HC37931Jump to 3179H to continuing initialization routine by setting up the RS-232
**3524NOP00
**3525NOP00
**3526NOP00
**3527NOP00
**3528NOP00
3529H - Deal with the cursor.
352CPUSH DEPush that return address onto the Stack.
352DIN 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.
352FLD 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.
3532OR ASet the flags.
3535LD 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.
3538OR ATest A.
353BLoad HL with 401AH.
NOTE: 401AH is the memory location that stores the cursor blink count.
353EDEC (HL)Reduce the memory contents of (401AH) by one.
NOTE: 401AH is the memory location that stores the cursor blink count.
3541LD (HL),07HSet the cursor blink count to 7.
NOTE: 401AH is the memory location that stores the cursor blink count.
3543INC HLBump HL. This will increase HL from 401AH to 401BH.
NOTE: 401BH holds the cursor blink status - 0 = Off, Anything Else = On.
3544LD A,(HL)Poll the cursor blink status memory location and put the results into A.
3545AND 01HMask A against 0000 0001, to have only Bit 0 active.
3547XOR 01HXOR A with 01H.
3549LD (HL),APut the toggled cursor blink status into the appropriate memory location.
354ALD HL,(4020H)Poll (4020H) and put the result into HL.
NOTE: 4020H holds the current cursor position.
354DIf the current cursor position is 0, then it is off, so JUMP down 2 instructions to 3554H to make the cursor a blank (space).
354FLD A,(4023H)Load A with the memory contents of (4023H).
NOTE: 4023H holds the cursor character.
3552JUMP down to 3556H to skip the next instruction and continue this routine by displaying the character in A.
3554LD A,20HLoad 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.
3556LD (HL),APut the character held in A into the memory location pointed to by (HL).
NOTE: (HL) will hold the current cursor position.
355ADEC (HL)Decrease the number held at (4216H) by 1.
355BRET NZIf the number held at (4216H) is not zero, then RETURN.
355CLD (HL),1EHPut a 1EH (Decimal: 30) into (4216H).
NOTE: 4216H is the heartbeat counter.
355EINC HLBump HL by 1. HL will now point to 4217H, which is the memory location that holds the SECONDS.
355FLoad 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.
3562LD B,03HLoad B with a 03H, to set up a loop where we test seconds, minutes, and hours against their maximums.
3564INC (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.
3565LD 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.
3566SUB (HL)Compare the maximum to what we have by subtracting that maximum from the value pointed at in (HL).
3567RET NZIf there is no difference between what we have and the maximum then RETURN.
3568LD (HL),AIf 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).
3569INC HLBump 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.
356AINC DEBump 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.
356BLoop back to 3564H until the loop of 3 has been met, meaning that we have processed seconds, minutes, and hours.
356DINC HLBump HL one more time, to 421BH.
NOTE: 421BH holds the current DAY portion of the date.
356EINC (HL)Bump the DAY portion of the date.
356FINC HLBump HL one more time, to 421CH.
NOTE: 421BH holds the current MONTH portion of the date.
3570LD A,(HL)Get the month and put it into A.
3571DEC HLDecrease HL back to to 421BH.
NOTE: 421BH holds the DAY portion of the date.
3572DEC ADecrease A by one (to the previous month).
NOTE: DE currently points to the memory locations housing the of days in each month.
3573ADD A,EAdd E to A.
3574LD E,ALoad E with the result. E = E + A.
3575LD A,(DE)Poll the number of days in a month and put the result into A.
3576CP (HL)Compare A against (HL).
NOTE: If this is not a loop, the HL holds the day portion of the date.
3577RET NCIf 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.
3578LD 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.
3579CP 1EHCompare 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.
357BIf the carry flag isn't set then JUMP to 3583H to update the MONTH, but not the YEAR.
357DDEC HLDecrement HL to now point to 421AH.
NOTE: 421AH points to the current YEAR.
357ELD A,(HL)Load the YEAR into A.
357FINC HLBump HL to 421BH.
NOTE: 421BH points to the current DAY.
3580AND 03HMask A (which is holding the year) with 03H (Binary: 00000011) to test for a leap year.
3582RET ZRETURN if that mask showed that we are in the 4th year of a cycle (because 04 and higher are turned off).
3583LD (HL),01HPut a 1 into the memory location pointed to by HL (which is DAY).
3585INC HLBump HL to 421CH.
NOTE: 421BH points to the current MONTH.
3586INC (HL)Increase the current MONTH by 1.
3587LD A,(HL)Put the current MONTH into A.
3588SUB 0DHSubract 13 from A.
NOTE: This will test against a month 13. If A is < 13, then the CARRY FLAG will be set.
358ARET CIf it is NOT month 13 then RETURN to skip the next code which increases the YEAR.
358BLD (HL),01HIf we are here, then MONTH = 13, so set MONTH to 1.
358DDEC HLDecrement HL to 421BH.
NOTE: 421BH points to the current DAY.
358EDEC HLDecrement HL to 421AH.
NOTE: 421BH points to the current YEAR.
358FINC (HL)Bump the current year.
3590RETRETURN.
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.
3591LD 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.
3594BIT 0,ATest Bit 0 of A to see if the clock is on or off.
3596RET ZIf Bit 0 of A is ZERO, then return.
3597LD 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.
359ACP 1EHCompare the heartbeat counter against 1EH.
NOTE: A CP actually subtracts 1E from A without modifying A, but the flags are set accordingly.
359CRET NZIf the clock was not just updated, then RETURN.
359DIf 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.
35A3LD C,3AHLoad C with a :.
NOTE: This routine is also used to convert the date, and C will be swapped out to a /for that routine.
35A5LD B,03HLoad B with a 3.
NOTE: This is because we need to convert 3 numbers, so we will loop 3 times.
35A7LD 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.
35A8DEC DEDecrement DE to point to the next unit to be dealt with.
35A9LD (HL),2FHLoad the memory location pointed to by (HL) with 2F.
NOTE: 2F is a /which is also 1 character below a 0.
35ABINC (HL)Increase whatever is held in (HL). On the first iteration, this change the character at the screen location to a 0.
35ACSUB 0AHA = 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.
35B0ADD A,3AHIf 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.
35B2INC HLBump HL to point to the next location on the video screen. On the first iteration, this will be the 2nd digit of the HOUR.
35B3LD (HL),APut 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.
35B4INC HLBump HL to point to the next location on the video screen. On the first iteration, this will be the 3rd character.
35B5DEC BDecrement B to the next unit. On the first iteration, this will move from 3 to 2.
35B6RET ZIf we have processed all passes in the loop, RETURN.
35B7LD (HL),CIf we are here, then the routine has not yet looped 3 times, so put a :onto the screen.
35B8INC HLBump HL to point to the next location on the video screen.
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 /.
35BELD C,2FHLoad C with a /.
35C2H - Maskable Interrupt Handler.
35C2PUSH AFSave AF to the STACK.
35C3IN A,(E0H)Poll Port E0H which is the MASKABLE INTERRUPT LATCH and put the results into A.
35C5RRARotate 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.
35C9RRARotate 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.
35CAIf Bit 1 (which is now in the carry) is low then JUMP to 3369H (which is a cassette routine with E set to LOW).
35CDPUSH BCSave all the registers.
35CEPUSH DE
35CFPUSH HL
35D0PUSH IX
35D2PUSH IY
35D7PUSH HLPush HL to the STACK.
35D8RRARotate 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.
35D9If 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.
35DCRRARotate 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.
35DDIf 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.
35E0RRARotate 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.
35E1If 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.
35E4RRARotate 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.
35E5If 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.
35E8RRARotate 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.
35E9If 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.
35ECRRARotate 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.
35EDIf 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.
35F0POP HLRestore all registers.
35F1POP IY
35F3POP IX
35F5POP HL
35F6POP DE
35F7POP BC
35F8POP AF
35F9EIEnable Interrupts.
35FARETRETURN.
35FBH - RS-232 Initialization Routine. I'm [guessing] that IX is set to 41F5H
35FBDIDisable Interrupts so they don't interrupt this routine.
35FCIN 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)
35FECP FFHCompare A with FFH to see if the RS-232 exists.
3602XOR AFlip the RS-232 Port Results, just to get a non-zero result.
3603OUT (0E8H),AOutput A to port E8H.
NOTE: Port E8H is the RS-232 Status Register & Master Reset. Outputting ANYTHING to Port E8H resets the RS-232.
3605LD A,(IX+03H)Load the BAUD RATE CODE into A.
NOTE: 41F8H holds the baud rate code.
3608OUT (0E9H),AOutput 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
360ALD A,(IX+04H)Load the CONFIURATION CODE into A.
NOTE: 41F9 holds the RS-232 Configuration Code.
360DOR ASet the flags.
3610OUT (0EAH),AOutput 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)
3619LD A,(IX+05H)Load the WAIT SWITCH into A.
NOTE: 41FAH holds the RS-232 Wait Switch.
361COR ASet the flags.
361FSET 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
3623SET 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
362BOR ASet flags.
362ESET 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
3632SET 02H,(IY+04H)Set Bit 2 of 41F1H to set the ACTIVE FLAG.
3636IN 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
3638EIRe-Enable Interrupts.
3639RETRETURN.
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.
363AXOR AClear A and all flags.
363BLD B,04HLoad 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
363DLD C,0E8HLoad C with E8H.
363FOUT (C),ASend a 0 to the current port. On the first iteration, this is E8H (the RS-232 Status Register & Master Reset).
3641INC CBUMP C to the next port.
3647LD B,03HLoad B with 3 as a counter. 3 is for 3 bytes - 41E8H, 41E9H, and 41EAH.
3649LD (HL),00HLoad (HL) with 00H.
364BINC HLBump HL.
364ELoad HL with 41F0H.
NOTE: 41F0H is the 1 characer output buffer for the RS-232 Output DCB.
3651LD B,03HLoad B with 3 as a counter. 3 is for 3 bytes - 41F0H, 41F1H, and 41F2H.
3653LD (HL),00HLoad (HL) with 00H.
3655INC HLBump HL.
365AH - RS-232 Input Routine.
365ALoad IX with 41E5H.
NOTE: 41E5H is the DCB for RS-232 Input. 41E8H is the 1 Character RS-232 Input.
365EXOR AClear A and all Flags.
365FLD (IX+03H),ALoad (41E8H) with a Zero.
NOTE: 41E8H is the 1 Character RS-232 Input.
3662BIT 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.
3666RET ZIf the Driver is OFF, RETURN.
3667IN 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)
3669BIT 7,ATest Bit 7 of A.
NOTE: Bit 7 will be 1 if DATA READY (1=True).
366DBIT 1,(IX+04H)Test Bit 1 of 41E9H.
NOTE: Bit 1 of 41E9 contains the WAIT/NO WAIT of the RS-232 Input DCB.
3671RET ZIf its NO WAIT then RETURN, otherwise continue (to keep polling).
3677JUMP to 4203H if the BREAKkey was pressed.
NOTE: 4203H JUMPS to 022EH and is the break vector for tape and RS-232.
367AIN 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.
367CLD (IX+03H),ALoad (41E8H) with a A (the data from Port EBH).
NOTE: 41E8H is the 1 Character RS-232 Input.
367FRETRETURN.
3680H - RS-232 Output Routine.
3680Note. 41EDH is the RS-232 Output DCB, and 41F1H holds DRIVER ON/OFF in BIT 2, and WAIT/NO WAIT in BIT 1.
3684BIT 2,(IX+04H)Test Bit 2 of 41F1H to see if the RS-232 is active.
3688RET ZIf the RS-232 is NOT active, RETURN.
3689IN 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)
368BBIT 6,ATest Bit 6 of Port EAH to see READY TO SEND.
368FBIT 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.
3693RET ZIf RS-232 is NOT active, RETURN.
3699JUMP to 4203H if the BREAKkey was pressed.
NOTE: 4203H JUMPS to 022EH and is the break vector for tape and RS-232.
369CLD A,(IX+03H)Load A with the memory contents of 41F0H.
NOTE: 41F0H is RS-232 output buffer byte.
369FOR ATest A and Set Flags.
36A0If not zero, then there is a character in the buffer, so skip the next instruction and leave that byte in A.
36A2LD A,CLoad A with C [GET CHAR FROM DISPATCHER].
36A3OUT (0EBH),ASend 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.
36A5LD (IX+03H),00HLoad memory contents of the RS-232 output byte (at 41F0H) with a 0.
36A9RETRETURN.
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
36D5FFH
36D600HThis 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.
36D7
36DARST 00H
36DBNOP
36DCNOP
36DDXOR A
36DERET
36DFNOP
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
36F4NOP
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-370006 1BTwo 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-37061B 02 21 30 00 00 00
3707-370808 00Two 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-370E02 1B 30 55 6C FF
370F-371000 00Two 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-371F00 00 FF FF 00 00 C3 2E 02 C3 FA 35 C3 FA 35
3720-373041 32 03 32 28 03 3C 04 00 00 1E 00 00 00 00 00 00
3731-373300 00 00Three 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-373800 00 00 00 FF
*3739H - Model 4 Gen 1
*373FLD D,00HLoad D with 0 (so D = ROW 0).
*3742LD E,ALoad E with the contents held in (BC) to check the keyboard row.
*3743XOR (HL)XOR (HL) to set changed bits.
*3744LD (HL),ESave the scan back into (HL).
*3745AND EMask A with E (to mask the released keys).
*3748 - Model 4 Gen 1 routine to Go to the next Keyboard row
*3748INC DBump D so that D holds the NEXT row number.
*3749INC HLBump HL so that HL holds the NEXT buffer location.
*374ARLC CWe 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.
*374CIf 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
*374FLD 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.
*3752LD L,ALoad L with the A.
*3753LD 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.
*3756AND (HL)MASK A against (HL) to see if the previous keys are still pressed.
*375ASBC HL,HLZero HL by subtracting HL from HL.
*375CLD (4201H),HLLoad 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.
*3762LD (41FFH),HLLoad the memory location at 41FFH with 05DCH.
NOTE: 41FFH holds the keyboard scan repeat delay count.
*3765XOR AClear A and all flags.
*3766RETRETURN.
*3767 - Model 4 Gen 1 routine to Keyboard Repeat - Jumps Here if the same keys are still pressed.
*3768PUSH BCSave BC to the STACK.
*376CGOSUB 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.
*376FPOP BCRestore BC from the STACK.
*3770LD A,(BC)Load A with the memory contents pointed to by BC to re-check the keyboard.
*3771AND ECompare A against E to check the pattern.
*3772RET ZIf not the same pattern then RETURN.
*3773LD (41FEH),AIf it is the same pattern then save A into (41FEH).
NOTE: 41FE is the SAVED IMAGE AT POSITION.
*3776LD A,LLoad A with L (the scan position).
*3777LD (41FDH),ASave A into (41FDH).
NOTE: 41FDH is the SAVED POSITION IN SCAN.
*377ALD A,DLoad A with D (8 * ROW #).
*377BRLARotate 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.
*377CRLARotate 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.
*377DRLARotate 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.
*377ELD D,AD = A
*377FLD A,EA = E
RRCARotate A right one bit, with the contents of BIT 0 being put into BOTH the CARRY FLAG and BIT 7. D = 8* ROW # + KEY #.
*3781RET CIf the contents of BIT 0 of A was SET, RETurn
*3782INC DINC DBump D so that D holds the NEXT row number.
*3785 - Model 4 Gen 1 routine to Keyboard Repeat - Jumps Here if the same keys are still pressed.
*3786LD HL,(4201H)Load HL with the repeat delay counter.
NOTE: 4201H is the REPEAT DELAY COUNTER.
*3789INC HLBump HL.
*378ALD (4201H),HLLoad the memory location at 4201H with HL to clear the repeat counter.
NOTE: 4201H is the REPEAT DELAY COUNTER.
*378DLD DE,(41FFH)Load DE with the byte stored at 41FFH.
NOTE: 41FFH is the REPEAT DELAY COUNT.
*3791SBC HL,DESubtract with CARRY DE from HL.
*3793POP DERestore old HL (which is what is in the stack) into DE.
*3797XOR AClear A and all flags.
*3798LD (DE),APut a 0 into the memory location pointed to (DE) to let the key be re-read.
*3799LD (4201H),HLLoad the memory location at 4201H with HL to clear the repeat counter.
NOTE: 4201H is the REPEAT DELAY COUNTER.
*379CLD L,96HLoad L with 96H to set a fast repeat count.
*379ELD (41FFH),HLSave HL into the memory location at 41FFH.
NOTE: 41FFH is the REPEAT DELAY COUNT.
*37A4 - Model 4 Gen 1 routine jumped to from the the middle of the tokenize routine.
CP 22HIf 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 ".
*37A6If the character in register A is not a ", then JUMP forward a few instructions to 37B2H.
*37A8LD A,(409FH)Load A with the memory contents of 409FH.
NOTE: 409FH is the DATA FLAG.
*37ABXOR 01HXOR 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.
*37ADLD (409FH),ALoad memory location 409FH with a the XOR'd results.
NOTE: 409FH is the DATA FLAG.
*37B0LD A,22HLoad A with 22H, which is a ".
CP 3AHCompare 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.
*37B4If A is 3AH then JUMP to 06AAH.
NOTE: 06AAH is in the middle of the KEYBOARD DRIVER ENTRY ROUTINE.
*37B7LD A,(409FH)Otherwise, Load A with the memory contents of 409FH.
NOTE: 409FH is the DATA FLAG.
*37BBRRARotate 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.
*3792If Bit 7 of the DATA FLAG was set, then JUMP to 06A8H.
NOTE: 06A8H is in the middle of the KEYBOARD DRIVER ENTRY ROUTINE.
*37BERLARotate 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.
*37BFJUMP 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$)
RST 10HCall 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.
*37C3PUSH HLSave HL (the current position) to the STACK.
*37C4LD A,11HLoad A with 11H (Decimal: 17).
NOTE: This is to set up for a 17 Byte String.
*37C9LD HL,(40D4H)Load HL with the memory contents of (40D4H).
NOTE: 40D4 is the string pointer.
*37CFLD (HL),20HLoad the memory location pointed to by HL with a SPACE.
*37D1INC HLIncrement HL to move 1 character over.
*37D8 - Model 4 Gen 1 routine to Toggle Caps Lock.
*37DDXOR (HL)Invert the contents of the (4019H).
*37DELD (HL),AStore the inverted (i.e., toggled) contents of (4019H) back into (4019H)
*37E0RETRETurn to Caller.
*37E1 - Model 4 Gen 1 routine to do a very short delay routine
*37E5 - Model 4 Gen 1 routine Unused Code
*37E5NOP
*37E6NOP
*37E7NOP
*37E8RST 38H
*37E9RST 38H
*37EANOP
*37EBH - Model 4 Gen 1 routine to display the Copyright Message
*37F4 - Model 4 Gen 1 routine to enable the TIME$ Command
*37F7LD (4177H),HLLoad 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.
*37F7JUMP to 022EH to continue.
NOTE: 022EH will enable interrupts, show the READY prompt, and RETURN.
*37FD - Model 4 Gen 1 unused Code
*37FDPUSH HL
*37FEPUSH HL
*37FFRST 38H
*3739H - Model 4 Gen 2 - Part of the Keyboard Routine to check for SHIFT and CAPS LOCK and deal with them accordingly.
*3739LD A,(3880H)3A 80 38Load A with the value held at 3880H (which are the SHIFT keys)
*373CAND 03HE6 03Mask the SHIFT Register against 03H (Binary: 0000 0011) to keep only Bits 0 and 1 to check for shifts.
*373EJR Z,3742H28 02If the masked A is 0, then we have no shifts, and skip the next instruction (to 3742H).
*3740SET 6,DCB F2Set BIT 6 of D to offset D for shifts.
*3742LD A,(4019H)3A 19 40Load A with the contents of memory location 4019H to check for CAPS LOCK.
NOTE: 4019H is the CAPS LOCK TOGGLE.
*3745OR AB7Set the flags.
*3746JR Z,3753H28 0BIf the ZERO flag is set then there is NO CAPS LOCK so JUMP to 3753H to skip the next intructions.
*3748SET 7,DCB FASet 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.
*374ALD A,(3880H)3A 80 38Put the contents of memory location 3880H into A to GET SHIFT(S).
*374DAND 03HE6 03Mask the SHIFT Register against 03H (Binary: 0000 0011) to keep only Bits 0 and 1 to check for shifts.
*374FJR Z,3753H28 02If the masked A is 0, then we have no shifts, and skip the next instruction (to 3753H).
*3751RES 7,DCB BARESET 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.
*3753LD HL,304EH21 4E 30Load HL with 304EH (the KEYBOARD TABLES).
*3756LD E,D5AWe need DE to be the OFFSET, so load E with D and ...
*3757LD D,00H16 00... load D with 00.
*3759ADD HL,DE19Add DE (the offset over the keyboard table) to HL (the keyboard table).
*375ALD A,(HL)7EGet the character pointed to by (HL) and put it into A.
*375BRETC9RETurn 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
*375CCP 22HFE 22If 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 ".
*375EJR NZ,376AH20 0AIf the character in register A is not a ", then JUMP forward a few instructions to 376AH.
*3760LD A,(409FH)3A 9F 40Load A with the memory contents of 409FH.
NOTE: 409FH is the DATA FLAG.
*3763XOR 01HEE 01XOR 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.
*3765LD (409FH),A32 9F 40Load memory location 409FH with a the XOR'd results.
NOTE: 409FH is the DATA FLAG.
*3768LD A,22H3E 22Load A with 22H, which is a ".
*376ACP 3AHFE 3ACompare 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.
*376CJP NZ,06AAHC2 AA 06If A is NOT a : then JUMP to 06AAH.
NOTE: 06AAH is in the middle of the KEYBOARD DRIVER ENTRY ROUTINE.
*376FLD A,(409FH)3A 9F 40Otherwise, Load A with the memory contents of 409FH.
NOTE: 409FH is the DATA FLAG.
*3772RRA1FRotate 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.
*3773JP C,06A8HDA A8 06If Bit 7 of the DATA FLAG was set, then JUMP to 06A8H.
NOTE: 06A8H is in the middle of the KEYBOARD DRIVER ENTRY ROUTINE.
*3776RLA17Rotate 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.
*3777JP 06A3HC3 A3 06JUMP 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
*377ALD (IX+05H),05HDD 36 05 00Load the memory location pointed to by IX+5 with a 05H.
NOTE: IX+5 is the number of characters printed.
*377EINC (IX+04H)DD 34 04Bump the number of lines already printed (which is tracked in IX+04H)
*3781LD A,(IX+04H)DD 7E 04Fetch the number of lines already printed (which is tracked in IX+04H) into Register A
*3784CP (IX+03H)DD BE 03Compare the number of lines already printed (held in Register A) against the number of lines left in the page (tracked in IX+03H)
*3787RET NZC0If the number of lines already printed doesn't match the number of lines left in the page then RETURN
*3788LD (IX+04H),04HDD 36 04 01If they DO match, then set the number of lines already printed to 04H
*378CRETC9RETurn 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
*378DLD A,C79LET Register A = Register C.
*378ECP E0HFE E0Compare 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
*3790JP NC,0043HD2 43 00If 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).
*3793CP C0HFE C0If 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
*3795JP C,0063HDA 63 00If 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).
*3798SUB C0HD6 C0If 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.
*379ALD B,00H06 00LET Register B = 00H
*379CLD C,A4FLET Register C = Register A.
*379DLD HL,(4220H)2A 20 42LET Register Pair HL the memory contents of 4220H and 4221H.
NOTE: 4220H-4221H is the destination device nName for ROUTE Routine. 2 Bytes
*37A0ADD HL,BC09LET HL = HL + BC
*37A1LD C,(HL)4ELET Register C = the contents of the memory location pointed to by Register Pair HL.
*37A2JP 0063HC3 63 00JUMP 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
*37A5LD HL,3C00H21 00 3CLoad HL with the memory location for the beginning of the video RAM.
*37A8LD A,(HL)7EPut the character at the screen location stored in HL into A.
*37A9RLCA07Rotate 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.
*37ACRLCA07Rotate 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.
*37ADJR C,37B3H38 04If 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
*37AFLD A,2EH3E 2ELoad Register A with a . which is the character which will print instead of non-printable characters.
*37B3LD A,(HL)7EReplace the non-printable character with a .
*37B4CALL 003BHCD 3B 00Call the PRINT CHARACTER routine at 003B (which sends the character in the A register to the printer).
*37B7INC HL23Bump HL to the next character on the screen.
*37B8BIT 6,HCB 74Check 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).
*37BDLD A,L7DTest of end of line by loading A with L.
*37BEAND 3FHE6 3FAND 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).
*37C0JR NZ,37A8H20 E6If 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
*37A5 - Model 4 Gen 2 - Screen Print Routine BUT exits witH Register A at 0 and all FLAGS cleared
*37C7CALL 37A5HCD A5 37GOSUB to 37A5H for the screen print
*37CAXOR AAFClear Register A and all the flags
*37CBRETC9RETurn to CALLer
*37CC - Model 4 Gen 2 - TOGGLE the CAPS LOCK
*37CCLD A,01H3E 01Load Register A with an 01H
*37CELD HL,4019H21 19 40Load HL with 4019H, which is the CAPS LOCK TOGGLE in the keyboard DCB.
*37D1XOR (HL)AEXOR the contents of the CAPS LOCK TOGGLE in RAM against 01H; this effectively toggles it
*37D2LD (HL),A77Put the toggled CAPS LOCK TOGGLE back into RAM where such thing is stored
*37D3XOR AAFClear Register A and Clear all FLAGs
*37D4RETC9RETurn 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.
*37D5PUSH AFF5Preserve Registers A and F to the top of the STACK
*37D6IN A,(EAH)DB EAPoll the RS-232 UART Control Register/Status Register (via Port EAH) into A.
*37D8BIT 6,ACB 77Test Bit 6 of the RS-232 Register. It will be 1 if READY TO SEND
*37DAJR Z,37D6H28 FAIf the UART reported that READY TO SEND is FALSE, LOOP back to 37D6 and poll again.
*37DCPOP AFF1Restore Registers A and F from the top of the STACK
*37DDOUT (EBH),AD3 EBSend the contents held in Register A to the RS-232C Data Register (via Port E8H) so as to send A to the RS-232C.
*37DFRETC9RETurn 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).
*37E0LD A,6DH3E 6DLoad Register A with a 6DH (Binary: 0110 1101)
*37E2OUT (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)
*37E4JP (HL)E9Jump to whatever (HL) is pointing to.
*37E5 - Model 4 Gen 2 - UNUSED CODE.
*37E5NOP00
*37E6NOP00
*37E7NOP00
*37E8RST 38HFF
*37E9RST 38HFF
**37D5-37E9 - Model 4 ROM Student Network Edition - Replace all the above with NOPs
*37EA - Model 4 Gen 2 - BASIC TIMES (DATE$+" "+TIME$)
*37EARST 10HD7Call the EXAMINE NEXT SYMBOL routine at RST 10H.
*37EBPUSH HLE5Save HL (the current position) to the STACK.
*37ECLD A,11H3E 11Load A with 11H (Decimal: 17) to set up for a 17 Byte String.
*37F1LD HL,(40D4H)2A D4 40Load HL with the memory contents of (40D4H), which is the string pointer.
*37F7LD (HL),20H36 20Load the memory location pointed to by HL with a SPACE.
*37F9INC HL23Increment HL to move 1 character over.
On entry, HL=address of LEFT$$, the STACK = string address, STACK+1 = n, and DE = code string address.
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.
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).
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).
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.
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.
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).
This evaluates an expression and leaves the result in DE as an integer.
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.
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).
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.
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.
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.
This routine sets the output device flag to PRINTER and then flows through to the LISTcommand.
Note: 409CH holds the current output device flag: -1=cassette, 0=video and 1=printer
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.
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.
Note: 40ECH-40EDH holds EDIT line number
Note: 40A7H-40A8H holds the input Buffer pointer
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.
Note: 40A7H-40A8H holds the input Buffer pointer
2B83
LD C,L44
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!
DEC BC
DEC BC
DEC BC0B
INC D
INC D
INC D14
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.
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.
2BD2
LD E,L54
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.
- Note: 40F9H-40FAH holds the starting address of the simple variable storage area.
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.
- Note: 40F9H-40FAH holds the starting address of the simple variable storage area.
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.
- Note: 40A4H-40A5H holds the starting address of BASIC program text also known as the PROGRAM STATEMENT TABLE (PST).
- Note: 40F9H-40FAH holds the starting address of the simple variable storage area.
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.
2C1F-2CA4 - LEVEL II BASIC CLOAD ROUTINE - ROM v1.0- "CLOAD"
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.
- 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.
- Note: 40F9H-40FAH holds the starting address of the simple variable storage area.
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.
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).
- Note: 40A4H-40A5H holds the starting address of BASIC program text also known as the PROGRAM STATEMENT TABLE (PST).
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.
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).
2CA5-2CA9 - MESSAGE STORAGE LOCATION- "NOOKCS"
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- ↳ PEEKCall 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)
2CADLD A,(HL)7ELoad Register A with the value at the location of the memory pointer in HL
2CAE-2CB0Go save the 8-bit value in Register A as the current result in ACCumulator
2CB1-2CBC - LEVEL II BASIC POKEROUTINE- "POKE"
2CB1-2CB3- ↳ POKEGo evaluate the expression at the location of the current BASIC program pointer in HL and return with the integer result in DE
2CB4PUSH DED5Save the address the user wants to POKE to (held in DE) to the STACK
2CB5-2CB6RST 08H ⇒ 2ESYNCHK ","CF 2ESince 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-2CB9GOSUB 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
2CBAPOP DED1Get the address the user wants to POKE to from the STACK and put it in DE
2CBBLD (DE),A12Save the value the user wanted to poke (held in Register A) in the location that the user wants to POKE to (held in DE)
2CBCRETC9RETurn 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- ↳ PRINUSGo evaluate the string expression at the location of the current BASIC program pointer in HL 2CC0-2CC2Go make sure the expression that was just evaluated was a string 2CC3-2CC4RST 08H ⇒ 3BSYNCHK ";"CF 3BSince 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).
2CC5EX DE,HLEBSwap DE and HL so that DE now holds the current BASIC program pointer 2CC6-2CC8LD HL,(4121H)LD HL,(FACLO)2A 21 41Load HL with the USING string's VARPTR 2CCB-2CCD- ↳ REUSSTLD A,(40DEH)LD A,(FLGINP)3A DE 40Load 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. 2CCEOR AB7Check to see if that flag indivates that we did, or did not, print out a value last time. 2CCF-2CD0If we did not print out a value last time, we have an error, so JUMP down to 2CDDH 2CD1POP DED1Restore the pointer to the "USING" string decription from the STACK into DE 2CD2EX DE,HLEBSwap 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- ↳ INIUSPUSH HLE5Save the pointer to the "USING" string descriptor (i.e., the USING string's VARPTR) in HL to the STACK 2CD4XOR AAFZero Register A and all the flags. 2CD5-2CD7LD (40DEH),ALD (FLGINP),A32 DE 40Clear the flag we are using to see if we printed the values or not. 2CD8CP DBATurn 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. 2CD9PUSH AFF5Save the flag indicating if the value list has ended or not to the STACK 2CDAPUSH DED5Save the pointer into the value list to the STACK 2CDBLD B,(HL)46Load Register B with the USING string's length 2CDCOR BB0Check to see if the USING string's length in Register B is equal to zero 2CDD-2CDF- ↳ FCERR3If the USING string is NULL then display a ?FC ERROR 2CE0INC HL23Bump the pointer to the USING string's data in HL by 1 2CE1LD C,(HL)4ELoad Register C with the LSB of the USING string's address at the location of the USING string's VARPTR in HL 2CE2INC HL23Bump the value of the USING string's VARPTR in HL 2CE3LD H,(HL)66Load Register H with the MSB of the USING string's address at the location of the USING string's VARPTR in HL 2CE4LD L,C69Load Register L with the LSB of the USING string's address in Register C
2CE7- ↳ BGSTRFLD E,B58Load Register E with the USING string's length 2CE8PUSH HLE5Save the pointer to the USING string pointer in HL to the top of the STACK 2CE9-2CEALD C,02H0E 02Since the \\ string field length is two plus number of enclosed spaces, add two 2CEB- ↳ LPSTRFLD A,(HL)7ELoad Register A with the character at the location of the USING string pointer in HL 2CECINC HL23Bump the value of the USING string data pointer in HL 2CED-2CEECP 25HCP CSTRNGFE 25Check 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-2CF1If it is a "%" then JUMP to 2E17H to evaluate a string and print 2CF2-2CF3CP 20HFE 20Check 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-2CF5If the character is not a field extender, then it isn't a string field, so JUMP down a few opcodes to 2CF9H 2CF6INC C0CIncrement the field width (tracked in in Register C) 2CF7-2CF8Decrement 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- ↳ NOSTRFPOP HLE1Restore the pointer to the "USING" string's data into Register Pair HL
2CFALD B,E43Load Register B with the USING string's length
2CFB-2CFCLD A,25HLD A,CSTRNG3E 25Restore 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- ↳ NEWUCHIf a +came before the character, make sure to print it
2D00-2D02Once that has been printed, now we print the character in Register A since we know it isn't part of a field
2D03- ↳ PRCCHRXOR AAFWe 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
2D04LD E,A5FZero Register E
2D05LD D,A57Zero Register D
2D06-2D08- ↳ PLSFINGo print a leading +if necessary (i.e., to allow for multiple plusses)
2D09LD D,A57Set 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.
2D0ALD A,(HL)7ELoad Register A with the next field description character in the USING string
2D0BINC HL23Bump the value of the USING string pointer in HL
2D0C-2D0DCP 21HCP "!"FE 21Check 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.
2D11-2D12CP 23HCP "#"FE 23Check 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.
2D15DEC B05Since every other possibility is actually a two character field, decrement the value of the string's length in Register B onem ore time
2D16-2D18If 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-2D1ACP 2BHCP "+"FE 2BCheck 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-2D1CLD A,08H3E 08Set 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
2D1FDEC HL2BDecrement the value of the USING string pointer so we can re-get the character.
2D20LD A,(HL)7ELoad Register A with the (current) character at the location of the USING string pointer in HL
2D21INC HL23Bump the value of the USING string pointer in HL
2D22-2D23CP 2EHCP "."FE 2ECheck 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-2D25Jump if the character in Register A is a .to scan with Register E holding the number of digits before the "." as 0
2D26-2D27CP 25HCP CSTRNGFE 25Check 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-2D29Jump to see if it is really a string field if the character in Register A is a %
2D2ACP (HL)BECheck 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-2D2CIf the NZ flag is set, then we can't have a $$or a **, so all remaining possibilities are exhausted, so JUMP to NEWUCH
2D2D-2D2ECP 24HCP "$"FE 24Check 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.
2D31-2D32CP 2AHCP "*"FE 2ACheck 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-2D34If 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
2D35LD A,B78Prepare 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-2D37CP 02HFE 02Check 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.
2D38INC HL23Bump the value of the USING string pointer in HL
2D39-2D3AJump to 2D3EH if the USING string's length in Register A isn't at least two
2D3BLD A,(HL)7ELoad Register A with the character at the location of the USING string pointer in HL
2D3C-2D3DCP 24HCP "$"FE 24Check 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- NOTSPCLD A,20H3E 20Set the *bit in Register A
2D40-2D41If we did not ultimately get a **$then JUMP (noting we do NOT set the dollar sign flag)
2D42DEC B05Decrement the value of the USING string's length to take the $into account
2D43INC E1CBump the field width tracker to account for the floating dollar sign
2D44-2D45CP 0AFHFE AFZ-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- ↳ DOLRNMXOR AAFThis is $processing for PRINT USING. Clear Register A.
2D46-2D47ADD A,10HC6 10Mask Register A to set the bit for a floating dollar sign flag.
2D48INC HL23Bump the value of the USING string pointer in HL to go past the special characters
2D49- ↳ SPCNUMINC E1CSince two characters specify the field size, start off with E=1
2D4AADD A,D82Combine the bits in Register D into the flag tracker
2D4BLD D,A57Preserve the modified flag tracker into Register D.
2D4C- ↳ NUMNUMINC E1CBump the number of characters to the left of the decimal point in Register E
2D4D-2D4ELD C,00H0E 00Set the number of digits to the right of the decimal point (tracked in Register C) to 0
2D4FDEC B05Check to see if there are any more characters by decrementing the value of the string's length in Register B
2D50-2D51If 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.
2D52LD A,(HL)7ELoad Register A with the next character at the location of the USING string pointer in HL
2D53INC HL23Bump the value of the USING string pointer in HL
2D54-2D55CP 2EHCP "."FE 2ECheck 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-2D57If yes, then need to use a special scan loop to scan after the decimal point, so JUMP to AFTDOT
2D58-2D59CP 23HCP "#"FE 23Check 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.
2D5C-2D5DCP 2CHCP ","FE 2CCheck 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-2D5FIf there is no comma, then JUMP to FINNUM because there are no more leading digits and we need to check for "^^^"
2D60LD A,D7AIf we are here, then a comma was requested. Turn on the COMMA bit
2D61-2D62OR 40HF6 40Mask the flag in Register A for ,
2D63LD D,A57Load Register D with the value of the flag in Register A
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- ↳ DOTNUMLD A,(HL)7ELoad Register A with the next character of the USING string
2D67-2D68CP 23HFE 23Check 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-2D6ALD A,2EHLD A,"."3E 2ELoad Register A with a decimal point
2D6B-2D6CIf it isn't a "." then JUMP AWAY to NEWUCH with A holding a "." so that a "." will get printed
2D6D-2D6ELD C,01H0E 01If 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
2D6FINC HL23Bump the value of the USING string pointer in HL
2D70- ↳ AFTDOTINC C0CBump the number of digits to the right of the decimal point (tracked in Register C)
2D71DEC B05Decrement the value of the USING STRING's length to test to see if there are more characters
2D72-2D73If the USING string length is now ZERO, JUMP to ENDNUS to stop scanning
2D74LD A,(HL)7ELoad Register A with the character at the location of the USING string pointer in HL
2D75INC HL23Bump the value of the USING string pointer in HL
2D76-2D77CP 23HFE 23Check 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-2D79If 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- ↳ FINNUMPUSH DED5Save the value of the flag (tracked in D) and the number of leading digits (tracked in E) to the STACK
2D7B-2D7DLoad DE with the return address in case this is not a scientific notation
2D7EPUSH DED5Save the value of the return address in DE to the STACK
2D7F
2D80LD D,H
LD E,L54Let DE = HL in case we need to rememer HL
2D81-2D82CP 5BHFE 5BCheck 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.
2D83RET NZC0Return if the character in Register A isn't an up arrow
2D84CP (HL)BECheck 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.
2D85RET NZC0Return 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)
2D86INC HL23Bump the value of the USING string pointer in HL
2D87CP (HL)BECheck 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.
2D88RET NZC0Return 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)
2D89INC HL23Bump the value of the USING string pointer in HL
2D8ACP (HL)BECheck 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.
2D8BRET NZC0Return 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)
2D8CINC HL23Bump the value of the USING string pointer in HL. If we are here we have a #.^^^^format
2D8DLD A,B78Now 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-2D8FSUB 04HD6 04Check to see if there are at least 4 characters left in the USING string
2D90RET CD8Return to 2D97 if there aren't at least four characters left in the USING string
2D91POP DED1If there are at least 4 characters left, then clean up the STACK by removing the NOTSCI return address
2D92POP DED1Get the flag and the count of the characters to the left of the decimal point from the STACK and put it in DE
2D93LD B,A47Load Register B with the new USING string's length in Register A
2D94INC D14Set the exponential notation flag (tracked in Register D)
2D95INC HL23Bump the value of the USING string pointer in HL
2D96JP Z,0D1EBHCA EB D1Z-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
2D98POP DED1(Ignored if passing through) Restore the flags into Register D and the number of leading digits into Register E
2D99- ↳ ENDNUSLD A,D7AWe 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
2D9ADEC HL2BDecrement the value of the USING string pointer in HL
2D9BINC E1CBump the number of characters to the left of the decimal point in Register E to take into account the leading plus
2D9C-2D9DAND 08HE6 08Mask Register A to NOT check for a trailing sign
2D9E-2D9FIf that AND leaves us with a NZ, then we are all done with the field, so JUMP to ENDNUM
2DA0DEC E1DOtherwise, 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
2DA1LD A,B78Check to see if there are more characters by first loading Register A with the USING string's length from Register B
2DA2OR AB7Check to see if this is the end of the USING string
2DA3-2DA4If we are out of characters, then we are all done, so JUMP to ENDNUM
2DA5LD A,(HL)7EIf there ARE more characters, then fill Register A with the character at the location of the USING string pointer in HL
2DA6-2DA7SUB 2DHSUB "-"D6 2DCheck to see if the character in Register A is a -(i.e., a trailing minus)
2DAA-2DABCP 0FEHCP "+" - "-"FE FECheck 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.
2DAE-2DAFLD A,08H3E 08If we are here then we did have a trailing "+" so first set the flag for a POSITIVE "+"
2DB0-2DB1- ↳ SGNTRLADD A,04HC6 04Then set the flag for a trailing sign
2DB2ADD A,D82Combine the value of the flag in Register D with the value of the flag in Register A
2DB3LD D,A57Load Register D with the current flags
2DB4DEC B05Decrement 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- ↳ ENDNUMPOP HLE1Get the value of the current BASIC program pointer from the STACK and put it in HL
2DB6POP AFF1Load Register A with the flag that tells us whether there are more values to process in the value list.
2DB7-2DB8If there are no more values in the value list to process, then JUMP to FLDFIN because we are done with the PRINT
2DB9PUSH BCC5Save 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
2DBAPUSH DED5Save the flags (held in D) and the number of leading digits (held in E) to the STACK
2DBB-2DBDRead 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
2DBEPOP DED1Restore the flags (held in D) and the number of leading digits (held in E) from the STACK
2DBFPOP BCC1Restore 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
2DC0PUSH BCC5Save 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
2DC1PUSH HLE5Save the value of the current BASIC program pointer in HL to the STACK
2DC2LD B,E43Set Register B to hold the number of leading digits (i.e., the number of characters to the left of the decimal point)
2DC3LD A,B78We 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
2DC4ADD A,C81Then 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-2DC6CP 19HFE 19Check 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-2DC9Display a FC ERROR message if the total number of digits is greater than 24
2DCALD A,D7ALoad Register A with the flags (held in Register D)
2DCB-2DCCOR 80HF6 80Turn on the "USING" bit in the flags
2DCD-2DCFPrepare 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-2DD2Call 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- ↳ FNSTRFPOP HLE1Top of a loop. Get the value of the current BASIC program pointer from the STACK and put it in HL
2DD4DEC HL2BDecrement the value of the current BASIC program pointer in HL so we can test to see what the terminator was
2DD5RST 10HCHRGETD7We 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.
2DD6SCF37Set the Carry flag to indicate that a CRLF is desired
2DD7-2DD8If 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-2DDBLD (40DEH),ALD (FLGINP),A32 DE 40Set the flag that the value HAS been printed!
2DDC-2DDDCP 3BHCP ";"FE 3BCheck 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.
2DE0-2DE1CP 2CHCP ","FE 2CCheck 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-2DE4If 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- ↳ SEMUSNRST 10HCHRGETD7We 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- ↳ CRDNUSPOP BCC1Restore the number of characters remaining to be procesed in the USING string into Register B
2DE7EX DE,HLEBSwap DE and HL so that DE will point to the location of the current BASIC program pointer. We don't care about HL.
2DE8POP HLE1Restore the position in the USING string from the STACK and put it in HL
2DE9PUSH HLE5Save the position in the USING string (held in HL) to the STACK
2DEAPUSH AFF5Save the flag that indicates whether or not the value list has terminated to the STACK
2DEBPUSH DED5Save 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.
2DECLD A,(HL)7ELoad Register A with the USING string's length at the location of the USING string's VARPTR in HL
2DEDSUB B90Subtract the number of characers which were already scanned
2DEEINC HL23Bump the pointer to the "USING" strings string data
2DEFLD C,(HL)4ELoad Register C with the LSB of the USING string's address at the location of the USING string's VARPTR in HL
2DF0INC HL23Bump the value of the USING string's VARPTR in HL
2DF1LD H,(HL)66Load Register H with the MSB of the USING string's address at the location of the USING string's VARPTR in HL
2DF2LD L,C69Load Register L with the LSB of the USING string's address in Register C
2DF3-2DF4LD D,00H16 00Zero Register D so that Register Pair DE can be a 16 bit offset of whatever is held in A.
2DF5LD E,A5FLoad Register E with the USING string's offset in Register A
2DF6ADD HL,DE19Add 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
2DF7LD A,B78Load Register A with the number of characters left to scan
2DF8OR AB7Check to see if this is the end of the USING string
2DF9-2DFBIf there are still more string characters to scan, JUMP to PRCCHR to do so
2DFE-2E00 - Part of the PRINT USING Routine- "REUSIN"
We will wind up here when we are done processing a numeric field
2E01-2E03Go send the FINAL character (held in Register A) to the current output device
2E04- ↳ FINUSIPOP HLE1Get the value of the current BASIC program pointer from the STACK and put it in HL
2E05POP AFF1Restore the flag which indicates whether or not the value list has ended into Register A
2E06-2E08If the value list has NOT ended, JUMP back to REUSST to reuse the USING string
2E09-2E0B- ↳ FLDFINIf 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
2E0CEX (SP),HLE3Swap (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
2E10POP HLE1Get the value of the current BASIC program pointer from the STACK and put it in HL
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- ↳ SMSTRFLD C,01H0E 01Set the field width to 1
2E16-2E17LD A,0F1H3E F1Z-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- ↳ ISSTRFPOP 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)
2E18DEC B05Decrement the USING string character count (tracked in Register B)
2E1CPOP HLE1Get the value of the current BASIC program pointer from the STACK and put it in HL
2E1DPOP AFF1Get the flag which indicates whether there are more values in the value list into Register A
2E1E-2E1FIf there are no more values in the value list, then we are done so JUMP back to 2E09H
2E20PUSH BCC5Save the number of characters still to be scanned from the USING string (tracked in B) to the STACK
2E21-2E23Read 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
2E27POP BCC1Restore the field width (a/k/a the number of characters to be printed) into Register C
2E28PUSH BCC5Save the USING string's length and the number of characters to be printed in BC to the STACK
2E29PUSH HLE5Save the value of the current BASIC program pointer in HL to the STACK
2E2A-2E2CLD HL,(4121H)LD HL,(FACLO)2A 21 41Load HL with the string's VARPTR in ACCumulator
2E2DLD B,C41Load Register B with field width (a/k/a the number of characters to be printed)
2E2E-2E2FLD C,00H0E 00Zero Register C so that we can use the LEFT$ routine
2E30PUSH BCC5Save the length of the string to be printed in Register B to the STACK (as we will need that for space padding)
2E31-2E33Truncate the string to B characters via a call to the LEFT$ routine
2E37-2E39LD HL,(4121H)LD HL,(FACLO)2A 21 41Load HL with the string's VARPTR in ACCumulator so we can see if we need to pad the string
2E3APOP AFF1Get the field width (a/k/a the length of the string to be printed) from the STACK and put it in Register A
2E3BSUB (HL)96Determine 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
2E3CLD B,A47Save the amount of padding needed into Register B
2E3D-2E3ELD A,20H3E 20Load Register A with a SPACE
2E3FINC B04Bump 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- ↳ UPRTSPDEC B05Top of a loop. Decrement the number of spaces in Register B
2E41-2E43If all of the spaces have been printed, Jump back to 2DD3H to see if the value list ended and to resume scanning
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- ↳ PLSPRTPUSH AFF5Save the current character (held in Register A) to the STACK
2E4ALD A,D7AWe need to test the PLUS BIT in D, so first load Register A with the value in Register D
2E4BOR AB7Check 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-2E4DLD A,2BHLD A,"+"3E 2BPrepare to print the +by loading Register A with a +
2E4E-2E50If the bit was set (i.e., A was non-zero), then send a +to the current output device
2E51POP AFF1Get the current character from the STACK and put it in Register A
2E52RETC9RETurn 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- ↳ ERREDTLD (409AH),ALD (ERRFLG),A32 9A 40Reset the EDIT flag.
Note: 409AH holds the ERROR/RESUME flag
2E56-2E58LD HL,(40EAH)LD HL,(ERRLIN)2A EA 40Load HL with the line number to be edited.
Note: 40EAH-40EBH holds the line number with error
2E59OR HB4OR Register A with the MSB of the error line number in Register H
2E5AAND LA5Combine 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
2E5BINC A3CBump 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
2E5CEX DE,HLEBSwap DE and HL so that DE now holds the line number to edit.
2E5DRET ZC8If there was no line number, return if Level II BASIC
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- ↳ EDITGet the first line number by calling 1E4F - returns in in DE
2E63RET NZC0If the zero flag got set, there was no line number, so return
2E64- ↳ EREDITPOP HLE1Clean up the STACK (i.e., discard the NEWSTT return address)
2E65- ↳ EEDITSEX DE,HLEBLoad HL with the line number to be edited in DE
2E66-2E68LD (40ECH),HLLD (DOT),HL22 EC 40Save 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
2E69EX DE,HLEBLoad HL with the line number to be edited
2E6A-2E6CFind 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
2E70
2E71LD H,B
LD L,C60At 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
2E73INC HL
INC HL23Bump the value of the memory pointer in HL twice to now point to the first byte of the line.
2E74LD C,(HL)4ELoad Register C with first byte of the line number being edited
2E75INC HL23Bump the value of the memory pointer in HL to point to the second byte of the line being edited
2E76LD B,(HL)46Load Register B with second byte of the line number being edited
2E77INC HL23Bump the value of the memory pointer in HL to now point to the first byte of the actual line
2E78PUSH BCC5Save the line number to the STACK
2E79-2E7BGOSUB 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- ↳ LLEDPOP HLE1Get the value of the line number from the STACK and put it in HL
2E7D- ↳ INLEDPUSH HLE5Save the value of the line number in HL to the STACK
2E7E-2E80Convert 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-2E82LD A,20H3E 20Load Register A with a space
2E86-2E88LD HL,(40A7H)LD HL,(BUFPNT)2A A7 40Load 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-2E8ALD A,0EH3E 0ELoad Register A with the "turn on the cursor" character
2E8EPUSH HLE5Save the value of the input buffer pointer (in HL) to the STACK
2E8F-2E90LD C,FFH0E FFLoad 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- ↳ LENLPINC C0CBump the number of characters examined so far in Register C
2E92LD A,(HL)7ELoad Register A with the character at the location of the input buffer pointer in HL
2E93OR AB7Check to see if the character in Register A is an end of the BASIC line character
2E94INC HL23Bump the value of the input buffer pointer in HL
2E95-2E96Loop back to 2E91H until the end of the BASIC line has been found
2E97POP HLE1At this point, C will be the maximum number of characters in the line at issue. Put the start of the expanded buffer into HL
2E98LD B,A47Set the current position in the BASIC line being edited (tracked by Register B) to ZERO
2E99-2E9A- ↳ DISPEDLD D,00H16 00Assume the repetition count for the upcoming command (tracked by Register D) is zero
2E9B-2E9D- ↳ DISPIGo scan the keyboard to wait for the user command
2D9E-2E9F- ↳ DISPSUB 30H20 15We need to test to see if the character was alphabetic or alphanumeric so we subtract 30H from it
2EA2-2EA3CP 0AHFE 0ACheck 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.
2EA6LD E,A5FLoad Register E with the binary value of the character in Register A
2EA7LD A,D7APut the repetition value into Register A
2EA8RLCA07Multiply the value in Register A by two (so now A has multiplied by 2)
2EA9RLCA07Multiply the value in Register A by two (so now A has multiplied by 4)
2EAAADD A,D82Add the value in Register D to the value in Register A (so now A has multiplied by 5)
2EABRLCA07Multiply the value in Register A by two (so now A has multiplied by 10). Now the "ones place" is empty.
2EACADD A,E83Add the value in Register E to the value in Register A in the "ones place"
2EADLD D,A57Load Register D with the value in Register A
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- ↳ NOTDGIPUSH HLE5Save the value of the input buffer pointer in HL to the STACK
2EB4EX (SP),HLE3Exchange the value of the return address in HL with the value of the input buffer pointer to the STACK
2EB5DEC D15We 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
2EB6INC D14... and then incrementing the numeric value in Register D to set the flags
2EB7-2EB9If we had a received a repetition count already, then JUMP to 2EBBH
2EBAINC D14Otherwise, set the repetition count (held in Register D) to be one
2EBB-2EBC- ↳ NTZERDCP 0D8HFE D8Check 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-2EBFIf the character in Register A is a BACKSPACEcharacter, JUMP to DELED
2EC0-2EC1CP 0DDHFE DDCheck 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-2EC4If the character in Register A is a CARRIAGE RETURN, JUMP to CRED
2EC5-2EC6CP 0F0HFE F0Check 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.
That's it for non-alphabetic instructions, so we need to need to convert a lower case command to upper case
2EC9-2ECACP 31HFE 31Check 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.
2ECD-2ECESUB 20HD6 20Convert the lowercase character in Register A to uppercase
2ECF-2ED0- ↳ NOTLW4CP 21HFE 21Check 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.
2ED4-2ED5CP 1CHFE 1CCheck 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.
2ED9-2EDACP 23HFE 23Check 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.
2EDD-2EDECP 19HFE 19Check 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.
2EE2-2EE3CP 14HFE 14Check 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.
2EE7-2EE8CP 13HFE 13Check 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.
2EEC-2EEDCP 15HFE 15Check 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.
2EF1-2EF2CP 28HFE 28Check 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.
2EF6-2EF7CP 1BHFE 1BCheck 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.
2EFA-2EFBCP 18HFE 18Check 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-2EFEJump if the character in Register A is an H(i.e., HACK off the rest of the line and then enter INSERT mode)
2EFF-2F00CP 11HFE 11Check 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.
2F01RET NZC0Return if the character in Register A isn't an A
2F02 - EDITCommand - Cancel and Restore Logic.
2F02POP BCC1Clean up the STACK (i.e., remove the DISPI return address)
2F03POP DED1Get the BASIC line number from the STACK and put it in DE
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- ↳ SPEDLD A,(HL)7ELoad Register A with the character at the location of the input buffer pointer in HL
2F0BOR AB7Check to see if the character in Register A is an end of the BASIC line character
2F0CRET ZC8Return if the character in Register A is an end of the BASIC line character
2F0DINC B04Bump the character position in Register B
2F11INC HL23Bump the value of the pointer in HL
2F12DEC D15Decrement the number of times to perform the operation in Register D
2F15RETC9RETurn to CALLer
2F16 - EDITCommand - KILL Logic- "KED".
2F16- ↳ KEDPUSH HLE5Save the current character position in the buffer in HL to the STACK
2F17-2F19Load HL with the return address of 2F5FH (which will print the final !
2F1AEX (SP),HLE3Exchange the value of the return address in HL with the value of the input buffer pointer to the STACK
2F1BSCF37Set the KILL/SEARCH flag for KILL since CARRY flag signals KILL
2F1C- ↳ SEDPUSH AFF5Save the KILL/SEARCH flag to the STACK
2F1D-2F1FGo scan the keyboard for the character the user wants to SEARCH for
2F20LD E,A5FSave the character the user wants to SEARCH for into Register E
2F21POP AFF1Get the KILL/SEARCH flag from the STACK
2F22PUSH AFF5Save the KILL/SEARCH flag to the STACK
2F23-2F25If KILL (because the CARRY flag was set) then GOSUB to 2F5FH to print a !
2F26- ↳ SRCALPLD A,(HL)7ELoad Register A with the character at the location of the input buffer pointer in HL
2F27OR AB7Check to see if the character in Register A is an end of the BASIC line character
2F28-2F2AJump down to 2F3EH if the character in Register A is an end of the BASIC line character
2F2EPOP AFF1Get the KILL/SEARCH flag from the STACK
2F2FPUSH AFF5Save the KILL/SEARCH flag to the STACK
2F30-2F32If 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-2F34Jump to 2F37H if KILL. Note, we do not move the HL pointer in this case because DELCHR already moved it.
2F35INC HL23If we are here, it must be SEARCH! So bump to the next character
2F36INC B04Bump the value of the character position in Register B
2F37- ↳ NOTSRCLD A,(HL)7ERegardless of whether we are SEARCH or KILL, load Register A with the character at the location of the current input buffer pointer in HL
2F38CP EBBCheck 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.
2F3BDEC D15Decrement the number of times to perform the operation in Register D (as initially specified by the user by entering a number before the command)
2F3E- ↳ POPARTPOP AFF1Get the KILL/SEARCH flag from the STACK
2F3FRETC9RETurn to CALLer
2F40 - EDITCommand - LIST Logic- "LED".
2F40-2F42- ↳ LEDSince 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
2F46POP BCC1Clear off the RETURN address to DISPED
2F47-2F49Jump to 2E7CH (to display the current line number and await the next EDIT command)
2F4A - EDITCommand - DELETE Logic- "DED"
2F4A- ↳ DEDLD A,(HL)7ELoad Register A with the character at the location of the input buffer pointer in HL
2F4BOR AB7Check to see if the character in Register A is an end of the BASIC line character
2F5CRET Z15Return if the character in Register A is an end of the BASIC line character
2F4D-2F4ELD A,21HLD A,"!"3E 21Load Register A with an !
2F52- ↳ DELLPLD A,(HL)7ELoad Register A with the character at the location of the input buffer pointer in HL
2F53OR AB7Check to see if the character in Register A is an end of the BASIC line character
2F54-2F5BJump to 2F5FH if the character in Register A is an end of the BASIC line character
2F5CDEC D15Decrement the number of times to perform the operation in Register D (as initially specified by the user by entering a number before the command)
2F5F-2F60- ↳ TYPSLHLD A,21HLD A,"!"3E 21Load Register A with an !
2F64RETC9RETurn to CALLer
2F65 - EDITCommand - CHANGE Logic- "CED".
2F65- ↳ CEDLD A,(HL)7ELoad Register A with the character at the location of the input buffer pointer in HL
2F66OR AB7Check to see if the character in Register A is an end of the BASIC line character
2F67RET ZC8Return if the character in Register A is an end of the BASIC line character
2F68-2F6AGo get the character to put in the input buffer from the keyboard
2F6BLD (HL),A77Save the character in Register A at the memory location of the input buffer pointer in HL
2F6FINC HL23Bump the value of the input buffer pointer in HL
2F70INC B04Bump the character position in Register B
2F71DEC D15Decrement the number of times to perform the operation in Register D (as initially specified by the user by entering a number before the command)
2F74RETC9RETurn to CALLer
2F75 - EDITCommand - HACK/INSERT Logic- "HED"
2F75-2F76- ↳ HEDLD (HL),00H36 00Set the line end to be the current position.
2F77LD C,B48Load Register C with the character position in Register B which will now be the line length
2F78-2F79- ↳ XEDLD D,0FFH16 FFPrepare for the next CALL to find the end of the line by loading Register D with the number of times to perform the operation
2F7D-2F7F- ↳ IEDGo get the character to be inserted from the keyboard
2F80OR AB7Check to see if a key was pressed
2F84-2F85CP 08HFE 08Check 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-2F87Jump to 2F92H if the character in Register A is a backspace character
2F88-2F89CP 0DHFE 0DCheck 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-2F8CJump to 2FE0H if the character in Register A is a carriage return
2F8D-2F8ECP 1BHFE 1BCheck 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.
2F8FRET ZC8Return if the character in Register A is shift up arrow
2F92 - EDITCommand - BACKSPACE CURSOR Logic- "TYPARW".
2F92-2F93- ↳ TYPARWLD A,08H3E 08Load Register A with a backspace the cursor character
2F94- ↳ TYPAR1DEC B05Decrement the character position in Register B
2F95INC B04Bump the character position in Register B
2F96-2F97If this is the first character of the BASIC line Jump forward to 2FB7H
2F9BDEC HL2BDecrement the value of the input buffer pointer in HL
2F9CDEC B05Decrement the character position in Register B
2FA0PUSH DED5Save 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- ↳ DELCHRPUSH HLE5Save the value of the input buffer pointer in HL to the STACK
2FA2DEC C0DDecrement the character position in Register C
2FA3- ↳ CMPRSSLD A,(HL)7ELoad Register A with the character at the location of the input buffer pointer in HL
2FA4OR AB7Check to see if the character in Register A is an end of the BASIC line character
2FA5SCF37Set the Carry flag to signal that DELCHR was called
2FA6-2FA8If the character in Register A is an end of the BASIC line character then we are done compressing so Jump to 0890H
2FA9INC HL23Bump the value of the input buffer pointer in HL
2FAALD A,(HL)7ELoad Register A with the character at the location of the input buffer pointer in HL
2FABDEC HL2BDecrement the value of the input buffer pointer in HL
2FACLD (HL),A77Save the character in Register A at the location of the current input buffer pointer in HL
2FADINC HL23Bump the value of the input buffer pointer in HL
2FB0 - EDITCommand - ADD A CHARACTER Logic- "NTARRW".
2FB0- ↳ NTARRWPUSH AFF5Save the character to be inserted in Register A to the STACK
2FB1LD A,C79Load Register A with the number of characters in the input buffer (i.e., the length of the line) in Register C
2FB2-2FB3CP FFHCP BUFLENFE FFWe 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-2FB5Jump forward to 2FB9H if the maximum BASIC line length hasn't been reached
2FB6POP AFF1Get the character to be inserted from the STACK and put it in Register A
2FB9- ↳ OKINSSUB B90Subtract 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
2FBAINC C0CBump the number of characters in the input buffer in Register C
2FBBINC B04Bump the character position in Register B
2FBCPUSH BCC5Save the character position and the number of characters in the input buffer in BC to the STACK
2FBDEX DE,HLEBLoad DE with the input buffer pointer in HL
2FBELD L,A6FLoad Register L with the number of bytes to move
2FBF-2FC0LD H,00H26 00Zero Register H so that the number of bytes to move can be done in a 16 bit Register Pair.
2FC1ADD HL,DE19Add the value of the input buffer pointer in DE to the character count in HL
2FC2LD B,H44Load Register B with the MSB of the end of the BASIC line pointer in Register H
2FC3LD C,L4DLoad Register C with the LSB of the end of the BASIC line pointer in Register L
2FC4INC HL23Bump the value of the end of the BASIC line pointer in HL
2FC8POP BCC1Get the character position and the number of characters in the input buffer from the STACK and put it in BC
2FC9POP AFF1Get the character to be inserted from the STACK and put it in Register A
2FCALD (HL),A77Save the character in Register A at the location of the current input buffer pointer in HL
2FCEINC HL23Bump the value of the input buffer pointer in HL
2FD2 - EDITCommand - BACKSPACE Logic- "DELED".
2FD2- ↳ DELEDLD A,B78Top 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
2FD3OR AB7Check to see if this is the start of the BASIC line
2FD4RET ZC8Return if this is the start of the BASIC line
2FD5DEC B05Decrement the character position in Register B
2FD6DEC HL2BDecrement the value of the buffer pointer in HL
2FD7-2FD8LD A,08H3E 08Load Register A with a backspace the cursor character
2FDCDEC D15Decrement the number of times to perform the operation in Register D
2FDFRETC9RETurn to CALLer
2FE0-2FE2- ↳ CREDSince 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
2FE6POP BCC1Clean up the STACK (to remove the DISPED return address)
2FE7POP DED1Get the BASIC line number (in binary) from the STACK and put it in DE
2FE8LD A,D7ALoad Register A with the MSB of the BASIC line number in Register D
2FE9AND EA3Combine the LSB of the BASIC line number in Register E with the MSB of the BASIC line number in Register A
2FEAINC A3CBump the combined BASIC line number in Register A
2FEB-2FED- ↳ EDITRTLD HL,(40A7H)LD HL,(BUFPNT)2A A7 40Load HL with the starting address of the input buffer.
Note: 40A7H-40A8H holds the input Buffer pointer
2FEEDEC HL2BDecrement the value of the input buffer pointer in HL
2FEFRET ZC8Return if this is the Level II BASIC command mode
2FF0SCF37Set the Carry flag to to fool the INSERT code; this flags the line number has having been seen
2FF1INC HL23Bump the value of the input buffer pointer in HL
2FF2PUSH AFF5Save the command mode flag in AF to the STACK
2FF6 - EDITCommand - QUIT Logic- "QED".
3000 - Jump Table.
3012 - Model 4 ROM Gen 1
3012 - Model 4 ROM Gen 2
*3024 - Model 4 ROM Gen 1
*3027RET
NOP
NOPI/O Re-Router was removed from the Model 4 ROM.
*3024 - Model 4 ROM Gen 2
*302D - Model 4 ROM Gen 1
*302D - Model 4 ROM Gen 2
*302DJP 375CHC3 5C 37Jump to 375CH for a routine which parses whether the current instruction on a the current line is in quotes.
3036Jump to 35A0H to put the TIME 10 characters from the upper right hand corner of the screen.
3039IN A,(0E4H)Poll Port E4H into A.
NOTE: Port E4H is the Non-Maskable Interrupt Latch.
303BBIT 5,ATest Bit 5 of Register A. Bit 5 of the NMI on an Input test is the RESET STATUS, with 0=False, and 1=True
*3042 - Model 4 ROM Gen 1 - Prompt the User to set the cassette baud rate.
*3042 - Model 4 ROM Gen 2 - Prompt the User to set the cassette baud rate.
*3045-3064 - Model 4 ROM Gen 1
*3045-305F@abcdefghijklmnopqrstuvwxyzKeyboard Rows 0-3, Unshifted, No Caps Lock
*3060NOPComputer version number, which is always 1 for a Model III and 0 for a Model 4
*3061NOP
*3062NOP
*3063NOP
*3064NOP
*3065-307D30 31 32 33 34 35 36 37 38 39 3A 3B 2C 2D 2E 2F 0D 1F 01 5B 0A 08 09 20 00Keyboard Rows 4-6, Unshifted, No Caps Lock
*307ENOP
*307FNOP
*3080NOP
*3081NOP
*3082NOP
*3083NOP
*3084NOP
*3085-309F60 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 5AKeyboard Rows 0-3, shifted, No Caps Lock
*30A0NOP
*30A1NOP
*30A2NOP
*30A3NOP
*30A4NOP
*30A5NOP
*30A6-30BC21 22 23 24 25 26 27 28 29 2A 2B 3C 3D 3E 3F 0D 1F 01 1B 1A 18 19 20Keyboard Rows 4-6, shifted, No Caps Lock
*30BDNOP
*30BENOP
*30BFNOP
*30C0NOP
*30C1NOP
*30C2NOP
*30C3NOP
*30C4NOP
*30C5-30DF40 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 5AKeyboard Rows 0-3, UNshifted, Caps Lock.
*30E0NOP
*30E1NOP
*30E2NOP
*30E3NOP
*30E4NOP
*30E5-30FC30 31 32 33 34 35 36 37 38 39 3A 3B 2C 2D 2E 2F 0D 1F 01 5B 0A 08 09 20Keyboard Rows 4-6, UNshifted, Caps Lock
*30FDNOP
*30FENOP
*30FFNOP
*3100NOP
*3101NOP
*3102NOP
*3103NOP
*3104NOP
*3105H - Model 4 Gen 1 jump to NON-Disk BASIC
GOSUB to the $SETCAS routine which prompts the user to set the cassette baud rate (310BH - 313AH)
*310BH - Model 4 Gen 1 Set the TAPE BAUD RATE ($SETCAS).
*310CGOSUB to 312DH which loads A with a carrage return, and jumps to 0033H to display it.
*3115GOSUB to 0049H.
NOTE: 0049H is the $KBWAIT routine which scans the keyboard and returns with the key pressed, if any, in register A.
*3118CP 0DHCompare A and 0D (a CARRIAGE RETURN).
*311CPUSH AFSave AF to the STACK.
NOTE: A currently holds the character pressed in response to the "CASS?" message.
*311DGOSUB to 0033H.
NOTE: 0033H is the character print routine, to put the character held in Register A at the current cursor position.
*3120POP AFRestore AF from the STACK.
NOTE: A will then hold the character pressed in response to the "CASS?" message.
*3121CP 48HCompare A with 48H (ASCII: H).
*3125CP 4CHCompare A with a 4CH (ASCII: L).
3129H - Model 4 Gen 1 Set the Selected Cassette Baud Rate as LOW SPEED
*3129XOR ASet 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
LD (4211H),ASave 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
*312FDisplay 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
Finish the cassette setup by calling a portion of the keyboard scan routine which CALLS the screen print routine at 34FDH
*313EXOR AClear Register A and All Flags
*313FRETReturn a null character.
*3140NOP
*3141NOP
*3142NOP
*3143NOP
*3144NOP
*3145 - Model 4 Gen 1 Printer Character Table Codes 32-127.
*3145-31A420 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.
*3045JP 378DHC3 8D 37Jump 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
*3048JP 377AHC3 7A 37Jump to 377AH to check to see if we are on a new printable page and set the pointers accordingly. CALLED from 0431 and 0445
*3045 - Model 4 ROM Gen 2 Keyboard Rows 0-3, Unshifted, No Caps Lock.
*304E-3068@abcdefghijklmnopqrstuvwxyz
*3069NOP
*306ANOP
*306BNOP
*306CNOP
*306DNOP
*306E-308530 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.
*3086IN A,(0EAH)DB EAPoll the RS-232 UART Control Register and Status Register (at Port EAH) and put the results into Register A
*3087OR AB7Set the FLAGS based on the RS-232 UART Control Register and Status Register
*308C-308DJR 30C6H18 38If 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.
**3086LD A,(3810H)3A 10 38Fetch the Keyboard Matrix 0-7 into Register A to check for certain keys.
**3089AND 10HE6 10MASK 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.
**306CNOP
**306DNOP
**306DNOP
*308E-30A8 - Model 4 ROM Gen 2 - Continuing with the Keyboard Table
*308E-30A860 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
*30A9NOP
*30AANOP
*30ABNOP
*30ACNOP
*30ADNOP
*30AENOP
*30AF-30C521 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-30C7IN A,(0EBH)DB EBPoll the RS-232 Register at Port EBH and put the results into Register A.
*30C8RETC9
**30C6-30C8 - Model 4 ROM Student Network Edition - A little code snippet
*30C6NOP
*30C7NOP
*30C8NOP
*3089 - Model 4 ROM Gen 2 - Continuing with the Keyboard Table
*30C9NOP
*30CA-30CDPUSH BC
POP BC
NOP
RETStandard code for a short delay
*30CE@ABCDEFGHIJKLMNOPQRSTUVWXYZ
*30E9NOP
*30EANOP
*30EBNOP
*30ECNOP
*30EDNOP
*30EE30 31 32 33 34 35 36 37 38 39 3A 3B 2C 2D 2E 2F 0D 1F 01 5B 0A 08 09 20Keyboard Rows 4-6, UNshifted, Caps Lock
*3106LD A,(3880H)3A 80 38Load A with the value held at 3880H (which are the SHIFT KEYS)
*3109LD HL,414FH21 F4 41Point HL to 414FH
*310CAND 7CHE6 7CMASK the value held in the SHIFT KEY RAM location against 0111 1100 to keep only bits 2, 3, 4, 5, and 6 live
*310EOR AB7OR A against itself to reset the flags
*310FJR Z,3142H28 31If the Z FLAG is set, JUMP to 3142H to continue checking fro special keys
*3111LD E,A5FPut the masked A into E.
*3112XOR (HL)AEToggle against the old image.
*3113LD (HL),E73Save the new image into (HL).
*3114AND EA3Mask Register E against Register A.
*3115JR Z,3157H28 40If ZERO then the LEFT SHIFT PRESSED was JUMP to 3157H to restart parsing the keyboard
*3117LD BC,05C4H01 C4 05Load BC with 05C4H to set up a 1/50 second delay for de-bounce.
*311ACALL 0060HCD 60 00GOSUB 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.
*311DLD A,(3880H)3A 80 38Load A with the value held at 3880H (which are the SHIFT KEYS)
*3120AND 7CHE6 7CMASK the value held in the SHIFT KEY RAM location against 0111 1100 to keep only bits 2, 3, 4, 5, and 6 live
*3122CP EBBCompare A with E.
*3126RLA17Rotate 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.
*3127RLA17Rotate 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.
*3128JR NC,312EH30 04We 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
*312ALD A,(41F3H)3A F3 41Load Register A with the value of the character to be returned when the F3 key is pressed
*312DRETC9RETurn to Caller
*312E - Model 4 Gen 2 - KEYBOARD Routine - Check and Process the F2 Key or Jump Away.
*312ERLA17Rotate 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.
*312FJR NC,3135H30 04If the NC FLAG is set, it isn't the F2 key, so JUMP to 3135H to test the F1 key
*3131LD A,(41ECH)3A EC 41Load Register A with the value of the character to be returned when the F2 key is pressed
*3134RETC9RETurn to Caller
*3135 - Model 4 Gen 2 - KEYBOARD Routine - Check and Process the F1 Key or Jump Away.
*3135RLA17Rotate 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.
*3136JR NC,313CH30 04If the NC FLAG is set, it isn't the F1 key, so JUMP to 313CH to keep checking special keys
*3138LD A,(41EBH)3A EB 41Load Register A with the value of the character to be returned when the F1 key is pressed
*313BRETC9RETurn to Caller
*33C4 - Model 4 Gen 2 - KEYBOARD Routine - Part of the Keyboard Scan Routine. Keep Checking Special Keys.
*313CRLA17Rotate 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.
*33CA - Model 4 Gen 1 - KEYBOARD Routine - Part of the Keyboard Scan Routine.
*3142LD (HL),A77Save the character held in Register A into the memory location pointed to by Register Pair HL
*3143LD A,FFH3E FFLoad A with FF to set up for a FLAG = 0FFH = NO CONTROL.
*3145LD HL,3840H21 40 38Load HL with 3840H to start a check for a DOWN ARROW.
*3148BIT 4,(HL)CB 66Test BIT 4 of (HL) to check for a DOWN ARROW.
*314AJR Z,3154H28 08JUMP to 3154H if the a DOWN ARROW was NOT pressed.
*314CSLA LCB 25Next we need to check for a a LEFT SHIFT so shift L left.
*314EBIT 0,(HL)CB 46Test BIT 0 of (HL) to check for a LEFT SHIFT.
*3152LD A,1FH3E 1FLoad A with 1F to set up for FLAG = CONTROL KEY.
*3154LD (4224H),A32 24 42Save the CONTROL FLAG into (4224H).
NOTE: 4224H Holds the CONTROL KEY flag.
*315ARET NCD0If that routine exited with NC FLAG set, RETurn
*315ECP 1AHFE 1ACheck A against 1AH to see if we have a SHIFT+DOWN ARROW.
*3163OR AB7Set the flags based on Register A
*3167LD HL,4224H21 24 42Set Register Pair HL to 4224H, which is the CONTROL KEY flag.
*316ABIT 7,(HL)CB 7ETest Bit 7 of the CONTROL KEY flag in RAM.
*316ECP 2AHFE 2ACheck A against a * key.
*3170JP Z,37C7HCA C7 37If 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.
*3173AND (HL)A6Prepare to check for a BREAK key by masking A against the memory contents of HL ...
*3174CP 01HFE 01... and COMPARING it to 01H.
*3176RET NZC0If the result of the compare is NOT zero, then RETURN.
*3177RST 28HEFIf we are here, then a BREAK key was hit, so call RST 28H to handle the BREAK key.
*3178RETC9RETURN to Caller
*3179 - Model 4 Gen 2 - Continuing Initialization Routine by setting up the RS-232.
*3179XOR AAFSet Register A to 0
*317AOUT (E8H),AD3 E8Output A to port E8H.
NOTE: Port E8H is the RS-232 Status Register & Master Reset. Outputting ANYTHING to Port E8H resets the RS-232.
*317CLD A,EEH3E EESet Register A to EEH for outputting to the RS-232 Baud Rate Select and Sense Switches: (Port E9H)
*317EOUT (E9H),AD3 E9Initialize 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
*3180LD A,6DH3E 6DSet Register A to 6DH for outputting to the RS-232 UART Control Register and Status Registe (Port EAH)
*3182OUT (EAH),AD3 EASent 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)
*3184IN A,(E8H)DB E8Poll the RS-232 Status Register and Master Reset of Port E8H and put the value into Register A.
*3186BIT 6,ACB 77Test Bit 6 of the RS-232 Status Register to check Data Set Ready (Pin 6 of the DB-25).
*3188JR Z,3184H28 FAIf the DATA SET READY is ZERO then LOP back 2 instructions to 3184 to poll again
*318ALD A,6CH3E 6CSet Register A to 6CH (0110 1100) for outputting to the RS-232 UART Control Register and Status Registe (Port EAH)
*318COUT (EAH),AD3 EASend 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
*318EIN A,(E8H)DB E8Poll the RS-232 Status Register and Master Reset of Port E8H and put the value into Register A.
*3190BIT 6,ACB 77Test Bit 6 of the RS-232 Status Register to check Data Set Ready (Pin 6 of the DB-25).
*3192JR NZ,318EH20 FAIf the DATA SET READY is ZERO then LOOP back 2 instructions to 318EH to poll again
*3194LD A,0FH3E 0FSet Register A to 0FH
*3196CALL 37D5HCD D5 37GOSUB to 37D5H to send the Character in Register A to the RS-232, once the RS-232 shows ready to accept that character.
*3199CALL 3086HCD 86 30GOSUB to 3086 to Poll the UART and wait for the P FLAG to not be set and then CONTINUE at 306CH
*319CCALL 3086HCD 86 30GOSUB to 3086 to Poll the UART and wait for the P FLAG to not be set and then CONTINUE at 306CH
*319FCALL 37D5HCD D5 37GOSUB to 37D5H to send the Character in Register A to the RS-232, once the RS-232 shows ready to accept that character.
*31A2-31A4JP 3517HC3 17 35JUMP 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
**3179LD A,(4210H)3A 10 42Get 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.
**317COR 10HF6 10OR against 10H (0001 0000) to turn all I/O ports on
**317ELD (4210H),A32 10 42Put the masked status back, first by loading it into 4210H
**3181OUT (ECH),AD3 EC... and then by sending it to 0ECH which is the same as 04210.
**3183LD A,08H3E 08Put an 08H into Register A
**3185OUT (D3H),AD3 D3Send 08H to Port D3H, which is the Network 4 Omninet MSB pointer
**3187XOR AAFPut an 00H into Register A
**3188OUT (D1H),AD3 D1Send 08H to Port D1H, which is the Network 4 Omninet LSB pointer
**318ALD BC,00D0H01 D0 00In 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
**318DLD HL,7000H21 00 70In further preparation for the INIR commands, point HL to the BUFFER for the code, which is 7000H
**3190PUSH HLE5Save the 7000H Buffer start point to the top of the stack
**3191INIRED B2Write a byte from port C to the memory location pointed to by HL, and then increase HL and decrease B.
**3193INIRED B2Write a byte from port C to the memory location pointed to by HL, and then increase HL and decrease B.
**3195INIRED B2Write a byte from port C to the memory location pointed to by HL, and then increase HL and decrease B.
**3197INIRED B2Write a byte from port C to the memory location pointed to by HL, and then increase HL and decrease B.
**3199INIRED B2Write a byte from port C to the memory location pointed to by HL, and then increase HL and decrease B.
**319BINIRED B2Write a byte from port C to the memory location pointed to by HL, and then increase HL and decrease B.
**319DINIRED B2Write a byte from port C to the memory location pointed to by HL, and then increase HL and decrease B.
**319FINIRED B2Write a byte from port C to the memory location pointed to by HL, and then increase HL and decrease B.
**31A1RETC9RETurn to CALLer
**31A2NOP00
**31A3NOP00
**31A4NOP00
31A5 - Output the TIMING MARK to the cassette
31A5LD A,01HLoad A with 01H. This is to prepare to send 0.46V to tape.
31A7OUT (0FFH),ALoad 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.
31A9LD B,0DHLoad B with 0DH in as a loop counter.
31ADLD A,02HLoad A with 01H. This is to prepare to send 0.0V to tape.
31AFOUT (0FFH),ALoad 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.
31B1LD B,0DHLoad B with 0DH in as a delay.
31B5GOSUB to 31F3 to send 0.46V to tape.
NOTE: 31F3H resets the cassette port, and then output a 0 to the Cassette Port FFH.
31B8LD B,78HLoad B with 78H in as a delay.
31BCRETRETURN.
31C0 - Turn Off The Cassette
31C0LD A,(4213H)Load A with the memory contents of 4213H.
NOTE: 4213H is the default interrupt vector setting for the cassette.
31C3OUT (0E0H),AOutput 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
31C5IN 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.
31C7LD 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.
31CAAND FDHMask A against FDH (1111 1101) to zero bit 1.
31CFEIEnable Interrupts.
31D0RETRETURN.
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 "**"
31D1EX DE,HLSwap DE and HL to remove the return address.
31D2EX (SP),HLSwap the memory contents pointed to by the STACK POINTER and HL (which is now what DE was).
31D3PUSH BCSave BC to the STACK.
31D4PUSH HLSave HL to the STACK.
31D5EX DE,HLSwap DE and HL back.
31D6IN 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.
31D8LD DE," "Load DE with SPACESPACE.
31DBLD (3C3EH),DELoad the screen memory location of 3C3EH with DE.
31E8 - Turn On The Cassette - Actually Set the Bit Mask and Output the Command
31E8LD 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.
31EBOR 02HOR A with 02H (0000 0010) to set Bit 1.
31EDLD (4210H),ASave 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.
31F0OUT (0ECH),AOutput that Bit Mask to port 0ECH.
31F2RETRETURN.
31F3 - Reset the Cassette Port. This routine OUTputs a 0 to the Cassette Port FFH
31F3XOR AWe want to reset the cassette port so we zero A.
31F4OUT (0FFH),ALoad 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.
31F6RETRETURN.
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.
31F7LD A,(HL)Load Register A with the memory contents pointed to by Register Pair HL.
31F8SUB 23HSubtract 23H so that we can test to see if the caller was a PRINT #command.
31FAIf 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.
3200RST 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.
3202RETIf we are here, then we have PRINT #n,so we RETURN.
3203 - Vector for a SLOW cassette read
3203LD B,08HLoad B with 8, representing the need to LOOP for 8 bits.
3205GOSUB to 3220H.
NOTE: 3220H reads the tape until it finds a timing mark or the BREAKis hit.
320ALD A,(4212H)Put the contents of 4212H into A.
NOTE: 4212H holds the cassette blinker counter.
320DINC ABump A.
320EAND 5FHMask A against 5F (0101 1111) to turn Bit 7 and Bit 5 off.
3210LD (4212H),APut A into 4212H.
NOTE: 4212H holds the cassette blinker counter.
3215LD A,(3C3FH)Load A with the screen contents at position 3C3FH.
3218XOR 0AHXOR 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).
321ALD (3C3FH),APut the revised A onto the screen at position 3C3FH.
321DLD A,DPut D (the byte) into A.
3220 - Cassette - Keep reading tape looking for a timing mark or BREAK.
3220PUSH BCSave BC to the STACK.
3221IN A,(FFH)Poll Port FFH with the results into Register A.
NOTE: FFH is the Cassette Port.
3223RLAPut 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).
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)
322ELD B,6EHLoad B with 6EH, which is the length of the timing mark.
Reset the cassette port
Next, wait for 98H Units (the length until a data pulse is expected)
3235LD B,98HLoad B with 98H, which is when the next data pulse should be available.
... continue
3239IN A,(FFH)Poll Port FFH and put the results into A.
NOTE: Port FFH is the Cassette Port.
323BPOP BCRestore BC from the STACK.
323CRLARotate 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.
323DRL DRotate 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.
3241 - Vector for a SLOW cassette write. On entry A is the byte to output.
3241PUSH AFSave AF to the STACK.
3242PUSH BCSave BC to the STACK.
3243PUSH DESave DE to the STACK.
3244LD C,08HLoad C with an 8, representing 8 bits to be written.
3246LD D,ALoad D with A.
NOTE: D will be the DATA BYTE.
324ARLC DRotate 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.
324CIf 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.
3251DEC CReduce the counter holding the number of bits to deal with by 1.
3254POP DERestore DE from the STACK.
3255POP BCRestore BC from the STACK.
3256POP AFRestore AF from the STACK.
3257RETRETURN.
3258 - "Write a 0 Bit" by simply waiting the appropriate amount of time and doing nothing.
325E - SLOW tape header write
325EPUSH HLSave HL to the STACK.
3262LD (420CH),HLLoad the memory contents of 420CH with HL.
NOTE: 420CH is the TAPE WRITE VECTOR.
3265LD B,53HLoad B with 53H (Decimal: 83) in prepartion to output 83 ZEROes.
3267XOR AClear A and all flags.
326DLD A,0A5HLoad A with A5H.
NOTE: A5H is the OUTPUT SYNC BYTE.
3274 - SLOW tape header read
3274PUSH HLSave HL to the STACK.
3278LD (420EH),HLPut the vector for a slow cassette read into the memory location at 420EH.
NOTE: 420EH is the TAPE READ VECTOR.
327BLD B,40HLoad B with 40H to set up a loop of 64 to try to find 64 zeroes.
327DLD D,00HLoad D with 0.
3282LD A,DLoad A with the D (the data byte) to begin to check the current data byte.
3283OR ASet up the flags.
3288GOSUB to 3220H.
NOTE: 3220H reads the tape until it finds a timing mark or the BREAKis hit.
328BLD A,DLoad A with the D (the data byte) to begin to check the current data byte.
328CCP 0A5HCompare A to A5H looking for a SYNC BYTE.
3290LD HL,"**"In preparation to display a "**", load HL with **.
3293LD (3C3EH),HLPut HL onto the screen at location 3C3EH.
3296LD A,HLoad A with H (which is a *.
3297POP HLRestore HL from the STACK.
3298POP BCRestore BC from the STACK.
3299POP DERestore DE from the STACK.
329ARETRETURN.
329B - FAST tape header write.
329BPUSH HLSave HL to the STACK.
329FLD (420CH),HLLoad the memory contents of 420CH with HL.
NOTE: 420CH is the TAPE WRITE VECTOR.
32A2LD B,00HLoad B with 00H to set up a loop of 256 to output 256 "55H" bytes.
32A4LD A,55HLoad A with "55H".
32A6GOSUB to 32B4H.
NOTE: 32B4 restore all registers from the STACK, and Fill C with A, and JUMP to cassette write
32ABLD A,7FHLoad A with 7FH.
NOTE: 7FH is the OUTPUT SYNC BYTE.
32ADGOSUB to 32B4H.
NOTE: 32B4 restore all registers from the STACK, and Fill C with A, and JUMP to cassette write
32B0LD A,A5HLoad A with A5H.
NOTE: A5H is the SLOW SYNC BYTE.
32B4 - Restore all registers from the STACK, and Fill C with A, and JUMP to cassette write.
32B4PUSH AFSave AF to the STACK.
32B5PUSH BCSave BC to the STACK.
32B6PUSH DESave DE to the STACK.
32B7LD C,ALoad C with A.
32BA - Save all registers to the STACK, and Fill C with A, GOSBUB to write out the START BIT ...
32BAPUSH AFSave AF to the STACK.
32BBPUSH BCSave BC to the STACK.
32BCPUSH DESave DE to the STACK.
32BDLD C,ALoad C with A.
32C1 - Call 3335H to Output a Bit 8 Times
32C1LD B,08HLoad B with an 8 to set up a loop for 8 bits.
32CA - Read the start bit, read 8 bits, check for error, and flash the star
32CDLD B,08HLoad B with an 8 to set up a loop for 8 bits.
32DA - FAST tape header read.
32DAPUSH HLSave HL to the STACK.
32DELD (420EH),HLLoad the TAPE READ VECTOR (memory location of 420EH) with the 32CAH.
32E1LD A,01HLoad A with a 1 to set the interrupt.
32E3OUT (0E0H),AOUTPUT 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
32E5LD B,80HSet up a loop of 80H (128) to try to read 128 bits.
32EALD A,CLoad A with C (which is the pulse width).
32EBCP 0FHCompare A to 0FH to see if the pulse width was too short.
32EFCP 3EHCompare A to 3EH to see if the pulse width was too long.
32F8LD B,40HSet up a loop of 40H (Decimal: 64) to try to read 64 bits.
3300LD D,CLoad D with C (which holds the delay count).
3304LD A,DLoad A with D to set up to find the difference in the delays.
3305SUB CSubtract C (the delay count) from A (which holds D).
3308NEGA = 0 - A.
330ACP 0DHCompare 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.
330CIf the CARRY FLAG is set, JUMP to 3313H which will bump L and continue the 64 bit loop.
330EINC HBump HL since we have one more zero bit.
3313INC LBump L since we have one more bit.
3316LD A,40HLoad A with 40H (64).
3318CP HCompare A with H to check for bits.
331BCP LCompare with A to check for one bits.
331ELD A,02HLoad A with 2 to set the interrupt vector.
3320OUT (0E0H),AOutput 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
3325LD D,00HZero D.
332DLD A,DLoad A with D (the read byte).
332ECP 7FHCompare A against 7F to check for a MARKER BYTE.
3335RLC CWe 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.
3341DEC DDecrement D as DELAY #1.
3344LD A,02HLoad A with a 2 to set up for a write of 0 VOLTS to TAPE.
3346OUT (0FFH),ALoad 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.
3348DEC EDecrement E as DELAY #2.
334BLD A,01HLoad A with 01H to prepare to send 0.85 VOLTS to TAPE.
334DOUT (0FFH),ALoad 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.
334FRETRETURN.
3350H - READ a BIT
3350EIEnable Interrupts.
3351LD C,00HLoad C with 0.
3353INC CBump C.
3354LD A,(3840H)Load A with the contents of 3840H so as to check for a BREAK.
3357AND 04HMask A with 4 (0000 0100).
335BDIDisable Interrupts.
335FLD (3C3EH),HLPut the "BK" in HL onto the video screen at 3C3EH.
3362JUMP 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
3365LD E,01HLoad E with 01H (Binary: 0000 0001) to make the bit go high.
3369LD E,00HLoad E with 0 to make the bit go low.
336BLD A,06HLoad A with 6.
336DADD A,CAdd A (6) to C, so COUNT = COUNT + 6.
336ELD C,APut the count held in A back into C.
336FIN A,(FFH)Poll FFH (to get the level) into A.
NOTE: FFH is the Cassette Port.
3371AND 01HMask A with 1 (0000 0001) to keep only Bit 0.
3373CP ECompare A with E (which was the set level).
3376POP AFRestore AF from the STACK.
3377POP AFRestore AF (the REMOTE CALLER'S ADDRESS) from the STACK.
3378RETRETURN to the caller's caller.
3379POP AFRestore AF from the STACK.
337AEIEnable Interrupts.
337BRETRETURN back top the loop.
337CH - Check for a Data Error.
337CLD A,CLoad A with C (the count).
337DCP 22HCompare 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
337FRL DWe 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.
3381CP 0FHMake 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.
3383If A < 0FH then it was too quick and we have a data error so JUMP forward a few instructions 3388H.
3385CP 3EHCompare A against 3EH to make sure it was not too slow.
3387RET CIf it wasn't too slow, RETURN.
3388LD A,44HIt was too slow, so load A with a D.
338ALD (3C3EH),APut the "D" on the screen at video location 3C3EH.
338DRETRETURN.
*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).
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).
*3391LD A,(3880H)Load A with the value held at 3880H (which are the SHIFT KEYS)
*3394AND 7CHMASK the value held in the SHIFT KEY RAM location against 0111 1100 to keep only bits 2, 3, 4, 5, and 6 live
*3396OR AOR A against itself to reset the flags
*3399LD E,APut the masked A into E.
*339AXOR (HL)Toggle against the old image.
*339BLD (HL),ESave the new image into (HL).
*339CAND EMask Register E against Rgister A.
*33A2GOSUB 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.
*33A5LD A,(3880H)Load A with the value held at 3880H (which are the SHIFT KEYS)
*33A8AND 7CHMASK the value held in the SHIFT KEY RAM location against 0111 1100 to keep only bits 2, 3, 4, 5, and 6 live
*33AACP ECompare A with E.
*33AERLARotate 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.
*33AFRLARotate 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.
*33B0We 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
*33B2LD A,(41F3H)Load Register A with the value of the character to be returned when the F3key is pressed
*33B5RETRETurn to Caller
*33B6 - Model 4 Gen 1 - KEYBOARD Routine - Check and Process the F2 Key or Jump Away.
RLARotate 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.
*33B9LD A,(41ECH)Load Register A with the value of the character to be returned when the F2key is pressed
*33BCRETRETurn to Caller
*33B6 - Model 4 Gen 1 - KEYBOARD Routine - Check and Process the F1 Key or Jump Away.
RLARotate 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.
*33BEIf the NC FLAG is set, it isn't the F1, so JUMP to 33C4 to keep checking special keys
*33C0LD A,(41EBH)Load Register A with the value of the character to be returned when the F1key is pressed
*33C3RETRETurn to Caller
*33C4 - Model 4 Gen 1 - KEYBOARD Routine - Part of the Keyboard Scan Routine. Keep Checking Special Keys.
RLARotate 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.
*33CA - Model 4 Gen 1 - KEYBOARD Routine - Part of the Keyboard Scan Routine.
LD (HL),ASave the character held in Register A into the memory location pointed to by Register Pair HL
33CBLD A,FFHLoad A with FF to set up for a FLAG = 0FFH = NO CONTROL.
33D0BIT 4,(HL)Test BIT 4 of (HL) to check for a DOWN ARROW.
33D4SLA LNext we need to check for a a LEFT SHIFTso shift L left.
33D6BIT 00H,(HL)Test BIT 0 of (HL) to check for a LEFT SHIFT.
33DALD A,1FHLoad A with 1F to set up for FLAG = CONTROL KEY.
33DCLD (4224H),ASave the CONTROL FLAG into (4224H).
NOTE: 4224H Holds the CONTROL KEY flag.
*33E2RET NCIf that routine exited with NC FLAG set, RETurn
33E6CP 1AHCheck A against 1AH to see if we have a SHIFT DOWN ARROW.
*33EBOR ASet the flags based on Register A
*33F2BIT 7,(HL)Test Bit 7 of the CONTROL KEY flag in RAM.
*33F6CP 2AHCheck A against a *.
*33FBAND (HL)Prepare to check for a BREAKby masking A against the memory contents of HL ...
*33FERET NZIf the result of the compare is NOT zero, then RETURN.
*33FFRST 28HIf we are here, then a BREAKwas hit, so call RST 28H to handle the BREAK.
*3400RETRETURN to Caller
*3401 - Model 4 Gen 1 - This is the BOOTSTRAP. Clears ports, checks for a BREAKkey and a Floppy Controller.
3401IM 1Set the INTERRUPT MODE to 1.
*3406LD B,0FHLet Register B = 0FH
*3408LD C,88HLet Register C = the CRT Controller Control Register Port
*340COUT (89H),ASend Register A to the CRT Controller Data Register
3410OUT (0E4H),AOutput A to Port E4H.
NOTE: Port E4H is the non-maskable interrupt latch. This is to clear the non-maskable interrupt status.
3412OR 20HOR 20H (0010 0000) against A to turn on Bit 5.
3414OUT (0ECH),AOutput 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.
3416LD A,81HLoad A with 81H (Decimal 129, Binary 10000001).
3418OUT (0F4H),AOutput 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.
341ALD A,D0HLoad A with D0H (Decimal: 208, Binary: 1101 0000).
341COUT (0F0H),AOutput "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
*341EPUSH BCUndertake a short delay of PUSHING BC, POPPING BC, and NOPing
*341FPOP BC
*3420NOP
3421LD A,04HLoad A with a 4 (Binary: 0000 0100).
3423OUT (0E0H),AOutput 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
3425LD A,0BHLoad A with 0BH (Binary: 00001011).
3427OUT (0F0H),AOutput 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).
3429Next we need to initialize some ports via a LDIR. The next 4 commands will move the 76 (4C) bytes from 36AAH to 4000H.
342C
342F
3432LDIR
3434Next we need to initialize more ports via a LDIR. The next 4 commands will move the 64 (40) bytes from 36F9H to 41E5H.
3437
343A
343DLDIR
3448IN 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
344AINC ABump the FDC Status by 1 (which would turn a FFH (128) into a 00H), making a Z flag mean "Disk Controller Not Ready".
344BIf the FDC Stats + 1 is zero, then we have no disk controller, so jump to Non-Disk BASIC at 3105H.
344EIf we are here, then we have a floppy controller, so set up for a loop of 65,535 times.
3451DEC BCDecrease BC by 1.
3452LD A,81HSet A to 81H (Decimal: 10000001).
3454OUT (0F4H),ASend 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.
3456LD A,B
OR CTo 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.
345BIN 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
345DBIT 2,ATest Bit 2 of A (the Floppy Status). If it is 0 then we haven't made it to track 0, otherwise we have.
345FStill 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.
3461LD E,05HSet up for a loop of 5 using Register E.
3466IN 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
3468BIT 01H,ACheck A to see if bit 1 (meaning "Drive Busy") is set.
346CDEC BCIf bit 1 is not set (meaning, the drive is NOT busy), reduce the counter (BC) by 1.
346DLD A,81HLoad A with 81H (Decimal: 129, Binary: 1000 0001).
346FOUT (0F4H),AOutput 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.
3471LD A,B
OR CTo 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.
3475If 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.
347BIf 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.
347DDEC EIf we are here, it found a diskette so next we need to find the index mark. First decrement E by 1.
3463HLD A,81HLoad A with 81H (Decimal: 129, Binary: 1000 0001).
3482OUT (0F4H),AOutput 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.
3484We 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.
3487LD (404AH),HL
348ALD A,C3HLoad A with C3H (Decimal: 195, Binary 1100 0011).
348CLD (4049H),APut the C3H into 4049H.
NOTE: 4049H is the Non-Maskable Interrupt Vector.
348FLD A,80HLoad A with 80H (Decimal: 128, Binary 1000 0000).
3491OUT (0E4H),AOutput "1000 0000" to Port E4H.
NOTE: Port E4H is the non-maskable interrupt latch.
3499LD A,01HPrepare to read Sector 1 by loading A with a 1.
349BOUT (0F2H),AOutput 1 to Port F2H.
NOTE: Port F2H is the Floppy Disk Controller Track Register.
349DLD A,80HPrepare to read a single sector by loading A with 80H (Decimal: 128, Binary: 1000 0000).
349FOUT (0F0H),AOutput "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).
34A1GOSUB to 37E1H to delay 3 instruction cycles, arguably the time it will take for the FDC to respond to the port command.
34A4IN 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
34A6AND 02HMask 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.
34ABINIInput the data byte.
34ADLD A,81HLoad A with 81H (Decimal: 129, Binary: 1000 0001) to select disk 0.
34AFOR 40HOR A with 40H (Binary: 0100 0000) to set A to double-density.
34B1OUT (0F4H),AOutput 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.
34B3INIInput the data byte.
34B5Loop 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.
34B8XOR AClear A and all flags.
34B9OUT (0E4H),ASend a 0 to Port E4H.
NOTE: Port E4H is the non-maskable interrupt latch. This is to clear the non-maskable interrupt status.
34BELD (4049H),HLLoad "45EDH" into 4049H.
NOTE: 4049H is the Non-Maskable Interrupt Vector.
34C1GOSUB to 37E1H to delay 3 instruction cycles, arguably the time it will take for the FDC to respond to the port command.
34C4IN 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
34C6POP HLClean up the Stack.
34C7AND 1CHMask 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.
34CCIf 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.
34CEIf 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.
34D1IN A,(E4H)Poll Port E4H into A.
NOTE: Port E4H is the Non-Maskable Interrupt Latch.
34D3BIT 5,ATest Bit 5 of Port E4 against A.
NOTE: Port E4H is the Non-Maskable Interrupt Latch.
*34DA - Model 4 Gen 1 - Part of the Keyboard routine.
*34DDAND 03HMask the SHIFT Register against 03H (Binary: 0000 0011) to keep only Bits 0 and 1 to check for shifts.
*34DFIf the masked A is 0, then we have no shifts, and skip the next instruction (to 34E3H).
*34E1SET 6,DSet BIT 6 of D to offset D for shifts.
LD A,(4019H)Load A with the contents of memory location 4019H to check for CAPS LOCK.
NOTE: 4019H is the CAPS LOCK TOGGLE.
*34E6OR ASet the flags.
*34E9SET 7,DSet 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.
*34EBLD A,(3880H)Put the contents of memory location 3880H into A to GET SHIFT(S).
*34EEAND 03HMask the SHIFT Register against 03H (Binary: 0000 0011) to keep only Bits 0 and 1 to check for shifts.
*34F0If the masked A is 0, then we have no shifts, and skip the next instruction (to 34F4H).
*34F2RES 7,DRESET 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.
34F7LD E,DWe need DE to be the OFFSET, so load E with D and ...
34F8LD D,00H... load D with 00.
34FAADD HL,DEAdd DE (the offset over the keyboard table) to HL (the keyboard table).
34FBLD A,(HL)Get the character pointed to by (HL) and put it into A.
*34FCRETRETurn 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.
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.
*3501RLCARotate the contents of A left one bit position. The contents of bit 7 are copied to the carry flag and bit 0.
*3504RLCARotate the contents of A left one bit position. The contents of bit 7 are copied to the carry flag and bit 0.
*3505If the CARRY FLAG is set, we have a non-graphic characters, so skip the next instructions.
*3507LD A,2EHOverwrite the current character held in Register A with a ., so that all graphic characters are printed as .'s.
Call the PRINT CHARACTER routine at 003BH (which sends the character in the A register to the printer).
*350FINC HLBump HL to the next character on the screen.
*3510BIT 6,HCheck 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).
*3515LD A,LPrepare to test of end of line by loading Register A with Register L.
*3516AND 03FHAND 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).
*3518If 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.
*351AGOSUB 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.
*351DLoop 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
*351FNOP
*3520NOP
*3521NOP
*3522NOP
*3523NOP
*3524NOP
*3525NOP
*3526NOP
*3527NOP
*3528NOP
*338EH - Model 4 Gen 2 - Jump Point for Keyboard Input.
*338ELD BC,3801H010138Load BC with 3801H (KEYBOARD ROW 0).
*3391LD HL,4036H213640Load HL with 4036H (BUFFER ROW 0).
*3394LD D,00H1600Load D with 0 (so D = ROW 0).
*3396LD A,(BC)0Aoad A with the contents held in (BC) to check the keyboard row.
*3397LD E,A5FLoad E with the contents held in (BC) to check the keyboard row.
*3398XOR (HL)AEXOR (HL) to set changed bits.
*3399LD (HL),E73Save the scan back into (HL).
*339AAND EA3Mask A with E (to mask the released keys).
Go to the next row
*339DINC D14Bump D so that D holds the NEXT row number.
*339EINC HL23Bump HL so that HL holds the NEXT buffer location.
*339FRLC CCB01We 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.
*33A1JP P,3396HF29633If 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
*33A4LD A,(41FDH)3AFD41If 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.
*33A7LD L,A6FLoad L with the contents of Register A.
*33A8LD A,(41FEH)3AFE41Load A with the memory contents of 41FEH.
NOTE: 41FEH is the saved IMAGE at the saved position in the keyboard scan data.
*33ABAND (HL)A6MASK A against (HL) to see if the previous keys are still pressed.
*33AFSBC HL,HLED62Zero HL by subtracting HL from HL.
*33B1LD (4201H),HL220142Load 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.
*33B7LD (41FFH),HL22FF41Load the memory location at 41FFH with 05DCH.
NOTE: 41FFH holds the keyboard scan repeat delay count.
*33BAXOR AAFClear A and all flags.
*33BBRETC9RETurn to Caller
*33BC - Model 4 Gen 2 - Keyboard Routine - If the same keys are still pressed then we need to deal with debounce.
*33BCLD E,A5FLoad E with A.
*33BDPUSH BCC5Save BC to the STACK.
*33C1CALL 0060HCD 60 00GOSUB 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.
*33C4POP BCC1Restore BC from the STACK.
*33C5LD A,(BC)0ALoad A with the memory contents pointed to by BC to re-check the keyboard.
*33C6AND EA3Compare A against E to check the pattern.
*33C7RET ZC8If not the same pattern then RETURN.
*33C8LD (41FEH),A32 FE 41If it is the same pattern then save A into (41FEH).
NOTE: 41FE is the SAVED IMAGE AT POSITION.
*33CBLD A,L7DLoad A with L (the scan position).
*33CCLD (41FDH),A32 FD 41Save A into (41FDH).
NOTE: 41FDH is the SAVED POSITION IN SCAN.
*33CFLD A,D7ALoad A with D (8 * ROW #).
*33D0RLA17Rotate 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.
*33D1RLA17Rotate 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.
*33D2RLA17Rotate 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.
*33D3LD D,A57Copy Register A into Register D
*33D4LD A,E7BCopy Register E into Register A
*33D5RRCA0FRotate A right one bit, with the contents of BIT 0 being put into BOTH the CARRY FLAG and BIT 7. D = 8* ROW # + KEY #.
*33D6RET CD8If the contents of BIT 0 of A was SET, RETurn
*33D7INC D14Bump D so that D holds the NEXT row number.
*33DA - Model 4 Gen 2 - Keyboard Routine.
*33DAPUSH HLE5Save HL to the top of the STACK.
*33DBLD HL,(4201H)2A0142Load HL with the repeat delay counter.
NOTE: 4201H is the REPEAT DELAY COUNTER.
*33DEINC HL23Bump HL.
*33DFLD (4201H),HL220142Load the memory location at 4201H with HL to clear the repeat counter.
NOTE: 4201H is the REPEAT DELAY COUNTER.
*33E2LD DE,(41FFH)ED5BFF41Load DE with the byte stored at 41FFH.
NOTE: 41FFH is the REPEAT DELAY COUNT.
*33E6SBC HL,DEED52Subtract with CARRY DE from HL.
*33E8POP DED1Restore old HL (which is what is in the stack) into DE.
*33ECXOR AAFClear A and all flags.
*33EDLD (DE),A12Put a 0 into the memory location pointed to (DE) to let the key be re-read.
*33EELD (4201H),HL220142Load the memory location at 4201H with HL to clear the repeat counter.
NOTE: 4201H is the REPEAT DELAY COUNTER.
*33F1LD L,96H2E96Load L with 96H to set a fast repeat count.
*33F3LD (41FFH),HL22FF41Save HL into the memory location at 41FFH.
NOTE: 41FFH is the REPEAT DELAY COUNT.
*33F9 - Model 4 Gen 2 - Enter NON-Disk BASIC.
*33FF - Model 4 Gen 2 Routine - Initialization - Process the CASS? Question
*33FFEIFBEnable Interrupts.
*3400CALL 3421HCD 21 34GOSUB to 3421H which loads A with a carrage return, and jumps to 0033H to display it.
*3403LD HL,05D1H21 D1 05Load HL with the address of the "CASS?" message.
*3406CALL 021BHCD 1B 02GOSUB to 021BH.
NOTE: 021BH will display the character at (HL) until a 03H is found.
*3409CALL 0049HCD 49 00GOSUB to 0049H.
NOTE: 0049H is the $KBWAIT routine which scans the keyboard and returns with the key pressed, if any, in register A.
*340CCP 0DHFE 0DCheck to see if Register A is holding a ENTER
*3410PUSH AFF5Save AF to the STACK.
NOTE: Register A currently holds the character pressed in response to the "CASS?" message.
*3411CALL 0033HCD 33 00GOSUB to 0033H pt display the character held in Register A at the current cursor position.
*3414POP AFF1Restore the answer to CASS? from the STACK into Register A.
*3415CP 48HFE 48Compare the answer to the CASS? Prompt held in Register A against with 48H (ASCII: H).
*3419CP 4CHFE 4CCompare the answer to the CASS? Prompt held in Register A against with 4CH (ASCII: L).
Set the flag for LOW SPEED CASSETTE
*341DXOR AAFSet A to 0.
*341ELD (4211H),A32 11 42Save 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
*3421LD A,0DH3E 0DPut a CARRIAGE RETURN into Register A
*3423JP 0033HC3 33 00JUMP 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.
*3426IM 1ED56Set the INTERRUPT MODE to 1.
*3428LD SP,407DH317D40Load the STACK POINTER with 407DH.
*342BLD B,0FH060FLet Register B = 0FH
*342DLD C,88H0E88Let Register C = the CRT Controller Control Register Port
*342FOUT (C),BED41Set the CRT Controller Control Register Port to the contents of B (which are decreasing as the loop progresses)
*3431OUT (89H),AD389Send Register A to the CRT Controller Data Register
*3435OUT (E4H),AD3E4Clear the Non-Maskable Interrupt Latch by sending the contents of Register A to Port E4H
*3437OR 20HF620OR 20H (0010 0000) against A to turn on Bit 5.
*3439OUT (ECH),AD3ECOutput 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.
*343BLD A,81H3E81Load A with 81H (Decimal 129, Binary 10000001).
*343DOUT (F4H),AD3F4Output 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.
*343FLD A,D0H3ED0Load A with D0H (Decimal: 208, Binary: 1101 0000).
*3441OUT (F0H),AD3F0Output "D0H" to the FDC Status Register at Port F0H. This resets the FDC and puts it in mode 1
*3443PUSH BCC5Undertake a short delay of PUSHING BC, POPPING BC, and NOPing
*3444POP BCC1Undertake a short delay of PUSHING BC, POPPING BC, and NOPing
*3445NOP00Undertake a short delay of PUSHING BC, POPPING BC, and NOPing
*3446LD A,04H3E04Load A with a 4 (Binary: 0000 0100).
*3448OUT (E0H),AD3E0Output 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.
*344ALD A,0BH3E0BLoad A with 0BH (Binary: 00001011).
*344COUT (F0H),AD3F0Output 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).
*344ELD HL,36AAH21AA36Next we need to initialize some ports via a LDIR. The next 4 commands will move the 76 (4C) bytes from 36AAH to 4000H.
*3451LD DE,4000H110040Set the LDIR Destination to 4000H
*3454LD BC,004CH014C00Set the number of bytes to move to 4CH
*3457LDIREDB0Move the 76 (4C) bytes from 36AAH to 4000H
*3459LD HL,36F9H21F936Next we need to initialize more ports via a LDIR. The next 4 commands will move the 64 (40) bytes from 36F9H to 41E5H.
*345CLD DE,41E5H11E541Set the LDIR Destination to 41E5H
*345FLD BC,0040H014000Set the number of bytes to move to 40H
*3462LDIREDB0Move the 64 (40) bytes from 36F9H to 41E5H.
The Model 4 ROM Student Network Edition ROM changes that to ...
*346AJP NZ,33F9HC2F933If we have a BREAK key then jump to Non-Disk BASIC at 33F9H.
*346DIN A,(F0H)DBF0We 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
*346FINC A3CBump the FDC Status by 1 (which would turn a FFH (128) into a 00H), making a Z flag mean "Disk Controller Not Ready".
*3470JP Z,33F9HCAF933If the FDC Stats + 1 is zero, then we have no disk controller, so jump to Non-Disk BASIC at 33F9H.
*3473LD BC,0000H010000If we are here, then we have a floppy controller, so set up for a loop of 65,535 times.
*3476DEC BC0BDecrease BC by 1.
*3477LD A,81H3E81Set A to 81H (Decimal: 10000001).
*3479OUT (F4H),AD3F4Send 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.
*347BLD A,B78To 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.
*347COR CB1
*3480IN A,(F0H)DBF0We 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
*3482BIT 2,ACB57Test Bit 2 of A (the Floppy Status). If it is 0 then we haven't made it to track 0, otherwise we have
*3484JR Z,3476H28F0Still 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.
*3486LD E,0AH1E0ASet up for a loop of 10 using Register E; this will be the number of tries to find an index mark.
*3488LD BC,0000H010000Set up for a loop of 65,536 using Register Pair BC.
*348BIN A,(F0H)DBF0Poll 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
*348DBIT 1,ACB4FCheck A to see if bit 1 (meaning "Drive Busy") is set.
*3491CALL 349BHCD9B34If 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.
*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.
*3496DEC E1DIf 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.
*3497JR NZ,34ACH2013So long as we have not run out of the loop of 10 tries, JUMP to 34ACH to find the index mark.
*349B - Model 4 Gen 2 Routine - We have exhausted our tries, so display DISKETTE and keep trying.
*349BDEC BC0BIf bit 1 is not set (meaning, the drive is NOT busy), reduce the counter (BC) by 1.
*349CLD A,81H3E 81Load A with 81H (Decimal: 129, Binary: 1000 0001).
*349EOUT (F4H),AD3 F4Output 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.
*34A0LD A,B78To 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.
*34A1OR CB1
*34A2RET NZC0If BC is not 0, then RETurn
*34A3POP HLE1Clear the STACK
*34A4LD HL,0277H21 77 02If 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.
*34A7CALL 021BHCD 1B 02GOSUB to 021BH. Note; 021BH will display the character at (HL) until a 03H is found.
*34AAJR 3486H18 DAIf 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.
*34ACLD BC,0000H010000Set up for a loop of 65,536 using Register Pair BC.
*34AFIN A,(F0H)DBF0Poll 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
*34B1BIT 1,ACB4FCheck the Floppy Disk Controller Status (held in Register A) to see if bit 1 (meaning "Drive Busy") is set.
*34B3JR Z,34BAH2805If bit 1 is not set (meaning, the drive is NOT busy), JUMP to 34BAH to restart the read attempts.
*34B5CALL 349BHCD9B34If we are here, then the drive was busy, so GOSUB to 349BH to display DISKETTE? and try again
*34B8JR 34AFH18F5JUMP back to the top of this routine and keep looking
*34BA - Model 4 Gen 2 Routine - Finish initializing the floppy disk boot.
*34BADEC E1DIf we are here, it found a diskette so next we need to find the index mark. First decrement E by 1.
We are going to try to read a sector
*34BDLD A,81H3E 81Load A with 81H (Decimal: 129, Binary: 1000 0001).
*34BFOUT (F4H),AD3 F4Output 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.
*34C1LD HL,34F5H21 F5 34We 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.
*34C4LD (404AH),HL22 4A 40Put the desired JUMP address of 34F5H into (404AH)
*34C7LD A,C3H3E C3Load A with C3H (Decimal: 195, Binary 1100 0011).
*34C9LD (4049H),A32 49 40Put the C3H into 4049H.
NOTE: 4049H is the Non-Maskable Interrupt Vector.
*34CCLD A,80H3E 80Load A with 80H (Decimal: 128, Binary 1000 0000).
*34CEOUT (E4H),AD3 E4Output "1000 0000" to the the non-maskable interrupt latch via Port E4H.
*34D0LD BC,00F3H01 F3 00Set BC to 0F3H.
NOTE: Port F3H is the Floppy Disk Controller Data Register.
*34D3LD HL,4300H21 00 43Set HL to 4300H, which is where the data is going to go.
*34D6LD A,01H3E 01Prepare to read Sector 1 by loading A with a 1.
*34D8OUT (F2H),AD3 F2Output 1 to the Floppy Disk Controller Track Register at Port F2H.
*34DALD A,80H3E 80Prepare to read a single sector by loading A with 80H (Decimal: 128, Binary: 1000 0000).
*34DCOUT (F0H),AD3 F0Output "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).
*34DECALL 30CAHCD CA 30GOSUB to 30CAH to delay 3 instruction cycles, arguably the time it will take for the FDC to respond to the port command.
*34E1IN A,(F0H)DB F0Poll 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
*34E3AND 02HE6 02Mask 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.
*34E8INIEDA2Input the data byte.
*34EALD A,81H3E81Load A with 81H (Decimal: 129, Binary: 1000 0001) to select disk 0.
*34ECOR 40HF640OR A with 40H (Binary: 0100 0000) to set A to double-density.
*34EEOUT (F4H),AD3F4Output 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.
*34F0INIEDA2Input the data byte.
*34F2JP 34EAHC3 EA 34Loop 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.
*34F5XOR AAFClear A and all flags.
*34F6OUT (E4H),AD3 E4Send a 0 to the non-maskable interrupt latch via Port E4H. This is to clear the non-maskable interrupt status.
*34F8LD HL,45EDH21 ED 45Now that the NMI jumped here, we need to set a new NMI jump, this time to 45EDH.
*34FBLD (4049H),HL22 49 40Load the destination location into (4049H) which is the Non-Maskable Interrupt Vector.
*34FECALL 30CAHCD CA 30GOSUB to 30CAH to delay 3 instruction cycles, arguably the time it will take for the FDC to respond to the port command.
*3501IN A,(F0H)DB F0Poll 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
*3503POP HLE1Clean up the Stack.
*3504AND 1CHE61CMask 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.
*3509JR 34BDH18B2If 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.
*350BJP NZ,4049HC24940If 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.
*350EIN A,(E4H)DBE4Poll the Non-Maskable Interrupt Latch at Port E4H into A.
*3510BIT 5,ACB6FTest Bit 5 of Port E4 against A.
*3512JR Z,350EH28FALoop back 2 instructions until it is set (i.e., not a zero).
*3517 - Model 4 Gen 2 Routine - Finish up initialization by filling 256 bytes into 4300H and then JUMPing there
*3517OR AB7Set the FLAGS based on Register A
*3518JP NZ,3179HC27931If A is not ZERO then Jump to 3179H to continuing initialization routine by setting up the RS-232
*351BLD HL,4300H210043Set HL to 4300H, which is where the data is going to go.
*351ECALL 3086HCD8630GOSUB to 3086 to Poll the UART and wait for the P FLAG to not be set and then CONTINUE at 306CH
*3521LD (HL),A77Store Register A into the memory location pointed to by Register Pair HL
*3522INC L2CBump Register L by 1
*3523JR NZ,351EH20F9So long as we have not overflowed L, LOOP back to 351EH
*3525JP 37E0HC3E037If 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).
*3528NOP00No Operation
**3517 - Model 4 ROM Student Network Edition - Finish up initialization
**3517CALL 3086HCD8630GOSUB to 3086 to simply poll the keyboard and mask for a "4" key
**351AJR NZ,3520H2004If the key isn't a "4" then restore BC and return to processing as a regular Model 4 would
**351CCALL 028DHCD8D02GOSUB to 028DH to check for a BREAK key.
**351FRETC9RETurn to CALLer
**3520POP BCC1Restore the contents at the top of the STACK into Register Pair BC
**3521JP 3179HC37931Jump to 3179H to continuing initialization routine by setting up the RS-232
**3524NOP00
**3525NOP00
**3526NOP00
**3527NOP00
**3528NOP00
3529H - Deal with the cursor.
352CPUSH DEPush that return address onto the Stack.
352DIN 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.
352FLD 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.
3532OR ASet the flags.
3535LD 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.
3538OR ATest A.
353BLoad HL with 401AH.
NOTE: 401AH is the memory location that stores the cursor blink count.
353EDEC (HL)Reduce the memory contents of (401AH) by one.
NOTE: 401AH is the memory location that stores the cursor blink count.
3541LD (HL),07HSet the cursor blink count to 7.
NOTE: 401AH is the memory location that stores the cursor blink count.
3543INC HLBump HL. This will increase HL from 401AH to 401BH.
NOTE: 401BH holds the cursor blink status - 0 = Off, Anything Else = On.
3544LD A,(HL)Poll the cursor blink status memory location and put the results into A.
3545AND 01HMask A against 0000 0001, to have only Bit 0 active.
3547XOR 01HXOR A with 01H.
3549LD (HL),APut the toggled cursor blink status into the appropriate memory location.
354ALD HL,(4020H)Poll (4020H) and put the result into HL.
NOTE: 4020H holds the current cursor position.
354DIf the current cursor position is 0, then it is off, so JUMP down 2 instructions to 3554H to make the cursor a blank (space).
354FLD A,(4023H)Load A with the memory contents of (4023H).
NOTE: 4023H holds the cursor character.
3552JUMP down to 3556H to skip the next instruction and continue this routine by displaying the character in A.
3554LD A,20HLoad 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.
3556LD (HL),APut the character held in A into the memory location pointed to by (HL).
NOTE: (HL) will hold the current cursor position.
355ADEC (HL)Decrease the number held at (4216H) by 1.
355BRET NZIf the number held at (4216H) is not zero, then RETURN.
355CLD (HL),1EHPut a 1EH (Decimal: 30) into (4216H).
NOTE: 4216H is the heartbeat counter.
355EINC HLBump HL by 1. HL will now point to 4217H, which is the memory location that holds the SECONDS.
355FLoad 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.
3562LD B,03HLoad B with a 03H, to set up a loop where we test seconds, minutes, and hours against their maximums.
3564INC (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.
3565LD 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.
3566SUB (HL)Compare the maximum to what we have by subtracting that maximum from the value pointed at in (HL).
3567RET NZIf there is no difference between what we have and the maximum then RETURN.
3568LD (HL),AIf 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).
3569INC HLBump 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.
356AINC DEBump 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.
356BLoop back to 3564H until the loop of 3 has been met, meaning that we have processed seconds, minutes, and hours.
356DINC HLBump HL one more time, to 421BH.
NOTE: 421BH holds the current DAY portion of the date.
356EINC (HL)Bump the DAY portion of the date.
356FINC HLBump HL one more time, to 421CH.
NOTE: 421BH holds the current MONTH portion of the date.
3570LD A,(HL)Get the month and put it into A.
3571DEC HLDecrease HL back to to 421BH.
NOTE: 421BH holds the DAY portion of the date.
3572DEC ADecrease A by one (to the previous month).
NOTE: DE currently points to the memory locations housing the of days in each month.
3573ADD A,EAdd E to A.
3574LD E,ALoad E with the result. E = E + A.
3575LD A,(DE)Poll the number of days in a month and put the result into A.
3576CP (HL)Compare A against (HL).
NOTE: If this is not a loop, the HL holds the day portion of the date.
3577RET NCIf 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.
3578LD 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.
3579CP 1EHCompare 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.
357BIf the carry flag isn't set then JUMP to 3583H to update the MONTH, but not the YEAR.
357DDEC HLDecrement HL to now point to 421AH.
NOTE: 421AH points to the current YEAR.
357ELD A,(HL)Load the YEAR into A.
357FINC HLBump HL to 421BH.
NOTE: 421BH points to the current DAY.
3580AND 03HMask A (which is holding the year) with 03H (Binary: 00000011) to test for a leap year.
3582RET ZRETURN if that mask showed that we are in the 4th year of a cycle (because 04 and higher are turned off).
3583LD (HL),01HPut a 1 into the memory location pointed to by HL (which is DAY).
3585INC HLBump HL to 421CH.
NOTE: 421BH points to the current MONTH.
3586INC (HL)Increase the current MONTH by 1.
3587LD A,(HL)Put the current MONTH into A.
3588SUB 0DHSubract 13 from A.
NOTE: This will test against a month 13. If A is < 13, then the CARRY FLAG will be set.
358ARET CIf it is NOT month 13 then RETURN to skip the next code which increases the YEAR.
358BLD (HL),01HIf we are here, then MONTH = 13, so set MONTH to 1.
358DDEC HLDecrement HL to 421BH.
NOTE: 421BH points to the current DAY.
358EDEC HLDecrement HL to 421AH.
NOTE: 421BH points to the current YEAR.
358FINC (HL)Bump the current year.
3590RETRETURN.
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.
3591LD 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.
3594BIT 0,ATest Bit 0 of A to see if the clock is on or off.
3596RET ZIf Bit 0 of A is ZERO, then return.
3597LD 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.
359ACP 1EHCompare the heartbeat counter against 1EH.
NOTE: A CP actually subtracts 1E from A without modifying A, but the flags are set accordingly.
359CRET NZIf the clock was not just updated, then RETURN.
359DIf 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.
35A3LD C,3AHLoad C with a :.
NOTE: This routine is also used to convert the date, and C will be swapped out to a /for that routine.
35A5LD B,03HLoad B with a 3.
NOTE: This is because we need to convert 3 numbers, so we will loop 3 times.
35A7LD 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.
35A8DEC DEDecrement DE to point to the next unit to be dealt with.
35A9LD (HL),2FHLoad the memory location pointed to by (HL) with 2F.
NOTE: 2F is a /which is also 1 character below a 0.
35ABINC (HL)Increase whatever is held in (HL). On the first iteration, this change the character at the screen location to a 0.
35ACSUB 0AHA = 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.
35B0ADD A,3AHIf 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.
35B2INC HLBump HL to point to the next location on the video screen. On the first iteration, this will be the 2nd digit of the HOUR.
35B3LD (HL),APut 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.
35B4INC HLBump HL to point to the next location on the video screen. On the first iteration, this will be the 3rd character.
35B5DEC BDecrement B to the next unit. On the first iteration, this will move from 3 to 2.
35B6RET ZIf we have processed all passes in the loop, RETURN.
35B7LD (HL),CIf we are here, then the routine has not yet looped 3 times, so put a :onto the screen.
35B8INC HLBump HL to point to the next location on the video screen.
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 /.
35BELD C,2FHLoad C with a /.
35C2H - Maskable Interrupt Handler.
35C2PUSH AFSave AF to the STACK.
35C3IN A,(E0H)Poll Port E0H which is the MASKABLE INTERRUPT LATCH and put the results into A.
35C5RRARotate 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.
35C9RRARotate 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.
35CAIf Bit 1 (which is now in the carry) is low then JUMP to 3369H (which is a cassette routine with E set to LOW).
35CDPUSH BCSave all the registers.
35CEPUSH DE
35CFPUSH HL
35D0PUSH IX
35D2PUSH IY
35D7PUSH HLPush HL to the STACK.
35D8RRARotate 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.
35D9If 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.
35DCRRARotate 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.
35DDIf 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.
35E0RRARotate 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.
35E1If 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.
35E4RRARotate 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.
35E5If 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.
35E8RRARotate 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.
35E9If 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.
35ECRRARotate 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.
35EDIf 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.
35F0POP HLRestore all registers.
35F1POP IY
35F3POP IX
35F5POP HL
35F6POP DE
35F7POP BC
35F8POP AF
35F9EIEnable Interrupts.
35FARETRETURN.
35FBH - RS-232 Initialization Routine. I'm [guessing] that IX is set to 41F5H
35FBDIDisable Interrupts so they don't interrupt this routine.
35FCIN 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)
35FECP FFHCompare A with FFH to see if the RS-232 exists.
3602XOR AFlip the RS-232 Port Results, just to get a non-zero result.
3603OUT (0E8H),AOutput A to port E8H.
NOTE: Port E8H is the RS-232 Status Register & Master Reset. Outputting ANYTHING to Port E8H resets the RS-232.
3605LD A,(IX+03H)Load the BAUD RATE CODE into A.
NOTE: 41F8H holds the baud rate code.
3608OUT (0E9H),AOutput 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
360ALD A,(IX+04H)Load the CONFIURATION CODE into A.
NOTE: 41F9 holds the RS-232 Configuration Code.
360DOR ASet the flags.
3610OUT (0EAH),AOutput 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)
3619LD A,(IX+05H)Load the WAIT SWITCH into A.
NOTE: 41FAH holds the RS-232 Wait Switch.
361COR ASet the flags.
361FSET 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
3623SET 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
362BOR ASet flags.
362ESET 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
3632SET 02H,(IY+04H)Set Bit 2 of 41F1H to set the ACTIVE FLAG.
3636IN 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
3638EIRe-Enable Interrupts.
3639RETRETURN.
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.
363AXOR AClear A and all flags.
363BLD B,04HLoad 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
363DLD C,0E8HLoad C with E8H.
363FOUT (C),ASend a 0 to the current port. On the first iteration, this is E8H (the RS-232 Status Register & Master Reset).
3641INC CBUMP C to the next port.
3647LD B,03HLoad B with 3 as a counter. 3 is for 3 bytes - 41E8H, 41E9H, and 41EAH.
3649LD (HL),00HLoad (HL) with 00H.
364BINC HLBump HL.
364ELoad HL with 41F0H.
NOTE: 41F0H is the 1 characer output buffer for the RS-232 Output DCB.
3651LD B,03HLoad B with 3 as a counter. 3 is for 3 bytes - 41F0H, 41F1H, and 41F2H.
3653LD (HL),00HLoad (HL) with 00H.
3655INC HLBump HL.
365AH - RS-232 Input Routine.
365ALoad IX with 41E5H.
NOTE: 41E5H is the DCB for RS-232 Input. 41E8H is the 1 Character RS-232 Input.
365EXOR AClear A and all Flags.
365FLD (IX+03H),ALoad (41E8H) with a Zero.
NOTE: 41E8H is the 1 Character RS-232 Input.
3662BIT 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.
3666RET ZIf the Driver is OFF, RETURN.
3667IN 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)
3669BIT 7,ATest Bit 7 of A.
NOTE: Bit 7 will be 1 if DATA READY (1=True).
366DBIT 1,(IX+04H)Test Bit 1 of 41E9H.
NOTE: Bit 1 of 41E9 contains the WAIT/NO WAIT of the RS-232 Input DCB.
3671RET ZIf its NO WAIT then RETURN, otherwise continue (to keep polling).
3677JUMP to 4203H if the BREAKkey was pressed.
NOTE: 4203H JUMPS to 022EH and is the break vector for tape and RS-232.
367AIN 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.
367CLD (IX+03H),ALoad (41E8H) with a A (the data from Port EBH).
NOTE: 41E8H is the 1 Character RS-232 Input.
367FRETRETURN.
3680H - RS-232 Output Routine.
3680Note. 41EDH is the RS-232 Output DCB, and 41F1H holds DRIVER ON/OFF in BIT 2, and WAIT/NO WAIT in BIT 1.
3684BIT 2,(IX+04H)Test Bit 2 of 41F1H to see if the RS-232 is active.
3688RET ZIf the RS-232 is NOT active, RETURN.
3689IN 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)
368BBIT 6,ATest Bit 6 of Port EAH to see READY TO SEND.
368FBIT 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.
3693RET ZIf RS-232 is NOT active, RETURN.
3699JUMP to 4203H if the BREAKkey was pressed.
NOTE: 4203H JUMPS to 022EH and is the break vector for tape and RS-232.
369CLD A,(IX+03H)Load A with the memory contents of 41F0H.
NOTE: 41F0H is RS-232 output buffer byte.
369FOR ATest A and Set Flags.
36A0If not zero, then there is a character in the buffer, so skip the next instruction and leave that byte in A.
36A2LD A,CLoad A with C [GET CHAR FROM DISPATCHER].
36A3OUT (0EBH),ASend 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.
36A5LD (IX+03H),00HLoad memory contents of the RS-232 output byte (at 41F0H) with a 0.
36A9RETRETURN.
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
36D5FFH
36D600HThis 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.
36D7
36DARST 00H
36DBNOP
36DCNOP
36DDXOR A
36DERET
36DFNOP
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
36F4NOP
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-370006 1BTwo 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-37061B 02 21 30 00 00 00
3707-370808 00Two 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-370E02 1B 30 55 6C FF
370F-371000 00Two 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-371F00 00 FF FF 00 00 C3 2E 02 C3 FA 35 C3 FA 35
3720-373041 32 03 32 28 03 3C 04 00 00 1E 00 00 00 00 00 00
3731-373300 00 00Three 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-373800 00 00 00 FF
*3739H - Model 4 Gen 1
*373FLD D,00HLoad D with 0 (so D = ROW 0).
*3742LD E,ALoad E with the contents held in (BC) to check the keyboard row.
*3743XOR (HL)XOR (HL) to set changed bits.
*3744LD (HL),ESave the scan back into (HL).
*3745AND EMask A with E (to mask the released keys).
*3748 - Model 4 Gen 1 routine to Go to the next Keyboard row
*3748INC DBump D so that D holds the NEXT row number.
*3749INC HLBump HL so that HL holds the NEXT buffer location.
*374ARLC CWe 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.
*374CIf 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
*374FLD 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.
*3752LD L,ALoad L with the A.
*3753LD 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.
*3756AND (HL)MASK A against (HL) to see if the previous keys are still pressed.
*375ASBC HL,HLZero HL by subtracting HL from HL.
*375CLD (4201H),HLLoad 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.
*3762LD (41FFH),HLLoad the memory location at 41FFH with 05DCH.
NOTE: 41FFH holds the keyboard scan repeat delay count.
*3765XOR AClear A and all flags.
*3766RETRETURN.
*3767 - Model 4 Gen 1 routine to Keyboard Repeat - Jumps Here if the same keys are still pressed.
*3768PUSH BCSave BC to the STACK.
*376CGOSUB 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.
*376FPOP BCRestore BC from the STACK.
*3770LD A,(BC)Load A with the memory contents pointed to by BC to re-check the keyboard.
*3771AND ECompare A against E to check the pattern.
*3772RET ZIf not the same pattern then RETURN.
*3773LD (41FEH),AIf it is the same pattern then save A into (41FEH).
NOTE: 41FE is the SAVED IMAGE AT POSITION.
*3776LD A,LLoad A with L (the scan position).
*3777LD (41FDH),ASave A into (41FDH).
NOTE: 41FDH is the SAVED POSITION IN SCAN.
*377ALD A,DLoad A with D (8 * ROW #).
*377BRLARotate 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.
*377CRLARotate 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.
*377DRLARotate 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.
*377ELD D,AD = A
*377FLD A,EA = E
RRCARotate A right one bit, with the contents of BIT 0 being put into BOTH the CARRY FLAG and BIT 7. D = 8* ROW # + KEY #.
*3781RET CIf the contents of BIT 0 of A was SET, RETurn
*3782INC DINC DBump D so that D holds the NEXT row number.
*3785 - Model 4 Gen 1 routine to Keyboard Repeat - Jumps Here if the same keys are still pressed.
*3786LD HL,(4201H)Load HL with the repeat delay counter.
NOTE: 4201H is the REPEAT DELAY COUNTER.
*3789INC HLBump HL.
*378ALD (4201H),HLLoad the memory location at 4201H with HL to clear the repeat counter.
NOTE: 4201H is the REPEAT DELAY COUNTER.
*378DLD DE,(41FFH)Load DE with the byte stored at 41FFH.
NOTE: 41FFH is the REPEAT DELAY COUNT.
*3791SBC HL,DESubtract with CARRY DE from HL.
*3793POP DERestore old HL (which is what is in the stack) into DE.
*3797XOR AClear A and all flags.
*3798LD (DE),APut a 0 into the memory location pointed to (DE) to let the key be re-read.
*3799LD (4201H),HLLoad the memory location at 4201H with HL to clear the repeat counter.
NOTE: 4201H is the REPEAT DELAY COUNTER.
*379CLD L,96HLoad L with 96H to set a fast repeat count.
*379ELD (41FFH),HLSave HL into the memory location at 41FFH.
NOTE: 41FFH is the REPEAT DELAY COUNT.
*37A4 - Model 4 Gen 1 routine jumped to from the the middle of the tokenize routine.
CP 22HIf 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 ".
*37A6If the character in register A is not a ", then JUMP forward a few instructions to 37B2H.
*37A8LD A,(409FH)Load A with the memory contents of 409FH.
NOTE: 409FH is the DATA FLAG.
*37ABXOR 01HXOR 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.
*37ADLD (409FH),ALoad memory location 409FH with a the XOR'd results.
NOTE: 409FH is the DATA FLAG.
*37B0LD A,22HLoad A with 22H, which is a ".
CP 3AHCompare 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.
*37B4If A is 3AH then JUMP to 06AAH.
NOTE: 06AAH is in the middle of the KEYBOARD DRIVER ENTRY ROUTINE.
*37B7LD A,(409FH)Otherwise, Load A with the memory contents of 409FH.
NOTE: 409FH is the DATA FLAG.
*37BBRRARotate 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.
*3792If Bit 7 of the DATA FLAG was set, then JUMP to 06A8H.
NOTE: 06A8H is in the middle of the KEYBOARD DRIVER ENTRY ROUTINE.
*37BERLARotate 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.
*37BFJUMP 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$)
RST 10HCall 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.
*37C3PUSH HLSave HL (the current position) to the STACK.
*37C4LD A,11HLoad A with 11H (Decimal: 17).
NOTE: This is to set up for a 17 Byte String.
*37C9LD HL,(40D4H)Load HL with the memory contents of (40D4H).
NOTE: 40D4 is the string pointer.
*37CFLD (HL),20HLoad the memory location pointed to by HL with a SPACE.
*37D1INC HLIncrement HL to move 1 character over.
*37D8 - Model 4 Gen 1 routine to Toggle Caps Lock.
*37DDXOR (HL)Invert the contents of the (4019H).
*37DELD (HL),AStore the inverted (i.e., toggled) contents of (4019H) back into (4019H)
*37E0RETRETurn to Caller.
*37E1 - Model 4 Gen 1 routine to do a very short delay routine
*37E5 - Model 4 Gen 1 routine Unused Code
*37E5NOP
*37E6NOP
*37E7NOP
*37E8RST 38H
*37E9RST 38H
*37EANOP
*37EBH - Model 4 Gen 1 routine to display the Copyright Message
*37F4 - Model 4 Gen 1 routine to enable the TIME$ Command
*37F7LD (4177H),HLLoad 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.
*37F7JUMP to 022EH to continue.
NOTE: 022EH will enable interrupts, show the READY prompt, and RETURN.
*37FD - Model 4 Gen 1 unused Code
*37FDPUSH HL
*37FEPUSH HL
*37FFRST 38H
*3739H - Model 4 Gen 2 - Part of the Keyboard Routine to check for SHIFT and CAPS LOCK and deal with them accordingly.
*3739LD A,(3880H)3A 80 38Load A with the value held at 3880H (which are the SHIFT keys)
*373CAND 03HE6 03Mask the SHIFT Register against 03H (Binary: 0000 0011) to keep only Bits 0 and 1 to check for shifts.
*373EJR Z,3742H28 02If the masked A is 0, then we have no shifts, and skip the next instruction (to 3742H).
*3740SET 6,DCB F2Set BIT 6 of D to offset D for shifts.
*3742LD A,(4019H)3A 19 40Load A with the contents of memory location 4019H to check for CAPS LOCK.
NOTE: 4019H is the CAPS LOCK TOGGLE.
*3745OR AB7Set the flags.
*3746JR Z,3753H28 0BIf the ZERO flag is set then there is NO CAPS LOCK so JUMP to 3753H to skip the next intructions.
*3748SET 7,DCB FASet 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.
*374ALD A,(3880H)3A 80 38Put the contents of memory location 3880H into A to GET SHIFT(S).
*374DAND 03HE6 03Mask the SHIFT Register against 03H (Binary: 0000 0011) to keep only Bits 0 and 1 to check for shifts.
*374FJR Z,3753H28 02If the masked A is 0, then we have no shifts, and skip the next instruction (to 3753H).
*3751RES 7,DCB BARESET 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.
*3753LD HL,304EH21 4E 30Load HL with 304EH (the KEYBOARD TABLES).
*3756LD E,D5AWe need DE to be the OFFSET, so load E with D and ...
*3757LD D,00H16 00... load D with 00.
*3759ADD HL,DE19Add DE (the offset over the keyboard table) to HL (the keyboard table).
*375ALD A,(HL)7EGet the character pointed to by (HL) and put it into A.
*375BRETC9RETurn 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
*375CCP 22HFE 22If 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 ".
*375EJR NZ,376AH20 0AIf the character in register A is not a ", then JUMP forward a few instructions to 376AH.
*3760LD A,(409FH)3A 9F 40Load A with the memory contents of 409FH.
NOTE: 409FH is the DATA FLAG.
*3763XOR 01HEE 01XOR 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.
*3765LD (409FH),A32 9F 40Load memory location 409FH with a the XOR'd results.
NOTE: 409FH is the DATA FLAG.
*3768LD A,22H3E 22Load A with 22H, which is a ".
*376ACP 3AHFE 3ACompare 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.
*376CJP NZ,06AAHC2 AA 06If A is NOT a : then JUMP to 06AAH.
NOTE: 06AAH is in the middle of the KEYBOARD DRIVER ENTRY ROUTINE.
*376FLD A,(409FH)3A 9F 40Otherwise, Load A with the memory contents of 409FH.
NOTE: 409FH is the DATA FLAG.
*3772RRA1FRotate 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.
*3773JP C,06A8HDA A8 06If Bit 7 of the DATA FLAG was set, then JUMP to 06A8H.
NOTE: 06A8H is in the middle of the KEYBOARD DRIVER ENTRY ROUTINE.
*3776RLA17Rotate 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.
*3777JP 06A3HC3 A3 06JUMP 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
*377ALD (IX+05H),05HDD 36 05 00Load the memory location pointed to by IX+5 with a 05H.
NOTE: IX+5 is the number of characters printed.
*377EINC (IX+04H)DD 34 04Bump the number of lines already printed (which is tracked in IX+04H)
*3781LD A,(IX+04H)DD 7E 04Fetch the number of lines already printed (which is tracked in IX+04H) into Register A
*3784CP (IX+03H)DD BE 03Compare the number of lines already printed (held in Register A) against the number of lines left in the page (tracked in IX+03H)
*3787RET NZC0If the number of lines already printed doesn't match the number of lines left in the page then RETURN
*3788LD (IX+04H),04HDD 36 04 01If they DO match, then set the number of lines already printed to 04H
*378CRETC9RETurn 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
*378DLD A,C79LET Register A = Register C.
*378ECP E0HFE E0Compare 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
*3790JP NC,0043HD2 43 00If 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).
*3793CP C0HFE C0If 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
*3795JP C,0063HDA 63 00If 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).
*3798SUB C0HD6 C0If 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.
*379ALD B,00H06 00LET Register B = 00H
*379CLD C,A4FLET Register C = Register A.
*379DLD HL,(4220H)2A 20 42LET Register Pair HL the memory contents of 4220H and 4221H.
NOTE: 4220H-4221H is the destination device nName for ROUTE Routine. 2 Bytes
*37A0ADD HL,BC09LET HL = HL + BC
*37A1LD C,(HL)4ELET Register C = the contents of the memory location pointed to by Register Pair HL.
*37A2JP 0063HC3 63 00JUMP 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
*37A5LD HL,3C00H21 00 3CLoad HL with the memory location for the beginning of the video RAM.
*37A8LD A,(HL)7EPut the character at the screen location stored in HL into A.
*37A9RLCA07Rotate 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.
*37ACRLCA07Rotate 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.
*37ADJR C,37B3H38 04If 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
*37AFLD A,2EH3E 2ELoad Register A with a . which is the character which will print instead of non-printable characters.
*37B3LD A,(HL)7EReplace the non-printable character with a .
*37B4CALL 003BHCD 3B 00Call the PRINT CHARACTER routine at 003B (which sends the character in the A register to the printer).
*37B7INC HL23Bump HL to the next character on the screen.
*37B8BIT 6,HCB 74Check 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).
*37BDLD A,L7DTest of end of line by loading A with L.
*37BEAND 3FHE6 3FAND 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).
*37C0JR NZ,37A8H20 E6If 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
*37A5 - Model 4 Gen 2 - Screen Print Routine BUT exits witH Register A at 0 and all FLAGS cleared
*37C7CALL 37A5HCD A5 37GOSUB to 37A5H for the screen print
*37CAXOR AAFClear Register A and all the flags
*37CBRETC9RETurn to CALLer
*37CC - Model 4 Gen 2 - TOGGLE the CAPS LOCK
*37CCLD A,01H3E 01Load Register A with an 01H
*37CELD HL,4019H21 19 40Load HL with 4019H, which is the CAPS LOCK TOGGLE in the keyboard DCB.
*37D1XOR (HL)AEXOR the contents of the CAPS LOCK TOGGLE in RAM against 01H; this effectively toggles it
*37D2LD (HL),A77Put the toggled CAPS LOCK TOGGLE back into RAM where such thing is stored
*37D3XOR AAFClear Register A and Clear all FLAGs
*37D4RETC9RETurn 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.
*37D5PUSH AFF5Preserve Registers A and F to the top of the STACK
*37D6IN A,(EAH)DB EAPoll the RS-232 UART Control Register/Status Register (via Port EAH) into A.
*37D8BIT 6,ACB 77Test Bit 6 of the RS-232 Register. It will be 1 if READY TO SEND
*37DAJR Z,37D6H28 FAIf the UART reported that READY TO SEND is FALSE, LOOP back to 37D6 and poll again.
*37DCPOP AFF1Restore Registers A and F from the top of the STACK
*37DDOUT (EBH),AD3 EBSend the contents held in Register A to the RS-232C Data Register (via Port E8H) so as to send A to the RS-232C.
*37DFRETC9RETurn 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).
*37E0LD A,6DH3E 6DLoad Register A with a 6DH (Binary: 0110 1101)
*37E2OUT (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)
*37E4JP (HL)E9Jump to whatever (HL) is pointing to.
*37E5 - Model 4 Gen 2 - UNUSED CODE.
*37E5NOP00
*37E6NOP00
*37E7NOP00
*37E8RST 38HFF
*37E9RST 38HFF
**37D5-37E9 - Model 4 ROM Student Network Edition - Replace all the above with NOPs
*37EA - Model 4 Gen 2 - BASIC TIMES (DATE$+" "+TIME$)
*37EARST 10HD7Call the EXAMINE NEXT SYMBOL routine at RST 10H.
*37EBPUSH HLE5Save HL (the current position) to the STACK.
*37ECLD A,11H3E 11Load A with 11H (Decimal: 17) to set up for a 17 Byte String.
*37F1LD HL,(40D4H)2A D4 40Load HL with the memory contents of (40D4H), which is the string pointer.
*37F7LD (HL),20H36 20Load the memory location pointed to by HL with a SPACE.
*37F9INC HL23Increment HL to move 1 character over.
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
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).
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
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).
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.
At this point we need to print the character held in Register A since it wasn't part of any field
Now we parse all the 2 character USING fields.
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 "#"
Now we move on to check the "^^^^" that indicates scientific notation
2D80
LD E,L54
Jump point for when we figure out that we are at the end of a string of digits within a USING string
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).
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.
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.
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.
We will wind up here when we are done processing a numeric field
We will wind up here when the "!" indicating a single character string field has been scanned
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
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.
Note: 409AH holds the ERROR/RESUME flag
Note: 40EAH-40EBH holds the line number with error
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
Note: 40ECH-40EDH holds EDIT/LIST line number
2E71
LD L,C60
2E73
INC HL23
Note: 40A7H-40A8H holds the input Buffer pointer
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)
That's it for non-alphabetic instructions, so we need to need to convert a lower case command to upper case
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
2F16 - EDITCommand - KILL Logic- "KED".
2F40 - EDITCommand - LIST Logic- "LED".
2F4A - EDITCommand - DELETE Logic- "DED"
2F65 - EDITCommand - CHANGE Logic- "CED".
2F75 - EDITCommand - HACK/INSERT Logic- "HED"
2F75-2F76- ↳ HEDLD (HL),00H36 00Set the line end to be the current position.
2F77LD C,B48Load Register C with the character position in Register B which will now be the line length
2F78-2F79- ↳ XEDLD D,0FFH16 FFPrepare for the next CALL to find the end of the line by loading Register D with the number of times to perform the operation
2F7D-2F7F- ↳ IEDGo get the character to be inserted from the keyboard
2F80OR AB7Check to see if a key was pressed
2F84-2F85CP 08HFE 08Check 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-2F87Jump to 2F92H if the character in Register A is a backspace character
2F88-2F89CP 0DHFE 0DCheck 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-2F8CJump to 2FE0H if the character in Register A is a carriage return
2F8D-2F8ECP 1BHFE 1BCheck 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.
2F8FRET ZC8Return if the character in Register A is shift up arrow
2F92 - EDITCommand - BACKSPACE CURSOR Logic- "TYPARW".
2F92-2F93- ↳ TYPARWLD A,08H3E 08Load Register A with a backspace the cursor character
2F94- ↳ TYPAR1DEC B05Decrement the character position in Register B
2F95INC B04Bump the character position in Register B
2F96-2F97If this is the first character of the BASIC line Jump forward to 2FB7H
2F9BDEC HL2BDecrement the value of the input buffer pointer in HL
2F9CDEC B05Decrement the character position in Register B
2FA0PUSH DED5Save 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- ↳ DELCHRPUSH HLE5Save the value of the input buffer pointer in HL to the STACK
2FA2DEC C0DDecrement the character position in Register C
2FA3- ↳ CMPRSSLD A,(HL)7ELoad Register A with the character at the location of the input buffer pointer in HL
2FA4OR AB7Check to see if the character in Register A is an end of the BASIC line character
2FA5SCF37Set the Carry flag to signal that DELCHR was called
2FA6-2FA8If the character in Register A is an end of the BASIC line character then we are done compressing so Jump to 0890H
2FA9INC HL23Bump the value of the input buffer pointer in HL
2FAALD A,(HL)7ELoad Register A with the character at the location of the input buffer pointer in HL
2FABDEC HL2BDecrement the value of the input buffer pointer in HL
2FACLD (HL),A77Save the character in Register A at the location of the current input buffer pointer in HL
2FADINC HL23Bump the value of the input buffer pointer in HL
2FB0 - EDITCommand - ADD A CHARACTER Logic- "NTARRW".
2FB0- ↳ NTARRWPUSH AFF5Save the character to be inserted in Register A to the STACK
2FB1LD A,C79Load Register A with the number of characters in the input buffer (i.e., the length of the line) in Register C
2FB2-2FB3CP FFHCP BUFLENFE FFWe 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-2FB5Jump forward to 2FB9H if the maximum BASIC line length hasn't been reached
2FB6POP AFF1Get the character to be inserted from the STACK and put it in Register A
2FB9- ↳ OKINSSUB B90Subtract 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
2FBAINC C0CBump the number of characters in the input buffer in Register C
2FBBINC B04Bump the character position in Register B
2FBCPUSH BCC5Save the character position and the number of characters in the input buffer in BC to the STACK
2FBDEX DE,HLEBLoad DE with the input buffer pointer in HL
2FBELD L,A6FLoad Register L with the number of bytes to move
2FBF-2FC0LD H,00H26 00Zero Register H so that the number of bytes to move can be done in a 16 bit Register Pair.
2FC1ADD HL,DE19Add the value of the input buffer pointer in DE to the character count in HL
2FC2LD B,H44Load Register B with the MSB of the end of the BASIC line pointer in Register H
2FC3LD C,L4DLoad Register C with the LSB of the end of the BASIC line pointer in Register L
2FC4INC HL23Bump the value of the end of the BASIC line pointer in HL
2FC8POP BCC1Get the character position and the number of characters in the input buffer from the STACK and put it in BC
2FC9POP AFF1Get the character to be inserted from the STACK and put it in Register A
2FCALD (HL),A77Save the character in Register A at the location of the current input buffer pointer in HL
2FCEINC HL23Bump the value of the input buffer pointer in HL
2FD2 - EDITCommand - BACKSPACE Logic- "DELED".
2FD2- ↳ DELEDLD A,B78Top 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
2FD3OR AB7Check to see if this is the start of the BASIC line
2FD4RET ZC8Return if this is the start of the BASIC line
2FD5DEC B05Decrement the character position in Register B
2FD6DEC HL2BDecrement the value of the buffer pointer in HL
2FD7-2FD8LD A,08H3E 08Load Register A with a backspace the cursor character
2FDCDEC D15Decrement the number of times to perform the operation in Register D
2FDFRETC9RETurn to CALLer
2FE0-2FE2- ↳ CREDSince 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
2FE6POP BCC1Clean up the STACK (to remove the DISPED return address)
2FE7POP DED1Get the BASIC line number (in binary) from the STACK and put it in DE
2FE8LD A,D7ALoad Register A with the MSB of the BASIC line number in Register D
2FE9AND EA3Combine the LSB of the BASIC line number in Register E with the MSB of the BASIC line number in Register A
2FEAINC A3CBump the combined BASIC line number in Register A
2FEB-2FED- ↳ EDITRTLD HL,(40A7H)LD HL,(BUFPNT)2A A7 40Load HL with the starting address of the input buffer.
Note: 40A7H-40A8H holds the input Buffer pointer
2FEEDEC HL2BDecrement the value of the input buffer pointer in HL
2FEFRET ZC8Return if this is the Level II BASIC command mode
2FF0SCF37Set the Carry flag to to fool the INSERT code; this flags the line number has having been seen
2FF1INC HL23Bump the value of the input buffer pointer in HL
2FF2PUSH AFF5Save the command mode flag in AF to the STACK
2FF6 - EDITCommand - QUIT Logic- "QED".
3000 - Jump Table.
3012 - Model 4 ROM Gen 1
3012 - Model 4 ROM Gen 2
*3024 - Model 4 ROM Gen 1
*3027RET
NOP
NOPI/O Re-Router was removed from the Model 4 ROM.
*3024 - Model 4 ROM Gen 2
*302D - Model 4 ROM Gen 1
*302D - Model 4 ROM Gen 2
*302DJP 375CHC3 5C 37Jump to 375CH for a routine which parses whether the current instruction on a the current line is in quotes.
3036Jump to 35A0H to put the TIME 10 characters from the upper right hand corner of the screen.
3039IN A,(0E4H)Poll Port E4H into A.
NOTE: Port E4H is the Non-Maskable Interrupt Latch.
303BBIT 5,ATest Bit 5 of Register A. Bit 5 of the NMI on an Input test is the RESET STATUS, with 0=False, and 1=True
*3042 - Model 4 ROM Gen 1 - Prompt the User to set the cassette baud rate.
*3042 - Model 4 ROM Gen 2 - Prompt the User to set the cassette baud rate.
*3045-3064 - Model 4 ROM Gen 1
*3045-305F@abcdefghijklmnopqrstuvwxyzKeyboard Rows 0-3, Unshifted, No Caps Lock
*3060NOPComputer version number, which is always 1 for a Model III and 0 for a Model 4
*3061NOP
*3062NOP
*3063NOP
*3064NOP
*3065-307D30 31 32 33 34 35 36 37 38 39 3A 3B 2C 2D 2E 2F 0D 1F 01 5B 0A 08 09 20 00Keyboard Rows 4-6, Unshifted, No Caps Lock
*307ENOP
*307FNOP
*3080NOP
*3081NOP
*3082NOP
*3083NOP
*3084NOP
*3085-309F60 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 5AKeyboard Rows 0-3, shifted, No Caps Lock
*30A0NOP
*30A1NOP
*30A2NOP
*30A3NOP
*30A4NOP
*30A5NOP
*30A6-30BC21 22 23 24 25 26 27 28 29 2A 2B 3C 3D 3E 3F 0D 1F 01 1B 1A 18 19 20Keyboard Rows 4-6, shifted, No Caps Lock
*30BDNOP
*30BENOP
*30BFNOP
*30C0NOP
*30C1NOP
*30C2NOP
*30C3NOP
*30C4NOP
*30C5-30DF40 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 5AKeyboard Rows 0-3, UNshifted, Caps Lock.
*30E0NOP
*30E1NOP
*30E2NOP
*30E3NOP
*30E4NOP
*30E5-30FC30 31 32 33 34 35 36 37 38 39 3A 3B 2C 2D 2E 2F 0D 1F 01 5B 0A 08 09 20Keyboard Rows 4-6, UNshifted, Caps Lock
*30FDNOP
*30FENOP
*30FFNOP
*3100NOP
*3101NOP
*3102NOP
*3103NOP
*3104NOP
*3105H - Model 4 Gen 1 jump to NON-Disk BASIC
GOSUB to the $SETCAS routine which prompts the user to set the cassette baud rate (310BH - 313AH)
*310BH - Model 4 Gen 1 Set the TAPE BAUD RATE ($SETCAS).
*310CGOSUB to 312DH which loads A with a carrage return, and jumps to 0033H to display it.
*3115GOSUB to 0049H.
NOTE: 0049H is the $KBWAIT routine which scans the keyboard and returns with the key pressed, if any, in register A.
*3118CP 0DHCompare A and 0D (a CARRIAGE RETURN).
*311CPUSH AFSave AF to the STACK.
NOTE: A currently holds the character pressed in response to the "CASS?" message.
*311DGOSUB to 0033H.
NOTE: 0033H is the character print routine, to put the character held in Register A at the current cursor position.
*3120POP AFRestore AF from the STACK.
NOTE: A will then hold the character pressed in response to the "CASS?" message.
*3121CP 48HCompare A with 48H (ASCII: H).
*3125CP 4CHCompare A with a 4CH (ASCII: L).
3129H - Model 4 Gen 1 Set the Selected Cassette Baud Rate as LOW SPEED
*3129XOR ASet 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
LD (4211H),ASave 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
*312FDisplay 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
Finish the cassette setup by calling a portion of the keyboard scan routine which CALLS the screen print routine at 34FDH
*313EXOR AClear Register A and All Flags
*313FRETReturn a null character.
*3140NOP
*3141NOP
*3142NOP
*3143NOP
*3144NOP
*3145 - Model 4 Gen 1 Printer Character Table Codes 32-127.
*3145-31A420 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.
*3045JP 378DHC3 8D 37Jump 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
*3048JP 377AHC3 7A 37Jump to 377AH to check to see if we are on a new printable page and set the pointers accordingly. CALLED from 0431 and 0445
*3045 - Model 4 ROM Gen 2 Keyboard Rows 0-3, Unshifted, No Caps Lock.
*304E-3068@abcdefghijklmnopqrstuvwxyz
*3069NOP
*306ANOP
*306BNOP
*306CNOP
*306DNOP
*306E-308530 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.
*3086IN A,(0EAH)DB EAPoll the RS-232 UART Control Register and Status Register (at Port EAH) and put the results into Register A
*3087OR AB7Set the FLAGS based on the RS-232 UART Control Register and Status Register
*308C-308DJR 30C6H18 38If 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.
**3086LD A,(3810H)3A 10 38Fetch the Keyboard Matrix 0-7 into Register A to check for certain keys.
**3089AND 10HE6 10MASK 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.
**306CNOP
**306DNOP
**306DNOP
*308E-30A8 - Model 4 ROM Gen 2 - Continuing with the Keyboard Table
*308E-30A860 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
*30A9NOP
*30AANOP
*30ABNOP
*30ACNOP
*30ADNOP
*30AENOP
*30AF-30C521 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-30C7IN A,(0EBH)DB EBPoll the RS-232 Register at Port EBH and put the results into Register A.
*30C8RETC9
**30C6-30C8 - Model 4 ROM Student Network Edition - A little code snippet
*30C6NOP
*30C7NOP
*30C8NOP
*3089 - Model 4 ROM Gen 2 - Continuing with the Keyboard Table
*30C9NOP
*30CA-30CDPUSH BC
POP BC
NOP
RETStandard code for a short delay
*30CE@ABCDEFGHIJKLMNOPQRSTUVWXYZ
*30E9NOP
*30EANOP
*30EBNOP
*30ECNOP
*30EDNOP
*30EE30 31 32 33 34 35 36 37 38 39 3A 3B 2C 2D 2E 2F 0D 1F 01 5B 0A 08 09 20Keyboard Rows 4-6, UNshifted, Caps Lock
*3106LD A,(3880H)3A 80 38Load A with the value held at 3880H (which are the SHIFT KEYS)
*3109LD HL,414FH21 F4 41Point HL to 414FH
*310CAND 7CHE6 7CMASK the value held in the SHIFT KEY RAM location against 0111 1100 to keep only bits 2, 3, 4, 5, and 6 live
*310EOR AB7OR A against itself to reset the flags
*310FJR Z,3142H28 31If the Z FLAG is set, JUMP to 3142H to continue checking fro special keys
*3111LD E,A5FPut the masked A into E.
*3112XOR (HL)AEToggle against the old image.
*3113LD (HL),E73Save the new image into (HL).
*3114AND EA3Mask Register E against Register A.
*3115JR Z,3157H28 40If ZERO then the LEFT SHIFT PRESSED was JUMP to 3157H to restart parsing the keyboard
*3117LD BC,05C4H01 C4 05Load BC with 05C4H to set up a 1/50 second delay for de-bounce.
*311ACALL 0060HCD 60 00GOSUB 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.
*311DLD A,(3880H)3A 80 38Load A with the value held at 3880H (which are the SHIFT KEYS)
*3120AND 7CHE6 7CMASK the value held in the SHIFT KEY RAM location against 0111 1100 to keep only bits 2, 3, 4, 5, and 6 live
*3122CP EBBCompare A with E.
*3126RLA17Rotate 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.
*3127RLA17Rotate 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.
*3128JR NC,312EH30 04We 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
*312ALD A,(41F3H)3A F3 41Load Register A with the value of the character to be returned when the F3 key is pressed
*312DRETC9RETurn to Caller
*312E - Model 4 Gen 2 - KEYBOARD Routine - Check and Process the F2 Key or Jump Away.
*312ERLA17Rotate 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.
*312FJR NC,3135H30 04If the NC FLAG is set, it isn't the F2 key, so JUMP to 3135H to test the F1 key
*3131LD A,(41ECH)3A EC 41Load Register A with the value of the character to be returned when the F2 key is pressed
*3134RETC9RETurn to Caller
*3135 - Model 4 Gen 2 - KEYBOARD Routine - Check and Process the F1 Key or Jump Away.
*3135RLA17Rotate 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.
*3136JR NC,313CH30 04If the NC FLAG is set, it isn't the F1 key, so JUMP to 313CH to keep checking special keys
*3138LD A,(41EBH)3A EB 41Load Register A with the value of the character to be returned when the F1 key is pressed
*313BRETC9RETurn to Caller
*33C4 - Model 4 Gen 2 - KEYBOARD Routine - Part of the Keyboard Scan Routine. Keep Checking Special Keys.
*313CRLA17Rotate 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.
*33CA - Model 4 Gen 1 - KEYBOARD Routine - Part of the Keyboard Scan Routine.
*3142LD (HL),A77Save the character held in Register A into the memory location pointed to by Register Pair HL
*3143LD A,FFH3E FFLoad A with FF to set up for a FLAG = 0FFH = NO CONTROL.
*3145LD HL,3840H21 40 38Load HL with 3840H to start a check for a DOWN ARROW.
*3148BIT 4,(HL)CB 66Test BIT 4 of (HL) to check for a DOWN ARROW.
*314AJR Z,3154H28 08JUMP to 3154H if the a DOWN ARROW was NOT pressed.
*314CSLA LCB 25Next we need to check for a a LEFT SHIFT so shift L left.
*314EBIT 0,(HL)CB 46Test BIT 0 of (HL) to check for a LEFT SHIFT.
*3152LD A,1FH3E 1FLoad A with 1F to set up for FLAG = CONTROL KEY.
*3154LD (4224H),A32 24 42Save the CONTROL FLAG into (4224H).
NOTE: 4224H Holds the CONTROL KEY flag.
*315ARET NCD0If that routine exited with NC FLAG set, RETurn
*315ECP 1AHFE 1ACheck A against 1AH to see if we have a SHIFT+DOWN ARROW.
*3163OR AB7Set the flags based on Register A
*3167LD HL,4224H21 24 42Set Register Pair HL to 4224H, which is the CONTROL KEY flag.
*316ABIT 7,(HL)CB 7ETest Bit 7 of the CONTROL KEY flag in RAM.
*316ECP 2AHFE 2ACheck A against a * key.
*3170JP Z,37C7HCA C7 37If 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.
*3173AND (HL)A6Prepare to check for a BREAK key by masking A against the memory contents of HL ...
*3174CP 01HFE 01... and COMPARING it to 01H.
*3176RET NZC0If the result of the compare is NOT zero, then RETURN.
*3177RST 28HEFIf we are here, then a BREAK key was hit, so call RST 28H to handle the BREAK key.
*3178RETC9RETURN to Caller
*3179 - Model 4 Gen 2 - Continuing Initialization Routine by setting up the RS-232.
*3179XOR AAFSet Register A to 0
*317AOUT (E8H),AD3 E8Output A to port E8H.
NOTE: Port E8H is the RS-232 Status Register & Master Reset. Outputting ANYTHING to Port E8H resets the RS-232.
*317CLD A,EEH3E EESet Register A to EEH for outputting to the RS-232 Baud Rate Select and Sense Switches: (Port E9H)
*317EOUT (E9H),AD3 E9Initialize 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
*3180LD A,6DH3E 6DSet Register A to 6DH for outputting to the RS-232 UART Control Register and Status Registe (Port EAH)
*3182OUT (EAH),AD3 EASent 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)
*3184IN A,(E8H)DB E8Poll the RS-232 Status Register and Master Reset of Port E8H and put the value into Register A.
*3186BIT 6,ACB 77Test Bit 6 of the RS-232 Status Register to check Data Set Ready (Pin 6 of the DB-25).
*3188JR Z,3184H28 FAIf the DATA SET READY is ZERO then LOP back 2 instructions to 3184 to poll again
*318ALD A,6CH3E 6CSet Register A to 6CH (0110 1100) for outputting to the RS-232 UART Control Register and Status Registe (Port EAH)
*318COUT (EAH),AD3 EASend 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
*318EIN A,(E8H)DB E8Poll the RS-232 Status Register and Master Reset of Port E8H and put the value into Register A.
*3190BIT 6,ACB 77Test Bit 6 of the RS-232 Status Register to check Data Set Ready (Pin 6 of the DB-25).
*3192JR NZ,318EH20 FAIf the DATA SET READY is ZERO then LOOP back 2 instructions to 318EH to poll again
*3194LD A,0FH3E 0FSet Register A to 0FH
*3196CALL 37D5HCD D5 37GOSUB to 37D5H to send the Character in Register A to the RS-232, once the RS-232 shows ready to accept that character.
*3199CALL 3086HCD 86 30GOSUB to 3086 to Poll the UART and wait for the P FLAG to not be set and then CONTINUE at 306CH
*319CCALL 3086HCD 86 30GOSUB to 3086 to Poll the UART and wait for the P FLAG to not be set and then CONTINUE at 306CH
*319FCALL 37D5HCD D5 37GOSUB to 37D5H to send the Character in Register A to the RS-232, once the RS-232 shows ready to accept that character.
*31A2-31A4JP 3517HC3 17 35JUMP 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
**3179LD A,(4210H)3A 10 42Get 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.
**317COR 10HF6 10OR against 10H (0001 0000) to turn all I/O ports on
**317ELD (4210H),A32 10 42Put the masked status back, first by loading it into 4210H
**3181OUT (ECH),AD3 EC... and then by sending it to 0ECH which is the same as 04210.
**3183LD A,08H3E 08Put an 08H into Register A
**3185OUT (D3H),AD3 D3Send 08H to Port D3H, which is the Network 4 Omninet MSB pointer
**3187XOR AAFPut an 00H into Register A
**3188OUT (D1H),AD3 D1Send 08H to Port D1H, which is the Network 4 Omninet LSB pointer
**318ALD BC,00D0H01 D0 00In 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
**318DLD HL,7000H21 00 70In further preparation for the INIR commands, point HL to the BUFFER for the code, which is 7000H
**3190PUSH HLE5Save the 7000H Buffer start point to the top of the stack
**3191INIRED B2Write a byte from port C to the memory location pointed to by HL, and then increase HL and decrease B.
**3193INIRED B2Write a byte from port C to the memory location pointed to by HL, and then increase HL and decrease B.
**3195INIRED B2Write a byte from port C to the memory location pointed to by HL, and then increase HL and decrease B.
**3197INIRED B2Write a byte from port C to the memory location pointed to by HL, and then increase HL and decrease B.
**3199INIRED B2Write a byte from port C to the memory location pointed to by HL, and then increase HL and decrease B.
**319BINIRED B2Write a byte from port C to the memory location pointed to by HL, and then increase HL and decrease B.
**319DINIRED B2Write a byte from port C to the memory location pointed to by HL, and then increase HL and decrease B.
**319FINIRED B2Write a byte from port C to the memory location pointed to by HL, and then increase HL and decrease B.
**31A1RETC9RETurn to CALLer
**31A2NOP00
**31A3NOP00
**31A4NOP00
31A5 - Output the TIMING MARK to the cassette
31A5LD A,01HLoad A with 01H. This is to prepare to send 0.46V to tape.
31A7OUT (0FFH),ALoad 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.
31A9LD B,0DHLoad B with 0DH in as a loop counter.
31ADLD A,02HLoad A with 01H. This is to prepare to send 0.0V to tape.
31AFOUT (0FFH),ALoad 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.
31B1LD B,0DHLoad B with 0DH in as a delay.
31B5GOSUB to 31F3 to send 0.46V to tape.
NOTE: 31F3H resets the cassette port, and then output a 0 to the Cassette Port FFH.
31B8LD B,78HLoad B with 78H in as a delay.
31BCRETRETURN.
31C0 - Turn Off The Cassette
31C0LD A,(4213H)Load A with the memory contents of 4213H.
NOTE: 4213H is the default interrupt vector setting for the cassette.
31C3OUT (0E0H),AOutput 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
31C5IN 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.
31C7LD 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.
31CAAND FDHMask A against FDH (1111 1101) to zero bit 1.
31CFEIEnable Interrupts.
31D0RETRETURN.
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 "**"
31D1EX DE,HLSwap DE and HL to remove the return address.
31D2EX (SP),HLSwap the memory contents pointed to by the STACK POINTER and HL (which is now what DE was).
31D3PUSH BCSave BC to the STACK.
31D4PUSH HLSave HL to the STACK.
31D5EX DE,HLSwap DE and HL back.
31D6IN 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.
31D8LD DE," "Load DE with SPACESPACE.
31DBLD (3C3EH),DELoad the screen memory location of 3C3EH with DE.
31E8 - Turn On The Cassette - Actually Set the Bit Mask and Output the Command
31E8LD 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.
31EBOR 02HOR A with 02H (0000 0010) to set Bit 1.
31EDLD (4210H),ASave 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.
31F0OUT (0ECH),AOutput that Bit Mask to port 0ECH.
31F2RETRETURN.
31F3 - Reset the Cassette Port. This routine OUTputs a 0 to the Cassette Port FFH
31F3XOR AWe want to reset the cassette port so we zero A.
31F4OUT (0FFH),ALoad 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.
31F6RETRETURN.
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.
31F7LD A,(HL)Load Register A with the memory contents pointed to by Register Pair HL.
31F8SUB 23HSubtract 23H so that we can test to see if the caller was a PRINT #command.
31FAIf 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.
3200RST 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.
3202RETIf we are here, then we have PRINT #n,so we RETURN.
3203 - Vector for a SLOW cassette read
3203LD B,08HLoad B with 8, representing the need to LOOP for 8 bits.
3205GOSUB to 3220H.
NOTE: 3220H reads the tape until it finds a timing mark or the BREAKis hit.
320ALD A,(4212H)Put the contents of 4212H into A.
NOTE: 4212H holds the cassette blinker counter.
320DINC ABump A.
320EAND 5FHMask A against 5F (0101 1111) to turn Bit 7 and Bit 5 off.
3210LD (4212H),APut A into 4212H.
NOTE: 4212H holds the cassette blinker counter.
3215LD A,(3C3FH)Load A with the screen contents at position 3C3FH.
3218XOR 0AHXOR 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).
321ALD (3C3FH),APut the revised A onto the screen at position 3C3FH.
321DLD A,DPut D (the byte) into A.
3220 - Cassette - Keep reading tape looking for a timing mark or BREAK.
3220PUSH BCSave BC to the STACK.
3221IN A,(FFH)Poll Port FFH with the results into Register A.
NOTE: FFH is the Cassette Port.
3223RLAPut 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).
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)
322ELD B,6EHLoad B with 6EH, which is the length of the timing mark.
Reset the cassette port
Next, wait for 98H Units (the length until a data pulse is expected)
3235LD B,98HLoad B with 98H, which is when the next data pulse should be available.
... continue
3239IN A,(FFH)Poll Port FFH and put the results into A.
NOTE: Port FFH is the Cassette Port.
323BPOP BCRestore BC from the STACK.
323CRLARotate 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.
323DRL DRotate 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.
3241 - Vector for a SLOW cassette write. On entry A is the byte to output.
3241PUSH AFSave AF to the STACK.
3242PUSH BCSave BC to the STACK.
3243PUSH DESave DE to the STACK.
3244LD C,08HLoad C with an 8, representing 8 bits to be written.
3246LD D,ALoad D with A.
NOTE: D will be the DATA BYTE.
324ARLC DRotate 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.
324CIf 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.
3251DEC CReduce the counter holding the number of bits to deal with by 1.
3254POP DERestore DE from the STACK.
3255POP BCRestore BC from the STACK.
3256POP AFRestore AF from the STACK.
3257RETRETURN.
3258 - "Write a 0 Bit" by simply waiting the appropriate amount of time and doing nothing.
325E - SLOW tape header write
325EPUSH HLSave HL to the STACK.
3262LD (420CH),HLLoad the memory contents of 420CH with HL.
NOTE: 420CH is the TAPE WRITE VECTOR.
3265LD B,53HLoad B with 53H (Decimal: 83) in prepartion to output 83 ZEROes.
3267XOR AClear A and all flags.
326DLD A,0A5HLoad A with A5H.
NOTE: A5H is the OUTPUT SYNC BYTE.
3274 - SLOW tape header read
3274PUSH HLSave HL to the STACK.
3278LD (420EH),HLPut the vector for a slow cassette read into the memory location at 420EH.
NOTE: 420EH is the TAPE READ VECTOR.
327BLD B,40HLoad B with 40H to set up a loop of 64 to try to find 64 zeroes.
327DLD D,00HLoad D with 0.
3282LD A,DLoad A with the D (the data byte) to begin to check the current data byte.
3283OR ASet up the flags.
3288GOSUB to 3220H.
NOTE: 3220H reads the tape until it finds a timing mark or the BREAKis hit.
328BLD A,DLoad A with the D (the data byte) to begin to check the current data byte.
328CCP 0A5HCompare A to A5H looking for a SYNC BYTE.
3290LD HL,"**"In preparation to display a "**", load HL with **.
3293LD (3C3EH),HLPut HL onto the screen at location 3C3EH.
3296LD A,HLoad A with H (which is a *.
3297POP HLRestore HL from the STACK.
3298POP BCRestore BC from the STACK.
3299POP DERestore DE from the STACK.
329ARETRETURN.
329B - FAST tape header write.
329BPUSH HLSave HL to the STACK.
329FLD (420CH),HLLoad the memory contents of 420CH with HL.
NOTE: 420CH is the TAPE WRITE VECTOR.
32A2LD B,00HLoad B with 00H to set up a loop of 256 to output 256 "55H" bytes.
32A4LD A,55HLoad A with "55H".
32A6GOSUB to 32B4H.
NOTE: 32B4 restore all registers from the STACK, and Fill C with A, and JUMP to cassette write
32ABLD A,7FHLoad A with 7FH.
NOTE: 7FH is the OUTPUT SYNC BYTE.
32ADGOSUB to 32B4H.
NOTE: 32B4 restore all registers from the STACK, and Fill C with A, and JUMP to cassette write
32B0LD A,A5HLoad A with A5H.
NOTE: A5H is the SLOW SYNC BYTE.
32B4 - Restore all registers from the STACK, and Fill C with A, and JUMP to cassette write.
32B4PUSH AFSave AF to the STACK.
32B5PUSH BCSave BC to the STACK.
32B6PUSH DESave DE to the STACK.
32B7LD C,ALoad C with A.
32BA - Save all registers to the STACK, and Fill C with A, GOSBUB to write out the START BIT ...
32BAPUSH AFSave AF to the STACK.
32BBPUSH BCSave BC to the STACK.
32BCPUSH DESave DE to the STACK.
32BDLD C,ALoad C with A.
32C1 - Call 3335H to Output a Bit 8 Times
32C1LD B,08HLoad B with an 8 to set up a loop for 8 bits.
32CA - Read the start bit, read 8 bits, check for error, and flash the star
32CDLD B,08HLoad B with an 8 to set up a loop for 8 bits.
32DA - FAST tape header read.
32DAPUSH HLSave HL to the STACK.
32DELD (420EH),HLLoad the TAPE READ VECTOR (memory location of 420EH) with the 32CAH.
32E1LD A,01HLoad A with a 1 to set the interrupt.
32E3OUT (0E0H),AOUTPUT 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
32E5LD B,80HSet up a loop of 80H (128) to try to read 128 bits.
32EALD A,CLoad A with C (which is the pulse width).
32EBCP 0FHCompare A to 0FH to see if the pulse width was too short.
32EFCP 3EHCompare A to 3EH to see if the pulse width was too long.
32F8LD B,40HSet up a loop of 40H (Decimal: 64) to try to read 64 bits.
3300LD D,CLoad D with C (which holds the delay count).
3304LD A,DLoad A with D to set up to find the difference in the delays.
3305SUB CSubtract C (the delay count) from A (which holds D).
3308NEGA = 0 - A.
330ACP 0DHCompare 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.
330CIf the CARRY FLAG is set, JUMP to 3313H which will bump L and continue the 64 bit loop.
330EINC HBump HL since we have one more zero bit.
3313INC LBump L since we have one more bit.
3316LD A,40HLoad A with 40H (64).
3318CP HCompare A with H to check for bits.
331BCP LCompare with A to check for one bits.
331ELD A,02HLoad A with 2 to set the interrupt vector.
3320OUT (0E0H),AOutput 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
3325LD D,00HZero D.
332DLD A,DLoad A with D (the read byte).
332ECP 7FHCompare A against 7F to check for a MARKER BYTE.
3335RLC CWe 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.
3341DEC DDecrement D as DELAY #1.
3344LD A,02HLoad A with a 2 to set up for a write of 0 VOLTS to TAPE.
3346OUT (0FFH),ALoad 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.
3348DEC EDecrement E as DELAY #2.
334BLD A,01HLoad A with 01H to prepare to send 0.85 VOLTS to TAPE.
334DOUT (0FFH),ALoad 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.
334FRETRETURN.
3350H - READ a BIT
3350EIEnable Interrupts.
3351LD C,00HLoad C with 0.
3353INC CBump C.
3354LD A,(3840H)Load A with the contents of 3840H so as to check for a BREAK.
3357AND 04HMask A with 4 (0000 0100).
335BDIDisable Interrupts.
335FLD (3C3EH),HLPut the "BK" in HL onto the video screen at 3C3EH.
3362JUMP 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
3365LD E,01HLoad E with 01H (Binary: 0000 0001) to make the bit go high.
3369LD E,00HLoad E with 0 to make the bit go low.
336BLD A,06HLoad A with 6.
336DADD A,CAdd A (6) to C, so COUNT = COUNT + 6.
336ELD C,APut the count held in A back into C.
336FIN A,(FFH)Poll FFH (to get the level) into A.
NOTE: FFH is the Cassette Port.
3371AND 01HMask A with 1 (0000 0001) to keep only Bit 0.
3373CP ECompare A with E (which was the set level).
3376POP AFRestore AF from the STACK.
3377POP AFRestore AF (the REMOTE CALLER'S ADDRESS) from the STACK.
3378RETRETURN to the caller's caller.
3379POP AFRestore AF from the STACK.
337AEIEnable Interrupts.
337BRETRETURN back top the loop.
337CH - Check for a Data Error.
337CLD A,CLoad A with C (the count).
337DCP 22HCompare 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
337FRL DWe 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.
3381CP 0FHMake 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.
3383If A < 0FH then it was too quick and we have a data error so JUMP forward a few instructions 3388H.
3385CP 3EHCompare A against 3EH to make sure it was not too slow.
3387RET CIf it wasn't too slow, RETURN.
3388LD A,44HIt was too slow, so load A with a D.
338ALD (3C3EH),APut the "D" on the screen at video location 3C3EH.
338DRETRETURN.
*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).
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).
*3391LD A,(3880H)Load A with the value held at 3880H (which are the SHIFT KEYS)
*3394AND 7CHMASK the value held in the SHIFT KEY RAM location against 0111 1100 to keep only bits 2, 3, 4, 5, and 6 live
*3396OR AOR A against itself to reset the flags
*3399LD E,APut the masked A into E.
*339AXOR (HL)Toggle against the old image.
*339BLD (HL),ESave the new image into (HL).
*339CAND EMask Register E against Rgister A.
*33A2GOSUB 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.
*33A5LD A,(3880H)Load A with the value held at 3880H (which are the SHIFT KEYS)
*33A8AND 7CHMASK the value held in the SHIFT KEY RAM location against 0111 1100 to keep only bits 2, 3, 4, 5, and 6 live
*33AACP ECompare A with E.
*33AERLARotate 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.
*33AFRLARotate 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.
*33B0We 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
*33B2LD A,(41F3H)Load Register A with the value of the character to be returned when the F3key is pressed
*33B5RETRETurn to Caller
*33B6 - Model 4 Gen 1 - KEYBOARD Routine - Check and Process the F2 Key or Jump Away.
RLARotate 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.
*33B9LD A,(41ECH)Load Register A with the value of the character to be returned when the F2key is pressed
*33BCRETRETurn to Caller
*33B6 - Model 4 Gen 1 - KEYBOARD Routine - Check and Process the F1 Key or Jump Away.
RLARotate 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.
*33BEIf the NC FLAG is set, it isn't the F1, so JUMP to 33C4 to keep checking special keys
*33C0LD A,(41EBH)Load Register A with the value of the character to be returned when the F1key is pressed
*33C3RETRETurn to Caller
*33C4 - Model 4 Gen 1 - KEYBOARD Routine - Part of the Keyboard Scan Routine. Keep Checking Special Keys.
RLARotate 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.
*33CA - Model 4 Gen 1 - KEYBOARD Routine - Part of the Keyboard Scan Routine.
LD (HL),ASave the character held in Register A into the memory location pointed to by Register Pair HL
33CBLD A,FFHLoad A with FF to set up for a FLAG = 0FFH = NO CONTROL.
33D0BIT 4,(HL)Test BIT 4 of (HL) to check for a DOWN ARROW.
33D4SLA LNext we need to check for a a LEFT SHIFTso shift L left.
33D6BIT 00H,(HL)Test BIT 0 of (HL) to check for a LEFT SHIFT.
33DALD A,1FHLoad A with 1F to set up for FLAG = CONTROL KEY.
33DCLD (4224H),ASave the CONTROL FLAG into (4224H).
NOTE: 4224H Holds the CONTROL KEY flag.
*33E2RET NCIf that routine exited with NC FLAG set, RETurn
33E6CP 1AHCheck A against 1AH to see if we have a SHIFT DOWN ARROW.
*33EBOR ASet the flags based on Register A
*33F2BIT 7,(HL)Test Bit 7 of the CONTROL KEY flag in RAM.
*33F6CP 2AHCheck A against a *.
*33FBAND (HL)Prepare to check for a BREAKby masking A against the memory contents of HL ...
*33FERET NZIf the result of the compare is NOT zero, then RETURN.
*33FFRST 28HIf we are here, then a BREAKwas hit, so call RST 28H to handle the BREAK.
*3400RETRETURN to Caller
*3401 - Model 4 Gen 1 - This is the BOOTSTRAP. Clears ports, checks for a BREAKkey and a Floppy Controller.
3401IM 1Set the INTERRUPT MODE to 1.
*3406LD B,0FHLet Register B = 0FH
*3408LD C,88HLet Register C = the CRT Controller Control Register Port
*340COUT (89H),ASend Register A to the CRT Controller Data Register
3410OUT (0E4H),AOutput A to Port E4H.
NOTE: Port E4H is the non-maskable interrupt latch. This is to clear the non-maskable interrupt status.
3412OR 20HOR 20H (0010 0000) against A to turn on Bit 5.
3414OUT (0ECH),AOutput 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.
3416LD A,81HLoad A with 81H (Decimal 129, Binary 10000001).
3418OUT (0F4H),AOutput 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.
341ALD A,D0HLoad A with D0H (Decimal: 208, Binary: 1101 0000).
341COUT (0F0H),AOutput "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
*341EPUSH BCUndertake a short delay of PUSHING BC, POPPING BC, and NOPing
*341FPOP BC
*3420NOP
3421LD A,04HLoad A with a 4 (Binary: 0000 0100).
3423OUT (0E0H),AOutput 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
3425LD A,0BHLoad A with 0BH (Binary: 00001011).
3427OUT (0F0H),AOutput 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).
3429Next we need to initialize some ports via a LDIR. The next 4 commands will move the 76 (4C) bytes from 36AAH to 4000H.
342C
342F
3432LDIR
3434Next we need to initialize more ports via a LDIR. The next 4 commands will move the 64 (40) bytes from 36F9H to 41E5H.
3437
343A
343DLDIR
3448IN 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
344AINC ABump the FDC Status by 1 (which would turn a FFH (128) into a 00H), making a Z flag mean "Disk Controller Not Ready".
344BIf the FDC Stats + 1 is zero, then we have no disk controller, so jump to Non-Disk BASIC at 3105H.
344EIf we are here, then we have a floppy controller, so set up for a loop of 65,535 times.
3451DEC BCDecrease BC by 1.
3452LD A,81HSet A to 81H (Decimal: 10000001).
3454OUT (0F4H),ASend 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.
3456LD A,B
OR CTo 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.
345BIN 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
345DBIT 2,ATest Bit 2 of A (the Floppy Status). If it is 0 then we haven't made it to track 0, otherwise we have.
345FStill 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.
3461LD E,05HSet up for a loop of 5 using Register E.
3466IN 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
3468BIT 01H,ACheck A to see if bit 1 (meaning "Drive Busy") is set.
346CDEC BCIf bit 1 is not set (meaning, the drive is NOT busy), reduce the counter (BC) by 1.
346DLD A,81HLoad A with 81H (Decimal: 129, Binary: 1000 0001).
346FOUT (0F4H),AOutput 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.
3471LD A,B
OR CTo 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.
3475If 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.
347BIf 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.
347DDEC EIf we are here, it found a diskette so next we need to find the index mark. First decrement E by 1.
3463HLD A,81HLoad A with 81H (Decimal: 129, Binary: 1000 0001).
3482OUT (0F4H),AOutput 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.
3484We 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.
3487LD (404AH),HL
348ALD A,C3HLoad A with C3H (Decimal: 195, Binary 1100 0011).
348CLD (4049H),APut the C3H into 4049H.
NOTE: 4049H is the Non-Maskable Interrupt Vector.
348FLD A,80HLoad A with 80H (Decimal: 128, Binary 1000 0000).
3491OUT (0E4H),AOutput "1000 0000" to Port E4H.
NOTE: Port E4H is the non-maskable interrupt latch.
3499LD A,01HPrepare to read Sector 1 by loading A with a 1.
349BOUT (0F2H),AOutput 1 to Port F2H.
NOTE: Port F2H is the Floppy Disk Controller Track Register.
349DLD A,80HPrepare to read a single sector by loading A with 80H (Decimal: 128, Binary: 1000 0000).
349FOUT (0F0H),AOutput "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).
34A1GOSUB to 37E1H to delay 3 instruction cycles, arguably the time it will take for the FDC to respond to the port command.
34A4IN 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
34A6AND 02HMask 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.
34ABINIInput the data byte.
34ADLD A,81HLoad A with 81H (Decimal: 129, Binary: 1000 0001) to select disk 0.
34AFOR 40HOR A with 40H (Binary: 0100 0000) to set A to double-density.
34B1OUT (0F4H),AOutput 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.
34B3INIInput the data byte.
34B5Loop 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.
34B8XOR AClear A and all flags.
34B9OUT (0E4H),ASend a 0 to Port E4H.
NOTE: Port E4H is the non-maskable interrupt latch. This is to clear the non-maskable interrupt status.
34BELD (4049H),HLLoad "45EDH" into 4049H.
NOTE: 4049H is the Non-Maskable Interrupt Vector.
34C1GOSUB to 37E1H to delay 3 instruction cycles, arguably the time it will take for the FDC to respond to the port command.
34C4IN 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
34C6POP HLClean up the Stack.
34C7AND 1CHMask 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.
34CCIf 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.
34CEIf 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.
34D1IN A,(E4H)Poll Port E4H into A.
NOTE: Port E4H is the Non-Maskable Interrupt Latch.
34D3BIT 5,ATest Bit 5 of Port E4 against A.
NOTE: Port E4H is the Non-Maskable Interrupt Latch.
*34DA - Model 4 Gen 1 - Part of the Keyboard routine.
*34DDAND 03HMask the SHIFT Register against 03H (Binary: 0000 0011) to keep only Bits 0 and 1 to check for shifts.
*34DFIf the masked A is 0, then we have no shifts, and skip the next instruction (to 34E3H).
*34E1SET 6,DSet BIT 6 of D to offset D for shifts.
LD A,(4019H)Load A with the contents of memory location 4019H to check for CAPS LOCK.
NOTE: 4019H is the CAPS LOCK TOGGLE.
*34E6OR ASet the flags.
*34E9SET 7,DSet 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.
*34EBLD A,(3880H)Put the contents of memory location 3880H into A to GET SHIFT(S).
*34EEAND 03HMask the SHIFT Register against 03H (Binary: 0000 0011) to keep only Bits 0 and 1 to check for shifts.
*34F0If the masked A is 0, then we have no shifts, and skip the next instruction (to 34F4H).
*34F2RES 7,DRESET 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.
34F7LD E,DWe need DE to be the OFFSET, so load E with D and ...
34F8LD D,00H... load D with 00.
34FAADD HL,DEAdd DE (the offset over the keyboard table) to HL (the keyboard table).
34FBLD A,(HL)Get the character pointed to by (HL) and put it into A.
*34FCRETRETurn 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.
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.
*3501RLCARotate the contents of A left one bit position. The contents of bit 7 are copied to the carry flag and bit 0.
*3504RLCARotate the contents of A left one bit position. The contents of bit 7 are copied to the carry flag and bit 0.
*3505If the CARRY FLAG is set, we have a non-graphic characters, so skip the next instructions.
*3507LD A,2EHOverwrite the current character held in Register A with a ., so that all graphic characters are printed as .'s.
Call the PRINT CHARACTER routine at 003BH (which sends the character in the A register to the printer).
*350FINC HLBump HL to the next character on the screen.
*3510BIT 6,HCheck 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).
*3515LD A,LPrepare to test of end of line by loading Register A with Register L.
*3516AND 03FHAND 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).
*3518If 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.
*351AGOSUB 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.
*351DLoop 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
*351FNOP
*3520NOP
*3521NOP
*3522NOP
*3523NOP
*3524NOP
*3525NOP
*3526NOP
*3527NOP
*3528NOP
*338EH - Model 4 Gen 2 - Jump Point for Keyboard Input.
*338ELD BC,3801H010138Load BC with 3801H (KEYBOARD ROW 0).
*3391LD HL,4036H213640Load HL with 4036H (BUFFER ROW 0).
*3394LD D,00H1600Load D with 0 (so D = ROW 0).
*3396LD A,(BC)0Aoad A with the contents held in (BC) to check the keyboard row.
*3397LD E,A5FLoad E with the contents held in (BC) to check the keyboard row.
*3398XOR (HL)AEXOR (HL) to set changed bits.
*3399LD (HL),E73Save the scan back into (HL).
*339AAND EA3Mask A with E (to mask the released keys).
Go to the next row
*339DINC D14Bump D so that D holds the NEXT row number.
*339EINC HL23Bump HL so that HL holds the NEXT buffer location.
*339FRLC CCB01We 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.
*33A1JP P,3396HF29633If 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
*33A4LD A,(41FDH)3AFD41If 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.
*33A7LD L,A6FLoad L with the contents of Register A.
*33A8LD A,(41FEH)3AFE41Load A with the memory contents of 41FEH.
NOTE: 41FEH is the saved IMAGE at the saved position in the keyboard scan data.
*33ABAND (HL)A6MASK A against (HL) to see if the previous keys are still pressed.
*33AFSBC HL,HLED62Zero HL by subtracting HL from HL.
*33B1LD (4201H),HL220142Load 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.
*33B7LD (41FFH),HL22FF41Load the memory location at 41FFH with 05DCH.
NOTE: 41FFH holds the keyboard scan repeat delay count.
*33BAXOR AAFClear A and all flags.
*33BBRETC9RETurn to Caller
*33BC - Model 4 Gen 2 - Keyboard Routine - If the same keys are still pressed then we need to deal with debounce.
*33BCLD E,A5FLoad E with A.
*33BDPUSH BCC5Save BC to the STACK.
*33C1CALL 0060HCD 60 00GOSUB 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.
*33C4POP BCC1Restore BC from the STACK.
*33C5LD A,(BC)0ALoad A with the memory contents pointed to by BC to re-check the keyboard.
*33C6AND EA3Compare A against E to check the pattern.
*33C7RET ZC8If not the same pattern then RETURN.
*33C8LD (41FEH),A32 FE 41If it is the same pattern then save A into (41FEH).
NOTE: 41FE is the SAVED IMAGE AT POSITION.
*33CBLD A,L7DLoad A with L (the scan position).
*33CCLD (41FDH),A32 FD 41Save A into (41FDH).
NOTE: 41FDH is the SAVED POSITION IN SCAN.
*33CFLD A,D7ALoad A with D (8 * ROW #).
*33D0RLA17Rotate 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.
*33D1RLA17Rotate 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.
*33D2RLA17Rotate 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.
*33D3LD D,A57Copy Register A into Register D
*33D4LD A,E7BCopy Register E into Register A
*33D5RRCA0FRotate A right one bit, with the contents of BIT 0 being put into BOTH the CARRY FLAG and BIT 7. D = 8* ROW # + KEY #.
*33D6RET CD8If the contents of BIT 0 of A was SET, RETurn
*33D7INC D14Bump D so that D holds the NEXT row number.
*33DA - Model 4 Gen 2 - Keyboard Routine.
*33DAPUSH HLE5Save HL to the top of the STACK.
*33DBLD HL,(4201H)2A0142Load HL with the repeat delay counter.
NOTE: 4201H is the REPEAT DELAY COUNTER.
*33DEINC HL23Bump HL.
*33DFLD (4201H),HL220142Load the memory location at 4201H with HL to clear the repeat counter.
NOTE: 4201H is the REPEAT DELAY COUNTER.
*33E2LD DE,(41FFH)ED5BFF41Load DE with the byte stored at 41FFH.
NOTE: 41FFH is the REPEAT DELAY COUNT.
*33E6SBC HL,DEED52Subtract with CARRY DE from HL.
*33E8POP DED1Restore old HL (which is what is in the stack) into DE.
*33ECXOR AAFClear A and all flags.
*33EDLD (DE),A12Put a 0 into the memory location pointed to (DE) to let the key be re-read.
*33EELD (4201H),HL220142Load the memory location at 4201H with HL to clear the repeat counter.
NOTE: 4201H is the REPEAT DELAY COUNTER.
*33F1LD L,96H2E96Load L with 96H to set a fast repeat count.
*33F3LD (41FFH),HL22FF41Save HL into the memory location at 41FFH.
NOTE: 41FFH is the REPEAT DELAY COUNT.
*33F9 - Model 4 Gen 2 - Enter NON-Disk BASIC.
*33FF - Model 4 Gen 2 Routine - Initialization - Process the CASS? Question
*33FFEIFBEnable Interrupts.
*3400CALL 3421HCD 21 34GOSUB to 3421H which loads A with a carrage return, and jumps to 0033H to display it.
*3403LD HL,05D1H21 D1 05Load HL with the address of the "CASS?" message.
*3406CALL 021BHCD 1B 02GOSUB to 021BH.
NOTE: 021BH will display the character at (HL) until a 03H is found.
*3409CALL 0049HCD 49 00GOSUB to 0049H.
NOTE: 0049H is the $KBWAIT routine which scans the keyboard and returns with the key pressed, if any, in register A.
*340CCP 0DHFE 0DCheck to see if Register A is holding a ENTER
*3410PUSH AFF5Save AF to the STACK.
NOTE: Register A currently holds the character pressed in response to the "CASS?" message.
*3411CALL 0033HCD 33 00GOSUB to 0033H pt display the character held in Register A at the current cursor position.
*3414POP AFF1Restore the answer to CASS? from the STACK into Register A.
*3415CP 48HFE 48Compare the answer to the CASS? Prompt held in Register A against with 48H (ASCII: H).
*3419CP 4CHFE 4CCompare the answer to the CASS? Prompt held in Register A against with 4CH (ASCII: L).
Set the flag for LOW SPEED CASSETTE
*341DXOR AAFSet A to 0.
*341ELD (4211H),A32 11 42Save 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
*3421LD A,0DH3E 0DPut a CARRIAGE RETURN into Register A
*3423JP 0033HC3 33 00JUMP 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.
*3426IM 1ED56Set the INTERRUPT MODE to 1.
*3428LD SP,407DH317D40Load the STACK POINTER with 407DH.
*342BLD B,0FH060FLet Register B = 0FH
*342DLD C,88H0E88Let Register C = the CRT Controller Control Register Port
*342FOUT (C),BED41Set the CRT Controller Control Register Port to the contents of B (which are decreasing as the loop progresses)
*3431OUT (89H),AD389Send Register A to the CRT Controller Data Register
*3435OUT (E4H),AD3E4Clear the Non-Maskable Interrupt Latch by sending the contents of Register A to Port E4H
*3437OR 20HF620OR 20H (0010 0000) against A to turn on Bit 5.
*3439OUT (ECH),AD3ECOutput 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.
*343BLD A,81H3E81Load A with 81H (Decimal 129, Binary 10000001).
*343DOUT (F4H),AD3F4Output 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.
*343FLD A,D0H3ED0Load A with D0H (Decimal: 208, Binary: 1101 0000).
*3441OUT (F0H),AD3F0Output "D0H" to the FDC Status Register at Port F0H. This resets the FDC and puts it in mode 1
*3443PUSH BCC5Undertake a short delay of PUSHING BC, POPPING BC, and NOPing
*3444POP BCC1Undertake a short delay of PUSHING BC, POPPING BC, and NOPing
*3445NOP00Undertake a short delay of PUSHING BC, POPPING BC, and NOPing
*3446LD A,04H3E04Load A with a 4 (Binary: 0000 0100).
*3448OUT (E0H),AD3E0Output 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.
*344ALD A,0BH3E0BLoad A with 0BH (Binary: 00001011).
*344COUT (F0H),AD3F0Output 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).
*344ELD HL,36AAH21AA36Next we need to initialize some ports via a LDIR. The next 4 commands will move the 76 (4C) bytes from 36AAH to 4000H.
*3451LD DE,4000H110040Set the LDIR Destination to 4000H
*3454LD BC,004CH014C00Set the number of bytes to move to 4CH
*3457LDIREDB0Move the 76 (4C) bytes from 36AAH to 4000H
*3459LD HL,36F9H21F936Next we need to initialize more ports via a LDIR. The next 4 commands will move the 64 (40) bytes from 36F9H to 41E5H.
*345CLD DE,41E5H11E541Set the LDIR Destination to 41E5H
*345FLD BC,0040H014000Set the number of bytes to move to 40H
*3462LDIREDB0Move the 64 (40) bytes from 36F9H to 41E5H.
The Model 4 ROM Student Network Edition ROM changes that to ...
*346AJP NZ,33F9HC2F933If we have a BREAK key then jump to Non-Disk BASIC at 33F9H.
*346DIN A,(F0H)DBF0We 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
*346FINC A3CBump the FDC Status by 1 (which would turn a FFH (128) into a 00H), making a Z flag mean "Disk Controller Not Ready".
*3470JP Z,33F9HCAF933If the FDC Stats + 1 is zero, then we have no disk controller, so jump to Non-Disk BASIC at 33F9H.
*3473LD BC,0000H010000If we are here, then we have a floppy controller, so set up for a loop of 65,535 times.
*3476DEC BC0BDecrease BC by 1.
*3477LD A,81H3E81Set A to 81H (Decimal: 10000001).
*3479OUT (F4H),AD3F4Send 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.
*347BLD A,B78To 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.
*347COR CB1
*3480IN A,(F0H)DBF0We 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
*3482BIT 2,ACB57Test Bit 2 of A (the Floppy Status). If it is 0 then we haven't made it to track 0, otherwise we have
*3484JR Z,3476H28F0Still 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.
*3486LD E,0AH1E0ASet up for a loop of 10 using Register E; this will be the number of tries to find an index mark.
*3488LD BC,0000H010000Set up for a loop of 65,536 using Register Pair BC.
*348BIN A,(F0H)DBF0Poll 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
*348DBIT 1,ACB4FCheck A to see if bit 1 (meaning "Drive Busy") is set.
*3491CALL 349BHCD9B34If 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.
*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.
*3496DEC E1DIf 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.
*3497JR NZ,34ACH2013So long as we have not run out of the loop of 10 tries, JUMP to 34ACH to find the index mark.
*349B - Model 4 Gen 2 Routine - We have exhausted our tries, so display DISKETTE and keep trying.
*349BDEC BC0BIf bit 1 is not set (meaning, the drive is NOT busy), reduce the counter (BC) by 1.
*349CLD A,81H3E 81Load A with 81H (Decimal: 129, Binary: 1000 0001).
*349EOUT (F4H),AD3 F4Output 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.
*34A0LD A,B78To 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.
*34A1OR CB1
*34A2RET NZC0If BC is not 0, then RETurn
*34A3POP HLE1Clear the STACK
*34A4LD HL,0277H21 77 02If 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.
*34A7CALL 021BHCD 1B 02GOSUB to 021BH. Note; 021BH will display the character at (HL) until a 03H is found.
*34AAJR 3486H18 DAIf 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.
*34ACLD BC,0000H010000Set up for a loop of 65,536 using Register Pair BC.
*34AFIN A,(F0H)DBF0Poll 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
*34B1BIT 1,ACB4FCheck the Floppy Disk Controller Status (held in Register A) to see if bit 1 (meaning "Drive Busy") is set.
*34B3JR Z,34BAH2805If bit 1 is not set (meaning, the drive is NOT busy), JUMP to 34BAH to restart the read attempts.
*34B5CALL 349BHCD9B34If we are here, then the drive was busy, so GOSUB to 349BH to display DISKETTE? and try again
*34B8JR 34AFH18F5JUMP back to the top of this routine and keep looking
*34BA - Model 4 Gen 2 Routine - Finish initializing the floppy disk boot.
*34BADEC E1DIf we are here, it found a diskette so next we need to find the index mark. First decrement E by 1.
We are going to try to read a sector
*34BDLD A,81H3E 81Load A with 81H (Decimal: 129, Binary: 1000 0001).
*34BFOUT (F4H),AD3 F4Output 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.
*34C1LD HL,34F5H21 F5 34We 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.
*34C4LD (404AH),HL22 4A 40Put the desired JUMP address of 34F5H into (404AH)
*34C7LD A,C3H3E C3Load A with C3H (Decimal: 195, Binary 1100 0011).
*34C9LD (4049H),A32 49 40Put the C3H into 4049H.
NOTE: 4049H is the Non-Maskable Interrupt Vector.
*34CCLD A,80H3E 80Load A with 80H (Decimal: 128, Binary 1000 0000).
*34CEOUT (E4H),AD3 E4Output "1000 0000" to the the non-maskable interrupt latch via Port E4H.
*34D0LD BC,00F3H01 F3 00Set BC to 0F3H.
NOTE: Port F3H is the Floppy Disk Controller Data Register.
*34D3LD HL,4300H21 00 43Set HL to 4300H, which is where the data is going to go.
*34D6LD A,01H3E 01Prepare to read Sector 1 by loading A with a 1.
*34D8OUT (F2H),AD3 F2Output 1 to the Floppy Disk Controller Track Register at Port F2H.
*34DALD A,80H3E 80Prepare to read a single sector by loading A with 80H (Decimal: 128, Binary: 1000 0000).
*34DCOUT (F0H),AD3 F0Output "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).
*34DECALL 30CAHCD CA 30GOSUB to 30CAH to delay 3 instruction cycles, arguably the time it will take for the FDC to respond to the port command.
*34E1IN A,(F0H)DB F0Poll 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
*34E3AND 02HE6 02Mask 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.
*34E8INIEDA2Input the data byte.
*34EALD A,81H3E81Load A with 81H (Decimal: 129, Binary: 1000 0001) to select disk 0.
*34ECOR 40HF640OR A with 40H (Binary: 0100 0000) to set A to double-density.
*34EEOUT (F4H),AD3F4Output 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.
*34F0INIEDA2Input the data byte.
*34F2JP 34EAHC3 EA 34Loop 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.
*34F5XOR AAFClear A and all flags.
*34F6OUT (E4H),AD3 E4Send a 0 to the non-maskable interrupt latch via Port E4H. This is to clear the non-maskable interrupt status.
*34F8LD HL,45EDH21 ED 45Now that the NMI jumped here, we need to set a new NMI jump, this time to 45EDH.
*34FBLD (4049H),HL22 49 40Load the destination location into (4049H) which is the Non-Maskable Interrupt Vector.
*34FECALL 30CAHCD CA 30GOSUB to 30CAH to delay 3 instruction cycles, arguably the time it will take for the FDC to respond to the port command.
*3501IN A,(F0H)DB F0Poll 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
*3503POP HLE1Clean up the Stack.
*3504AND 1CHE61CMask 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.
*3509JR 34BDH18B2If 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.
*350BJP NZ,4049HC24940If 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.
*350EIN A,(E4H)DBE4Poll the Non-Maskable Interrupt Latch at Port E4H into A.
*3510BIT 5,ACB6FTest Bit 5 of Port E4 against A.
*3512JR Z,350EH28FALoop back 2 instructions until it is set (i.e., not a zero).
*3517 - Model 4 Gen 2 Routine - Finish up initialization by filling 256 bytes into 4300H and then JUMPing there
*3517OR AB7Set the FLAGS based on Register A
*3518JP NZ,3179HC27931If A is not ZERO then Jump to 3179H to continuing initialization routine by setting up the RS-232
*351BLD HL,4300H210043Set HL to 4300H, which is where the data is going to go.
*351ECALL 3086HCD8630GOSUB to 3086 to Poll the UART and wait for the P FLAG to not be set and then CONTINUE at 306CH
*3521LD (HL),A77Store Register A into the memory location pointed to by Register Pair HL
*3522INC L2CBump Register L by 1
*3523JR NZ,351EH20F9So long as we have not overflowed L, LOOP back to 351EH
*3525JP 37E0HC3E037If 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).
*3528NOP00No Operation
**3517 - Model 4 ROM Student Network Edition - Finish up initialization
**3517CALL 3086HCD8630GOSUB to 3086 to simply poll the keyboard and mask for a "4" key
**351AJR NZ,3520H2004If the key isn't a "4" then restore BC and return to processing as a regular Model 4 would
**351CCALL 028DHCD8D02GOSUB to 028DH to check for a BREAK key.
**351FRETC9RETurn to CALLer
**3520POP BCC1Restore the contents at the top of the STACK into Register Pair BC
**3521JP 3179HC37931Jump to 3179H to continuing initialization routine by setting up the RS-232
**3524NOP00
**3525NOP00
**3526NOP00
**3527NOP00
**3528NOP00
3529H - Deal with the cursor.
352CPUSH DEPush that return address onto the Stack.
352DIN 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.
352FLD 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.
3532OR ASet the flags.
3535LD 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.
3538OR ATest A.
353BLoad HL with 401AH.
NOTE: 401AH is the memory location that stores the cursor blink count.
353EDEC (HL)Reduce the memory contents of (401AH) by one.
NOTE: 401AH is the memory location that stores the cursor blink count.
3541LD (HL),07HSet the cursor blink count to 7.
NOTE: 401AH is the memory location that stores the cursor blink count.
3543INC HLBump HL. This will increase HL from 401AH to 401BH.
NOTE: 401BH holds the cursor blink status - 0 = Off, Anything Else = On.
3544LD A,(HL)Poll the cursor blink status memory location and put the results into A.
3545AND 01HMask A against 0000 0001, to have only Bit 0 active.
3547XOR 01HXOR A with 01H.
3549LD (HL),APut the toggled cursor blink status into the appropriate memory location.
354ALD HL,(4020H)Poll (4020H) and put the result into HL.
NOTE: 4020H holds the current cursor position.
354DIf the current cursor position is 0, then it is off, so JUMP down 2 instructions to 3554H to make the cursor a blank (space).
354FLD A,(4023H)Load A with the memory contents of (4023H).
NOTE: 4023H holds the cursor character.
3552JUMP down to 3556H to skip the next instruction and continue this routine by displaying the character in A.
3554LD A,20HLoad 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.
3556LD (HL),APut the character held in A into the memory location pointed to by (HL).
NOTE: (HL) will hold the current cursor position.
355ADEC (HL)Decrease the number held at (4216H) by 1.
355BRET NZIf the number held at (4216H) is not zero, then RETURN.
355CLD (HL),1EHPut a 1EH (Decimal: 30) into (4216H).
NOTE: 4216H is the heartbeat counter.
355EINC HLBump HL by 1. HL will now point to 4217H, which is the memory location that holds the SECONDS.
355FLoad 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.
3562LD B,03HLoad B with a 03H, to set up a loop where we test seconds, minutes, and hours against their maximums.
3564INC (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.
3565LD 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.
3566SUB (HL)Compare the maximum to what we have by subtracting that maximum from the value pointed at in (HL).
3567RET NZIf there is no difference between what we have and the maximum then RETURN.
3568LD (HL),AIf 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).
3569INC HLBump 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.
356AINC DEBump 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.
356BLoop back to 3564H until the loop of 3 has been met, meaning that we have processed seconds, minutes, and hours.
356DINC HLBump HL one more time, to 421BH.
NOTE: 421BH holds the current DAY portion of the date.
356EINC (HL)Bump the DAY portion of the date.
356FINC HLBump HL one more time, to 421CH.
NOTE: 421BH holds the current MONTH portion of the date.
3570LD A,(HL)Get the month and put it into A.
3571DEC HLDecrease HL back to to 421BH.
NOTE: 421BH holds the DAY portion of the date.
3572DEC ADecrease A by one (to the previous month).
NOTE: DE currently points to the memory locations housing the of days in each month.
3573ADD A,EAdd E to A.
3574LD E,ALoad E with the result. E = E + A.
3575LD A,(DE)Poll the number of days in a month and put the result into A.
3576CP (HL)Compare A against (HL).
NOTE: If this is not a loop, the HL holds the day portion of the date.
3577RET NCIf 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.
3578LD 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.
3579CP 1EHCompare 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.
357BIf the carry flag isn't set then JUMP to 3583H to update the MONTH, but not the YEAR.
357DDEC HLDecrement HL to now point to 421AH.
NOTE: 421AH points to the current YEAR.
357ELD A,(HL)Load the YEAR into A.
357FINC HLBump HL to 421BH.
NOTE: 421BH points to the current DAY.
3580AND 03HMask A (which is holding the year) with 03H (Binary: 00000011) to test for a leap year.
3582RET ZRETURN if that mask showed that we are in the 4th year of a cycle (because 04 and higher are turned off).
3583LD (HL),01HPut a 1 into the memory location pointed to by HL (which is DAY).
3585INC HLBump HL to 421CH.
NOTE: 421BH points to the current MONTH.
3586INC (HL)Increase the current MONTH by 1.
3587LD A,(HL)Put the current MONTH into A.
3588SUB 0DHSubract 13 from A.
NOTE: This will test against a month 13. If A is < 13, then the CARRY FLAG will be set.
358ARET CIf it is NOT month 13 then RETURN to skip the next code which increases the YEAR.
358BLD (HL),01HIf we are here, then MONTH = 13, so set MONTH to 1.
358DDEC HLDecrement HL to 421BH.
NOTE: 421BH points to the current DAY.
358EDEC HLDecrement HL to 421AH.
NOTE: 421BH points to the current YEAR.
358FINC (HL)Bump the current year.
3590RETRETURN.
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.
3591LD 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.
3594BIT 0,ATest Bit 0 of A to see if the clock is on or off.
3596RET ZIf Bit 0 of A is ZERO, then return.
3597LD 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.
359ACP 1EHCompare the heartbeat counter against 1EH.
NOTE: A CP actually subtracts 1E from A without modifying A, but the flags are set accordingly.
359CRET NZIf the clock was not just updated, then RETURN.
359DIf 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.
35A3LD C,3AHLoad C with a :.
NOTE: This routine is also used to convert the date, and C will be swapped out to a /for that routine.
35A5LD B,03HLoad B with a 3.
NOTE: This is because we need to convert 3 numbers, so we will loop 3 times.
35A7LD 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.
35A8DEC DEDecrement DE to point to the next unit to be dealt with.
35A9LD (HL),2FHLoad the memory location pointed to by (HL) with 2F.
NOTE: 2F is a /which is also 1 character below a 0.
35ABINC (HL)Increase whatever is held in (HL). On the first iteration, this change the character at the screen location to a 0.
35ACSUB 0AHA = 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.
35B0ADD A,3AHIf 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.
35B2INC HLBump HL to point to the next location on the video screen. On the first iteration, this will be the 2nd digit of the HOUR.
35B3LD (HL),APut 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.
35B4INC HLBump HL to point to the next location on the video screen. On the first iteration, this will be the 3rd character.
35B5DEC BDecrement B to the next unit. On the first iteration, this will move from 3 to 2.
35B6RET ZIf we have processed all passes in the loop, RETURN.
35B7LD (HL),CIf we are here, then the routine has not yet looped 3 times, so put a :onto the screen.
35B8INC HLBump HL to point to the next location on the video screen.
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 /.
35BELD C,2FHLoad C with a /.
35C2H - Maskable Interrupt Handler.
35C2PUSH AFSave AF to the STACK.
35C3IN A,(E0H)Poll Port E0H which is the MASKABLE INTERRUPT LATCH and put the results into A.
35C5RRARotate 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.
35C9RRARotate 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.
35CAIf Bit 1 (which is now in the carry) is low then JUMP to 3369H (which is a cassette routine with E set to LOW).
35CDPUSH BCSave all the registers.
35CEPUSH DE
35CFPUSH HL
35D0PUSH IX
35D2PUSH IY
35D7PUSH HLPush HL to the STACK.
35D8RRARotate 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.
35D9If 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.
35DCRRARotate 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.
35DDIf 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.
35E0RRARotate 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.
35E1If 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.
35E4RRARotate 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.
35E5If 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.
35E8RRARotate 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.
35E9If 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.
35ECRRARotate 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.
35EDIf 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.
35F0POP HLRestore all registers.
35F1POP IY
35F3POP IX
35F5POP HL
35F6POP DE
35F7POP BC
35F8POP AF
35F9EIEnable Interrupts.
35FARETRETURN.
35FBH - RS-232 Initialization Routine. I'm [guessing] that IX is set to 41F5H
35FBDIDisable Interrupts so they don't interrupt this routine.
35FCIN 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)
35FECP FFHCompare A with FFH to see if the RS-232 exists.
3602XOR AFlip the RS-232 Port Results, just to get a non-zero result.
3603OUT (0E8H),AOutput A to port E8H.
NOTE: Port E8H is the RS-232 Status Register & Master Reset. Outputting ANYTHING to Port E8H resets the RS-232.
3605LD A,(IX+03H)Load the BAUD RATE CODE into A.
NOTE: 41F8H holds the baud rate code.
3608OUT (0E9H),AOutput 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
360ALD A,(IX+04H)Load the CONFIURATION CODE into A.
NOTE: 41F9 holds the RS-232 Configuration Code.
360DOR ASet the flags.
3610OUT (0EAH),AOutput 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)
3619LD A,(IX+05H)Load the WAIT SWITCH into A.
NOTE: 41FAH holds the RS-232 Wait Switch.
361COR ASet the flags.
361FSET 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
3623SET 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
362BOR ASet flags.
362ESET 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
3632SET 02H,(IY+04H)Set Bit 2 of 41F1H to set the ACTIVE FLAG.
3636IN 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
3638EIRe-Enable Interrupts.
3639RETRETURN.
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.
363AXOR AClear A and all flags.
363BLD B,04HLoad 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
363DLD C,0E8HLoad C with E8H.
363FOUT (C),ASend a 0 to the current port. On the first iteration, this is E8H (the RS-232 Status Register & Master Reset).
3641INC CBUMP C to the next port.
3647LD B,03HLoad B with 3 as a counter. 3 is for 3 bytes - 41E8H, 41E9H, and 41EAH.
3649LD (HL),00HLoad (HL) with 00H.
364BINC HLBump HL.
364ELoad HL with 41F0H.
NOTE: 41F0H is the 1 characer output buffer for the RS-232 Output DCB.
3651LD B,03HLoad B with 3 as a counter. 3 is for 3 bytes - 41F0H, 41F1H, and 41F2H.
3653LD (HL),00HLoad (HL) with 00H.
3655INC HLBump HL.
365AH - RS-232 Input Routine.
365ALoad IX with 41E5H.
NOTE: 41E5H is the DCB for RS-232 Input. 41E8H is the 1 Character RS-232 Input.
365EXOR AClear A and all Flags.
365FLD (IX+03H),ALoad (41E8H) with a Zero.
NOTE: 41E8H is the 1 Character RS-232 Input.
3662BIT 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.
3666RET ZIf the Driver is OFF, RETURN.
3667IN 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)
3669BIT 7,ATest Bit 7 of A.
NOTE: Bit 7 will be 1 if DATA READY (1=True).
366DBIT 1,(IX+04H)Test Bit 1 of 41E9H.
NOTE: Bit 1 of 41E9 contains the WAIT/NO WAIT of the RS-232 Input DCB.
3671RET ZIf its NO WAIT then RETURN, otherwise continue (to keep polling).
3677JUMP to 4203H if the BREAKkey was pressed.
NOTE: 4203H JUMPS to 022EH and is the break vector for tape and RS-232.
367AIN 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.
367CLD (IX+03H),ALoad (41E8H) with a A (the data from Port EBH).
NOTE: 41E8H is the 1 Character RS-232 Input.
367FRETRETURN.
3680H - RS-232 Output Routine.
3680Note. 41EDH is the RS-232 Output DCB, and 41F1H holds DRIVER ON/OFF in BIT 2, and WAIT/NO WAIT in BIT 1.
3684BIT 2,(IX+04H)Test Bit 2 of 41F1H to see if the RS-232 is active.
3688RET ZIf the RS-232 is NOT active, RETURN.
3689IN 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)
368BBIT 6,ATest Bit 6 of Port EAH to see READY TO SEND.
368FBIT 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.
3693RET ZIf RS-232 is NOT active, RETURN.
3699JUMP to 4203H if the BREAKkey was pressed.
NOTE: 4203H JUMPS to 022EH and is the break vector for tape and RS-232.
369CLD A,(IX+03H)Load A with the memory contents of 41F0H.
NOTE: 41F0H is RS-232 output buffer byte.
369FOR ATest A and Set Flags.
36A0If not zero, then there is a character in the buffer, so skip the next instruction and leave that byte in A.
36A2LD A,CLoad A with C [GET CHAR FROM DISPATCHER].
36A3OUT (0EBH),ASend 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.
36A5LD (IX+03H),00HLoad memory contents of the RS-232 output byte (at 41F0H) with a 0.
36A9RETRETURN.
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
36D5FFH
36D600HThis 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.
36D7
36DARST 00H
36DBNOP
36DCNOP
36DDXOR A
36DERET
36DFNOP
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
36F4NOP
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-370006 1BTwo 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-37061B 02 21 30 00 00 00
3707-370808 00Two 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-370E02 1B 30 55 6C FF
370F-371000 00Two 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-371F00 00 FF FF 00 00 C3 2E 02 C3 FA 35 C3 FA 35
3720-373041 32 03 32 28 03 3C 04 00 00 1E 00 00 00 00 00 00
3731-373300 00 00Three 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-373800 00 00 00 FF
*3739H - Model 4 Gen 1
*373FLD D,00HLoad D with 0 (so D = ROW 0).
*3742LD E,ALoad E with the contents held in (BC) to check the keyboard row.
*3743XOR (HL)XOR (HL) to set changed bits.
*3744LD (HL),ESave the scan back into (HL).
*3745AND EMask A with E (to mask the released keys).
*3748 - Model 4 Gen 1 routine to Go to the next Keyboard row
*3748INC DBump D so that D holds the NEXT row number.
*3749INC HLBump HL so that HL holds the NEXT buffer location.
*374ARLC CWe 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.
*374CIf 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
*374FLD 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.
*3752LD L,ALoad L with the A.
*3753LD 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.
*3756AND (HL)MASK A against (HL) to see if the previous keys are still pressed.
*375ASBC HL,HLZero HL by subtracting HL from HL.
*375CLD (4201H),HLLoad 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.
*3762LD (41FFH),HLLoad the memory location at 41FFH with 05DCH.
NOTE: 41FFH holds the keyboard scan repeat delay count.
*3765XOR AClear A and all flags.
*3766RETRETURN.
*3767 - Model 4 Gen 1 routine to Keyboard Repeat - Jumps Here if the same keys are still pressed.
*3768PUSH BCSave BC to the STACK.
*376CGOSUB 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.
*376FPOP BCRestore BC from the STACK.
*3770LD A,(BC)Load A with the memory contents pointed to by BC to re-check the keyboard.
*3771AND ECompare A against E to check the pattern.
*3772RET ZIf not the same pattern then RETURN.
*3773LD (41FEH),AIf it is the same pattern then save A into (41FEH).
NOTE: 41FE is the SAVED IMAGE AT POSITION.
*3776LD A,LLoad A with L (the scan position).
*3777LD (41FDH),ASave A into (41FDH).
NOTE: 41FDH is the SAVED POSITION IN SCAN.
*377ALD A,DLoad A with D (8 * ROW #).
*377BRLARotate 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.
*377CRLARotate 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.
*377DRLARotate 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.
*377ELD D,AD = A
*377FLD A,EA = E
RRCARotate A right one bit, with the contents of BIT 0 being put into BOTH the CARRY FLAG and BIT 7. D = 8* ROW # + KEY #.
*3781RET CIf the contents of BIT 0 of A was SET, RETurn
*3782INC DINC DBump D so that D holds the NEXT row number.
*3785 - Model 4 Gen 1 routine to Keyboard Repeat - Jumps Here if the same keys are still pressed.
*3786LD HL,(4201H)Load HL with the repeat delay counter.
NOTE: 4201H is the REPEAT DELAY COUNTER.
*3789INC HLBump HL.
*378ALD (4201H),HLLoad the memory location at 4201H with HL to clear the repeat counter.
NOTE: 4201H is the REPEAT DELAY COUNTER.
*378DLD DE,(41FFH)Load DE with the byte stored at 41FFH.
NOTE: 41FFH is the REPEAT DELAY COUNT.
*3791SBC HL,DESubtract with CARRY DE from HL.
*3793POP DERestore old HL (which is what is in the stack) into DE.
*3797XOR AClear A and all flags.
*3798LD (DE),APut a 0 into the memory location pointed to (DE) to let the key be re-read.
*3799LD (4201H),HLLoad the memory location at 4201H with HL to clear the repeat counter.
NOTE: 4201H is the REPEAT DELAY COUNTER.
*379CLD L,96HLoad L with 96H to set a fast repeat count.
*379ELD (41FFH),HLSave HL into the memory location at 41FFH.
NOTE: 41FFH is the REPEAT DELAY COUNT.
*37A4 - Model 4 Gen 1 routine jumped to from the the middle of the tokenize routine.
CP 22HIf 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 ".
*37A6If the character in register A is not a ", then JUMP forward a few instructions to 37B2H.
*37A8LD A,(409FH)Load A with the memory contents of 409FH.
NOTE: 409FH is the DATA FLAG.
*37ABXOR 01HXOR 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.
*37ADLD (409FH),ALoad memory location 409FH with a the XOR'd results.
NOTE: 409FH is the DATA FLAG.
*37B0LD A,22HLoad A with 22H, which is a ".
CP 3AHCompare 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.
*37B4If A is 3AH then JUMP to 06AAH.
NOTE: 06AAH is in the middle of the KEYBOARD DRIVER ENTRY ROUTINE.
*37B7LD A,(409FH)Otherwise, Load A with the memory contents of 409FH.
NOTE: 409FH is the DATA FLAG.
*37BBRRARotate 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.
*3792If Bit 7 of the DATA FLAG was set, then JUMP to 06A8H.
NOTE: 06A8H is in the middle of the KEYBOARD DRIVER ENTRY ROUTINE.
*37BERLARotate 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.
*37BFJUMP 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$)
RST 10HCall 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.
*37C3PUSH HLSave HL (the current position) to the STACK.
*37C4LD A,11HLoad A with 11H (Decimal: 17).
NOTE: This is to set up for a 17 Byte String.
*37C9LD HL,(40D4H)Load HL with the memory contents of (40D4H).
NOTE: 40D4 is the string pointer.
*37CFLD (HL),20HLoad the memory location pointed to by HL with a SPACE.
*37D1INC HLIncrement HL to move 1 character over.
*37D8 - Model 4 Gen 1 routine to Toggle Caps Lock.
*37DDXOR (HL)Invert the contents of the (4019H).
*37DELD (HL),AStore the inverted (i.e., toggled) contents of (4019H) back into (4019H)
*37E0RETRETurn to Caller.
*37E1 - Model 4 Gen 1 routine to do a very short delay routine
*37E5 - Model 4 Gen 1 routine Unused Code
*37E5NOP
*37E6NOP
*37E7NOP
*37E8RST 38H
*37E9RST 38H
*37EANOP
*37EBH - Model 4 Gen 1 routine to display the Copyright Message
*37F4 - Model 4 Gen 1 routine to enable the TIME$ Command
*37F7LD (4177H),HLLoad 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.
*37F7JUMP to 022EH to continue.
NOTE: 022EH will enable interrupts, show the READY prompt, and RETURN.
*37FD - Model 4 Gen 1 unused Code
*37FDPUSH HL
*37FEPUSH HL
*37FFRST 38H
*3739H - Model 4 Gen 2 - Part of the Keyboard Routine to check for SHIFT and CAPS LOCK and deal with them accordingly.
*3739LD A,(3880H)3A 80 38Load A with the value held at 3880H (which are the SHIFT keys)
*373CAND 03HE6 03Mask the SHIFT Register against 03H (Binary: 0000 0011) to keep only Bits 0 and 1 to check for shifts.
*373EJR Z,3742H28 02If the masked A is 0, then we have no shifts, and skip the next instruction (to 3742H).
*3740SET 6,DCB F2Set BIT 6 of D to offset D for shifts.
*3742LD A,(4019H)3A 19 40Load A with the contents of memory location 4019H to check for CAPS LOCK.
NOTE: 4019H is the CAPS LOCK TOGGLE.
*3745OR AB7Set the flags.
*3746JR Z,3753H28 0BIf the ZERO flag is set then there is NO CAPS LOCK so JUMP to 3753H to skip the next intructions.
*3748SET 7,DCB FASet 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.
*374ALD A,(3880H)3A 80 38Put the contents of memory location 3880H into A to GET SHIFT(S).
*374DAND 03HE6 03Mask the SHIFT Register against 03H (Binary: 0000 0011) to keep only Bits 0 and 1 to check for shifts.
*374FJR Z,3753H28 02If the masked A is 0, then we have no shifts, and skip the next instruction (to 3753H).
*3751RES 7,DCB BARESET 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.
*3753LD HL,304EH21 4E 30Load HL with 304EH (the KEYBOARD TABLES).
*3756LD E,D5AWe need DE to be the OFFSET, so load E with D and ...
*3757LD D,00H16 00... load D with 00.
*3759ADD HL,DE19Add DE (the offset over the keyboard table) to HL (the keyboard table).
*375ALD A,(HL)7EGet the character pointed to by (HL) and put it into A.
*375BRETC9RETurn 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
*375CCP 22HFE 22If 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 ".
*375EJR NZ,376AH20 0AIf the character in register A is not a ", then JUMP forward a few instructions to 376AH.
*3760LD A,(409FH)3A 9F 40Load A with the memory contents of 409FH.
NOTE: 409FH is the DATA FLAG.
*3763XOR 01HEE 01XOR 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.
*3765LD (409FH),A32 9F 40Load memory location 409FH with a the XOR'd results.
NOTE: 409FH is the DATA FLAG.
*3768LD A,22H3E 22Load A with 22H, which is a ".
*376ACP 3AHFE 3ACompare 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.
*376CJP NZ,06AAHC2 AA 06If A is NOT a : then JUMP to 06AAH.
NOTE: 06AAH is in the middle of the KEYBOARD DRIVER ENTRY ROUTINE.
*376FLD A,(409FH)3A 9F 40Otherwise, Load A with the memory contents of 409FH.
NOTE: 409FH is the DATA FLAG.
*3772RRA1FRotate 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.
*3773JP C,06A8HDA A8 06If Bit 7 of the DATA FLAG was set, then JUMP to 06A8H.
NOTE: 06A8H is in the middle of the KEYBOARD DRIVER ENTRY ROUTINE.
*3776RLA17Rotate 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.
*3777JP 06A3HC3 A3 06JUMP 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
*377ALD (IX+05H),05HDD 36 05 00Load the memory location pointed to by IX+5 with a 05H.
NOTE: IX+5 is the number of characters printed.
*377EINC (IX+04H)DD 34 04Bump the number of lines already printed (which is tracked in IX+04H)
*3781LD A,(IX+04H)DD 7E 04Fetch the number of lines already printed (which is tracked in IX+04H) into Register A
*3784CP (IX+03H)DD BE 03Compare the number of lines already printed (held in Register A) against the number of lines left in the page (tracked in IX+03H)
*3787RET NZC0If the number of lines already printed doesn't match the number of lines left in the page then RETURN
*3788LD (IX+04H),04HDD 36 04 01If they DO match, then set the number of lines already printed to 04H
*378CRETC9RETurn 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
*378DLD A,C79LET Register A = Register C.
*378ECP E0HFE E0Compare 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
*3790JP NC,0043HD2 43 00If 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).
*3793CP C0HFE C0If 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
*3795JP C,0063HDA 63 00If 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).
*3798SUB C0HD6 C0If 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.
*379ALD B,00H06 00LET Register B = 00H
*379CLD C,A4FLET Register C = Register A.
*379DLD HL,(4220H)2A 20 42LET Register Pair HL the memory contents of 4220H and 4221H.
NOTE: 4220H-4221H is the destination device nName for ROUTE Routine. 2 Bytes
*37A0ADD HL,BC09LET HL = HL + BC
*37A1LD C,(HL)4ELET Register C = the contents of the memory location pointed to by Register Pair HL.
*37A2JP 0063HC3 63 00JUMP 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
*37A5LD HL,3C00H21 00 3CLoad HL with the memory location for the beginning of the video RAM.
*37A8LD A,(HL)7EPut the character at the screen location stored in HL into A.
*37A9RLCA07Rotate 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.
*37ACRLCA07Rotate 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.
*37ADJR C,37B3H38 04If 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
*37AFLD A,2EH3E 2ELoad Register A with a . which is the character which will print instead of non-printable characters.
*37B3LD A,(HL)7EReplace the non-printable character with a .
*37B4CALL 003BHCD 3B 00Call the PRINT CHARACTER routine at 003B (which sends the character in the A register to the printer).
*37B7INC HL23Bump HL to the next character on the screen.
*37B8BIT 6,HCB 74Check 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).
*37BDLD A,L7DTest of end of line by loading A with L.
*37BEAND 3FHE6 3FAND 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).
*37C0JR NZ,37A8H20 E6If 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
*37A5 - Model 4 Gen 2 - Screen Print Routine BUT exits witH Register A at 0 and all FLAGS cleared
*37C7CALL 37A5HCD A5 37GOSUB to 37A5H for the screen print
*37CAXOR AAFClear Register A and all the flags
*37CBRETC9RETurn to CALLer
*37CC - Model 4 Gen 2 - TOGGLE the CAPS LOCK
*37CCLD A,01H3E 01Load Register A with an 01H
*37CELD HL,4019H21 19 40Load HL with 4019H, which is the CAPS LOCK TOGGLE in the keyboard DCB.
*37D1XOR (HL)AEXOR the contents of the CAPS LOCK TOGGLE in RAM against 01H; this effectively toggles it
*37D2LD (HL),A77Put the toggled CAPS LOCK TOGGLE back into RAM where such thing is stored
*37D3XOR AAFClear Register A and Clear all FLAGs
*37D4RETC9RETurn 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.
*37D5PUSH AFF5Preserve Registers A and F to the top of the STACK
*37D6IN A,(EAH)DB EAPoll the RS-232 UART Control Register/Status Register (via Port EAH) into A.
*37D8BIT 6,ACB 77Test Bit 6 of the RS-232 Register. It will be 1 if READY TO SEND
*37DAJR Z,37D6H28 FAIf the UART reported that READY TO SEND is FALSE, LOOP back to 37D6 and poll again.
*37DCPOP AFF1Restore Registers A and F from the top of the STACK
*37DDOUT (EBH),AD3 EBSend the contents held in Register A to the RS-232C Data Register (via Port E8H) so as to send A to the RS-232C.
*37DFRETC9RETurn 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).
*37E0LD A,6DH3E 6DLoad Register A with a 6DH (Binary: 0110 1101)
*37E2OUT (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)
*37E4JP (HL)E9Jump to whatever (HL) is pointing to.
*37E5 - Model 4 Gen 2 - UNUSED CODE.
*37E5NOP00
*37E6NOP00
*37E7NOP00
*37E8RST 38HFF
*37E9RST 38HFF
**37D5-37E9 - Model 4 ROM Student Network Edition - Replace all the above with NOPs
*37EA - Model 4 Gen 2 - BASIC TIMES (DATE$+" "+TIME$)
*37EARST 10HD7Call the EXAMINE NEXT SYMBOL routine at RST 10H.
*37EBPUSH HLE5Save HL (the current position) to the STACK.
*37ECLD A,11H3E 11Load A with 11H (Decimal: 17) to set up for a 17 Byte String.
*37F1LD HL,(40D4H)2A D4 40Load HL with the memory contents of (40D4H), which is the string pointer.
*37F7LD (HL),20H36 20Load the memory location pointed to by HL with a SPACE.
*37F9INC HL23Increment HL to move 1 character over.
This subroutine will delete the character pointed to by Register Pair HL and will correct Register C
Note: 40A7H-40A8H holds the input Buffer pointer
3012 - Model 4 ROM Gen 1
3012 - Model 4 ROM Gen 2
*3024 - Model 4 ROM Gen 1
NOP
NOP
*3024 - Model 4 ROM Gen 2
*302D - Model 4 ROM Gen 1
*302D - Model 4 ROM Gen 2
NOTE: Port E4H is the Non-Maskable Interrupt Latch.
*3042 - Model 4 ROM Gen 1 - Prompt the User to set the cassette baud rate.
*3042 - Model 4 ROM Gen 2 - Prompt the User to set the cassette baud rate.
*3045-3064 - Model 4 ROM Gen 1
*3105H - Model 4 Gen 1 jump to NON-Disk BASIC
*310BH - Model 4 Gen 1 Set the TAPE BAUD RATE ($SETCAS).
NOTE: 0049H is the $KBWAIT routine which scans the keyboard and returns with the key pressed, if any, in register A.
NOTE: A currently holds the character pressed in response to the "CASS?" message.
NOTE: 0033H is the character print routine, to put the character held in Register A at the current cursor position.
NOTE: A will then hold the character pressed in response to the "CASS?" message.
3129H - Model 4 Gen 1 Set the Selected Cassette Baud Rate as LOW SPEED
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
NOTE: 4211H holds the CASSETTE BAUD RATE SELECT as:
- 0: 500 Baud
- Anything Else: 1500 baud
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
*3145 - Model 4 Gen 1 Printer Character Table Codes 32-127.
*3045 - Model 4 ROM Gen 2.
*3045 - Model 4 ROM Gen 2 Keyboard Rows 0-3, Unshifted, No Caps Lock.
*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 - 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.
*308E-30A8 - Model 4 ROM Gen 2 - Continuing with the Keyboard Table
*30C6-30C8 - Model 4 ROM Gen 2 - A little code snippet
**30C6-30C8 - Model 4 ROM Student Network Edition - A little code snippet
*3089 - Model 4 ROM Gen 2 - Continuing with the Keyboard Table
POP BC
NOP
RET
*312E - Model 4 Gen 2 - KEYBOARD Routine - Check and Process the F2 Key or Jump Away.
*3135 - Model 4 Gen 2 - KEYBOARD Routine - Check and Process the F1 Key or Jump Away.
*33C4 - Model 4 Gen 2 - KEYBOARD Routine - Part of the Keyboard Scan Routine. Keep Checking Special Keys.
*33CA - Model 4 Gen 1 - KEYBOARD Routine - Part of the Keyboard Scan Routine.
NOTE: 4224H Holds the CONTROL KEY flag.
*3179 - Model 4 Gen 2 - Continuing Initialization Routine by setting up the RS-232.
NOTE: Port E8H is the RS-232 Status Register & Master Reset. Outputting ANYTHING to Port E8H resets the RS-232.
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
- 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)
**3179 - Model 4 ROM Student Network Edition - Replacement for the above RS-232 Initialization
NOTE: 4210H holds the bit mask for port ECH. Port ECH stores miscellaneous controls.
31A5 - Output the TIMING MARK to the cassette
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.
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.
NOTE: 31F3H resets the cassette port, and then output a 0 to the Cassette Port FFH.
31C0 - Turn Off The Cassette
NOTE: 4213H is the default interrupt vector setting for the cassette.
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
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.
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.
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 "**"
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.
31E8 - Turn On The Cassette - Actually Set the Bit Mask and Output the Command
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.
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.
31F3 - Reset the Cassette Port. This routine OUTputs a 0 to the Cassette Port FFH
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.
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.
NOTE: 0253H is in the middle of the "Write a Byte to Cassette" Routine. It ends in a RETURN.
","
3203 - Vector for a SLOW cassette read
NOTE: 3220H reads the tape until it finds a timing mark or the BREAKis hit.
NOTE: 4212H holds the cassette blinker counter.
NOTE: 4212H holds the cassette blinker counter.
3220 - Cassette - Keep reading tape looking for a timing mark or BREAK.
NOTE: FFH is the Cassette Port.
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)
Reset the cassette port
Next, wait for 98H Units (the length until a data pulse is expected)
... continue
NOTE: Port FFH is the Cassette Port.
3241 - Vector for a SLOW cassette write. On entry A is the byte to output.
NOTE: D will be the DATA BYTE.
3258 - "Write a 0 Bit" by simply waiting the appropriate amount of time and doing nothing.
325E - SLOW tape header write
NOTE: 420CH is the TAPE WRITE VECTOR.
NOTE: A5H is the OUTPUT SYNC BYTE.
3274 - SLOW tape header read
NOTE: 420EH is the TAPE READ VECTOR.
NOTE: 3220H reads the tape until it finds a timing mark or the BREAKis hit.
329B - FAST tape header write.
NOTE: 420CH is the TAPE WRITE VECTOR.
NOTE: 32B4 restore all registers from the STACK, and Fill C with A, and JUMP to cassette write
NOTE: 7FH is the OUTPUT SYNC BYTE.
NOTE: 32B4 restore all registers from the STACK, and Fill C with A, and JUMP to cassette write
NOTE: A5H is the SLOW SYNC BYTE.
32B4 - Restore all registers from the STACK, and Fill C with A, and JUMP to cassette write.
32BA - Save all registers to the STACK, and Fill C with A, GOSBUB to write out the START BIT ...
32C1 - Call 3335H to Output a Bit 8 Times
32CA - Read the start bit, read 8 bits, check for error, and flash the star
32DA - FAST tape header read.
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
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
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.
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.
3350H - READ a BIT
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
NOTE: FFH is the Cassette Port.
337CH - Check for a Data Error.
- 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
*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).
*33B6 - Model 4 Gen 1 - KEYBOARD Routine - Check and Process the F2 Key or Jump Away.
*33B6 - Model 4 Gen 1 - KEYBOARD Routine - Check and Process the F1 Key or Jump Away.
*33C4 - Model 4 Gen 1 - KEYBOARD Routine - Part of the Keyboard Scan Routine. Keep Checking Special Keys.
*33CA - Model 4 Gen 1 - KEYBOARD Routine - Part of the Keyboard Scan Routine.
NOTE: 4224H Holds the CONTROL KEY flag.
*3401 - Model 4 Gen 1 - This is the BOOTSTRAP. Clears ports, checks for a BREAKkey and a Floppy Controller.
NOTE: Port E4H is the non-maskable interrupt latch. This is to clear the non-maskable interrupt status.
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.
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.
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
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
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).
- 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
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.
OR C
- 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
*3461 - Model 4 Gen 1 - Warm Boot Routine.
- 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
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.
OR C
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.
NOTE: 4049H is the Non-Maskable Interrupt Vector.
NOTE: Port E4H is the non-maskable interrupt latch.
NOTE: Port F2H is the Floppy Disk Controller Track Register.
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).
- 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
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.
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.
NOTE: Port E4H is the non-maskable interrupt latch. This is to clear the non-maskable interrupt status.
NOTE: 4049H is the Non-Maskable Interrupt Vector.
- 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
*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.
NOTE: Port E4H is the Non-Maskable Interrupt Latch.
NOTE: Port E4H is the Non-Maskable Interrupt Latch.
*34DA - Model 4 Gen 1 - Part of the Keyboard routine.
NOTE: 4019H is the CAPS LOCK TOGGLE.
NOTE: BIT 7 is the difference between UPPER CASE and LOWER CASE versions of the same letter.
NOTE: BIT 7 is the difference between UPPER CASE and LOWER CASE versions of the same letter.
*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.
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.
Difference between M1 and M3: 01F0H contains CALL 0221H instruction on Model I, and CALL 0214H instruction on Model III.
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
*338EH - Model 4 Gen 2 - Jump Point for Keyboard Input.
Go to the next row
NOTE: 41FDH is the saved position in the keyboard scan.
NOTE: 41FEH is the saved IMAGE at the saved position in the keyboard scan data.
NOTE: 4201H is the REPEAT DELAY COUNTER.
Set the Keyboard Repeat Delay Count to 1500.
NOTE: 41FFH holds the keyboard scan repeat delay count.
*33BC - Model 4 Gen 2 - Keyboard Routine - If the same keys are still pressed then we need to deal with debounce.
NOTE: 41FE is the SAVED IMAGE AT POSITION.
NOTE: 41FDH is the SAVED POSITION IN SCAN.
*33DA - Model 4 Gen 2 - Keyboard Routine.
NOTE: 4201H is the REPEAT DELAY COUNTER.
NOTE: 4201H is the REPEAT DELAY COUNTER.
NOTE: 41FFH is the REPEAT DELAY COUNT.
NOTE: 4201H is the REPEAT DELAY COUNTER.
NOTE: 41FFH is the REPEAT DELAY COUNT.
*33F9 - Model 4 Gen 2 - Enter NON-Disk BASIC.
*33FF - Model 4 Gen 2 Routine - Initialization - Process the CASS? Question
NOTE: 021BH will display the character at (HL) until a 03H is found.
NOTE: 0049H is the $KBWAIT routine which scans the keyboard and returns with the key pressed, if any, in register A.
NOTE: Register A currently holds the character pressed in response to the "CASS?" message.
Set the flag for LOW SPEED CASSETTE
NOTE: 4211H holds the CASSETTE BAUD RATE SELECT as:
- 0: 500 Baud
- Anything Else: 1500 baud
*3426 - Model 4 Gen 2 Routine - This is the BOOTSTRAP. Turns on Interrupts, sets the stack, sets the CRT Controller, sets the FDC.
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.
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.
NOTE: Port E0H is the maskable interrupt latch, which directs jumps. In the case of 0100, to 4046H.
The Model 4 ROM Student Network Edition ROM changes that to ...
- 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
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.
- 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
If we are here, then we hit track 0, so proceed to process a Warm Boot.
- 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
*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.
*349B - Model 4 Gen 2 Routine - We have exhausted our tries, so display DISKETTE and keep trying.
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.
*34AC - Model 4 Gen 2 Routine - Look for the INDEX Mark on the diskette.
- 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
*34BA - Model 4 Gen 2 Routine - Finish initializing the floppy disk boot.
We are going to try to read a sector
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.
NOTE: 4049H is the Non-Maskable Interrupt Vector.
NOTE: Port F3H is the Floppy Disk Controller Data Register.
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).
- 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
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.
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.
- 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
*350B - Model 4 Gen 2 Routine - NMI handler. On entry, A holds Bit 5 of the Non-Maskable Interrupt Latch at port 0E4H.
*3517 - Model 4 Gen 2 Routine - Finish up initialization by filling 256 bytes into 4300H and then JUMPing there
**3517 - Model 4 ROM Student Network Edition - Finish up initialization
3529H - Deal with the cursor.
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.
NOTE: 4022H holds the Cursor ON/OFF Flag and will be 0 if the cursor is off, or the the underscore character otherwise.
NOTE: 401CH holds the Cursor Blink Switch and will be 0 for Blink, and anything else for No Blink.
NOTE: 401AH is the memory location that stores the cursor blink count.
NOTE: 401AH is the memory location that stores the cursor blink count.
NOTE: 401AH is the memory location that stores the cursor blink count.
NOTE: 401BH holds the cursor blink status - 0 = Off, Anything Else = On.
NOTE: 4020H holds the current cursor position.
NOTE: 4023H holds the cursor character.
3556H - Update the heartbeat and deal with the time, including rollover to the next day, month, and year.
NOTE: (HL) will hold the current cursor position.
NOTE: 4216H is the heartbeat counter.
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.
NOTE: If HL is 4217H then it is seconds, 4218H then it is minutes, 4217H then it is hours.
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.
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.
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.
NOTE: 421BH holds the current DAY portion of the date.
NOTE: 421BH holds the current MONTH portion of the date.
NOTE: 421BH holds the DAY portion of the date.
NOTE: DE currently points to the memory locations housing the of days in each month.
NOTE: If this is not a loop, the HL holds the day portion of the date.
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.
NOTE: 421AH points to the current YEAR.
NOTE: 421BH points to the current DAY.
NOTE: 421BH points to the current MONTH.
NOTE: This will test against a month 13. If A is < 13, then the CARRY FLAG will be set.
NOTE: 421BH points to the current DAY.
NOTE: 421BH points to the current YEAR.
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.
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.
NOTE: 4216H is the heartbeat counter.
NOTE: A CP actually subtracts 1E from A without modifying A, but the flags are set accordingly.
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.
NOTE: This routine is also used to convert the date, and C will be swapped out to a /for that routine.
NOTE: This is because we need to convert 3 numbers, so we will loop 3 times.
NOTE: This will be HOUR (4219H) on the first pass, MINUTE (4218H) on the second pass, and SECOND (4219H) on the third pass.
NOTE: 2F is a /which is also 1 character below a 0.
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 /.
35C2H - Maskable Interrupt Handler.
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.
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.
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.
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.
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.
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.
35FBH - RS-232 Initialization Routine. I'm [guessing] that IX is set to 41F5H
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)
NOTE: Port E8H is the RS-232 Status Register & Master Reset. Outputting ANYTHING to Port E8H resets the RS-232.
NOTE: 41F8H holds the baud rate code.
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
NOTE: 41F9 holds the RS-232 Configuration Code.
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)
NOTE: 41FAH holds the RS-232 Wait Switch.
NOTE: 41E9H is the RS-232 Input DCB:
- Bit 2: Driver On/Off
- Bit 1: Wait/No Wait
NOTE: 41E9H is the RS-232 Input DCB:
- Bit 2: Driver On/Off
- Bit 1: Wait/No Wait
NOTE: 41F1H is part of the RS-232 Output DCB:
- Bit 2: Driver ON/OFF
- Bit 1: Wait/No Wait
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
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.
- 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
NOTE: 41F0H is the 1 characer output buffer for the RS-232 Output DCB.
365AH - RS-232 Input Routine.
NOTE: 41E5H is the DCB for RS-232 Input. 41E8H is the 1 Character RS-232 Input.
NOTE: 41E8H is the 1 Character RS-232 Input.
NOTE: Bit 2 of 41E9 contains the DRIVER ON/OFF.
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)
NOTE: Bit 7 will be 1 if DATA READY (1=True).
NOTE: Bit 1 of 41E9 contains the WAIT/NO WAIT of the RS-232 Input DCB.
NOTE: 4203H JUMPS to 022EH and is the break vector for tape and RS-232.
NOTE: Port EBH is the RS-232C Data Register. It contains the data received from the RS-232C.
NOTE: 41E8H is the 1 Character RS-232 Input.
3680H - RS-232 Output Routine.
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)
NOTE: 41F1H Hholds DRIVER ON/OFF in BIT 2, and WAIT/NO WAIT in BIT 1.
NOTE: 4203H JUMPS to 022EH and is the break vector for tape and RS-232.
NOTE: 41F0H is RS-232 output buffer byte.
NOTE: Port EBH is the RS-232C Data Register. It contains the data to be sent to the RS-232C.
36AAH - Initial Vectors and DCBs for RAM 4000H-404BH.
36F5H - UNUSED.
36F9H - Initial Vectors and DCBs for RAM 41E5H-4224H.
*3739H - Model 4 Gen 1
*3748 - Model 4 Gen 1 routine to Go to the next Keyboard row
NOTE: 41FDH is the saved position in the keyboard scan.
NOTE: 41FEH is the saved IMAGE at the saved position in the keyboard scan data.
NOTE: 4201H is the REPEAT DELAY COUNTER.
*375F - Model 4 Gen 1 routine to Set the Keyboard Repeat Delay Count to 1500.
NOTE: 41FFH holds the keyboard scan repeat delay count.
*3767 - Model 4 Gen 1 routine to Keyboard Repeat - Jumps Here if the same keys are still pressed.
NOTE: 41FE is the SAVED IMAGE AT POSITION.
NOTE: 41FDH is the SAVED POSITION IN SCAN.
*3785 - Model 4 Gen 1 routine to Keyboard Repeat - Jumps Here if the same keys are still pressed.
NOTE: 4201H is the REPEAT DELAY COUNTER.
NOTE: 4201H is the REPEAT DELAY COUNTER.
NOTE: 41FFH is the REPEAT DELAY COUNT.
NOTE: 4201H is the REPEAT DELAY COUNTER.
NOTE: 41FFH is the REPEAT DELAY COUNT.
*37A4 - Model 4 Gen 1 routine jumped to from the the middle of the tokenize routine.
NOTE: 409FH is the DATA FLAG.
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.
NOTE: 409FH is the DATA FLAG.
NOTE: 06AAH is in the middle of the KEYBOARD DRIVER ENTRY ROUTINE.
NOTE: 409FH is the DATA FLAG.
NOTE: 06A8H is in the middle of the KEYBOARD DRIVER ENTRY ROUTINE.
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$)
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.
NOTE: This is to set up for a 17 Byte String.
NOTE: 40D4 is the string pointer.
*37D8 - Model 4 Gen 1 routine to Toggle Caps Lock.
*37E1 - Model 4 Gen 1 routine to do a very short delay routine
*37E5 - Model 4 Gen 1 routine Unused Code
*37EBH - Model 4 Gen 1 routine to display the Copyright Message
*37F4 - Model 4 Gen 1 routine to enable the TIME$ Command
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.
NOTE: 022EH will enable interrupts, show the READY prompt, and RETURN.
*37FD - Model 4 Gen 1 unused Code
*3739H - Model 4 Gen 2 - Part of the Keyboard Routine to check for SHIFT and CAPS LOCK and deal with them accordingly.
NOTE: 4019H is the CAPS LOCK TOGGLE.
NOTE: Bit 7 is the difference between UPPER CASE and LOWER CASE versions of the same letter.
NOTE: BIT 7 is the difference between UPPER CASE and LOWER CASE versions of the same letter.
*375C - Model 4 Gen 2 - Part of the TOKENIZE Routine and KEYBOARD shared routine. Same as Gen 1 but with different JUMP points
NOTE: 409FH is the DATA FLAG.
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.
NOTE: 409FH is the DATA FLAG.
NOTE: 06AAH is in the middle of the KEYBOARD DRIVER ENTRY ROUTINE.
NOTE: 409FH is the DATA FLAG.
NOTE: 06A8H is in the middle of the KEYBOARD DRIVER ENTRY ROUTINE.
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
NOTE: IX+5 is the number of characters printed.
*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
- 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
- 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
NOTE: 4220H-4221H is the destination device nName for ROUTE Routine. 2 Bytes
*37A5 - Model 4 Gen 2 - Screen Print Routine
*37A5 - Model 4 Gen 2 - Screen Print Routine BUT exits witH Register A at 0 and all FLAGS cleared
*37CC - Model 4 Gen 2 - TOGGLE the CAPS LOCK
*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.
*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).
- 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)