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

Page Customization

Introduction/Summary

VTOS 4.0.2 SYS8/SYS Disassembly - Disk Space for a File (Model I)

SYS8/SYS is the system overlay that finds the extent of a granule SYS0/SYS could not find, and gives a file more disk space. When SYS0/SYS works out the sector of a file's next record (4A3CH) and none of the five extents in the file's control block holds the granule, it issues request 9AH (4AB9H); the request handler loads SYS8/SYS at 4E00H (directory entry code 22H) and calls it with Index Register IX = the control block and Register Pair BC = the granule of the file wanted. When SYS1/SYS was the overlay in memory before, SYS0/SYS loads it again afterwards; no other overlay is put back (4AA2H). The manual: "This overlay performs several functions which are intrinsic to the operation of VTOS. You should never purge this overlay."

The file starts with a record of type 05H (the name SYS8), a record of type 1FH holding the copyright notice of Randolph Cook (1980) and three load blocks that fill 4E00H-5096H, 663 bytes; then a record of type 07H with the name SYS8A and three load blocks of that patch: 5000H (13H becomes 14H), 5005H (0AH becomes 0BH) and 5008H-5026H (the search for a free directory record rewritten). The page shows the bytes after the patch. The last record gives the transfer address 4E00H; one byte 03H follows it, which the loader never reads.

What SYS8/SYS Does

The file's directory record (entry code +07H of the control block, drive +06H) is read into 4200H and its extents are walked: each is a cylinder and a granule byte (bits 7-5 the first granule on the cylinder, bits 4-0 the granules less one) at +16H-+1FH; a cylinder FEH links to an extension record whose entry code follows it, FFH ends the list (4E4CH). The first extent that reaches the granule wanted is the answer.

When the list ends first, the file is given granules up to the one wanted (4E97H): the allocation table (directory cylinder, sector 0) is read into 5100H; each granule extends the last extent when the granule after its end is free (an extent counts at most 32 granules and may run on over the next cylinders) or starts a new extent at the first free granule found from the cylinder reached on, then from cylinder 0 (from cylinder 0 at once when no cylinder has been reached yet in the request: a record with no extent, or a last extent of 32 granules). A record's fifth extent place (+1EH-+1FH) is kept for the link: when the four others are full, a free directory record is taken (4F8CH), searched for from the directory sector of the file's own record and passing over the codes of the system files, given the file's hash code in the hash index, made an extension record (attribute 90H, +01H the entry code of the record linking to it, no extents) and linked from the full record with FEH and its entry code. The allocation table and the directory record are written back after each run (4F80H). The walk then starts again and finds the new extent.

ReturnRegisters
Z, NCAn extent of the control block is the one (its count brought up to date, 4E45H): Register Pair HL = the extent (Register L the cylinder, Register H the granule byte), Register A = the granule's place in it, Register Pair DE = the address of the control block's granule byte.
Z, CThe extent is not in the control block: the extents of the second to fourth places move one place up, the fifth is dropped (4E34H); Register Pair BC = the extent (Register C the cylinder, Register B the granule byte), Register Pair DE = the file's granules before it, Register Pair HL = +13H of the control block. SYS0/SYS stores it there as the second extent (4ADCH). Register A = 00H.
NZAn error code in Register A: 11H or 12H (directory read or write), 14H or 15H (allocation table read or write), 16H or 17H (hash index read or write), 1BH DISK SPACE FULL, 1EH DIRECTORY FULL - CAN'T EXTEND FILE, or 0FH from a directory write to a write protected diskette. A request other than 9AH returns the NZ FLAG with Register A = the code AND 70H, which is no error code.

Points to Note

