GTerm 1.0 --------- Text/Grafik-Terminal als Weiterentwicklung des b&w MyLCD Terminals. Text: Controlcodes weitgehend wie beim MyLCD. Grafik: nach Controlcode 0x1F. Features: - RS232: HW oder optional SW Flow Control - optional Error Correction - Text: Textmodus wie beim MyLCD (Latin-1 character set) - Grafik: Grafik-Primitives - Grafik: Pixelbuffer-Objekte als Paint Targets - Grafik: Pixelbuffer-Objekte können schwebend eingeblendet werden - Grafik: Benutzer-definierte Funktionen - optional Maus- und Tastaturabfrage am Terminal - Dateispeicher im Terminal Abweichende Controlcodes ------------------------ 0x1E IDENTIFY The terminal responds with this string: --> 0x1E, "MyLCD,GTerm 0a,ssz=ROWS*COLS,fsz=ROWS*COLS,csz=12*8, gfx,rgb=888,mem=1G,str=240,fs,ptr,kbd\n" MyLCD = Kio's terminal GTerm = Name ssz = screen size = ROWS*COLS Textzeilen im Textmodus fsz = framebuffer size = ROWS*COLS Textzeilen im Textmodus csz = character size = ROWS*COLS Zeichengröße im Textmodus (Pixel) gfx = Grafik-VDU vorhanden rgb = Bits pro Kanal in True Color mem = Verfügbarer Speicher für alle Pixelbuffer incl. Framebuffer (in Pixel) str = Anzahl logischer Streams fs = File System: Terminal can store files ptr = Pointer device available, e.g. mouse kbd = Keyboard available 0x1B ESC Set temporary or permanent settings. 0 write settings to EEPROM - sio_baudrate - transmission mode 1 display lighter ignored 2 display darker ignored 4 set UDG: , must follow must be in range 0x80 .. 0x9F UDG characters are stored in EEPROM immediately. 6…17: set sio baudrate to 150 << (n/2) * (2+n&1) applied after write_to_eeprom + reset 6 150<<3 * 2 = 2400 7 150<<3 * 3 = 3600 8 150<<4 * 2 = 4800 9 150<<4 * 3 = 7200 10 150<<5 * 2 = 9600 (default) 11 150<<5 * 3 = 14k4 12 150<<6 * 2 = 19k2 13 150<<6 * 3 = 28k8 14 150<<7 * 2 = 38k4 15 150<<7 * 3 = 57k6 16 150<<8 * 2 = 76k8 17 150<<8 * 3 = 115k2 18 150<<9 * 2 = 153k6 new, ignored (impossible value, only for systematical reason) 19 150<<9 * 3 = 230k4 new 20 reserved 21 reserved 22 reserved 23 reserved set transmission mode: if both bits 0xC0 are set. $C0 disable all special transmission modes (default). $Ex enable special transmission mode. enables at least escaped reset, if no other bits set. note: any occurance of CTL must be escaped. bit fields in the low bits specify the transmission mode. $10 enable logical streams $08 enable enable SW handshake note: XON and XOFF must be escaped $04 window size in error correction mode 0: 2 blocks (tiny) 1: 4 blocks (default) $03 enable error correction $03: max. block size = 256 (default) $02: max. block size = 128 $01: max. block size = 64 (tiny) 0x1F GFX [ var. arguments ] Graphics command. See below. Reset ----- The terminal can be reset with control code 0x00, but this won't be detected within control code arguments. Up to 13 bytes can vanish this way in text mode (ESC + UDG). If no control codes with character bitmask argument are used then the maximum is 4 (COPY_WINDOW). If COPY_WINDOW is not used, then the maximum is 2. If graphics commands are used, then the number of outstanding arguments may be virtually unlimited. If a special transmission mode (escaped reset, software handshake or error correction) is used, then the CTL RST reset sequence (see below) is detected at *any* position. So use of 'escaped reset' transmission mode is recommended as a minimum, at least if using graphics. Error Correction and SW Handshake --------------------------------- These options are mostly for RS232 only. If using pipes, sockets or similar there is already a secure transport layer in use. CTL Control Codes ----------------- Bit representation of characters in RS232 bit stream. start bit is 0, data bits are lsb first, stop bit is 1: $85 = 0101000011 = CTL Bytes after CTL: $F0 = 0000011111 (not used) $F1 = 0100011111 = esc_XON escaped $13 $F2 = 0010011111 = esc_XOFF escaped $11 $F3 = 0110011111 = esc_CTL escaped $85 $F4 = 0001011111 reserved $F5 = 0101011111 reserved $F6 = 0011011111 reserved $F7 = 0111011111 reserved $F8 = 0000111111 (not used) $F9 = 0100111111 = ACK $FA = 0010111111 = NAK $FB = 0110111111 = RST $FC = 0001111111 (not used) $FD = 0101111111 = BRK $FE = 0011111111 (not used) $FF = 0111111111 (not used) Reserved Bytes: If any of the special transmission modes is used, then certain characters are used for flow control and must be escaped if they happen to occur anywhere else in the data stream: $F5 = CTL in all special transmission modes $11 = XOFF in software handshake mode $13 = XON in software handshake mode CTL Control Codes: All special transportation modes: CTL, $F3 escaped byte $85 (CTL) CTL, $FB, 0,0,0,0 RST: reset terminal. In software handshake mode: CTL, $F2 escaped byte $11 (XOFF) CTL, $F1 escaped byte $13 (XON) Multiple streams: CTL, S Select stream S. (str < 240) note: maximum number of streams is reported with IDENTIFY. note: maximum number of streams can be at most 240. Error corrected transmission: CTL, S, N, ... Data block N for stream S CTL, $F9, N, ... ACK: block N received OK CTL, $FA, N, ... NAK: checksum error in block N CTL, $FD, 9,9,9,9 BRK: lost synchronization. wait and restart with block 0. CTL, CTL, ... CTL: recursive CTL call, e.g. for sending ACK or NAK. or the stream number must be escaped. Some CTL codes can "interrupt" others at any position: NAK and ACK can interrupt a data block at any position. BRK and RST can interrupt and terminate any CTL sequence at any position. Escaped Reset ------------- When any special transportation mode is in use, then the escaped reset sequence is detected at *any* position. db CTL,RST db 0,0,0,0 This will reset the terminal and reset the serial speed and transmission mode to what is stored in EPROM, which may be different from what was used right now. Multiple Streams ---------------- db CTL,STR This will switch to logical stream STR. If STR is beyond terminal specs, then this command will be ignored. If STR is not open, then the terminal will switch to stream 0. The switch stream command will be ignored if 'multiple streams' was not enabled. The stream number may be escaped, e.g. for stream $11 (~XOFF), $13 (~XON) or $85 (~CTL). Error Corrected Transmission ---------------------------- Data Block: db CTL start of block db STR stream 0 .. 239 db BLK block serial number 0 .. 255 db LEN length of payload 0 .. 255 (or less, depending on setting) db DATA[LEN] payload dw CRC-16 CCITT block checksum [STR .. DATA], low byte transmitted first The transmitted data is split into blocks of up to 255 bytes. A smaller maximum block size may be requested by tiny hosts with little ram. All bytes ( STR, BLK, LEN, DATA and CRC-16 ) except CTL at block start may be escaped! Payload length and block checksum are calculated in the unescaped state. The checksum encloses STR, BLK, LEN and DATA. Handshake Blocks: db CTL, ACK Block BLK received OK. db BLK block number db CRC-8 CRC [$96,BLK], DALLAS-MAXIM polynomial db CTL, NAK Transmission error in block BLK. db BLK block number db CRC-8 CRC [$94,BLK], DALLAS-MAXIM polynomial The BLK and CRC-8 byte may be escaped! Handshake blocks can interrupt data blocks at *any* position. Handshake blocks can even interrupt other handshake blocks. (though they shouldn't.) Reset transportation layer: db CTL, BRK Lost synchronization. db 9,9,9,9 Both sides purge their input and output, wait for 0.1 sec with no input data, wait another 0.1 sec and restart communication with block serial number 0. CRC-16 calculation: #define POLY16 0x8408 /* this is the CCITT CRC 16 polynomial X^16 + X^12 + X^5 + X^0. %10001000000100001 This works out to be 0x1021, but the way the algorithm works % 0001000000100001 lets us use 0x8408 (the reverse of the bit pattern). The high % 1000010000001000 bit is always assumed to be set, thus we only use 16 bits to represent the 17 bit value. */ uint16 crc16( uchar const* q, uint count ) { uint crc = 0xffff; while(count--) { for( uint c = 0x0100 + *q++; c>1; c >>= 1 ) { crc = (crc^c) & 1 ? (crc >> 1) ^ POLY16 : (crc >> 1); } } return ~crc; } CRC-8 calculation: #define POLY8 0x8C /* this is the DALLAS / MAXIM polynomial X^8 + X^5 + X^4 + X^0 %100110001 bits reverted and bit 8 assumed to be set implicitely. */ uint8 crc8( uchar const* q, uint count ) { uint crc = 0xff; while(count--) { for( uint c = 0x0100 + *q++; c>1; c >>= 1 ) { crc = (crc^c) & 1 ? (crc >> 1) ^ POLY8 : (crc >> 1); } } return ~crc; } Graphics Commands ================= All graphics commands are prefixed with control code GFX = $1F. Graphics commands include: - drawing primitives - creating and managing pixel buffers - select pixel buffers for drawing and printing - maintaining up to 64 paining instances (~streams) with separate attributes and drawing target - floating pixel buffers ontop of the frame buffer - handling of mouse and keyboard at terminal itself TODO: aliased (not anti-aliased) drawing: top/left pixel of screen is at 0/0. X and Y are integer numbers. Affected pixels tend to hang to the right & down for even line widths. e.g. plot(0,0) actually affects the top/left pixel of the screen. outlined shapes are wider and higher by the line width. TODO: anti-aliased drawing: X and Y are float numbers. Affected pixels bleed to both sides. plot(0.0,0.0) with line width = 1 actually affects the top/left pixel of the screen. General: LSB is sent first. Legal values: x-pos -2^15 .. +2^15-1 y-pos -2^15 .. +2^15-1 width 0 .. 2^15-1 0 will create an empty buffer height 0 .. 2^15-1 0 will create an empty buffer pixmap ID 0 .. 2^15-1 0 is the frame buffer painter ID 0 .. 63 0 is always assigned to the frame buffer Syntax: u32 4 byte unsigned int, lsb sent first i16 2 byte signed int, lsb sent first u32:ARGB specify a rgb color with alpha mask Reset: - Close all streams: dispose all painters - Dispose all pixmaps: dispose all pixel buffers - Create pixmap 0 for frame buffer - Open stream 0 for drawing to pixmap 0 CREATE_PIXMAP GFX, $40, , , , Create an offscreen pixel buffer. Ignored if ID, width, height or colors is illegal. Ignored if ID = 0 (frame buffer). If pixmap already exists, then dispose it. Clear pixmap to transparency (ARGB), $00 (grey) or 0 (b&w) pixels. pixmapID ≥ 1 width ≥ 1 height ≥ 1 colors = bits/pixel: 1 = b&w, 8 = grey, 32 = ARGB DISPOSE_PIXMAP GFX, $41, Dispose pixmap, unless there are aliasses. Ignored if pixmap does not exist. Ignored if ID = 0 (frame buffer). Unmap pixmap from parent pixmap, if any. Close assigned stream, if any. Dispose pixel buffer and resources, unless there are aliases. Does not affect the current stream unless it was assigned to this pixmap. pixmapID ≥ 1 CREATE_PIXMAP_ALIAS GFX, $42, , Define an alias ID for a pixmap. Ignored if both pixmaps already refer to the same pixmap. Ignored if source pixmap does not exist. Ignored if source or alias ID = 0. If alias pixmap already exists, then dispose it. Assign pixmaps. For use with sprites or other objects, which are mapped more than once. Does not affect the current stream unless it was assigned to the alias pixmap. SELECT_PIXMAP GFX, $43, Select pixmap for current stream. Ignored if pixmap ID is illegal. RESIZE_PIXMAP GFX, $44, , , Resize a pixmap. Ignored if pixmap does not exist. Ignored if width or height is illegal. New pixels are set to transparent (ARGB) or 0 (grey, b&w and frame buffer) The framebuffer may be resized to 0*0 pixels. Then it becomes either totally black (fullscreen) or transparent (in window) and mapped child pixmaps are not cropped to the 0*0 pixmap rect. But printing or drawing to it becomes invisible. Does not affect the current stream. pixmapID ≥ 0, 0 = framebuffer width ≥ 1 (0) height ≥ 1 (0) OPEN_STREAM_FOR_PIXMAP GFX, $45, , Open and select stream for printing and painting to pixmap. Note: painter == stream. If pixmap does not exist then select stream 0. If stream ID is invalid then select stream 0. If stream = 0 or pixmap = 0 then select stream 0. If the stream is open, then close it. If the pixmap is currently assigned to a stream, then close that stream. Assign painter to pixmap, reset painter, and select the painter for drawing and printing. streamID = 1 .. 63 pixmapID ≥ 1 CLOSE_STREAM GFX, $46, Close stream. Ignored if stream is not open. Ignored for stream 0. If the stream is currently selected, then select stream 0. Releases all resources for this stream and delete painter. streamID = 1 .. 63 SELECT_STREAM GFX, $47, Select stream for drawing and printing. If the stream is not open, then select stream 0. streamID = 1 .. 63 SET_PEN_COLOR GFX, $48, Set color to be used for outlines. SET_PEN_COLOR GFX, $49, Set greyscale color to be used for outlines. SET_BRUSH_COLOR GFX, $4A, Set color to be used for filling shapes. SET_BRUSH_COLOR GFX, $4B, Set greyscale color to be used for filling shapes. SET_PEN_WIDTH GFX, $4C, Set width for outlines. PLOT_POINT GFX, $4D, , Plot a single point. DRAW_LINE GFX, $4E, , , , Draw a single line. Both end points are drawn. DRAW_RECT GFX, $4F, , , , Draw a rectangle. The outline is inset by the pen width. DRAW_ELLIPSE GFX, $50, , , , Draw an ellipse. The outline is inset by the pen width. DRAW_POLYGON GFX, $51, , , , ... Draw polygon. If num_points=1 then a single point is plotted. If num_points=2 then a single line is drawn (not filled) DRAW_BEZIER GFX, $52, , , , , , , , ... , Draw Bézier polygon. If num_points=1 then a single point is plotted. If num_points=2 then a single line is drawn. The points are a series of vertices (corners) and control points. num_points defines the number of vertices. Between two vertices there are 2 control points. VccVccVccVcc ... V PLOT_POINTS GFX, $53, , , , ... Plot a bunch of arbitrary points. SET_TEXT_FONT GFX, $99, , , ... SET_TEXT_SIZE GFX, $99, ... SET_TEXT_ATTR GFX, $99, ... SET_TEXT_COLOR GFX, $99, ... DRAW_TEXT GFX, $99, ... GET_DISPLAY_SIZE GFX, $99 ... The DRAW_PIXMAP commands are used to copy the current pixmap into another pixmap, possibly into the frame buffer, with alpha mask applied. Arguments are limited to fit both pixmaps' size. if colors of source and dest are different: source dest grey b&w use msb of each pixel rgba b&w convert to grey (see below) then to b&w (see above) b&w grey 0=0x00, 1=0xFF rgba grey pre-multiply alpha channel, then use weighted formula: r=0.2989, g=0.5870, b=0.1140 b&w argb use background and foreground colors. (same as convert to grey, then to argb) grey argb use grey value as blend from fore- to background color. the foreground color should be opaque and the background color should be transparent. DRAW_PIXMAP GFX, $99, , , Draw pixmap ID at position X,Y into current pixmap. Does not move the drawing position. DRAW_PIXMAP GFX, $99, , , , , , , Draw rectangular portion of pixmap qID at position X,Y into destination pixmap. DRAW_PIXMAP_SCALED GFX, $99, , , , , , , , , Draw rectangular portion of pixmap ID into the currently selected pixmap. The source box is scaled to fit the destination box. The MAP_PIXMAP commands are used to map an RGBA pixmap into the frame buffer, using it's ID as Z index and with alpha mask applied. Arguments are limited to fit pixmap and frame buffer size. Drawing to a mapped pixmap updates the framebuffer asap. UNMAP_PIXMAP GFX, $99, Unmap pixmap from current parent. MAP_PIXMAP GFX, $99, , , Map pixmap at position X,Y into current pixmap. On top of all existing childs. MAP_PIXMAP GFX, $99, , , , , , , Map rectangular portion of pixmap at position X,Y into current pixmap. On top of all existing childs. RAISE_PIXMAP GFX, $99, , , Raise or lower a mapped pixmap. Ignored if pixmap ID is not a child of the current pixmap. if other == ID: how = 0 lower one level how = 1 raise one level if other is a child of the current pixmap: how = 0 below other how = 1 above other else: how = 0 below all others how = 1 above all others DISTORT_PIXMAP ... DRAW_JPG ... DEF_FUNCTION GFX, $53, , , ... Define a simple function which can be executed by the terminal. Functions are assigned to a painter (stream). Functions $00 .. $7F can be stored in a painter. Functions $80 .. $BF are always stored in painter 0 and can be used by any painter. Functions $C0 .. $FF are predefined or may be stored in EPROM. (TODO) Opcodes: get_arg.u8 ( -- n ) read u8 argument from stream get_arg.i16 ( -- n ) read i16 argument from stream get_arg.argb ( -- n ) read color argument from stream ival.u8 ( -- n ) push immediate value ival.i16 ( -- n ) push immediate value set_var.i ( n -- ) set local variable i get_var.i ( -- n ) get local variable i get_bmp_width ( -- n ) get pixmap width get_bmp_height ( -- n ) get pixmap height random ( n -- n ) get a random number in range [0 .. n-1] add, sub, mul, div, rem, and, or, xor, >>, << ( n n -- n ) > >= < <= != == ( n n -- n ) set_pen ( argb -- ) get_pen ( -- argb ) set_pen_width ( n -- ) set_brush ( argb -- ) get_brush (-- argb ) plot ( x y -- ) peek ( x y -- argb ) read pixel from bitmap draw_line (x y x y -- ) draw_rect ( x y w h -- ) draw_ellipse (x y w h -- ) label.l ( -- ) define label l here bra.l ( -- ) branch to label l bra0.l ( n -- ) branch to label l if top==0 bra1.l ( n -- ) branch to label l if top==1 and0.l ( n -- ) if top==0 then push 0 and branch to l or1.l ( n -- ) if top!=0 then push 1 and branch to l return ( -- ) return from procedure wait ( msec -- ) wait: resume stream processing, after msec rum proc in a co-routine unmap_pixmap ( id -- ) map_pixmap ( id x y -- ) map_pixmap ( id zx zy qx qy w h -- ) raise_pixmap ( id other -- ) draw_pixmap ( id x y -- ) draw_pixmap ( id zx zy qx qy w h -- ) draw_pixmap ( id zx zy zw zh qx qy qw qh -- ) RUN_FUNCTION GFX, $54, , ... Run function in current painter or from painter 0. The function will read arguments with each get_arg.x opcode. If the function terminates with return() then the stream resumes normally. If the function suspends with wait() then the stream is resumed too, but an instance of the function remains active as long as the painter is assigned to the pixmap and the function does not return. Whenever wait() times out the function is resumed synchronously until it again calls wait() or return(). When a function blocks the terminal for too long then it is killed. Multiple functions can run in the same painter and on the same pixmap at the same time (synchronized). Even the same function can be run several times in parallel. Possible applications: - Animation: The function maps a series of pixmaps or a series of rects from a single pixmap in regular intervals. - Drawing complex shapes PLOT_GRAPH GFX, $54, $C0, , , , , , ... Draw a series of points with a fixed horizontal spacing. Point N is drawn at x + N*dx, y + dy1 PLOT_VGRAPH GFX, $54, $C1, , , , , , ... Draw a series of points with a fixed vertical spacing. Point N is drawn at x + dx1, y + N*dy DRAW_GRAPH GFX, $54, $C2, , , , , , ... Draw connected lines with a fixed horizontal spacing. End point N is drawn at x + N*dx, y + dy1 DRAW_VGRAPH GFX, $54, $C3, , , , , , ... Draw connected lines with a fixed vertical spacing. Point N is drawn at x + dx1, y + N*dy DRAW_BARGRAPH GFX, $54, $C4, , , , , , ... Draw a series of vertical lines with a fixed horizontal spacing. End point N is drawn at x + N*dx, y + dy1 DRAW_VBARGRAPH GFX, $54, $C5, , , , , , ... Draw a series of horizontal lines with a fixed vertical spacing. Point N is drawn at x + dx1, y + N*dy DRAW_GRID GFX, $54, $C6, , , , , , ENABLE_KEYBOARD after ESC ? GFX, $99, flags & $01 enable keyboard flags & $02 report keycode if this bit is set, then the terminal sends key events to the host, else the terminal only sends the character codes of keys pressed. flags & $04 if $02: also send keyup events flags & $08 if $02: also send modifier key up/down events flags & $10 if $02: also send auto repeat events --> if bit $02 is set, then the terminal sends: GFX, $99, , , key down GFX, $99, , key up modifiers = new state of modifiers bitmask keycode = key number acc. to some standard charcode = latin-1 character code of the key, modified by modifiers. modifiers and keys without character report $00. ENABLE_MOUSE after ESC ? GFX, $99, flags & $01 enable mouse reporting flags & $02 show hardware mouse pointer flags & $04 report all movements flags & $08 report movements if mouse pointer is down flags & $10 report hovered pixmap changes After this command the terminal immediately sends all enabled informations. --> terminal sends GFX, $99, , mouse moved GFX, $99, button up or down GFX, $99, if hovered pixmap changes SET_MOUSE_PIXMAP GFX, $99, Set's a pixmap to be used as mouse pointer. If hardware mouse pointer is enabled, then the pixmap may be cropped and converted to b&w. Else it may be any size and color. SET_MOUSE_POSITION GFX, $99, , Move mouse to position x,y. Position x,y will be limited to the frame buffer size. Will respond with a MOUSE_MOVED and may be other events. ---------------------------------------------------- SourceProc -> stream.send[] -> SenderProc -> sent[][] and pipe Source stores data in send[] buffer for stream. Sender checks for ACK/NAK to send. Sender checks whether received ACKs say it can send next block. Sender chooses a stream (round robbin) and creates a block. Sender sends block and puts it into sent blocks list. Receiver reads pipe until SOB found. CTL blocks are processed! Receiver reads block. CTL blocks are processed! Receiver adds HSK block to Sender's hsk[] list. If OK, received data is stored into stream.rcvd[] as set in STR. class Pipe { char obu[1024]; uint orp, owp; // buffer read and write pointers char ibu[1024]; uint irp, iwp; // buffer read and write pointers void write_SOB(); void write_CTL(); void write_char(char); int read_char(char&); // returns error codes for SOB, timeout. void write(bu,sz); int read(bu,sz); // returns error codes for SOB or timeout. uint free(); uint avail(); } // class for error correcting stream without support for logical streams class PipeNoMuxer { Pipe* ipip; // character pipe Pipe* opip; // error corrected block pipe char* oblks[4]; // sent blocks, waiting for ACK uint oblks_free=4; char* iblks[4]; // rcvd blocks, which could not yet be sent to ipip uint iblks_free=4; char hsk[2*4*SIZEOFHSK]; } // class for stream multiplexer class PipeMuxer { Pipe* ipips[PIPES]; // character pipes Pipe* opip; // error corrected block pipe char* oblks[4]; // sent blocks, waiting for ACK uint oblks_free=4; char* iblks[4]; // rcvd blocks, which could not yet be sent to consumer uint iblks_free=4; char hsk[2*4*SIZEOFHSK]; } // class for serial connection: class SerialEndpoint { FILE istream; FILE ostream; Pipe* pipe; } // class for stream end point: painter class TerminalEndpoint { FILE istream; FILE ostream; Pipe* pipe; } void Sender(Pipe ipip[], Pipe opip) { uint8 oblk=0; // next block number to use uint pip = 0; for(;;) { if(!opip.avail()) { wait(); continue; } if(hsk.avail()) { send_hsk(); continue; } if(oblks_free==0) { wait(); continue; } for(uint i=0;i