TRS-80 DOS - TRSDOS v2.3 for the Model I - SYS0/SYS Disassembled

Page Customization

Introduction/Summary

TRSDOS v2.3 SYS0/SYS Disassembly - The Resident DOS (Model I)

SYS0/SYS is the part of TRSDOS 2.3 that stays in memory. BOOT/SYS reads it from the system diskette and jumps to its transfer address, 4E00H. The file holds 2,647 bytes in 72 load records between 400CH and 4F28H; the records skip the areas the ROM and the other modules own, and one record (4768H-476AH) loads over bytes an earlier record put there. The start-up code at 4E00H-4F28H runs once and is then overwritten, because 4E00H-51FFH is where the system overlays SYS1/SYS-SYS5/SYS are loaded.

The resident part gives TRSDOS 2.3 its fixed entry points at 4400H-4478H and 402DH-4033H, the RST 28H overlay call, the RST 30H DEBUG entry, the interrupt handler with its twelve timer task slots and the clock, the keyboard scan with debounce, and all of the file input and output: read, write, position, the conversion of a record number into a track and sector, the search of a file's extents in the directory and the allocation of new granules. Opening, closing, killing, 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, from 4000H to 4DFFH, are shown as DEFS rows without bytes, with what TRSDOS 2.3 keeps there.

Memory Map

Address RangeUse
4000H-400BH
12 bytes
ROM restart vectors; not loaded by SYS0/SYS.
400CH-4011H
6 bytes
RST 28H vector (JP 4BA2H) and RST 30H vector (JP 44B4H).
4012H-402CH
27 bytes
RST 38H vector (JP 4518H, written at start-up) and the ROM's keyboard, video and printer control blocks.
402DH-4035H
9 bytes
DOS READY exit, exit after an error, device dispatch.
403EH-405CH
31 bytes
Version byte, clock tick counter, time and date, memory limits, interrupt latch copy, enable mask and the eight interrupt vectors.
405DH-42FFH
675 bytes
Not loaded: DEBUG work area, Level II BASIC work area, the DOS stack (top 41FCH), the system sector buffer at 4200H.
4300H-4317H
24 bytes
Per-drive track and directory track tables, selected drive, last file call, overlay in memory, system flags, the hook for RST 28H codes below 80H, the BREAK vector.
4318H-43D0H
185 bytes
Command line buffer, keyboard control block copy, disk controller wait routines, the device list.
43D1H-447FH
175 bytes
Last record patch, keyboard scan, the DOS entry point table.
4480H-44EEH
111 bytes
Command FCB, overlay FCB, error and DEBUG entries, device dispatch, message display.
4500H-45FDH
254 bytes
Timer task slots, interrupt handler, clock tick routine, task dispatch, clock task and calendar table.
4600H-46FBH
252 bytes
Drive select, seek, sector read and write.
4700H-4B9FH
1,184 bytes
File routines, disk address of a record, extent search, granule allocation, directory and allocation table sectors, multiply and divide.
4BA0H-4CFFH
352 bytes
RST 28H overlay call, program load and run, load module loader, time display and TRACE tasks.
4D00H-4DFFH
256 bytes
Sector buffer for loading and for the allocation table; not loaded.
4E00H-4F28H
297 bytes
Start-up code and sign-on message (overwritten by the overlays).

RST 28H and the System Overlays

RST 28H with bit 7 of Register A set loads a system overlay and calls it (4BA2H). Bits 0-3 are the overlay number, which is also the position of its directory entry on drive 0: overlay n uses entry 0 of directory sector n plus 2, and overlays 8-15 use entry 1 of sectors 2-9. Bits 4-6 choose the function inside the overlay; SYS1/SYS and SYS2/SYS test them (AND 70H), SYS3/SYS likewise. Codes with bit 7 clear go to the hook at 4312H. The RST's own return address is dropped, so an overlay function returns to the caller of the entry point that executed the RST.

CodeOverlayFunction (entry point in SYS0/SYS)
93H, A3H3, SYS1/SYSDOS READY at 4E1FH (4400H, 4030H).
B3H3, SYS1/SYSRun the command line at Register Pair HL, 4E4EH (4405H).
C3H3, SYS1/SYSParse a filespec into an FCB, 4F56H (441CH).
D3H3, SYS1/SYSAdd a default extension, 4FC0H (4473H).
E3H3, SYS1/SYSParse a parameter list, 507DH (4476H).
94H4, SYS2/SYSOpen an existing file, 4E12H (4424H).
A4H4, SYS2/SYSOpen or create a file, 4ED8H (4420H).
B4H4, SYS2/SYSMake a continuation directory entry, 4F50H (4A6BH).
95H5, SYS3/SYSClose a file, 4E6DH (4428H).
A5H5, SYS3/SYSKill a file, 4FD0H (442CH).
86H6, SYS4/SYSDisplay the error message for the number on the stack (4409H, 44B0H).
87H7, SYS5/SYSDEBUG (400FH, 440DH, 44B4H).

DOS Entry Points

AddressEntry (Register Pair DE = FCB for the file calls)
402DHDOS READY
Return to the command prompt.
4030HExit After an Error
DOS READY, or DEBUG while SYS1/SYS's DEBUG command has made it CALL 400FH.
4033HDevice Dispatch
The ROM's driver entry jumps here when a control block's type does not match the request.
4400H, 4405HDOS READY and Run a Command
SYS1/SYS.
4409H, 440DHError Message and DEBUG
SYS4/SYS and SYS5/SYS.
4410H-4419HTimer Tasks
Add (Register A = slot, Register Pair DE = control block), remove, suspend, end.
441CH-442CHFilespec, Open or Create, Open, Close, Kill
SYS1/SYS, SYS2/SYS, SYS3/SYS.
4430H, 4433HLoad a Program, Load and Run
Register Pair HL returns the transfer address.
4436H-443CHRead, Write, Write with Verify
A sector, or one logical record to or from Register Pair HL.
443FH-4448HRewind, Position (Register Pair BC), Back Up a Record, Position to the End
4467H, 446AHDisplay or Print a Message
Text at Register Pair HL ending with 0DH or 03H.
446DH, 4470HTime or Date as Text
Eight characters at Register Pair HL.
4473H, 4476HDefault Extension, Parameter List
SYS1/SYS.

The File Control Block

An open FCB is 32 bytes. SYS2/SYS fills it in when the file is opened (its 4FA7H); these are the bytes SYS0/SYS uses.

OffsetContents
00HBit 7 set while the file is open (4892H refuses the FCB otherwise, error 26H). A value of 10H in a device control block's byte 0 links it to another block (44C4H).
01HBit 7: the file has a logical record length. Bit 6: the file has been positioned (writes inside the file leave its end alone). Bit 5: the buffer must be loaded before the next byte. Bit 4: the buffer has been changed. Bits 0-2: access level (5-7 no writing, 6-7 no reading).
03H-04HThe 256-byte sector buffer.
05HByte offset in the buffer.
06HDrive (00H-03H).
07HDirectory entry locator: bits 5-7 the entry's offset in the sector (times 20H), bits 0-4 the directory sector minus 2.
08HEnd-of-file byte: the bytes used in the last sector, 00H when it is full.
09HLogical record length (00H = 256).
0AH-0BHNext record number: the sector to read next. While bytes are being read from the buffer it is one past the sector in the buffer.
0CH-0DHEnding record number: the number of sectors in the file.
0EH-1FHUp to five extents, four bytes each: track, granule byte (bits 5-7 first granule on the track, bits 0-4 granules minus 1), and the granule of the file at which the next FCB extent begins. Extent 1 begins at granule 0; an extent found in the directory is put in as extent 2 (48DCH).

Diskette Layout Used by SYS0/SYS

A track holds ten 256-byte sectors (0-9) and two granules of five sectors; a diskette has 35 tracks (00H-22H). The directory track is 11H (17) unless the diskette's boot sector says otherwise in its byte 2 (4B3EH). Its sector 0 is the granule allocation table, one byte per track at offsets 00H-22H (bit 0 = granule 0, bit 1 = granule 1, FFH = no free granule), with the AUTO command at offsets E0H-FFH. Sectors 2-9 hold the directory entries, eight of 32 bytes in each sector; an entry's bytes 16H-1FH are five extents of two bytes (track, granule byte), with FEH followed by a locator continuing the list in another entry and FFH ending it. The directory and the allocation table are written with the FAH data mark, which a read reports as error 06H; 4B35H accepts only sectors with that mark.

Error Numbers

Error numbers are bits 0-5 of Register A; the texts are SYS4/SYS's own messages. Bit 6 set asks SYS4/SYS for the message text only, without the ERRCOD number and the FILE and REFERENCED AT lines; bit 7 set makes it return to the caller instead of exiting through 4030H. The disk errors 01H-0FH are built by 46D1H from the base 01H (read) or 09H (write) plus the position of the lowest error bit in the controller status.

NumberMessage
00HNO ERROR
01H-07HPARITY ERROR DURING HEADER READ, SEEK ERROR DURING READ, LOST DATA DURING READ, PARITY ERROR DURING READ, DATA RECORD NOT FOUND DURING READ, ATTEMPTED TO READ LOCKED/DELETED DATA RECORD (FAH mark), ATTEMPTED TO READ SYSTEM DATA RECORD (F9H mark)
08HDEVICE NOT AVAILABLE
09H-0FHPARITY ERROR DURING HEADER WRITE, SEEK ERROR DURING WRITE, LOST DATA DURING WRITE, PARITY ERROR DURING WRITE, DATA RECORD NOT FOUND DURING WRITE, WRITE FAULT ON DISK DRIVE, WRITE PROTECTED DISKETTE
10H-17HILLEGAL LOGICAL FILE NUMBER, DIRECTORY READ ERROR, DIRECTORY WRITE ERROR, ILLEGAL FILE NAME, GAT READ ERROR, GAT WRITE ERROR, HIT READ ERROR, HIT WRITE ERROR
18H-1FHFILE NOT IN DIRECTORY, FILE ACCESS DENIED, DIRECTORY SPACE FULL, DISK SPACE FULL, END OF FILE ENCOUNTERED, RECORD NUMBER OUT OF RANGE, DIRECTORY FULL - CAN'T EXTEND FILE, PROGRAM NOT FOUND
20H-26HILLEGAL DRIVE NUMBER, NO DEVICE SPACE AVAILABLE, LOAD FILE FORMAT ERROR, MEMORY FAULT, ATTEMPTED TO LOAD READ ONLY MEMORY, ILLEGAL ACCESS ATTEMPTED TO PROTECTED FILE, FILE NOT OPEN
27H-3FHUNKNOWN ERROR CODE

Interrupts and Timer Tasks

The RST 38H vector at 4012H is set to JP 4518H at start-up. The handler reads the interrupt latch at 37E0H, keeps the bits enabled in 404CH and calls the routine whose address is in the vector for each bit (404DH for bit 0 up to 405BH for bit 7). Bit 7 is serviced by the clock tick routine at 4560H and bit 6 by 4669H, which waits for the disk controller and reads its status. The clock tick routine runs timer slots 8-11 on every tick and one of slots 0-7 per tick by the low three bits of the counter at 4040H. A slot holds the address of a task control block whose first word is the task's routine; Index Register IX points to the block while the task runs. Slot 7 holds the clock (45AFH), which counts five of its runs as one second; SYS6/SYS puts the time display 4CA9H into slot 6 and SYS1/SYS's TRACE puts the address display 4CD9H into slot 11. When BREAK is down and 4315H holds C3H, the handler leaves through the address in 4316H-4317H.

Notes from the Code

  • A later load record replaces LD A,(IX+08H) at 4768H with CALL 43D1H, so positioning to the end of a file whose last sector is only partly used goes to that sector at its end byte.
  • Back up one record (4737H) takes the next record number as the sector the current byte is in. That holds while bit 5 of FCB+01H is set; after a read that ends inside a sector the next record number is one past the buffered sector, and the position set is one sector further on.
  • Granule allocation (4A00H) adds the granule wanted minus the granules the file has, plus two: one granule more than the file needs.
  • The program loader (4C81H) returns error 24H (ATTEMPTED TO LOAD READ ONLY MEMORY) when the failing location does take its complement, and 23H (MEMORY FAULT) when it does not, as ROM does.
  • The start-up loops at 4E53H and 4E74H end in JR NZ with a displacement of 00H, so only 43CCH is cleared and only the keyboard control block is copied to 4358H.
  • 4661H-4668H (issue a command and wait) is not called by any TRSDOS 2.3 module; 4B9FH is a loaded byte nothing executes.
  • The overlays are read through the FCB at 44A0H using only its first extent; the two bytes after it (44B0H-44B1H) are code and serve as that extent's running granule total.

Variables

Resident Variables

Address RangePurpose
4012H-4014H
3 bytes
RST 38H vector, JP 4518H (written at 4E81H-4E8AH).
4016H-4017H
2 bytes
Keyboard driver address in the ROM's control block, set to 43D8H at 4F23H.
4030H-4032H
3 bytes
LD A,A3H / RST 28H; CALL 400FH while SYS1/SYS's DEBUG is on.
4036H-403CH
7 bytes
Keyboard image used by the scan at 43D8H.
403EH
1 byte
DOS version byte 22H, tested by BASIC/CMD.
4040H
1 byte
Clock tick counter; bits 0-2 pick timer slot 0-7.
4041H-4043H
3 bytes
Seconds, minutes, hours (binary).
4044H-4046H
3 bytes
Year, day, month (binary).
4047H-4048H
2 bytes
Lowest free address, 5200H.
4049H-404AH
2 bytes
Top of memory found by the RAM test.
404BH
1 byte
Last interrupt latch value read from 37E0H.
404CH
1 byte
Interrupt enable mask (bit 7 clock, bit 6 disk controller).
404DH-405CH
16 bytes
Interrupt vectors for latch bits 0-7 (4059H = 4669H, 405BH = 4560H).
41FCH
1 byte
Top of the DOS stack (the first push uses 41FAH-41FBH).
4200H-42FFH
256 bytes
System sector buffer: directory sectors, and the allocation table sector at start-up (AUTO command at 42E0H).
4300H-4303H
4 bytes
Track register copy for drives 0-3.
4304H-4307H
4 bytes
Directory track of drives 0-3 (11H).
4308H
1 byte
Selected drive (FFH after a disk error).
4309H
1 byte
Select latch value of the selected drive.
430AH-430BH
2 bytes
FCB address of the last file call.
430CH-430DH
2 bytes
Return address of the last file call.
430EH
1 byte
RST 28H code of the last overlay call (bits 0-3 = overlay in memory).
430FH
1 byte
System flags; bit 7 = DEBUG.
4312H-4314H
3 bytes
JP 4BA0H, the hook for RST 28H codes below 80H (BASIC/CMD changes 4313H).
4315H-4317H
3 bytes
BREAK vector: C3H and an address when armed, 00H when not.
4318H-4357H
64 bytes
Command line buffer.
4358H-4377H
32 bytes
Copy of the keyboard control block, eight 00H, sixteen FFH.
43B8H-43BDH
6 bytes
Keyboard, video and printer driver addresses at start-up.
43C0H-43CCH
13 bytes
Device list: name and driver for KI, DO and PR, 00H at the end.
43FFH
1 byte
Receives the track register when 4308H holds FFH.
4480H-449FH
32 bytes
Command FCB (filled by SYS1/SYS).
44A0H-44AFH
16 bytes
Overlay FCB (buffer 4D00H, drive 0).
4500H-4517H
24 bytes
Timer task slots 0-11.
45AFH-45B1H
3 bytes
Clock task control block (routine 45B2H, countdown).
4CA9H-4CABH
3 bytes
Time display task control block (routine 4CACH, countdown).
4CD9H-4CDAH
2 bytes
TRACE task control block (routine 4CDBH).
4D00H-4DFFH
256 bytes
Sector buffer for loading and for the allocation table.

Self-Modifying Code

Address RangeOperand, Who Writes It and What It Holds
4313H-4314H
2 bytes
JP target at 4312H; BASIC/CMD.
45A6H-45A7H
2 bytes
LD HL at 45A5H: the running timer slot's address (457FH).
4645H
1 byte
SET n,A at 4644H: bit number for 4639H (4640H).
4693H
1 byte
LD (HL),nn at 4692H: the controller command, 88H, A8H or A9H (4671H).
46A7H-46A8H
2 bytes
The two data move instructions of the transfer loop (4681H).
46D3H
1 byte
LD A,nn at 46D2H: error number base 01H or 09H (467AH).
485EH
1 byte
LD A,nn at 485DH: verify switch (478EH; SYS6/SYS 60D5H).
499AH
1 byte
ADD A,nn at 4999H: granule offset inside the extent (4933H, 496AH).
49AAH
1 byte
ADD A,nn at 49A9H: sector inside the granule (48E7H).
49AEH
1 byte
ADD A,nn at 49ADH: tracks past the extent's track (499FH).
4ABEH
1 byte
LD B,nn at 4ABDH: locator of the directory entry being changed (49BDH; read by SYS2/SYS).
4B68H
1 byte
BIT n,B at 4B67H: granule bit to test (4B64H).
4BFCH-4BFDH
2 bytes
CALL at 4BFBH: the overlay's transfer address (4BE8H).
4C00H
1 byte
LD A,nn at 4BFFH: the BREAK vector byte saved during an overlay call (4BF1H).

Addresses Outside SYS0/SYS

Address RangeUse
37E0H
1 byte
Interrupt latch, read by 451AH.
37E1H
1 byte
Drive select latch, written by 43AFH and 462AH.
37ECH-37EFH
4 bytes
WD1771 command/status, track, sector and data registers.
3801H-3840H
64 bytes
Keyboard rows scanned by 43D8H; BREAK is bit 2 of 3840H (452DH).
3C2EH-3C3CH
15 bytes
Top screen line: TRACE address at 3C2EH-3C31H, time at 3C35H-3C3CH.
002BH, 0033H, 003BH, 0060H
ROM
Keyboard scan, display a character, print a character, delay.
03C2H, 03DDH, 03FBH
ROM
Driver entry (comes to 4033H), its register restore, the keyboard driver after the row scan.

Disassembly

4000H - Restart Vectors Kept by the ROM

The Model I ROM sends RST 08H, RST 10H, RST 18H and RST 20H to the three-byte vectors at 4000H, 4003H, 4006H and 4009H. SYS0/SYS does not load these twelve bytes and none of its code reads or changes them; they are listed so that the whole of 4000H-4DFFH can be followed on this page.

4000-4002
DEFS 3
RST 08H Vector
The ROM's 0008H jumps here. Not part of SYS0/SYS: the file has no load record for 4000H-4002H and no TRSDOS 2.3 routine in SYS0/SYS uses it.
4003-4005
DEFS 3
RST 10H Vector
The ROM's 0010H jumps here. Not part of SYS0/SYS: the file has no load record for 4003H-4005H and SYS0/SYS never reads or writes these three bytes.
4006-4008
DEFS 3
RST 18H Vector
The ROM's 0018H jumps here. Not part of SYS0/SYS: the file has no load record for 4006H-4008H and SYS0/SYS never reads or writes these three bytes.
4009-400B
DEFS 3
RST 20H Vector
The ROM's 0020H jumps here. Not part of SYS0/SYS: the file has no load record for 4009H-400BH and SYS0/SYS never reads or writes these three bytes.

400CH - RST 28H and RST 30H Vectors

The ROM's 0028H does JP 400CH and its 0030H does JP 400FH. SYS0/SYS loads a JP into each: RST 28H becomes the TRSDOS 2.3 overlay call (Register A = function code) and RST 30H becomes the DEBUG entry. The RST 38H vector at 4012H-4014H is written at run time by the start-up code at 4E81H-4E8AH.

400C
RST 28H Vector
JUMP to 4BA2H, the overlay call handler. A program executes RST 28H with Register A holding a function code: bits 0-3 select the system overlay (directory slot) and bits 4-6 the function inside it; codes with bit 7 clear go to the hook at 4312H instead. The ROM's BREAK test at 0456H also executes RST 28H, with Register A = 01H.
400F
RST 30H Vector
JUMP to 44B4H, which executes RST 28H with Register A = 87H to load SYS5/SYS (overlay 7) and enter DEBUG. 4C12H jumps here when the DEBUG flag (bit 7 of 430FH) is set and a program has just been loaded, and SYS1/SYS's DEBUG command puts this address into the BREAK vector at 4316H and into the CALL at 4030H.
4012-4014
DEFS 3
RST 38H Vector
The ROM's 0038H jumps here on every maskable interrupt. Not in the file: the start-up code stores C3H into 4012H at 4E8AH and 4518H into 4013H-4014H at 4E86H, so it becomes JP 4518H, the TRSDOS 2.3 interrupt handler. BASIC/CMD writes 4012H again at its 570BH.
4015-401C
DEFS 8
Keyboard Device Control Block (ROM)
Set up by the ROM; 002BH points Register Pair DE here. SYS0/SYS changes only the driver address at 4016H-4017H: 4F23H stores 43D8H there, the TRSDOS 2.3 keyboard scan with its own debounce. 4E57H-4E61H copy these eight bytes to 4358H-435FH, and 4E29H copies 401BH-401CH to 43C0H-43C1H.
401D-4024
DEFS 8
Video Device Control Block (ROM)
Set up by the ROM; 0033H points Register Pair DE here. SYS0/SYS does not change it: 4E2FH copies the driver address at 401EH-401FH to 43BAH and 43C6H and 4E38H copies 4023H-4024H to 43C4H. SYS5/SYS (DEBUG) writes the cursor address at 4020H.
4025-402C
DEFS 8
Printer Device Control Block (ROM)
Set up by the ROM; 003BH points Register Pair DE here. SYS0/SYS does not change it: 4E3EH copies the driver address at 4026H-4027H to 43BCH and 43CAH and 4E47H copies 402BH-402CH to 43C8H.

402DH - DOS Exit Vectors

Three fixed entries that programs and the overlays jump to when they finish. 402DH returns to DOS READY, 4030H is the exit after an error message has already been shown, and 4033H is the device dispatch the ROM's driver entry jumps to when a device control block's type does not match the request.

402D
Exit to DOS READY
JUMP to 4400H, which executes RST 28H with Register A = 93H: SYS1/SYS (overlay 3) function 10H, its DOS READY prompt at 4E1FH. SYS6/SYS, BASIC/CMD and TAPEDISK/CMD leave through here, and SYS1/SYS pushes 402DH as the return address of every command it runs (4E4EH).
4030
LD A,A3H 3E A3
Exit After an Error
Load Register A with A3H, the RST 28H code for SYS1/SYS (overlay 3) function 20H. SYS1/SYS sends both 93H and A3H to its DOS READY code at 4E1FH. SYS1/SYS's DEBUG command (5178H-517EH) overwrites 4030H-4032H with CALL 400FH (enter DEBUG) and 518AH-518CH put LD A,A3H and RST 28H back.
4032
RST 28H EF
Execute RST 28H with Register A = A3H. The handler at 4BA2H drops this RST's return address, loads SYS1/SYS into 4E00H if it is not already there and calls it; SYS1/SYS resets the Stack Pointer to 41FCH at 4E20H, so nothing returns here.
4033
Device Dispatch for Mismatched Device Types
The ROM's driver entry at 03D1H jumps here when the type byte of the device control block (Index Register IX) does not match the request in Register B. JUMP to 44BBH, which follows a 10H link to another control block, sends an open file's control block to 47AEH, or calls the driver address in IX+01H/IX+02H.
4036-403C
DEFS 7
Keyboard Image (ROM)
The ROM's keyboard work area, one byte for each of the keyboard rows 3801H-3840H. Not in the file; the TRSDOS 2.3 keyboard scan at 43D8H keeps the keys that were down at the last scan here (Register Pair HL = 4036H plus the row).
403D
DEFS 1
Port FFH Copy (ROM)
Not in the file and not used by SYS0/SYS. The ROM keeps its copy of the value last sent to port FFH here; TAPEDISK/CMD reads and writes it at 53DDH and 53E4H.

403EH - Version Byte and Clock Counters

Three bytes loaded at 403EH-4040H. 4041H-404AH (the time, the date and two memory limits) are not in the file; the start-up code sets 4047H-404AH and the time and date start at whatever the RAM held.

403E
DEFB 22H 22
DOS Version Byte
22H. BASIC/CMD compares 403EH with 22H at 5C68H and does not start when the byte is lower. Nothing in SYS0/SYS reads it.
403F
DEFB 01H 01
Spare Byte
01H. No routine in SYS0/SYS, the overlays SYS1/SYS-SYS6/SYS or BASIC/CMD, FORMAT/CMD, BACKUP/CMD and TAPEDISK/CMD reads or writes 403FH.
4040
DEFB 00H 00
Clock Tick Counter
00H at load. 4574H-4577H add 1 on every clock interrupt and 4578H-457AH use its low three bits to pick which of the timer task slots 0-7 runs on this tick, so each of those slots runs once every eight ticks; the clock task in slot 7 counts five of its turns, 40 ticks, as one second. SYS2/SYS reads it at 50ABH.
4041
DEFS 1
Seconds
Binary, 00H-3BH. The once-a-second task at 45B2H (timer slot 7) adds 1 at 45C2H and wraps it to 00H at 60 (the limit 3CH at 45EFH), carrying into the minutes. 4CB7H displays it as the last two digits of HH:MM:SS.
4042
DEFS 1
Minutes
Binary, 00H-3BH. Incremented by 45C2H when 4041H wraps, and wrapped at 60 (the limit 3CH at 45F0H), carrying into the hours.
4043
DEFS 1
Hours
Binary, 00H-17H. Incremented by 45C2H when 4042H wraps, and wrapped at 24 (the limit 18H at 45F1H), carrying into the day at 4045H. 4CB7H starts its HH:MM:SS display here (Register Pair DE = 4043H).
4044
DEFS 1
Year
Binary, two digits. 45EDH adds 1 when the month passes 12; 45DBH-45E0H test its low two bits to allow 29 February when they are 00. Last of the three values 4CD2H displays as MM/DD/YY.
4045
DEFS 1
Day of the Month
Binary, from 01H. 45CCH adds 1 when the hours wrap; 45D3H-45D5H compare it with the length of the month from the table at 45F2H, and 45E1H sets it back to 01H when the month is over.
4046
DEFS 1
Month
Binary, 01H-0CH. 45CEH reads it to index the month length table; 45E4H adds 1 at the end of a month and 45E9H sets it back to 01H after December. 4CD2H starts its MM/DD/YY display here (Register Pair DE = 4046H).
4047-4048
DEFS 2
Lowest Free Address
The start-up code stores 5200H here at 4E06H-4E09H: the first address above the 4E00H-51FFH overlay area. Nothing else in SYS0/SYS reads it.
4049-404A
DEFS 2
Top of Memory
The highest RAM address found by the start-up code's test at 4E0CH-4E1DH, which tries FFFFH and then every address 1K (400H) lower until one keeps a written value: FFFFH, BFFFH or 7FFFH with 48K, 32K or 16K. BASIC/CMD reads it at 5C3DH, 5D0EH and 5D22H, and SYS3/SYS uses 404AH (its high byte) at 4E09H.

404BH - Interrupt Status, Enable Mask and Interrupt Task Vectors

The interrupt handler at 4518H reads the Model I interrupt latch at 37E0H, keeps the value in 404BH, masks it with the enable bits in 404CH and calls the routine whose address is in the two-byte slot for each set bit: bit 0 uses 404DH, bit 1 404FH and so on up to bit 7 at 405BH. Every slot starts as 4537H, a RET.

404B
DEFB 00H 00
Interrupt Latch Copy
4520H stores the byte read from 37E0H here on every interrupt: bit 7 is the source the clock routine 4560H services and bit 6 the source the disk routine 4669H services. Only the interrupt handler writes it; nothing reads it back.
404C
DEFB 00H 00
Interrupt Enable Mask
4522H ANDs the latch value with this byte, so only the sources whose bit is set here are serviced. 4E80H clears it, 4E94H sets bit 7 (clock, routine 4560H) and 4E9DH sets bit 6 (disk controller, routine 4669H).
404D-404E
DEFW 4537H 37 45
Interrupt Bit 0 Vector
4537H, the RET at the end of the interrupt handler. Called by 4538H when bit 0 of the masked latch value is set; nothing in TRSDOS 2.3 enables bit 0 in 404CH.
404F-4050
DEFW 4537H 37 45
Interrupt Bit 1 Vector
4537H, a RET. Called by 4538H when bit 1 of the masked latch value is set; nothing in TRSDOS 2.3 enables bit 1 in 404CH.
4051-4052
DEFW 4537H 37 45
Interrupt Bit 2 Vector
4537H, a RET. Called by 4538H when bit 2 of the masked latch value is set; nothing in TRSDOS 2.3 enables bit 2 in 404CH.
4053-4054
DEFW 4537H 37 45
Interrupt Bit 3 Vector
4537H, a RET. Called by 4538H when bit 3 of the masked latch value is set; nothing in TRSDOS 2.3 enables bit 3 in 404CH.
4055-4056
DEFW 4537H 37 45
Interrupt Bit 4 Vector
4537H, a RET. Called by 4538H when bit 4 of the masked latch value is set; nothing in TRSDOS 2.3 enables bit 4 in 404CH.
4057-4058
DEFW 4537H 37 45
Interrupt Bit 5 Vector
4537H, a RET. Called by 4538H when bit 5 of the masked latch value is set; nothing in TRSDOS 2.3 enables bit 5 in 404CH.
4059-405A
DEFW 4537H 37 45
Interrupt Bit 6 Vector (Disk Controller)
Loaded as 4537H (a RET); the start-up code stores 4669H here at 4E96H-4E99H, so a disk controller interrupt re-selects the drive and reads the status register at 37ECH, which clears the interrupt.
405B-405C
DEFW 4537H 37 45
Interrupt Bit 7 Vector (Clock)
Loaded as 4537H (a RET); the start-up code stores 4560H here at 4E8DH-4E90H, the clock tick routine that runs the timer task slots.

405DH - RAM Not Loaded by SYS0/SYS

405DH-42FFH is outside the SYS0/SYS file. Two parts of it matter to the resident DOS: the stack, which every TRSDOS 2.3 entry point (4E03H here, 4E20H in SYS1/SYS) sets to 41FCH, and the sector buffer at 4200H-42FFH that SYS0/SYS uses for directory and allocation table sectors.

405D-407F
DEFS 35
DEBUG Work Area
Not loaded or used by SYS0/SYS. SYS5/SYS (DEBUG) keeps its variables in this area: the two breakpoint entries at 405DH-405FH and 4060H-4062H (address and the byte replaced), whose first bytes also hold the display letter (405DH), the display mode (405EH) and the M address (4060H-4061H) between breakpoints; the display address at 4063H-4064H; and the program's saved registers at 4065H-407CH, with the Stack Pointer at 4079H-407AH and the program counter at 407BH-407CH.
4080-41FF
DEFS 384
Level II BASIC Work Area and DOS Stack
Not loaded or used by SYS0/SYS except as stack space: 4E03H sets the Stack Pointer to 41FCH, so the stack grows down from 41FBH. 4080H-41E4H is the ROM's own BASIC work area; the ROM jumps to the three-byte links at 41A6H-41E2H, which BASIC/CMD fills in.
4200-42FF
DEFS 256
System Sector Buffer
The ROM reads the boot sector here. SYS0/SYS uses it for every directory sector: 4B1EH points Register H at 42H, 4AC1H reads a directory sector here and 4AD6H writes it back. The start-up code at 4EC8H-4ECBH reads the allocation table sector (directory track sector 0) here and copies its AUTO command at 42E0H-42FFH to 4318H.

