Model III ROM Explained - Part 1

Introduction

These 3 pages are a breakdown of the Model III ROM, with comments to help you understand what is going on.

The two thing to keep in mind while reading this disassembly are:

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

Addresses with a "*" in front of them, which are also in blue, differ from the Model I.

There is no difference between the early Model III ROM and the later Model III ROM in this range. The Model 4 ROM is also identical to the Model III ROM in this range.

Model III ROM Disassembled


0000-0004 - POWER UP ROUTINE - $RESET

A jump to this routine reinitializes the entire system starting at the "CASS?" prompt. If a disk controller is present, the Computer will attempt to load DOS UNLESS BREAK was held down before this jump.

0000RESET
DI
Disables the interrupts and turns off clock
0001
XOR A
Clears the A register and status
*0002-0004
Go to the Boostrap/Initialization routine at 3015H (which just jumps to 3455H which is the BOOTSTRAP (sets interrupts, clears ports, checks for a BREAK key and tries to work with the disk controller).
Difference between M1 and M3: The jump is to the power-up routine, located at 0674H in the Model I but jump is to 3015H in the Model III (3015H is the location of a vector to the start of the actual routine).
0005-0007
JP 4000H
Go to RST 0008H code via 4000H.

0008 - RST 08H

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

000B-000F - DISK ROUTINE - "WHERE"

000B
POP HL
Get the address from the STACK and put it in register pair HL.
000C
JP (HL)
JUMP to the location of the address in register pair HL.
*000D-000FDBOOT
JUMP to the disk load and run sector routine at 069FH.
Difference between M1 and M3: The jump is to the disk bootstrap loader routine, located at 069FH in the Model I but jump is to 3012H in the Model III (3012H is the location of a vector to the start of the actual routine).

0010 - RST 10H

0010H
JP 4003H
JUMPs to 1D78H (RST 0010H vector) through 4003H.

This routine INCrements HL and tests the characters pointed to by the HL register pair. It will bypass any spaces and CHAR'S 9 and 10 (shifted left and down arrows respectively). Upon return from this routine HL will point to the next non-blank character; the CARRY FLAG will be SET 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.

0013-0017 - KEYBOARD ROUTINE.

This is jumped to from 002E (to get a byte from the Keyboard) and 0053 (to get a byte from the RS-232).

This routine inputs a byte from an input device. When calling, DE = must contain the starting address of DCB of device. On exit, A = byte received from device, Z set if device ready. Uses AF.

0013INBYT
PUSH BC
Save the value in Register Pair BC to the STACK.
0014-0015
LD B, 01H
Load register B with the device type entry code of 01H.
0016-0017
JUMP to the Level II BASIC driver entry routine at 0046H which in turn jumps to 0674H, which is the keyboard driver entry routine.

0018 - RST 18H

0018
(RST 018H)
JP 4006H
JUMPs to lC90H through 4006H. This routine can be called by using RST 18H or CALL lC90H. It compares two 16 bit values in HL and DE and sets the S and Z flags accordingly (they are set in the same way as for a normal 8 bit CP). All registers are unchanged except for A.
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:
ConditionFlag
If HL < DECARRY SET
If HL > DENO CARRY
If HL ≠ DENZ
If HL = DEZ

001B-001E - DRIVER ENTRY ROUTINE - Part 1 - "PUT" or "OUTBYT"

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

001B
PUSH BC
Save the value in register pair BC on the STACK.
001C-001D
LD B,02H
Load register B with the device type entry code of 02H.
001E-001F
JUMP to the Level II BASIC driver entry routine at 0046H which in turn jumps to 0674H, which is the keyboard driver entry routine.

0020 - RST 20H

0020H
JP 4009H
This routine jumps to 25D9H through 4009H. If the NTF=8 then C=RESET or else C=SET, Z flag will be SET if NTF=3 (S flag is valid also.). After execution of RST 20H or CALL 25D9H, A will contain the value NTF-3, all other registers are unchanged.
Returns a combination of STATUS flags and unique numeric values in the A Register according to the data mode flag (40AFH).
If the Variable Type is ...and the Flags
are set ...
... then Register
A will be set ...
IntegerNZ/C/M/E-1
StringZ/C/P/E0
Single PrecisionNZ/C/P/O1
Double PrecisionNZ/NC/P/E5
This CALL is usually made to determine the type of the current value in REG 1 (i.e., 4121H). It should be used with caution, however since the mode flag and REG 1 (i.e., 4121H) can get out of phase particularly if some of the CALLS described here are used to load REG l.

0023-0027 - DISK ROUTINE - "CTLBYT"

0023
PUSH BC
Save the value in register pair BC on the STACK.
0024-0025
LD B,04H
Load register B with the device type entry code of 04H.
0026-0027
JUMP to the Level II BASIC driver entry routine at 0046H which in turn jumps to 0674H, which is the keyboard driver entry routine.

*0028H - RST 28H Routine Vector

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

002B-002F - KEYBOARD ROUTINE - "$KBCHAR"

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 Aregister 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 SYSO 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

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).

002B-002DKBCHAR
LD DE,4015H
Load register pair DE with the starting address of the keyboard device control block.
NOTE: 4015H holds Keyboard DCB - Device type.
002E-002F
JUMP to the Level II BASIC driver entry routine.

0030 - RST 30H

0030H
JP 400FH
This location passes control to 400FH which contains a RET (C9H) under Level II. This location is only used by a Disk system.
This is the LOAD DEBUG routine, and loads the DEBUG program and transfers control to it. When DEBUG processing is complete, control is returned to the orginal caller. For non-disk systems control is returned immediately.

0033-0037 - "$VDCHAR" - Display a character.

This subroutine displays a character at the current cursor location. On entry, Register A contains the character to display.

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

0033-0035VDCHAR
LD DE,401DH
Load register pair DE with the starting address of the video display device control block.
NOTE: 401DH holds is the beggining of Video Display Control Block.
0036-0037
JUMP to the Level II BASIC driver entry routine at 001BH.
NOTE: 001BH saves BC to the STACK, Loads B with a 02H device code, and then JUMPs to 0046H which JUMPs to the keyboard entry driver routine at 0674H.

0030 - RST 38H

0038HRST38
JP 4012H
This location will pass control to 4012H. This location is only used by a Disk system.
This is the INTERRUPT ENTRY POINT routine at RST 38H which is the system entry point for all interrupts. It contains a jump to a section of code in the Communications Region designed to field interrupts. That section of code consists of a DI (disables further interrupts) followed by a RET (returns to the point of interrupt) for non-disk systems, or a jump to an interrupt processor in SYS0/SYS 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.

003B-003F - PRINTER ROUTINE - "$PRCHAR"

This routine waits until the printer is available or until a BREAK key his pressed. If BREAK is pressed, this routine returns to the caller. On entry Register A holds the ASCII character to be output to the printer.

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.

003B-003D
LD DE,4025H
Load register pair DE with the starting address of the printer device control block.
NOTE: 4025H holds Printer DCB - Device type.
003E-003F
JUMP to the Level II BASIC driver entry routine at 001BH.
NOTE: 001BH saves BC to the STACK, Loads B with a 02H device code, and then JUMPs to 0046H which JUMPs to the keyboard entry driver routine at 0674H.

0040-0042 - INPUT ROUTINE - "$KBLINE"

This routine gets a full like from the keyboard. The line is terminated by a carriage return or BREAK. Characters typed are echoed to the display. On entry, B must be the maximum length of line to be accepted and (HL) must be the storage buffer which should be set to B+1. On Exit, CARRY will be set if the BREAK key was hit, Register B will contain the number of characters entered, and (HL) will contain the line from the keyboard followed by the terminating character. Register paid DE is altered in this routine.

0040-0042KBLINE
JUMP to the "WAIT FOR NEXT LINE" keyboard input routine at 05D9H (which takes keyboard entry until a carriage return, a break, or buffer overrun occurs).
0043
RET
0044
NOP
0045
NOP
0043-0045
JUMP to 0434H to print the character held in C.

0046-0048 - DRIVER ENTRY ROUTINE - Part 2 - "DRIVRV"

*0046-0048
JUMP to 0674H.
NOTE: 0674H is the Level II BASIC keyboard driver entry routine.

Difference between M1 and M3: The jump is to the I/O driver entry routine, located at 03C2H in the Model I but relocated to 0674H in the Model III.

*0049-004F - KEYBOARD ROUTINE - "$KBWAIT" - Go scan the keyboard and return with the key pressed, if any, in register A.

A CALL to this memory location returns as soon as any key on keyboard is pressed. ASCII value for character entered is returned in A register. Uses A, status, and DE registers. If BREAK is hit, then it will be returned in Register A just like any other key (as an 01H). The characters are not echoed to the display.

