5372H - Program Data Area and Initialization
This section contains the program's data storage area and initialization code. The LMOFFSET utility is a load module relocation program that allows users to change the base load address of CMD files. When entered, Register SP is set to 5E95H, and various working storage locations are initialized to prepare for the relocation process.
5372
LD BC,9A00H 01 00 9A
Load Register BC with 9A00H. This appears to be initialization data that will be modified - note that location 5372H is written to later at address 5381H, suggesting this is a variable storage location rather than pure code.
5375
LD E,(HL) 5E
Load Register E with the byte pointed to by Register HL. This instruction would execute if control fell through from 5372H, but this area is actually being used for data storage.
5376
RST 38H FF
Call the ROM routine at address 0038H. These bytes (5376H-537BH) are variable storage locations that will be initialized by the actual program entry point at 537CH.
5377
RST 38H FF
Another RST 38H instruction - this is part of the data area.
5378
NOP 00
No operation. Part of the variable storage area.
5379
NOP 00
No operation. Part of the variable storage area.
537A
NOP 00
No operation. Part of the variable storage area.
537B
NOP 00
No operation. Part of the variable storage area.
537CH - Program Entry Point and Initialization
This is the actual entry point of the LMOFFSET program (as indicated by "PROGRAM ENTRY = 537CH" at the end of the listing). The code initializes the stack pointer, sets up working storage variables at locations 5372H-537AH, clears a 64-byte buffer at 58C6H, and prepares various pointer locations for the relocation process.
[PROGRAM INITIALIZATION] Set up stack and working storage for the load module offset utility.
537C
LD SP,5E95H 31 95 5E
Load the Stack Pointer to 5E95H. This establishes the program's stack in high memory, just before the end of the utility's code space.
537F
LD A,01H 3E 01
Load Register A with 01H to prepare for initialization.
5381
LD (5372H),A 32 72 53
Store 01H at memory location 5372H. This initializes the first byte of the variable storage area, overwriting the "01" byte of the "LD BC,9A00H" instruction that appeared there.
5384
XOR A AF
Exclusive-OR Register A with itself, setting Register A to 00H and setting the Z FLAG.
5385
LD (5373H),A 32 73 53
Store 00H at memory location 5373H, clearing the second byte of the variable storage area.
Clear a 64-byte buffer at 58C6H, presumably used for temporary data storage during module processing.
5388
LD HL,58C6H 21 C6 58
Load Register HL with 58C6H, pointing to the start of the buffer to be cleared.
538B
LD B,40H 06 40
Load Register B with 40H (64 decimal), setting up a loop counter to clear 64 bytes.
538D
LD (HL),A 77
[LOOP START] Store 00H (value in Register A) at the memory location pointed to by Register HL.
538E
INC HL 23
Increment Register HL to point to the next byte in the buffer.
538F
Decrement Register B and JUMP to 538DH if Register B is Not Zero. This creates a loop that clears 64 bytes from 58C6H through 5905H. [LOOP END]
Initialize various pointer and control variables used throughout the program. Register A still contains 00H from the XOR operation.
5391
LD H,A 67
Load Register H with 00H (value in Register A).
5392
LD L,A 6F
Load Register L with 00H (value in Register A), making Register HL = 0000H.
5393
LD (5378H),HL 22 78 53
Store 0000H at memory locations 5378H-5379H. This initializes a 16-bit variable to zero.
5396
LD (537AH),HL 22 7A 53
Store 0000H at memory locations 537AH-537BH. This initializes another 16-bit variable to zero.
5399
DEC HL 2B
Decrement Register HL, changing it from 0000H to FFFFH.
539A
LD (5376H),HL 22 76 53
Store FFFFH at memory locations 5376H-5377H. This initializes a variable to -1 or maximum value.
539D
LD HL,5E9AH 21 9A 5E
Load Register HL with 5E9AH, pointing to a location in high memory just above the stack.
53A0
LD (5374H),HL 22 74 53
Store 5E9AH at memory locations 5374H-5375H. This initializes a pointer variable, likely used as a buffer pointer during module processing.
53A3
CALL subroutine at 5D15H. This routine stores 50H at location 58C3H and loads HL with 5A3EH (the address of the program title message "APPARAT LOAD MODULE OFFSET PROGRAM, VERSION 2.0").
53A6
CALL subroutine at 4467H (a DOS routine). This likely displays the title message pointed to by Register HL and initializes the screen or I/O system.
53A9H - Main Menu Loop - Source Type Selection
This is the main program loop that prompts the user to specify whether the source will be from Disk or Tape. Register B is used as a retry counter (initialized to some value by the previous 4467H call). The program displays "SOURCE FROM DISK OR TAPE? (D OR T)" and waits for the user to press D, T, or * (asterisk, ASCII 2AH, to cancel). If an invalid key is pressed, the program beeps and re-prompts.
[MAIN MENU LOOP START] Prompt user for source type (Disk or Tape).
53A9
LD HL,5ACBH 21 CB 5A
Load Register HL with 5ACBH, which points to the message "SOURCE FROM DISK OR TAPE? (D OR T)" (terminated with 03H).
53AC
CALL subroutine at 5893H. This displays the message pointed to by Register HL and gets a keystroke from the user, returning the character in Register A.
53AF
Decrement Register B and JUMP back to 53A9H if Register B is Not Zero. This provides a limited number of retry attempts if the user keeps entering invalid responses.
Check if user pressed D (44H) to select Disk as source.
53B1
CP 44H FE 44
Compare Register A with 44H (ASCII 'D'). If they are equal, the Z FLAG is set; otherwise the NZ FLAG is set.
53B3
If the Z FLAG is set (meaning the user pressed D), JUMP to 54DCH to handle disk-based source processing.
Check if user pressed T (54H) to select Tape as source.
53B6
CP 54H FE 54
Compare Register A with 54H (ASCII 'T'). If they are equal, the Z FLAG is set; otherwise the NZ FLAG is set.
53B8
If the NZ FLAG is set (meaning the user pressed neither D nor T - and it wasn't an asterisk which would have been handled by 5893H), JUMP back to 53A9H to re-prompt. This creates an error loop for invalid input.
[TAPE SOURCE SELECTED] User chose Tape as source. Display an asterisk indicator at top-right of screen and proceed to tape processing.
53BA
LD A,2AH 3E 2A
Load Register A with 2AH (ASCII '*' asterisk character).
53BC
LD (3C3DH),A 32 3D 3C
Store the asterisk at video RAM location 3C3DH, which is near the top-right corner of the screen (column 61 of row 0). This provides a visual indicator that tape mode is active.
53BF
CALL subroutine at 583AH. This likely prepares the system for tape operations.
53C2H - Tape Input Block Read Loop
This section implements the tape reading process. It disables interrupts, reads tape data blocks, and validates the data with checksums. The routine at 0293H reads a tape block header, and 0235H reads the actual data byte. The code implements a retry mechanism with visual feedback, displaying 'P' during retries and 'U' or "<" depending on the result. When a block passes checksum validation, it displays "<" and either continues reading or proceeds to the next phase.
[TAPE BLOCK READ LOOP START] Read tape data blocks with checksum validation.
53C2
DI F3
Disable Interrupts to ensure tape timing is not disrupted by interrupt service routines.
53C3
CALL ROM routine at 0293H. This reads a tape block header and returns status/data in registers. This is part of the TRS-80 Model I tape I/O routines.
53C6
LD A,FFH 3E FF
Load Register A with FFH. This value will be written to the output buffer.
53C8
CALL subroutine at 5457H. This writes the byte in Register A (FFH) to the output buffer and performs initialization: it gets the buffer pointer from (5374H), increments it, clears Register BC, and writes two more bytes (00H) to the buffer.
53CB
[RETRY POINT] CALL subroutine at 583BH. This likely re-enables interrupts and prepares for the next tape read operation.
53CE
CALL ROM routine at 0235H. This reads a single data byte from tape, returning it in Register A with status flags set.
Check if byte read was 'U' (55H), indicating successful read or specific tape marker.
53D1
CP 55H FE 55
Compare Register A with 55H (ASCII 'U'). If they are equal, the Z FLAG is set; otherwise the NZ FLAG is set.
53D3
If the Z FLAG is set (byte read was 'U'), JUMP to 53DFH to continue with the next phase of tape processing.
[ERROR HANDLING] Byte was not "U", so indicate retry with "P" character and try again.
53D5
CALL subroutine at 5492H. This writes a byte to the buffer and sets up a 2-byte sequence at (HL) with BC values.
53D8
LD A,50H 3E 50
Load Register A with 50H (ASCII 'P'). This will be displayed to indicate a retry is occurring.
53DA
LD (3C3CH),A 32 3C 3C
Store the 'P' character at video RAM location 3C3CH (column 60 of row 0), providing visual feedback of the retry.
53DD
JUMP back to 53CBH to retry reading the tape block.
53DFH - Tape Block Validation and Continuation
After successfully reading 'U' (55H), this section writes FEH to the output buffer and continues reading tape data. It reads bytes, checking for 'x' (78H) which signals completion, or "<" (3CH) which signals continuation to another block. Any other byte causes a retry with visual feedback. When "<" is received, the code reads the block length byte, calculates checksums, and validates the data integrity.
[TAPE BLOCK CONTINUATION] Process the 'U' response and continue reading the block.
53DF
LD A,FEH 3E FE
Load Register A with FEH. This marker value will be written to the buffer.
53E1
CALL subroutine at 5457H to write FEH to the output buffer and initialize buffer variables.
53E4
[INNER RETRY POINT] CALL subroutine at 583BH to prepare for next byte read.
53E7
CALL ROM routine at 0235H to read the next data byte from tape into Register A.
Check for completion marker 'x' (78H).
53EA
CP 78H FE 78
Compare Register A with 78H (ASCII 'x'). If they are equal, the Z FLAG is set; otherwise the NZ FLAG is set.
53EC
If the Z FLAG is set (byte read was 'x'), JUMP to 54CAH to handle tape completion and finalize the transfer.
Check for continuation marker "<" (3CH).
53EF
CP 3CH FE 3C
Compare Register A with 3CH (ASCII "<"). If they are equal, the Z FLAG is set; otherwise the NZ FLAG is set.
53F1
If the Z FLAG is set (byte read was "<"), JUMP to 53F8H to begin reading a data block with checksum validation.
[ERROR - INVALID BYTE] Neither 'x' nor "<" was received. Retry the read operation.
53F3
CALL subroutine at 5492H to write error information to the buffer.
53F6
JUMP back to 53E4H to retry reading the byte.
53F8H - Block Length and Checksum Processing
When the "<" marker is received, this section reads the block length byte from tape and processes the block with checksum validation. It reads two bytes (presumably the block length), stores them in the buffer via subroutine 547BH, then reads two more bytes to form an address in Register HL via subroutine 5467H. It enters a loop that reads the specified number of data bytes, accumulating a checksum in Register C. After all bytes are read, it compares the calculated checksum with the checksum byte from tape. If they match, it displays '<' and continues; if not, it displays 'C' (checksum error) and retries.
[DATA BLOCK READ WITH CHECKSUM] Read block length, data bytes, and validate checksum.
53F8
LD A,01H 3E 01
Load Register A with 01H. This will be used as a parameter for the next call.
53FA
CALL subroutine at 547BH. This writes the byte in Register A (01H) to the output buffer at the location pointed to by (5374H), checking for buffer overflow.
53FD
CALL ROM routine at 0235H to read a byte from tape into Register A. This is the block length byte.
5400
LD B,A 47
Load Register B with the block length value from Register A. Register B will be used as the loop counter for reading data bytes.
5401
ADD 02H C6 02
Add 02H to Register A. This adjusts the count, possibly to include two additional bytes (perhaps address bytes).
5403
CALL subroutine at 547BH to write the adjusted length value to the output buffer.
5406
CALL subroutine at 5467H. This reads two bytes from tape (via calls to 5478H which calls 0235H), forming a 16-bit address in Register HL (low byte first, then high byte).
5409
ADD A,L 85
Add Register L to Register A. This begins accumulating the checksum, adding the low byte of the address just read.
540A
LD C,A 4F
Load Register C with the checksum accumulator from Register A. Register C will hold the running checksum value.
540B
CALL subroutine at 549BH. Looking at later code, this likely writes Register HL to the output buffer or performs other buffer management.
[DATA BYTE READ LOOP] Read B data bytes from tape, adding each to the checksum.
540E
[LOOP START] CALL subroutine at 5478H to read one data byte from tape (via ROM routine 0235H) and write it to the output buffer (via 547BH), returning the byte in Register A.
5411
ADD A,C 81
Add Register C (current checksum) to Register A (byte just read), accumulating the checksum.
5412
LD C,A 4F
Load Register C with the updated checksum value from Register A.
5413
Decrement Register B (loop counter) and JUMP back to 540EH if Register B is Not Zero. This continues until all data bytes have been read and added to the checksum. [LOOP END]
[CHECKSUM VALIDATION] Read the checksum byte from tape and compare with calculated checksum.
5415
CALL ROM routine at 0235H to read the checksum byte from tape into Register A.
5418
CP C B9
Compare Register A (checksum from tape) with Register C (calculated checksum). If they are equal, the Z FLAG is set; otherwise the NZ FLAG is set.
5419
If the NZ FLAG is set (meaning checksum mismatch), JUMP to 5442H to handle the checksum error.
[CHECKSUM VALID] Toggle the indicator character and check for continuation or completion.
541B
LD A,(3C3FH) 3A 3F 3C
Load Register A with the byte at video RAM location 3C3FH (the last position of the top row, column 63). This is the status indicator character.
541E
XOR 0AH EE 0A
Exclusive-OR Register A with 0AH. This toggles certain bits to create a visual variation in the indicator character, providing feedback that processing is continuing.
5420
LD (3C3FH),A 32 3F 3C
Store the modified indicator character back at video RAM location 3C3FH, updating the display.
5423
CALL subroutine at 583BH to prepare for the next tape read.
5426
CALL ROM routine at 0235H to read the next control byte from tape.
Check what the next byte indicates: "<" means another block follows, 'x' means completion.
5429
CP 3CH FE 3C
Compare Register A with 3CH (ASCII "<"). If they are equal, the Z FLAG is set; otherwise the NZ FLAG is set.
542B
If the Z FLAG is set (another block follows), JUMP back to 53F8H to read the next block.
542E
CP 78H FE 78
Compare Register A with 78H (ASCII 'x'). If they are equal, the Z FLAG is set; otherwise the NZ FLAG is set.
5430
If the Z FLAG is set (tape transfer complete), JUMP to 54CAH to finalize and continue to destination selection.
[UNEXPECTED BYTE] Neither "<" nor 'x' received. Display 'I' (invalid) and retry.
5433
PUSH AF F5
PUSH Register AF onto the stack to preserve the byte value.
5434
LD A,49H 3E 49
Load Register A with 49H (ASCII 'I' for Invalid).
5436
LD (3C3DH),A 32 3D 3C
Store the 'I' character at video RAM location 3C3DH (column 61 of row 0) as an error indicator.
5439
LD A,FDH 3E FD
Load Register A with FDH to write to the buffer as an error marker.
543B
CALL subroutine at 5457H to write FDH to the output buffer and initialize counters.
543E
POP AF F1
POP the original byte value from the stack back into Register AF.
543F
JUMP to 53F3H to continue error handling and retry the operation.
5442H - Checksum Error Handler
This section handles checksum validation failures. When the calculated checksum do not match the checksum byte read from tape, this code writes an error marker (FCH) to the buffer, followed by the received checksum byte and the calculated checksum value. It then displays "C" (for Checksum error) at screen position 3C3EH and jumps back to retry the block read at 5423H.
[CHECKSUM ERROR] Checksums don't match. Record error information and retry.
5442
PUSH AF F5
PUSH Register AF onto the stack to preserve the checksum byte that was read from tape.
5443
LD A,FCH 3E FC
Load Register A with FCH, a marker indicating a checksum error.
5445
CALL subroutine at 547BH to write the error marker FCH to the output buffer.
5448
POP AF F1
POP the checksum byte from the stack back into Register AF.
5449
CALL subroutine at 547BH to write the received checksum byte to the buffer for error logging.
544C
LD A,C 79
Load Register A with the calculated checksum value from Register C.
544D
CALL subroutine at 547BH to write the calculated checksum to the buffer for comparison.
5450
LD A,43H 3E 43
Load Register A with 43H (ASCII 'C' for Checksum error).
5452
LD (3C3EH),A 32 3E 3C
Store the 'C' character at video RAM location 3C3EH (column 62 of row 0) as a visual error indicator.
5455
JUMP back to 5423H to retry reading the next control byte and continue processing.
5457H - Write Marker and Initialize Buffer Counters
This subroutine writes a marker byte (passed in Register A) to the output buffer, then initializes buffer management variables. It retrieves the buffer pointer from location 5374H, increments it, clears Register BC (setting both B and C to 00H), then writes two additional zero bytes to the buffer. This is used to start new sections in the output buffer with proper initialization.
5457
CALL subroutine at 547BH to write the byte in Register A (marker value) to the output buffer.
545A
LD HL,(5374H) 2A 74 53
Load Register HL with the 16-bit value stored at memory locations 5374H-5375H. This is the current buffer pointer.
545D
INC HL 23
Increment Register HL to point to the next buffer location.
545E
XOR A AF
Exclusive-OR Register A with itself, setting Register A to 00H.
545F
LD B,A 47
Load Register B with 00H (value in Register A).
5460
LD C,A 4F
Load Register C with 00H (value in Register A), making Register BC = 0000H.
5461
CALL subroutine at 547BH to write 00H to the buffer.
5464
JUMP to 547BH to write another 00H to the buffer, then return to the caller (since 547BH ends with RET).
5467H - Read 16-Bit Value from Tape (Little-Endian)
This subroutine reads two bytes from tape and assembles them into a 16-bit value in Register HL. It first calls 5478H to read the low byte into Register L, then calls 5478H again to read the high byte into Register H. The subroutine returns with the complete 16-bit value in HL (little-endian format: low byte first, then high byte).
5467
CALL subroutine at 5478H to read one byte from tape and write it to the buffer, returning the byte in Register A.
546A
LD L,A 6F
Load Register L with the byte just read (the low byte of the 16-bit value).
546B
CALL subroutine at 5478H again to read the second byte from tape.
546E
LD H,A 67
Load Register H with the byte just read (the high byte of the 16-bit value). Register HL now contains the complete 16-bit value in little-endian format.
546F
RET C9
RETURN to the calling routine with the 16-bit value in Register HL.
5470H - Write B Bytes from Memory to Buffer
This subroutine writes B bytes from memory (pointed to by HL) to the output buffer. It implements a loop that reads each byte from (HL), calls 547BH to write it to the buffer, increments HL to the next source byte, and continues until Register B reaches zero. This is used for block memory transfers to the output buffer.
[MEMORY TO BUFFER LOOP] Copy B bytes from memory to output buffer.
5470
LD A,(HL) 7E
[LOOP START] Load Register A with the byte at the memory location pointed to by Register HL.
5471
INC HL 23
Increment Register HL to point to the next source byte.
5472
CALL subroutine at 547BH to write the byte in Register A to the output buffer.
5475
Decrement Register B (byte counter) and JUMP back to 5470H if Register B is Not Zero. Continue until all bytes have been written. [LOOP END]
5477
RET C9
RETURN to the calling routine.
5478H - Read Byte from Tape and Write to Buffer
This simple wrapper subroutine reads one byte from tape via ROM routine 0235H and then falls through to subroutine 547BH to write that byte to the output buffer. It effectively combines a tape read operation with a buffer write, returning the byte read in Register A.
5478
CALL ROM routine at 0235H to read one byte from tape, returning it in Register A.
Fall through to 547BH to write this byte to the buffer and return.
547BH - Write Byte to Buffer with Overflow Check
This critical subroutine writes a byte to the output buffer with overflow protection. It preserves HL and DE on the stack, retrieves the current buffer pointer from (5374H), loads the buffer limit from DOS system variable at (4049H), and compares them using the RST 18H routine (which performs a 16-bit compare). If the buffer would overflow (Carry flag not set), it jumps to error handler 585CH. Otherwise, it increments the pointer, stores the byte, updates the pointer at (5374H), and returns. This ensures the output buffer never overflows into critical memory areas.
547B
PUSH HL E5
PUSH Register HL onto the stack to preserve it.
547C
PUSH DE D5
PUSH Register DE onto the stack to preserve it.
547D
LD HL,(5374H) 2A 74 53
Load Register HL with the 16-bit value stored at memory locations 5374H-5375H, which is the current output buffer pointer.
5480
LD DE,(4049H) ED 5B 49 40
Load Register DE with the 16-bit value stored at memory locations 4049H-404AH. This is a DOS system variable that likely contains the buffer limit address or end of available memory.
5484
PUSH AF F5
PUSH Register AF onto the stack to preserve the byte to be written.
5485
RST 18H DF
Call ROM routine at 0018H (RST 18H). This routine performs a 16-bit compare of HL with DE, setting the Carry flag if HL < DE (buffer has space), or clearing it if HL ≥ DE (buffer overflow).
5486
If the NC FLAG (No Carry) is set (meaning HL ≥ DE, buffer overflow), JUMP to 585CH to handle the error condition (insufficient memory).
5489
INC HL 23
Increment Register HL to point to the next buffer location.
548A
POP AF F1
POP the byte value from the stack back into Register AF.
548B
LD (HL),A 77
Store the byte from Register A at the memory location pointed to by Register HL, writing it to the buffer.
548C
LD (5374H),HL 22 74 53
Store the updated buffer pointer from Register HL back to memory locations 5374H-5375H, updating the pointer for the next write.
548F
POP DE D1
POP the saved Register DE from the stack.
5490
POP HL E1
POP the saved Register HL from the stack.
5491
RET C9
RETURN to the calling routine.
5492H - Write Byte and Store BC Values
This short subroutine writes the byte in Register A to the buffer (via 547BH), then increments Register BC and stores the BC value at the memory location pointed to by HL (C at (HL), B at (HL+1)), before decrementing HL back to its original position. This appears to be used for storing count or status information along with buffer writes.
5492
CALL subroutine at 547BH to write the byte in Register A to the output buffer.
5495
INC BC 03
Increment Register BC.
5496
LD (HL),C 71
Store Register C at the memory location pointed to by Register HL.
5497
INC HL 23
Increment Register HL to point to the next memory location.
5498
LD (HL),B 70
Store Register B at the memory location pointed to by Register HL, completing a 16-bit store of BC in little-endian format.
5499
DEC HL 2B
Decrement Register HL back to its original value (pointing to where C was stored).
549A
RET C9
RETURN to the calling routine.
549BH - Write HL Register Pair to Buffer
This subroutine writes the 16-bit value in Register HL to the output buffer in little-endian format (low byte first, then high byte). It transfers L to Register A and writes it via 547BH, then transfers H to Register A and writes it. This is used to store 16-bit addresses or values in the output buffer.
549B
LD A,L 7D
Load Register A with the contents of Register L (the low byte of the 16-bit value).
549C
CALL subroutine at 547BH to write the low byte to the output buffer.
549F
LD A,H 7C
Load Register A with the contents of Register H (the high byte of the 16-bit value).
54A0
JUMP to 547BH to write the high byte to the buffer, then return to the caller (since 547BH ends with RET).
54A3H - Complex Data Processing Routine
This section implements complex data manipulation involving buffer reading, modification, and writing. It reads 16-bit values from the buffer, performs arithmetic operations including addition with the value from (5378H), handles byte copies, and manages loop counters. The routine appears to be processing data blocks that were read from tape, potentially performing address relocation by adding an offset to addresses within the module.
54A3
LD (5374H),HL 22 74 53
Store Register HL at memory locations 5374H-5375H, updating the buffer pointer.
54A6
LD E,(HL) 5E
Load Register E with the byte at the memory location pointed to by Register HL.
54A7
INC HL 23
Increment Register HL to point to the next byte.
54A8
LD D,(HL) 56
Load Register D with the byte at the memory location pointed to by Register HL, completing a 16-bit read into Register DE.
54A9
INC HL 23
Increment Register HL to point past the 16-bit value just read.
54AA
LD A,(HL) 7E
Load Register A with the byte at the memory location pointed to by Register HL.
54AB
INC HL 23
Increment Register HL to advance the pointer.
54AC
FE01 FE 01
Compare Register A with 01H. If they are equal, the Z FLAG is set; otherwise the NZ FLAG is set. This appears to be checking a block type or flag.
54AE
If the Z FLAG is set (byte was 01H), JUMP to 54F2H to handle this special case.
54B1
FEFF FE FF
Compare Register A with FFH. If they are equal, the Z FLAG is set; otherwise the NZ FLAG is set.
54B3
If the Z FLAG is set (byte was FFH), JUMP to 55D4H to handle this special case (likely end of data marker).
54B6
FEFE FE FE
Compare Register A with FEH. If they are equal, the Z FLAG is set; otherwise the NZ FLAG is set.
54B8
If the Z FLAG is set (byte was FEH), JUMP to 5582H to handle this marker type.
54BB
E5 E5
PUSH Register HL onto the stack to preserve the buffer pointer.
54BC
EB EB
EXchange the contents of Register DE with Register HL. Now HL contains the 16-bit value that was in DE (likely an address to be relocated).
54BD
2A7853 2A 78 53
Load Register HL with the 16-bit value stored at memory locations 5378H-5379H. This is the relocation offset that will be added to addresses.
54C0
19 19
Add Register DE to Register HL. This adds the relocation offset to the address, performing the address translation.
54C1
EB EB
EXchange the contents of Register DE with Register HL again. Now DE contains the relocated address.
54C2
E1 E1
POP the saved buffer pointer from the stack back into Register HL.
54C3
73 73
Store Register E (low byte of relocated address) at the memory location pointed to by Register HL.
54C4
23 23
Increment Register HL to point to the next byte.
54C5
72 72
Store Register D (high byte of relocated address) at the memory location pointed to by Register HL, completing the write of the relocated address back to the buffer.
54C6
23 23
Increment Register HL to point past the address just written.
54C7
JUMP back to 54A3H to continue processing the next data item in the buffer.
54CAH - Tape Read Complete - Prepare for Destination Selection
This section is reached when the 'x' (78H) completion marker is received from tape, indicating all source data has been successfully read. The code stores the final buffer pointer, loads the start of data buffer at 5E9AH, and calls processing routine 54A3H to perform final data manipulation. It then prepares to prompt the user for the destination (Disk or Tape) by clearing location 58C3H and displaying the destination prompt at 5AF0H.
[TAPE READ COMPLETE] All data has been read from tape. Process the data and prepare for destination selection.
54CA
LD (5374H),HL 22 74 53
Store Register HL at memory locations 5374H-5375H, saving the current buffer pointer position.
54CD
LD HL,5E9AH 21 9A 5E
Load Register HL with 5E9AH, which is the start address of the data buffer (where the tape data was stored).
54D0
CALL subroutine at 54A3H to process the buffered data (likely performing address relocation and data validation).
54D3
AF AF
Exclusive-OR Register A with itself, setting Register A to 00H.
54D4
32C358 32 C3 58
Store 00H at memory location 58C3H, clearing a status or flag variable.
54D7
21F05A 21 F0 5A
Load Register HL with 5AF0H, which points to the message "DESTINATION DISK OR TAPE? (D OR T)" (terminated with 03H).
54DA
JUMP to 5908H to display the destination prompt and get user input.
54DCH - Disk Source Selected - File Specification Prompt
This section is reached when the user selects Disk (pressed 'D') as the source type. It prompts the user to enter the source file specification by displaying "SOURCE FILE SPEC?" at address 5B15H. The routine at 5893H displays the prompt and gets user input, which is then stored at location 58CAH. After input, it proceeds to prompt for the destination type.
[DISK SOURCE SELECTED] User chose Disk as source. Prompt for source filespec.
54DC
LD HL,5B15H 21 15 5B
Load Register HL with 5B15H, which points to the message "SOURCE FILE SPEC?" (terminated with 03H).
54DF
CALL subroutine at 5893H to display the prompt and get user input. This likely stores the entered filespec in a buffer.
54E2
LD (58CAH),A 32 CA 58
Store Register A (presumably the terminator or last character from user input) at memory location 58CAH.
54E5
XOR A AF
Exclusive-OR Register A with itself, setting Register A to 00H.
54E6
LD (58C3H),A 32 C3 58
Store 00H at memory location 58C3H, initializing a flag or status variable to indicate disk source.
54E9
LD HL,5AF0H 21 F0 5A
Load Register HL with 5AF0H, which points to the message "DESTINATION DISK OR TAPE? (D OR T)".
54EC
CALL subroutine at 5893H to display the destination prompt and get user response (D or T).
54EF
JUMP to 5910H to process the destination selection.
54F2H - Process Type 01H Block
This section handles data blocks marked with type 01H. It reads the block length from the buffer, sets up a loop counter in Register B, and implements a byte-copy loop that reads bytes from the source buffer (pointed to by HL) and processes them. The routine appears to handle standard data blocks that require sequential copying with possible relocation offset application.
[TYPE 01H BLOCK PROCESSING] Handle standard data block with byte-by-byte copying.
54F2
LD A,(HL) 7E
Load Register A with the byte at the memory location pointed to by Register HL. This is the block length.
54F3
INC HL 23
Increment Register HL to point to the first data byte.
54F4
LD B,A 47
Load Register B with the block length, setting up the loop counter.
54F5
DEC B 05
Decrement Register B by one. This adjusts the count, possibly because two bytes have already been accounted for.
54F6
DEC B 05
Decrement Register B again, subtracting another byte from the count.
54F7
PUSH DE D5
PUSH Register DE onto the stack to preserve the destination address.
54F8
LD C,B 48
Load Register C with Register B, creating a backup of the loop counter.
[BYTE COPY LOOP] Copy B bytes from buffer to memory, checking each for relocation needs.
54F9
LD A,(HL) 7E
[LOOP START] Load Register A with the byte at the memory location pointed to by Register HL.
54FA
LD (DE),A 12
Store Register A at the memory location pointed to by Register DE, copying the byte to the destination.
54FB
INC HL 23
Increment Register HL to point to the next source byte.
54FC
INC DE 13
Increment Register DE to point to the next destination byte.
54FD
Decrement Register B and JUMP back to 54F9H if Register B is Not Zero. Continue copying until all bytes are transferred. [LOOP END]
54FF
LD B,C 41
Load Register B with the saved count from Register C.
5500
POP DE D1
POP the destination address from the stack back into Register DE.
5501
LD A,(HL) 7E
Load Register A with the next byte from the buffer (the relocation bitmap or count).
5502
INC HL 23
Increment Register HL to advance the buffer pointer.
5503
OR A B7
OR Register A with itself to set flags based on the value. If A is zero, the Z FLAG is set.
5504
If the Z FLAG is set (no relocations needed in this block), JUMP to 54A3H to continue with the next block.
5507
LD C,A 4F
Load Register C with the relocation count from Register A.
[RELOCATION APPLICATION LOOP] Apply relocation offset to C addresses within the block just copied.
5508
PUSH BC C5
[RELOCATION LOOP START] PUSH Register BC onto the stack to preserve the relocation counter.
5509
LD C,(HL) 4E
Load Register C with the byte at (HL), which is the offset within the block where a relocatable address resides.
550A
INC HL 23
Increment Register HL to point to the next relocation offset.
550B
LD B,00H 06 00
Load Register B with 00H, making BC a 16-bit offset value (with the offset in C).
550D
PUSH HL E5
PUSH Register HL onto the stack to preserve the buffer pointer.
550E
PUSH DE D5
PUSH Register DE onto the stack to preserve the block base address.
550F
LD H,D 62
Load Register H with Register D.
5510
LD L,E 6B
Load Register L with Register E, making HL equal to DE (the block base address).
5511
ADD HL,BC 09
Add Register BC (the offset) to Register HL. Register HL now points to the address within the block that needs relocation.
5512
LD E,(HL) 5E
Load Register E with the low byte of the address to be relocated.
5513
INC HL 23
Increment Register HL to point to the high byte.
5514
LD D,(HL) 56
Load Register D with the high byte of the address. Register DE now contains the address to be relocated.
5515
PUSH HL E5
PUSH Register HL onto the stack (points to where the high byte came from).
5516
LD HL,(5378H) 2A 78 53
Load Register HL with the 16-bit relocation offset stored at memory locations 5378H-5379H.
5519
ADD HL,DE 19
Add Register DE (original address) to Register HL (relocation offset), producing the relocated address in HL.
551A
POP DE D1
POP the high byte pointer from the stack into Register DE.
551B
LD (DE),H 72
Store the high byte of the relocated address (Register H) at the location pointed to by Register DE.
551C
DEC DE 15
Decrement Register DE to point to where the low byte should be stored.
551D
LD (DE),L 73
Store the low byte of the relocated address (Register L) at the location pointed to by Register DE, completing the relocation of this address.
551E
POP DE D1
POP the block base address from the stack back into Register DE.
551F
POP HL E1
POP the buffer pointer from the stack back into Register HL.
5520
POP BC C1
POP the relocation counter from the stack back into Register BC.
5521
DEC C 0D
Decrement Register C (the relocation counter).
5522
If Register C is Not Zero (more relocations to process), JUMP back to 5508H to relocate the next address. [RELOCATION LOOP END]
5524
JUMP to 54A3H to continue processing the next block.
5527H - Display Load Address Range and Check for DOS Overlap
This section displays the module load address range to the user in the format "MODULE LOADS TO XXXX-XXXX" where the addresses are shown in hexadecimal. It loads the base address from (537AH), adds it to the module length from (5376H), and displays both addresses. It then checks if the module would overlap with DOS memory (4000H-5FFFH) by comparing the end address. If overlap is detected, it displays the warning message "MODULE LOAD OVERLAPS DOS (4000-5FFF)" and prompts for a new load base address.
[DISPLAY LOAD RANGE] Show the user where the module will load in memory.
5527
LD HL,5B3EH 21 3E 5B
Load Register HL with 5B3EH, which points to the message "MODULE LOADS TO XXXX-XXXX" (with placeholders for the addresses).
552A
CALL subroutine at 58A9H to display the message pointed to by Register HL.
552D
LD HL,(537AH) 2A 7A 53
Load Register HL with the 16-bit value stored at memory locations 537AH-537BH, which is the module base load address.
5530
CALL subroutine at 5A27H to convert the address in HL to hexadecimal ASCII and display or store it.
5533
LD BC,(5376H) ED 4B 76 53
Load Register BC with the 16-bit value stored at memory locations 5376H-5377H, which is the module length.
5537
ADD HL,BC 09
Add Register BC (module length) to Register HL (base address), calculating the end address of the module.
5538
CALL subroutine at 5A27H to convert and display the end address.
553B
LD A,0DH 3E 0D
Load Register A with 0DH (carriage return character).
553D
CALL subroutine at 5893H to output the carriage return, completing the display line.
[DOS OVERLAP CHECK] Verify the module won't overwrite DOS memory (4000H-5FFFH).
5540
LD HL,(537AH) 2A 7A 53
Load Register HL with the module base address from 537AH-537BH.
5543
LD BC,6000H 01 00 60
Load Register BC with 6000H, which is the address just above the DOS memory area (DOS occupies 4000H-5FFFH).
5546
OR A B7
OR Register A with itself to clear the Carry flag, preparing for subtraction.
5547
SBC HL,BC ED 42
Subtract Register BC from Register HL with carry (SBC HL,BC). If the module base address is ≥ 6000H, the result is positive and NC FLAG is set; if < 6000H, the result is negative and Carry flag is set, indicating potential DOS overlap.
5549
If the NC FLAG (No Carry) is set (base address ≥ 6000H, no DOS overlap), JUMP to 556DH to continue without warning.
554C
LD HL,(5376H) 2A 76 53
Load Register HL with the module length from 5376H-5377H.
554F
LD BC,(537AH) ED 4B 7A 53
Load Register BC with the base address from 537AH-537BH.
5553
ADD HL,BC 09
Add Register BC (base address) to Register HL (module length), calculating the end address of the module.
5554
LD BC,4000H 01 00 40
Load Register BC with 4000H, the start of DOS memory.
5557
OR A B7
OR Register A with itself to clear the Carry flag.
5558
SBC HL,BC ED 42
Subtract Register BC from Register HL. If the module end address is ≥ 4000H, there is overlap with DOS memory.
555A
If the Carry flag is set (end address < 4000H, no overlap), JUMP to 556DH to continue.
[DOS OVERLAP DETECTED] Module would overwrite DOS. Display warning and prompt for new address.
555D
LD HL,5B58H 21 58 5B
Load Register HL with 5B58H, which points to the message "MODULE LOAD OVERLAPS DOS (4000-5FFF)".
5560
CALL subroutine at 58A9H to display the overlap warning message.
5563
LD HL,5B7DH 21 7D 5B
Load Register HL with 5B7DH, which points to the message "NEW LOAD BASE ADDRESS (HEX)?".
5566
CALL subroutine at 5893H to display the prompt and get hexadecimal input from the user.
5569
CALL subroutine at 5A1BH to parse the hexadecimal input and convert it to a binary address, storing it as the new base address.
556C
22 7A 53 22 7A 53
Store the new base address from Register HL at memory locations 537AH-537BH, updating the module load address.
556DH - Check for ROM Overlap and Prompt for DOS Disable
After verifying no DOS overlap (or getting a corrected address), this section checks if the module would overlap with ROM memory (below 3000H). If ROM overlap is detected, it displays "MODULE LOAD WILL OVERLAP ROM" and prompts "DISABLE DOS? (Y OR N)". The user's response determines whether DOS will be disabled to allow the module to load. The section also handles checking for appendage suppression after address validation.
[ROM OVERLAP CHECK] Check if module would load into ROM space (< 3000H).
556D
LD HL,(537AH) 2A 7A 53
Load Register HL with the module base address from 537AH-537BH.
5570
LD BC,3000H 01 00 30
Load Register BC with 3000H, the end of ROM space on the TRS-80.
5573
OR A B7
OR Register A with itself to clear the Carry flag.
5574
SBC HL,BC ED 42
Subtract Register BC from Register HL. If base address ≥ 3000H, NC FLAG is set (no ROM overlap); if < 3000H, Carry flag is set (ROM overlap).
5576
If the NC FLAG is set (base address ≥ 3000H, no ROM overlap), JUMP to 5592H to continue with appendage check.
[ROM OVERLAP DETECTED] Module would overwrite ROM. Display warning and prompt about disabling DOS.
5579
LD HL,5B9BH 21 9B 5B
Load Register HL with 5B9BH, which points to the message "MODULE LOAD WILL OVERLAP ROM".
557C
CALL subroutine at 58A9H to display the ROM overlap warning.
557F
JUMP to 55D9H to prompt the user about disabling DOS.
5582H - Process Type FEH Marker (Transfer Address Block)
This section processes blocks marked with type FEH. It reads a 16-bit value from the buffer (pointed to by HL) into Register DE, adds the relocation offset from (5378H), and stores the relocated value at (537AH-537BH). This appears to handle the transfer address or entry point of the module, ensuring it's properly relocated. After processing, it displays the load address range by jumping to 5527H.
[TYPE FEH BLOCK - TRANSFER ADDRESS] Process the module entry point and apply relocation.
5582
LD E,(HL) 5E
Load Register E with the byte at the memory location pointed to by Register HL (low byte of transfer address).
5583
INC HL 23
Increment Register HL to point to the next byte.
5584
LD D,(HL) 56
Load Register D with the byte at the memory location pointed to by Register HL (high byte of transfer address). Register DE now contains the original transfer address.
5585
INC HL 23
Increment Register HL to advance past the address.
5586
PUSH HL E5
PUSH Register HL onto the stack to preserve the buffer pointer.
5587
LD HL,(5378H) 2A 78 53
Load Register HL with the relocation offset from memory locations 5378H-5379H.
558A
ADD HL,DE 19
Add Register DE (original transfer address) to Register HL (relocation offset), producing the relocated entry point address.
558B
LD (537AH),HL 22 7A 53
Store the relocated entry point at memory locations 537AH-537BH. This overwrites the previous base address with the actual entry point.
558E
POP HL E1
POP the buffer pointer from the stack back into Register HL.
558F
JUMP to 5527H to display the module load address range and continue with address validation.
5592H - Appendage Suppression Prompt
After address validation passes, this section prompts the user "SHALL APPENDAGE BE SUPPRESSED? (Y OR N)". The appendage is additional code or data that can be optionally excluded from the relocated module. Based on the user's response (Y or N), the program either suppresses the appendage or includes it in the output. This section then proceeds to either destination disk processing or tape processing based on earlier user selections.
[APPENDAGE SUPPRESSION PROMPT] Ask if optional appendage code should be excluded.
5592
LD HL,5C3FH 21 3F 5C
Load Register HL with 5C3FH, which points to the message "SHALL APPENDAGE BE SUPPRESSED? (Y OR N)".
5595
CALL subroutine at 5893H to display the prompt and get the user's response (Y or N).
5598
CP 59H FE 59
Compare Register A with 59H (ASCII 'Y'). If they are equal, the Z FLAG is set; otherwise the NZ FLAG is set.
559A
If the Z FLAG is set (user pressed 'Y' to suppress appendage), JUMP to 55A1H to handle suppression.
559C
LD HL,0FFFFH 21 FF FF
Load Register HL with FFFFH, a flag value indicating appendage should NOT be suppressed.
559F
JUMP to 55A4H to continue with appendage included.
55A1
LD HL,0000H 21 00 00
Load Register HL with 0000H, a flag value indicating appendage SHOULD be suppressed.
55A4
LD (5372H),HL 22 72 53
Store the appendage suppression flag from Register HL at memory locations 5372H-5373H.
55A7
LD A,(58C3H) 3A C3 58
Load Register A with the byte at memory location 58C3H, which indicates the destination type (00H=disk, 50H=tape).
55AA
OR A B7
OR Register A with itself to set flags. If A is 00H (disk destination), the Z FLAG is set.
55AB
If the Z FLAG is set (destination is disk), JUMP to 567CH to handle disk output processing.
55AE
LD HL,5AA0H 21 A0 5A
Load Register HL with 5AA0H, which points to the message "ANOTHER SOURCE FILE TO PROCESS? (Y OR N)".
55B1
CALL subroutine at 5893H to display the prompt and get user response.
55B4
CP 59H FE 59
Compare Register A with 59H (ASCII 'Y'). If they are equal, the Z FLAG is set.
55B6
If the Z FLAG is set (user wants to process another source file), JUMP to 55C0H to handle continuation.
User does not want to process another source. Finalize tape output and display completion message.
55B9
LD HL,5C27H 21 27 5C
Load Register HL with 5C27H, which points to the message "DONE".
55BC
CALL subroutine at 58A9H to display the completion message.
55BF
RET C9
RETURN to DOS or calling program, ending the LMOFFSET utility.
55C0
LD BC,(537AH) ED 4B 7A 53
Load Register BC with the current entry point address from 537AH-537BH.
55C4
LD HL,(5376H) 2A 76 53
Load Register HL with the module length from 5376H-5377H.
55C7
ADD HL,BC 09
Add Register BC (entry point) to Register HL (module length), calculating a new combined value.
55C8
LD (5376H),HL 22 76 53
Store the result at memory locations 5376H-5377H, updating the length value.
55CB
LD HL,(5378H) 2A 78 53
Load Register HL with the relocation offset from 5378H-5379H.
55CE
ADD HL,BC 09
Add Register BC to Register HL, adjusting the relocation offset.
55CF
LD (5378H),HL 22 78 53
Store the new offset at memory locations 5378H-5379H.
55D2
JUMP back to 53C2H to read and process the next source file from tape.
55D4H - Process Type FFH Marker (End of Data)
This section handles blocks marked with type FFH, which indicates the end of module data. It stores the 16-bit value from the buffer (pointed to by HL) at memory location 5376H-5377H, which represents the final module length or related parameter. After storing this value, it jumps to 5527H to display the load address range and continue with address validation and appendage processing.
[TYPE FFH BLOCK - END OF DATA] Store final module parameters and proceed to display.
55D4
LD (5376H),DE ED 53 76 53
Store the 16-bit value in Register DE at memory locations 5376H-5377H, saving the final module length or end marker value.
55D8
JUMP to 5527H to display the module load address range and continue processing.
55D9H - Appendage Suppression and Base Address Adjustment
This section handles the user's response to the "Suppress Appendage?" prompt. If the user presses 'N' (4EH), the code recalculates the module display and prompts again. If the user presses 'Y' (59H), it sets the appendage suppression flag at 5372H to 00H (suppressing the transfer address and any trailing code) and recalculates. Any other input causes a re-prompt.
55D9
LD (5372H),HL 22 72 53
Store Register HL at memory location 5372H-5373H, updating the appendage suppression flag.
55DC
CP 4EH FE 4E
Compare Register A with 4EH (ASCII 'N' for No). If equal, the Z FLAG is set.
55DE
If the Z FLAG is set (user pressed 'N'), JUMP back to 5550H to recalculate module load addresses and re-display the information without suppressing the appendage.
55E1
CP 59H FE 59
Compare Register A with 59H (ASCII 'Y' for Yes). If equal, the Z FLAG is set.
55E3
If the NZ FLAG is set (user pressed neither 'Y' nor 'N'), JUMP back to 55CFH to re-prompt for appendage suppression.
55E5
INC (HL) 34
Increment the byte at memory location pointed to by Register HL (which is 5372H). This changes the appendage flag from 00H to 01H, indicating appendage should be included.
55E6
JUMP to 5550H to recalculate the module information with the appendage included and continue processing.
55E9H - Read Source Module from Disk and Prepare for Relocation
This section handles reading the source load module from disk. It retrieves the final buffer pointer (saved at 55ECH as 59FFH), calculates buffer boundaries, and enters a loop to read and process the module data blocks. The code reads blocks from disk, identifies them by type markers (01H for data, 02H for transfer address, 20H+ for padding), and processes each accordingly. Type 01H blocks contain relocatable code/data, type 02H contains the entry point, and blocks starting with 20H or higher indicate end of module.
[DISK READ AND RELOCATION PREPARATION] Read source module from disk, process blocks, apply relocation offset.
55E9
CALL subroutine at 5771H. This retrieves the saved buffer pointer: it pushes HL, loads HL from (5374H), gets D from (HL) and E from (HL-1), pops HL, and returns with DE containing the end pointer.
55EC
LD (59FFH),DE ED 53 FF 59
Store Register DE (the buffer end pointer) at memory locations 59FFH-5A00H for later reference.
55F0
LD HL,5E9BH 21 9B 5E
Load Register HL with 5E9BH, pointing to one byte before the actual data buffer start (5E9AH + 1). This will be the starting point for reading module blocks.
[BUFFER BOUNDARY CHECK LOOP] Scan backward to find the actual end of valid data, skipping padding.
55F3
DEC HL 2B
[BOUNDARY SCAN START] Decrement Register HL to point to the previous byte in the buffer.
55F4
LD DE,(5374H) ED 5B 74 53
Load Register DE with the current write pointer from memory locations 5374H-5375H.
55F8
RST 18H DF
Call ROM routine at 0018H (RST 18H) to compare HL with DE. Sets Carry flag if HL < DE.
55F9
If the NC FLAG (No Carry) is set (HL ≥ DE, reached beginning of buffer), JUMP to 563EH to handle completion.
55FB
INC HL 23
Increment Register HL to move forward one byte (undoing the DEC from 55F3H).
55FC
CALL subroutine at 5733H. This reads the byte at (HL) and checks if it's less than 20H (control character/block marker) or ≥ FEH (special marker). Returns with Carry clear if it's a valid block marker.
55FF
If the NC FLAG is set (found a valid block marker), JUMP back to 55F3H to continue scanning backward.
[BLOCK PROCESSING LOOP] Process each block in the module, handling different block types.
5601
INC HL 23
Increment Register HL to point to the block length byte.
5602
LD C,(HL) 4E
Load Register C with the block length from the buffer.
5603
LD B,00H 06 00
Load Register B with 00H, making BC a 16-bit block length value.
5605
INC HL 23
Increment Register HL to point to the first data byte or block type indicator.
5606
CP 02H FE 02
Compare Register A (block type from 5733H) with 02H (transfer address block). If equal, the Z FLAG is set.
5608
If the Z FLAG is set (type 02H block), JUMP to 561BH to handle the transfer address specially.
560A
CP 01H FE 01
Compare Register A with 01H (standard data block). If equal, the Z FLAG is set.
560C
If the Z FLAG is set (type 01H block), JUMP to 5615H to handle standard data block.
Block type is neither 01H nor 02H. Check if the block is empty (length = 0).
560E
INC C 0C
Increment Register C.
560F
DEC C 0D
Decrement Register C. This sets the Z FLAG if C was 00H, without changing the value.
5610
If the NZ FLAG is set (block has length), JUMP to 5613H to skip the block.
5612
INC B 04
Increment Register B, making BC = 0100H (256). This handles empty blocks by treating them as having 256-byte length.
5613
JUMP to 563BH to add BC to HL (skipping this unknown block type) and continue processing.
[TYPE 01H STANDARD DATA BLOCK] Check if block is too short (less than 3 bytes).
5615
LD A,C 79
Load Register A with Register C (the block length).
5616
CP 03H FE 03
Compare Register A with 03H. If A ≥ 03H, the NC FLAG is set; if A < 03H, the Carry flag is set.
5618
If the NC FLAG is set (block length ≥ 3), JUMP to 561BH to process normally.
561A
INC B 04
Increment Register B, making the block length 256+ bytes. This handles malformed short blocks.
[READ LOAD ADDRESS] Get the 16-bit load address for this block from the buffer.
561B
LD E,(HL) 5E
Load Register E with the low byte of the load address from the buffer.
561C
INC HL 23
Increment Register HL to point to the high byte.
561D
LD D,(HL) 56
Load Register D with the high byte of the load address. Register DE now contains the original load address for this block.
561E
PUSH HL E5
PUSH Register HL onto the stack to preserve the buffer pointer.
[ADDRESS RELOCATION CHECK] Determine if this address needs relocation by checking if it's in relocatable memory range.
561F
LD A,D 7A
Load Register A with the high byte of the address to check the memory range.
5620
CP 40H FE 40
Compare Register A with 40H. If A < 40H (address is in ROM area 0000H-3FFFH), the Carry flag is set.
5622
If the Carry flag is set (address < 4000H, in ROM), JUMP to 5637H to write the address back unchanged (ROM addresses are not relocated).
5624
LD HL,(5378H) 2A 78 53
Load Register HL with the minimum address stored at memory locations 5378H-5379H. This is the base address of the original module.
5627
RST 18H DF
Call ROM routine at 0018H to compare HL with DE. Sets Carry flag if HL < DE (address is above minimum).
5628
If the Carry flag is set (DE > minimum, address is in relocatable range), continue to check upper bound. Otherwise fall through.
562A
LD HL,(5376H) 2A 76 53
Load Register HL with the maximum address stored at memory locations 5376H-5377H. This is the end address of the original module.
562D
EX DE,HL EB
EXchange Register DE with Register HL. Now HL contains the block address, DE contains the maximum.
562E
RST 18H DF
Call ROM routine at 0018H to compare HL with DE. Sets Carry flag if HL < DE (address is below maximum).
562F
EX DE,HL EB
EXchange back, restoring original register contents (HL = maximum, DE = block address).
5630
If the Carry flag is set (block address < maximum, within relocatable range), JUMP to 5637H to skip relocation (address is outside the module bounds).
[APPLY RELOCATION OFFSET] Address is within relocatable range. Add the relocation offset.
5632
LD HL,(537AH) 2A 7A 53
Load Register HL with the relocation offset stored at memory locations 537AH-537BH. This is the delta between old and new base addresses.
5635
ADD HL,DE 19
Add Register DE (original address) to Register HL (relocation offset), producing the relocated address in HL.
5636
EX DE,HL EB
EXchange Register DE with Register HL. Now DE contains the relocated address.
[WRITE BACK ADDRESS] Write the (possibly relocated) address back to the buffer.
5637
POP HL E1
POP the buffer pointer from the stack back into Register HL (points to high byte position).
5638
LD (HL),D 72
Store the high byte of the (possibly relocated) address at the buffer position.
5639
DEC HL 2B
Decrement Register HL to point to the low byte position.
563A
LD (HL),E 73
Store the low byte of the (possibly relocated) address at the buffer position.
[ADVANCE TO NEXT BLOCK] Skip past this block's data to find the next block.
563B
ADD HL,BC 09
Add Register BC (block length) to Register HL, advancing the pointer past this entire block.
563C
JUMP back to 55F3H to continue processing the next block in the buffer.
563EH - Relocation Complete - Prompt for Destination
This section is reached when all blocks have been processed and relocation is complete. It checks the appendage suppression flag at (5372H). If appendage is not suppressed (flag != 0), it jumps to 56E6H to prompt for destination. If appendage is suppressed, it prompts the user with "DISABLE DOS? (Y OR N)" to determine if DOS should be disabled for the relocated module. Based on the response, it either sets up for non-DOS operation or proceeds to destination selection.
[RELOCATION PROCESSING COMPLETE] Check appendage flag and proceed accordingly.
563E
LD A,(5372H) 3A 72 53
Load Register A with the appendage suppression flag from memory location 5372H. If A = 00H, appendage is suppressed.
5641
OR A B7
OR Register A with itself to set flags. If A = 00H, the Z FLAG is set.
5642
If the NZ FLAG is set (appendage not suppressed, flag != 0), JUMP to 56E6H to prepare the module header and prompt for destination.
[APPENDAGE SUPPRESSED] Prompt user whether to disable DOS for this module.
5645
LD HL,5BB8H 21 B8 5B
[DOS DISABLE PROMPT LOOP] Load Register HL with 5BB8H, which points to the message "DISABLE DOS? (Y OR N)".
5648
CALL subroutine at 5893H to display the prompt and get user keystroke, returning the character in Register A.
564B
Decrement Register B and JUMP back to 5645H if Register B is Not Zero. This provides retry attempts for invalid responses.
Calculate the adjusted load address range based on whether DOS will be disabled.
564D
LD HL,(5378H) 2A 78 53
Load Register HL with the minimum module address from memory locations 5378H-5379H.
5650
LD DE,(537AH) ED 5B 7A 53
Load Register DE with the relocation offset from memory locations 537AH-537BH.
5654
ADD HL,DE 19
Add Register DE (relocation offset) to Register HL (original min address), producing the new minimum load address.
5655
INC HL 23
Increment Register HL by one byte (adjusting the address).
5656
LD (58C4H),HL 22 C4 58
Store the adjusted address at memory locations 58C4H-58C5H for later use.
5659
SUB 4EH D6 4E
Subtract 4EH (ASCII 'N') from Register A (the user's keystroke). If user pressed 'N', result is 00H and Z FLAG is set. If user pressed 'Y' (59H), result is 0BH.
565B
LD (56D7H),A 32 D7 56
Store the result at memory location 56D7H, encoding the user's DOS disable choice (00H = keep DOS, 0BH = disable DOS).
565E
If the Z FLAG is set (user pressed 'N', keep DOS enabled), JUMP to 568FH to continue with DOS-enabled module creation.
5660
CP 0BH FE 0B
Compare Register A with 0BH. If A = 0BH (user pressed 'Y'), the Z FLAG is set.
5662
If the NZ FLAG is set (user pressed neither 'Y' nor 'N'), JUMP back to 5645H to re-prompt.
[DOS DISABLED MODE] User chose to disable DOS. Calculate memory layout for non-DOS operation and set up bootstrap pointers.
5664
LD DE,005EH 11 5E 00
Load Register DE with 005EH (94 decimal), an offset for the non-DOS bootstrap code section.
5667
ADD HL,DE 19
Add Register DE to Register HL, advancing the pointer by 94 bytes.
5668
LD (5912H),HL 22 12 59
Store this address at memory locations 5912H-5913H. This becomes the destination address for a LDIR operation in the bootstrap code.
566B
LD DE,0006H 11 06 00
Load Register DE with 0006H, the next offset.
566E
ADD HL,DE 19
Add Register DE to Register HL, advancing 6 more bytes.
566F
LD (591DH),HL 22 1D 59
Store this address at memory locations 591DH-591EH. This becomes another destination in the bootstrap.
5672
LD DE,005BH 11 5B 00
Load Register DE with 005BH (91 decimal).
5675
ADD HL,DE 19
Add Register DE to Register HL, advancing 91 more bytes.
5676
LD (5987H),HL 22 87 59
Store this address at memory locations 5987H-5988H.
5679
LD DE,005FH 11 5F 00
Load Register DE with 005FH (95 decimal).
567C
ADD HL,DE 19
Add Register DE to Register HL, advancing 95 more bytes.
567D
LD (5928H),HL 22 28 59
Store this address at memory locations 5928H-5929H.
5680
LD DE,0019H 11 19 00
Load Register DE with 0019H (25 decimal).
5683
ADD HL,DE 19
Add Register DE to Register HL, advancing 25 more bytes.
5684
LD (59E9H),HL 22 E9 59
Store this address at memory locations 59E9H-59EAH. This points to where the bootstrap code will copy data.
5687
LD (5907H),HL 22 07 59
Store the same address at memory locations 5907H-5908H for a second reference in the bootstrap.
568A
LD HL,58C3H 21 C3 58
Load Register HL with 58C3H, pointing to a status/mode flag location.
568D
LD (HL),C1H 36 C1
Store C1H at location 58C3H, setting a flag to indicate DOS-disabled mode with special bootstrap requirements.
568FH - Calculate Module Size and Prepare Output Header
This section calculates the actual size of the relocated module by subtracting the minimum address from the maximum address. It then stores this length at 59FAH. The code also determines the appropriate load module header byte (B0H for normal, B8H for wraparound), calculates the maximum load address, and stores all these values in preparation for writing the output file. Finally, it retrieves the saved buffer pointer, subtracts 4 to position before the module data, writes 68 bytes (44H) of header information starting from 58C2H, and prepares to write the actual module data.
[CALCULATE MODULE SIZE] Determine the length of the relocated module.
568F
LD DE,(5376H) ED 5B 76 53
Load Register DE with the maximum address from memory locations 5376H-5377H.
5693
LD HL,(5378H) 2A 78 53
Load Register HL with the minimum address from memory locations 5378H-5379H.
5696
XOR A AF
Exclusive-OR Register A with itself, setting A to 00H and clearing the Carry flag.
5697
SBC HL,DE ED 52
Subtract Register DE (maximum) from Register HL (minimum) with borrow. This calculates HL = minimum - maximum. If the result is negative, the Carry flag is set.
5699
If the Carry flag is set (minimum < maximum, which is normal), JUMP to 5850H to display "SOURCE MODULE BAD FORMAT" error and abort.
569C
LD B,H 44
Load Register B with Register H (high byte of module size).
569D
LD C,L 4D
Load Register C with Register L (low byte of module size). Register BC now contains the module length.
569E
INC HL 23
Increment Register HL (adjust the size calculation by 1).
569F
LD (59FAH),HL 22 FA 59
Store the adjusted module size at memory locations 59FAH-59FBH for later reference.
[DETERMINE LOAD TYPE] Check if module will wrap around memory and set appropriate header byte.
56A2
LD DE,(5376H) ED 5B 76 53
Load Register DE with the maximum address again.
56A6
LD HL,(537AH) 2A 7A 53
Load Register HL with the relocation offset from memory locations 537AH-537BH.
56A9
LD A,B0H 3E B0
Load Register A with B0H, the standard CMD file type byte for normal load modules.
56AB
ADD HL,DE 19
Add Register DE (max address) to Register HL (relocation offset), calculating the new maximum address. If this causes overflow past FFFFH, the Carry flag is set.
56AC
If the NC FLAG is set (no overflow, normal load), JUMP to 56B4H to continue with type B0H.
56AE
LD A,B8H 3E B8
Load Register A with B8H, the CMD file type byte for wraparound load modules that span across the FFFF/0000 boundary.
56B0
ADD HL,BC 09
Add Register BC (module size) to Register HL to adjust the maximum address calculation.
56B1
EX DE,HL EB
EXchange Register DE with Register HL. Now DE contains the adjusted maximum.
56B2
ADD HL,BC 09
Add Register BC to Register HL again.
56B3
EX DE,HL EB
EXchange back to restore DE.
[STORE OUTPUT PARAMETERS] Save the load type, maximum address, and size for output file generation.
56B4
LD (59FDH),A 32 FD 59
Store the load type byte (B0H or B8H) at memory location 59FDH.
56B7
LD (59F4H),HL 22 F4 59
Store Register HL (calculated maximum or parameter) at memory locations 59F4H-59F5H.
56BA
LD (59F7H),DE ED 53 F7 59
Store Register DE (maximum load address) at memory locations 59F7H-59F8H.
[PREPARE OUTPUT BUFFER] Position buffer pointer before module data and write header information.
56BE
LD HL,(5374H) 2A 74 53
Load Register HL with the current buffer write pointer from memory locations 5374H-5375H.
56C1
LD DE,FFFCH 11 FC FF
Load Register DE with FFFCH (-4 in two's complement), a negative offset.
56C4
ADD HL,DE 19
Add Register DE (-4) to Register HL, moving the pointer back 4 bytes to position before the module data.
56C5
LD (5374H),HL 22 74 53
Store the adjusted pointer back at memory locations 5374H-5375H.
56C8
LD B,44H 06 44
Load Register B with 44H (68 decimal), the number of header bytes to copy.
56CA
LD HL,58C2H 21 C2 58
Load Register HL with 58C2H, pointing to the source of header data to be written.
56CD
CALL subroutine at 5470H to write B bytes from memory at (HL) to the output buffer. This copies the 68-byte header to the buffer.
56D0
EX DE,HL EB
EXchange Register DE with Register HL. DE now points to the position after the header write.
56D1
LD HL,59F3H 21 F3 59
Load Register HL with 59F3H, pointing to additional data to write.
56D4
LD B,12H 06 12
Load Register B with 12H (18 decimal), the count for the next write operation.
56D6
LD A,00H 3E 00
Load Register A with 00H. This value was stored at location 56D7H earlier based on the DOS disable choice.
56D8
OR A B7
OR Register A with itself to set flags. If A = 00H, the Z FLAG is set.
56D9
If the Z FLAG is set (DOS is NOT disabled, normal mode), JUMP to 56E3H to write the standard header continuation.
[DOS DISABLED MODE HEADER] Write extended bootstrap code for non-DOS operation.
56DB
EX DE,HL EB
EXchange Register DE with Register HL, swapping source and destination for the extended write.
56DC
LD B,7FH 06 7F
Load Register B with 7FH (127 decimal), the size of the bootstrap code section.
56DE
CALL subroutine at 5470H to write 127 bytes of bootstrap code to the buffer.
56E1
LD B,80H 06 80
Load Register B with 80H (128 decimal), preparing for another write.
56E3
CALL subroutine at 5470H to write B bytes (18 for normal mode, 128 for DOS-disabled mode) to complete the header.
56E6H - Set Disk Output Mode and Prompt for Destination
This section sets a flag at 5373H to 01H indicating disk output mode is active, then displays the "DESTINATION DISK OR TAPE? (D OR T)" prompt at 5AF0H. It waits for the user to press 'D' for Disk or 'T' for Tape. If 'T' is selected, it jumps to tape output handling at 577AH. If 'D' is selected, it prompts for the destination filespec and proceeds to disk writing. Any other input causes a re-prompt with retry counter.
56E6
LD HL,5373H 21 73 53
Load Register HL with 5373H, pointing to the disk output mode flag.
56E9
LD (HL),01H 36 01
Store 01H at location 5373H, setting the flag to indicate disk output mode is active.
56EB
LD HL,5AF0H 21 F0 5A
[DESTINATION PROMPT LOOP] Load Register HL with 5AF0H, pointing to the message "DESTINATION DISK OR TAPE? (D OR T)".
56EE
CALL subroutine at 5893H to display the prompt and get user keystroke.
56F1
Decrement Register B and JUMP back to 56EBH if Register B is Not Zero, providing retry attempts.
56F3
CP 54H FE 54
Compare Register A with 54H (ASCII 'T' for Tape). If equal, the Z FLAG is set.
56F5
If the Z FLAG is set (user selected Tape), JUMP to 577AH to handle tape output.
56F8
CP 44H FE 44
Compare Register A with 44H (ASCII 'D' for Disk). If equal, the Z FLAG is set.
56FA
If the NZ FLAG is set (user pressed neither 'D' nor 'T'), JUMP back to 56E6H to re-prompt.
[DISK DESTINATION SELECTED] Prompt for destination filespec and open output file.
56FC
LD HL,5B27H 21 27 5B
[FILESPEC PROMPT LOOP] Load Register HL with 5B27H, pointing to the message "DESTINATION FILE SPEC?".
56FF
CALL subroutine at 5521H. This displays the prompt, gets user input, validates the filespec format, and returns Z FLAG set if cancelled/invalid.
5702
If the Z FLAG is set (invalid filespec or user cancelled), JUMP back to 56FCH to re-prompt.
5704
CALL 4420H CD 20 44
CALL DOS routine at 4420H. This opens the destination file for writing. Returns with Z FLAG set on success, NZ on error.
5707
If the NZ FLAG is set (file open error), JUMP to 5855H to display the DOS error message and retry.
LMOFFSET Disassembly Section: 570AH-573BH
570AH - Write Module Data Blocks to Disk
After successfully opening the destination file, this section switches to the alternate register set (which maintains the disk write buffer state) and begins processing module data blocks from the buffer at 5E9BH. It reads each block header, identifies the block type (01H for data, 02H for transfer address, or other markers), and writes the appropriate output. The routine at 5733H checks each byte to determine if it's a valid block marker, and blocks are processed sequentially until all data has been written to disk.
[SWITCH TO DISK WRITE MODE] Use alternate register set for disk buffer management.
570A
EXX D9
EXchange register sets. Switch to the alternate register set (BC', DE', HL') which maintains the disk write buffer pointer and sector byte counter during the write operation.
[DISK WRITE MAIN LOOP] Process each block from buffer and write to disk.
570B
LD HL,5E9BH 21 9B 5E
Load Register HL with 5E9BH, pointing to the start of the module data buffer where relocated blocks are stored.
570E
[BLOCK SCAN LOOP] CALL subroutine at 5733H. This checks if the byte at (HL) is a valid block marker: returns with Carry clear if byte < 20H (valid marker), or Carry set if ≥ 20H (padding/end). For bytes ≥ FEH, it reads the block length and skips past the entire block.
5711
If the NC FLAG is set (valid block marker found), JUMP back to 570EH to continue scanning for the next valid block marker.
[PROCESS BLOCK BY TYPE] Valid block found at current position. Check its type.
5713
CP 01H FE 01
Compare Register A (block type byte from 5733H) with 01H (standard data block). If equal, the Z FLAG is set.
5715
If the Z FLAG is set (type 01H data block), JUMP to 5721H to write this data block to disk.
5717
CP 02H FE 02
Compare Register A with 02H (transfer address block). If equal, the Z FLAG is set.
5719
If the Z FLAG is set (type 02H transfer address), JUMP to 5744H to write the transfer address and finalize the output file.
[UNKNOWN BLOCK TYPE] Block is neither 01H nor 02H. Write one byte and skip past it.
571C
CALL subroutine at 575CH to read one byte from the buffer and write it to disk, returning the byte count in Register B.
571F
JUMP to 572CH to write the remaining bytes of this unknown block type.
5721H - Write Type 01H Data Block to Disk
This section handles type 01H data blocks, which contain the actual relocatable module code and data. It calls 575CH to read and write the block type byte (01H), then decrements Register B twice to account for the 2-byte load address that will be written separately. It calls 5760H twice to write the load address bytes (low byte, then high byte), then enters a loop calling 5760H repeatedly to write all remaining data bytes in the block. After all bytes are written, it loops back to 570EH to process the next block.
[WRITE TYPE 01H DATA BLOCK] Output block marker, address, and all data bytes.
5721
CALL subroutine at 575CH. This reads the block type byte (01H) from the source buffer and writes it to disk, returning the block length in Register B.
5724
DEC B 05
Decrement Register B by 1, subtracting the load address low byte from the remaining byte count.
5725
DEC B 05
Decrement Register B again, subtracting the load address high byte. Register B now contains the count of actual data bytes to write (block length - 2).
5726
CALL subroutine at 5760H to read one byte from the source buffer and write it to the disk buffer. This writes the load address low byte.
5729
CALL subroutine at 5760H again to write the load address high byte.
[DATA BYTE WRITE LOOP] Write all data bytes in this block.
572C
[LOOP START] CALL subroutine at 5760H to read one data byte from the source buffer and write it to the disk buffer.
572F
Decrement Register B (data byte counter) and JUMP back to 572CH if Register B is Not Zero. Continue until all data bytes in this block have been written. [LOOP END]
5731
JUMP back to 570EH to scan for and process the next block in the buffer.
5733H - Check Block Marker and Skip Invalid Blocks
This utility subroutine examines the byte at (HL) to determine if it's a valid block marker. Bytes less than 20H (space) are considered valid block type markers (01H, 02H, etc.) and the routine returns with Carry clear and the byte in Register A. Bytes from 20H to FDH are considered padding or invalid and return with Carry set. Bytes FEH and FFH are special: the routine reads the following 2-byte length value, adds it to HL to skip past the entire block, clears Register A, and returns with Carry clear. This allows the main loop to efficiently skip over blocks that aren't needed (like error markers from tape reads).
5733
LD A,(HL) 7E
Load Register A with the byte at the memory location pointed to by Register HL (potential block marker).
5734
CP 20H FE 20
Compare Register A with 20H (space character). If A < 20H (control character/block marker), the Carry flag is set.
5736
RET C D8
If the Carry flag is set (byte < 20H, valid block marker), RETURN immediately with the block type in Register A and Carry flag set.
Byte is ≥ 20H. Check if it's a special skip marker (FEH or FFH).
5737
CP FEH FE FE
Compare Register A with FEH. If A < FEH (normal padding 20H-FDH), the Carry flag is set.
5739
If the Carry flag is set (byte is 20H-FDH, padding or invalid), JUMP to 5846H to display "BAD MAIN MEMORY" error. This indicates data corruption in the buffer.
[SKIP BLOCK MARKER FEH/FFH] Special markers that need to be skipped. Read length and advance past entire block.
573C
INC HL 23
Increment Register HL to point to the block length low byte.
573D
LD E,(HL) 5E
Load Register E with the block length low byte.
573E
INC HL 23
Increment Register HL to point to the length high byte.
573F
LD D,(HL) 56
Load Register D with the block length high byte. Register DE now contains the 16-bit block length.
5740
INC HL 23
Increment Register HL to point to the first byte of the block data.
5741
ADD HL,DE 19
Add Register DE (block length) to Register HL, advancing the pointer past the entire block, effectively skipping it.
5742
XOR A AF
Exclusive-OR Register A with itself, setting Register A to 00H and clearing the Carry flag.
5743
RET C9
RETURN to the calling routine with Register A = 00H and Carry clear, indicating a block was skipped and the caller should check the next position.
5744H - Write Transfer Address Block and Finalize File
This section writes the type 02H transfer address block to the disk output. It calls 575CH twice to read and write the transfer address bytes (2 bytes), then switches to the alternate register set to check if there are any buffered bytes waiting to be written. If Register B (in the alternate set) is non-zero, it calls 443CH to flush the partial sector buffer. Finally, it calls 4428H to close the output file, switches back to the primary register set, displays the "DONE" message from 5C27H, and jumps to completion handling at 585FH.
[WRITE TRANSFER ADDRESS] Output the 2-byte entry point to the file.
5744
CALL subroutine at 575CH. This calls 5760H to read one byte from (HL) into A and write it to the disk buffer at (HL'), then stores that byte count in Register B.
5747
CALL subroutine at 575CH again to write the second byte of the transfer address.
[FLUSH BUFFER AND CLOSE FILE] Write any remaining buffered data and close the output file.
574A
EXX D9
EXchange register sets. Switch to the alternate register set (BC', DE', HL') where the disk write buffer pointer and count are maintained.
574B
LD A,B 78
Load Register A with Register B' (from alternate set), which contains the count of buffered bytes not yet written to disk.
574C
OR A B7
OR Register A with itself to set flags. If A = 00H (no buffered bytes), the Z FLAG is set.
574D
If the Z FLAG is set (buffer is empty), JUMP to 5752H to skip the buffer flush and close the file.
574F
CALL 443CH CD 3C 44
CALL DOS routine at 443CH to flush the partial sector from the write buffer to disk, ensuring all data is written.
5752
CALL 4428H CD 28 44
CALL DOS routine at 4428H to close the output file, finalizing the write operation and updating the directory.
5755
EXX D9
EXchange register sets. Switch back to the primary register set (BC, DE, HL).
[COMPLETION MESSAGE] Display "DONE" message and proceed to finish.
5756
LD HL,5C27H 21 27 5C
Load Register HL with 5C27H, which points to the message "DONE" + 0DH (carriage return).
5759
JUMP to 585FH to display the completion message and prompt for processing another file.
575CH - Read Byte Count and Write Single Byte
This short subroutine calls 5760H to read a byte and get its count in Register A, then stores that count in Register B for the caller's use. It's a wrapper that provides the byte count in two places (A and B) for different calling conventions.
575C
CALL subroutine at 5760H to read one byte from the source buffer and write it to the disk buffer.
575F
LD B,A 47
Load Register B with Register A, copying the byte count for the caller.
5760H - Read Source Byte and Write to Disk Buffer
This critical subroutine reads one byte from the source buffer (pointed to by HL in the primary register set), switches to the alternate register set, writes that byte to the disk output buffer (pointed to by HL'), increments the buffer pointer, decrements the sector byte counter (B'), and checks if a complete 256-byte sector has been filled. If the counter reaches zero (sector full), it calls 443CH to write that sector to disk, returning any error via the NZ flag and jumping to error handler 5855H. It also decrements H' to wrap the buffer pointer within a 256-byte page. The routine returns the byte in Register A.
5760
LD A,(HL) 7E
Load Register A with the byte at the memory location pointed to by Register HL (in primary set), reading from the source buffer.
5761
INC HL 23
Increment Register HL to point to the next source byte.
5762
EXX D9
EXchange register sets. Switch to the alternate register set where the disk write buffer state is maintained.
5763
LD (HL),A 77
Store Register A (the byte just read) at the memory location pointed to by Register HL' (in alternate set), writing to the disk buffer.
5764
INC HL 23
Increment Register HL' to point to the next position in the disk buffer.
5765
Decrement Register B' (sector byte counter) and JUMP to 576EH if Register B' is Not Zero. If B' reaches zero, a complete 256-byte sector is ready to write.
[SECTOR FULL - WRITE TO DISK] Buffer contains 256 bytes. Write the sector and check for errors.
5767
CALL 443CH CD 3C 44
CALL DOS routine at 443CH to write the 256-byte sector from the buffer to disk. Returns with Z FLAG set on success, NZ on error.
576A
If the NZ FLAG is set (disk write error), JUMP to 5855H to display the DOS error message and handle the error.
576D
DEC H 25
Decrement Register H' by one. This wraps the buffer pointer back by 256 bytes (since the buffer is page-aligned), effectively resetting it to the start of the same 256-byte page for the next sector.
576E
EXX D9
EXchange register sets. Switch back to the primary register set.
576F
LD A,(HL) 7E
Load Register A with the byte at (HL) again (which is now the NEXT byte due to INC HL at 5761H), preparing the return value.
5770
RET C9
RETURN to the calling routine with the byte in Register A.
5771H - Retrieve Buffer End Pointer
This utility subroutine retrieves a 16-bit pointer value that was stored at the end of the buffer area. It preserves HL on the stack, loads HL with the buffer pointer from (5374H), reads the 16-bit value in reverse order (high byte at (HL), low byte at (HL-1)) into Register DE, restores HL, and returns. This is used to get the saved end-of-buffer marker.
5771
PUSH HL E5
PUSH Register HL onto the stack to preserve its value.
5772
LD HL,(5374H) 2A 74 53
Load Register HL with the buffer pointer from memory locations 5374H-5375H.
5775
LD D,(HL) 56
Load Register D with the byte at (HL), getting the high byte of the stored pointer.
5776
DEC HL 2B
Decrement Register HL to point to the previous byte.
5777
LD E,(HL) 5E
Load Register E with the byte at (HL-1), getting the low byte. Register DE now contains the 16-bit pointer value.
5778
POP HL E1
POP the saved Register HL from the stack, restoring its original value.
5779
RET C9
RETURN to the calling routine with the retrieved pointer in Register DE.
577AH - Tape Output - Get Module Name
This section handles tape output. It prompts the user to input a 1-6 character tape module name using the message at 5C79H ("INPUT 1-6 CHAR TAPE MODULE NAME"). The code validates that the first character is A-Z and subsequent characters are A-Z or 0-9, as per cassette tape naming conventions. It reads up to 6 characters, converting lowercase to uppercase, and validates each character against the allowed character sets. If validation fails, it re-prompts. Once a valid name is entered, it stores the name at 5E95H, pads to 6 characters with spaces, and prepares for tape write operations.
[TAPE MODULE NAME INPUT] Prompt for and validate tape module name.
577A
LD HL,5C79H 21 79 5C
[NAME INPUT LOOP] Load Register HL with 5C79H, pointing to the message "INPUT 1-6 CHAR TAPE MODULE NAME." + 0AH.
577D
CALL subroutine at 5893H to display the prompt and get user input string.
5780
If the Z FLAG is set (user cancelled with BREAK or entered empty string), JUMP back to 577AH to re-prompt.
5782
LD A,B 78
Load Register A with Register B, which contains the length of the entered string.
5783
CP 07H FE 07
Compare Register A with 07H. If A ≥ 07H (name is 7+ characters, too long), the NC FLAG is set.
5785
If the NC FLAG is set (name too long), JUMP back to 577AH to re-prompt for a valid name length.
[NAME VALIDATION AND STORAGE] Validate character set and store the name padded to 6 characters.
5787
LD DE,5E95H 11 95 5E
Load Register DE with 5E95H, the destination address where the tape module name will be stored.
578A
LD C,06H 0E 06
Load Register C with 06H, the required tape name length (6 characters). C will track how many padding spaces are needed.
578C
PUSH DE D5
PUSH Register DE onto the stack to save the start address of the name storage.
[CHARACTER COPY AND VALIDATION LOOP] Copy each character, validating it meets tape naming requirements.
578D
LD A,(HL) 7E
[CHAR COPY LOOP] Load Register A with the next character from the input string.
578E
LD (DE),A 12
Store the character at the destination location pointed to by Register DE.
578F
INC DE 13
Increment Register DE to point to the next storage position.
5790
INC HL 23
Increment Register HL to point to the next input character.
Validate character is in allowed set: 0-9 for non-first characters, A-Z for all positions.
5791
SUB 30H D6 30
Subtract 30H (ASCII '0') from Register A. If A was '0'-'9', result is 00H-09H; if A was 'A', result would be 11H.
5793
CP 0AH FE 0A
Compare Register A with 0AH. If A < 0AH (character was '0'-'9'), the Carry flag is set.
5795
If the Carry flag is set (character is a digit '0'-'9'), JUMP to 579BH to check if it's allowed at this position.
Character is not a digit. Check if it's uppercase A-Z.
5797
SUB 11H D6 11
Subtract 11H from Register A. If A was 'A' (which became 11H after -30H), result is 00H. If 'Z' (1AH), result is 09H.
5799
CP 1AH FE 1A
Compare Register A with 1AH (26 decimal). If A < 1AH (character was 'A'-'Z'), the Carry flag is set.
579B
If the Carry flag is set (character is valid 'A'-'Z'), JUMP to 57A3H to continue.
Character might be lowercase a-z. Check and convert if so.
579D
SUB 20H D6 20
Subtract 20H (the difference between uppercase and lowercase in ASCII) from Register A.
579F
CP 1AH FE 1A
Compare Register A with 1AH. If A ≥ 1AH (character was NOT lowercase 'a'-'z'), the NC FLAG is set.
57A1
If the NC FLAG is set (character is invalid - not a digit, not uppercase, not lowercase letter), JUMP back to 577AH to re-prompt for a valid name.
[CHARACTER VALIDATED] Decrement padding counter and continue or pad with spaces.
57A3
DEC C 0D
Decrement Register C (padding counter), accounting for this character.
57A4
If the Z FLAG is set (6 characters entered, no padding needed), JUMP to 57AFH to proceed with tape write.
57A6
Decrement Register B (remaining input characters) and JUMP back to 578DH if more characters remain to be copied.
[PAD WITH SPACES] User entered fewer than 6 characters. Fill remaining positions with spaces.
57A8
LD A,20H 3E 20
[PADDING LOOP] Load Register A with 20H (ASCII space character).
57AA
LD (DE),A 12
Store the space character at the destination.
57AB
DEC C 0D
Decrement Register C (padding counter).
57AC
INC DE 13
Increment Register DE to the next position.
57AD
If the NZ FLAG is set (more padding needed), JUMP back to 57A8H to write another space. Continue until all 6 positions are filled.
57AFH - Prepare Tape Write and Prompt User
After the tape module name is validated and stored, this section calls 583AH to prepare the tape system, then displays the message "REPLY ENTER WHEN TAPE POSITIONED AND IN RECORD MODE" at 5CC2H. It waits for the user to press a key (ignoring the specific key pressed), then disables interrupts (DI) and calls the ROM cassette write initialization routine at 0284H. It displays an asterisk at the top-right of the screen (3C3FH) as a record indicator, then writes the sync byte 55H followed by the 6-character module name to tape.
[TAPE SYSTEM PREPARATION] Initialize tape I/O and prompt user to position tape.
57AF
CALL subroutine at 583AH. This appears to be a placeholder that immediately returns (RET), used for tape preparation hooks.
57B2
LD HL,5CC2H 21 C2 5C
[TAPE READY PROMPT] Load Register HL with 5CC2H, pointing to "REPLY ENTER WHEN TAPE POSITIONED AND IN RECORD MODE".
57B5
CALL subroutine at 5893H to display the message and wait for any keystroke from the user.
57B8
If the NZ FLAG is set (which shouldn't happen as 5893H returns with keystroke in A), JUMP back to 57B2H. This appears to be a safety check.
[BEGIN TAPE WRITE] Disable interrupts and initialize tape write sequence.
57BA
DI F3
Disable Interrupts to ensure precise timing for tape write operations.
57BB
CALL ROM routine at 0284H. This initializes the cassette write system, setting up timing and activating the cassette motor.
57BE
LD A,2AH 3E 2A
Load Register A with 2AH (ASCII asterisk '*').
57C0
LD (3C3FH),A 32 3F 3C
Store the asterisk at video RAM location 3C3FH (top-right corner, column 63), providing visual feedback that tape recording is active.
57C3
LD A,55H 3E 55
Load Register A with 55H (ASCII 'U'). This is the tape sync byte that marks the start of a tape block.
57C5
CALL ROM routine at 0264H to write the byte in Register A (55H) to cassette tape.
[WRITE MODULE NAME] Write the 6-character module name to tape.
57C8
POP HL E1
POP the module name address (5E95H) from the stack into Register HL.
57C9
LD B,06H 06 06
Load Register B with 06H, the loop counter for writing 6 name characters.
57CB
LD A,(HL) 7E
[NAME WRITE LOOP] Load Register A with the next name character.
57CC
INC HL 23
Increment Register HL to point to the next character.
57CD
CALL ROM routine at 0264H to write this character to tape.
57D0
Decrement Register B and JUMP back to 57CBH if more name characters remain to be written.
57D2H - Write Module Data Blocks to Tape
This section implements the main tape write loop that processes all module data blocks from the buffer at 5E9BH. It reads each block type byte and handles them accordingly: type 01H blocks contain data with optional relocation bitmaps, type 02H blocks contain the transfer address (entry point). For type 01H blocks, it writes the '<' continuation marker, the block length, load address, and all data bytes, computing a checksum as it goes. After all data blocks, it toggles the screen indicator and continues reading. The loop processes blocks sequentially until reaching the end marker or type 02H transfer address block.
[TAPE WRITE MAIN LOOP] Process each block from buffer and write to tape with checksums.
57D2
LD HL,5E9BH 21 9B 5E
[BLOCK PROCESSING LOOP] Load Register HL with 5E9BH, pointing to the start of the module data buffer.
57D5
LD A,(HL) 7E
[BLOCK TYPE CHECK] Load Register A with the block type byte from the buffer.
57D6
CP 01H FE 01
Compare Register A with 01H (standard data block). If equal, the Z FLAG is set.
57D8
If the Z FLAG is set (type 01H data block), JUMP to 57F0H to write this data block to tape.
57DA
CP 02H FE 02
Compare Register A with 02H (transfer address block). If equal, the Z FLAG is set.
57DC
If the Z FLAG is set (type 02H transfer address), JUMP to 5827H to write the transfer address and finalize tape output.
[UNKNOWN BLOCK TYPE] Block is neither 01H nor 02H. Skip it by reading its length and advancing past it.
57DE
CALL subroutine at 5733H. This checks if the byte at (HL) is a valid block marker (< 20H or ≥ FEH), and if it's an end marker (>= FEH), it skips to the next block by reading the block length and adding it to HL.
57E1
If the NC FLAG is set (valid block marker found), JUMP back to 57D5H to check this block's type.
If Carry is set, we've reached padding or end of data. Advance past this block.
57E3
INC HL 23
Increment Register HL to point to the block length byte.
57E4
LD C,(HL) 4E
Load Register C with the block length.
57E5
LD B,00H 06 00
Load Register B with 00H, making BC a 16-bit length value.
57E7
INC HL 23
Increment Register HL to point past the length byte.
57E8
LD A,C 79
Load Register A with Register C (the block length).
57E9
OR A B7
OR Register A with itself. If A = 00H (empty block), the Z FLAG is set.
57EA
If the NZ FLAG is set (block has length), JUMP to 57EDH to skip past it.
57EC
INC B 04
Increment Register B, making BC = 0100H (256), handling zero-length as 256-byte block.
57ED
ADD HL,BC 09
Add Register BC (block length) to Register HL, skipping past this entire block.
57EE
JUMP back to 57D5H to check the next block.
57F0H - Write Type 01H Data Block to Tape
This section writes a type 01H data block to tape with full checksum validation. It first writes the '<' (3CH) continuation marker, then reads the block length from (HL+1), subtracts 2 to account for the load address bytes, and writes this adjusted length. It then reads the 16-bit load address and writes both bytes, accumulating them in the checksum (Register C). The code then enters a loop to write all data bytes, adding each to the checksum. After all data bytes are written, it writes the final checksum byte to tape, re-enables interrupts briefly via 583BH, toggles the screen indicator, and loops back to process the next block.
[WRITE DATA BLOCK HEADER] Write '<' marker, adjusted length, and load address.
57F0
LD A,3CH 3E 3C
Load Register A with 3CH (ASCII '<'), the data block continuation marker.
57F2
CALL ROM routine at 0264H to write the '<' marker to tape.
57F5
INC HL 23
Increment Register HL to point to the block length byte.
57F6
LD B,(HL) 46
Load Register B with the block length from the buffer.
57F7
DEC B 05
Decrement Register B by 1 (subtracting address low byte from count).
57F8
DEC B 05
Decrement Register B again (subtracting address high byte from count). B now contains the actual data byte count.
57F9
INC HL 23
Increment Register HL to point to the load address low byte.
57FA
LD A,B 78
Load Register A with the data byte count.
57FB
CALL ROM routine at 0264H to write the data byte count to tape.
57FE
LD E,(HL) 5E
Load Register E with the load address low byte.
57FF
INC HL 23
Increment Register HL to point to the high byte.
5800
LD D,(HL) 56
Load Register D with the load address high byte. Register DE now contains the complete load address.
5801
LD A,E 7B
Load Register A with the low byte of the address.
5802
INC HL 23
Increment Register HL to point to the first data byte.
5803
CALL ROM routine at 0264H to write the address low byte to tape.
5806
LD A,D 7A
Load Register A with the high byte of the address.
5807
CALL ROM routine at 0264H to write the address high byte to tape.
[INITIALIZE CHECKSUM] Begin checksum calculation with the two address bytes.
580A
LD A,D 7A
Load Register A with the high byte again.
580B
ADD A,E 83
Add Register E (low byte) to Register A, starting the checksum accumulation.
580C
LD C,A 4F
Load Register C with the checksum. Register C will accumulate the running checksum.
[DATA WRITE LOOP WITH CHECKSUM] Write each data byte to tape and add it to the checksum.
580D
LD A,(HL) 7E
[DATA BYTE LOOP] Load Register A with the next data byte from the buffer.
580E
CALL ROM routine at 0264H to write this data byte to tape.
5811
LD A,(HL) 7E
Load Register A with the same byte again (for checksum calculation).
5812
ADD A,C 81
Add Register C (current checksum) to Register A (data byte), accumulating the checksum.
5813
LD C,A 4F
Load Register C with the updated checksum value.
5814
INC HL 23
Increment Register HL to point to the next data byte.
5815
Decrement Register B (data byte counter) and JUMP back to 580DH if more data bytes remain. [LOOP END]
[WRITE CHECKSUM AND UPDATE INDICATOR] Write the final checksum byte and toggle screen indicator.
5817
CALL ROM routine at 0264H to write the checksum byte (in Register C, loaded into A by 0264H's calling convention) to tape.
581A
CALL subroutine at 583BH. This briefly re-enables interrupts if the BREAK key is pressed, allowing user to abort.
581D
LD A,(3C3FH) 3A 3F 3C
Load Register A with the indicator character from video RAM location 3C3FH (top-right corner).
5820
XOR 0AH EE 0A
Exclusive-OR Register A with 0AH, toggling certain bits to animate the indicator.
5822
LD (3C3FH),A 32 3F 3C
Store the modified indicator character back to video RAM, updating the display.
5825
JUMP back to 57D5H to process the next block in the buffer.
5827H - Write Transfer Address and Complete Tape Output
This section is reached when a type 02H transfer address block is encountered, signaling the end of the module data. It writes the 'x' (78H) completion marker to tape, then skips the block type and length bytes by incrementing HL twice. It reads the two transfer address bytes from the buffer and writes them to tape. Finally, it jumps to 5756H to display the "DONE" message and complete the operation.
[WRITE COMPLETION MARKER] Signal end of module with 'x' marker.
5827
LD A,78H 3E 78
Load Register A with 78H (ASCII 'x'), the tape completion marker indicating all module data has been written.
5829
CALL ROM routine at 0264H to write the 'x' completion marker to tape.
[WRITE TRANSFER ADDRESS] Write the 2-byte entry point address.
582C
INC HL 23
Increment Register HL to skip past the block type byte (02H).
582D
INC HL 23
Increment Register HL again to skip past the block length byte.
582E
LD A,(HL) 7E
Load Register A with the first byte of the transfer address (low byte).
582F
INC HL 23
Increment Register HL to point to the second byte.
5830
CALL ROM routine at 0264H to write the transfer address low byte to tape.
5833
LD A,(HL) 7E
Load Register A with the second byte of the transfer address (high byte).
5834
CALL ROM routine at 0264H to write the transfer address high byte to tape.
[TAPE OUTPUT COMPLETE] All data written. Display completion message.
5837
JUMP to 5756H to display "DONE" message and prompt for processing another file.
583AH - Tape Preparation Hook (Placeholder)
This is a placeholder subroutine for tape system preparation. It simply returns immediately, allowing the code structure to call this address for initialization hooks without actually executing any code. This may be used for hardware-specific setup in modified versions of the utility.
583A
RET C9
RETURN immediately. This is a placeholder for tape preparation routines.
583BH - Check BREAK Key and Allow Abort
This subroutine checks if the BREAK key is currently pressed by reading keyboard memory location 3840H and testing bit 3. If the BREAK key is not pressed, it returns immediately. If BREAK is pressed, it loads HL with 5CF6H (pointing to "FUNCTION CANCELLED BY OPERATOR" message) and falls through to 585FH to display the message and exit gracefully, allowing the user to abort long tape operations.
583B
LD A,(3840H) 3A 40 38
Load Register A with the keyboard status byte from memory-mapped location 3840H. This reads the keyboard row containing the BREAK key.
583E
AND 08HAND 00001000 E6 08
AND Register A with 08H (bit 3 mask). If bit 3 is set (BREAK key pressed), result is non-zero; if bit 3 is clear (BREAK not pressed), result is zero and Z FLAG is set.
5840
RET Z C8
If the Z FLAG is set (BREAK key NOT pressed), RETURN immediately without taking any action.
[BREAK KEY PRESSED] User wants to abort. Display cancellation message.
5841
LD HL,5CF6H 21 F6 5C
Load Register HL with 5CF6H, which points to the message "FUNCTION CANCELLED BY OPERATOR".
5844
JUMP to 585FH to display the cancellation message and exit gracefully.
5846H - Error Handler - Bad Main Memory
This error handler is reached when a memory validation check fails. It loads HL with 5C69H pointing to "BAD MAIN MEMORY" message and jumps to 585FH to display it. This indicates that memory tests detected corruption or hardware problems during the relocation process.
5846
LD HL,5C69H 21 69 5C
Load Register HL with 5C69H, pointing to the error message "BAD MAIN MEMORY" + 0DH.
5849
JUMP to 585FH to display the error message and handle completion.
584BH - Error Handler - Illegal Load Address
This error handler is reached when the module specifies an invalid or illegal load-to-memory address. It loads HL with 5C01H pointing to "SOURCE ILLEGAL LOAD-TO-MEMORY ADDRESS" message and jumps to 585FH to display it.
584B
LD HL,5C01H 21 01 5C
Load Register HL with 5C01H, pointing to the error message "SOURCE ILLEGAL LOAD-TO-MEMORY ADDRESS" + 0DH.
584E
JUMP to 585FH to display the error message and handle completion.
5850H - Error Handler - Bad Module Format
This error handler is reached when the source module has an invalid or corrupted format that cannot be processed. It loads HL with 5BD0H pointing to "SOURCE MODULE BAD FORMAT" message and falls through to 585FH to display it.
5850
LD HL,5BD0H 21 D0 5B
Load Register HL with 5BD0H, pointing to the error message "SOURCE MODULE BAD FORMAT" + 0DH.
5853
JUMP to 585FH to display the error message and handle completion.
5855H - DOS Error Handler
This error handler is reached when a DOS operation (file open, read, write, close) returns an error code. It takes the error code in Register A, sets bit 6 and bit 7 (OR C0H) to form the DOS error number, calls DOS routine 4409H to convert the error code to an error message, then falls through to display the message. This provides user-friendly error messages for all disk I/O failures.
5855
OR 0C0HOR 11000000 F6 C0
OR Register A (DOS error code) with C0H, setting bits 6 and 7 to form the standard DOS error number format.
5857
CALL 4409H CD 09 44
CALL DOS routine at 4409H. This converts the DOS error code in Register A to a human-readable error message and loads HL with the message address.
585A
JUMP to 5862H to skip the insufficient RAM error handler and proceed to display the message.
585CH - Error Handler - Insufficient RAM Memory
This error handler is reached when the buffer write pointer exceeds the available memory limit, indicating insufficient RAM to complete the relocation. It loads HL with 5BE9H pointing to "INSUFFICIENT RAM MEMORY" message and falls through to display it and handle the error.
585C
LD HL,5BE9H 21 E9 5B
Load Register HL with 5BE9H, pointing to the error message "INSUFFICIENT RAM MEMORY" + 0DH.
585FH - Display Message and Prompt for Another Operation
This is the common completion/error message display routine. It calls DOS routine 4467H to display the message pointed to by HL, then calls 01F8H to reset DOS, and re-enables interrupts with EI. After displaying the message, it checks the disk output mode flag at (5373H). If disk mode was active (flag = 01H), it displays "ANOTHER DESTINATION FILE? (Y OR N)" and processes the response. If 'Y', it loops back to destination prompting at 56E6H. If 'N' or disk mode wasn't active, it displays "ANOTHER SOURCE FILE TO PROCESS? (Y OR N)". If 'Y', it returns to the program entry point at 537CH to start over. If 'N', it exits to DOS at 402DH.
585F
CALL DOS routine at 4467H to display the message pointed to by Register HL on the screen.
5862
CALL ROM routine at 01F8H to reset DOS system, cleaning up any open files and resetting DOS variables.
5865
EI FB
Enable Interrupts, restoring normal system operation after any tape operations that disabled interrupts.
[CHECK FOR DESTINATION CONTINUATION] If disk output mode was active, prompt for another destination file.
5866
LD A,(5373H) 3A 73 53
Load Register A with the disk output mode flag from memory location 5373H.
5869
OR A B7
OR Register A with itself to set flags. If A = 00H (tape mode or error before destination), the Z FLAG is set.
586A
If the Z FLAG is set (not in disk output mode), JUMP to 587FH to skip destination continuation prompt.
[PROMPT FOR ANOTHER DESTINATION] Disk output was active. Ask if user wants to write to another destination file.
586C
LD HL,5A70H 21 70 5A
[ANOTHER DESTINATION LOOP] Load Register HL with 5A70H, pointing to "*********** ANOTHER DESTINATION FILE? (Y OR N)".
586F
CALL subroutine at 5893H to display the prompt and get user response.
5872
Decrement Register B and JUMP back to 5866H if Register B is Not Zero, providing retry attempts for invalid responses.
5874
CP 4EH FE 4E
Compare Register A with 4EH (ASCII 'N' for No). If equal, the Z FLAG is set.
5876
If the Z FLAG is set (user doesn't want another destination file), JUMP to 587FH to prompt for another source file.
5878
CP 59H FE 59
Compare Register A with 59H (ASCII 'Y' for Yes). If equal, the Z FLAG is set.
587A
If the NZ FLAG is set (user pressed neither 'Y' nor 'N'), JUMP back to 586CH to re-prompt.
587C
User pressed 'Y'. JUMP to 56E6H to prompt for destination type (Disk or Tape) and write the module to another destination.
[PROMPT FOR ANOTHER SOURCE FILE] Ask if user wants to process a different source file.
587F
LD HL,5AA0H 21 A0 5A
[ANOTHER SOURCE LOOP] Load Register HL with 5AA0H, pointing to "ANOTHER SOURCE FILE TO PROCESS? (Y OR N)".
5882
CALL subroutine at 5893H to display the prompt and get user response.
5885
Decrement Register B and JUMP back to 587FH if Register B is Not Zero, providing retry attempts.
5887
CP 59H FE 59
Compare Register A with 59H (ASCII 'Y' for Yes). If equal, the Z FLAG is set.
5889
If the Z FLAG is set (user wants to process another file), JUMP to 537CH, the program entry point, to start the entire process over.
588C
CP 4EH FE 4E
Compare Register A with 4EH (ASCII 'N' for No). If equal, the Z FLAG is set.
588E
If the NZ FLAG is set (user pressed neither 'Y' nor 'N'), JUMP back to 587FH to re-prompt.
[EXIT TO DOS] User finished processing files. Return to DOS command prompt.
5890
JP 402DH C3 2D 40
JUMP to DOS warm boot entry point at 402DH, exiting the utility and returning control to the operating system.
5893H - Display Message and Get User Input
This versatile subroutine displays a message and gets user keyboard input. It calls 4467H to display the message pointed to by HL, sets up a 64-byte input buffer at 5332H, then calls ROM routine 0040H in a loop to wait for keystrokes. It processes each keystroke through 583BH (which checks for BREAK key abort), converts lowercase to uppercase, and continues until ENTER (0DH) is pressed. It returns with the character count in Register B, the last character in Register A, and Z FLAG set if the input was just ENTER (empty string). The routine handles BREAK key detection for user abort capability.
5893
CALL DOS routine at 4467H to display the message pointed to by Register HL.
5896
LD HL,5332H 21 32 53
Load Register HL with 5332H, pointing to the input buffer where user input will be stored.
5899
LD A,40H 3E 40
Load Register A with 40H (64 decimal), the maximum input buffer size.
589B
LD B,A 47
[ALTERNATE ENTRY POINT] Load Register B with the buffer size from Register A. This provides an alternate entry at 589BH that allows callers to specify a custom buffer size.
589C
PUSH HL E5
[INPUT LOOP START] PUSH Register HL onto the stack to save the current buffer position.
589D
PUSH BC C5
PUSH Register BC onto the stack to save the buffer size.
589E
CALL ROM routine at 0040H to wait for a keystroke and return it in Register A with echo to screen.
58A1
PUSH AF F5
PUSH Register AF onto the stack to preserve the keystroke.
58A2
CALL subroutine at 583BH to check if BREAK key is pressed, allowing user abort.
58A5
POP AF F1
POP the keystroke from the stack back into Register AF.
58A6
LD A,B 78
Load Register A with Register B (the character count/remaining buffer space).
58A7
POP BC C1
POP the saved buffer size from the stack back into Register BC.
58A8
POP HL E1
POP the saved buffer position from the stack back into Register HL.
58A9
[ALTERNATE ENTRY POINT - DISPLAY ONLY] If the Carry flag is set (error condition from 0040H or keystroke not ready), JUMP back to 589CH to wait for another keystroke. This entry point at 58A9H can also be used to just display a message without getting input.
[CONVERT LOWERCASE TO UPPERCASE] Process the keystroke, converting any lowercase letters to uppercase.
58AB
PUSH HL E5
PUSH Register HL onto the stack to preserve the buffer position during character conversion.
58AC
LD B,A 47
Load Register B with Register A (the character count), saving it.
58AD
LD A,(HL) 7E
[UPPERCASE CONVERSION LOOP] Load Register A with the character at the current buffer position.
58AE
SUB 61H D6 61
Subtract 61H (ASCII 'a') from Register A. If A was 'a'-'z', result is 00H-19H.
58B0
CP 1AH FE 1A
Compare Register A with 1AH (26 decimal). If A < 1AH (was lowercase letter), the Carry flag is set.
58B2
If the NC FLAG is set (character was NOT lowercase 'a'-'z'), JUMP to 58B7H to skip conversion.
58B4
ADD 41H C6 41
Add 41H (ASCII 'A') to Register A, converting lowercase to uppercase.
58B6
LD (HL),A 77
Store the converted uppercase character back at the buffer position.
58B7
LD A,(HL) 7E
Load Register A with the character at (HL) (either converted or original).
58B8
CP 0DH FE 0D
Compare Register A with 0DH (carriage return/ENTER key). If equal, the Z FLAG is set.
58BA
INC HL 23
Increment Register HL to point to the next buffer position.
58BB
If the NZ FLAG is set (character was NOT ENTER), JUMP back to 58ADH to check the next character in the buffer for lowercase conversion.
[INPUT COMPLETE] ENTER was pressed. Return with character count and last character.
58BD
POP HL E1
POP the original buffer start address from the stack back into Register HL.
58BE
LD A,(HL) 7E
Load Register A with the first character of the input (or 0DH if empty).
58BF
CP 0DH FE 0D
Compare Register A with 0DH. If equal (empty input), the Z FLAG is set; otherwise NZ FLAG is set.
58C1
RET C9
RETURN to the calling routine with Register B containing the character count, Register A containing the first/last character, and flags set: Z if empty input, NZ if input was entered.
58C2H-58C5H - Data Area and Utility Subroutines
This section contains variable storage, bootstrap code templates for DOS-disabled mode, and utility subroutines for hexadecimal conversion. The area from 58C2H includes configuration bytes and parameters that get copied into the output file header. This is followed by bootstrap code sections (5906H-5A02H) that are used when creating non-DOS executable modules, and hex conversion routines (5A05H-5A3DH) for displaying addresses and converting user input.
[MODULE HEADER DATA] Configuration bytes that become part of the output file header.
58C2
LD BC,0050H 01 50 00
Data: 01 50 00. This appears to be initialization data, possibly a load address or parameter.
58C5
NOP 00
Data byte: 00H.
Additional data area and working storage continues through this section, followed by bootstrap templates and utility routines.
5906H - Bootstrap Code for DOS-Disabled Module (Template 1)
This section contains bootstrap code that gets embedded in the output file when DOS is disabled (flag C1H at 58C3H). This code executes after the module loads and performs system initialization. It clears the video RAM (3C00H-3FFFH, 1024 bytes), then copies 6 bytes from address 0000H to 42E9H (likely interrupt vectors or system parameters), loads HL with 0000H, calls a DOS display routine at 4467H, then enters an infinite loop at 5922H. This bootstrap ensures the relocated module can run in a non-DOS environment.
[BOOTSTRAP TEMPLATE 1 - CLEAR VIDEO RAM] Initialize display memory.
5906
LD DE,0000H 11 00 00
Load Register DE with 0000H. This will be the source address for the LDIR operation (filling with zeros from ROM area).
5909
LD HL,3C00H 21 00 3C
Load Register HL with 3C00H, the start of video RAM on the TRS-80.
590C
LD BC,0400H 01 00 04
Load Register BC with 0400H (1024 decimal), the size of video RAM in bytes.
590F
LDIR ED B0
Load, Increment, Repeat: Copy BC bytes from (HL) to (DE), incrementing both pointers. This clears the entire video display (1024 bytes from 3C00H-3FFFH).
[COPY SYSTEM PARAMETERS] Transfer 6 bytes of configuration data.
5911
LD HL,0000H 21 00 00
Load Register HL with 0000H, pointing to the source address in ROM or low memory.
5914
LD DE,42E9H 11 E9 42
Load Register DE with 42E9H, the destination address in DOS memory area.
5917
LD BC,0006H 01 06 00
Load Register BC with 0006H (6 bytes), the number of bytes to copy.
591A
LDIR ED B0
Load, Increment, Repeat: Copy 6 bytes from (HL) at 0000H to (DE) at 42E9H. This transfers system parameters or vectors.
[DISPLAY MESSAGE AND HALT] Show message and enter infinite loop.
591C
LD HL,0000H 21 00 00
Load Register HL with 0000H. This address will be replaced during module generation with the actual message address.
591F
CALL DOS routine at 4467H to display the message pointed to by Register HL.
5922
JUMP to itself (infinite loop). This halts execution after displaying the message, waiting for user intervention.
[PADDING AND JUMP TEMPLATE] Additional space and jump instruction template.
5924
NOP 00
No operation. Padding byte.
5925
NOP 00
No operation. Padding byte.
5926
NOP 00
No operation. Padding byte.
5927
JP 0000H C3 00 00
JUMP to address 0000H. This address will be replaced with the actual module entry point during generation.
592AH - User Instruction Messages for DOS-Disabled Bootstrap
This section contains the text messages that appear when a DOS-disabled module boots. These instructions guide the user through the process of activating non-disk BASIC, initializing it, entering SYSTEM mode, and then executing the relocated module. The messages are stored as ASCII text with control characters for screen clearing (1CH + 1FH) and line feeds (0AH). The DEFM statements represent "DEFine Message" data.
592A
DEFM 1CH + 1FH
Clear the screen
592C-593D
DEFM "DO THE FOLLOWING:" + 0AH + 0AH
593F-597A
DEFM "1. HOLD DOWN BREAK KEY AND PRESS RESET TO ACTIVATE NON-DISK BASIC." + 0AH
597B-5984
DEFM " BASIC." + 0AH
5985H - Bootstrap Code Continuation
This section contains additional bootstrap code parameters and user instruction messages that continue from the previous section.
5985
LD BC,007AH 01 7A 00
Load Register BC with 007AH (122 decimal). This appears to be a parameter or length value used in the bootstrap.
5988
NOP 00
No operation. Padding or placeholder byte.
5989-59C7
DEFM "2. RELEASE BREAK KEY AND INPUT BASIC INITIALIZATION RESPONSES." + 0AH
59C8-59DA
DEFM "3. ENTER \"SYSTEM\" /" + 0AH
59DB-59E7
DEFM "4. ENTER \"/" + 0DH
59E8H - Bootstrap Code Template 2
This section contains a second bootstrap code template that performs memory copying operations. It first clears video RAM (similar to template 1), then copies a variable-length block of memory from one location to another (both addresses and length are filled in at module generation time), and finally jumps to the module's entry point. This template is used for modules that need to relocate themselves or copy initialization data before execution.
59E8
Data byte 21
Data: 21H. This byte appears to be part of a partially formed instruction or data.
59E9
NOP 00
No operation or data byte 00H.
59EA
NOP 00
No operation or data byte 00H.
[BOOTSTRAP TEMPLATE 2 - CLEAR VIDEO RAM] Initialize display memory.
59EB
LD DE,3C00H 11 00 3C
Load Register DE with 3C00H, the start of video RAM (used as destination).
59EE
LD BC,0400H 01 00 04
Load Register BC with 0400H (1024 decimal), the size of video RAM.
59F1
LDIR ED B0
Load, Increment, Repeat: Copy 1024 bytes from (HL) to (DE). HL should be set to point to a zeroed area to clear video RAM.
[COPY MODULE DATA] Transfer the relocated module code.
59F3
LD HL,0000H 21 00 00
Load Register HL with 0000H. This will be replaced with the actual source address during module generation.
59F6
LD DE,0000H 11 00 00
Load Register DE with 0000H. This will be replaced with the actual destination address.
59F9
LD BC,0000H 01 00 00
Load Register BC with 0000H. This will be replaced with the actual byte count to copy.
59FC
LDIR ED B0
Load, Increment, Repeat: Copy BC bytes from (HL) to (DE), relocating the module to its final memory location.
[JUMP TO MODULE] Transfer control to the relocated module's entry point.
59FE
JP 0000H C3 00 00
JUMP to address 0000H. This will be replaced with the module's actual entry point address, starting execution of the relocated program.
5A01H - Data Area and Configuration Bytes
This section contains configuration bytes and data storage used during the module relocation process. These bytes are read and written by the main program to store temporary values, flags, and calculated addresses.
5A01
LD (BC),A 02
Data byte: 02H. This may be a type code or configuration value.
5A02
LD (BC),A 02
Data byte: 02H. Duplicate or related configuration value.
5A03
NOP 00
Data byte: 00H.
5A04
NOP 00
Data byte: 00H.
5A05H - Convert Hexadecimal String to 16-Bit Binary
This utility subroutine converts a hexadecimal string (pointed to by DE) into a 16-bit binary value in Register HL. It processes B hex digits (typically 4 for addresses), validating each character as 0-9 or A-F. The routine shifts HL left 4 bits for each digit and adds the new digit value. For digits 0-9, it subtracts 30H; for A-F, it subtracts 37H (30H + 7). If any character is invalid or the digit count is too large, it returns with Carry flag set to indicate an error. This routine is used to parse user-entered hexadecimal addresses.
5A05
LD B,A 47
Load Register B with Register A (the number of hex digits to convert), setting up the loop counter.
5A06
ADD FBH C6 FB
Add FBH (-5 in two's complement) to Register A. This validates the digit count: if B > 4, the result will be ≥ 00H and Carry will be clear.
5A08
RET C D8
If the Carry flag is set (digit count is valid, ≤ 4), continue. Otherwise RETURN with error. Note: This logic seems inverted - if B > 4, Carry would be clear and we'd continue, which seems wrong. This may be a bounds check with unusual logic.
[INITIALIZE CONVERSION] Start with HL = 0000H.
5A09
LD HL,0000H 21 00 00
Load Register HL with 0000H, initializing the result accumulator.
[HEX DIGIT CONVERSION LOOP] Process each hex digit, shifting and accumulating.
5A0C
ADD HL,HL 29
[LOOP START] Add Register HL to itself (shift left 1 bit). This is the first of four shifts to make room for the next hex digit.
5A0D
ADD HL,HL 29
Shift left 1 bit (second shift).
5A0E
ADD HL,HL 29
Shift left 1 bit (third shift).
5A0F
ADD HL,HL 29
Shift left 1 bit (fourth shift). HL is now shifted left by 4 bits total, ready for the next hex digit (0-F).
Read next character and validate it as a hex digit.
5A10
LD A,(DE) 1A
Load Register A with the next character from the hex string pointed to by Register DE.
5A11
SUB 30H D6 30
Subtract 30H (ASCII '0') from Register A. If A was '0'-'9', result is 00H-09H; if ':', result is 0AH; if 'A', result is 11H.
5A13
CP 0AH FE 0A
Compare Register A with 0AH. If A < 0AH (character was '0'-'9', valid digit), the Carry flag is set.
5A15
INC DE 13
Increment Register DE to point to the next character in the string.
5A16
If the Carry flag is set (digit was '0'-'9'), JUMP to 5A20H to add this digit to HL.
Character was not '0'-'9'. Check if it's 'A'-'F'.
5A18
SUB 11H D6 11
Subtract 11H from Register A. If A was 'A' (which became 11H after -30H), result is 00H. If 'F' (16H), result is 05H. If ':', result becomes F9H.
5A1A
CP 06H FE 06
Compare Register A with 06H. If A < 06H (was 'A'-'F', now 00H-05H), the Carry flag is set.
5A1C
CCF 3F
Complement Carry Flag. Inverts the Carry flag. If Carry was set (valid A-F), it becomes clear. If Carry was clear (invalid character), it becomes set.
5A1D
RET C D8
If the Carry flag is set (invalid character, not 0-9 or A-F), RETURN with error.
5A1E
ADD 0AH C6 0A
Add 0AH (10 decimal) to Register A. This converts the A-F value (00H-05H) to the correct hex digit value (0AH-0FH for A-F).
[ACCUMULATE DIGIT] Add the hex digit value to the low nibble of HL.
5A20
OR L B5
OR Register L with Register A. This adds the hex digit value (0-F) to the low 4 bits of HL.
5A21
LD L,A 6F
Load Register L with the result from Register A.
5A22
Decrement Register B (digit counter) and JUMP back to 5A0CH if more digits remain to process. [LOOP END]
5A24
RET C9
RETURN to the calling routine with the converted 16-bit value in Register HL and Carry clear (success).
5A25H - Convert 16-Bit Binary to Hexadecimal ASCII String
This utility subroutine converts a 16-bit binary value in Register DE into a 4-character hexadecimal ASCII string, storing the result at the memory location pointed to by HL. It processes the high byte (D) first via a CALL to 5A2AH, then processes the low byte (E). The routine at 5A2AH handles one byte by calling 5A33H twice: once for the high nibble (after 4 right rotations) and once for the low nibble. Each nibble is converted using a BCD adjustment algorithm that produces ASCII hex digits '0'-'9' or 'A'-'F'.
5A25
LD A,D 7A
Load Register A with Register D (the high byte of the 16-bit value to convert).
5A26
CALL subroutine at 5A2AH to convert the high byte to two ASCII hex characters and store them at (HL) and (HL+1).
5A29
LD A,E 7B
Load Register A with Register E (the low byte of the 16-bit value).
Fall through to 5A2AH to convert the low byte.
5A2AH - Convert Single Byte to Two Hex ASCII Characters
This subroutine converts one byte (in Register A) into two ASCII hex characters. It saves the byte on the stack, rotates it right 4 times to get the high nibble, converts that nibble to ASCII via 5A33H (storing at (HL)), increments HL, restores the original byte, and converts the low nibble to ASCII. Each nibble conversion uses the BCD adjustment trick: add 90H, decimal adjust, add 40H with carry, decimal adjust again. This produces '0'-'9' for values 0-9 and 'A'-'F' for values 10-15.
5A2A
PUSH AF F5
PUSH Register AF onto the stack to preserve the byte value.
[CONVERT HIGH NIBBLE] Rotate right 4 times to move high nibble to low position.
5A2B
RRCA 0F
Rotate Right Circular Accumulator. Rotate Register A right 1 bit (first rotation).
5A2C
RRCA 0F
Rotate right 1 bit (second rotation).
5A2D
RRCA 0F
Rotate right 1 bit (third rotation).
5A2E
RRCA 0F
Rotate right 1 bit (fourth rotation). The high nibble is now in bits 0-3 of Register A.
5A2F
CALL subroutine at 5A33H to convert the nibble (0-F) to an ASCII character ('0'-'9' or 'A'-'F') and store it at (HL), then increment HL.
5A32
POP AF F1
POP the original byte from the stack back into Register AF.
Fall through to 5A33H to convert the low nibble.
5A33H - Convert Single Nibble to ASCII Hex Character
This clever subroutine converts a nibble value (0-F in the low 4 bits of Register A) into its ASCII hex representation ('0'-'9' or 'A'-'F'). It uses the Z80's Decimal Adjust Accumulator (DAA) instruction in a creative way: mask to 4 bits, add 90H, DAA, add 40H with carry, DAA again. This produces ASCII '0' (30H) through '9' (39H) for input 0-9, and 'A' (41H) through 'F' (46H) for input 10-15. The result is stored at (HL) and HL is incremented.
5A33
AND 0FHAND 00001111 E6 0F
AND Register A with 0FH, masking to keep only the low 4 bits (the nibble value 0-F).
5A35
ADD 90H C6 90
Add 90H to Register A. For values 0-9, this produces 90H-99H. For A-F (0AH-0FH), this produces 9AH-9FH.
5A37
DAA 27
Decimal Adjust Accumulator. This BCD adjustment converts 90H-99H to 90H-99H (no change), but converts 9AH-9FH to A0H-A5H (adds 6 to high nibble).
5A38
ADC 40H CE 40
Add with Carry 40H to Register A. For 90H-99H, this produces D0H-D9H (no carry). For A0H-A5H, this produces E0H-E5H + carry.
5A3A
DAA 27
Decimal Adjust Accumulator again. For D0H-D9H (from digits 0-9), DAA adds 6 to high nibble producing 30H-39H (ASCII '0'-'9'). For E1H-E6H with carry (from digits A-F), DAA produces 41H-46H (ASCII 'A'-'F').
5A3B
LD (HL),A 77
Store the ASCII hex character at the memory location pointed to by Register HL.
5A3C
INC HL 23
Increment Register HL to point to the next character position.
5A3D
RET C9
RETURN to the calling routine.
5A3EH - Data Area and Program Messages
This section contains two data bytes followed by all the text messages used by the LMOFFSET utility. The messages are stored as ASCII text with terminators: 0DH (carriage return) for display messages and 03H for input prompts. The DEFM (DEFine Message) statements represent the actual text strings stored in memory.
5A3E-5A3F
DB 1CH + 1FH 1C 1F
Clear the screen
[PROGRAM MESSAGES] All text messages used by the LMOFFSET utility.
5A40-5A6F
DEFM "APPARAT LOAD MODULE OFFSET PROGRAM, VERSION 2.0" + 0DH
5A70-5A9F
DEFM "*********** ANOTHER DESTINATION FILE? (Y OR N) " + 03H
5AA0-5ACA
DEFM "ANOTHER SOURCE FILE TO PROCESS? (Y OR N) " + 03H
5ACB-5AEF
DEFM "SOURCE FROM DISK OR TAPE? (D OR T) " + 03H
5AF0-5B14
DEFM "DESTINATION DISK OR TAPE? (D OR T) " + 03H
5B15-5B26
DEFM "SOURCE FILE SPEC? " + 03H
5B27-5B3D
DEFM "DESTINATION FILE SPEC? " + 03H
5B3E-5B57
DEFM "MODULE LOADS TO XXXX-XXXX" + 0DH
5B58-5B7C
DEFM "MODULE LOAD OVERLAPS DOS (4000-5FFF)" + 0DH
5B7D-5B9A
DEFM "NEW LOAD BASE ADDRESS (HEX)? " + 03H
5B9B-5BB7
DEFM "MODULE LOAD WILL OVERLAP ROM" + 0DH
5BB8-5BCF
DEFM "DISABLE DOS? (Y OR N) " + 03H
5BD0-5BE8
DEFM "SOURCE MODULE BAD FORMAT" + 0DH
5BE9-5C00
DEFM "INSUFFICIENT RAM MEMORY" + 0DH
5C01-5C26
DEFM "SOURCE ILLEGAL LOAD-TO-MEMORY ADDRESS" + 0DH
5C27-5C2B
DEFM "DONE" + 0DH
5C2C-5C3E
DEFM "ENTRY POINT = XXXX" + 0DH
5C3F-5C68
DEFM "SHALL APPENDAGE BE SUPPRESSED? (Y OR N) " + 03H
5C69-5C78
DEFM "BAD MAIN MEMORY" + 0DH
5C79-5C99
DEFM "INPUT 1-6 CHAR TAPE MODULE NAME." + 0AH
5C9A-5CC1
DEFM "1ST CHAR MUST BE A-Z; OTHERS A-Z OR 0-9" + 0DH
5CC2-5CF5
DEFM "REPLY ENTER WHEN TAPE POSITIONED AND IN RECORD MODE" + 03H
5CF6-5D14
DEFM "FUNCTION CANCELLED BY OPERATOR" + 0DH
5D15H - Initialize Display and Return Title Message Address
This small utility subroutine is called at program initialization. It stores 50H at location 58C3H (initializing a mode flag), loads HL with 5A3EH (the address of the program title "APPARAT LOAD MODULE OFFSET PROGRAM, VERSION 2.0"), and returns. The calling code then uses this address to display the title message.
5D15
LD A,50H 3E 50
Load Register A with 50H.
5D17
LD (58C3H),A 32 C3 58
Store 50H at memory location 58C3H, initializing a mode/status flag for normal (non-DOS-disabled) operation.
5D1A
Load Register HL with 5A3EH, the address of the program title message.
5D1D
RET C9
RETURN to the calling routine with the title message address in Register HL.
5D1EH-5D81H - Padding Area (End of Program)
This section contains 100 bytes of NOP instructions (00H) that pad out the program to its final size. This padding ensures the program occupies a complete block and may provide space for future modifications or patches. The program ends at 5D81H, with the entry point documented as 537CH.
[PADDING - 100 NOP INSTRUCTIONS] Program padding to final size.
5D1E
NOP 00
No operation. Padding byte 1 of 100.
5D1F
NOP 00
No operation. Padding byte 2 of 100.
5D20
NOP 00
No operation. Padding byte 3 of 100.
... (97 more NOP instructions follow, addresses 5D21H through 5D81H) ...
5D7F
NOP 00
No operation. Padding byte 98 of 100.
5D80
NOP 00
No operation. Padding byte 99 of 100.
5D81
NOP 00
No operation. Padding byte 100 of 100. END OF PROGRAM.
PROGRAM ENTRY POINT = 537CH