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

Page Customization

Introduction/Summary

NEWDOS/80 v2.0 SYS7/SYS Disassembly - SYSTEM, HIMEM, PDRIVE, AUTO, ATTRIB, PROT, DUMP, PURGE, TIME and DATE (Model III)

SYS7/SYS is the overlay that carries out ten library commands. Its five load records fill 4D00H-51E7H of the overlay area (1,256 bytes) and its transfer address is 4D00H. SYS1/SYS runs it with RST 28H code E9H (directory slot 09H, function 7) and the command's sub-function in Register C, taken from byte 1 of the command's entry in SYS1/SYS's command table: 1 SYSTEM, 2 HIMEM, 3 PDRIVE, 4 AUTO, 5 ATTRIB, 6 PROT, 7 DUMP, 9 PURGE, 0AH TIME, 0BH DATE. Sub-function 8 is the second half of DUMP, which SYS7/SYS calls itself through SYS0/SYS 4978H. The dispatcher at 4D00H sends each sub-function to its command; any other code or sub-function gives error 2AH.

The page shows every byte of the file once, in address order. Bytes that are not in the file are shown as a DEFS row without a byte column.

Commands

SYSTEM, PDRIVE and PURGE (4D68H) only read [password]:d, select the drive and check the diskette password (4E90H), then hand the rest of the command line to another overlay with Register B = the drive: SYS17/SYS (code 33H), SYS16/SYS (code 32H) and SYS3/SYS function 7 sub-function 6 (code E5H).

HIMEM (4D7DH) displays 4411H as four hexadecimal digits, or sets it to a value of 7000H or more, lowered to the top of memory found at reset (42C9H) when it is above it.

TIME and DATE (4D34H) display the clock (4219H-4217H) as HH:MM:SS or the date (421CH-421AH) as MM/DD/YY through the ROM routines at 3036H and 3033H, or set three two-digit fields with interrupts off (4DAEH). The values are not range-checked.

AUTO (4DDBH) stores the rest of the command line, carriage return included, in bytes E0H-FFH of the GAT of drive 0. At most 31 characters fit.

ATTRIB (4DFAH) works on a file SYS1/SYS has opened with full access (else error 19H): its FPDE is read, changed by the keywords and written back. PROT (4ECCH) changes a diskette's GAT (PW=, NAME=, DATE=) and, after the last keyword, applies LOCK, UNLOCK and RUF to every file in the directory (4F10H). LOCK and UNLOCK leave system files and invisible files alone.

DUMP (50F4H) writes memory to a file in load module format; see DUMP File Format below.

Numbers are read by 4FA5H (decimal, or hexadecimal with a trailing H). Passwords are read and encoded by 5005H: a blank password has the code 4296H. With option AA on (bit 7 of 428CH) a diskette password that does not match gives error 37H, also when none was typed.

Keyword Tables

The keyword search at 4F78H takes a table of entries made of the keyword, a 00H and the handler's address, and jumps to the handler of the first keyword the text starts with. The search at 4F84H works for any table whose names are followed by a fixed number of bytes; the access level table has one level byte after each name.

TableEntries
5054HATTRIB: INV, VIS, PROT=, ACC=, UPD=, ASE=, ASC=, UDF=, LRL=
5093HAccess levels: LOCK 7, EXEC 6, READ 5, WRITE 4, RENAME 2, NAME 2, KILL 1, FULL 0
50C7HPROT: PW=, UNLOCK, LOCK, NAME=, DATE=, RUF

ATTRIB changes these FPDE bytes: byte 0 bit 3 (INV, VIS) and bits 0-2 (PROT=); byte 1 bit 7 (ASE=, set for N), bit 6 (ASC=, set for N) and bit 5 (UDF=, set for Y); byte 4 (LRL=, 256 stored as 00H); bytes 16-17 (UPD=) and 18-19 (ACC=). PROT changes GAT bytes CEH-CFH (PW=), D0H-D7H (NAME=) and D8H-DFH (DATE=), and in every FPDE in use byte 1 bit 5 (RUF) and bytes 16-19 (LOCK, UNLOCK).

DUMP File Format

DUMP filespec,start,end[,entry[,load]]. SYS1/SYS opens the file in the DOS FCB at 4480H. The file's sector buffer is set to 4D68H-4E67H, inside SYS7/SYS's own code (the SYSTEM to ATTRIB code, which DUMP does not need). First one sector is written at relative sector (end - start + 5) / 252 of the file, so that the file's space is allocated in one step; the write returns through SYS0/SYS 4978H, which runs SYS7/SYS sub-function 8 (5149H).

The file is then rewound and written one byte at a time through the ROM output routine at 001BH with Register Pair DE = the FCB. The entry address defaults to 402DH and the load address to start. Records:

BytesContents
01H, n, addressA block of n - 2 data bytes (up to 254; n = 00H means 256) to be loaded at the two-byte address. Addresses between start and end are written as address - start + load (51BBH).
02H, 02H, addressThe transfer record with the entry address, translated in the same way.

With entry = FFFFH no records are written: the file holds the start address as two bytes and then the data. Finally the file is rewound and closed (SYS0/SYS 4428H).

Variables

Memory Outside SYS7/SYS

Address
Size
Contents
4217H-4219H
3 bytes
Clock
Seconds, minutes and hours in binary, counted by the clock interrupt. TIME displays them through ROM 3036H (4D4EH) and stores the hours first at 4219H (4D29H, 4DC8H).
421AH-421CH
3 bytes
Date
Year, day and month in binary. DATE displays them through ROM 3033H (4D4EH) and stores the month first at 421CH (4D38H, 4DC8H).
427EH
1 byte
Current Drive
The drive SYS0/SYS 4723H selected last; read through Index Register IY-02H (4D79H) for SYSTEM, PDRIVE and PURGE.
428CH
1 byte
System Options
Copy of system data sector byte F0H. Bit 7, option AA: diskette passwords are checked (4EC2H).
42C9H-42CAH
2 bytes
Top of Memory
The top of memory found at reset; HIMEM is lowered to it (4D9FH).
4300H-43FFH
256 bytes
Sector Buffer
The GAT (AUTO, PROT and the password check), the HIT (4F2AH) and the FDE sectors (ATTRIB, PROT). 431FH is HIT byte 1FH, the FDE sectors beyond the first 8 (4F2EH); 43CEH-43CFH the diskette password code (4EBAH, 4F0CH, 4F1EH); 43D0H-43D7H the diskette name (4EDDH); 43D8H-43DFH its date (4ED8H); 43E0H-43FFH the AUTO command (4DE8H).
4411H-4412H
2 bytes
HIMEM
The highest address free for programs, displayed by 4D82H and set by 4DA9H.
4480H-449FH
32 bytes
DOS FCB
The file SYS1/SYS opened for ATTRIB and DUMP. DUMP stores its buffer address 4D68H at 4483H-4484H (50F7H) and the relative sector to write at 448AH-448BH, the middle and high bytes of NEXT (5138H).

SYS7/SYS Variables

Address
Size
Contents
4D68H-4E67H
256 bytes
DUMP Sector Buffer
SYS7/SYS's own code from 4D68H on, used as the DUMP file's buffer (50F4H).
51E8H-51F0H
9 bytes
Text and Password Buffer
Past the end of the file: the eight-character password buffer of 5005H, and the HIMEM, TIME and DATE text with its carriage return (4D4AH, 4D86H).

Operands Changed at Run Time

Address
Size
Contents
4D4FH
1 byte
CALL Operand Low Byte
36H as loaded (ROM 3036H, the time); 4D3EH stores 33H for DATE (ROM 3033H) or 36H for TIME.
4DD2H
1 byte
Field Separator
The operand of the CP at 4DD1H: 00H in the file, 3AH (colon) for TIME or 2FH (slash) for DATE, stored by 4D42H.
4F1AH
1 byte
PROT File Flags
The operand of the LD A at 4F19H: bit 0 UNLOCK, bit 1 LOCK, bit 2 RUF, ORed in by 4F00H-4F05H.
4F6DH
1 byte
FDE Sector Count
The operand of the CP at 4F6CH: 8 plus HIT byte 1FH, stored by 4F33H.
51BEH-51BFH
2 bytes
DUMP End Address
The operand of the LD HL at 51BDH, stored by 5157H.
51C5H-51C6H
2 bytes
DUMP Start Address
The operand of the LD DE at 51C4H, stored by 515BH.
51CCH-51CDH
2 bytes
DUMP Load Address
The operand of the LD HL at 51CBH, stored by 5153H.

Outside Routines Called

AddressContents
001BHROM: send the byte in Register A to the control block at Register Pair DE (51DBH).
3033H, 3036HROM: write the date as MM/DD/YY or the time as HH:MM:SS at Register Pair HL (4D4EH).
402DHThe DOS READY jump, DUMP's default entry address (5107H).
4033H, 4A80HSYS0/SYS: the ROM's output routine reaches the file write through 4033H (51DBH).
4408H, 4409HSYS0/SYS: the command's return address and the DOS error exit (4D65H).
4428H, 4439H, 443FHSYS0/SYS: close, write and rewind a file (51A1H, 5146H, 514AH and 519CH).
4467H, 44D2HSYS0/SYS: display a message, a word as four hexadecimal digits (4D90H, 4D8AH).
4723H, 4791HSYS0/SYS: select a drive, select it and test for a diskette (4DDDH, 4EAEH).
48AFH, 48C4H, 48DBH, 48F0HSYS0/SYS: read and write directory sectors, read an FDE sector, read the FPDE of an open FCB (4DE2H, 4DF7H, 4F38H, 4E0AH).
4978HSYS0/SYS: the function chain return that brings DUMP back as sub-function 8 (5141H).
4BC2H, 4BC5HSYS0/SYS: the dispatcher's CALL to 4D00H and its return address, dropped by ATTRIB and DUMP (4DFAH, 50FAH, 5149H).
4C59HSYS0/SYS: divide Register Pair HL by Register A (5135H).
4C6AH, 4C7AH, 4C7EHSYS0/SYS: compare a keyword, test for the end of the line, step over a separator (4F84H, 4D5BH, 4F72H).

Disassembly

4D00H - Function Dispatcher

SYS7/SYS (directory slot 09H) is loaded by the SYS0/SYS RST 28H dispatcher into 4D00H-51E7H of the overlay area and entered at 4D00H by the CALL at 4BC2H, so the word on top of the stack is the dispatcher's return address 4BC5H. Register A holds the RST 28H code, Register C the sub-function and Register Pair HL the rest of the command line, as SYS1/SYS left them. Only function 7 (code E9H) exists. Register C comes from byte 1 of SYS7/SYS's entries in SYS1/SYS's command table (4F6FH-50CDH): 1 SYSTEM, 2 HIMEM, 3 PDRIVE, 4 AUTO, 5 ATTRIB, 6 PROT, 7 DUMP, 9 PURGE, 0AH TIME, 0BH DATE; 8 is the second half of DUMP, reached only through the 4978H chain set up at 513EH. Any other code or sub-function returns error 2AH.