A call to 0049H returns as soon as any key on keyboard is pressed. ASCII value for character entered is returned in A register.

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

*0050-0053 - "$RSRCV" - Receive a character from the RS-232-C Interface

If the RS-232-C wait is enabled, this routine will wait for a character to be received, unless a BREAK is pressed. If the wait is NOT enabled, it returns whether a character is received or not. The character is stored in 16872. A 0 means no character received.

Difference between M1 and M3: In the Model I, locations 0050H through 005FH are a lookup table for special characters associated with the keyboard scan routine. These same locations in the Model III contain the entry points for routines associated with the RS-232-C interface C Receive character at 00S0H, Transmit character at 00SSH, Initialize RS-232-C at 00SAH). In both models, 0060H is the start of a time-delay routine, but in the Model III the three bytes starting at 0060H have been changed to a JP 01FBH instruction. The actual time delay routine (same as in the Model I except that an extra instruction is added to compensate for the faster clock speed of the Model III) has been moved to 01FBH in the Model III. Note that entry at 0063H has the same effect in both models, that is, the time delay will occur only if the z flag is reset (NZ).

*0050RSRCV
LD DE,41E5H
Load DE with the RS-232 Input DCB of 41E5H.
*0053
Input a Byte from the RS-232

*0055-0058 - "$RSTX" - Transmit a character to the RS-232-C Interface

If the RS-232-C wait is enabled, this routine will wait until the character is transmitted unless a BREAK is pressed. If the wait is NOT enabled, it returns whether a character is transmitted or not. The character is stored in Register A.

*0055RSTX
LD DE,41EDH
Load DE with the RS-232 Output DCB of 41EDH
*0058
Output a Byte to the RS-232

*005A-005F - "$RSINIT" - Initialize the RS-232-C Interface

Initialize the RS-232-C interface to the following: Baud=300, Word Length=8, Parity=None, Stop Bits=One, Wait for completion of character I/O.

*005ARSINIT
LD DE,41F5H
Load DE with the RS-232 Controller DCB of 41F5H
*005D
Set up the RS-232
*005F
NOP
-

*0060-0065 - "$DELAY"

This is a general purpose routine to be used whenever you want to pause before continuing with a program.

On entry, BC must be loaded with the delay multiplier. The actual delay will be 2.46 + (14.8 * BC) microseconds. When BC is zero, the maximum delay is used (65,535) which is about 1 second. Routine uses BC and A.

*0060
*0063
If the ZERO flag is not set, run that delay routine again.
*0064DELAY
JUMP to the delay routine at 01FBH, which uses BC as a loop counter. It RETs when done, so the next instruction is NOT a pass-through!
*0065
RET
Return
*0063
This is jumped to by 377EH and JUMPs to 041FH (all printer stuff).
*0066
Go to the non-maskable interrupt routine at 3039H.
Difference between M1 and M3: Pressing the RESET button causes a non-maskable interrupt, which in turn forces a jump to 0066H. In the Model I, a portion of the code that handles non-maskable interrupts is located from 0066H to 0071H. In the Model III, 0066H contains a jump to 3039H (the non-maskable interrupt routine in the Model III), 0069H contains a jump to 0452H (routine to initialize all I/O drivers), and 006CH is the beginning of the routine to route I/O ( loads DE with 421DH and then jumps to 001BH).

*0069 - "$INITIO" - A call to $INITIO restores all I/O drivers to their initial default conditions, including I/O routes. No entry conditions. All registers are altered.

*0069INITIO
Initialize all I/O Drivers by jumping to 0452

*006C - "$ROUTE" - Change I/O Device Routing

On entry store the source device of KI, DO, RI, RO, or PR into 4222H and 4223H as the destination device in 4220H and 4221H.

*006CROUTE
LD DE,421DH
Load DE with the $ Routine DCB at 421DH
*006F
Set the I/O Routine by jumping to 001BH
*0071
NOP
-
*0072
JUMP to 06CCH which decrements BC, gets the character at (BC) and checks to see if it is an "R".
*006C
RET
It does not appear that this byte is ever used.
*006D-006F
LD BC,1A18H
Load BC with the memory address of the routine to return to Level II BASIC because the next JP requires a memory address in BC. This new code is the jump point for hitting a BREAK during a SYSTEM request for FILENAME. The Model III used to reboot. With this code, it goes back to the READY prompt via THIS routine.
*0070-0072
JUMP to 19EAH which is the middle of the startup routine. With BC as it is, the variables will get reset and we will return to Level II BASIC.

0075-0104 - INITIALIZATION ROUTINE

This is part of the Level II initialization procedure. It moves a block of memory from 18F7H to l9lEH up to 4080H to 40A7H. (reserved RAM. area).
NOTE: 4080H-408DH is a division support routine.

0075-0077CSTLII
LD DE,4080H
Load register pair DE with the ROM storage location of the Level II BASIC division routine.
NOTE: 4080H-408DH is a division support routine.
0078-007A
LD HL,18F7H
Load register pair HL with the RAM storage location of the Level II BASIC division routine.
007B-007D
LD BC,0027H
Load register pair BC with the length of the Level II BASIC division routine (39 bytes).
007E-007F
LDIR
Move the Level II BASIC division routine in ROM (18F7H-191DH) to RAM (4080H-40A6H).
*0080-0082
LD HL,42E5H
Continue with the communication region initialization by loading register pair HL with 42E5H.
Difference between M1 and M3: The instruction starting at 0080H loads HL with 41E5H in the Model I, 42E5H in the Model III. Following this, three memory locations (starting with the one pointed to by HL) are loaded with the values 3AH, 00H, and 2CH respectively. HL is then incremented once more and the result (41E8H or 42E8H) points to the start of the input buffer (and is stored at 40A7H).
0083-0084
LD (HL),3AH
Save a 3AH at the location of the memory pointer in register pair HL.
NOTE: 3AH is the Z-80 Instruction of LOAD A, (xxH)
0085
INC HL
Increment the memory pointer in register pair HL.
0086
LD (HL),B
Zero out the location of the memory pointer in register pair HL.
0087
INC HL
Increment the memory pointer in register pair HL.
0088-0089
LD (HL),2CH
Save a 2CH at the location of the memory pointer in register pair HL.
NOTE: 2CH is the Z-80 Instruction INC L.
008A
INC HL
Increment the memory pointer in register pair HL.
008B-008D
LD (40A7H),HL
This loads the input buffer pointer (held at 40A7H) with the keyboard buffer location address of 42E8H. (40A7H is the I/O buffer pointer and can be changed to relocate the buffer.). Save the value in register pair HL as the starting address of the keyboard input buffer area.
NOTE: 40A7H-40A8H holds the input Buffer pointer.
008E-0090
LD DE,012DH
In prepartaion for a jump, load register pair DE with the starting address of the ?L3 ERROR routine.

0091-0104 - 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 1A19H which is the entry point for the BASIC command mode.

Top of a DJNZ loop of 28 iterations.

0091-0092
LD B,1CH
Load register B with the number of times disk commands (=28) to save the jump to the ?L3 ERROR routine.
0093-0095
LD HL,4152H
Load register pair HL with the starting address of the Disk Basic links (which is 4152H) in preparation for generating an error if disk basic commands are attempted.
NOTE: 4152H-41A3H holds Disk Basic links.
0096-0097
LD (HL),C3H
Save a C3H to the location of the memory pointer in register pair HL.
NOTE: C3H is the first byte of a 3 byte JUMP xxxxH command.
0098
INC HL
Increment the memory pointer in register pair HL to the 2nd instruction of a 3 byte JUMP xxxxH command.
0099
LD (HL),E
Save the LSB of the ?L3 ERROR routine's starting address in register E (i.e., a 2DH) to the location of the memory pointer in register pair HL.
NOTE: Now we have JUMP 2Dxx as the instruction.
009A
INC HL
Increment the memory pointer in register pair HL to the 3rtd instruction of a 3 byte JUMP xxxxH command.
009B
LD (HL),D
Save the MSB of the ?L3 ERROR routine's starting address in register D (i.e., a 01H) to the location of the memory pointer in register pair HL.
NOTE: Now we have JUMP 2D01H as the instruction. 012DH is the JUMP point for a ?L3 ERROR.
009C
INC HL
Increment the memory pointer in register pair HL to the next extnry in the Disk Basic links table.
009D-009E
Do this for all 28 disk basic commands until all of the Disk Basic links have been set to jump to the ?L3 ERROR routine.

End of the DJNZ loop of 28 iterations.

