Model 4 ROM Explained – XDROM

This page covers the 3000H and higher portion of the Model 4 ROM which Frank Durda developed as the “XDROM” and “XROM”.

The ROM was designed to improve on certain aspects of the Model III-mode features. In physical form, it would replace either the “C” or “D” ROM.

The main features of the XDROM are:

  • When running in Model III mode, the ROM driver allows the keyboard to produce all 128 ASCII character codes. The keyboard repeat rate and other parameters can also be adjusted. Key-rollover and “debouncing” are correctly handled at both 2 and 4 MHz.
  • The keyboard repeat rate is adjustable.
  • When running in Model III mode at 4MHZ, the real-time clock continues to keep correct time.
  • The cursor blinks at the same rate regardless of the current CPU speed.
  • Allows the use of a fully programmable CRT Controller (6845) in place of the preprogrammed version (68045) that came installed with the Model 4.
  • All calls into the “C” ROM that are documented by LDOS and Radio Shack produce the same results as in the original part.
  • The interrupt handler will process more than one event per hardware interrupt. This change makes high speed RS-232 communications far more reliable by reducing missed interrupts.
  • The XDROM includes built-in tests for memory, video, floppy drive, and printer (available via a factory test option jumper.)
  • Ability to boot LS-DOS 6.3 or TRSDOS 6.2 directly from a hard disk drive without using a startup diskette. (You must use the HBUILD6 utility to install a new boot track on your LS-DOS 6.3 or TRSDOS 6.2 hard disk).
  • With Network III or Network 4 hardware installed, either can be booted by pressing a single key

Limits:

  • Only Model 4 or 4D gate-array systems can use this ROM. It is not compatible with NGA systems.
  • Only works with the latest “A” ROM (Radio Shack part number 8048364A). If you enter PRINT CHR$(PEEK(1490)) in ROM basic and get a “O”, you have the OLD ROM.
  • SuperScripsit is unhappy with this ROM and requires patches.

Frank had been working on a ROM which is not entirely this XDROM. He versioned it 1(20) 10-Nov-85. He said that the features there were as follows. These MAY be in this version:

  • Network 3 and Network 4 boots have been included in the USA assembly. Previously, you could have one or the other.
  • Network 3 can be started by pressing “3” and Network 4 will try to start if you press “4”. Previously Network 3 (if present) had to be entered via BASIC SYSTEM command.
  • Old American keyboard driver has been scrapped. Brand-new keyboard driver was written from scratch that generates all 128 ASCII characters. Model III Emulation modes are switchable and SHIFT+DOWN ARROW normally acts like the CTRL key, but this can be disabled. Keyboard has n-key rollover, limited to “squaring” in the diodeless keyboard. Repeat function continues until a new down-stroke or repeated key is released. FN keys are now repeatable also.
  • Keyboard repeat rates are now adjustable.
  • Boot Code will boot off floppy, one of the networks (if selected), enter ROM BASIC or boot off the hard disk drive. (The hard disk must contain magic to boot. If no magic the hard disk will not be considered a bootable device.)
  • Model III mode screen/system clock runs at the correct speed regardless of CPU speed. Previously, it ran twice as fast at 4mhz mode.
  • Cursor blink also remains the same regardless of CPU speed.
  • If a REAL 6845 video processor is installed, it will be properly programmed for 64×16.
  • All of video RAM is cleared on RESET. This eliminates the glitch-flash that is normally seen when booting Model 4 mode.
  • In Image form, the 4P boot ROM [BREAK] handshake is passed to the “CROM” section instead of being ignored.
  • RS232 board is now initialized to prevent it from hanging-up host computers.
  • Floppy drive deselects after 3-5 seconds if no boot disk is found instead of running forever like the old version. Pressing any key restarts boot sequence.
  • Hard disk code will handle the WD1000 and all versions of the WD1010-xx controller.
  • Corvus (Network 4) ROM-loader has magic test for generic network and will select it over Network 4 if present.

Instructions:

Booting

The “X” and “XD” ROM can be started in five ways. If there is a floppy disk in drive 0 and no keys are pressed, the system will boot from the floppy disk drive when RESET is pressed or the power switch is turned on.

If there is no floppy disk available, the system will examine the keyboard for about three seconds. During that time, the following keys will perform these booting operations:

  • If the BREAK key is pressed, the system will enter ROM BASIC as in the old ROM.
  • If the 3 key is pressed, the system will attempt to boot from the Network 3 (RS-232).
  • If the 4 key is pressed, the system will boot from the Network 4 if the hardware is present.
  • If no key is pressed within three seconds, the ROM will check for a hard disk drive. If a drive is present, is ready, and has the correct boot track, the system will boot directly from the hard disk drive. If there is no hard disk drive, or it is not ready or does not have the correct boot track, the system will display Diskette? and will wait for a key to be pressed. When a key is pressed, the ROM will repeat all five checks. When booting off a hard disk drive, there may be a floppy in drive 0, but the door must be open.

Keyboard:

The “X”/”XD” ROM keyboard driver utilizes the CTRL key to allow the full 128 ASCII characters to be generated by the keyboard. Some codes are intercepted when using BASIC, but are available in most other programs.

The following table shows the keystrokes required to get the new characters. When possible the same keys that are used under LS-DOS/TRSDOS 6 were used.

CONTROL+, produces a [

CONTROL+. produces a ]

CONTROL+/ produces a \

CONTROL+; produces a ^

CONTROL+- produces a _

CONTROL+1 produces ASCII Code 27 (Move Cursor Up)

CONTROL+2 produces ASCII Code 28 (Move Cursor to Upper Left Corner)

CONTROL+3 produces ASCII Code 29 (Move Cursor to the Beginning of the Line)

CONTROL+4 produces ASCII Code 30

CONTROL+5 produces ASCII Code 127

CONTROL+6 produces a ~

CONTROL+7 produces a '

CONTROL+8 produces a {

CONTROL+9 produces a }

CONTROL+0 produces a |

CONTROL+@ produces ASCII CODE 0

CONTROL+: causes a screen print to be performed if there is a printer

In addition to the new keystrokes, the keyboard driver allows you to adjust how long the system will wait before starting to repeat a character. Another adjustment changes how quickly the repeated characters are generated. Other controls change the way the keyboard behaves when certain keys are pressed. All of these items are changed by modifying memory locations. This can be done from BASIC using PEEK and POKE, with DEBUG or using the MEMORY command (LDOS only).

Address 4200H/16896
This byte controls the delay between pressing a key and the generation of the first repeat of that key. The higher the value, the longer the delay. (Initial value is 14H/20.)
Address 41FFH/16895
This byte controls the delay between each repeated character. The higher the value, the longer the delay. (Initial value is 02H/2.)
Address 4019H/16409
This byte controls operation of the keyboard, and two bits may be changed to force the keyboard into specific states of operation. All other bits are reserved for use by the ROM and should not be altered.

Bit 0 controls the CAPS LOCK toggle. If the bit is on (1), all alphabetic keys will produce uppercase characters. (The default for this bit is on.) In BASIC, use POKE 16409,PEEK(16409) OR 1 to turn Bit 0 on and use POKE 16409,PEEK(16409) AND 254 to turn Bit 0 off.

Bit 6 controls the SHIFT+? emulation of the CTRL key. If this bit is on (1), the SHIFT+? key will produce the ASCII code 10. If the bit is off (0), no code will be produced, and any other key that is pressed will be produce the code that would be generated if the CTRL key was being pressed. (The default for this bit is off (0).)

Bit 4 controls the BREAK key. If this bit is off (0), pressing the BREAK key will cause control to divert to 0028H. The call to the keyboard driver will not return to the caller unless a RET instruction has been inserted at 0028H. (Some operating systems do this.) If the bit is on (1), pressing BREAK will cause the keyboard driver to return the ASCII code 1. (The default for this bit is off (0).)



3000 – Jump Table.

3000
Slow Tape Header Write Vector.
3003
Fast Tape Header Write Vector.
3006
Slow Tape Header Read Vector.
3009
Fast Tape Header Read Vector.
300C
Cassette Motor Off Vector.
300F
Cassette Motor On Vector.
3012
Warm Boot Vector.
3015
Bootstrap Vector.
3018
“POLLZ”
Maskable Interrupt Handler Vector.
301B
RS-232 Initialization Vector.
301E
RS-232 Input Vector.
3021
RS-232 Output Vector
3024
“KEYBDZ”
Keyboard Input Vector
3027
I/O Re-Router Vector
302A
Part of the Cassette Header Routine.
302D
routine which parses whether the current instruction on a the current line is in quotes.
3030
STRING=DATE$+””+TIME$.
3033
put the DATE onto the upper right hand corner of the screen.
3036
put the TIME 10 characters from the upper right hand corner of the screen.
3039
Jump to NMI Vector at 4049H (which in TRSDOS is just a JUMP to 42BDH which is just a RETurn). This is what happens when a NMI is called while NOT doing disk I/O. If disk I/O was active, then the DOS itself would have an alternate NMI routine in the read/write routine which would involve loading the NMI vector to a different routine. In TRSDOS, that would a call to 459CH to load (404AH) and (404BH) with 45ADH and loading (4049H) with a status.

303C-306F – Embedding of (c) Text

303C
N/A
“FDIV (c)85,86,90 M.A.D. W 3(33) 15-May-90”. This code is never executed.

3070 – Small routine to display the message held at (HL) and then to accept a keypress (stored in Register A)

3070
GOSUB to 021BH. Note; 021BH will display the character at (HL) until a 03H is found.
3073
JUMP to 0049H in the ROM

NOTE: 0049H is the $KBWAIT routine which scans the keyboard and returns with the key pressed, if any, in register A.

3076H – Part of the Network 4 Controller Boot.

Jumping here as an entry is used when the FDC cannot be found. It checks to see if it is a student station (FDCless) system for education If it isn’t, it simply falls into the cassette BASIC code, which is about all they can do with the machine. This makes the assumption that if you don’t have a FDC you won’t have a hard disk you will boot a “normal” operating system off of.

3076
“NETCAS”
GOSUB to 34FEH to test for a Network 3 or Network 4.

This entry is used instead if Break was pressed or we never get a track 0 indication from the FDC.

3079
“CASMSG”
LD HL,0075H
LET HL = 0075H. 30E4 jumps here, but it can also be a pass through.
307C
PUSH HL
Save the contents of Register Pair HL to the STACK.

Entering HERE is the CASS prompt routine, which is also a double fall through from entry at 3076 and 307C as a RAM saving measure.

307D
“CPRMPT”
LD HL,05D1H
HL = 05D1H (which is the location of the “CASS?” prompt in ROM).
3080
GOSUB to 3070H to display the prompt and accept a keypress.
3083
GOSUB to 0033H.

NOTE: 0033H is the character print routine, to put the character held in Register A at the current cursor position.
3086
SUB 4CH
SUBtract 4CH (ASCII: L) from Register A, so that if the L was pressed, the Z flag will be set.
3088
LD (4211H),A
Set the Model III Cassette Baud by putting A (which is either 0 if L was hit, something else if anything else was hit) into (4211H).