When the extent found is not in the control block, Register A is 00H (4E42H), not the granule's place kept at 4E4AH; SYS0/SYS takes it as the place (4AADH), and the record is read or written in the extent's first granule. That happens for a granule that is not the first of an extent beyond those the control block holds, and for an extent this request has just made when it added more than one granule: a write positioned more than one granule past the end of the file (ALLOC's sector, for one) goes into the wrong granule, and the granule wanted is left unwritten.

4EBDH-4EC5H halve the file's entry code until it is below the count of cylinders, but the result is never used: Register A is loaded again at 4ECAH and Register L still holds the count of cylinders, so a file with no extent gets its first granule from the search that starts at cylinder 0 (4F46H).

The cylinder of a granule that extends an extent is tested against CBH, not against the drive's last cylinder (4F02H); the allocation table has FFH for a cylinder that does not exist, so no granule is taken there.

When a record runs full, the walk is restarted from 4F21H with Register Pair BC holding the granules still to add instead of the granule wanted; that walk can itself give the file granules, and 4E93H then walks again with the granule wanted and adds the rest, so the count comes out exact.

4FE5H, an entry that would start the search for a free record at the tick counter 4040H, is never called. 5026H, a byte 00H loaded by the patch, is never reached.

Before the patch SYS8A the search for a free directory record tested one index byte and returned (its old 5019H-501BH) and passed over directory sectors 2 and 3, so it tried two places, both codes 02H-07H: on a diskette with the system files those hold SYS0/SYS-SYS5/SYS, and no extension record could ever be made.

After request 9AH SYS0/SYS reloads only SYS1/SYS (4AA2H). A record access made from code inside another overlay that needs SYS8/SYS returns into SYS8/SYS's bytes: SYS7/SYS reading a file of commands long enough to have an extension record (its 4EF5H), or a buffer written back from inside SYS3/SYS's close.

Memory Map

Load Areas

Addresses
Size
Contents
4E00H-4E06H
7 bytes
Entry
Function 1 only.
4E07H-4E4BH
69 bytes
Function 1
The extent found, compared with the five of the control block, returned.
4E4CH-4E96H
75 bytes
Find the Extent in the Directory
The extents of the record and its extension records walked.
4E97H-4F23H
141 bytes
More Granules
The allocation table read, the last extent extended, a full record.
4F24H-4F65H
66 bytes
A New Extent
The first cylinder with a free granule, its first free granule.
4F66H-4F7FH
26 bytes
Take the Granule
The bit set, the extent counted, the next granule.
4F80H-4F8BH
12 bytes
Write the Table and the Record
Allocation table and directory sector written back.
4F8CH-4FE4H
89 bytes
An Extension Record
A free record taken, filled and linked.
4FE5H-5026H
66 bytes
Find a Free Directory Record
The hash index searched for a free entry code.
5027H-503FH
25 bytes
Bit of a Granule
BIT n,B and SET n,A built in place.
5040H-5096H
87 bytes
Table and Index Sectors
Read and write the allocation table and the hash index.

Variables

SYS8/SYS Cells

Address
Size
Contents
4E4AH
1 byte
Place in the Extent
Operand of LD A,00H at 4E49H: the granule's place in its extent (4E12H), returned at 4E4BH.
4F3CH
1 byte
Cylinders
Operand of CP 00H at 4F3BH: the drive's cylinders, at most CBH (4F35H).
4F88H
1 byte
Entry Code of the Record
Operand of LD B,00H at 4F87H: the record in 4200H (4E54H); written back by 4F89H, linked from by 4F8CH.
4FE0H
1 byte
Entry Code of the New Record
Operand of LD (HL),00H at 4FDFH: the extension record, stored at 4FA0H.
5014H
1 byte
Sectors of Records
Operand of CP 00H at 5013H: the directory sectors that hold records (4FFFH).
5032H
1 byte
BIT Opcode
Second byte of BIT n,B at 5031H, built at 502EH.
503EH
1 byte
SET Opcode
Second byte of SET n,A at 503DH, built at 5039H.
5100H-51FFH
256 bytes
Allocation Table
Not in the file: the drive's allocation table (5040H, 4F6BH, 5053H).

Memory Outside the File

Address
Size
Contents
4040H
1 byte
Tick Counter
Read by the unused entry 4FE5H.
4200H-42FFH
256 bytes
Sector Buffer
SYS0/SYS's buffer: the directory sector of the record (4E5AH, 4FADH, 4FD4H) or the hash index (4F8FH).
IX+06H, IX+07H
2 bytes
Control Block: Drive, Entry Code
The drive and the directory entry code of the file.
IX+0EH-IX+1FH
18 bytes
Control Block: Extents
The five extents compared at 4E21H; a granule byte brought up to date at 4E45H; the second to fourth moved up at 4E3DH.
Drive Code Table +06H-+08H
3 bytes
Drive Geometry
+06H the last cylinder (4EB1H, 4F2AH), +07H the sectors of a track and cylinder (4FEBH), +08H the granules of a cylinder (4EEBH); +09H the directory cylinder through 4B65H.

Outside Routines

AddressContents
4797HSYS0/SYS: byte Register A of the drive code table of drive Register C.
4B10HSYS0/SYS: read the directory sector of entry code Register B into 4200H, Register Pair HL = the record.
4B1FHSYS0/SYS: write the directory sector in 4200H and read it back.
4B45HSYS0/SYS: read a directory cylinder sector, checking its data mark.
4B65HSYS0/SYS: Register D = the directory cylinder of drive Register C.
4B6CHSYS0/SYS: Register A = Register A times Register E.
4B7BHSYS0/SYS: Register A = Register E divided by Register A, Register E = the remainder.
4768HSYS0/SYS: driver function 0EH, write a sector with the FAH data mark.
4772HSYS0/SYS: driver function 0AH, read a sector without storing it.

Disassembly

4E00H - Entry: Function 1 Only

SYS8/SYS is loaded at 4E00H by the request handler of SYS0/SYS (4C31H) for a request code whose bits 3-0 are AH (directory entry code 22H) and called at 4E00H with Register A = the request code. Its one function is 1, request 9AH, which SYS0/SYS issues at 4AB9H when no extent in an open file's control block holds the granule of the record position: Index Register IX = the file's control block, Register Pair BC = the granule of the file wanted (counted from 0). Any other code returns at once; XFER (SYS6/SYS member 28) issues 8AH only to have the file in memory.

4E00
AND 70H E6 70
Mask Register A (the request code) with 70H, keeping bits 6-4, the function number times 10H.
4E02
CP 10H FE 10
Compare Register A against 10H, function 1 (request 9AH).
4E04
If the Z FLAG is set (function 1), JUMP to 4E07H.
4E06
RET C9
RETURN to the request handler with the NZ FLAG of the compare and Register A = the masked code (00H for request 8AH).

4E07H - Function 1: The Extent of the Granule Wanted

4E4CH walks the extents of the file's directory records, giving it more granules when it has too few, until an extent holds the granule wanted. The granule's place in that extent is kept at 4E4AH. When one of the five extents of the control block (+0EH-+1FH) has the same cylinder and first granule, its granule byte is brought up to date and SYS8/SYS returns with the NC FLAG, Register Pair HL = the extent (Register L the cylinder, Register H the granule byte) and Register A = the place. Otherwise the extents from the second place on move one place up (the fifth is lost) and SYS8/SYS returns with the CARRY FLAG, Register Pair BC = the extent (Register C the cylinder, Register B the granule byte), Register Pair DE = the file's granules before it and Register Pair HL = +13H of the control block, where SYS0/SYS's 4ADCH stores it as the second extent; Register A is then 00H, not the place.

4E07
GOSUB to 4E4CH to find, or make, the extent that holds granule Register Pair BC: Register Pair HL = the extent (Register L cylinder, Register H granule byte), Register Pair DE = the granules of the file before it; the NZ FLAG and an error code in Register A when it fails.
4E0A
RET NZ C0
If the NZ FLAG is set (an error), RETURN to SYS0/SYS with the error code in Register A.
4E0B
PUSH HL E5
Save Register Pair HL (the extent) to the stack.
4E0C
LD H,B 60
Copy Register B (high byte of the granule wanted) into Register H.
4E0D
LD L,C 69
Copy Register C (low byte of the granule wanted) into Register L: Register Pair HL = the granule wanted.
4E0E
XOR A AF
Set Register A to 00H and clear the CARRY FLAG (XOR A) for the subtraction.
4E0F
SBC HL,DE ED 52
Subtract Register Pair DE (the granules before the extent) from Register Pair HL: Register Pair HL = the granule's place in the extent.
4E11
LD A,L 7D
Copy Register L (the place, 0-31) into Register A.
4E12
LD (4E4AH),A 32 4A 4E
Store Register A (the place) to 4E4AH, the operand of LD A,00H at 4E49H.
4E15
POP HL E1
Restore Register Pair HL (the extent) from the stack.
4E16
PUSH DE D5
Save Register Pair DE (the granules before the extent) to the stack.
4E17
PUSH IX DD E5
Save Index Register IX (the control block) to the stack.
4E19
EX (SP),HL E3
Exchange Register Pair HL with the word at the Stack Pointer: Register Pair HL = the control block, the stack holds the extent.
4E1A
LD DE,000EH 11 0E 00
Load Register Pair DE with 000EH, the offset of the first extent in a control block.
4E1D
ADD HL,DE 19
Add Register Pair DE to Register Pair HL: Register Pair HL = +0EH of the control block, the cylinder of its first extent.
4E1E
POP DE D1
Restore Register Pair DE from the stack: the extent, Register E the cylinder, Register D the granule byte.
4E1F
LD B,05H 06 05
Load Register B with 05H, the five extents of the control block.
4E21
LD A,(HL) 7E
Fetch the cylinder of the control block's extent at Register Pair HL into Register A.
4E22
INC HL 23
Increment Register Pair HL to that extent's granule byte.
4E23
CP E BB
Compare Register A against Register E (the cylinder of the extent found).
4E24
If the NZ FLAG is set (another cylinder), JUMP to 4E2CH for the next extent.
4E26
LD A,(HL) 7E
Fetch the control block's granule byte from Register Pair HL into Register A.
4E27
XOR D AA
Exclusive OR Register A with Register D (the granule byte of the extent found).
4E28
AND E0H E6 E0
Mask Register A to bits 7-5: the Z FLAG is set when the first granules are the same.
4E2A
If the Z FLAG is set (the same extent), JUMP to 4E45H to bring its count up to date.
4E2C
DEC B 05
Decrement Register B, the extents left to compare.
4E2D
If the Z FLAG is set (none of the five), JUMP to 4E34H to put the extent in the second place.
4E2F
INC HL 23
Increment Register Pair HL past the granule byte, to the low byte of the next extent's count of granules before it.
4E30
INC HL 23
Increment Register Pair HL to the high byte of that count.
4E31
INC HL 23
Increment Register Pair HL to the next extent's cylinder.
4E32
LOOP BACK to 4E21H to compare the next extent of the control block.
4E34
PUSH DE D5
Save Register Pair DE (the extent found) to the stack.
4E35
EX DE,HL EB
Exchange Register Pairs DE and HL: Register Pair DE = +1FH of the control block, the granule byte of the fifth extent; Register Pair HL = the extent found (Register L the cylinder, Register H the granule byte).
4E36
LD HL,FFFCH 21 FC FF
Load Register Pair HL with FFFCH (minus 4, the size of an extent with its count).
4E39
ADD HL,DE 19
Add Register Pair DE to Register Pair HL: Register Pair HL = +1BH, the granule byte of the fourth extent.
4E3A
LD BC,000CH 01 0C 00
Load Register Pair BC with 000CH, the 12 bytes of the second, third and fourth extents with their counts.
4E3D
LDDR ED B8
Move Register Pair BC bytes from Register Pair HL down to Register Pair DE, both counting down (LDDR): +10H-+1BH go to +14H-+1FH, the extents of the second to fourth places move one place up and the fifth is dropped. Register Pair BC ends at 0000H, Register Pair HL at +0FH, Register Pair DE at +13H.
4E3F
EX DE,HL EB
Exchange Register Pairs DE and HL: Register Pair HL = +13H of the control block, the granule byte of the second place; Register Pair DE = +0FH.
4E40
POP BC C1
Restore Register Pair BC from the stack: the extent found, Register C the cylinder, Register B the granule byte.
4E41
POP DE D1
Restore Register Pair DE from the stack: the granules of the file before the extent.
4E42
XOR A AF
Set Register A to 00H (XOR A). The place kept at 4E4AH is not returned on this road, so SYS0/SYS's 4AADH takes 00H as the place: the first granule of the extent instead of the one wanted when the granule wanted is not the extent's first.
4E43
SCF 37
Set the CARRY FLAG: SYS0/SYS's 4AB5H then stores the extent from Register Pairs BC and DE at Register Pair HL (4ADCH).
4E44
RET C9
RETURN to SYS0/SYS with Register A = 00H, the Z FLAG and the CARRY FLAG.
4E45
LD (HL),D 72
Store Register D (the granule byte from the directory) at Register Pair HL, the granule byte of the control block's extent: its count of granules is brought up to date.
4E46
EX DE,HL EB
Exchange Register Pairs DE and HL: Register Pair HL = the extent (Register L the cylinder, Register H the granule byte), Register Pair DE = the address of the control block's granule byte.
4E47
POP AF F1
Restore Register Pair AF from the stack: the granules before the extent saved at 4E16H are dropped.
4E48
XOR A AF
Set Register A to 00H, the Z FLAG and the NC FLAG (XOR A).
4E49
LD A,00H 3E 00
Self-Modifying Code
Load Register A with the granule's place in the extent, the operand 4E4AH (00H in the file, stored at 4E12H). LD does not change the flags.
4E4B
RET C9
RETURN to SYS0/SYS with the Z FLAG, the NC FLAG, Register Pair HL = the extent and Register A = the place.

4E4CH - Find the Extent in the Directory

Register Pair BC = the granule wanted, Index Register IX = the control block. The file's directory record (entry code +07H, drive +06H) is read and its extents (+16H-+1FH: cylinder and granule byte, a cylinder FEH links to an extension record whose entry code follows, FFH ends the list) are walked, Register Pair HL counting the granules before each. The first extent whose last granule is the granule wanted or beyond it is returned: Register Pair HL = the extent (Register L the cylinder, Register H the granule byte), Register Pair DE = the granules before it, Register A = 00H with the Z FLAG. At the end of the list 4E97H gives the file more granules and the walk starts again. 4F88H keeps the entry code of the record in 4200H.

4E4C
PUSH BC C5
Save Register Pair BC (the granule wanted) to the stack.
4E4D
LD DE,0000H 11 00 00
Load Register Pair DE with 0000H: no granules counted yet.
4E50
LD B,(IX+07H) DD 46 07
Fetch the directory entry code of the file from +07H of the control block at Index Register IX into Register B.
4E53
LD A,B 78
Copy Register B (the entry code of the record to read) into Register A. 4E8EH comes here with the entry code of an extension record.
4E54
LD (4F88H),A 32 88 4F
Store Register A (the entry code) to 4F88H, the operand of LD B,00H at 4F87H: the record 4F80H writes back and the record an extension links from.
4E57
LD C,(IX+06H) DD 4E 06
Fetch the drive from +06H of the control block at Index Register IX into Register C.
4E5A
GOSUB to 4B10H to read the directory sector of entry Register B on drive Register C into 4200H: Register Pair HL = the record; the NZ FLAG and error 11H when it cannot be read.
4E5D
LD BC,0016H 01 16 00
Load Register Pair BC with 0016H, the offset of the extents in a directory record.
4E60
ADD HL,BC 09
Add Register Pair BC to Register Pair HL: Register Pair HL = +16H of the record, the cylinder of its first extent. ADD of register pairs does not change the Z FLAG.
4E61
EX DE,HL EB
Exchange Register Pairs DE and HL: Register Pair DE = the extent pointer, Register Pair HL = the granules counted so far.
4E62
POP BC C1
Restore Register Pair BC from the stack: the granule wanted.
4E63
RET NZ C0
If the NZ FLAG is set (the directory read failed), RETURN with error 11H in Register A.
4E64
LD A,(DE) 1A
Fetch the cylinder of the extent at Register Pair DE into Register A.
4E65
CP FEH FE FE
Compare Register A against FEH: the NC FLAG for FEH (a link) or FFH (the end of the list), the Z FLAG for FEH.
4E67
If the NC FLAG is set (FEH or FFH), JUMP to 4E88H.
4E69
INC DE 13
Increment Register Pair DE to the extent's granule byte.
4E6A
LD A,(DE) 1A
Fetch the granule byte (bits 7-5 the first granule, bits 4-0 the granules less one) from Register Pair DE into Register A.
4E6B
PUSH HL E5
Save Register Pair HL (the granules before this extent) to the stack.
4E6C
AND 1FH E6 1F
Mask Register A to bits 4-0: the extent's granules less one.
4E6E
INC A 3C
Increment Register A: the extent's granules (1-32).
4E6F
ADD A,L 85
Add Register L (low byte of the granules before) to Register A.
4E70
LD L,A 6F
Copy Register A (the low byte of the sum) into Register L.
4E71
If the NC FLAG is set (no carry out of the low byte), JUMP to 4E74H.
4E73
INC H 24
Increment Register H: Register Pair HL = the granules up to the end of this extent.
4E74
PUSH HL E5
Save Register Pair HL (the granules up to the end of this extent) to the stack.
4E75
DEC HL 2B
Decrement Register Pair HL: the number of the extent's last granule.
4E76
XOR A AF
Set Register A to 00H and clear the CARRY FLAG (XOR A) for the subtraction.
4E77
SBC HL,BC ED 42
Subtract Register Pair BC (the granule wanted) from Register Pair HL: the NC FLAG is set when the extent's last granule is the granule wanted or beyond it.
4E79
POP HL E1
Restore Register Pair HL (the granules up to the end of this extent) from the stack.
4E7A
If the NC FLAG is set (the granule is in this extent), JUMP to 4E80H.
4E7C
INC DE 13
Increment Register Pair DE to the next extent's cylinder.
4E7D
POP AF F1
Restore Register Pair AF from the stack: the granules before this extent are dropped, Register Pair HL keeps the new count.
4E7E
LOOP BACK to 4E64H for the next extent of the record at Register Pair DE.
4E80
POP HL E1
Restore Register Pair HL from the stack: the granules before this extent.
4E81
EX DE,HL EB
Exchange Register Pairs DE and HL: Register Pair DE = the granules before the extent, Register Pair HL = its granule byte.
4E82
LD A,(HL) 7E
Fetch the granule byte from Register Pair HL into Register A.
4E83
DEC HL 2B
Decrement Register Pair HL to the extent's cylinder.
4E84
LD L,(HL) 6E
Fetch the cylinder from Register Pair HL into Register L.
4E85
LD H,A 67
Copy Register A (the granule byte) into Register H: Register Pair HL = the extent.
4E86
XOR A AF
Set Register A to 00H and the Z FLAG (XOR A): found.
4E87
RET C9
RETURN to the caller with the extent in Register Pair HL and the granules before it in Register Pair DE.
4E88
PUSH BC C5
Save Register Pair BC (the granule wanted) to the stack.
4E89
EX DE,HL EB
Exchange Register Pairs DE and HL: Register Pair HL = the FEH or FFH byte, Register Pair DE = the granules the file holds up to here.
4E8A
If the NZ FLAG is set (FFH, the end of the list), JUMP to 4E90H to give the file more granules.
4E8C
INC HL 23
Increment Register Pair HL to the byte after FEH: the entry code of the extension record.
4E8D
LD B,(HL) 46
Fetch the entry code of the extension record from Register Pair HL into Register B.
4E8E
JUMP to 4E53H to read the extension record and walk its extents; the granule wanted is on the stack again for 4E62H.
4E90
GOSUB to 4E97H to give the file the granules up to the one wanted: Register Pair DE = the granules held, Register Pair HL = the FFH byte, Register Pair BC = the granule wanted.
4E93
POP BC C1
Restore Register Pair BC from the stack: the granule wanted.
4E94
RET NZ C0
If the NZ FLAG is set (no space, or a disk error), RETURN with the error code in Register A.
4E95
LOOP BACK to 4E4CH to walk the extents again, now with the new granules.

4E97H - Give the File More Granules

Register Pair BC = the granule wanted, Register Pair DE = the granules the file holds, Register Pair HL = the byte FFH that ends the extents of the record in 4200H. The allocation table of the drive is read into 5100H and granule after granule is taken until the file holds the granule wanted: each one extends the last extent when the granule after its end is free on the allocation table (4ED3H), else starts a new extent at the first free granule from the cylinder reached, or from cylinder 0 when none has been reached yet in this request: a record with no extent, or a last extent that already counts 32 granules (4F24H). A record whose four extent places are full gets an extension record (4F8CH). At the end the allocation table and the directory record are written back (4F80H).

4E97
PUSH BC C5
Save Register Pair BC (the granule wanted) to the stack.
4E98
LD C,(IX+06H) DD 4E 06
Fetch the drive from +06H of the control block at Index Register IX into Register C.
4E9B
GOSUB to 5040H to read the allocation table (directory cylinder, sector 0) of drive Register C into 5100H: the NZ FLAG and error 14H when it cannot be read.
4E9E
POP BC C1
Restore Register Pair BC (the granule wanted) from the stack.
4E9F
RET NZ C0
If the NZ FLAG is set (the table could not be read), RETURN with error 14H in Register A.
4EA0
PUSH HL E5
Save Register Pair HL (the address of the FFH byte) to the stack.
4EA1
LD H,B 60
Copy Register B (high byte of the granule wanted) into Register H.
4EA2
LD L,C 69
Copy Register C (low byte of the granule wanted) into Register L: Register Pair HL = the granule wanted.
4EA3
XOR A AF
Set Register A to 00H and clear the CARRY FLAG (XOR A) for the subtraction.
4EA4
SBC HL,DE ED 52
Subtract Register Pair DE (the granules held) from Register Pair HL: the granule wanted less the granules held.
4EA6
LD B,H 44
Copy Register H (high byte of the difference) into Register B.
4EA7
LD C,L 4D
Copy Register L into Register C: Register Pair BC = the granule wanted less the granules held.
4EA8
INC BC 03
Increment Register Pair BC: the granules to add, so that the granule wanted is the last one.
4EA9
POP DE D1
Restore Register Pair DE from the stack: the address of the FFH byte, the cylinder place of the first empty extent.
4EAA
INC DE 13
Increment Register Pair DE to the granule byte place of the first empty extent.
4EAB
PUSH BC C5
Save Register Pair BC (the granules to add) to the stack.
4EAC
LD C,(IX+06H) DD 4E 06
Fetch the drive from +06H of the control block at Index Register IX into Register C.
4EAF
LD A,06H 3E 06
Load Register A with 06H, the offset of the last cylinder in a drive code table.
4EB1
GOSUB to 4797H to fetch byte +06H (the last cylinder) of the drive code table of drive Register C into Register A.
4EB4
INC A 3C
Increment Register A: the cylinders of the drive.
4EB5
POP BC C1
Restore Register Pair BC (the granules to add) from the stack.
4EB6
CP CBH FE CB
Compare Register A (the cylinders) against CBH, the most this code allows.
4EB8
If the C FLAG is set (fewer than CBH), JUMP to 4EBCH.
4EBA
LD A,CBH 3E CB
Load Register A with CBH, the most cylinders.
4EBC
LD L,A 6F
Copy Register A (the cylinders) into Register L.
4EBD
LD A,(IX+07H) DD 7E 07
Fetch the directory entry code of the file from +07H of the control block at Index Register IX into Register A.
4EC0
CP L BD
Compare Register A against Register L (the cylinders): the C FLAG is set when it is below.
4EC1
If the C FLAG is set (below the count of cylinders), JUMP to 4EC7H.
4EC3
SRL A CB 3F
Shift Register A right one bit (SRL): the entry code halved.
4EC5
LOOP BACK to 4EC0H. Register A ends as a cylinder number taken from the entry code, but nothing uses it: 4ECAH loads Register A again, and Register L keeps the count of cylinders.
4EC7
LD H,51H 26 51
Load Register H with 51H: Register Pair HL = 5100H + Register L, a byte of the allocation table (Register L is still the count of cylinders, so a new extent's search at 4F3AH begins with its second pass, from cylinder 0).
4EC9
PUSH BC C5
Save Register Pair BC (the granules to add) to the stack.
4ECA
LD A,E 7B
Copy Register E (the low byte of the address of the first empty extent's granule byte) into Register A.
4ECB
AND 1EH E6 1E
Mask Register A to bits 4-1: the place of the byte inside its 32 byte record.
4ECD
CP 16H FE 16
Compare Register A against 16H: the empty extent is the record's first (+16H-+17H).
4ECF
If the Z FLAG is set (the record has no extent), JUMP to 4F24H to start a new extent.
4ED1
DEC E 1D
Decrement Register E to the cylinder byte of the empty extent.
4ED2
DEC E 1D
Decrement Register E to the granule byte of the last extent.
4ED3
LD A,(DE) 1A
Fetch the granule byte of the last extent from Register Pair DE into Register A. 4F7CH comes back here for each further granule.
4ED4
AND 1FH E6 1F
Mask Register A to bits 4-0: the extent's granules less one.
4ED6
INC A 3C
Increment Register A: the extent's granules (1-32).
4ED7
LD C,A 4F
Copy Register A (the extent's granules) into Register C.
4ED8
CP 20H FE 20
Compare Register A against 20H, the most granules an extent can count.
4EDA
If the Z FLAG is set (the extent is full), JUMP to 4F0DH for the next extent place.
4EDC
LD A,(DE) 1A
Fetch the granule byte from Register Pair DE into Register A again.
4EDD
AND E0H E6 E0
Mask Register A to bits 7-5: the extent's first granule on its cylinder, shifted.
4EDF
RLCA 07
Rotate Register A left, the first of three, moving bits 7-5 round.
4EE0
RLCA 07
Rotate Register A left, the second of three, moving bits 7-5 round.
4EE1
RLCA 07
Rotate Register A left: bits 7-5 are now bits 2-0, the first granule.
4EE2
ADD A,C 81
Add Register C (the extent's granules) to Register A: the granule just after the extent, counted from the start of its cylinder.
4EE3
PUSH DE D5
Save Register Pair DE (the address of the granule byte) to the stack.
4EE4
LD E,A 5F
Copy Register A (the granule after the extent) into Register E, the dividend for 4B7BH.
4EE5
LD A,08H 3E 08
Load Register A with 08H, the offset of the granule geometry byte of a drive code table (+08H: sectors of a granule, granules of a cylinder).
4EE7
PUSH BC C5
Save Register Pair BC (Register C the extent's granules) to the stack.
4EE8
LD C,(IX+06H) DD 4E 06
Fetch the drive from +06H of the control block at Index Register IX into Register C.
4EEB
GOSUB to 4797H 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.
4EEE
POP BC C1
Restore Register Pair BC from the stack: Register C the extent's granules.
4EEF
RLCA 07
Rotate Register A (the drive code table byte) left, the first of three.
4EF0
RLCA 07
Rotate Register A (the drive code table byte) left, the second of three.
4EF1
RLCA 07
Rotate Register A left: bits 7-5 are now bits 2-0.
4EF2
AND 07H E6 07
Mask Register A to bits 2-0: the granules of a cylinder less one.
4EF4
INC A 3C
Increment Register A: the granules of a cylinder.
4EF5
GOSUB to 4B7BH to divide Register E (the granule after the extent) by Register A: Register A = the cylinders after the extent's cylinder, Register E = the granule on that cylinder.
4EF8
LD B,A 47
Copy Register A (the cylinders on) into Register B.
4EF9
LD C,E 4B
Copy Register E (the granule on the cylinder) into Register C.
4EFA
POP DE D1
Restore Register Pair DE (the address of the granule byte) from the stack.
4EFB
DEC DE 1B
Decrement Register Pair DE to the extent's cylinder byte.
4EFC
LD A,(DE) 1A
Fetch the extent's cylinder from Register Pair DE into Register A.
4EFD
INC DE 13
Increment Register Pair DE back to the granule byte.
4EFE
ADD A,B 80
Add Register B (the cylinders on) to Register A: the cylinder of the granule after the extent.
4EFF
LD L,A 6F
Copy Register A (that cylinder) into Register L.
4F00
LD H,51H 26 51
Load Register H with 51H: Register Pair HL = 5100H + the cylinder, its allocation byte.
4F02
CP CBH FE CB
Compare Register A (the cylinder) against CBH. The test is not against the drive's last cylinder; the allocation table marks a cylinder that does not exist with FFH.
4F04
If the NC FLAG is set (CBH or more), JUMP to 4F0DH for the next extent place.
4F06
LD A,C 79
Copy Register C (the granule on the cylinder) into Register A.
4F07
LD B,(HL) 46
Fetch the allocation byte of the cylinder from Register Pair HL into Register B.
4F08
GOSUB to 5027H to test the bit of granule Register A in Register B: the Z FLAG is set when the granule is free.
4F0B
If the Z FLAG is set (the granule after the extent is free), JUMP to 4F66H to add it to the extent.
4F0D
INC E 1C
Increment Register E to the cylinder byte of the next extent place.
4F0E
INC E 1C
Increment Register E to the granule byte of the next extent place.
4F0F
LD A,E 7B
Copy Register E (the low byte of the address) into Register A.
4F10
AND 1EH E6 1E
Mask Register A to bits 4-1: the place of the byte inside its record.
4F12
CP 1EH FE 1E
Compare Register A against 1EH: +1EH-+1FH, the last pair of the record, which is kept for the link to an extension record.
4F14
If the NZ FLAG is set (an extent place is left), JUMP to 4F24H to start a new extent there.
4F16
GOSUB to 4F80H to write the allocation table and the directory record back.
4F19
POP BC C1
Restore Register Pair BC (the granules to add) from the stack.
4F1A
RET NZ C0
If the NZ FLAG is set (a write failed), RETURN with the error code in Register A.
4F1B
PUSH BC C5
Save Register Pair BC (the granules to add) to the stack.
4F1C
GOSUB to 4F8CH to make an extension record and link the full record to it.
4F1F
POP BC C1
Restore Register Pair BC (the granules to add) from the stack.
4F20
RET NZ C0
If the NZ FLAG is set (no free record, or a disk error), RETURN with the error code in Register A.
4F21
JUMP to 4E4CH to walk the extents again into the new record. Register Pair BC holds the granules still to add here, not the granule wanted: when that count is not below the granules the file holds, this walk itself gives the file granules; its result returns to 4E93H, which takes the granule wanted off the stack and walks again, adding the rest, so the file ends with exactly the granules up to the one wanted.

4F24H - A New Extent

Register Pair DE = the granule byte place of the empty extent, Register L = the cylinder to start from. The allocation table is searched for a byte that is not FFH, from cylinder Register L to the last one and then from cylinder 0 (two passes, Register B). When every byte is FFH the table and the record are written back and error 1BH (DISK SPACE FULL) is returned.

4F24
LD A,06H 3E 06
Load Register A with 06H, the offset of the last cylinder in a drive code table.
4F26
PUSH BC C5
Save Register Pair BC (the granules to add) to the stack.
4F27
LD C,(IX+06H) DD 4E 06
Fetch the drive from +06H of the control block at Index Register IX into Register C.
4F2A
GOSUB to 4797H to fetch byte +06H (the last cylinder) of the drive code table of drive Register C into Register A.
4F2D
INC A 3C
Increment Register A: the cylinders of the drive.
4F2E
POP BC C1
Restore Register Pair BC (the granules to add) from the stack.
4F2F
CP CBH FE CB
Compare Register A (the cylinders) against CBH, the most this code allows.
4F31
If the C FLAG is set (fewer than CBH), JUMP to 4F35H.
4F33
LD A,CBH 3E CB
Load Register A with CBH, the most cylinders.
4F35
LD (4F3CH),A 32 3C 4F
Store Register A (the cylinders) to 4F3CH, the operand of CP 00H at 4F3BH.
4F38
LD B,02H 06 02
Load Register B with 02H, the two passes of the search.
4F3A
LD A,L 7D
Copy Register L (the cylinder) into Register A.
4F3B
CP 00H FE 00
Self-Modifying Code
Compare Register A against the cylinders of the drive, the operand 4F3CH (00H in the file, stored at 4F35H).
4F3D
If the NC FLAG is set (past the last cylinder), JUMP to 4F46H to start the next pass.
4F3F
LD A,(HL) 7E
Fetch the allocation byte of cylinder Register L from Register Pair HL into Register A.
4F40
INC A 3C
Increment Register A: 00H (the Z FLAG) only for FFH, a cylinder with no free granule.
4F41
If the NZ FLAG is set (a free granule), JUMP to 4F53H to start the extent on this cylinder.
4F43
INC L 2C
Increment Register L to the next cylinder of the allocation table.
4F44
LOOP BACK to 4F3AH to test the allocation byte of cylinder Register L.
4F46
LD L,00H 2E 00
Load Register L with 00H: the next pass starts at cylinder 0.
4F48
Decrement Register B (the passes left) and LOOP BACK to 4F3AH until both passes are done.
4F4A
POP BC C1
Restore Register Pair BC (the granules to add) from the stack.
4F4B
GOSUB to 4F80H to write the allocation table and the directory record back with the granules already taken.
4F4E
RET NZ C0
If the NZ FLAG is set (a write failed), RETURN with the error code in Register A.
4F4F
LD A,1BH 3E 1B
Load Register A with 1BH, the error DISK SPACE FULL.
4F51
OR A B7
OR Register A (1BH) with itself: the NZ FLAG set for the error.
4F52
RET C9
RETURN with error 1BH, DISK SPACE FULL, and the NZ FLAG.
4F53
LD A,FFH 3E FF
Load Register A with FFH: the granule byte of the new extent before its first granule is counted.
4F55
LD (DE),A 12
Store Register A (FFH) at Register Pair DE, the granule byte place of the new extent.
4F56
LD C,00H 0E 00
Load Register C with 00H, the first granule of the cylinder to test.
4F58
LD B,(HL) 46
Fetch the allocation byte of the cylinder from Register Pair HL into Register B.
4F59
LD A,C 79
Copy Register C (the granule) into Register A. The loop has no bound at the granules of a cylinder: it ends because the allocation byte is not FFH, and the bits of granules a cylinder does not have are 1 on the tables FORMAT/CMD writes (FCH for a free cylinder of two granules).
4F5A
GOSUB to 5027H to test the bit of granule Register A in Register B: the Z FLAG is set when it is free.
4F5D
If the Z FLAG is set (free), JUMP to 4F66H to take it.
4F5F
LD A,(DE) 1A
Fetch the granule byte of the new extent from Register Pair DE into Register A.
4F60
ADD A,20H C6 20
Add 20H to Register A: one more in bits 7-5, the first granule.
4F62
LD (DE),A 12
Store Register A back at Register Pair DE: FFH plus 20H for each granule passed over, so that the increment at 4F75H makes bits 7-5 the first granule and bits 4-0 zero.
4F63
INC C 0C
Increment Register C to the next granule of the cylinder.
4F64
LOOP BACK to 4F59H to test granule Register C of the cylinder.

4F66H - Take the Granule

Register C = the granule on cylinder Register L, Register Pair HL = its allocation byte, Register Pair DE = the granule byte of the extent. The granule is marked in use; a new extent gets the cylinder; the extent's count goes up by one. While granules are still to add, 4ED3H tries to add the next one to the same extent.

4F66
LD A,C 79
Copy Register C (the granule) into Register A.
4F67
GOSUB to 5034H to make the bit of granule Register A: Register A = 01H shifted left by the granule.
4F6A
OR (HL) B6
OR the allocation byte at Register Pair HL into Register A.
4F6B
LD (HL),A 77
Store Register A at Register Pair HL: the granule is in use on the allocation table in 5100H.
4F6C
DEC E 1D
Decrement Register E to the cylinder byte of the extent.
4F6D
LD A,(DE) 1A
Fetch the cylinder byte from Register Pair DE into Register A.
4F6E
INC A 3C
Increment Register A: 00H (the Z FLAG) for FFH, an extent place not yet used.
4F6F
If the NZ FLAG is set (the extent has its cylinder), JUMP to 4F73H.
4F71
LD A,L 7D
Copy Register L (the cylinder of the granule) into Register A.
4F72
LD (DE),A 12
Store Register A (the cylinder) at Register Pair DE: the new extent's cylinder.
4F73
INC E 1C
Increment Register E to the extent's granule byte.
4F74
LD A,(DE) 1A
Fetch the granule byte from Register Pair DE into Register A.
4F75
INC A 3C
Increment Register A: one more granule in bits 4-0 (for a new extent, bits 7-5 the first granule and bits 4-0 zero).
4F76
LD (DE),A 12
Store Register A (the new granule byte) at Register Pair DE.
4F77
POP BC C1
Restore Register Pair BC (the granules to add) from the stack.
4F78
DEC BC 0B
Decrement Register Pair BC: one granule added.
4F79
PUSH BC C5
Save Register Pair BC (the granules still to add) to the stack.
4F7A
LD A,B 78
Copy Register B (the high byte of the count) into Register A.
4F7B
OR C B1
OR Register C (the low byte) into Register A: the Z FLAG is set when no granule is left to add.
4F7C
If the NZ FLAG is set (more to add), JUMP to 4ED3H to try the granule after this extent.
4F7F
POP BC C1
Restore Register Pair BC from the stack: 0000H. The code runs on into 4F80H.

4F80H - Write the Table and the Record

Writes the allocation table at 5100H back (error 15H) and then the directory sector in 4200H with the record whose entry code is at 4F88H (SYS0/SYS's 4B1FH: errors 12H or 0FH). Returns the Z FLAG when both writes are good.

4F80
LD C,(IX+06H) DD 4E 06
Fetch the drive from +06H of the control block at Index Register IX into Register C.
4F83
GOSUB to 5053H to write the allocation table at 5100H to drive Register C and read it back: the NZ FLAG and error 15H when it fails.
4F86
RET NZ C0
If the NZ FLAG is set (the table could not be written), RETURN with error 15H in Register A.
4F87
LD B,00H 06 00
Self-Modifying Code
Load Register B with the entry code of the record in 4200H, the operand 4F88H (00H in the file, stored at 4E54H).
4F89
JUMP to 4B1FH to write the directory sector of entry Register B, drive Register C, from 4200H and read it back; its RETURN goes to the caller.

4F8CH - An Extension Record

The hash index is read into 4200H and a free directory record is found (4FE8H), starting in the directory sector of the file's own record. Its index byte gets the file's hash code and the index is written back. The new record gets the attribute byte 90H (bit 7 an extension record, bit 4 in use), +01H the entry code of the record that links to it (4F88H), 20 bytes 00H and ten bytes FFH (no extents). The full record then gets FEH and the new entry code at +1EH-+1FH, the link 4E8CH follows. No free record gives error 1EH, DIRECTORY FULL - CAN'T EXTEND FILE.

4F8C
LD C,(IX+06H) DD 4E 06
Fetch the drive from +06H of the control block at Index Register IX into Register C.
4F8F
GOSUB to 506CH to read the hash index (directory sector 1) of drive Register C into 4200H: Register A = 16H, the NZ FLAG when the read failed.
4F92
RET NZ C0
If the NZ FLAG is set (the index could not be read), RETURN with error 16H in Register A.
4F93
LD A,(IX+07H) DD 7E 07
Fetch the directory entry code of the file from +07H of the control block at Index Register IX into Register A.
4F96
AND 1FH E6 1F
Mask Register A to bits 4-0: the directory sector of the file's record, less 2.
4F98
GOSUB to 4FE8H to find a free directory record from that sector on: the Z FLAG and Register Pair HL = its index byte in 4200H (Register L its entry code) when one is free.
4F9B
LD A,1EH 3E 1E
Load Register A with 1EH, the error DIRECTORY FULL - CAN'T EXTEND FILE. LD does not change the flags.
4F9D
RET NZ C0
If the NZ FLAG is set (no free record), RETURN with error 1EH in Register A.
4F9E
LD B,L 45
Copy Register L (the entry code of the new record) into Register B.
4F9F
LD A,L 7D
Copy Register L (the entry code) into Register A.
4FA0
LD (4FE0H),A 32 E0 4F
Store Register A (the entry code of the new record) to 4FE0H, the operand of LD (HL),00H at 4FDFH, for the link.
4FA3
LD D,H 54
Copy Register H (42H, the page of the hash index at 4200H) into Register D.
4FA4
LD E,(IX+07H) DD 5E 07
Fetch the file's entry code from +07H of the control block at Index Register IX into Register E: Register Pair DE = the file's index byte in 4200H.
4FA7
LD A,(DE) 1A
Fetch the file's index byte (the hash code of its name) from Register Pair DE into Register A.
4FA8
LD (HL),A 77
Store Register A (the hash code) at Register Pair HL, the index byte of the new record.
4FA9
GOSUB to 507EH to write the hash index in 4200H back to drive Register C and read it back: the NZ FLAG and error 17H when it fails.
4FAC
RET NZ C0
If the NZ FLAG is set (the index could not be written), RETURN with error 17H in Register A.
4FAD
GOSUB to 4B10H to read the directory sector of the new record (entry Register B, drive Register C) into 4200H: Register Pair HL = the record; error 11H when it fails.
4FB0
RET NZ C0
If the NZ FLAG is set (the directory read failed), RETURN with error 11H in Register A.
4FB1
LD (HL),90H 36 90
Store 90H at Register Pair HL, the attribute byte of the new record: bit 7 an extension record, bit 4 in use.
4FB3
INC L 2C
Increment Register L to byte +01H of the record.
4FB4
PUSH BC C5
Save Register Pair BC (Register B the new entry code, Register C the drive) to the stack.
4FB5
LD A,(4F88H) 3A 88 4F
Fetch the entry code of the full record from 4F88H into Register A.
4FB8
LD (HL),A 77
Store Register A at Register Pair HL, byte +01H of the new record: the record that links to it (SYS3/SYS reads it back when it frees the record).
4FB9
INC L 2C
Increment Register L to byte +02H of the record.
4FBA
LD B,14H 06 14
Load Register B with 14H, the 20 bytes +02H-+15H.
4FBC
LD (HL),00H 36 00
Store 00H at Register Pair HL, one of the bytes +02H-+15H of the new record.
4FBE
INC L 2C
Increment Register L to the next byte of the record.
4FBF
Decrement Register B and LOOP BACK to 4FBCH until the 20 bytes are 00H.
4FC1
PUSH HL E5
Save Register Pair HL (+16H, the first extent place) to the stack.
4FC2
LD B,0AH 06 0A
Load Register B with 0AH, the 10 bytes +16H-+1FH: the four extent places and the place of the link.
4FC4
LD (HL),FFH 36 FF
Store FFH at Register Pair HL, one of the bytes +16H-+1FH: no extent.
4FC6
INC L 2C
Increment Register L to the next byte of the record.
4FC7
Decrement Register B and LOOP BACK to 4FC4H until the 10 bytes are FFH.
4FC9
POP DE D1
Restore Register Pair DE from the stack: +16H of the new record.
4FCA
INC DE 13
Increment Register Pair DE to +17H. Nothing uses Register Pair DE after this.
4FCB
POP BC C1
Restore Register Pair BC from the stack: Register B the new entry code, Register C the drive.
4FCC
GOSUB to 4B1FH to write the directory sector of entry Register B from 4200H and read it back.
4FCF
RET NZ C0
If the NZ FLAG is set (the write failed), RETURN with the error code in Register A.
4FD0
LD A,(4F88H) 3A 88 4F
Fetch the entry code of the full record from 4F88H into Register A.
4FD3
LD B,A 47
Copy Register A (that entry code) into Register B.
4FD4
GOSUB to 4B10H to read the directory sector of the full record (entry Register B, drive Register C) into 4200H: Register Pair HL = the record.
4FD7
RET NZ C0
If the NZ FLAG is set (the read failed), RETURN with error 11H in Register A.
4FD8
LD A,L 7D
Copy Register L (the low byte of the record's address) into Register A.
4FD9
ADD A,1EH C6 1E
Add 1EH to Register A: byte +1EH of the record, the last extent place.
4FDB
LD L,A 6F
Copy Register A into Register L: Register Pair HL = +1EH of the full record.
4FDC
LD (HL),FEH 36 FE
Store FEH at Register Pair HL: the link to an extension record.
4FDE
INC L 2C
Increment Register L to byte +1FH of the record.
4FDF
LD (HL),00H 36 00
Self-Modifying Code
Store the entry code of the new record, the operand 4FE0H (00H in the file, stored at 4FA0H), at Register Pair HL.
4FE1
GOSUB to 4B1FH to write the directory sector of entry Register B (the full record) from 4200H and read it back.
4FE4
RET C9
RETURN to the caller with the flags of the write.

4FE5H - Find a Free Directory Record

4FE8H: Register A = the entry code to start from (4F98H passes the sector of the file's record with record 0; 4FE5H would pass the tick counter), Register C = the drive, the hash index in 4200H. Entry codes are tried from that sector on, each sector's eight records (+20H) in turn, then the next sector, up to the last directory sector of the drive (its sectors a cylinder less 2, put into 5014H); codes 00H-07H and 20H-27H (the system files' records) are passed over. A second pass starts at 40H, the third record of directory sector 2. Returns the Z FLAG and Register Pair HL = the free index byte (Register L the entry code), or the NZ FLAG. The patch SYS8A (a 07H record and three load blocks) put the loop 5008H-5026H in: before it the search returned after testing one index byte (old 5019H-501BH) and passed over sector indexes 0 and 1, so only two places were tried, both codes 02H-07H: on a diskette with the system files those are system records and error 1EH was certain.

4FE5
LD A,(4040H) 3A 40 40
Fetch the tick counter from 4040H into Register A, which 4FE8H would take as a whole entry code to start from. Nothing on the diskette calls 4FE5H; 4F98H calls 4FE8H.
4FE8
PUSH AF F5
Save Register Pair AF (Register A the entry code to start from) to the stack.
4FE9
LD A,07H 3E 07
Load Register A with 07H, the offset of the sector byte in a drive code table.
4FEB
GOSUB to 4797H to fetch byte +07H of the drive code table of drive Register C into Register A: bits 4-0 the highest sector of a track, bits 7-5 a count less one that multiplies the sectors of a track.
4FEE
PUSH DE D5
Save Register Pair DE (the caller's) to the stack while it is used for the product.
4FEF
LD D,A 57
Copy Register A (the drive code table byte) into Register D.
4FF0
AND 1FH E6 1F
Mask Register A to bits 4-0: the highest sector number of a track.
4FF2
LD E,A 5F
Copy Register A (the highest sector of a track) into Register E.
4FF3
INC E 1C
Increment Register E: the sectors of a track.
4FF4
XOR D AA
Exclusive OR Register A with Register D: Register A = bits 7-5 of the byte alone.
4FF5
RLCA 07
Rotate Register A (bits 7-5 of the byte) left, the first of three.
4FF6
RLCA 07
Rotate Register A (bits 7-5 of the byte) left, the second of three.
4FF7
RLCA 07
Rotate Register A left: bits 7-5 are now bits 2-0, the multiplier less one.
4FF8
INC A 3C
Increment Register A: the multiplier of the sectors of a track.
4FF9
GOSUB to 4B6CH to multiply Register A by Register E: Register A = the sectors of a cylinder.
4FFC
POP DE D1
Restore Register Pair DE (the caller's) from the stack.
4FFD
SUB 02H D6 02
SUBtract 02H from Register A: the sectors of the directory cylinder that hold records (all but the allocation table and the hash index).
4FFF
LD (5014H),A 32 14 50
Store Register A to 5014H, the operand of CP 00H at 5013H (the patch SYS8A changed the address from 5013H).
5002
POP AF F1
Restore Register Pair AF from the stack: Register A = the entry code to start from.
5003
LD L,A 6F
Copy Register A (the entry code to start from) into Register L: Register Pair HL = its index byte in 4200H (Register H is 42H from 506CH's LD HL,4200H at 5073H, called at 4F8FH; the routines between keep Register Pair HL).
5004
GOSUB to 500BH to search from entry code Register L (the patch SYS8A changed the call from 500AH).
5007
RET Z C8
If the Z FLAG is set (a free record), RETURN with Register Pair HL at its index byte.
5008
LD L,3FH 2E 3F
Load Register L with 3FH, one below 40H (the third record of directory sector 2), for the second pass of the search.
500A
INC L 2C
Increment Register L: the next entry code (40H for the second pass, the third record of the first directory sector).
500B
LD A,L 7D
Copy Register L (the entry code) into Register A.
500C
AND D8H E6 D8
Mask Register A with D8H: bits 7, 6, 4 and 3 are all 0 only for codes 00H-07H and 20H-27H.
500E
If the Z FLAG is set (a system file's record), JUMP to 501AH for the next record.
5010
LD A,L 7D
Copy Register L (the entry code) into Register A.
5011
AND 1FH E6 1F
Mask Register A to bits 4-0: the directory sector less 2.
5013
CP 00H FE 00
Self-Modifying Code
Compare Register A against the sectors that hold records, the operand 5014H (00H in the file, stored at 4FFFH).
5015
If the NC FLAG is set (the sector is past the directory), JUMP to 5024H: no free record.
5017
LD A,(HL) 7E
Fetch the index byte of the entry code from Register Pair HL into Register A.
5018
OR A B7
OR Register A with itself: the Z FLAG is set for 00H, a free record.
5019
RET Z C8
If the Z FLAG is set (free), RETURN with Register Pair HL at the index byte.
501A
LD A,L 7D
Copy Register L (the entry code) into Register A.
501B
ADD A,20H C6 20
Add 20H to Register A: the next record of the same sector (bits 7-5).
501D
LD L,A 6F
Copy Register A (the next entry code) into Register L.
501E
If the NC FLAG is set (the eight records of the sector are not all done), LOOP BACK to 500BH.
5020
CP 1FH FE 1F
Compare Register A (now the sector less 2, the record bits wrapped to 0) against 1FH, the last sector number a code can name.
5022
If the NZ FLAG is set, LOOP BACK to 500AH to start the next sector at its first record.
5024
OR A B7
OR Register A with itself: the NZ FLAG (Register A is the sector, not 00H here).
5025
RET C9
RETURN with the NZ FLAG: no free record found.
5026
DEFB 00H 00
A byte 00H the patch SYS8A loaded here (it had held C9H, RET); nothing reaches it.

5027H - Test or Set the Bit of a Granule

5027H: Register A = a granule (0-7), Register B = an allocation byte; builds BIT n,B at 5031H and returns the Z FLAG when the granule's bit is 0, free. 5034H: Register A = a granule; builds SET n,A at 503DH and returns Register A = the granule's bit.

5027
AND 07H E6 07
Mask Register A to bits 2-0, the granule 0-7.
5029
RLCA 07
Rotate Register A left, the first of three, moving the granule bits up.
502A
RLCA 07
Rotate Register A left, the second of three, moving the granule bits up.
502B
RLCA 07
Rotate Register A left: the granule in bits 5-3.
502C
OR 40H F6 40
OR Register A with 40H: the second byte of BIT n,B (01 nnn 000).
502E
LD (5032H),A 32 32 50
Store Register A to 5032H, the second byte of the BIT instruction at 5031H.
5031
BIT 0,B CB 40
Self-Modifying Code
Test bit n of Register B (the opcode's second byte at 5032H, 40H in the file, set at 502EH): the Z FLAG is set when the granule is free.
5033
RET C9
RETURN with the Z FLAG for a free granule, the NZ FLAG for one in use.
5034
RLCA 07
Rotate Register A (the granule 0-7) left, the first of three.
5035
RLCA 07
Rotate Register A left, the second of three, moving the granule bits up.
5036
RLCA 07
Rotate Register A left: the granule in bits 5-3.
5037
OR C7H F6 C7
OR Register A with C7H: the second byte of SET n,A (11 nnn 111).
5039
LD (503EH),A 32 3E 50
Store Register A to 503EH, the second byte of the SET instruction at 503DH.
503C
XOR A AF
Set Register A to 00H (XOR A), the byte the SET below puts one bit into.
503D
SET 0,A CB C7
Self-Modifying Code
Set bit n of Register A (the opcode's second byte at 503EH, C7H in the file, set at 5039H): Register A = the granule's bit alone.
503F
RET C9
RETURN with Register A = the bit of the granule.

5040H - The Allocation Table and the Hash Index

Register C = the drive. Each routine finds the directory cylinder with 4B65H. 5040H reads sector 0, the allocation table, into 5100H (error 14H); 5053H writes it with the FAH data mark and reads it back (error 15H); 506CH reads sector 1, the hash index, into 4200H (Register A = 16H with the read's flags); 507EH writes it back (error 17H). The reads go through 4B45H, which checks for the data mark of the directory.

5040
PUSH DE D5
Save Register Pair DE (the caller's) to the stack.
5041
PUSH HL E5
Save Register Pair HL (the caller's) to the stack.
5042
GOSUB to 4B65H to load Register D with the directory cylinder of drive Register C (drive code table +09H).
5045
LD E,00H 1E 00
Load Register E with 00H, sector 0: the allocation table.
5047
LD HL,5100H 21 00 51
Point Register Pair HL to 5100H, the buffer of the allocation table.
504A
GOSUB to 4B45H to read cylinder Register D, sector Register E of drive Register C into Register Pair HL: the Z FLAG when it is read with the directory's data mark.
504D
POP HL E1
Restore Register Pair HL (the caller's) from the stack.
504E
POP DE D1
Restore Register Pair DE (the caller's) from the stack.
504F
RET Z C8
If the Z FLAG is set (read), RETURN with the Z FLAG.
5050
LD A,14H 3E 14
Load Register A with 14H, the error GAT READ ERROR.
5052
RET C9
RETURN with error 14H and the NZ FLAG of the read.
5053
PUSH DE D5
Save Register Pair DE (the caller's) to the stack.
5054
PUSH HL E5
Save Register Pair HL (the caller's) to the stack.
5055
GOSUB to 4B65H to load Register D with the directory cylinder of drive Register C.
5058
LD E,00H 1E 00
Load Register E with 00H, sector 0: the allocation table.
505A
LD HL,5100H 21 00 51
Point Register Pair HL to 5100H, the buffer of the allocation table.
505D
GOSUB to 4768H, driver function 0EH: write the sector from Register Pair HL with the data mark FAH.
5060
If the NZ FLAG is set (the write failed), JUMP to 5067H.
5062
GOSUB to 4772H, driver function 0AH: read the sector back without storing it; Register A = 06H for the FAH mark.
5065
CP 06H FE 06
Compare Register A against 06H: the Z FLAG is set when the sector reads back with the directory's mark.
5067
LD A,15H 3E 15
Load Register A with 15H, the error GAT WRITE ERROR. LD does not change the flags, so a failed write also gives 15H.
5069
POP HL E1
Restore Register Pair HL (the caller's) from the stack.
506A
POP DE D1
Restore Register Pair DE (the caller's) from the stack.
506B
RET C9
RETURN with the Z FLAG for a good write, else the NZ FLAG and error 15H.
506C
PUSH BC C5
Save Register Pair BC (the caller's) to the stack.
506D
PUSH DE D5
Save Register Pair DE (the caller's) to the stack.
506E
GOSUB to 4B65H to load Register D with the directory cylinder of drive Register C.
5071
LD E,01H 1E 01
Load Register E with 01H, sector 1: the hash index.
5073
LD HL,4200H 21 00 42
Point Register Pair HL to 4200H, SYS0/SYS's sector buffer.
5076
GOSUB to 4B45H to read cylinder Register D, sector Register E of drive Register C into Register Pair HL: the Z FLAG when it is read with the directory's data mark.
5079
POP DE D1
Restore Register Pair DE (the caller's) from the stack.
507A
POP BC C1
Restore Register Pair BC (the caller's) from the stack.
507B
LD A,16H 3E 16
Load Register A with 16H, the error HIT READ ERROR. LD does not change the flags.
507D
RET C9
RETURN with the flags of the read: Register A = 16H matters only with the NZ FLAG.
507E
PUSH BC C5
Save Register Pair BC (the caller's) to the stack.
507F
PUSH DE D5
Save Register Pair DE (the caller's) to the stack.
5080
GOSUB to 4B65H to load Register D with the directory cylinder of drive Register C.
5083
LD E,01H 1E 01
Load Register E with 01H, sector 1: the hash index.
5085
LD HL,4200H 21 00 42
Point Register Pair HL to 4200H, the hash index in SYS0/SYS's sector buffer.
5088
GOSUB to 4768H, driver function 0EH: write the sector from Register Pair HL with the data mark FAH.
508B
If the NZ FLAG is set (the write failed), JUMP to 5092H.
508D
GOSUB to 4772H, driver function 0AH: read the sector back without storing it; Register A = 06H for the FAH mark.
5090
CP 06H FE 06
Compare Register A against 06H: the Z FLAG is set when the sector reads back with the directory's mark.
5092
LD A,17H 3E 17
Load Register A with 17H, the error HIT WRITE ERROR. LD does not change the flags.
5094
POP DE D1
Restore Register Pair DE (the caller's) from the stack.
5095
POP BC C1
Restore Register Pair BC (the caller's) from the stack.
5096
RET C9
RETURN with the Z FLAG for a good write, else the NZ FLAG and error 17H.

5100H - The Allocation Table Buffer

Not in the file.

5100-51FF
DEFS 256
Not in the file. The allocation table of the drive (directory cylinder, sector 0): one byte a cylinder from 5100H, bit n set when granule n is in use (FFH for a cylinder that does not exist); read by 5040H, changed by 4F6BH, written back by 5053H.