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

Page Customization

General:

SYS0/SYS is the core module of NEWDOS/80 v2.0 for the TRS-80 Model III, loaded into memory at 4000H during the boot process. This module provides the fundamental DOS services including disk I/O, file management, and system initialization. The program entry point is 4D00H.

FCB (File Control Block) Structure

The FCB is accessed via the IX register throughout file operations. Each open file maintains an FCB containing position tracking, buffer pointers, and status flags.

OffsetSizeFieldDescription
+00H1Status Byte Bit 0: If =1, sectors written to the file are written read protected in the same manner as DOS writes directory sectors. This bit should never be 1 during byte I/O via the 0013H or 001BH calls.
Bit 1: If =1, the value in the FCB's NEXT and EOF fields are RBAs within the diskette, rather than the file. This allows the user to I/O directly to diskette sectors, bypassing the file concept altogether. This bit should never be 1 during byte I/O via the 0013H or 001BH calls.
Bit 2: Undefined
Bit 3: Undefined
Bit 4: Undefined
Bit 5: Undefined
Bit 6: Undefined
Bit 7: If =1, the link is in existence (i.e., an open has been done). If =0, the link is not in existence (i.e., either an open has not been done or a close has been subsequently done).
+01H1Operation Flags Bits 2-0. Access level code
Bit 3:
  • 0: This FCB is in the old format and is illegal in NEWDOS/80 Version 2
  • 1: This FCB is in the NEWDOS/80 Version 2 format for the 18th - 32nd bytes. This bit is set to 1 by DOS open.
Bit 4:
  • =0, the FCB's buffer does not contain updated data not yet sent to the file
  • =1, the FCB's buffer does contain updated data not yet sent to the file. During DOS close, if this bit is 1, the sector data in the buffer is automatically written to disk. This updated data is also written on every 443FH and 4451H call and on every 4442H, 4445H, 4448H and 444EH call that positions the file within a different sector.
Bit 5:
  • =0, the FCB's buffer contains the current file sector's data.
  • =1, the buffer does not contain the current file sector's data; if needed, that sector's data must be read into the buffer.
Bit 6:
  • =0. The FCB's EOF value is to be set equal to the FCB's resulting NEXT value on every successful write operation.
  • =1. The FCB's EOF value is to be set equal to the FCB's resulting NEXT value only for those successful write operations resulting in the NEXT value exceeding the current EOF value.
