TRS-80 DOS - NEWDOS/80 v2.0 for the Model I - SYS2/SYS Disassembled

Page Customization

Introduction/Summary

NEWDOS/80 v2.0 SYS2/SYS Disassembly - Open, Load, Allocate and RENAME (Model I)

SYS2/SYS is the overlay that opens files. It loads into the overlay area at 4D00H-51ACH (1,197 bytes, transfer address 4D00H) and is called with LD A,code / RST 28H; the SYS0 dispatcher (4BC2H) loads it when 4317H does not already hold its number (04H). Besides the two opens (4424H and 4420H) it loads and runs programs (4430H and 4433H), allocates granules when a file grows (called by SYS0 from inside a read or write), encodes passwords, and does the directory work of RENAME and of running a program named on the command line for SYS1.

The page shows every byte of the file once, in address order. 51ADH-51FFH, after the end of the file, is SYS2's work space (see Variables).

RST 28H Codes Handled by SYS2

CodeCalled fromFunction
24H4424HOpen an existing file (4E2EH).
44H4420HOpen a file, creating it if it does not exist (4DBDH).
64HSYS0 48BDHAllocate granules to a file whose read or write has gone past its last granule (4F82H).
84H-Encode an 8-character password into its 16-bit code (5155H). SYS2 uses the routine itself at 4E84H.
A4H4430HLoad a program file (4D92H).
C4H4433HLoad a program file and run it, or enter DEBUG first if the DEBUG command was given (4D80H).
E4H, C = 1SYS1 (RENAME)Rename the open file at DE to the filespec at 51E0H (4D32H).
E4H, C = 2-Find a free directory entry on drive D and give it a HIT code (50CAH). SYS2 and SYS6 call 50CFH directly.
E4H, C = 3SYS1 4E96HRun the program named by the command word (4D7BH).
E4H, C = 4-Create a new file; error 35H if it exists (4D72H).
anything elseSYS6 568DH (C = FFH)Error 2AH (4D2EH). SYS6 uses this to load SYS2 without running anything and then calls 4E21H, 4F2EH and 50CFH itself.

A dash means that no caller was found in SYS0, SYS1 or SYS3-SYS10.