4300H - Drive Tables and System Variables

Eighteen bytes loaded at 4300H-4311H: the last head position and the directory track of each of the four drives, the drive that is selected now, the last file call, the overlay that is in memory and the system flags.

4300-4303
DEFB 11H x 4 11 11 11 11
Track Register Copy per Drive
One byte for each of drives 0-3, all 11H (track 17, the directory track) at load. When 4600H switches to another drive it stores the controller's track register (37EDH) into the old drive's byte and loads the new drive's byte into 37EDH, so each drive keeps its own head position.
4304-4307
DEFB 11H x 4 11 11 11 11
Directory Track per Drive
One byte for each of drives 0-3, all 11H (track 17) at load. 4B55H loads Register D from 4304H plus the drive number. When a directory sector is read with a normal data mark, 4B3EH-4B51H read byte 2 of the diskette's boot sector (track 0, sector 0) and store it here.
4308
DEFB 00H 00
Selected Drive Number
00H-03H, the drive 4600H last selected (00H at load). 4600H compares the requested drive in Register C with it; 46C3H stores FFH here after a disk error so that the next 4600H call selects the drive again and reloads its track register.
4309
DEFB 01H 01
Select Latch Value of the Selected Drive
01H at load (drive 0). 4621H stores the value built by 4639H (bit 0-3 for drive 0-3); 43ACH and 4669H write it to the drive select latch at 37E1H again before every status read, which keeps the selected drive's motor running.
430A-430B
DEFW 0000H 00 00
FCB Address of the Last File Call
489BH stores Register Pair DE here at the start of every file call through 4892H (read, write, position, rewind). SYS4/SYS loads Index Register IX from it at 4E7DH to name the file in an error message.
430C-430D
DEFW 0000H 00 00
Return Address of the Last File Call
4898H stores the caller's return address here at the start of every file call through 4892H. SYS4/SYS reads it at 4F0CH and subtracts 3 to show the address of the CALL in its REFERENCED AT message.
430E
DEFB 02H 02
Overlay in Memory
The RST 28H function code of the last overlay call. Bits 0-3 name the overlay that is in 4E00H-51FFH; 02H at load is overlay 2, SYS0/SYS itself, so the first call always reads the file. 4BAAH-4BB1H compare the new code's bits 0-3 with it and store the new code.
430F
DEFB 00H 00
System Flags
00H at load; 4E9FH-4EA1H set it to 01H. Bit 7 is the DEBUG flag: SYS1/SYS's DEBUG command sets it (516BH) and clears it (5184H); 4C0EH-4C12H enter DEBUG before a loaded program runs and 4C32H-4C38H refuse to load a program with access level 6 or 7 while it is set. Other modules test bit 5 (SYS6/SYS, FORMAT/CMD, BACKUP/CMD), bit 4 (SYS1/SYS) and bits 1-2 (BASIC/CMD).
4310
DEFB 00H 00
Spare Byte
00H. SYS0/SYS never uses it. The only access in the TRSDOS 2.3 files is SYS1/SYS's LD HL,(430FH) at 4E28H, which takes it as the high byte of a word.
4311
DEFB 00H 00
Spare Byte
00H. No routine in SYS0/SYS, the overlays SYS1/SYS-SYS6/SYS or BASIC/CMD, FORMAT/CMD, BACKUP/CMD and TAPEDISK/CMD reads or writes 4311H.
4312
Hook for RST 28H Codes Below 80H
4BA5H jumps here when the function code in Register A has bit 7 clear, for example the 01H that the ROM's keyboard routine passes at 0456H when BREAK is pressed. JUMP to 4BA0H, which clears Register A and returns. BASIC/CMD changes the address in 4313H-4314H to its own routine at 5D4DH (5C22H-5C25H) and puts the old one back at 5BB5H-5BB8H.
4315-4317
DEFS 3
BREAK Key Vector
Not in the file; 4E79H-4E7AH store 00H at 4315H. When 4315H holds C3H (JP) and BREAK is down during a clock interrupt, 454FH clears 4315H and leaves the interrupt by jumping to the address in 4316H-4317H with the interrupted address still on the stack. SYS1/SYS's DEBUG command sets it to JP 400FH (5170H-5175H); SYS5/SYS and BASIC/CMD set it as well. 4BEEH-4C04H disarm it while an overlay runs.
4318-4357
DEFS 64
Command Line Buffer
Not in the file. SYS1/SYS's DOS READY code reads the typed command into it (4E46H-4E4BH, up to 63 characters plus ENTER). The start-up code copies the 32-byte AUTO command from 42E0H to 4318H at 4ED9H-4EE2H and displays it before running it. TAPEDISK/CMD also uses it.
4358-435F
DEFS 8
Copy of the Keyboard Device Control Block
The start-up code copies the eight bytes of 4015H-401CH here at 4E57H-4E61H (after 4F23H has put 43D8H in its driver address). SYS1/SYS reads 4359H at 4E23H.
4360-4367
DEFS 8
Zeroed Bytes
The start-up code stores 00H into these eight bytes at 4E63H-4E68H, right after the keyboard control block copy. Nothing else in SYS0/SYS reads or writes them.
4368-4377
DEFS 16
FFH Bytes
The start-up code stores FFH into these sixteen bytes at 4E6AH-4E70H. Nothing else in SYS0/SYS reads or writes them.
4378-4397
DEFS 32
Unused
SYS0/SYS does not load or use 4378H-4397H. The start-up loop at 4E55H-4E74H would fill this area too if its JR NZ at 4E74H jumped back, but that JR has a displacement of 00H, so the loop body runs once.

4398H - Wait Until the Disk Controller Is Not Busy

Called with nothing in particular in the registers. Polls bit 0 (BUSY) of the WD1771 status register at 37ECH until it is clear. Unlike 4669H it does not write the drive select latch first. Changes Register A.

4398
LD A,(37ECH) 3A EC 37
Fetch the WD1771 status register from 37ECH into Register A. Bit 0 is BUSY while a command is running. 4610H calls here before reading the track register, and 439EH loops back here.
439B
BIT 0,A CB 47
Test bit 0 (BUSY) of Register A, the status byte just read from 37ECH.
439D
RET Z C8
If the Z FLAG is set (the controller is not busy), RETURN to the caller with Register A holding the status byte.
439E
LOOP BACK to 4398H to read the status register at 37ECH again until the BUSY bit clears.
43A0-43AB
DEFS 12
Unused
SYS0/SYS does not load or use 43A0H-43ABH; its load records skip from 439FH to 43ACH.

43ACH - Select the Drive and Read the Disk Status

Called by 4669H, which loops on it until the controller is not busy. Writes the saved select latch value to 37E1H, which also restarts the drive motor timer, then returns the WD1771 status in Register A.

