TRS-80 DOS - TRSDOS v2.3 for the Model I - TAPEDISK/CMD Disassembled

Page Customization

Introduction/Summary

TRSDOS v2.3 TAPEDISK/CMD Disassembly - Cassette to Disk (Model I)

TAPEDISK/CMD moves a machine language program from a SYSTEM cassette tape to a disk file. It shows ? and takes one of three commands: C reads a SYSTEM tape into memory at the addresses its blocks name; F filespec:d ssss eeee xxxx writes memory from ssss to eeee to the file as a load module with transfer address xxxx; E goes back to DOS READY. Anything else shows SN ERROR. Nothing is written to tape, and the program read from tape is not run.

The file is 512 bytes: two load records (5200H-52FFH, whose length byte 02H means 256 bytes, and 5300H-53F1H), a transfer record (5200H) and two bytes after it that are not part of the module. There is no name record and no comment record. The 498 loaded bytes are all on this page, each on one row, checked against S:\1\TAPEDISK.CMD and its listing.

Memory Map

Address RangeUse
3C3EH-3C3FH
2 bytes
The last two columns of the top line of the screen: the checksum error mark C (3C3EH) and the character that blinks once per good block (3C3FH).
403DH
1 byte
The ROM's copy of the last value sent to port FFH; bit 2 is the cassette motor.
4318H-4357H
64 bytes
SYS0/SYS's command line buffer: the line typed after ?. For F the filespec in it serves as the FCB.
5200H-522BH
44 bytes
The prompt and the choice of command.
522CH-5267H
60 bytes
F: the filespec and the three addresses.
5268H-52CEH
103 bytes
F: open the file, write the load module, close; the disk error and SN ERROR exits.
52CFH-5326H
88 bytes
C: read a SYSTEM tape into memory.
5327H-53A5H
127 bytes
Put a load record, put a byte into the sector buffer, skip spaces, read an address from tape, display a text, hexadecimal digits.
53A6H-53E9H
68 bytes
Read a byte and a bit from tape; the cassette motor and port FFH.
53EAH-53F1H
8 bytes
The text SN ERROR.
53F2H-53F3H
2 bytes
Not in the file: the transfer address of F.
53F4H-54F3H
256 bytes
Not in the file: the sector buffer of the file F writes.

The Commands

C turns the cassette motor on, disables interrupts and reads the tape (see below). The data blocks are stored wherever the tape says, with no check on the address. When the end block comes, the motor is turned off, interrupts are enabled and ? is shown again. The transfer address on the tape is read but not kept, so it has to be typed with F.

F expects the filespec after F and any spaces, with a colon and a one-character drive number, then the first address, the last address and the transfer address, each as four hexadecimal digits (capital A-F) separated by spaces, for example F PROG/CMD:1 7000 7FFF 7000. The addresses go into the operands at 5277H, 527AH and 53F2H. The FCB is the command line buffer itself, from the first character of the filespec on: 4420H opens the file or creates it, with whole 256-byte records and the sector buffer at 53F4H. The load module is built in the sector buffer by 5342H, which writes each sector as it fills: type 01H records of 256 bytes (length byte 02H) from the first address, then one record with the bytes left (length byte = the count plus 2), then the type 02H record with the transfer address. The part-filled last sector is written, the file is closed and ? is shown again. A disk error shows its message through 4409H (with bit 7 set, so it comes back) and returns to ?; the file is not closed then.

E jumps to 402DH, DOS READY.

The Tape Format Read

Each bit is a clock pulse followed by a data pulse for 1 or no pulse for 0. A pulse sets bit 7 of port FFH and any output to port FFH clears it. 53B0H waits for bit 7 (the clock pulse), waits 65 loop turns, clears the latch, waits 118 turns and reads bit 7 as the data bit, then clears the latch again. The search for the sync byte A5H goes bit by bit; after it, the bytes read are:

BytesMeaning
55H
1 byte
Start of a SYSTEM tape; other bytes before it are passed over.
Name
6 bytes
Read and not used.
3CH, n, low, high, data, sum
n + 5 bytes
A data block: n bytes (00H = 256) stored from the address low, high. The checksum is the sum of the two address bytes and the data, modulo 256. Bytes other than 3CH and 78H between blocks are passed over.
78H, low, high
3 bytes
The end: the transfer address, read and not used.

Notes from the Code

  • A bad checksum only puts a C at 3C3EH: the block stays in memory as read and the next block is read. The C stays on the screen.
  • BREAK is not looked at while the tape is read; a tape that never gives the 78H block leaves TAPEDISK reading until RESET.
  • A tape block can be stored over TAPEDISK itself (5200H-53F3H) or over TRSDOS, as the addresses are not checked.
  • 5232H looks for the colon without a limit: a filespec without a drive number makes it search past the line typed.
  • The count of bytes F writes is split by DEC H and a test of the SIGN FLAG (5282H): from 8100H bytes on, no 256-byte records are written, only the record of the bytes left in Register L.
  • The part of the last sector after the module holds what the buffer held before; the loader stops at the 02H record, so it is never read.
  • Five NOP bytes follow the C test (521DH-5221H), and a second JP 5200H at 5324H is not reached.
  • 53E7H tests the motor bit, but no caller uses the result.

