TRS-80 DOS - LDOS 5.3.1 for the Model I - TED/CMD Disassembled

Page Customization

Summary:

LDOS 5.3.1 TED/CMD Disassembly - the MISOSYS Text Editor (Model I)

TED is the full screen text editor supplied on the LDOS 5.3.1 distribution disk. It is not part of the operating system: it is an ordinary transient utility, and nothing else on the disk depends on it. The manual describes it as "a full screen quick text editor with typical word-processing type features" that "was not designed to be a full featured word processor", and the code bears that out. There is no word wrap, no formatting, no printer control and no undo. What there is, is a text buffer that fills all of free memory, four-directional cursor movement, insert and overstrike entry, five kinds of deletion, six block operations, search and replace, a directory query, and disk LOAD and FILE, all driven from single keystrokes.

The banner the program puts on the status line reads TED 1.2 - (c) 1991 MISOSYS, Inc, while the copyright string that precedes the code in the load module reads Copyright (c) 1987 MISOSYS, Inc., All rights reserved. The command is TED [filename[/TXT]]; a file named on the command line is loaded automatically, because initialisation copies the filespec into the prompt buffer and queues a CTRL L keystroke for the main loop to find. A command line beginning with an asterisk suppresses the clearing of the text buffer, so TED * re-enters the editor over whatever the previous run left in memory.

TED will not run without KI/DVR, the DOS v5 keyboard driver installed with SET *KI TO KI/DVR. The very first thing the transfer address does is test bit 4 of DFLAG$ at 441FH and abort with TED requires KI/DVR! if the driver is absent. The reason is the command key table: initialisation sets bit 6 of KFLAG$ at 4423H to select the driver's special-key translation table, and the whole command set is built around the codes that table returns. The bit is reset again at 530DH on the way out.

The screen is divided in three. Video rows 0 to 13, 3C00H to 3F7FH, are the text area, fourteen lines of 64 characters. Video row 14, 3F80H to 3FBFH, is a rule of underscores. Video row 15, 3FC0H to 3FFFH, is the status line, which carries the sign-on banner when nothing else is happening and every prompt, query and error message when something is. Alongside the display TED keeps a line table at 5B96H holding the text address of the first character of each of the fourteen text rows, rebuilt by every redraw, and it is that table rather than any calculation that turns a cursor row and column back into a text address.

The text buffer starts at 5D00H and runs to HIGH$, which TED reads once at start-up and never lowers, so it does not protect itself and the buffer is simply everything above the image. Text is stored as a plain run of characters ending in a zero byte, with 0DH for a newline. The two block markers the operator plants with CTRL B B and CTRL B E are stored in the text as ordinary characters, 0FEH and 0FFH, which is why the manual warns that they occupy text positions, can be deleted or overstruck, and will be written to the disk file if they are still there when the text is filed. The display translates all three: a newline is shown as the small graphics block 84H, a begin marker as the graphic left bracket 0B7H and an end marker as the graphic right bracket 0BBH.

Six of the module's instructions carry their working values in their own operand fields instead of in variables. The main loop alone holds the saved stack pointer at 540EH, the cursor row and column at 5422H, the pending keystroke at 5426H and the text address of the cursor at 5438H; the end-of-text pointer lives at 58C2H and the buffer ceiling at 58C5H. The end-of-text pointer is the interesting one, because the image loads with zero in that field and the value is first put there by the line display routine at 5828H, which records the address of the terminating zero byte every time a redraw reaches the end of the text.

There are ten places where a branch lands one byte inside another instruction so that an FDH index prefix, an 06H, an 01H, an 11H, a 3EH or an 0F6H is stepped over and the bytes that follow read as a different instruction. Three message setters are packed into eleven bytes that way at 5324H, the "this is a move" and "this is a delete" flags at 5361H are one instruction, the two block marker constants at 5393H are another, and both the CTRL L/CTRL F pair at 5642H and the CTRL N entry at 5638H are reached through the trick from the command key table itself. Every one of them is shown on this page twice, once as it reads on the fall-through path and once as it reads when it is entered.

Two fragments in the image are unreachable. The three bytes at 5508H form a LD (5423H),A that no branch names and that the unconditional jump above it cannot fall into, and the nineteen bytes at 5831H are a complete subroutine, converting a video address into a row and a column, that nothing calls; it loads its divisor into Register C while the resident @DIV routine at 44C4H takes the divisor in Register A.

File Format and Load Map

TED/CMD is a single LDOS load module. The header string Copyright (c) 1987 MISOSYS, Inc., All rights reserved precedes the code and is not executable. The image is one contiguous run of 2365 bytes from 5259H to 5B95H with no gaps and no byte loaded twice, and the transfer address is 529FH, not the first byte of the image; the two-byte jump at 5259H exists so that a caller entering at the front of the image reaches the same place. Every hex byte on this page has been verified against the module.

The program uses three areas of memory outside its own image. Below it, 5200H to 5258H is the workspace addressed through Register Pair IX, which is loaded with 5200H at 52C4H and never changed again. Above it, 5B96H to 5CCBH holds the line table, the prompt reply buffer and the buffer a DOS error message is returned into. Above that, 5D00H to HIGH$ is the text buffer.

The Command Key Table (525DH-529EH)

Twenty-two three-byte entries, each a key code followed by the address of its handler, scanned by the dispatcher at 5449H. The high-bit codes are what KI/DVR returns with its special-key translation table selected. On the Model I keyboard the DOS 5 control prefix is SHIFT with the down arrow, so the manual's CTRL Z is that prefix followed by Z.

Key CodeKeyHandlerAction
81HLeft arrow554FHMove the cursor one position left. In column 0 it wraps to the end of the previous line.
84HRight arrow553DHMove the cursor one position right. At a newline or at column 63 it wraps to the start of the next line.
88HDown arrow5529HMove the cursor one display line down, scrolling the screen up when it is on the bottom line.
82HUp arrow5553HMove the cursor one display line up, scrolling the screen down when it is on the top line.
91HSHIFT left arrow54CCHMove the cursor to column 0, or in DELETE mode delete back to the beginning of the line.
94HSHIFT right arrow54A9HMove the cursor to the end of the line, or in DELETE mode delete to the end of the line.
92HSHIFT up arrow53DCHMove the cursor to the top of the text, or in DELETE mode delete from the top down to the cursor.
1AHCTRL Z556DHMove the cursor to the end of the text, or in DELETE mode delete from the cursor to the bottom.
0DHENTER, CTRL M5467HEnter a 0DH newline into the text.
01HCTRL A55CEHToggle between overstrike and insert entry, changing the cursor character with it.
02HCTRL B5320HBLOCK. The next keystroke is read as one of the sub-commands B, E, C, D, M or P.
04HCTRL D55F3HDelete the character under the cursor and set DELETE mode for one further keystroke.
06HCTRL F5643HFILE the text buffer to disk.
07HCTRL G559FHGO on to the next occurrence of the current search string.
0CHCTRL L5642HLOAD a text file, appending it to the buffer.
0EHCTRL N5639HDisplay the next video page.
10HCTRL P53BCHPRINT the whole text buffer.
11HCTRL Q5715HQUERY a disk directory.
12HCTRL R592EHREPLACE the search string at the cursor with the replacement string.
13HCTRL S558DHSEARCH for a string.
15HCTRL U5626HGo up to the previous video page.
BDHCLEAR SHIFT =5313HEXIT the editor, confirming first if the buffer is not empty.

Variables, Buffers and Self-Modifying Operand Cells

Address RangePurpose
5200H-5203H
4 bytes
Part of the workspace addressed through Register Pair IX but never referenced by any instruction in the module.
5204H
1 byte
The mode flag byte, IX+04H. Bit 0 insert entry mode, bit 1 DELETE mode, bit 2 BLOCK mode, bit 6 a status message is queued at 5208H, bit 7 a status message is on the screen. Bits 1, 2, 6 and 7 are cleared by 58FFH on the keystroke after the one that set them.
5205H
1 byte
The current cursor character, IX+05H. 5FH, the underscore, in overstrike mode and 0BFH, the solid graphics block, in insert mode. Written by 55DCH from the pair of constants at 525BH.
5206H
1 byte
The length of the search string, IX+06H. Zero until a SEARCH has been answered, and the search loop at 55BBH returns at once while it is.
5207H
1 byte
The length of the replacement string, IX+07H.
5208H-5209H
2 bytes
The address of the status message queued by 532FH, IX+08H, read back by 58F6H when the main loop displays it.
520AH-5229H
32 bytes
The File Control Block, IX+0AH. The three fields the module reaches directly are 520DH and 520EH, FCB+03H and FCB+04H, the buffer address whose high byte is walked up the text buffer a page per record; 5212H, FCB+08H, the end-of-file byte offset; and 5216H, FCB+0CH, the ending record number.
522AH-5241H
24 bytes
The search string, copied here from the prompt reply buffer by 559DH.
5242H-5258H
23 bytes
The replacement string, copied here by 593EH. It runs up to the first byte of the loaded image.
540EH-540FH
2 bytes
The stack pointer as it stood when 5304H called the editor, saved by 53D8H. It is the operand of the LD SP,nnnn at 540DH, so the main loop resets the stack simply by executing that instruction.
5422H-5423H
2 bytes
The cursor position: 5422H the column 0 to 63 and 5423H the video row 0 to 13. The operand of the LD HL,nnnn at 5421H.
5426H
1 byte
The pending keystroke. The operand of the LD A,nn at 5425H. Initialisation puts 0CH there when a filespec was given on the command line, and 564BH reads it and 564CH clears it.
5438H-5439H
2 bytes
The text address of the character under the cursor. The operand of the LD DE,nnnn at 5437H, so every command routine is handed it in Register Pair DE.
5670H
1 byte
The LOAD or FILE selector, 00H for FILE and 0AFH for LOAD. The operand of the LD B,nn at 566FH.
58C2H-58C3H
2 bytes
The end-of-text pointer, the address of the terminating zero byte. The operand of the LD DE,nnnn at 58C1H. The image loads with 0000H here and the value is first established by the line display routine at 5828H, which records the address whenever a redraw reaches the end of the text.
58C5H-58C6H
2 bytes
The text buffer ceiling, HIGH$ as it stood when TED started, written once by 52D8H. The operand of the LD HL,nnnn at 58C4H, so 58C1H followed by 58C4H is the buffer-full test.
5B96H-5BB1H
28 bytes
The line table: fourteen two-byte text addresses, one for each video row 0 to 13, rebuilt by every redraw. A row that lies past the end of the text holds 0000H. 5B94H and 5B95H, the last two bytes of the image padding, act as the guard entry below row 0.
5BB4H-5BCBH
24 bytes
The filespec buffer and the reply buffer for every prompt. Initialisation copies 24 bytes of the command line here at 52FCH.
5BCCH-5CCBH
256 bytes
The buffer SYS4/SYS builds a DOS error message into when 56B1H calls @ERROR. 56BEH converts the carriage return that ends it into TED's own bit 7 terminator.
5D00H upwards
to HIGH$
The text buffer. A plain run of characters terminated by a zero byte, with 0DH for a newline, 0FEH for a block begin marker and 0FFH for a block end marker.

Major Routines

AddressName and PurposeEntryExit
529FHTransfer Address
Tests DFLAG$ 441FH bit 4 and refuses to run without the DOS v5 keyboard driver.
NoneAborts to @ABORT 4030H if KI/DVR is absent
52C4HInitialisation
Sets Register Pair IX to 5200H, selects KI/DVR's special-key translation table, saves HIGH$ as the buffer ceiling, draws the screen furniture and queues a LOAD if the command line names a file.
Register Pair HL = the command line tailFalls into the editor
540DHMain Command Loop
Resets the stack, refreshes the status line, blinks the cursor, reads a key and dispatches it as a block sub-command, a table command or a text character.
NoneNever returns; every command re-enters it
5449HCommand Key Lookup
Linear scan of the twenty-two entry table at 525DH.
Register A = the key codeJumps to the handler, or returns if the key is unknown
5469HText Entry
Overstrike or insert according to bit 0 of 5204H, growing the text and refusing with No room! at HIGH$.
Register A = the character, Register Pair DE = the cursor text addressCursor moved right, screen updated
55BBHString Search
Compares the string at 522AH at every position from one past the address given.
Register Pair HL = the text address to search fromZ FLAG set and Register Pair HL on the match
57B6HScreen Refresh
Fourteen calls to the line display routine, then blanks whatever is left of the text area.
Register Pair HL = the text address of the first row, Register A = the first row at 57B7HText area redrawn, line table rebuilt
580AHDisplay One Text Line
Up to 64 characters, stopping at a newline or the terminator, and the routine that establishes the end-of-text pointer at 5828H.
Register Pair HL = the text, Register Pair DE = the video addressRegister A zero at the end of the text
5852HStart of a Display Line
Counts back to the previous newline and strips whole multiples of 64.
Register Pair DE = any text addressRegister Pair DE = the first character of its display line
5892HText Address from Row and Column
Looks the row up in the line table at 5B96H and adds the column.
Register H = the row, Register L = the columnRegister Pair DE = the text address
58CDHConfirmation Prompt
Displays Press <ENTER> to confirm.
NoneReturns only on ENTER, otherwise discards the caller's return address
58E8HPrompt and Reply
Writes the prompt on the status line and reads an edited reply.
Register Pair HL = the messageRegister Pair HL = 5BB4H, Register B = the reply length
590AHWrite the Status Line
Homes the cursor to video row 15, erases the line and displays until a character with bit 7 set.
Register Pair HL = the messageRegister C = the message terminator
598CHInsert Text at the Cursor
Opens the gap with a backwards block move and copies the source down into it. Used by block copy, block move and replace.
Register Pair HL = the last source byte, Register Pair DE = one below the firstText inserted, or No room!
59CEHFind the Marked Block
Searches forward for 0FFH and then backwards for 0FEH.
Register Pair DE = where to search fromRegister Pair HL = the begin marker, Register Pair DE = the end marker
5A0DHPrompt Line Input
Echoes and edits a reply, accepting ENTER, BREAK, the left arrow and CTRL X.
Register Pair HL = the bufferRegister B = the length, CARRY FLAG set if abandoned
5A71HCursor Primitives
Read the cell, write the cell, set the hardware cursor, or return the current row and column.
Register B = 1 to 4, Register Pair HL = the row and columnDepends on the function code
5ABFHBREAK Key Test
Clears KFLAG$ 4423H bit 0 and looks again, so holding the key holds the program.
NoneNZ FLAG set if BREAK was pressed

Cross-Reference Notes

What TED calls
Twelve resident LDOS vectors and seven Level II ROM routines. The DOS vectors are @ABORT 4030H when KI/DVR is missing, @ERROR 4409H to turn a disk error code into text, @FSPEC 441CH to parse a filespec into the File Control Block, @INIT 4420H to create the file a FILE writes, @OPEN 4424H to open the file a LOAD reads, @CLOSE 4428H, @READ 4436H, @WRITE 4439H, @DODIR 4463H for the directory query, @FEXT 4473H to apply the default /TXT extension, and @LOGOT 447BH for the one message TED displays before the screen belongs to it. @DIV 44C4H is named only by the unreachable routine at 5831H. The ROM entries are $CTL 0023H, $KBD 002BH, $DSP 0033H, $PRT 003BH, $KEY 0049H, PAUSE 0060H and CLS 01C9H.

Resident bytes TED reads or writes directly
DFLAG$ 441FH bit 4, tested once to prove KI/DVR is installed; KFLAG$ 4423H bit 6, set at 52CCH to select the driver's special-key translation table and reset at 530DH, and bit 0, the BREAK flag, cleared and re-tested by 5ABFH; HIGH$ 4049H, read once at 52D5H to fix the top of the text buffer; the SYS1 status byte 4758H, whose bit 7 is set at 56AFH so that SYS4/SYS builds an error message without displaying it; the *KI Device Control Block at 4015H, handed to the ROM $CTL entry at 5ADAH; and 4020H, the ROM display driver's cursor position cell, which is the only place TED keeps the hardware cursor.

Which overlays a TED session loads
TED issues no RST 28H request of its own; every service it uses is a resident vector, so the overlays are reached indirectly. LOAD and FILE bring in SYS2/SYS through @OPEN and @INIT and SYS3/SYS through @CLOSE, a FILE that extends a file will bring in SYS8/SYS through the resident allocate path, the directory query brings in SYS12/SYS through @DODIR, and any disk error brings in SYS4/SYS through @ERROR. Nothing else on the LDOS 5.3.1 disk calls TED, and TED is the only file on the distribution disk that is an application rather than part of the operating system.

Disassembly:

5259H - Image Head, Cursor Character Pair and the Command Key Table

The first three bytes of the loaded image are not the transfer address. TED/CMD declares its transfer address as 529FH, so 5259H is entered only if something jumps to the front of the image, and the two-byte jump at 5259H sends that caller to the same place. 525BH and 525CH hold the two cursor characters, and 525DH-529EH is the command key table that decides what every non-text keystroke does.

The table is twenty-two three-byte entries: one key code followed by the address of the routine that services it. The dispatcher at 5449H walks it with Register B set to 16H, which is exactly twenty-two, so the table ends at the byte before the transfer address. The high-bit key codes are the values the KI/DVR keyboard driver returns when its special-key translation table is selected, which is what initialisation switches on at 52CCH by setting bit 6 of KFLAG$ 4423H. TED refuses to run without that driver, which is why the codes can be relied on.

5259
Jump forward to the transfer address at 529FH. The load module names 529FH as its entry point, so this two-byte jump only matters to a caller that enters the image at its first byte.
525B
DEFB 5FH 5F
The overstrike cursor character, ASCII 5FH, the underscore. 55CEH copies whichever of these two bytes matches the new mode into the current cursor character cell at 5205H every time CTRL A toggles the text entry mode.
525C
DEFB 0BFH BF
The insert cursor character, 0BFH, the solid graphics block. Selecting it is how the operator can see at a glance that typing will push the text apart rather than write over it.
525D
DEFB 81H 81
Key code 81H, which is what the LEFT arrow produces. The dispatcher at 544FH compares the key just read against this byte.
525E
DEFW 554FH 4F 55
The handler address for that key. 5461H loads this word into Register Pair HL and reaches the routine at 554FH, which will move the cursor one position left, wrapping to the end of the previous line when it is already in column 0.
5260
DEFB 84H 84
Key code 84H, which is what the RIGHT arrow produces. The dispatcher at 544FH compares the key just read against this byte.
5261
DEFW 553DH 3D 55
The handler address for that key. 5461H loads this word into Register Pair HL and reaches the routine at 553DH, which will move the cursor one position right, wrapping to the start of the next line at column 64 or at a newline.
5263
DEFB 88H 88
Key code 88H, which is what the DOWN arrow produces. The dispatcher at 544FH compares the key just read against this byte.
5264
DEFW 5529H 29 55
The handler address for that key. 5461H loads this word into Register Pair HL and reaches the routine at 5529H, which will move the cursor one display line down, scrolling the screen up by one line when it is already on the bottom text line.
5266
DEFB 82H 82
Key code 82H, which is what the UP arrow produces. The dispatcher at 544FH compares the key just read against this byte.
5267
DEFW 5553H 53 55
The handler address for that key. 5461H loads this word into Register Pair HL and reaches the routine at 5553H, which will move the cursor one display line up, scrolling the screen down by one line when it is already on the top text line.
5269
DEFB 0DH 0D
Key code 0DH, which is what ENTER, which the manual also calls CTRL M produces. The dispatcher at 544FH compares the key just read against this byte.
526A
DEFW 5467H 67 54
The handler address for that key. 5461H loads this word into Register Pair HL and reaches the routine at 5467H, which will enter a 0DH newline into the text at the cursor.
526C
DEFB 91H 91
Key code 91H, which is what SHIFT and the LEFT arrow produces. The dispatcher at 544FH compares the key just read against this byte.
526D
DEFW 54CCH CC 54
The handler address for that key. 5461H loads this word into Register Pair HL and reaches the routine at 54CCH, which will move the cursor to the first position of the current line, or, in DELETE mode, delete back to the beginning of the line.
526F
DEFB 94H 94
Key code 94H, which is what SHIFT and the RIGHT arrow produces. The dispatcher at 544FH compares the key just read against this byte.
5270
DEFW 54A9H A9 54
The handler address for that key. 5461H loads this word into Register Pair HL and reaches the routine at 54A9H, which will move the cursor to the last position of the current line, or, in DELETE mode, delete forward to the end of the line.
5272
DEFB 1AH 1A
Key code 1AH, which is what CTRL Z, which on the Model I keyboard is SHIFT and the down arrow followed by Z produces. The dispatcher at 544FH compares the key just read against this byte.
5273
DEFW 556DH 6D 55
The handler address for that key. 5461H loads this word into Register Pair HL and reaches the routine at 556DH, which will move the cursor to the end of the text, or, in DELETE mode, delete from the cursor to the bottom of the text.
5275
DEFB 92H 92
Key code 92H, which is what SHIFT and the UP arrow produces. The dispatcher at 544FH compares the key just read against this byte.
5276
DEFW 53DCH DC 53
The handler address for that key. 5461H loads this word into Register Pair HL and reaches the routine at 53DCH, which will move the cursor to the beginning of the text, or, in DELETE mode, delete from the top of the text back to the cursor.
5278
DEFB 01H 01
Key code 01H, which is what CTRL A produces. The dispatcher at 544FH compares the key just read against this byte.
5279
DEFW 55CEH CE 55
The handler address for that key. 5461H loads this word into Register Pair HL and reaches the routine at 55CEH, which will toggle between overstrike and insert text entry.
527B
DEFB 02H 02
Key code 02H, which is what CTRL B produces. The dispatcher at 544FH compares the key just read against this byte.
527C
DEFW 5320H 20 53
The handler address for that key. 5461H loads this word into Register Pair HL and reaches the routine at 5320H, which will start a BLOCK command and wait for its one letter sub-command.
527E
DEFB 04H 04
Key code 04H, which is what CTRL D produces. The dispatcher at 544FH compares the key just read against this byte.
527F
DEFW 55F3H F3 55
The handler address for that key. 5461H loads this word into Register Pair HL and reaches the routine at 55F3H, which will delete the character under the cursor and enter DELETE mode for one further keystroke.
5281
DEFB 06H 06
Key code 06H, which is what CTRL F produces. The dispatcher at 544FH compares the key just read against this byte.
5282
DEFW 5643H 43 56
The handler address for that key. 5461H loads this word into Register Pair HL and reaches the routine at 5643H, which will FILE the text buffer to disk.
5284
DEFB 07H 07
Key code 07H, which is what CTRL G produces. The dispatcher at 544FH compares the key just read against this byte.
5285
DEFW 559FH 9F 55
The handler address for that key. 5461H loads this word into Register Pair HL and reaches the routine at 559FH, which will GO on and find the next occurrence of the current search string.
5287
DEFB 0CH 0C
Key code 0CH, which is what CTRL L produces. The dispatcher at 544FH compares the key just read against this byte.
5288
DEFW 5642H 42 56
The handler address for that key. 5461H loads this word into Register Pair HL and reaches the routine at 5642H, which will LOAD a text file, appending it to whatever is already in the buffer.
528A
DEFB 0EH 0E
Key code 0EH, which is what CTRL N produces. The dispatcher at 544FH compares the key just read against this byte.
528B
DEFW 5639H 39 56
The handler address for that key. 5461H loads this word into Register Pair HL and reaches the routine at 5639H, which will display the NEXT video page.
528D
DEFB 10H 10
Key code 10H, which is what CTRL P produces. The dispatcher at 544FH compares the key just read against this byte.
528E
DEFW 53BCH BC 53
The handler address for that key. 5461H loads this word into Register Pair HL and reaches the routine at 53BCH, which will PRINT the whole text buffer.
5290
DEFB 11H 11
Key code 11H, which is what CTRL Q produces. The dispatcher at 544FH compares the key just read against this byte.
5291
DEFW 5715H 15 57
The handler address for that key. 5461H loads this word into Register Pair HL and reaches the routine at 5715H, which will QUERY a disk directory.
5293
DEFB 12H 12
Key code 12H, which is what CTRL R produces. The dispatcher at 544FH compares the key just read against this byte.
5294
DEFW 592EH 2E 59
The handler address for that key. 5461H loads this word into Register Pair HL and reaches the routine at 592EH, which will REPLACE the search string at the cursor with a new string.
5296
DEFB 13H 13
Key code 13H, which is what CTRL S produces. The dispatcher at 544FH compares the key just read against this byte.
5297
DEFW 558DH 8D 55
The handler address for that key. 5461H loads this word into Register Pair HL and reaches the routine at 558DH, which will SEARCH for a string.
5299
DEFB 15H 15
Key code 15H, which is what CTRL U produces. The dispatcher at 544FH compares the key just read against this byte.
529A
DEFW 5626H 26 56
The handler address for that key. 5461H loads this word into Register Pair HL and reaches the routine at 5626H, which will go UP to the previous video page.
529C
DEFB BDH BD
Key code BDH, which is what CLEAR with SHIFT and = produces. The dispatcher at 544FH compares the key just read against this byte.
529D
DEFW 5313H 13 53
The handler address for that key. 5461H loads this word into Register Pair HL and reaches the routine at 5313H, which will EXIT the editor.

