sms: add support for VDP's text mode

Because that mode behaves exactly like in a regular TMS9918, a new
driver for TMS9918 has been added in blkfs and SMS' VDP now uses it.

Also, fix broken 5x7 font.
This commit is contained in:
Virgil Dupras 2020-11-13 12:15:20 -05:00
parent 09c01c4a43
commit d1718a90c7
21 changed files with 193 additions and 99 deletions

View File

@ -1,16 +1,11 @@
VDP Driver
Implement (emit) on the console. Characters start at the top
left. Every (emit) call converts the ASCII char received to its
internal font, then put that char on screen, advancing the
cursor by one. When reaching the end of the line (33rd char),
wrap to the next.
In the future, there's going to be a scrolling mechanism when
we reach the bottom of the screen, but for now, when the end of
the screen is reached, we wrap up to the top.
When reaching a new line, we clear that line and the next to
help readability.
Load range: 623-628
( 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?) )

View File

@ -1,9 +1,14 @@
CODE _ctl ( a -- sends LSB then MSB )
HL POP, chkPS,
A L LDrr, VDP_CTLPORT OUTiA,
A H LDrr, VDP_CTLPORT OUTiA,
;CODE
CODE _data
HL POP, chkPS,
A L LDrr, VDP_DATAPORT OUTiA,
;CODE
: _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! ( tilenum pos )
2 * 0x7800 OR _ctl ( tilenum )
0x5e MOD _data 1 _zero ;

View File

@ -1,9 +1,11 @@
CODE _blank ( this is way too slow in Forth )
A XORr, VDP_CTLPORT OUTiA,
A 0x40 LDri, VDP_CTLPORT OUTiA,
HL 0x4000 LDdi,
BEGIN,
A XORr, VDP_DATAPORT OUTiA,
HL DECd, HLZ,
JRNZ, AGAIN,
;CODE
: 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 ;

View File

@ -1,10 +0,0 @@
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?) )

View File

@ -1,12 +0,0 @@
: _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 ;

View File

@ -1,3 +0,0 @@
: CELL! ( tilenum pos )
2 * 0x7800 OR _ctl ( tilenum )
0x5e MOD _data 1 _zero ;

View File

@ -1,11 +0,0 @@
: 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 ;

View File

@ -4,8 +4,8 @@
0xddca CONSTANT PS_ADDR
RS_ADDR 0x80 - CONSTANT SYSVARS
0xc000 CONSTANT HERESTART
0xbf CONSTANT VDP_CTLPORT
0xbe CONSTANT VDP_DATAPORT
0xbf CONSTANT TMS_CTLPORT
0xbe CONSTANT TMS_DATAPORT
SYSVARS 0x70 + CONSTANT GRID_MEM
SYSVARS 0x72 + CONSTANT CPORT_MEM
0x3f CONSTANT CPORT_CTL
@ -27,7 +27,8 @@ CURRENT @ XCURRENT !
283 335 LOADR ( boot.z80 )
353 LOAD ( xcomp core low )
CREATE ~FNT CPFNT7x7
603 608 LOADR ( VDP )
470 472 LOADR ( TMS9918 )
602 604 LOADR ( VDP )
402 404 LOADR ( Grid )
625 626 LOADR ( SMS ports )
612 617 LOADR ( PAD )

View File

@ -5,8 +5,8 @@
0xddca CONSTANT PS_ADDR
RS_ADDR 0x80 - CONSTANT SYSVARS
0xc000 CONSTANT HERESTART
0xbf CONSTANT VDP_CTLPORT
0xbe CONSTANT VDP_DATAPORT
0xbf CONSTANT TMS_CTLPORT
0xbe CONSTANT TMS_DATAPORT
SYSVARS 0x70 + CONSTANT GRID_MEM
SYSVARS 0x72 + CONSTANT CPORT_MEM
0x3f CONSTANT CPORT_CTL
@ -28,7 +28,8 @@ CURRENT @ XCURRENT !
283 335 LOADR ( boot.z80 )
353 LOAD ( xcomp core low )
CREATE ~FNT CPFNT7x7
603 608 LOADR ( VDP )
470 472 LOADR ( TMS9918 )
602 604 LOADR ( VDP )
402 404 LOADR ( Grid )
625 626 LOADR ( SMS ports )
620 LOAD ( PAD ) : (ps2kc) (ps2kcA) ; 411 414 LOADR