Variables

Variables and Changed Operands

Address RangePurpose
5269H-526AH
2 bytes
The FCB address, the start of the filespec in the command line buffer (522FH); the operand of LD DE,nn at 5268H, read also at 52ABH and 5352H.
5277H-5278H
2 bytes
The first address of F (5245H low, 523EH high), the operand of LD DE,nn at 5276H.
527AH-527BH
2 bytes
The last address of F (5255H low, 524EH high), the operand of LD HL,nn at 5279H.
53F2H-53F3H
2 bytes
The transfer address of F (5265H low, 525EH high), read at 529DH and 52A3H.
53F4H-54F3H
256 bytes
The sector buffer (526DH), filled by 5342H at 53F4H plus Register C.
403DH
1 byte
The ROM's port FFH copy, read and written by 53DDH-53E4H.
3C3EH-3C3FH
2 bytes
Screen: the checksum error mark (5315H) and the blinking character (52E8H-52EDH).

Disassembly

5200H - TAPEDISK Starts: the Prompt and the Command

SYS1/SYS loads TAPEDISK/CMD at 5200H and jumps here; every command comes back here too. Shows ? on a new line and reads a line of up to 63 characters into the command line buffer at 4318H. The first character picks the command: C reads a SYSTEM tape into memory (52CFH), E goes back to DOS READY (402DH), F writes memory to a disk file (522CH); anything else shows SN ERROR.

5200
LD SP,41FCH 31 FC 41
Load the Stack Pointer with 41FCH, the top of TRSDOS's stack area below 4200H; 5321H, 5324H, 52B9H, 52C1H and 52CCH jump here, so each command starts with a clean stack.
5203
LD A,0DH 3E 0D
Load Register A with 0DH, ENTER, to start the prompt on a new line.
5205
GOSUB to ROM routine at 0033H to display the character in Register A (0DH) at the cursor.
5208
LD A,3FH 3E 3F
Show the Prompt
Load Register A with 3FH, the character ?, the prompt. 5215H jumps here after BREAK.
520A
GOSUB to ROM routine at 0033H to display the character in Register A (?) at the cursor.
520D
LD B,3FH 06 3F
Load Register B with 3FH: up to 63 characters may be typed.
520F
LD HL,4318H 21 18 43
Point Register Pair HL to 4318H, SYS0/SYS's command line buffer (4318H-4357H), where the line is read.
5212
GOSUB to ROM routine at 05D9H, the line input that 0040H jumps to: reads up to Register B characters into the buffer at Register Pair HL, ending with ENTER; returns the CARRY FLAG for BREAK and Register Pair HL = 4318H.
5215
If the CARRY FLAG is set (BREAK was pressed), LOOP BACK to 5208H to show ? again.
5217
LD A,(HL) 7E
Fetch the first character typed from (HL) (Register Pair HL = 4318H) into Register A: the command letter.
5218
CP 43H FE 43
Compare Register A (the command letter) against 43H, the letter C.
521A
If the Z FLAG is set (C), JUMP to 52CFH to read a SYSTEM tape into memory.
521D
NOP 00
No Operation: the first of five NOP bytes between the C test and the E test.
521E
NOP 00
No Operation: the second of the five NOP bytes after the C test.
521F
NOP 00
No Operation: the third of the five NOP bytes after the C test.
5220
NOP 00
No Operation: the fourth of the five NOP bytes after the C test.
5221
NOP 00
No Operation: the fifth of the five NOP bytes; Register A still holds the command letter.
5222
CP 45H FE 45
Compare Register A (the command letter) against 45H, the letter E.
5224
If the Z FLAG is set (E), JUMP to 402DH, SYS0/SYS's DOS READY exit: TAPEDISK ends.
5227
CP 46H FE 46
Compare Register A (the command letter) against 46H, the letter F.
5229
If the NZ FLAG is set (none of C, E or F), JUMP to 52C4H to show SN ERROR and prompt again.

522CH - The F Command: Read the File Name and the Addresses

The line is F filespec:d ssss eeee xxxx, with ssss the first address, eeee the last address and xxxx the transfer address, each exactly four hexadecimal digits (0-9, A-F). The filespec is not copied: its place in the command line buffer serves as the FCB, and its address is stored at 5269H. The four digits of each address are turned into bytes and stored into the operands of the code that uses them: 5277H-5278H, 527AH-527BH and 53F2H-53F3H. A character that is not a hexadecimal digit gives SN ERROR (53A0H).