009F-00A0
LD B,15H
Load register B with the number of DOS links (=21) to set to RETs.
NOTE: A DJNZ loop uses Register B to count down, so to prep for that loop, Register B has to be filled with the number of iterations.
00A1-00A2
LD (HL),C9H
Save a C9H to the location of the memory pointer in register pair HL.
NOTE: C9H is a RET instruction.
00A3
INC HL
Increment the memory pointer in register pair HL a total of 3 times, as a JUMP is 3 instructions, but RET is just 1 instruction.
00A4
INC HL
Increment the memory pointer in register pair HL.
00A5
INC HL
Increment the memory pointer in register pair HL.
00A6-00A7
Loop back to 00A1 until all 28 of the DOS links have been set to RETs.

001B-001F - VIDEO AND PRINTER ROUTINE

*00A8-00AA
LD HL,43E8H
Load register pair HL with the starting address of user RAM (which is 42E8H).
Difference between M1 and M3: The instruction starting at 00A8H loads HL with 42E8H in the Model I, 43E8H in the Model II I. This memory location is then zeroed (BASIC programs begin at the FOLLOWING memory location in non-disk systems).
00AB
LD (HL),B
Zero 42E8H (i.e., the location of the memory pointer in register pair HL).
*00AC-00AE
LD SP,42F8H
Set the current STACK pointer to 42F8H.
Difference between M1 and M3: Instruction starting at 00ACB loads the Stack Pointer with 41F8H in the Model I and 42F8H in the Model III.
00AF-00B1
GOSUB to 1B8FH to initialize the Level II BASIC variables and pointers.
*00B2-00B4
NOP
NOP
NOP
Difference between M1 and M3: In the Model I, a CALL to 01C9H (the "clear screen" routine) in located here (just prior to printing the "MEMORY SIZE?" prompt on the video display). In the Model III this has been replaced by three zero bytes (NOPs).
00B5-00B7
LD HL,0105H
Load register pair HL with the starting address of the MEMORY SIZE? message.
00B8-00BA
We need to display the MEMORY SIZE? prompt so we must 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).
00BB-00BD
Print a ? and get input from the keyboard.
00BE-00BF
If the BREAK key was pressed (because the CARRY FLAG was set), ask again.
00C0
RST 10H
Since we now need to increment the input buffer pointer until it points to the first character of the input, call the EXAMINE NEXT SYMBOL routine at RST 10H.
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.
00C1
OR A
Set the status flag based on if the character at the location of the input buffer pointer in register A is an end of the input character (00H).
00C2-00C3
JUMP forward to 00D6H (to skip the next instructions which attempt to calculate the memory size) if there was a response to the MEMORY SIZE? question.
*00C4-00C6MEMSIZ
LD HL,444CH
If we are here then just an ENTER was hit in response to the MEMORY SIZE? question, so we need to figure it out dynamically, so load register pair HL with the starting address for the memory size check.Difference between M1 and M3: The instruction starting at 00C4H loads HL with 434CH in the Model I, 444CH in the Model III. If only "ENTER" was pressed in response to the "MEMORY SIZE?" prompt, a memory test is initiated starting at the location pointed to by HL, and continuing upward until the end of memory (or a bad memory location) is reached.
00C7
INC HL
We are going to start testing RAM at 444DH (Decimal: 17485) toward 65535, so increment the memory pointer in register pair HL.
00C8-00C9
LD A,H
OR L
The easiest way to test a 2 byte register for zero is to load the MSB into A and then OR it with the LSB. if the MSB was 0 and the LSB was 0, then A will be 0. So ... first, load register A with the MSB of the current memory pointer in register H.
00CA-00CB
If HL has gone all the way up to 0000H (which means the last test tested 65535), JUMP to out of this testing routine to 00E7H.
00CC
LD A,(HL)
Load register A with the value at the location of the current memory pointer in register pair HL.
00CD
LD B,A
Load register B with the value in register A to preserve it, as A is about to get used.
00CE
CPL
Complement the value in register A (which is basically a test pattern).
00CF
LD (HL),A
Save the test pattern in register A to the location of the current memory pointer in register pair HL.
00D0
CP (HL)
Check to see if the value at the location of the memory pointer in register pair HL is the same as the value in register A.
00D1
LD (HL),B
Put back the original memory value (which was saved in B) to the location of the memory pointed in register pair HL.
00D2-00D3
If the address exists, loop back to 00C7H until the end of memory is found.
00D4-00D5
If the address didn't exist, jump to 00E7H (which goes to he next address and tries again).

00D6 - Interpret the MEMORY SIZE response (whether it was actual via a JUMP from 00C2H or determined via a pass through), and do a memory test.

00D6-00D8
Here the MEMORY SIZE? answer is in HL so call the ASCII TO INTEGER routine at 1E5AH (which will put the answer into DE in integer format).
NOTE: The routine at 1E5A converts the ASCII string pointed to by HL to an integer deposited into DE. If the routine finds a non-numerica character, the conversion is stopped.
00D9
OR A
Set the FLAGS based on A.
00DA-00DC
Display a ?SN ERROR if register A is not equal to zero.
00DD
EX DE,HL
Swap DE (where the integer version of the MEMORY SIZE? answer is located) and HL, so that register pair HL now has with the MEMORY SIZE answer again, but in integer format.
00DE
DEC HL
Decrement the MEMORY SIZE? in register pair HL.
00DF-00E0
LD A,8FH
Load register A with a memory test value of 8FH (Binary: 1000 1111).
00E1
LD B,(HL)
Load register B with the value at the location of the MEMORY SIZE? pointer in register pair HL (to save the data thats there).
00E2
LD (HL),A
Put the test pattern (in A which is 8FH) into that the location of the MEMORY SIZE? pointer in register pair HL.
00E3
CP (HL)
Check to see if the value in the memory location set in HL matches the test pattern in A.
00E4
LD (HL),B
Restore the old memory contents back.
00E5-00E6
The test at MEMORY SIZE? -1 failed so we have to ask MEMORY SIZE again by jumping to 00B5H.
00E7
DEC HL
Decrement the memory size pointer in register pair HL, so it is the amount of memory - 2.
*00E8-00EA
LD DE,4514H
Load register pair DE with the minimum MEMORY SIZE? response (held at 4514H).
Difference between M1 and M3: The instruction starting at 00E8H loads DE with 4414H in the Model I and 4514H in the Model III. This is the minimum "MEMORY SIZE" that can be specified by the user.
00EB
RST 18H
Now we need to check to see if the MEMORY SIZE? pointer (in HL) is less than the minimum MEMORY SIZE? response (in DE), so we call the COMPARE DE:HL routine, which numerically compares DE and HL. Will not work for signed integers (except positive ones). Uses the A-register only. The result of the comparison is returned in the status register as:
ConditionFlag
If HL < DECARRY SET
If HL > DENO CARRY
If HL ≠ DENZ
If HL = DEZ
00EC-00EE
If C is set, then the amount of actual memory (in HL) is less than the minimum memory required (in DE), so we have to go to the Level II BASIC error routine and display an OM ERROR.
00EF-00F1
LD DE,FFCEH
Load register pair DE with the default size of the string area (i.e., negative fifty).
00F2-00F4
LD (40B1H),HL
Save the MEMORY SIZE? amount (which is in HL) to 40B1H (which holds the MEMORY SIZE? pointer).
00F5
ADD HL,DE
Subtract the size of the string data (which was -50) from the highest memory address (stored in HE).
00F6-00F8
LD (40A0H),HL
Save the start of string space pointer (which is now held register pair HL) to 40A0H.
NOTE: 40A0H-40A1H holds the start of string space pointer.
00F9-00FB
Go initialize/reset the Level II BASIC variables and pointers.
00FC-00FE
LD HL,0111H
Load register pair HL with the starting address of the RADIO SHACK LEVEL II BASIC message. 00FF-0101H Go display the RADIO SHACK LEVEL II BASIC message.
*00FF
JP 37EBH or CALL 021BH
Different ROM Versions handle this differently. Both will display the message pointed to by HL.
Difference between M1 and M3: The instruction previous to this loads HL with the starting address of the opening message ("RADIO SHACK..."). In the Model I, a CALL to 28A7H (the "display message" routine) is stored here, while in the Model III this has been changed to a jump to 37EBH.
*0102
If the ROM used the JUMP command in the prior instruction, we won't get here. Otherwise, jump to 01E7H (which seems odd since it is jumping right in the middle of the print screen routine).
*00FF
CALL 021BH or CALL 021BH
GOSUB to 021BH to display the message pointed to by HL.
*0102
JUMP to 01E6H to display the copyright message, turn on TIME$, and then JUMP to 022EH to will enable interrupts, show the READY prompt, and RETURN. This was changed by Frank Durda because he moved all the routines from the end of the 3Fxx into where the PRINT SCREEN routine used to be.

0105 - "MEMORY SIZE" Storage Area

*0105
"MEMORY SIZE" + 00H
MEMORY SIZE message storage area.
Difference between M1 and M3: "EMORY" changed to "emory" and "IZE" changed to "ize".