529FH - Transfer Address: the KI/DVR Requirement Test

The transfer address of the load module. TED reads its keyboard through the DOS v5 keyboard driver KI/DVR rather than through the ROM, because only that driver produces the shifted-arrow and control-letter codes the command key table at 525DH is built around. The first thing the program does is prove the driver is installed, and it abandons the run if it is not.

529F
LD A,(441FH) 3A 1F 44
Load Register A with DFLAG$, the resident DOS flag byte at 441FH. Bit 4 of that byte is set by KI/DVR when the driver installs itself over the *KI device.
52A2
BIT 4,A CB 67
Test bit 4 of Register A, the KI/DVR-is-active flag just fetched from DFLAG$ 441FH. If the driver is installed the Z FLAG is cleared; if it is absent the Z FLAG is set.
52A4
If the NZ FLAG (Not Zero) has been set, meaning DFLAG$ 441FH bit 4 is on and KI/DVR is installed, JUMP to 52C4H to initialise the editor.
52A6
LD HL,52AFH 21 AF 52
Point Register Pair HL at the message text at 52AFH, TED requires KI/DVR! followed by the carriage return that @LOGOT expects.
52A9
GOSUB to the resident @LOGOT vector at 447BH, which displays the carriage-return-terminated string addressed by Register Pair HL on the video screen.
52AC
JUMP to the resident @ABORT vector at 4030H. TED never returns to the caller on this path; the DOS takes the run down as an aborted program.
52AF
DEFM 'TED requ' 54 45 44 20 72 65 71 75
The first eight characters of the refusal message, TED requires KI/DVR!.
52B7
DEFM 'ires KI/' 69 72 65 73 20 4B 49 2F
The middle eight characters of the refusal message.
52BF
DEFM 'DVR!' 44 56 52 21
The last four characters of the refusal message.
52C3
DEFB 0DH 0D
The carriage return that ends the string. @LOGOT at 447BH stops on this byte.

52C4H - Initialisation and the Return to the DOS

Everything TED needs set up before the editor runs: the index register that addresses its low workspace, the keyboard translation table, the text buffer ceiling, the screen furniture, the initial text entry mode, and the optional filespec taken from the command line. The tail of this section, from 5307H, is where the editor returns when the operator exits, and it puts the keyboard back the way it found it.

Register Pair IX is loaded with 5200H and is never changed again, so every (IX+nn) in the file addresses TED's workspace in the 89 bytes below the image: 5204H the mode flag byte, 5205H the current cursor character, 5206H the search string length, 5207H the replace string length, 5208H the pending status message pointer, 520AH-5229H the File Control Block, 522AH the search string and 5242H the replace string.

52C4
LD IX,5200H DD 21 00 52
Point Register Pair IX at 5200H, the base of the workspace TED keeps below its own image. Every indexed reference in the rest of the file is relative to this one value, which is never reloaded.
52C8
PUSH HL E5
Save Register Pair HL onto the stack. The DOS passes the address of the command line tail in Register Pair HL, and the filespec test at 52DFH needs it back.
52C9
LD HL,4423H 21 23 44
Point Register Pair HL at KFLAG$, the resident keyboard flag byte at 4423H.
52CC
SET 6,(HL) CB F6
Set bit 6 of KFLAG$ 4423H, which selects KI/DVR's special-key translation table. That is what makes the arrow keys and the shifted arrow keys return the 81H, 82H, 84H, 88H, 91H, 92H and 94H codes the command key table at 525DH is built around.
52CE
LD HL,0000H 21 00 00
Set Register Pair HL to ZERO, ready to clear the two string length cells with one store.
52D1
LD (5206H),HL 22 06 52
Store zero into 5206H and 5207H, the search string length and the replace string length. With both zero the search loop at 55BBH returns at once and no search or replace can fire until the operator has typed a string.
52D4
LD B,L 45
Load Register B with Register L, which is zero. Register B is the byte written into the text buffer at 52E8H to make it empty.
52D5
LD HL,(4049H) 2A 49 40
Load Register Pair HL with HIGH$, the top-of-memory pointer the DOS keeps at 4049H. That is the highest address TED may use for text.
52D8
LD (58C5H),HL 22 C5 58
Store the top of memory into 58C5H. That cell is the operand of the LD HL,nnnn instruction at 58C4H, so from here on executing 58C4H loads Register Pair HL with the text buffer ceiling. Every insert tests against it before it makes the text longer.
52DB
GOSUB to 5AADH, which fills video line 14 with underscores to rule the text area off from the status line and blanks video line 15, the status line itself.
52DE
POP HL E1
Restore Register Pair HL from the stack, the command line pointer the DOS supplied.
52DF
LD A,(HL) 7E
Fetch the first character of the command line tail into Register A.
52E0
EX DE,HL EB
Exchange Register Pair DE with Register Pair HL, parking the command line pointer in Register Pair DE.
52E1
LD HL,5D00H 21 00 5D
Point Register Pair HL at 5D00H, the first byte of the text buffer.
52E4
CP 2AH FE 2A
Compare Register A, the first character of the command line, against 2AH, ASCII *. If it is an asterisk the Z FLAG is set.
52E6
If the Z FLAG (Zero) has been set, meaning the command was TED *, JUMP to 52E9H and leave the text buffer exactly as the previous run left it.
52E8
LD (HL),B 70
Store Register B, which is zero, at 5D00H. A zero byte at the front of the buffer is what the display routine reads as an empty text, so this is what clears the buffer on a normal start.
52E9
LD (IX+04H),81H DD 36 04 81
Store 81H into the mode flag byte at 5204H. Bit 0 is the insert flag and bit 7 says a status message is on the screen. Bit 0 is set here only so that the toggle in the next instruction turns it off and picks up the overstrike cursor character with it.
52ED
GOSUB to 55CEH, the CTRL A mode toggle, which flips bit 0 of the flag byte at 5204H to zero, selects overstrike, and copies the underscore from 525BH into the current cursor character cell at 5205H.
52F0
EX DE,HL EB
Exchange Register Pair DE with Register Pair HL, bringing the command line pointer back into Register Pair HL.
52F1
LD A,(HL) 7E
Fetch the first character of the command line into Register A again.
52F2
CP 41H FE 41
Compare Register A against 41H, ASCII A. If the character is below A the CARRY FLAG is set, which covers an empty command line, a blank, and the asterisk tested at 52E4H.
52F4
If the CARRY FLAG has been set, meaning nothing that could be a file name follows the command, JUMP to 5304H and start the editor with an empty screen.
52F6
LD DE,5BB4H 11 B4 5B
Point Register Pair DE at 5BB4H, the 24-byte buffer TED uses both for the filespec and for every prompt reply.
52F9
LD BC,0018H 01 18 00
Load Register Pair BC with 18H, 24 bytes, the size of that buffer.
52FC
LDIR ED B0
Block move 24 bytes from the command line addressed by Register Pair HL into the filespec buffer at 5BB4H, incrementing both pointers. The whole fixed length is copied whether the filespec is that long or not; the filespec parser stops at the first delimiter.
52FE
LD A,(5287H) 3A 87 52
Load Register A from 5287H. That byte is inside the command key table and holds 0CH, the key code of CTRL L, the LOAD command. Reading the constant out of the table rather than coding it here keeps the two in step.

Self-Modifying Code
5426H is the immediate operand of the instruction at 5425H, not an ordinary variable. Writing 0CH into it makes 5425H execute as LD A,0CH once; 564BH reads it and 564CH puts it back to zero, so the queued command fires exactly once.

5301
LD (5426H),A 32 26 54
Store that 0CH into 5426H, the operand of the LD A,nn instruction at 5425H in the main loop. The main loop treats a non-zero value there as a keystroke that has already been typed, so the editor starts by loading the file named on the command line.
5304
GOSUB to 53D8H, the editor. Everything the operator does happens inside this one call, which returns only when the EXIT command is confirmed.
5307
GOSUB to the Level II ROM CLS routine at 01C9H to clear the video screen on the way out.
530A
LD HL,4423H 21 23 44
Point Register Pair HL at KFLAG$ 4423H again.
530D
RES 6,(HL) CB B6
Reset bit 6 of KFLAG$ 4423H, putting KI/DVR back on its normal key translation table so that the DOS and the next program see the ordinary key codes.
530F
LD HL,0000H 21 00 00
Set Register Pair HL to ZERO. LDOS takes the contents of Register Pair HL on return from a transient as the error code, and zero means no error.
5312
RET C9
RETurn to the DOS.

5313H - The EXIT Command

Reached from the command key table when the operator presses CLEAR with SHIFT and =, key code BDH. An empty buffer leaves without comment; anything else has to be confirmed, because exiting is what throws the text away.

5313
LD A,(5D00H) 3A 00 5D
Load Register A with the first byte of the text buffer at 5D00H. A zero there means the buffer is empty and there is nothing to lose.
5316
OR A B7
Test Register A, that first text byte, against zero. An empty buffer sets the Z FLAG.
5317
LD HL,5B05H 21 05 5B
Point Register Pair HL at the message at 5B05H, Press <ENTER> to EXIT.
531A
If the NZ FLAG (Not Zero) has been set, meaning there is text in the buffer, GOSUB to 58DCH, which puts that message on the status line and waits for a key. It returns with the Z FLAG set only if the key was ENTER. An empty buffer skips the call and arrives at the next instruction with the Z FLAG already set from 5316H.
531D
RET NZ C0
If the NZ FLAG (Not Zero) has been set, meaning the operator pressed something other than ENTER, RETurn. The return address on the stack is 540DH, which the dispatcher pushed, so this drops straight back into the main command loop and the editor carries on.
531E
POP HL E1
Discard the 540DH main-loop return address the dispatcher pushed at 5432H into Register Pair HL. That leaves the return address that 5304H pushed on top of the stack.
531F
RET C9
RETurn, which now lands at 5307H and shuts the editor down.

5320H - The BLOCK Command and the Three Status Message Setters

CTRL B does not act on the text. It sets the BLOCK flag, puts the word Block on the status line, and returns to the main loop; the next keystroke is then routed to the block sub-command dispatcher at 537AH instead of being treated as text.

The three instructions at 5324H, 5327H and 532BH are a deliberate overlap. Entering at 5324H loads Register Pair HL with the address of Block and then executes two harmless LD IY,nnnn instructions on the way to 532FH. Entering one byte later, at 5328H or 532CH, strips the FDH prefix so the same bytes read as LD HL,nnnn and select a different message. That is how three message setters are packed into eleven bytes, and it is why 5328H and 532CH are branch targets that sit inside another instruction.

5320
SET 2,(IX+04H) DD CB 04 D6
Set bit 2 of the mode flag byte at 5204H, the BLOCK flag. The main loop tests it at 543AH and sends the next keystroke to the block sub-command dispatcher at 537AH.
5324
LD HL,5B27H 21 27 5B
Point Register Pair HL at the message at 5B27H, Block.
5327
LD IY,5B3BH FD 21 3B 5B
Point Register Pair IY at 5B3BH. On the fall-through path from 5324H this instruction does nothing that matters: Register Pair IY is reloaded before it is next used and Register Pair HL, which carries the message address, is untouched.
5328
LD HL,5B3BH 21 3B 5B
Point Register Pair HL at the message at 5B3BH, Cursor!. This is the same three bytes as the operand and tail of the instruction above, entered one byte later so that the FDH index prefix is skipped. 535BH and 5988H branch here when the cursor is on the wrong side of a marked block.
532B
LD IY,5B4AH FD 21 4A 5B
Point Register Pair IY at 5B4AH, again a harmless instruction on the fall-through path that exists only so that its operand can be entered as a LD HL,nnnn.
532C
LD HL,5B4AH 21 4A 5B
Point Register Pair HL at the message at 5B4AH, Can't. 55A5H and 5947H branch here when a search or a replace finds no match.
532F
LD (5208H),HL 22 08 52
Store the message address in Register Pair HL into 5208H, the pending status message pointer. 58F6H reads it back when the main loop comes round to display the message.
5332
SET 6,(IX+04H) DD CB 04 F6
Set bit 6 of the mode flag byte at 5204H, which tells the main loop at 541AH that a message is waiting to be displayed.
5336
RES 7,(IX+04H) DD CB 04 BE
Reset bit 7 of the mode flag byte at 5204H, the message-is-on-screen flag. Clearing it stops the main loop wiping the new message before it has been shown; the display routine at 58F9H sets it again.
533A
INC H 24
INCrement Register H by 1. Register H is the high byte of the message address, always 5BH, so this exists only to leave the NZ FLAG set. Callers such as 5981H and 59CEH use that as their error return.
533B
RET C9
RETurn to the caller, or, when this was reached from the CTRL B handler, to the main loop at 540DH.

533CH - Block Move

The M sub-command of BLOCK. A move is a copy followed by a delete: 5981H does the whole of the copy, including the room test and the error checks, and this routine then finds the original block a second time and removes it through the block delete code at 5363H. The cursor has to be adjusted first, because deleting the original pulls everything after it down.

533C
GOSUB to 5981H, which finds the first marked block in the text, refuses if the cursor is inside it, and copies the marked text into the buffer at the cursor. It returns with the NZ FLAG set if it could not do the copy.
533F
RET NZ C0
If the NZ FLAG (Not Zero) has been set, meaning no properly marked block was found or there was no room for the copy, RETurn to the main loop; the message is already queued.
5340
GOSUB to 59CEH, which searches the text from 5D00H for the block end marker 0FFH and then back for the block begin marker 0FEH. It returns Register Pair HL addressing the begin marker and Register Pair DE the end marker. The block is found again rather than remembered because the copy just made will have moved it.
5343
PUSH HL E5
Save Register Pair HL, the address of the begin marker, onto the stack.
5344
GOSUB to 5A07H, which subtracts Register Pair DE, the end marker address, from Register Pair HL, the begin marker address, and copies the result into Register Pair BC. Because the begin marker is the lower address, Register Pair BC ends up holding the negative of the block length.
5347
LD HL,(5438H) 2A 38 54
Load Register Pair HL with the text address of the character under the cursor, kept at 5438H.
534A
GOSUB to 58C7H, which compares Register Pair HL, the cursor address, against Register Pair DE, the end marker address. The CARRY FLAG is set if the cursor lies below the block.
534D
If the CARRY FLAG has been set, meaning the cursor is above the block in the text and will not be disturbed when the block is deleted, JUMP to 5351H and use it unchanged.
534F
ADD HL,BC 09
ADD Register Pair BC, the negative block length, to Register Pair HL, the cursor address. The cursor is below the block, so removing the block moves it down by the length of the block.
5350
DEC HL 2B
DECrement Register Pair HL by 1 to account for the two marker bytes and the inclusive count, leaving the address the cursor will occupy once the original block has gone.
5351
EX (SP),HL E3
Exchange the top of the stack with Register Pair HL. The adjusted cursor address is left on the stack for 5377H and Register Pair HL gets back the begin marker address.
5352
JUMP to 5362H, which clears Register A and so sets the Z FLAG. The shared code at 5363H reads that flag as "this is a move, not a delete".

5354H - Block Delete

The D sub-command of BLOCK, and the common tail that Block Move also uses to take the original block out. Unlike copy and move, delete requires the cursor to be inside the marked block, which is how the operator says which of several marked blocks is meant, and it always asks for confirmation first.

5354
GOSUB to 59D1H, the second entry of the marked-block finder. That entry searches forward from the cursor rather than from the start of the text, so the end marker it finds is the one belonging to the block the cursor is in. It returns Register Pair HL addressing the begin marker and Register Pair DE the end marker.
5357
RET NZ C0
If the NZ FLAG (Not Zero) has been set, meaning no properly marked block follows the cursor, RETurn; 59EFH has already queued Marker!.
5358
GOSUB to 59F5H, which tests the cursor address at 5438H against the two marker addresses. It sets the CARRY FLAG when the cursor is outside the block.
535B
If the CARRY FLAG has been set, meaning the cursor is not inside the marked block, JUMP to 5328H to queue Cursor! and give up.
535D
GOSUB to 58CDH, which displays Press <ENTER> to confirm and waits for a key. If the key is anything but ENTER that routine discards this routine's return address and abandons the delete, so reaching the next instruction means the operator agreed.
5360
EX DE,HL EB
Exchange Register Pair DE with Register Pair HL, so that Register Pair HL holds the begin marker address and Register Pair DE the end marker address.
5361
OR 0AFH F6 AF
OR Register A with 0AFH. The result can never be zero, so the NZ FLAG is set, and that is how the shared code below knows it is doing a delete rather than the delete half of a move.
5362
XOR A AF
Set Register A to ZERO and clear all flags. Block Move jumps here from 5352H, entering the middle of the instruction above so that the 0AFH operand executes as an XOR A. The Z FLAG that leaves is the "this is a move" marker.
5363
PUSH AF F5
Save Register A and the flags onto the stack. The flag that matters is the Z FLAG set at 5362H or cleared at 5361H.
5364
PUSH HL E5
Save Register Pair HL, the begin marker address, onto the stack.
5365
GOSUB to 5A04H, which loads Register Pair HL with the end-of-text pointer from 58C2H, subtracts Register Pair DE, the end marker address, and leaves the difference in Register Pair BC. That is the number of bytes of text that follow the block.
5368
POP HL E1
Restore Register Pair HL from the stack, the begin marker address.
5369
INC DE 13
INCrement Register Pair DE by 1 so that it addresses the first byte after the end marker.
536A
EX DE,HL EB
Exchange Register Pair DE with Register Pair HL. Register Pair HL now addresses the first byte after the block and Register Pair DE the begin marker, which are the source and destination of the move that closes the gap.
536B
PUSH DE D5
Save Register Pair DE, the begin marker address, onto the stack. It becomes the new cursor position.
536C
LDIR ED B0
Block move the Register Pair BC bytes that follow the block down over the block itself, incrementing both pointers. The block, both its markers included, is now gone from the text.
536E
DEC DE 1B
DECrement Register Pair DE by 1. After the block move Register Pair DE addresses one past the last byte written, so this leaves it on the terminating zero byte.
536F
LD (58C2H),DE ED 53 C2 58
Store Register Pair DE into 58C2H, the end-of-text pointer, which is also the operand of the LD DE,nnnn instruction at 58C1H.
5373
POP HL E1
Restore Register Pair HL from the stack, the begin marker address, which is where the cursor belongs after a delete.
5374
POP AF F1
Restore Register A and the flags from the stack, recovering the delete-or-move flag pushed at 5363H.
5375
If the NZ FLAG (Not Zero) has been set, meaning this was a plain block delete, JUMP to 53AEH and put the cursor where the block used to start.
5377
POP HL E1
Restore Register Pair HL from the stack. This is a move, so the cursor address wanted is the adjusted one that 5351H left there, which points at the block in its new home.
5378
JUMP to 53AEH to bring the cursor line to the top of the screen and redraw.

537AH - The Block Sub-Command Dispatcher

Reached from the main loop at 543EH when the BLOCK flag, bit 2 of 5204H, is set, so this is the keystroke immediately after CTRL B. The six sub-commands are ordinary letters, not control codes, and either case is accepted. Anything else is ignored and the flag byte is cleared by the main loop on the next pass.

537A
GOSUB to 59C5H, which folds a lower case letter in Register A to upper case and leaves anything else alone, so that b and B mean the same thing.
537D
CP 50H FE 50
Compare Register A, the sub-command letter, against 50H, ASCII P.
537F
If the Z FLAG (Zero) has been set, meaning the operator typed P, JUMP to 53B1H to print the marked block.
5381
CP 4DH FE 4D
Compare Register A against 4DH, ASCII M.
5383
If the Z FLAG (Zero) has been set, meaning M, JUMP to 533CH to move the marked block to the cursor.
5385
SUB 42H D6 42
SUBtract 42H, ASCII B, from Register A. The four remaining sub-commands are the consecutive letters B, C, D and E, so from here they can be picked off by counting down.
5387
If the Z FLAG (Zero) has been set, meaning B, JUMP to 5394H to insert a block begin marker at the cursor.
5389
DEC A 3D
DECrement Register A by 1, testing for the next letter along.
538A
If the Z FLAG (Zero) has been set, meaning C, JUMP to 53A7H to copy the marked block to the cursor.
538C
DEC A 3D
DECrement Register A by 1 again.
538D
If the Z FLAG (Zero) has been set, meaning D, JUMP to 5354H to delete the marked block the cursor is sitting in.
538F
DEC A 3D
DECrement Register A by 1 a third time.
5390
RET NZ C0
If the NZ FLAG (Not Zero) has been set, meaning the key was not one of the six sub-commands, RETurn to the main loop and ignore it.
5391
LD A,0FFH 3E FF
Load Register A with 0FFH, the block end marker byte. That is the character the sub-command E inserts into the text; the display routine at 578BH shows it as the graphic right bracket 0BBH.
5393
LD BC,0FE3EH 01 3E FE
Load Register Pair BC with 0FE3EH. Only the 01H opcode byte is executed on this path, purely so that the two bytes of its operand can be entered from 5387H as the instruction below.
5394
LD A,0FEH 3E FE
Load Register A with 0FEH, the block begin marker byte, shown on the screen as the graphic left bracket 0B7H. The dispatcher jumps here for sub-command B, one byte into the instruction above.
5396
LD B,(IX+04H) DD 46 04
Load Register B with the current mode flag byte from 5204H so that it can be put back after the marker has been inserted.
5399
PUSH BC C5
Save Register Pair BC onto the stack. Register B holds the saved flag byte and Register C is not used here.
539A
SET 0,(IX+04H) DD CB 04 C6
Set bit 0 of the mode flag byte at 5204H, forcing insert mode. A marker must be pushed into the text rather than written over the character the operator has the cursor on, whatever entry mode is actually in force.
539E
GOSUB to 5469H, the text entry routine, which inserts the marker byte in Register A into the text at the cursor and redraws the affected part of the screen.
53A1
POP AF F1
Restore the saved flag byte from the stack into Register A.
53A2
SET 7,A CB FF
Set bit 7 of Register A, the message-is-on-screen flag, so that the main loop at 5413H wipes the word Block off the status line on its next pass.
53A4
JUMP to 55DFH, which stores Register A back into the mode flag byte at 5204H and returns. The insert bit forced on at 539AH is discarded with it, so the operator's own entry mode survives.

53A7H - Block Copy

The C sub-command of BLOCK. All the work is in 5981H; what is left here is to put the cursor back on the screen. The markers themselves are not copied and the original block is left where it was.

53A7
GOSUB to 5981H, which finds the first marked block in the text, refuses if the cursor is inside it or if there is no room, and copies the marked characters into the text at the cursor.
53AA
RET NZ C0
If the NZ FLAG (Not Zero) has been set, meaning the copy did not happen, RETurn to the main loop with the error message already queued. This instruction is also the entry point 5972H uses at the end of a REPLACE.
53AB
LD HL,(5438H) 2A 38 54
Load Register Pair HL with the text address of the cursor from 5438H, which after a copy is the first character of the inserted text.
53AE
JUMP to 55A8H, which brings the display line containing that address to the top of the screen, redraws, and leaves the cursor on it.

53B1H - Block Print and the PRINT Command

Two entry points into one output loop. 53B1H is the P sub-command of BLOCK and prints only the marked text, without its markers; 53BCH is CTRL P and prints the whole buffer. Both send raw text bytes to the ROM printer driver, so the newlines in the text are the only line structure the printer sees, and BREAK stops the listing.

