TRS-80 DOS - NEWDOS/80 v2.0 for the Model III - SYS0/SYS Disassembled

Page Customization

Introduction/Summary

NEWDOS/80 v2.0 SYS0/SYS Disassembly - The Resident DOS (Model III)

SYS0/SYS is the part of NEWDOS/80 v2.0 that stays in memory. The ROM's boot loader reads the boot sector into 4300H, the boot sector loads SYS0/SYS from the system diskette, checks that the byte at its transfer address 4D00H is A5H and jumps to 4D01H. The file holds 3,723 bytes in 23 load records between 400CH and 51EBH (with a record of type 1FH, the text ALIGNMENT FILLER, which the loader skips). The records skip the areas the ROM owns. The reset code at 4D00H-4F95H and its data up to 51EBH run once and are then overwritten, because 4D00H-51FFH is where the system overlays are loaded.

The resident part gives NEWDOS/80 its fixed entry points at 4400H-447FH and 402DH-4033H, the RST 28H overlay dispatcher, the RST 30H DEBUG entry, a keyboard driver with a clock driven repeat key, the clock routine that turns the Model III's 30 (or 25) clock interrupts a second into 40 ticks of 25 ms for the timer routines, the D-F-G, 1-2-3 and J-K-L key combinations, the sector I/O through the 1793 controller (ended by an NMI), drive selection, and all of the file input and output: read, write, position, the conversion of NEXT into a sector of the diskette through the extents in the FCB and the directory, the program loader and ROUTE. Opening, closing, the commands, the error messages and DEBUG live in the overlays and are reached through RST 28H.

Every byte of the file is shown once on this page, in address order, checked against S:\1\SYS0.SYS and its listing. The areas between the load records are shown as DEFS rows without bytes, with what is kept there.

Memory Map

Address RangeUse
4000H-400BH
12 bytes
The ROM's RST 08H-20H vectors; not loaded.
400CH-4014H
9 bytes
RST 28H vector (JP 4B67H), RST 30H vector (JP 45D4H), RST 38H vector (JP 3018H, the ROM's interrupt handler).
4015H-402CH
24 bytes
The ROM's keyboard, display and printer control blocks; not loaded.
402DH-4035H
9 bytes
DOS READY exit, exit after a displayed error, device dispatch hook.
4036H-4051H
28 bytes
The ROM's keyboard row images and interrupt vectors, the clock vector 4046H and the NMI vector 4049H; not loaded.
4052H-407FH
46 bytes
Bytes of 00H loaded by SYS0/SYS and used by SYS5/SYS.
4080H-427DH
510 bytes
The ROM's work areas (with 41FDH-41FEH and 4203H-4205H loaded by SYS0/SYS), the clock 4216H-421CH, the command buffer 4225H, the saved track registers 4276H-4279H.
427EH-42C8H
75 bytes
The current drive, its PDRIVE bytes 4280H-4287H, the overlay number, the system flags, option copies, the PDRIVE table 4291H-42B8H, option values.
42C9H-42D1H
9 bytes
The top of memory, the A5H mark of an active DOS, the clock copy; not loaded.
42D2H-42FFH
46 bytes
Bytes for SYS14/SYS and the six ROUTE entries.
4300H-43FFH
256 bytes
The DOS sector buffer (directory sectors, system files, the boot sector at reset); not loaded.
4400H-447FH
128 bytes
The DOS entry vectors, HIMEM at 4411H, the tick counter at 441FH, the BREAK hook at 4478H.
4480H-449FH
32 bytes
The DOS FCB.
44A0H-44BFH
32 bytes
Not loaded; used by SYS9/SYS.
44C0H-44D1H
18 bytes
The FCB for loading system files.
44D2H-4CA9H
2,008 bytes
The resident routines.
4CAAH-4CFFH
86 bytes
Bytes of 00H.
4D00H-4F95H
662 bytes
The reset code, run once from 4D01H.
4F96H-51EBH
598 bytes
The reset messages, questions and limits, then 00H; overwritten by the overlays.

RST 28H and the System Overlays

A function code in Register A names a system file by its directory slot (bits 0-4) and a function in it (bits 5-7). Slot 02H is SYS0/SYS, 03H SYS1/SYS, and so on: slot = 3 + the SYS number. Bits 0-2 of the slot are the directory sector minus 2 that holds the file's entry and bits 3-4 its entry in that sector (4B86H-4B94H). Codes below 20H are BREAK requests from the ROM's keyboard routine and go to 4478H. The dispatcher at 4B67H loads the file through the FCB at 44C0H if it is not the overlay already in memory (4288H), then calls its transfer address. It drops the return address of the RST, so CALL 4982H (an RST 28H) calls an overlay function and comes back.

CodeFunction (where SYS0/SYS uses it)
23H, 43H, 63HSYS1/SYS functions 1-3: DOS READY (4400H), the exit after a displayed error (4030H), execute a command (4405H).
83H, A3H, C3H, E3HSYS1/SYS functions 4-7: extract a filespec (441CH), default extension (4473H), DOS-CALL (4419H), function 7 with sub-function 04H in Register C for MINI-DOS (45ACH).
24H, 44H, 64H, A4H, C4HSYS2/SYS: open an existing file (4424H), open a new or existing file (4420H), allocate granules (4862H), load (4430H), load and run (4433H).
25H, 45H, 65H, 85H, A5H, C5HSYS3/SYS: close (4428H), kill (442CH), enqueue a timer routine (447BH), dequeue it (4413H), the screen print of J-K-L (45D1H), write EOF (4451H).
26H, 46HSYS4/SYS: the error message (4409H), the reset's error report (4DE0H).
27HSYS5/SYS: DEBUG (45D5H).
2BH, 4BH, CBHSYS9/SYS: enqueue and dequeue a named routine (4461H, 4464H), the next key of a chain file (44F2H).

Sector I/O on the Model III

The controller is reached through ports F0H-F3H and the drive through port F4H (drive bit, side, write precompensation, wait states, density; the byte is kept at 427FH). A sector number on the diskette is divided by the sectors per track into a track and a sector (4612H); the PDRIVE flags then add 1 to the track or the sector, double the track for double stepping, split a two-sided track into two sides and set the density and precompensation bits. After a Seek, the track register is loaded with the number written in the sector headers, the NMI vector at 4049H is set to JP 46B6H, the NMI is enabled for the controller's interrupt request (port E4H) and the command is issued. The 256 bytes are moved with INI or OUTI on port F3H while the wait state bit of port F4H holds the CPU until each byte is ready, and the CPU then waits at 46A9H for the NMI. 46B6H resets the stack, makes the NMI vector a RETN, reads the status, puts the physical track back and retries errors as option AM allows.

The File Control Block

OffsetContents (as used by SYS0/SYS)
+0Bit 7 open, bit 2 bad FCB, bit 1 NEXT and EOF are sectors of the whole diskette, bit 0 write with the directory mark.
+1Bit 7 record mode, bit 6 EOF only moves forward, bit 5 the buffer does not hold NEXT, bit 4 changes waiting, bit 3 the second extent set is valid, bits 0-2 the access level.
+2Bit 5 the FPDE has been marked as updated by this FCB.
+3, +4The buffer address.
+5NEXT's low byte (the byte within the sector).
+6The drive.
+7The DEC of the file's directory entry.
+8EOF's low byte.
+9The logical record length (00H = 256).
+10, +11NEXT's middle and high bytes (the sector of the file).
+12, +13EOF's middle and high bytes.
+14 to +21The first four extents (lump; bits 5-7 the first granule, bits 0-4 the granules minus 1; FFH = the end).
+22, +23The granules of the file before the extents at +24.
+24 to +31Four more extents, copied from an FXDE.

Error Codes Returned by SYS0/SYS

CodeMeaning (manual 9.1)
03H-08HRead errors from the controller: 03H lost data, 04H CRC (parity) error, 05H record not found, 06H a deleted data mark (the directory mark), 08H device not available (46E0H-46E5H).
0BH-10HThe same for a write, plus 0EH write fault and 0FH write protected diskette.
11H, 12HDIRECTORY READ ERROR (48BFH), DIRECTORY WRITE ERROR (48D0H).
14HTRACK # TOO HIGH (461EH).
1CH, 1DHEND OF FILE ENCOUNTERED, PAST END OF FILE (496DH, 4971H).
20HILLEGAL OR MISSING DRIVE # (4729H).
22H, 24HLOAD FILE FORMAT ERROR (4BDAH), TRIED TO LOAD READ ONLY MEMORY (4C00H).
25H, 26HILLEGAL ACCESS TRIED TO PROTECTED FILE (4965H), FILE NOT OPEN (4927H).
27HILLEGAL INITIALIZATION DATA ON SYSTEM DISKETTE (4DDDH, at reset).
2AHILLEGAL DOS FUNCTION (4457H).
2CH, 2DH, 2EHBAD DIRECTORY DATA (4815H), BAD FCB DATA (47C9H, 490AH), SYSTEM PROGRAM NOT FOUND (4BB8H).
31H, 34H, 3EHWRONG DISKETTE RECORD TYPE (48AAH), ILLEGAL KEYWORD OR SEPARATOR OR TERMINATOR (4C8FH), CAN'T EXTEND FILE VIA READ (485DH).

Notes from the Code

  • The DOS does not take over RST 38H: the vector at 4012H stays JP 3018H, and the DOS hooks the ROM's real-time clock vector at 4046H instead (4D27H). Its clock routine runs the ROM's clock first, then adds one third (or 0.6 with option BB) of a tick per interrupt, so that the timer routines get 40 ticks a second, sometimes two in one interrupt (manual 3.8).
  • The vector at 4410H is a RET; the timer routines are enqueued through 447BH. 4411H-4412H hold HIMEM, 4478H is the BREAK hook (C3H = BREAK gives 00H, C9H = BREAK is the key 01H).
  • The bytes after the RST 28H instructions in the vector table are never executed: 4403H-4404H hold the entry address of the last program loaded, 441FH is the tick counter.
  • The sector routine changes its own transfer loop: INI for a read, OUTI for a write (46ABH), and the first byte of a write is followed by a delay of 150 DJNZ passes (469FH). The end of the command arrives as an NMI; afterwards the NMI vector is left as RETN.
  • With option AP above the top of memory, the reset stores the top of memory minus option AP as HIMEM (4DCDH).
  • The date and time answers are stored before they are range-checked (4F7BH); an invalid answer can leave some clock bytes changed until the question is answered again. After a power-on either option AY or option AZ asks for the date and time (4ECDH).
  • If SYS4/SYS (the error messages) cannot be loaded, the dispatcher executes HALT (4BBDH).

Variables

Resident Variables

AddressContents
41FDH-41FEH
2 bytes
The ROM's last-row and last-column bytes of the keyboard scan, loaded as 36H, 00H; read by the keyboard driver (44FEH, 452BH).
4275H
1 byte
A5H, loaded by SYS0/SYS; SYS0/SYS does not read it.
4276H-4279H
4 bytes
The saved track register of drives 0-3 (4737H-473CH, 476AH).
427EH
1 byte
The current drive number (4734H).
427FH
1 byte
The byte written to port F4H for the current drive: drive bit, side (bit 4), precompensation (bit 5), double density (bit 7).
4280H-4287H
8 bytes
The current drive's PDRIVE bytes 0-7 (475CH); Index Register IY points here.
4288H
1 byte
The directory slot of the overlay in 4D00H-51FFH (4B85H).
4289H
1 byte
System flags: bit 2 RUN-ONLY, bit 4 option AG, bit 5 keys from a chain file, bit 6 an overlay function running.
428AH-428BH
2 bytes
Flags of the overlays; not used by SYS0/SYS.
428CH-428FH
4 bytes
Copies of bytes F0H-F3H of the system data sector (4D66H, 4D6CH).
4290H
1 byte
Option AX (4DBCH).
4291H-42B8H
40 bytes
The PDRIVE table, ten bytes for each of drives 0-3.
42B9H-42BAH
2 bytes
The address of the current drive's PDRIVE entry (4754H).
42BBH-42BEH
4 bytes
Words used by SYS1/SYS.
42BFH-42C2H
4 bytes
Options AL, AN, AO and BJ (4D72H-4D98H).
42C3H-42C8H
6 bytes
Bytes used by SYS5/SYS, SYS9/SYS and SYS1/SYS.
42C9H-42CAH
2 bytes
The top of memory found at reset (4D10H); not loaded.
42CBH
1 byte
A5H while the DOS is active (4F06H); not loaded.
42CCH-42D1H
6 bytes
The copy of the clock 4217H-421CH, made every second (4567H); not loaded.
42D2H-42D5H
4 bytes
Bytes used by SYS14/SYS.
42D6H-42FFH
42 bytes
Six ROUTE entries of 7 bytes.
4300H-43FFH
256 bytes
The DOS sector buffer; not loaded.
4403H-4404H
2 bytes
The entry address of the last program loaded.
4411H-4412H
2 bytes
HIMEM (4D13H, 4DD0H).
441FH
1 byte
The tick counter, counted every 25 ms (457AH).
4423H, 4427H, 442BH, 442FH, 4454H-4456H, 4476H-4477H
9 bytes
Bytes after RST 28H instructions in the vector table; not used by SYS0/SYS.
4480H-449FH
32 bytes
The DOS FCB.
44A0H-44BFH
32 bytes
Used by SYS9/SYS; not loaded.
44C0H-44D1H
18 bytes
The FCB for loading system files.
4CA1H-4CA3H
3 bytes
The NL device's control block (type C9H, driver 4B49H).
504BH-5052H
8 bytes
The copy of bytes F8H-FFH of the system data sector, used during the reset (4D61H, 4F11H).

Self-Modifying Code

AddressContents
4508H
1 byte
Option AV, the first repeat delay after a release (stored by 4E25H).
4512H
1 byte
The repeat countdown in ticks (451DH, 4535H; counted down by 4582H).
4513H
1 byte
JR (18H), or JR NZ (20H) with option AU (4E1CH).
452AH
1 byte
The delay for a repeated key (450BH, 453AH).
4534H
1 byte
Option AV, the first repeat delay of a new key (4E22H).
4544H
1 byte
The CLEAR key code 1FH, or 00H with option AQ (4E6BH).
4552H-4553H
2 bytes
The clock routine the vector at 4046H held before the reset (4D2AH).
455DH
1 byte
Clock interrupts per second: 1EH, or 19H with option BB (4EB2H).
456BH-456CH
2 bytes
The fraction of a tick carried between interrupts (4571H).
456EH-456FH
2 bytes
The fraction of a tick per interrupt: 5555H, or 9999H with option BB (4EB8H).
4584H-4585H
2 bytes
The head of the timer routine chain (SYS3/SYS).
459CH
1 byte
NOP, or RET while the keyboard driver runs (44FBH, 4541H, 4B7AH).
45AAH
1 byte
JR, or JR NZ with option AF (4E63H).
45B6H
1 byte
JR, or JR NZ with option AE (4E74H).
45D0H
1 byte
RET, or RET NZ with option AD (4E5AH).
4605H
1 byte
Option AM, the tries of a sector read or write (4D92H).
4687H
1 byte
The read or write command (45EDH).
468FH
1 byte
The first byte count: 02H read, 97H write (46ABH).
469EH
1 byte
A2H (INI) or A3H (OUTI) (46AFH).
46A6H
1 byte
A2H (INI) or A3H (OUTI) (46B2H).
46BAH-46BBH
2 bytes
The Stack Pointer saved for the NMI routine (4672H).
46E1H
1 byte
The error base: 00H read, 08H write (45FDH).
4727H
1 byte
Option AL, the number of drives (4D75H).
478AH
1 byte
The status delay, 8 x option BJ (4DABH).
4799H-479AH
2 bytes
The diskette test time-out, 2A00H x option BJ (4DB6H).
480FH
1 byte
The DEC of the directory entry last looked at (4845H).
4860H
1 byte
The JR offset: 00H allocate, B6H for a read (47B5H).
48D5H
1 byte
The directory sector in 4300H (4C19H, 4EE4H).
494BH-494CH
2 bytes
The address of the code run as a DOS function (492EH).
4973H-4974H
2 bytes
The Stack Pointer of the DOS function (4940H, 494EH).
4A2DH-4A2FH
3 bytes
LD A,(IX+01H), or NOP / LD A,FFH (SYS3/SYS).
4A30H
1 byte
AND (E6H) or OR (F6H) for the verify test (49DDH).
4A5FH
1 byte
Option AW, the write tries (4D80H).
4A81H-4A82H
2 bytes
The head of the ROUTE list (4E94H).
4BC3H-4BC4H
2 bytes
The transfer address of the overlay last loaded (4BAEH).
4C0FH-4C10H
2 bytes
The FCB used by the program loader (4BCDH).
4C94H
1 byte
Option BJ, the passes of the delay routine (4D9BH).
4F8EH
1 byte
The date or time separator (4F5DH).

Addresses Outside SYS0/SYS

AddressContents
3801H, 3802H, 3810H, 3840H
4 bytes
Keyboard rows: D-F-G (45A5H), J-K-L (45CAH), 1-2-3 (45B1H), ENTER and up-arrow (4F18H, 4E35H).
4016H-4017H
2 bytes
The keyboard control block's driver address (4E3FH).
4019H
1 byte
The ROM's caps lock flag (4E49H).
401CH
1 byte
The ROM's cursor blink switch (4E51H).
401DH, 4025H
2 control blocks
The display and printer control blocks (4B4CH, 4B62H, 4E97H).
4023H
1 byte
The ROM's cursor character (4E2EH).
4036H-403CH
7 bytes
The ROM's keyboard row images (4504H, 4518H).
4046H-4048H
3 bytes
The ROM's real-time clock vector (4D27H-4D30H).
4049H-404BH
3 bytes
The NMI vector (467FH, 4679H, 46BFH).
4201H-4202H
2 bytes
The ROM's repeat counter (4515H).
4216H-421CH
7 bytes
The ROM's clock: the count of interrupts in the second, seconds, minutes, hours, year, day, month.
4225H-4244H
32 bytes
The command buffer: the AUTO command (4F01H) and the date and time answers (4F55H).
Ports E4H, F0H-F4H
6 ports
The NMI mask, the controller's command/status, track, sector and data registers, the drive select register.

Disassembly

4000H - RST Vectors

The Model III ROM sends every RST instruction from 0008H to 0038H through a three-byte jump in RAM at 4000H-4014H. The ROM sets up 4000H-400BH itself; SYS0/SYS loads only the RST 28H, RST 30H and RST 38H vectors.

4000-400B
DEFS 12
Not in the file. The RST 08H (4000H), RST 10H (4003H), RST 18H (4006H) and RST 20H (4009H) vectors, which the ROM fills in at power-up for BASIC's syntax check, character fetch, compare and type test. SYS0/SYS leaves them as the ROM set them.
400C
RST 28H vector: JUMP to 4B67H, the DOS function dispatcher. Register A holds the function code; codes below 20H are BREAK key requests from the ROM's keyboard driver and go to the BREAK hook at 4478H.
400F
RST 30H vector: JUMP to 45D4H, which enters DEBUG (SYS5/SYS). A breakpoint byte F7H (RST 30H) planted by DEBUG arrives here.
4012
RST 38H vector (the maskable interrupt, Interrupt Mode 1): JUMP to the ROM's interrupt handler at 3018H, which reads the interrupt latch (port E0H) and calls the RAM vectors at 4046H (real-time clock), 403DH, 4040H, 4043H, 4206H and 4209H. SYS0/SYS hooks the clock vector at 4046H (4D27H) instead of taking over RST 38H.
4015-402C
DEFS 24
Not in the file. The ROM's three Device Control Blocks: the keyboard (4015H-401CH; its driver address at 4016H-4017H is replaced by 44EBH at reset when option AJ is Y, 4E3CH), the display (401DH-4024H; 4023H is the cursor character, set from option BI at 4E2EH) and the printer (4025H-402CH).

402DH - DOS Exits

The two fixed exits of every program (manual sections 3.2 and 3.3) and the ROM's device dispatch hook.

402D
Normal program exit: JUMP to 4400H, DOS READY. Programs end with JP 402DH.
4030
LD A,43H 3E 43
Error exit, entered after a program has already displayed its own error message. Load Register A with function code 43H (SYS1/SYS, function 2), the request to show DOS READY without another message. 4203H, the ROM's disk error and BREAK vector, also comes here.
4032
RST 28H EF
RST 28H: Register A (43H) asks the dispatcher at 4B67H to load SYS1/SYS and run function 2. It does not return.
4033
The ROM's character I/O dispatcher (0687H) jumps here when the type byte of the control block at Register Pair DE does not match the request: JUMP to 4A80H, which handles routed devices and FCBs used as devices.
4036-4051
DEFS 28
Not in the file. ROM work areas and interrupt vectors: the keyboard row images 4036H-403CH (41FDH and 41FEH point into them), the vectors 403DH, 4040H, 4043H and 4046H (real-time clock; 4D27H puts 4551H here) called by the ROM's interrupt handler, and the NMI vector 4049H-404BH that the sector I/O routine sets to JP 46B6H for each transfer (467DH) and to RETN afterwards (46BFH).

4052H - Zero Bytes

Loaded by SYS0/SYS as 46 bytes of 00H. Nothing in SYS0/SYS reads or writes them; SYS5/SYS (DEBUG) refers to 4052H.

4052-407F
DEFB 00H x 46 00 x 46
46 bytes of 00H, 4052H-407FH, cleared by the load. SYS0/SYS never refers to this area; SYS5/SYS (DEBUG) addresses it at 4D27H and 4DA1H of its own code.
4080-41FC
DEFS 381
Not in the file. The ROM's BASIC work area (communication region, string area, variable pointers and the ROM's input buffer), left as the ROM set it.

41FDH - Keyboard Scan Bytes

Two bytes of the ROM's keyboard scan that SYS0/SYS loads with values of its own: the keyboard driver at 44EBH uses them to find the key that is held down.

41FD
DEFB 36H 36
The ROM's last-row byte: the low byte of the address of the row image (4036H-403CH) in which the ROM's keyboard routine (3408H) last found a pressed key. Loaded as 36H (the 3801H row). The keyboard driver reads it at 44FEH and 452BH.
41FE
DEFB 00H 00
The ROM's last-column byte: the bit mask of the key the ROM's keyboard routine last found (3404H). Loaded as 00H (no key). Read together with 41FDH as a word at 44FEH and 452BH.
41FF-4202
DEFS 4
Not in the file. 41FFH-4200H is the ROM's key repeat delay and 4201H-4202H its repeat counter (33D4H-33F3H); the keyboard driver clears 4201H-4202H at 4515H to force a repeat.

4203H - Disk Error and BREAK Vector

The ROM's cassette routines jump to 4203H on BREAK (3362H, 3677H, 3699H).

4203
JUMP to 4030H, the error exit: a BREAK during a ROM cassette operation returns to DOS READY through SYS1/SYS.
4206-4274
DEFS 111
Not in the file. The ROM's interrupt vectors 4206H and 4209H, its port ECH copy and cassette bytes (420CH-4214H), the real-time clock 4216H-421CH (heartbeat counter, seconds, minutes, hours, year, day, month; the clock routine at 4551H copies 4217H-421CH to 42CCH every second), the RS-232 and route control blocks, the command buffer 4225H-4264H (the AUTO command is copied here at 4EFBH and the date and time answers are typed here, 4F55H) and work bytes up to 4274H.

4275H - Marker Byte

A single byte loaded by SYS0/SYS.

4275
DEFB A5H A5
The byte A5H, loaded at 4275H. SYS0/SYS does not read it.
4276-427D
DEFS 8
Not in the file. 4276H-4279H hold the track register of drives 0-3, saved when a drive is deselected and loaded back when it is selected again (4737H-473CH, 476AH-476BH). 427AH-427DH are not used by SYS0/SYS.

427EH - Drive Selection Variables

The state of the current drive. 4723H fills these at every drive change.

427E
DEFB 00H 00
The current drive number (0-3). Stored by 4734H when a drive is selected; read by 4720H and by 4730H, which saves the old drive's track register.
427F
DEFB 00H 00
The byte written to port F4H to select the current drive: bit 0-3 the drive bit (set by 4749H), bit 4 side 1 (4654H), bit 5 write precompensation (463BH), bit 7 double density (4632H). The sector routine ORs in bit 6 (wait states) during a transfer (4693H); 4715H writes it to port F4H.
4280-4287
DEFB 11H, 23H, 03H, 23H, 0AH, 02H, 00H, 00H 11 23 03 23 0A 02 00 00
The first eight bytes of the current drive's PDRIVE entry, copied from 4291H + 10 x drive at every drive change (475CH). Index Register IY points to 4280H in the DOS functions and the reset code. As loaded they are drive 0's values below. 4282H: bits 0-1 the stepping rate (46F7H, 477AH bit 7 = head settle delay); 4283H: the track count (4617H, 4CA4H); 4284H: sectors per track (460EH); 4285H: granules per lump (4C23H); 4286H: the value written to the sector register at selection (475EH); 4287H: the disk I/O flags (4627H: bit 0 double density, bit 1 tracks counted from 1, bit 2 double step, bit 4 sectors counted from 1, bit 6 two sides).

4288H - Overlay and System Flags

The overlay number, the system flags and the copies of the option bytes of the system data sector.

4288
DEFB 00H 00
The number (directory slot, bits 0-4 of the RST 28H code) of the overlay now in 4D00H-51FFH; 00H = none. The dispatcher compares the requested slot with it at 4B82H and stores the new one at 4B85H; 4BB3H tests for SYS4/SYS (06H).
4289
DEFB 40H 40
System flags. Bit 6 is set by the dispatcher while an overlay function runs (4B75H) and cleared after it (4BC9H); bits 2, 3, 5 and 6 (mask 6CH) stop the key combinations (45A1H); bits 3, 5 and 6 decide whether the keyboard driver takes keys from a chain file (44EEH). The reset code sets bit 2 for option AB (4E0EH) and bit 4 for option AG (4E7BH). Loaded as 40H.
428A
DEFB 00H 00
Flags used by SYS1/SYS, SYS4/SYS, SYS6/SYS and SYS20/SYS. SYS0/SYS does not read or write this byte.
428B
DEFB 00H 00
Flags used by SYS1/SYS, SYS3/SYS, SYS6/SYS and SYS14/SYS. SYS0/SYS does not read or write this byte.
428C-428D
DEFB 00H, 00H 00 00
Copies of bytes F0H-F1H of the system data sector, stored at reset (4D66H). 428CH bit 6 (option AB) makes the reset run the AUTO command unconditionally (4F0BH); bit 4 is set at 4E7FH; 428DH bit 6 (option BC) is cleared for option AB (4E12H).
428E-428F
DEFB 00H, 00H 00 00
Copies of bytes F2H-F3H of the system data sector, stored at reset (4D6CH). SYS0/SYS does not read them again.
4290
DEFB 5AH 5A
SYSTEM option AX, the ASCII code of the highest character the printer can print (manual, SYSTEM command), stored at reset from byte A8H of the system data sector (4DBCH). Loaded as 5AH. SYS0/SYS does not use the value itself; the overlays that print read it here.

4291H - PDRIVE Table

Ten bytes for each of drives 0-3, copied at reset from the system data sector for every drive below option AL (4DE3H-4DEFH). A drive's first eight bytes are copied to 4280H-4287H when it is selected (475CH).

4291-429A
DEFB 11H, 23H, 03H, 23H, 0AH, 02H, 00H, 00H, 11H, 02H 11 23 03 23 0A 02 00 00 11 02
Drive 0 as loaded: +0 the directory's first lump 11H (17), +1 the number of lumps 23H, +2 the flags 03H (stepping rate 3), +3 the track count 23H (35), +4 sectors per track 0AH, +5 granules per lump 02H, +6 the sector register value 00H, +7 the disk I/O flags 00H, +8 11H, +9 02H. The reset code replaces +2, +6 and +7 from bytes FEH, FDH and FFH of the boot sector (4D3BH-4D44H) and then the whole entry from the system data sector.
429B-42A4
DEFB FFH, 01H, 00H, 01H, 00H, 00H, 00H, 00H, FFH, 00H FF 01 00 01 00 00 00 00 FF 00
Drive 1 as loaded, replaced at reset from the system data sector when option AL is above 1.
42A5-42AE
DEFB FFH, 01H, 00H, 01H, 00H, 00H, 00H, 00H, FFH, 00H FF 01 00 01 00 00 00 00 FF 00
Drive 2 as loaded, replaced at reset when option AL is above 2.
42AF-42B8
DEFB FFH, 01H, 00H, 01H, 00H, 00H, 00H, 00H, FFH, 00H FF 01 00 01 00 00 00 00 FF 00
Drive 3 as loaded, replaced at reset when option AL is 4.
42B9-42BA
DEFW 4291H 91 42
The address of the current drive's PDRIVE entry (4291H + 10 x drive), stored by 4754H at every drive change. 4898H stores the directory's first lump through it; 4C1EH reads that byte to find the directory.

42BBH - Option Values and Work Bytes

Bytes filled from the system data sector at reset, and bytes used by the overlays.

42BB-42BE
DEFB 00H, 00H, 00H, 00H 00 00 00 00
Two words used by SYS1/SYS (42BBH and 42BDH hold saved Stack Pointer values there). SYS0/SYS does not use them.
42BF
DEFB 04H 04
SYSTEM option AL, the number of drives (1-4), stored at reset (4D72H). The reset uses it to decide how many PDRIVE entries to copy (4DE7H); the same value is put in the CP operand at 4727H, which refuses higher drive numbers.
42C0
DEFB 01H 01
SYSTEM option AN, the default drive of the DIR command (manual, SYSTEM command), stored at reset from byte A2H of the system data sector (4D86H). SYS0/SYS does not use the value itself.
42C1
DEFB 00H 00
SYSTEM option AO, the first drive searched for free directory space when a new file is created without a drive number (manual, SYSTEM command), stored at reset from byte A3H of the system data sector (4D8CH). SYS0/SYS does not use the value itself.
42C2
DEFB 01H 01
SYSTEM option BJ, stored at reset from byte A9H of the system data sector (4D98H). The reset also puts it in the delay routine (4C94H) and uses it to scale the controller delay (478AH) and the diskette test time-out (4799H).
42C3
DEFB 00H 00
A byte loaded as 00H. SYS0/SYS does not use it.
42C4
DEFB 01H 01
Used by SYS5/SYS (DEBUG) at 4EA2H, 4EE4H, 4F16H and 4FD0H of its code. Loaded as 01H. SYS0/SYS does not use it.
42C5-42C6
DEFB 00H, 00H 00 00
42C5H is used by SYS9/SYS (4E02H of its code). SYS0/SYS does not use these bytes.
42C7-42C8
DEFB 0DH, 0DH 0D 0D
A word used by SYS1/SYS (stored at its 4E1CH, read at its 4E86H). Loaded as two carriage returns. SYS0/SYS does not use it.
42C9-42D1
DEFS 9
Not in the file. 42C9H-42CAH: the top of memory found at reset (4D10H). 42CBH: A5H while the DOS is active, stored at the end of the reset (4F06H) and tested by the next reset (4D16H, 4EC1H). 42CCH-42D1H: the copy of the clock bytes 4217H-421CH, made every second by 4567H and copied back by a reset while the DOS was active (4D25H).

42D2H - ROUTE Entries

Four bytes used by SYS14/SYS, then six seven-byte ROUTE entries. An entry holds the address of the control block that is routed (+0), its original type byte (+2), the link to the next entry (+3, 0000H = the last) and the control block the I/O goes to (+5). 4A80H walks the list from the head stored in its operand at 4A81H.

42D2-42D5
DEFB 00H, 00H, 00H, 00H 00 00 00 00
Four bytes of 00H. SYS14/SYS (the ROUTE command) refers to 42D3H; SYS0/SYS does not use them.
42D6-42DC
DEFB 00H, 00H, 00H, FFH, FFH, A1H, 4CH 00 00 00 FF FF A1 4C
ROUTE entry 1. When option BA is Y the reset fills it in (4E91H-4EA7H): the display's control block 401DH at +0, its type byte at +2, link 0000H, and the target at +5, 4CA1H, the NL device, which throws the output away.
42DD-42E3
DEFB 00H, 00H, 00H, FFH, FFH, 00H, 00H 00 00 00 FF FF 00 00
ROUTE entry 2, as loaded: no control block, link FFFFH.
42E4-42EA
DEFB 00H, 00H, 00H, FFH, FFH, 00H, 00H 00 00 00 FF FF 00 00
ROUTE entry 3, as loaded: no control block, link FFFFH.
42EB-42F1
DEFB 00H, 00H, 00H, FFH, FFH, 00H, 00H 00 00 00 FF FF 00 00
ROUTE entry 4, as loaded: no control block, link FFFFH.
42F2-42F8
DEFB 00H, 00H, 00H, FFH, FFH, 00H, 00H 00 00 00 FF FF 00 00
ROUTE entry 5, as loaded: no control block, link FFFFH.
42F9-42FF
DEFB 00H, 00H, 00H, FFH, FFH, 00H, 00H 00 00 00 FF FF 00 00
ROUTE entry 6, as loaded: no control block, link FFFFH.
4300-43FF
DEFS 256
Not in the file. The sector buffer for the directory and the system files: the ROM's boot loader leaves the boot sector here, the reset reads the system data sector into it (4D4AH), and every directory sector read by 48A2H and every system file sector read through the FCB at 44C0H lands here.

