3C00H - Sign-On Screen Image (Video RAM)
The module opens with a block of data that the loader writes directly into the Model I video display, whose RAM begins at 3C00H. This paints the LDOS sign-on screen while the system loads. None of this region is executable; it is character and graphics data. The title text occupies the first display row, the copyright notice the second, and a block-graphics logo spelling "LDOS" with a trademark mark occupies rows five through nine.
3C00
DEFM ' L D O S - THE LOGICAL DISK OPERATING SYSTEM - VER 5.3.1' 20 20 20 4C 20 44 20 4F 20 53 20 2D 20 54 48 45 20 4C 4F 47 49 43 41 4C 20 44 49 53 4B 20 4F 50 45 52 41 54 49 4E 47 20 53 59 53 54 45 4D 20 2D 20 56 45 52 20 35 2E 33 2E 31
Title line written to display row 0. The name LDOS is letter-spaced for emphasis, followed by its expansion "THE LOGICAL DISK OPERATING SYSTEM" and the version string VER 5.3.1. Each byte is a screen character code placed at successive video cells starting at the top-left of the display.
Display cells 3C3AH-3C45H are not written by the module and are left as whatever the screen was cleared to. The next loaded data resumes at 3C46H.
3C46
DEFM 'Copyright 1991 MISOSYS, INC., ALL RIGHTS RESERVED' 43 6F 70 79 72 69 67 68 74 20 31 39 39 31 20 4D 49 53 4F 53 59 53 2C 20 49 4E 43 2E 2C 20 41 4C 4C 20 52 49 47 48 54 53 20 52 45 53 45 52 56 45 44
Copyright notice written to display row 1, beginning at column 6. Identifies this release as the MISOSYS build of LDOS. Byte 6FH onward are the mixed-case characters of the word "Copyright"; the remainder is upper case.
Display cells 3C77H-3D3FH are not written by the module. The block-graphics logo resumes at 3D40H, which is display row 5.
3D40
DEFB 20H,BFH, BFH,20H, ... 20 BF BF 20 20 20 20 20 20 20 20 20 20 20 20 20 20 BF BF 8F 8F 8F 8F 8F 8F AF BD B4 20 20 20 20 20 BF BF 8F 8F 8F 8F 8F 8F 8F 8F BF BF 20 20 20 20 BF BF 8F 8F 8F 8F 8F 8F 8F 8F BF BF 20 54 4D
Display row 5 of the logo. TRS-80 block-graphics characters (codes 80H-BFH) form the tops of the large letters spelling LDOS. The final two bytes 54H 4DH are the ASCII characters T and M, placing a trademark mark to the right of the logo.
3D80
DEFB 20H,BFH, BFH,20H, ... 20 BF BF 20 20 20 20 20 20 20 20 20 20 20 20 20 20 BF BF 20 20 20 20 20 20 20 82 BF BD 20 20 20 20 BF BF 20 20 20 20 20 20 20 20 BF BF 20 20 20 20 BF BF 20 20 20 20 20 20 20 20 20 20 20 20 20
Display row 6 of the logo. The vertical strokes of the letters continue as paired 0BFH block characters, with individual shaping bytes (82H, 0BDH) forming the curve of the letter "D".
3DC0
DEFB 20H,BFH, BFH,20H, ... 20 BF BF 20 20 20 20 20 20 20 20 20 20 20 20 20 20 BF BF 20 20 20 20 20 20 20 20 BF BF 20 20 20 20 BF BF 20 20 20 20 20 20 20 20 BF BF 20 20 20 20 8F 8F 8F 8F 8F 8F 8F 8F 8F 8F BF BF 20 20 20
Display row 7 of the logo. The vertical strokes of the "L", "D", and "O" continue, and the top bar of the "S" is drawn by the run of 8FH block characters near the end of the row.
3E00
DEFB 20H,BFH, BFH,20H, ... 20 BF BF 20 20 20 20 20 20 20 20 20 20 20 20 20 20 BF BF 20 20 20 20 20 20 20 A0 BF 9F 20 20 20 20 BF BF 20 20 20 20 20 20 20 20 BF BF 20 20 20 20 20 20 20 20 20 20 20 20 20 20 BF BF 20 20 20
Display row 8 of the logo. Shaping bytes 0A0H and 9FH form the lower curve of the "D", while the paired 0BFH characters carry the strokes of the "L", "O", and "S" downward.
3E40
DEFB 20H,BFH, BFH,BCH, ... 20 BF BF BC BC BC BC BC BC BC BC BF BF 20 20 20 20 BF BF BC BC BC BC BC BC BE 9F 87 20 20 20 20 20 BF BF BC BC BC BC BC BC BC BC BF BF 20 20 20 20 BF BF BC BC BC BC BC BC BC BC BF BF 20 20 20
Display row 9, the base of the logo. The 0BCH block characters draw the bottom bars of the "L", "D", "O", and "S", closing off each large letter. This is the final row of the sign-on image; loaded data ends at 3E7FH and executable code begins at 400CH.
400CH - RST and DOS Jump Vectors
The Model I Level II ROM redirects three of its restart and interrupt vectors into this fixed low-RAM window so the resident DOS can service them. Each entry is a three-byte jump into the body of SYS0.
400C
The DOS supervisor-call vector. The ROM RST 28H instruction at 0028H jumps here, and this forwards to the RST 28H supervisor-call dispatcher at 4BCDH, which decodes the request code passed in Register A.
400F
The DEBUG vector. The ROM RST 30H instruction jumps here, forwarding to the resident handler at 44B4H.
4012
The maskable-interrupt vector. The ROM RST 38H instruction jumps here on every hardware interrupt, forwarding to the interrupt service routine at 4518H that dispatches the periodic system tasks.
4015H - Device Control Blocks: KIDCB$, DODCB$, PRDCB$ (Keyboard, Video, Printer)
Three eight-byte Device Control Blocks for the standard character devices. Each block is a data structure: byte 0 is the type/attribute byte, bytes 1-2 the driver entry address, bytes 3-5 the driver's working storage, and bytes 6-7 the two-character device name.
Keyboard input DCB (KIDCB$, 4015H), device name KI.
4015
DEFB 01H 01
Byte 0, the type byte 01H, marking this as an input device.
4016
DEFW 03E3H E3 03
Bytes 1-2, the driver entry address: the Model I ROM keyboard-scan routine at 03E3H.
4018
DEFB 00H,00H,00H 00 00 00
Bytes 3-5, the block's driver working storage (cleared at load).
401B
DEFM 'KI' 4B 49
Bytes 6-7, the device name characters K and I.
Video display DCB (DODCB$, 401DH), device name DO.
401D
DEFB 07H 07
Byte 0, the type byte 07H, marking this as an output device.
401E
DEFW 0458H 58 04
Bytes 1-2, the driver entry address: the Model I ROM video routine at 0458H.
4020
DEFW 3C00H 00 3C
Bytes 3-4, the Cursor Position field (CURPOS at 4020H), initialised to 3C00H, the top-left corner of the display.
4022
DEFB 00H 00
Byte 5, the block's remaining working storage.
4023
DEFM 'DO' 44 4F
Bytes 6-7, the device name characters D and O.
Printer DCB (PRDCB$, 4025H), device name PR.
4025
DEFB 06H 06
Byte 0, the type byte 06H, marking this as an output device.
4026
DEFW 058DH 8D 05
Bytes 1-2, the driver entry address: the Model I ROM printer routine at 058DH.
4028
DEFB 42H,00H,00H 42 00 00
Bytes 3-5, the block's driver working storage.
402B
DEFM 'PR' 50 52
Bytes 6-7, the device name characters P and R.
402DH - Exit and Abort Vectors
Two short entry points that return control from a running program to the operating system by issuing a supervisor request through the RST 28H dispatcher.
402D
LD A,93H 3E 93
The Exit To DOS vector, labelled @EXIT. Load request code 93H, the return-to-system function, into Register A.
402F
RST 28H EF
Invoke the supervisor-call dispatcher (through the ROM RST 28H, which vectors to 400CH and on to 4BCDH) to carry out request 93H and return to DOS Ready.
4030
LD A,93H 3E 93
The Abort To DOS vector, labelled @ABORT. Load the same return-to-system request code 93H into Register A. This entry is reached when a program terminates abnormally, so the system can display the "Operation Aborted" message.
4032
RST 28H EF
Invoke the supervisor-call dispatcher to carry out request 93H and return to DOS.
4033H - Device Driver Hook Vector
A single jump vector, labelled @DVRHK, giving a fixed address at which the device-driver linkage routine can be reached.
4033
Jump to the device-driver linkage routine at 44CAH, which walks the driver chain for a requested device.
4036H - Unused Fill
Filler occupying the gap before the next initialized workspace location.
4036-403C
DEFB FFH x 7 FF FF FF FF FF FF FF
Seven unused bytes set to FFH. Not executed.
403EH - Clock Counters and Interrupt Workspace
Sparse locations in the resident workspace. The loader writes only the bytes shown; the surrounding cells are cleared or left for runtime use. This area holds the interrupt tick counter, the software real-time-clock counters, the interrupt status/mask bytes, and the interrupt task-vector table.
403E
DEFB 53H 53
The OSVER$ operating-system version byte (Model I equate OSVER$ = 403EH), initialised to 53H, which encodes LDOS version 5.3. No resident instruction references it directly; it is read as part of the surrounding control area.
403F
DEFB 00H 00
Cleared workspace byte.
4040
DEFB 00H 00
The TIMER$ interrupt tick counter (Model I equate TIMER$ = 4040H). The clock task increments it every interrupt (at 4583H) and masks it to its low three bits to choose a rotating task slot, so it also paces the once-per-eight-ticks slot service.
Addresses 4041H-4043H are not written by the load module. At run time they hold the software real-time clock TIME$ (Model I equate TIME$ = 4041H): 4041H seconds, 4042H minutes, 4043H hours, cascaded by the clock task at 45C1H.
4044
DEFB 00H 00
The DATE$ date field (Model I equate DATE$ = 4044H): 4044H year, 4045H day, 4046H month, used by the start-up date/time entry code (written at 4F2FH, read at 4F76H). The high-memory pointer HIGH$ occupies 4049H just below.
4045
DEFB 00H, 00H, 00H, 00H 00 00 00 00
Cleared workspace bytes accompanying the date/time work byte.
404B
DEFB 00H 00
The INTIM$ interrupt-status save byte (Model I equate INTIM$ = 404BH). The interrupt service routine stores the interrupt-latch reading here (at 4522H) so task code can see which sources fired.
404C
DEFB 00H 00
The INTMSK$ interrupt-enable mask byte (Model I equate INTMSK$ = 404CH). The interrupt routine ANDs it with the status (at 4524H); a set bit enables handling of that source.
The INTVC$ interrupt task-slot vector table (Model I equate INTVC$ = 404DH). The interrupt service routine at 4518H calls each slot in turn on every timer tick.
404D
DEFW 4537H 37 45
Slot 1 defaults to the do-nothing handler at 4537H.
404F
DEFW 4537H 37 45
Slot 2 defaults to the do-nothing handler at 4537H.
4051
DEFW 4537H 37 45
Slot 3 defaults to the do-nothing handler at 4537H.
4053
DEFW 4537H 37 45
Slot 4 defaults to the do-nothing handler at 4537H.
4055
DEFW 4537H 37 45
Slot 5 defaults to the do-nothing handler at 4537H.
4057
DEFW 4537H 37 45
Slot 6 defaults to the do-nothing handler at 4537H.
4059
DEFW 461CH 1C 46
Slot 7 is an active slot pointing to the installed periodic-task handler at 461CH.
405B
DEFW 456FH 6F 45
Slot 8 is an active slot pointing to the installed periodic-task handler at 456FH.
41DEH - Reserved Workspace
Two initialized bytes standing alone in the workspace.
41DE
DEFB 00H,00H 00 00
Reserved workspace, cleared at load time.
4300H - Patchable Hook Stubs (@KITSK, @ICNFG)
Two three-byte slots each initialized so that calling them simply returns. A feature that needs to intercept these points overwrites the RET with a jump at runtime.
4300
RET C9
Self-Modifying Code
First hook stub, the @KITSK keyboard-interrupt-task hook (Model I equate @KITSK = 4300H). As loaded it is a bare RET (C9H) followed by two NOP-space bytes, so the hook does nothing. When an extension installs itself it patches this location with a jump to its own handler.
4301
DEFB 00H,00H 00 00
The two-byte operand space patched with a target address when the hook above is enabled.
4303
RET C9
Self-Modifying Code
Second hook stub, the @ICNFG install-configuration hook (Model I equate @ICNFG = 4303H), identical in form to the first. Defaults to RET and is patched to a jump when its feature is installed.
4304
DEFB 00H,00H 00 00
The two-byte operand space for the second hook.
4308H - Drive State, System Flags, and Stored Addresses
Resident drive and system state. Each cell is identified by the code that reads or writes it. The system flag byte at 430FH is consulted throughout SYS0; 4310H and 4316H hold stored routine addresses rather than loose bytes.
4308
DEFB FFH FF
The LDRV$ current-drive byte (Model I equate LDRV$ = 4308H), read and written by the interrupt-driven drive-service routine at 4750H and 477CH; FFH is its idle value.
4309
DEFB FFH FF
The PDRV$ physical drive-select latch value (Model I equate PDRV$ = 4309H). The FDC code writes the assembled drive-select here (at 45F0H) and re-asserts it to the latch while waiting for the controller (at 4622H).
430A
DEFW 0000H 00 00
The JDCB$ pointer word (Model I equate JDCB$ = 430AH) holding the address of the current file/device control block. The sequential-write code stores a value here as a word (at 49F9H, LD (430AH),DE); SYS4/SYS reads it (LD IX,(430AH)) to identify the object that an error refers to.
430C
DEFB 00H,00H 00 00
The JRET$ workspace word (Model I equate JRET$ = 430CH) holding the address from which the current supervisor service was called. SYS4/SYS reads it (LD DE,(430CH)), backs up three bytes, and prints it as the "Referenced at X'NNNN'" address.
430E
DEFB 00H 00
The OVRLY$ byte (Model I equate OVRLY$ = 430EH), the current overlay number, read and written by the GAT and program-load code (at 4A92H, 4BE1H, 4C03H, 4C0AH).
430F
DEFB 00H 00
The SFLAG$ system flag byte (Model I equate SFLAG$ = 430FH), consulted throughout SYS0 (at 4399H, 4558H, 4C5FH, 4C6BH, 4DCCH, 505AH). Bit 3 gates the periodic timing/blink action tested at 439CH; bit 4 suppresses the interrupt DEBUG-trap diversion tested at 455BH.
4310
DEFW 4876H 76 48
A stored resident routine address, 4876H, in the random-file-access region. Held here for use as an indirect entry point.
4312
DEFB 00H,00H,00H 00 00 00
Cleared workspace bytes.
4315
DEFB C9H C9
DEBUG Trap Arm Flag
A one-byte flag, not executed. It reads C9H as loaded; the DEBUG dispatcher sets it to C3H (a JP opcode) at 4BEAH to arm the interrupt trap, and the interrupt routine tests it against C3H at 4562H and clears it once fired.
4316
DEFW 44B4H B4 44
The DEBUG-trap target address, 44B4H (the DEBUG handler). The interrupt routine loads it at 4568H when the trap fires.
431BH - Prompt and Keyboard Input Routine
Displays a caller-supplied prompt at a caller-supplied screen position, reads a line from the keyboard into the input buffer, and returns with a subroutine ready to convert the typed characters into binary digits. On entry HL holds the screen position and DE points to the prompt string.
431B
LD (4020H),HL 22 20 40
Store the caller-supplied screen position (Register Pair HL) into the Cursor Position field at 4020H (CURPOS) inside the video DCB, so the prompt appears where the caller wants it.
431E
EX DE,HL EB
Exchange DE and HL so that HL now points to the prompt string that was passed in DE.
431F
GOSUB to the display-string routine at 4467H (the @DSPLY vector) to write the prompt to the screen.
4322
LD HL,4E00H 21 00 4E
Point HL to the keyboard line buffer at 4E00H. This is the transient boot region reused as an input buffer once the system is running.
4325
PUSH BC C5
Save Register Pair BC across the keyboard call.
4326
GOSUB to the ROM line-input routine $KEYIN at 0040H, which collects keystrokes into the buffer at (HL) until ENTER, and returns the character count in Register B.
4329
XOR A AF
Set Register A to zero and clear the carry flag.
432A
OR B B0
OR Register B into A to test the character count. If B is zero the Z flag is set.
432B
POP BC C1
Restore Register Pair BC.
432C
RET Z C8
If no characters were entered (Z flag set), return to the caller.
432D
Otherwise JUMP to 4349H to parse the entered characters into their binary field values.
432F
LD A,(HL) 7E
Get Digit Subroutine
Fetch the next character of the input buffer (addressed by HL) into Register A.
4330
INC HL 23
Advance the buffer pointer to the following character.
4331
SUB 30H D6 30
Subtract ASCII 0 (30H) to convert the character to its numeric value.
4333
CP 0AH FE 0A
Compare the result against 10. If the value is 0 through 9 the CARRY flag is set, marking a valid decimal digit; a non-digit clears carry.
4335
RET C9
Return to the caller with the digit value in A and the carry flag reporting validity.
4338H - CONFIG/SYS Filespec String
A constant filename string stored in the workspace and used by the boot process to locate the system configuration file.
4338
DEFM 'CONFIG/SYS.CCC:0' 43 4F 4E 46 49 47 2F 53 59 53 2E 43 43 43 3A 30
The file specification for the LDOS system configuration file, giving filename CONFIG, extension SYS, password field CCC, and drive 0.
4348
DEFB 03H 03
The 03H end-of-string marker used by the filespec and display routines.
4349H - Parse Decimal Fields Routine
Converts the typed input into up to three one-byte binary values, as used for a date (month, day, year) or time (hours, minutes, seconds) entry. Each field is one or two decimal digits, separated by a delimiter, and the input ends at a carriage return.
4349
LD DE,431AH 11 1A 43
Point Register Pair DE to the three-byte result area at 431AH, where the parsed field values are stored.
434C
LD B,03H 06 03
Load Register B with 3, the number of fields to parse.
434E
PUSH DE D5
Field Loop
Save the result pointer while a field is parsed.
434F
GOSUB to the get-digit subroutine at 432FH to read the first digit of the field.
4352
If the character was not a valid digit (NO CARRY), JUMP to 4363H to finish this field.
4354
LD E,A 5F
Save the first digit value in Register E.
4355
RLCA 07
Rotate A left, doubling the digit to twice its value.
4356
RLCA 07
Rotate A left again, giving four times the digit.
4357
ADD A,E 83
Add the saved digit, giving five times the digit.
4358
RLCA 07
Rotate A left once more, giving ten times the digit. This is the tens place of a two-digit value.
4359
LD E,A 5F
Store the tens value back into Register E.
435A
GOSUB to the get-digit subroutine again for the units digit.
435D
If the second character was not a digit (NO CARRY), JUMP to 4363H to finish with the one-digit value.
435F
ADD A,E 83
Add the tens value to the units digit, forming the complete two-digit binary value.
4360
LD E,A 5F
Store the finished value in Register E.
4361
SCF 37
Set the carry flag to signal that a value was successfully parsed.
4362
LD A,E 7B
Move the parsed value into Register A for storage.
4363
POP DE D1
Restore the result pointer into DE.
4364
RET NC D0
If no value was parsed (NO CARRY), return to the caller.
4365
LD (DE),A 12
Store the parsed field value at the result pointer.
4366
DEC B 05
Decrement the count of fields remaining.
4367
SCF 37
Set the carry flag.
4368
RET Z C8
If all three fields have been parsed (B reached zero), return to the caller.
4369
INC DE 13
Advance the result pointer to the next field slot.
436A
LD A,(HL) 7E
Fetch the next input character, which should be the field delimiter.
436B
INC HL 23
Advance the input pointer past the delimiter.
436C
CP C B9
Compare the character against the expected delimiter held in Register C.
436D
RET NZ C0
If the character is not the expected delimiter, return to the caller.
436E
CP 0DH FE 0D
Compare the character against carriage return (0DH).
4370
RET Z C8
If the input ended with a carriage return, return to the caller.
4371
Field Loop
LOOP BACK to 434EH to parse the next field.
4378H - Display Character Output Driver
The video character driver. It restores the character sitting under the cursor, then routes the new character to the appropriate ROM display routine according to whether it is a control code, a printable character, or a graphics code. On entry Register C holds the character and IX points to the video Device Control Block.
4378
LD L,(IX+03H) DD 6E 03
Load the low byte of the current cursor address from offset 3 of the video DCB pointed to by IX.
437B
LD H,(IX+04H) DD 66 04
Load the high byte of the cursor address from offset 4, so HL now points at the current screen cell.
437E
If the carry flag is set on entry (a special-mode request from the caller), JUMP to the ROM video routine at 049AH.
4381
LD A,(IX+05H) DD 7E 05
Load the saved character from offset 5 of the DCB, which holds whatever character was underneath the cursor block.
4384
OR A B7
Test that saved character. If it is zero the Z flag is set.
4385
If there is no saved character (Z flag set), JUMP to 4388H and skip the restore.
4387
LD (HL),A 77
Write the saved character back to the screen at the cursor position, erasing the cursor block.
4388
LD A,C 79
Move the character to be displayed (passed in Register C) into Register A.
4389
CP 20H FE 20
Compare the character against 20H (the space). If it is below 20H the CARRY flag is set, marking a control code.
438B
If the character is a control code (CARRY set), JUMP to the ROM control-code handler at 0506H.
438E
CP 80H FE 80
Compare the character against 80H. If it is 80H or above the NO CARRY flag is set, marking a graphics or special character.
4390
If the character is 80H or above (NO CARRY), JUMP to the ROM graphics-character routine at 04A6H.
4393
Otherwise the character is printable (20H through 7FH), so JUMP to the ROM character-display routine at 047DH to place it on the screen.
4396H - Supervisor Request and Timed Delay Helper
Issues a supervisor request, then conditionally performs a short timed delay based on two system flags. Used to pace a system operation.
4396
LD A,9EH 3E 9E
Load supervisor request code 9EH into Register A.
4398
RST 28H EF
Invoke the supervisor-call dispatcher (through 400CH to 4BCDH) to carry out request 9EH.
4399
LD A,(430FH) 3A 0F 43
Load the system flag byte from 430FH (set in the configuration area at 4308H).
439C
BIT 3,A CB 5F
Test bit 3 of the system flag byte just loaded from 430FH. Bit 3 enables the periodic timing action: when it is set (NZ) the routine performs the flag check and settle delay below; when it is clear the routine skips them. It therefore acts as the flag that turns the timed cursor/heartbeat pacing on or off.
439E
If bit 3 is set (NZ), GOSUB to 43A1H.
43A1
BIT 2,(IY+03H) FD CB 03 56
Test bit 2 of the flag byte at offset 3 of the system control block addressed by Register Pair IY. Bit 2 is the settle-delay gate: when it is clear (Z) the routine calls the delay at 43A8H, and when it is set the delay is skipped, so a set bit means the required settling time has already elapsed.
43A5
If that bit is clear (Z), GOSUB to the delay routine at 43A8H.
43A8
PUSH BC C5
Timed Delay
Save Register Pair BC across the delay.
43A9
LD B,7FH 06 7F
Load Register B with 7FH as the delay loop count.
43AB
GOSUB to the ROM delay routine $DELAY at 0060H, which loops for the count in Register Pair BC.
43AE
POP BC C1
Restore Register Pair BC.
43AF
RET C9
Return to the caller.
43C0H - Additional Device Control Blocks: JLDCB$, SIDCB$, SODCB$ (Job Log, System Input, System Output)
Before this block the load module leaves a gap: addresses 43B0H-43BFH are not written by the module (sixteen bytes left as uninitialised RAM), so nothing is loaded there. Loaded data resumes at 43C0H with three more eight-byte Device Control Blocks. Each DCB has the same layout as the keyboard/video/printer blocks at 4015H: byte 0 is the device type/attribute byte, bytes 1-2 are the driver entry address (low byte first), bytes 3-5 are the driver's working storage, and bytes 6-7 are the two-character device name. These are data structures, not code.
The Job Log DCB (JLDCB$, 43C0H), device name JL
43C0
DEFB 08H 08
Byte 0 is the type/attribute byte 08H (the value LDOS uses to mark this as a system logical device).
43C1
DEFW 0000H 00 00
Bytes 1-2 are the driver entry address, here 0000H because no driver is linked until the log is routed to a destination
43C3
DEFB 00H,00H,00H00 00 00
Bytes 3-5 are zeroed working storage
43C6
DEFM 'JL' 4A 4C
Bytes 6-7 are the name characters J and L
The System Input DCB (SIDCB$, 43C8H), device name SI, the standard input stream
43C8
DEFB 08H 08
Byte 0 is the type/attribute byte 08H.
43C9
DEFW 0000H 00 00
Bytes 1-2 are the driver entry address 0000H (unlinked at load time, so input is unrouted until the system attaches a source).
43CB
DEFB 00H,00H,00H 00 00 00
Bytes 3-5 are zeroed working storage.
43CE
DEFM 'SI' 53 49
Bytes 6-7 are the name characters S and I.
The System Output DCB (SODCB$, 43D0H), device name SO, the standard output stream
43D0
DEFB 08H 08
Byte 0 is the type/attribute byte 08H.
43D1
DEFW 0000H 00 00
Bytes 1-2 are the driver entry address 0000H (unlinked at load time).
43D3
DEFB 00H,00H,00H 00 00 00
Bytes 3-5 are zeroed working storage.
43D6
DEFM 'SO' 53 4F
Bytes 6-7 are the name characters S and O.
4400H - Command Supervisor Entries (@CMD, @CMNDI)
A short supervisor-call entry. It loads a DOS request code into Register A and executes RST 28H, the one-byte restart that the ROM vectors through 0028H to 400CH and on to the supervisor-call dispatcher at 4BCDH. The dispatcher reads Register A to decide which function to run. The first is the @CMD vector (4400H, official LDOS Model I equate); the second, @CMNDI, follows at 4405H.
4400
LD A,93H 3E 93
Load Register A with request code 93H, the return-to-DOS function (the same code the @EXIT and @ABORT vectors use to hand control back to the operating system).
4402
RST 28H EF
Execute the supervisor call. The dispatcher at 4BCDH sees code 93H in Register A and returns the machine to DOS Ready.
4405
LD A,B3H 3E B3
The @CMNDI entry (Model I equate @CMNDI = 4405H). Load Register A with request code B3H, the command-line input/interpret function that fetches the next command line and passes it to the interpreter, dispatched through 4BCDH.
4407
RST 28H EF
Execute the supervisor call for request B3H.
4408
RET Z C8
Return to the caller if the dispatcher left the Z flag set, which for this request signals a zero (success/empty) result.
4409H - Error Display and DEBUG Vectors
The error-message display vector (@ERROR) followed by the DEBUG entry vector (@DEBUG).
4409
PUSH AF F5
The @ERROR entry. Register A holds the DOS error code supplied by the caller; push Register Pair AF so that error code (and the flags) are preserved across the supervisor call that follows.
440A
LD A,96H 3E 96
Load Register A with request code 96H, the error-message display function, which converts the saved error code into its text message or number on screen.
440C
RST 28H EF
Execute the supervisor call so the dispatcher at 4BCDH displays the error.
440D
The @DEBUG entry. JUMP to the DEBUG handler at 44B4H, the same handler reached from the RST 30H restart vector at 400FH.
4410H - Task-Slot Management Vectors
Four jump vectors that install and remove periodic-task handlers in the Task Control Block table at 4500H, the same table the interrupt service routine scans on every timer tick. Each table slot is a two-byte pointer; an empty slot holds the default do-nothing handler at 45B2H. In the Model I LDOS equates these four vectors are @ADTSK (4410H, add a task), @RMTSK (4413H, remove a task by slot), @RPTSK (4416H, replace/install a task at a preset slot) and @KLTSK (4419H, kill the task currently being dispatched); each targets the task-table code at 459DH-45B3H, matching those names exactly.
4410
The @ADTSK (add-task) vector. JUMP to 45A5H, which stores the handler-block pointer supplied by the caller in Register Pair DE into the Task Control Block slot selected by Register A (slot numbers 0 through 11), adding a periodic task the interrupt routine will then call each tick.
4413
The @RMTSK (remove-task-by-slot) vector. JUMP to 45A2H, which first loads Register Pair DE with 45B2H (the default do-nothing handler) and then stores it into the Task Control Block slot selected by Register A, removing whatever task occupied that slot.
4416
The @RPTSK (replace/install-at-preset-slot) vector. JUMP to 45B4H, which reads a Task Control Block slot address from a pointer that is patched into its own LD HL instruction at run time, then stores the caller's handler-block pointer (taken from the stack) into that slot through the shared store code at 45ACH. The patched LD HL operand is self-modifying code.
4419
The @KLTSK (kill-current-task) vector. JUMP to 459DH, which derives the slot index from the dispatcher's saved pointer at 45B5H, loads Register Pair DE with the default handler 45B2H, and stores it into that slot, removing the task that is currently being dispatched.
441CH - Filespec-Syntax Vector and Version Byte
The filespec-syntax supervisor vector (@FSPEC, 441CH) and, at 441FH, the device/DOS flag byte DFLAG$. (The operating-system version byte OSVER$ is at 403EH, not here.)
441C
LD A,C3H 3E C3
The @FSPEC entry. Load Register A with request code C3H, the filespec-syntax-check function, which validates the file specification the caller points to and copies it into a file control block.
441E
RST 28H EF
Execute the supervisor call so the dispatcher at 4BCDH performs the filespec check.
441F
DEFB 00H 00
The DFLAG$ byte (Model I LDOS equate DFLAG$ = 441FH), a device/DOS flag cell in the one-byte gap after the @FSPEC vector; 00H as loaded and set during boot initialisation.
4420H - Supervisor Call Vector Table
The fixed supervisor-call vector table. This is the contract every program and overlay depends on, so these addresses are identical across the Model I DOSes and must never move. Two forms of entry appear. A file-management call that needs a resident routine or a loadable overlay is coded as a request code loaded into Register A followed by RST 28H, which the dispatcher at 4BCDH decodes. A call handled entirely inside SYS0 is coded as a direct jump to its routine. Single pad bytes fill the one-byte gaps left after the shorter request-code entries so that every vector begins on its fixed three-byte boundary.
4420
LD A,A4H 3E A4
The @INIT entry. Load Register A with request code A4H, the function that creates a new directory entry for a file, or opens the entry if the file already exists. The caller supplies the file control block describing the file.
4422
RST 28H EF
Execute the supervisor call so the dispatcher at 4BCDH carries out the @INIT function.
4423
DEFB 20H 20
A byte between the vector entries that also serves as the KFLAG$ key-status flag byte at 4423H (Model I equate KFLAG$ = 4423H), set by the keyboard scan at 4DA6H (bit 0 = BREAK, bit 1 = SHIFT-@, bit 2 = ENTER).
4424
LD A,94H 3E 94
The @OPEN entry. Load Register A with request code 94H, the function that opens an existing file by reading its directory entry into the caller's file control block.
4426
RST 28H EF
Execute the supervisor call to carry out the @OPEN function.
4427
DEFB 00H 00
Pad byte. Not executed.
4428
LD A,95H 3E 95
The @CLOSE entry. Load Register A with request code 95H, the function that closes an open file, flushing any buffered sector to disk and updating the file's directory entry.
442A
RST 28H EF
Execute the supervisor call to carry out the @CLOSE function.
442B
DEFB 00H 00
Pad byte. Not executed.
442C
LD A,9CH 3E 9C
The @KILL entry. Load Register A with request code 9CH, the function that deletes a file, removing its directory entry and releasing its allocated disk granules.
442E
RST 28H EF
Execute the supervisor call to carry out the @KILL function.
442F
DEFB 00H 00
The MFLAG$ byte (Model I equate MFLAG$ = 442FH), a system flag cell occupying the inter-vector gap; 00H as loaded.
4430
The @LOAD entry. JUMP to the program-load routine at 4C77H, which loads an executable file into memory without transferring control to it.
4433
The @RUN entry. JUMP to the load-and-execute routine at 4C5EH, which loads an executable file and then jumps to its transfer address.
4436
The @READ entry. JUMP to the read-logical-record routine at 48ADH, which delivers the next logical record of the open file described by the caller's file control block.
4439
The @WRITE entry. JUMP to the write-logical-record routine at 48CBH, which writes the caller's record as the next logical record of the open file.
443C
The @VER entry. JUMP to the write-with-verify routine at 48E6H, which writes a record and then reads it back to confirm it recorded correctly.
443F
The @REW entry. JUMP to the rewind routine at 4835H, which repositions the file's record pointer back to the first record.
4442
The @POSN entry. JUMP to the position routine at 47CFH, which moves the file's record pointer to a caller-selected record number.
4445
The @BKSP entry. JUMP to the backspace routine at 4816H, which moves the file's record pointer back by one record.
4448
The @PEOF entry. JUMP to the position-to-end-of-file routine at 483CH, which moves the record pointer to the file's end for appending.
444B
The @CKEOF entry (Model I equate @CKEOF = 444BH). JUMP to 4A1AH, in the EOF/extent region, which tests whether the file's record pointer has reached end of file.
444E
The @WEOF entry (Model I equate @WEOF = 444EH). JUMP to 4886H, which writes an end-of-file mark for the open file. (The integer-multiply service @MULT is at 44C1H, not here.)
4451
LD A,A3H 3E A3
Load Register A with request code A3H. Together with the RST 28H at 4453H this forms a supervisor-call stub that issues RST 28H request A3H. Because A3H has bit 7 set, the dispatcher at 4BCDH routes it through the overlay loader at 4BF5H; it is the service that hands a command line (addressed by HL) to DOS for execution, and the SYS7 AUTO command jumps here to re-run the stored auto-execute command. This slot carries no @-symbol of its own in the Model I equates, and it is not the integer-divide entry; the named @DIV vector is the separate resident routine at 44C4H.
4453
RST 28H EF
Execute the supervisor call, passing request code A3H (in Register A) to the RST 28H dispatcher at 4BCDH to process the command line.
4454
The @RREAD entry (Model I equate @RREAD = 4454H). JUMP to 480FH, the re-read-current-record service. (The parameter parser @PARAM is at 4476H, not here.)
4457H - File Access Extension Vectors
Five further file-access vectors continuing the table. The Model I LDOS equates name them @RWRIT (4457H, re-write current record), @LOC (445AH, return current record number), @LOF (445DH, return end-of-file record number), @SKIP (4460H, skip one record) and @DODIR (4463H, directory service, request code AEH). The table then leaves a one-byte pad before the display/print group at 4467H.
4457
The @RWRIT entry (Model I equate @RWRIT = 4457H). JUMP to 499EH, the re-write-current-record service.
445A
The @LOC entry (Model I equate @LOC = 445AH). JUMP to 484DH, which returns the current logical record number of the open file.
445D
The @LOF entry (Model I equate @LOF = 445DH). JUMP to 4878H, which returns the file's end-of-file record number (its length in records).
4460
The @SKIP entry (Model I equate @SKIP = 4460H). JUMP to 47CBH, which advances the file's record pointer past one record.
4463
LD A,AEH 3E AE
The @DODIR entry (Model I equate @DODIR = 4463H). Load Register A with request code AEH, the directory-display/build function. The RST 28H that dispatches it follows at 4465H.
4465
RST 28H EF
Execute the supervisor call to carry out the skip-record function.
4466
DEFB 00H 00
Pad byte before the next vector group. Not executed.
4467H - Display, Print, and File-Length Vectors
The display-string vector (@DSPLY, 4467H), the print-string vector (@PRINT, 446AH), the get/display-time vector (@TIME, 446DH) and get/display-date vector (@DATE, 4470H), then @FEXT (4473H, request D3H), @PARAM (4476H, request E3H), @MSG (4479H, display a message), @LOGOT (447BH, display a carriage-return-terminated line) and @LOGER (447EH).
4467
The @DSPLY entry. JUMP to the display-string routine at 44E3H, which writes a message (addressed by the caller in Register Pair HL) to the video screen.
446A
The @PRINT entry. JUMP to the print-string routine at 44F8H, which writes the caller's message to the printer.
446D
The @TIME entry (Model I equate @TIME = 446DH). JUMP to 4D48H, which writes the software clock's time (HH:MM:SS) to the screen. The Model I equate confirms the target's function; the Model III @LOC lands elsewhere.
4470
The @DATE entry (Model I equate @DATE = 4470H). JUMP to 4D63H, which writes the date to the screen. The Model I equate confirms the target's function; the Model III @LOF lands elsewhere.
4473
LD A,D3H 3E D3
The @FEXT entry (Model I equate @FEXT = 4473H). Load Register A with request code D3H, the apply-default-file-extension function, dispatched through 4BCDH.
4475
RST 28H EF
Execute the supervisor call for request D3H.
4476
LD A,E3H 3E E3
The @PARAM entry (Model I equate @PARAM = 4476H). Load Register A with request code E3H, the parameter-table parsing function, dispatched through 4BCDH.
4478
RST 28H EF
Execute the supervisor call for request E3H.
4479
The @MSG entry (Model I equate @MSG = 4479H). JUMP into the display-string output loop at 44E6H, reusing that routine to display a message already prepared by the code above.
447B
The @LOGOT entry (Model I equate @LOGOT = 447BH). GOSUB to the display-string routine at 44E3H to write a carriage-return-terminated message to the screen. Overlays reach this entry to display assembled text; SYS4/SYS calls it to show each error line.
447E
The @LOGER entry (Model I equate @LOGER = 447EH). JUMP to the return stub at 44FDH.
44A0H - Program-Loader Parameter Buffer
Not loose workspace: this is the parameter buffer the program loader uses. The loader stores into 44A1H (at 4C22H) and points Register Pair DE at 44A0H as a buffer base (at 4C3CH). The bytes shown are the buffer's initial contents, overwritten while a program is loaded, followed by fill before the DEBUG handler.
44A0
DEFB 80H 80
Loader parameter buffer, byte 0. Holds 80H initially; overwritten by the loader at run time.
44A1
DEFB 00H,00H,00H 00 00 00
Loader parameter buffer, bytes 1-3. The loader writes byte 1 (44A1H) during a load (at 4C22H).
44A4
DEFB 42H 42
Loader parameter buffer, byte 4. Holds 42H initially; a working value in the load buffer, overwritten at run time.
44A5
DEFB 00H,00H,00H,00H,00H,00H,00H 00 00 00 00 00 00 00
Loader parameter buffer, bytes 5-11, cleared.
44AC
DEFB FFH,FFH FF FF
Fill bytes (FFH) after the loader buffer.
44AE
DEFB 00H,00H 00 00
Two cleared bytes bridging toward the DEBUG handler.
44B0
DEFB FFH,FFH,FFH,FFH FF FF FF FF
Fill bytes (FFH) immediately before the DEBUG handler at 44B4H.
44B4H - DEBUG Launcher, then @CKDRV / @FNAME / @DOKEY / @MULT / @DIV Vectors
The @DEBUG launcher (44B4H-44B7H) is reached from the RST 30H restart vector at 400FH and the @DEBUG vector at 440DH; it saves AF and issues request 87H, which brings in and starts the DEBUG monitor overlay (that call does not return here). Physically following it are five further supervisor-call vectors that the Model I LDOS equates name @CKDRV (44B8H, request C4H), @FNAME (44BBH, request A5H), @DOKEY (44BEH, request CDH), @MULT (44C1H, JP to the multiply routine at 4B8FH) and @DIV (44C4H, JP to the divide routine at 4BA9H). The disassembler had read these as one continuous DEBUG sequence; the equate table shows they are independent entry points.
44B4
PUSH AF F5
Save Register Pair AF (the accumulator and flags as they were when DEBUG was invoked) so the monitor can later show the caller's register state.
44B5
LD A,87H 3E 87
Load Register A with request code 87H, one of the DEBUG-support functions selected by the dispatcher at 4BCDH (documented when that routine is analysed).
44B7
RST 28H EF
Execute the supervisor call for request 87H.
44B8
LD A,C4H 3E C4
The @CKDRV entry (Model I equate @CKDRV = 44B8H). Load Register A with request code C4H, the check-drive function, decoded by the dispatcher at 4BCDH.
44BA
RST 28H EF
Execute the supervisor call for request C4H.
44BB
LD A,A5H 3E A5
The @FNAME entry (Model I equate @FNAME = 44BBH). Load Register A with request code A5H, the build-filename-from-directory function (serviced by SYS3/SYS), decoded by the dispatcher at 4BCDH.
44BD
RST 28H EF
Execute the supervisor call for request A5H.
44BE
LD A,CDH 3E CD
The @DOKEY entry (Model I equate @DOKEY = 44BEH). Load Register A with request code CDH, the DO/KSM key-processing function, decoded by the dispatcher at 4BCDH.
44C0
RST 28H EF
Execute the supervisor call for request CDH.
44C1
The @MULT entry (Model I equate @MULT = 44C1H). JUMP to the integer-multiply routine at 4B8FH (its primitive is MULTEA at 4B6CH).
44C4
The @DIV entry (Model I equate @DIV = 44C4H). JUMP to the integer-divide routine at 4BA9H (its primitive is DIVEA at 4B7BH).
44C7H - Device Driver Linkage Routine
Reached from the @DVRHK vector at 4033H. Given a Device Control Block, it fetches that block's driver entry address and type/attribute byte, follows any chain link to another block, checks whether the device is enabled, and transfers control to the driver. On entry Register Pair HL points to the DCB and Register B holds the caller's driver-operation code.
44C7
PUSH HL E5
Push the DCB pointer (Register Pair HL) so it can be transferred into the index register.
44C8
POP IX DD E1
Pop that DCB pointer into Register Pair IX, so the block's fields can be reached by fixed offsets from IX.
44CA
LD L,(IX+01H) DD 6E 01
Load Register L with the low byte of the driver entry address, held at offset 1 of the DCB.
44CD
LD H,(IX+02H) DD 66 02
Load Register H with the high byte of the driver entry address, held at offset 2, so Register Pair HL now holds the driver's entry address.
44D0
LD A,(IX+00H) DD 7E 00
Load Register A with the DCB type/attribute byte, held at offset 0.
44D3
CP 10H FE 10
Compare the type byte against 10H, the value that marks this block as a link (an alias pointing at another DCB rather than a real driver). Equality sets the Z flag.
44D5
If the block is a link (Z flag set), LOOP BACK to 44C7H to follow it, using the address just fetched into Register Pair HL as the next DCB to examine.
44D7
If the type byte is greater than 10H (NO CARRY, i.e. not a plain device block), JUMP to 48ECH to handle that case.
44DA
AND 08H E6 08
Isolate bit 3 of the type/attribute byte, the device-enabled flag, discarding all other bits.
44DC
XOR 08H EE 08
Invert that isolated bit 3, so the Z flag will be set when bit 3 was originally on (device enabled) and clear when it was off.
44DE
RET Z C8
Return to the caller when bit 3 was set, meaning this device is flagged such that the linkage does not itself dispatch to the driver.
44DF
LD A,B 78
Move the caller's driver-operation code from Register B into Register A.
44E0
CP 02H FE 02
Compare that operation code against 2. The resulting flags distinguish which driver operation is requested and are carried unchanged into the driver by the jump that follows.
44E2
JP (HL) E9
Transfer control to the driver whose entry address is in Register Pair HL, with the operation flags set by the comparison above.
44E3H - Display String Routine
The worker behind the @DSPLY vector. It sends a string to the video device one character at a time through the ROM device-output routine, stopping at the 03H terminator (and also stopping after a carriage return). On entry Register Pair HL points to the string.
44E3
LD DE,401DH 11 1D 40
Point Register Pair DE at the video display DCB at 401DH, so the ROM output routine sends characters to the screen.
44E6
PUSH HL E5
Output Loop
Save the string pointer (Register Pair HL) across the character-output call.
44E7
LD A,(HL) 7E
Fetch into Register A the next character of the string addressed by Register Pair HL.
44E8
CP 03H FE 03
Compare the character against 03H, the end-of-string terminator LDOS uses for display strings. Equality sets the Z flag.
44EA
If the terminator was reached (Z flag set), JUMP to 44F6H to finish.
44EC
PUSH AF F5
Save the character in Register A across the output call.
44ED
GOSUB to the ROM device-output routine at 001BH, which sends the character in Register A to the device whose DCB is addressed by Register Pair DE (here the screen).
44F0
POP AF F1
Restore the character into Register A.
44F1
INC HL 23
Advance Register Pair HL to the next character of the string.
44F2
CP 0DH FE 0D
Compare the character just sent against carriage return (0DH), which also ends the string.
44F4
Output Loop
If the character was not a carriage return (NZ), LOOP BACK to 44E7H to output the next character.
44F6
POP HL E1
Restore the saved string pointer into Register Pair HL.
44F7
RET C9
Return to the caller.
44F8H - Print String Routine
The worker behind the @PRINT vector. It is the display routine directed at the printer instead of the screen, sharing the same output loop.
44F8
LD DE,4025H 11 25 40
Point Register Pair DE at the printer DCB at 4025H, so the shared output loop sends characters to the printer.
44FB
JUMP into the shared output loop at 44E6H to emit the string (addressed by Register Pair HL) to the printer.
44FD
RET C9
Return stub reached from the 447EH vector.
4500H - Task Control Block Table (TCB$)
The Task Control Block table, labelled TCB$. Twelve task-handler slots ... holds twelve two-byte handler addresses that the interrupt service routine calls in turn on each timer tick. Every slot is initialised to the do-nothing default handler at 45B2H until a real periodic task is installed in that slot. This is a data table, not code.
Each two-byte entry is initialised to 45B2H, the default handler that simply returns, so an unused slot does nothing when the interrupt routine calls it. Installing a periodic task replaces one entry with that task's handler address
4500
DEFW 45B2H B2 45
Task Slot 1.
4502
DEFW 45B2H B2 45
Task Slot 2.
4504
DEFW 45B2H B2 45
Task Slot 3.
4506
DEFW 45B2H B2 45
Task Slot 4.
4508
DEFW 45B2H B2 45
Task Slot 5.
450A
DEFW 45B2H B2 45
Task Slot 6.
450C
DEFW 45B2H B2 45
Task Slot 7.
450E
DEFW 45B2H B2 45
Task Slot 8.
4510
DEFW 45B2H B2 45
Task Slot 9.
4512
DEFW 45B2H B2 45
Task Slot 10.
4514
DEFW 45B2H B2 45
Task Slot 11.
4516
DEFW 45B2H B2 45
Task Slot 12.
4518H - Interrupt Service Routine
Reached on every hardware interrupt through the RST 38H vector at 4012H. It reads the Model I interrupt-status latch, masks it against the enabled sources, runs the task attached to each pending source, performs the per-tick clock housekeeping, and can divert into the DEBUG monitor when a trap is armed. The latch is read at the memory-mapped address 37E0H (a port would be used on a Model III), which fixes this image as the Model I build.
4518
PUSH HL E5
Save Register Pair HL; an interrupt handler must preserve every register it disturbs so the interrupted program resumes unchanged.
4519
PUSH AF F5
Save Register Pair AF (the accumulator and the flags).
451A
LD HL,37E0H 21 E0 37
Point Register Pair HL at 37E0H, the Model I memory-mapped interrupt-status latch in the Expansion Interface. Each bit of this latch marks one pending interrupt source, with the real-time-clock heartbeat as the highest bit.
451D
LD A,(HL) 7E
Read the latch into Register A, capturing the set of currently pending interrupt sources.
451E
OR (HL) B6
OR a second read of the same latch into Register A, combining the status so no source bit is lost if it changed between the two reads.
451F
LD HL,404BH 21 4B 40
Point Register Pair HL at 404BH, the interrupt-status save byte in the resident workspace.
4522
LD (HL),A 77
Store the captured interrupt status at 404BH so task code can inspect which sources fired.
4523
INC L 2C
Advance Register Pair HL to 404CH, the interrupt-enable mask byte, in which a set bit enables handling of the corresponding source.
4524
AND (HL) A6
AND the status in Register A with the enable mask at 404CH, leaving set only the sources that are both pending and enabled.
4525
If no enabled source is pending (Z flag set), skip the task scan and JUMP to the per-tick housekeeping at 452FH.
4527
INC L 2C
Source Scan Loop
Advance Register Pair HL to the next entry of the task-vector table that begins at 404DH, so HL tracks the vector for the source about to be tested.
4528
RRA 1F
Rotate Register A right, shifting the lowest still-pending interrupt bit out into the carry flag.
4529
If that source bit was set (carry), JUMP to 4538H to run the task attached to this interrupt source.
452B
INC L 2C
Advance Register Pair HL past the second byte of the current two-byte task vector, so it points at the next vector.
452C
OR A B7
Test the interrupt bits still left in Register A.
452D
Source Scan Loop
If pending bits remain (NZ), LOOP BACK to 4527H to test the next source.
452F
GOSUB to the per-tick keyboard/BREAK scan at 4DA6H, which reads the SHIFT, ENTER, and BREAK keys and records them in the key-status flag byte at 4423H. It returns NZ when BREAK was pressed and the handler must act.
4532
If housekeeping requested further action (NZ), JUMP to 454FH to service it.
4534
POP AF F1
Exit Path
Restore Register Pair AF to its value before the interrupt.
4535
POP HL E1
Restore Register Pair HL.
4536
EI FB
Re-enable interrupts, which were held off automatically while the handler ran.
4537
RET C9
Return to the interrupted program.
4538
PUSH AF F5
Task Dispatch
A pending source was found. Save Register Pair AF for the task about to run.
4539
PUSH BC C5
Save Register Pair BC for the task.
453A
PUSH DE D5
Save Register Pair DE for the task.
453B
PUSH HL E5
Save Register Pair HL, which holds the current position in the task-vector table.
453C
PUSH IX DD E5
Save Register Pair IX for the task.
453E
LD DE,4547H 11 47 45
Load Register Pair DE with 4547H, the address the task must return to after it runs.
4541
PUSH DE D5
Push 4547H onto the stack as the task's return address, so the task ends with an ordinary RET.
4542
LD E,(HL) 5E
Load Register E with the low byte of this source's task-handler address from the vector table.
4543
INC L 2C
Advance Register Pair HL to the high byte of the vector.
4544
LD D,(HL) 56
Load Register D with the high byte, so Register Pair DE now holds the task-handler address.
4545
EX DE,HL EB
Move the task-handler address into Register Pair HL.
4546
JP (HL) E9
Jump to the task handler; because 4547H was pushed as its return address, its closing RET brings control back to 4547H.
4547
POP IX DD E1
The task has returned. Restore Register Pair IX.
4549
POP HL E1
Restore Register Pair HL, resuming the scan at the current vector position.
454A
POP DE D1
Restore Register Pair DE.
454B
POP BC C1
Restore Register Pair BC.
454C
POP AF F1
Restore Register Pair AF, which still holds the remaining interrupt bits.
454D
LOOP BACK to 452BH to continue scanning for any further pending sources.
454F
Housekeeping asked for action. If the carry flag is clear (no disk work), JUMP to 4558H to test the DEBUG trap; a set carry means a disk operation needs servicing and falls through.
4551
PUSH BC C5
Save Register Pair BC across the disk-service call.
4552
GOSUB to the disk-service routine at 4750H, which continues a pending floppy-drive restore or seek that the clock tick is timing.
4555
POP BC C1
Restore Register Pair BC.
4556
JUMP to the exit path at 4534H.
4558
LD A,(430FH) 3A 0F 43
DEBUG Trap Check
Load the system flag byte at 430FH.
455B
BIT 4,A CB 67
Test bit 4 of the flag byte at 430FH. When this bit is set it suppresses the DEBUG-trap diversion described below.
455D
If bit 4 is set (DEBUG diversion suppressed), JUMP to the normal exit at 4534H.
455F
LD HL,4315H 21 15 43
Point Register Pair HL at the patchable hook stub at 4315H, which is armed by being set to a JP opcode.
4562
LD A,C3H 3E C3
Load Register A with C3H, the Z80 opcode for an unconditional JP; this is the value the stub at 4315H holds when the DEBUG trap is armed.
4564
SUB (HL) 96
Subtract the current byte at 4315H from C3H. The result is zero (Z flag set) only when the stub already holds C3H, meaning the trap is armed.
4565
If the trap is not armed (NZ), JUMP to the normal exit at 4534H.
4567
LD (HL),A 77
Store the zero result into 4315H, disarming the trap so this diversion fires only once.
4568
LD HL,(4316H) 2A 16 43
Load Register Pair HL with the routine address held at 4316H, which is 44B4H, the DEBUG handler.
456B
POP AF F1
Restore the saved Register Pair AF from the entry PUSH at 4519H.
456C
EX (SP),HL E3
Exchange Register Pair HL with the interrupt return address on top of the stack, so the address the closing RET will use becomes 44B4H while HL is restored to the interrupted program's value.
456D
EI FB
Re-enable interrupts.
456E
RET C9
Return, but to the address just swapped onto the stack (44B4H), diverting execution into the DEBUG handler instead of the interrupted program.
456FH - Clock Periodic Task
One of the periodic tasks the interrupt service routine dispatches on each timer tick. It services four fixed task slots, then advances a rotating sub-second tick counter and services one further slot chosen by that counter.
456F
LD A,08H 3E 08
Load Register A with task-slot index 8, selecting slot 8 of the Task Control Block table at 4500H.
4571
GOSUB to the task dispatcher at 458AH to run the handler installed in slot 8.
4574
LD A,09H 3E 09
Load Register A with task-slot index 9.
4576
GOSUB to the dispatcher to run the handler in slot 9.
4579
LD A,0AH 3E 0A
Load Register A with task-slot index 10.
457B
GOSUB to the dispatcher to run the handler in slot 10.
457E
LD A,0BH 3E 0B
Load Register A with task-slot index 11.
4580
GOSUB to the dispatcher to run the handler in slot 11.
4583
LD HL,4040H 21 40 40
Point Register Pair HL at 4040H, the sub-second tick counter that counts interrupt ticks between clock updates.
4586
INC (HL) 34
Increment the tick counter at 4040H by one for this interrupt.
4587
LD A,(HL) 7E
Load the new tick count from 4040H into Register A.
4588
AND 07H E6 07
Mask the count to its low three bits, producing a rotating slot index in the range 0 through 7. Execution falls through into the dispatcher, which runs the slot named by that index, so slots 0 to 7 are each serviced once every eight ticks.
458AH - Task Handler-Block Dispatcher
Given a slot index in Register A, this runs the task installed in that slot of the Task Control Block table at 4500H. Each slot holds a pointer to a handler block; the first word of the block is the handler entry address, and the block base is passed to the handler in Register Pair IX.
458A
RLCA 07
Double the slot index in Register A to a byte offset, because each Task Control Block slot is a two-byte pointer.
458B
LD L,A 6F
Place the offset into Register L.
458C
LD H,45H 26 45
Set Register H to 45H, so Register Pair HL now addresses slot A within the Task Control Block table (4500H to 4517H).
458E
LD (45B5H),HL 22 B5 45
Save the slot address in the scratch pointer at 45B5H, so the task-removal routines can later identify which slot is being dispatched.
4591
LD E,(HL) 5E
Load Register E with the low byte of the slot's handler-block pointer.
4592
INC L 2C
Advance to the high byte of the pointer.
4593
LD D,(HL) 56
Load Register D with the high byte, so Register Pair DE holds the handler-block pointer.
4594
PUSH DE D5
Push the handler-block pointer so it can be transferred into the index register.
4595
POP IX DD E1
Pop the handler-block pointer into Register Pair IX, giving the handler its block base for indexed access.
4597
EX DE,HL EB
Move the handler-block pointer into Register Pair HL.
4598
LD E,(HL) 5E
Load Register E with the low byte of the first word of the block, which is the handler entry address.
4599
INC HL 23
Advance to the high byte of the handler entry address.
459A
LD D,(HL) 56
Load Register D with the high byte, so Register Pair DE holds the handler entry address.
459B
EX DE,HL EB
Move the handler entry address into Register Pair HL.
459C
JP (HL) E9
Jump to the handler, with Register Pair IX pointing at its handler block. An empty slot holds the default block at 45B2H, whose handler word is 45B1H (a bare RET), so an unused slot does nothing.
459DH - Task-Slot Management Routines
The routines the 4410H-4419H vectors jump to. They install a task handler-block pointer into a Task Control Block slot, or overwrite a slot with the default do-nothing block to remove a task. Interrupts are disabled while a slot is updated because the interrupt routine reads the same table. Entry at 459DH removes the task currently being dispatched; entry at 45A2H removes the task in the slot named by Register A; entry at 45A5H installs the block pointer in Register Pair DE into the slot named by Register A.
459D
POP DE D1
Remove the caller's return word from the stack into Register Pair DE (it is discarded; the routine returns via the shared exit below).
459E
LD A,(45B5H) 3A B5 45
Load Register A with the low byte of the dispatcher's saved slot address at 45B5H, identifying the slot of the currently dispatching task.
45A1
RRCA 0F
Halve that byte, converting the two-byte offset back into a slot index in Register A.
45A2
LD DE,45B2H 11 B2 45
Load Register Pair DE with 45B2H, the default do-nothing handler block, so storing it into a slot removes any task there. (Entry point for remove-by-slot, with the slot index already in Register A.)
45A5
CP 0CH FE 0C
Compare the slot index in Register A against 12, the number of slots. (Entry point for install, with the handler-block pointer already in Register Pair DE.)
45A7
RET NC D0
If the index is 12 or greater (NO CARRY), it is out of range, so return without changing anything.
45A8
RLCA 07
Double the slot index to a byte offset into the table.
45A9
LD L,A 6F
Place the offset into Register L.
45AA
LD H,45H 26 45
Set Register H to 45H, so Register Pair HL addresses the target slot in the Task Control Block table.
45AC
DI F3
Disable interrupts so the interrupt routine cannot read the table mid-update.
45AD
LD (HL),E 73
Store the low byte of the handler-block pointer (Register E) into the slot.
45AE
INC L 2C
Advance to the slot's high byte.
45AF
LD (HL),D 72
Store the high byte of the handler-block pointer (Register D), completing the slot update.
45B0
EI FB
Re-enable interrupts.
45B1
RET C9
Return to the caller. This RET is also the default task handler reached through the block at 45B2H.
45B2
DEFW 45B1H B1 45
The default handler block. Its single word points at the RET at 45B1H, so a slot left at this default value runs a routine that does nothing.
45B4H - Install-At-Preset-Slot Routine
The routine the 4416H vector jumps to. It installs a handler into a slot whose address is held in a pointer that is patched into its own LD instruction at run time.
45B4
LD HL,0000H 21 00 00
Self-Modifying Code
The 0000H operand is overwritten at run time with the address of a stored slot pointer. As loaded it is 0000H; the installing code patches it before this vector is used.
45B7
LD E,(HL) 5E
Load Register E with the low byte of the slot address held at the patched location.
45B8
INC HL 23
Advance to the high byte.
45B9
LD D,(HL) 56
Load Register D with the high byte, so Register Pair DE holds the target slot address.
45BA
EX DE,HL EB
Move that slot address into Register Pair HL.
45BB
POP DE D1
Pop the handler-block pointer supplied by the caller from the stack into Register Pair DE.
45BC
JUMP to the shared store code at 45ACH, which writes the handler-block pointer into the addressed slot with interrupts disabled.
45BEH - Software Real-Time-Clock Task
A handler-block task that maintains the time of day. Register Pair IX points at its handler block; the block holds a divider count at offset 2. The task runs only once every five ticks, then cascades the seconds, minutes, and hours counters at 4041H-4043H against the rollover limits stored at 45DDH.
45BE
POP BC C1
Remove the dispatcher's return word into Register Pair BC in preparation for the tick handling below.
45BF
LD B,L 45
Copy Register L into Register B as scratch working storage for this task.
45C0
DEC B 05
Decrement that scratch value in Register B.
45C1
DEC (IX+02H) DD 35 02
Decrement the divider count held at offset 2 of this task's handler block, which paces the clock so it advances only every few ticks.
45C4
RET NZ C0
If the divider has not yet reached zero (NZ), return; it is not yet time to advance the clock.
45C5
LD (IX+02H),05H DD 36 02 05
Reload the divider count at block offset 2 with 5, so the clock advances once every five of these ticks.
45C9
LD B,03H 06 03
Load Register B with 3, the number of time units to cascade (seconds, minutes, hours).
45CB
LD HL,4041H 21 41 40
Point Register Pair HL at 4041H, the seconds counter (the minutes counter is at 4042H and the hours counter at 4043H).
45CE
LD DE,45DDH 11 DD 45
Point Register Pair DE at the rollover-limit table at 45DDH, which holds 60 for seconds, 60 for minutes, and 24 for hours.
45D1
INC (HL) 34
Cascade Loop
Increment the current time counter addressed by Register Pair HL.
45D2
LD A,(DE) 1A
Load Register A with this unit's rollover limit from the table.
45D3
SUB (HL) 96
Subtract the counter's new value from the limit; the result is zero (Z flag set) only when the counter has reached its limit.
45D4
RET NZ C0
If the counter has not reached its limit (NZ), the cascade stops here, so return.
45D5
LD (HL),A 77
The counter reached its limit, so store the zero in Register A back into it, wrapping this unit to zero.
45D6
INC L 2C
Advance Register Pair HL to the next time counter (seconds to minutes to hours).
45D7
INC E 1C
Advance Register Pair DE to the next unit's rollover limit in the table.
45D8
Cascade Loop
Decrement Register B and LOOP BACK to 45D1H to carry into the next time unit, until all three units are processed.
45DA
RET C9
Return once the cascade completes.
45DB
DEFB 00H,00H 00 00
Two unused workspace bytes preceding the limit table.
45DD
DEFB 3CH, 3CH, 18H 3C 3C 18
The clock rollover-limit table read by the cascade loop above: 3CH (60) for seconds, 3CH (60) for minutes, and 18H (24) for hours. These three data bytes are misread as instructions by the disassembler and are corrected here.
45E0H - Select Drive and Wait For Controller
Selects the drive described by the current drive control block (addressed by Register Pair IY) and waits for the WD1771 to be free. The drive-select value is assembled from the control block and written to the memory-mapped drive-select latch at 37E1H.
45E0
GOSUB to the wait-not-busy routine at 461CH so no new selection is made while the controller is still executing a command.
45E3
RLCA 07
Rotate Register A left, positioning the carry returned by the wait routine for the RET NC test below.
45E4
PUSH AF F5
Save Register Pair AF (that status) across the drive-select assembly.
45E5
LD A,(IY+03H) FD 7E 03
Load Register A with the drive-option flags at offset 3 of the drive control block.
45E8
AND 10H E6 10
Isolate bit 4 of the option flags, the flag that contributes to the drive-select latch.
45EA
RRCA 0F
Rotate that isolated bit right into the position it occupies in the drive-select latch.
45EB
OR (IY+04H) FD B6 04
OR in the drive-selection bits held at offset 4 of the control block, which identify which physical drive to select.
45EE
AND 0FH E6 0F
Keep only the low four bits, the four drive-select lines the latch drives.
45F0
LD (4309H),A 32 09 43
Save the assembled drive-select value in the resident image byte at 4309H, so the wait loop can re-assert it.
45F3
LD (37E1H),A 32 E1 37
Write the drive-select value to the memory-mapped drive-select latch at 37E1H, energising the chosen drive.
45F6
POP AF F1
Restore the saved status into Register Pair AF.
45F7
RET NC D0
Return to the caller if no carry, meaning the controller was ready and the selection completed normally.
45F8
Otherwise JUMP to the flag-check and delay helper at 4399H to pace the operation before proceeding.
45FBH - FDC Operation Dispatcher
Selects which floppy-controller operation to perform from the operation code in Register B. Restore, seek, and step share the Type I command path; the read and write operations branch away to their own handlers. The step direction and command base are set up here before falling into the command-issue routine.
45FB
LD A,B 78
Copy the operation code from Register B into Register A for testing.
45FC
OR A B7
Test the operation code; a value of zero sets the Z flag.
45FD
RET Z C8
If the operation code is zero (no operation requested), return to the caller.
45FE
CP 07H FE 07
Compare the operation code against 7, the value that requests a simple wait for the controller.
4600
If the code is 7 (NZ not set), JUMP to the wait-not-busy routine at 461CH.
4602
If the code is above 7 (NO CARRY), it is a read or write request, so JUMP to the data-transfer handler at 466BH.
4604
CP 06H FE 06
Compare the operation code against 6, the seek-to-track request.
4606
If the code is 6 (seek), JUMP to the Type I command-issue routine at 462AH, which sets the target track first.
4608
DEC A 3D
Decrement the operation code; a code of 1 (restore) now becomes zero.
4609
If the code was 1 (a bare drive-select/restore, now zero), JUMP to the drive-select routine at 45E0H.
460B
INC (IY+05H) FD 34 05
Increment the current-track byte at offset 5 of the drive control block, stepping the head one track outward.
460E
CP 04H FE 04
Compare the (decremented) operation code against 4, distinguishing a step-in from a step-out request.
4610
LD B,58H 06 58
Load Register B with 58H, the WD1771 Step-In command base (with head-load and track-update set), to be combined with the stepping rate.
4612
If the code selected step-in (Z), JUMP to the command-issue tail at 4659H to send that command.
4614
LD (IY+05H),00H FD 36 05 00
Otherwise reset the current-track byte at control-block offset 5 to zero, as a Restore returns the head to track 0.
4618
LD B,08H 06 08
Load Register B with 08H, the WD1771 Restore command base (with head-load set).
461A
JUMP to the command-issue tail at 4659H to send the Restore command.
461CH - Wait For FDC Not Busy
Polls the WD1771 status register until the controller finishes its current command, re-asserting the drive selection on each pass. The controller is read at the memory-mapped status address 37ECH.
461C
LD A,(37ECH) 3A EC 37
Read the WD1771 status register from the memory-mapped command/status address 37ECH into Register A.
461F
BIT 0,A CB 47
Test bit 0 of the status register, the Busy bit. It is 1 while the controller is executing a command and 0 when the controller is idle.
| 1771 FDC Type I Status: bit 0 tested (Busy) | | Function Description |
| N | W | H | S | C | T | I | B | | Type I Command Status Register N: 1=Not Ready, 0=Ready W: 1=Write Protected, 0=Not Protected H: 1=Head Loaded, 0=Head Not Loaded S: 1=Seek Error, 0=No Error C: 1=CRC Error, 0=No Error T: 1=Track 0, 0=Not Track 0 I: 1=Index Mark Detected, 0=No Index B: 1=Busy, 0=Not Busy |
4621
RET Z C8
If the Busy bit is clear (Z flag set), the controller is idle, so return to the caller.
4622
LD A,(4309H) 3A 09 43
Load Register A with the saved drive-select value from 4309H.
4625
LD (37E1H),A 32 E1 37
Re-write the drive-select value to the latch at 37E1H, holding the drive energised while waiting.
4628
LOOP BACK to 461CH to test the Busy bit again until the controller is idle.
462AH - Issue WD1771 Type I Command
Sets the target track and sector in the WD1771, computes the seek distance, then builds and issues a Type I command (Restore, Seek, or Step) whose stepping rate comes from the drive control block. The command byte is written to the memory-mapped command register at 37ECH.
462A
GOSUB to the wait-not-busy routine at 461CH so the controller is idle before loading its registers.
462D
LD A,(IY+05H) FD 7E 05
Load Register A with the current-track number from offset 5 of the drive control block.
4630
LD (37EDH),A 32 ED 37
Write that track number into the WD1771 track register at 37EDH, telling the controller where the head currently is.
4633
LD A,(IY+07H) FD 7E 07
Load Register A with the requested sector information from offset 7 of the drive control block.
4636
AND 1FH E6 1F
Keep the low five bits, the sector number within the track.
4638
INC A 3C
Add one, converting the zero-based sector number to the one-based value the controller expects.
4639
RES 4,(IY+03H) FD CB 03 A6
Clear bit 4 of the drive-option flags at control-block offset 3, the side-selection flag, defaulting the operation to side 0.
463D
SUB E 93
Subtract the destination track in Register E, giving the signed distance from the current track to the target.
463E
NEG ED 44
Negate the result so Register A holds the track distance in the direction of travel, and set the sign flag if the target is below the current track.
4640
PUSH DE D5
Save Register Pair DE (holding the destination track and sector) across the side test.
4641
If the distance is negative (sign flag set), the target is on the lower half of travel, so JUMP to 464FH without setting the side flag.
4644
LD E,A 5F
Save the positive track distance in Register E.
4645
BIT 5,(IY+04H) FD CB 04 6E
Test bit 5 of the drive flags at control-block offset 4, the double-sided-media flag, which is set when the drive holds a two-sided disk.
4649
If the media is single-sided (bit 5 clear, Z), JUMP to 464FH leaving the side flag clear.
464B
SET 4,(IY+03H) FD CB 03 E6
Otherwise set bit 4 of the option flags at offset 3, selecting side 1 for this double-sided access.
464F
LD (37EEH),DE ED 53 EE 37
Write Register Pair DE to the WD1771 sector register at 37EEH (low byte) and the data register at 37EFH (high byte), loading the sector number and the accompanying data-register value in one transfer.
4653
POP DE D1
Restore Register Pair DE.
4654
LD (IY+05H),D FD 72 05
Store the destination track (Register D) into the current-track byte at offset 5, recording where the head will be after the seek.
4657
LD B,18H 06 18
Load Register B with 18H, the WD1771 Seek command base (with head-load set), to be combined with the stepping rate below.
4659
GOSUB to the drive-select routine at 45E0H to energise the drive and confirm the controller is ready before the command is issued.
465C
LD A,(IY+03H) FD 7E 03
Load Register A with the drive-option flags at control-block offset 3.
465F
AND 03H E6 03
Keep the low two bits, which hold the WD1771 stepping-rate selection (00=6ms through 11=40ms on this controller family, per the drive's configured rate).
4661
OR B B0
Combine the stepping-rate bits with the command base in Register B, forming the complete Type I command byte.
4662
LD (37ECH),A 32 EC 37
Write the assembled command to the WD1771 command register at 37ECH, starting the seek/step/restore. The command base in Register B selects the operation (08H Restore, 18H Seek, 58H Step-In) and the low two bits set the stepping rate.
| 1771 FDC Command: Type I (base OR stepping rate) | | Function Description |
| 0 | 0 | 0/1 | 0/1 | h | V | r1 | r0 | | Type I Command (Restore/Seek/Step) Bit 7-5: Command class (000=Restore/Seek, 010/011=Step In/Out) Bit 4: u, Update Track Register (Step commands) h: 1=Head Load at start V: 1=Verify destination track, 0=No verify r1, r0: Stepping rate, from option-flag bits 1-0 |
4665
LD B,08H 06 08
Load Register B with 8 as a short settle-delay loop count following the command.
4667
Decrement Register B and loop in place until it reaches zero, giving the controller a brief settling delay after accepting the command.
4669
XOR A AF
Set Register A to zero and clear the carry flag, signalling a successful command issue.
466A
RET C9
Return to the caller.
466BH - FDC Read/Write Sector Engine
The data-transfer core reached from the FDC operation dispatcher for read and write requests. It patches the byte-transfer instruction and error vector for the requested direction, issues the WD1771 Type II command, then moves a sector's worth of bytes through the data register while polling the Data Request status bit. On entry Register B holds the operation code and the caller has supplied, inline after the call, the transfer instruction and its target address.
466B
LD BC,0588H 01 88 05
Load Register B with 05H (a retry count) and Register C with 88H (a WD1771 Read-Sector command pattern) as the defaults for this transfer.
466E
CP 0AH FE 0A
Compare the operation code in Register A against 0AH (the verify-sector operation).
4670
If the request is a verify (Z), JUMP to 467CH to set up a verify transfer.
4672
If the operation code is above 0AH (NO CARRY), it is a write, so JUMP to 4681H to set up a write transfer.
4674
LD B,0AH 06 0A
Otherwise the request is a read; load Register B with 0AH as the read transfer parameter.
4676
GOSUB to the transfer-setup routine at 469AH, which reads the inline parameters and patches the transfer instruction for a read.
4679
LD BC,021AH 01 1A 02
Load Register B with 1AH and Register C with 02H, the read-direction transfer control values.
467C
GOSUB to the transfer-setup routine at 469AH to read the inline parameters and patch the transfer instruction.
467F
LD BC,0E1AH 01 1A 0E
Load Register B with 1AH and Register C with 0EH, the write-direction transfer control values.
4681
XOR B A8
Exclusive-OR Register B into Register A, combining the operation code with the direction parameter to form the command selector.
4682
CP 0EH FE 0E
Compare the result against 0EH to decide the write-command form.
4685
If below 0EH (CARRY), JUMP to 468DH keeping the current command byte.
4687
LD C,0F4H 0E F4
Otherwise load Register C with 0F4H, the WD1771 Write-Track (format) command.
4689
If the result was not exactly 0EH (NZ), JUMP to 468DH with that command.
468B
LD C,0ABH 0E AB
Otherwise load Register C with 0ABH, the WD1771 Write-Sector command with the deleted-data address mark.
468D
BIT 7,(IY+03H) FD CB 03 7E
Test bit 7 of the option flags at offset 3 of the active drive's control block (Register Pair IY). Bit 7 marks the drive as not ready or write-inhibited.
4691
LD A,0FH 3E 0F
Load Register A with error code 0FH (a drive-fault error) in case the test above fails.
4693
RET NZ C0
If bit 7 was set (NZ), return to the caller with the fault error code.
4694
GOSUB to the transfer-setup routine at 469AH once more for this path.
4697
ADD HL,BC 09
Add Register Pair BC to HL, advancing the buffer pointer by the direction offset.
4698
LD A,(BC) 0A
Load Register A from the address in Register Pair BC (fetching a control value).
4699
LD (DE),A 12
Store it at the address in Register Pair DE.
469A
EX (SP),HL E3
Transfer Setup
Exchange Register Pair HL with the return address on the stack, so HL now points at the caller's inline parameters that follow the CALL, and the stacked value becomes the true return address past them.
469B
LD A,(HL) 7E
Load Register A with the first inline parameter byte, the byte-transfer opcode to be planted in the loop.
469C
INC HL 23
Advance the inline-parameter pointer.
469D
LD (46F6H),A 32 F6 46
Self-Modifying Code
Store that opcode byte into 46F6H, patching the completion-check instruction so it matches the read or write direction.
46A0
LD A,(HL) 7E
Load Register A with the next inline parameter, the low byte of the transfer instruction.
46A1
INC HL 23
Advance the pointer.
46A2
LD H,(HL) 66
Load Register H with the following inline parameter (the high byte).
46A3
LD L,A 6F
Move the saved low byte into Register L, so Register Pair HL now holds the two-byte transfer instruction to plant.
46A4
LD (46D1H),HL 22 D1 46
Self-Modifying Code
Store those two bytes into 46D1H, planting the actual byte-move instruction (a read from or write to the FDC data register) into the transfer loop below.
46A7
POP HL E1
Restore the caller's buffer pointer into Register Pair HL.
46A8
PUSH BC C5
Retry Entry
Save the retry count and command in Register Pair BC.
46A9
BIT 4,C CB 61
Test bit 4 of the command byte in Register C, which when set indicates the head is already positioned so no seek is needed.
46AB
If a seek is needed (bit 4 clear, Z), GOSUB to the Type I command routine at 462AH to position the head at the target track.
46AE
PUSH DE D5
Save Register Pair DE across the transfer.
46AF
PUSH HL E5
Save the buffer pointer.
46B0
LD HL,37ECH 21 EC 37
Point Register Pair HL at the WD1771 command/status register at 37ECH so the loop can poll status.
46B3
GOSUB to the wait-not-busy routine at 461CH so the controller is idle before the command is issued.
46B6
GOSUB to the patchable pre-transfer hook at 46FDH (a bare RET as loaded, patched when a special pre-transfer action is installed).
46B9
LD A,D 7A
Load Register A with the target track from Register D.
46BA
LD (37EDH),A 32 ED 37
Write it to the WD1771 track register at 37EDH.
46BD
LD DE,37EFH 11 EF 37
Point Register Pair DE at the WD1771 data register at 37EFH, the port the transfer loop reads or writes.
46C0
LD A,C 79
Load Register A with the assembled Type II command from Register C.
46C1
GOSUB to the command-issue point at 4662H to write the read/write command to the controller.
46C4
POP BC C1
Restore Register Pair BC.
46C5
PUSH BC C5
Save it again for the duration of the transfer.
46C6
JUMP into the poll loop at 46CBH to begin waiting for the first Data Request.
46C8
RRCA 0F
Transfer Loop
Rotate the status byte in Register A right, moving the completion bit toward the carry for the test below.
46C9
If the rotated bit shows the command has completed (NO CARRY on the Busy bit), JUMP to 46D7H to finish.
46CB
LD A,(HL) 7E
Read the WD1771 status register (Register Pair HL points at 37ECH) into Register A.
46CC
BIT 1,A CB 4F
Test bit 1 of the status, the Data Request (DRQ) bit, which is 1 when the controller has a byte ready to transfer.
46CE
If no byte is ready yet (DRQ clear, Z), JUMP to 46C8H to re-test for completion and keep polling.
46D0
DI F3
Disable interrupts so the byte transfer is not delayed and the controller does not lose data.
46D1
DEFB 00H,00H 00 00
Self-Modifying Code
The two-byte byte-transfer instruction planted here at 46A4H. For a read it moves a byte from the data register (Register Pair DE) into the buffer (Register Pair HL); for a write it moves the other way. As loaded it is two NOPs.
46D3
INC BC 03
Increment the transfer byte counter in Register Pair BC.
46D4
JUMP back to 46CBH to transfer the next byte.
46D7
EI FB
The transfer is complete; re-enable interrupts.
46D8
LD A,(HL) 7E
Read the final WD1771 status register value into Register A.
46D9
AND 7CH E6 7C
Mask the status to its Type II error bits, discarding Busy and Data Request and keeping the fault indicators.
| 1771 FDC Type II Status: AND 7CH keeps error bits | | Function Description |
| N | W | R | F | C | L | D | B | | Type II Command Status Register (bits kept by 7CH) N: 1=Not Ready, 0=Ready W: 1=Write Protected R: 1=Record Type/Deleted Data F: 1=Record Not Found C: 1=CRC Error L: 1=Lost Data D: 1=Data Request (masked out) B: 1=Busy (masked out) |
46DB
POP HL E1
Restore the buffer pointer.
46DC
POP DE D1
Restore Register Pair DE.
46DD
POP BC C1
Restore the retry count and command.
46DE
RET Z C8
If no error bits are set (Z), the transfer succeeded, so return to the caller.
46DF
BIT 2,A CB 57
Test bit 2 of the masked status, the Lost Data bit, which means the CPU did not service a byte in time.
46E1
If data was lost (NZ), JUMP back to the retry entry at 46A8H to attempt the transfer again.
46E3
PUSH AF F5
Save the error status.
46E4
AND 18H E6 18
Isolate bits 3 and 4, the CRC-error and Record-Not-Found bits.
46E6
If neither of those errors is present (Z), JUMP to 46F3H to compute the error code.
46E8
BIT 4,A CB 67
Test bit 4, the Record-Not-Found error.
46EA
PUSH BC C5
Save the retry count.
46EB
If a record was not found (NZ), GOSUB to 4614H to issue a Restore and re-home the head before retrying.
46EE
POP BC C1
Restore the retry count.
46EF
POP AF F1
Restore the error status.
46F0
Decrement the retry count in Register B and, while retries remain, LOOP BACK to 46A8H to try the transfer again.
46F2
LD B,A 47
Retries are exhausted; move the error status into Register B to convert it to an error number. (The preceding byte 06H is the operand consumed by the DJNZ above.)
46F3
LD B,A 47
Copy the masked status into Register B as the bit pattern to convert into an error number.
46F5
LD A,00H 3E 00
Set Register A to zero as the running error-number counter.
46F7
RRC B CB 08
Error-Number Loop
Rotate the status bits in Register B right; the bit that falls into carry marks the current error position. (This is the byte patched at 46F6H for direction, here reading as RRC B.)
46F9
RET C D8
If a set error bit reached the carry, return with the corresponding error number in Register A.
46FA
INC A 3C
Otherwise count one more bit position in Register A.
46FB
Error-Number Loop
LOOP BACK to 46F7H to test the next status bit.
46FD
NOP 00
Self-Modifying Code
The patchable pre-transfer hook. As loaded it is a NOP followed by the RET below, so calling it does nothing until a special pre-transfer routine patches this location.
46FE
RET C9
Return from the pre-transfer hook.
46FFH - Drive Configuration Table (DCT$)
A single filler byte, then the Drive Configuration Table at 4700H (labelled DCT$), holding one ten-byte control block per drive for up to four drives. Each block begins with a jump to the floppy-controller driver, so the block address is itself callable, and continues with the drive's flags, select mask, current track, and geometry. The active drive's block is the one Register Pair IY points at throughout the FDC code.
46FF
DEFB FDH FD
A single filler byte preceding the table.
Drive 0 control block.
4700
Bytes 0-2: a jump to the FDC operation dispatcher at 45FBH, so calling the block address executes the driver with this drive selected.
4703
DEFB 83H 83
Byte 3: drive option flags. Bit 7 set marks drive 0 as present/enabled; the low two bits (11) select the slowest WD1771 stepping rate.
4704
DEFB 01H 01
Byte 4: the drive-select bit mask, 01H for drive 0, written to the 37E1H latch to energise this drive.
4705
DEFB 01H 01
Byte 5: the current track the head is positioned on.
4706
DEFB 22H, 09H, 24H, 11H 22 09 24 11
Bytes 6-9: drive geometry defaults (step-rate/sector and track-count parameters) shared by all four drives.
Drive 1 control block.
470A
Bytes 0-2: jump to the FDC dispatcher at 45FBH for drive 1.
470D
DEFB 03H 03
Byte 3: drive 1 option flags. Bit 7 clear (drive not currently marked present); low two bits (11) set the stepping rate.
470E
DEFB 02H 02
Byte 4: drive-select mask 02H for drive 1.
470F
DEFB 11H 11
Byte 5: current track for drive 1.
4710
DEFB 22H, 09H, 24H, 11H 22 09 24 11
Bytes 6-9: drive geometry defaults.
Drive 2 control block.
4714
Bytes 0-2: jump to the FDC dispatcher at 45FBH for drive 2.
4717
DEFB 03H 03
Byte 3: drive 2 option flags (stepping rate in the low bits).
4718
DEFB 04H 04
Byte 4: drive-select mask 04H for drive 2.
4719
DEFB 11H 11
Byte 5: current track for drive 2.
471A
DEFB 22H, 09H, 24H, 11H 22 09 24 11
Bytes 6-9: drive geometry defaults.
Drive 3 control block.
471E
Bytes 0-2: jump to the FDC dispatcher at 45FBH for drive 3.
4721
DEFB 03H 03
Byte 3: drive 3 option flags (stepping rate in the low bits).
4722
DEFB 08H 08
Byte 4: drive-select mask 08H for drive 3.
4723
DEFB 11H 11
Byte 5: current track for drive 3.
4724
DEFB 22H, 09H, 24H, 11H 22 09 24 11
Bytes 6-9: drive geometry defaults.
4728H - Per-Drive Driver Hook Stubs
Four short stubs, one per drive, each a RET followed by cleared bytes. They are patchable hooks a driver extension can overwrite; as loaded they simply return.
4728
RET C9
Self-Modifying Code
Drive 0 driver hook, a bare RET as loaded.
4729
DEFB 00H x 9 00 00 00 00 00 00 00 00 00
Cleared bytes reserved for the drive 0 hook's patched code.
4732
RET C9
Self-Modifying Code
Drive 1 driver hook, a bare RET as loaded.
4733
DEFB 00H x 9 00 00 00 00 00 00 00 00 00
Cleared bytes for the drive 1 hook.
473C
RET C9
Self-Modifying Code
Drive 2 driver hook, a bare RET as loaded.
473D
DEFB 00H x 9 00 00 00 00 00 00 00 00 00
Cleared bytes for the drive 2 hook.
4746
RET C9
Self-Modifying Code
Drive 3 driver hook, a bare RET as loaded.
4747
DEFB 00H x 9 00 00 00 00 00 00 00 00 00
Cleared bytes for the drive 3 hook.
4750H - Disk Primitive Entry Points
The named low-level disk operations. Each loads its operation code into Register A and funnels into the common tail at 4779H, which records the drive, locates its control block, and runs the driver. On entry Register C holds the drive number; the entry at 4750H instead reuses the last drive saved at 4308H.
4750
LD A,(4308H) 3A 08 43
Load Register A with the last-used drive number saved at 4308H (the current-drive entry point).
4753
LD C,A 4F
Move that drive number into Register C, the drive selector the tail expects.
4754
LD A,01H 3E 01
SELECT entry. Load operation code 01H (select and log-in the drive) into Register A.
4756
JUMP to the common tail at 4779H.
4758
DEFB 00H 00
Command-processor status-flag byte, occupying the spare byte of the SELECT primitive entry. It is used by the SYS1 command-interpreter overlay: bit 1 gates the abort path, bit 2 marks command-line entry, and bit 4 requests parse-only. See the
SYS1 page.
4759
LD A,07H 3E 07
RSELCT entry. Load operation code 07H (re-select the drive) into Register A.
475B
JUMP to the common tail at 4779H.
475D
DEFB 00H 00
Command-processor status/geometry byte, occupying the spare byte between the RSELCT and SEEK primitive entries. Like the spare byte at 4758H (used by SYS1), it is repurposed by an overlay: the SYS2 file/device open overlay writes a disk density/geometry flag bit into it through a self-modified BIT/RES/SET instruction while reading the GAT. See the
SYS2 page.
475E
LD A,06H 3E 06
SEEK entry. Load operation code 06H (seek to a track) into Register A.
4760
JUMP to the common tail at 4779H.
4762
DEFB FFH FF
Filler byte between entries.
4763
LD A,0DH 3E 0D
WRSECT entry. Load operation code 0DH (write a sector) into Register A.
4765
JUMP to the common tail at 4779H.
4767
DEFB 01H 01
Filler byte before the next entry.
4768
LD A,0EH 3E 0E
WRPROT entry. Load operation code 0EH (write with the deleted-data mark / write-protect handling) into Register A.
476A
JUMP to the common tail at 4779H.
476C
DEFB 00H 00
Filler byte.
476D
LD A,0FH 3E 0F
WRTRK entry. Load operation code 0FH (write/format a whole track) into Register A.
476F
JUMP to the common tail at 4779H.
4771
DEFB 00H 00
Filler byte.
4772
LD A,0AH 3E 0A
VERSEC entry. Load operation code 0AH (verify a sector) into Register A.
4774
JUMP to the common tail at 4779H.
4776
DEFB 00H 00
Filler byte.
4777
LD A,09H 3E 09
RDSECT entry. Load operation code 09H (read a sector) into Register A, then fall through to the common tail.
4779
PUSH BC C5
Common Tail
Save Register Pair BC.
477A
LD B,A 47
Move the operation code into Register B, where the driver expects it.
477B
LD A,C 79
Move the drive number from Register C into Register A.
477C
LD (4308H),A 32 08 43
Store the drive number at 4308H as the current drive for later operations.
477F
PUSH IY FD E5
Save the caller's Register Pair IY.
4781
GOSUB to GETDCT at 478FH to set Register Pair IY to this drive's control block in the table at 4700H.
4784
LD A,20H 3E 20
Load Register A with 20H, the retry/attempt count for the operation.
4786
OR A B7
Test that count (clearing the carry flag before the call).
4787
GOSUB to the operation executor at 4BCBH, which invokes the drive's control block (its leading jump to the FDC dispatcher) with the operation in Register B.
478A
POP IY FD E1
Restore the caller's Register Pair IY.
478C
POP BC C1
Restore Register Pair BC.
478D
RET C9
Return to the caller with the operation's result.
478FH - Get Drive Control Table Entry (GETDCT)
Sets Register Pair IY to the control block for a drive in the table at 4700H, and provides a helper to fetch a byte from any drive's block. The block address is computed as 4700H plus the drive number times ten.
478F
PUSH HL E5
Save the caller's Register Pair HL.
4790
GOSUB to the address calculator at 47A5H, returning the drive's control-block address in Register Pair HL.
4793
EX (SP),HL E3
Exchange that address with the saved HL on the stack, so the block address is on the stack and the caller's HL is back in the register pair.
4794
POP IY FD E1
Pop the block address into Register Pair IY, making it the active drive control block.
4796
RET C9
Return to the caller.
4797
LD C,(IX+06H) DD 4E 06
Byte-fetch helper. Load Register C with the drive number held at offset 6 of the caller's file control block (Register Pair IX).
479A
LD A,08H 3E 08
Load Register A with 8, the block offset of the byte to fetch.
479C
PUSH HL E5
DCTBYT entry. Save Register Pair HL.
479D
LD H,A 67
Save the requested offset in Register H.
479E
GOSUB to the address calculator at 47A5H to get the drive's block base in Register Pair HL.
47A1
LD L,A 6F
Add the offset (returned in Register A) into Register L, pointing HL at the requested byte.
47A2
LD A,(HL) 7E
Load the requested control-block byte into Register A.
47A3
POP HL E1
Restore Register Pair HL.
47A4
RET C9
Return with the byte in Register A.
47A5
LD A,C 79
Block Address Calculator
Load Register A with the drive number from Register C.
47A6
AND 07H E6 07
Keep the low three bits, limiting the drive number to 0 through 7.
47A8
ADD A,A 87
Double the drive number (drive times 2).
47A9
LD L,A 6F
Save drive-times-2 in Register L.
47AA
ADD A,A 87
Double again (drive times 4).
47AB
ADD A,A 87
Double again (drive times 8).
47AC
ADD A,L 85
Add drive-times-2 back, giving drive times 10, the block size.
47AD
ADD A,00H C6 00
Add the low byte of the table base (00H, the low byte of 4700H), leaving the block's low address byte in Register A.
47AF
LD L,A 6F
Place the low address byte in Register L.
47B0
ADD A,H 84
Add Register H, carrying the requested field offset (zero for a plain block lookup) into the low byte.
47B1
LD H,47H 26 47
Set Register H to 47H, the high byte of the table page, so Register Pair HL addresses 4700H plus drive times ten.
47B3
RET C9
Return with the block (or field) address in Register Pair HL and its low byte in Register A.
47B4H - Build FDC Track/Sector Parameter Block
Copies the target sector and following control fields into the FDC parameter block at 44AEH, adjusting the sector number to the one-based value the controller expects. Two short byte-copy helpers follow. On entry Register Pair HL points at the source fields and Register A holds the raw sector value.
47B4
LD L,A 6F
Place the raw sector value from Register A into Register L as scratch.
47B5
LD DE,44AEH 11 AE 44
Point Register Pair DE at the FDC track/sector parameter block at 44AEH, the destination for the copied fields.
47B8
GOSUB to the byte-copy helper at 47C3H to copy the first source byte into the parameter block and return it in Register A.
47BB
AND 1FH E6 1F
Keep the low five bits, the sector number within the track.
47BD
INC A 3C
Add one, converting the zero-based sector number to the one-based value the WD1771 expects.
47BE
LD (DE),A 12
Store the adjusted sector number into the parameter block at 44AEH.
47BF
INC DE 13
Advance the destination pointer to 44AFH.
47C0
XOR A AF
Set Register A to zero.
47C1
LD (DE),A 12
Store zero into 44AFH, clearing the parameter block's second byte.
47C2
INC DE 13
Advance the destination pointer.
47C3
Byte-copy helper entry: GOSUB to 47C6H to copy one byte, then fall through to copy a second.
47C6
LD A,(HL) 7E
Load the source byte addressed by Register Pair HL into Register A.
47C7
LD (DE),A 12
Store it at the destination addressed by Register Pair DE.
47C8
INC HL 23
Advance the source pointer.
47C9
INC DE 13
Advance the destination pointer.
47CA
RET C9
Return with the copied byte in Register A.
47CBH - Position File and Prepare Record
The file record-positioning core behind the write, position, and backspace vectors. It validates the open file control block (IX), computes the buffer offset for the requested logical record, and flags the buffer as holding a record to write. IX offsets: +01 access/status flags (bit5 = buffer holds a record, bit6 = write pending, bit7 = open), +05 current byte offset in the buffer, +09 logical record length, +0A/0B next record number.
47CB
Random-write entry. GOSUB to 484DH to position the file to the caller-supplied record number before writing.
47CE
INC BC 03
Advance the record number in Register Pair BC to the next record.
47CF
Position vector entry. GOSUB to the FCB setup/validation routine at 49F3H, which establishes the buffer pointers for the open file.
47D2
SET 6,(IX+01H) DD CB 01 F6
Set bit 6 of the FCB flags at offset 1, marking that a write to the current record is pending.
47D6
BIT 7,(IX+01H) DD CB 01 7E
Test bit 7 of the FCB flags, the file-open flag.
47DA
If the file is not open (bit 7 clear, Z), JUMP to 47FAH to handle it without record math.
47DC
LD H,B 60
Move the high byte of the requested record number into Register H.
47DD
LD L,C 69
Move the low byte into Register L, so Register Pair HL holds the requested record number.
47DE
OR (IX+09H) DD B6 09
OR the logical record length at FCB offset 9 into Register A to test whether the file uses fixed-length records.
47E1
If there is no record length (Z), JUMP to 47FAH.
47E3
GOSUB to the record-to-offset routine at 4B8FH, which converts the record number in Register Pair HL into a sector count (returned in HL) and a byte offset within the sector (returned in Register A), using the record length.
47E6
LD B,H 44
Move the resulting sector count high byte into Register B.
47E7
LD C,L 4D
Move the low byte into Register C, so Register Pair BC holds the target sector number.
47E8
LD (IX+05H),A DD 77 05
Store the byte offset within the sector into FCB offset 5, the current position in the buffer.
47EB
BIT 5,(IX+01H) DD CB 01 6E
Test bit 5 of the FCB flags, which is set when the buffer already holds a record.
47EF
If the buffer already holds a record (NZ), JUMP to 47FDH.
47F1
GOSUB to 497EH to load the file's current next-record number (FCB offset 0A/0B) into Register Pair HL.
47F4
SCF 37
Set the carry flag so the subtraction below also subtracts one.
47F5
SBC HL,BC ED 42
Subtract the target sector (Register Pair BC) plus one from the current record position, giving zero only when the target is the record already positioned.
47F7
If the target is the current record (Z), JUMP to the end-of-file check at 4A1DH, since no repositioning is needed.
47FA
LD (IX+05H),A DD 77 05
Store the byte offset (Register A) into FCB offset 5, the current buffer position.
47FD
PUSH BC C5
Save the target sector number.
47FE
GOSUB to 4993H to confirm the file is open with the required access; it returns NZ on an access error.
4801
POP BC C1
Restore the target sector number.
4802
RET NZ C0
Return to the caller if the access check failed (NZ).
4803
LD (IX+0AH),C DD 71 0A
Store the target sector low byte into FCB offset 0A, the next-record-number low byte.
4806
LD (IX+0BH),B DD 70 0B
Store the target sector high byte into FCB offset 0B, the next-record-number high byte, positioning the file.
4809
GOSUB to 4914H to set bit 5 of the FCB flags, marking that the buffer now holds the positioned record.
480C
JUMP to the end-of-file check at 4A1DH and return through it.
480F
File-position vector entry (the vector at 4454H). GOSUB to the FCB setup routine at 49F3H.
4812
LD C,01H 0E 01
Load Register C with 1, positioning by a single record step.
4814
JUMP to the shared reposition tail at 482BH.
4816
Backspace vector entry (the vector at 4445H). GOSUB to the FCB setup routine at 49F3H.
4819
LD C,A 4F
Copy Register A into Register C as the step count.
481A
LD B,(IX+09H) DD 46 09
Load Register B with the logical record length from FCB offset 9.
481D
OR B B0
OR the record length into Register A to test whether records are fixed length.
481E
If there is no fixed record length (Z), JUMP to the reposition tail at 482BH.
4820
LD A,(IX+05H) DD 7E 05
Load the current buffer byte offset from FCB offset 5.
4823
SUB B 90
Subtract one record length, backing the position up by one record within the buffer.
4824
LD (IX+05H),A DD 77 05
Store the backed-up offset into FCB offset 5.
4827
If the subtraction borrowed (CARRY), the backspace crossed a sector boundary, so JUMP to the reposition tail at 482BH to step back a sector.
4829
XOR A AF
Otherwise clear Register A to signal success (no error).
482A
RET C9
Return to the caller.
482B
Reposition Tail
GOSUB to 497EH to load the current next-record number into Register Pair HL.
482E
DEC HL 2B
Decrement the record number, stepping one record backward.
482F
GOSUB to 4D95H to store the adjusted record number back and recompute the buffer sector.
4832
PUSH HL E5
Save the recomputed record number, continuing into the shared setup at 47FEH.
4835H - Rewind File (@REW)
The @REW worker (vector 443FH). Repositions the file to its first record.
4835
GOSUB to the FCB setup/validation routine at 49F3H, which clears Register A to zero and prepares the file control block.
4838
LD B,A 47
Copy the zero into Register B, the high byte of the target record number.
4839
LD C,A 4F
Copy the zero into Register C, the low byte, so Register Pair BC is record number 0.
483A
JUMP to the position-setup code at 47FAH to make record 0 the current position.
483CH - Position To End Of File (@PEOF)
The @PEOF worker (vector 4448H). Positions the file to its last record so the next write appends.
483C
GOSUB to the FCB setup routine at 49F3H.
483F
LD C,(IX+0CH) DD 4E 0C
Load Register C with the low byte of the end record number from FCB offset 0C.
4842
LD B,(IX+0DH) DD 46 0D
Load Register B with the high byte from FCB offset 0D, so Register Pair BC holds the end record number.
4845
OR (IX+08H) DD B6 08
OR the end-of-file byte offset at FCB offset 08 into Register A to test whether the file is completely empty.
4848
If the file is empty (Z), JUMP to 47FAH leaving the position at record 0.
484A
DEC BC 0B
Otherwise step Register Pair BC back one, since the end record number counts one past the last record.
484B
JUMP to 47FAH to make that last record the current position.
484DH - Byte Position To Record Converter
Converts a byte position into a record number and remaining offset by dividing by the logical record length, using the divide routine at 4BA9H. Reached from the random-access vectors and from the write positioner.
484D
GOSUB to the FCB setup routine at 49F3H.
4850
GOSUB to 497EH to load the file's next-record number into Register Pair HL.
4853
GOSUB to 4D92H to fold in the current buffer offset, forming the absolute byte position.
4856
LD E,(IX+09H) DD 5E 09
Load Register E with the logical record length from FCB offset 09, the divisor.
4859
LD A,E 7B
Copy the record length into Register A to test it.
485A
OR A B7
Test the record length; zero marks a byte-addressed (variable-length) file.
485B
If the record length is zero (Z), JUMP to 4873H, since no division is needed.
485D
INC C 0C
Increment Register C to test it against zero next.
485E
DEC C 0D
Decrement it again, setting the Z flag according to the original value of Register C.
485F
If Register C was zero (Z), JUMP to 4862H.
4861
DEC HL 2B
Otherwise decrement the byte position in Register Pair HL by one for the partial count.
4862
GOSUB to the divide routine at 4BA9H, dividing the byte position (Register Pair HL) by the record length (Register E), returning a quotient and remainder.
4865
LD B,L 45
Save the quotient low byte in Register B.
4866
LD D,H 54
Save the quotient high byte in Register D.
4867
LD H,A 67
Move the remainder into Register H for the second division stage.
4868
LD L,C 69
Move Register C into Register L.
4869
LD A,E 7B
Reload the record length into Register A.
486A
GOSUB to 4BA9H again to complete the division across the two halves of the position.
486D
LD H,B 60
Recombine the quotient high byte from Register B into Register H.
486E
OR A B7
Test the final remainder in Register A.
486F
If the position falls exactly on a record boundary (remainder zero, Z), JUMP to 4872H.
4871
INC HL 23
Otherwise round the record number up by one to cover the partial record.
4872
LD A,D 7A
Load Register A with the record-number byte from Register D.
4873
POP BC C1
Restore the caller's return context from the stack into Register Pair BC.
4874
EX (SP),HL E3
Exchange Register Pair HL with the top of stack, placing the computed record number where the caller expects it and recovering the return address.
4875
PUSH BC C5
Restore Register Pair BC to the stack for the return.
4876
OR A B7
Clear the carry flag to signal success.
4877
RET C9
Return with the record number computed.
4878H - File-Length From End Record
Random-access setup (vector 445DH). Loads the end record number and end-of-file byte offset, then reuses the converter above to compute the file's byte length.
4878
GOSUB to the FCB setup routine at 49F3H.
487B
LD L,(IX+0CH) DD 6E 0C
Load Register L with the end record number low byte from FCB offset 0C.
487E
LD H,(IX+0DH) DD 66 0D
Load Register H with the high byte from FCB offset 0D, so Register Pair HL holds the end record number.
4881
LD C,(IX+08H) DD 4E 08
Load Register C with the end-of-file byte offset in the last sector from FCB offset 08.
4884
JUMP into the converter at 4856H to combine these into the total byte length.
4886H - Flush End-Of-File To Directory Entry
The worker the 444EH vector reaches. Despite the Model III equate naming that vector @MULT, its target is not a multiply: it reads this file's directory entry, writes the current end-of-file offset and end record number into it, and writes the entry back. This keeps the on-disk directory in step with the file's size.
4886
GOSUB to the FCB setup routine at 49F3H.
4889
GOSUB to 4993H to confirm the file is open with write access; it returns NZ on failure.
488C
LD B,(IX+07H) DD 46 07
Load Register B with the high byte of the directory-entry locator from FCB offset 07.
488F
LD C,(IX+06H) DD 4E 06
Load Register C with the low byte of the directory locator from FCB offset 06, so Register Pair BC identifies this file's directory entry.
4892
GOSUB to the read-directory-sector routine at 4B10H, which reads the directory sector holding this entry into a buffer and points Register Pair HL at the entry.
4895
RET NZ C0
Return if the directory read failed (NZ).
4896
INC L 2C
Advance the entry pointer to the end-of-file offset field within the directory entry.
4897
INC L 2C
Continue advancing.
4898
INC L 2C
The entry pointer now addresses the directory entry's end-of-file-offset byte.
4899
LD A,(IX+08H) DD 7E 08
Load Register A with the file's end-of-file byte offset from FCB offset 08.
489C
LD (HL),A 77
Store it into the directory entry's end-of-file-offset field.
489D
LD DE,0011H 11 11 00
Load Register Pair DE with 17, the offset from that field to the entry's end-record-number field.
48A0
ADD HL,DE 19
Advance the entry pointer to the end-record-number field.
48A1
LD A,(IX+0CH) DD 7E 0C
Load Register A with the end record number low byte from FCB offset 0C.
48A4
LD (HL),A 77
Store it into the directory entry.
48A5
INC HL 23
Advance to the high byte of the field.
48A6
LD A,(IX+0DH) DD 7E 0D
Load Register A with the end record number high byte from FCB offset 0D.
48A9
LD (HL),A 77
Store it, completing the directory entry's size fields.
48AA
JUMP to the write-directory-sector routine at 4B1FH to write the updated entry back to disk and return through it.
48ADH - Read Logical Record (@READ)
The @READ worker (vector 4436H). Copies one logical record from the file into the caller's buffer, one byte at a time. On entry Register Pair HL points at the caller's buffer.
48AD
GOSUB to the FCB setup routine at 49F3H.
48B0
PUSH HL E5
Save the caller's buffer pointer.
48B1
GOSUB to 4993H to confirm the file is open with read access.
48B4
POP HL E1
Restore the buffer pointer.
48B5
RET NZ C0
Return if the access check failed (NZ).
48B6
LD B,(IX+09H) DD 46 09
Load Register B with the logical record length from FCB offset 09, the number of bytes to read.
48B9
LD A,B 78
Copy the length into Register A to test it.
48BA
OR A B7
Test the record length.
48BB
If the record length is zero (Z), JUMP to 494FH to handle the byte/variable-length read path.
48BE
PUSH HL E5
Read Loop
Save the buffer pointer across the byte fetch.
48BF
PUSH BC C5
Save the remaining byte count.
48C0
GOSUB to the get-next-byte routine at 48FCH, which returns the next byte of the file in Register A, reading a new sector when needed.
48C3
POP BC C1
Restore the byte count.
48C4
POP HL E1
Restore the buffer pointer.
48C5
RET NZ C0
Return if the byte fetch reported an error such as end of file (NZ).
48C6
LD (HL),A 77
Store the fetched byte into the caller's buffer.
48C7
INC HL 23
Advance the buffer pointer.
48C8
Read Loop
Decrement the byte count in Register B and LOOP BACK to 48BEH until the whole record has been copied.
48CA
RET C9
Return with the record read.
48CBH - Write Logical Record (@WRITE) and Verify (@VER)
The @WRITE worker (vector 4439H) and the @VER worker (vector 443CH), which share the same loop. The verify flag at 49CFH selects a plain write or a write-and-verify. On entry Register Pair HL points at the caller's buffer.
48CB
GOSUB to the FCB setup routine at 49F3H. On return Register A is zero (a plain write request).
48CE
LD (49CFH),A 32 CF 49
Store Register A into the verify-flag byte at 49CFH: zero for a plain write, non-zero (set by the verify entry below) for write-and-verify. The sector-write code reads this flag to decide whether to read the sector back.
48D1
LD B,(IX+09H) DD 46 09
Load Register B with the logical record length from FCB offset 09.
48D4
LD A,B 78
Copy the length into Register A to test it.
48D5
OR A B7
Test the record length; the following byte handles the zero (variable-length) case in the code that continues past this fragment.
48D9
LD A,(HL) 7E
Write Loop
Load the next byte from the caller's buffer into Register A.
48DA
INC HL 23
Advance the buffer pointer.
48DB
PUSH HL E5
Save the buffer pointer across the byte store.
48DC
PUSH BC C5
Save the remaining byte count.
48DD
GOSUB to the put-next-byte routine at 4919H, which stores the byte in Register A into the file buffer, writing a full sector to disk when the buffer fills.
48E0
POP BC C1
Restore the byte count.
48E1
POP HL E1
Restore the buffer pointer.
48E2
RET NZ C0
Return if the byte store reported an error such as a full disk (NZ).
48E3
Write Loop
Decrement the byte count and LOOP BACK to 48D9H until the whole record has been written.
48E5
RET C9
Return with the record written.
48E6H - Write With Verify Entry (@VER)
The @VER worker (vector 443CH). Identical to the write path but sets the verify flag so each written sector is read back and checked.
48E6
GOSUB to the FCB setup routine at 49F3H.
48E9
INC A 3C
Increment Register A from zero to one, the non-zero verify flag.
48EA
JUMP to 48CEH to store the verify flag and run the shared write loop, so each sector written is verified.
48ECH - File Byte I/O (Device-Driver Entry)
Reached from the device-driver linkage when a file is used as a byte device. Register B selects the operation (a get for values below 2, a put otherwise). This entry fetches the next byte from the file's buffer, refilling the buffer from disk when the current sector is exhausted. On entry Register Pair IX points at the FCB.
48EC
PUSH IX DD E5
Push the FCB pointer so it can be moved into Register Pair DE.
48EE
POP DE D1
Pop the FCB pointer into Register Pair DE for the setup routine.
48EF
GOSUB to the FCB prologue at 49F3H, which establishes Register Pair IX as the working FCB and saves the caller's registers.
48F2
SET 7,(IX+01H) DD CB 01 FE
Set bit 7 of the FCB flags at offset 1, marking the file open/active.
48F6
LD A,B 78
Load the driver operation code from Register B.
48F7
CP 02H FE 02
Compare it against 2; values of 2 or more request a put (write) rather than a get.
48F9
LD A,C 79
Load Register A with the byte to write (from Register C) in case this is a put.
48FA
If the operation is a put (code 2 or more, NO CARRY), JUMP to the put-byte routine at 4919H.
48FC
GOSUB to the end-of-file check at 4A1DH; it returns NZ if the current position is at end of file.
48FF
RET NZ C0
Return if at end of file (NZ), signalling no byte available.
4900
BIT 5,(IX+01H) DD CB 01 6E
Test bit 5 of the FCB flags, set when the buffer currently holds a valid sector.
4904
If a sector is buffered (NZ) and the position has advanced past it, GOSUB to 494FH to flush and load the next sector.
4907
RET NZ C0
Return if that sector operation reported an error (NZ).
4908
GOSUB to 4985H to compute the current byte's address (buffer base plus offset) into Register Pair DE.
490B
LD A,(DE) 1A
Load the next file byte from the buffer into Register A.
490C
INC (IX+05H) DD 34 05
Advance the buffer byte offset at FCB offset 5 to the next byte; a wrap to zero sets the Z flag.
490F
If the offset wrapped past the end of the sector (Z), GOSUB to 4914H to mark that the next access must load a new sector.
4912
CP A BF
Compare Register A with itself to set the Z flag, signalling success with the fetched byte still in Register A.
4913
RET C9
Return with the byte in Register A.
4914H - Mark Buffer Holds Record
Sets the FCB flag that records the buffer now holds the current sector.
4914
SET 5,(IX+01H) DD CB 01 EE
Set bit 5 of the FCB flags at offset 1, marking that the buffer holds a valid sector of the file.
4919H - Put Byte To File
Stores one byte into the file's buffer, flushing the buffer to disk when it fills and extending the recorded end of file. On entry Register A holds the byte and Register Pair IX the FCB.
4919
PUSH AF F5
Save the byte to be written.
491A
BIT 5,(IX+01H) DD CB 01 6E
Test bit 5 of the FCB flags, set when the buffer holds a sector.
491E
If a sector is buffered (NZ), GOSUB to 4953H to flush it and load the sector for the current position.
4921
If the buffer is ready (Z), JUMP to 4926H to store the byte.
4923
EX (SP),HL E3
Otherwise an error occurred; exchange HL with the stacked byte to unwind.
4924
POP HL E1
Discard the saved byte and restore HL.
4925
RET C9
Return with the error status.
4926
GOSUB to 4985H to compute the current byte's address in the buffer into Register Pair DE.
4929
POP AF F1
Restore the byte to be written.
492A
LD (DE),A 12
Store the byte into the buffer at the current position.
492B
SET 4,(IX+01H) DD CB 01 E6
Set bit 4 of the FCB flags, marking the buffer modified so it will be written back.
492F
INC (IX+05H) DD 34 05
Advance the buffer byte offset at FCB offset 5; a wrap to zero sets the Z flag.
4932
PUSH AF F5
Save the wrap status.
4933
If the offset wrapped past the sector end (Z), GOSUB to 4914H to flag that a new sector is needed.
4936
GOSUB to the end-of-file check at 4A1DH to see whether this write is past the previous end of file.
4939
If the write extended the file (NZ), JUMP to 4941H to update the end-of-file marks.
493B
BIT 6,(IX+01H) DD CB 01 76
Otherwise test bit 6 of the FCB flags, the write-pending flag.
493F
If a write is pending (NZ), JUMP to 494AH.
4941
LD (IX+08H),C DD 71 08
Store the byte offset within the last sector (Register C) into FCB offset 8, the new end-of-file offset.
4944
LD (IX+0CH),L DD 75 0C
Store the low byte of the new end record number into FCB offset 0C.
4947
LD (IX+0DH),H DD 74 0D
Store the high byte into FCB offset 0D, recording the extended end of file.
494A
POP AF F1
Restore the wrap status.
494B
If the sector filled (Z), JUMP to 49A1H to write the full buffer to disk.
494D
XOR A AF
Otherwise clear Register A to signal success.
494FH - Flush And Load Sector For Position
Ensures the buffer holds the sector for the current record position, writing a modified buffer back first, then reading the required sector. Register A access-level bits in the FCB decide whether writing is allowed.
494F
GOSUB to the end-of-file check at 4A1DH.
4952
RET NZ C0
Return if at end of file (NZ).
4953
LD A,(IX+01H) DD 7E 01
Load the FCB flags from offset 1.
4956
AND 07H E6 07
Keep the low three bits, the file's access level.
4958
CP 06H FE 06
Compare the access level against 6 (the update/write threshold).
495A
If the access level is below 6 (CARRY, read-type access), JUMP to 495EH to just read the sector.
495C
Otherwise JUMP to 49B4H, which returns an access-violation error.
495E
GOSUB to the extent-translation routine at 4A41H to convert the record position into a physical track and sector.
4961
RET NZ C0
Return if the translation failed (NZ).
4962
RES 5,(IX+01H) DD CB 01 AE
Clear bit 5 of the FCB flags, marking the buffer no longer valid while the new sector is read.
4966
LD L,(IX+03H) DD 6E 03
Load the low byte of the file buffer address from FCB offset 3.
4969
LD H,(IX+04H) DD 66 04
Load the high byte from FCB offset 4, so Register Pair HL points at the buffer.
496C
GOSUB to the RDSECT primitive at 4777H to read the required sector into the buffer.
496F
If the read succeeded (Z), JUMP to 4974H.
4971
CP 06H FE 06
Otherwise compare the error against 6 (a normal not-found-at-eof condition).
4973
RET NZ C0
Return the error unless it was that condition (NZ).
4974
INC (IX+0AH) DD 34 0A
Increment the low byte of the next-record number at FCB offset 0A to advance to the following sector.
4977
If that did not wrap (NZ), JUMP to 497CH.
4979
INC (IX+0BH) DD 34 0B
Otherwise carry into the high byte at FCB offset 0B.
497C
XOR A AF
Clear Register A to signal success.
497EH - Get Next Record Number / Current Byte Address / Access Check
Three short FCB helpers: fetch the next record number, compute the current byte address in the buffer, and test the open/write access flags.
497E
LD L,(IX+0AH) DD 6E 0A
Load the low byte of the next record number from FCB offset 0A into Register L.
4981
LD H,(IX+0BH) DD 66 0B
Load the high byte from FCB offset 0B, so Register Pair HL holds the next record number.
4985
LD A,(IX+05H) DD 7E 05
Load the current buffer byte offset from FCB offset 5.
4988
ADD A,(IX+03H) DD 86 03
Add the low byte of the buffer address from FCB offset 3.
498B
LD E,A 5F
Store the resulting low address byte in Register E.
498C
LD A,(IX+04H) DD 7E 04
Load the high byte of the buffer address from FCB offset 4.
498F
ADC A,00H CE 00
Add any carry from the low byte, completing the high address byte.
4991
LD D,A 57
Store it in Register D, so Register Pair DE addresses the current byte in the buffer.
4993
LD A,(IX+01H) DD 7E 01
Load the FCB flags from offset 1.
4996
AND 90H E6 90
Keep bit 7 (open) and bit 4 (buffer modified/write-active).
4998
CP 90H FE 90
Test whether both bits are set, meaning the file is open with an active write.
499A
If both are set (Z), JUMP to 49A1H to flush the modified buffer.
499C
Otherwise JUMP to 497CH to return success with nothing to flush.
499EH - Flush Modified Buffer To Disk
Writes a modified file buffer back to its sector (with optional verify), updates the position, and, when the file grew, flushes the new size to the directory. Entered directly (vector at 4457H) or from the helpers above.
499E
GOSUB to the FCB prologue at 49F3H to set up the working FCB.
49A1
GOSUB to 497EH to load the next record number into Register Pair HL.
49A4
DEC HL 2B
Step back to the record number of the sector currently in the buffer.
49A5
LD (IX+0AH),L DD 75 0A
Store the low byte of that record number into FCB offset 0A.
49A8
LD (IX+0BH),H DD 74 0B
Store the high byte into FCB offset 0B.
49AB
LD A,(IX+01H) DD 7E 01
Load the FCB flags.
49AE
AND 07H E6 07
Keep the access level.
49B0
CP 05H FE 05
Compare the access level against 5, the minimum that allows writing.
49B2
If the access level is at least 5 (CARRY clear means below; here CARRY set means below 5), JUMP to 49B8H when writing is permitted.
49B4
LD A,25H 3E 25
Otherwise load error code 25H (write-protected / access-level violation).
49B6
OR A B7
Set the flags for a non-zero (error) return.
49B7
RET C9
Return the access error.
49B8
GOSUB to the extent-translation routine at 4A41H to find the physical track and sector for this record.
49BB
RET NZ C0
Return if the translation failed (NZ).
49BC
LD L,(IX+03H) DD 6E 03
Load the buffer address low byte from FCB offset 3.
49BF
LD H,(IX+04H) DD 66 04
Load the high byte from FCB offset 4, so Register Pair HL points at the buffer to write.
49C2
RES 4,(IX+01H) DD CB 01 A6
Clear bit 4 of the FCB flags, the buffer-modified flag, since the buffer is about to be written out.
49C6
SET 2,(IX+00H) DD CB 00 D6
Set bit 2 of the FCB type byte at offset 0, marking the file as having been updated on disk.
49CA
GOSUB to the WRSECT primitive at 4763H to write the buffer to its sector.
49CD
RET NZ C0
Return if the write failed (NZ).
49CF
LD A,00H 3E 00
Self-Modifying Code
Load the verify flag; the 00H operand at 49CFH is set by the write/verify entry (48CEH) to zero for a plain write or non-zero for write-and-verify.
49D1
OR A B7
Test the verify flag.
49D2
If verification is requested (NZ), GOSUB to the VERSEC primitive at 4772H to read the sector back and confirm it.
49D5
RET NZ C0
Return if the verify failed (NZ).
49D6
GOSUB to 4974H to advance the next-record number past the sector just written.
49D9
GOSUB to the end-of-file check at 4A1DH.
49DC
DEC A 3D
Adjust the returned status by one for the flag test that follows.
49DD
AND (IX+01H) DD A6 01
AND with the FCB flags to combine the end-of-file result with the file state.
49DF
AND 40H E6 40
Isolate bit 6, the write-pending flag, in the combined result.
49E1
If the file did not grow past its recorded end (NZ here), JUMP to 497CH to return success.
49E3
LD (IX+0CH),L DD 75 0C
Otherwise store the low byte of the new end record number into FCB offset 0C.
49E6
LD (IX+0DH),H DD 74 0D
Store the high byte into FCB offset 0D.
49E9
BIT 3,(IX+01H) DD CB 01 5E
Test bit 3 of the FCB flags, set when the directory must be updated with the new size.
49ED
If a directory update is due (NZ), GOSUB to the directory EOF-flush code at 488CH.
49F0
RET C9
Return with the buffer flushed and the size recorded.
49F1H - File Operation Prologue (FCB Setup)
The common prologue every file operation calls. It has two overlapping entry points: entry at 49F1H forces the "already open" state, while entry at 49F3H first checks the FCB's open flag. Both save the caller's registers, make Register Pair IX the working FCB, and arrange for a register-restoring return, so the operation body runs with IX pointing at the file control block. On entry Register Pair DE points at the FCB.
49F1
SCF 37
Alternate entry point that sets the carry flag to assert the file is open, skipping the open check below.
The byte at 49F2H is D2H. Entered at 49F1H it reads as a JP NC that the set carry never takes, so execution falls through to 49F5H. Entered at the primary point 49F3H, the same bytes are executed as the two instructions below, performing the open check. This is deliberate overlapping code.
49F3
LD A,(DE) 1A
Primary entry point. Load Register A with the FCB type/attribute byte from FCB offset 0 (addressed by Register Pair DE).
49F4
RLCA 07
Rotate Register A left, moving the FCB type byte's high bit (the file-open flag) into the carry flag.
49F5
EX (SP),HL E3
Exchange Register Pair HL with the return address on the stack, so HL holds the caller's return address.
49F6
LD (430CH),HL 22 0C 43
Save that return address in the file-engine scratch word at 430CH.
49F9
LD (430AH),DE ED 53 0A 43
Save the FCB pointer (Register Pair DE) in the file-engine scratch word at 430AH.
49FD
EX (SP),HL E3
Exchange HL with the stack again, restoring the caller's HL and putting the return address back on the stack.
49FE
If the file-open flag was clear (NO CARRY from the rotate), JUMP to 4A0FH to return a file-not-open error.
4A00
POP AF F1
Discard the saved flags, leaving the caller's return address on the stack.
4A01
PUSH DE D5
Push the FCB pointer so it can be placed in the index register.
4A02
EX (SP),IX DD E3
Exchange Register Pair IX with the stacked FCB pointer, so IX now addresses the FCB and the caller's IX is saved on the stack.
4A04
PUSH HL E5
Save the caller's Register Pair HL.
4A05
PUSH DE D5
Save Register Pair DE.
4A06
PUSH BC C5
Save Register Pair BC.
4A07
PUSH HL E5
Push HL again as a placeholder that the exchange below turns into the operation's return address.
4A08
LD HL,4A14H 21 14 4A
Load Register Pair HL with the address of the register-restoring cleanup routine at 4A14H.
4A0B
EX (SP),HL E3
Exchange HL with the stack, so the cleanup address 4A14H becomes the operation's return address and HL is restored.
4A0C
PUSH AF F5
Push the flags so the following return lands at the caller with them intact.
4A0D
XOR A AF
Clear Register A to signal a successful setup.
4A0E
RET C9
Return to the caller. The operation body now runs with IX pointing at the FCB, and its own RET will pass through the cleanup at 4A14H.
4A0F
POP AF F1
File-not-open path: discard the saved flags.
4A10
LD A,26H 3E 26
Load error code 26H, the file-not-open error.
4A12
OR A B7
Set the flags to a non-zero (error) result.
4A13
RET C9
Return the error to the caller.
4A14
POP BC C1
Cleanup Return
Reached when an operation body returns. Restore Register Pair BC saved by the prologue.
4A15
POP DE D1
Restore Register Pair DE.
4A16
POP HL E1
Restore Register Pair HL.
4A17
POP IX DD E1
Restore the caller's Register Pair IX.
4A19
RET C9
Return to the caller of the file operation with all registers restored and the operation's status in Register A.
4A1AH - End-Of-File Check
The worker the 444BH vector reaches, also called throughout the file engine. It reports whether the current record position has reached the file's end by comparing the next record number and byte offset against the recorded end record number and end-of-file offset. Despite the Model III equate naming its vector @FEXT, its function here is the end-of-file test.
4A1A
GOSUB to the FCB prologue at 49F3H to establish the working FCB, then fall into the check.
4A1D
GOSUB to 497EH to load the current next record number into Register Pair HL.
4A20
GOSUB to 4D8FH to load the current buffer byte offset into Register C for the comparison.
4A23
LD A,H 7C
Load the high byte of the current record number.
4A24
CP (IX+0DH) DD BE 0D
Compare it against the high byte of the end record number at FCB offset 0D.
4A27
If the high bytes differ (NZ), the position is not at end of file, so JUMP to 4A37H with that result.
4A29
LD A,L 7D
Load the low byte of the current record number.
4A2A
CP (IX+0CH) DD BE 0C
Compare it against the low byte of the end record number at FCB offset 0C.
4A2D
If the low bytes differ (NZ), the position is not the end record, so JUMP to 4A37H.
4A2F
DEC C 0D
The record numbers match; decrement the current byte offset in Register C for the boundary comparison.
4A30
LD A,(IX+08H) DD 7E 08
Load the end-of-file byte offset in the last sector from FCB offset 8.
4A33
DEC A 3D
Decrement it to align with the zero-based comparison.
4A34
SUB C 91
Subtract the current offset, so the sign and zero flags report whether the position has reached the end-of-file byte.
4A35
CCF 3F
Complement the carry so it reflects at-or-past end of file for the caller.
4A36
INC BC 03
Restore Register Pair BC (undoing the earlier decrement of Register C).
4A37
POP HL E1
Discard the prologue's stacked entry so this check returns directly to its caller.
4A38
LD A,1DH 3E 1D
Load error code 1DH, the past-end-of-file error, as the candidate result.
4A3A
If the position was not the end record (NZ from the compares above), JUMP to 4A3EH.
4A3C
DEC A 3D
Otherwise adjust the error code to 1CH, the exactly-at-end-of-file code.
4A3D
RET C9
Return with the end-of-file status.
4A3E
RET NC D0
If not at or past end of file (NO CARRY), return with the not-at-eof status.
4A3F
XOR A AF
Otherwise clear Register A to report a clean not-past-eof result.
4A41H - Translate File Record To Physical Track And Sector
Converts the file's current record position into the physical track and sector that hold it. The record number is divided by the sectors-per-granule to get a granule index, the file's extent table (starting at FCB offset 0EH) is walked to find which allocated run contains that granule, and the drive geometry is applied to produce the track (Register D) and sector (Register E). When the position is past the last allocated granule, a new granule is requested through the allocate supervisor call. Several instruction operands here are patched at run time.
4A41
GOSUB to 497EH to load the current record number (FCB offset 0A/0B) into Register Pair HL.
4A44
GOSUB to 4797H to read this drive's sectors-per-granule geometry byte from its control block into Register A.
4A47
AND 1FH E6 1F
Keep the low five bits, the count of sectors per granule minus one.
4A49
INC A 3C
Add one to form the true sectors-per-granule divisor.
4A4A
GOSUB to the 16-bit divide at 4BA9H, dividing the record number by the sectors-per-granule: Register Pair HL becomes the granule index and Register A the sector offset within that granule.
4A4D
LD (4B0CH),A 32 0C 4B
Save the sector-within-granule offset in the scratch byte at 4B0CH.
4A50
PUSH IX DD E5
Push the FCB pointer to move it into Register Pair HL.
4A52
EX (SP),HL E3
Exchange with the stack so Register Pair HL holds the FCB address and the granule index is saved on the stack.
4A53
LD BC,000EH 01 0E 00
Load Register Pair BC with 0EH, the FCB offset of the extent table.
4A56
ADD HL,BC 09
Add it to the FCB address so Register Pair HL points at the first extent entry.
4A57
POP BC C1
Pop the granule index back into Register Pair BC as the target granule.
4A58
LD A,05H 3E 05
Load Register A with 5, the maximum number of extents to scan.
4A5A
LD DE,0000H 11 00 00
Clear Register Pair DE, the running total of granules covered by scanned extents.
4A5D
PUSH AF F5
Extent Scan Loop
Save the remaining-extent count.
4A5E
LD A,(HL) 7E
Load the current extent's starting-cylinder byte.
4A5F
INC HL 23
Advance to the extent's granule-count field.
4A60
INC A 3C
Increment the cylinder byte; an unused extent holds FFH, which becomes zero here.
4A61
If the extent is unused (was FFH, now Z), JUMP to 4A6EH to move past it.
4A63
PUSH HL E5
Save the extent pointer.
4A64
LD H,D 62
Copy the running granule total into Register Pair HL for comparison (high byte).
4A65
LD L,E 6B
Copy the low byte, so HL holds the running total.
4A66
XOR A AF
Clear the carry flag for the subtraction.
4A67
SBC HL,BC ED 42
Subtract the target granule from the running total, testing whether the target lies within the extents scanned so far.
4A69
If the running total is still below the target (CARRY), the target is farther on, so JUMP to 4A79H to add this extent's granules.
4A6B
POP HL E1
Restore the extent pointer.
4A6C
If the running total exactly reaches the target (Z), the granule sits at this extent boundary, so JUMP to 4AB6H to compute its address.
4A6E
INC HL 23
Advance past the granule-count field's first byte.
4A6F
POP AF F1
Restore the remaining-extent count.
4A70
DEC A 3D
Count down one extent.
4A71
If all extents have been scanned without finding the granule (Z), JUMP to 4A8CH to allocate a new granule.
4A73
LD E,(HL) 5E
Load the low byte of this extent's cumulative granule count into Register E.
4A74
INC HL 23
Advance to the high byte.
4A75
LD D,(HL) 56
Load the high byte into Register D, so Register Pair DE is the new running total.
4A76
INC HL 23
Advance to the next extent entry.
4A77
Extent Scan Loop
LOOP BACK to 4A5DH to test the next extent.
4A79
INC H 24
Adjust the high byte of the difference for the within-extent offset calculation.
4A7A
LD A,L 7D
Take the low byte of the difference (how far into this extent the target granule is).
4A7B
POP HL E1
Restore the extent pointer.
4A7C
If the offset spans more than one byte's worth of granules, JUMP back to 4A6EH to keep scanning.
4A7E
PUSH DE D5
Save the running total.
4A7F
LD E,A 5F
Save the within-extent granule offset in Register E.
4A80
LD A,(HL) 7E
Load this extent's granule-count byte.
4A81
AND 1FH E6 1F
Keep the low five bits, the number of granules in the extent.
4A83
ADD A,E 83
Add the within-extent offset to test whether the target granule falls inside this extent's run.
4A84
LD A,E 7B
Recover the within-extent offset into Register A.
4A85
POP DE D1
Restore the running total.
4A86
If the target lies beyond this extent's granules (NO CARRY), JUMP back to 4A6EH to continue with the next extent.
4A88
NEG ED 44
Negate the offset to form the granule's position relative to the extent's start.
4A8A
JUMP to 4AB6H to compute the physical address for the found granule.
4A8C
LD A,(4B0CH) 3A 0C 4B
Allocation path. Load the saved sector-within-granule offset from 4B0CH.
4A8F
LD (4AABH),A 32 AB 4A
Self-Modifying Code
Store it into the operand of the instruction at 4AAAH, so the offset is restored after the allocation call.
4A92
LD A,(430EH) 3A 0E 43
Load the current overlay number from 430EH (the allocate call brings in an overlay).
4A95
LD (4A9DH),A 32 9D 4A
Self-Modifying Code
Store it into the operand at 4A9CH so the overlay number can be checked after allocation.
4A98
GOSUB to 4AB3H to issue the granule-allocation supervisor request.
4A9B
PUSH AF F5
Save the allocation result.
4A9C
LD A,00H 3E 00
Self-Modifying Code
Load the overlay number saved at 4A9DH (the 00H operand was patched at 4A95H).
4A9E
AND 8FH E6 8F
Keep the high bit and low nibble, isolating the overlay identity.
4AA0
CP 83H FE 83
Compare against 83H to check whether the allocation overlay is the expected one.
4AA2
If it matches (Z), GOSUB to 4AB5H to re-enter the supervisor path for the follow-up allocation step.
4AA5
POP AF F1
Restore the allocation result.
4AA6
RET NZ C0
Return if the allocation failed (NZ, for example a full disk).
4AA7
LD (4AE5H),A 32 E5 4A
Self-Modifying Code
Store the allocation status into the operand at 4AE4H for the address computation below.
4AAA
LD A,00H 3E 00
Self-Modifying Code
Load the sector-within-granule offset saved at 4AABH (patched at 4A8FH).
4AAC
LD (4B0CH),A 32 0C 4B
Restore that offset to the scratch byte at 4B0CH.
4AAF
JUMP to 4ADEH to compute the track and sector from the newly allocated granule.
4AB1
Otherwise JUMP to 4AD5H to store the extent update first.
4AB3
LD A,9AH 3E 9A
Load supervisor request code 9AH, the allocate-granule / extend-file function.
4AB5
RST 28H EF
Invoke the supervisor dispatcher to carry out the granule allocation through the appropriate overlay.
4AB6
LD (4AE5H),A 32 E5 4A
Self-Modifying Code
Found-granule path. Store the within-extent offset into the operand at 4AE4H for the sector computation below.
4AB9
LD B,(HL) 46
Load the high byte of the extent's starting cylinder into Register B.
4ABA
DEC HL 2B
Step back to the low byte of the extent field.
4ABB
LD C,(HL) 4E
Load the low byte into Register C, so Register Pair BC holds the extent's starting cylinder/granule value.
4ABC
INC HL 23
Restore the extent pointer.
4ABD
POP AF F1
Recover the remaining-extent count from the scan loop's stack frame.
4ABE
CPL 2F
Complement the within-extent offset to convert it toward the count of granules from the extent start.
4ABF
ADD A,04H C6 04
Add 4, adjusting for the fixed extent-table layout.
4AC1
If no carry, the extent entry does not need shifting, so JUMP to 4ADCH to use it directly.
4AC3
INC A 3C
Adjust the shift count.
4AC5
RLCA 07
Double again, giving a byte count of four bytes per extent entry to move.
4AC6
PUSH BC C5
Save the extent value.
4AC7
PUSH DE D5
Save the running total.
4AC8
LD C,A 4F
Move the byte count into Register C.
4AC9
LD B,00H 06 00
Clear Register B, so Register Pair BC is the byte count for the block move.
4ACB
EX DE,HL EB
Move the extent pointer into Register Pair DE as the move destination.
4ACC
LD HL,0FFFCH 21 FC FF
Load Register Pair HL with -4, the offset to the source three entries earlier.
4ACF
ADD HL,DE 19
Compute the source address (the destination minus four).
4AD0
LDDR ED B8
Block-move BC bytes downward from (HL) to (DE), shifting the extent-table entries to open a slot for the new extent.
4AD2
EX DE,HL EB
Move the destination pointer back into Register Pair HL.
4AD3
POP DE D1
Restore the running total.
4AD4
POP BC C1
Restore the extent value.
4AD5
LD (HL),B 70
Store the extent value high byte into the table.
4AD7
LD (HL),C 71
Store the extent value low byte.
4AD9
LD (HL),D 72
Store the running-total high byte.
4ADB
LD (HL),E 73
Store the running-total low byte, completing the new extent entry.
4ADC
LD H,B 60
Move the extent value high byte into Register H.
4ADD
LD L,C 69
Move the low byte into Register L, so Register Pair HL is the granule's cylinder value.
4ADE
LD A,H 7C
Take the high byte of the granule value to extract the cylinder-group bits.
4ADF
RLCA 07
Rotate left to align the granule bits.
4AE0
RLCA 07
Rotate left again.
4AE1
RLCA 07
Rotate left a third time, moving the top three bits into the low three bit positions.
4AE2
AND 07H E6 07
Keep those three bits, the granule's index within its cylinder.
4AE4
ADD A,00H C6 00
Self-Modifying Code
Add the within-granule offset (the 00H operand at 4AE5H was patched at 4AB6H/4AA7H).
4AE6
LD E,A 5F
Store the partial sector index in Register E.
4AE7
GOSUB to 4797H to read the drive's sectors-per-granule geometry byte again into Register A.
4AEA
PUSH AF F5
Save the geometry byte.
4AEB
RLCA 07
Rotate to extract the granules-per-cylinder field.
4AED
RLCA 07
Rotate a third time, aligning the granules-per-cylinder bits.
4AEE
AND 07H E6 07
Keep the three-bit granules-per-cylinder value.
4AF0
INC A 3C
Add one to form the true granules-per-cylinder count.
4AF1
LD D,A 57
Store it in Register D as the divisor for the cylinder computation.
4AF2
LD A,04H 3E 04
Load Register A with 4, the DCT offset of the drive-flags byte.
4AF4
GOSUB to the DCTBYT helper at 479CH to read that drive-flags byte into Register A.
4AF7
BIT 5,A CB 6F
Test bit 5 of the drive flags, the double-sided-media flag.
4AF9
LD A,D 7A
Load the granules-per-cylinder count into Register A.
4AFA
If the media is single-sided (bit 5 clear, Z), JUMP past the doubling.
4AFC
RLCA 07
Otherwise double the granules-per-cylinder count to account for both sides.
4AFD
GOSUB to the 8-bit divide at 4B7BH, dividing the granule index by the granules-per-cylinder to get the cylinder number (Register A) and the granule offset within the cylinder (Register E).
4B00
ADD A,L 85
Add the directory-track base in Register L, biasing the cylinder past the reserved directory track.
4B01
LD D,A 57
Store the resulting physical track number in Register D.
4B02
POP AF F1
Restore the geometry byte.
4B03
AND 1FH E6 1F
Keep the sectors-per-granule minus one.
4B05
INC A 3C
Add one to form the sectors-per-granule multiplier.
4B06
PUSH DE D5
Save the physical track and the within-cylinder granule offset.
4B07
GOSUB to the 8-bit multiply at 4B6CH, multiplying the within-cylinder granule offset by the sectors-per-granule to get the base sector.
4B0A
POP DE D1
Restore the physical track into Register D.
4B0B
ADD A,00H C6 00
Add the sector-within-granule offset (from the scratch computation) to the base sector.
4B0D
LD E,A 5F
Store the final physical sector number in Register E, so Register Pair DE now holds the track and sector.
4B0E
XOR A AF
Clear Register A to signal success.
4B0F
RET C9
Return with the physical track in Register D and sector in Register E.
4B10H - Read Directory Sector (DIRRD)
Reads the directory sector that contains a given entry into the directory buffer and points Register Pair HL at the entry. On entry Register Pair BC holds the directory-entry locator.
4B10
PUSH DE D5
Save Register Pair DE across the read.
4B11
GOSUB to 4B37H to translate the entry locator into the directory buffer address (Register Pair HL) and the sector number (Register E).
4B14
PUSH HL E5
Save the computed entry address.
4B15
LD L,00H 2E 00
Set Register L to zero, selecting the start of the directory buffer for the sector read.
4B17
GOSUB to the read-sector-into-buffer routine at 4B45H to load the directory sector from disk.
4B1A
POP HL E1
Restore the entry address into Register Pair HL.
4B1B
LD A,11H 3E 11
Load Register A with error code 11H (directory-read error) as the default result the read routine leaves in place on success.
4B1D
POP DE D1
Restore Register Pair DE.
4B1E
RET C9
Return with HL pointing at the entry in the directory buffer.
4B1FH - Write Directory Sector (DIRWR)
Writes the directory buffer back to disk with verification and maps the controller result to a DOS error code.
4B1F
PUSH DE D5
Save Register Pair DE.
4B20
GOSUB to 4B37H to recompute the directory buffer address and sector number.
4B23
LD L,00H 2E 00
Select the start of the directory buffer.
4B25
GOSUB to the WRPROT disk primitive at 4768H to write the sector with the protect handling.
4B28
If that write succeeded (Z), GOSUB to the VERSEC primitive at 4772H to read the sector back and verify it.
4B2B
SUB 06H D6 06
Subtract 6 from the returned status to translate the drive-level code toward a DOS error code.
4B2D
POP DE D1
Restore Register Pair DE.
4B2E
RET Z C8
If the result is zero (no error), return success.
4B2F
CP 09H FE 09
Compare the residual against 9 to distinguish a lost/format error from other faults.
4B31
LD A,12H 3E 12
Load Register A with error code 12H (directory-write error) as the default returned error.
4B33
RET NZ C0
Return that error unless the residual was exactly 9 (NZ).
4B34
SUB 03H D6 03
For the residual-9 case, subtract 3 to form the specific error code.
4B36
RET C9
Return with the error code in Register A.
4B37H - Compute Directory Buffer Address
Translates a directory-entry number into its address within the directory buffer on page 42H and the sector that holds it.
4B37
GOSUB to 4B65H to fetch the directory track from the drive's control block into Register D.
4B3A
LD A,B 78
Load Register A with the entry number from Register B.
4B3B
AND 0E0H E6 E0
Keep the top three bits, which select the 32-byte entry group within a sector.
4B3D
LD L,A 6F
Use that as the low byte of the buffer address in Register L.
4B3E
LD H,42H 26 42
Set Register H to 42H, the directory buffer page, so Register Pair HL addresses the entry.
4B40
XOR B A8
Exclusive-OR with the entry number to isolate the low five bits, the entry's sector index.
4B41
ADD A,02H C6 02
Add 2 to skip the directory's leading system sectors, giving the physical sector number.
4B43
LD E,A 5F
Store the sector number in Register E.
4B44
RET C9
Return with the buffer address in HL, directory track in D, sector in E.
4B45H - Read Sector Into Directory Buffer (RDSSEC)
Reads the directory track and sector into the directory buffer.
4B45
GOSUB to 4B5EH to perform the sector read and translate the result code.
4B48
RET Z C8
Return if the read succeeded (Z).
4B49
PUSH DE D5
Save Register Pair DE before the retry read.
4B4A
LD DE,0000H 11 00 00
Clear Register Pair DE to select track 0/sector 0 addressing for the retry.
4B4D
GOSUB to the RDSECT primitive at 4777H to re-read the sector.
4B50
POP DE D1
Restore Register Pair DE.
4B51
RET NZ C0
Return if the retry also failed (NZ).
4B52
PUSH HL E5
Save Register Pair HL.
4B53
INC HL 23
Advance HL to the directory-header field that records the number of cylinders.
4B54
INC HL 23
Continue advancing to that field.
4B55
LD D,(HL) 56
Load Register D with the directory cylinder count from the header.
4B56
LD H,09H 26 09
Set Register H to 9, the DCT offset of the directory-cylinder field.
4B58
GOSUB to the DCT address calculator at 47A5H to address that field in the active drive's control block.
4B5B
LD L,A 6F
Point Register L at the field.
4B5C
LD (HL),D 72
Store the directory cylinder count into the DCT so later seeks know the disk geometry.
4B5D
POP HL E1
Restore Register Pair HL and return through the routine below.
4B5E
GOSUB to the RDSECT primitive at 4777H to read the directory sector.
4B61
SUB 06H D6 06
Subtract 6 to translate the drive-level status into a DOS-level code, setting the Z flag on success.
4B63
RET C9
Return with the translated status in Register A.
4B64
DEFB 00H 00
The DIRCYL data byte, a one-byte scratch for the directory-cylinder value.
4B65H - Get Directory Track From Drive Block
Fetches the directory track number from the active drive's control block.
4B65
LD A,09H 3E 09
Load Register A with 9, the DCT offset of the directory-track field.
4B67
GOSUB to the DCTBYT helper at 479CH to read that byte from the drive's control block into Register A.
4B6A
LD D,A 57
Store the directory track number in Register D.
4B6CH - Eight-Bit Multiply (MULTEA)
Multiplies Register E by Register A, returning the low byte of the product in Register A. A shift-and-add loop over eight bits.
4B6C
PUSH BC C5
Save Register Pair BC.
4B6D
LD D,A 57
Copy the multiplicand from Register A into Register D.
4B6E
XOR A AF
Clear Register A to hold the running product.
4B6F
LD B,08H 06 08
Load Register B with 8, the bit count.
4B71
ADD A,A 87
Multiply Loop
Double the running product, shifting it left one bit.
4B72
SLA E CB 23
Shift the multiplier in Register E left, moving its top bit into the carry.
4B74
If that multiplier bit was zero (NO CARRY), JUMP past the add.
4B76
ADD A,D 82
Otherwise add the multiplicand into the product.
4B77
Multiply Loop
Decrement Register B and LOOP BACK to 4B71H until all eight bits are processed.
4B79
POP BC C1
Restore Register Pair BC.
4B7A
RET C9
Return with the product in Register A.
4B7BH - Eight-Bit Divide (DIVEA)
Divides Register E by Register A, returning the quotient in Register E and the remainder in Register A. A shift-and-subtract loop over eight bits.
4B7B
PUSH BC C5
Save Register Pair BC.
4B7C
LD C,A 4F
Copy the divisor from Register A into Register C.
4B7D
LD B,08H 06 08
Load Register B with 8, the bit count.
4B7F
XOR A AF
Clear Register A to hold the running remainder.
4B80
SLA E CB 23
Divide Loop
Shift the dividend in Register E left, freeing its low bit for a quotient bit and moving its top bit into carry.
4B82
RLA 17
Rotate that bit into the remainder in Register A.
4B83
CP C B9
Compare the remainder against the divisor.
4B84
If the remainder is smaller than the divisor (CARRY), JUMP past the subtract, leaving the quotient bit zero.
4B86
SUB C 91
Otherwise subtract the divisor from the remainder.
4B87
INC E 1C
Set the current quotient bit in Register E.
4B88
Divide Loop
Decrement Register B and LOOP BACK to 4B80H until all eight bits are processed.
4B8A
LD C,A 4F
Save the remainder in Register C.
4B8B
LD A,E 7B
Move the quotient into Register A briefly.
4B8C
LD E,C 59
Place the remainder into Register E.
4B8D
POP BC C1
Restore Register Pair BC.
4B8E
RET C9
Return with the quotient in Register A and the remainder in Register E.
4B8FH - Sixteen-Bit Multiply
Multiplies Register Pair DE by Register C, returning the 24-bit product in Register A (high) and Register Pair HL (low sixteen bits). Used to convert a record number times the record length into a byte or sector position.
4B8F
PUSH BC C5
Save Register Pair BC.
4B90
EX DE,HL EB
Move the multiplicand into Register Pair DE (from HL).
4B91
LD C,A 4F
Copy the byte multiplier into Register C.
4B92
LD HL,0000H 21 00 00
Clear Register Pair HL to hold the running product.
4B95
LD A,L 7D
Clear Register A (from the zero in Register L) to hold the product's high byte.
4B96
LD B,08H 06 08
Load Register B with 8, the bit count of the multiplier.
4B98
ADD HL,HL 29
Multiply Loop
Double the running product in Register Pair HL.
4B99
RLA 17
Carry the product overflow up into the high byte in Register A.
4B9A
RLC C CB 01
Rotate the multiplier in Register C, moving its top bit into the carry.
4B9C
If that multiplier bit was zero (NO CARRY), JUMP past the add.
4B9E
ADD HL,DE 19
Otherwise add the multiplicand into the product.
4B9F
ADC A,00H CE 00
Propagate any carry into the product's high byte.
4BA1
Multiply Loop
Decrement Register B and LOOP BACK to 4B98H until all eight multiplier bits are used.
4BA3
LD C,A 4F
Save the product high byte in Register C.
4BA4
LD A,L 7D
Rearrange the result: move the product low byte toward its return position.
4BA5
LD L,H 6C
Shift the middle byte down.
4BA6
LD H,C 61
Place the high byte, so Register Pair HL holds the scaled result and Register A the byte offset within it.
4BA7
POP BC C1
Restore Register Pair BC.
4BA8
RET C9
Return with the product in Register Pair HL and Register A.
4BA9H - Sixteen-Bit Divide
Divides Register Pair HL by Register A, returning the quotient in Register Pair HL and the remainder in Register A. A sixteen-step shift-and-subtract loop. Used to convert a byte position into a record number and offset.
4BA9
PUSH DE D5
Save Register Pair DE.
4BAA
LD D,A 57
Copy the divisor from Register A into Register D.
4BAB
LD E,10H 1E 10
Load Register E with 16, the bit count of the sixteen-bit dividend.
4BAD
XOR A AF
Clear Register A to hold the running remainder.
4BAE
ADD HL,HL 29
Divide Loop
Shift the dividend in Register Pair HL left, freeing its low bit for a quotient bit and moving its top bit into carry.
4BAF
RLA 17
Rotate that bit into the remainder in Register A.
4BB0
If the remainder overflowed (CARRY), it is at least the divisor, so JUMP to the subtract at 4BB5H.
4BB2
CP D BA
Compare the remainder against the divisor.
4BB3
If the remainder is smaller than the divisor (CARRY), JUMP past the subtract, leaving the quotient bit zero.
4BB5
SUB D 92
Subtract the divisor from the remainder.
4BB6
INC L 2C
Set the current quotient bit in Register L.
4BB7
DEC E 1D
Count down one of the sixteen steps.
4BB8
Divide Loop
LOOP BACK to 4BAEH until all sixteen bits are processed.
4BBA
POP DE D1
Restore Register Pair DE.
4BBB
RET C9
Return with the quotient in Register Pair HL and the remainder in Register A.
4BBCH - Empty
4BCBH - Execute Drive Operation
Transfers control to the active drive's control block, whose leading instruction jumps into the floppy driver. Reached from the disk-primitive tail after Register Pair IY has been set to that block.
4BCB
JP (IY) FD E9
Jump to the address in Register Pair IY, the current drive's control block in the table at 4700H, whose first bytes are a jump to the FDC operation dispatcher at 45FBH with the operation code in Register B.
4BCDH - RST 28H Supervisor Dispatcher (Overlay Call)
Entered from the RST 28H vector at 400CH with a request code in Register A. A code with its high bit set selects a system overlay: the dispatcher disarms the DEBUG trap, loads the overlay for the code, calls it, then restores the trap. A code with the high bit clear is treated as a no-op return. The call to the loaded overlay is a self-modified CALL whose target is patched by the loader.
4BCD
OR A B7
Test the request code in Register A, setting the sign flag from its high bit.
4BCE
If the code is positive (high bit clear, below 80H), it is not an overlay request, so JUMP to 4BEFH.
4BD1
EX (SP),HL E3
Exchange Register Pair HL with the caller's return address on the stack, saving HL and gaining the return address for later.
4BD2
PUSH AF F5
Save the request code and flags.
4BD3
LD HL,4315H 21 15 43
Point Register Pair HL at the DEBUG-trap arm flag at 4315H.
4BD6
LD A,(HL) 7E
Read the current value of the trap flag into Register A.
4BD7
LD (4BE9H),A 32 E9 4B
Self-Modifying Code
Save that trap-flag value into the operand of the restore instruction at 4BE8H, so the flag can be put back after the overlay runs.
4BDA
LD (HL),0C9H 36 C9
Store C9H into the trap flag, disarming the DEBUG trap while the overlay executes.
4BDC
POP AF F1
Restore the request code and flags.
4BDD
POP HL E1
Restore the caller's HL from the stack.
4BDE
GOSUB to the overlay loader at 4BF5H, which ensures the SYS overlay for this request code is in memory and patches the call target below.
4BE1
LD A,(430EH) 3A 0E 43
Load Register A with the current overlay number from 430EH, which the overlay may inspect.
4BE4
CALL 0000H CD 00 00
Self-Modifying Code
Call the loaded overlay. The 0000H operand is patched at 4BE5H by the loader (via 4C46H) with the overlay's entry address, so this actually calls the overlay that services the request.
4BE7
PUSH AF F5
Save the overlay's return status.
4BE8
LD A,00H 3E 00
Self-Modifying Code
Load Register A with the saved DEBUG-trap value; the 00H operand at 4BE9H was set at 4BD7H to the flag's prior value. The SYS2 file/device open overlay also deposits C9H (RET) into this operand byte at 4BE9H on its open-completion path (see the SYS2 page).
4BEA
LD (4315H),A 32 15 43
Restore the DEBUG-trap arm flag at 4315H to its value before the overlay ran.
4BED
POP AF F1
Restore the overlay's return status.
4BEE
RET C9
Return to the program that issued the supervisor call.
4BEFH - Positive-Code Return Path
Handles request codes with the high bit clear. Code 1 simply returns; any other positive code is treated as an error.
4BEF
DEC A 3D
Decrement the request code; a code of 1 becomes zero.
4BF0
NOP 00
A one-byte no-operation left for a possible patch.
4BF1
If the code was not 1 (NZ), JUMP to the system-error exit at 4C4BH.
4BF3
INC A 3C
Restore Register A to 1.
4BF4
RET C9
Return to the caller.
4BF5H - Load System Overlay
Ensures the SYS overlay that services the request code is resident at 4E00H. If the requested overlay is already loaded it is reused; otherwise its directory entry is found, its parameters built, and it is read from disk. On return the entry address is planted into the dispatcher's call. On entry Register H (from Register A) holds the request code.
4BF5
PUSH HL E5
Save Register Pair HL.
4BF6
LD H,A 67
Copy the request code from Register A into Register H.
4BF7
LD A,B 78
Load Register A with the caller's Register B parameter.
4BF8
LD (4C3BH),A 32 3B 4C
Self-Modifying Code
Store that parameter into the loader operand at 4C3BH for use during the module load.
4BFB
LD A,H 7C
Reload the request code into Register A.
4BFC
OR 01H F6 01
Set the low bit so codes 88H and 89H compare equal in the next test.
4BFE
CP 89H FE 89
Compare against 89H. Codes 88H and 89H are the command-library invocation codes issued by the SYS1 command interpreter (88H selects Library A in SYS6/SYS, 89H selects Library B in SYS7/SYS), with Register B holding the command code; both load unconditionally through the always-resident path.
4C00
LD A,H 7C
Reload the request code (the compare above only set flags).
4C01
If the code is 88H/89H (Z), JUMP to 4C12H to load unconditionally.
4C03
LD A,(430EH) 3A 0E 43
Load the currently loaded overlay number from 430EH.
4C06
XOR H AC
Exclusive-OR with the requested code to test whether the same overlay is already loaded.
4C07
AND 0FH E6 0F
Keep the low four bits, the overlay number, discarding the function bits.
4C09
LD A,H 7C
Reload the request code.
4C0A
LD (430EH),A 32 0E 43
Record this code as the current overlay in 430EH.
4C0D
LD HL,4E00H 21 00 4E
Point Register Pair HL at 4E00H, the address overlays load and execute at.
4C10
If the required overlay is already loaded (Z from the overlay-number test), JUMP to 4C46H to reuse it without reading the disk.
4C12
PUSH DE D5
Save Register Pair DE for the load.
4C13
PUSH BC C5
Save Register Pair BC.
4C14
AND 0FH E6 0F
Isolate the overlay number from the request code.
4C16
BIT 3,A CB 5F
Test bit 3 of the overlay number, which distinguishes the higher-numbered SYS overlays.
4C18
If bit 3 is clear (Z), JUMP past the adjustment.
4C1A
ADD A,18H C6 18
Otherwise add 18H to map the overlay number to its SYS filename character.
4C1C
LD (44A7H),A 32 A7 44
Store the overlay filename character into the loader's filespec workspace at 44A7H.
4C1F
LD B,A 47
Keep a copy in Register B.
4C20
LD A,20H 3E 20
Load Register A with a space character.
4C22
LD (44A1H),A 32 A1 44
Store the space into the loader parameter buffer at 44A1H, padding the filespec.
4C25
SBC HL,HL ED 62
Form 0000H (or FFFFH) in Register Pair HL to seed the buffer control words.
4C27
LD (44AAH),HL 22 AA 44
Store it into the loader parameter buffer at 44AAH.
4C2A
LD C,H 4C
Set Register C from Register H as a directory-search parameter.
4C2B
GOSUB to the read-directory-sector routine at 4B10H to locate the overlay's directory entry.
4C2E
If the directory read failed (NZ), JUMP to the system-error exit at 4C4BH.
4C30
BIT 4,(HL) CB 66
Test bit 4 of the directory entry's flag byte, which marks the entry as an active file.
4C32
If the entry is not an active file (Z), JUMP to the system-error exit.
4C34
LD A,L 7D
Take the low byte of the entry address.
4C35
ADD A,16H C6 16
Add 16H to reach the entry's first extent (track/sector location) field.
4C37
GOSUB to the parameter builder at 47B4H to set the FDC track and sector from that extent.
4C3A
LD B,00H 06 00
Clear Register B for the module-load call.
4C3C
LD DE,44A0H 11 A0 44
Point Register Pair DE at the loader parameter buffer at 44A0H.
4C3F
GOSUB to the module loader at 4C8DH to read the overlay's load records into memory at 4E00H.
4C42
If the load failed (NZ), JUMP to the system-error exit.
4C44
POP BC C1
Restore Register Pair BC.
4C45
POP DE D1
Restore Register Pair DE.
4C46
LD (4BE5H),HL 22 E5 4B
Self-Modifying Code
Store the overlay's entry address (Register Pair HL) into the operand at 4BE5H, patching the dispatcher's CALL at 4BE4H so it calls this overlay.
4C49
POP HL E1
Restore Register Pair HL.
4C4A
RET C9
Return to the dispatcher.
4C4BH - System Error Exit
Displays the fatal system-error message and aborts to DOS. The message string follows.
4C4B
LD HL,4C54H 21 54 4C
Point Register Pair HL at the "SYS Error" message string at 4C54H.
4C4E
GOSUB to the display-string routine at 447BH to write the message to the screen.
4C51
JUMP to the @ABORT vector at 4030H to terminate the operation and return to DOS with the abort message.
4C54
DEFM 'SYS Error' 53 59 53 20 45 72 72 6F 72
The fatal system-error message text.
4C5D
DEFB 0DH 0D
The carriage-return terminator ending the message string.
4C5EH - Run Program (@RUN)
The @RUN worker (vector 4433H). Loads a program file and transfers control to it, honouring the debug and no-execute flags.
4C5E
PUSH HL E5
Save Register Pair HL.
4C5F
LD HL,430FH 21 0F 43
Point Register Pair HL at the system flag byte at 430FH.
4C62
SET 2,(HL) CB D6
Set bit 2 of the system flag byte, marking that a user program is being run (so this bit records the run state).
4C64
GOSUB to the @LOAD vector at 4430H to load the program file into memory.
4C67
If the load failed (NZ), JUMP to the @ERROR vector at 4409H to report it.
4C6A
EX (SP),HL E3
Exchange Register Pair HL with the stacked value, retrieving the program's transfer address into HL for the return.
4C6B
LD A,(430FH) 3A 0F 43
Load the system flag byte.
4C6E
BIT 1,A CB 4F
Test bit 1 of the system flag byte, the load-only flag that suppresses execution.
4C70
RET NZ C0
If load-only is set (NZ), return without executing, so control passes to the loaded program's transfer address on the stack.
4C71
BIT 7,A CB 7F
Test bit 7 of the system flag byte, the debug-on-run flag.
4C73
If debug-on-run is set (NZ), JUMP to the DEBUG vector at 400FH so the program starts under the monitor.
4C76
RET C9
Otherwise return, transferring control to the loaded program.
4C77H - Load Program (@LOAD)
The @LOAD worker (vector 4430H). Opens the program file and reads its load module into memory, returning the transfer address.
4C77
LD B,00H 06 00
Clear Register B as the load mode (load without an address bias).
4C79
LD HL,4200H 21 00 42
Point Register Pair HL at the file control block workspace at 4200H used for the program file.
4C7C
GOSUB to the @OPEN vector at 4424H to open the program file described in that control block.
4C7F
PUSH DE D5
Save Register Pair DE.
4C80
If the file opened successfully (Z), GOSUB to the module loader at 4C8DH to read its load records.
4C83
POP DE D1
Restore Register Pair DE.
4C84
RET Z C8
Return if the load completed without error (Z).
4C85
SET 6,A CB F7
Set bit 6 of the returned status to fold the open/load error into a DOS error code.
4C87
CP 58H FE 58
Compare the error code against 58H, the end-of-file marker that can occur at the end of a load.
4C89
RET NZ C0
Return the error unless it was that end-of-file value (NZ).
4C8A
ADD A,07H C6 07
For the end-of-file case, add 7 to form the specific load error code.
4C8C
RET C9
Return with the error code in Register A.
4C8DH - Module Loader Setup
Prepares to parse a load module by pointing the byte-stream pointer at the file buffer and passing the parameter block in Register Pair IX.
4C8D
LD A,B 78
Load the load-mode byte from Register B.
4C8E
LD (4CF9H),A 32 F9 4C
Self-Modifying Code
Store the load mode into the operand at 4CF9H for use during parsing.
4C91
PUSH DE D5
Save the parameter-block pointer in Register Pair DE.
4C92
EX (SP),IX DD E3
Exchange it with Register Pair IX, so IX now points at the parameter block and the caller's IX is saved.
4C94
LD DE,42FFH 11 FF 42
Point Register Pair DE at 42FFH, one byte before the file sector buffer, so the first fetch advances into the buffer.
4C97
GOSUB to the record parser at 4C9DH to read the module's load records into memory.
4C9A
POP IX DD E1
Restore the caller's Register Pair IX.
4C9C
RET C9
Return to the caller.
4C9DH - Load-Module Record Parser
Reads the module one record at a time and acts on the record-type byte: a data record is copied to its load address and verified, a transfer-address record ends the load, and other record types are handled or skipped. This is the LDOS load-module format reader.
4C9D
GOSUB to the module byte-fetch at 4D1CH to read the next record's type byte into Register A.
4CA0
CP 01H FE 01
Compare the record type against 1, a load-data record.
4CA2
If it is a data record (Z), JUMP to 4CCEH to load the data.
4CA4
CP 02H FE 02
Compare the record type against 2, a transfer-address record.
4CA6
If it is a transfer-address record (Z), JUMP to 4CC7H to read the entry address and finish.
4CA8
CP 04H FE 04
Compare against record type 4.
4CAA
If type 4 (Z), JUMP to 4CEEH to end the load and discard the caller frame.
4CAC
CP 08H FE 08
Compare against record type 8.
4CAE
If type 8 (Z), JUMP to 4CF0H to handle the positioning/relocation record.
4CB0
CP 0AH FE 0A
Compare against record type 0AH.
4CB2
If type 0AH (Z), JUMP to 4CB8H to return the unsupported-record error.
4CB4
CP 20H FE 20
Compare the record type against 20H.
4CB6
If the type is below 20H (CARRY), it is a control record to skip, so JUMP to 4CBCH.
4CB8
LD A,22H 3E 22
Load error code 22H, the unrecognised load-record error.
4CBA
OR A B7
Set the flags for a non-zero (error) return.
4CBB
RET C9
Return the error.
4CBC
Skip-record path: read the record's length byte.
4CBF
LD B,A 47
Use it as the skip count in Register B.
4CC0
Skip Loop
Read and discard one byte of the skipped record.
4CC3
Skip Loop
Decrement Register B and LOOP BACK to 4CC0H until the record is consumed.
4CC5
JUMP back to 4C9DH to read the next record.
4CC7
Transfer-address record: read the record length byte (and discard it).
4CCA
GOSUB to the word-fetch at 4D30H to read the two-byte program transfer address into Register Pair HL.
4CCD
RET C9
Return with the transfer address in HL, ending the load.
4CCE
Data record: read the record length byte.
4CD1
LD B,A 47
Use it as the byte count in Register B.
4CD2
GOSUB to the word-fetch at 4D30H to read the two-byte load address into Register Pair HL.
4CD5
RET NZ C0
Return if the fetch reported an error (NZ).
4CD6
DEC B 05
Reduce the count by one for the two address bytes just read.
4CD7
DEC B 05
Reduce it a second time, leaving the count of data bytes to load.
4CD8
Load Loop
Read the next data byte into Register A.
4CDB
LD (HL),A 77
Store it at the current load address.
4CDC
CP (HL) BE
Read the byte back and compare, verifying the store took (it will fail on ROM or absent RAM).
4CDD
If the readback differs (NZ), JUMP to 4CE4H to report a memory fault.
4CDF
INC HL 23
Advance the load address.
4CE0
Load Loop
Decrement Register B and LOOP BACK to 4CD8H until the record's data is loaded.
4CE2
JUMP back to 4C9DH for the next record.
4CE4
LD B,(HL) 46
Memory-fault check: read the stored byte into Register B.
4CE5
CPL 2F
Complement the intended byte in Register A to form a different test value.
4CE6
LD (HL),A 77
Store the complemented value.
4CE7
LD A,(HL) 7E
Read it back.
4CE8
CP B B8
Compare against the earlier value; a working RAM cell would now differ.
4CE9
LD A,63H 3E 63
Load error code 63H, the memory-fault error.
4CEB
RET NZ C0
If the cell is genuinely faulty (NZ), return the memory-fault error.
4CEC
INC A 3C
Otherwise adjust the code (it was a load-into-ROM address rather than a fault).
4CED
RET C9
Return with the adjusted status.
4CEE
POP HL E1
Record-type-4 path: discard the parser's return address so control returns one level up, ending the load.
4CEF
RET C9
Return to the load caller.
4CF0
Record-type-8 path: read the record length byte.
4CF3
LD B,A 47
Save it as a counter in Register B.
4CF4
Read the record's sub-type byte.
4CF7
DEC B 05
Account for the sub-type byte in the count.
4CF8
CP 00H FE 00
Test the sub-type; the 00H operand at 4CF9H is patched with the load mode at 4C8EH.
4CFA
If the sub-type does not match the load mode (NZ), JUMP to 4CC0H to skip the record's remaining bytes.
4CFC
Otherwise read a two-byte value (a record number) into Register Pair HL.
4D00
Conditionally read a second word.
4D03
If a fetch error occurred (NZ), JUMP to the error return at 4D2CH.
4D05
Read another byte of the record.
4D08
LD E,A 5F
Save it in Register E.
4D09
PUSH BC C5
Save the record counter.
4D0A
LD B,H 44
Move the saved record number into Register Pair BC (high byte).
4D0B
LD C,L 4D
Move the low byte, so Register Pair BC holds the record number to position to.
4D0C
PUSH DE D5
Save the module stream pointer.
4D0D
PUSH IX DD E5
Push the parameter block pointer.
4D0F
POP DE D1
Pop it into Register Pair DE as the file control block for positioning.
4D10
GOSUB to the @POSN vector at 4442H to position the file to the record number in Register Pair BC.
4D13
POP DE D1
Restore the module stream pointer.
4D14
POP BC C1
Restore the record counter.
4D15
If positioning failed (NZ), JUMP to the error return at 4D2CH.
4D17
GOSUB to the buffer-refill routine at 4D21H to reload the module stream at the new position.
4D1A
JUMP back to 4CA0H to continue parsing records from the new position.
4D1CH - Module Byte And Word Fetch
Reads the next byte of the load module from the sector buffer, refilling the buffer from disk when it is exhausted, and a helper that reads two bytes as a little-endian word.
4D1C
INC E 1C
Advance the low byte of the buffer pointer to the next byte; a wrap to zero means the 256-byte sector buffer is exhausted.
4D1D
If the buffer is exhausted (Z), JUMP to 4D21H to read the next sector.
4D1F
LD A,(DE) 1A
Load the next module byte from the buffer at Register Pair DE into Register A.
4D20
RET C9
Return with the byte.
4D21
PUSH HL E5
Buffer-refill: save Register Pair HL.
4D22
PUSH DE D5
Save the buffer pointer.
4D23
PUSH BC C5
Save Register Pair BC.
4D24
GOSUB to 494FH to read the file's next sector into the buffer.
4D27
POP BC C1
Restore Register Pair BC.
4D28
POP DE D1
Restore the buffer pointer.
4D29
POP HL E1
Restore Register Pair HL.
4D2A
If the sector read succeeded (Z), JUMP back to 4D1FH to return the first byte of the new sector.
4D2C
POP BC C1
Error path: discard the parser's return address so the error unwinds one level.
4D2D
OR 40H F6 40
Fold bit 6 into the status to form the load-error code.
4D2F
RET C9
Return the error.
4D30
Word-fetch: read the low byte of the word.
4D33
LD L,A 6F
Store it in Register L.
4D34
Read the high byte of the word.
4D37
LD H,A 67
Store it in Register H, so Register Pair HL holds the little-endian word.
4D38
CP A BF
Set the Z flag to signal success.
4D39
RET C9
Return with the word in Register Pair HL.
4D3AH - Clock-Display Task Block
A task handler block installed in the Task Control Block table for the on-screen clock. Its first word is the handler entry; the handler runs once every five ticks to refresh the time on screen.
4D3A
DEFW 4D3DH 3D 4D
The task-block handler pointer, addressing the clock-display handler at 4D3DH.
4D3C
DEFB 05H 05
The block's divider byte (read as offset 2 by the handler), initialising the once-every-five-ticks pacing.
4D3D
DEC (IX+02H) DD 35 02
Clock-display handler. Decrement the block's divider at offset 2 (Register Pair IX points at this block).
4D40
RET NZ C0
Return until the divider reaches zero, so the display updates only every fifth tick.
4D41
LD (IX+02H),05H DD 36 02 05
Reload the divider with 5, then fall into the time-display code at 4D45H.
4D45H - Display Time
Formats the software clock's hours, minutes, and seconds as HH:MM:SS and writes them to the screen. The clock task enters at 4D45H with the fixed screen position; the vector at 446DH enters at 4D48H with the caller's screen position in Register Pair HL. The time counters are read downward from 4043H (hours), 4042H (minutes), 4041H (seconds).
4D45
LD HL,3C35H 21 35 3C
Point Register Pair HL at screen position 3C35H (top row) where the clock task shows the time.
4D48
LD DE,4043H 11 43 40
Point Register Pair DE at 4043H, the hours counter (the display-time vector entry point).
4D4B
LD C,3AH 0E 3A
Load Register C with the colon character, the field separator for time.
4D4D
LD B,03H 06 03
Load Register B with 3, the number of fields to display (also the date entry point, which supplies its own pointer and separator).
4D4F
LD A,(DE) 1A
Field Loop
Load the current time (or date) value from the counter.
4D50
DEC DE 1B
Step down to the next counter (hours to minutes to seconds).
4D51
LD (HL),2FH 36 2F
Seed the screen tens digit with the character just below ASCII '0' so the count-up below lands on the right digit.
4D53
INC (HL) 34
Tens Loop
Increment the on-screen tens digit.
4D54
SUB 0AH D6 0A
Subtract ten from the value.
4D56
Tens Loop
While ten or more remains (NO CARRY), LOOP BACK to 4D53H, counting the tens digit up.
4D58
ADD A,3AH C6 3A
Add back to convert the remaining units into an ASCII digit.
4D5A
INC HL 23
Advance to the units position on screen.
4D5B
LD (HL),A 77
Write the units digit.
4D5C
INC HL 23
Advance past the two-digit field.
4D5D
DEC B 05
Count down one field.
4D5E
RET Z C8
Return when all three fields are shown.
4D5F
LD (HL),C 71
Otherwise write the separator character (colon or slash) between fields.
4D60
INC HL 23
Advance past the separator.
4D61
Field Loop
LOOP BACK to 4D4FH for the next field.
4D63H - Display Date
Displays the date as three slash-separated fields, reusing the field formatter above. The vector at 4470H reaches this with the caller's screen position in Register Pair HL. The date counters are read downward from 4046H.
4D63
LD DE,4046H 11 46 40
Point Register Pair DE at 4046H, the highest date field.
4D66
LD C,2FH 0E 2F
Load Register C with the slash character, the date field separator.
4D68
JUMP to the shared field formatter at 4D4DH to display the three date fields.
4D6A
DEFW 4D6CH 6C 4D
A stored pointer to the hex-display routine that follows.
4D6CH - Display 16-Bit Value As Hex
Takes a two-byte value from the stack frame and writes it as four hexadecimal digits to the screen, using the classic DAA-based binary-to-hex conversion.
4D6C
LD HL,0012H 21 12 00
Load the offset to the caller's value within the stack frame.
4D6F
ADD HL,SP 39
Add the stack pointer to address that value.
4D70
LD E,(HL) 5E
Load the low byte of the value into Register E.
4D71
INC HL 23
Advance to the high byte.
4D72
LD D,(HL) 56
Load the high byte into Register D, so Register Pair DE holds the value.
4D73
LD HL,3C2EH 21 2E 3C
Point Register Pair HL at screen position 3C2EH for the hex output.
4D76
LD A,D 7A
Entry point that converts the value in Register Pair DE to four hexadecimal ASCII digits and stores them at the buffer addressed by Register Pair HL. Take the high byte (Register D) first. SYS4/SYS calls this to fill the "Referenced at X'NNNN'" address field.
4D77
GOSUB to 4D7BH to display it as two hex digits.
4D7A
LD A,E 7B
Take the low byte and fall into the digit routine to show it.
4D7B
PUSH AF F5
Save the byte.
4D7C
RRA 1F
Shift the high nibble down, one bit.
4D7F
RRA 1F
Fourth shift, leaving the high nibble in the low four bits.
4D80
GOSUB to 4D84H to convert and display that nibble.
4D83
POP AF F1
Restore the byte for the low nibble.
4D84
AND 0FH E6 0F
Keep the low nibble.
4D86
ADD A,90H C6 90
Begin the DAA hex conversion by adding 90H.
4D88
DAA 27
Decimal-adjust, producing a value that carries for nibbles above 9.
4D89
ADC A,40H CE 40
Add 40H with carry.
4D8B
DAA 27
Decimal-adjust again, yielding the ASCII code for the hex digit 0-9 or A-F.
4D8C
LD (HL),A 77
Write the hex digit to the screen.
4D8D
INC HL 23
Advance the screen pointer.
4D8FH - File Position Helpers
Adjust the record number in Register Pair HL by the buffer state, so callers get the effective current position. Used by the end-of-file check and the byte-position converter.
4D8F
POP BC C1
Retrieve the caller's return address into Register Pair BC to re-order the stack for the helper below.
4D90
PUSH HL E5
Save Register Pair HL.
4D91
PUSH BC C5
Restore the return address to the stack.
4D92
LD C,(IX+05H) DD 4E 05
Load the current buffer byte offset from FCB offset 5 into Register C.
4D95
BIT 7,(IX+01H) DD CB 01 7E
Test the file-open flag (bit 7 of FCB offset 1).
4D99
RET Z C8
Return unchanged if the file is not open.
4D9A
LD A,C 79
Load the buffer offset.
4D9B
OR A B7
Test whether the buffer position is at the start of a record.
4D9C
If at a record boundary (Z), JUMP past the increment.
4D9E
INC HL 23
Otherwise count the partially-filled current record by advancing the record number.
4D9F
BIT 5,(IX+01H) DD CB 01 6E
Test bit 5 of the FCB flags, set when the buffer holds a record.
4DA3
RET NZ C0
Return if the buffer holds a record.
4DA4
DEC HL 2B
Otherwise step the record number back by one.
4DA5
RET C9
Return with the adjusted position in Register Pair HL.
4DA6H - Per-Tick Keyboard And BREAK Scan
Called by the interrupt service routine on every tick. It reads the memory-mapped keyboard rows for the SHIFT, ENTER/CLEAR, and BREAK keys and records their state in the key-status flag byte at 4423H, returning to the interrupt whether a BREAK diversion should occur.
4DA6
PUSH HL E5
Save Register Pair HL.
4DA7
LD HL,4423H 21 23 44
Point Register Pair HL at the key-status flag byte at 4423H.
4DAA
LD A,(3880H) 3A 80 38
Read the memory-mapped keyboard SHIFT row at 3880H.
4DAD
AND 03H E6 03
Keep the two SHIFT-key bits.
4DAF
NEG ED 44
Negate to set the carry when a SHIFT key is down, for the tests below.
4DB1
LD A,(3801H) 3A 01 38
Read keyboard row 3801H (the @/A-G row).
4DB4
BIT 0,A CB 47
Test bit 0 of that row, the @ key.
4DB6
If that key is not pressed (Z), JUMP past the flag set.
4DB8
If SHIFT is not also down (NO CARRY), JUMP past.
4DBA
SET 1,(HL) CB CE
Set bit 1 of the key-status flag at 4423H, recording the SHIFT-@ combination.
4DBC
LD A,(3840H) 3A 40 38
Read keyboard row 3840H, which holds ENTER, CLEAR, BREAK, the arrows, and SPACE.
4DBF
BIT 0,A CB 47
Test bit 0, the ENTER key.
4DC1
If ENTER is not pressed (Z), JUMP past the flag set.
4DC3
SET 2,(HL) CB D6
Set bit 2 of the key-status flag, recording ENTER.
4DC5
BIT 2,A CB 57
Test bit 2 of the row, the BREAK key.
4DC7
PUSH AF F5
Save the BREAK test result.
4DC8
If BREAK is not pressed (Z), JUMP to the exit.
4DCA
If the carry indicates the combination is not to be acted on, JUMP to the exit.
4DCC
LD A,(430FH) 3A 0F 43
Load the system flag byte at 430FH.
4DCF
BIT 4,A CB 67
Test bit 4, the flag that suppresses BREAK handling.
4DD1
If BREAK handling is suppressed (NZ), JUMP to the exit.
4DD3
SET 0,(HL) CB C6
Otherwise set bit 0 of the key-status flag at 4423H, marking that BREAK was pressed.
4DD5
POP AF F1
Restore the BREAK test result, which the interrupt routine reads as the return status.
4DD6
POP HL E1
Restore Register Pair HL.
4DD7
RET C9
Return to the interrupt service routine.
4DF0H - End Of Resident Image (USTOR$)
Padding to the end of the resident image, followed by the USTOR$ word that points into the user/system storage area.
4DF0
DEFB 00H x 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00
Padding bytes filling to the USTOR$ word (4DF0H-4DFDH), cleared at load time.
4DFE
DEFW 43B0H B0 43
USTOR$, the user-storage pointer at 4DFEH, holding 43B0H.
4E00H - System Boot Initialization
The transient start-up code and the module's execution entry point. It sets interrupt mode 1 and the stack, sizes installed RAM, records the device-driver vectors, initialises the drive configuration table, enables interrupts, and reads the configuration sector. After it hands off, this 4E00H area is reused as the load address for system overlays.
4E00
DI F3
Disable interrupts while the system is being set up.
4E01
IM 1 ED 56
Select Z80 interrupt mode 1, so hardware interrupts vector through RST 38H.
4E03
LD SP,41DEH 31 DE 41
Set the stack pointer to 41DEH, the system stack top in the resident workspace.
4E06
LD HL,0FFFFH 21 FF FF
Point Register Pair HL at the top of the address space to begin sizing RAM downward.
4E09
LD A,(HL) 7E
Memory-Size Loop
Read the byte at the current address.
4E0A
LD B,A 47
Save the original byte in Register B.
4E0B
CPL 2F
Complement it to form a different test value.
4E0C
LD (HL),A 77
Write the complemented value.
4E0D
CP (HL) BE
Read it back and compare; a writable RAM cell matches, an empty or ROM address does not.
4E0E
LD (HL),B 70
Restore the original byte.
4E0F
If the address is writable RAM (Z), the top of memory has been found, so JUMP to 4E17H.
4E11
LD A,H 7C
Otherwise take the high address byte.
4E12
SUB 40H D6 40
Drop the address by 4000H (16K) to test the next lower memory page.
4E14
LD H,A 67
Update the high address byte.
4E15
Memory-Size Loop
LOOP BACK to 4E09H to test the next page down.
4E17
LD (4049H),HL 22 49 40
Store the highest RAM address in the memory-size word at 4049H.
4E1A
LD (4403H),HL 22 03 44
Also store it at HIGH$ (4403H), the high-memory pointer programs read.
4E1D
LD A,00H 3E 00
Load zero.
4E1F
OUT (0FEH),A D3 FE
Write it to port FEH, clearing the Model I expansion control latch.
4E21
XOR A AF
Clear Register A again.
4E22
LD (37E4H),A 32 E4 37
Write zero to 37E4H, resetting the disk-controller drive-latch area.
4E25
LD HL,(4016H) 2A 16 40
Load the keyboard DCB's driver vector from 4016H.
4E28
LD (43B8H),HL 22 B8 43
Save it at 43B8H (KISV$, the keyboard-driver save vector, Model I equate KISV$ = 43B8H) so the resident system remembers the keyboard driver entry.
4E2B
LD HL,3C00H 21 00 3C
Point Register Pair HL at the base of video RAM to test it.
4E2E
CPL 2F
Complement Register A for the video-RAM write test.
4E2F
LD (HL),A 77
Write the test value to the first screen cell.
4E30
CP (HL) BE
Read it back to confirm video RAM is present and writable.
4E31
LD (HL),20H 36 20
Store a space to clear that cell.
4E33
LD HL,(401EH) 2A 1E 40
Load the video DCB's driver vector from 401EH.
4E36
If video RAM did not verify (NZ), JUMP to 4E3EH leaving the existing driver vector.
4E38
LD HL,4378H 21 78 43
Otherwise load the address of the resident display-character driver at 4378H.
4E3B
LD (401EH),HL 22 1E 40
Install it as the video DCB's driver vector.
4E3E
LD (43BAH),HL 22 BA 43
Save the video driver vector at 43BAH (DOSV$, the display-driver save vector, Model I equate DOSV$ = 43BAH) for the resident system.
4E41
LD HL,(4026H) 2A 26 40
Load the printer DCB's driver vector from 4026H.
4E44
LD (43BCH),HL 22 BC 43
Save it at 43BCH (PRSV$, the printer-driver save vector, Model I equate PRSV$ = 43BCH; the job-command-line save KIJCL$ follows at 43BEH).
4E47
LD HL,43D8H 21 D8 43
Point Register Pair HL at the workspace block starting 43D8H (the S1DCB$-S5DCB$ system device-control blocks, 43D8H-43FFH; Model I equates S1DCB$=43D8H, S2DCB$=43E0H, S3DCB$=43E8H, S4DCB$=43F0H, S5DCB$=43F8H) to clear it.
4E4A
LD (HL),00H 36 00
Clear Loop
Store zero into the current workspace byte.
4E4C
INC L 2C
Advance to the next byte within the page.
4E4D
Clear Loop
LOOP BACK to 4E4AH until the low byte wraps, clearing 43D8H-43FFH.
4E4F
PUSH IY FD E5
Push Register Pair IY, the pointer to the boot configuration block the loader passed.
4E51
POP HL E1
Pop it into Register Pair HL.
4E52
LD A,H 7C
Take the high byte.
4E53
OR L B5
OR with the low byte to test whether the configuration pointer is zero.
4E54
If no configuration block was passed (Z), JUMP to 4E66H to skip copying it.
4E56
LD DE,4700H 11 00 47
Point Register Pair DE at the drive configuration table DCT$ at 4700H.
4E59
LD BC,000AH 01 0A 00
Load Register Pair BC with 10, the size of drive 0's control block.
4E5C
LDIR ED B0
Block-copy the ten-byte drive-0 configuration from the boot block into DCT$.
4E5E
LD HL,442FH 21 2F 44
Point Register Pair HL at the SVDAT1$ flag byte at 442FH.
4E61
SET 0,(HL) CB C6
Set bit 0 of that flag byte, recording that a boot configuration was applied.
4E63H - Apply Drive Config, Enable Interrupts, Read Config Sector
Merges the boot-supplied drive flags into drive 0's DCT block, sets the interrupt-enable mask and turns interrupts on, then reads the configuration sector into the file buffer. This region uses an overlapping-code trick: the no-config path (the jump from 4E54H to 4E66H) and the config path (falling in at 4E63H) reach the shared code at 4E68H having set Register L differently.
4E63
LD L,10H 2E 10
Config path. Set Register L to 10H, selecting an offset into the parameter page addressed below.
4E65
LD BC,0BC2EH 01 2E BC
Load Register Pair BC. On the config path this instruction's real purpose is to consume the two bytes 2EH BCH so the shared load at 4E68H runs with Register L still 10H; the value in BC is not used.
The jump from 4E54H lands at 4E66H, in the middle of the instruction above. Entered there, the bytes 2EH BCH read instead as LD L,0BCH, so the no-config path reaches 4E68H with Register L set to 0BCH rather than 10H.
4E66
LD L,0BCH 2E BC
No-config entry point (reached by the jump from 4E54H). Set Register L to 0BCH, addressing a different byte of the parameter page.
4E68
LD H,42H 26 42
Both paths converge here. Set Register H to 42H, so Register Pair HL addresses the selected byte on page 42H.
4E6A
LD A,(HL) 7E
Load that parameter byte into Register A.
4E6B
AND 03H E6 03
Keep the low two bits, the drive stepping-rate selection.
4E6D
LD B,A 47
Save the stepping-rate bits in Register B.
4E6E
LD HL,4703H 21 03 47
Point Register Pair HL at drive 0's option-flags byte in DCT$ (offset 3 of the block at 4700H).
4E71
LD A,(HL) 7E
Load the current drive-0 option flags.
4E72
AND 7CH E6 7C
Clear the stepping-rate and present bits, keeping the other option flags.
4E74
OR B B0
Merge in the stepping-rate bits read from the configuration.
4E75
LD (HL),A 77
Store the updated option flags back into drive 0's DCT block.
4E76
LD A,(4202H) 3A 02 42
Load a configuration byte from 4202H in the buffer.
4E79
LD (4709H),A 32 09 47
Store it into drive 0's DCT block at offset 9 (the directory-track / geometry field).
4E7C
LD A,(37ECH) 3A EC 37
Read the WD1771 status register at 37ECH to clear any pending controller state.
4E7F
LD HL,404CH 21 4C 40
Point Register Pair HL at the interrupt-enable mask byte at 404CH.
4E82
LD (HL),0C0H 36 C0
Set the interrupt-enable mask to C0H, enabling the two interrupt sources (bits 7 and 6) the system services each tick.
4E84
EI FB
Enable interrupts, so the interrupt service routine, the clock, and the keyboard scan now run.
4E85
LD DE,0002H 11 02 00
Load Register Pair DE with track 0, sector 2, the location of the configuration sector.
4E88
LD C,D 4A
Set Register C to 0 (from Register D), selecting drive 0.
4E89
LD HL,4200H 21 00 42
Point Register Pair HL at the buffer at 4200H to receive the sector.
4E8C
GOSUB to the RDSECT primitive at 4777H to read the configuration sector into the buffer.
4E8F
RET NZ C0
Return (aborting the boot) if the configuration sector could not be read.
4E90H - Read Configuration And Select Date/Time Path
Reads the configuration byte, and from the stored date/time configuration decides whether to prompt the operator for the date, prompt for the time, or skip straight to finishing the boot.
4E90
LD L,01H 2E 01
Point Register Pair HL at the configuration byte at 4201H in the buffer.
4E92
LD A,(HL) 7E
Read the configuration byte.
4E93
LD (HL),00H 36 00
Clear it in the buffer.
4E95
PUSH AF F5
Save the configuration byte for later use.
4E96
LD A,(42C2H) 3A C2 42
Load the KIJCL$ chain byte at 42C2H that flags a job/command already in progress.
4E9A
If a command is queued (NZ), JUMP to 4F89H to skip the date prompt and handle the time path.
4E9D
LD A,(4306H) 3A 06 43
Load the stored date-configuration byte at 4306H.
4EA0
XOR 50H EE 50
Exclusive-OR with 50H, the flag that marks the date as already valid.
4EA2
If the date is already set (Z), JUMP to 4FBDH to skip entry and install the clock task.
4EA5
CP 0DH FE 0D
Compare the residual against 0DH.
4EA7
If below 0DH (CARRY), the date is already partly configured, so JUMP to 4EF4H to process it.
4EA9H - Prompt For And Parse Date
Displays the "Date ? " prompt, reads the operator's entry, and validates and stores the month, day, and year. Re-prompts on invalid input.
4EA9
LD HL,3F17H 21 17 3F
Date Prompt
Point Register Pair HL at the screen position for the prompt.
4EAC
LD DE,50B6H 11 B6 50
Point Register Pair DE at the "Date ? " prompt string.
4EAF
LD BC,0830H 01 30 08
Load the field count and delimiter parameters for the parser.
4EB2
GOSUB to the prompt-and-parse routine at 431BH to show the prompt and read the entered fields.
4EB5
If nothing valid was entered (NO CARRY), LOOP BACK to 4EA9H to re-prompt.
4EB7
LD A,(DE) 1A
Load the first parsed field value.
4EB8
CP 0CH FE 0C
Compare the month against 12.
4EBA
If 12 or greater, JUMP to 4EBEH to range-check it.
4EBC
ADD A,64H C6 64
Otherwise bias the month value for the validity test.
4EBE
SUB 50H D6 50
Subtract the base to normalise the month.
4EC0
CP 20H FE 20
Range-check the normalised value.
4EC2
If out of range (NO CARRY), LOOP BACK to 4EA9H to re-prompt.
4EC4
LD (4466H),A 32 66 44
Store the accepted month in the date-entry scratch byte at 4466H.
4EC7
AND 03H E6 03
Keep the low two bits to test for a special value.
4EC9
LD A,1CH 3E 1C
Load 1CH as the default month-length base.
4ECB
If not the special value (NZ), JUMP to 4ED3H.
4ECD
LD HL,4048H 21 48 40
Otherwise point at the date-flags byte at 4048H.
4ED0
SET 7,(HL) CB FE
Set bit 7 of the date flags, marking a leap-year adjustment.
4ED2
INC A 3C
Bump the month-length base for the leap case.
4ED3
LD HL,4203H 21 03 42
Point at the month-length working byte at 4203H.
4ED6
LD (HL),A 77
Store the month-length base.
4ED7
LD A,(431AH) 3A 1A 43
Load the parsed day value from the date scratch at 431AH.
4EDA
DEC A 3D
Convert to a zero-based day.
4EDB
CP 0CH FE 0C
Range-check the day roughly.
4EDD
If out of range (NO CARRY), LOOP BACK to 4EA9H to re-prompt.
4EDF
DEC HL 2B
Step to the previous working byte.
4EE0
ADD A,L 85
Index into the month-length table by the day.
4EE1
LD L,A 6F
Point Register L at the entry.
4EE2
INC DE 13
Advance to the next parsed field.
4EE3
LD A,(DE) 1A
Load the day-of-month field.
4EE4
LD (4307H),A 32 07 43
Store the day at 4307H.
4EE7
DEC A 3D
Convert to zero-based.
4EE8
CP (HL) BE
Compare the day against the number of days in the month.
4EE9
If the day exceeds the month length (NO CARRY), LOOP BACK to 4EA9H to re-prompt.
4EEB
INC DE 13
Advance to the year field.
4EEC
LD A,(DE) 1A
Load the year field.
4EED
OR 50H F6 50
Set the valid-marker bit in the year value.
4EEF
LD (4306H),A 32 06 43
Store the marked year at 4306H, the date-configuration byte.
4EF2
XOR 50H EE 50
Strip the marker back off for the working value.
4EF4H - Compute Date Fields And Day Of Week
Builds the working month, day, and year counters and computes the day of the week by accumulating a day number and dividing by seven.
4EF4
LD (4046H),A 32 46 40
Store the working year value at 4046H (the highest date display field).
4EF7
LD B,A 47
Keep it in Register B.
4EF8
LD A,(4466H) 3A 66 44
Load the stored month from the scratch byte at 4466H.
4EFB
PUSH AF F5
Save the month.
4EFC
AND 03H E6 03
Keep its low two bits for the leap test.
4EFE
LD HL,4203H 21 03 42
Point at the month-day working byte at 4203H.
4F01
LD (HL),1CH 36 1C
Set the base month length.
4F03
If not a leap-year month case (NZ), JUMP past the adjustment.
4F05
INC (HL) 34
Otherwise add a day for leap February.
4F06
LD A,(4307H) 3A 07 43
Load the day of month from 4307H.
4F09
LD (4045H),A 32 45 40
Store it at the date field 4045H.
4F0C
LD L,A 6F
Seed the running day number low byte with the day.
4F0D
LD H,00H 26 00
Clear the high byte, so Register Pair HL is the running day count.
4F0F
LD DE,4201H 11 01 42
Point Register Pair DE at the month-length table at 4201H.
4F12
LD A,(DE) 1A
Month Loop
Load the current month's length.
4F13
ADD A,L 85
Add it into the running day number low byte.
4F14
LD L,A 6F
Update the low byte.
4F15
ADC A,H 8C
Carry into the high byte.
4F16
SUB L 95
Recover just the carry.
4F17
LD H,A 67
Update the high byte of the running day count.
4F18
INC DE 13
Advance to the next month length.
4F19
Month Loop
Decrement the month count in Register B and LOOP BACK to 4F12H, summing days for each elapsed month.
4F1B
EX DE,HL EB
Move the running day count into Register Pair DE.
4F1C
LD HL,4047H 21 47 40
Point at the day-number store at 4047H.
4F1F
LD (HL),E 73
Store the low byte of the day number.
4F21
LD A,D 7A
Take the high byte.
4F22
OR (HL) B6
Merge with the existing flags byte.
4F23
LD (HL),A 77
Store it back.
4F24
EX DE,HL EB
Move the day number back into Register Pair HL.
4F25
POP AF F1
Restore the month value.
4F26
LD E,A 5F
Save it in Register E.
4F27
ADD A,50H C6 50
Bias for the century wrap test.
4F29
CP 64H FE 64
Compare against 100.
4F2B
If below 100 (CARRY), JUMP past the subtraction.
4F2D
SUB 64H D6 64
Otherwise wrap the year modulo 100.
4F2F
LD (4044H),A 32 44 40
Store the two-digit year at 4044H.
4F32
LD A,E 7B
Recover the year value.
4F33
ADD A,03H C6 03
Add a fixed offset for the epoch's starting weekday.
4F35
RRCA 0F
Halve the year for the leap-day count.
4F36
RRCA 0F
Halve again, giving the number of leap years.
4F37
AND 0FH E6 0F
Keep the low nibble.
4F39
ADD A,E 83
Add the year back, forming the weekday accumulator.
4F3A
LD E,A 5F
Store the accumulator low byte in Register E.
4F3B
LD D,00H 16 00
Clear the high byte.
4F3D
ADD HL,DE 19
Add the year/leap accumulator to the day number.
4F3E
INC HL 23
Add one for the base weekday.
4F3F
LD A,07H 3E 07
Load 7, the divisor for days of the week.
4F41
GOSUB to the 16-bit divide at 4BA9H, so Register A becomes the day-of-week remainder.
4F44
INC A 3C
Make the day-of-week one-based.
4F45
LD B,A 47
Keep it in Register B for the display below.
4F47
LD C,A 4F
Save the doubled value in Register C.
4F48
LD HL,4048H 21 48 40
Point at the date-flags byte at 4048H.
4F4B
LD A,(HL) 7E
Load the current flags.
4F4C
AND 0F1H E6 F1
Clear the day-of-week field bits.
4F4E
OR C B1
Insert the day-of-week value.
4F4F
LD (HL),A 77
Store the updated flags, recording the day of the week.
4F50H - Display Date And Time
Positions the cursor and displays the day-of-week name, the date, and the time using the display helpers.
4F50
LD HL,3F17H 21 17 3F
Point at the screen position for the date/time line.
4F53
LD (4020H),HL 22 20 40
Set the cursor position (CURPOS) to it.
4F56
LD HL,42C7H 21 C7 42
Point at the day-name table.
4F59
GOSUB to display the three-character day-of-week name (indexed by Register B).
4F5C
GOSUB to display a comma and space after the day name.
4F5F
LD A,(4046H) 3A 46 40
Load the month value.
4F62
LD B,A 47
Keep it in Register B.
4F63
LD L,0DCH 2E DC
Point at the month-name table.
4F65
GOSUB to display the three-character month name.
4F68
GOSUB to display a space.
4F6B
LD A,(4045H) 3A 45 40
Load the day of month.
4F6E
LD D,00H 16 00
Clear the leading-zero-suppression control so the day shows in full.
4F70
GOSUB to display the day as two decimal digits.
4F73
GOSUB to display a comma and space.
4F76
LD A,(4044H) 3A 44 40
Load the two-digit year.
4F79
LD D,14H 16 14
Load the century base for the year display.
4F7B
CP 0CH FE 0C
Compare the year against the century-wrap threshold.
4F7D
If below the threshold (CARRY), JUMP past the century decrement.
4F7F
DEC D 15
Otherwise use the earlier century.
4F80
PUSH AF F5
Save the year.
4F81
LD A,D 7A
Load the century.
4F82
GOSUB to display the century digits.
4F85
POP AF F1
Restore the year.
4F86
GOSUB to display the two-digit year.
4F89H - Prompt For And Parse Time
Displays the "Time ? " prompt, reads and validates the hours, minutes, and seconds, and copies them into the software clock counters.
4F89
LD A,(42C3H) 3A C3 42
Load the chain byte at 42C3H that suppresses the time prompt when a command is queued.
4F8D
If the time prompt is suppressed (NZ), JUMP to 4FBDH to skip it.
4F8F
XOR A AF
Clear Register A.
4F90
LD HL,4318H 21 18 43
Point at the time working area at 4318H.
4F93
LD (HL),A 77
Clear the first time byte.
4F95
LD (HL),A 77
Clear the second time byte.
4F97
LD (HL),A 77
Clear the third time byte, so hours, minutes and seconds start at zero.
4F98
PUSH HL E5
Save the working pointer.
4F99
LD HL,3F57H 21 57 3F
Time Prompt
Point at the screen position for the time prompt.
4F9C
LD DE,50BFH 11 BF 50
Point at the "Time ? " prompt string.
4F9F
LD BC,083BH 01 3B 08
Load the field/delimiter parameters.
4FA2
GOSUB to the prompt-and-parse routine at 431BH to read the time fields.
4FA5
POP HL E1
Restore the working pointer to the parsed time.
4FA6
LD A,17H 3E 17
Load 23, the maximum hour.
4FA8
CP (HL) BE
Compare against the entered hours.
4FA9
If the hours exceed 23 (CARRY), re-prompt by JUMPing to 4F8FH.
4FAB
DEC HL 2B
Step to the minutes.
4FAC
LD A,3BH 3E 3B
Load 59, the maximum minutes/seconds.
4FAE
CP (HL) BE
Compare against the entered minutes.
4FAF
If the minutes exceed 59 (CARRY), re-prompt.
4FB1
DEC HL 2B
Step to the seconds.
4FB2
CP (HL) BE
Compare against the entered seconds.
4FB3
If the seconds exceed 59 (CARRY), re-prompt.
4FB5
LD DE,4041H 11 41 40
Point Register Pair DE at the software clock seconds counter at 4041H.
4FB8
LD BC,0003H 01 03 00
Load 3, the number of time bytes to copy.
4FBB
LDIR ED B0
Copy the parsed seconds, minutes, and hours into the running software clock.
4FBDH - Install Clock Task And Load Drive Configuration
Installs the software real-time-clock task into the task table, copies the configuration for drives 1 to 3 into their DCT blocks, and reads the directory.
4FBD
LD DE,45BEH 11 BE 45
Point Register Pair DE at the software RTC task handler block at 45BEH.
4FC0
LD A,07H 3E 07
Select task slot 7 for the clock.
4FC2
GOSUB to the install-task vector at 4410H to place the RTC task in slot 7 of the Task Control Block table.
4FC5
LD HL,422AH 21 2A 42
Point at the drive-1-to-3 configuration in the buffer.
4FC8
LD DE,470AH 11 0A 47
Point at the DCT block for drive 1 (offset 0AH into DCT$).
4FCB
LD BC,0046H 01 46 00
Load the byte count covering drives 1 to 3.
4FCE
LD A,(442FH) 3A 2F 44
Load the SVDAT1$ flag at 442FH.
4FD1
RRCA 0F
Rotate its low bit into carry.
4FD2
If the flag bit is set (CARRY), JUMP to the copy with the current source.
4FD4
LD L,7AH 2E 7A
Otherwise select the alternate configuration source page.
4FD6
LDIR ED B0
Block-copy the drive 1 to 3 configuration into DCT$.
4FD8
GOSUB to fetch the directory track from the drive block.
4FDB
LD E,C 59
Set the sector selector.
4FDC
LD L,C 69
Set the buffer offset.
4FDD
GOSUB to read the directory sector into the buffer.
4FE0
If the directory read failed (NZ), JUMP to the @ERROR vector at 4409H.
4FE3H - Boot Finalization
Applies directory-derived options, paints the sign-on line, patches the dispatcher for the operating mode, checks for a boot-time DEBUG key, copies vector tables into place, and hands off to load the start-up command.
4FE3
LD L,0CDH 2E CD
Point at a directory field in the buffer.
4FE6
AND 20H E6 20
Isolate the relevant option bit.
4FE8
LD B,A 47
Save it in Register B.
4FE9
LD A,(4704H) 3A 04 47
Load drive 0's DCT select byte.
4FEC
OR B B0
Merge in the directory option bit.
4FED
LD (4704H),A 32 04 47
Store it back into the DCT block.
4FF0
LD L,0D0H 2E D0
Point at the sign-on / version field in the directory buffer.
4FF2
LD DE,3CD6H 11 D6 3C
Point at the screen position for the sign-on line.
4FF5
LD BC,0008H 01 08 00
Load an eight-byte count.
4FF8
LDIR ED B0
Copy eight characters to the screen.
4FFA
LD C,08H 0E 08
Reload the count for a second field.
4FFC
INC DE 13
Skip a screen position.
4FFD
INC DE 13
Skip another.
4FFE
LDIR ED B0
Copy the second eight characters to the screen.
5000
LD A,(HL) 7E
Load the next directory byte.
5001
CP 2AH FE 2A
Compare it against the asterisk that marks a special configuration.
5003
If it is not an asterisk (NZ), JUMP to 5012H.
5005
INC HL 23
Otherwise step past the asterisk.
5006
LD A,0E6H 3E E6
Load the AND opcode value.
5008
LD (505EH),A 32 5E 50
Self-Modifying Code
Patch the boot-copied routine's operand at 505EH.
500B
LD A,0C8H 3E C8
Load the RET Z opcode value.
500D
LD (4BF0H),A 32 F0 4B
Self-Modifying Code
Patch the dispatcher's spare byte at 4BF0H, enabling the special-mode return in the supervisor path.
5010
JUMP to 501FH to continue.
5012
LD A,(3841H) 3A 41 38
Read keyboard row 3841H (the H/I-O row).
5015
BIT 4,A CB 67
Test bit 4, a key held to request DEBUG at boot.
5017
If that key is held (NZ), GOSUB to the @DEBUG vector at 440DH to enter the monitor.
501A
CPL 2F
Complement the key reading.
501B
AND 01H E6 01
Isolate the low bit.
501D
If clear (Z), JUMP to 5022H.
501F
LD A,(HL) 7E
Load the next directory byte.
5020
CP 0DH FE 0D
Compare it against carriage return, testing for an end-of-line command marker.
5022
POP DE D1
Recover the saved configuration byte into Register Pair DE.
5023
LD A,D 7A
Take its high byte.
5024
LD DE,4400H 11 00 44
Point Register Pair DE at the DOS-ready entry region as the default start-up target.
5027
If the command marker matched (Z), JUMP to 502FH.
5029
LD E,05H 1E 05
Otherwise adjust the target low byte.
502B
PUSH DE D5
Push that target.
502C
LD DE,4467H 11 67 44
Point Register Pair DE at the @DSPLY vector as a secondary target.
502F
PUSH DE D5
Push the chosen start-up target.
5030
LD DE,4318H 11 18 43
Point Register Pair DE at the 4318H workspace destination.
5034
LD BC,0020H 01 20 00
Load a 32-byte count.
5037
LDIR ED B0
Copy a 32-byte block into the 4318H workspace.
5039
LD HL,505AH 21 5A 50
Point at the routine at 505AH to be relocated.
503C
LD DE,405DH 11 5D 40
Point at its resident destination at 405DH (the DBGSV$ area).
503F
PUSH DE D5
Push the destination as the routine's entry to run after the copy.
5040
LD C,2AH 0E 2A
Load the 42-byte length of the routine.
5042
LDIR ED B0
Relocate the routine to 405DH.
5044
LD HL,3860H 21 60 38
Point at keyboard row 3860H.
5047
OR (HL) B6
Merge the key state into the mode value in Register A.
5048
LD DE,4700H 11 00 47
Preset Register Pair DE to DCT$ for the relocated routine.
504B
LD HL,4152H 21 52 41
Preset Register Pair HL to the Disk BASIC vector area source.
504E
LD C,50H 0E 50
Preset the copy count for the relocated routine.
5050
EXX D9
Switch to the alternate register set, carrying those presets to the relocated routine.
5051
LD DE,4338H 11 38 43
Point the primary Register Pair DE at the 4338H area.
5054
AND 82H E6 82
Test the mode bits (auto-run and command-present).
5056
If no start-up command is pending (Z), JUMP to the @LOAD vector at 4430H to bring in the start-up program (the relocated routine at 405DH runs on the way through).
5059
RET C9
Otherwise return, letting the pushed start-up target take control.
505AH - Boot-Relocated Interrupt-Vector Installer
Copied to 405DH at boot and run there. It applies the operating-mode flags, optionally programs an output port, block-copies the presets carried in the alternate registers, installs the RST 38H interrupt vector, sets the cursor, and returns to start the system.
505A
LD HL,430FH 21 0F 43
Point at the system flag byte at 430FH.
505D
RES 4,(HL) CB A6
Clear bit 4, re-enabling BREAK/DEBUG-trap handling now that boot is over.
505F
The byte at 505EH here is patched at boot (4FE3H path); depending on that patch this either falls through or JUMPs to 5076H.
5061
BIT 3,(HL) CB 5E
Test bit 3 of the system flags.
5063
If clear (Z), JUMP past the port programming.
5065
LD BC,01FEH 01 FE 01
Load Register B with 1 and Register C with the expansion port FEH.
5068
LD HL,4427H 21 27 44
Point at the LDRV$ drive-latch image at 4427H.
506B
LD A,(HL) 7E
Load the current latch image.
506C
OR B B0
Set the required bit.
506D
LD (HL),A 77
Store it back.
506E
OUT (C),A ED 79
Write it to port FEH, setting the expansion latch.
5070
EXX D9
Switch to the alternate registers holding the presets from 5048H-504E.
5071
LDIR ED B0
Block-copy the preset table (the Disk BASIC vectors) into place.
5073
GOSUB to the patchable hook at 4303H.
5076
LD HL,3F40H 21 40 3F
Point at the screen position for the cursor's home.
5079
LD (4020H),HL 22 20 40
Set the cursor position (CURPOS).
507C
LD HL,4012H 21 12 40
Point at the RST 38H interrupt vector at 4012H.
507F
LD (HL),0C3H 36 C3
Store the JP opcode there, arming the interrupt vector so ticks now reach the interrupt service routine at 4518H.
5081
POP HL E1
Pop the start-up target address into Register Pair HL.
5082
EI FB
Enable interrupts.
5083
RET C9
Return, transferring control to the start-up target to begin normal operation.
5084H - Display Three-Character Label
Displays a three-character field from a table, indexed by Register B, via the ROM character-output routine. Used with the separator helper below to show a labelled value such as the day name.
5084
DEC B 05
Decrement the entry index in Register B to a zero-based value.
5085
LD A,L 7D
Take the table base low byte from Register L.
5086
ADD A,B 80
Add the index once.
5087
ADD A,B 80
Add it a second time.
5088
ADD A,B 80
Add it a third time, so the index is multiplied by three (three characters per entry).
5089
LD L,A 6F
Point Register L at the selected three-character entry.
508A
LD B,03H 06 03
Load Register B with 3, the number of characters to display.
508C
LD A,(HL) 7E
Char Loop
Load the next character of the entry.
508D
INC HL 23
Advance to the following character.
508E
GOSUB to the ROM character-display routine at 0033H to show the character.
5091
Char Loop
Decrement Register B and LOOP BACK to 508CH until all three characters are shown.
5094H - Display Separator
Writes a comma and a space to the screen, and a shorter entry that writes just a space.
5094
LD A,2CH 3E 2C
Load the comma character.
5096
GOSUB to the ROM display routine at 0033H to show the comma.
5099
LD A,20H 3E 20
Load the space character (also the space-only entry point).
509B
JUMP to the ROM display routine at 0033H to show the space and return through it.
509EH - Display Two-Digit Decimal
Displays the value in Register A as up to two decimal digits, suppressing a leading zero when Register D is zero. Uses the ROM character-output routine.
509E
LD E,2FH 1E 2F
Seed Register E just below ASCII '0' as the tens-digit accumulator.
50A0
INC E 1C
Tens Loop
Count the tens digit up.
50A1
SUB 0AH D6 0A
Subtract ten from the value.
50A3
Tens Loop
While ten or more remains (NO CARRY), LOOP BACK to 50A0H.
50A5
PUSH AF F5
Save the remaining units value.
50A6
LD A,D 7A
Load the leading-zero-suppression control from Register D.
50A8
LD A,E 7B
Load the tens digit into Register A.
50A9
If suppression is off (NZ), JUMP to 50ADH to always show the tens digit.
50AB
CP 30H FE 30
Otherwise compare the tens digit against ASCII '0'.
50AD
Display the tens digit unless it is a suppressed leading zero (Z).
50B0
POP AF F1
Restore the units value.
50B1
ADD A,3AH C6 3A
Convert the units value back to an ASCII digit.
50B3
JUMP to the ROM display routine at 0033H to show the units digit and return.
50B6H - Date And Time Prompt Strings
The two prompt strings used by the boot date/time entry. Each begins with the 1EH control code, holds the label and a question mark, and ends with the 03H string terminator.
50B6
DEFB 1EH; DEFM 'Date ? '; DEFB 03H 1E 44 61 74 65 20 3F 20 03
The date prompt: a 1EH control code, the text "Date ? ", and the 03H terminator.
50BF
DEFB 1EH; DEFM 'Time ? '; DEFB 03H 1E 54 69 6D 65 20 3F 20 03
The time prompt: a 1EH control code, the text "Time ? ", and the 03H terminator.
50C8H - End Fill
Zero fill from 50C8H to the end of the file image at 50FFH.
50C8-50FF
DEFB 00H x 56 00 x 56
Fifty-six zero bytes padding the module to its end. Not executed.