TRS-80 DOS - VTOS 4.0 for the Model I - SYS10/SYS Disassembled

Page Customization

Introduction:

VTOS 4.0 SYS10/SYS Disassembly - Sector Write-Back Overlay (Model I)

SYS10/SYS is the sector write-back overlay for VTOS 4.0 on the TRS-80 Model I. Its sole purpose is to flush a modified sector from an FCB's (File Control Block's) data buffer back to disk, then optionally clear the FCB's internal buffer so it is ready for the next sector.

The overlay loads at 4E00H in the standard VTOS overlay slot and occupies the range 4E00H-4E77H (120 bytes of executable code). It is reached via the RST 28H overlay dispatcher in SYS0.

The overall logic proceeds in two distinct phases depending on the dispatch byte received in the SVC mechanism:

  • Phase 1 - Data Sector Write Path (entry at 4E00H). The overlay is entered with a dispatch byte whose upper nibble encodes the operation class. The code first checks whether the FCB's internal info byte (read from the IX register, which points to the FCB) indicates a data sector that actually needs to be written (AND A,0E0H tests the three directory-ownership bits; zero means the FCB holds a pure data sector). If the sector is already clean (zero), execution jumps forward to the FCB-clear path at 4E6DH. Otherwise the overlay calls the standard directory-sector context setup at 49EBH, locates the appropriate drive configuration entry, confirms the sector buffer address and record/sector coordinates are valid, constructs the full 16-bit disk sector address into DE from the FCB's extent-chain table, and writes the sector to disk via 4768H with a read-after-write verify via 4772H. After a successful write, the FCB's dirty-sector indicator byte at (HL) is cleared (RES 4) and the buffer clear loop executes.
  • Phase 2 - FCB Buffer Clear Path (entry at 4E6DH). When the sector carries a non-zero directory-ownership value (upper three bits of FCB info byte are non-zero), the overlay is performing a directory sector write-back rather than a data write. In this case it calls the alternate context setup at 49E9H, walks the two drive configuration tables (4015H-402CH and 43C0H-43D7H) to find the matching drive entry (matched by the two-byte drive-type ID in IX+06H/IX+07H), validates that the entry's bit 3 is set (marking it as a terminal/active entry), then clears the 8-byte FCB internal buffer by writing 00H to (IX+00H) through (IX+07H) via a DJNZ loop.

All error returns from subroutine calls use the standard VTOS convention: A contains the error code and RET NZ returns it to the caller. Error codes returned include 14H (Read Error from the write-verify read-back), 15H (Write Error from the sector write), 16H (Directory Read Error), 17H (Directory Write Error), 25H (sector address out of range), and 28H (no matching active drive configuration entry found).

Key Variables and Data Locations

Address / FieldPurpose
IX+00HFCB status/flags byte. Also the first byte zeroed by the FCB-clear loop.
IX+01HFCB drive config table entry pointer (low byte). Used to locate the drive configuration entry for the sector write.
IX+06HFCB drive number. Loaded into C for the drive configuration table scan.
IX+07HFCB directory entry info byte. Loaded into B for the drive configuration table scan.
4015H-402CHDrive configuration table #1 (3 entries x 8 bytes). Scanned first during drive lookup.
43C0H-43D7HDrive configuration table #2 (3 entries x 8 bytes). Scanned when table #1 yields no match.
4E56HSelf-Modifying Code
Two-byte runtime variable. Stores the 16-bit disk sector address (DE) extracted from the FCB extent chain table at the time of the sector write. Written by LD (4E56H),DE at 4E2FH. The disassembly listing shows the initial cold-start value 0000H at 4E55H-4E57H.

Major Routines

AddressName and Purpose
4E00HSYS10 Entry / Dispatch Gate
Entry point for all SYS10 calls. Tests the upper nibble of the dispatch byte against 10H. If not 10H, returns immediately (wrong overlay). Otherwise falls through to the data sector write path.
4E0AHData Sector Write Path
Tests FCB info byte upper 3 bits; if zero (pure data sector), calls context setup, resolves drive config, validates sector address, writes sector to disk with verify, clears dirty flag, then clears FCB buffer.
4E6DHDirectory Sector Write-Back / FCB Buffer Clear
Called when the FCB info byte has non-zero upper bits. Calls alternate context setup, scans drive config tables for the matching drive, validates the entry, zeroes 8 bytes of the FCB internal buffer.
4EE4HSector Address Extraction Subroutine
Reads the sector address from the FCB extent-chain table entry pointed to by HL. Extracts the track/sector word into DE, stores it to the self-modifying operand at 4E56H, rotates the track byte to select the drive step rate, and encodes the sector count into the CB-prefix bit-manipulation operand at 4F09H and the FDC sector field.

Cross-Reference Notes

SYS10 is called from SYS0 via the RST 28H overlay dispatcher whenever a file write operation determines that the FCB's current sector buffer must be flushed to disk before the next sector can be loaded. It is the write counterpart to the sector-read mechanism built into SYS0's file I/O engine.

Disassembly:

4E00H - SYS10 Entry / Dispatch Gate

SYS10 is entered via the RST 28H overlay dispatcher. On entry, Register A holds the dispatch byte supplied by the caller's inline SVC byte. The upper nibble of that byte encodes the operation class. SYS10 handles only class 10H (sector write-back). Any other class indicates a dispatcher mismatch and the routine returns immediately without action.

4E00
AND A,70H E6 70
Mask Register A against 70H (binary 01110000) to isolate bits 6-4, which encode the SVC operation class. The lower four bits and bit 7 are discarded. After this AND, Register A holds only the class field.
4E02
CP A,10H FE 10
Compare Register A (the masked class field) against 10H. SYS10 handles operation class 10H only. If the class field equals 10H, the Z FLAG is set and execution falls through. Any other class means this overlay is not the correct handler.
4E04
RET NZ C0
If the NZ FLAG (Not Zero) has been set - meaning the dispatch class is not 10H - return immediately to the overlay dispatcher. The dispatcher will handle the mismatch.

4E05H - Data Sector Write Path

Execution reaches here with the dispatch class confirmed as 10H. Register IX points to the FCB (File Control Block) for the file whose buffer must be flushed. The first task is to examine the FCB's info byte to determine whether this is a pure data sector write or a directory-ownership write-back. The FCB info byte is held in DE: Register DE points to the current directory entry, and the info byte at (DE) encodes ownership in its upper three bits (bits 7-5). Zero in those bits means this is a pure data sector.

