/*
** File: tms9928a.c -- software implementation of the Texas Instruments
** TMS9918(A), TMS9928(A) and TMS9929(A), used by the Coleco, MSX and
** TI99/4(A).
**
** All undocumented features as described in the following file
** should be emulated.
**
** http://www.msxnet.org/tech/tms9918a.txt
**
** By Sean Young 1999 (sean@msxnet.org).
** Based on code by Mike Balfour. Features added:
** - read-ahead
** - single read/write address
** - AND mask for mode 2
** - multicolor mode
** - undocumented screen modes
** - illegal sprites (max 4 on one line)
** - vertical coordinate corrected -- was one to high (255 => 0, 0 => 1)
** - errors in interrupt emulation
** - back drop correctly emulated.
**
** 19 feb 2000, Sean:
** - now uses plot _pixel (..), so -ror works properly
** - fixed bug in tms.patternmask
**
** 3 nov 2000, Raphael Nabet:
** - fixed a nasty bug in draw_sprites. A transparent sprite caused
** sprites at lower levels not to be displayed, which is wrong.
**
** 3 jan 2001, Sean Young:
** - A few minor cleanups
** - Changed TMS9928A_vram_[rw] and TMS9928A_register_[rw] to READ8_HANDLER
** and WRITE8_HANDLER.
** - Got rid of the color table, unused. Also got rid of the old colors,
** which were commented out anyway.
**
**
** Todo:
** - The screen image is rendered in `one go'. Modifications during
** screen build up are not shown.
** - Correctly emulate 4,8,16 kb VRAM if needed.
** - uses plot _pixel (...) in TMS_sprites (...), which is rended in
** in a back buffer created with malloc (). Hmm..
** - Colours are incorrect. [fixed by R Nabet ?]
*/
#include "driver.h"
#include "tms9928a.h"
/*
New palette (R. Nabet).
First 3 columns from TI datasheet (in volts).
Next 3 columns based on formula :
Y = .299*R + .587*G + .114*B (NTSC)
(the coefficients are likely to be slightly different with PAL, but who cares ?)
I assumed the "zero" for R-Y and B-Y was 0.47V.
Last 3 coeffs are the 8-bit values.
Color Y R-Y B-Y R G B R G B
0 Transparent
1 Black 0.00 0.47 0.47 0.00 0.00 0.00 0 0 0
2 Medium green 0.53 0.07 0.20 0.13 0.79 0.26 33 200 66
3 Light green 0.67 0.17 0.27 0.37 0.86 0.47 94 220 120
4 Dark blue 0.40 0.40 1.00 0.33 0.33 0.93 84 85 237
5 Light blue 0.53 0.43 0.93 0.49 0.46 0.99 125 118 252
6 Dark red 0.47 0.83 0.30 0.83 0.32 0.30 212 82 77
7 Cyan 0.73 0.00 0.70 0.26 0.92 0.96 66 235 245
8 Medium red 0.53 0.93 0.27 0.99 0.33 0.33 252 85 84
9 Light red 0.67 0.93 0.27 1.13(!) 0.47 0.47 255 121 120
A Dark yellow 0.73 0.57 0.07 0.83 0.76 0.33 212 193 84
B Light yellow 0.80 0.57 0.17 0.90 0.81 0.50 230 206 128
C Dark green 0.47 0.13 0.23 0.13 0.69 0.23 33 176 59
D Magenta 0.53 0.73 0.67 0.79 0.36 0.73 201 91 186
E Gray 0.80 0.47 0.47 0.80 0.80 0.80 204 204 204
F White 1.00 0.47 0.47 1.00 1.00 1.00 255 255 255
*/
static const rgb_t TMS9928A_palette[16] =
{
RGB_BLACK,
RGB_BLACK,
MAKE_RGB(33, 200, 66),
MAKE_RGB(94, 220, 120),
MAKE_RGB(84, 85, 237),
MAKE_RGB(125, 118, 252),
MAKE_RGB(212, 82, 77),
MAKE_RGB(66, 235, 245),
MAKE_RGB(252, 85, 84),
MAKE_RGB(255, 121, 120),
MAKE_RGB(212, 193, 84),
MAKE_RGB(230, 206, 128),
MAKE_RGB(33, 176, 59),
MAKE_RGB(201, 91, 186),
MAKE_RGB(204, 204, 204),
RGB_WHITE
};
/*
** Forward declarations of internal functions.
*/
static void draw_mode0 (running_machine *machine, mame_bitmap *bitmap, const rectangle *cliprect);
static void draw_mode1 (running_machine *machine, mame_bitmap *bitmap, const rectangle *cliprect);
static void draw_mode2 (running_machine *machine, mame_bitmap *bitmap, const rectangle *cliprect);
static void draw_mode12 (running_machine *machine, mame_bitmap *bitmap, const rectangle *cliprect);
static void draw_mode3 (running_machine *machine, mame_bitmap *bitmap, const rectangle *cliprect);
static void draw_mode23 (running_machine *machine, mame_bitmap *bitmap, const rectangle *cliprect);
static void draw_modebogus (running_machine *machine, mame_bitmap *bitmap, const rectangle *cliprect);
static void draw_sprites (running_machine *machine, mame_bitmap *bitmap, const rectangle *cliprect);
static void change_register (int reg, UINT8 data);
static void (*const ModeHandlers[])(running_machine *machine, mame_bitmap *bitmap, const rectangle *cliprect) = {
draw_mode0, draw_mode1, draw_mode2, draw_mode12,
draw_mode3, draw_modebogus, draw_mode23,
draw_modebogus };
#define IMAGE_SIZE (256*192) /* size of rendered image */
#define LEFT_BORDER 15 /* a bit less for 9918a??? */
#define RIGHT_BORDER 15 /* 13 for 9929a */
#define TOP_BORDER_60HZ 27
#define BOTTOM_BORDER_60HZ 24
#define TOP_BORDER_50HZ 51 /* unknown (102 for top+bottom?) */
#define BOTTOM_BORDER_50HZ 51 /* unknown (102 for top+bottom?) */
#define TOP_BORDER tms.top_border
#define BOTTOM_BORDER tms.bottom_border
#define TMS_SPRITES_ENABLED ((tms.Regs[1] & 0x50) == 0x40)
#define TMS_50HZ ((tms.model == TMS9929) || (tms.model == TMS9929A))
#define TMS_REVA ((tms.model == TMS99x8A) || (tms.model == TMS9929A))
#define TMS_MODE ( (TMS_REVA ? (tms.Regs[0] & 2) : 0) | \
((tms.Regs[1] & 0x10)>>4) | ((tms.Regs[1] & 8)>>1))
typedef struct {
/* TMS9928A internal settings */
UINT8 ReadAhead,Regs[8],StatusReg,FirstByte,latch,INT;
INT32 Addr;
int colour,pattern,nametbl,spriteattribute,spritepattern;
int colourmask,patternmask;
void (*INTCallback)(int);
/* memory */
UINT8 *vMem, *dBackMem;
mame_bitmap *tmpbmp;
int vramsize, model;
/* emulation settings */
int LimitSprites; /* max 4 sprites on a row, like original TMS9918A */
int top_border, bottom_border;
rectangle visarea;
} TMS9928A;
static TMS9928A tms;
/*
** initialize the palette
*/
static PALETTE_INIT( tms9928a )
{
palette_set_colors(machine, 0, TMS9928A_palette, TMS9928A_PALETTE_SIZE);
}
/*
** The init, reset and shutdown functions
*/
void TMS9928A_reset () {
int i;
for (i=0;i<8;i++) tms.Regs[i] = 0;
tms.StatusReg = 0;
tms.nametbl = tms.pattern = tms.colour = 0;
tms.spritepattern = tms.spriteattribute = 0;
tms.colourmask = tms.patternmask = 0;
tms.Addr = tms.ReadAhead = tms.INT = 0;
tms.FirstByte = 0;
tms.latch = 0;
}
static void TMS9928A_start (running_machine *machine, const TMS9928a_interface *intf)
{
assert_always(((intf->vram == 0x1000) || (intf->vram == 0x2000) || (intf->vram == 0x4000)), "4, 8 or 16 kB vram please");
tms.model = intf->model;
tms.top_border = TMS_50HZ ? TOP_BORDER_50HZ : TOP_BORDER_60HZ;
tms.bottom_border = TMS_50HZ ? BOTTOM_BORDER_50HZ : BOTTOM_BORDER_60HZ;
tms.INTCallback = intf->int_callback;
/* determine the visible area */
tms.visarea.min_x = LEFT_BORDER - MIN(intf->borderx, LEFT_BORDER);
tms.visarea.max_x = LEFT_BORDER + 32*8 - 1 + MIN(intf->borderx, RIGHT_BORDER);
tms.visarea.min_y = tms.top_border - MIN(intf->bordery, tms.top_border);
tms.visarea.max_y = tms.top_border + 24*8 - 1 + MIN(intf->bordery, tms.bottom_border);
/* configure the screen if we weren't overridden */
if (machine->screen[0].width == LEFT_BORDER+32*8+RIGHT_BORDER && machine->screen[0].height == TOP_BORDER_60HZ+24*8+BOTTOM_BORDER_60HZ)
video_screen_configure(0, LEFT_BORDER + 32*8 + RIGHT_BORDER, tms.top_border + 24*8 + tms.bottom_border, &tms.visarea, machine->screen[0].refresh);
/* Video RAM */
tms.vramsize = intf->vram;
tms.vMem = (UINT8*) auto_malloc (intf->vram);
memset (tms.vMem, 0, intf->vram);
/* Sprite back buffer */
tms.dBackMem = (UINT8*)auto_malloc (IMAGE_SIZE);
/* back bitmap */
tms.tmpbmp = auto_bitmap_alloc (256, 192, machine->screen[0].format);
TMS9928A_reset ();
tms.LimitSprites = 1;
state_save_register_item("tms9928a", 0, tms.Regs[0]);
state_save_register_item("tms9928a", 0, tms.Regs[1]);
state_save_register_item("tms9928a", 0, tms.Regs[2]);
state_save_register_item("tms9928a", 0, tms.Regs[3]);
state_save_register_item("tms9928a", 0, tms.Regs[4]);
state_save_register_item("tms9928a", 0, tms.Regs[5]);
state_save_register_item("tms9928a", 0, tms.Regs[6]);
state_save_register_item("tms9928a", 0, tms.Regs[7]);
state_save_register_item("tms9928a", 0, tms.StatusReg);
state_save_register_item("tms9928a", 0, tms.ReadAhead);
state_save_register_item("tms9928a", 0, tms.FirstByte);
state_save_register_item("tms9928a", 0, tms.latch);
state_save_register_item("tms9928a", 0, tms.Addr);
state_save_register_item("tms9928a", 0, tms.INT);
state_save_register_item_pointer("tms9928a", 0, tms.vMem, intf->vram);
}
const rectangle *TMS9928A_get_visarea (void)
{
return &tms.visarea;
}
void TMS9928A_post_load (void) {
int i;
/* all registers need to be re-written, so tables are recalculated */
for (i=0;i<8;i++)
change_register (i, tms.Regs[i]);
/* make sure the interrupt request is set properly */
if (tms.INTCallback) tms.INTCallback (tms.INT);
}
/*
** The I/O functions.
*/
READ8_HANDLER (TMS9928A_vram_r) {
UINT8 b;
b = tms.ReadAhead;
tms.ReadAhead = tms.vMem[tms.Addr];
tms.Addr = (tms.Addr + 1) & (tms.vramsize - 1);
tms.latch = 0;
return b;
}
WRITE8_HANDLER (TMS9928A_vram_w) {
tms.vMem[tms.Addr] = data;
tms.Addr = (tms.Addr + 1) & (tms.vramsize - 1);
tms.ReadAhead = data;
tms.latch = 0;
}
READ8_HANDLER (TMS9928A_register_r) {
UINT8 b;
b = tms.StatusReg;
tms.StatusReg = 0x1f;
if (tms.INT) {
tms.INT = 0;
if (tms.INTCallback) tms.INTCallback (tms.INT);
}
tms.latch = 0;
return b;
}
WRITE8_HANDLER (TMS9928A_register_w) {
int reg;
if (tms.latch) {
if (data & 0x80) {
/* register write */
reg = data & 7;
/*if (tms.FirstByte != tms.Regs[reg])*/ /* Removed to fix ColecoVision MESS Driver*/
change_register (reg, tms.FirstByte);
} else {
/* set read/write address */
tms.Addr = ((UINT16)data << 8 | tms.FirstByte) & (tms.vramsize - 1);
if ( !(data & 0x40) ) {
/* read ahead */
TMS9928A_vram_r (0);
}
}
tms.latch = 0;
} else {
tms.FirstByte = data;
tms.latch = 1;
}
}
static void change_register (int reg, UINT8 val) {
static const UINT8 Mask[8] =
{ 0x03, 0xfb, 0x0f, 0xff, 0x07, 0x7f, 0x07, 0xff };
static const char *const modes[] = {
"Mode 0 (GRAPHIC 1)", "Mode 1 (TEXT 1)", "Mode 2 (GRAPHIC 2)",
"Mode 1+2 (TEXT 1 variation)", "Mode 3 (MULTICOLOR)",
"Mode 1+3 (BOGUS)", "Mode 2+3 (MULTICOLOR variation)",
"Mode 1+2+3 (BOGUS)" };
UINT8 b;
val &= Mask[reg];
tms.Regs[reg] = val;
logerror("TMS9928A: Reg %d = %02xh\n", reg, (int)val);
switch (reg) {
case 0:
/* re-calculate masks and pattern generator & colour */
if (val & 2) {
tms.colour = ((tms.Regs[3] & 0x80) * 64) & (tms.vramsize - 1);
tms.colourmask = (tms.Regs[3] & 0x7f) * 8 | 7;
tms.pattern = ((tms.Regs[4] & 4) * 2048) & (tms.vramsize - 1);
tms.patternmask = (tms.Regs[4] & 3) * 256 |
(tms.colourmask & 255);
} else {
tms.colour = (tms.Regs[3] * 64) & (tms.vramsize - 1);
tms.pattern = (tms.Regs[4] * 2048) & (tms.vramsize - 1);
}
logerror("TMS9928A: %s\n", modes[TMS_MODE]);
break;
case 1:
/* check for changes in the INT line */
b = (val & 0x20) && (tms.StatusReg & 0x80) ;
if (b != tms.INT) {
tms.INT = b;
if (tms.INTCallback) tms.INTCallback (tms.INT);
}
logerror("TMS9928A: %s\n", modes[TMS_MODE]);
break;
case 2:
tms.nametbl = (val * 1024) & (tms.vramsize - 1);
break;
case 3:
if (tms.Regs[0] & 2) {
tms.colour = ((val & 0x80) * 64) & (tms.vramsize - 1);
tms.colourmask = (val & 0x7f) * 8 | 7;
} else {
tms.colour = (val * 64) & (tms.vramsize - 1);
}
tms.patternmask = (tms.Regs[4] & 3) * 256 | (tms.colourmask & 255);
break;
case 4:
if (tms.Regs[0] & 2) {
tms.pattern = ((val & 4) * 2048) & (tms.vramsize - 1);
tms.patternmask = (val & 3) * 256 | 255;
} else {
tms.pattern = (val * 2048) & (tms.vramsize - 1);
}
break;
case 5:
tms.spriteattribute = (val * 128) & (tms.vramsize - 1);
break;
case 6:
tms.spritepattern = (val * 2048) & (tms.vramsize - 1);
break;
case 7:
/* The backdrop is updated at TMS9928A_refresh() */
break;
}
}
/*
** Interface functions
*/
/*void TMS9928A_int_callback (void (*callback)(int)) {
tms.INTCallback = callback;
}*/
void TMS9928A_set_spriteslimit (int limit) {
tms.LimitSprites = limit;
}
/*
** Updates the screen (the dMem memory area).
*/
VIDEO_UPDATE( tms9928a )
{
INT32 BackColour = tms.Regs[7] & 15; if (!BackColour) BackColour=1;
palette_set_color(machine, 0, TMS9928A_palette[BackColour]);
if (! (tms.Regs[1] & 0x40))
fillbitmap(bitmap, machine->pens[BackColour], cliprect);
else
{
(*ModeHandlers[TMS_MODE])(machine, tms.tmpbmp, cliprect);
copybitmap(bitmap, tms.tmpbmp, 0, 0, LEFT_BORDER, TOP_BORDER, cliprect);
{
rectangle rt;
/* set borders */
rt.min_x = 0; rt.max_x = LEFT_BORDER+256+RIGHT_BORDER-1;
rt.min_y = 0; rt.max_y = TOP_BORDER-1;
fillbitmap (bitmap, BackColour, &rt);
rt.min_y = TOP_BORDER+192; rt.max_y = TOP_BORDER+192+BOTTOM_BORDER-1;
fillbitmap (bitmap, BackColour, &rt);
rt.min_y = TOP_BORDER; rt.max_y = TOP_BORDER+192-1;
rt.min_x = 0; rt.max_x = LEFT_BORDER-1;
fillbitmap (bitmap, BackColour, &rt);
rt.min_x = LEFT_BORDER+256; rt.max_x = LEFT_BORDER+256+RIGHT_BORDER-1;
fillbitmap (bitmap, BackColour, &rt);
}
if (TMS_SPRITES_ENABLED)
draw_sprites(machine, bitmap, cliprect);
}
return 0;
}
int TMS9928A_interrupt () {
int b;
/* when skipping frames, calculate sprite collision */
if (video_skip_this_frame() ) {
if (TMS_SPRITES_ENABLED) {
draw_sprites (NULL, NULL, NULL);
}
}
tms.StatusReg |= 0x80;
b = (tms.Regs[1] & 0x20) != 0;
if (b != tms.INT) {
tms.INT = b;
if (tms.INTCallback) tms.INTCallback (tms.INT);
}
return b;
}
static void draw_mode1 (running_machine *machine, mame_bitmap *bitmap, const rectangle *cliprect) {
int pattern,x,y,yy,xx,name,charcode;
UINT8 fg,bg,*patternptr;
rectangle rt;
fg = machine->pens[tms.Regs[7] / 16];
bg = machine->pens[tms.Regs[7] & 15];
/* colours at sides must be reset */
rt.min_y = 0; rt.max_y = 191;
rt.min_x = 0; rt.max_x = 7;
fillbitmap (bitmap, bg, &rt);
rt.min_y = 0; rt.max_y = 191;
rt.min_x = 248; rt.max_x = 255;
fillbitmap (bitmap, bg, &rt);
name = 0;
for (y=0;y<24;y++) {
for (x=0;x<40;x++) {
charcode = tms.vMem[tms.nametbl+name];
name++;
patternptr = tms.vMem + tms.pattern + (charcode*8);
for (yy=0;yy<8;yy++) {
pattern = *patternptr++;
for (xx=0;xx<6;xx++) {
*BITMAP_ADDR16(bitmap, y*8+yy, 8+x*6+xx) = (pattern & 0x80) ? fg : bg;
pattern *= 2;
}
}
}
}
}
static void draw_mode12 (running_machine *machine, mame_bitmap *bitmap, const rectangle *cliprect) {
int pattern,x,y,yy,xx,name,charcode;
UINT8 fg,bg,*patternptr;
rectangle rt;
fg = machine->pens[tms.Regs[7] / 16];
bg = machine->pens[tms.Regs[7] & 15];
/* colours at sides must be reset */
rt.min_y = 0; rt.max_y = 191;
rt.min_x = 0; rt.max_x = 7;
fillbitmap (bitmap, bg, &rt);
rt.min_y = 0; rt.max_y = 191;
rt.min_x = 248; rt.max_x = 255;
fillbitmap (bitmap, bg, &rt);
name = 0;
for (y=0;y<24;y++) {
for (x=0;x<40;x++) {
charcode = (tms.vMem[tms.nametbl+name]+(y/8)*256)&tms.patternmask;
name++;
patternptr = tms.vMem + tms.pattern + (charcode*8);
for (yy=0;yy<8;yy++) {
pattern = *patternptr++;
for (xx=0;xx<6;xx++) {
*BITMAP_ADDR16(bitmap, y*8+yy, 8+x*6+xx) = (pattern & 0x80) ? fg : bg;
pattern *= 2;
}
}
}
}
}
static void draw_mode0 (running_machine *machine, mame_bitmap *bitmap, const rectangle *cliprect) {
int pattern,x,y,yy,xx,name,charcode,colour;
UINT8 fg,bg,*patternptr;
name = 0;
for (y=0;y<24;y++) {
for (x=0;x<32;x++) {
charcode = tms.vMem[tms.nametbl+name];
name++;
patternptr = tms.vMem + tms.pattern + charcode*8;
colour = tms.vMem[tms.colour+charcode/8];
fg = machine->pens[colour / 16];
bg = machine->pens[colour & 15];
for (yy=0;yy<8;yy++) {
pattern=*patternptr++;
for (xx=0;xx<8;xx++) {
*BITMAP_ADDR16(bitmap, y*8+yy, x*8+xx) = (pattern & 0x80) ? fg : bg;
pattern *= 2;
}
}
}
}
}
static void draw_mode2 (running_machine *machine, mame_bitmap *bitmap, const rectangle *cliprect) {
int colour,name,x,y,yy,pattern,xx,charcode;
UINT8 fg,bg;
UINT8 *colourptr,*patternptr;
name = 0;
for (y=0;y<24;y++) {
for (x=0;x<32;x++) {
charcode = tms.vMem[tms.nametbl+name]+(y/8)*256;
name++;
colour = (charcode&tms.colourmask);
pattern = (charcode&tms.patternmask);
patternptr = tms.vMem+tms.pattern+colour*8;
colourptr = tms.vMem+tms.colour+pattern*8;
for (yy=0;yy<8;yy++) {
pattern = *patternptr++;
colour = *colourptr++;
fg = machine->pens[colour / 16];
bg = machine->pens[colour & 15];
for (xx=0;xx<8;xx++) {
*BITMAP_ADDR16(bitmap, y*8+yy, x*8+xx) = (pattern & 0x80) ? fg : bg;
pattern *= 2;
}
}
}
}
}
static void draw_mode3 (running_machine *machine, mame_bitmap *bitmap, const rectangle *cliprect) {
int x,y,yy,yyy,name,charcode;
UINT8 fg,bg,*patternptr;
name = 0;
for (y=0;y<24;y++) {
for (x=0;x<32;x++) {
charcode = tms.vMem[tms.nametbl+name];
name++;
patternptr = tms.vMem+tms.pattern+charcode*8+(y&3)*2;
for (yy=0;yy<2;yy++) {
fg = machine->pens[(*patternptr / 16)];
bg = machine->pens[((*patternptr++) & 15)];
for (yyy=0;yyy<4;yyy++) {
*BITMAP_ADDR16(bitmap, y*8+yy*4+yyy, x*8+0) = fg;
*BITMAP_ADDR16(bitmap, y*8+yy*4+yyy, x*8+1) = fg;
*BITMAP_ADDR16(bitmap, y*8+yy*4+yyy, x*8+2) = fg;
*BITMAP_ADDR16(bitmap, y*8+yy*4+yyy, x*8+3) = fg;
*BITMAP_ADDR16(bitmap, y*8+yy*4+yyy, x*8+4) = fg;
*BITMAP_ADDR16(bitmap, y*8+yy*4+yyy, x*8+5) = fg;
*BITMAP_ADDR16(bitmap, y*8+yy*4+yyy, x*8+6) = fg;
*BITMAP_ADDR16(bitmap, y*8+yy*4+yyy, x*8+7) = fg;
}
}
}
}
}
static void draw_mode23 (running_machine *machine, mame_bitmap *bitmap, const rectangle *cliprect) {
int x,y,yy,yyy,name,charcode;
UINT8 fg,bg,*patternptr;
name = 0;
for (y=0;y<24;y++) {
for (x=0;x<32;x++) {
charcode = tms.vMem[tms.nametbl+name];
name++;
patternptr = tms.vMem + tms.pattern +
((charcode+(y&3)*2+(y/8)*256)&tms.patternmask)*8;
for (yy=0;yy<2;yy++) {
fg = machine->pens[(*patternptr / 16)];
bg = machine->pens[((*patternptr++) & 15)];
for (yyy=0;yyy<4;yyy++) {
*BITMAP_ADDR16(bitmap, y*8+yy*4+yyy, x*8+0) = fg;
*BITMAP_ADDR16(bitmap, y*8+yy*4+yyy, x*8+1) = fg;
*BITMAP_ADDR16(bitmap, y*8+yy*4+yyy, x*8+2) = fg;
*BITMAP_ADDR16(bitmap, y*8+yy*4+yyy, x*8+3) = fg;
*BITMAP_ADDR16(bitmap, y*8+yy*4+yyy, x*8+4) = fg;
*BITMAP_ADDR16(bitmap, y*8+yy*4+yyy, x*8+5) = fg;
*BITMAP_ADDR16(bitmap, y*8+yy*4+yyy, x*8+6) = fg;
*BITMAP_ADDR16(bitmap, y*8+yy*4+yyy, x*8+7) = fg;
}
}
}
}
}
static void draw_modebogus (running_machine *machine, mame_bitmap *bitmap, const rectangle *cliprect) {
UINT8 fg,bg;
int x,y,n,xx;
fg = machine->pens[tms.Regs[7] / 16];
bg = machine->pens[tms.Regs[7] & 15];
for (y=0;y<192;y++) {
xx=0;
n=8; while (n--) *BITMAP_ADDR16(bitmap, y, xx++) = bg;
for (x=0;x<40;x++) {
n=4; while (n--) *BITMAP_ADDR16(bitmap, y, xx++) = fg;
n=2; while (n--) *BITMAP_ADDR16(bitmap, y, xx++) = bg;
}
n=8; while (n--) *BITMAP_ADDR16(bitmap, y, xx++) = bg;
}
}
/*
** This function renders the sprites. Sprite collision is calculated in
** in a back buffer (tms.dBackMem), because sprite collision detection
** is rather complicated (transparent sprites also cause the sprite
** collision bit to be set, and ``illegal'' sprites do not count
** (they're not displayed)).
**
** This code should be optimized. One day.
*/
static void draw_sprites (running_machine *machine, mame_bitmap *bitmap, const rectangle *cliprect) {
UINT8 *attributeptr,*patternptr,c;
int p,x,y,size,i,j,large,yy,xx,limit[192],
illegalsprite,illegalspriteline;
UINT16 line,line2;
attributeptr = tms.vMem + tms.spriteattribute;
size = (tms.Regs[1] & 2) ? 16 : 8;
large = (int)(tms.Regs[1] & 1);
for (x=0;x<192;x++) limit[x] = 4;
tms.StatusReg = 0x80;
illegalspriteline = 255;
illegalsprite = 0;
memset (tms.dBackMem, 0, IMAGE_SIZE);
for (p=0;p<32;p++) {
y = *attributeptr++;
if (y == 208) break;
if (y > 208) {
y=-(~y&255);
} else {
y++;
}
x = *attributeptr++;
patternptr = tms.vMem + tms.spritepattern +
((size == 16) ? *attributeptr & 0xfc : *attributeptr) * 8;
attributeptr++;
c = (*attributeptr & 0x0f);
if (*attributeptr & 0x80) x -= 32;
attributeptr++;
if (!large) {
/* draw sprite (not enlarged) */
for (yy=y;yy<(y+size);yy++) {
if ( (yy < 0) || (yy > 191) ) continue;
if (limit[yy] == 0) {
/* illegal sprite line */
if (yy < illegalspriteline) {
illegalspriteline = yy;
illegalsprite = p;
} else if (illegalspriteline == yy) {
if (illegalsprite > p) {
illegalsprite = p;
}
}
if (tms.LimitSprites) continue;
} else limit[yy]--;
line = 256*patternptr[yy-y] + patternptr[yy-y+16];
for (xx=x;xx<(x+size);xx++) {
if (line & 0x8000) {
if ((xx >= 0) && (xx < 256)) {
if (tms.dBackMem[yy*256+xx]) {
tms.StatusReg |= 0x20;
} else {
tms.dBackMem[yy*256+xx] = 0x01;
}
if (c && ! (tms.dBackMem[yy*256+xx] & 0x02))
{
tms.dBackMem[yy*256+xx] |= 0x02;
if (bitmap)
*BITMAP_ADDR16(bitmap, TOP_BORDER+yy, LEFT_BORDER+xx) = machine->pens[c];
}
}
}
line *= 2;
}
}
} else {
/* draw enlarged sprite */
for (i=0;i<size;i++) {
yy=y+i*2;
line2 = 256*patternptr[i] + patternptr[i+16];
for (j=0;j<2;j++) {
if ( (yy >= 0) && (yy <= 191) ) {
if (limit[yy] == 0) {
/* illegal sprite line */
if (yy < illegalspriteline) {
illegalspriteline = yy;
illegalsprite = p;
} else if (illegalspriteline == yy) {
if (illegalsprite > p) {
illegalsprite = p;
}
}
if (tms.LimitSprites) continue;
} else limit[yy]--;
line = line2;
for (xx=x;xx<(x+size*2);xx+=2) {
if (line & 0x8000) {
if ((xx >=0) && (xx < 256)) {
if (tms.dBackMem[yy*256+xx]) {
tms.StatusReg |= 0x20;
} else {
tms.dBackMem[yy*256+xx] = 0x01;
}
if (c && ! (tms.dBackMem[yy*256+xx] & 0x02))
{
tms.dBackMem[yy*256+xx] |= 0x02;
if (bitmap)
*BITMAP_ADDR16(bitmap, TOP_BORDER+yy, LEFT_BORDER+xx) = machine->pens[c];
}
}
if (((xx+1) >=0) && ((xx+1) < 256)) {
if (tms.dBackMem[yy*256+xx+1]) {
tms.StatusReg |= 0x20;
} else {
tms.dBackMem[yy*256+xx+1] = 0x01;
}
if (c && ! (tms.dBackMem[yy*256+xx+1] & 0x02))
{
tms.dBackMem[yy*256+xx+1] |= 0x02;
if (bitmap)
*BITMAP_ADDR16(bitmap, TOP_BORDER+yy, LEFT_BORDER+xx+1) = machine->pens[c];
}
}
}
line *= 2;
}
}
yy++;
}
}
}
}
if (illegalspriteline == 255) {
tms.StatusReg |= (p > 31) ? 31 : p;
} else {
tms.StatusReg |= 0x40 + illegalsprite;
}
}
static TMS9928a_interface sIntf;
void TMS9928A_configure (const TMS9928a_interface *intf)
{
sIntf = *intf;
}
VIDEO_START( tms9928a )
{
assert(sIntf.model != TMS_INVALID_MODEL);
TMS9928A_start(machine, &sIntf);
}
MACHINE_DRIVER_START( tms9928a )
/* video hardware */
MDRV_VIDEO_ATTRIBUTES(VIDEO_UPDATE_BEFORE_VBLANK)
MDRV_SCREEN_ADD("main", RASTER)
MDRV_SCREEN_FORMAT(BITMAP_FORMAT_INDEXED16)
MDRV_SCREEN_SIZE(LEFT_BORDER+32*8+RIGHT_BORDER, TOP_BORDER_60HZ+24*8+BOTTOM_BORDER_60HZ)
MDRV_SCREEN_VISIBLE_AREA(LEFT_BORDER-12, LEFT_BORDER+32*8+12-1, TOP_BORDER_60HZ-9, TOP_BORDER_60HZ+24*8+9-1)
MDRV_PALETTE_LENGTH(TMS9928A_PALETTE_SIZE)
MDRV_PALETTE_INIT(tms9928a)
MDRV_VIDEO_START(tms9928a)
MDRV_VIDEO_UPDATE(tms9928a)
MACHINE_DRIVER_END