53B1
GOSUB to 59CEH, which finds the first marked block in the text and returns Register Pair HL addressing the begin marker and Register Pair DE the end marker.
53B4
RET NZ C0
If the NZ FLAG (Not Zero) has been set, meaning there is no properly marked block, RETurn with Marker! queued.
53B5
INC HL 23
INCrement Register Pair HL by 1 so that it addresses the first character of the block proper rather than the 0FEH begin marker.
53B6
EX DE,HL EB
Exchange Register Pair DE with Register Pair HL. Register Pair DE now holds the address of the first character to print and Register Pair HL the address of the end marker.
53B7
GOSUB to 5A07H, which subtracts Register Pair DE from Register Pair HL and leaves the result in Register Pair BC. That is the number of characters between the two markers.
53BA
JUMP to 53C2H, the common print loop.
53BC
LD DE,5D00H 11 00 5D
Point Register Pair DE at 5D00H, the first byte of the text buffer. This is where CTRL P starts.
53BF
GOSUB to 5A04H, which loads Register Pair HL with the end-of-text pointer from 58C2H, subtracts Register Pair DE, and leaves the length of the whole text in Register Pair BC.
53C2
EX DE,HL EB
Exchange Register Pair DE with Register Pair HL so that Register Pair HL addresses the first character to print.

Loop Start
One character is sent to the printer on each pass and the BREAK key is tested between characters.

53C3
LD A,B 78
Load Register A with Register B, the high byte of the remaining character count in Register Pair BC.
53C4
OR C B1
OR Register A with Register C, the low byte of the count. The Z FLAG is set only when the whole of Register Pair BC is zero.
53C5
RET Z C8
If the Z FLAG (Zero) has been set, meaning every character has been printed, RETurn to the main loop.
53C6
PUSH BC C5
Save Register Pair BC, the remaining character count, onto the stack. The printer driver does not preserve it.
53C7
LD A,(HL) 7E
Load Register A with the text character addressed by Register Pair HL.
53C8
INC HL 23
INCrement Register Pair HL by 1 to address the next text character.
53C9
GOSUB to the Level II ROM $PRT routine at 003BH, which sends the character in Register A to the line printer.
53CC
POP BC C1
Restore Register Pair BC, the remaining character count, from the stack.
53CD
GOSUB to 5ABFH, which tests the BREAK bit, bit 0 of KFLAG$ 4423H, holds the listing for as long as the key is held down, and returns with the NZ FLAG set if BREAK was pressed at all.
53D0
LD A,0DH 3E 0D
Load Register A with 0DH, a carriage return, ready to finish the partly printed line if the operator is stopping the listing.
53D2
If the NZ FLAG (Not Zero) has been set, meaning BREAK was pressed, JUMP to the ROM $PRT routine at 003BH, which prints that carriage return and returns straight to the main loop.
53D5
DEC BC 0B
DECrement Register Pair BC by 1, one character fewer to print.

Loop End

53D6
LOOP BACK to 53C3H for the next character.

53D8H - Editor Entry, and SHIFT UP: Top of Text or Delete to Top

Two routines share this space. 53D8H is the editor proper, called once from 5304H, and its first act is to record the stack pointer so that the main loop can throw the stack away on every keystroke. 53DCH is the handler for key code 92H, SHIFT with the up arrow, which means "go to the top of the text" normally and "delete everything above the cursor" when DELETE mode is in force; on the ordinary path it falls straight through into the main loop set-up at 53FEH, which is exactly what going to the top of the text amounts to.

The end-of-text pointer at 58C2H is deliberately not adjusted when text is deleted here. It is re-established by the redraw at 5404H, because the line display routine records the address of the terminating zero byte at 5828H as soon as it reaches it.

Self-Modifying Code
540EH is an instruction operand, not a variable. It is written once, here, and holds the stack pointer value that 5304H's CALL left behind.

53D8
LD (540EH),SP ED 73 0E 54
Store the Stack Pointer into 540EH, the operand of the LD SP,nnnn instruction at 540DH. Executing 540DH therefore restores the stack to exactly what it was when the editor was called, which is how the main loop can be re-entered from any depth without unwinding.
53DC
BIT 1,(IX+04H) DD CB 04 4E
Test bit 1 of the mode flag byte at 5204H, the DELETE flag. It is set only by the CTRL D handler at 55F3H and only for one keystroke.
53E0
If the Z FLAG (Zero) has been set, meaning DELETE mode is not in force, JUMP to 53FEH, which puts the cursor at the top of the text and redraws. That is the whole of the plain SHIFT-up-arrow command.
53E2
LD HL,5D00H 21 00 5D
Point Register Pair HL at 5D00H, the first byte of the text buffer.
53E5
GOSUB to 58C7H, which compares Register Pair HL, the start of the text, against Register Pair DE, the text address of the cursor that the main loop put there at 5437H. They are equal if the cursor is already at the top.
53E8
RET Z C8
If the Z FLAG (Zero) has been set, meaning the cursor is already on the first character of the text, RETurn; there is nothing above it to delete.
53E9
GOSUB to 58CDH, which displays Press <ENTER> to confirm and waits. Any key but ENTER makes that routine drop this routine's return address, so the delete is abandoned without returning here.
53EC
LD (5438H),HL 22 38 54
Store Register Pair HL, which is still 5D00H, into 5438H, the text address of the cursor. Once the text above the cursor is gone the cursor belongs on the first byte of the buffer.
53EF
LD A,(DE) 1A
Load Register A with the text character the cursor is on, addressed by Register Pair DE.
53F0
OR A B7
Test Register A against zero. A zero byte means the cursor is on the terminator, so the delete takes the whole text.
53F1
If the Z FLAG (Zero) has been set, meaning the cursor is at the end of the text, JUMP to 53FDH, which simply writes that zero at 5D00H and empties the buffer.
53F3
PUSH DE D5
Save Register Pair DE, the cursor address, onto the stack.
53F4
PUSH HL E5
Save Register Pair HL, the address 5D00H, onto the stack.
53F5
GOSUB to 5A04H, which loads Register Pair HL with the end-of-text pointer from 58C2H, subtracts Register Pair DE, the cursor address, and leaves the difference in Register Pair BC. That is the number of text bytes from the cursor to the terminator.
53F8
INC BC 03
INCrement Register Pair BC by 1 so that the terminating zero byte is moved down with the text.
53F9
POP DE D1
Restore the first value pushed off the stack into Register Pair DE. The two pushes were in the order cursor then 5D00H, so Register Pair DE now holds 5D00H, the destination of the move.
53FA
POP HL E1
Restore the second value into Register Pair HL, the cursor address, which is the source of the move.
53FB
LDIR ED B0
Block move Register Pair BC bytes from the cursor down to 5D00H, incrementing both pointers. Everything above the cursor has now been deleted.
53FD
LD (HL),A 77
Store Register A, which is zero, at the address in Register Pair HL. On the jump from 53F1H that address is 5D00H and this is what makes the buffer empty.

53FEH - The Main Command Loop

The heart of the editor. 53FEH is the full restart, used at start-up and whenever the whole screen has to be rebuilt from the top of the text; 540DH is the per-keystroke entry that every command routine returns to. Because 540DH reloads the stack pointer from the value saved at 53D8H, a command routine may be several calls deep when it decides to give up and can simply jump here.

Four of the instructions in this loop carry their working values in their own operand fields rather than in variables: 540DH holds the saved stack pointer at 540EH, 5421H holds the cursor row and column at 5422H, 5425H holds the pending keystroke at 5426H, and 5437H holds the text address of the cursor at 5438H. Register H of the 5422H pair is the video row 0 to 13 and Register L the column 0 to 63.

53FE
LD HL,5D00H 21 00 5D
Point Register Pair HL at 5D00H, the first byte of the text buffer.
5401
LD (5438H),HL 22 38 54
Store Register Pair HL into 5438H, the text address of the cursor, and so the address of the first character the redraw will display. The page-up handler at 563FH jumps here with its own value in Register Pair HL.
5404
GOSUB to 57B6H, which redraws all fourteen text lines from the address in Register Pair HL and rebuilds the line table at 5B96H.
5407
LD HL,0000H 21 00 00
Set Register Pair HL to ZERO, row 0 and column 0.
540A
LD (5422H),HL 22 22 54
Store that into 5422H, the operand of the LD HL,nnnn at 5421H, putting the cursor in the top left corner of the text area.

Loop Start
Every command routine returns or jumps to 540DH. The stack is reset first, so a routine that abandons what it was doing does not have to unwind its own calls.

540D
LD SP,0000H 31 00 00
Load the Stack Pointer with the value 53D8H stored into this instruction's operand at 540EH, which is the stack as it stood when 5304H called the editor. Anything left on the stack by the command just executed is discarded.
5410
GOSUB to 5ABFH, which tests the BREAK bit in KFLAG$ 4423H and, if the key is down, holds here until it is released.
5413
BIT 7,(IX+04H) DD CB 04 7E
Test bit 7 of the mode flag byte at 5204H, which is set while a status message other than the sign-on banner is on the screen.
5417
If the NZ FLAG (Not Zero) has been set, meaning a message is on the status line, GOSUB to 58FFH, which puts the sign-on banner back and clears every bit of the flag byte except the insert flag. That is why Block, Delete and every error message last for exactly one keystroke.
541A
BIT 6,(IX+04H) DD CB 04 76
Test bit 6 of the mode flag byte at 5204H, which 5332H sets when a message has been queued at 5208H but not yet displayed.
541E
If the NZ FLAG (Not Zero) has been set, meaning a message is waiting, GOSUB to 58F6H, which loads the queued message address from 5208H and writes it on the status line.
5421
LD HL,0000H 21 00 00
Load Register Pair HL with the cursor position from this instruction's own operand at 5422H: Register H is the video row and Register L the column.
5424
PUSH HL E5
Save the cursor row and column onto the stack. 5435H swaps them back out again once the return address has been planted.
5425
LD A,00H 3E 00
Load Register A with the pending keystroke from this instruction's own operand at 5426H. Initialisation puts 0CH there when a filespec was given on the command line, and 5644H is where it is read and cleared.
5427
OR A B7
Test Register A, the pending keystroke, against zero.
5428
If the NZ FLAG (Not Zero) has been set, meaning a keystroke has already been supplied, JUMP to 5432H and act on it without touching the keyboard.
542A
LD B,03H 06 03
Load Register B with 3, the function code that makes the cursor primitive at 5A71H set the hardware cursor position.
542C
GOSUB to 5A71H, which converts the row and column in Register Pair HL into a video address and stores it at 4020H, the ROM cursor position cell.
542F
GOSUB to 55E3H, which blinks the cursor character over the character the cursor is on until a key is pressed, and returns that key in Register A.
5432
LD HL,540DH 21 0D 54
Point Register Pair HL at 540DH, the top of this loop.
5435
EX (SP),HL E3
Exchange the top of the stack with Register Pair HL. 540DH becomes the return address every command routine will use, and Register Pair HL gets back the cursor row and column that 5424H pushed.
5436
RET Z C8
If the Z FLAG (Zero) has been set, RETurn, which lands on 540DH and restarts the loop. 55E3H leaves the Z FLAG set only when the key was BREAK, which it has already dealt with by clearing the mode flag byte and resetting the cursor character.
5437
LD DE,0000H 11 00 00
Load Register Pair DE with the text address of the character under the cursor from this instruction's own operand at 5438H. Every command routine can rely on finding it there.
543A
BIT 2,(IX+04H) DD CB 04 56
Test bit 2 of the mode flag byte at 5204H, the BLOCK flag set by 5320H on the previous keystroke.
543E
If the NZ FLAG (Not Zero) has been set, meaning the previous key was CTRL B, JUMP to 537AH, which reads this key as a block sub-command letter instead of as text or a command.
5441
BIT 7,A CB 7F
Test bit 7 of Register A, the key just read. The KI/DVR special-key translation table returns the arrow keys, the shifted arrow keys and the exit key with bit 7 set, so a set bit 7 always means a command.
5443
If the NZ FLAG (Not Zero) has been set, meaning the key has bit 7 set, JUMP to 5449H to look it up in the command key table.
5445
CP 20H FE 20
Compare Register A against 20H, the ASCII space. Anything below it is a control code.
5447
If the NO CARRY FLAG has been set, meaning the key is 20H or above and, since bit 7 was clear, no higher than 7FH, JUMP to 5469H and enter it into the text. This is the test the manual describes as "only displayable ASCII characters in the range 20H through 7FH".

5449H - The Command Key Lookup and the No Room Message

A straight linear scan of the twenty-two entry table at 525DH. A key that is not in the table is thrown away, exactly as the manual says. The tail of the section, 5459H, is the common "buffer full" exit that the text entry and block copy routines jump to.

5449
PUSH HL E5
Save Register Pair HL, the cursor row and column, onto the stack. The lookup needs the stack slot as the place to build the address it will jump to.
544A
LD HL,525DH 21 5D 52
Point Register Pair HL at 525DH, the first key code in the command key table.
544D
LD B,16H 06 16
Load Register B with 16H, twenty-two, the number of entries in the table.

Loop Start
Three bytes are stepped over on each pass: one key code and the two bytes of its handler address.

544F
CP (HL) BE
Compare Register A, the key just read, against the key code addressed by Register Pair HL. If they match the Z FLAG is set.
5450
INC HL 23
INCrement Register Pair HL by 1 so that it addresses the low byte of this entry's handler address.
5451
If the Z FLAG (Zero) has been set, meaning this table entry is the key that was pressed, JUMP to 5461H to fetch the handler address.
5453
INC HL 23
INCrement Register Pair HL by 1, stepping over the low byte of the handler address.
5454
INC HL 23
INCrement Register Pair HL by 1 again, stepping over the high byte and leaving Register Pair HL on the next key code.

Loop End

5455
DECrement Register B and LOOP BACK to 544FH if there are entries left to test.
5457
POP HL E1
Restore Register Pair HL, the cursor row and column, from the stack. The key matched nothing in the table.
5458
RET C9
RETurn to 540DH, the top of the main loop. An unrecognised key is simply ignored.
5459
LD HL,5B33H 21 33 5B
Point Register Pair HL at the message at 5B33H, No room!. 547AH reaches here when a character cannot be inserted and 59A0H when a block copy will not fit.
545C
GOSUB to 532FH, which stores the message address at 5208H and sets the message-pending bit in the flag byte at 5204H.
545F
JUMP to 540DH, the top of the main loop, which will display the message on its way round.
5461
LD A,(HL) 7E
Load Register A with the low byte of the handler address, addressed by Register Pair HL.
5462
INC HL 23
INCrement Register Pair HL by 1 to address the high byte of the handler address.
5463
LD H,(HL) 66
Load Register H with that high byte.
5464
LD L,A 6F
Load Register L with the low byte held in Register A, so that Register Pair HL now holds the handler address itself.
5465
EX (SP),HL E3
Exchange the top of the stack with Register Pair HL. The handler address replaces the cursor row and column on the stack, and Register Pair HL gets the row and column back, which is what every handler expects to find there.
5466
RET C9
RETurn, which jumps to the handler. The 540DH planted at 5435H is still underneath, so the handler's own RETurn goes back to the main loop.

5467H - Text Entry: ENTER and Every Displayable Character

The routine that puts a character into the text. 5467H is the ENTER handler, which does nothing but choose 0DH as the character; 5469H is the entry the main loop uses for any key between 20H and 7FH, and the block marker inserter at 539EH uses it too. In overstrike mode the character replaces what is under the cursor unless the cursor is at the end of the text, in which case the text has to grow and the insert path is taken anyway.

5467
LD A,0DH 3E 0D
Load Register A with 0DH, a carriage return. That is the byte TED stores in the text for a newline, and the display routine shows it as the small graphics block 84H.
5469
LD C,A 4F
Load Register C with the character to be entered, which the main loop left in Register A.
546A
PUSH BC C5
Save Register Pair BC onto the stack. Register C carries the character through the insert, which uses Register Pair BC as a block move counter.
546B
BIT 0,(IX+04H) DD CB 04 46
Test bit 0 of the mode flag byte at 5204H, the insert flag.
546F
If the NZ FLAG (Not Zero) has been set, meaning insert mode is in force, JUMP to 5475H to open a hole in the text.
5471
LD A,(DE) 1A
Load Register A with the character currently under the cursor, addressed by Register Pair DE.
5472
OR A B7
Test Register A, that character, against zero.
5473
If the NZ FLAG (Not Zero) has been set, meaning there is a character there to write over, JUMP to 5492H and simply store the new one. A zero means the cursor is on the terminator at the end of the text, so even in overstrike mode the text has to be made longer and the insert path below is used.
5475
PUSH HL E5
Save Register Pair HL, the cursor row and column, onto the stack.
5476
PUSH DE D5
Save Register Pair DE, the text address of the cursor, onto the stack.
5477
GOSUB to 58C1H. That instruction loads Register Pair DE with the end-of-text pointer held in its own operand at 58C2H and the one below it loads Register Pair HL with the buffer ceiling held at 58C5H, and the two are then compared. The Z FLAG is set when the text has grown up to HIGH$ and there is no room for another character.
547A
If the Z FLAG (Zero) has been set, meaning the text buffer is full, JUMP to 5459H to queue No room! and go back to the main loop.
547C
LD HL,(58C2H) 2A C2 58
Load Register Pair HL with the end-of-text pointer from 58C2H.
547F
INC HL 23
INCrement Register Pair HL by 1; the text is about to become one byte longer.
5480
LD (58C2H),HL 22 C2 58
Store the new end-of-text pointer back into 58C2H.
5483
POP DE D1
Restore the first value pushed off the stack into Register Pair DE. The pushes were row and column then cursor address, so Register Pair DE now holds the text address of the cursor.
5484
PUSH HL E5
Save Register Pair HL, the new end-of-text address, onto the stack.
5485
GOSUB to 5A04H, which reloads Register Pair HL from 58C2H, subtracts Register Pair DE, the cursor address, and leaves the result in Register Pair BC. That is the number of bytes that have to move up to make room, counting the terminator.
5488
EX DE,HL EB
Exchange Register Pair DE with Register Pair HL, putting the cursor address in Register Pair HL and the byte count in Register Pair DE.
5489
EX (SP),HL E3
Exchange the top of the stack with Register Pair HL. Register Pair HL takes the new end-of-text address and the cursor address is left on the stack for 548FH.
548A
LD D,H 54
Load Register D with Register H.
548B
LD E,L 5D
Load Register E with Register L, so that Register Pair DE now holds the new end-of-text address, the destination of the move.
548C
DEC HL 2B
DECrement Register Pair HL by 1, giving the old end-of-text address, the source of the move.
548D
LDDR ED B8
Block move Register Pair BC bytes downwards from the old end of the text to the new one, decrementing both pointers. Every character from the cursor to the terminator shifts up one position and the byte under the cursor is now free.
548F
POP DE D1
Restore Register Pair DE from the stack, the text address of the cursor.
5490
POP HL E1
Restore Register Pair HL from the stack, the cursor row and column.
5491
LD A,C 79
Load Register A with Register C, which the block move has left at zero. A zero here tells the test at 5493H that nothing was overwritten, because this was an insert.
5492
POP BC C1
Restore Register Pair BC from the stack, bringing back the character to be entered in Register C.
5493
CP 0DH FE 0D
Compare Register A against 0DH. On the overstrike path Register A holds the character that is about to be replaced, so the Z FLAG is set when a newline is being written over; on the insert path Register A is zero and the Z FLAG is cleared.
5495
LD A,C 79
Load Register A with the character to be entered from Register C.
5496
LD (DE),A 12
Store that character into the text at the address in Register Pair DE, the cursor.
5497
If the Z FLAG (Zero) has been set, meaning a newline has just been written over, JUMP to 54FCH, because the line structure of the display has changed.
5499
BIT 0,(IX+04H) DD CB 04 46
Test bit 0 of the mode flag byte at 5204H, the insert flag, again.
549D
If the NZ FLAG (Not Zero) has been set, meaning the character was inserted rather than written over, JUMP to 54FEH, which always redraws because everything after the cursor has moved.
549F
CP 0DH FE 0D
Compare Register A, the character just stored, against 0DH.
54A1
If the Z FLAG (Zero) has been set, meaning a newline has been typed in overstrike mode, JUMP to 5503H, which redraws and then fixes the line table before moving to the next line.
54A3
GOSUB to 5775H, which converts the character in Register C to its display form and writes it on the screen at the cursor. Nothing else on the screen has changed, so no redraw is needed.
54A6
JUMP to 5516H, which moves the cursor one position right, wrapping to the next line at column 63.

54A9H - SHIFT RIGHT: End of Line, or Delete to End of Line

Key code 94H. In DELETE mode this is the "delete to eol" sub-command the manual lists, and it asks for confirmation first; otherwise it just parks the cursor in the last used column of the current display line.

54A9
BIT 1,(IX+04H) DD CB 04 4E
Test bit 1 of the mode flag byte at 5204H, the DELETE flag set by the CTRL D handler for one keystroke.
54AD
If the Z FLAG (Zero) has been set, meaning DELETE mode is not in force, JUMP to 54F5H, which measures the line and puts the cursor at its end.
54AF
LD A,(DE) 1A
Load Register A with the character under the cursor, addressed by Register Pair DE.
54B0
OR A B7
Test Register A against zero.
54B1
RET Z C8
If the Z FLAG (Zero) has been set, meaning the cursor is on the terminator at the end of the text, RETurn; there is nothing to delete.
54B2
GOSUB to 58CDH for the Press <ENTER> to confirm prompt. Any other key abandons the command inside that routine.
54B5
PUSH DE D5
Save Register Pair DE, the text address of the cursor, onto the stack.
54B6
GOSUB to 5879H, which walks the current display line and returns Register Pair DE addressing the character that ended it, either a newline, the terminating zero, or the sixty-fourth character.
54B9
LD A,(DE) 1A
Load Register A with that line-ending character.
54BA
OR A B7
Test Register A against zero.
54BB
If the NZ FLAG (Not Zero) has been set, meaning the line ended for some reason other than the end of the text, JUMP to 54C1H to close the gap.
54BD
POP HL E1
Restore Register Pair HL from the stack, the text address of the cursor. The line runs to the end of the text, so there is nothing to move down.
54BE
LD (HL),A 77
Store Register A, which is zero, at the cursor. The text now ends there.
54BF
JUMP to 54EFH to record the new end-of-text address and redraw.
54C1
LD H,D 62
Load Register H with Register D.
54C2
LD L,E 6B
Load Register L with Register E, so that Register Pair HL addresses the line-ending character.
54C3
INC HL 23
INCrement Register Pair HL by 1 so that it addresses the first byte beyond it, which is the source of the move that closes the gap.
54C4
PUSH HL E5
Save Register Pair HL, that source address, onto the stack.
54C5
GOSUB to 5A04H, which loads Register Pair HL from the end-of-text pointer at 58C2H, subtracts Register Pair DE, the line-ending character address, and leaves the number of bytes to move in Register Pair BC.
54C8
POP HL E1
Restore Register Pair HL from the stack, the source address.
54C9
POP DE D1
Restore Register Pair DE from the stack, the cursor address, which is the destination.
54CA
JUMP to 54E5H, the common tail that performs the move and rebuilds the end-of-text pointer.

54CCH - SHIFT LEFT: Beginning of Line, or Delete to Beginning of Line

Key code 91H, the mirror of the routine above. In DELETE mode it removes everything from the start of the display line up to the cursor, which is the manual's "delete to bol"; otherwise it moves the cursor to column 0.

54CC
BIT 1,(IX+04H) DD CB 04 4E
Test bit 1 of the mode flag byte at 5204H, the DELETE flag.
54D0
If the Z FLAG (Zero) has been set, meaning DELETE mode is not in force, JUMP to 54F9H, which sets the column to zero.
54D2
LD A,L 7D
Load Register A with Register L, the cursor column.
54D3
OR A B7
Test Register A, the column, against zero.
54D4
RET Z C8
If the Z FLAG (Zero) has been set, meaning the cursor is already in column 0, RETurn; there is nothing on the line before it.
54D5
GOSUB to 58CDH for the confirmation prompt. It returns with Register A cleared to zero, because it got there by subtracting 0DH from the ENTER the operator pressed.
54D8
LD L,A 6F
Load Register L with Register A, which is zero, moving the cursor to column 0 of the same row.
54D9
LD (5422H),HL 22 22 54
Store the row and column into 5422H, the operand of the LD HL,nnnn at 5421H.
54DC
PUSH DE D5
Save Register Pair DE, the old cursor address, onto the stack; it is the source of the move.
54DD
GOSUB to 5A04H, which loads Register Pair HL from 58C2H, subtracts the old cursor address in Register Pair DE, and leaves the number of bytes from the cursor to the terminator in Register Pair BC.
54E0
INC BC 03
INCrement Register Pair BC by 1 so that the terminating zero moves down with the text.
54E1
GOSUB to 550BH, which recomputes the text address of the cursor from the row and column just stored and returns it in Register Pair DE. That address, the start of the display line, is the destination of the move.
54E4
POP HL E1
Restore Register Pair HL from the stack, the old cursor address, the source of the move.