0111 - "RADIO SHACK MODEL-III BASIC" Storage Area

*0111
"RADIO SHACK MODEL-III BASIC" + 0DH
RADIO SHACK MODEL-III BASIC message storage area.
Difference between M1 and M3: "ADIO" changed to "adio", "HACK" changed to "hack", "LEVEL" changed to "Model", etc.

012D-0131 - ?L3 ERROR ROUTINE - "L3ERR"

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

0132-0134 - LEVEL II BASIC "POINT" COMMAND ENTRY POINT

0132
RST 10H
Since we need to bump the current BASIC program pointer until it points to the next character, call the EXAMINE NEXT SYMBOL routine at RST 10H.
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.
0133
XOR A
A will wind up being 0 if the POINT command was entered ... otherwise.
0134
013E80
Z-80 Trick! The byte at this memory location, 01H, is there to turn the real instruction that follows (the operative action of the SET command) into a harmless LD BC,xxxx. This way, they didn't have to jump over SET or RESET to get to the common graphics code. If parsing straight down, this loads BC with 0380H and then moves to 0136H. But if jump straight to 0136H, you skip that 01H opcode, and get a real instruciton of 3EH 80H.

0135-0137 - LEVEL II BASIC SET COMMAND ENTRY POINT

0135-0136
LD A,80H
Load register A with 80H (Decimal:1 28) which is SET.
0136
1A
Z-80 Trick - See the note at 0134H for an explanation.

0138-0139 - LEVEL II BASIC RESET COMMAND ENTRY POINT - "RESET"

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

013A-019CH GRAPHICS ROUTINE - "GRAPH"

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

013A
PUSH AF
Save the graphics mode in register A on the STACK so we can use A for other things.
013B-013C
RST 08H ⇒ 28H
Since SET/RESET/POINT all need a "(" to start with, call the COMPARE SYMBOL routine which comparess the symbol in the input string pointed to by HL register to the value in the location following the RST 08 call. If there is a match, control is returned to address of the RST 08 instruction 2 with the next symbol in the Register A and HL incremented by one. If the two characters do not match, a syntax error message is given and control returns to the Input Phase).
013D-013F
Go evaluate the expression at the location of the current BASIC program pointer in register pair HL (which is the X variable) and return with the 8-bit value in register A.
0140-0141
CP 80H
Check to see if the X value in register A is greater than 80H (Decimal: 128) by testing A-80H. Results:
  • If A=80H it sets the ZERO FLAG
  • If A<80H then the CARRY FLAG will be set
  • If A>=80H then the NO CARRY FLAG will be set
0142-0144
If A is greater than 128, go to 1E4AH to display a ?FC ERROR.
0145
PUSH AF
Save the X value in register A on the STACK.
0146-0147
RST 08H ⇒ ","
At this point we have SET/RESET/POINT, an open parenthesis, and an X variable, so now we must find a ,. To do this call the COMPARE SYMBOL routine which comparess the symbol in the input string pointed to by HL register to the value in the location following the RST 08 call. If there is a match, control is returned to address of the RST 08 instruction 2 with the next symbol in the Aregister and HL incremented by one. If the two characters do not match, a syntax error message is given and control returns to the Input Phase).
0148-014A
Go evaluate the expression at the location of the current BASIC program pointer in register pair HL (which is the Y variable) and return with the 8-bit value in register A.
014B-014C
CP 30H
Check to see if the Y value in register A is greater than 30H (Decimal: 48). If A=30H it sets the ZERO FLAG. If A<30H then the CARRY FLAG will be set and if A>=30H then the NO CARRY FLAG will be set.
014D-014F
If the Y value is greater than 48, display a ?FC ERROR by JUMPing to 1E4AH.

At this point, we have valid cordinates of X <= 128 and a Y <= 47.

0150-0151SETRES
LD D,FFH
Prepare to divide Y coordinate by 3 ... load register D with starting quotient of FFH (which will turn into 00 once it enters the loop).

These next few instructions divide the "Y" coordinate by subtraction. It will keep subtracting 3 (and tracking the number of times it does that in D) until the CARRY FLAG gets set, and then will add 3 to get the remainder.

0152
INC D
Increment the quotient in register D.
0153-0154
SUB 03H
Divide by subtraction ... subtract 3 from register A.
0155-0156
Loop back to 0152H to subtract again until we get to a subtraction where the CARRY FLAG gets set. At that point, register D equals the Y value divided by 3 and we know we can't divide by 3 anymore.
0157-0158
ADD 03H
Add 3 to Register A so then we have the remainder.
0159
LD C,A
Save the remainder (in Register A) to Register C.

These next few instructions fetch the "X" coordinate and do fun math to it together with the "Y" results go get a video memory offset for the graphic.

015A
POP AF
Get the X value from the STACK and put it in register A.
015B
ADD A,A
A = A + A.
015C
LD E,A
Load register E with the X value times two in register A.
015D-015E
LD B,02H
Load register B with the number of times to shift register pair DE (which is 2).
015F
LD A,D
Load register A with the adjusted Y value in register D.
0160
RRA
Divide the adjusted Y value in register A by two.
0161
LD D,A
Save the new Y value in register A in register D.
0162
LD A,E
Load register A with the adjusted X value in register E.
0163
RRA
Divide the adjusted X value in register A by two.
0164
LD E,A
Load register E with the new X value in register A.
0165-0166
Loop until the memory offset in register pair DE has been figured.
0167
LD A,C
Now we need to compute the position of the point so load register A with the value in register C.
0168
ADC A,A
Multiply the value in register A by two and add the value of the CARRY FLAG to register A.
0169
INC A
Increment the value in register A.
016A
LD B,A
Save the bit position in register A in register B.
016B
XOR A
Zero register A and reset the CARRY FLAG.
016C
SCF
Set the CARRY FLAG.
016D
ADC A,A
Multiply the value in register A by two and add the value of the CARRY FLAG to register A.
016E-016F
Loop to the prior instruction until the graphic mask has been completed in register A.
0170
LD C,A
Save the graphic mask in register A in register C.
0171
LD A,D
Load register A with the MSB of the video memory offset in register D.
0172-0173
OR 3CH
Set the bits of the MSB of the video memory offset in register A to turn on Bits 2, 3, 4, and 5 (3H = Decimal: 0011 1100) so that it will point to the correct location in video memory. The only possible resulting values are 3CH-3FH, 7CH-7FH, BCH-BFH, and FCH-FFH.
0174
LD D,A
Save the revised MSB of the video memory pointer in register A in register D.

We now have the location in video memory for the graphic character. Since SET/RESET/POINT use the graphic ASCII character rather than plotting dots, we first need to make sure there is at least a graphic character already at the location.

0175
LD A,(DE)
Load register A with the character at the location of the video memory pointer in register pair DE.
0176
OR A
Set the flags. To help with the next instruction here are a few pointers. The lowest graphic character on the TRS-80 is 80H (Decimal: 128; Binary: 1000 0000). When dealing with bits, a high bit 7 is also utilized to check the sign of something
0177-0179
If the character at (DE) has the sign bit HIGH, it is a graphic character, so skip the next opcode if the character in register A is a graphic character.
017A-017B
LD A,80H
Load register A with a blank graphic character which is CHR$(128).
017C
LD B,A
Save the graphic character (held in Register A) into Register B.
017D
POP AF
Get the graphic character from the STACK (Register B) and put it in register A.
017E
OR A
Set the flags according to the graphic mode in register A.
017F
LD A,B
Get the graphic character from register B and put it in register A.
0180-0181
JUMP forward to 0192H if the graphic mode is POINT.

If we are here, then we are dealing with either SET or RESET, as we would have jumped away in the prior instruction if it was the only other possible alternative, POINT.

0182
LD (DE),A
Save the graphic character in register A at the location of the video memory pointer in register pair DE.
0183-0185
JUMP forward to 018FH if the graphic mode is SET.

If we are here, then we are dealing with RESET, as we would have jumped away otherwise.

0186
LD A,C
Load register A with the graphic mask in register C.
0187
CPL
Reverse the graphic mask in register A.
0188
LD C,A
Load register C with the adjusted graphic mask in register A.
0189
LD A,(DE)
Load register A with the character at the location of the video memory pointer in register pair DE.
018A
AND C
RESET the graphic bit by combining the graphic mask in register C with the graphic character in register A.

Inside the RESET routine. This is common code though. It will just put the graphic character, representing the SET or RESET (held in A) onto the screen (at the location held in DE).

018B
LD (DE),A
Save the adjusted graphic character in register A at the location of the video memory pointer in register pair DE.

Inside the RESET routine. The character has been displayed (if a character was to be displayed), but we still need to make sure that we had proper syntax, so next we check for the close parenthesis and RETURN if we have it or ERROR if we do not.

