LDOS 5.3.1 for the Model III - SYS10/SYS Disassembled

Page Customization

Introduction/Summary

LDOS 5.3.1 SYS10/SYS Disassembly - @KILL and the Module Search (Model III)

SYS10/SYS carries two services. The supervisor dispatcher loads it into the overlay area at 4E00H and calls it with the request code in Register A, whose bits 6-4 choose the service (4E00H): request 9CH, issued by @KILL (442CH), deletes the file whose control block is at Register Pair DE, and request BCH finds a module kept in memory above HIGH$ by its name (4F28H). The manual says SYS10/SYS holds the procedures to delete a file and should stay on working system diskettes.

The file is a load module of 440 bytes: a record of type 1FH with the text Copyright 1991 MISOSYS, Inc., All rights reserved, two load blocks for 4E00H-4F72H, a load block for the two bytes 4BC8H-4BC9H (0173H, the 371 bytes that SYS10/SYS takes up in the overlay area) and the transfer address record for 4E00H.

How a File Is Deleted

  • A control block with bits 7-5 of +00H clear is a device: it is turned back into its specification, an asterisk, the two letters of its name (from +06H-+07H) and 03H (4E72H), and nothing more is done.
  • A disk file must be open (the file prologue at 49F3H gives error 26H otherwise) with the access level 0 or 1 in bits 2-0 of +01H; any other level gives error 25H, illegal access attempted to protected file.
  • The granule allocation table (sector 0 of the directory cylinder) is read into 5000H-50FFH. For the file's directory record (+07H of the control block) and then each extension record, linked by FEH and a record number at +1EH-+1FH: every extent's granules have their bits cleared in the table (4E8BH), bit 4 of the record's attributes (in use) is cleared and its sector written, and its byte in the hash index table (sector 1, read into 4300H) is set to 00H and the table written.
  • Last the allocation table is written back, so the granules are free on the disk only when every record is done, and the 32 bytes of the control block are cleared. @KILL returns Register A = 00H with the Z FLAG, or an error number with the NZ FLAG: 11H, 12H or 0FH (the directory sector), 14H and 15H (the allocation table), 16H and 17H (the hash index table).

An extent is a cylinder byte and a granule byte (bits 7-5 its first granule on the cylinder, bits 4-0 its granules less one); its granules run on into the next cylinder past the last granule of a cylinder, which is bits 7-5 of byte +08H of the drive code table plus one, doubled when bit 5 of byte +04H says the drive has two sides.

The Module Search

Programs that keep themselves in memory above HIGH$ (4411H) begin with a header, and each new one goes below the one before:

OffsetContents
+00H-+01HA JR over the header; the search takes the byte 18H as the mark of a module.
+02H-+03HThe link word: the HIGH$ before the module was put in memory. The link + 1 is the start of the module before it; FFFFH ends the chain.
+04HBits 3-0: the length of the name.
+05HThe name, followed by the module's own data, where Register Pair DE points when the module is found.

The name looked for is at Register Pair DE, ended by any character below 21H. The search starts at HIGH$ less the top of memory found by the reset (4415H-4416H) (4F30H), which is HIGH$ + 1 when the top is FFFFH, a 48K computer. With the top at 7FFFH or BFFFH that subtraction gives another address, so on a 16K or 32K computer the search does not start at the newest module. A name that is not found returns error 08H, device not available.

About This Page

The Model III file is the Model I file of the same release with the same code at the same addresses; its 10 different bytes are operands that name the Model III resident area: the page of the system sector buffer 43H (4E51H, and 4300H at 4EFDH and 4F16H; Model I 42H), DIRCYL 4B64H (4ED9H, 4F02H, 4F11H; the Model I routines are one byte higher), HIGH$ 4411H (4F29H) and the top of memory 4415H (4F2CH), and the overlay length block at 4BC8H-4BC9H (Model I 4BC9H-4BCAH).

Every byte of the file is shown, in address order: the load block at 4BC8H first, then 4E00H-4F72H. One instruction is entered in its middle: OR AFH at 4ED4H, whose second byte is XOR A for the call from 4E63H. SYS10/SYS changes three bytes of its own while it runs, all operands (the table of Variables).

Memory Map

Load Blocks in File Order

The three load blocks of the file in the order the overlay loader (4C8CH) reads them, after the copyright record. The transfer address is 4E00H.

AddressContents
4E00H-4EFFH256 bytes
4F00H-4F72H115 bytes
4BC8H-4BC9H2 bytes

Sections of the Disassembly

AddressContents
4BC8HThe Overlay Length (Data)
4E00HThe Request Codes, and @KILL: Delete a File
4E29HFree the Directory Records of the File
4E72HClose a Device
4E8BHFree the Granules of One Extent
4EC7HClear a Granule's Bit
4ED4HRead or Write the Granule Allocation Table
4EF8HRead and Write the Hash Index Table
4F28HFind a Module above HIGH$

Memory SYS10/SYS Uses

