summaryrefslogtreecommitdiffstatshomepage
path: root/src/emu/video/tms9928a.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/emu/video/tms9928a.c')
-rw-r--r--src/emu/video/tms9928a.c809
1 files changed, 809 insertions, 0 deletions
diff --git a/src/emu/video/tms9928a.c b/src/emu/video/tms9928a.c
new file mode 100644
index 00000000000..cf20b9d60ee
--- /dev/null
+++ b/src/emu/video/tms9928a.c
@@ -0,0 +1,809 @@
+/*
+** 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 (*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 *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, TRANSPARENCY_NONE, 0);
+ {
+ 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 | VIDEO_TYPE_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
+