4E05
LD A,(DE) 1A
Fetch the FCB directory-entry info byte from the address held in Register DE. This byte encodes ownership: bits 7-5 identify whether the FCB's current sector belongs to a directory track (non-zero) or is a pure data sector (zero).
4E06
AND A,0E0H E6 E0
Mask Register A against 0E0H (binary 11100000) to isolate bits 7-5, the directory-ownership field. If the FCB holds a pure data sector, all three bits are zero and this AND produces zero (Z FLAG set). If the FCB holds a directory-track sector, one or more bits are set (NZ FLAG set).
4E08
If the Z FLAG (Zero) has been set - meaning bits 7-5 of the info byte are all zero, indicating a pure data sector - JUMP to 4E6DH to take the FCB buffer clear path instead of the full data sector write. This is the fast exit for directory-sector write-back.
4E0A
CALL 49EBH CD EB 49
GOSUB to SYS0 routine at 49EBH to set up the overlay call context. This routine saves the current FCB pointer (DE) to 430AH and the return address (HL) to 430CH, preparing the DOS error context for any subsequent error reporting. On return, the CPU state is ready for the drive configuration lookup.
4E0D
LD A,(IX+01H) DD 7E 01
Fetch the FCB drive configuration table entry pointer byte from FCB offset +01H (Register IX holds the FCB base address). This byte identifies which drive configuration entry is associated with this file. The value is used to select the drive's parameter block.
4E10
AND A,07H E6 07
Mask Register A against 07H to isolate the drive-entry index field from the FCB pointer byte. The result (0-7) is the zero-based index into the drive configuration tables.
4E12
CP A,02H FE 02
Compare the drive index against 02H. VTOS supports a maximum of two active drive configuration entries in the primary table (indices 0 and 1). An index of 2 or higher falls outside the supported range for a direct data sector write.
4E14
If the CARRY FLAG has been set - meaning the drive index is less than 02H (index 0 or 1, within the supported range) - JUMP to 4E1AH to continue with the drive configuration lookup. Indices 2 and above fall through to the error return.
4E16
LD A,25H 3E 25
Load Register A with error code 25H. This is the VTOS "Sector address out of range" error, returned when the FCB's drive index field encodes an invalid or unsupported drive entry.
4E18
OR A,A B7
OR Register A with itself. This clears the CARRY FLAG and sets the NZ FLAG because A (25H) is non-zero. The NZ FLAG signals to the caller that an error has occurred.
4E19
RET C9
Return to the caller with NZ FLAG set and A = 25H (Sector Address Out of Range error).

4E1AH - Drive Configuration Lookup and Sector Address Resolution

The drive index is valid (0 or 1). Load the FCB's drive number and directory entry info byte into BC for use in the SYS0 sector validation and write routines, then call the chain of SYS0 subroutines that confirm the sector buffer, locate the sector on disk, and perform the write.

4E1A
LD C,(IX+06H) DD 4E 06
Load Register C with the drive number from FCB offset +06H (Register IX holds the FCB base address). The drive number (0-3) identifies which physical disk drive holds the sector to be written.
4E1D
LD B,(IX+07H) DD 46 07
Load Register B with the directory entry info byte from FCB offset +07H. This byte encodes the sector's ownership information used by the SYS0 validation routine at 4F21H.
4E20
CALL 4F21H CD 21 4F
GOSUB to SYS0 routine at 4F21H to validate the FCB sector buffer address and confirm the record position is within range. On return, Z FLAG set = valid; NZ FLAG set = error (A = 14H, Read Error). Register pair HL is set to point into the FCB sector buffer on success.
4E23
RET NZ C0
If the NZ FLAG (Not Zero) has been set - meaning the sector buffer validation failed - return immediately with the error code in A.
4E24
CALL 4B10H CD 10 4B
GOSUB to SYS0 routine at 4B10H for directory entry validation. This confirms that the directory entry pointed to by the FCB is still valid on disk and that the sector write target is a legal disk location. Z FLAG set = valid; NZ = error.
4E27
RET NZ C0
If the NZ FLAG (Not Zero) has been set - meaning directory entry validation failed - return with the error code in A.

4E28H - Sector Address Extraction Loop

The directory entry is valid. Now extract the 16-bit disk sector address (track and sector number) from the FCB's extent-chain table. The extent chain is an array of 2-byte entries embedded in the directory entry structure starting at offset +16H. Register HL currently points into the sector buffer; the code adjusts HL to point to the correct extent-chain entry for the current sector position, then reads the two-byte sector address from that entry. This loop advances through the chain until it reaches the entry whose embedded address is below the sentinel value 0FEFEH (which marks the end of the allocated chain).

4E28
LD A,16H 3E 16
Load Register A with 16H, the byte offset within the directory entry at which the extent chain data begins. This is the starting offset for the chain-walk calculation.
4E2A
ADD A,L 85
ADD the current value of Register L (the low byte of the HL pointer into the FCB sector buffer) to Register A (the chain-start offset 16H). This computes the absolute low-byte address of the first extent-chain entry within the directory entry buffer.
4E2B
LD L,A 6F
Load Register L with the computed offset sum, updating the low byte of HL to point to the first 2-byte entry in the extent chain.
4E2C
LD E,(HL) 5E
Loop Start
Fetch the low byte of the current 2-byte extent-chain entry from the address in HL into Register E. This is the first (low) byte of the sector address at the current chain position.
4E2D
INC L 2C
INCrement Register L by 1 to advance HL to the second (high) byte of the current 2-byte extent-chain entry.
4E2E
LD D,(HL) 56
Fetch the high byte of the current 2-byte extent-chain entry from the address in HL into Register D. Register pair DE now holds the complete 16-bit sector address (track in D, sector in E) for the current extent-chain entry.
4E2F
LD (4E56H),DE ED 53 56 4E
Self-Modifying Code
Store the 16-bit sector address (track/sector word in DE) to the two-byte runtime variable at 4E56H-4E57H. The initial cold-start value shown in the disassembly listing at 4E55H is 0000H; at runtime this cell holds the actual disk sector address extracted from the extent chain. It is used by the write subroutine called at 4E3EH to know which physical sector to write.
4E33
LD A,E 7B
Load Register A with Register E (the low byte of the sector address, i.e., the sector number). This is tested against the chain-end sentinel value.
4E34
CP A,0FEH FE FE
Compare Register A (sector number) against 0FEH. Values 0FEH and above are sentinel values that mark the end of the allocated extent chain. A sector number below 0FEH is a valid allocated sector.
4E36
If the NO CARRY FLAG has been set - meaning the sector number is 0FEH or above (a sentinel value indicating end of chain) - JUMP to 4E3EH to finalize the write using the last valid address stored at 4E56H. Otherwise the sector number is valid; continue advancing through the chain.
4E38
INC L 2C
INCrement Register L by 1 to advance HL past the high byte of the current entry to the low byte of the next 2-byte entry in the extent chain.
4E39
CALL 4EE4H CD E4 4E
GOSUB to the sector address extraction subroutine at 4EE4H (within this overlay) to process the current extent-chain entry: extract the track/sector bytes, rotate the track nibble to derive the WD1771 step rate, and encode the sector count into the self-modifying BIT instruction operand at 4F09H. On return, DE is updated and HL points to the next chain entry.
4E3C
JR 4E2CH 18 EE
LOOP BACK to 4E2CH (Loop Start) to process the next extent-chain entry. The loop continues until the sector number sentinel (0FEH or above) is detected at 4E34H-4E36H.

4E3EH - Sector Write Execution

The extent-chain walk is complete. Register HL points one byte past the last valid entry (L now holds a page-aligned offset into the extent chain area). The sector address for this write has been saved to the self-modifying variable at 4E56H. The following code clears the dirty flag bit in the FCB, writes the sector to disk, and then clears the FCB's internal buffer.

