mirror of
https://github.com/hsoft/collapseos.git
synced 2024-11-14 15:08:05 +11:00
224 lines
7.7 KiB
Forth
224 lines
7.7 KiB
Forth
|
( ----- 600 )
|
||
|
TI-84+ Recipe
|
||
|
|
||
|
Support code for the TI-84+ recipe. Contains drivers for the
|
||
|
keyboard and LCD.
|
||
|
|
||
|
551 LCD 564 Keyboard
|
||
|
( ----- 601 )
|
||
|
TI-84+ LCD driver
|
||
|
|
||
|
Implement (emit) on TI-84+ (for now)'s LCD screen.
|
||
|
Load range: 555-560
|
||
|
|
||
|
The screen is 96x64 pixels. The 64 rows are addressed directly
|
||
|
with CMD_ROW but columns are addressed in chunks of 6 or 8 bits
|
||
|
(there are two modes).
|
||
|
|
||
|
In 6-bit mode, there are 16 visible columns. In 8-bit mode,
|
||
|
there are 12.
|
||
|
|
||
|
Note that "X-increment" and "Y-increment" work in the opposite
|
||
|
way than what most people expect. Y moves left and right, X
|
||
|
moves up and down.
|
||
|
(cont.)
|
||
|
( ----- 602 )
|
||
|
# Z-Offset
|
||
|
|
||
|
This LCD has a "Z-Offset" parameter, allowing to offset rows on
|
||
|
the screen however we wish. This is handy because it allows us
|
||
|
to scroll more efficiently. Instead of having to copy the LCD
|
||
|
ram around at each linefeed (or instead of having to maintain
|
||
|
an in-memory buffer), we can use this feature.
|
||
|
|
||
|
The Z-Offset goes upwards, with wrapping. For example, if we
|
||
|
have an 8 pixels high line at row 0 and if our offset is 8,
|
||
|
that line will go up 8 pixels, wrapping itself to the bottom of
|
||
|
the screen.
|
||
|
|
||
|
The principle is this: The active line is always the bottom
|
||
|
one. Therefore, when active row is 0, Z is FNTH+1, when row is
|
||
|
1, Z is (FNTH+1)*2, When row is 8, Z is 0. (cont.)
|
||
|
( ----- 603 )
|
||
|
# 6/8 bit columns and smaller fonts
|
||
|
|
||
|
If your glyphs, including padding, are 6 or 8 pixels wide,
|
||
|
you're in luck because pushing them to the LCD can be done in a
|
||
|
very efficient manner. Unfortunately, this makes the LCD
|
||
|
unsuitable for a Collapse OS shell: 6 pixels per glyph gives us
|
||
|
only 16 characters per line, which is hardly usable.
|
||
|
|
||
|
This is why we have this buffering system. How it works is that
|
||
|
we're always in 8-bit mode and we hold the whole area (8 pixels
|
||
|
wide by FNTH high) in memory. When we want to put a glyph to
|
||
|
screen, we first read the contents of that area, then add our
|
||
|
new glyph, offsetted and masked, to that buffer, then push the
|
||
|
buffer back to the LCD. If the glyph is split, move to the next
|
||
|
area and finish the job.
|
||
|
(cont.)
|
||
|
( ----- 604 )
|
||
|
That being said, it's important to define clearly what CURX and
|
||
|
CURY variable mean. Those variable keep track of the current
|
||
|
position *in pixels*, in both axes.
|
||
|
( ----- 605 )
|
||
|
( Required config: LCD_MEM )
|
||
|
: _mem+ [ LCD_MEM LITN ] @ + ;
|
||
|
: FNTW 3 ; : FNTH 5 ;
|
||
|
: COLS 96 FNTW 1+ / ; : LINES 64 FNTH 1+ / ;
|
||
|
( Wait until the lcd is ready to receive a command. It's a bit
|
||
|
weird to implement a waiting routine in asm, but the forth
|
||
|
version is a bit heavy and we don't want to wait longer than
|
||
|
we have to. )
|
||
|
CODE _wait
|
||
|
BEGIN,
|
||
|
0x10 ( CMD ) INAi,
|
||
|
RLA, ( When 7th bit is clr, we can send a new cmd )
|
||
|
JRC, AGAIN,
|
||
|
;CODE
|
||
|
( ----- 606 )
|
||
|
( two pixel buffers that are 8 pixels wide (1b) by FNTH
|
||
|
pixels high. This is where we compose our resulting pixels
|
||
|
blocks when spitting a glyph. )
|
||
|
: LCD_BUF 0 _mem+ ;
|
||
|
: _cmd 0x10 ( CMD ) PC! _wait ;
|
||
|
: _data! 0x11 ( DATA ) PC! _wait ;
|
||
|
: _data@ 0x11 ( DATA ) PC@ _wait ;
|
||
|
: LCDOFF 0x02 ( CMD_DISABLE ) _cmd ;
|
||
|
: LCDON 0x03 ( CMD_ENABLE ) _cmd ;
|
||
|
( ----- 607 )
|
||
|
: _yinc 0x07 _cmd ; : _xinc 0x05 _cmd ;
|
||
|
: _zoff! ( off -- ) 0x40 + _cmd ;
|
||
|
: _col! ( col -- ) 0x20 + _cmd ;
|
||
|
: _row! ( row -- ) 0x80 + _cmd ;
|
||
|
: LCD$
|
||
|
H@ [ LCD_MEM LITN ] ! FNTH 2 * ALLOT
|
||
|
LCDON 0x01 ( 8-bit mode ) _cmd
|
||
|
FNTH 1+ _zoff!
|
||
|
;
|
||
|
( ----- 608 )
|
||
|
: _clrrows ( n u -- Clears u rows starting at n )
|
||
|
SWAP _row!
|
||
|
( u ) 0 DO
|
||
|
_yinc 0 _col!
|
||
|
11 0 DO 0 _data! LOOP
|
||
|
_xinc 0 _data!
|
||
|
LOOP ;
|
||
|
: NEWLN ( ln -- )
|
||
|
DUP 1+ FNTH 1+ * _zoff!
|
||
|
FNTH 1+ * FNTH 1+ _clrrows ;
|
||
|
: LCDCLR 0 64 _clrrows ;
|
||
|
( ----- 609 )
|
||
|
: _atrow! ( pos -- ) COLS / FNTH 1+ * _row! ;
|
||
|
: _tocol ( pos -- col off ) COLS MOD FNTW 1+ * 8 /MOD ;
|
||
|
: CELL! ( g pos -- )
|
||
|
DUP _atrow! DUP _tocol _col! ROT ( pos coff g )
|
||
|
FNTH * ~FNT + ( pos coff a )
|
||
|
_xinc _data@ DROP
|
||
|
FNTH 0 DO ( pos coff a )
|
||
|
C@+ 2 PICK 8 -^ LSHIFT
|
||
|
_data@ 8 LSHIFT OR
|
||
|
LCD_BUF I + 2DUP FNTH + C!
|
||
|
SWAP 8 RSHIFT SWAP C!
|
||
|
LOOP 2DROP
|
||
|
DUP _atrow!
|
||
|
FNTH 0 DO LCD_BUF I + C@ _data! LOOP
|
||
|
DUP _atrow! _tocol NIP 1+ _col!
|
||
|
FNTH 0 DO LCD_BUF FNTH + I + C@ _data! LOOP ;
|
||
|
( ----- 614 )
|
||
|
Keyboard driver
|
||
|
|
||
|
Load range: 566-570
|
||
|
|
||
|
Implement a (key) word that interpret keystrokes from the
|
||
|
builtin keyboard. The word waits for a digit to be pressed and
|
||
|
returns the corresponding ASCII value.
|
||
|
|
||
|
This routine waits for a key to be pressed, but before that, it
|
||
|
waits for all keys to be de-pressed. It does that to ensure
|
||
|
that two calls to _wait only go through after two actual key
|
||
|
presses (otherwise, the user doesn't have enough time to
|
||
|
de-press the button before the next _wait routine registers the
|
||
|
same key press as a second one).
|
||
|
|
||
|
(cont.)
|
||
|
( ----- 615 )
|
||
|
Sending 0xff to the port resets the keyboard, and then we have
|
||
|
to send groups we want to "listen" to, with a 0 in the group
|
||
|
bit. Thus, to know if *any* key is pressed, we send 0xff to
|
||
|
reset the keypad, then 0x00 to select all groups, if the result
|
||
|
isn't 0xff, at least one key is pressed.
|
||
|
( ----- 616 )
|
||
|
( Requires KBD_MEM, KBD_PORT )
|
||
|
( gm -- pm, get pressed keys mask for group mask gm )
|
||
|
CODE _get
|
||
|
HL POP,
|
||
|
chkPS,
|
||
|
DI,
|
||
|
A 0xff LDri,
|
||
|
KBD_PORT OUTiA,
|
||
|
A L LDrr,
|
||
|
KBD_PORT OUTiA,
|
||
|
KBD_PORT INAi,
|
||
|
EI,
|
||
|
L A LDrr, HL PUSH,
|
||
|
;CODE
|
||
|
( ----- 617 )
|
||
|
( wait until all keys are de-pressed. To avoid repeat keys, we
|
||
|
require 64 subsequent polls to indicate all depressed keys.
|
||
|
all keys are considered depressed when the 0 group returns
|
||
|
0xff. )
|
||
|
: _wait 64 BEGIN 0 _get 0xff = NOT IF DROP 64 THEN
|
||
|
1- DUP NOT UNTIL DROP ;
|
||
|
( digits table. each row represents a group. 0 means
|
||
|
unsupported. no group 7 because it has no key. )
|
||
|
CREATE _dtbl
|
||
|
0 C, 0 C, 0 C, 0 C, 0 C, 0 C, 0 C, 0 C,
|
||
|
0xd C, '+' C, '-' C, '*' C, '/' C, '^' C, 0 C, 0 C,
|
||
|
0 C, '3' C, '6' C, '9' C, ')' C, 0 C, 0 C, 0 C,
|
||
|
'.' C, '2' C, '5' C, '8' C, '(' C, 0 C, 0 C, 0 C,
|
||
|
'0' C, '1' C, '4' C, '7' C, ',' C, 0 C, 0 C, 0 C,
|
||
|
0 C, 0 C, 0 C, 0 C, 0 C, 0 C, 0 C, 0x80 ( alpha ) C,
|
||
|
0 C, 0 C, 0 C, 0 C, 0 C, 0x81 ( 2nd ) C, 0 C, 0x7f C,
|
||
|
( ----- 618 )
|
||
|
( alpha table. same as _dtbl, for when we're in alpha mode. )
|
||
|
CREATE _atbl
|
||
|
0 C, 0 C, 0 C, 0 C, 0 C, 0 C, 0 C, 0 C,
|
||
|
0xd C, '"' C, 'W' C, 'R' C, 'M' C, 'H' C, 0 C, 0 C,
|
||
|
'?' C, 0 C, 'V' C, 'Q' C, 'L' C, 'G' C, 0 C, 0 C,
|
||
|
':' C, 'Z' C, 'U' C, 'P' C, 'K' C, 'F' C, 'C' C, 0 C,
|
||
|
0x20 C, 'Y' C, 'T' C, 'O' C, 'J' C, 'E' C, 'B' C, 0 C,
|
||
|
0 C, 'X' C, 'S' C, 'N' C, 'I' C, 'D' C, 'A' C, 0x80 C,
|
||
|
0 C, 0 C, 0 C, 0 C, 0 C, 0x81 ( 2nd ) C, 0 C, 0x7f C,
|
||
|
: _2nd@ [ KBD_MEM LITN ] C@ 1 AND ;
|
||
|
: _2nd! [ KBD_MEM LITN ] C@ 0xfe AND + [ KBD_MEM LITN ] C! ;
|
||
|
: _alock@ [ KBD_MEM LITN ] C@ 2 AND ;
|
||
|
: _alock^ [ KBD_MEM LITN ] C@ 2 XOR [ KBD_MEM LITN ] C! ;
|
||
|
( ----- 619 )
|
||
|
: _gti ( -- tindex, that it, index in _dtbl or _atbl )
|
||
|
0 ( gid ) 0 ( dummy )
|
||
|
BEGIN ( loop until a digit is pressed )
|
||
|
DROP
|
||
|
1+ DUP 7 = IF DROP 0 THEN ( inc gid )
|
||
|
1 OVER LSHIFT 0xff -^ ( group dmask ) _get
|
||
|
DUP 0xff = NOT UNTIL _wait
|
||
|
( gid dmask )
|
||
|
0xff XOR ( dpos ) 0 ( dindex )
|
||
|
BEGIN 1+ 2DUP RSHIFT NOT UNTIL 1-
|
||
|
( gid dpos dindex ) NIP
|
||
|
( gid dindex ) SWAP 8 * + ;
|
||
|
( ----- 620 )
|
||
|
: _tbl^ ( swap input tbl )
|
||
|
_atbl = IF _dtbl ELSE _atbl THEN ;
|
||
|
: (key)
|
||
|
0 _2nd! 0 ( lastchr ) BEGIN
|
||
|
_alock@ IF _atbl ELSE _dtbl THEN
|
||
|
OVER 0x80 ( alpha ) =
|
||
|
IF _tbl^ _2nd@ IF _alock^ THEN THEN
|
||
|
SWAP 0x81 = _2nd!
|
||
|
_gti + C@
|
||
|
DUP 0 0x80 >< UNTIL ( loop if not in range )
|
||
|
( lowercase? )
|
||
|
_2nd@ IF DUP 'A' 'Z' =><= IF 0x20 OR THEN THEN
|
||
|
;
|
||
|
: KBD$ 0 [ KBD_MEM LITN ] C! ;
|