018C-018D
RST 08 ⇒ ")"
We need to check HL against 29H (ASCII: )) so we call the COMPARE SYMBOL routine which comparess the symbol in the input string pointed to by HL register to the value in the location following the RST 08 call. If there is a match, control is returned to address of the RST 08 instruction 2 with the next symbol in the Aregister and HL incremented by one. If the two characters do not match, a syntax error message is given and control returns to the Input Phase).
018E
RET
Return.

Inside the SET routine. All this does is change the character accordingly and then jump back up into RESET where it displays the character, checks for the ) and RETurns.

018F
OR C
SET the graphic bit by combining the graphic mask in register C with the graphic character in register A.
0190-0191
JUMP back a few bytes to 018BH.

Next is the POINT routine.

0192
AND C
POINT the graphic bit by combining the graphic mask in register C with the graphic character in register A.
0193-0194
ADD FFH
Subtract one from the value in register A.
0195
SBC A,A
Adjust the value in register A so that A will equal zero if the bit was off in register A.
0196
PUSH HL
Save the current BASIC program pointer in register pair HL on the STACK.
0197-0199
Save the value in register A as the current result in REG 1 (i.e., 4121H).
019A
POP HL
Get the current BASIC program pointer from the STACK and put it in register pair HL.
019B-019C
JUMP to 018CH.

019D-01C8 - LEVEL II BASIC - "INKEY" - INKEY$ ROUTINE

019D
RST 10H
Since we need to bump the current BASIC program pointer in register pair HL until it points to the next character, call the EXAMINE NEXT SYMBOL routine at RST 10H.
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.
019E
PUSH HL
Save the current BASIC program pointer in register pair HL on the STACK.
019F-01A1
LD A,(4099H)
Put the last key pressed (stored at 4099H) into Register A.
01A2
OR A
Set the status flags.
01A3-01A4
JUMP to 01ABH (to skip the next character scan) if a key has been pressed.
01A5-01A7
GOSUB to 0358H to scan the keyboard.
01A8
OR A
Set the status flags.
01A9-01AA
JUMP down to 01BCH if a key wasn't pressed.

If we are here, a key was pressed and it has been stored in Register A

01AB
PUSH AF
Save the key pressed in register A on the STACK.
01AC
XOR A
Zero register A and clear the status flags.
01AD-01AF
LD (4099H),A
Save the value in register A to (4099H).
NOTE: 4099H holds the last key pressed.
01B0
INC A
Increment the value in register A (which is going to represet the size of the character string to be built).
01B1-01B3
Make sure there is room for this character string by calling 2857H to make an entry in string space.
01B4
POP AF
Get the last key pressed from the STACK and put it in register A.
01B5-01B7
LD HL,(40D4H)
Load register pair HL with the string's starting address (which is kept in 40D4H).
01B8
LD (HL),A
Save the last key pressed in register A at the location of the string pointer in register pair HL.
01B9-01BB
Go save the string's VARPTR as the current result.
01BC-01BE
LD HL,1928H
Load register pair HL with the starting address of the "READY" message (which is stored at 1928H).
01BF-01C1
LD (4121H),HL
Save the address in register pair HL as the current result in REG 1 (i.e., 4121H).
01C2-01C3
LD A,03H
Load register A with a string number type flag.
01C4-01C6
LD (40AFH),A
Save the value in register A as the current number type.
Note: 40AFH holds the current number's type flag as follows:
  • 2: Integer
  • 3: String
  • 4: Single precision
  • 8: Double precision
01C7
POP HL
Get the current BASIC program pointer from the STACK and put it in register pair HL.
01C8
RET
Return.

01C9-01D2 - "$VDCLS" - Clear the video display screen.

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

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

01C9-01CA$VDCLS
LD A,1CH
Load register A with the ASCII character to home the cursor.
01CB-01CD
GOSUB to 033AH to send the character in register A (i.e., the ASCII character to home the cursor) to the video display.
01CE-01CF
LD A,1FH
Load register A with the ASCII character to clear to the end of the screen.
01D0-01D2
JUMP to 033AH to send the character in register A (i.e., the ASCII character to clear to the end of the screen) to the video display. By JUMPing intead of CALLing, the RET in that routine will act as the RET for this routine.

01D3-01D8 - LEVEL II BASIC RANDOM ROUTINE - "RANDOM"

This is part of the RANDOM routine which takes a value out of the REFRESH register, stores it in location 40ABH and then returns.
A call to 01D3H reseeds the random number seed (location 40AB) with the current contents of the refresh register.

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

01D3
LD A,R
Load register A with the current value of the refresh register.
01D5-01D7
LD (40ABH),A
Save the value of the refresh register, as a pseudi-random value, to memory location 40ABH (the random number seed).
01D8
RET
Return.

*01D9-01F7H- 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.

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

*01D9-01E5H - Subroutine to wait for PRINTER READY, but Honor a BREAK Key. This routine used to be at 0440-044A.

*01D9-01DB
JP 3027H
JUMP to 3027H which is the VECTOR JUMP to 3781H which is the Print Screen Routine that used to be here.
*01DC-01DE
CALL 044BH
GOSUB to 044BH to check to see if PRINTER READY by polling port F8H. Printer will be READY if Z is set.
*01DF
RET Z
If it is ready (because Z is set) then RETURN.
*01E0-01E2
CALL 028DH
if we are here, the printer is not ready. GOSUB to 028DH to check for a BREAK key being pressed.
*01E3-01E4
JR Z,01DCH
Loop back to 01DCH if BREAK wasn't pressed.
*01E5
RET
RETURN.

*01E6H-01EC - Display the Copyright Message. This routine used to be at 37EBH-37F5H.

*01E6-01E8
LD HL,0202H
Load HL with 0202H.
NOTE: 0202H points to the message '(C) "80 Tandy"'.
*01E9
NOP
-
*01EA-01EC
GOSUB to 021BH.
NOTE: 021BH will display the message pointed to by HL.

*01EDH-01F7H - - Enable the TIME$ Command - This routine used to be at 37DCH.

*01ED-01EF
LD HL,3030H
Load HL with 3030H.
NOTE: 3030H is the STRING=DATE$+""+TIME$ routine.
*01F0-01F2
LD (4177H),HL
Load the memory location held at 4177H with HL.
NOTE: 4176H is the TIME$ vector as is currently set to be JP ?L3 ERROR as 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.
*01F3-01F5
JUMP to 022EH to continue.
NOTE: 022EH will enable interrupts, show the READY prompt, and RETURN.
*01F6
NOP
-
*01F7
NOP
-

*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.

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

*01FB-0201 - DELAY ROUTINE - "$DELAY"

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

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

*202 - Message Storage Location

*0202
(C) "80 Tandy" + 0DH
Copyright message storage area.

0210 - These instructions are never called or used.

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

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

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

0216-021A - Display a Carriage Return

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

021B-0227 - "$VDLINE" - Display a Line Until 03H or 0DH Reached.

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

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

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

*0228
EX (SP),HL
Put HL (which presumably has a return address in it) into the memory location of the STACK pointer.
*0229
JUMP to 302AH.
NOTE: 302AH is an entry in a jump vector table that JUMPs to 31F7H. 31F7H checks to see if we have a PRINT #.

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

*022C
JUMP to 0212H to zero the flags and RETURN.
*022E
EI
Enable Interrupts.
*022F
Show READY prompt by jumping to 1A19H.

0232 - These instructions are never called or used.

*0232
CCF
*0233
INC A
*0234
RET
*0232
NOP
-
*0233
NOP
-
*0234
NOP
-

*0235-0240 - CASSETTE ROUTINE - Read a Byte from Cassette - "CSIN"

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

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

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

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

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

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

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

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

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).

*0261
GOSUB to the very next instruction.
*0264
JUMP to 024DH to write a byte to cassette.

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

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

0284 - This subroutine is called by 2076H to turn the tape on, no header - it jumps out to 023DH.

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

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

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

0287 - Write Leader and Sync Byte - "$CSHWR"

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

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

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

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

0293 - CASSETTE ROUTINE - Read the Header and Sync Bytes

*0293
JUMP to 0243H to read the cassette header.

0296 - CASSETTE ROUTINE - "CSHIN" - Search for Cassette Header and Sync Byte

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

*0296
JUMP to 0243H to read the cassette header.

0298 - Enable the Clock Display - "CLKON"
No entry conditions. A is altered by this routine.

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

02A1 - Disable the Clock Display - "CLKOFF"
No entry conditions. A is altered by this routine.

*02A1CLKOFF
LD A,(4210H)
Put the clock bit stored in memory location 4210H into A.
NOTE: 4210H holds the bit mask for port ECH. Port ECH stores miscellaneous controls.
*02A4
RES 0,A
Reset BIT 0 of A.
*02A6
JUMP to 029DH.