54E5H - The Common Deletion Tail

Where both of the line deletions above and the block delete at 54BFH end up. The end-of-text pointer is not adjusted arithmetically; the text is scanned from the front for its terminating zero, which costs nothing noticeable and cannot get out of step. 54FCH onwards is the tail the text entry routine uses when the shape of the display has changed.

54E5
GOSUB to 5928H, which block moves Register Pair BC bytes from Register Pair HL to Register Pair DE, but returns at once if the count is zero rather than moving 65536 bytes.
54E8
LD HL,5D00H 21 00 5D
Point Register Pair HL at 5D00H, the first byte of the text buffer.
54EB
XOR A AF
Set Register A to ZERO and clear all flags, ready to search for the terminating zero byte.
54EC
CPIR ED B1
Search forward from 5D00H for a zero byte. Register Pair BC is left at zero by the block move above, so the search has no practical limit and simply stops on the first zero it finds, which is the end of the text.
54EE
DEC HL 2B
DECrement Register Pair HL by 1. The search leaves Register Pair HL one past the byte it matched, so this puts it back on the terminator.
54EF
LD (58C2H),HL 22 C2 58
Store the address of the terminating zero into 58C2H, the end-of-text pointer and the operand of the instruction at 58C1H.
54F2
JUMP to 5611H, which redraws the screen from the cursor row down.
54F5
GOSUB to 5879H, which returns in Register A the number of characters on the current display line. That is the column the cursor wants for "end of line".
54F8
LD B,0AFH 06 AF
Load Register B with 0AFH. Only the 06H opcode byte is executed on this path; the operand exists so that 54D0H can jump one byte in and read it as the instruction below.
54F9
XOR A AF
Set Register A to ZERO and clear all flags, giving column 0 for the "beginning of line" command that jumps here from 54D0H.
54FA
JUMP to 554AH, which stores Register A as the new cursor column and recomputes the text address that goes with it.
54FC
CP 0DH FE 0D
Compare Register A, the character that has just been entered, against 0DH. This is reached only when a newline was written over, so the Z FLAG is set when one newline replaced another and the shape of the display has not changed.
54FE
If the NZ FLAG (Not Zero) has been set, GOSUB to 5614H, which redraws the screen from the cursor row down. The insert path from 549DH always arrives with the NZ FLAG set, so an inserted character always causes a redraw.
5501
JUMP to 553DH to move the cursor one position right.
5503
GOSUB to 5614H to redraw the screen from the cursor row down. A newline typed in overstrike mode has re-broken every line below it.
5506
JUMP to 551BH, which repairs the line table entry for the following row and then moves the cursor to the start of it.

5508H - An Unreachable Fragment

Three bytes that form a complete instruction but that nothing in the module reaches. The instruction before them, at 5506H, is an unconditional jump, and no branch anywhere in the file names 5508H. It would store Register A into 5423H, the cursor row half of the position word at 5422H.

5508
LD (5423H),A 32 23 54
Store Register A into 5423H, the cursor row. No path in the module executes this instruction; it is left in the image as three bytes of dead code between the newline handler above and the cursor address routine below.

550BH - Recomputing the Text Address of the Cursor

Called whenever the cursor row or column has been changed, to bring the text address at 5438H back into step with it. 550EH is a second entry used when the caller already has the row and column in Register Pair HL.

550B
LD HL,(5422H) 2A 22 54
Load Register Pair HL with the cursor row and column from 5422H.
550E
GOSUB to 5892H, which looks the row up in the line table at 5B96H, adds the column, and returns the text address in Register Pair DE.
5511
LD (5438H),DE ED 53 38 54
Store that text address into 5438H, the operand of the LD DE,nnnn instruction at 5437H, so that the next pass round the main loop hands it to the next command.
5515
RET C9
RETurn.

5516H - Column Wrap and the Newline Line-Table Repair

5516H is the "move one position right" that text entry uses. When the cursor is already in the last column, or when a newline has just been typed over an existing character, the following row has to be told where it now starts, which is what 551BH writes into the line table at 5B96H before the cursor is dropped onto it.

5516
LD A,L 7D
Load Register A with Register L, the cursor column.
5517
CP 3FH FE 3F
Compare Register A against 3FH, column 63, the last column of a display line.
5519
If the NZ FLAG (Not Zero) has been set, meaning there is room to move right on this line, JUMP to 553DH, the ordinary cursor right handler.
551B
INC DE 13
INCrement Register Pair DE by 1. Register Pair DE addresses the character just entered, so this is the text address of the first character of the next row.
551C
PUSH DE D5
Save that address onto the stack.
551D
GOSUB to 58ADH, which points Register Pair IY at the line table entry for the row in Register H.
5520
POP DE D1
Restore the text address into Register Pair DE.
5521
LD (IY+02H),E FD 73 02
Store Register E, the low byte of that address, into the line table two bytes past the current row, which is the entry for the row below.
5524
LD (IY+03H),D FD 72 03
Store Register D, the high byte, into the byte after it, completing the line table entry for the next row.
5527
LD L,00H 2E 00
Load Register L with 0, column 0, the first position of that next row. Execution falls into the cursor down handler, which does the rest.

5529H - Cursor Down

Key code 88H, the down arrow, and the tail of the column wrap above. The row number is simply incremented unless the cursor is already on the bottom text line, in which case the display is scrolled up one line first and the cursor stays where it is. The column is then clamped to the length of the line it has landed on.

5529
GOSUB to 5879H, which measures the current display line. It returns the character count in Register A and, more usefully here, Register Pair DE addressing the character that ended the line.
552C
LD A,(DE) 1A
Load Register A with that line-ending character.
552D
OR A B7
Test Register A against zero.
552E
RET Z C8
If the Z FLAG (Zero) has been set, meaning the current line runs to the end of the text, RETurn; there is no line below to move to.
552F
LD A,H 7C
Load Register A with Register H, the cursor row.
5530
CP 0DH FE 0D
Compare Register A against 0DH, row 13, the bottom of the fourteen line text area.
5532
If the NZ FLAG (Not Zero) has been set, meaning the cursor is not on the bottom line, JUMP to 553AH and simply move down a row.
5534
PUSH HL E5
Save Register Pair HL, the cursor row and column, onto the stack.
5535
GOSUB to 57B3H, which reloads Register Pair HL from the line table entry for row 1 at 5B98H and redraws the whole text area from there, scrolling the display up by one line.
5538
POP HL E1
Restore Register Pair HL, the cursor row and column, from the stack.
5539
DEC H 25
DECrement Register H by 1 to cancel the increment in the next instruction. The display has moved instead of the cursor, so the cursor must stay on row 13.
553A
INC H 24
INCrement Register H by 1, moving the cursor down one row.
553B
JUMP to 5561H, which clamps the column to the length of the new line and stores the position.

553DH - Cursor Right

Key code 84H, the right arrow. The cursor stops at the end of the text, and steps to the start of the following row when it is on a newline or has reached column 63.

553D
LD A,(DE) 1A
Load Register A with the character under the cursor, addressed by Register Pair DE.
553E
OR A B7
Test Register A against zero.
553F
RET Z C8
If the Z FLAG (Zero) has been set, meaning the cursor is on the terminator at the end of the text, RETurn; there is nowhere to move to.
5540
CP 0DH FE 0D
Compare Register A, the character under the cursor, against 0DH, a newline.
5542
If the Z FLAG (Zero) has been set, meaning the cursor is on a newline, JUMP to 5527H, which sets the column to zero and drops onto the next row.
5544
LD A,L 7D
Load Register A with Register L, the cursor column.
5545
INC A 3C
INCrement Register A by 1, the column the cursor is moving to.
5546
CP 40H FE 40
Compare Register A against 40H, column 64, one past the last column on the screen.
5548
If the Z FLAG (Zero) has been set, meaning the cursor has run off the right-hand end of the line, JUMP to 5527H to start the next row.
554A
LD (5422H),A 32 22 54
Store Register A, the new column, into 5422H, the low half of the cursor position word. The row at 5423H is untouched.
554D
JUMP to 550BH, which recomputes the text address that goes with the new column and stores it at 5438H.

554FH - Cursor Left, Cursor Up and the Column Clamp

Two handlers that run into one another on purpose. Key code 81H, the left arrow, decrements the column; if that takes it below zero the code drops straight into the up-arrow handler with the column left at 0FFH, and the clamp at 5561H then pulls it back to the length of the line above. That is how the left arrow wraps to the end of the previous line without any code of its own.

554F
DEC L 2D
DECrement Register L by 1, the cursor column. If the cursor was in column 0 the result is 0FFH and the S FLAG is set.
5550
If the S FLAG is clear, meaning the column is still 0 or above, JUMP to 5568H to store the new position.
5553
DEC H 25
DECrement Register H by 1, the cursor row. This is the entry point for key code 82H, the up arrow, and also where the left arrow arrives when it has run off the left-hand end of a line. If the cursor was on row 0 the result is 0FFH and the S FLAG is set.
5554
If the S FLAG is clear, meaning there is a row above to move to, JUMP to 5561H to clamp the column and store the position.
5557
GOSUB to 58B9H, which loads Register Pair HL from the line table entry for row 0 at 5B96H and compares it with 5D00H, the start of the text.
555A
RET Z C8
If the Z FLAG (Zero) has been set, meaning the top line of the display is already the first line of the text, RETurn; there is nothing above it.
555B
GOSUB to 57ABH, which works out the display line before the one at the top of the screen and redraws from it, scrolling the display down by one line.
555E
LD HL,(5422H) 2A 22 54
Load Register Pair HL with the cursor row and column from 5422H again. The row is still 0, which now addresses the line that has just been scrolled into view.
5561
GOSUB to 5879H, which returns in Register A the number of characters on the display line the cursor is now on.
5564
CP L BD
Compare Register A, that line length, against Register L, the column. The CARRY FLAG is set when the line is shorter than the column asks for.
5565
If the NO CARRY FLAG has been set, meaning the column is within the line, JUMP to 5568H and keep it.
5567
LD L,A 6F
Load Register L with Register A, clamping the column to the end of the line. This is also what turns the 0FFH left by 554FH into "the last character of the line above".
5568
LD (5422H),HL 22 22 54
Store the row and column into 5422H, the cursor position word.
556B
JUMP to 550EH, which converts that row and column into a text address and stores it at 5438H.

556DH - CTRL Z: End of Text, or Delete to Bottom

Key code 1AH. On the Model I keyboard the DOS 5 control prefix is SHIFT with the down arrow, so this is the key sequence the TED manual writes as SHIFT down arrow then Z. In DELETE mode it is the manual's "delete to bottom"; otherwise it puts the last line of the text on the top row of the screen and parks the cursor at the very end.

556D
BIT 1,(IX+04H) DD CB 04 4E
Test bit 1 of the mode flag byte at 5204H, the DELETE flag.
5571
If the Z FLAG (Zero) has been set, meaning DELETE mode is not in force, JUMP to 557BH to move to the end of the text.
5573
GOSUB to 58CDH for the Press <ENTER> to confirm prompt. It returns with Register A cleared to zero.
5576
EX DE,HL EB
Exchange Register Pair DE with Register Pair HL so that Register Pair HL holds the text address of the cursor.
5577
LD (HL),A 77
Store Register A, which is zero, at the cursor. Everything from the cursor to the old end of the text has been thrown away in one instruction.
5578
JUMP to 54EFH, which records the address of that new terminating zero at 58C2H and redraws.
557B
LD DE,(58C2H) ED 5B C2 58
Load Register Pair DE with the end-of-text pointer from 58C2H.
557F
GOSUB to 584DH, which rounds that address down to the start of the display line it falls on and returns it in Register Pair HL.
5582
GOSUB to 57B6H, which redraws the whole text area from that line, so that the last line of the text is on row 0.
5585
LD H,00H 26 00
Load Register H with 0, putting the cursor on the top row, which is the line just brought into view.
5587
GOSUB to 5879H, which returns the number of characters on that line in Register A.
558A
LD L,A 6F
Load Register L with Register A, putting the cursor one past the last character, which is where new text will be added.
558B
JUMP to 5568H to store the row and column and recompute the text address.

558DH - SEARCH and FIND NEXT

558DH is CTRL S, which prompts for the string and then searches; 559FH is CTRL G, which searches again for whatever is already in the search buffer. Answering the prompt with a bare ENTER keeps the previous string, so the two commands share everything below 559FH.

558D
GOSUB to 58E5H, which puts String? on the status line and reads a reply into the buffer at 5BB4H. It returns Register Pair HL addressing that buffer and Register B holding the number of characters typed.
5590
LD A,B 78
Load Register A with Register B, the length of the reply.
5591
OR A B7
Test Register A, the reply length, against zero.
5592
If the Z FLAG (Zero) has been set, meaning the operator pressed ENTER without typing anything, JUMP to 559FH and search for the string already held at 522AH.
5594
LD (IX+06H),B DD 70 06
Store Register B, the reply length, into 5206H, the search string length.
5597
LD DE,522AH 11 2A 52
Point Register Pair DE at 522AH, the search string buffer in the workspace below the image.
559A
LD C,B 48
Load Register C with Register B, the reply length.
559B
LD B,00H 06 00
Load Register B with 0, making Register Pair BC the byte count for the move.
559D
LDIR ED B0
Block move the reply from the prompt buffer at 5BB4H into the search string buffer at 522AH, incrementing both pointers.
559F
LD HL,(5438H) 2A 38 54
Load Register Pair HL with the text address of the cursor from 5438H. This is the CTRL G entry point, and the search starts from the character after the cursor so that a repeated find moves forward.
55A2
GOSUB to 55BBH, which walks the text forward comparing the search string at every position. It returns the Z FLAG set and Register Pair HL addressing the match, or the NZ FLAG set at the end of the text.
55A5
If the NZ FLAG (Not Zero) has been set, meaning the string was not found, JUMP to 532CH to queue Can't and return to the main loop.

55A8H - Bringing a Text Address to the Top of the Screen

Given a text address in Register Pair HL, redraw the text area so that the display line containing it is row 0, and leave the cursor on it. Every command that jumps somewhere in the text ends here: search, find next, block copy, block move, block delete and replace.

55A8
GOSUB to 58BCH, which loads Register Pair DE with 5D00H, the start of the text, and compares it against Register Pair HL. The Z FLAG is set when the address is the very first byte of the text.
55AB
LD D,H 54
Load Register D with Register H.
55AC
LD E,L 5D
Load Register E with Register L, copying the text address into Register Pair DE, which is what the line-start routine works on.
55AD
If the NZ FLAG (Not Zero) has been set, meaning the address is not the first byte of the text, GOSUB to 5852H, which rounds Register Pair DE down to the first character of the display line it falls on. At the very start of the text no rounding is needed and the call is skipped.
55B0
OR A B7
Clear the CARRY FLAG ready for the subtraction below.
55B1
SBC HL,DE ED 52
SUBtract Register Pair DE, the address of the start of the display line, from Register Pair HL, the address that was asked for. The difference is the column the cursor belongs in.
55B3
PUSH HL E5
Save that column onto the stack.
55B4
EX DE,HL EB
Exchange Register Pair DE with Register Pair HL so that Register Pair HL addresses the start of the display line.
55B5
GOSUB to 57B6H, which redraws all fourteen text lines starting from that line and rebuilds the line table at 5B96H.
55B8
POP HL E1
Restore the column into Register Pair HL. Register H is zero, which is the row the line was just redrawn on.
55B9
JUMP to 558BH, which is itself a jump to 5568H, where the row and column are stored and the text address recomputed.

55BBH - The String Search Loop

Walks the text one character at a time from the address in Register Pair HL, comparing the search string at 522AH against each position. The first position tested is one past the address supplied, which is what makes find-next move on rather than finding the same match again; the replace command compensates by handing in the address one below the cursor.

55BB
LD A,(IX+06H) DD 7E 06
Load Register A with the search string length from 5206H.
55BE
OR A B7
Test Register A, that length, against zero.
55BF
RET Z C8
If the Z FLAG (Zero) has been set, RETurn. A zero length means no search string has been given yet, and it is also the exit the loop below takes when the string has been matched.
55C0
LD A,(HL) 7E
Load Register A with the text character addressed by Register Pair HL.
55C1
OR A B7
Test Register A, that character, against zero.
55C2
If the NZ FLAG (Not Zero) has been set, meaning there is more text to search, JUMP to 55C6H.
55C4
OR H B4
OR Register A with Register H, the high byte of the text pointer, which is always at least 5CH. The result cannot be zero, so this forces the NZ FLAG on, which is the "string not found" answer.
55C5
RET C9
RETurn with the NZ FLAG set; the end of the text was reached.

Loop Start
One text position is tested on each pass.

55C6
INC HL 23
INCrement Register Pair HL by 1, moving on to the next candidate position.
55C7
PUSH HL E5
Save that position onto the stack; the comparison routine advances Register Pair HL as it works.
55C8
GOSUB to 579DH, which compares the search string at 522AH against the text at Register Pair HL and returns the Z FLAG set on a full match.
55CB
POP HL E1
Restore the candidate position into Register Pair HL.

Loop End

55CC
LOOP BACK to 55BFH, which returns if the comparison matched and otherwise falls into 55C0H to try the next position.

55CEH - CTRL A: Toggle Overstrike and Insert

Key code 01H. Flipping bit 0 of the mode flag byte is the whole of the command; the cursor character is changed with it so that the operator can see which mode is in force. 55DFH is a second entry used by the block marker inserter, and 55D3H a third used by the BREAK handling in 55EFH.

55CE
LD A,(IX+04H) DD 7E 04
Load Register A with the mode flag byte from 5204H.
55D1
XOR 01H EE 01
Exclusive-OR Register A with 01H, flipping bit 0, the insert flag, and leaving every other bit alone.
55D3
LD HL,525BH 21 5B 52
Point Register Pair HL at 525BH, the overstrike cursor character.
55D6
BIT 0,A CB 47
Test bit 0 of Register A, the new insert flag.
55D8
If the Z FLAG (Zero) has been set, meaning the new mode is overstrike, JUMP to 55DBH and take the underscore.
55DA
INC HL 23
INCrement Register Pair HL by 1 to address 525CH instead, the solid graphics block used as the insert mode cursor.
55DB
LD C,(HL) 4E
Load Register C with the chosen cursor character.
55DC
LD (IX+05H),C DD 71 05
Store it into 5205H, the current cursor character, which the blink routine at 5755H reads on every pass.
55DF
LD (IX+04H),A DD 77 04
Store Register A into the mode flag byte at 5204H. 53A4H jumps here to put the flag byte back after forcing insert mode for a block marker.
55E2
RET C9
RETurn.

55E3H - Waiting for a Keystroke

Called once per pass round the main loop. The cursor is blinked over whatever character it is sitting on until a key is pressed. BREAK is intercepted here rather than in the command table: it clears the DELETE and BLOCK modes and puts the overstrike cursor back, which is how the operator gets out of a half-typed command.

55E3
LD B,04H 06 04
Load Register B with 4, the function code that makes the cursor primitive return the current row and column.
55E5
GOSUB to 5A71H, which reads the hardware cursor address from 4020H and returns the row in Register H and the column in Register L.
55E8
GOSUB to 5751H, which alternates the cursor character from 5205H with the real character underneath it, scanning the keyboard between each change, and returns the key in Register A.
55EB
RET C D8
If the CARRY FLAG has been set, RETurn at once with the key in Register A. The flags on return from 5751H are those the keyboard driver left, and a set CARRY FLAG bypasses the BREAK test below.
55EC
CP 01H FE 01
Compare Register A against 01H, the value the keyboard returns for the BREAK key.
55EE
RET NZ C0
If the NZ FLAG (Not Zero) has been set, meaning an ordinary key was pressed, RETurn with it in Register A for the main loop to dispatch.
55EF
LD A,80H 3E 80
Load Register A with 80H. Bit 7 is the message-is-on-screen flag, so this value clears the insert, DELETE and BLOCK bits at once and asks the main loop to restore the sign-on banner.
55F1
JUMP to 55D3H, which selects the overstrike cursor character to match the cleared insert bit, stores it at 5205H, stores 80H into the flag byte at 5204H and returns. It leaves the Z FLAG set from the bit test, and the main loop reads that at 5436H as "start again without dispatching a key".

55F3H - CTRL D: Delete the Character Under the Cursor

Key code 04H. The character under the cursor goes, everything after it is pulled back one place, and DELETE mode is left set so that the next keystroke can turn the command into one of the four larger deletions.

55F3
SET 1,(IX+04H) DD CB 04 CE
Set bit 1 of the mode flag byte at 5204H, the DELETE flag. The main loop clears it again on the pass after next, so it governs exactly one further keystroke.
55F7
LD HL,5B1AH 21 1A 5B
Point Register Pair HL at the message at 5B1AH, Delete.
55FA
GOSUB to 532FH, which queues that message for the status line.
55FD
LD A,(DE) 1A
Load Register A with the character under the cursor, addressed by Register Pair DE.
55FE
OR A B7
Test Register A, that character, against zero.
55FF
RET Z C8
If the Z FLAG (Zero) has been set, meaning the cursor is on the terminator at the end of the text, RETurn; there is nothing to delete, but DELETE mode has still been set.
5600
PUSH DE D5
Save Register Pair DE, the cursor address, onto the stack. It is needed twice, as the destination of the move and as the base for the count.
5601
PUSH DE D5
Save Register Pair DE onto the stack a second time.
5602
GOSUB to 5A04H, which loads Register Pair HL from the end-of-text pointer at 58C2H, subtracts Register Pair DE, and leaves the number of bytes from the cursor to the terminator in Register Pair BC.
5605
LD HL,(58C2H) 2A C2 58
Load Register Pair HL with the end-of-text pointer from 58C2H.
5608
DEC HL 2B
DECrement Register Pair HL by 1; the text is about to be one byte shorter.
5609
LD (58C2H),HL 22 C2 58
Store the new end-of-text pointer back into 58C2H.
560C
POP HL E1
Restore one copy of the cursor address into Register Pair HL.
560D
INC HL 23
INCrement Register Pair HL by 1, so that it addresses the character after the one being deleted, the source of the move.
560E
POP DE D1
Restore the other copy of the cursor address into Register Pair DE, the destination of the move.
560F
LDIR ED B0
Block move Register Pair BC bytes down one position, incrementing both pointers, which closes the gap and brings the terminating zero with it.

5611H - Redrawing from the Cursor Row Down

The cheap redraw. Nothing above the cursor row can have changed, so only the rows from the cursor down are rebuilt, and the line table entries above the cursor keep their existing values. 5614H is the entry used when the caller already has the row and column in Register Pair HL.

5611
LD HL,(5422H) 2A 22 54
Load Register Pair HL with the cursor row and column from 5422H.
5614
PUSH HL E5
Save the row and column onto the stack.
5615
PUSH DE D5
Save Register Pair DE onto the stack; the callers of this routine expect it back unchanged.
5616
GOSUB to 58ADH, which points Register Pair IY at the line table entry for the row in Register H.
5619
LD A,H 7C
Load Register A with Register H, the row number, which the redraw routine takes as the first row to rebuild.
561A
LD L,(IY+00H) FD 6E 00
Load Register L with the low byte of that row's text address from the line table.
561D
LD H,(IY+01H) FD 66 01
Load Register H with the high byte, so that Register Pair HL now addresses the first character of the cursor row.
5620
GOSUB to 57B7H, the redraw entry that starts at the row in Register A rather than at row 0.
5623
POP DE D1
Restore Register Pair DE from the stack.
5624
POP HL E1
Restore the cursor row and column into Register Pair HL.
5625
RET C9
RETurn.

5626H - CTRL U: Page Up, and CTRL N: Page Down

The two paging commands. Page up steps the top-of-screen pointer back thirteen display lines, one at a time, because a display line is only known by walking the text; page down simply takes the text address of the bottom row and redraws from there, which is why the manual says the previous page overlaps by one line and the next page starts on the last line displayed.