4400H - DOS Entry Vectors

The fixed entry points of chapter 3 of the manual. Most load an RST 28H function code into Register A and execute RST 28H. The dispatcher at 4B67H drops the return address of the RST, so the bytes that follow an RST 28H here are never executed and serve as data.

4400
LD A,23H 3E 23
DOS READY (manual 3.4, the same as 402DH). Load Register A with function code 23H: SYS1/SYS (slot 03H), function 1.
4402
RST 28H EF
RST 28H: the dispatcher at 4B67H loads SYS1/SYS and runs function 1 (Register A = 23H). It does not return.
4403-4404
DEFW 0000H 00 00
The two-byte parameter of the named routines run by a command that starts with an asterisk (manual 3.31) and the entry address of the last program loaded (manual 3.17): SYS2/SYS stores the transfer address of every load here. 0000H in the file.
4405
LD A,63H 3E 63
Enter DOS and execute the command at Register Pair HL (manual 3.5). Load Register A with function code 63H: SYS1/SYS, function 3. The reset jumps here with HL = the AUTO command (4F28H).
4407
RST 28H EF
RST 28H: the dispatcher loads SYS1/SYS and runs function 3 with Register A = 63H and Register Pair HL = the command. It does not return.
4408
RET Z C8
RETurn if the Z FLAG is set. Never reached through 4405H (the RST does not return); the byte is only used as a return address by the overlays that push 4408H.
4409
PUSH AF F5
DOS error exit (manual 3.6): Register A holds the error code; bit 7 set means display the message and return. Save Register Pair AF (the error code and the flags) onto the stack for SYS4/SYS.
440A
LD A,26H 3E 26
Load Register A with function code 26H: SYS4/SYS (slot 06H), function 1, the error message display. The error code saved at 4409H is on the stack.
440C
RST 28H EF
RST 28H: the dispatcher loads SYS4/SYS and runs function 1 (Register A = 26H), which takes the error code from the stack.
440D
Enter DEBUG (manual 3.7): JUMP to 45D4H, which saves Register Pair AF and calls SYS5/SYS.
4410
RET C9
RETurn. 4410H is not a vector of this DOS (447BH enqueues the timer routines); a CALL 4410H returns at once.
4411-4412
DEFW FFFFH FF FF
HIMEM, the highest address the DOS lets programs use (manual, option AP). FFFFH in the file; the reset stores the top of memory here (4D13H) and then option AP if it is set (4DD0H).
4413
LD A,85H 3E 85
Dequeue a timer routine (manual 3.9; Register Pair DE = the routine). Load Register A with function code 85H: SYS3/SYS (slot 05H), function 4.
4415
RST 28H EF
RST 28H: the dispatcher loads SYS3/SYS and runs function 4 (Register A = 85H), which takes the routine at Register Pair DE out of the timer routine chain (manual 3.9).
4416
Keep the drives rotating (manual 3.10): JUMP to 4711H, which reselects the current drive if the drives are turning.
4419
LD A,C3H 3E C3
DOS-CALL (manual 3.11): execute the command at Register Pair HL and return. Load Register A with function code C3H: SYS1/SYS, function 6.
441B
RST 28H EF
RST 28H: the dispatcher loads SYS1/SYS and runs function 6 (Register A = C3H).
441C
LD A,83H 3E 83
Extract a filespec (manual 3.12) from the text at Register Pair HL into the area at Register Pair DE. Load Register A with function code 83H: SYS1/SYS, function 4.
441E
RST 28H EF
RST 28H: the dispatcher loads SYS1/SYS and runs function 4 (Register A = 83H).
441F
DEFB 00H 00
The tick counter: INCremented by the clock routine for every 25 ms tick (457AH), 40 times a second. 00H in the file. It sits after the RST 28H of 441EH, which never returns here.
4420
LD A,44H 3E 44
Open a new or existing file (manual 3.13; Register Pair DE = the FCB, Register Pair HL = the buffer, Register B = the record length). Load Register A with function code 44H: SYS2/SYS (slot 04H), function 2.
4422
RST 28H EF
RST 28H: the dispatcher loads SYS2/SYS and runs function 2 (Register A = 44H).
4423
DEFB 00H 00
A byte after the RST 28H of 4422H, never executed. 00H in the file; SYS0/SYS does not use it.
4424
LD A,24H 3E 24
Open an existing file (manual 3.14; Register Pair DE = the FCB, Register Pair HL = the buffer, Register B = the record length). Load Register A with function code 24H: SYS2/SYS, function 1.
4426
RST 28H EF
RST 28H: the dispatcher loads SYS2/SYS and runs function 1 (Register A = 24H).
4427
DEFB 82H 82
A byte after the RST 28H of 4426H, never executed. 82H in the file; SYS0/SYS does not use it.
4428
LD A,25H 3E 25
Close a file (manual 3.15; Register Pair DE = the FCB). Load Register A with function code 25H: SYS3/SYS (slot 05H), function 1.
442A
RST 28H EF
RST 28H: the dispatcher loads SYS3/SYS and runs function 1 (Register A = 25H).
442B
DEFB 03H 03
A byte after the RST 28H of 442AH, never executed. 03H in the file; SYS0/SYS does not use it.
442C
LD A,45H 3E 45
Kill the file of an FCB (manual 3.16; Register Pair DE = the FCB). Load Register A with function code 45H: SYS3/SYS, function 2.
442E
RST 28H EF
RST 28H: the dispatcher loads SYS3/SYS and runs function 2 (Register A = 45H).
442F
DEFB 00H 00
A byte after the RST 28H of 442EH, never executed. 00H in the file; SYS0/SYS does not use it.
4430
LD A,A4H 3E A4
Load a program file (manual 3.17; Register Pair DE = the FCB). Load Register A with function code A4H: SYS2/SYS, function 5. The entry address comes back in Register Pair HL and at 4403H.
4432
RST 28H EF
RST 28H: the dispatcher loads SYS2/SYS and runs function 5 (Register A = A4H).
4433
LD A,C4H 3E C4
Load and run a program file (manual 3.18; Register Pair DE = the FCB). Load Register A with function code C4H: SYS2/SYS, function 6.
4435
RST 28H EF
RST 28H: the dispatcher loads SYS2/SYS and runs function 6 (Register A = C4H). It does not return.
4436
READ (manual 3.19): JUMP to 49A1H. Register Pair DE = the FCB; in record mode Register Pair HL = the caller's record buffer.
4439
WRITE (manual 3.20): JUMP to 49DBH. Register Pair DE = the FCB; in record mode Register Pair HL = the caller's record.
443C
WRITE with verify (manual 3.21): JUMP to 49D7H. Register Pair DE = the FCB.
443F
Position to the start of the file (manual 3.22): JUMP to 4AF1H. Register Pair DE = the FCB.
4442
Position to the record in Register Pair BC (manual 3.23): JUMP to 4B18H. Register Pair DE = the FCB.
4445
Position back one record (manual 3.24): JUMP to 4B07H. Register Pair DE = the FCB.
4448
Position to the end of the file (manual 3.25): JUMP to 4AF9H. Register Pair DE = the FCB.
444B
Allocate file space up to NEXT (manual 3.26): JUMP to 47AEH. Register Pair DE = the FCB.
444E
Position to the byte address in Registers H, L and C (manual 3.27): JUMP to 4AECH. Register Pair DE = the FCB.
4451
LD A,C5H 3E C5
Write the FCB's EOF to the directory (manual 3.28; Register Pair DE = the FCB). Load Register A with function code C5H: SYS3/SYS, function 6.
4453
RST 28H EF
RST 28H: the dispatcher loads SYS3/SYS and runs function 6 (Register A = C5H).
4454-4456
DEFB 00H, 00H, 00H 00 00 00
Three bytes after the RST 28H of 4453H, never executed. 00H in the file; SYS0/SYS does not use them.
4457
LD A,2AH 3E 2A
Load Register A with error code 2AH (ILLEGAL DOS FUNCTION).
4459
JUMP to 4409H, the error exit, with Register A = 2AH.
445B
Select and power up the drive in Register A (manual 3.29): JUMP to 4723H.
445E
Test for a mounted diskette in the drive in Register A (manual 3.30): JUMP to 4791H.
4461
LD A,2BH 3E 2B
Enqueue a named routine, one run by a command that starts with an asterisk (manual 3.31; Register Pair HL = the routine). Load Register A with function code 2BH: SYS9/SYS (slot 0BH), function 1.
4463
RST 28H EF
RST 28H: the dispatcher loads SYS9/SYS and runs function 1 (Register A = 2BH).
4464
LD A,4BH 3E 4B
Dequeue a named routine (manual 3.32; Register Pair HL = the routine). Load Register A with function code 4BH: SYS9/SYS, function 2.
4466
RST 28H EF
RST 28H: the dispatcher loads SYS9/SYS and runs function 2 (Register A = 4BH).
4467
Send the message at Register Pair HL to the display (manual 3.33): JUMP to 4B4BH.
446A
Send the message at Register Pair HL to the printer (manual 3.34): JUMP to 4B61H.
446D
Convert the time to HH:MM:SS at Register Pair HL (manual 3.35): JUMP to the ROM's time routine at 3036H, which converts the clock bytes 4217H-4219H.
4470
Convert the date to MM/DD/YY at Register Pair HL (manual 3.36): JUMP to the ROM's date routine at 3033H, which converts 421AH-421CH.
4473
LD A,A3H 3E A3
Insert a default extension (manual 3.37; Register Pair DE = the filespec, Register Pair HL = the three characters). Load Register A with function code A3H: SYS1/SYS, function 5.
4475
RST 28H EF
RST 28H: the dispatcher loads SYS1/SYS and runs function 5 (Register A = A3H).
4476-4477
DEFB 00H, 00H 00 00
Two bytes after the RST 28H of 4475H, never executed. 00H in the file; SYS0/SYS does not use them.
4478
The BREAK key hook. The dispatcher (4B6BH) jumps here for every function code below 20H, which the ROM's keyboard routine sends with Register A = 01H when BREAK is pressed (ROM 3100H-3103H). As loaded JUMP to 4546H, which clears Register A so that BREAK gives the null key 00H. Storing C9H here (RET) makes BREAK an ordinary key with code 01H, storing C3H restores the jump (manual, BREAK command and option AG).
447B
LD A,65H 3E 65
Enqueue a timer routine (manual 3.8; Register Pair DE = the routine). Load Register A with function code 65H: SYS3/SYS, function 3.
447D
RST 28H EF
RST 28H: the dispatcher loads SYS3/SYS and runs function 3 (Register A = 65H), which links the routine at Register Pair DE into the timer routine chain walked by 4583H (manual 3.8).
447E
XOR A AF
Set Register A to 00H and set the Z FLAG. Nothing in SYS0/SYS jumps here.
447F
RET C9
RETurn with Register A = 00H and the Z FLAG set.

4480H - The DOS FCB

A 32-byte FCB used by SYS0/SYS and the overlays for their own files. As loaded it is open, its buffer is 4300H, and FCB+0 bit 1 makes NEXT a sector number of the whole diskette: NEXT = 000200H, sector 2, the system data sector, which the reset reads with one call to 4436H (4D4AH).

4480
DEFB 82H 82
FCB+0: bit 7 = open, bit 1 = NEXT and EOF are sector addresses on the diskette, not in a file.
4481
DEFB 20H 20
FCB+1: bit 5 = the buffer does not hold the sector at NEXT; access level 0 (FULL); bit 7 clear, sector mode.
4482
DEFB 00H 00
FCB+2: 00H, no flags (the file may be extended and deallocated).
4483-4484
DEFW 4300H 00 43
FCB+3 and +4: the address of the sector buffer, 4300H.
4485
DEFB 00H 00
FCB+5: NEXT's low byte, the byte within the sector.
4486
DEFB 00H 00
FCB+6: the drive number, drive 0 (the system diskette).
4487
DEFB FFH FF
FCB+7: the DEC of the directory entry; FFH = none.
4488
DEFB 00H 00
FCB+8: EOF's low byte, 00H (the byte within the last sector).
4489
DEFB 00H 00
FCB+9: the record length; 00H stands for 256 bytes.
448A-448B
DEFW 0002H 02 00
FCB+10 and +11: NEXT's middle and high bytes, 0002H: NEXT is sector 2.
448C-448D
DEFW FFFFH FF FF
FCB+12 and +13: EOF's middle and high bytes, FFFFH, so no read reaches the end of the file.
448E-449F
DEFB FFH x 18 FF x 18
FCB+14 to +31: the extent area, all FFH (no extents; FCB+0 bit 1 makes them unnecessary).
44A0-44BF
DEFS 32
Not in the file. A 32-byte area that SYS9/SYS uses (it reads 44A0H at its 4D72H and points Register Pair DE to it at its 4DDDH). SYS0/SYS does not use it.

44C0H - The System File FCB

The FCB through which the dispatcher (4BA8H) loads the overlays and the other system files. Its buffer is 4300H, it reads drive 0, its EOF is 64 sectors so that no read of a system file stops early, and only its first extent is filled in (44CEH, by 4BA4H): a system file has to be one extent.

44C0
DEFB 80H 80
FCB+0: 80H, bit 7 = the FCB is open; FCB+0 bit 1 is clear (NEXT counts in the file).
44C1
DEFB 28H 28
FCB+1: bit 5 = the buffer does not hold the sector at NEXT, bit 3 = the second set of extents is valid; access level 0 (FULL).
44C2
DEFB 00H 00
FCB+2: 00H, no flags (the file may be extended and deallocated).
44C3-44C4
DEFW 4300H 00 43
FCB+3 and +4: the address of the sector buffer, 4300H. The loader at 4BCDH starts its byte pointer at 43FFH, so it relies on this buffer.
44C5
DEFB 00H 00
FCB+5: NEXT's low byte, the byte within the sector, 00H.
44C6
DEFB 00H 00
FCB+6: the drive, drive 0, where the system files are.
44C7
DEFB FFH FF
FCB+7: the DEC of the directory entry; FFH = none.
44C8
DEFB 00H 00
FCB+8: EOF's low byte, 00H (the byte within the last sector).
44C9
DEFB 00H 00
FCB+9: the record length; 00H stands for 256 bytes.
44CA-44CB
DEFW 0000H 00 00
FCB+10 and +11: NEXT's middle and high bytes, cleared by the dispatcher (4B8AH stores 00H at 44CAH) before every load.
44CC-44CD
DEFW 0040H 40 00
FCB+12 and +13: EOF's middle and high bytes, 0040H: with FCB+8 = 00H the EOF is 64 sectors (16,384 bytes), more than any system file.
44CE-44CF
DEFW 0000H 00 00
FCB+14 and +15: the first extent; the dispatcher stores the file's first extent here (4BA4H).
44D0-44D1
DEFW FFFFH FF FF
FCB+16 and +17: FFFFH, the end of the extents.

44D2H - Convert to Hexadecimal Text

Two entry points used by the overlays. 44D2H stores Register Pair DE as four hexadecimal characters at Register Pair HL; 44D7H stores Register A as two. Register Pair HL is left pointing after the last character.

44D2
LD A,D 7A
Load Register A with Register D, the high byte of the value in Register Pair DE.
44D3
GOSUB to 44D7H to store Register A (the high byte) as two hexadecimal characters at Register Pair HL.
44D6
LD A,E 7B
Load Register A with Register E, the low byte, and fall into 44D7H for its two characters.
44D7
PUSH AF F5
Entry with the byte in Register A. Save Register Pair AF (the byte) onto the stack for its low digit.
44D8
RRCA 0F
Rotate Register A right: the first of four rotates that move the high digit (bits 4-7) into bits 0-3.
44D9
RRCA 0F
Rotate Register A right, the second of the four rotates of the high digit.
44DA
RRCA 0F
Rotate Register A right, the third of the four rotates of the high digit.
44DB
RRCA 0F
Rotate Register A right (fourth): bits 0-3 of Register A now hold the high digit.
44DC
GOSUB to 44E0H to store bits 0-3 of Register A (the high digit) as a character at Register Pair HL.
44DF
POP AF F1
Restore Register Pair AF from the stack: Register A = the byte again, for its low digit, and fall into 44E0H.
44E0
AND 0FH E6 0F
Keep bits 0-3 of Register A (AND 0FH): one hexadecimal digit, 0-15.
44E2
ADD A,90H C6 90
ADD 90H to Register A: the first step of the DAA trick that turns 0-15 into the ASCII codes of 0-9 and A-F.
44E4
DAA 27
Decimal Adjust Register A: 0-9 give 90H-99H with the CARRY FLAG clear, 10-15 give 00H-05H with the CARRY FLAG set.
44E5
ADC A,40H CE 40
ADD 40H and the CARRY FLAG to Register A: D0H-D9H for 0-9, 41H-46H for 10-15.
44E7
DAA 27
Decimal Adjust Register A again: 0-9 become 30H-39H (ASCII 0-9); 41H-46H (ASCII A-F) stay.
44E8
LD (HL),A 77
Store Register A (the character) at Register Pair HL.
44E9
INC HL 23
INCrement Register Pair HL to the next character position.
44EA
RET C9
RETurn to the caller with Register Pair HL past the character.

44EBH - Keyboard Driver

Put into the keyboard's control block (4016H) at reset when SYSTEM option AJ is Y and the up-arrow key is not held (4E31H-4E3FH). It gives chain file keys first, runs the ROM's own keyboard routine (3024H), adds the clock driven repeat key of option AU, checks the key combinations (459DH) and filters the CLEAR key unless option AQ is Y. It returns the key in Register A (00H = none).