02A8 - These instructions are never called or used.

*02A8
RET
*02A8
NOP

02A9-02B1 - LEVEL II SYSTEM ROUTINE-ENTRY POINT- "GSYSTR"

02A9-02AB
Go read 2 bytes from the cassette, which should be the execution address, and return with it in register pair HL.
02AC-02AE
LD (40DFH),HL
Save the just read execution address from HL into 40DFH.
NOTE: 40DFH-40E0H is used by DOS.
02AF-02B1
GOSUB to 01F8H to turn off the cassette motor.
02B2-02B4SYSTEM
CALL 41E2H
Go call the DOS link at 41E2H.
In NEWDOS 2.1, this is called during a SYSTEM operation.
02B5-02B7
LD SP,4288H
Set the STACK pointer to 4288H (which is the assumed load address). This location passes control to the routine used by the BASIC command SYSTEM.
02B5-02B7
LD SP,42E8H
Set the STACK pointer to 42E8H (which is the assumed load address). By Frank moving this from 4228H to 42E8H he has avoided stepping on a lot of DOS memory locations.
02B8-02BA
Go display a carriage return on the video display if necessary.
02BB-02BC
LD A,2AH
Load register A with an * character (which will form the next prompt).
02BD-02BF
Go display the * character in register A on the video display.
02C0-02C2
We need a filename now with a "?: prompt so GOSUB to 1BB3H to get the input from the keyboard.
02C3-02C5
If a BREAK key was hit (because the CARRY FLAG is now on from the 1BB3H call), go to 06CCH which is in the middle of a token routine looking for REM. This is a BUG. The jump this way will cause the system to reboot.
02C3-02C5
If a BREAK key was hit (because the CARRY FLAG is now on), go to the Level II BASIC READY routine at 006D. The original code jumped to an address which was inappropriate and would cause a reboot.
02C6
RST 10H
Since we need to bump the input buffer pointer in register pair HL until it points to the first character input, call the EXAMINE NEXT SYMBOL routine at RST 10H.
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.
02C7-02C9
JUMP to 1997H to display a ?SN ERROR if there wasn't any input.
02CA-02CB
CP 2FH
Check to see if the character at the location of the input buffer pointer in register A is a / character. "/" is 2FH.
02CC-02CD
JUMP to 031DH if the character at the location of the input buffer pointer in register A is a /.
02CE-02D0
Go turn on the cassette motor.
02D1-02D3
Calls the READ ONE BYTE FROM CASSETTE routine at 0235H (whichh reads one byte from the cassette drive specified in register A, and returns the byte in register A).
02D4-02D5
CP 55H
Check to see if the byte read from the cassette in register A is a header byte.
02D6-02D7
Loop until the header byte is found.
02D8-02D9
LD B,06H
Load register B with the length of the filename to read from the cassette (which is 6 characters).

Top of a DJNZ loop of 06 characters.

02DA
LD A,(HL)
Load register A with the character at the location of the current input buffer pointer in register pair HL.
02DB
OR A
Check to see if the character at the location of the current input buffer pointer in register A is an end of input character.
02DC-02DD
JUMP out of this 'read the filename from the cassette' routine if the character at the location of the current input buffer pointer in register A is an end of input character.
02DE-02E0
Calls the READ ONE BYTE FROM CASSETTE routine at 0235H (which reads one byte from the cassette drive specified in register A, and returns the byte in register A).
02E1
CP (HL)
Check to see if the character at the location of the current input buffer pointer in register pair HL is the same as the character read from the cassette in register A.
*02E2
INC HL
Increment the input buffer pointer in register pair HL.
Difference between M1 and M3: This is part of the SYSTEM tape load routine. In the Model I, these locations contain a JR NZ,02D1H instruction followed by an INC HL instruction. In the Model III, these instructions have been in effect reversed C INC HL followed by JR NZ,02D1H). Since this area is not referenced by any other part of the ROM, it is POSSIBLE that this change may have been implemented in order to correct some problem that occured while reading the filename on a SYSTEM tape.
*02E3-02E4
JUMP to 02D1H (skip to the next program on cassette) if the character at the location of the current input buffer pointer in register pair HL isn't the same as the character read from the cassette in register A.
02E5-02E6
Loop until the whole of the filename has been read from the cassette and checked against the user response.

End of the DJNZ loop of 06 characters.

02E7-02E9
GOSUB to 022CH which just zeroes A and all flags and jumps back here.
02EA-02EC
GOSUB to READ ONE BYTE FROM CASSETTE routine at 0235H (whichh reads one byte from the cassette drive specified in register A, and returns the byte in register A).
02ED-02EE
CP 78H
Check to see if the byte read from the cassette in register A is an execution address header byte (which is 78H).
02EF-02F0
JUMP if the byte read from the cassette in register A is an execution address header byte.
02F1-02F2
CP 3CH
Check to see if the byte read from the cassette in register A is a file block header byte (which is 3CH).
02F3-02F4
Loop until either an execution address header byte or a file block header byte is read from the cassette.
02F5-02F7
Calls the READ ONE BYTE FROM CASSETTE routine at 0235H (which reads one byte from the cassette drive specified in register A, and returns the byte in register A).
02F8
LD B,A
In preparation for the next DJZ loop, load register B with the number of bytes to be loaded in register A.
02F9-02FB
Go read the file block's starting address from the cassette and return with it in register pair HL.
02FC
ADD A,L
Add the LSB of the file block's starting address in register L to the MSB of the file block's starting address in register A.
02FD
LD C,A
Load register C with the file block's starting checksum in register A.
02FE-0300
Calls the READ ONE BYTE FROM CASSETTE routine at 0235H (which reads one byte from the cassette drive specified in register A, and returns the byte in register A).
0301
LD (HL),A
Save the byte read from the cassette in register A at the location of the memory pointer in register pair HL.
0302
INC HL
Increment the memory pointer in register pair HL.
0303
ADD A,C
Add the value of the current checksum in register C to the value in register A.
0304
LD C,A
Load register C with the updated checksum in register A.
0305-0306
Loop until the whole file block has been read.
0307-0309
Calls the READ ONE BYTE FROM CASSETTE routine at 0235H (which reads one byte from the cassette drive specified in register A, and returns the byte in register A).
030A
CP C
Check to see if the computed checksum in register C is the same as the checksum read from the cassette in register A.
030B-030C
If its the same, jump to 02E7H because the next instructions are for bad checksums.
030D-030E
LD A,43H
Load register A with a C character.
030F-0311
LD (3C3EH),A
Display a C on the video display (at 15422).
0312-0313
JUMP to 02EAH and keep reading bytes.

0314-0316 - This subroutine reads two bytes from tape (providing that the tape is already running) and puts them in the HL register pair. It is used by the SYSTEM routine to read the last two bytes on tape which give the entry point. A JP (HL) can then be executed to jump to the location specified, when used for this purpose. Only HL is used by this routine.

0314GETADR
GOSUB to the READ ONE BYTE FROM CASSETTE routine at 0235H (whichh reads one byte from the cassette drive specified in register A, and returns the byte in register A).
0317
LD L,A
Load register L with the byte read from the cassette in register A (which is the LSB of the 16 bit value).
0318-031A
GOSUB to the READ ONE BYTE FROM CASSETTE routine at 0235H (whichh reads one byte from the cassette drive specified in register A, and returns the byte in register A).
031B
LD H,A
Load register H with the byte read from the cassette in register A (which is the MSB of the 16 bit value).
031C
RET
Return.

031D - "SYSGO" - This subroutine checks to see if we have a starting address from the "*?" prompt and if not, default to 40DFH, and then JUMP to whatever that address is.

031D
EX DE,HL
Load register pair DE with the input buffer pointer in register pair HL.
031E-0320
LD HL,(40DFH)
Load register pair HL with the current execution address (which is stored at 40DFH).
NOTE: 40DFH-40E0H is used by DOS.
0321
EX DE,HL
So that we can run a RST 10H in the next instruction, we need to exchange the execution address in register pair HL with the input buffer pointer in register pair DE.
0322
RST 10H
Since we need to bump the current input buffer pointer in register pair HL until it points to the next character, call the EXAMINE NEXT SYMBOL routine at RST 10H.
NOTE:
  • The RST 10H routine loads the next character from the string pointed to by the HL register into the A-register and clears the CARRY FLAG if it is alphabetic, or sets it if is alphanumeric.
  • Blanks and control codes 09H and 0BH are ignored causing the following character to be loaded and tested.
  • The HL register will be incremented before loading any character therfore on the first call the HL register should contain the string address minus one.
  • The string must be terminated by a byte of zeros.