The four bytes at 5638H are another deliberate overlap. Read from 5638H they load Register Pair IY and do nothing useful; entered at 5639H, which is where the command key table sends CTRL N, the FDH prefix is skipped and the same operand loads Register Pair HL with the line table entry for row 13.

5626
LD B,0DH 06 0D
Load Register B with 0DH, thirteen, the number of display lines to step back.
5628
LD HL,(5B96H) 2A 96 5B
Load Register Pair HL with the text address of the top display row from the line table entry at 5B96H.

Loop Start
One display line is stepped back on each pass, or the loop ends early at the start of the text.

562B
GOSUB to 58BCH, which loads Register Pair DE with 5D00H, the start of the text, and compares it against Register Pair HL.
562E
If the Z FLAG (Zero) has been set, meaning the top of the text has been reached, JUMP to 563FH and redraw from there.
5630
PUSH BC C5
Save Register Pair BC, which holds the remaining line count in Register B, onto the stack.
5631
EX DE,HL EB
Exchange Register Pair DE with Register Pair HL, putting the current line address in Register Pair DE where the previous-line routine expects it.
5632
GOSUB to 5844H, which returns in Register Pair HL the text address of the display line before the one in Register Pair DE.
5635
POP BC C1
Restore Register Pair BC, the remaining line count.

Loop End

5636
DECrement Register B and LOOP BACK to 562BH until thirteen lines have been stepped back.
5638
LD IY,(5BB0H) FD 2A B0 5B
Load Register Pair IY from 5BB0H. On the fall-through path from the loop above this is a harmless instruction whose only purpose is to let the command key table enter it one byte later.
5639
LD HL,(5BB0H) 2A B0 5B
Load Register Pair HL from 5BB0H, the line table entry for row 13, which holds the text address of the bottom display row. This is the CTRL N entry point.
563C
LD A,H 7C
Load Register A with Register H, the high byte of that address.
563D
OR L B5
OR Register A with Register L, the low byte. The Z FLAG is set only if the whole entry is zero, which is how the redraw records a row that lies past the end of the text.
563E
RET Z C8
If the Z FLAG (Zero) has been set, meaning the screen is not full of text, RETurn without moving. This is the manual's "if the video display has an incomplete screen of text displayed, the page next request will be ignored".
563F
JUMP to 5401H, which stores the address in Register Pair HL as the new top of screen, redraws all fourteen lines from it, and puts the cursor at row 0 column 0.

5642H - LOAD and FILE: the Filespec, the File and the Write

Both disk commands come through here. 5642H is CTRL L, LOAD, and 5643H is CTRL F, FILE; the only difference is the byte planted at 5670H, which is the operand of the LD B,nn at 566FH and decides which of the two paths is taken once the file has been named. A filespec taken from the command line at 52F6H skips the prompt, which is what makes TED filename load the file at start-up.

The File Control Block is at 520AH, which is IX+0AH. The three indexed references in this section reach the standard LDOS FCB fields through it: IX+0EH is FCB+04H, the high byte of the buffer address, IX+12H is FCB+08H, the end-of-file byte offset, and IX+16H is FCB+0CH, the ending record number. Bumping the high byte of the buffer address by one between records is what walks the transfer up the text buffer a page at a time.

5642
LD A,0AFH 3E AF
Load Register A with 0AFH, any non-zero value. This is the CTRL L entry point and the non-zero value is what selects the LOAD path at 5675H.
5643
XOR A AF
Set Register A to ZERO and clear all flags. This is the CTRL F entry point, one byte into the instruction above, and zero is what selects the FILE path.

Self-Modifying Code
5670H is an instruction operand. It holds 00H after CTRL F and 0AFH after CTRL L, and 566FH reads it back once the file has been named and opened.

5644
LD (5670H),A 32 70 56
Store Register A into 5670H, the operand of the LD B,nn instruction at 566FH.
5647
LD HL,5426H 21 26 54
Point Register Pair HL at 5426H, the pending keystroke cell that is the operand of the instruction at 5425H.
564A
LD C,A 4F
Load Register C with Register A, keeping the LOAD or FILE selector where the prompt choice at 5657H can test it.
564B
LD A,(HL) 7E
Load Register A with the pending keystroke from 5426H. It is 0CH only on the very first pass, when initialisation queued a LOAD because a filespec was given on the command line.
564C
LD (HL),00H 36 00
Store zero into 5426H, so that the queued command can never fire twice.
564E
OR A B7
Test Register A, the pending keystroke, against zero.
564F
LD HL,5BB4H 21 B4 5B
Point Register Pair HL at 5BB4H, the buffer that holds either the filespec copied from the command line or the reply to the prompt.
5652
If the NZ FLAG (Not Zero) has been set, meaning this LOAD was queued by the command line, JUMP to 5660H and use the filespec already in that buffer without prompting.
5654
LD HL,5B54H 21 54 5B
Point Register Pair HL at the message at 5B54H, Filespec? .
5657
INC C 0C
INCrement Register C by 1.
5658
DEC C 0D
DECrement Register C by 1. The pair of instructions sets the flags from the LOAD or FILE selector without destroying it.
5659
If the Z FLAG (Zero) has been set, meaning the selector is zero and this is a FILE, JUMP to 565DH and prompt with Filespec? .
565B
LD L,4FH 2E 4F
Load Register L with 4FH, moving Register Pair HL back five bytes to 5B4FH, where the same message text begins with the word Load in front of it. One string serves both prompts.
565D
GOSUB to 58E8H, which writes the prompt on the status line and reads the operator's reply into 5BB4H. If the reply is abandoned that routine discards this routine's return address and the command never comes back here.
5660
LD DE,520AH 11 0A 52
Point Register Pair DE at 520AH, TED's File Control Block, which lives in the workspace below the image.
5663
GOSUB to the resident @FSPEC vector at 441CH, which parses the filespec addressed by Register Pair HL into the File Control Block addressed by Register Pair DE.
5666
LD HL,5AE2H 21 E2 5A
Point Register Pair HL at 5AE2H, the three characters TXT.
5669
GOSUB to the resident @FEXT vector at 4473H, which gives the file in the File Control Block the extension addressed by Register Pair HL if the operator did not type one. This is the manual's "if the extension is omitted from your entry, /TXT will be automatically provided".
566C
LD HL,5D00H 21 00 5D
Point Register Pair HL at 5D00H, the first byte of the text buffer. For a FILE this is where the data to be written starts and it becomes the File Control Block's buffer address.
566F
LD B,00H 06 00
Load Register B with the LOAD or FILE selector held in this instruction's own operand at 5670H.
5671
INC B 04
INCrement Register B by 1.
5672
DEC B 05
DECrement Register B by 1, setting the flags from the selector without changing it.
5673
LD B,00H 06 00
Load Register B with 0. The DOS takes a logical record length of zero as 256 bytes, which is what makes every transfer a whole sector.
5675
If the NZ FLAG (Not Zero) has been set, meaning the selector was 0AFH and this is a LOAD, JUMP to 569DH.
5677
GOSUB to the resident @INIT vector at 4420H, which opens the file named in the File Control Block addressed by Register Pair DE for output, creating it if it does not exist, with the buffer at the address in Register Pair HL and the record length in Register B.
567A
If the NZ FLAG (Not Zero) has been set, meaning the DOS returned an error code in Register A, JUMP to 56A5H to report it.
567C
PUSH DE D5
Save Register Pair DE, the File Control Block address, onto the stack.
567D
LD DE,5D00H 11 00 5D
Point Register Pair DE at 5D00H, the start of the text.
5680
GOSUB to 5A04H, which loads Register Pair HL from the end-of-text pointer at 58C2H, subtracts 5D00H and leaves the length of the text in Register Pair BC and in Register Pair HL.
5683
POP DE D1
Restore Register Pair DE, the File Control Block address.
5684
If the Z FLAG (Zero) has been set, meaning the text buffer is empty, JUMP to 5699H to close the file again; an empty file is written.
5686
LD C,L 4D
Load Register C with Register L, the low byte of the text length, which is the number of bytes in the last, partly filled record.
5687
INC C 0C
INCrement Register C by 1.
5688
DEC C 0D
DECrement Register C by 1, setting the flags from that byte count without changing it.
5689
If the Z FLAG (Zero) has been set, meaning the text is an exact multiple of 256 bytes, JUMP to 568CH; Register B already holds the right number of records.
568B
INC B 04
INCrement Register B by 1 to account for the partly filled last record.

Loop Start
One 256-byte record is written on each pass and the File Control Block's buffer address is walked up the text buffer a page at a time.

568C
GOSUB to the resident @WRITE vector at 4439H, which writes the next record of the file addressed by Register Pair DE from the buffer address held in the File Control Block.
568F
If the NZ FLAG (Not Zero) has been set, meaning a disk error, JUMP to 56A5H to report it.
5691
INC (IX+0EH) DD 34 0E
INCrement 520EH by 1, the high byte of the buffer address inside the File Control Block. Adding one to the high byte alone advances the buffer by exactly 256 bytes, so the next record is taken from the next page of the text.

Loop End

5694
DECrement Register B and LOOP BACK to 568CH until every record has been written.
5696
LD (IX+12H),C DD 71 12
Store Register C, the number of bytes in the last record, into 5212H, the end-of-file byte field of the File Control Block. That is what tells the DOS how much of the final sector is real text.
5699
GOSUB to the resident @CLOSE vector at 4428H, which finalises the directory entry of the file addressed by Register Pair DE and releases any granules that were allocated but not used.
569C
RET C9
RETurn to the main loop.
569D
LD HL,(58C2H) 2A C2 58
Load Register Pair HL with the end-of-text pointer from 58C2H. A LOAD reads into the buffer starting at the current end of the text, which is what appends the file to whatever is already there rather than replacing it.
56A0
GOSUB to the resident @OPEN vector at 4424H, which opens the existing file named in the File Control Block addressed by Register Pair DE with the buffer at Register Pair HL and the record length in Register B.
56A3
If the Z FLAG (Zero) has been set, meaning the file opened, JUMP to 56C3H to check that it will fit.

56A5H - Reporting a Disk Error

Every failure from @INIT, @OPEN, @WRITE and @READ ends up here. Rather than let the DOS display the error, which would destroy the editing screen, TED has SYS4/SYS build the message text into a buffer and then puts it on the status line in its own format. The message SYS4 returns is terminated by a carriage return, so the last character has to be given bit 7 to match the convention the status line writer at 590AH uses.

56A5
PUSH DE D5
Save Register Pair DE, the File Control Block address, onto the stack; the caller needs it back.
56A6
LD DE,5BCCH 11 CC 5B
Point Register Pair DE at 5BCCH, the 256-byte buffer TED keeps above its image for the returned error text.
56A9
PUSH DE D5
Save that buffer address onto the stack as well.
56AA
OR 0C0H F6 C0
OR Register A, the DOS error code, with 0C0H. Bit 6 asks @ERROR for the message on its own, without the *** Error code = framing, and bit 7 asks it to return to the caller instead of aborting the program.
56AC
LD HL,4758H 21 58 47
Point Register Pair HL at 4758H, the SYS1 status byte held in one of the spare bytes of the resident disk primitive table.
56AF
SET 7,(HL) CB FE
Set bit 7 of the SYS1 status byte at 4758H, which tells SYS4/SYS to build the message but not display it. The bit is a one-shot and SYS4 clears it again.
56B1
GOSUB to the resident @ERROR vector at 4409H, which loads SYS4/SYS and turns the error code in Register A into English text.
56B4
POP HL E1
Restore the buffer address 5BCCH into Register Pair HL.
56B5
LD A,0DH 3E 0D
Load Register A with 0DH, the carriage return that ends the message SYS4 built.
56B7
LD BC,0100H 01 00 01
Load Register Pair BC with 0100H, 256, the size of the message buffer and so the limit on the search.
56BA
CPIR ED B1
Search forward through the message buffer for that carriage return. Register Pair HL is left one byte past it.
56BC
DEC HL 2B
DECrement Register Pair HL by 1, back onto the carriage return itself.
56BD
DEC HL 2B
DECrement Register Pair HL by 1 again, onto the last character of the message text.
56BE
SET 7,(HL) CB FE
Set bit 7 of that last character. The status line writer at 590AH stops on the first character with bit 7 set, so this converts the DOS message into TED's own string format in place.
56C0
POP DE D1
Restore Register Pair DE, the File Control Block address, from the stack.
56C1
JUMP to 56DCH, which points Register Pair HL at the message buffer and queues it for the status line.

56C3H - LOAD: the Room Test and the Read Loop

The size of the file is known before a byte is read, because the directory gives the ending record number and the end-of-file byte offset, so TED can refuse a file that will not fit without disturbing the text already in the buffer. That is the manual's "if the file is too large to fit into the available text buffer, the error message No room! will be displayed and no text will be loaded".

56C3
LD HL,(58C5H) 2A C5 58
Load Register Pair HL with the text buffer ceiling from 58C5H, which is HIGH$ as it stood when TED started.
56C6
LD BC,(58C2H) ED 4B C2 58
Load Register Pair BC with the end-of-text pointer from 58C2H.
56CA
SBC HL,BC ED 42
SUBtract Register Pair BC, the current end of the text, from Register Pair HL, the ceiling. What is left is the number of bytes still free in the buffer.
56CC
LD C,(IX+12H) DD 4E 12
Load Register C with the end-of-file byte offset from 5212H, which is FCB+08H and holds how many bytes of the file's last record are real.
56CF
LD B,(IX+16H) DD 46 16
Load Register B with the ending record number from 5216H, which is FCB+0CH. Register Pair BC now holds the size of the file in bytes, because the record length is 256.
56D2
SBC HL,BC ED 42
SUBtract the file size in Register Pair BC from the free space in Register Pair HL.
56D4
If the CARRY FLAG has been set, meaning the file is larger than the space left, JUMP to 56D8H to refuse it.
56D6
If the NZ FLAG (Not Zero) has been set, meaning there is space to spare, JUMP to 56E7H and read the file in.
56D8
LD HL,5B33H 21 33 5B
Point Register Pair HL at the message at 5B33H, No room!.
56DB
LD IY,5BCCH FD 21 CC 5B
Point Register Pair IY at 5BCCH. On this path the instruction is only a carrier for its operand; Register Pair HL, which holds the message address, is not touched.
56DC
LD HL,5BCCH 21 CC 5B
Point Register Pair HL at 5BCCH, the buffer holding the DOS error text. This is the entry the error reporter jumps to from 56C1H, one byte into the instruction above.
56DF
GOSUB to 532FH, which stores the message address at 5208H and asks the main loop to display it.
56E2
LD A,(DE) 1A
Load Register A with the first byte of the File Control Block at 520AH, addressed by Register Pair DE. Bit 7 of that byte is the DOS's open-file flag.
56E3
RLCA 07
Rotate Register A left, moving bit 7, the open-file flag, into the CARRY FLAG.
56E4
If the CARRY FLAG has been set, meaning the file is still open, JUMP to 5699H to close it before returning.
56E6
RET C9
RETurn to the main loop; the file was never opened.

Loop Start
Register B holds the ending record number from the directory, so the loop reads exactly as many records as the file contains.

56E7
GOSUB to the resident @READ vector at 4436H, which reads the next record of the file addressed by Register Pair DE into the buffer address held in the File Control Block.
56EA
If the NZ FLAG (Not Zero) has been set, meaning the DOS returned an error, JUMP to 570CH, which reports it and keeps the text read so far.
56EC
LD H,(IX+0EH) DD 66 0E
Load Register H with the high byte of the buffer address from 520EH, which is FCB+04H. That is the page the record just read went into, and it is needed after the loop to work out where the text now ends.
56EF
INC (IX+0EH) DD 34 0E
INCrement 520EH by 1, advancing the File Control Block's buffer address by one page so that the next record lands after this one.

Loop End

56F2
DECrement Register B and LOOP BACK to 56E7H until every record has been read.
56F4
LD A,C 79
Load Register A with Register C, the end-of-file byte offset taken from the directory at 56CCH.
56F5
OR A B7
Test Register A, that offset, against zero.
56F6
If the NZ FLAG (Not Zero) has been set, meaning the last record was only partly used, JUMP to 56F9H.
56F8
INC H 24
INCrement Register H by 1. The file filled its last record exactly, so the text now runs to the start of the page after the one the last record went into.
56F9
LD A,(58C2H) 3A C2 58
Load Register A with the low byte of the end-of-text pointer at 58C2H, which is the offset within the page at which the read started.
56FC
ADD A,C 81
ADD Register C, the end-of-file byte offset, to Register A.
56FD
LD L,A 6F
Load Register L with that sum, so that Register Pair HL now addresses the byte after the last character read.
56FE
If the NO CARRY FLAG has been set, meaning the addition did not cross a page boundary, JUMP to 5701H.
5700
INC H 24
INCrement Register H by 1 to carry the addition into the high byte of the address.
5701
LD (HL),00H 36 00
Store a zero byte at that address, terminating the text.
5703
LD (58C2H),HL 22 C2 58
Store the address of that terminator into 58C2H, the end-of-text pointer.
5706
GOSUB to the resident @CLOSE vector at 4428H to close the file.
5709
JUMP to 53FEH, which redraws the whole screen from the top of the text and puts the cursor at row 0 column 0.
570C
GOSUB to 56A5H, which has the DOS build the error message and queues it for the status line.
570F
LD HL,(520DH) 2A 0D 52
Load Register Pair HL with the File Control Block's buffer address from 520DH, which is the page the failed read was aimed at.
5712
DEC H 25
DECrement Register H by 1, backing the pointer up one 256-byte page, so that the record before the failure is dropped along with the one that failed.
5713
JUMP to 5701H, which writes the terminating zero at that address and records it as the new end of the text. Everything read before that point is kept.

5715H - CTRL Q: Directory Query

Puts a disk directory on the screen through the DOS @DODIR service and then rebuilds the editing display. The reply may be a bare drive number, a drive number preceded by a colon, or a slash and a three character extension mask followed by the drive, which is what selects @DODIR's masked function code 2.

5715
LD HL,5B20H 21 20 5B
Point Register Pair HL at the message at 5B20H, Drive? .
5718
GOSUB to 58E8H, which puts that prompt on the status line and reads the reply into the buffer at 5BB4H, returning Register Pair HL addressing it.
571B
LD B,00H 06 00
Load Register B with 0, @DODIR's function code for the plain four-across directory display.
571D
LD A,(HL) 7E
Load Register A with the first character of the reply.
571E
CP 2FH FE 2F
Compare Register A against 2FH, ASCII /.
5720
If the NZ FLAG (Not Zero) has been set, meaning no extension mask was given, JUMP to 572BH.
5722
INC HL 23
INCrement Register Pair HL by 1, past the slash and onto the first character of the three character extension mask.
5723
LD B,02H 06 02
Load Register B with 2, @DODIR's function code for a display restricted to files whose extension matches the mask in the first three bytes of the buffer.
5725
LD DE,0003H 11 03 00
Load Register Pair DE with 3, the length of the mask.
5728
EX DE,HL EB
Exchange Register Pair DE with Register Pair HL, so that Register Pair HL holds 3 and Register Pair DE addresses the mask.
5729
ADD HL,DE 19
ADD Register Pair DE to Register Pair HL, giving the address of the character that follows the mask.
572A
LD A,(HL) 7E
Load Register A with that character, which should be the colon before the drive number.
572B
EX DE,HL EB
Exchange Register Pair DE with Register Pair HL. On the mask path this leaves Register Pair HL addressing the three character mask, which is what @DODIR function 2 expects; on the plain path @DODIR function 0 writes straight to the screen and does not use the buffer address at all.
572C
CP 3AH FE 3A
Compare Register A against 3AH, ASCII :.
572E
INC DE 13
INCrement Register Pair DE by 1 so that it addresses the character after the one just tested.
572F
If the NZ FLAG (Not Zero) has been set, meaning the character was not a colon, JUMP to 5732H and take it as the drive digit itself.
5731
LD A,(DE) 1A
Load Register A with the character after the colon, which is the drive digit.
5732
SUB 30H D6 30
SUBtract 30H, ASCII 0, from Register A, converting the digit into a drive number.
5734
CP 08H FE 08
Compare Register A against 8. There are eight possible LDOS drives, 0 to 7.
5736
RET NC D0
If the NO CARRY FLAG has been set, meaning the character was not a digit in that range, RETurn to the main loop and ignore the command.
5737
LD C,A 4F
Load Register C with the drive number, which is where @DODIR expects it.
5738
PUSH HL E5
Save Register Pair HL, the buffer address, onto the stack.
5739
PUSH BC C5
Save Register Pair BC, the function code and drive number, onto the stack.
573A
GOSUB to the Level II ROM CLS routine at 01C9H, clearing the screen for the directory listing.
573D
POP BC C1
Restore Register Pair BC, the function code and drive number.
573E
POP HL E1
Restore Register Pair HL, the buffer address.
573F
GOSUB to the resident @DODIR vector at 4463H, which loads SYS12/SYS and writes the directory of the drive in Register C to the screen.
5742
LD HL,1700H 21 00 17
Load Register Pair HL with 1700H, row 17H and column 0. The video address routine at 5A9DH folds that to 3DC0H, the first column of video row 7, and that is where the cursor blinks while the listing is being read.
5745
GOSUB to 5751H, which blinks the cursor there until the operator presses a key.
5748
GOSUB to 5AADH, which rules video line 14 off with underscores and blanks the status line, undoing the damage the directory listing did to the editing screen furniture.
574B
LD HL,(5B96H) 2A 96 5B
Load Register Pair HL with the text address of the top display row from the line table entry at 5B96H.
574E
JUMP to 57B6H, which redraws all fourteen text lines from that address, restoring the screen exactly as it was.

5751H - Blinking the Cursor and Waiting for a Key

The only place TED reads the keyboard. The character the cursor is sitting on and the cursor character from 5205H are written alternately over the same screen cell, and the keyboard is scanned 180 times between each change, which is what produces the blink. The exit is unusual: when a key is found, 5773H throws away the return address of the inner call so that the routine falls into the character writer at 5775H and returns straight to whoever called 5751H, with the real character restored on the screen and the key in Register A.

5751
GOSUB to 577FH, which reads back the character the cursor is standing on and returns it in Register A.
5754
LD C,A 4F
Load Register C with that character. It is held there for the whole of the blink and is what gets put back on the screen when a key is pressed.

Loop Start
Each pass writes the cursor character, scans the keyboard, writes the real character back, and scans again.

5755
LD A,(IX+05H) DD 7E 05
Load Register A with the current cursor character from 5205H, which is the underscore in overstrike mode and the solid block in insert mode.
5758
GOSUB to 5761H, which writes that character on the screen and scans the keyboard 180 times.
575B
LD A,C 79
Load Register A with Register C, the real character that belongs under the cursor.
575C
GOSUB to 5761H again, which puts the real character back and scans the keyboard another 180 times.

Loop End

575F
LOOP BACK to 5755H. Nothing ends this loop from the inside; it is left by 5773H when a key is found.
5761
PUSH BC C5
Save Register Pair BC onto the stack, protecting the character under the cursor held in Register C.
5762
LD C,A 4F
Load Register C with the character to be shown, which the caller put in Register A.
5763
GOSUB to 5775H, which converts the character in Register C to its display form and writes it at the cursor position.
5766
LD B,0B4H 06 B4
Load Register B with 0B4H, 180, the number of keyboard scans that make up one half of the blink.

Loop Start
The keyboard is scanned once per pass; the loop is also what times the blink.

5768
PUSH DE D5
Save Register Pair DE onto the stack; the ROM keyboard driver does not preserve it.
5769
GOSUB to the Level II ROM $KBD routine at 002BH, which makes one pass over the keyboard matrix and returns the character in Register A, or zero and the Z FLAG set if no key is down.
576C
POP DE D1
Restore Register Pair DE from the stack.
576D
If the NZ FLAG (Not Zero) has been set, meaning a key is down, JUMP to 5773H to leave the blink.

Loop End