NOTE: 4211H is the RAM location which stores the Cassette Baud Rate Select (0 = 500 baud, Not 0 = 1500 baud).
308B
If the subtraction set Register A at zero (meaning LOW speed was selected at the CASS? prompt, JUMP down a few instructions to 3092H to display the L, restore AF from the STACK, and RETURN.
308D
CP C1H
If we are here, a key response other than L was entered at the “CASS?” prompt, so lets COMPARE Register A against C1H. Why C1H? Well, if the key hit was an ENTER (0DH) and you subtract 41H from it, you get C1H.
Results:
  • If Register A equals C1H, the Z FLAG is set.
  • If A < C1H, the CARRY FLAG will be set
  • if A >= C1H, the NO CARRY FLAG will be set.
308F
RET Z
If the modified Register A was C1H, meaning they just hit enter, then RETURN (which will default to high speed).
3090
CP FCH
If we are here, then Register A was not 0 and was not C1H, so lets compare Register A with FCH. Why? ASCII “H” is 48H, and 48H-4CH = -4, which is FCH. Results: If NZ is set, then they didn’t hit an H and therefore no valid key was received. If they hit H then the Z FLAG is set.
3092
PUSH AF
Save Register Pair AF to the top of the STACK so we can use A in the next 2 instructions.
3093
LD A,0DH
We want to display a carriage return, so first set A = 0DH (ASCII: Carriage Return).
3095
GOSUB to 0033H.

NOTE: 0033H is the character print routine, to put the character held in Register A at the current cursor position.
3098
POP AF
Restore AF from the top of the STACK.
3099
LOOP back to 307DH until the Z FLAG is set (meaning we got a valid H).
309B
RET
RETurn.

309CH – BOOTSTRAP VECTOR – This routine resets all the ports, sets the interrupt to jump to 4046H, moved 76 bytes from 36AAH to 4000H, and reads disk sectors into 4000H+ until the interrupt fires. This is a VECTOR from 3015H.

309C
“INITZ”
IM 1
Set the INTERRUPT MODE to 1.
309E
LD SP,407DH
Initialize the stack pointer by pointing it to 407DH.
30A1
Disable disk interrupts by OUTputting the value held in Register A to Port E4H.

NOTE: Port E4H is the non-maskable interrupt latch. Sending a 0 to Port E4 will turn off all Non-Maskable Interrupts.

By now the NMI circuitry should be shut off. Now, abort any floppy command that was executing when RESET was pressed. Do not change the drive select, as we may not have been writing on drive 0 when RESET was pressed, but we could start if we loaded the select port.

30A3
Force an interrupt on the Floppy Disk Controller by GOSUBing to 37A5H to sends a RESET to the FDC, waits a DJNZ loop of 10H, and then RETurn.

Next we program the CRTC in case there is a real one. In any event, zero the starting base registers which always works. You don’t have to do this in th 4P image as JP 0 always re-enters the real boot rom.

30A6
LD HL,3741H
Let HL = 3741H which will be the memory location that Port 89H feeds. This is the CRTC Table + 15 bytes.
30A9
XOR A
Zero register A and clear the flags.
30AA
LD C,89H
Let C = 89H as the Port which will feed (HL).
30AC
“PGM1”
Select a CRT Controller Control Register by sending the value held in Register A to Port 88H (the CRT Controller Control Register).
30AE
OUTD
This command takes the byte held in the memory location pointed to by HL and writes it to Port C. HL and B are then both decremented.

As a future space compression, the CRTC can be stored backwards with the trailing zeros overlaying the end of the RAM init blocks. There is no additional program cost, but you will save 4 or 5 bytes. Note: This will work for American versions, but may botch others!

30B0
INC A
LET A = A + 1.
30B1
CP 10H
Compare Register A with 10H . Results:
  • If Register A equals 10H, the Z FLAG is set.
  • If A < 10H, the CARRY FLAG will be set
  • if A >= 10H, the NO CARRY FLAG will be set.
30B3
LOOP back to the OUT instruction until A = 10H.
30B5
LD A,38H
Let A = 38H (Binary: 0011 1000).
30B7
OUTput the value held in Register A to Port ECH.

NOTE: Port ECH is the control port. In this case, this is turning on bits 3, 4 and 5 and turning off everything else. This sets the CASS MOTOR to OFF, SCREEN WIDTH to NORMAL, ALT CHAR to MISC, I/O BUS to ENABLE, the VIDEO WAITS to ENABLE, and CPU to 2Mhz. Note, Bit 5, which ENABLEs the Video Waits, is mirrored into a chip called a latch which has a wire running to both the Z-80 and the video circuity that mediates access to video RAM.
30B9
LD A,04H
Let A = 04H (Binary: 0000 0100) to leave only the real time clock interrupt enabled, and turn everything else off.
30BB
OUTput the value held in Register A to Port E0H.

NOTE: Port E0H is the maskable interrupt latch, which directs jumps.
Jump Table:
  • xxxxxxx1 jumps to 3365H
  • xxxxxx1x jumps to 3369H
  • xxxxx1xx jumps to 4046H
  • xxxx1xxx jumps to 403DH
  • xxx1xxxx jumps to 4206H
  • xx1xxxxx jumps to 4209H
  • x1xxxxxx jumps to 44040H
  • 1xxxxxxx jumps to 44043H

30BDH – This is in the middle of the machine setup routine – While this is a pass through to set up the machine / warm boot, it is also a jump point for a failure to boot from anything. This is a VECTOR from 3012H.

30BD
“BOOTZZ”
LD A,81H
Let A = 81H (Binary: 1000 0001) whbich is Double Density, Side 0, Drive 0.
30BF
Set the drive select to Drive 0 by OUTputting the value held in Register A to Port F4H.

NOTE: Port F4H is the Disk Drive and Disk Density Select. Bits 0-3 are the drive select of 0-3 and Bit 7 is 0 for single density and 1 for double density.
30C1
Do a master reset of the UART by OUTputting the value held in Register A to Port E8H.

NOTE: Port E8H is the RS-232 Status Register & Master Reset. Outputting ANYTHING to Port E8H resets the RS-232.
30C3
LD A,6FH
Let A = 6FH (Binary: 0110 1111). For the UART, this would be ENABLE transmit data, DROP DTR/Hang Up, DROP Ready-To-Send, Word Length 8, No Parity, and other bits needed for the Network III.
30C5
OUTput the value held in Register A to Port EAH.

NOTE: Port EAH is the RS-232 UART Control Register/Status Register. For output:
  • Bit 0: Data Terminal Ready
  • Bit 1: Request to End
  • Bit 2: Break
  • Bit 3: Parity Enable
  • Bit 4: Stop Bits
  • Bit 5: Select
  • Bit 6: Word Length
  • Bit 7: Parity (0=Odd, 1=Even)
30C7
LD HL,36AAH
LET HL = 36AAH to point to the RST Vector Table.
30CA
LD DE,4000H
LET DE = 4000H to point to the Destination Area in RAM.
30CD
LD BC,004FH
Let BC = 004FH (Decimal: 79 Bytes) as the number of bytes to move.
30D0
LDIR
Move the 79 bytes starting at 36AAH to 4000H.

NOTE: The LDIR instruction moves the byte from (HL) to (DE), then HL and DE are incremented, and BC is decremented until BC is zero. Interrupts CAN fire during this command.

At one point in time, Frank had a LD A,0CH and an OUT (FDC),A here stating that TEAC FD55’s get confused about the direction if the drive select comes too close to the restore command. So move the restore further away. Restore was only about 15-t states from the select.

That aside, at this point Frank writes that the two chunks of RAM init data have three bytes between them which are unused. The two tables must maintain their present positions or you will have to reassembly AROM and re-release Network 4. To save on code, the following LDIR moves three more bytes than it seems it should. This allows us to use the fallout HL from the last LDIR, saving three bytes in program space.

30D2
LD DE,41E5H
LET DE = 41E5H to be the new MOVE TO location.
30D5
LD C,3FH
LET C = 3F (Decimal: 63) for the number of bytes to move.
30D7
LDIR
Move the 63 bytes starting at 36FA [36AA + 4CH] to 41E5H.

NOTE: The LDIR instruction moves the byte from (HL) to (DE), then HL and DE are incremented, and BC is decremented until BC is zero. Interrupts CAN fire during this command.
30D9
GOSUB to 37C5H to clear all 2K of video RAM and initialize the Port 84H settings.
30DC
INput from Port F0H.

NOTE: Port F0H is the FDC Status port on input. Input Results:
  • Bit 0: Busy
  • Bit 1: Index/DRQ
  • Bit 2: Track 0/Data Lost
  • Bit 3: CRC error
  • Bit 4: Seek error/Record not found
  • Bit 5: If Reading, Record Type, if Writing, Write Fault/Head loaded
  • Bit 6: Write Protect
  • Bit 7: Not ready
30DE
INC A
LET A = A + 1.
30DF
If A is ZERO (meaning that Port F0H was FFH), then there is no floppy controller. In this case, JUMP to 3076H to process the CASS? prompt.
30E1
If we’re here then we were either jumped here or there is a Floppy Controller, so GOSUB to 34FEH (Part of the Network 4 Controller Boot).
30E4
If the NOT Z FLAG is set, JUMP BACK to 3079H (which is the CASS prompt).
30E6
LD DE,0381H
LET DE = 0381H.
30E9
INput from Port F0H.

NOTE: Port F0H is the FDC Status port on input. Input Results:
  • Bit 0: Busy
  • Bit 1: Index/DRQ
  • Bit 2: Track 0/Data Lost
  • Bit 3: CRC error
  • Bit 4: Seek error/Record not found
  • Bit 5: If Reading, Record Type, if Writing, Write Fault/Head loaded
  • Bit 6: Write Protect
  • Bit 7: Not ready
30EB
LD C,A
LET C = A.
30EC
BIT 2,A
Test Bit 2 (Track 0 indicator) of A (the Floppy Status). If it is 0 then the Floppy Drive is not on track 0, otherwise it is.
30EE
If Bit 2 of Register A is LOW, the Floppy Drive is not on track 0, then skip the next instruction and JUMP to 30F1H.
30F0
LD B,A
Let Register B = Register A (to save it from the next operation; it will be restored a few instructions down).
30F1
AND E
Bitwise AND on A with E. IF E remained 081H (Binary: 0101 0001), so only bits 0, 4, and 6 are live.
30F2
DEC A
Decrement the value held in Register A by 1. I suspect this is some three step trick to determine a floppy status with very few instructions.
30F3
LOOP BACK to 30E1H to re-poll the Floppy Controller if the Z (ZERO) Flag is set based on the AND E against A.
30F5
LD A,B
If we’re here, then the Masked A hit Zero, so restore Register A from Register B.
30F6
OR A
Since a LD command does not affect the flags, an OR A is necessary to the flags based on the contents Register A.
30F7
If the Z (ZERO) Flag is set JUMP to 30FDH.

Ira Note: This feels like a waste of a few cycles and I’m not sure why Frank did this. 30FDH if a JR NZ, but this is a JR Z, which means 30FDH could never trigger, and he should have jumped to the following instruction instead.
30F9
DI
Disable Interrupts so they don’t interrupt this routine. NOTE: This turns off the clock.
30FA
LD A,C
Let Register A = Register C.
30FB
AND 98H
MASK Register A against 98H (Binary: 1001 1000) to keep only Bits 3, 4, and 7 (which are CRC ERROR, SEEK ERROR, and NOT READY).
30FD
If none of those 3 errors are present, then JUMP to down to 313AH which tests for a Hard Drive.
30FF
LD A,01H
If we are here, then we have a CRC ERROR, SEEK ERROR, or NOT READY. Let A = 01H.
3101
OUTput the value held in Register A (i.e., 01H) to Port F2H.

NOTE: Port F2H is the Floppy Disk Controller Sector Register.
3103
LD A,E
LET Register A = Register E. Register E was set to 81H (Binary: 1000 0001) at 30E6H.
3104
OUTput the value held in Register A to Port F4H (to seelct DRIVE 0 and DOUBLE DENSITY).

NOTE: Port F4H is the Disk Drive and Disk Density Select. Bits 0-3 are the drive select of 0-3 (So Bit 0 is Drive 0, Bit 1 is Drive 1, Bit 2 is Drive 2, and Bit 3 is Drive 3, and Bit 7 is 0 for single density and 1 for double density.
3106
DEC A
Decrement the value held in Register A by 1 (Since there is no interim JUMP, Register A should now hold 80H, which is 1000 0000 in binary).
3107
OUTput the value held in Register A to Port E4H.

NOTE: Port E4H is the non-maskable interrupt latch. When outputting 1000 0000 (i.e., Bit 6 disabled / Bit 7 enabled) to Port E4H, INTRQ Interrupt is ENABLED and DRQ Interrupt is DISABLED.
3109
LD HL,27C3H
LET HL = 27C3H.

NOTE: This will ultimately be a JUMPed to instruction via Interrupts.
310C
LD (4049H),HL
Load 27C3H into the memory location held at 4049H.

NOTE: 4049H is the Non-Maskable Interrupt Vector.
310F
LD C,F3H
LET C = F3H. This identifies the PORT to be used in the INI in 311DH. In this case, it is port F3H, whcih is the FDC Data Register (the data byte to be READ or WRITTEN to disk).
3111
LD HL,4300H
LET HL = 4300H. This sets up the starting location for the block move of the INI command in 311DH.
3114
GOSUB to 37A7H (which outputs Register A to the FDC, delays 10H instructions, and returns).
3117
INput from Port F0H to read the FDC Status.

NOTE: Port F0H is the FDC Status port on input. Input Results:
  • Bit 0: Busy
  • Bit 1: Index/DRQ
  • Bit 2: Track 0/Data Lost
  • Bit 3: CRC error
  • Bit 4: Seek error/Record not found
  • Bit 5: If Reading, Record Type, if Writing, Write Fault/Head loaded
  • Bit 6: Write Protect
  • Bit 7: Not ready
3119
AND 02H
Mask A against 0000 0010 to lose everthing bu the DRQ status.
311B
If Z is set then DRQ was NOT found, so LOOP back to 3117H and keep trying until the Index is found. An error will cause an exit via NMI.
311D
INI
If we are here then the DRQ was just found, so read a byte from Port C (the FDC Data Register) into the memory location pointed to by HL (4300H+), then DECrement HL and INCrement B. Z is set if B = 0; otherwise it is reset.
311F
LD A,C1H
LET A = C1H (Binary: 11000001) to turn on hardware waits on INI.
3121
OUTput the value held in Register A (which is a C1H on initial run) to Port F4H. C1H is Bits 0, 6, and 7 ON so this sets Drive 0, Enable Wait State, Double-Density

NOTE: Port F4H is the Disk Drive and Disk Density Select. Bits 0-3 are the drive select of 0-3 and Bit 7 is 0 for single density and 1 for double density.
3123
INI
Read a byte from Port C into the memory location pointed to by HL, then DECrement HL and INCrement B. Z is set if B = 0; otherwise it is reset.
3125
JUMP BACK 2 instructions to 3121H. This will continue looping until there is an interrupt.

3127H – FLOPPY DRIVE – This routine will clear the stack, check the floppy drive status register after a read, and JUMP to 4300H to continue to DOS if successful, and jump to 310BH if errors were found.

3127
POP HL
Clear the STACK (since HL is about to be overwriten) to clear the NMI exit address.
3128
LD HL,45EDH
LET HL = 45EDH to set up to put a RETN instruction in the NMI vector.
312B
LD (4049H),HL
Load 45EDH into 4049H.

NOTE: 4049H is the Non-Maskable Interrupt Vector.
312E
INput from Port F0H.

NOTE: Port F0H is the FDC Status port on input. Input Results:
  • Bit 0: Busy
  • Bit 1: Index/DRQ
  • Bit 2: Track 0/Data Lost
  • Bit 3: CRC error
  • Bit 4: Seek error/Record not found
  • Bit 5: If Reading, Record Type, if Writing, Write Fault/Head loaded
  • Bit 6: Write Protect
  • Bit 7: Not ready
3130
AND 1CH
Mask A with 1CH (Binary: 0001 1100). This keep only bit 3 (Track 0/Lost Data), bit 4 (CRC Error) and bit 5 (Seek Error/Record Not Found). If NONE of these are present, then Z will be set.
3132
OUTput the value held in Register A to Port E4H.

NOTE: Port E4H is the non-maskable interrupt latch. When outputting to E4H, only bits 6 and 7 are used. Since this is sending a Bit 6 = 0, Bit 7 = 0, to Port E4H, both the DRQ interrupt and the INTRQ interrupt are disabled.
3134
If A was ZERO (meaning, none of those errors were present), JUMP to 4300H to execute the boot sector which was copied there to run DOS.
3137
DEC D
Decrement the value held in Register D by 1.
3138
If D is still not zero, JUMP BACK to 30FFH to keep trying.

313AH – HARD DRIVE – This routine is jumped from 30FDH if the last disk read did not produce any of CRC ERROR, SEEK ERROR, and NOT READY.

313A
TRYHD
GOSUB to 37BBH (cold start/reset the Hard Drive controller).
313D
LD A,01H
LET A = 01H for the sector number.
313F
OUTPUT the sector (held in A, from C) to Port CBH.

NOTE: Port CBH is Register 3 for WD1010 Winchester Disk Controller Chip which is the Hard Disk Sector Number Register.
3141
Test to see if the hardware is there by polling Port CBH and put the results into A.

NOTE: Port CBH is Register 3 for WD1010 Winchester Disk Controller Chip which is the Hard Disk Sector Number Register.
3143
DEC A
Decrement the value held in Register A by 1. If the Hard Drive is there, then A should be 0 after this instruction.

NOTE: A was set to 1 and then written to the HD Controller Chip and then read back. If a 1 was written and read back, then we have an active Hard Drive. Doing this decrement just sets the Z FLAG for that fact.
3144
If A is not ZERO (meaning a 1 was written to the HD controller, but a 1 was not returned when the HD controller was polled), JUMP to 318BH to fail the boot up process, display the DISKETTE? message and then restart the system.
3146
If we are here then there is a hard drive and A=0. First, OUTPUT the contents of A (which are ZERO) to Port CEH, which sets the hard drive to Head 0, Drive 0, Sector Size 256.

NOTE: Port CEH is Register 6 for WD1010 Winchester Disk Controller Chip which is the register contaning Hard Disk Sector Size / Drive # / Head # as follows:
  • Bits 0-2: Head Number (0-7)
  • Bits 3-4: Drive Number (00=#1, 01=#2, 10=#3, 11=#4)
  • Bits 5-6: Sector Size (00=256, 01=512, 10=1024, 11=128)
  • Bit 7: Extension (if this is set, Error Checking and Correction codes are in use and the R/W data [sector length + 7 bytes] do not check or generate CRC)
3148
OUTPUT the contents of A (which are ZERO) to Port CDH to send a zero to the cylinder field.

NOTE: Port CDH is Register 5 for WD1010 Winchester Disk Controller Chip which is the Hard Disk Cylinder LSB.
314A
OUTPUT the contents of A (which are ZERO) to Port CCH to send a zero to the cylinder field.

NOTE: Port CCH is Register 4 for WD1010 Winchester Disk Controller Chip which is the Hard Disk Cylinder MSB.
314C
LD B,A
LET B = A, so now B is ZERO.
314D
LD D,0AH
LET D = 0AH (Decimal: 10).
314F
DEC B
Decrement the value held in Register B by 1.
3150
JUMP down a few instructions to 3157H if B is not ZERO.

NOTE: Talk about spaghetti code, this ia a jump if something that starts off at FFH is NOT zero!
3152
DEC DE
If we are here, then B was zero. Decrement the value held in Register Pair DE by 1.
3153
3154
LD A,D
OR E
Since there is no way to test DE for ZERO, a common way to do that is to load Register A with Register D and then OR Register E. A will be ZERO only if both Registers D and E were zero.
3155
If Register A is zero (meaning Register DE was Zero) then JUMP DOWN to 318BH to display the DISKETTE? message and then restart the system.
3157
HARDX1
If we are here, then we either jumped here from 3150H or DE was not zero, so Poll Port CFH and put the result into Register A.

NOTE: Port CCH is Register 7 for WD1010 Winchester Disk Controller Chip and is the Hard Disk Error Status Register when used for INPut.
3159
CP FFH
Compare Register A with FFH.

Note: If Register A equals FFH, the Z FLAG will be set. This command is new to XDROM (and was not in Frank’s proposed Model III C ROM revision)
315B
If A is FFH then the WD1010 Winchester Disk Controller Chip threw off some major errors (basically, every error), so JUMP to 313AH to reset the controller and try again. This command is new to XDROM (and was not in Frank’s proposed Model III C ROM revision).
315D
AND 40H
If we are here, then the WD1010 Winchester Disk Controller Chip didn’t throw FF, so we need to mask to see what is what. In this case, MASK Register A against 40H (Binary: 0100 0000) to leave only Bit 6 live. Bit 6 would be the DRIVE READY.
315F
If we are here, then we got DRIVE READY from that MASK, so LOOP BACK to 314FH.
3161
LD HL,4300H
If we are here, the Hard Drive returned a READY code and we are going to boot from it. Let HL = 4300H, which is the standard memory location for loading a BOOT sector into.
3164
LD A,10H
Let A = 10H so as to trigger a RESTORE TO CYLINDER 0 command.
3166
GOSUB to 37B0H with a parameter of 10H to be output to Port CFH to restore to cylinder 0. Routine will return Z FLAG if there is no error.
3169
If NZ is set, then the Hard Drive operation in the prior instruction returned an error, so JUMP to 318BH to display the DISKETTE? message and then restart the system.
316B
LD A,20H
LET A = 20H so as to trigger a READ FROM THE HARD DRIVE command.
316D
GOSUB to 37B0H with a parameter of 20H to be output to Port CFH to read the boot sector. Routine will return Z FLAG if there is no error.
3170
LD BC,00C8H
Let BC = 00C8H which sets C to C8H which is the Hard Disk Data Register (Read/Write). This is to read 256 sectors from the data port.
3173
If the Z FLAG is set (from the CALL to 37B0H, meaning there was no error in the Hard Drive SCAN ID routine), skip the next few instructions and go to 3180H to read the sector and return to 4300H.

If we are here, then the call to 37B0H with a parameter of 20H (to do a SCAN ID) produced an error.

3175
LD A,20H
LET A = 20H (Binary: 0 01 00 000; meaning only Bit 6 is HIGH) to try again but with 512 byte sectors.
3177
OUTPUT the contents of A to Port CEH (which would be Head 0 [Bit 0-2], Drive 0 [Bit 3-4], 512K Sector [Bit 5-6], No Extension [Bit 7]).

NOTE: Port CEH is Register 6 for WD1010 Winchester Disk Controller Chip which is the register contaning Hard Disk Sector Size / Drive # / Head # as follows:
  • Bits 0-2: Head Number (0-7)
  • Bits 3-4: Drive Number (00=#1, 01=#2, 10=#3, 11=#4)
  • Bits 5-6: Sector Size (00=256, 01=512, 10=1024, 11=128)
  • Bit 7: Extension (if this is set, Error Checking and Correction codes are in use and the R/W data [sector length + 7 bytes] do not check or generate CRC)
3179
GOSUB to 37B0H with a parameter of 20H to be output to Port CFH. Routine will return Z FLAG if there is no error.
317C
If the NOT Z FLAG is set then 256 sectors failed and 512 sectors failed so we need to give up on the Hard Drive by JUMPing to 318BH to display the DISKETTE? message and then restart the system.
317E
INIR
Read the first half of the sector. The INIR command takes a byte from Port C (D0H) and writes it to the memory location pointed to by (HL). HL is then incremented, and B is decremented. If B is not zero, this operation is repeated. Interrupts can fire during this command.

3180 – If we are here, then all is good to boot from the Hard Drive.

3180
RD256
INIR
Read another 256 bytes. The INIT command command takes a byte from Port C (D0H) and writes it to the memory location pointed to by (HL). HL is then incremented, and B is decremented. If B is not zero, this operation is repeated. Interrupts can fire during this command.
3182
LD HL,43FFH
Let HL = 43FFH (which is the BUFFER + 255 bytes).
3185
GOSUB to 37A5H to sends a RESET to the FDC in case it was left busy, waits a DJNZ loop of 10H, and then RETurn.
3188
GOSUB to 3520H which tests 4400H for FDH and if its there, then JUMP to 4401H, otherwise return here with NZ set.

318BH – This fails out the FLOPPY DISK/HARD DRIVE routine.

318B
NOHARD
LD HL,0277H
LET HL = 0277H which is the memory address of the DISKETTE? message.
318E
GOSUB to 3070H (which GOSUBs to 021BH [to display the message pointed to by HL] and then JUMPS to 0049H).
3191
JUMP to 30BDH which restarts the system startup (clearing the ports, polling the floppy drives, etc).

3194H – NOT USED.

3194
RST 38H
This instruction is never executed.
3195
RST 38H
This instruction is never executed.
3196
RST 38H
This instruction is never executed.
3197
RST 38H
This instruction is never executed.
3198
RST 38H
This instruction is never executed.
3199
RST 38H
This instruction is never executed.
319A
RST 38H
This instruction is never executed.
319B
RST 38H
This instruction is never executed.
319C
RST 38H
This instruction is never executed.
319D
RST 38H
This instruction is never executed.
319E
RST 38H
This instruction is never executed.
319F
RST 38H
This instruction is never executed.
31A0
RST 38H
This instruction is never executed.
31A1
RST 38H
This instruction is never executed.
31A2
RST 38H
This instruction is never executed.
31A3
RST 38H
A call to RST 38H is a call to the DOS Vector 4012H (Jump point to interrupt service routine).

31A4H – DIFFERENCE BETWEEN XDROM AND XROM [Code is never used]


XDROM

31A4
28H 0EH
This would be JUMP to 31B4H if the Z (ZERO) Flag is set. Note: There is no workable code at 31B4H

XROM

31A4
01 27
This would be an incomplete OPCODE.

31A6H – CASSETTE – Write a Timing Mark to Tape. Writes 14 .46 volt pulses to tape, 14 .00 volt pulses to tape, waits for 78H loops, and RETurns. Called from 3258H and 325FH.

31A6
CTPULS
LD A,01H
LET Register A = 01H.
31A8
Make a TOP OF PULSE by OUTputing the value held in Register A to Port FFH.

NOTE: Port FFH is the CASSETTE PORT. Bits 0-2 for OUTPUT control what is written. In the case of 01H a .46 volt spike is written.
31AA
LD B,0DH
LET Register B = 0DH (for a DELAY loop of 13 or 80 milliseconds).
31AC
CT1
Execute this instruction until Register B = 0.
31AE
INC A
LET A = A + 1.

Note: IN Frank’s Proposed Model III C ROM Update this was LD A,2 instead.
31AF
Make a BOTTOM OF PULSE by OUTputing the value held in Register A to Port FFH.

NOTE: Port FFH is the CASSETTE PORT. Bits 0-2 for OUTPUT control what is written. In the case of 02H a .00 volt spike is written.
31B1
LD B,0DH
LET Register B = 0DH (for a DELAY loop of 13 or 80 milliseconds).
31B3
CT2
Execute this instruction until Register B = 0.
31B5
GOSUB to 324EH to terminate the pulse by resetting the cassette port.
31B8
LD B,78H
LET Register B = 78H (for a DELAY loop of 120 or 771 milliseconds).
31BA
CT3
Execute this instruction until Register B = 0.
31BC
RET
RETurn.

31BDH – CASSETTE – This SUBROUTINE resets the cassette, sets the NMI, turns on interrupts, and RETURNs.

31BD
LD HL,2CA5H
LET HL = 2CA5H. 2CA5H Points to the BAD Message.

31C0H – CASSETTE – Turn the Cassette Motor Off. This is a VECTOR from 300CH.

31C0
CASOFZ
LD A,(4213H)
Get the old interrupt status by setting A to be the memory contents of 4213H.

NOTE: 4213H holds the Default Interrupt Vector Setting.
31C3
Put the status into the interrupt register by outputting the value held in Register A to Port E0H.

NOTE: Port E0H is the maskable interrupt latch, which directs jumps.
Jump Table:
  • xxxxxxx1 jumps to 3365H
  • xxxxxx1x jumps to 3369H
  • xxxxx1xx jumps to 4046H
  • xxxx1xxx jumps to 403DH
  • xxx1xxxx jumps to 4206H
  • xx1xxxxx jumps to 4209H
  • x1xxxxxx jumps to 44040H
  • 1xxxxxxx jumps to 44043H
31C5
Clear the interrupt status by INputting a byte from Port FFH and put it into Register A.

NOTE: Port FFH is the CASSETTE PORT.
31C7
LD A,(4210H)
Get the status of the status I/O by setting Register A to hold the contents of memory location 4210H into A.

NOTE: 4210H holds the bit mask for port ECH. Port ECH stores miscellaneous controls.
31CA
AND FDH
Reset the cassette motor bit by MASKing the memory contents of 4210H against FDH (Binary: 1111 1101) to turn off Bit 1 (so as to indicate CASSETTE OFF).
31CC
Set the status to off by GOSUBing to 31EDH to save the mask into 4210H, output it to Port ECH (The Misc Port), and return here.
31CF
EI
Enable Interrupts.
31D0
RET
RETurn.

31D1H – CASSETTE – This routine is to turn on the cassette – Part 1. This will remove the return address, save DE and BC, restore the return address, and than blank the **. This is a VECTOR from 300FH.

31D1
CTON
EX DE,HL
Swap DE and HL to remove the return address.
31D2
EX (SP),HL
Put DE on top of the stack by EXchanging the memory contents pointed to by the STACK POINTER and HL (which is now what DE was).
31D3
PUSH BC
Save the contents of Register Pair BC to the top of the STACK.
31D4
PUSH HL
Save the contents of Register Pair HL (which contains the return address) to the top of the STACK.
31D5
EX DE,HL
Put HL back into HL by EXchanging the value held in Register Pair DE and the value held in Register Pair HL.
31D6
Poll Port ECH and put the results into A.

NOTE: Port ECH is the Miscellaneous Controls port, which covers clock on/off (Bit 0), cassette motor on or off (Bit 1), double size video on or off (Bit 2), and special character set select of Kana or misc (Bit 3). Higher bits are used for the Model 4 only.
31D8
LD DE,2020H
Load DE with SPACE SPACE to clear the cassette video.
31DB
LD (3C3EH),DE
Load the screen memory location of 3C3EH with DE to put 2 spaces.
31DF
GOSUB to 31E8H to turn on the cassette.
31E2
LD BC,7D00H
Load a delay count of 7D00 (Decimal: 32,000) into BC to set up for a 520 millisecond delay.
31E5
Jump to 0060H, which JUMPs to the delay routine at 01FBH and EXITs the routine via a RETurn.

31E8H – CASSETTE – Turn On The Cassette – Actually Set the Bit Mask and Output the Command. This is only called from 31DFH (a few instructions up)

31E8
“CTON0”
LD A,(4210H)
Get the cassette switch byte by LoaDing A with the memory contents of 4210H.

NOTE: 4210H is the bit mask for Port ECH. Port ECH is the Miscellaneous Controls port, which covers clock on/off (Bit 0), cassette motor on or off (Bit 1), double size video on or off (Bit 2), and special character set select of Kana or misc (Bit 3). Higher bits are used for the Model 4 only.
31EB
OR 02H
Turn the cassette motor on by ORing A with 02H (0000 0010) to set Bit 1.
31ED
“CASET”
LD (4210H),A
Put out the new I/O status by LoaDing A into 4210H.

NOTE: 4210H is the bit mask for Port ECH. Port ECH is the Miscellaneous Controls port, which covers clock on/off (Bit 0), cassette motor on or off (Bit 1), double size video on or off (Bit 2), and special character set select of Kana or misc (Bit 3). Higher bits are used for the Model 4 only.
31F0
Turn the cassette motor on by OUTputing the value held in Register A to Port ECH.

NOTE: Port ECH is the control port. In this case, this is turning on bit 5 which ENABLEs the Video Waits. This bit is mirrored into a chip called a latch which has a wire running to both the Z-80 and the video circuity that mediates access to video RAM.
31F2
RET
RETurn.

31F3H – CASSETTE – Check for a Data Error. This routine takes 60 T-States or 2 Countrs to process the data

31F3
LD A,C
Let Register A = Register C (which is the cycle time of a wave from the cassette). A longer cycle time means lower frequency. Low frequency is a 0, high frequency a 1.
31F4
CP 22H
We need to figure out if the data was 1 or a 0 so we compare Register A with 22H. This will set the carry if a high frequency (low cycle time) wave was found (thank you George Phillips for this explanation).
31F6
RL D
Since Register D holds the data bit which was read in, we need to get the CARRY BIT into that. To do this, we rotate D left one bit, with the contents of BIT 7 being put into the CARRY FLAG and the CARRY FLAG are put into BIT 0. This puts the read data into BIT 0 of D and uses the CARRY FLAG which was set in the prior CP 22H instruction (thank you George Phillips for the hint on the CP 22H carry flag!).
31F8
CP 0FH
Make sure it the wave cycle time was not too quick (or too LOW) by checking A against 0FH. Results:
  • If Register A equals 0FH, the Z FLAG is set.
  • If A < 0FH, the CARRY FLAG will be set
  • if A >= 0FH, the NO CARRY FLAG will be set.
31FA
If A < 0FH then the wave cycle time was too quick and we have a data error so JUMP a few instructions forward to 31FFH.
31FC
CP 3EH
Compare A against 3EH to make sure it was not too slow (or too HIGH). Results:
  • If Register A equals 3EH, the Z FLAG is set.
  • If A < 3EH, the CARRY FLAG will be set
  • if A >= 3EH, the NO CARRY FLAG will be set.
31FE
RET C
If it wasn’t too slow (and wasn’t too quick), RETURN.

31FFH – This puts a D on the screen over one of the *‘s to show a DATA ERROR.

31FF
“ERROR1”
LD A,44H
It was too slow, so load A with a D.
3201
LD (3C3EH),A
Put the D on the screen at video location 3C3EH.
3204
RET
RETurn.

3205H – CASSETTE – This routine reads 8 bits from cassette. Is not expressly jumped to, but is loaded up into a vector at 32DCH.

3205
“DATAR”
GOSUB to 3350H to READ a BIT from Cassette and poll for a BREAK keypress. We are just loading dummy bits here.
3208
LD B,08H
LET B = 08H.
320A
“DATAR1”
GOSUB to 3350H to READ a BIT from Cassette and poll for a BREAK keypress.
320D
GOSUB to 31F3H to check for a data error.
3210
Loop back to 320AH and keep looping until Register B = 0 (i.e., 8 bits have been read).
3212
JUMP to 321BH to blink and exit since 8 bits have been read.

3214H – CASSETTE – Vector for a SLOW cassette read. Frank saved 2 bytes by putting the memory addresses into BC and then using 1 byte instructions to manipulate the memory address.

3214
“RDATA”
LD B,08H
Load B with 8, representing the need to LOOP for 8 bits.
3216
CTBO
GOSUB to 322FH to get one bit.

NOTE: 321EH reads the tape until it finds a timing mark or the BREAK is hit.
3219
Loop back to 3216H and keep looping until Register B = 0, meaning 8 tries.
321B
“CTB1”
LD BC,4212H
LET BC = 4212H to point at the blink value.
321E
LD A,(BC)
Read the blink value byte by putting the contents of the memory location pointed to by (BC) into A.

NOTE: 4212H holds the cassette blinker counter.
321F
INC A
LET A = A + 1 to advance the counter.
3220
AND 5FH
Mask A against 5FH (Binary: 0101 1111) to turn off bits 5 and 7 (which locks A to be between 00H and 7FH).
3222
LD (BC),A
Put A into (BC) to store the masked value.

NOTE: 4212H holds the cassette blinker counter.
3223
LOOP DOWN to 322CH if masked counter is still not zero as we did not roll over.
3225
LD BC,3C3FH
If we are here then the blink counter did roll over so, set BC = 3C3FH (which is a screen address).
3228
LD A,(BC)
Get the character that was on the screen by LoaDing the value stored in the memory location pointed to by Register Pair BC into Register A.
3229
XOR 0AH
XOR A against 0AH (00001010). This turns a * into a SPACE and vice versa as a * is 0010 1010 and when you XOR that against 0000 1010 you get 0010 0000 which is a SPACE (and vice versa).
322B
LD (BC),A
Put the revised A (the * or the SPACE) onto the screen at position 3C3FH.
322C
“CTB2”
LD A,D
Put D (the byte which was read from cassette) into A.
322D
JUMP to 32A9H to restore the registers and RETURN.

322FH – CASSETTE – Read the tape until it finds a timing mark or the BREAK is hit.

322F
“CTBIT”
PUSH BC
Save the contents of Register Pair BC to the top of the STACK.
3230
“CB0”
Attempt to get a bit from cassette by INputing from Port FFH and putting it into Register A.

NOTE: Port FFH is the CASSETTE PORT.
3232
RLA
Put the bit received in A into the Carry Bit by rotating A left 1 bit (putting bit 7 into the CARRY FLAG and the old CARRY FLAG into bit 0).
3233
If the CARRY FLAG is set then the timing mark was found, so JUMP to 323DH.
3235
GOSUB to 028DH to check for a BREAK key.
3238
Loop back to 3230H until we get either a timing mark or a BREAK key.
323A
If we are here, then we got a BREAK key, so abort by JUMPing to 335CH.

323DH – CASSETTE – Wait for the timing mark to pass and the next data to show up. Put that data into Bit 0 of D.

323D
CB1
LD B,6EH
Load B with 6EH, which is the length of the timing mark of 703 milliseconds.
323F
“CB2”
LOOP this command until Register B = 0.
3241
GOSUB to 324EH to RESET the cassette port.
3244
LD B,98H
Load B with 98H for a delay of 974 milliseconds, which is when the next data pulse should be available.
3246
“CB3”
LOOP this command until Register B = 0.
3248
INput a byte from Port FFH and put it into Register A.

NOTE: Port FFH is the CASSETTE PORT.
324A
“CB4”
POP BC
Move the value at the top of the STACK to Register Pair BC.
324B
RLA
Put the new bit into the CARRY via RLA which rotates A left one bit, with the contents of BIT 7 being put into the CARRY FLAG and the CARRY FLAG are put into BIT 0. This puts the read data into the CARRY FLAG.
324C
RL D
We need to build the byte by adding this new bit so we use the RL D instruction rotate D left one bit, with the contents of BIT 7 being put into the CARRY FLAG and the CARRY FLAG are put into BIT 0. This puts the read data into BIT 0 of D.

324EH – CASSETTE – Output to the cassette latch / clear input data latch. This routine OUTputs a 0 to the Cassette Port FFH. Note: This wasn’t here in Franks proposed revised ROM C. He had a JUMP to this location here, so he saved some bytes by relocating this routine here.

324E
CTSTAT
XOR A
Zero register A and clear the flags.
324F
CTCHG
OUTput the value held in Register A to Port FFH.

NOTE: Port FFH is the cassette port. When outputting to FFH, Bits Zero and 1 set to: 00 is .85V, 01 is .46V, and 10 is 0.0V.
3251
RET
RETurn.

3252H – CASSETTE – Output one data byte (held in Register A) to cassette. Vector for a SLOW cassette write.

3252
“WDATA”
PUSH AF
Save the contents of Register Pair AF to the top of the STACK.
3253
PUSH BC
Save the contents of Register Pair BC to the top of the STACK.
3254
PUSH DE
Save the contents of Register Pair DE to the top of the STACK.
3255
LD C,08H
Load C with an 8, representing 8 bits to be written.
3257
LD D,A
Load D with A to save the data byte into Register D.
3258
BYT0
Geenrate a clocking pulse by GOSUBing to 31A6H to write the timing mark to tape. That routine writes 14 .46 volt pulses to tape, 14 .00 volt pulses to tape, waits for 78H loops, and RETurns.
325B
RLC D
Since we need to send a bit, and the easiest way to extract it is by using the CARRY FLAG, RLC D is used to rotate D left one bit, with the contents of BIT 7 being put into BOTH the CARRY FLAG and BIT 0. This puts the DATA BIT into CARRY.
325D
If there is no DATA BIT (because CARRY is 0) then JUMP to 3269H to write a 0 bit (which is actually just a wait).
325F
If we are here, then there was a PULSE, so GOSUB to 31A6H to write the data bit to tape. That routine writes 14 .46 volt pulses to tape, 14 .00 volt pulses to tape, waits for 78H loops, and RETurns.
3262
“BYT1”
DEC C
Reduce the counter holding the number of bits to deal with by 1.
3263
Loop back to 3258H until the counter in C hits 0.
3265
“EXITD”
POP DE
Move the value at the top of the STACK to Register Pair DE.
3266
“EXITB”
POP BC
Move the value at the top of the STACK to Register Pair BC.
3267
POP AF
Move the value at the top of the STACK to Register Pair AF.
3268
RET
Return.

3269H – CASSETTE – Write a 0 Bit. This is accomplished by simply waiting the appropriate amount of time and doing nothing. Called only from 325DH.

3269
BYT2
LD B,9AH
Set a delay of 9A (i.e., 986 milliseconds).
326B
“BYT3”
Keep running this countdown until 9A loops.
326D
JUMP to 3262H (which is 1 instruction later than the JUMP to this location).

326FH – CASSETTE – SLOW tape header write. TTurn on the cassette and generate a SYNC byte. his is a VECTOR from 3000H.

326F
“WSYNC”
PUSH HL
Save the contents of Register Pair HL to the STACK.
3270
LD HL,3252H
Load HL with 3252H.

NOTE: 3252H is the Vector for a SLOW cassette write.
3273
LD (420CH),HL
Load the memory contents of 420CH with HL.

NOTE: 420CH is the TAPE WRITE VECTOR.
3276
LD B,53H
Load B with 53H (Decimal: 83) in prepartion to output 83 ZEROes.
3278
XOR A
Zero register A and clear the flags.
3279
“CSAV1”
GOSUB to 3252H to output a byte.

NOTE: 3252H is the Vector for a SLOW cassette write.
327C
Loop back to the prior instruction (Slow Cassette Write) and keep looping until Register B = 0.
327E
LD A,A5H
Load A with A5H.

NOTE: A5H is the OUTPUT SYNC BYTE.
3280
Output the SYNC byte to cassette by GOSUBing to 3252H (which is the Vector for a SLOW cassette write).
3283
JUMP to 32A8H.

3285H – CASSETTE – Turn cassette on and read SYNC data. SLOW tape header read. This is a VECTOR from 3006H.

3285
“RSYNC”
PUSH HL
Save the contents of Register Pair HL to the top of the STACK.
3286
LD HL,3214H
Put the Vector for a SLOW Cassette Read into HL.
3289
LD (420EH),HL
Load the SLOW Cassette Read Vector (of 3214H) into memory location 420EH.
328C
“HEADR”
LD B,40H
Load B with 40H to set up a loop of 64 to try to find 64 zeroes.
328E
LD D,00H
Clear the data bit by LoaDing D with 0.
3290
“HEADR1”
GOSUB to 322FH.

NOTE: 321EH reads the tape bit and deals with BREAK.
3293
LD A,D
Load A with the D (the data byte) to begin to check the current data byte.
3294
OR A
Since a LD command does not affect the flags, an OR A is necessary to the flags based on the contents Register A.
3295
If the data was not a ZERO, then LOOP BACK to 328CH.
3297
LOOP BACK to 3290H and keep looping until Register B = 0 (i.e., loop until we have 64 good counts).
3299
“CLOD1”
GOSUB to 322FH.

NOTE: 321EH reads the tape bit and deals with BREAK.
329C
LD A,D
Move the DATA to the A register by LoaDing A with the D.
329D
CP A5H
Compare Register A with A5H (which is the SYNC BYTE).
329F
JUMP back to to 3299H if a SYNC BYTE wasn’t found.
32A1
“CLOD2”
LD HL,2A2AH
In preparation to display a **, load HL with **.
32A4
LD (3C3EH),HL
Put HL (**) onto the screen at location 3C3EH.
32A7
LD A,H
Load A with H (which is a single *).
32A8
“EXITH”
POP HL
Move the value at the top of the STACK to Register Pair HL.
32A9
“EXITBC”
POP BC
Move the value at the top of the STACK to Register Pair BC.
32AA
POP DE
Move the value at the top of the STACK to Register Pair DE.
32AB
RET
RETurn.

32ACH – CASSETTE – 1500 Baud Routine. Writes out a header of 256 bytes of 55H and then outputs the SYNC byte of 07FH using 8 bit bytes. FAST tape header write. This is a VECTOR from 3003H.

32AC
“SYNCW”
PUSH HL
Save the contents of Register Pair HL to the top of the STACK.
32AD
LD HL,32CBH
Put 32CBH into HL for preparation of loading that address into the TAPE WRITE VECTOR.
32B0
LD (420CH),HL
Load the memory contents of 420CH with HL.
NOTE: 420CH is the TAPE WRITE VECTOR.
32B3
LD B,00H
Load B with 00H to set up a loop of 256 to output 256 bytes of 55H.
32B5
“SYNLP1”
LD A,55H
Load A with 55H (the header byte).
32B7
GOSUB to 32C5H.

NOTE: 32C5H restore all registers from the STACK, and Fill C with A, and JUMP to cassette write.
32BA
LOOP BACK two instructions until B=0 (e.g., 256 iterations).
32BC
LD A,7FH
Load A with 7FH.

NOTE: 7FH is the OUTPUT SYNC BYTE.
32BE
GOSUB to 32C5H.

NOTE: 32C5H restore all registers from the STACK, and Fill C with A, and JUMP to cassette write.
32C1
LD A,A5H
Load A with A5H.

NOTE: A5H is the SLOW SYNC BYTE.
32C3
JUMP to 32A8H to POP the HL, DE, and BC Registers and RETurn.

32C5H – CASSETTE – 1500 Baud Routine. Save all registers to the STACK, and Fill C with A (which is the byte to be written), and JUMP to cassette write.

32C5
“DATAWA”
PUSH AF
Save the contents of Register Pair AF to the top of the STACK.
32C6
PUSH BC
Save the contents of Register Pair BC to the top of the STACK.
32C7
PUSH DE
Save the contents of Register Pair DE to the top of the STACK.
32C8
LD C,A
Store the DATA BYTE (byte to be written to Cassette) (held in Register A) into Register C.
32C9
JUMP to 32D2H to write to cassette without a start bit.

32CBH – CASSETTE – Save all registers toe the STACK, and Fill C with A (which is the byte to be written), and GOSUB to write out the START BIT

32CB
“DATAW”
PUSH AF
Save the contents of Register Pair AF to the STACK.
32CC
PUSH BC
Save the contents of Register Pair BC to the top of the STACK.
32CD
PUSH DE
Save the contents of Register Pair DE to the STACK.
32CE
LD C,A
Store the byte to be written to Cassette (held in Register A) into Register C.
32CF
GOSUB to 333FH to write the START BIT.

32D2H – CASSETTE – Write to cassette WITHOUT a start bit. This routine outputs 8 bits

32D2
“DATAW1”
LD B,08H
Load B with an 8 to set up a loop for 8 bits.
32D4
“DATAW2”
GOSUB to 3336H to output a bit.
32D7
Loop back one instruction and keep looping until Register B = 0 (i.e., 8 bits have been written).
32D9
JUMP to 3265H to restore all the registers and RETURN.

32DBH – CASSETTE – This routine will seek a good header, determine whether to use the rising of falling edge, and will find the sync byte. FAST tape header read. This is a VECTOR from 3009H.

32DB
“SYNCR”
PUSH HL
Save the contents of Register Pair HL to the STACK.
32DC
LD HL,3205H
Load HL with 3205H (the DATAR location).

NOTE: 3205H reads an verifies a byte.
32DF
LD (420EH),HL
Store that into the TAPE READ VECTOR (memory location of 420EH) with the 3205H.
32E2
LD A,01H
Load A with a 1 to set the interrupt for “rising edge”.
32E4
OUTput the value held in Register A to Port E0H.
NOTE: Port E0H is the maskable interrupt latch, which directs jumps.
Jump Table:
  • xxxxxxx1 jumps to 3365H
  • xxxxxx1x jumps to 3369H
  • xxxxx1xx jumps to 4046H
  • xxxx1xxx jumps to 403DH
  • xxx1xxxx jumps to 4206H
  • xx1xxxxx jumps to 4209H
  • x1xxxxxx jumps to 44040H
  • 1xxxxxxx jumps to 44043H
32E6
“BAD”
LD B,80H
Set up a loop of 80H (128) to try to find good pulses.
32E8
“LOOK”
GOSUB to 3350H to get a pulse count by reading a BIT from Cassette and poll for a BREAK keypress.
32EB
LD A,C
Let Register A = Register C. Register C is holding the pulse width.
32EC
CP 0FH
We need to find out if the count is GREATER THAN the lower limit. To to this, we compare Register A with 0FH to see if the pulse width was too short. Results:
  • If Register A equals 0FH, the Z FLAG is set.
  • If A < 0FH, the CARRY FLAG will be set
  • if A >= 0FH, the NO CARRY FLAG will be set.
32EE
Loop back to 32E6H if the count is bad (i.e., the pulse width was too short).
32F0
CP 3EH
Now we need to find out if the count is LESS THAN the upper limit. To do this, we compare Register A with 3EH to see if the pulse width was too long. Results:
  • If Register A equals 3EH, the Z FLAG is set.
  • If A < 3EH, the CARRY FLAG will be set
  • if A >= 3EH, the NO CARRY FLAG will be set.
32F2
Loop back to 32E6H if the count is bad (i.e., pulse width was too long).
32F4
Loop back to 32E8H and keep looping until we get 128 good counts.
32F6
“EDGE”
LD HL,0000H
Load H with 00H and L with 00H in one instruction.
32F9
LD B,40H
Set up a loop of 40H (Decimal: 64) to try to read 64 bits.
32FB
“EDGE1”
First, get a dummy bit to help synchronize data. To do so we GOSUB to 3350H to READ a BIT from Cassette and poll for a BREAK keypress.
32FE
GOSUB to 3350H to READ a BIT from Cassette and poll for a BREAK keypress.
3301
LD D,C
Save the bit into Register D.
3302
We need a second count so GOSUB to 3350H to READ a BIT from Cassette and poll for a BREAK keypress.
3305
LD A,D
Save the first count to the A Register.
3306
SUB C
Subtract C (new count) from A (old count).
3307
If the difference is negative then we need to NEG it, so JUMP to 330BH if the result is positive (i.e., if the carry flag is NOT set).
3309
NEG
LET A = 0 – A to make A a positive number.
330B
“EDGE2”
CP 0DH
Compare the difference in counts against 0DH. Results:
  • If Register A equals 0DH, the Z FLAG is set.
  • If A < 0DH, the CARRY FLAG will be set
  • if A >= 0DH, the NO CARRY FLAG will be set.
330D
If the CARRY FLAG is set (i.e., A < 0DH), then we need to INCrement the FALLING EDGE count so JUMP to 3314H which will bump L and continue this 64 bit loop.
330F
“INCH”
INC H
Otherwise, we need to INCrement the RISING EDGE count so bump H.
3310
Loop back to 32FBH and keep looping until Register B = 0 (i.e., 64 bits read).
3312
JUMP to 3317H to check to see the results of the count.

3314H – CASSETTE – This will increment the FALLING EDGE count and toss us right back into the prior routine.

3314
“INCL”
INC L
Increment the FALLING EDGE count.
3315
Re-enter the 64 cycle Loop at 32FBH.

3317H – CASSETTE – The continues from 3312H. No idea why Frank put the prior 2 instructions right where they are – it created this extra JUMP.

3317
CHEZK
LD A,40H
Load A with 40H (64) as the compare count.
3319
CP H
Compare Register A with Register H to see if we had 64 counts in H. Results: If Register A equals Register H, the Z FLAG will be set.
331A
If Register A = Register H then we will be working with RISING EDGE, so then JUMP FORWARD to 3326H.
331C
CP L
Compare Register A with Register L to see if we had 64 counts in L. Results: If Register A equals Register L, the Z FLAG will be set.
331D
If Register A does not equal Register L, JUMP BACK to 32F6H as we had neither a RISING EDGE or FALLING EDGE count of 64.
331F
“SETF”
LD A,02H
If we are here, then the count equaled Register L, meaning we will be working with a FALLING EDGE, so set the interrupt mask for a FALLING EDGE by loading A with 2.
3321
OUTput the value held in Register A (i.e., 2) to Port E0H.
NOTE: Port E0H is the maskable interrupt latch, which directs jumps.
Jump Table:
  • xxxxxxx1 jumps to 3365H
  • xxxxxx1x jumps to 3369H
  • xxxxx1xx jumps to 4046H
  • xxxx1xxx jumps to 403DH
  • xxx1xxxx jumps to 4206H
  • xx1xxxxx jumps to 4209H
  • x1xxxxxx jumps to 44040H
  • 1xxxxxxx jumps to 44043H
3323
To avoid a bad data read, GOSUB to 3350H to READ a BIT from Cassette and poll for a BREAK keypress.
3326
“SETFR”
LD D,00H
Zero out Register D.
3328
“FDSYNC”
Start looking for a SYNC BYTE by GOSUBing to 3350H to READ a BIT from Cassette and poll for a BREAK keypress.
332B
GOSUB to 31F3H to convert to a data bit / check for a data error.
332E
LD A,D
Load A with D (the read byte).
332F
CP 7FH
We need to see if that byte was a SYNC BIT (07FH) so compare Register A with 7FH (i.e., the MARKER byte). Results: If Register A equals 7FH, the Z FLAG is set; otherwise the NZ Flag is set.
3331
If the byte was NOT a SYNC byte, JUMP back to 3328H to read again until a SYNC byte shows up.
3333
If we are here, we finally got a SYNC byte, so JUMP BACK to 32A1H to flash the * on the screen and RETurn.

3336H – CASSETTE – This is called to OUTPUT a Bit to Cassette

3336
“BITOUT”
RLC C
We need to shift the bit into CARRY so we rotate C left one bit, copying BIT 7 to the CARRY FLAG and the CARRY FLAG to BIT 0.
3338
If the bit was 0, JUMP forward 2 instructions to 333FH to output a “0” bit.
333A
“BIT1”
LD DE,1217H
LET Register D = 12 and Register E = 17.
333D
Skip the next instruction to keep Register D = 12 and Register E = 17.

333FH – CASSETTE – This is the middle of the OUTPUT a Bit to Cassette Routine, except it sets DE to 2B2FH, to set the delay for a 0 Bit.

333F
“BIT0”
LD DE,2B2FH
LET Register D = 2B and Register E = 2F.

3342H – CASSETTE – This continues on, either from a JUMP in 333DH (where DE is 1217) or a pass-through (where DE is 2B2F) to continue the “write the bit” routine.

3342
“BIT3”
DEC D
Decrement D as DELAY #1.
3343
JUMP back to the prior instruction until D is 0, for a timing for HIGH OUT.
3345
LD A,02H
Load A with a 2 to set up for a write of 0 VOLTS to TAPE.
3347
OUTput the value held in Register A (the LOW PART of the Pulse) to Port FFH.
NOTE: Port FFH is the cassette port. When outputting to FFH, Bits Zero and 1 set to: 00 is .85V, 01 is .46V, and 10 is 0.0V.
3349
“BIT 4”
DEC E
Decrement E as DELAY #2.
334A
JUMP back to the prior instruction until E is 0 (the timing for LOW OUT).
334C
DEC A
Decrement the value held in Register A by 1 (4 CPU Cyles). This replaces a prior instruction here which was a more straightforward LD A,01H (7 CPU Cycles). Regardless, this is in preparation to send to prepare to send 0.85 VOLTS to the Cassette port.
334D
OUTput the value held in Register A (the HIGH PART of the Pulse) to Port FFH.
NOTE: Port FFH is the cassette port. When outputting to FFH, Bits Zero and 1 set to: 00 is .85V, 01 is .46V, and 10 is 0.0V.
334F
RET
RETurn.

3350H – CASSETTE – This is the interruptable pulse length routine. There is no code to actually read a bit here; that is all interrupt based which is why interrupts are enabled.

3350
“SEEK”
EI
Enable Interrupts.
3351
LD C,00H
Clear the counter by setting Register C = 00H.

3353H – 5 Instruction Loop to keep checking for BREAK

3353
“SEEK1”
INC C
Bump Register C to start a count loop.
3354
LD A,(3840H)
Scan for a BREAK by LoaDing A with the contents of memory location 3840H.
3357
AND 04H
Mask A against 0000 0100 to leave only Bit 2 (the BREAK key) active.
3359
Continue the loop if there was no BREAK key.
335B
DI
If we are here, the BREAK key was hit. Disable Interrupts so they don’t interrupt this routine. NOTE: This turns off the clock.

335CH – ABORT- This is the tape abort routine. It will place a BK on the screen and return to BASIC at the READY prompt.

335C
“ABORT”
LD HL,4B42H
Load HL with “BK” to set up to display BK over the **.
335F
LD (3C3EH),HL
Put HL (i.e., BK) onto the screen at location 3C3EH (i.e., overlay the BK over the **).
3362
JUMP to 4203H.
NOTE: 4203H JUMPS to 022EH (i.e., READY prompt) and is the break vector for tape and RS-232.

3365H – CASSETTE – This is the cassette interrupt routine. This is a Port E0H Masked Jump. If the MASKABLE INTERRUPT is xxxxxxx1, it jumps here. Register C should be set prior to entry.

In this routine, polling takes 77 t-states for rising edge or 91 for falling edge. The CASINT routine takes 103 or 91 t-states. The total interrupt time is either 180 or 182 T-States (or 6 counts of C)

3365
“RCASIN”
LD E,01H
Load E with 01H (Binary: 0000 0001) for RISING EDGE work.
3367
Skip the next instruction and go straight to the routine.

3369H – This is a jump point to use the common code at 336BH but with E=00H (for FALLING EDGE) instead of E=01H (for RISING EDGE). This is a masked interrupt jump when Port E0H is set with Bit 1 as 1 (i.e., xxxxxx1x).

3369
“FCASIN”
LD E,00H
Load E with 00H (Binary: 0000 0000) for FALLING EDGE work.

336BH – Regardless of whether E is 01 or 00, continue here to process

336B
“CASINT”
LD A,06H
Load A with 6.
336D
ADD A,C
Add the value in register C to the value in register A.
336E
LD C,A
This loads 6 to correct the count.
336F
Clear the Interrupt and Read Data by INput a byte from Port FFH and putting it into Register A.
NOTE: Port FFH is the CASSETTE PORT.

3371H – This part of the routine is also called from 3B4CH and 3B56H.

3371
AND 01H
Mask A against 0000 0001, to have only Bit 0 active to look only at the cassette data bit.
3373
CP E
Compare Register A with Register E (which was the set level) to see if they match. Results: If Register A equals Register E, the Z FLAG is set; otherwise the NZ FLAG is set.
3374
If A does NOT match E then we had a false trigger and we skip the next 3 instructions, restore AF, Enable Interrupts, and continue the loop.
3376
POP AF
Toss away the top entry in the STACK.
3377
POP AF
Restore Register Pair AF (the REMOTE CALLER’S ADDRESS) from the STACK.
3378
RET
RETURN to the caller’s caller.

3379H – If A matches E (the Set Level) continue here

3379
EXTRA
POP AF
Move the value at the top of the STACK to Register Pair AF.
337A
EI
Enable Interrupts so that we can jump back to the 3550H routine.
337B
RET
Return back to the top of the loop.

337CH – PRINT # – This routine is to correct for PRINT # and switch to 500 baud. It checks to see if we have a PRINT # command and, if so, gets the port number, validates that the next character is a , and RETurns. This is a VECTOR from 302AH.

337C
“LK”
LD A,(HL)
Load the value stored in the memory location pointed to by Register Pair HL into Register A.
337D
SUB 23H
Subtract 23H (which is the ASCII value for a # so that we can test to see if the caller was a PRINT # command.
337F
If NZ is set then it wasn’t a # so JUMP to 0253H.

NOTE: 0253H is in the middle of the “Write a Byte to Cassette” Routine. It ends in a RETURN.
3382
If we are here, then it was a #, so GOSUB to 2B01H to get the device number.
3385
RST 08H
,
If we are here then we have PRINT #n and the next character needs to be a , so call RST 08 to check for the next character against a , and generate a SYNTAX ERROR if it wasn’t.

NOTE: The RST 08H routine compares the symbol in the input string pointed to by HL register to the value in the location following the RST 08 call.
  • If there is a match, control is returned to the next execution address (i.e, the RST 08H instruction + 2) with the next symbol in the A Register and HL incremented by one.
  • If the two characters do not match, a syntax error message is given and control returns to the Input Phase).
3386
INC L
BUMP Register L.
Note: This was not in Frank’s proposed update to ROM C.
3387
RET
RETurn to the 500 baud routine.

3388H – KEYBOARD – Frank modified the repeat keyboard routine to take into account whether we are at 2Mhz or 4Mhz and behave identically regardless. This is a VECTOR from 3024H.

In Frank’s notes to his November 1985 ROM, which may or may not match this ROM:

This keyboard driver uses algorithms to calculate character values instead of using lookup tables. It can return all 128 ASCII characters. All characters repeat, including the function keys. Function key and repeat delays are still alterable using the old RAM locations. In addition, the SHIFT+0 function and SHIFT+DOWN ARROW function can be disabled if desired. A fancy drop/assert handler has been added to handle trailing repeats which will allow very reliable keystroke pickup, but WILL show squaring very vividly if a square is generated in the keyboard. But “That’s not a bug, its a hardware problem.” Started Work 27-Jan-85 Frank Durda IV Finished (I Hope) 10-Nov-85 Frank Durda IV

3388
“KEYBD”
LD HL,4036H
Load HL with 4036H to point to an image of the old keyboard.
338B
LD BC,3801H
Load BC with 3801H to point to KEYBOARD ROW 0 on the real keyboard.
338E
LD D,00H
Zero the row counter by LoaDing D with 0.

3390H – This is the beginning of a loop ending in 3399H

3390
“DIFLOP”
LD A,(BC)
Get a row from the real keyboard by LoaDing A with the contents held in (BC).
3391
LD E,A
Save a copy of that keyboard row.
3392
XOR (HL)
XOR (HL) to get the difference between what was set in this row the last time it was read and what is there right now.
3393
If NZ is set then there was a change, so JUMP to 33C2H. 33C2H does a DOS call and then RETURNs if the Z Flag is Set.
3395
INC D
Bump D so that D holds the NEXT keyboard row number.
3396
INC HL
Bump HL so that HL holds the next row in the image of the old keyboard.
3397
RLC C
We need C to point to the next row of keys, so we rotate C left one bit, copying BIT 7 to the CARRY FLAG and the CARRY FLAG to BIT 0.
3399
If the PARITY BIT is set, LOOP BACK to 3390H until all 8 rows are done.
339C
LD HL,41F4H
LET HL = 41F4H which is the value from the old keyboard row 8.
339F
LD A,(BC)
Get a row from the real keyboard and put it into Register A.
33A0
AND 78H
Lose the SHIFT and CTRL keys by masking Register A against 78H (Binary: 1111000) to remove the lowest 3 bits.
33A2
LD E,A
LET Register E = the MASKed version of A.
33A3
XOR (HL)
XOR (HL) to calculate the differences.
33A4
If there was a change, JUMP to the next routine below at 33C2H.

If we are here then there was nothing different in the matrix, so test for repeats. Repeat will behave differently than the old ROM code. In particular, pressing the SHIFT key during a repeat operation will not change the character being repeated. This makes it the same as TRSDOS 6.

33A6
LD A,(4201H)
Load A with the memory contents of 4201H.

NOTE: This is the last key which was pressed.
33A9
INC A
LET A = A + 1.
33AA
RET Z
If the INCrement turned A to Zero (meaning 4201H held FFH and there was no character available) then RETURN.

This is post November 1985 ROM code to adjust for 2Mhz vs 4Mhz and produce the same results regardless.

33AB
GOSUB to 37D4H.

NOTE: The routine at 37D4H tests the active CPU SPEED and sets Z and Register A=0 if we are at 2Mhz and sets NZ/C and Register A=1 if we are at 4Mhz.
33AE
LD HL,41FDH
Let HL = 41FDH.

NOTE: 41FDH is used by the repeating key routine. Saves LSB of keyboard buffer pointer (buffer at 4036H-403CH) when key is found depressed.
33B1
CP (HL)
Compare Register A with the memory contents of (41FDH). If they match, the Z flag will be set.
33B2
JUMP to 3401H to exit out of this routine if the Z (ZERO) Flag is set.

NOTE: 3401H clears Register A and flags, and issues a RETurn.
33B4
LD (HL),A
Load Register A with the memory contents pointed to by Register Pair HL.
33B5
INC HL
LET HL = HL + 1, meaning 41FEH. 41FEH is used by the repeating key routine. Saves contents of current keyboard row when key is found depressed.
33B6
DEC (HL)
Decrement the value held in memory at 41FEH.
33B7
JUMP to 3401H to exit out of this routine if the NZ (ZERO) Flag is set.

NOTE: 3401H clears Register A and flags, and issues a RETurn.
33B9
INC HL
LET HL = HL + 1, meaning 41FFH.

NOTE: 41FFH controls the delay between each repeated character. The higher the value, the longer the delay.
33BA
LD A,(HL)
Load the value stored in the memory location pointed to by Register Pair HL into Register A.
33BB
DEC HL
Decrement the value held in Register Pair HL by 1, meaning 41FEH. 41FEH is used by the repeating key routine. Saves contents of current keyboard row when key is found depressed.
33BC
LD (HL),A
Put the value stored in Register A into 41FEH.
33BD
LD A,(4018H)
Retrieve the value stored in 4018H and put it into Register A.

NOTE: I am going to assume that 4018H is the now-programmable keyboard repeat rate. It used to be the RIGHT SHIFT Toggle.
33C0
CP L
Compare Register A with Register L. Results:
  • If Register A equals Register L, the Z FLAG is set.
  • If A < Register L, the CARRY FLAG will be set
  • if A >= Register L, the NO CARRY FLAG will be set.
33C1
RET
Return.

33C2H – KEYBOARD – Keyboard Repeat Routine – JUMPED from 33A4H meaning something has changed in the keyboard matrix. On entry, D is pointing to a keyboard row.

These next 2 instructions are new to the XDROM. They were NOT in Frank’s proposed revised ROM C, which started with the code at 33C6.

33C2
“CHANGE”
GOSUB to 404CH in DOS.
33C5
RET Z
RETURN if that CALL returns a Z flag.
33C6
PUSH AF
Save the contents of Register Pair AF to the top of the STACK.
33C7
PUSH BC
Save the contents of Register Pair BC (the hardware pointer) to the top of the STACK because BC is used in the ROM DELAY routine.
33C8
LD BC,0152H
LET BC = 0152H. This will be a base value if we are at 2Mhz.
33CB
GOSUB to 37D4H.

NOTE: The routine at 37D4H tests the active CPU SPEED and sets Z and Register A=0 if we are at 2Mhz and sets NZ/C and Register A=1 if we are at 4Mhz.
33CE
If the Z FLAG is set, use the NORMAL keybounce routine by skipping the next instruction.
33D0
LD BC,02A4H
If we’re here then we are at 4Mhz so we need to set BC to a longer delay. This is supposed to be the number in 33C8H x 2.
33D3
“NORMDB”
GOSUB to 0060H which jumps to the delay routine at 01FBH (which uses BC as a loop counter). It RETs when done so it doesn’t come back here.
33D6
POP BC
Get the hardware pointer back into BC from the top of the STACK.
33D7
LD A,(BC)
LET Register A = the memory contents held at (BC), to read the hardware again.
33D8
CP E
Compare Register A with Register E to see if it was the same. If they match, the Z flag will be set and otherwise the NZ flag will be set.
33D9
POP BC
Get the value from the stack (which used to be Register Pair AF) and put it in register pair BC

NOTE: POP does not affect any flag, so the JUMP in the next instruction is based on the test from the previous instruction.
33DA
If Register A does NOT equal Register E then something has changed, but we don’t quite know yet if it is stable enough, so JUMP to 3401H.
33DC
LD A,B
LET Register A = Register B.
33DD
AND E
MASK Register A with Register E to merge with the delta. This will leave Register A with the new (different) bit.
33DE
LD E,A
Save a copy of this to Register A.
33DF
If the MASK returned a NOT Z FLAG, then JUMP forward a few instructions to 33E3H as we have identified the key which is being pressed.
33E1
LD E,B
LET Register E = Register B.
33E2
LD B,A
LET Register B = Register A. A zero indicates a released key.

This next code builds a scan code from the row/column data.

33E3
“PRESS”
LD A,D
Let Register A = Register D.
33E4
RLCA
Rotate the bits in A left (with Bit 7 going into both the CARRY and Bit 0). By doing this, A is multiplied by 2.
33E5
RLCA
Rotate the bits in A left (with Bit 7 going into both the CARRY and Bit 0). By doing this, A is multiplied by 2.
33E6
RLCA
Rotate the bits in A left (with Bit 7 going into both the CARRY and Bit 0). By doing this, A is multiplied by 2.
33E7
LD D,A
LET Register D = the rotated Register A.

Next, translate matrix bit number to binary value using 8 x row + column. This is different from what Frank had in his proposed replacement ROM C. What this used to do was add 8 * row + column, AND that against E to see if the bits match, and if a match exists exit. If no match, increment the row/column combined value, RLC, and loop back.

33E8
LD A,01H
LET Register A = 01H.
33EA
RRC E
We need to test the contents of Bit 0 and Frank decided to use the CARRY flag to do so. With this, he used a RRC command where the contents of E are rotated right one bit position. The contents of bit 0 are copied to the carry flag and bit 7.
33EC
If Bit 0 of Register E was a ONE, then JUMP to 33F2H.
33EE
RLCA
If we are here, Bit 0 of Register E was a ZERO. Rotate the bits in A left (with Bit 7 going into both the CARRY and Bit 0). By doing this, A is multiplied by 2.
33EF
INC D
LET D = D + 1. Note: D was holding the triple rotated-left version of Register A, which was previously Register D.
33F0
JUMP BACK a few instructions to 33EAH to keep testing, and bumping, keyboard rows.

33F2H – KEYBOARD – If Bit 0 of Register E was a ONE at 33ECH, pick up here. At this point D has the scan code and H is 0 if the key is released, and NZ if pressed.

33F2
“COLEXT”
XOR (HL)
Toggle the value in Register A against the value held in the memory location pointed to by Register Pair HL. This will toggle the bit in the stored image and leaves any other bits that dropped or added from being lost just because they are further up the same row.
NOTE: XOR is an exclusive OR, meaning that if the 2 things are the SAME the result is 0, and if the 2 things are DIFFERENT, the result is 1.
33F3
LD (HL),A
Put the contents of Register A into the memory location pointed to by (HL).
33F4
LD A,B
LET Register A = Register B.
33F5
OR A
Since a LD command does not affect the flags, an OR A is necessary to the flags based on the contents Register A.
33F6
If NZ is set, then a key was pressed, so JUMP down to 3403H to translate it.

This code checks the key being released to see if it is the last one pressed down. If not, we can still start/continue repeating. If it is, we stop repeating.

33F8
LD HL,4201H
Put the memory location for the last key pressed into Register Pair HL.
33FB
LD A,(HL)
Get the last key pressed from memory and put it into Register A.
33FC
CP D
Compare Register A with Register D to see if it is the same scan code. If they are identical, the Z flag will be set. Otherwise, the NZ flag will be set.
33FD
If it is not the same scan code, then ignore and jump to 3401H.
33FF
LD (HL),FFH
Put a FFH (Decimal: 256) into (HL) to clear the scan code.

3401H – This is a tiny routine to XOR Register A and Return

3401
XOR A
Zero register A and clear the flags. In exiting the 33F2H routine, this forces a Z status to mean that there was no character.
3402
RET
Return.

3403H – KEYBOARD – Translate keystrokes

3403
“TRANSL”
LD HL,4019H
Load HL with 4019H.

NOTE: 4019H uses bits 0, 4, and 6 to force keyboard state. Bit 0 high makes everything uppercase. Bit 6 handles SHIFT+? – if ON it produces ASCII 10, if OFF no code is produced. Bit 4 handles the BREAK, if LOW, control jumps to 0028H and if HIGH, it returns ASCII CODE 1.
3406
LD A,(HL)
Load Register A with the current caps lock toggle (held at the memory location pointed to by Register Pair HL.).
3407
AND 51H
MASK the CAPS LOCK TOGGLE with 51H (Binary: 0101 0001).
3409
LD B,A
LET Register B = Register A to save the modified Register A.
340A
LD A,(3880H)
Load A with the memory contents of (3880H) (which is the 8th and final row of the Keyboard matrix so as to look for a SHIFT, CAPS LOCK, or CTRL).
340D
BIT 2,A
Check Bit 2 of Register A. Bit 2 of 3880H is the CTRL key.
340F
If the CTRL key is pressed (i.e., Bit 2 is 1), JUMP to 3424H to handle a key that has a CONTROL Code. Frank’s revised keyboard routine uses bit 7 to indicate if CTRL is in use.

We know we don’t have a CONTROL code, so next we test for the SHIFT key.

3411
AND 03H
Mask A against 0000 0011, to have only Bits 0 and 1 active (to isolate SHIFT keys only).

NOTE: Bit 0 would be LEFT SHIFT and Bit 1 would be RIGHT SHIFT.
3413
If NEITHER SHIFT key is pressed (i.e., the MASKED A is 0), JUMP DOWN to 3426H.

We know we have a SHIFTED character …

3415
SET 1,B
SET Bit 1 of Register B to indicate that the SHIFT key is pressed.
3417
BIT 6,B
TEST Bit 6 of Register B. This is to test for the old SHIFTDOWN ARROW combination which was treated like a CTRL.
3419
It Bit 6 of Register B is ON, then we are not emulating a CTRL and so JUMP to 3426H.
341B
LD A,(3840H)
Load A with the contents of 3840H to test for a DOWN ARROW.

NOTE: 3840H holds ENTER (1), CLEAR (2), BREAK (4), RIGHT ARROW (8), DOWN ARROW (16), LEFT ARROW (32), and SPACE (64).
341E
AND 10H
MASK Register A against 10H (Binary: 0001 0000) to check for DOWN ARROW.
3420
If there is no DOWN ARROW, then skip the next instructions which set bits that we don’t need set.
3422
SET 5,B
SET Bit 5 of Register B to show that it was the old indicator for a CTRL.

3424 is also a jump point if we have identified that a CTRL is being done.

3424
“CTRLON”
SET 7,B
SET Bit 7 of Register B to indicate that a CTRL key is being used.

3426H is also a jump point if NO SHIFT keys are pressed.

3426
“NOSHFT”
LD A,D
LET Register A = Register D (the code).
3427
CP 1BH
Check Register A (which was Register D) against 1BH to see if it is in the A-Z range. Results:
  • If Register A equals 1BH, the Z FLAG is set.
  • If A < 1BH, the CARRY FLAG will be set
  • if A >= 1BH, the NO CARRY FLAG will be set.
3429
If Register A (which was Register D) >= 1BH, then we are outside the A-Z range so JUMP to 3445H to deal with that.
342B
BIT 7,B
Test Bit 7 of Register B to see if the CTRL is pressed.
342D
If the CTRL is not pressed, then the scan code is the same as the ASCII code, so JUMP to 3477H.
342F
ADD 40H
ADD 40H (Decimal: 64; or 1 character below A) to Register A to make Register A an ASCII value.
3431
LD C,A
LET Register C = Register A to save that character.
3432
CP 40H
Compare Register A against against “@”. Results: If Register A equals 40H, the Z FLAG is set; otherwise NZ is set.
3434
If Register A equals “@” then JUMP to 343FH to test for SHIFT and CTRL.
3436
LD A,B
LET Register A = Register B so that we can get the SHIFT and CAPS LOCK flags.
3437
AND 03H
Mask A against 0000 0011, to have only Bits 0 and 1 active to see if either SHIFT or CAPS LOCK is on.
3439
If those are 00, then we will leave as lowercase and JUMP to 3476H.
343B
SET 5,C
SET Bit 5 of Register C to shift lower case.
343D
JUMP to 3476H as letters A-Z have now been handled.

343FH – KEYBOARD – Check Bit 1 of Register B, and JUMP to 3477H if it is 0 and 343BH otherwise.

343F
“AT”
BIT 1,B
Test Bit 1 or Register B to see if the SHIFT is down. This command does NOT affect the C FLAG.
3441
If the SHIFT is NOT down, JUMP to 343BH to translate that into a back-tick.
3443
Otherwise, don’t make any adjustments and just consider it an “@” by JUMPing to 3477H.

3445H – KEYBOARD – This routine is to handle keyboard rows 4-5 (meaning 0-9, !-), Shifted-0, * : + ; < , > . ? / = –

3445
“NOTLOW”
CP 3BH
Compare Register A with 3BH to see if it is the CAPS LOCK. If Register A equals 3BH, the Z FLAG is set; otherwise NZ is set.
3447
If Register A equals 3BH then we have a CAPS LOCK so JUMP to 3461H to deal with that.
3449
CP 30H
Compare Register A with 30H to see if the scan code is within the range we want to deal with. Results:
  • If Register A equals ASCII “0”, the Z FLAG is set.
  • If A < ASCII “0”, the CARRY FLAG will be set
  • if A >= ASCII “0”, the NO CARRY FLAG will be set.
344B
If Register A >= 30H, then the data must be from rows 6-7 so JUMP to 348BH.
344D
BIT 7,B
Test Bit 7 of Register B to see if the CTRL key is pressed.
344F
If Bit 7 of Register B is HIGH, then we need to handle special control character translation, so JUMP to 346BH.
3451
ADD 10H
Convert the key pressed to ASCII 0 plus …
3453
CP 3CH
Compare the increased-by-10H Register A with 3CH (ASCII: <) . Results:
  • If Register A equals ASCII <, the Z FLAG is set.
  • If A < ASCII <, the CARRY FLAG will be set
  • if A >= ASCII <, the NO CARRY FLAG will be set.
3455
SHFTF
BIT 1,B
Test Bit 1 or Register B. This command does NOT affect the C FLAG so any test for the C FLAG is still based on the prior instruction.
3457
If Register A >= ASCII <, JUMP to 3468H which further jumps to 3477H if Bit 1 was 1 or 345BH if Bit 1 was 0.
3459
If Bit 1 of Register B is ZERO, JUMP to 3477H.
345B
BACK5
XOR 10H
If we are here then Bit 1 of Register B is ZERO, so XOR Register A against 10H (Binary: 0001 0000) to toggle Bit 4 of Register A.
345D
CP 20H
Compare Register A with ASCII: SPACE . If Register A equals SPACE, the Z FLAG is set; otherwise the NZ FLAG is set.
345F
If the XOR’d Register A is NOT a SPACE then JUMP to 3477H.
3461
CAPS
LD A,01H
Prepare to toggle the CAPS LOCK flag.
3463
XOR (HL)
Toggle the value in Register A against the value held in the memory location pointed to by Register Pair HL.
NOTE: XOR is an exclusive OR, meaning that if the 2 things are the SAME the result is 0, and if the 2 things are DIFFERENT, the result is 1.
3464
LD (HL),A
Put the XOR’d contents of Register A into the memory location pointed to by (HL).
3465
NOCHAR
XOR A
Zero register A and clear the flags to leave with a Z status (no character) since CAPS LOCK does not generate a code.
3466
RET
Return.

3467H – KEYBOARD

3467
“UP5”
If the NZ flag is set, JUMP to 3477H to indicate we are done.
3469
If the NZ flag is NOT set, JUMP to 345BH to go back.

346BH – KEYBOARD – This routine will handle keyboard rows 4-5 but with the CTRL key down

346B
“SPCL45”
LD HL,366CH
LET Register Pair HL = 366CH. This is the special tab location MINUS 20H
346E
ADD A,L
LET Register A = Register A + Register L to get to a starting value.
346F
LD L,A
LET Register L = Register A.
3470
“FKEY”
LD C,(HL)
Load the value stored in the memory location pointed to by Register Pair HL into Register C.
3471
LD A,C
Let Register A = Register C.
3472
INC A
LET A = A + 1 to see if it was a SCREEN PRINT
3473
If Register A is ZERO, then handle the screen print via a JUMP to 378AH.
3476
“CDONE”
LD A,C
If we are here then Register C was not FF and Register A is not Zero. Let Register A = Register C.
3477
“DONE”
LD (4018H),A
Put Register A into 4018H.

NOTE: I am going to assume that 4018H is the now-programmable keyboard repeat rate. It used to be the RIGHT SHIFT Toggle.
347A
LD HL,4201H
Let HL = 4201H

NOTE: This is the last key which was pressed.
347D
LD (HL),D
Store the contents of Register D into the memory location holding the Keyboard Scan Repeat Delay Counter.
347E
DEC HL
Decrement the value held in Register Pair HL by 1, now 4200H.

NOTE: 4200H controls the delay between pressing a key and the generation of the first repeat of that key. The higher the value, the longer the delay.
347F
LD B,(HL)
Load the value stored in the memory location pointed to by Register Pair HL into Register B.
3480
DEC HL
Decrement the value held in Register Pair HL by 1, now 41FFH.

NOTE: 41FFH controls the delay between each repeated character. The higher the value, the longer the delay.
3481
DEC HL
Decrement the value held in Register Pair HL by 1, now 41FEH. Note: 41FEH is used by repeating key routine. Saves contents of current keyboard row when key is found depressed.
3482
LD (HL),B
Put the value stored in Register B into 41FEH (the current keyboard row).
3483
CP 01H
Compare Register A with 01H. If they match, the Z flag is set; otherwise the NZ flag is set.
3485
RET NZ
If A is NOT 01, RETURN.
3486
BIT 4,B
If Register A=01H, then Test Bit 4 of Register B.
3488
RET NZ
If Bit 4 of Register B is 1 then RETURN.
3489
RST 28H
If Register A=01H and Bit 4 of Register B=0 then we must have a BREAK so, call RST 28H to handle the BREAK.
348A
RET
Return.

348BH – KEYBOARD – This routine will handle keyboard row 7

348B
“ROW7”
CP 38H
Compare Register A with 38H to see if this is really keyboard row 8. Results:
  • If Register A equals ASCII 38H, the Z FLAG is set.
  • If A < 38H, the CARRY FLAG will be set
  • if A >= ASCII 38H, the NO CARRY FLAG will be set.
348D
If NC is set, then we have row 8 so JUMP to 34BDH to deal with that.
348F
CP 33H
Compare Register A with 33H to see if it is 30H, 31H, or 32H. If they match, the Z flag will be set; otherwise the NZ flag will be set.
3491
If Register A is NOT 33H, JUMP to 349EH to check for a DOWN ARROW.
3493
LD C,5BH
Prepare to check to see if we have an UP ARROW by putting that value into Register C.
3495
LD A,B
LET A = B to get the bit flags.
3496
AND 82H
MASK Register A against 82H (Binary: 1000 0010) to keep only Bits 1 and 7 to see if SHIFT or CTRL is pressed.
3498
If Bits 1 and 7 of Register A are both 0, then neither SHIFT nor CTRL is pressed, so JUMP to 3476H.
349A
LD A,1BH
Put an ASCII ESCAPE into Register A.
349C
JUMP BACK to 3477H.

349EH – KEYBOARD – Check for DOWN ARROW

This is new to XDROM. The jump here from 348D used to jump straight to the “SPCL45” routine without paying special attention to the down arrow.

349E
JUMP to 346BH if the C (CARRY) Flag is set.
34A0
CP 37H
Compare Register A with ASCII 7. Results:
  • If Register A equals ASCII 7, the Z FLAG is set.
  • If A < ASCII 7, the CARRY FLAG will be set
  • if A >= ASCII 7, the NO CARRY FLAG will be set.
34A2
LD C,20H
LET C = 20H.
34A4
If Register A equals ASCII 7, JUMP to 3476H.
34A6
“NX1”
CP 34H
Compare Register A with 34H to check for DOWN ARROW.
34A8
If Register A is NOT 34H then JUMP to 34B2H because we need to keep trying to figure out what it is.
34AA
BIT 5,B
Test Bit 5 of Register B to see if this is part of a CTRL emulation (SHIFT+DOWN ARROW).
34AC
If NZ is set then ignore the character and JUMP to 3465H (aka NOCHAR).
34AE
LD A,0AH
LET Register A = 0AH to be a LINE FEED.
34B0
JUMP to 3477H (aka DONE).

34B2H – Process a RIGHT ARROW as a TAB

34B2
“QX”
LD C,09H
LET Register C = 09H to test for a TAB key.
34B4
CP 36H
Compare Register A with 36H to test for a RIGHT ARROW. If they match, the Z flag will be set; otherwise the NZ flag will be set.
34B6
If Register A is a 36H then treat that like a TAB and JUMP to 34B9H.
34B8
DEC C
Decrement the value held in Register C by 1 to test for a BACKSPACE.
34B9
“SDONE”
LD A,C
Let Register A = Register C.
34BA
SCF
Set the CARRY FLAG so that shared code will work.
34BB
JUMP to 3455H to shift the values by 10H if the SHIFT key is down.

34BDH – KEYBOARD – Handle Row 8 (which is the F1, F2, F3, and CTRL keys)

This is new to XDROM. TThe old code was the same code, 3 times – compare to something (3C, 3D, 3EH), have HL point to something which increments by 1 each time, and JUMP.

34BD
“ROW8”
CP 3CH
Compare Register A with 3CH to see if we have a F1 key.
34BF
If A < 3CH, JUMP to 3465H to exit out with no character.
34C1
SUB 9DH
LET A = A – 9DH.
34C3
LD L,A
LET Register L = Register A.
34C4
LD H,36H
LET Register H = 36H (to form a MSB of 36xxH).
34C6
LD L,(HL)
LET Register L = the value stored at the memory location pointed to by Register Pair HL.
34C7
LD H,41H
LET Register H = 41H (to form a MSB of 41xxH).
34C9
JUMP to 3470H.

34CBH – Network 4 BOOT – Note: Jumped here from 3503 because the key press was NOT a 3

34CB
“NETWORK3”
POP BC
Adjust the stack by moving the value at the top of the STACK to Register Pair BC.
34CC
LD A,EEH
Prepare to set up the UART by setting Register A = EEH (Binary: 1110 1110).
34CE
OUTput the value held in Register A to Port E9H.
NOTE: Port E9H is the RS-232 Baud Rate Selects and Sense Switches. Outputting to Port E9H will set the Baud Rate for Receive (bits 0-3) and Transmit (Bits 4-7) as follows:
  • 0000 0000 – Baud: 50
  • 0001 0001 – Baud: 75
  • 0010 0010 – Baud: 100
  • 0011 0011 – Baud: 134.5
  • 0100 0100 – Baud: 150
  • 0101 0101 – Baud: 300
  • 0110 0110 – Baud: 600
  • 0111 0111 – Baud: 1,200
  • 1000 1000 – Baud: 1,800
  • 1001 1001 – Baud: 2,000
  • 1010 1010 – Baud: 2,400
  • 1011 1011 – Baud: 3,600
  • 1100 1100 – Baud: 4,800
  • 1101 1101 – Baud: 7,200
  • 1110 1110 – Baud: 9,600
  • 1111 1111 – Baud: 19,200
34D0
“NET31”
We next need to make sure the UART is ready for us. Poll Port E8H and put the result into A.
NOTE: Port E8H is the RS-232 Status Register & Master Reset. Input:
  • Bit 4: Ring Indicator
  • Bit 5: Carrier Detect
  • Bit 6: Data Set Ready
  • Bit 7: Clear to Send
34D2
BIT 6,A
Test Bit 6 of Port E8H. Bit 6 is DATA SET READY.
34D4
If Bit 6 of Port E8H is 0 (i.e., it is not ready), JUMP back 2 instructions and poll the RS-232 again.

If we are here, the UART reported back with DATA SET READY.

34D6
LD A,6CH
We need to set up the UART. LET Register A = 6CH (Binary: 0110 1100). When applied to port EAH this means: Data Terminal Ready, Ready to Send, Send BREAK signal, Parity: Disabled, Stop Bits: 0, Word Length: 7, Parity: Odd.
34D8
OUTput the value held in Register A to Port EAH.
NOTE: Port EAH is the RS-232 UART Control Register/Status Register. For output:
  • Bit 0: Data Terminal Ready
  • Bit 1: Request to End
  • Bit 2: Break
  • Bit 3: Parity Enable
  • Bit 4: Stop Bits
  • Bit 5: Select
  • Bit 6: Word Length
  • Bit 7: Parity (0=Odd, 1=Even)
34DA
“NET32”
Get the modem status by polling Port E8H and put the result into A.
NOTE: Port E8H is the RS-232 Status Register & Master Reset. Input:
  • Bit 4: Ring Indicator
  • Bit 5: Carrier Detect
  • Bit 6: Data Set Ready
  • Bit 7: Clear to Send
34DC
BIT 6,A
Test Bit 6 of Port E8H. Bit 6 is DATA SET READY.
34DE
If Bit 6 of Port E8H is 1, JUMP back 2 instructions and poll the RS-232 again.

If we are here, the UART is ready, so we will request the boot sector and synchronize.

34E0
LD A,0FH
LET Register A = 0FH for a boot request.
34E2
Send the boot request to the UART by OUTputting the value held in Register A to Port EBH.
NOTE: Port EBH is the RS-232C Data Register. It contains the data to be sent to the RS-232C.
34E4
GOSUB to 37DFH, which reads a byte from the RS-232 until a 00H is read.
34E7
GOSUB to 37DFH, which reads a byte from the RS-232 until a 00H is read.

We have no delay here between this and the previous byte. We don’t need it we wait in the meantime for two characters to come in. Since xmit and rcv are at the same rate, we will wait long enough.

34EA
OUTput the value held in Register A to Port EBH.
NOTE: Port EBH is the RS-232C Data Register. It contains the data to be sent to the RS-232C.
34EC
OR A
Set the flags.
34ED
The NZ flag is set then we have an error, so we need to LOOP BACK UP to the top of this routine at 34CBH to try again.

At this point we are all good to get the code.

34EF
LD HL,4300H
Let HL = 4300H which is an I/O buffer.
34F2
“NET34”
GOSUB to 37DFH, which reads a byte from the RS-232 until a 00H is read.
34F5
LD (HL),A
Put the byte which was read from the RS2-232 into the buffer starting at 4300H.
34F6
INC L
Bump L to point to the next empty byte in the buffer.
34F7
If L has not hit 00, then LOOP back to 34F2H and keep reading bytes and filling the buffer.

The 256 byte code has been moved to the 4300H memory block, so now we need to release the UART and jump to the code.

34F9
LD A,6FH
LET A = 6FH (Decimal: 01101111) for UART STATUS and READY TO SEND.
34FB
OUTput the value held in Register A to Port EAH.
NOTE: Port EAH is the RS-232 UART Control Register/Status Register. For output:
  • Bit 0: Data Terminal Ready
  • Bit 1: Request to End
  • Bit 2: Break
  • Bit 3: Parity Enable
  • Bit 4: Stop Bits
  • Bit 5: Select
  • Bit 6: Word Length
  • Bit 7: Parity (0=Odd, 1=Even)
34FD
JP (HL)
Jump to the memory address pointed to by HL.

34FEH – Network 4 Boot – GOSUBed by 3076H and 30E1H

This routine will check for both the ‘4’ key and the BREAK key being pressed. If the ‘4’ key is pressed, the return address that is on the stack will be popped off and processing will jump direct to BOOTNET4 code. If the ‘4’ key is not pressed and the BREAK key is, then this routine will return an NZ to the caller, otherwise a Z will be returned.

34FE
“CK4BRK”
LD A,(3810H)
Put the contents of 3810H into Register A.

NOTE: 3810H is the row of the keyboard matrix holding 0 through 7.
3501
BIT 3,A
Check to see if a 3 was pressed by checking Bit 3 of A. Bit 3 of 3810H will be on only for the 3 key.
3503
JUMP to 34CBH if the NZ (ZERO) Flag is set, meaning, only if the key pressed is NOT a 3 go to the RS-232 boot routine.
3505
AND 10H
MASK A against 10H (Binary: 0001 0000) to leave only Bit 5 so as to check to see if a 4 key was pressed.
3507
If a 4 key was pressed, JUMP to 028DH to check for a BREAK.

NETWORK 4 BOOT ROUTINE:
This routine will execute a boot from an external board called “Omninet”. The process requires the initialization of two new ports and the turning on of the External Bus bit in IOSEL (port 0ECH). The two values send to the two new ports (0D1H and 0D3H) are, respectively, LSB and MSB of Omninet’s address pointer. A third new port (0D0H) is the input (to the Z80A) of the data being read from the memory of the Omninet board. This port has an auto-increment function that increments the address given it in the other two ports.

350A
“NTWK4”
POP BC
Clear the caller’s return address from the stack.
350B
XOR A
Build a pointer to 0800H by zeroing register A.
350C
Output the contents of Register A (00H of 0800H) to Port D1H.

NOTE: Port D1H is the LSB of the Network 4/Omninet.
350E
LD A,08H
Set the rest of the pointer (the MSB) to 08H in Register A.
3510
Output the contents of Register A (08 or 0800H) to Port D3H.

NOTE: Port D3H is the MSB of the Network 4/Omninet. At this point, 0800H was sent to the Network 4/Omninet.
3512
LD BC,00D0H
LET BC = 00D0H. This is setting us up for B=0 as a counter, and C=D0H as a port.

NOTE: Port D0H is the data input port of the Network 4/Omninet.
3515
LD HL,7000H
LET HL = 7000H (Decimal: 28672). This will be the memory block where the Network 4/Omninet will be loaded into RAM.
3518
PUSH HL
Save the contents of Register Pair HL (the start address of the Network 4 RAM block) to the top of the STACK.
3519
“NETLOAD”
INIR
This command takes a byte from Port C (D0H) and writes it to the memory location pointed to by (HL). HL is then incremented, and B is decremented. If B is not zero, this operation is repeated. Interrupts can fire during this command.
351B
DEC A
Decrement the value held in Register A by 1.
351C
If Register A is NOT ZERO then LOOP BACK to 3519H to do another 256 bytes.
351E
LD H,73H
If we are here, then A was ZERO, so lets LET HL = 7300H, which is the memory location of the NETWORK 4 ROM.

In Frank’s prior work, this was LD HL,7000H but this is one byte less.

3520H – This tests to see if the first byte of the RAM address to pass control to (4300H for Floppy or Hard Drive, 7300H for Network 4) is a FDH. If not, we have a problem and RETURN, with NZ set. Ohterwise, Bump the starting memory address beyond that first byte, and JUMP to it to pass control to that BOOT CODE.

This block of code is actually shared between the NETWORK 4 and the Hard Disk Boot!

3520
“MAGIC”
LD A,(HL)
Load the value stored in the memory location pointed to by Register Pair HL into Register A.
3521
CP FDH
Compare Register A with FDH to check for a specific “magic number”. If Register A equals FDH, the Z FLAG is set; and otherwise the NZ FLAG is set.
3523
RET NZ
If A is NOT FDH then RETURN out of the routine to either use the TRSDOS v1.3 Fixed Loader or to simply not boot off of hard disk, whichever called the routine.
3524
POP DE
If we are here then we got the “magin number” so we need to clear the return from the stack.
3525
INC L
LET L = L + 1. This will either set HL to 7301H which is the start of the Network 4 code OR will advance to 4300H in the case of a hard drive boot.
3526
JP (HL)
Jump to the address in HL to run the loader (either the Network 4 loader or the Hard Drive loader).

3527H – DIFFERENCE BETWEEN XDROM AND XROM [Code is never used]


XDROM

3527
LD D,H
3528
LD (HL),D

XROM

3527
LD A,L
3528
LD E,E

3529H – MISC – REAL TIME CLOCK ROUTINE. Blink the cursor and update the clock and calendar

3529
“CLOCKZ”
LD DE,3593H
LET Register Pair DE = 3593H to point to the exit address.
352C
PUSH DE
Save the contents of Register Pair DE to the top of the STACK.
352D
IN A,(ECH)
Reset the clock latch by polling Port ECH and put the results into A.
NOTE: Port ECH is the Miscellaneous Controls port, which covers clock on/off (Bit 0), cassette motor on or off (Bit 1), double size video on or off (Bit 2), and special character set select of Kana or misc (Bit 3). Higher bits are used for the Model 4 only.
352F
LD A,(4022H)
Check to see if the Cursor is on by loading (4022H) into A.

NOTE: 4022H holds the Cursor ON/OFF Flag and will be 0 if the cursor is off, or the the underscore character otherwise.
3532
OR A
Since a LD command does not affect the flags, an OR A is necessary to the flags based on the contents Register A.
3533
If A was zero, then the cursor is off and we do not need to blink it, so JUMP down to 3556H.
3535
LD A,(401CH)
We need to blink th cursor but first check to see if the cursor is to blink by loading (401CH) into A.

NOTE: 401CH holds the Cursor Blink Switch and will be 0 for Blink, and anything else for No Blink.
3538
OR A
Since a LD command does not affect the flags, an OR A is necessary to the flags based on the contents Register A.
3539
If the system is set for BLINK MODE OFF, then JUMP down to 3556H.
353B
LD HL,401AH
Load HL with 401AH.

NOTE: 401AH is the memory location that stores the cursor blink count.
353E
DEC (HL)
Reduce the memory contents of (401AH) by one to lower the blink count.

NOTE: 401AH is the memory location that stores the cursor blink count.
353F
If that reduced count is still not zero, don’t blink and instead JUMP down to 3556H.
3541
LD (HL),07H
If the reduced count is ZERO, then put an 07H into (401AH) to reset the blink counter.
3543
Check to see if we are 2Mhz or 4Mhz by GOSUBing to 36A2H which checks to see if we are 2Mhz or 4Mhz, and if we are at 4Mhz – rotate the memory contents of (HL) left one bit. In either case, then RETurn.
3546
INC HL
Bump HL. This will increase HL from 401AH to 401BH (the cursor blink flag: 0 = Off, Anything Else = On).
3547
LD A,(HL)
Fetch the value of the CURSOR BLINK FLAG (pointed to by HL) into Register A.
3548
XOR 01H
Whatever the CURSOR BLINK FLAG IS (on or off), flip it.
354A
LD (HL),A
Put the toggled cursor blink status into the appropriate memory location.
354B
LD HL,(4020H)
Poll (4020H) and put the result into HL.

NOTE: 4020H holds the current cursor position.
354E
LD A,(4023H)
Fetch the memory contents of (4023H) into Register A.

NOTE: 4023H holds the cursor character.
3551
If the CURSOR CHARACTER is NOT ZERO then skip the next instruction (i.e., don’t change the cursor character to a space).
3553
LD A,20H
LET Register A = the ASCII character for a SPACE (since the CURSOR CHARACTER is ZERO).
3555
“CLOCK4”
LD (HL),A
Put the contents of Register A into the current cursor position (i.e., show the cursor).
3556
“CLOCK5”
LD HL,4216H
LET Register Pair HL = 4216H.

NOTE: 4216H is the heartbeat counter.
3559
DEC (HL)
Decrement the value held in memory at the location pointed to by Register Pair HL (i.e., the heartbeat counter of 30) by 1.
355A
RET NZ
If the reduced heartbeat counter is NOT zero, then just RETURN.
355B
LD (HL),1EH
If we’re here, then the heartbeat counter was reduced to Zero. Put a 1EH (Decimal: 30) into the heatbeat counter.

Note: 30 is for a 60 Hertz system. If this was expressly going into a 50 Hertz system this would be 25 instead of 30.
355D
Adjust for 2Mhz vs 4Mhz by GOSUBing to 36A2H which checks to see if we are 2Mhz or 4Mhz, and if we are at 4Mhz – rotate the memory contents of (HL) left one bit. In either case, then RETurn.
3560
INC HL
Bump HL by 1. HL will now point to 4217H, which is the memory location that holds the SECONDS.
3561
LD DE,0266H
Load DE with 0266H.

NOTE: 0266H points to the TIME DATA of the number of seconds in a minute, the number of minutes in an hour, and the number of hours in a day.
3564
LD B,03H
Load B with a 03H, to set up a loop where we test seconds, minutes, and hours against their maximums.
3566
“TIMLOP”
INC (HL)
Bump the number currently held in HL to increase the number stored there 1.

NOTE: If HL is 4217H then it is seconds, 4218H then it is minutes, 4217H then it is hours.
3567
LD A,(DE)
Fetch the memory contents held at (DE) to get the maximum possible units for each time measure and put the result into A.

NOTE: 0266H points to the TIME DATA of the number of seconds in a minute, 0267H points to the number of mnutes in an hour, and 0268H points to the number of hours in a day.
3568
SUB (HL)
Compare the maximum to what we have by subtracting that maximum from the value pointed at in (HL).
3569
RET NZ
If there is no difference between what we have and the maximum then we are done and just RETURN.
356A
LD (HL),A
If there is a difference, then put a 0 into HL and then continue on to bump the next highest thing (seconds to minutes, minutes to hours, hours to days).
356B
INC HL
Bump HL.

NOTE: If HL is 4217H then it is seconds, 4218H then it is minutes, 4219H then it is hours, and 421AH then it is YEARS.
356C
INC DE
Bump DE.

NOTE: 0266H points to the TIME DATA of the number of seconds in a minute, 0267H points to the number of mnutes in an hour, and 0268H points to the number of hours in a day.
356D
Loop back to 3566H until the loop of 3 has been met, meaning that we have processed seconds, minutes, and hours.

If we are here, then HL is greater than or equal to the year, meaning it is a new day

356F
INC HL
Bump HL one more time, to 421BH for the day.

NOTE: 421BH holds the current DAY portion of the date.
3570
INC (HL)
Bump the DAY portion of the date.
3571
INC HL
Bump HL one more time, to 421CH (which holds the current MONTH portion of the date).
3572
LD A,(HL)
Get the month from memory location 421CH and put it into Register A.
3573
DEC HL
Decrease HL back to to 421BH (to point to the DAY portion of the date).
3574
DEC A
Decrease A by one to correct for 1’s offset.
3575
ADD A,E
LET A = A + E to get an offset to the maximum number of days in a month table.
3576
LD E,A
LET E = A so that DE will point to the specific month’s maximum number of days.
3577
LD A,(DE)
Fetch the number of days in the specific month and put the result into A.
3578
CP (HL)
Now that A holds the maximum number of days in this specific month, ComPare A to it to see if we are at the maximum. Results:
  • If Register A equals that value, the Z FLAG is set.
  • If A < that value, the CARRY FLAG will be set
  • if A >= that value, the NO CARRY FLAG will be set.
3579
RET NC
If all of this shows that the current day of the month is less than the maximum number of days in a month, then RETURN because we don’t need to bump anything more.
357A
LD A,(HL)
Load A with the current day of the month, as HL was pushed back to point to the day of the month at instruction 3571H.
357B
CP 1EH
Compare A to 1E (Decimal: 30) to see if we are at the end of the month. Results:
  • If Register A equals 30 (Decimal), the Z FLAG is set.
  • If A < 30 (Decimal), the CARRY FLAG will be set
  • if A >= 30 (Decimal), the NO CARRY FLAG will be set.
357D
If A >= 30 (Decimal), then JUMP to 3585H to update the MONTH, but not the YEAR.
357F
DEC HL
Decrement the value held in Register Pair HL by 1 to now point to 421AH (the current year).
3580
LD A,(HL)
Fetch the value stored in the memory location pointed to by Register Pair HL (i.e., the current year) into Register A.
3581
INC HL
Bump HL to 421BH (the current DAY).
3582
AND 03H
Mask A (which is holding the year) with 03H (Binary: 00000011) to test for a leap year.
3584
RET Z
If Z is set, then we are not in a leap year (because 04 and higher are turned off).
3585
“DAYRST”
LD (HL),01H
Set to the first day of the month by putting a 1 into the memory location pointed to by HL (which is DAY).
3587
INC HL
Bump HL to 421CH (i.e., the current month).
3588
INC (HL)
Increase whatever is held in (HL) (the current month) by 1.
3589
LD A,(HL)
Put the current MONTH into A.
358A
SUB 0DH
Subract 0DH (Decimal: 13) from A to see if we are past December.
NOTE: This will test against a month 13. If A is < 13, then the CARRY FLAG will be set.
358C
RET C
If it is NOT month 13 then RETURN to skip the next code which increases the YEAR.
358D
LD (HL),01H
If we are here, then MONTH = 13, so set MONTH to 1.
358F
DEC HL
Decrement HL to 421BH (i.e., the current day).
3590
DEC HL
Decrement HL to 421AH (i.e., the current year).
3591
INC (HL)
Increase whatever is held in (HL) (i.e., the current year), by 1.
3592
RET
RETurn.

3593H – CLOCK – Check to see if the clock is on and exit back out if it is off OR the heartbeat shows that the clock was just updated. Pass through otherwise. Note: This routine is not expressly jumped to, but was loaded into the stack at 3529H

3593
“CLKOUT”
LD A,(4210H)
Test to see if the clock is on by putting the contents of memory location 4210H into A.

NOTE: 4210H holds the bit mask for port ECH. Port ECH stores miscellaneous controls.
3596
RRCA
Rotate A to put Bit 0 into the CARRY FLAG.
3597
RET NC
If Bit 0 of (4210H) is ZERO (i.e., the clock is off), then return.
3598
LD A,(4216H)
If we are here, then the clock is on, so load A with the memory contents of (4216H) to see if the clock was just updated.

NOTE: 4216H is the heartbeat/tick counter.
359B
CP 1EH
Compare Register A (i.e., the current heartbeat/tick counter) with 1E. Results: If Register A equals 1E, the Z FLAG is set; otherwise the NZ flag is set.
359D
RET NZ
If the current heartbeat counter is not 1E (which was a new refresh) then it was not just updated, so RETURN.
359E
“CLKTRA”
LD HL,3C35H
If we are here then the current heartbeat counter was just refreshed (to 1E). In that case, we need to display to screen so we set HL to the screen location of 3C35H which is the top line of the screen, 10 characters from the end of first line.

35A1H – CLOCK – Put the Clock 10 characters from the end of the first line. We enter this routine with HL pointing to the screen location 10 characters from the end of the first line. This is a VECTOR from 3036H.

35A1
“RTCLK”
LD DE,4219H
Set DE to 4219H (i.e., the memory location for the current hour).
35A4
LD C,3AH
Put an ASCII : into Register C.
35A6
“RTCLK1”
LD B,03H
Since both the date and the time have 3 sets of numbers (HOUR:MIN:SEC and DAY:MON:YEAR), set a counter of 3.
35A8
“RTLOOP”
LD A,(DE)
Put the memory contents of DE into A. NOTE: This will be HOUR (4219H) on the first pass of the clock routine, MINUTE (4218H) on the second pass of the clock routine, and SECONDS (4219H) on the third pass of the clock routine.
35A9
DEC DE
Decrement DE to point to the next unit to be dealt with.
35AA
LD (HL),2FH
Load the memory location pointed to by (HL) with 2FH which is one character below an ASCII 0).
35AC
“RTLOP1”
INC (HL)
Increase the MSB of the ASCII digit.
35AD
SUB 0AH
LET A = A – 10. If A is < 10, then the CARRY FLAG will be set and if A > 10, the NOT CARRY FLAG will be set.
35AF
Loop back 2 instructions if A > 10 until we get to a correct decimal.
35B1
ADD 3AH
If we are here, then A was less than 10, so we need to increase A by 3A (Binary: 0011 1010) to bring it back to ASCII for the remainder.
35B3
INC HL
Bump HL to point to the next location on the video screen. On the first iteration, this will be the 2nd digit of the HOUR.
35B4
LD (HL),A
Put the ASCII value of the remainder onto the screen. On the first iteration, this will be the 2nd digit of the HOUR.
35B5
INC HL
Bump HL to point to the next location on the video screen. On the first iteration, this will be the 3rd character.
35B6
DEC B
Decrement B to the next unit. On the first iteration, this will move from 3 to 2.
35B7
RET Z
If we have processed all passes in the loop, RETURN.
35B8
LD (HL),C
If we are here, then the routine has not yet looped 3 times, so put a : onto the screen.
35B9
INC HL
Bump HL to point to the next location on the video screen.
35BA
JUMP back to 35A8H to continue the loop.

35BCH – CLOCK – Put the DATE 8 characters from the end of the first line. We enter this routine with HL pointing to the screen location and we just jump into the prior routine with a different pointer to the DATE and a change in the delimeter to a /. This is a VECTOR from 3033H.

35BC
“RTDAT”
LD DE,421CH
Load DE with 421CH (i.e., memory location holding the current month).
35BF
LD C,2FH
Put an ASCII / into Register C.
35C1
Jump into the above routine but with Month, Day, and Year.

35C3H – INTERRUPTS – Maskable Interrupt Handler. This is a VECTOR from 3018H.

35C3
“POLL”
PUSH AF
Save the contents of Register Pair AF to the top of the STACK.
35C4
IN A,(E0H)
Poll Port E0H which is the MASKABLE INTERRUPT LATCH and put the results (the interrupt register) into A.
35C6
RRA
We need to test Bit 0, so just rotate the contents of A one bit to the right, with the contents of the carry bit being moved to bit 7 and the contents of bit 0 being moved to the carry bit. If Bit 0 is low then NC will be set.
35C7
If Bit 0 is 0 then JUMP to 3365H (which is a Port E0H Masked Jump / cassette routine with E set to HIGH).
35CA
RRA
We need to test Bit 1 so AGAIN rotate the contents of A one bit to the right, with the contents of the carry bit being moved to bit 7 and the contents of bit 0 being moved to the carry bit. If Bit 0 is low then NC will be set.
35CB
If Bit 0 (which is now in the carry) is 0 then JUMP to 3369H (which is a cassette routine with E set to LOW).
35CE
PUSH BC
Save the contents of Register Pair BC to the top of the STACK.
35CF
PUSH DE
Save the contents of Register Pair DE to the STACK.
35D0
PUSH HL
Save the contents of Register Pair HL to the STACK.
35D1
PUSH IX
Save the contents of Register Pair IX to the STACK.
35D3
PUSH IY
Save the contents of Register Pair IY to the STACK.
35D5
RRA
Test Bit 2 so AGAIN rotate the contents of A one bit to the right, with the contents of the carry bit being moved to bit 7 and the contents of bit 0 being moved to the carry bit. If Bit 0 is low then NC will be set.
35D6
PUSH AF
Save the contents of Register Pair AF to the STACK.
35D7
If Bit 0 is 0 then GOSUB to 4046H.
NOTE: 4046H is Interrupt Vector 2 (Real Time Clock Pulse) and contains a JUMP to “CLKZZ”. It is commonly used by the clock.
35DA
POP AF
Move the value at the top of the STACK to Register Pair AF.
35DB
RRA
Test bit 3 so AGAIN rotate the contents of A one bit to the right, with the contents of the carry bit being moved to bit 7 and the contents of bit 0 being moved to the carry bit. If Bit 0 is low then NC will be set.
35DC
PUSH AF
Save the contents of Register Pair AF to the STACK.
35DD
If Bit 0 is low then GOSUB to 403DH.
NOTE: 403DH is Interrupt Vector 3 (I/O Extension Bus) and contains a JUMP to IODRVZ.
35E0
POP AF
Move the value at the top of the STACK to Register Pair AF.
35E1
RRA
Test bit 4 so AGAIN rotate the contents of A one bit to the right, with the contents of the carry bit being moved to bit 7 and the contents of bit 0 being moved to the carry bit. If Bit 0 is low then NC will be set.
35E2
PUSH AF
Save the contents of Register Pair AF to the STACK.
35E3
If Bit 0 is low then GOSUB to 4206H.
NOTE: 4206H is Interrupt Vector 4 (RS-232 Received Character) and contains a JUMP to RSI11.
35E6
POP AF
Move the value at the top of the STACK to Register Pair AF.
35E7
RRA
Test bit 5 so AGAIN rotate the contents of A one bit to the right, with the contents of the carry bit being moved to bit 7 and the contents of bit 0 being moved to the carry bit. If Bit 0 is low then NC will be set.
35E8
PUSH AF
Save the contents of Register Pair AF to the STACK.
35E9
If Bit 0 is low then GOSUB to 4209H.
NOTE: 4209H is Interrupt Vector 5 (RS-232 Transmit Butter Empty) and contains a JUMP to RSO11.
35EC
POP AF
Move the value at the top of the STACK to Register Pair AF.
35ED
RRA
Test bit 6 so AGAIN rotate the contents of A one bit to the right, with the contents of the carry bit being moved to bit 7 and the contents of bit 0 being moved to the carry bit. If Bit 0 is low then NC will be set.
35EE
PUSH AF
Save the contents of Register Pair AF to the STACK.
35EF
If Bit 0 is low then JUMP to 4040H.
NOTE: 4040H is Interrupt Vector 6 (RS-232 Error Detected) and contains a JUMP to POL6ZZ.
35F2
POP AF
Move the value at the top of the STACK to Register Pair AF.
35F3
RRA
Test bit 7 so AGAIN rotate the contents of A one bit to the right, with the contents of the carry bit being moved to bit 7 and the contents of bit 0 being moved to the carry bit. If Bit 0 is low then NC will be set.
35F4
If Bit 0 (which was BIT 7) is low then JUMP to 4043H.
NOTE: 4043H is Interrupt Vector 7 (Undefined) and contains a JUMP to POL7ZZ.
35F7
POP IY
Move the value at the top of the STACK to Register Pair IY.
35F9
POP IX
Move the value at the top of the STACK to Register Pair IX.
35FB
POP HL
Move the value at the top of the STACK to Register Pair HL.
35FC
POP DE
Move the value at the top of the STACK to Register Pair DE.
35FD
POP BC
Move the value at the top of the STACK to Register Pair BC.
35FE
POP AF
Move the value at the top of the STACK to Register Pair AF.
35FF
EI
Enable Interrupts.
3600
“POLOUT”
RET
Return.

3601H – RS-232 – RS-232 Initialization. This is a VECTOR from 301BH.

3601
“RSINIT”
DI
Disable Interrupts so they don’t interrupt this routine. NOTE: This turns off the clock.
3602
IN A,(EAH)
Read the UART by polling Port EAH into A.
NOTE: Port EAH is the RS-232 UART Control Register/Status Register. Input:
  • Bit 3: Parity error (1=True)
  • Bit 4: Framing Error (1=True)
  • Bit 5: Overrun (1=True)
  • Bit 6: Data Sent (1=True)
  • Bit 7: Data Ready (1=True)
3604
INC A
INCrement Register A by 1 which means that any test for Z was really to see if Register A held FFH to identify if the RS-232 exists.
3605
If the RS-232 does NOT exist, JUMP down to the next routine at 362DH (“RSOFF”) to zero out all of the RS-232 Ports.
3607
Reset the RS-232 by OUTputting the value held in Register A to Port E8H.
NOTE: Port E8H is the RS-232 Status Register & Master Reset. Outputting ANYTHING to Port E8H resets the RS-232.
3609
LD A,(IX+03H)
Fetch the BAUD RATE CODE from the memory location 41F8H and put it into Register A.
360C
Set the BAUD RATE by OUTputting the value held in Register A to Port E9H.
NOTE: Port E9H is the RS-232 Baud Rate Selects and Sense Switches. Outputting to Port E9H will set the Baud Rate for Receive (bits 0-3) and Transmit (Bits 4-7) as follows:
  • 0000 0000 – Baud: 50
  • 0001 0001 – Baud: 75
  • 0010 0010 – Baud: 100
  • 0011 0011 – Baud: 134.5
  • 0100 0100 – Baud: 150
  • 0101 0101 – Baud: 300
  • 0110 0110 – Baud: 600
  • 0111 0111 – Baud: 1,200
  • 1000 1000 – Baud: 1,800
  • 1001 1001 – Baud: 2,000
  • 1010 1010 – Baud: 2,400
  • 1011 1011 – Baud: 3,600
  • 1100 1100 – Baud: 4,800
  • 1101 1101 – Baud: 7,200
  • 1110 1110 – Baud: 9,600
  • 1111 1111 – Baud: 19,200
360E
LD A,(IX+04H)
Fetch the CONFIGURATION CODE from the memory location 41F9H and put it into into A.
3611
OR A
Since a LD command does not affect the flags, an OR A is necessary to the flags based on the contents Register A.
3612
If the RS-232 is off then the CONFIGURATION CODE A will be 0. If so, JUMP down to the next routine at 362DH to zero out all of the RS-232 Ports.
3614
OUTput the value held in Register A to Port EAH.
NOTE: Port EAH is the RS-232 UART Control Register/Status Register. For output:
  • Bit 0: Data Terminal Ready
  • Bit 1: Request to End
  • Bit 2: Break
  • Bit 3: Parity Enable
  • Bit 4: Stop Bits
  • Bit 5: Select
  • Bit 6: Word Length
  • Bit 7: Parity (0=Odd, 1=Even)
3616
GOSUB to 3635H to Clear RI/RO DCBs.
3619
LD A,(IX+05H)
Fetch the WAIT FLAG from the memory location 41FAH and put it into into A.
361C
OR A
Since a LD command does not affect the flags, an OR A is necessary to the flags based on the contents Register A.
361D
If the WAIT SWITCH was ZERO, skip the next instruction (which sets Register A to 02H) and pick up at 3621H.
361F
LD A,02H
Set Bit 1 on by setting A = 02H (Binary: 0000 0010).
3621
“RSN00”
OR 04H
OR Register A against 04H (Binary: 0000 0100) to turn on Bit 2.
3623
LD (41E9H),A
Put the contents of Register A into memory location 41E9H.

NOTE: 41E9H is the RS-232 Input DCB:
  • Bit 2: Driver On/Off
  • Bit 1: Wait/No Wait
3626
LD (41F1H),A
Put the contents of Register A into memory location 41F1H.
NOTE: 41F1H is the RS-232 Output DCB: Bit 2 is DRIVER ON or OFF.
3629
“RSN02”
IN A,(E8H)
Clear any interrupts by polling Port E8H and put the result into A.
NOTE: Port E8H is the RS-232 Status Register & Master Reset. Input:
  • Bit 4: Ring Indicator
  • Bit 5: Carrier Detect
  • Bit 6: Data Set Ready
  • Bit 7: Clear to Send
362B
EI
Enable Interrupts.
362C
RET
Return.

362DH – RS-232 – This will zero out a bunch of RS-232 Related Ports and Memory Addresses. We wind up here if there is no RS-232 or the RS-232 CONFIGURATION CODE is 0.

362D
“RSOFF”
LD BC,04E8H
LET Register B = 04H and LET Register C = E8H. This saves a byte over the actual Model III Code which has those as 2 different instructions. Register B is set to 04 because it will be a counter to all 4 RS-232 ports (E8H-EBH) and Register C is set to E8H as the first of those ports.
3630
Send Register A to the current port. On the first iteration, this is E8H (the RS-232 Status Register & Master Reset). Sending ANYTHING to Port E8H will reset the RS-232.
3632
INC C
INCrement Register C by 1 (to advance to the next port).
3633
Reduce Register B by 1 and then loop back to 3630H until Register B = 0 (i.e., 4 ports, E8H – EBH, which are all the RS-232 Ports).
3635
“CLEAN”
XOR A
Zero register A and clear the flags.
3636
LD H,A
LET Register H = Register A (which is 0).
3637
LD L,A
LET Register L = Register A (which is 0).
3638
LD (41E8H),HL
Put the value stored in Register Pair HL (which is 0) into the memory location 41E8. Note: 41E8 is the Input Buffer of the RS-232 Input DCB.
363B
LD (41EAH),A
Put the value stored in Register A (which is 0) into the memory location 41EAH.
Note: 41EAH is the ERROR FLAG in the RS-232 INPUT DCB. The flag would be:
  • 00 = NO ERROR
  • 01 = NO CHAR IN BUFFER
  • 02 = OVER RUN CONDITION
  • 03 = NO CARRIER PRESENT
  • 04 = NOT ACTIVATED
  • 05 = FRAMING ERROR
  • 06 = PARITY ERROR
363E
LD (41F0H),HL
Put the value stored in Register HL (which is 0) into the memory location 41F0H (the 1 character output buffer for the RS-232 Output DCB) and 41F1H (the RS-232 Output DCB: Bit 2 is DRIVER ON or OFF).
3641
LD (41F2H),A
Put the value stored in Register A (which is 0) into the memory location 41F2H.
Note: 41F2H is the ERROR FLAG in the RS-232 OUTPUT DCB.
3644
JUMP BACK to 3629H to put the value of Port E8H into Register A, Enable Interrupts, and RETURN.

3646H – RS-232 – This is the RS-232 Input Routine. This is a VECTOR from 301EH.

3646
“RSIN”
LD HL,41E8H
Load HL with 41E8H (which is the Input Buffer of the RS-232 Input DCB).
3649
XOR A
Zero register A and clear the flags.
364A
LD (HL),A
Put the contents of Register A into the memory location pointed to by (HL). Since this is also a JUMPED TO instruction, A is not restricted to 0 and HL is not restricted to 41E8H. If this is a pass-through, however, this clears the previous character.
364B
INC HL
INCrement Register Pair HL by 1 to 41E9H.

NOTE: 41E9H is the RS-232 Input DCB:
  • Bit 2: Driver On/Off
  • Bit 1: Wait/No Wait
364C
BIT 2,(HL)
Test Bit 2 of HL. In the case of the first run, HL is 41E9H, so testing Bit 2 will check to see if the RS-232 is active.
364E
RET Z
If the Z flag is set because the RS-232 driver is set to OFF, RETURN.
364F
DEC HL
If we are here, the RS-232 driver was either set to ON or we LOOPed back here from 364CH below. Regardless, decrement the value held in Register Pair HL by 1 to point to 41E8H.
3650
GOSUB to 3663H to check the RS-232 Port for DATA READY and if so, takes in a character from the RS-232.
3653
If the Z FLAG is set (meaning a byte was read from the RS-232 Port), JUMP to 3661H to put it into (HL) and RETURN.
3655
INC HL
INCrement Register Pair HL by 1. On the initial pass of this loop, this will put HL back to 41E9H.
3656
BIT 1,(HL)
Test Bit 1 of 41E9H to see if the RS-232 is active.
NOTE: Bit 1 of 41E9 contains the WAIT/NO WAIT.
3658
RET Z
If the Z flag is set because Bit 1 of 41E9 (WAIT/NO WAIT) is set to 0, RETURN.
3659
If we are here, then Bit 1 of 41E9H was set to 1, so GOSUB to 028DH to check for a BREAK key.
365C
JUMP BACK to 364FH (to keep checking the RS-232) if the Z (ZERO) Flag is set.
365E
JUMP to 4203H if the BREAK was pressed.
NOTE: 4203H JUMPS to 022EH and is the break vector for tape and RS-232.

3661H – RS-232 – On Entry A contains a byte which was read from the RS-232 port. This routine just puts it into (HL) and RETurns.

3661
LD (HL),A
Put the value stored in Register A into the memory location pointed to by Register Pair HL.
3662
RET
RETurn.

3663H – RS-232 – RS-232 Input Routine – This checks for DATA READY and if so, takes in a character from the RS-232. Exits with NC set if DATA NOT READY.

3663
IN A,(EAH)
Poll Port EAH into A.
NOTE: Port EAH is the RS-232 UART Control Register/Status Register. Input:
  • Bit 3: Parity error (1=True)
  • Bit 4: Framing Error (1=True)
  • Bit 5: Overrun (1=True)
  • Bit 6: Data Sent (1=True)
  • Bit 7: Data Ready (1=True)
3665
OR A
Set the flags based on Register A.
3666
RET P
If the PARITY flag is set (meaning there was an even number of bits set), RETurn out of this routine. I will not make believe I know the voodoo going on here, but I can say that it replaced two instructions that would RETurn if Bit 7 (DATA READY) was 0/FALSE.
3667
XOR A
Zero register A and clear the flags.
3668
IN A,(EBH)
Poll Port EBH to A.
NOTE: Port EBH is the RS-232C Data Register. It contains the data received from the RS-232C.
366A
RET
Return.

366BH – RS-232 – This is the RS-232 Output Routine. This is a VECTOR from 3021H.

366B
“RSOUT”
LD HL,41F1H
Point HL to 41F1H, which is the RS-232 Output DCB.
366E
BIT 2,(HL)
Test Bit 2 of 41E9H to see if the RS-232 is active.
NOTE: Bit 2 of 41E9 contains the DRIVER ON/OFF.
3670
RET Z
If the Z flag is set because Bit 2 of the RS-232 Output DCB is set to 0/OFF, then RETURN.
3671
“RSLOOP”
IN A,(EAH)
Since the RS-232 is confirmed active, poll Port EAH into A to get the UART status.
NOTE: Port EAH is the RS-232 UART Control Register/Status Register. Input:
  • Bit 3: Parity error (1=True)
  • Bit 4: Framing Error (1=True)
  • Bit 5: Overrun (1=True)
  • Bit 6: Data Sent (1=True)
  • Bit 7: Data Ready (1=True)
3673
BIT 6,A
Test Bit 6 of Port EAH to see if the transmit buffer is empty.
3675
If so, the JUMP to 3681H to output the byte.
3677
BIT 1,(HL)
If we are here, we did NOT get a READY TO SEND from the RS-232. Test Bit 1 of 41F1H to see the WAIT/NO WAIT status.
NOTE: 41F1H Hholds DRIVER ON/OFF in BIT 2, and WAIT/NO WAIT in BIT 1.
3679
RET Z
If the Z flag is set because we had a NO WAIT, RETURN.
367A
If we are here, then Bit 1 of 41E9H was set to 1, so GOSUB to 028DH to check for a BREAK key.
367D
JUMP back to 3671H to poll again if the Z (ZERO) Flag is set.
367F
Otherwise (i.e., the BREAK was pressed), JUMP to 365EH.
NOTE: 365EH JUMPS to 4203H which JUMPS to 022EH and is the break vector for tape and RS-232.

3681H – RS-232 – If the RS-232 Returned READY TO SEND, then we are here to do the send.

3681
“RSLOP1”
DEC HL
Decrement the value held in Register Pair HL by 1 (so, since there is only 1 jump to this location, from 3675H above, where HL was 41F1H, change HL to 41F0H, which is the RS-232 Output Buffer).
3682
LD A,(HL)
Check to see if there is a character in the buffer by fetching the value stored in the memory location pointed to by Register Pair HL (i.e., 41F0H, which is the RS-232 Output Buffer) into Register A.
3683
OR A
Since a LD command does not affect the flags, an OR A is necessary to the flags based on the contents Register A.
3684
If there is actually a byte in there, SKIP the next instruction and send it out.
3686
LD A,C
If we are here, then there was no byte, so we need one. Put the contents of Register C into Register A.1.
3687
OUTput the value held in Register A to Port EBH.
NOTE: Port EBH is the RS-232C Data Register. It contains the data to be sent to the RS-232C.
3689
LD (HL),00H
Clear the buffer by putting a 00H into the memory location pointed to by HL (i.e., 41F0H).
368B
RET
RETurn.

368CH – This the special keyboard translation for (US ONLY)

368C
“SPCLTB”
7CH
CTRL+0 – Vertical Bar (|)
368D
1BH
CTRL+1 – Escape
368E
1CH
CTRL+2 – FS
368F
1DH
CTRL+3 – GS
3690
1EH
CTRL+4 – RS
3691
7FH
CTRL+5 – Rubout
3692
7EH
CTRL+6 – Tilde
3693
60H
CTRL+7 – Backtick
3694
7BH
CTRL+8 – Left Brace
3695
7DH
CTRL+9 – Right Brace
3696
FFH
CTRL+: – Screen Print Function
3697
5EH
CTRL+; – Carat
3698
5BH
CTRL+, – Left Bracket
3699
5FH
CTRL+- – Underline
369A
5DH
CTRL+. – Right Bracket
369B
5CH
CTRL+/ – Backslash
369C
0DH
ENTER
369D
1FH
CLEAR
369E
01H
BREAK
369F
EBH
36A0
ECH
36A1
F3H

36A2H – This routine checks to see if we are 2Mhz or 4Mhz. If we are at 4Mhz then rotate the memory contents of (HL) left one bit. In either case, then RETurn to caller.

36A2
“MULBY2”
GOSUB to 37D4H to test the active CPU SPEED and sets Z and Register A=0 if we are at 2Mhz and sets NZ/C and Register A=1 if we are at 4Mhz.
36A5
RET Z
If the Z flag is set because we are at 2MHz, RETURN because we don’t need to stall.
36A6
RLC (HL)
If we are at 4Mhz speed, do a RLC (rotate the memory contents) to multiply it by 2.
36A8
RET
RETurn.
36A9
NOP
NO OPERATION.

36AAH – Initial Vectors and DCBs for RAM 4000H-404FH. This is moved to RAM at 30CA

36AA
“RT1”
JP 1C96H
JUMP to 1C96H. This instruction is placed at 4000H-4002H.
36AD
“RT2”
JP 1D78H
JUMP to 1D78H. This instruction is placed at 4003H-4005H.
36B0
“RT3”
JP 1C90H
JUMP to 1C90H. This instruction is placed at 4006H-4008H.
36B3
“RT4”
JP 25D9H
JUMP to 25D9H. This instruction is placed at 4009H-400BH.
36B6
“RT5”
RET
NOP
NOP
Return. This instruction is placed at 400CH-400EH.
36B9
“RT6”
RET
NOP
NOP
Return. This instruction is placed at 400FH-4011H.
36BC
“RT7”
JUMP to 3018H (poll who interrupted). This instruction is placed at 4012H-4014H.
36BF
“KDCB”
DEFB 1
Memory location to store the I/O Type. This memory location is placed at 4015H.
36C0
DEFW 3024H
This is the driver address for the KEYBOARD INPUT VECTOR. This driver address is placed at 4016H-4017H.
36C2
“LASTKEY”
DEFB 0
Memory location to store the Last ASCII Character Scanned. This byte is placed at memory storage location 4018H.
36C3
“SHFLCK”
DEFB 1
Memory location to store the Caps Lock and Model III Emulation. Bit 6 is on for no emulation. LDOS sets this byte to 01h on boot. This byte is placed at memory storage location 4019H.
36C4
“BLKCNT”
DEFB 7
Memory location to store the BLINK COUNTER. This byte is placed at memory storage location 401AH.
36C5
“BLKFLG”
DEFB 0
Memory location to store the CURSOR BLINK ON/OFF Flag. This byte is placed at memory storage location 401BH.
36C6
“BLKON”
DEFB 0
Memory location to store the CURSOR BLINK FLAG. This byte is placed at memory storage location 401CH.
36C7
“DDCB”
DEFB 7
Memory location to store the I/O TYPE 3. This byte is placed at memory storage location 401DH.
36C8
DEFW 0473H
Address of the display driver. This address is placed at 401EH-401FH.
36CA
“CURSOR”
DEFW 3C00H
Memory location to store the cursor location. Default is 3C00H (the first location on screen). This byte is placed at memory storage location 4020H-4021H.
36CC
“CURCAR”
DEFB 0
Memory location to store the CURSOR ON/OFF FLAG. Default is 176. This byte is placed at memory storage location 4022H.
36CD
“CURSCR”
DEFB 176
Memory location to store the DEFAULT CURSOR CHARACTER. Default is 176. This byte is placed at memory storage location 4023H.
36CE
“CURSWT”
DEFB 0
Memory location to store the SPACE COMPRESSION/GRAPHIC FLAG and set for Special Character. This byte is placed at memory storage location 4024H.
36CF
“PDCB”
DEFB 6
Memory location to store the I/O TYPE 2. This byte is placed at memory storage location 4025H.
36D0
DEFW 03C2H
Address of the printer driver. This byte is placed at memory storage location 4026H-4027H.
36D2
“PFORM”
DEFB 67
Memory location to store the max number of lines + 1. This byte is placed at memory storage location 4028H.
36D3
“PLCNT”
DEFB 1
Memory location to store the LINE COUNTER. This byte is placed at memory storage location 4029H.
36D4
“PCCNT”
DEFB 1
Memory location to store the CHARACTER COUNT. This byte is placed at memory storage location 402AH.
36D5
“PCR”
DEFB 255
Memory location to store the USER CR COUNT. This byte is placed at memory storage location 402BH.
36D6
NOP
This is unused by printer because of the removal of the ROUTE routine. This instruction is placed at 402CH.
36D7
“EXITZ”
JP 5000H
JUMP to 5000H. This instruction is placed at 402DH-402FH.
36DA
“ABORTZ”
RST 00H
NOP
NOP
RST 00H. This instruction is placed at 4030H-4032H.
36DD
“CIOEX”
XOR A
RET
NOP
There is no disk drive, so Zero register A and clear the flags. This instruction is placed at 4033H.
36E0
“KBROLT”
DEFW 0
DEFW 0
DEFW 0
DEFB 0
Keyboard Rollover Table. This table is placed at 4034H-403AH.
36E7
“IODRVZ”
JUMP to 3600H. This JUMP is placed at 403BH-403DH.
36EA
“POL6ZZ”
JUMP to 3600H. This JUMP is placed at 403EH-4040H.
36ED
“POL7ZZ”
JUMP to 3600H. This JUMP is placed at 4041H-4043H.
36F0
“CLKZZ”
JUMP to 3529H for Real Time Clock. This JUMP is placed at 4044H-4046H.
36F3
“DOS”
RST 00H
Do a restart until DOS gets here. This instruction is placed at 4044H-4047H.
36F4
00
NO IDEA.
36F5
31 C9 00
NO IDEA.
36F8
00
NO IDEA.

36F9H – The next 63 bytes are moved to 41E5H

36F9
“RIDCB”
DEFB 01H
Memory location to store the I/O TYPE. When dealing with the RS-232, this is DCB+0. This byte is placed at memory storage location 41E5H.
36FA
DEFW 301EH
Address of the INPUT VECTOR LSB and MSB. When dealing with the RS-232, this is DCB+1 (LSB) and DCB+2 (MSB). This address is placed at 41E6H-41E7H.
36FC
“RBUFF”
DEFB 0
Memory location to store the INPUT BUFFER. When dealing with the RS-232, this is DCB+3. This byte is placed at memory storage location 41E8H.
36FD
“RFLAG”
DEFB 0
Memory location to store the INTERNAL FLAGS. When dealing with the RS-232, this is DCB+4. The flag would be:
  • B0 = 0-NO CHAR IN BUFFER, 1-CHAR IN BUFF
  • B1 = 0-NO WAIT MODE, 1-WAIT FOR CHAR MODE
  • B2 = 0-NOT ACTIVE, 1-ACTIVE
  • REST UNUSED

This byte is placed at memory storage location 41E9H.
36FE
“RERROR”
DEFB 0
Memory location to store the ERROR FLAG. When dealing with the RS-232, this is DCB+5. The flag would be:
  • 00 = NO ERROR
  • 01 = NO CHAR IN BUFFER
  • 02 = OVER RUN CONDITION
  • 03 = NO CARRIER PRESENT
  • 04 = NOT ACTIVATED
  • 05 = FRAMING ERROR
  • 06 = PARITY ERROR

This byte is placed at memory storage location 41EAH.

While ROUTE was still present, the next 2 bytes would have been “RI” as part of the RS-232 DCB

36FF
“F1KEY”
DEFB 60H
Memory location to store the F1 Key. Default will be PAUSE. This byte is placed at memory storage location 41EBH.
3700
“F2KEY”
DEFB 1BH
Memory location to store the F2 Key. Default will be ESCAPE. This byte is placed at memory storage location 41ECH.
3701
“RODCB”
DEFB 2
Memory location to store I/O TYPE. Default will be ESCAPE. This byte is placed at memory storage location 41EDH.
3702
DEFW 2
Address of the RS-232 OUTPUT VECTOR. This instruction is placed at 41EEH-41EFH.
3704
“TBUFF”
DEFB 0
Memory location to store OUTPUT BUFFER. This byte is placed at memory storage location 41F0H.
3705
“TFLAG”
DEFB 0
Memory location to store INTERNAL FLAGS. This byte is placed at memory storage location 41F1H.
3706
“TERROR”
DEFB 0
Memory location to store ERROR CODE. This byte is placed at memory storage location 41F2H.
3707
“F3KEY”
DEFB 08H
Memory location to store the F3 Key. Default will be BACKSPACE. This byte is placed at memory storage location 41F3H.
3708
DEFB 0
Memory location to store the NEW 5 KEYS’ ROLL OVER BYTE. This byte is placed at memory storage location 41F4H.
3709
“RNDCB”
DEFB 2
Memory location to store I/O TYPE. When dealing with the RS-232 Initialization, this is DCB+0. This byte is placed at memory storage location 41F5H.
370A
DEFW 301BH
Address of the RS-232 Initialization Vector. When dealing with the RS-232 Initialization, this is DCB+1 (LSB) and DCB+2 (MSB). This address is placed at 41F6H-41F7H.
370C
“BAUD”
DEFB 55H
Memory location to store the USER BAUD RATE. When dealing with the RS-232 Initialization, this is DCB+3. This byte is placed at memory storage location 41F8H.
370D
“UART”
DEFB 6CH
Memory location to store the USER UART CONFIGURATION RATE. When dealing with the RS-232 Initialization, this is DCB+4. This byte is placed at memory storage location 41F9H.
370E
“WAIT”
DEFB FFH
Memory location to store the WAIT/NO WAIT FLAG. When dealing with the RS-232 Initialization, this is DCB+5. This byte is placed at memory storage location 41FAH.

While ROUTE was still present, the next 2 bytes would have been “RN” as part of the RS-232 Initialization DCB

370F
“PRTSW”
DEFB 0
Memory location to store the DAISY WHEEL II SWITCH. This byte is placed at memory storage location 41FBH.
3710
“GRAPHSW”
DEFB 0
Memory location to store the GRAPHICS SWITCH FOR LINE PRINTERS. This byte is placed at memory storage location 41FCH.
3711
DEFB 01
Memory location to store the LSB of keyboard buffer pointer when key is found depressed. This byte is placed at memory storage location 41FDH.
3712
DEFB 00
Memory location to store CONTENTS OF CURRENT KEYBOARD ROW WHEN KEY IS FOUND DEPRESSED. This byte is placed at memory storage location 41FEH.
3713
DEFB 02
Memory location to store DELAY BETWEEN EACH REPEATED CHARACTER. The higher the value, the longer the delay. This byte is placed at memory storage location 41FFH.
3714
DEFB 14
Memory location to store DELAY BETWEEN PRESSING A KEY AND THE GENERATION OF THE FIRST REPEAT OF THAT KEY. The higher the value, the longer the delay. This byte is placed at memory storage location 4200H.
3715
DEFB 00
Memory location to store the LAST KEY WHICH WAS PRESSED. This byte is placed at memory storage location 4201H.
3716
DEFB 00
NO IDEA. This instruction is placed at 4202H.
3717
“READY”
JUMP to 022EH to enable interrupts, show the READY prompt, and RETURN. This instruction is placed at 4203H-4205H.
371A
“RSI11”
JUMP to 3600H. This instruction is placed at 4206H-4208H.
371D
“RSO11”
JUMP to 3600H. This instruction is placed at 4209H-420BH.
3720
“WRILOC”
DEFW 3252H
Address of the Vector for a SLOW cassette write. This address is placed at 420CH-420DH.
3722
“REDLOC”
DEFW 3214H
Address of the Vector for a SLOW cassette read. This address is placed at 420EH-420FH.
3724
“INTMD”
EQU 38H
This is very confusing. Frank notes that in the original ROM this is 30H which causes AUTHOR I to crash. 4210H is the DEFINITELY the bit mask for port ECH. This value is placed at 4210H.
3725
DEFB 03H
Memory location to store the CASSETTE BAUD RATE SELECT (0 = 500 baud, Not 0 = 1500 baud). This value is placed at memory storage location 4211H.
3726
“BLINK”
DEFB 3CH
Memory location to store the CASSETTE BLINKER COUNTER. This byte is placed at memory storage location 4212H.
3727
“MSKSTA”
DEFB 04
Memory location to store the DEFAULT INTERRUPT VECTOR SETTING. This byte is placed at memory storage location 4213H.
3728
“SCRLCT”
DEFB
NO IDEA. This byte is placed at memory storage location 4214H.
3729
“MSKSAV”
DEFB
NO IDEA. This byte is placed at memory storage location 4215H.
372A
“TICKIT”
DEFB 1EH
Memory location to store the HEARTBEAT COUNTER. This byte is placed at memory storage location 4216H.
372B
“SECOND”
DEFB 00
Memory location to store the clock seconds. This byte is placed at memory storage location 4217H.
372C
“MINUTE”
DEFB 00
Memory location to store the clock minutes. This byte is placed at memory storage location 4218H.
372D
“HOUR”
DEFB 00
Memory location to store the clock hours. This byte is placed at memory storage location 4219H.
372E
“YEAR”
DEFB 00
Memory location to store the calendar year. This byte is placed at memory storage location 421AH.
372F
“DAY”
DEFB 00
Memory location to store the calendar day. This byte is placed at memory storage location 421BH.
3730
“MONTH”
DEFB 00
Memory location to store the calendar month. This byte is placed at memory storage location 421CH.
3731
“KEYBX”
DEFB 00
Frank says this should be free. This byte is placed at memory storage location 421DH.
3732
“KEYBY”
DEFW 0000
Frank says this should be free. This byte is placed at memory storage location 421EH-421FH.
3734
“DWIITAB”
DEFW 0000
This is the address of the DAISY WHEEL II TRANSLATION TABLE which is to be supplied when table is loaded. This table is placed at 4220H-4221H.
3736
DEFB 00H
This is moved to 4222H.
3737
DEFB 00H
This is moved to 4223H.
3738
DEFB 0BH
This is moved to 4224H.
3739
DEFB 00H
This is moved to 4225H.
373A
DEFB 12H
This is moved to 4226H.
373B
DEFB 10H
This is moved to 4227H.
373C
DEFB 00H
This is moved to 4228H.
373D
DEFB 15H
This is moved to 4229H.
373E
DEFB 08H
This is moved to 422AH.
373F
DEFB 44H
This is moved to 422BH.
3740
DEFB 40H
This is moved to 422CH.
3741
DEFB 4FH
This is moved to 422DH.

3742H – OTHER – This is a vector to parse whether the current instruction on a the current line is in quotes. This is a VECTOR from 302DH.

3742
“CPATCH”
CP 22H
If the character in register A is not an end of the BASIC line character, then test the character against 22H to see if it is a .
3744
If the character in register A is not a , then JUMP forward a few instructions to 3789H.
3746
LD A,(409FH)
If we are here, then it was a so we need to deal with the KANA by Fetching the memory contents of 409FH into Register A.
NOTE: 409FH is the DATA FLAG.
3749
XOR 01H
XOR A against 1 (Binary: 0000 0001) to flip the quote bit (Bit 0).
NOTE: XOR is an exclusive OR, meaning that if the 2 things are the SAME the result is 0, and if the 2 things are DIFFERENT, the result is 1.
374B
LD (409FH),A
Load memory location 409FH with a the XOR’d results (i.e., with the quote bit flipped).
NOTE: 409FH is the DATA FLAG.
374E
LD A,22H
Load A with 22H, which is a .
3750
“BFCHKC”
CP 3AH
Compare A against 3AH (which is a :) doing A – 3AH. If A < 3AH, for UNSIGNED items, then the C FLAG is set. If A => 3AH, for UNSIGNED items, then the C FLAG is reset. If A = 3AH then the Z flag is set.
3752
If A NOT a “:” then then JUMP to 06AAH.
NOTE: 06AAH is in the middle of the KEYBOARD DRIVER ENTRY ROUTINE.
3755
LD A,(409FH)
Otherwise, Fetch the contents of 409FH into Register A.
NOTE: 409FH is the DATA FLAG.
3758
RRA
We need to put the quote bit into the CARRY FLAG so we do a RRA to Rotate the contents of A one bit to the right, with the contents of the carry bit being moved to bit 7 and the contents of bit 0 being moved to the carry bit. If Bit 0 is low then NC will be set.
3759
If Bit 7 of the DATA FLAG was set (meaning, we are within quotes), then JUMP to 06A8H.
NOTE: 06A8H is in the middle of the KEYBOARD DRIVER ENTRY ROUTINE.
375C
RLA
Reset Register A to what it was by doing a RLA to Rotate A left one bit, with the contents of BIT 7 being put into the CARRY FLAG and the CARRY FLAG are put into BIT 0. This puts the read data into the CARRY FLAG.
375D
JUMP to 06A3H.
NOTE: 06A3H is the REM processor in the middle of the KEYBOARD DRIVER ENTRY ROUTINE.

3760 – I/O Re-Router. Based on the jump table, I suspect IX was set to 4025H

3760
“CRLF”
LD (IX+05H),00H
Clear the character count by putting a 00H at the memory location pointed to by (IX+5), which is the first character of the source.
3764
INC (IX+04H)
INCrement the line count by INCrementing the memory location pointed to by (IX+4), which is the second character of the destination.
3767
LD A,(IX+04H)
Fetch the line count by Fetching the character at the memory location pointed to by (IX+4) which is the second character of the destination and putting it into Register A.
376A
CP (IX+03H)
Check to see if the line counter is beyond the limit by comparing A with the character at the memory location pointed to by (IX+3) which is the first character of the destination.
376D
RET NZ
If the lince counter is ok, then exit via RETurn.
376E
LD (IX+04H),01H
If the line counter is not ok, the clear the line counter by putting a 00H the memory location pointed to by (IX+4), which is the second character of the destination.
3772
RET
RETURN.

3773 – This subroutine is called by the print routine when a 01H or LINE FEED or CARRIAGE RETURN is the current character.

3773
“DWII”
LD A,C
LET Register A = Register C to get the character.
3774
CP E0H
Compare A against E0H (Binary:1110 0000) to see if the character is above the printer table range. Results:
  • If A=E0H it sets the Z FLAG
  • If A<E0H then the CARRY FLAG will be set
  • if A>=E0H then the NO CARRY FLAG will be set.
3776
JP NC,0043H
If A>=E0H, then it is good to go, so JUMP back to 0043H (which USED to be the KBLINE routine, but now just JUMPs to 0434H which is the routine which prints the character held in C).
3779
CP C0H
If we are here then A>=E0H, so lets further test to see if A is “C0H” or higher to see if it is in the special character set. Results:
  • If A=C0H it sets the Z FLAG
  • If A<C0H then the CARRY FLAG will be set
  • if A>=C0H then the NO CARRY FLAG will be set.
377B
JR C,3787H
If A<C0H then JUMP to 3787H to send it directly to the printer.
Note: Frank did this to save a byte; it used to JUMP to 0063H directly.
377D
SUB 0C0H
If we are here then A<E0H, but A=>C0H (i.e., betweem 160 and 191), so SUBTRACT C0H (Decimal: 64; or 1 character below “A”) so it is in offset size.
377F
LD B,00H
LET Register B = 00H.
3781
LD C,A
LET Register C = Register A so that Register Pair BC will have the offset value in the table.
3782
LD HL,(4220H)
LET Register Pair HL to point to the printer table.
3785
ADD HL,BC
LET HL = HL + BC so get to the the specific character in the table.
3786
LD C,(HL)
LET Register C = the contents of the specific character in the table.
3787
“LNGPRT7”
JUMP to 0063H (which jumps to 041FH which is inside the PRINTER ROUTINE; C holds the printable character to be printed as determined by the PRINTER CHARACTER TABLE).

378AH – OTHER – This is the I/O Rerouter Routine (and when the output is screen, it is the Print Screen Routine). This is a VECTOR from 3027H.

378A
“SCPRTICS”
LD HL,3C00H
Load HL with the memory location for the beginning of the video RAM.
378D
“SCPRT1”
LD A,(HL)
Put the character at the screen location stored in HL into A.
378E
RLCA
Test to see if the character on the video screen is above the ASCII range by rotating the bits in A left (with Bit 7 going into both the CARRY and Bit 0). This will double as a test of Bit 7 since that is now sitting in the CARRY.
378F
LD A,2EH
Set A with . just in case we need to overwrite the character. This LD does not change the C FLAG.
3791
If Bit 7 of A was HIGH, then A is a graphics character, so skip 1 instruction so that we keep the . in Register A.
3793
LD A,(HL)
Load Register A with the memory contents pointed to by Register Pair HL.
3794
“OKAY1”
Call the PRINT CHARACTER routine at 003B (which sends the character in the A register to the printer).
3797
INC HL
Bump HL to the next character on the screen.
3798
BIT 6,H
Check the 6th Bit in H to see if we are at memory location 4000H yet.
379A
If we are at 4000H then we are done so, then JUMP to 0214H for a new line and exit (note: this is a JUMP making it an exit).
379D
LD A,L
Test of end of line by loading A with L.
379E
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).
37A0
If that AND turned A to ZERO, then we are at the end of a line, so GOSUB to 0214H for a new line (note: this is a CALL).
37A3
Since we are not at the end of the screen, loop back to 378DH near the top of this routine for the next character.

37A5H – FLOPPY DRIVE – Called from 30A3 and 3185. Sends a RESET to the FDC, waits a DJNZ loop of 10H, and then RETurns

37A5
INTFDC
LD A,D0H
LET Register A = D0H to set up for a FORCE INTERRUPT command.
37A7
“DOFDC”
OUTput the value held in Register A to Port F0H, which sends a RESET to the Floppy Disk Controller and puts it in Mode 1.
NOTE: Port F0H is the FDC Status Register. Outputting 1000 0000 is issuing the command READ SECTOR (100), SINGLE RECORD (0), SIDE 0 (0), NO 15MS DELAY (0), DISABLE SIDE COMPARE (0).

37A9H – This routine delays 10H and RETURNs.

37A9
“HDELY”
PUSH BC
Save the contents of Register Pair BC to the STACK so that we can use Register B as part of a DJNZ delay. This is because the FDC was slow.
37AA
LD B,10H
Let Register B = 10H for a delay.
37AC
Run this instruction until Register B = 0.
37AE
POP BC
Restore Register Pair BC from the Stack.
37AF
RET
RETurn.

37B0H – HARD DRIVE – This SUBROUTINE is called inside the HARD DRIVE routine once the READY status is set. On entry A is either 10H (Binary: 0001 0000; Bit 5 on) or 20H (Binary: 0010 0000; Bit 6 on).

37B0
“HRDCMD”
Issue a command to the Hard Drive Controller by OUTputting A to Port CFH.

NOTE: Port CCH is Register 7 for WD1010 Winchester Disk Controller Chip and is the Hard Disk Error Command Register when used for OUTput. 10H is RESTORE with a 35 millisecond stepping rate. 20H is SCAN ID. 70H is “Seek with a 35 milisecond stepping rate”. 16H is “Restore with a 3 milisecond stepping rate”.
37B2
CPL
Flip the bits in register A. If Register A was 10H then Register A = 1110 1111. If Register A was 20H then Register A = 1101 1111. This is to force bit 7 to ON to let us sync-up
37B3
“HWAITL”
RLCA
We need to check to see if the busy bit (bit 7) is ON, so do a RLCA to put Bit 7 into the CARRY FLAG.
37B4
“HWAIT”
IN A,(CFH)
Get the Hard Disk Controller status by polling Port CFH and putting the result into A.
NOTE: Port CFH is Register 7 for WD1010 Winchester Disk Controller Chip and is the Hard Disk Error Status Register when used for INPut.
37B6
If the CARRY flag is set, then the BUSY bit is still on, so continue waiting by LOOPING back to 37B3H.
37B8
BIT 0,A
Check BIT 0 (the error bit) of Register A.
NOTE: Bit 0 is LOW on Port CFH if there is no error.
37BA
RET Z
If the Z flag is set because CFH returned NO ERROR then, RETURN.

37BBH – Called from 313A (beginning of the Hard Drive test). This is inside the HARD DRIVE routine once the READY status is set. If we are here, we either were called straight away at the top of the Hard Drive routine OR we got an error on the read despite the drive being READY, so we need to reset the controller.

37BB
“HCOLD”
LD A,1CH
LET Register A = 1CH (Binary: 0001 1100) to prepare to do a software reset of the Hard Drive Controller.
37BD
Reset the Hard Drive Controller by OUTputting a 1CH (Binary: 0001 1100) to Port C1H.

NOTE: Port C1H is the hard drive controller board control register. Sending Bit 3 high means ENABLE CONTROLLER and Bit 4 high means RESET CONTROLLER.
37BF
LD A,0CH
LET Register A = 0CH (Binary: 0000 1100) to re-enable the Hard Drive Controller.
37C1
DO a software reset and enable waits by OUTputting a 0CH (Binary: 0000 1100) to Port C1H.

NOTE: Port C1H is the hard drive controller board control register. Sending Bit 3 high means ENABLE CONTROLLER .
37C3
JUMP BACK to 37A9H to do a 10H DJNZ Wait Loop and then RETurn. This is because Western Digital says that you cannot check the status bit after a command for 12 microseconds and cannot check other bits for 28 microseconds afer a command.

37C5H – OTHER – This SUBROUTINE is called during WARM BOOT. It handles setting up the Port 84H Settings.

37C5
“CLRM4S”
LD A,0CH
Let A = 0CH.
37C7
Send 0CH (Binary: 0000 1100) to port F0H.
NOTE: Port F0H is the Floppy Disk Command/Status Register. 0000 1100 is the command to RESTORE (0000), LOAD HEAD AT THE BEGINNING (1), VERIFY ON (1), 6ms STEP (00).
37C9
“CLRM4S”
LD A,80H
Let A = 80H to point to the second page of video memory on a Model 4.
37CB
GOSUB to 37CFH to clear 1K of the screen.
37CE
XOR A
Zero register A to select page 1.

37CFH – A little routine to output A to Port 84 to reset the Model III and jump to clear the screen.

37CF
“CLR”
OUTput Register A to Port 84H, which will have the effect of saying: Model III ROMs Enabled, Video/Keyboard are Model III, Video Mode 64×16, Lower 32K RAM in Bank 0 and Upper 32K RAM in Bank 1, and Video Page Select 0.
NOTE: Port 84H handles miscellaneous memory items. Output:
  • Bits 0-1: Identify the Model of the Computer
  • Bit 2: Video Display Mode [0=64×16, 1=80×24]
  • Bit 3: Reverse Video
  • Bit 4-6: RAM Bank Select
  • Bit 7: Video Page Select [0=Page 0, 1=Page 1]
37D1
JUMP to the Clear the Screen routine and return to caller.

37D4H – This SUBROUTINE tests the active CPU SPEED and sets Z and Register A=0 if we are at 2Mhz and sets NZ/C and Register A=1 if we are at 4Mhz.

37D4
“FASTCK”
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.
37D7
AND 40H
MASK Register A against 40H (Binary: 0100 0000) to leave only Bit 6 live. Bit 6 would be the CPU CLOCK SPEED (0=2mhz, 1=4mhz).
37D9
LD A,(4216H)
Load the value into 4216H. 4216H is a tape system heartbeat counter, and may actually be a spare memory location.
37DC
RET Z
If the Z flag is set because we are in 2Mhz speed, RETURN.
37DD
RRA
If we are here, then A is 1 and we are in 4Mhz speed. This will then set the CARRY FLAG to 1.
37DE
RET
Return.

37DFH – RS-232 – Reads a byte from the RS-232 until a 00H is read.

37DF
GOSUB to 3663H to check the RS-232 Port for DATA READY and if so, takes in a character from the RS-232.
37E2
LOOP back to the prior instruction until a 00H is read from the RS-232.
37E4
RET
RETurn.

37E5H – NOT USED.

37E5
RST 38H
37E6
RST 38H

31A4H – DIFFERENCE BETWEEN XDROM AND XROM [Code is never used]

These are never jumped to.


XDROM

37E7
INC B

XROM

37E7
INC BC

37E8H – NOT USED.

37E8
NOP
37E9
NOP

37EAH – BASIC Command to Get Time and Date – A vector to STRING=DATE$+””+TIME$. This is a VECTOR from 3039H.

37EA
RST 10H
Call the EXAMINE NEXT SYMBOL routine at RST 10H.
NOTE:
  • The RST 10H routine loads the next character from the string pointed to by the HL register into the A-register and clears the CARRY FLAG if it is alphabetic, or sets it if is alphanumeric.
  • Blanks and control codes 09H and 0BH are ignored causing the following character to be loaded and tested.
  • The HL register will be incremented before loading any character therfore on the first call the HL register should contain the string address minus one.
  • The string must be terminated by a byte of zeros.
37EB
PUSH HL
Save the contents of Register Pair HL (the text pointer) to the STACK.
37EC
LD A,11H
Load A with 11H (Decimal: 17).
NOTE: This is to set up for a 17 Byte String – 8 Time, 1 Space, 8 Date).
37EE
Allocate a 17 byte space by GOSUBing to 2857H to allocate A bytes in the temporary string work area.
37F1
LD HL,(40D4H)
Load HL with the memory contents of (40D4H).
NOTE: 40D4 is the string pointer.
37F4
GOSUB to 35BCH.
NOTE: 35BCH populates the DATE$ and puts it on the screen.
37F7
LD (HL),20H
Load the memory location pointed to by HL with a SPACE.
37F9
INC HL
Increment HL to move 1 character over.
37FA
GOSUB to 35A1H.
NOTE: 35A1H populates the TIME$ and puts it on the screen.
37FD
JUMP to 2884H, which is in the middle of the STRING routine, to save the string’s VARPTR as the current result.

END OF REPLACEMENT ROM

What went into this:

In order to do this page I needed to use a few tools:

  • TRDL, to convert a ROM/BIN file to a CMD file, as diassemblers won’t disassemble a ROM/BIN file. The command line was trld file.rom file.cmd. Lawrence Kesteloot’s TRS80-TOOL can do this as well, effective as of v2.4.1, with trs80-tool convert –start 0x1000 in.rom out.cmd.
  • David Goben’s Model I/III/4 Disassembler v1.4 under Model III TRSDOS sending the output to PRINTER. It is the only disassembler which seemed to give me everything needed, including NO labels, no attempts to (mis)identify what might be text, no errors in disassembling (XOR A,A), the hex and ASCII, uniform columns so I could manipulate them easily, etc.
  • TRS80GP because David Goben’s disassembler produced unusable output when I tried it with saving source to disk, but produced wonderful output when sent to the printer; and TRS80GP allows you to save the printer output!.
  • Microsoft Visual Basic 6. Had to write a program to parse the columns and to add HTML cross reference jumps for jumps. I also used my prior disassemblies to populate the comment field. A waste in terms of things like PUSH HL, but a godsend for jumps to code in ROM A and B, or explaining how the C and NC jumps work.

and the following pages to help with the commenting:

/div>