b1e162b8a3
Replace the "g" arg (glyph) with "c" (character). The reason why "g" was used was to save a "0x20 -" operation at all CELL! implementations, but this came with too big a drawback: it made CELL! hardly usable outside of the Grid subsystem, mostly because the user of CELL! would often have to do "0x20 -". For example, I want the SMS's Pad driver to use CELL! directly instead of having to do EMIT+XYPOS-messing-around. I would have had to do a "0x20 -" there.
190 lines
6.8 KiB
Forth
190 lines
6.8 KiB
Forth
( ----- 600 )
|
|
Sega Master System Recipe
|
|
|
|
602 VDP 610 PAD
|
|
620 KBD 625 Ports
|
|
( ----- 602 )
|
|
( VDP Driver. requires TMS9918 driver. Load range B602-B604. )
|
|
CREATE _idat
|
|
0b00000100 C, 0x80 C, ( Bit 2: Select mode 4 )
|
|
0b00000000 C, 0x81 C,
|
|
0b00001111 C, 0x82 C, ( Name table: 0x3800, *B0 must be 1* )
|
|
0b11111111 C, 0x85 C, ( Sprite table: 0x3f00 )
|
|
0b11111111 C, 0x86 C, ( sprite use tiles from 0x2000 )
|
|
0b11111111 C, 0x87 C, ( Border uses palette 0xf )
|
|
0b00000000 C, 0x88 C, ( BG X scroll )
|
|
0b00000000 C, 0x89 C, ( BG Y scroll )
|
|
0b11111111 C, 0x8a C, ( Line counter (why have this?) )
|
|
( ----- 603 )
|
|
: _zero ( x -- send 0 _data x times )
|
|
( x ) 0 DO 0 _data LOOP ;
|
|
( Each row in ~FNT is a row of the glyph and there is 7 of
|
|
them. We insert a blank one at the end of those 7. For each
|
|
row we set, we need to send 3 zero-bytes because each pixel in
|
|
the tile is actually 4 bits because it can select among 16
|
|
palettes. We use only 2 of them, which is why those bytes
|
|
always stay zero. )
|
|
: _sfont ( a -- Send font to VDP )
|
|
7 0 DO C@+ _data 3 _zero LOOP DROP
|
|
( blank row ) 4 _zero ;
|
|
: CELL! ( c pos )
|
|
2 * 0x7800 OR _ctl ( c )
|
|
0x20 - ( glyph ) 0x5e MOD _data 0 _data ;
|
|
( ----- 604 )
|
|
: VDP$
|
|
9 0 DO _idat I 2 * + @ _ctl LOOP _blank
|
|
( palettes )
|
|
0xc000 _ctl
|
|
( BG ) 1 _zero 0x3f _data 14 _zero
|
|
( sprite, inverted colors ) 0x3f _data 15 _zero
|
|
0x4000 _ctl 0x5e 0 DO ~FNT I 7 * + _sfont LOOP
|
|
( bit 6, enable display, bit 7, ?? ) 0x81c0 _ctl ;
|
|
|
|
: COLS 32 ;
|
|
: LINES 24 ;
|
|
( ----- 610 )
|
|
Pad driver - read input from MD controller
|
|
|
|
Conveniently expose an API to read the status of a MD pad A.
|
|
Moreover, implement a mechanism to input arbitrary characters
|
|
from it. It goes as follow:
|
|
|
|
* Direction pad select characters. Up/Down move by one,
|
|
Left/Right move by 5
|
|
* Start acts like Return
|
|
* A acts like Backspace
|
|
* B changes "character class": lowercase, uppercase, numbers,
|
|
special chars. The space character is the first among special
|
|
chars.
|
|
* C confirms letter selection
|
|
|
|
(cont.)
|
|
( ----- 611 )
|
|
This module is currently hard-wired to VDP driver, that is, it
|
|
calls vdp's routines during (key) to update character
|
|
selection.
|
|
|
|
Load range: 632-637
|
|
( ----- 612 )
|
|
: _prevstat [ PAD_MEM LITN ] ;
|
|
: _sel [ PAD_MEM 1+ LITN ] ;
|
|
: _next [ PAD_MEM 2+ LITN ] ;
|
|
|
|
( Put status for port A in register A. Bits, from MSB to LSB:
|
|
Start - A - C - B - Right - Left - Down - Up
|
|
Each bit is high when button is unpressed and low if button is
|
|
pressed. When no button is pressed, 0xff is returned.
|
|
This logic below is for the Genesis controller, which is modal.
|
|
TH is an output pin that switches the meaning of TL and TR. When
|
|
TH is high (unselected), TL = Button B and TR = Button C. When
|
|
TH is low (selected), TL = Button A and TR = Start. )
|
|
( ----- 613 )
|
|
: _status
|
|
1 _THA! ( output, high/unselected )
|
|
_D1@ 0x3f AND ( low 6 bits are good )
|
|
( Start and A are returned when TH is selected, in bits 5 and
|
|
4. Well get them, left-shift them and integrate them to B. )
|
|
0 _THA! ( output, low/selected )
|
|
_D1@ 0x30 AND 2 LSHIFT OR ;
|
|
( ----- 614 )
|
|
: _chk ( c --, check _sel range )
|
|
_sel C@ DUP 0x7f > IF 0x20 _sel C! THEN
|
|
0x20 < IF 0x7f _sel C! THEN ;
|
|
CREATE _ '0' C, ':' C, 'A' C, '[' C, 'a' C, 0xff C,
|
|
: _nxtcls
|
|
_sel @ _ BEGIN ( c a ) C@+ 2 PICK > UNTIL ( c a )
|
|
1- C@ NIP _sel !
|
|
;
|
|
( ----- 615 )
|
|
: _updsel ( -- f, has an action button been pressed? )
|
|
_status _prevstat C@ OVER = IF DROP 0 EXIT THEN
|
|
DUP _prevstat C! ( changed, update ) ( s )
|
|
0x01 ( UP ) OVER AND NOT IF 1 _sel +! THEN
|
|
0x02 ( DOWN ) OVER AND NOT IF -1 _sel +! THEN
|
|
0x04 ( LEFT ) OVER AND NOT IF -5 _sel +! THEN
|
|
0x08 ( RIGHT ) OVER AND NOT IF 5 _sel +! THEN
|
|
0x10 ( BUTB ) OVER AND NOT IF _nxtcls THEN
|
|
( update sel in VDP )
|
|
_chk _sel C@ (emit) -1 XYPOS +!
|
|
( return whether any of the high 3 bits is low )
|
|
0xe0 AND 0xe0 <
|
|
;
|
|
( ----- 616 )
|
|
: (key)
|
|
_next C@ IF _next C@ 0 _next C! EXIT THEN
|
|
BEGIN _updsel UNTIL
|
|
_prevstat C@
|
|
0x20 ( BUTC ) OVER AND NOT IF DROP _sel C@ EXIT THEN
|
|
0x40 ( BUTA ) AND NOT IF 0x8 ( BS ) EXIT THEN
|
|
( If not BUTC or BUTA, it has to be START )
|
|
0xd _next C! _sel C@
|
|
;
|
|
( ----- 617 )
|
|
: PAD$
|
|
0xff _prevstat C! 'a' _sel C! 0 _next C! ;
|
|
( ----- 620 )
|
|
( kbd - implement (ps2kc) for SMS PS/2 adapter )
|
|
: (ps2kcA) ( for port A )
|
|
( Before reading a character, we must first verify that there
|
|
is something to read. When the adapter is finished filling its
|
|
'164 up, it resets the latch, which output's is connected to
|
|
TL. When the '164 is full, TL is low. Port A TL is bit 4 )
|
|
_D1@ 0x10 AND IF 0 EXIT ( nothing ) THEN
|
|
0 _THA! ( Port A TH output, low )
|
|
_D1@ ( bit 3:0 go in 3:0 ) 0x0f AND ( n )
|
|
1 _THA! ( Port A TH output, high )
|
|
_D1@ ( bit 3:0 go in 7:4 ) 0x0f AND 4 LSHIFT OR ( n )
|
|
2 _THA! ( TH input ) ;
|
|
( ----- 621 )
|
|
: (ps2kcB) ( for port B )
|
|
( Port B TL is bit 2 )
|
|
_D2@ 0x04 AND IF 0 EXIT ( nothing ) THEN
|
|
0 _THB! ( Port B TH output, low )
|
|
_D1@ ( bit 7:6 go in 1:0 ) 6 RSHIFT ( n )
|
|
_D2@ ( bit 1:0 go in 3:2 ) 0x03 AND 2 LSHIFT OR ( n )
|
|
1 _THB! ( Port B TH output, high )
|
|
_D1@ ( bit 7:6 go in 5:4 ) 0xc0 AND 2 RSHIFT OR ( n )
|
|
_D2@ ( bit 1:0 go in 7:6 ) 0x03 AND 6 LSHIFT OR ( n )
|
|
2 _THB! ( TH input ) ;
|
|
( ----- 622 )
|
|
: (spie) DROP ; ( always enabled )
|
|
: (spix) ( x -- x, for port B )
|
|
0 SWAP ( rx tx ) 8 0 DO
|
|
( send current bit to TRB, TR's output bit )
|
|
DUP 7 I - RSHIFT 1 AND _TRB!
|
|
1 _THB! ( CLK hi )
|
|
( read into rx ) SWAP 1 LSHIFT _D1@ ( tx rx<< x )
|
|
0 _THB! ( CLK lo )
|
|
( out bit is the 6th ) 6 RSHIFT 1 AND OR
|
|
SWAP LOOP ( rx tx ) DROP ;
|
|
( ----- 625 )
|
|
( Routines for interacting with SMS controller ports.
|
|
Requires CPORT_MEM, CPORT_CTL, CPORT_D1 and CPORT_D2 to be
|
|
defined. CPORT_MEM is a 1 byte buffer for CPORT_CTL. The last
|
|
3 consts will usually be 0x3f, 0xdc, 0xdd. )
|
|
( mode -- set TR pin on mode a on:
|
|
0= output low 1=output high 2=input )
|
|
CODE _TRA! HL POP, chkPS, ( B0 -> B4, B1 -> B0 )
|
|
L RR, RLA, RLA, RLA, RLA, L RR, RLA,
|
|
0x11 ANDi, L A LDrr, CPORT_MEM LDA(i),
|
|
0xee ANDi, L ORr, CPORT_CTL OUTiA, CPORT_MEM LD(i)A,
|
|
;CODE
|
|
CODE _THA! HL POP, chkPS, ( B0 -> B5, B1 -> B1 )
|
|
L RR, RLA, RLA, RLA, RLA, L RR, RLA, RLA,
|
|
0x22 ANDi, L A LDrr, CPORT_MEM LDA(i),
|
|
0xdd ANDi, L ORr, CPORT_CTL OUTiA, CPORT_MEM LD(i)A,
|
|
;CODE
|
|
( ----- 626 )
|
|
CODE _TRB! HL POP, chkPS, ( B0 -> B6, B1 -> B2 )
|
|
L RR, RLA, RLA, RLA, RLA, L RR, RLA, RLA, RLA,
|
|
0x44 ANDi, L A LDrr, CPORT_MEM LDA(i),
|
|
0xbb ANDi, L ORr, CPORT_CTL OUTiA, CPORT_MEM LD(i)A,
|
|
;CODE
|
|
CODE _THB! HL POP, chkPS, ( B0 -> B7, B1 -> B3 )
|
|
L RR, RLA, RLA, RLA, RLA, L RR, RLA, RLA, RLA, RLA,
|
|
0x88 ANDi, L A LDrr, CPORT_MEM LDA(i),
|
|
0x77 ANDi, L ORr, CPORT_CTL OUTiA, CPORT_MEM LD(i)A,
|
|
;CODE
|
|
CODE _D1@ CPORT_D1 INAi, PUSHA, ;CODE
|
|
CODE _D2@ CPORT_D2 INAi, PUSHA, ;CODE
|