diff options
Diffstat (limited to 'src/emu/video/tms3556.c')
-rw-r--r-- | src/emu/video/tms3556.c | 588 |
1 files changed, 588 insertions, 0 deletions
diff --git a/src/emu/video/tms3556.c b/src/emu/video/tms3556.c new file mode 100644 index 00000000000..03f2ef1f2ca --- /dev/null +++ b/src/emu/video/tms3556.c @@ -0,0 +1,588 @@ +/* + 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); +} + + +//------------------------------------------------- +// writebyte - write a byte at the given address +//------------------------------------------------- + +inline void tms3556_device::writebyte(offs_t address, UINT8 data) +{ + space().write_byte(address, 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), + device_memory_interface(mconfig, *this), + m_space_config("videoram", ENDIANNESS_LITTLE, 8, 17, 0, NULL, *ADDRESS_MAP_NAME(tms3556)), + m_write_ptr(0), + m_reg_ptr(0), + m_reg_access_phase(0), + m_magical_mystery_flag(0), + m_scanline(0), + m_blink(0), + m_blink_count(0) +{ + for (int i = 0; i < 8; i++) + { + m_control_regs[i] = 0; + m_address_regs[i] = 0; + } +} + + +//------------------------------------------------- +// 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_write_ptr)); + save_item(NAME(m_reg_ptr)); + save_item(NAME(m_reg_access_phase)); + save_item(NAME(m_magical_mystery_flag)); + 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().primary_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 ) +{ + if (LOG) logerror("TMS3556 VRAM Read: %06x\n", offset); + + if (m_magical_mystery_flag) + { + m_write_ptr = ((m_control_regs[2] << 8) | m_control_regs[1]) + 1; + m_magical_mystery_flag = 0; + } + + return readbyte(m_address_regs[1]++); +} + +//------------------------------------------------- +// vram_w - VRAM write +//------------------------------------------------- + +WRITE8_MEMBER( tms3556_device::vram_w ) +{ + if (LOG) logerror("TMS3556 VRAM Write: %06x = %02x\n", offset, data); + + if (m_magical_mystery_flag) + { + m_write_ptr = (m_control_regs[2] << 8) | m_control_regs[1]; + m_magical_mystery_flag = 0; + } + + writebyte(m_write_ptr++, data); +} + + +//------------------------------------------------- +// reg_r - read from register port +//------------------------------------------------- + +READ8_MEMBER( tms3556_device::reg_r ) +{ + if (LOG) logerror("TMS3556 Reg Read: %06x\n", offset); + + int reply = 0; + + if (m_reg_ptr < 8) + { + reply = m_control_regs[m_reg_ptr]; + m_reg_access_phase = 0; + } + else + { + // ??? + } + + return reply; +} + +//------------------------------------------------- +// reg_w - write to register port +//------------------------------------------------- + +WRITE8_MEMBER( tms3556_device::reg_w ) +{ + if (LOG) logerror("TMS3556 Reg Write: %06x = %02x\n", offset, data); + + if ((m_reg_access_phase == 3) && (data)) + m_reg_access_phase = 0; /* ???????????? */ + + switch (m_reg_access_phase) + { + case 0: + m_reg_ptr = data & 0x0f; + m_reg_access_phase = 1; + break; + + case 1: + if (m_reg_ptr < 8) + { + m_control_regs[m_reg_ptr] = data; + m_reg_access_phase = 0; + if (m_reg_ptr == 2) + m_magical_mystery_flag = 1; + } + else if (m_reg_ptr == 9) + { /* I don't understand what is going on, but it is the only way to + get this to work */ + m_address_regs[m_reg_ptr - 8] = ((m_control_regs[2] << 8) | m_control_regs[1]) + 1; + m_reg_access_phase = 0; + m_magical_mystery_flag = 0; + } + else + { + m_address_regs[m_reg_ptr - 8] = (m_control_regs[m_reg_ptr - 8] & 0xff00) | m_control_regs[1]; + m_reg_access_phase = 2; + m_magical_mystery_flag = 0; + } + break; + + case 2: + m_address_regs[m_reg_ptr - 8] = (m_control_regs[m_reg_ptr - 8] & 0x00ff) | (m_control_regs[2] << 8); + if ((m_reg_ptr <= 10) || (m_reg_ptr == 15)) + m_address_regs[m_reg_ptr - 8]++; + else + m_address_regs[m_reg_ptr - 8] += 2; + m_reg_access_phase = 3; + break; + + case 3: + m_reg_access_phase = 0; + break; + } +} + + +//------------------------------------------------- +// 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); + } + 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) + { + memcpy (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; +} |