TRS-80 DOS - NEWDOS/80 v2.0 for the Model III - SYS3/SYS Disassembled

Page Customization

Introduction/Summary

NEWDOS/80 v2.0 SYS3/SYS Disassembly - CLOSE, KILL, Timer Chain and Library Commands (Model III)

SYS3/SYS is a one-granule overlay that loads into the overlay area at 4D00H-51DFH (1,248 bytes in five load records, transfer address 4D00H). The manual describes it as the overlay that closes FCBs, kills files, and inserts and deletes entries in the 25 ms timer chain, and as the executor of the library commands BLINK, BREAK, CLOCK, DEBUG, JKL, LC, VERIFY and most of PURGE. It is called with LD A,code / RST 28H; the SYS0/SYS dispatcher at 4B67H loads it when 4288H does not already hold its directory slot, 05H, and calls 4D00H with the caller's registers.

The page shows every byte of the file once, in address order. 518FH-51DFH are 81 bytes of 00H.

RST 28H Codes Handled by SYS3/SYS

CodeCalled fromFunction
25H4428HCLOSE an FCB (4E02H): write the buffer, write the EOF into the FPDE, release the granules beyond the EOF, turn the FCB back into a filespec.
45H442CHKILL the file of an FCB (4DE6H): release its granules and directory entries, clear the FCB.
65H447BHPut a timer routine into the 25 ms chain (4D47H).
85H4413HTake a timer routine out of the chain (4D55H).
A5HSYS0/SYS 459DH (J-K-L), SYS1/SYS (JKL)Print the screen (515FH).
C5H4451HWrite the EOF of the FCB into the directory (502DH).
E5H, C = 1SYS1/SYS (BLINK)Cursor blink on or off (4D88H).
E5H, C = 2SYS1/SYS (CLOCK)Time display on or off through the ROM (4D70H).
E5H, C = 3SYS1/SYS (DEBUG)DEBUG before the next program (4D95H).
E5H, C = 4SYS1/SYS (VERIFY)Verify every sector write (4DA2H).
E5H, C = 5SYS1/SYS (BREAK)BREAK key on or off (4DB8H).
E5H, C = 6SYS7/SYS 4D70H (PURGE)The PURGE directory scan and questions (503AH).
E5H, C = 7SYS3/SYS 4FEAH through SYS0/SYS 497CHWrite the EOF of the FCB into the FPDE (4FF5H).
E5H, C = 8noneNot used: error 2AH (4D3CH).
E5H, C = 9SYS1/SYS (LC)Keyboard a-z toggle (4D7BH).
anything elseSYS6/SYS 568DH (C = FFH)Error 2AH (4D43H). SYS6/SYS uses this to load SYS3/SYS without running anything, then jumps to 4E1AH itself (60BCH) with the mode at 4FB8H set to CLOSE (60ADH), to release the granules beyond the EOF of a copied file.

The library command routines take Register Pair HL pointing to the parameter after the command word and return Register A = 00H with the Z FLAG, or an error code with the NZ FLAG. The Y/N parameter reader at 4DCCH takes a missing parameter as Y.

How CLOSE and KILL Release Space

CLOSE and KILL share one routine, told apart by the mode at 4FB8H, the operand of the LD A,nn at 4FB7H (00H CLOSE, 01H KILL):

  1. Granules to keep. CLOSE works out from the EOF how many granules the file needs (4E1AH: sectors rounded up, divided by 5 and rounded up). KILL keeps none.
  2. Granules owned. The extent elements of the FPDE and every FXDE (entry bytes 22-31: lump, then the first granule in bits 5-7 and the granules minus 1 in bits 0-4; FEH = the DEC of the next entry follows, FFH = the end) are added up (4E2FH). A wrong entry, or a link that is not in the fifth element, is error 2CH.
  3. Work maps. SYS3/SYS overwrites its own code at 4D00H-4DDFH with two maps and clears the overlay slot at 4288H (4E72H): 4D00H-4DBFH is a mask for GAT bytes 00H-BFH, 4DC0H-4DDFH a map with one bit per DEC.
  4. Freeing. Working back from the last element (4E9EH), one granule at a time is cleared in the mask until the excess is gone. CLOSE shortens the elements and turns an emptied element into the end mark FFH FFH; an FXDE left with no elements, and for KILL every entry, is freed by clearing bit 4 of its first byte and marking its DEC in the HIT map (4FC2H).
  5. GAT and HIT. The GAT (4F10H) and the HIT (4F33H) are read, changed and written only if the maps mark anything.
  6. The FCB. CLOSE rewrites the FCB as NAME/EXT:d ended by 03H; KILL sets its 32 bytes to 00H (4F5AH). Neither is done while bit 2 of 428BH is set (SYS6/SYS sets it at 5F74H).

CLOSE does not release anything when the file may not be written (access level 5 or more), when bit 6 of FCB+2 (the no deallocate bit copied from the FPDE) is set, or when the file needs all its granules. When it does have excess granules, it first writes back the directory sector it last read (4E97H); if that write fails it skips the release, and the error code is not returned.

Bytes Changed by SYS6/SYS

SYS6/SYS exchanges nine places in SYS3/SYS with its own values from its patch list at 5BE1H, to match bit 5 of the PDRIVE flags at 4282H (SYS6/SYS 560DH): 4E1DH, 4E3EH, 4E49H, 4E4FH-4E51H, 4E68H, 4EABH, 4EB7H, 4ECCH-4ECDH and 500FH. The page shows the values in the file.

Variables

Memory Used by SYS3/SYS

Address
Size
Contents
4D00H-4DBFH
192 bytes
GAT Mask
During CLOSE and KILL only, over the code of SYS3/SYS: FFH = keep, a 0 bit = free that granule. Set to FFH at 4E80H, marked at 4EF4H, applied to the GAT at 4F29H.
4DC0H-4DDFH
32 bytes
HIT Map
During CLOSE and KILL only: one bit per DEC, bit 7 of 4DC0H for DEC 00H. Cleared at 4E87H, set at 4FE7H; a set bit clears the HIT byte of that DEC at 4F4FH.
4EA1H
1 byte
Entry Being Worked On (Self-Modifying Code)
Operand of the LD E,nn at 4EA0H, stored at 4E36H and 4EB3H: the offset in 4300H of the directory entry being worked on. Read at 4FC2H.
4FB8H
1 byte
Mode (Self-Modifying Code)
Operand of the LD A,nn at 4FB7H, stored at 4DFCH (KILL, 01H) and 4E06H (CLOSE, 00H), and by SYS6/SYS at 60ADH. Read at 4E8DH, 4EB9H and 4EFFH.
5130H
1 byte
FDE Sectors (Self-Modifying Code)
Operand of the CP nn at 512FH, stored at 5090H: the number of FDE sectors PURGE scans (8 + HIT byte 1FH).
5140H
1 byte
PURGE Options
Bit 0 /ext given, bit 1 USR given. Stored at 5082H, read at 50A7H.
5141H-5143H
3 bytes
PURGE Extension
The wanted extension, three spaces in the file, filled at 5063H and compared at 50BAH.
3840H
1 byte
Keyboard Row 6
Bit 2 is the BREAK key, tested by the screen print at 5162H.
3C00H-3FFFH
1,024 bytes
Display Memory
Sent to the printer by the J-K-L screen print (515FH).
4019H
1 byte
Shift Lock Flag (ROM)
Bit 0 set: the ROM keyboard routine changes a-z to A-Z. Set and reset by LC (4D81H, 4D84H).
401CH
1 byte
Cursor Blink Switch (ROM)
00H = the cursor blinks. Bit 0 set and reset by BLINK (4D8EH, 4D91H).
4210H
1 byte
I/O Control Byte (ROM)
Bit 0 = show the time on the display, set by ROM 0298H and cleared by ROM 02A1H for CLOCK (4D76H, 4D73H).
4300H-43FFH
256 bytes
Directory Sector Buffer
The SYS0/SYS directory routines read the GAT, the HIT and FDE sectors here.
431FH
1 byte
HIT Byte 1FH
The number of FDE sectors over 8, read by PURGE at 508BH.
427EH
1 byte
Current Drive
The drive of the file, put into the filespec at 4F94H.
4285H
1 byte
Granules per Lump
IY+05H, byte 5 of the PDRIVE entry of the selected drive. Read at 4EDEH.
4288H
1 byte
Overlay Slot
Cleared at 4E77H so that SYS3/SYS is reloaded after its code has been used as work space.
4289H
1 byte
System Flags
Bit 7, DEBUG enabled, reset and set by DEBUG (4D9BH, 4D9EH).
428BH
1 byte
Temporary Flags
Bit 2 (set by SYS6/SYS at 5F74H) skips the FCB rewrite at 4F5AH.
4290H
1 byte
Option AX
The highest code the printer can print, read by the screen print at 5181H.
4478H-447AH
3 bytes
BREAK Hook
JP 4546H or RET, set by BREAK (4DBEH, 4DC7H).
4480H-449FH
32 bytes
DOS FCB
PURGE kills through it: FCB+0 = 80H (5118H), FCB+7 = the DEC (50CEH).
4584H-4585H
2 bytes
Timer Chain Head
The operand of the SYS0/SYS LD HL,nnnn at 4583H, changed by the enqueue (4D48H, 4D4BH) and the dequeue (4D57H).
4A2DH-4A2FH
3 bytes
Verify Switch
LD A,(IX+01H) or NOP / LD A,0FFH in the SYS0/SYS sector write, set by VERIFY (4DB0H, 4DB3H).

Outside Routines Called

AddressContents
0033HROM: display the character in Register A (50E2H, 5102H, 5107H, 513AH).
003BHROM: send the character in Register A to the printer (5173H, 518AH).
0049HROM: wait for a key (50F0H).
0298H, 02A1HROM: turn the time display on (4D76H) or off (4D73H).
4409HSYS0/SYS: the error exit that displays the error and returns to DOS READY (4DE0H).
4467HSYS0/SYS: display a message (50EDH).
4791HSYS0/SYS: select a drive and check for a turning diskette (503DH).
48AFHSYS0/SYS: read directory sector Register A into 4300H (4F1FH, 4F3FH, 5087H).
48C4HSYS0/SYS: write the directory sector in 4300H (4E5FH, 4E97H).
48D4H, 48DBHSYS0/SYS: read the FDE sector of the DEC in Register A, if needed (4E5AH) or always (5095H, 511FH).
48F0HSYS0/SYS: read the FPDE of the open FCB at Index Register IX (4DE9H, 4FF5H).
4925HSYS0/SYS: check that the FCB is open and run the rest as a DOS function (4DE6H, 4DF4H, 4E02H, 502DH).
495EHSYS0/SYS: check the access level of the FCB against Register B (4DEFH).
4972HSYS0/SYS: the error exit of a DOS function (4E56H).
497CHSYS0/SYS: the return point that chains to another overlay function, pushed at 4FF1H.
4A07HSYS0/SYS: write the buffer of the FCB if it holds changed data (4FF2H).
4C57HSYS0/SYS: divide Register Pair HL by 5 (4E1FH).
4C7AHSYS0/SYS: test for the end of the command line or a separator (4DDDH, 5041H).

Disassembly

4D00H - Function Dispatcher

SYS3/SYS is entered here by the SYS0/SYS RST 28H dispatcher (4B67H) with Register A = the RST 28H code and the caller's Register Pairs BC, DE and HL unchanged. Bits 5-7 of the code are the function number (bits 0-4 = 05H, SYS3/SYS's directory slot): 25H CLOSE (4428H), 45H KILL (442CH), 65H enqueue a timer routine (447BH), 85H dequeue a timer routine (4413H), A5H the J-K-L screen print (SYS0/SYS 459DH), C5H write EOF (4451H). Function 7 (E5H) takes a sub-function in Register C: 1 BLINK, 2 CLOCK, 3 DEBUG, 4 VERIFY, 5 BREAK, 6 PURGE (chained from SYS7/SYS), 7 EOF to the FPDE (chained from 4FEAH), 8 not used (error 2AH), 9 LC. Any other code or sub-function returns error 2AH; SYS6/SYS (568DH) relies on this to load SYS3/SYS with Register C = FFH and then use its code. The first 224 bytes (4D00H-4DDFH) are also used as work space by CLOSE and KILL once this code has run (4E7BH).