576F
DECrement Register B and LOOP BACK to 5768H until the 180 scans are done.
5771
POP BC C1
Restore Register Pair BC from the stack, bringing back the character under the cursor in Register C.
5772
RET C9
RETurn to the blink loop for the other half of the cycle.
5773
POP BC C1
Restore Register Pair BC from the stack, bringing back the character under the cursor in Register C.
5774
POP DE D1
Restore the return address of the call from 5758H or 575CH into Register Pair DE and discard it. The blink loop is abandoned without unwinding, and the next instruction's RETurn will go all the way back to the caller of 5751H.
5775
PUSH AF F5
Save Register A, which holds the key just pressed, and the flags that the keyboard scan left, onto the stack. This is also the entry used by 54A3H, 5763H, 5A48H and 5A62H simply to display the character in Register C.
5776
LD A,C 79
Load Register A with Register C, the character to be displayed.
5777
GOSUB to 578BH, which converts the character in Register A into the form it is shown in and returns it in Register C.
577A
GOSUB to 5783H, which writes that display character on the screen at the cursor position.
577D
POP AF F1
Restore Register A, the key that was pressed, and the flags from the keyboard scan.
577E
RET C9
RETurn.

577FH - The Two Cursor Cell Primitives

Two three-byte stubs and the wrapper they share. Both go to the cursor primitive dispatcher at 5A71H, which works out the video address from the row and column in Register Pair HL; the only difference is the function code, 1 to read the cell and 2 to write it.

577F
LD B,01H 06 01
Load Register B with 1, the function code that reads the character at the cursor position into Register A.
5781
JUMP to the wrapper at 5785H.
5783
LD B,02H 06 02
Load Register B with 2, the function code that writes the character in Register C at the cursor position.
5785
PUSH DE D5
Save Register Pair DE onto the stack. The primitive builds the video address in Register Pair DE and every caller wants its own value back.
5786
GOSUB to 5A71H, the cursor primitive dispatcher, which takes the function code in Register B and the row and column in Register Pair HL.
5789
POP DE D1
Restore Register Pair DE from the stack.
578A
RET C9
RETurn.

578BH - Display-Form Translation

Three characters in the text are not shown as themselves. A newline appears as the small graphics block 84H, the block begin marker 0FEH as the graphic left bracket 0B7H and the block end marker 0FFH as the graphic right bracket 0BBH. Everything else is displayed unchanged. The CARRY FLAG set on the newline is used by the line display routine at 5819H to decide that the line has ended.

578B
LD C,84H 0E 84
Load Register C with 84H, the small graphics block that stands for a newline on the screen.
578D
CP 0DH FE 0D
Compare Register A, the text character, against 0DH, a newline.
578F
SCF 37
Set the CARRY FLAG. It does not disturb the Z FLAG left by the comparison, and it is how the caller is told that this character ends a line.
5790
RET Z C8
If the Z FLAG (Zero) has been set, meaning the character is a newline, RETurn with the graphics block in Register C and the CARRY FLAG set.
5791
LD C,0B7H 0E B7
Load Register C with 0B7H, the graphic left bracket that stands for a block begin marker.
5793
CP 0FEH FE FE
Compare Register A against 0FEH, the block begin marker byte.
5795
RET Z C8
If the Z FLAG (Zero) has been set, RETurn with the left bracket in Register C. The comparison matched exactly, so the CARRY FLAG is clear.
5796
LD C,0BBH 0E BB
Load Register C with 0BBH, the graphic right bracket that stands for a block end marker.
5798
CP 0FFH FE FF
Compare Register A against 0FFH, the block end marker byte.
579A
RET Z C8
If the Z FLAG (Zero) has been set, RETurn with the right bracket in Register C and the CARRY FLAG clear.
579B
LD C,A 4F
Load Register C with Register A; an ordinary character is displayed as itself.
579C
RET C9
RETurn. The flags are those of the comparison against 0FFH, so the NZ FLAG is set and the CARRY FLAG is set for every character below 0FFH.

579DH - Comparing the Search String Against the Text

A plain byte for byte comparison of the search string held at 522AH against the text at the address in Register Pair HL. The Z FLAG on return means the whole string matched.

579D
LD B,(IX+06H) DD 46 06
Load Register B with the search string length from 5206H, the number of characters to compare.
57A0
LD DE,522AH 11 2A 52
Point Register Pair DE at 522AH, the first character of the search string.

Loop Start
One character of the search string is compared on each pass.

57A3
LD A,(DE) 1A
Load Register A with the search string character addressed by Register Pair DE.
57A4
CP (HL) BE
Compare Register A against the text character addressed by Register Pair HL.
57A5
RET NZ C0
If the NZ FLAG (Not Zero) has been set, meaning the two characters differ, RETurn; this position does not match.
57A6
INC HL 23
INCrement Register Pair HL by 1 to the next text character.
57A7
INC DE 13
INCrement Register Pair DE by 1 to the next search string character.

Loop End

57A8
DECrement Register B and LOOP BACK to 57A3H until every character has been compared.
57AA
RET C9
RETurn with the Z FLAG set from the last comparison; the whole string matched.

57ABH - Scrolling the Display Down One Line

Works out the display line before the one currently at the top of the screen and redraws from it. The four bytes at 57B2H are the last of the module's overlaps: read from 57B2H they load Register Pair IY and do nothing that matters, while entering at 57B3H makes the same operand load Register Pair HL with the line table entry for row 1, which is what scrolls the display the other way.

57AB
LD DE,(5B96H) ED 5B 96 5B
Load Register Pair DE with the text address of the top display row from the line table entry at 5B96H.
57AF
GOSUB to 5844H, which returns in Register Pair HL the text address of the display line before it.
57B2
LD IY,(5B98H) FD 2A 98 5B
Load Register Pair IY from 5B98H. On the fall-through path this is a harmless instruction; Register Pair HL, which carries the line address, is untouched.
57B3
LD HL,(5B98H) 2A 98 5B
Load Register Pair HL from 5B98H, the line table entry for row 1, so that the redraw below starts one line further down the text. This is the entry the cursor down handler uses at 5535H to scroll the display up.

57B6H - The Screen Refresh

Redraws the fourteen line text area from the text address in Register Pair HL and rebuilds the line table at 5B96H as it goes, so that after a redraw every row of the screen has a known text address. 57B6H starts at row 0; 57B7H is the entry that starts at the row number in Register A, which is what makes a change near the bottom of the screen cheap.

The line table is fourteen two-byte entries at 5B96H through 5BB1H, one per text row. A row that lies past the end of the text gets a zero entry, which is what the page down command tests at 563DH. Register Pair IY is set to 5B94H, one entry below the table, because the stepping loop always runs at least once.

57B6
XOR A AF
Set Register A to ZERO and clear all flags, selecting row 0 as the first row to rebuild. This is the full-screen entry point.
57B7
PUSH HL E5
Save Register Pair HL, the text address the first rebuilt row is to start at, onto the stack. This is the entry point used when Register A already names the first row to rebuild.
57B8
LD DE,0040H 11 40 00
Load Register Pair DE with 40H, 64, the length of one video line and the amount to step for each row skipped.
57BB
LD HL,3BC0H 21 C0 3B
Point Register Pair HL at 3BC0H, one line above the top of the video screen.
57BE
LD IY,5B94H FD 21 94 5B
Point Register Pair IY at 5B94H, one entry below the start of the line table at 5B96H.
57C2
LD C,0FH 0E 0F
Load Register C with 0FH, fifteen, one more than the number of text rows, ready to be counted down as rows are skipped.

Loop Start
One row is stepped over on each pass. The loop always runs at least once, because Register A is decremented past zero to end it, which is why the pointers start one row and one entry low.

57C4
ADD HL,DE 19
ADD Register Pair DE, 64, to Register Pair HL, stepping the video address on by one line.
57C5
INC IY FD 23
INCrement Register Pair IY by 1.
57C7
INC IY FD 23
INCrement Register Pair IY by 1 again, stepping over one two-byte line table entry.
57C9
DEC C 0D
DECrement Register C by 1, one fewer row left to rebuild.
57CA
DEC A 3D
DECrement Register A by 1, the count of rows still to be skipped.

Loop End
On exit Register Pair HL holds the video address of the first row to be rebuilt, Register Pair IY its line table entry, and Register C the number of rows from there to the bottom of the text area.

57CB
If the S FLAG is clear, meaning Register A has not yet gone below zero, LOOP BACK to 57C4H.
57CE
EX DE,HL EB
Exchange Register Pair DE with Register Pair HL, putting the video address in Register Pair DE, which is where the line display routine expects it.
57CF
POP HL E1
Restore Register Pair HL from the stack, the text address the first row is to start at.

Loop Start
One text line is written to the screen on each pass and its starting text address is recorded in the line table.

57D0
LD (IY+00H),L FD 75 00
Store Register L, the low byte of this row's text address, into the line table entry addressed by Register Pair IY.
57D3
INC IY FD 23
INCrement Register Pair IY by 1.
57D5
LD (IY+00H),H FD 74 00
Store Register H, the high byte of the text address, into the next byte of the line table entry.
57D8
INC IY FD 23
INCrement Register Pair IY by 1, leaving it on the entry for the next row.
57DA
GOSUB to 580AH, which writes up to 64 characters of text from Register Pair HL to the video line at Register Pair DE, blanking the rest of the line and advancing both pointers. It returns Register A zero if it reached the end of the text.
57DD
OR A B7
Test Register A. Zero means the terminating byte of the text was reached on this line.
57DE
If the Z FLAG (Zero) has been set, meaning the text has run out, JUMP to 57EAH to zero the rest of the line table.
57E0
DEC C 0D
DECrement Register C by 1, one row of the text area written.

Loop End

57E1
LOOP BACK to 57D0H while rows remain.
57E3
LD HL,3C00H 21 00 3C
Point Register Pair HL at 3C00H, the top left corner of the video screen.
57E6
LD B,05H 06 05
Load Register B with 5. The cursor primitive at 5A71H implements function codes 1 to 4 only, so this call does nothing and simply returns; it is how the redraw ends.
57E8
JUMP to the wrapper at 5785H, which calls the primitive with Register Pair DE saved and then returns to the caller of the redraw.

Loop Start
One line table entry is zeroed on each pass. Register B is zero, left behind by the blanking loop inside 580AH.

57EA
LD (IY+00H),B FD 70 00
Store Register B, which is zero, into the line table entry addressed by Register Pair IY.
57ED
INC IY FD 23
INCrement Register Pair IY by 1.
57EF
LD (IY+00H),B FD 70 00
Store Register B, still zero, into the second byte of the entry.
57F2
INC IY FD 23
INCrement Register Pair IY by 1, on to the next entry.
57F4
DEC C 0D
DECrement Register C by 1, one fewer row left below the end of the text.

Loop End

57F5
LOOP BACK to 57EAH until every remaining line table entry is zero.
57F7
LD HL,3F80H 21 80 3F
Point Register Pair HL at 3F80H, the first cell of video line 14, which is the rule below the text area and so the end of the text display.
57FA
GOSUB to 5A07H, which subtracts Register Pair DE, the video address the display stopped at, from Register Pair HL and leaves the number of screen cells still to be blanked in Register Pair BC.
57FD
EX DE,HL EB
Exchange Register Pair DE with Register Pair HL, putting the video address to start blanking at into Register Pair HL.
57FE
If the Z FLAG (Zero) has been set, meaning the display already reaches the bottom of the text area, JUMP to 57E3H and finish.
5800
LD (HL),20H 36 20
Store 20H, a space, into the first cell to be blanked.
5802
DEC BC 0B
DECrement Register Pair BC by 1; that first cell is done.
5803
LD D,H 54
Load Register D with Register H.
5804
LD E,L 5D
Load Register E with Register L, copying the video address into Register Pair DE.
5805
INC DE 13
INCrement Register Pair DE by 1 so that the destination is one cell ahead of the source.
5806
LDIR ED B0
Block move the space through the rest of the text area, each cell copying itself into the one after it, which blanks everything below the last line of text.
5808
JUMP to 57E3H to finish.

580AH - Displaying One Text Line

Writes one video line of up to 64 characters from the text at Register Pair HL to the screen at Register Pair DE. It stops on a newline, on the terminating zero, or when the line is full, and blanks whatever is left of the line. Register A on return is zero only when the end of the text was reached, and that is also the moment the end-of-text pointer at 58C2H is refreshed.

580A
LD B,40H 06 40
Load Register B with 40H, 64, the number of cells in a video line.

Loop Start
One text character is placed on each pass.

580C
LD A,(HL) 7E
Load Register A with the text character addressed by Register Pair HL.
580D
OR A B7
Test Register A against zero.
580E
If the Z FLAG (Zero) has been set, meaning the terminating byte of the text has been reached, JUMP to 581FH to blank the rest of the line.
5810
INC HL 23
INCrement Register Pair HL by 1 to the next text character.
5811
PUSH BC C5
Save Register Pair BC, which holds the remaining cell count in Register B, onto the stack.
5812
GOSUB to 578BH, which returns the display form of the character in Register C and sets the CARRY FLAG if the character was a newline.
5815
LD A,C 79
Load Register A with the display form from Register C.
5816
POP BC C1
Restore Register Pair BC, the remaining cell count.
5817
If the NZ FLAG (Not Zero) has been set, meaning the character was not one of the three the translator recognises, JUMP to 582CH to store it and go round again.
5819
If the NO CARRY FLAG has been set, meaning the character was a block begin or end marker rather than a newline, JUMP to 582CH; those are ordinary characters as far as the line is concerned.
581B
LD (DE),A 12
Store the newline's graphics block into the video cell addressed by Register Pair DE. The line ends here.
581C
INC DE 13
INCrement Register Pair DE by 1 to the next video cell.
581D
DEC B 05
DECrement Register B by 1, one fewer cell left on the line.
581E
RET Z C8
If the Z FLAG (Zero) has been set, meaning the newline fell in the last cell of the line, RETurn. Register A holds the graphics block and is not zero, so the caller carries on with the next line.
581F
PUSH AF F5
Save Register A and the flags onto the stack. Register A is zero when the end of the text brought us here and the newline glyph when a newline did, and that is what the test at 5827H reads.
5820
LD A,20H 3E 20
Load Register A with 20H, a space.

Loop Start
The rest of the video line is blanked.

5822
LD (DE),A 12
Store a space into the video cell addressed by Register Pair DE.
5823
INC DE 13
INCrement Register Pair DE by 1.

Loop End

5824
DECrement Register B and LOOP BACK to 5822H until the line is full of spaces.
5826
POP AF F1
Restore Register A and the flags.
5827
RET NZ C0
If the NZ FLAG (Not Zero) has been set, meaning the line ended on a newline rather than at the end of the text, RETurn.
5828
LD (58C2H),HL 22 C2 58
Store Register Pair HL into 58C2H, the end-of-text pointer. Register Pair HL is standing on the terminating zero byte, so every redraw that reaches the end of the text re-establishes the pointer. This is what gives it its value in the first place, because the image loads with 0000H in the operand field at 58C2H.
582B
RET C9
RETurn with Register A zero, telling the redraw that the text has run out.
582C
LD (DE),A 12
Store the display character in Register A into the video cell addressed by Register Pair DE.
582D
INC DE 13
INCrement Register Pair DE by 1 to the next video cell.

Loop End

582E
DECrement Register B and LOOP BACK to 580CH until the 64 cells of the line are used up.
5830
RET C9
RETurn with Register A holding the last character placed, which cannot be zero, so the redraw carries on with the next line.

5831H - An Uncalled Video Address Converter

Nineteen bytes that form a complete subroutine which nothing in the module calls: no instruction anywhere in the image names 5831H, and the instruction before it is a RETurn. It would convert a video address in Register Pair HL into a row in Register H and a column plus one in Register L. It loads the divisor into Register C, while the resident @DIV routine at 44C4H takes the dividend in Register Pair HL and the divisor in Register A.

5831
PUSH DE D5
Save Register Pair DE onto the stack.
5832
PUSH BC C5
Save Register Pair BC onto the stack.
5833
LD HL,3C00H 21 00 3C
Point Register Pair HL at 3C00H, the top left corner of the video screen.
5836
EX DE,HL EB
Exchange Register Pair DE with Register Pair HL, so that Register Pair HL holds the video address supplied by the caller and Register Pair DE the screen base.
5837
SBC HL,DE ED 52
SUBtract the screen base from the video address, giving the offset into the screen.
5839
LD C,40H 0E 40
Load Register C with 40H, 64, the number of cells in a video line.
583B
CALL 44C4H CD C4 44
GOSUB to the resident @DIV vector at 44C4H, which divides Register Pair HL by Register A and returns the quotient in Register Pair HL and the remainder in Register A.
583E
LD H,L 65
Load Register H with Register L, the low byte of the quotient, which would be the video row.
583F
LD L,A 6F
Load Register L with Register A, the remainder, which would be the column.
5840
INC L 2C
INCrement Register L by 1.
5841
POP BC C1
Restore Register Pair BC from the stack.
5842
POP DE D1
Restore Register Pair DE from the stack.
5843
RET C9
RETurn.

5844H - Finding the Previous Display Line

Given the text address of the first character of a display line in Register Pair DE, return in Register Pair HL the text address of the display line before it. There are two cases. If the byte just below the line is not a newline then the line is the continuation of a logical line that wrapped, and the previous display line begins exactly 64 characters earlier. If it is a newline then the previous logical line ends there and its last display line has to be found by walking back to the newline before that, which is what 5852H does.

5844
LD HL,0FFC0H 21 C0 FF
Load Register Pair HL with 0FFC0H, which is minus 64 in sixteen bit arithmetic.
5847
ADD HL,DE 19
ADD Register Pair DE, the address of the start of the current display line, to Register Pair HL, giving the address 64 characters earlier. That is the answer for a wrapped line.
5848
DEC DE 1B
DECrement Register Pair DE by 1 so that it addresses the character immediately before the line.
5849
LD A,(DE) 1A
Load Register A with that character.
584A
CP 0DH FE 0D
Compare Register A against 0DH, a newline.
584C
RET NZ C0
If the NZ FLAG (Not Zero) has been set, meaning the line is the continuation of a wrapped logical line, RETurn with the answer already in Register Pair HL.
584D
GOSUB to 5852H, which walks back from the newline in Register Pair DE to the start of the display line that newline falls on and returns it in Register Pair DE. 557FH also enters here directly to find the start of the last line of the text.
5850
EX DE,HL EB
Exchange Register Pair DE with Register Pair HL so that the answer is returned in Register Pair HL.
5851
RET C9
RETurn.

5852H - Finding the Start of a Display Line

Rounds the text address in Register Pair DE down to the first character of the display line it falls on. A logical line runs from one newline to the next and is broken into display lines every 64 characters, so the routine counts backwards to the previous newline, or to the start of the text, and then discards whole multiples of 64 from that count.

The count is kept as a negative number in Register Pair BC, and the multiple of 64 is stripped by adding 64 repeatedly until the value goes positive rather than by dividing, which on a Z80 is both shorter and faster for the short counts involved.

5852
PUSH HL E5
Save Register Pair HL onto the stack; the caller gets it back unchanged.
5853
PUSH DE D5
Save Register Pair DE, the address that is to be rounded down, onto the stack.
5854
LD BC,0FFFFH 01 FF FF
Load Register Pair BC with 0FFFFH, minus one, the running count of characters walked back.

Loop Start
One character is walked back on each pass.

5857
LD HL,5D00H 21 00 5D
Point Register Pair HL at 5D00H, the first byte of the text buffer.
585A
OR A B7
Clear the CARRY FLAG ready for the subtraction.
585B
SBC HL,DE ED 52
SUBtract Register Pair DE, the address reached so far, from 5D00H. The Z FLAG is set when the walk has reached the start of the text.
585D
If the Z FLAG (Zero) has been set, meaning the start of the text has been reached, JUMP to 5868H with the count as it stands.
585F
DEC DE 1B
DECrement Register Pair DE by 1, stepping back one character.
5860
LD A,(DE) 1A
Load Register A with the character there.
5861
CP 0DH FE 0D
Compare Register A against 0DH, a newline.
5863
If the Z FLAG (Zero) has been set, meaning the previous newline has been found, JUMP to 5868H.
5865
DEC BC 0B
DECrement Register Pair BC by 1, one more character between the newline and the address that was supplied.

Loop End

5866
LOOP BACK to 5857H for the next character.
5868
LD H,B 60
Load Register H with Register B.
5869
LD L,C 69
Load Register L with Register C, moving the negative character count into Register Pair HL.
586A
LD BC,0040H 01 40 00
Load Register Pair BC with 40H, 64, the width of a display line.

Loop Start
64 is added until the count goes positive, which strips the whole display lines out of it.

586D
ADD HL,BC 09
ADD Register Pair BC, 64, to Register Pair HL.

Loop End

586E
LOOP BACK to 586DH while the addition does not carry, meaning the count is still negative.
5870
OR A B7
Clear the CARRY FLAG ready for the subtraction.
5871
SBC HL,BC ED 42
SUBtract 64 from Register Pair HL, undoing the addition that pushed it positive. What is left is minus the offset of the address within its display line, less one.
5873
POP DE D1
Restore Register Pair DE from the stack, the address that was supplied.
5874
INC DE 13
INCrement Register Pair DE by 1 to cancel the extra one that the count started with.
5875
ADD HL,DE 19
ADD Register Pair DE to Register Pair HL, giving the address of the first character of the display line.
5876
EX DE,HL EB
Exchange Register Pair DE with Register Pair HL so that the answer is returned in Register Pair DE.
5877
POP HL E1
Restore Register Pair HL from the stack.
5878
RET C9
RETurn.

5879H - Measuring the Current Display Line

Returns in Register A the number of characters on the display line the cursor row names, and in Register Pair DE the address of the character that ended it. The count stops at 63, so the highest column the cursor can be clamped to is 63 and a line that fills the screen width reports 63 with Register Pair DE addressing its sixty-fourth character.

5879
PUSH HL E5
Save Register Pair HL, the cursor row and column, onto the stack.
587A
LD L,00H 2E 00
Load Register L with 0, column 0, so that the address worked out below is the start of the line rather than the cursor position.
587C
GOSUB to 5892H, which looks the row in Register H up in the line table and returns the text address of that column in Register Pair DE.
587F
LD C,3FH 0E 3F
Load Register C with 3FH, 63, the number of characters to count before giving up.

Loop Start
One character is examined on each pass.

5881
LD A,(DE) 1A
Load Register A with the text character addressed by Register Pair DE.
5882
OR A B7
Test Register A against zero.
5883
If the Z FLAG (Zero) has been set, meaning the end of the text, JUMP to 588DH.
5885
CP 0DH FE 0D
Compare Register A against 0DH, a newline.
5887
If the Z FLAG (Zero) has been set, meaning the line ends here, JUMP to 588DH.
5889
INC DE 13
INCrement Register Pair DE by 1 to the next text character.
588A
DEC C 0D
DECrement Register C by 1, one fewer character left to count.

Loop End

588B
LOOP BACK to 5881H unless 63 characters have been counted.
588D
LD A,3FH 3E 3F
Load Register A with 3FH, 63.
588F
SUB C 91
SUBtract Register C, the unused part of the count, from Register A, leaving the number of characters actually on the line.
5890
POP HL E1
Restore Register Pair HL, the cursor row and column, from the stack.
5891
RET C9
RETurn.

5892H - Text Address from Row and Column

Turns a cursor row in Register H and column in Register L into the text address of that screen position, using the line table at 5B96H. A row whose line table entry is zero lies past the end of the text; in that case the entry for the row above is used and 64 is added, which puts the address just past the last line of text and lets the cursor be moved into the blank area below it.

5892
GOSUB to 58ADH, which points Register Pair IY at the line table entry for the row in Register H and clears Register D to zero.
5895
LD H,D 62
Load Register H with Register D, which is zero, so that Register Pair HL now holds just the column.
5896
LD E,(IY+00H) FD 5E 00
Load Register E with the low byte of this row's text address from the line table.
5899
LD D,(IY+01H) FD 56 01
Load Register D with the high byte, so that Register Pair DE holds the text address of the first character of the row.
589C
LD A,D 7A
Load Register A with Register D, the high byte of that address.
589D
OR E B3
OR Register A with Register E, the low byte. The Z FLAG is set only if the whole line table entry is zero, meaning the row is past the end of the text.
589E
If the NZ FLAG (Not Zero) has been set, meaning the row has a real text address, JUMP to 58AAH.
58A0
LD DE,0040H 11 40 00
Load Register Pair DE with 40H, 64, the width of a display line.
58A3
ADD HL,DE 19
ADD 64 to Register Pair HL, the column, so that the address worked out below lands one whole line past the row above.
58A4
LD E,(IY-02H) FD 5E FE
Load Register E with the low byte of the previous row's line table entry.
58A7
LD D,(IY-01H) FD 56 FF
Load Register D with its high byte, so that Register Pair DE addresses the start of the last row that does have text on it.
58AA
ADD HL,DE 19
ADD Register Pair DE, the address of the start of the row, to Register Pair HL, the column, giving the text address of the position.
58AB
EX DE,HL EB
Exchange Register Pair DE with Register Pair HL so that the answer is returned in Register Pair DE.
58AC
RET C9
RETurn.

