summaryrefslogtreecommitdiffstatshomepage
path: root/src/devices/video/tms3556.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/devices/video/tms3556.cpp')
-rw-r--r--src/devices/video/tms3556.cpp658
1 files changed, 658 insertions, 0 deletions
diff --git a/src/devices/video/tms3556.cpp b/src/devices/video/tms3556.cpp
new file mode 100644
index 00000000000..9d073bdbe0d
--- /dev/null
+++ b/src/devices/video/tms3556.cpp
@@ -0,0 +1,658 @@
+// license:BSD-3-Clause
+// copyright-holders:Raphael Nabet
+/*
+ tms3556 emulation
+
+ TODO:
+ * implement remaining flags in control registers
+ * test the whole thing
+ * find the bloody tms3556 manual. I mean the register and VRAM interfaces
+ are mostly guesswork full of hacks, and I'd like to compare it with
+ documentation.
+
+ Raphael Nabet, 2004
+*/
+
+#include "emu.h"
+#include "tms3556.h"
+
+//**************************************************************************
+// MACROS / CONSTANTS
+//**************************************************************************
+
+#define LOG 0
+
+
+
+//**************************************************************************
+// GLOBAL VARIABLES
+//**************************************************************************
+
+// devices
+const device_type TMS3556 = &device_creator<tms3556_device>;
+
+
+// default address map
+static ADDRESS_MAP_START( tms3556, AS_0, 8, tms3556_device )
+ AM_RANGE(0x0000, 0xffff) AM_RAM
+ADDRESS_MAP_END
+
+//-------------------------------------------------
+// memory_space_config - return a description of
+// any address spaces owned by this device
+//-------------------------------------------------
+
+const address_space_config *tms3556_device::memory_space_config(address_spacenum spacenum) const
+{
+ return (spacenum == AS_0) ? &m_space_config : NULL;
+}
+
+
+//**************************************************************************
+// INLINE HELPERS
+//**************************************************************************
+
+//-------------------------------------------------
+// readbyte - read a byte at the given address
+//-------------------------------------------------
+
+inline UINT8 tms3556_device::readbyte(offs_t address)
+{
+ return space().read_byte(address&0xFFFF);
+}
+
+
+//-------------------------------------------------
+// writebyte - write a byte at the given address
+//-------------------------------------------------
+
+inline void tms3556_device::writebyte(offs_t address, UINT8 data)
+{
+ space().write_byte(address&0xFFFF, data);
+}
+
+
+//**************************************************************************
+// LIVE DEVICE
+//**************************************************************************
+
+//-------------------------------------------------
+// tms3556_device - constructor
+//-------------------------------------------------
+
+tms3556_device::tms3556_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock)
+ : device_t(mconfig, TMS3556, "Texas Instruments VDP TMS3556", tag, owner, clock, "tms3556", __FILE__),
+ device_memory_interface(mconfig, *this),
+ m_space_config("videoram", ENDIANNESS_LITTLE, 8, 17, 0, NULL, *ADDRESS_MAP_NAME(tms3556)),
+ m_reg_access_phase(0),
+ m_row_col_written(0),
+ m_bamp_written(0),
+ m_colrow(0),
+ m_vdp_acmpxy_mode(dma_write),
+ m_vdp_acmpxy(0),
+ m_vdp_acmp(0),
+ m_init_read(0),
+ m_scanline(0),
+ m_blink(0),
+ m_blink_count(0),
+ m_bg_color(0)
+{
+ for (int i = 0; i < 8; i++)
+ {
+ m_control_regs[i] = 0;
+ m_address_regs[i] = 0xFFFF;
+ }
+}
+
+
+//-------------------------------------------------
+// device_start - device-specific startup
+//-------------------------------------------------
+
+void tms3556_device::device_start()
+{
+ // register for state saving
+ save_item(NAME(m_control_regs));
+ save_item(NAME(m_address_regs));
+ save_item(NAME(m_reg_access_phase));
+ save_item(NAME(m_row_col_written));
+ save_item(NAME(m_bamp_written));
+ save_item(NAME(m_colrow));
+// save_item(NAME(m_vdp_acmpxy_mode)); // FIXME : mame cannot save enum
+ save_item(NAME(m_vdp_acmpxy));
+ save_item(NAME(m_vdp_acmp));
+ save_item(NAME(m_scanline));
+ save_item(NAME(m_blink));
+ save_item(NAME(m_blink_count));
+ save_item(NAME(m_bg_color));
+ save_item(NAME(m_name_offset));
+ save_item(NAME(m_cg_flag));
+ save_item(NAME(m_char_line_counter));
+ save_item(NAME(m_dbl_h_phase));
+
+ machine().first_screen()->register_screen_bitmap(m_bitmap);
+}
+
+
+/*static const char *const tms3556_mode_names[] = { "DISPLAY OFF", "TEXT", "GRAPHIC", "MIXED" };*/
+
+
+UINT32 tms3556_device::screen_update(screen_device &screen, bitmap_ind16 &bitmap, const rectangle &cliprect)
+{
+ copybitmap(bitmap, m_bitmap, 0, 0, 0, 0, cliprect);
+ return 0;
+}
+
+
+//-------------------------------------------------
+// vram_r - VRAM read
+//-------------------------------------------------
+
+READ8_MEMBER( tms3556_device::vram_r )
+{
+ UINT8 ret;
+ if (m_bamp_written) {
+ m_bamp_written=false;
+ m_vdp_acmpxy_mode=dma_write;
+ if (m_init_read)
+ m_vdp_acmp=VDP_BAMP;
+ else
+ m_vdp_acmp=(VDP_BAMP-1)&0xFFFF;
+ }
+
+ if (m_row_col_written) {
+ m_row_col_written=0;
+ m_vdp_acmpxy_mode=dma_read;
+ if (m_init_read)
+ m_vdp_acmpxy=m_colrow;
+ else
+ m_vdp_acmpxy=(m_colrow-1)&0xFFFF;
+ }
+
+ m_init_read=false;
+ if (m_vdp_acmpxy_mode==dma_read) {
+ ret=readbyte(m_vdp_acmpxy);
+ m_vdp_acmpxy++;
+ if (m_vdp_acmpxy==VDP_BAMTF) m_vdp_acmpxy=VDP_BAMP;
+ } else {
+ ret=readbyte(m_vdp_acmp);
+ m_vdp_acmp++;
+ if (m_vdp_acmp==VDP_BAMTF) m_vdp_acmp=VDP_BAMP;
+ }
+ return ret;
+}
+
+//-------------------------------------------------
+// vram_w - VRAM write
+//-------------------------------------------------
+
+WRITE8_MEMBER( tms3556_device::vram_w )
+{
+ if (m_bamp_written) {
+ m_bamp_written=false;
+ m_vdp_acmpxy_mode=dma_read;
+ m_vdp_acmp=VDP_BAMP;
+ }
+
+ if (m_row_col_written) {
+ m_row_col_written=0;
+ m_vdp_acmpxy_mode=dma_write;
+ m_vdp_acmpxy=m_colrow;
+ }
+
+ if (m_vdp_acmpxy_mode==dma_write) {
+ writebyte(m_vdp_acmpxy,data);
+ m_vdp_acmpxy++;
+ if (m_vdp_acmpxy==VDP_BAMTF) m_vdp_acmpxy=VDP_BAMP;
+ } else {
+ writebyte(m_vdp_acmp,data);
+ m_vdp_acmp++;
+ if (m_vdp_acmp==VDP_BAMTF) m_vdp_acmp=VDP_BAMP;
+ }
+
+}
+
+
+//-------------------------------------------------
+// reg_r - read from register port
+//-------------------------------------------------
+
+READ8_MEMBER( tms3556_device::reg_r )
+{
+ if (LOG) logerror("TMS3556 Reg Read: %06x\n", offset);
+
+ int reply = 0; // FIXME : will send internal status (VBL, HBL...)
+ m_reg_access_phase=0;
+ return reply;
+}
+
+//-------------------------------------------------
+// reg_w - write to register port
+//-------------------------------------------------
+
+WRITE8_MEMBER( tms3556_device::reg_w )
+{
+ static int reg2=0; // FIXME : this static makes that only one TMS3556 will be present in one system...
+ static int reg=0;
+
+ if (LOG) logerror("TMS3556 Reg Write: %06x = %02x\n", offset, data);
+
+ switch (m_reg_access_phase) {
+ case 0:
+ reg=data&0x0F;
+ reg2=(data&0xF0)>>4;
+ if (reg!=0)
+ m_reg_access_phase=1;
+ return;
+
+ case 1:
+ if (reg<8) {
+ m_control_regs[reg]=data;
+ // leve un flag si le dernier registre ecrit est row ou col
+ if ((reg==2) || (reg==1)) {
+ m_colrow=(m_control_regs[2]<<8)|m_control_regs[1];
+ m_row_col_written=true;
+ }
+
+ if (reg2==0) {
+ m_reg_access_phase=0;
+ return;
+ }
+ else {
+ m_reg_access_phase=1;
+ reg=reg2;
+ reg2=0;
+ return;
+ }
+ } else {
+ m_address_regs[reg-8]=(m_control_regs[2]<<8)|m_control_regs[1];
+ // cas speciaux de decalage pour les generateurs
+ if ((reg>=0xB) && (reg<=0xE)) {
+ m_address_regs[reg-8]+=2;
+ m_address_regs[reg-8]&=0xFFFF;
+ } else {
+ m_address_regs[reg-8]+=1;
+ m_address_regs[reg-8]&=0xFFFF;
+ }
+ if (reg==9) {
+ m_row_col_written=false;
+ m_bamp_written=true;
+ m_reg_access_phase=0;
+ return;
+ } else {
+ m_row_col_written=0;
+ m_bamp_written=false;
+ m_reg_access_phase=2;//???
+ return;
+ }
+ logerror("VDP16[%d] = x%x",reg,m_address_regs[reg-8]);
+ if (reg2==0) {
+ m_reg_access_phase=0;
+ return;
+ }
+ else {
+ m_reg_access_phase=1;
+ reg=reg2;
+ reg2=0;
+ return;
+ }
+ }
+ case 2:
+ m_reg_access_phase=0;
+ return;
+ }
+}
+
+//--------------------------------------------------------------------------
+// initptr_r - set VDP in read mode (not exacly on the VDP but on the TAL)
+//--------------------------------------------------------------------------
+
+READ8_MEMBER( tms3556_device::initptr_r )
+{
+ m_init_read=true;
+ return 0xff;
+}
+
+
+//-------------------------------------------------
+// redraw code
+//-------------------------------------------------
+
+
+//-------------------------------------------------
+// draw_line_empty - draw an empty line (used for
+// top and bottom borders, and screen off mode)
+//-------------------------------------------------
+
+void tms3556_device::draw_line_empty(UINT16 *ln)
+{
+ int i;
+
+ for (i = 0; i < TMS3556_TOTAL_WIDTH; i++)
+#if TMS3556_DOUBLE_WIDTH
+ *ln++ = m_bg_color;
+#endif
+ *ln++ = m_bg_color;
+}
+
+
+//-------------------------------------------------
+// draw_line_text_common - draw a line of text
+// (called by draw_line_text and draw_line_mixed)
+//-------------------------------------------------
+
+void tms3556_device::draw_line_text_common(UINT16 *ln)
+{
+ int pattern, x, xx, i, name_offset;
+ UINT16 fg, bg;
+ offs_t nametbl_base;
+ offs_t patterntbl_base[4];
+ int name_hi, name_lo;
+ int pattern_ix;
+ int alphanumeric_mode, dbl_w, dbl_h, dbl_w_phase = 0;
+
+ nametbl_base = m_address_regs[2];
+ for (i = 0; i < 4; i++)
+ patterntbl_base[i] = m_address_regs[i + 3];
+
+ for (xx = 0; xx < TMS3556_LEFT_BORDER; xx++)
+#if TMS3556_DOUBLE_WIDTH
+ *ln++ = m_bg_color;
+#endif
+ *ln++ = m_bg_color;
+
+ name_offset = m_name_offset;
+
+ for (x = 0; x < 40; x++)
+ {
+ name_hi = readbyte(nametbl_base + name_offset);
+ name_lo = readbyte(nametbl_base + name_offset + 1);
+ pattern_ix = ((name_hi >> 2) & 2) | ((name_hi >> 4) & 1);
+ alphanumeric_mode = (pattern_ix < 2) || ((pattern_ix == 3) && !(m_control_regs[7] & 0x08));
+ fg = (name_hi >> 5) & 0x7;
+ if (alphanumeric_mode)
+ {
+ if (name_hi & 4)
+ { /* inverted color */
+ bg = fg;
+ fg = m_bg_color;
+ }
+ else
+ bg = m_bg_color;
+ dbl_w = name_hi & 0x2;
+ dbl_h = name_hi & 0x1;
+ }
+ else
+ {
+ bg = name_hi & 0x7;
+ dbl_w = 0;
+ dbl_h = 0;
+ }
+ if ((name_lo & 0x80) && m_blink)
+ fg = bg; /* blink off time */
+ if (! dbl_h)
+ { /* single height */
+ pattern = readbyte(patterntbl_base[pattern_ix] + (name_lo & 0x7f) + 128 * m_char_line_counter);
+ if (m_char_line_counter == 0)
+ m_dbl_h_phase[x] = 0;
+ }
+ else
+ { /* double height */
+ if (! m_dbl_h_phase[x])
+ /* first phase: pattern from upper half */
+ pattern = readbyte(patterntbl_base[pattern_ix] + (name_lo & 0x7f) + 128 * (5 + (m_char_line_counter >> 1)));
+ else
+ /* second phase: pattern from lower half */
+ pattern = readbyte(patterntbl_base[pattern_ix] + (name_lo & 0x7f) + 128 * (m_char_line_counter >> 1));
+ if (m_char_line_counter == 0)
+ m_dbl_h_phase[x] = !m_dbl_h_phase[x];
+ }
+ if (!dbl_w)
+ { /* single width */
+ for (xx = 0; xx < 8; xx++)
+ {
+ UINT16 color = (pattern & 0x80) ? fg : bg;
+#if TMS3556_DOUBLE_WIDTH
+ *ln++ = color;
+#endif
+ *ln++ = color;
+ pattern <<= 1;
+ }
+ dbl_w_phase = 0;
+ }
+ else
+ { /* double width */
+ if (dbl_w_phase)
+ /* second phase: display right half */
+ pattern <<= 4;
+ for (xx = 0; xx < 4; xx++)
+ {
+ UINT16 color = (pattern & 0x80) ? fg : bg;
+#if TMS3556_DOUBLE_WIDTH
+ *ln++ = color; *ln++ = color;
+#endif
+ *ln++ = color; *ln++ = color;
+ pattern <<= 1;
+ }
+ dbl_w_phase = !dbl_w_phase;
+ }
+ name_offset += 2;
+ }
+
+ for (xx = 0; xx < TMS3556_RIGHT_BORDER; xx++)
+#if TMS3556_DOUBLE_WIDTH
+ *ln++ = m_bg_color;
+#endif
+ *ln++ = m_bg_color;
+
+ if (m_char_line_counter == 0)
+ m_name_offset = name_offset;
+}
+
+
+//-------------------------------------------------
+// draw_line_bitmap_common - draw a line of bitmap
+// (called by draw_line_bitmap and draw_line_mixed)
+//-------------------------------------------------
+
+void tms3556_device::draw_line_bitmap_common(UINT16 *ln)
+{
+ int x, xx;
+ offs_t nametbl_base;
+ int name_b, name_g, name_r;
+
+ nametbl_base = m_address_regs[2];
+
+ for (xx = 0; xx < TMS3556_LEFT_BORDER; xx++)
+#if TMS3556_DOUBLE_WIDTH
+ *ln++ = m_bg_color;
+#endif
+ *ln++ = m_bg_color;
+
+ for (x = 0; x < 40; x++)
+ {
+ name_b = readbyte(nametbl_base + m_name_offset);
+ name_g = readbyte(nametbl_base + m_name_offset + 1);
+ name_r = readbyte(nametbl_base + m_name_offset + 2);
+ for (xx = 0; xx < 8; xx++)
+ {
+ UINT16 color = ((name_b >> 5) & 0x4) | ((name_g >> 6) & 0x2) | ((name_r >> 7) & 0x1);
+#if TMS3556_DOUBLE_WIDTH
+ *ln++ = color;
+#endif
+ *ln++ = color;
+ name_b <<= 1;
+ name_g <<= 1;
+ name_r <<= 1;
+ }
+ m_name_offset += 3;
+ }
+
+ for (xx = 0; xx < TMS3556_RIGHT_BORDER; xx++)
+#if TMS3556_DOUBLE_WIDTH
+ *ln++ = m_bg_color;
+#endif
+ *ln++ = m_bg_color;
+}
+
+
+//-------------------------------------------------
+// draw_line_text - draw a line in text mode
+//-------------------------------------------------
+
+void tms3556_device::draw_line_text(UINT16 *ln)
+{
+ if (m_char_line_counter == 0)
+ m_char_line_counter = 10;
+ m_char_line_counter--;
+ draw_line_text_common(ln);
+}
+
+
+//-------------------------------------------------
+// draw_line_bitmap - draw a line in bitmap mode
+//-------------------------------------------------
+
+void tms3556_device::draw_line_bitmap(UINT16 *ln)
+{
+ draw_line_bitmap_common(ln);
+ m_bg_color = (readbyte(m_address_regs[2] + m_name_offset) >> 5) & 0x7;
+ m_name_offset += 2;
+}
+
+
+//-------------------------------------------------
+// draw_line_mixed - draw a line in mixed mode
+//-------------------------------------------------
+
+void tms3556_device::draw_line_mixed(UINT16 *ln)
+{
+ if (m_cg_flag)
+ { /* bitmap line */
+ draw_line_bitmap_common(ln);
+ m_bg_color = (readbyte(m_address_regs[2] + m_name_offset) >> 5) & 0x7;
+ m_cg_flag = (readbyte(m_address_regs[2] + m_name_offset) >> 4) & 0x1;
+ m_name_offset += 2;
+ }
+ else
+ { /* text line */
+ if (m_char_line_counter == 0)
+ m_char_line_counter = 10;
+ m_char_line_counter--;
+ draw_line_text_common(ln);
+ if (m_char_line_counter == 0)
+ {
+ m_bg_color = (readbyte(m_address_regs[2] + m_name_offset) >> 5) & 0x7;
+ m_cg_flag = (readbyte(m_address_regs[2] + m_name_offset) >> 4) & 0x1;
+ m_name_offset += 2;
+ }
+ }
+}
+
+
+//-------------------------------------------------
+// draw_line - draw a line. If non-interlaced mode,
+// duplicate the line.
+//-------------------------------------------------
+
+void tms3556_device::draw_line(bitmap_ind16 &bmp, int line)
+{
+ int double_lines = 0;
+ UINT16 *ln, *ln2 = NULL;
+
+// if (m_control_regs[4] & 0x??)
+// { // interlaced mode
+// ln = &bmp->pix16(line, m_field);
+// }
+// else
+ { /* non-interlaced mode */
+ ln = &bmp.pix16(line);
+ ln2 = &bmp.pix16(line, 1);
+ double_lines = 1;
+ }
+
+ if ((line < TMS3556_TOP_BORDER) || (line >= (TMS3556_TOP_BORDER + 250)))
+ {
+ /* draw top and bottom borders */
+ draw_line_empty(ln);
+ m_cg_flag=0; // FIXME : forme text mode for 1st line in mixed
+ }
+ else
+ {
+ /* draw useful area */
+ switch (m_control_regs[6] >> 6)
+ {
+ case TMS3556_MODE_OFF:
+ draw_line_empty(ln);
+ break;
+ case TMS3556_MODE_TEXT:
+ draw_line_text(ln);
+ break;
+ case TMS3556_MODE_BITMAP:
+ draw_line_bitmap(ln);
+ break;
+ case TMS3556_MODE_MIXED:
+ draw_line_mixed(ln);
+ break;
+ }
+ }
+
+ if (double_lines)
+ {
+ // TODO: this overlaps in exeltel - use memmove for now
+ //memcpy(ln2, ln, TMS3556_TOTAL_WIDTH * (TMS3556_DOUBLE_WIDTH ? 2 : 1));
+ memmove(ln2, ln, TMS3556_TOTAL_WIDTH * (TMS3556_DOUBLE_WIDTH ? 2 : 1));
+ }
+}
+
+
+//-------------------------------------------------
+// interrupt_start_vblank - Do vblank-time tasks
+//-------------------------------------------------
+
+void tms3556_device::interrupt_start_vblank(void)
+{
+ /* at every frame, vdp switches fields */
+ //m_field = !m_field;
+
+ /* color blinking */
+ if (m_blink_count)
+ m_blink_count--;
+ if (!m_blink_count)
+ {
+ m_blink = !m_blink;
+ m_blink_count = 60; /*no idea what the real value is*/
+ }
+ /* reset background color */
+ m_bg_color = (m_control_regs[7] >> 5) & 0x7;
+ /* reset name offset */
+ m_name_offset = 0;
+ /* reset character line counter */
+ m_char_line_counter = 0;
+ /* reset c/g flag */
+ m_cg_flag = 0;
+ /* reset double height phase flags */
+ memset(m_dbl_h_phase, 0, sizeof(m_dbl_h_phase));
+}
+
+
+//-------------------------------------------------
+// interrupt - scanline handler
+//-------------------------------------------------
+
+void tms3556_device::interrupt(running_machine &machine)
+{
+ /* check for start of vblank */
+ if (m_scanline == 310) /*no idea what the real value is*/
+ interrupt_start_vblank();
+
+ /* render the current line */
+ if ((m_scanline >= 0) && (m_scanline < TMS3556_TOTAL_HEIGHT))
+ {
+ //if (!video_skip_this_frame())
+ draw_line(m_bitmap, m_scanline);
+ }
+
+ if (++m_scanline == 313)
+ m_scanline = 0;
+}