0323-0325
Call the ASCII TO INTEGER routine at 1E5AH.
NOTE: The routine at 1E5A converts the ASCII string pointed to by HL to an integer deposited into DE. If the routine finds a non-numerica character, the conversion is stopped.
0326-0327
JUMP if it turns out there weren't any digits in the input.
0328
EX DE,HL
Since there were digits (or else we would have jumped in the prior instruction), exchange the input buffer pointer in register pair HL with the execution address in register pair DE.
0329
JP (HL)
JUMP to the execution address (i.e. "/xxxx") which is in register pair HL.

032A-0347 - OUTPUT ROUTINE - "OUTCHR" or "DSPCHR"

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
  • 1: Printer

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

032A
PUSH BC
We are going to need to use Register C, so push register pair BC into the STACK.
032B
LD C,A
Load register C with the character to be output in register A.
032C-032E
CALL 41C1H
Go call the DOS link at 41ClH.
In NEWDOS 2.1, this writes to the system output device.
032F-0331
LD A,(409CH)
Load register A with the current output device number stored in 409CH.
NOTE: 409CH holds the current output device flag:
  • -1: Cassette
  • 0: Video
  • 1: Printer
0332
OR A
Set the flags.
0333
LD A,C
A = C (the character to be output).
0334
POP BC
Get the value from the STACK and put it in register pair BC.
0335-0337
If the character in register A is to be sent to the cassette (because A was NEGATIVE) then JUMP to the the WRITE ONE BYTE TO CASSETTE routine at 0264H (which writes the byte in the A register to the cassette drive selected in the A register).
0338-0339
If the character in register A is to be sent to the printer (because A was NOT ZERO) then JUMP to 039CH.

033A-0347 - "CRTOUT" OUTPUT ROUTINE

  • To use a ROM call to print a single character at the current cursor position, and to update the cursor position, load the ASCII value of the character into the A register and then CALL 033AH.
  • To display special functions using a ROM call, load the A register with the value given below for the special function and then CALL 033AH.
    1. Backspace and erase previous character - 08H
    2. Carriage return and linefeed - 0DH
    3. Turn on cursor - 0EH
    4. Turn off cursor - 0FH
    5. Convert to 32 characters per line mode - 17H
    6. Backspace cursor - 18H
    7. Advance cursor one position - 19H
    8. Downward line feed - 1AH
    9. Upward line feed - 1BH
    10. Home (cursor to upper left corner) - 1CH
    11. Move cursor to beginning of current line - 1DH
    12. Erase from cursor position to end of line - 1EH
    13. Erase from cursor position to end of screen - 1FH

033A
PUSH DE
If we're here, then that value in A wasn't going to the cassette or the printer, so it must be going to the video. This routine performs the same function as 33H except that it doesn't destroy the contents of the DE register pair. This means that all the general purpose registers are saved, which is often desirable.

Save the value in register pair DE on the STACK.
033B-033D
Call the DISPLAY A CHARACTER routine at 0033H (which puts the character in register A on the video screen).
033E
PUSH AF
Save the character in register A on the STACK.
033F-0341
Go update the current cursor position and test to see if the display memory is full.
0342-0344
LD (40A6H),A
Save the current cursor line position stored in 40A6H to register A.
NOTE: 40A6H holds the current cursor line position.
0345
POP AF
Get the character from the STACK and put it in register A.
0346
POP DE
Get the value from the STACK and put it in register pair DE.
0347
RET
Return.

0348-0357 - VIDEO ROUTINE - Get the cursor location (taking into account whether we are in 32 or 64 character mode)

This is a bug in the Model III code. The code is a holdover from the Model I to test whether or not the video display is currently in the 32-character mode. The next 2 instructions (LD A,403DH and AND 08H) was supposed to test bit 3 of memory location 403DH to make that determination, but 403DH does not hold the 32 character flag on the Model III! To fix this bug, the next 2 instructions should be LD A,(4210H) and AND 04H. This bug results in the improper operation of the TAB function when the video display is in the 32 character mode.

0348-034A
LD A,(403DH)
Load register A with the 32/64 character per line flag (which is stored in 403DH).
034B-034C
AND 08H
MASK register A with a 08H (Binary: 0000 1000) to leave only BIT 3 live enabling a test for the 32/64 character per line flag in register A, and set the flags (so if Bit 3 was a 0 then Z FLAG is set).
034D-034F
LD A,(4020H)
Load register A with the LSB of the current cursor position.
NOTE: 4020H-4021H holds Video DCB - Cursor location.
0350-0351
JUMP to 0355H if this is the 64 character per line mode.
0352
RRCA
Divide the LSB of the current cursor position in register A by two.
0353-0354
AND 1FH
Mask the cursor line position in register A for a 32 character per line (AND against 0001 1111) to force its position to be no less than 3C00H.
0355-0356
AND 3FH
Mask the cursor line position in register A for 64 characters per line (AND against 0011 1111) to force its position to be no more than 3FFFH.
0357
RET
Return.

0358-0360 - KEYBOARD ROUTINE - "KBD2"
Here is the routine to simulate the INKEY$ function. It performs exactly the same function as 2BH but it restores all registers, whereas 2BH destroys the contents of the DE register pair. This makes 35BH more useful than 2BH.

0358-035A
CALL 41C4H
GOSUB to the DOS link at 41C4H.
035B
PUSH DE
Since the next routine uses DE, save the value in register pair DE on the STACK.
035C-035E
GOSUB to the SCAN KEYBOARD routine at 002BH.
035F
POP DE
Restore Register Pair DE from the stack.
0360
RET
Return.

0361-0383 - INPUT ROUTINE - "LINP2"

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

0361
XOR A
Zero register A.
0362
LD (4099H),A
Save the value in register A as the last key pressed (which is stored in 4099H).
NOTE: 4099H holds the last key pressed.
0365
LD (40A6H),A
Save the value in register A as the current cursor line position (which is stored in 40A6H).
NOTE: 40A6H holds the current cursor line position.
0368
CALL 41AFH
GOSUB the DOS link at 41AFH.
In NEWDOS 2.1, this is the start of keyboard input.
036B
PUSH BC
Save the value in Register Pair BC to the STACK.
036C
LD HL,(40A7H)
Load register pair HL with the starting address of the input buffer (which is stored in 40A7H).
NOTE: 40A7H-40A8H holds the input Buffer pointer.
036F
LD B,F0H
Load register B with the length of the input buffer (which is 240).
0371
GOSUB to the "WAIT FOR NEXT LINE" keyboard input routine at 05D9H (which takes keyboard entry until a carriage return, a break, or buffer overrun occurs).
0374
PUSH AF
Save the flags on the STACK.
0375
LD C,B
Load register C with the length of the input in register B.
0376
LD B,00H
Zero register B so that register pair BC will have the length of the input.
0378
ADD HL,BC
Add the length of the input in register pair BC to the starting address of the input buffer in register pair HL.
0379
LD (HL),00H
Save an end of the input character at the location of the end of input pointer in register pair HL.
037B
LD HL,(40A7H)
Load register pair HL with the starting address of the input buffer (which is 40A7H).
NOTE: 40A7H-40A8H holds the input Buffer pointer.
037E
POP AF
Get the flags from the STACK.
037F
POP BC
Get the value from the STACK and put it in register pair BC.
0380
DEC HL
Decrement the input buffer pointer in register pair HL (so that HL is the input area pointer - 1).
0381
RET C
Return if the BREAK key was pressed.
0382
XOR A
Otherwise (i.e., the BREAK key was not pressed), zero all the status flags.
0383
RET
Return.

0384-038A - KEYBOARD ROUTINE - "KBWT2" - Waits for keypress

0384
Go scan the keyboard.
0387
OR A
Check to see if a key was pressed.
0388
RET NZ
Return if a key was pressed (meaning OR A was set to NZ).
0389
Loop until a key is pressed.

038B-039B - PRINTER ROUTINE

This routine resets device type flag at 409CH to zero (output to video display), also outputs a carriage return to the line printer if printer is not at beginning of line (determined by checking the contents of the printer line position flag at 409B - if flag contains zero, printer is at start of line). Note that if printer line position flag does not contain zero and the printer is not on line, the computer will "hang up" waiting for a "printer ready" signal.

038B
XOR A
Zero register A, which then means it contains the device code for VIDEO.
038C-038E
LD (409CH),A
Save the value in register A (the current output device code of video) to 409CH.
NOTE: 409CH holds the current output device flag:
  • -1: Cassette
  • 0: Video
  • 1: Printer
038F-0391
LD A,(409BH)
Load register A with the current printer carriage position (which is stored at 409BH).
NOTE: 409BH holds the printer carriage position.
0392
OR A
Check to see if the carriage position in register A is equal to zero.
0393
RET Z
Return if the carriage position in register A is equal to zero.
0394-0395
LD A,0DH
Load register A with a carriage return character (i.e., 0DH).
0396
PUSH DE
Save the value in register pair DE on the STACK.
0397-0399
Go send the carriage return character in register A to the printer.
039A
POP DE
Get the value from the STACK and put it in register pair DE.
039B
RET
Return.