58ADH - Addressing a Line Table Entry

The line table at 5B96H is fourteen two-byte entries, one per text row, so the entry for a row is found by doubling the row number and adding it to the base. Register Pair IY is used because the callers want to reach the entry above and below as well as the entry itself.

58AD
LD E,H 5C
Load Register E with Register H, the cursor row.
58AE
LD D,00H 16 00
Load Register D with 0, clearing the high byte of the offset.
58B0
SLA E CB 23
Shift Register E left: multiply the row number by 2, because each line table entry is two bytes long.
58B2
LD IY,5B96H FD 21 96 5B
Point Register Pair IY at 5B96H, the first entry of the line table.
58B6
ADD IY,DE FD 19
ADD the doubled row number in Register Pair DE to Register Pair IY, leaving it on this row's entry.
58B8
RET C9
RETurn.

58B9H - Pointer Comparison and the Two Operand Cells

A sixteen bit comparison with four entry points. The two instructions at 58C1H and 58C4H are not really instructions that the module executes in sequence; their operand fields at 58C2H and 58C5H are the end-of-text pointer and the text buffer ceiling, written by the rest of the program and read back by executing the instruction that contains them. Calling 58C1H therefore compares the end of the text against the ceiling, which is the buffer-full test the insert code uses.

58B9
LD HL,(5B96H) 2A 96 5B
Load Register Pair HL with the text address of the top display row from the line table entry at 5B96H, and fall into the comparison against the start of the text.
58BC
LD DE,5D00H 11 00 5D
Load Register Pair DE with 5D00H, the first byte of the text buffer, ready to compare against Register Pair HL.
58BF
JUMP to the comparison at 58C7H.

Self-Modifying Code
58C2H and 58C5H are instruction operands used as variables. 58C2H is the address of the terminating zero byte of the text; it is written by 5480H, 5609H, 536FH, 54EFH, 5703H, 59A7H and, whenever a redraw reaches the end of the text, by 5828H. 58C5H is the text buffer ceiling, written once at 52D8H from HIGH$ 4049H. The image loads with 0000H in both fields.

58C1
LD DE,0000H 11 00 00
Load Register Pair DE with the end-of-text pointer held in this instruction's own operand at 58C2H, then fall into the instruction below and the comparison.
58C4
LD HL,0000H 21 00 00
Load Register Pair HL with the text buffer ceiling held in this instruction's own operand at 58C5H, which is HIGH$ as it stood when TED started, and fall into the comparison. 599DH calls this entry on its own to test a block copy for room.
58C7
LD A,H 7C
Load Register A with Register H, the high byte of the first operand.
58C8
SUB D 92
SUBtract Register D, the high byte of the second operand, from Register A.
58C9
RET NZ C0
If the NZ FLAG (Not Zero) has been set, meaning the high bytes differ, RETurn. The CARRY FLAG is set if the first address is the lower of the two.
58CA
LD A,L 7D
Load Register A with Register L, the low byte of the first operand.
58CB
SUB E 93
SUBtract Register E, the low byte of the second operand.
58CC
RET C9
RETurn with the Z FLAG set if the two addresses are equal and the CARRY FLAG set if the first is the lower.

58CDH - The Confirmation Prompt

Used by every command that throws text away. It puts Press <ENTER> to confirm on the status line and waits. Only ENTER lets the caller carry on; any other key makes the routine discard the caller's return address, so the command is abandoned without the caller having to test anything. That is the manual's "any other character entry (including a Y) will cause TED to ignore the request".

58CD
PUSH HL E5
Save Register Pair HL onto the stack; the caller gets it back unchanged.
58CE
PUSH DE D5
Save Register Pair DE onto the stack.
58CF
LD HL,5B5EH 21 5E 5B
Point Register Pair HL at the message at 5B5EH, Press <ENTER> to confirm.
58D2
GOSUB to 58DCH, which puts the message on the status line, waits for a key and returns it in Register A.
58D5
POP DE D1
Restore Register Pair DE from the stack.
58D6
POP HL E1
Restore Register Pair HL from the stack.
58D7
SUB 0DH D6 0D
SUBtract 0DH from Register A, the key that was pressed. The Z FLAG is set, and Register A is left at zero, only for ENTER.
58D9
RET Z C8
If the Z FLAG (Zero) has been set, meaning the operator confirmed, RETurn to the caller with Register A cleared to zero.
58DA
POP HL E1
Discard the caller's return address into Register Pair HL.
58DB
RET C9
RETurn, which now goes to whoever called the caller. The command has been abandoned.
58DC
GOSUB to 58F9H, which marks a message as being on the screen and writes the string addressed by Register Pair HL on the status line.
58DF
GOSUB to the Level II ROM $KEY routine at 0049H, which waits until a key is pressed and returns it in Register A without echoing it.
58E2
CP 0DH FE 0D
Compare Register A against 0DH, ENTER.
58E4
RET C9
RETurn with the Z FLAG set if that is what was pressed. 531AH calls this entry directly for the EXIT confirmation.

58E5H - Prompting for a Reply

Puts a message on the status line and reads a reply into the buffer at 5BB4H. 58E5H is the entry that supplies the String? message for the search and replace commands; 58E8H is the entry used when the caller has already chosen the message. An abandoned reply is dealt with the same way as an unconfirmed delete: the caller's return address is thrown away.

The 17H loaded into Register B here as a reply length limit never takes effect, because the input routine's first instruction, at 5A0DH, replaces it with Register C. Register C is left by the message writer at 590AH holding the message's own terminator byte, and since every prompt in the file ends with a space that has bit 7 set, the limit that is actually used is 0A0H, 160 characters.

58E5
LD HL,5B42H 21 42 5B
Point Register Pair HL at the message at 5B42H, String? .
58E8
GOSUB to 58F9H, which writes the message addressed by Register Pair HL on the status line and leaves its terminating byte in Register C.
58EB
LD HL,5BB4H 21 B4 5B
Point Register Pair HL at 5BB4H, the 24-byte reply buffer, which is also where a filespec taken from the command line is put.
58EE
LD B,17H 06 17
Load Register B with 17H, 23, as a reply length limit. The input routine overwrites Register B from Register C at 5A0DH before using it, so this value has no effect.
58F0
GOSUB to 5A0DH, which echoes and edits the reply into the buffer and returns Register Pair HL addressing it, Register B holding the number of characters typed, and the CARRY FLAG set if the operator abandoned the entry.
58F3
RET NC D0
If the NO CARRY FLAG has been set, meaning the reply was completed with ENTER, RETurn to the caller.
58F4
POP HL E1
Discard the caller's return address into Register Pair HL.
58F5
RET C9
RETurn, which goes to whoever called the caller and so abandons the whole command.

58F6H - Writing the Status Line

Video line 15 is the status line, and every message TED shows goes through here. 58F6H displays the message queued at 5208H, 58F9H displays the message addressed by Register Pair HL and records that a message is on the screen, and 58FFH puts the sign-on banner back and clears the mode flags. A message ends at the first character with bit 7 set, and the character 1EH sent before the message erases the rest of the line.

58F6
LD HL,(5208H) 2A 08 52
Load Register Pair HL with the queued message address from 5208H, where 532FH put it, and fall into the display below.
58F9
SET 7,(IX+04H) DD CB 04 FE
Set bit 7 of the mode flag byte at 5204H, which tells the main loop that a message is on the status line and that it should be cleared on the next keystroke.
58FD
JUMP to 590AH to display the message.
58FF
LD HL,5AE5H 21 E5 5A
Point Register Pair HL at 5AE5H, the sign-on banner TED 1.2 - (c) 1991 MISOSYS, Inc, which begins with the 0FH that turns the ROM cursor off.
5902
LD A,(IX+04H) DD 7E 04
Load Register A with the mode flag byte from 5204H.
5905
AND 01H E6 01
AND Register A with 1, keeping only the insert flag and clearing the DELETE, BLOCK and message bits. That is what makes Block and Delete last for one keystroke.
5907
LD (IX+04H),A DD 77 04
Store the reduced flag byte back into 5204H.
590A
PUSH DE D5
Save Register Pair DE onto the stack.
590B
PUSH HL E5
Save Register Pair HL, the message address, onto the stack.
590C
LD HL,0F00H 21 00 0F
Load Register Pair HL with 0F00H, row 15 and column 0, the first cell of the status line.
590F
LD B,03H 06 03
Load Register B with 3, the function code that sets the hardware cursor position.
5911
GOSUB to 5A71H, which converts that row and column into the video address 3FC0H and stores it at 4020H, so that the ROM display routine writes there.
5914
POP HL E1
Restore Register Pair HL, the message address.
5915
LD C,1EH 0E 1E
Load Register C with 1EH, the control code that erases the video line from the cursor to its end. It is sent as the first character so that whatever the previous message left behind is wiped.
5917
LD DE,234EH 11 4E 23
Load Register Pair DE with 234EH. Only the 11H opcode byte is executed here; the two operand bytes exist so that the loop below can be entered at 5918H and read them as instructions.
5918
LD C,(HL) 4E
Load Register C with the next character of the message, addressed by Register Pair HL. This is the loop entry that 5924H branches to, one byte into the instruction above.
5919
INC HL 23
INCrement Register Pair HL by 1 to the following character.

Loop Start
One character is displayed on each pass and the loop ends on the character that carries bit 7.

591A
PUSH BC C5
Save Register Pair BC onto the stack, keeping the character with its terminator bit intact.
591B
RES 7,C CB B9
Reset bit 7 of Register C, turning the terminator byte back into an ordinary displayable character.
591D
LD A,C 79
Load Register A with that character.
591E
GOSUB to the Level II ROM $DSP routine at 0033H, which writes the character in Register A at the cursor position and advances it.
5921
POP BC C1
Restore Register Pair BC, bringing back the character with bit 7 as it was.
5922
BIT 7,C CB 79
Test bit 7 of Register C. It is set only on the last character of a message.

Loop End
Register C is left holding the terminating character, which is what the prompt reply routine unwittingly takes as its length limit at 5A0DH.

5924
If the Z FLAG (Zero) has been set, meaning the message has not ended, LOOP BACK to 5918H for the next character.
5926
POP DE D1
Restore Register Pair DE from the stack.
5927
RET C9
RETurn.

5928H - The Guarded Block Move

A block move that does nothing when the count is zero, instead of moving 65536 bytes as the Z80 instruction would on its own. Every deletion in the file goes through it.

5928
LD A,B 78
Load Register A with Register B, the high byte of the count in Register Pair BC.
5929
OR C B1
OR Register A with Register C, the low byte. The Z FLAG is set only if the whole count is zero.
592A
RET Z C8
If the Z FLAG (Zero) has been set, meaning there is nothing to move, RETurn.
592B
LDIR ED B0
Block move Register Pair BC bytes from the address in Register Pair HL to the address in Register Pair DE, incrementing both pointers.
592D
RET C9
RETurn.

592EH - CTRL R: Replace

Key code 12H. The replacement string is prompted for and kept at 5242H with its length at 5207H, exactly as the search string is kept at 522AH with its length at 5206H. The search then starts one character below the cursor rather than one above it, so that the occurrence the cursor is sitting on, which is where a preceding SEARCH or FIND NEXT left it, is the one replaced.

592E
GOSUB to 58E5H, which puts String? on the status line and reads the replacement into the buffer at 5BB4H, returning the length in Register B.
5931
LD A,B 78
Load Register A with Register B, the length of the reply.
5932
OR A B7
Test Register A against zero.
5933
If the Z FLAG (Zero) has been set, meaning a bare ENTER was given, JUMP to 5940H and use the replacement string already held at 5242H.
5935
LD (IX+07H),B DD 70 07
Store Register B, the reply length, into 5207H, the replace string length.
5938
LD DE,5242H 11 42 52
Point Register Pair DE at 5242H, the replace string buffer in the workspace below the image.
593B
LD C,B 48
Load Register C with Register B, the reply length.
593C
LD B,00H 06 00
Load Register B with 0, making Register Pair BC the byte count.
593E
LDIR ED B0
Block move the reply from 5BB4H into the replace string buffer at 5242H, incrementing both pointers.
5940
LD HL,(5438H) 2A 38 54
Load Register Pair HL with the text address of the cursor from 5438H.
5943
DEC HL 2B
DECrement Register Pair HL by 1. The search loop tests the position one above the address it is given, so this makes the character under the cursor the first candidate.
5944
GOSUB to 55BBH, the string search, which returns the Z FLAG set and Register Pair HL addressing the match.
5947
If the NZ FLAG (Not Zero) has been set, meaning the search string is not at or after the cursor, JUMP to 532CH to queue Can't.
594A
LD (5438H),HL 22 38 54
Store the address of the match into 5438H, the text address of the cursor.
594D
LD C,(IX+06H) DD 4E 06
Load Register C with the search string length from 5206H. Register B is already zero, so Register Pair BC is that length.
5950
PUSH HL E5
Save the address of the match onto the stack; it is the destination of the move that closes the gap.
5951
ADD HL,BC 09
ADD the search string length to Register Pair HL, giving the address of the first character after the matched text.
5952
EX DE,HL EB
Exchange Register Pair DE with Register Pair HL so that Register Pair DE addresses the text after the match.
5953
LD HL,(58C2H) 2A C2 58
Load Register Pair HL with the end-of-text pointer from 58C2H.
5956
PUSH HL E5
Save the old end-of-text address onto the stack.
5957
OR A B7
Clear the CARRY FLAG ready for the subtraction.
5958
SBC HL,BC ED 42
SUBtract the search string length from the end-of-text address; the text is about to lose exactly that many characters.
595A
LD (58C2H),HL 22 C2 58
Store the shortened end-of-text address into 58C2H.
595D
POP HL E1
Restore the old end-of-text address into Register Pair HL.
595E
GOSUB to 5A07H, which subtracts Register Pair DE, the address after the match, from Register Pair HL and leaves the number of bytes that follow the matched text in Register Pair BC.
5961
INC BC 03
INCrement Register Pair BC by 1 so that the terminating zero byte moves down with the text.
5962
EX DE,HL EB
Exchange Register Pair DE with Register Pair HL, putting the address after the match into Register Pair HL as the source of the move.
5963
POP DE D1
Restore the address of the match into Register Pair DE, the destination of the move.
5964
GOSUB to 5928H, which block moves the text down over the matched string, so that the old text has now been removed.
5967
LD DE,5241H 11 41 52
Point Register Pair DE at 5241H, one byte below the replace string buffer at 5242H. The insert routine takes the byte below the source so that it can work out the length by subtraction.
596A
LD L,(IX+07H) DD 6E 07
Load Register L with the replace string length from 5207H.
596D
LD H,B 60
Load Register H with Register B, which the block move left at zero.
596E
ADD HL,DE 19
ADD 5241H to that length, giving the address of the last character of the replacement string.
596F
GOSUB to 598CH, which opens a gap of that many bytes at the cursor and copies the replacement string into it, refusing with No room! if the text will not fit.
5972
GOSUB to 53AAH, which returns at once if the insert failed and otherwise redraws the screen with the replaced text's line at the top and the cursor on it.
5975
LD B,(IX+07H) DD 46 07
Load Register B with the replace string length from 5207H, the number of positions the cursor is to be advanced.

Loop Start
The cursor is stepped one position right per character of the replacement, so that it finishes just past the text that was put in.

5978
LD HL,(5422H) 2A 22 54
Load Register Pair HL with the cursor row and column from 5422H.
597B
GOSUB to 5516H, which moves the cursor one position right and wraps to the next row at column 63.

Loop End

597E
DECrement Register B and LOOP BACK to 5978H until the cursor has passed the whole replacement.
5980
RET C9
RETurn to the main loop.

5981H - Locating the Marked Block and Copying It

Shared by block copy and block move. The first marked block in the text is found, the cursor is checked to be outside it, and 598CH then opens a gap at the cursor and copies the marked characters into it. The two markers themselves are left where they are and are not copied, which is the manual's "the successful block copy operation only copies the marked text; the markers are not copied as well".

5981
GOSUB to 59CEH, which searches the text from 5D00H for a block end marker and then back for the matching begin marker, returning Register Pair HL addressing the begin marker and Register Pair DE the end marker.
5984
RET NZ C0
If the NZ FLAG (Not Zero) has been set, meaning no properly marked block exists, RETurn with Marker! already queued.
5985
GOSUB to 59F5H, which sets the CARRY FLAG when the cursor lies outside the marked block.
5988
If the NO CARRY FLAG has been set, meaning the cursor is inside the block and the copy would be into the middle of itself, JUMP to 5328H to queue Cursor!.
598B
DEC HL 2B
DECrement Register Pair HL by 1. Register Pair HL is addressing the block end marker, so this leaves it on the last character of the marked text, which is where the copy below works backwards from.

598CH - Opening a Gap and Copying Text Into It

Given the last byte of a run of source text in Register Pair HL and the byte before its first in Register Pair DE, insert that run into the text at the cursor. Used both by block copy and move, where the source is the marked text itself, and by replace, where it is the replacement string in the workspace at 5242H. The text after the cursor is pushed up first and the run is then copied down into the hole, both with backwards block moves so that overlapping source and destination cannot corrupt the text.

598C
PUSH HL E5
Save Register Pair HL, the address of the last byte of the source, onto the stack.
598D
GOSUB to 5A07H, which subtracts Register Pair DE from Register Pair HL and leaves the number of bytes to be inserted in Register Pair BC.
5990
PUSH HL E5
Save that count onto the stack.
5991
LD HL,(5438H) 2A 38 54
Load Register Pair HL with the text address of the cursor from 5438H.
5994
GOSUB to 58C7H, which compares the cursor address against Register Pair DE, the byte below the source. The CARRY FLAG is set when the cursor is below the source, which means the source will itself be moved by the insertion.
5997
PUSH AF F5
Save Register A and that flag onto the stack for 59B8H.
5998
LD HL,(58C2H) 2A C2 58
Load Register Pair HL with the end-of-text pointer from 58C2H.
599B
ADD HL,BC 09
ADD the insertion length in Register Pair BC, giving the address the terminating zero byte will occupy once the gap has been opened.
599C
EX DE,HL EB
Exchange Register Pair DE with Register Pair HL, putting that prospective new end of text into Register Pair DE.
599D
If the NO CARRY FLAG has been set, meaning the addition did not overflow, GOSUB to 58C4H, which loads Register Pair HL with the text buffer ceiling from 58C5H and compares it against the prospective new end. The CARRY FLAG comes back set if the ceiling is the lower of the two.
59A0
If the CARRY FLAG has been set, meaning the insertion would run past HIGH$, JUMP to 5459H to queue No room! and abandon the command.
59A3
LD HL,(58C2H) 2A C2 58
Load Register Pair HL with the end-of-text pointer from 58C2H again.
59A6
PUSH HL E5
Save the old end-of-text address onto the stack.
59A7
LD (58C2H),DE ED 53 C2 58
Store the new end-of-text address from Register Pair DE into 58C2H.
59AB
LD BC,(5438H) ED 4B 38 54
Load Register Pair BC with the text address of the cursor from 5438H.
59AF
SBC HL,BC ED 42
SUBtract the cursor address from the old end-of-text address, giving the number of bytes from the cursor to the terminator.
59B1
LD B,H 44
Load Register B with Register H.
59B2
LD C,L 4D
Load Register C with Register L, moving that count into Register Pair BC.
59B3
INC BC 03
INCrement Register Pair BC by 1 so that the terminating zero byte is moved with the text.
59B4
POP HL E1
Restore the old end-of-text address into Register Pair HL, the source of the move.
59B5
GOSUB to 59BEH, which block moves that many bytes backwards from the old end of the text to the new one, opening the gap at the cursor.
59B8
POP AF F1
Restore Register A and the flag saved at 5997H, which says whether the cursor was below the source text.
59B9
POP BC C1
Restore the insertion length into Register Pair BC.
59BA
POP HL E1
Restore the address of the last byte of the source into Register Pair HL.
59BB
If the NO CARRY FLAG has been set, meaning the source lies below the cursor and has not been disturbed, JUMP to 59BEH and copy it as it is.
59BD
ADD HL,BC 09
ADD the insertion length to Register Pair HL. The source was above the cursor, so opening the gap moved it up by exactly that much.
59BE
LD A,B 78
Load Register A with Register B, the high byte of the count.
59BF
OR C B1
OR Register A with Register C, the low byte.
59C0
RET Z C8
If the Z FLAG (Zero) has been set, meaning there is nothing to move, RETurn.
59C1
LDDR ED B8
Block move Register Pair BC bytes backwards, decrementing both pointers, which copies the source text down into the gap that was opened for it.
59C3
CP A BF
Compare Register A with itself, which sets the Z FLAG unconditionally. That is the "the insertion worked" answer that 53AAH and 5972H test.
59C4
RET C9
RETurn.

59C5H - Folding a Letter to Upper Case

Used only by the block sub-command dispatcher, so that the six letters may be typed in either case as the manual promises. Anything that is not a lower case letter is left alone.

59C5
CP 61H FE 61
Compare Register A, the key that was pressed, against 61H, ASCII a.
59C7
RET C D8
If the CARRY FLAG has been set, meaning the character is below a, RETurn with it unchanged.
59C8
CP 7BH FE 7B
Compare Register A against 7BH, one past ASCII z.
59CA
RET NC D0
If the NO CARRY FLAG has been set, meaning the character is above z, RETurn with it unchanged.
59CB
SUB 20H D6 20
SUBtract 20H from Register A, turning a lower case letter into its upper case equivalent.
59CD
RET C9
RETurn.

59CEH - Finding the Marked Block

Searches forward for the block end marker 0FFH and then backwards from it for the block begin marker 0FEH, so that a stray begin marker earlier in the text cannot be picked up. 59CEH searches from the start of the text, which is what copy, move and print use to find "the first block marked in the text"; 59D1H searches from the cursor, which is what block delete uses to find the block the cursor is standing in.

59CE
LD DE,5D00H 11 00 5D
Point Register Pair DE at 5D00H, the first byte of the text buffer, the starting point for copy, move and print.
59D1
LD HL,(58C2H) 2A C2 58
Load Register Pair HL with the end-of-text pointer from 58C2H. This is the entry block delete uses, with Register Pair DE already holding the cursor address.
59D4
GOSUB to 5A04H, which reloads Register Pair HL from 58C2H, subtracts Register Pair DE and leaves the number of bytes to be searched in Register Pair BC.
59D7
LD A,0FFH 3E FF
Load Register A with 0FFH, the block end marker byte.
59D9
EX DE,HL EB
Exchange Register Pair DE with Register Pair HL so that Register Pair HL addresses the first byte to be searched.
59DA
CPIR ED B1
Search forward for the end marker, stopping when it is found or when Register Pair BC runs out.
59DC
If the NZ FLAG (Not Zero) has been set, meaning no end marker was found, JUMP to 59EFH to report it.
59DE
DEC HL 2B
DECrement Register Pair HL by 1, back onto the end marker itself; the search leaves the pointer one byte past its match.
59DF
LD DE,5CFFH 11 FF 5C
Point Register Pair DE at 5CFFH, one byte below the text buffer, which is the limit for the backwards search.
59E2
PUSH HL E5
Save the address of the end marker onto the stack.
59E3
GOSUB to 5A07H, which subtracts 5CFFH from the end marker address and leaves the number of bytes between them in Register Pair BC.
59E6
POP HL E1
Restore the address of the end marker into Register Pair HL.
59E7
LD D,H 54
Load Register D with Register H.
59E8
LD E,L 5D
Load Register E with Register L, copying the end marker address into Register Pair DE where the callers expect to find it.
59E9
LD A,0FEH 3E FE
Load Register A with 0FEH, the block begin marker byte.
59EB
CPDR ED B9
Search backwards from just below the end marker for the begin marker.
59ED
INC HL 23
INCrement Register Pair HL by 1, back onto the begin marker itself; a backwards search leaves the pointer one byte below its match.
59EE
RET Z C8
If the Z FLAG (Zero) has been set, meaning the begin marker was found, RETurn with Register Pair HL addressing it and Register Pair DE the end marker.
59EF
LD HL,5B2CH 21 2C 5B
Point Register Pair HL at the message at 5B2CH, Marker!.
59F2
JUMP to 532FH, which queues the message and returns to the caller with the NZ FLAG set.