4E3E
LD A,L 7D
Load Register A with Register L (the current low byte of HL, pointing into the extent chain area). This value is used to compute the aligned base address for the dirty-flag byte in the FCB buffer map.
4E3F
AND A,0E0H E6 E0
Mask Register A against 0E0H (binary 11100000) to clear the lower 5 bits and align the offset to a 32-byte page boundary. This yields the base address (within the current page) of the FCB's buffer-map byte that holds the dirty flag for the current sector.
4E41
LD L,A 6F
Load Register L with the aligned offset, updating HL to point to the dirty-flag byte for this sector in the FCB buffer map.
4E42
RES 4,(HL) CB A6
Clear bit 4 of the byte at the address in HL (the FCB buffer-map dirty-flag byte for the current sector). Bit 4 is the "sector dirty" flag; clearing it marks the sector as clean, indicating that it has been (or is about to be) successfully written to disk.
4E44
CALL 4B1FH CD 1F 4B
GOSUB to SYS0 routine at 4B1FH to write and update the directory entry for this file. This commits any directory-level changes (such as updated sector counts or extent pointers) to disk before the data sector write. Z FLAG set = success; NZ = error.
4E47
RET NZ C0
If the NZ FLAG (Not Zero) has been set - meaning the directory entry write failed - return with the error code in A.
4E48
CALL 4F4DH CD 4D 4F
GOSUB to SYS0 routine at 4F4DH to read the target sector into the FCB buffer before writing. This pre-read-for-merge step ensures that any bytes outside the FCB's dirty range retain their original disk content. Z FLAG set = success; NZ = error.
4E4B
RET NZ C0
If the NZ FLAG (Not Zero) has been set - meaning the pre-write read failed - return with the error code in A.
4E4C
LD H,42H 26 42
Load Register H with 42H. This sets the high byte of HL to 42H, pointing HL into the directory entry buffer at 4200H-42FFH (page 42H). The low byte in L (from the prior aligned computation) selects the specific entry within the buffer.
4E4E
LD L,B 68
Load Register L with Register B (the directory entry info byte saved from FCB offset +07H at 4E1DH). This sets HL to the address of the directory entry within the 4200H buffer page that corresponds to the file being written, ready for the FCB buffer clear loop.
4E4F
LD (HL),00H 36 00
Store 00H to the directory entry byte pointed to by HL (within the 4200H directory buffer). This zeroes the first byte of the FCB's internal buffer area, beginning the buffer clear operation.
4E51
CALL 4F5FH CD 5F 4F
GOSUB to SYS0 routine at 4F5FH to write the FCB's sector buffer to disk and perform the read-after-write verification. This is the actual physical disk write. Z FLAG set = success; NZ = error with A = 15H (Write Error) or 14H (Verify mismatch).
4E54
RET NZ C0
If the NZ FLAG (Not Zero) has been set - meaning the sector write or verify failed - return with the error code in A.
4E55
LD DE,0000H 11 00 00
Self-Modifying Code
This instruction's two operand bytes at 4E56H-4E57H are the runtime sector-address variable described above. The disassembly shows the cold-start value (0000H). At runtime, the LD (4E56H),DE instruction at 4E2FH overwrites these two bytes with the actual track/sector address extracted from the FCB extent chain. The resulting LD DE,nnnnH instruction thus re-loads DE with that saved sector address, making it available for any post-write processing by the caller.
4E58
LD B,D 42
Load Register B with Register D (the high byte of the sector address now in DE). BC is being prepared as a loop counter and sector index for the FCB buffer clear loop that follows.
4E59
LD A,E 7B
Load Register A with Register E (the low byte of the sector address in DE). This is compared against the sentinel value to determine whether to begin another write cycle or proceed to the buffer clear loop.
4E5A
CP A,0FEH FE FE
Compare Register A against 0FEH. If the sector low byte equals 0FEH, the sector address is the chain-end sentinel, signalling that the full extent has been written and the FCB buffer clear should proceed. If below 0FEH, a valid sector address remains in DE and another write cycle is needed.
4E5C
If the Z FLAG (Zero) has been set - meaning the sector address is the end-of-chain sentinel (0FEH) - JUMP back to 4E24H to call 4B10H and begin another directory-validation and write cycle for any remaining extents.
4E5E
CALL 4F34H CD 34 4F
GOSUB to SYS0 routine at 4F34H to set up the FDC track and sector registers for the next data sector write. This routine loads the WD1771 Track Register and Sector Register from the sector address in DE and prepares the drive for the write command. Z FLAG set = success; NZ = error.
4E61
RET NZ C0
If the NZ FLAG (Not Zero) has been set - meaning the FDC setup failed - return with the error code in A.
4E62
PUSH IX DD E5
Push Register IX (the FCB base address) onto the stack to preserve it across the FCB buffer clear loop, which uses HL as its primary pointer and will step through the FCB internal buffer bytes.
4E64
POP HL E1
Pop the FCB base address (previously in IX, just pushed at 4E62H) from the stack into Register HL. HL now points to the start of the FCB (offset +00H) and will be used as the destination pointer for the buffer clear loop.
4E65
LD B,20H 06 20
Load Register B with 20H (decimal 32). This is the byte count for the FCB buffer clear loop: 32 bytes of the FCB's internal sector buffer will be zeroed, from (HL) to (HL+1FH).
4E67
XOR A,A AF
Exclusive-OR Register A with itself, setting A to 00H. A is the zero value that will be written to each byte of the FCB buffer in the clear loop.
4E68
LD (HL),A 77
Loop Start
Store 00H (in Register A) to the FCB byte at the address in HL. This zeros one byte of the FCB's internal buffer.
4E69
INC HL 23
INCrement Register pair HL by 1 to advance the pointer to the next FCB buffer byte.
4E6A
DECrement Register B and, if B is Not Zero, LOOP BACK to 4E68H. Repeats the store-and-advance until all 32H (32) bytes of the FCB internal buffer have been zeroed. When B reaches zero, the loop exits.
4E6C
RET C9
Return to the caller with Z FLAG set (the last DJNZ set NZ then fell through, but the final LD (HL),A / INC HL / DJNZ=zero path exits here) and A = 00H, indicating a successful sector write and FCB buffer clear. The calling file I/O engine may now load the next sector.

4E6DH - Directory Sector Write-Back / FCB Buffer Clear

This path is reached when the FCB info byte's upper three bits (bits 7-5) are non-zero, indicating that the FCB's current buffer holds a directory-track sector rather than a pure data sector. In this case, no data write to the data area of the disk is required here - SYS0 handles directory writes through its own path. SYS10's job is instead to locate the drive configuration entry for this FCB and clear the FCB's 8-byte internal buffer, releasing the sector slot.

