TRS-80 DOS - VTOS 4.0.2 for the Model I - SYS3/SYS Disassembled

Page Customization

Introduction/Summary

VTOS 4.0.2 SYS3/SYS Disassembly - Closing Files and Devices (Model I)

SYS3/SYS is the system overlay that closes files and devices. The request handler of SYS0/SYS (4C31H) loads it at 4E00H for every request code whose bits 3-0 are 5 (directory entry code 05H). A program closes a file through the entry 4428H (request 95H); the DEVICE command asks it for the name of a file through 44BBH (request B5H). The manual calls it the system close overlay, which must not be purged.

The file starts with a record of type 05H (the name SYS3), a record of type 1FH holding the copyright notice of Randolph Cook (1980) and three load blocks that fill 4E00H-5059H. Then come a record of type 07H naming the patch SYS3ABC (the distributor's manual: Timothy P. Mann and Jesse Bob Overholt) and seven load blocks: six load over the first ones (4E08H, 4FE3H-4FE5H, 4FE8H-4FEAH, 4EC6H-4ED0H, 4F74H-4F86H, 4F89H) and one adds 505AH-5061H past the end of the file. The last record gives the transfer address 4E00H. This page shows the bytes as they are after the patch, 610 in all.

The Functions

CodeFunction
95HFunction 1 (4428H): close the file or device whose control block is at Register Pair DE (4E0DH). Register A = 00H with the Z FLAG when closed, or the error code with the NZ FLAG. Register Pairs BC, DE and HL and Index Register IX are given back.
B5HFunction 3 (44BBH): write the name of the file of directory entry code Register B on drive Register C as text at Register Pair DE (4FCBH).
85H, A5H, C5H-F5HReturn at once with the NZ FLAG.

How a File Is Closed

  1. A control block whose first byte has bits 7 and 6 clear is a device route (type 10H, made by SYS2/SYS): it becomes the text of the device's name again, an asterisk, the two letters and 03H (4FD8H). Any other block must be open (bit 7), or the request returns error 26H, FILE NOT OPEN.
  2. A changed buffer of a file with records shorter than 256 bytes is written to its sector (SYS0/SYS 4988H).
  3. The directory record is read. When the end of file of the control block (+08H, +0CH-+0DH) differs from the record's (+03H, +14H-+15H) it is stored. When the file was written while open (bit 2 of byte +00H of the control block), byte +01H of the record gets bit 6, the mark for files changed since the last BACKUP, and the date of the system (4E63H). The record is written back when either changed.
  4. Unless the ALLOC command set bit 7 of byte +01H of the record, the granules the file holds beyond the sectors it needs are given back: their bits are cleared in the granule allocation table, the extents shortened, and an extension record left with no extent freed together with its hash index byte and the link to it (4E91H, 4EE7H).
  5. The control block becomes the text of the file's specification, the name, the extension and the drive, without the password (4F74H). Its first byte is then a letter: the block is closed.

The Directory Record

ByteWhat SYS3/SYS does with it
+00HThe attribute byte: bit 7 marks an extension record; 00H is stored when one is freed.
+01HBit 7: set by ALLOC, the space is kept. Bit 6: set when a file written while open is closed. Bits 4-0: the month of the date; with no date in memory bit 4 is set and the old month bits stay. In an extension record: the directory entry code of the record that links to it.
+02HThe day times 8 plus the year less 80.
+03HThe end of file offset (control block +08H).
+14H-+15HThe ending record number (control block +0CH-+0DH).
+16H-+1FHThe extents, two bytes each: the cylinder, then bits 7-5 the first granule and bits 4-0 the granules less one. A cylinder byte FEH is a link: the next byte is the directory entry code of the extension record. FFH ends the list.

Error Codes

CodeMeaning (the texts of SYS4/SYS)
0FHWRITE PROTECTED DISK: the directory record could not be written (SYS0/SYS 4B1FH).
11H, 12HDIRECTORY READ ERROR, DIRECTORY WRITE ERROR (SYS0/SYS 4B10H, 4B1FH).
14H, 15HGAT READ ERROR, GAT WRITE ERROR: the granule allocation table (5003H, 5016H).
16H, 17HHIT READ ERROR, HIT WRITE ERROR: the hash index (502FH, 5041H).
26HFILE NOT OPEN (SYS0/SYS 49EBH).

A buffer that cannot be written (SYS0/SYS 4988H) gives that write's error.

Points to Note

When the granules given back reach past an extension record into the record that links to it, the next pass starts at byte +1EH of that record, which was just set to FFH, instead of the second byte of its last extent at +1DH (4F5FH). The FFH is read as first granule 7 with 32 granules and +1DH as the cylinder, so a bit of an unrelated allocation byte is cleared; each further pass lowers +1EH by one and never finds the extent empty. The record is written that way (4F66H): with one granule left it ends FEH FFH, a link to entry FFH, otherwise +1EH holds a false extent, and the granules of the real last extent are not given back.

On a write protected diskette, a close of a file that was not written, whose end of file is unchanged and that frees no extension record ends with no error although granules found beyond its end are not given back (4F6EH).

The date stamp puts the year less 80 into bits 2-0 of byte +02H without a mask, so a year above 87 runs into the day bits. Bit 4 of byte +01H is set only when no date is in memory; the date bytes then keep the values the record already held (4E63H).

The count of the extents stops only at a byte FEH or FFH (4EA0H).

Since the patch SYS3ABC the LD A,04H at 4FE1H loads a value nothing uses.

Memory Map

Load Areas

Addresses
Size
Contents
4E00H-4E0CH
13 bytes
Dispatcher
The two functions (4E08H patched).
4E0DH-4E62H
86 bytes
Close: the End of the File
The open test, the buffer, the end of file compared and stored.
4E63H-4E90H
46 bytes
Update Mark and Date
Byte +01H and +02H of the directory record, the record written.
4E91H-4EE6H
86 bytes
Count the Granules
The extents and extension records; the granules beyond the end (4EC6H-4ED0H patched).
4EE7H-4F73H
141 bytes
Give Back the Granules
The allocation table, the extents, freed extension records, the hash index.
4F74H-4F86H
19 bytes
Finish the Close
Patch SYS3ABC: the file's directory sector read again.
4F87H-4FCAH
68 bytes
Name as Text
NAME/EXT:d and 03H (4F89H patched).
4FCBH-4FD7H
13 bytes
Function 3
The name of a file for the DEVICE command.
4FD8H-4FF5H
30 bytes
Close a Device
The text of the device name (4FE3H-4FE5H, 4FE8H-4FEAH patched).
4FF6H-5002H
13 bytes
Clear a Bit
RES n,B built at 5001H.
5003H-5059H
87 bytes
Directory Sectors
Read and write the allocation table and the hash index.
505AH-5061H
8 bytes
Add Register A to HL
Patch SYS3ABC, past the end of the original file.

Variables

Memory Outside the File

Address
Size
Contents
5062H-50FFH
158 bytes
Not Used
Not in the file and not used by SYS3/SYS.
5100H-51FFH
256 bytes
Allocation Table Buffer
Not in the file: sector 0 of the directory cylinder (5003H, 5016H); 5100H plus a cylinder is its allocation byte.
4200H-42FFH
256 bytes
Sector Buffer of SYS0/SYS
The directory sector of each record read (4B10H) and the hash index (502FH).
4044H-4046H
3 bytes
Date
The year (80-87, 00H when not set), the day and the month, read at 4E6BH-4E82H for the date stamp.

Operands Changed While Running

Address
Size
Contents
4F4BH
1 byte
Entry Code of the Linking Record
Operand of LD B,00H at 4F4AH: byte +01H of a freed extension record (4F3CH).
4FC3H
1 byte
Drive Digit
Operand of LD A,00H at 4FC2H: 30H plus the drive (4F8CH).
5001H
1 byte
RES n,B
Second byte of the instruction at 5000H: 80H plus the bit number times 8 (4FFDH).

Outside Routines

AddressContents
49E9HSYS0/SYS: the start of a file request without the open test (4FD8H).
49EBHSYS0/SYS: the start of a file request: error 26H when the control block is not open (4E13H).
4A0CHSYS0/SYS: where every RET of a request started at 49E9H or 49EBH goes: Register Pairs BC, DE, HL and Index Register IX given back.
4988HSYS0/SYS: write a changed record buffer (4E16H).
4B10HSYS0/SYS: read the directory sector of entry Register B into 4200H (4E20H, 4EB5H, 4F4CH, 4F7EH, 4FCDH).
4B1FHSYS0/SYS: write it back and check it (4E8CH, 4F30H, 4F5CH, 4F66H).
4B45HSYS0/SYS: read a sector of the directory cylinder (500DH, 5039H).
4B65HSYS0/SYS: Register D = the directory cylinder of drive Register C (5005H, 5018H, 5031H, 5043H).
4B7BHSYS0/SYS: Register A = Register E / Register A, Register E = the remainder (4EFFH).
4BA9HSYS0/SYS: Register Pair HL = Register Pair HL / Register A (4ED2H).
4797HSYS0/SYS: byte Register A of the drive code table of drive Register C (4EC8H, 4EF6H).
4768HSYS0/SYS: write a sector with the data mark FAH (5020H, 504BH).
4772HSYS0/SYS: read a sector back without storing it (5025H, 5050H).

Disassembly

4E00H - Request Dispatcher

SYS3/SYS is loaded at 4E00H by the request handler of SYS0/SYS for every request code whose bits 3-0 are 5 (directory entry code 05H) and entered here with Register A = the request code. Bits 6-4 choose the function: 1 (95H, the entry 4428H) closes a file or device, 3 (B5H, the entry 44BBH) writes the name of a file into a text. The other codes return at once. Registers BC, DE and HL hold the caller's values. The operand 30H at 4E08H is part of the patch SYS3ABC: before it the byte was 20H, so request A5H reached 4FCBH and B5H, the code 44BBH gives, returned without doing anything.

4E00
AND 70H E6 70
Mask Register A (the request code) to bits 6-4, the function number times 10H: 95H gives 10H, B5H gives 30H.
4E02
CP 10H FE 10
Compare Register A (the function bits) against 10H, function 1, request 95H (4428H): close.
4E04
If the Z FLAG is set (request 95H), JUMP to 4E0DH to close the file or device whose control block is at Register Pair DE.
4E07
CP 30H FE 30
Compare Register A (the function bits) against 30H, function 3, request B5H (44BBH). The operand 30H is the patch SYS3ABC (it was 20H).
4E09
If the Z FLAG is set (request B5H), JUMP to 4FCBH to write the name of the file of directory entry code Register B on drive Register C as text at Register Pair DE.
4E0C
RET C9
RETURN to the caller of the request with Register A = the function bits and the NZ FLAG for any other request code (85H, A5H, C5H, D5H, E5H, F5H): nothing is done.

4E0DH - Close: the End of the File

Function 1, request 95H (4428H). Register Pair DE = the control block. A control block whose first byte has bits 7 and 6 clear (type 10H, a route to a device, made by SYS2/SYS) is a device and goes to 4FD8H. For a file, SYS0/SYS's 49EBH tests the open bit and points Index Register IX at the block; every later RET of this routine goes to SYS0/SYS's 4A0CH, which gives the caller back Register Pairs BC, DE and HL and Index Register IX. The buffer is written when it holds a changed record, the directory record is read, and its end of file offset (+03H) and ending record number (+14H-+15H) are compared with the control block's (+08H, +0CH-+0DH).

4E0D
LD A,(DE) 1A
Fetch byte +00H of the control block at Register Pair DE into Register A: 80H for an open file, 10H for a device route, a letter for a closed one.
4E0E
AND C0H E6 C0
Mask Register A (the first byte of the control block) to bits 7 and 6: 00H for a device route or a text that starts with a character below 40H.
4E10
If the Z FLAG is set (bits 7 and 6 of the first byte clear), JUMP to 4FD8H to close a device: the control block becomes the text of its name again.
4E13
GOSUB to 49EBH (SYS0/SYS) with Register Pair DE = the control block: a block whose bit 7 is clear (not open) makes 49EBH return to the caller of this request with error 26H, FILE NOT OPEN; an open one gets Index Register IX = the block, and the caller's Register Pairs BC, DE, HL and Index Register IX are saved for 4A0CH.
4E16
GOSUB to 4988H (SYS0/SYS) to write the buffer of the file at Index Register IX back to its sector when byte +01H has bit 7 (record mode) and bit 4 (the buffer changed) set; Register A = 00H with the Z FLAG when nothing had to be written or the write was good.
4E19
RET NZ C0
If the NZ FLAG is set (the buffer could not be written), RETURN with the error code in Register A.
4E1A
LD B,(IX+07H) DD 46 07
Fetch byte +07H of the control block at Index Register IX, the directory entry code of the file, into Register B.
4E1D
LD C,(IX+06H) DD 4E 06
Fetch byte +06H of the control block at Index Register IX, the drive, into Register C.
4E20
GOSUB to 4B10H (SYS0/SYS) to read the directory sector of entry Register B on drive Register C into 4200H; Register Pair HL = the record (4200H plus the record's offset), Register A = 11H.
4E23
RET NZ C0
If the NZ FLAG is set (the sector could not be read), RETURN with error 11H, DIRECTORY READ ERROR, in Register A.
4E24
LD A,03H 3E 03
Load Register A with 03H, the offset of the end of file byte in a directory record.
4E26
ADD A,L 85
Add Register L (the low byte of the record's address) to Register A.
4E27
LD L,A 6F
Copy Register A into Register L: Register Pair HL = byte +03H of the directory record, the end of file offset.
4E28
PUSH HL E5
Save Register Pair HL (the address of record +03H) to the stack.
4E29
LD A,(IX+08H) DD 7E 08
Fetch byte +08H of the control block at Index Register IX, the end of file offset of the file as it is now, into Register A.
4E2C
CP (HL) BE
Compare Register A (the end of file offset of the control block) against byte +03H of the directory record at Register Pair HL.
4E2D
If the NZ FLAG is set (the offsets differ), JUMP to 4E43H to store the new end of file in the record.
4E2F
LD A,11H 3E 11
Load Register A with 11H, the step from byte +03H to byte +14H of the record.
4E31
ADD A,L 85
Add Register L (the low byte of the address of record +03H) to Register A.
4E32
LD L,A 6F
Copy Register A into Register L: Register Pair HL = byte +14H of the directory record, the low byte of the ending record number.
4E33
LD A,(IX+0CH) DD 7E 0C
Fetch byte +0CH of the control block at Index Register IX, the low byte of the ending record number, into Register A.
4E36
CP (HL) BE
Compare Register A (the low byte of the ending record number of the control block) against byte +14H of the record at Register Pair HL.
4E37
If the NZ FLAG is set (they differ), JUMP to 4E43H to store the new end of file in the record.
4E39
INC L 2C
Increment Register L: Register Pair HL = byte +15H of the directory record, the high byte of the ending record number.
4E3A
LD A,(IX+0DH) DD 7E 0D
Fetch byte +0DH of the control block at Index Register IX, the high byte of the ending record number, into Register A.
4E3D
CP (HL) BE
Compare Register A (the high byte of the ending record number of the control block) against byte +15H of the record at Register Pair HL.
4E3E
If the NZ FLAG is set (they differ), JUMP to 4E43H to store the new end of file in the record.
4E40
POP AF F1
Restore Register Pair AF from the stack: it takes the address of record +03H off the stack, since the end of file is unchanged. Register Pair HL stays at record +15H.
4E41
JUMP to 4E5DH with Register Pair HL = record +15H: the end of file in the directory is the file's.

4E43H - Store the End of File

The end of file offset and the ending record number of the control block go into the directory record. When the file was written while open (bit 2 of byte +00H of the control block, set by SYS0/SYS's write) the record also gets the update mark and the date (4E63H); otherwise it is written as it is (4E8BH).

4E43
POP HL E1
Restore Register Pair HL from the stack: the address of byte +03H of the directory record.
4E44
LD A,(IX+08H) DD 7E 08
Fetch byte +08H of the control block at Index Register IX, the end of file offset, into Register A.
4E47
LD (HL),A 77
Store Register A (the end of file offset) to byte +03H of the directory record at Register Pair HL.
4E48
LD A,11H 3E 11
Load Register A with 11H, the step from byte +03H to byte +14H of the record.
4E4A
ADD A,L 85
Add Register L (the low byte of the address of record +03H) to Register A.
4E4B
LD L,A 6F
Copy Register A into Register L: Register Pair HL = byte +14H of the directory record.
4E4C
LD A,(IX+0CH) DD 7E 0C
Fetch byte +0CH of the control block at Index Register IX, the low byte of the ending record number, into Register A.
4E4F
LD (HL),A 77
Store Register A (the low byte of the ending record number) to byte +14H of the directory record at Register Pair HL.
4E50
INC L 2C
Increment Register L: Register Pair HL = byte +15H of the directory record.
4E51
LD A,(IX+0DH) DD 7E 0D
Fetch byte +0DH of the control block at Index Register IX, the high byte of the ending record number, into Register A.
4E54
LD (HL),A 77
Store Register A (the high byte of the ending record number) to byte +15H of the directory record at Register Pair HL.
4E55
BIT 2,(IX+00H) DD CB 00 56
Test bit 2 of byte +00H of the control block at Index Register IX: set by SYS0/SYS when a sector of the file was written.
4E59
If the Z FLAG is set (the file was not written), JUMP to 4E8BH to write the directory record with the new end of file and no date.
4E5B
JUMP to 4E63H to mark the record updated and date it before it is written.
4E5D
BIT 2,(IX+00H) DD CB 00 56
Test bit 2 of byte +00H of the control block at Index Register IX: set by SYS0/SYS when a sector of the file was written. Register Pair HL = record +15H.
4E61
If the Z FLAG is set (not written and the end of file unchanged), JUMP to 4E91H: the directory record need not be written.

4E63H - Mark the Record Updated and Date It

Byte +01H of the directory record gets bit 6 (the file was changed: the plus sign the DIR command shows until BACKUP copies the file) and bit 4. When the date is set (4044H, the year, not 00H) bits 4-0 of +01H become the month and byte +02H the day times 8 plus the year less 80; bit 4 is then clear, since a month is at most 12. With no date in memory the date bytes keep their old values and bit 4 stays set. Register Pair HL = record +15H on entry and on exit.

4E63
PUSH HL E5
Save Register Pair HL (the address of record +15H) to the stack.
4E64
LD A,L 7D
Copy Register L (the low byte of the address of record +15H) into Register A.
4E65
AND E0H E6 E0
Mask Register A to bits 7-5: the offset of the record in the sector, the address of its byte +00H.
4E67
LD L,A 6F
Copy Register A into Register L: Register Pair HL = byte +00H of the directory record.
4E68
INC L 2C
Increment Register L: Register Pair HL = byte +01H of the directory record.
4E69
SET 6,(HL) CB F6
Set bit 6 of byte +01H of the directory record at Register Pair HL: the file has been updated since it was last backed up.
4E6B
LD DE,4044H 11 44 40
Load Register Pair DE with 4044H, the year of the date in SYS0/SYS (binary, 80-87; 00H when no date was set).
4E6E
SET 4,(HL) CB E6
Set bit 4 of byte +01H of the directory record at Register Pair HL; the month stored below clears it again.
4E70
LD A,(DE) 1A
Fetch the year from 4044H at Register Pair DE into Register A.
4E71
OR A B7
OR Register A with itself to test the year for 00H.
4E72
If the Z FLAG is set (no date in memory), JUMP to 4E8AH: the date bytes of the record are left as they are.
4E74
SUB 50H D6 50
SUBtract 50H (80) from Register A: Register A = the year less 80, 0-7 for the years 80-87 (no mask follows, so a later year reaches bit 3).
4E76
PUSH BC C5
Save Register Pair BC (Register B the directory entry code, Register C the drive) to the stack.
4E77
LD B,A 47
Copy Register A (the year less 80) into Register B.
4E78
INC DE 13
Increment Register Pair DE to 4045H, the day of the month.
4E79
LD A,(DE) 1A
Fetch the day from 4045H at Register Pair DE into Register A.
4E7A
RLCA 07
Rotate Register A (the day) left: day times 2.
4E7B
RLCA 07
Rotate Register A (the day times 2) left again: the day times 4.
4E7C
RLCA 07
Rotate Register A left again: day times 8, the day in bits 7-3.
4E7D
OR B B0
OR Register B (the year less 80) into Register A: bits 7-3 the day, bits 2-0 the year less 80 (a year above 87 also sets day bits).
4E7E
INC L 2C
Increment Register L: Register Pair HL = byte +02H of the directory record.
4E7F
LD (HL),A 77
Store Register A (the day and the year) to byte +02H of the directory record at Register Pair HL.
4E80
DEC L 2D
Decrement Register L: Register Pair HL = byte +01H of the directory record again.
4E81
INC DE 13
Increment Register Pair DE to 4046H, the month.
4E82
LD A,(DE) 1A
Fetch the month (1-12) from 4046H at Register Pair DE into Register A.
4E83
LD B,A 47
Copy Register A (the month, 1-12) into Register B for the OR at 4E87H.
4E84
LD A,(HL) 7E
Fetch byte +01H of the directory record at Register Pair HL into Register A.
4E85
AND E0H E6 E0
Mask Register A to bits 7-5 of byte +01H: bit 7 (the space set by ALLOC is kept) and bit 6 (updated) stay; bits 4-0 are cleared.
4E87
OR B B0
OR Register B (the month) into Register A: bits 4-0 = the month.
4E88
LD (HL),A 77
Store Register A to byte +01H of the directory record at Register Pair HL.
4E89
POP BC C1
Restore Register Pair BC from the stack: Register B the directory entry code, Register C the drive.
4E8A
POP HL E1
Restore Register Pair HL from the stack: the address of byte +15H of the record.
4E8B
PUSH HL E5
Save Register Pair HL (the address of record +15H) to the stack. Entry 4E8BH: the end of file changed and the file was not written.
4E8C
GOSUB to 4B1FH (SYS0/SYS) to write the directory sector in 4200H back to entry Register B of drive Register C and read it back.
4E8F
POP HL E1
Restore Register Pair HL from the stack: the address of byte +15H of the record.
4E90
RET NZ C0
If the NZ FLAG is set (the sector could not be written), RETURN with the error in Register A: 12H, DIRECTORY WRITE ERROR, or 0FH, WRITE PROTECTED DISK.

4E91H - Count the Granules of the File

Space a file holds beyond its end is given back to the allocation table, unless bit 7 of byte +01H of its directory record is set: the ALLOC command (SYS6/SYS) sets it to keep a minimum size. The extents from +16H (two bytes each: the cylinder, then bits 7-5 the first granule and bits 4-0 the granules less one) are added up into Register Pair DE. A cylinder byte FEH links to an extension record whose directory entry code is the next byte; FFH ends the list. The loop stops only at a byte FEH or FFH.

4E91
INC L 2C
Increment Register L: Register Pair HL = byte +16H of the directory record, the first extent.
4E92
PUSH HL E5
Save Register Pair HL (the address of record +16H) to the stack.
4E93
LD A,L 7D
Copy Register L (the low byte of the address of record +16H) into Register A.
4E94
SUB 15H D6 15
SUBtract 15H from Register A: the low byte of the address of record +01H.
4E96
LD L,A 6F
Copy Register A into Register L: Register Pair HL = byte +01H of the directory record.
4E97
BIT 7,(HL) CB 7E
Test bit 7 of byte +01H of the directory record at Register Pair HL: set by the ALLOC command for a file whose space is to be kept.
4E99
POP HL E1
Restore Register Pair HL from the stack: the address of byte +16H of the record. POP does not change the flags.
4E9A
If the NZ FLAG is set (bit 7 set: the space set by ALLOC is kept), JUMP to 4F74H to finish the close.
4E9D
LD DE,0000H 11 00 00
Load Register Pair DE with 0000H: the count of the granules of the file.
4EA0
LD A,(HL) 7E
Fetch the cylinder byte of the extent at Register Pair HL into Register A.
4EA1
INC L 2C
Increment Register L: Register Pair HL = the extent's second byte.
4EA2
CP FEH FE FE
Compare Register A (the cylinder byte) against FEH.
4EA4
If the NC FLAG is set (FEH, a link, or FFH, the end), JUMP to 4EB2H.
4EA6
LD A,(HL) 7E
Fetch the extent's second byte at Register Pair HL into Register A: bits 7-5 the first granule, bits 4-0 the granules less one.
4EA7
INC L 2C
Increment Register L: Register Pair HL = the next extent.
4EA8
AND 1FH E6 1F
Mask Register A to bits 4-0: the granules of the extent less one.
4EAA
INC A 3C
Increment Register A: the granules of the extent.
4EAB
ADD A,E 83
Add Register E (the low byte of the count) to Register A.
4EAC
LD E,A 5F
Copy Register A into Register E: the low byte of the count of granules.
4EAD
If the NC FLAG is set (no carry), JUMP back to 4EA0H for the next extent.
4EAF
INC D 14
Increment Register D: the carry into the high byte of the count in Register Pair DE.
4EB0
JUMP back to 4EA0H to add the granules of the next extent to Register Pair DE.
4EB2
If the NZ FLAG is set (the byte was FFH, the end of the list), JUMP to 4EBFH with Register Pair HL = the byte after it.
4EB4
LD B,(HL) 46
Fetch the byte after FEH at Register Pair HL into Register B: the directory entry code of the extension record.
4EB5
GOSUB to 4B10H (SYS0/SYS) to read the directory sector of entry Register B on drive Register C into 4200H; Register Pair HL = the extension record.
4EB8
RET NZ C0
If the NZ FLAG is set (the sector could not be read), RETURN with error 11H, DIRECTORY READ ERROR, in Register A.
4EB9
LD A,L 7D
Copy Register L (the low byte of the address of the extension record) into Register A.
4EBA
ADD A,16H C6 16
Add 16H to Register A: the offset of the first extent of the extension record.
4EBC
LD L,A 6F
Copy Register A into Register L: Register Pair HL = byte +16H of the extension record.
4EBD
JUMP back to 4EA0H to count the extents of the extension record.

4EBFH - The Granules Beyond the End

The file needs the ending record number (+0CH-+0DH of the control block: the sectors up to the end of file) divided by the sectors of a granule, rounded up. The 11 bytes at 4EC6H-4ED0H and the routine 505AH are the patch SYS3ABC: before it the code added 4 to the ending record number, the rounding for 5 sectors a granule, whatever the drive's granule held. Register Pair DE = the granules the file holds; Register Pair HL = the byte after the FFH that ends the list.

4EBF
PUSH HL E5
Save Register Pair HL (the address after the FFH that ends the extents) to the stack.
4EC0
LD L,(IX+0CH) DD 6E 0C
Fetch byte +0CH of the control block at Index Register IX, the low byte of the ending record number, into Register L.
4EC3
LD H,(IX+0DH) DD 66 0D
Fetch byte +0DH of the control block at Index Register IX, the high byte, into Register H: Register Pair HL = the ending record number.
4EC6
LD A,08H 3E 08
Load Register A with 08H, the offset of the granule byte in a drive code table. Patch SYS3ABC.
4EC8
GOSUB to 4797H (SYS0/SYS) to fetch byte +08H of the drive code table of drive Register C into Register A: bits 7-5 the granules of a cylinder less one, bits 4-0 the sectors of a granule less one.
4ECB
AND 1FH E6 1F
Mask Register A to bits 4-0: the sectors of a granule less one.
4ECD
GOSUB to 505AH to add Register A to Register Pair HL: the ending record number plus the sectors of a granule less one, so that the division rounds up.
4ED0
NOP 00
No operation: the byte the patch SYS3ABC left over at the end of its 11 bytes.
4ED1
INC A 3C
Increment Register A: the sectors of a granule.
4ED2
GOSUB to 4BA9H (SYS0/SYS) to divide Register Pair HL by Register A: Register Pair HL = the granules the file needs.
4ED5
XOR A AF
Set Register A to 00H (XOR A), which clears the CARRY FLAG for the subtraction.
4ED6
EX DE,HL EB
Exchange Register Pair DE (the granules held) with Register Pair HL (the granules needed).
4ED7
SBC HL,DE ED 52
SUBtract Register Pair DE (the granules needed) from Register Pair HL (the granules held): the granules beyond the end of file.
4ED9
EX DE,HL EB
Exchange Register Pair DE with Register Pair HL: Register Pair DE = the granules to give back.
4EDA
POP HL E1
Restore Register Pair HL from the stack: the address after the FFH that ends the extents.
4EDB
If the Z FLAG is set (no granule beyond the end), JUMP to 4F74H to finish the close.
4EDE
If the C FLAG is set (the file holds fewer granules than it needs), JUMP to 4F74H to finish the close.
4EE1
DEC L 2D
Decrement Register L: Register Pair HL = the FFH byte that ends the extents.
4EE2
DEC L 2D
Decrement Register L: Register Pair HL = the second byte of the last extent (first granule and count).
4EE3
GOSUB to 5003H to read the granule allocation table of drive Register C into 5100H.
4EE6
RET NZ C0
If the NZ FLAG is set (the table could not be read), RETURN with error 14H, GAT READ ERROR, in Register A.

4EE7H - Give Back One Granule

One pass for each granule in Register Pair DE, from the end of the last extent: the granule's bit in the allocation table at 5100H is cleared and the extent's count is lowered by one. An extent left empty is set to FFH FFH; when it was the first extent of an extension record, that record is freed (attribute 00H), its hash index byte is cleared, and the record before it loses its link (+1EH-+1FH = FFH FFH). Register Pair HL = the second byte of the extent, Register B = the directory entry code of the record in 4200H, Register C = the drive.

4EE7
PUSH DE D5
Save Register Pair DE (the granules still to give back) to the stack.
4EE8
LD A,(HL) 7E
Fetch the extent's second byte at Register Pair HL into Register A.
4EE9
AND E0H E6 E0
Mask Register A to bits 7-5: the first granule of the extent times 32.
4EEB
RLCA 07
Rotate Register A left: bits 7-5 move to bit 0 and bits 7-6.
4EEC
RLCA 07
Rotate Register A left again: the first granule moves on towards bits 2-0.
4EED
RLCA 07
Rotate Register A left again: Register A = the first granule, 0-7.
4EEE
LD E,A 5F
Copy Register A (the first granule) into Register E.
4EEF
LD A,(HL) 7E
Fetch the extent's second byte at Register Pair HL into Register A again.
4EF0
AND 1FH E6 1F
Mask Register A to bits 4-0: the granules of the extent less one.
4EF2
ADD A,E 83
Add Register E (the first granule) to Register A: the number of the extent's last granule, counted from the start of its first cylinder.
4EF3
LD E,A 5F
Copy Register A into Register E: the last granule of the extent.
4EF4
LD A,08H 3E 08
Load Register A with 08H, the offset of the granule byte in a drive code table.
4EF6
GOSUB to 4797H (SYS0/SYS) to fetch byte +08H of the drive code table of drive Register C into Register A: bits 7-5 the granules of a cylinder less one.
4EF9
RLCA 07
Rotate Register A left: bits 7-5 move towards bits 2-0.
4EFA
RLCA 07
Rotate Register A left again: the count moves on towards bits 2-0.
4EFB
RLCA 07
Rotate Register A left again: bits 2-0 = the granules of a cylinder less one.
4EFC
AND 07H E6 07
Mask Register A to bits 2-0: the granules of a cylinder less one.
4EFE
INC A 3C
Increment Register A: the granules of a cylinder.
4EFF
GOSUB to 4B7BH (SYS0/SYS) to divide Register E (the last granule) by Register A: Register A = the cylinders past the extent's first, Register E = the granule in that cylinder.
4F02
DEC L 2D
Decrement Register L: Register Pair HL = the cylinder byte of the extent.
4F03
ADD A,(HL) 86
Add the extent's cylinder at Register Pair HL to Register A: the cylinder of the last granule.
4F04
LD D,A 57
Copy Register A (the cylinder) into Register D.
4F05
PUSH HL E5
Save Register Pair HL (the address of the extent's cylinder byte) to the stack.
4F06
PUSH BC C5
Save Register Pair BC (Register B the directory entry code, Register C the drive) to the stack.
4F07
LD H,51H 26 51
Load Register H with 51H, the page of the allocation table read at 5003H.
4F09
LD L,D 6A
Copy Register D (the cylinder) into Register L: Register Pair HL = 5100H plus the cylinder, the cylinder's allocation byte.
4F0A
LD B,(HL) 46
Fetch the allocation byte at Register Pair HL into Register B: bit n set = granule n of the cylinder in use.
4F0B
LD A,E 7B
Copy Register E (the granule in the cylinder) into Register A.
4F0C
GOSUB to 4FF6H to clear bit Register A of Register B: the granule is free.
4F0F
LD (HL),B 70
Store Register B (the allocation byte with the granule's bit cleared) to the allocation table at Register Pair HL.
4F10
POP BC C1
Restore Register Pair BC from the stack: Register B the directory entry code, Register C the drive.
4F11
POP HL E1
Restore Register Pair HL from the stack: the address of the extent's cylinder byte.
4F12
INC L 2C
Increment Register L: Register Pair HL = the extent's second byte.
4F13
DEC (HL) 35
Decrement the extent's second byte at Register Pair HL: one granule less in the count of bits 4-0 (from 00H the borrow takes 1 from the first granule in bits 7-5 and leaves bits 4-0 = 1FH).
4F14
LD A,(HL) 7E
Fetch the extent's second byte at Register Pair HL into Register A.
4F15
INC A 3C
Increment Register A: bits 4-0 become 00H only when they were 1FH, an extent with no granule left.
4F16
AND 1FH E6 1F
Mask Register A to bits 4-0: 00H and the Z FLAG for an extent left with no granule.
4F18
POP DE D1
Restore Register Pair DE from the stack: the granules still to give back. POP does not change the flags.
4F19
If the NZ FLAG is set (the extent still has granules), JUMP to 4F61H to count this granule and go on.
4F1B
LD (HL),FFH 36 FF
Store FFH to the extent's second byte at Register Pair HL: the extent is empty.
4F1D
DEC L 2D
Decrement Register L: Register Pair HL = the extent's cylinder byte.
4F1E
LD (HL),FFH 36 FF
Store FFH to the cylinder byte at Register Pair HL: the extent slot ends the list.
4F20
DEC L 2D
Decrement Register L: Register Pair HL = the second byte of the extent before.
4F21
LD A,L 7D
Copy Register L (the low byte of that address) into Register A.
4F22
AND 1FH E6 1F
Mask Register A to bits 4-0: the offset in the record.
4F24
CP 15H FE 15
Compare Register A against 15H: byte +15H means the extent just emptied was the record's first (+16H).
4F26
If the NZ FLAG is set (an earlier extent of the record is left), JUMP to 4F61H to count this granule and go on with Register Pair HL at that extent.
4F28
XOR L AD
Exclusive OR Register L into Register A (15H): Register A = Register L with bits 4-0 cleared, the address of the record's byte +00H.
4F29
LD L,A 6F
Copy Register A into Register L: Register Pair HL = byte +00H of the directory record, the attribute byte.
4F2A
BIT 7,(HL) CB 7E
Test bit 7 of the attribute byte at Register Pair HL: set for an extension record.
4F2C
If the Z FLAG is set (the file's own record, now without extents), JUMP to 4F61H to count this granule.
4F2E
LD (HL),00H 36 00
Store 00H to the attribute byte at Register Pair HL: the extension record is free.
4F30
GOSUB to 4B1FH (SYS0/SYS) to write the directory sector in 4200H back to entry Register B of drive Register C and read it back.
4F33
RET NZ C0
If the NZ FLAG is set (the sector could not be written), RETURN with the error in Register A (12H or 0FH).
4F34
LD A,B 78
Copy Register B (the directory entry code of the freed extension record) into Register A.
4F35
AND E0H E6 E0
Mask Register A to bits 7-5: the offset of the record in its sector.
4F37
INC A 3C
Increment Register A: the offset of the record's byte +01H.
4F38
LD L,A 6F
Copy Register A (the offset of byte +01H of the extension record) into Register L.
4F39
LD H,42H 26 42
Load Register H with 42H: Register Pair HL = byte +01H of the freed extension record in the sector buffer 4200H.
4F3B
LD A,(HL) 7E
Fetch byte +01H of the extension record at Register Pair HL into Register A: the directory entry code of the record that links to it.
4F3C
LD (4F4BH),A 32 4B 4F
Store Register A (that directory entry code) to 4F4BH, the operand of LD B,00H at 4F4AH.
4F3F
GOSUB to 502FH to read the hash index (sector 1 of the directory cylinder) of drive Register C into 4200H.
4F42
RET NZ C0
If the NZ FLAG is set (the sector could not be read), RETURN with error 16H, HIT READ ERROR, in Register A.
4F43
LD L,B 68
Copy Register B (the directory entry code of the freed record) into Register L: Register Pair HL = its byte in the hash index at 4200H.
4F44
LD (HL),00H 36 00
Store 00H to the hash index byte at Register Pair HL: the directory record is free for a new file.
4F46
GOSUB to 5041H to write the hash index from 4200H back to sector 1 of the directory cylinder of drive Register C and read it back.
4F49
RET NZ C0
If the NZ FLAG is set (the sector could not be written), RETURN with error 17H, HIT WRITE ERROR, in Register A.
4F4A
LD B,00H 06 00
Load Register B with the directory entry code of the record before the freed one; the operand 4F4BH is written at 4F3CH (00H in the file).
4F4C
GOSUB to 4B10H (SYS0/SYS) to read the directory sector of entry Register B on drive Register C into 4200H; Register Pair HL = that record.
4F4F
RET NZ C0
If the NZ FLAG is set (the sector could not be read), RETURN with error 11H, DIRECTORY READ ERROR, in Register A.
4F50
LD A,B 78
Copy Register B (the directory entry code of the record before) into Register A.
4F51
AND E0H E6 E0
Mask Register A to bits 7-5: the offset of the record in its sector.
4F53
ADD A,1FH C6 1F
Add 1FH to Register A: the offset of the record's byte +1FH.
4F55
LD L,A 6F
Copy Register A into Register L: Register Pair HL = byte +1FH of the record, the entry code of the link.
4F56
LD (HL),FFH 36 FF
Store FFH to byte +1FH of the record at Register Pair HL.
4F58
DEC L 2D
Decrement Register L: Register Pair HL = byte +1EH, the link byte FEH.
4F59
LD (HL),FFH 36 FF
Store FFH to byte +1EH at Register Pair HL: the link is gone and the extent list of the record ends there.
4F5B
PUSH HL E5
Save Register Pair HL (the address of record +1EH) to the stack.
4F5C
GOSUB to 4B1FH (SYS0/SYS) to write the directory sector in 4200H back to entry Register B of drive Register C and read it back.
4F5F
POP HL E1
Restore Register Pair HL from the stack: byte +1EH of the record. When granules are left, the next pass reads this byte (now FFH) as the second byte of an extent and +1DH as its cylinder, although the record's last extent is at +1CH-+1DH.
4F60
RET NZ C0
If the NZ FLAG is set (the sector could not be written), RETURN with the error in Register A (12H or 0FH).
4F61
DEC DE 1B
Decrement Register Pair DE: one granule fewer to give back.
4F62
LD A,D 7A
Copy Register D (the high byte of the count) into Register A.
4F63
OR E B3
OR Register E (the low byte of the count) into Register A to test Register Pair DE for 0000H.
4F64
If the NZ FLAG is set (granules are left to give back), JUMP back to 4EE7H with Register Pair HL at the extent to take from.
4F66
GOSUB to 4B1FH (SYS0/SYS) to write the directory sector in 4200H (the record holding the last extent changed) back to entry Register B of drive Register C and read it back.
4F69
If the Z FLAG is set (written), JUMP to 4F70H to write the allocation table.
4F6B
CP 0FH FE 0F
Compare Register A (the error) against 0FH, WRITE PROTECTED DISK.
4F6D
RET NZ C0
If the NZ FLAG is set (any other error), RETURN with error 12H, DIRECTORY WRITE ERROR, in Register A.
4F6E
JUMP to 4F74H: on a write protected diskette the allocation table is not written and the close goes on as if it were good.
4F70
GOSUB to 5016H to write the allocation table from 5100H back to sector 0 of the directory cylinder of drive Register C and read it back.
4F73
RET NZ C0
If the NZ FLAG is set (the sector could not be written), RETURN with error 15H, GAT WRITE ERROR, in Register A.

4F74H - Finish the Close

The control block becomes the text of the file's specification again: the name, a slash and the extension when there is one, a colon, the drive and 03H. The password is not put back. Its first byte is then a letter, so bit 7 (open) is clear. 4F74H-4F86H are the patch SYS3ABC: before it the code read the directory sector while Register B still held the entry code of the record already in 4200H (Register B and Register C were loaded from the control block only after the read), so the name could come from another record. Register B = the directory entry code of the record in 4200H.

4F74
LD A,B 78
Copy Register B (the directory entry code of the record now in 4200H) into Register A.
4F75
LD B,(IX+07H) DD 46 07
Fetch byte +07H of the control block at Index Register IX, the directory entry code of the file, into Register B.
4F78
LD C,(IX+06H) DD 4E 06
Fetch byte +06H of the control block at Index Register IX, the drive, into Register C.
4F7B
XOR B A8
Exclusive OR Register B (the file's entry code) into Register A (the entry code of the record in 4200H).
4F7C
AND 1FH E6 1F
Mask Register A to bits 4-0: 00H when both records are in the same directory sector.
4F7E
If the NZ FLAG is set (4200H holds another sector), GOSUB to 4B10H (SYS0/SYS) to read the directory sector of entry Register B on drive Register C into 4200H.
4F81
RET NZ C0
If the NZ FLAG is set (the sector could not be read), RETURN with error 11H, DIRECTORY READ ERROR, in Register A.
4F82
PUSH IX DD E5
Save Index Register IX (the control block) to the stack.
4F84
POP DE D1
Restore Register Pair DE from the stack: Register Pair DE = the control block, where the text is written.
4F85
NOP 00
No operation: one of the two bytes the patch SYS3ABC left over.
4F86
NOP 00
No operation: the second byte the patch SYS3ABC left over; fall into 4F87H.

4F87H - Write the Name of a File as Text

Register B = a directory entry code, Register C = the drive, Register Pair DE = where the text goes; the directory sector holding the record is in 4200H. The text is the name (spaces dropped), a slash and the extension when it is not blank, a colon, the drive digit and 03H. Returns Register A = 00H with the Z FLAG and Register Pair DE at the 03H.

4F87
LD A,C 79
Copy Register C (the drive of the file) into Register A for the drive digit.
4F88
AND 07H E6 07
Mask Register A to bits 2-0: the drive, 0-7. The operand 07H is the patch SYS3ABC (it was 03H, which gave drives 4-7 the digits 0-3).
4F8A
OR 30H F6 30
OR Register A with 30H: the ASCII digit of the drive.
4F8C
LD (4FC3H),A 32 C3 4F
Store Register A (the drive digit) to 4FC3H, the operand of LD A,00H at 4FC2H.
4F8F
LD H,42H 26 42
Load Register H with 42H, the page of the sector buffer 4200H.
4F91
LD A,B 78
Copy Register B (the directory entry code) into Register A.
4F92
AND E0H E6 E0
Mask Register A to bits 7-5: the offset of the record in its sector.
4F94
OR 05H F6 05
OR Register A with 05H: the offset of the record's name, bytes +05H-+0CH.
4F96
LD L,A 6F
Copy Register A into Register L: Register Pair HL = the name in the directory record.
4F97
PUSH HL E5
Save Register Pair HL (the address of the name) to the stack.
4F98
LD B,08H 06 08
Load Register B with 08H, the most characters of a name.
4F9A
LD A,(HL) 7E
Fetch a character of the name at Register Pair HL into Register A.
4F9B
CP 20H FE 20
Compare Register A (the character) against 20H, a space.
4F9D
If the Z FLAG is set (a space: the name has ended), JUMP to 4FA4H.
4F9F
LD (DE),A 12
Store Register A (the character) to the text at Register Pair DE.
4FA0
INC HL 23
Increment Register Pair HL to the next character of the name.
4FA1
INC DE 13
Increment Register Pair DE to the next place in the text.
4FA2
Decrement Register B and JUMP back to 4F9AH while characters of the name are left.
4FA4
POP HL E1
Restore Register Pair HL from the stack: the address of the name, byte +05H of the record.
4FA5
LD A,L 7D
Copy Register L (the low byte of the address of the name) into Register A.
4FA6
ADD A,08H C6 08
Add 08H to Register A: the offset of the extension, bytes +0DH-+0FH.
4FA8
LD L,A 6F
Copy Register A into Register L: Register Pair HL = the extension in the directory record.
4FA9
LD A,(HL) 7E
Fetch the extension's first character at Register Pair HL into Register A.
4FAA
CP 20H FE 20
Compare Register A (the first character of the extension) against 20H, a space.
4FAC
If the Z FLAG is set (no extension), JUMP to 4FBEH to put the colon and the drive.
4FAE
LD A,2FH 3E 2F
Load Register A with 2FH, the slash between the name and the extension.
4FB0
LD (DE),A 12
Store Register A (the slash) to the text at Register Pair DE.
4FB1
INC DE 13
Increment Register Pair DE to the next place in the text.
4FB2
LD B,03H 06 03
Load Register B with 03H, the most characters of an extension.
4FB4
LD A,(HL) 7E
Fetch a character of the extension at Register Pair HL into Register A.
4FB5
CP 20H FE 20
Compare Register A (the character) against 20H, a space.
4FB7
If the Z FLAG is set (a space: the extension has ended), JUMP to 4FBEH.
4FB9
LD (DE),A 12
Store Register A (the character) to the text at Register Pair DE.
4FBA
INC HL 23
Increment Register Pair HL to the next character of the extension.
4FBB
INC DE 13
Increment Register Pair DE to the next place in the text.
4FBC
Decrement Register B and JUMP back to 4FB4H while characters of the extension are left.
4FBE
LD A,3AH 3E 3A
Load Register A with 3AH, the colon before the drive.
4FC0
LD (DE),A 12
Store Register A (the colon) to the text at Register Pair DE.
4FC1
INC DE 13
Increment Register Pair DE to the next place in the text.
4FC2
LD A,00H 3E 00
Load Register A with the drive digit; the operand 4FC3H is written at 4F8CH (00H in the file).
4FC4
LD (DE),A 12
Store Register A (the drive digit) to the text at Register Pair DE.
4FC5
INC DE 13
Increment Register Pair DE to the next place in the text.
4FC6
LD A,03H 3E 03
Load Register A with 03H, the byte that ends a specification.
4FC8
LD (DE),A 12
Store Register A (03H) to the text at Register Pair DE.
4FC9
XOR A AF
Set Register A to 00H and the Z FLAG (XOR A): done.
4FCA
RET C9
RETURN with Register A = 00H and the Z FLAG; on the close path the RET goes to SYS0/SYS's 4A0CH, which gives the caller back Register Pairs BC, DE, HL and Index Register IX.

4FCBH - The Name of a File

Function 3, request B5H (44BBH). Register B = a directory entry code, Register C = the drive, Register Pair DE = a text area. The DEVICE command (SYS6/SYS, member 61H, its 538AH) uses it to show the file a device is routed to. The record is read and its name written as text at Register Pair DE (4F87H). Register Pairs DE and HL are given back; Register A = 00H with the Z FLAG, or the error with the NZ FLAG.

4FCB
PUSH DE D5
Save Register Pair DE (the text area) to the stack.
4FCC
PUSH HL E5
Save Register Pair HL (the caller's) to the stack.
4FCD
GOSUB to 4B10H (SYS0/SYS) to read the directory sector of entry Register B on drive Register C into 4200H.
4FD0
If the NZ FLAG is set (the sector could not be read: Register A = 11H), JUMP to 4FD5H.
4FD2
GOSUB to 4F87H to write the name of the record of entry Register B and drive Register C as text at Register Pair DE.
4FD5
POP HL E1
Restore Register Pair HL from the stack: the caller's.
4FD6
POP DE D1
Restore Register Pair DE from the stack: the start of the text.
4FD7
RET C9
RETURN to the caller with Register A = 00H and the Z FLAG, or error 11H and the NZ FLAG.

4FD8H - Close a Device

Reached from 4E10H for a control block whose first byte has bits 7 and 6 clear. Its bytes +06H-+07H (the device's name, stored by SYS2/SYS's open) go back to bytes +01H-+02H after an asterisk, and 03H ends the text. 4FE3H-4FE5H held CALL 0023H, which the patch SYS3ABC replaced with three NOPs; the patch also wrote 00H 2AH DDH to 4FE8H-4FEAH, the bytes the file already held there.

4FD8
GOSUB to 49E9H (SYS0/SYS) with Register Pair DE = the control block: the open test is passed over, Index Register IX = the block, and the caller's Register Pairs BC, DE, HL and Index Register IX are saved for 4A0CH.
4FDB
LD C,(IX+06H) DD 4E 06
Fetch byte +06H of the control block at Index Register IX, the first letter of the device's name, into Register C.
4FDE
LD B,(IX+07H) DD 46 07
Fetch byte +07H of the control block at Index Register IX, the second letter, into Register B.
4FE1
LD A,04H 3E 04
Load Register A with 04H, the value Register A held for the CALL 0023H the patch SYS3ABC removed; nothing reads Register A now before the XOR A at 4FF4H.
4FE3
NOP 00
No operation: the first of the three bytes of the patch SYS3ABC over CALL 0023H.
4FE4
NOP 00
No operation: the second of the three bytes of the patch SYS3ABC over CALL 0023H.
4FE5
NOP 00
No operation: the third of the three bytes of the patch SYS3ABC over CALL 0023H.
4FE6
LD (IX+00H),2AH DD 36 00 2A
Store 2AH, the asterisk, to byte +00H of the control block at Index Register IX: bit 7 clear, the block is closed.
4FEA
LD (IX+01H),C DD 71 01
Store Register C (the first letter of the device's name) to byte +01H of the control block at Index Register IX.
4FED
LD (IX+02H),B DD 70 02
Store Register B (the second letter of the device's name) to byte +02H of the control block at Index Register IX.
4FF0
LD (IX+03H),03H DD 36 03 03
Store 03H to byte +03H of the control block at Index Register IX: the end of the text: an asterisk and the two letters of the device.
4FF4
XOR A AF
Set Register A to 00H and the Z FLAG (XOR A): the device is closed.
4FF5
RET C9
RETURN through SYS0/SYS's 4A0CH, which gives the caller back Register Pairs BC, DE, HL and Index Register IX, with Register A = 00H and the Z FLAG.

4FF6H - Clear a Bit of Register B

Register A = the bit number (0-7), Register B = the byte. The instruction at 5000H is made into RES n,B for bit n: its second byte is 80H + n x 8.

4FF6
AND 07H E6 07
Mask Register A (the bit number) to bits 2-0, 0-7.
4FF8
RLCA 07
Rotate Register A (the bit number) left: the bit number times 2.
4FF9
RLCA 07
Rotate Register A left again: bit number times 4.
4FFA
RLCA 07
Rotate Register A left again: bit number times 8, bits 5-3 of the RES opcode.
4FFB
OR 80H F6 80
OR Register A with 80H: the second byte of RES n,B (CB 80 is RES 0,B).
4FFD
LD (5001H),A 32 01 50
Store Register A (the second byte of RES n,B) to 5001H, the second byte of the instruction at 5000H.
5000
RES 0,B CB 80
Clear bit n of Register B (the allocation byte); the second byte at 5001H is written at 4FFDH (80H in the file, RES 0,B).
5002
RET C9
RETURN with bit n of Register B (the allocation byte) cleared.

5003H - Read the Allocation Table

Sector 0 of the directory cylinder of drive Register C is read into 5100H-51FFH, which the file does not load. Returns the Z FLAG when read, or the NZ FLAG with Register A = 14H.

5003
PUSH DE D5
Save Register Pair DE (the caller's: the granules still to give back) to the stack.
5004
PUSH HL E5
Save Register Pair HL (the caller's: the address of the last extent) to the stack.
5005
GOSUB to 4B65H (SYS0/SYS) to fetch the directory cylinder of drive Register C into Register D.
5008
LD E,00H 1E 00
Load Register E with 00H: sector 0 of the directory cylinder, the granule allocation table.
500A
LD HL,5100H 21 00 51
Load Register Pair HL with 5100H, the buffer for the allocation table.
500D
GOSUB to 4B45H (SYS0/SYS) to read cylinder Register D, sector Register E of drive Register C into the buffer at Register Pair HL; the Z FLAG when the sector has the directory's data mark.
5010
POP HL E1
Restore Register Pair HL from the stack: the caller's.
5011
POP DE D1
Restore Register Pair DE from the stack: the caller's.
5012
RET Z C8
If the Z FLAG is set (read), RETURN with Register A = 00H and the Z FLAG.
5013
LD A,14H 3E 14
Load Register A with 14H, the error code GAT READ ERROR.
5015
RET C9
RETURN with error 14H in Register A and the NZ FLAG.

5016H - Write the Allocation Table

5100H-51FFH go back to sector 0 of the directory cylinder of drive Register C with the data mark FAH and are read back. Returns the Z FLAG when the read back gives 06H (the mark is FAH), or the NZ FLAG with Register A = 15H.

5016
PUSH DE D5
Save Register Pair DE (the caller's) to the stack.
5017
PUSH HL E5
Save Register Pair HL (the caller's) to the stack.
5018
GOSUB to 4B65H (SYS0/SYS) to fetch the directory cylinder of drive Register C into Register D.
501B
LD E,00H 1E 00
Load Register E with 00H: sector 0 of the directory cylinder, the granule allocation table.
501D
LD HL,5100H 21 00 51
Load Register Pair HL with 5100H, the allocation table changed at 4F0FH.
5020
GOSUB to 4768H (SYS0/SYS) to write the sector at Register D, Register E of drive Register C from Register Pair HL with the data mark FAH.
5023
If the NZ FLAG is set (the write failed), JUMP to 502AH to return error 15H.
5025
GOSUB to 4772H (SYS0/SYS) to read the sector at Register D, Register E of drive Register C back without storing it.
5028
CP 06H FE 06
Compare Register A against 06H: the read back of a sector with the data mark FAH ends with 06H, which means the write is good.
502A
LD A,15H 3E 15
Load Register A with 15H, the error code GAT WRITE ERROR. LD does not change the flags.
502C
POP HL E1
Restore Register Pair HL from the stack: the caller's.
502D
POP DE D1
Restore Register Pair DE from the stack: the caller's.
502E
RET C9
RETURN with the Z FLAG (written) or the NZ FLAG and error 15H in Register A.

502FH - Read the Hash Index

Sector 1 of the directory cylinder of drive Register C, the hash index (one byte for each directory record, 00H for a free one), is read into 4200H. Register Pairs BC and DE are kept. Returns the Z FLAG when read, or the NZ FLAG with Register A = 16H.

502F
PUSH BC C5
Save Register Pair BC (Register B the directory entry code, Register C the drive) to the stack.
5030
PUSH DE D5
Save Register Pair DE (the granules still to give back) to the stack.
5031
GOSUB to 4B65H (SYS0/SYS) to fetch the directory cylinder of drive Register C into Register D.
5034
LD E,01H 1E 01
Load Register E with 01H: sector 1 of the directory cylinder, the hash index.
5036
LD HL,4200H 21 00 42
Load Register Pair HL with 4200H, the sector buffer of SYS0/SYS.
5039
GOSUB to 4B45H (SYS0/SYS) to read cylinder Register D, sector Register E of drive Register C into the buffer at Register Pair HL; the Z FLAG when read.
503C
POP DE D1
Restore Register Pair DE from the stack: the granules still to give back.
503D
POP BC C1
Restore Register Pair BC from the stack: Register B the directory entry code, Register C the drive.
503E
LD A,16H 3E 16
Load Register A with 16H, the error code HIT READ ERROR. LD does not change the flags.
5040
RET C9
RETURN with the Z FLAG (read) or the NZ FLAG and error 16H in Register A.

5041H - Write the Hash Index

4200H goes back to sector 1 of the directory cylinder of drive Register C with the data mark FAH and is read back. Register Pairs BC and DE are kept. Returns the Z FLAG when good, or the NZ FLAG with Register A = 17H.

5041
PUSH BC C5
Save Register Pair BC (Register B the directory entry code, Register C the drive) to the stack.
5042
PUSH DE D5
Save Register Pair DE (the granules still to give back) to the stack.
5043
GOSUB to 4B65H (SYS0/SYS) to fetch the directory cylinder of drive Register C into Register D.
5046
LD E,01H 1E 01
Load Register E with 01H: sector 1 of the directory cylinder, the hash index.
5048
LD HL,4200H 21 00 42
Load Register Pair HL with 4200H, the hash index changed at 4F44H.
504B
GOSUB to 4768H (SYS0/SYS) to write the sector at Register D, Register E of drive Register C from Register Pair HL with the data mark FAH.
504E
If the NZ FLAG is set (the write failed), JUMP to 5055H to return error 17H.
5050
GOSUB to 4772H (SYS0/SYS) to read the sector at Register D, Register E of drive Register C back without storing it.
5053
CP 06H FE 06
Compare Register A against 06H: the read back of a sector with the data mark FAH ends with 06H, which means the write is good.
5055
LD A,17H 3E 17
Load Register A with 17H, the error code HIT WRITE ERROR. LD does not change the flags.
5057
POP DE D1
Restore Register Pair DE from the stack: the granules still to give back.
5058
POP BC C1
Restore Register Pair BC from the stack: Register B the directory entry code, Register C the drive.
5059
RET C9
RETURN with the Z FLAG (written) or the NZ FLAG and error 17H in Register A.

505AH - Add Register A to Register Pair HL

Part of the patch SYS3ABC (loaded at 505AH-5061H, past the end of the original file), called from 4ECDH. Register A is kept.

505A
PUSH AF F5
Save Register Pair AF (Register A the value to add) to the stack.
505B
ADD A,L 85
Add Register L (the low byte of Register Pair HL) to Register A.
505C
LD L,A 6F
Copy Register A into Register L: the low byte of the sum.
505D
If the NC FLAG is set (no carry), JUMP to 5060H.
505F
INC H 24
Increment Register H: the carry into the high byte of Register Pair HL.
5060
POP AF F1
Restore Register Pair AF from the stack: Register A is the value added again.
5061
RET C9
RETURN with Register Pair HL = Register Pair HL plus Register A.

5062H - Memory Outside the File

Not loaded by SYS3/SYS. 5100H-51FFH is the buffer of the granule allocation table (5003H, 5016H).

5062-50FF
DEFS 158
Not in the file, and not used by SYS3/SYS: the bytes between the end of the patch at 5061H and the allocation table buffer at 5100H.
5100-51FF
DEFS 256
Not in the file: the granule allocation table of the drive (sector 0 of the directory cylinder), read at 5003H and written at 5016H. Byte 5100H plus a cylinder is that cylinder's allocation byte (bit n = granule n in use), cleared bit by bit at 4F0FH.