44EB
LD A,(4289H) 3A 89 42
Fetch the system flags from 4289H into Register A: bit 5 = a chain file is supplying keys, bits 3 and 6 = states in which the chain file must not be read.
44EE
XOR 20H EE 20
Flip bit 5 of Register A (XOR 20H), so that the test below gives zero when bit 5 is set.
44F0
AND 68H E6 68
Keep bits 3, 5 and 6 of Register A (AND 68H). The Z FLAG is set only when bit 5 of 4289H is set and bits 3 and 6 are clear: the key is to come from the chain file.
44F2
LD A,CBH 3E CB
Load Register A with function code CBH (SYS9/SYS, function 6, the chain file key) without changing the flags.
44F4
If the Z FLAG is set, GOSUB to 4982H, an RST 28H that runs SYS9/SYS function 6 (Register A = CBH) and returns here: it gives the next key of the chain file in Register A.
44F7
RET Z C8
If the Z FLAG is set (the key came from the chain file, Register A), RETurn with it.
44F8
LD HL,459CH 21 9C 45
Point Register Pair HL to 459CH, the first byte of the key combination check that the timer routine falls into.
44FB
LD (HL),C9H 36 C9
Store C9H (RET) at Register Pair HL (459CH): while the driver runs, the timer interrupt does not check the key combinations; the driver checks them itself at 453DH.
44FD
PUSH HL E5
Save Register Pair HL (459CH) onto the stack, to put the NOP back at 4540H.
44FE
LD HL,(41FDH) 2A FD 41
Fetch the word at 41FDH into Register Pair HL: Register L = the ROM's last-row byte (the low byte of the row image 4036H-403CH of the last key found), Register H = the ROM's last-column bit mask.
4501
LD D,40H 16 40
Load Register D with 40H, the high byte of the row images 4036H-403CH.
4503
LD E,L 5D
Copy Register L (the low byte of the last key's row image) into Register E: Register Pair DE = the address of that row image.
4504
LD A,(DE) 1A
Fetch the row image at Register Pair DE into Register A: the state of that keyboard row when the ROM last scanned it.
4505
AND H A4
AND Register A with Register H (the last key's bit mask): not zero if the last key is still held down.
4506
LD B,A 47
Copy Register A (the held key's bit, or 00H) into Register B.
4507
LD A,00H 3E 00
Load Register A with option AV, the delay before the first repeat. Self-Modifying Code
The operand at 4508H is stored by the reset (4E25H) from byte A7H of the system data sector. The load does not change the flags of the AND.
4509
If the NZ FLAG is set (the last key is still held), JUMP to 450EH and leave the repeat delay as it is.
450B
LD (452AH),A 32 2A 45
The key was released: store Register A (option AV) at 452AH, the operand of the LD at 4529H, so that the next key starts with the full first delay.
450E
XOR A AF
Set Register A to 00H, the value for Registers H and L below.
450F
LD H,A 67
Copy Register A (00H) into Register H, the high byte of Register Pair HL.
4510
LD L,A 6F
Copy Register A (00H) into Register L: Register Pair HL = 0000H, meaning no repeat is being forced.
4511
OR 00H F6 00
OR Register A with the repeat countdown. Self-Modifying Code
The operand at 4512H is the countdown of 25 ms ticks to the next repeat: stored at 451DH and 4535H, counted down by the clock routine (457BH). The Z FLAG is set when it has reached 00H.
4513
JUMP to 4520H to read the keyboard. Self-Modifying Code
With option AU (repeat key) the reset stores 20H at 4513H (4E1CH), turning this into JR NZ,4520H: when the countdown at 4512H has reached 0 the code below forces a repeat.
4515
LD (4201H),HL 22 01 42
Store Register Pair HL (0000H) at 4201H, the ROM's own repeat counter, so that the ROM does not produce a repeat of its own.
4518
LD (DE),A 12
Store Register A (00H) at Register Pair DE, the row image of the held key: the ROM's next scan sees the key as newly pressed and delivers it again, which is the repeat.
4519
LD L,E 6B
Copy Register E (the row image's low byte) into Register L.
451A
LD H,B 60
Copy Register B (the held key's bit) into Register H: Register Pair HL = the identity of the key being repeated, in the form of the word at 41FDH.
451B
LD A,FFH 3E FF
Load Register A with FFH, a countdown too long to run out before the repeat arrives.
451D
LD (4512H),A 32 12 45
Store Register A (FFH) at 4512H, the repeat countdown, so that no second repeat is forced before this one has been delivered.
4520
PUSH HL E5
Save Register Pair HL (0000H, or the identity of the key being repeated) onto the stack.
4521
GOSUB to the ROM's keyboard routine through its vector at 3024H (338EH): it scans the keyboard rows and returns the key in Register A (00H = none), storing the key's row and bit at 41FDH-41FEH.
4524
POP BC C1
Restore Register Pair BC from the stack: the identity of the key being repeated (0000H if none).
4525
OR A B7
OR Register A (the key) with itself. The Z FLAG is set if no key was pressed.
4526
LD D,A 57
Copy Register A (the key) into Register D, where 459DH and the code below expect it.
4527
If the Z FLAG is set (no key), JUMP to 453DH to check the key combinations.
4529
LD A,FFH 3E FF
Load Register A with the delay for a key the driver has repeated. Self-Modifying Code
The operand at 452AH is stored by 450BH (option AV, after a release) and by 453AH (03H, after a key was delivered).
452B
LD HL,(41FDH) 2A FD 41
Fetch the word at 41FDH into Register Pair HL: the row and bit of the key the ROM has just found.
452E
OR A B7
OR Register A with itself to clear the CARRY FLAG for the SBC; Register A is unchanged.
452F
SBC HL,BC ED 42
SUBtract Register Pair BC (the key being repeated) from Register Pair HL (the key just found). The Z FLAG is set if it is the same key.
4531
If the Z FLAG is set (the repeat came through), JUMP to 4535H with Register A = the delay from 452AH.
4533
LD A,00H 3E 00
A new key: load Register A with option AV, the delay before its first repeat. Self-Modifying Code
The operand at 4534H is stored by the reset (4E22H) from byte A7H of the system data sector.
4535
LD (4512H),A 32 12 45
Store Register A (the delay in 25 ms ticks) at 4512H, the repeat countdown.
4538
LD A,03H 3E 03
Load Register A with 03H: after a key has been delivered, the repeats come every 3 ticks (75 ms).
453A
LD (452AH),A 32 2A 45
Store Register A (03H) at 452AH, the operand of the LD at 4529H.
453D
GOSUB to 459DH to check for the key combinations D-F-G, 1-2-3 and J-K-L, with the key in Register D. It returns with Register A = the key.
4540
POP HL E1
Restore Register Pair HL from the stack: 459CH (pushed at 44FDH).
4541
LD (HL),00H 36 00
Store 00H (NOP) at Register Pair HL (459CH): the timer interrupt checks the key combinations again.
4543
CP 1FH FE 1F
Compare Register A (the key) against 1FH, the CLEAR key. Self-Modifying Code
With option AQ (CLEAR enabled) the reset stores 00H in the operand at 4544H (4E6BH), so CLEAR passes and only a missing key (00H) reaches the XOR below.
4545
RET NZ C0
If the NZ FLAG is set (any other key), RETurn with the key in Register A.
4546
XOR A AF
Set Register A to 00H (no key) and set the Z FLAG. The CLEAR key ends here when option AQ is N; the BREAK hook at 4478H also jumps here, turning BREAK into the null key.
4547
RET C9
RETurn to the caller with Register A = 00H, the null key.

4548H - Convert Lower Case to Upper Case

Converts a lower case letter in Register A to upper case; any other value is returned unchanged.

4548
CP 61H FE 61
Compare Register A against 61H (a). If Register A is below it, the CARRY FLAG is set.
454A
RET C D8
If the CARRY FLAG is set (below a), RETurn with Register A unchanged.
454B
CP 7BH FE 7B
Compare Register A against 7BH, the code after z. The NO CARRY FLAG is set for 7BH and above.
454D
RET NC D0
If the NO CARRY FLAG is set (above z), RETurn with Register A unchanged.
454E
SUB 20H D6 20
SUBtract 20H from Register A: the lower case letter becomes upper case.
4550
RET C9
RETurn with the upper case letter in Register A.

4551H - Clock Routine

The reset puts 4551H in the real-time clock vector at 4046H (4D27H-4D30H), so the ROM's interrupt handler calls it at every clock interrupt (30 a second, or 25 with option BB). It first runs the clock routine the vector held before, keeps a copy of the clock for a later reset, then turns the interrupt into one or two 25 ms ticks: 40 ticks a second in both cases. Each tick counts 441FH, counts down the repeat delay and runs the timer routine chain (manual 3.8); then it falls into the key combination check.

4551
GOSUB to the routine the clock vector held before the reset hooked it (the ROM's clock and cursor routine; the operand is 0000H in the file). Self-Modifying Code
The operand at 4552H is stored by the reset (4D2AH) from the clock vector's operand at 4047H.
4554
LD HL,4216H 21 16 42
Point Register Pair HL to 4216H, the ROM's count of clock interrupts within the second, which the ROM reloads with 1EH (30) when a second has passed.
4557
LD A,(HL) 7E
Fetch the count at Register Pair HL (4216H) into Register A.
4558
CP 1EH FE 1E
Compare Register A against 1EH. The Z FLAG is set right after the ROM has reloaded it, once a second.
455A
If the NZ FLAG is set (not the start of a second), JUMP to 4569H.
455C
LD (HL),1EH 36 1E
Store the number of interrupts per second at Register Pair HL (4216H). Self-Modifying Code
The operand at 455DH is 1EH (30); the reset stores 19H (25) there for option BB (4EB2H), so that the ROM counts seconds correctly from 25 interrupts.
455E
LD HL,4217H 21 17 42
Point Register Pair HL to 4217H, the ROM's clock: seconds, minutes, hours, year, day, month.
4561
LD DE,42CCH 11 CC 42
Point Register Pair DE to 42CCH, the copy kept for a reset.
4564
LD BC,0006H 01 06 00
Load Register Pair BC with 0006H, the six clock and date bytes.
4567
LDIR ED B0
Copy the 6 bytes from Register Pair HL (4217H-421CH) to Register Pair DE (42CCH-42D1H), Register Pair BC counting down to 0. A reset while the DOS is active copies them back (4D25H).
4569
XOR A AF
Set Register A to 00H and clear the CARRY FLAG.
456A
LD HL,0000H 21 00 00
Load Register Pair HL with the fraction of a tick carried over (0000H in the file). Self-Modifying Code
The operand at 456BH is stored at 4571H after every interrupt.
456D
LD DE,5555H 11 55 55
Load Register Pair DE with the fraction of a tick added per interrupt. Self-Modifying Code
The operand at 456EH is 5555H (one third: 30 interrupts make 40 ticks); the reset stores 9999H (0.6: 25 interrupts make 40 ticks) for option BB (4EB8H).
4570
ADD HL,DE 19
ADD Register Pair DE (the fraction per interrupt) to Register Pair HL (the fraction carried). The CARRY FLAG is set when a whole extra tick is due.
4571
LD (456BH),HL 22 6B 45
Store Register Pair HL (the new fraction) at 456BH, the operand of 456AH.
4574
ADC A,01H CE 01
ADD 01H and the CARRY FLAG to Register A (00H): Register A = 1 or 2, the number of 25 ms ticks in this interrupt.
4576
LD B,A 47
Copy Register A (the tick count) into Register B for the DJNZ at 459AH.
4577
LD HL,441FH 21 1F 44
Loop Start
Once per tick. Point Register Pair HL to 441FH, the tick counter.
457A
INC (HL) 34
INCrement the tick counter at Register Pair HL (441FH).
457B
LD HL,4512H 21 12 45
Point Register Pair HL to 4512H, the keyboard driver's repeat countdown (the operand of its OR at 4511H).
457E
LD A,(HL) 7E
Fetch the countdown at Register Pair HL into Register A.
457F
OR A B7
OR Register A with itself. The Z FLAG is set if it is already 0.
4580
If the Z FLAG is set (the countdown is at 0), JUMP to 4583H.
4582
DEC (HL) 35
DECrement the countdown at Register Pair HL (4512H) by 1.
4583
LD HL,0000H 21 00 00
Load Register Pair HL with the first timer routine of the chain. Self-Modifying Code
The operand at 4584H is the head of the chain, stored by SYS3/SYS when a routine is enqueued through 447BH or dequeued through 4413H; 0000H = empty.
4586
LD A,H 7C
Loop Start
Copy Register H into Register A: the high byte of the entry address in Register Pair HL.
4587
OR L B5
OR Register L into Register A. The Z FLAG is set when Register Pair HL is 0000H, the end of the chain.
4588
If the Z FLAG is set (end of the chain), JUMP to 459AH for the next tick.
458A
LD E,(HL) 5E
Fetch the low byte of the entry's link (bytes 1-2, the next entry) at Register Pair HL into Register E.
458B
INC HL 23
INCrement Register Pair HL to the link's high byte.
458C
LD D,(HL) 56
Fetch the high byte of the link at Register Pair HL into Register D: Register Pair DE = the next entry.
458D
PUSH DE D5
Save Register Pair DE (the next entry) onto the stack.
458E
INC HL 23
INCrement Register Pair HL to the entry's third byte, the interval in ticks.
458F
PUSH BC C5
Save Register Pair BC (Register B = the ticks still to do in this interrupt) onto the stack.
4590
LD B,(HL) 46
Fetch the interval at Register Pair HL into Register B.
4591
INC HL 23
INCrement Register Pair HL to the entry's fourth byte, its countdown.
4592
DEC (HL) 35
DECrement the countdown at Register Pair HL. The Z FLAG is set when it reaches 0.
4593
If the Z FLAG is set (the routine is due), GOSUB to 45D8H, which reloads the countdown from Register B and runs the routine at its fifth byte.
4596
POP BC C1
Restore Register Pair BC from the stack: Register B = the ticks still to do.
4597
POP HL E1
Restore Register Pair HL from the stack: the next entry (pushed at 458DH).
4598
LOOP BACK to 4586H with Register Pair HL = the next entry of the chain.
459A
Loop End
DECrement Register B (the ticks in this interrupt) and LOOP BACK to 4577H if it is not zero.

459CH - Check for D-F-G, 1-2-3 and J-K-L

Entered by falling out of the clock routine (from the interrupt) and by the keyboard driver at 453DH with the key in Register D. D-F-G enters MINI-DOS (option AF), 1-2-3 enters DEBUG (option AE), J-K-L sends the screen to the printer (option AD). Each test is a plain jump past the action until the reset turns it into a conditional one. Returns with Register A = Register D.

459C
NOP 00
No operation. Self-Modifying Code
The keyboard driver stores C9H (RET) here while it runs (44FBH) and 00H back afterwards (4541H); the dispatcher stores 00H (4B7AH). As a RET it makes the interrupt skip the check, and 45B8H uses it to tell where the check was entered from.
459D
LD HL,4289H 21 89 42
Point Register Pair HL to 4289H, the system flags.
45A0
LD A,(HL) 7E
Fetch the system flags at Register Pair HL (4289H) into Register A.
45A1
AND 6CH E6 6C
Keep bits 2, 3, 5 and 6 of Register A (AND 6CH): bit 2 RUN-ONLY (set at reset for option AB, 4E0EH), bit 3, bit 5 a chain file supplying keys (44EBH), bit 6 an overlay function running (4B75H).
45A3
If the NZ FLAG is set (any of them), JUMP to 45CFH to return without checking.
45A5
LD A,(3801H) 3A 01 38
Fetch the keyboard row at 3801H into Register A: bit 4 = D, bit 6 = F, bit 7 = G.
45A8
CP D0H FE D0
Compare Register A against D0H (D, F and G held, nothing else in the row). The Z FLAG is set on D-F-G.
45AA
JUMP to 45B1H past MINI-DOS. Self-Modifying Code
With option AF the reset stores 20H at 45AAH (4E63H), turning this into JR NZ,45B1H so that D-F-G falls through to 45ACH.
45AC
LD A,E3H 3E E3
D-F-G: load Register A with function code E3H: SYS1/SYS, function 7.
45AE
LD C,04H 0E 04
Load Register C with sub-function 04H, MINI-DOS.
45B0
RST 28H EF
RST 28H: the dispatcher loads SYS1/SYS and runs function 7 with Register A = E3H and Register C = 04H, MINI-DOS.
45B1
LD A,(3810H) 3A 10 38
Fetch the keyboard row at 3810H into Register A: bit 1 = 1, bit 2 = 2, bit 3 = 3.
45B4
CP 0EH FE 0E
Compare Register A against 0EH (1, 2 and 3 held, nothing else in the row). The Z FLAG is set on 1-2-3.
45B6
JUMP to 45CAH past DEBUG. Self-Modifying Code
With option AE the reset stores 20H at 45B6H (4E74H), turning this into JR NZ,45CAH so that 1-2-3 falls through to 45B8H.
45B8
LD A,(459CH) 3A 9C 45
1-2-3: fetch the byte at 459CH into Register A: C9H while the keyboard driver runs, 00H when the check was entered from the clock interrupt.
45BB
SUB C9H D6 C9
SUBtract C9H from Register A. The Z FLAG is set if the keyboard driver called the check.
45BD
If the Z FLAG is set (called from the keyboard driver), JUMP to 440DH to enter DEBUG as an ordinary call.
45C0
POP AF F1
Called from the interrupt: undo what the ROM's interrupt handler pushed, so that DEBUG sees the interrupted program. Restore Register Pair AF from the stack: this takes the ROM handler's return address 35F1H off the stack.
45C1
POP IY FD E1
Restore Index Register IY from the stack: the interrupted program's IY, pushed by the ROM handler (35D2H).
45C3
POP IX DD E1
Restore Index Register IX from the stack: the interrupted program's IX (35D0H).
45C5
POP HL E1
Restore Register Pair HL from the stack: the interrupted program's HL (35CFH).
45C6
POP DE D1
Restore Register Pair DE from the stack: the interrupted program's DE (35CEH).
45C7
POP BC C1
Restore Register Pair BC from the stack: the interrupted program's BC (35CDH). Its AF (35C2H) and its program counter stay on the stack.
45C8
JUMP to 45D5H to enter DEBUG with the interrupted program's AF on the stack, as 45D4H would.
45CA
LD A,(3802H) 3A 02 38
Fetch the keyboard row at 3802H into Register A: bit 2 = J, bit 3 = K, bit 4 = L.
45CD
CP 1CH FE 1C
Compare Register A against 1CH (J, K and L held, nothing else in the row). The Z FLAG is set on J-K-L.
45CF
LD A,D 7A
Load Register A with Register D, the key, without changing the flags.
45D0
RET C9
RETurn with Register A = the key. Self-Modifying Code
With option AD the reset stores C0H at 45D0H (4E5AH), turning this into RET NZ so that J-K-L falls through to 45D1H.
45D1
LD A,A5H 3E A5
J-K-L: load Register A with function code A5H: SYS3/SYS, function 5, which sends the screen to the printer.
45D3
RST 28H EF
RST 28H: the dispatcher loads SYS3/SYS and runs function 5 (Register A = A5H).

45D4H - Enter DEBUG

The RST 30H vector (400FH) and 440DH come here; 45C8H enters at 45D5H with Register Pair AF already on the stack.

45D4
PUSH AF F5
Save Register Pair AF onto the stack: DEBUG (SYS5/SYS) finds the caller's AF there.
45D5
LD A,27H 3E 27
Load Register A with function code 27H: SYS5/SYS (slot 07H), function 1, DEBUG.
45D7
RST 28H EF
RST 28H: the dispatcher loads SYS5/SYS and runs function 1 (Register A = 27H).

45D8H - Run a Timer Routine

Called from 4593H with Register Pair HL pointing to the fourth byte (the countdown) of a timer routine entry and Register B = its interval (the third byte).

45D8
LD (HL),B 70
Store Register B (the interval) at Register Pair HL, reloading the routine's countdown.
45D9
INC HL 23
INCrement Register Pair HL to the entry's fifth byte, where the routine's code starts.
45DA
JP (HL) E9
JUMP to the routine at Register Pair HL. Its RET returns to 4596H in the clock routine.

45DBH - Read or Write a Sector

The sector level disk I/O. Register Pair DE = the sector number on the diskette (counted from 0 across all tracks and sides), Register Pair HL = the 256-byte buffer, the drive = the current drive (427EH). 45DBH reads, 45DFH reads without keeping the data (a verify), 45E7H writes with the deleted data mark F8H (the mark of a directory sector), 45EBH writes normally. The sector is converted to a track, side and sector, the head is moved with a Seek, and the 256 bytes are moved with INI or OUTI while the controller's wait states pace the CPU; the end of the command arrives as an NMI that jumps to 46B6H. Errors are retried (option AM) and returned as an error code in Register A with the NZ FLAG; the Z FLAG means success.

45DB
LD A,88H 3E 88
Read entry. Load Register A with the 1793 Read Sector command 88H:
1793 FDC Command: 88H (10001000)Function Description
Bit 7Bit 6Bit 5Bit 4Bit 3Bit 2Bit 1Bit 0Summary of Bits
10001000Command=Read Sector (bits 7-5 = 100)
m = 0: one sector
S = 1: side to compare (used only when C = 1)
E = 0: no 15 ms delay
C = 0: side compare off
45DD
JUMP to 45EDH to do the read with Register A = 88H.
45DF
PUSH HL E5
Verify entry: read the sector without keeping its data. Save Register Pair HL (the caller's buffer) onto the stack.
45E0
LD H,01H 26 01
Load Register H with 01H: Register Pair HL now points into the ROM (0100H-01FFH), so the bytes read are stored nowhere and only the read's error status matters.
45E2
GOSUB to 45DBH to read the sector at Register Pair DE into the ROM area. Register A returns the error code, the Z FLAG = no error.
45E5
POP HL E1
Restore Register Pair HL (the caller's buffer) from the stack.
45E6
RET C9
RETurn with the read's result in Register A and the flags.
45E7
LD A,A9H 3E A9
Write entry with the deleted data mark (the directory's mark). Load Register A with the 1793 Write Sector command A9H:
1793 FDC Command: A9H (10101001)Function Description
Bit 7Bit 6Bit 5Bit 4Bit 3Bit 2Bit 1Bit 0Summary of Bits
10101001Command=Write Sector (bits 7-5 = 101)
m = 0: one sector
S = 1: side to compare (used only when C = 1)
E = 0: no 15 ms delay
C = 0: side compare off
a0 = 1: data address mark F8H (deleted)
45E9
JUMP to 45EDH to do the write with Register A = A9H.
45EB
LD A,A8H 3E A8
Normal write entry. Load Register A with the 1793 Write Sector command A8H:
1793 FDC Command: A8H (10101000)Function Description
Bit 7Bit 6Bit 5Bit 4Bit 3Bit 2Bit 1Bit 0Summary of Bits
10101000Command=Write Sector (bits 7-5 = 101)
m = 0: one sector
S = 1: side to compare (used only when C = 1)
E = 0: no 15 ms delay
C = 0: side compare off
a0 = 0: data address mark FBH (normal)
45ED
LD (4687H),A 32 87 46
Store Register A (the command) at 4687H, the operand of the LD A,nn at 4686H that issues it.
45F0
AND 20H E6 20
Keep bit 5 of Register A (AND 20H): bit 5 is set in the write commands A8H/A9H and clear in 88H. The Z FLAG is set for a read.
45F2
PUSH BC C5
Save Register Pair BC (the caller's) onto the stack; 46E8H restores it.
45F3
LD BC,A202H 01 02 A2
Load Register Pair BC with A202H for a read: Register B = A2H, the second byte of INI, and Register C = 02H, the initial byte count for the transfer loop.
45F6
If the Z FLAG is set (a read), JUMP to 45FDH with Register A = 00H, the read's error base.
45F8
LD BC,A397H 01 97 A3
A write: load Register Pair BC with A397H: Register B = A3H, the second byte of OUTI, and Register C = 97H, a longer count whose countdown at 469FH gives the controller time after the first byte.
45FB
LD A,08H 3E 08
Load Register A with 08H, the write's error base (write errors are the read errors plus 8).
45FD
LD (46E1H),A 32 E1 46
Store Register A (the error base, 00H or 08H) at 46E1H, the operand of the LD A,nn at 46E0H.
4600
LD A,C 79
Copy Register C (02H or 97H, the first count) into Register A.
4601
GOSUB to 46ABH, which stores Register A as the B operand of the LD BC at 468DH and Register B (A2H or A3H) into the two INI/OUTI instructions at 469DH and 46A5H.
4604
LD B,0AH 06 0A
Load Register B with the number of tries. Self-Modifying Code
The operand at 4605H is SYSTEM option AM, stored by the reset (4D92H) from byte A6H of the system data sector; 0AH in the file.
4606
GOSUB to 4720H to select the current drive (427EH). The NZ FLAG returns with Register A = 20H if the drive number is not allowed.
4609
If the NZ FLAG is set (bad drive, Register A = 20H), JUMP to 4623H to return the error.
460B
PUSH BC C5
Loop Start
Each try starts here. Save Register Pair BC (Register B = the tries left) onto the stack.
460C
PUSH DE D5
Save Register Pair DE (the sector number) onto the stack.
460D
PUSH HL E5
Save Register Pair HL (the buffer) onto the stack.
460E
LD A,(4284H) 3A 84 42
Fetch the sectors per track of the current drive from 4284H (PDRIVE byte 4) into Register A.
4611
EX DE,HL EB
Exchange Register Pairs DE and HL: Register Pair HL = the sector number to divide.
4612
GOSUB to 4C59H to divide Register Pair HL by Register A (sectors per track): Register Pair HL returns the track, Register A the sector within the track.
4615
LD D,L 55
Copy Register L (the track; the quotient fits in one byte) into Register D.
4616
LD E,A 5F
Copy Register A (the sector within the track) into Register E.
4617
LD HL,4283H 21 83 42
Point Register Pair HL to 4283H, the current drive's track count (PDRIVE byte 3).
461A
LD A,D 7A
Copy Register D (the track the sector is on) into Register A for the comparison with the track count.
461B
CP (HL) BE
Compare Register A (the track) against the track count at Register Pair HL (4283H). The CARRY FLAG is set if the track exists.
461C
If the CARRY FLAG is set (the track is below the track count), JUMP to 4627H to go on.
461E
LD A,14H 3E 14
Load Register A with error code 14H (TRACK # TOO HIGH).
4620
POP HL E1
Restore Register Pair HL (the buffer) from the stack.
4621
POP DE D1
Restore Register Pair DE (the sector number) from the stack.
4622
POP BC C1
Restore Register Pair BC from the stack (Register B = the tries).
4623
OR A B7
OR Register A (the error code, 14H or 20H) with itself to set the NZ FLAG.
4624
JUMP to 46E7H to re-enable interrupts, restore the caller's Register Pair BC and return the error in Register A.
4627
LD A,(4287H) 3A 87 42
Fetch the current drive's disk I/O flags from 4287H (PDRIVE byte 7) into Register A.
462A
LD B,A 47
Copy Register A (the disk I/O flags) into Register B for the bit tests below; the tries count is safe on the stack.
462B
BIT 0,B CB 40
Test bit 0 of Register B: set for a double density diskette.
462D
LD HL,427FH 21 7F 42
Point Register Pair HL to 427FH, the byte written to port F4H to select the drive.
4630
If the Z FLAG is set (single density), JUMP to 4634H.
4632
SET 7,(HL) CB FE
Set bit 7 of the select byte at Register Pair HL (427FH): double density (MFM).
4634
GOSUB to 4CA4H, which returns Register A = half the track count (4283H shifted right), the first track written with precompensation.
4637
NOP 00
No operation: a spare byte between the CALL and the compare; Register A (half the track count) is unchanged.
4638
CP D BA
Compare Register A (half the track count) against Register D (the track). The NO CARRY FLAG is set if the track is not beyond the middle.
4639
If the NO CARRY FLAG is set (an inner half track is not reached), JUMP to 463DH.
463B
SET 5,(HL) CB EE
Set bit 5 of the select byte at Register Pair HL (427FH): write precompensation for the inner tracks.
463D
BIT 1,B CB 48
Test bit 1 of Register B (the disk I/O flags): set when the diskette's tracks are numbered from 1.
463F
If the Z FLAG is set (tracks numbered from 0), JUMP to 4642H.
4641
INC D 14
INCrement Register D (the track): the number written in the sector headers.
4642
PUSH DE D5
Save Register Pair DE (Register D = the track number in the headers, Register E = the sector within the track) onto the stack; 4669H takes it back.
4643
BIT 2,B CB 50
Test bit 2 of Register B (the disk I/O flags): set for double stepping, a 40-track diskette in an 80-track drive.
4645
If the Z FLAG is set (no double stepping), JUMP to 4649H.
4647
RLC D CB 02
Rotate Register D (the track) left: the head is moved twice as far, to the physical track.
4649
BIT 6,B CB 70
Test bit 6 of Register B (the disk I/O flags): set for a two-sided diskette.
464B
If the Z FLAG is set (one side), JUMP to 4656H.
464D
RRC C CB 09
Rotate Register C right. Register C still holds the sectors per track from the divide (4C59H keeps it there), so it becomes the sectors on one side.
464F
LD A,E 7B
Copy Register E (the sector within the track) into Register A.
4650
SUB C 91
SUBtract Register C (sectors per side) from Register A. The CARRY FLAG is set if the sector is on side 0.
4651
If the CARRY FLAG is set (side 0), JUMP to 4656H.
4653
LD E,A 5F
Copy Register A (the sector within side 1) into Register E.
4654
SET 4,(HL) CB E6
Set bit 4 of the select byte at Register Pair HL (427FH): side 1.
4656
GOSUB to 4715H to write the select byte (427FH) with its new density, precompensation and side bits to port F4H.
4659
BIT 4,B CB 60
Test bit 4 of Register B (the disk I/O flags): set when the sectors are numbered from 1.
465B
If the Z FLAG is set (sectors from 0), JUMP to 465EH.
465D
INC E 1C
INCrement Register E (the sector) to the number in the headers.
465E
LD A,E 7B
Copy Register E (the sector number) into Register A.
465F
OUT (F2H),A D3 F2
Send Register A (the sector number) to the controller's sector register, port F2H.
4661
LD A,D 7A
Copy Register D (the physical track) into Register A.
4662
OUT (F3H),A D3 F3
Send Register A (the physical track) to the controller's data register, port F3H, the destination of the Seek.
4664
LD C,18H 0E 18
Load Register C with the 1793 Seek command 18H:
1793 FDC Command: 18H (00011000)Function Description
Bit 7Bit 6Bit 5Bit 4Bit 3Bit 2Bit 1Bit 0Summary of Bits
00011000Command=Seek (bits 7-4 = 0001)
h = 1: head load at the start
V = 0: no verify
r1 r0 = 00: stepping rate 6 ms; the code ORs in the drive's rate (6 ms, 12 ms, 20 ms or 30 ms)
4666
GOSUB to 46F7H to add the drive's stepping rate to Register C, issue the Seek and wait until the controller is no longer busy.
4669
POP AF F1
Restore Register Pair AF from the stack: Register A = the track number in the headers (Register D pushed at 4642H).
466A
POP HL E1
Restore Register Pair HL from the stack: the buffer.
466B
PUSH HL E5
Save Register Pair HL (the buffer) back onto the stack for the error path.
466C
OUT (F1H),A D3 F1
Send Register A (the track number in the headers) to the controller's track register, port F1H, so that the controller looks for that number in the sector headers. 46CAH puts the physical track back afterwards.
466E
PUSH DE D5
Save Register Pair DE (Register D = the physical track) onto the stack; 46C9H restores it.
466F
DI F3
Disable interrupts: from here the CPU only moves the data and waits for the controller.
4670
IN A,(F0H) DB F0
Read the controller's status from port F0H into Register A, which clears any interrupt request left from the Seek.
4672
LD (46BAH),SP ED 73 BA 46
Store the Stack Pointer at 46BAH, the operand of the LD SP,nnnn at 46B9H, so that the NMI routine can return to this level.
4676
LD DE,46B6H 11 B6 46
Point Register Pair DE to 46B6H, the routine the NMI is to run at the end of the command.
4679
LD (404AH),DE ED 53 4A 40
Store Register Pair DE (46B6H) at 404AH, the address part of the NMI vector at 4049H, which the ROM's NMI handler (0066H, 3039H) jumps to.
467D
LD A,C3H 3E C3
Load Register A with C3H, the opcode of JP, for the first byte of the NMI vector.
467F
LD (4049H),A 32 49 40
Store Register A (C3H) at 4049H: the NMI vector is now JP 46B6H.
4682
LD A,C0H 3E C0
Load Register A with C0H, the NMI mask:
Port E4H (NMI Mask): C0H (11000000)Function Description
Bit 7Bit 6Bit 5Bit 4Bit 3Bit 2Bit 1Bit 0Summary of Bits
11000000Bit 7 = 1: NMI from the controller's interrupt request (INTRQ) enabled
Bit 6 = 1: NMI from the drive motor time-out enabled
Bits 5-0: not used
4684
OUT (E4H),A D3 E4
Send Register A (C0H) to the NMI mask, port E4H: the controller's interrupt at the end of the command now causes an NMI.
4686
LD A,00H 3E 00
Load Register A with the read or write command. Self-Modifying Code
The operand at 4687H is stored by 45EDH (88H, A8H or A9H).
4688
OUT (F0H),A D3 F0
Send Register A (the command) to the controller's command register, port F0H: the read or write starts.
468A
GOSUB to 4789H, a short delay so that the controller's status is valid after the command (Register A returns the status).
468D
LD BC,00F3H 01 F3 00
Load Register Pair BC (00F3H as loaded): Register C = F3H, the data port for INI/OUTI, Register B = the first count. Self-Modifying Code
The operand byte at 468FH (Register B, 00H in the file) is stored by 46ABH: 02H for a read, 97H for a write.
4690
LD A,(427FH) 3A 7F 42
Fetch the select byte from 427FH into Register A.
4693
OR 40H F6 40
Set bit 6 of Register A (OR 40H): wait states, so that an INI or OUTI on port F3H holds the CPU until the controller has the byte.
Port F4H: Drive Select and OptionsFunction Description
Bit 7Bit 6Bit 5Bit 4Bit 3Bit 2Bit 1Bit 0Summary of Bits
DWPSd3d2d1d0D (bit 7): 1 = double density (MFM), 0 = single density (FM)
W (bit 6): 1 = wait states while the controller has no data ready
P (bit 5): 1 = write precompensation
S (bit 4): 0 = side 0, 1 = side 1
d3-d0: one bit per drive, 1 = selected
4695
LD E,A 5F
Copy Register A (the select byte with wait states) into Register E for 46A3H.
4696
LD D,02H 16 02
Load Register D with 02H, the data request bit of the status.
4698
IN A,(F0H) DB F0
Loop Start
Read the controller's status from port F0H into Register A.
469A
AND D A2
Keep the data request bit: AND Register A (the status) with Register D (02H).
469B
If the Z FLAG is set (no data request yet), LOOP BACK to 4698H.
469D
INI ED A2
Move the first byte: INI (read) reads port C (F3H) into the buffer at Register Pair HL; Register Pair HL goes up by 1 and Register B (the first count) down by 1. Self-Modifying Code
The second byte at 469EH is A2H (INI) for a read or A3H (OUTI, the buffer byte to port F3H) for a write, stored by 46AFH.
469F
DECrement Register B and loop at 469FH itself until it is zero: no delay for a read (Register B = 01H), 150 passes for a write (96H), giving the controller time before the rest of the data.
46A1
DEC B 05
DECrement Register B from 00H to FFH, the 255 bytes still to move.
46A2
LD A,E 7B
Copy Register E (the select byte with the wait state bit) into Register A.
46A3
OUT (F4H),A D3 F4
Loop Start
Send Register A (the select byte) to port F4H, re-selecting the drive with wait states before each byte.
46A5
INI ED A2
Move the next byte: INI (read) or OUTI (write) between port C (F3H) and the buffer at Register Pair HL; Register B counts down. The CPU waits on the port until the controller is ready. Self-Modifying Code
The byte at 46A6H is A2H or A3H, stored by 46B2H.
46A7
Loop End
If the NZ FLAG is set (Register B not yet zero), LOOP BACK to 46A3H for the next byte.
46A9
All 256 bytes are moved: wait here (JUMP to 46A9H) until the controller ends the command; its interrupt request causes the NMI that runs 46B6H.

46ABH - Patch the Transfer Loop

Called once per read or write (4601H) with Register A = the first byte count and Register B = A2H (INI) or A3H (OUTI).

46AB
LD (468FH),A 32 8F 46
Store Register A (02H or 97H) at 468FH, the Register B operand of the LD BC at 468DH.
46AE
LD A,B 78
Copy Register B (A2H or A3H) into Register A.
46AF
LD (469EH),A 32 9E 46
Store Register A at 469EH, the second byte of the instruction at 469DH: INI or OUTI.
46B2
LD (46A6H),A 32 A6 46
Store Register A at 46A6H, the second byte of the instruction at 46A5H: INI or OUTI.
46B5
RET C9
RETurn to 4604H with the transfer loop set up for a read or a write.

46B6H - End of a Read or Write (NMI)

The controller's interrupt request at the end of the command causes an NMI; the ROM's handler (3039H) jumps through the vector at 4049H to here. The stack is reset to the level saved at 4672H, the NMI vector is turned into RETN, and the status is checked: errors other than a deleted data mark or write protection are retried.

46B6
XOR A AF
Set Register A to 00H, the value that masks every NMI.
46B7
OUT (E4H),A D3 E4
Send Register A (00H) to the NMI mask, port E4H:
Port E4H (NMI Mask): 00H (00000000)Function Description
Bit 7Bit 6Bit 5Bit 4Bit 3Bit 2Bit 1Bit 0Summary of Bits
00000000Bit 7 = 0: NMI from INTRQ disabled
Bit 6 = 0: NMI from the motor time-out disabled
Bits 5-0: not used
46B9
LD SP,0000H 31 00 00
Load the Stack Pointer with the value saved before the command (0000H in the file), which drops the NMI's return address (the waiting loop at 46A9H). Self-Modifying Code
The operand at 46BAH is stored by 4672H.
46BC
LD HL,45EDH 21 ED 45
Load Register Pair HL with 45EDH: its bytes ED 45 are the opcode of RETN.
46BF
LD (4049H),HL 22 49 40
Store Register Pair HL at 4049H: the NMI vector becomes RETN (ED 45), so a stray NMI returns at once.
46C2
IN A,(F0H) DB F0
Read the controller's status from port F0H into Register A: the result of the read or write.
46C4
LD B,A 47
Copy Register A (the status) into Register B.
46C5
LD A,D0H 3E D0
Load Register A with D0H, the Force Interrupt command that ends the command without an interrupt:
1793 FDC Command: D0H (11010000)Function Description
Bit 7Bit 6Bit 5Bit 4Bit 3Bit 2Bit 1Bit 0Summary of Bits
11010000Command=Force Interrupt (bits 7-4 = 1101)
I3-I0 = 0000: end the command without an interrupt
46C7
OUT (F0H),A D3 F0
Send Register A (D0H) to the controller's command register, port F0H, ending any command still running.
46C9
POP AF F1
Restore Register Pair AF from the stack: Register A = the physical track (Register D pushed at 466EH).
46CA
OUT (F1H),A D3 F1
Send Register A (the physical track) to the controller's track register, port F1H, putting back the head's real position.
46CC
LD A,B 78
Copy Register B (the status) into Register A.
46CD
POP HL E1
Restore Register Pair HL (the buffer) from the stack.
46CE
POP DE D1
Restore Register Pair DE (the sector number) from the stack.
46CF
POP BC C1
Restore Register Pair BC from the stack: Register B = the tries left.
46D0
EI FB
Enable interrupts again, now that the transfer is over (they were disabled at 466FH).
46D1
AND FCH E6 FC
Keep bits 2-7 of Register A (the status, AND FCH):
1793 Status after Read/Write Sector, AND FCHFunction Description
Bit 7Bit 6Bit 5Bit 4Bit 3Bit 2Bit 1Bit 0Summary of Bits
NWRFCL00N: 1 = not ready
W: 1 = write protected (write)
R: 1 = deleted data mark F8H (read), write fault (write)
F: 1 = record not found
C: 1 = CRC error
L: 1 = lost data
Bits 1-0 (data request, busy): removed by AND FCH
46D3
If the Z FLAG is set (no error bit), JUMP to 46E7H to return Register A = 00H with the Z FLAG.
46D5
LD C,A 4F
Copy Register A (the error bits) into Register C.
46D6
AND 9CH E6 9C
Keep the bits of Register A that can be retried (AND 9CH): not ready, record not found, CRC error, lost data.
46D8
If the Z FLAG is set (only the deleted mark or write protection, bits 5-6), JUMP to 46E0H to turn them into an error code without a retry.
46DA
LD C,A 4F
Copy Register A (the retryable bits) into Register C.
46DB
ADD A,A 87
ADD Register A to itself: bit 7 (not ready) moves out. The Z FLAG is set if not ready was the only bit.
46DC
If the Z FLAG is set (drive not ready), JUMP to 46E0H: no retry.
46DE
DECrement Register B (the tries left) and, if it is not zero, JUMP to 46EAH to try again.
46E0
LD A,00H 3E 00
Load Register A with the error base: 00H for a read, 08H for a write. Self-Modifying Code
The operand at 46E1H is stored by 45FDH.
46E2
INC A 3C
Loop Start
INCrement Register A (the error code) by 1 for each bit position.
46E3
RRC C CB 09
Rotate Register C (the error bits) right: the lowest bit goes into the CARRY FLAG.
46E5
Loop End
If the NO CARRY FLAG is set, LOOP BACK to 46E2H. Register A ends as the error base + 1 + the number of the lowest error bit: 03H/0BH lost data, 04H/0CH CRC error, 05H/0DH record not found, 06H deleted mark (read), 0EH write fault, 0FH write protected, 08H/10H not ready.
46E7
EI FB
Enable interrupts (also the exit for success and for 4624H).
46E8
POP BC C1
Restore Register Pair BC from the stack: the caller's BC (pushed at 45F2H).
46E9
RET C9
RETurn with Register A = 00H and the Z FLAG on success, or Register A = the error code and the NZ FLAG.
46EA
BIT 4,C CB 61
Retry: test bit 4 of Register C (the error bits): record not found.
46EC
If the NZ FLAG is set (record not found), GOSUB to 46F2H, which restores the head to track 0 on every second retry.
46EF
JUMP to 460BH to try the sector again, Register B = the tries left.

46F2H - Restore or Seek and Wait

46F2H restores the head to track 0 when Register B (the tries left) is odd. 46F7H issues the Type I command in Register C (08H Restore, 18H Seek) with the drive's stepping rate and waits until the controller is not busy; if the drive is not ready the command is ended with a Force Interrupt. Register A returns the status.

46F2
BIT 0,B CB 40
Test bit 0 of Register B (the tries left): set on every second retry.
46F4
RET Z C8
If the Z FLAG is set (an even count), RETurn without restoring.
46F5
LD C,08H 0E 08
Load Register C with the 1793 Restore command 08H:
1793 FDC Command: 08H (00001000)Function Description
Bit 7Bit 6Bit 5Bit 4Bit 3Bit 2Bit 1Bit 0Summary of Bits
00001000Command=Restore (bits 7-4 = 0000)
h = 1: head load at the start
V = 0: no verify
r1 r0 = 00: stepping rate 6 ms; the code ORs in the drive's rate (6 ms, 12 ms, 20 ms or 30 ms)
46F7
LD A,(4282H) 3A 82 42
Fetch the current drive's flags from 4282H (PDRIVE byte 2) into Register A.
46FA
AND 03H E6 03
Keep bits 0-1 of Register A (AND 03H): the drive's stepping rate.
46FC
OR C B1
OR Register C (the command) into Register A: the command with the stepping rate.
46FD
OUT (F0H),A D3 F0
Send Register A (the command) to the controller's command register, port F0H.
46FF
Loop Start
GOSUB to 4789H for a short delay and the controller's status in Register A.
4702
BIT 0,A CB 47
Test bit 0 of Register A (the status): 1 while the controller is busy with the command.
4704
RET Z C8
If the Z FLAG is set (not busy), RETurn with the status in Register A.
4705
RLCA 07
Rotate Register A left: bit 7 (not ready) goes into the CARRY FLAG.
4706
If the CARRY FLAG is set (the drive is not ready), JUMP to 470DH to end the command.
4708
GOSUB to 4715H to select the drive again (port F4H), which keeps its motor running during a long seek.
470B
LOOP BACK to 46FFH to delay and read the status into Register A again.
470D
LD A,D0H 3E D0
Load Register A with D0H, the Force Interrupt command (end the command without an interrupt). 472DH and 4763H call here to make sure the controller is idle.
470F
JUMP to 46FDH to send Register A (D0H) and wait until the controller is not busy.

4711H - Keep the Drives Rotating

4416H comes here (manual 3.10). If the drive is turning (the status' not-ready bit is clear), the current drive is selected again, which restarts the drives' motor timer. 4715H only writes the select byte to port F4H.

4711
IN A,(F0H) DB F0
Read the controller's status from port F0H into Register A.
4713
RLCA 07
Rotate Register A left: bit 7 (not ready) goes into the CARRY FLAG.
4714
RET C D8
If the CARRY FLAG is set (not ready: the drives are not turning), RETurn.
4715
LD A,(427FH) 3A 7F 42
Fetch the select byte from 427FH into Register A: the current drive's bit with its density, precompensation and side bits.
4718
OUT (F4H),A D3 F4
Send Register A (the select byte) to port F4H:
Port F4H: Drive Select and OptionsFunction Description
Bit 7Bit 6Bit 5Bit 4Bit 3Bit 2Bit 1Bit 0Summary of Bits
DWPSd3d2d1d0D (bit 7): 1 = double density (MFM), 0 = single density (FM)
W (bit 6): 1 = wait states while the controller has no data ready
P (bit 5): 1 = write precompensation
S (bit 4): 0 = side 0, 1 = side 1
d3-d0: one bit per drive, 1 = selected
471A
RET C9
RETurn to the caller; Register A holds the select byte written to port F4H.

471BH - Select a Drive

Enter at 471BH for the drive of the FCB at Index Register IX, at 4720H for the current drive, or at 4723H (445BH) with the drive in Register A. A drive number not below option AL gives the NZ FLAG with Register A = 20H. Otherwise the old drive's track register is saved, the new drive becomes current, its PDRIVE bytes are copied to 4280H-4287H, the controller's sector and track registers are loaded for it and the drive is selected; motor start and head settle delays are added when needed. Returns the Z FLAG with Register A = 00H. Registers BC, DE and HL are preserved.

471B
LD A,(IX+06H) DD 7E 06
Fetch the FCB's drive number from (IX+06H) (Index Register IX = the FCB) into Register A.
471E
JUMP to 4723H to select the drive in Register A.
4720
LD A,(427EH) 3A 7E 42
Fetch the current drive number from 427EH into Register A.
4723
PUSH HL E5
Save Register Pair HL (the caller's) onto the stack; 4786H restores it.
4724
PUSH DE D5
Save Register Pair DE (the caller's) onto the stack; 4785H restores it.
4725
PUSH BC C5
Save Register Pair BC (the caller's) onto the stack; 4784H restores it.
4726
CP 01H FE 01
Compare Register A (the drive number) against option AL, the number of drives. Self-Modifying Code
The operand at 4727H is stored by the reset (4D75H) from byte A0H of the system data sector; 01H in the file. The NO CARRY FLAG is set if the drive does not exist.
4728
LD C,A 4F
Copy Register A (the drive number) into Register C.
4729
LD A,20H 3E 20
Load Register A with error code 20H (ILLEGAL OR MISSING DRIVE #) without changing the flags.
472B
If the NO CARRY FLAG is set (no such drive), JUMP to 4784H to return Register A = 20H with the NZ FLAG.
472D
GOSUB to 470DH to end any controller command and wait until it is idle.
4730
LD HL,427EH 21 7E 42
Point Register Pair HL to 427EH, the current drive number.
4733
LD A,(HL) 7E
Fetch the old drive number at Register Pair HL (427EH) into Register A.
4734
LD (HL),C 71
Store Register C (the new drive) at Register Pair HL (427EH): it is now the current drive.
4735
CP C B9
Compare Register A (the old drive) against Register C (the new drive). The Z FLAG is set if it is the same drive.
4736
PUSH AF F5
Save Register Pair AF (the Z FLAG = same drive) onto the stack for 4777H.
4737
ADD A,76H C6 76
ADD 76H to Register A (the old drive): the low byte of 4276H + the old drive.
4739
LD L,A 6F
Copy Register A into Register L: Register Pair HL = 4276H + the old drive, its saved track register.
473A
IN A,(F1H) DB F1
Read the controller's track register from port F1H into Register A: the old drive's head position.
473C
LD (HL),A 77
Store Register A (the track) at Register Pair HL (4276H + old drive).
473D
LD A,C 79
Copy Register C (the new drive) into Register A.
473E
ADD A,76H C6 76
ADD 76H to Register A (the new drive): the low byte of 4276H + the new drive.
4740
LD L,A 6F
Copy Register A into Register L: Register Pair HL = 4276H + the new drive, its saved track register.
4741
PUSH HL E5
Save Register Pair HL (the new drive's saved track address) onto the stack for 4769H.
4742
LD B,C 41
Copy Register C (the new drive) into Register B.
4743
LD A,80H 3E 80
Load Register A with 80H, so that the rotates below put a single bit at the drive's position.
4745
INC B 04
INCrement Register B (the drive number): the loop below rotates drive + 1 times.
4746
RLCA 07
Loop Start
Rotate Register A left (bit 7 goes to bit 0).
4747
Loop End
DECrement Register B and LOOP BACK to 4746H until it is zero: Register A = 1 shifted left by the drive number (01H, 02H, 04H, 08H).
4749
LD (427FH),A 32 7F 42
Store Register A (the drive bit) at 427FH, the select byte; its density, precompensation and side bits start clear.
474C
LD A,C 79
Copy Register C (the new drive) into Register A.
474D
RLCA 07
Rotate Register A (the drive number, 0-3) left: drive x 2.
474E
RLCA 07
Rotate Register A left again: drive x 4, for the drive x 10 below.
474F
ADD A,C 81
ADD Register C (the drive) to Register A: drive x 5.
4750
ADD A,A 87
ADD Register A to itself: drive x 10, the offset of the drive's PDRIVE entry.
4751
ADD A,91H C6 91
ADD 91H to Register A: the low byte of 4291H + drive x 10.
4753
LD L,A 6F
Copy Register A into Register L; Register H is still 42H, so Register Pair HL = the drive's PDRIVE entry.
4754
LD (42B9H),HL 22 B9 42
Store Register Pair HL (the entry address) at 42B9H.
4757
LD C,08H 0E 08
Load Register C with 08H; Register B is 0 after the DJNZ, so Register Pair BC = 8 bytes.
4759
LD DE,4280H 11 80 42
Point Register Pair DE to 4280H, the current drive's PDRIVE bytes.
475C
LDIR ED B0
Copy 8 bytes from Register Pair HL (the PDRIVE entry) to Register Pair DE (4280H-4287H), Register Pair BC counting down to 0.
475E
LD A,(4286H) 3A 86 42
Fetch PDRIVE byte 6 from 4286H into Register A: the value for the sector register at selection.
4761
OUT (F2H),A D3 F2
Send Register A to the controller's sector register, port F2H.
4763
GOSUB to 470DH to end any command and wait until the controller is idle.
4766
IN A,(F0H) DB F0
Read the controller's status from port F0H into Register A: bit 7 is set if the drives were not turning.
4768
LD E,A 5F
Copy Register A (the status) into Register E for the test at 4772H.
4769
POP BC C1
Restore Register Pair BC from the stack: the address of the new drive's saved track (pushed at 4741H).
476A
LD A,(BC) 0A
Fetch the new drive's saved track at Register Pair BC into Register A.
476B
OUT (F1H),A D3 F1
Send Register A (the track) to the controller's track register, port F1H.
476D
GOSUB to 4708H, which writes the select byte to port F4H (4715H) and waits until the controller is not busy.
4770
LD B,80H 06 80
Load Register B with 80H, the length of the motor start delay for 4C92H.
4772
BIT 7,E CB 7B
Test bit 7 of Register E (the status before selection): not ready.
4774
If the NZ FLAG is set (the drives were not turning), GOSUB to 4C92H to wait for the motor to come up to speed (Register B = 80H).
4777
POP AF F1
Restore Register Pair AF from the stack: the Z FLAG is set if the same drive was selected before (pushed at 4736H).
4778
If the Z FLAG is set (same drive), JUMP to 4783H: no head settle delay.
477A
LD A,(4282H) 3A 82 42
Fetch the drive's flags from 4282H (PDRIVE byte 2) into Register A.
477D
RLCA 07
Rotate Register A left: bit 7 (a drive that needs a head settle delay) goes into the CARRY FLAG.
477E
LD B,0CH 06 0C
Load Register B with 0CH, the length of the head settle delay.
4780
If the CARRY FLAG is set, GOSUB to 4C92H to wait for the head to settle (Register B = 0CH).
4783
XOR A AF
Set Register A to 00H: success (also reached from 4778H and 47A3H).
4784
POP BC C1
Restore Register Pair BC (the caller's, pushed at 4725H or 4797H) from the stack.
4785
POP DE D1
Restore Register Pair DE (the caller's, pushed at 4724H or 4796H) from the stack.
4786
POP HL E1
Restore Register Pair HL (the caller's, pushed at 4723H or 4795H) from the stack.
4787
OR A B7
OR Register A with itself: the Z FLAG for 00H, the NZ FLAG for an error code (20H from 472BH, 08H from 47ACH).
4788
RET C9
RETurn with Register A = 00H or the error code.

4789H - Short Delay and Read the Status

Waits a few microseconds (the controller needs time after a command before its status is valid), then reads the status into Register A.

4789
LD A,08H 3E 08
Load Register A with the delay count. Self-Modifying Code
The operand at 478AH is 8 x option BJ, stored by the reset (4DABH); 08H in the file.
478B
DEC A 3D
Loop Start
DECrement Register A (the delay count) by 1.
478C
Loop End
If the NZ FLAG is set (count not yet zero), LOOP BACK to 478BH.
478E
IN A,(F0H) DB F0
Read the controller's status from port F0H into Register A.
4790
RET C9
RETurn to the caller with the controller's status in Register A.

4791H - Test for a Mounted Diskette

445EH comes here (manual 3.30). Selects the drive in Register A, then waits for the index bit of the status to change, which proves that a diskette is in the drive and turning. Returns the Z FLAG with Register A = 00H, or the NZ FLAG with Register A = 08H (DEVICE NOT AVAILABLE) or 20H (no such drive).

4791
GOSUB to 4723H to select the drive in Register A.
4794
RET NZ C0
If the NZ FLAG is set (no such drive, Register A = 20H), RETurn.
4795
PUSH HL E5
Save Register Pair HL onto the stack (4784H restores it).
4796
PUSH DE D5
Save Register Pair DE (the caller's) onto the stack; it is used as the time-out count.
4797
PUSH BC C5
Save Register Pair BC (the caller's) onto the stack; Register B holds the first status.
4798
LD DE,0000H 11 00 00
Load Register Pair DE with the time-out count. Self-Modifying Code
The operand at 4799H is 2A00H x option BJ, stored by the reset (4DB6H); 0000H in the file.
479B
IN A,(F0H) DB F0
Read the controller's status from port F0H into Register A.
479D
LD B,A 47
Copy Register A (the first status) into Register B.
479E
IN A,(F0H) DB F0
Loop Start
Read the controller's status from port F0H into Register A.
47A0
XOR B A8
XOR Register B (the first status) into Register A: the bits that have changed.
47A1
AND 02H E6 02
Keep bit 1 of Register A (AND 02H), the index pulse bit.
47A3
If the NZ FLAG is set (the index bit changed: a diskette is turning), JUMP to 4783H to return Register A = 00H with the Z FLAG.
47A5
DEC DE 1B
DECrement Register Pair DE (the time-out count) by 1.
47A6
LD A,D 7A
Copy Register D (the high byte of the time-out count) into Register A.
47A7
OR E B3
OR Register E into Register A. The Z FLAG is set when Register Pair DE is 0000H.
47A8
Loop End
If the NZ FLAG is set (time left), LOOP BACK to 479EH.
47AA
LD A,08H 3E 08
Load Register A with error code 08H (DEVICE NOT AVAILABLE).
47AC
JUMP to 4784H to return Register A = 08H with the NZ FLAG.

47AEH - Allocate, and Find the Sector for NEXT

444BH comes here (manual 3.26). The part from 47B5H is used by every read and write: it turns the FCB's NEXT position into a sector number on the diskette. It looks through the extents kept in the FCB, then through the file's directory entries (FPDE and FXDEs), and when the sector lies past the file's last granule it calls SYS2/SYS to allocate more (a read gets error 3EH instead). On exit Register Pair DE = the sector number, Register Pair HL = the FCB's buffer, Register A = 00H and the Z FLAG is set; errors leave through 4972H.

47AE
GOSUB to 4925H: the rest of this routine runs as a DOS function, with Index Register IX = the FCB (Register Pair DE on entry) and Index Register IY = 4280H; an error at any depth leaves through 4972H.
47B1
Entry used by the write routines (4A0CH, 4A40H). GOSUB to 495CH: the file's access level (FCB+1 bits 0-2, Index Register IX) must be below 5 (writing allowed), else error 25H.
47B4
XOR A AF
Set Register A to 00H: when NEXT is past the end of the file's space, more space is to be allocated.
47B5
LD (4860H),A 32 60 48
Entry from the read routine (49C6H) with Register A = B6H. Store Register A at 4860H, the offset of the JR at 485FH: 00H lets the allocation at 4861H run, B6H turns the JR into a jump to 4817H with error 3EH.

Search Loop
488BH comes back here after copying a directory entry's extents into the FCB.

47B8
GOSUB to 471BH to select the drive of the FCB, (IX+06H). Register A returns 20H with the NZ FLAG for a bad drive number.
47BB
If the NZ FLAG is set (the drive cannot be selected, Register A = the error), JUMP to 4817H to leave the DOS function with it.
47BD
GOSUB to 490DH: Register C = NEXT's low byte (the byte in the sector) and Register Pair HL = NEXT's middle and high bytes, the sector of the file (the flags from its EOF comparison are not used here).
47C0
EX DE,HL EB
Exchange Register Pairs DE and HL: Register Pair DE = the relative sector number from NEXT.
47C1
LD A,(IX+00H) DD 7E 00
Fetch FCB+0 from (IX+00H) (Index Register IX = the FCB) into Register A: bit 1 = NEXT is a sector of the whole diskette, bit 2 = a bad FCB.
47C4
RRCA 0F
Rotate Register A (FCB+0) right: bit 0 goes out.
47C5
RRCA 0F
Rotate Register A right again: bit 1 of FCB+0 goes into the CARRY FLAG.
47C6
If the CARRY FLAG is set (FCB+0 bit 1: NEXT is already a sector of the whole diskette, Register Pair DE), JUMP to 47F8H to load Register Pair HL with the buffer and return.
47C8
RRCA 0F
Rotate Register A right again: bit 2 of FCB+0 goes into the CARRY FLAG.
47C9
LD A,2DH 3E 2D
Load Register A with error code 2DH (BAD FCB DATA). 4821H also jumps here with the CARRY FLAG set.
47CB
If the CARRY FLAG is set (FCB+0 bit 2, or FCB+1 bit 3 clear when 4821H jumps here), JUMP to 4817H to leave with Register A = 2DH.
47CD
EX DE,HL EB
Exchange Register Pairs DE and HL: Register Pair HL = the relative sector number.
47CE
GOSUB to 4C57H to divide Register Pair HL by 5: Register Pair HL = the granule within the file, Register A = the sector within the granule (0-4), Register B = 0.
47D1
LD C,0EH 0E 0E
Load Register C with 0EH: with Register B = 0, Register Pair BC = 14, the offset of the FCB's first extent.
47D3
EX DE,HL EB
Exchange Register Pairs DE and HL: Register Pair DE = the granule within the file.
47D4
PUSH IX DD E5
Save Index Register IX (the FCB address) onto the stack.
47D6
POP HL E1
Restore it into Register Pair HL: Register Pair HL = the FCB.
47D7
ADD HL,BC 09
ADD Register Pair BC (14) to Register Pair HL: Register Pair HL = FCB+14, the first of the extents kept in the FCB.
47D8
PUSH AF F5
Save Register Pair AF onto the stack: Register A = the sector within the granule.
47D9
PUSH HL E5
Save Register Pair HL (FCB+14) onto the stack.
47DA
PUSH DE D5
Save Register Pair DE (the granule within the file) onto the stack.
47DB
LD A,08H 3E 08
Load Register A with 08H, the number of extents kept in the FCB (FCB+14 to +21 and FCB+24 to +31).

FCB Extent Loop
Register Pair HL = the extent (2 bytes: the lump, then bits 5-7 first granule and bits 0-4 count minus 1), Register Pair DE = the granules still to go, AF' = extents left.

47DD
EX AF,AF' 08
Exchange Register Pair AF with the alternate AF': the count of extents left is kept in Register A'.
47DE
LD A,(HL) 7E
Fetch the extent's lump number at Register Pair HL into Register A (FFH = no more extents).
47DF
INC A 3C
INCrement Register A (the lump). The Z FLAG is set if it was FFH.
47E0
INC HL 23
INCrement Register Pair HL to the extent's second byte. INC HL does not change the flags.
47E1
If the Z FLAG is set (the lump was FFH: no more extents in the FCB), JUMP to 4837H to look through the directory entries.
47E3
LD A,(HL) 7E
Fetch the extent's second byte at Register Pair HL into Register A: bits 5-7 = the first granule in the lump, bits 0-4 = the number of granules minus 1.
47E4
GOSUB to 488EH, which subtracts the extent's granule count (Register Pair BC) from the granules to go (Register Pair DE): it returns Register Pair HL = the difference, Register Pair DE = the extent pointer, Register A = bits 0-4 of the second byte, and the CARRY FLAG set if the granule is in this extent.
47E7
If the NO CARRY FLAG is set (the granule lies beyond this extent), JUMP to 481AH to try the next one.
47E9
ADD HL,BC 09
ADD Register Pair BC (the extent's granule count) back to Register Pair HL: Register Pair HL = the granule's position within this extent.
47EA
EX DE,HL EB
Exchange Register Pairs DE and HL: Register Pair DE = the position in the extent, Register Pair HL = the extent's second byte.
47EB
XOR (HL) AE
XOR Register A (bits 0-4 of the second byte) with the second byte at Register Pair HL: Register A keeps bits 5-7, the extent's first granule in the lump.
47EC
RLCA 07
Rotate Register A left: bits 5-7 move towards bits 0-2 (three rotates in all).
47ED
RLCA 07
Rotate Register A left again (second of three).
47EE
RLCA 07
Rotate Register A left a third time: Register A = the extent's first granule in the lump (0-7).
47EF
ADD A,E 83
ADD Register E (the position in the extent) to Register A: the granule within the lump.
47F0
LD E,A 5F
Copy Register A (the granule within the lump) into Register E for 4C21H.
47F1
POP AF F1
Restore Register Pair AF from the stack to drop the granule within the file (pushed at 47DAH).
47F2
POP AF F1
Restore Register Pair AF from the stack again to drop FCB+14 (pushed at 47D9H).
47F3
POP AF F1
Restore Register Pair AF from the stack: Register A = the sector within the granule (pushed at 47D8H).
47F4
DEC HL 2B
DECrement Register Pair HL back to the extent's first byte, the lump number.
47F5
GOSUB to 4C21H with Register Pair HL = the lump byte, Register E = the granule within the lump and Register A = the sector in the granule: it returns Register Pair DE = (lump x granules per lump + Register E) x 5 + Register A, the sector on the diskette.
47F8
LD L,(IX+03H) DD 6E 03
Load Register L with the low byte of the FCB's buffer address from (IX+03H) (Index Register IX = the FCB). 4AB0H also calls this part to get the buffer address.
47FB
LD H,(IX+04H) DD 66 04
Load Register H with the high byte of the buffer address from (IX+04H) (Index Register IX = the FCB). Register Pair HL = the FCB's buffer.
47FE
XOR A AF
Set Register A to 00H and set the Z FLAG: success.
47FF
RET C9
RETurn with Register Pair DE = the sector on the diskette, Register Pair HL = the buffer and the Z FLAG set.

FXDE Link
Reached from 485BH when an entry's extent list ends in FEH: Register A = the DEC of the FXDE that continues the file, Register Pair DE = the granules still to go.

4800
POP BC C1
Restore Register Pair BC from the stack to drop the entry's extent pointer (pushed at 4852H).
4801
POP BC C1
Restore Register Pair BC from the stack: the granules in the file before this entry (pushed at 484DH).
4802
POP HL E1
Restore Register Pair HL from the stack: the granules to go when this entry was started (pushed at 484CH).
4803
SBC HL,DE ED 52
SUBtract Register Pair DE (the granules still to go) from Register Pair HL (with the CARRY FLAG, clear after the CP FEH): Register Pair HL = the granules in this entry.
4805
ADD HL,BC 09
ADD Register Pair BC (the granules before this entry) to Register Pair HL: the granules before the next entry.
4806
LD B,H 44
Copy Register H into Register B: Register Pair BC = the granules before the next entry (high byte).
4807
LD C,L 4D
Copy Register L into Register C (the low byte).
4808
PUSH AF F5
Save Register Pair AF onto the stack: Register A = the DEC of the FXDE.
4809
GOSUB to 48D4H to read the FXDE's directory sector (only if it is not already in 4300H): Register Pair HL returns the address of the FXDE.
480C
If the NZ FLAG is set (a read error or a bad entry, Register A = the error code), JUMP to 4817H to leave with it.
480E
LD A,FFH 3E FF
Load Register A with the DEC of the entry that pointed here. Self-Modifying Code
The operand at 480FH is stored by 4845H for every entry looked at; SYS2/SYS's allocation (64H) also reads it as the DEC of the file's last entry.
4810
INC HL 23
INCrement Register Pair HL to the FXDE's byte 1, the DEC of the entry before it (the back link).
4811
CP (HL) BE
Compare Register A (the DEC of the entry that pointed here) against the back link at Register Pair HL. The Z FLAG is set if the chain is consistent.
4812
DEC HL 2B
DECrement Register Pair HL back to the FXDE's byte 0.
4813
If the Z FLAG is set (the back link matches), JUMP to 4844H to go on with this FXDE.
4815
LD A,2CH 3E 2C
Load Register A with error code 2CH (BAD DIRECTORY DATA). Also reached from 484AH (an entry not in use) and 486DH (a real extent in the fifth element).
4817
JUMP to 4972H to leave the DOS function with the error code in Register A; 4972H restores the Stack Pointer saved by 4925H.
481A
EX DE,HL EB
Exchange Register Pairs DE and HL: Register Pair HL = the extent's second byte, Register Pair DE = the granules still to go (after this extent).
481B
INC HL 23
INCrement Register Pair HL to the next extent in the FCB.
481C
BIT 3,(IX+01H) DD CB 01 5E
Test bit 3 of FCB+1 at (IX+01H) (Index Register IX = the FCB): set when the FCB carries the second set of extents at FCB+22 and up.
4820
SCF 37
Set the CARRY FLAG, for the error path to 47C9H. SCF does not change the Z FLAG.
4821
If the Z FLAG is set (FCB+1 bit 3 clear: an FCB without the second extent set), JUMP to 47C9H to leave with error 2DH (the CARRY FLAG is set).
4823
EX AF,AF' 08
Exchange Register Pair AF with the alternate AF': Register A = the extents left.
4824
CP 05H FE 05
Compare Register A (the extents left) against 05H. The Z FLAG is set after the first four extents (FCB+14 to +21).
4826
If the NZ FLAG is set (not at the end of the first four), JUMP to 4834H to count this extent.
4828
LD C,(HL) 4E
Load Register C with the low byte of FCB+22 at Register Pair HL: the number of granules in the file before the extents kept at FCB+24.
4829
INC HL 23
INCrement Register Pair HL to FCB+23, the high byte of the granules before the second extent set.
482A
LD B,(HL) 46
Load Register B with the high byte at Register Pair HL (FCB+23). Register Pair BC = the granules before the second set of extents.
482B
INC HL 23
INCrement Register Pair HL to FCB+24, the first extent of the second set.
482C
POP DE D1
Restore Register Pair DE from the stack: the granule within the file (pushed at 47DAH).
482D
PUSH DE D5
Save Register Pair DE back onto the stack; it is needed again at 483AH.
482E
EX DE,HL EB
Exchange Register Pairs DE and HL: Register Pair HL = the granule within the file, Register Pair DE = FCB+24.
482F
SBC HL,BC ED 42
SUBtract Register Pair BC (the granules before the second set) from Register Pair HL (the CARRY FLAG is clear after the CP). The CARRY FLAG is set if the granule lies before them.
4831
EX DE,HL EB
Exchange Register Pairs DE and HL: Register Pair DE = the granule relative to the second set, Register Pair HL = FCB+24.
4832
If the CARRY FLAG is set (the granule is in a directory entry between the two sets), JUMP to 4837H to look through the directory.
4834
DEC A 3D
DECrement Register A, the count of FCB extents still to look at.
4835
If the NZ FLAG is set (Register A is not 0, extents are left), LOOP BACK to 47DDH for the next extent at Register Pair HL.

Directory Entry Search
The granule is not in the FCB's extents: walk the FPDE and its FXDEs. Register Pair BC = the granules before the entry, Register Pair DE = the granules to go.

4837
LD BC,0000H 01 00 00
Load Register Pair BC with 0000H: no granules lie before the FPDE.
483A
POP DE D1
Restore Register Pair DE from the stack: the granule within the file (pushed at 47DAH), the granules still to go.
483B
GOSUB to 48F0H to read the file's FPDE (the FCB's DEC) and check that it is an FPDE in use belonging to this FCB. Register Pair HL returns the entry.
483E
If the NZ FLAG is set (Register A = the error), JUMP to 480CH, which goes on to 4817H to leave with it.
4840
LD A,(IX+07H) DD 7E 07
Load Register A with the FCB's DEC from (IX+07H) (Index Register IX = the FCB), the DEC of the FPDE.
4843
PUSH AF F5
Save Register Pair AF (Register A = the FPDE's DEC) onto the stack, so that 4844H finds it as it finds an FXDE's DEC from 4808H.
4844
POP AF F1
Restore Register Pair AF from the stack: Register A = the DEC of the entry about to be looked at (the FPDE, or the FXDE from 4808H).
4845
LD (480FH),A 32 0F 48
Store Register A (the entry's DEC) at 480FH, the operand of 480EH: the next FXDE's back link must name it, and SYS2/SYS takes it as the last entry when it allocates.

Entry Loop
Register Pair HL = the directory entry in 4300H.

4848
BIT 4,(HL) CB 66
Test bit 4 of the entry's byte 0 at Register Pair HL (in use). The Z FLAG is set if the entry is free.
484A
If the Z FLAG is set (the entry is not in use), JUMP to 4815H to leave with error 2CH.
484C
PUSH DE D5
Save Register Pair DE (the granules to go at the start of this entry) onto the stack.
484D
PUSH BC C5
Save Register Pair BC (the granules before this entry) onto the stack.
484E
LD A,L 7D
Load Register A with Register L, the low byte of the entry's address.
484F
ADD A,16H C6 16
ADD 16H (22) to Register A: the offset of the entry's first extent element.
4851
LD L,A 6F
Copy Register A into Register L: Register Pair HL = the entry's first extent element.
4852
PUSH HL E5
Save Register Pair HL (the entry's extent elements) onto the stack.

Element Loop

4853
LD A,(HL) 7E
Fetch the element's first byte at Register Pair HL into Register A: a lump number, FEH (an FXDE follows) or FFH (the end).
4854
CP FEH FE FE
Compare Register A against FEH. The CARRY FLAG is set for a lump number, the Z FLAG for FEH.
4856
INC HL 23
INCrement Register Pair HL to the element's second byte. INC HL does not change the flags.
4857
LD A,(HL) 7E
Fetch the second byte at Register Pair HL into Register A: the granule bits, or the FXDE's DEC after FEH.
4858
INC HL 23
INCrement Register Pair HL to the next element.
4859
If the CARRY FLAG is set (a real extent, Register A = its second byte), JUMP to 486BH.
485B
If the Z FLAG is set (FEH: Register A = the DEC of the FXDE that continues the file), JUMP to 4800H.
485D
LD A,3EH 3E 3E
FFH, the end of the file's elements: the granule is past the file's last granule. Load Register A with error code 3EH (CAN'T EXTEND FILE VIA READ).
485F
JUMP to 4861H to allocate. Self-Modifying Code
The offset at 4860H is stored by 47B5H: 00H here, or B6H for a read, which makes it a jump to 4817H to leave with Register A = 3EH.
4861
POP AF F1
Restore Register Pair AF from the stack to drop the entry's extent pointer (pushed at 4852H).
4862
LD A,64H 3E 64
Load Register A with 64H, the RST 28H code of SYS2/SYS function 3, which allocates granules to the file; it is called with Register Pair DE = the granules still to go and 480FH = the DEC of the file's last directory entry.
4864
GOSUB to 4982H to call SYS2/SYS function 3 with Register A = 64H. It returns with Register Pair HL = the directory entry to look through again, now holding the new granules, or leaves through 4972H with an error.
4867
POP BC C1
Restore Register Pair BC from the stack: the granules before this entry (pushed at 484DH).
4868
POP DE D1
Restore Register Pair DE from the stack: the granules to go at the start of this entry (pushed at 484CH).
4869
JUMP to 4848H to look through the entry at Register Pair HL again, now with the new granules.
486B
BIT 4,L CB 65
A real extent: test bit 4 of Register L. Register Pair HL now points past the element; bit 4 is clear only when it has gone past byte 31, so this element was the fifth (bytes 30-31), which may only hold FEH or FFH.
486D
If the Z FLAG is set (a real extent in the fifth element), JUMP to 4815H to leave with error 2CH.
486F
GOSUB to 488EH with Register A = the element's second byte: subtract its granule count from the granules to go in Register Pair DE. Register Pair HL returns the difference and Register Pair DE the element pointer.
4872
EX DE,HL EB
Exchange Register Pairs DE and HL: Register Pair DE = the granules still to go, Register Pair HL = the next element.
4873
If the NO CARRY FLAG is set (the granule lies beyond this element), LOOP BACK to 4853H for the next element at Register Pair HL.
4875
POP BC C1
Found in this entry. Restore Register Pair BC from the stack: the entry's first extent element (pushed at 4852H).
4876
POP DE D1
Restore Register Pair DE from the stack: the granules before this entry (pushed at 484DH as Register Pair BC).
4877
POP AF F1
Restore Register Pair AF from the stack to drop the granules to go (pushed at 484CH).
4878
POP HL E1
Restore Register Pair HL from the stack: FCB+14 (pushed at 47D9H).
4879
POP AF F1
Restore Register Pair AF from the stack to drop the sector within the granule (pushed at 47D8H); the search is redone from 47B8H.
487A
PUSH BC C5
Save Register Pair BC (the entry's extent elements) onto the stack.
487B
LD BC,0008H 01 08 00
Load Register Pair BC with 0008H: four elements of two bytes are copied into the FCB.
487E
LD A,D 7A
Load Register A with Register D, the high byte of the granules before this entry.
487F
OR E B3
OR Register E (the low byte) into Register A. The Z FLAG is set if no granules lie before the entry, so it is the FPDE.
4880
If the Z FLAG is set (the FPDE), JUMP to 4887H to copy its elements to FCB+14 (Register Pair HL).
4882
ADD HL,BC 09
ADD Register Pair BC (8) to Register Pair HL: Register Pair HL = FCB+22.
4883
LD (HL),E 73
Store Register E (the low byte of the granules before this entry) at Register Pair HL (FCB+22).
4884
INC HL 23
INCrement Register Pair HL to FCB+23, for the high byte of the count.
4885
LD (HL),D 72
Store Register D (the high byte of the granules before this entry) at Register Pair HL (FCB+23).
4886
INC HL 23
INCrement Register Pair HL to FCB+24, where the FXDE's elements go.
4887
EX DE,HL EB
Exchange Register Pairs DE and HL: Register Pair DE = the destination in the FCB (FCB+14 or FCB+24).
4888
POP HL E1
Restore Register Pair HL from the stack: the entry's extent elements (pushed at 487AH).
4889
LDIR ED B0
Copy Register Pair BC (8) bytes from Register Pair HL (the entry's elements) to Register Pair DE (the FCB).
488B
JUMP to 47B8H to look for the granule again, now in the FCB's extents.

488EH - Subtract an Extent's Granule Count

Entered with Register A = the extent's second byte, Register Pair DE = the granules to go and Register Pair HL = the extent pointer. Returns Register Pair BC = the extent's granule count, Register Pair HL = the granules to go minus that count, Register Pair DE = the extent pointer, and the CARRY FLAG set if the granule lies in this extent.

488E
AND 1FH E6 1F
AND Register A (the extent's second byte) with 1FH, keeping bits 0-4, the number of granules minus 1. This also clears the CARRY FLAG for the SBC below.
4890
LD C,A 4F
Copy Register A (the count minus 1) into Register C.
4891
LD B,00H 06 00
Load Register B with 00H: Register Pair BC = the count minus 1.
4893
INC BC 03
INCrement Register Pair BC: the extent's granule count (1-32).
4894
EX DE,HL EB
Exchange Register Pairs DE and HL: Register Pair HL = the granules to go, Register Pair DE = the extent pointer.
4895
SBC HL,BC ED 42
SUBtract Register Pair BC (the extent's granule count) from Register Pair HL (the granules to go). The CARRY FLAG is set if the granule lies inside this extent.
4897
RET C9
RETurn with Register Pair HL = the difference and the CARRY FLAG set for a granule in this extent.

4898H - Read a Directory Sector

Enter at 48AFH to read directory sector A (0 = GAT, 1 = HIT, 2 and up = FDE sectors) of the current drive into 4300H, or at 48A2H for one attempt. Directory sectors carry the directory mark, so a good read returns controller error 06H; a sector without it gives error 31H (WRONG DISKETTE RECORD TYPE). If the read fails, the boot sector is read to find where the directory is (byte 2, the directory's lump), the drive's PDRIVE record is updated and the read is tried again. On exit the Z FLAG is set, or the NZ FLAG with error 11H (DIRECTORY READ ERROR). Register Pairs DE and BC are preserved.

4898
LD A,(4302H) 3A 02 43
Fetch byte 2 of the boot sector now in the buffer, at 4302H, into Register A: the lump where the directory starts (DDSL).
489B
LD HL,(42B9H) 2A B9 42
Load Register Pair HL with the address of the current drive's PDRIVE record from 42B9H.
489E
LD (HL),A 77
Store Register A (the directory lump from the boot sector) at Register Pair HL: byte 0 of the drive's PDRIVE record in 4291H-42B8H.
489F
LD A,(48D5H) 3A D5 48
Load Register A with the directory sector wanted from 48D5H (stored by 4C19H on the first attempt).
48A2
GOSUB to 4C19H with Register A = the directory sector: it stores Register A at 48D5H and returns Register Pair DE = its sector number on the diskette and Register Pair HL = 4300H.
48A5
GOSUB to 45DBH to read sector Register Pair DE into the buffer at Register Pair HL (4300H). Register A returns the error code with the NZ FLAG, or 00H with the Z FLAG.
48A8
If the NZ FLAG is set (an error code in Register A), JUMP to 48ACH to see whether it is the directory mark. 4A79H also jumps here after a verify read.
48AA
OR 31H F6 31
The read had no error, so the sector has no directory mark: OR 31H into Register A (00H), giving error 31H (WRONG DISKETTE RECORD TYPE) and the NZ FLAG.
48AC
CP 06H FE 06
Compare Register A (the error code) against 06H, the error a read returns for a sector with the directory mark. The Z FLAG is set for a good directory read.
48AE
RET C9
RETurn: the Z FLAG for a directory sector read correctly, or the NZ FLAG with Register A = the error code.
48AF
PUSH DE D5
Save Register Pair DE (the caller's) onto the stack.
48B0
PUSH BC C5
Save Register Pair BC (the caller's) onto the stack.
48B1
GOSUB to 48A2H to read directory sector Register A into 4300H.
48B4
If the Z FLAG is set (the directory sector was read), JUMP to 48C1H to restore the registers and return.
48B6
LD DE,0000H 11 00 00
Load Register Pair DE with 0000H, sector 0 of the diskette: the boot sector. 4EF0H also jumps here.
48B9
GOSUB to 45DBH to read the boot sector into the buffer at Register Pair HL (4300H, from 4C19H).
48BC
If the Z FLAG is set (the boot sector was read), GOSUB to 4898H to take the directory's lump from its byte 2 into the PDRIVE record and read the directory sector (48D5H) again.
48BF
LD A,11H 3E 11
Load Register A with error code 11H (DIRECTORY READ ERROR). LD does not change the flags: the Z FLAG from 48BCH's read means success and the code is ignored.
48C1
POP BC C1
Restore Register Pair BC from the stack (saved at 48B0H or 48C8H).
48C2
POP DE D1
Restore Register Pair DE from the stack (saved at 48AFH or 48C7H).
48C3
RET C9
RETurn: the Z FLAG for success, or the NZ FLAG with Register A = 11H (or 12H from 48D0H).

48C4H - Write a Directory Sector

Enter at 48C4H to write back the directory sector now in 4300H, or at 48C7H with its number in Register A. The sector is written with the directory mark and the verify read. On exit the Z FLAG is set, or the NZ FLAG with error 12H (DIRECTORY WRITE ERROR).

48C4
LD A,(48D5H) 3A D5 48
Load Register A with the number of the directory sector now in 4300H, from 48D5H.
48C7
PUSH DE D5
Save Register Pair DE (the caller's) onto the stack.
48C8
PUSH BC C5
Save Register Pair BC (the caller's) onto the stack.
48C9
GOSUB to 4C19H with Register A = the directory sector: Register Pair DE = its sector number, Register Pair HL = 4300H.
48CC
OR H B4
OR Register H (43H, from Register Pair HL = 4300H) into Register A: the NZ FLAG is set, which tells 4A5DH to write with the directory mark.
48CD
GOSUB to 4A5DH to write the buffer at Register Pair HL (4300H) to sector Register Pair DE with the directory mark, verify it, and retry as option AW allows.
48D0
LD A,12H 3E 12
Load Register A with error code 12H (DIRECTORY WRITE ERROR). LD does not change the flags of the write.
48D2
JUMP to 48C1H to restore Register Pairs BC and DE and return.

48D4H - Read the Directory Sector of an Entry

Register A holds a DEC (bits 0-4 the FDE sector minus 2, bits 5-7 the entry in the sector). 48D4H reads the sector only if it is not already in 4300H, 48DBH always reads it, 48D8H always reads the sector of the FCB's DEC (FCB+7). On exit Register Pair HL points to the entry, Register A = its offset in the sector, and the Z FLAG is set.

48D4
LD L,FFH 2E FF
Load Register L with the number of the directory sector now in 4300H. Self-Modifying Code
The operand at 48D5H is stored by 4C19H every time a directory sector is read or written; FFH for none.
48D6
JUMP to 48DDH with Register L = the sector in 4300H.
48D8
LD A,(IX+07H) DD 7E 07
Load Register A with the FCB's DEC from (IX+07H), byte +7 of the FCB at Index Register IX.
48DB
LD L,FFH 2E FF
Load Register L with FFH, a sector number that never matches, so the sector is always read.
48DD
PUSH AF F5
Save Register Pair AF (Register A = the DEC) onto the stack.
48DE
AND 1FH E6 1F
AND Register A (the DEC) with 1FH, keeping bits 0-4: the FDE sector minus 2.
48E0
INC A 3C
INCrement Register A (the FDE sector minus 2) by 1.
48E1
INC A 3C
INCrement Register A again: Register A = the directory sector (2 and up).
48E2
CP L BD
Compare Register A (the directory sector wanted) against Register L (the sector in 4300H). The Z FLAG is set if it is already there.
48E3
If the NZ FLAG is set (not in the buffer), GOSUB to 48AFH to read directory sector Register A into 4300H.
48E6
POP HL E1
Restore Register Pair HL from the stack: Register H = the DEC (pushed as Register A at 48DDH). POP does not change the flags.
48E7
RET NZ C0
If the NZ FLAG is set (the read failed, Register A = 11H), RETurn with the error.
48E8
LD A,E0H 3E E0
Load Register A with E0H, the mask for bits 5-7 of the DEC.
48EA
AND H A4
AND Register H (the DEC) into Register A: bits 5-7 = the entry number x 32, which is the entry's offset in the sector.
48EB
LD L,A 6F
Copy Register A (the entry's offset) into Register L.
48EC
LD H,43H 26 43
Load Register H with 43H: Register Pair HL = 4300H + the offset, the entry in the directory sector buffer.
48EE
CP A BF
Compare Register A with itself to set the Z FLAG (success).
48EF
RET C9
RETurn with Register Pair HL = the entry, Register A = its offset in the sector and the Z FLAG set.

48F0H - Read the FPDE of an Open FCB

Selects the FCB's drive and reads the directory entry of FCB+7. The entry must still belong to the file: it must be an FPDE in use, and its first extent must match the FCB's (unless the FCB has none yet). On exit Register Pair HL points to the entry and the Z FLAG is set, or the NZ FLAG with Register A = the error (2DH BAD FCB DATA).

48F0
GOSUB to 471BH to select the drive of the FCB at Index Register IX, (IX+06H).
48F3
If the Z FLAG is set (the drive is selected), GOSUB to 48D8H to read the directory sector of the FCB's DEC: Register Pair HL = the entry, Register A = its offset in the sector.
48F6
RET NZ C0
If the NZ FLAG is set (Register A = the error from the select or the read), RETurn with it.
48F7
PUSH HL E5
Save Register Pair HL (the entry) onto the stack.
48F8
ADD A,16H C6 16
ADD 16H (22) to Register A (the entry's offset): the offset of its first extent element.
48FA
LD L,A 6F
Copy Register A into Register L: Register Pair HL = the entry's first extent element.
48FB
LD A,(IX+0EH) DD 7E 0E
Load Register A with the lump of the FCB's first extent from (IX+0EH) (Index Register IX = the FCB), FCB+14.
48FE
CP (HL) BE
Compare Register A (the FCB's first lump) against the entry's first lump at Register Pair HL. The Z FLAG is set if they agree.
48FF
POP HL E1
Restore Register Pair HL from the stack: the entry. POP does not change the flags.
4900
If the Z FLAG is set (the first extents agree), JUMP to 4905H to check the entry's flags.
4902
INC A 3C
INCrement Register A (the FCB's first lump). The Z FLAG is set if it was FFH, an FCB that has no extent yet.
4903
If the NZ FLAG is set (the FCB has an extent that the entry does not have), JUMP to 490AH to return error 2DH.
4905
LD A,(HL) 7E
Fetch the entry's flag byte (byte 0) at Register Pair HL into Register A.
4906
AND 90H E6 90
AND Register A with 90H, keeping bit 7 (FXDE) and bit 4 (in use).
4908
CP 10H FE 10
Compare Register A against 10H. The Z FLAG is set only for an FPDE in use.
490A
LD A,2DH 3E 2D
Load Register A with error code 2DH (BAD FCB DATA). LD does not change the flags.
490C
RET C9
RETurn: the Z FLAG with Register Pair HL = the FPDE, or the NZ FLAG with Register A = 2DH.

490DH - Compare NEXT with EOF

Loads Register C with NEXT's low byte and Register Pair HL with NEXT's middle and high bytes, and compares NEXT with EOF, highest byte first. The CARRY FLAG is set if NEXT is below EOF and the Z FLAG if they are equal.

490D
LD C,(IX+05H) DD 4E 05
Load Register C with NEXT's low byte from (IX+05H) (Index Register IX = the FCB), FCB+5 (the byte position in the sector).
4910
LD L,(IX+0AH) DD 6E 0A
Load Register L with NEXT's middle byte from (IX+0AH) (Index Register IX = the FCB), FCB+10.
4913
LD H,(IX+0BH) DD 66 0B
Load Register H with NEXT's high byte from (IX+0BH) (Index Register IX = the FCB), FCB+11. Register Pair HL = the sector of the file NEXT is in.
4916
LD A,H 7C
Load Register A with Register H, NEXT's high byte.
4917
CP (IX+0DH) DD BE 0D
Compare Register A (NEXT high) against EOF's high byte at (IX+0DH) (Index Register IX = the FCB), FCB+13. The CARRY FLAG is set if NEXT is lower.
491A
RET NZ C0
If the NZ FLAG is set (the high bytes differ), RETurn: the CARRY FLAG already gives the answer.
491B
LD A,L 7D
Load Register A with Register L, NEXT's middle byte.
491C
CP (IX+0CH) DD BE 0C
Compare Register A (NEXT middle) against EOF's middle byte at (IX+0CH) (Index Register IX = the FCB), FCB+12.
491F
RET NZ C0
If the NZ FLAG is set (the middle bytes differ), RETurn with the CARRY FLAG giving the answer.
4920
LD A,C 79
Load Register A with Register C, NEXT's low byte.
4921
CP (IX+08H) DD BE 08
Compare Register A (NEXT low) against EOF's low byte at (IX+08H) (Index Register IX = the FCB), FCB+8.
4924
RET C9
RETurn: the CARRY FLAG if NEXT is below EOF, the Z FLAG if NEXT equals EOF.

4925H - Run the Rest of a Routine as a DOS Function

Every FCB routine starts with CALL 4925H, Register Pair DE pointing to the FCB. If the FCB is not open, the routine returns at once with error 26H (FILE NOT OPEN). Otherwise the code after the CALL runs as a subroutine with Index Register IX = the FCB and Index Register IY = 4280H, and the Stack Pointer is saved at 4973H so that any error can leave through 4972H from any depth. On return Register A and the flags hold the result, Register Pairs BC, DE, HL, AF', IX and IY are as they were on entry, and the return goes to the caller of the routine.

4925
LD A,(DE) 1A
Fetch FCB+0 from the FCB at Register Pair DE into Register A (bit 7 = the file is open).
4926
RLCA 07
Rotate Register A (FCB+0) left: bit 7 (open) goes into the CARRY FLAG.
4927
LD A,26H 3E 26
Load Register A with error code 26H (FILE NOT OPEN). LD does not change the CARRY FLAG.
4929
If the NO CARRY FLAG is set (the FCB is not open), JUMP to 492CH with Register A = 26H.
492B
XOR A AF
The FCB is open: set Register A to 00H, the go-ahead kept in AF' below.
492C
EX AF,AF' 08
Exchange Register Pair AF with the alternate AF': AF' now keeps 00H or 26H, and Register Pair AF holds the caller's AF' (saved at 4937H and restored at the end).
492D
EX (SP),HL E3
Exchange Register Pair HL with the word on top of the stack (the Stack Pointer points to it): Register Pair HL = the return address of the CALL 4925H, the code to run; the stack now holds the caller's HL.
492E
LD (494BH),HL 22 4B 49
Store Register Pair HL (the address of the code after the CALL 4925H) at 494BH, the operand of the CALL Z at 494AH.
4931
POP HL E1
Restore Register Pair HL from the stack: the caller's HL. The top of the stack is now the return address into the caller of the routine.
4932
PUSH IY FD E5
Save Index Register IY (the caller's) onto the stack.
4934
PUSH HL E5
Save Register Pair HL (the caller's) onto the stack.
4935
PUSH DE D5
Save Register Pair DE (the FCB address) onto the stack.
4936
PUSH BC C5
Save Register Pair BC (the caller's) onto the stack.
4937
PUSH AF F5
Save Register Pair AF (the caller's AF', from 492CH) onto the stack.
4938
PUSH DE D5
Save Register Pair DE (the FCB address) onto the stack once more, for the exchange with IX.
4939
EX (SP),IX DD E3
Exchange Index Register IX with the word on top of the stack (the Stack Pointer's target): Index Register IX = the FCB, and the caller's IX is saved there.
493B
PUSH HL E5
Save Register Pair HL (the caller's) onto the stack for a moment.
493C
LD HL,(4973H) 2A 73 49
Load Register Pair HL with the Stack Pointer saved at 4973H by an outer DOS function (a DOS function can call another), to be kept on the stack.
493F
EX (SP),HL E3
Exchange Register Pair HL with the word on top of the stack (the Stack Pointer's target): the outer saved Stack Pointer goes onto the stack and Register Pair HL is the caller's HL again.
4940
LD (4973H),SP ED 73 73 49
Store the Stack Pointer at 4973H, the operand of the LD SP at 4972H: an error exit restores this stack level, where the outer saved Stack Pointer lies.
4944
LD IY,4280H FD 21 80 42
Load Index Register IY with 4280H, the base the DOS functions use for IY+d addressing of the variables around 4280H.
4948
EX AF,AF' 08
Exchange Register Pair AF with the alternate AF': Register A = 00H (open) or 26H (not open).
4949
OR A B7
OR Register A with itself. The Z FLAG is set if the FCB is open; the CARRY FLAG is cleared.
494A
If the Z FLAG is set (the FCB is open), GOSUB to the code after the CALL 4925H (0000H in the file). Self-Modifying Code
The address at 494BH-494CH is stored by 492EH. The code runs with Register Pairs BC, DE and HL as the caller passed them and returns its result in Register A and the flags.
494D
POP HL E1
Restore Register Pair HL from the stack: the outer DOS function's saved Stack Pointer (pushed at 493FH). 4976H comes here after an error, with the Stack Pointer reset to this level.
494E
LD (4973H),HL 22 73 49
Store Register Pair HL back at 4973H, so that an error in the outer DOS function still finds its own level.
4951
EX AF,AF' 08
Exchange Register Pair AF with the alternate AF': the result (Register A and the flags) is kept in AF'.
4952
POP IX DD E1
Restore Index Register IX from the stack: the caller's IX (saved at 4939H).
4954
POP AF F1
Restore Register Pair AF from the stack: the caller's AF' (pushed at 4937H).
4955
POP BC C1
Restore Register Pair BC from the stack: the caller's BC.
4956
POP DE D1
Restore Register Pair DE from the stack: the FCB address the caller passed.
4957
POP HL E1
Restore Register Pair HL from the stack: the caller's HL.
4958
POP IY FD E1
Restore Index Register IY from the stack: the caller's IY.
495A
EX AF,AF' 08
Exchange Register Pair AF with the alternate AF': Register A and the flags = the result, and AF' = the caller's AF' again.
495B
RET C9
RETurn to the caller of the routine that did CALL 4925H, with Register A = 00H and the Z FLAG, or the error code with the NZ FLAG.

495CH - Check the Access Level

The access level (FCB+1 bits 0-2) must be below Register B: 5 for a write (entry 495CH), 6 for a read (entry 495EH with Register B = 6). Otherwise the DOS function ends with error 25H (ILLEGAL ACCESS TRIED TO PROTECTED FILE). 4969H checks NEXT against EOF.

495C
LD B,05H 06 05
Load Register B with 05H: writing needs an access level below 5 (FULL, KILL, RENAME or WRITE).
495E
LD A,(IX+01H) DD 7E 01
Fetch FCB+1 from (IX+01H) (Index Register IX = the FCB) into Register A; bits 0-2 are the access level the file was opened with.
4961
AND 07H E6 07
AND Register A with 07H, keeping the access level (0 FULL, 1 KILL, 2 RENAME, 4 WRITE, 5 READ, 6 EXEC, 7 LOCK).
4963
CP B B8
Compare Register A (the access level) against Register B (5 or 6). The CARRY FLAG is set if it is lower, so the access is allowed.
4964
RET C D8
If the CARRY FLAG is set (allowed), RETurn to the read or write routine.
4965
LD A,25H 3E 25
Load Register A with error code 25H (ILLEGAL ACCESS TRIED TO PROTECTED FILE).
4967
JUMP to 4972H to leave the DOS function with Register A = 25H.
4969
GOSUB to 490DH to compare NEXT with EOF in the FCB at Index Register IX: the CARRY FLAG if NEXT is below EOF, the Z FLAG if equal.
496C
RET C D8
If the CARRY FLAG is set (NEXT is below EOF), RETurn: the position can be read.
496D
LD A,1CH 3E 1C
Load Register A with error code 1CH (END OF FILE ENCOUNTERED). LD does not change the flags.
496F
If the Z FLAG is set (NEXT equals EOF), JUMP to 4972H to leave with Register A = 1CH.
4971
INC A 3C
NEXT is past EOF: INCrement Register A to 1DH (PAST END OF FILE) and fall into 4972H.

4972H - Leave a DOS Function with an Error

Register A holds the error code. The Stack Pointer saved by 4925H is restored, which throws away everything the function pushed, and the function returns to its caller with the NZ FLAG set. SYS2/SYS and SYS3/SYS jump here too. 4978H-4982H serve the overlays that call another overlay and come back.

4972
LD SP,0000H 31 00 00
Load the Stack Pointer with the value saved by 4925H. Self-Modifying Code
The operand at 4973H-4974H is stored by 4940H (and put back by 494EH); the file has 0000H.
4975
OR A B7
OR Register A (the error code, not 0) with itself to set the NZ FLAG.
4976
JUMP to 494DH to restore the registers and return from the DOS function with Register A = the error code.
4978
If the NZ FLAG is set, JUMP to 4409H to leave through the DOS error exit with Register A = the error. 4978H is a return address: SYS1/SYS, SYS7/SYS, SYS9/SYS and SYS14/SYS push a word holding an RST 28H code (high byte) and a sub-function (low byte), then 4978H, and jump to a function; when it returns here without an error, the word on the stack names the function to run next.
497B
XOR A AF
Set Register A to 00H and set the Z FLAG. 497BH is the return address for callers that do not want the error test (SYS1/SYS).
497C
If the NZ FLAG is set, JUMP to 4972H to leave the current DOS function with Register A = the error. 497CH is the return address used inside a DOS function (SYS3/SYS after 4A07H).
497E
EX (SP),HL E3
Exchange Register Pair HL with the word on top of the stack (the Stack Pointer's target): Register Pair HL = the word pushed by the overlay (RST 28H code and sub-function), and the caller's HL is put in its place.
497F
LD A,H 7C
Load Register A with Register H, the RST 28H code of the function to run next.
4980
LD C,L 4D
Load Register C with Register L, its sub-function.
4981
POP HL E1
Restore Register Pair HL from the stack: the caller's HL. The word is gone from the stack.
4982
RST 28H EF
RST 28H with Register A = the function code and Register C = the sub-function: call the overlay function through the dispatcher at 4B67H. Because the dispatcher throws away the RST's return address, CALL 4982H works as a call of an overlay function that returns to the caller; SYS0/SYS (44F4H, 4864H), SYS6/SYS and SYS9/SYS use it that way.

4983H - Move a Logical Record

Called at the start of the READ (with the CARRY FLAG set) and WRITE (CARRY FLAG clear) routines. In sector mode (FCB+1 bit 7 clear) it returns at once. In record mode it replaces the rest of the routine: LRL bytes (0 = 256) are moved one by one between the caller's buffer at Register Pair HL and the file, through the byte routine at 4A92H, and the DOS function returns from here.

4983
BIT 7,(IX+01H) DD CB 01 7E
Test bit 7 of FCB+1 at (IX+01H) (Index Register IX = the FCB): record mode. The Z FLAG is set in sector mode. BIT does not change the CARRY FLAG (read or write).
4987
RET Z C8
If the Z FLAG is set (sector mode), RETurn to the READ or WRITE routine.
4988
POP DE D1
Restore Register Pair DE from the stack to drop the return address into the READ or WRITE routine: the record loop below returns from the DOS function itself (to 494DH).
4989
LD B,(IX+09H) DD 46 09
Load Register B with the logical record length from (IX+09H) (Index Register IX = the FCB), FCB+9 (00H = 256 bytes through the DJNZ).

Record Loop
Register Pair HL = the caller's record buffer, Register B = bytes left, the CARRY FLAG (in Register Pair AF) = read.

498C
PUSH AF F5
Save Register Pair AF onto the stack: the CARRY FLAG says read (set) or write (clear).
498D
PUSH HL E5
Save Register Pair HL (the position in the caller's record buffer) onto the stack.
498E
PUSH BC C5
Save Register Pair BC (Register B = bytes left) onto the stack.
498F
LD C,(HL) 4E
Load Register C with the byte at Register Pair HL in the caller's buffer, the byte to write (ignored when reading).
4990
GOSUB to 4A92H to read a byte from the file into Register A (CARRY FLAG set) or write Register C to it (CARRY FLAG clear).
4993
If the NZ FLAG is set (an error, Register A = the code), JUMP to 4972H to leave the DOS function with it.
4995
LD E,A 5F
Copy Register A (the byte read) into Register E.
4996
POP BC C1
Restore Register Pair BC from the stack: Register B = bytes left.
4997
POP HL E1
Restore Register Pair HL from the stack: the position in the caller's buffer.
4998
POP AF F1
Restore Register Pair AF from the stack: the CARRY FLAG = read or write.
4999
If the NO CARRY FLAG is set (writing), JUMP to 499CH: nothing to store.
499B
LD (HL),E 73
Reading: store Register E (the byte read) in the caller's buffer at Register Pair HL.
499C
INC HL 23
INCrement Register Pair HL to the next byte of the caller's buffer.
499D
DECrement Register B (bytes left) and, if it is not 0, LOOP BACK to 498CH.
499F
XOR A AF
The record is done: set Register A to 00H and the Z FLAG.
49A0
RET C9
RETurn from the DOS function (to 494DH) with Register A = 00H, success.

49A1H - READ

4436H comes here (manual 3.19). In sector mode the sector at NEXT is read into the FCB's buffer and NEXT is advanced by 256, also after error 06H (the sector has the directory mark), which is still returned.

49A1
GOSUB to 4925H: the rest runs as a DOS function with Index Register IX = the FCB at Register Pair DE.
49A4
SCF 37
Set the CARRY FLAG: this is a read, for 4983H.
49A5
GOSUB to 4983H: in record mode it reads LRL bytes into the buffer at Register Pair HL and returns from the DOS function; in sector mode it returns here.
49A8
GOSUB to 49BEH to read the sector at NEXT into the FCB's buffer. Register A returns the error code, the Z FLAG for success.
49AB
If the Z FLAG is set (read without error), JUMP to 49B0H to advance NEXT.
49AD
CP 06H FE 06
Compare Register A (the error code) against 06H, the error a sector with the directory mark gives.
49AF
RET NZ C0
If the NZ FLAG is set (any other error), RETurn with it in Register A; NEXT is not advanced.
49B0
INC (IX+0AH) DD 34 0A
INCrement NEXT's middle byte at (IX+0AH) (Index Register IX = the FCB), FCB+10: NEXT + 256, the next sector. The Z FLAG is set if it went round to 0.
49B3
If the NZ FLAG is set (no carry out of the middle byte), JUMP to 49B8H.
49B5
INC (IX+0BH) DD 34 0B
INCrement NEXT's high byte at (IX+0BH) (Index Register IX = the FCB), FCB+11.
49B8
SET 5,(IX+01H) DD CB 01 EE
Set bit 5 of FCB+1 at (IX+01H) (Index Register IX = the FCB): the buffer does not hold the sector at NEXT.
49BC
OR A B7
OR Register A with itself: the Z FLAG for 00H (success), the NZ FLAG for 06H (the directory mark).
49BD
RET C9
RETurn from the DOS function with Register A = 00H or 06H.
49BE
LD B,06H 06 06
Read the sector at NEXT into the FCB's buffer. Load Register B with 06H: reading needs an access level below 6.
49C0
GOSUB to 495EH to check the FCB's access level against Register B (6); error 25H leaves the DOS function.
49C3
GOSUB to 4969H: NEXT must be below EOF, else errors 1CH or 1DH leave the DOS function.
49C6
LD A,B6H 3E B6
Load Register A with B6H, for 47B5H: a sector past the file's last granule gives error 3EH instead of an allocation. 4A49H enters here.
49C8
GOSUB to 47B5H to find the sector at NEXT: Register Pair DE = its number on the diskette, Register Pair HL = the FCB's buffer.
49CB
GOSUB to 45DBH to read sector Register Pair DE into the buffer at Register Pair HL. Register A and the flags give the result.
49CE
RES 5,(IX+01H) DD CB 01 AE
Clear bit 5 of FCB+1 at (IX+01H) (Index Register IX = the FCB): the buffer holds the sector at NEXT. RES does not change the flags. 4A4EH calls here.
49D2
RES 4,(IX+01H) DD CB 01 A6
Clear bit 4 of FCB+1 at (IX+01H) (Index Register IX = the FCB): the buffer has no changes waiting to be written.
49D6
RET C9
RETurn with the read's result in Register A and the flags (or the Z FLAG with Register A = 00H from 4A3BH).

49D7H - WRITE

443CH (write with verify, manual 3.21) enters at 49D7H and 4439H (WRITE, manual 3.20) at 49DBH. In sector mode the FCB's buffer is written to the sector at NEXT, NEXT is advanced by 256 when its low byte is 0, and EOF is set to NEXT if NEXT is past EOF or if FCB+1 bit 6 is clear.

49D7
LD A,F6H 3E F6
Write with verify: load Register A with F6H, the opcode of OR n.
49D9
JUMP to 49DDH to store the opcode in Register A.
49DB
LD A,E6H 3E E6
Write: load Register A with E6H, the opcode of AND n.
49DD
LD (4A30H),A 32 30 4A
Store Register A (the opcode) at 4A30H: with OR 90H every write is verified, with AND 90H only those in record mode or with changes from the byte routine.
49E0
GOSUB to 4925H: the rest runs as a DOS function with Index Register IX = the FCB at Register Pair DE.
49E3
GOSUB to 4983H with the CARRY FLAG clear (from 4949H): in record mode it writes LRL bytes from the buffer at Register Pair HL and returns from the DOS function.
49E6
GOSUB to 4A0CH to write the FCB's buffer to the sector at NEXT. 4A59H jumps here when the byte routine has filled the buffer.
49E9
RET NZ C0
If the NZ FLAG is set (Register A = the error code), RETurn with it.
49EA
LD A,(IX+05H) DD 7E 05
Fetch NEXT's low byte from (IX+05H) (Index Register IX = the FCB), FCB+5, into Register A.
49ED
OR A B7
OR Register A with itself. The Z FLAG is set if NEXT is at the start of a sector.
49EE
If the Z FLAG is set (Register A = 00H), GOSUB to 49B0H to advance NEXT by 256 and mark the buffer as not holding NEXT.
49F1
GOSUB to 490DH to compare NEXT with EOF: Register C, Register Pair HL = NEXT; the CARRY FLAG if NEXT is below EOF. 4A5BH jumps here.
49F4
If the NO CARRY FLAG is set (NEXT is at or past EOF), JUMP to 49FCH to set EOF = NEXT.
49F6
BIT 6,(IX+01H) DD CB 01 76
NEXT is below EOF: test bit 6 of FCB+1 at (IX+01H) (Index Register IX = the FCB), set when EOF may only move forward.
49FA
If the NZ FLAG is set (EOF only moves forward), JUMP to 4A05H and keep the EOF.
49FC
LD (IX+08H),C DD 71 08
Store Register C (NEXT's low byte) at (IX+08H) (Index Register IX = the FCB), FCB+8, EOF's low byte.
49FF
LD (IX+0CH),L DD 75 0C
Store Register L (NEXT's middle byte) at (IX+0CH) (Index Register IX = the FCB), FCB+12.
4A02
LD (IX+0DH),H DD 74 0D
Store Register H (NEXT's high byte) at (IX+0DH) (Index Register IX = the FCB), FCB+13. EOF now equals NEXT.
4A05
XOR A AF
Set Register A to 00H and the Z FLAG: success.
4A06
RET C9
RETurn from the DOS function (or to 4A3DH's caller) with Register A = 00H.
4A07
BIT 4,(IX+01H) DD CB 01 66
Write the buffer only if it has changes waiting: test bit 4 of FCB+1 at (IX+01H) (Index Register IX = the FCB). Used before the FCB moves to another sector (4AA5H, 4B37H).
4A0B
RET Z C8
If the Z FLAG is set (no changes waiting), RETurn with the Z FLAG set.
4A0C
GOSUB to 47B1H to check the access level and find the sector at NEXT, allocating it if needed: Register Pair DE = its number on the diskette, Register Pair HL = the buffer.
4A0F
BIT 5,(IX+02H) DD CB 02 6E
Test bit 5 of FCB+2 at (IX+02H) (Index Register IX = the FCB): set once this FCB has marked the file's FPDE as updated.
4A13
If the NZ FLAG is set (already marked), JUMP to 4A2DH to write the sector.
4A15
BIT 1,(IX+00H) DD CB 00 4E
Test bit 1 of FCB+0 at (IX+00H) (Index Register IX = the FCB): NEXT is a sector of the whole diskette, so there is no directory entry to mark.
4A19
If the NZ FLAG is set (whole-diskette sectors), JUMP to 4A2DH.
4A1B
PUSH HL E5
Save Register Pair HL (the buffer address) onto the stack.
4A1C
GOSUB to 48F0H to read the file's FPDE into 4300H: Register Pair HL = the entry, the Z FLAG if it is good. Register Pair DE (the sector) is kept.
4A1F
If the NZ FLAG is set (Register A = the error), JUMP to 4A24H, where the CALL Z is skipped and the error is returned.
4A21
INC HL 23
INCrement Register Pair HL to the FPDE's byte 1.
4A22
SET 5,(HL) CB EE
Set bit 5 of the FPDE's byte 1 at Register Pair HL: the file has been updated since the flag was last cleared.
4A24
If the Z FLAG is set, GOSUB to 48C4H to write the directory sector in 4300H back with the directory mark.
4A27
POP HL E1
Restore Register Pair HL from the stack: the buffer address.
4A28
RET NZ C0
If the NZ FLAG is set (Register A = the error from 48F0H or 48C4H), RETurn with it.
4A29
SET 5,(IX+02H) DD CB 02 EE
Set bit 5 of FCB+2 at (IX+02H) (Index Register IX = the FCB): this FCB has marked the FPDE, so the directory is not written again for each sector.
4A2D
LD A,(IX+01H) DD 7E 01
Fetch FCB+1 from (IX+01H) (Index Register IX = the FCB) into Register A. Self-Modifying Code
SYS3/SYS rewrites these three bytes (4DA2H-4DB3H of its code): NOP / LD A,FFH makes every write verified, DD 7E 01 puts this instruction back.
4A30
AND 90H E6 90
AND Register A (FCB+1) with 90H, keeping bit 7 (record mode) and bit 4 (changes from the byte routine): those writes are verified. Self-Modifying Code
The opcode at 4A30H is stored at 49DDH: for 443CH it is OR 90H, which always gives a non-zero Register A, so the write is always verified.
4A32
BIT 0,(IX+00H) DD CB 00 46
Test bit 0 of FCB+0 at (IX+00H) (Index Register IX = the FCB): the NZ FLAG asks for the directory mark on this write.
4A36
GOSUB to 4A5DH to write the buffer at Register Pair HL to sector Register Pair DE (Register A not 0 = verify, the NZ FLAG = directory mark).
4A39
RET NZ C0
If the NZ FLAG is set (Register A = the error), RETurn with it.
4A3A
XOR A AF
Set Register A to 00H: success, the sector is written.
4A3B
JUMP to 49D2H to clear FCB+1 bit 4 (no changes waiting) and return with Register A = 00H.

4A3DH - Write a Byte to a File

The write half of the byte routine at 4A92H, entered with the byte in Register C and the Z FLAG set if the FCB's buffer holds the sector at NEXT. The sector is read in first unless NEXT is at EOF on a sector boundary. The byte is stored at NEXT and NEXT is advanced; when the buffer is full it is written, and EOF is brought up to NEXT.

4A3D
If the Z FLAG is set (the buffer holds the sector at NEXT), JUMP to 4A51H to store the byte in Register C.
4A3F
PUSH BC C5
Save Register Pair BC (Register C = the byte to write) onto the stack.
4A40
GOSUB to 47B1H to check the access level and find (or allocate) the sector at NEXT: Register Pair DE = its number, Register Pair HL = the buffer.
4A43
GOSUB to 490DH to compare NEXT with EOF. When they are equal Register A = NEXT's low byte and the Z FLAG is set.
4A46
If the NZ FLAG is set (NEXT is not at EOF), JUMP to 4A49H to read the sector before changing it.
4A48
OR A B7
NEXT is at EOF: OR Register A (NEXT's low byte) with itself. The Z FLAG is set when NEXT is at the start of a sector, which then holds nothing yet.
4A49
If the NZ FLAG is set, GOSUB to 49C6H to read the sector at NEXT into the buffer (without the access and EOF tests).
4A4C
POP BC C1
Restore Register Pair BC from the stack: Register C = the byte to write.
4A4D
RET NZ C0
If the NZ FLAG is set (Register A = a read error), RETurn with it.
4A4E
GOSUB to 49CEH to clear FCB+1 bits 5 and 4: the buffer holds the sector at NEXT, with no changes yet.
4A51
GOSUB to 4AB0H: Register Pair HL = the byte's place in the buffer, and NEXT's low byte is counted up. The Z FLAG is set if it went round to 0.
4A54
LD (HL),C 71
Store Register C (the byte) in the buffer at Register Pair HL.
4A55
SET 4,(IX+01H) DD CB 01 E6
Set bit 4 of FCB+1 at (IX+01H) (Index Register IX = the FCB): the buffer has changes waiting.
4A59
If the Z FLAG is set (NEXT's low byte went round: the buffer is full), JUMP to 49E6H to write it, advance NEXT and set EOF.
4A5B
JUMP to 49F1H to bring EOF up to NEXT if needed.

4A5DH - Write a Sector, Verify and Retry

Register Pair DE = the sector number, Register Pair HL = the buffer. Register A not zero asks for a verify read after the write, and the NZ FLAG asks for the directory mark. When the verify read fails, the write is repeated up to SYSTEM option AW times. On exit the Z FLAG is set or Register A holds the error.

4A5D
LD C,A 4F
Copy Register A (not 0 = verify) into Register C. LD does not change the flags.
4A5E
LD B,03H 06 03
Load Register B with the number of write tries. Self-Modifying Code
The operand at 4A5FH is SYSTEM option AW, stored by the reset (4D80H) from byte A1H of the system data sector; the file has 03H.
4A60
If the NZ FLAG is set (the directory mark is wanted), JUMP to 4A6FH.

Write Loop (Normal Mark)

4A62
GOSUB to 45EBH to write the buffer at Register Pair HL to sector Register Pair DE with the normal data mark.
4A65
RET NZ C0
If the NZ FLAG is set (Register A = the write error), RETurn with it.
4A66
LD A,C 79
Load Register A with Register C, the verify flag.
4A67
OR A B7
OR Register A with itself. The Z FLAG is set if no verify is wanted.
4A68
If the NZ FLAG is set (verify), GOSUB to 45DFH to read sector Register Pair DE back, throwing the data away.
4A6B
RET Z C8
If the Z FLAG is set (no verify, or the verify read was good), RETurn with success.
4A6C
DECrement Register B (the write tries) and, if it is not 0, LOOP BACK to 4A62H to write again.
4A6E
RET C9
RETurn with the verify error in Register A after the last try failed.

Write Loop (Directory Mark)

4A6F
GOSUB to 45E7H to write the buffer at Register Pair HL to sector Register Pair DE with the directory mark.
4A72
RET NZ C0
If the NZ FLAG is set (Register A = the write error), RETurn with it.
4A73
LD A,C 79
Load Register A with Register C, the verify flag.
4A74
OR A B7
OR Register A with itself. The Z FLAG is set if no verify is wanted.
4A75
RET Z C8
If the Z FLAG is set (no verify), RETurn with success.
4A76
GOSUB to 45DFH to read sector Register Pair DE back.
4A79
GOSUB to 48A8H: a read that returned error 06H (the directory mark was read back) gives the Z FLAG; no error gives 31H, any other error itself.
4A7C
RET Z C8
If the Z FLAG is set (the sector reads back with its directory mark), RETurn with success.
4A7D
DECrement Register B (the write tries) and, if it is not 0, LOOP BACK to 4A6FH to write again.
4A7F
RET C9
RETurn with the error in Register A after the last try failed.

4A80H - Device I/O Through ROUTE or an FCB

Reached through 4033H when the ROM's dispatcher finds that the type byte of the control block at Register Pair DE does not match the request in Register B (01H input, 02H output). A type byte of C0H marks a device that ROUTE has redirected: the ROUTE list is searched for it. Anything else is taken to be an FCB, and one byte is read from or written to the file (manual 3.38 and 3.39); this is how 0013H and 001BH work with disk files.

4A80
LD HL,0000H 21 00 00
Load Register Pair HL with the first ROUTE entry. Self-Modifying Code
The operand at 4A81H-4A82H is the head of the ROUTE list, stored by the reset for option BA (4E94H) and read by SYS14/SYS (its 4E0AH); 0000H in the file, an empty list.
4A83
LD A,(DE) 1A
Fetch the type byte of the control block at Register Pair DE into Register A.
4A84
CP C0H FE C0
Compare Register A (the type byte) against C0H, the type of a routed device.
4A86
If the Z FLAG is set (a routed device), JUMP to 4AE5H to search the ROUTE list from Register Pair HL.
4A88
An FCB: GOSUB to 4925H, so the rest runs as a DOS function with Index Register IX = the FCB at Register Pair DE.
4A8B
SET 7,(IX+01H) DD CB 01 FE
Set bit 7 of FCB+1 at (IX+01H) (Index Register IX = the FCB): record mode, so the byte is moved through NEXT.
4A8F
LD A,B 78
Load Register A with Register B, the request (01H input, 02H output).
4A90
CP 02H FE 02
Compare Register A (the request) against 02H. The CARRY FLAG is set for input.
4A92
BIT 5,(IX+01H) DD CB 01 6E
Test bit 5 of FCB+1 at (IX+01H) (Index Register IX = the FCB). The Z FLAG is set if the buffer holds the sector at NEXT. BIT does not change the CARRY FLAG. 4990H enters here for each byte of a record.
4A96
If the NO CARRY FLAG is set (output), JUMP to 4A3DH to write Register C.
4A98
Input: if the NZ FLAG is set (the buffer does not hold the sector), GOSUB to 49BEH to read the sector at NEXT.
4A9B
RET NZ C0
If the NZ FLAG is set (Register A = the read error), RETurn with it.
4A9C
GOSUB to 4969H: NEXT must be below EOF, else errors 1CH or 1DH leave the DOS function.
4A9F
GOSUB to 4AB0H: Register Pair HL = the byte at NEXT in the buffer, and NEXT's low byte is counted up (the Z FLAG if it went round to 0).
4AA2
If the NZ FLAG is set (still in the same sector), JUMP to 4AADH to fetch the byte.
4AA4
PUSH HL E5
The sector is used up: save Register Pair HL (the byte's address in the buffer) onto the stack.
4AA5
GOSUB to 4A07H to write the buffer if it has changes waiting.
4AA8
POP HL E1
Restore Register Pair HL from the stack: the byte's address.
4AA9
RET NZ C0
If the NZ FLAG is set (Register A = the write error), RETurn with it.
4AAA
GOSUB to 49B0H to advance NEXT by 256 and mark the buffer as not holding the sector at NEXT.
4AAD
LD A,(HL) 7E
Fetch the byte at Register Pair HL (the buffer) into Register A, the byte read.
4AAE
CP A BF
Compare Register A with itself to set the Z FLAG (success).
4AAF
RET C9
RETurn with Register A = the byte read and the Z FLAG.
4AB0
GOSUB to 47F8H: Register Pair HL = the FCB's buffer, Register A = 00H.
4AB3
LD E,(IX+05H) DD 5E 05
Load Register E with NEXT's low byte from (IX+05H) (Index Register IX = the FCB), FCB+5, the byte's position in the sector.
4AB6
LD D,A 57
Load Register D with Register A (00H): Register Pair DE = the byte's position.
4AB7
ADD HL,DE 19
ADD Register Pair DE (the position) to Register Pair HL (the buffer): Register Pair HL = the byte's place.
4AB8
INC (IX+05H) DD 34 05
INCrement NEXT's low byte at (IX+05H) (Index Register IX = the FCB). The Z FLAG is set if it went round to 0, the end of the sector.
4ABB
RET C9
RETurn with Register Pair HL = the byte's place in the buffer.

4ABCH - Search the ROUTE List

Register Pair HL points to a ROUTE entry, Register Pair DE to the control block the I/O was aimed at, Register B holds the request and Register C the output character. Every entry for this control block gets the request: each is passed to the target device's driver through the ROM at 068BH (part of the ROM's driver entry 0674H: it compares Register A with 02H and jumps to the driver whose address is at IX+1). An output goes to every target; an input returns the first character received.

Entry Loop
Register Pair HL = the ROUTE entry: +0/+1 the control block it serves, +2 the saved type, +3/+4 the link, +5/+6 the target control block.

4ABC
LD A,(HL) 7E
Fetch the low byte of the entry's control block address at Register Pair HL (+0) into Register A.
4ABD
CP E BB
Compare Register A against Register E, the low byte of the control block in Register Pair DE.
4ABE
INC HL 23
INCrement Register Pair HL to the entry's +1, the high byte. INC HL does not change the flags.
4ABF
If the NZ FLAG is set (the low bytes differ), JUMP to 4AC3H.
4AC1
LD A,(HL) 7E
Fetch the high byte at Register Pair HL (+1) into Register A.
4AC2
CP D BA
Compare Register A against Register D, the high byte of the control block. The Z FLAG is set if the entry serves this device.
4AC3
INC HL 23
INCrement Register Pair HL to the entry's +2, the saved type byte.
4AC4
If the NZ FLAG is set (another device's entry), JUMP to 4AE0H to follow the link.
4AC6
PUSH HL E5
Save Register Pair HL (the entry's +2) onto the stack.
4AC7
PUSH DE D5
Save Register Pair DE (the control block the I/O was aimed at) onto the stack.
4AC8
PUSH BC C5
Save Register Pair BC (Register B = the request, Register C = the character) onto the stack.
4AC9
LD A,(HL) 7E
Fetch the device's original type byte at Register Pair HL (+2) into Register A.
4ACA
AND B A0
AND Register A (the type byte) with Register B (the request): Register A keeps the request bit if the device supports it. The ROM code at 068BH compares Register A with 02H.
4ACB
INC HL 23
INCrement Register Pair HL to the entry's +3.
4ACC
INC HL 23
INCrement Register Pair HL to the entry's +4.
4ACD
INC HL 23
INCrement Register Pair HL to the entry's +5, the target control block.
4ACE
LD E,(HL) 5E
Load Register E with the low byte of the target control block at Register Pair HL (+5).
4ACF
INC HL 23
INCrement Register Pair HL to the entry's +6.
4AD0
LD D,(HL) 56
Load Register D with the high byte at Register Pair HL (+6): Register Pair DE = the target control block.
4AD1
PUSH DE D5
Save Register Pair DE (the target control block) onto the stack.
4AD2
POP IX DD E1
Restore it into Index Register IX: Index Register IX = the target control block, as a driver expects.
4AD4
CALL 068BH CD 8B 06
GOSUB to the ROM at 068BH, which compares Register A (the request bit) with 02H, giving the CARRY FLAG for an input, and jumps to the driver whose address is at IX+1 and IX+2 of the target control block (Index Register IX). Register C carries the character for an output; an input returns its character in Register A.
4AD7
POP BC C1
Restore Register Pair BC from the stack: Register B = the request, Register C = the character.
4AD8
POP DE D1
Restore Register Pair DE from the stack: the control block the I/O was aimed at.
4AD9
POP HL E1
Restore Register Pair HL from the stack: the entry's +2.
4ADA
BIT 0,B CB 40
Test bit 0 of Register B (the request): 1 for input. The Z FLAG is set for output.
4ADC
If the Z FLAG is set (output), JUMP to 4AE0H to send the character to the next target too.
4ADE
OR A B7
Input: OR Register A (the character from the target's driver) with itself. The NZ FLAG is set if a character was received.
4ADF
RET NZ C0
If the NZ FLAG is set (Register A = a character), RETurn with it.
4AE0
INC HL 23
INCrement Register Pair HL from the entry's +2 to +3, the link to the next entry.
4AE1
LD A,(HL) 7E
Fetch the link's low byte at Register Pair HL into Register A.
4AE2
INC HL 23
INCrement Register Pair HL to the link's high byte (+4).
4AE3
LD H,(HL) 66
Load Register H with the link's high byte at Register Pair HL.
4AE4
LD L,A 6F
Load Register L with Register A, the link's low byte: Register Pair HL = the next entry.
4AE5
LD A,H 7C
Load Register A with Register H, the high byte of the entry address in Register Pair HL. 4A86H enters here with the list head.
4AE6
OR L B5
OR Register L into Register A. The Z FLAG is set if Register Pair HL is 0000H, the end of the list.
4AE7
If the NZ FLAG is set (another entry), LOOP BACK to 4ABCH.
4AE9
The end of the list: JUMP to 4C9DH, which returns Register A = 00H with the NZ FLAG for an input, or Register A = Register C for an output.

4AECH - Position an FCB

The positioning vectors of the manual (sections 3.22-3.27) come here. Each works out a new NEXT in Registers H (high), L (middle) and C (low). If the new NEXT is in another sector, the buffer is written first if it has changes waiting and is marked empty. Except for 443FH and 4448H, FCB+1 bit 6 is set, so that later writes do not pull EOF back.

4AEC
444EH, position to the byte address in Registers H, L and C: GOSUB to 4925H, so the rest runs as a DOS function with Index Register IX = the FCB at Register Pair DE and Registers H, L, C as the caller passed them.
4AEF
JUMP to 4B25H with the new NEXT in Registers H, L and C.
4AF1
443FH, position to the start of the file: GOSUB to 4925H to run as a DOS function (Register A = 00H when the code runs).
4AF4
LD H,A 67
Load Register H with Register A (00H): the new NEXT's high byte.
4AF5
LD L,A 6F
Load Register L with Register A (00H): the new NEXT's middle byte.
4AF6
LD C,A 4F
Load Register C with Register A (00H): the new NEXT's low byte. NEXT = 0, the first byte of the file.
4AF7
JUMP to 4B35H to write the buffer if needed, mark it empty and store NEXT (FCB+1 bit 6 is not set).
4AF9
4448H, position to EOF: GOSUB to 4925H to run as a DOS function with Index Register IX = the FCB.
4AFC
LD C,(IX+08H) DD 4E 08
Load Register C with EOF's low byte from (IX+08H) (Index Register IX = the FCB), FCB+8.
4AFF
LD L,(IX+0CH) DD 6E 0C
Load Register L with EOF's middle byte from (IX+0CH) (Index Register IX = the FCB), FCB+12.
4B02
LD H,(IX+0DH) DD 66 0D
Load Register H with EOF's high byte from (IX+0DH) (Index Register IX = the FCB), FCB+13. Registers H, L, C = the new NEXT.
4B05
JUMP to 4B29H, skipping the SET of FCB+1 bit 6.
4B07
4445H, position back one record: GOSUB to 4925H to run as a DOS function with Index Register IX = the FCB.
4B0A
GOSUB to 490DH: Register C = NEXT's low byte and Register Pair HL = NEXT's middle and high bytes.
4B0D
XOR A AF
Set Register A to 00H and clear the CARRY FLAG.
4B0E
SUB (IX+09H) DD 96 09
SUBtract the LRL at (IX+09H) (Index Register IX = the FCB), FCB+9, from Register A: Register A = 256 minus the LRL (00H for an LRL of 0, meaning 256).
4B11
ADD A,C 81
ADD Register C (NEXT's low byte) to Register A: the new low byte. The CARRY FLAG is set when NEXT's low byte was at least the LRL, so no borrow from the middle byte is needed (never for an LRL of 256).
4B12
LD C,A 4F
Store Register A (the new low byte) in Register C.
4B13
If the CARRY FLAG is set (no borrow), JUMP to 4B25H.
4B15
DEC HL 2B
A borrow: DECrement Register Pair HL, NEXT's middle and high bytes: the previous sector.
4B16
JUMP to 4B25H with Registers H, L, C = the new NEXT.
4B18
4442H, position to the record number in Register Pair BC: GOSUB to 4925H to run as a DOS function.
4B1B
LD H,B 60
Load Register H with Register B, the record number's high byte.
4B1C
LD L,C 69
Load Register L with Register C: Register Pair HL = the record number.
4B1D
LD A,(IX+09H) DD 7E 09
Load Register A with the LRL from (IX+09H) (Index Register IX = the FCB), FCB+9.
4B20
OR A B7
OR Register A (the LRL) with itself. The Z FLAG is set for an LRL of 0 (256).
4B21
LD C,A 4F
Copy Register A (the LRL) into Register C. With an LRL of 256 Register C = 00H and Register Pair HL stays the record number, so NEXT = record x 256.
4B22
If the NZ FLAG is set (an LRL of 1-255), GOSUB to 4C42H to multiply Register Pair HL (the record) by Register A (the LRL): Registers H, L and C = the byte address.
4B25
SET 6,(IX+01H) DD CB 01 F6
Set bit 6 of FCB+1 at (IX+01H) (Index Register IX = the FCB): after a positioning, EOF only moves forward.
4B29
LD A,H 7C
Load Register A with Register H, the new NEXT's high byte.
4B2A
CP (IX+0BH) DD BE 0B
Compare Register A against NEXT's present high byte at (IX+0BH) (Index Register IX = the FCB), FCB+11.
4B2D
If the NZ FLAG is set (another sector), JUMP to 4B35H.
4B2F
LD A,L 7D
Load Register A with Register L, the new NEXT's middle byte.
4B30
CP (IX+0AH) DD BE 0A
Compare Register A against NEXT's present middle byte at (IX+0AH) (Index Register IX = the FCB), FCB+10.
4B33
If the Z FLAG is set (the same sector), JUMP to 4B46H to change only the low byte; the buffer stays valid.
4B35
PUSH HL E5
Save Register Pair HL (the new NEXT's middle and high bytes) onto the stack.
4B36
PUSH BC C5
Save Register Pair BC (Register C = the new NEXT's low byte) onto the stack.
4B37
GOSUB to 4A07H to write the buffer if it has changes waiting (FCB+1 bit 4).
4B3A
POP BC C1
Restore Register Pair BC from the stack: Register C = the new low byte.
4B3B
POP HL E1
Restore Register Pair HL from the stack: the new middle and high bytes.
4B3C
RET NZ C0
If the NZ FLAG is set (Register A = the write error), RETurn with it; NEXT is not changed.
4B3D
GOSUB to 49B8H to set FCB+1 bit 5: the buffer does not hold the sector at the new NEXT.
4B40
LD (IX+0AH),L DD 75 0A
Store Register L (the new middle byte) at (IX+0AH) (Index Register IX = the FCB), FCB+10.
4B43
LD (IX+0BH),H DD 74 0B
Store Register H (the new high byte) at (IX+0BH) (Index Register IX = the FCB), FCB+11.
4B46
LD (IX+05H),C DD 71 05
Store Register C (the new low byte) at (IX+05H) (Index Register IX = the FCB), FCB+5.
4B49
XOR A AF
Set Register A to 00H and the Z FLAG: success. 4B49H is also the driver of the NL device (its control block is 4CA1H): it returns 00H for an input and throws an output away.
4B4A
RET C9
RETurn from the DOS function with Register A = 00H and the Z FLAG (success).

4B4BH - Display or Print a Message

4467H (display, manual 3.33) enters at 4B4BH and 446AH (print, manual 3.34) at 4B61H. The message at Register Pair HL is sent through the ROM's output routine at 001BH up to and including a 0DH, or up to but not including a 03H. Register Pairs HL and DE are preserved.

4B4B
PUSH DE D5
Display: save Register Pair DE (the caller's) onto the stack.
4B4C
LD DE,401DH 11 1D 40
Point Register Pair DE to 401DH, the display's device control block.
4B4F
PUSH HL E5
Save Register Pair HL (the message address) onto the stack. 4B65H joins here with Register Pair DE = 4025H.

Character Loop

4B50
LD A,(HL) 7E
Fetch the message byte at Register Pair HL into Register A.
4B51
CP 03H FE 03
Compare Register A (the byte) against 03H, the end of message byte that is not sent.
4B53
If the Z FLAG is set (Register A = 03H), JUMP to 4B5EH to finish.
4B55
GOSUB to the ROM at 001BH to send the character in Register A to the device whose control block is at Register Pair DE (401DH or 4025H).
4B58
LD A,(HL) 7E
Fetch the byte at Register Pair HL again into Register A (the ROM routine does not keep Register A).
4B59
CP 0DH FE 0D
Compare Register A against 0DH (carriage return), which ends a message after being sent.
4B5B
INC HL 23
INCrement Register Pair HL to the next byte of the message. INC HL does not change the flags.
4B5C
If the NZ FLAG is set (Register A was not 0DH), LOOP BACK to 4B50H for the next character.
4B5E
POP HL E1
Restore Register Pair HL from the stack: the message address.
4B5F
POP DE D1
Restore Register Pair DE from the stack: the caller's DE.
4B60
RET C9
RETurn with Register Pairs HL and DE as they were.
4B61
PUSH DE D5
Print: save Register Pair DE (the caller's) onto the stack.
4B62
LD DE,4025H 11 25 40
Point Register Pair DE to 4025H, the printer's device control block.
4B65
JUMP to 4B4FH to send the message at Register Pair HL to the printer.

4B67H - RST 28H Dispatcher

RST 28H comes here through 400CH with a function code in Register A. Codes below 20H (function 0) are sent to the BREAK hook at 4478H. Any other code names a system file by its directory slot (bits 0-4) and a function in it (bits 5-7). If that file is not the overlay now in 4D00H-51FFH, its directory entry is read from drive 0, and it is loaded through the FCB at 44C0H; a system file that cannot be found gives error 2EH (SYSTEM PROGRAM NOT FOUND), or a HALT if the missing file is SYS4/SYS, the error message module. The overlay is then called at its transfer address with Register Pairs AF, BC, DE and HL as they were at the RST. The return address the RST pushed is thrown away, so the overlay returns to whoever called the routine that executed the RST.

4B67
INC SP 33
INCrement the Stack Pointer: the first of two steps that drop the return address the RST 28H pushed.
4B68
INC SP 33
INCrement the Stack Pointer again: the RST's return address is gone, and the top of the stack is the return address of whoever called the routine that did the RST.
4B69
CP 20H FE 20
Compare Register A (the function code) against 20H. The CARRY FLAG is set for codes 00H-1FH (function 0, used by the ROM's keyboard driver for BREAK).
4B6B
If the CARRY FLAG is set (a code below 20H), JUMP to 4478H, the BREAK key hook, with Register A = the code.
4B6E
PUSH HL E5
Save Register Pair HL (the caller's) onto the stack, to pass it to the overlay.
4B6F
PUSH DE D5
Save Register Pair DE (the caller's) onto the stack.
4B70
PUSH BC C5
Save Register Pair BC (the caller's, Register C often a sub-function) onto the stack.
4B71
PUSH AF F5
Save Register Pair AF (Register A = the function code) onto the stack.
4B72
LD HL,4289H 21 89 42
Point Register Pair HL to 4289H, the DOS flags.
4B75
SET 6,(HL) CB F6
Set bit 6 of the DOS flags at Register Pair HL (4289H): an overlay function is running, which also stops the key combinations.
4B77
LD HL,459CH 21 9C 45
Point Register Pair HL to 459CH, the first byte of the key combination check.
4B7A
LD (HL),00H 36 00
Store 00H (NOP) at Register Pair HL (459CH), in case the keyboard driver's RET (stored at 44FBH) is still there.
4B7C
EI FB
Enable interrupts. The RST may come from code running with them disabled, such as the interrupt handler's DEBUG entry (45C8H).
4B7D
AND 1FH E6 1F
AND Register A (the function code) with 1FH, keeping bits 0-4: the overlay's directory slot.
4B7F
LD HL,4288H 21 88 42
Point Register Pair HL to 4288H, the directory slot of the overlay now in 4D00H-51FFH.
4B82
CP (HL) BE
Compare Register A (the slot wanted) against the loaded overlay's slot at Register Pair HL (4288H). The Z FLAG is set if it is already in memory.
4B83
If the Z FLAG is set (the overlay is loaded), JUMP to 4BBEH to call it.
4B85
LD (HL),A 77
Store Register A (the new slot) at Register Pair HL (4288H), the overlay about to be loaded.
4B86
AND 07H E6 07
AND Register A (the slot) with 07H, keeping bits 0-2: the directory sector minus 2 that holds the file's entry.
4B88
LD C,A 4F
Copy Register A (the sector bits) into Register C.
4B89
XOR A AF
Set Register A to 00H, for NEXT and for the drive number below.
4B8A
LD (44CAH),A 32 CA 44
Store Register A (00H) at 44CAH, NEXT's middle byte (FCB+10) of the load FCB at 44C0H: the load starts at the file's first sector.
4B8D
GOSUB to 4723H to select drive 0 (Register A = 00H): system files are always loaded from drive 0.
4B90
LD A,(HL) 7E
Fetch the slot at Register Pair HL (4288H) into Register A.
4B91
SUB C 91
SUBtract Register C (bits 0-2) from Register A, leaving bits 3-4: the file's entry number within the directory sector.
4B92
RLCA 07
Rotate Register A left: bits 3-4 move up (two rotates in all).
4B93
RLCA 07
Rotate Register A left again: the entry number is now in bits 5-6, where a DEC keeps it.
4B94
ADD A,C 81
ADD Register C (the sector bits) to Register A: Register A = the DEC of the system file's directory entry.
4B95
GOSUB to 48DBH to read the directory sector of the DEC in Register A: Register Pair HL = the entry, Register A = its offset in the sector.
4B98
If the NZ FLAG is set (the directory could not be read), JUMP to 4BB3H.
4B9A
BIT 6,(HL) CB 76
Test bit 6 of the entry's byte 0 at Register Pair HL: a system file. The Z FLAG is set if it is not.
4B9C
If the Z FLAG is set (not a system file: the slot holds something else), JUMP to 4BB3H.
4B9E
ADD A,16H C6 16
ADD 16H (22) to Register A (the entry's offset in the sector): the offset of its first extent.
4BA0
LD L,A 6F
Copy Register A into Register L: Register Pair HL = the entry's first extent element.
4BA1
LD E,(HL) 5E
Fetch the first extent's lump byte at Register Pair HL into Register E.
4BA2
INC HL 23
INCrement Register Pair HL to the extent's second byte.
4BA3
LD D,(HL) 56
Fetch the extent's second byte at Register Pair HL into Register D: Register Pair DE = the file's only extent.
4BA4
LD (44CEH),DE ED 53 CE 44
Store Register Pair DE (the extent) at 44CEH-44CFH, FCB+14 and +15 of the load FCB; 44D0H (FFH) ends its extent list.
4BA8
LD HL,44C0H 21 C0 44
Point Register Pair HL to 44C0H, the load FCB (buffer 4300H, drive 0).
4BAB
GOSUB to 4BCDH to load the system file through the FCB at Register Pair HL: Register Pair HL returns its transfer address, the Z FLAG for success.
4BAE
LD (4BC3H),HL 22 C3 4B
Store Register Pair HL (the transfer address) at 4BC3H, the operand of the CALL at 4BC2H. LD does not change the flags.
4BB1
If the Z FLAG is set (loaded), JUMP to 4BBEH to call it.
4BB3
LD A,(4288H) 3A 88 42
The system file could not be loaded: fetch the slot from 4288H into Register A.
4BB6
CP 06H FE 06
Compare Register A (the slot) against 06H, SYS4/SYS's slot (SYS4/SYS holds the error messages).
4BB8
LD A,2EH 3E 2E
Load Register A with error code 2EH (SYSTEM PROGRAM NOT FOUND). LD does not change the flags.
4BBA
If the NZ FLAG is set (it was not SYS4/SYS), JUMP to 4409H to report error 2EH through SYS4/SYS.
4BBD
HALT 76
HALT: SYS4/SYS itself cannot be loaded, so no error message can be shown. The CPU stops until the next interrupt; interrupts are enabled (4B7CH), so after the clock interrupt it goes on at 4BBEH.
4BBE
POP AF F1
Restore Register Pair AF from the stack: Register A = the function code (the overlay uses bits 5-7 as the function).
4BBF
POP BC C1
Restore Register Pair BC from the stack: the caller's BC.
4BC0
POP DE D1
Restore Register Pair DE from the stack: the caller's DE.
4BC1
POP HL E1
Restore Register Pair HL from the stack: the caller's HL.
4BC2
GOSUB to the overlay's transfer address (0000H in the file). Self-Modifying Code
The operand at 4BC3H-4BC4H is stored by 4BAEH: the transfer address of the overlay last loaded. The overlay returns here, or pops this return address and returns elsewhere.
4BC5
PUSH HL E5
Save Register Pair HL (the overlay's result) onto the stack. Overlays that end by jumping to 4BC5H come here too.
4BC6
LD HL,4289H 21 89 42
Point Register Pair HL to 4289H, the DOS flags.
4BC9
RES 6,(HL) CB B6
Clear bit 6 of the DOS flags at Register Pair HL (4289H): no overlay function is running.
4BCB
POP HL E1
Restore Register Pair HL from the stack: the overlay's result.
4BCC
RET C9
RETurn to the caller of the routine that executed the RST 28H, with the overlay's Register Pairs AF, BC, DE and HL.

4BCDH - Load a Program File

Loads the load module file of the open FCB at Register Pair HL into memory and returns its transfer address in Register Pair HL with the Z FLAG set. The file is read a sector at a time into 4300H, so the FCB's buffer must be 4300H. Record type 01H is a load block (length, two address bytes, data), 02H is the transfer address (it ends the load), and any other type up to 1FH is skipped. Errors: 22H (LOAD FILE FORMAT ERROR) for a type above 1FH, 24H (TRIED TO LOAD READ ONLY MEMORY) if a loaded byte does not read back, or the read error. Also called by SYS2/SYS.

4BCD
LD (4C0FH),HL 22 0F 4C
Store Register Pair HL (the FCB address) at 4C0FH, the operand of the LD DE at 4C0EH used for each sector read.
4BD0
LD DE,43FFH 11 FF 43
Point Register Pair DE to 43FFH, the byte before the buffer: the first fetch steps Register E to 00H and reads the first sector.

Record Loop

4BD3
GOSUB to 4C0AH to fetch the next byte of the file into Register A: the record type.
4BD6
LD C,A 4F
Copy Register A (the record type) into Register C.
4BD7
LD A,1FH 3E 1F
Load Register A with 1FH, the highest valid record type.
4BD9
CP C B9
Compare Register A (1FH) against Register C (the type). The CARRY FLAG is set for a type above 1FH.
4BDA
LD A,22H 3E 22
Load Register A with error code 22H (LOAD FILE FORMAT ERROR). LD does not change the flags.
4BDC
RET C D8
If the CARRY FLAG is set (a type above 1FH), RETurn with Register A = 22H and the NZ FLAG.
4BDD
GOSUB to 4C0AH to fetch the record length into Register A (00H = 256).
4BE0
LD B,A 47
Copy Register A (the record length) into Register B, the count of bytes in the record.
4BE1
GOSUB to 4C0AH to fetch the record's first byte into Register A (the low byte of an address for types 01H and 02H).
4BE4
LD L,A 6F
Copy Register A (the first byte) into Register L.
4BE5
LD H,C 61
Copy Register C (the record type) into Register H: for a skipped record Register Pair HL is then an address in the ROM.
4BE6
DEC C 0D
DECrement Register C (the type). The Z FLAG is set for type 01H.
4BE7
If the Z FLAG is set (a load block), JUMP to 4BF2H.
4BE9
DEC C 0D
DECrement Register C again. The Z FLAG is set for type 02H.
4BEA
If the NZ FLAG is set (another type), JUMP to 4C06H to skip the rest of the record. Its bytes are stored at Register Pair HL, an address in the ROM (Register H = the type), where the stores do nothing; the read-back error is ignored because Register C is not 0.
4BEC
Transfer address: GOSUB to 4C0AH to fetch its high byte into Register A.
4BEF
LD H,A 67
Copy Register A into Register H: Register Pair HL = the transfer address.
4BF0
XOR A AF
Set Register A to 00H and the Z FLAG: the load is complete.
4BF1
RET C9
RETurn with Register Pair HL = the transfer address and the Z FLAG.
4BF2
Load block: GOSUB to 4C0AH to fetch the load address's high byte into Register A.
4BF5
LD H,A 67
Copy Register A into Register H: Register Pair HL = the load address.
4BF6
DEC B 05
DECrement Register B (the record length) for the low address byte.
4BF7
DEC B 05
DECrement Register B again for the high address byte: Register B = the number of data bytes.

Data Loop
Register Pair HL = the load address, Register B = bytes left, Register C = 00H for a load block.

4BF8
GOSUB to 4C0AH to fetch the next data byte into Register A.
4BFB
LD (HL),A 77
Store Register A (the data byte) at Register Pair HL, its load address.
4BFC
CP (HL) BE
Compare Register A against the byte at Register Pair HL. The Z FLAG is set if it reads back (RAM); the NZ FLAG means ROM or missing memory.
4BFD
INC HL 23
INCrement Register Pair HL to the next load address. INC HL does not change the flags.
4BFE
If the Z FLAG is set (the byte read back), JUMP to 4C06H.
4C00
LD A,24H 3E 24
Load Register A with error code 24H (TRIED TO LOAD READ ONLY MEMORY).
4C02
INC C 0C
INCrement Register C, the first of two steps that test it for 0.
4C03
DEC C 0D
DECrement Register C back. The Z FLAG is set if Register C is 0: a load block, where the error counts.
4C04
If the Z FLAG is set (a load block), JUMP to 4C68H to return Register A = 24H with the NZ FLAG.
4C06
DECrement Register B (bytes left) and, if it is not 0, LOOP BACK to 4BF8H.
4C08
JUMP to 4BD3H to read the next record's type byte.
4C0A
INC E 1C
Fetch the next byte of the file: INCrement Register E, the low byte of the pointer into the buffer at Register Pair DE. The Z FLAG is set when it wraps to 00H.
4C0B
LD A,(DE) 1A
Fetch the byte at Register Pair DE into Register A. LD does not change the flags.
4C0C
RET NZ C0
If the NZ FLAG is set (still in the sector), RETurn with the byte in Register A.
4C0D
PUSH DE D5
The sector is used up: save Register Pair DE (4300H) onto the stack.
4C0E
LD DE,0000H 11 00 00
Point Register Pair DE to the FCB being loaded (0000H in the file). Self-Modifying Code
The operand at 4C0FH is stored by 4BCDH.
4C11
GOSUB to 4436H (READ) to read the next sector of the file into its buffer at 4300H; NEXT advances by 256.
4C14
POP DE D1
Restore Register Pair DE from the stack: 4300H, the first byte of the new sector.
4C15
If the NZ FLAG is set (Register A = the read error), JUMP to 4C40H: its POP BC drops this call's return address and its RET returns from 4BCDH with the error.
4C17
LD A,(DE) 1A
Fetch the first byte of the new sector at Register Pair DE (4300H) into Register A.
4C18
RET C9
RETurn to the loader with the byte in Register A and the Z FLAG from the read.

4C19H - Sector Number of a Directory Sector

Register A holds a directory sector (0 = GAT). It is stored at 48D5H as the sector now in 4300H, and Register Pair DE is loaded with its sector number on the current drive's diskette: (the directory's lump x granules per lump) x 5 + Register A. Register Pair HL is set to 4300H. 4C21H is the general entry, used by 47F5H: Register Pair HL points to a lump byte, Register E holds a granule in the lump and Register A a sector in the granule.

4C19
LD (48D5H),A 32 D5 48
Store Register A (the directory sector) at 48D5H, the operand of 48D4H: the directory sector the buffer at 4300H will hold.
4C1C
LD E,00H 1E 00
Load Register E with 00H: the directory starts at granule 0 of its lump.
4C1E
LD HL,(42B9H) 2A B9 42
Load Register Pair HL with the address of the current drive's PDRIVE record from 42B9H; its byte 0 is the directory's starting lump.
4C21
LD C,A 4F
Copy Register A (the sector within the granule) into Register C. 47F5H enters here.
4C22
LD L,(HL) 6E
Load Register L with the lump number at Register Pair HL (the PDRIVE record's byte 0, or an extent's lump byte).
4C23
LD A,(4285H) 3A 85 42
Load Register A with the granules per lump of the current drive from 4285H (PDRIVE byte +5).
4C26
GOSUB to 4C37H to multiply Register L (the lump) by Register A (the granules per lump): Register Pair HL = the first granule of the lump, Register A = the high byte (00H).
4C29
LD B,A 47
Copy Register A (00H) into Register B: Register Pair BC = the sector within the granule.
4C2A
LD D,A 57
Copy Register A (00H) into Register D: Register Pair DE = the granule within the lump.
4C2B
ADD HL,DE 19
ADD Register Pair DE (the granule within the lump) to Register Pair HL: the granule number on the diskette.
4C2C
LD A,05H 3E 05
Load Register A with 05H, the sectors per granule.
4C2E
GOSUB to 4C39H to multiply Register Pair HL (the granule) by Register A (5): Register Pair HL = its first sector, Register A = the high byte.
4C31
ADD HL,BC 09
ADD Register Pair BC (the sector within the granule) to Register Pair HL: the sector number on the diskette.
4C32
EX DE,HL EB
Exchange Register Pairs DE and HL: Register Pair DE = the sector number.
4C33
LD HL,4300H 21 00 43
Point Register Pair HL to 4300H, the DOS sector buffer, for the read or write that follows.
4C36
RET C9
RETurn with Register Pair DE = the sector number and Register Pair HL = 4300H.

4C37H - Multiply and Divide

4C37H: Register Pair HL = Register L x Register A. 4C39H: Register Pair HL x Register A, with the result in Register A (high byte), Register H and Register L. 4C42H: Register Pair HL x Register A with the result in Register H (high), Register L and Register C (low), used for record x LRL. 4C57H divides Register Pair HL by 5 and 4C59H by Register A: the quotient in Register Pair HL, the remainder in Register A. Used by several overlays.

4C37
LD H,00H 26 00
Load Register H with 00H: Register Pair HL = Register L, the 8-bit number to multiply.
4C39
PUSH BC C5
Save Register Pair BC (the caller's) onto the stack; 4C42H uses Register C for the low byte of the product.
4C3A
GOSUB to 4C42H to multiply Register Pair HL by Register A: the 24-bit product comes back in Registers H, L and C.
4C3D
LD A,H 7C
Load Register A with Register H, the product's high byte.
4C3E
LD H,L 65
Load Register H with Register L, the product's middle byte.
4C3F
LD L,C 69
Load Register L with Register C, the product's low byte: Register Pair HL = the low 16 bits.
4C40
POP BC C1
Restore Register Pair BC from the stack (the caller's). 4C15H jumps here to use this POP to drop the return address of its caller.
4C41
RET C9
RETurn with Register A = the high byte and Register Pair HL = the low 16 bits of the product.
4C42
PUSH DE D5
Save Register Pair DE (the caller's) onto the stack.
4C43
EX DE,HL EB
Exchange Register Pairs DE and HL: Register Pair DE = the number to multiply.
4C44
LD C,80H 0E 80
Load Register C with 80H: this marker bit is shifted down through Register C and falls into the CARRY FLAG after eight shifts, ending the loop.
4C46
LD HL,0000H 21 00 00
Load Register Pair HL with 0000H, the product's upper 16 bits.

Multiply Loop
Register A = the multiplier (rotated right, back to its value after 8 passes), Register Pair DE = the multiplicand, Registers H, L, C = the product shifted right as it grows.

4C49
RRCA 0F
Rotate Register A (the multiplier) right: its lowest bit goes into the CARRY FLAG (and to bit 7).
4C4A
If the NO CARRY FLAG is set (that multiplier bit is 0), JUMP to 4C4DH.
4C4C
ADD HL,DE 19
ADD Register Pair DE (the multiplicand) to Register Pair HL (the product's upper part).
4C4D
SRL H CB 3C
Shift Register H right: bit 0 goes into the CARRY FLAG.
4C4F
RR L CB 1D
Rotate Register L right through the CARRY FLAG, taking Register H's bit 0.
4C51
RR C CB 19
Rotate Register C right through the CARRY FLAG, taking Register L's bit 0; the marker bit in Register C falls out after eight passes.
4C53
If the NO CARRY FLAG is set (the marker has not come out), LOOP BACK to 4C49H.
4C55
POP DE D1
Restore Register Pair DE from the stack (the caller's).
4C56
RET C9
RETurn with the product in Registers H (high), L and C (low).
4C57
LD A,05H 3E 05
Divide by 5: load Register A with 05H, the divisor. 47CEH calls here to turn a sector of a file into a granule.
4C59
LD C,A 4F
Copy Register A (the divisor) into Register C; it stays there after the division (4612H uses it).
4C5A
LD B,10H 06 10
Load Register B with 10H (16), one pass per bit of Register Pair HL.
4C5C
XOR A AF
Set Register A to 00H: the remainder starts at 0.

Divide Loop
Register Pair HL = the dividend, becoming the quotient bit by bit; Register A = the remainder; Register C = the divisor.

4C5D
ADD HL,HL 29
ADD Register Pair HL to itself: shift the dividend left; its top bit goes into the CARRY FLAG.
4C5E
RLA 17
Rotate Register A (the remainder) left through the CARRY FLAG, taking the dividend's top bit.
4C5F
If the CARRY FLAG is set (a bit fell out of Register A, so the remainder is above the divisor), JUMP to 4C64H to subtract.
4C61
CP C B9
Compare Register A (the remainder) against Register C (the divisor). The CARRY FLAG is set if it is lower.
4C62
If the CARRY FLAG is set (the remainder is below the divisor), JUMP to 4C66H: this quotient bit is 0.
4C64
SUB C 91
SUBtract Register C (the divisor) from Register A (the remainder).
4C65
INC L 2C
INCrement Register L: set the quotient bit just shifted into Register Pair HL's bit 0.
4C66
DECrement Register B and, if it is not 0, LOOP BACK to 4C5DH for the next bit.
4C68
OR A B7
OR Register A (the remainder) with itself to set the flags. 4C04H jumps here to return Register A = 24H with the NZ FLAG.
4C69
RET C9
RETurn with Register Pair HL = the quotient, Register A = the remainder (or the error code from 4C04H).

4C6AH - Compare a Keyword and Step Over Separators

4C6AH compares the 00H-terminated string at Register Pair BC with the text at Register Pair HL: on a match it returns the Z FLAG with Register Pair HL after the text, otherwise the NZ FLAG, Register A = 34H and Register Pair HL unchanged. 4C7AH returns the Z FLAG if Register Pair HL points to 0DH (the end of the command line). 4C7EH steps over a comma or over spaces and returns the NZ and NO CARRY flags; for any other character it returns the CARRY FLAG with Register Pair HL unchanged. Register A is 34H (ILLEGAL KEYWORD OR SEPARATOR OR TERMINATOR) whenever the Z FLAG is not set.

4C6A
PUSH HL E5
Save Register Pair HL (the text pointer) onto the stack, to go back to it if the keyword does not match.

Compare Loop
Register Pair BC = the keyword, Register Pair HL = the text.

4C6B
LD A,(BC) 0A
Fetch the next keyword character at Register Pair BC into Register A.
4C6C
OR A B7
OR Register A with itself. The Z FLAG is set at the keyword's 00H end.
4C6D
INC BC 03
INCrement Register Pair BC to the next keyword character. INC BC does not change the flags.
4C6E
If the NZ FLAG is set (Register A is a keyword character), JUMP to 4C73H to compare it.
4C70
EX (SP),HL E3
The whole keyword matched: exchange Register Pair HL (the text after the keyword) with the saved pointer on the stack (the Stack Pointer's target).
4C71
POP HL E1
Restore Register Pair HL from the stack: the text after the keyword (the original pointer is dropped).
4C72
RET C9
RETurn with the Z FLAG set (from 4C6CH) and Register Pair HL after the keyword.
4C73
CP (HL) BE
Compare Register A (the keyword character) against the text character at Register Pair HL.
4C74
INC HL 23
INCrement Register Pair HL to the next text character. INC HL does not change the flags.
4C75
If the Z FLAG is set (the characters are equal), LOOP BACK to 4C6BH.
4C77
POP HL E1
Different: restore Register Pair HL from the stack, the text pointer as it was on entry.
4C78
JUMP to 4C8EH to return the NZ FLAG and Register A = 34H.
4C7A
LD A,(HL) 7E
Fetch the character at Register Pair HL (the command line) into Register A.
4C7B
CP 0DH FE 0D
Compare Register A against 0DH, the end of the command line.
4C7D
RET Z C8
If the Z FLAG is set (Register A = 0DH), RETurn with it.
4C7E
LD A,(HL) 7E
Fetch the character at Register Pair HL into Register A. Entry for stepping over a separator.
4C7F
CP 2CH FE 2C
Compare Register A against 2CH (ASCII comma).
4C81
INC HL 23
INCrement Register Pair HL past the character. INC HL does not change the flags.
4C82
If the Z FLAG is set (a comma), JUMP to 4C8EH with Register Pair HL after it.
4C84
CP 20H FE 20
Compare Register A against 20H (ASCII space).
4C86
DEC HL 2B
DECrement Register Pair HL back to the character. DEC HL does not change the flags.
4C87
SCF 37
Set the CARRY FLAG, the answer for a character that is neither comma nor space.
4C88
If the NZ FLAG is set (Register A is not a space), JUMP to 4C8FH with the CARRY FLAG set and Register Pair HL unchanged.

Space Loop

4C8A
INC HL 23
INCrement Register Pair HL to the next character.
4C8B
CP (HL) BE
Compare Register A (20H, a space) against the character at Register Pair HL.
4C8C
If the Z FLAG is set (another space), LOOP BACK to 4C8AH.
4C8E
OR A B7
OR Register A (not 0) with itself: the NZ FLAG and the NO CARRY FLAG.
4C8F
LD A,34H 3E 34
Load Register A with 34H (ILLEGAL KEYWORD OR SEPARATOR OR TERMINATOR) for the caller to use as an error code.
4C91
RET C9
RETurn with Register A = 34H and Register Pair HL at the next item.

4C92H - Delay

Waits Register B x 256 passes of the ROM's delay loop at 0060H (plus the passes in Register C), repeated SYSTEM option BJ times. Used for the motor start and head settle delays, and by SYS5/SYS and SYS6/SYS.

4C92
LD D,B 50
Copy Register B (the delay count, high byte) into Register D, to reload it for each pass.
4C93
LD E,01H 1E 01
Load Register E with the number of passes. Self-Modifying Code
The operand at 4C94H is SYSTEM option BJ (the CPU speed factor), stored by the reset (4D9BH) from byte A9H of the system data sector; the file has 01H.
4C95
LD B,D 42
Load Register B with Register D, the delay count. Register C is as the caller left it.
4C96
GOSUB to the ROM at 0060H, which jumps to the ROM's delay loop at 01FBH and counts Register Pair BC down to 0.
4C99
DEC E 1D
DECrement Register E, the passes left (option BJ counts down to 0).
4C9A
If the NZ FLAG is set (Register E has not reached 0), LOOP BACK to 4C95H.
4C9C
RET C9
RETurn after the delay; Register Pair BC = 0000H.
4C9D
BIT 0,B CB 40
The end of the ROUTE list, reached from 4AE9H with Register A = 00H. Test bit 0 of Register B (the request): 1 for input.
4C9F
RET NZ C0
If the NZ FLAG is set (an input), RETurn with Register A = 00H: no character was received.
4CA0
LD A,C 79
Output: load Register A with Register C, the character sent, as the ROM's drivers return it.
4CA1
RET C9
RETurn with Register A = the character. This byte (C9H) is also the type byte of the NL device's control block 4CA1H-4CA3H, the target of ROUTE entry 1 (42DBH).
4CA2-4CA3
DEFW 4B49H 49 4B
The NL device's driver address, bytes 1 and 2 of its control block at 4CA1H: 4B49H, which returns Register A = 00H with the Z FLAG (an input gets no character, an output is thrown away). The bytes are never executed: the RET at 4CA1H comes before them.

4CA4H - Half the Track Count

Called by the sector routine at 4634H to find the first track that is written with precompensation.

4CA4
LD A,(4283H) 3A 83 42
Fetch the current drive's track count from 4283H (PDRIVE byte 3) into Register A.
4CA7
SRL A CB 3F
Shift Register A (the track count) right: Register A = half the track count, the middle of the diskette.
4CA9
RET C9
RETurn to 4637H with Register A = half the track count.

4CAAH - Zero Bytes

86 bytes of 00H at the end of the resident DOS.

4CAA-4CFF
DEFB 00H x 86 00 x 86
86 bytes of 00H, 4CAAH-4CFFH, the end of the resident part of SYS0/SYS. Nothing in SYS0/SYS refers to them.

4D00H - Reset: Memory Size and Clock

The ROM's boot loader reads the boot sector (BOOT/SYS) into 4300H, and the boot sector loads SYS0/SYS and checks that the byte at its transfer address 4D00H is A5H before jumping to 4D01H. The reset code runs once from here and is overwritten afterwards: 4D00H-51FFH is the overlay area. This part finds the top of memory, restores the clock after a reset while the DOS was active, and hooks the clock interrupt.

4D00
DEFB A5H A5
A5H at the transfer address. The boot sector checks for this byte before it jumps to 4D01H, so a file that is not SYS0/SYS is never run.
4D01
IM 1 ED 56
Set Interrupt Mode 1: every maskable interrupt goes to the ROM at 0038H, and from there through the RST 38H vector at 4012H to the ROM's handler at 3018H.
4D03
LD HL,FFFFH 21 FF FF
Point Register Pair HL to FFFFH, the top of the address space, to search downward for the highest RAM byte.
4D06
LD A,(HL) 7E
Loop Start
Fetch the byte at Register Pair HL into Register A.
4D07
CPL 2F
Invert Register A (every bit of the byte flipped), so that the store below changes every bit of a RAM byte.
4D08
LD (HL),A 77
Store Register A (the inverted byte) at Register Pair HL.
4D09
CP (HL) BE
Compare Register A (the inverted byte) against the byte at Register Pair HL. Only RAM keeps what was stored, giving the Z FLAG.
4D0A
DEC HL 2B
DECrement Register Pair HL to the next lower address. DEC HL does not change the flags.
4D0B
Loop End
If the NZ FLAG is set (no RAM at that address), LOOP BACK to 4D06H.
4D0D
CPL 2F
RAM was found: invert Register A back to the byte's original value.
4D0E
INC HL 23
INCrement Register Pair HL back to the RAM byte that was found: the top of memory.
4D0F
LD (HL),A 77
Store Register A (the original value) back at Register Pair HL.
4D10
LD (42C9H),HL 22 C9 42
Store Register Pair HL (the top of memory) at 42C9H, where it is kept for the overlays.
4D13
LD (4411H),HL 22 11 44
Store Register Pair HL (the top of memory) at 4411H as HIMEM; 4DD0H may replace it with option AP.
4D16
LD HL,42CBH 21 CB 42
Point Register Pair HL to 42CBH, which holds A5H only if the DOS was running before this reset.
4D19
LD A,A5H 3E A5
Load Register A with A5H, the mark stored at 42CBH by 4F06H.
4D1B
CP (HL) BE
Compare Register A (A5H) against the byte at Register Pair HL (42CBH). The Z FLAG is set after a reset while the DOS was active.
4D1C
If the NZ FLAG is set (a power-on, or a reset after the DOS was not running), JUMP to 4D27H; the clock keeps what the ROM set.
4D1E
INC HL 23
INCrement Register Pair HL to 42CCH, the copy of the clock made every second by the clock routine (4567H).
4D1F
LD DE,4217H 11 17 42
Point Register Pair DE to 4217H, the ROM's clock (seconds, minutes, hours, then year, day, month).
4D22
LD BC,0006H 01 06 00
Load Register Pair BC with 0006H, the six bytes of time and date.
4D25
LDIR ED B0
Copy 6 bytes from Register Pair HL (42CCH-42D1H) to Register Pair DE (4217H-421CH), Register Pair BC counting down to 0: the time and date come back as they were before the reset.
4D27
LD HL,(4047H) 2A 47 40
Fetch the address part of the real-time clock vector at 4047H (the JP at 4046H, which the ROM's interrupt handler calls) into Register Pair HL: the ROM's own clock routine.
4D2A
LD (4552H),HL 22 52 45
Store Register Pair HL (the old clock routine) at 4552H, the operand of the CALL at 4551H, so that the DOS clock routine still runs it first.
4D2D
LD HL,4551H 21 51 45
Point Register Pair HL to 4551H, the DOS clock routine.
4D30
LD (4047H),HL 22 47 40
Store Register Pair HL (4551H) at 4047H: every clock interrupt now runs 4551H.

4D33H - Reset: Read the System Data Sector

The boot sector is still in 4300H. Its bytes FDH-FFH describe the system diskette in drive 0, and they are put into drive 0's PDRIVE entry so that the DOS can read that diskette. Then the system data sector (sector 2 of drive 0) is read, its mark A5H checked, and the option values are stored where the DOS uses them. A bad value gives error 27H.

4D33
LD IY,4280H FD 21 80 42
Load Index Register IY with 4280H, the base for IY+d addressing of the variables 4280H-42BFH.
4D37
LD BC,(43FEH) ED 4B FE 43
Load Register Pair BC from 43FEH-43FFH, the last bytes of the boot sector still in the buffer: Register C = byte FEH (the PDRIVE flags, byte +2) and Register B = byte FFH (the disk I/O flags, byte +7) of the system diskette.
4D3B
LD (IY+18H),B FD 70 18
Store Register B (byte FFH, the disk I/O flags) at (IY+18H) (Index Register IY = 4280H), which is 4298H, byte +7 of drive 0's PDRIVE entry.
4D3E
LD (IY+13H),C FD 71 13
Store Register C (byte FEH, the flags) at (IY+13H) (Index Register IY = 4280H), which is 4293H, byte +2 of drive 0's PDRIVE entry.
4D41
LD A,(43FDH) 3A FD 43
Fetch byte FDH of the boot sector at 43FDH into Register A: the value for the sector register at selection (PDRIVE byte +6).
4D44
LD (IY+17H),A FD 77 17
Store Register A (byte FDH) at (IY+17H) (Index Register IY = 4280H), which is 4297H, byte +6 of drive 0's PDRIVE entry.
4D47
LD DE,4480H 11 80 44
Point Register Pair DE to 4480H, the DOS FCB, which as loaded addresses sector 2 of drive 0 with buffer 4300H.
4D4A
GOSUB to 4436H (READ) to read the system data sector into 4300H through the FCB at Register Pair DE.
4D4D
If the NZ FLAG is set (Register A = the read error), JUMP to 4DDFH to report it through SYS4/SYS.
4D50
LD A,(43EFH) 3A EF 43
Fetch byte EFH of the system data sector at 43EFH into Register A.
4D53
CP A5H FE A5
Compare Register A against A5H, the mark of a valid system data sector.
4D55
If the NZ FLAG is set (no A5H), JUMP to 4DDDH for error 27H.
4D58
LD HL,43F8H 21 F8 43
Point Register Pair HL to 43F8H, bytes F8H-FFH of the sector (yes/no options).
4D5B
LD DE,504BH 11 4B 50
Point Register Pair DE to 504BH, a place in the reset data where they are kept, because the buffer is used again before 4F11H needs byte F9H.
4D5E
LD BC,0008H 01 08 00
Load Register Pair BC with 0008H: eight bytes.
4D61
LDIR ED B0
Copy 8 bytes from Register Pair HL (43F8H-43FFH) to Register Pair DE (504BH-5052H), Register Pair BC counting down to 0.
4D63
LD HL,(43F0H) 2A F0 43
Load Register Pair HL from 43F0H-43F1H: bytes F0H-F1H of the sector, yes/no options (F0H: AA, AB, AG, AR; F1H: AT, BC, BE, BK).
4D66
LD (428CH),HL 22 8C 42
Store Register Pair HL (bytes F0H-F1H) at 428CH-428DH, where the DOS and the overlays test them.
4D69
LD HL,(43F2H) 2A F2 43
Load Register Pair HL from 43F2H-43F3H, bytes F2H-F3H of the sector.
4D6C
LD (428EH),HL 22 8E 42
Store Register Pair HL (bytes F2H-F3H) at 428EH-428FH.
4D6F
LD A,(43A0H) 3A A0 43
Fetch byte A0H at 43A0H into Register A: option AL, the number of drives.
4D72
LD (42BFH),A 32 BF 42
Store Register A (option AL) at 42BFH, which 4DE7H compares with the drive number.
4D75
LD (4727H),A 32 27 47
Store Register A (option AL) at 4727H, the operand of the CP at 4726H, where the drive select routine refuses higher drive numbers.
4D78
DEC A 3D
DECrement Register A (option AL): 1-4 become 0-3, and 0 becomes FFH.
4D79
CP 04H FE 04
Compare Register A against 04H. The NO CARRY FLAG is set unless option AL was 1-4.
4D7B
If the NO CARRY FLAG is set (option AL is 0 or above 4), JUMP to 4DDDH for error 27H.
4D7D
LD A,(43A1H) 3A A1 43
Fetch byte A1H at 43A1H into Register A: option AW, the number of write-with-verify tries.
4D80
LD (4A5FH),A 32 5F 4A
Store Register A (option AW) at 4A5FH, the operand of the LD B at 4A5EH.
4D83
LD A,(43A2H) 3A A2 43
Fetch byte A2H at 43A2H into Register A: option AN, the default drive of DIR.
4D86
LD (42C0H),A 32 C0 42
Store Register A (option AN) at 42C0H, where the overlays find it.
4D89
LD A,(43A3H) 3A A3 43
Fetch byte A3H at 43A3H into Register A: option AO, the first drive searched for room for a new file.
4D8C
LD (42C1H),A 32 C1 42
Store Register A (option AO) at 42C1H, where the overlays find it.
4D8F
LD A,(43A6H) 3A A6 43
Fetch byte A6H at 43A6H into Register A: option AM, the number of tries of a disk read or write.
4D92
LD (4605H),A 32 05 46
Store Register A (option AM) at 4605H, the operand of the LD B at 4604H.
4D95
LD A,(43A9H) 3A A9 43
Fetch byte A9H at 43A9H into Register A: option BJ, the CPU speed factor.
4D98
LD (42C2H),A 32 C2 42
Store Register A (option BJ) at 42C2H, where the overlays find the CPU speed factor.
4D9B
LD (4C94H),A 32 94 4C
Store Register A (option BJ) at 4C94H, the operand of the LD E at 4C93H (the passes of the delay routine).
4D9E
LD L,08H 2E 08
Load Register L with 08H, the status delay count for a CPU at normal speed.
4DA0
LD DE,2A00H 11 00 2A
Load Register Pair DE with 2A00H, the diskette test time-out for a CPU at normal speed.
4DA3
PUSH AF F5
Save Register Pair AF (Register A = option BJ) onto the stack.
4DA4
GOSUB to 4C37H to multiply Register L (8) by Register A (option BJ): Register Pair HL = 8 x BJ, Register A = the high byte.
4DA7
OR H B4
OR Register H into Register A (the product's high bytes). The NZ FLAG is set if 8 x BJ is above 255.
4DA8
If the NZ FLAG is set (the product does not fit in one byte), JUMP to 4DDDH for error 27H.
4DAA
LD A,L 7D
Load Register A with Register L, the product 8 x BJ.
4DAB
LD (478AH),A 32 8A 47
Store Register A (8 x BJ) at 478AH, the operand of the LD A at 4789H (the status delay).
4DAE
POP AF F1
Restore Register Pair AF from the stack: Register A = option BJ.
4DAF
EX DE,HL EB
Exchange Register Pairs DE and HL: Register Pair HL = 2A00H.
4DB0
GOSUB to 4C39H to multiply Register Pair HL (2A00H) by Register A (option BJ): Register A = the high byte, Register Pair HL = the low 16 bits.
4DB3
OR A B7
OR Register A (the product's high byte) with itself. The NZ FLAG is set if 2A00H x BJ is above FFFFH.
4DB4
If the NZ FLAG is set (too large), JUMP to 4DDDH for error 27H.
4DB6
LD (4799H),HL 22 99 47
Store Register Pair HL (2A00H x BJ) at 4799H, the operand of the LD DE at 4798H (the index pulse time-out).
4DB9
LD A,(43A8H) 3A A8 43
Fetch byte A8H at 43A8H into Register A: option AX, the highest character the printer can print.
4DBC
LD (4290H),A 32 90 42
Store Register A (option AX) at 4290H, where the overlays find it.
4DBF
LD HL,(4411H) 2A 11 44
Load Register Pair HL with HIMEM from 4411H: the top of memory found at 4D13H.
4DC2
LD DE,(43D0H) ED 5B D0 43
Load Register Pair DE from 43D0H-43D1H: option AP, the HIMEM wanted (0 = leave it).
4DC6
LD A,D 7A
Load Register A with Register D, option AP's high byte.
4DC7
OR E B3
OR Register E (the low byte) into Register A. The Z FLAG is set if option AP is 0.
4DC8
If the Z FLAG is set (option AP = 0), JUMP to 4DD0H to keep the top of memory in Register Pair HL.
4DCA
OR A B7
OR Register A with itself to clear the CARRY FLAG for the SBC.
4DCB
SBC HL,DE ED 52
SUBtract Register Pair DE (option AP) from Register Pair HL (the top of memory). The CARRY FLAG is set if option AP is above the top of memory.
4DCD
If the CARRY FLAG is set (option AP is above the top of memory), JUMP to 4DD0H. Register Pair HL then holds the top of memory minus option AP, a wrapped value, and that is what 4DD0H stores as HIMEM; the manual says option AP is used only when it lies within existing memory.
4DCF
EX DE,HL EB
Exchange Register Pairs DE and HL: Register Pair HL = option AP.
4DD0
LD (4411H),HL 22 11 44
Store Register Pair HL at 4411H as HIMEM: the top of memory, option AP, or (after 4DCDH) the difference.
4DD3
XOR A AF
Set Register A to 00H: drive 0, the first PDRIVE record.
4DD4
LD DE,4291H 11 91 42
Point Register Pair DE to 4291H, the in-memory PDRIVE table (10 bytes per drive, drives 0-3).
4DD7
LD IX,4300H DD 21 00 43
Load Index Register IX with 4300H, the first PDRIVE record in the system data sector (16 bytes per drive, drives 0-9).
4DDB
JUMP to 4DE3H to check all ten PDRIVE records and copy those of the drives in use.
4DDD
LD A,27H 3E 27
Load Register A with error code 27H (ILLEGAL INITIALIZATION DATA ON SYSTEM DISKETTE).
4DDF
PUSH AF F5
Save Register Pair AF (Register A = the error code) onto the stack for SYS4/SYS. 4D4DH (a read error) and 4EF3H come here with their own code.
4DE0
LD A,46H 3E 46
Load Register A with 46H, the RST 28H code of SYS4/SYS function 2, its error report for the reset.
4DE2
RST 28H EF
RST 28H with Register A = 46H: call SYS4/SYS with the error code on the stack. The dispatcher drops the RST's return address, so it does not come back here.
4DE3
LD BC,000AH 01 0A 00
Loop Start
Register A = the drive number (0-9), Index Register IX = its record in the sector, Register Pair DE = the next place in the table at 4291H. Load Register Pair BC with 000AH, the ten bytes of a record kept in memory.
4DE6
PUSH AF F5
Save Register Pair AF (Register A = the drive number) onto the stack.
4DE7
CP (IY+3FH) FD BE 3F
Compare Register A (the drive number) against option AL at (IY+3FH) (Index Register IY = 4280H), which is 42BFH. The CARRY FLAG is set for a drive in use.
4DEA
If the NO CARRY FLAG is set (the drive is not counted by option AL), JUMP to 4DF1H: it is checked but not copied.
4DEC
PUSH IX DD E5
Save Index Register IX (the record in the sector) onto the stack.
4DEE
POP HL E1
Restore it into Register Pair HL: Register Pair HL = the record in the sector.
4DEF
LDIR ED B0
Copy 10 bytes from Register Pair HL (the record) to Register Pair DE (the table at 4291H + 10 x drive), Register Pair BC counting down to 0; Register Pair DE moves on to the next drive's place.
4DF1
LD A,(IX+02H) DD 7E 02
Fetch byte +2 of the record from (IX+02H) (Index Register IX = the record in the sector) into Register A.
4DF4
AND 1CH E6 1C
AND Register A with 1CH, keeping bits 2-4 of the record's byte +2. The Z FLAG is set if they are all 0.
4DF6
If the Z FLAG is set (bits 2-4 are 0), JUMP to 4DDDH for error 27H. All ten records must have a value there.
4DF8
LD C,10H 0E 10
Load Register C with 10H: with Register B = 0, Register Pair BC = 16, the length of a record in the sector.
4DFA
ADD IX,BC DD 09
ADD Register Pair BC (16) to Index Register IX: the next record in the sector.
4DFC
POP AF F1
Restore Register Pair AF from the stack: Register A = the drive number.
4DFD
INC A 3C
INCrement Register A, the drive number, to the next PDRIVE record.
4DFE
CP 0AH FE 0A
Compare Register A (the drive number) against 0AH. The CARRY FLAG is set while drives 0-9 remain.
4E00
Loop End
If the CARRY FLAG is set (drives left), LOOP BACK to 4DE3H.

4E02H - Reset: Apply the Yes/No Options

Changes the resident code according to the options in bytes F8H, F9H and F0H of the system data sector: Register C holds byte F8H, Register B byte F9H, Register E byte F0H.

4E02
LD BC,(43F8H) ED 4B F8 43
Load Register Pair BC from 43F8H-43F9H: Register C = byte F8H (options AD, AE, AF, AQ, AJ, AU) and Register B = byte F9H (options AY, AZ, BA, BB, BD, BG, BH).
4E06
LD DE,(428CH) ED 5B 8C 42
Load Register Pair DE from 428CH-428DH: Register E = byte F0H (options AA, AB, AG, AR) and Register D = byte F1H.
4E0A
BIT 6,E CB 73
Test bit 6 of Register E (byte F0H): option AB, RUN-ONLY. The Z FLAG is set for N.
4E0C
If the Z FLAG is set (AB = N), JUMP to 4E16H.
4E0E
SET 2,(IY+09H) FD CB 09 D6
Set bit 2 of (IY+09H) (Index Register IY = 4280H), which is 4289H, the system flags: RUN-ONLY, which also stops the key combinations (45A1H).
4E12
RES 6,(IY+0DH) FD CB 0D B6
Clear bit 6 of (IY+0DH) (Index Register IY = 4280H), which is 428DH, the copy of byte F1H: option BC is turned off in RUN-ONLY mode.
4E16
BIT 0,C CB 41
Test bit 0 of Register C (byte F8H): option AU, the repeat key. The Z FLAG is set for N.
4E18
If the Z FLAG is set (AU = N), JUMP to 4E28H; the JR at 4513H stays unconditional (no repeat).
4E1A
LD A,20H 3E 20
Load Register A with 20H, the opcode of JR NZ.
4E1C
LD (4513H),A 32 13 45
Store Register A (20H) at 4513H: the keyboard driver now makes repeats when the countdown runs out.
4E1F
LD A,(43A7H) 3A A7 43
Fetch byte A7H at 43A7H into Register A: option AV, the ticks before the first repeat.
4E22
LD (4534H),A 32 34 45
Store Register A (option AV) at 4534H, the operand of the LD A at 4533H (the delay for a new key).
4E25
LD (4508H),A 32 08 45
Store Register A (option AV) at 4508H, the operand of the LD A at 4507H (the delay after a key is released).
4E28
LD A,(43A5H) 3A A5 43
Fetch byte A5H at 43A5H into Register A: option BI, the cursor character (0 = keep the standard one).
4E2B
OR A B7
OR Register A (option BI) with itself. The Z FLAG is set for 0.
4E2C
If the Z FLAG is set (BI = 0), JUMP to 4E31H and keep the ROM's cursor character.
4E2E
LD (4023H),A 32 23 40
Store Register A (option BI) at 4023H, the cursor character in the display's control block.
4E31
BIT 1,C CB 49
Test bit 1 of Register C (byte F8H): option AJ, the DOS keyboard driver. The Z FLAG is set for N.
4E33
If the Z FLAG is set (AJ = N), JUMP to 4E42H: the ROM's driver stays in the keyboard's control block.
4E35
LD A,(3840H) 3A 40 38
Read the keyboard row at 3840H into Register A: bit 3 is the up-arrow key.
4E38
BIT 3,A CB 5F
Test bit 3 of Register A (the up-arrow key). The NZ FLAG is set if it is held.
4E3A
If the NZ FLAG is set (up-arrow held during the reset), JUMP to 4E42H: the driver is not installed.
4E3C
LD HL,44EBH 21 EB 44
Point Register Pair HL to 44EBH, the DOS keyboard driver.
4E3F
LD (4016H),HL 22 16 40
Store Register Pair HL (44EBH) at 4016H-4017H, the driver address in the keyboard's control block.
4E42
BIT 1,B CB 48
Test bit 1 of Register B (byte F9H): option BG, the equivalent of LC,Y at reset (manual). The Z FLAG is set for N.
4E44
If the Z FLAG is set (BG = N), JUMP to 4E4BH.
4E46
LD HL,4019H 21 19 40
Point Register Pair HL to 4019H, the ROM's caps lock flag.
4E49
RES 0,(HL) CB 86
Clear bit 0 of the byte at Register Pair HL (4019H): caps lock off, so lower case letters reach the programs.
4E4B
BIT 0,B CB 40
Test bit 0 of Register B (byte F9H): option BH, cursor blinking. The Z FLAG is set for N.
4E4D
If the NZ FLAG is set (BH = Y), JUMP to 4E54H and leave the cursor blinking.
4E4F
LD A,01H 3E 01
Load Register A with 01H, a non-zero value for the blink switch.
4E51
LD (401CH),A 32 1C 40
Store Register A (01H) at 401CH, the ROM's blink switch: the cursor no longer blinks (the ROM's clock routine tests it at 3535H).
4E54
BIT 6,C CB 71
Test bit 6 of Register C (byte F8H): option AD, J-K-L. The Z FLAG is set for N.
4E56
If the Z FLAG is set (AD = N), JUMP to 4E5DH.
4E58
LD A,C0H 3E C0
Load Register A with C0H, the opcode of RET NZ.
4E5A
LD (45D0H),A 32 D0 45
Store Register A (C0H) at 45D0H: J-K-L now falls through to the screen print at 45D1H.
4E5D
BIT 4,C CB 61
Test bit 4 of Register C (byte F8H): option AF, D-F-G. The Z FLAG is set for N.
4E5F
If the Z FLAG is set (AF = N), JUMP to 4E66H.
4E61
LD A,20H 3E 20
Load Register A with 20H, the opcode of JR NZ.
4E63
LD (45AAH),A 32 AA 45
Store Register A (20H) at 45AAH: D-F-G now falls through to MINI-DOS at 45ACH.
4E66
BIT 2,C CB 51
Test bit 2 of Register C (byte F8H): option AQ, the CLEAR key. The Z FLAG is set for N.
4E68
If the Z FLAG is set (AQ = N), JUMP to 4E6EH: the CLEAR key stays filtered out.
4E6A
XOR A AF
Set Register A to 00H, the value for the CLEAR filter.
4E6B
LD (4544H),A 32 44 45
Store Register A (00H) at 4544H, the operand of the CP at 4543H: the keyboard driver no longer drops 1FH (CLEAR).
4E6E
BIT 5,C CB 69
Test bit 5 of Register C (byte F8H): option AE, 1-2-3. The Z FLAG is set for N.
4E70
If the Z FLAG is set (AE = N), JUMP to 4E77H.
4E72
LD A,20H 3E 20
Load Register A with 20H, the opcode of JR NZ.
4E74
LD (45B6H),A 32 B6 45
Store Register A (20H) at 45B6H: 1-2-3 now falls through to the DEBUG entry at 45B8H.
4E77
BIT 5,E CB 6B
Test bit 5 of Register E (byte F0H): option AG, BREAK as a key. The Z FLAG is set for N.
4E79
If the Z FLAG is set (AG = N), JUMP to 4E7FH.
4E7B
SET 4,(IY+09H) FD CB 09 E6
Set bit 4 of (IY+09H) (Index Register IY = 4280H), which is 4289H, the system flags: option AG is Y.
4E7F
SET 4,(IY+0CH) FD CB 0C E6
Set bit 4 of (IY+0CH) (Index Register IY = 4280H), which is 428CH, the copy of byte F0H. Bit 4 is set on every reset, whatever the sector holds; SYS5/SYS (DEBUG) tests it at its 5052H.

4E83H - Reset: Screen, Banner, Date and Time

Clears the screen, routes the display to NL if option BA is Y, adjusts the clock routine for option BB, shows the banner and, depending on options AY and AZ, asks for the date and time. Then the date and time are displayed, the GAT of drive 0 is read and its AUTO command is run unless ENTER is held (option BD).

4E83
LD HL,4F96H 21 96 4F
Point Register Pair HL to 4F96H, the message 1CH 1FH 03H (cursor to the top left, erase to the end of the screen).
4E86
GOSUB to 4467H to display the message at Register Pair HL: the screen is cleared.
4E89
LD A,(43F9H) 3A F9 43
Fetch byte F9H of the system data sector at 43F9H into Register A.
4E8C
BIT 5,A CB 6F
Test bit 5 of Register A (byte F9H): option BA, ROUTE,DO,NL at reset. The Z FLAG is set for N.
4E8E
If the Z FLAG is set (BA = N), JUMP to 4EA8H.
4E90
DI F3
Disable interrupts while the display's control block and ROUTE entry 1 are being changed.
4E91
LD HL,42D6H 21 D6 42
Point Register Pair HL to 42D6H, ROUTE entry 1, whose target is already the NL device (4CA1H).
4E94
LD (4A81H),HL 22 81 4A
Store Register Pair HL (42D6H) at 4A81H, the operand of 4A80H: the head of the ROUTE list.
4E97
LD DE,401DH 11 1D 40
Point Register Pair DE to 401DH, the display's device control block.
4E9A
LD (HL),E 73
Store Register E (1DH) at Register Pair HL, entry +0: the low byte of the device served.
4E9B
INC HL 23
INCrement Register Pair HL to entry +1, the high byte of the device served.
4E9C
LD (HL),D 72
Store Register D (40H) at Register Pair HL, entry +1: the entry serves 401DH, the display.
4E9D
INC HL 23
INCrement Register Pair HL to entry +2, the saved type byte.
4E9E
LD A,(DE) 1A
Fetch the display control block's type byte at Register Pair DE (401DH) into Register A.
4E9F
LD (HL),A 77
Store Register A (the original type) at Register Pair HL, entry +2.
4EA0
INC HL 23
INCrement Register Pair HL to entry +3, the link.
4EA1
LD A,C0H 3E C0
Load Register A with C0H, the type byte of a routed device.
4EA3
LD (DE),A 12
Store Register A (C0H) at Register Pair DE (401DH): the display is now routed, and the ROM's dispatcher sends its I/O to 4033H.
4EA4
XOR A AF
Set Register A to 00H for the link: no further entry for the display.
4EA5
LD (HL),A 77
Store Register A (00H) at Register Pair HL, the link's low byte (entry +3).
4EA6
INC HL 23
INCrement Register Pair HL to entry +4, the link's high byte.
4EA7
LD (HL),A 77
Store Register A (00H) at Register Pair HL, the link's high byte: link 0000H, this is the only entry.
4EA8
EI FB
Enable interrupts again (also reached from 4E8EH when option BA is N).
4EA9
LD A,(43F9H) 3A F9 43
Fetch byte F9H of the system data sector at 43F9H into Register A.
4EAC
BIT 4,A CB 67
Test bit 4 of Register A (byte F9H): option BB, clock interrupts 50 times a second instead of 60 (manual). The Z FLAG is set for N.
4EAE
If the Z FLAG is set (BB = N), JUMP to 4EBBH.
4EB0
LD A,19H 3E 19
Load Register A with 19H (25), the clock interrupts per second the ROM is to count.
4EB2
LD (455DH),A 32 5D 45
Store Register A (19H) at 455DH, the operand of the LD (HL) at 455CH: the clock routine reloads the ROM's count at 4216H with 25.
4EB5
LD HL,9999H 21 99 99
Load Register Pair HL with 9999H, 0.6 of a tick: 25 interrupts then make 40 ticks a second.
4EB8
LD (456EH),HL 22 6E 45
Store Register Pair HL (9999H) at 456EH, the operand of the LD DE at 456DH in the clock routine.
4EBB
LD HL,4F99H 21 99 4F
Point Register Pair HL to 4F99H, the banner (NEWDOS/80 in block graphics, Apparat, Inc., Version 2.0, Model III).
4EBE
GOSUB to 4467H to display the banner at Register Pair HL (lost if the display was routed to NL).
4EC1
LD A,(42CBH) 3A CB 42
Fetch the byte at 42CBH into Register A: A5H if the DOS was active before this reset.
4EC4
CP A5H FE A5
Compare Register A against A5H. The Z FLAG is set after a reset while the DOS was active.
4EC6
LD A,(43F9H) 3A F9 43
Fetch byte F9H of the system data sector at 43F9H into Register A (bit 7 option AY, bit 6 option AZ). LD does not change the flags.
4EC9
If the NZ FLAG is set (the DOS was not active), JUMP to 4ECDH with both options.
4ECB
RES 7,A CB BF
A reset while the DOS was active: clear bit 7 of Register A, so that only option AZ counts.
4ECD
AND C0H E6 C0
AND Register A with C0H, keeping options AY and AZ. After a power-on either one asks for the date and time (the manual names AY alone for that case).
4ECF
If the NZ FLAG is set (Register A shows a Y), GOSUB to 4F2BH to ask for the date and time.
4ED2
LD HL,5079H 21 79 50
Point Register Pair HL to 5079H, the date and time line (MM/DD/YY HH:MM:SS, filled in below).
4ED5
PUSH HL E5
Save Register Pair HL (5079H) onto the stack, to display the line afterwards.
4ED6
GOSUB to the ROM's date routine at 3033H to write the date as MM/DD/YY at Register Pair HL (5079H).
4ED9
LD HL,5083H 21 83 50
Point Register Pair HL to 5083H, the time's place in the line.
4EDC
GOSUB to the ROM's time routine at 3036H to write the time as HH:MM:SS at Register Pair HL (5083H).
4EDF
POP HL E1
Restore Register Pair HL from the stack: 5079H, the line.
4EE0
GOSUB to 4467H to display the date and time line at Register Pair HL.
4EE3
XOR A AF
Set Register A to 00H: directory sector 0, the GAT.
4EE4
LD (48D5H),A 32 D5 48
Store Register A (00H) at 48D5H, the directory sector 4898H will read.
4EE7
LD HL,4EF3H 21 F3 4E
Point Register Pair HL to 4EF3H, the place to continue after the read.
4EEA
PUSH HL E5
Save Register Pair HL (4EF3H) onto the stack as the return address.
4EEB
PUSH DE D5
Save Register Pair DE onto the stack, as 48AFH does on entry, for the POP DE at 48C2H.
4EEC
PUSH BC C5
Save Register Pair BC onto the stack, for the POP BC at 48C1H.
4EED
LD HL,4300H 21 00 43
Point Register Pair HL to 4300H, the buffer for the read at 48B9H.
4EF0
JUMP to 48B6H to read the boot sector of drive 0 (the current drive), take the directory's lump from its byte 2 into drive 0's PDRIVE entry, and read the GAT into 4300H. It returns to 4EF3H with the Z FLAG for success.
4EF3
If the NZ FLAG is set (Register A = 11H, the GAT could not be read), JUMP to 4DDFH to report the error through SYS4/SYS.
4EF6
LD DE,4225H 11 25 42
Point Register Pair DE to 4225H, the command buffer.
4EF9
PUSH DE D5
Save Register Pair DE (4225H) onto the stack.
4EFA
PUSH BC C5
Save Register Pair BC onto the stack; the LDIR below uses it as the count.
4EFB
LD HL,43E0H 21 E0 43
Point Register Pair HL to 43E0H, bytes E0H-FFH of the GAT: the AUTO command (0DH alone = none).
4EFE
LD BC,0020H 01 20 00
Load Register Pair BC with 0020H: the 32 bytes of the AUTO command field.
4F01
LDIR ED B0
Copy 32 bytes from Register Pair HL (the GAT's AUTO command) to Register Pair DE (the command buffer at 4225H), Register Pair BC counting down to 0.
4F03
LD HL,42CBH 21 CB 42
Point Register Pair HL to 42CBH, the mark of an active DOS.
4F06
LD (HL),A5H 36 A5
Store A5H at Register Pair HL (42CBH): the DOS is now active, which the next reset tests.
4F08
POP BC C1
Restore Register Pair BC from the stack (saved at 4EFAH).
4F09
POP HL E1
Restore Register Pair HL from the stack: 4225H, the AUTO command in the command buffer.
4F0A
XOR A AF
Set Register A to 00H; it is reloaded before it is used below.
4F0B
BIT 6,(IY+0CH) FD CB 0C 76
Test bit 6 of (IY+0CH) (Index Register IY = 4280H), which is 428CH, the copy of byte F0H: option AB, RUN-ONLY.
4F0F
If the NZ FLAG is set (RUN-ONLY), JUMP to 4F25H: the AUTO command is run and ENTER cannot skip it.
4F11
LD A,(504CH) 3A 4C 50
Fetch the copy of byte F9H at 504CH (made at 4D61H) into Register A.
4F14
BIT 3,A CB 5F
Test bit 3 of Register A (byte F9H): option BD, ENTER may skip the AUTO command. The Z FLAG is set for N.
4F16
If the Z FLAG is set (BD = N), JUMP to 4F25H to run the AUTO command.
4F18
LD A,(3840H) 3A 40 38
Read the keyboard row at 3840H into Register A; bit 0 is the ENTER key.
4F1B
RRCA 0F
Rotate Register A right: bit 0 (ENTER) goes into the CARRY FLAG.
4F1C
If the CARRY FLAG is set (ENTER is held), JUMP to 4400H, DOS READY, without the AUTO command.
4F1F
LD A,(HL) 7E
Fetch the first character of the AUTO command at Register Pair HL (4225H) into Register A.
4F20
CP 0DH FE 0D
Compare Register A against 0DH. The Z FLAG is set if there is no AUTO command.
4F22
If the Z FLAG is set (no AUTO command), JUMP to 4400H, DOS READY.
4F25
GOSUB to 4467H to display the AUTO command at Register Pair HL (4225H), ended by its 0DH.
4F28
JUMP to 4405H to execute the command at Register Pair HL (4225H). The reset is over; 4D00H-51FFH may now be overwritten by the overlays.

4F2BH - Ask for the Date and Time

Each question is repeated until the answer is valid. The answer is typed into the command buffer at 4225H through the ROM's line input at 0040H and checked by 4F5DH against the limits at 508CH or 5092H. The values go straight into the ROM's clock bytes.

4F2B
LD HL,5053H 21 53 50
Loop Start
Point Register Pair HL to 5053H, the question Date? (MM/DD/YY).
4F2E
GOSUB to 4F52H to display the question at Register Pair HL and read the answer into 4225H; Register Pair HL returns 4225H.
4F31
LD BC,508CH 01 8C 50
Point Register Pair BC to 508CH, the date limits (month 1-12, day 1-31, year 0-99).
4F34
LD DE,421CH 11 1C 42
Point Register Pair DE to 421CH, the month; the day (421BH) and the year (421AH) follow downward.
4F37
LD A,2FH 3E 2F
Load Register A with 2FH (ASCII slash), the separator expected between the values.
4F39
GOSUB to 4F5DH to check the answer at Register Pair HL and store the three values from Register Pair DE down. The Z FLAG is set if it was valid.
4F3C
Loop End
If the NZ FLAG is set (an invalid answer), LOOP BACK to 4F2BH to ask again.
4F3E
LD HL,5066H 21 66 50
Loop Start
Point Register Pair HL to 5066H, the question Time? (HH:MM:SS).
4F41
GOSUB to 4F52H to display the question at Register Pair HL and read the answer into 4225H.
4F44
LD BC,5092H 01 92 50
Point Register Pair BC to 5092H, the time limits (hours 0-23, minutes 0-59, seconds 0-59).
4F47
LD DE,4219H 11 19 42
Point Register Pair DE to 4219H, the hours; the minutes (4218H) and the seconds (4217H) follow downward.
4F4A
LD A,3AH 3E 3A
Load Register A with 3AH (ASCII colon), the separator expected between the values.
4F4C
GOSUB to 4F5DH to check the answer at Register Pair HL and store the three values. The Z FLAG is set if it was valid.
4F4F
Loop End
If the NZ FLAG is set (an invalid answer), LOOP BACK to 4F3EH to ask again.
4F51
RET C9
RETurn to 4ED2H with the date and time set in the ROM's clock bytes 4217H-421CH.
4F52
GOSUB to 4467H to display the question at Register Pair HL (it ends with 03H, so the cursor stays on the line).
4F55
LD HL,4225H 21 25 42
Point Register Pair HL to 4225H, the command buffer, for the answer.
4F58
LD B,09H 06 09
Load Register B with 09H, the most characters the ROM's line input accepts (8 and the ENTER).
4F5A
JUMP to the ROM's line input at 0040H, which reads up to Register B characters into the buffer at Register Pair HL and returns to the caller of 4F52H.

4F5DH - Check and Store a Date or Time

Register Pair HL points to the typed answer, Register Pair DE to the first of three clock bytes (stored downward), Register Pair BC to three pairs of limits (the lowest value, then the number of values allowed) and Register A to the separator. Each value must be two digits within its limits, the values must be separated by the separator, and the answer must end there. Returns the Z FLAG if it was valid.

4F5D
LD (4F8EH),A 32 8E 4F
Store Register A (the separator) at 4F8EH, the operand of the CP at 4F8DH.
4F60
DI F3
Disable interrupts: the clock routine must not change the clock bytes while they are being stored.
4F61
PUSH BC C5
Save Register Pair BC (the limits pointer) onto the stack; it is exchanged with Register Pair HL for each value.
4F62
LD B,03H 06 03
Load Register B with 03H, the three values to read.
4F64
LD A,(HL) 7E
Loop Start
Fetch the first digit at Register Pair HL into Register A.
4F65
SUB 30H D6 30
SUBtract 30H (ASCII 0) from Register A: the digit's value if it is a digit.
4F67
CP 0AH FE 0A
Compare Register A against 0AH. The NO CARRY FLAG is set if it was not a digit.
4F69
INC HL 23
INCrement Register Pair HL to the next character. INC HL does not change the flags.
4F6A
If the NO CARRY FLAG is set (not a digit), JUMP to 4F92H to return the NZ FLAG.
4F6C
LD C,A 4F
Copy Register A (the tens digit) into Register C.
4F6D
RLCA 07
Rotate Register A (the tens digit, 0-9) left: the tens digit x 2.
4F6E
RLCA 07
Rotate Register A left again: the tens digit x 4.
4F6F
ADD A,C 81
ADD Register C (the tens digit) to Register A: the tens digit x 5.
4F70
ADD A,A 87
ADD Register A to itself: the tens digit x 10.
4F71
LD C,A 4F
Copy Register A (the tens digit x 10) into Register C.
4F72
LD A,(HL) 7E
Fetch the second digit at Register Pair HL into Register A.
4F73
SUB 30H D6 30
SUBtract 30H (ASCII 0) from Register A: the digit's value if it is a digit.
4F75
CP 0AH FE 0A
Compare Register A against 0AH. The NO CARRY FLAG is set if it was not a digit.
4F77
INC HL 23
INCrement Register Pair HL to the next character. INC HL does not change the flags.
4F78
If the NO CARRY FLAG is set (not a digit), JUMP to 4F92H to return the NZ FLAG.
4F7A
ADD A,C 81
ADD Register C (the tens x 10) to Register A (the units): Register A = the value 0-99.
4F7B
LD (DE),A 12
Store Register A (the value) at Register Pair DE, its clock byte. The value is stored before it is checked, so an invalid answer can leave some clock bytes changed (the question is then asked again).
4F7C
DEC DE 1B
DECrement Register Pair DE to the next clock byte down.
4F7D
EX (SP),HL E3
Exchange Register Pair HL (the text pointer) with the limits pointer on top of the stack (the Stack Pointer's target).
4F7E
SUB (HL) 96
SUBtract the lowest allowed value at Register Pair HL from Register A (the value).
4F7F
INC HL 23
INCrement Register Pair HL to the number of values allowed.
4F80
CP (HL) BE
Compare Register A (the value minus the lowest) against the number of values allowed at Register Pair HL. The NO CARRY FLAG is set if it is out of range.
4F81
INC HL 23
INCrement Register Pair HL to the next pair of limits.
4F82
If the NO CARRY FLAG is set (out of range), JUMP to 4F92H to return the NZ FLAG.
4F84
EX (SP),HL E3
Exchange Register Pair HL (the limits pointer) with the text pointer on top of the stack (the Stack Pointer's target).
4F85
DECrement Register B (the values left) and, if it is not zero, JUMP to 4F8CH to check the separator.
4F87
POP BC C1
All three values are good: restore Register Pair BC from the stack (the limits pointer).
4F88
EI FB
Enable interrupts again: the clock bytes are complete and the clock routine may run.
4F89
JUMP to 4C7AH, which returns the Z FLAG if the character at Register Pair HL is 0DH, the end of the answer.
4F8C
LD A,(HL) 7E
Fetch the character at Register Pair HL into Register A: it must be the separator.
4F8D
CP 00H FE 00
Compare Register A against the separator. Self-Modifying Code
The operand at 4F8EH is stored by 4F5DH: 2FH (slash) for the date, 3AH (colon) for the time; 00H in the file.
4F8F
INC HL 23
INCrement Register Pair HL past the separator. INC HL does not change the flags.
4F90
Loop End
If the Z FLAG is set (the separator is there), LOOP BACK to 4F64H for the next value.
4F92
EI FB
An invalid answer: enable interrupts again (they were disabled at 4F60H).
4F93
POP AF F1
Restore Register Pair AF from the stack: this drops the limits pointer (or, after an out-of-range value, the text pointer), whose high byte in Register A is not 0.
4F94
OR A B7
OR Register A with itself: the NZ FLAG, since Register A is not 0.
4F95
RET C9
RETurn with the NZ FLAG: the question is asked again.

4F96H - Reset Messages (Data)

The messages displayed by the reset code. The banner draws NEWDOS/80 in large letters, three text lines high, with the block graphics characters 80H-BFH (bits 0-5 are the six dots of a character cell, 80H is an empty cell); the text Apparat, Inc., Version 2.0 and Model III follows on the three lines.

4F96-4F98
DEFB 1CH, 1FH, 03H 1C 1F 03
The screen clearing message displayed at 4E86H: 1CH moves the cursor to the top left, 1FH erases to the end of the screen, 03H ends the message without a new line.
4F99-4FC6
DEFB BFH, B4H, 80H, BFH, 20H, BEH, 83H, 83H, BDH, 20H, BFH, 80H, 80H, BFH, 20H, BFH, 83H, 83H, BDH, 20H, BEH, 83H, 83H, BDH, 20H, BEH, 83H, 83H, 8DH, 20H, 80H, 80H, B8H, 87H, 20H, BEH, 83H, 83H, BDH, 20H, BEH, 83H, 83H, BDH, 20H, 20H BF B4 80 BF 20 BE 83 83 BD 20 BF 80 80 BF 20 BF 83 83 BD 20 BE 83 83 BD 20 BE 83 83 8D 20 80 80 B8 87 20 BE 83 83 BD 20 BE 83 83 BD 20 20
The first of the banner's three lines, displayed by 4EBEH: the top third of the large letters NEWDOS/80 in block graphics, then two spaces.
4FC7-4FD4
DEFM "Apparat, Inc." / DEFB 0AH 41 70 70 61 72 61 74 2C 20 49 6E 63 2E 0A
The text at the end of the first line, then 0AH, which moves the cursor to the start of the next line.
4FD5-5003
DEFB BFH, 82H, ADH, BFH, 20H, BFH, 83H, 83H, B3H, 20H, BFH, 9EH, ADH, BFH, 20H, BFH, 80H, 80H, BFH, 20H, BFH, 80H, 80H, BFH, 20H, B2H, 83H, 83H, BDH, 20H, A0H, 9EH, 81H, 80H, 20H, BEH, 83H, 83H, BDH, 20H, BFH, 80H, 80H, BFH, 20H, 20H, 20H BF 82 AD BF 20 BF 83 83 B3 20 BF 9E AD BF 20 BF 80 80 BF 20 BF 80 80 BF 20 B2 83 83 BD 20 A0 9E 81 80 20 BE 83 83 BD 20 BF 80 80 BF 20 20 20
The second line of the banner: the middle third of the large letters, then three spaces.
5004-500F
DEFM "Version 2.0" / DEFB 0AH 56 65 72 73 69 6F 6E 20 32 2E 30 0A
The text at the end of the second line, then 0AH for the next line.
5010-503F
DEFB 83H, 80H, 80H, 83H, 20H, 82H, 83H, 83H, 81H, 20H, 83H, 80H, 80H, 83H, 20H, 83H, 83H, 83H, 81H, 20H, 82H, 83H, 83H, 81H, 20H, 82H, 83H, 83H, 81H, 20H, 83H, 80H, 80H, 80H, 20H, 82H, 83H, 83H, 81H, 20H, 82H, 83H, 83H, 81H, 20H, 20H, 20H, 20H 83 80 80 83 20 82 83 83 81 20 83 80 80 83 20 83 83 83 81 20 82 83 83 81 20 82 83 83 81 20 83 80 80 80 20 82 83 83 81 20 82 83 83 81 20 20 20 20
The third line of the banner: the bottom third of the large letters, then four spaces.
5040-504A
DEFM "Model III" / DEFB 0AH, 0DH 4D 6F 64 65 6C 20 49 49 49 0A 0D
The text at the end of the third line, then 0AH and the 0DH that ends the message (displayed as a new line).

504BH - Reset Data

The copy of the system data sector's bytes F8H-FFH, the date and time questions, the date and time line, and the limits used to check the answers.

504B-5052
DEFB 00H, 00H, 00H, 00H, 00H, 00H, 00H, 00H 00 00 00 00 00 00 00 00
Eight bytes that receive bytes F8H-FFH of the system data sector at 4D61H (00H in the file). 4F11H reads 504CH, the copy of byte F9H, after the buffer at 4300H has been reused for the GAT.
5053-5065
DEFM "Date? (MM/DD/YY) " / DEFB 03H 44 61 74 65 3F 20 20 28 4D 4D 2F 44 44 2F 59 59 29 20 03
The date question displayed by 4F2EH, ended by 03H so that the answer is typed on the same line.
5066-5078
DEFM "Time? (HH:MM:SS) " / DEFB 03H 54 69 6D 65 3F 20 20 28 48 48 3A 4D 4D 3A 53 53 29 20 03
The time question displayed by 4F41H, ended by 03H.
5079-508B
DEFM "MM/DD/YY HH:MM:SS" / DEFB 0DH 4D 4D 2F 44 44 2F 59 59 20 20 48 48 3A 4D 4D 3A 53 53 0D
The date and time line: 4ED6H writes the date over MM/DD/YY at 5079H, 4EDCH the time over HH:MM:SS at 5083H, and 4EE0H displays it.
508C-5091
DEFB 01H, 0CH, 01H, 1FH, 00H, 64H 01 0C 01 1F 00 64
The date limits used by 4F5DH, a pair for each value: month from 01H, 12 values (1-12); day from 01H, 31 values (1-31); year from 00H, 100 values (0-99).
5092-5097
DEFB 00H, 18H, 00H, 3CH, 00H, 3CH 00 18 00 3C 00 3C
The time limits: hours from 00H, 24 values (0-23); minutes from 00H, 60 values (0-59); seconds from 00H, 60 values (0-59).

5098H - Rest of the File (Data)

The file continues to 51EBH with bytes of 00H that nothing refers to. The overlays load over them.

5098-51EB
DEFB 00H x 340 00 x 340
340 bytes of 00H, 5098H-51EBH, the end of the file. Nothing in SYS0/SYS refers to them; the overlays load over this area.