The drive configuration tables at 4015H-402CH (table #1) and 43C0H-43D7H (table #2) are walked. The match criterion is the two-byte drive-type ID stored at entry offsets +06H and +07H, compared against the FCB's drive number (IX+06H) and directory info byte (IX+07H).

4E6D
CALL 49E9H CD E9 49
GOSUB to SYS0 routine at 49E9H to set up the alternate overlay call context (without saving the FCB pointer). This prepares the DOS error context for any subsequent error reporting from this path.
4E70
LD E,(IX+06H) DD 5E 06
Load Register E with the drive number from FCB offset +06H (Register IX points to the FCB base). This is the first byte of the two-byte drive-type ID that will be matched against entries in the drive configuration tables.
4E73
LD D,(IX+07H) DD 56 07
Load Register D with the directory entry info byte from FCB offset +07H. Together with E, Register pair DE holds the complete two-byte drive-type ID to match against the configuration table entries at offsets +06H/+07H.
4E76
LD HL,4015H 21 15 40
Point Register pair HL to 4015H, the start of drive configuration table #1. The scan begins here. Each entry is 8 bytes wide; the match ID bytes are at offsets +06H and +07H within each entry.
4E79
PUSH HL E5
Loop Start
Push the current HL (pointer to the start of the current drive configuration table entry) onto the stack. This saves the entry base address for use after the match comparison.
4E7A
LD A,L 7D
Load Register A with Register L (the low byte of HL, which is the low byte of the current entry's base address). This is used to compute the address of the drive-type ID bytes at offsets +06H and +07H within the current entry.
4E7B
ADD A,06H C6 06
ADD 06H to Register A to compute the low byte of the address of the first drive-type ID byte (at offset +06H within the current 8-byte entry).
4E7D
LD L,A 6F
Load Register L with the computed offset sum, updating HL to point to offset +06H within the current drive configuration entry (the first byte of the two-byte drive-type ID).
4E7E
LD A,(HL) 7E
Fetch the first drive-type ID byte from offset +06H of the current configuration entry into Register A. This will be compared against the FCB's drive number (in Register E).
4E7F
INC L 2C
INCrement Register L by 1 to advance HL to offset +07H (the second drive-type ID byte) within the current configuration entry.
4E80
CP A,E BB
Compare Register A (first ID byte from the table entry at +06H) against Register E (the FCB's drive number). If they match, the Z FLAG is set and the comparison continues for the second byte. If they do not match, the NZ FLAG is set and this entry is skipped.
4E81
If the NZ FLAG (Not Zero) has been set - meaning the first ID byte did not match - JUMP to 4E8AH to advance HL to the next entry and continue the table scan.
4E83
LD A,(HL) 7E
The first ID byte matched. Fetch the second drive-type ID byte from offset +07H of the current configuration entry (HL now points to +07H after the INC L at 4E7FH) into Register A.
4E84
CP A,D BA
Compare Register A (second ID byte from the table entry at +07H) against Register D (the FCB's directory entry info byte). If they match, the Z FLAG is set and a full two-byte match is confirmed. If not, the NZ FLAG is set and this entry is skipped.
4E85
If the NZ FLAG (Not Zero) has been set - meaning the second ID byte did not match - JUMP to 4E8AH to advance HL to the next entry and continue scanning.
4E87
POP HL E1
A full two-byte match was found. Pop the saved entry base address (pushed at 4E79H) from the stack back into HL. HL now points to the start of the matching drive configuration entry.
4E88
JR 4E9CH 18 12
JUMP to 4E9CH to validate the matched entry and proceed to the FCB buffer clear loop.

4E8AH - Table Scan Advance (No Match at Current Entry)

The current entry did not match. Discard the saved entry base, advance HL to the next entry, and check whether the table scan has been exhausted or needs to switch to table #2.

4E8A
POP AF F1
Pop the saved entry base address (pushed at 4E79H) from the stack into AF (discarding the value - we only needed to clean the stack). The flags are also overwritten here but are not relevant at this point.
4E8B
INC L 2C
INCrement Register L by 1. After comparing the two ID bytes, HL was pointing to offset +07H of the current entry. INC L advances it to +08H, which is the start of the next 8-byte entry.
4E8C
If the NZ FLAG (Not Zero) has been set after the INC L - meaning Register L did not wrap to zero (00H) - JUMP to 4E92H to check whether L has reached the end-of-table sentinel for this page. If L wrapped to zero, the scan has walked off the end of the current 256-byte page and falls through to the error path.
4E8E
LD A,08H 3E 08
Load Register A with error code 08H. This is returned when no matching drive configuration entry was found in either table and the scan wrapped past the page boundary (an unexpected overflow condition).
4E90
OR A,A B7
OR Register A with itself to set the NZ FLAG (A = 08H is non-zero) and clear the CARRY FLAG, preparing the error return convention.
4E91
RET C9
Return to the caller with NZ FLAG set and A = 08H (internal table overflow error).
4E92
LD A,L 7D
Load Register A with Register L (the current low byte of HL, which now points to the start of the next candidate entry after the INC L at 4E8BH). This value is tested against the end-of-table and table-switch sentinel values.
4E93
CP A,2DH FE 2D
Compare Register A (the current entry offset) against 2DH. The value 2DH is the end-of-table sentinel for the drive configuration tables at 4015H and 43C0H: an offset of 2DH means HL has advanced past all three 8-byte entries in the current table (3 x 8 = 18H bytes from the table base, but the sentinel is offset-based and reflects the page-relative address). If A = 2DH, the current table is exhausted and the scan must switch to the alternate table.
4E95
If the NZ FLAG (Not Zero) has been set - meaning L does not equal 2DH (the table is not yet exhausted) - LOOP BACK to 4E79H to push HL and compare the next entry in the current table.
4E97
LD HL,43C0H 21 C0 43
Point Register pair HL to 43C0H, the start of drive configuration table #2. The first table (starting at 4015H) is exhausted; the scan continues in the overflow table.
4E9A
JR 4E79H 18 DD
JUMP to 4E79H to begin scanning table #2. The loop structure is identical; the only difference is the starting address (43C0H instead of 4015H).

4E9CH - Matched Entry Validation and FCB Buffer Clear

A matching drive configuration entry has been found (HL points to its base). Before zeroing the FCB buffer, validate that the matched entry is an active terminal entry (bit 3 of the entry type byte must be set). Then clear 8 bytes of the FCB's internal buffer by writing 00H to each byte starting at (IX+00H).

4E9C
LD A,L 7D
Load Register A with Register L (the low byte of HL, which points to the base of the matched drive configuration entry). This is used to check whether the match address is within the valid range for an active terminal entry.
4E9D
CP A,0D8H FE D8
Compare Register A (the entry base offset L) against 0D8H. This tests whether the matched entry's base address is at or above offset 0D8H within the current page. Entries at or above 0D8H fall outside the valid configuration table range.
4E9F
If the NO CARRY FLAG has been set - meaning the entry offset is 0D8H or above (out of valid range) - JUMP to 4EA5H to test the entry's terminal bit before proceeding.
4EA1
LD A,28H 3E 28
Load Register A with error code 28H. This is the VTOS "Protected System Device" error, returned when no valid active drive configuration entry was found for the FCB's drive-type ID.
4EA3
OR A,A B7
OR Register A with itself to set the NZ FLAG (A = 28H is non-zero) and clear the CARRY FLAG, signaling an error return to the caller.
4EA4
RET C9
Return to the caller with NZ FLAG set and A = 28H (Protected System Device error).
4EA5
BIT 3,(HL) CB 5E
Test bit 3 of the drive configuration entry type byte at the address in HL (the base of the matched entry). In the VTOS drive configuration table format, bit 3 of the type byte (offset +00H) must be set to identify the entry as an active terminal entry (type byte 08H). A forward-link entry (type 10H) has bit 3 clear and is not a valid write target.
4EA7
If the NZ FLAG (Not Zero) has been set - meaning bit 3 is set (the entry is an active terminal entry) - JUMP to 4EADH to proceed with the FCB buffer clear. If bit 3 is clear, the entry is a forward-link and an error must be returned.
4EA9
LD A,27H 3E 27
Load Register A with error code 27H. This is the VTOS "Device In Use" error, returned when the matched configuration entry is a forward-link type rather than an active terminal entry, meaning the drive is not available for direct write access at this time.
4EAB
OR A,A B7
OR Register A with itself to set the NZ FLAG (A = 27H is non-zero) and clear the CARRY FLAG, signaling an error return.
4EAC
RET C9
Return to the caller with NZ FLAG set and A = 27H (Device In Use error).

4EADH - FCB Internal Buffer Clear (8 Bytes)

The matched drive configuration entry is confirmed as an active terminal entry. Clear the 8-byte internal buffer in the FCB by writing 00H to each of (IX+00H) through (IX+07H). After clearing, check the drive configuration tables one more time to handle any secondary cleanup, then return.

4EAD
LD B,08H 06 08
Load Register B with 08H (decimal 8), the byte count for the FCB internal buffer clear loop. Eight bytes (IX+00H through IX+07H) will be zeroed.
4EAF
XOR A,A AF
Exclusive-OR Register A with itself, setting A to 00H. This is the value that will be written to each FCB internal buffer byte.
4EB0
LD (HL),A 77
Loop Start
Store 00H (in Register A) to the FCB byte at the address in HL. On the first iteration, HL points to IX+00H (the FCB status byte, which is the first byte of the 8-byte internal buffer).
4EB1
INC L 2C
INCrement Register L by 1 to advance HL to the next FCB buffer byte. Because IX is treated as an HL base here (transferred via PUSH IX / POP HL), INC L steps through the FCB's byte offsets +00H through +07H in sequence.
4EB2
DECrement Register B and, if B is Not Zero, LOOP BACK to 4EB0H. The loop zeroes all 8 FCB internal buffer bytes (IX+00H through IX+07H). When B reaches zero, the loop exits and the FCB's internal buffer is fully cleared.

4EB4H - Post-Clear Drive Configuration Check

The FCB internal buffer has been cleared. The code now re-examines the drive configuration table at 4015H to check bit 4 of the system flags byte there. If set, a secondary buffer address is fetched from the table and used to conditionally zero one additional byte at the secondary buffer's first entry. This handles a linked secondary configuration cleanup. The loop then advances through the 4015H table in the same way as the scan above, and finally returns.

4EB4
LD HL,4015H 21 15 40
Point Register pair HL to 4015H, the start of drive configuration table #1 in the system work area. This begins the post-clear secondary configuration check.
4EB7
BIT 4,(HL) CB 66
Loop Start
Test bit 4 of the byte at the address in HL (the current drive configuration entry's system flags byte at 4015H or a subsequent entry). Bit 4 is the "SYS6 library overlay cached" flag. If set, a secondary buffer address is embedded at offsets +01H/+02H of this entry and must be processed.
4EB9
If the Z FLAG (Zero) has been set - meaning bit 4 is clear (no secondary buffer for this entry) - JUMP to 4ECAH to advance HL to the next entry without performing the secondary clear.
4EBB
PUSH HL E5
Push the current HL (base of the entry with bit 4 set) onto the stack to preserve it while the secondary buffer address is read from offsets +01H and +02H.
4EBC
INC L 2C
INCrement Register L by 1 to advance HL to offset +01H (the low byte of the secondary buffer address stored in this entry).
4EBD
LD A,(HL) 7E
Fetch the low byte of the secondary buffer address from offset +01H of the current entry into Register A.
4EBE
INC L 2C
INCrement Register L by 1 to advance HL to offset +02H (the high byte of the secondary buffer address).
4EBF
LD H,(HL) 66
Fetch the high byte of the secondary buffer address from offset +02H into Register H. Register pair HL is now being assembled: H = high byte of the secondary buffer address, A = low byte.
4EC0
LD L,A 6F
Load Register L with Register A (the low byte of the secondary buffer address fetched at 4EBDH). Register pair HL now holds the complete 16-bit address of the secondary buffer's first byte.
4EC1
LD A,(HL) 7E
Fetch the first byte of the secondary buffer (at the address just computed in HL) into Register A. This byte is tested to determine whether the secondary buffer slot is occupied (non-zero) or empty (zero).
4EC2
POP HL E1
Pop the saved entry base address (pushed at 4EBBH) from the stack back into HL, restoring the pointer to the start of the current drive configuration entry.
4EC3
OR A,A B7
OR Register A with itself to set/clear the Z FLAG based on the secondary buffer's first byte (fetched at 4EC1H). If the byte is 00H (buffer empty), Z FLAG is set. If non-zero (buffer occupied), NZ FLAG is set.
4EC4
If the NZ FLAG (Not Zero) has been set - meaning the secondary buffer's first byte is non-zero (the slot is in use) - JUMP to 4ECAH to skip the secondary zero-write and advance to the next entry. The slot is occupied and must not be cleared.
4EC6
LD (HL),08H 36 08
Store 08H to the byte at HL (the base byte of the current drive configuration entry). This writes the value 08H (the active terminal entry type code) back to the entry's type byte, re-marking this entry as a terminal entry after the secondary buffer was found empty. This is the secondary cleanup: restoring the terminal marker when no secondary session is active.
4EC8
JR 4EB4H 18 EA
LOOP BACK to 4EB4H to reload HL = 4015H and repeat the post-clear check from the beginning. This restart ensures that all entries in table #1 are re-examined after modifying the terminal type byte.

4ECAH - Post-Clear Table Advance and Return

Advance HL through the current drive configuration table, checking for the end-of-table sentinel and switching to table #2 at 43C0H if necessary. When both tables are exhausted, return successfully.

4ECA
LD A,L 7D
Load Register A with Register L (the low byte of HL, the current entry offset within the drive configuration table page).
4ECB
ADD A,08H C6 08
ADD 08H to Register A to advance the offset by one full 8-byte entry, stepping HL to the next drive configuration table entry.
4ECD
LD L,A 6F
Load Register L with the new offset, updating the low byte of HL to point to the next entry in the current table page.
4ECE
If the Z FLAG (Zero) has been set after the ADD A,08H - meaning Register L wrapped to 00H (the offset overflowed past the end of the 256-byte page) - JUMP to 4ED9H to perform the final FCB IX buffer clear and return. This handles the table-exhaustion case when advancing through 43C0H wraps L.
4ED0
CP A,2DH FE 2D
Compare Register A (the new entry offset after the ADD at 4ECBH) against 2DH, the end-of-table sentinel for the 4015H table. An offset of 2DH means all three entries (starting at offsets 15H, 1DH, and 25H) have been processed and the table is exhausted.
4ED2
If the NZ FLAG (Not Zero) has been set - meaning the offset is not yet 2DH (more entries remain in the current table) - LOOP BACK to 4EB7H to test bit 4 of the next entry.
4ED4
LD HL,43C0H 21 C0 43
Point Register pair HL to 43C0H, the start of drive configuration table #2. Table #1 (at 4015H) is exhausted; the post-clear check continues in the overflow table.
4ED7
JR 4EB7H 18 DE
JUMP to 4EB7H to begin the post-clear check for table #2 (at 43C0H). The loop structure is the same as for table #1.

4ED9H - Final FCB IX Buffer Clear and Return

Both drive configuration tables have been fully processed. Zero the 8-byte FCB internal buffer via the IX register directly, then return successfully.

4ED9
PUSH IX DD E5
Push Register IX (the FCB base address) onto the stack so it can be transferred into HL for use as the loop pointer in the FCB clear loop that follows.
4EDB
POP HL E1
Pop the FCB base address from the stack into Register HL. HL now points to FCB offset +00H (the FCB status byte), ready for the clear loop.
4EDC
LD B,08H 06 08
Load Register B with 08H (decimal 8), the byte count for this final FCB buffer clear loop. The same 8 FCB internal buffer bytes (IX+00H through IX+07H) are zeroed via HL here.
4EDE
XOR A,A AF
Exclusive-OR Register A with itself, setting A to 00H for use as the zero value in the store loop.
4EDF
LD (HL),A 77
Loop Start
Store 00H (in Register A) to the FCB byte at the address in HL (starting at FCB offset +00H).
4EE0
INC HL 23
INCrement Register pair HL by 1 to advance to the next FCB buffer byte (offset +01H, then +02H, etc.).
4EE1
DECrement Register B and, if B is Not Zero, LOOP BACK to 4EDFH. Repeats until all 8 FCB internal buffer bytes have been zeroed. Loop End
4EE3
RET C9
Return to the caller with Z FLAG set (DJNZ set Z on B reaching zero) and A = 00H, indicating a successful directory sector write-back and FCB buffer clear.

4EE4H - Sector Address Extraction Subroutine

This subroutine is called from the extent-chain walk loop at 4E39H. On entry, DE points into the extent chain table (at the current entry), and the loop has already read a 2-byte sector address from the table into DE (track in D, sector in E). This routine reads the track byte via an FDC call to determine the WD1771 step rate and sector count, encodes the result into the self-modifying BIT instruction operand at 4F09H, then returns with HL and DE updated for the next loop iteration.

The FDC step rate is derived from the track number by rotating the upper bits of the track byte into the lower 3 bits. The sector count (number of sectors in this extent chunk) is incremented and stored to 4F09H as the operand of a CB-prefix BIT instruction, which will be executed later to test that sector bit in the WD1771 sector register.

4EE4
PUSH HL E5
Push Register pair HL (the current position pointer within the extent chain table) onto the stack to preserve it across the subroutine's internal register manipulations.
4EE5
PUSH BC C5
Push Register pair BC (which holds the drive number in C and the directory info byte in B from the calling context at 4E1AH) onto the stack to preserve it.
4EE6
LD A,08H 3E 08
Load Register A with 08H. This value is passed to the SYS0 routine at 4797H as the sector type / directory-info byte parameter, requesting that routine to read the directory sector associated with this extent entry.
4EE8
CALL 4797H CD 97 47
GOSUB to SYS0 routine at 4797H to read the directory sector identified by the info byte in A. On return, Register A contains the track byte read from the FDC Track Register, which is used to derive the WD1771 stepping rate for this sector.
4EEB
RLCA 07
Rotate Register A Left Circular by 1 bit. Bit 7 of the track byte moves to bit 0, and all other bits shift left by one. This is the first of three left rotations used to extract the upper 3 bits of the track byte (which encode the WD1771 stepping rate) into the lower 3 bit positions of A.
4EEC
RLCA 07
Rotate Register A Left Circular by 1 bit again (second rotation). Bits 7-6 of the original track byte now occupy bits 1-0 of A.
4EED
RLCA 07
Rotate Register A Left Circular by 1 bit (third rotation). Bits 7-5 of the original track byte now occupy bits 2-0 of A. The upper 3 bits of the track byte, which encode the sector/stepping-rate field, are now in bits 2-0 of A.
4EEE
AND A,07H E6 07
Mask Register A against 07H (binary 00000111) to isolate bits 2-0 (the sector/step-rate field extracted from the track byte). All other bits are cleared.
4EF0
INC A 3C
INCrement Register A by 1. This converts the 0-based sector field into a 1-based sector count, representing the number of sectors in this extent entry (sectors are numbered 1 through 8 in VTOS extent chain encoding).
4EF1
LD (4F09H),A 32 09 4F
Self-Modifying Code
Store Register A (the 1-based sector count, 1-8) to address 4F09H. The byte at 4F09H is the second byte of a CB-prefix BIT instruction that will be executed later in the write path. By writing the sector count here, the code dynamically sets which bit the BIT instruction will test in the WD1771 Sector Register, selecting the correct sector for the write operation.
4EF4
LD L,E 6B
Load Register L with Register E (the low byte of DE, which holds the sector number from the current extent-chain entry). This begins constructing a new HL pointer for the next iteration of the extent-chain walk loop.
4EF5
LD H,51H 26 51
Load Register H with 51H. Combined with L (= the sector number byte in E), HL now points into page 51H (5100H-51FFH), the GAT sector buffer page. This positions HL to reference the sector's GAT entry for the subsequent drive-sector operations.
4EF7
LD A,D 7A
Load Register A with Register D (the high byte of DE, which holds the track number from the current extent-chain entry). The track number is processed to extract the actual WD1771 Sector Register value.
4EF8
AND A,1FH E6 1F
Mask Register A against 1FH (binary 00011111) to isolate bits 4-0 of the track byte. In VTOS extent chain encoding, bits 4-0 of the track byte hold the sector number within the track (1-based, 1-18 for typical single-density disks). Bits 7-5 hold the stepping-rate field already extracted above.
4EFA
LD C,A 4F
Load Register C with the masked sector-number field from D. This stores the sector number within the track for use by the subsequent FDC command that positions to and writes the sector.
4EFB
INC C 0C
INCrement Register C by 1 to convert the 0-based sector index to a 1-based WD1771 Sector Register value. WD1771 sector numbers begin at 1.
4EFC
XOR A,D AA
Exclusive-OR Register A with Register D (the original track byte). Since A currently holds (D AND 1FH), XOR with D yields the upper 3 bits of D (bits 7-5, the stepping-rate field) isolated in bits 7-5 of A, with bits 4-0 cleared. This extracts the step-rate field for the next three rotation steps.
4EFD
RLCA 07
Rotate Register A Left Circular by 1 bit (first of three). Bit 7 of the step-rate field rotates toward bit 0.
4EFE
RLCA 07
Rotate Register A Left Circular by 1 bit (second rotation). Bits 7-6 of the step-rate field now occupy bits 1-0.
4EFF
RLCA 07
Rotate Register A Left Circular by 1 bit (third rotation). Bits 7-5 of the original D byte (the step-rate field) now occupy bits 2-0 of A. This value will be used as the WD1771 step-rate operand.
4F00
PUSH AF F5
Push Register pair AF (A = step-rate field in bits 2-0) onto the stack to preserve it across the sector-encoding loop that follows.
4F01
LD B,(HL) 46
Load Register B with the byte at the address in HL (pointing into page 51H at the current sector's GAT entry). This byte is the existing GAT bitmap byte for this sector, which will be modified by the CB-prefix BIT/RES instruction at 4F08H.
4F02
CALL 4F14H CD 14 4F
GOSUB to the sector-encoding helper at 4F14H (within this overlay) to encode the step-rate field and sector count into the self-modifying RES/BIT opcode byte. The helper writes the modified opcode to the CB-prefix instruction at 4F1EH, which clears the appropriate bit in Register B (the GAT byte).
4F05
LD (HL),B 70
Store the modified GAT bitmap byte (in Register B, updated by the CALL 4F14H helper) back to the address in HL (the current sector's GAT entry in page 51H). This writes the new allocation state for this sector back to the GAT buffer.
4F06
POP AF F1
Pop the preserved step-rate value (pushed at 4F00H) from the stack back into Register A.
4F07
INC A 3C
INCrement Register A (the step-rate accumulator) by 1. This advances the step-rate counter to the next sector position within the current extent entry.
4F08
CP A,00H FE 00
Self-Modifying Code
Compare Register A against 00H. The operand byte at 4F09H is the self-modifying sector count set by LD (4F09H),A at 4EF1H. At runtime, this instruction reads as "CP A,nn" where nn is the 1-based sector count for this extent entry. When A increments to match nn, all sectors in the current extent entry have been processed.
4F0A
If the NZ FLAG (Not Zero) has been set - meaning A has not yet reached the sector count (more sectors remain in this extent entry) - JUMP to 4F0EH to advance L and process the next sector.
4F0C
XOR A,A AF
Exclusive-OR Register A with itself, setting A to 00H. This resets the step-rate accumulator when all sectors in the current extent entry have been processed, preparing for the next extent entry in the chain-walk loop.
4F0D
INC L 2C
INCrement Register L by 1 to advance the GAT buffer pointer (HL in page 51H) to the next sector's GAT entry, ready for the next extent entry.
4F0E
DEC C 0D
DECrement Register C (the 1-based WD1771 sector number within the current track) by 1. This steps backward through the sector numbering within the track as each sector in the extent entry is processed.
4F0F
If the NZ FLAG (Not Zero) has been set - meaning C has not yet reached zero (more sector positions remain) - LOOP BACK to 4F00H to push AF and process the next sector position within the current track.
4F11
POP BC C1
Pop the preserved drive number and directory info byte (pushed at 4EE5H) from the stack back into Register pair BC, restoring the calling context values.
4F12
POP HL E1
Pop the preserved extent chain table pointer (pushed at 4EE4H) from the stack back into Register pair HL, restoring it so the main chain-walk loop at 4E2CH can continue to the next entry.
4F13
RET C9
Return to the caller (the extent chain walk loop at 4E39H) with HL restored to the chain table pointer and DE containing the sector address of the last processed entry.

4F14H - Sector Encoding Helper (Self-Modifying Opcode Writer)

This helper subroutine is called from CALL 4F14H at 4F02H. On entry, Register A holds the step-rate field (in bits 2-0) and Register B holds the GAT bitmap byte. The helper encodes the step-rate field into a CB-prefix opcode byte, writes it to 4F1FH (the second byte of the RES instruction at 4F1EH), and executes the RES instruction to clear the appropriate bit in Register B (marking the sector as write-in-progress in the GAT). This is a standard VTOS self-modifying BIT/RES pattern, identical in structure to the GAT bit-clear encoder in SYS8 at 5034H.

4F14
AND A,07H E6 07
Mask Register A against 07H to isolate bits 2-0 (the step-rate / sector-bit-position field). Bits 3-7 are cleared.
4F16
RLCA 07
Rotate Register A Left Circular by 1 bit (first of three). The 3-bit field moves from bits 2-0 toward the bit positions required for a CB-prefix opcode encoding.
4F17
RLCA 07
Rotate Register A Left Circular by 1 bit (second rotation).
4F18
RLCA 07
Rotate Register A Left Circular by 1 bit (third rotation). After three left rotations, the 3-bit field originally in bits 2-0 now occupies bits 5-3 of A, which is exactly the bit-position field within a Z80 CB-prefix opcode (bits 5-3 select which bit 0-7 to operate on).
4F19
OR A,80H F6 80
OR Register A with 80H (binary 10000000) to set bit 7. Combined with the bit-position field in bits 5-3, this produces a CB-prefix RES opcode of the form 1bb_bbb00 where bbb is the bit number. For bit 0: A = 80H. For bit 7: A = F8H. This is the standard CB-opcode encoding for "RES n,B" (where B is register 000).
4F1B
LD (4F1FH),A 32 1F 4F
Self-Modifying Code
Store Register A (the encoded RES opcode byte) to address 4F1FH. The byte at 4F1FH is the second byte of the two-byte CB-prefix instruction at 4F1EH-4F1FH. By writing the RES opcode here, the code dynamically configures which bit of Register B the following CB instruction will clear. The initial disassembly value at 4F1EH (CB 80H) shows RES 0,B; at runtime, the bit position is determined by the sector-encoding field.
4F1E
RES 0,B CB 80
Self-Modifying Code
Clear bit 0 of Register B. This is the instruction whose second opcode byte (at 4F1FH) is overwritten by the LD (4F1FH),A at 4F1BH. At cold start the opcode is CB 80H (RES 0,B); at runtime the bit position (encoded in the second byte) reflects the sector number within the extent entry. Clearing that bit in B marks the corresponding granule/sector as allocated in the GAT byte.
4F20
RET C9
Return to the caller at 4F02H with Register B holding the modified GAT bitmap byte (one bit cleared to mark the current sector as allocated).

4F21H - Sector Buffer Validation (CALL 4F21H)

This subroutine (called at 4E20H) validates that the FCB sector buffer address and record position are legal for a write operation. It calls SYS0's directory I/O setup at 4B65H, then calls 4B45H to read the sector, returning Z on success and A=14H on failure. The data verified here includes the FCB buffer base address and the E parameter (sector type 00H for data sectors).

4F21
PUSH DE D5
Push Register pair DE (FCB directory entry pointer) onto the stack to preserve it across the subroutine's internal register use.
4F22
PUSH HL E5
Push Register pair HL onto the stack to preserve it.
4F23
CALL 4B65H CD 65 4B
GOSUB to SYS0 routine at 4B65H to set up the track and sector registers for directory I/O. This configures the WD1771 for access to the sector identified by the FCB's current position. On return, the FDC is positioned at the correct track and sector.
4F26
LD E,00H 1E 00
Load Register E with 00H, the sector type code for a data sector (as opposed to 01H for a directory sector). This selects the data sector buffer at 5100H for the subsequent sector read at 4B45H.
4F28
LD HL,5100H 21 00 51
Point Register pair HL to 5100H, the data sector buffer. This is the buffer into which the sector will be read by the CALL 4B45H at 4F2BH, and from which the data will be written to disk by the subsequent write calls.
4F2B
CALL 4B45H CD 45 4B
GOSUB to SYS0 routine at 4B45H to read the sector from disk into the buffer at 5100H. E = 00H selects the data sector path. Z FLAG set = sector read successfully; NZ = read error (A = error code).
4F2E
POP HL E1
Pop the preserved HL value from the stack, restoring it.
4F2F
POP DE D1
Pop the preserved DE value (FCB directory entry pointer) from the stack, restoring it.
4F30
RET Z C8
If the Z FLAG (Zero) has been set - meaning the sector read succeeded - return with Z FLAG set and A = 00H (success).
4F31
LD A,14H 3E 14
Load Register A with error code 14H (Read Error). The sector read at 4B45H failed; this error code is returned to indicate that the FCB's sector buffer could not be validated because the underlying sector could not be read.
4F33
RET C9
Return to the caller with NZ FLAG set and A = 14H (Read Error).

4F34H - FDC Track/Sector Setup for Data Write (CALL 4F34H)

This subroutine (called at 4E5EH) sets up the WD1771 FDC track and sector registers for the data sector write, then calls 4768H (Write Sector) and 4772H (Write-Verify). It uses the same directory I/O setup as 4F21H but with E = 00H and calls 4768H/4772H instead of 4B45H. Returns Z on success, NZ with A = 15H on write failure.

4F34
PUSH DE D5
Push Register pair DE onto the stack to preserve it.
4F35
PUSH HL E5
Push Register pair HL onto the stack to preserve it.
4F36
CALL 4B65H CD 65 4B
GOSUB to SYS0 routine at 4B65H to set up the track and sector registers for directory I/O. Positions the WD1771 at the correct track and sector for the data write.
4F39
LD E,00H 1E 00
Load Register E with 00H (data sector type code), selecting the data sector buffer path.
4F3B
LD HL,5100H 21 00 51
Point Register pair HL to 5100H, the data sector buffer whose contents will be written to disk.
4F3E
CALL 4768H CD 68 47
GOSUB to SYS0 routine at 4768H to issue the WD1771 Write Sector command and write the 256 bytes at 5100H to the disk sector identified by the FDC registers. Z FLAG set = write completed; NZ = write error.
4F41
If the NZ FLAG (Not Zero) has been set - meaning the write failed - JUMP to 4F48H to restore DE and HL and return the error.
4F43
CALL 4772H CD 72 47
GOSUB to SYS0 routine at 4772H to perform the read-after-write verification: re-read the sector just written and compare it against the data in the buffer. Z FLAG set = data matches (write verified); NZ = mismatch (write failed).
4F46
CP A,06H FE 06
Compare Register A against 06H. The 4772H verify routine returns A = 06H when the read-back data matches the written data (successful verify). If A = 06H, the Z FLAG is set and the write is confirmed. Any other value in A indicates a verify mismatch.
4F48
LD A,15H 3E 15
Load Register A with error code 15H (Write Error). This value is loaded unconditionally here; if the JR NZ at 4F41H branched here the write failed, or if the CP A,06H at 4F46H was NZ the verify failed. In either case, A = 15H is the appropriate error code.
4F4A
POP HL E1
Pop the preserved HL value from the stack, restoring it.
4F4B
POP DE D1
Pop the preserved DE value from the stack, restoring it.
4F4C
RET C9
Return to the caller. If the write and verify both succeeded, the CP A,06H at 4F46H set Z FLAG and A = 06H before LD A,15H overwrote A - but note the RET here carries whatever flag state resulted from the LD A,15H (which sets NZ). The success path returns via RET Z is not present here; instead 4F46H's CP sets Z on match and the caller at 4E61H tests RET NZ for failure. On the success path (A was 06H at 4F46H, Z set), execution falls through to LD A,15H which reloads A but does not affect the Z FLAG set by the preceding CP. The Z FLAG from CP A,06H propagates to the RET.

4F4DH - FCB Buffer Pre-Read (CALL 4F4DH)

This subroutine (called at 4E48H) reads the target data sector into the FCB buffer at 4200H before the write, implementing the read-modify-write (pre-read) cycle. It calls 4B65H and 4B45H with E = 01H (directory sector type), reading into the directory buffer at 4200H. Returns Z on success, A=16H on directory read error.

4F4D
PUSH BC C5
Push Register pair BC onto the stack to preserve the drive number (C) and directory info byte (B) across this subroutine.
4F4E
PUSH DE D5
Push Register pair DE onto the stack to preserve it.
4F4F
CALL 4B65H CD 65 4B
GOSUB to SYS0 routine at 4B65H to set up the track and sector registers for directory I/O. Positions the WD1771 at the sector to be pre-read before the write.
4F52
LD E,01H 1E 01
Load Register E with 01H, the directory sector type code. This selects the directory sector buffer at 4200H for the read, rather than the data sector buffer at 5100H.
4F54
LD HL,4200H 21 00 42
Point Register pair HL to 4200H, the directory sector buffer. The pre-read sector data will be loaded here, merged with the FCB's write data, and then written back to disk.
4F57
CALL 4B45H CD 45 4B
GOSUB to SYS0 routine at 4B45H to read the sector from disk into the buffer at 4200H. E = 01H selects the directory sector path. Z FLAG set = sector read successfully; NZ = read error.
4F5A
POP DE D1
Pop the preserved DE value from the stack, restoring it.
4F5B
POP BC C1
Pop the preserved BC value (drive number and directory info byte) from the stack, restoring it.
4F5C
LD A,16H 3E 16
Load Register A with error code 16H (Directory Read Error). This is loaded unconditionally; the Z FLAG from the CALL 4B45H at 4F57H is still valid (not modified by the two POP instructions). The caller tests Z/NZ to determine success or failure; A = 16H is available as the error code if NZ.
4F5E
RET C9
Return to the caller. The Z FLAG from the sector read at 4B45H is preserved through the POPs and the LD A (which does not affect flags). Z FLAG set = success (sector pre-read into 4200H); NZ FLAG set = failure (A = 16H, Directory Read Error).

4F5FH - FCB Buffer Write with Verify (CALL 4F5FH)

This subroutine (called at 4E51H) writes the merged sector from the directory buffer at 4200H to disk and performs the read-after-write verify. Structure mirrors 4F34H but uses E = 01H and HL = 4200H. Returns Z on success, A=17H on write/verify error.

4F5F
PUSH BC C5
Push Register pair BC onto the stack to preserve the drive number (C) and directory info byte (B).
4F60
PUSH DE D5
Push Register pair DE onto the stack to preserve it.
4F61
CALL 4B65H CD 65 4B
GOSUB to SYS0 routine at 4B65H to set up the track and sector registers for directory I/O. Positions the WD1771 at the sector to be written.
4F64
LD E,01H 1E 01
Load Register E with 01H (directory sector type code), selecting the directory sector buffer at 4200H as the write source.
4F66
LD HL,4200H 21 00 42
Point Register pair HL to 4200H, the directory sector buffer containing the merged write data to be committed to disk.
4F69
CALL 4768H CD 68 47
GOSUB to SYS0 routine at 4768H to issue the WD1771 Write Sector command, writing the 256 bytes at 4200H to disk. Z FLAG set = write completed; NZ = write error.
4F6C
If the NZ FLAG (Not Zero) has been set - meaning the write failed - JUMP to 4F73H to restore registers and return the error.
4F6E
CALL 4772H CD 72 47
GOSUB to SYS0 routine at 4772H to perform the read-after-write verification for the directory sector just written. Z FLAG set = verify success; NZ = verify failure.
4F71
CP A,06H FE 06
Compare Register A against 06H. As with 4F34H/4F43H above, 4772H returns A = 06H on a successful verify. If A = 06H, the Z FLAG is set and the write is confirmed. Any other value indicates a verify mismatch.
4F73
LD A,17H 3E 17
Load Register A with error code 17H (Directory Write Error). Loaded unconditionally; the Z FLAG from the preceding CP A,06H (or the write failure at 4768H) is not disturbed by this LD. The caller tests Z/NZ to determine success; A = 17H is available as the error code if NZ.
4F75
POP DE D1
Pop the preserved DE value from the stack, restoring it.
4F76
POP BC C1
Pop the preserved BC value from the stack, restoring it.
4F77
RET C9
Return to the caller. Z FLAG from the CP A,06H (or write-failure path) is preserved through the POPs and the LD A,17H (which does not affect flags). Z FLAG set = success (directory sector written and verified); NZ FLAG set = failure (A = 17H, Directory Write Error).