4D00
CP 25H FE 25
Compare Register A (the RST 28H code) against 25H, function 1: CLOSE (the 4428H vector).
4D02
If the Z FLAG is set (Register A = 25H), JUMP to 4E02H to close the FCB at Register Pair DE.
4D05
CP 45H FE 45
Compare Register A (the RST 28H code) against 45H, function 2: KILL (the 442CH vector).
4D07
If the Z FLAG is set (Register A = 45H), JUMP to 4DE6H to kill the file of the FCB at Register Pair DE.
4D0A
CP 65H FE 65
Compare Register A (the RST 28H code) against 65H, function 3: enqueue a timer routine (the 447BH vector).
4D0C
If the Z FLAG is set (Register A = 65H), JUMP to 4D47H to put the block at Register Pair DE into the timer chain.
4D0E
CP 85H FE 85
Compare Register A (the RST 28H code) against 85H, function 4: dequeue a timer routine (the 4413H vector).
4D10
If the Z FLAG is set (Register A = 85H), JUMP to 4D55H to take the block at Register Pair DE out of the timer chain.
4D12
CP A5H FE A5
Compare Register A (the RST 28H code) against A5H, function 5: the J-K-L key combination found by SYS0/SYS at 459DH.
4D14
If the Z FLAG is set (Register A = A5H), JUMP to 515FH to print the screen.
4D17
CP C5H FE C5
Compare Register A (the RST 28H code) against C5H, function 6: write the EOF (the 4451H vector).
4D19
If the Z FLAG is set (Register A = C5H), JUMP to 502DH to write the EOF of the FCB at Register Pair DE.
4D1C
CP E5H FE E5
Compare Register A (the RST 28H code) against E5H, function 7, whose sub-function is in Register C.
4D1E
If the NZ FLAG is set (any other code), JUMP to 4D43H to return error 2AH.
4D20
DEC C 0D
Function 7: DECrement Register C (the sub-function). The Z FLAG is set for sub-function 1.
4D21
If the Z FLAG is set (Register C was 1, BLINK), JUMP to 4D88H.
4D24
DEC C 0D
DECrement Register C (the sub-function) again. The Z FLAG is set for sub-function 2.
4D25
If the Z FLAG is set (Register C was 2, CLOCK), JUMP to 4D70H.
4D27
DEC C 0D
DECrement Register C (the sub-function) again. The Z FLAG is set for sub-function 3.
4D28
If the Z FLAG is set (Register C was 3, DEBUG), JUMP to 4D95H.
4D2B
DEC C 0D
DECrement Register C (the sub-function) again. The Z FLAG is set for sub-function 4.
4D2C
If the Z FLAG is set (Register C was 4, VERIFY), JUMP to 4DA2H.
4D2F
DEC C 0D
DECrement Register C (the sub-function) again. The Z FLAG is set for sub-function 5.
4D30
If the Z FLAG is set (Register C was 5, BREAK), JUMP to 4DB8H.
4D33
DEC C 0D
DECrement Register C (the sub-function) again. The Z FLAG is set for sub-function 6.
4D34
If the Z FLAG is set (Register C was 6, PURGE, chained from SYS7/SYS 4D70H with Register B = the drive number), JUMP to 503AH.
4D37
DEC C 0D
DECrement Register C (the sub-function) again. The Z FLAG is set for sub-function 7.
4D38
If the Z FLAG is set (Register C was 7, write the EOF of the FCB into its FPDE, chained from 4FEAH through SYS0/SYS 497CH), JUMP to 4FF5H.
4D3B
DEC C 0D
DECrement Register C (the sub-function) again. The Z FLAG is set for sub-function 8.
4D3C
If the Z FLAG is set (Register C was 8), JUMP to 4D43H to return error 2AH: sub-function 8 is not used by this DOS.
4D3F
DEC C 0D
DECrement Register C (the sub-function) again. The Z FLAG is set for sub-function 9.
4D40
If the Z FLAG is set (Register C was 9, LC), JUMP to 4D7BH. Any other sub-function (0, 10 and up, and the FFH that SYS6/SYS uses to load SYS3/SYS) goes on into 4D43H.

4D43H - Return Error 2AH

Reached from the dispatcher for an unknown function code or function 7 sub-function.

4D43
LD A,2AH 3E 2A
Load Register A with error code 2AH (ILLEGAL DOS FUNCTION).
4D45
OR A B7
OR Register A (2AH) with itself to set the NZ FLAG, since Register A is not zero.
4D46
RET C9
RETurn to the caller of the RST 28H with error 2AH in Register A and the NZ FLAG set.

4D47H - Function 65H: Enqueue a Timer Routine (447BH)

Manual section 3.8. Register Pair DE points to the caller's timer routine block: bytes 0-1 the forward link (filled in here), byte 2 the interval in 25 ms ticks, byte 3 the countdown, the routine itself from byte 4. The block is put at the head of the chain whose head pointer is the operand of the SYS0/SYS LD HL,nnnn at 4583H (the operand is at 4584H-4585H), which the clock routine of SYS0/SYS walks at every 25 ms tick.