039C-03C1 - PRINTER ROUTINE

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

039C
PUSH AF
Save the value in register pair AF on the STACK.
039D
PUSH DE
Save the value in register pair DE on the STACK.
039E
PUSH BC
Save the value in register pair BC on the STACK.
039F
LD C,A
Load register C with the character to be sent to the printer in register A.
03A0
LD E,00H
Zero register E (which will ultimately hold the new character/line count of 0CH, 0DH, or 0AH).
03A2
CP 0CH
Check to see if the character to be sent to the printer in register A is equal to 0CH (which is 'skip to next line').
03A4
JUMP to 03B6H if the character to be sent to the printer in register A is equal to 0CH.
03A6
CP 0AH
Check to see if the character to be sent to the printer in register A is a line feed character (i.e., 0AH).
03A8
JUMP to 03ADH if the character to be sent to the printer in register A isn't a line feed character.
03AA
LD A,0DH
Load register A with a carriage return character (i.e., 0DH).
03AC
LD C,A
Load register C with the character to be sent to printer in register A.
03AD
CP 0DH
Check to see if the character to be sent to the printer in register A is a carriage return character.
03AF
JUMP to 03B6H if the character to be sent to the printer in register A is a carriage return character.
03B1
LD A,(409BH)
Load register A with the current printer carriage position (stored in 409BH).
NOTE: 409BH holds the printer carriage position.
03B4
INC A
Increment the current carriage position in register A.
03B5
LD E,A
Load register E with the current carriage position in register A.

03B6H - If we are here, the character sto be sent to the printer was a SKIP TO NEXT LINE (0CH) or a CARRIAGE RETURN (0DH).

03B6
LD A,E
Load register A with the current carriage position in register E. Why do this since its obviously already done? Because this is a jump point!
03B7
LD (409BH),A
Put Register A (the current carriage position) into 409BH.
NOTE: 409BH holds the printer carriage position.
03BA
LD A,C
Load register A with the character to be sent to the printer in register C.
03BB
Call the PRINT CHARACTER routine at 003B (which sends the character in the C register to the printer).
03BE
POP BC
Get the value from the STACK and put it in register pair BC.
03BF
POP DE
Get the value from the STACK and put it in register pair DE.
03C0
POP AF
Get the value from the STACK and put it in register pair AF.
03C1
RET
Return.

*03C2H-0451H - PRINTER ROUTINE

Difference between M1 and M3: Start of I/O driver area which has been totally rearranged in the Model III. Specifically, in the Model I the area from 03C2H through 0SD0H was arranged as follows: 03C2H - 03E2H is the I/O driver entry routine, 03E3H - 0457H is the keyboard driver routine, 0458H - 058CH is the video driver routine, and 058DH - 0SD0H is the line printer driver routine. In the Model III, 03C2H - 0451H is the line printer driver routine, 0452H - 0468H is the actual location of the routine to initialize all I/O drivers, 046BH - 0472H is a routine used by the RUN/EDIT/NEW commands to unprotect the video display and to load HL with the start of BASIC program pointer at 40A4H-40A5H, and 0473H-05D0H is the video driver routine and the keyboard driver begins at 3024H in the Model III).

*03C2
LD A,C
A = C (the current character).
*03C3
CP 20H
Check to see if the character is a control character by testing A - 20H. Results:
  • If A=20H it sets the ZERO FLAG
  • If A<20H then the CARRY FLAG will be set
  • if A>=20H then the NO CARRY FLAG will be set.
If A is a CONTROL CHARACTER then C will be set.
*03C5
If A was >= a SPACE, the NC would be set meaning A is a control character, jump to 03E5H to skip a bunch of needless tests.
*03C7
CP 0DH
Check to see if the character is a carriage return.
*03C9
If it is a carriage return, jump to 03F5H.
NOTE: 03F5H prints a character while maintaining page height and width.
*03CB
CP 0CH
Check to see if the character is a FORM FEED.
*03CD
JUMP to 03FFH if it is not a FORM FEED.
*03CF
LD A,(IX+03H)
If we are here then the character must be a printable one, so we need to get the number of lines left in the page and put them into B for a DJNZ countdown.
*03D2
SUB (IX+04H)
Subtract the number of lines printed from A.
NOTE: IX+4 is the number of lines printed.
*03D5
LD B,A
LET B = A
*03D6
GOSUB to 0440H to wait until the printer is ready (honoring BREAK, if hit).
*03D9
LD A,0AH
Put a LINE FEED character into A.
*03DB
OUT (0F8H),A
Output the LINE FEED character to port 0F8H.
NOTE: 0F8H is the printer port. If you put data to it, it prints it. Otherwise, Bits 4-7 hold printer status.
*03DD
JUMP back to 03D6H until the number of lines left in the page is zero.
*03DF
LD (IX+05H),00H
Load the memory location pointed to by IX+5 with a zero. NOTE: IX+5 is the number of characters printed.
*03E3
JUMP to 0439H to set the number of lines printed to 01 and exit

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

*03E5
CP 80H
Test for a graphics character by comparing A to 80H. If it is a graphics character than NC will be set.
*03E7
JUMP to 0419H to handle the graphics character.
*03E9
LD B,00H
Load B with a 00 (to set a MSB = 0).
*03EB
SUB 20H
Subtract 20H from A to adjust the character to the table.
*03ED
LD C,A
Load C with A. Now BC has the adjusted character value.
*03EE
LD HL,3145H
Load HL with 3145H.
NOTE: 3145H is the PRINTER CHARACTER LOOKUP TABLE.
*03F1
ADD HL,BC
Add BC to HL so that HL will have the character location in the character table.
*03F2
LD C,(HL)
Load C with the character at the position of HL in the character table.
*03F3
JUMP to 0403H to continue.

*03F5-0424 - Inside the PRINTER ROUTINE - Print A Character Honoring Page Height and Width

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

*0403 - Inside the PRINTER ROUTINE - If we are here, then C holds the printable character to be printed as determined by the PRINTER CHARACTER TABLE.

*0403
LD A,(IX+06H)
Load A with the MAXIMUM PRINT WIDTH.
NOTE: IX+06H holds the MAXIMUM PRINT WIDTH.
*0406
INC A
Bump A to one beyond that (i.e., unlimited).
*0407
If the maximum print width is unlimited, jump to 0419H.
*0409
CP (IX+05H)
Check to see if the line is full by comparing A with IX+5.
NOTE: IX+5 is the number of characters printed.
*040C
If the line is NOT full, jump to 0419H.
*040E
GOSUB to 0440H to wait until the printer is ready (honoring BREAK, if hit).
*0411
LD A,0DH
Load A with a carriage return.
*0413
OUT (0F8H),A
Send the carriage return to port 0F8H.
NOTE: 0F8H is the printer port. If you put data to it, it prints it. Otherwise, Bits 4-7 hold printer status.
*0415
LD (IX+05H),00H
Set the number of characters printed to zero.
NOTE: IX+5 is the number of characters printed.
*0419
GOSUB to 0440H to wait until the printer is ready (honoring BREAK, if hit).
*041C
LD A,C
Restore the character held in C back into A.
*041D
OUT (F8H),A
Send the character to port F8H.
NOTE: F8H is the printer port. If you put data to it, it prints it. Otherwise, Bits 4-7 hold printer status.
*041F
INC (IX+05H)
Bump the number of characters printed.
NOTE: IX+5 is the number of characters printed.
*0422
CP 0DH
Check A for a carriage return.
*0424
If A was a carriage return, skip the next few instructions and jump to 042AH.
*0426
CP 0AH
Check to see if the character in register A is 0AH (ASCII: LINE FEED character).
*0428
IF A is not a LINE FEED then jump to 043DH.

042A - Inside the PRINTER ROUTINE - If we are here, then we have a LINE FEED or a CARRIAGE RETURN in A.

*042A
LD (IX+05H),00H
Reset the number of characters printed.
NOTE: IX+5 is the number of characters printed.
*042E
INC (IX+04H)
Bump IX+04.
NOTE: IX+4 is the number of lines printed.
*0431
LD A,(IX+04H)
Load A with the number of lines printed.
*0434
CP (IX+03H)
Compare that to the maximum number of lines per page.
NOTE: IX+3 is the maximum number of lines per page.
*0437
Skip the next instruction by JUMPing to 043DH if the number of lines printed is less than maximum number of lines per page.
*0439
LD (IX+04H),01H
We must be at top of page so set the number of lines printed to 01.
NOTE: IX+4 is the number of lines printed.
*043D
XOR A
Clear A and the status bits.
*043E
LD A,C
Load the character into A.
*043F
RET
RETURN.