95ab1ad588
Working in "blk/" folder from a modern system is harder than it should be. Moving blocks around is a bit awkward, grepping is a bit less convenient than it could be, git blame has troubles following, etc. In this commit, we modify blkpack and blkunpack to work with single text files with blocks being separated by a special markup. I think this will make the code significantly more convenient to work into.
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! ;
|