View File

@ -6,8 +6,8 @@
0xddca CONSTANT PS_ADDR
RS_ADDR 0x80 - CONSTANT SYSVARS
0xc000 CONSTANT HERESTART
0xbf CONSTANT VDP_CTLPORT
0xbe CONSTANT VDP_DATAPORT
0xbf CONSTANT TMS_CTLPORT
0xbe CONSTANT TMS_DATAPORT
SYSVARS 0x70 + CONSTANT GRID_MEM
SYSVARS 0x72 + CONSTANT CPORT_MEM
0x3f CONSTANT CPORT_CTL
@ -29,7 +29,8 @@ CURRENT @ XCURRENT !
283 335 LOADR ( boot.z80 )
353 LOAD ( xcomp core low )
CREATE ~FNT CPFNT7x7
603 608 LOADR ( VDP )
470 472 LOADR ( TMS9918 )
602 604 LOADR ( VDP )
402 404 LOADR ( Grid )
625 626 LOADR ( SMS ports )
620 LOAD ( PAD ) : (ps2kc) (ps2kcA) ; 411 414 LOADR

View File

@ -0,0 +1,42 @@
( xcomp using the Text Mode if the VDP. Only works on actual
SMS. The Megadrive's VDP doesn't have TMS9918 modes in it. )
( 8K of onboard RAM )
0xdd00 CONSTANT RS_ADDR
( Memory register at the end of RAM. Must not overwrite )
0xddca CONSTANT PS_ADDR
RS_ADDR 0x80 - CONSTANT SYSVARS
0xc000 CONSTANT HERESTART
0xbf CONSTANT TMS_CTLPORT
0xbe CONSTANT TMS_DATAPORT
SYSVARS 0x70 + CONSTANT GRID_MEM
SYSVARS 0x72 + CONSTANT CPORT_MEM
0x3f CONSTANT CPORT_CTL
0xdc CONSTANT CPORT_D1
0xdd CONSTANT CPORT_D2
SYSVARS 0x73 + CONSTANT PS2_MEM
5 LOAD ( z80 assembler )
: ZFILL, ( u ) 0 DO 0 A, LOOP ;
262 LOAD ( xcomp )
523 LOAD ( font compiler )
282 LOAD ( boot.z80.decl )
270 LOAD ( xcomp overrides )
DI, 0x100 JP, 0x62 ZFILL, ( 0x66 )
RETN, 0x98 ZFILL, ( 0x100 )
( All set, carry on! )
CURRENT @ XCURRENT !
0x100 BIN( !
283 335 LOADR ( boot.z80 )
353 LOAD ( xcomp core low )
CREATE ~FNT CPFNT5x7
470 472 LOADR ( VDP )
402 404 LOADR ( Grid )
625 626 LOADR ( SMS ports )
620 LOAD ( PAD ) : (ps2kc) (ps2kcA) ; 411 414 LOADR
390 LOAD ( xcomp core high )
(entry) _
( Update LATEST )
PC ORG @ 8 + !
," TMS$ 0 0 AT-XY PS2$ (im1) " EOT,
ORG @ 0x100 - 256 /MOD 2 PC! 2 PC!
H@ 256 /MOD 2 PC! 2 PC!

View File

@ -9,4 +9,5 @@ MASTER INDEX
400 AT28 EEPROM driver 401 Grid subsystem
410 PS/2 keyboard subsystem 418 Z80 SPI Relay driver
420 SD Card subsystem 440 8086 boot code
470-519 unused 520 Fonts
470 Z80 TMS9918 driver
480-519 unused 520 Fonts

12
blk/470 Normal file
View File

@ -0,0 +1,12 @@
( Z80 driver for TMS9918. Implements grid protocol. Requires
TMS_CTLPORT, TMS_DATAPORT and ~FNT from the Font compiler at
B520. Load range B470-472 )
CODE _ctl ( a -- sends LSB then MSB )
HL POP, chkPS,
A L LDrr, TMS_CTLPORT OUTiA,
A H LDrr, TMS_CTLPORT OUTiA,
;CODE
CODE _data
HL POP, chkPS,
A L LDrr, TMS_DATAPORT OUTiA,
;CODE

9
blk/471 Normal file
View File

@ -0,0 +1,9 @@
CODE _blank ( this is way too slow in Forth )
A XORr, TMS_CTLPORT OUTiA,
A 0x40 LDri, TMS_CTLPORT OUTiA,
HL 0x4000 LDdi,
BEGIN,
A XORr, TMS_DATAPORT OUTiA,
HL DECd, HLZ,
JRNZ, AGAIN,
;CODE

16
blk/472 Normal file
View File

@ -0,0 +1,16 @@
( 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. )
: _sfont ( a -- Send font to TMS )
7 0 DO C@+ _data LOOP DROP
( blank row ) 0 _data ;
: CELL! ( tilenum pos )
0x7800 OR _ctl ( tilenum )
0x5e MOD _data ;
: COLS 40 ; : LINES 24 ;
: TMS$
0x8100 _ctl ( blank screen ) _blank
0x4000 _ctl 0x5e 0 DO ~FNT I 7 * + _sfont LOOP
0x820e _ctl ( name table 0x3800 )
0x8400 _ctl ( patter table 0x0000 )
0x87f0 _ctl ( colors 0 and 1 )
0x8000 _ctl 0x81d0 _ctl ( text mode, display on ) ;

View File

@ -9,7 +9,7 @@
DUP I 5 * + _g
LOOP ;
: CPFNT5x7
0 , 0 , 0 C, ( space char )
534 532 DO I BLK@ BLK( 12 _l 448 + 12 _l DROP LOOP ( 72 )
0 , 0 , 0 , 0 C, ( space char )
535 532 DO I BLK@ BLK( 12 _l 448 + 12 _l DROP LOOP ( 72 )
535 BLK@ BLK( 12 _l 448 + 10 _l DROP ( 94! )
;

View File

@ -34,7 +34,7 @@ static xcb_gcontext_t fg;
static xcb_drawable_t win;
// pixels to draw. We draw them in one shot.
static xcb_rectangle_t rectangles[VDP_SCREENW*VDP_SCREENH];
static xcb_rectangle_t rectangles[(32*8)*(24*8)];
static Machine *m;
static VDP vdp;
@ -144,19 +144,19 @@ void draw_pixels()
xcb_clear_area(
conn, 0, win, 0, 0, geom->width, geom->height);
// Figure out inner size to maximize our screen's aspect ratio
int psize = geom->height / VDP_SCREENH;
if (geom->width / VDP_SCREENW < psize) {
int psize = geom->height / vdp.tms.height;
if (geom->width / vdp.tms.width < psize) {
// width is the constraint
psize = geom->width / VDP_SCREENW;
psize = geom->width / vdp.tms.width;
}
int innerw = psize * VDP_SCREENW;
int innerh = psize * VDP_SCREENH;
int innerw = psize * vdp.tms.width;
int innerh = psize * vdp.tms.height;
int innerx = (geom->width - innerw) / 2;
int innery = (geom->height - innerh) / 2;
free(geom);
int drawcnt = 0;
for (int i=0; i<VDP_SCREENW; i++) {
for (int j=0; j<VDP_SCREENH; j++) {
for (int i=0; i<vdp.tms.width; i++) {
for (int j=0; j<vdp.tms.height; j++) {
if (vdp_pixel(&vdp, i, j)) {
int x = innerx + (i*psize);
int y = innery + (j*psize);

View File

@ -1,6 +1,11 @@
#include <string.h>
#include "sms_vdp.h"
static bool _is_mode4(VDP *vdp)
{
return (vdp->tms.regs[0]&0x4);
}
void vdp_init(VDP *vdp)
{
tms_init(&vdp->tms);
@ -18,6 +23,7 @@ void vdp_cmd_wr(VDP *vdp, uint8_t val)
vdp->tms.curaddr = TMS_VRAM_SIZE + (vdp->tms.cmdlsb&0x1f);
} else {
tms_cmd_wr(&vdp->tms, val);
vdp->tms.width = _is_mode4(vdp) ? 32*8 : 40*6;
}
}
}
@ -46,13 +52,16 @@ void vdp_data_wr(VDP *vdp, uint8_t val)
uint8_t vdp_pixel(VDP *vdp, uint16_t x, uint16_t y)
{
if (x >= VDP_SCREENW) {
return 0;
}
if (y >= VDP_SCREENH) {
return 0;
}
TMS9918 *tms = &vdp->tms;
if (!_is_mode4(vdp)) {
return tms_pixel(tms, x, y);
}
if (x >= tms->width) {
return 0;
}
if (y >= tms->height) {
return 0;
}
// name table offset
uint16_t offset = (tms->regs[2] & 0xe) << 10;
offset += ((y/8) << 6) + ((x/8) << 1);

View File

@ -1,8 +1,6 @@
#include "tms9918.h"
#define VDP_CRAM_SIZE 0x20
#define VDP_SCREENW (32*8)
#define VDP_SCREENH (24*8)
typedef struct {
TMS9918 tms;

View File

@ -1,12 +1,34 @@
#include <stdio.h>
#include <string.h>
#include "tms9918.h"
static uint8_t COLORS[0x10] = { // TODO: put actual color codes
0, // transparent
0, // black
1, // medium green
1, // light green
1, // dark blue
1, // light blue
1, // dark red
1, // cyan
1, // medium red
1, // light red
1, // dark yellow
1, // light yellow
1, // dark green
1, // magenta
1, // gray
1, // white
};
void tms_init(TMS9918 *tms)
{
memset(tms->vram, 0, TMS_VRAM_SIZE);
memset(tms->regs, 0, 0x10);
tms->has_cmdlsb = false;
tms->curaddr = 0;
tms->width = 40*6;
tms->height = 24*8;
}
uint8_t tms_cmd_rd(TMS9918 *tms)
@ -50,5 +72,19 @@ void tms_data_wr(TMS9918 *tms, uint8_t val)
// Returns a 8-bit RGB value (0b00bbggrr)
uint8_t tms_pixel(TMS9918 *tms, uint16_t x, uint16_t y)
{
return 0; // no TMS9918 mode implemented yet
if ((tms->regs[1]&0x18) == 0x10 && (tms->regs[0]&0x40) == 0) {
// Text mode
uint16_t nameoff = (tms->regs[2] & 0xf) << 10;
uint16_t patternoff = (tms->regs[4] & 0x7) << 11;
uint8_t nameid = tms->vram[nameoff+(((y/8) * 40) + (x/6))];
uint8_t patternline = tms->vram[patternoff+(nameid*8)+(y%8)];
uint8_t color = tms->regs[7];
if ((patternline>>(8-(x%6)))&1) {
color >>= 4;
}
color &= 0xf;
return color;
} else { // unsupported mode
return 0;
}
}

View File

@ -13,6 +13,8 @@ typedef struct {
uint8_t cmdlsb;
bool has_cmdlsb;
uint16_t curaddr;
uint16_t width; // in pixels
uint16_t height; // in pixels
} TMS9918;
void tms_init(TMS9918 *tms);