Node 411

This node requests data from block buffer or command line buffer, extracts symbols, and sends them to node 412. It generates a frame, each frame composed of 24 rows, each row having 20 scan lines and length of 50 characters at most. First 23 lines are read from block buffer (nodes 313, 314, 315, and 415), the last line from command line buffer in node 311.

Source code

usb node 412 node 311 node 411
411 frame
12/ 00 w-c 2/ 2/ 2/ 2/ 2/ 2/
6/ 02 2/ 2/ 2/ 2/ 2/ 2/
0/ 04 3F and ;
sym 06 pw-pwc over 3 and if 2/
if 2 drop push 2 + dup 2/ 2/ !b @b pop 0/ ;
then 1 drop push 1 + pop dup 6/ ;
then 0 drop push 1 + pop dup 12/ ;
line 17 pw-pw over ! sym dup 39 eol or if drop ! line ;
then drop ! dup ! 20030 ! ;
row 21 pwxx-pw 19 for drop drop over over line next ;
rows 27 pan push !b @b
begin over over sym 3C eob or .
while drop row pop -1 . + push end
then drop drop drop over ! 30030 ! pop ! ;
frame 35 dup or dup 22 rows
dup or - !b 800 dup dup or dup rows frame ; 3E

init left a! up b! frame ; 42

2 d 2 r 0 ether


12/, 6/, 0/
Extract symbol c from word w by shifting it right by the given number of bits and masking bits 6-17 off.
Get symbol c from word w pointed to by pointer p. Increment pointer after extracting each symbol; request next word from a buffer if the extracted symbol is the last one in the word.
Send pointer p to LEFT, and tests if symbol c is eol. If so, send eol tag, (which renders as a space on screen), and send 20030 to signalize to node 412 that spaces are to be printed till the end of row. Otherwise send the symbol and repeat.
Call line 20 times always with the same initial value of pointer p and first word in the row w.
Display multiple rows. Push n, the number of rows to be displayed less one, on return stack, and request a word from a buffer at address a. In the loop extract the first symbol, and test if it is eob. If not, call row, then decrement row counter in R, and repeat the loop. When at the end of block (as indicated by eob symbol), send current pointer and eob tag LEFT, followed by 30030 to signalize end of block, and then the number of remaining rows less one (thus -1 means all rows have already been displayed). Node 412 generates empty rows from this number if needed.
Display all 24 rows of one frame. Put pointer p, initial address a, and number of rows less one n, corresponding to block display, on stack, and call rows. This displays 23 rows of current block. Then send a negative flag to node 311 indicating start of command line rendering, set parameters corresponding to command line on stack, and call rows again. When whole screen has been rendered jump back to frame.
Set registers A and B, and jump to frame.


Display in etherforth is character oriented. In order to display one row of characters, we need to get symbols for the row 20 times, once for each scan line. Furthermore, rows are stored in video buffer back-to-back, regardless of their length (see more details here). Thus, for rows shorter than the maximum length spaces have to be displayed till the true end of row. Similarly, past end-of-block symbol rows full of spaces have to be generated till the bottom of the screen, where command line is displayed. Node 411 takes care of providing symbols for all scan lines; filling rows with spaces is only requested here, and carried out later by node 513.

Another issue that had to be solved is a speed constraint. This node has about 83 ns to generate one symbol. This time includes retrieval of a word from a buffer, extraction of one symbol indicated by the pointer, and sending it down the display chain. In order to make this process fast Chuck has invented a clever way how to compress data in a buffer, and how to quickly extract symbols.

Data compression is achieved by using 6-bit symbols for f18 instructions that may be composed of up to 5 characters. Those symbols are called tokens. This node sends tokens as they are extracted from words retrieved from buffers, and they are expanded to character strings later in node 412. On average, using tokens we gain some time here.

The other problem that had to be solved was symbol addressing. Address a points to a word to be read from buffer. Both block buffer and command line buffer start at address zero. Pointer p, on the other hand, informs about relative position of a symbol in the frame. There are three symbols per word so calculation of address from pointer would require division by three, which cannot be simply implemented with shift instructions. To overcome this obstacle, pointers skip each fourth value. Thus, when incremented from zero, they grow in a series: 0 1 2 4 5 6 8 9 10 12 13 14... Every triplet of pointers is addressing symbols in the same word. Calculation of address from a pointer then requires division by four, which is easily achieved by 2/ 2/.

After boot up this node starts word frame. This is and endless loop, where one full frame is generated per cycle. We start by setting block parameters on stack: pointer p equals to zero, initial address a equals to zero too, and number of rows less one n is 22. Calling word rows generates 23 block rows. Sending a negative flag to node 311 indicates we want to read from command line buffer from now on. We set command line buffer parametres on stack, i.e. 800 as the initial pointer, zero as the initial buffer address, and zero as n, indicating only one row to be rendered. With those parameters on stack we call rows again. Full screen frame rendered, we jump back to frame, prepare for the next frame, and wait till the end of vertical blanking period.

The initial pointer used for command line rendering is interpreted as zero address (only bits 0-9 are relevant for addressing) for value 800. Bit 11 set indicates to node 412 that cursor is located in the command line and not in block rows.

Special codes 20030 and 30030 are format marks signaling end-of-row and end-of-block, respectively. They have the following structure:


Bit 17 indicates it's a special formating code. Can be easily tested with -if instruction.
Bit 16 set indicates end-of-block. Zero value indicates end-of-row. This bit can be tested with -if instruction after shifting whole word one bit left.
Bits 12-15 have to be zero as they are interpreted as a bitmap slice.
Bits 6-11 are irrelevant.
Bis 4 and 5 must be set. This makes these formating words behave as if they were tags.
Bits 0-3 actually indicate color of this "tag" but due to empty "bitmap slice" bits no color is ever shown and their value is irrelevant too.

Mark 20030 is actually rendered on screen. This is why "bitmap slice" bits must be zero. If you wish to see it, set bits 12-15 high and color in bits 0-3 according to this table. You will see vertical bars four pixels wide filling lines till the end of row. You can also visualize empty rows past end-of-block by modifying this number in node 412.