summaryrefslogtreecommitdiffstatshomepage
path: root/src/emu/video/tms34061.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/emu/video/tms34061.c')
-rw-r--r--src/emu/video/tms34061.c541
1 files changed, 541 insertions, 0 deletions
diff --git a/src/emu/video/tms34061.c b/src/emu/video/tms34061.c
new file mode 100644
index 00000000000..49d20d31da2
--- /dev/null
+++ b/src/emu/video/tms34061.c
@@ -0,0 +1,541 @@
+/****************************************************************************
+ * *
+ * Functions to emulate the TMS34061 video controller *
+ * *
+ * Created by Zsolt Vasvari on 5/26/1998. *
+ * Updated by Aaron Giles on 11/21/2000. *
+ * *
+ * This is far from complete. See the TMS34061 User's Guide available on *
+ * www.spies.com/arcade *
+ * *
+ ****************************************************************************/
+
+#include "driver.h"
+#include "tms34061.h"
+
+
+#define VERBOSE (0)
+
+
+/*************************************
+ *
+ * Internal structure
+ *
+ *************************************/
+
+struct tms34061_data
+{
+ UINT16 regs[TMS34061_REGCOUNT];
+ UINT16 xmask;
+ UINT8 yshift;
+ UINT32 vrammask;
+ UINT8 * vram;
+ UINT8 * latchram;
+ UINT8 latchdata;
+ UINT8 * shiftreg;
+ emu_timer * timer;
+ struct tms34061_interface intf;
+};
+
+
+
+/*************************************
+ *
+ * Global variables
+ *
+ *************************************/
+
+static struct tms34061_data tms34061;
+
+static const char *regnames[] =
+{
+ "HORENDSYNC", "HORENDBLNK", "HORSTARTBLNK", "HORTOTAL",
+ "VERENDSYNC", "VERENDBLNK", "VERSTARTBLNK", "VERTOTAL",
+ "DISPUPDATE", "DISPSTART", "VERINT", "CONTROL1",
+ "CONTROL2", "STATUS", "XYOFFSET", "XYADDRESS",
+ "DISPADDRESS", "VERCOUNTER"
+};
+
+
+
+/*************************************
+ *
+ * Prototypes
+ *
+ *************************************/
+
+static TIMER_CALLBACK( tms34061_interrupt );
+
+
+
+/*************************************
+ *
+ * Hardware startup
+ *
+ *************************************/
+
+void tms34061_start(struct tms34061_interface *interface)
+{
+ /* reset the data */
+ memset(&tms34061, 0, sizeof(tms34061));
+ tms34061.intf = *interface;
+ tms34061.vrammask = tms34061.intf.vramsize - 1;
+
+ /* allocate memory for VRAM */
+ tms34061.vram = auto_malloc(tms34061.intf.vramsize + 256 * 2);
+ memset(tms34061.vram, 0, tms34061.intf.vramsize + 256 * 2);
+
+ /* allocate memory for latch RAM */
+ tms34061.latchram = auto_malloc(tms34061.intf.vramsize + 256 * 2);
+ memset(tms34061.latchram, 0, tms34061.intf.vramsize + 256 * 2);
+
+ /* add some buffer space for VRAM and latch RAM */
+ tms34061.vram += 256;
+ tms34061.latchram += 256;
+
+ /* point the shift register to the base of VRAM for now */
+ tms34061.shiftreg = tms34061.vram;
+
+ /* initialize registers to their default values from the manual */
+ tms34061.regs[TMS34061_HORENDSYNC] = 0x0010;
+ tms34061.regs[TMS34061_HORENDBLNK] = 0x0020;
+ tms34061.regs[TMS34061_HORSTARTBLNK] = 0x01f0;
+ tms34061.regs[TMS34061_HORTOTAL] = 0x0200;
+ tms34061.regs[TMS34061_VERENDSYNC] = 0x0004;
+ tms34061.regs[TMS34061_VERENDBLNK] = 0x0010;
+ tms34061.regs[TMS34061_VERSTARTBLNK] = 0x00f0;
+ tms34061.regs[TMS34061_VERTOTAL] = 0x0100;
+ tms34061.regs[TMS34061_DISPUPDATE] = 0x0000;
+ tms34061.regs[TMS34061_DISPSTART] = 0x0000;
+ tms34061.regs[TMS34061_VERINT] = 0x0000;
+ tms34061.regs[TMS34061_CONTROL1] = 0x7000;
+ tms34061.regs[TMS34061_CONTROL2] = 0x0600;
+ tms34061.regs[TMS34061_STATUS] = 0x0000;
+ tms34061.regs[TMS34061_XYOFFSET] = 0x0010;
+ tms34061.regs[TMS34061_XYADDRESS] = 0x0000;
+ tms34061.regs[TMS34061_DISPADDRESS] = 0x0000;
+ tms34061.regs[TMS34061_VERCOUNTER] = 0x0000;
+
+ /* start vertical interrupt timer */
+ tms34061.timer = timer_alloc(tms34061_interrupt);
+}
+
+
+
+/*************************************
+ *
+ * Interrupt handling
+ *
+ *************************************/
+
+INLINE void update_interrupts(void)
+{
+ /* if we have a callback, process it */
+ if (tms34061.intf.interrupt)
+ {
+ /* if the status bit is set, and ints are enabled, turn it on */
+ if ((tms34061.regs[TMS34061_STATUS] & 0x0001) && (tms34061.regs[TMS34061_CONTROL1] & 0x0400))
+ (*tms34061.intf.interrupt)(ASSERT_LINE);
+ else
+ (*tms34061.intf.interrupt)(CLEAR_LINE);
+ }
+}
+
+
+static TIMER_CALLBACK( tms34061_interrupt )
+{
+ /* set timer for next frame */
+ timer_adjust(tms34061.timer, video_screen_get_frame_period(tms34061.intf.scrnum), 0, attotime_zero);
+
+ /* set the interrupt bit in the status reg */
+ tms34061.regs[TMS34061_STATUS] |= 1;
+
+ /* update the interrupt state */
+ update_interrupts();
+}
+
+
+
+/*************************************
+ *
+ * Register writes
+ *
+ *************************************/
+
+static WRITE8_HANDLER( register_w )
+{
+ int scanline;
+ int regnum = offset >> 2;
+
+ /* certain registers affect the display directly */
+ if ((regnum >= TMS34061_HORENDSYNC && regnum <= TMS34061_DISPSTART) ||
+ (regnum == TMS34061_CONTROL2))
+ video_screen_update_partial(0, video_screen_get_vpos(0));
+
+ /* store the hi/lo half */
+ if (offset & 0x02)
+ tms34061.regs[regnum] = (tms34061.regs[regnum] & 0x00ff) | (data << 8);
+ else
+ tms34061.regs[regnum] = (tms34061.regs[regnum] & 0xff00) | data;
+
+ /* log it */
+ if (VERBOSE) logerror("%04X:tms34061 %s = %04x\n", activecpu_get_pc(), regnames[regnum], tms34061.regs[regnum]);
+
+ /* update the state of things */
+ switch (regnum)
+ {
+ /* vertical interrupt: adjust the timer */
+ case TMS34061_VERINT:
+ scanline = tms34061.regs[TMS34061_VERINT] - tms34061.regs[TMS34061_VERENDBLNK];
+
+ if (scanline < 0)
+ scanline += tms34061.regs[TMS34061_VERTOTAL];
+
+ timer_adjust(tms34061.timer, video_screen_get_time_until_pos(tms34061.intf.scrnum, scanline, tms34061.regs[TMS34061_HORSTARTBLNK]), 0, attotime_zero);
+ break;
+
+ /* XY offset: set the X and Y masks */
+ case TMS34061_XYOFFSET:
+ switch (tms34061.regs[TMS34061_XYOFFSET] & 0x00ff)
+ {
+ case 0x01: tms34061.yshift = 2; break;
+ case 0x02: tms34061.yshift = 3; break;
+ case 0x04: tms34061.yshift = 4; break;
+ case 0x08: tms34061.yshift = 5; break;
+ case 0x10: tms34061.yshift = 6; break;
+ case 0x20: tms34061.yshift = 7; break;
+ case 0x40: tms34061.yshift = 8; break;
+ case 0x80: tms34061.yshift = 9; break;
+ default: logerror("Invalid value for XYOFFSET = %04x\n", tms34061.regs[TMS34061_XYOFFSET]); break;
+ }
+ tms34061.xmask = (1 << tms34061.yshift) - 1;
+ break;
+
+ /* CONTROL1: they could have turned interrupts on */
+ case TMS34061_CONTROL1:
+ update_interrupts();
+ break;
+
+ /* other supported registers */
+ case TMS34061_XYADDRESS:
+ break;
+ }
+}
+
+
+
+/*************************************
+ *
+ * Register reads
+ *
+ *************************************/
+
+static READ8_HANDLER( register_r )
+{
+ int regnum = offset >> 2;
+ UINT16 result;
+
+ /* extract the correct portion of the register */
+ result = tms34061.regs[regnum];
+
+ /* special cases: */
+ switch (regnum)
+ {
+ /* status register: a read here clears it */
+ case TMS34061_STATUS:
+ tms34061.regs[TMS34061_STATUS] = 0;
+ update_interrupts();
+ break;
+
+ /* vertical count register: return the current scanline */
+ case TMS34061_VERCOUNTER:
+ result = (video_screen_get_vpos(0)+ tms34061.regs[TMS34061_VERENDBLNK]) % tms34061.regs[TMS34061_VERTOTAL];
+ break;
+ }
+
+ /* log it */
+ if (VERBOSE) logerror("%04X:tms34061 %s read = %04X\n", activecpu_get_pc(), regnames[regnum], result);
+ return (offset & 0x02) ? (result >> 8) : result;
+}
+
+
+
+/*************************************
+ *
+ * XY addressing
+ *
+ *************************************/
+
+INLINE void adjust_xyaddress(int offset)
+{
+ /* note that carries are allowed if the Y coordinate isn't being modified */
+ switch (offset & 0x1e)
+ {
+ case 0x00: /* no change */
+ break;
+
+ case 0x02: /* X + 1 */
+ tms34061.regs[TMS34061_XYADDRESS]++;
+ break;
+
+ case 0x04: /* X - 1 */
+ tms34061.regs[TMS34061_XYADDRESS]--;
+ break;
+
+ case 0x06: /* X = 0 */
+ tms34061.regs[TMS34061_XYADDRESS] &= ~tms34061.xmask;
+ break;
+
+ case 0x08: /* Y + 1 */
+ tms34061.regs[TMS34061_XYADDRESS] += 1 << tms34061.yshift;
+ break;
+
+ case 0x0a: /* X + 1, Y + 1 */
+ tms34061.regs[TMS34061_XYADDRESS] = (tms34061.regs[TMS34061_XYADDRESS] & ~tms34061.xmask) |
+ ((tms34061.regs[TMS34061_XYADDRESS] + 1) & tms34061.xmask);
+ tms34061.regs[TMS34061_XYADDRESS] += 1 << tms34061.yshift;
+ break;
+
+ case 0x0c: /* X - 1, Y + 1 */
+ tms34061.regs[TMS34061_XYADDRESS] = (tms34061.regs[TMS34061_XYADDRESS] & ~tms34061.xmask) |
+ ((tms34061.regs[TMS34061_XYADDRESS] - 1) & tms34061.xmask);
+ tms34061.regs[TMS34061_XYADDRESS] += 1 << tms34061.yshift;
+ break;
+
+ case 0x0e: /* X = 0, Y + 1 */
+ tms34061.regs[TMS34061_XYADDRESS] &= ~tms34061.xmask;
+ tms34061.regs[TMS34061_XYADDRESS] += 1 << tms34061.yshift;
+ break;
+
+ case 0x10: /* Y - 1 */
+ tms34061.regs[TMS34061_XYADDRESS] -= 1 << tms34061.yshift;
+ break;
+
+ case 0x12: /* X + 1, Y - 1 */
+ tms34061.regs[TMS34061_XYADDRESS] = (tms34061.regs[TMS34061_XYADDRESS] & ~tms34061.xmask) |
+ ((tms34061.regs[TMS34061_XYADDRESS] + 1) & tms34061.xmask);
+ tms34061.regs[TMS34061_XYADDRESS] -= 1 << tms34061.yshift;
+ break;
+
+ case 0x14: /* X - 1, Y - 1 */
+ tms34061.regs[TMS34061_XYADDRESS] = (tms34061.regs[TMS34061_XYADDRESS] & ~tms34061.xmask) |
+ ((tms34061.regs[TMS34061_XYADDRESS] - 1) & tms34061.xmask);
+ tms34061.regs[TMS34061_XYADDRESS] -= 1 << tms34061.yshift;
+ break;
+
+ case 0x16: /* X = 0, Y - 1 */
+ tms34061.regs[TMS34061_XYADDRESS] &= ~tms34061.xmask;
+ tms34061.regs[TMS34061_XYADDRESS] -= 1 << tms34061.yshift;
+ break;
+
+ case 0x18: /* Y = 0 */
+ tms34061.regs[TMS34061_XYADDRESS] &= tms34061.xmask;
+ break;
+
+ case 0x1a: /* X + 1, Y = 0 */
+ tms34061.regs[TMS34061_XYADDRESS]++;
+ tms34061.regs[TMS34061_XYADDRESS] &= tms34061.xmask;
+ break;
+
+ case 0x1c: /* X - 1, Y = 0 */
+ tms34061.regs[TMS34061_XYADDRESS]--;
+ tms34061.regs[TMS34061_XYADDRESS] &= tms34061.xmask;
+ break;
+
+ case 0x1e: /* X = 0, Y = 0 */
+ tms34061.regs[TMS34061_XYADDRESS] = 0;
+ break;
+ }
+}
+
+
+static WRITE8_HANDLER( xypixel_w )
+{
+ /* determine the offset, then adjust it */
+ offs_t pixeloffs = tms34061.regs[TMS34061_XYADDRESS];
+ if (offset)
+ adjust_xyaddress(offset);
+
+ /* adjust for the upper bits */
+ pixeloffs |= (tms34061.regs[TMS34061_XYOFFSET] & 0x0f00) << 8;
+
+ /* mask to the VRAM size */
+ pixeloffs &= tms34061.vrammask;
+
+ /* set the pixel data */
+ tms34061.vram[pixeloffs] = data;
+ tms34061.latchram[pixeloffs] = tms34061.latchdata;
+}
+
+
+static READ8_HANDLER( xypixel_r )
+{
+ /* determine the offset, then adjust it */
+ offs_t pixeloffs = tms34061.regs[TMS34061_XYADDRESS];
+ if (offset)
+ adjust_xyaddress(offset);
+
+ /* adjust for the upper bits */
+ pixeloffs |= (tms34061.regs[TMS34061_XYOFFSET] & 0x0f00) << 8;
+
+ /* mask to the VRAM size */
+ pixeloffs &= tms34061.vrammask;
+
+ /* return the result */
+ return tms34061.vram[pixeloffs];
+}
+
+
+
+/*************************************
+ *
+ * Core writes
+ *
+ *************************************/
+
+void tms34061_w(int col, int row, int func, UINT8 data)
+{
+ offs_t offs;
+
+ /* the function code determines what to do */
+ switch (func)
+ {
+ /* both 0 and 2 map to register access */
+ case 0:
+ case 2:
+ register_w(col, data);
+ break;
+
+ /* function 1 maps to XY access; col is the address adjustment */
+ case 1:
+ xypixel_w(col, data);
+ break;
+
+ /* function 3 maps to direct access */
+ case 3:
+ offs = ((row << tms34061.intf.rowshift) | col) & tms34061.vrammask;
+ if (tms34061.regs[TMS34061_CONTROL2] & 0x0040)
+ offs |= (tms34061.regs[TMS34061_CONTROL2] & 3) << 16;
+ if (tms34061.vram[offs] != data || tms34061.latchram[offs] != tms34061.latchdata)
+ {
+ tms34061.vram[offs] = data;
+ tms34061.latchram[offs] = tms34061.latchdata;
+ }
+ break;
+
+ /* function 4 performs a shift reg transfer to VRAM */
+ case 4:
+ offs = col << tms34061.intf.rowshift;
+ if (tms34061.regs[TMS34061_CONTROL2] & 0x0040)
+ offs |= (tms34061.regs[TMS34061_CONTROL2] & 3) << 16;
+ offs &= tms34061.vrammask;
+
+ memcpy(&tms34061.vram[offs], tms34061.shiftreg, (size_t)1 << tms34061.intf.rowshift);
+ memset(&tms34061.latchram[offs], tms34061.latchdata, (size_t)1 << tms34061.intf.rowshift);
+ break;
+
+ /* function 5 performs a shift reg transfer from VRAM */
+ case 5:
+ offs = col << tms34061.intf.rowshift;
+ if (tms34061.regs[TMS34061_CONTROL2] & 0x0040)
+ offs |= (tms34061.regs[TMS34061_CONTROL2] & 3) << 16;
+ offs &= tms34061.vrammask;
+
+ tms34061.shiftreg = &tms34061.vram[offs];
+ break;
+
+ /* log anything else */
+ default:
+ logerror("Unsupported TMS34061 function %d - PC: %04X\n", func, activecpu_get_pc());
+ break;
+ }
+}
+
+
+UINT8 tms34061_r(int col, int row, int func)
+{
+ int result = 0;
+ offs_t offs;
+
+ /* the function code determines what to do */
+ switch (func)
+ {
+ /* both 0 and 2 map to register access */
+ case 0:
+ case 2:
+ result = register_r(col);
+ break;
+
+ /* function 1 maps to XY access; col is the address adjustment */
+ case 1:
+ result = xypixel_r(col);
+ break;
+
+ /* funtion 3 maps to direct access */
+ case 3:
+ offs = ((row << tms34061.intf.rowshift) | col) & tms34061.vrammask;
+ result = tms34061.vram[offs];
+ break;
+
+ /* function 4 performs a shift reg transfer to VRAM */
+ case 4:
+ offs = col << tms34061.intf.rowshift;
+ if (tms34061.regs[TMS34061_CONTROL2] & 0x0040)
+ offs |= (tms34061.regs[TMS34061_CONTROL2] & 3) << 16;
+ offs &= tms34061.vrammask;
+
+ memcpy(&tms34061.vram[offs], tms34061.shiftreg, (size_t)1 << tms34061.intf.rowshift);
+ memset(&tms34061.latchram[offs], tms34061.latchdata, (size_t)1 << tms34061.intf.rowshift);
+ break;
+
+ /* function 5 performs a shift reg transfer from VRAM */
+ case 5:
+ offs = col << tms34061.intf.rowshift;
+ if (tms34061.regs[TMS34061_CONTROL2] & 0x0040)
+ offs |= (tms34061.regs[TMS34061_CONTROL2] & 3) << 16;
+ offs &= tms34061.vrammask;
+
+ tms34061.shiftreg = &tms34061.vram[offs];
+ break;
+
+ /* log anything else */
+ default:
+ logerror("Unsupported TMS34061 function %d - PC: %04X\n",
+ func, activecpu_get_pc());
+ break;
+ }
+
+ return result;
+}
+
+
+
+/*************************************
+ *
+ * Misc functions
+ *
+ *************************************/
+
+READ8_HANDLER( tms34061_latch_r )
+{
+ return tms34061.latchdata;
+}
+
+
+WRITE8_HANDLER( tms34061_latch_w )
+{
+ if (VERBOSE) logerror("tms34061_latch = %02X\n", data);
+ tms34061.latchdata = data;
+}
+
+
+void tms34061_get_display_state(struct tms34061_display *state)
+{
+ state->blanked = (~tms34061.regs[TMS34061_CONTROL2] >> 13) & 1;
+ state->vram = tms34061.vram;
+ state->latchram = tms34061.latchram;
+ state->regs = tms34061.regs;
+
+ /* compute the display start */
+ state->dispstart = (tms34061.regs[TMS34061_DISPSTART] << (tms34061.intf.rowshift - 2)) & tms34061.vrammask;
+}