522C
GOSUB to 535EH to move Register Pair HL past the F and any spaces: Register Pair HL = the first character of the filespec, Register A = that character.
522F
LD (5269H),HL 22 69 52
Store Register Pair HL (the start of the filespec in the command line buffer) into 5269H, the operand of LD DE,nn at 5268H: the FCB address used by every disk call.
5232
LD A,(HL) 7E
Find the Colon
Fetch the next character of the filespec from (HL) (Register Pair HL = the text pointer) into Register A. 5236H loops back here.
5233
INC HL 23
INCrement Register Pair HL (the text pointer) by 1, past the character just fetched.
5234
CP 3AH FE 3A
Compare Register A (the character) against 3AH, the colon before the drive number.
5236
Find the Colon End
If the NZ FLAG is set (not the colon), LOOP BACK to 5232H; Register Pair HL ends on the character after the colon, the drive number.
5238
GOSUB to 535EH to move Register Pair HL past the one-character drive number and any spaces: Register Pair HL = the first digit of the first address.
523B
GOSUB to 5376H to turn the two hexadecimal digits at Register Pair HL into Register A; Register Pair HL is left on the second digit.
523E
LD (5278H),A 32 78 52
Store Register A (the high byte of the first address) into 5278H, the high byte of the operand of LD DE,nn at 5276H.
5241
INC HL 23
INCrement Register Pair HL (the text pointer) by 1, to the third digit of the first address.
5242
GOSUB to 5376H to turn the third and fourth digits at Register Pair HL into Register A.
5245
LD (5277H),A 32 77 52
Store Register A (the low byte of the first address) into 5277H, the low byte of the operand of LD DE,nn at 5276H.
5248
GOSUB to 535EH to move Register Pair HL past the last digit and any spaces: Register Pair HL = the first digit of the last address.
524B
GOSUB to 5376H to turn the first two digits of the last address into Register A.
524E
LD (527BH),A 32 7B 52
Store Register A (the high byte of the last address) into 527BH, the high byte of the operand of LD HL,nn at 5279H.
5251
INC HL 23
INCrement Register Pair HL (the text pointer) by 1, to the third digit of the last address.
5252
GOSUB to 5376H to turn the third and fourth digits of the last address into Register A.
5255
LD (527AH),A 32 7A 52
Store Register A (the low byte of the last address) into 527AH, the low byte of the operand of LD HL,nn at 5279H.
5258
GOSUB to 535EH to move Register Pair HL past the last digit and any spaces: Register Pair HL = the first digit of the transfer address.
525B
GOSUB to 5376H to turn the first two digits of the transfer address into Register A.
525E
LD (53F3H),A 32 F3 53
Store Register A (the high byte of the transfer address) into 53F3H, one byte past the end of the file.
5261
INC HL 23
INCrement Register Pair HL (the text pointer) by 1, to the third digit of the transfer address.
5262
GOSUB to 5376H to turn the third and fourth digits of the transfer address into Register A.
5265
LD (53F2H),A 32 F2 53
Store Register A (the low byte of the transfer address) into 53F2H, the byte before 53F3H.

5268H - The F Command: Open the File and Write the Load Module

Opens or creates the file with whole 256-byte records and the sector buffer at 53F4H, then writes a load module through 5342H, which collects the bytes at 53F4H and writes each full sector: type 01H records of 256 bytes from the first address on, one shorter record for the rest, and a type 02H record with the transfer address. The last part of a sector is written, the file is closed, and ? is shown again. A disk error shows its message (4409H) and goes back to the prompt.