How a File Is Opened

  1. Filespec. NAME/EXT.PASSWORD:d is copied to 51CDH-51DFH (5121H): each field starts with a letter and holds letters and digits; lower case is changed to upper case. The drive number may have more than one digit.
  2. Codes. The password is replaced by its 16-bit code (5152H; blank = 4296H). The name and extension give a one-byte hash code (4E8DH): the 11 characters XORed with a left rotate after each; 00H becomes 01H.
  3. Drives. With a drive number only that drive is searched; otherwise drives 0 up to option AL (4E9FH). A drive without a diskette is skipped.
  4. Search. The HIT (directory sector 1) is scanned for bytes equal to the hash code (4EAEH); each such entry is read and must be an FPDE in use with the same name and extension (4EDDH).
  5. Password. With option AA on, the code must match the update password (access FULL) or the access password (the file's access level); otherwise error 19H (4F06H).
  6. FCB. The FCB is rebuilt from the FPDE (4F2EH): open, access level, record mode, FPDE byte 1, buffer, drive, DEC, EOF, record length and the first four extent elements. The EOF middle and high bytes are one lower than in the entry when the low byte is not 0.

The 4420H open does the same and, when the file is not found, takes the first free entry (50CFH) on the drive given, or on the first drive from option AO up that has room, and writes a new FPDE (4DEAH).

How Granules Are Allocated

SYS0's sector mapping (4809H) calls function 64H when the sector wanted is past the file's last granule, with the DEC of the file's last directory entry at 486AH. SYS2 reads the GAT and looks for free granules from the one after the file's last granule, to the end of the diskette and then from lump 0 (4FC5H). A free granule that follows the last extent element is added to it (up to 32 granules per element); otherwise it starts a new element. When the entry's four elements are used, a new FXDE is created and linked (5058H). After the granules asked for, SYS2 takes up to three more if they follow on without a gap (5018H); CLOSE (SYS3) releases the granules the file does not use. A file with the ASE=N attribute (bit 7 of FCB+2) gives error 3DH.

Variables and Work Areas

AddressContents
4D6EH
1 byte
Operand of LD (HL),nn at 4D6DH: the hash code of the name being opened (stored at 4E9BH).
4E0AH-4E0BH
2 bytes
Operand of LD HL,nnnn at 4E09H: the drive limit (4E0AH) and the first drive (4E0BH).
4E0EH
1 byte
Operand of LD A,nn at 4E0DH: the drive being searched.
4E75H
1 byte
Opcode of the JR 4E7FH at 4E75H (18H in the file): 3EH during RENAME.
4F24H
1 byte
Operand of AND nn at 4F23H: byte 0 of the FPDE found.
4F46H
1 byte
Operand at 4F45H: FPDE byte 1, for FCB+2.
4F48H-4F49H
2 bytes
Operand at 4F47H: the buffer address, for FCB+3/4.
4F56H
1 byte
Operand at 4F55H: the DEC, for FCB+7 (also stored by SYS6).
4F58H
1 byte
Operand at 4F57H: the EOF low byte, for FCB+8.
4F5EH
1 byte
Operand at 4F5DH: the record length, for FCB+9 (also stored by SYS6).
505EH
1 byte
Operand of LD B,nn at 505DH: the DEC of the entry whose extent elements are being filled.
51ADH-51CCH
32 bytes
Not in the file. Open: the list of DECs whose HIT byte matches. Allocate: the entry's four extent elements (51ADH-51B4H) and FEH at 51B5H.
51CDH-51D7H
11 bytes
The name (8) and extension (3), padded with spaces.
51D8H-51DFH
8 bytes
The password; after encoding, 51D8H-51D9H and 51DAH-51DBH both hold its code (the two passwords of a new file).
51E0H-51FFH
32 bytes
The second filespec, put there by SYS1: the new name for RENAME.

SYS0 locations SYS2 uses: 4308H (current drive, becomes FCB+6), 430BH (lumps on the diskette), 430FH (granules per lump), 4369H bit 7 (DEBUG), 436BH bits 0-1 (extra granules), 436CH bit 7 (option AA), 439FH (option AL), 43A1H (option AO), 4403H-4404H (transfer address of the last load), 486AH (DEC of the file's last entry).

SYS6 Uses SYS2's Code

SYS6 loads SYS2 through 568DH (code E4H with C = FFH) and then, for COPY, stores the DEC at 4F56H and the record length at 4F5EH, calls 4E21H and 4F2EH to open a file whose entry it has found itself, and calls 50CFH to take destination directory entries.

Disassembly

4D00H - Function Dispatcher

SYS2 is entered here by the SYS0 RST 28H dispatcher (4BC2H) with Register A = the RST 28H code and the caller's BC, DE and HL unchanged. Bits 5-7 of the code are the function number (bits 0-4 = 04H, SYS2's directory slot): 24H open an existing file (4424H), 44H open a new or existing file (4420H), 64H allocate granules (SYS0 48BDH, inside the allocate routine 4809H), 84H encode a password, A4H load a program (4430H), C4H load and run a program (4433H). Function 7 (E4H) takes a sub-function in Register C: 1 RENAME (SYS1), 2 find a free directory entry on drive D, 3 run the program named on the command line (SYS1 4E96H), 4 create a new file. Any other code or sub-function returns error 2AH; SYS6 (568DH) relies on this to load SYS2 with C = FFH and then call into it.

4D00
CP 24H FE 24
Register A holds the RST 28H code. Compare it with 24H (function 1, open an existing file, from the 4424H vector).
4D02
Open existing: JUMP to 4E2EH.
4D05
CP 44H FE 44
Compare the code in Register A with 44H (function 2, open a new or existing file, from the 4420H vector).
4D07
Open new or existing: JUMP to 4DBDH.
4D0A
CP 64H FE 64
Compare the code in Register A with 64H (function 3, allocate granules; SYS0 calls it at 48BDH through 49DDH when a write goes past the last allocated granule).
4D0C
Allocate: JUMP to 4F82H.
4D0F
CP 84H FE 84
Compare the code in Register A with 84H (function 4, encode a password).
4D11
Encode: JUMP to 5155H with HL pointing to the last of the 8 password characters.
4D14
CP A4H FE A4
Compare the code in Register A with A4H (function 5, load a program, from the 4430H vector).
4D16
Load: JUMP to 4D92H.
4D18
CP C4H FE C4
Compare the code in Register A with C4H (function 6, load and run a program, from the 4433H vector).
4D1A
Load and run: JUMP to 4D80H.
4D1C
CP E4H FE E4
Compare the code in Register A with E4H (function 7; Register C holds the sub-function).
4D1E
Any other code: JUMP to 4D2EH to return error 2AH.
4D20
DEC C 0D
Function 7. Decrement the sub-function in Register C.
4D21
C was 1 (RENAME, from SYS1): JUMP to 4D32H.
4D23
DEC C 0D
Decrement the sub-function in Register C again.
4D24
C was 2 (find a free directory entry on drive D): JUMP to 50CAH.
4D27
DEC C 0D
Decrement the sub-function in Register C again.
4D28
C was 3 (run the program named on the command line, from SYS1 4E96H): JUMP to 4D7BH.
4D2A
DEC C 0D
Decrement the sub-function in Register C again.
4D2B
C was 4 (create a new file): JUMP to 4D72H. Any other sub-function (0, 5 and up, and the FFH that SYS6 uses to load SYS2) falls through to 4D2EH.

4D2EH - Return Error 2AH

Reached from the dispatcher for an unknown function code or function 7 sub-function. RENAME also uses 4D30H to return error 25H.

4D2E
LD A,2AH 3E 2A
Load Register A with error code 2AH (ILLEGAL DOS FUNCTION).
4D30
OR A B7
Also reached from 4D3DH with A = 25H. Set the NZ FLAG (A is not zero) so the caller sees an error.
4D31
RET C9
RETURN to the caller with the error code in Register A and the NZ FLAG set.

4D32H - Function E4H, C = 1: RENAME

Called by SYS1 for the RENAME command (manual section 2.43) after it has opened the old file. DE points to the open FCB of the old file; SYS1 has put the new filespec at 51E0H. The new name is looked for on the drive of the old file only (4E75H is patched so that 4E77H-4E7EH run); if it exists the result is error 35H. Otherwise the new name and extension are written into the old file's FPDE and the new name's hash code into its HIT byte. The password and everything else in the entry are kept.

4D32
PUSH DE D5
Save the FCB address.
4D33
POP IX DD E1
IX = the FCB of the old file.
4D35
INC DE 13
Point DE to FCB+1.
4D36
LD A,(DE) 1A
Fetch FCB+1.
4D37
AND 07H E6 07
Keep bits 0-2, the access level the file was opened with.
4D39
CP 03H FE 03
Compare it with 03H. RENAME needs level 0 (FULL), 1 (KILL) or 2 (RENAME).
4D3B
LD A,25H 3E 25
Load Register A with error code 25H (ILLEGAL ACCESS TRIED TO PROTECTED FILE).
4D3D
Level 3 or more: JUMP to 4D30H to return error 25H.
4D3F
LD HL,4E75H 21 75 4E
Point HL to 4E75H, the JR 4E7FH in the open routine.
4D42
LD (HL),3EH 36 3E
Change its opcode to 3EH: 4E75H becomes LD A,08H and the open falls into the RENAME code at 4E77H (the new name must not have a drive number and is looked for on the current drive only).
4D44
LD DE,51E0H 11 E0 51
Point DE to 51E0H, the new filespec.
4D47
GOSUB to 4E2CH to parse the new filespec and look for it (open existing with Register B = 0). This fills in 51CDH-51D7H (name and extension) and 4D6EH (the name's hash code). HL is kept.
4D4A
LD (HL),18H 36 18
Put the JR opcode (18H) back at 4E75H.
4D4C
The new name exists: JUMP to 4D77H to return error 35H (FILE ALREADY EXISTS).
4D4E
CP 18H FE 18
Compare the error with 18H (FILE NOT IN DIRECTORY), the one wanted.
4D50
Not found: GOSUB to the SYS0 routine at 494BH to read the old file's FPDE (FCB at IX). HL points to it.
4D53
RET NZ C0
Any other error: RETURN with it.
4D54
LD A,L 7D
Copy the low byte of the FPDE address into Register A.
4D55
ADD A,05H C6 05
ADD 5: the offset of the name in the entry.
4D57
LD L,A 6F
HL points to byte 5 of the FPDE, the name.
4D58
EX DE,HL EB
Exchange: DE = the name in the FPDE.
4D59
LD HL,51CDH 21 CD 51
Point HL to 51CDH, the new name and extension (8 + 3 characters, padded with spaces).
4D5C
LD BC,000BH 01 0B 00
Load BC with 11, the number of bytes to copy.
4D5F
LDIR ED B0
Copy the new name and extension into bytes 5-15 of the FPDE.
4D61
GOSUB to the SYS0 routine at 491FH to write the directory sector back.
4D64
LD A,01H 3E 01
Load Register A with 01H, the HIT sector.
4D66
If the write was good, GOSUB to the SYS0 routine at 490AH to read the HIT into 4200H.
4D69
RET NZ C0
Write or read error: RETURN with it.
4D6A
LD L,(IX+07H) DD 6E 07
Load Register L with the file's DEC (FCB+7): HL points to its HIT byte.
4D6D
LD (HL),00H 36 00
Self-Modifying Code
Store the new name's hash code (the operand at 4D6EH, stored at 4E9BH) as the file's HIT byte.
4D6F
JUMP to the SYS0 routine at 491FH to write the HIT back; it RETurns to the caller.

4D72H - Function E4H, C = 4: Create a New File

Opens a file as the 4420H function does, but returns error 35H if the file already exists. DE points to the FCB holding the filespec, HL to the buffer, Register B = the record length.

4D72
GOSUB to 4DBDH to open the file, creating it if it does not exist.
4D75
RET NZ C0
Error: RETURN with it.
4D76
RET C D8
The CARRY FLAG is set if the file was created: RETURN with Register A = 0 and the Z FLAG.
4D77
LD A,35H 3E 35
Also reached from RENAME (4D4CH). Load Register A with error code 35H (FILE ALREADY EXISTS).
4D79
OR A B7
Set the NZ FLAG.
4D7A
RET C9
RETURN with the error.

4D7BH - Function E4H, C = 3: Run the Program Named on the Command Line

SYS1 calls this (4E96H, code E443H) for a command word that is not a library command: the word has been put into the FCB at DE as a filespec with the default extension /CMD, and HL points to the command line after it. The program is loaded and run with HL pointing to the rest of the command line.

4D7B
GOSUB to the SYS0 routine at 4CD5H to check what follows the filespec: the Z FLAG for the end of the line (0DH), NC after a comma or spaces (HL moved past them), the CARRY FLAG with A = 34H for anything else.
4D7E
RET C D8
Something else follows the name: RETURN with error 34H (ILLEGAL KEYWORD OR SEPARATOR OR TERMINATOR).
4D7F
POP AF F1
Discard the return address into the SYS0 dispatcher (4C20H), so that 4D80H replaces SYS1's return address with the command line pointer. Fall into 4D80H.

4D80H - Function C4H: Load and Run a Program (4433H)

DE points to the FCB holding the program's filespec. The program is loaded (4D92H) and entered at its transfer address with HL = the caller's HL (from 4D7BH, the command line pointer). If 4369H bit 7 is set (the DEBUG command was given), DEBUG is entered instead, with the transfer address on the stack as the program counter.

4D80
EX (SP),HL E3
Exchange HL with the word on the stack: the caller's HL takes the place of the dispatcher's return address (4C20H), or, from 4D7BH, of SYS1's return address.
4D81
GOSUB to 4D92H to load the program. HL = its transfer address.
4D84
Load error: JUMP to the SYS0 error exit at 4409H, which displays the error and returns to DOS READY.
4D87
EX (SP),HL E3
Exchange again: the transfer address goes on the stack and HL gets the caller's HL (the command line pointer) back.
4D88
LD A,(4369H) 3A 69 43
Fetch the flags at 4369H.
4D8B
RLCA 07
Move bit 7 (DEBUG enabled, set by the DEBUG command) into the CARRY FLAG.
4D8C
DEBUG enabled: JUMP to 440DH (DEBUG) with the transfer address on the stack.
4D8F
JUMP to the SYS0 routine at 4C20H, which clears bit 6 of 4369H (overlay function running) and RETurns: the RET goes to the transfer address.

4D92H - Function A4H: Load a Program (4430H)

DE points to the FCB holding the program's filespec. The file is opened with 4200H as the buffer and loaded by the SYS0 loader (4C28H). The access level must allow EXEC (level 6 or below); for the load it is set to READ and put back afterwards. Returns HL = the transfer address (also stored at 4403H-4404H) and the Z FLAG, or an error.

4D92
LD HL,4200H 21 00 42
Point HL to 4200H, the buffer for the open.
4D95
GOSUB to 4E2CH to open the file with Register B = 0 (256-byte records).
4D98
Opened: JUMP to 4DA0H.
4D9A
CP 18H FE 18
Compare the error with 18H (FILE NOT IN DIRECTORY).
4D9C
RET NZ C0
Any other error: RETURN with it.
4D9D
ADD A,07H C6 07
ADD 7: error 18H becomes 1FH (PROGRAM NOT FOUND).
4D9F
RET C9
RETURN with error 1FH and the NZ FLAG.
4DA0
EX DE,HL EB
Exchange: HL = the FCB.
4DA1
INC HL 23
Point HL to FCB+1.
4DA2
LD A,(HL) 7E
Fetch FCB+1 (access level in bits 0-2).
4DA3
PUSH AF F5
Save it.
4DA4
PUSH HL E5
Save the pointer to FCB+1.
4DA5
AND 07H E6 07
Keep bits 0-2, the access level.
4DA7
LD B,A 47
Copy it into Register B.
4DA8
LD A,06H 3E 06
Load Register A with 06H (EXEC).
4DAA
CP B B8
Compare 6 with the level: the CARRY FLAG is set for level 7 (LOCK), which may not be loaded.
4DAB
LD A,25H 3E 25
Load Register A with error code 25H (ILLEGAL ACCESS TRIED TO PROTECTED FILE), kept if the load is skipped.
4DAD
LD (HL),2DH 36 2D
Set FCB+1 to 2DH: buffer empty (bit 5), v2 format (bit 3), access level 5 (READ), so that the loader can read the file.
4DAF
DEC HL 2B
Point HL back to the FCB.
4DB0
Level 0-6: GOSUB to the SYS0 load routine at 4C28H to load the file through the FCB at HL. It returns HL = the transfer address and the Z FLAG, or an error with NZ.
4DB3
LD (4403H),HL 22 03 44
Store HL (the transfer address) at 4403H-4404H.
4DB6
EX DE,HL EB
Exchange: DE = the transfer address.
4DB7
POP HL E1
Restore the pointer to FCB+1.
4DB8
POP BC C1
Restore the original FCB+1 into Register B.
4DB9
LD (HL),B 70
Put it back.
4DBA
DEC HL 2B
Point HL to the FCB.
4DBB
EX DE,HL EB
Exchange: HL = the transfer address, DE = the FCB.
4DBC
RET C9
RETURN with the loader's result.

4DBDH - Function 44H: Open a New or Existing File (4420H)

Manual section 3.4. DE points to the FCB holding the filespec, HL to the buffer, Register B = the record length (0 = 256). The file is looked for first (4E2EH). If it does not exist, a free directory entry is taken on the drive given in the filespec, or, without a drive number, on the first drive from option AO (43A1H) up that has room, and a new FPDE is written: in use, access FULL, EOF 0, no granules, the password's code as both passwords. Returns the Z FLAG with the CARRY FLAG set if the file was created, NC if it existed.

4DBD
GOSUB to 4E2EH to open the file if it exists.
4DC0
RET Z C8
Opened: RETURN with the Z FLAG and NC.
4DC1
CP 18H FE 18
Compare the error with 18H (FILE NOT IN DIRECTORY).
4DC3
RET NZ C0
Any other error: RETURN with it.
4DC4
GOSUB to the SYS0 routine at 4986H: the rest of this routine runs as a DOS function with IX = the FCB and IY = 4380H; on return all registers except AF are restored, and an error exit (49CDH) returns from here to the caller.
4DC7
LD BC,(4E0AH) ED 4B 0A 4E
Fetch 4E0AH-4E0BH, stored by the open: Register C = the drive limit, Register B = the first drive. They are equal when the filespec has a drive number.
4DCB
LD A,B 78
Copy the first drive into Register A.
4DCC
CP C B9
Compare it with the limit.
4DCD
A drive number was given: JUMP to 4DD2H to use it.
4DCF
LD A,(43A1H) 3A A1 43
No drive number: fetch option AO from 43A1H, the first drive tried for a new file.
4DD2
LD (4E0EH),A 32 0E 4E
Loop Start
Store the drive at 4E0EH (the operand of the LD A,nn at 4E0DH).
4DD5
GOSUB to the SYS0 routine at 47ECH to select the drive and check that it holds a diskette.
4DD8
Not ready: JUMP to 4DE3H to try the next drive.
4DDA
LD HL,4D6EH 21 6E 4D
Point HL to 4D6EH, the name's hash code.
4DDD
LD B,(HL) 46
Load Register B with the hash code: the free entry search starts in the FDE sector it selects.
4DDE
GOSUB to 50CFH to find a free directory entry, enter the hash code at 4D6EH into its HIT byte and clear the entry. HL = the entry, A = its DEC.
4DE1
Found: JUMP to 4DEAH.
4DE3
LD E,1AH 1E 1A
Load Register E with error code 1AH (DIRECTORY SPACE FULL), returned if no drive has room.
4DE5
GOSUB to 4E09H: Register A = the next drive, or it returns the error to our caller.
4DE8
Loop End
LOOP BACK to 4DD2H to try the next drive.

4DEAH - Write the New FPDE

HL points to the cleared directory entry (bytes 0-21 are 00H, 22-31 FFH), Register A = its DEC.

4DEA
LD (4F56H),A 32 56 4F
Store the DEC at 4F56H, from where it goes to FCB+7.
4DED
LD (HL),10H 36 10
Byte 0 of the entry = 10H: an FPDE in use, access level 0 (FULL).
4DEF
GOSUB to 4E1FH to take byte 1 (00H) and the EOF low byte (00H) into the FCB building code. HL points to byte 4.
4DF2
LD A,(4F5EH) 3A 5E 4F
Fetch the record length from 4F5EH (stored by the open).
4DF5
LD (HL),A 77
Store it in byte 4 of the entry (LRL).
4DF6
INC HL 23
Point HL to byte 5.
4DF7
EX DE,HL EB
Exchange: DE = byte 5 of the entry.
4DF8
LD HL,51CDH 21 CD 51
Point HL to 51CDH: name, extension, and the password code twice (51D8H-51DBH).
4DFB
LD BC,000FH 01 0F 00
Load BC with 15.
4DFE
LDIR ED B0
Copy them into bytes 5-19: name, extension, update password code, access password code. DE points to byte 20.
4E00
GOSUB to the SYS0 routine at 491FH to write the directory sector.
4E03
RET NZ C0
Write error: RETURN with it.
4E04
GOSUB to 4F2EH to build the open FCB with access level 0 (FULL); DE points to the EOF middle and high bytes of the entry.
4E07
SCF 37
Set the CARRY FLAG: the file was created.
4E08
RET C9
RETURN with Register A = 0, the Z FLAG and the CARRY FLAG.

4E09H - Try the Next Drive or Return the Error

Called by the drive loops of the open (4EA9H) and of the create (4DE5H) with Register E = the error to give if no drive is left (18H or 1AH) and Register A = the error of this drive (or 0). Returns Register A = the next drive if there is one. Otherwise it discards its return address and RETurns from the routine that called it with an error: when all drives have been tried, the one in Register E; when a drive number was given, the drive's own error if there was one, else the one in Register E.

4E09
LD HL,0000H 21 00 00
Self-Modifying Code
Load HL with the drive range stored at 4E7FH: Register L = the limit (the number of drives, option AL, or the drive number given), Register H = the first drive.
4E0C
LD D,A 57
Save this drive's error in Register D.
4E0D
LD A,00H 3E 00
Self-Modifying Code
Load Register A with the drive just tried (the operand at 4E0EH, stored at 4DD2H and 4E9FH).
4E0F
INC A 3C
INCrement it to the next drive.
4E10
CP L BD
Compare it with the limit.
4E11
RET C D8
Below the limit: RETURN with the next drive in Register A.
4E12
POP HL E1
Discard the return address: the RET below returns from the caller.
4E13
The next drive is the limit (all drives tried): JUMP to 4E18H to return the error in Register E.
4E15
LD A,D 7A
A drive number was given. Fetch that drive's error.
4E16
OR A B7
Set the flags.
4E17
RET NZ C0
There was one: RETURN with it and the NZ FLAG.
4E18
LD A,E 7B
Load Register A with the error in Register E.
4E19
OR A B7
Set the NZ FLAG.
4E1A
RET C9
RETURN with the error.

4E1BH - Return Error 20H

Reached for a bad drive number in the filespec: not a digit, over 255, or (for RENAME) given at all.

4E1B
LD A,20H 3E 20
Load Register A with error code 20H (ILLEGAL OR MISSING DRIVE #).
4E1D
OR A B7
Set the NZ FLAG.
4E1E
RET C9
RETURN with the error.

4E1FH - Take Byte 1 and the EOF Low Byte of an Entry

HL points to byte 0 of a directory entry. Byte 1 is stored at 4F46H (it becomes FCB+2) and byte 3 (the EOF low byte) at 4F58H (it becomes FCB+8). Returns HL pointing to byte 4. SYS6 calls 4E21H (56C4H) with HL = byte 1 and Register A = 00H, which it stores instead of byte 1.

4E1F
INC HL 23
Point HL to byte 1 of the entry.
4E20
LD A,(HL) 7E
Fetch byte 1 (bit 7 no extend, bit 6 no deallocate, bit 5 updated).
4E21
LD (4F46H),A 32 46 4F
Store it at 4F46H, the operand of the LD (HL),nn that sets FCB+2.
4E24
INC HL 23
Point HL to byte 2.
4E25
INC HL 23
Point HL to byte 3.
4E26
LD A,(HL) 7E
Fetch byte 3, the EOF low byte.
4E27
LD (4F58H),A 32 58 4F
Store it at 4F58H, the operand of the LD A,nn that sets FCB+8.
4E2A
INC HL 23
Point HL to byte 4 (LRL).
4E2B
RET C9
RETURN.

4E2CH - Function 24H: Open an Existing File (4424H)

Manual section 3.4. DE points to the FCB holding the filespec NAME/EXT.PASSWORD:d, HL to the buffer, Register B = the record length (0 = 256; 4E2CH sets 0). The filespec is parsed into 51CDH-51DFH, the password encoded, the name's hash code worked out, and the directory of each drive searched (4EAEH) until the file is found. The FCB is then built from the FPDE (4F2EH). Errors: 30H bad filespec, 20H bad drive number, 18H not found, 19H wrong password, a disk error for a drive that was given.

4E2C
LD B,00H 06 00
Load Register B with 0: 256-byte records (entry used by RENAME at 4D47H and the load at 4D95H).
4E2E
GOSUB to the SYS0 routine at 4986H: the rest runs as a DOS function with IX = the FCB and IY = 4380H, whether the FCB is open or not.
4E31
LD (4F48H),HL 22 48 4F
Store the buffer address at 4F48H, the operand of the LD DE,nnnn that sets FCB+3/4.
4E34
LD A,B 78
Copy the record length into Register A.
4E35
LD (4F5EH),A 32 5E 4F
Store it at 4F5EH, the operand of the LD (HL),nn that sets FCB+9.
4E38
LD HL,51CDH 21 CD 51
Point HL to 51CDH, where the name goes.
4E3B
DEC DE 1B
Point DE one byte before the filespec (5146H steps first).
4E3C
XOR A AF
Set Register A to 0 and the Z FLAG: the name must be there.
4E3D
GOSUB to 5121H to copy the name (up to 8 characters) to 51CDH-51D4H, padded with spaces. Register A = the character after it.
4E40
CP 2FH FE 2F
Compare it with 2FH (/).
4E42
LD B,03H 06 03
Load Register B with 3, the length of the extension.
4E44
GOSUB to 5123H: after a / copy the extension to 51D5H-51D7H, else fill it with spaces.
4E47
CP 2EH FE 2E
Compare the next character with 2EH (.).
4E49
GOSUB to 5121H: after a . copy the password to 51D8H-51DFH, else fill it with spaces.
4E4C
LD B,00H 06 00
Load Register B with 0, the first drive to search.
4E4E
LD C,(IY+1FH) FD 4E 1F
Load Register C with the number of drives, option AL (439FH = IY+1FH), the drive limit.
4E51
CP 3AH FE 3A
Compare the next character with 3AH (:).
4E53
No drive number: JUMP to 4E75H.
4E55
INC DE 13
Point DE to the next character.
4E56
LD A,(DE) 1A
Fetch it.
4E57
SUB 30H D6 30
SUBtract 30H to turn an ASCII digit into its value.
4E59
CP 0AH FE 0A
Compare it with 10.
4E5B
Not a digit: JUMP to 4E1BH to return error 20H.
4E5D
LD C,A 4F
Loop Start
Register C = the drive number so far.
4E5E
INC DE 13
Point DE to the next character.
4E5F
LD A,(DE) 1A
Fetch it.
4E60
SUB 30H D6 30
SUBtract 30H.
4E62
CP 0AH FE 0A
Compare it with 10.
4E64
Not a digit: JUMP to 4E74H, the number is complete.
4E66
LD L,A 6F
Save the digit in Register L.
4E67
LD A,C 79
Copy the number so far into Register A.
4E68
LD B,09H 06 09
Load Register B with 9 for the multiply.
4E6A
ADD A,C 81
ADD the number to Register A (nine times: A = number x 10).
4E6B
Over 255: JUMP to 4E1BH to return error 20H.
4E6D
LOOP BACK to 4E6AH until done.
4E6F
ADD A,L 85
ADD the new digit.
4E70
Loop End
No overflow: LOOP BACK to 4E5DH for the next character.
4E72
Over 255: JUMP to 4E1BH to return error 20H.
4E74
LD B,C 41
A drive number was given: Register B = Register C = the drive, so only that drive is searched. The number is checked against option AL by the drive select at 47ECH.
4E75
JUMP to 4E7FH. RENAME (4D42H) changes the opcode to 3EH, making this LD A,08H so that 4E77H runs.

4E77H - RENAME Only: Use the Old File's Drive

Reached only while 4E75H is LD A,08H (RENAME).

4E77
LD A,C 79
Copy the limit into Register A.
4E78
CP B B8
Compare it with the first drive: they are the same only if the new name has a drive number.
4E79
Drive number given: JUMP to 4E1BH to return error 20H.
4E7B
LD B,(IY-78H) FD 46 88
Load Register B with the current drive (4308H = IY-78H), the drive of the old file.
4E7E
LD C,B 48
Register C = the same drive: only it is searched.

4E7FH - Encode the Password and Hash the Name

Stores the drive range, replaces the password characters by their code (51D8H-51DBH) and works out the name's hash code (4D6EH) from the 11 name and extension characters.

4E7F
LD (4E0AH),BC ED 43 0A 4E
Store the drive range at 4E0AH-4E0BH (the operand of the LD HL,nnnn at 4E09H): the limit at 4E0AH, the first drive at 4E0BH.
4E83
PUSH BC C5
Save it: Register B (the first drive) comes back as Register A at 4E9EH.
4E84
GOSUB to 5152H to encode the 8 password characters at 51D8H-51DFH: HL = the 16-bit code; the characters are set to spaces.
4E87
LD (51D8H),HL 22 D8 51
Store the code at 51D8H-51D9H (update password for a new file).
4E8A
LD (51DAH),HL 22 DA 51
Store it at 51DAH-51DBH as well (access password for a new file).
4E8D
LD HL,51CDH 21 CD 51
Point HL to 51CDH, the name and extension.
4E90
LD B,0BH 06 0B
Load Register B with 11.
4E92
XOR A AF
Set Register A to 0.
4E93
XOR (HL) AE
Loop Start
XOR the next character into Register A.
4E94
INC HL 23
Point HL to the next character.
4E95
RLCA 07
Rotate Register A left one bit.
4E96
Loop End
LOOP BACK to 4E93H for all 11 characters.
4E98
Not 0: JUMP to 4E9BH.
4E9A
INC A 3C
A result of 0 becomes 01H: 00H marks a free entry in the HIT.
4E9B
LD (4D6EH),A 32 6E 4D
Store the hash code at 4D6EH (the operand of the LD (HL),nn at 4D6DH).
4E9E
POP AF F1
Restore the first drive into Register A.

4E9FH - Search the Drives

Tries each drive from the first to the limit (4E09H). Register E = 18H is the error returned if the file is on none of them.

4E9F
LD (4E0EH),A 32 0E 4E
Loop Start
Store the drive at 4E0EH.
4EA2
GOSUB to the SYS0 routine at 47ECH to select the drive and check that it holds a diskette.
4EA5
Ready: JUMP to 4EAEH to search its directory.
4EA7
LD E,18H 1E 18
Also reached from 4EB6H when the directory has been searched. Load Register E with error code 18H (FILE NOT IN DIRECTORY).
4EA9
GOSUB to 4E09H: Register A = the next drive, or it returns the error to our caller.
4EAC
Loop End
LOOP BACK to 4E9FH to try the next drive.

4EAEH - Search One Directory

The HIT (directory sector 1) has one byte per DEC: 00H for a free entry, else the hash code of the name. The DECs whose HIT byte equals the name's hash code are collected at 51ADH-51CBH (up to 31 at a time), then each is checked (4EDDH). Register B = the next DEC to look at, Register C = 1FH (DECs with bits 0-4 = 1FH are not looked at: HIT byte 1FH holds the number of extra FDE sectors).

4EAE
LD DE,51ADH 11 AD 51
Point DE to 51ADH, the start of the list of matching DECs.
4EB1
LD BC,001FH 01 1F 00
Register B = 00H, the first DEC; Register C = 1FH, the FDE sector limit.
4EB4
LD A,B 78
Loop Start
Copy the next DEC into Register A.
4EB5
SUB C 91
SUBtract the limit: 0 when every DEC has been looked at.
4EB6
All done and not found: JUMP to 4EA7H to try the next drive.
4EB8
LD A,01H 3E 01
Load Register A with 01H, the HIT sector.
4EBA
GOSUB to the SYS0 routine at 490AH to read it into 4200H (it may have been replaced by an FDE sector).
4EBD
RET NZ C0
Read error: RETURN with it.
4EBE
LD A,B 78
Copy the DEC into Register A.
4EBF
LD B,A 47
Register B = the DEC to look at.
4EC0
LD (DE),A 12
Store it in the list at DE; it is kept only if DE is moved on.
4EC1
LD L,A 6F
HL = 4200H + DEC, its HIT byte.
4EC2
LD A,E 7B
Copy the list pointer's low byte into Register A.
4EC3
CP CCH FE CC
Compare it with CCH: the list (51ADH-51CBH) is full.
4EC5
Full: JUMP to 4EDDH to check the DECs in it; this DEC (stored at 51CCH) is looked at again afterwards.
4EC7
LD A,(4D6EH) 3A 6E 4D
Fetch the name's hash code from 4D6EH.
4ECA
CP (HL) BE
Compare it with the HIT byte.
4ECB
Different: JUMP to 4ECEH.
4ECD
INC DE 13
Same: keep the DEC in the list.
4ECE
LD A,B 78
Copy the DEC into Register A.
4ECF
ADD A,20H C6 20
ADD 20H: the next entry (bits 5-7) of the same FDE sector.
4ED1
No carry: LOOP BACK to 4EBFH.
4ED3
INC A 3C
Past entry 7: INCrement to the next FDE sector (bits 0-4), entry 0.
4ED4
CP C B9
Compare it with the limit (1FH).
4ED5
LD B,A 47
Register B = the next DEC.
4ED6
Loop End
Below the limit: LOOP BACK to 4EBFH.
4ED8
LD A,E 7B
Also reached from 4F02H. Copy the list pointer's low byte into Register A.
4ED9
CP ADH FE AD
Compare it with ADH: the list is empty.
4EDB
Empty: LOOP BACK to 4EB4H to go on scanning the HIT (or end at the limit).

4EDDH - Check the Matching Entries

Takes the DECs from the list, last first. An entry must be an FPDE in use with the same name and extension. For the matching entry the password is checked (4F06H).

4EDD
DEC DE 1B
Point DE to the last DEC in the list.
4EDE
LD A,(DE) 1A
Fetch it.
4EDF
LD (4F56H),A 32 56 4F
Store it at 4F56H (the operand of the LD (HL),nn that sets FCB+7).
4EE2
GOSUB to the SYS0 routine at 492FH to read the DEC's FDE sector if it is not in 4200H. HL points to the entry.
4EE5
RET NZ C0
Read error: RETURN with it.
4EE6
PUSH DE D5
Save the list pointer.
4EE7
PUSH BC C5
Save the scan position (B) and limit (C).
4EE8
LD A,(HL) 7E
Fetch byte 0 of the entry.
4EE9
LD (4F24H),A 32 24 4F
Store it at 4F24H (the operand of the AND nn at 4F23H, for the access level).
4EEC
AND 90H E6 90
Keep bits 7 (FXDE) and 4 (in use).
4EEE
CP 10H FE 10
Compare with 10H: an FPDE in use.
4EF0
Not an FPDE in use: JUMP to 4F00H to try the next DEC.
4EF2
GOSUB to 4E1FH to take byte 1 and the EOF low byte. HL points to byte 4.
4EF5
LD DE,51CDH 11 CD 51
Point DE to 51CDH, the name and extension wanted.
4EF8
LD B,0BH 06 0B
Load Register B with 11.
4EFA
INC HL 23
Loop Start
Point HL to the next byte of the entry (5-15).
4EFB
LD A,(DE) 1A
Fetch the next character wanted.
4EFC
CP (HL) BE
Compare it with the entry.
4EFD
INC DE 13
Point DE to the next character.
4EFE
Same: JUMP to 4F04H.
4F00
POP BC C1
Different. Restore the scan position and limit.
4F01
POP DE D1
Restore the list pointer.
4F02
JUMP to 4ED8H to try the next DEC in the list.
4F04
Loop End
LOOP BACK to 4EFAH for all 11 characters.

4F06H - The File Is Found: Check the Password

HL points to byte 15 of the FPDE. If option AA (436CH bit 7, passwords on) is off, or the password given has the update password's code, the file is opened with access level 0 (FULL). If it has the access password's code, the level in bits 0-2 of byte 0 is used. Otherwise the result is error 19H.

4F06
POP BC C1
Discard the scan position.
4F07
POP DE D1
Discard the list pointer.
4F08
INC HL 23
Point HL to byte 16.
4F09
LD E,(HL) 5E
Load Register E with the low byte of the update password code.
4F0A
INC HL 23
Point HL to byte 17.
4F0B
LD D,(HL) 56
Load Register D with the high byte: DE = the update password code.
4F0C
INC HL 23
Point HL to byte 18.
4F0D
LD C,(HL) 4E
Load Register C with the low byte of the access password code.
4F0E
INC HL 23
Point HL to byte 19.
4F0F
LD B,(HL) 46
Load Register B with the high byte: BC = the access password code.
4F10
INC HL 23
Point HL to byte 20, the EOF middle byte.
4F11
PUSH HL E5
Save it for 4F62H.
4F12
LD HL,(51D8H) 2A D8 51
Fetch the code of the password given from 51D8H.
4F15
BIT 7,(IY-14H) FD CB EC 7E
Test bit 7 of 436CH (IY-14H), option AA: passwords are checked.
4F19
Off: JUMP to 4F2FH to open with access level 0.
4F1B
OR A B7
Clear the CARRY FLAG.
4F1C
SBC HL,DE ED 52
SUBtract the update password code.
4F1E
Same: JUMP to 4F2FH to open with access level 0.
4F20
ADD HL,DE 19
ADD it back.
4F21
LD A,07H 3E 07
Load Register A with 07H.
4F23
AND 00H E6 00
Self-Modifying Code
AND it with byte 0 of the entry (the operand at 4F24H, stored at 4EE9H): Register A = the access level. The CARRY FLAG is cleared.
4F25
SBC HL,BC ED 42
SUBtract the access password code.
4F27
Same: JUMP to 4F30H to open with that access level.
4F29
POP HL E1
Wrong password. Discard the entry pointer.
4F2A
LD A,19H 3E 19
Load Register A with error code 19H (FILE ACCESS DENIED).
4F2C
OR A B7
Set the NZ FLAG.
4F2D
RET C9
RETURN with the error.

4F2EH - Build the Open FCB

IX points to the FCB. DE (4F2EH) or the word on the stack (4F30H) points to byte 20 of the FPDE, Register A (4F30H) = the access level. The other values come from operands stored earlier: FCB+2 (4F46H), the buffer (4F48H), the DEC (4F56H), the EOF low byte (4F58H) and the record length (4F5EH). SYS6 calls 4F2EH (56CCH) to open a file whose entry it has found itself. Returns Register A = 0 and the Z FLAG, or error 2CH if the EOF in the entry is bad.

4F2E
PUSH DE D5
Save the pointer to byte 20 of the entry.
4F2F
XOR A AF
Access level 0 (FULL).
4F30
PUSH IX DD E5
Copy the FCB address...
4F32
POP HL E1
...into HL.
4F33
GOSUB to 5110H to set FCB+0 to FCB+21 to 00H and FCB+22 to FCB+31 to FFH.
4F36
LD (HL),80H 36 80
FCB+0 = 80H: the file is open.
4F38
INC HL 23
Point HL to FCB+1.
4F39
OR 28H F6 28
Add bit 5 (buffer does not hold NEXT) and bit 3 (v2 format) to the access level.
4F3B
LD (HL),A 77
Store it as FCB+1.
4F3C
LD A,(4F5EH) 3A 5E 4F
Fetch the record length from 4F5EH.
4F3F
OR A B7
Set the flags.
4F40
0 (256-byte records): JUMP to 4F44H.
4F42
SET 7,(HL) CB FE
Any other length: set bit 7 of FCB+1, record mode.
4F44
INC HL 23
Point HL to FCB+2.
4F45
LD (HL),00H 36 00
Self-Modifying Code
FCB+2 = byte 1 of the FPDE (the operand at 4F46H, stored at 4E21H): no extend, no deallocate, updated.
4F47
LD DE,0000H 11 00 00
Self-Modifying Code
Load DE with the buffer address (the operand at 4F48H, stored at 4E31H).
4F4A
INC HL 23
Point HL to FCB+3.
4F4B
LD (HL),E 73
Store the low byte of the buffer address.
4F4C
INC HL 23
Point HL to FCB+4.
4F4D
LD (HL),D 72
Store the high byte.
4F4E
INC HL 23
Point HL to FCB+5 (NEXT low byte, left 00H).
4F4F
INC HL 23
Point HL to FCB+6.
4F50
LD A,(IY-78H) FD 7E 88
Fetch the current drive from 4308H (IY-78H), the drive the file was found on.
4F53
LD (HL),A 77
Store it as FCB+6.
4F54
INC HL 23
Point HL to FCB+7.
4F55
LD (HL),00H 36 00
Self-Modifying Code
FCB+7 = the DEC of the FPDE (the operand at 4F56H, stored at 4DEAH and 4EDFH, and by SYS6 at 56B9H).
4F57
LD A,00H 3E 00
Self-Modifying Code
Load Register A with the EOF low byte (the operand at 4F58H, stored at 4E27H).
4F59
INC HL 23
Point HL to FCB+8.
4F5A
OR A B7
Set the flags: the Z FLAG if the EOF low byte is 0.
4F5B
LD (HL),A 77
Store it as FCB+8.
4F5C
INC HL 23
Point HL to FCB+9.
4F5D
LD (HL),00H 36 00
Self-Modifying Code
FCB+9 = the record length (the operand at 4F5EH, stored at 4E35H, and by SYS6 at 56C0H).
4F5F
INC HL 23
Point HL to FCB+10 (NEXT middle byte, left 00H).
4F60
INC HL 23
Point HL to FCB+11 (NEXT high byte, left 00H).
4F61
INC HL 23
Point HL to FCB+12.
4F62
POP DE D1
Restore DE = byte 20 of the entry.
4F63
LD A,(DE) 1A
Fetch the EOF middle byte.
4F64
INC DE 13
Point DE to byte 21.
4F65
EOF low byte 0: JUMP to 4F69H.
4F67
SUB 01H D6 01
Not 0: SUBtract 1. The entry keeps the middle and high bytes one higher when the low byte is not 0.
4F69
LD (HL),A 77
Store it as FCB+12.
4F6A
INC HL 23
Point HL to FCB+13.
4F6B
LD A,(DE) 1A
Fetch the EOF high byte.
4F6C
SBC A,00H DE 00
SUBtract the borrow.
4F6E
LD (HL),A 77
Store it as FCB+13.
4F6F
INC DE 13
Point DE to byte 22 of the entry, the extent elements.
4F70
INC HL 23
Point HL to FCB+14.
4F71
LD A,2CH 3E 2C
Load Register A with error code 2CH (BAD DIRECTORY DATA).
4F73
RET C D8
A borrow out of the high byte (EOF middle and high 0 with a low byte): RETURN with error 2CH.
4F74
GOSUB to 4F79H to copy the four extent elements (bytes 22-29) to FCB+14 to FCB+21.
4F77
XOR A AF
Set Register A to 0 and the Z FLAG.
4F78
RET C9
RETURN.

4F79H - Copy 8 Bytes

4F79H copies 8 bytes from DE to HL, 4F7AH from HL to DE: four extent elements.

4F79
EX DE,HL EB
Exchange DE and HL.
4F7A
LD A,08H 3E 08
Load Register A with 8.
4F7C
LD C,A 4F
Copy it into Register C.
4F7D
LD B,00H 06 00
Register B = 0: BC = 8.
4F7F
LDIR ED B0
Copy the 8 bytes.
4F81
RET C9
RETURN with Register A = 08H.

4F82H - Function 64H: Allocate Granules

Called by SYS0 at 48BDH (CALL 49DDH) from the sector mapping routine (4809H) when the sector wanted lies past the last granule the file has. IX = the FCB, IY = 4380H, DE = the number of granules missing minus 1, and 486AH (the operand of the LD A,nn at SYS0 4869H) holds the DEC of the file's last directory entry (the FPDE, or its last FXDE). SYS2 allocates DE + 1 granules, extending the last extent element while the next granule is free, else starting a new element; when the entry's four elements are used up it creates an FXDE (5058H). After the granules asked for it takes up to 3 more that follow on without a gap (5018H); CLOSE (SYS3) gives back what the file does not use. The GAT and the entries are written, the elements of the FPDE copied into FCB+14 to FCB+21, and SYS2 returns with HL pointing to the last entry, from which SYS0 (48C2H) maps the sector again. Errors (3DH no extend, 1BH diskette full, 2CH, disk errors) leave through SYS0's DOS function error exit (49CDH).

4F82
LD A,3DH 3E 3D
Load Register A with error code 3DH (ASE=N ATTRIBUTE. CAN'T EXTEND FILE).
4F84
BIT 7,(IX+02H) DD CB 02 7E
Test bit 7 of FCB+2, the FPDE's no extend bit.
4F88
Clear: GOSUB to the SYS0 routine at 476EH to select the FCB's drive.
4F8B
No extend, or a drive error: JUMP to 4FFBH to leave with the error.
4F8D
LD A,(486AH) 3A 6A 48
Fetch the DEC of the file's last entry from 486AH (stored by SYS0 at 48A0H).
4F90
LD (505EH),A 32 5E 50
Store it at 505EH, the entry being extended.
4F93
PUSH AF F5
Save it: it is restored at 5032H to point to the entry at the end.
4F94
GOSUB to the SYS0 routine at 4936H to read the entry's FDE sector. HL points to the entry, A = its offset in the sector.
4F97
GOSUB to 5036H: leave with the error if the read failed, or with 2CH if the entry is not in use.
4F9A
INC DE 13
DE + 1 = the number of granules to allocate.
4F9B
PUSH DE D5
Keep it on the stack as the count.
4F9C
GOSUB to 50B4H to copy the entry's four extent elements to 51ADH-51B4H. HL points to the last element in use (or 51ADH if none), the Z FLAG is set if it is empty.
4F9F
LD B,(IY-71H) FD 46 8F
Load Register B with the granules per lump (430FH = IY-71H).
4FA2
LD C,01H 0E 01
Register C = 01H, the GAT bit of granule 0.
4FA4
LD E,(HL) 5E
Fetch the element's lump number.
4FA5
INC E 1C
INCrement it: 0 if the element is empty (FFH).
4FA6
Empty: JUMP to 4FC5H to search from granule 0 of lump 0.
4FA8
DEC E 1D
DECrement Register E...
4FA9
DEC E 1D
...to the lump number minus 1.
4FAA
INC HL 23
Point HL to the element's second byte.
4FAB
LD A,(HL) 7E
Fetch it.
4FAC
AND 1FH E6 1F
Keep bits 0-4, the number of granules minus 1.
4FAE
LD D,A 57
Copy it into Register D.
4FAF
INC D 14
INCrement: Register D = the number of granules.
4FB0
LD A,(HL) 7E
Fetch the second byte again.
4FB1
AND E0H E6 E0
Keep bits 5-7, the first granule within the lump.
4FB3
DEC HL 2B
Point HL back to the lump byte.
4FB4
RLCA 07
Rotate...
4FB5
RLCA 07
...bits 5-7...
4FB6
RLCA 07
...into bits 0-2.
4FB7
ADD A,D 82
ADD the number of granules: Register A = the granule after the element's last, counted from its lump.
4FB8
INC E 1C
Loop Start
INCrement the lump number.
4FB9
SUB B 90
SUBtract the granules per lump.
4FBA
Loop End
No borrow: LOOP BACK to 4FB8H. Register E = the lump that holds the next granule.
4FBC
ADD A,B 80
ADD the granules per lump back: Register A = the granule within that lump.
4FBD
Granule 0: JUMP to 4FC5H with Register C = 01H and Register B = the granules per lump.
4FBF
RLC C CB 01
Loop Start
Rotate the GAT bit mask left.
4FC1
DEC B 05
One granule fewer left in the lump.
4FC2
DEC A 3D
DECrement the granule number.
4FC3
Loop End
LOOP BACK to 4FBFH. Register C = the next granule's GAT bit, Register B = the granules from it to the end of the lump.

4FC5H - Scan the GAT

The GAT (directory sector 0) has one byte per lump, one bit per granule (1 = in use). HL = 4200H + lump, Register C = the granule's bit, Register B = the granules left in this lump, DE = the extent element being filled in 51ADH-51B4H. The scan runs to the last lump (430BH), then once more from lump 0.

4FC5
PUSH HL E5
Also reached from 5089H. Save the element pointer.
4FC6
XOR A AF
Register A = 0, the GAT sector.
4FC7
GOSUB to the SYS0 routine at 490AH to read the GAT into 4200H.
4FCA
Read error: JUMP to 4FFBH to leave with it.
4FCC
LD L,E 6B
HL = 4200H + lump, its GAT byte.
4FCD
POP DE D1
Restore DE = the element pointer.
4FCE
LD A,01H 3E 01
Load Register A with 1: one wrap to lump 0 is allowed.
4FD0
EX AF,AF' 08
Keep the count in the alternate Register A.
4FD1
JUMP to 4FE5H to check the lump number.
4FD3
LD A,(HL) 7E
Loop Start
Fetch the lump's GAT byte.
4FD4
AND C A1
Test the granule's bit.
4FD5
LD A,(DE) 1A
Fetch the element's lump byte (the flags stay).
4FD6
The granule is free: JUMP to 4FFDH.
4FD8
INC A 3C
In use. INCrement the lump byte: 0 if the element is empty.
4FD9
The element is in use: a gap has been found, JUMP to 504CH to go on with the next element.
4FDB
RLC C CB 01
Also reached from 5016H and 5028H. Rotate the GAT bit mask to the next granule.
4FDD
Loop End
LOOP BACK to 4FD3H while granules are left in this lump.
4FDF
INC L 2C
Next lump.
4FE0
LD B,(IY-71H) FD 46 8F
Register B = the granules per lump.
4FE3
LD C,01H 0E 01
Register C = 01H, granule 0.
4FE5
LD A,L 7D
Copy the lump number into Register A.
4FE6
CP (IY-75H) FD BE 8B
Compare it with the number of lumps on the diskette (430BH = IY-75H).
4FE9
Below it: LOOP BACK to 4FD3H.
4FEB
EX AF,AF' 08
Past the last lump. Fetch the wrap count.
4FEC
DEC A 3D
DECrement it.
4FED
LD L,A 6F
Register L = 0 (lump 0) the first time.
4FEE
First time: JUMP to 4FD0H to keep the count and scan from lump 0.
4FF0
BIT 0,(IY-15H) FD CB EB 46
Second time: the diskette is full. Test bit 0 of 436BH (IY-15H): the extra granules are being taken.
4FF4
Yes: JUMP to 502AH to finish without an error.
4FF6
GOSUB to 508CH to write the GAT and the extent elements taken so far.
4FF9
LD A,1BH 3E 1B
Load Register A with error code 1BH (DISKETTE SPACE FULL).
4FFB
Also reached from 4F8BH and 4FCAH. JUMP to 503DH to leave with the error.

4FFDH - Take a Free Granule

Register A = the element's lump byte plus 1 (0 if the element is empty), HL = the GAT byte, Register C = the granule's bit.

4FFD
INC A 3C
INCrement: 0 if the element is empty (FFH).
4FFE
The element is in use: JUMP to 5043H to add the granule to it.
5000
LD A,L 7D
Empty element. Copy the lump number into Register A.
5001
LD (DE),A 12
Store it as the element's lump byte.
5002
INC DE 13
Point DE to the element's second byte.
5003
LD A,(IY-71H) FD 7E 8F
Fetch the granules per lump.
5006
SUB B 90
SUBtract the granules left in the lump: Register A = the granule within the lump.
5007
RRCA 0F
Rotate...
5008
RRCA 0F
...it into...
5009
RRCA 0F
...bits 5-7 (the count bits 0-4 stay 0).
500A
DEC A 3D
DECrement Register A (the INC below makes the count 0: one granule).
500B
INC A 3C
Also reached from 5049H with the second byte of an element in use. INCrement the count in bits 0-4 by one granule.
500C
LD (DE),A 12
Store the element's second byte.
500D
DEC DE 1B
Point DE back to the lump byte.
500E
LD A,(HL) 7E
Fetch the GAT byte.
500F
OR C B1
Set the granule's bit.
5010
LD (HL),A 77
Store it: the granule is in use.
5011
EX (SP),HL E3
Exchange HL with the count on the stack.
5012
DEC HL 2B
DECrement the count.
5013
LD A,H 7C
Copy the high byte into Register A.
5014
OR L B5
OR the low byte: the Z FLAG is set when no more granules are wanted.
5015
EX (SP),HL E3
Exchange back.
5016
More wanted: JUMP to 4FDBH to take the next granule.

5018H - Up to Three More Granules

When the granules asked for have been taken, three more are asked for, unless bit 0 of 436BH shows that this has been done already or bit 1 is set (SYS6's copy, which sets it during its pre-allocation write). While bit 0 is set, a granule that does not follow on (5052H) or a full diskette (4FF0H) ends the allocation without an error.

5018
LD A,(IY-15H) FD 7E EB
Fetch the flags at 436BH (IY-15H).
501B
AND 03H E6 03
Keep bits 0 and 1.
501D
Either set: JUMP to 502AH to finish.
501F
EX (SP),HL E3
Exchange HL with the count on the stack.
5020
INC HL 23
Add 1...
5021
INC HL 23
...2...
5022
INC HL 23
...3 granules to the count.
5023
EX (SP),HL E3
Exchange back.
5024
SET 0,(IY-15H) FD CB EB C6
Set bit 0 of 436BH: the extra granules are being taken.
5028
JUMP to 4FDBH to take the next granule.

502AH - Finish

Writes the GAT and the extent elements and returns to SYS0 with HL pointing to the file's last entry as it was at the start (the one in 486AH).

502A
RES 0,(IY-15H) FD CB EB 86
Clear bit 0 of 436BH.
502E
GOSUB to 508CH to write the GAT and the extent elements.
5031
POP AF F1
Discard the count.
5032
POP AF F1
Restore Register A = the DEC saved at 4F93H.
5033
Also called from 5070H, 5081H and 5092H. GOSUB to the SYS0 routine at 492FH to read the DEC's FDE sector if it is not in 4200H. HL points to the entry, A = its offset.
5036
Also called from 4F97H. A read error: JUMP to 503DH to leave with it.
5038
BIT 4,(HL) CB 66
Test bit 4 of byte 0, the entry is in use.
503A
RET NZ C0
In use: RETURN (to the SYS0 dispatcher, which returns to SYS0 48C2H, when reached from 502AH).
503B
LD A,2CH 3E 2C
Not in use: load Register A with error code 2CH (BAD DIRECTORY DATA).
503D
Also reached from 4FFBH, 5036H, 5064H and 50B2H. GOSUB to the SYS0 routine at 4C20H to clear bit 6 of 4369H, which the dispatcher would have done on return.
5040
JUMP to the SYS0 error exit at 49CDH: the error goes back to the caller of the running DOS function (the read or write that needed the granules).

5043H - Add the Granule to the Element

DE points to the element's lump byte. The granule is the one after the element's last, so it is added if the element has fewer than 32 granules.

5043
INC DE 13
Point DE to the element's second byte.
5044
LD A,(DE) 1A
Fetch it.
5045
INC A 3C
INCrement it...
5046
AND 1FH E6 1F
...and keep bits 0-4: 0 if the element already has 32 granules.
5048
LD A,(DE) 1A
Fetch the second byte again (the flags stay).
5049
Room: JUMP to 500BH to add the granule.
504B
DEC DE 1B
Full. Point DE back to the lump byte and fall into 504CH.

504CH - Next Element

The granule does not follow the element (or the element is full): go on with the next element of the entry. After the fourth comes the FEH stored at 51B5H.

504C
INC DE 13
Point DE to the next...
504D
INC DE 13
...element.
504E
LD A,(DE) 1A
Fetch its lump byte.
504F
INC A 3C
INCrement it: 0 if it is empty (FFH).
5050
Empty: JUMP to 4FD3H to look at the same granule for this element.
5052
BIT 0,(IY-15H) FD CB EB 46
The entry is full. Test bit 0 of 436BH: the extra granules are being taken.
5056
Yes: JUMP to 502AH to finish.

5058H - Create an FXDE

All four elements of the entry are used. A free directory entry is found (50CFH), starting in the FDE sector of the old entry and with the old entry's HIT byte as its hash code. It becomes an FXDE: byte 0 = 90H, byte 1 = the DEC of the old entry. The old entry gets FEH and the new DEC in bytes 30-31. The allocation then goes on with the new entry's empty elements.

5058
PUSH HL E5
Save the GAT pointer.
5059
PUSH BC C5
Save the granule's bit and count.
505A
GOSUB to 508CH to write the GAT and the old entry's elements. HL = 4200H afterwards.
505D
LD B,00H 06 00
Self-Modifying Code
Load Register B with the DEC of the entry being extended (the operand at 505EH, stored at 4F90H and 507EH).
505F
LD L,B 68
HL = 4200H + DEC: after the HIT is read (50D0H) it points to the old entry's HIT byte.
5060
PUSH BC C5
Save the old DEC (Register B).
5061
GOSUB to 50CFH to find a free entry, starting in the old entry's FDE sector, and give it the old entry's HIT byte. HL = the new entry (cleared), A = its DEC.
5064
No free entry (1AH) or an error: JUMP to 503DH to leave with it.
5066
LD C,A 4F
Register C = the new DEC.
5067
LD (HL),90H 36 90
Byte 0 of the new entry = 90H: an FXDE (bit 7) in use (bit 4).
5069
INC HL 23
Point HL to byte 1.
506A
POP DE D1
Restore Register D = the old DEC.
506B
LD (HL),D 72
Byte 1 = the old DEC, the back link.
506C
GOSUB to 50AEH to write the directory sector.
506F
LD A,D 7A
Copy the old DEC into Register A.
5070
GOSUB to 5033H to point HL to the old entry (reading its sector). A = its offset.
5073
ADD A,1FH C6 1F
ADD 1FH: byte 31.
5075
LD L,A 6F
HL points to byte 31 of the old entry.
5076
LD (HL),C 71
Byte 31 = the new DEC.
5077
DEC HL 2B
Point HL to byte 30.
5078
LD (HL),FEH 36 FE
Byte 30 = FEH: the next element is the DEC of an FXDE.
507A
GOSUB to 50AEH to write the directory sector.
507D
LD A,C 79
Copy the new DEC into Register A.
507E
LD (505EH),A 32 5E 50
Store it at 505EH: the new entry is now the one being extended.
5081
GOSUB to 5033H to point HL to the new entry.
5084
GOSUB to 50B4H to copy its four (empty) elements to 51ADH-51B4H. HL = 51ADH.
5087
POP BC C1
Restore the granule's bit and count.
5088
POP DE D1
Restore DE = the GAT pointer.
5089
JUMP to 4FC5H to read the GAT again and go on.

508CH - Write the GAT and the Extent Elements

Writes the directory sector in 4200H (the GAT), then copies 51ADH-51B4H into bytes 22-29 of the entry being extended (505EH) and writes it. If that entry is the FPDE, the elements are also copied to FCB+14 to FCB+21. Returns HL = 4200H.

508C
GOSUB to 50AEH to write the GAT.
508F
LD A,(505EH) 3A 5E 50
Fetch the DEC of the entry being extended.
5092
GOSUB to 5033H to point HL to it (reading its sector). A = its offset.
5095
ADD A,16H C6 16
ADD 16H: byte 22, the first extent element.
5097
BIT 7,(HL) CB 7E
Test bit 7 of byte 0 (FXDE).
5099
LD L,A 6F
HL points to byte 22.
509A
PUSH HL E5
Save it.
509B
PUSH IX DD E5
Copy the FCB address...
509D
POP HL E1
...into HL.
509E
LD BC,000EH 01 0E 00
Load BC with 14.
50A1
ADD HL,BC 09
HL points to FCB+14, the FCB's copy of the FPDE's elements.
50A2
LD DE,51ADH 11 AD 51
Point DE to 51ADH, the elements.
50A5
PUSH DE D5
Save it.
50A6
An FPDE: GOSUB to 4F79H to copy the elements to FCB+14 to FCB+21.
50A9
POP HL E1
Restore HL = 51ADH.
50AA
POP DE D1
Restore DE = byte 22 of the entry.
50AB
GOSUB to 4F7AH to copy the elements into bytes 22-29 and fall into 50AEH to write the sector.

50AEH - Write the Directory Sector

Writes the sector in 4200H; an error leaves through 503DH.

50AE
GOSUB to the SYS0 routine at 491FH to write the directory sector in 4200H (HL = 4200H afterwards).
50B1
RET Z C8
Written: RETURN.
50B2
JUMP to 503DH to leave with the error.

50B4H - Copy an Entry's Extent Elements

Register A = the entry's offset in 4200H. Copies bytes 22-29 to 51ADH-51B4H and puts FEH at 51B5H. Returns HL pointing to the last element in use, with NZ; or to 51ADH with the Z FLAG if all four are empty.

50B4
ADD A,16H C6 16
ADD 16H: byte 22.
50B6
LD L,A 6F
HL points to byte 22 of the entry.
50B7
LD DE,51ADH 11 AD 51
Point DE to 51ADH.
50BA
GOSUB to 4F7AH to copy the four elements. A = 08H.
50BD
EX DE,HL EB
HL = 51B5H.
50BE
LD (HL),FEH 36 FE
Store FEH there, after the fourth element.
50C0
RRCA 0F
Register A = 4...
50C1
LD B,A 47
...into Register B, the number of elements.
50C2
DEC HL 2B
Loop Start
Point HL back...
50C3
DEC HL 2B
...one element.
50C4
LD A,(HL) 7E
Fetch its lump byte.
50C5
INC A 3C
INCrement it: 0 if it is empty (FFH).
50C6
RET NZ C0
In use: RETURN with NZ.
50C7
Loop End
LOOP BACK to 50C2H.
50C9
RET C9
All four are empty: RETURN with HL = 51ADH and the Z FLAG.

50CAH - Function E4H, C = 2: Find a Free Directory Entry

Register D = the drive, HL = the address of the hash code for the HIT, Register B = the DEC or hash code whose FDE sector the search starts in. 50CFH is called directly by the open (4DDEH), by 5061H and by SYS6 (610EH) with the drive already selected. The search starts in FDE sector (B AND 1FH) modulo the number of FDE sectors (8 + HIT byte 1FH), takes the first entry whose HIT byte is 00H, stores the hash code there and writes the HIT, then clears the entry (5110H). Returns HL = the entry, Register A = its DEC and the Z FLAG, or 1AH (DIRECTORY SPACE FULL), 2CH, or a disk error.

50CA
LD A,D 7A
Copy the drive number into Register A.
50CB
GOSUB to the SYS0 routine at 4776H to select it.
50CE
RET NZ C0
Bad drive: RETURN with the error.
50CF
EX DE,HL EB
Exchange: DE = the address of the hash code.
50D0
LD A,01H 3E 01
Load Register A with 01H, the HIT sector.
50D2
GOSUB to the SYS0 routine at 490AH to read the HIT into 4200H.
50D5
RET NZ C0
Read error: RETURN with it.
50D6
LD A,(421FH) 3A 1F 42
Fetch HIT byte 1FH, the number of extra FDE sectors.
50D9
ADD A,08H C6 08
ADD 8.
50DB
LD C,A 4F
Register C = the number of FDE sectors.
50DC
LD A,B 78
Copy the start value into Register A.
50DD
AND 1FH E6 1F
Keep bits 0-4.
50DF
SUB C 91
Loop Start
SUBtract the number of FDE sectors.
50E0
Loop End
LOOP BACK to 50DFH until it goes below 0.
50E2
ADD A,C 81
ADD it back: Register A = the first FDE sector to look in.
50E3
LD B,A 47
Also reached from 50F9H with A = 0. Register B = the first sector.
50E4
LD L,A 6F
HL = 4200H + sector: the HIT byte of entry 0 in that sector.
50E5
JUMP to 50F2H.
50E7
LD A,(HL) 7E
Loop Start
Fetch the HIT byte.
50E8
OR A B7
Set the flags.
50E9
00H, a free entry: JUMP to 50FEH.
50EB
LD A,L 7D
Copy the DEC into Register A.
50EC
ADD A,20H C6 20
ADD 20H: the next entry of the same sector.
50EE
LD L,A 6F
Register L = the next DEC.
50EF
Loop End
No carry: LOOP BACK to 50E7H.
50F1
INC L 2C
Past entry 7: the next sector, entry 0.
50F2
LD A,L 7D
Copy the DEC into Register A.
50F3
CP C B9
Compare it with the number of FDE sectors.
50F4
Below it: LOOP BACK to 50E7H.
50F6
XOR A AF
Set Register A to 0.
50F7
INC B 04
INCrement...
50F8
DEC B 05
...and DECrement Register B to test it.
50F9
The search did not start at sector 0: JUMP to 50E3H to search from sector 0.
50FB
OR 1AH F6 1A
Register A = 1AH (DIRECTORY SPACE FULL), NZ.
50FD
RET C9
RETURN with the error.
50FE
LD A,(DE) 1A
Fetch the hash code (at DE).
50FF
LD (HL),A 77
Store it in the HIT: the entry is taken.
5100
LD C,L 4D
Register C = the DEC.
5101
GOSUB to the SYS0 routine at 491FH to write the HIT.
5104
RET NZ C0
Write error: RETURN with it.
5105
LD A,C 79
Copy the DEC into Register A.
5106
GOSUB to the SYS0 routine at 4936H to read its FDE sector. HL points to the entry.
5109
RET NZ C0
Read error: RETURN with it.
510A
BIT 4,(HL) CB 66
Test bit 4 of byte 0, in use.
510C
LD A,2CH 3E 2C
Load Register A with error code 2CH (BAD DIRECTORY DATA).
510E
RET NZ C0
In use although its HIT byte was 00H: RETURN with error 2CH.
510F
LD A,C 79
Register A = the DEC. Fall into 5110H to clear the entry.

5110H - Clear an Entry or an FCB

HL points to 32 bytes: bytes 0-21 are set to 00H and bytes 22-31 to FFH (empty extent elements). Used for a new directory entry (510FH) and for the FCB (4F33H). Registers A and HL are kept; returns the Z FLAG.

5110
LD BC,0A16H 01 16 0A
Register B = 10 (bytes of FFH), Register C = 22 (bytes of 00H).
5113
PUSH HL E5
Save the start.
5114
LD (HL),00H 36 00
Loop Start
Store 00H.
5116
INC HL 23
Point HL to the next byte.
5117
DEC C 0D
DECrement the count.
5118
Loop End
LOOP BACK to 5114H for 22 bytes.
511A
LD (HL),FFH 36 FF
Loop Start
Store FFH.
511C
INC HL 23
Point HL to the next byte.
511D
Loop End
LOOP BACK to 511AH for 10 bytes.
511F
POP HL E1
Restore HL = the start.
5120
RET C9
RETURN.

5121H - Copy a Filespec Field

DE points to the character before the field, HL to where it goes, Register B = the field length (5121H sets 8). With the NZ FLAG on entry the field is not there and is filled with spaces. Otherwise its first character must be a letter, the others letters or digits; it ends at the first other character, which is returned in Register A, and the rest of the field is filled with spaces. A bad first character or a field that is too long returns error 30H (BAD FILESPEC) from the open.

5121
LD B,08H 06 08
Register B = 8, the length of a name or a password.
5123
Entry with Register B = the length. NZ (the field is not there): JUMP to 5140H to fill it with spaces.
5125
GOSUB to 5146H to fetch the first character.
5128
Not a letter: JUMP to 513BH to return error 30H.
512A
LD (HL),A 77
Loop Start
Store the character.
512B
INC HL 23
Point HL to the next position.
512C
GOSUB to 5146H to fetch the next character.
512F
A letter: JUMP to 5139H.
5131
CP 30H FE 30
Compare it with 30H (0).
5133
Below: the field ends, JUMP to 5143H.
5135
CP 3AH FE 3A
Compare it with 3AH (one past 9).
5137
Not a digit: the field ends, JUMP to 5143H.
5139
Loop End
DECrement the length left and LOOP BACK to 512AH to store the character.
513B
POP AF F1
Too long, or a bad first character. Discard the return address.
513C
LD A,30H 3E 30
Load Register A with error code 30H (BAD FILESPEC).
513E
OR A B7
Set the NZ FLAG.
513F
RET C9
RETURN from the open with the error.
5140
LD (HL),20H 36 20
Loop Start
Store a space.
5142
INC HL 23
Point HL to the next position.
5143
Loop End
DECrement the length left and LOOP BACK to 5140H to pad the field.
5145
RET C9
RETURN with the character after the field in Register A.

5146H - Fetch the Next Filespec Character

Returns the next character, in upper case, in Register A with NC if it is a letter A-Z, the CARRY FLAG otherwise.

5146
INC DE 13
Point DE to the next character.
5147
LD A,(DE) 1A
Fetch it.
5148
GOSUB to the SYS0 routine at 45B5H to change a lower case letter to upper case.
514B
CP 41H FE 41
Compare it with 41H (A).
514D
RET C D8
Below A: RETURN with the CARRY FLAG.
514E
CP 5BH FE 5B
Compare it with 5BH (one past Z).
5150
CCF 3F
Complement the CARRY FLAG: NC for A-Z.
5151
RET C9
RETURN.

5152H - Function 84H: Encode a Password

Works out the 16-bit code of the 8 characters ending at HL (5152H: the password at 51D8H-51DFH), going from the last character to the first, and sets the characters to spaces. Returns HL = the code, BC and DE kept. A blank password gives 4296H, PASSWORD gives 42E0H. Through RST 28H (code 84H) the caller's HL points to the last character and the code is returned in HL.

5152
LD HL,51DFH 21 DF 51
Point HL to 51DFH, the last password character.
5155
PUSH DE D5
Function 84H enters here. Save DE.
5156
PUSH BC C5
Save BC.
5157
LD DE,FFFFH 11 FF FF
Start the code at FFFFH (Register D = high byte, Register E = low byte).
515A
LD B,08H 06 08
Load Register B with 8, the number of characters.
515C
PUSH BC C5
Loop Start
Save the count.
515D
LD A,E 7B
Copy the low byte into Register A.
515E
AND 07H E6 07
Keep bits 0-2.
5160
LD C,A 4F
Save them in Register C.
5161
LD A,E 7B
Copy the low byte into Register A again.
5162
RLCA 07
Rotate it left...
5163
RLCA 07
...three...
5164
RLCA 07
...times.
5165
XOR C A9
XOR the bits kept in Register C.
5166
RLCA 07
Rotate left once more.
5167
LD C,A 4F
Save the result in Register C.
5168
AND F0H E6 F0
Keep its bits 4-7.
516A
LD B,A 47
Save them in Register B.
516B
LD A,C 79
Fetch the result from Register C.
516C
RLCA 07
Rotate it left.
516D
AND 1FH E6 1F
Keep bits 0-4.
516F
XOR B A8
XOR the bits in Register B.
5170
XOR D AA
XOR the high byte of the code.
5171
LD E,A 5F
This is the new low byte.
5172
LD A,C 79
Fetch the result from Register C.
5173
AND 0FH E6 0F
Keep bits 0-3.
5175
LD B,A 47
Save them in Register B.
5176
LD A,C 79
Fetch the result from Register C.
5177
RLCA 07
Rotate it left...
5178
RLCA 07
...four...
5179
RLCA 07
...times...
517A
RLCA 07
...(swap its halves).
517B
XOR B A8
XOR the bits in Register B.
517C
POP BC C1
Restore the count.
517D
XOR (HL) AE
XOR the character.
517E
LD D,A 57
This is the new high byte.
517F
LD (HL),20H 36 20
Replace the character with a space.
5181
DEC HL 2B
Point HL to the character before.
5182
Loop End
LOOP BACK to 515CH for all 8 characters.
5184
EX DE,HL EB
HL = the code.
5185
POP BC C1
Restore BC.
5186
POP DE D1
Restore DE.
5187
RET C9
RETURN.

5188H - Unused Bytes

The end of the file. Nothing in SYS2 reaches these bytes.

5188-51AC
NOP x 37 00 x 37
37 bytes of 00H to the end of the file (51ACH). 51ADH-51FFH is not in the file; it is used as work space (see Variables).