4D00
LD IY,4280H FD 21 80 42
Load Index Register IY with 4280H, the base SYS0/SYS and the overlays use for the variables around the current drive's PDRIVE values (4280H-4287H); IY-02H is then 427EH, the current drive number, which 4D79H reads.
4D04
CP E9H FE E9
Compare Register A, the RST 28H code passed by the dispatcher from SYS1/SYS, against E9H (SYS7/SYS slot 09H, function 7): the Z FLAG is set only for the one function SYS7/SYS has.
4D06
If the NZ FLAG is set (Register A holds some other code), JUMP to 4D34H, which returns error 2AH ILLEGAL DOS FUNCTION.
4D08
DEC C 0D
Decrement Register C, the sub-function number taken from SYS1/SYS's command table; the Z FLAG is set if Register C was 1 (SYSTEM).
4D09
If the Z FLAG is set (Register C was 1, the SYSTEM command), JUMP to 4D68H to read the drive and pass the command on to SYS17/SYS.
4D0B
DEC C 0D
Decrement Register C, the sub-function number (now 1 lower than SYS1/SYS passed); the Z FLAG is set if Register C was 2 (HIMEM).
4D0C
If the Z FLAG is set (Register C was 2, the HIMEM command), JUMP to 4D7DH to display or set HIMEM (4411H).
4D0F
DEC C 0D
Decrement Register C, the sub-function number (now 2 lower than SYS1/SYS passed); the Z FLAG is set if Register C was 3 (PDRIVE).
4D10
If the Z FLAG is set (Register C was 3, the PDRIVE command), JUMP to 4D6CH to read the drive and pass the command on to SYS16/SYS.
4D12
DEC C 0D
Decrement Register C, the sub-function number (now 3 lower than SYS1/SYS passed); the Z FLAG is set if Register C was 4 (AUTO).
4D13
If the Z FLAG is set (Register C was 4, the AUTO command), JUMP to 4DDBH to store the AUTO command line in the GAT of drive 0.
4D16
DEC C 0D
Decrement Register C, the sub-function number (now 4 lower than SYS1/SYS passed); the Z FLAG is set if Register C was 5 (ATTRIB).
4D17
If the Z FLAG is set (Register C was 5, the ATTRIB command), JUMP to 4DFAH to change the attributes of the file SYS1/SYS has opened.
4D1A
DEC C 0D
Decrement Register C, the sub-function number (now 5 lower than SYS1/SYS passed); the Z FLAG is set if Register C was 6 (PROT).
4D1B
If the Z FLAG is set (Register C was 6, the PROT command), JUMP to 4ECCH to change a diskette's GAT and the passwords of its files.
4D1E
DEC C 0D
Decrement Register C, the sub-function number (now 6 lower than SYS1/SYS passed); the Z FLAG is set if Register C was 7 (DUMP).
4D1F
If the Z FLAG is set (Register C was 7, the DUMP command), JUMP to 50F4H to read the addresses and allocate the file's space.
4D22
DEC C 0D
Decrement Register C, the sub-function number (now 7 lower than SYS1/SYS passed); the Z FLAG is set if Register C was 8 (DUMP's second half).
4D23
If the Z FLAG is set (Register C was 8, which only the SYS0/SYS 4978H chain started at 513EH passes), JUMP to 5149H to write the memory to the file.
4D26
DEC C 0D
Decrement Register C, the sub-function number (now 8 lower than SYS1/SYS passed); the Z FLAG is set if Register C was 9 (PURGE).
4D27
If the Z FLAG is set (Register C was 9, the PURGE command), JUMP to 4D70H to read the drive and pass the command on to SYS3/SYS.
4D29
LD DE,4219H 11 19 42
Load Register Pair DE with 4219H, the hours byte of the clock kept in binary at 4217H-4219H (seconds 4217H, minutes 4218H, hours 4219H); TIME handles its fields from 4219H downward, and DATE changes Register E at 4D38H. LD does not change the flags.
4D2C
LD B,3AH 06 3A
Load Register B with 3AH, the colon that separates the TIME fields (HH:MM:SS); 4D41H stores it at 4DD2H, the separator operand of the loop that sets the clock. LD does not change the flags.
4D2E
LD A,36H 3E 36
Load Register A with 36H, the low byte of ROM 3036H (write the time as HH:MM:SS at Register Pair HL); 4D3EH stores it at 4D4FH, the low byte of the CALL operand at 4D4EH. LD does not change the flags.
4D30
DEC C 0D
Decrement Register C, the sub-function number (now 9 lower than SYS1/SYS passed); the Z FLAG is set if Register C was 0AH (TIME).
4D31
If the Z FLAG is set (Register C was 0AH, the TIME command), JUMP to 4D3EH with Register Pair DE = 4219H, Register B = 3AH and Register A = 36H, to store the low byte of ROM 3036H at 4D4FH.
4D33
DEC C 0D
Decrement Register C, the sub-function number (now 10 lower than SYS1/SYS passed); the Z FLAG is set if Register C was 0BH (DATE), and 4D34H falls through to DATE with it.

4D34H - Error 2AH, and the TIME and DATE Commands

4D34H is reached from 4D06H for a code that is not E9H (NZ FLAG) and falls through from 4D33H, where the Z FLAG says whether the sub-function was 0BH (DATE). TIME and DATE without a parameter display the clock as HH:MM:SS or the date as MM/DD/YY through the ROM routines at 3036H and 3033H; with a parameter they set it at 4DAEH.

4D34
LD A,2AH 3E 2A
Load Register A with 2AH, the error code ILLEGAL DOS FUNCTION, in case the next instruction leaves; LD does not change the flags, so the Z FLAG from 4D33H (or the NZ FLAG from 4D04H) is still the one tested.
4D36
If the NZ FLAG is set (the code was not E9H, or Register C was not 0BH after all the DEC C steps), JUMP to 4D65H, the error exit, with Register A = 2AH.
4D38
LD E,1CH 1E 1C
DATE: load Register E with 1CH, so that Register Pair DE (4219H from 4D29H) becomes 421CH, the month byte of the date kept in binary at 421AH-421CH (year 421AH, day 421BH, month 421CH); DATE handles its fields from 421CH downward.
4D3A
LD B,2FH 06 2F
Load Register B with 2FH, the slash that separates the DATE fields (MM/DD/YY); it replaces the colon put in Register B at 4D2CH.
4D3C
LD A,33H 3E 33
Load Register A with 33H, the low byte of ROM 3033H (write the date as MM/DD/YY at Register Pair HL), in place of the 36H loaded at 4D2EH for TIME.
4D3E
LD (4D4FH),A 32 4F 4D
Self-Modifying Code
Store Register A (36H for TIME from 4D2EH, 33H for DATE from 4D3CH) at 4D4FH, the low byte of the CALL operand at 4D4EH, so that a display calls ROM 3036H (time) or ROM 3033H (date). 4D31H enters here for TIME.
4D41
LD A,B 78
Load Register A with Register B, the field separator (3AH colon for TIME from 4D2CH, 2FH slash for DATE from 4D3AH).
4D42
LD (4DD2H),A 32 D2 4D
Self-Modifying Code
Store Register A, the separator, at 4DD2H, the operand of the CP at 4DD1H, so that the setting loop at 4DB1H checks for the right separator between the fields.
4D45
LD A,(HL) 7E
Load Register A with the byte at Register Pair HL, the next character of the command line after TIME or DATE (SYS1/SYS left Register Pair HL there).
4D46
CP 0DH FE 0D
Compare Register A, the next command line character, against 0DH, the carriage return that ends the line; the Z FLAG is set when nothing follows the command.
4D48
If the NZ FLAG is set (Register A is not the carriage return, so a time or date follows), JUMP to 4DAEH to set the clock or date from the text at Register Pair HL.
4D4A
LD HL,51E8H 21 E8 51
No parameter: load Register Pair HL with 51E8H, a text buffer just past the end of SYS7/SYS's file (51E8H-51FFH is in the overlay area but not in the file).
4D4D
PUSH HL E5
Save Register Pair HL, 51E8H (the start of the text to be built), on the stack for 4D8FH, which takes it back to display the text.
4D4E
Self-Modifying Code
GOSUB to the ROM routine named by the operand at 4D4FH-4D50H: 3036H as loaded (TIME), or 3033H after 4D3EH has stored 33H (DATE). ROM 3036H writes the hours, minutes and seconds from 4219H-4217H as HH:MM:SS, ROM 3033H the month, day and year from 421CH-421AH as MM/DD/YY, at Register Pair HL (51E8H-51EFH); Register Pair HL comes back after the last digit, and Registers A, B and C and Register Pair DE are changed.
4D51
JUMP to 4D8DH to put a carriage return at Register Pair HL (51F0H, after the last digit) and display the text from 51E8H.

4D53H - Unreferenced Error Exit

No branch, table entry or pushed address in SYS7/SYS names 4D53H, so these two rows are never executed by SYS7/SYS's own code.

4D53
LD A,2CH 3E 2C
Load Register A with 2CH, the error code BAD DIRECTORY DATA. No instruction in SYS7/SYS branches here, so this row is not executed by SYS7/SYS.
4D55
JUMP to 4D65H, the error exit, with Register A = 2CH; like 4D53H, not executed by SYS7/SYS because nothing in the file reaches 4D53H.

4D57H - Error Exits and the End of the Command

4D57H returns error 20H (from 4EA9H, a drive number of 256 or more). 4D5BH accepts the end of the line (Z FLAG) or a separator (NZ FLAG, Register Pair HL past it) and gives error 34H for anything else; 4D5FH accepts only the end of the line. 4D65H is the common error exit through SYS0/SYS 4409H, which displays the error and ends the command.

4D57
LD A,20H 3E 20
Load Register A with 20H, the error code ILLEGAL OR MISSING DRIVE #, for a drive number of 256 or more found at 4EA9H.
4D59
JUMP to 4D65H, the error exit, with Register A = 20H.
4D5B
GOSUB to SYS0/SYS 4C7AH with Register Pair HL = the command line: at the carriage return it returns the Z FLAG (and NO CARRY); after stepping Register Pair HL over a comma or spaces it returns the NZ FLAG and NO CARRY; before any other character it returns the CARRY FLAG with Register Pair HL unchanged. Register A comes back as 34H except at the carriage return.
4D5E
RET NC D0
If the NO CARRY FLAG is set (Register Pair HL is at the end of the line, Z FLAG, or past a separator, NZ FLAG), RETURN to the caller with those flags.
4D5F
LD A,(HL) 7E
Load Register A with the byte at Register Pair HL, the next command line character (the caller wants nothing more on the line).
4D60
CP 0DH FE 0D
Compare Register A, that character, against 0DH, the carriage return that ends the line; the Z FLAG is set at the end.
4D62
RET Z C8
If the Z FLAG is set (Register A is the carriage return: the line has ended as required), RETURN with the Z FLAG set and Register A = 0DH.
4D63
LD A,34H 3E 34
Load Register A with 34H, the error code ILLEGAL KEYWORD OR SEPARATOR OR TERMINATOR: the character at Register Pair HL is not allowed here (also reached from 4F97H when no keyword matched).
4D65
JUMP to SYS0/SYS 4409H, the DOS error exit, with the error code in Register A; 4409H passes it to SYS4/SYS (code 26H), which displays the message and ends the command (or returns it to a DOS-CALL caller).

4D68H - SYSTEM, PDRIVE and PURGE Commands

These three read only [password]:d or d and check the diskette password at 4E90H; then they pass the rest of the command line to another overlay with Register B = the drive (427EH): SYSTEM to SYS17/SYS function 1 (code 33H), PDRIVE to SYS16/SYS function 1 (code 32H), PURGE to SYS3/SYS function 7 sub-function 6 (code E5H, Register C = 06H).

4D68
LD B,33H 06 33
SYSTEM: load Register B with 33H, the RST 28H code of SYS17/SYS function 1 (slot 13H, function 1), which performs the SYSTEM command.
4D6A
JUMP to 4D73H with Register B = 33H, the SYS17/SYS code; Register C is not used by SYS17/SYS function 1.
4D6C
LD B,32H 06 32
PDRIVE: load Register B with 32H, the RST 28H code of SYS16/SYS function 1 (slot 12H, function 1), which performs the PDRIVE command.
4D6E
JUMP to 4D73H with Register B = 32H, the SYS16/SYS code; Register C is not used by SYS16/SYS function 1.
4D70
LD BC,E506H 01 06 E5
PURGE: load Register Pair BC with E506H: Register B = E5H, the RST 28H code of SYS3/SYS function 7, and Register C = 06H, SYS3/SYS's PURGE sub-function.
4D73
PUSH BC C5
Save Register Pair BC (Register B = the RST 28H code, Register C = the sub-function for PURGE) on the stack, because 4E90H uses Register Pair BC.
4D74
GOSUB to 4E90H to read [password]:d at Register Pair HL, select the drive, read its GAT into 4300H and check the diskette password; Register Pair HL comes back after the drive number.
4D77
POP BC C1
Restore Register Pair BC from the stack: Register B = the RST 28H code (33H, 32H or E5H) and Register C = 06H for PURGE.
4D78
LD A,B 78
Load Register A with Register B, the RST 28H code of the overlay that does the work.
4D79
LD B,(IY-02H) FD 46 FE
Load Register B with the byte at Index Register IY-02H, 427EH: the current drive, which SYS0/SYS 4723H stored when 4E90H selected the drive typed; the other overlay takes the drive from Register B.
4D7C
RST 28H EF
RST 28H with Register A = the code (33H, 32H or E5H): the SYS0/SYS dispatcher drops the RST's return address, loads the other overlay over SYS7/SYS and runs it with Register B = the drive, Register C = the sub-function (06H for PURGE) and Register Pair HL = the rest of the command line; SYS7/SYS does not run again for this command.

4D7DH - HIMEM Command

Without a parameter HIMEM displays the address held in 4411H (HIMEM) as four hexadecimal digits. With one it stores the value, which may be decimal or hexadecimal with a trailing H (4FA5H); it must be 7000H or higher, and it is lowered to the top of memory found at reset (42C9H) if it is above it.

4D7D
LD A,(HL) 7E
Load Register A with the byte at Register Pair HL, the next command line character after HIMEM (SYS1/SYS left Register Pair HL there).
4D7E
CP 0DH FE 0D
Compare Register A, that character, against 0DH, the carriage return that ends the line; the Z FLAG is set when no value follows.
4D80
If the NZ FLAG is set (Register A is not the carriage return, so a value follows), JUMP to 4D93H to set HIMEM.
4D82
LD DE,(4411H) ED 5B 11 44
Load Register Pair DE with the 16-bit value at 4411H, HIMEM: the highest address free for programs, set at reset and by HIMEM.
4D86
LD HL,51E8H 21 E8 51
Load Register Pair HL with 51E8H, a text buffer just past the end of SYS7/SYS's file, where the four hexadecimal digits are built.
4D89
PUSH HL E5
Save Register Pair HL, 51E8H (the start of the text), on the stack for 4D8FH, which takes it back to display the text.
4D8A
GOSUB to SYS0/SYS 44D2H to write Register Pair DE (HIMEM) as four hexadecimal digits at Register Pair HL (51E8H-51EBH); Register Pair HL comes back past the last digit (51ECH).
4D8D
LD (HL),0DH 36 0D
Store 0DH (a carriage return) at Register Pair HL, the byte after the last digit, to end the text (TIME and DATE also arrive here from 4D51H).
4D8F
POP HL E1
Restore Register Pair HL from the stack: 51E8H, the start of the text saved at 4D4DH or 4D89H.
4D90
JUMP to SYS0/SYS 4467H to display the text at Register Pair HL up to and including the carriage return; it returns with the Z FLAG set (from its CP 0DH) through the dispatcher's return address 4BC5H to SYS0/SYS 4408H, which takes the Z FLAG as no error.
4D93
GOSUB to 4FA5H to read the number at Register Pair HL (decimal, or hexadecimal with a trailing H) into Register Pair DE; a bad number gives error 2FH there.
4D96
GOSUB to 4D5FH: the line must end after the number at Register Pair HL, else error 34H.
4D99
LD A,D 7A
Load Register A with Register D, the high byte of the new HIMEM value in Register Pair DE.
4D9A
CP 70H FE 70
Compare Register A, the high byte of the value, against 70H: the CARRY FLAG is set if the value in Register Pair DE is below 7000H.
4D9C
If the CARRY FLAG is set (the value in Register Pair DE is below 7000H), JUMP to 4DD7H for error 2FH BAD PARAMETER(S).
4D9F
LD HL,(42C9H) 2A C9 42
Load Register Pair HL with the 16-bit value at 42C9H, the top of memory that the SYS0/SYS reset code found (HIMEM may not be set above it).
4DA2
OR A B7
OR Register A with itself to clear the CARRY FLAG before the SBC; Register A (the value's high byte) is not changed.
4DA3
SBC HL,DE ED 52
Subtract Register Pair DE (the new value) from Register Pair HL (the top of memory from 42C9H): the CARRY FLAG is set if the value is above the top of memory.
4DA5
ADD HL,DE 19
Add Register Pair DE (the new value) back to Register Pair HL, which is the top of memory again; the ADD carries exactly when the SBC borrowed, so the CARRY FLAG still says whether the value was above the top.
4DA6
If the CARRY FLAG is set (the value in Register Pair DE is above the top of memory), JUMP to 4DA9H to store the top of memory in Register Pair HL instead.
4DA8
EX DE,HL EB
Exchange Register Pairs DE and HL: Register Pair HL = the new value (accepted as it is) and Register Pair DE = the top of memory.
4DA9
LD (4411H),HL 22 11 44
Store Register Pair HL (the accepted value, or the top of memory from 42C9H) at 4411H, HIMEM.
4DAC
XOR A AF
Load Register A with 00H (XOR A with itself), which also sets the Z FLAG: the no-error result of the command.
4DAD
RET C9
RETURN through the dispatcher's return address 4BC5H to SYS0/SYS 4408H with Register A = 00H and the Z FLAG set: HIMEM has been set.

4DAEH - TIME and DATE: Set the Clock or Date

Three fields of two decimal digits, separated by the character stored at 4DD2H (: or /), are stored downward from Register Pair DE: hours, minutes and seconds at 4219H-4217H, or month, day and year at 421CH-421AH. The values are not range-checked. Interrupts are off while the fields are stored, so the clock does not tick in between. A field that is not two digits, or a wrong separator, gives error 2FH; anything but the end of the line after the third field gives error 34H, after the values are already stored.

4DAE
DI F3
Disable interrupts, so that the clock interrupt (SYS0/SYS 4551H, which runs the ROM clock routine that counts 4217H-421CH) cannot change the clock or date while it is being set.
4DAF
LD B,03H 06 03
Load Register B with 03H, the number of two-digit fields to read (hours, minutes, seconds or month, day, year).

Field Loop
Register Pair HL = the command line, Register Pair DE = the clock or date byte for this field, Register B = the fields left.

4DB1
LD A,(HL) 7E
Load Register A with the byte at Register Pair HL, the first (tens) digit of the field.
4DB2
SUB 30H D6 30
Subtract 30H from Register A, the character: the digits 0-9 become 00H-09H, anything else a value of 0AH or more (or a borrow).
4DB4
CP 0AH FE 0A
Compare Register A, the converted character, against 0AH: the CARRY FLAG is set only for a digit 0-9.
4DB6
INC HL 23
Increment Register Pair HL past the tens digit in the command line; INC HL does not change the flags.
4DB7
If the NO CARRY FLAG is set (Register A was not a digit), JUMP to 4DD6H to enable interrupts and give error 2FH.
4DB9
LD C,A 4F
Load Register C with Register A, the tens digit (00H-09H), to be multiplied by 10.
4DBA
RLCA 07
Rotate Register A (the tens digit, 00H-09H) left: Register A = the digit times 2; no bit leaves the byte for values this small.
4DBB
RLCA 07
Rotate Register A left again: Register A = the tens digit times 4.
4DBC
ADD A,C 81
Add Register C (the tens digit) to Register A (the digit times 4): Register A = the tens digit times 5.
4DBD
ADD A,A 87
Add Register A to itself: Register A = the tens digit times 10, the value of the tens place.
4DBE
LD C,A 4F
Load Register C with Register A, the tens place value (0-90), to add the units digit to it.
4DBF
LD A,(HL) 7E
Load Register A with the byte at Register Pair HL, the second (units) digit of the field.
4DC0
SUB 30H D6 30
Subtract 30H from Register A, the character: the digits 0-9 become 00H-09H.
4DC2
CP 0AH FE 0A
Compare Register A, the converted character, against 0AH: the CARRY FLAG is set only for a digit 0-9.
4DC4
INC HL 23
Increment Register Pair HL past the units digit in the command line; INC HL does not change the flags.
4DC5
If the NO CARRY FLAG is set (Register A was not a digit), JUMP to 4DD6H to enable interrupts and give error 2FH.
4DC7
ADD A,C 81
Add Register C (the tens place value) to Register A (the units digit): Register A = the field's binary value, 0-99.
4DC8
LD (DE),A 12
Store Register A, the field's value, at Register Pair DE, the clock or date byte (4219H or 421CH first, then the lower ones).
4DC9
DEC DE 1B
Decrement Register Pair DE to the next clock or date byte down (minutes then seconds, or day then year).
4DCA
Decrement Register B, the fields left; if Register B is not 0, JUMP to 4DD0H to check the separator before the next field.
4DCC
EI FB
All three fields are stored: enable interrupts again, so that the clock interrupt resumes counting from the new values at 4217H-421CH.
4DCD
JUMP to SYS0/SYS 4C7AH with Register Pair HL after the third field: at the carriage return it returns the Z FLAG (no error) through the dispatcher's return address 4BC5H to SYS0/SYS 4408H; otherwise it returns the NZ FLAG with Register A = 34H, error 34H for the command.
4DD0
LD A,(HL) 7E
Load Register A with the byte at Register Pair HL, the character after the field, which must be the separator.
4DD1
CP 00H FE 00
Self-Modifying Code
Compare Register A, that character, against the separator in the operand at 4DD2H (00H in the file; 4D42H stores 3AH for TIME or 2FH for DATE); the Z FLAG is set if it matches.
4DD3
INC HL 23
Increment Register Pair HL past the separator; INC HL does not change the flags.
4DD4
If the Z FLAG is set (Register A was the right separator), LOOP back to 4DB1H for the next field, with Register Pair DE at the next byte down.
4DD6
EI FB
Enable interrupts again before reporting the error (a field that was not two digits, or a wrong separator); fields already read stay stored.
4DD7
LD A,2FH 3E 2F
Load Register A with 2FH, the error code BAD PARAMETER(S); also the error entry for every other bad value in SYS7/SYS (HIMEM below 7000H, AUTO too long, LRL=, Y/N, numbers, DUMP addresses).
4DD9
JUMP to 4D65H, the error exit, with Register A = 2FH.

4DDBH - AUTO Command

Stores the rest of the command line, up to and including its carriage return, in bytes E0H-FFH of the GAT of drive 0: the command the reset code runs automatically. At most 31 characters fit with the carriage return; longer text gives error 2FH and nothing is written. AUTO with nothing after it stores only the carriage return, which removes the AUTO command.

4DDB
EX DE,HL EB
Exchange Register Pairs DE and HL: Register Pair DE = the command line after AUTO (from SYS1/SYS), because Register Pair HL is needed for the GAT.
4DDC
XOR A AF
Load Register A with 00H (XOR A with itself): drive 0, the system diskette whose GAT holds the AUTO command.
4DDD
GOSUB to SYS0/SYS 4723H to select the drive in Register A (drive 0); it returns the NZ FLAG with an error code in Register A if the drive cannot be used.
4DE0
RET NZ C0
If the NZ FLAG is set (drive 0 could not be selected), RETURN through the dispatcher's return address 4BC5H to SYS0/SYS 4408H with the error code in Register A.
4DE1
XOR A AF
Load Register A with 00H (XOR A with itself): directory sector 0 of the diskette, the GAT.
4DE2
GOSUB to SYS0/SYS 48AFH to read directory sector Register A (0, the GAT) into the sector buffer at 4300H; Register Pair HL comes back as 4300H.
4DE5
RET NZ C0
If the NZ FLAG is set (the GAT could not be read), RETURN through the dispatcher's return address 4BC5H to SYS0/SYS 4408H with the error code in Register A.
4DE6
LD B,20H 06 20
Load Register B with 20H, the 32 bytes of GAT E0H-FFH: room for 31 characters and the carriage return.
4DE8
LD L,E0H 2E E0
Load Register L with E0H: Register Pair HL (4300H from 48AFH) becomes 43E0H, GAT byte E0H, where the AUTO command starts.

Copy Loop
Register Pair DE = the command line, Register Pair HL = the GAT byte (43E0H-43FFH), Register B = the bytes of room left.

4DEA
LD A,(DE) 1A
Load Register A with the byte at Register Pair DE, the next character of the AUTO text.
4DEB
CP 0DH FE 0D
Compare Register A, that character, against 0DH, the carriage return that ends the line; the Z FLAG is set at the end.
4DED
LD (HL),A 77
Store Register A (the character, or the final carriage return) at Register Pair HL in the GAT copy at 43E0H-43FFH; LD does not change the flags.
4DEE
INC DE 13
Increment Register Pair DE to the next character of the command line.
4DEF
INC HL 23
Increment Register Pair HL to the next GAT byte.
4DF0
If the Z FLAG is set (the carriage return in Register A has been stored), JUMP to 4DF7H to write the GAT back.
4DF2
Decrement Register B, the bytes of room left; if Register B is not 0, LOOP back to 4DEAH for the next character.
4DF4
The 32 bytes are used and no carriage return was stored: JUMP to 4DD7H for error 2FH BAD PARAMETER(S); the GAT in 4300H is not written.
4DF7
JUMP to SYS0/SYS 48C4H to write the directory sector now in 4300H (the GAT for AUTO, the FPDE's sector for ATTRIB from 4E68H) back with the directory mark and the verify read; it returns the Z FLAG, or the NZ FLAG with error 12H, to the command's return address.

4DFAH - ATTRIB Command

SYS1/SYS has opened the file (FCB at Register Pair DE) and, because its command table entry has bit 3 of byte 3 set, left the command line pointer on the stack under the dispatcher's return address 4BC5H. ATTRIB drops 4BC5H, so it returns straight to the command's return address, SYS0/SYS 4408H. The file must have been opened with full access (access level 0: its update password, or any password when option AA is off), otherwise error 19H. Its FPDE is read and changed by the keywords in the table at 5054H, separated by commas or spaces; then the directory sector is written back. Keywords: INV and VIS (invisible bit), PROT=level (5093H), ACC=password, UPD=password, ASE=Y/N, ASC=Y/N, UDF=Y/N and LRL=n.

4DFA
POP AF F1
Restore Register Pair AF from the stack, discarding the dispatcher's return address 4BC5H (pushed by the CALL at 4BC2H), so that the command line pointer SYS1/SYS left is on top.
4DFB
POP HL E1
Restore Register Pair HL from the stack: the command line pointer left by SYS1/SYS, at the character after the filespec.
4DFC
GOSUB to 4F72H: a comma or spaces must follow the filespec at Register Pair HL (else error 34H); Register Pair HL comes back past them, at the first keyword.
4DFF
PUSH DE D5
Save Register Pair DE, the FCB address from SYS1/SYS, on the stack to move it into Index Register IX.
4E00
POP IX DD E1
Restore Index Register IX from the stack: Index Register IX = the FCB, which SYS0/SYS 48F0H (4E0AH) needs.
4E02
INC DE 13
Increment Register Pair DE (the FCB address) to FCB+1, which holds the access level granted when the file was opened.
4E03
LD A,(DE) 1A
Load Register A with the byte at Register Pair DE, FCB+1: bit 7 record mode, bits 5-3 buffer flags, bits 0-2 the access level.
4E04
AND 07H E6 07
Mask Register A (FCB+1) against 07H (0000 0111) to keep bits 0-2, the access level; the Z FLAG is set for level 0, FULL access.
4E06
LD A,19H 3E 19
Load Register A with 19H, the error code FILE ACCESS DENIED, in case the next instruction leaves; LD does not change the flags.
4E08
RET NZ C0
If the NZ FLAG is set (the file was opened with a level other than FULL), RETURN to the command's return address, SYS0/SYS 4408H, with error 19H in Register A.
4E09
PUSH HL E5
Save Register Pair HL, the command line pointer at the first keyword, on the stack (48F0H uses Register Pair HL).
4E0A
GOSUB to SYS0/SYS 48F0H to read the file's FPDE (using the FCB at Index Register IX): Register Pair HL comes back pointing at the entry in the directory sector at 4300H-43FFH.
4E0D
If the NZ FLAG is set (the FPDE could not be read or does not match the FCB), JUMP to 4D65H, the error exit, with the error code in Register A.
4E10
EX (SP),HL E3
Exchange Register Pair HL with the word at the top of the stack (at the Stack Pointer SP): Register Pair HL = the command line pointer again, and the FPDE's address is kept on the stack for the keyword handlers.
4E11
LD BC,5054H 01 54 50
Load Register Pair BC with 5054H, the ATTRIB keyword table (4E65H loops back here for each further keyword).
4E14
JUMP to 4F78H to find the keyword at Register Pair HL in the table at Register Pair BC and jump to its handler with Register Pair HL past the keyword.

4E17H - ATTRIB LRL=

LRL=n sets the record length (FPDE byte 4) to n, 1-256; 256 is stored as 00H. Other values give error 2FH.

4E17
GOSUB to 4FA5H to read the number after LRL= at Register Pair HL into Register Pair DE (decimal, or hexadecimal with a trailing H).
4E1A
DEC DE 1B
Decrement Register Pair DE, the record length: 1-256 become 0000H-00FFH, so a valid length has Register D = 00H.
4E1B
LD A,D 7A
Load Register A with Register D, the high byte of the record length minus 1.
4E1C
OR A B7
OR Register A (the high byte) with itself: the Z FLAG is set only if Register D is 00H, that is for a record length of 1-256.
4E1D
If the NZ FLAG is set (the number in Register Pair DE was 0 or above 256), JUMP to 4DD7H for error 2FH BAD PARAMETER(S).
4E1F
EX (SP),HL E3
Exchange Register Pair HL with the word at the top of the stack (at the Stack Pointer SP): Register Pair HL = the FPDE's address, and the command line pointer is kept on the stack.
4E20
PUSH HL E5
Save Register Pair HL, the FPDE's address (byte 0 of the entry), on the stack again.
4E21
INC HL 23
Increment Register Pair HL, the FPDE pointer, to byte 1 of the entry (the second flag byte).
4E22
INC HL 23
Increment Register Pair HL, the FPDE pointer, to byte 2 of the entry.
4E23
INC HL 23
Increment Register Pair HL, the FPDE pointer, to byte 3 of the entry (the EOF low byte).
4E24
INC HL 23
Increment Register Pair HL, the FPDE pointer, to byte 4 of the entry, the record length (LRL).
4E25
INC E 1C
Increment Register E, the record length minus 1: the 1 is added back, and a length of 256 becomes 00H.
4E26
LD (HL),E 73
Store Register E, the record length (00H for 256), at Register Pair HL, byte 4 of the FPDE.
4E27
POP HL E1
Restore Register Pair HL from the stack: the FPDE's address (byte 0 of the entry).
4E28
JUMP to 4E51H to put the FPDE's address back on the stack and look for the next keyword.

4E2AH - ATTRIB UDF=, ASE= and ASC=

Each changes one bit of FPDE byte 1: UDF (bit 5, the file has been updated) is set by Y and cleared by N; ASE (bit 7, the file may not be extended) and ASC (bit 6, CLOSE keeps the space beyond EOF) are cleared by Y and set by N, because the bits stand for the opposite of the keyword. Anything but Y or N gives error 2FH.

4E2A
LD C,20H 0E 20
UDF=: load Register C with 20H (0010 0000), bit 5 of FPDE byte 1, the bit that says the file has been updated.
4E2C
LD DE,FF00H 11 00 FF
Load Register Pair DE with FF00H: Register D = FFH (the bit is set) for Y, Register E = 00H (the bit is cleared) for N.
4E2F
JUMP to 4E3AH to read the Y or N with Register C = 20H and Register Pair DE = FF00H.
4E31
LD C,80H 0E 80
ASE=: load Register C with 80H (1000 0000), bit 7 of FPDE byte 1, the bit that stops the file from being extended.
4E33
JUMP to 4E37H to load the Y/N values for an inverted bit, with Register C = 80H.
4E35
LD C,40H 0E 40
ASC=: load Register C with 40H (0100 0000), bit 6 of FPDE byte 1, the bit that stops CLOSE from releasing the space beyond EOF.
4E37
LD DE,00FFH 11 FF 00
Load Register Pair DE with 00FFH: Register D = 00H (the bit is cleared) for Y, Register E = FFH (the bit is set) for N.
4E3A
LD A,(HL) 7E
Load Register A with the byte at Register Pair HL, the character after the = of the keyword.
4E3B
CP 59H FE 59
Compare Register A, that character, against 59H, the letter Y; the Z FLAG is set for Y.
4E3D
INC HL 23
Increment Register Pair HL past the Y or N in the command line; INC HL does not change the flags.
4E3E
If the Z FLAG is set (Register A was Y), JUMP to 4E45H with the Y value already in Register D.
4E40
LD D,E 53
Load Register D with Register E, the N value, in case the character is N.
4E41
CP 4EH FE 4E
Compare Register A, the character, against 4EH, the letter N; the Z FLAG is set for N.
4E43
If the NZ FLAG is set (Register A is neither Y nor N), JUMP to 4DD7H for error 2FH BAD PARAMETER(S).
4E45
EX (SP),HL E3
Exchange Register Pair HL with the word at the top of the stack (at the Stack Pointer SP): Register Pair HL = the FPDE's address, and the command line pointer is kept on the stack.
4E46
INC HL 23
Increment Register Pair HL, the FPDE pointer, to byte 1 of the entry, the flag byte with bits 7, 6 and 5.
4E47
LD A,C 79
Load Register A with Register C, the single bit to change (80H, 40H or 20H).
4E48
AND D A2
AND Register A (the bit) with Register D, the value FFH or 00H chosen by the Y or N: Register A = the bit if it is to be set, else 00H.
4E49
LD B,A 47
Load Register B with Register A, the new state of the bit (the bit itself or 00H).
4E4A
LD A,FFH 3E FF
Load Register A with FFH, all bits set, to build the mask that keeps the other bits of byte 1.
4E4C
XOR C A9
XOR Register A (FFH) with Register C, the bit: Register A = every bit except that one, the mask that keeps the other bits.
4E4D
AND (HL) A6
AND Register A (the mask) with the byte at Register Pair HL, FPDE byte 1: Register A = byte 1 with the bit cleared.
4E4E
OR B B0
OR Register A (byte 1 with the bit cleared) with Register B, the new state of the bit: Register A = the new byte 1.
4E4F
LD (HL),A 77
Store Register A, the new flag byte, at Register Pair HL, byte 1 of the FPDE.
4E50
DEC HL 2B
Decrement Register Pair HL, the FPDE pointer, back to byte 0 of the entry, the address kept on the stack.
4E51
EX (SP),HL E3
Exchange Register Pair HL with the word at the top of the stack (at the Stack Pointer SP): Register Pair HL = the command line pointer again, and the FPDE's address is kept on the stack.
4E52
JUMP to 4E62H to look for a separator and the next keyword at Register Pair HL, or the end of the line.

4E54H - ATTRIB INV, VIS and PROT=

INV sets and VIS clears bit 3 of FPDE byte 0 (invisible). PROT=level stores the access level (LOCK 7, EXEC 6, READ 5, WRITE 4, RENAME or NAME 2, KILL 1, FULL 0; table 5093H) in bits 0-2 of byte 0. 4E62H then continues with the next keyword, or at the end of the line writes the directory sector.

4E54
LD C,08H 0E 08
INV: load Register C with 08H (0000 1000), bit 3 of FPDE byte 0 set: the file becomes invisible to DIR without the I option.
4E56
JUMP to 4E5AH with the new bit in Register C (08H).
4E58
LD C,00H 0E 00
VIS: load Register C with 00H, bit 3 of FPDE byte 0 clear: the file becomes visible.
4E5A
LD B,F7H 06 F7
Load Register B with F7H (1111 0111), the mask that keeps every bit of FPDE byte 0 except bit 3.
4E5C
POP DE D1
Restore Register Pair DE from the stack: the FPDE's address, kept there since 4E10H (4E76H also enters here for PROT= with Register B = F8H and Register C = the level).
4E5D
LD A,(DE) 1A
Load Register A with the byte at Register Pair DE, byte 0 of the FPDE (bit 7 FXDE, 6 system, 4 in use, 3 invisible, 2-0 access level).
4E5E
AND B A0
AND Register A (FPDE byte 0) with Register B, the mask: the bits being replaced are cleared, the others kept.
4E5F
OR C B1
OR Register A with Register C, the new bits (08H or 00H for INV/VIS, the level 0-7 for PROT=): Register A = the new byte 0.
4E60
LD (DE),A 12
Store Register A, the new flag byte, at Register Pair DE, byte 0 of the FPDE.
4E61
PUSH DE D5
Save Register Pair DE (the FPDE's address) on the stack again for the next keyword.
4E62
GOSUB to 4D5BH with Register Pair HL = the command line: the Z FLAG at the end of the line, the NZ FLAG with Register Pair HL past a comma or spaces; any other character gives error 34H.
4E65
If the NZ FLAG is set (a separator was passed, so another keyword follows), LOOP back to 4E11H for the next keyword at Register Pair HL.
4E67
POP AF F1
The end of the line: restore Register Pair AF from the stack, discarding the FPDE's address; the sector in 4300H already holds every change.
4E68
JUMP to 4DF7H, which jumps to SYS0/SYS 48C4H to write the directory sector in 4300H back.
4E6A
LD BC,5093H 01 93 50
PROT=: load Register Pair BC with 5093H, the access level table (level names, each followed by 00H and the level byte).
4E6D
LD D,01H 16 01
Load Register D with 01H: one byte, the level, follows each name in that table (4F84H skips Register D bytes after a name).
4E6F
GOSUB to 4F84H to find the level name at Register Pair HL in the table at Register Pair BC; error 34H if none matches, else Register Pair BC comes back at the name's level byte and Register Pair HL past the name.
4E72
LD A,(BC) 0A
Load Register A with the byte at Register Pair BC, the access level 0-7 for the name found.
4E73
LD C,A 4F
Load Register C with Register A, the level, for bits 0-2 of FPDE byte 0.
4E74
LD B,F8H 06 F8
Load Register B with F8H (1111 1000), the mask that keeps bits 3-7 of FPDE byte 0.
4E76
JUMP to 4E5CH to merge Register C (the level) into byte 0 of the FPDE with the mask in Register B.

4E78H - ATTRIB ACC= and UPD=

ACC=password stores the password's code at FPDE bytes 18-19 (the access password), UPD=password at bytes 16-17 (the update password). A blank password (nothing after the =) gives the code 4296H.

4E78
LD A,12H 3E 12
ACC=: load Register A with 12H, the offset of the access password code (bytes 18-19) in the FPDE.
4E7A
JUMP to 4E7EH with the offset 12H in Register A.
4E7C
LD A,10H 3E 10
UPD=: load Register A with 10H, the offset of the update password code (bytes 16-17) in the FPDE.
4E7E
PUSH AF F5
Save Register Pair AF (Register A = the offset 10H or 12H) on the stack, because 5005H uses Register A.
4E7F
GOSUB to 5005H to read the password at Register Pair HL and return its 16-bit code in Register Pair DE, with Register Pair HL after the password.
4E82
PUSH HL E5
Save Register Pair HL, the command line pointer after the password, on the stack to move it into Register Pair BC.
4E83
POP BC C1
Restore Register Pair BC from the stack: Register Pair BC = the command line pointer, kept there while Register Pair HL addresses the FPDE.
4E84
POP AF F1
Restore Register Pair AF from the stack: Register A = the offset of the password field (10H or 12H).
4E85
POP HL E1
Restore Register Pair HL from the stack: the FPDE's address (byte 0 of the entry), kept there since 4E10H.
4E86
PUSH HL E5
Save Register Pair HL, the FPDE's address, on the stack again for the next keyword.
4E87
ADD A,L 85
Add Register L (the low byte of the FPDE's address) to Register A (the offset): Register A = the low byte of the password field's address. An entry starts at a multiple of 20H, so this cannot carry.
4E88
LD L,A 6F
Load Register L with Register A: Register Pair HL = the password code field in the FPDE (byte 16 or 18).
4E89
LD (HL),E 73
Store Register E, the low byte of the password code, at Register Pair HL.
4E8A
INC HL 23
Increment Register Pair HL to the high byte of the password code field (byte 17 or 19).
4E8B
LD (HL),D 72
Store Register D, the high byte of the password code, at Register Pair HL.
4E8C
LD H,B 60
Load Register H with Register B, the high byte of the command line pointer.
4E8D
LD L,C 69
Load Register L with Register C, the low byte: Register Pair HL = the command line pointer after the password.
4E8E
JUMP to 4E62H to look for a separator and the next keyword at Register Pair HL, or the end of the line.

4E90H - Read a Drive and Check the Diskette Password

Used by PROT, SYSTEM, PDRIVE and PURGE. The text is password:d, :d or d. The drive is selected (SYS0/SYS 4791H, error 08H if no diskette turns) and its GAT is read into 4300H. The password code read (4296H, the blank code, when no password was typed) is then compared with the diskette password code in GAT bytes CEH-CFH; when they differ the result is error 37H, but only while option AA (bit 7 of 428CH) is on. So with option AA on, a diskette that has a password needs it even when none is typed. Returns with Register Pair HL after the drive number.

4E90
GOSUB to 5005H to read a name at Register Pair HL and return its password code in Register Pair DE; if the text does not start with a letter nothing is taken and Register Pair DE = 4296H, the blank code.
4E93
LD A,(HL) 7E
Load Register A with the byte at Register Pair HL, the character after the name (or the first character when there was no name).
4E94
CP 3AH FE 3A
Compare Register A, that character, against 3AH, a colon; the Z FLAG is set when a colon separates the password from the drive number.
4E96
INC HL 23
Increment Register Pair HL past the character; INC HL does not change the flags.
4E97
If the Z FLAG is set (Register A was a colon), JUMP to 4EA3H to read the drive number after it.
4E99
PUSH HL E5
No colon: save Register Pair HL, the command line pointer one past the character, on the stack (RST 18H needs Register Pair HL).
4E9A
LD HL,4296H 21 96 42
Load Register Pair HL with 4296H, the code of a blank password, to find out whether a name was read.
4E9D
RST 18H DF
RST 18H: compare Register Pair HL (4296H) with Register Pair DE (the code read at 4E90H); the Z FLAG is set if they are equal, that is if no name was given.
4E9E
POP HL E1
Restore Register Pair HL from the stack: the command line pointer one past the character; POP does not change the flags.
4E9F
DEC HL 2B
Decrement Register Pair HL back to the character, which is the drive number; DEC HL does not change the flags.
4EA0
If the NZ FLAG is set (Register Pair DE holds the code of a name, but no colon followed it), JUMP to 4DD7H for error 2FH BAD PARAMETER(S).
4EA3
PUSH DE D5
Save Register Pair DE, the password code (4296H if none was typed), on the stack, because 4FA5H returns the drive number in Register Pair DE.
4EA4
GOSUB to 4FA5H to read the drive number at Register Pair HL into Register Pair DE (decimal, or hexadecimal with a trailing H); error 2FH if there is no number.
4EA7
LD A,D 7A
Load Register A with Register D, the high byte of the drive number.
4EA8
OR A B7
OR Register A (the high byte) with itself: the NZ FLAG is set for a drive number of 256 or more.
4EA9
If the NZ FLAG is set (Register Pair DE is 256 or more), JUMP to 4D57H for error 20H ILLEGAL OR MISSING DRIVE #.
4EAC
LD A,E 7B
Load Register A with Register E, the drive number (0-255; SYS0/SYS checks it against option AL).
4EAD
POP DE D1
Restore Register Pair DE from the stack: the password code saved at 4EA3H.
4EAE
GOSUB to SYS0/SYS 4791H to select the drive in Register A and wait for the index hole to show that a diskette turns; it returns the NZ FLAG with error 20H (drive too high) or 08H (no diskette) in Register A.
4EB1
If the NZ FLAG is set (the drive cannot be used), JUMP to 4EC9H to give the error in Register A.
4EB3
PUSH HL E5
Save Register Pair HL, the command line pointer after the drive number, on the stack (48AFH returns Register Pair HL = 4300H).
4EB4
XOR A AF
Load Register A with 00H (XOR A with itself): directory sector 0, the GAT.
4EB5
GOSUB to SYS0/SYS 48AFH to read directory sector Register A (0, the GAT) of the selected drive into 4300H.
4EB8
If the NZ FLAG is set (the GAT could not be read), JUMP to 4EC9H to give the error code in Register A.
4EBA
LD HL,(43CEH) 2A CE 43
Load Register Pair HL with the 16-bit value at 43CEH, GAT bytes CEH-CFH: the diskette password code (4296H when the diskette has no password).
4EBD
OR A B7
OR Register A with itself to clear the CARRY FLAG before the SBC (Register A, 00H after the good read, is not changed).
4EBE
SBC HL,DE ED 52
Subtract Register Pair DE (the code typed, or 4296H) from Register Pair HL (the diskette's code): the Z FLAG is set if they match.
4EC0
POP HL E1
Restore Register Pair HL from the stack: the command line pointer after the drive number; POP does not change the flags.
4EC1
RET Z C8
If the Z FLAG is set (the codes match), RETURN to the caller with Register Pair HL after the drive number.
4EC2
LD A,(428CH) 3A 8C 42
Load Register A with the byte at 428CH, the copy of system data sector byte F0H made at reset: bit 7 is option AA (passwords are checked).
4EC5
RLCA 07
Rotate Register A (the 428CH flags) left: bit 7, option AA, goes into the CARRY FLAG.
4EC6
RET NC D0
If the NO CARRY FLAG is set (option AA is off), RETURN with Register Pair HL after the drive number: the password is not checked. The NZ FLAG from 4EBEH is still set and Register A holds the rotated flags, but neither caller tests them.
4EC7
LD A,37H 3E 37
Load Register A with 37H, the error code DISKETTE ACCESS DENIED: option AA is on and the password code does not match the diskette's.
4EC9
JUMP to 4D65H, the error exit, with the error code in Register A.

4ECCH - PROT Command

PROT [password]:d,keyword,... changes the diskette's GAT: PW=password (a new diskette password), NAME=name and DATE=date (8 characters each); and it applies LOCK, UNLOCK and RUF to all the diskette's files after the last keyword (4F10H). The keywords are in the table at 50C7H.

4ECC
GOSUB to 4E90H to read [password]:d at Register Pair HL, select the drive, read its GAT into 4300H and check the diskette password; Register Pair HL comes back after the drive number.
4ECF
GOSUB to 4F72H: a comma or spaces must follow the drive number at Register Pair HL (else error 34H); Register Pair HL comes back at the first keyword.
4ED2
LD BC,50C7H 01 C7 50
Load Register Pair BC with 50C7H, the PROT keyword table (4F13H loops back here for each further keyword).
4ED5
JUMP to 4F78H to find the keyword at Register Pair HL in the table at Register Pair BC and jump to its handler with Register Pair HL past the keyword.

4ED8H - PROT DATE= and NAME=

Copies up to 8 characters, up to a comma, a space or the end of the line, to GAT bytes D8H-DFH (DATE=) or D0H-D7H (NAME=), padded with spaces. Characters after the eighth are left in the line, where 4F10H finds them and gives error 34H.

4ED8
LD DE,43D8H 11 D8 43
DATE=: load Register Pair DE with 43D8H, GAT bytes D8H-DFH in the sector buffer: the diskette's date.
4EDB
JUMP to 4EE0H with the field address 43D8H in Register Pair DE.
4EDD
LD DE,43D0H 11 D0 43
NAME=: load Register Pair DE with 43D0H, GAT bytes D0H-D7H in the sector buffer: the diskette's name.
4EE0
LD B,08H 06 08
Load Register B with 08H, the eight characters of the name or date field.

Field Loop
Register Pair HL = the command line, Register Pair DE = the GAT byte, Register B = the field positions left.

4EE2
GOSUB to SYS0/SYS 4C7AH with Register Pair HL = the command line: the NO CARRY FLAG at the end of the line (Z FLAG) or after stepping over a comma or spaces (NZ FLAG); the CARRY FLAG before any other character.
4EE5
If the NO CARRY FLAG is set (the field text has ended), JUMP to 4EEBH to pad the rest of the field with spaces.
4EE7
LD A,(HL) 7E
An ordinary character: load Register A with the byte at Register Pair HL, the next character of the field.
4EE8
INC HL 23
Increment Register Pair HL past that character in the command line.
4EE9
JUMP to 4EF0H to store the character in Register A.
4EEB
If the Z FLAG is set (Register Pair HL is at the carriage return), JUMP to 4EEEH; Register Pair HL stays at the carriage return.
4EED
DEC HL 2B
A separator: decrement Register Pair HL back one character, so that 4D5BH at 4F10H finds a separator again and reads the next keyword.
4EEE
LD A,20H 3E 20
Load Register A with 20H, a space, to pad the field.
4EF0
LD (DE),A 12
Store Register A (the character, or a space) at Register Pair DE, the field in the GAT copy at 4300H.
4EF1
INC DE 13
Increment Register Pair DE to the next GAT byte of the field.
4EF2
Decrement Register B, the field positions left; if Register B is not 0, LOOP back to 4EE2H.
4EF4
JUMP to 4F10H to look for the next keyword at Register Pair HL, or the end of the line.

4EF6H - PROT RUF, UNLOCK and LOCK

These only set a bit in the operand at 4F1AH; the files are changed after the last keyword (4F19H). Bit 2 RUF, bit 0 UNLOCK, bit 1 LOCK. When both LOCK and UNLOCK are given, UNLOCK wins (4F22H).

4EF6
LD A,04H 3E 04
RUF: load Register A with 04H (0000 0100), bit 2 of the PROT file flags: reset the updated flag of every file.
4EF8
JUMP to 4F00H to merge the bit in Register A (04H) into the flags at 4F1AH.
4EFA
LD A,01H 3E 01
UNLOCK: load Register A with 01H (0000 0001), bit 0 of the PROT file flags: give every file blank passwords.
4EFC
JUMP to 4F00H to merge the bit in Register A (01H) into the flags at 4F1AH.
4EFE
LD A,02H 3E 02
LOCK: load Register A with 02H (0000 0010), bit 1 of the PROT file flags: give every file the diskette password.
4F00
PUSH HL E5
Save Register Pair HL, the command line pointer after the keyword, on the stack.
4F01
LD HL,4F1AH 21 1A 4F
Load Register Pair HL with 4F1AH, the operand of the LD A,nn at 4F19H, which collects the PROT file flags.
4F04
OR (HL) B6
OR Register A (the new bit) with the byte at Register Pair HL, the flags collected so far.
4F05
LD (HL),A 77
Self-Modifying Code
Store Register A, the updated flags, at Register Pair HL (4F1AH), the operand that 4F19H loads after the last keyword.
4F06
POP HL E1
Restore Register Pair HL from the stack: the command line pointer after the keyword.
4F07
JUMP to 4F10H to look for the next keyword at Register Pair HL, or the end of the line.

4F09H - PROT PW=

PW=password stores the new password's code in GAT bytes CEH-CFH. A later LOCK uses the new code, because 4F1EH reads it from the GAT copy after every keyword has been handled.

4F09
GOSUB to 5005H to read the password at Register Pair HL and return its 16-bit code in Register Pair DE (4296H for a blank password), with Register Pair HL after it.
4F0C
LD (43CEH),DE ED 53 CE 43
Store Register Pair DE, the new password code, at 43CEH, GAT bytes CEH-CFH in the sector buffer: the diskette password.

4F10H - PROT: Next Keyword, Then Change the Files

After the last keyword the GAT is written back. Then, if LOCK, UNLOCK or RUF was given, every FDE sector of the directory (8 plus the extra sectors counted in HIT byte 1FH) is read and each FPDE in use is changed: RUF clears the updated bit (byte 1 bit 5) of every file; LOCK sets both password codes of every file that is neither a system file nor invisible to the diskette password, UNLOCK sets them to the blank code 4296H. Each sector is written back.

4F10
GOSUB to 4D5BH with Register Pair HL = the command line: the Z FLAG at the end of the line, the NZ FLAG with Register Pair HL past a comma or spaces; any other character gives error 34H.
4F13
If the NZ FLAG is set (a separator was passed, so another keyword follows), LOOP back to 4ED2H for the next keyword at Register Pair HL.
4F15
GOSUB to SYS0/SYS 48C4H to write the GAT in 4300H (with any PW=, NAME= and DATE= changes) back to directory sector 0, with the directory mark and the verify read.
4F18
RET NZ C0
If the NZ FLAG is set (the GAT could not be written), RETURN through the dispatcher's return address 4BC5H to SYS0/SYS 4408H with the error code in Register A.
4F19
LD A,00H 3E 00
Self-Modifying Code
Load Register A with the PROT file flags, the operand at 4F1AH: 00H in the file, ORed with 01H (UNLOCK), 02H (LOCK) and 04H (RUF) by 4F00H.
4F1B
OR A B7
OR Register A (the file flags) with itself: the Z FLAG is set if none of LOCK, UNLOCK or RUF was given.
4F1C
LD B,A 47
Load Register B with Register A, the file flags, for the entry loop; LD does not change the flags.
4F1D
RET Z C8
If the Z FLAG is set (no file flags), RETURN through the dispatcher's return address 4BC5H to SYS0/SYS 4408H with the Z FLAG set and Register A = 00H: no error.
4F1E
LD DE,(43CEH) ED 5B CE 43
Load Register Pair DE with the 16-bit value at 43CEH, GAT bytes CEH-CFH: the diskette password code, the code LOCK gives every file (already the new one if PW= was given).
4F22
RRCA 0F
Rotate Register A (the file flags) right: bit 0, UNLOCK, goes into the CARRY FLAG.
4F23
If the NO CARRY FLAG is set (no UNLOCK), JUMP to 4F28H with Register Pair DE = the diskette password code.
4F25
LD DE,4296H 11 96 42
UNLOCK: load Register Pair DE with 4296H, the code of a blank password, which the files get instead.
4F28
LD A,01H 3E 01
Load Register A with 01H: directory sector 1, the HIT, which tells how many FDE sectors the directory has.
4F2A
GOSUB to SYS0/SYS 48AFH to read directory sector Register A (1, the HIT) into 4300H.
4F2D
RET NZ C0
If the NZ FLAG is set (the HIT could not be read), RETURN through the dispatcher's return address 4BC5H to SYS0/SYS 4408H with the error code in Register A.
4F2E
LD A,(431FH) 3A 1F 43
Load Register A with the byte at 431FH, HIT byte 1FH: the number of FDE sectors the directory has beyond the first 8.
4F31
ADD A,08H C6 08
Add 08H to Register A (the extra FDE sectors): Register A = the number of FDE sectors in the directory.
4F33
LD (4F6DH),A 32 6D 4F
Self-Modifying Code
Store Register A, the number of FDE sectors, at 4F6DH, the operand of the CP at 4F6CH that ends the sector loop.
4F36
XOR A AF
Load Register A with 00H (XOR A with itself): FDE sector 0, the first.

Sector Loop
Register C = the FDE sector (0 up), Register B = the file flags, Register Pair DE = the password code for LOCK or UNLOCK.

4F37
LD C,A 4F
Load Register C with Register A, the FDE sector number (0 up), to keep it for 4F6AH; as a DEC (bits 0-4 the FDE sector, bits 5-7 entry 0) Register A names the sector's first entry.
4F38
GOSUB to SYS0/SYS 48DBH to read the FDE sector of the DEC in Register A (directory sector (Register A AND 1FH) + 2) into 4300H and point Register Pair HL to its first entry, 4300H.
4F3B
RET NZ C0
If the NZ FLAG is set (the sector could not be read), RETURN through the dispatcher's return address 4BC5H to SYS0/SYS 4408H with the error code in Register A.

Entry Loop
Register Pair HL = byte 0 of the entry (4300H, 4320H, ... 43E0H).

4F3C
LD A,(HL) 7E
Load Register A with the byte at Register Pair HL, byte 0 of the directory entry (bit 7 FXDE, bit 6 system, bit 4 in use, bit 3 invisible).
4F3D
AND 90H E6 90
Mask Register A (byte 0) against 90H (1001 0000) to keep bit 7 (an extension entry, FXDE) and bit 4 (the entry is in use).
4F3F
CP 10H FE 10
Compare Register A, those two bits, against 10H: the Z FLAG is set only for a primary entry (FPDE) in use.
4F41
If the NZ FLAG is set (the entry is free, or an FXDE), JUMP to 4F5EH for the next entry.
4F43
BIT 2,B CB 50
Test bit 2 of Register B, the file flags: bit 2 is RUF.
4F45
If the Z FLAG is set (bit 2 of Register B clear, no RUF), JUMP to 4F4BH.
4F47
INC HL 23
Increment Register Pair HL, the entry pointer, to byte 1 of the entry (the second flag byte).
4F48
RES 5,(HL) CB AE
Clear bit 5 of the byte at Register Pair HL, entry byte 1: the file is no longer marked as updated.
4F4A
DEC HL 2B
Decrement Register Pair HL, the entry pointer, back to byte 0 of the entry.
4F4B
LD A,B 78
Load Register A with Register B, the file flags (bit 0 UNLOCK, bit 1 LOCK, bit 2 RUF).
4F4C
AND 03H E6 03
Mask Register A (the file flags) against 03H (0000 0011) to keep UNLOCK (bit 0) and LOCK (bit 1); the Z FLAG is set if neither was given.
4F4E
If the Z FLAG is set (no LOCK or UNLOCK), JUMP to 4F5EH for the next entry.
4F50
LD A,(HL) 7E
Load Register A with the byte at Register Pair HL, byte 0 of the entry.
4F51
AND 48H E6 48
Mask Register A (byte 0) against 48H (0100 1000) to keep bit 6 (a system file) and bit 3 (an invisible file).
4F53
If the NZ FLAG is set (Register A shows a system or invisible file), JUMP to 4F5EH: its passwords are left alone.
4F55
SET 4,L CB E5
Set bit 4 of Register L: Register Pair HL (an entry start, a multiple of 20H) now points at byte 16 of the entry (entry + 10H), the update password code.
4F57
LD (HL),E 73
Store Register E, the low byte of the password code (the diskette's, or 4296H for UNLOCK), at Register Pair HL, entry byte 16.
4F58
INC HL 23
Increment Register Pair HL, the entry pointer, to byte 17, the high byte of the update password code.
4F59
LD (HL),D 72
Store Register D, the high byte of the password code, at Register Pair HL, entry byte 17.
4F5A
INC HL 23
Increment Register Pair HL, the entry pointer, to byte 18, the access password code.
4F5B
LD (HL),E 73
Store Register E, the low byte of the password code, at Register Pair HL, entry byte 18.
4F5C
INC HL 23
Increment Register Pair HL, the entry pointer, to byte 19, the high byte of the access password code.
4F5D
LD (HL),D 72
Store Register D, the high byte of the password code, at Register Pair HL, entry byte 19.
4F5E
LD A,L 7D
Load Register A with Register L, the low byte of the entry pointer (somewhere inside the current 32-byte entry).
4F5F
AND E0H E6 E0
Mask Register A (the low byte) against E0H (1110 0000): Register A = the low byte of the entry's start.
4F61
ADD A,20H C6 20
Add 20H to Register A: the low byte of the next entry; the CARRY FLAG is set after the eighth entry (E0H + 20H).
4F63
LD L,A 6F
Load Register L with Register A: Register Pair HL = the next entry in 4300H-43FFH.
4F64
If the NO CARRY FLAG is set (Register Pair HL is still inside the sector), LOOP back to 4F3CH for the next entry.
4F66
GOSUB to SYS0/SYS 48C4H to write the changed FDE sector in 4300H back with the directory mark and the verify read.
4F69
RET NZ C0
If the NZ FLAG is set (the sector could not be written), RETURN through the dispatcher's return address 4BC5H to SYS0/SYS 4408H with the error code in Register A.
4F6A
INC C 0C
Increment Register C, the FDE sector number, to the next FDE sector.
4F6B
LD A,C 79
Load Register A with Register C, the next FDE sector number.
4F6C
CP 00H FE 00
Self-Modifying Code
Compare Register A, the next FDE sector number, against the number of FDE sectors in the operand at 4F6DH (00H in the file, stored at 4F33H): the CARRY FLAG is set while sectors remain.
4F6E
If the CARRY FLAG is set (Register A is below the number of FDE sectors), LOOP back to 4F37H for the next FDE sector.
4F70
XOR A AF
Load Register A with 00H (XOR A with itself), which also sets the Z FLAG: no error.
4F71
RET C9
RETURN through the dispatcher's return address 4BC5H to SYS0/SYS 4408H with Register A = 00H and the Z FLAG set: PROT has finished.

4F72H - A Separator Must Follow

Steps Register Pair HL over a comma or spaces; any other character, or the end of the line, gives error 34H.

4F72
GOSUB to SYS0/SYS 4C7EH with Register Pair HL = the command line: the NO CARRY FLAG with Register Pair HL past a comma or spaces; the CARRY FLAG (Register Pair HL unchanged) before any other character, the carriage return included.
4F75
RET NC D0
If the NO CARRY FLAG is set (a separator was passed), RETURN with Register Pair HL at the next keyword or value.
4F76
No separator: JUMP to 4F97H, which jumps to 4D63H for error 34H ILLEGAL KEYWORD OR SEPARATOR OR TERMINATOR.

4F78H - Find a Keyword

4F78H searches a table of keyword entries (the name, 00H, and a two-byte handler address) and jumps to the handler of the keyword at Register Pair HL, with Register Pair HL past the keyword. 4F84H does the search for any table with Register D bytes after each name and returns Register Pair BC pointing at them. No match gives error 34H. The text only has to start with the keyword, so anything typed after it is read by the handler.

4F78
LD D,02H 16 02
Load Register D with 02H: two bytes, the handler's address, follow each name in the ATTRIB and PROT keyword tables.
4F7A
GOSUB to 4F84H to find the keyword at Register Pair HL in the table at Register Pair BC: Register Pair BC comes back at the handler address and Register Pair HL past the keyword (no match: error 34H).
4F7D
LD A,(BC) 0A
Load Register A with the byte at Register Pair BC, the low byte of the keyword's handler address.
4F7E
LD E,A 5F
Load Register E with Register A, the handler's low byte.
4F7F
INC BC 03
Increment Register Pair BC, the table pointer, to the high byte of the handler address.
4F80
LD A,(BC) 0A
Load Register A with the byte at Register Pair BC, the high byte of the handler address.
4F81
LD D,A 57
Load Register D with Register A, the handler's high byte: Register Pair DE = the handler's address.
4F82
PUSH DE D5
Push Register Pair DE, the handler's address, on the stack as a return address.
4F83
RET C9
RETURN, which pops the handler's address and jumps to it with Register Pair HL past the keyword (the FPDE's address or the caller's return is the next word on the stack).

Table Loop
Register Pair BC = the table entry, Register Pair HL = the command line, Register D = the bytes after each name.

4F84
GOSUB to SYS0/SYS 4C6AH to compare the name at Register Pair BC (ending in 00H) with the text at Register Pair HL: on a match it returns the Z FLAG with Register Pair HL past the name in the text and Register Pair BC past the name's 00H; otherwise the NZ FLAG with Register Pair HL unchanged and Register Pair BC one past the byte that differed.
4F87
RET Z C8
If the Z FLAG is set (the text starts with this name), RETURN with Register Pair BC at the bytes after the name (the handler address or the level).
4F88
LD A,(BC) 0A
Load Register A with the byte at Register Pair BC, the next byte of the name that did not match.
4F89
CP 00H FE 00
Compare Register A, that byte, against 00H, the byte that ends every name in the table.
4F8B
INC BC 03
Increment Register Pair BC, the table pointer, to the next byte; INC BC does not change the flags.
4F8C
If the NZ FLAG is set (Register A was not the 00H), LOOP back to 4F88H; when it was, Register Pair BC is past the 00H, at the bytes after the name.
4F8E
LD E,D 5A
Load Register E with Register D, the number of bytes after each name (2 for the keyword tables, 1 for the level table).
4F8F
INC BC 03
Increment Register Pair BC, the table pointer, past one of the bytes that follow the name.
4F90
DEC E 1D
Decrement Register E, the bytes still to skip after the name.
4F91
If the NZ FLAG is set (Register E is not 0, more bytes to skip), LOOP back to 4F8FH.
4F93
LD A,(BC) 0A
Load Register A with the byte at Register Pair BC, the first byte of the next entry, or the 00H that ends the table.
4F94
OR A B7
OR Register A with itself: the Z FLAG is set for the 00H that ends the table.
4F95
If the NZ FLAG is set (Register A starts another name), LOOP back to 4F84H to compare it.
4F97
No keyword matched (also reached from 4F76H and 4FA3H when a separator is missing): JUMP to 4D63H for error 34H ILLEGAL KEYWORD OR SEPARATOR OR TERMINATOR.

4F9AH - Read an Optional Number

4F9AH: at the end of the line RETURN with Register Pair DE unchanged (the default the caller put there); after a separator read a number. 4FA0H: a separator and a number must follow. 4FA5H reads a number: decimal, or hexadecimal with a trailing H (a letter A-H after the decimal digits makes the reading start again as hexadecimal). Returns Register Pair DE = the value; no digit, a missing H or a value over FFFFH gives error 2FH.

4F9A
GOSUB to SYS0/SYS 4C7AH with Register Pair HL = the command line: the Z FLAG at the end of the line; the NZ FLAG and NO CARRY after stepping over a separator; the CARRY FLAG before another character.
4F9D
If the NZ FLAG is set (not the end of the line), JUMP to 4FA3H, which reads a number after a separator or gives error 34H.
4F9F
RET C9
The end of the line: RETURN with Register Pair DE unchanged, the default the caller loaded (402DH for the entry, the start for the load address).
4FA0
GOSUB to SYS0/SYS 4C7EH with Register Pair HL = the command line: the NO CARRY FLAG with Register Pair HL past a comma or spaces; the CARRY FLAG otherwise (the end of the line included).
4FA3
If the CARRY FLAG is set (no separator before the number), JUMP to 4F97H for error 34H.
4FA5
PUSH HL E5
Save Register Pair HL, the start of the number in the command line, on the stack, to read it a second time as hexadecimal if needed.
4FA6
LD B,00H 06 00
Load Register B with 00H, the reading flags: bit 0 clear (decimal) and bit 1 clear (no digit read yet).
4FA8
GOSUB to 4FC6H to read the digits at Register Pair HL into Register Pair DE with the flags in Register B; Register Pair HL comes back at the first character that is not a digit.
4FAB
LD A,(HL) 7E
Load Register A with the byte at Register Pair HL, the character that ended the decimal digits.
4FAC
SUB 41H D6 41
Subtract 41H from Register A, that character: the letters A-H become 00H-07H.
4FAE
CP 08H FE 08
Compare Register A, the converted character, against 08H: the CARRY FLAG is set for a letter A-H (a hexadecimal digit, or the H suffix).
4FB0
If the NO CARRY FLAG is set (no letter A-H: a decimal number), JUMP to 4FBFH with the value in Register Pair DE.
4FB2
POP HL E1
Restore Register Pair HL from the stack: the start of the number, to read it again.
4FB3
LD B,01H 06 01
Load Register B with 01H, the reading flags: bit 0 set (hexadecimal), bit 1 clear (no digit read yet).
4FB5
PUSH HL E5
Save Register Pair HL, the start of the number, on the stack again (4FC1H drops it).
4FB6
GOSUB to 4FC6H to read the hexadecimal digits at Register Pair HL into Register Pair DE with the flags in Register B; Register Pair HL comes back at the first character that is not one.
4FB9
LD A,(HL) 7E
Load Register A with the byte at Register Pair HL, the character that ended the hexadecimal digits.
4FBA
CP 48H FE 48
Compare Register A, that character, against 48H, the letter H that must end a hexadecimal number.
4FBC
INC HL 23
Increment Register Pair HL past the H; INC HL does not change the flags.
4FBD
If the NZ FLAG is set (Register A was not H), JUMP to 4FC3H for error 2FH BAD PARAMETER(S).
4FBF
BIT 1,B CB 48
Test bit 1 of Register B, the reading flags: bit 1 is set once a digit has been read.
4FC1
POP BC C1
Restore Register Pair BC from the stack, discarding the saved start of the number; POP does not change the flags.
4FC2
RET NZ C0
If the NZ FLAG is set (at least one digit was read), RETURN with the value in Register Pair DE and Register Pair HL after the number.
4FC3
No digit, or no H: JUMP to 4DD7H for error 2FH BAD PARAMETER(S).
4FC6
LD DE,0000H 11 00 00
Load Register Pair DE with 0000H, the value of the number so far.

Digit Loop
Register Pair HL = the command line, Register Pair DE = the value, Register B = the reading flags (bit 0 hexadecimal, bit 1 a digit has been read).

4FC9
LD A,(HL) 7E
Load Register A with the byte at Register Pair HL, the next character of the number.
4FCA
SUB 30H D6 30
Subtract 30H from Register A, the character: the digits 0-9 become 00H-09H.
4FCC
CP 0AH FE 0A
Compare Register A, the converted character, against 0AH: the CARRY FLAG is set for a digit 0-9.
4FCE
If the CARRY FLAG is set (a digit 0-9 in Register A), JUMP to 4FDAH to add it to the value.
4FD0
BIT 0,B CB 40
Test bit 0 of Register B, the reading flags: bit 0 is set when reading hexadecimal.
4FD2
RET Z C8
If the Z FLAG is set (decimal: Register A is not a digit), RETURN with the value in Register Pair DE and Register Pair HL at the character.
4FD3
SUB 11H D6 11
Subtract 11H from Register A (the character minus 30H): the letters A-F become 00H-05H.
4FD5
CP 06H FE 06
Compare Register A, the converted character, against 06H: the CARRY FLAG is set for a letter A-F.
4FD7
RET NC D0
If the NO CARRY FLAG is set (Register A is not A-F), RETURN with the value in Register Pair DE and Register Pair HL at the character.
4FD8
ADD A,0AH C6 0A
Add 0AH to Register A: the letters A-F become the digit values 10-15.
4FDA
PUSH HL E5
Save Register Pair HL, the command line pointer, on the stack while Register Pair HL does the arithmetic.
4FDB
LD H,D 62
Load Register H with Register D, the high byte of the value so far.
4FDC
LD L,E 6B
Load Register L with Register E, the low byte: Register Pair HL = the value so far (Register Pair DE keeps a copy for the times-5 step).
4FDD
LD C,A 4F
Load Register C with Register A, the new digit (0-15), to add after the multiplication.
4FDE
XOR A AF
Load Register A with 00H (XOR A with itself): Register A collects the bits carried out of Register Pair HL (any overflow).
4FDF
SET 1,B CB C8
Set bit 1 of Register B, the reading flags: a digit has been read.
4FE1
ADD HL,HL 29
Add Register Pair HL to itself: Register Pair HL = the value times 2; the CARRY FLAG is set on overflow.
4FE2
ADC A,A 8F
Add Register A to itself with the CARRY FLAG: an overflow bit is shifted into Register A, which stays 00H only while nothing overflows.
4FE3
ADD HL,HL 29
Add Register Pair HL to itself: Register Pair HL = the value times 4.
4FE4
ADC A,A 8F
Add Register A to itself with the CARRY FLAG: an overflow bit is kept in Register A.
4FE5
BIT 0,B CB 40
Test bit 0 of Register B, the reading flags: bit 0 is set for hexadecimal.
4FE7
If the Z FLAG is set (decimal), JUMP to 4FECH to make the value times 5.
4FE9
ADD HL,HL 29
Hexadecimal: add Register Pair HL to itself, Register Pair HL = the value times 8.
4FEA
JUMP to 4FEDH to keep the overflow bit and double once more (times 16).
4FEC
ADD HL,DE 19
Decimal: add Register Pair DE (the value before this digit) to Register Pair HL (the value times 4): Register Pair HL = the value times 5.
4FED
ADC A,A 8F
Add Register A to itself with the CARRY FLAG: an overflow bit is kept in Register A.
4FEE
ADD HL,HL 29
Add Register Pair HL to itself: Register Pair HL = the value times 16 (hexadecimal) or times 10 (decimal).
4FEF
ADC A,A 8F
Add Register A to itself with the CARRY FLAG: an overflow bit is kept in Register A.
4FF0
LD E,C 59
Load Register E with Register C, the new digit.
4FF1
LD D,00H 16 00
Load Register D with 00H: Register Pair DE = the new digit (0-15).
4FF3
ADD HL,DE 19
Add Register Pair DE (the digit) to Register Pair HL: Register Pair HL = the new value.
4FF4
ADC A,A 8F
Add Register A to itself with the CARRY FLAG: the NZ FLAG is set if any step carried out of Register Pair HL (a value over FFFFH).
4FF5
EX DE,HL EB
Exchange Register Pairs DE and HL: Register Pair DE = the new value.
4FF6
POP HL E1
Restore Register Pair HL from the stack: the command line pointer at the digit; POP does not change the flags.
4FF7
If the NZ FLAG is set (the number is over FFFFH), JUMP to 4FC3H for error 2FH BAD PARAMETER(S).
4FF9
INC HL 23
Increment Register Pair HL, the command line pointer, past the digit.
4FFA
LOOP back to 4FC9H for the next digit with the value in Register Pair DE.

4FFCH - Read a Password and Encode It

5005H copies a name of up to 8 characters (a letter, then letters and digits) to 51E8H-51EFH, padded with spaces, and leaves Register Pair HL after it. If the text does not start with a letter nothing is taken and the name is blank. 5023H then encodes the 8 characters into a 16-bit code in Register Pair DE with the same steps as SYS2/SYS's password encoder (5152H): a blank name gives 4296H, PASSWORD gives 42E0H. The buffer is blank again afterwards.

Copy Loop
Register Pair HL = the command line, Register Pair DE = the buffer position before the next character, Register B = the characters left (from 8).

4FFC
LD A,(HL) 7E
Load Register A with the byte at Register Pair HL, the next character of the name (the second or a later one).
4FFD
CP 30H FE 30
Compare Register A, that character, against 30H, the digit 0: the CARRY FLAG is set for anything below it.
4FFF
If the CARRY FLAG is set (Register A is below 0, so not a letter or digit), JUMP to 5016H: the name ends here.
5001
CP 3AH FE 3A
Compare Register A, the character, against 3AH, one above the digit 9: the CARRY FLAG is set for a digit 0-9.
5003
JUMP to 500BH with that CARRY FLAG: a digit is stored there, anything else is checked for a letter.
5005
LD DE,51E7H 11 E7 51
Entry: load Register Pair DE with 51E7H, one below the eight-byte buffer at 51E8H-51EFH (the store at 501EH increments first).
5008
LD B,08H 06 08
Load Register B with 08H, the eight characters of a name.
500A
OR A B7
OR Register A with itself to clear the CARRY FLAG: for the first character a digit is not allowed, so 500BH always goes on to the letter test.
500B
If the CARRY FLAG is set (Register A is a digit, and not the first character), JUMP to 501EH to store it.
500D
LD A,(HL) 7E
Load Register A with the byte at Register Pair HL, the character, again (for the first character it has not been read yet).
500E
CP 41H FE 41
Compare Register A, the character, against 41H, the letter A: the CARRY FLAG is set for anything below it.
5010
If the CARRY FLAG is set (Register A is not a letter), JUMP to 5016H: the name ends here (with no character taken, for the first).
5012
CP 5BH FE 5B
Compare Register A, the character, against 5BH, one above the letter Z: the CARRY FLAG is set for a letter A-Z.
5014
If the CARRY FLAG is set (Register A is a letter), JUMP to 501EH to store it.
5016
INC DE 13
Increment Register Pair DE to the next buffer position, which gets a space.

Pad Loop
Register Pair DE = the buffer position, Register B = the positions left.

5017
LD A,20H 3E 20
Load Register A with 20H, a space, to fill the rest of the eight-byte buffer.
5019
LD (DE),A 12
Store Register A (a space) at Register Pair DE in the buffer at 51E8H-51EFH.
501A
Decrement Register B, the positions left; if Register B is not 0, LOOP back to 5016H for the next position.
501C
JUMP to 5023H to encode the eight characters now in 51E8H-51EFH.
501E
INC DE 13
Increment Register Pair DE to the next buffer position.
501F
LD (DE),A 12
Store Register A, the letter or digit, at Register Pair DE in the buffer at 51E8H-51EFH.
5020
INC HL 23
Increment Register Pair HL, the command line pointer, past the character.
5021
Decrement Register B, the characters left; if Register B is not 0, LOOP back to 4FFCH for the next character. After the eighth the name ends without looking at what follows.
5023
PUSH HL E5
Save Register Pair HL, the command line pointer after the name, on the stack.
5024
EX DE,HL EB
Exchange Register Pairs DE and HL: Register Pair HL = 51EFH, the last byte of the buffer (Register Pair DE was left there by the copy or the padding).
5025
LD DE,FFFFH 11 FF FF
Load Register Pair DE with FFFFH, the starting value of the password code.
5028
LD B,08H 06 08
Load Register B with 08H: eight characters, taken from the last (51EFH) down to the first (51E8H).

Encode Loop
Register Pair DE = the code so far, Register Pair HL = the character, Register B = the characters left (kept on the stack while Register Pair BC is work space).

502A
PUSH BC C5
Save Register Pair BC (Register B = the characters left) on the stack, because Registers B and C are used for the mixing steps.
502B
LD A,E 7B
Load Register A with Register E, the low byte of the code so far.
502C
AND 07H E6 07
Mask Register A (the code's low byte) against 07H (0000 0111) to keep its bits 0-2.
502E
LD C,A 4F
Load Register C with Register A, bits 0-2 of the code's low byte.
502F
LD A,E 7B
Load Register A with Register E, the low byte of the code so far, again.
5030
RLCA 07
Rotate Register A (the code's low byte) left by one bit.
5031
RLCA 07
Rotate Register A left again: the code's low byte rotated left by two bits.
5032
RLCA 07
Rotate Register A left a third time: the code's low byte rotated left by three bits.
5033
XOR C A9
XOR Register A (the rotated low byte) with Register C, the low byte's original bits 0-2.
5034
RLCA 07
Rotate Register A left once more: Register A = the mixed value used for both new bytes.
5035
LD C,A 4F
Load Register C with Register A, the mixed value.
5036
AND F0H E6 F0
Mask Register A (the mixed value) against F0H (1111 0000) to keep its high four bits.
5038
LD B,A 47
Load Register B with Register A, the high four bits of the mixed value.
5039
LD A,C 79
Load Register A with Register C, the mixed value, again.
503A
RLCA 07
Rotate Register A (the mixed value) left by one bit.
503B
AND 1FH E6 1F
Mask Register A against 1FH (0001 1111) to keep its low five bits.
503D
XOR B A8
XOR Register A with Register B, the high four bits of the mixed value.
503E
XOR D AA
XOR Register A with Register D, the high byte of the code so far.
503F
LD E,A 5F
Load Register E with Register A: the new low byte of the code.
5040
LD A,C 79
Load Register A with Register C, the mixed value, again.
5041
AND 0FH E6 0F
Mask Register A (the mixed value) against 0FH (0000 1111) to keep its low four bits.
5043
LD B,A 47
Load Register B with Register A, the low four bits of the mixed value.
5044
LD A,C 79
Load Register A with Register C, the mixed value, again.
5045
RLCA 07
Rotate Register A (the mixed value) left by one bit.
5046
RLCA 07
Rotate Register A (the mixed value) left again: two bits so far.
5047
RLCA 07
Rotate Register A left a third time (three bits).
5048
RLCA 07
Rotate Register A left a fourth time: the two halves of the mixed value are swapped.
5049
XOR B A8
XOR Register A (the swapped mixed value) with Register B, its low four bits.
504A
POP BC C1
Restore Register Pair BC from the stack: Register B = the characters left.
504B
XOR (HL) AE
XOR Register A with the byte at Register Pair HL, the character from the buffer at 51E8H-51EFH.
504C
LD D,A 57
Load Register D with Register A: the new high byte of the code.
504D
LD (HL),20H 36 20
Store 20H (a space) at Register Pair HL: the buffer is blanked as it is used, so no password stays in memory.
504F
DEC HL 2B
Decrement Register Pair HL to the character before, in the buffer at 51E8H-51EFH.
5050
Decrement Register B, the characters left; if Register B is not 0, LOOP back to 502AH.
5052
POP HL E1
Restore Register Pair HL from the stack: the command line pointer after the name.
5053
RET C9
RETURN with the password code in Register Pair DE and Register Pair HL after the name.

5054H - ATTRIB Keyword Table

Each entry is the keyword, a 00H and the two-byte address of its handler; a 00H in place of a keyword ends the table. 4F78H looks the keywords up in this order with Register Pair BC = 5054H (4E11H), and the text only has to start with a keyword.

5054-5059
DEFM "INV" / DEFB 00H / DEFW 4E54H 49 4E 56 00 54 4E
INV: handler 4E54H, which sets bit 3 of FPDE byte 0 so that the file is invisible.
505A-505F
DEFM "VIS" / DEFB 00H / DEFW 4E58H 56 49 53 00 58 4E
VIS: handler 4E58H, which clears bit 3 of FPDE byte 0 so that the file is visible.
5060-5067
DEFM "PROT=" / DEFB 00H / DEFW 4E6AH 50 52 4F 54 3D 00 6A 4E
PROT=: handler 4E6AH, which looks the access level name up in the table at 5093H and stores the level in bits 0-2 of FPDE byte 0.
5068-506E
DEFM "ACC=" / DEFB 00H / DEFW 4E78H 41 43 43 3D 00 78 4E
ACC=: handler 4E78H, which stores the code of the password that follows at FPDE bytes 18-19, the access password.
506F-5075
DEFM "UPD=" / DEFB 00H / DEFW 4E7CH 55 50 44 3D 00 7C 4E
UPD=: handler 4E7CH, which stores the code of the password that follows at FPDE bytes 16-17, the update password.
5076-507C
DEFM "ASE=" / DEFB 00H / DEFW 4E31H 41 53 45 3D 00 31 4E
ASE=: handler 4E31H, which clears bit 7 of FPDE byte 1 for Y (the file may be extended) and sets it for N.
507D-5083
DEFM "ASC=" / DEFB 00H / DEFW 4E35H 41 53 43 3D 00 35 4E
ASC=: handler 4E35H, which clears bit 6 of FPDE byte 1 for Y and sets it for N (CLOSE then keeps the space beyond EOF).
5084-508A
DEFM "UDF=" / DEFB 00H / DEFW 4E2AH 55 44 46 3D 00 2A 4E
UDF=: handler 4E2AH, which sets bit 5 of FPDE byte 1 (the file has been updated) for Y and clears it for N.
508B-5091
DEFM "LRL=" / DEFB 00H / DEFW 4E17H 4C 52 4C 3D 00 17 4E
LRL=: handler 4E17H, which stores the record length 1-256 that follows at FPDE byte 4 (256 as 00H).
5092
DEFB 00H 00
The 00H that ends the ATTRIB keyword table: 4F93H finds it where the next keyword would start and gives error 34H.

5093H - Access Level Table

Used by ATTRIB PROT= (4E6AH): each entry is the level name, a 00H and the level byte stored in bits 0-2 of FPDE byte 0; a 00H in place of a name ends the table. RENAME and NAME both give level 2, and no name gives level 3. RENAME comes before NAME, so the longer name is tried first.

5093-5098
DEFM "LOCK" / DEFB 00H, 07H 4C 4F 43 4B 00 07
LOCK: access level 07H, no access without the update password.
5099-509E
DEFM "EXEC" / DEFB 00H, 06H 45 58 45 43 00 06
EXEC: access level 06H, the file may only be executed.
509F-50A4
DEFM "READ" / DEFB 00H, 05H 52 45 41 44 00 05
READ: access level 05H, the file may be read and executed.
50A5-50AB
DEFM "WRITE" / DEFB 00H, 04H 57 52 49 54 45 00 04
WRITE: access level 04H, the file may also be written.
50AC-50B3
DEFM "RENAME" / DEFB 00H, 02H 52 45 4E 41 4D 45 00 02
RENAME: access level 02H, the file may also be renamed.
50B4-50B9
DEFM "NAME" / DEFB 00H, 02H 4E 41 4D 45 00 02
NAME: access level 02H, the same level as RENAME under a shorter name.
50BA-50BF
DEFM "KILL" / DEFB 00H, 01H 4B 49 4C 4C 00 01
KILL: access level 01H, the file may also be killed.
50C0-50C5
DEFM "FULL" / DEFB 00H, 00H 46 55 4C 4C 00 00
FULL: access level 00H, every access, the level the update password gives.
50C6
DEFB 00H 00
The 00H that ends the access level table: 4F93H finds it where the next name would start and gives error 34H.

50C7H - PROT Keyword Table

Each entry is the keyword, a 00H and the two-byte address of its handler; a 00H in place of a keyword ends the table. 4F78H looks the keywords up in this order with Register Pair BC = 50C7H (4ED2H). UNLOCK comes before LOCK in the table.

50C7-50CC
DEFM "PW=" / DEFB 00H / DEFW 4F09H 50 57 3D 00 09 4F
PW=: handler 4F09H, which stores the code of the new diskette password at GAT bytes CEH-CFH (43CEH).
50CD-50D5
DEFM "UNLOCK" / DEFB 00H / DEFW 4EFAH 55 4E 4C 4F 43 4B 00 FA 4E
UNLOCK: handler 4EFAH, which sets bit 0 of the PROT file flags at 4F1AH: every file gets blank passwords.
50D6-50DC
DEFM "LOCK" / DEFB 00H / DEFW 4EFEH 4C 4F 43 4B 00 FE 4E
LOCK: handler 4EFEH, which sets bit 1 of the PROT file flags at 4F1AH: every file gets the diskette password.
50DD-50E4
DEFM "NAME=" / DEFB 00H / DEFW 4EDDH 4E 41 4D 45 3D 00 DD 4E
NAME=: handler 4EDDH, which copies up to 8 characters to GAT bytes D0H-D7H (43D0H), the diskette name.
50E5-50EC
DEFM "DATE=" / DEFB 00H / DEFW 4ED8H 44 41 54 45 3D 00 D8 4E
DATE=: handler 4ED8H, which copies up to 8 characters to GAT bytes D8H-DFH (43D8H), the diskette date.
50ED-50F2
DEFM "RUF" / DEFB 00H / DEFW 4EF6H 52 55 46 00 F6 4E
RUF: handler 4EF6H, which sets bit 2 of the PROT file flags at 4F1AH: the updated flag of every file is reset.
50F3
DEFB 00H 00
The 00H that ends the PROT keyword table: 4F93H finds it where the next keyword would start and gives error 34H.

50F4H - DUMP Command

DUMP filespec,start,end[,entry[,load]] writes memory from start to end to the file (opened by SYS1/SYS in the DOS FCB at 4480H) in load module format. Numbers are decimal or hexadecimal with H. The entry address defaults to 402DH (DOS READY); the load address, where the data will be loaded later, defaults to start. With entry = FFFFH the file is written without records instead (two address bytes, then the data) and no load address is read. The file's sector buffer is set to 4D68H, inside SYS7/SYS itself (over the code for SYSTEM to ATTRIB, which DUMP does not need). Before the dump one sector is written at relative sector (count + 4) / 252 of the file, so that all the space the file needs is allocated at once; the 4978H chain then brings DUMP back as sub-function 8.

50F4
LD HL,4D68H 21 68 4D
Load Register Pair HL with 4D68H, the 256 bytes 4D68H-4E67H of SYS7/SYS's own code (SYSTEM to ATTRIB, not needed by DUMP), used as the file's sector buffer.
50F7
LD (4483H),HL 22 83 44
Store Register Pair HL, 4D68H, at 4483H, bytes 3-4 of the DOS FCB at 4480H: the buffer address for every read and write of the file.
50FA
POP AF F1
Restore Register Pair AF from the stack, discarding the dispatcher's return address 4BC5H (pushed by the CALL at 4BC2H), so that the command line pointer SYS1/SYS left is on top.
50FB
POP HL E1
Restore Register Pair HL from the stack: the command line pointer left by SYS1/SYS, at the character after the filespec.
50FC
GOSUB to 4FA0H: a separator must follow at Register Pair HL, then the start address is read into Register Pair DE.
50FF
PUSH DE D5
Save Register Pair DE, the start address, on the stack.
5100
GOSUB to 4FA0H: a separator must follow at Register Pair HL, then the end address is read into Register Pair DE.
5103
EX DE,HL EB
Exchange Register Pairs DE and HL: Register Pair HL = the end address and Register Pair DE = the command line pointer.
5104
EX (SP),HL E3
Exchange Register Pair HL with the word at the top of the stack (at the Stack Pointer SP): the end address is kept on the stack and Register Pair HL = the start address.
5105
EX DE,HL EB
Exchange Register Pairs DE and HL: Register Pair DE = the start address and Register Pair HL = the command line pointer.
5106
PUSH DE D5
Save Register Pair DE, the start address, on the stack above the end address.
5107
LD DE,402DH 11 2D 40
Load Register Pair DE with 402DH, the default entry address (the DOS READY jump), used when no entry address is typed.
510A
GOSUB to 4F9AH: at the end of the line Register Pair DE keeps 402DH; after a separator the entry address is read into Register Pair DE.
510D
LD A,D 7A
Load Register A with Register D, the high byte of the entry address.
510E
AND E A3
AND Register A (the high byte) with Register E, the low byte: Register A = FFH only if the entry address is FFFFH.
510F
INC A 3C
Increment Register A: the Z FLAG is set if the entry address in Register Pair DE is FFFFH (write without records).
5110
EX DE,HL EB
Exchange Register Pairs DE and HL: Register Pair HL = the entry address and Register Pair DE = the command line pointer; EX does not change the flags.
5111
EX (SP),HL E3
Exchange Register Pair HL with the word at the top of the stack (at the Stack Pointer SP): the entry address is kept on the stack and Register Pair HL = the start address.
5112
EX DE,HL EB
Exchange Register Pairs DE and HL: Register Pair DE = the start address (the default load address) and Register Pair HL = the command line pointer.
5113
PUSH DE D5
Save Register Pair DE, the start address, on the stack; PUSH does not change the flags.
5114
If the NZ FLAG is set (the entry address is not FFFFH), GOSUB to 4F9AH to read a load address into Register Pair DE after a separator; at the end of the line Register Pair DE stays the start address.
5117
GOSUB to 4D5FH: the line must end at Register Pair HL, else error 34H.
511A
POP BC C1
Restore Register Pair BC from the stack: the start address (the word pushed at 5113H).
511B
POP AF F1
Restore Register Pair AF from the stack: the entry address (Register A = its high byte, the flags its low byte).
511C
POP HL E1
Restore Register Pair HL from the stack: the end address.
511D
PUSH AF F5
Save Register Pair AF, the entry address, on the stack (popped into Register Pair BC at 515EH).
511E
PUSH BC C5
Save Register Pair BC, the start address, on the stack (popped at 515AH).
511F
PUSH HL E5
Save Register Pair HL, the end address, on the stack (popped at 5156H).
5120
PUSH DE D5
Save Register Pair DE, the load address, on the stack (popped at 5151H).
5121
OR A B7
OR Register A with itself to clear the CARRY FLAG before the SBC; Register A (the entry's high byte) is not changed.
5122
SBC HL,BC ED 42
Subtract Register Pair BC (the start address) from Register Pair HL (the end address): Register Pair HL = the byte count minus 1; the CARRY FLAG is set if the end is below the start.
5124
PUSH HL E5
Save Register Pair HL, the byte count minus 1, on the stack (popped at 5150H); PUSH does not change the flags.
5125
If the CARRY FLAG is set (the end address is below the start), JUMP to 4DD7H for error 2FH BAD PARAMETER(S).
5128
LD BC,0005H 01 05 00
Load Register Pair BC with 0005H, to turn the count minus 1 in Register Pair HL into the count plus 4.
512B
ADD HL,BC 09
Add Register Pair BC (5) to Register Pair HL: Register Pair HL = the byte count plus 4; the CARRY FLAG is set if that passes FFFFH.
512C
If the NO CARRY FLAG is set (the sum fits in 16 bits), JUMP to 5133H to divide it.
512E
LD HL,0104H 21 04 01
A count of FFFCH or more: load Register Pair HL with 0104H (260), the relative sector to write, the quotient a count near 65536 would give.
5131
JUMP to 5138H with the relative sector number in Register Pair HL.
5133
LD A,FCH 3E FC
Load Register A with FCH, 252: the bytes reckoned per sector of the file, leaving room for the record headers.
5135
GOSUB to SYS0/SYS 4C59H to divide Register Pair HL (the count plus 4) by Register A (252): Register Pair HL = the quotient, the relative sector at which the file's data will end.
5138
LD (448AH),HL 22 8A 44
Store Register Pair HL, the relative sector number, at 448AH, bytes 10-11 of the DOS FCB (NEXT middle and high): the file is positioned at that sector.
513B
LD DE,4480H 11 80 44
Load Register Pair DE with 4480H, the DOS FCB holding the open DUMP file.
513E
LD BC,E908H 01 08 E9
Load Register Pair BC with E908H: Register B = E9H (the RST 28H code of SYS7/SYS function 7) and Register C = 08H (sub-function 8, DUMP's second half).
5141
LD HL,4978H 21 78 49
Load Register Pair HL with 4978H, the SYS0/SYS return point that pops a word into Registers A and C and does RST 28H with it.
5144
PUSH BC C5
Push Register Pair BC (E908H, the code and sub-function) on the stack for 4978H.
5145
PUSH HL E5
Push Register Pair HL (4978H) on the stack, as the return address of the write.
5146
JUMP to SYS0/SYS 4439H to write the buffer through the FCB at Register Pair DE at the sector set at 448AH; this allocates the file's space up to that sector, and SYS2/SYS may be loaded over SYS7/SYS for it. The write returns to 4978H, which goes to the error exit 4409H on an error, or reloads SYS7/SYS with code E9H and sub-function 8.

5149H - DUMP, Sub-Function 8: Write the Data

Entered through 4978H with Register Pair DE = the FCB and the values pushed at 511DH-5124H still on the stack under the dispatcher's return address 4BC5H. The file is rewound and the bytes are sent one at a time through the ROM output routine at 001BH with Register Pair DE = the FCB; the ROM finds that the FCB's first byte is not the output device type and jumps through 4033H to SYS0/SYS 4A80H, which writes the byte to the file. Load module records are type 01H, a length byte (the data length plus 2; 00H means 256), the two-byte load address (51BBH translates it) and up to 254 data bytes; the last record is the transfer record 02H 02H and the entry address. Finally the file is rewound again and closed through SYS0/SYS 4428H.

5149
POP AF F1
Restore Register Pair AF from the stack, discarding the dispatcher's return address 4BC5H, so that the values pushed at 511DH-5124H are on top.
514A
GOSUB to SYS0/SYS 443FH to rewind the file at Register Pair DE (the DOS FCB at 4480H, which 4978H passed on): NEXT = 0.
514D
If the NZ FLAG is set (the rewind failed), JUMP to 519FH, whose JR NZ goes on to the error exit with the error code in Register A.
514F
EXX D9
Exchange Register Pairs BC, DE and HL with the alternate set: the alternate Register Pair DE keeps the FCB address (4480H) for the output routine at 51DAH.
5150
POP DE D1
Restore Register Pair DE from the stack: the byte count minus 1, pushed at 5124H.
5151
POP HL E1
Restore Register Pair HL from the stack: the load address, pushed at 5120H.
5152
INC DE 13
Increment Register Pair DE: the byte count (0000H stands for 65536 bytes).
5153
LD (51CCH),HL 22 CC 51
Self-Modifying Code
Store Register Pair HL, the load address, at 51CCH, the operand of the LD HL,nnnn at 51CBH used to translate addresses.
5156
POP HL E1
Restore Register Pair HL from the stack: the end address, pushed at 511FH.
5157
LD (51BEH),HL 22 BE 51
Self-Modifying Code
Store Register Pair HL, the end address, at 51BEH, the operand of the LD HL,nnnn at 51BDH.
515A
POP HL E1
Restore Register Pair HL from the stack: the start address, pushed at 511EH.
515B
LD (51C5H),HL 22 C5 51
Self-Modifying Code
Store Register Pair HL, the start address, at 51C5H, the operand of the LD DE,nnnn at 51C4H.
515E
POP BC C1
Restore Register Pair BC from the stack: the entry address, pushed as Register Pair AF at 511DH.
515F
EX DE,HL EB
Exchange Register Pairs DE and HL: Register Pair HL = the byte count and Register Pair DE = the start address, the first byte to write.
5160
LD A,B 78
Load Register A with Register B, the high byte of the entry address.
5161
AND C A1
AND Register A (the high byte) with Register C, the low byte: Register A = FFH only if the entry address is FFFFH.
5162
INC A 3C
Increment Register A: the Z FLAG is set if the entry address in Register Pair BC is FFFFH.
5163
If the Z FLAG is set (entry FFFFH: no records), JUMP to 51A4H to write the address and the raw data.
5165
PUSH BC C5
Save Register Pair BC, the entry address, on the stack for the transfer record (popped at 5194H).
5166
LD A,H 7C
Load Register A with Register H, the high byte of the byte count.
5167
OR L B5
OR Register A (the high byte) with Register L, the low byte: the Z FLAG is set for a count of 0000H, meaning 65536 bytes.
5168
If the NZ FLAG is set (a count below 65536 in Register Pair HL), JUMP to 5172H to start the records.
516A
LD BC,00FEH 01 FE 00
65536 bytes: load Register Pair BC with 00FEH, the 254 data bytes of the first full record.
516D
OR A B7
OR Register A with itself to clear the CARRY FLAG before the SBC.
516E
SBC HL,BC ED 42
Subtract Register Pair BC (254) from Register Pair HL (0000H): Register Pair HL = FF02H, the bytes left after the first record.
5170
JUMP to 517AH to write the first full record, without the borrow test of 5178H (which the 0000H count would fail).

Record Loop
Register Pair HL = the bytes left, Register Pair DE = the next memory byte, Register C = the record's data length.

5172
LD BC,00FEH 01 FE 00
Load Register Pair BC with 00FEH, the 254 data bytes of a full record.
5175
OR A B7
OR Register A with itself to clear the CARRY FLAG before the SBC.
5176
SBC HL,BC ED 42
Subtract Register Pair BC (254) from Register Pair HL (the bytes left): the CARRY FLAG is set if fewer than 254 bytes are left.
5178
If the CARRY FLAG is set (fewer than 254 bytes left), JUMP to 518DH for the last, short record or the transfer record.
517A
INC C 0C
Increment Register C, the record's data length (FEH for a full record): the length byte is the data length plus 2.
517B
INC C 0C
Increment Register C again: Register C = the length byte (FEH + 2 wraps to 00H, which stands for 256).
517C
LD B,01H 06 01
Load Register B with 01H, record type 01H: a block of data with its load address.
517E
GOSUB to 51B3H to write the type (Register B), the length byte (Register C) and the load address of Register Pair DE (translated to the load address).
5181
DEC C 0D
Decrement Register C, the length byte, to take the 2 off again.
5182
DEC C 0D
Decrement Register C again: Register C = the number of data bytes in this record (00H stands for 256, FEH for a full record).

Data Loop
Register Pair DE = the next memory byte, Register C = the bytes left in the record.

5183
LD A,(DE) 1A
Load Register A with the byte at Register Pair DE, the next byte of the memory being dumped.
5184
INC DE 13
Increment Register Pair DE, the memory pointer, to the next byte to dump.
5185
GOSUB to 51DAH to write the byte in Register A to the file through the FCB in the alternate Register Pair DE.
5188
DEC C 0D
Decrement Register C, the data bytes left in this record.
5189
If the NZ FLAG is set (Register C is not 0, more data in the record), LOOP back to 5183H.
518B
LOOP back to 5172H for the next record, with Register Pair HL = the bytes left.
518D
OR A B7
OR Register A with itself to clear the CARRY FLAG before the ADC.
518E
ADC HL,BC ED 4A
Add Register Pair BC (254) back to Register Pair HL: Register Pair HL = the bytes left (0-253), and the Z FLAG is set if none are left.
5190
LD C,L 4D
Load Register C with Register L, the bytes left (0-253), the data length of the last record.
5191
LD L,H 6C
Load Register L with Register H (00H, since fewer than 254 are left): Register Pair HL = 0000H, nothing left after this record. LD does not change the flags.
5192
If the NZ FLAG is set (bytes are left), JUMP to 517AH for a short record of Register C bytes; 5172H then finds Register Pair HL = 0000H and comes back here with the Z FLAG.
5194
POP DE D1
Restore Register Pair DE from the stack: the entry address, pushed at 5165H.
5195
LD B,02H 06 02
Load Register B with 02H, record type 02H: the transfer address.
5197
LD C,B 48
Load Register C with Register B: the length byte 02H, for the two address bytes.
5198
GOSUB to 51B3H to write the transfer record: the type in Register B, the length in Register C and the entry address in Register Pair DE (translated if it lies in the dumped range).
519B
EXX D9
Exchange Register Pairs BC, DE and HL with the alternate set again: the main Register Pair DE = the FCB address 4480H again.
519C
GOSUB to SYS0/SYS 443FH to rewind the file at Register Pair DE (NEXT = 0), which also writes the last buffer if it was changed.
519F
If the NZ FLAG is set (an error, here or from 514DH), JUMP to 51E0H to give the error code in Register A.
51A1
JUMP to SYS0/SYS 4428H to close the file at Register Pair DE (RST 28H code 25H, SYS3/SYS); the result goes to the command's return address, SYS0/SYS 4408H, now on top of the stack.
51A4
Without records: GOSUB to 51BBH to write the address in Register Pair DE (the start, translated: the load address, which is the start itself since no load address was read) as two bytes.

Raw Data Loop
Register Pair DE = the next memory byte, Register Pair HL = the bytes left.

51A7
LD A,(DE) 1A
Load Register A with the byte at Register Pair DE, the next byte of the memory being dumped.
51A8
INC DE 13
Increment Register Pair DE, the memory pointer, to the next byte to dump.
51A9
GOSUB to 51DAH to write the byte in Register A to the file through the FCB in the alternate Register Pair DE.
51AC
DEC HL 2B
Decrement Register Pair HL, the bytes left to write.
51AD
LD A,H 7C
Load Register A with Register H, the high byte of the bytes left, to test Register Pair HL for zero.
51AE
OR L B5
OR Register A (the high byte) with Register L, the low byte: the Z FLAG is set when no bytes are left.
51AF
If the NZ FLAG is set (Register Pair HL is not 0), LOOP back to 51A7H for the next byte.
51B1
JUMP to 519BH to switch back to the FCB in Register Pair DE, rewind and close the file.

51B3H - DUMP: Write a Record Header and an Address

51B3H writes Register B (the record type) and Register C (the length byte). 51BBH writes the address in Register Pair DE as two bytes, translated when it lies between start and end: address - start + load. 51DAH writes the byte in Register A through ROM 001BH with the FCB in the alternate Register Pair DE, and ends DUMP with the error on a write error.

51B3
LD A,B 78
Load Register A with Register B, the record type (01H data or 02H transfer).
51B4
GOSUB to 51DAH to write the record type in Register A to the file.
51B7
LD A,C 79
Load Register A with Register C, the record's length byte (data length plus 2, or 02H).
51B8
GOSUB to 51DAH to write the length byte in Register A to the file.
51BB
PUSH HL E5
Save Register Pair HL, the caller's count of bytes left, on the stack.
51BC
PUSH DE D5
Save Register Pair DE, the address to write (the record's first memory byte or the entry address), on the stack.
51BD
LD HL,0000H 21 00 00
Self-Modifying Code
Load Register Pair HL with the end address of the dump, the operand at 51BEH (0000H in the file, stored at 5157H).
51C0
RST 18H DF
RST 18H: compare Register Pair HL (the end address) with Register Pair DE (the address to write); the CARRY FLAG is set if the address is past the end.
51C1
EX DE,HL EB
Exchange Register Pairs DE and HL: Register Pair HL = the address to write and Register Pair DE = the end address; EX does not change the flags.
51C2
If the CARRY FLAG is set (the address is past the end), JUMP to 51D3H to write it unchanged.
51C4
LD DE,0000H 11 00 00
Self-Modifying Code
Load Register Pair DE with the start address of the dump, the operand at 51C5H (0000H in the file, stored at 515BH).
51C7
RST 18H DF
RST 18H: compare Register Pair HL (the address to write) with Register Pair DE (the start address); the CARRY FLAG is set if the address is below the start.
51C8
If the CARRY FLAG is set (the address is below the start), JUMP to 51D3H to write it unchanged.
51CA
PUSH HL E5
Save Register Pair HL, the address to write, on the stack.
51CB
LD HL,0000H 21 00 00
Self-Modifying Code
Load Register Pair HL with the load address, the operand at 51CCH (0000H in the file, stored at 5153H).
51CE
OR A B7
OR Register A with itself to clear the CARRY FLAG before the SBC.
51CF
SBC HL,DE ED 52
Subtract Register Pair DE (the start address) from Register Pair HL (the load address): Register Pair HL = the load address minus the start.
51D1
POP DE D1
Restore Register Pair DE from the stack: the address to write.
51D2
ADD HL,DE 19
Add Register Pair DE (the address) to Register Pair HL (load minus start): Register Pair HL = the address translated to where it will be loaded.
51D3
LD A,L 7D
Load Register A with Register L, the low byte of the address to write (translated or not).
51D4
GOSUB to 51DAH to write the low byte in Register A to the file.
51D7
LD A,H 7C
Load Register A with Register H, the high byte of the address, which 51DAH below writes before returning to the caller.
51D8
POP DE D1
Restore Register Pair DE from the stack: the caller's memory pointer (or entry address), saved at 51BCH.
51D9
POP HL E1
Restore Register Pair HL from the stack: the caller's count of bytes left, saved at 51BBH.
51DA
EXX D9
Exchange Register Pairs BC, DE and HL with the alternate set: Register Pair DE = the DOS FCB at 4480H (kept there since 514FH).
51DB
GOSUB to ROM 001BH, the output routine, to send the byte in Register A to the control block at Register Pair DE; byte 0 of the FCB (bit 7 set, open) is not the output device type 02H, so the ROM's dispatcher jumps through 4033H to SYS0/SYS 4A80H, which writes the byte to the file and returns the Z FLAG, or the NZ FLAG with the error code in Register A.
51DE
EXX D9
Exchange Register Pairs BC, DE and HL with the alternate set again: the caller's Register Pairs BC, DE and HL again; the flags from the write are kept.
51DF
RET Z C8
If the Z FLAG is set (the byte in Register A was written), RETURN to the caller.
51E0
JUMP to 4D65H, the error exit, with the error code from the write (or from the rewind at 514AH or 519CH) in Register A.
51E3-51E7
DEFB 00H x 5 00 x 5
Five bytes of 00H at the end of the file, after the JP at 51E0H; no instruction reaches them.
51E8-51FF
DEFS 24
Not in the file: the rest of the overlay area. SYS7/SYS uses 51E8H-51EFH as the eight-byte password buffer (5005H) and 51E8H-51F0H for the HIMEM, TIME and DATE text with its carriage return (4D4AH, 4D86H).