5268
LD DE,431AH 11 1A 43
Point Register Pair DE to the FCB, the start of the filespec in the command line buffer stored into this operand at 522FH (431AH in the file, the character after F and a space).
526B
LD B,00H 06 00
Load Register B with 00H, the logical record length: 00H means whole 256-byte sectors.
526D
LD HL,53F4H 21 F4 53
Point Register Pair HL to 53F4H, the file's sector buffer, just past TAPEDISK's code and the transfer address.
5270
GOSUB to SYS0/SYS's 4420H to open the file named in the FCB at Register Pair DE, or create it when it is not in the directory; Register A returns 00H and the Z FLAG with the file open.
5273
If the NZ FLAG is set (Register A = an error number), JUMP to 52BCH to show the error and prompt again.
5276
LD DE,5200H 11 00 52
Load Register Pair DE with the first address, stored into this operand at 5245H (low byte) and 523EH (high byte); 5200H in the file.
5279
LD HL,53F1H 21 F1 53
Load Register Pair HL with the last address, stored into this operand at 5255H (low byte) and 524EH (high byte); 53F1H in the file.
527C
XOR A AF
Set Register A to ZERO with XOR A, which also clears the CARRY FLAG for the SBC.
527D
SBC HL,DE ED 52
SUBtract Register Pair DE (the first address) from Register Pair HL (the last address) with the CARRY FLAG (clear).
527F
INC HL 23
INCrement Register Pair HL by 1: Register Pair HL = the number of bytes to write, the last address included.
5280
LD C,00H 0E 00
Load Register C with 00H, the position in the sector buffer at 53F4H where 5342H puts the next byte.
5282
DEC H 25
Block Loop
DECrement Register H (the count of 256-byte blocks left) by 1: the SIGN FLAG is set when the result has bit 7 set, which is below 00H, or at once for a count of 8100H bytes or more. 528BH loops back here.
5283
If the SIGN FLAG is set (no whole block left), JUMP to 528DH for the bytes that remain in Register L.
5286
LD B,00H 06 00
Load Register B with 00H: a record of 256 bytes.
5288
GOSUB to 5327H to put a type 01H record of Register B (256) bytes from (DE) into the file; Register Pair DE moves on by 256.
528B
Block Loop End
JUMP to 5282H for the next 256 bytes.
528D
XOR A AF
Set Register A to ZERO with XOR A. 5283H jumps here.
528E
CP L BD
Compare Register A (00H) against Register L, the bytes left after the whole blocks: the Z FLAG is set when none are left.
528F
If the Z FLAG is set (no bytes left), JUMP to 5295H to write the transfer record.
5291
LD B,L 45
Copy the bytes left from Register L into Register B, the length of the last record.
5292
GOSUB to 5327H to put a type 01H record of Register B bytes from (DE) into the file.
5295
LD A,02H 3E 02
Load Register A with 02H, the type of the transfer record. 528FH jumps here.
5297
GOSUB to 5342H to put Register A (02H, the record type) into the sector buffer.
529A
GOSUB to 5342H to put Register A (still 02H, now the record's length: the two address bytes) into the sector buffer.
529D
LD A,(53F2H) 3A F2 53
Fetch the low byte of the transfer address from 53F2H, stored there at 5265H, into Register A.
52A0
GOSUB to 5342H to put Register A (the low byte of the transfer address) into the sector buffer.
52A3
LD A,(53F3H) 3A F3 53
Fetch the high byte of the transfer address from 53F3H, stored there at 525EH, into Register A.
52A6
GOSUB to 5342H to put Register A (the high byte of the transfer address) into the sector buffer.
52A9
XOR A AF
Set Register A to ZERO with XOR A, the value Register C is compared with.
52AA
CP C B9
Compare Register A (00H) against Register C, the bytes in the sector buffer: the Z FLAG is set when the buffer was just written out by 5342H.
52AB
LD DE,(5269H) ED 5B 69 52
Point Register Pair DE to the FCB, the operand at 5269H stored by 522FH; LD does not change the flags.
52AF
If the Z FLAG is set (the buffer is empty), JUMP to 52B6H to close the file.
52B1
GOSUB to SYS0/SYS's 4439H to write the sector buffer at 53F4H as the next sector of the file in the FCB at Register Pair DE; the bytes after the first Register C are what the buffer held before.
52B4
If the NZ FLAG is set (Register A = an error number), JUMP to 52BCH to show the error.
52B6
GOSUB to SYS0/SYS's 4428H to close the file in the FCB at Register Pair DE, writing its end of file into the directory. 52AFH jumps here.
52B9
If the Z FLAG is set (closed), JUMP to 5200H for the next command.
52BC
OR 80H F6 80
Disk Error
OR Register A (the error number) with 80H: bit 7 asks 4409H to come back after the message. 5273H, 52B4H and 5359H jump here.
52BE
GOSUB to SYS0/SYS's 4409H to display the message of the error number in Register A (bits 0-5).
52C1
JUMP to 5200H for the next command; the file is left as it was when the error came.
52C4
LD HL,53EAH 21 EA 53
Syntax Error
Point Register Pair HL to 53EAH, the text SN ERROR. 5229H jumps here, and 53A0H makes a bad digit return here.
52C7
LD B,08H 06 08
Load Register B with 08H, the eight characters of SN ERROR.
52C9
GOSUB to 536EH to display Register B characters from (HL) (Register Pair HL = 53EAH).
52CC
JUMP to 5200H to start a new line and prompt again.

52CFH - The C Command: Read a SYSTEM Tape into Memory

Starts the cassette motor and reads bits until the sync byte A5H, then waits for the header byte 55H and skips the six bytes of the name. Then it reads blocks: 3CH, a count (00H = 256), a load address and the data, which is stored at that address, then a checksum. After a good block the character at the top right of the screen (3C3FH) is changed (XOR 0AH turns an asterisk into a space and back); after a bad one a C is put at 3C3EH and reading goes on. The block 78H ends the tape: its address is read but not used, the motor is stopped and ? is shown again. Interrupts are off while the tape is read; BREAK is not looked at.

52CF
DI F3
Disable interrupts: the bit timing of 53B0H is counted in loop turns and an interrupt would upset it. 521AH jumps here.
52D0
GOSUB to 53D5H to set bit 2 of port FFH, which starts the cassette motor.
52D3
Find the Sync Byte
GOSUB to 53B0H to read one bit from the tape and shift it into Register A from the right. 52D8H loops back here.
52D6
CP A5H FE A5
Compare Register A (the last eight bits read) against A5H, the sync byte.
52D8
If the NZ FLAG is set (no sync byte yet), LOOP BACK to 52D3H for one more bit: the search moves bit by bit.
52DA
Find the Header
GOSUB to 53A6H to read the next eight bits, a whole byte, into Register A. 52DFH loops back here.
52DD
CP 55H FE 55
Compare Register A (the byte read) against 55H, the byte that starts a SYSTEM tape.
52DF
If the NZ FLAG is set (not 55H), LOOP BACK to 52DAH for the next byte.
52E1
LD B,06H 06 06
Load Register B with 06H, the six characters of the program name after 55H.
52E3
Skip the Name
GOSUB to 53A6H to read a byte of the name into Register A; it is not used. 52E6H loops back here.
52E6
Skip the Name End
DECrement Register B and LOOP BACK to 52E3H until the six bytes are read.
52E8
LD A,(3C3FH) 3A 3F 3C
Next Block, Good
Fetch the character at 3C3FH, the last column of the top line of the screen, into Register A. 5311H jumps here after a good block.
52EB
XOR 0AH EE 0A
XOR Register A (the character) with 0AH: 2AH (an asterisk) becomes 20H (a space) and 20H becomes 2AH.
52ED
LD (3C3FH),A 32 3F 3C
Store Register A (the changed character) into 3C3FH: the corner of the screen blinks once per good block.
52F0
Find a Block
GOSUB to 53A6H to read the next byte from the tape into Register A. 52F9H and 5318H loop back here.
52F3
CP 78H FE 78
Compare Register A (the byte read) against 78H, the start of the block with the transfer address.
52F5
If the Z FLAG is set (78H), JUMP to 531AH to end the tape.
52F7
CP 3CH FE 3C
Compare Register A (the byte read) against 3CH, the start of a data block.
52F9
If the NZ FLAG is set (neither 78H nor 3CH), LOOP BACK to 52F0H to read the next byte.
52FB
GOSUB to 53A6H to read the block's count into Register A.
52FE
LD B,A 47
Copy the count from Register A into Register B, the bytes still to read; 00H gives 256.
52FF
GOSUB to 5365H to read the block's load address, low byte then high byte, into Register Pair HL; Register A returns the high byte (Register H).
5302
ADD A,L 85
ADD Register L (the low byte of the address) to Register A (the high byte): the checksum starts with the sum of the two address bytes.
5303
LD C,A 4F
Copy the checksum from Register A into Register C.
5304
Data Loop
GOSUB to 53A6H to read the next data byte into Register A. 530BH loops back here.
5307
LD (HL),A 77
Store the data byte from Register A into (HL), Register Pair HL = the load address of this byte.
5308
INC HL 23
INCrement Register Pair HL (the load address) by 1.
5309
ADD A,C 81
ADD Register C (the checksum so far) to Register A (the data byte), modulo 256.
530A
LD C,A 4F
Copy the new checksum from Register A into Register C.
530B
Data Loop End
DECrement Register B (the bytes still to read) and LOOP BACK to 5304H until the block is stored.
530D
GOSUB to 53A6H to read the block's checksum byte from the tape into Register A.
5310
CP C B9
Compare Register A (the checksum on the tape) against Register C (the sum of the address and data bytes).
5311
If the Z FLAG is set (they agree), LOOP BACK to 52E8H to blink the corner and read the next block.
5313
LD A,43H 3E 43
Load Register A with 43H, the letter C, the mark of a checksum error.
5315
LD (3C3EH),A 32 3E 3C
Store Register A (C) into 3C3EH, the column next to the blinking corner of the top line; the bad block stays in memory as it was read.
5318
LOOP BACK to 52F0H to look for the next block, without blinking the corner.
531A
GOSUB to 5365H to read the transfer address, low byte then high byte, into Register Pair HL; it is not used. 52F5H jumps here.
531D
GOSUB to 53D0H to clear bit 2 of port FFH, which stops the cassette motor.
5320
EI FB
Enable interrupts again, after the tape has been read.
5321
JUMP to 5200H for the next command, normally F to write what was read to a disk file.
5324
JUMP to 5200H: a second JP 5200H that no code in TAPEDISK reaches.

5327H - Put a Load Record into the File

Register B = the number of data bytes (00H = 256), Register Pair DE = the address of the first one. Puts the record type 01H, the length (Register B plus 2, for the two address bytes), the address low byte and high byte, and the data bytes through 5342H. Returns Register Pair DE past the last byte and Register B = 00H.

5327
LD A,01H 3E 01
Load Register A with 01H, the type of a load record. 5288H and 5292H call here.
5329
GOSUB to 5342H to put Register A (01H) into the sector buffer.
532C
LD A,02H 3E 02
Load Register A with 02H, the two address bytes the length byte counts besides the data.
532E
ADD A,B 80
ADD Register B (the data bytes) to Register A: the length byte; 256 data bytes give 02H, which the loader reads as 256.
532F
GOSUB to 5342H to put Register A (the length byte) into the sector buffer.
5332
LD A,E 7B
Copy Register E (the low byte of the record's address) into Register A.
5333
GOSUB to 5342H to put Register A (the low byte of the address) into the sector buffer.
5336
LD A,D 7A
Copy Register D (the high byte of the record's address) into Register A.
5337
GOSUB to 5342H to put Register A (the high byte of the address) into the sector buffer.
533A
LD A,(DE) 1A
Data Loop
Fetch the next byte of memory from (DE) (Register Pair DE = the address of the byte) into Register A. 533FH loops back here.
533B
GOSUB to 5342H to put Register A (the data byte) into the sector buffer.
533E
INC DE 13
INCrement Register Pair DE (the address of the byte) by 1.
533F
Data Loop End
DECrement Register B (the bytes left) and LOOP BACK to 533AH until the record is done.
5341
RET C9
RETURN to 528BH or 5295H with Register Pair DE = the address after the record.

5342H - Put a Byte into the Sector Buffer

Register A = the byte, Register C = its position in the buffer at 53F4H. Stores the byte at 53F4H plus Register C and counts Register C up; when it wraps to 00H the 256 bytes are full and are written as the next sector of the file (4439H). A write error goes to 52BCH. Register Pair DE and Register B are kept.

5342
LD IX,53F4H DD 21 F4 53
Point Index Register IX to 53F4H, the sector buffer. Called by 5297H-52A6H and 5329H-533BH.
5346
PUSH BC C5
Save Register Pair BC (Register B = the caller's count, Register C = the position) onto the stack.
5347
LD B,00H 06 00
Load Register B with 00H: Register Pair BC = the position in the buffer, 0000H-00FFH.
5349
ADD IX,BC DD 09
ADD Register Pair BC (the position) to Index Register IX: Index Register IX = 53F4H plus the position.
534B
POP BC C1
Restore Register Pair BC (the caller's count and the position) from the stack.
534C
LD (IX+00H),A DD 77 00
Store Register A (the byte) into (IX+00H), Index Register IX = the byte's place in the sector buffer.
534F
INC C 0C
INCrement Register C (the position) by 1: the Z FLAG is set when it wraps from FFH to 00H, the buffer full.
5350
RET NZ C0
If the NZ FLAG is set (room left), RETURN to the caller.
5351
PUSH DE D5
Save Register Pair DE (the caller's memory address) onto the stack.
5352
LD DE,(5269H) ED 5B 69 52
Point Register Pair DE to the FCB, the operand at 5269H stored by 522FH.
5356
GOSUB to SYS0/SYS's 4439H to write the 256 bytes at 53F4H as the next sector of the file in the FCB at Register Pair DE.
5359
If the NZ FLAG is set (Register A = an error number), JUMP to 52BCH to show the error; 5200H resets the Stack Pointer afterwards.
535C
POP DE D1
Restore Register Pair DE (the caller's memory address) from the stack.
535D
RET C9
RETURN to the caller with Register C = 00H, the buffer empty again.

535EH - Skip to the Next Word

Register Pair HL = the text. Moves Register Pair HL on by one character, then past any spaces. Returns Register Pair HL = the first character that is not a space and Register A = that character.

535E
INC HL 23
INCrement Register Pair HL (the text pointer) by 1. 522CH, 5238H, 5248H and 5258H call here; 5362H loops back here.
535F
LD A,(HL) 7E
Fetch the character at (HL) (Register Pair HL = the text pointer) into Register A.
5360
CP 20H FE 20
Compare Register A (the character) against 20H, a space.
5362
If the Z FLAG is set (a space), LOOP BACK to 535EH to skip it.
5364
RET C9
RETURN to the caller with Register Pair HL and Register A = the first character that is not a space.

5365H - Read an Address from the Tape

Reads two bytes through 53A6H, low byte first, into Register Pair HL. Returns Register A = the high byte.

5365
GOSUB to 53A6H to read the low byte of the address from the tape into Register A. 52FFH and 531AH call here.
5368
LD L,A 6F
Copy the low byte from Register A into Register L.
5369
GOSUB to 53A6H to read the high byte of the address from the tape into Register A.
536C
LD H,A 67
Copy the high byte from Register A into Register H: Register Pair HL = the address read.
536D
RET C9
RETURN to the caller with Register Pair HL = the address and Register A = its high byte.

536EH - Display a Text

Register Pair HL = the text, Register B = its length. Shows Register B characters through the ROM.

536E
LD A,(HL) 7E
Display Loop
Fetch the next character from (HL) (Register Pair HL = the text pointer) into Register A. 52C9H calls here; 5373H loops back here.
536F
GOSUB to ROM routine at 0033H to display the character in Register A at the cursor.
5372
INC HL 23
INCrement Register Pair HL (the text pointer) by 1.
5373
Display Loop End
DECrement Register B (the characters left) and LOOP BACK to 536EH until the text is shown.
5375
RET C9
RETURN to 52CCH with the text shown and Register Pair HL past its last character.

5376H - Two Hexadecimal Digits to a Byte

Register Pair HL = the first of two hexadecimal digits. Returns Register A = their value, with Register Pair HL on the second digit. A character that is not 0-9 or A-F makes 538AH return to 52C4H (SN ERROR) instead.

5376
LD A,(HL) 7E
Fetch the first digit from (HL) (Register Pair HL = the text pointer) into Register A. Called by 523BH-5262H.
5377
GOSUB to 538AH to turn the digit in Register A into its value 00H-0FH.
537A
LD B,A 47
Copy the value of the first digit from Register A into Register B, the high four bits to be.
537B
INC HL 23
INCrement Register Pair HL (the text pointer) by 1, to the second digit.
537C
LD A,(HL) 7E
Fetch the second digit from (HL) (Register Pair HL = the text pointer) into Register A.
537D
GOSUB to 538AH to turn the digit in Register A into its value 00H-0FH.
5380
SLA B CB 20
Shift Register B (the first digit's value) left by one bit: times 2.
5382
SLA B CB 20
Shift Register B (the first digit's value) left again: times 4.
5384
SLA B CB 20
Shift Register B (the first digit's value) left a third time: times 8.
5386
SLA B CB 20
Shift Register B left a fourth time: the first digit's value is now in bits 4-7.
5388
OR B B0
OR Register A (the second digit's value, bits 0-3) with Register B (the first digit, bits 4-7): Register A = the byte.
5389
RET C9
RETURN to the caller with Register A = the byte.

538AH - One Hexadecimal Digit

Register A = the character. Returns Register A = 00H-09H for 0-9 and 0AH-0FH for A-F (capital letters only). Any other character goes to 53A0H, which makes 5376H return to 52C4H.

538A
CP 41H FE 41
Compare Register A (the character) against 41H, the letter A: the CARRY FLAG is set below it. Called by 5377H and 537DH.
538C
If the CARRY FLAG is set (below A), JUMP to 5395H to test for a digit.
538E
CP 47H FE 47
Compare Register A (the character) against 47H, the letter after F: the NO CARRY FLAG is set for G or above.
5390
If the NO CARRY FLAG is set (above F), JUMP to 53A0H: not a hexadecimal digit.
5392
SUB 37H D6 37
SUBtract 37H from Register A: the letters A-F (41H-46H) become 0AH-0FH.
5394
RET C9
RETURN to 537AH or 5380H with Register A = the value of the letter.
5395
CP 30H FE 30
Compare Register A (the character) against 30H, the digit 0: the CARRY FLAG is set below it. 538CH jumps here.
5397
If the CARRY FLAG is set (below 0), JUMP to 53A0H: not a hexadecimal digit.
5399
CP 3AH FE 3A
Compare Register A (the character) against 3AH, the character after 9: the NO CARRY FLAG is set above 9.
539B
If the NO CARRY FLAG is set (between 9 and A), JUMP to 53A0H: not a hexadecimal digit.
539D
SUB 30H D6 30
SUBtract 30H from Register A: the digits 0-9 (30H-39H) become 00H-09H.
539F
RET C9
RETURN to 537AH or 5380H with Register A = the value of the digit.
53A0
POP BC C1
Not a Digit
Restore Register Pair BC from the stack: it takes off 538AH's return address into 5376H, which is not used. 5390H, 5397H and 539BH jump here.
53A1
LD HL,52C4H 21 C4 52
Point Register Pair HL to 52C4H, the SN ERROR code.
53A4
EX (SP),HL E3
Exchange Register Pair HL with the word at the top of the stack, at the address in the Stack Pointer (SP): 5376H's return address into the F command is replaced by 52C4H.
53A5
RET C9
RETURN to 52C4H, the address just put on the stack, to show SN ERROR.

53A6H - Read a Byte from the Tape

Reads eight bits through 53B0H, the first bit read ending up in bit 7. Returns Register A = the byte; Register Pair BC is kept.

53A6
PUSH BC C5
Save Register Pair BC (the caller's count and checksum) onto the stack. Called by 52DAH-530DH and 5365H-5369H.
53A7
LD B,08H 06 08
Load Register B with 08H, the eight bits of a byte.
53A9
Bit Loop
GOSUB to 53B0H to read one bit and shift it into Register A from the right. 53ACH loops back here.
53AC
Bit Loop End
DECrement Register B (the bits left) and LOOP BACK to 53A9H until eight bits are read.
53AE
POP BC C1
Restore Register Pair BC (the caller's count and checksum) from the stack.
53AF
RET C9
RETURN to the caller with Register A = the byte read.

53B0H - Read One Bit from the Tape

Register A = the bits read so far. Each bit on the tape is a clock pulse, then a pulse for a 1 or none for a 0. Bit 7 of port FFH is set by a pulse and cleared by any output to port FFH. The routine waits for the clock pulse, waits 41H loop turns, clears the latch, waits 76H loop turns more and reads the latch: bit 7 is the bit. The latch is cleared again for the next clock pulse. Returns Register A shifted left with the new bit in bit 0; the main Register Pairs BC, DE and HL are kept by using the alternate set.

53B0
EXX D9
Exchange Register Pairs BC, DE and HL with the alternate set, so that the delays and 53DAH do not change the caller's registers. 52D3H and 53A9H call here.
53B1
EX AF,AF' 08
Exchange Register Pair AF with the alternate Register Pair AF': the bits read so far are kept in AF' while Register A reads the port.
53B2
IN A,(FFH) DB FF
Wait for the Clock Pulse
Read port FFH, the cassette input, into Register A: bit 7 is set once a pulse has come. 53B5H loops back here.
53B4
RLA 17
Rotate Register A left through the CARRY FLAG: bit 7, the pulse latch, goes into the CARRY FLAG.
53B5
If the NO CARRY FLAG is set (no pulse yet), LOOP BACK to 53B2H.
53B7
LD B,41H 06 41
Load Register B with 41H, 65 turns of the delay loop past the clock pulse.
53B9
Delay Loop
DECrement Register B and LOOP BACK to 53B9H (itself) until it reaches 00H: a wait of 65 turns.
53BB
GOSUB to 53DAH to send the port FFH copy to port FFH unchanged, which clears the pulse latch for the data pulse.
53BE
LD B,76H 06 76
Load Register B with 76H, 118 turns of the delay loop into the place of the data pulse.
53C0
Delay Loop
DECrement Register B and LOOP BACK to 53C0H (itself) until it reaches 00H: a wait of 118 turns.
53C2
IN A,(FFH) DB FF
Read port FFH, the cassette input, into Register A: bit 7 is set when a data pulse came, a 1 bit.
53C4
LD B,A 47
Copy the port value from Register A into Register B.
53C5
EX AF,AF' 08
Exchange Register Pair AF with the alternate Register Pair AF' again: Register A = the bits read so far.
53C6
RL B CB 10
Rotate Register B (the port value) left through the CARRY FLAG: its bit 7, the data bit, goes into the CARRY FLAG.
53C8
RLA 17
Rotate Register A (the bits read so far) left through the CARRY FLAG: the data bit comes in at bit 0.
53C9
PUSH AF F5
Save Register Pair AF (the bits read, with the new one) onto the stack, as 53DAH changes Register A.
53CA
GOSUB to 53DAH to send the port FFH copy to port FFH again, clearing the latch for the next clock pulse.
53CD
POP AF F1
Restore Register Pair AF (the bits read) from the stack.
53CE
EXX D9
Exchange Register Pairs BC, DE and HL with the alternate set again: the caller's registers are back.
53CF
RET C9
RETURN to the caller with Register A = the bits read, the newest in bit 0.

53D0H - Cassette Motor and Port FFH

Each entry loads Register Pair HL with a mask in Register H and bits to set in Register L for the ROM's copy of port FFH at 403DH: 53D0H clears bit 2 (motor off), 53D5H sets bit 2 (motor on), 53DAH changes nothing. 53DDH sends the result to port FFH, which also clears the cassette pulse latch, and stores it at 403DH.

53D0
LD HL,FB00H 21 00 FB
Load Register Pair HL with FB00H: Register H = FBH, the mask that clears bit 2, Register L = 00H, no bit to set. 531DH calls here.
53D3
JUMP to 53DDH to change the port FFH copy and send it: the cassette motor stops.
53D5
LD HL,FF04H 21 04 FF
Load Register Pair HL with FF04H: Register H = FFH, keep every bit, Register L = 04H, set bit 2. 52D0H calls here.
53D8
JUMP to 53DDH to change the port FFH copy and send it: the cassette motor starts.
53DA
LD HL,FF00H 21 00 FF
Load Register Pair HL with FF00H: Register H = FFH and Register L = 00H, no change. 53BBH and 53CAH call here.
53DD
LD A,(403DH) 3A 3D 40
Fetch the ROM's copy of the last value sent to port FFH from 403DH into Register A. 53D3H and 53D8H jump here.
53E0
AND H A4
AND Register A (the port copy) with Register H, the mask: FBH clears bit 2, the motor bit.
53E1
OR L B5
OR Register A (the port copy) with Register L: 04H sets bit 2, the motor bit.
53E2
OUT (FFH),A D3 FF
Send Register A to port FFH: the motor bit takes effect and the cassette pulse latch is cleared.
53E4
LD (403DH),A 32 3D 40
Store Register A (the value sent) into 403DH, the ROM's copy of port FFH.
53E7
BIT 2,A CB 57
Test bit 2 of Register A, the motor bit: the Z FLAG is set when the motor is off. No caller uses this flag.
53E9
RET C9
RETURN to the caller with Register A = the value sent to port FFH.

53EAH - The Message and the Data Areas

The one message and, past the end of the file, the transfer address of the F command and the sector buffer of the file being written.

53EA-53F1
DEFM "SN ERROR" 53 4E 20 45 52 52 4F 52
Shown by 52C4H through 536EH: eight characters, no ENTER; 5200H starts the next line.
53F2-53F3
DEFS 2
Not in the file: the transfer address of the F command, low byte at 53F2H (5265H) and high byte at 53F3H (525EH), read at 529DH and 52A3H.
53F4-54F3
DEFS 256
Not in the file: the sector buffer of the file being written (526DH, 5342H), filled with the load module and written by 4439H.