~vertigo/forthbox

245380abef46634a8506bd16dc72de781810c7c9 — Samuel A. Falvo II 3 months ago c2d068d
Design document for Console output
1 files changed, 133 insertions(+), 0 deletions(-)

A docs/Console-Output-Design.md
A docs/Console-Output-Design.md => docs/Console-Output-Design.md +133 -0
@@ 0,0 1,133 @@
# Console Output
## Parameters
|    Name    | a.k.a. |    Value     | Purpose                                                                  |
| :--------: | :----: | :----------: | ------------------------------------------------------------------------ |
|     W      |        |  80 (typ.)   | Maximum width in characters                                              |
|     H      |        |  25 (typ.)   | Maximum height in characters                                             |
| FONTHEIGHT |   FH   |   8 (typ.)   | Font Height; maximum number of rasters per character row.                |
| FONTWIDTH  |   FW   |  256 (typ.)  | Font width in bytes; maximum number of bytes per raster row in the font. |
|   FBSIZE   |        | W \* H \* FH | Maximum number of bytes in the framebuffer                               |
## Global State
|    Name     | Type |                      Invariant                       | Purpose                                                                                                                                                             |
| :---------: | :--: | :--------------------------------------------------: | ------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|   cursorX   |  i8  |                      0 <= n < W                      | Current cursor column                                                                                                                                               |
|   cursorY   |  i8  |                      0 <= n < H                      | Current cursor row                                                                                                                                                  |
|  cursorOfs  | i16  | (0 <= n < FBSIZE) /\\ (n = FH\*W\*cursorY + cursorX) | Current cursor offset in the raw framebuffer, pre-computed because 65816 lacks efficient multiply functionality                                                     |
| offCounter  |  i8  |                     0 <= n < 256                     | Cursor Off Counter.  If **zero**, the cursor remains visible to the user.  If **non-zero**, the cursor will appear to disappear.  This makes screen updates faster. |
| cursorDrawn | bool |                  n in {true, false}                  | If true, the block cursor is visible to the user; otherwise, it has been removed from the display.                                                                  |
## Main API
### EMIT
    Turn cursor off.
    Copy glyph from font to current screen address.
    Advance cursor right by one.
    Turn cursor on.
### TYPE
    Turn cursor off.
    For each character in the supplied buffer do
        Copy glyph from font to current screen address.
        Advance cursor right by one.
    Turn cursor off.
### TYPEZ
    Turn cursor off.
    While a NUL character has not been found, do
        Copy glyph from font to current screen address.
        Advance cursor right by one.
        Retrieve next character.
	Turn cursor on.
### PAGE
    Turn cursor off.
    Set cursor to (0,0).
    Fill framebuffer with 0.
    Turn cursor on.
### AT-XY
    Turn cursor off.
    Set cursor X,Y to coordinates given.
    Turn cursor on.
### AT-XY?
    Return cursorX, cursorY, cursorOfs.
### Turn Cursor On
    Decrement offCounter if offCounter > 0.
    If offCounter == 0 then Draw cursor.
### Turn Cursor Off
    Increment offCounter if limit not reached.
    If offCounter != 0 then Erase cursor.
## Miscellaneous Backing Functions
These functions are not typically invoked by the client software, but rather exist primarily to support the main entry-points above.
### Advance cursor right
    If cursor already on right-hand edge of the display, then
        Advance cursor down.
	    Return cursor to left edge of the display.
	Else
		Move cursor right by 1.
    end 
### Advance cursor down
    If cursor already on bottom edge of the display, then
        Scroll display up.
    Else,
	    Move cursor down by 1.
    end.
### Draw Cursor
    If cursor not drawn then
        XOR cursor rectangle.
        Set cursor to drawn.
    end
### Erase Cursor
    If cursor not erased then
        XOR cursor rectangle.
        Set cursor to erased.
    End
## Frame Buffer Procedures
These functions are not typically invoked by the client software, but rather exist primarily to support the main entry-points above.
### Copy Glyph to Current Screen Address
This algorithm assumes a font is just a 2048x8 bitmap, arranged 256 bytes across and 8 bytes high.

    Calculate font pointer from glyph.
    Calculate framebuffer pointer by adding cursorOfs to the base address of the frame buffer.
    For all raster rows in the font,
        Copy byte from font to the framebuffer.
        Advance font pointer by FW.
        Advance framebuffer pointer by W.
### XOR Cursor Rectangle
    For each byte in the cursor image,
        Invert all bits in the byte.
### Scroll Display Up
    Block copy the frame buffer from raster row FH to raster row 0.
    Fill the bottom-most line with 0.
### Fill Framebuffer
    For each byte address in the frame buffer,
	    Set byte to desired value.
### Fill Bottom-Most Line
    For each byte address in the bottom-most line,
        Set byte to desired value.
## Cursor Coordinate Accessors
Because of the need to keep the cursorX, cursorY, and cursorOffset variables in sync at all times, attempts to *alter* one or more of these variables should always be done behind a procedure of some kind.  (It is always safe to read these variables when needed.)  This should help keep text display relatively quick for a 4MHz CPU without multiplication instructions.
### Set cursor X,Y
This seemingly strange implementation exists because the 65816 lacks a multiply instruction.  Therefore, when calculating `cursorOffset`, we just add or subtract a fixed quantity as often as needed to achieve the desired multiplication effect.

This implementation is **slow** in the worst-case.  I estimate about 32 clock cycles for each (for lack of better term) "row seek."  Worst case on a 640x480 display with an 8-pixel font height, that's about 60\*32=1920 clock cycles (480us at 4MHz) to recalculate the new cursorOffset.  But, since most cursor moves are expected to be only a small handful of rows at most, *on average*, performance should be faster than when using a dedicated multiplication routine.

However, it is correct.  As long as `cursorX` and `cursorY` are inside the character matrix boundaries, the new coordinates (remember, they are constrained as well) ensures that the new value of `cursorOffset` also resides within the framebuffer boundaries.

    Constrain new cursor position.
    Let delta be the (signed) difference from the new column to the current column.
    Add delta to cursorX and to cursorOffset.
    While new row lies below the current row, do
	    Move cursor down by 1.
    end
    While new row lies above the current row, do
	    Move cursor up by 1.
    end
### Constrain Cursor Position
    If X is less than zero, then set X to 0.
    If X is greater than or equal to W, then set X to W-1.
    If Y is less than zero, then set Y to 0.
    If Y is greater than or equal to H, then set Y to H-1.
### Move Cursor Down by 1
    Increment cursorY by 1.
    Increment cursorOffset by W*FH.
### Move Cursor Up by 1
    Decrement cursorY by 1.
    Decrement cursorOffset by W*FH.
### Move Cursor Right by 1
    Increment cursorX by 1.
    Increment cursorOffset by 1.