; Wang 2200 MXD Terminal Mux card source code. ; Disassembled and commented by by Jim Battle. All errors are mine. ; Last update: 2018/10/21 ; ; TODO: ; Some of the comments were written while much of the code was a mystery. ; Everything needs a scrubbing in light of subsequent discoveries. ; ; -------------------------------------------------------------------------- ; 2536DWTerminalAndTerminalControllerProtocol.pdf describes the communication ; protocol used between the MXD controller and the terminal. ; ; Controller to terminal protocol: ; is the escape character ; -- repeated character (count 0x01 to 0x5F) ; -- repeated space character (count 0x01 to 0x5F) ; -- x/6 second delay (x is 0x0 to 0x9) ; -- literal character (can't be sent compressed) ; * -- route subsequent data to crt ; * -- route subsequent data to printer ; * -- restart terminal ; * -- reset crt only ; -- stop cursor blinking ; -- start cursor blinking ; ; The '*'-marked commands are "immediate" meaning they can arrive any time, ; even in the middle of a non-immediate sequence, eg ; (sequence interrupted here) ; ; ; ; Terminal to Controller protocol: ; <12> -- SHIFT RESET was pressed ; <13> -- halt/step key pressed ; <81> -- CLEAR key ; <82> -- RUN key ; <84> -- CONTINUE key ; -- PRINT key ; -- LOAD key ; -- edit key ; -- lowercase FN ; -- uppercase FN ; - -- special function keys ; -- line erase ; -- crt go (crt buffer ready to accept more data) ; -- printer go (print buffer ready to accept more data) ; -- crt stop (terminal not ready to accept more crt data) ; -- printer stop (terminal not ready to accept more printer data) ; ; The following byte pairs are atomic, meaning no other character can appear ; between the two bytes: ; -- transparent: interpret character literally ; -- EndI/Atom: if char is less than <80>, it is s.f. key ; not sure why since we have - above ; if char is more than <80>, it is an atom ; -- dead key: like a transparent char, but with special semantics ; ; page 4 of the pdf says this about "CRT GO": ; The MXD uses the first occurrence of "CRT GO" to distinguish a 2236D ; terminal from a non-Wang terminal. The MXD will only send compressed ; data to a Wang terminal. [ Ed.: see WANGTERM variable ] ; ; page 4, section 2.7.4, says: ; When the MXD is powered on, it assumes that the terminal's print buffer ; is full and the CRT buffer is empty. ... ; -------------------------------------------------------------------------- ; The 8080 frequency is 15.9774/9 MHz, or about 1.78 MHz. ; ; The UARTs are i8251. ; ; MEMORY MAP ; 0000 - 0FFF : 4K : ROM ; 2000 - 2FFF : 4K : RAM ; ; Each port gets a block of RAM, minus some holes for stack and globals. ; uart 1: 0x2000-23FF ; uart 2: 0x2400-27FF ; uart 3: 0x2800-2BFF ; uart 4: 0x2C00-2FFF ; ; p5 of 2236MXE_Documentation.8-83.pdf says this is the MXD buffer usage ; KEYBOARD buffer: 36 bytes ; LINE_REQ buffer: 480 bytes ; PRINTER buffer: 160 bytes ; CRT buffer: 250 bytes ; ; Here is my top-level reconstruction of how RAM is used. "control" is ; the state associated with managing buffer. Each 1KB block also has ; a hole in it where globals/stack live. ; ; region control buffer unused? ctl + buff + unused ; ---------------- --------- --------- --------- ------------------- ; 2000-2002 3 ; UART tx buffer 2003-2004 2005-2015 2 + 17 ; KEYBOARD buffer 2016-201A 201B-205A 5 + 64 ; LINE_REQ buffer 205B-206A 206B-2252 16 + 488 ; global state 2254-2265 2253 18 + 1 ; PRINTER buffer 22FE-22FF 2266-22F9 22FA-22FD 2 + 148 + 4 ; CRT buffer 23FE-23FF 2300-23F9 23FA-23FD 2 + 250 + 4 ; ------------ ; 1024 bytes ;------------------------------------------------------------------------------ ; rough overview of how the MXD operates ; ; The MXD supports up four terminals. The board decodes ports 01-07 (or 41-47, ; 81-87, or C1-C7). An onboard 8080 polls the host cpu interface for CBS/OBS ; strobes as well as requests for data. It manipulates the ready/busy (RBI) ; pin to control the handshake with the host CPU. At that level, the board ; is a "soft peripheral" in that it is 8080 software that determines how the ; CPU sees it. ; ; The 8080 program is 4KB of code in EPROM; the board has 4KB of RAM, which ; is divided up as one 1KB block for each terminal. Each block has a few ; regions not associated with the terminal which hold global state. ; ; BUFFERS ; There are five buffers associated with each terminal. ; + RX buffer (32 entries of two bytes each, circular) ; This is a circular buffer of keys received from the terminal. ; Each entry is two bytes because more than 256 types of events ; may need to be saved. ; + CRT buffer (250 bytes, not circular) ; The host sends data to the crt via OBS strobes to port 05 (VP mode) ; or port 07 (MVP mode). The MXD itself may want to send bytes to ; the CRT as well, for example, echoing keys from the terminal. ; These get queued, then the MXD stops accepting any more data on ; port 05/07 until all the bytes have been transferred. ; + PRT buffer (150 bytes, not circular) ; Much like the CRT buffer, the host cpu sends bytes via OBS to port 04. ; + UART tx buffer (16 bytes, not circular) ; The MXD must send both crt and prt buffer traffic over the same ; serial line. The MXD will transfer up to 16 bytes at a time from ; either the crt or prt buffer to the tx buffer when the tx buffer ; goes empty. A polling loop in the MXD will transfer a byte from ; a tx buffer to the corresponding UART once the UART says it is ready ; for more data. ; + line request buffer (488 bytes) ; This buffer holds the data being input and possibly edited by the ; user. The MXD handles echoing the characters, buffer editing, and ; CRT refresh to effect the edits to keep the display in sync with ; the contents of the buffer. ; ; INTERRUPTS ; Any time one of the four UARTs has received a byte from its terminal, it ; raises an interrupt. The interrupt routine on the 8080 will fetch the ; just received byte, do a bit of preprocessing on it, then push it into ; the RX buffer. Speed is of the essence. ; ; MAIN EVENT LOOP ; MAIN_EVENT is the top level routine which executes forever, polling for ; activity from the host CPU and shuttling data from the output buffers ; to the UART output port. The outline of the main_event loop is this: ; while (true) { ; - reset stack ; - call CHECK_HOST routine: poll for OBS or CBS from host ; - main2: check which uart tx buffers are empty; ; call TX_POLL to fill them from the crt/prt buffers ; - txd_lookup: foreach terminal, transfer a byte from the uart tx buffer ; to the corresponding UART if there is room ; - call CHECK_HOST routine: poll for OBS or CBS from host ; - if there is any line_req in progress and the corresponding rx buffer ; has data, drop into the RX_POLL routine ; } ; ; TX_POLL ; foreach (terminal with an empty uart tx buffer) { ; Move data from either the crt or prt buffer to the uart tx buffer. ; Routing control bytes are inserted in the stream to multiplex between ; crt and prt data. ; } ; ; RX_POLL ; foreach (terminal with an active line_req and pending key data) { ; - if there is a "continuation" routine pending jump to it ; - else dispatch the key according to value. the routine which handles ; the key may push bytes to the crt buffer to update the display ; } ; jump back to MAIN_EVENT ; ; CHECK_HOST ; This routine is called twice in each iteration of the MAIN_EVENT loop. ; It polls the I/O status registers to determine if the board is addressed ; and if a CBS or OBS strobe happened, or or if the host CPU is wait for ; a response (!CBP). For example, an OBS to port 07 pushes the byte into ; the terminal's CRT buffer, while an OBS to port 04 pushes the byte into ; the PRT buffer. ; ; Where things get interesting is a CBS strobe to port 06 initiates a command ; sequence. The CBS is typically followed by one or more OBS strobes which ; supply parameters to the command. These sequences from the host are delivered ; as quickly as the bus can support them (and the 8080 can handshake them), ; so the handler simply directly polls for the OBS bytes; there is no need ; to suspend in the middle to service TX_POLL or RX_POLL work (though interrupts ; are still active). Once the command sequence is received, the 8080 may need ; to do some work (eg, reset a given terminal, clear a buffer). But the most ; interesting commands are those associated with LINE_REQ, which is the main ; reason why MXD exists. ; ; When the bit corresponding to the current terminal has its bit set in the ; LREQ_ACCEPT byte and a key is waiting to be processed, the key gets interpreted ; in light of the active command. Most keystrokes just represent ordinary data ; input, so the byte is appended into the line_req buffer and the character is ; pushed into the crt buffer fifo to effect the echo. But some keys have a ; special meaning and require more work. For instance in EDIT mode, the S.F. 10 ; key means "insert a character". The 8080 must shift all the bytes from the ; current cursor position to the end of the line_req buffer right one position; ; bytes must be sent to the crt to redraw the affected portion of the buffer ; (keeping in mind line wrapping and such); then the cursor must be repositioned ; to the active cursor position. ; ; Some redrawing commands make take a long time or require stuffing many bytes ; into the crt buffer. As a result, these routines have to be broken up into ; chunks of work. The byte at 0x2n5C is normally 00, but if it is not, the ; RX_POLL will take a detour to a "continuation" of the suspended work to allow ; more progress. One command may have many continuations chained together ; (see the first half of the INDTAB to see them). ;------------------------------------------------------------------------------ ; What follows is a map of how memory is allocated for the first terminal. ; (add 0x0400*n for terminal N-1) ; ; 0x2000 terminal associated with this block, one-hot (01=term 1, 02=term 2, 04=term 3, 08=term 4) ; 0x2001 ? per-term RBI status? (see code after REQ_PRINT_BUFFER, and SET_ACT_TERM) ; 0x2002 terminal status byte (see STATUS_RESPONSE routine) ; There is a buffer for data destined for the crt at 0x2300-0x23F9, and a buffer ; for printer data at 0x2266-0x22F9. the tx channel needs to multiplex both of ; these streams onto the one serial port. It does so by prefixing crt data with ; the escape, and printer data by . It would be inefficient to ; switch too often, so the mxd batches up work of 8-16 (ish) bytes at least. ; this data is saved in the uart tx buffer at 0x2005-0x201F. This is not a ; circular buffer. [2n03] is the count of bytes remaining to send, and [2n04] is ; the get pointer (just one byte, since it is all in the current page). Once ; all the data is transmitted, [2n03] is reinitialized to 0x00 and [2n04] to 0x04. ; the latter is surprising, it is pre-incremented before use, so get=0x04 really ; means the next byte to send is at 0x2n05. ; ; 0x2003 remaining bytes to send in uart tx buffer ; 0x2004 uart tx buffer get pointer (next byte to send is at get+1) ; 0x2005-0x201F uart tx buffer (27 bytes, though I'm not sure how full it gets) ; I suspect the crt is allowed to save 14 bytes, and the printer 8, ; plus two bytes each to establish mode (2+14+2+8) = 26 ; pending keystroke circular buffer. ; as characters are received from the terminal, they are saved in this buffer. ; they aren't processed until the 2200 issues some type of command which may ; consume them (eg, LINE_REQ, or KEYIN_LINE_REQ). ; The first five bytes (2n16-2n1A) are zero-filled in INIT_CRT_BUF. ; 0x2016 - saved keycode value ; 0x2017 - saved prefix byte, waiting for 2nd byte to arrive ; 0x2018 - count of entries in the rx buffer ; 0x2019 - put pointer is left pointing to the 2nd byte of the most recent ; entry, which means it pre-increments on push ; 0x201A - get pointer to most recent entry of the rx buffer ; 0x201B..5A - is a circular buffer of 32 two-byte entries. the first (prefix) ; (prefix) byte indicates the class of the second byte. the second ; byte is the key value. the prefix byte is mostly the same as ; documented on page 4 of this pdf, but with a FF type added: ; 2536DWTerminalAndTerminalControllerProtocol.pdf ; 0xFC: transparent -- interpret keycode literally, no atom or escape processing ; 0xFD: EndI/Atom -- <= 0x7F is special function key; >= 0x80 is an atom ; 0xFE: dead key -- char echoed, then the cursor is backed up one, ; but otherwise identical to transparent key handling ; 0xFF: normal -- this isn't sent on the line, but is placed into the kb buff; ; it tags the value as being none of the above cases, I guess ; This section is associated with procesing LINE_REQ and any of the associated commands. ; 0x205B unused? ; 0x205C "continuation offset" (see the comment in SAV_CONT) ; 0x205D/5E big endian pointer to next byte of line_req buffer that needs to be redrawn ; 0x205F/60 big endian pointer to last byte of line_req buffer that needs to be redrawn ; 0x2061 key prefix byte; used in SF15 (recall) handling, and in QUERY_LINE_REQ ; 0x2062 key code value; used in SF15 (recall) handling, and in QUERY_LINE_REQ ; 0x2063 line request flag byte ; 01: flush previously stored keystrokes ; 02: keyin_line_req command flag ; 04: EDIT mode ; 08: ; 10: refill_line_req command flag ; 20: LINPUT mode (learned from DAMVP35) ; 40: OS > 1.3 (learned from DAMVP35) ; 80: underline ; 0x2064 line_req cursor column ; 0x2065/66 line_req pointer (big endian) to end of buffer ; 0x2067/68 pointer (big endian) to last used byte of line_req buffer, ; perhaps one past the last byte used. I haven't checked carefully. ; 0x2069/6A pointer (big endian) to current cursor location in line_req buffer ; 0x206B-0x2252 line request buffer (0x1E8=488 characters max) ; 0x2266-0x22F9 prt buffer; data waiting to be sent to the terminal printer (148 bytes) ; 0x22FE print buffer put pointer; set to 0x76 in INIT_PRT_BUF routine (note: offset byte after area zero'd out above) ; 0x22FF print buffer get pointer; set to 0x66 in INIT_PRT_BUF routine (note: offset byte of area zero'd out above) ; ; 0x2300-0x23F9 crt buffer; data waiting to be sent to the terminal crt (250 bytes) ; 0x23FE crt buffer put pointer ; 0x23FF crt buffer get pointer ;------------------------------------------------------------------------------ ; global locations, not per-terminal 20 00 START_OF_RAM equ 02000H ; 0x2252-2265 should be free (fits between end of line_req buffer and start of prt buffer) ; 0x2266-0x22F9 prt buffer; data waiting to be sent to the terminal printer (148 bytes) 22 56 MVP_MODE equ 02256H ; 0x00=vp mode, 0xFF=mvp mode 22 57 SHADOW_RBI equ 02257H ; most recent value stored in RBI register ; CUR_BLOCK is a 16b pointer to the base of the 1KB block of memory associated ; with the terminal most recently selected by SET_ACT_TERM. It takes one of ; these values: 0x2000, 0x2400, 0x2800, or 0x2C00. ; CUR_BLOCK_PAGE is just the ms byte, namely 0x20, 0x24, 0x28, or 0x2C. 22 58 CUR_BLOCK equ 02258H 22 59 CUR_BLOCK_PAGE equ 02259H ; CUR_TERM is closely related to CUR_BLOCK variable; it is a one-hot encoding ; of the terminal most recently selected by SET_ACT_TERM. It takes one of ; these values: 0x01, 0x02, 0x04, 0x08. 22 5A CUR_TERM equ 0225AH 22 5C CUR_UART equ 0225CH ; one-hot bitmap to select default uart 22 5E HAS_UART_TXD equ 0225EH ; bitmap of which ports have data in their uart tx buffer ; a map which ports need to be serviced (1=port 1, 2=port 2, 4=port 3, 8=port 4) ; in TX_POLL, it is which terminal is having crt or prt data transferred to the uart tx buffer. ; in RX_POLL, it is which terminal is processing keyboard buffer entries. 26 56 PENDING_TERMS equ 02656H ; terminal bitmap ; LINE_REQ_BLOCK is akin to CUR_BLOCK in that it holds a pointer to the 2n00 ; base address for a 1K block of RAM. CUR_BLOCK always points to the base of ; the terminal current addressed. LINE_REQ_BLOCK does too during the QUERY_LINE_REQ ; and ACCEPT_LINE_REQ_DATA commands, but during RX_POLL processing it points at ; whichever terminal is being serviced in that iteration of the RX_POLL loop. 26 57 LINE_REQ_BLOCK equ 02657H ; memory block for most recent line request. how is this different from CUR_BLOCK? 26 58 LINE_REQ_PAGE equ 02658H ; ms byte of LINE_REQ_BLOCK pointer 26 59 ULINE_FLAG equ 02659H ; underline flag (either 0x00 or ox80) 26 5A BUF_ACTIVE_IND equ 0265AH ; pointer to either CRT_BUF_ACTIVE or PRT_BUF_ACTIVE 26 5C CUR_CAN_CMPRS equ 0265CH ; non-zero means current terminal supports Wang compression 26 5D TX_SERVICE equ 0265DH ; term being serviced by tx polling. [265D] = crt/printer map; [265E] = page0 for term ; note that two different bits of code use the same 265F/60 pair for their own purposes ; it stores a pointer to data associated with terminal currently being serviced. 26 5F CUR_INIT equ 0265FH ; 2n00 (in INIT_CRT_BUF/INIT_PRT_BUF) 26 5F CUR_TX_BUF equ 0265FH ; 2n03 (in TX_POLL) 26 61 TX_RECEIVER equ 02661H ; bitmap: current routing target of tx bytes: 0=crt, 1=printer ; The data for the crt and printer associated with a given terminal are ; multiplexed on the same serial line. These two bytes act heuristically to ; reduce the effect of the printer traffic on the flow of crt traffic. The logic ; is arcane, so this is my best reconstruction of what is going on. Both ; PRT_PRIO and SENT_CR are bitmaps, one bit per terminal in the same fashion as ; other bitmaps here. in the description below when I say something is set or ; cleared, I just mean the bit associated with the current terminal. ; ; SENT_CR is reset to zero. If, during the middle of a transfer of bytes from ; the crt or prt buffer to the uart tx buffer, a carriage return is found ; (see CR_LIMIT_XFER:), this bit gets set. Once the remainder of the crt or ; prt buffer is transferred to the tx buffer (see txp4:), this bit is cleared ; back to zero. Its state is used only to affect PRT_PRIO. ; ; A confusing thing is that the logic which twiddles these bytes is largely ; independent of whether we are transferring crt or printer data. Eg, when a ; is sent to the uart tx buffer, it affects crt/prt transmission priority, ; but the logic doesn't care if it was a for the printer or the crt. ; ; PRT_PRIO=1 is used by the logic in main2 to prioritize prt traffic over the ; serial line if both crt/prt buffers have data to send. ; PRT_PRIO is ; - reset to zero. ; - in main2:, if the uart tx buffer is empty and the prt buffer is ready ; to send (non-empty and not flow-controlled), this bit gets cleared. ; - if, during the middle of a transfer of bytes from the crt or prt buffer ; to the uart tx buffer, a carriage return is found (see CR_LIMIT_XFER:), ; this bit gets set (as does SENT_CR). ; - in txp4:, the SENT_CR state comes into play; when the crt or prt buffer ; is completely drained to the tx buffer, this bit is set if the prt buffer ; is ready to send data and the prt/crt buffer hasn't sent a CR since the ; last time the prt/crt buffer was empty ; Perhaps the reason for the last bullet above is to promote a prt transfer ; every other tx chunk sent. 26 62 PRT_PRIO equ 02662H ; terminal bitmap; see the description above 26 63 SENT_CR equ 02663H ; terminal bitmap; see the description above ; these four bytes are analogous to same for crt 2A 56 PRT_STAT_PEND equ 02A56H ; which terms were queried for REQUEST_CRT_BUFFER but were not empty at the time 2A 57 PRT_CAN_TX equ 02A57H ; which terms are ready to tx (1=non-empty && not flow controlled) 2A 58 PRT_BUF_ACTIVE equ 02A58H ; which terms have non-empty prt buffer (1=non-empty) 2A 59 PRT_STOPPED equ 02A59H ; which terms are in prt flow control state (1=stopped) ; the following 10 bytes each contain a terminal bitmap of some status, ; where 01=term 1, 02=term 2, 04=term 3, 08=term 4 2A 5A WANGTERM equ 02A5AH ; 1=flow control seen, so term is wang-compatible ; cleared on POR or INIT_CUR_TERM or reset key ; set on reception of STOP flow control (independent of CRT or PRT type) ; this is probably the "Wang Terminal" qualifier ; The MXD uses the first occurrence of "CRT GO" to distinguish a 2236D ; terminal from a non-Wang terminal. The MXD will only send compressed ; data to a Wang terminal. ; these four bytes are analogous to same for printer 2A 5B CRT_STAT_PEND equ 02A5BH ; which terms were queried for REQUEST_CRT_BUFFER but were not empty at the time 2A 5C CRT_CAN_TX equ 02A5CH ; which terms are ready to tx (1=non-empty && not flow controlled) 2A 5D CRT_BUF_ACTIVE equ 02A5DH ; which terms have non-empty crt buffer (1=not empty) 2A 5E CRT_STOPPED equ 02A5EH ; which terms are in crt flow control state (1=stopped) 2A 5F KB_BUF_READY equ 02A5FH ; which terms have non-empty keyboard buffer (1=not empty) or a continuation pending ; These flags encode important state about line_req processing for each terminal. ; The comment block here is extensive because it took a lot of reverse engineering ; to figure out what they mean (assuming they are right at all). ; ; LREQ_ACTIVE is true from when the END_LINE_REQ command is issued ; (n.b.: don't be mislead by the name; it means the setup for the line ; request is done, and the MXD should start processing the command) until ; the host abandons the command via DELETE_LINE_REQ or the term is reset. ; It is reset to 0 ; set in END_LINE_REQ ; cleared in DELETE_LINE_REQ ; tested in QUERY_LINE_REQ to determine response code ; ; LREQ_ACCEPT should never be true unless LREQ_ACTIVE is also true. When 0 it ; indicates that processing is done, either because of DELETE_LINE_REQ, or ; the user enters a key which terminates processing (CR, s.f. key, or recall). ; When 1 it indicates either that the RX_POLL loop should perform a ; "continuation" command or it should dispatch any key it receives. ; It is reset to 0 ; is set in END_LINE_REQ ; is set in alrd6: to initiate continuation callbacks ; is cleared in DELETE_LINE_REQ ; is cleared in L0D43 << called when RX_POLL processes 0D, or (nn>E6), or ; is tested in main5; if ==0 then MAIN_EVENT, else perform RX_POLL ; is tested in QUERY_LINE_REQ 2A 60 LREQ_ACCEPT equ 02A60H ; which terms have line_req still in progress (active, but not terminated; see QUERY_LINE_REQ) 2A 61 LREQ_ACTIVE equ 02A61H ; which terms are doing LINE_REQUEST (1=active) 2A 62 RESET_MAP equ 02A62H ; which terms have hit RESET (1=reset) 2A 63 HALT_MAP equ 02A63H ; which terms have hit HALT (1=halt) ; The stack is on INTERRUPT, 10 bytes are saved on the stack. Non-interrupt code ; can get as deep as four call/push layers: ; MAIN_EVENT -> call CHECK_HOST -> call COMPRESS -> call TIMED_POLL -> push h ; So 8 bytes for normal code, and 10 for interrupt = 18 bytes. ; Actually, I don't see that 2E53 is used, but one extra byte for the stack doesn't help. 2E 66 TOP_OF_STACK equ 02E66H ; stack occupies 2E65 -> 2E54 2F FD INIT_TYPE equ 02FFDH ; either 0x03 (clear screen) or 0x07 (bell) ; sent after initializing a given terminal ; input port defines 00 00 IN_UART_TXRDY equ 000H ; parallel poll of which UARTs have room in their tx fifo 00 01 IN_2200_STATUS equ 001H ; various status bits ; 0x01 = OBS strobe seen ; 0x02 = CBS strobe seen ; 0x04 = PRIME (reset) strobe seen (cleared by OUT 0) ; 0x08 = high means we are selected and the CPU is waiting for input ; 0x10 = board selected at time of ABS ; 0x20 = AB1 when ABS strobed ; 0x40 = AB2 when ABS strobed ; 0x80 = AB3 when ABS strobed 00 02 IN_OBUS_N equ 002H ; read !OB bus, and clear obs/cbs strobe status 00 03 IN_OBSCBS_ADDR equ 003H ; [7:5] = [AB3:AB1] at time of cbs/obs strobe (active low?) 00 04 IN_UART_RXRDY equ 004H ; parallel poll of which UARTs have received a byte 00 06 IN_UART_DATA equ 006H 00 0E IN_UART_STATUS equ 00EH ; 0x80 = DSR (data set ready) ; 0x40 = BRKDET (break detect) ; 0x20 = FE (framing error) ; 0x10 = OE (overrun error) ; 0x08 = PE (parity error) ; 0x04 = TxEMPTY (the tx fifo is empty and the serializer is done) ; 0x02 = RxRDY (a byte has been received) ; 0x01 = TxRDY (the tx fifo buffer has room for a character) ; output port defines 00 00 OUT_CLR_PRIME equ 000H ; clears reset latch 00 01 OUT_IB_N equ 001H ; drive !IB1-!IB8, pulse IBS 00 11 OUT_IB9_N equ 011H ; same as OUT_IB_N, plus drive IB9 00 02 OUT_PRIME equ 002H ; fires the !PRIME strobe 00 03 OUT_HALT_STEP equ 003H ; one shot strobe 00 05 OUT_UART_SEL equ 005H ; uart chip select 00 06 OUT_UART_DATA equ 006H ; write to selected uart data reg 00 07 OUT_RBI equ 007H ; 0=ready/1=busy; bit n=addr (n+1); bit 7=n/c ; 01=1(kb),02=2(status),04=3(n/c),08=4(prt),10=5(vpcrt),20=6(cmd),40=7(crt) 00 0E OUT_UART_CMD equ 00EH ; write to selected uart command reg ; The RBI register is a bitmap indicating whether a given address offset is ; ready or not. Unfortunately, addr n isn't just bit n. 00 01 RBI_ADDR_1 equ 001H 00 02 RBI_ADDR_2 equ 002H 00 04 RBI_ADDR_3 equ 004H 00 08 RBI_ADDR_4 equ 008H 00 10 RBI_ADDR_5 equ 010H 00 20 RBI_ADDR_6 equ 020H 00 40 RBI_ADDR_7 equ 040H ;------------------------------------------------------------------------------ ; In the code below, Lxxxx are autogenerated labels from the original disassembly. ; Labels use this prefix: ; Lxxxx = address xxxx, target of multiple jumps/calls ; sxxxx = single entry point ; txxxx = local target (no far jumps or calls) ; TBD: ideally, all targets would have a semantic label. ; ; Labels using upper case are call/jump targets from distant code. ; Lowercase labels signify local branch targets from related code. ; This helps better understand the program structure. 00 00 org 00000H coldstart: ; nothing branches here; reset only 519 0000 F3 di RESTART: 521 0001 3E FF mvi a,0FFH 522 0003 D3 07 out OUT_RBI ; all addresses are busy 523 0005 C3 46 01 jmp COLD_INIT ; save A to SHADOW_RBI, then send it to !RBI register RST1: ; depth=1 527 0008 32 57 22 sta SHADOW_RBI 528 000B D3 07 out OUT_RBI 529 000D C9 ret ; mystery -- perhaps it encodes the ROM version number HLF001: 533 000E db 001H 01 534 000F db 03EH 3E ; like RST1, but enable interrupts before returning ; save A to SHADOW_RBI, send it to !RBI register, enable interrupts RST2: ; depth=1 539 0010 32 57 22 sta SHADOW_RBI 540 0013 D3 07 out OUT_RBI 541 0015 FB ei 542 0016 C9 ret 543 0017 00 nop ; check if OBS strobe has arrived. ; if it has, read the data bus, convert to proper polarity, and return. ; if it hasn't and the address is port 6, keep polling for OBS. ; if the address is other than 6, jump to the MAIN_EVENT loop to keep ; the movement of data to the terminals. RST3: ; depth=1 551 0018 DB 01 in IN_2200_STATUS 552 001A 0F rrc 553 001B D2 4A 00 jnc NO_OBS ; branch if no OBS strobe ; fall through ... ; fetch the data that came with OBS/CBS, clear strobes GET_OBS_DAT: ; depth=1 558 001E DB 02 in IN_OBUS_N ; get !OB[7:0], clear OBS/CBS latches 559 0020 2F cma ; true polarity 560 0021 C9 ret 562 0022 00 nop 563 0023 00 nop 564 0024 00 nop 565 0025 00 nop ; emit bell tone to CRT output buffer BEEP: ; depth=2 569 0026 3E 07 mvi a,007H ; fall through ... ; Save A in the crt output buffer ; This buffer isn't circular; no wrapping is done. It is the responsibility ; of the caller to check for space before calling. RST5: ; depth=2 (call here, push h) 576 0028 E5 push h RST5b: ; depth=1 578 0029 2A 57 26 lhld LINE_REQ_BLOCK 579 002C 24 inr h ; next page 580 002D 24 inr h ; next page 581 002E 24 inr h ; page 3 582 002F 2E FE mvi l,0FEH ; 23FE+n*0x0400: CRT buffer put pointer 583 0031 34 inr m ; bump it to next byte 584 0032 6E mov l,m 585 0033 2D dcr l 586 0034 77 mov m,a ; save A at the ptr location before the bump ; that is the put pointer is where the next one will go ; fall through ... ; pop hl and return (used only by the fall-through and A_TO_LINE_REQ) POPRET: 592 0035 E1 pop h 593 0036 C9 ret 594 0037 00 nop ; An interrupt is raised any time the RxRdy line of any of the four UARTs go ; high, indicating a character has been received. Most of the work is done by ; INT_HANDLER. INTERRUPT: 600 0038 F3 di 601 0039 E5 push h 602 003A D5 push d 603 003B C5 push b 604 003C F5 push psw 605 003D 21 00 00 lxi h,0 606 0040 39 dad sp 607 0041 3E 2E mvi a,02EH ; sanity check. if SP is under 2E00, something 608 0043 BC cmp h ; ... is terribly wrong. The stack occupies ... 609 0044 CA D9 09 jz INT_HANDLER ; ... only 0x2E65->0x2E56 (16 bytes). 610 0047 C3 01 00 jmp RESTART ; abort everything and reset ; OBS latch isn't set NO_OBS: 614 004A 07 rlc ; get original IN_2200_STATUS back 615 004B E6 FB ani 0FBH ; clear the reset status bit (PRIME) 616 004D FE D0 cpi 0D0H ; 1101 = check for ab[3:1]=6, board selected; 0000 = no cbs, no obs 617 004F CA 18 00 jz RST3 ; keep polling if addr 6 618 0052 C3 B9 01 jmp MAIN_EVENT ;------------------------------------------------------------------------------ ; zero fill (hl) for de bytes, kind of (starting hl must be even aligned) ; really it fills from (hl) to the end of the current page, and each subsequent ; page for D times. Then the first E bytes of the next page are zero filled. ; But there is only one caller, and the initial HL is page aligned. ZFILL_HL_DE: ; depth=1 626 0055 AF xra a zfill2: 628 0056 77 mov m,a 629 0057 2C inr l 630 0058 77 mov m,a 631 0059 2C inr l 632 005A C2 56 00 jnz zfill2 633 005D 24 inr h ; another page ... 634 005E 15 dcr d ; ... has been filled 635 005F C2 56 00 jnz zfill2 636 0062 43 mov b,e ; ... fall through and do the remaining partial page ; zero fill (hl) with B bytes of memory ZFILL_HL_B: ; depth=1 641 0063 AF xra a ; fall through ... ; fill (hl) with B bytes of memory with the value in A A_FILL_HL_B: ; depth=1 646 0064 77 mov m,a 647 0065 05 dcr b 648 0066 23 inx h 649 0067 C2 64 00 jnz A_FILL_HL_B 650 006A C9 ret ;------------------------------------------------------------------------------ ; fetch the 16b pointer in memory pointed at by (256*B + L) into HL ; compare that to DE. return with larger in HL and smaller in DE. ; (in practice, the caller always sets L to 0x67) HLPTR_MAXMIN_DE: ; depth=1 657 006B 60 mov h,b ; fetch the 16b pointer in memory pointed at by HL into HL. ; compare that to DE. ; return with larger in HL and smaller in DE. HLPTR_MAXMIN_DE2: 663 006C 7E mov a,m ; HL = pointer stored at HL 664 006D 23 inx h 665 006E 6E mov l,m 666 006F 67 mov h,a ; fall through ... ; compare HL and DE ; return with larger in HL, smaller in DE ; a better way (7 bytes instead of 10) would have been: ; mov a,e ; sub l ; mov a,d ; sbc h ; rnc return if hl >= de ; xchg ; ret MAX_HL_MIN_DE: 680 0070 7C mov a,h 681 0071 92 sub d 682 0072 C2 77 00 jnz mhlmde2 683 0075 B5 ora l 684 0076 BB cmp e mhlmde2: 686 0077 D0 rnc 687 0078 EB xchg 688 0079 C9 ret ; save A to the current CRT output buffer. ; it is different than a naked "rst 5" in that it remaps non-printable chars, ; and sets the underline bit if ULINE_FLAG is 0x80. A_TO_CRT_BUF: ; depth=2 (call here, push h) 694 007A E5 push h 695 007B 67 mov h,a 696 007C 3A 59 26 lda ULINE_FLAG 697 007F B4 ora h 698 0080 FE 10 cpi 010H ; is it a control character? 699 0082 D2 87 00 jnc atcb1 ; no 700 0085 3E 2E mvi a,02EH ; yes, use '.' instead atcb1: 702 0087 FE FB cpi 0FBH ; is it a compression escape char? 703 0089 C2 29 00 jnz RST5b ; do RST 5 but don't return 704 008C EF rst 5 ; put FB in the terminal output buffer 705 008D 3E D0 mvi a,0D0H 706 008F C3 29 00 jmp RST5b ; FB D0 sequence means literal "FB" byte ; set terminal response byte to indicate keyboard buffer not empty or line request complete ; (see STATUS_RESPONSE) SET_KB_RDY_STATUS: 711 0092 3E 04 mvi a,004H ; keyboard buffer not empty or line request complete (see STATUS_RESPONSE) ; fall through ... ; set terminal response byte according to reg A (see STATUS_RESPONSE) SET_STATUS_RESP: ; depth=1 716 0094 2E 02 mvi l,002H ; 2n02 = terminal status response byte 717 0096 B6 ora m 718 0097 77 mov m,a ; fall through ... ; indicate that the current terminal is ready to send a status response ; note this also disabled interrupts ADDR2_READY: 724 0098 F3 di 725 0099 3A 57 22 lda SHADOW_RBI 726 009C E6 FD ani NOT(RBI_ADDR_2) ; addr 2 is ready 727 009E CF rst 1 ; save A to SHADOW_RBI, then send it to !RBI register 728 009F C9 ret ; wait for the CPU to poll for input. ; however, if the current addr is not 1, 2, or 6, reset the terminal state ; we have built as we are confused and can't trust it. CPU_POLL_WAIT: ; depth=1 734 00A0 DB 01 in IN_2200_STATUS 735 00A2 E6 08 ani 008H ; test if CPU is waiting for input from us 736 00A4 C0 rnz ; return if it is 737 00A5 DB 01 in IN_2200_STATUS 738 00A7 E6 F3 ani 0F3H ; ignore PRIME and !CPB 739 00A9 FE 50 cpi 050H ; check addr=2, board selected, !CBS, !OBS 740 00AB CA A0 00 jz CPU_POLL_WAIT 741 00AE FE D0 cpi 0D0H ; check addr=6, board selected, !CBS, !OBS 742 00B0 CA A0 00 jz CPU_POLL_WAIT 743 00B3 FE 30 cpi 030H ; check addr=1, board selected, !CBS, !OBS 744 00B5 CA A0 00 jz CPU_POLL_WAIT 745 00B8 C3 B9 01 jmp MAIN_EVENT ; the board isn't selected, AB=3/4/5/7, CBS, or OBS 747 00BB 00 nop ; alignment padding ;------------------------------------------------------------------------------ ; this is the jump table that maps addr 6 CBS command bytes to action; ; it is also used by code at RX_POLL to continue certain commands by branching ; based on the contents of 2n5C (continuation byte). ; see code at IDXJMPA for how this table is addressed. ; ; can't actually put the org here or the assembler chops up the .hex output, ; making comparison to the original more difficult. ; org 000BCH INDTAB: ; this part of the table is used for dispatching command continuations ; TBD: it isn't clear how the ?? offsets are reached 762 00BC dw CHECK_KB_MT_FLAG ; 00BC - ?? (continuation byte = 05EH) C60E 763 00BE dw CURSOR_ON ; 00BE - continuation#1 of END_OF_LINE_REQ D50E 764 00C0 dw BLANK_SPAN ; 00C0 - continuation#2 of END_OF_LINE_REQ F10E 765 00C2 dw DO_REDRAW ; 00C2 - continuation#3 of END_OF_LINE_REQ + cont#1 of EDIT_SF9/10 0D0F 766 00C4 dw CURSOR_TO_START ; 00C4 - continuation#4 of END_OF_LINE_REQ + cont#2 of EDIT_SF9/10 8A0F 767 00C6 dw CHECK_KB_MT_FLAG ; 00C6 - continuation#5 of END_OF_LINE_REQ + cont#3 of EDIT_SF9/10 C60E 768 00C8 dw ERROR_LINE_REQ2 ; 00C8 - continuation of ERROR_LINE_REQ 570B 769 00CA dw CURSOR_ON ; 00CA - continuation#1 of TERMINATE_LINE_REQ D50E 770 00CC dw BLANK_SPAN ; 00CC - continuation#2 of TERMINATE_LINE_REQ F10E 771 00CE dw DO_REDRAW ; 00CE - continuation#3 of TERMINATE_LINE_REQ 0D0F 772 00D0 dw CURSOR_TO_START ; 00DO - continuation#4 of TERMINATE_LINE_REQ 8A0F 773 00D2 dw INJECT_CR ; 00D2 - continuation#5 of TERMINATE_LINE_REQ A10F 774 00D4 dw CHECK_KB_MT_FLAG ; 00D4 - ?? (continuation byte = 06AH) C60E 775 00D6 dw DO_REDRAW ; 00D6 - triggered from alrd6 0D0F 776 00D8 dw SET_LINEREQ_STATUS ; 00D8 - continuation#1 from alrd6 BF0E ; this part of the table is used for io6 CBS(nn) dispatch 779 00DA dw CHECK_HOST ; 00DA - addr 6, CBS 12 - ignore (triple controller, mxe only) EF07 780 00DC dw CHECK_HOST ; 00DC - addr 6, CBS 11 - ignore (triple controller, mxe only) EF07 781 00DE dw TERMINATE_LINE_REQ ; 00DE - addr 6, CBS 10 9C06 782 00E0 dw ERROR_LINE_REQ ; 00E0 - addr 6, CBS 0F A106 783 00E2 dw REQ_PRINT_BUFFER ; 00E2 - addr 6, CBS 0E EA05 784 00E4 dw REQ_CRT_BUFFER ; 00E4 - addr 6, CBS 0D D305 785 00E6 dw ACCEPT_LINE_REQ_DATA ; 00E6 - addr 6, CBS 0C AA02 786 00E8 dw QUERY_LINE_REQ ; 00E8 - addr 6, CBS 0B 4402 787 00EA dw END_OF_LINE_REQ ; 00EA - addr 6, CBS 0A A606 788 00EC dw REFILL_LINE_REQ ; 00EC - addr 6, CBS 09 6C06 789 00EE dw PREFILL_LINE_REQ ; 00DA - addr 6, CBS 08 7506 790 00F0 dw LINE_REQ ; 00F0 - addr 6, CBS 07 1006 791 00F2 dw KEYIN_LINE_REQ ; 00F2 - addr 6, CBS 06 5E07 792 00F4 dw KEYIN_POLL_REQ ; 00F4 - addr 6, CBS 05 6707 793 00F6 dw KB_READY_CHECK ; 00F6 - addr 6, CBS 04 4F07 794 00F8 dw DELETE_LINE_REQ ; 00F8 - addr 6, CBS 03 0F03 795 00FA dw INIT_CUR_TERM ; 00FA - addr 6, CBS 02 0607 796 00FC dw POWER_ON_INIT ; 00FC - addr 6, CBS 01 4801 797 00FE dw CHECK_HOST ; 00FE - addr 6, CBS 00 -- null command EF07 ;------------------------------------------------------------------------------ 01 00 org 00100H ; TERM_LOOKUP requires this be page aligned TERM_PRIORITY: 802 0100 dw 00000H 0000 803 0102 dw 02001H 0120 804 0104 dw 02402H 0224 805 0106 dw 02001H 0120 806 0108 dw 02804H 0428 807 010A dw 02001H 0120 808 010C dw 02402H 0224 809 010E dw 02001H 0120 810 0110 dw 02C08H 082C 811 0112 dw 02C08H 082C 812 0114 dw 02402H 0224 813 0116 dw 02C08H 082C 814 0118 dw 02804H 0428 815 011A dw 02804H 0428 816 011C dw 02402H 0224 817 011E dw 02C08H 082C ; TERM_LOOKUP picks one of four terminals to consider next. ; Eg, it might be a map of uarts which have receive data, or which uarts have ; room in their tx buffer, etc. ; entry: ; a[3:0] contains a bitmap of terminals to consider ; exit: ; c[3:0] contains the chosen terminal as a one-hot bitmap. ; a[3:0] is the same as the original except the chosen bit is cleared. ; b and h both contain a page pointer to the chosen port block. ; l is garbage. ; ; Here is the full mapping of all 16 possible inputs: ; A C B A (exit) ; 0000 -> 00, 00 0000 ; 0001 -> 01, 20 0000 ; 0010 -> 02, 24 0000 ; 0011 -> 01, 20 0010 ; 0100 -> 04, 28 0000 ; 0101 -> 01, 20 0100 ; 0110 -> 02, 24 0100 ; 0111 -> 01, 20 0110 ; 1000 -> 08, 2C 0000 ; 1001 -> 08, 2C 0001 ; 1010 -> 02, 24 1000 ; 1011 -> 08, 2C 0011 ; 1100 -> 04, 28 1000 ; 1101 -> 04, 28 1001 ; 1110 -> 02, 24 1100 ; 1111 -> 08, 2C 0111 ; ; The TERM_PRIORITY table is structured like this. ; The first byte priority encodes which terminal to look at. ; For the first 8 entries, uart 1>2 and 2>3. ; For the next 8 entries, the pattern isn't evident. ; Maybe it is just a scrambled code to try to give them all; equal priority ; over all combinations of active channels. In fact, each channel is given ; top priority in half of all cases where it's is active. ; ; The second byte = (0x20 + 4*(first_byte-1)), and is a pointer to a page in RAM ; where all the terminal-specific state and buffers live. Typically that value ; is saved in H, and other code fiddles with L to develop an (HL) pointer to ; whatever offset is desired. ; uart 1: 0x20xx ; uart 2: 0x24xx ; uart 3: 0x28xx ; uart 4: 0x2Cxx ; TERM_LOOKUP: ; depth=1 867 0120 07 rlc ; double the table index 868 0121 26 01 mvi h,high(TERM_PRIORITY) ; page aligned table 869 0123 6F mov l,a 870 0124 4E mov c,m ; grab 1st byte of entry 871 0125 0F rrc 872 0126 A9 xra c 873 0127 23 inx h 874 0128 46 mov b,m ; grab 2nd byte of entry 875 0129 60 mov h,b ; h points to pages for this port 876 012A C9 ret ;------------------------------------------------------------------------------ ; hl = de-hl, with A destroyed and flags set DE_SUB_HL: ; depth=1 882 012B EB xchg ; hl = hl-de, with A destroyed and flags set HL_SUB_DE: 885 012C 7D mov a,l 886 012D 93 sub e 887 012E 6F mov l,a 888 012F 7C mov a,h 889 0130 9A sbb d 890 0131 67 mov h,a 891 0132 C9 ret ; this is called when the board is addressed at offset 5. ; return if we are in VP mode, otherwise reinitialize structs. SET_VP_MODE: ; depth=1 896 0133 3A 56 22 lda MVP_MODE 897 0136 A7 ana a 898 0137 C8 rz ; return if we were already in vp mode 899 0138 3E CF mvi a,0CFH ; addr 5/6 are ready 900 013A 32 57 22 sta SHADOW_RBI 901 013D AF xra a 902 013E 32 56 22 sta MVP_MODE ; indicate VP mode (AKA boot mode) 903 0141 3E 07 mvi a,007H ; send bell at end of re-init 904 0143 C3 4A 01 jmp pwron2 ; re-init ; only path here is via power on reset. ; "rst 0" would do it too, but that never happens. COLD_INIT: 909 0146 DB 02 in IN_OBUS_N ; side effect: clear OBS/CBS latches ; fall through ... POWER_ON_INIT: 913 0148 3E 03 mvi a,003H ; clear crt at end of init pwron2: 915 014A F3 di 916 014B 31 66 2E lxi sp,TOP_OF_STACK 917 014E 32 FD 2F sta INIT_TYPE ; crt initialization char 918 0151 21 00 20 lxi h,START_OF_RAM 919 0154 11 64 0E lxi d,00E64H ; zero fill 0x2000 to 0x2E63 (going any ; farther would clobber the return stack) 921 0157 CD 55 00 call ZFILL_HL_DE 923 015A 0E 08 mvi c,008H ; process term 4 down to term 1 init_uarts: 925 015C CD C5 06 call INIT_CRT_BUF ; returns with hl=0x2312+n*0x400 926 015F 3A FD 2F lda INIT_TYPE 927 0162 2B dcx h ; 0x2311+n*0x400 928 0163 77 mov m,a ; either clear screen or bell 929 0164 CD 2D 07 call INIT_PRT_BUF 931 0167 79 mov a,c 932 0168 32 5C 22 sta CUR_UART 933 016B D3 05 out OUT_UART_SEL ; the i8251 manual, page 2-12, says ; When power is first applied, the 8251A may come up in the Mode, Sync, ; character or Command format. To guarantee that the device is in the ; Command Instruction format before the Reset command is issued, it is ; safest to execute the worst-case initialization sequence (sync mode ; with two sync characters). Loading three 00Hs consecutive into the ; device with C/!D=1 configures sync operation and writes to dummy 00H ; sync characters. An Internal Reset command (40H) may then be issued ; to return the device to the "Idle" state. 943 016D AF xra a 944 016E D3 0E out OUT_UART_CMD 945 0170 D3 0E out OUT_UART_CMD 946 0172 D3 0E out OUT_UART_CMD 947 0174 D3 0E out OUT_UART_CMD 948 0176 3E 40 mvi a,040H ; internal reset command 949 0178 D3 0E out OUT_UART_CMD 950 017A 3E 5E mvi a,05EH ; mode byte: 1 stop bit, odd parity, 8 bits, 16x baud rate factor 951 017C D3 0E out OUT_UART_CMD 952 017E 3E 37 mvi a,037H ; command: RTS=1, DTR=1, Rx/Tx en, error reset 953 0180 D3 0E out OUT_UART_CMD 954 0182 DB 06 in IN_UART_DATA 955 0184 79 mov a,c 956 0185 0F rrc 957 0186 4F mov c,a 958 0187 D2 5C 01 jnc init_uarts ; process next terminal in sequence 960 018A 3E 87 mvi a,087H ; addr 4,5,6,7 are ready 961 018C D7 rst 2 ; save A to SHADOW_RBI, send it to !RBI register, enable interrupts ; check sum the eprom for integrity. if it fails, transmit a constant stream of ; "D" to the terminal, otherwise jump to the main event loop entry point 965 018D 06 10 mvi b,010H ; do 16 pages 966 018F 21 00 00 lxi h,0000H ; start of EPROM 967 0192 3E FF mvi a,0FFH 968 0194 0E 08 mvi c,008H rom_cksum1: 970 0196 07 rlc ; some kind ... 971 0197 D2 9B 01 jnc rom_cksum2 ; ... of ... 972 019A A9 xra c ; ... lfsr rom_cksum2: 974 019B AE xra m ; checksum the next byte of eprom 975 019C 2C inr l 976 019D C2 96 01 jnz rom_cksum1 ; loop until end of page 977 01A0 24 inr h ; then go to next page 978 01A1 05 dcr b 979 01A2 C2 96 01 jnz rom_cksum1 980 01A5 A7 ana a 981 01A6 CA B9 01 jz MAIN_EVENT ; jump if checksum is 00 (good) 982 01A9 2B dcx h ; \ my guess is developers use RAM not EPROM, 983 01AA AE xra m ; >-- and this fixes the the checksum in RAM 984 01AB 77 mov m,a ; / for the next restart ; emit an endless stream of 'D' characters to indicate an MXD EPROM ; checksum failure cksum_hang: 988 01AC DB 0E in IN_UART_STATUS 989 01AE 0F rrc ; TxRDY status bit 990 01AF D2 AC 01 jnc cksum_hang ; jump if no room in the tx fifo 991 01B2 3E 44 mvi a,'D' 992 01B4 D3 06 out OUT_UART_DATA 993 01B6 C3 AC 01 jmp cksum_hang ;------------------------------------------------------------------------------ ; Main Event Loop ; give up on any operation that was in progress and reset state for next activity MAIN_EVENT: 999 01B9 FB ei 1000 01BA 31 66 2E lxi sp,TOP_OF_STACK ; reset stack pointer 1001 01BD DB 01 in IN_2200_STATUS 1002 01BF FE B0 cpi 0B0H ; addr=5, selected, !cpb, no reset, no cbs, no obs 1003 01C1 C2 CB 01 jnz main2 1004 01C4 DB 01 in IN_2200_STATUS ; double check in case we ... 1005 01C6 FE B0 cpi 0B0H ; ... sampled during transition? (yuk) 1006 01C8 CC 33 01 cz SET_VP_MODE ; if port 5 is addressed, revert to VP mode main2: 1009 01CB CD EF 07 call CHECK_HOST ; check if the host cpu needs attention ; CPU hasn't sent anything and isn't expecting input; ; check if anything can be sent to the terminals 1013 01CE 3A 5E 22 lda HAS_UART_TXD ; bitmap of which ports have data in uart tx buffer 1014 01D1 2F cma 1015 01D2 47 mov b,a ; bitmap, 1=tx buffer is empty 1017 01D3 21 57 2A lxi h,PRT_CAN_TX 1018 01D6 A6 ana m ; 1=(printer is ready to send) & (uart tx buffer is empty) ; I think in some way, PRT_PRIO=0 means deprioritize prt behind crt transfers ; looking at the logic of it... ; ; PRT_PRIO is initialized to 0. ; OK. by default CRT has priority. ; here, if (uart tx buff is empty) & (prt buff ready to send) and [PRT_PRIO] is set, ; the printer is given priority, but [PRT_PRIO] gets cleared for the next time through. ; hmm ; if, during the middle of a partial buffer transfer, a CR is found, it gets set. ; unexpected; this says to give the printer priority the next time we evaluate this again ; I would have thought it would clear it; sending a is a high latency operation on a printer, ; so the printer should have lower priority for awhile. ; when we drain the end of a crt/prt buffer, it gets set if ; (prt buff ready to send) && (we haven't sent a CR since the last time we emptied the crt/prt buffer) ; unexpected; give the printer priority next time through this loop. I guess it prevents printer starvation. 1035 01D7 21 62 26 lxi h,PRT_PRIO ; 1=give prt traffic priority over crt traffic 1036 01DA A6 ana m 1037 01DB 57 mov d,a ; d = (uart tx buff empty) & (prt buff ready to send) & [PRT_PRIO] ; calling this value "old_d" later 1039 01DC AE xra m ; if (uart tx buff empty) & (prt buff ready to send), ... 1040 01DD 77 mov m,a ; ... clear the chosen bits in [PRT_PRIO] 1041 01DE 7A mov a,d 1042 01DF 2F cma ; 1=prt buff is empty, or is flow controlled, or uart tx buff isn't empty, or ~[PRT_PRIO] 1044 01E0 21 5C 2A lxi h,CRT_CAN_TX 1045 01E3 A6 ana m 1046 01E4 5F mov e,a ; e = [CRT_CAN_TX] & ~old_d = [CRT_CAN_TX] & (don't use prt buff) ; TX_POLL looks at 'e'; if the current term bit is 1, ; it moves crt data, else prt data ; the following logic is highly convoluted and I'm not sure if I didn't make a mistake, ; but I've attempted to reduce, logically, what is going on in the comments 1052 01E5 B2 ora d 1053 01E6 57 mov d,a ; d = e | old_d = ([CRT_CAN_TX] & ~old_d) | old_d ; = [CRT_CAN_TX] | old_d 1055 01E7 2F cma ; a = ~([CRT_CAN_TX] | old_d) = ~[CRT_CAN_TX] & ~old_d 1056 01E8 21 57 2A lxi h,PRT_CAN_TX 1057 01EB A6 ana m ; a = [PRT_CAN_TX] & ~[CRT_CAN_TX] & ~old_d 1058 01EC B2 ora d ; a = ([PRT_CAN_TX] & ~[CRT_CAN_TX] & ~old_d) | [CRT_CAN_TX] | old_d ; = ([PRT_CAN_TX] & ~[CRT_CAN_TX]) | [CRT_CAN_TX] | old_d ; = [PRT_CAN_TX] | [CRT_CAN_TX] | old_d ; = [PRT_CAN_TX] | [CRT_CAN_TX] | ((uart tx buffer empty) & [PRT_CAN_TX] & [PRT_PRIO]) ; = [PRT_CAN_TX] | [CRT_CAN_TX] | ((uart tx buffer empty) & [PRT_PRIO]) ; that third term seems to be extraneous 1064 01ED A0 ana b ; if tx buffer isn't empty, don't even think about adding more (trumps everything else) ; the overall effect, convoluted as it is, comes down to: ; A is the set of terminals to consider in the TX_POLL loop ; reg A = ([CRT_CAN_TX] | [PRT_CAN_TX]) & (tx uart buffer is empty) ; reg E = map of buffers to read from; 1=crt, 0=prt 1070 01EE C4 10 04 cnz TX_POLL ; move data from crt or prt buffer to tx buffer for all terms set in A reg 1072 01F1 CD EF 07 call CHECK_HOST ; check if the host cpu needs attention 1074 01F4 DB 00 in IN_UART_TXRDY ; only place this is read 1075 01F6 2F cma 1076 01F7 E6 0F ani 00FH ; '1' bits indicate which terms are not ready to tx 1077 01F9 57 mov d,a 1078 01FA 3A 5E 22 lda HAS_UART_TXD ; bitmap of which ports have data in uart tx buffer 1079 01FD A2 ana d ; 1=term has buffer data, not flow controlled, and uart is ready to accept a byte 1080 01FE CA 33 02 jz main5 ; jump if no data to send or UART tx buff is full 1081 0201 57 mov d,a ; fall through ... ; reg d has a bitmap of which UARTs are ready to accept tx data ; pick a uart, and transfer one byte each to the corresponding uart. txd_lookup: ; this code is similar to TERM_LOOKUP 1088 0202 07 rlc ; double the index 1089 0203 26 01 mvi h,high(TERM_PRIORITY) ; page aligned table 1090 0205 6F mov l,a 1091 0206 7E mov a,m ; grab 1st byte of entry 1092 0207 4F mov c,a 1093 0208 32 5C 22 sta CUR_UART ; remember which TX port we chose to work on 1094 020B D3 05 out OUT_UART_SEL 1095 020D AA xra d ; clear the bit of the chosen terminal 1096 020E 57 mov d,a ; d now contains remaining ports needing attention 1097 020F 23 inx h 1098 0210 5E mov e,m ; grab 2nd byte of entry 1099 0211 63 mov h,e ; term memory page pointer, 20/24/28/2C 1101 0212 2E 03 mvi l,003H ; 2n03 = tx byte count 1102 0214 35 dcr m ; number of bytes left to send 1103 0215 23 inx h ; 2n04 1104 0216 C2 29 02 jnz main3 ; jump if there are more bytes after this one ; indicate chosen term's uart tx buffer is empty 1107 0219 3A 5E 22 lda HAS_UART_TXD ; bitmap of which ports have data in uart tx buffer 1108 021C 2F cma 1109 021D B1 ora c ; clear the bit for this term 1110 021E 2F cma 1111 021F 32 5E 22 sta HAS_UART_TXD 1113 0222 7E mov a,m ; read the tx buffer get pointer 1114 0223 3C inr a ; preincrement 1115 0224 75 mov m,l ; [2n04]=0x04; reset the uart get pointer 1116 0225 6F mov l,a ; hl is the pointer just before it wrapped 1117 0226 C3 2B 02 jmp main4 main3: 1120 0229 34 inr m ; bump pointer [2n04] 1121 022A 6E mov l,m ; hl points to next char main4: 1123 022B 7E mov a,m ; get next byte to xmit 1124 022C D3 06 out OUT_UART_DATA ; this is the only spot were data is xmit'd (except cksum_hang) 1125 022E AF xra a 1126 022F B2 ora d ; d contains map of tx ports needing service 1127 0230 C2 02 02 jnz txd_lookup ; jump if any other tx ports need servicing ; fall through ... main5: 1131 0233 CD EF 07 call CHECK_HOST ; keep looping while no active keys ready (KB_BUF_READY), ; or LINE_REQ is not active, ; or LINE_REQ is active but LREQ_ACCEPT is false (because 0D, s.f. key, or recall was pressed) 1136 0236 21 5F 2A lxi h,KB_BUF_READY 1137 0239 7E mov a,m 1138 023A 23 inx h ; LREQ_ACCEPT 1139 023B A6 ana m 1140 023C E6 0F ani 00FH 1141 023E CA B9 01 jz MAIN_EVENT ; keep looping while no active keys ready (KB_BUF_READY), or no LINE_REQ is outstanding, or line_req is but L0D43 clared LREQ_ACCEPT 1142 0241 C3 84 0B jmp RX_POLL ; act on available keystrokes ;------------------------------------------------------------------------------ ; When a CBS(0B) command is received, the controller responds with one of the ; following IBS values: ; 00h -- No line request in progress ; 01h -- line request still in progress ; 0Dh -- line request terminated by CR ; FFh -- Recall key pressed (see note) ; ENDI(XX) -- S.F. key pressed ; Note on recall: ; After the FF, the controller may send one or more bytes to the MVP. ; Each time the MVP sets CPB ready, the controller will send one more data ; byte with IBS. These are the characters from the entered text, read ; from right to left, beginning with the cursor position. The beginning of ; the buffer is indicated by ENDI. This sequence ends whenever the MVP ; stops setting CPB ready, sends OBS or CBS, or switches address. The ; controller should not clear the buffer when the 2200 has read all the ; bytes contained therein. Unfortunately, the 2200 takes some shortcuts ; for expediency and may reread the buffer later. ; ; Following the query, the MVP may do one of the following: ; 1. Nothing (another query later) ; 2. Delete line request (usually for HALT, and SF keys without parameters) ; 3. Refill -- this is more data to be merged to the present line request, ; as though the operator typed it (used for recall and DEFFN' quotes). ; Then End Line Request. ; 4. Terminate Line Request -- used to implement DEFFN' HEX(0D) ; 5. Error Line Request -- this beeps an error and continues the line request ; 6. Ask for data QUERY_LINE_REQ: 1174 0244 3A 5A 22 lda CUR_TERM 1175 0247 4F mov c,a ; LREQ_ACTIVE LREQ_ACCEPT return meaning ; ----------- ----------- --------- ---------------------------- ; 0 x -> 00 no line request in progress ; 1 0 -> 01 line request still in progress ; 1 1 && last key was 0xFE/0xFF -> FE/FF recall key pressed ; 1 1 && last key was 0x0D -> 0D carriage return pressed 1182 0248 21 61 2A lxi h,LREQ_ACTIVE ; map of line request in progress 1183 024B A6 ana m 1184 024C CA E6 07 jz SEND_A_IBS ; return 00 if no line request in progress 1185 024F 2B dcx h ; LREQ_ACCEPT 1186 0250 AE xra m 1187 0251 A1 ana c 1188 0252 06 01 mvi b,001H 1189 0254 CA E7 07 jz SEND_B_IBS ; return 01 to indicate line request still in progress 1190 0257 2A 58 22 lhld CUR_BLOCK ; 2000/2400/2800/2C00 1191 025A 22 57 26 shld LINE_REQ_BLOCK 1192 025D 44 mov b,h 1193 025E 4E mov c,m ; 2n00: one-hot encoding of current term 1195 025F 2E 62 mvi l,062H 1196 0261 5E mov e,m ; e = [2n62] (keypress value) 1197 0262 3E FD mvi a,0FDH 1198 0264 BB cmp e ; 0xFD - e 1199 0265 2B dcx h 1200 0266 7E mov a,m ; a = [2n61] (key prefix byte) 1201 0267 D2 93 07 jnc RKBE2 ; jump if E is <= 0xFD into RELAY_KB_ENTRY code ; note we are sending either 0xFE or 0xFF, and the docs say 0xFF means recall key pressed ; FE=dead key, FF=special handling? 1204 026A CD E5 07 call SEND_E_IBS ; see "note on recall" above. send contents of line_req from current cursor position ; backwards to start of buffer (with that last byte sent with ENDI). ; that means this code thinks [2n5F/60] is the current position, not [2n69/6A]. ; TBD to reconcile these ideas. ; de = [2n5F/60] (pointer to end of prefill data) 1211 026D 44 mov b,h 1212 026E 2B dcx h 1213 026F 5E mov e,m 1214 0270 2B dcx h 1215 0271 56 mov d,m 1217 0272 2E 6B mvi l,06BH ; 2n6B: start of line request buffer qlr1: 1219 0274 DB 01 in IN_2200_STATUS 1220 0276 EE D8 xri 0D8H ; 1101 1000 1221 0278 E6 FB ani 0FBH ; 1111 1011 (ignore PRIME) 1222 027A C2 94 02 jnz qlr3 ; jump if not addr 6, or not selected, or CPU is not waiting for input, or CBS, or OBS 1223 027D 7B mov a,e 1224 027E AD xra l 1225 027F C2 8C 02 jnz qlr2 ; jump if de != hl (lsb check) 1226 0282 B2 ora d 1227 0283 AC xra h 1228 0284 C2 8C 02 jnz qlr2 ; jump if de != hl (msb check) 1229 0287 D3 11 out OUT_IB9_N ; sending first char, so mark it with ENDI 1230 0289 C3 74 02 jmp qlr1 qlr2: 1232 028C 1B dcx d ; predecrement pointer (we are reading out in reverse order) 1233 028D 1A ldax d ; get next byte from buffer 1234 028E 2F cma 1235 028F D3 01 out OUT_IB_N ; send it w/o ENDI 1236 0291 C3 74 02 jmp qlr1 qlr3: 1238 0294 FE 08 cpi 008H ; 0000 1000 1239 0296 CA 74 02 jz qlr1 ; jump if port=6, board selected, CPU is not waiting for input, not CBS, and not OBS ; after recall press, we've sent all the input buffer data to the host ; (in reverse order) ; [2n5D/5E] = de = 2n6B (start of buffer) I believe 1244 0299 2E 5D mvi l,05DH 1245 029B 72 mov m,d 1246 029C 23 inx h 1247 029D 73 mov m,e ; [2n5F/60] = de = 2n6B (start of buffer) 1250 029E 23 inx h 1251 029F 72 mov m,d 1252 02A0 23 inx h 1253 02A1 73 mov m,e ; [2n67/68] (last byte used) = de = 2n6B (start of buffer) 1256 02A2 2E 67 mvi l,067H 1257 02A4 72 mov m,d 1258 02A5 23 inx h 1259 02A6 73 mov m,e 1260 02A7 C3 EF 07 jmp CHECK_HOST ; end of QUERY_LINE_REQUEST command ; port 6 ACCEPT LINE REQUEST DATA, sequence CBS(0C) ; When a CBS(0C) is received after a line request has been completed, the ; controller will send the data. It should only be issued after a query has ; shown that the line is complete. ; ; The controller sends the data if any, then an ENDI as terminator. If the ; ENDI is zero, the line request is complete; if 01h, the controller needs ; more time to finish updating the screen. ACCEPT_LINE_REQ_DATA: 1271 02AA 2A 58 22 lhld CUR_BLOCK ; 2000/2400/2800/2C00 1272 02AD 22 57 26 shld LINE_REQ_BLOCK 1273 02B0 2E 67 mvi l,067H 1274 02B2 56 mov d,m 1275 02B3 23 inx h 1276 02B4 5E mov e,m ; de = [2n67/68] (last byte used) 1277 02B5 2E 6B mvi l,06BH ; 2n6B = start of line_req buffer 1278 02B7 CD 2B 01 call DE_SUB_HL ; hl = number of bytes used, de=2n6B 1279 02BA EB xchg ; de = number of bytes used, hl=2n6B ; transfer the contents of the line_req buffer 1282 02BB 44 mov b,h 1283 02BC 14 inr d ; pre-increment page counter because loop uses pre-decrement to count 1284 02BD 1C inr e ; pre-increment byte counter because loop uses pre-decrement to count 1285 02BE C3 C9 02 jmp alrd2 alrd1: 1287 02C1 CD A0 00 call CPU_POLL_WAIT ; wait for host CPU to ask for input 1288 02C4 7E mov a,m ; read next line_req buffer byte 1289 02C5 2F cma 1290 02C6 D3 01 out OUT_IB_N ; send it 1291 02C8 23 inx h alrd2: 1293 02C9 1D dcr e 1294 02CA C2 C1 02 jnz alrd1 1295 02CD 15 dcr d 1296 02CE C2 C1 02 jnz alrd1 ; after transferring contents of buffer, send flag byte with IB9 1299 02D1 CD A0 00 call CPU_POLL_WAIT 1300 02D4 60 mov h,b 1301 02D5 2E 63 mvi l,063H 1302 02D7 7E mov a,m ; [2n63] = line_req flag byte 1303 02D8 4F mov c,a 1304 02D9 E6 20 ani 020H ; test keyin_line_req flag 1305 02DB 2E 67 mvi l,067H ; 2n67 (line_req last byte used) 1306 02DD CA E2 02 jz alrd3 1307 02E0 2E 65 mvi l,065H ; 2n65 = (line_req end of buffer) alrd3: 1309 02E2 56 mov d,m 1310 02E3 23 inx h 1311 02E4 5E mov e,m ; de = [2n65/66] or [2n67/68] 1313 02E5 3A 5A 2A lda WANGTERM 1314 02E8 60 mov h,b 1315 02E9 2E 00 mvi l,000H ; 2n00: this term mask 1316 02EB A6 ana m 1317 02EC CA FA 02 jz alrd4 ; jump if not a wang-compatible terminal ; send = stop cursor blinking (blinking means input mode) 1320 02EF 3E FB mvi a,0FBH 1321 02F1 EF rst 5 1322 02F2 3E F8 mvi a,0F8H 1323 02F4 EF rst 5 1325 02F5 79 mov a,c ; restore line_req flag byte 1326 02F6 07 rlc 1327 02F7 DA 21 03 jc alrd5 ; jump if underline bit set alrd4: 1329 02FA 3E FF mvi a,0FFH 1330 02FC D3 11 out OUT_IB9_N ; 00 w/ENDI means the request is complete 1331 02FE CD 74 03 call CURS_TO_DE ; fall through ... SEND_CR_LF: 1335 0301 3E 0D mvi a,00DH ; carriage return 1336 0303 EF rst 5 ; save it in the terminal output buffer 1337 0304 3E 0A mvi a,00AH ; linefeed 1338 0306 EF rst 5 ; save it in the terminal output buffer 1339 0307 60 mov h,b 1340 0308 2E 00 mvi l,000H ; 2n00: this term mask 1341 030A 4E mov c,m 1342 030B CD 83 08 call TEST_CRT_BUF_ACTIVE 1343 030E FB ei ; fall through ... ; port 6 DELETE CURRENT LINE REQUEST CBS(03) ; This command causes a pending line request and input buffers of the current ; terminal to be cleared (used at HALT and with special function keys). DELETE_LINE_REQ: 1350 030F 79 mov a,c ; C=one-hot map of current terminal 1351 0310 2F cma 1352 0311 57 mov d,a ; D=A=one cold map of current terminal 1353 0312 21 60 2A lxi h,LREQ_ACCEPT 1354 0315 A6 ana m ; clear bit corresponding to current term 1355 0316 77 mov m,a 1356 0317 23 inx h ; LREQ_ACTIVE 1357 0318 7E mov a,m 1358 0319 A2 ana d ; clear LREQ_ACTIVE bit 1359 031A 77 mov m,a ; fall through ... ; clear the line_req flag byte CLEAR_LR_FLAGS: 1364 031B 60 mov h,b 1365 031C 2E 63 mvi l,063H ; 2n63 = line_req flag byte 1366 031E 36 00 mvi m,000H ; clear flags 1367 0320 C9 ret ; more of ACCEPT_LINE_REQ_DATA command. ; the command is complete and the data returned to the host, but the request ; used underlines. redraw the buffer without the underscores, and clear any ; underscores which came after the end of the input. alrd5: 1374 0321 2E 63 mvi l,063H ; 2n63: line_req flag byte 1375 0323 3F cmc ; we jumped to alrd5 because carry was set, so this clears it 1376 0324 1F rar ; effectely clears the msb (preceding code did rlc) 1377 0325 77 mov m,a ; [2n63] = clear underline bit ; [2n5F/60](redraw end) = de = [2n65/66](end of buffer) or [2n67/68](last byte used) ; depending on keyin_line_req flag (see alrd2) 1381 0326 2E 5F mvi l,05FH 1382 0328 72 mov m,d 1383 0329 23 inx h 1384 032A 73 mov m,e ; hl = [2n67/68](last byte used) 1387 032B 2E 67 mvi l,067H 1388 032D 7E mov a,m 1389 032E 23 inx h 1390 032F 6E mov l,m 1391 0330 67 mov h,a ; loop filling (hl++) with spaces until hl=de ; if keyin_line_req, fill with spaces from (last byte used) to (end of buffer) ; if !keyin_line_req, fill with spaces from (last byte used) to (last byte used) 1396 0331 0E 20 mvi c,' ' ; fill with spaces 1397 0333 2B dcx h ; predecrement because the loop preincrements alrd6: 1399 0334 23 inx h 1400 0335 71 mov m,c 1401 0336 7D mov a,l 1402 0337 AB xra e 1403 0338 C2 34 03 jnz alrd6 1404 033B B4 ora h 1405 033C BA cmp d 1406 033D C2 34 03 jnz alrd6 ; rewrite the contents of the buffer, without underline 1409 0340 CD 70 03 call CURS_TO_SOL ; cursor to start of line 1410 0343 AF xra a 1411 0344 32 59 26 sta ULINE_FLAG ; no underline 1412 0347 CD 13 0F call ALRD_REDRAW ; copy chars from line_req buffer to crt buffer, from sol to (redraw end) ; ALRD_REDRAW bumps the [2n5C] continuation byte if it completed all work ; clear any continuations, but if one was pending, ; signal not everything has been redrawn yet 1417 034A 60 mov h,b 1418 034B 2E 5C mvi l,05CH 1419 034D AF xra a 1420 034E BE cmp m 1421 034F 77 mov m,a ; [2n5C] = 00 ; no continuation 1422 0350 C2 69 03 jnz alrd7 ; jump if ALRD_REDRAW completed its work ; schedule ALRD_REDRAW to be called later to complete the remaining work 1425 0353 3E FE mvi a,0FEH 1426 0355 D3 11 out OUT_IB9_N ; 01 w/ENDI means the request needs more time to redraw 1427 0357 36 6B mvi m,06BH ; [2n5C](continuation byte) = 0x6B -> INDTAB 00D6 entry DO_REDRAW 1429 0359 2E 00 mvi l,000H 1430 035B 4E mov c,m ; c=[2n00] (this term mask) 1431 035C CD BC 06 call UPDATE_KB_BUF_READY ; set LREQ_ACCEPT to ensure the ALRD_REDRAW gets called 1434 035F 23 inx h ; LREQ_ACCEPT 1435 0360 F3 di 1436 0361 7E mov a,m 1437 0362 B1 ora c 1438 0363 77 mov m,a ; set "this term" bit in [LREQ_ACCEPT] 1440 0364 CD 83 08 call TEST_CRT_BUF_ACTIVE ; conditionally set CRT_BUF_ACTIVE bit for the current xterm 1441 0367 FB ei 1442 0368 C9 ret alrd7: 1445 0369 3E FF mvi a,0FFH 1446 036B D3 11 out OUT_IB9_N ; 00 w/ENDI means the request is complete 1447 036D C3 01 03 jmp SEND_CR_LF ;------------------------------------------------------------------------------ ; EDIT subroutine: move cursor to start of line (non-destructive) CURS_TO_SOL: ; depth=2 ; hl = 2n6B = start of line_req buffer 1453 0370 60 mov h,b 1454 0371 2E 6B mvi l,06BH ; fall through ... ; EDIT subroutine: move cursor to the buffer location in HL CURS_TO_HL: ; depth=2 1459 0373 EB xchg ; fall through ... ; EDIT subroutine: move cursor to the buffer location in DE CURS_TO_DE: ; depth=2 (call here, rst 5) 1464 0374 60 mov h,b 1465 0375 2E 6A mvi l,06AH ; 2n6A 1466 0377 7B mov a,e ; note: pointers are big endian order 1467 0378 96 sub m 1468 0379 73 mov m,e 1469 037A 5F mov e,a 1470 037B 2B dcx h ; 2n69 1471 037C 7A mov a,d 1472 037D 9E sbb m 1473 037E 72 mov m,d 1474 037F 57 mov d,a ; here: [2n69/6A]=updated cursor; de = new_location - prev_location CTDE1: 1477 0380 60 mov h,b 1478 0381 2E 64 mvi l,064H ; 2n64: line_req cursor column 1479 0383 6E mov l,m 1480 0384 EB xchg ; hl=cursor delta; e=starting cursor column 1481 0385 AF xra a 1482 0386 B4 ora h 1483 0387 FA B3 03 jm ctde6 ; negative delta: cursor moves (logically) left 1484 038A 7D mov a,l 1485 038B CA 92 03 jz ctde2 ; jump if delta<256 ; h was 1, meaning delta of 256 plus whatever was in L ; 256 = 3*80+16, which means go down three rows and right 16 1488 038E C6 10 adi 010H ; add 16 1489 0390 26 03 mvi h,3 ; counts required linefeeds ctde2: 1491 0392 24 inr h ; one more line feed needed 1492 0393 D6 50 sui 80 ; reduce delta by 80 1493 0395 D2 92 03 jnc ctde2 1494 0398 C6 50 adi 80 ; restore quotient/remainder after going one too far 1495 039A 25 dcr h 1496 039B 6F mov l,a ; remainder 1497 039C 83 add e ; add original cursor column 1498 039D 5F mov e,a ; e = new cursor column 1499 039E D6 50 sui 80 1500 03A0 DA A5 03 jc ctde3 ; didn't wrap past end of line 1501 03A3 24 inr h ; need one more linefeed 1502 03A4 5F mov e,a ; e=e-80 (or e mod 80, i.e. wrap it) ctde3: 1504 03A5 24 inr h ; bump by one to compensate for initial dcr at ctde5 1505 03A6 C3 AC 03 jmp ctde5 ctde4: 1508 03A9 3E 0A mvi a,00AH ; linefeed 1509 03AB EF rst 5 ; send to terminal output buffer ctde5: 1511 03AC 25 dcr h 1512 03AD C2 A9 03 jnz ctde4 1513 03B0 C3 DA 03 jmp ctde10 ; horiz cursor motion to target column ; negative cursor delta; logically moving the cursor left. ; this closely follows the logic of the move right code above. ctde6: 1518 03B3 24 inr h ; delta += 256? 1519 03B4 7D mov a,l 1520 03B5 CA BC 03 jz ctde7 ; jump if H was 0xFF ; moving left 256 plus whatever is in L 1522 03B8 26 03 mvi h,3 ; three lines up... 1523 03BA C6 F0 adi 0F0H ; and 16 chars left (-256 = -3*80 - 16) ctde7: 1525 03BC 24 inr h 1526 03BD C6 50 adi 80 1527 03BF D2 BC 03 jnc ctde7 1528 03C2 25 dcr h 1529 03C3 6F mov l,a 1530 03C4 83 add e 1531 03C5 5F mov e,a 1532 03C6 D6 50 sui 80 1533 03C8 D2 CD 03 jnc ctde8 1534 03CB 7B mov a,e 1535 03CC 24 inr h ctde8: 1537 03CD 5F mov e,a 1538 03CE 7C mov a,h 1539 03CF A7 ana a 1540 03D0 CA DA 03 jz ctde10 ; horiz cursor motion to target column ctde9: 1542 03D3 3E 0C mvi a,00CH ; cursor up 1543 03D5 EF rst 5 ; save in the terminal output buffer 1544 03D6 25 dcr h ; prev page 1545 03D7 C2 D3 03 jnz ctde9 ; fall through ... ; adjust the horizontal cursor position right to the target position ctde10: 1550 03DA 7D mov a,l 1551 03DB FE 4B cpi 75 1552 03DD D2 EF 03 jnc ctde11 ; large cursor motion 1553 03E0 A7 ana a 1554 03E1 CA F9 03 jz ctde13 ; cursor already in the correct column 1555 03E4 3E FB mvi a,0FBH ; send a char compression token 1556 03E6 EF rst 5 ; push compression sequence token 1557 03E7 7D mov a,l 1558 03E8 EF rst 5 ; push repetition count 1559 03E9 3E 09 mvi a,009H ; nondestructive space 1560 03EB EF rst 5 ; save cursor motion in the terminal output buffer 1561 03EC C3 F9 03 jmp ctde13 ; moving >75 chars; instead move cursor left and wrap to the right side ctde11: 1565 03EF C6 B0 adi 0B0H ; -80 1566 03F1 6F mov l,a ctde12: 1568 03F2 3E 08 mvi a,008H ; move to the right by moving left and wrapping 1569 03F4 EF rst 5 ; save in the terminal output buffer 1570 03F5 2C inr l 1571 03F6 C2 F2 03 jnz ctde12 ctde13: 1573 03F9 60 mov h,b 1574 03FA 2E 64 mvi l,064H ; 2n64: line_req cursor column 1575 03FC 73 mov m,e 1576 03FD C9 ret ;------------------------------------------------------------------------------ ; This code block copies data from the print or crt buffer of the terminal to ; the uart tx buffer for each terminal. The entry point is TX_POLL. txp1: 1583 03FE CD F6 04 call CR_LIMIT_XFER ; clamp xfer count if a CR is in the xfer window ; set the cur term bits in [PRT_PRIO] and [SENT_CR] if CR is found txp2: 1586 0401 CD 1E 05 call MOV2TXBUF ; move data from crt/print buffer to tx buffer txp3: 1588 0404 2E FF mvi l,0FFH ; hl = (22FF|23FF)+n*0x400 1589 0406 73 mov m,e ; update get pointer 1590 0407 3A 56 26 lda PENDING_TERMS ; retrieve map of which terms still need servicing 1591 040A A7 ana a 1592 040B C8 rz ; return when no terms left to service 1593 040C 2A 5D 26 lhld TX_SERVICE ; we don't care about h; l=xfer crt/printer data map 1594 040F 5D mov e,l ; move it to e, where TX_POLL requires it ; fall through ... ; This is the entry point to this section of code, from MAIN_EVENT. ; entry: ; a = a bitmap of which terminals we should attempt to service ; e = term bitmap; 0=send crt data, 1=send printer data ; for each terminal in the A map, up to 16 bytes will be transferred from the ; chosen crt or prt buffer to the corresonding uart tx buffer. TX_POLL: ; depth=2 (call here, eg: call to TERM_LOOKUP) 1604 0410 CD 20 01 call TERM_LOOKUP ; c=chosen port bitmap, b=h=page0 ptr, a=remaining ports 1605 0413 32 56 26 sta PENDING_TERMS ; which terms are waiting to xfer data to their tx buffer 1606 0416 54 mov d,h ; 20/24/28/2C 1607 0417 6B mov l,e ; bitmap indicating crt/prt data to xfer 1608 0418 22 5D 26 shld TX_SERVICE ; only place this is written! lsb=crt/prt map, msb=page0 of chosen term 1610 041B 2E 03 mvi l,003H ; 2n03 1611 041D 22 5F 26 shld CUR_TX_BUF ; points to term's ram block ; will transfer data to the tx buffer shortly; mark it non-empty 1614 0420 3A 5E 22 lda HAS_UART_TXD ; bitmap of which ports have data in uart tx buffer 1615 0423 B1 ora c 1616 0424 32 5E 22 sta HAS_UART_TXD ; this uart tx buffer has data to send ; if the target (1=crt/0=printer) has changed, inject routing bytes. ; note: in e, target is 1=crt/0=printer ; in TX_RECEIVER, current target is 0=crt/1=printer ; that's why (xra e/jz txp8) uses jz. a bit confusing, but it works. 1622 0427 3A 61 26 lda TX_RECEIVER 1623 042A AB xra e 1624 042B A1 ana c ; isolate this terminal 1625 042C CA 86 04 jz txp8 ; reset the uart tx buffer 1628 042F 36 00 mvi m,000H ; [2n03] = 0x00 1629 0431 23 inx h 1630 0432 75 mov m,l ; [2n04] = 0x04 (get pointer runs one behind next byte to fetch) 1632 0433 06 10 mvi b,010H ; limit of the number of bytes to tranfer to uart tx buffer 1633 0435 7B mov a,e 1634 0436 A1 ana c 1635 0437 CA AA 04 jz txp10 ; jump to printer path 1637 043A 24 inr h ; 0x21xx+n*0x400 to indicate crt path (LIMIT_TX_XFER does h+=2) 1638 043B CD E7 04 call LIMIT_TX_XFER ; de=get ptr, b=byte count to transfer 1639 043E DA FE 03 jc txp1 ; jump if this doesn't empty the source buffer ; fall through ... ; this transfer will empty the remaining bytes of the crt or ptr ; buffer, so change state related to that event txp4: 1645 0441 3A 57 2A lda PRT_CAN_TX 1646 0444 A1 ana c 1647 0445 6F mov l,a ; l = nonzero if prt buff has data and isn't flow-controlled ; see the description of PRT_PRIO where it is declared. 1650 0446 3A 63 26 lda SENT_CR ; 1=a has been sent recently 1651 0449 2F cma 1652 044A A5 ana l ; a = ~(CR has been sent recently) & (this_prt_can_tx) ; if A is non-zero, set [PRT_PRIO], else leave [PRT_PRIO] alone. always set [SENT_CR] 1654 044B CD 10 05 call S0510 ; returns with hl=SENT_CR, a=[SENT_CR] 1655 044E A9 xra c 1656 044F 77 mov m,a ; [SENT_CR] clear bit corrsponding to chosen term 1658 0450 21 5D 2A lxi h,CRT_BUF_ACTIVE 1659 0453 CD 33 05 call MARK_BUF_EMPTY 1660 0456 CA 63 04 jz txp5 ; jump if CRT_STAT_PEND bit for this term is false ; clear the CRT_STAT_PEND bit corresponding to this terminal 1663 0459 AE xra m 1664 045A 77 mov m,a 1666 045B 2A 5D 26 lhld TX_SERVICE ; fetch page0 of term we are servicing for tx 1667 045E 3E 02 mvi a,002H ; crt buffer became empty (see STATUS_RESPONSE) 1668 0460 CD 94 00 call SET_STATUS_RESP txp5: 1670 0463 2A 5D 26 lhld TX_SERVICE ; fetch page0 of term we are servicing for tx 1671 0466 2E 01 mvi l,001H ; 2n01: per-term rbi status 1672 0468 7E mov a,m 1673 0469 E6 AF ani 0AFH ; 10101111: addr 5&7 are ready 1674 046B 77 mov m,a 1675 046C FB ei 1676 046D 3A 5A 22 lda CUR_TERM 1677 0470 B9 cmp c 1678 0471 C2 7B 04 jnz txp6 ; jump if this isn't the currently selected term 1679 0474 F3 di 1680 0475 3A 57 22 lda SHADOW_RBI 1681 0478 E6 AF ani 0AFH ; addr 5&7 are ready 1682 047A D7 rst 2 ; save A to SHADOW_RBI, send it to !RBI register, enable interrupts txp6: 1684 047B CD 1E 05 call MOV2TXBUF ; move crt data to tx buff 1685 047E 1E 00 mvi e,000H ; lsb of start of crt buffer (0x2300+n*0x400) ; fall through ... txp7: 1689 0480 2E FE mvi l,0FEH ; (22FE/23FE) depending how we arrived here 1690 0482 73 mov m,e ; update put pointer 1691 0483 C3 04 04 jmp txp3 ; inject the crt/printer routing prefix, as the target has changed txp8: ; flip the byte stream target, crt <-> printer 1696 0486 3A 61 26 lda TX_RECEIVER 1697 0489 A9 xra c 1698 048A 32 61 26 sta TX_RECEIVER ; only place this is written (except the init to 00 on reset) ; initialize the tx buffer 1701 048D 36 02 mvi m,002H ; [2n03] = 2 = bytes in tx buffer (account for routing sequence) 1702 048F 06 0E mvi b,00EH ; we used up two bytes, so allow only 14 bytes to prevent overflow of 16b uart tx buffer 1703 0491 23 inx h ; 2n04 1704 0492 75 mov m,l ; [2n04]=04 (start of tx buffer-1) 1705 0493 23 inx h ; before sending a chunk ('b' bytes) of data to the terminal, ; prefix it with a routing control sequence 1709 0494 36 FB mvi m,0FBH 1710 0496 23 inx h ; jump if the target is the printer (0); drop through if crt (1) 1713 0497 7B mov a,e 1714 0498 A1 ana c 1715 0499 CA A8 04 jz txp9 1717 049C 36 F0 mvi m,0F0H ; the rest of "route data to crt" sequence 1718 049E 24 inr h ; bump to 0x21xx+n*0x400 to get to crt zone in LIMIT_TX_XFER 1719 049F CD E7 04 call LIMIT_TX_XFER ; de=get ptr, b=byte count to transfer 1720 04A2 DA 01 04 jc txp2 ; jump if not all data has been transferred 1721 04A5 C3 41 04 jmp txp4 ; //// send printer data to terminal ; 'B' is the number of bytes of data to send to the uart. txp9: 1726 04A8 36 F1 mvi m,0F1H ; the rest of "route data to printer" sequence txp10: 1728 04AA 78 mov a,b 1729 04AB D6 08 sui 008H ; the printer gets to send smaller bursts 1730 04AD 47 mov b,a ; (8 bytes normally, 6 bytes if routing bytes preceded) 1731 04AE CD E7 04 call LIMIT_TX_XFER ; de=get ptr, b=byte count to transfer 1732 04B1 DA 01 04 jc txp2 ; jump if not all data has been transferred 1734 04B4 21 58 2A lxi h,PRT_BUF_ACTIVE 1735 04B7 CD 33 05 call MARK_BUF_EMPTY 1736 04BA CA C7 04 jz txp11 ; jump if PRT_STAT_PEND bit for this term is false ; clear the PRT_STAT_PEND bit corresponding to this terminal 1739 04BD AE xra m 1740 04BE 77 mov m,a 1742 04BF 2A 5D 26 lhld TX_SERVICE ; fetch page0 of term we are servicing for tx 1743 04C2 3E 01 mvi a,001H ; print buffer became empty (see STATUS_RESPONSE) 1744 04C4 CD 94 00 call SET_STATUS_RESP txp11: 1746 04C7 2A 5D 26 lhld TX_SERVICE ; fetch page0 of term we are servicing for tx 1747 04CA 2E 01 mvi l,001H ; 2n01: per-term rbi status 1748 04CC 7E mov a,m 1749 04CD E6 F7 ani 0F7H ; addr 4 ready 1750 04CF 77 mov m,a 1751 04D0 FB ei 1752 04D1 3A 5A 22 lda CUR_TERM 1753 04D4 B9 cmp c 1754 04D5 C2 DF 04 jnz txp12 ; jump if this term isn't currently addressed ; we just serviced the printer for this term, so indicate there is room ; to accept more print data 1757 04D8 F3 di 1758 04D9 3A 57 22 lda SHADOW_RBI 1759 04DC E6 F7 ani 0F7H ; addr 4 is ready 1760 04DE D7 rst 2 ; save A to SHADOW_RBI, send it to !RBI register, enable interrupts txp12: 1762 04DF CD 1E 05 call MOV2TXBUF ; move prt data to tx buffer 1763 04E2 1E 66 mvi e,066H ; start of print buffer (0x2266+n*0x400) 1764 04E4 C3 80 04 jmp txp7 ;------------------------------------------------------------------------------ ; this routine fetches the printer or crt get pointer into de. ; it reduces the transfer size limit value 'b' passed in if the remaining ; number of bytes in the crt or print buffer is less than the limit. ; entry: ; h=20/24/28/2C if printer, 21/25/29/2D if crt ; b=max number of bytes to transfer ; exit: ; de = printer or crt character get pointer ; b = max(b, put-get) ; cy=1 indicates limited, meaning data is left, 0=b is all the data that's left LIMIT_TX_XFER: ; depth=1 ; de = printer/crt get pointer (22|23)FF+n*0x400 1779 04E7 24 inr h 1780 04E8 24 inr h 1781 04E9 2E FF mvi l,0FFH 1782 04EB 54 mov d,h 1783 04EC 5E mov e,m ; return with b = max(b, put-get) 1786 04ED 2B dcx h ; 0x22FE+n*0x400 (printer put pointer) 1787 04EE 7E mov a,m ; h,a = printer put pointer 1788 04EF 93 sub e 1789 04F0 6F mov l,a ; l = put - get 1790 04F1 78 mov a,b ; 0x10, 0x10-8=0x08, 0x0E, 0x0E-8=0x06 depending on caller 1791 04F2 BD cmp l 1792 04F3 D8 rc ; return with b=limit if (diff > limit) 1793 04F4 45 mov b,l 1794 04F5 C9 ret ; return with b=(put-get) ;------------------------------------------------------------------------------ ; If the printer is flow controlled or empty (which means we don't need to mux ; the tx serial stream between printer and crt traffic), return immediately. ; Otherwise, scan from B-1 bytes after get pointer back to the get pointer, ; looking for a . If one is found, return with B clamped to that character. ; ; The point of this is to make the flow of characters to the crt a bit smoother ; when muxing printer/crt data. Because a is a high latency operation for ; mechanical printers, there is no pressing need to send the bytes after a . ; They can wait a while, allowing crt traffic to resume sooner. ; ; entry: ; b = number of bytes to check ; c = 1-hot encoding of terminal under consideration ; de = get pointer of crt or print buffer ; exit: ; c, de are unaffected ; exit if no CR found: ; b is unaffected ; exit if CR found: ; b = number of bytes clamped to the last found in the span, ; and the term bits at PRT_PRIO/SENT_CR are set CR_LIMIT_XFER: ; depth=1 1819 04F6 3A 57 2A lda PRT_CAN_TX 1820 04F9 A1 ana c 1821 04FA C8 rz 1822 04FB 62 mov h,d 1823 04FC 7B mov a,e 1824 04FD 80 add b 1825 04FE 6F mov l,a ; hl now points to 'b' bytes after get pointer 1826 04FF 50 mov d,b ; save original count ; scan (hl--) looking for 0x0D character, quitting if 'b' bytes are tested crlx1: 1829 0500 2B dcx h 1830 0501 7E mov a,m 1831 0502 FE 0D cpi 00DH 1832 0504 CA 0E 05 jz crlx2 ; jump if CR found 1833 0507 05 dcr b 1834 0508 C2 00 05 jnz crlx1 1835 050B 42 mov b,d ; restore entry value 1836 050C 54 mov d,h ; restore entry value 1837 050D C9 ret crlx2: ; we found a CR 1840 050E 54 mov d,h ; restore entry value 1841 050F 3C inr a ; a=0x0E, but probably done to clear z flag ; set both flags after fall through ... ;------------------------------------------------------------------------------ ; if z flag is clear, [PRT_PRIO] |= C (1-hot term map) ; unconditionally, [SENT_CR] |= C (1-hot term map) S0510: ; depth=1 1848 0510 21 62 26 lxi h,PRT_PRIO 1849 0513 CA 19 05 jz t0519 1850 0516 7E mov a,m 1851 0517 B1 ora c ; set bit for current terminal 1852 0518 77 mov m,a t0519: 1854 0519 23 inx h ; SENT_CR 1855 051A 7E mov a,m 1856 051B B1 ora c ; set bit for current terminal 1857 051C 77 mov m,a 1858 051D C9 ret ;------------------------------------------------------------------------------ ; Move 'b' bytes from the get pointer (de) to the uart tx buffer. ; The buffer might already have valid bytes, but it is the caller's ; responsibility to not overflow the buffer. ; entry: ; b = number of bytes to transfer ; de = get pointer ; exit: ; de = updated get pointer ; h = 0x22/23 if printer/crt MOV2TXBUF: ; depth=1 1871 051E 2A 5F 26 lhld CUR_TX_BUF ; hl = 2n03 of term being serviced by tx polling 1872 0521 4E mov c,m ; c = [2n03]=number of valid bytes in the tx buffer 1873 0522 79 mov a,c 1874 0523 80 add b 1875 0524 77 mov m,a ; [2n03] += b; the buffer now has (old_count + b) valid bytes 1876 0525 23 inx h 1877 0526 7E mov a,m ; a = [2n04]=tx buffer get pointer 1878 0527 81 add c 1879 0528 6F mov l,a ; hl points to the last valid byte mtxb2: ; block copy loop from (de) -> (hl+1) for 'b' bytes 1882 0529 1A ldax d 1883 052A 13 inx d ; << note: get ptr is postincremented 1884 052B 23 inx h ; << note: put ptr is preincremented 1885 052C 77 mov m,a 1886 052D 05 dcr b 1887 052E C2 29 05 jnz mtxb2 1889 0531 62 mov h,d ; hl = 0x22nn or 0x23nn 1890 0532 C9 ret ;------------------------------------------------------------------------------ ; this routine changes the state of a buffer indicating it is empty. ; entry: ; hl is either CRT_BUF_ACTIVE or PRT_BUF_ACTIVE ; c is a 1-hot bitmap represting the current terminal ; exit: ; z flag is cleared if this term's STAT_PEND bit is true ; disables interrupts MARK_BUF_EMPTY: ; depth=1 1901 0533 F3 di ; clear bit for current term in (CRT/PRT)_BUF_ACTIVE 1903 0534 79 mov a,c 1904 0535 2F cma 1905 0536 A6 ana m 1906 0537 77 mov m,a ; clear bit for current term in (CRT/PRT)_CAN_TX 1908 0538 2B dcx h 1909 0539 79 mov a,c 1910 053A 2F cma 1911 053B A6 ana m 1912 053C 77 mov m,a ; test bit for current term in (CRT/PRT)_STAT_PEND 1914 053D 2B dcx h 1915 053E 7E mov a,m 1916 053F A1 ana c 1917 0540 C9 ret ; respond to polling at addr 2 (STATUS). ; the response sequence is: ; 1. RESET flags ; 2. HALT flags ; 3. DSR flags (manual says MVP returns clock info here) ; 4. terminal 1 status ; 5. terminal 2 status ; 6. terminal 3 status ; 7. terminal 4 status ; the status bytes are encoded as: ; 01: print buffer became empty (requested) ; 02: CRT buffer became empty (requested) ; 04: keyboard buffer not empty or Line Request complete ; 08: unused (MVP uses it) ; 10: unused ; 20: unused ; 40: unused ; 80: unused (MVP uses it) STATUS_RESPONSE: 1938 0541 F3 di 1939 0542 3A 57 22 lda SHADOW_RBI 1940 0545 F6 02 ori 002H ; addr 2 is not ready 1941 0547 CF rst 1 ; save A to SHADOW_RBI, then send it to !RBI register 1942 0548 11 00 04 lxi d,00400H ; step between memory block for each term 1943 054B 21 63 2A lxi h,HALT_MAP 1944 054E 4E mov c,m ; C=halt map 1945 054F 73 mov m,e ; zero out HALT_MAP 1946 0550 2B dcx h 1947 0551 46 mov b,m ; B=reset map 1948 0552 73 mov m,e ; zero out RESET_MAP 1949 0553 FB ei 1950 0554 CD E7 07 call SEND_B_IBS ; return reset map 1951 0557 41 mov b,c 1952 0558 CD E7 07 call SEND_B_IBS ; return halt map ; build next response byte 1954 055B 06 00 mvi b,000H ; next response byte we are building 1955 055D 3E 08 mvi a,008H ; work from term 4 down to term 1 srs1: 1957 055F 4F mov c,a 1958 0560 32 5C 22 sta CUR_UART 1959 0563 D3 05 out OUT_UART_SEL 1960 0565 DB 0E in IN_UART_STATUS 1961 0567 17 ral ; test data set ready bit 1962 0568 D2 6E 05 jnc srs2 1963 056B 78 mov a,b 1964 056C B1 ora c 1965 056D 47 mov b,a srs2: 1967 056E 79 mov a,c 1968 056F 0F rrc 1969 0570 D2 5F 05 jnc srs1 ; loop over remaining terminals 1970 0573 CD E7 07 call SEND_B_IBS ; next status byte (DSR bitmap) ; build next four response bytes 1973 0576 21 02 20 lxi h,02002H ; 2n02 = terminal status response byte (2002 for terminal 1) 1974 0579 0E 04 mvi c,004H ; iterate over four terminals srs3: 1976 057B 46 mov b,m ; fetch [0x2002+n*0x0400] 1977 057C 73 mov m,e ; zero it out 1978 057D CD E7 07 call SEND_B_IBS ; send status byte 1979 0580 19 dad d ; move to next block 1980 0581 0D dcr c 1981 0582 C2 7B 05 jnz srs3 ; loop over all terminals ; fall through ... ; send sequence terminating byte 0xF0 w/IB9 set SEND_F0_IB9: 1986 0585 3E F0 mvi a,0F0H ; fall through ... ; send sequence terminating byte (in A) w/IB9 set SEND_A_IB9: 1991 0587 F5 push psw 1992 0588 CD A0 00 call CPU_POLL_WAIT 1993 058B F1 pop psw 1994 058C 2F cma 1995 058D D3 11 out OUT_IB9_N 1996 058F C9 ret ; received CBS or OBS with addr=6 or 7 ADDR6_OR_7: 2000 0590 C2 21 08 jnz ADDR7 ; jump if obs/cbs addr=7 ; CBS or OBS with addr=6 ; double check that it was a CBS, not OBS, ; and reject any CBS which is not 0xFF, or >0x12 2005 0593 3E FF mvi a,0FFH 2006 0595 32 56 22 sta MVP_MODE ; indicate MVP mode 2007 0598 DB 01 in IN_2200_STATUS 2008 059A 0F rrc ; test OBS strobe latch 2009 059B DB 02 in IN_OBUS_N ; read OB_N, and clear OBS/CBS latches 2010 059D D8 rc ; return if OBS was set 2011 059E A7 ana a 2012 059F CA AE 05 jz SET_ACT_TERM ; jump if OB_N=0 (OBS=FF) 2013 05A2 FE ED cpi 0EDH 2014 05A4 D8 rc ; return if OB_N < xED (OB >= 0x12) ; fall through for io6 CBS() command dispatch ... ; OB OB_N command ; -- ---- ---------------------- ; FF -> 00 -> select terminal ; 00 -> FF -> null ; 01 -> FE -> power-on sequence ; 02 -> FD -> init current term ; 03 -> FC -> delete line request ; 04 -> FB -> keyboard ready check ; 05 -> FA -> keyin poll request ; 06 -> F9 -> keyin line request ; 07 -> F8 -> line request ; 08 -> F7 -> prefill line request ; 09 -> F6 -> refill line request ; 0A -> F5 -> end of line request ; 0B -> F4 -> query line request ; 0C -> F3 -> accept line request data ; 0D -> F2 -> request crt buffer ; 0E -> F1 -> request print buffer ; 0F -> F0 -> error line request ; 10 -> EF -> terminal line request ; 11 -> EE -> device status ** (triple controller and mxe only) ; 12 -> ED -> start clock ** (triple controller and mxe only) ; A is doubled, then a 16 bit pointer is fetched into hl ; at that offset on page 0, and an indirect branch is taken. IDXJMPA: 2043 05A5 17 ral 2044 05A6 6F mov l,a 2045 05A7 26 00 mvi h,000H 2046 05A9 56 mov d,m 2047 05AA 23 inx h 2048 05AB 66 mov h,m 2049 05AC 6A mov l,d 2050 05AD E9 pchl ;INFO: index jump ; Set Active Terminal: expected sequence CBS(FF) OBS(nn) ; nn=00 means terminal 1, 01 means terminal 2, etc. ; this routine sets CUR_TERM, which is 0x01, 0x02, 0x04, or 0x08 (one hot encoding). ; it also sets CUR_BLOCK_PAGE with 0x20, 0x24, 0x28, or 0x2C. SET_ACT_TERM: 2057 05AE DF rst 3 ; get an OBS byte 2058 05AF E6 07 ani 007H 2059 05B1 07 rlc 2060 05B2 07 rlc 2061 05B3 C6 20 adi 020H ; 0->20, 1->24, 2->28, 3->2C 2062 05B5 32 59 22 sta CUR_BLOCK_PAGE ; this is one of two places where this is set 2063 05B8 67 mov h,a 2064 05B9 2E 00 mvi l,000H ; hl = 2n00 2065 05BB 7E mov a,m 2066 05BC 32 5A 22 sta CUR_TERM ; this is one of two places where this is set ; fall through ... ; read the term-specific RBI byte at [2n01]. if either bit for addr 4&7 are ready, ; make the actual port bits ready, otherwise leave the RBI state alone. S05BF: ; depth=1 2072 05BF 2E 01 mvi l,001H ; hl = 2n01 2073 05C1 7E mov a,m 2074 05C2 F6 A7 ori 0A7H ; bits for addr 1,2,3,6 should be unaffected by "ana c" below 2075 05C4 4F mov c,a 2076 05C5 F3 di 2077 05C6 3A 57 22 lda SHADOW_RBI 2078 05C9 F6 48 ori 048H ; assume addr 4&7 (prt and crt) are busy 2079 05CB A1 ana c ; clear addr 4&7 if [2n01] byte has these not busy, but leave ... 2080 05CC 32 57 22 sta SHADOW_RBI ; ... them clear if the SHADOW_RBI bits had them not busy already 2081 05CF D3 07 out OUT_RBI 2082 05D1 FB ei 2083 05D2 C9 ret ; This command causes the controller to check the CRT buffer of the current ; terminal. If it is empty, the appropriate status bit is set (address 02h = ; ready) to signal the fact. If not, then the controller will set the bit ; when the buffer does go empty. REQ_CRT_BUFFER: 2090 05D3 F3 di 2091 05D4 3A 5A 22 lda CUR_TERM 2092 05D7 47 mov b,a 2093 05D8 3A 5D 2A lda CRT_BUF_ACTIVE ; 1=crt buffer is not empty 2094 05DB A0 ana b 2095 05DC 11 02 AD lxi d,0AD02H ; really, D=0xAD=10101101, E=0x02 2096 05DF CA FC 05 jz rpb1 ; jump if buffer is empty; set status response 2097 05E2 21 5B 2A lxi h,CRT_STAT_PEND ; buffer isn't empty; set the status when it is rcb1: 2099 05E5 7E mov a,m 2100 05E6 B0 ora b ; set bit corresponding to the current term 2101 05E7 77 mov m,a 2102 05E8 FB ei 2103 05E9 C9 ret ; This is just like REQ_CRT_BUFFER, except it refers to the current terminal's ; PRINT buffer, not the CRT buffer. REQ_PRINT_BUFFER: 2108 05EA F3 di 2109 05EB 3A 5A 22 lda CUR_TERM 2110 05EE 47 mov b,a 2111 05EF 3A 58 2A lda PRT_BUF_ACTIVE ; PRINT buffer status bitmap? 2112 05F2 A0 ana b 2113 05F3 21 56 2A lxi h,PRT_STAT_PEND 2114 05F6 C2 E5 05 jnz rcb1 ; jump if buffer isn't empty; set status when it is 2115 05F9 11 01 F5 lxi d,0F501H ; really, D=0xF5=111110101, E=0x01 rpb1: ; CRT/PRINT buffer was queried and is empty; update corresponding status 2117 05FC F3 di ; not sure why; intr is already disabled 2118 05FD 3A 57 22 lda SHADOW_RBI 2119 0600 A2 ana d ; CRT: addr 2&5&7 are ready; PRINT: addr 2&4 are ready 2120 0601 CF rst 1 ; save A to SHADOW_RBI, then send it to !RBI register 2121 0602 3A 59 22 lda CUR_BLOCK_PAGE ; page0 of current term 2122 0605 67 mov h,a 2123 0606 2E 01 mvi l,001H ; 2n01 -- per-term RBI status? 2124 0608 7E mov a,m 2125 0609 A2 ana d 2126 060A 77 mov m,a 2127 060B 23 inx h ; 2n02 = terminal status response byte 2128 060C 43 mov b,e ; CRT: B=02; PRINT: B=01 2129 060D C3 E5 05 jmp rcb1 ; expected sequence: CBS(07), OBS(XXXXYYZZ), where ; XXXX: field size (up to 480=0x1E0 bytes according to the manual, but 0x1E8 below) ; YY: 0x80=underline flag, 0x04=EDIT mode, 0x01=flush old chars ; ZZ: CRT cursor column (it should already be there) LINE_REQ: 2136 0610 2A 58 22 lhld CUR_BLOCK ; 2000/2400/2800/2C00 2137 0613 4E mov c,m ; this routine doesn't appear to use 'c' 2138 0614 2E 5C mvi l,05CH ; 2n5C 2139 0616 06 0F mvi b,00FH ; 15 bytes 2140 0618 CD 63 00 call ZFILL_HL_B ; clear 2n5C - 2n6A (line_req flags and pointers) 2141 061B 44 mov b,h ; 20/24/28/2C 2142 061C 54 mov d,h 2143 061D 5D mov e,l ; 0x6B (0x5C+15) ; [2n69/6A] (cursor location) = 2n6B (start of LINE_REQ buffer) 2146 061E 2B dcx h 2147 061F 73 mov m,e ; 2n6a = 6b 2148 0620 2B dcx h 2149 0621 72 mov m,d ; 2n69 = 2n -- note: msb is in lower address! ; [2n67/68] (last byte used) = 2n6B (start of LINE_REQ buffer) 2152 0622 2B dcx h 2153 0623 73 mov m,e 2154 0624 2B dcx h 2155 0625 72 mov m,d ; [2n5D/5E] (redraw start) = 2n6B (start of LINE_REQ buffer) 2158 0626 2E 5D mvi l,05DH 2159 0628 72 mov m,d 2160 0629 23 inx h 2161 062A 73 mov m,e ; [2n5F/60] (redraw end)= 2n6B (start of LINE_REQ buffer) 2164 062B 23 inx h 2165 062C 72 mov m,d 2166 062D 23 inx h 2167 062E 73 mov m,e ; receive two OBS bytes (field size), and compute end of buffer 2170 062F DF rst 3 2171 0630 57 mov d,a 2172 0631 DF rst 3 2173 0632 5F mov e,a 2174 0633 21 E8 01 lxi h,001E8H ; = 488, buffer size limit 2175 0636 CD 70 00 call MAX_HL_MIN_DE ; de = smaller of 488 and the requested size 2176 0639 60 mov h,b ; 20/24/28/2C 2177 063A 2E 6B mvi l,06BH ; 2n6B is the start of the line request buffer 2178 063C 19 dad d 2179 063D EB xchg ; de = pointer to end of buffer ; 2n65/66 = pointer to end of LINE_REQ buffer (not sure if inclusive or not) 2182 063E 60 mov h,b 2183 063F 2E 65 mvi l,065H 2184 0641 72 mov m,d 2185 0642 23 inx h 2186 0643 73 mov m,e ; get one OBS byte for YY, the edit/underline/flush flag byte 2189 0644 DF rst 3 2190 0645 2E 63 mvi l,063H ; 2n63 = flag byte; 80=underline, 04=edit, 01=flush previously queued keystrokes 2191 0647 E6 EC ani 0ECH ; 11101100b : interestingly clears the 01 bit before testing it and doesn't clear undefined bits 2192 0649 77 mov m,a ; get one OBS byte for ZZ, the CRT cursor column of the field start 2195 064A DF rst 3 ; get an OBS byte (ZZ byte) 2196 064B 2E 64 mvi l,064H ; 2n64 = current column of CRT cursor 2197 064D 77 mov m,a 2198 064E C9 ret lreq1: 2201 064F DB 02 in IN_OBUS_N 2202 0651 2F cma ; right polarity 2203 0652 77 mov m,a ; save it to hl buffer 2204 0653 7D mov a,l 2205 0654 BB cmp e 2206 0655 C2 5D 06 jnz lreq2 2207 0658 7C mov a,h 2208 0659 BA cmp d 2209 065A CA 5E 06 jz BUFFER_OBS_6_DATA lreq2: 2211 065D 23 inx h ; bump hl if hl != de ; fall through ... ; accumulate run of addr 6 OBS data to buffer at hl ; keep accepting data until we get something other than OBS. ; on entry, HL is where we want to write, and DL points to the end of buffer ; if we get more OBSs than buffer expects, last char of the buffer ; gets written over and over without incrementing the put pointer, ; but no error occurs. ; on exit, HL=pointer to one past last byte written (or last byte written if we clamped), BUFFER_OBS_6_DATA: 2222 065E DB 01 in IN_2200_STATUS 2223 0660 1F rar 2224 0661 DA 4F 06 jc lreq1 ; OBS is set 2225 0664 E6 7D ani 07DH ; x1111101(x) 2226 0666 FE 68 cpi 068H ; 01101000 = no cbs, no prime, not waiting for input, selected, addr=6 2227 0668 C0 rnz ; return if not true 2228 0669 C3 5E 06 jmp BUFFER_OBS_6_DATA ;------------------------------------------------------------------------------ ; The refill command is identical to a prefill except that it does not cause ; repositioning of the cursor to the beginning. Thus the characters are ; treated as keystrokes. It is normally used for RECALL and DEFFN' quotes. ; It is generally followed by an END-OF-LINE-REQUEST CBS(0A) or a ; TERMINATE-LINE-REQUEST CBS(10). REFILL_LINE_REQ: 2237 066C 2A 58 22 lhld CUR_BLOCK ; 2000/2400/2800/2C00 2238 066F 2E 63 mvi l,063H ; 2n63: line_req flag byte 2239 0671 7E mov a,m 2240 0672 F6 10 ori 010H ; refill_line_req flag 2241 0674 77 mov m,a ; fall through ... ; expected sequence: CBS(08) OBS(YYYYYY...) ; This optional command can be sent after a line request command, CBS(07), ; to prefill the desired line with the supplied characters YYYY... starting ; with the leftmost position. The characters are treated as keystrokes. ; The cursor is left at the leftmost character. The string of characters is ; terminated by the next CBS, which will normally be an END-OF-LINE-REQUEST. PREFILL_LINE_REQ: 2251 0675 2A 58 22 lhld CUR_BLOCK ; 2000/2400/2800/2C00 2252 0678 44 mov b,h 2253 0679 4E mov c,m ; one-hot term encoding ; de = [2n65/66] = end of line_req buffer 2256 067A 2E 65 mvi l,065H 2257 067C 56 mov d,m 2258 067D 23 inx h 2259 067E 5E mov e,m ; hl = [2n5F/60] = start of buffer, or where previous prefill ended (probably) 2262 067F 2E 5F mvi l,05FH 2263 0681 7E mov a,m 2264 0682 23 inx h 2265 0683 6E mov l,m 2266 0684 67 mov h,a 2268 0685 CD 5E 06 call BUFFER_OBS_6_DATA ; fill buffer at [hl] up to de with io6 OBS data 2269 0688 EB xchg ; de=where prefill ended; hl points to end of buffer 2270 0689 60 mov h,b 2271 068A 2E 5F mvi l,05FH 2272 068C 72 mov m,d 2273 068D 23 inx h 2274 068E 73 mov m,e ; [2n5F/60] <- where prefill ended ; save last used by pointer: [2n67/68] = larger of [2n67/68] vs [2n5F/60] 2277 068F 2E 67 mvi l,067H 2278 0691 CD 6C 00 call HLPTR_MAXMIN_DE2 ; hl <- [2n67/68]; conditionally swap hl/de 2279 0694 EB xchg ; de=larger, hl=smaller 2280 0695 60 mov h,b 2281 0696 2E 67 mvi l,067H 2282 0698 72 mov m,d 2283 0699 23 inx h 2284 069A 73 mov m,e 2285 069B C9 ret ;------------------------------------------------------------------------------ ; This command is used (after optional PREFILL or REFILL) to cause all the same ; actions as the operator pressing EXEC. It is normally used for the BASIC ; statement DEFFN' HEX(0D). TERMINATE_LINE_REQ: 2292 069C 3E 65 mvi a,065H ; continuation code; INDTAB offset 00CA (CURSOR_ON) 2293 069E C3 A8 06 jmp END_LINE_REQ ; This command causes the line request to resume, just like END_OF_LINE_REQ, ; except it beeps first. It should not be used in conjunction with PREFILL or ; REFILL. It is normally used for undefined function keys. ERROR_LINE_REQ: 2299 06A1 3E 64 mvi a,064H ; continuation code; INDTAB offset 00C8 (ERROR_LINE_REQ2) 2300 06A3 C3 A8 06 jmp END_LINE_REQ ; A special command must be supplied to signal the end of a line request ; sequence which consists of the setup and prefill if desired. The last command ; sent, however, must be a CBS(0A), to signal the microcode to invoke the line ; request. Nothing is sent to the CRT until the CBS(0A) is issued. ; ; This command is also used after successful RECALL or DEFFN' text entry to ; signal the controller to resume processing the line request. ; ; NOTE: The 2200 will sometimes skip this command when it knows it will read in ; the data from the controller on the next command. This is a violation of ; protocol but the controllers live with it. Should the 2200 skip this command ; and go directly to CBS(0C) [ ACCEPT_LINE_REQ_DATA ] the controller should ; update the screen before allowing another Line Request to be started. END_OF_LINE_REQ: 2316 06A6 3E 5F mvi a,05FH ; continuation code; INDTAB offset 00BE (CURSOR_ON) ; fall through ... ; common to TERMINATE_LINE_REQ, ERROR_LINE_REQ, and END_OF_LINE_REQ END_LINE_REQ: 2321 06A8 2A 58 22 lhld CUR_BLOCK ; 2000/2400/2800/2C00 2322 06AB 44 mov b,h 2323 06AC 4E mov c,m ; one-hot term encoding 2324 06AD CD B9 06 call SAV_CONT ; save continuation (on return hl=KB_BUF_READY) ; mark [LREQ_ACCEPT] true to cause continuation processing 2327 06B0 23 inx h ; LREQ_ACCEPT 2328 06B1 7E mov a,m 2329 06B2 B1 ora c 2330 06B3 77 mov m,a ; mark this term as having an active line req 2333 06B4 23 inx h ; LREQ_ACTIVE 2334 06B5 7E mov a,m 2335 06B6 B1 ora c 2336 06B7 77 mov m,a 2337 06B8 C9 ret ;------------------------------------------------------------------------------ ; On entry A indicates an offset into the INDTAB table. At a later time, this ; offset is recalled and IDXJMPA does a branch through the table. I'm calling ; this is a "continuation" as it continues processing a suspended command. ; The offsets used are shown here. Some continuations increment the [2n5C] ; byte to link to another continuation. ; ; Why interrupt commands like this? Because the supervisor has to multitask ; between four terminals and none can block the others. For instance, ; on END_OF_LINE_REQ, all the stored line buffer contents must be sent to the ; terminal, but we can't afford to wait in one event loop to push all those ; bytes. Instead, we push what fits in the uart tx buffer, then suspend and ; later the routine will continue to send more bytes as room appears in the ; uart tx buffer. ; ; caller A IDXJMPA offset ; ------------------- ---- -------------- ; END_OF_LINE_REQ 0x5F -> 0xBE ; EDIT_SF10 (insert) 0x61 -> 0xC2 ; EDIT_SF9 (delete) 0x61 -> 0xC2 ; ERROR_LINE_REQ 0x64 -> 0xC8 ; TERMINATE_LINE_REQ 0x65 -> 0xCA ; ; After saving that index value, UPDATE_KB_BUF_READY is called SAV_CONT: ; depth=1 2364 06B9 2E 5C mvi l,05CH 2365 06BB 77 mov m,a ; [2n5C] = continuation byte ; fall through ... ; update KB_BUF_READY flags by OR'ing in C mask ; C: 01=term 1 has non empty KB buff, .., 08=term 4 has non-empty KB buff ; note: it returns with the updated mask in A and HL=KB_BUF_READY UPDATE_KB_BUF_READY: ; depth=1 2372 06BC 21 5F 2A lxi h,KB_BUF_READY 2373 06BF F3 di 2374 06C0 7E mov a,m 2375 06C1 B1 ora c 2376 06C2 77 mov m,a 2377 06C3 FB ei 2378 06C4 C9 ret ;------------------------------------------------------------------------------ ; this initializes some data structs for one terminal. ; it is called by both INIT_CUR_TERM and POWER_ON_INIT code paths. ; the CRT buffer is initialized with an init command sequence. INIT_CRT_BUF: ; depth=2 2385 06C5 CD 8D 08 call SET_CRT_BUF_ACTIVE ; we will send an init sequence 2386 06C8 79 mov a,c 2387 06C9 32 5A 22 sta CUR_TERM ; this is one of two places where this is set 2388 06CC CD 20 01 call TERM_LOOKUP 2389 06CF 78 mov a,b 2390 06D0 32 59 22 sta CUR_BLOCK_PAGE ; this is one of two places where this is set 2391 06D3 2E 00 mvi l,000H ; 2n00 2392 06D5 22 5F 26 shld CUR_INIT ; save it for use by INIT_PRT_BUF 2393 06D8 71 mov m,c ; 2n00: this term 1-hot bitmap 2395 06D9 2E 16 mvi l,016H ; 2n16 2396 06DB 06 05 mvi b,005H ; fill five bytes (2n16 through 2n1A) 2397 06DD CD 63 00 call ZFILL_HL_B ; zero out keyboard rx buffer pointers (2n16-2n1A) 2398 06E0 CD FC 06 call INIT_KB_BUF ; initialize the crt buffer with this 18 byte sequence: ; -- immediate command to reset the crt ; <01> x 16 -- delay a few character times? <01> means home cursor 2403 06E3 24 inr h 2404 06E4 24 inr h 2405 06E5 24 inr h 2406 06E6 2E FF mvi l,0FFH 2407 06E8 36 00 mvi m,000H ; 23FF+n*0x400=0x00 = crt buffer get pointer 2408 06EA 2B dcx h 2409 06EB 36 12 mvi m,012H ; 23FE+n*0x400=0x12 = crt put pointer 2410 06ED 2E 00 mvi l,000H 2411 06EF 36 FB mvi m,0FBH ; 2300+n*0x400=0xFB 2412 06F1 23 inx h 2413 06F2 36 F6 mvi m,0F6H ; 2301+n*0x400=0xF6 << INIT_PRT_BUF overrides this 2414 06F4 23 inx h 2415 06F5 06 10 mvi b,010H ; fill 16 bytes (2302 to 2311)+n*0x0400 ... 2416 06F7 3E 01 mvi a,001H ; ... with 0x01 2417 06F9 C3 64 00 jmp A_FILL_HL_B ; fill and return ;------------------------------------------------------------------------------ ; initialize the keyboard rx buffer. INIT_KB_BUF: ; depth=1 2422 06FC 2E 1A mvi l,01AH ; 2n1A: get pointer of rx buffer 2423 06FE 7D mov a,l 2424 06FF 77 mov m,a 2425 0700 2B dcx h ; 2n19: put pointer of rx buffer 2426 0701 77 mov m,a 2427 0702 2B dcx h ; 2n18: count of entries in keyboard buffer 2428 0703 36 00 mvi m,000H 2429 0705 C9 ret ;------------------------------------------------------------------------------ ; INITIALIZE CURRENT TERMINAL CBS(02) OBS(00) ; "This command will cause the CRT screen, pending line request CRT buffer, ; print buffer, and input buffer of the current terminal to be cleared ; (used at RESET)." ; Interestingly, if the trailing byte isn't 00, the print tx buffer is not ; reset. That behavior isn't described by 2236MXE_Documentation.8-83.pdf ; Apparently it is sent in response to a RESET keypress in MVP mode, where ; we want to reset the foreground keyboard/terminal without interfering with ; a background print job. INIT_CUR_TERM: 2442 0706 3A 59 22 lda CUR_BLOCK_PAGE ; page0 of current term 2443 0709 67 mov h,a 2444 070A CD BF 05 call S05BF 2445 070D 3A 5A 22 lda CUR_TERM 2446 0710 4F mov c,a 2447 0711 2F cma 2448 0712 47 mov b,a 2449 0713 1E 0A mvi e,10 ; there are 10 bitmap flags to clear 2450 0715 21 5A 2A lxi h,WANGTERM ; starting at this byte 2451 0718 F3 di ict1: 2453 0719 7E mov a,m 2454 071A A0 ana b ; clear current term bit 2455 071B 77 mov m,a 2456 071C 23 inx h 2457 071D 1D dcr e 2458 071E C2 19 07 jnz ict1 ; loop through all 10 bytes 2460 0721 FB ei 2461 0722 CD C5 06 call INIT_CRT_BUF 2462 0725 FB ei 2463 0726 DF rst 3 ; get an OBS byte 2464 0727 A7 ana a 2465 0728 CC 2D 07 cz INIT_PRT_BUF ; 00 is normal 2466 072B FB ei 2467 072C C9 ret ;------------------------------------------------------------------------------ ; initialize the print tx buffer for current terminal INIT_PRT_BUF: ; depth=2 2472 072D 2A 5F 26 lhld CUR_INIT ; term ram ptr INIT_CRT_BUF saved off 2473 0730 24 inr h 2474 0731 24 inr h 2475 0732 2E FE mvi l,0FEH ; hl=0x22FE+n*0x0400 2476 0734 36 76 mvi m,076H ; prt put pointer = 0x76 2477 0736 23 inx h ; hl=0x22FF+n*0x0400 2478 0737 36 66 mvi m,066H ; prt get pointer = 0x66 2479 0739 2E 66 mvi l,066H ; hl=0x2266+n*0x0400 2480 073B 06 10 mvi b,010H ; 16 bytes 2481 073D CD 63 00 call ZFILL_HL_B ; fill with <00> ; INIT_CRT_BUF was initialized to send , which resets the crt. ; INIT_PRT_BUF is conditionally called after INIT_CRT_BUF. ; here we replace the with , which means "full terminal reset". 2486 0740 24 inr h 2487 0741 2E 01 mvi l,001H ; hl=0x2301+n*0x0400 = 2nd byte of crt tx buff 2488 0743 36 F2 mvi m,0F2H ; assume flow control is in effect; wait for "PRT ON" flow control ; before resuming traffic to the printer 2492 0745 21 59 2A lxi h,PRT_STOPPED 2493 0748 7E mov a,m 2494 0749 B1 ora c 2495 074A 77 mov m,a 2496 074B 2B dcx h ; PRT_BUF_ACTIVE 2497 074C C3 90 08 jmp SET_BUF_ACTIVE ;------------------------------------------------------------------------------ ; Whenever an io6 CBS(04) is received, the controller checks the keyboard ; buffer of the selected terminal. An IBS(00) is sent if it is empty, and non- ; zero if there is a character (the non-zero byte is of no particular value). KB_READY_CHECK: ; depth=2 2504 074F CD A0 00 call CPU_POLL_WAIT 2505 0752 3A 5A 22 lda CUR_TERM ; one-hot 2506 0755 4F mov c,a 2507 0756 21 5F 2A lxi h,KB_BUF_READY 2508 0759 A6 ana m ; 0=empty, 1=non-empty 2509 075A 2F cma 2510 075B D3 01 out OUT_IB_N 2511 075D C9 ret ; Whenever a command code of 06h is received, the control checks the keyboard ; buffer. If there is a byte in it, this command is treated exactly as the ; command 05h (KEYIN_POLL_REQUEST). If not, an IBS(00) is sent, and a special ; Keyin Line Request is set up. An interrupt (completed line request) will be ; generated at address 02h when a character is later received. KEYIN_LINE_REQ: 2519 075E F3 di 2520 075F 2A 58 22 lhld CUR_BLOCK ; 2000/2400/2800/2C00 2521 0762 2E 63 mvi l,063H ; 2n63: line_req flag byte 2522 0764 36 02 mvi m,002H ; set flag for KEYIN_LINE_REQ 2523 0766 FB ei ; fall through ... ; Whenever a command code of 05h is received, the controller checks the keyboard ; buffer. If there is no character an IBS(00) is returned. Otherwise, a non- ; zero byte is returned followed by an IBS of the byte (with ENDI if it is a ; Special Function key). It seems that if the user hit one of the atom keys, ; eg PRINT, it returns the atom value and doesn't expand it. KEYIN_POLL_REQ: 2532 0767 CD 4F 07 call KB_READY_CHECK ; emits IBS(00) or IBS(non-zero) 2533 076A C8 rz 2534 076B 3A 59 22 lda CUR_BLOCK_PAGE ; page0 of current term 2535 076E 47 mov b,a 2536 076F CD A0 00 call CPU_POLL_WAIT ; wait for !CPB 2537 0772 CD 90 07 call RELAY_KB_ENTRY ; this must return the next kb buffer byte via IBS, optionally with IB9 set if it is a special function key 2538 0775 3A 59 22 lda CUR_BLOCK_PAGE ; page0 of current term 2539 0778 47 mov b,a 2540 0779 C3 1B 03 jmp CLEAR_LR_FLAGS ; clear keyin_line_req bit (02H) of line_req flag byte ;------------------------------------------------------------------------------ ; continuation of the CHECK_HOST code ; host is ready for input (!CPB) CKHOST2: ; depth=4 (call here, jump to CHECK_HOST) 2546 077C DB 01 in IN_2200_STATUS 2547 077E E6 E0 ani 0E0H ; isolate the address 2548 0780 FE 40 cpi 040H 2549 0782 CA 41 05 jz STATUS_RESPONSE ; jump if addr 2 2550 0785 D2 EF 07 jnc CHECK_HOST ; jump if addr 3-7 (ignore them) ; we know the addr must be 1, VP keyboard, because 0 is never received 2552 0788 3A 57 22 lda SHADOW_RBI 2553 078B 0F rrc 2554 078C D8 rc 2555 078D 01 01 20 lxi b,02001H ; b=0x20=term 1; c=0x01 one hot encoding ; fall through ... ;------------------------------------------------------------------------------ ; read the next entry of the kb receive buffer, and send it to the host CPU ; possibly with the IB9 bit set (for s.f. keys). RELAY_KB_ENTRY: ; depth=3 2562 0790 CD 1A 0B call NEXT_KB_PAIR ; on return, A=prefix byte, E=char code ; fall through ... ; reg A = prefix byte, reg E = key code RKBE2: 2567 0793 3C inr a 2568 0794 CA BF 07 jz rkbe4 ; jump if a=FF: KB buffer was empty (E=00) or special (eg, backspace, CR) 2569 0797 3C inr a 2570 0798 CA A8 07 jz rkbe3 ; jump if A=0xFE (dead key) 2571 079B 3C inr a ; set z flag if A=0xFD (EndI/Atom) 2572 079C 7B mov a,e 2573 079D 07 rlc 2574 079E C2 E2 07 jnz rkbe6 ; jump if A!=0xFD, namely 0xFC 2575 07A1 DA E5 07 jc SEND_E_IBS ; jump if E >= 0x80 (atom) ; fall through ... ; respond to CPU with byte=E and IB9 set (s.f. key) SEND_E_IB9: 2580 07A4 7B mov a,e 2581 07A5 C3 87 05 jmp SEND_A_IB9 ; send terminating byte (in A) w/IB9 bit set rkbe3: 2584 07A8 60 mov h,b 2585 07A9 2E 16 mvi l,016H ; 2n16 2586 07AB 73 mov m,e ; save key value 2587 07AC 06 FF mvi b,0FFH 2588 07AE CD BC 06 call UPDATE_KB_BUF_READY 2589 07B1 79 mov a,c 2590 07B2 0F rrc 2591 07B3 D2 E7 07 jnc SEND_B_IBS ; jump if not term 1 2592 07B6 3A 57 22 lda SHADOW_RBI 2593 07B9 E6 FE ani 0FEH ; addr 1 is ready 2594 07BB CF rst 1 ; save A to SHADOW_RBI, then send it to !RBI register 2595 07BC C3 E7 07 jmp SEND_B_IBS rkbe4: 2597 07BF 7B mov a,e 2598 07C0 FE BD cpi 0BDH ; EDIT key 2599 07C2 CA 85 05 jz SEND_F0_IB9 ; relay EDIT key event to host 2600 07C5 FE E7 cpi 0E7H 2601 07C7 DA E6 07 jc SEND_A_IBS ; jump if char < 0xE7 2602 07CA 60 mov h,b 2603 07CB 24 inr h 2604 07CC 24 inr h 2605 07CD 24 inr h 2606 07CE 2E FE mvi l,0FEH ; hl=23FE+n*0x0400 2607 07D0 7E mov a,m ; put offset 2608 07D1 BD cmp l ; don't write past 23FD 2609 07D2 CA DD 07 jz rkbe5 ; jump if at end 2610 07D5 34 inr m ; bump put pointer 2611 07D6 6E mov l,m 2612 07D7 2D dcr l ; hl points at one byte before put ptr offset 2613 07D8 36 07 mvi m,007H ; bell character? 2614 07DA CD 8D 08 call SET_CRT_BUF_ACTIVE rkbe5: 2616 07DD 06 23 mvi b,023H ; '#' 2617 07DF C3 E7 07 jmp SEND_B_IBS rkbe6: 2619 07E2 DA A4 07 jc SEND_E_IB9 ; jump if keycode was >= 0x80 ; fall through ... ;------------------------------------------------------------------------------ ; send E to the CPU via an IBS strobe after waiting for the CPU to ask for it SEND_E_IBS: ; depth=2 2625 07E5 7B mov a,e ; fall through ... ; send A to the CPU via an IBS strobe after waiting for the CPU to ask for it SEND_A_IBS: ; depth=2 2630 07E6 47 mov b,a ; fall through ... ; send B to the CPU via an IBS strobe after waiting for the CPU to ask for it SEND_B_IBS: ; depth=2 2635 07E7 CD A0 00 call CPU_POLL_WAIT 2636 07EA 78 mov a,b 2637 07EB 2F cma 2638 07EC D3 01 out OUT_IB_N 2639 07EE C9 ret ;------------------------------------------------------------------------------ ; has the host given us something to do? OBS, CBS, or !CPB (request response) ; this subroutine doesn't return anything (in a reg or in flags). ; it is called simply to poll for something to do. CHECK_HOST: ; depth=4 (here, call COMPRESS) 2646 07EF DB 01 in IN_2200_STATUS 2647 07F1 E6 0F ani 00FH ; isolate (SEL & !CPB), PRIME, CBS, OBS status 2648 07F3 C8 rz ; return if the cpu hasn't done anything 2649 07F4 47 mov b,a 2650 07F5 E6 03 ani 003H 2651 07F7 C2 05 08 jnz ckhost3 ; jump if CBS or OBS 2652 07FA B0 ora b 2653 07FB E6 04 ani 004H ; isolate PRIME status 2654 07FD CA 7C 07 jz CKHOST2 ; jump if !reset, which means !cpb (host ready for input) 2655 0800 D3 00 out OUT_CLR_PRIME ; clear the reset latch (only places this is done) 2656 0802 C3 EF 07 jmp CHECK_HOST ; poll again ckhost3: ; we've received CBS or OBS strobe 2660 0805 2A 58 22 lhld CUR_BLOCK ; 2000/2400/2800/2C00 2661 0808 44 mov b,h 2662 0809 4E mov c,m ; [2n00] one-hot encoding of this term ; route to the appropriate addr handler 2665 080A DB 03 in IN_OBSCBS_ADDR 2666 080C E6 E0 ani 0E0H ; isolate the address 2667 080E FE C0 cpi 0C0H 2668 0810 D2 90 05 jnc ADDR6_OR_7 ; jump if addr 6 or 7 2669 0813 FE A0 cpi 0A0H 2670 0815 DA 9C 08 jc ADDR1_TO_4 ; jump if addr 1-4 ; board is addressed at addr 5 (VP mode CRT) 2673 0818 CD 33 01 call SET_VP_MODE 2674 081B 11 50 A0 lxi d,0A050H ; d=0xA0 means addr 5, e=0x50 (addr7|5 rbi bit) 2675 081E C3 2D 08 jmp ckhost4 ; received CBS or OBS with addr=7 ADDR7: 2679 0821 11 40 E0 lxi d,0E040H ; d=0xE0 means addr 7, e=0x40 (addr7 rbi bit) 2680 0824 3A 5A 22 lda CUR_TERM 2681 0827 0F rrc 2682 0828 D2 2D 08 jnc ckhost4 ; jump if not term 1 2683 082B 1E 50 mvi e,050H ; e = 0x50 (addr7|5 rbi bit) ; fall through ... ; received CBS or OBS with addr=5 or 7 (send data to crt) ckhost4: 2688 082D 21 5D 2A lxi h,CRT_BUF_ACTIVE 2689 0830 22 5A 26 shld BUF_ACTIVE_IND 2690 0833 21 FE 03 lxi h,03FEH ; index to 23FE+n*0x400, CRT buffer get/put pair ; fall through ... ; board addressed at addr 4 (printer), 5 (vp crt), or 7 (mvp crt) ; and the host CPU has performed either a CBS or OBS. ; hl contains a pointer to the appropriate output buffer (printer or crt) ; d[7:5] contains the address active at the start of the sequence, ; which is used to check that the same port is still addressed later ; e contains an rbi bitmap of which ports correspond to this address CKHOST5: 2700 0836 CD B0 08 call COMPRESS ; keep accumulating bytes, compressing into buffer 2701 0839 C2 70 08 jnz ckhost7 ; jump if ready/busy state hasn't changed ; update the term-local rbi byte and the active rbi status ; to indicate the current port(s) are busy 2705 083C E5 push h 2706 083D 3A 59 22 lda CUR_BLOCK_PAGE ; page0 of current term 2707 0840 67 mov h,a 2708 0841 2E 01 mvi l,001H ; 2n01: per-term rbi status 2709 0843 F3 di 2710 0844 7E mov a,m 2711 0845 B3 ora e ; mark port(s) busy 2712 0846 77 mov m,a 2713 0847 E1 pop h 2714 0848 F3 di ; why di again? di was just 5 instructions ago 2715 0849 3A 57 22 lda SHADOW_RBI 2716 084C B3 ora e ; mark port(s) busy 2717 084D D7 rst 2 ; save A to SHADOW_RBI, send it to !RBI register, enable interrupts 2719 084E DB 01 in IN_2200_STATUS 2720 0850 1F rar 2721 0851 DA 63 08 jc ckhost6 ; OBS is set 2722 0854 1F rar 2723 0855 D2 70 08 jnc ckhost7 ; CBS is not set ; CBS to addr 4/5/7 2726 0858 DB 03 in IN_OBSCBS_ADDR ; card address at time of CBS 2727 085A E6 E0 ani 0E0H ; isolate address 2728 085C BA cmp d 2729 085D CC 9A 09 cz CBS457 ; if addr hasn't changed, handle the CBS(nn) data 2730 0860 C3 70 08 jmp ckhost7 ckhost6: ; OBS to addr 4/5/7 2734 0863 DB 02 in IN_OBUS_N 2735 0865 2F cma 2736 0866 77 mov m,a ; save datum to [hl++] 2737 0867 23 inx h 2738 0868 FE FB cpi 0FBH ; literal FB is escaped as FB D0 2739 086A C2 70 08 jnz ckhost7 2740 086D 36 D0 mvi m,0D0H 2741 086F 23 inx h ; fall through ... ckhost7: 2745 0870 5D mov e,l 2746 0871 2E FE mvi l,0FEH ; 22FE/23FE 2747 0873 7E mov a,m ; fetch old put pointer 2748 0874 73 mov m,e ; update put pointer to where hl stopped 2749 0875 BB cmp e 2750 0876 C8 rz ; return if the put pointer didn't move ; we saved something, so mark the buffer as non-empty 2753 0877 2A 5A 26 lhld BUF_ACTIVE_IND ; pointer to either (PRT|CRT)_BUF_ACTIVE 2754 087A 3A 5A 22 lda CUR_TERM 2755 087D 4F mov c,a 2756 087E CD 90 08 call SET_BUF_ACTIVE 2757 0881 FB ei 2758 0882 C9 ret ;------------------------------------------------------------------------------ ; if [23FE+n] != [23FF+n], ... ; set the CRT_BUF_ACTIVE bit (buffer is non-empty) for the current xterm. ; note it doesn't clear the bit if the buffer is empty. TEST_CRT_BUF_ACTIVE: ; depth=1 2765 0883 60 mov h,b ; 20/24/28/2C 2766 0884 24 inr h 2767 0885 24 inr h 2768 0886 24 inr h 2769 0887 2E FE mvi l,0FEH ; 23FE+n*0x0400 2770 0889 7E mov a,m 2771 088A 23 inx h ; 23FF+n*0x0400 2772 088B BE cmp m 2773 088C C8 rz ; done if [23FE] = [23FF] (guess: put ptr == get ptr) ; fall through ... ; set the CRT_BUF_ACTIVE bit for the current xterm SET_CRT_BUF_ACTIVE: ; depth=1 2778 088D 21 5D 2A lxi h,CRT_BUF_ACTIVE ; fall through ... ; set the buffer active bit corresponding to the current terminal. ; the hl pointer should be set either to CRT_BUF_ACTIVE or PRT_BUF_ACTIVE on entry. SET_BUF_ACTIVE: 2784 0890 F3 di 2785 0891 7E mov a,m ; map of terms with non-empty crt/prt buffers 2786 0892 B1 ora c 2787 0893 77 mov m,a ; mark current term buffer as non-empty 2788 0894 23 inx h ; (CRT/PRT)_STOPPED 2789 0895 2F cma ; A: 1=empty 2790 0896 B6 ora m ; A: 1=empty or stopped 2791 0897 2F cma ; A: 0=empty or stopped; 1=non-empty and not stopped 2792 0898 2B dcx h 2793 0899 2B dcx h 2794 089A 77 mov m,a ; (CRT/PRT)_CAN_TX = ![FOO_STOPPED] && [FOO_BUF_ACTIVE] 2795 089B C9 ret ;------------------------------------------------------------------------------ ; handle CHECK_HOST CBS or OBS to addr 1, 2, 3, or 4 ; for some perverse reason, it isn't inline with the rest of CHECK_HOST code ADDR1_TO_4: 2801 089C FE 80 cpi 080H 2802 089E DA 1E 00 jc GET_OBS_DAT ; if addr 1-3, ignore the OBS/CBS ; OBS to addr 4 sends bytes to the printer 2805 08A1 21 58 2A lxi h,PRT_BUF_ACTIVE 2806 08A4 22 5A 26 shld BUF_ACTIVE_IND 2807 08A7 21 FE 02 lxi h,002FEH ; index into 0x22FE+n*0x400: PRT buffer get/put pointer pair 2808 08AA 11 08 80 lxi d,08008H ; d=0x80 means addr 4, e=0x08 (addr4 rbi bit) 2809 08AD C3 36 08 jmp CKHOST5 ;------------------------------------------------------------------------------ ; called from CKHOST5 when a the first byte has been sent to addr 4/5/7. ; we save the byte in the output buffer and then stay here waiting for ; more CBS/OBS to the same address. If repeated characters occur, this ; routine converts them to the terminal compression encoding. We fall ; out of this routine if the address changes, or we don't see more data ; quickly enough, or if the buffer wraps. ; ; on entry: ; hl contains a pointer to the (printer/crt) output buffer offset ; d[7:5] contains the address active at the start of the sequence, which ; is used to check that the same port is still addressed later ; e contains an rbi bitmap of which ports correspond to this address ; on return: ; the z flag set if the interface must be set to !ready COMPRESS: ; depth=3 (call here, call TIMED_POLL) 2828 08B0 3A 59 22 lda CUR_BLOCK_PAGE ; page0 of current term 2829 08B3 84 add h ; hl is invariably either 0x02FE or 0x03FE 2830 08B4 67 mov h,a ; now hl points to 0x22FE or 0x23FE 2831 08B5 6E mov l,m ; hl is now the current prt/crt buffer put pointer ; check if the current terminal supports Wang compression 2834 08B6 3A 5A 2A lda WANGTERM 2835 08B9 47 mov b,a 2836 08BA 3A 5A 22 lda CUR_TERM 2837 08BD A0 ana b 2838 08BE 32 5C 26 sta CUR_CAN_CMPRS ; the only place this location is set ; prepare to look for runs of repeated characters 2841 08C1 0E FB mvi c,0FBH ; escape code 2842 08C3 41 mov b,c ; b=previous char (esc care ensures on false positive) 2843 08C4 C3 E1 08 jmp cmprs4 ; poll for more OBS data (from the same address in d: 4/5/7) ; and write it into consecutive addresses at [hl]. ; at this point there is no established repeat sequence. ; if we detect a repeat, we will back up and overwrite the initial ; literal characters with a compression sequence. cmprs1: 2851 08C7 DB 01 in IN_2200_STATUS 2852 08C9 0F rrc 2853 08CA D2 E9 08 jnc cmprs5 ; jump if not OBS 2854 08CD DB 03 in IN_OBSCBS_ADDR 2855 08CF E6 E0 ani 0E0H ; isolate addr 2856 08D1 BA cmp d 2857 08D2 C0 rnz ; the address changed, so we are done here 2858 08D3 DB 02 in IN_OBUS_N 2859 08D5 2F cma ; normal polarity cmprs2: 2861 08D6 77 mov m,a ; save next byte 2862 08D7 23 inx h 2863 08D8 B9 cmp c ; c is always 0xFD here 2864 08D9 CA 14 09 jz cmprs8 ; branch if literal 0xFD was received 2865 08DC B8 cmp b 2866 08DD CA 08 09 jz cmprs7 ; branch if this char is same as last cmprs3: 2868 08E0 47 mov b,a ; new previous char ; return if the hl buffer pointer needs to wrap. ; otherwise keep accumulating OBS data cmprs4: 2873 08E1 3E F9 mvi a,0F9H 2874 08E3 BD cmp l 2875 08E4 D2 C7 08 jnc cmprs1 ; keep accumulating if L <= 0xF9 2876 08E7 AF xra a ; set z flag: update ready/busy status 2877 08E8 C9 ret ; return to CKHOST5 ; we've been accumulating OBS data in the hl buffer, ; but we've gotten something other than OBS cmprs5: 2882 08E9 0F rrc 2883 08EA D2 F9 08 jnc cmprs6 ; jump if not CBS 2884 08ED DB 03 in IN_OBSCBS_ADDR 2885 08EF E6 E0 ani 0E0H ; isolate address 2886 08F1 BA cmp d 2887 08F2 C0 rnz ; the address changed, so we are done here 2888 08F3 CD 9A 09 call CBS457 ; CBS(nn) requires special handling 2889 08F6 C3 4A 09 jmp cmprs13 ; no OBS, no CBS. is current port still addressed? cmprs6: 2893 08F9 07 rlc ; \_ restore original IN_2200_STATUS 2894 08FA 07 rlc ; / value read 2895 08FB EE 10 xri 010H ; flip board select bit polarity 2896 08FD E6 F8 ani 0F8H ; ignore prime, cbs, obs bits 2897 08FF BA cmp d 2898 0900 C0 rnz ; return if addr mismatch, or !board_sel, or !CPB 2899 0901 CD 78 09 call POLL_150 ; see if we get a quick CBS or OBS 2900 0904 C0 rnz ; don't wait any longer if none 2901 0905 C3 C7 08 jmp cmprs1 ; process the new byte ; the start of a repeated sequence has been seen cmprs7: 2905 0908 3A 5C 26 lda CUR_CAN_CMPRS 2906 090B A7 ana a 2907 090C CA E1 08 jz cmprs4 ; jump if terminal doesn't support compression 2908 090F 0E 02 mvi c,2 ; we've seen the start of a run 2909 0911 C3 3E 09 jmp cmprs11 ; we got a literal byte. emit escape sequence. cmprs8: 2913 0914 36 D0 mvi m,0D0H 2914 0916 23 inx h 2915 0917 C3 E0 08 jmp cmprs3 cmprs9: 2918 091A 0F rrc 2919 091B DA 44 09 jc cmprs12 ; jump if CBS 2920 091E CD 74 09 call TIMED_POLL ; time-limited poll for OBS or CBS, 'L' attempts made ; Bizarrely, the caller doesn't set L; instead it is just whatever the ; output buffer put pointer happens to be. I guess the idea is that is ; just isn't unbounded, but the window varies from 50us to 12ms! 2924 0921 C2 44 09 jnz cmprs12 ; didn't get a CBS or OBS in a timely fashion cmprs10: 2926 0924 DB 01 in IN_2200_STATUS 2927 0926 0F rrc 2928 0927 D2 1A 09 jnc cmprs9 ; jump if not OBS 2929 092A DB 03 in IN_OBSCBS_ADDR 2930 092C E6 E0 ani 0E0H ; isolate addr 2931 092E BA cmp d 2932 092F C0 rnz ; return if addr has changed 2933 0930 DB 02 in IN_OBUS_N 2934 0932 2F cma ; normal polarity 2935 0933 B8 cmp b 2936 0934 C2 50 09 jnz cmprs14 ; jump if it ends the repeat 2937 0937 0C inr c ; repeat_count++ 2938 0938 3E 4F mvi a,79 2939 093A B9 cmp c 2940 093B DA 44 09 jc cmprs12 ; jump if count >= 80 ; fall through ... ; we have started a run of identical characters (two or more) ; the repetition count is in c, and the prev char is in b. cmprs11: 2946 093E 3E F9 mvi a,0F9H 2947 0940 BD cmp l 2948 0941 D2 24 09 jnc cmprs10 ; keep accumulating if pointer <= 0x23F9+n*0x400 ; fall through ... ; end the repeated char sequence at this point for one of these reasons: ; (1) we got a CBS (ending the string of OBS data) ; (2) we didn't see a CBS or OBS in a timely manner ; (3) the repeat count is 80 characters cmprs12: 2956 0944 3E 02 mvi a,2 2957 0946 B9 cmp c 2958 0947 DC 5E 09 cc CMPRS15 ; rewrite with compression if count > 2 cmprs13: 2960 094A 0E FB mvi c,0FBH ; anticipate looking for literal later 2961 094C 41 mov b,c ; prev char= prevents matching 2962 094D C3 E1 08 jmp cmprs4 ; received a character that doesn't continue a repeat string. ; a=new char, b=prev char, c=how many repeats cmprs14: 2967 0950 F5 push psw ; depth 2968 0951 3E 02 mvi a,2 2969 0953 B9 cmp c 2970 0954 DC 5E 09 cc CMPRS15 ; if count > 2, rewrite first two inline repeated characters with compression ; otherwise let literal inline bytes stay where they are 2972 0957 0E FB mvi c,0FBH ; anticipate looking for literal later 2973 0959 41 mov b,c ; prev char= prevents matching 2974 095A F1 pop psw 2975 095B C3 D6 08 jmp cmprs2 ; keep receiving OBS bytes, and start a new sequence perhaps ; two bytes literal bytes were written into the buffer before detecting the sequence. ; back up two bytes and overwrite with the compression sequence (either space or char) ; b=character being sent, c=number of times ; ; as literal bytes are saved the buffer, hl is simply incremented up to, and ; including, 0x23F9. But the three byte sequence can write 0x23F8/F9/FA. ; The crt buffer isn't circular, instead the host fills the buffer and the tx ; process drains it, then we can fill it again. Nothing uses 23FA/FB/FC, ; so perhaps that allows for a little bit of overrun. CMPRS15: ; depth=1 2987 095E 2B dcx h 2988 095F 2B dcx h 2989 0960 36 FB mvi m,0FBH ; compression token 2990 0962 23 inx h 2991 0963 3E 20 mvi a,' ' 2992 0965 B8 cmp b 2993 0966 CA 6E 09 jz cmprs16 ; use space compression sequence 2994 0969 71 mov m,c ; repeat count 2995 096A 23 inx h 2996 096B 70 mov m,b ; char to repeat 2997 096C 23 inx h 2998 096D C9 ret ; emit space compression sequence cmprs16: 3002 096E 3E 60 mvi a,060H 3003 0970 81 add c 3004 0971 77 mov m,a ; 3005 0972 23 inx h 3006 0973 C9 ret ;------------------------------------------------------------------------------ ; Time-limited poll for CBS or OBS (about L*50 usec). ; (the loop period is 88 cycles; at 1.78Mhz, this is about 50 uS) ; If CBS or OBS are set, return with z=true (oddly, addr isn't checked) ; otherwise return with z=false. TIMED_POLL: ; depth=2 (call here, plus a push) 3014 0974 E5 push h 3015 0975 C3 85 09 jmp tpoll2 ; Like TIMED_POLL, but limited to 150 usec (three attempts) POLL_150: ; depth=2 (call here, plus a push) 3019 0978 E5 push h 3020 0979 2E 03 mvi l,3 ; loop counter tpoll1: 3022 097B DB 01 in IN_2200_STATUS 3023 097D 0F rrc 3024 097E DA 97 09 jc tpoll3 ; jump if OBS 3025 0981 0F rrc 3026 0982 DA 97 09 jc tpoll3 ; jump if CBS tpoll2: 3028 0985 07 rlc 3029 0986 07 rlc 3030 0987 EE 10 xri 010H ; flip board select 3031 0989 E6 F8 ani 0F8H ; 1111 1000 3032 098B BA cmp d 3033 098C C2 98 09 jnz tpoll4 ; done if addr changed, !board_sel, or !cpb 3034 098F 2D dcr l 3035 0990 C2 7B 09 jnz tpoll1 3036 0993 F6 01 ori 001H ; clear z flag 3037 0995 E1 pop h 3038 0996 C9 ret tpoll3: 3041 0997 AF xra a ; set z flag tpoll4: 3043 0998 E1 pop h 3044 0999 C9 ret ;------------------------------------------------------------------------------ ; this is called when CBS(nn) occurs at addr 4/5/7 ; ; quoting 2236MXE_Documentation.8-83.pdf (pdf page 16) ; In addition to OBS, with data to be displayed, this address also supports ; CBS's at this address. When the controller receives a CBS(xx) at address ; 07h it should place a FBh before it in the buffer. This will cause the ; terminal to accept this string as a command. The following are the three ; possible commands to the terminal. ; -- repeat times, count 0x00 to 0x50 ; -- x=0..9; delay x/6 seconds at the terminal ; -- literal character ; ; For addr 4 (printer), compression is not allowed, but must still be ; expanded to . ; ; It doesn't mention if addr 5 allows it, but I suspect not. CBS457: ; depth=2 (call here, rst 2) 3064 099A DB 02 in IN_OBUS_N 3065 099C 2F cma ; non-inverted polarity 3066 099D FE 51 cpi 051H 3067 099F DA AE 09 jc cbs457b ; jump if <= 0x50 3068 09A2 FE C0 cpi 0C0H 3069 09A4 D8 rc ; return if < 0xC0 3070 09A5 FE D0 cpi 0D0H 3071 09A7 D0 rnc ; return if >= 0xD0 3072 09A8 36 FB mvi m,0FBH ; escape code cbs457a: 3074 09AA 23 inx h ; bump past escape code 3075 09AB 77 mov m,a ; save char 3076 09AC 23 inx h 3077 09AD C9 ret ; CBS(nn), nn <= 0x50 means repeat the next OBS char times. ; it isn't expanded locally; just send the bytes. cbs457b: 3082 09AE 36 FB mvi m,0FBH ; escape code 3083 09B0 23 inx h 3084 09B1 77 mov m,a 3085 09B2 F3 di 3086 09B3 3A 57 22 lda SHADOW_RBI 3087 09B6 2F cma 3088 09B7 B3 ora e ; mark port(s) ready, as we need to get an OBS 3089 09B8 2F cma 3090 09B9 D7 rst 2 ; save A to SHADOW_RBI, send it to !RBI register, enable interrupts cbs457c: 3092 09BA DB 01 in IN_2200_STATUS 3093 09BC 0F rrc 3094 09BD D2 BA 09 jnc cbs457c ; wait for OBS (repeated char value) 3095 09C0 3E F9 mvi a,0F9H 3096 09C2 BD cmp l 3097 09C3 D2 CB 09 jnc cbs457d ; jump if L <= 0xF9 (not end of buffer) ; hit end of buffer; mark port(s) busy 3099 09C6 3A 57 22 lda SHADOW_RBI 3100 09C9 B3 ora e ; mark port(s) busy 3101 09CA CF rst 1 ; save A to SHADOW_RBI, then send it to !RBI register cbs457d: 3103 09CB DB 02 in IN_OBUS_N ; fetch the OBS value 3104 09CD 2F cma 3105 09CE FE 20 cpi ' ' 3106 09D0 C2 AA 09 jnz cbs457a ; use three byte sequence 3107 09D3 7E mov a,m 3108 09D4 C6 60 adi 060H ; use two byte space sequence 3109 09D6 77 mov m,a 3110 09D7 23 inx h 3111 09D8 C9 ret ;------------------------------------------------------------------------------ ; Interrupt handler. The mxd uses interrupt only for rx activity. ; verify that the byte was received without error (echo '#' if not). ; keys are received in pairs of (prefix, value) bytes. Once a pair is assembled, ; it is pushed into the keyboard buffer. ; ; If a FC/FD/FE/FF byte is received, it is saved as the prefix byte; the next ; byte received is the associated value and the pair are pushed as-is into the ; KB buffer. But if there is no pending prefix byte, the various values are ; interpreted as follows: ; ; rx error, 00-07, 09-0C, 0E-0F -> FF F8 ; 08 -> FF 08 (backspace) ; 0D -> FF 0D (return) ; 12 -> (reset: immediate action) ; 13 -> (halt: immediate action) ; 10-11,14-7F -> FC <10-11,14-7F> ; 80-BC -> FD (81=CLEAR,82=RUN,84=CONTINUE,A0=PRINT,A1=LOAD) ; BD -> FD BD (edit key) ; BE -> FD 7E (lowercase FN) ; BF -> FD 7F (uppercase FN) ; C0-DF -> FD <00-1F> (s.f. keys) ; E0-F7 -> FF (E5=line erase) ; F8/F9/FA/FB -> (flow control handled immediately, not queued) ; FC/FD/FE/FF -> (save as prefix for next byte) INT_HANDLER: 3140 09D9 DB 04 in IN_UART_RXRDY 3141 09DB E6 0F ani 00FH 3142 09DD CA 83 0A jz INT_EXIT ; done if something other than RX activity 3143 09E0 CD 20 01 call TERM_LOOKUP ; choose one of the ready RX ports 3144 09E3 79 mov a,c 3145 09E4 D3 05 out OUT_UART_SEL ; select the chosen UART 3146 09E6 2E 17 mvi l,017H ; 2n17: rx sequence prefix byte 3147 09E8 56 mov d,m ; see if there was a prefix character 3148 09E9 36 00 mvi m,000H ; forget it for next time 3149 09EB DB 0E in IN_UART_STATUS ; retrieve status associated with char 3150 09ED EE 80 xri 080H ; invert DSR bit 3151 09EF E6 B8 ani 0B8H ; 10111000b = leave DSR/FE/OE/PE bits 3152 09F1 DB 06 in IN_UART_DATA ; fetch the character which caused the interrupt 3153 09F3 FA 83 0A jm INT_EXIT ; done if !DSR (received a byte even though we said we weren't ready for it) 3154 09F6 CA 00 0A jz int_hand1 ; jump if no FE/OE/PE errors 3155 09F9 3E 37 mvi a,037H ; 00110111b = RTS + error flag reset + rx enable + dtr + tx enable 3156 09FB D3 0E out OUT_UART_CMD 3157 09FD C3 25 0A jmp int_hand2 ; we received an error-free character from the terminal (in A) ; D holds a possible prefix character (00 if none) int_hand1: 3162 0A00 5F mov e,a ; E now holds the rx character 3163 0A01 AF xra a 3164 0A02 BA cmp d 3165 0A03 C2 48 0A jnz PUSH_KEY_PAIR ; jump if there was a prefix character ; from here out, we have received a character and no prefix byte. 3168 0A06 B3 ora e ; A holds the rx character again (saves 1 cycle over mov a,e) 3169 0A07 FE F8 cpi 0F8H 3170 0A09 D2 CF 0A jnc RX_GE_F8 ; jump if >= 0xF8 (F8..FB are flow control) 3171 0A0C FE 14 cpi 014H 3172 0A0E D2 2C 0A jnc int_hand4 ; jump if >= 0x14 3173 0A11 FE 12 cpi 012H 3174 0A13 D2 8E 0A jnc RX_12_OR_13 ; jump if 0x12 or 0x13 (reset or halt key) 3175 0A16 FE 0D cpi 00DH ; carriage return 3176 0A18 CA 27 0A jz int_hand3 3177 0A1B FE 08 cpi 008H ; backspace 3178 0A1D CA 27 0A jz int_hand3 3179 0A20 FE 10 cpi 010H 3180 0A22 D2 2C 0A jnc int_hand4 ; jump if not a control character ; fall through ... ; we received some char with error, or a ... ; control char other than 0x08 or 0x0D, and no prefix byte (fall through case) int_hand2: 3186 0A25 1E F8 mvi e,0F8H ; pretend we received 0xF8 char int_hand3: 3188 0A27 16 FF mvi d,0FFH ; set the prefix byte to 0xFF 3189 0A29 C3 48 0A jmp PUSH_KEY_PAIR ; pair, is 08/0D/BD/>=E0, or F8(error) ; we received 0x10, 0x11, or 0x14 <= char < 0xF8, and no prefix byte int_hand4: 3193 0A2C FE 80 cpi 080H 3194 0A2E DA 46 0A jc int_hand6 ; jump if < 0x80 --> 3195 0A31 FE E0 cpi 0E0H 3196 0A33 D2 27 0A jnc int_hand3 ; jump if >= 0xE0 --> 3197 0A36 FE BD cpi 0BDH ; EDIT key 3198 0A38 DA 41 0A jc int_hand5 ; jump if < 0xBD --> 3199 0A3B CA 27 0A jz int_hand3 ; jump if = 0xBD --> 3200 0A3E EE C0 xri 0C0H ; map SF keys (0xC0-0xDF) to (0x00-0x1F), FN (BE-BF) to (7E-7F) 3201 0A40 5F mov e,a int_hand5: ; save in key buffer as endi/atom pair () 3204 0A41 16 FD mvi d,0FDH ; prefix 0xFD = 253 3205 0A43 C3 48 0A jmp PUSH_KEY_PAIR ; we received 0x10, 0x11, or 0x14 <= char < 0x80 ; save in key buffer as transparent pair () int_hand6: 3210 0A46 16 FC mvi d,0FCH ; prefix 0xFC = 252 ; fall through ... ;------------------------------------------------------------------------------ ; save a keystroke to the KEYBOARD buffer, which is 32 entries of 2 bytes each. ; a key event is encoded as a pair of bytes with D=prefix byte, E=key. PUSH_KEY_PAIR: 3217 0A48 21 5F 2A lxi h,KB_BUF_READY 3218 0A4B 7E mov a,m 3219 0A4C B1 ora c ; OR in current port bit 3220 0A4D 77 mov m,a 3221 0A4E 60 mov h,b 3222 0A4F 2E 63 mvi l,063H ; 2n63: line_req flag byte 3223 0A51 7E mov a,m 3224 0A52 E6 02 ani 002H ; test KEYIN_LINE_REQ flag 3225 0A54 C4 92 00 cnz SET_KB_RDY_STATUS ; if command is KEYIN_LINE_REQ, set terminal status response indicating a key is ready 3226 0A57 3A 56 22 lda MVP_MODE 3227 0A5A A7 ana a 3228 0A5B C2 69 0A jnz pkp1 ; jump if mvp mode 3229 0A5E 79 mov a,c 3230 0A5F 0F rrc 3231 0A60 D2 83 0A jnc INT_EXIT ; bail if not term 1 3232 0A63 3A 57 22 lda SHADOW_RBI 3233 0A66 E6 FE ani 0FEH ; addr 1 (keyboard) is ready 3234 0A68 CF rst 1 ; save A to SHADOW_RBI, then send it to !RBI register pkp1: 3236 0A69 2E 18 mvi l,018H ; 2n18: count of entries in rx kb buffer 3237 0A6B 60 mov h,b 3238 0A6C 7E mov a,m ; get count of saved keystrokes 3239 0A6D FE 20 cpi 020H ; >=32 entries? 3240 0A6F D2 83 0A jnc INT_EXIT ; bail if buffer is full 3241 0A72 34 inr m ; increment keystroke count 3242 0A73 23 inx h ; 2n19 3243 0A74 34 inr m 3244 0A75 34 inr m ; bump by 2 (two bytes per rx char) 3245 0A76 7E mov a,m 3246 0A77 FE 5B cpi 05BH ; end of kb buffer? (2n5B is just past the end) 3247 0A79 DA 7F 0A jc pkp2 ; not wrapping 3248 0A7C 3E 1B mvi a,01BH ; yes; wrap back to start 3249 0A7E 77 mov m,a pkp2: 3251 0A7F 6F mov l,a 3252 0A80 72 mov m,d ; save prefix byte 3253 0A81 23 inx h 3254 0A82 73 mov m,e ; save char ; fall through ... INT_EXIT: 3258 0A83 3A 5C 22 lda CUR_UART 3259 0A86 D3 05 out OUT_UART_SEL ; restore default UART select 3260 0A88 F1 pop psw ; clean up (see INTERRUPT push sequence) 3261 0A89 C1 pop b 3262 0A8A D1 pop d 3263 0A8B E1 pop h 3264 0A8C FB ei 3265 0A8D C9 ret ; we received 0x12 (RESET) or 0x13 (HALT) from the terminal RX_12_OR_13: 3269 0A8E 21 63 2A lxi h,HALT_MAP 3270 0A91 C2 AF 0A jnz RX_RST_HALT ; jump if char is 0x13 (HALT) ; reset has been pressed 3273 0A94 3A 56 22 lda MVP_MODE 3274 0A97 A7 ana a 3275 0A98 C2 A5 0A jnz RX_RST ; jump if MVP mode ; we are in VP mode 3278 0A9B 79 mov a,c 3279 0A9C 0F rrc 3280 0A9D D2 83 0A jnc INT_EXIT ; ignore it if not term 1 3281 0AA0 D3 02 out OUT_PRIME ; strobe bus reset 3282 0AA2 C3 48 01 jmp POWER_ON_INIT ; reset key pressed in MVP mode RX_RST: ; in WANGTERM, clear the bit corresponding to the current term 3287 0AA5 79 mov a,c 3288 0AA6 2F cma 3289 0AA7 21 5A 2A lxi h,WANGTERM 3290 0AAA A6 ana m 3291 0AAB 77 mov m,a 3293 0AAC 21 62 2A lxi h,RESET_MAP ; fall through ... ; HL=RESET_MAP if char was 0x12 or HL=HALT_MAP if it was 0x13 RX_RST_HALT: 3298 0AAF 7E mov a,m 3299 0AB0 B1 ora c 3300 0AB1 77 mov m,a ; mark which term hit RESET or HALT 3301 0AB2 CD 98 00 call ADDR2_READY ; indicate status has changed 3302 0AB5 CD A6 0F call RESET_KB_BUF 3303 0AB8 3A 56 22 lda MVP_MODE 3304 0ABB A7 ana a 3305 0ABC C2 83 0A jnz INT_EXIT ; done if MVP mode 3306 0ABF 79 mov a,c 3307 0AC0 0F rrc 3308 0AC1 D2 83 0A jnc INT_EXIT ; done if not port 1 3309 0AC4 3A 57 22 lda SHADOW_RBI 3310 0AC7 F6 01 ori 001H ; addr 1 (KB) is not ready 3311 0AC9 CF rst 1 ; save A to SHADOW_RBI, then send it to !RBI register 3312 0ACA D3 03 out OUT_HALT_STEP ; strobe real HALT line on bus as we are in VP mode 3313 0ACC C3 83 0A jmp INT_EXIT ;------------------------------------------------------------------------------ ; we've received a character >= 0xF8 from the terminal, with no prefix byte RX_GE_F8: 3318 0ACF FE FC cpi 0FCH 3319 0AD1 D2 F9 0A jnc rxgf8d ; jump if >= 0xFC (not a flow control char) ; 0xF8-0xFB are flow control characters from the terminal to the MXD ; F8: CRT go ; F9: PRINT go ; FA: CRT stop ; FB: PRINT stop ; these don't go into the rx queue and immediately affect the flow ; control state. 3327 0AD4 21 59 2A lxi h,PRT_STOPPED ; printer flow control status 3328 0AD7 0F rrc ; bit 0: evens are crt-related, odds are prt-related 3329 0AD8 DA DE 0A jc rxgf8a ; jump if printer flow control 3330 0ADB 21 5E 2A lxi h,CRT_STOPPED ; crt flow control status 3331 0ADE 0F rxgf8a: rrc ; bit 1: 0 is go, 1 is stop 3332 0ADF 79 mov a,c ; current term bitmap 3333 0AE0 DA E8 0A jc rxgf8b ; jump if "stop" flow control byte ; we have a "go" flow control byte 3335 0AE3 2F cma 3336 0AE4 A6 ana m ; clear bit for current term 3337 0AE5 C3 E9 0A jmp rxgf8c ; we have a "stop" flow control byte 3340 0AE8 B6 rxgf8b: ora m ; set bit for current term 3341 0AE9 77 rxgf8c: mov m,a ; save updated *_STOPPED byte 3342 0AEA 2F cma ; now 0=stopped, 1=not stopped 3343 0AEB 2B dcx h ; (PRT/CRT)_BUF_ACTIVE 3344 0AEC A6 ana m ; clear bit if the buffer is empty 3345 0AED 2B dcx h ; (PRT/CRT)_CAN_TX 3346 0AEE 77 mov m,a ; 0=flow controlled or buffer empty ; remember that we've seen flow control, meaning the terminal ; can handle compression and such. this provision seems half- ; baked, though, as there are so many unconditional dependencies 3351 0AEF 3A 5A 2A lda WANGTERM 3352 0AF2 B1 ora c ; set bit corresponding to current term 3353 0AF3 32 5A 2A sta WANGTERM ; 1=(this term && !flow_controlled && !empty) 3354 0AF6 C3 83 0A jmp INT_EXIT ; received character >= 0xFC from the terminal without prior prefix byte. ; only FC/FD/FE are documented. but if FF is received, save that too. ; save the rx byte as the prefix to the next byte. rxgf8d: 3360 0AF9 60 mov h,b 3361 0AFA 2E 17 mvi l,017H 3362 0AFC 77 mov m,a ; [2n17] = prefix character of some sequence 3363 0AFD C3 83 0A jmp INT_EXIT ;------------------------------------------------------------------------------ ; subroutine: if kb rx buffer is empty, set RBI to not ready CHK_KB_BUF: ; depth=1 3368 0B00 2E 18 mvi l,018H ; 2n18: count of entries in rx kb buffer 3369 0B02 F3 di 3370 0B03 7E mov a,m 3371 0B04 A7 ana a 3372 0B05 C0 rnz ; return if buffer is non-empty ; fall through ... ; subroutine: mark kb rx buffer as empty, and set RBI for addr 1 to not ready ; return with A=0x00 and z flag set (not sure anyone uses this) MARK_KB_BUF_MT: ; depth=1 3378 0B06 79 mov a,c 3379 0B07 2F cma 3380 0B08 21 5F 2A lxi h,KB_BUF_READY 3381 0B0B A6 ana m ; clear kb buf ready status for this term 3382 0B0C 77 mov m,a 3383 0B0D 79 mov a,c 3384 0B0E 0F rrc 3385 0B0F D2 17 0B jnc mkbmt1 ; jump if not term 1 3386 0B12 3A 57 22 lda SHADOW_RBI 3387 0B15 B1 ora c ; mark not ready 3388 0B16 CF rst 1 ; save A to SHADOW_RBI, then send it to !RBI register mkbmt1: 3390 0B17 AF xra a 3391 0B18 60 mov h,b 3392 0B19 C9 ret ;------------------------------------------------------------------------------ ; fetch the next entry from the KB rx buffer, ; returning with A=entry prefix byte, E=entry char value. ; if the kb buffer is empty, A=0xFF, E=0x00. NEXT_KB_PAIR: ; depth=2 (call here, call CHK_KB_BUF) 3399 0B1A 60 mov h,b ; save 20/24/28/2C page info 3400 0B1B 2E 16 mvi l,016H ; 2n16 3401 0B1D 7E mov a,m 3402 0B1E A7 ana a 3403 0B1F C2 3F 0B jnz nkbp2 3404 0B22 CD 00 0B call CHK_KB_BUF ; check if kb buffer is empty 3405 0B25 CA 4C 0B jz gkbe4 ; jump if it is empty 3406 0B28 35 dcr m ; hl=2n18; decrement entry count 3407 0B29 CC 06 0B cz MARK_KB_BUF_MT ; set addr 1 status if newly empty 3408 0B2C 2E 1A mvi l,01AH ; 2n1A: get pointer of rx buffer 3409 0B2E 34 inr m 3410 0B2F 34 inr m ; bump by 2 (two bytes per rx char) 3411 0B30 7E mov a,m 3412 0B31 FE 5B cpi 05BH ; end of kb buffer? (2n5B is just past the end) 3413 0B33 DA 39 0B jc nkbp1 ; not wrapping 3414 0B36 3E 1B mvi a,01BH ; wrap to start of buffer 3415 0B38 77 mov m,a nkbp1: 3417 0B39 6F mov l,a 3418 0B3A 7E mov a,m ; A=prefix byte 3419 0B3B 23 inx h 3420 0B3C 5E mov e,m ; E=char value 3421 0B3D FB ei 3422 0B3E C9 ret nkbp2: 3425 0B3F 36 00 mvi m,000H 3426 0B41 5F mov e,a 3427 0B42 CD 00 0B call CHK_KB_BUF 3428 0B45 FB ei 3429 0B46 3E FC mvi a,0FCH 3430 0B48 C9 ret ;------------------------------------------------------------------------------ ; mark the keyboard buffer as being empty and reenable interrupts MARK_KB_MT_EI: ; depth=2 3435 0B49 CD 06 0B call MARK_KB_BUF_MT gkbe4: ; return status indicating empty buffer 3437 0B4C FB ei 3438 0B4D 3E FF mvi a,0FFH 3439 0B4F 1E 00 mvi e,000H 3440 0B51 C9 ret ;------------------------------------------------------------------------------ ; got some kind of unexpected byte or a byte with parity error ; emit '#' to indicate the bad character BAD_BYTE: 3446 0B52 3E 23 mvi a,'#' 3447 0B54 CD 01 0C call A_TO_LINE_REQ ; fall through ... ; continuation of ERROR_LINE_REQ processing ERROR_LINE_REQ2: ; [2n5C] no further continuations 3453 0B57 60 mov h,b 3454 0B58 2E 5C mvi l,05CH 3455 0B5A 36 00 mvi m,000H ; de = [2n69/6A] (cursor location) 3458 0B5C 2E 69 mvi l,069H 3459 0B5E 56 mov d,m 3460 0B5F 23 inx h 3461 0B60 5E mov e,m ; de=max(cursor location, last byte used), hl=min(...) 3464 0B61 2E 67 mvi l,067H 3465 0B63 CD 6C 00 call HLPTR_MAXMIN_DE2 ; hl <- [2n67/68]; conditionally swap hl/de 3466 0B66 EB xchg ; [2n67/68] (last byte used) = max(cursor location, last byte used) 3469 0B67 60 mov h,b 3470 0B68 2E 67 mvi l,067H ; 2n67 3471 0B6A 72 mov m,d 3472 0B6B 23 inx h 3473 0B6C 73 mov m,e 3475 0B6D 3E 07 mvi a,007H ; bell 3476 0B6F EF rst 5 ; save bell in the terminal output buffer 3477 0B70 CD A6 0F call RESET_KB_BUF ; ignore any accumulated keys ; fall through ... ;------------------------------------------------------------------------------ ; After processing a key event, the various handlers (mostly) dump back here ; to continue the rx polling process. RX_POLL1: 3484 0B73 2A 57 26 lhld LINE_REQ_BLOCK 3485 0B76 44 mov b,h 3486 0B77 4E mov c,m ; one-hot terminal encoding 3487 0B78 CD 83 08 call TEST_CRT_BUF_ACTIVE rxp2: 3489 0B7B FB ei ; fall through ... RXP3: 3493 0B7C 3A 56 26 lda PENDING_TERMS 3494 0B7F E6 0F ani 00FH 3495 0B81 CA B9 01 jz MAIN_EVENT ; fall through ... ; pick a terminal with rx chars, grab the next char, and act on it ; the calling routine set the bitmask in A to only allow terms which ; are processing a KEYIN or LINE_REQ command. RX_POLL: ; pick a terminal to work on 3503 0B84 CD 20 01 call TERM_LOOKUP ; c=chosen port bitmap, b=h=page0 ptr, a=remaining ports 3504 0B87 32 56 26 sta PENDING_TERMS ; these uarts have rx data to be unloaded 3505 0B8A 78 mov a,b 3506 0B8B 32 58 26 sta LINE_REQ_PAGE ; change page of LINE_REQ_BLOCK (20/24/28/2C) 3508 0B8E 21 FE 03 lxi h,03FEH ; crt buffer pointer offset 3509 0B91 7C mov a,h 3510 0B92 80 add b 3511 0B93 67 mov h,a ; 0x23FE+n*0x400 = crt put pointer ; the crt buffer is not circular. downstream logic may copy blocks of bytes ; from the line_req buffer to the crt buffer, and no end-of-buffer condition ; is tested. instead, we ensure that there is at least (250-35), or 215, bytes ; free in the buffer. note we are testing the position of the put ptr, and not ; (put-get). if the put ptr is too far along, just give up for now and eventually ; the crt buffer will be drained to the uart tx buffer and the put ptr will get ; reset to zero. 3520 0B94 7E mov a,m ; fetch put pointer 3521 0B95 FE 23 cpi 023H ; don't process kb events if crt output buffer is too deep 3522 0B97 D2 7C 0B jnc RXP3 ; jump if >= 35 ; set global underline flag byte if current line_req asked for it 3525 0B9A 60 mov h,b 3526 0B9B 2E 63 mvi l,063H 3527 0B9D 7E mov a,m ; a = [2n63] 3528 0B9E E6 80 ani 080H ; isolate underline flag 3529 0BA0 32 59 26 sta ULINE_FLAG ; dispatch to continuation routine if current command isn't complete. ; any time the continuation byte is set, [KB_BUF_READY] is also set, ; which ensures this will get called in the RX_POLL loop. 3534 0BA3 2E 5C mvi l,05CH 3535 0BA5 7E mov a,m ; a = [2n5C]: continuation byte 3536 0BA6 A7 ana a 3537 0BA7 C2 A5 05 jnz IDXJMPA 3539 0BAA CD 1A 0B call NEXT_KB_PAIR ; a=prefix byte, e=key code 3540 0BAD 57 mov d,a ; d=prefix byte 3542 0BAE 60 mov h,b 3543 0BAF 2E 63 mvi l,063H ; hl=2n63 (flag byte) 3545 0BB1 3C inr a 3546 0BB2 CA E9 0B jz KB_TYPE_FF ; jump if 0xFF (buffer empty or special) 3547 0BB5 3C inr a 3548 0BB6 CA CC 0B jz KB_TYPE_FE ; jump if 0xFE = dead key 3549 0BB9 3C inr a 3550 0BBA CA B5 0F jz KB_TYPE_FD ; jump if 0xFD = EndI(<=x7f)/Atom(>=x80) ; must be type FC, which is "transparent" class (nothing special) 3552 0BBD 7B mov a,e 3553 0BBE FE 0D cpi 00DH 3554 0BC0 CA 43 0D jz L0D43 ; jump if carriage return 3555 0BC3 CD 01 0C call A_TO_LINE_REQ rxp4: 3557 0BC6 CC 26 00 cz BEEP ; beep if there was no room 3558 0BC9 C3 73 0B jmp RX_POLL1 ;------------------------------------------------------------------------------ ; received a dead key: echo it, then backspace KB_TYPE_FE: 3563 0BCC 7B mov a,e 3564 0BCD FE 08 cpi 008H 3565 0BCF CA DE 0B jz kbtfe2 ; jump if backspace 3566 0BD2 CD 01 0C call A_TO_LINE_REQ 3567 0BD5 CA C6 0B jz rxp4 ; z means no room: beep and quit kbtfe1: 3569 0BD8 11 FF FF lxi d,0FFFFH ; -1: move cursor left 1 place 3570 0BDB C3 9B 0D jmp CUR_LEFT_DE kbtfe2: ; <08> sequence 3574 0BDE 3E 20 mvi a,' ' 3575 0BE0 CD 01 0C call A_TO_LINE_REQ 3576 0BE3 CA 73 0C jz DO_BS ; z means no room 3577 0BE6 C3 D8 0B jmp kbtfe1 ;------------------------------------------------------------------------------ ; pair pulled from kb buffer ; inspecting the table in the comment before INT_HANDLER, ; the only bytes we should expect are 08, 0D, E0-F7. KB_TYPE_FF: 3584 0BE9 7B mov a,e 3585 0BEA FE 10 cpi 010H 3586 0BEC DA 69 0C jc KB_FF_CTRL ; jump if a control code 3587 0BEF FE BD cpi 0BDH 3588 0BF1 CA 5B 0C jz KB_FF_EDIT ; jump if EDIT key 3589 0BF4 FE E6 cpi 0E6H ; key for statement number 3590 0BF6 DA 45 0C jc KB_FF_LT_E6 ; jump if < 0xE6 3591 0BF9 C2 52 0B jnz BAD_BYTE ; jump if not 0xE6 3592 0BFC 3E FE mvi a,0FEH ; map to key pair (TBD what does this mean?) 3593 0BFE C3 43 0D jmp L0D43 ; report (it was ) ;------------------------------------------------------------------------------ ; stuff A into line request buffer, updating the (cursor location) and ; (last byte used) pointers, as well as the dirty span. ; return with Z flag set if no room for it, or if CR is passed in A_TO_LINE_REQ: ; depth=3 (call here, call A_TO_CRT_BUF) 3600 0C01 FE 0D cpi 00DH 3601 0C03 C8 rz ; ignore carriage return 3602 0C04 F5 push psw ; save new character ; de =[2n69/6A] 3605 0C05 60 mov h,b 3606 0C06 2E 6A mvi l,06AH 3607 0C08 5E mov e,m 3608 0C09 2B dcx h 3609 0C0A 56 mov d,m 3611 0C0B EB xchg 3612 0C0C 77 mov m,a ; store character to location pointed to by [2n69/6A] 3613 0C0D EB xchg ; jump to atlr2 if [2n67/68](last used byte) != [2n69/6A](cursor location) 3616 0C0E 2B dcx h 3617 0C0F 7E mov a,m 3618 0C10 BB cmp e 3619 0C11 C2 2D 0C jnz atlr2 3620 0C14 2B dcx h 3621 0C15 7E mov a,m 3622 0C16 BA cmp d 3623 0C17 C2 2D 0C jnz atlr2 ; if [2n65/66](end of buffer) == [2n69/6A](cursor location), ignore request 3626 0C1A 2B dcx h 3627 0C1B 7E mov a,m 3628 0C1C BB cmp e 3629 0C1D C2 26 0C jnz atlr1 3630 0C20 2B dcx h 3631 0C21 7E mov a,m 3632 0C22 BA cmp d 3633 0C23 CA 35 00 jz POPRET ; give up ; the cursor is at the last byte used, but we aren't at the end of buffer atlr1: ; increment [2n67/68](last byte used) 3638 0C26 13 inx d 3639 0C27 2E 67 mvi l,067H 3640 0C29 72 mov m,d 3641 0C2A 23 inx h 3642 0C2B 73 mov m,e 3643 0C2C 1B dcx d ; revert de ; fall through ... atlr2: ; increment [2n69/6A](cursor location) 3648 0C2D 13 inx d 3649 0C2E 2E 69 mvi l,069H 3650 0C30 72 mov m,d 3651 0C31 23 inx h 3652 0C32 73 mov m,e ; echo the character to the crt buffer 3655 0C33 F1 pop psw ; recall original character value 3656 0C34 CD 7A 00 call A_TO_CRT_BUF ; echo the character ; [2n64]++ (bump cursor column) 3659 0C37 2E 64 mvi l,064H 3660 0C39 34 inr m ; fall through ... ;------------------------------------------------------------------------------ ; wrap the line_req cursor column if it is 80 ; hl=2n64 on entry WRAP_CUR_COL: 3667 0C3A 7E mov a,m ; fetch current line_req cursor column 3668 0C3B FE 50 cpi 80 3669 0C3D D8 rc ; return if column didn't wrap 3670 0C3E 36 00 mvi m,000H ; [2n64] (cursor column) = 0 3671 0C40 3E 0A mvi a,00AH ; issue linefeed 3672 0C42 EF rst 5 ; push it to the terminal output buffer 3673 0C43 3D dcr a ; clear Z flag 3674 0C44 C9 ret ;------------------------------------------------------------------------------ ; pair pulled from kb buffer, and < 0xE6 ; erase the line_req field, but if the 0x20 bit of the flag byte ; was not set, clear the edit mode state. KB_FF_LT_E6: 3681 0C45 1E 6B mvi e,06BH ; start of line_req buffer 3682 0C47 50 mov d,b 3683 0C48 CD 74 03 call CURS_TO_DE ; move cursor to start of buffer 3684 0C4B CD AE 0C call CLR_CUR_TO_EOB ; clear buffer to the right of the cursor 3685 0C4E 60 mov h,b 3686 0C4F 2E 63 mvi l,063H ; 2n63: line_req flag byte 3687 0C51 7E mov a,m 3688 0C52 E6 20 ani 020H ; linput statement active 3689 0C54 C2 73 0B jnz RX_POLL1 ; set the edit mode bit, but the next section is going to invert it ; so really this is setting it up to be cleared 3693 0C57 3E 04 mvi a,004H 3694 0C59 B6 ora m 3695 0C5A 77 mov m,a ; fall through ... ; flip the line_req edit mode bit; change cursor blink mode to correspond ; arrived here due to key pair, or fall-through KB_FF_EDIT: 3701 0C5B 3E FB mvi a,0FBH ; compression token 3702 0C5D EF rst 5 ; push it to the terminal output buffer 3703 0C5E 7E mov a,m 3704 0C5F EE 04 xri 004H ; flip the edit mode bit 3705 0C61 77 mov m,a 3706 0C62 E6 04 ani 004H ; isolate the edit mode bit 3707 0C64 C6 F8 adi 0F8H ; 0xF8 (no cursor blink) or 0xFC (cursor blink) 3708 0C66 C3 A4 0C jmp DOBS3 ; send A to crt buffer ;------------------------------------------------------------------------------ ; KB input was a control character of some kind (<0x10) KB_FF_CTRL: 3713 0C69 FE 0D cpi 00DH ; carriage return 3714 0C6B CA 43 0D jz L0D43 3715 0C6E FE 08 cpi 008H ; back space 3716 0C70 C2 7C 0B jnz RXP3 ; ignore it ; fall through ... ;------------------------------------------------------------------------------ ; process a backspace request DO_BS: ; de = [2n69/6A] (cursor location) 3723 0C73 60 mov h,b 3724 0C74 2E 69 mvi l,069H 3725 0C76 56 mov d,m 3726 0C77 23 inx h 3727 0C78 5E mov e,m ; branch if cursor location==2n6B (start of buffer) 3730 0C79 3E 6B mvi a,06BH 3731 0C7B 93 sub e 3732 0C7C C2 84 0C jnz dobs1 3733 0C7F B4 ora h 3734 0C80 BA cmp d 3735 0C81 CA 7C 0B jz RXP3 ; jump if at start of buffer dobs1: ; jump if [2n67/68] (last byte used) != de 3739 0C84 2E 67 mvi l,067H 3740 0C86 7E mov a,m 3741 0C87 BA cmp d 3742 0C88 C2 96 0C jnz dobs2 3743 0C8B 23 inx h 3744 0C8C 7E mov a,m 3745 0C8D BB cmp e 3746 0C8E C2 96 0C jnz dobs2 ; [2n67/68](last byte used) = (cursor location - 1) 3749 0C91 1B dcx d 3750 0C92 73 mov m,e 3751 0C93 2B dcx h 3752 0C94 72 mov m,d 3753 0C95 13 inx d dobs2: 3755 0C96 1B dcx d ; move cursor pointer left one 3756 0C97 EB xchg 3757 0C98 36 20 mvi m,' ' 3758 0C9A CD 73 03 call CURS_TO_HL 3759 0C9D 3E 20 mvi a,' ' 3760 0C9F CD 7A 00 call A_TO_CRT_BUF 3761 0CA2 3E 08 mvi a,008H ; backspace DOBS3: 3763 0CA4 EF rst 5 ; save A in the terminal output buffer 3764 0CA5 C3 73 0B jmp RX_POLL1 ;------------------------------------------------------------------------------ ; received s.f. 8 in EDIT mode (erase from cursor to end of buffer) EDIT_SF8: 3769 0CA8 CD AE 0C call CLR_CUR_TO_EOB 3770 0CAB C3 73 0B jmp RX_POLL1 ; clear the line_req buffer from the cursor to the end of buffer CLR_CUR_TO_EOB: ; de = [2n69/6A] (cursor location) 3775 0CAE 60 mov h,b 3776 0CAF 2E 69 mvi l,069H 3777 0CB1 56 mov d,m 3778 0CB2 23 inx h 3779 0CB3 5E mov e,m 3780 0CB4 D5 push d ; why? de isn't changed between here and the pop ; hl = [2n67/68] (last byte used), then update ; [2n67/68] (last byte used) = de = (cursor location) 3784 0CB5 2E 67 mvi l,067H 3785 0CB7 7E mov a,m 3786 0CB8 72 mov m,d 3787 0CB9 23 inx h 3788 0CBA 4E mov c,m 3789 0CBB 73 mov m,e 3790 0CBC 67 mov h,a 3791 0CBD 69 mov l,c 3793 0CBE D1 pop d ; == cursor location 3794 0CBF CD 2C 01 call HL_SUB_DE ; hl = distance from cursor to last byte used ; fall through ... ;------------------------------------------------------------------------------ ; hl = count of spaces (possibly underlined) to emit to the terminal ; the cursor will be moved back to the starting point afterwards EMIT_HL_SPACES: 3801 0CC2 E5 push h 3802 0CC3 EB xchg 3803 0CC4 60 mov h,b 3804 0CC5 2E 64 mvi l,064H ; 2n64: line_req cursor column 3805 0CC7 7E mov a,m 3806 0CC8 2F cma 3807 0CC9 C6 51 adi 81 3808 0CCB 4F mov c,a ; c=80-cursor_column 3809 0CCC EB xchg ; hl=distance from cursor to last byte used ehls1: ; draw the minimum of current cursor location to column 80, or to residual hl count 3812 0CCD AF xra a 3813 0CCE 57 mov d,a 3814 0CCF 59 mov e,c ; de=distance from cursor to column 80 3815 0CD0 BC cmp h 3816 0CD1 C2 DA 0C jnz ehls2 3817 0CD4 B5 ora l 3818 0CD5 B9 cmp c ; (cursor to last used byte) - (cursor to col 80) 3819 0CD6 D2 DA 0C jnc ehls2 ; jump if (cursor to last byte used) >= (cursor to col 80) 3820 0CD9 5D mov e,l ; de is now the smaller of the two ehls2: 3822 0CDA 79 mov a,c ; distance of cursor to col 80 3823 0CDB 93 sub e 3824 0CDC 4F mov c,a 3825 0CDD CD 2C 01 call HL_SUB_DE ; hl=(current clear point to last byte used) 3826 0CE0 3E 02 mvi a,2 3827 0CE2 BB cmp e 3828 0CE3 D2 FF 0C jnc ehls4 ; if run was < 3 chars, don't use compression 3829 0CE6 3E FB mvi a,0FBH ; compression token 3830 0CE8 EF rst 5 ; push compression token to the terminal output buffer 3831 0CE9 3A 59 26 lda ULINE_FLAG 3832 0CEC A7 ana a 3833 0CED C2 F7 0C jnz ehls3 ; if underline mode is in effect, use three-byte compression 3834 0CF0 3E 60 mvi a,060H ; we can use two byte compression: 3835 0CF2 83 add e ; FB nn, where nn >= 0x60 means (nn-0x60) spaces 3836 0CF3 EF rst 5 ; push A to the terminal output buffer 3837 0CF4 C3 0E 0D jmp ehls6 ; use three byte compression for underlined spaces: FB nn ehls3: 3841 0CF7 7B mov a,e 3842 0CF8 EF rst 5 ; push the repetition count to the terminal output buffer 3843 0CF9 3E A0 mvi a,0A0H ; underlined space 3844 0CFB EF rst 5 ; push A to the terminal output buffer 3845 0CFC C3 0E 0D jmp ehls6 ; emit E spaces (possibly underlined) without compression ehls4: 3849 0CFF AF xra a 3850 0D00 BB cmp e 3851 0D01 CA 0E 0D jz ehls6 ; nothing to erase ehls5: 3853 0D04 3A 59 26 lda ULINE_FLAG 3854 0D07 F6 20 ori ' ' ; possibly underline the space 3855 0D09 EF rst 5 ; push it to the terminal output buffer 3856 0D0A 1D dcr e 3857 0D0B C2 04 0D jnz ehls5 ; fall through ... ; all characters to the right of the cursor have been cleared on the terminal, ; but the buffer might extend beyond the first row. clear chars on subsequent ; rows if necessary. ehls6: 3864 0D0E AF xra a 3865 0D0F B9 cmp c 3866 0D10 C2 18 0D jnz ehls7 3867 0D13 3E 0A mvi a,00AH ; linefeed 3868 0D15 EF rst 5 ; push it to the terminal output buffer 3869 0D16 0E 50 mvi c,80 ; if we have do do another line, we will clear 80 columns ehls7: 3871 0D18 7D mov a,l 3872 0D19 B4 ora h 3873 0D1A C2 CD 0C jnz ehls1 ; still work left 3875 0D1D 3E 50 mvi a,80 3876 0D1F 91 sub c 3877 0D20 60 mov h,b 3878 0D21 2E 64 mvi l,064H ; 2n64: line_req cursor column 3879 0D23 77 mov m,a 3881 0D24 D1 pop d ; distance from original cursor to last byte used 3882 0D25 7A mov a,d ; \ 3883 0D26 2F cma ; \ 3884 0D27 57 mov d,a ; \ 3885 0D28 7B mov a,e ; > two's complement of de 3886 0D29 2F cma ; / 3887 0D2A 5F mov e,a ; / 3888 0D2B 13 inx d ; / 3890 0D2C C3 80 03 jmp CTDE1 ; move the cursor back to where the clear started ;------------------------------------------------------------------------------ ; received s.f. 15 (recall) in EDIT mode EDIT_SF15: ; de = [2n69/6A] (cursor location) 3896 0D2F 2E 69 mvi l,069H 3897 0D31 56 mov d,m 3898 0D32 23 inx h 3899 0D33 5E mov e,m ; [2n67/68] (last used byte) =?= [2n68/6A] (cursor location) 3902 0D34 2E 67 mvi l,067H 3903 0D36 7E mov a,m 3904 0D37 AA xra d 3905 0D38 C2 73 0B jnz RX_POLL1 3906 0D3B 23 inx h 3907 0D3C 7E mov a,m 3908 0D3D AB xra e 3909 0D3E C2 73 0B jnz RX_POLL1 ; jump to RX_POLL1 if (last byte used) != (cursor location) ; perhaps don't attempt recall if pointers don't match ; for instance, recall when editing lines has no effect unless the cursor is before the first non-space character of the buffer ; ... but even that isn't right, in that we can edit the line number itself, so the buffer really starts before the first non-space char after the line number. 3913 0D41 3E FF mvi a,0FFH ; fall through ... ; but it is weird because d=2n, and isn't a prefix byte ;------------------------------------------------------------------------------ ; do something with the rx pair; d=prefix byte and a=keycode ; it is called when a key is processed that the line_req processing can't handle itself: ; KB_FF_CTRL jumps here with <0D> ; RX_POLL jumps here with <0D> ; INJECT_CR jumps here with <0D> ; KB_TYPE_FF jumps here with (in response to =STMT# key) ; KB_TYPE_ENDI jumps here with , where 0x60 <= nn <= 0x7F (s.f. key) ; KB_TYPE_ENDI jumps here with , where 0x20 <= nn <= 0x3F (s.f. key) ; KB_TYPE_ENDI jumps here with , where 0x00 <= nn <= 0x1F (s.f. key) and line_req edit_mode bit is set L0D43: ; QUERY_LINE_REQUEST uses this state to know ... ; ... when to return FFh (see that command's description) 3930 0D43 60 mov h,b 3931 0D44 2E 61 mvi l,061H 3932 0D46 72 mov m,d ; [2n61] = d = key prefix byte 3933 0D47 23 inx h 3934 0D48 77 mov m,a ; [2n62] = a = keypress value ; de = [2n69/6A] (cursor location) 3937 0D49 2E 69 mvi l,069H 3938 0D4B 56 mov d,m 3939 0D4C 23 inx h 3940 0D4D 5E mov e,m ; [2n5D/5E] (redraw start) = [2n69/6A] (cursor location) 3943 0D4E 2E 5D mvi l,05DH 3944 0D50 72 mov m,d 3945 0D51 23 inx h 3946 0D52 73 mov m,e ; [2n5F/60] (redraw end) = [2n69/6A] (cursor location) 3949 0D53 23 inx h 3950 0D54 72 mov m,d 3951 0D55 23 inx h 3952 0D56 73 mov m,e 3954 0D57 CD 92 00 call SET_KB_RDY_STATUS 3955 0D5A 21 60 2A lxi h,LREQ_ACCEPT ; map, 1=line_req active 3956 0D5D 79 mov a,c 3957 0D5E 2F cma 3958 0D5F A6 ana m ; clear bit for the current terminal 3959 0D60 77 mov m,a 3960 0D61 C3 7B 0B jmp rxp2 ;------------------------------------------------------------------------------ ; key sequence received. ; d=prefix byte, e=key code ; it is described as a special function key received, but the code below is far ; more complicated than that. KB_TYPE_ENDI: 3968 0D64 FE 60 cpi 060H 3969 0D66 D2 43 0D jnc L0D43 ; jump if A >= 0x60 3970 0D69 FE 40 cpi 040H 3971 0D6B D2 7A 0D jnc kbte1 ; jump if A >= 0x40 (and < 0x60) 3972 0D6E FE 20 cpi 020H 3973 0D70 D2 43 0D jnc L0D43 ; jump if A >= 0x20 (and < 0x40) 3974 0D73 7E mov a,m ; a = [2n63]: line_req flag byte 3975 0D74 E6 04 ani 004H ; test EDIT mode flag 3976 0D76 7B mov a,e ; save key code 3977 0D77 CA 43 0D jz L0D43 ; jump if not edit mode ; either where 0x40 <= cc < 0x60 ; or where cc < 0x20 in edit mode kbte1: ; 0x01-0x1F or 0x20-0x3F 3982 0D7A E6 0F ani 00FH 3983 0D7C D6 04 sui 004H 3984 0D7E DA 7C 0B jc RXP3 ; jump if s.f. 0-3; ignore it 3985 0D81 D6 07 sui 007H 3986 0D83 DA B0 0D jc EDIT_SF4_10 ; jump if s.f. 4-10 3987 0D86 CA EA 0D jz EDIT_SF11 ; jump if s.f. 11 (move cursor right 5) 3988 0D89 3D dcr a 3989 0D8A CA F0 0D jz EDIT_SF12 ; jump if s.f. 12 (move cursor right 1) 3990 0D8D 3D dcr a 3991 0D8E 11 FF FF lxi d,0FFFFH ; -1 3992 0D91 CA 9B 0D jz CUR_LEFT_DE ; jump if s.f. 13 (move cursor left 1) 3993 0D94 3D dcr a 3994 0D95 C2 2F 0D jnz EDIT_SF15 ; jump if s.f. 15 (recall) ; fall through ... ; received s.f. 14 (move cursor left 5) in EDIT mode ; EDIT_SF14: 3999 0D98 11 FB FF lxi d,0FFFBH ; -5 ; fall through ... ; adjust the cursor DE places (de is signed) CUR_LEFT_DE: ; hl=[2n69/6A] 4005 0D9B 60 mov h,b 4006 0D9C 2E 69 mvi l,069H 4007 0D9E 7E mov a,m 4008 0D9F 23 inx h 4009 0DA0 6E mov l,m 4010 0DA1 67 mov h,a 4012 0DA2 19 dad d ; move left or 1 or 5 places 4013 0DA3 EB xchg 4014 0DA4 60 mov h,b 4015 0DA5 2E 6B mvi l,06BH ; 2n6B: start of buffer 4016 0DA7 CD 70 00 call MAX_HL_MIN_DE ; don't let cursor move to the left of the beginning 4017 0DAA CD 73 03 call CURS_TO_HL 4018 0DAD C3 73 0B jmp RX_POLL1 ; received s.f. key 4 to 10 in EDIT mode EDIT_SF4_10: 4022 0DB0 3C inr a 4023 0DB1 CA 33 0E jz EDIT_SF10 ; insert 4024 0DB4 3C inr a 4025 0DB5 CA 7A 0E jz EDIT_SF9 ; delete 4026 0DB8 3C inr a 4027 0DB9 CA A8 0C jz EDIT_SF8 ; erase 4028 0DBC 3C inr a 4029 0DBD CA E4 0D jz EDIT_SF7 ; start of line 4030 0DC0 3C inr a 4031 0DC1 CA 1D 0E jz EDIT_SF6 ; up a line 4032 0DC4 3C inr a 4033 0DC5 CA 07 0E jz EDIT_SF5 ; down a line ; fall through ... ; received s.f. key 4 (end) in EDIT mode ; start at the end of buffer, then scan back until either the start of buffer ; is found or a non-space character is found ; EDIT_SF4: ; de = [2n67/68] (last byte used) 4041 0DC8 2E 67 mvi l,067H 4042 0DCA 56 mov d,m 4043 0DCB 23 inx h 4044 0DCC 5E mov e,m 4046 0DCD 0E 20 mvi c,' ' sf4a: ; done if (last byte used) is at the start of buffer 4049 0DCF 3E 6B mvi a,06BH ; 2n6B is start of line req buffer 4050 0DD1 93 sub e 4051 0DD2 C2 DA 0D jnz sf4b 4052 0DD5 B2 ora d 4053 0DD6 B8 cmp b 4054 0DD7 CA 01 0E jz CUR_TO_DE_DONE ; jump if at start of buffer sf4b: 4056 0DDA 1B dcx d ; back up one char 4057 0DDB 1A ldax d ; fetch it from line_req buffer 4058 0DDC B9 cmp c 4059 0DDD CA CF 0D jz sf4a ; jump if space 4060 0DE0 13 inx d ; go back one 4061 0DE1 C3 01 0E jmp CUR_TO_DE_DONE ; received s.f. 7 (begin) in EDIT mode EDIT_SF7: 4065 0DE4 CD 70 03 call CURS_TO_SOL 4066 0DE7 C3 73 0B jmp RX_POLL1 ; received s.f. 11 (right cursor 5) in EDIT mode EDIT_SF11: 4070 0DEA 11 05 00 lxi d,5 4071 0DED C3 F3 0D jmp CUR_RIGHT_DE ; received s.f. 12 (right cursor 1) in EDIT mode EDIT_SF12: 4075 0DF0 11 01 00 lxi d,1 ; fall through ... ; move the cursor right DE places CUR_RIGHT_DE: ; hl = [2n69/6A] (cursor location) 4081 0DF3 60 mov h,b 4082 0DF4 2E 69 mvi l,069H 4083 0DF6 7E mov a,m 4084 0DF7 23 inx h 4085 0DF8 6E mov l,m 4086 0DF9 67 mov h,a 4088 0DFA 19 dad d ; adjust right 1 or 5 4089 0DFB EB xchg 4090 0DFC 2E 67 mvi l,067H 4091 0DFE CD 6B 00 call HLPTR_MAXMIN_DE ; hl = [2n67/68](last byte used); hl=max, de=min ; fall through ... ; move cursor to the line_req buffer offset pointed at by de, ; then resume timeslicing CUR_TO_DE_DONE: 4097 0E01 CD 74 03 call CURS_TO_DE 4098 0E04 C3 73 0B jmp RX_POLL1 ;------------------------------------------------------------------------------ ; received s.f. 5 (down) in EDIT mode EDIT_SF5: ; hl = [2n69/6A] (cursor location) 4104 0E07 60 mov h,b 4105 0E08 2E 69 mvi l,069H 4106 0E0A 56 mov d,m 4107 0E0B 23 inx h 4108 0E0C 5E mov e,m 4110 0E0D 21 50 00 lxi h,80 4111 0E10 19 dad d ; hl += 80 4113 0E11 EB xchg 4114 0E12 2E 67 mvi l,067H ; 2n67 4115 0E14 CD 6B 00 call HLPTR_MAXMIN_DE ; hl = [2n67/68](last byte used); hl=max, de=min 4116 0E17 D4 74 03 cnc CURS_TO_DE ; call if we swapped above (don't move if we tried to go past end of line) 4117 0E1A C3 73 0B jmp RX_POLL1 ;------------------------------------------------------------------------------ ; received s.f. 6 (up) in EDIT mode EDIT_SF6: ; de = [2n69/6A] (cursor location) 4123 0E1D 60 mov h,b 4124 0E1E 2E 69 mvi l,069H 4125 0E20 56 mov d,m 4126 0E21 23 inx h 4127 0E22 5E mov e,m 4129 0E23 21 B0 FF lxi h,0FFB0H 4130 0E26 19 dad d ; hl = (cursor location) - 80 4132 0E27 50 mov d,b 4133 0E28 1E 6B mvi e,06BH 4134 0E2A CD 70 00 call MAX_HL_MIN_DE ; de = min(start of buff, cur_loc-80) 4135 0E2D D4 73 03 cnc CURS_TO_HL ; if we didn't undershoot, move the cursor 4136 0E30 C3 73 0B jmp RX_POLL1 ;------------------------------------------------------------------------------ ; received s.f. 10 (insert) in EDIT mode ; this shifts the contents of the line_req buffer, but other code does the ; redraw to the terminal (also true of most other commands) EDIT_SF10: 4143 0E33 60 mov h,b 4144 0E34 3E 61 mvi a,061H ; continuation byte 4145 0E36 CD B9 06 call SAV_CONT ; de = [2n67/68] (last byte used) 4148 0E39 60 mov h,b 4149 0E3A 2E 67 mvi l,067H 4150 0E3C 56 mov d,m 4151 0E3D 23 inx h 4152 0E3E 5E mov e,m ; if de (last byte used) != [2n65/66] (end of buffer), increment (last byte used) ; that is, inserting will increment the last byte used pointer ; funny that the jump instructions jump to the rewrite of [2n67/68] instead of ; just skipping that part of it. 4158 0E3F 2E 66 mvi l,066H 4159 0E41 7E mov a,m 4160 0E42 BB cmp e 4161 0E43 C2 4C 0E jnz sf10a 4162 0E46 2B dcx h 4163 0E47 7E mov a,m 4164 0E48 BA cmp d 4165 0E49 CA 4D 0E jz sf10b sf10a: 4167 0E4C 13 inx d ; increment last byte used pointer sf10b: ; [2n67/68] (line_req last byte used) = de 4170 0E4D 2E 67 mvi l,067H 4171 0E4F 72 mov m,d 4172 0E50 23 inx h 4173 0E51 73 mov m,e ; [2n5F/60] = de = copy of last byte used 4176 0E52 2E 5F mvi l,05FH 4177 0E54 72 mov m,d 4178 0E55 23 inx h 4179 0E56 73 mov m,e 4180 0E57 D5 push d ; de = [2n69/6A] (cursor location) 4183 0E58 2E 69 mvi l,069H 4184 0E5A 56 mov d,m 4185 0E5B 23 inx h 4186 0E5C 5E mov e,m ; [2n5D/5E] = de = [2n69/6A] (cursor location) 4189 0E5D 2E 5D mvi l,05DH 4190 0E5F 72 mov m,d 4191 0E60 23 inx h 4192 0E61 73 mov m,e 4194 0E62 EB xchg ; hl = cursor location 4195 0E63 D1 pop d ; de = last byte used 4196 0E64 0E 20 mvi c,' ' ; we want to erase at the insertion point 4197 0E66 C3 6D 0E jmp sf10d sf10c: 4200 0E69 7E mov a,m ; read byte we are about to overwrite 4201 0E6A 71 mov m,c ; write the byte that was to the left 4202 0E6B 4F mov c,a ; prepare for next loop 4203 0E6C 23 inx h sf10d: ; loop until de=hl 4206 0E6D 7D mov a,l 4207 0E6E BB cmp e 4208 0E6F C2 69 0E jnz sf10c 4209 0E72 7C mov a,h 4210 0E73 BA cmp d 4211 0E74 C2 69 0E jnz sf10c 4212 0E77 C3 73 0B jmp RX_POLL1 ;------------------------------------------------------------------------------ ; received s.f. 9 (delete) in EDIT mode EDIT_SF9: ; de = [2n69/6A] (cursor location) 4218 0E7A 60 mov h,b 4219 0E7B 2E 69 mvi l,069H 4220 0E7D 56 mov d,m 4221 0E7E 23 inx h 4222 0E7F 5E mov e,m ; [2n5D/5E] = de = cursor location 4225 0E80 2E 5D mvi l,05DH 4226 0E82 72 mov m,d 4227 0E83 23 inx h 4228 0E84 73 mov m,e ; [2n67/68] (last byte used) =?= (cursor location) 4231 0E85 2E 68 mvi l,068H 4232 0E87 7E mov a,m 4233 0E88 BB cmp e 4234 0E89 2B dcx h 4235 0E8A C2 92 0E jnz sf9a 4236 0E8D 7E mov a,m 4237 0E8E BA cmp d 4238 0E8F CA 73 0B jz RX_POLL1 ; jump if (cursor location) = (last byte used) sf9a: 4240 0E92 D5 push d ; cursor location ; de = [2n67/68] (last byte used) 4243 0E93 56 mov d,m 4244 0E94 23 inx h 4245 0E95 5E mov e,m ; [2n5F/60] = de = last byte used 4248 0E96 2E 5F mvi l,05FH 4249 0E98 72 mov m,d 4250 0E99 23 inx h 4251 0E9A 73 mov m,e ; decrement [2n67/68] (last byte used) 4254 0E9B 1B dcx d 4255 0E9C 2E 67 mvi l,067H 4256 0E9E 72 mov m,d 4257 0E9F 23 inx h 4258 0EA0 73 mov m,e 4260 0EA1 13 inx d ; restore to old last byte used location to en 4262 0EA2 3E 61 mvi a,061H ; continuation byte (C2 = DO_REDRAW) 4263 0EA4 CD B9 06 call SAV_CONT 4264 0EA7 EB xchg ; de=..., hl=old last byte used pointer 4265 0EA8 D1 pop d ; de=cursor location, hl=old last byte used 4266 0EA9 0E 20 mvi c,' ' ; fill last used byte with space 4267 0EAB C3 B2 0E jmp sf9c ; move the characters one to the left from last used to cursor location sf9b: 4271 0EAE 2B dcx h 4272 0EAF 7E mov a,m 4273 0EB0 71 mov m,c 4274 0EB1 4F mov c,a sf9c: ; loop until hl=de 4277 0EB2 7D mov a,l 4278 0EB3 BB cmp e 4279 0EB4 C2 AE 0E jnz sf9b 4280 0EB7 7C mov a,h 4281 0EB8 BA cmp d 4282 0EB9 C2 AE 0E jnz sf9b 4283 0EBC C3 73 0B jmp RX_POLL1 ;------------------------------------------------------------------------------ ; continuation#1 from DO_REDRAW via alrd6 SET_LINEREQ_STATUS: 4288 0EBF 60 mov h,b 4289 0EC0 CD 92 00 call SET_KB_RDY_STATUS ; indicate line_req complete for status reponse 4290 0EC3 CD 01 03 call SEND_CR_LF ; fall through ... ; continuation#3 of EDIT_SF9/10 + continuation#5 of END_OF_LINE_REQ ; if the keyboard buffer is empty, set flags indicating it CHECK_KB_MT_FLAG: ; [2n5C] no more continuations 4297 0EC6 60 mov h,b 4298 0EC7 2E 5C mvi l,05CH 4299 0EC9 36 00 mvi m,000H 4301 0ECB 2E 18 mvi l,018H ; a = [2n18] (count of entries in rx kb buffer) 4302 0ECD 7E mov a,m 4303 0ECE A7 ana a 4304 0ECF CC 49 0B cz MARK_KB_MT_EI ; mark the rx buffer empty if it is and reenable interrupts 4305 0ED2 C3 73 0B jmp RX_POLL1 ;------------------------------------------------------------------------------ ; continuation #1 of END_OF_LINE_REQ and TERMINATE_LINE_REQ ; (via INDTAB branch table and the 2n5C byte) ; enable the cursor and set it to blink (if edit mode) or nonblink (if not edit) CURSOR_ON: ; [2n5C]++ continue at BLANK_SPAN next time through event loop 4313 0ED5 60 mov h,b 4314 0ED6 2E 5C mvi l,05CH 4315 0ED8 34 inr m 4317 0ED9 3A 5A 2A lda WANGTERM 4318 0EDC A1 ana c 4319 0EDD CA 73 0B jz RX_POLL1 ; done if not a Wang terminal ; enable cursor code (<05>) 4322 0EE0 3E 05 mvi a,005H 4323 0EE2 EF rst 5 ; make cursor either not blink () or blink () 4326 0EE3 3E FB mvi a,0FBH ; compression token 4327 0EE5 EF rst 5 4328 0EE6 2E 63 mvi l,063H 4329 0EE8 7E mov a,m ; [2n63] = line_req flag byte 4330 0EE9 E6 04 ani 004H ; test edit mode flag 4331 0EEB C6 F8 adi 0F8H ; 0xF8 (no blinking) or 0xFC (blinking) 4332 0EED EF rst 5 4333 0EEE C3 73 0B jmp RX_POLL1 ;------------------------------------------------------------------------------ ; continuation #2 of END_OF_LINE_REQ and TERMINATE_LINE_REQ ; (via INDTAB branch table and the 2n5C byte) ; move cursor to start of redraw span and emit spaces to the end of span BLANK_SPAN: ; [2n5C]++ continue to DO_REDRAW at next event loop 4341 0EF1 60 mov h,b 4342 0EF2 2E 5C mvi l,05CH 4343 0EF4 34 inr m ; de = [2n5D/5E] (redraw start) 4346 0EF5 23 inx h 4347 0EF6 56 mov d,m 4348 0EF7 23 inx h 4349 0EF8 5E mov e,m ; hl = [2n69/6A] (cursor location) 4352 0EF9 2E 69 mvi l,069H 4353 0EFB 7E mov a,m 4354 0EFC 23 inx h 4355 0EFD 6E mov l,m 4356 0EFE 67 mov h,a 4358 0EFF CD 2C 01 call HL_SUB_DE ; hl = (cursor)-(redraw start) 4359 0F02 E5 push h 4360 0F03 CD 74 03 call CURS_TO_DE ; move to redraw start point 4361 0F06 E1 pop h 4362 0F07 CD C2 0C call EMIT_HL_SPACES 4363 0F0A C3 73 0B jmp RX_POLL1 ;------------------------------------------------------------------------------ ; push the characters from (cursor location) to (end of redraw span). ; continuation of EDIT_SF9/SF10 ; continuation #3 of END_OF_LINE_REQ ; continuation after alrd6 DO_REDRAW: 4371 0F0D CD 13 0F call ALRD_REDRAW 4372 0F10 C3 73 0B jmp RX_POLL1 ; ACCEPT_LINE_REQ_DATA (alrd6) leads here, and DO_REDRAW continuation uses this. ; scan the line_req buffer from the cursor location to the end of the redraw ; span, [2nF5/60]. copy each character to the tx output buffer, taking care ; to map control characters to '.' and to escape literal 0xFB characters. ; ; because the crt buffer is smaller than the size of the redraw span, this ; routine will redraw from the current cursor column up to column 80, which means ; a maximum of 80 characters, which might mean 160 bytes in the worst case ; (the worst case would be a string of nothing but , and each byte would need ; to be escaped individually). If there is work left, the continuation code will ; remain set to this routine, and the next piece will get drawn. ALRD_REDRAW: ; depth=3 (call here, push h, call DE_SUB_HL) 4386 0F13 60 mov h,b 4387 0F14 2E 64 mvi l,064H ; 2n64: line_req cursor column 4388 0F16 3E 50 mvi a,80 4389 0F18 96 sub m 4390 0F19 4F mov c,a ; c=80-cursor_col ; de = [2n5F/60] (end of redraw span) 4393 0F1A 2E 5F mvi l,05FH 4394 0F1C 56 mov d,m 4395 0F1D 23 inx h 4396 0F1E 5E mov e,m ; hl = [2n69/6A] (cursor location) 4399 0F1F 2E 69 mvi l,069H 4400 0F21 7E mov a,m 4401 0F22 23 inx h 4402 0F23 6E mov l,m 4403 0F24 67 mov h,a 4404 0F25 E5 push h 4406 0F26 CD 2B 01 call DE_SUB_HL ; hl = de-hl = (end of redraw span) - (cursor location) 4407 0F29 C2 3D 0F jnz alrdd1 ; jump if distance >= 256; lots left 4408 0F2C 79 mov a,c 4409 0F2D BD cmp l ; (cols left to 80) - (buffer left to redraw) 4410 0F2E DA 3D 0F jc alrdd1 ; jump if the latter is larger 4411 0F31 4D mov c,l ; c=smaller of the two, which means we are done ; we are about to complete the transfer to (end of redraw span) ; [2n5C] bump the continuation byte ; DO_REDRAW -> SET_LINEREQ_STATUS (if via alrd6) ; DO_REDRAW -> CURSOR_TO_START (all other users) 4417 0F32 60 mov h,b 4418 0F33 2E 5C mvi l,05CH 4419 0F35 34 inr m 4421 0F36 AF xra a 4422 0F37 B9 cmp c 4423 0F38 C2 3D 0F jnz alrdd1 4424 0F3B E1 pop h ; it is already done; over and out 4425 0F3C C9 ret alrdd1: ; de = output buffer put pointer 4429 0F3D 21 FE 03 lxi h,03FEH 4430 0F40 7C mov a,h 4431 0F41 80 add b 4432 0F42 67 mov h,a ; hl=23FE+n*0x400 4433 0F43 54 mov d,h 4434 0F44 5E mov e,m ; a = this term one-hot code 4437 0F45 60 mov h,b 4438 0F46 2E 00 mvi l,000H ; 2n00 4439 0F48 7E mov a,m ; [CRT_BUF_ACTIVE] |= 1hot_term 4442 0F49 21 5D 2A lxi h,CRT_BUF_ACTIVE 4443 0F4C F3 di 4444 0F4D B6 ora m 4445 0F4E 77 mov m,a ; [CRT_CAN_TX] = ~[CRT_STOPPED] & [CRT_BUF_ACTIVE] 4448 0F4F 23 inx h ; CRT_STOPPED 4449 0F50 7E mov a,m 4450 0F51 2F cma 4451 0F52 2B dcx h ; CRT_BUF_ACTIVE 4452 0F53 A6 ana m 4453 0F54 2B dcx h ; CRT_CAN_TX 4454 0F55 77 mov m,a 4456 0F56 FB ei ; regC is the number of bytes we are sending to the crt buffer ; [2n64] (cursor column) += number of bytes we are sending to crt buffer ; we know there are enough bytes for it in the crt buffer because ; RX_POLL wouldn't have called the continuation routine unless there ; was lots of room (the crt put ptr must be less than 35). 4463 0F57 60 mov h,b 4464 0F58 2E 64 mvi l,064H 4465 0F5A 7E mov a,m 4466 0F5B 81 add c 4467 0F5C 77 mov m,a 4469 0F5D E1 pop h ; hl = cursor location alrdd2: 4471 0F5E 3A 59 26 lda ULINE_FLAG 4472 0F61 B6 ora m ; read next byte from line_req buffer 4473 0F62 23 inx h 4474 0F63 FE 10 cpi 010H ; is it a control character? 4475 0F65 D2 6A 0F jnc alrdd3 4476 0F68 3E 2E mvi a,'.' alrdd3: 4478 0F6A 12 stax d ; save char in output buffer 4479 0F6B 13 inx d ; bump put pointer 4480 0F6C FE FB cpi 0FBH ; are we are sending literal FB? 4481 0F6E C2 75 0F jnz alrdd4 ; jump if not 4482 0F71 3E D0 mvi a,0D0H ; we need to escape it 4483 0F73 12 stax d 4484 0F74 13 inx d alrdd4: 4486 0F75 0D dcr c 4487 0F76 C2 5E 0F jnz alrdd2 4489 0F79 E5 push h ; save line_req get pointer 4490 0F7A 62 mov h,d 4491 0F7B 2E FE mvi l,0FEH 4492 0F7D 73 mov m,e ; save output buffer put pointer 4493 0F7E D1 pop d ; [2n69/6A] (cursor location) = de 4496 0F7F 60 mov h,b 4497 0F80 2E 69 mvi l,069H 4498 0F82 72 mov m,d 4499 0F83 23 inx h 4500 0F84 73 mov m,e 4502 0F85 2E 64 mvi l,064H ; 2n64: line_req cursor column 4503 0F87 C3 3A 0C jmp WRAP_CUR_COL ;------------------------------------------------------------------------------ ; continuation#2 of EDIT_SF9/10 or continuation#4 of END_OF_LINE_REQ ; move the cursor to the start of the redraw region (if not REFILL_LINE_REQ) CURSOR_TO_START: ; [2n5c] bump continuation 4510 0F8A 60 mov h,b 4511 0F8B 2E 5C mvi l,05CH 4512 0F8D 34 inr m ; de = [2n5D/5E] (redraw start) 4515 0F8E 23 inx h 4516 0F8F 56 mov d,m 4517 0F90 23 inx h 4518 0F91 5E mov e,m ; a = [2n63] (line_req flag byte) 4521 0F92 2E 63 mvi l,063H 4522 0F94 7E mov a,m 4524 0F95 E6 10 ani 010H ; refill_line_req flag 4525 0F97 F5 push psw 4526 0F98 AE xra m ; invert refill_line_req flag 4527 0F99 77 mov m,a 4528 0F9A F1 pop psw 4529 0F9B CC 74 03 cz CURS_TO_DE ; refill (unlike prefill) doesn't move cursor to the start of buffer 4530 0F9E C3 73 0B jmp RX_POLL1 ; continuation#5 of TERMINATE_LINE_REQ ; pretend the key has been seen INJECT_CR: 4535 0FA1 3E 0D mvi a,00DH ; carriage return 4536 0FA3 C3 43 0D jmp L0D43 ; clear the keyboard buffer for this term and mark the kb status as empty RESET_KB_BUF: 4540 0FA6 F3 di 4541 0FA7 60 mov h,b 4542 0FA8 CD FC 06 call INIT_KB_BUF 4543 0FAB 2E 00 mvi l,000H 4544 0FAD 7E mov a,m ; [2n00] = one-hot encoding of current term 4545 0FAE 2F cma 4546 0FAF 21 5F 2A lxi h,KB_BUF_READY 4547 0FB2 A6 ana m 4548 0FB3 77 mov m,a ; clear kb buffer ready bit for the current terminal 4549 0FB4 C9 ret ; we have a byte which is >= 0x80. ; "EndI/Atom - where = HEX(FD) ; MXD response: Char is treated as a special function key if less than 0x80. ; The MXD is responsible for deatomizing the atom code codes in the case of ; INPUT or LINPUT or supplying the proper atom code to KEYIN. The atom code ; sent from the terminal to the MXD need not be the same as the atom code ; sent from the MXD to the CPU." ; if it matches an entry in the atom table, expand it into the string literal ; into the keyboard buffer KB_TYPE_FD: 4561 0FB5 7B mov a,e 4562 0FB6 B7 ora a 4563 0FB7 F2 64 0D jp KB_TYPE_ENDI ; jump if < 0x80 4564 0FBA 21 D9 0F lxi h,CLEAR_TOK ; start of atom/string table kbtfd1: 4566 0FBD BE cmp m ; compare to atom byte 4567 0FBE 23 inx h ; hl now points to start of corresponding string 4568 0FBF CA D1 0F jz kbtfd3 ; jump if the byte matched the atom in the table 4569 0FC2 D2 BD 0F jnc kbtfd1 ; keep scanning table 4570 0FC5 C3 52 0B jmp BAD_BYTE ; must have gotten an atom byte > than byte in A kbtfd2: 4573 0FC8 E5 push h 4574 0FC9 CD 01 0C call A_TO_LINE_REQ ; stuff A into line request buffer 4575 0FCC CC 26 00 cz BEEP ; signal if there is no room left in buffer 4576 0FCF E1 pop h 4577 0FD0 23 inx h ; point to next byte in the atom/string table ; found a table entry matching the atom byte kbtfd3: 4580 0FD1 7E mov a,m 4581 0FD2 A7 ana a 4582 0FD3 F2 C8 0F jp kbtfd2 ; string bytes are all < 0x80 4583 0FD6 C3 73 0B jmp RX_POLL1 ; must have hit the next atom byte or sentinel ; atom/string table, to allow expanding keyboard atoms to simple text ; note: entries are stored in ascending order ; note: msb is used to distinguish atom byte vs following expansion CLEAR_TOK: 4589 0FD9 db 081H 81 4590 0FDA db "CLEAR" 43 4C 45 41 52 4592 0FDF db 082H 82 4593 0FE0 db "RUN" 52 55 4E 4595 0FE3 db 084H 84 4596 0FE4 db "CONTINUE" 43 4F 4E 54 49 4E 55 45 4598 0FEC db 0A0H A0 4599 0FED db "PRINT " 50 52 49 4E 54 20 4601 0FF3 db 0A1H A1 4602 0FF4 db "LOAD " 4C 4F 41 44 20 ; end of table sentinel 4605 0FF9 db 0FFH FF 4606 0FFA db 0FFH FF 4608 0FFB 00 nop 4609 0FFC 00 nop 4610 0FFD 00 nop 4611 0FFE 00 nop 4612 0FFF db 042H ; this is forced to produce a 00 checksum 42 ; vim: ts=8:noet:sw=8 ******************************************************************************* Symbols table ******************************************************************************* Names Types Values ----- ----- ------ START_OF_RAM EQU 02000h MVP_MODE EQU 02256h SHADOW_RBI EQU 02257h CUR_BLOCK EQU 02258h CUR_BLOCK_PAGE EQU 02259h CUR_TERM EQU 0225Ah CUR_UART EQU 0225Ch HAS_UART_TXD EQU 0225Eh PENDING_TERMS EQU 02656h LINE_REQ_BLOCK EQU 02657h LINE_REQ_PAGE EQU 02658h ULINE_FLAG EQU 02659h BUF_ACTIVE_IND EQU 0265Ah CUR_CAN_CMPRS EQU 0265Ch TX_SERVICE EQU 0265Dh CUR_INIT EQU 0265Fh CUR_TX_BUF EQU 0265Fh TX_RECEIVER EQU 02661h PRT_PRIO EQU 02662h SENT_CR EQU 02663h PRT_STAT_PEND EQU 02A56h PRT_CAN_TX EQU 02A57h PRT_BUF_ACTIVE EQU 02A58h PRT_STOPPED EQU 02A59h WANGTERM EQU 02A5Ah CRT_STAT_PEND EQU 02A5Bh CRT_CAN_TX EQU 02A5Ch CRT_BUF_ACTIVE EQU 02A5Dh CRT_STOPPED EQU 02A5Eh KB_BUF_READY EQU 02A5Fh LREQ_ACCEPT EQU 02A60h LREQ_ACTIVE EQU 02A61h RESET_MAP EQU 02A62h HALT_MAP EQU 02A63h TOP_OF_STACK EQU 02E66h INIT_TYPE EQU 02FFDh IN_UART_TXRDY EQU 00000h IN_2200_STATUS EQU 00001h IN_OBUS_N EQU 00002h IN_OBSCBS_ADDR EQU 00003h IN_UART_RXRDY EQU 00004h IN_UART_DATA EQU 00006h IN_UART_STATUS EQU 0000Eh OUT_CLR_PRIME EQU 00000h OUT_IB_N EQU 00001h OUT_IB9_N EQU 00011h OUT_PRIME EQU 00002h OUT_HALT_STEP EQU 00003h OUT_UART_SEL EQU 00005h OUT_UART_DATA EQU 00006h OUT_RBI EQU 00007h OUT_UART_CMD EQU 0000Eh RBI_ADDR_1 EQU 00001h RBI_ADDR_2 EQU 00002h RBI_ADDR_3 EQU 00004h RBI_ADDR_4 EQU 00008h RBI_ADDR_5 EQU 00010h RBI_ADDR_6 EQU 00020h RBI_ADDR_7 EQU 00040h coldstart Label 00000h RESTART Label 00001h RST1 Label 00008h HLF001 Label 0000Eh RST2 Label 00010h RST3 Label 00018h GET_OBS_DAT Label 0001Eh BEEP Label 00026h RST5 Label 00028h RST5b Label 00029h POPRET Label 00035h INTERRUPT Label 00038h NO_OBS Label 0004Ah ZFILL_HL_DE Label 00055h zfill2 Label 00056h ZFILL_HL_B Label 00063h A_FILL_HL_B Label 00064h HLPTR_MAXMIN_DE Label 0006Bh HLPTR_MAXMIN_DE2 Label 0006Ch MAX_HL_MIN_DE Label 00070h mhlmde2 Label 00077h A_TO_CRT_BUF Label 0007Ah atcb1 Label 00087h SET_KB_RDY_STATUS Label 00092h SET_STATUS_RESP Label 00094h ADDR2_READY Label 00098h CPU_POLL_WAIT Label 000A0h INDTAB Label 000BCh TERM_PRIORITY Label 00100h TERM_LOOKUP Label 00120h DE_SUB_HL Label 0012Bh HL_SUB_DE Label 0012Ch SET_VP_MODE Label 00133h COLD_INIT Label 00146h POWER_ON_INIT Label 00148h pwron2 Label 0014Ah init_uarts Label 0015Ch rom_cksum1 Label 00196h rom_cksum2 Label 0019Bh cksum_hang Label 001ACh MAIN_EVENT Label 001B9h main2 Label 001CBh txd_lookup Label 00202h main3 Label 00229h main4 Label 0022Bh main5 Label 00233h QUERY_LINE_REQ Label 00244h qlr1 Label 00274h qlr2 Label 0028Ch qlr3 Label 00294h ACCEPT_LINE_REQ_DATA Label 002AAh alrd1 Label 002C1h alrd2 Label 002C9h alrd3 Label 002E2h alrd4 Label 002FAh SEND_CR_LF Label 00301h DELETE_LINE_REQ Label 0030Fh CLEAR_LR_FLAGS Label 0031Bh alrd5 Label 00321h alrd6 Label 00334h alrd7 Label 00369h CURS_TO_SOL Label 00370h CURS_TO_HL Label 00373h CURS_TO_DE Label 00374h CTDE1 Label 00380h ctde2 Label 00392h ctde3 Label 003A5h ctde4 Label 003A9h ctde5 Label 003ACh ctde6 Label 003B3h ctde7 Label 003BCh ctde8 Label 003CDh ctde9 Label 003D3h ctde10 Label 003DAh ctde11 Label 003EFh ctde12 Label 003F2h ctde13 Label 003F9h txp1 Label 003FEh txp2 Label 00401h txp3 Label 00404h TX_POLL Label 00410h txp4 Label 00441h txp5 Label 00463h txp6 Label 0047Bh txp7 Label 00480h txp8 Label 00486h txp9 Label 004A8h txp10 Label 004AAh txp11 Label 004C7h txp12 Label 004DFh LIMIT_TX_XFER Label 004E7h CR_LIMIT_XFER Label 004F6h crlx1 Label 00500h crlx2 Label 0050Eh S0510 Label 00510h t0519 Label 00519h MOV2TXBUF Label 0051Eh mtxb2 Label 00529h MARK_BUF_EMPTY Label 00533h STATUS_RESPONSE Label 00541h srs1 Label 0055Fh srs2 Label 0056Eh srs3 Label 0057Bh SEND_F0_IB9 Label 00585h SEND_A_IB9 Label 00587h ADDR6_OR_7 Label 00590h IDXJMPA Label 005A5h SET_ACT_TERM Label 005AEh S05BF Label 005BFh REQ_CRT_BUFFER Label 005D3h rcb1 Label 005E5h REQ_PRINT_BUFFER Label 005EAh rpb1 Label 005FCh LINE_REQ Label 00610h lreq1 Label 0064Fh lreq2 Label 0065Dh BUFFER_OBS_6_DATA Label 0065Eh REFILL_LINE_REQ Label 0066Ch PREFILL_LINE_REQ Label 00675h TERMINATE_LINE_REQ Label 0069Ch ERROR_LINE_REQ Label 006A1h END_OF_LINE_REQ Label 006A6h END_LINE_REQ Label 006A8h SAV_CONT Label 006B9h UPDATE_KB_BUF_READY Label 006BCh INIT_CRT_BUF Label 006C5h INIT_KB_BUF Label 006FCh INIT_CUR_TERM Label 00706h ict1 Label 00719h INIT_PRT_BUF Label 0072Dh KB_READY_CHECK Label 0074Fh KEYIN_LINE_REQ Label 0075Eh KEYIN_POLL_REQ Label 00767h CKHOST2 Label 0077Ch RELAY_KB_ENTRY Label 00790h RKBE2 Label 00793h SEND_E_IB9 Label 007A4h rkbe3 Label 007A8h rkbe4 Label 007BFh rkbe5 Label 007DDh rkbe6 Label 007E2h SEND_E_IBS Label 007E5h SEND_A_IBS Label 007E6h SEND_B_IBS Label 007E7h CHECK_HOST Label 007EFh ckhost3 Label 00805h ADDR7 Label 00821h ckhost4 Label 0082Dh CKHOST5 Label 00836h ckhost6 Label 00863h ckhost7 Label 00870h TEST_CRT_BUF_ACTIVE Label 00883h SET_CRT_BUF_ACTIVE Label 0088Dh SET_BUF_ACTIVE Label 00890h ADDR1_TO_4 Label 0089Ch COMPRESS Label 008B0h cmprs1 Label 008C7h cmprs2 Label 008D6h cmprs3 Label 008E0h cmprs4 Label 008E1h cmprs5 Label 008E9h cmprs6 Label 008F9h cmprs7 Label 00908h cmprs8 Label 00914h cmprs9 Label 0091Ah cmprs10 Label 00924h cmprs11 Label 0093Eh cmprs12 Label 00944h cmprs13 Label 0094Ah cmprs14 Label 00950h CMPRS15 Label 0095Eh cmprs16 Label 0096Eh TIMED_POLL Label 00974h POLL_150 Label 00978h tpoll1 Label 0097Bh tpoll2 Label 00985h tpoll3 Label 00997h tpoll4 Label 00998h CBS457 Label 0099Ah cbs457a Label 009AAh cbs457b Label 009AEh cbs457c Label 009BAh cbs457d Label 009CBh INT_HANDLER Label 009D9h int_hand1 Label 00A00h int_hand2 Label 00A25h int_hand3 Label 00A27h int_hand4 Label 00A2Ch int_hand5 Label 00A41h int_hand6 Label 00A46h PUSH_KEY_PAIR Label 00A48h pkp1 Label 00A69h pkp2 Label 00A7Fh INT_EXIT Label 00A83h RX_12_OR_13 Label 00A8Eh RX_RST Label 00AA5h RX_RST_HALT Label 00AAFh RX_GE_F8 Label 00ACFh rxgf8a Label 00ADEh rxgf8b Label 00AE8h rxgf8c Label 00AE9h rxgf8d Label 00AF9h CHK_KB_BUF Label 00B00h MARK_KB_BUF_MT Label 00B06h mkbmt1 Label 00B17h NEXT_KB_PAIR Label 00B1Ah nkbp1 Label 00B39h nkbp2 Label 00B3Fh MARK_KB_MT_EI Label 00B49h gkbe4 Label 00B4Ch BAD_BYTE Label 00B52h ERROR_LINE_REQ2 Label 00B57h RX_POLL1 Label 00B73h rxp2 Label 00B7Bh RXP3 Label 00B7Ch RX_POLL Label 00B84h rxp4 Label 00BC6h KB_TYPE_FE Label 00BCCh kbtfe1 Label 00BD8h kbtfe2 Label 00BDEh KB_TYPE_FF Label 00BE9h A_TO_LINE_REQ Label 00C01h atlr1 Label 00C26h atlr2 Label 00C2Dh WRAP_CUR_COL Label 00C3Ah KB_FF_LT_E6 Label 00C45h KB_FF_EDIT Label 00C5Bh KB_FF_CTRL Label 00C69h DO_BS Label 00C73h dobs1 Label 00C84h dobs2 Label 00C96h DOBS3 Label 00CA4h EDIT_SF8 Label 00CA8h CLR_CUR_TO_EOB Label 00CAEh EMIT_HL_SPACES Label 00CC2h ehls1 Label 00CCDh ehls2 Label 00CDAh ehls3 Label 00CF7h ehls4 Label 00CFFh ehls5 Label 00D04h ehls6 Label 00D0Eh ehls7 Label 00D18h EDIT_SF15 Label 00D2Fh L0D43 Label 00D43h KB_TYPE_ENDI Label 00D64h kbte1 Label 00D7Ah CUR_LEFT_DE Label 00D9Bh EDIT_SF4_10 Label 00DB0h sf4a Label 00DCFh sf4b Label 00DDAh EDIT_SF7 Label 00DE4h EDIT_SF11 Label 00DEAh EDIT_SF12 Label 00DF0h CUR_RIGHT_DE Label 00DF3h CUR_TO_DE_DONE Label 00E01h EDIT_SF5 Label 00E07h EDIT_SF6 Label 00E1Dh EDIT_SF10 Label 00E33h sf10a Label 00E4Ch sf10b Label 00E4Dh sf10c Label 00E69h sf10d Label 00E6Dh EDIT_SF9 Label 00E7Ah sf9a Label 00E92h sf9b Label 00EAEh sf9c Label 00EB2h SET_LINEREQ_STATUS Label 00EBFh CHECK_KB_MT_FLAG Label 00EC6h CURSOR_ON Label 00ED5h BLANK_SPAN Label 00EF1h DO_REDRAW Label 00F0Dh ALRD_REDRAW Label 00F13h alrdd1 Label 00F3Dh alrdd2 Label 00F5Eh alrdd3 Label 00F6Ah alrdd4 Label 00F75h CURSOR_TO_START Label 00F8Ah INJECT_CR Label 00FA1h RESET_KB_BUF Label 00FA6h KB_TYPE_FD Label 00FB5h kbtfd1 Label 00FBDh kbtfd2 Label 00FC8h kbtfd3 Label 00FD1h CLEAR_TOK Label 00FD9h Statistics ---------- "Name" = 0 "EQU" = 59 "SET" = 0 Labels = 284