AddressContents
4411H-4412HHIGH$: the newest module is above it (4F29H).
4300H-43FFHThe system sector buffer: the directory sector that DIRRD reads (4E29H), then the hash index table (4E4DH).
4415H-4416HThe top of memory found by the reset (4F2CH).
442CH@KILL, which issues request 9CH.
479CHDCTBYT: bytes +04H and +08H of the drive code table (4E8FH, 4E9BH).
4768H, 4772HWRPROT and VERSEC: write a system sector with the deleted data mark and verify it.
49F1H, 49F3HThe file prologue of SYS0/SYS (4E72H, 4E0FH).
4B10H, 4B1FHDIRRD and DIRWR (4E29H, 4E49H).
4B45H, 4B64HRDSSEC and DIRCYL: read a sector of the directory cylinder and find that cylinder.
4BC8H-4BC9HThe length of the system file in the overlay area: 0173H for SYS10/SYS.
4E00H-4F72HSYS10/SYS in the overlay area.
5000H-50FFHThe granule allocation table of the file's drive, read and written by SYS10/SYS (4ED4H).

Variables

The entry points of SYS10/SYS, then the data of the file, the operands it changes while it runs, and the cells outside the file that it uses, by address.

Entry Points

AddressNameContents
4E00H@KILLRequest 9CH (function 10H): delete the file or close the device at Register Pair DE; request BCH (function 30H) goes to 4F28H.
4E29HFree a directory record of the file, then its extension records; write the table; clear the control block.
4E72HClose a device: its control block becomes its specification again.
4E8BHFree the granules of the extent in Register Pair DE.
4EC7HClear bit Register A of Register B.
4ED4HRead the granule allocation table into 5000H; 4ED5H write it.
4EF8HRead the hash index table into 5000H (not called); 4EFDH into 4300H; 4F0FH write it from 4300H.
4F28HRequest BCH: find the module above HIGH$ named at Register Pair DE.

Data in the File

Address
Size
Contents
4BC8H-4BC9H
2 bytes
DEFW 0173H
4BC8H-4BC9H: 0173H (371), the number of bytes from 4E00H that SYS10/SYS takes up (4E00H-4F72H). Loading SYS10/SYS puts it over the length the system file loaded before it left here.

Operands Changed While It Runs

Address
Size
Contents
4E5BH-4E5CH
2 bytes
The operand of LD DE,0000H at 4E5AH: the last extent pair of the record read (Register E the cylinder byte, Register D the granule byte), stored by 4E34H; after the extents it is FEH and the extension record's number, or FFH.
4EBCH
1 byte
The operand of CP 00H at 4EBBH: the granules of a cylinder, stored by 4EA4H.
4ED2H
1 byte
The second byte of RES 0,B at 4ED1H (80H in the file): 80H + 8n, stored by 4ECEH, makes it RES n,B.

Cells Outside the File That SYS10/SYS Uses

Address
Size
Contents
DE +00H
1 byte
The control block: bit 7 an open file; bits 7-5 clear for a device (4E0AH). A device gets 2AH (4E7BH).
IX +01H
1 byte
Bits 2-0: the access the file was opened with, 0 or 1 to delete (4E12H). A device gets the first letter of its name.
IX +02H-+03H
2 bytes
A device gets the second letter of its name and 03H (4E82H, 4E85H).
IX +06H-+07H
2 bytes
The file's drive and directory record (4E1FH, 4E22H), or a device's two letter name (4E75H, 4E78H).
4411H-4412H
2 bytes
HIGH$: the newest module above it starts at HIGH$ + 1.
4300H-43FFH
256 bytes
A directory sector, then the hash index table: one byte per record, set to 00H when the record is freed.
4415H-4416H
2 bytes
The top of memory found by the reset: 7FFFH, BFFFH or FFFFH.
5000H-50FFH
256 bytes
The granule allocation table: byte 5000H + c for cylinder c, bit n set while granule n is in use.

Disassembly

4BC8H - The Overlay Length (Data)

Every system file from SYS1/SYS to SYS12/SYS carries a load block for the two bytes 4BC8H-4BC9H in the resident area of SYS0/SYS. They hold the length of the file's part in the overlay area: for SYS10/SYS 0173H, the 371 bytes 4E00H-4F72H. In the file this block is the last load block, after the two blocks of 4E00H-4F72H and before the transfer address record (4E00H). The two bytes are data, not instructions.

4BC8-4BC9
DEFW 0173H 73 01
4BC8H-4BC9H: 0173H (371), the number of bytes from 4E00H that SYS10/SYS takes up (4E00H-4F72H). Loading SYS10/SYS puts it over the length the system file loaded before it left here.

4E00H - The Request Codes, and @KILL: Delete a File

The supervisor dispatcher loads SYS10/SYS into 4E00H and calls it with the request code in Register A. Bits 6-4 select the function: 30H (request BCH) is the search for a module above HIGH$ (4F28H); 10H (request 9CH, issued by @KILL at 442CH) deletes the file whose control block is at Register Pair DE; any other function only loads SYS10/SYS and returns with the NZ FLAG. A control block whose bits 7-5 of +00H are all clear is a device: it is closed (4E72H) and no disk is touched. For an open file the access granted (bits 2-0 of +01H) must be 0 or 1, else error 25H; then the granule allocation table is read into 5000H and each directory record of the file is freed (4E29H).