59F5H - Testing Whether the Cursor Is Inside the Block

Called with Register Pair HL addressing the block begin marker and Register Pair DE the end marker. The CARRY FLAG comes back set when the cursor is outside the pair. Block delete needs it clear, because the cursor identifies which marked block is meant; copy and move need it set, because a block cannot be copied into itself.

59F5
PUSH HL E5
Save Register Pair HL, the address of the begin marker, onto the stack.
59F6
LD HL,(5438H) 2A 38 54
Load Register Pair HL with the text address of the cursor from 5438H.
59F9
EX DE,HL EB
Exchange Register Pair DE with Register Pair HL, so that Register Pair HL holds the end marker address and Register Pair DE the cursor address.
59FA
GOSUB to 58C7H, which compares the end marker address against the cursor address. The CARRY FLAG is set when the end marker is below the cursor, meaning the cursor is past the end of the block.
59FD
EX (SP),HL E3
Exchange the top of the stack with Register Pair HL, taking the begin marker address into Register Pair HL and leaving the end marker address on the stack.
59FE
EX DE,HL EB
Exchange Register Pair DE with Register Pair HL, so that Register Pair HL holds the cursor address and Register Pair DE the begin marker address.
59FF
If the NO CARRY FLAG has been set, meaning the cursor is not past the end of the block, GOSUB to 58C7H again to compare the cursor against the begin marker. The CARRY FLAG comes back set when the cursor is above the start of the block.
5A02
POP HL E1
Restore the end marker address into Register Pair HL.
5A03
RET C9
RETurn with the CARRY FLAG set if the cursor is outside the block on either side.

5A04H - Distance to the End of the Text

Two entries. 5A04H loads the end-of-text pointer from 58C2H first; 5A07H works on whatever is already in Register Pair HL. Both leave the difference in Register Pair BC as well as in Register Pair HL, because almost every caller wants it as a block move count.

5A04
LD HL,(58C2H) 2A C2 58
Load Register Pair HL with the end-of-text pointer from 58C2H.
5A07
OR A B7
Clear the CARRY FLAG ready for the subtraction. This is the entry used when the caller has already put an address in Register Pair HL.
5A08
SBC HL,DE ED 52
SUBtract Register Pair DE from Register Pair HL.
5A0A
LD B,H 44
Load Register B with Register H.
5A0B
LD C,L 4D
Load Register C with Register L, copying the difference into Register Pair BC.
5A0C
RET C9
RETurn.

5A0DH - The Prompt Line Input Routine

Reads a reply to a status line prompt into the buffer at 5BB4H, echoing each character as it is typed. Four keys are special: ENTER accepts the reply, the BREAK code 01H abandons it and returns with the CARRY FLAG set, the left arrow backspaces over the last character, and 18H, CTRL X, backspaces over all of them. Everything below 20H or at 80H and above is ignored.

The loop is built by planting 5A1BH on the stack as the return address at 5A26H, so that every path through the body simply RETurns to go round again; the two exits pop that address and the saved buffer start together and return to the real caller. The reply is terminated in the buffer with 03H and the number of characters is returned in Register B.

The length limit in Register B comes from Register C, which the caller has not set. The status line writer at 590AH leaves Register C holding the terminating byte of the prompt it has just displayed, and every prompt in the module ends with a space carrying bit 7, so the limit is always 0A0H, 160 characters, into a 24-byte buffer.

5A0D
LD B,C 41
Load Register B with Register C, the reply length limit. The 17H that 58EEH put in Register B is discarded here.
5A0E
PUSH HL E5
Save Register Pair HL, the address of the reply buffer, onto the stack.
5A0F
PUSH BC C5
Save Register Pair BC, which now holds the length limit in Register B, onto the stack.
5A10
LD B,04H 06 04
Load Register B with 4, the function code that returns the current cursor row and column.
5A12
GOSUB to 5A71H, which reads the hardware cursor address from 4020H and returns the row in Register H and the column in Register L. That is where the prompt left it, so the reply is echoed straight after the prompt text.
5A15
EX DE,HL EB
Exchange Register Pair DE with Register Pair HL, parking the cursor row and column in Register Pair DE, where they stay for the whole of the input.
5A16
POP BC C1
Restore Register Pair BC, the length limit.
5A17
POP HL E1
Restore Register Pair HL, the address of the reply buffer.
5A18
PUSH HL E5
Save the buffer address onto the stack again. This copy is popped by the two exits so that the caller is given the start of the buffer.
5A19
LD C,00H 0E 00
Load Register C with 0, the count of characters typed so far.

Loop Start
One keystroke is dealt with on each pass. 5A26H replaces the pushed pointer with 5A1BH, so every RETurn in the body comes back here.

5A1B
PUSH HL E5
Save Register Pair HL, the current position in the reply buffer, onto the stack.
5A1C
PUSH BC C5
Save Register Pair BC, the limit and the count, onto the stack.
5A1D
EX DE,HL EB
Exchange Register Pair DE with Register Pair HL, putting the cursor row and column into Register Pair HL for the blink routine.
5A1E
GOSUB to 5751H, which blinks the cursor at that position and returns the key that was pressed in Register A. It leaves Register Pair DE holding a discarded return address, which the next instruction throws away.
5A21
EX DE,HL EB
Exchange Register Pair DE with Register Pair HL, putting the cursor row and column back in Register Pair DE.
5A22
POP BC C1
Restore Register Pair BC, the limit and the count.
5A23
LD HL,5A1BH 21 1B 5A
Point Register Pair HL at 5A1BH, the top of this loop.
5A26
EX (SP),HL E3
Exchange the top of the stack with Register Pair HL. 5A1BH becomes the return address for this pass and Register Pair HL takes back the position in the reply buffer.
5A27
LD (HL),A 77
Store the key that was pressed into the reply buffer at that position. It is only kept if the tests below accept it and advance the pointer.
5A28
CP 01H FE 01
Compare Register A against 01H, the value the keyboard returns for the BREAK key.
5A2A
If the Z FLAG (Zero) has been set, meaning BREAK was pressed, JUMP to 5A50H to abandon the reply.
5A2C
CP 0DH FE 0D
Compare Register A against 0DH, ENTER.
5A2E
If the Z FLAG (Zero) has been set, JUMP to 5A51H to accept the reply.
5A30
CP 81H FE 81
Compare Register A against 81H, the code the left arrow returns.
5A32
If the Z FLAG (Zero) has been set, JUMP to 5A57H to rub out the last character typed.
5A34
CP 18H FE 18
Compare Register A against 18H, which on the Model I keyboard is CTRL X.
5A36
If the Z FLAG (Zero) has been set, JUMP to 5A69H to rub out the whole reply.
5A38
CP 80H FE 80
Compare Register A against 80H.
5A3A
RET NC D0
If the NO CARRY FLAG has been set, meaning the key has bit 7 set and is one of the arrow or command codes, RETurn to 5A1BH and ignore it.
5A3B
CP 20H FE 20
Compare Register A against 20H, the ASCII space.
5A3D
RET C D8
If the CARRY FLAG has been set, meaning the key is a control code, RETurn to 5A1BH and ignore it.
5A3E
LD A,C 79
Load Register A with Register C, the number of characters typed so far.
5A3F
CP B B8
Compare Register A against Register B, the length limit.
5A40
RET Z C8
If the Z FLAG (Zero) has been set, meaning the limit has been reached, RETurn to 5A1BH and ignore the key.
5A41
LD A,(HL) 7E
Load Register A with the character just stored in the reply buffer, addressed by Register Pair HL.
5A42
INC HL 23
INCrement Register Pair HL by 1, accepting the character and moving on to the next buffer position.
5A43
INC C 0C
INCrement Register C by 1, one more character in the reply.
5A44
PUSH BC C5
Save Register Pair BC, the limit and the count, onto the stack.
5A45
PUSH HL E5
Save Register Pair HL, the buffer position, onto the stack.
5A46
EX DE,HL EB
Exchange Register Pair DE with Register Pair HL, putting the cursor row and column into Register Pair HL.
5A47
LD C,A 4F
Load Register C with the character to be echoed.
5A48
GOSUB to 5775H, which writes it on the screen at that position.
5A4B
INC L 2C
INCrement Register L by 1, moving the echo position one column right.
5A4C
EX DE,HL EB
Exchange Register Pair DE with Register Pair HL, putting the updated row and column back in Register Pair DE.
5A4D
POP HL E1
Restore Register Pair HL, the buffer position.
5A4E
POP BC C1
Restore Register Pair BC, the limit and the count.

Loop End

5A4F
RET C9
RETurn to 5A1BH for the next keystroke.
5A50
SCF 37
Set the CARRY FLAG, which is how the caller is told that the reply was abandoned. 58F3H and 58F0H's caller use it to give up on the whole command.
5A51
LD (HL),03H 36 03
Store 03H into the reply buffer at the current position, the byte that terminates the reply for the DOS filespec parser.
5A53
LD B,C 41
Load Register B with Register C, the number of characters typed, which is what the caller reads as the reply length.
5A54
POP HL E1
Discard the 5A1BH loop address from the stack into Register Pair HL.
5A55
POP HL E1
Restore the address of the start of the reply buffer into Register Pair HL, which is what the caller expects.
5A56
RET C9
RETurn to the caller.
5A57
LD A,C 79
Load Register A with Register C, the number of characters typed so far.
5A58
OR A B7
Test Register A against zero.
5A59
RET Z C8
If the Z FLAG (Zero) has been set, meaning nothing has been typed, RETurn to 5A1BH; there is nothing to rub out.
5A5A
DEC HL 2B
DECrement Register Pair HL by 1, back onto the last character accepted.
5A5B
DEC C 0D
DECrement Register C by 1, one fewer character in the reply.
5A5C
DEC E 1D
DECrement Register E by 1, the column of the echo position, moving it one place left.
5A5D
PUSH BC C5
Save Register Pair BC, the limit and the count, onto the stack.
5A5E
PUSH HL E5
Save Register Pair HL, the buffer position, onto the stack.
5A5F
EX DE,HL EB
Exchange Register Pair DE with Register Pair HL, putting the echo position into Register Pair HL.
5A60
LD C,20H 0E 20
Load Register C with 20H, a space, to wipe the character off the screen.
5A62
GOSUB to 5775H, which writes that space at the echo position.
5A65
EX DE,HL EB
Exchange Register Pair DE with Register Pair HL, putting the echo position back in Register Pair DE.
5A66
POP HL E1
Restore Register Pair HL, the buffer position.
5A67
POP BC C1
Restore Register Pair BC, the limit and the count.
5A68
RET C9
RETurn, which goes to 5A1BH after a single backspace and to 5A6FH when the whole line is being rubbed out.

Loop Start
Characters are rubbed out one at a time until the reply is empty.

5A69
LD A,C 79
Load Register A with Register C, the number of characters left.
5A6A
OR A B7
Test Register A against zero.
5A6B
RET Z C8
If the Z FLAG (Zero) has been set, meaning the reply is now empty, RETurn to 5A1BH.
5A6C
GOSUB to 5A57H to rub out one character.

Loop End

5A6F
LOOP BACK to 5A69H for the next one.

5A71H - The Cursor Primitives

A four-way dispatcher on the function code in Register B: 1 reads the character at the cursor into Register A, 2 writes the character in Register C there, 3 sets the hardware cursor position from the row in Register H and the column in Register L, and 4 returns the current row and column in Register Pair HL. Register B is counted down rather than compared, so any code of 5 or above simply falls out of the bottom and returns having done nothing, which is the exit the screen refresh uses at 57E6H.

5A71
DEC B 05
DECrement Register B, the function code.
5A72
If the Z FLAG (Zero) has been set, meaning the code was 1, JUMP to 5A8AH to read the character at the cursor.
5A74
DEC B 05
DECrement Register B again.
5A75
If the Z FLAG (Zero) has been set, meaning the code was 2, JUMP to 5A8FH to write a character at the cursor.
5A77
DEC B 05
DECrement Register B again.
5A78
If the Z FLAG (Zero) has been set, meaning the code was 3, JUMP to 5A95H to set the hardware cursor position.
5A7A
DEC B 05
DECrement Register B a fourth time.
5A7B
RET NZ C0
If the NZ FLAG (Not Zero) has been set, meaning the code was 5 or more, RETurn without doing anything.
5A7C
LD HL,(4020H) 2A 20 40
Load Register Pair HL with the cursor address from 4020H, the ROM display driver's cursor position cell. This is function code 4.
5A7F
LD A,L 7D
Load Register A with Register L, the low byte of that video address.
5A80
AND 3FH E6 3F
AND Register A with 3FH, keeping the six bits that give the column within the 64 character line.
5A82
ADD HL,HL 29
ADD Register Pair HL to itself: multiply the video address by 2.
5A83
ADD HL,HL 29
ADD Register Pair HL to itself again, so that the address has been multiplied by 4 and the line number has been shifted up into Register H.
5A84
LD L,A 6F
Load Register L with the column held in Register A.
5A85
LD A,H 7C
Load Register A with Register H.
5A86
AND 0FH E6 0F
AND Register A with 0FH, keeping the four bits that give the video row 0 to 15.
5A88
LD H,A 67
Load Register H with that row, so that Register Pair HL now holds the row and column.
5A89
RET C9
RETurn.
5A8A
GOSUB to 5A9DH, which turns the row in Register H and the column in Register L into a video address in Register Pair DE.
5A8D
LD A,(DE) 1A
Load Register A with the character on the screen at that address.
5A8E
RET C9
RETurn.
5A8F
GOSUB to 5A9DH for the video address of the row and column in Register Pair HL.
5A92
LD A,C 79
Load Register A with Register C, the character to be written.
5A93
LD (DE),A 12
Store it on the screen at that address.
5A94
RET C9
RETurn.
5A95
GOSUB to 5A9DH for the video address of the row and column in Register Pair HL.
5A98
LD (4020H),DE ED 53 20 40
Store that address into 4020H, the ROM display driver's cursor position cell, so that the next character it displays goes there.
5A9C
RET C9
RETurn.

5A9DH - Video Address from Row and Column

The screen is 16 lines of 64 characters based at 3C00H, so the address of a cell is 3C00H plus 64 times the row plus the column. The routine builds it by putting the low two bits of the row into the top two bits of the low byte and the high two bits of the row into the low two bits of 3CH, which costs four rotates and no multiplication.

5A9D
LD A,H 7C
Load Register A with Register H, the video row 0 to 15.
5A9E
AND 03H E6 03
AND Register A with 3, keeping the low two bits of the row.
5AA0
RRCA 0F
Rotate Register A right; bit 0 moves into bit 7.
5AA1
RRCA 0F
Rotate Register A right again, so that the two row bits are now in bits 7 and 6, which is a multiplication by 64.
5AA2
OR L B5
OR Register A with Register L, the column, filling in the low six bits.
5AA3
LD E,A 5F
Load Register E with that low byte of the video address.
5AA4
LD A,H 7C
Load Register A with Register H, the row, again.
5AA5
RRCA 0F
Rotate Register A right.
5AA6
RRCA 0F
Rotate Register A right again, moving the high two bits of the row down into bits 1 and 0.
5AA7
AND 03H E6 03
AND Register A with 3, discarding everything else.
5AA9
OR 3CH F6 3C
OR Register A with 3CH, the high byte of the video screen base address 3C00H.
5AAB
LD D,A 57
Load Register D with that high byte, completing the video address in Register Pair DE.
5AAC
RET C9
RETurn.

5AADH - Ruling Off the Text Area and Clearing the Status Line

The screen is divided into fourteen lines of text on video rows 0 to 13, a rule of underscores on video row 14 and the status line on video row 15. This routine draws the rule and blanks the status line, and is called at start-up and again after a directory listing has scrolled the screen.

5AAD
LD HL,3F80H 21 80 3F
Point Register Pair HL at 3F80H, the first cell of video line 14.
5AB0
LD BC,405FH 01 5F 40
Load Register Pair BC with 405FH: Register B is 40H, 64 cells, and Register C is 5FH, the underscore character.

Loop Start
The rule is drawn one cell at a time.

5AB3
LD (HL),C 71
Store the underscore into the video cell addressed by Register Pair HL.
5AB4
INC HL 23
INCrement Register Pair HL by 1.

Loop End

5AB5
DECrement Register B and LOOP BACK to 5AB3H until the whole line is ruled.
5AB7
LD BC,4020H 01 20 40
Load Register Pair BC with 4020H: Register B is 40H, 64 cells, and Register C is 20H, a space. Register Pair HL is already standing on 3FC0H, the first cell of the status line.

Loop Start
The status line is blanked one cell at a time.

5ABA
LD (HL),C 71
Store a space into the video cell addressed by Register Pair HL.
5ABB
INC HL 23
INCrement Register Pair HL by 1.

Loop End

5ABC
DECrement Register B and LOOP BACK to 5ABAH until the status line is clear.
5ABE
RET C9
RETurn.

5ABFH - The BREAK Key Test

The DOS keyboard driver records a BREAK by setting bit 0 of KFLAG$ at 4423H rather than returning it as a character. This routine clears that bit and then looks again a moment later, so that holding BREAK down holds the program here; it is called at the top of the main loop and between characters while the buffer is being printed. The NZ FLAG on return means BREAK was pressed, which is what stops a listing at 53D2H.

5ABF
PUSH HL E5
Save Register Pair HL onto the stack; the callers want it back unchanged.
5AC0
LD HL,4423H 21 23 44
Point Register Pair HL at KFLAG$, the resident keyboard flag byte at 4423H.
5AC3
BIT 0,(HL) CB 46
Test bit 0 of KFLAG$ 4423H, the BREAK flag.
5AC5
If the Z FLAG (Zero) has been set, meaning BREAK has not been pressed, JUMP to 5AE0H and return.
5AC7
PUSH AF F5
Save Register A and the flags onto the stack. The NZ FLAG saved here is what the caller reads on return.
5AC8
PUSH BC C5
Save Register Pair BC onto the stack.
5AC9
PUSH DE D5
Save Register Pair DE onto the stack.

Loop Start
The flag is cleared and looked at again a moment later, so the program waits here for as long as the key is held down.

5ACA
RES 0,(HL) CB 86
Reset bit 0 of KFLAG$ 4423H, acknowledging the BREAK.
5ACC
LD B,0BH 06 0B
Load Register B with 0BH, the delay count.
5ACE
GOSUB to the Level II ROM PAUSE routine at 0060H, which counts Register Pair BC down to zero.
5AD1
BIT 0,(HL) CB 46
Test bit 0 of KFLAG$ 4423H again.

Loop End

5AD3
If the NZ FLAG (Not Zero) has been set, meaning the key is still down and the driver has set the flag again, LOOP BACK to 5ACAH.
5AD5
LD DE,4015H 11 15 40
Point Register Pair DE at 4015H, the Device Control Block of the *KI keyboard device.
5AD8
LD A,03H 3E 03
Load Register A with 3, the control byte to be sent.
5ADA
GOSUB to the Level II ROM $CTL routine at 0023H, which passes the control byte in Register A to the driver whose Device Control Block is addressed by Register Pair DE, which here is the KI/DVR keyboard driver.
5ADD
POP DE D1
Restore Register Pair DE from the stack.
5ADE
POP BC C1
Restore Register Pair BC from the stack.
5ADF
POP AF F1
Restore Register A and the flags from the stack, so that the caller sees the NZ FLAG that says BREAK was pressed.
5AE0
POP HL E1
Restore Register Pair HL from the stack.
5AE1
RET C9
RETurn.

5AE2H - Text Constants and Messages

Every string in the module ends with a character whose bit 7 is set, which is what the status line writer at 5922H tests, and none of them carries a length byte. Two of them overlap on purpose: 5B4FH is Load Filespec? and 5B54H is the same text five characters in, Filespec? , which is how one string serves both the LOAD and the FILE prompt.

The three bytes at 5AE2H are not a message at all. They are the default file extension handed to the resident @FEXT vector at 5669H, and they carry no terminator because @FEXT always takes exactly three characters. The 0FH at 5AE5H is the video control code that turns the ROM cursor off and is displayed as the first character of the sign-on banner.

5AE2
DEFM 'TXT' 54 58 54
The default file extension, applied by the resident @FEXT vector at 4473H when the operator gives a filespec with no extension of its own. This is the manual's "if the extension is omitted from your entry, /TXT will be automatically provided".
5AE5
DEFB 0FH 0F
The video control code that turns the ROM cursor off. It is the first byte of the sign-on banner, so displaying the banner also puts the cursor out.
5AE6
DEFM 'TED 1.2 - (c) 1991 MISOSYS, In', DEFB 0E3H 54 45 44 20 31 2E 32 20 2D 20 28 63 29 20 31 39 39 31 20 4D 49 53 4F 53 59 53 2C 20 49 6E E3
The sign-on banner, terminated by c with bit 7 set. It is displayed by 58FFH whenever a status message has to be wiped, so it is the normal contents of the status line. The banner gives the version as 1.2 and the year as 1991, while the load module's own copyright record reads Copyright (c) 1987 MISOSYS, Inc., All rights reserved.
5B05
DEFM 'Press to EXI', DEFB 0D4H 50 72 65 73 73 20 3C 45 4E 54 45 52 3E 20 74 6F 20 45 58 49 D4
The message the EXIT command displays at 531AH when there is text in the buffer, terminated by T with bit 7 set.
5B1A
DEFM 'Delet', DEFB 0E5H 44 65 6C 65 74 E5
The message the CTRL D handler queues at 55FAH to show that DELETE mode is in force for the next keystroke, terminated by e with bit 7 set.
5B20
DEFM 'Drive?', DEFB 0A0H 44 72 69 76 65 3F A0
The prompt the directory query displays at 5718H, terminated by a space with bit 7 set. It is that terminating byte, 0A0H, that ends up as the reply length limit at 5A0DH.
5B27
DEFM 'Bloc', DEFB 0EBH 42 6C 6F 63 EB
The message the CTRL B handler queues at 5324H to show that the next keystroke will be read as a block sub-command, terminated by k with bit 7 set.
5B2C
DEFM 'Marker', DEFB 0A1H 4D 61 72 6B 65 72 A1
The error the marked-block finder queues at 59EFH when there is no properly marked block in the text, terminated by an exclamation mark with bit 7 set.
5B33
DEFM 'No room', DEFB 0A1H 4E 6F 20 72 6F 6F 6D A1
The error queued at 5459H when a character cannot be inserted, at 56D8H when a file will not fit in the buffer, and at 59A0H when a block copy would run past HIGH$.
5B3B
DEFM 'Cursor', DEFB 0A1H 43 75 72 73 6F 72 A1
The error reached through 5328H when the cursor is on the wrong side of a marked block: inside it for a copy or a move, outside it for a delete.
5B42
DEFM 'String?', DEFB 0A0H 53 74 72 69 6E 67 3F A0
The prompt the SEARCH and REPLACE commands display through 58E5H, terminated by a space with bit 7 set.
5B4A
DEFM 'Can''t', DEFB 0F4H 43 61 6E 27 F4
The error reached through 532CH when a search or a replace finds no match, terminated by t with bit 7 set.
5B4F
DEFM 'Load ' 4C 6F 61 64 20
The five characters that turn the prompt below into the LOAD version of it. 565BH selects this address by loading Register L with 4FH, so one string serves both disk commands.
5B54
DEFM 'Filespec?', DEFB 0A0H 46 69 6C 65 73 70 65 63 3F A0
The prompt for a file name, terminated by a space with bit 7 set. CTRL F displays it from here and CTRL L from five bytes earlier.
5B5E
DEFM 'Press to confir', DEFB 0EDH 50 72 65 73 73 20 3C 45 4E 54 45 52 3E 20 74 6F 20 63 6F 6E 66 69 72 ED
The confirmation prompt 58CDH displays before every command that throws text away, terminated by m with bit 7 set.

5B76H - Padding

Thirty-two zero bytes that round the image out. They are loaded, so they are part of the file, and the last two of them are read as data: the screen refresh sets Register Pair IY to 5B94H at 57BEH, one entry below the line table, and the routine at 58A4H reads the entry below the one it is given, so a request for the row above row 0 lands on 5B94H and 5B95H and finds zero there.

5B76-5B95
DEFB 00H x 32 00 x 32
Thirty-two zero bytes of padding at the end of the loaded image. 5B94H and 5B95H serve as the guard entry immediately below the line table that starts at 5B96H.