Bit 7:
  • =0, Read and write operations are by full 256 byte sectors with the FCB's NEXT value incremented 256 bytes upon the completion of each successful I/O.
  • =1. Either single byte operations or logical record operations (record length in FCB's 10th byte) are being done via this FCB. NEXT value is maintained at the next byte to be read or written. This bit is set to 1 at open time if register B is not 0. It is also set to 1 whenever byte I/O is done via the 0013H or 001BH ROM calls.
+02H1Extended Flags
  • Bits 4 - 0. Undefined and reserved for future definition.
  • Bits 7-5. These bits are defined the same as those in the FPDE 2nd byte. If bit 5 equals 0, the DOS sector write routine sets the bit to 1 in both the FCB and the FPDE just before it actually writes the current sector to disk.
+03H–04H2Buffer PointerAddress of 256-byte sector buffer for this FCB. The user determines where the buffer is to be and puts this address into register HL before the call to the DOS open routine. Sectors are read from disk into this buffer and written to disk from this buffer.
+05H1Byte OffsetThe low order byte of the FCB's NEXT field. This is the relative position within sector value.
+06H1Drive NumberThe relative number of the drive containing the diskette containing the file
+07H1Directory SectorThe DEC code of file's FPDE. After the FCB is opened, this DEC code is the link between the open FCB and the file's directory information as the FCB itself no longer contains the filespec.
+08H1EOF Byte OffsetThe low order byte of EOF. This is the relative position within the EOF sector.
+09H1Record LengthThe logical record length (LRECL) (0 = 256) for records of this file. This value is supplied in register B by the caller at open time. If not 0 at open time, bit 7 of the FCB's 2nd byte is set to 1, and subsequent DOS sector read or write calls must contain, in register HL, the address of the logical record to be moved to the FCB's buffer (write) or filled from the FCB's buffer (read).
+0AH1Current RecordMiddle order byte of the NEXT field.
+0BH2Current RecordHigh order byte of the NEXT field. The 12th, 11th and 5th bytes form a 3 byte RBA within the file of the next byte to be processed, either input or output.
+0CH–0DH2EOF RecordLast record containing data (low byte at +0CH)
+0EH1Directory IndexIndex of file’s entry within the directory sector
+0FH–+10H2 bytesReserved / unused in basic FCB
+11H–+1FH15 bytesExtended FCB area (NEWDOS/80 v2 format)
Contains additional extent pointers, user data, or model-specific flags when bit 3 of +01H is set.

System Variables Reference:

AddressSizePurpose
4052H-407FH46 bytesSystem variables and counters (NOPs at load time, populated at runtime)
4275H1 byteDOS signature byte (A5H)
427EH1 byteCurrent drive number (0-3)
427FH1 byteDrive select register shadow (for port F4H)
4280H2 bytesDisk parameter configuration (0323H = sectors/track and heads)
4282H1 byteStep rate configuration (bits 0-1: 00=6ms, 01=12ms, 10=20ms, 11=30ms)
4283H1 byteMaximum track number (35 = 23H for 35-track drives)
4284H1 byteSectors per track (10 = 0AH)
4285H1 byteGranules per track or sectors per granule (2)
4287H1 byteDrive flags (bit 0: double-sided, bit 1: track offset, bit 2: 80-track, bit 4: sector numbering, bit 6: precompensation)
4289H1 byteSystem flags (40H default)
4290H1 byteDisk parameter (5AH)
4299H-42A5H13 bytesDrive 0 parameter block and FCB template
42A6H-42AFH13 bytesDrive 1 parameter block and FCB template
42B0H-42BCH13 bytesDrive 2 parameter block and FCB template
42B9H-42BAH2 bytesPointer to current drive parameter block
4302H1 byteSystem configuration flags
• Bit 0: 1 = allow lowercase input (if supported by driver)
• Bit 1: 1 = date/time support enabled
• Other bits: reserved / model-specific
441FH1 byteTimer tick increment counter (incremented by interrupt handler)
4480H-449FH32 bytesDefault FCB template data
44C0H-44D1H18 bytesAdditional FCB data template
4511H-4512H2 bytesSelf-modifying code: OR instruction operand (modified at runtime)
456BH2 bytesSelf-modifying code: LD HL address operand
4583H-4584H2 bytesSelf-modifying code: LD HL address operand (timer callback list pointer)
459CH1 byteSelf-modifying code: NOP/RET toggle (00H=NOP, C9H=RET)
4687H1 byteSelf-modifying code: FDC command byte (88H=read, A8H/A9H=write)
468FH1 byteSelf-modifying code: Byte count for FDC transfer
469EH, 46A6H1 byte eachSelf-modifying code: Loop instruction operands
46BAH2 bytesSaved stack pointer during FDC operations
480FH1 byteSelf-modifying code: FCB flags storage
4860H1 byteStatus flag for file operations
48D5H1 byteFile operation parameter
494BH2 bytesSaved return address for directory operations
4973H2 bytesSaved stack pointer for error recovery

Self-Modifying Code Locations

  • 4511H-4512H: OR instruction operand modified to change timer behavior
  • 456BH, 4583H-4584H: LD HL address operands for callback list pointers
  • 459CH: NOP/RET toggle for interrupt control
  • 4687H: FDC command byte (88H read, A8H/A9H write)
  • 468FH, 469EH, 46A6H: Loop counters and DJNZ targets for data transfer
  • 46BAH: Stack pointer storage/restoration
  • 480FH: FCB flags temporary storage

Memory Map:

Address RangeFunction
400CH-4014HDOS Jump Table - Fixed entry points for system services
402DH-4035HWarm Start vector and DOS Ready handler
4052H-407FHSystem variables and storage
41FDHMemory clear operation
4203HJump to DOS Ready command
4275HDOS signature byte (A5H)
427EH-42C8HDrive configuration and FCB templates
42D2H-42FFHAdditional system storage
4400H-440DHMain DOS Ready handler
4413H-4435HSystem service call table
4436H-4475HFile operation jump table
4476H-449FHFCB template and storage
44C0H-44D1HAdditional FCB data
44D2H-44EAHHexadecimal to ASCII conversion
44EBH-453DHKeyboard input handler
4546H-4550HCharacter case conversion
4551H-459CHTimer interrupt handler and callback system
459DH-45D0HKeyboard scan (Model III hardware)
45D1H-45DAHInterrupt service routine entry
45DBH-45F1HFDC command setup
45F2H-4623HMain sector read/write entry
4627H-465CHDrive select and FDC parameters
465EH-469FHFDC sector read (Model III port I/O)
46ABH-46B5HSelf-modifying code setup
46B6H-46CFHFDC operation completion
46D0H-46E9HFDC status analysis and error codes
46F2H-470FHDrive restore and FDC commands
4711H-471AHFDC status check and drive select
471BH-4788HDrive change and initialization
4789H-4790HFDC wait for ready
4791H-47ACHWait for index pulse with timeout
47AEH-47FFHFile open / directory search
4800H-488DHExtent management and directory update
488EH-4897HExtent length calculation
4898H-4924HFile I/O support routines
4925H-4950HDirectory search setup

Major Routines:

Entry Points and Vectors

DOS Jump Table (400CH–4035H): Fixed entry points for system services, interrupt handling, and the DOS READY warm-start vector. Programs call these addresses to access DOS functions without needing to know internal routine locations.
AddressRoutinePurpose
400CHSystem Service EntryJump to routine at 4B67H
400FHInterrupt Handler EntryJump to interrupt service at 45D4H
4012HROM Routine VectorJump to ROM at 3018H
402DHDOS Ready/Warm StartMain error recovery and DOS READY entry point
4030HDOS Ready CommandDisplay DOS READY prompt (RST 28H function 43H)
4052H–42FFHSystem VariablesRuntime storage including the current drive number (427EH), drive select register shadow (427FH), disk geometry parameters (4280H–4289H), Drive Parameter Blocks for up to 4 drives (4290H–42C8H), and FCB slot templates (42D2H–42FFH).
4400H–447FHDOS Ready Handler / SVC Dispatch TableThe DOS READY handler and Supervisor Call (RST 28H) function table. Each SVC function code maps to a specific routine for operations like file open, read, write, close, and directory management.

File Operations

AddressRoutinePurpose
4436H-444EHFile Operation Jump TableVectors for open, close, read, write, seek operations
44EBH–45D0HKeyboard and Timer ServicesKeyboard input handling with case conversion, timer interrupt processing, and callback management for timed operations.
45DBH–4715HFloppy Disk Controller InterfaceLow-level FDC routines for the WD1793 controller using Model III port-based I/O (ports F0H–F4H). Includes sector read/write with DMA-style transfers, drive selection, seek operations, and error recovery with retry logic.
4715H–4790HDrive ManagementDrive change detection, track position caching, and drive parameter block switching. Maintains shadow copies of FDC registers to minimize hardware access.
47AEH–4A06HFile OperationsHigh-level file I/O including open, read, write, and position management. Handles extent allocation, EOF tracking, and directory entry updates. Two write modes support sequential extension (49D7H) and random-access overwrite (49DBH).
4925H–4982HDirectory ServicesDirectory search, entry creation, and FCB initialization. Manages the translation between filenames and disk locations through the Granule Allocation Table.
488EHExtent Length CalculationCalculate sector counts from extent length values
490DHEOF DetectionCompare current position against EOF in FCB

Disk I/O Operations (Model III Port-Based)

AddressRoutinePurpose
45DBHFDC Read Command SetupPrepare 88H read sector command
45E7H/45EBHFDC Write Command SetupPrepare A8H/A9H write sector commands
45F2HSector Read/Write EntryMain entry for disk operations (handles both read/write)
465EHFDC Sector ReadExecute sector read using ports F0H-F4H
4670HFDC Data Transfer LoopHigh-speed byte-by-byte transfer using INI instruction
46B6HFDC Completion HandlerRestore system state after transfer, read final status
46D0HFDC Status AnalysisConvert FDC status bits to DOS error codes
46F7HFDC Command ExecutionSend command with step rate, wait for completion
4711HFDC Status CheckRead status from port F0H, test Not Ready flag
4715HDrive SelectOutput drive select byte to port F4H
4720H/4723HDrive ChangeSave/restore track positions, copy parameters, initialize drive
4789HFDC Wait for ReadyPoll busy flag until FDC completes operation
4791HWait for Index PulseDetect disk spinning by watching index bit with timeout

Interrupt and Timer

AddressRoutinePurpose
4551H-459CHTimer Callback SystemManage linked list of timer callbacks with countdown
45D1H/45D4HInterrupt Service EntryMain interrupt handler entry (called by hardware)
45D8HTimer Callback ExecutionExecute callback when countdown reaches zero

Keyboard and Input

AddressRoutinePurpose
44EBH-453DHKeyboard Input HandlerProcess keyboard, manage timers, self-modifying code
459DHKeyboard ScanRead Model III keyboard hardware (3801H-3810H), check BREAK
4548HLowercase to UppercaseConvert characters 61H-7AH to 41H-5AH

Utility Routines

AddressRoutinePurpose
44D2HHex to ASCII ConversionConvert byte in DE to ASCII hex representation
46ABHSelf-Modifying Code SetupModify FDC transfer loop for different sector sizes
4A80H–4AE9HDevice Driver ChainExtensible device driver framework allowing additional I/O devices to be linked into the system.
4BCDH–4CA1HUtility RoutinesModule loading, arithmetic helpers (multiply/divide), string comparison, and command-line token parsing.
4D00H–4F95HInitialization CodeCold/warm boot detection, RAM sizing, boot sector parameter copying, drive configuration, date/time prompts, and AUTO command execution.
4F96H–504BHBoot BannerThe NEWDOS/80 startup display with semigraphic logo, "MODEL III VER 2" text, and "APPARAT, INC" copyright notice.

Disassembly

400CH - DOS Jump Table Entry Points

The DOS Jump Table provides fixed entry points for system services. These vectors allow programs to call DOS functions without needing to know the actual location of the routines.

400C
JUMP to system service at 4B67H.
400F
JUMP to interrupt entry handler at 45D4H.
4012
JUMP to ROM routine at 3018H which jumps to 35C2H for Maskable Interrupt Handler.

402DH - Error Handler and DOS Ready Jump

The DOS Ready jump at 402DH is the standard warm-start entry point. When a program terminates or an error occurs, execution returns here.

402D
JUMP to DOS Ready/warm start handler at 4400H. This is the error recovery entry point.
4030
LD A,43H 3E 43
Load Register A with 43H (SVC function code). This is the "DOS Ready" command indicator.
4032
RST 28H EF
Call Supervisor Call dispatcher. RST 28H is the DOS system call mechanism - function 43H displays the DOS READY prompt.
4033
JUMP to device driver chain handler at 4A80H. After displaying ready prompt, process any pending driver requests.

4052H–407FH — Runtime System Variables (Zero at Load Time)

This 46-byte area is zero-initialized at load time (appears as NOPs in the disassembly). At runtime, these locations hold system state information including track cache tables for each drive, timer callback data, and temporary storage used by various DOS routines.

[RUNTIME VARIABLE AREA] All bytes are 00H at load time; populated during DOS initialization and operation.

4052
DB 00H 00
Runtime variable: Temporary storage used during FDC operations.
4053–405F
DB 00H × 13 00 00 00 00 00 00 00 00 00 00 00 00 00
Runtime storage: General-purpose scratch area used by sector read/write, directory search, and file I/O routines.
4060–406F
DB 00H × 16 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
Runtime storage: Continuation of scratch area; holds partial FCB data, buffer pointers, or granule allocation maps during file operations.
4070–4075
DB 00H × 6 00 00 00 00 00 00
Runtime storage: Track cache status bytes for drives 0–3 plus 2 reserved. Each byte indicates whether the corresponding drive’s track data is cached in memory.
4076–4079
DB 00H × 4 00 00 00 00
Runtime storage: Current track position cache for drives 0–3. FDC track register shadow values are stored here after SEEK operations.
407A–407F
DB 00H × 6 00 00 00 00 00 00
Runtime storage: Additional scratch area for timer callbacks and interrupt handler temporary data.

41FDH - Memory Clear Operation

This instruction clears a memory location pointed to by HL.

41FD
LD (HL),00H 36 00
Store zero at the memory location pointed to by HL. This initializes or clears a memory byte.

4203H - Jump to DOS Ready Command

This jump redirects control to the DOS Ready command handler.

4203
JUMP to DOS Ready command at 4030H. This displays the DOS READY prompt.

4275H - DOS Signature Byte

The DOS signature byte A5H is used to validate DOS presence and integrity.

4275
DB A5H A5
Data byte A5H - DOS signature/marker byte.

427EH–42C8H — Drive Configuration and Parameter Blocks

This area contains the core disk drive configuration including the current drive number, drive select register shadow, disk geometry parameters, and Drive Parameter Blocks (DPBs) for up to 4 drives. Many values are initialized from the boot sector during DOS startup. The DPB for each drive contains information about track count, sector size, and FCB buffer pointers.

[CORE SYSTEM CONFIGURATION]

427E
DB 00H 00
Current Drive Number (0–3). Written by 4734H during drive change. The routine at 4720H reads this to determine which drive is active. Value 00H = Drive 0, 01H = Drive 1, etc.
427F
DB 00H 00
Drive Select Register Shadow. This is the last value written to FDC port F4H. The routine at 4715H outputs (427FH) to port F4H. Bits: 0–3 = drive select (01=D0, 02=D1, 04=D2, 08=D3), bit 5 = MFM enable, bit 6 = precompensation.

[DEFAULT DISK GEOMETRY (SS/SD FORMAT)]

4280–4281
DW 0323H 23 03
Disk Parameter Word: Low byte (23H) = 35 decimal = sectors per track configuration; High byte (03H) = step rate (03=30ms per step for older drives). Referenced by FDC routines through IY+offset.
4282
DB 00H 00
Step Rate Configuration. Bits 0–1 contain the WD1793 step rate: 00=6ms, 01=12ms, 10=20ms, 11=30ms. The routine at 46F7H masks this with AND 03H before OR’ing with FDC commands.
4283
DB 23H 23
Maximum Track Number (35 decimal). For 35-track drives, tracks 0–34 are valid. The routine at 4634H compares against this to detect track-out-of-range errors. 40-track drives use 28H (40 decimal).
4284
DB 0AH 0A
Sectors Per Track (10 decimal for single-density). Read by 460EH to compute absolute sector from track/sector pair. Double-density disks use 12H (18 sectors).
4285
DB 02H 02
Sectors Per Granule. Granules are the allocation unit for files. Value 02H = 2 sectors per granule = 512 bytes per granule. The divide routine at 4C57H uses this for granule calculations.
4286
DB 00H 00
Reserved (unused in standard configuration).
4287
DB 00H 00
Drive Flags for current drive. Bit 0: double-sided; Bit 1: track offset mode; Bit 2: 80-track mode; Bit 4: sector numbering base; Bit 6: write precompensation enable. Read by 462AH and tested throughout FDC routines.
4288
DB 00H 00
Last SVC Number. The SVC dispatcher at 4B67H stores the current SVC function code here for reference by subroutines.
4289
DB 40H 40
System Flags. Bit 6 = SVC in progress (set at 4B75H, cleared at 4BC9H). The routine at 459DH tests bit 6 to control timer callback behavior during system calls.
428A–428F
DB 00H × 6 00 00 00 00 00 00
Reserved bytes (initialized to 00H, may be used by DOS extensions).

[DRIVE 0 PARAMETER BLOCK (4290H–42A5H)]

4290
DB 5AH 5A
Drive 0 configuration byte. This value is copied from boot sector location 43A8H during initialization at 4DBCH.
4291–4292
DW 0323H 23 03
Drive 0 geometry parameters (same format as 4280H–4281H).
4293–4298
DB 23H, 0AH, 02H, 00H, 00H, 00H 23 0A 02 00 00 00
Drive 0 extended parameters: max track (23H), sectors/track (0AH), sectors/granule (02H), plus 3 reserved bytes.
4299–429BH
DB 11H, 02H, FFH 11 02 FF
Drive 0 FCB template prefix: status byte (11H), flags (02H), uninitialized marker (FFH).
429C–42A5
DB 01H, 00H, 01H, 00H, 00H, 00H, 00H, FFH, 00H, FFH 01 00 01 00 00 00 00 FF 00 FF
Drive 0 FCB template: sector count/size (0100H), reserved bytes, uninitialized markers (FFH).

[DRIVE 1 PARAMETER BLOCK (42A6H–42AFH)]

42A6–42AFH
DB 01H, 00H, 01H, 00H, 00H, 00H, 00H, FFH, 00H, FFH 01 00 01 00 00 00 00 FF 00 FF
Drive 1 parameter block and FCB template (same structure as Drive 0).

[DRIVE 2 PARAMETER BLOCK (42B0H–42B8H)]

42B0–42B8H
DB 01H, 00H, 01H, 00H, 00H, 00H, 00H, FFH, 00H 01 00 01 00 00 00 00 FF 00
Drive 2 parameter block and FCB template.

[SYSTEM POINTERS AND CONFIGURATION]

42B9–42BAH
DW 4291H 91 42
Current Drive Parameter Block Pointer. Points to the DPB for the currently selected drive. The routine at 489BH reads this with LD HL,(42B9H) to access drive parameters. Updated when drive changes.
42BB–42BEH
DB 00H, 00H, 00H, 00H 00 00 00 00
Reserved bytes (additional drive parameter expansion space).
42BF
DB 04H 04
Number of Drives configured in system (4 drives maximum). Loaded from boot sector at 4D72H and stored here. Used for drive number validation.
42C0–42C2H
DB 01H, 00H, 01H 01 00 01
Additional configuration bytes. Values copied from boot sector locations 43A2H–43A3H at 4D86H–4D8CH.
42C3–42C6H
DB 00H, 01H, 00H, 00H 00 01 00 00
Additional system configuration (loader address operand at 4BC3H is modified here).
42C7–42C8H
DB 0DH, 0DH 0D 0D
Carriage return bytes (0DH = CR). Used as string terminators for command-line parsing.

42D2H–42FFH — Extended System Storage and FCB Templates

This area continues the system variable storage with additional Drive Parameter Block entries for Drive 3, FCB buffer templates, and system pointers. The FFH bytes indicate uninitialized buffer positions; 00H bytes are reserved for runtime data.

[DRIVE 3 PARAMETER BLOCK AND FCB TEMPLATE]

42D2–42D8H
DB 00H × 7 00 00 00 00 00 00 00
Drive 3 parameter block: reserved bytes for geometry and configuration data. Populated when Drive 3 is accessed.
42D9–42DAH
DB FFH, FFH FF FF
Drive 3 FCB buffer pointer (uninitialized = FFFFH). When a file is opened on Drive 3, this is set to the actual buffer address.

[SYSTEM POINTERS]

42DB–42DCH
DW 4CA1H A1 4C
End-of-Chain Return Address. Points to the end-of-device-chain handler at 4CA1H. Used by the device driver chain walker at 4AE9H.
42DD–42DFH
DB 00H, 00H, 00H 00 00 00
Reserved bytes (runtime scratch area).

[FCB SLOT TEMPLATES] Each 7-byte group represents an FCB slot with status/pointer/marker bytes.

42E0–42E1H
DB FFH, FFH FF FF
FCB Slot 1 buffer pointer (FFFFH = uninitialized/not in use).
42E2–42E6H
DB 00H, 00H, 00H, 00H, 00H 00 00 00 00 00
FCB Slot 1 status bytes: file position, record counter, flags.
42E7–42E8H
DB FFH, FFH FF FF
FCB Slot 2 buffer pointer (FFFFH = uninitialized).
42E9–42EDH
DB 00H, 00H, 00H, 00H, 00H 00 00 00 00 00
FCB Slot 2 status bytes.
42EE–42EFH
DB FFH, FFH FF FF
FCB Slot 3 buffer pointer (FFFFH = uninitialized).
42F0–42F4H
DB 00H, 00H, 00H, 00H, 00H 00 00 00 00 00
FCB Slot 3 status bytes.
42F5–42F6H
DB FFH, FFH FF FF
FCB Slot 4 buffer pointer (FFFFH = uninitialized).
42F7–42FBH
DB 00H, 00H, 00H, 00H, 00H 00 00 00 00 00
FCB Slot 4 status bytes.
42FC–42FDH
DB FFH, FFH FF FF
FCB Slot 5 buffer pointer (FFFFH = uninitialized).
42FE–42FFH
DB 00H, 00H 00 00
FCB Slot 5 status bytes (partial—continues past this area).

4400H - DOS Ready Handler

This is the main DOS Ready handler, the warm-start entry point called when programs terminate or errors occur. It performs system reinitialization and displays the DOS READY prompt.

4400
LD A,23H 3E 23
Load Register A with 23H (SVC function code for system reinitialization).
4402
RST 28H EF
Call Supervisor Call dispatcher to execute function 23H.
4403
NOP 00
NO OPERATION (padding or unused).
4404
NOP 00
NO OPERATION (padding or unused).
4405
LD A,63H 3E 63
Load Register A with 63H (SVC function code).
4407
RST 28H EF
Call Supervisor Call dispatcher to execute function 63H.
4408
RET Z C8
If the Z FLAG (Zero) has been set, RETURN to caller.
4409
PUSH AF F5
Save Register Pair AF onto the stack.
440A
LD A,26H 3E 26
Load Register A with 26H (SVC function code).
440C
RST 28H EF
Call Supervisor Call dispatcher to execute function 26H.
440D
JUMP to interrupt entry handler at 45D4H.
4410
RET C9
RETURN to caller.
4411
RST 38H FF
Data byte FFH (padding or unused).
4412
RST 38H FF
Data byte FFH (padding or unused).

4413H-4435H - System Service Call Table

This section contains a series of system service calls using RST 28H with various function codes, followed by jump vectors to service routines.

4413
LD A,85H 3E 85
Load Register A with 85H (SVC function code).
4415
RST 28H EF
Call Supervisor Call dispatcher to execute function 85H.
4416
JUMP to service routine at 4711H.
4419
LD A,C3H 3E C3
Load Register A with C3H (SVC function code).
441B
RST 28H EF
Call Supervisor Call dispatcher to execute function C3H.
441C
LD A,83H 3E 83
Load Register A with 83H (SVC function code).
441E
RST 28H EF
Call Supervisor Call dispatcher to execute function 83H.
441F
NOP 00
NO OPERATION (padding or counter byte).
4420
LD A,44H 3E 44
Load Register A with 44H (SVC function code).
4422
RST 28H EF
Call Supervisor Call dispatcher to execute function 44H.
4423
NOP 00
NO OPERATION (padding).
4424
LD A,24H 3E 24
Load Register A with 24H (SVC function code).
4426
RST 28H EF
Call Supervisor Call dispatcher to execute function 24H.
4427
ADD A,D 82
Data byte 82H.
4428
LD A,25H 3E 25
Load Register A with 25H (SVC function code).
442A
RST 28H EF
Call Supervisor Call dispatcher to execute function 25H.
442B
INC BC 03
Data byte 03H.
442C
LD A,45H 3E 45
Load Register A with 45H (SVC function code).
442E
RST 28H EF
Call Supervisor Call dispatcher to execute function 45H.
442F
NOP 00
NO OPERATION (padding).
4430
LD A,A4H 3E A4
Load Register A with A4H (SVC function code).
4432
RST 28H EF
Call Supervisor Call dispatcher to execute function A4H.
4433
LD A,C4H 3E C4
Load Register A with C4H (SVC function code).
4435
RST 28H EF
Call Supervisor Call dispatcher to execute function C4H.

4436H-4475H - File Operation Jump Table

This table provides jump vectors for file operations including open, close, read, write, and seek. These are the primary file I/O entry points used by programs.

4436
File operation: JUMP to the File Read Operation routine at 49A1H.
4439
File operation: JUMP to WRITE WITHOUT EOF UPDATE routine at 49DBH.
443C
File operation: JUMP to WRITE WITH EOF UPDATE routine at 49D7H.
443F
File operation: JUMP to the RETURN CURRENT FILE POSITION INTO CALLER’S BUFFER routine at 4AF1H.
4442
File operation: JUMP to the Seek to ABSOLUTE RECORD/SECTOR POSITION routine at 4B18H.
4445
File operation: JUMP to the SEEK BY RELATIVE BYTE COUNT routine at 4B07H.
4448
File operation: JUMP to the POSITION TO END OF FILE routine at 4AF9H.
444B
File operation: JUMP to the FILE OPEN / DIRECTORY SEARCH AND FCB SETUP routine at 47AEH.
444E
File operation: JUMP to the SET POSITION TO BEGINNING-OF-FILE / REWIND routine at 4AECH.
4451
LD A,C5H 3E C5
Load Register A with C5H (SVC function code).
4453
RST 28H EF
Call Supervisor Call dispatcher to execute function C5H.
4454
NOP 00
NO OPERATION (padding).
4455
NOP 00
NO OPERATION (padding).
4456
NOP 00
NO OPERATION (padding).
4457
LD A,2AH 3E 2A
Load Register A with 2AH (SVC function code).
4459
JUMP relative to 4409H.
445B
JUMP to routine at 4723H.
445E
JUMP to routine at 4791H.
4461
LD A,2BH 3E 2B
Load Register A with 2BH (SVC function code).
4463
RST 28H EF
Call Supervisor Call dispatcher to execute function 2BH.
4464
LD A,4BH 3E 4B
Load Register A with 4BH (SVC function code).
4466
RST 28H EF
Call Supervisor Call dispatcher to execute function 4BH.
4467
JUMP to routine at 4B4BH to print a NUL/CR-terminated string, using 401DH as the base address.
446A
JUMP to routine at 4B61H to print a NUL/CR-terminated string, but using 4025H as the base address.
446D
JUMP to ROM routine at 3036H which gets the time.
4470
JUMP to ROM routine at 3033H which puts the DATE onto the upper right hand corner of the screen.
4473
LD A,A3H 3E A3
Load Register A with A3H (SVC function code).
4475
RST 28H EF
Call Supervisor Call dispatcher to execute function A3H.

4476H-447FH - Additional Service Handlers

Short service routines for system operations.

4476
NOP 00
NO OPERATION (padding).
4477
NOP 00
NO OPERATION (padding).
4478
JUMP to routine at 4546H.
447B
LD A,65H 3E 65
Load Register A with 65H (SVC function code).
447D
RST 28H EF
Call Supervisor Call dispatcher to execute function 65H.
447E
XOR A AF
Set Register A to ZERO and clear all flags.
447F
RET C9
RETURN to caller with A=0 (success).

4480H-449FH - FCB Template and Storage

Default FCB (File Control Block) template and additional storage area.

4480
ADD A,D 82
Data byte 82H - FCB status flag.
4481
Conditional jump (data bytes).
4483
NOP 00
Data byte 00H.
4484
LD B,E 43
Data byte 43H (ASCII 'C').
4485
NOP 00
Data byte 00H.
4486
NOP 00
Data byte 00H.
4487
RST 38H FF
Data byte FFH (uninitialized marker).
4488
NOP 00
Data byte 00H.
4489
NOP 00
Data byte 00H.
448A
LD (BC),A 02
Data byte 02H.
448B
NOP 00
Data byte 00H.
448C
RST 38H FF
Data byte FFH (fill).
448D
RST 38H FF
Data byte FFH (fill).
448E
RST 38H FF
Data byte FFH (fill).
448F
RST 38H FF
Data byte FFH (fill).
4490
RST 38H FF
Data byte FFH (fill).
4491
RST 38H FF
Data byte FFH (fill).
4492
RST 38H FF
Data byte FFH (fill).
4493
RST 38H FF
Data byte FFH (fill).
4494
RST 38H FF
Data byte FFH (fill).
4495
RST 38H FF
Data byte FFH (fill).
4496
RST 38H FF
Data byte FFH (fill).
4497
RST 38H FF
Data byte FFH (fill).
4498
RST 38H FF
Data byte FFH (fill).
4499
RST 38H FF
Data byte FFH (fill).
449A
RST 38H FF
Data byte FFH (fill).
449B
RST 38H FF
Data byte FFH (fill).
449C
RST 38H FF
Data byte FFH (fill).
449D
RST 38H FF
Data byte FFH (fill).
449E
RST 38H FF
Data byte FFH (fill).
449F
RST 38H FF
Data byte FFH (fill).

44C0H-44D1H - Additional FCB Data

More FCB template data and system variables.

44C0
ADD A,B 80
Data byte 80H.
44C1
Conditional jump (data bytes).
44C3
NOP 00
Data byte 00H.
44C4
LD B,E 43
Data byte 43H (ASCII 'C').
44C5
NOP 00
Data byte 00H.
44C6
NOP 00
Data byte 00H.
44C7
RST 38H FF
Data byte FFH (uninitialized marker).
44C8
NOP 00
Data byte 00H.
44C9
NOP 00
Data byte 00H.
44CA
NOP 00
Data byte 00H.
44CB
NOP 00
Data byte 00H.
44CC
LD B,B 40
Data byte 40H.
44CD
NOP 00
Data byte 00H.
44CE
NOP 00
Data byte 00H.
44CF
NOP 00
Data byte 00H.
44D0
RST 38H FF
Data byte FFH (fill).
44D1
RST 38H FF
Data byte FFH (fill).

44D2H-44EAH - Hexadecimal to ASCII Conversion

This routine converts hexadecimal values to ASCII representation for display. It processes both bytes of DE register pair.

44D2
LD A,D 7A
Load Register A with D (high byte of value to convert).
44D3
GOSUB to hex digit conversion routine at 44D7H to convert high byte.
44D6
LD A,E 7B
Load Register A with E (low byte of value to convert).
44D7
PUSH AF F5
Save Register Pair AF onto the stack to preserve the value.
44D8
RRCA 0F
Rotate Register A Right Circular - shifts high nibble into low position (bit 0).
44D9
RRCA 0F
Rotate Register A Right Circular.
44DA
RRCA 0F
Rotate Register A Right Circular.
44DB
RRCA 0F
Rotate Register A Right Circular. After 4 rotations, high nibble is now in low nibble position.
44DC
GOSUB to convert high nibble (now in low position) to ASCII at 44E0H.
44DF
POP AF F1
Restore Register Pair AF from the stack to get original value back.
44E0
AND 0FH E6 0F
Mask off high nibble, keeping only bits 0-3 (the low nibble).
44E2
ADD 90H C6 90
ADD 90H to Register A. This is part of the BCD adjustment algorithm.
44E4
DAA 27
Decimal Adjust Accumulator. Adjusts the value for BCD conversion.
44E5
ADC 40H CE 40
ADD with Carry 40H to Register A. Completes the hex-to-ASCII conversion (0-9 become 30H-39H, A-F become 41H-46H).
44E7
DAA 27
Decimal Adjust Accumulator again to finalize ASCII character.
44E8
LD (HL),A 77
Store the converted ASCII character at the memory location pointed to by HL.
44E9
INC HL 23
INCrement HL by 1 to point to next character position.
44EA
RET C9
RETURN to caller.

44EBH-453DH - Keyboard Input Handler and System Check

This complex routine handles keyboard input, system state checks, and timer management. It interfaces with the Model III keyboard hardware at memory-mapped locations 3801H-3810H.

44EB
LD A,(4289H) 3A 89 42
Fetch system flags from memory location 4289H into Register A.
44EE
XOR 20H EE 20
XOR Register A with 20H to toggle bit 5.
44F0
AND 68H E6 68
Mask bits 6, 5, and 3 (01101000 binary).
44F2
LD A,CBH 3E CB
Load Register A with CBH (function code).
44F4
If the Z FLAG (Zero) has been set (masked value is zero), GOSUB to 4982H.
44F7
RET Z C8
If the Z FLAG (Zero) has been set, RETURN to caller.
44F8
LD HL,459CH 21 9C 45
Point Register Pair HL to memory location 459CH.
44FB
LD (HL),C9H 36 C9
Store C9H (RET instruction opcode) at location 459CH. This is self-modifying code that creates a RET instruction.
44FD
PUSH HL E5
Save Register Pair HL onto the stack.
44FE
LD HL,(41FDH) 2A FD 41
Fetch 16-bit value from memory location 41FDH into HL. This is a pointer to a system variable.
4501
LD D,40H 16 40
Load Register D with 40H (high byte of address in page 40XXH).
4503
LD E,L 5D
Load Register E with L to form address in DE.
4504
LD A,(DE) 1A
Fetch byte from memory location pointed to by DE into Register A.
4505
AND H A4
AND Register A with H to test bits.
4506
LD B,A 47
Load Register B with the result in A.
4507
LD A,00H 3E 00
Load Register A with 00H.
4509
If the NZ FLAG (Not Zero) has been set (AND result was non-zero), JUMP to 450EH.
450B
LD (452AH),A 32 2A 45
Store Register A (00H) to memory location 452AH (self-modifying code address).
450E
XOR A AF
Set Register A to ZERO and clear all flags.
450F
LD H,A 67
Load Register H with A (zero).
4510
LD L,A 6F
Load Register L with A (zero). HL is now 0000H.
4511
OR 00H F6 00
OR Register A with 00H. This is self-modifying code - the 00H byte at 4512H gets modified.
4512
(Self-modifying operand byte)
This byte is modified by code at 450BH, 451DH, 4535H, etc.
4513
JUMP relative to 4520H.
4515
LD (4201H),HL 22 01 42
Store Register Pair HL to memory location 4201H.
4518
LD (DE),A 12
Store Register A at the memory location pointed to by DE.
4519
LD L,E 6B
Copy E (the low byte of the keyboard row address) into L. HL now forms the complete keyboard row address for testing.
451A
LD H,B 60
Copy B (the current counter value) into H to form part of the result address.
451B
LD A,FFH 3E FF
Load Register A with FFH.
451D
LD (4512H),A 32 12 45
Store FFH at 4512H (modify the OR instruction operand at 4511H-4512H).
4520
PUSH HL E5
Save Register Pair HL onto the stack.
4521
GOSUB to ROM routine at 3024H, which jumps to 338EH for Keyboard Input.
4524
POP BC C1
Restore Register Pair BC from the stack (was HL when pushed).
4525
OR A B7
Test if Register A is zero (sets Z FLAG if A=0, clears CARRY).
4526
LD D,A 57
Copy the result in A into D for later comparison or storage.
4527
If the Z FLAG (Zero) has been set (A was zero), JUMP to 453DH.
4529
LD A,FFH 3E FF
Load Register A with FFH.
452A
(Self-modifying instruction byte)
This byte location is used by other code sections for self-modification.
452B
LD HL,(41FDH) 2A FD 41
Fetch 16-bit value from memory location 41FDH into HL.
452E
OR A B7
Test if Register A is zero (clears CARRY for the following SBC).
452F
SBC HL,BC ED 42
SUBtract with Carry: HL = HL - BC - CARRY. This compares HL against BC.
4531
If the Z FLAG (Zero) has been set (HL equals BC), JUMP to 4535H.
4533
LD A,00H 3E 00
Load Register A with 00H.
4535
LD (4512H),A 32 12 45
Store Register A at 4512H (modify the OR instruction operand).
4538
LD A,03H 3E 03
Load Register A with 03H.
453A
LD (452AH),A 32 2A 45
Store 03H at 452AH (self-modifying code).
453D
GOSUB to routine at 459DH.
4540
POP HL E1
Restore Register Pair HL from the stack (459CH address).
4541
LD (HL),00H 36 00
Store 00H at location 459CH (modify the self-modifying code back to NOP from RET).
4543
CP 1FH FE 1F
Compare Register A against 1FH. If Register A equals 1FH, the Z FLAG is set; otherwise the NZ FLAG is set.
4545
RET NZ C0
If the NZ FLAG (Not Zero) has been set (A does not equal 1FH), RETURN to caller.

4546H-4550H - Character Case Conversion Routines

These routines handle character processing, including lowercase to uppercase conversion for the Model III keyboard.

4546
XOR A AF
Set Register A to ZERO and clear all flags.
4547
RET C9
RETURN to caller with A=0.
4548
CP 61H FE 61
Compare Register A against 61H (ASCII 'a'). If Register A equals 61H, the Z FLAG is set. If Register A < 61H, the CARRY FLAG is set.
454A
RET C D8
If the CARRY FLAG has been set (character is less than 'a'), RETURN to caller without conversion.
454B
CP 7BH FE 7B
Compare Register A against 7BH (one past 'z'). If Register A >= 7BH, the NO CARRY FLAG is set.
454D
RET NC D0
If the NO CARRY FLAG has been set (character is greater than 'z'), RETURN to caller without conversion.
454E
SUB 20H D6 20
SUBtract 20H from Register A. This converts lowercase (61H-7AH) to uppercase (41H-5AH).
4550
RET C9
RETURN to caller with converted uppercase character in A.

4551H-459CH - Timer Interrupt Handler and Callback System

This section implements a timer callback system that manages periodic tasks. It maintains a linked list of timer callbacks with countdown values. Self-modifying code at 4511H, 4512H, 4583H, and 459CH is used throughout.

4551
GOSUB to address 0000H. This is self-modifying code - the address gets modified at runtime.
4554
LD HL,4216H 21 16 42
Point Register Pair HL to memory location 4216H.
4557
LD A,(HL) 7E
Fetch byte from memory location 4216H into Register A.
4558
CP 1EH FE 1E
Compare Register A against 1EH. If Register A equals 1EH, the Z FLAG is set; otherwise the NZ FLAG is set.
455A
If the NZ FLAG (Not Zero) has been set (value is not 1EH), JUMP to 4569H to skip the copy.
455C
LD (HL),1EH 36 1E
Store 1EH at the memory location pointed to by HL (4216H).
455E
LD HL,4217H 21 17 42
Point Register Pair HL to source address 4217H.
4561
LD DE,42CCH 11 CC 42
Point Register Pair DE to destination address 42CCH.
4564
LD BC,0006H 01 06 00
Load BC with 0006H (6 bytes to copy).
4567
LDIR ED B0
Load and Increment Repeat: Copy 6 bytes from (HL) to (DE), incrementing both pointers until BC reaches zero.
4569
XOR A AF
Set Register A to ZERO and clear all flags.
456A
LD HL,0000H 21 00 00
Point Register Pair HL to address 0000H. This is self-modifying code.
456B
(Self-modifying address bytes)
The address at 456AH-456BH gets modified by code at 4571H.
456D
LD DE,5555H 11 55 55
Load DE with 5555H (test pattern for address calculation).
4570
ADD HL,DE 19
ADD DE to HL. This calculates a new address.
4571
LD (456BH),HL 22 6B 45
Store Register Pair HL to memory location 456BH (modify the LD HL instruction at 456AH).
4574
ADC 01H CE 01
ADD with Carry 01H to Register A.
4576
LD B,A 47
Load Register B with A (loop counter).
4577
LD HL,441FH 21 1F 44
[OUTER LOOP START] Point Register Pair HL to memory location 441FH.
457A
INC (HL) 34
INCrement the byte at memory location 441FH by 1.
457B
LD HL,4512H 21 12 45
Point Register Pair HL to self-modifying code byte at 4512H.
457E
LD A,(HL) 7E
Fetch the self-modified byte from 4512H into Register A.
457F
OR A B7
Test if Register A is zero. If A=0, the Z FLAG is set.
4580
If the Z FLAG (Zero) has been set (byte is 00H), JUMP to 4583H to skip decrement.
4582
DEC (HL) 35
DECrement the byte at (HL) - this decrements the self-modifying code counter.
4583
LD HL,0000H 21 00 00
Point Register Pair HL to address 0000H. This is self-modifying code.
4584
(Self-modifying address bytes)
The address at 4583H-4584H gets modified to point to timer callback list.
4586
LD A,H 7C
[CALLBACK LOOP START] Load Register A with H (high byte of callback pointer).
4587
OR L B5
OR with L to check if HL is zero. If HL=0000H, the Z FLAG is set.
4588
If the Z FLAG (Zero) has been set (end of callback list), JUMP to 459AH to exit callback loop.
458A
LD E,(HL) 5E
Fetch low byte of next callback pointer from (HL) into E.
458B
INC HL 23
INCrement HL by 1 to point to high byte.
458C
LD D,(HL) 56
Fetch high byte of next callback pointer into D. DE now points to next callback.
458D
PUSH DE D5
Save Register Pair DE onto the stack (next callback pointer).
458E
INC HL 23
INCrement HL to point to callback function pointer.
458F
PUSH BC C5
Save Register Pair BC onto the stack (preserve loop counter).
4590
LD B,(HL) 46
Fetch countdown value from (HL) into B.
4591
INC HL 23
INCrement HL to point to tick counter.
4592
DEC (HL) 35
DECrement the tick counter at (HL). When it reaches zero, callback fires.
4593
If the Z FLAG (Zero) has been set (counter reached zero), GOSUB to 45D8H to execute callback.
4596
POP BC C1
Restore Register Pair BC from the stack.
4597
POP HL E1
Restore Register Pair HL from the stack (next callback pointer).
4598
JUMP back to 4586H to process next callback in the list.
459A
[OUTER LOOP END] DECrement B and loop back to 4577H if not zero. This repeats the callback processing.
459C
NOP 00
NO OPERATION. This byte is modified to C9H (RET) by code at 44FBH, creating self-modifying code.

459DH-45D0H - Keyboard Scan and Break Key Handler

This routine scans the Model III keyboard hardware at memory-mapped I/O addresses 3801H-3810H. It checks for BREAK key combinations and handles interrupt restoration.

459D
LD HL,4289H 21 89 42
Point Register Pair HL to system flags at 4289H.
45A0
LD A,(HL) 7E
Fetch system flags from 4289H into Register A.
45A1
AND 6CH E6 6C
Mask bits 6, 5, 3, and 2 (01101100 binary) to test specific system states.
45A3
If the NZ FLAG (Not Zero) has been set (any masked bit is set), JUMP to 45CFH to skip keyboard scan.
45A5
LD A,(3801H) 3A 01 38
Fetch keyboard row 0 from Model III keyboard hardware at 3801H (@ A B C D E F G keys). Bits are active LOW.
45A8
CP D0H FE D0
Compare Register A against D0H. This checks for specific key combination.
45AA
JUMP to 45B1H to continue keyboard processing.
45AC
LD A,E3H 3E E3
Load Register A with E3H (SVC function code).
45AE
LD C,04H 0E 04
Load Register C with 04H (parameter).
45B0
RST 28H EF
Call Supervisor Call dispatcher to execute function E3H.
45B1
LD A,(3810H) 3A 10 38
Fetch keyboard row 4 from Model III keyboard hardware at 3810H (0 1 2 3 4 5 6 7 keys).
45B4
CP 0EH FE 0E
Compare Register A against 0EH. This checks for BREAK key (bit 0 clear = pressed).
45B6
JUMP to 45CAH to continue processing.
45B8
LD A,(459CH) 3A 9C 45
Fetch byte from 459CH into Register A. This is the self-modifying code location.
45BB
SUB C9H D6 C9
SUBtract C9H from Register A. This tests if the byte is C9H (RET instruction).
45BD
If the Z FLAG (Zero) has been set (459CH contains C9H), JUMP to DOS Ready handler at 440DH.
45C0
POP AF F1
Restore Register Pair AF from the stack.
45C1
POP IY FD E1
Restore Index Register IY from the stack.
45C3
POP IX DD E1
Restore Index Register IX from the stack.
45C5
POP HL E1
Restore Register Pair HL from the stack.
45C6
POP DE D1
Restore Register Pair DE from the stack.
45C7
POP BC C1
Restore Register Pair BC from the stack.
45C8
JUMP to 45D5H to continue interrupt return.
45CA
LD A,(3802H) 3A 02 38
Fetch keyboard row 1 from Model III keyboard hardware at 3802H (H I J K L M N O keys).
45CD
CP 1CH FE 1C
Compare Register A against 1CH to check for additional key combination.
45CF
LD A,D 7A
Load Register A with D (restore value).
45D0
RET C9
RETURN to caller.

45D1H-45DAH - Interrupt Service Routine Entry

This is the interrupt service routine entry point, called by the hardware interrupt system. It executes system calls and manages interrupt context.

45D1
LD A,A5H 3E A5
Load Register A with A5H (SVC function code for interrupt processing).
45D3
RST 28H EF
Call Supervisor Call dispatcher to execute function A5H.
45D4
PUSH AF F5
Save Register Pair AF onto the stack.
45D5
LD A,27H 3E 27
Load Register A with 27H (SVC function code).
45D7
RST 28H EF
Call Supervisor Call dispatcher to execute function 27H.
45D8
LD (HL),B 70
Store Register B at the memory location pointed to by HL. This resets the timer countdown.
45D9
INC HL 23
INCrement HL by 1 to point to callback routine address.
45DA
JP (HL) E9
JUMP to the address contained in HL. This executes the timer callback routine.

45DBH-45F1H - FDC Command Setup Routines

These routines prepare FDC commands for read and write operations. The routines set up command bytes that will be sent to the WD1793 FDC via port F0H.

45DB
LD A,88H 3E 88
Load Register A with 88H - FDC Read Sector command with side compare enabled.
45DD
JUMP to 45EDH to continue FDC command setup.
45DF
PUSH HL E5
Save Register Pair HL onto the stack.
45E0
LD H,01H 26 01
Load Register H with 01H.
45E2
GOSUB to 45DBH to set up read command.
45E5
POP HL E1
Restore Register Pair HL from the stack.
45E6
RET C9
RETURN to caller.
45E7
LD A,A9H 3E A9
Load Register A with A9H - FDC Write Sector command with side compare enabled.
45E9
JUMP to 45EDH to continue FDC command setup.
45EB
LD A,A8H 3E A8
Load Register A with A8H - FDC Write Sector command.
45ED
LD (4687H),A 32 87 46
Store FDC command byte at 4687H for later use (self-modifying code address).
45F0
AND 20H E6 20
Mask bit 5 to test for write operation (bit 5 set in A8H/A9H write commands).

45F2H-4623H - Sector Read/Write Entry Point

Main entry point for disk sector read and write operations. This routine handles both read (88H) and write (A8H/A9H) commands, sets up the FDC, and manages the data transfer.

45F2
PUSH BC C5
Save Register Pair BC onto the stack.
45F3
LD BC,A202H 01 02 A2
Load BC with A202H - B=A2H (command setup), C=02H (sector size indicator). This is for read operations.
45F6
If the Z FLAG (Zero) has been set (bit 5 was clear, indicating read operation), JUMP to 45FDH.
45F8
LD BC,A340H 01 40 A3
Load BC with A340H - B=A3H (write command setup), C=40H (write size indicator). This is for write operations.
45FB
LD A,08H 3E 08
Load Register A with 08H (delay count for write operations).
45FD
LD (46E1H),A 32 E1 46
Store delay count at 46E1H (self-modifying code operand).
4600
LD A,C 79
Load Register A with C (sector size/operation indicator).
4601
GOSUB to 46ABH to set up self-modifying code for the operation.
4604
LD B,0AH 06 0A
Load Register B with 0AH (10 decimal - retry counter for disk operations).
4606
GOSUB to 4720H to perform the sector read/write operation.
4609
If the NZ FLAG (Not Zero) has been set (operation failed), JUMP to error handler at 4623H.
460B
PUSH BC C5
Save Register Pair BC onto the stack.
460C
PUSH DE D5
Save Register Pair DE onto the stack.
460D
PUSH HL E5
Save Register Pair HL onto the stack.
460E
LD A,(4284H) 3A 84 42
Fetch sectors per track value from 4284H into Register A.
4611
EX DE,HL EB
Exchange DE and HL registers.
4612
GOSUB to 4C59H for sector calculation (absolute sector to track/sector conversion).
4615
LD D,L 55
Load Register D with L (track number from calculation).
4616
LD E,A 5F
Load Register E with A (sector number from calculation).
4617
LD HL,4283H 21 83 42
Point Register Pair HL to maximum track number at 4283H.
461A
LD A,D 7A
Load Register A with D (calculated track number).
461B
CP (HL) BE
Compare Register A against (HL) - check if track number exceeds maximum. If A < (HL), CARRY is set.
461C
If the CARRY FLAG has been set (track is valid), JUMP to 4627H to continue.
461E
LD A,14H 3E 14
Load Register A with 14H (error code: invalid track - beyond disk capacity).
4620
POP HL E1
Restore Register Pair HL from the stack.
4621
POP DE D1
Restore Register Pair DE from the stack.
4622
POP BC C1
Restore Register Pair BC from the stack.
4623
OR A B7
Test if Register A is zero (sets flags for error code). If A=0, Z FLAG is set; if A≠0, NZ FLAG is set.
4624
JUMP to error return handler at 46E7H.

4627H-465CH - Drive Select and FDC Parameter Setup

This routine prepares the FDC for a sector operation by setting drive parameters, handling side selection for double-sided drives, and adjusting for density and precompensation.

4627
LD A,(4287H) 3A 87 42
Fetch drive flags from 4287H into Register A.
462A
LD B,A 47
Load Register B with A (preserve drive flags).
462B
BIT 0,B CB 40
Test bit 0 of B (double-sided drive flag).
462D
LD HL,427FH 21 7F 42
Point Register Pair HL to drive select register shadow at 427FH.
4630
If the Z FLAG (Zero) has been set (single-sided drive), JUMP to 4634H to skip side selection.
4632
SET 7,(HL) CB FE
Set bit 7 at (HL) - enable side 1 for double-sided drives.
4634
LD A,(4283H) 3A 83 42
Fetch maximum track number from 4283H into Register A.
4637
RRCA 0F
Rotate Register A Right Circular. Divides track count by 2 for double-sided calculation.
4638
CP D BA
Compare Register A against D (current track number). If A > D, CARRY is set.
4639
If the NO CARRY FLAG has been set (track is in upper half), JUMP to 463DH.
463B
SET 5,(HL) CB EE
Set bit 5 at (HL) - enable MFM (double density) mode.
463D
BIT 1,B CB 48
Test bit 1 of B (track offset flag).
463F
If the Z FLAG (Zero) has been set (no offset needed), JUMP to 4642H.
4641
INC D 14
INCrement D by 1 (adjust track number).
4642
PUSH DE D5
Save Register Pair DE onto the stack (track/sector numbers).
4643
BIT 2,B CB 50
Test bit 2 of B (double track flag for 80-track drives).
4645
If the Z FLAG (Zero) has been set (40-track mode), JUMP to 4649H.
4647
RLC D CB 02
Rotate D Left Circular through carry. Multiplies track number by 2 for 80-track drives.
4649
BIT 6,B CB 70
Test bit 6 of B (precompensation enable flag).
464B
If the Z FLAG (Zero) has been set (precompensation disabled), JUMP to 4656H.
464D
RRC C CB 09
Rotate C Right Circular through carry.
464F
LD A,E 7B
Load Register A with E (sector number).
4650
SUB C 91
SUBtract C from Register A. If result is negative, CARRY is set.
4651
If the CARRY FLAG has been set (sector too low for precomp), JUMP to 4656H.
4653
LD E,A 5F
Load Register E with A (adjusted sector number).
4654
SET 4,(HL) CB E6
Set bit 4 at (HL) - enable precompensation in drive select register.
4656
GOSUB to 4715H to select the drive.
4659
BIT 4,B CB 60
Test bit 4 of B (sector numbering flag).
465B
If the Z FLAG (Zero) has been set (0-based sectors), JUMP to 465EH.
465D
INC E 1C
INCrement E by 1 (convert to 1-based sector numbering).

465EH-469FH - FDC Sector Read Operation (Model III Port I/O)

This is the main FDC sector read routine using Model III port-based I/O. The WD1793 FDC is accessed via ports F0H-F4H. This routine sets up the FDC registers, issues the read command, and handles the high-speed data transfer from the FDC Data Register (port F3H).

465E
LD A,E 7B
Load Register A with E (sector number to read).
465F
OUT (F2H),A D3 F2
Send sector number to FDC Sector Register at port F2H.
4661
LD A,D 7A
Load Register A with D (track number).
4662
OUT (F3H),A D3 F3
Send track number to FDC Data Register at port F3H (used for verify).
4664
LD C,18H 0E 18
Load Register C with 18H - FDC Seek command with verify and 6ms step rate.
4666
GOSUB to 46F7H to issue seek command and wait for completion.
4669
POP AF F1
Restore Register Pair AF from the stack (contains track number for command).
466A
POP HL E1
Restore Register Pair HL from the stack (buffer address).
466B
PUSH HL E5
Save Register Pair HL onto the stack again.
466C
OUT (F1H),A D3 F1
Send track number to FDC Track Register at port F1H.
466E
PUSH DE D5
Save Register Pair DE onto the stack.
466F
DI F3
Disable Interrupts to prevent interference during high-speed FDC data transfer.
4670
IN A,(F0H) DB F0
Read FDC Status Register from port F0H into Register A to clear any pending status.
76543210
Not
Ready
Write
Protect
Head
Loaded
Seek
Error
CRC
Error
Track 00IndexBusy
4672
LD (46BAH),SP ED 73 BA 46
Store current Stack Pointer to memory location 46BAH for later restoration.
4676
LD DE,46B6H 11 B6 46
Point Register Pair DE to error handler address 46B6H.
4679
LD (404AH),DE ED 53 4A 40
Store error handler address at 404AH (interrupt vector location).
467D
LD A,C3H 3E C3
Load Register A with C3H (JP instruction opcode).
467F
LD (4049H),A 32 49 40
Store C3H at 4049H to create a JP instruction (self-modifying code for interrupt handling).
4682
LD A,C0H 3E C0
Load Register A with C0H - NMI mask value.
4684
OUT (E4H),A D3 E4
Send C0H to NMI Mask Register at port E4H to enable disk NMI.
4686
LD A,00H 3E 00
Load Register A with 00H. This is self-modifying code - the operand at 4687H will be changed to the actual FDC command.
4687
(Self-modifying FDC command byte)
This byte is modified by code at 45EDH to contain the actual FDC command (88H for read, A8H/A9H for write).
4688
OUT (F0H),A D3 F0
Send FDC command to Command Register at port F0H to initiate the read operation.
1793 FDC Command: 88H (10001000)Function Description
Bit 7Bit 6Bit 5Bit 4Bit 3Bit 2Bit 1Bit 0Summary
10001000Read Sector, Multiple records, Side compare enabled, 15ms settle delay
468A
GOSUB to 4789H to wait for FDC ready (busy flag clear).
468D
LD BC,00F3H 01 F3 00
Load BC with 00F3H - B=00H (256 bytes to read), C=F3H (FDC Data Register port).
468F
(Self-modifying byte count)
The byte at 468EH is modified by code at 46ABH for the actual transfer size.
4690
LD A,(427FH) 3A 7F 42
Fetch drive select register shadow from 427FH into Register A.
4693
OR 40H F6 40
OR with 40H to set bit 6 (motor on).
4695
LD E,A 5F
Load Register E with A (drive select value with motor on).
4696
LD D,02H 16 02
Load Register D with 02H - mask for DRQ bit (bit 1 of FDC status).

[HIGH-SPEED DATA READ LOOP] - This loop reads 256 bytes from the FDC at maximum Z80 speed. Interrupts are disabled to ensure timing.

4698
IN A,(F0H) DB F0
Read FDC Status Register from port F0H.
76543210
Not
Ready
0Record
Type
Record
Not Found
CRC
Error
Lost
Data
DRQBusy
469A
AND D A2
AND with D (02H) to test DRQ bit (Data Request - bit 1).
469B
If the Z FLAG (Zero) has been set (DRQ not ready), LOOP BACK to 4698H to wait for data ready.
469D
INI ED A2
Input from port C (F3H - FDC Data Register) to (HL), increment HL, decrement B. This reads one byte from the FDC.
469E
(Self-modifying byte)
Modified by code at 46AFH.
469F
DECrement B and loop back to 469FH if not zero. This is self-modifying code - the jump target changes.
46A1
DEC B 05
DECrement B by 1.
46A2
LD A,E 7B
Load Register A with E (drive select value).
46A3
OUT (F4H),A D3 F4
Send drive select byte to Drive Select Register at port F4H to maintain motor on during transfer.
46A5
(Self-modifying instruction)
Modified by code at 46B2H.
46A6
(Self-modifying byte)
Modified by code at 46B2H for loop control.
46A7
INI ED A2
Input from port C (F3H) to (HL), increment HL, decrement B.
46A9
If the NZ FLAG (Not Zero) has been set (more bytes to read), LOOP BACK to maintain drive select.
46AB
Infinite loop - execution should not reach here (will be interrupted or handled by NMI).

46ABH-46B5H - Self-Modifying Code Setup for FDC Transfer

This routine modifies the FDC transfer loop code to handle different sector sizes. It changes instruction opcodes and operands at runtime to optimize the data transfer loop.

46AB
LD (468FH),A 32 8F 46
Store Register A at 468FH (modify the LD BC instruction's B value for transfer size).
46AE
LD A,B 78
Copy B (the loop count originally passed to 46ABH) into A for patching into the self-modifying code locations.
46AF
LD (469EH),A 32 9E 46
Store Register A at 469EH (modify the DJNZ target address).
46B2
LD (46A6H),A 32 A6 46
Store Register A at 46A6H (modify another loop instruction).
46B5
RET C9
RETURN to caller with self-modifying code updated.

46B6H-46CFH - FDC Operation Completion and Error Handler

This is the FDC operation completion handler, reached when the data transfer finishes. It restores the system state, reads the final FDC status, and issues the Force Interrupt command to terminate the operation.

46B6
XOR A AF
Set Register A to ZERO and clear all flags.
46B7
OUT (E4H),A D3 E4
Send 00H to NMI Mask Register at port E4H to disable disk NMI.
46B9
LD SP,0000H 31 00 00
Load Stack Pointer with 0000H. This is self-modifying code - the address at 46BAH gets modified.
46BA
(Self-modifying SP address)
Modified by code at 4672H to contain the saved stack pointer value.
46BC
LD HL,45EDH 21 ED 45
Point Register Pair HL to address 45EDH (restore interrupt vector).
46BF
LD (4049H),HL 22 49 40
Store HL at 4049H-404AH to restore the interrupt handler address.
46C2
IN A,(F0H) DB F0
Read FDC Status Register from port F0H into Register A to get final operation status.
76543210
Not
Ready
Write
Protect
Record
Type
Record
Not Found
CRC
Error
Lost
Data
DRQBusy
46C4
LD B,A 47
Load Register B with A (save FDC status).
46C5
LD A,D0H 3E D0
Load Register A with D0H - Force Interrupt command.
46C7
OUT (F0H),A D3 F0
Send Force Interrupt command to FDC Command Register at port F0H to terminate any pending operation.
1793 FDC Command: D0H (11010000)Function Description
Bit 7Bit 6Bit 5Bit 4Bit 3Bit 2Bit 1Bit 0Summary
11010000Force Interrupt - Immediately terminate current command
46C9
POP AF F1
Restore Register Pair AF from the stack.
46CA
OUT (F1H),A D3 F1
Send track number to FDC Track Register at port F1H to update the controller's track position.
46CC
LD A,B 78
Load Register A with B (restore FDC status for checking).
46CD
POP HL E1
Restore Register Pair HL from the stack (buffer address).
46CE
POP DE D1
Restore Register Pair DE from the stack (track/sector).
46CF
POP BC C1
Restore Register Pair BC from the stack (retry counter).

46D0H-46E9H - FDC Status Analysis and Error Code Generation

This routine examines the FDC status byte returned from the operation and converts it into DOS error codes. It prioritizes critical errors and handles retry logic for transient errors.

46D0
EI FB
Enable Interrupts now that the critical FDC transfer is complete.
46D1
AND FCH E6 FC
Mask bits 7-2 (11111100 binary) to test for any error conditions in FDC status. Bits 1 (DRQ) and 0 (BUSY) are ignored.
46D3
If the Z FLAG (Zero) has been set (no error bits set), JUMP to success return at 46E7H.
46D5
LD C,A 4F
Load Register C with A (save error status).
46D6
AND 9CH E6 9C
Mask bits 7, 4, 3, and 2 (10011100 binary) to test for critical errors: Not Ready (7), Record Not Found (4), CRC Error (3), Lost Data (2).
46D8
If the Z FLAG (Zero) has been set (no critical errors), JUMP to 46E0H to handle write protect.
46DA
LD C,A 4F
Load Register C with A (update to critical error bits only).
46DB
ADD A,A 87
ADD A to itself (multiply by 2, shift left 1). This tests bit 7 (Not Ready) by moving it to CARRY.
46DC
If the Z FLAG (Zero) has been set (no Not Ready error after shift), JUMP to 46E0H.
46DE
DECrement B (retry counter) and jump to 46EAH if not zero to retry the operation.
46E0
LD A,00H 3E 00
Load Register A with 00H (initialize error code).

[ERROR CODE GENERATION LOOP] - Convert FDC status bits to sequential error code

46E1
(Self-modifying operand)
Modified by code at 45FDH for delay count.
46E2
INC A 3C
INCrement A by 1 (next error code number).
46E3
RRC C CB 09
Rotate C Right Circular through carry. Shifts next error bit into bit 0 and previous bit 0 into CARRY.
46E5
If the NO CARRY FLAG has been set (bit was 0), LOOP BACK to 46E2H to check next bit.

46E7H-46F6H - Return from FDC Operation

This routine handles the return from FDC operations, either with success (A=0) or with an error code in Register A.

46E7
EI FB
Enable Interrupts.
46E8
POP BC C1
Restore Register Pair BC from the stack.
46E9
RET C9
RETURN to caller with error code in A (0=success, non-zero=error).
46EA
BIT 4,C CB 61
Test bit 4 of C (Record Not Found error).
46EC
If the NZ FLAG (Not Zero) has been set (Record Not Found occurred), GOSUB to 46F2H to restore drive.
46EF
JUMP back to 460BH to retry the sector operation.

46F2H-470FH - Drive Restore and Command Routines

These routines handle drive restoration (recalibration to track 0) and FDC command execution with proper step rate and verify settings.

46F2
BIT 0,B CB 40
Test bit 0 of B (retry counter test).
46F4
RET Z C8
If the Z FLAG (Zero) has been set, RETURN to caller.
46F5
LD C,08H 0E 08
Load Register C with 08H - Restore command with verify.
46F7
LD A,(4282H) 3A 82 42
Fetch step rate configuration from 4282H into Register A.
46FA
AND 03H E6 03
Mask bits 0-1 to extract step rate (00=6ms, 01=12ms, 10=20ms, 11=30ms for WD1793).
46FC
OR C B1
OR with C to combine step rate with command code.
46FD
OUT (F0H),A D3 F0
Send combined command to FDC Command Register at port F0H.
1793 FDC Command: 08H-0BH (0000100x)Function Description
Bit 7Bit 6Bit 5Bit 4Bit 3Bit 2Bit 1Bit 0Summary
000010r1r0Restore to Track 0 with verify, step rate in bits 0-1
46FF
GOSUB to 4789H to wait for FDC operation completion.
4702
BIT 0,A CB 47
Test bit 0 of A (BUSY flag in status).
4704
RET Z C8
If the Z FLAG (Zero) has been set (not busy), RETURN to caller.
4705
RLCA 07
Rotate Register A Left Circular. Moves bit 7 into CARRY and bit 0.
4706
If the CARRY FLAG has been set (Not Ready error), JUMP to 470DH for force interrupt.
4708
GOSUB to 4715H to select drive.
470B
JUMP back to 46FFH to retry restore command.
470D
LD A,D0H 3E D0
Load Register A with D0H - Force Interrupt command.
470F
JUMP back to 46FDH to send Force Interrupt.

4711H-471AH - FDC Status Check and Drive Select

These short utility routines check FDC status and select the active drive by outputting to port F4H.

4711
IN A,(F0H) DB F0
Read FDC Status Register from port F0H into Register A.
76543210
Not
Ready
Write
Protect
Head
Loaded
Seek
Error
CRC
Error
Track 00IndexBusy
4713
RLCA 07
Rotate Register A Left Circular. Moves bit 7 (Not Ready) into CARRY and bit 0.
4714
RET C D8
If the CARRY FLAG has been set (drive not ready), RETURN to caller with error indication.
4715
LD A,(427FH) 3A 7F 42
Fetch drive select register shadow from 427FH into Register A.
4718
OUT (F4H),A D3 F4
Send drive select byte to Drive Select Register at port F4H. Bits 0-3 select the drive (0001, 0010, 0100, 1000), bit 5 enables MFM (double density), bit 6 enables precompensation.
471A
RET C9
RETURN to caller.

471BH-4788H - Drive Change and Initialization

This routine handles drive selection changes, saves and restores FDC track positions, copies drive parameter blocks, and performs drive initialization including restore operations. It manages the complex state associated with changing the active drive in a multi-drive system.

471B
LD A,(IX+06H) DD 7E 06
Fetch byte from IX+06H (FCB offset 6) into Register A.
471E
JUMP to 4723H to continue.
4720
LD A,(427EH) 3A 7E 42
Fetch current drive number from 427EH into Register A.
4723
PUSH HL E5
Save Register Pair HL onto the stack.
4724
PUSH DE D5
Save Register Pair DE onto the stack.
4725
PUSH BC C5
Save Register Pair BC onto the stack.
4726
CP 01H FE 01
Compare Register A against 01H. If A < 01H, CARRY is set; if A >= 01H, NO CARRY is set.
4728
LD C,A 4F
Load Register C with A (new drive number).
4729
LD A,20H 3E 20
Load Register A with 20H (error code: invalid drive number).
472B
If the NO CARRY FLAG has been set (drive number >= 1 is invalid for 0-based drives), JUMP to error return at 4784H.
472D
GOSUB to 470DH to send Force Interrupt to FDC.
4730
LD HL,427EH 21 7E 42
Point Register Pair HL to current drive number at 427EH.
4733
LD A,(HL) 7E
Fetch current drive number from 427EH into Register A.
4734
LD (HL),C 71
Store Register C (new drive number) at 427EH to update current drive.
4735
CP C B9
Compare Register A (old drive) against C (new drive). If equal, Z FLAG is set.
4736
PUSH AF F5
Save Register Pair AF onto the stack (preserves comparison result).
4737
ADD 76H C6 76
ADD 76H to Register A to calculate offset into drive parameter table (427EH + 76H = 42F4H base).
4739
LD L,A 6F
Load Register L with A (form pointer to old drive's saved track position).
473A
IN A,(F1H) DB F1
Read current FDC Track Register from port F1H into Register A to save old drive's track position.
473C
LD (HL),A 77
Store current track position at (HL) to save it for old drive.
473D
LD A,C 79
Load Register A with C (new drive number).
473E
ADD 76H C6 76
ADD 76H to Register A to calculate offset for new drive's saved position.
4740
LD L,A 6F
Load Register L with A (form pointer to new drive's saved track position).
4741
PUSH HL E5
Save Register Pair HL onto the stack (pointer to new drive's track).
4742
LD B,C 41
Load Register B with C (new drive number for bit calculation).
4743
LD A,80H 3E 80
Load Register A with 80H (10000000 binary - starting bit pattern).

[DRIVE SELECT BIT CALCULATION] - Convert drive number to bit position

4745
INC B 04
INCrement B by 1 (adjust for loop).
4746
RLCA 07
Rotate Register A Left Circular. Shifts bit pattern left.
4747
DECrement B and loop back to 4746H if not zero. This rotates the bit to the correct position for drive select.
4749
LD (427FH),A 32 7F 42
Store drive select bit pattern at 427FH (drive select register shadow).
474C
LD A,C 79
Load Register A with C (new drive number).
474D
RLCA 07
Rotate Register A Left Circular.
474E
RLCA 07
Rotate Register A Left Circular (multiply by 4).
474F
ADD A,C 81
ADD C to Register A (drive × 4 + drive = drive × 5).
4750
ADD A,A 87
ADD A to itself (drive × 10).
4751
ADD 91H C6 91
ADD 91H to Register A to calculate base address of drive parameter block (4291H base + drive × 10).
4753
LD L,A 6F
Load Register L with A (form pointer to drive parameters).
4754
LD (42B9H),HL 22 B9 42
Store Register Pair HL to 42B9H-42BAH (save pointer to parameter block).
4757
LD C,08H 0E 08
Load Register C with 08H (8 bytes to copy).
4759
LD DE,4280H 11 80 42
Point Register Pair DE to destination address 4280H (active drive parameters).
475C
LDIR ED B0
Load and Increment Repeat: Copy 8 bytes from (HL) to (DE) to load the new drive's parameters into active area.
475E
LD A,(4286H) 3A 86 42
Fetch drive parameter from 4286H into Register A.
4761
OUT (F2H),A D3 F2
Send parameter to FDC Sector Register at port F2H.
4763
GOSUB to 470DH to send Force Interrupt command.
4766
IN A,(F0H) DB F0
Read FDC Status Register from port F0H into Register A.
76543210
Not
Ready
Write
Protect
Head
Loaded
Seek
Error
CRC
Error
Track 00IndexBusy
4768
LD E,A 5F
Load Register E with A (save FDC status).
4769
POP BC C1
Restore Register Pair BC from the stack (was HL - pointer to saved track).
476A
LD A,(BC) 0A
Fetch saved track position for new drive from (BC) into Register A.
476B
OUT (F1H),A D3 F1
Send saved track position to FDC Track Register at port F1H to restore new drive's position.
476D
GOSUB to 4708H to reissue restore command.
4770
LD B,80H 06 80
Load Register B with 80H (delay count).
4772
BIT 7,E CB 7B
Test bit 7 of E (Not Ready status from earlier).
4774
If the NZ FLAG (Not Zero) has been set (drive was not ready), GOSUB to delay routine at 4C92H.
4777
POP AF F1
Restore Register Pair AF from the stack (drive comparison result).
4778
If the Z FLAG (Zero) has been set (same drive selected), JUMP to 4783H to skip additional delays.
477A
LD A,(4282H) 3A 82 42
Fetch step rate configuration from 4282H into Register A.
477D
RLCA 07
Rotate Register A Left Circular to test bit 7.
477E
LD B,0CH 06 0C
Load Register B with 0CH (12 decimal - delay count).
4780
If the CARRY FLAG has been set (bit 7 was set), GOSUB to delay routine at 4C92H for additional settling time.
4783
XOR A AF
Set Register A to ZERO and clear all flags (success return).
4784
POP BC C1
Restore Register Pair BC from the stack.
4785
POP DE D1
Restore Register Pair DE from the stack.
4786
POP HL E1
Restore Register Pair HL from the stack.
4787
OR A B7
Test if Register A is zero. Sets Z FLAG if A=0 (success), NZ FLAG if A≠0 (error).
4788
RET C9
RETURN to caller with status in A and flags.

4789H-4790H - FDC Wait for Ready (Busy Flag Clear)

This routine waits for the FDC to complete its current operation by polling the busy flag. It includes a small delay loop before checking status to allow the FDC time to begin processing.

4789
LD A,08H 3E 08
Load Register A with 08H (delay counter for 8 iterations).
478B
DEC A 3D
[DELAY LOOP] DECrement A by 1.
478C
If the NZ FLAG (Not Zero) has been set (counter not zero), LOOP BACK to 478BH. This creates a short delay before checking FDC status.
478E
IN A,(F0H) DB F0
Read FDC Status Register from port F0H into Register A after delay.
76543210
Not
Ready
Write
Protect
Head
Loaded
Seek
Error
CRC
Error
Track 00IndexBusy
4790
RET C9
RETURN to caller with FDC status in Register A.

4791H-47ACH - Wait for Index Pulse with Timeout

This routine waits for the disk's index pulse to be detected, indicating the disk is spinning. It polls the FDC status register watching for the index bit (bit 1) to change state. Returns error code 08H if timeout occurs.

4791
GOSUB to drive change routine at 4723H to ensure proper drive selected.
4794
RET NZ C0
If the NZ FLAG (Not Zero) has been set (drive change failed), RETURN to caller with error.
4795
PUSH HL E5
Save Register Pair HL onto the stack.
4796
PUSH DE D5
Save Register Pair DE onto the stack.
4797
PUSH BC C5
Save Register Pair BC onto the stack.
4798
LD DE,0000H 11 00 00
Initialize DE to 0000H as timeout counter (65536 iterations).
479B
IN A,(F0H) DB F0
Read FDC Status Register from port F0H to get initial status.
76543210
Not
Ready
Write
Protect
Head
Loaded
Seek
Error
CRC
Error
Track 00IndexBusy
479D
LD B,A 47
Load Register B with A (save initial status for comparison).
479E
IN A,(F0H) DB F0
[INDEX WAIT LOOP] Read current FDC status.
76543210
Not
Ready
Write
Protect
Head
Loaded
Seek
Error
CRC
Error
Track 00IndexBusy
47A0
XOR B A8
XOR Register A with B to detect any status bit changes between readings.
47A1
AND 02H E6 02
Mask bit 1 (INDEX bit). If bit changed, result is non-zero.
47A3
If the NZ FLAG (Not Zero) has been set (index pulse detected - bit 1 changed), JUMP to success return at 4783H.
47A5
DEC DE 1B
DECrement timeout counter DE by 1.
47A6
LD A,D 7A
Load Register A with D (high byte of counter).
47A7
OR E B3
OR with E to test if DE is zero. If DE=0000H, Z FLAG is set.
47A8
If the NZ FLAG (Not Zero) has been set (timeout not reached), LOOP BACK to 479EH to continue waiting.
47AA
LD A,08H 3E 08
Load Register A with 08H (error code: disk not spinning / timeout).
47AC
JUMP to error return at 4784H with error code in A.

47AEH-47FFH - File Open / Directory Search and FCB Setup

This is the main file open routine. It searches the directory for a matching filename, validates access permissions, sets up the FCB (File Control Block) with extent information, and prepares for file operations.

47AE
GOSUB to routine at 4925H to prepare for directory operations.
47B1
GOSUB to directory search routine at 495CH to find the file.
47B4
XOR A AF
Set Register A to ZERO and clear all flags.
47B5
LD (4860H),A 32 60 48
Store zero at 4860H (clear status flag).
47B8
GOSUB to drive setup routine at 471BH.
47BB
If the NZ FLAG (Not Zero) has been set (drive setup failed), JUMP to error handler at 4817H.
47BD
GOSUB to routine at 490DH.
47C0
EX DE,HL EB
Exchange DE and HL registers.
47C1
LD A,(IX+00H) DD 7E 00
Fetch FCB status byte from IX+00H into Register A.
47C4
RRCA 0F
Rotate Register A Right Circular. Tests bits by moving them to CARRY.
47C5
RRCA 0F
Rotate Register A Right Circular again.
47C6
If the CARRY FLAG has been set (bit was 1), JUMP to 47F8H.
47C8
RRCA 0F
Rotate Register A Right Circular once more.
47C9
LD A,2DH 3E 2D
Load Register A with 2DH (error code: file access denied or permission error).
47CB
If the CARRY FLAG has been set, JUMP to error handler at 4817H.
47CD
EX DE,HL EB
Exchange DE and HL registers.
47CE
GOSUB to routine at 4C57H.
47D1
LD C,0EH 0E 0E
Load Register C with 0EH (14 decimal - offset to extent table in FCB).
47D3
EX DE,HL EB
Exchange DE and HL registers.
47D4
PUSH IX DD E5
Save Index Register IX onto the stack.
47D6
POP HL E1
Restore into HL (copy IX value to HL).
47D7
ADD HL,BC 09
ADD BC to HL. HL now points to FCB extent table at IX+0EH.
47D8
PUSH AF F5
Save Register Pair AF onto the stack.
47D9
PUSH HL E5
Save Register Pair HL onto the stack (FCB extent pointer).
47DA
PUSH DE D5
Save Register Pair DE onto the stack.
47DB
LD A,08H 3E 08
Load Register A with 08H (8 extents maximum in FCB).
47DD
EX AF,AF' 08
Exchange AF with shadow register AF' to preserve extent counter.
47DE
LD A,(HL) 7E
[EXTENT SCAN LOOP] Fetch extent granule number from FCB extent table.
47DF
INC A 3C
INCrement A by 1. If A was FFH (empty extent), result is 00H and Z FLAG is set.
47E0
INC HL 23
INCrement HL by 1 to point to extent length byte.
47E1
If the Z FLAG (Zero) has been set (empty extent - was FFH), JUMP to 4837H to complete.
47E3
LD A,(HL) 7E
Fetch extent length from (HL).
47E4
GOSUB to extent processing routine at 488EH.
47E7
If the NO CARRY FLAG has been set, JUMP to 481AH.
47E9
ADD HL,BC 09
ADD BC to HL to advance to next extent entry.
47EA
EX DE,HL EB
Exchange DE and HL registers.
47EB
XOR (HL) AE
XOR Register A with (HL).
47EC
RLCA 07
Rotate Register A Left Circular.
47ED
RLCA 07
Rotate Register A Left Circular.
47EE
RLCA 07
Rotate Register A Left Circular. Three rotations left = multiply by 8.
47EF
ADD A,E 83
ADD E to Register A.
47F0
LD E,A 5F
Load Register E with A (updated offset).
47F1
POP AF F1
Restore Register Pair AF from the stack.
47F2
POP AF F1
Restore Register Pair AF from the stack again.
47F3
POP AF F1
Restore Register Pair AF from the stack third time.
47F4
DEC HL 2B
DECrement HL by 1.
47F5
GOSUB to routine at 4C21H.
47F8
LD L,(IX+03H) DD 6E 03
Fetch low byte of buffer address from IX+03H into L.
47FB
LD H,(IX+04H) DD 66 04
Fetch high byte of buffer address from IX+04H into H. HL now points to sector buffer.
47FE
XOR A AF
Set Register A to ZERO and clear all flags (success).
47FF
RET C9
RETURN to caller with A=0 (success).

4800H-488DH - Extent Management and Directory Update

This section handles extent table management during file operations. It processes extent entries, validates directory entries, updates extent information in the FCB, and manages file allocation.

4800
POP BC C1
Restore Register Pair BC from the stack.
4801
POP BC C1
Restore Register Pair BC from the stack again.
4802
POP HL E1
Restore Register Pair HL from the stack.
4803
SBC HL,DE ED 52
SUBtract with Carry: HL = HL - DE - CARRY.
4805
ADD HL,BC 09
ADD BC to HL.
4806
LD B,H 44
Load Register B with H.
4807
LD C,L 4D
Load Register C with L. BC now contains calculated value.
4808
PUSH AF F5
Save Register Pair AF onto the stack.
4809
GOSUB to directory validation routine at 48D4H.
480C
If the NZ FLAG (Not Zero) has been set (validation failed), JUMP to error handler at 4817H.
480E
LD A,FFH 3E FF
Load Register A with FFH (empty extent marker).
4810
INC HL 23
INCrement HL by 1.
4811
CP (HL) BE
Compare Register A against (HL). If equal, Z FLAG is set.
4812
DEC HL 2B
DECrement HL by 1.
4813
If the Z FLAG (Zero) has been set (extent is empty), JUMP to 4844H.
4815
LD A,2CH 3E 2C
Load Register A with 2CH (error code: directory full or allocation error).
4817
JUMP to error return handler at 4972H.
481A
EX DE,HL EB
Exchange DE and HL registers.
481B
INC HL 23
INCrement HL by 1.
481C
BIT 3,(IX+01H) DD CB 01 5E
Test bit 3 of FCB flags at IX+01H.
4820
SCF 37
Set Carry Flag.
4821
If the Z FLAG (Zero) has been set (bit 3 clear), JUMP back to 47C9H.
4823
EX AF,AF' 08
Exchange AF with shadow register AF' to check extent counter.
4824
CP 05H FE 05
Compare Register A against 05H (extent 5 check).
4826
If the NZ FLAG (Not Zero) has been set, JUMP to 4834H.
4828
LD C,(HL) 4E
Fetch low byte from (HL) into C.
4829
INC HL 23
INCrement HL by 1.
482A
LD B,(HL) 46
Fetch high byte from (HL) into B. BC now contains 16-bit value.
482B
INC HL 23
INCrement HL by 1.
482C
POP DE D1
Restore Register Pair DE from the stack.
482D
PUSH DE D5
Save Register Pair DE onto the stack again.
482E
EX DE,HL EB
Exchange DE and HL registers.
482F
SBC HL,BC ED 42
SUBtract with Carry: HL = HL - BC - CARRY.
4831
EX DE,HL EB
Exchange DE and HL registers back.
4832
If the CARRY FLAG has been set, JUMP to 4837H.
4834
DEC A 3D
DECrement extent counter A by 1.
4835
If the NZ FLAG (Not Zero) has been set (more extents to process), LOOP BACK to 47DDH.
4837
LD BC,0000H 01 00 00
Load BC with 0000H (no extent data).
483A
POP DE D1
Restore Register Pair DE from the stack.
483B
GOSUB to routine at 48F0H.
483E
If the NZ FLAG (Not Zero) has been set, JUMP back to 480CH.
4840
LD A,(IX+07H) DD 7E 07
Fetch FCB byte from IX+07H into Register A.
4843
PUSH AF F5
Save Register Pair AF onto the stack.
4844
POP AF F1
Restore Register Pair AF from the stack.
4845
LD (480FH),A 32 0F 48
Store Register A at 480FH (self-modifying code location).
4848
BIT 4,(HL) CB 66
Test bit 4 of (HL) - directory entry flags.
484A
If the Z FLAG (Zero) has been set (bit 4 clear), JUMP to error at 4815H.
484C
PUSH DE D5
Save Register Pair DE onto the stack.
484D
PUSH BC C5
Save Register Pair BC onto the stack.
484E
LD A,L 7D
Copy L (the low byte of the directory entry pointer) into A for offset calculation.
484F
ADD 16H C6 16
ADD 16H to Register A to calculate offset to extent area in directory entry.
4851
LD L,A 6F
Load Register L with A. HL now points to extent data in directory entry.
4852
PUSH HL E5
Save Register Pair HL onto the stack.
4853
LD A,(HL) 7E
[EXTENT COPY LOOP] Fetch extent granule number from directory entry.
4854
CP FEH FE FE
Compare Register A against FEH (reserved extent marker). If equal, Z FLAG is set; if less, CARRY is set.
4856
INC HL 23
INCrement HL by 1 to point to extent length.
4857
LD A,(HL) 7E
Fetch extent length from (HL).
4858
INC HL 23
INCrement HL by 1 to point to next extent.
4859
If the CARRY FLAG has been set (granule < FEH), JUMP to 486BH to process extent.
485B
If the Z FLAG (Zero) has been set (granule = FEH), JUMP to 4800H.
485D
LD A,3EH 3E 3E
Load Register A with 3EH (error code).
485F
JUMP to 4861H (next instruction - effectively fall through).
4861
POP AF F1
Restore Register Pair AF from the stack.
4862
LD A,64H 3E 64
Load Register A with 64H (SVC function code).
4864
GOSUB to routine at 4982H.
4867
POP BC C1
Restore Register Pair BC from the stack.
4868
POP DE D1
Restore Register Pair DE from the stack.
4869
JUMP back to 4848H to continue processing.
486B
BIT 4,L CB 65
Test bit 4 of L to check extent position.
486D
If the Z FLAG (Zero) has been set, JUMP to error at 4815H.
486F
GOSUB to extent calculation routine at 488EH.
4872
EX DE,HL EB
Exchange DE and HL registers.
4873
If the NO CARRY FLAG has been set, LOOP BACK to 4853H to get next extent.
4875
POP BC C1
Restore Register Pair BC from the stack.
4876
POP DE D1
Restore Register Pair DE from the stack.
4877
POP AF F1
Restore Register Pair AF from the stack.
4878
POP HL E1
Restore Register Pair HL from the stack.
4879
POP AF F1
Restore Register Pair AF from the stack again.
487A
PUSH BC C5
Save Register Pair BC onto the stack.
487B
LD BC,0008H 01 08 00
Load BC with 0008H (8 bytes - size of extent entry).
487E
LD A,D 7A
Copy D (the high byte of the extent address) into A for storing in the FCB extent fields.
487F
OR E B3
OR with E to test if DE is zero.
4880
If the Z FLAG (Zero) has been set (DE=0), JUMP to 4887H to skip extent storage.
4882
ADD HL,BC 09
ADD BC to HL to advance to next FCB extent entry.
4883
LD (HL),E 73
Store E (low byte of extent info) at (HL).
4884
INC HL 23
INCrement HL by 1.
4885
LD (HL),D 72
Store D (high byte of extent info) at (HL).
4886
INC HL 23
INCrement HL by 1.
4887
EX DE,HL EB
Exchange DE and HL registers.
4888
POP HL E1
Restore Register Pair HL from the stack.
4889
LDIR ED B0
Load and Increment Repeat: Copy 8 bytes from (HL) to (DE) to copy extent data into FCB.
488B
JUMP back to 47B8H to continue file open processing.

488EH-4897H - Extent Length Calculation

This small routine calculates sector counts from extent length values. It extracts the extent length (bits 0-4) and performs arithmetic to determine the number of sectors.

488E
AND 1FH E6 1F
Mask bits 0-4 to extract extent length (0-31 granules).
4890
LD C,A 4F
Load Register C with A (extent length).
4891
LD B,00H 06 00
Load Register B with 00H. BC now contains extent length.
4893
INC BC 03
INCrement BC by 1.
4894
EX DE,HL EB
Exchange DE and HL registers.
4895
SBC HL,BC ED 42
SUBtract with Carry: HL = HL - BC - CARRY. This tests if position is within extent.
4897
RET C9
RETURN to caller with CARRY set if position beyond extent, NO CARRY if within extent.

4898H-4924H - File I/O Support Routines

These routines support file read/write operations, including buffer management, directory updates, and position tracking within the FCB structure.

4898
LD A,(4302H) 3A 02 43
Fetch value from 4302H into Register A.
489B
LD HL,(42B9H) 2A B9 42
Fetch 16-bit pointer from 42B9H-42BAH into HL.
489E
LD (HL),A 77
Store Register A at the memory location pointed to by HL.
489F
LD A,(48D5H) 3A D5 48
Fetch byte from 48D5H into Register A.
48A2
GOSUB to routine at 4C19H.
48A5
GOSUB to FDC read setup routine at 45DBH.
48A8
If the NZ FLAG (Not Zero) has been set, JUMP to 48ACH.
48AA
OR 31H F6 31
OR Register A with 31H to set bits.
48AC
CP 06H FE 06
Compare Register A against 06H.
48AE
RET C9
RETURN to caller.
48AF
PUSH DE D5
Save Register Pair DE onto the stack.
48B0
PUSH BC C5
Save Register Pair BC onto the stack.
48B1
GOSUB to 48A2H.
48B4
If the Z FLAG (Zero) has been set, JUMP to cleanup at 48C1H.
48B6
LD DE,0000H 11 00 00
Load DE with 0000H.
48B9
GOSUB to FDC read setup at 45DBH.
48BC
If the Z FLAG (Zero) has been set, GOSUB to 4898H.
48BF
LD A,11H 3E 11
Load Register A with 11H (error code: read error).
48C1
POP BC C1
Restore Register Pair BC from the stack.
48C2
POP DE D1
Restore Register Pair DE from the stack.
48C3
RET C9
RETURN to caller.
48C4
LD A,(48D5H) 3A D5 48
Fetch byte from 48D5H into Register A.
48C7
PUSH DE D5
Save Register Pair DE onto the stack.
48C8
PUSH BC C5
Save Register Pair BC onto the stack.
48C9
GOSUB to routine at 4C19H.
48CC
OR H B4
OR the high byte of HL (directory entry pointer) into A to form the combined status byte for sector write.
48CD
GOSUB to routine at 4A5DH.
48D0
LD A,12H 3E 12
Load Register A with 12H (error code: write error).
48D2
JUMP to cleanup at 48C1H.
48D4
LD L,FFH 2E FF
Load Register L with FFH (marker value).
48D6
JUMP to 48DDH.
48D8
LD A,(IX+07H) DD 7E 07
Fetch FCB byte from IX+07H into Register A.
48DB
LD L,FFH 2E FF
Load Register L with FFH.
48DD
PUSH AF F5
Save Register Pair AF onto the stack.
48DE
AND 1FH E6 1F
Mask bits 0-4.
48E0
INC A 3C
INCrement A by 1.
48E1
INC A 3C
INCrement A by 1 again.
48E2
CP L BD
Compare Register A against L.
48E3
If the NZ FLAG (Not Zero) has been set, GOSUB to 48AFH.
48E6
POP HL E1
Restore into HL (was AF).
48E7
RET NZ C0
If the NZ FLAG (Not Zero) has been set, RETURN to caller.
48E8
LD A,E0H 3E E0
Load Register A with E0H (mask value).
48EA
AND H A4
AND with H.
48EB
LD L,A 6F
Load Register L with A.
48EC
LD H,43H 26 43
Load Register H with 43H. HL now points to address in 43XXH range.
48EE
CP A BF
Compare A with itself (always sets Z FLAG, clears CARRY).
48EF
RET C9
RETURN to caller with Z FLAG set.
48F0
GOSUB to drive change routine at 471BH.
48F3
If the Z FLAG (Zero) has been set, GOSUB to 48D8H.
48F6
RET NZ C0
If the NZ FLAG (Not Zero) has been set, RETURN to caller.
48F7
PUSH HL E5
Save Register Pair HL onto the stack.
48F8
ADD 16H C6 16
ADD 16H to Register A.
48FA
LD L,A 6F
Load Register L with A.
48FB
LD A,(IX+0EH) DD 7E 0E
Fetch first extent granule from IX+0EH into Register A.
48FE
CP (HL) BE
Compare Register A against (HL).
48FF
POP HL E1
Restore Register Pair HL from the stack.
4900
If the Z FLAG (Zero) has been set (extents match), JUMP to 4905H.
4902
INC A 3C
INCrement A by 1.
4903
If the NZ FLAG (Not Zero) has been set, JUMP to error at 490AH.
4905
LD A,(HL) 7E
Fetch byte from (HL) into Register A.
4906
AND 90H E6 90
Mask bits 7 and 4.
4908
CP 10H FE 10
Compare Register A against 10H.
490A
LD A,2DH 3E 2D
Load Register A with 2DH (error code: permission denied).
490C
RET C9
RETURN to caller.
490D
LD C,(IX+05H) DD 4E 05
Fetch FCB sector offset from IX+05H into C.
4910
LD L,(IX+0AH) DD 6E 0A
Fetch current record number low byte from IX+0AH into L.
4913
LD H,(IX+0BH) DD 66 0B
Fetch current record number high byte from IX+0BH into H.
4916
LD A,H 7C
Copy H (the high byte of the extent address from FCB+0AH/0BH) into A for comparison against FCB+0DH.
4917
CP (IX+0DH) DD BE 0D
Compare Register A against EOF position high byte at IX+0DH.
491A
RET NZ C0
If the NZ FLAG (Not Zero) has been set (not at EOF), RETURN to caller.
491B
LD A,L 7D
Copy L (the low byte of the extent address) into A for comparison against FCB+0CH.
491C
CP (IX+0CH) DD BE 0C
Compare Register A against EOF position low byte at IX+0CH.
491F
RET NZ C0
If the NZ FLAG (Not Zero) has been set (not at EOF), RETURN to caller.
4920
LD A,C 79
Load Register A with C (sector offset).
4921
CP (IX+08H) DD BE 08
Compare Register A against EOF sector offset at IX+08H. Sets Z FLAG if at exact EOF position.
4924
RET C9
RETURN to caller with Z FLAG set if at EOF, NZ FLAG if not.

4925H-4950H - Directory Search Setup and Stack Management

This routine sets up for directory search operations by examining file access flags and managing the call stack for proper return handling.

4925
LD A,(DE) 1A
Fetch byte from (DE) - file access mode byte.
4926
RLCA 07
Rotate Register A Left Circular. Moves bit 7 to bit 0 and CARRY.
4927
LD A,26H 3E 26
Load Register A with 26H (default error code).
4929
If the NO CARRY FLAG has been set (bit 7 was clear), JUMP to 492CH.
492B
XOR A AF
Set Register A to ZERO and clear all flags.
492C
EX AF,AF' 08
Exchange AF with shadow register AF' to save the flag value.
492D
EX (SP),HL E3
Exchange HL with top of stack (return address).
492E
LD (494BH),HL 22 4B 49
Store return address at 494BH for later use.
4931
POP HL E1
Restore Register Pair HL from the stack.
4932
PUSH IY FD E5
Save Index Register IY onto the stack.
4935
PUSH HL E5
Save Register Pair HL onto the stack.
4936
PUSH DE D5
Save Register Pair DE onto the stack.
4937
PUSH BC C5
Save Register Pair BC onto the stack.
4938
PUSH AF F5
Save Register Pair AF onto the stack.
4939
PUSH DE D5
Save Register Pair DE onto the stack again.
493A
EX (SP),IX DD E3
Exchange IX with top of stack. IX now points to FCB.
493C
PUSH HL E5
Save Register Pair HL onto the stack.
493D
LD HL,(4973H) 2A 73 49
Fetch saved stack pointer from 4973H into HL.
4940
EX (SP),HL E3
Exchange HL with top of stack.
4941
LD (4973H),SP ED 73 73 49
Store current Stack Pointer to 4973H for later restoration.

4944H-495BH - Directory Search Completion and Cleanup

This routine completes the directory search operation by restoring registers from the stack and calling any registered completion handlers.

4944
LD IY,4280H FD 21 80 42
Point Index Register IY to system parameters at 4280H.
4948
EX AF,AF' 08
Exchange AF with shadow register AF' to retrieve access mode flag.
4949
OR A B7
Test if Register A is zero (check access mode).
494A
CALL Z,0000H CC 00 00
If the Z FLAG (Zero) has been set, GOSUB to address 0000H. This is self-modifying code - address modified at runtime.
494B
(Self-modifying address)
The address at 494AH-494BH gets modified by code at 492EH to point to completion handler.
494D
POP HL E1
Restore Register Pair HL from the stack.
494E
LD (4973H),HL 22 73 49
Store HL at 4973H to save stack pointer.
4951
EX AF,AF' 08
Exchange AF with shadow register AF' to restore original AF.
4952
POP IX DD E1
Restore Index Register IX from the stack.
4954
POP AF F1
Restore Register Pair AF from the stack.
4955
POP BC C1
Restore Register Pair BC from the stack.
4956
POP DE D1
Restore Register Pair DE from the stack.
4957
POP HL E1
Restore Register Pair HL from the stack.
4958
POP IY FD E1
Restore Index Register IY from the stack.
495A
EX AF,AF' 08
Exchange AF with shadow register AF' one final time.
495B
RET C9
RETURN to caller with all registers restored.

495CH-4967H - File Access Mode Validation

This routine validates the file access mode stored in the FCB flags (IX+01H). Access modes 0-4 are valid; mode 5+ returns error 25H.

495C
LD B,05H 06 05
Load Register B with 05H (maximum valid access mode + 1).
495E
LD A,(IX+01H) DD 7E 01
Fetch FCB flags from IX+01H into Register A.
4961
AND 07H E6 07
Mask bits 0-2 to extract access mode (0-7).
4963
CP B B8
Compare Register A against B (5). If A < B, CARRY is set.
4964
RET C D8
If the CARRY FLAG has been set (valid mode 0-4), RETURN to caller with success.
4965
LD A,25H 3E 25
Load Register A with 25H (error code: invalid access mode).
4967
JUMP to error handler at 4972H.

4969H-4971H - End-of-File Check

This routine checks if the current file position is at or past the end-of-file marker. Returns appropriate error codes for EOF conditions.

4969
GOSUB to EOF detection routine at 490DH.
496C
RET C D8
If the CARRY FLAG has been set (not at EOF), RETURN to caller.
496D
LD A,1CH 3E 1C
Load Register A with 1CH (error code: EOF reached).
496F
If the Z FLAG (Zero) has been set (at exact EOF), JUMP to error handler at 4972H.
4971
INC A 3C
INCrement A by 1 to error code 1DH (past EOF).

4972H-4976H - Error Return Handler

Common error return point that restores the stack pointer and returns with error code in Register A and NZ FLAG set.

4972
LD SP,0000H 31 00 00
Load Stack Pointer with 0000H. This is self-modifying code - the address at 4973H gets modified.
4973
(Self-modifying SP value)
Modified by code at 4941H and 494EH to contain saved stack pointer.
4975
OR A B7
Test if Register A is zero. Sets NZ FLAG if error code present.
4976
JUMP back to 494DH to complete error cleanup.

4978H-4982H - System Call Interface

This routine provides the RST 28H supervisor call interface, extracting function codes and parameters from the stack.

4978
If the NZ FLAG (Not Zero) has been set, JUMP to handler at 4409H.
497B
XOR A AF
Set Register A to ZERO and clear all flags.
497C
If the NZ FLAG (Not Zero) has been set (impossible after XOR A), JUMP to 4972H. This appears to be dead code or safety check.
497E
EX (SP),HL E3
Exchange HL with top of stack (return address from RST 28H call).
497F
LD A,H 7C
Load Register A with H (high byte of return address - function code page).
4980
LD C,L 4D
Load Register C with L (low byte - function code).
4981
POP HL E1
Restore Register Pair HL from the stack.
4982
RST 28H EF
Call Supervisor Call dispatcher with function code in AC.

4983H-49A0H - String I/O with Length Limit

This routine handles string input/output operations with a length limit specified in IX+09H. It processes characters one at a time, calling a character handler routine.

4983
BIT 7,(IX+01H) DD CB 01 7E
Test bit 7 of FCB flags at IX+01H (string I/O mode flag).
4987
RET Z C8
If the Z FLAG (Zero) has been set (not string mode), RETURN to caller.
4988
POP DE D1
Restore Register Pair DE from the stack (discard return address).
4989
LD B,(IX+09H) DD 46 09
Fetch string length limit from IX+09H into B (loop counter).
498C
PUSH AF F5
[STRING LOOP START] Save Register Pair AF onto the stack.
498D
PUSH HL E5
Save Register Pair HL onto the stack.
498E
PUSH BC C5
Save Register Pair BC onto the stack.
498F
LD C,(HL) 4E
Fetch character from (HL) into C.
4990
GOSUB to character handler at 4A92H.
4993
If the NZ FLAG (Not Zero) has been set (error), JUMP to error handler at 4972H.
4995
LD E,A 5F
Load Register E with A (processed character).
4996
POP BC C1
Restore Register Pair BC from the stack.
4997
POP HL E1
Restore Register Pair HL from the stack.
4998
POP AF F1
Restore Register Pair AF from the stack.
4999
If the NO CARRY FLAG has been set (read mode), JUMP to 499CH to skip write.
499B
LD (HL),E 73
Store processed character E at (HL) (write mode).
499C
INC HL 23
INCrement HL by 1 to point to next character position.
499D
[STRING LOOP END] DECrement B and loop back to 498CH if not zero to process next character.
499F
XOR A AF
Set Register A to ZERO and clear all flags (success).
49A0
RET C9
RETURN to caller.

49A1H-49BDH - File Read Operation

Main file read routine. Reads data from the current file position, handles record advancement, and manages EOF conditions.

49A1
GOSUB to directory search setup at 4925H.
49A4
SCF 37
Set Carry Flag (indicates read operation to string handler).
49A5
GOSUB to string I/O routine at 4983H.
49A8
GOSUB to read sector routine at 49BEH.
49AB
If the Z FLAG (Zero) has been set (success), JUMP to record advance at 49B0H.
49AD
CP 06H FE 06
Compare Register A against 06H (specific error check).
49AF
RET NZ C0
If the NZ FLAG (Not Zero) has been set (not error 06H), RETURN to caller with error.
49B0
INC (IX+0AH) DD 34 0A
INCrement low byte of record number at IX+0AH.
49B3
If the NZ FLAG (Not Zero) has been set (no overflow), JUMP to 49B8H.
49B5
INC (IX+0BH) DD 34 0B
INCrement high byte of record number at IX+0BH (handle overflow from low byte).
49B8
SET 5,(IX+01H) DD CB 01 EE
Set bit 5 of FCB flags at IX+01H (mark record advanced).
49BC
OR A B7
Test if Register A is zero. Sets Z FLAG for success.
49BD
RET C9
RETURN to caller.

49BEH-49D6H - Sector Read Routine

Performs the actual sector read operation by validating access mode, checking EOF, and calling the low-level FDC read routine.

49BE
LD B,06H 06 06
Load Register B with 06H (mode 6 - special read mode check).
49C0
GOSUB to access mode validation at 495EH.
49C3
GOSUB to EOF check at 4969H.
49C6
LD A,B6H 3E B6
Load Register A with B6H (parameter for read).
49C8
GOSUB to routine at 47B5H.
49CB
GOSUB to FDC read setup at 45DBH to perform actual sector read.
49CE
RES 5,(IX+01H) DD CB 01 AE
Clear bit 5 of FCB flags at IX+01H.
49D2
RES 4,(IX+01H) DD CB 01 A6
Clear bit 4 of FCB flags at IX+01H.
49D6
RET C9
RETURN to caller.

49D7H–49DAH — File Write with EOF Update (Entry Point)

This is the entry point for file write operations that should update the EOF marker when writing past the current end-of-file. Loads A with F6H (11110110b) as the AND mask for FCB flag testing, then jumps to the common write code at 49DDH. The F6H mask preserves bit 4 (modified/dirty flag) during the flag test at 4A30H.

49D7
LD A,F6H 3E F6
Load A with F6H (11110110b). This mask value will be stored at the self-modifying location 4A30H. When ANDed with FCB flags, it preserves bit 4 (dirty flag), allowing EOF updates.
49D9
JUMP to common write code at 49DDH, skipping the alternate entry point at 49DBH.

49DBH–4A06H — File Write without EOF Update / Common Write Handler

Entry point 49DBH is for file write operations that should NOT update the EOF marker (used for random-access overwrites within existing file bounds). Loads A with E6H (11100110b) which clears bit 4 during the flag test. The common code at 49DDH handles the actual write operation: stores the mode flag, calls directory setup, performs the sector write, updates FCB position pointers, and optionally updates EOF markers based on the mask value.

[ALTERNATE ENTRY - Write without EOF update]

49DB
LD A,E6H 3E E6
Load A with E6H (11100110b). This mask clears bit 4 (dirty flag) during the AND at 4A30H, preventing EOF marker updates for random-access writes within existing file bounds.

[COMMON WRITE CODE]

49DD
LD (4A30H),A 32 30 4A
Store the mode flag (F6H or E6H) at 4A30H. This is self-modifying code: the byte at 4A30H is the operand of the AND instruction at 4A2FH, controlling which FCB flag bits are preserved during the write completion check.
49E0
GOSUB to directory search setup at 4925H. Prepares the FCB (IX) and directory context for the write operation.
49E3
GOSUB to string/record I/O handler at 4983H. This performs the actual data transfer to the sector buffer.
49E6
GOSUB to sector write routine at 4A0CH. Flushes the modified sector buffer to disk.
49E9
RET NZ C0
If NZ FLAG (write error occurred), RETURN immediately with error code in A.

[CHECK IF AT SECTOR BOUNDARY]

49EA
LD A,(IX+05H) DD 7E 05
Fetch current byte offset within sector from FCB+05H into A. Value 00H means we just completed writing the last byte of a sector.
49ED
OR A B7
Test if A = 0 (at sector boundary).
49EE
If Z FLAG (at sector boundary), GOSUB to 49B0H to advance the record/sector counters to the next sector.

[CHECK IF PAST EOF - UPDATE EOF MARKERS IF NEEDED]

49F1
GOSUB to EOF comparison at 490DH. Compares current position (in C, HL) against EOF markers (in FCB+08H, FCB+0CH/0DH). Sets CARRY if current position > EOF.
49F4
If NO CARRY (current position ≤ EOF), jump to 49FCH to update EOF markers unconditionally.
49F6
BIT 6,(IX+01H) DD CB 01 76
Test bit 6 of FCB+01H (alternate position valid flag). If set, the write was within existing file bounds.
49FA
If NZ FLAG (bit 6 set — within existing bounds), skip EOF update and jump to success return at 4A05H.

[UPDATE EOF MARKERS]

49FC
LD (IX+08H),C DD 71 08
Store C (current byte offset within sector) at FCB+08H (EOF byte offset).
49FF
LD (IX+0CH),L DD 75 0C
Store L (low byte of current record number) at FCB+0CH (EOF record low).
4A02
LD (IX+0DH),H DD 74 0D
Store H (high byte of current record number) at FCB+0DH (EOF record high).

[SUCCESS RETURN]

4A05
XOR A AF
Set A = 0 and clear all flags. A = 0 indicates successful write completion.
4A06
RET C9
RETURN to caller with A = 0 (success).

4A07H-4A3BH - Directory Update and Write Sector

This routine handles directory updates when files are modified, ensuring extent information is written back to disk and managing the "dirty" flags in the FCB.

4A07
BIT 4,(IX+01H) DD CB 01 66
Test bit 4 of FCB flags at IX+01H (directory dirty flag).
4A0B
RET Z C8
If the Z FLAG (Zero) has been set (directory not dirty), RETURN to caller.
4A0C
GOSUB to routine at 47B1H.
4A0F
BIT 5,(IX+02H) DD CB 02 6E
Test bit 5 of extended FCB flags at IX+02H.
4A13
If the NZ FLAG (Not Zero) has been set, JUMP to 4A2DH.
4A15
BIT 1,(IX+00H) DD CB 00 4E
Test bit 1 of FCB status at IX+00H.
4A19
If the NZ FLAG (Not Zero) has been set, JUMP to 4A2DH.
4A1B
PUSH HL E5
Save Register Pair HL onto the stack.
4A1C
GOSUB to directory validation routine at 48F0H.
4A1F
If the NZ FLAG (Not Zero) has been set, JUMP to 4A24H.
4A21
INC HL 23
INCrement HL by 1 to point to directory entry flags.
4A22
SET 5,(HL) CB EE
Set bit 5 at (HL) - mark directory entry as modified.
4A24
If the Z FLAG (Zero) has been set, GOSUB to write routine at 48C4H.
4A27
POP HL E1
Restore Register Pair HL from the stack.
4A28
RET NZ C0
If the NZ FLAG (Not Zero) has been set (error), RETURN to caller.
4A29
SET 5,(IX+02H) DD CB 02 EE
Set bit 5 of extended FCB flags at IX+02H.
4A2D
LD A,(IX+01H) DD 7E 01
Fetch FCB flags from IX+01H into Register A.
4A30
AND 90H E6 90
Mask bits 7 and 4. This is self-modifying code - the operand gets modified.
4A32
BIT 0,(IX+00H) DD CB 00 46
Test bit 0 of FCB status at IX+00H.
4A36
GOSUB to write sector routine at 4A5DH.
4A39
RET NZ C0
If the NZ FLAG (Not Zero) has been set (error), RETURN to caller.
4A3A
XOR A AF
Set Register A to ZERO and clear all flags.
4A3B
JUMP back to 49D2H to clear flags.

4A5DH-4A7FH - Physical Sector Write with Retry

This routine performs the actual physical sector write to disk with retry logic. It handles both read-modify-write operations and direct writes, with up to 3 retry attempts on error.

4A5D
LD C,A 4F
Load Register C with A (save flags).
4A5E
LD B,03H 06 03
Load Register B with 03H (3 retry attempts).
4A60
If the NZ FLAG (Not Zero) has been set, JUMP to write path at 4A6FH.
4A62
[READ-MODIFY-WRITE LOOP] GOSUB to FDC write command setup at 45EBH.
4A65
RET NZ C0
If the NZ FLAG (Not Zero) has been set (write setup failed), RETURN to caller with error.
4A66
LD A,C 79
Load Register A with C (restore flags).
4A67
OR A B7
Test if Register A is zero.
4A68
If the NZ FLAG (Not Zero) has been set, GOSUB to read sector first at 45DFH (read-modify-write).
4A6B
RET Z C8
If the Z FLAG (Zero) has been set (read succeeded), RETURN to caller.
4A6C
DECrement retry counter B and loop back to 4A62H if not zero.
4A6E
RET C9
RETURN to caller (all retries exhausted).
4A6F
[DIRECT WRITE LOOP] GOSUB to FDC write command setup at 45E7H.
4A72
RET NZ C0
If the NZ FLAG (Not Zero) has been set (write setup failed), RETURN to caller with error.
4A73
LD A,C 79
Load Register A with C (restore flags).
4A74
OR A B7
Test if Register A is zero.
4A75
RET Z C8
If the Z FLAG (Zero) has been set (no verify needed), RETURN to caller.
4A76
GOSUB to read sector for verify at 45DFH.
4A79
GOSUB to verify routine at 48A8H.
4A7C
RET Z C8
If the Z FLAG (Zero) has been set (verify succeeded), RETURN to caller.
4A7D
DECrement retry counter B and loop back to 4A6FH if not zero.
4A7F
RET C9
RETURN to caller (all retries exhausted).

4A80H-4AAFH - Device Driver Chain and Character I/O

This is the main device driver entry point for character I/O operations. It handles device selection, string mode operations, and manages the buffer pointer for sequential I/O.

4A80
LD HL,0000H 21 00 00
Point Register Pair HL to address 0000H (null device or start of chain).
4A83
LD A,(DE) 1A
Fetch byte from (DE) - device type or command byte.
4A84
CP C0H FE C0
Compare Register A against C0H (special device marker).
4A86
If the Z FLAG (Zero) has been set (special device), JUMP to handler at 4AE5H.
4A88
GOSUB to directory search setup at 4925H.
4A8B
SET 7,(IX+01H) DD CB 01 FE
Set bit 7 of FCB flags at IX+01H (enable string mode).
4A8F
LD A,B 78
Copy B (the operation mode byte from the caller) into A for comparison at the following instruction.
4A90
CP 02H FE 02
Compare Register A against 02H.
4A92
BIT 5,(IX+01H) DD CB 01 6E
Test bit 5 of FCB flags at IX+01H.
4A96
If the NO CARRY FLAG has been set, JUMP to 4A3DH.
4A98
If the NZ FLAG (Not Zero) has been set, GOSUB to read sector at 49BEH.
4A9B
RET NZ C0
If the NZ FLAG (Not Zero) has been set (error), RETURN to caller.
4A9C
GOSUB to EOF check at 4969H.
4A9F
GOSUB to buffer pointer calculation at 4AB0H.
4AA2
If the NZ FLAG (Not Zero) has been set, JUMP to 4AADH.
4AA4
PUSH HL E5
Save Register Pair HL onto the stack.
4AA5
GOSUB to directory update at 4A07H.
4AA8
POP HL E1
Restore Register Pair HL from the stack.
4AA9
RET NZ C0
If the NZ FLAG (Not Zero) has been set (error), RETURN to caller.
4AAA
GOSUB to record advance at 49B0H.
4AAD
LD A,(HL) 7E
Fetch character from buffer at (HL) into Register A.
4AAE
CP A BF
Compare A with itself (always sets Z FLAG, clears CARRY).
4AAF
RET C9
RETURN to caller with character in A.

4AB0H-4ABBH - Buffer Pointer Calculation

Calculates the address in the sector buffer corresponding to the current sector offset within the file.

4AB0
GOSUB to get buffer base address at 47F8H.
4AB3
LD E,(IX+05H) DD 5E 05
Fetch sector offset from IX+05H into E.
4AB6
LD D,A 57
Load Register D with A (usually 00H).
4AB7
ADD HL,DE 19
ADD DE to HL. HL now points to current position in buffer.
4AB8
INC (IX+05H) DD 34 05
INCrement sector offset at IX+05H by 1.
4ABB
RET C9
RETURN to caller with buffer pointer in HL.

4ABCH–4AE8H — Device Driver Chain Walk

Context: HL points to the current node in the linked device-driver chain. Each node is a small record whose fields (relative to the base of the node) are:
  +0 low byte of “match” value
  +1 high byte of “match” value
  +2 flag/mask byte
  +3 reserved
  +4 reserved
  +5 low byte of address of next node (or 00H for end of chain)
  +6 high byte of address of next node
DE holds the 16-bit key being searched for; B holds a flag mask. The loop walks every node until either a match is found or the chain pointer is 0000H.

4ABC
LD A,(HL) 7E
Fetch the low byte of the node’s match value from (HL) into A. HL currently points to offset +0 of the current chain node.
4ABD
CP E BB
Compare A (node’s match-low) against E (low byte of the search key held in DE). Sets CARRY if A < E, sets Z if they are equal.
4ABE
INC HL 23
Advance HL to offset +1 — the high byte of the node’s match value. INC HL does not alter the flags set by CP E, so the branch below can still use them.
4ABF
If the NZ FLAG is set (low bytes do not match), skip the high-byte comparison and jump to 4AC3H to advance HL past the remaining fields of this node. No point comparing high bytes when low bytes already differ.
4AC1
LD A,(HL) 7E
Low bytes matched. Fetch the high byte of the node’s match value from (HL) into A.
4AC2
CP D BA
Compare A (node match-high) against D (high byte of the search key in DE). Z FLAG set means both bytes match — we found the right node.
4AC3
INC HL 23
Advance HL to offset +2 — the flags/mask byte of the node. HL is now at the correct position whether we arrived from the NZ branch above (low mismatch) or fell through from the CP D (full comparison done).
4AC4
If NZ FLAG is set (either byte of the key did not match this node), jump to 4AE0H to skip this node’s dispatch and walk to the next node pointer. Only a full 16-bit key match allows execution to fall through.

[MATCH FOUND — DISPATCH NODE] Both bytes of the search key equal the node’s match value. HL is at offset +2 (flags byte).

4AC6
PUSH HL E5
Save HL (pointer to flags byte of matched node) onto the stack so it can be restored after the dispatch call returns.
4AC7
PUSH DE D5
Save DE (the 16-bit search key) onto the stack.
4AC8
PUSH BC C5
Save BC (B = flag mask, C = varies) onto the stack.
4AC9
LD A,(HL) 7E
Load A with the flags byte at offset +2 of the matched node (HL still points there).
4ACA
AND B A0
AND the node’s flags byte (in A) with the caller-supplied flag mask in B. If the result is zero, the caller does not want this node’s handler invoked for this particular request.
4ACB
INC HL 23
Advance HL to offset +3 (first reserved byte).
4ACC
INC HL 23
Advance HL to offset +4 (second reserved byte).
4ACD
INC HL 23
Advance HL to offset +5 — the low byte of the dispatch (handler) address.
4ACE
LD E,(HL) 5E
Load E with the low byte of the handler’s address from offset +5.
4ACF
INC HL 23
Advance HL to offset +6 — the high byte of the handler address.
4AD0
LD D,(HL) 56
Load D with the high byte of the handler’s address from offset +6. DE now holds the complete 16-bit address of the device handler for this node.
4AD1
PUSH DE D5
Push the handler address DE onto the stack so it can be transferred to IX.
4AD2
POP IX DD E1
Pop the handler address into IX. IX now points to the device handler routine that will service this request.
4AD4
CALL 068BH CD 8B 06
Call the device handler indirectly: address 068BH is in ROM and contains a JP (IX) trampoline (or equivalent), so execution jumps to the handler whose address was just loaded into IX. The handler may read or write a character, open a device, etc., and returns with status in A and flags.
4AD7
POP BC C1
Restore BC (B = flag mask, C = caller’s parameter) from the stack.
4AD8
POP DE D1
Restore DE (the original search key) from the stack.
4AD9
POP HL E1
Restore HL (pointer to flags byte of the matched node) from the stack.
4ADA
BIT 0,B CB 40
Test bit 0 of B (the flag mask supplied by the caller). Bit 0 = 1 means “keep walking the chain even after a successful dispatch”; bit 0 = 0 means “stop here.”
4ADC
If Z FLAG is set (bit 0 of B = 0 — stop-after-first-match mode), jump to 4AE0H to follow the chain pointer. The return value from the handler (in A and flags) will then be forwarded to the caller.
4ADE
OR A B7
Test the handler’s return code in A. Sets Z if A = 0 (handler succeeded), sets NZ if A ≠ 0 (handler returned an error).
4ADF
RET NZ C0
If NZ FLAG is set (handler returned an error code), return immediately to the caller, propagating the error in A. Do not continue walking the chain.

[ADVANCE TO NEXT NODE] Either the key did not match, or the match was successful and the chain-walk bit tells us to continue.

4AE0
INC HL 23
Advance HL. Depending on the path taken to reach here, HL is either at offset +2 (flags, arrived via NZ branch at 4AC4H) or offset +5/+6 after the dispatch block. This and the two loads below collectively read the forward-pointer to the next node (a 16-bit address stored little-endian).
4AE1
LD A,(HL) 7E
Load A with the low byte of the next-node pointer from (HL).
4AE2
INC HL 23
Advance HL to the byte that holds the high byte of the next-node pointer.
4AE3
LD H,(HL) 66
Load H with the high byte of the next-node pointer from (HL). Note: L is loaded after H would change the pointer; A still holds the low byte from 4AE1H.
4AE4
LD L,A 6F
Load L with A (the low byte fetched two instructions ago). HL now holds the address of the next node in the chain.

[LOOP TEST — END OF CHAIN?]

4AE5
LD A,H 7C
Load A with H (high byte of the new HL = potential next-node address).
4AE6
OR L B5
OR A with L to test whether HL = 0000H (the null / end-of-chain sentinel). If HL ≠ 0, Z FLAG is clear and the loop continues; if HL = 0000H, Z FLAG is set.
4AE7
If NZ FLAG is set (next-node pointer ≠ 0000H), jump back to 4ABCH to compare the new node’s match value. This is the main loop back-edge.
4AE8
JP 4C9DH C3 9D 4C
HL = 0000H: end of chain reached with no further matches. Jump to 4C9DH (the “end of chain” handler) which returns the result to the original caller.

4AECH–4B1FH — DOS File-Position SVC Entry Points

Five small supervisor-call handlers reached via the SVC dispatcher (RST 28H). Each sets up register values for a particular file-positioning or inquiry function then falls into the common “apply position” code at 4B25H or 4B35H. On entry IX points to the caller’s FCB; DE points to the caller’s parameter block.

[SVC: Set position to beginning-of-file / rewind]

4AEC
CALL 4925H CD 25 49
Standard SVC prologue: save all caller registers, validate FCB pointer in IX, set up IY = 4280H (system parameters), and enable interrupts.
4AEF
Jump to the position-apply block at 4B25H with no pre-loaded HL or C values: HL will be read from the FCB’s current-record field there. Effectively this requests a “rewind to start” by setting the “position valid” bit without changing the record number.

[SVC: Return current file position into caller’s buffer]

4AF1
CALL 4925H CD 25 49
Standard SVC prologue (save registers, validate FCB, IY = 4280H, EI).
4AF4
LD H,A 67
A = 0 after CALL 4925H’s setup. Load H = 0.
4AF5
LD L,A 6F
Load L = 0. HL = 0000H, signalling “return current position, do not change it.”
4AF6
LD C,A 4F
Load C = 0 (sector offset = 0).
4AF7
Jump to 4B35H to write the current record number (HL=0 taken as “use current”) and sector offset (C=0) back to the caller’s FCB, then return to the caller.

[SVC: Set position to end-of-file]

4AF9
CALL 4925H CD 25 49
Standard SVC prologue.
4AFC
LD C,(IX+08H) DD 4E 08
Load C with the EOF sector-offset byte from FCB offset +08H. This is the byte position within the last sector that contains valid data.
4AFF
LD L,(IX+0CH) DD 6E 0C
Load L with the EOF record-number low byte from FCB offset +0CH.
4B02
LD H,(IX+0DH) DD 66 0D
Load H with the EOF record-number high byte from FCB offset +0DH. HL now holds the 16-bit EOF record number; C holds the EOF sector offset. Together they form the exact EOF position.
4B05
Jump to 4B29H to apply this EOF position to the FCB’s current-record and sector-offset fields.

[SVC: Seek by relative byte count]

4B07
CALL 4925H CD 25 49
Standard SVC prologue.
4B0A
CALL 490DH CD 0D 49
Compute the current position into HL (record number) and C (sector offset) by calling the position-fetch routine at 490DH.
4B0D
XOR A AF
Clear A to zero, also clears CARRY for the subtraction below.
4B0E
SUB (IX+09H) DD 96 09
A = 0 − (IX+09H). FCB offset +09H holds the relative seek delta (a signed byte supplied by the caller: positive = seek forward, negative when sign-extended = seek backward). Result in A is the negated delta.
4B11
ADD A,C 81
A = (negated delta) + C. C holds the current sector offset. The sum gives the new sector offset after applying the seek delta.
4B12
LD C,A 4F
Store the new sector offset back into C.
4B13
If CARRY is set (the addition overflowed, meaning the new offset is beyond one sector boundary), jump to 4B25H which will adjust the record number accordingly.
4B15
DEC HL 2B
Decrement HL (record number) by 1 to compensate for the sector boundary wrap introduced by the seek.
4B16
Jump to 4B25H to apply the adjusted HL record number and C sector offset.

[SVC: Seek to absolute record/sector position]

4B18
CALL 4925H CD 25 49
Standard SVC prologue.
4B1B
LD H,B 60
Load H with B. B holds the high byte of the desired absolute record number supplied by the caller in BC.
4B1C
LD L,C 69
Load L with C. HL now holds the complete 16-bit absolute record number.
4B1D
LD A,(IX+09H) DD 7E 09
Load A with the desired sector offset from FCB offset +09H. The caller stores the target byte-within-sector here before issuing the SVC.
4B20
OR A B7
Test if A (desired sector offset) is zero. Sets Z if the seek is to the start of a sector.
4B21
LD C,A 4F
Copy the sector offset into C regardless (C = desired byte position within the target sector).
4B22
CALL NZ,4C42H C4 42 4C
If NZ FLAG (sector offset ≠ 0), call the 16×16 multiply utility at 4C42H with HL = record number and A = sector offset to compute the byte address within the file for range-checking.

4B25H–4B4AH — Apply File Position to FCB

Common path reached by all the seek SVCs above. On entry: HL = target record number; C = target sector offset within that record; IX = FCB pointer. The routine sets the “position valid” bit in the FCB, compares the target position against the current position, and if they differ it flushes any dirty sector buffer before updating the FCB fields.

4B25
SET 6,(IX+01H) DD CB 01 F6
Set bit 6 of the FCB flags byte at IX+01H. Bit 6 = “position is valid / file has been seeked.” From this point on read and write operations use HL as the current record number.
4B29
LD A,H 7C
Load A with H (high byte of the target record number in HL).
4B2A
CP (IX+0BH) DD BE 0B
Compare A (target record high) against FCB offset +0BH (current record number high byte). Z FLAG set if they are equal.
4B2D
If NZ FLAG (high bytes differ), the position is definitely changing — jump to 4B35H to flush and update without checking the low byte.
4B2F
LD A,L 7D
Load A with L (low byte of the target record number). High bytes matched, so check the low bytes.
4B30
CP (IX+0AH) DD BE 0A
Compare A (target record low) against FCB offset +0AH (current record number low byte).
4B33
If Z FLAG (both bytes match — target = current record), jump to 4B46H to update only the sector offset (C) in the FCB without flushing the buffer, since the sector buffer already holds the correct sector.

[POSITION CHANGE — FLUSH & UPDATE] The target record number differs from the current record. Must flush any buffered write before moving to the new sector.

4B35
PUSH HL E5
Save HL (target record number) so it survives the directory-flush call.
4B36
PUSH BC C5
Save BC (B = flag mask; C = target sector offset).
4B37
CALL 4A07H CD 07 4A
Call the directory-update routine at 4A07H. If the FCB “dirty” bit is set, this writes the current sector buffer back to disk before allowing the seek to proceed.
4B3A
POP BC C1
Restore BC (B = flag mask; C = target sector offset).
4B3B
POP HL E1
Restore HL (target record number).
4B3C
RET NZ C0
If NZ FLAG (directory flush returned an error code in A), return immediately to propagate the error to the caller. Do not update the FCB with a bad position.
4B3D
CALL 49B8H CD B8 49
Call the “mark position advanced” helper at 49B8H which sets bit 5 of the FCB flags (IX+01H), indicating that the next I/O operation must read the new sector from disk before proceeding.
4B40
LD (IX+0AH),L DD 75 0A
Store L (new current-record-number low byte) into FCB offset +0AH.
4B43
LD (IX+0BH),H DD 74 0B
Store H (new current-record-number high byte) into FCB offset +0BH.
4B46
LD (IX+05H),C DD 71 05
Store C (new sector offset within the current record) into FCB offset +05H. This is the byte position within the 256-byte sector buffer.
4B49
XOR A AF
Set A = 0 (success return code) and clear all flags.
4B4A
RET C9
Return to the SVC epilogue (in 4925H / 494DH) with A = 0 (no error).

4B4BH–4B65H — String-Output Helpers (via ROM CALL 1BH)

Two short routines that print a NUL/CR-terminated string by calling the Model III ROM character-output routine at 001BH, which prints the character in A to the current cursor position on the video display.

4B4B
PUSH DE D5
Save DE (caller’s parameter or address) onto the stack.
4B4C
LD DE,401DH 11 1D 40
Load DE with 401DH — the address of the DOS “error message table” or the start of the string that this variant of the helper is designed to output. DE is the source pointer passed to the loop.
4B4F
PUSH HL E5
Save HL (the string pointer already in HL when the routine is entered) so it can be restored after printing.

[STRING OUTPUT LOOP] HL points to the current character in the string.

4B50
LD A,(HL) 7E
Load A with the next character from the string pointed to by HL.
4B51
CP 03H FE 03
Compare A against 03H (ETX / end-of-string sentinel used by NEWDOS/80).
4B53
If Z FLAG (character = 03H), string is finished — jump to 4B5EH to restore registers and return.
4B55
CALL 001BH CD 1B 00
Call the Model III ROM routine at 001BH with A = character to display. ROM 001BH outputs A to the screen at the current cursor position and advances the cursor. This is the standard NEWDOS/80 method for console output.
4B58
LD A,(HL) 7E
Reload A with the same character (CALL 001BH may have altered A).
4B59
CP 0DH FE 0D
Compare A against 0DH (carriage return). Some strings are CR-terminated rather than ETX-terminated.
4B5B
INC HL 23
Advance HL to the next character in the string.
4B5C
If NZ FLAG (character was not a CR), loop back to 4B50H to print the next character.
4B5E
POP HL E1
Restore HL from the stack.
4B5F
POP DE D1
Restore DE from the stack.
4B60
RET C9
Return to the caller.

[SECOND ENTRY POINT — alternate string base address]

4B61
PUSH DE D5
Save DE onto the stack (same as the first entry point).
4B62
LD DE,4025H 11 25 40
Load DE with 4025H — the alternate DOS string table base address (used for a different set of system messages).
4B65
Jump into the shared loop body at 4B4FH to print from HL using DE as the base for any address offsets.

4B67H–4BCCH — Supervisor-Call (RST 28H) Dispatcher

Entry point for all RST 28H supervisor calls. When code executes RST 28H the CPU pushes PC and jumps to 0028H; from there a JP at 0028H leads here. The byte immediately following RST 28H in the caller’s code is the function code. The dispatcher pops this code from the stack, looks it up in a jump table, and invokes the corresponding handler.

4B67
INC SP 33
Increment SP by 1. The top of stack holds the return address = address of the function-code byte inside the caller’s code stream. INC SP skips the low byte of the return address (not used directly; the full address is derived below).
4B68
INC SP 33
Increment SP again, effectively discarding the two-byte return address from the stack. The actual return to the caller is done at 4BBEH by using the address saved in (4BC3H).
4B69
CP 20H FE 20
Compare A (the function code, which was loaded into A before RST 28H was called) against 20H. Function codes below 20H are “fast” built-in handlers mapped directly via a short jump table.
4B6B
JP C,4478H DA 78 44
If CARRY (function code < 20H), jump to the fast-dispatch table at 4478H which handles the low-numbered built-in SVCs (console I/O, disk primitives, etc.).
4B6E
PUSH HL E5
Save HL onto the stack; HL will be used as a work register in this routine.
4B6F
PUSH DE D5
Save DE onto the stack.
4B70
PUSH BC C5
Save BC onto the stack.
4B71
PUSH AF F5
Save AF (the function code in A) onto the stack for later restoration.
4B72
LD HL,4289H 21 89 42
Load HL with 4289H — the address of the system-state flags byte.
4B75
SET 6,(HL) CB F6
Set bit 6 of the system-state byte at 4289H. Bit 6 = “inside SVC dispatcher.” This prevents nested SVC invocations from re-entering critical sections and is cleared at 4BC9H on exit.
4B77
LD HL,459CH 21 9C 45
Load HL with 459CH — the address of the self-modifying NOP/RET byte in the keyboard/interrupt handler.
4B7A
LD (HL),00H 36 00
Write 00H (NOP opcode) to 459CH. This re-enables the interrupt-driven keyboard scan that may have been suppressed (RET = C9H) during a previous critical section. See the self-modifying code discussion at 459CH.
4B7C
EI FB
Enable interrupts. The SVC handler runs with interrupts enabled so that disk and keyboard interrupts continue to service in the background.
4B7D
AND 1FH E6 1F
Mask bits 0–4 of A (the SVC function code) to extract the function table index (0–31). Bits 5–7 are attribute flags that control the dispatcher’s behaviour, not part of the table index.
4B7F
LD HL,4288H 21 88 42
Load HL with 4288H — address of the last-dispatched SVC index byte (used for re-entrancy checking and error reporting).
4B82
CP (HL) BE
Compare A (new SVC index) against (HL) (previous SVC index stored at 4288H). Z FLAG set if this is a re-entrant call to the same SVC.
4B83
If Z FLAG (same SVC re-entered), jump to 4BBEH to restore registers and return immediately, preventing infinite re-entry loops.
4B85
LD (HL),A 77
Store the new SVC index into 4288H, recording which SVC is now active.
4B86
AND 07H E6 07
Mask bits 0–2 to get the driver-chain selector (0–7). Each value selects a different linked list of device handlers to walk.
4B88
LD C,A 4F
Store the driver-chain selector into C for use in the chain-walk below.
4B89
XOR A AF
Clear A = 0.
4B8A
LD (44CAH),A 32 CA 44
Store 00H into the SVC result/status staging byte at 44CAH, clearing any previous result before dispatching.
4B8D
CALL 4723H CD 23 47
Call the drive-change/initialization routine at 4723H to ensure the correct drive is selected before invoking any handler that might need disk access.
4B90
LD A,(HL) 7E
HL still points to 4288H (last-SVC index). Load A = current SVC index.
4B91
SUB C 91
A = SVC index − chain selector. The difference selects which segment of the SVC table to use.
4B92
RLCA 07
Rotate A left through carry (multiply by 2, first rotation).
4B93
RLCA 07
Rotate A left again (×4 total after two rotations). Each SVC table entry is 4 bytes wide, so multiplying the index by 4 gives the byte offset into the table.
4B94
ADD A,C 81
Add C (chain selector) back into A to get the final table byte offset.
4B95
CALL 48DBH CD DB 48
Call the directory-buffer-management helper at 48DBH with A = table offset. This validates that the target handler is available in the current context and sets up internal pointers.
4B98
If NZ FLAG (handler not found or not valid for current state), jump to 4BB3H to report error 2EH.
4B9A
BIT 6,(HL) CB 76
HL now points into the SVC table entry. Test bit 6 of the entry’s flag byte. Bit 6 = 1 means “this SVC has an associated device-driver record that must be located first.”
4B9C
If Z FLAG (bit 6 = 0 — no driver record needed), jump to 4BB3H to handle the error / alternate path.
4B9E
ADD 16H C6 16
Add 16H (22 decimal) to A. A held the table byte offset; adding 16H gives the offset into the SVC table entry where the driver record’s address (a 16-bit pointer) is stored.
4BA0
LD L,A 6F
Set L = A. H was already set to the SVC table page, so HL now points to the driver-record address inside the table entry.
4BA1
LD E,(HL) 5E
Load E with the low byte of the driver-record address from the table.
4BA2
INC HL 23
Advance HL to the high byte of the driver-record address.
4BA3
LD D,(HL) 56
Load D with the high byte of the driver-record address. DE now holds the address of the driver record to use.
4BA4
LD (44CEH),DE ED 53 CE 44
Store DE (driver-record address) into the current-driver-record pointer at 44CEH–44CFH for use by lower-level I/O routines during this SVC.
4BA8
LD HL,44C0H 21 C0 44
Load HL with 44C0H — the base address of the FCB template / work buffer used when constructing a temporary FCB for this SVC.
4BAB
CALL 4BCDH CD CD 4B
Call the filename-parse routine at 4BCDH with HL = FCB work buffer, DE = driver record (source of filename). Returns HL = pointer to filled-in FCB and Z if parsing succeeded.
4BAE
LD (4BC3H),HL 22 C3 4B
Store HL (pointer to the parsed FCB) at 4BC3H. This address is used at 4BC2H as the CALL target for the actual SVC handler.
4BB1
If Z FLAG (filename parse succeeded), jump to 4BBEH to restore registers and dispatch to the SVC handler.

[ERROR PATH — handler not found or parse failed]

4BB3
LD A,(4288H) 3A 88 42
Load A with the current SVC index from 4288H (for inclusion in the error message).
4BB6
CP 06H FE 06
Compare A against 06H. SVC codes ≥6 are “user-defined”; if this was a user SVC that could not be dispatched, the error handling differs.
4BB8
LD A,2EH 3E 2E
Load A = 2EH — error code for “unknown or unimplemented SVC.”
4BBA
JP NZ,4409H C2 09 44
If NZ FLAG (SVC index ≠ 06H — was a system SVC, not user), jump to the DOS fatal-error handler at 4409H with error code 2EH in A.
4BBD
HALT 76
HALT the CPU. If a user SVC dispatcher returns with error and A = 06H, execution halts here as a last-resort debug stop — this should never occur in normal operation.

[SVC EPILOGUE — RESTORE & DISPATCH]

4BBE
POP AF F1
Restore AF (the original function code and flags saved at 4B71H).
4BBF
POP BC C1
Restore BC.
4BC0
POP DE D1
Restore DE.
4BC1
POP HL E1
Restore HL.
4BC2
CALL 0000H CD 00 00
This is self-modifying code: the address at 4BC3H–4BC4H was overwritten at 4BAEH with the FCB pointer, so this CALL actually jumps to the SVC handler routine whose address was stored there. The CALL pushes the return address (4BC5H) and transfers control to the handler.
4BC3
(self-modifying target address)
Modified at 4BAEH to contain the address of the specific SVC handler. At cold-boot this holds 0000H; it is always overwritten before the CALL.
4BC5
PUSH HL E5
Handler has returned. Save HL (the handler’s return value, often a buffer pointer or status) for safe-keeping while cleaning up SVC state.
4BC6
LD HL,4289H 21 89 42
Load HL = 4289H (address of the system-state flags byte).
4BC9
RES 6,(HL) CB B6
Clear bit 6 of the system-state byte at 4289H, marking the SVC dispatcher as no longer active. This mirrors the SET 6 done at 4B75H.
4BCB
POP HL E1
Restore HL (the handler’s return value / buffer pointer).
4BCC
RET C9
Return to the original caller of RST 28H with A = result code, HL = result pointer, and flags reflecting the result.

4BCDH–4C09H — Filename Parse / FCB Build from Driver Record

Reads successive bytes from a driver record (via the lazy-read helper at 4C0AH) and constructs an FCB (File Control Block) in the buffer pointed to by HL. The driver record is stored with a length prefix followed by the filename bytes. Returns Z on success with HL pointing to the completed FCB; returns error code in A on failure (C flag set).

4BCD
LD (4C0FH),HL 22 0F 4C
Save HL (base address of the FCB work buffer) into the pointer at 4C0FH. The lazy-read helper at 4C0AH uses this address to know where to fetch the next driver-record byte from disk if the in-memory cache is exhausted.
4BD0
LD DE,43FFH 11 FF 43
Load DE = 43FFH. DE is the running pointer into the driver record’s data area; the helper at 4C0AH increments E before each read, so the first byte it fetches will be from 4400H (first byte past 43FFH).
4BD3
CALL 4C0AH CD 0A 4C
Call the lazy-read helper at 4C0AH. DE is incremented then the byte at (DE) is returned in A. If (DE) = 0 (end of in-memory data), 4C0AH reads the next sector from disk and restores DE before returning. On return A = first driver-record byte (the filename-length prefix).
4BD6
LD C,A 4F
Store the filename-length prefix in C for use as a loop counter below.
4BD7
LD A,1FH 3E 1F
Load A = 1FH (31 decimal — maximum permitted filename length in NEWDOS/80).
4BD9
CP C B9
Compare A (max length 1FH) against C (actual length prefix). CARRY is set if A < C, i.e., if the filename is longer than allowed.
4BDA
LD A,22H 3E 22
Pre-load A = 22H — error code for “filename too long.”
4BDC
RET C D8
If CARRY is set (filename too long), return immediately with A = 22H.
4BDD
CALL 4C0AH CD 0A 4C
Read the next driver-record byte into A. This is the drive specifier byte (drive number 0–3 or FFH = current drive).
4BE0
LD B,A 47
Store the drive specifier in B.
4BE1
CALL 4C0AH CD 0A 4C
Read the next byte into A. This is the file-type / extension byte.
4BE4
LD L,A 6F
Store the file-type byte in L. H was set below; together HL will form a pointer after the next instruction fills H.
4BE5
LD H,C 61
Load H = C (the filename-length prefix read at 4BD6H). HL = (filename_length << 8) | file_type — a composite descriptor used by the FCB-builder to set the correct fields.
4BE6
DEC C 0D
Decrement C (filename-length counter). This is the first of two tests that classify the record type: C = 0 originally means “no filename bytes follow” (special device); C = 1 originally means “single-byte special name.”
4BE7
If Z FLAG (original length was exactly 1 — single-char name), jump to 4BF2H to handle the one-character-name path.
4BE9
DEC C 0D
Decrement C again. Now C = original_length − 2.
4BEA
If NZ FLAG (C ≠ 0, i.e., original length > 2), jump to 4C06H for the multi-character filename path.

[TWO-CHARACTER FILENAME PATH] Original length = 2. Read one more byte into the FCB name field.

4BEC
CALL 4C0AH CD 0A 4C
Read the next driver-record byte into A — the second (and final) filename character for a 2-byte name.
4BEF
LD H,A 67
Store this filename character in H.
4BF0
XOR A AF
Set A = 0 and clear all flags. Z FLAG = success.
4BF1
RET C9
Return with A = 0 (success), Z FLAG set, H = second filename character, B = drive, L = file type.

[ONE-CHARACTER FILENAME PATH] Original length = 1.

4BF2
CALL 4C0AH CD 0A 4C
Read the single filename byte into A.
4BF5
LD H,A 67
Store the filename byte in H.
4BF6
DEC B 05
Decrement B (the drive specifier) by 1. B was set at 4BE0H from the drive byte in the driver record.
4BF7
DEC B 05
Decrement B again. After two decrements B is used as a flag to decide the next action in the comparison below.
4BF8
CALL 4C0AH CD 0A 4C
Read the next byte from the driver record into A. For 1-char names this byte is the second field of the FCB name (or an attribute byte).
4BFB
LD (HL),A 77
Store A into the FCB work buffer at (HL). HL still points into the 44C0H buffer area.
4BFC
CP (HL) BE
Compare A against the byte just stored (i.e., A vs itself) to set Z FLAG. This sets Z unconditionally, which will be used for branching.
4BFD
INC HL 23
Advance HL to the next FCB field byte.
4BFE
Z FLAG is set (from CP (HL) above — always true). Jump to 4C06H to continue reading remaining FCB fields into the buffer.
4C00
LD A,24H 3E 24
Pre-load A = 24H (error code for malformed driver record). This path is reached when the comparison above unexpectedly fails.
4C02
INC C 0C
Increment C (loop counter).
4C03
DEC C 0D
Decrement C immediately. This INC/DEC pair is used to set or clear the Z FLAG without changing C’s value — equivalent to OR C but preserving CARRY.
4C04
If Z FLAG (C = 0 — no more bytes to read), jump to 4C68H to terminate the parse with the result already in A.

[MULTI-BYTE COPY LOOP] C holds remaining byte count. Reads bytes from the driver record into (HL) until C reaches zero.

4C06
DJNZ 4BF8H 10 F0
DECrement B and jump back to 4BF8H if B ≠ 0. B was decremented twice above (at 4BF6H–4BF7H), so this loop reads (original_B − 2) more bytes from the driver record into successive FCB buffer locations via the LD (HL),A / INC HL sequence.
4C08
When B reaches zero, jump back to 4BD3H to read the next record from the driver data and continue filling the FCB buffer — handles multi-sector driver records.

4C0AH–4C18H — Lazy Driver-Record Byte Reader

Helper called by 4BCDH. DE is a running pointer into the driver-record data already in the 4300H–43FFH page buffer. On entry the routine increments E, then fetches the byte at (DE). If the byte is non-zero it returns it in A immediately. If it is zero (meaning the current 256-byte page is exhausted) it calls the file-read SVC at 4436H to load the next page, resets DE to 4300H, and then returns the refreshed byte.

4C0A
INC E 1C
Increment E, advancing the low byte of the page pointer DE. Since D holds 43H the pointer wraps within the 4300H–43FFH page.
4C0B
LD A,(DE) 1A
Load A with the byte at (DE) — the next data byte from the driver record page currently in memory.
4C0C
RET NZ C0
If NZ FLAG (byte ≠ 00H — valid data byte), return immediately with A = the data byte. The fast path for non-zero bytes takes only 3 instructions.
4C0D
PUSH DE D5
Save DE (current page pointer) because the SVC call below may alter it.
4C0E
LD DE,0000H 11 00 00
Load DE = 0000H. This is the self-modifying address operand; the two bytes at 4C0FH–4C10H are overwritten by the store at 4BCDH with the FCB work-buffer address (44C0H), so after the write 4C0EH effectively becomes LD DE,44C0H — pointing DE at the FCB buffer to use as the parameter for the read SVC.
4C11
CALL 4436H CD 36 44
Call the file-operation jump-table entry at 4436H (Read Next Sector SVC). DE points to the FCB; this reads the next 256-byte sector of the driver record into the 4300H page buffer and updates the FCB’s record counter.
4C14
POP DE D1
Restore DE. E is now the old (exhausted) page offset; D is still 43H.
4C15
If NZ FLAG (SVC returned error), jump to 4C40H to propagate the error.
4C17
LD A,(DE) 1A
Read the first byte of the freshly-loaded page at (DE) = 4300H into A. DE was not reset here; the first valid byte is the one at the current (just-incremented) E offset into the new page.
4C18
RET C9
Return to the caller (4BD3H / 4BDDH etc.) with A = the next driver byte.

4C0AH–4C18H — Lazy Driver-Record Byte Reader

Converts an FCB granule reference (from the extent table) into the physical disk parameters needed by the FDC: track, sector, and byte offset. Entry: A = granule index; IY = 4280H (system parameter block). Returns HL = pointer into the 4300H sector buffer for this granule’s data; DE = drive/physical-sector address pair. The sub-routines at 4C37H, 4C39H, and 4C42H are factored integer-multiply helpers.

4C19
LD (48D5H),A 32 D5 48
Store A (the granule index) at 48D5H for use by the write-sector verification logic later in the I/O pipeline.
4C1C
LD E,00H 1E 00
Clear E = 0 (will form the low byte of a 16-bit intermediate result).
4C1E
LD HL,(42B9H) 2A B9 42
Load HL with the current drive parameter block pointer from 42B9H. HL now points to the start of the active drive’s parameter block (containing sectors-per-granule, granules-per-track, etc.).
4C21
LD C,A 4F
Copy A (granule index) into C as a working copy for the arithmetic below.
4C22
LD L,(HL) 6E
Load L with the byte at (HL) — the granules-per-track value from the drive parameter block (typically 2 for NEWDOS/80 SS/SD, 6 for DS/DD). H is unchanged so HL is now <H_of_parameter_block>:<granules_per_track>.
4C23
LD A,(4285H) 3A 85 42
Load A with the sectors-per-granule value from system variable 4285H (e.g., 5 for SS/SD, 3 for DS/DD).
4C26
CALL 4C37H CD 37 4C
Call the 8×8-bit multiply helper at 4C37H with A = sectors-per-granule and L = granules-per-track. Returns HL = product = sectors-per-track.
4C29
LD B,A 47
Save A (sectors-per-granule, preserved from before the CALL) into B.
4C2A
LD D,A 57
Also copy sectors-per-granule into D (used as an operand below).
4C2B
ADD HL,DE 19
HL (sectors-per-track) += DE (0:sectors-per-granule). This adjusts sectors-per-track for a 1-based sector numbering scheme if needed.
4C2C
LD A,05H 3E 05
Load A = 05H (used as the divisor in the division: 5 = sectors per granule boundary for certain disk formats).
4C2E
CALL 4C39H CD 39 4C
Call the 16÷8-bit divide helper at 4C39H with HL = dividend (adjusted sectors), A = divisor (5). Returns HL = quotient = granule’s starting track number; remainder (sector within track) left in B/C.
4C31
ADD HL,BC 09
HL (track number) += BC (sector offset within track). HL now holds the absolute physical track number on the disk.
4C32
EX DE,HL EB
Swap HL and DE. DE = absolute track number; HL = sectors-per-granule (the old DE value). Setting DE = track number prepares it for the FDC seek.
4C33
LD HL,4300H 21 00 43
Load HL = 4300H — the start address of the disk sector data buffer in the 4300H page. All sector reads/writes use this 256-byte buffer.
4C36
RET C9
Return with HL = 4300H (sector buffer base) and DE = physical track/sector address for the FDC routines.

4C37H–4C56H — Integer Multiply/Divide Utilities

Three small arithmetic helpers used by the granule converter and elsewhere.
4C37H: 8×8 multiply returning 16-bit product.
4C39H: 16÷8 divide returning quotient and remainder (wraps 4C42H).
4C42H: 16×8 multiply (also used standalone) by shift-and-add.
4C57H/4C59H: 16-bit divide of HL by C, returning quotient in HL and remainder in A.

[8×8 MULTIPLY — entry point] A × L → HL (16-bit product)

4C37
LD H,00H 26 00
Clear H = 0 so that HL = 00:L = (unsigned) L. This sign-extends L to 16 bits for the 16-bit multiply that follows at 4C42H.

[16÷8 DIVIDE — entry point] HL ÷ A → result via 4C42H

4C39
PUSH BC C5
Save BC (B and C may hold values needed by the caller after the divide).
4C3A
CALL 4C42H CD 42 4C
Call the 16-bit multiply core at 4C42H. In the divide context: HL = dividend, A = divisor; 4C42H performs HL × A and on return HL = product (used as quotient approximation), C = bitmask remnant, H:L = remainder packed in the high/low bytes.
4C3D
LD A,H 7C
Load A = H (the high byte of the 16-bit result in HL).
4C3E
LD H,L 65
Move L → H. Rearranges the two-byte result so H holds what was in L.
4C3F
LD L,C 69
Move C → L. C holds the bitmask remnant from 4C42H which encodes the remainder. Together the three moves produce HL = (old_L):C = quotient, A = remainder in packed form.
4C40
POP BC C1
Restore BC from the stack.
4C41
RET C9
Return with HL = quotient (or product), A = remainder.

[16-BIT SHIFT-AND-ADD MULTIPLY CORE]Computes HL × A → HL (lower 16 bits) by testing each bit of A, shifting A right, and conditionally adding DE to an accumulator.

4C42
PUSH DE D5
Save DE (caller’s DE; will be overwritten internally).
4C43
EX DE,HL EB
DE = HL (the multiplicand moved to DE for use in ADD HL,DE), HL = 0000H (product accumulator, cleared below).
4C44
LD C,80H 0E 80
Load C = 80H (10000000b) — an 8-bit sentinel/bitmask that starts at the MSB and is shifted right each iteration to test successive bits of A.
4C46
LD HL,0000H 21 00 00
Clear HL = 0000H — the 16-bit product accumulator.

[MULTIPLY LOOP] 8 iterations (one per bit of A).

4C49
RRCA 0F
Rotate A right: the current LSB of A moves into CARRY; A shifts right by 1. The loop processes A bit-by-bit from LSB to MSB.
4C4A
If CARRY is clear (this bit of the multiplier A was 0), skip the add and go straight to the shift-the-accumulator step. Nothing contributed by this bit.
4C4C
ADD HL,DE 19
This bit of A was 1: add DE (the multiplicand) to the product accumulator HL.
4C4D
SRL H CB 3C
Shift H right 1 bit, MSB of H becomes 0, LSB of H moves to CARRY.
4C4F
RR L CB 1D
Rotate L right through CARRY (the bit shifted out of H enters L’s MSB). Together SRL H / RR L perform a 16-bit logical right-shift of HL by 1.
4C51
RR C CB 19
Rotate C right through CARRY. C started as 80H and is right-shifted each iteration; when bit 0 of C is shifted out, CARRY is set to signal the end of the 8-iteration loop.
4C53
If CARRY is clear (C still has bits remaining), loop back to 4C49H. After 8 iterations the last bit is shifted out of C, setting CARRY and ending the loop.
4C55
POP DE D1
Restore DE (caller’s original value).
4C56
RET C9
Return with HL = lower 16 bits of the product; C = 00H (all bits used).

4C57H–4C68H — 16-bit ÷ 8-bit Non-Restoring Divide

Divides the 16-bit value in HL by the 8-bit divisor in C (set to 5 at 4C57H for the granule calculation, or passed in directly at 4C59H). Returns quotient in HL, remainder in A. Uses a standard bit-serial non-restoring algorithm with a 16-bit loop counter in B.

4C57
LD A,05H 3E 05
Load A = 05H then fall through. This entry point hard-codes the divisor as 5 (sectors per granule for SS/SD format) before entering the general divide at 4C59H.
4C59
LD C,A 4F
Store A (divisor) into C. All arithmetic below uses C as the divisor.
4C5A
LD B,10H 06 10
Load B = 10H (16 decimal) — the loop will iterate once per bit of the 16-bit dividend.
4C5C
XOR A AF
Clear A = 0; A serves as the running partial remainder.

[DIVIDE LOOP] 16 iterations, one per bit of dividend HL.

4C5D
ADD HL,HL 29
Shift HL left by 1: the MSB of HL moves into CARRY. This extracts the next dividend bit for the partial-remainder step.
4C5E
RLA 17
Rotate A (partial remainder) left through CARRY: the bit shifted out of HL enters A’s LSB, effectively shifting a new dividend bit into the partial remainder for comparison.
4C5F
If CARRY is set (overflow from RLA — partial remainder now ≥ 256), the remainder definitely exceeds the divisor; jump to 4C64H to subtract and record a “1” quotient bit.
4C61
CP C B9
Compare A (partial remainder) against C (divisor). If A ≥ C (CARRY clear), the remainder exceeds the divisor and we can subtract.
4C62
If CARRY is set (A < C — partial remainder is smaller than divisor), this bit of the quotient is 0; jump to 4C66H to continue without subtracting.
4C64
SUB C 91
A −= C (divisor): restore the partial remainder by subtracting the divisor.
4C65
INC L 2C
Set the current quotient bit to 1 by incrementing L. Because HL was just shifted left one position and the new LSB of L is 0, INC L correctly sets that bit to 1.
4C66
DJNZ 4C5DH 10 F5
Decrement B; if B ≠ 0 loop back to 4C5DH for the next bit. After 16 iterations B = 0 and the loop exits.

[DIVIDE COMPLETE] HL = quotient; A = remainder.

4C68
OR A B7
Set flags from A (the remainder). Z FLAG is set if A = 0 (exact division, no remainder); NZ if A ≠ 0 (there is a remainder).
4C69
RET C9
Return with HL = quotient, A = remainder, Z FLAG reflecting whether the division was exact.

4C6AH–4C91H — String Search: Match (BC) against (HL)

Walks a table pointed to by HL, comparing each entry against the string pointed to by BC. The table entries are NULL (00H) terminated. Returns with HL pointing just past the matching entry if found, or falls to 4C8EH (no-match path) if not.

[OUTER LOOP — advance to next table entry]

4C6A
PUSH HL E5
Save HL (current table-entry start pointer) so we can restore it if this entry does not match and we need to skip to the next entry.

[INNER LOOP — compare one character]

4C6B
LD A,(BC) 0A
Load A with the next character from the search string at (BC). BC is the running pointer into the string being looked up.
4C6C
OR A B7
Test A. If A = 00H (null = end of search string), Z FLAG is set and the search string is fully consumed.
4C6D
INC BC 03
Advance BC to point to the next character of the search string. INC BC does not change flags.
4C6E
If NZ FLAG (A ≠ 00H — still have search-string characters to compare), jump to 4C73H to compare against the table entry.

[SEARCH STRING EXHAUSTED — match found]

4C70
EX (SP),HL E3
Exchange HL with the top-of-stack value (the saved table-entry start pointer). After this HL holds the start-of-entry pointer and the saved HL is now on the stack. This effectively discards the stacked value and leaves HL at the entry.
4C71
POP HL E1
Pop HL. Combined with the EX (SP),HL above, HL is now restored to its pre-search value pointing past the matched entry.
4C72
RET C9
Return with match found; HL points just past the end of the matched entry, Z FLAG reflects the result of the last OR A (which was zero).
4C73
CP (HL) BE
Compare A (search-string character) against (HL) (table-entry character).
4C74
INC HL 23
Advance HL to the next character of the current table entry.
4C75
If Z FLAG (characters matched), loop back to 4C6BH to compare the next character.

[MISMATCH — skip rest of this table entry]

4C77
POP HL E1
Restore HL (the table-entry start pointer that was pushed at 4C6AH). We will skip this entry and try the next.
4C78
Jump to 4C8EH to advance past the end of this (mismatched) table entry and check for end-of-table or try the next entry.

4C7AH–4C91H — Command-Line Token Scanner

Scans the command-line buffer pointed to by HL for tokens delimited by commas, spaces, or carriage return. Returns with HL pointing to the next non-space character, sets CARRY if a non-comma delimiter was found (end-of-field), and sets Z on end-of-line. Used by the command parser to walk through arguments.

4C7A
LD A,(HL) 7E
Load A with the current character from the command-line buffer at (HL).
4C7B
CP 0DH FE 0D
Compare A against 0DH (carriage return — end of command line).
4C7D
RET Z C8
If Z FLAG (end-of-line CR found), return immediately. Z FLAG signals “no more tokens.”
4C7E
LD A,(HL) 7E
Reload A with the current character (flags may have been altered by RET Z above).
4C7F
CP 2CH FE 2C
Compare A against 2CH (ASCII comma ‘,’).
4C81
INC HL 23
Advance HL past this character regardless of what it is (the branch below decides whether to use it).
4C82
If Z FLAG (comma found), this is a field separator; jump to 4C8EH to clear CARRY (comma = normal separator, not end-of-argument) and continue.
4C84
CP 20H FE 20
Compare A against 20H (ASCII space ‘ ’). Re-uses A which still holds the character from before INC HL.
4C86
DEC HL 2B
Step HL back by one (undo the INC HL done at 4C81H) if the character was a space — we don’t consume spaces as token separators here, we just note that a space was seen.
4C87
SCF 37
Set CARRY FLAG unconditionally. CARRY = 1 means “a non-comma delimiter (space) was encountered — end of token.”
4C88
If NZ FLAG (character was NOT a space), it is a printable token character; jump to 4C8FH to return with CARRY set and A = the character.

[SPACE FOUND — skip consecutive spaces]

4C8A
INC HL 23
Advance HL past the space character.
4C8B
CP (HL) BE
Compare A (still 20H from the space comparison) against (HL) (next character). Z FLAG set if the next character is also a space.
4C8C
If Z FLAG (next character is also a space), loop back to 4C8AH to skip it. This loop skips all consecutive spaces.
4C8E
OR A B7
Clear CARRY FLAG by executing OR A (OR of A with itself never sets CARRY). CARRY = 0 means “normal field separator (comma) or start of next field.”
4C8F
LD A,34H 3E 34
Load A = 34H — a token-type code used by the command parser to identify “a printable token follows” or “the next call to this routine should re-read HL.” The exact semantic is command-context dependent.
4C91
RET C9
Return with A = 34H (token-type), CARRY reflecting field-delimiter type, and HL pointing to the next non-space character in the command line.

4C92H–4C9CH — Delay Loop (B × ROM call 0060H)

Introduces a calibrated delay used after drive-selection changes and after “Not Ready” errors to allow the drive motor to reach operating speed. Calls the Model III ROM delay routine at 0060H exactly B times, each call being 1 ms (approximately).

4C92
LD D,B 50
Copy B (the delay-loop count, set by the caller: 80H = 128 ms for drive spin-up, 0CH = 12 ms for shorter waits) into D as the outer iteration counter.
4C93
LD E,01H 1E 01
Load E = 01H. E is passed to ROM 0060H as the inner delay multiplier (1 = minimum inner loop count, produces approximately 1 ms per outer iteration).

[DELAY LOOP]

4C95
LD B,D 42
Reload B from D at the start of each outer iteration (ROM 0060H may destroy B, so it must be refreshed).
4C96
CALL 0060H CD 60 00
Call the Model III ROM timing delay at 0060H. With E = 1 this produces a delay of approximately 1 ms. The ROM routine uses the E register as its inner-loop count and returns with B and E preserved but A altered.
4C99
DEC E 1D
Decrement E (the outer iteration counter). When E reaches 0 the loop terminates.
4C9A
If NZ FLAG (E ≠ 0), loop back to 4C95H for the next 1 ms slice.
4C9C
RET C9
Return after total delay of D × 1 ms.

4C9DH–4CA1H — End-of-Device-Chain Return

Reached from the device-driver chain walk (4AE8H) when the end of the chain is found with no further matches. Tests bit 0 of B (the caller’s stop-after-first-match flag); if set it returns a null result, otherwise it returns A = C (the chain-selector, effectively 00H = success).

4C9D
BIT 0,B CB 40
Test bit 0 of B. If bit 0 = 1 the caller asked for stop-after-first; reaching end-of-chain in that mode means no match was found, so return without a result character (A is whatever the last handler left it).
4C9F
RET NZ C0
If NZ FLAG (bit 0 of B = 1 — stop-after-first mode), return immediately. The caller will see whatever A contains.
4CA0
LD A,C 79
Load A = C (the chain-selector / device-code, typically 00H here). Returning A = 00H signals success / “no error” for walk-all-nodes callers.
4CA1
RET C9
Return to the original SVC or device-driver caller. This is the last executed instruction of SYS0.

4CA2H–4CACH — Module-End Signature and Padding

The two bytes at 4CA2H–4CA3H are the module-end signature; the remaining bytes to 4CACH are zero padding to align the next module (SYS1) on a 256-byte boundary. These bytes are not executed.

4CA2
DB 49H,4BH — “IK” 49 4B
Two-byte module-end signature: ASCII ‘I’‘K’. NEWDOS/80 uses this two-character stamp to verify that SYS0 loaded correctly before transferring control.
4CA4–4CAC
DB 00H × 9 00 00 00 00 00 00 00 00 00
Nine zero-padding bytes. These fill out the 256-byte boundary so that the next loadable module begins at 4D00H.

4CADH–4CFFH — Module Padding

Zero-byte padding fills the remainder of the 256-byte page boundary. This ensures the next module (SYS1 or the initialization code at 4D00H) starts at a clean page address for efficient sector loading.

4CAD–4CFF
DB 00H × 83 00 (repeated)
83 zero-padding bytes completing the page alignment. Not executed.

4D00H — DOS Initialization Entry Point (PROGRAM ENTRY = 4D00H)

This is the main entry point for NEWDOS/80 initialization. The boot loader transfers control here after loading SYS0 and SYS1. This code determines the top of RAM, checks for a warm boot signature, copies boot sector parameters to DOS work areas, configures drives, and eventually displays the startup banner before entering the DOS READY state.

[MEMORY SIZE DETECTION]

4D00
AND L A5
Data byte A5H — initialization signature. This is read by the boot loader to verify the module loaded correctly.
4D01
IM 1 ED 56
Set Z80 Interrupt Mode 1. All maskable interrupts will execute RST 38H (location 0038H in ROM).
4D03
LD HL,FFFFH 21 FF FF
Point HL to top of memory (FFFFH). Begin RAM size detection by working backwards from here.
4D06
LD A,(HL) 7E
[RAM TEST LOOP START] Read byte at current test address (HL).
4D07
CPL 2F
Complement A (flip all bits). If address is RAM, writing this back will produce the complement.
4D08
LD (HL),A 77
Write complemented value back to memory.
4D09
CP (HL) BE
Compare written value with what’s actually there. If not equal, this isn’t writable RAM.
4D0A
DEC HL 2B
Move to next lower address.
4D0B
If NZ FLAG (memory didn’t hold value), continue testing lower addresses until RAM is found.
4D0D
CPL 2F
Restore original value.
4D0E
INC HL 23
HL now points to first byte of writable RAM (top of memory).
4D0F
LD (HL),A 77
Restore the byte that was modified during testing.
4D10
LD (42C9H),HL 22 C9 42
Store top-of-RAM address at 42C9H for later use by memory allocation routines.
4D13
LD (4411H),HL 22 11 44
Store same value at 4411H (high memory limit for DOS).

[WARM BOOT CHECK]

4D16
LD HL,42CBH 21 CB 42
Point HL to warm boot signature location.
4D19
LD A,A5H 3E A5
Load A with A5H (warm boot signature value).
4D1B
CP (HL) BE
Compare A5H against (42CBH). Z FLAG set if this is a warm boot (signature present).
4D1C
If NZ FLAG (cold boot — signature not found), skip the configuration copy and proceed to fresh initialization.

4D1EH–4F95H — DOS Configuration and Driver Initialization

The remaining initialization code (4D1EH–4F95H) performs these major functions:
• Copies configuration data from boot sector (43xxH area) to DOS work areas
• Initializes Drive Parameter Blocks for all configured drives
• Sets up system vectors at 4047H, 4049H, etc.
• Configures step rates, sector counts, and FCB templates
• Processes date/time entry prompts
• Loads and executes AUTO command if configured

This section contains extensive self-modifying code where boot sector values are patched into instruction operands throughout the DOS code area. A detailed byte-by-byte analysis is available separately.

4D1E–4F95H
(initialization code)
Approximately 630 bytes of initialization code. Key addresses: 4D47H loads FCB template, 4D6FH copies disk parameters, 4DDBH begins drive configuration loop, 4E83H displays boot banner, 4EF3H processes AUTO command file.

4F96H–504BH — NEWDOS/80 Boot Banner (Graphic Data)

This area contains the NEWDOS/80 startup banner displayed when the system boots. The data consists of TRS-80 semigraphic characters (bytes 80H–BFH) that create the distinctive “NEWDOS/80” logo with a decorative border, followed by ASCII text for “MODEL III VER 2” and copyright information. The banner is displayed by calling the routine at 4467H with HL pointing to 4F96H.

[SEMIGRAPHIC BANNER DATA] Bytes 80H–BFH are TRS-80 block graphics characters.

4F96–4FC6H
DB 1CH, 1FH, 03H, BFH, B4H… (graphic bytes)
Semigraphic character data forming the decorative border and “NEWDOS” logo. Uses block graphics characters to create the visual pattern displayed on the 64-column Model III screen.
4FC7–4FD3H
DEFM “ MODEL III VER 2” 20 20 4D 4F 44 45 4C…
ASCII text: “ MODEL III VER 2” (with leading spaces for centering).
4FD4–500FH
DB (continued graphic data) (graphic bytes)
Continuation of semigraphic border pattern.
5001–500FH
DEFM “ APPARAT, INC” 20 20 41 50 50 41 52…
ASCII text: “ APPARAT, INC” (copyright holder).
5010–504AH
DB (bottom border data) (graphic bytes)
Final graphic data completing the banner border, followed by CR (0DH) terminator.

504BH–509BH — Date/Time Entry Prompts and Validation Tables

Contains the date and time entry prompt strings and validation range tables used during system startup when the user is prompted to enter the current date and time.

504B–504CH
DB 0DH, 00H 0D 00
String terminator and null byte.
504D–5052H
DB 00H × 6 00 00 00 00 00 00
Reserved bytes.
5053–5065H
DEFM “DATE? (MM/DD/YY) ” 44 41 54 45 3F…
Date entry prompt string displayed at startup. Includes format hint (MM/DD/YY) followed by CR (03H terminator pattern).
5066–5078H
DEFM “TIME? (HH:MM:SS) ” 54 49 4D 45 3F…
Time entry prompt string displayed at startup.
5079–508BH
DB (validation data) 4D 4D 2F 44 44…
Date/time format templates: MM/DD/YY and HH:MM:SS patterns used for input validation parsing.
508C–509BH
DB 01H, 0CH, 01H, 1FH… 01 0C 01 1F…
Validation range table: minimum/maximum values for month (01–12), day (01–31), year (00–99), hour (00–23), minute (00–59), second (00–59).

509CH–51EBH — Reserved Buffer Space

The remainder of the SYS0 module consists of zero-initialized buffer space used at runtime for various system operations including command line input buffers, directory entry caching, and file I/O operations.

509C–51EBH
DB 00H × 336 00 (repeated)
Zero-initialized buffer space. Total of 336 bytes (150H) extending to address 51EBH. This memory is used dynamically at runtime for command input, directory operations, and temporary file data storage. End of SYS0/SYS module.