4E00
AND 70H E6 70
Keep bits 6-4 of Register A, the request code (AND 70H): the function, 10H for request 9CH (@KILL), 30H for request BCH. AND clears the CARRY FLAG.
4E02
CP 30H FE 30
Compare Register A (the function) against 30H, the search for a module.
4E04
If the Z FLAG is set (request BCH), JUMP to 4F28H to search the modules above HIGH$.
4E07
CP 10H FE 10
Compare Register A (the function) against 10H, @KILL.
4E09
RET NZ C0
RETurn with the NZ FLAG for any other function: the request only loads SYS10/SYS.
4E0A
LD A,(DE) 1A
Fetch byte +00H of the control block at Register Pair DE into Register A: bit 7 an open file, bit 4 a device.
4E0B
AND E0H E6 E0
Keep bits 7-5 of Register A (AND E0H): 00H with the Z FLAG for a device.
4E0D
If the Z FLAG is set (a device), JUMP to 4E72H to close it.
4E0F
GOSUB to 49F3H, the file prologue of SYS0/SYS: error 26H (file not open) unless bit 7 of +00H is set; else Index Register IX = the control block, the caller's registers are saved and this routine's RET goes through 4A14H, which puts them back.
4E12
LD A,(IX+01H) DD 7E 01
Fetch byte +01H of the control block at Index Register IX into Register A: bits 2-0 the access the file was opened with.
4E15
AND 07H E6 07
Keep bits 2-0 of Register A (AND 07H): the access granted, 0 the fullest.
4E17
CP 02H FE 02
Compare Register A (the access granted) against 02H: the CARRY FLAG for 0 and 1, the two levels that allow a file to be deleted.
4E19
If the CARRY FLAG is set (deleting is allowed), JUMP to 4E1FH.
4E1B
LD A,25H 3E 25
Load Register A with 25H, error 37: illegal access attempted to protected file.
4E1D
OR A B7
Set the flags from Register A (OR A): the NZ FLAG, Register A not being 00H.
4E1E
RET C9
RETurn with the NZ FLAG and Register A = 25H: the file is not deleted.
4E1F
LD C,(IX+06H) DD 4E 06
Fetch the file's drive from Index Register IX +06H into Register C.
4E22
LD B,(IX+07H) DD 46 07
Fetch the number of the file's directory record from Index Register IX +07H into Register B.
4E25
GOSUB to 4ED4H to read the granule allocation table of drive Register C into 5000H-50FFH: the NZ FLAG with Register A = 14H on failure.
4E28
RET NZ C0
RETurn if the NZ FLAG is set (the table could not be read) with Register A = 14H, error 20: GAT read error.

4E29H - Free the Directory Records of the File

For each directory record of the file, the primary record and then each extension record: the granules of its extents are freed in the table at 5000H (4E8BH); bit 4 of its attributes (the record in use) is cleared and the directory sector written back; its byte in the hash index table is set to 00H and the table written back. The cylinder byte that ends the record's extents (FEH or FFH) and the byte after it are kept in the operand at 4E5BH: FEH means the file goes on in the extension record whose number is that byte after it. After the last record the allocation table is written back and the 32 bytes of the control block are cleared.