4D47
DI F3
Disable interrupts, so that the clock routine of SYS0/SYS (4583H) cannot walk the chain while it is being changed.
4D48
LD HL,(4584H) 2A 84 45
Fetch the chain head from 4584H-4585H (the operand of the SYS0/SYS LD HL,nnnn at 4583H) into Register Pair HL: the first block in the chain, or 0000H if the chain is empty.
4D4B
LD (4584H),DE ED 53 84 45
Store Register Pair DE (the caller's new block) at 4584H-4585H: the new block becomes the head of the chain.
4D4F
EX DE,HL EB
Exchange Register Pairs DE and HL: Register Pair HL = the link to set (byte 0 of the new block), Register Pair DE = the value for it (the old chain head). The dequeue (4D6EH) comes here with Register Pair HL = the block after the removed one and Register Pair DE = the link that pointed to the removed block.
4D50
LD (HL),E 73
Store Register E (the low byte of the link value) at Register Pair HL, byte 0 of the link.
4D51
INC HL 23
INCrement Register Pair HL to byte 1 of the link.
4D52
LD (HL),D 72
Store Register D (the high byte of the link value) at Register Pair HL: the link now points to the next block.
4D53
EI FB
Enable interrupts again. 4D61H comes here when the dequeue did not find the block.
4D54
RET C9
RETurn to the caller of the RST 28H. The enqueue and the dequeue give no result; Register A holds whatever was last loaded into it.

4D55H - Function 85H: Dequeue a Timer Routine (4413H)

Manual section 3.9. Register Pair DE points to the block to take out of the timer chain. The links are followed from the chain head at 4584H; when a link points to the block, that link is replaced by the block's own link. A block that is not in the chain is ignored.

4D55
LD B,D 42
Copy Register D (the high byte of the block to remove) into Register B.
4D56
LD C,E 4B
Copy Register E (the low byte) into Register C: Register Pair BC = the block to remove.
4D57
LD HL,4584H 21 84 45
Point Register Pair HL to 4584H, where the chain head is kept: the first link to examine.
4D5A
DI F3
Disable interrupts, so that the clock routine of SYS0/SYS (4583H) cannot walk the chain while it is being changed.

Chain Loop
Register Pair HL = the low byte of the link being examined.

4D5B
LD E,(HL) 5E
Fetch the low byte of the link at Register Pair HL into Register E.
4D5C
INC HL 23
INCrement Register Pair HL to the high byte of the link.
4D5D
LD D,(HL) 56
Fetch the high byte of the link at Register Pair HL into Register D: Register Pair DE = the block this link points to.
4D5E
EX DE,HL EB
Exchange Register Pairs DE and HL: Register Pair HL = the block the link points to, Register Pair DE = the high byte of the link.
4D5F
LD A,H 7C
Copy Register H (the high byte of the block address) into Register A.
4D60
OR L B5
OR Register L (the low byte) into Register A: the Z FLAG is set if the link is 0000H, the end of the chain.
4D61
If the Z FLAG is set (the end of the chain and the block was not in it), JUMP to 4D53H to enable interrupts and return.
4D63
OR A B7
Clear the CARRY FLAG with OR Register A, ready for the SBC.
4D64
SBC HL,BC ED 42
SUBtract Register Pair BC (the block to remove) from Register Pair HL (the block the link points to): the Z FLAG is set if they are the same.
4D66
ADD HL,BC 09
ADD Register Pair BC back to Register Pair HL: Register Pair HL = the block address again. ADD HL,BC does not change the Z FLAG.
4D67
If the NZ FLAG is set (another block), LOOP BACK to 4D5BH to examine that block's own link (Register Pair HL = its byte 0).
4D69
DEC DE 1B
Found. DECrement Register Pair DE to the low byte of the link that points to the block.
4D6A
LD A,(HL) 7E
Fetch the low byte of the removed block's own link at Register Pair HL into Register A.
4D6B
INC HL 23
INCrement Register Pair HL to the high byte of that link.
4D6C
LD H,(HL) 66
Fetch the high byte at Register Pair HL into Register H.
4D6D
LD L,A 6F
Copy Register A (the low byte) into Register L: Register Pair HL = the block after the removed one, or 0000H.
4D6E
JUMP to 4D4FH to store Register Pair HL into the link at Register Pair DE, enable interrupts and return.

4D70H - Function E5H Sub-function 2: CLOCK

CLOCK[,Y|N] (manual 2.12). The ROM shows the time on the display while bit 0 of its I/O control byte at 4210H is set: CLOCK or CLOCK,Y sets it through ROM 0298H, CLOCK,N clears it through ROM 02A1H.

4D70
GOSUB to 4DCCH to read the Y/N parameter at Register Pair HL (the command line after CLOCK). The Z FLAG returns for N, the NZ FLAG for Y or no parameter.
4D73
If the Z FLAG is set (N), JUMP to the ROM at 02A1H, which clears bit 0 of the ROM's I/O control byte at 4210H (the time is no longer shown) and RETurns to SYS1/SYS with the Z FLAG still set.
4D76
Y or no parameter: GOSUB to the ROM at 0298H, which sets bit 0 of the ROM's I/O control byte at 4210H: the ROM shows the time on the display.
4D79
XOR A AF
Set Register A to 0 with XOR, which also sets the Z FLAG: no error.
4D7A
RET C9
RETurn to SYS1/SYS with Register A = 00H and the Z FLAG.

4D7BH - Function E5H Sub-function 9: LC

LC[,Y|N] (manual 2.28). Sets the keyboard a-z toggle, bit 0 of 4019H, the shift lock flag of the ROM keyboard routine: LC or LC,Y clears it (a-z are accepted as they are), LC,N sets it (a-z are changed to A-Z).

4D7B
GOSUB to 4DCCH to read the Y/N parameter at Register Pair HL (the command line after LC); nothing may follow it. The Z FLAG and Register A = 00H return for N, the NZ FLAG for Y or no parameter.
4D7E
LD HL,4019H 21 19 40
Point Register Pair HL to 4019H, the ROM's shift lock flag: while its bit 0 is set the ROM keyboard routine changes a-z to A-Z.
4D81
SET 0,(HL) CB C6
Set bit 0 of the byte at Register Pair HL (4019H): a-z are changed to A-Z. SET does not change the flags.
4D83
RET Z C8
If the Z FLAG is set (N), RETurn to SYS1/SYS with Register A = 00H (set by 4DCCH) and the Z FLAG.
4D84
RES 0,(HL) CB 86
Y or no parameter: reset bit 0 of the byte at Register Pair HL (4019H): a-z are accepted without change.
4D86
XOR A AF
Set Register A to 0 with XOR, which also sets the Z FLAG: no error.
4D87
RET C9
RETurn to SYS1/SYS with Register A = 00H and the Z FLAG.

4D88H - Function E5H Sub-function 1: BLINK

BLINK[,Y|N] (manual 2.6). Sets bit 0 of 401CH, the cursor blink switch of the ROM (00H = the cursor blinks): BLINK or BLINK,Y clears it, BLINK,N sets it.

4D88
GOSUB to 4DCCH to read the Y/N parameter at Register Pair HL (the command line after BLINK); nothing may follow it. The Z FLAG and Register A = 00H return for N, the NZ FLAG for Y or no parameter.
4D8B
LD HL,401CH 21 1C 40
Point Register Pair HL to 401CH, the ROM's cursor blink switch: the cursor blinks while it is 00H.
4D8E
SET 0,(HL) CB C6
Set bit 0 of the byte at Register Pair HL (401CH): the cursor does not blink. SET does not change the flags.
4D90
RET Z C8
If the Z FLAG is set (N), RETurn to SYS1/SYS with Register A = 00H (set by 4DCCH) and the Z FLAG.
4D91
RES 0,(HL) CB 86
Y or no parameter: reset bit 0 of the byte at Register Pair HL (401CH): the cursor blinks.
4D93
XOR A AF
Set Register A to 0 with XOR, which also sets the Z FLAG: no error.
4D94
RET C9
RETurn to SYS1/SYS with Register A = 00H and the Z FLAG.

4D95H - Function E5H Sub-function 3: DEBUG

DEBUG[,Y|N] (manual 2.18). Bit 7 of 4289H is the DEBUG enable flag: SYS2/SYS (4D88H) tests it after loading a program with load-and-run and enters DEBUG (440DH) instead of starting the program.

4D95
GOSUB to 4DCCH to read the Y/N parameter at Register Pair HL; nothing may follow it. The Z FLAG and Register A = 00H return for N, the NZ FLAG for Y or no parameter.
4D98
LD HL,4289H 21 89 42
Point Register Pair HL to 4289H, the DOS flag byte.
4D9B
RES 7,(HL) CB BE
Reset bit 7 of the byte at Register Pair HL (4289H): DEBUG disabled. RES does not change the flags.
4D9D
RET Z C8
If the Z FLAG is set (N), RETurn to SYS1/SYS with Register A = 00H and the Z FLAG.
4D9E
SET 7,(HL) CB FE
Y: set bit 7 of the byte at Register Pair HL (4289H): DEBUG is entered before the next program started with load-and-run.
4DA0
XOR A AF
Set Register A to 0 with XOR, which also sets the Z FLAG: no error.
4DA1
RET C9
RETurn to SYS1/SYS with Register A = 00H and the Z FLAG.

4DA2H - Function E5H Sub-function 4: VERIFY

VERIFY[,Y|N] (manual 2.48). 4A2DH-4A2FH in the SYS0/SYS sector write routine loads the value whose bits 7 and 4 decide whether the write is read back: LD A,(IX+01H) (FCB+1, verify only for record and byte I/O) or NOP / LD A,0FFH (every write through 4439H is verified).

4DA2
GOSUB to 4DCCH to read the Y/N parameter at Register Pair HL; nothing may follow it. The Z FLAG returns for N, the NZ FLAG for Y or no parameter.
4DA5
LD A,DDH 3E DD
Load Register A with DDH, the first byte of LD A,(IX+01H).
4DA7
LD HL,017EH 21 7E 01
Load Register Pair HL with 017EH: the bytes 7EH, 01H, the rest of LD A,(IX+01H) (DD 7E 01).
4DAA
If the Z FLAG is set (N), JUMP to 4DB0H to store LD A,(IX+01H): the verify follows FCB+1.
4DAC
XOR A AF
Y: set Register A to 00H (the NOP opcode) with XOR.
4DAD
LD HL,FF3EH 21 3E FF
Load Register Pair HL with FF3EH: the bytes 3EH, FFH = LD A,0FFH. With the NOP, 4A2DH-4A2FH becomes NOP / LD A,0FFH and every write is verified.
4DB0
LD (4A2DH),A 32 2D 4A
Store Register A (DDH or 00H) at 4A2DH in the SYS0/SYS sector write routine.
4DB3
LD (4A2EH),HL 22 2E 4A
Store Register Pair HL (017EH or FF3EH) at 4A2EH-4A2FH.
4DB6
XOR A AF
Set Register A to 0 with XOR, which also sets the Z FLAG: no error.
4DB7
RET C9
RETurn to SYS1/SYS with Register A = 00H and the Z FLAG.

4DB8H - Function E5H Sub-function 5: BREAK

BREAK[,Y|N] (manual 2.8). 4478H-447AH is the SYS0/SYS BREAK hook: the ROM keyboard driver does an RST 28H with Register A = 01H when BREAK is pressed, and the dispatcher sends codes below 20H to 4478H. JP 4546H throws the key away, RET passes it on as a key with code 01H. DOS READY sets it again from option AG.

4DB8
GOSUB to 4DCCH to read the Y/N parameter at Register Pair HL; nothing may follow it. The Z FLAG returns for N, the NZ FLAG for Y or no parameter.
4DBB
LD HL,4546H 21 46 45
Point Register Pair HL to 4546H, the SYS0/SYS routine that clears Register A (the key is ignored).
4DBE
LD (4479H),HL 22 79 44
Store Register Pair HL (4546H) at 4479H-447AH, the address of the JP at 4478H.
4DC1
LD A,C3H 3E C3
Load Register A with C3H (the JP opcode). LD does not change the flags.
4DC3
If the Z FLAG is set (N), JUMP to 4DC7H to store JP: BREAK is ignored.
4DC5
LD A,C9H 3E C9
Y: load Register A with C9H (the RET opcode): BREAK is a normal key.
4DC7
LD (4478H),A 32 78 44
Store Register A (C3H or C9H) at 4478H, the first byte of the BREAK hook.
4DCA
XOR A AF
Set Register A to 0 with XOR, which also sets the Z FLAG: no error.
4DCB
RET C9
RETurn to SYS1/SYS with Register A = 00H and the Z FLAG.

4DCCH - Read a Y/N Parameter

4DCCH reads the parameter and then requires the end of the command line; 4DD1H only reads it. On entry Register Pair HL points to the parameter (SYS1/SYS has already stepped past the comma or spaces after the command word). Result: the Z FLAG and Register A = 00H for N; the NZ FLAG for Y (Register A = 0BH) or when the line ends there (Register A = BFH). Anything else, or anything after the parameter when 4DCCH is used, goes to the SYS0/SYS error exit 4409H with error 34H.

4DCC
GOSUB to 4DD1H to read Y or N at Register Pair HL into the flags and Register B. 4D70H, 4D7BH, 4D88H, 4D95H, 4DA2H and 4DB8H call here.
4DCF
JUMP to 4DDDH to require the carriage return after the parameter.
4DD1
LD A,(HL) 7E
Fetch the character at Register Pair HL (the parameter) into Register A. 4DCCH calls here.
4DD2
SUB 4EH D6 4E
SUBtract 4EH (N) from Register A: Register A = 00H for N, 0BH for Y, BFH for a carriage return (0DH).
4DD4
LD B,A 47
Copy Register A (the result) into Register B, where 4DE3H takes it from. LD does not change the flags.
4DD5
INC HL 23
INCrement Register Pair HL past the character. INC HL does not change the flags.
4DD6
If the Z FLAG is set (N), JUMP to 4DE3H to return Register A = 00H and the Z FLAG.
4DD8
CP 0BH FE 0B
Compare Register A (the result) against 0BH, the result for Y.
4DDA
If the Z FLAG is set (Y), JUMP to 4DE3H to return Register A = 0BH and the NZ FLAG.
4DDC
DEC HL 2B
Neither: DECrement Register Pair HL back to the character, which must be the end of the line.
4DDD
GOSUB to 4C7AH (SYS0/SYS) to test the character at Register Pair HL: the Z FLAG only at a carriage return (0DH); otherwise the NZ FLAG with Register A = 34H. 4DCFH comes here.
4DE0
If the NZ FLAG is set (not the end of the line), JUMP to 4409H, the SYS0/SYS error exit, with Register A = 34H (ILLEGAL KEYWORD OR SEPARATOR OR TERMINATOR). It does not come back.
4DE3
LD A,B 78
Copy Register B (the result) into Register A: 00H for N, 0BH for Y, BFH when the line ended without a parameter. 4DD6H and 4DDAH come here.
4DE4
OR A B7
OR Register A with itself: the Z FLAG for N (00H), the NZ FLAG otherwise.
4DE5
RET C9
RETurn to the command routine with the answer in Register A and the flags.

4DE6H - Function 45H: KILL the File of an FCB (442CH)

Manual section 3.16. Register Pair DE points to an open FCB. The directory entries and granules of the file are released through the same code as CLOSE, with the mode byte at 4FB8H = 01H and a granule target of 0, so every granule is freed; finally the FCB is set to all zeroes (4FB5H).

4DE6
GOSUB to 4925H (SYS0/SYS): the FCB at Register Pair DE must be open (else error 26H); the rest runs as a DOS function with Index Register IX = the FCB, Index Register IY = 4280H and the Stack Pointer saved for the error exit 4972H. The caller's registers except AF are restored on return.
4DE9
GOSUB to 48F0H (SYS0/SYS) to select the drive of the FCB and read the directory sector of its FPDE (the DEC in FCB+7). Register Pair HL returns the FPDE in the buffer at 4300H, or the NZ FLAG with error 2DH if it is not an FPDE in use or its first extent differs from the FCB's.
4DEC
RET NZ C0
If the NZ FLAG is set (Register A = the error), RETurn from the DOS function with it.
4DED
LD B,02H 06 02
Load Register B with 02H: the access level must be below 2 for a kill (0 FULL or 1 KILL).
4DEF
GOSUB to 495EH (SYS0/SYS) to compare the access level of the FCB (FCB+1 bits 0-2) against Register B; a level of 2 or more leaves through 4972H with error 25H (ILLEGAL ACCESS TRIED TO PROTECTED FILE).
4DF2
JUMP to 4DF7H to set the KILL mode and the granule target of 0.

4DF4H - KILL Entry Used by PURGE

PURGE (5113H) comes here with Register Pair DE = 4480H (the DOS FCB, marked open, with the DEC at 4487H) and Register Pair HL = the FPDE in the directory buffer at 4300H. It skips the FPDE check and the access check of 4DE6H.

4DF4
GOSUB to 4925H (SYS0/SYS): the FCB at Register Pair DE (4480H) is marked open (80H), so the rest runs as a DOS function with Index Register IX = 4480H and Index Register IY = 4280H. Register Pair HL still points to the FPDE.
4DF7
LD BC,0000H 01 00 00
Load Register Pair BC with 0000H: the number of granules the file keeps (none). 4DF2H comes here.
4DFA
LD A,01H 3E 01
Load Register A with 01H, the mode byte that marks this run as a KILL.
4DFC
LD (4FB8H),A 32 B8 4F
Store Register A (01H) at 4FB8H, the operand of the LD A,nn at 4FB7H, which the routine reads to tell KILL (01H) from CLOSE (00H).
4DFF
PUSH BC C5
Save Register Pair BC (0000H) onto the stack as the granule target for 4E72H.
4E00
JUMP to 4E27H to count the granules of the file.

4E02H - Function 25H: CLOSE the FCB (4428H)

Manual section 3.15. Register Pair DE points to an open FCB. The buffer is written if it holds changed data, the EOF of the FCB is written into the FPDE, granules beyond the EOF are released unless the no deallocate bit of the file is set, and the FCB is turned back into a filespec (NAME/EXT:d).

4E02
GOSUB to 4925H (SYS0/SYS): the FCB at Register Pair DE must be open (else error 26H); the rest runs as a DOS function with Index Register IX = the FCB, Index Register IY = 4280H and the Stack Pointer saved for the error exit 4972H.
4E05
XOR A AF
Set Register A to 0 with XOR: the mode for CLOSE.
4E06
LD (4FB8H),A 32 B8 4F
Store Register A (00H) at 4FB8H, the operand of the LD A,nn at 4FB7H.
4E09
GOSUB to 4FEAH to write the buffer if FCB+1 bit 4 says it holds changed data, then write the EOF of the FCB into the FPDE (sub-function 7, 4FF5H). It returns the CARRY FLAG with Register Pair HL = the FPDE in 4300H and Register Pair DE = the EOF middle and high bytes as stored in the FPDE; the NO CARRY FLAG if the access level is 5 or more.
4E0C
If the NO CARRY FLAG is set (the file may not be written), JUMP to 4F5AH to put the filespec into the FCB and return. Nothing is deallocated.
4E0F
BIT 6,(IX+02H) DD CB 02 76
Test bit 6 of (IX+02H) (Index Register IX = the FCB), FCB+2: the no deallocate bit, a copy of byte 1 of the FPDE (manual 5.7 and 5.9).
4E13
If the NZ FLAG is set (no deallocate), JUMP to 4F5AH to put the filespec into the FCB and return. The excess granules stay with the file.
4E16
PUSH HL E5
Save Register Pair HL (the FPDE in the directory buffer) onto the stack.
4E17
LD A,(IX+08H) DD 7E 08
Fetch (IX+08H) (Index Register IX = the FCB), FCB+8, the EOF low byte, into Register A.

4E1AH - Granules Needed for the EOF

Also entered from SYS6/SYS (60BCH) with Register Pair HL = an FPDE in 4300H pushed onto the stack, Register A = its EOF low byte and Register Pair DE = its EOF middle and high bytes, to release the granules of a copied file beyond its EOF. Register Pair DE is the number of sectors the EOF reaches into (the FPDE form: the middle and high bytes are one higher when the low byte is not 0).

4E1A
OR A B7
OR Register A (the EOF low byte) with itself: the Z FLAG is set if it is 0.
4E1B
If the Z FLAG is set (the EOF low byte is 0), JUMP to 4E1EH.
4E1D
NOP 00
NOP. Self-Modifying Code
The SYS6/SYS patch list at 5BE1H puts INC DE (13H) here, and a NOP at 500FH, to match bit 5 of the PDRIVE flags at 4282H (SYS6/SYS 560DH); the file has the NOP.
4E1E
EX DE,HL EB
Exchange Register Pairs DE and HL: Register Pair HL = the number of sectors the EOF reaches into; Register Pair DE gets the old Register Pair HL, which is not used again.
4E1F
GOSUB to 4C57H (SYS0/SYS) to divide Register Pair HL by 5 (sectors per granule): Register Pair HL returns the whole granules, Register A the remainder.
4E22
OR A B7
OR Register A (the remainder) with itself: the Z FLAG is set if it is 0.
4E23
If the Z FLAG is set (no remainder), JUMP to 4E26H.
4E25
INC HL 23
Part of a granule is used: INCrement Register Pair HL (the granules needed) to count it.
4E26
EX (SP),HL E3
Exchange Register Pair HL with the word at the top of the stack (Stack Pointer): the stack now holds the granules the file needs, Register Pair HL = the FPDE saved at 4E16H.
4E27
LD D,(IX+07H) DD 56 07
Load Register D with (IX+07H) (Index Register IX = the FCB), FCB+7, the DEC of the FPDE. KILL (4E00H) comes here with 0000H on the stack.
4E2A
LD BC,0000H 01 00 00
Load Register Pair BC with 0000H: the count of granules the file owns.
4E2D
LD E,10H 1E 10
Load Register E with 10H: bits 7 and 4 of the first entry must be 0 and 1, an FPDE in use.

4E2FH - Count the Granules of the File

Register Pair HL points to a directory entry in the buffer at 4300H, Register D holds its DEC and Register E the value bits 7 and 4 of its first byte must have. The extent elements at bytes 22-31 are added into Register Pair BC. An element with lump FEH links to an FXDE (the DEC in its second byte) and must be the fifth one; FFH ends the list. The offset of the last entry examined is kept at 4EA1H.

Entry Loop
Register Pair HL = the entry, Register D = its DEC, Register E = the flag value it must have.

4E2F
LD A,(HL) 7E
Fetch byte 0 of the entry at Register Pair HL (the flags) into Register A.
4E30
AND 90H E6 90
AND Register A with 90H, keeping bit 7 (FXDE) and bit 4 (in use).
4E32
CP E BB
Compare Register A against Register E (10H for the FPDE, 90H for an FXDE).
4E33
If the NZ FLAG is set (the wrong kind of entry), JUMP to 4E52H to leave with error 2CH (BAD DIRECTORY DATA).
4E35
LD A,L 7D
Copy Register L (the offset of the entry in the buffer) into Register A.
4E36
LD (4EA1H),A 32 A1 4E
Store Register A (the offset) at 4EA1H, the operand of the LD E,nn at 4EA0H: the entry the freeing loop starts from.
4E39
ADD A,16H C6 16
ADD 16H to Register A: the offset of byte 22, the first extent element of the entry.
4E3B
LD L,A 6F
Copy Register A into Register L: Register Pair HL = the first extent element.
4E3C
PUSH DE D5
Save Register Pair DE onto the stack: Register D = the DEC of this entry, Register E = the flag value.
4E3D
LD E,05H 1E 05
Load Register E with 05H: five two-byte elements per entry (bytes 22-31). SYS6/SYS changes the operand at 4E3EH from its patch list at 5BE1H, to match bit 5 of the PDRIVE flags at 4282H (SYS6/SYS 560DH).

Element Loop
Register Pair HL = the element, Register E = the elements left in the entry, Register Pair BC = the granules counted.

4E3F
LD A,(HL) 7E
Fetch the first byte of the element at Register Pair HL (the lump number, or FEH or FFH) into Register A.
4E40
CP FEH FE FE
Compare Register A against FEH: the NO CARRY FLAG for FEH (a link to an FXDE) and FFH (the end of the list).
4E42
INC HL 23
INCrement Register Pair HL to the second byte of the element (bits 5-7 the first granule within the lump, bits 0-4 the number of granules minus 1). INC HL does not change the flags.
4E43
If the NO CARRY FLAG is set (FEH or FFH), JUMP to 4E64H.
4E45
LD A,(HL) 7E
Fetch the second byte of the element at Register Pair HL into Register A.
4E46
AND 1FH E6 1F
AND Register A with 1FH, keeping bits 0-4: the number of granules in the element minus 1.
4E48
INC HL 23
INCrement Register Pair HL to the next element.
4E49
INC A 3C
INCrement Register A: the number of granules in this element. SYS6/SYS changes this byte from its patch list at 5BE1H, to match bit 5 of the PDRIVE flags at 4282H (SYS6/SYS 560DH).
4E4A
ADD A,C 81
ADD Register C (the low byte of the count) to Register A.
4E4B
LD C,A 4F
Copy Register A into Register C, the new low byte of the count.
4E4C
If the NO CARRY FLAG is set (no carry out of the low byte), JUMP to 4E4FH.
4E4E
INC B 04
INCrement Register B, the high byte of the count: Register Pair BC = the granules counted so far.
4E4F
DEC E 1D
DECrement Register E (the elements left in this entry). SYS6/SYS changes 4E4FH-4E51H from its patch list at 5BE1H, to match bit 5 of the PDRIVE flags at 4282H (SYS6/SYS 560DH).
4E50
If the NZ FLAG is set (more elements), LOOP BACK to 4E3FH. After the fifth element the code goes on into 4E52H: five elements without an FEH or FFH are bad data.

4E52H - Error Exit and Directory Helpers

4E52H: five elements without an end mark, or the wrong kind of entry, is error 2CH. 4E59H: read the directory entry whose DEC is in Register D. 4E5FH: write the directory sector in 4300H. Both helpers return only on success (the Z FLAG); an error leaves the DOS function through 4972H.

4E52
LD A,2CH 3E 2C
Load Register A with error code 2CH (BAD DIRECTORY DATA). 4E33H and 4E68H come here.
4E54
OR A B7
OR Register A (2CH) with itself to set the NZ FLAG.
4E55
RET Z C8
If the Z FLAG is set (no error), RETurn to the caller. 4E5DH and 4E62H come here with the result of a SYS0/SYS call.
4E56
JUMP to 4972H, the SYS0/SYS error exit, with the error code in Register A: it restores the Stack Pointer saved by 4925H and returns from the DOS function. 4FF8H comes here too.
4E59
LD A,D 7A
Copy Register D (the DEC of the entry wanted) into Register A. 4E6DH and 4EB0H call here.
4E5A
GOSUB to 48D4H (SYS0/SYS) to read the directory sector holding the DEC in Register A into 4300H unless it is already there. Register Pair HL returns the entry, Register A its offset in the buffer; the NZ FLAG on a read error. 4F64H calls here with the DEC in Register A.
4E5D
JUMP to 4E55H to RETurn, or to leave with the error in Register A.
4E5F
GOSUB to 48C4H (SYS0/SYS) to write the directory sector in 4300H back (its number is at 48D5H) with the directory mark and a verify read; the NZ FLAG with error 12H on failure. 4F0AH, 4F2FH, 4F57H, 4FCBH and 5027H call here.
4E62
JUMP to 4E55H to RETurn, or to leave with the error in Register A.

4E64H - End of the Extents of an Entry

Register A holds FEH or FFH from 4E3FH and Register Pair HL points to the second byte of the element.

4E64
If the NZ FLAG is set (Register A = FFH, the end of the list), JUMP to 4E72H.
4E66
DEC E 1D
FEH: DECrement Register E (the elements left): it becomes 0 only when this is the fifth element.
4E67
POP DE D1
Restore Register Pair DE from the stack: Register D = the DEC of the entry just finished, Register E = its flag value. POP does not change the flags.
4E68
If the NZ FLAG is set (FEH before the fifth element), JUMP to 4E52H to leave with error 2CH. SYS6/SYS changes this byte from its patch list at 5BE1H, to match bit 5 of the PDRIVE flags at 4282H (SYS6/SYS 560DH).
4E6A
LD D,(HL) 56
Fetch the second byte of the element at Register Pair HL into Register D: the DEC of the FXDE that continues the list.
4E6B
LD E,90H 1E 90
Load Register E with 90H: the FXDE must be in use (bit 4) and marked as an FXDE (bit 7).
4E6D
GOSUB to 4E59H to read the entry whose DEC is in Register D. Register Pair HL returns the FXDE, Register A its offset.
4E70
LOOP BACK to 4E2FH to count the elements of the FXDE.

4E72H - Prepare the Work Maps

All the entries of the file have been counted: Register Pair BC = the granules the file owns, the stack holds the granules it keeps (what the EOF needs for CLOSE, 0 for KILL) and Register Pair HL points to the second byte of the element with the FFH end mark. From here SYS3/SYS uses its own first 224 bytes as work space: 4D00H-4DBFH becomes a mask for the allocation bytes of the GAT (FFH = keep, a 0 bit = a granule to free) and 4DC0H-4DDFH a 256-bit map of DECs to clear in the HIT. Since that overwrites the code of SYS3/SYS, the overlay number at 4288H is cleared so that the next RST 28H for SYS3/SYS reloads it.

4E72
POP DE D1
Restore Register Pair DE from the stack: Register D = the DEC of the last entry of the file, Register E = its flag value.
4E73
EX (SP),HL E3
Exchange Register Pair HL with the word at the top of the stack (Stack Pointer): the stack now holds the end mark pointer, Register Pair HL = the granules the file keeps.
4E74
XOR A AF
Set Register A to 0 with XOR, which also clears the CARRY FLAG.
4E75
SBC HL,BC ED 42
SUBtract Register Pair BC (the granules the file owns) from Register Pair HL: Register Pair HL = keep minus own. A negative value is minus the number of excess granules; KILL gives minus all of them.
4E77
LD (4288H),A 32 88 42
Store Register A (00H) at 4288H, the number of the overlay in 4D00H-51FFH: none, since the code at 4D00H is about to be overwritten.
4E7A
PUSH HL E5
Save Register Pair HL (the difference) onto the stack.
4E7B
LD HL,4D00H 21 00 4D
Point Register Pair HL to 4D00H, the start of the GAT mask.
4E7E
LD B,C0H 06 C0
Load Register B with C0H: 192 bytes, one per allocation byte of the GAT (GAT bytes 00H-BFH).

Mask Fill Loop

4E80
LD (HL),FFH 36 FF
Store FFH (keep every granule) at Register Pair HL.
4E82
INC HL 23
INCrement Register Pair HL to the next byte of the mask.
4E83
DECrement Register B and LOOP BACK to 4E80H until 4D00H-4DBFH is all FFH.
4E85
LD B,20H 06 20
Load Register B with 20H: the 32 bytes of the HIT map (4DC0H-4DDFH).

Map Clear Loop

4E87
LD (HL),A 77
Store Register A (00H, no DEC to clear) at Register Pair HL.
4E88
INC HL 23
INCrement Register Pair HL to the next byte of the map.
4E89
DECrement Register B and LOOP BACK to 4E87H until 4DC0H-4DDFH is all 00H.
4E8B
POP BC C1
Restore Register Pair BC from the stack: keep minus own, saved at 4E7AH.
4E8C
POP HL E1
Restore Register Pair HL from the stack: the second byte of the element with the end mark.
4E8D
LD A,(4FB8H) 3A B8 4F
Fetch the mode from 4FB8H (00H CLOSE, 01H KILL) into Register A.
4E90
OR A B7
OR Register A (the mode) with itself to set the flags.
4E91
If the NZ FLAG is set (KILL), JUMP to 4E9EH to free every granule.
4E93
LD A,B 78
CLOSE: copy Register B (the high byte of the difference) into Register A.
4E94
CP 80H FE 80
Compare Register A against 80H: the NO CARRY FLAG when the difference is negative (the file owns more granules than its EOF needs), the CARRY FLAG and the NZ FLAG otherwise.
4E96
PUSH HL E5
Save Register Pair HL (the end mark pointer) onto the stack.
4E97
If the NO CARRY FLAG is set (excess granules), GOSUB to 48C4H (SYS0/SYS) to write the directory sector now in 4300H (the one holding the last entry of the file, unchanged since it was read) back to the diskette. A write error returns the NZ FLAG with Register A = 12H.
4E9A
POP HL E1
Restore Register Pair HL from the stack: the end mark pointer.
4E9B
If the NZ FLAG is set (no excess granules, from 4E94H, or the write failed, from 48C4H), JUMP to 4F0DH to put the filespec into the FCB and finish without freeing anything. The error code of a failed write is not returned.

4E9EH - Free Granules from the End of the File

Register Pair BC counts up from minus the number of granules to free. Working backwards from the last extent element, each granule is marked in the GAT mask at 4D00H (lump offset, granule bit). CLOSE shortens the element, and an element or an FXDE that becomes empty is replaced by an end mark (FFH FFH). KILL leaves the elements as they are, since the whole entry is released. An FXDE that has been emptied (CLOSE) or passed (KILL) is freed through 4FC2H and the walk goes on in the entry its back link names. The offset of the entry being worked on is at 4EA1H; its DEC is in Register D.

Element Walk Loop
Register Pair HL = the second byte of the element after the one to look at.

4E9E
DEC L 2D
DECrement Register L (the low byte of the element pointer) by one. 4E91H and 4EBDH come here.
4E9F
DEC L 2D
DECrement Register L again: Register Pair HL = the second byte of the previous element. 4EC3H comes here.
4EA0
LD E,00H 1E 00
Load Register E with the offset of the entry being worked on. Self-Modifying Code
The operand at 4EA1H is stored at 4E36H and 4EB3H.
4EA2
LD A,E 7B
Copy Register E (the offset of the entry) into Register A.
4EA3
ADD A,16H C6 16
ADD 16H to Register A: the offset of byte 22, the first byte of the first element of the entry.
4EA5
CP L BD
Compare Register A against Register L (where Register Pair HL points): the CARRY FLAG is set while Register Pair HL is past byte 22, in one of the elements of this entry.
4EA6
If the CARRY FLAG is set (Register Pair HL is in an element of this entry), JUMP to 4EC5H to free granules from it.
4EA8
LD L,E 6B
Every element of this entry is done: copy Register E (its offset) into Register L, so that Register Pair HL points to byte 0 of the entry.
4EA9
BIT 7,(HL) CB 7E
Test bit 7 of byte 0 of the entry at Register Pair HL: 1 for an FXDE.
4EAB
If the Z FLAG is set (the FPDE, the first entry of the file), JUMP to 4EFFH to finish. SYS6/SYS changes this byte from its patch list at 5BE1H, to match bit 5 of the PDRIVE flags at 4282H (SYS6/SYS 560DH).
4EAD
An FXDE: GOSUB to 4FC2H to free it (clear bit 4, write the sector, mark its DEC in the HIT map). Register D returns the DEC of the entry before it (the back link of the FXDE).
4EB0
GOSUB to 4E59H to read the entry whose DEC is in Register D. Register Pair HL returns the entry, Register A its offset.
4EB3
LD (4EA1H),A 32 A1 4E
Store Register A (the offset of the entry) at 4EA1H: this entry is now the one being worked on.
4EB6
ADD A,1FH C6 1F
ADD 1FH to Register A: the offset of byte 31, the second byte of the fifth element, the one that linked to the freed FXDE. SYS6/SYS changes the operand at 4EB7H from its patch list at 5BE1H, to match bit 5 of the PDRIVE flags at 4282H (SYS6/SYS 560DH).
4EB8
LD L,A 6F
Copy Register A into Register L: Register Pair HL = byte 31 of the entry.
4EB9
LD A,(4FB8H) 3A B8 4F
Fetch the mode from 4FB8H (00H CLOSE, 01H KILL) into Register A. 4EFDH comes here.
4EBC
OR A B7
OR Register A (the mode) with itself to set the flags.
4EBD
If the NZ FLAG is set (KILL), LOOP BACK to 4E9EH to go back one element and leave this one as it is.
4EBF
DEC A 3D
CLOSE: DECrement Register A (the mode) from 00H to FFH, the end mark value.
4EC0
LD (HL),A 77
Store Register A (FFH) at Register Pair HL, the second byte of the element.
4EC1
DEC HL 2B
DECrement Register Pair HL to the first byte of the element.
4EC2
LD (HL),A 77
Store Register A (FFH) at Register Pair HL: the element is now the end mark of the list.
4EC3
LOOP BACK to 4E9FH to go back to the second byte of the previous element.
4EC5
LD E,(HL) 5E
Fetch the second byte of the element at Register Pair HL into Register E, kept to put back if the element becomes empty. 4EA6H comes here.

Granule Free Loop
Register Pair HL = the second byte of the element, Register Pair BC = minus the granules still to free.

4EC6
LD A,B 78
Copy Register B (the high byte of the count) into Register A. 4EFAH comes here.
4EC7
OR C B1
OR Register C (the low byte of the count) into Register A: the Z FLAG when Register Pair BC has counted up to 0.
4EC8
If the Z FLAG is set (all the excess granules are freed), JUMP to 4EFFH to finish.
4ECA
PUSH DE D5
Save Register Pair DE onto the stack: Register D = the DEC of the entry, Register E = the original second byte of the element.
4ECB
PUSH HL E5
Save Register Pair HL (the second byte of the element) onto the stack.
4ECC
LD D,(HL) 56
Fetch the current second byte of the element at Register Pair HL into Register D. SYS6/SYS changes 4ECCH-4ECDH from its patch list at 5BE1H, to match bit 5 of the PDRIVE flags at 4282H (SYS6/SYS 560DH).
4ECD
DEC (HL) 35
DECrement the second byte of the element at Register Pair HL: the element loses its last granule.
4ECE
DEC HL 2B
DECrement Register Pair HL to the first byte of the element.
4ECF
LD E,(HL) 5E
Fetch the lump number of the element at Register Pair HL into Register E.
4ED0
DEC E 1D
DECrement Register E (the lump number), because the loop at 4EE1H adds 1 before it subtracts.
4ED1
PUSH BC C5
Save Register Pair BC (the count) onto the stack.
4ED2
LD A,D 7A
Copy Register D (the second byte of the element) into Register A.
4ED3
AND 1FH E6 1F
AND Register A with 1FH, keeping bits 0-4: the number of granules minus 1, the position of the last granule of the element counted from its first.
4ED5
LD B,A 47
Copy Register A (the position) into Register B. LD does not change the flags.
4ED6
PUSH AF F5
Save Register Pair AF onto the stack: the Z FLAG from 4ED3H is set if the element had only one granule left.
4ED7
LD A,D 7A
Copy Register D (the second byte of the element) into Register A again.
4ED8
RLCA 07
Rotate Register A left (the first of three RLCAs).
4ED9
RLCA 07
Rotate Register A left (the second of three RLCAs).
4EDA
RLCA 07
Rotate Register A left (the third): bits 5-7 (the first granule of the element within its lump) are now bits 0-2.
4EDB
AND 07H E6 07
AND Register A with 07H, keeping bits 0-2: the first granule within the lump.
4EDD
ADD A,B 80
ADD Register B (the position of the last granule) to Register A: the last granule counted from the start of the first lump of the element.
4EDE
LD B,(IY+05H) FD 46 05
Load Register B with (IY+05H): Index Register IY = 4280H, so this is 4285H, the granules per lump of the selected drive.

Lump Loop

4EE1
INC E 1C
INCrement Register E (the lump number) to the next lump of the element.
4EE2
SUB B 90
SUBtract Register B (the granules per lump) from Register A (the granule number).
4EE3
If the NO CARRY FLAG is set (no borrow), LOOP BACK to 4EE1H. Register E ends as the lump that holds the granule.
4EE5
ADD A,B 80
ADD Register B (the granules per lump) back to Register A: the granule within that lump.
4EE6
LD B,A 47
Copy Register A (the granule within the lump) into Register B.
4EE7
LD A,7FH 3E 7F
Load Register A with 7FH, a mask with only bit 7 clear.
4EE9
LD D,00H 16 00
Load Register D with 00H: Register Pair DE = the lump number.
4EEB
INC B 04
INCrement Register B: the number of left rotations needed.

Rotate Loop

4EEC
RLCA 07
Rotate Register A (the mask) left one bit, bit 7 going into bit 0.
4EED
DECrement Register B and LOOP BACK to 4EECH until it is 0. The clear bit of Register A ends up at the position of the granule.
4EEF
LD HL,4D00H 21 00 4D
Point Register Pair HL to 4D00H, the GAT mask.
4EF2
ADD HL,DE 19
ADD Register Pair DE (the lump number) to Register Pair HL: the mask byte for this lump.
4EF3
AND (HL) A6
AND Register A (the new mask) with the mask byte at Register Pair HL.
4EF4
LD (HL),A 77
Store Register A at Register Pair HL: the granule is marked to be freed.
4EF5
POP AF F1
Restore Register Pair AF from the stack (saved at 4ED6H): the Z FLAG if the element has no granules left.
4EF6
POP BC C1
Restore Register Pair BC from the stack: the count.
4EF7
POP HL E1
Restore Register Pair HL from the stack: the second byte of the element.
4EF8
POP DE D1
Restore Register Pair DE from the stack: Register D = the DEC of the entry, Register E = the original second byte of the element.
4EF9
INC BC 03
INCrement Register Pair BC (the count) towards 0: one granule freed. INC BC does not change the flags.
4EFA
If the NZ FLAG is set (the element still has granules), LOOP BACK to 4EC6H.
4EFC
LD (HL),E 73
The element is empty. Store Register E (its original second byte) back at Register Pair HL, since the DEC at 4ECDH ran through bits 0-4 into bits 5-7.
4EFD
JUMP to 4EB9H: CLOSE makes the element the end mark, KILL leaves it.

4EFFH - Write the Last Entry and Finish

Reached when the excess granules are freed (4EC8H) or when the walk has come back to the FPDE (4EABH). Register Pair HL points into the entry whose offset is at 4EA1H.

4EFF
LD A,(4FB8H) 3A B8 4F
Fetch the mode from 4FB8H (00H CLOSE, 01H KILL) into Register A.
4F02
OR A B7
OR Register A (the mode) with itself to set the flags.
4F03
If the Z FLAG is set (CLOSE), JUMP to 4F0AH to write the shortened entry.
4F05
KILL: GOSUB to 4FC2H to free the FPDE itself (clear bit 4, write the sector, mark its DEC in the HIT map).
4F08
JUMP to 4F0DH to clear the FCB and apply the GAT mask and HIT map.
4F0A
CLOSE: GOSUB to 4E5FH to write the directory sector in 4300H with the shortened entry.
4F0D
GOSUB to 4F5AH to put the filespec into the FCB (CLOSE) or clear the FCB (KILL). 4E9BH comes here.

4F10H - Apply the GAT Mask

If any byte of the mask at 4D00H-4DBFH is not FFH, the GAT (directory sector 0) is read, each of its allocation bytes 00H-BFH is ANDed with the mask (a cleared bit frees that granule) and the GAT is written back.

4F10
LD HL,4D00H 21 00 4D
Point Register Pair HL to 4D00H, the GAT mask.
4F13
LD B,C0H 06 C0
Load Register B with C0H: the 192 bytes of the mask.

Mask Test Loop

4F15
LD A,(HL) 7E
Fetch the mask byte at Register Pair HL into Register A.
4F16
INC A 3C
INCrement Register A: the Z FLAG is set only for FFH (nothing to free in this lump).
4F17
INC HL 23
INCrement Register Pair HL to the next mask byte. INC HL does not change the flags.
4F18
If the NZ FLAG is set (a granule to free), JUMP to 4F1EH.
4F1A
DECrement Register B and LOOP BACK to 4F15H until all 192 mask bytes have been tested.
4F1C
The whole mask is FFH: JUMP to 4F33H to look at the HIT map, with Register Pair HL = 4DC0H.
4F1E
XOR A AF
Set Register A to 0 with XOR: directory sector 0, the GAT.
4F1F
GOSUB to 48AFH (SYS0/SYS) to read the GAT into the buffer at 4300H. Register Pair HL returns 4300H; the NZ FLAG and the error code in Register A on a read error.
4F22
RET NZ C0
If the NZ FLAG is set (a read error), RETurn from the DOS function with the error code in Register A.
4F23
LD DE,4D00H 11 00 4D
Point Register Pair DE to 4D00H, the GAT mask.
4F26
LD B,C0H 06 C0
Load Register B with C0H: the 192 allocation bytes of the GAT.

Mask Apply Loop
Register Pair DE = the mask byte, Register Pair HL = the GAT byte.

4F28
LD A,(DE) 1A
Fetch the mask byte at Register Pair DE into Register A.
4F29
AND (HL) A6
AND Register A (the mask byte) with the GAT byte at Register Pair HL.
4F2A
LD (HL),A 77
Store Register A at Register Pair HL in the GAT: the marked granules are now free.
4F2B
INC DE 13
INCrement Register Pair DE to the next mask byte.
4F2C
INC HL 23
INCrement Register Pair HL to the next GAT byte.
4F2D
DECrement Register B and LOOP BACK to 4F28H until all 192 bytes are done.
4F2F
GOSUB to 4E5FH to write the GAT in 4300H back (48D5H still holds sector 0).
4F32
EX DE,HL EB
Exchange Register Pairs DE and HL: Register Pair HL = 4DC0H, where Register Pair DE has run to past the mask, the start of the HIT map.

4F33H - Apply the HIT Map

If any bit of the map at 4DC0H-4DDFH is set, the HIT (directory sector 1) is read and the byte of every marked DEC is set to 00H (entry free), then the HIT is written back. The map holds one bit per DEC, bit 7 of 4DC0H for DEC 00H.

4F33
LD B,20H 06 20
Load Register B with 20H: the 32 map bytes. Register Pair HL = 4DC0H. 4F1CH comes here.

Map Test Loop

4F35
LD A,(HL) 7E
Fetch the map byte at Register Pair HL into Register A.
4F36
OR A B7
OR Register A (the map byte) with itself: the Z FLAG is set if no DEC is marked in it.
4F37
INC HL 23
INCrement Register Pair HL to the next map byte. INC HL does not change the flags.
4F38
If the NZ FLAG is set (a DEC is marked), JUMP to 4F3DH.
4F3A
DECrement Register B and LOOP BACK to 4F35H until all 32 map bytes have been tested.
4F3C
RET C9
Nothing is marked: RETurn from the DOS function with Register A = 00H and the Z FLAG (no error).
4F3D
LD A,01H 3E 01
Load Register A with 01H, directory sector 1, the HIT.
4F3F
GOSUB to 48AFH (SYS0/SYS) to read the HIT into 4300H. Register Pair HL returns 4300H, the HIT byte of DEC 00H; the NZ FLAG and the error code on a read error.
4F42
RET NZ C0
If the NZ FLAG is set (a read error), RETurn from the DOS function with the error code in Register A.
4F43
LD DE,4DBFH 11 BF 4D
Point Register Pair DE to 4DBFH, one byte before the map, because the loop moves on first.
4F46
LD C,20H 0E 20
Load Register C with 20H: the 32 map bytes, 256 DECs in all.

Map Byte Loop
Register Pair HL = the HIT byte of the next DEC, Register C = the map bytes left.

4F48
INC DE 13
INCrement Register Pair DE to the next map byte.
4F49
LD B,08H 06 08
Load Register B with 08H: eight bits per map byte.
4F4B
LD A,(DE) 1A
Fetch the map byte at Register Pair DE into Register A.

Map Bit Loop

4F4C
RLCA 07
Rotate Register A (the map byte) left: the next bit, from bit 7 down, goes into the CARRY FLAG.
4F4D
If the NO CARRY FLAG is set (this DEC is not marked), JUMP to 4F51H.
4F4F
LD (HL),00H 36 00
Marked: store 00H at Register Pair HL, the HIT byte of this DEC: the entry is free.
4F51
INC HL 23
INCrement Register Pair HL to the HIT byte of the next DEC.
4F52
DECrement Register B (the bits left) and LOOP BACK to 4F4CH until all 8 are done.
4F54
DEC C 0D
DECrement Register C (the map bytes left), setting the Z FLAG at 0.
4F55
If the NZ FLAG is set (more map bytes), LOOP BACK to 4F48H.
4F57
JUMP to 4E5FH to write the HIT back; it RETurns from the DOS function with the Z FLAG, or leaves with the error.

4F5AH - Put the Filespec into the FCB

Manual sections 3.15 and 3.16. The FCB (Index Register IX) is rewritten as NAME/EXT:d with 03H at the end, from the entry whose DEC is in FCB+7 and the drive number at 427EH; for KILL (mode 01H at 4FB8H) its 32 bytes are then cleared. Skipped when bit 2 of 428BH is set (SYS6/SYS sets it while it uses this code during COPY). Reached as a subroutine from 4F0DH, or with JP from CLOSE (4E0CH, 4E13H), in which case its RET ends the DOS function.

4F5A
LD A,(428BH) 3A 8B 42
Fetch the temporary flags at 428BH into Register A. 4E0CH, 4E13H and 4F0DH come here.
4F5D
BIT 2,A CB 57
Test bit 2 of Register A (428BH), set by SYS6/SYS while it uses this code.
4F5F
If the NZ FLAG is set (SYS6/SYS), JUMP to 4FC0H to RETurn with Register A = 00H.
4F61
LD A,(IX+07H) DD 7E 07
Fetch (IX+07H) (Index Register IX = the FCB), FCB+7, the DEC of the FPDE, into Register A.
4F64
GOSUB to 4E5AH with Register A = the DEC to read its directory sector if needed. Register Pair HL returns the entry.
4F67
EX DE,HL EB
Exchange Register Pairs DE and HL: Register Pair DE = the entry (its name starts at byte 5).
4F68
PUSH IX DD E5
Save Index Register IX (the FCB address) onto the stack.
4F6A
POP HL E1
Restore it into Register Pair HL: Register Pair HL = the FCB, where the text is written.
4F6B
LD B,08H 06 08
Load Register B with 08H: up to eight name characters.
4F6D
PUSH HL E5
Save Register Pair HL (the FCB address) onto the stack for 4FB4H.
4F6E
INC DE 13
INCrement Register Pair DE to byte 1 of the entry.
4F6F
INC DE 13
INCrement Register Pair DE to byte 2 of the entry.
4F70
INC DE 13
INCrement Register Pair DE to byte 3 of the entry.
4F71
INC DE 13
INCrement Register Pair DE to byte 4 of the entry, one before the name.

Name Loop
Register Pair DE = the entry, Register Pair HL = the FCB, Register B = the characters left.

4F72
INC DE 13
INCrement Register Pair DE to the next name character (bytes 5-12 of the entry).
4F73
LD A,(DE) 1A
Fetch the name character at Register Pair DE into Register A.
4F74
CP 20H FE 20
Compare Register A against 20H (a space), the padding of the name.
4F76
If the Z FLAG is set (a space), JUMP to 4F7AH without copying it.
4F78
LD (HL),A 77
Store Register A (the character) at Register Pair HL in the FCB.
4F79
INC HL 23
INCrement Register Pair HL to the next place in the FCB.
4F7A
DECrement Register B and LOOP BACK to 4F72H until all 8 name characters have been looked at.
4F7C
INC DE 13
INCrement Register Pair DE to byte 13 of the entry, the first extension character.
4F7D
LD A,(DE) 1A
Fetch the first extension character at Register Pair DE into Register A.
4F7E
CP 20H FE 20
Compare Register A (the first extension character) against 20H (a space).
4F80
If the Z FLAG is set (no extension), JUMP to 4F91H.
4F82
LD (HL),2FH 36 2F
Store 2FH (/) at Register Pair HL in the FCB, before the extension.
4F84
LD B,03H 06 03
Load Register B with 03H: up to three extension characters.
4F86
INC HL 23
INCrement Register Pair HL past the / in the FCB.

Extension Loop

4F87
LD A,(DE) 1A
Fetch the extension character at Register Pair DE into Register A.
4F88
CP 20H FE 20
Compare Register A (the extension character) against 20H (a space).
4F8A
INC DE 13
INCrement Register Pair DE to the next extension character. INC DE does not change the flags.
4F8B
If the Z FLAG is set (a space ends the extension), JUMP to 4F91H.
4F8D
LD (HL),A 77
Store Register A (the character) at Register Pair HL in the FCB.
4F8E
INC HL 23
INCrement Register Pair HL to the next place in the FCB.
4F8F
DECrement Register B and LOOP BACK to 4F87H until all 3 extension characters have been looked at.
4F91
LD (HL),3AH 36 3A
Store 3AH (:) at Register Pair HL in the FCB, after the name and extension. 4F80H and 4F8BH come here.
4F93
INC HL 23
INCrement Register Pair HL to the next place in the FCB.
4F94
LD A,(427EH) 3A 7E 42
Fetch the current drive number from 427EH (the drive of the file, selected by 48F0H or 48D4H) into Register A.
4F97
LD BC,0264H 01 64 02
Load Register Pair BC with 0264H: Register B = 02H (two digit places to work out), Register C = 64H (100, the first power of ten).

Digit Loop
Register A = the rest of the drive number, Register C = the power of ten.

4F9A
LD D,2FH 16 2F
Load Register D with 2FH, one below the character 0.
4F9C
INC D 14
INCrement Register D, the digit character, once for each subtraction.
4F9D
SUB C 91
SUBtract Register C (the power of ten) from Register A.
4F9E
If the NO CARRY FLAG is set (no borrow), LOOP BACK to 4F9CH.
4FA0
ADD A,C 81
ADD Register C (the power of ten) back to Register A: the remainder.
4FA1
LD E,A 5F
Copy Register A (the remainder) into Register E.
4FA2
LD A,D 7A
Copy Register D (the digit character) into Register A.
4FA3
CP 30H FE 30
Compare Register A against 30H (the character 0).
4FA5
If the Z FLAG is set (a 0 in the hundreds or tens place), JUMP to 4FA9H without storing it.
4FA7
LD (HL),A 77
Store Register A (the digit) at Register Pair HL in the FCB.
4FA8
INC HL 23
INCrement Register Pair HL to the next place in the FCB.
4FA9
LD A,E 7B
Copy Register E (the remainder) back into Register A.
4FAA
LD C,0AH 0E 0A
Load Register C with 0AH (10), the power of ten for the next place.
4FAC
DECrement Register B (the places left) and LOOP BACK to 4F9AH until it is 0.
4FAE
ADD A,30H C6 30
ADD 30H to Register A (the last remainder): the units digit as a character.
4FB0
LD (HL),A 77
Store Register A (the units digit) at Register Pair HL in the FCB.
4FB1
INC HL 23
INCrement Register Pair HL to the next place in the FCB.
4FB2
LD (HL),03H 36 03
Store 03H at Register Pair HL, the end of the filespec.
4FB4
POP HL E1
Restore Register Pair HL from the stack: the FCB address saved at 4F6DH.
4FB5
LD B,20H 06 20
Load Register B with 20H: the 32 bytes of the FCB.
4FB7
LD A,00H 3E 00
Load Register A with the mode: 00H CLOSE, 01H KILL. Self-Modifying Code
The operand at 4FB8H is stored at 4DFCH and 4E06H, and by SYS6/SYS at 60ADH.
4FB9
DEC A 3D
DECrement Register A (the mode): 00H for KILL, FFH for CLOSE.
4FBA
If the NZ FLAG is set (CLOSE), JUMP to 4FC0H to RETurn, keeping the filespec in the FCB.

FCB Clear Loop

4FBC
LD (HL),A 77
KILL: store Register A (00H) at Register Pair HL in the FCB.
4FBD
INC HL 23
INCrement Register Pair HL to the next byte of the FCB.
4FBE
DECrement Register B and LOOP BACK to 4FBCH until all 32 bytes of the FCB are 00H.
4FC0
XOR A AF
Set Register A to 0 with XOR, which also sets the Z FLAG: no error. 4F5FH, 4FBAH and 5033H come here.
4FC1
RET C9
RETurn to the caller with Register A = 00H and the Z FLAG: no error.

4FC2H - Free a Directory Entry

Frees the entry at the offset stored at 4EA1H (in the buffer at 4300H), whose DEC is in Register D: bit 4 of its first byte is cleared (manual 5.7: the rest of the entry is left as it was), the sector is written, and the DEC is marked in the HIT map at 4DC0H. Returns Register D = the second byte of the entry, which for an FXDE is the DEC of the entry before it.

4FC2
LD A,(4EA1H) 3A A1 4E
Fetch the offset of the entry from 4EA1H (the operand of the LD E,nn at 4EA0H) into Register A. 4EADH and 4F05H call here.
4FC5
LD L,A 6F
Copy Register A (the offset) into Register L: Register Pair HL = the entry (Register H is 43H, the buffer at 4300H).
4FC6
RES 4,(HL) CB A6
Reset bit 4 of byte 0 of the entry at Register Pair HL: the entry is no longer in use.
4FC8
LD E,D 5A
Copy Register D (the DEC of the entry) into Register E.
4FC9
INC HL 23
INCrement Register Pair HL to byte 1 of the entry.
4FCA
LD D,(HL) 56
Fetch byte 1 of the entry at Register Pair HL into Register D: the back link of an FXDE.
4FCB
GOSUB to 4E5FH to write the directory sector in 4300H back.
4FCE
LD A,E 7B
Copy Register E (the DEC of the freed entry) into Register A.
4FCF
RRCA 0F
Rotate Register A right (the first of three RRCAs).
4FD0
RRCA 0F
Rotate Register A right (the second of three RRCAs).
4FD1
RRCA 0F
Rotate Register A right (the third): bits 3-7 of the DEC are now bits 0-4.
4FD2
AND 1FH E6 1F
AND Register A with 1FH, keeping bits 0-4: the map byte number (the DEC divided by 8).
4FD4
PUSH BC C5
Save Register Pair BC (the count of the freeing loop) onto the stack.
4FD5
LD HL,4DC0H 21 C0 4D
Point Register Pair HL to 4DC0H, the HIT map.
4FD8
LD C,A 4F
Copy Register A (the map byte number) into Register C.
4FD9
LD B,00H 06 00
Load Register B with 00H: Register Pair BC = the map byte number.
4FDB
ADD HL,BC 09
ADD Register Pair BC to Register Pair HL: Register Pair HL = the map byte for this DEC.
4FDC
LD A,E 7B
Copy Register E (the DEC of the freed entry) into Register A again.
4FDD
AND 07H E6 07
AND Register A with 07H, keeping bits 0-2: the bit within the map byte, counted from bit 7.
4FDF
LD B,A 47
Copy Register A (the bit number) into Register B.
4FE0
LD A,01H 3E 01
Load Register A with 01H, the bit that 4FE3H rotates into place.
4FE2
INC B 04
INCrement Register B: the number of right rotations.

Bit Loop

4FE3
RRCA 0F
Rotate Register A right (01H becomes 80H after one rotation).
4FE4
DECrement Register B and LOOP BACK to 4FE3H until it is 0. Register A = the bit of this DEC.
4FE6
OR (HL) B6
OR the map byte at Register Pair HL into Register A.
4FE7
LD (HL),A 77
Store Register A at Register Pair HL: the HIT byte of this DEC will be cleared at 4F33H.
4FE8
POP BC C1
Restore Register Pair BC from the stack: the count of the freeing loop.
4FE9
RET C9
RETurn with Register D = byte 1 of the freed entry.

4FEAH - Write the Buffer, Then the EOF

Used by CLOSE (4E09H) and write EOF (5030H) inside their DOS function. Two words are pushed so that the SYS0/SYS buffer write at 4A07H returns into 497CH: an error there leaves through 4972H, otherwise 497CH pops E507H and does RST 28H with Register A = E5H and Register C = 07H, which runs sub-function 7 (4FF5H) of this same overlay. Its RET comes back to the caller of 4FEAH.

4FEA
LD HL,E507H 21 07 E5
Load Register Pair HL with E507H: E5H = SYS3/SYS function 7, 07H = sub-function 7. 4E09H and 5030H call here.
4FED
PUSH HL E5
Save Register Pair HL (E507H) onto the stack for 497CH to take into Register A and Register C.
4FEE
LD HL,497CH 21 7C 49
Load Register Pair HL with 497CH, the SYS0/SYS return point that chains to another overlay function.
4FF1
PUSH HL E5
Save Register Pair HL (497CH) onto the stack as the return address for 4A07H.
4FF2
JUMP to 4A07H (SYS0/SYS) to write the buffer of the FCB if FCB+1 bit 4 says it holds changed data; it RETurns to 497CH.

4FF5H - Function E5H Sub-function 7: Write the EOF of the FCB into the FPDE

Reached only through the chain set up by 4FEAH, so it runs inside the caller's DOS function (Index Register IX = the FCB). Files whose access level is 5 or more (READ, EXEC, LOCK) are not changed: RETurn with the CARRY FLAG clear. Otherwise the EOF of the FCB is stored in the FPDE (low byte at byte 3, middle and high at bytes 20 and 21 in the FPDE form, one higher when the low byte is not 0) and the directory sector is written only if the value changed. Returns the CARRY FLAG, Register Pair HL = the FPDE and Register Pair DE = the EOF middle and high bytes in the FPDE form.

4FF5
GOSUB to 48F0H (SYS0/SYS) to select the drive of the FCB and read the directory sector of its FPDE (the DEC in FCB+7). Register Pair HL returns the FPDE, or the NZ FLAG with error 2DH if it is not the file's FPDE. 4D38H comes here.
4FF8
If the NZ FLAG is set (an error), JUMP to 4E56H, which leaves the DOS function through 4972H with the error code in Register A.
4FFB
LD A,(IX+01H) DD 7E 01
Fetch (IX+01H) (Index Register IX = the FCB), FCB+1, into Register A.
4FFE
AND 07H E6 07
AND Register A with 07H, keeping bits 0-2: the access level (0 FULL to 4 WRITE, 5 READ, 6 EXEC, 7 LOCK).
5000
CP 05H FE 05
Compare Register A (the access level) against 05H.
5002
RET NC D0
If the NO CARRY FLAG is set (level 5 or more), RETurn with the CARRY FLAG clear; the FPDE is not changed.
5003
LD E,(IX+0CH) DD 5E 0C
Load Register E with (IX+0CH) (Index Register IX = the FCB), FCB+12, the EOF middle byte.
5006
LD D,(IX+0DH) DD 56 0D
Load Register D with (IX+0DH) (Index Register IX = the FCB), FCB+13, the EOF high byte: Register Pair DE = the whole sectors of the EOF.
5009
LD A,(IX+08H) DD 7E 08
Fetch (IX+08H) (Index Register IX = the FCB), FCB+8, the EOF low byte (the position within the last sector), into Register A.
500C
OR A B7
OR Register A (the EOF low byte) with itself: the Z FLAG is set if it is 0.
500D
If the Z FLAG is set (the EOF low byte is 0), JUMP to 5010H.
500F
INC DE 13
A partly used sector counts: INCrement Register Pair DE, the FPDE form of the EOF. Self-Modifying Code
The SYS6/SYS patch list at 5BE1H replaces this INC DE with a NOP, and the NOP at 4E1DH with INC DE, to match bit 5 of the PDRIVE flags at 4282H (SYS6/SYS 560DH).
5010
PUSH HL E5
Save Register Pair HL (the FPDE) onto the stack. 500DH comes here.
5011
INC L 2C
INCrement Register L (the FPDE pointer) to byte 1 of the FPDE.
5012
INC L 2C
INCrement Register L (the FPDE pointer) to byte 2 of the FPDE.
5013
INC L 2C
INCrement Register L to byte 3 of the FPDE, the EOF low byte.
5014
LD C,(HL) 4E
Fetch the old EOF low byte at Register Pair HL into Register C.
5015
LD (HL),A 77
Store Register A (the new EOF low byte) at Register Pair HL.
5016
SUB C 91
SUBtract Register C (the old low byte) from Register A: the Z FLAG is set if the low byte did not change.
5017
LD BC,0011H 01 11 00
Load Register Pair BC with 0011H (17). LD does not change the flags.
501A
ADD HL,BC 09
ADD Register Pair BC to Register Pair HL: Register Pair HL = byte 20 of the FPDE, the EOF middle byte. ADD HL,BC does not change the Z FLAG.
501B
LD C,(HL) 4E
Fetch the old EOF middle byte at Register Pair HL into Register C.
501C
INC HL 23
INCrement Register Pair HL to byte 21 of the FPDE, the EOF high byte.
501D
LD B,(HL) 46
Fetch the old EOF high byte at Register Pair HL into Register B: Register Pair BC = the old EOF sectors.
501E
LD (HL),D 72
Store Register D (the new high byte) at Register Pair HL, byte 21.
501F
DEC HL 2B
DECrement Register Pair HL to byte 20 of the FPDE.
5020
LD (HL),E 73
Store Register E (the new middle byte) at Register Pair HL, byte 20.
5021
If the NZ FLAG is set (the low byte changed), JUMP to 5027H to write the sector.
5023
LD H,D 62
Copy Register D (the new high byte) into Register H.
5024
LD L,E 6B
Copy Register E (the new middle byte) into Register L: Register Pair HL = the new EOF sectors.
5025
SBC HL,BC ED 42
SUBtract Register Pair BC (the old EOF sectors) from Register Pair HL; the CARRY FLAG is clear after the equal SUB at 5016H and the ADD at 501AH. The Z FLAG is set if they are the same.
5027
If the NZ FLAG is set (the EOF changed), GOSUB to 4E5FH to write the directory sector with the new EOF. 5021H comes here.
502A
POP HL E1
Restore Register Pair HL from the stack: the FPDE.
502B
SCF 37
Set the CARRY FLAG: the FPDE could be updated.
502C
RET C9
RETurn to the caller of 4FEAH with the CARRY FLAG, Register Pair HL = the FPDE and Register Pair DE = the EOF in the FPDE form.

502DH - Function C5H: Write the EOF to the Directory (4451H)

Manual section 3.28. Register Pair DE points to an open FCB. The buffer is written if it holds changed data, then the EOF is stored in the FPDE. Returns error 25H if the access level of the file does not allow the change.

502D
GOSUB to 4925H (SYS0/SYS): the FCB at Register Pair DE must be open (else error 26H); the rest runs as a DOS function with Index Register IX = the FCB and Index Register IY = 4280H.
5030
GOSUB to 4FEAH to write the buffer if needed, then the EOF into the FPDE (4FF5H). The CARRY FLAG returns if it was done.
5033
If the CARRY FLAG is set (done), JUMP to 4FC0H to RETurn with Register A = 00H and the Z FLAG.
5036
LD A,25H 3E 25
Load Register A with error code 25H (ILLEGAL ACCESS TRIED TO PROTECTED FILE).
5038
OR A B7
OR Register A (25H) with itself to set the NZ FLAG.
5039
RET C9
RETurn from the DOS function with error 25H in Register A.

503AH - Function E5H Sub-function 6: PURGE

PURGE,[password:]dn[,/ext][,USR] (manual 2.40). SYS7/SYS (4D70H-4D7CH) handles the start of the command and chains here with RST 28H: Register B = the drive number (from 427EH) and Register Pair HL = the command line after the drive. This part reads the options into Register C: bit 0 = /ext given (the extension at 5141H-5143H), bit 1 = USR (leave out system and invisible files).

503A
LD A,B 78
Copy Register B (the drive number from SYS7/SYS) into Register A.
503B
LD C,00H 0E 00
Load Register C with 00H: no PURGE options have been read yet.
503D
GOSUB to 4791H (SYS0/SYS) to select the drive in Register A and wait for the index hole to change. The NZ FLAG returns with error 08H if there is no turning diskette.
5040
RET NZ C0
If the NZ FLAG is set (no diskette), RETurn to SYS7/SYS with the error code in Register A.

Option Loop
Register Pair HL = the command line, Register C = the option bits.

5041
GOSUB to 4C7AH (SYS0/SYS) to test the character at Register Pair HL: the Z FLAG at the carriage return; the NZ FLAG and NO CARRY after a comma or spaces (Register Pair HL moved past them); the CARRY FLAG for anything else. Register A returns 34H. 505FH, 5067H, 506BH, 5074H, 507AH and 507FH come here.
5044
RET C D8
If the CARRY FLAG is set (anything else), RETurn to SYS7/SYS with Register A = 34H (ILLEGAL KEYWORD OR SEPARATOR OR TERMINATOR).
5045
If the Z FLAG is set (the end of the command line), JUMP to 5081H to start the directory scan.
5047
LD A,(HL) 7E
Fetch the next character of the command line at Register Pair HL into Register A.
5048
CP 2FH FE 2F
Compare Register A (the option character) against 2FH (/).
504A
If the NZ FLAG is set (not /), JUMP to 5069H to test for USR.
504C
SET 0,C CB C1
Set bit 0 of Register C (the options): an extension is given.
504E
INC HL 23
INCrement Register Pair HL past the / on the command line.
504F
LD DE,5140H 11 40 51
Point Register Pair DE to 5140H, one byte before the extension at 5141H, because the loop moves on first.
5052
LD B,03H 06 03
Load Register B with 03H: up to three extension characters.

Extension Loop
Register Pair HL = the command line, Register Pair DE = the last extension character stored, Register B = the places left.

5054
LD A,(HL) 7E
Fetch the character at Register Pair HL into Register A.
5055
SUB 30H D6 30
SUBtract 30H (the character 0) from Register A.
5057
CP 0AH FE 0A
Compare Register A against 0AH: the CARRY FLAG for a digit 0-9.
5059
If the CARRY FLAG is set (a digit), JUMP to 5061H to store it.
505B
SUB 11H D6 11
SUBtract 11H more from Register A: the letters A-Z become 00H-19H.
505D
CP 1AH FE 1A
Compare Register A against 1AH: the CARRY FLAG for a letter A-Z.
505F
If the NO CARRY FLAG is set (not a letter), the extension ends here: LOOP BACK to 5041H, which needs a separator or the end of the line at this character.
5061
INC DE 13
INCrement Register Pair DE to the next place of the extension.
5062
LD A,(HL) 7E
Fetch the character at Register Pair HL into Register A again.
5063
LD (DE),A 12
Store Register A (the character) at Register Pair DE in the extension at 5141H-5143H.
5064
INC HL 23
INCrement Register Pair HL to the next character of the command line.
5065
DECrement Register B (the places left) and LOOP BACK to 5054H while it is not 0.
5067
Three characters taken: LOOP BACK to 5041H for the next option.
5069
CP 55H FE 55
Compare Register A (the character) against 55H (U).
506B
If the NZ FLAG is set (not U), LOOP BACK to 5041H, which gives error 34H at this character.
506D
LD D,H 54
Copy Register H (the high byte of the command line pointer) into Register D.
506E
LD E,L 5D
Copy Register L into Register E: Register Pair DE = the U on the command line.
506F
INC DE 13
INCrement Register Pair DE to the character after the U.
5070
LD A,(DE) 1A
Fetch the character at Register Pair DE into Register A.
5071
CP 53H FE 53
Compare Register A (the character after the U) against 53H (S).
5073
INC DE 13
INCrement Register Pair DE to the next character. INC DE does not change the flags.
5074
If the NZ FLAG is set (not S), LOOP BACK to 5041H with Register Pair HL still at the U, which gives error 34H.
5076
LD A,(DE) 1A
Fetch the character after the S at Register Pair DE into Register A.
5077
CP 52H FE 52
Compare Register A (the character after the S) against 52H (R).
5079
INC DE 13
INCrement Register Pair DE past it. INC DE does not change the flags.
507A
If the NZ FLAG is set (not R), LOOP BACK to 5041H, which gives error 34H at the U.
507C
SET 1,C CB C9
Set bit 1 of Register C (the options): USR given.
507E
EX DE,HL EB
Exchange Register Pairs DE and HL: Register Pair HL = the command line after USR.
507F
LOOP BACK to 5041H to look for the next option or the end of the line.

5081H - PURGE: Ask About Every File

Every FDE sector of the directory is read in turn (Register C = the FDE sector number, counted from 0, which is also the DEC of its first entry) and each entry that is an FPDE in use is offered, except DEC 00H (BOOT/SYS) and DEC 01H (DIR/SYS). The operator answers Y (kill it), N (keep it) or Q (stop).

5081
LD A,C 79
Copy Register C (the option bits) into Register A. 5045H comes here.
5082
LD (5140H),A 32 40 51
Store Register A (the option bits) at 5140H for the entry loop to read.
5085
LD A,01H 3E 01
Load Register A with 01H, directory sector 1, the HIT.
5087
GOSUB to 48AFH (SYS0/SYS) to read the HIT into the buffer at 4300H. The NZ FLAG returns with the error code on a read error.
508A
RET NZ C0
If the NZ FLAG is set (a read error), RETurn to SYS7/SYS with the error code in Register A.
508B
LD A,(431FH) 3A 1F 43
Fetch HIT byte 1FH from 431FH into Register A: the number of FDE sectors beyond the first eight.
508E
ADD A,08H C6 08
ADD 08H to Register A: the number of FDE sectors in this directory.
5090
LD (5130H),A 32 30 51
Store Register A (the number of FDE sectors) at 5130H, the operand of the CP nn at 512FH: the end of the scan.
5093
XOR A AF
Set Register A to 0 with XOR: FDE sector 0, the first one to read.

FDE Sector Loop
Register A = the FDE sector number.

5094
LD C,A 4F
Copy Register A (the FDE sector number) into Register C.
5095
GOSUB to 48DBH (SYS0/SYS) with Register A = the DEC of the first entry of that FDE sector, to read the sector into 4300H. Register Pair HL returns its first entry; the NZ FLAG on a read error.
5098
RET NZ C0
If the NZ FLAG is set (a read error), RETurn to SYS7/SYS with the error code in Register A.

Entry Loop
Register Pair HL = the entry, Register C = the FDE sector number.

5099
LD A,(HL) 7E
Fetch byte 0 of the entry at Register Pair HL (the flags) into Register A.
509A
AND 90H E6 90
AND Register A with 90H, keeping bit 7 (FXDE) and bit 4 (in use).
509C
CP 10H FE 10
Compare Register A against 10H: an FPDE in use.
509E
If the NZ FLAG is set (anything else), JUMP to 5124H for the next entry.
50A1
LD A,(HL) 7E
Fetch byte 0 of the entry at Register Pair HL into Register A again.
50A2
AND 48H E6 48
AND Register A with 48H, keeping bit 6 (a system file) and bit 3 (an invisible file).
50A4
LD DE,5140H 11 40 51
Point Register Pair DE to 5140H, the option bits. LD does not change the flags.
50A7
LD A,(DE) 1A
Fetch the option bits at Register Pair DE into Register A. LD does not change the flags.
50A8
If the Z FLAG is set (neither a system nor an invisible file), JUMP to 50AEH.
50AA
BIT 1,A CB 4F
Test bit 1 of Register A (the option bits): USR, leave out system and invisible files.
50AC
If the NZ FLAG is set (USR given), JUMP to 5124H to skip this system or invisible file.
50AE
BIT 0,A CB 47
Test bit 0 of Register A (the options): an extension is given. 50A8H comes here.
50B0
If the Z FLAG is set (no extension given), JUMP to 50C4H.
50B2
PUSH HL E5
Save Register Pair HL (the entry) onto the stack.
50B3
LD A,L 7D
Copy Register L (the offset of the entry) into Register A.
50B4
ADD A,0DH C6 0D
ADD 0DH to Register A: the offset of the extension of the entry (byte 13).
50B6
LD L,A 6F
Copy Register A into Register L: Register Pair HL = the extension of the entry.
50B7
LD B,03H 06 03
Load Register B with 03H: three characters to compare.

Extension Compare Loop
Register Pair DE = the wanted extension, Register Pair HL = the extension of the entry.

50B9
INC DE 13
INCrement Register Pair DE to the next wanted character (5141H-5143H).
50BA
LD A,(DE) 1A
Fetch the wanted character at Register Pair DE into Register A.
50BB
CP (HL) BE
Compare Register A against the character of the entry at Register Pair HL.
50BC
INC HL 23
INCrement Register Pair HL to the next character of the entry. INC HL does not change the flags.
50BD
If the NZ FLAG is set (different), JUMP to 50C1H.
50BF
DECrement Register B and LOOP BACK to 50B9H until all 3 characters match.
50C1
POP HL E1
Restore Register Pair HL from the stack: the entry. POP does not change the flags. 50BDH comes here.
50C2
If the NZ FLAG is set (a different extension), JUMP to 5124H for the next entry.
50C4
LD A,C 79
Copy Register C (the FDE sector number) into Register A. 50B0H comes here.
50C5
CP 02H FE 02
Compare Register A against 02H: the NO CARRY FLAG for FDE sector 2 or higher.
50C7
LD A,L 7D
Copy Register L (the offset of the entry: 00H, 20H up to E0H) into Register A. LD does not change the flags.
50C8
If the NO CARRY FLAG is set (FDE sector 2 or higher), JUMP to 50CDH.
50CA
OR A B7
OR Register A (the offset) with itself: the Z FLAG for the first entry of FDE sector 0 or 1.
50CB
If the Z FLAG is set (DEC 00H, BOOT/SYS, or DEC 01H, DIR/SYS), JUMP to 5124H: they are never offered.
50CD
ADD A,C 81
ADD Register C (the FDE sector number) to Register A (the offset): Register A = the DEC of the entry (bits 5-7 the entry, bits 0-4 the sector). 50C8H comes here.
50CE
LD (4487H),A 32 87 44
Store Register A (the DEC) at 4487H, FCB+7 of the DOS FCB at 4480H, for the kill at 5119H.
50D1
PUSH HL E5
Save Register Pair HL (the entry) onto the stack.
50D2
LD A,L 7D
Copy Register L (the offset of the entry) into Register A.
50D3
ADD A,05H C6 05
ADD 05H to Register A: the offset of the name (byte 5).
50D5
LD L,A 6F
Copy Register A into Register L: Register Pair HL = the name of the entry.
50D6
LD B,08H 06 08
Load Register B with 08H: eight name characters.
50D8
GOSUB to 5136H to display the Register B characters at Register Pair HL without spaces. Register Pair HL returns byte 13, the extension.
50DB
LD A,(HL) 7E
Fetch the first extension character at Register Pair HL into Register A.
50DC
CP 20H FE 20
Compare Register A (the first extension character) against 20H (a space).
50DE
If the Z FLAG is set (no extension), JUMP to 50EAH.
50E0
LD A,2FH 3E 2F
Load Register A with 2FH (/), shown between the name and the extension.
50E2
GOSUB to 0033H (ROM) to display the character in Register A.
50E5
LD B,03H 06 03
Load Register B with 03H: three extension characters.
50E7
GOSUB to 5136H to display the extension at Register Pair HL without spaces.
50EA
LD HL,5144H 21 44 51
Point Register Pair HL to 5144H, the prompt: eight spaces and Kill It? (Y/N/Q). 50DEH comes here.
50ED
GOSUB to 4467H (SYS0/SYS) to display the message at Register Pair HL up to the 03H at 515EH.

Key Loop

50F0
GOSUB to 0049H (ROM) to wait for a key; Register A returns it.
50F3
RES 5,A CB AF
Reset bit 5 of Register A (the key): lower case letters become upper case.
50F5
CP 51H FE 51
Compare Register A (the key) against 51H (Q).
50F7
If the Z FLAG is set (Q), JUMP to 5101H to show and act on the answer.
50F9
CP 4EH FE 4E
Compare Register A (the key) against 4EH (N).
50FB
If the Z FLAG is set (N), JUMP to 5101H to show and act on the answer.
50FD
CP 59H FE 59
Compare Register A (the key) against 59H (Y).
50FF
If the NZ FLAG is set (any other key), LOOP BACK to 50F0H to wait for another key.
5101
PUSH AF F5
Save Register Pair AF (Register A = the answer) onto the stack. 50F7H and 50FBH come here.
5102
GOSUB to 0033H (ROM) to display the answer in Register A.
5105
LD A,0DH 3E 0D
Load Register A with 0DH (carriage return) to end the prompt line.
5107
GOSUB to 0033H (ROM) to display the carriage return in Register A and end the line.
510A
POP AF F1
Restore Register Pair AF from the stack: Register A = the answer.
510B
POP HL E1
Restore Register Pair HL from the stack: the entry, saved at 50D1H.
510C
CP 51H FE 51
Compare Register A (the answer) against 51H (Q).
510E
RET Z C8
If the Z FLAG is set (Q), RETurn to SYS7/SYS with the Z FLAG (no error) and Register A = 51H: the PURGE ends.
510F
CP 4EH FE 4E
Compare Register A (the answer) against 4EH (N).
5111
If the Z FLAG is set (N), JUMP to 5124H for the next entry.
5113
LD DE,4480H 11 80 44
Y: point Register Pair DE to 4480H, the DOS FCB.
5116
LD A,80H 3E 80
Load Register A with 80H, the open bit of FCB+0.
5118
LD (DE),A 12
Store Register A (80H) at Register Pair DE as FCB+0: the FCB counts as open. Its DEC (4487H) was stored at 50CEH.
5119
GOSUB to 4DF4H to kill the file through the DOS FCB at Register Pair DE, with Register Pair HL = its FPDE. The registers other than AF come back unchanged; the NZ FLAG with an error code on failure.
511C
RET NZ C0
If the NZ FLAG is set (an error), RETurn to SYS7/SYS with the error code in Register A.
511D
PUSH HL E5
Save Register Pair HL (the entry) onto the stack.
511E
LD A,C 79
Copy Register C (the FDE sector number) into Register A.
511F
GOSUB to 48DBH (SYS0/SYS) to read the FDE sector again: the kill used the buffer at 4300H for the GAT and the HIT.
5122
POP HL E1
Restore Register Pair HL from the stack: the entry. POP does not change the flags.
5123
RET NZ C0
If the NZ FLAG is set (a read error), RETurn to SYS7/SYS with the error code in Register A.
5124
LD A,L 7D
Copy Register L (the offset of the entry) into Register A. 509EH, 50ACH, 50C2H, 50CBH and 5111H come here.
5125
AND E0H E6 E0
AND Register A with E0H, keeping bits 5-7: the start of the entry.
5127
ADD A,20H C6 20
ADD 20H to Register A: the next entry. The CARRY FLAG is set after the last entry (E0H).
5129
LD L,A 6F
Copy Register A into Register L: Register Pair HL = the next entry.
512A
If the NO CARRY FLAG is set (still in this sector), JUMP to 5099H for the next entry.
512D
INC C 0C
INCrement Register C (the FDE sector number).
512E
LD A,C 79
Copy Register C (the next FDE sector) into Register A.
512F
CP 00H FE 00
Compare Register A against the number of FDE sectors (00H in the file). Self-Modifying Code
The operand at 5130H is stored at 5090H.
5131
If the CARRY FLAG is set (more FDE sectors), JUMP to 5094H to read the next one.
5134
XOR A AF
Set Register A to 0 with XOR, which also sets the Z FLAG: no error.
5135
RET C9
RETurn to SYS7/SYS with Register A = 00H and the Z FLAG.

5136H - Display Characters Without Spaces

Register Pair HL points to the characters, Register B holds how many. Spaces are skipped; Register Pair HL is left after the last character.

Display Loop

5136
LD A,(HL) 7E
Fetch the character at Register Pair HL into Register A. 50D8H and 50E7H call here.
5137
CP 20H FE 20
Compare Register A (the character) against 20H (a space), which is skipped.
5139
INC HL 23
INCrement Register Pair HL to the next character. INC HL does not change the flags.
513A
If the NZ FLAG is set (not a space), GOSUB to 0033H (ROM) to display the character in Register A.
513D
DECrement Register B (the characters left) and LOOP BACK to 5136H until it is 0.
513F
RET C9
RETurn with Register Pair HL after the last character.

5140H - PURGE Data

The option bits, the wanted extension and the prompt. SYS3/SYS is loaded from the diskette for every PURGE (SYS7/SYS runs first in the same overlay area), so the extension starts as three spaces each time.

5140
DEFB 00H 00
The PURGE option bits, stored at 5082H and read at 50A7H: bit 0 = /ext given, bit 1 = USR given. 504FH points Register Pair DE here, one before the extension.
5141-5143
DEFM " " 20 20 20
The wanted extension, filled from the command line at 5063H and compared at 50BAH. Characters not given stay spaces.
5144-515E
DEFM " Kill It? (Y/N/Q) ", 03H 20 20 20 20 20 20 20 20 4B 69 6C 6C 20 49 74 3F 20 28 59 2F 4E 2F 51 29 20 20 03
The prompt displayed by 50EDH after the name, ended by 03H.

515FH - Function A5H: J-K-L Screen Print

Manual sections 2.26 and 4.5. The SYS0/SYS key combination check (459DH) does RST 28H with code A5H when J, K and L are held down. The display memory 3C00H-3FFFH is sent to the printer through ROM 003BH, a carriage return before each 64-character line. Characters 00H-1FH and anything above the value of option AX (4290H) are sent as a period. BREAK stops the print.

515F
LD HL,3C00H 21 00 3C
Point Register Pair HL to 3C00H, the start of the display memory.

Print Loop
Register Pair HL = the next display address.

5162
LD A,(3840H) 3A 40 38
Fetch keyboard row 6 from 3840H into Register A. 516CH and 518DH come here.
5165
AND 04H E6 04
AND Register A with 04H, keeping bit 2, the BREAK key.
5167
If the Z FLAG is set (BREAK is not pressed), JUMP to 516EH.
5169
LD HL,4000H 21 00 40
BREAK is pressed: point Register Pair HL to 4000H, the end of the display memory.
516C
LOOP BACK to 5162H. While BREAK is held Register Pair HL stays 4000H; when it is let go 516EH sends a carriage return and 5179H ends the print.
516E
LD A,L 7D
Copy Register L (the low byte of the display address) into Register A. 5167H comes here.
516F
AND 3FH E6 3F
AND Register A with 3FH, keeping bits 0-5: the Z FLAG at the start of a 64-character display line.
5171
LD A,0DH 3E 0D
Load Register A with 0DH (carriage return). LD does not change the flags.
5173
If the Z FLAG is set (the start of a line), GOSUB to 003BH (ROM) to send the carriage return in Register A to the printer.
5176
LD A,H 7C
Copy Register H (the high byte of the display address) into Register A.
5177
AND 3FH E6 3F
AND Register A with 3FH, keeping bits 0-5: 00H when Register Pair HL has reached 4000H (3CH-3FH stay above 0).
5179
RET Z C8
If the Z FLAG is set (the end of the display memory, or BREAK), RETurn to the caller of the RST 28H.
517A
LD A,(HL) 7E
Fetch the display character at Register Pair HL into Register A.
517B
CP 20H FE 20
Compare Register A against 20H: the CARRY FLAG for 00H-1FH.
517D
INC HL 23
INCrement Register Pair HL to the next display address. INC HL does not change the flags.
517E
If the CARRY FLAG is set (00H-1FH), JUMP to 5188H to print a period instead.
5180
LD B,A 47
Copy Register A (the character to print) into Register B.
5181
LD A,(4290H) 3A 90 42
Fetch option AX from 4290H into Register A: the highest code the printer can print.
5184
CP B B8
Compare Register A (option AX) against Register B (the character): the NO CARRY FLAG if the character is not above option AX.
5185
LD A,B 78
Copy Register B (the character) back into Register A. LD does not change the flags.
5186
If the NO CARRY FLAG is set (printable), JUMP to 518AH.
5188
LD A,2EH 3E 2E
00H-1FH, or above option AX: load Register A with 2EH (a period) instead. 517EH comes here.
518A
GOSUB to 003BH (ROM) to send the character in Register A to the printer; it waits while the printer is not ready. 5186H comes here.
518D
LOOP BACK to 5162H for the next display character.

518FH - Unused Bytes

The end of the file. Nothing in SYS3/SYS reaches these bytes.

518F-51DF
NOP x 81 00 x 81
81 bytes of 00H to the end of the file (51DFH). 51E0H-51FFH is not in the file.