43AC
LD A,(4309H) 3A 09 43
Fetch the select latch value of the selected drive from 4309H into Register A (01H, 02H, 04H or 08H, stored by 4621H).
43AF
LD (37E1H),A 32 E1 37
Store Register A (the select latch value from 4309H) to the drive select latch at 37E1H, selecting the drive again and keeping its motor turning.
43B2
LD A,(37ECH) 3A EC 37
Fetch the WD1771 status register from 37ECH into Register A; bit 0 is BUSY.
43B5
RET C9
RETURN to the caller (4669H) with Register A holding the status byte read from 37ECH.
43B6-43B7
DEFS 2
Unused
SYS0/SYS does not load or use 43B6H-43B7H.
43B8-43B9
DEFS 2
Keyboard Driver Address at Start-up
4E23H stores 43D8H here, the address 4F23H has just put into the keyboard control block at 4016H. Nothing else in SYS0/SYS reads it.
43BA-43BB
DEFS 2
Video Driver Address at Start-up
4E32H stores the word from 401EH-401FH here (the ROM's display driver address). Nothing else in SYS0/SYS reads it.
43BC-43BD
DEFS 2
Printer Driver Address at Start-up
4E41H stores the word from 4026H-4027H here (the ROM's printer driver address). Nothing else in SYS0/SYS reads it.
43BE-43BF
DEFS 2
Unused
SYS0/SYS does not load or write 43BEH-43BFH.
43C0-43C3
DEFS 4
Device List Entry 1 (KI)
4E2CH stores the word at 401BH-401CH here (the name KI from the keyboard control block) and 4E26H stores 43D8H (the keyboard driver) at 43C2H. SYS6/SYS's routine at 531DH lists each entry's two-letter name.
43C4-43C7
DEFS 4
Device List Entry 2 (DO)
4E3BH stores the word at 4023H-4024H here (the name DO from the video control block) and 4E35H stores the video driver address from 401EH at 43C6H.
43C8-43CB
DEFS 4
Device List Entry 3 (PR)
4E4AH stores the word at 402BH-402CH here (the name PR from the printer control block) and 4E44H stores the printer driver address from 4026H at 43CAH.
43CC
DEFS 1
End of the Device List
4E4DH-4E50H store 00H here. SYS6/SYS's listing loop at 5320H-5323H stops at the entry whose first name byte is 00H.
43CD-43D0
DEFS 4
Unused
SYS0/SYS does not load or write 43CDH-43D0H.

43D1H - Last Record Adjustment (Patch for 4768H)

A separate load record whose CALL replaces the LD A,(IX+08H) that SYS0/SYS first loads at 4768H. Entered from the position-to-end routine with Register Pair BC = the file's ending record number (ERN). When the last sector is only partly used it steps back to that sector, so the next write goes into it at the end byte.

43D1
LD A,(IX+08H) DD 7E 08
Fetch the end-of-file byte offset from (IX+08H) into Register A (Index Register IX = the open FCB). 00H means the last sector of the file is full.
43D4
OR A B7
OR Register A (the end-of-file byte offset from IX+08H) with itself to set the flags.
43D5
RET Z C8
If the Z FLAG is set (offset 00H: the file ends exactly at the end of a sector), RETURN with Register Pair BC still = ERN, so positioning goes to the start of a new sector.
43D6
DEC BC 0B
DECrement Register Pair BC (the ERN) by 1, to the record number of the last, partly used sector.
43D7
RET C9
RETURN to 476BH with Register A = the end-of-file byte offset and Register Pair BC = the last sector's record number; 4717H stores them as the byte offset and the next record number.

43D8H - Keyboard Scan (TRSDOS 2.3 Driver)

4F23H installs this as the keyboard driver address at 4016H. It is the ROM's row scan from 03E3H with an added debounce: a newly pressed key must still be down after a short delay, otherwise the scan reports no key. Returns Register A = 00H when no new key is down.

43D8
LD HL,4036H 21 36 40
Point Register Pair HL to 4036H, the ROM's keyboard image: one byte per keyboard row holding the keys that were down at the last scan.
43DB
LD BC,3801H 01 01 38
Point Register Pair BC to 3801H, the address of keyboard row 0 (@ A B C D E F G). Register C doubles as the row bit.
43DE
LD D,00H 16 00
Load Register D with 00H, the row counter: it counts the keyboard rows as they are scanned and is passed to the ROM at 03FBH.
43E0
LD A,(BC) 0A
Loop Start
Fetch the keys that are down in this row from the keyboard address in Register Pair BC (3801H, 3802H ... 3840H) into Register A. 43EBH loops back here.
43E1
LD E,A 5F
Copy the row's keys from Register A into Register E.
43E2
XOR (HL) AE
XOR Register A with the old image of this row at (HL) (Register Pair HL = 4036H plus the row): the bits that changed since the last scan.
43E3
LD (HL),E 73
Store the row's current keys (Register E) into the keyboard image at (HL) (Register Pair HL = 4036H plus the row number).
43E4
AND E A3
AND the changed bits in Register A with the current keys in Register E, keeping only keys that have just gone down.
43E5
If the NZ FLAG is set (a key in this row has just been pressed), JUMP to 43EFH to debounce it.
43E7
INC D 14
INCrement the row counter in Register D by 1.
43E8
INC L 2C
INCrement Register L by 1 so Register Pair HL points at the next row's byte in the keyboard image (4037H, 4038H ...).
43E9
RLC C CB 01
Rotate the row bit in Register C left, so Register Pair BC addresses the next keyboard row (3802H, 3804H ... 3840H, then 3880H).
43EB
Loop End
If the P FLAG is set (bit 7 of Register C is still clear, rows 3801H-3840H), JUMP back to 43E0H for the next row. The SHIFT row at 3880H is not scanned here.
43EE
RET C9
RETURN to the caller with Register A = 00H (no new key in any row).
43EF
LD E,A 5F
Copy the new key bits from Register A into Register E; Register D holds the row number and Register Pair BC the row address. 43E5H comes here.
43F0
PUSH BC C5
Save Register Pair BC (the keyboard row address) onto the stack.
43F1
LD BC,04DFH 01 DF 04
Load Register Pair BC with 04DFH, the delay count for the debounce wait.
43F4
GOSUB to ROM routine at 0060H, which counts Register Pair BC (04DFH) down to zero.
43F7
POP BC C1
Restore Register Pair BC (the keyboard row address) from the stack.
43F8
LD A,(BC) 0A
Fetch the same keyboard row again from the address in Register Pair BC into Register A.
43F9
AND E A3
AND the row in Register A with the new key bits in Register E: are those keys still down after the delay?
43FA
RET Z C8
If the Z FLAG is set (the key was released: contact bounce), RETURN with Register A = 00H, reporting no key.
43FB
JP 03FBH C3 FB 03
The key is still down: JUMP to the ROM keyboard driver at 03FBH, which continues after its own LD E,A at 03FAH with Register D = row number and Register E = column bits and turns them into a character code.
43FE-43FF
DEFS 2
Unused, Except 43FFH After a Disk Error
Not loaded by SYS0/SYS. After a disk error 46C3H stores FFH into 4308H, so the next call to 4600H saves the controller's track register into 4300H plus FFH, which is 43FFH. Nothing reads it back.

4400H - DOS Entry Point Table

Fixed addresses that SYS1/SYS-SYS6/SYS, BASIC/CMD and the utilities call. Each entry is either a JP into SYS0/SYS or LD A,code / RST 28H, which loads the overlay named by bits 0-3 of the code into 4E00H-51FFH and runs the function named by bits 4-6 (SYS1/SYS is overlay 3, SYS2/SYS 4, SYS3/SYS 5, SYS4/SYS 6, SYS5/SYS 7). The bytes between the entries are not part of the file.

4400
LD A,93H 3E 93
DOS READY
Load Register A with 93H: overlay 3 (SYS1/SYS), function 10H, its command prompt at 4E1FH. 402DH, 4EBFH and 4ED6H jump here.
4402
RST 28H EF
Execute RST 28H with Register A = 93H. 4BA2H drops this RST's return address, loads SYS1/SYS if 430EH shows another overlay, and calls it; SYS1/SYS resets the Stack Pointer to 41FCH at 4E20H and never returns here.
4403-4404
DEFS 2
Not Part of the File
SYS0/SYS has no load record for 4403H-4404H and no TRSDOS 2.3 code in SYS0/SYS, SYS1/SYS-SYS6/SYS or BASIC/CMD jumps here.
4405
LD A,B3H 3E B3
Run a Command Line
Load Register A with B3H: overlay 3 (SYS1/SYS), function 30H, its command interpreter at 4E4EH, which runs the command whose text Register Pair HL points to with 402DH (DOS READY) as the return address. 4EEAH (the AUTO command) and BASIC/CMD (572EH) come here.
4407
RST 28H EF
Execute RST 28H with Register A = B3H, loading SYS1/SYS and entering its command interpreter at 4E4EH with Register Pair HL still pointing to the command text.
4408
DEFS 1
Not Part of the File
SYS0/SYS has no load record for 4408H and no TRSDOS 2.3 routine jumps to it.
4409
Display an Error Message
JUMP to 44B0H with Register A = the error number (bits 0-5; bit 6 set = show only the message text, without the ERRCOD number and the FILE and REFERENCED AT lines; bit 7 set = return to the caller afterwards). Used by 4BD1H, 4BE5H, 4C0AH, 4ECEH, SYS6/SYS, BASIC/CMD and TAPEDISK/CMD.
440C
DEFS 1
Not Part of the File
SYS0/SYS has no load record for 440CH and no TRSDOS 2.3 routine jumps to it.
440D
Enter DEBUG
JUMP to 44B4H, which loads SYS5/SYS (overlay 7) with RST 28H code 87H. The same entry as the RST 30H vector at 400FH; BASIC/CMD calls it at 5740H.
4410
Add a Timer Task
JUMP to 4596H: store Register Pair DE (the address of a task control block whose first word is the task's routine) into timer slot Register A (00H-0BH). Slots 0-7 run once every eight clock ticks, slots 8-11 on every tick. 4EA9H, SYS1/SYS (519CH) and SYS6/SYS (52D0H) use it.
4413
Remove a Timer Task
JUMP to 4593H: put the empty task 45A3H (whose routine is the RET at 45A2H) into timer slot Register A. SYS1/SYS (5199H) and SYS6/SYS (52CDH) use it.
4416
Suspend the Running Task
JUMP to 45A5H. Called by a timer task: its return address (the instruction after the CALL) becomes the task's routine address, and control goes back to the clock routine, so the next turn of this slot continues right after the CALL.
4419
End the Running Task
JUMP to 458EH. Called by a timer task: the task's return address is dropped and the empty task 45A3H is put into the slot that is running, so the task never runs again.
441C
LD A,C3H 3E C3
Parse a File Specification
Load Register A with C3H: overlay 3 (SYS1/SYS), function 40H, its filespec scanner at 4F56H, which copies the name at Register Pair HL into the FCB at Register Pair DE. SYS6/SYS and BASIC/CMD call 441CH.
441E
RST 28H EF
Execute RST 28H with Register A = C3H; 4BA2H drops this RST's return address, so SYS1/SYS's function returns straight to the program that called 441CH.
441F
DEFS 1
Not Part of the File
SYS0/SYS has no load record for 441FH and no TRSDOS 2.3 routine jumps to it.
4420
LD A,A4H 3E A4
Open or Create a File (INIT)
Load Register A with A4H: overlay 4 (SYS2/SYS), function 20H at 4ED8H, which opens the file named in the FCB at Register Pair DE and creates a directory entry when it does not exist (error 18H from the open).
4422
RST 28H EF
Execute RST 28H with Register A = A4H; SYS2/SYS's function returns straight to the program that called 4420H with Register A = the error number (00H and the Z FLAG when the file is open).
4423
DEFS 1
Not Part of the File
SYS0/SYS has no load record for 4423H and no TRSDOS 2.3 routine jumps to it.
4424
LD A,94H 3E 94
Open an Existing File
Load Register A with 94H: overlay 4 (SYS2/SYS), function 10H at 4E12H, which finds the file named in the FCB at Register Pair DE (Register Pair HL = sector buffer, Register B = logical record length) and fills in the FCB. 4C1BH calls it to load a program.
4426
RST 28H EF
Execute RST 28H with Register A = 94H; SYS2/SYS returns straight to the caller of 4424H with Register A = 00H and the Z FLAG, or an error number such as 18H (FILE NOT IN DIRECTORY).
4427
DEFS 1
Not Part of the File
SYS0/SYS has no load record for 4427H and no TRSDOS 2.3 routine jumps to it.
4428
LD A,95H 3E 95
Close a File
Load Register A with 95H: overlay 5 (SYS3/SYS), function 10H at 4E6DH, which writes the buffer if it was changed and stores the end-of-file byte and ending record number from the FCB at Register Pair DE back into the directory.
442A
RST 28H EF
Execute RST 28H with Register A = 95H; SYS3/SYS returns straight to the caller of 4428H with Register A = 00H, or 26H (FILE NOT OPEN) when its checks at 4E04H-4E52H reject the FCB.
442B
DEFS 1
Not Part of the File
SYS0/SYS has no load record for 442BH and no TRSDOS 2.3 code in SYS0/SYS or the overlays uses it.
442C
LD A,A5H 3E A5
Kill a File
Load Register A with A5H: overlay 5 (SYS3/SYS), function 20H at 4FD0H, which removes the open file in the FCB at Register Pair DE from the directory and frees its granules.
442E
RST 28H EF
Execute RST 28H with Register A = A5H; SYS3/SYS returns straight to the caller of 442CH with the error number in Register A.
442F
DEFS 1
Not Part of the File
SYS0/SYS has no load record for 442FH and no TRSDOS 2.3 routine jumps to it.
4430
Load a Program
JUMP to 4C16H: open the program file named in the FCB at Register Pair DE with its buffer at 4D00H and load its load module records into memory. Returns Register Pair HL = the transfer address. SYS6/SYS (59A3H) calls it; 4C07H calls it from the run entry.
4433
Load and Run a Program
JUMP to 4C06H: load the program in the FCB at Register Pair DE through 4430H and jump to its transfer address (through DEBUG when bit 7 of 430FH is set). SYS1/SYS's command interpreter jumps here at 4E81H.
4436
Read
JUMP to 476DH: read the next record of the open file in the FCB at Register Pair DE (a whole sector into the FCB's buffer, or one logical record to Register Pair HL).
4439
Write
JUMP to 478BH: write the next record of the open file in the FCB at Register Pair DE (the FCB's buffer as a sector, or one logical record from Register Pair HL).
443C
Write with Verify
JUMP to 47A8H: the same as 4439H, but every sector written is read back afterwards (the operand at 485EH is set to 01H). SYS6/SYS stores this address at 550CH-550FH.
443F
Rewind
JUMP to 4756H: set the open file in the FCB at Register Pair DE back to record 0, byte 0.
4442
Position
JUMP to 4700H: set the open file in the FCB at Register Pair DE to the record number in Register Pair BC. BASIC/CMD calls it at 6389H.
4445
Back Up One Record
JUMP to 4737H: move the file in the FCB at Register Pair DE back by one logical record (or one sector when the file has no logical records). BASIC/CMD calls it at 6031H.
4448
Position to the End of the File
JUMP to 475FH: set the file in the FCB at Register Pair DE to its end, ready to add to it. SYS6/SYS calls it at 573AH.
444B-4466
DEFS 28
Not Part of the File
SYS0/SYS has no load record for 444BH-4466H and no TRSDOS 2.3 code in SYS0/SYS, the overlays or BASIC/CMD uses this area.
4467
Display a Message
JUMP to 44CFH: send the text at Register Pair HL to the display, up to and including a 0DH, or up to a 03H (not shown). Used throughout SYS1/SYS, SYS4/SYS and SYS6/SYS.
446A
Print a Message
JUMP to 44DFH: send the text at Register Pair HL to the printer, up to and including a 0DH, or up to a 03H (not printed).
446D
Put the Time into a Buffer
JUMP to 4CB7H: store the time from 4043H-4041H as eight characters HH:MM:SS at Register Pair HL.
4470
Put the Date into a Buffer
JUMP to 4CD2H: store the date from 4046H-4044H as eight characters MM/DD/YY at Register Pair HL.
4473
LD A,D3H 3E D3
Add a Default Extension
Load Register A with D3H: overlay 3 (SYS1/SYS), function 50H at 4FC0H, which inserts the extension at Register Pair HL into the filespec in the FCB at Register Pair DE when the filespec has none. SYS6/SYS calls it at 582AH.
4475
RST 28H EF
Execute RST 28H with Register A = D3H; SYS1/SYS's function returns straight to the caller of 4473H.
4476
LD A,E3H 3E E3
Parse Command Parameters
Load Register A with E3H: overlay 3 (SYS1/SYS), function 60H at 507DH, which reads a parameter list in parentheses at Register Pair HL against the parameter table at Register Pair DE. SYS6/SYS calls it.
4478
RST 28H EF
Execute RST 28H with Register A = E3H; SYS1/SYS's function returns straight to the caller of 4476H.
4479-447F
DEFS 7
Not Part of the File
SYS0/SYS has no load record for 4479H-447FH and no TRSDOS 2.3 routine uses this area.

4480H - Command FCB and the Overlay FCB

4480H-449FH is the 32-byte FCB that SYS1/SYS fills with the filespec of a typed command (4E52H); SYS0/SYS loads it with a placeholder text. 44A0H-44AFH is the FCB that the overlay loader at 4BA2H uses to read SYS1/SYS-SYS6/SYS into memory; its fields are filled in at 4BC1H-4BDCH.

4480-4496
DEFM "NAMENAME/EXT.PASSWORD:N" 4E 41 4D 45 4E 41 4D 45 2F 45 58 54 2E 50 41 53 53 57 4F 52 44 3A 4E
Placeholder Text
The layout of a filespec (name, extension, password, drive), loaded into the command FCB. SYS1/SYS overwrites it with the parsed command name before the FCB is used.
4497-449F
DEFB 20H x 9 20 20 20 20 20 20 20 20 20
Rest of the Command FCB
Nine spaces filling the command FCB to 449FH. SYS1/SYS's filespec scanner writes over them.
44A0
DEFB 80H 80
Overlay FCB + 00H: Open Flag
80H: bit 7 set marks the FCB as open, so the file routines (4892H) accept it when 4C8CH calls 481BH to read the next sector of an overlay.
44A1
DEFB 00H 00
Overlay FCB + 01H: Access Flags
4BC5H stores 00H here before every load: bit 7 clear (whole sectors, no logical records), bit 5 clear, access level 0 (bits 0-2).
44A2
DEFB 00H 00
Overlay FCB + 02H
00H. Not used by the SYS0/SYS file routines.
44A3-44A4
DEFW 4D00H 00 4D
Overlay FCB + 03H: Buffer Address
4D00H. 482DH reads every sector of an overlay to 4D00H-4DFFH, and 4CA3H takes the load module bytes from there.
44A5
DEFB 00H 00
Overlay FCB + 05H: Byte Offset
00H. Not used while loading, because 4C8CH keeps its own offset in Register C.
44A6
DEFB 00H 00
Overlay FCB + 06H: Drive
00H: the overlays are always read from drive 0, where 48DCH-49B4H find their sectors.
44A7
DEFB 00H 00
Overlay FCB + 07H: Directory Entry Locator
4BC1H stores the locator built from the overlay number here (bits 5-7 = entry offset in the sector, bits 0-4 = directory sector minus 2). Used if the file's extents have to be read from the directory again (49B9H).
44A8
DEFB 00H 00
Overlay FCB + 08H: End-of-File Byte
00H. Only used by 48B9H when the next record number reaches the ending record number, which 44ACH prevents.
44A9
DEFB 00H 00
Overlay FCB + 09H: Logical Record Length
00H (whole sectors).
44AA-44AB
DEFW 0000H 00 00
Overlay FCB + 0AH: Next Record Number
4BC8H-4BCAH clear it before every load, so the overlay is read from its first sector. 4823H adds 1 after each sector.
44AC-44AD
DEFW FFFFH FF FF
Overlay FCB + 0CH: Ending Record Number
FFFFH, so 48B9H never reports the end of the file while an overlay is read; the load stops at the overlay's transfer record.
44AE-44AF
DEFB 00H, 00H 00 00
Overlay FCB + 0EH: First Extent
The track and the granule byte (bits 5-7 granule, bits 0-4 count minus 1) of the overlay's first extent, copied from the directory entry's bytes 16H-17H at 4BDCH. 48DCH finds all of an overlay's sectors in this one extent; the following bytes 44B0H-44B1H are code and are read as the extent's running granule total.

44B0H - Error Message and DEBUG Entries

44B0H saves the error number and loads SYS4/SYS, which displays the message and then returns to the caller or exits through 4030H. 44B4H saves Register Pair AF and loads SYS5/SYS, the DEBUG monitor.

44B0
PUSH AF F5
Save Register Pair AF onto the stack: Register A holds the error number that SYS4/SYS will display (4409H comes here). SYS4/SYS's entry at 4E05H pops it.
44B1
LD A,86H 3E 86
Load Register A with 86H: overlay 6 (SYS4/SYS), the error message display.
44B3
RST 28H EF
Execute RST 28H with Register A = 86H. SYS4/SYS shows the message for the error number on the stack and returns to the caller only when bit 7 of that number is set; otherwise it jumps to 4030H.
44B4
PUSH AF F5
Save Register Pair AF onto the stack so DEBUG can show the program's Register A and flags. 400FH (RST 30H) and 440DH come here.
44B5
LD A,87H 3E 87
Load Register A with 87H: overlay 7 (SYS5/SYS), DEBUG.
44B7
RST 28H EF
Execute RST 28H with Register A = 87H; SYS5/SYS's entry at 4E00H-4E02H takes the saved Register Pair AF back off the stack.

44B8H - Device Dispatch (4033H)

Entered through 4033H when the ROM's driver entry at 03C2H finds that the type byte of the device control block in Index Register IX does not match the request in Register B (01H input, 02H output, 04H control) and Register C holds the character. A type of 10H links to another control block, a type above 10H (an open FCB) sends the request to the file routines, and anything else calls the driver.

44B8
PUSH HL E5
Save Register Pair HL (the address of the linked control block, from IX+01H/IX+02H) onto the stack. 44C6H loops back here for a type 10H link.
44B9
POP IX DD E1
Restore that address from the stack into Index Register IX, so Index Register IX now points to the linked control block.
44BB
LD L,(IX+01H) DD 6E 01
Fetch the low byte of the driver address (or link) from (IX+01H) into Register L (Index Register IX = the device control block). 4033H comes here.
44BE
LD H,(IX+02H) DD 66 02
Fetch the high byte from (IX+02H) (Index Register IX = the same control block) into Register H, so Register Pair HL = the driver address or the address of the linked control block.
44C1
LD A,(IX+00H) DD 7E 00
Fetch the device type byte from (IX+00H) into Register A (Index Register IX = the control block being dispatched).
44C4
CP 10H FE 10
Compare Register A (the type byte) against 10H, the type that links this control block to another one at the address in Register Pair HL.
44C6
If the Z FLAG is set (type 10H), LOOP BACK to 44B8H to follow the link in Register Pair HL.
44C8
If the NO CARRY FLAG is set (type above 10H, the open-file flag 80H of an FCB), JUMP to 47AEH to read or write one byte of that file.
44CB
LD A,B 78
Copy the request (Register B: 01H input, 02H output) into Register A.
44CC
CP 02H FE 02
Compare Register A (the request from Register B) against 02H, setting the flags the way the ROM's drivers expect them (the ROM's own entry does the same CP 02H at 03D4H).
44CE
JP (HL) E9
JUMP to the driver at Register Pair HL (from IX+01H/IX+02H) with Register C = the character; the driver returns to the ROM's 03DDH, which restores the registers.

44CFH - Display a Message (4467H)

Register Pair HL points to the text. Each character goes to the display through the ROM's 0033H; the text ends after a 0DH (displayed) or at a 03H (not displayed). Register Pair HL is returned unchanged.

44CF
PUSH HL E5
Save Register Pair HL (the start of the text) onto the stack so it is returned unchanged.
44D0
LD A,(HL) 7E
Loop Start
Fetch the next character from (HL) into Register A; Register Pair HL steps through the text. 44DBH loops back here.
44D1
CP 03H FE 03
Compare Register A (the character) against 03H, the end-of-text mark that is not displayed.
44D3
If the Z FLAG is set (03H), JUMP to 44DDH to finish.
44D5
GOSUB to ROM routine at 0033H to display the character in Register A at the cursor.
44D8
INC HL 23
INCrement Register Pair HL by 1 to the next character of the text.
44D9
CP 0DH FE 0D
Compare Register A (the character just displayed) against 0DH, ENTER.
44DB
Loop End
If the NZ FLAG is set (not 0DH), LOOP BACK to 44D0H for the next character at Register Pair HL.
44DD
POP HL E1
Restore Register Pair HL (the start of the text) from the stack. 44D3H comes here.
44DE
RET C9
RETURN to the caller with Register Pair HL = the start of the text and Register A = the last character (0DH or 03H).

44DFH - Print a Message (446AH)

The same as 44CFH, but each character goes to the printer through the ROM's 003BH. The text ends after a 0DH (printed) or at a 03H (not printed). Register Pair HL is returned unchanged.

44DF
PUSH HL E5
Save Register Pair HL (the start of the text) onto the stack so it is returned unchanged.
44E0
LD A,(HL) 7E
Loop Start
Fetch the next character from (HL) into Register A; Register Pair HL steps through the text. 44EBH loops back here.
44E1
CP 03H FE 03
Compare Register A (the character) against 03H, the end-of-text mark that is not printed.
44E3
If the Z FLAG is set (03H), JUMP to 44EDH to finish.
44E5
GOSUB to ROM routine at 003BH to send the character in Register A to the printer.
44E8
INC HL 23
INCrement Register Pair HL by 1 to the next character of the text.
44E9
CP 0DH FE 0D
Compare Register A (the character just printed) against 0DH, ENTER.
44EB
Loop End
If the NZ FLAG is set (not 0DH), LOOP BACK to 44E0H for the next character at Register Pair HL.
44ED
POP HL E1
Restore Register Pair HL (the start of the text) from the stack. 44E3H comes here.
44EE
RET C9
RETURN to the caller with Register Pair HL = the start of the text and Register A = the last character (0DH or 03H).
44EF-44FF
DEFS 17
Not Part of the File
SYS0/SYS has no load record for 44EFH-44FFH and no TRSDOS 2.3 code in SYS0/SYS or the overlays uses this area.

4500H - Timer Task Slots

Twelve two-byte slots, 4500H-4517H. Each holds the address of a task control block: a word with the task's routine address, followed by whatever the task keeps for itself (Index Register IX points to the block while the task runs). The clock routine at 4560H runs slots 8-11 on every clock tick and one of slots 0-7 per tick in turn. Every slot starts with 45A3H, the empty task.

4500-4501
DEFW 45A3H A3 45
Slot 0
45A3H, the empty task (its routine is the RET at 45A2H). Runs on every eighth clock tick when the tick counter at 4040H ends in 0. No TRSDOS 2.3 module puts a task here.
4502-4503
DEFW 45A3H A3 45
Slot 1
45A3H, the empty task. Runs on every eighth clock tick when the tick counter at 4040H ends in 1. No TRSDOS 2.3 module puts a task here.
4504-4505
DEFW 45A3H A3 45
Slot 2
45A3H, the empty task. Runs on every eighth clock tick when the tick counter at 4040H ends in 2. No TRSDOS 2.3 module puts a task here.
4506-4507
DEFW 45A3H A3 45
Slot 3
45A3H, the empty task. Runs on every eighth clock tick when the tick counter at 4040H ends in 3. No TRSDOS 2.3 module puts a task here.
4508-4509
DEFW 45A3H A3 45
Slot 4
45A3H, the empty task. Runs on every eighth clock tick when the tick counter at 4040H ends in 4. No TRSDOS 2.3 module puts a task here.
450A-450B
DEFW 45A3H A3 45
Slot 5
45A3H, the empty task. Runs on every eighth clock tick when the tick counter at 4040H ends in 5. No TRSDOS 2.3 module puts a task here.
450C-450D
DEFW 45A3H A3 45
Slot 6
45A3H, the empty task. Runs on every eighth clock tick when the counter at 4040H ends in 6. SYS6/SYS (52C5H-52D0H) puts the time display task 4CA9H here or takes it out again.
450E-450F
DEFW 45A3H A3 45
Slot 7
45A3H, the empty task. Runs on every eighth clock tick when the counter at 4040H ends in 7. The start-up code puts the clock task 45AFH here at 4EA4H-4EA9H.
4510-4511
DEFW 45A3H A3 45
Slot 8
45A3H, the empty task. Runs on every clock tick (4560H-4564H). No TRSDOS 2.3 module puts a task here.
4512-4513
DEFW 45A3H A3 45
Slot 9
45A3H, the empty task. Runs on every clock tick (4565H-4569H). No TRSDOS 2.3 module puts a task here.
4514-4515
DEFW 45A3H A3 45
Slot 10
45A3H, the empty task. Runs on every clock tick (456AH-456EH). No TRSDOS 2.3 module puts a task here.
4516-4517
DEFW 45A3H A3 45
Slot 11
45A3H, the empty task. Runs on every clock tick (456FH-4573H). SYS1/SYS's TRACE command (5191H-519CH) puts the address display task 4CD9H here or takes it out again.

4518H - Interrupt Handler (RST 38H)

The RST 38H vector at 4012H jumps here (set at 4E81H-4E8AH). Reads the Model I interrupt latch at 37E0H, keeps only the sources enabled in 404CH and calls the routine in the vector for each set bit (bit 0 at 404DH up to bit 7 at 405BH). Then, if BREAK is held and the BREAK vector at 4315H is armed, leaves through that vector instead of returning.

4518
PUSH HL E5
Save Register Pair HL of the interrupted program onto the stack.
4519
PUSH AF F5
Save Register Pair AF of the interrupted program onto the stack.
451A
LD A,(37E0H) 3A E0 37
Fetch the interrupt latch from 37E0H into Register A: one bit for each interrupt source that is asking for service.
451D
LD HL,404BH 21 4B 40
Point Register Pair HL to 404BH, the copy of the latch value; 404CH (the enable mask) and the vectors at 404DH-405CH follow it.
4520
LD (HL),A 77
Store the latch value (Register A) to (HL): Register Pair HL = 404BH, the latch copy.
4521
INC L 2C
INCrement Register L by 1, so Register Pair HL = 404CH, the interrupt enable mask.
4522
AND (HL) A6
AND Register A (the latch value) with the enable mask at (HL) (Register Pair HL = 404CH), keeping only the enabled sources: bit 7 (clock, set at 4E94H) and bit 6 (disk controller, set at 4E9DH).
4523
If the Z FLAG is set (no enabled source is asking), JUMP to 452DH to check BREAK.
4525
INC L 2C
Loop Start
INCrement Register L by 1, so Register Pair HL points at the low byte of the vector for the next bit (404DH for bit 0, 404FH for bit 1 ...). 452BH loops back here.
4526
RRA 1F
Rotate Register A (the remaining enabled latch bits) right: the lowest bit goes into the CARRY FLAG.
4527
If the CARRY FLAG is set (this source is asking), JUMP to 4538H to call its routine through the vector at Register Pair HL.
4529
INC L 2C
INCrement Register L by 1, so Register Pair HL points at the high byte of this vector. 454DH comes back here after a routine has run.
452A
OR A B7
OR Register A (the latch bits still to be handled) with itself to set the flags.
452B
Loop End
If the NZ FLAG is set (more bits are set in Register A), LOOP BACK to 4525H for the next vector.
452D
LD A,(3840H) 3A 40 38
Fetch keyboard row 6 from 3840H into Register A (ENTER, CLEAR, BREAK, the arrows and SPACE). 4523H comes here when no source was enabled.
4530
AND 04H E6 04
AND Register A (keyboard row 6) with 04H, keeping bit 2, the BREAK key.
4532
If the NZ FLAG is set (BREAK is down), JUMP to 454FH to see whether the BREAK vector at 4315H is armed.
4534
POP AF F1
Restore Register Pair AF of the interrupted program from the stack. 4554H comes here when the BREAK vector is not armed.
4535
POP HL E1
Restore Register Pair HL of the interrupted program from the stack.
4536
EI FB
Enable interrupts (EI) again; they take effect after the next instruction.
4537
RET C9
RETURN to the interrupted program. This RET is also the do-nothing routine in the eight interrupt vectors at 404DH-405CH.
4538
PUSH AF F5
Save Register Pair AF (Register A = the latch bits still to be handled) onto the stack. 4527H comes here for a source that is asking.
4539
PUSH BC C5
Save Register Pair BC of the interrupted program onto the stack, so the source's routine may change it.
453A
PUSH DE D5
Save Register Pair DE of the interrupted program onto the stack; 453EH-4545H use it to fetch the routine address.
453B
PUSH HL E5
Save Register Pair HL (the address of the low byte of this source's vector) onto the stack.
453C
PUSH IX DD E5
Save Index Register IX of the interrupted program onto the stack; the clock routine's timer tasks point it at their control blocks.
453E
LD DE,4547H 11 47 45
Load Register Pair DE with 4547H, the address the source's routine is to return to.
4541
PUSH DE D5
Save Register Pair DE (4547H) onto the stack as the routine's return address.
4542
LD E,(HL) 5E
Fetch the low byte of the routine address from the vector at (HL) into Register E (Register Pair HL = 404DH plus twice the bit number).
4543
INC L 2C
INCrement Register L by 1 to the high byte of the vector.
4544
LD D,(HL) 56
Fetch the high byte of the routine address from (HL) (Register Pair HL now = the vector's high byte) into Register D, so Register Pair DE = the routine (4560H for the clock, 4669H for the disk controller).
4545
EX DE,HL EB
Exchange Register Pairs DE and HL, so Register Pair HL = the routine address.
4546
JP (HL) E9
JUMP to the routine at Register Pair HL; it returns to 4547H.
4547
POP IX DD E1
Restore Index Register IX from the stack. The source's routine returns here.
4549
POP HL E1
Restore Register Pair HL (the address of the low byte of this source's vector) from the stack.
454A
POP DE D1
Restore Register Pair DE of the interrupted program from the stack (saved at 453AH).
454B
POP BC C1
Restore Register Pair BC of the interrupted program from the stack (saved at 4539H).
454C
POP AF F1
Restore Register Pair AF (Register A = the latch bits still to be handled) from the stack.
454D
JUMP to 4529H to move Register Pair HL past this vector and go on with the remaining bits in Register A.
454F
LD HL,4315H 21 15 43
BREAK Pressed
Point Register Pair HL to 4315H, the BREAK vector (C3H when armed, followed by the address at 4316H-4317H). 4532H comes here.
4552
XOR A AF
Set Register A to ZERO and clear all flags, ready to compare with the BREAK vector's first byte.
4553
CP (HL) BE
Compare Register A (00H) with the byte at (HL) (Register Pair HL = 4315H): the Z FLAG is set when the BREAK vector is not armed.
4554
If the Z FLAG is set (4315H holds 00H), JUMP to 4534H and return to the interrupted program.
4556
LD (HL),A 77
Store Register A (00H) to (HL) (Register Pair HL = 4315H), disarming the BREAK vector so that it fires only once.
4557
INC L 2C
INCrement Register L by 1, so Register Pair HL = 4316H.
4558
LD A,(HL) 7E
Fetch the low byte of the BREAK address from (HL) (Register Pair HL = 4316H) into Register A.
4559
INC L 2C
INCrement Register L by 1, so Register Pair HL = 4317H.
455A
LD H,(HL) 66
Fetch the high byte of the BREAK address from (HL) (Register Pair HL = 4317H) into Register H.
455B
LD L,A 6F
Copy the low byte from Register A into Register L, so Register Pair HL = the BREAK address (400FH when SYS1/SYS's DEBUG command armed it).
455C
POP AF F1
Restore Register Pair AF of the interrupted program from the stack.
455D
EX (SP),HL E3
Exchange Register Pair HL (the BREAK address) with the word at the top of the stack (Stack Pointer = the interrupted program's saved Register Pair HL): Register Pair HL is restored and the BREAK address is on the stack, above the interrupted program's address.
455E
EI FB
Enable interrupts (EI) again before leaving through the BREAK address.
455F
RET C9
RETURN, which jumps to the BREAK address; the address the interrupted program was at is left on the stack for it.

4560H - Clock Tick Routine

The vector for interrupt bit 7 (405BH) points here (set at 4E8DH-4E90H). Runs the tasks in slots 8-11 on every tick, adds 1 to the tick counter at 4040H and runs one of slots 0-7, chosen by the counter's low three bits. Called from 4538H with Register Pair HL, Register Pair DE, Register Pair BC, Register Pair AF and Index Register IX already saved.

4560
LD A,08H 3E 08
Load Register A with 08H, timer slot 8, the first of the four slots that run on every tick.
4562
GOSUB to 457BH to run the task in slot 8 (Register A = 08H).
4565
LD A,09H 3E 09
Load Register A with 09H, timer slot 9, which also runs on every clock tick.
4567
GOSUB to 457BH to run the task in slot 9 (Register A = 09H).
456A
LD A,0AH 3E 0A
Load Register A with 0AH, timer slot 10, which also runs on every clock tick.
456C
GOSUB to 457BH to run the task in slot 10 (Register A = 0AH).
456F
LD A,0BH 3E 0B
Load Register A with 0BH, timer slot 11 (where SYS1/SYS's TRACE puts 4CD9H).
4571
GOSUB to 457BH to run the task in slot 11 (Register A = 0BH).
4574
LD HL,4040H 21 40 40
Point Register Pair HL to 4040H, the clock tick counter.
4577
INC (HL) 34
INCrement the tick counter at (HL) (Register Pair HL = 4040H) by 1.
4578
LD A,(HL) 7E
Fetch the new tick count from (HL) (Register Pair HL = 4040H) into Register A.
4579
AND 07H E6 07
AND Register A (the tick count) with 07H: slot 0-7 for this tick. Execution falls into 457BH to run that slot's task.

457BH - Run the Task in a Timer Slot

Register A = the slot number (00H-0BH). The address of the slot is remembered in the operand at 45A6H for 4416H and 4419H, Index Register IX is pointed to the task control block and the task's routine is jumped to; the task's RET returns to whoever called 457BH.

457B
RLCA 07
Rotate Register A (the slot number) left: times 2, the offset of the slot in the table at 4500H. 4562H, 4567H, 456CH and 4571H call here; 4579H falls in.
457C
LD L,A 6F
Copy the offset from Register A into Register L.
457D
LD H,45H 26 45
Load Register H with 45H, so Register Pair HL = 4500H plus twice the slot number: the slot's address.
457F
LD (45A6H),HL 22 A6 45
Store Register Pair HL (the address of the running slot) into 45A6H, the operand of the LD HL at 45A5H. Self-Modifying Code
45A5H (suspend) and 458FH (end) use it to find the slot of the task that is running.
4582
LD E,(HL) 5E
Fetch the low byte of the task control block address from (HL) (Register Pair HL = the slot) into Register E.
4583
INC L 2C
INCrement Register L by 1 to the high byte of the slot.
4584
LD D,(HL) 56
Fetch the high byte from (HL) (Register Pair HL = the slot's second byte) into Register D, so Register Pair DE = the task control block (45A3H when the slot is empty).
4585
PUSH DE D5
Save Register Pair DE (the task control block address) onto the stack.
4586
POP IX DD E1
Restore it into Index Register IX, so Index Register IX points to the task control block while the task runs (45B2H uses IX+02H as its counter).
4588
EX DE,HL EB
Exchange Register Pairs DE and HL, so Register Pair HL = the task control block address.
4589
LD E,(HL) 5E
Fetch the low byte of the task's routine from (HL) (Register Pair HL = the control block, whose first word is the routine) into Register E.
458A
INC HL 23
INCrement Register Pair HL by 1 to the high byte.
458B
LD D,(HL) 56
Fetch the high byte from (HL) (Register Pair HL = the control block plus 1) into Register D, so Register Pair DE = the task's routine (45A2H, a RET, for the empty task).
458C
EX DE,HL EB
Exchange Register Pairs DE and HL, so Register Pair HL = the task's routine address.
458D
JP (HL) E9
JUMP to the task's routine at Register Pair HL with Index Register IX = its control block.

458EH - Timer Slot Maintenance (4410H-4419H)

Four entries share the code that stores a task control block address into a slot with interrupts off. 4596H stores Register Pair DE into slot Register A (4410H); 4593H stores the empty task 45A3H (4413H); 458EH does that for the slot that is running, dropping the caller's return address (4419H); 45A5H makes the running task continue after its CALL next time (4416H).

458E
POP DE D1
Restore the return address of the task's CALL 4419H from the stack into Register Pair DE, dropping it: the RET at 45A2H will go back to the clock routine instead of to the task.
458F
LD A,(45A6H) 3A A6 45
Fetch the low byte of the running slot's address from 45A6H into Register A: 00H-16H, twice the slot number (stored by 457FH).
4592
RRCA 0F
Rotate Register A right: halve it to the slot number of the running task.
4593
LD DE,45A3H 11 A3 45
Load Register Pair DE with 45A3H, the empty task control block. 4413H comes here with Register A = the slot to empty.
4596
CP 0CH FE 0C
Compare Register A (the slot number) against 0CH: only slots 00H-0BH exist. 4410H comes here with Register Pair DE = the task control block to install.
4598
RET NC D0
If the NO CARRY FLAG is set (slot 0CH or higher), RETURN without changing anything.
4599
RLCA 07
Rotate Register A (the slot number) left: times 2, the offset in the slot table.
459A
LD L,A 6F
Copy the offset from Register A into Register L.
459B
LD H,45H 26 45
Load Register H with 45H, so Register Pair HL = 4500H plus twice the slot number.
459D
DI F3
Disable interrupts (DI), so the clock routine cannot run this slot while it is half written. 45ADH comes here.
459E
LD (HL),E 73
Store the low byte of the new address (Register E) into (HL): Register Pair HL = the slot (or the task control block, when 45ADH comes here).
459F
INC L 2C
INCrement Register L by 1 to the second byte.
45A0
LD (HL),D 72
Store the high byte (Register D) into (HL) (Register Pair HL = the second byte), so the word now holds Register Pair DE.
45A1
EI FB
Enable interrupts (EI) again now that the slot or the control block holds the complete new address.
45A2
RET C9
RETURN to the caller. This RET is also the routine of the empty task: the word at 45A3H points here.
45A3-45A4
DEFW 45A2H A2 45
The Empty Task Control Block
Its routine address is 45A2H, the RET above. Every slot at 4500H-4517H starts with 45A3H, and 4593H puts it back into a slot to empty it.
45A5
LD HL,0000H 21 00 00
Suspend the Running Task (4416H)
Load Register Pair HL with the running slot's address, which 457FH stores into this operand at 45A6H. Self-Modifying Code
The loaded file holds 0000H.
45A8
LD E,(HL) 5E
Fetch the low byte of the running task's control block address from (HL) (Register Pair HL = the running slot) into Register E.
45A9
INC HL 23
INCrement Register Pair HL by 1 to the high byte of the slot.
45AA
LD D,(HL) 56
Fetch the high byte from (HL) (Register Pair HL = the slot's second byte) into Register D, so Register Pair DE = the task control block.
45AB
EX DE,HL EB
Exchange Register Pairs DE and HL, so Register Pair HL = the task control block (its first word is the routine address).
45AC
POP DE D1
Restore the return address of the task's CALL 4416H from the stack into Register Pair DE: the instruction after that CALL.
45AD
JUMP to 459DH to store Register Pair DE (the task's own continuation) as its routine address at Register Pair HL; the RET at 45A2H then returns to the clock routine.

45AFH - Clock Task (Timer Slot 7)

The task control block 45AFH-45B1H is put into slot 7 by the start-up code (4EA4H-4EA9H). Slot 7 runs once every eight ticks; the counter at IX+02H lets five of those runs pass, so the routine at 45B6H runs once for every 40 ticks and adds one second to the time at 4041H-4043H, carrying into the date at 4044H-4046H.

45AF-45B0
DEFW 45B2H B2 45
Clock Task + 00H: Routine Address
45B2H. 457BH-458DH jump to it with Index Register IX = 45AFH.
45B1
DEFB 05H 05
Clock Task + 02H: Countdown
05H. 45B2H subtracts 1 on each run and 45B6H reloads 05H when it reaches zero.
45B2
DEC (IX+02H) DD 35 02
DECrement the countdown at (IX+02H) by 1 (Index Register IX = 45AFH, so this is 45B1H).
45B5
RET NZ C0
If the NZ FLAG is set (fewer than five runs since the last second), RETURN to the clock routine.
45B6
LD (IX+02H),05H DD 36 02 05
Store 05H into (IX+02H) (Index Register IX = 45AFH, so 45B1H), starting the next count of five runs.
45BA
LD B,03H 06 03
Load Register B with 03H: three counters (seconds, minutes, hours) may carry.
45BC
LD HL,4041H 21 41 40
Point Register Pair HL to 4041H, the seconds.
45BF
LD DE,45EFH 11 EF 45
Point Register Pair DE to 45EFH, the limits table (60 seconds, 60 minutes, 24 hours, then the month lengths).
45C2
INC (HL) 34
Loop Start
INCrement the counter at (HL) by 1 (Register Pair HL = 4041H seconds, 4042H minutes or 4043H hours). 45C9H loops back here.
45C3
LD A,(DE) 1A
Fetch the counter's limit from (DE) into Register A (Register Pair DE = 45EFH, 45F0H or 45F1H: 3CH, 3CH or 18H).
45C4
SUB (HL) 96
SUBtract the counter at (HL) (Register Pair HL = 4041H, 4042H or 4043H) from Register A (its limit): zero only when the counter has just reached the limit.
45C5
RET NZ C0
If the NZ FLAG is set (no carry into the next counter), RETURN to the clock routine.
45C6
LD (HL),A 77
Store Register A (00H) to (HL) (Register Pair HL = the counter that reached its limit), wrapping it to zero.
45C7
INC L 2C
INCrement Register L by 1, so Register Pair HL points to the next counter (4042H, 4043H, then 4044H).
45C8
INC E 1C
INCrement Register E by 1, so Register Pair DE points to the next limit (45F0H, 45F1H, then 45F2H, the length of January).
45C9
Loop End
DECrement Register B and loop back to 45C2H while counters are left to carry into.
45CB
INC L 2C
INCrement Register L by 1: Register Pair HL goes from 4044H (the year) to 4045H, the day of the month.
45CC
INC (HL) 34
INCrement the day at (HL) (Register Pair HL = 4045H) by 1: the hours have wrapped past midnight.
45CD
INC L 2C
INCrement Register L by 1, so Register Pair HL = 4046H, the month.
45CE
LD A,(HL) 7E
Fetch the month (01H-0CH) from (HL) (Register Pair HL = 4046H) into Register A.
45CF
DEC L 2D
DECrement Register L by 1, so Register Pair HL = 4045H again (the day).
45D0
DEC A 3D
DECrement Register A (the month) by 1, to 00H-0BH.
45D1
ADD A,E 83
ADD Register E (F2H, the low byte of 45F2H, the length of January) to Register A (the month minus 1).
45D2
LD E,A 5F
Copy the sum from Register A into Register E, so Register Pair DE points to this month's length in the table at 45F2H-45FDH.
45D3
LD A,(DE) 1A
Fetch the number of days in this month from (DE) (Register Pair DE = 45F2H plus the month minus 1) into Register A (1CH = 28 for February).
45D4
CP (HL) BE
Compare Register A (the month length) with the day at (HL) (Register Pair HL = 4045H): the NO CARRY FLAG is set while the day is still within the month.
45D5
RET NC D0
If the NO CARRY FLAG is set (the day exists in this month), RETURN to the clock routine.
45D6
LD A,(HL) 7E
Fetch the day from (HL) (Register Pair HL = 4045H) into Register A; it is one past the month's length.
45D7
CP 1EH FE 1E
Compare Register A (the day) against 1EH (30): only 29 February can be below 30 here.
45D9
If the NO CARRY FLAG is set (day 30 or later), JUMP to 45E1H to start the next month.
45DB
DEC L 2D
DECrement Register L by 1, so Register Pair HL = 4044H, the year.
45DC
LD A,(HL) 7E
Fetch the year from (HL) (Register Pair HL = 4044H) into Register A.
45DD
INC L 2C
INCrement Register L by 1, so Register Pair HL = 4045H (the day) again.
45DE
AND 03H E6 03
AND Register A (the year) with 03H: 00H for a year divisible by four.
45E0
RET Z C8
If the Z FLAG is set (a leap year), RETURN and keep 29 February.
45E1
LD (HL),01H 36 01
Store 01H into (HL) (Register Pair HL = 4045H): the day becomes the 1st. 45D9H comes here.
45E3
INC L 2C
INCrement Register L by 1, so Register Pair HL = 4046H, the month.
45E4
INC (HL) 34
INCrement the month at (HL) (Register Pair HL = 4046H) by 1, since the last day of the month has passed.
45E5
LD A,(HL) 7E
Fetch the new month from (HL) (Register Pair HL = 4046H) into Register A.
45E6
SUB 0DH D6 0D
SUBtract 0DH (13) from Register A (the month): the CARRY FLAG is set for months 1-12.
45E8
RET C D8
If the CARRY FLAG is set (the month is 12 or less), RETURN to the clock routine.
45E9
LD (HL),01H 36 01
Store 01H into (HL) (Register Pair HL = 4046H): January.
45EB
DEC L 2D
DECrement Register L by 1, so Register Pair HL = 4045H.
45EC
DEC L 2D
DECrement Register L by 1, so Register Pair HL = 4044H, the year.
45ED
INC (HL) 34
INCrement the year at (HL) (Register Pair HL = 4044H) by 1, since December has passed.
45EE
RET C9
RETURN to the clock routine with the time and the date at 4041H-4046H updated.
45EF-45F1
DEFB 3CH, 3CH, 18H 3C 3C 18
Time Limits
60 seconds, 60 minutes and 24 hours, compared with 4041H-4043H by 45C3H-45C4H.
45F2-45FD
DEFB 1FH, 1CH, 1FH, 1EH, 1FH, 1EH, 1FH, 1FH, 1EH, 1FH, 1EH, 1FH 1F 1C 1F 1E 1F 1E 1F 1F 1E 1F 1E 1F
Month Lengths
31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30 and 31 days for January to December, indexed by 45CEH-45D3H. 29 February is allowed by 45D7H-45E0H when the year's low two bits are 00.
45FE-45FF
DEFS 2
Not Part of the File
SYS0/SYS has no load record for 45FEH-45FFH and no TRSDOS 2.3 code in SYS0/SYS or the overlays uses them.

4600H - Select a Drive

Register C = the drive number (00H-03H). When the drive differs from the one in 4308H, the controller's track register is saved for the old drive at 4300H plus its number and loaded from the new drive's byte, and the new select latch value is built. The drive is then selected through 37E1H; if the controller showed NOT READY, a delay lets the motor come up to speed. Register Pair HL is preserved.

4600
PUSH HL E5
Save Register Pair HL (the caller's buffer address) onto the stack. 4647H and 46C6H call here with Register C = the drive.
4601
LD HL,4308H 21 08 43
Point Register Pair HL to 4308H, the number of the drive that is selected now.
4604
LD A,(HL) 7E
Fetch the selected drive number from (HL) (Register Pair HL = 4308H) into Register A (FFH after a disk error).
4605
CP C B9
Compare Register A (the selected drive) with Register C (the drive wanted).
4606
If the NZ FLAG is set (another drive), JUMP to 460CH to switch drives.
4608
INC L 2C
INCrement Register L by 1, so Register Pair HL = 4309H, the select latch value of that drive.
4609
LD A,(HL) 7E
Fetch the select latch value from (HL) (Register Pair HL = 4309H) into Register A.
460A
JUMP to 4624H to select the same drive again with Register A = its latch value.
460C
LD (HL),C 71
Store the new drive number (Register C) into (HL) (Register Pair HL = 4308H).
460D
LD H,43H 26 43
Load Register H with 43H, the page of the per-drive track table at 4300H-4303H.
460F
LD L,A 6F
Copy the old drive number from Register A into Register L, so Register Pair HL = 4300H plus the old drive (43FFH when 4308H held FFH).
4610
GOSUB to 4398H to wait until the controller is not busy, so its track register is valid.
4613
LD A,(37EDH) 3A ED 37
Fetch the WD1771 track register from 37EDH into Register A: the track the old drive's head is on.
4616
LD (HL),A 77
Store that track (Register A) into (HL) (Register Pair HL = 4300H plus the old drive number).
4617
LD A,C 79
Copy the new drive number from Register C into Register A.
4618
LD L,A 6F
Copy the new drive number from Register A into Register L, so Register Pair HL = 4300H plus the new drive number.
4619
LD A,(HL) 7E
Fetch the new drive's last track from (HL) (Register Pair HL = 4300H plus the drive) into Register A.
461A
LD (37EDH),A 32 ED 37
Store Register A (the new drive's last track) into the track register at 37EDH, so the controller knows where that head is.
461D
LD A,C 79
Copy the new drive number from Register C into Register A.
461E
GOSUB to 4639H to turn the drive number in Register A into its select latch bit (01H, 02H, 04H or 08H).
4621
LD (4309H),A 32 09 43
Store Register A (the select latch value) into 4309H for 43ACH and 4669H.
4624
LD L,A 6F
Copy the select latch value from Register A into Register L. 460AH comes here for the drive already selected.
4625
LD A,(37ECH) 3A EC 37
Fetch the WD1771 status register from 37ECH into Register A; bit 7 is NOT READY.
4628
RLCA 07
Rotate Register A left: bit 7 (NOT READY) goes into the CARRY FLAG.
4629
LD A,L 7D
Copy the select latch value from Register L back into Register A.
462A
LD (37E1H),A 32 E1 37
Store Register A (the select latch value) to the drive select latch at 37E1H: the drive is selected and its motor started or kept running.
462D
If the NO CARRY FLAG is set (the controller was READY before this select), JUMP to 4637H without waiting.
462F
LD HL,0000H 21 00 00
Load Register Pair HL with 0000H: the delay counts 65,536 times while the motor comes up to speed.
4632
DEC HL 2B
Loop Start
DECrement Register Pair HL (the delay count) by 1. 4635H loops back here.
4633
LD A,H 7C
Copy the high byte of the count from Register H into Register A.
4634
OR L B5
OR Register A (the high byte) with Register L (the low byte): the Z FLAG is set when Register Pair HL has reached 0000H.
4635
Loop End
If the NZ FLAG is set (the count in Register Pair HL is not finished), LOOP BACK to 4632H.
4637
POP HL E1
Restore Register Pair HL (the caller's buffer address) from the stack. 462DH comes here.
4638
RET C9
RETURN to the caller with the drive selected and Register A = its select latch value.

4639H - Bit Number to Bit Mask

Register A = a bit number (only bits 0-2 are used). Builds the second byte of SET n,A in the operand at 4645H and executes it on a cleared Register A, returning Register A = 1 shifted left n times. 461EH uses it for the drive select latch value and 4A9DH for the granule bit in the allocation table.

4639
AND 07H E6 07
AND Register A (the drive or granule number) with 07H, keeping bits 0-2 (0-7).
463B
RLCA 07
Rotate Register A (the bit number) left: times 2.
463C
RLCA 07
Rotate Register A (the bit number) left again: times 4.
463D
RLCA 07
Rotate Register A left (times 8): the bit number now sits in bits 3-5, where a CB-prefixed instruction keeps it.
463E
OR C7H F6 C7
OR Register A with C7H: C7H plus 8 times n is the second byte of SET n,A.
4640
LD (4645H),A 32 45 46
Store Register A (the SET n,A byte) into 4645H, the second byte of the SET at 4644H. Self-Modifying Code
The file holds C7H (SET 0,A); at run time it is C7H, CFH, D7H ... FFH for bit 0-7.
4643
XOR A AF
Set Register A to ZERO and clear all flags, so the SET below leaves only the one bit in Register A.
4644
SET 0,A CB C7
Set bit n of Register A, where n is the bit number stored into 4645H by 4640H. Self-Modifying Code
The file shows SET 0,A; Register A becomes 01H, 02H, 04H ... 80H.
4646
RET C9
RETURN to the caller with Register A = 1 shifted left by the bit number.

4647H - Select the Drive and Seek to the Track

Register C = drive, Register D = track, Register E = sector. Selects the drive (4600H), waits until the controller is free, loads the sector register with Register E and the data register with Register D, and issues a SEEK (1FH) only when the head is not already on track Register D. 4686H calls it before every try of a sector read or write; 4732H calls it to move the head after positioning a file.

4647
GOSUB to 4600H to select the drive in Register C (switching the track register if it is another drive).
464A
GOSUB to 4669H to wait until the controller (selected again through 4309H) is not busy.
464D
LD A,(37EDH) 3A ED 37
Fetch the track register from 37EDH into Register A: the track the head is on now.
4650
CP D BA
Compare Register A (the current track) with Register D (the track wanted).
4651
LD (37EEH),DE ED 53 EE 37
Store Register Pair DE to 37EEH: Register E (the sector) goes into the sector register at 37EEH and Register D (the track) into the data register at 37EFH, where a SEEK takes its destination. LD does not change the flags of the compare.
4655
RET Z C8
If the Z FLAG is set (the head is already on track Register D), RETURN without seeking.
4656
LD A,1FH 3E 1F
Load Register A with 1FH, the SEEK command, which moves the head to the track in the data register (Register D) and verifies it.
1771 FDC Command: 1FH (00011111)Function Description
Bit 7Bit 6Bit 5Bit 4Bit 3Bit 2Bit 1Bit 0Summary of Bits
00011111Command=Seek (bits 7-4 = 0001)
h = 1: head load and settle delay
V = 1: verify the track ID
r1 r0 = 11: 15 ms stepping rate
4658
PUSH AF F5
Issue a Controller Command
Save Register Pair AF (Register A = the command) onto the stack. 46CBH and 4661H come here with other commands.
4659
GOSUB to 4669H to wait until the controller is not busy before giving it a command.
465C
POP AF F1
Restore Register Pair AF (Register A = the command) from the stack.
465D
LD (37ECH),A 32 EC 37
Store the command (Register A) to the WD1771 command register at 37ECH.
4660
RET C9
RETURN to the caller; the controller is now carrying out the command.
4661
Unused Entry
GOSUB to 4658H to issue the command in Register A. No routine in SYS0/SYS or the other TRSDOS 2.3 files calls 4661H.
4664
PUSH AF F5
Save Register Pair AF onto the stack; with the next instructions this only passes time before the status register is read.
4665
POP AF F1
Restore Register Pair AF from the stack (delay, Register Pair AF unchanged).
4666
NOP 00
NOP (delay before the status register is valid after a command).
4667
NOP 00
NOP (delay before the status register is valid after a command).
4668
NOP 00
NOP (delay before the status register is valid after a command); execution falls into 4669H.

4669H - Wait Until the Controller Is Not Busy

Selects the drive again through 43ACH and polls BUSY (bit 0) of the status register until it clears. Also the routine of interrupt bit 6 (vector 4059H, set at 4E96H): reading the status register clears the disk controller's interrupt request.

4669
Loop Start
GOSUB to 43ACH, which writes the select latch value from 4309H to 37E1H and returns the WD1771 status from 37ECH in Register A. 466FH loops back here.
466C
BIT 0,A CB 47
Test bit 0 (BUSY) of Register A, the status byte from 37ECH.
466E
RET Z C8
If the Z FLAG is set (not busy), RETURN to the caller with Register A = the status byte.
466F
Loop End
LOOP BACK to 4669H while the controller is still busy.

4671H - Sector Read and Write Core

Called from the four entries at 46DDH-46F3H with Register A = the WD1771 command and four inline bytes after the CALL: the number of tries, the error number for status bit 0 (the error is that number plus the position of the lowest status error bit), and the two instructions that move a data byte. The CALL's return address is used only to find those bytes, so the routine returns to whoever called the entry. Register C = drive, Register D = track, Register E = sector, Register Pair HL = the 256-byte buffer. Returns Register A = 00H and the Z FLAG, or the error number and the NZ FLAG.

4671
LD (4693H),A 32 93 46
Store the command (Register A: 88H read, A8H write, A9H write with an FAH data mark) into 4693H, the operand of LD (HL),nn at 4692H. Self-Modifying Code
46DFH, 46E8H and 46F5H call here.
4674
EX (SP),HL E3
Exchange Register Pair HL (the buffer) with the top of the stack (Stack Pointer = the return address of the entry's CALL): Register Pair HL now points to the inline bytes and the buffer address is on the stack.
4675
PUSH BC C5
Save Register Pair BC (Register C = the drive) onto the stack.
4676
LD B,(HL) 46
Fetch the number of tries from (HL) (Register Pair HL = the first inline byte) into Register B (0AH for a read, 05H for a write or check read).
4677
INC HL 23
INCrement Register Pair HL by 1 to the second inline byte.
4678
LD A,(HL) 7E
Fetch the error number base from (HL) (Register Pair HL = the second inline byte) into Register A (01H for reads, 09H for writes).
4679
INC HL 23
INCrement Register Pair HL by 1 to the third inline byte.
467A
LD (46D3H),A 32 D3 46
Store the error number base (Register A) into 46D3H, the operand of LD A,nn at 46D2H. Self-Modifying Code
467D
LD A,(HL) 7E
Fetch the first data move instruction from (HL) (Register Pair HL = the third inline byte) into Register A.
467E
INC HL 23
INCrement Register Pair HL by 1 to the fourth inline byte.
467F
LD H,(HL) 66
Fetch the second data move instruction from (HL) (Register Pair HL = the fourth inline byte) into Register H.
4680
LD L,A 6F
Copy the first instruction from Register A into Register L, so Register Pair HL = the two instruction bytes.
4681
LD (46A7H),HL 22 A7 46
Store Register Pair HL into 46A7H-46A8H, the two bytes the transfer loop executes for each data byte. Self-Modifying Code
1AH 02H (LD A,(DE) / LD (BC),A) to read, 0AH 12H (LD A,(BC) / LD (DE),A) to write, 1AH 00H (LD A,(DE) / NOP) to check that a sector reads.
4684
POP HL E1
Restore Register Pair HL from the stack: the caller's Register Pair BC (Register C = the drive). The pointer past the inline bytes is discarded.
4685
EX (SP),HL E3
Exchange Register Pair HL with the top of the stack (Stack Pointer = the buffer address): Register Pair HL = the buffer and the caller's Register Pair BC is kept on the stack for 46DBH. Register B = the number of tries, Register C = the drive.
4686
Loop Start
GOSUB to 4647H to select drive Register C, seek to track Register D and load the sector register with Register E. 46CFH loops back here for another try.
4689
GOSUB to 4669H to wait until the seek is finished and the controller is not busy.
468C
PUSH BC C5
Save Register Pair BC (Register B = tries left, Register C = drive) onto the stack.
468D
PUSH DE D5
Save Register Pair DE (Register D = track, Register E = sector) onto the stack.
468E
PUSH HL E5
Save Register Pair HL (the buffer address) onto the stack.
468F
LD HL,37ECH 21 EC 37
Point Register Pair HL to 37ECH, the WD1771 command and status register.
4692
LD (HL),00H 36 00
Store the command into (HL) (Register Pair HL = 37ECH), starting the read or write. Self-Modifying Code
The operand at 4693H is 00H in the file; 4671H stores the command from the entry (88H, A8H or A9H) there.
4694
LD DE,37EFH 11 EF 37
Point Register Pair DE to 37EFH, the WD1771 data register.
4697
POP BC C1
Restore Register Pair BC from the stack: the buffer address (saved from Register Pair HL at 468EH).
4698
PUSH BC C5
Save Register Pair BC (the buffer address) onto the stack again; with the next two instructions this gives the controller time to start the command.
4699
POP BC C1
Restore Register Pair BC (the buffer address) from the stack.
469A
PUSH BC C5
Save Register Pair BC (the buffer address) onto the stack for 46B3H.
469B
DI F3
Disable interrupts (DI): the transfer must not be interrupted.
469C
JUMP to 46A1H to start polling the status register.
469E
RRCA 0F
Rotate Register A (the status byte) right: bit 0 (BUSY) goes into the CARRY FLAG. 46A4H comes here when no data byte is waiting.
469F
If the NO CARRY FLAG is set (no longer busy: the command has finished), JUMP to 46ADH.
46A1
LD A,(HL) 7E
Loop Start
Fetch the status register from (HL) (Register Pair HL = 37ECH) into Register A. 469CH and 46AAH come here.
46A2
BIT 1,A CB 4F
Test bit 1 (DATA REQUEST) of Register A, the status byte.
46A4
If the Z FLAG is set (no data byte waiting), JUMP to 469EH to check BUSY.
46A6
DI F3
Disable interrupts (DI) again before the data byte is moved (the transfer runs with interrupts off).
46A7
NOP 00
First data move instruction, stored here by 4681H. Self-Modifying Code
NOP in the file. LD A,(DE) for a read or check read: Register A = the byte from the data register (Register Pair DE = 37EFH); LD A,(BC) for a write: Register A = the next byte of the buffer (Register Pair BC).
46A8
NOP 00
Second data move instruction, stored here by 4681H. Self-Modifying Code
NOP in the file. LD (BC),A for a read: store Register A into the buffer at Register Pair BC; LD (DE),A for a write: Register A to the data register (Register Pair DE = 37EFH); NOP for a check read.
46A9
INC BC 03
INCrement Register Pair BC (the buffer pointer) by 1.
46AA
Loop End
JUMP back to 46A1H for the next byte.
46AD
EI FB
Enable interrupts (EI) again: the command has finished. 469FH comes here.
46AE
LD A,(HL) 7E
Fetch the final status from (HL) (Register Pair HL = 37ECH) into Register A.
46AF
AND 7CH E6 7C
AND Register A (the status) with 7CH, keeping bits 2-6, the error bits; bits 0-1 (BUSY and DATA REQUEST) and bit 7 (NOT READY) are dropped.
1771 Status after Read/Write Sector, AND 7CHFunction Description
Bit 7Bit 6Bit 5Bit 4Bit 3Bit 2Bit 1Bit 0Summary of Bits
01111100Bits kept by AND 7CH:
6: F9H/F8H mark (read), write protect
5: FAH/F8H mark (read), write fault
4: record not found
3: CRC error
2: lost data
46B1
LD (HL),D0H 36 D0
Store D0H (FORCE INTERRUPT with no condition) to (HL) (Register Pair HL = 37ECH), ending any command still in progress.
1771 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, no interrupt
46B3
POP HL E1
Restore Register Pair HL (the buffer address) from the stack.
46B4
POP DE D1
Restore Register Pair DE (Register D = track, Register E = sector) from the stack.
46B5
POP BC C1
Restore Register Pair BC (Register B = tries left, Register C = drive) from the stack.
46B6
If the Z FLAG is set (no error bit in the status), JUMP to 46DBH to return with Register A = 00H.
46B8
BIT 2,A CB 57
Test bit 2 (LOST DATA) of Register A, the masked status.
46BA
If the NZ FLAG is set (a data byte was missed), JUMP to 46CFH to try again without restoring the head.
46BC
CP 20H FE 20
Compare Register A (the masked status) against 20H: only bit 5, which a read sets for a sector with an FAH data mark.
46BE
If the Z FLAG is set (only bit 5), JUMP to 46D1H to return its error number at once: error 06H after a read, which 4B38H and the checks after a write treat as the mark of a system sector.
46C0
PUSH AF F5
Save Register Pair AF (Register A = the masked status) onto the stack.
46C1
LD A,FFH 3E FF
Load Register A with FFH, a drive number that never matches, for 4308H.
46C3
LD (4308H),A 32 08 43
Store Register A (FFH) into 4308H, so 4600H treats the drive as not selected and reloads its track register.
46C6
GOSUB to 4600H to select drive Register C again (the track register is saved into 43FFH and reloaded from 4300H plus the drive).
46C9
LD A,0BH 3E 0B
Load Register A with 0BH, the RESTORE command, which moves the head back to track 0.
1771 FDC Command: 0BH (00001011)Function Description
Bit 7Bit 6Bit 5Bit 4Bit 3Bit 2Bit 1Bit 0Summary of Bits
00001011Command=Restore (bits 7-4 = 0000)
h = 1: head load and settle delay
V = 0: no verify
r1 r0 = 11: 15 ms stepping rate
46CB
GOSUB to 4658H to wait until the controller is free and issue the RESTORE in Register A.
46CE
POP AF F1
Restore Register Pair AF (Register A = the masked status) from the stack.
46CF
Loop End
DECrement Register B (tries left) and loop back to 4686H, which seeks to track Register D again, while tries remain. 46BAH comes here after lost data.
46D1
LD B,A 47
Copy the masked status from Register A into Register B. 46BEH comes here.
46D2
LD A,00H 3E 00
Load Register A with the error number base stored into 46D3H by 467AH (01H for reads, 09H for writes). Self-Modifying Code
00H in the file.
46D4
RRC B CB 08
Loop Start
Rotate Register B (the status bits) right: the lowest bit goes into the CARRY FLAG. 46D9H loops back here.
46D6
If the CARRY FLAG is set (the lowest error bit is found), JUMP to 46DBH with Register A = the error number and the NZ FLAG set by the rotate.
46D8
INC A 3C
INCrement Register A (the error number) by 1 for each bit position passed.
46D9
Loop End
LOOP BACK to 46D4H to test the next status bit in Register B.
46DB
POP BC C1
Restore Register Pair BC (the caller's Register Pair BC) from the stack. 46B6H and 46D6H come here.
46DC
RET C9
RETURN to the program that called the entry (46DDH, 46E6H, 46EFH or 46F3H) with Register A = 00H and the Z FLAG, or Register A = the error number and the NZ FLAG. Read errors: 03H lost data, 04H CRC, 05H record not found, 06H FAH mark, 07H F9H mark; write errors: 0BH lost data, 0CH CRC, 0DH record not found, 0EH write fault, 0FH write protected.

46DDH - Sector Read and Write Entries

Each entry loads the WD1771 command into Register A and calls 4671H, followed by its four inline bytes. Register C = drive, Register D = track, Register E = sector, Register Pair HL = buffer. 46DDH reads a sector into the buffer, 46E6H writes the buffer with the normal FBH data mark, 46EFH writes it with an FAH data mark (directory and allocation table sectors), and 46F3H reads a sector without storing it, to check that it can be read.

46DD
LD A,88H 3E 88
Read a Sector
Load Register A with 88H, READ SECTOR. 483BH, 4B35H and 4B42H come here.
1771 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
b = 1: IBM sector length
E = 0: no head load delay
46DF
GOSUB to 4671H with Register A = 88H; the four bytes after this CALL are its parameters, and 4671H returns to the caller of 46DDH.
46E2-46E3
DEFB 0AH, 01H 0A 01
Read Parameters
0AH: ten tries. 01H: the error number base, so the read errors are 03H (lost data), 04H (CRC), 05H (record not found), 06H (FAH mark) and 07H (F9H mark).
46E4-46E5
DEFB 1AH, 02H 1A 02
Read Data Move
1AH = LD A,(DE) and 02H = LD (BC),A, copied to 46A7H-46A8H: each byte goes from the data register at 37EFH into the buffer.
46E6
LD A,A8H 3E A8
Write a Sector
Load Register A with A8H, WRITE SECTOR with the normal FBH data mark. 4855H comes here.
1771 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
b = 1: IBM sector length
E = 0: no head load delay
a1 a0 = 00: data mark FBH (normal)
46E8
GOSUB to 4671H with Register A = the write command (A8H, or A9H from 46F1H); the four bytes after this CALL are its parameters.
46EB-46EC
DEFB 05H, 09H 05 09
Write Parameters
05H: five tries. 09H: the error number base, so the write errors are 0BH (lost data), 0CH (CRC), 0DH (record not found), 0EH (write fault) and 0FH (write protected).
46ED-46EE
DEFB 0AH, 12H 0A 12
Write Data Move
0AH = LD A,(BC) and 12H = LD (DE),A, copied to 46A7H-46A8H: each byte goes from the buffer to the data register at 37EFH.
46EF
LD A,A9H 3E A9
Write a System Sector
Load Register A with A9H, WRITE SECTOR with an FAH data mark. 4ADFH and 4B0DH come here for directory and allocation table sectors; SYS2/SYS, SYS3/SYS and SYS6/SYS call it too.
1771 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
b = 1: IBM sector length
E = 0: no head load delay
a1 a0 = 01: data mark FAH
46F1
JUMP to 46E8H to call 4671H with the write parameters and Register A = A9H.
46F3
LD A,88H 3E 88
Check That a Sector Reads
Load Register A with 88H, READ SECTOR. Called after a write: by 4860H when verify is on, and by 4AE4H and 4B12H (which expect error 06H for an FAH mark).
1771 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
b = 1: IBM sector length
E = 0: no head load delay
46F5
GOSUB to 4671H with Register A = 88H; the four bytes after this CALL are its parameters.
46F8-46F9
DEFB 05H, 01H 05 01
Check Read Parameters
05H: five tries. 01H: the read error number base (03H-07H).
46FA-46FB
DEFB 1AH, 00H 1A 00
Check Read Data Move
1AH = LD A,(DE) and 00H = NOP, copied to 46A7H-46A8H: each byte is taken from the data register at 37EFH and dropped, so the buffer is not changed.
46FC-46FF
DEFS 4
Not Part of the File
SYS0/SYS has no load record for 46FCH-46FFH and no TRSDOS 2.3 routine in SYS0/SYS or the overlays uses it.

4700H - Position (4442H)

Register Pair DE = an open FCB, Register Pair BC = the record number. With logical records (bit 7 of FCB+01H set) the record number is multiplied by the record length to find the sector and the byte offset in it; otherwise Register Pair BC is the sector. The changed buffer is written first, the sector becomes the next record number with bit 5 of FCB+01H set (load the buffer before the next byte), and the sector's address is worked out (allocating space when needed) and the head moved there. Bit 6 of FCB+01H is set, so later writes inside the file do not move its end.

4700
GOSUB to 4892H, which checks that the FCB at Register Pair DE is open (error 26H otherwise), saves Register Pair DE in 430AH and the caller's return address in 430CH, points Index Register IX at the FCB and arranges for the caller's Register Pair BC, Register Pair DE, Register Pair HL and Index Register IX to be restored at the end. Returns Register A = 00H.
4703
SET 6,(IX+01H) DD CB 01 F6
Set bit 6 of (IX+01H) (Index Register IX = the FCB): the file has been positioned, so 47FBH and 4869H leave its end where it is when a write falls inside the file.
4707
BIT 7,(IX+01H) DD CB 01 7E
Test bit 7 of (IX+01H) (Index Register IX = the FCB): set when the file was opened with a logical record length.
470B
If the Z FLAG is set (whole sectors), JUMP to 4717H with Register Pair BC = the sector and Register A = 00H (byte offset 0).
470D
LD H,B 60
Copy the high byte of the record number from Register B into Register H.
470E
LD L,C 69
Copy the low byte from Register C into Register L, so Register Pair HL = the record number.
470F
LD A,(IX+09H) DD 7E 09
Fetch the logical record length from (IX+09H) (Index Register IX = the FCB) into Register A (00H means 256).
4712
GOSUB to 4B6AH to multiply Register Pair HL (the record number) by Register A (the record length): it returns Register Pair HL = the sector number and Register A = the byte offset in that sector.
4715
LD B,H 44
Copy the sector number's high byte from Register H into Register B.
4716
LD C,L 4D
Copy its low byte from Register L into Register C, so Register Pair BC = the sector number.
4717
LD (IX+05H),A DD 77 05
Set the Position
Store Register A (the byte offset) into (IX+05H) (Index Register IX = the FCB). 470BH, 4751H, 4754H, 475DH and 476BH come here with Register Pair BC = the sector.
471A
PUSH BC C5
Save Register Pair BC (the sector number) onto the stack.
471B
GOSUB to 4878H, which writes the FCB's buffer back first when it holds logical records and has been changed (bits 7 and 4 of IX+01H).
471E
POP BC C1
Restore Register Pair BC (the sector number) from the stack.
471F
RET NZ C0
If the NZ FLAG is set (writing the buffer failed), RETURN with Register A = the error number.
4720
LD (IX+0AH),C DD 71 0A
Store Register C (the sector number's low byte) into (IX+0AH), the low byte of the next record number (Index Register IX = the FCB).
4723
LD (IX+0BH),B DD 70 0B
Store Register B (the high byte) into (IX+0BH) (Index Register IX = the FCB), so the next record number = the sector to read.
4726
SET 5,(IX+01H) DD CB 01 EE
Set bit 5 of (IX+01H) (Index Register IX = the FCB): the buffer does not hold this sector, so the next byte access reads it first.
472A
GOSUB to 48B9H to compare the new position with the end of the file (ending record number at IX+0CH, end-of-file byte at IX+08H).
472D
RET NZ C0
If the NZ FLAG is set (Register A = 1CH, at the end, or 1DH, past the end), RETURN with that number.
472E
GOSUB to 48DCH to find the track (Register D) and sector (Register E) of the next record number, reading the directory or allocating granules when the FCB's extents do not cover it.
4731
RET NZ C0
If the NZ FLAG is set (directory, allocation table or disk space error), RETURN with Register A = the error number.
4732
GOSUB to 4647H to select drive Register C and move the head to track Register D now, before the sector is needed.
4735
XOR A AF
Set Register A to ZERO and clear all flags: no error.
4736
RET C9
RETURN through 48B3H (stored by 4892H) to the caller with Register A = 00H and the Z FLAG.

4737H - Back Up One Record (4445H)

Register Pair DE = an open FCB. Without logical records the next record number is reduced by one sector. With logical records the byte offset is reduced by the record length, and the record number by one when the offset goes below zero. The routine takes the next record number as the sector the current byte is in, which holds while bit 5 of FCB+01H is set (the buffer is used up); with bit 5 clear the next record number is one past the buffered sector, so the position set is one sector further on.

4737
GOSUB to 4892H: check that the FCB at Register Pair DE is open, point Index Register IX at it and save the caller's registers. Returns Register A = 00H.
473A
LD C,(IX+0AH) DD 4E 0A
Fetch the low byte of the next record number from (IX+0AH) (Index Register IX = the FCB) into Register C.
473D
LD B,(IX+0BH) DD 46 0B
Fetch the high byte from (IX+0BH) (Index Register IX = the FCB) into Register B, so Register Pair BC = the next record number.
4740
BIT 7,(IX+01H) DD CB 01 7E
Test bit 7 of (IX+01H) (Index Register IX = the FCB): set when the file has a logical record length.
4744
If the Z FLAG is set (whole sectors), JUMP to 4753H to step back one sector with byte offset 00H (Register A = 00H from 4892H).
4746
LD A,(IX+09H) DD 7E 09
Fetch the logical record length from (IX+09H) (Index Register IX = the FCB) into Register A.
4749
OR A B7
OR Register A (the record length) with itself to set the flags.
474A
If the Z FLAG is set (record length 00H = 256, one sector per record), JUMP to 4753H to step back one sector with offset 00H.
474C
NEG ED 44
NEGate Register A: 256 minus the record length.
474E
ADD A,(IX+05H) DD 86 05
ADD the byte offset at (IX+05H) (Index Register IX = the FCB) to Register A: the offset minus the record length; the CARRY FLAG is set when the offset is at least one record length.
4751
If the CARRY FLAG is set (the previous record starts in the same sector), JUMP to 4717H with Register A = the new offset and Register Pair BC = the next record number unchanged.
4753
DEC BC 0B
DECrement Register Pair BC (the next record number) by 1: the previous record starts in the sector before. 4744H and 474AH come here.
4754
JUMP to 4717H to set Register Pair BC as the next record number and Register A as the byte offset.

4756H - Rewind (443FH)

Register Pair DE = an open FCB. Sets the next record number to 0 and the byte offset to 0 through 4717H.

4756
GOSUB to 4892H: check that the FCB at Register Pair DE is open, point Index Register IX at it and save the caller's registers.
4759
LD BC,0000H 01 00 00
Load Register Pair BC with 0000H: record 0, the first sector of the file.
475C
XOR A AF
Set Register A to ZERO and clear all flags: byte offset 0.
475D
JUMP to 4717H to make Register Pair BC (0000H) the next record number with Register A (00H) as the byte offset.

475FH - Position to the End of the File (4448H)

Register Pair DE = an open FCB. Takes the ending record number from FCB+0CH and the end-of-file byte from FCB+08H; the CALL 43D1H (a later load record over the original LD A,(IX+08H)) steps back to the last sector when it is only partly used. The result is set through 4717H, so a following write adds to the file.

475F
GOSUB to 4892H: check that the FCB at Register Pair DE is open, point Index Register IX at it and save the caller's registers.
4762
LD C,(IX+0CH) DD 4E 0C
Fetch the low byte of the ending record number from (IX+0CH) (Index Register IX = the FCB) into Register C.
4765
LD B,(IX+0DH) DD 46 0D
Fetch the high byte from (IX+0DH) (Index Register IX = the FCB) into Register B, so Register Pair BC = the ending record number (the number of sectors in the file).
4768
GOSUB to 43D1H: Register A = the end-of-file byte from IX+08H, and Register Pair BC is reduced by 1 when that byte is not 00H. The file first loads DD 7E 08 (LD A,(IX+08H)) here; a later load record of SYS0/SYS replaces it with this CALL.
476B
JUMP to 4717H with Register Pair BC = the sector and Register A = the byte offset where the file ends.

476DH - Read (4436H)

Register Pair DE = an open FCB. Without logical records the next sector is read into the FCB's buffer (480EH). With logical records, one record of FCB+09H bytes is read byte by byte (47BBH) into the memory at Register Pair HL.

476D
GOSUB to 4892H: check that the FCB at Register Pair DE is open, point Index Register IX at it and save the caller's registers.
4770
GOSUB to 4878H, which writes the buffer back first when it holds logical records and has been changed.
4773
RET NZ C0
If the NZ FLAG is set (that write failed), RETURN with Register A = the error number.
4774
BIT 7,(IX+01H) DD CB 01 7E
Test bit 7 of (IX+01H) (Index Register IX = the FCB): set when the file has a logical record length.
4778
If the Z FLAG is set (whole sectors), JUMP to 480EH to read the next sector into the FCB's buffer.
477B
LD B,(IX+09H) DD 46 09
Fetch the logical record length from (IX+09H) (Index Register IX = the FCB) into Register B, the byte count (00H counts 256).
477E
PUSH HL E5
Loop Start
Save Register Pair HL (the next byte of the caller's record area) onto the stack. 4788H loops back here.
477F
PUSH BC C5
Save Register Pair BC (Register B = bytes left) onto the stack.
4780
GOSUB to 47BBH to fetch the next byte of the file into Register A (reading the next sector when the buffer is used up).
4783
POP BC C1
Restore Register Pair BC (Register B = bytes left) from the stack.
4784
POP HL E1
Restore Register Pair HL (the record area pointer) from the stack.
4785
RET NZ C0
If the NZ FLAG is set (end of file or a disk error), RETURN with Register A = the error number.
4786
LD (HL),A 77
Store the byte (Register A) into the caller's record area at (HL) (Register Pair HL = the record area pointer).
4787
INC HL 23
INCrement Register Pair HL (the record area pointer) by 1.
4788
Loop End
DECrement Register B and loop back to 477EH until the whole record has been read.
478A
RET C9
RETURN through 48B3H with Register A = 00H and the Z FLAG (from 47BBH).

478BH - Write (4439H) and Write with Verify (443CH)

Register Pair DE = an open FCB. The operand at 485EH is set to 00H (no check) or 01H (read the sector back after writing). Without logical records the FCB's buffer is written as the next sector (483EH). With logical records, one record of FCB+09H bytes is taken from Register Pair HL and written byte by byte (47DAH).

478B
GOSUB to 4892H: check that the FCB at Register Pair DE is open, point Index Register IX at it and save the caller's registers. Returns Register A = 00H.
478E
LD (485EH),A 32 5E 48
Store Register A into 485EH, the operand of LD A,nn at 485DH: 00H from 478BH (no check) or 01H from 47A8H (read back after writing). Self-Modifying Code
SYS6/SYS also writes it at 60D5H.
4791
BIT 7,(IX+01H) DD CB 01 7E
Test bit 7 of (IX+01H) (Index Register IX = the FCB): set when the file has a logical record length.
4795
If the Z FLAG is set (whole sectors), JUMP to 483EH to write the FCB's buffer as the next sector.
4798
LD B,(IX+09H) DD 46 09
Fetch the logical record length from (IX+09H) (Index Register IX = the FCB) into Register B, the byte count (00H counts 256).
479B
LD A,(HL) 7E
Loop Start
Fetch the next byte of the record from (HL) (Register Pair HL = the caller's record area) into Register A. 47A5H loops back here.
479C
INC HL 23
INCrement Register Pair HL (the record area pointer) by 1.
479D
PUSH HL E5
Save Register Pair HL (the record area pointer) onto the stack.
479E
PUSH BC C5
Save Register Pair BC (Register B = bytes left) onto the stack.
479F
GOSUB to 47DAH to put the byte in Register A into the file (writing the sector when the buffer is full).
47A2
POP BC C1
Restore Register Pair BC (Register B = bytes left) from the stack.
47A3
POP HL E1
Restore Register Pair HL (the record area pointer) from the stack.
47A4
RET NZ C0
If the NZ FLAG is set (a disk or space error), RETURN with Register A = the error number.
47A5
Loop End
DECrement Register B and loop back to 479BH until the whole record has been written.
47A7
RET C9
RETURN through 48B3H with Register A = 00H and the Z FLAG.
47A8
Write with Verify (443CH)
GOSUB to 4892H: check that the FCB at Register Pair DE is open, point Index Register IX at it and save the caller's registers. Returns Register A = 00H.
47AB
INC A 3C
INCrement Register A (00H) by 1, to 01H: read each sector back after writing it.
47AC
JUMP to 478EH to store Register A (01H) into 485EH and write the record.

47AEH - One Byte to or from a File Used as a Device (44C8H)

Reached from the device dispatch at 44C8H when the control block in Index Register IX is an open FCB. Register B = the ROM's request (01H input, 02H output) and Register C = the character. Input reads the next byte of the file (47BBH); output writes Register C (47DAH). Bit 7 of FCB+01H is set, so the buffer is handled as a stream of bytes.

47AE
GOSUB to 4892H with Register Pair DE = the control block the ROM's 03C2H was given (the FCB itself when its type byte has bit 7 set): check that it is open (error 26H otherwise), point Index Register IX at it and save the registers.
47B1
SET 7,(IX+01H) DD CB 01 FE
Set bit 7 of (IX+01H) (Index Register IX = the FCB): byte by byte access through the FCB's buffer.
47B5
LD A,B 78
Copy the request from Register B into Register A (01H input, 02H output).
47B6
CP 02H FE 02
Compare Register A (the request) against 02H: the NO CARRY FLAG is set for output.
47B8
LD A,C 79
Copy the character to send from Register C into Register A (the flags of the compare are kept).
47B9
If the NO CARRY FLAG is set (output), JUMP to 47DAH to write the character in Register A to the file. Input falls into 47BBH.

47BBH - Read the Next Byte

Index Register IX = an open FCB (after 4892H). Checks the position against the end of the file, reads the next sector into the buffer when bit 5 of FCB+01H says the buffer is used up, takes the byte at the buffer plus the byte offset and adds 1 to the offset, setting bit 5 again when the offset wraps to 00H. Returns Register A = the byte and the Z FLAG, or the error number and the NZ FLAG.

47BB
GOSUB to 48B9H to compare the position with the end of the file. 4780H calls here.
47BE
RET NZ C0
If the NZ FLAG is set (Register A = 1CH, END OF FILE ENCOUNTERED, or 1DH), RETURN with that number.
47BF
BIT 5,(IX+01H) DD CB 01 6E
Test bit 5 of (IX+01H) (Index Register IX = the FCB): set when the buffer has to be loaded with the next sector first.
47C3
If the Z FLAG is set (the buffer holds the sector), JUMP to 47C9H.
47C5
GOSUB to 480EH to read the sector of the next record number into the buffer and add 1 to the next record number.
47C8
RET NZ C0
If the NZ FLAG is set (a read error or access denied), RETURN with Register A = the error number.
47C9
GOSUB to 4883H: Register Pair DE = the buffer address (IX+03H) plus the byte offset, Register C = the byte offset (IX+05H). 47C3H comes here.
47CC
XOR A AF
Set Register A to ZERO and clear all flags: the Z FLAG for success.
47CD
LD A,(DE) 1A
Fetch the byte from the buffer at (DE) (Register Pair DE = buffer plus byte offset) into Register A.
47CE
PUSH AF F5
Save Register Pair AF (Register A = the byte, Z FLAG set) onto the stack.
47CF
INC (IX+05H) DD 34 05
INCrement the byte offset at (IX+05H) (Index Register IX = the FCB) by 1.
47D2
If the NZ FLAG is set (the offset did not wrap), JUMP to 47D8H.
47D4
SET 5,(IX+01H) DD CB 01 EE
Set bit 5 of (IX+01H) (Index Register IX = the FCB): all 256 bytes of the buffer have been used, so the next byte needs the next sector.
47D8
POP AF F1
Restore Register Pair AF (Register A = the byte, Z FLAG set) from the stack. 47D2H comes here.
47D9
RET C9
RETURN to the caller with Register A = the byte read and the Z FLAG.

47DAH - Write the Next Byte

Register A = the byte, Index Register IX = an open FCB. When bit 5 of FCB+01H is set the sector at the next record number is read into the buffer first, so the rest of it is kept. The byte is stored at the buffer plus the byte offset, the buffer is marked as changed and the offset moved on; the end-of-file byte follows the offset when the write is at or past the end (or when the file has not been positioned). When the offset wraps to 00H the full buffer is written (483EH).

47DA
PUSH AF F5
Save Register Pair AF (Register A = the byte to write) onto the stack. 479FH and 47B9H come here.
47DB
BIT 5,(IX+01H) DD CB 01 6E
Test bit 5 of (IX+01H) (Index Register IX = the FCB): set when the buffer does not hold the sector at the next record number.
47DF
If the Z FLAG is set (the buffer holds the sector), JUMP to 47E9H.
47E1
GOSUB to 482DH to read the sector at the next record number into the buffer (allocating it if the file does not reach it yet) and clear bit 5.
47E4
If the Z FLAG is set (read without error), JUMP to 47E9H.
47E6
EX (SP),HL E3
Exchange Register Pair HL with the top of the stack (Stack Pointer = the saved Register Pair AF): Register Pair HL now holds the byte and flags, and Register Pair HL's old value is on the stack.
47E7
POP HL E1
Restore Register Pair HL from the stack, so Register Pair HL is back to its old value and the saved byte is dropped.
47E8
RET C9
RETURN with Register A = the error number from 482DH and the NZ FLAG.
47E9
GOSUB to 4883H: Register Pair DE = buffer plus byte offset, Register C = the byte offset (IX+05H). 47DFH and 47E4H come here.
47EC
POP AF F1
Restore Register Pair AF (Register A = the byte to write) from the stack.
47ED
LD (DE),A 12
Store the byte (Register A) into the buffer at (DE) (Register Pair DE = buffer plus byte offset).
47EE
INC DE 13
INCrement Register Pair DE by 1 (not used afterwards).
47EF
SET 4,(IX+01H) DD CB 01 E6
Set bit 4 of (IX+01H) (Index Register IX = the FCB): the buffer has been changed and must be written before another sector is loaded.
47F3
INC (IX+05H) DD 34 05
INCrement the byte offset at (IX+05H) (Index Register IX = the FCB) by 1.
47F6
GOSUB to 48B9H to compare the new position with the end of the file; it also loads Register C with the new byte offset.
47F9
If the NZ FLAG is set (the write reached or passed the end), JUMP to 4801H to move the end-of-file byte.
47FB
BIT 6,(IX+01H) DD CB 01 76
Test bit 6 of (IX+01H) (Index Register IX = the FCB): set when the file has been positioned (4703H).
47FF
If the NZ FLAG is set (positioned: the write is inside the file), JUMP to 4804H and leave the end-of-file byte alone.
4801
LD (IX+08H),C DD 71 08
Store Register C (the new byte offset) into (IX+08H), the end-of-file byte (Index Register IX = the FCB). 47F9H comes here.
4804
LD A,C 79
Copy the new byte offset from Register C into Register A. 47FFH comes here.
4805
OR A B7
OR Register A (the byte offset) with itself to set the flags.
4806
If the NZ FLAG is set (the buffer is not full yet), JUMP to 482BH to return with Register A = 00H.
4808
SET 5,(IX+01H) DD CB 01 EE
Set bit 5 of (IX+01H) (Index Register IX = the FCB): the buffer is full, the next byte goes into the next sector.
480C
JUMP to 483EH to write the full buffer to the disk.

480EH - Read and Write a Sector of a File

480EH reads the sector at the next record number into the FCB's buffer and adds 1 to the next record number (481BH is the same without the access check); 482DH only reads the sector; 483EH writes the buffer as the sector at the next record number (484BH without the access check), moves the ending record number when the write is at the end, and adds 1 to the next record number. Index Register IX = the open FCB throughout.

480E
LD A,(IX+01H) DD 7E 01
Fetch the flags from (IX+01H) (Index Register IX = the FCB) into Register A; bits 0-2 are the file's access level. 4778H and 47C5H come here.
4811
AND 07H E6 07
AND Register A with 07H, keeping the access level (0-7).
4813
CP 06H FE 06
Compare Register A (the access level) against 06H: levels 6 and 7 do not allow reading.
4815
If the CARRY FLAG is set (level 0-5), JUMP to 481BH to read.
4817
LD A,25H 3E 25
Load Register A with 25H, ILLEGAL ACCESS ATTEMPTED TO PROTECTED FILE.
4819
OR A B7
OR Register A (25H) with itself, setting the NZ FLAG.
481A
RET C9
RETURN with Register A = 25H and the NZ FLAG.
481B
GOSUB to 48B9H to compare the position with the end of the file. 4815H and 4C95H (the program loader) come here.
481E
RET NZ C0
If the NZ FLAG is set (Register A = 1CH or 1DH), RETURN with that number.
481F
GOSUB to 482DH to read the sector at the next record number into the FCB's buffer.
4822
RET NZ C0
If the NZ FLAG is set (a read, directory or space error), RETURN with Register A = the error number.
4823
INC (IX+0AH) DD 34 0A
INCrement the low byte of the next record number at (IX+0AH) (Index Register IX = the FCB) by 1. 486DH and 4876H come here after a sector has been written.
4826
If the NZ FLAG is set (no carry into the high byte), JUMP to 482BH.
4828
INC (IX+0BH) DD 34 0B
INCrement the high byte of the next record number at (IX+0BH) (Index Register IX = the FCB) by 1.
482B
XOR A AF
Set Register A to ZERO and clear all flags. 4806H and 4826H come here.
482C
RET C9
RETURN with Register A = 00H and the Z FLAG: the sector has been read or written and the next record number moved on.
482D
Read the Sector at the Next Record Number
GOSUB to 48DCH to find drive Register C, track Register D and sector Register E of the next record number. 47E1H and 481FH come here.
4830
RET NZ C0
If the NZ FLAG is set (directory, allocation table or space error), RETURN with Register A = the error number.
4831
RES 5,(IX+01H) DD CB 01 AE
Reset bit 5 of (IX+01H) (Index Register IX = the FCB): the buffer is about to hold the sector.
4835
LD L,(IX+03H) DD 6E 03
Fetch the low byte of the buffer address from (IX+03H) (Index Register IX = the FCB) into Register L.
4838
LD H,(IX+04H) DD 66 04
Fetch the high byte from (IX+04H) (Index Register IX = the FCB) into Register H, so Register Pair HL = the FCB's 256-byte buffer.
483B
JUMP to 46DDH to read drive Register C, track Register D, sector Register E into the buffer at Register Pair HL; it returns to this routine's caller.
483E
LD A,(IX+01H) DD 7E 01
Write the Buffer as the Next Sector
Fetch the flags from (IX+01H) (Index Register IX = the FCB) into Register A; bits 0-2 are the access level. 4795H, 480CH and 487FH come here.
4841
AND 07H E6 07
AND Register A with 07H, keeping the access level (0-7).
4843
CP 05H FE 05
Compare Register A (the access level) against 05H: levels 5-7 do not allow writing.
4845
If the CARRY FLAG is set (level 0-4), JUMP to 484BH to write.
4847
LD A,25H 3E 25
Load Register A with 25H, ILLEGAL ACCESS ATTEMPTED TO PROTECTED FILE.
4849
OR A B7
OR Register A (25H) with itself, setting the NZ FLAG.
484A
RET C9
RETURN with Register A = 25H and the NZ FLAG.
484B
GOSUB to 48DCH to find drive Register C, track Register D and sector Register E of the next record number, allocating space when the file does not reach it.
484E
RET NZ C0
If the NZ FLAG is set (directory, allocation table or DISK SPACE FULL error), RETURN with Register A = the error number.
484F
LD L,(IX+03H) DD 6E 03
Fetch the low byte of the buffer address from (IX+03H) (Index Register IX = the FCB) into Register L.
4852
LD H,(IX+04H) DD 66 04
Fetch the high byte from (IX+04H) (Index Register IX = the FCB) into Register H, so Register Pair HL = the buffer.
4855
GOSUB to 46E6H to write the buffer at Register Pair HL to drive Register C, track Register D, sector Register E.
4858
RET NZ C0
If the NZ FLAG is set (a write error 0BH-0FH), RETURN with Register A = the error number.
4859
RES 4,(IX+01H) DD CB 01 A6
Reset bit 4 of (IX+01H) (Index Register IX = the FCB): the buffer now matches the disk.
485D
LD A,00H 3E 00
Load Register A with the verify switch stored into 485EH by 478EH (00H for 4439H, 01H for 443CH). Self-Modifying Code
00H in the file; SYS6/SYS also sets it at 60D5H.
485F
OR A B7
OR Register A (the verify switch) with itself to set the flags.
4860
If the NZ FLAG is set (verify on), GOSUB to 46F3H to read the sector just written back from drive Register C, track Register D, sector Register E without storing it.
4863
RET NZ C0
If the NZ FLAG is set (the check read failed), RETURN with Register A = the read error number.
4864
GOSUB to 48B9H to compare the record just written with the end of the file; it leaves Register Pair HL = the next record number (IX+0AH).
4867
If the NZ FLAG is set (the write was at or past the end), JUMP to 486FH to move the ending record number.
4869
BIT 6,(IX+01H) DD CB 01 76
Test bit 6 of (IX+01H) (Index Register IX = the FCB): set when the file has been positioned.
486D
If the NZ FLAG is set (positioned: the write was inside the file), JUMP to 4823H to add 1 to the next record number and leave the end alone.
486F
INC HL 23
INCrement Register Pair HL (the record just written, from 48B9H) by 1. 4867H comes here.
4870
LD (IX+0CH),L DD 75 0C
Store Register L into (IX+0CH), the low byte of the ending record number (Index Register IX = the FCB).
4873
LD (IX+0DH),H DD 74 0D
Store Register H into (IX+0DH) (Index Register IX = the FCB), so the ending record number = the record just written plus 1.
4876
JUMP to 4823H to add 1 to the next record number.

4878H - Write the Buffer if It Holds Changed Records

Index Register IX = an open FCB. When the file has logical records (bit 7 of FCB+01H) and the buffer has been changed (bit 4), the buffer is written as the sector at the next record number through 483EH; otherwise Register A = 00H. SYS3/SYS also calls it at 4E71H before closing a file.

4878
LD A,(IX+01H) DD 7E 01
Fetch the flags from (IX+01H) (Index Register IX = the FCB) into Register A. 471BH and 4770H call here.
487B
AND 90H E6 90
AND Register A with 90H, keeping bit 7 (logical records) and bit 4 (buffer changed).
487D
CP 90H FE 90
Compare Register A against 90H: both bits set.
487F
If the Z FLAG is set (changed records in the buffer), JUMP to 483EH to write it.
4881
XOR A AF
Set Register A to ZERO and clear all flags: nothing to write.
4882
RET C9
RETURN with Register A = 00H and the Z FLAG: the buffer did not need writing.

4883H - Address of the Current Byte in the Buffer

Index Register IX = an open FCB. Returns Register C = the byte offset (FCB+05H) and Register Pair DE = the buffer address (FCB+03H) plus that offset.

4883
LD A,(IX+05H) DD 7E 05
Fetch the byte offset from (IX+05H) (Index Register IX = the FCB) into Register A. 47C9H and 47E9H call here.
4886
LD C,A 4F
Copy the byte offset from Register A into Register C.
4887
ADD A,(IX+03H) DD 86 03
ADD the low byte of the buffer address at (IX+03H) (Index Register IX = the FCB) to Register A (the offset).
488A
LD E,A 5F
Copy the sum into Register E, the low byte of the byte's address, from Register A.
488B
LD A,(IX+04H) DD 7E 04
Fetch the high byte of the buffer address from (IX+04H) (Index Register IX = the FCB) into Register A.
488E
ADC A,00H CE 00
ADD the CARRY FLAG from 4887H to Register A (ADC 00H).
4890
LD D,A 57
Copy the result into Register D, so Register Pair DE = the buffer address plus the byte offset, from Register A.
4891
RET C9
RETURN with Register Pair DE = the byte's address and Register C = the byte offset.

4892H - File Call Set-Up

Called as the first instruction of every file routine (4700H, 4737H, 4756H, 475FH, 476DH, 478BH, 47A8H, 47AEH and SYS3/SYS's close and kill) with Register Pair DE = the FCB. Refuses an FCB without bit 7 of byte 0 set (error 26H, FILE NOT OPEN). Otherwise it records the FCB and the caller's return address in 430AH and 430CH, points Index Register IX at the FCB, saves the caller's Register Pair BC, Register Pair DE, Register Pair HL and Index Register IX and pushes 48B3H, so the file routine's final RET restores them. SYS2/SYS enters at 4896H to set up an FCB that is not open yet.

4892
LD A,(DE) 1A
Fetch byte 0 of the FCB from (DE) (Register Pair DE = the FCB) into Register A; bit 7 is set while the file is open.
4893
RLCA 07
Rotate Register A left: bit 7 (open) goes into the CARRY FLAG.
4894
If the NO CARRY FLAG is set (the FCB is not open), JUMP to 48AEH to return error 26H.
4896
POP AF F1
Restore Register Pair AF from the stack: it now holds the address in the file routine just after its CALL 4892H, which is used at 48ABH to go back there. SYS2/SYS calls here at 4E12H and 4ED8H.
4897
EX (SP),HL E3
Exchange Register Pair HL with the top of the stack (Stack Pointer = the address the file routine's caller will return to): Register Pair HL = the caller's return address, and the caller's Register Pair HL is on the stack.
4898
LD (430CH),HL 22 0C 43
Store Register Pair HL (the caller's return address) into 430CH; SYS4/SYS shows it, minus 3, in its REFERENCED AT line.
489B
LD (430AH),DE ED 53 0A 43
Store Register Pair DE (the FCB address) into 430AH; SYS4/SYS uses it to name the file in an error message.
489F
EX (SP),HL E3
Exchange Register Pair HL with the top of the stack again (Stack Pointer = the caller's Register Pair HL): Register Pair HL is restored and the caller's return address is on the stack.
48A0
PUSH DE D5
Save Register Pair DE (the FCB address) onto the stack.
48A1
EX (SP),IX DD E3
Exchange Index Register IX with the top of the stack (Stack Pointer = the FCB address): Index Register IX = the FCB and the caller's Index Register IX is saved on the stack.
48A3
PUSH HL E5
Save the caller's Register Pair HL onto the stack.
48A4
PUSH DE D5
Save the caller's Register Pair DE (the FCB address) onto the stack.
48A5
PUSH BC C5
Save the caller's Register Pair BC onto the stack.
48A6
PUSH HL E5
Save Register Pair HL onto the stack; 48A7H-48AAH turn this word into 48B3H.
48A7
LD HL,48B3H 21 B3 48
Load Register Pair HL with 48B3H, the routine that restores the caller's registers.
48AA
EX (SP),HL E3
Exchange Register Pair HL with the top of the stack (Stack Pointer = the word pushed at 48A6H): 48B3H is now the return address of the file routine and Register Pair HL is the caller's again.
48AB
PUSH AF F5
Save Register Pair AF (the address in the file routine after its CALL 4892H) onto the stack as the address to go to.
48AC
XOR A AF
Set Register A to ZERO and clear all flags: the file routine starts with Register A = 00H and the Z FLAG.
48AD
RET C9
RETURN to the file routine just after its CALL 4892H with Register A = 00H, Index Register IX = the FCB and 48B3H waiting on the stack.
48AE
POP AF F1
Restore Register Pair AF from the stack, dropping the return address into the file routine. 4894H comes here.
48AF
LD A,26H 3E 26
Load Register A with 26H, error FILE NOT OPEN, for the FCB at Register Pair DE.
48B1
OR A B7
OR Register A (26H) with itself, setting the NZ FLAG.
48B2
RET C9
RETURN to the program that called the file routine with Register A = 26H and the NZ FLAG.
48B3
POP BC C1
Restore the Caller's Registers
Restore the caller's Register Pair BC from the stack. Every file routine set up by 4892H ends here.
48B4
POP DE D1
Restore the caller's Register Pair DE (the FCB address) from the stack.
48B5
POP HL E1
Restore the caller's Register Pair HL from the stack.
48B6
POP IX DD E1
Restore the caller's Index Register IX from the stack.
48B8
RET C9
RETURN to the program that called the file routine; Register A and the flags are the file routine's result.

48B9H - Compare the Position with the End of the File

Index Register IX = an open FCB. Compares the next record number (FCB+0AH) with the ending record number (FCB+0CH); when they are equal, compares the byte offset (FCB+05H) with the end-of-file byte (FCB+08H). Returns Register A = 00H and the Z FLAG before the end, Register A = 1CH (END OF FILE ENCOUNTERED) at the end, or 1DH (RECORD NUMBER OUT OF RANGE) past it, with the NZ FLAG. Also returns Register Pair HL = the next record number and Register C = the byte offset.

48B9
LD L,(IX+0AH) DD 6E 0A
Fetch the low byte of the next record number from (IX+0AH) (Index Register IX = the FCB) into Register L. 472AH, 47BBH, 47F6H, 481BH and 4864H call here.
48BC
LD H,(IX+0BH) DD 66 0B
Fetch the high byte from (IX+0BH) (Index Register IX = the FCB) into Register H, so Register Pair HL = the next record number.
48BF
LD C,(IX+05H) DD 4E 05
Fetch the byte offset from (IX+05H) (Index Register IX = the FCB) into Register C.
48C2
LD A,H 7C
Copy the high byte of the next record number from Register H into Register A.
48C3
CP (IX+0DH) DD BE 0D
Compare Register A with the high byte of the ending record number at (IX+0DH) (Index Register IX = the FCB).
48C6
If the NZ FLAG is set (the high bytes differ), JUMP to 48D7H with the CARRY FLAG telling which is lower.
48C8
LD A,L 7D
Copy the low byte of the next record number from Register L into Register A.
48C9
CP (IX+0CH) DD BE 0C
Compare Register A with the low byte of the ending record number at (IX+0CH) (Index Register IX = the FCB).
48CC
If the NZ FLAG is set (the record numbers differ), JUMP to 48D7H.
48CE
DEC C 0D
DECrement Register C (the byte offset) by 1 for the compare in 4B97H (an offset of 00H becomes FFH).
48CF
GOSUB to 4B97H: Register A = the end-of-file byte minus 1 minus Register C, the CARRY FLAG set when the offset is before the end, and Register Pair BC put back.
48D2
If the NZ FLAG is set (the offset is not exactly at the end), JUMP to 48D7H.
48D4
OR 1CH F6 1C
OR Register A (00H) with 1CH: END OF FILE ENCOUNTERED, with the NZ FLAG.
48D6
RET C9
RETURN with Register A = 1CH and the NZ FLAG.
48D7
LD A,1DH 3E 1D
Load Register A with 1DH, RECORD NUMBER OUT OF RANGE. 48C6H, 48CCH and 48D2H come here; LD does not change the flags.
48D9
RET NC D0
If the NO CARRY FLAG is set (the position is past the end), RETURN with Register A = 1DH and the NZ FLAG.
48DA
XOR A AF
Set Register A to ZERO and clear all flags: the position is inside the file.
48DB
RET C9
RETURN with Register A = 00H and the Z FLAG: the position is before the end of the file.

48DCH - Disk Address of the Next Record Number

Index Register IX = an open FCB. A granule is five sectors and a track holds two granules (sectors 0-4 and 5-9). The next record number (FCB+0AH) is divided by 5: the quotient is the granule counted from the start of the file and the remainder the sector inside it. The FCB keeps up to five extents of the file at FCB+0EH, four bytes each: the track, the granule byte (bits 5-7 = first granule on that track, bits 0-4 = number of granules minus 1) and the granule number at which the next extent of the FCB begins. Extent 1 always begins at granule 0. When no extent in the FCB covers the granule, the directory is searched (49B5H), extending the file when needed, and the extent found is put into the second extent of the FCB. Returns Register C = drive, Register D = track, Register E = sector and the Z FLAG, or an error number and the NZ FLAG.

48DC
LD L,(IX+0AH) DD 6E 0A
Fetch the low byte of the next record number from (IX+0AH) (Index Register IX = the FCB) into Register L. 472EH, 482DH and 484BH call here.
48DF
LD H,(IX+0BH) DD 66 0B
Fetch the high byte from (IX+0BH) (Index Register IX = the FCB) into Register H, so Register Pair HL = the next record number (the sector of the file).
48E2
LD A,05H 3E 05
Load Register A with 05H, the number of sectors in a granule.
48E4
GOSUB to 4B84H to divide Register Pair HL by Register A (5): Register Pair HL = the granule of the file, Register A = the sector inside the granule (0-4).
48E7
LD (49AAH),A 32 AA 49
Store the sector inside the granule (Register A) into 49AAH, the operand of ADD A,nn at 49A9H. Self-Modifying Code
48EA
PUSH IX DD E5
Save Index Register IX (the FCB address) onto the stack.
48EC
EX (SP),HL E3
Exchange Register Pair HL (the granule wanted) with the top of the stack (Stack Pointer = the FCB address): Register Pair HL = the FCB address and the granule wanted is on the stack.
48ED
LD BC,000FH 01 0F 00
Load Register Pair BC with 000FH, the offset of extent 1's granule byte in the FCB.
48F0
ADD HL,BC 09
ADD Register Pair BC (000FH) to Register Pair HL (the FCB address): Register Pair HL = FCB+0FH, the granule byte of extent 1.
48F1
POP BC C1
Restore Register Pair BC from the stack: the granule of the file that is wanted.
48F2
LD A,05H 3E 05
Load Register A with 05H, the number of extents in the FCB.
48F4
LD DE,0000H 11 00 00
Load Register Pair DE with 0000H: extent 1 begins at granule 0.
48F7
PUSH AF F5
Loop Start
Save Register Pair AF (Register A = extents left to look at) onto the stack. 4913H loops back here.
48F8
DEC HL 2B
DECrement Register Pair HL by 1, to the track byte of this extent.
48F9
LD A,(HL) 7E
Fetch the extent's track from (HL) (Register Pair HL = the track byte) into Register A; FFH marks an unused extent.
48FA
INC HL 23
INCrement Register Pair HL by 1, back to the extent's granule byte.
48FB
INC A 3C
INCrement Register A (the track) by 1: zero only for FFH.
48FC
If the Z FLAG is set (unused extent), JUMP to 4909H to go on to the next one.
48FE
PUSH HL E5
Save Register Pair HL (the extent's granule byte pointer) onto the stack.
48FF
LD H,D 62
Copy the extent's first granule number from Register D into Register H.
4900
LD L,E 6B
Copy the low byte from Register E into Register L, so Register Pair HL = the granule of the file at which this extent begins.
4901
XOR A AF
Set Register A to ZERO and clear all flags (the CARRY FLAG for the SBC, and Register A = offset 0).
4902
SBC HL,BC ED 42
SUBtract Register Pair BC (the granule wanted) from Register Pair HL (the extent's first granule).
4904
If the CARRY FLAG is set (the extent begins before the granule wanted), JUMP to 4915H to see whether it reaches it.
4906
POP HL E1
Restore Register Pair HL (the extent's granule byte pointer) from the stack.
4907
If the Z FLAG is set (the granule wanted is this extent's first), JUMP to 496AH with Register A = 00H.
4909
INC HL 23
INCrement Register Pair HL by 1, to the word holding the first granule of the next extent. 48FCH, 4918H and 4922H come here.
490A
POP AF F1
Restore Register Pair AF (Register A = extents left) from the stack.
490B
DEC A 3D
DECrement Register A (the number of FCB extents still to look at) by 1.
490C
If the Z FLAG is set (all five extents have been looked at), JUMP to 4928H to search the directory.
490E
LD E,(HL) 5E
Fetch the low byte of the next extent's first granule from (HL) (Register Pair HL = the word after this extent) into Register E.
490F
INC HL 23
INCrement Register Pair HL by 1, to the high byte of the next-extent word.
4910
LD D,(HL) 56
Fetch the high byte from (HL) (Register Pair HL = the second byte of that word) into Register D, so Register Pair DE = the first granule of the next extent.
4911
INC HL 23
INCrement Register Pair HL by 1, to the next extent's track byte.
4912
INC HL 23
INCrement Register Pair HL by 1, to the next extent's granule byte.
4913
Loop End
LOOP BACK to 48F7H to look at the next extent.
4915
INC H 24
INCrement Register H (the high byte of first granule minus granule wanted) by 1: zero when the granule wanted is 1-256 granules beyond the extent's start. 4904H comes here.
4916
LD A,L 7D
Copy the low byte of the difference from Register L into Register A (the extent's first granule minus the granule wanted, as a negative number).
4917
POP HL E1
Restore Register Pair HL (the extent's granule byte pointer) from the stack.
4918
If the NZ FLAG is set (the granule wanted is too far beyond this extent's start), JUMP to 4909H for the next extent.
491A
PUSH DE D5
Save Register Pair DE (the extent's first granule) onto the stack.
491B
LD E,A 5F
Copy the negative difference from Register A into Register E.
491C
LD A,(HL) 7E
Fetch the extent's granule byte from (HL) (Register Pair HL = the granule byte pointer) into Register A.
491D
AND 1FH E6 1F
AND Register A with 1FH, keeping bits 0-4: the number of granules in the extent minus 1.
491F
ADD A,E 83
ADD Register E (the negative difference) to Register A (granules minus 1): the CARRY FLAG is set when the granule wanted lies inside the extent.
4920
LD A,E 7B
Copy the negative difference from Register E back into Register A (the flags are kept).
4921
POP DE D1
Restore Register Pair DE (the extent's first granule) from the stack.
4922
If the NO CARRY FLAG is set (the granule wanted is past this extent), JUMP to 4909H for the next extent.
4924
NEG ED 44
NEGate Register A: the granule wanted minus the extent's first granule, the offset inside the extent.
4926
JUMP to 496AH with Register A = the offset inside this extent.
4928
Not in the FCB's Extents
GOSUB to 49B5H to find the granule wanted (Register Pair BC) in the directory's extents for this file, allocating granules when the file is too short. It returns Register H = the extent's granule byte, Register L = its track and Register Pair DE = the granule of the file at which that extent begins. 490CH comes here.
492B
RET NZ C0
If the NZ FLAG is set (a directory, allocation table or DISK SPACE FULL error), RETURN with Register A = the error number.
492C
PUSH HL E5
Save Register Pair HL (Register H = granule byte, Register L = track) onto the stack.
492D
LD H,B 60
Copy the high byte of the granule wanted from Register B into Register H.
492E
LD L,C 69
Copy the low byte from Register C into Register L, so Register Pair HL = the granule wanted.
492F
XOR A AF
Set Register A to ZERO and clear all flags (the CARRY FLAG for the SBC).
4930
SBC HL,DE ED 52
SUBtract Register Pair DE (the extent's first granule) from Register Pair HL (the granule wanted): the offset inside the extent.
4932
LD A,L 7D
Copy the low byte of the offset from Register L into Register A.
4933
LD (499AH),A 32 9A 49
Store the offset (Register A) into 499AH, the operand of ADD A,nn at 4999H. Self-Modifying Code
4936
POP HL E1
Restore Register Pair HL (Register H = granule byte, Register L = track) from the stack.
4937
PUSH DE D5
Save Register Pair DE (the extent's first granule) onto the stack.
4938
PUSH IX DD E5
Save Index Register IX (the FCB address) onto the stack.
493A
EX (SP),HL E3
Exchange Register Pair HL (granule byte and track) with the top of the stack (Stack Pointer = the FCB address): Register Pair HL = the FCB address, the extent is on the stack.
493B
LD DE,000EH 11 0E 00
Load Register Pair DE with 000EH, the offset of extent 1's track byte in the FCB.
493E
ADD HL,DE 19
ADD Register Pair DE (000EH) to Register Pair HL (the FCB address): Register Pair HL = FCB+0EH, the track byte of extent 1.
493F
POP DE D1
Restore Register Pair DE from the stack: Register D = the found extent's granule byte, Register E = its track.
4940
LD B,05H 06 05
Load Register B with 05H, the number of FCB extents to compare with.
4942
LD A,(HL) 7E
Loop Start
Fetch an FCB extent's track from (HL) (Register Pair HL = its track byte) into Register A. 4953H loops back here.
4943
INC HL 23
INCrement Register Pair HL by 1, to the extent's granule byte.
4944
CP E BB
Compare Register A (the FCB extent's track) with Register E (the found extent's track).
4945
If the NZ FLAG is set (another track), JUMP to 494DH for the next FCB extent.
4947
LD A,(HL) 7E
Fetch the FCB extent's granule byte from (HL) (Register Pair HL = the granule byte pointer) into Register A.
4948
XOR D AA
XOR Register A with Register D (the found extent's granule byte): the bits that differ.
4949
AND E0H E6 E0
AND Register A with E0H, keeping bits 5-7, the first granule on the track.
494B
If the Z FLAG is set (the same extent, which has grown since the FCB copy was made), JUMP to 4965H to update its count.
494D
DEC B 05
DECrement Register B (FCB extents left) by 1. 4945H comes here.
494E
If the Z FLAG is set (no FCB extent matches), JUMP to 4955H to insert the found extent.
4950
INC HL 23
INCrement Register Pair HL by 1 (to the next-extent word).
4951
INC HL 23
INCrement Register Pair HL by 1 (the second byte of the next-extent word).
4952
INC HL 23
INCrement Register Pair HL by 1, to the next FCB extent's track byte.
4953
Loop End
LOOP BACK to 4942H to compare the next FCB extent.
4955
PUSH DE D5
Save Register Pair DE (Register D = granule byte, Register E = track of the found extent) onto the stack. 494EH comes here with Register Pair HL = FCB+1FH, the last byte of extent 5's granule.
4956
EX DE,HL EB
Exchange Register Pairs DE and HL: Register Pair DE = FCB+1FH, the destination end of the move.
4957
LD HL,FFFCH 21 FC FF
Load Register Pair HL with FFFCH (minus 4): the move source lies four bytes below its destination.
495A
ADD HL,DE 19
ADD Register Pair DE (FCB+1FH) to Register Pair HL: Register Pair HL = FCB+1BH, the source end of the move.
495B
LD BC,000CH 01 0C 00
Load Register Pair BC with 000CH, twelve bytes: the next-extent word of extent 1 and extents 2-4 with their words.
495E
LDDR ED B8
Move Register Pair BC (twelve) bytes downward from Register Pair HL (FCB+1BH) to Register Pair DE (FCB+1FH): extents 2-4 move up to become extents 3-5 and extent 5 is dropped. Register Pair HL ends at FCB+0FH and Register Pair DE at FCB+13H.
4960
EX DE,HL EB
Exchange Register Pairs DE and HL: Register Pair HL = FCB+13H, extent 2's granule byte.
4961
POP BC C1
Restore Register Pair BC from the stack: Register B = the found extent's granule byte, Register C = its track.
4962
POP DE D1
Restore Register Pair DE from the stack: the granule of the file at which the found extent begins.
4963
JUMP to 498AH to store the found extent as extent 2.
4965
LD (HL),D 72
Store Register D (the found extent's granule byte, with its new count) into the FCB extent's granule byte at (HL) (Register Pair HL = that byte). 494BH comes here.
4966
EX DE,HL EB
Exchange Register Pairs DE and HL: Register H = the granule byte, Register L = the track, for 4993H.
4967
POP AF F1
Restore Register Pair AF from the stack, dropping the extent's first granule pushed at 4937H.
4968
JUMP to 4993H to work out the track and sector.
496A
LD (499AH),A 32 9A 49
Found in an FCB Extent
Store Register A (the offset inside the extent) into 499AH, the operand of ADD A,nn at 4999H. Self-Modifying Code
4907H and 4926H come here with Register Pair HL = the extent's granule byte.
496D
LD B,(HL) 46
Fetch the extent's granule byte from (HL) (Register Pair HL = the granule byte pointer) into Register B.
496E
DEC HL 2B
DECrement Register Pair HL by 1, to the extent's track byte.
496F
LD C,(HL) 4E
Fetch the extent's track from (HL) (Register Pair HL = the track byte) into Register C.
4970
INC HL 23
INCrement Register Pair HL by 1, back to the granule byte.
4971
POP AF F1
Restore Register Pair AF (Register A = extents left: 5 for extent 1, 4 for extent 2 ... 1 for extent 5) from the stack.
4972
CPL 2F
Complement Register A (minus the count minus 1).
4973
ADD A,04H C6 04
ADD 04H to Register A: the CARRY FLAG is set for extents 3-5, with Register A = 00H, 01H or 02H.
4975
If the NO CARRY FLAG is set (extent 1 or 2), JUMP to 4991H without moving anything.
4977
INC A 3C
INCrement Register A by 1 (1, 2 or 3 for extent 3, 4 or 5).
4978
RLCA 07
Rotate Register A (1, 2 or 3) left: times 2 on the way to the byte count.
4979
RLCA 07
Rotate Register A left again (times 4): 4, 8 or 12 bytes to move.
497A
PUSH BC C5
Save Register Pair BC (Register B = granule byte, Register C = track of the found extent) onto the stack.
497B
PUSH DE D5
Save Register Pair DE (the extent's first granule) onto the stack.
497C
EX DE,HL EB
Exchange Register Pairs DE and HL: Register Pair DE = the found extent's granule byte, the destination end of the move.
497D
LD HL,FFFCH 21 FC FF
Load Register Pair HL with FFFCH (minus 4): the move source lies four bytes below its destination.
4980
ADD HL,DE 19
ADD Register Pair DE to Register Pair HL: Register Pair HL = four bytes lower, the source end of the move.
4981
LD B,00H 06 00
Load Register B with 00H, the high byte of the byte count.
4983
LD C,A 4F
Copy the byte count from Register A into Register C, so Register Pair BC = 4, 8 or 12.
4984
LDDR ED B8
Move Register Pair BC bytes downward from Register Pair HL to Register Pair DE: the extents between extent 2 and the found one move up four bytes, over the found extent. Register Pair DE ends at FCB+13H.
4986
EX DE,HL EB
Exchange Register Pairs DE and HL: Register Pair HL = FCB+13H, extent 2's granule byte.
4987
POP DE D1
Restore Register Pair DE (the found extent's first granule) from the stack.
4988
POP BC C1
Restore Register Pair BC (Register B = granule byte, Register C = track) from the stack.
4989
NOP 00
NOP (no operation). A one-byte gap the code passes through on its way to 498AH.
498A
LD (HL),B 70
Store Register B (the granule byte) into (HL) (Register Pair HL = FCB+13H, extent 2's granule byte). 4963H comes here.
498B
DEC HL 2B
DECrement Register Pair HL by 1, to FCB+12H, extent 2's track byte.
498C
LD (HL),C 71
Store Register C (the track) into (HL) (Register Pair HL = FCB+12H, extent 2's track).
498D
DEC HL 2B
DECrement Register Pair HL by 1, to FCB+11H, the high byte of the word after extent 1.
498E
LD (HL),D 72
Store Register D (high byte of the extent's first granule) into (HL) (Register Pair HL = FCB+11H).
498F
DEC HL 2B
DECrement Register Pair HL by 1, to FCB+10H, the low byte of the word after extent 1.
4990
LD (HL),E 73
Store Register E (low byte) into (HL) (Register Pair HL = FCB+10H): the word after extent 1 now says where extent 2 begins.
4991
LD H,B 60
Copy the granule byte from Register B into Register H. 4975H comes here.
4992
LD L,C 69
Copy the track from Register C into Register L, so Register H = granule byte and Register L = track.
4993
LD A,H 7C
Copy the extent's granule byte from Register H into Register A. 4968H comes here.
4994
RLCA 07
Rotate Register A (the extent's granule byte) left: bit 7 moves to bit 0.
4995
RLCA 07
Rotate Register A left again: bits 6-7 are now bits 0-1.
4996
RLCA 07
Rotate Register A left again: bits 5-7 (the first granule on the extent's track) are now bits 0-2.
4997
AND 07H E6 07
AND Register A with 07H, keeping the extent's first granule on its track.
4999
ADD A,00H C6 00
ADD the offset inside the extent (the operand at 499AH, stored by 4933H or 496AH) to Register A: the granule counted from the extent's track. Self-Modifying Code
00H in the file.
499B
LD E,A 5F
Copy the granule count from Register A into Register E.
499C
AND FEH E6 FE
AND Register A with FEH, dropping the granule-in-track bit.
499E
RRCA 0F
Rotate Register A right: halve it, the number of tracks past the extent's track (two granules per track).
499F
LD (49AEH),A 32 AE 49
Store Register A (the tracks to add) into 49AEH, the operand of ADD A,nn at 49ADH. Self-Modifying Code
49A2
LD A,E 7B
Copy the granule count from Register E into Register A.
49A3
AND 01H E6 01
AND Register A with 01H: the granule on its track, 0 (sectors 0-4) or 1 (sectors 5-9).
49A5
LD E,A 5F
Copy the granule on its track (0 or 1) from Register A into Register E.
49A6
RLCA 07
Rotate Register A (the granule on its track, 0 or 1) left: times 2.
49A7
RLCA 07
Rotate Register A left again: times 4 (0 or 4).
49A8
ADD A,E 83
ADD Register E to Register A: times 5, the granule's first sector (0 or 5).
49A9
ADD A,00H C6 00
ADD the sector inside the granule (the operand at 49AAH, stored by 48E7H) to Register A: the sector number, 0-9. Self-Modifying Code
00H in the file.
49AB
LD E,A 5F
Copy the sector number from Register A into Register E.
49AC
LD A,L 7D
Copy the extent's track from Register L into Register A.
49AD
ADD A,00H C6 00
ADD the tracks to add (the operand at 49AEH, stored by 499FH) to Register A: the track number. Self-Modifying Code
00H in the file.
49AF
LD D,A 57
Copy the track number from Register A into Register D.
49B0
LD C,(IX+06H) DD 4E 06
Fetch the drive number from (IX+06H) (Index Register IX = the FCB) into Register C.
49B3
XOR A AF
Set Register A to ZERO and clear all flags: the Z FLAG tells the caller the address is valid.
49B4
RET C9
RETURN with Register C = drive, Register D = track, Register E = sector, Register A = 00H and the Z FLAG.

49B5H - Find a Granule in the Directory Extents

Register Pair BC = the granule of the file that is wanted, Index Register IX = the open FCB. Reads the file's directory entry (locator FCB+07H) and walks its extent list (entry bytes 16H-1FH, two bytes each: track, then granule byte). A track byte of FEH means the list goes on in another directory entry whose locator follows; FFH ends the list. When the list ends before the granule wanted, granules are allocated (4A00H) and the search starts again. Returns Register H = the extent's granule byte, Register L = its track and Register Pair DE = the granule of the file at which the extent begins.

49B5
PUSH BC C5
Save Register Pair BC (the granule wanted) onto the stack. 4928H calls here; 49FEH and 4A68H come back here.
49B6
LD DE,0000H 11 00 00
Load Register Pair DE with 0000H: no granules counted yet.
49B9
LD B,(IX+07H) DD 46 07
Fetch the file's directory entry locator from (IX+07H) (Index Register IX = the FCB) into Register B.
49BC
LD A,B 78
Copy the entry locator from Register B into Register A. 49F7H comes here for a continuation entry.
49BD
LD (4ABEH),A 32 BE 4A
Store Register A (the locator of the entry being read) into 4ABEH, the operand of LD B,nn at 4ABDH, so 4AB6H writes this entry's sector back. Self-Modifying Code
SYS2/SYS reads it at 4F77H and 4F92H.
49C0
LD C,(IX+06H) DD 4E 06
Fetch the drive number from (IX+06H) (Index Register IX = the FCB) into Register C.
49C3
GOSUB to 4AC1H to read the directory sector holding entry Register B of drive Register C into 4200H; it returns Register Pair HL = the entry.
49C6
LD BC,0016H 01 16 00
Load Register Pair BC with 0016H, the offset of the extent list in a directory entry.
49C9
ADD HL,BC 09
ADD Register Pair BC (0016H) to Register Pair HL: Register Pair HL = the entry's first extent.
49CA
EX DE,HL EB
Exchange Register Pairs DE and HL: Register Pair DE = the extent pointer, Register Pair HL = the granules counted so far.
49CB
POP BC C1
Restore Register Pair BC (the granule wanted) from the stack.
49CC
RET NZ C0
If the NZ FLAG is set (11H DIRECTORY READ ERROR or 10H from 4AC1H), RETURN with Register A = the error number.
49CD
LD A,(DE) 1A
Loop Start
Fetch the extent's track from (DE) (Register Pair DE = the extent pointer) into Register A. 49E7H loops back here.
49CE
CP FEH FE FE
Compare Register A (the track byte) against FEH: FEH (continued in another entry) and FFH (end of the list) are not tracks.
49D0
If the NO CARRY FLAG is set (FEH or FFH), JUMP to 49F1H.
49D2
INC DE 13
INCrement Register Pair DE by 1, to the extent's granule byte.
49D3
LD A,(DE) 1A
Fetch the granule byte from (DE) (Register Pair DE = the granule byte pointer) into Register A.
49D4
PUSH HL E5
Save Register Pair HL (the granule of the file at which this extent begins) onto the stack.
49D5
AND 1FH E6 1F
AND Register A with 1FH, keeping bits 0-4: the number of granules in the extent minus 1.
49D7
INC A 3C
INCrement Register A by 1: the number of granules in the extent.
49D8
ADD A,L 85
ADD Register L (the low byte of the count) to Register A.
49D9
LD L,A 6F
Copy the sum into Register L from Register A.
49DA
If the NO CARRY FLAG is set (no carry into the high byte), JUMP to 49DDH.
49DC
INC H 24
INCrement Register H by 1, so Register Pair HL = the granule at which the next extent begins.
49DD
PUSH HL E5
Save Register Pair HL (the start of the next extent) onto the stack. 49DAH comes here.
49DE
DEC HL 2B
DECrement Register Pair HL by 1: the last granule of this extent.
49DF
XOR A AF
Set Register A to ZERO and clear all flags (the CARRY FLAG for the SBC).
49E0
SBC HL,BC ED 42
SUBtract Register Pair BC (the granule wanted) from Register Pair HL (the extent's last granule): the NO CARRY FLAG is set when this extent reaches the granule wanted.
49E2
POP HL E1
Restore Register Pair HL (the start of the next extent) from the stack.
49E3
If the NO CARRY FLAG is set (found), JUMP to 49E9H.
49E5
INC DE 13
INCrement Register Pair DE by 1, to the next extent's track byte.
49E6
POP AF F1
Restore Register Pair AF from the stack, dropping the start of this extent pushed at 49D4H.
49E7
Loop End
LOOP BACK to 49CDH with Register Pair HL = the start of the next extent.
49E9
POP HL E1
Restore Register Pair HL from the stack: the granule of the file at which the found extent begins. 49E3H comes here.
49EA
EX DE,HL EB
Exchange Register Pairs DE and HL: Register Pair DE = the extent's first granule, Register Pair HL = the pointer to its granule byte.
49EB
LD A,(HL) 7E
Fetch the extent's granule byte from (HL) (Register Pair HL = the granule byte pointer) into Register A.
49EC
DEC HL 2B
DECrement Register Pair HL by 1, to the extent's track byte.
49ED
LD L,(HL) 6E
Fetch the extent's track from (HL) (Register Pair HL = the track byte) into Register L.
49EE
LD H,A 67
Copy the granule byte from Register A into Register H, so Register H = granule byte and Register L = track.
49EF
XOR A AF
Set Register A to ZERO and clear all flags: the Z FLAG tells 4928H that the granule was found.
49F0
RET C9
RETURN with Register H = granule byte, Register L = track, Register Pair DE = the extent's first granule and the Z FLAG.
49F1
PUSH BC C5
Save Register Pair BC (the granule wanted) onto the stack. 49D0H comes here with the flags of the compare with FEH.
49F2
EX DE,HL EB
Exchange Register Pairs DE and HL: Register Pair HL = the pointer to the FEH or FFH byte, Register Pair DE = the granules counted so far (the file's size in granules up to here).
49F3
If the NZ FLAG is set (FFH: the end of the extent list), JUMP to 49F9H to allocate more granules.
49F5
INC HL 23
INCrement Register Pair HL by 1, to the byte after the FEH.
49F6
LD B,(HL) 46
Fetch the locator of the continuation entry from (HL) (Register Pair HL = the byte after FEH) into Register B.
49F7
JUMP to 49BCH to read that entry and go on counting with Register Pair DE = the granules so far.
49F9
GOSUB to 4A00H to allocate granules up to the granule wanted (Register Pair HL = the end mark in the entry, Register Pair DE = the granules the file has). 49F3H comes here.
49FC
POP BC C1
Restore Register Pair BC (the granule wanted) from the stack.
49FD
RET NZ C0
If the NZ FLAG is set (a directory, allocation table or DISK SPACE FULL error), RETURN with Register A = the error number.
49FE
JUMP to 49B5H to search the extents again, now that the file is long enough.

4A00H - Allocate Granules to a File

Register Pair BC = the granule of the file that is wanted, Register Pair DE = the granules the file has, Register Pair HL = the FFH end mark in its directory entry (in the sector at 4200H), Index Register IX = the open FCB. Reads the allocation table of the drive into 4D00H-4DFFH (one byte per track, bit 0 = granule 0, bit 1 = granule 1; FFH = no granule free) and allocates the granule wanted minus the granules the file has, plus two. Each granule extends the last extent when the next granule on the disk is free and the extent has fewer than 32 granules; otherwise a new extent is started at the first free granule found from a track chosen by the entry's directory sector. The allocation table and the directory sector are written back at the end (4AB6H).

4A00
PUSH BC C5
Save Register Pair BC (the granule wanted) onto the stack. 49F9H calls here.
4A01
LD B,(IX+07H) DD 46 07
Fetch the directory entry locator from (IX+07H) (Index Register IX = the FCB) into Register B.
4A04
LD C,(IX+06H) DD 4E 06
Fetch the drive number from (IX+06H) (Index Register IX = the FCB) into Register C.
4A07
GOSUB to 4AF0H to read the allocation table of drive Register C into 4D00H-4DFFH.
4A0A
POP BC C1
Restore Register Pair BC (the granule wanted) from the stack.
4A0B
RET NZ C0
If the NZ FLAG is set (14H, GAT READ ERROR), RETURN with that number.
4A0C
PUSH HL E5
Save Register Pair HL (the end mark in the directory entry) onto the stack.
4A0D
LD H,B 60
Copy the high byte of the granule wanted from Register B into Register H.
4A0E
LD L,C 69
Copy the low byte from Register C into Register L, so Register Pair HL = the granule wanted.
4A0F
XOR A AF
Set Register A to ZERO and clear all flags (the CARRY FLAG for the SBC).
4A10
SBC HL,DE ED 52
SUBtract Register Pair DE (the granules the file has) from Register Pair HL (the granule wanted).
4A12
LD B,H 44
Copy the high byte of the difference from Register H into Register B.
4A13
LD C,L 4D
Copy the low byte from Register L into Register C, so Register Pair BC = the granule wanted minus the granules the file has.
4A14
INC BC 03
INCrement Register Pair BC by 1: the granules needed to reach the granule wanted.
4A15
POP DE D1
Restore Register Pair DE from the stack: the end mark (the track byte of the first unused extent) in the directory entry.
4A16
INC DE 13
INCrement Register Pair DE by 1, to the granule byte of that unused extent.
4A17
INC BC 03
INCrement Register Pair BC by 1 again: one granule more than needed is allocated.
4A18
LD A,(IX+07H) DD 7E 07
Fetch the directory entry locator from (IX+07H) (Index Register IX = the FCB) into Register A.
4A1B
AND 07H E6 07
AND Register A with 07H, keeping the entry's directory sector (0-7, sector 2-9).
4A1D
RLCA 07
Rotate Register A (the directory sector index) left: times 2.
4A1E
RLCA 07
Rotate Register A left again (times 4): four tracks for each directory sector.
4A1F
LD L,A 6F
Copy the track from Register A into Register L.
4A20
LD H,4DH 26 4D
Load Register H with 4DH, so Register Pair HL = 4D00H plus the track: the allocation table byte where a search for a free granule starts.
4A22
PUSH BC C5
Save Register Pair BC (the granules still to allocate) onto the stack.
4A23
LD A,E 7B
Copy the low byte of the extent pointer from Register E into Register A.
4A24
AND 1EH E6 1E
AND Register A with 1EH, keeping the position of the extent in the entry (16H for the first extent's granule byte at entry+17H).
4A26
CP 16H FE 16
Compare Register A against 16H: the unused extent is the entry's first one.
4A28
If the Z FLAG is set (no extent yet to extend), JUMP to 4A6EH to search for a free granule.
4A2A
DEC E 1D
DECrement Register E by 1, from the unused extent's granule byte to its track byte.
4A2B
DEC E 1D
DECrement Register E by 1 again, so Register Pair DE = the previous extent's granule byte.
4A2C
LD A,(DE) 1A
Loop Start
Fetch the extent's granule byte from (DE) (Register Pair DE = the granule byte pointer) into Register A. 4AB2H loops back here for the next granule.
4A2D
AND 1FH E6 1F
AND Register A with 1FH: the number of granules in the extent minus 1.
4A2F
INC A 3C
INCrement Register A by 1: the number of granules in the extent.
4A30
LD C,A 4F
Copy the number of granules from Register A into Register C.
4A31
CP 20H FE 20
Compare Register A (the number of granules) against 20H (32, the most an extent can hold).
4A33
If the Z FLAG is set (the extent is full), JUMP to 4A54H to start a new extent.
4A35
LD A,(DE) 1A
Fetch the granule byte from (DE) (Register Pair DE = the granule byte pointer) into Register A again.
4A36
AND E0H E6 E0
AND Register A with E0H, keeping bits 5-7: the extent's first granule on its track.
4A38
RLCA 07
Rotate Register A (the granule byte's bits 5-7) left: bit 7 moves to bit 0.
4A39
RLCA 07
Rotate Register A left again: bits 6-7 are now bits 0-1.
4A3A
RLCA 07
Rotate Register A left again: the first granule (0 or 1) is now in bits 0-2.
4A3B
ADD A,C 81
ADD Register C (the number of granules) to Register A: the granule just after the extent, counted from the extent's track.
4A3C
LD B,A 47
Copy that count from Register A into Register B.
4A3D
SRL B CB 38
Shift Register B right: halve it, the number of tracks past the extent's track.
4A3F
AND 01H E6 01
AND Register A with 01H: that granule's place on its track (0 or 1).
4A41
LD C,A 4F
Copy the granule place from Register A into Register C.
4A42
DEC DE 1B
DECrement Register Pair DE by 1, to the extent's track byte.
4A43
LD A,(DE) 1A
Fetch the extent's track from (DE) (Register Pair DE = the track byte) into Register A.
4A44
INC DE 13
INCrement Register Pair DE by 1, back to the granule byte.
4A45
ADD A,B 80
ADD Register B (the tracks to add) to Register A (the extent's track): the track of the granule after the extent.
4A46
LD L,A 6F
Copy that track from Register A into Register L.
4A47
LD H,4DH 26 4D
Load Register H with 4DH, so Register Pair HL = that track's byte in the allocation table at 4D00H.
4A49
CP 23H FE 23
Compare Register A (the track) against 23H (35 tracks, 0-34).
4A4B
If the NO CARRY FLAG is set (past the last track), JUMP to 4A54H to start a new extent.
4A4D
LD A,C 79
Copy the granule place (0 or 1) from Register C into Register A.
4A4E
LD B,(HL) 46
Fetch the track's allocation byte from (HL) (Register Pair HL = 4D00H plus the track) into Register B.
4A4F
GOSUB to 4B5DH to test bit Register A of Register B: the Z FLAG is set when that granule is free.
4A52
If the Z FLAG is set (the next granule is free), JUMP to 4A9CH to add it to this extent.
4A54
INC E 1C
INCrement Register E by 1. 4A33H and 4A4BH come here.
4A55
INC E 1C
INCrement Register E by 1 again, so Register Pair DE = the granule byte of the next (unused) extent.
4A56
LD A,E 7B
Copy the low byte of the pointer from Register E into Register A.
4A57
AND 1EH E6 1E
AND Register A with 1EH: the position of the extent in the entry.
4A59
CP 1EH FE 1E
Compare Register A against 1EH: the last extent of the entry (bytes 1EH-1FH), which is kept for an FEH continuation.
4A5B
If the NZ FLAG is set (another extent is free in this entry), JUMP to 4A6EH to start it at a free granule.
4A5D
GOSUB to 4AB6H to write the allocation table and this directory sector back.
4A60
POP BC C1
Restore Register Pair BC (the granules still to allocate) from the stack.
4A61
RET NZ C0
If the NZ FLAG is set (a write error), RETURN with Register A = the error number.
4A62
PUSH BC C5
Save Register Pair BC (the granules still to allocate) onto the stack.
4A63
GOSUB to 4A6BH to have SYS2/SYS make a continuation entry for the file.
4A66
POP BC C1
Restore Register Pair BC (the granules still to allocate) from the stack.
4A67
RET NZ C0
If the NZ FLAG is set (no directory space: error from SYS2/SYS), RETURN with Register A = the error number.
4A68
JUMP to 49B5H with Register Pair BC = the granules still to allocate. That search ends with a RET to 49FCH, which takes the granule wanted back off the stack and searches again.
4A6B
LD A,B4H 3E B4
Load Register A with B4H: overlay 4 (SYS2/SYS), function 30H at 4F50H, which makes a continuation directory entry for the open FCB in Index Register IX.
4A6D
RST 28H EF
Execute RST 28H with Register A = B4H; SYS2/SYS returns straight to 4A66H.
4A6E
LD B,02H 06 02
Start a New Extent
Load Register B with 02H: the search for a free granule goes up to track 34 and then once more from track 0. 4A28H and 4A5BH come here.
4A70
LD A,L 7D
Loop Start
Copy the track being looked at from Register L into Register A. 4A7AH and 4A7EH loop back here.
4A71
CP 23H FE 23
Compare Register A (the track) against 23H (35 tracks).
4A73
If the NO CARRY FLAG is set (past the last track), JUMP to 4A7CH to wrap to track 0.
4A75
LD A,(HL) 7E
Fetch the track's allocation byte from (HL) (Register Pair HL = 4D00H plus the track) into Register A.
4A76
INC A 3C
INCrement Register A by 1: zero only when the byte is FFH (no free granule on the track).
4A77
If the NZ FLAG is set (a granule is free on this track), JUMP to 4A89H.
4A79
INC L 2C
INCrement Register L by 1 to the next track's allocation byte.
4A7A
Loop End
LOOP BACK to 4A70H for the next track.
4A7C
LD L,00H 2E 00
Load Register L with 00H: start again at track 0. 4A73H comes here.
4A7E
DECrement Register B (passes left) and loop back to 4A70H while a pass remains.
4A80
POP BC C1
Restore Register Pair BC (the granules still to allocate) from the stack.
4A81
GOSUB to 4AB6H to write back the allocation table and the directory sector with the granules allocated so far.
4A84
RET NZ C0
If the NZ FLAG is set (a write error), RETURN with Register A = the error number.
4A85
LD A,1BH 3E 1B
Load Register A with 1BH, error DISK SPACE FULL: no free granule on any track.
4A87
OR A B7
OR Register A (1BH) with itself, setting the NZ FLAG.
4A88
RET C9
RETURN with Register A = 1BH and the NZ FLAG.
4A89
LD A,FFH 3E FF
Load Register A with FFH. 4A77H comes here with Register Pair HL = a track with a free granule.
4A8B
LD (DE),A 12
Store Register A (FFH) into the new extent's granule byte at (DE) (Register Pair DE = the granule byte pointer); 20H is added for each granule passed, and 4AABH adds 1 at the end, giving the first granule in bits 5-7 and a count of 1.
4A8C
LD C,00H 0E 00
Load Register C with 00H, granule 0 of the track.
4A8E
LD B,(HL) 46
Fetch the track's allocation byte from (HL) (Register Pair HL = 4D00H plus the track) into Register B.
4A8F
LD A,C 79
Loop Start
Copy the granule number from Register C into Register A. 4A9AH loops back here.
4A90
GOSUB to 4B5DH to test bit Register A of Register B (the allocation byte): the Z FLAG is set when the granule is free.
4A93
If the Z FLAG is set (free), JUMP to 4A9CH to take it.
4A95
LD A,(DE) 1A
Fetch the new extent's granule byte from (DE) (Register Pair DE = the granule byte pointer) into Register A.
4A96
ADD A,20H C6 20
ADD 20H to Register A: one granule further along the track.
4A98
LD (DE),A 12
Store Register A back into the granule byte at (DE) (Register Pair DE = the granule byte pointer).
4A99
INC C 0C
INCrement Register C (the granule number) by 1.
4A9A
Loop End
LOOP BACK to 4A8FH to test the next granule.
4A9C
LD A,C 79
Copy the free granule's number from Register C into Register A. 4A52H and 4A93H come here.
4A9D
GOSUB to 4639H to turn the granule number in Register A into its bit mask (01H or 02H).
4AA0
OR (HL) B6
OR Register A (the granule's bit) with the track's allocation byte at (HL) (Register Pair HL = 4D00H plus the track).
4AA1
LD (HL),A 77
Store Register A into (HL) (Register Pair HL = the allocation byte): the granule is now allocated.
4AA2
DEC E 1D
DECrement Register E by 1, so Register Pair DE = the extent's track byte.
4AA3
LD A,(DE) 1A
Fetch the extent's track byte from (DE) (Register Pair DE = the track byte pointer) into Register A; FFH for an unused extent.
4AA4
INC A 3C
INCrement Register A by 1: zero only for FFH.
4AA5
If the NZ FLAG is set (the extent already has a track: it is being extended), JUMP to 4AA9H.
4AA7
LD A,L 7D
Copy the track number from Register L into Register A.
4AA8
LD (DE),A 12
Store Register A (the track) into the new extent's track byte at (DE) (Register Pair DE = the track byte pointer).
4AA9
INC E 1C
INCrement Register E by 1, so Register Pair DE = the extent's granule byte. 4AA5H comes here.
4AAA
LD A,(DE) 1A
Fetch the extent's granule byte from (DE) (Register Pair DE = the granule byte pointer) into Register A.
4AAB
INC A 3C
INCrement Register A by 1: one granule more in the extent (FFH or FFH plus 20H becomes a count of 1 for a new extent).
4AAC
LD (DE),A 12
Store Register A into the granule byte at (DE) (Register Pair DE = the granule byte pointer).
4AAD
POP BC C1
Restore Register Pair BC (the granules still to allocate) from the stack.
4AAE
DEC BC 0B
DECrement Register Pair BC by 1: one granule allocated.
4AAF
PUSH BC C5
Save Register Pair BC (the granules still to allocate) onto the stack.
4AB0
LD A,B 78
Copy the high byte of the count from Register B into Register A.
4AB1
OR C B1
OR Register A with Register C: the Z FLAG is set when no granules are left to allocate.
4AB2
Loop End
If the NZ FLAG is set (more to allocate), JUMP back to 4A2CH to extend the extent at Register Pair DE.
4AB5
POP BC C1
Restore Register Pair BC from the stack (the count, now 0000H).
4AB6
LD C,(IX+06H) DD 4E 06
Write the Allocation Table and the Directory Sector
Fetch the drive number from (IX+06H) (Index Register IX = the FCB) into Register C. 4A5DH and 4A81H call here.
4AB9
GOSUB to 4B03H to write the allocation table at 4D00H to drive Register C.
4ABC
RET NZ C0
If the NZ FLAG is set (15H, GAT WRITE ERROR), RETURN with that number.
4ABD
LD B,00H 06 00
Load Register B with the locator of the directory entry being changed, stored into 4ABEH by 49BDH. Self-Modifying Code
00H in the file.
4ABF
JUMP to 4AD6H to write the directory sector at 4200H holding entry Register B to drive Register C.

4AC1H - Read a Directory Sector

Register B = a directory entry locator (bits 5-7 = the entry's offset in the sector in steps of 20H, bits 0-4 = the directory sector minus 2, 0-7), Register C = drive. Reads the directory sector holding the entry into 4200H-42FFH and returns Register Pair HL = the entry. Registers B, C, D and E are kept. Returns Register A = 00H and the Z FLAG, or 11H (DIRECTORY READ ERROR) and the NZ FLAG. Used by SYS2/SYS, SYS3/SYS, SYS4/SYS and SYS6/SYS.

4AC1
PUSH BC C5
Save Register Pair BC (Register B = locator, Register C = drive) onto the stack. 49C3H and 4BCEH call here.
4AC2
PUSH DE D5
Save Register Pair DE (the caller's track and sector) onto the stack; it is returned unchanged.
4AC3
GOSUB to 4B1EH: Register D = the directory track of drive Register C, Register E = the sector (2-9), Register Pair HL = 4200H plus the entry's offset.
4AC6
If the NZ FLAG is set (the locator names a sector above 9), JUMP to 4AD1H.
4AC8
PUSH HL E5
Save Register Pair HL (the entry's address in 4200H) onto the stack.
4AC9
LD L,00H 2E 00
Load Register L with 00H, so Register Pair HL = 4200H, the start of the sector buffer.
4ACB
GOSUB to 4B35H to read drive Register C, track Register D, sector Register E into 4200H, accepting it only with the FAH mark of a system sector.
4ACE
POP HL E1
Restore Register Pair HL (the entry's address) from the stack.
4ACF
If the Z FLAG is set (read correctly), JUMP to 4AD3H.
4AD1
LD A,11H 3E 11
Load Register A with 11H, DIRECTORY READ ERROR. 4AC6H comes here; the NZ FLAG is still set.
4AD3
POP DE D1
Restore Register Pair DE from the stack. 4ACFH comes here.
4AD4
POP BC C1
Restore Register Pair BC (Register B = locator, Register C = drive) from the stack.
4AD5
RET C9
RETURN with Register Pair HL = the entry, Register A = 00H and the Z FLAG, or Register A = 11H and the NZ FLAG.

4AD6H - Write a Directory Sector

Register B = a directory entry locator, Register C = drive; the sector is in 4200H-42FFH. Writes it with the FAH data mark and reads it back, expecting the read to report that mark (error 06H). Returns the Z FLAG, or Register A = 12H (DIRECTORY WRITE ERROR) or 10H and the NZ FLAG. Used by SYS2/SYS, SYS3/SYS and SYS6/SYS.

4AD6
PUSH BC C5
Save Register Pair BC (Register B = locator, Register C = drive) onto the stack. 4ABFH comes here.
4AD7
PUSH DE D5
Save Register Pair DE (the caller's track and sector) onto the stack; it is returned unchanged.
4AD8
GOSUB to 4B1EH: Register D = the directory track, Register E = the sector, Register Pair HL = 4200H plus the entry's offset.
4ADB
If the NZ FLAG is set (Register A = 10H, the locator names a sector above 9), JUMP to 4AEDH.
4ADD
LD L,00H 2E 00
Load Register L with 00H, so Register Pair HL = 4200H, the sector buffer.
4ADF
GOSUB to 46EFH to write 4200H to drive Register C, track Register D, sector Register E with the FAH data mark.
4AE2
If the NZ FLAG is set (a write error), JUMP to 4AEBH.
4AE4
GOSUB to 46F3H to read the sector back without storing it.
4AE7
CP 06H FE 06
Compare Register A (the check read's result) against 06H, the error a read gives for an FAH mark: the sector was written correctly.
4AE9
If the Z FLAG is set (06H), JUMP to 4AEDH to return with the Z FLAG.
4AEB
LD A,12H 3E 12
Load Register A with 12H, DIRECTORY WRITE ERROR; the NZ FLAG is still set. 4AE2H comes here.
4AED
POP DE D1
Restore Register Pair DE from the stack. 4ADBH and 4AE9H come here.
4AEE
POP BC C1
Restore Register Pair BC (Register B = locator, Register C = drive) from the stack.
4AEF
RET C9
RETURN with the Z FLAG for success, or the NZ FLAG and Register A = 10H or 12H.

4AF0H - Read the Allocation Table

Register C = drive. Reads sector 0 of the drive's directory track (the granule allocation table) into 4D00H-4DFFH, accepting it only with the FAH mark. Registers D, E, H and L are kept. Returns the Z FLAG, or Register A = 14H (GAT READ ERROR) and the NZ FLAG. Used by SYS3/SYS and SYS6/SYS.

4AF0
PUSH DE D5
Save Register Pair DE onto the stack. 4A07H calls here.
4AF1
PUSH HL E5
Save Register Pair HL (the caller's pointer) onto the stack; it is returned unchanged.
4AF2
GOSUB to 4B55H: Register D = the directory track of drive Register C (from 4304H plus the drive).
4AF5
LD E,00H 1E 00
Load Register E with 00H, sector 0 of the directory track, the allocation table.
4AF7
LD HL,4D00H 21 00 4D
Point Register Pair HL to 4D00H, the buffer the allocation table is kept in.
4AFA
GOSUB to 4B35H to read drive Register C, track Register D, sector Register E into 4D00H as a system sector.
4AFD
POP HL E1
Restore Register Pair HL (the caller's pointer) from the stack.
4AFE
POP DE D1
Restore Register Pair DE (the caller's Register Pair DE) from the stack.
4AFF
RET Z C8
If the Z FLAG is set (read correctly), RETURN with Register A = 00H.
4B00
LD A,14H 3E 14
Load Register A with 14H, error GAT READ ERROR (the allocation table could not be read).
4B02
RET C9
RETURN with Register A = 14H and the NZ FLAG from the read.

4B03H - Write the Allocation Table

Register C = drive; the allocation table is in 4D00H-4DFFH. Writes it to sector 0 of the directory track with the FAH mark and reads it back. Returns the Z FLAG, or Register A = 15H (GAT WRITE ERROR) and the NZ FLAG. Used by SYS3/SYS and SYS6/SYS.

4B03
PUSH DE D5
Save Register Pair DE onto the stack. 4AB9H calls here.
4B04
PUSH HL E5
Save Register Pair HL (the caller's pointer) onto the stack; it is returned unchanged.
4B05
GOSUB to 4B55H: Register D = the directory track of drive Register C.
4B08
LD E,00H 1E 00
Load Register E with 00H, sector 0 of the directory track.
4B0A
LD HL,4D00H 21 00 4D
Point Register Pair HL to 4D00H, the allocation table.
4B0D
GOSUB to 46EFH to write 4D00H to drive Register C, track Register D, sector Register E with the FAH data mark.
4B10
If the NZ FLAG is set (a write error), JUMP to 4B19H.
4B12
GOSUB to 46F3H to read the sector back without storing it.
4B15
CP 06H FE 06
Compare Register A (the check read's result) against 06H, the error a read gives for the FAH mark.
4B17
If the Z FLAG is set (06H: written correctly), JUMP to 4B1BH.
4B19
LD A,15H 3E 15
Load Register A with 15H, GAT WRITE ERROR; the NZ FLAG is still set. 4B10H comes here.
4B1B
POP HL E1
Restore Register Pair HL from the stack. 4B17H comes here.
4B1C
POP DE D1
Restore Register Pair DE (the caller's Register Pair DE) from the stack.
4B1D
RET C9
RETURN with the Z FLAG for success, or the NZ FLAG and Register A = 15H.

4B1EH - Directory Entry Locator to Track, Sector and Address

Register B = an entry locator, Register C = drive. Returns Register D = the drive's directory track, Register E = the sector (locator bits 0-4 plus 2), Register Pair HL = 4200H plus the entry offset (locator bits 5-7 times 20H) and the Z FLAG, or Register A = 10H (ILLEGAL LOGICAL FILE NUMBER) and the NZ FLAG when bits 0-4 are 8 or more.

4B1E
GOSUB to 4B55H: Register D = the directory track of drive Register C. 4AC3H and 4AD8H call here.
4B21
LD A,B 78
Copy the locator from Register B into Register A.
4B22
AND E0H E6 E0
AND Register A with E0H, keeping bits 5-7: the entry's offset in the sector (00H, 20H ... E0H).
4B24
LD L,A 6F
Copy the offset from Register A into Register L.
4B25
LD H,42H 26 42
Load Register H with 42H, so Register Pair HL = 4200H plus the offset: the entry's address in the sector buffer.
4B27
XOR B A8
XOR Register A (the offset bits) with Register B (the locator): only bits 0-4 remain, the sector index.
4B28
CP 08H FE 08
Compare Register A (the sector index) against 08H: the directory uses sectors 2-9.
4B2A
If the NO CARRY FLAG is set (index 8 or more), JUMP to 4B31H to return error 10H.
4B2C
ADD A,02H C6 02
ADD 02H to Register A: the sector number (2-9).
4B2E
LD E,A 5F
Copy the sector number from Register A into Register E.
4B2F
XOR A AF
Set Register A to ZERO and clear all flags: the Z FLAG tells the caller the locator was valid.
4B30
RET C9
RETURN with Register D = track, Register E = sector, Register Pair HL = the entry address and the Z FLAG.
4B31
LD A,10H 3E 10
Load Register A with 10H, ILLEGAL LOGICAL FILE NUMBER. 4B2AH comes here.
4B33
OR A B7
OR Register A (10H) with itself, setting the NZ FLAG.
4B34
RET C9
RETURN with Register A = 10H and the NZ FLAG.

4B35H - Read a System Sector

Register C = drive, Register D = track, Register E = sector, Register Pair HL = buffer. Directory and allocation table sectors are written with the FAH data mark, which a read reports as error 06H; this routine turns 06H into success. When the read succeeds with a normal mark instead, the diskette's directory is not on track Register D: the boot sector (track 0, sector 0) is read and its byte 2 becomes the drive's directory track at 4304H plus the drive, and the read is repeated. Used by SYS2/SYS, SYS3/SYS, SYS6/SYS and the start-up code (4ECBH).

4B35
GOSUB to 46DDH to read drive Register C, track Register D, sector Register E into the buffer at Register Pair HL. 4ACBH, 4AFAH and 4ECBH call here; 4B53H loops back here.
4B38
SUB 06H D6 06
SUBtract 06H from Register A (the read's error number): zero for 06H, the FAH mark of a system sector.
4B3A
RET Z C8
If the Z FLAG is set (the sector carries the FAH mark), RETURN with Register A = 00H.
4B3B
ADD A,06H C6 06
ADD 06H to Register A, giving back the read's error number.
4B3D
RET NZ C0
If the NZ FLAG is set (a real read error), RETURN with Register A = the error number.
4B3E
PUSH DE D5
Save Register Pair DE (Register D = track, Register E = sector) onto the stack: the sector read with a normal mark is not a system sector.
4B3F
LD DE,0000H 11 00 00
Load Register Pair DE with 0000H: track 0, sector 0, the boot sector.
4B42
GOSUB to 46DDH to read the boot sector of drive Register C into the buffer at Register Pair HL.
4B45
POP DE D1
Restore Register Pair DE (Register E = the sector wanted) from the stack.
4B46
RET NZ C0
If the NZ FLAG is set (the boot sector could not be read), RETURN with Register A = the error number.
4B47
PUSH HL E5
Save Register Pair HL (the buffer address) onto the stack.
4B48
INC HL 23
INCrement Register Pair HL (the buffer address) by 1, to the boot sector's byte 1.
4B49
INC HL 23
INCrement Register Pair HL by 1, so Register Pair HL = the boot sector's byte 2.
4B4A
LD D,(HL) 56
Fetch the boot sector's byte 2 from (HL) (Register Pair HL = buffer plus 2) into Register D: the diskette's directory track.
4B4B
LD A,04H 3E 04
Load Register A with 04H, the low byte of 4304H, the directory track table.
4B4D
ADD A,C 81
ADD Register C (the drive) to Register A: 04H plus the drive.
4B4E
LD L,A 6F
Copy 04H plus the drive from Register A into Register L.
4B4F
LD H,43H 26 43
Load Register H with 43H, so Register Pair HL = 4304H plus the drive, the drive's directory track byte.
4B51
LD (HL),D 72
Store the directory track (Register D) into (HL) (Register Pair HL = 4304H plus the drive).
4B52
POP HL E1
Restore Register Pair HL (the buffer address) from the stack.
4B53
JUMP to 4B35H to read sector Register E again, now on track Register D.

4B55H - Directory Track of a Drive

Register C = drive. Returns Register D = the directory track from 4304H plus the drive, and Register Pair HL = that address. Used by SYS2/SYS, SYS3/SYS and SYS6/SYS.

4B55
LD A,04H 3E 04
Load Register A with 04H. 4AF2H, 4B05H and 4B1EH call here.
4B57
ADD A,C 81
ADD Register C (the drive) to Register A: 04H plus the drive.
4B58
LD L,A 6F
Copy 04H plus the drive from Register A into Register L.
4B59
LD H,43H 26 43
Load Register H with 43H, so Register Pair HL = 4304H plus the drive.
4B5B
LD D,(HL) 56
Fetch the drive's directory track from (HL) (Register Pair HL = 4304H plus the drive) into Register D.
4B5C
RET C9
RETURN with Register D = the directory track.

4B5DH - Test a Bit Chosen at Run Time

Register A = a bit number (0-7), Register B = the byte to test. Builds BIT n,B in the operand at 4B68H and executes it, returning the Z FLAG when bit n of Register B is clear. 4A4FH and 4A90H use it to find a free granule in an allocation table byte.

4B5D
AND 07H E6 07
AND Register A (the bit number) with 07H, so only bits 0-2 are used. 4A4FH and 4A90H call here.
4B5F
RLCA 07
Rotate Register A (the bit number) left: times 2.
4B60
RLCA 07
Rotate Register A (the bit number) left again: times 4.
4B61
RLCA 07
Rotate Register A left again: times 8, the bit number now in bits 3-5.
4B62
OR 40H F6 40
OR Register A with 40H: 40H plus 8 times n is the second byte of BIT n,B.
4B64
LD (4B68H),A 32 68 4B
Store Register A into 4B68H, the second byte of the BIT instruction at 4B67H. Self-Modifying Code
40H (BIT 0,B) in the file.
4B67
BIT 0,B CB 40
Test bit n of Register B (the allocation byte), n being the bit number stored into 4B68H. Self-Modifying Code
The file shows BIT 0,B.
4B69
RET C9
RETURN with the Z FLAG set when the bit is clear (the granule is free).

4B6AH - Multiply a Record Number by the Record Length

Register Pair HL = the record number, Register A = the logical record length (00H stands for 256). Returns Register Pair HL = the product divided by 256 (the sector) and Register A = the product's low byte (the byte offset). Register Pair BC is kept; Register Pair DE returns the record number.

4B6A
PUSH BC C5
Save Register Pair BC onto the stack. 4712H calls here.
4B6B
EX DE,HL EB
Exchange Register Pairs DE and HL: Register Pair DE = the record number (the multiplicand).
4B6C
LD C,A 4F
Copy the record length from Register A into Register C (the multiplier).
4B6D
LD HL,0000H 21 00 00
Load Register Pair HL with 0000H: the low 16 bits of the product.
4B70
LD A,L 7D
Copy Register L (00H) into Register A: the product's high byte starts at zero.
4B71
LD B,08H 06 08
Load Register B with 08H: eight bits of the multiplier.
4B73
ADD HL,HL 29
Loop Start
ADD Register Pair HL to itself: shift the product left; the carry out of Register H goes into the CARRY FLAG. 4B7CH loops back here.
4B74
RLA 17
Rotate Register A (the product's high byte) left through the CARRY FLAG, taking the bit out of Register Pair HL.
4B75
RLC C CB 01
Rotate Register C (the multiplier) left: its highest remaining bit goes into the CARRY FLAG.
4B77
If the NO CARRY FLAG is set (the bit is 0), JUMP to 4B7CH.
4B79
ADD HL,DE 19
ADD Register Pair DE (the record number) to Register Pair HL (the product's low 16 bits).
4B7A
ADC A,00H CE 00
ADD the carry to Register A (the product's high byte) with ADC 00H.
4B7C
Loop End
DECrement Register B and loop back to 4B73H for the next multiplier bit. 4B77H comes here.
4B7E
LD C,A 4F
Copy the product's high byte from Register A into Register C.
4B7F
LD A,L 7D
Copy the product's low byte from Register L into Register A: the byte offset in the sector.
4B80
LD L,H 6C
Copy the product's middle byte from Register H into Register L.
4B81
LD H,C 61
Copy the high byte from Register C into Register H, so Register Pair HL = the product divided by 256.
4B82
POP BC C1
Restore the caller's Register Pair BC from the stack (saved at 4B6AH).
4B83
RET C9
RETURN with Register Pair HL = the sector and Register A = the byte offset.

4B84H - Divide by a Byte

Register Pair HL = the dividend, Register A = the divisor. Returns Register Pair HL = the quotient and Register A = the remainder; Register Pair DE is kept. 48E4H uses it with 5 (sectors per granule); SYS3/SYS calls it at 4EE2H.

4B84
PUSH DE D5
Save the caller's Register Pair DE onto the stack; Register D is used for the divisor. 48E4H calls here.
4B85
LD D,A 57
Copy the divisor from Register A into Register D.
4B86
LD E,10H 1E 10
Load Register E with 10H: sixteen bits of the dividend.
4B88
XOR A AF
Set Register A to ZERO and clear all flags: the remainder starts at zero.
4B89
ADD HL,HL 29
Loop Start
ADD Register Pair HL to itself: shift the dividend left; its top bit goes into the CARRY FLAG and a 0 quotient bit comes in at the bottom. 4B93H loops back here.
4B8A
RLA 17
Rotate Register A (the remainder) left through the CARRY FLAG, taking the dividend's top bit.
4B8B
If the CARRY FLAG is set (the remainder overflowed 8 bits, so it is larger than the divisor), JUMP to 4B90H to subtract.
4B8D
CP D BA
Compare Register A (the remainder) with Register D (the divisor).
4B8E
If the CARRY FLAG is set (the remainder is smaller than the divisor), JUMP to 4B92H with a 0 quotient bit.
4B90
SUB D 92
SUBtract Register D (the divisor) from Register A (the remainder). 4B8BH comes here.
4B91
INC L 2C
INCrement Register L by 1: the quotient bit just shifted into Register Pair HL becomes 1.
4B92
DEC E 1D
DECrement Register E (the bits left) by 1. 4B8EH comes here.
4B93
Loop End
If the NZ FLAG is set (bits are left), LOOP BACK to 4B89H.
4B95
POP DE D1
Restore the caller's Register Pair DE from the stack (saved at 4B84H).
4B96
RET C9
RETURN with Register Pair HL = the quotient and Register A = the remainder.

4B97H - End-of-File Byte Compare

Called by 48CFH when the next record number equals the ending record number, with Register C = the byte offset minus 1 and Index Register IX = the FCB. Returns Register A = (end-of-file byte minus 1) minus Register C, the Z FLAG when the offset is exactly the end, the CARRY FLAG when it is before the end, and Register C put back to the byte offset.

4B97
LD A,(IX+08H) DD 7E 08
Fetch the end-of-file byte from (IX+08H) (Index Register IX = the FCB) into Register A.
4B9A
DEC A 3D
DECrement Register A by 1 (an end byte of 00H, a full last sector, becomes FFH).
4B9B
SUB C 91
SUBtract Register C (the byte offset minus 1) from Register A: the borrow (CARRY FLAG) is set when the offset is past the end.
4B9C
CCF 3F
Complement the CARRY FLAG: now set when the offset is before the end.
4B9D
INC BC 03
INCrement Register Pair BC by 1, putting Register C back to the byte offset (INC BC does not change the flags).
4B9E
RET C9
RETURN to 48D2H with the flags of the compare.
4B9F
NOP 00
NOP (no operation). This byte is loaded with the routine above but nothing executes it or reads it.

4BA0H - Default Hook for RST 28H Codes Below 80H

The JP at 4312H points here until a program (BASIC/CMD) changes it. Returns Register A = 00H, so the ROM's keyboard routine (which executes RST 28H with Register A = 01H for BREAK at 0456H) reports no key.

4BA0
XOR A AF
Set Register A to ZERO and clear all flags. 4312H jumps here for an RST 28H code with bit 7 clear.
4BA1
RET C9
RETURN with Register A = 00H. The RST's own return address has been dropped by 4BA2H-4BA3H, so this returns to the caller of the routine that executed the RST.

4BA2H - RST 28H: Load and Call a System Overlay

Register A = the function code. Bits 0-3 name the overlay: its directory entry is in sector (bits 0-3) plus 2 of the directory track, entry 0, or for overlay numbers 8-15 in sector (bits 0-3) minus 6, entry 1 (offset 20H). Bits 4-6 select the function inside the overlay. The overlay is read through the FCB at 44A0H into 4E00H-51FFH or wherever its load records say, unless 430EH shows it is already there, and its transfer address is called with Register A = the code and the caller's Register Pair BC, Register Pair DE and Register Pair HL. The BREAK vector at 4315H is disarmed while the overlay runs. The RST's own return address is dropped, so the overlay returns to the caller of the routine that executed the RST.

4BA2
EX (SP),HL E3
Exchange Register Pair HL with the top of the stack (Stack Pointer = the RST's return address): Register Pair HL = that return address, the caller's Register Pair HL is on the stack. The RST 28H vector at 400CH jumps here.
4BA3
POP HL E1
Restore Register Pair HL (the caller's) from the stack; the RST's return address is dropped.
4BA4
OR A B7
OR Register A (the function code) with itself: the P FLAG is set when bit 7 is clear.
4BA5
If the P FLAG is set (code 00H-7FH), JUMP to 4312H, the hook for such codes (the ROM's BREAK code 01H arrives here).
4BA8
PUSH HL E5
Save Register Pair HL (the caller's) onto the stack.
4BA9
LD H,A 67
Copy the function code from Register A into Register H.
4BAA
LD A,(430EH) 3A 0E 43
Fetch the code of the last overlay call from 430EH into Register A.
4BAD
XOR H AC
XOR Register A (the last code) with Register H (the new code): the bits that differ.
4BAE
AND 0FH E6 0F
AND Register A with 0FH, keeping the overlay number bits: zero when the same overlay is wanted.
4BB0
LD A,H 7C
Copy the new function code from Register H into Register A (LD does not change the flags).
4BB1
LD (430EH),A 32 0E 43
Store Register A (the new function code) into 430EH.
4BB4
If the Z FLAG is set (the overlay is already in memory), JUMP to 4BEDH to call it at once.
4BB6
PUSH DE D5
Save Register Pair DE (the caller's) onto the stack.
4BB7
PUSH BC C5
Save Register Pair BC (the caller's) onto the stack.
4BB8
AND 0FH E6 0F
AND Register A (the function code) with 0FH: the overlay number.
4BBA
CP 08H FE 08
Compare Register A (the overlay number) against 08H.
4BBC
If the CARRY FLAG is set (overlay 0-7), JUMP to 4BC0H: the locator is the number itself (sector number plus 2, entry 0).
4BBE
ADD A,18H C6 18
ADD 18H to Register A (overlay 8-15): locator 20H-27H, entry 1 (offset 20H) of sectors 2-9.
4BC0
LD B,A 47
Copy the directory entry locator from Register A into Register B. 4BBCH comes here.
4BC1
LD (44A7H),A 32 A7 44
Store Register A (the locator) into 44A7H, byte 07H of the overlay FCB.
4BC4
XOR A AF
Set Register A to ZERO and clear all flags (the CARRY FLAG too).
4BC5
LD (44A1H),A 32 A1 44
Store Register A (00H) into 44A1H, the overlay FCB's flags: whole sectors, access level 0, buffer not marked.
4BC8
SBC HL,HL ED 62
SUBtract Register Pair HL and the CARRY FLAG (clear) from Register Pair HL: Register Pair HL = 0000H.
4BCA
LD (44AAH),HL 22 AA 44
Store Register Pair HL (0000H) into 44AAH, the overlay FCB's next record number: read from the first sector.
4BCD
LD C,A 4F
Copy Register A (00H) into Register C: drive 0, where the overlays are.
4BCE
GOSUB to 4AC1H to read the directory sector holding entry Register B of drive Register C (0) into 4200H; Register Pair HL = the entry.
4BD1
If the NZ FLAG is set (11H, DIRECTORY READ ERROR), JUMP to 4409H to display the error.
4BD4
LD A,L 7D
Copy the low byte of the entry address from Register L into Register A.
4BD5
ADD A,16H C6 16
ADD 16H to Register A: the offset of the entry's first extent.
4BD7
LD L,A 6F
Copy it into Register L from Register A, so Register Pair HL = entry+16H.
4BD8
LD A,(HL) 7E
Fetch the first extent's track from (HL) (Register Pair HL = entry+16H) into Register A.
4BD9
INC L 2C
INCrement Register L by 1, so Register Pair HL = entry+17H, the first extent's granule byte.
4BDA
LD H,(HL) 66
Fetch the first extent's granule byte from (HL) (Register Pair HL = entry+17H) into Register H.
4BDB
LD L,A 6F
Copy the track from Register A into Register L, so Register Pair HL = the granule byte and the track.
4BDC
LD (44AEH),HL 22 AE 44
Store Register Pair HL into 44AEH: Register L (track) into 44AEH and Register H (granule byte) into 44AFH, the overlay FCB's first extent.
4BDF
LD DE,44A0H 11 A0 44
Point Register Pair DE to 44A0H, the overlay FCB.
4BE2
GOSUB to 4C39H to load the overlay's load module records through the FCB at Register Pair DE; Register Pair HL returns the transfer address.
4BE5
If the NZ FLAG is set (a read or load error), JUMP to 4409H to display it.
4BE8
LD (4BFCH),HL 22 FC 4B
Store Register Pair HL (the overlay's transfer address) into 4BFCH, the operand of the CALL at 4BFBH. Self-Modifying Code
4BEB
POP BC C1
Restore Register Pair BC (the caller's) from the stack.
4BEC
POP DE D1
Restore Register Pair DE (the caller's) from the stack.
4BED
POP HL E1
Restore Register Pair HL (the caller's) from the stack. 4BB4H comes here.
4BEE
LD A,(4315H) 3A 15 43
Fetch the first byte of the BREAK vector from 4315H into Register A (C3H when armed, 00H when not).
4BF1
LD (4C00H),A 32 00 4C
Store Register A into 4C00H, the operand of LD A,nn at 4BFFH, to restore it afterwards. Self-Modifying Code
4BF4
XOR A AF
Set Register A to ZERO and clear all flags, the value that disarms the BREAK vector.
4BF5
LD (4315H),A 32 15 43
Store Register A (00H) into 4315H: the BREAK vector is disarmed while the overlay runs.
4BF8
LD A,(430EH) 3A 0E 43
Fetch the function code from 430EH into Register A for the overlay's dispatcher.
4BFB
GOSUB to the overlay's transfer address stored into 4BFCH by 4BE8H, with Register A = the function code and the caller's Register Pair BC, Register Pair DE and Register Pair HL. Self-Modifying Code
0000H in the file.
4BFE
PUSH AF F5
Save Register Pair AF (the overlay's result) onto the stack.
4BFF
LD A,00H 3E 00
Load Register A with the BREAK vector byte saved into 4C00H by 4BF1H. Self-Modifying Code
00H in the file.
4C01
LD (4315H),A 32 15 43
Store Register A into 4315H, putting the BREAK vector back as it was.
4C04
POP AF F1
Restore Register Pair AF (the overlay's result) from the stack.
4C05
RET C9
RETURN to the program that called the DOS entry with the overlay's Register A and flags.

4C06H - Load and Run a Program (4433H)

Register Pair DE = an FCB holding a program's filespec. The program is loaded through 4430H; on an error the message is displayed (4409H). Otherwise its transfer address is jumped to, or, when the DEBUG flag (bit 7 of 430FH) is set, DEBUG is entered first with the transfer address as the address to continue at.

4C06
PUSH HL E5
Save Register Pair HL onto the stack, keeping a place on the stack for the transfer address. SYS1/SYS's command interpreter jumps to 4433H, which comes here.
4C07
GOSUB to 4430H, the program loader at 4C16H, with Register Pair DE = the FCB; it returns Register Pair HL = the transfer address.
4C0A
If the NZ FLAG is set (the program could not be loaded), JUMP to 4409H to display the error.
4C0D
EX (SP),HL E3
Exchange Register Pair HL (the transfer address) with the top of the stack (Stack Pointer = the word saved at 4C06H): the transfer address is now the return address and Register Pair HL is the caller's again.
4C0E
LD A,(430FH) 3A 0F 43
Fetch the system flags from 430FH into Register A.
4C11
OR A B7
OR Register A (the system flags) with itself: the M FLAG is set when bit 7 (DEBUG) is set.
4C12
If the M FLAG is set (DEBUG on), JUMP to 400FH (RST 30H) to enter DEBUG with the program's transfer address on the stack.
4C15
RET C9
RETURN, which jumps to the program's transfer address.

4C16H - Load a Program (4430H)

Register Pair DE = an FCB holding a program's filespec. Opens it with the buffer at 4D00H and whole sectors, refuses a file of access level 6 or 7 while DEBUG is on (error 25H), and loads its records (4C39H). Errors from the open come back with bit 6 set; 18H (not in the directory) becomes 5FH, PROGRAM NOT FOUND with bit 6. Returns Register Pair HL = the transfer address and the Z FLAG.

4C16
LD B,00H 06 00
Load Register B with 00H: logical record length 00H (whole sectors) for the open. 4430H and SYS6/SYS (59A3H) come here.
4C18
LD HL,4D00H 21 00 4D
Point Register Pair HL to 4D00H, the sector buffer the loader takes its bytes from.
4C1B
GOSUB to 4424H to open the file in the FCB at Register Pair DE (SYS2/SYS) with Register Pair HL = buffer and Register B = record length.
4C1E
If the Z FLAG is set (the file is open and the FCB filled in), JUMP to 4C29H.
4C20
SET 6,A CB F7
Set bit 6 of Register A (the open's error number): SYS4/SYS will show only the message text.
4C22
CP 58H FE 58
Compare Register A against 58H: error 18H (FILE NOT IN DIRECTORY) with bit 6.
4C24
RET NZ C0
If the NZ FLAG is set (another error), RETURN with Register A = that error with bit 6.
4C25
LD A,5FH 3E 5F
Load Register A with 5FH: error 1FH (PROGRAM NOT FOUND) with bit 6.
4C27
OR A B7
OR Register A (5FH) with itself, setting the NZ FLAG.
4C28
RET C9
RETURN with Register A = 5FH and the NZ FLAG.
4C29
INC DE 13
INCrement Register Pair DE by 1, to FCB+01H. 4C1EH comes here.
4C2A
LD A,(DE) 1A
Fetch the FCB's flags from (DE) (Register Pair DE = FCB+01H) into Register A; bits 0-2 are the file's access level.
4C2B
DEC DE 1B
DECrement Register Pair DE by 1, back to the FCB.
4C2C
AND 07H E6 07
AND Register A (the FCB flags) with 07H, keeping the file's access level.
4C2E
CP 06H FE 06
Compare Register A (the access level) against 06H.
4C30
If the CARRY FLAG is set (level 0-5), JUMP to 4C39H to load.
4C32
LD A,(430FH) 3A 0F 43
Fetch the system flags from 430FH into Register A.
4C35
RLCA 07
Rotate Register A left: bit 7 (DEBUG) goes into the CARRY FLAG.
4C36
LD A,25H 3E 25
Load Register A with 25H, ILLEGAL ACCESS ATTEMPTED TO PROTECTED FILE (LD keeps the CARRY FLAG).
4C38
RET C D8
If the CARRY FLAG is set (DEBUG on), RETURN with Register A = 25H: a level 6 or 7 program cannot be loaded under DEBUG. With DEBUG off execution falls into the loader.

4C39H - Load Module Loader

Register Pair DE = an open FCB whose buffer is 4D00H. Reads the file a byte at a time (4C8CH) as records: a type byte, a length byte (00H = 256), then that many bytes. Type 01H is a load block (two-byte address, then data, each byte checked after storing); type 02H is the transfer address and ends the load; other types below 20H are skipped; 20H and above give error 22H. Returns Register Pair HL = the transfer address and the Z FLAG. 4BE2H uses it for overlays.

4C39
LD BC,4DFFH 01 FF 4D
Load Register Pair BC with 4DFFH: Register C = FFH, so the first byte fetch moves to offset 00H and reads the first sector. 4BE2H calls here; 4C30H comes here.
4C3C
Loop Start
GOSUB to 4C8CH to fetch the next byte, the record type, into Register A. 4C58H and 4C7FH loop back here.
4C3F
CP 01H FE 01
Compare Register A (the record type) against 01H, a load block.
4C41
If the Z FLAG is set (01H), JUMP to 4C67H to load it.
4C43
CP 02H FE 02
Compare Register A (the record type) against 02H, the transfer address.
4C45
If the Z FLAG is set (02H, the transfer address record), JUMP to 4C5AH.
4C47
CP 20H FE 20
Compare Register A (the record type) against 20H.
4C49
If the CARRY FLAG is set (another type below 20H, such as a name or comment record), JUMP to 4C4FH to skip it.
4C4B
LD A,22H 3E 22
Load Register A with 22H, LOAD FILE FORMAT ERROR.
4C4D
OR A B7
OR Register A (22H) with itself, setting the NZ FLAG.
4C4E
RET C9
RETURN with Register A = 22H and the NZ FLAG.
4C4F
GOSUB to 4C8CH to fetch the record's length byte into Register A. 4C49H comes here.
4C52
LD B,A 47
Copy the length from Register A into Register B (00H counts 256).
4C53
GOSUB to 4C8CH to fetch (and ignore) the next byte of the record. 4C56H loops back here.
4C56
DECrement Register B and loop back to 4C53H until the whole record has been skipped.
4C58
Loop End
LOOP BACK to 4C3CH for the next record.
4C5A
GOSUB to 4C8CH to fetch the transfer record's length byte (not used). 4C45H comes here.
4C5D
GOSUB to 4C8CH to fetch the low byte of the transfer address into Register A.
4C60
LD L,A 6F
Copy the low byte from Register A into Register L.
4C61
GOSUB to 4C8CH to fetch the high byte of the transfer address into Register A.
4C64
LD H,A 67
Copy the high byte from Register A into Register H, so Register Pair HL = the transfer address.
4C65
XOR A AF
Set Register A to ZERO and clear all flags: the load ended at its transfer record.
4C66
RET C9
RETURN with Register Pair HL = the transfer address, Register A = 00H and the Z FLAG.
4C67
GOSUB to 4C8CH to fetch the load block's length byte into Register A. 4C41H comes here.
4C6A
LD B,A 47
Copy the length from Register A into Register B: two address bytes plus the data.
4C6B
GOSUB to 4C8CH to fetch the low byte of the load address into Register A.
4C6E
LD L,A 6F
Copy the low byte from Register A into Register L.
4C6F
DEC B 05
DECrement Register B (the length) by 1 for the address byte.
4C70
GOSUB to 4C8CH to fetch the high byte of the load address into Register A.
4C73
LD H,A 67
Copy the high byte from Register A into Register H, so Register Pair HL = the load address.
4C74
DEC B 05
DECrement Register B by 1 for the second address byte: Register B = the number of data bytes (a length of 00H gives 254).
4C75
Loop Start
GOSUB to 4C8CH to fetch the next data byte into Register A. 4C7DH loops back here.
4C78
LD (HL),A 77
Store the data byte (Register A) into memory at (HL) (Register Pair HL = the load address).
4C79
CP (HL) BE
Compare Register A with (HL) (Register Pair HL = the load address): did the memory keep the byte?
4C7A
If the NZ FLAG is set (it did not), JUMP to 4C81H.
4C7C
INC HL 23
INCrement Register Pair HL (the load address) by 1.
4C7D
Loop End
DECrement Register B and loop back to 4C75H while data bytes remain.
4C7F
JUMP to 4C3CH for the next record once the whole load block is in memory.
4C81
LD A,(HL) 7E
Fetch what the location holds from (HL) (Register Pair HL = the failing load address) into Register A. 4C7AH comes here.
4C82
CPL 2F
Complement Register A (what the failing location holds), to test whether it can be written at all.
4C83
LD (HL),A 77
Store the complement (Register A) into (HL) (Register Pair HL = the failing address).
4C84
CP (HL) BE
Compare Register A (the complement) with (HL) (Register Pair HL = the failing address): the Z FLAG is set when the location took it.
4C85
LD A,63H 3E 63
Load Register A with 63H: error 23H (MEMORY FAULT) with bit 6 (LD keeps the flags).
4C87
RET NZ C0
If the NZ FLAG is set (the location did not take its complement either, as with ROM or no memory), RETURN with Register A = 63H.
4C88
LD A,64H 3E 64
Load Register A with 64H: error 24H (ATTEMPTED TO LOAD READ ONLY MEMORY) with bit 6, returned when the location did take its complement.
4C8A
OR A B7
OR Register A (64H) with itself, setting the NZ FLAG.
4C8B
RET C9
RETURN with Register A = 64H and the NZ FLAG.

4C8CH - Fetch the Next Byte for the Loader

Register C = the byte offset in the buffer at 4D00H, Register Pair DE = the open FCB. Moves Register C on by one; when it wraps to 00H the next sector of the file is read into the FCB's buffer through 481BH. Returns Register A = the byte at 4D00H plus Register C. On a read error it drops the loader's return address and returns to the loader's caller with the error number plus bit 6.

4C8C
INC C 0C
INCrement Register C (the byte offset in the buffer) by 1. The loader calls here ten times (4C3CH-4C75H).
4C8D
If the NZ FLAG is set (still inside the sector in the buffer), JUMP to 4CA3H to fetch the byte.
4C8F
PUSH DE D5
Save Register Pair DE (the FCB address) onto the stack.
4C90
EX (SP),IX DD E3
Exchange Index Register IX with the top of the stack (Stack Pointer = the FCB address): Index Register IX = the FCB and the caller's Index Register IX is saved.
4C92
PUSH HL E5
Save Register Pair HL (the load address) onto the stack.
4C93
PUSH DE D5
Save Register Pair DE (the FCB address) onto the stack.
4C94
PUSH BC C5
Save Register Pair BC (Register B = the loader's count, Register C = 00H) onto the stack.
4C95
GOSUB to 481BH to read the sector at the FCB's next record number into its buffer (4D00H) and add 1 to the next record number.
4C98
POP BC C1
Restore Register Pair BC (Register B = the loader's count, Register C = 00H) from the stack.
4C99
POP DE D1
Restore Register Pair DE (the FCB address) from the stack.
4C9A
POP HL E1
Restore Register Pair HL (the load address) from the stack.
4C9B
POP IX DD E1
Restore the caller's Index Register IX from the stack.
4C9D
If the Z FLAG is set (read without error), JUMP to 4CA3H to fetch the first byte of the new sector.
4C9F
POP BC C1
Restore Register Pair BC from the stack, dropping the return address into the loader.
4CA0
OR 40H F6 40
OR Register A (the error number, 1CH at the end of the file) with 40H: SYS4/SYS will show only the message text.
4CA2
RET C9
RETURN to the loader's caller with Register A = the error number plus 40H and the NZ FLAG.
4CA3
PUSH BC C5
Save Register Pair BC (Register B = the loader's count) onto the stack. 4C8DH and 4C9DH come here.
4CA4
LD B,4DH 06 4D
Load Register B with 4DH, so Register Pair BC = 4D00H plus the byte offset in Register C.
4CA6
LD A,(BC) 0A
Fetch the byte from (BC) (Register Pair BC = 4D00H plus the offset) into Register A.
4CA7
POP BC C1
Restore Register Pair BC (Register B = the loader's count, Register C = the offset) from the stack.
4CA8
RET C9
RETURN to the loader with Register A = the byte at 4D00H plus the offset.

4CA9H - Time Display Task

A task control block for timer slot 6, where SYS6/SYS puts it (52C8H-52D0H). Slot 6 runs once every eight ticks and the counter at 4CABH lets five runs pass, so once a second the time is written as HH:MM:SS into the top line of the screen at 3C35H-3C3CH.

4CA9-4CAA
DEFW 4CACH AC 4C
Time Task + 00H: Routine Address
4CACH. 457BH-458DH jump to it with Index Register IX = 4CA9H.
4CAB
DEFB 05H 05
Time Task + 02H: Countdown
05H. 4CACH subtracts 1 on each run of slot 6 and 4CB0H reloads 05H.
4CAC
DEC (IX+02H) DD 35 02
DECrement the countdown at (IX+02H) (Index Register IX = 4CA9H, so 4CABH) by 1.
4CAF
RET NZ C0
If the NZ FLAG is set (fewer than five runs since the last display), RETURN to the clock routine.
4CB0
LD (IX+02H),05H DD 36 02 05
Store 05H into (IX+02H) (Index Register IX = 4CA9H, so 4CABH), starting the next count.
4CB4
LD HL,3C35H 21 35 3C
Point Register Pair HL to 3C35H, the top line of the screen at column 53, where the time is shown.

4CB7H - Time and Date as Text (446DH and 4470H)

4CB7H writes the time as HH:MM:SS and 4CD2H the date as MM/DD/YY, eight characters at Register Pair HL. Each of the three values (00-99, binary) is turned into two decimal digits; the separator is in Register C. Register Pair HL returns just past the text.

4CB7
LD DE,4043H 11 43 40
Point Register Pair DE to 4043H, the hours; the minutes and seconds follow downward at 4042H and 4041H. 446DH and the time task come here.
4CBA
LD C,3AH 0E 3A
Load Register C with 3AH, the colon written between the values.
4CBC
LD B,03H 06 03
Load Register B with 03H: three values. 4CD7H comes here for the date.
4CBE
LD A,(DE) 1A
Loop Start
Fetch the next value from (DE) (Register Pair DE = 4043H, 4042H, 4041H or 4046H, 4045H, 4044H) into Register A. 4CD0H loops back here.
4CBF
DEC DE 1B
DECrement Register Pair DE by 1, to the next value.
4CC0
LD (HL),2FH 36 2F
Store 2FH, one below the digit 0, into (HL) (Register Pair HL = the tens digit's place).
4CC2
INC (HL) 34
INCrement the tens digit at (HL) (Register Pair HL = the tens digit's place) by 1. 4CC5H loops back here.
4CC3
SUB 0AH D6 0A
SUBtract 0AH (10) from Register A (the value).
4CC5
If the NO CARRY FLAG is set (no borrow: another ten), LOOP BACK to 4CC2H.
4CC7
ADD A,3AH C6 3A
ADD 3AH to Register A: the units digit (value minus the tens, minus 10, plus 3AH) in ASCII.
4CC9
INC HL 23
INCrement Register Pair HL by 1, to the units digit's place.
4CCA
LD (HL),A 77
Store the units digit (Register A) into (HL) (Register Pair HL = the units digit's place).
4CCB
INC HL 23
INCrement Register Pair HL by 1, past the two digits.
4CCC
DEC B 05
DECrement Register B (the number of values still to write) by 1.
4CCD
RET Z C8
If the Z FLAG is set (all three values written), RETURN with Register Pair HL just past the text.
4CCE
LD (HL),C 71
Store the separator (Register C: a colon or a slash) into (HL) (Register Pair HL = the place after the digits).
4CCF
INC HL 23
INCrement Register Pair HL by 1, past the separator.
4CD0
Loop End
LOOP BACK to 4CBEH for the next value.
4CD2
LD DE,4046H 11 46 40
Point Register Pair DE to 4046H, the month; the day and year follow downward at 4045H and 4044H. 4470H comes here.
4CD5
LD C,2FH 0E 2F
Load Register C with 2FH, the slash written between the values.
4CD7
JUMP to 4CBCH to write the three values with Register C = slash.

4CD9H - Program Address Display Task (TRACE)

A task control block for timer slot 11, where SYS1/SYS's TRACE command puts it (5191H-519CH). Slot 11 runs on every clock tick, called from 4571H, so the address the interrupted program was executing is the word 12H bytes up the stack; it is shown as four hexadecimal digits at the top of the screen at 3C2EH-3C31H.

4CD9-4CDA
DEFW 4CDBH DB 4C
Trace Task + 00H: Routine Address
4CDBH. 457BH-458DH jump to it with Index Register IX = 4CD9H; the routine does not use Index Register IX.
4CDB
LD HL,0012H 21 12 00
Load Register Pair HL with 0012H: the offset up the stack of the interrupted program's address (the return to 4574H, 4547H, the five registers saved at 4538H-453CH and the two at 4518H-4519H lie below it).
4CDE
ADD HL,SP 39
ADD the Stack Pointer to Register Pair HL: Register Pair HL = the address of the interrupted program's address on the stack.
4CDF
LD E,(HL) 5E
Fetch the low byte of the interrupted address from (HL) (Register Pair HL = its place on the stack) into Register E.
4CE0
INC HL 23
INCrement Register Pair HL by 1, to the high byte of the interrupted program's address.
4CE1
LD D,(HL) 56
Fetch the high byte from (HL) (Register Pair HL = the next byte on the stack) into Register D, so Register Pair DE = the interrupted program's address.
4CE2
LD HL,3C2EH 21 2E 3C
Point Register Pair HL to 3C2EH, the top line of the screen at column 46.
4CE5
LD A,D 7A
Copy the high byte of the address from Register D into Register A.
4CE6
GOSUB to 4CEAH to show Register A as two hexadecimal digits at Register Pair HL.
4CE9
LD A,E 7B
Copy the low byte of the address from Register E into Register A; execution falls into 4CEAH to show it and return to the clock routine.

4CEAH - Show a Byte as Two Hexadecimal Digits

Register A = the byte, Register Pair HL = the screen address. Writes the high digit and then the low digit and returns with Register Pair HL past them.

4CEA
PUSH AF F5
Save Register Pair AF (Register A = the byte) onto the stack. 4CE6H calls here; 4CE9H falls in.
4CEB
RRA 1F
Rotate Register A right through the CARRY FLAG.
4CEC
RRA 1F
Rotate Register A right through the CARRY FLAG again.
4CED
RRA 1F
Rotate Register A right through the CARRY FLAG again.
4CEE
RRA 1F
Rotate Register A right through the CARRY FLAG again: the high four bits are now bits 0-3.
4CEF
GOSUB to 4CF3H to write the high digit at Register Pair HL.
4CF2
POP AF F1
Restore Register Pair AF (Register A = the byte) from the stack; execution falls into 4CF3H for the low digit.
4CF3
AND 0FH E6 0F
AND Register A with 0FH, keeping one hexadecimal digit (0-15).
4CF5
ADD A,30H C6 30
ADD 30H to Register A (the digit value): ASCII 0-9 for the values 0-9.
4CF7
CP 3AH FE 3A
Compare Register A against 3AH, one past the digit 9.
4CF9
If the CARRY FLAG is set (0-9), JUMP to 4CFDH.
4CFB
ADD A,07H C6 07
ADD 07H to Register A: ASCII A-F for the digit values 10-15.
4CFD
LD (HL),A 77
Store the digit (Register A) into the screen at (HL) (Register Pair HL = the screen address). 4CF9H comes here.
4CFE
INC HL 23
INCrement Register Pair HL by 1, to the next screen position.
4CFF
RET C9
RETURN with Register Pair HL pointing at the next screen position, past the digit.

4D00H - Overlay and Allocation Table Buffer

Not part of the file. 4D00H-4DFFH is the 256-byte buffer the program loader (4C16H) and the overlay loader (FCB at 44A0H) read sectors into, and where 4AF0H and 4B03H keep the allocation table while granules are allocated.

4D00-4DFF
DEFS 256
Sector Buffer at 4D00H
4C18H opens programs with this buffer and 44A3H-44A4H give it to the overlay FCB; 4CA3H-4CA6H take the load module bytes from 4D00H plus the offset in Register C. 4AF7H and 4B0AH read and write the allocation table here (one byte per track: bit 0 = granule 0, bit 1 = granule 1, FFH = track full). BOOT/SYS reads SYS0/SYS's directory entry here; SYS2/SYS and SYS6/SYS use the buffer as well.

4E00H - Start-Up

BOOT/SYS loads SYS0/SYS and jumps to its transfer address 4E00H. This code runs once and is then overwritten by the overlays, which load at 4E00H. It finds the top of memory, installs the keyboard driver, copies the device control blocks, sets up the interrupt handler, the clock and the disk interrupt routine, shows the sign-on message and then runs the AUTO command stored in the allocation table sector, unless ENTER is down or no AUTO command is set. The start-up code does not enable interrupts itself; SYS1/SYS's DOS READY code does (EI at 4E1FH).

4E00
DI F3
Disable interrupts (DI) while the vectors and the RST 38H handler are set up.
4E01
IM 1 ED 56
Select interrupt mode 1 (IM 1), so a maskable interrupt executes RST 38H, which the ROM sends to 4012H.
4E03
LD SP,41FCH 31 FC 41
Set the Stack Pointer to 41FCH, the DOS stack below the 4200H sector buffer.
4E06
LD HL,5200H 21 00 52
Load Register Pair HL with 5200H, the first address above the 4E00H-51FFH overlay area.
4E09
LD (4047H),HL 22 47 40
Store Register Pair HL (5200H) into 4047H, the lowest free address.
4E0C
LD HL,FFFFH 21 FF FF
Load Register Pair HL with FFFFH, the highest address the RAM test tries.
4E0F
LD A,(HL) 7E
Loop Start
Fetch the byte at (HL) (Register Pair HL = the address being tested) into Register A. 4E1BH loops back here.
4E10
LD B,A 47
Copy the byte from Register A into Register B to put it back afterwards.
4E11
CPL 2F
Complement Register A (the byte read from the address being tested).
4E12
LD (HL),A 77
Store the complement (Register A) into (HL) (Register Pair HL = the address being tested).
4E13
CP (HL) BE
Compare Register A (the complement) with (HL) (Register Pair HL = the address): the Z FLAG is set when the location is RAM.
4E14
LD (HL),B 70
Store the original byte (Register B) back into (HL) (Register Pair HL = the address); LD keeps the flags.
4E15
If the Z FLAG is set (RAM found), JUMP to 4E1DH.
4E17
LD A,H 7C
Copy the high byte of the address from Register H into Register A.
4E18
SUB 04H D6 04
SUBtract 04H from Register A: 1K (400H) lower.
4E1A
LD H,A 67
Copy the new high byte from Register A into Register H (Register L stays FFH).
4E1B
Loop End
LOOP BACK to 4E0FH to test the next address down.
4E1D
LD (4049H),HL 22 49 40
Store Register Pair HL (the highest RAM address found, FFFFH, BFFFH or 7FFFH for 48K, 32K or 16K) into 4049H, the top of memory. 4E15H comes here.
4E20
JUMP to 4F20H, a separate load record that installs the keyboard driver and comes back to 4E23H.
4E23
LD (43B8H),HL 22 B8 43
Store Register Pair HL (43D8H, set by 4F20H) into 43B8H, the keyboard driver address at start-up. 4F26H comes here.
4E26
LD (43C2H),HL 22 C2 43
Store Register Pair HL (43D8H) into 43C2H, the driver address of device list entry 1 (KI).
4E29
LD HL,(401BH) 2A 1B 40
Fetch the word at 401BH-401CH (bytes 6-7 of the keyboard control block, the name KI) into Register Pair HL.
4E2C
LD (43C0H),HL 22 C0 43
Store Register Pair HL (the name KI) into 43C0H, device list entry 1.
4E2F
LD HL,(401EH) 2A 1E 40
Fetch the video driver address from 401EH-401FH into Register Pair HL.
4E32
LD (43BAH),HL 22 BA 43
Store Register Pair HL (the video driver address) into 43BAH.
4E35
LD (43C6H),HL 22 C6 43
Store Register Pair HL (the video driver address) into 43C6H, the driver address of device list entry 2 (DO).
4E38
LD HL,(4023H) 2A 23 40
Fetch the word at 4023H-4024H (bytes 6-7 of the video control block, the name DO) into Register Pair HL.
4E3B
LD (43C4H),HL 22 C4 43
Store Register Pair HL (the name DO) into 43C4H, device list entry 2.
4E3E
LD HL,(4026H) 2A 26 40
Fetch the printer driver address from 4026H-4027H into Register Pair HL.
4E41
LD (43BCH),HL 22 BC 43
Store Register Pair HL (the printer driver address) into 43BCH.
4E44
LD (43CAH),HL 22 CA 43
Store Register Pair HL (the printer driver address) into 43CAH, the driver address of device list entry 3 (PR).
4E47
LD HL,(402BH) 2A 2B 40
Fetch the word at 402BH-402CH (bytes 6-7 of the printer control block, the name PR) into Register Pair HL.
4E4A
LD (43C8H),HL 22 C8 43
Store Register Pair HL (the name PR) into 43C8H, device list entry 3.
4E4D
LD HL,43CCH 21 CC 43
Point Register Pair HL to 43CCH, the byte after the device list.
4E50
LD (HL),00H 36 00
Store 00H into (HL) (Register Pair HL = 43CCH): the end of the device list.
4E52
INC L 2C
INCrement Register L by 1 (Register Pair HL = 43CDH), the step of a clearing loop that the JR below does not repeat.
4E53
If the NZ FLAG is set, JUMP to 4E55H. The displacement is 00H, so both outcomes go on at 4E55H and only 43CCH is cleared.
4E55
LD A,03H 3E 03
Load Register A with 03H, a loop count for the copy below; the JR NZ at 4E74H has a displacement of 00H, so the body runs only once.
4E57
LD HL,4015H 21 15 40
Point Register Pair HL to 4015H, the keyboard device control block.
4E5A
LD DE,4358H 11 58 43
Point Register Pair DE to 4358H, where its copy goes.
4E5D
PUSH AF F5
Save Register Pair AF (Register A = the loop count) onto the stack.
4E5E
LD BC,0008H 01 08 00
Load Register Pair BC with 0008H, the size of a device control block.
4E61
LDIR ED B0
Copy Register Pair BC (eight) bytes from Register Pair HL (4015H) to Register Pair DE (4358H): the keyboard control block, with 43D8H as its driver. Register Pair DE ends at 4360H.
4E63
XOR A AF
Set Register A to ZERO and clear all flags: the value for the eight bytes after the copy.
4E64
LD B,08H 06 08
Load Register B with 08H: eight zero bytes at 4360H-4367H.
4E66
LD (DE),A 12
Loop Start
Store Register A (00H) into (DE) (Register Pair DE = 4360H-4367H). 4E68H loops back here.
4E67
INC DE 13
INCrement Register Pair DE by 1, to the next byte of 4360H-4367H.
4E68
Loop End
DECrement Register B and loop back to 4E66H until 4360H-4367H are 00H.
4E6A
LD A,FFH 3E FF
Load Register A with FFH, the value for the next sixteen bytes.
4E6C
LD B,10H 06 10
Load Register B with 10H: sixteen FFH bytes at 4368H-4377H.
4E6E
LD (DE),A 12
Loop Start
Store Register A (FFH) into (DE) (Register Pair DE = 4368H-4377H). 4E70H loops back here.
4E6F
INC DE 13
INCrement Register Pair DE by 1, to the next byte of 4368H-4377H.
4E70
Loop End
DECrement Register B and loop back to 4E6EH until 4368H-4377H are FFH.
4E72
POP AF F1
Restore Register Pair AF (Register A = the loop count, 03H) from the stack.
4E73
DEC A 3D
DECrement Register A (the loop count) by 1, to 02H; the JR NZ below goes on at 4E76H anyway.
4E74
If the NZ FLAG is set, JUMP to 4E76H. The displacement is 00H, so the copy above is not repeated for the video and printer control blocks.
4E76
LD A,(37ECH) 3A EC 37
Fetch the WD1771 status register from 37ECH into Register A; reading it clears any pending disk controller interrupt.
4E79
XOR A AF
Set Register A to ZERO and clear all flags: the value for 4315H and 404CH.
4E7A
LD (4315H),A 32 15 43
Store Register A (00H) into 4315H: the BREAK vector is not armed.
4E7D
LD HL,404CH 21 4C 40
Point Register Pair HL to 404CH, the interrupt enable mask.
4E80
LD (HL),A 77
Store Register A (00H) into (HL) (Register Pair HL = 404CH): no interrupt source is serviced yet.
4E81
LD A,C3H 3E C3
Load Register A with C3H, the opcode of JP, for the RST 38H vector at 4012H.
4E83
LD DE,4518H 11 18 45
Load Register Pair DE with 4518H, the interrupt handler.
4E86
LD (4013H),DE ED 53 13 40
Store Register Pair DE (4518H) into 4013H-4014H, the address of the RST 38H vector.
4E8A
LD (4012H),A 32 12 40
Store Register A (C3H) into 4012H: the RST 38H vector is now JP 4518H.
4E8D
LD DE,4560H 11 60 45
Load Register Pair DE with 4560H, the clock tick routine.
4E90
LD (405BH),DE ED 53 5B 40
Store Register Pair DE (4560H) into 405BH, the vector for interrupt bit 7.
4E94
SET 7,(HL) CB FE
Set bit 7 of (HL) (Register Pair HL = 404CH): interrupt bit 7 (the clock) is serviced.
4E96
LD DE,4669H 11 69 46
Load Register Pair DE with 4669H, the routine that waits for the disk controller and reads its status.
4E99
LD (4059H),DE ED 53 59 40
Store Register Pair DE (4669H) into 4059H, the vector for interrupt bit 6.
4E9D
SET 6,(HL) CB F6
Set bit 6 of (HL) (Register Pair HL = 404CH): interrupt bit 6 (the disk controller) is serviced.
4E9F
LD A,01H 3E 01
Load Register A with 01H, the starting value of the system flags at 430FH.
4EA1
LD (430FH),A 32 0F 43
Store Register A (01H) into 430FH, the system flags: DEBUG (bit 7) off.
4EA4
LD DE,45AFH 11 AF 45
Load Register Pair DE with 45AFH, the clock task control block.
4EA7
LD A,07H 3E 07
Load Register A with 07H, timer slot 7, which runs once every eight clock ticks.
4EA9
GOSUB to 4410H to put the clock task in Register Pair DE into slot Register A (7).
4EAC
LD HL,4EEDH 21 ED 4E
Point Register Pair HL to 4EEDH, the sign-on message (each character stored negated).
4EAF
LD A,(HL) 7E
Loop Start
Fetch the next stored character from (HL) (Register Pair HL = the message pointer) into Register A. 4EB8H loops back here.
4EB0
NEG ED 44
NEGate Register A, giving the real character (0 minus the stored byte).
4EB2
GOSUB to ROM routine at 0033H to display the character in Register A.
4EB5
INC HL 23
INCrement Register Pair HL (the message pointer) by 1.
4EB6
CP 0DH FE 0D
Compare Register A (the character) against 0DH, ENTER, the last character of the message.
4EB8
Loop End
If the NZ FLAG is set (not the end), LOOP BACK to 4EAFH.
4EBA
GOSUB to ROM routine at 002BH to scan the keyboard once: Register A = the key that is down, or 00H.
4EBD
CP 0DH FE 0D
Compare Register A (the key) against 0DH, ENTER.
4EBF
If the Z FLAG is set (ENTER is down), JUMP to 4400H, DOS READY, without running the AUTO command.
4EC2
LD C,00H 0E 00
Load Register C with 00H, drive 0, the system diskette.
4EC4
LD D,11H 16 11
Load Register D with 11H, track 17, the directory track.
4EC6
LD E,00H 1E 00
Load Register E with 00H, sector 0: the allocation table sector.
4EC8
LD HL,4200H 21 00 42
Point Register Pair HL to 4200H, the sector buffer.
4ECB
GOSUB to 4B35H to read drive Register C, track Register D, sector Register E into 4200H as a system sector (switching to the diskette's own directory track when needed).
4ECE
If the NZ FLAG is set (the sector could not be read), JUMP to 4409H to display the error.
4ED1
LD A,(42E0H) 3A E0 42
Fetch the first character of the AUTO command from 42E0H into Register A (bytes E0H-FFH of the allocation table sector).
4ED4
CP 0DH FE 0D
Compare Register A against 0DH: an AUTO command that is only ENTER means none is set.
4ED6
If the Z FLAG is set (no AUTO command), JUMP to 4400H, DOS READY.
4ED9
LD HL,42E0H 21 E0 42
Point Register Pair HL to 42E0H, the AUTO command in the sector buffer.
4EDC
LD DE,4318H 11 18 43
Point Register Pair DE to 4318H, the command line buffer.
4EDF
LD BC,0020H 01 20 00
Load Register Pair BC with 0020H, the 32 bytes of the AUTO command.
4EE2
LDIR ED B0
Copy Register Pair BC (32) bytes from Register Pair HL (42E0H) to Register Pair DE (4318H).
4EE4
LD HL,4318H 21 18 43
Point Register Pair HL to 4318H, the AUTO command in the command line buffer.
4EE7
GOSUB to 4467H to display the command at Register Pair HL (up to its 0DH).
4EEA
JUMP to 4405H to run the command at Register Pair HL (4318H) through SYS1/SYS.
4EED-4EF7
DEFB ACH, AEH, ADH, BCH, B1H, ADH, E0H, D3H, E0H, BCH, B7H AC AE AD BC B1 AD E0 D3 E0 BC B7
Sign-On Message, Stored Negated
4EB0H negates each byte before it is displayed: TRSDOS - DI.
4EF8-4F02
DEFB ADH, B5H, E0H, B1H, B0H, BBH, AEH, BFH, ACH, B7H, B2H AD B5 E0 B1 B0 BB AE BF AC B7 B2
Sign-On Message (continued)
Negated, these eleven bytes read SK OPERATIN.
4F03-4F0D
DEFB B9H, E0H, ADH, A7H, ADH, ACH, BBH, B3H, E0H, D3H, E0H B9 E0 AD A7 AD AC BB B3 E0 D3 E0
Sign-On Message (continued)
Negated, these eleven bytes read G SYSTEM followed by a space, a hyphen and a space.
4F0E-4F18
DEFB AAH, BBH, AEH, E0H, CEH, D2H, CDH, E0H, E0H, F6H, F3H AA BB AE E0 CE D2 CD E0 E0 F6 F3
Sign-On Message (end)
Negated: VER 2.3, two spaces, 0AH (line feed) and 0DH (ENTER), which ends the display loop. The whole message is TRSDOS - DISK OPERATING SYSTEM - VER 2.3.
4F19-4F1F
DEFS 7
Not Part of the File
SYS0/SYS has no load record for 4F19H-4F1FH.

4F20H - Install the Keyboard Driver

A separate load record reached by the JP at 4E20H. It puts the keyboard scan with debounce at 43D8H into the ROM's keyboard control block and goes back to the start-up code with Register Pair HL = 43D8H.

4F20
LD HL,43D8H 21 D8 43
Load Register Pair HL with 43D8H, the TRSDOS 2.3 keyboard scan.
4F23
LD (4016H),HL 22 16 40
Store Register Pair HL (43D8H) into 4016H, the driver address in the keyboard device control block, so the ROM's keyboard calls (002BH, 0049H) use it.
4F26
JUMP to 4E23H to go on with the start-up with Register Pair HL = 43D8H.