4E29
GOSUB to 4B10H (DIRRD) to read the directory sector of record Register B of drive Register C into 4300H: Register Pair HL = the record, or the NZ FLAG with Register A = 11H. 4E61H comes back here for an extension record.
4E2C
RET NZ C0
RETurn if the NZ FLAG is set (the sector could not be read) with Register A = 11H, error 17: directory read error.
4E2D
LD A,16H 3E 16
Load Register A with 16H, the offset of the first extent in a directory record.
4E2F
ADD A,L 85
ADD Register L (the low byte of the record's address) to Register A.
4E30
LD L,A 6F
Copy Register A into Register L: Register Pair HL = the record +16H, the first extent's cylinder byte (the record does not cross a page).
4E31
LD E,(HL) 5E
Fetch the extent's cylinder byte at Register Pair HL into Register E. 4E41H loops back here for the next extent.
4E32
INC L 2C
INCrement Register L to the extent's granule byte, the byte after its cylinder byte.
4E33
LD D,(HL) 56
Fetch the extent's granule byte at Register Pair HL into Register D: bits 7-5 its first granule on the cylinder, bits 4-0 its granules less one.
4E34
LD (4E5BH),DE ED 53 5B 4E
Store Register Pair DE (Register E the cylinder byte, Register D the granule byte) at 4E5BH-4E5CH, the operand of the LD DE at 4E5AH: the last pair stored is the one that ends the extents.
4E38
LD A,E 7B
Copy Register E (the cylinder byte) into Register A.
4E39
CP FEH FE FE
Compare Register A against FEH: FEH is the link to an extension record, FFH an extent not in use; both leave the NO CARRY FLAG.
4E3B
If the NO CARRY FLAG is set (FEH or FFH), JUMP to 4E43H: the record's extents end here.
4E3D
INC L 2C
INCrement Register L to the next extent's cylinder byte.
4E3E
GOSUB to 4E8BH to free the granules of the extent in Register Pair DE in the allocation table at 5000H.
4E41
LOOP BACK to 4E31H for the next extent of the record.
4E43
LD A,L 7D
Copy Register L (the low byte of the address in the record) into Register A.
4E44
AND E0H E6 E0
Keep bits 7-5 of Register A (AND E0H): the start of the 32 byte record.
4E46
LD L,A 6F
Copy Register A into Register L: Register Pair HL = byte +00H of the record, its attributes.
4E47
RES 4,(HL) CB A6
Clear bit 4 of the attributes at Register Pair HL: the record is no longer in use.
4E49
GOSUB to 4B1FH (DIRWR) to write the directory sector at 4300H back to drive Register C and verify it: the NZ FLAG with the error number on failure.
4E4C
RET NZ C0
RETurn if the NZ FLAG is set (the sector could not be written) with the error number in Register A (12H, or 0FH write protected).
4E4D
GOSUB to 4EFDH to read the hash index table of drive Register C into 4300H: the NZ FLAG with Register A = 16H on failure.
4E50
RET NZ C0
RETurn if the NZ FLAG is set (the table could not be read) with Register A = 16H, error 22: HIT read error.
4E51
LD H,43H 26 43
Load Register H with 43H, the page of the hash index table at 4300H.
4E53
LD L,B 68
Copy Register B (the record's number) into Register L: Register Pair HL = the record's byte in the hash index table.
4E54
LD (HL),00H 36 00
Store 00H at Register Pair HL: the record is free in the hash index table.
4E56
GOSUB to 4F0FH to write the hash index table at 4300H back to drive Register C and verify it: the NZ FLAG with Register A = 17H on failure.
4E59
RET NZ C0
RETurn if the NZ FLAG is set (the table could not be written) with Register A = 17H, error 23: HIT write error.
4E5A
LD DE,0000H 11 00 00
Load Register Pair DE with the pair stored at 4E5BH by 4E34H (0000H in the file): Register E = the byte that ended the extents, Register D = the byte after it.
4E5D
LD B,D 42
Copy Register D (the byte after the FEH: the number of the extension record) into Register B, the record for DIRRD.
4E5E
LD A,E 7B
Copy Register E (FEH or FFH) into Register A.
4E5F
CP FEH FE FE
Compare Register A against FEH, the link to an extension record.
4E61
If the Z FLAG is set (FEH: the file goes on), JUMP to 4E29H to free the extension record Register B.
4E63
GOSUB to 4ED5H to write the allocation table at 5000H back to drive Register C and verify it: the granules are now free on the disk; the NZ FLAG with Register A = 15H on failure.
4E66
RET NZ C0
RETurn if the NZ FLAG is set (the table could not be written) with Register A = 15H, error 21: GAT write error.
4E67
PUSH IX DD E5
Save Index Register IX (the control block) to the stack.
4E69
POP HL E1
Restore Register Pair HL from the stack: Register Pair HL = the control block.
4E6A
LD B,20H 06 20
Load Register B with 20H, the 32 bytes of the control block.
4E6C
XOR A AF
Set Register A to 00H (XOR A), the value to store, with the Z FLAG set: the result of @KILL.
4E6D
LD (HL),A 77
Store Register A (00H) at Register Pair HL, the next byte of the control block. 4E6FH loops back here.
4E6E
INC HL 23
INCrement Register Pair HL to the next byte of the control block.
4E6F
DECrement Register B and LOOP BACK to 4E6DH until the 32 bytes are 00H.
4E71
RET C9
RETurn through 4A14H, which puts the caller's registers back, with the Z FLAG and Register A = 00H: the file is deleted.

4E72H - Close a Device

The control block at Register Pair DE is a device: +06H-+07H hold its two letter name. It is turned back into the device specification it was opened from: an asterisk (2AH), the two letters and the end byte 03H at +00H-+03H. No disk is touched.

4E72
GOSUB to 49F1H, the file prologue of SYS0/SYS without the open test: Index Register IX = the control block, the caller's registers saved and put back by 4A14H when this routine returns.
4E75
LD C,(IX+06H) DD 4E 06
Fetch the first letter of the device's name from Index Register IX +06H into Register C.
4E78
LD B,(IX+07H) DD 46 07
Fetch the second letter of the device's name from Index Register IX +07H into Register B.
4E7B
LD (IX+00H),2AH DD 36 00 2A
Store 2AH, an asterisk, at Index Register IX +00H: the control block is a device specification again.
4E7F
LD (IX+01H),C DD 71 01
Store Register C (the first letter) at Index Register IX +01H.
4E82
LD (IX+02H),B DD 70 02
Store Register B (the second letter) at Index Register IX +02H.
4E85
LD (IX+03H),03H DD 36 03 03
Store 03H, the byte that ends a specification, at Index Register IX +03H.
4E89
XOR A AF
Set Register A to 00H (XOR A) with the Z FLAG: no error.
4E8A
RET C9
RETurn through 4A14H with the Z FLAG and Register A = 00H.

4E8BH - Free the Granules of One Extent

Register E = the extent's cylinder, Register D = its granule byte (bits 7-5 the first granule on the cylinder, bits 4-0 the granules less one), Register C = the drive, and the allocation table in 5000H-50FFH (byte 5000H + c for cylinder c, bit n set while granule n is in use). The granules of a cylinder are bits 7-5 of byte +08H of the drive code table plus one, doubled when bit 5 of byte +04H says the drive has two sides; that count is stored at 4EBCH. Starting at the extent's first granule, each of its granules has its bit cleared; past the cylinder's last granule the count goes on at granule 0 of the next cylinder. Register Pairs HL and BC are kept.

4E8B
PUSH HL E5
Save Register Pair HL (the address in the directory record) to the stack.
4E8C
PUSH BC C5
Save Register Pair BC (Register B = the record, Register C = the drive) to the stack.
4E8D
LD A,08H 3E 08
Load Register A with 08H, the offset of the granule layout in a drive code table.
4E8F
GOSUB to 479CH (DCTBYT) to fetch byte +08H of drive Register C's code table into Register A: bits 7-5 the granules on a track less one, bits 4-0 the sectors of a granule less one.
4E92
RLCA 07
Rotate Register A left one bit (RLCA): first of three rotations that bring bits 7-5 to bits 2-0.
4E93
RLCA 07
Rotate Register A left one bit (RLCA): second of the three rotations.
4E94
RLCA 07
Rotate Register A left one bit (RLCA): bits 2-0 now hold the granules on a track less one.
4E95
AND 07H E6 07
Keep bits 2-0 of Register A (AND 07H): the granules on a track less one.
4E97
INC A 3C
INCrement Register A: the granules on a track (one side of a cylinder).
4E98
LD L,A 6F
Copy Register A (the granules on one side) into Register L to keep it.
4E99
LD A,04H 3E 04
Load Register A with 04H, the offset of the second drive specification byte in a drive code table.
4E9B
GOSUB to 479CH (DCTBYT) to fetch byte +04H of drive Register C's code table into Register A.
4E9E
BIT 5,A CB 6F
Test bit 5 of Register A, set when the drive has two sides: the Z FLAG for one side.
4EA0
LD A,L 7D
Copy Register L (the granules on one side) back into Register A. LD does not change the flags.
4EA1
If the Z FLAG is set (one side), JUMP to 4EA4H.
4EA3
ADD A,A 87
ADD Register A to itself: the granules on both sides of a cylinder.
4EA4
LD (4EBCH),A 32 BC 4E
Store Register A (the granules of a cylinder) at 4EBCH, the operand of the CP at 4EBBH, where the granule count wraps to the next cylinder.
4EA7
LD L,E 6B
Copy Register E (the extent's cylinder) into Register L.
4EA8
LD H,50H 26 50
Load Register H with 50H: Register Pair HL = 5000H + the cylinder, its byte in the allocation table.
4EAA
LD A,D 7A
Copy Register D (the extent's granule byte) into Register A.
4EAB
AND 1FH E6 1F
Keep bits 4-0 of Register A (AND 1FH): the extent's granules less one.
4EAD
LD C,A 4F
Copy Register A (the granules less one) into Register C.
4EAE
INC C 0C
INCrement Register C: the number of granules to free, 1-32.
4EAF
XOR D AA
Exclusive OR Register D (the granule byte) into Register A (its bits 4-0): Register A = bits 7-5 of the granule byte alone, the first granule.
4EB0
RLCA 07
Rotate Register A left one bit (RLCA): first of three rotations that bring bits 7-5 to bits 2-0.
4EB1
RLCA 07
Rotate Register A left one bit (RLCA): second of the three rotations.
4EB2
RLCA 07
Rotate Register A left one bit (RLCA): Register A = the extent's first granule on its cylinder, 0-7.
4EB3
PUSH AF F5
Save Register Pair AF (Register A = the granule to free) to the stack. 4EC2H loops back here.
4EB4
LD B,(HL) 46
Fetch the cylinder's allocation table byte at Register Pair HL into Register B.
4EB5
GOSUB to 4EC7H to clear bit Register A (the granule) of Register B.
4EB8
LD (HL),B 70
Store Register B (the byte with the granule's bit cleared) at Register Pair HL: the granule is free.
4EB9
POP AF F1
Restore Register Pair AF (Register A = the granule just freed) from the stack.
4EBA
INC A 3C
INCrement Register A (the granule just freed) to the next granule of the extent.
4EBB
CP 00H FE 00
Compare Register A against the granules of a cylinder, the operand at 4EBCH (00H in the file) stored by 4EA4H: the Z FLAG past the cylinder's last granule.
4EBD
If the NZ FLAG is set (the granule is on this cylinder), JUMP to 4EC1H.
4EBF
XOR A AF
Set Register A to 00H (XOR A): granule 0 of the next cylinder.
4EC0
INC L 2C
INCrement Register L to the next cylinder's byte in the allocation table.
4EC1
DEC C 0D
DECrement Register C, the granules still to free.
4EC2
If the NZ FLAG is set (granules left), LOOP BACK to 4EB3H to free the next one.
4EC4
POP BC C1
Restore Register Pair BC (Register B = the record, Register C = the drive) from the stack.
4EC5
POP HL E1
Restore Register Pair HL (the address in the directory record) from the stack.
4EC6
RET C9
RETurn to 4E41H with the extent's granules free in the table at 5000H.

4EC7H - Clear a Granule's Bit

Register A = the granule n (bits 2-0), Register B = an allocation table byte. Builds RES n,B at 4ED1H by storing 80H + 8n at its second byte 4ED2H and runs it: Register B comes back with bit n cleared.

4EC7
AND 07H E6 07
Keep bits 2-0 of Register A (AND 07H): the number n of the bit to clear.
4EC9
RLCA 07
Rotate Register A left one bit (RLCA): first of three rotations that make n into 8 x n.
4ECA
RLCA 07
Rotate Register A left one bit (RLCA): second of the three rotations.
4ECB
RLCA 07
Rotate Register A left one bit (RLCA): Register A = 8 x n, the bit field of a CB instruction.
4ECC
OR 80H F6 80
OR 80H into Register A: 80H + 8n, the second byte of RES n,B.
4ECE
LD (4ED2H),A 32 D2 4E
Store Register A (80H + 8n) at 4ED2H, the second byte of the RES 0,B at 4ED1H: it becomes RES n,B.
4ED1
RES 0,B CB 80
Clear bit n of Register B (the allocation table byte), the bit stored at 4ED2H (80H, bit 0, in the file).
4ED3
RET C9
RETurn to 4EB8H with Register B = the byte with the granule's bit cleared.

4ED4H - Read or Write the Granule Allocation Table

Two entries one byte apart. 4ED4H is OR AFH, which leaves Register A non zero and so the NZ FLAG: read the table. Entered at 4ED5H, the byte AFH alone is XOR A, which sets the Z FLAG: write it. The flags are kept on the stack across DIRCYL and choose the work. The table is sector 0 of the directory cylinder of drive Register C, held at 5000H-50FFH: read with RDSSEC (error 14H, GAT read error), or written with the deleted data mark and verified (error 15H, GAT write error). Each returns the Z FLAG on success. Register Pairs DE and HL are kept.

4ED4
OR AFH F6 AF
Read entry (from 4E25H): OR AFH into Register A, which is then not 00H: the NZ FLAG, which selects the read. Entered at 4ED5H (from 4E63H), the byte AFH is XOR A: Register A = 00H and the Z FLAG, which selects the write.
4ED6
PUSH DE D5
Save Register Pair DE to the stack while Register Pair DE names the sector.
4ED7
PUSH HL E5
Save Register Pair HL to the stack while Register Pair HL points at the buffer.
4ED8
PUSH AF F5
Save Register Pair AF to the stack: the Z FLAG (write) or the NZ FLAG (read), which DIRCYL would change.
4ED9
GOSUB to 4B64H (DIRCYL) to load Register D with the directory cylinder of drive Register C (byte +09H of its code table).
4EDC
LD E,00H 1E 00
Load Register E with 00H, sector 0 of the directory cylinder: the granule allocation table.
4EDE
LD HL,5000H 21 00 50
Load Register Pair HL with 5000H, the buffer for the allocation table, after SYS10/SYS's code.
4EE1
POP AF F1
Restore Register Pair AF from the stack: the Z FLAG for a write, the NZ FLAG for a read.
4EE2
If the Z FLAG is set (a write, entered at 4ED5H), JUMP to 4EEBH to write the table.
4EE4
GOSUB to 4B45H (RDSSEC) to read cylinder Register D, sector Register E of drive Register C into 5000H: the Z FLAG when it was read and carries the deleted data mark.
4EE7
LD A,14H 3E 14
Load Register A with 14H, error 20: GAT read error. LD does not change the flags.
4EE9
JUMP to 4EF5H to return with the flags of the read.
4EEB
GOSUB to 4768H (WRPROT) to write the buffer at 5000H to cylinder Register D, sector Register E of drive Register C with the deleted data mark: the Z FLAG on success.
4EEE
If the Z FLAG is set (written), GOSUB to 4772H (VERSEC) to read the sector back without storing it: Register A = 06H for a good sector with the deleted data mark.
4EF1
CP 06H FE 06
Compare Register A (the verify result, or the write's error number) against 06H: the Z FLAG when the sector was written well.
4EF3
LD A,15H 3E 15
Load Register A with 15H, error 21: GAT write error. LD does not change the flags.
4EF5
POP HL E1
Restore Register Pair HL from the stack. 4EE9H joins here after the read.
4EF6
POP DE D1
Restore Register Pair DE from the stack. POP does not change the flags.
4EF7
RET C9
RETurn with the Z FLAG on success, or the NZ FLAG and Register A = 14H (read) or 15H (write).

4EF8H - Read and Write the Hash Index Table

The hash index table is sector 1 of the directory cylinder of drive Register C. 4EFDH reads it into 4300H (error 16H, HIT read error); 4EF8H would read it into 5000H instead, but nothing calls it. 4F0FH writes it back from 4300H with the deleted data mark and verifies it (error 17H, HIT write error). Each returns the Z FLAG on success and keeps Register Pairs BC and DE.

4EF8
LD HL,5000H 21 00 50
Load Register Pair HL with 5000H, a buffer for the table. No code of SYS10/SYS calls this entry.
4EFB
JUMP to 4F00H to read the table into the buffer at Register Pair HL.
4EFD
LD HL,4300H 21 00 43
Load Register Pair HL with 4300H, the system sector buffer, for the hash index table (from 4E4DH).
4F00
PUSH BC C5
Save Register Pair BC (Register B = the record, Register C = the drive) to the stack.
4F01
PUSH DE D5
Save Register Pair DE to the stack while Register Pair DE names the sector.
4F02
GOSUB to 4B64H (DIRCYL) to load Register D with the directory cylinder of drive Register C.
4F05
LD E,01H 1E 01
Load Register E with 01H, sector 1 of the directory cylinder: the hash index table.
4F07
GOSUB to 4B45H (RDSSEC) to read cylinder Register D, sector Register E of drive Register C into the buffer at Register Pair HL: the Z FLAG when it was read and carries the deleted data mark.
4F0A
POP DE D1
Restore the caller's Register Pair DE from the stack.
4F0B
POP BC C1
Restore Register Pair BC (Register B = the record, Register C = the drive) from the stack. POP does not change the flags.
4F0C
LD A,16H 3E 16
Load Register A with 16H, error 22: HIT read error. LD does not change the flags.
4F0E
RET C9
RETurn with the Z FLAG on success, or the NZ FLAG and Register A = 16H.
4F0F
PUSH BC C5
Save Register Pair BC (Register B = the record, Register C = the drive) to the stack.
4F10
PUSH DE D5
Save Register Pair DE to the stack while Register Pair DE names the sector.
4F11
GOSUB to 4B64H (DIRCYL) to load Register D with the directory cylinder of drive Register C.
4F14
LD E,01H 1E 01
Load Register E with 01H, sector 1 of the directory cylinder: the hash index table.
4F16
LD HL,4300H 21 00 43
Load Register Pair HL with 4300H, the hash index table with the record's byte cleared.
4F19
GOSUB to 4768H (WRPROT) to write the buffer at 4300H to cylinder Register D, sector Register E of drive Register C with the deleted data mark: the NZ FLAG with an error number on failure.
4F1C
If the NZ FLAG is set (the write failed), JUMP to 4F23H to return error 17H.
4F1E
GOSUB to 4772H (VERSEC) to read the sector back without storing it: Register A = 06H for a good sector with the deleted data mark.
4F21
CP 06H FE 06
Compare Register A (the verify result) against 06H: the Z FLAG when the sector was written well.
4F23
LD A,17H 3E 17
Load Register A with 17H, error 23: HIT write error. LD does not change the flags. 4F1CH jumps here with the NZ FLAG.
4F25
POP DE D1
Restore the caller's Register Pair DE from the stack.
4F26
POP BC C1
Restore Register Pair BC (Register B = the record, Register C = the drive) from the stack. POP does not change the flags.
4F27
RET C9
RETurn with the Z FLAG on success, or the NZ FLAG and Register A = 17H.

4F28H - Find a Module above HIGH$

Request BCH. Register Pair DE = a name, ended by any character below 21H (a space, ENTER, 03H). Programs that keep themselves in memory above HIGH$ (4411H) start with a header: a JR (18H) over it, the link word (the HIGH$ before the module was put there), a byte whose bits 3-0 are the length of the name, and the name. The newest module starts at HIGH$ + 1, and each link word + 1 is the start of the module before it, up to a link of FFFFH. Found: Register Pair HL = the module, Register Pair DE = the byte after its name, Register A = 00H and the Z FLAG. Not found (or no module): error 08H, device not available. The first module is taken at HIGH$ less the top of memory found by the reset (4415H), with the CARRY FLAG clear: that is HIGH$ + 1 only when the top is FFFFH (48K); with the top at 7FFFH or BFFFH it is another address, whose byte is not taken as a module unless it holds 18H. When HIGH$ equals that top, there is no module.

4F28
PUSH BC C5
Save Register Pair BC to the stack: it comes back unchanged at either exit.
4F29
LD HL,(4411H) 2A 11 44
Fetch HIGH$ from 4411H-4412H into Register Pair HL.
4F2C
LD BC,(4415H) ED 4B 15 44
Fetch the top of memory that the reset found from 4415H-4416H into Register Pair BC (7FFFH, BFFFH or FFFFH).
4F30
SBC HL,BC ED 42
SUBtract Register Pair BC (the top) and the CARRY FLAG (clear after the CP 30H at 4E02H) from Register Pair HL (HIGH$): the Z FLAG when HIGH$ is the top, and with the top at FFFFH Register Pair HL = HIGH$ + 1, the first module.
4F32
If the Z FLAG is set (HIGH$ is the top: no module), JUMP to 4F6EH to return error 08H.
4F35
LD C,FFH 0E FF
Load Register C with FFH, so that the first INC C makes it 00H: the length of the name.
4F37
PUSH DE D5
Save Register Pair DE (the name) to the stack while it is measured.
4F38
INC C 0C
INCrement Register C: one more character of the name. 4F3DH loops back here.
4F39
LD A,(DE) 1A
Fetch the character at Register Pair DE into Register A.
4F3A
INC DE 13
INCrement Register Pair DE to the next character.
4F3B
CP 21H FE 21
Compare Register A (the character) against 21H: the NO CARRY FLAG for a character of the name (21H and above).
4F3D
If the NO CARRY FLAG is set (part of the name), LOOP BACK to 4F38H to count it.
4F3F
POP DE D1
Restore Register Pair DE (the name) from the stack: Register C = its length.
4F40
LD A,(HL) 7E
Fetch the first byte of the module at Register Pair HL into Register A. 4F6CH loops back here for the next module.
4F41
CP 18H FE 18
Compare Register A against 18H, the JR that starts every module header.
4F43
If the NZ FLAG is set (no header here), JUMP to 4F6EH to return error 08H: the chain ends.
4F45
PUSH HL E5
Save Register Pair HL (the module's start) to the stack.
4F46
INC HL 23
INCrement Register Pair HL to +01H, the JR's offset.
4F47
INC HL 23
INCrement Register Pair HL to +02H, the low byte of the link word.
4F48
INC HL 23
INCrement Register Pair HL to +03H, the high byte of the link word.
4F49
INC HL 23
INCrement Register Pair HL to +04H, the byte with the length of the module's name.
4F4A
LD A,(HL) 7E
Fetch the byte at +04H at Register Pair HL into Register A.
4F4B
AND 0FH E6 0F
Keep bits 3-0 of Register A (AND 0FH): the length of the module's name.
4F4D
CP C B9
Compare Register A (that length) against Register C (the length of the name looked for).
4F4E
If the NZ FLAG is set (the lengths differ), JUMP to 4F62H to go on to the next module.
4F50
INC HL 23
INCrement Register Pair HL to +05H, the first character of the module's name.
4F51
LD B,A 47
Copy Register A (the length) into Register B, the count of characters to compare.
4F52
PUSH DE D5
Save Register Pair DE (the name looked for) to the stack.
4F53
LD A,(DE) 1A
Fetch the next character of the name looked for at Register Pair DE into Register A. 4F59H loops back here.
4F54
CP (HL) BE
Compare Register A against the module's character at Register Pair HL.
4F55
If the NZ FLAG is set (they differ), JUMP to 4F61H to go on to the next module.
4F57
INC HL 23
INCrement Register Pair HL to the module's next character.
4F58
INC DE 13
INCrement Register Pair DE to the next character of the name looked for.
4F59
DECrement Register B and LOOP BACK to 4F53H until every character matched.
4F5B
EX DE,HL EB
Exchange Register Pairs DE and HL: Register Pair DE = the module's byte after its name, where its own data starts.
4F5C
POP HL E1
Discard the saved name from the stack into Register Pair HL.
4F5D
POP HL E1
Restore the module's start from the stack into Register Pair HL.
4F5E
POP BC C1
Restore the caller's Register Pair BC from the stack.
4F5F
XOR A AF
Set Register A to 00H (XOR A) with the Z FLAG: the module is found.
4F60
RET C9
RETurn with the Z FLAG, Register Pair HL = the module and Register Pair DE = the byte after its name.
4F61
POP DE D1
Restore Register Pair DE (the name looked for) from the stack.
4F62
POP HL E1
Restore Register Pair HL (the module's start) from the stack.
4F63
INC HL 23
INCrement Register Pair HL to +01H of the module.
4F64
INC HL 23
INCrement Register Pair HL to +02H, the low byte of the link word.
4F65
LD A,(HL) 7E
Fetch the low byte of the link word at Register Pair HL into Register A.
4F66
INC HL 23
INCrement Register Pair HL to +03H, the high byte of the link word.
4F67
LD H,(HL) 66
Fetch the high byte of the link word at Register Pair HL into Register H.
4F68
LD L,A 6F
Copy Register A (the low byte) into Register L: Register Pair HL = the link, the HIGH$ before this module was put in memory.
4F69
INC HL 23
INCrement Register Pair HL: the start of the module before it, or 0000H after a link of FFFFH.
4F6A
LD A,H 7C
Copy Register H (the high byte of the next module's start) into Register A.
4F6B
OR L B5
OR Register L (the low byte) into Register A: the Z FLAG when it is 0000H, the end of the chain.
4F6C
If the NZ FLAG is set (another module), LOOP BACK to 4F40H to look at it.
4F6E
POP BC C1
Restore the caller's Register Pair BC from the stack. 4F32H and 4F43H jump here.
4F6F
LD A,08H 3E 08
Load Register A with 08H, error 8: device not available.
4F71
OR A B7
Set the flags from Register A (OR A): the NZ FLAG, Register A not being 00H.
4F72
RET C9
RETurn with the NZ FLAG and Register A = 08H: no module has that name.