// license:BSD-3-Clause // copyright-holders:Sean Young, Nathan Woods, Aaron Giles, Wilbert Pol, hap /* ** 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://bifi.msxnet.org/msxnet/tech/tms9918a.txt ** ** By Sean Young 1999 (sean@msxnet.org). ** Based on code by Mike Balfour. ** Improved over the years by MESS and MAME teams. ** ** Todo: ** - External VDP input and sync (pin 34/35 on 9918A) ** - Updates during mid-scanline, probably only used in some MSX1 demos ** - Colours are incorrect. [fixed by R Nabet ?] ** - Sprites 8-31 are ghosted/cloned in mode 3 when using less than ** three pattern tables. Exact behaviour is not known. ** - Address scrambling when setting TMS99xxA to 4K (not on TMS91xx) */ #include "emu.h" #include "tms9928a.h" DEFINE_DEVICE_TYPE(TMS9928A, tms9928a_device, "tms9928a", "TMS9928A VDP") DEFINE_DEVICE_TYPE(TMS9918, tms9918_device, "tms9918", "TMS9918 VDP") DEFINE_DEVICE_TYPE(TMS9918A, tms9918a_device, "tms9918a", "TMS9918A VDP") DEFINE_DEVICE_TYPE(TMS9118, tms9118_device, "tms9118", "TMS9118 VDP") DEFINE_DEVICE_TYPE(TMS9128, tms9128_device, "tms9128", "TMS9128 VDP") DEFINE_DEVICE_TYPE(TMS9929, tms9929_device, "tms9929", "TMS9929 VDP") DEFINE_DEVICE_TYPE(TMS9929A, tms9929a_device, "tms9929a", "TMS9929A VDP") DEFINE_DEVICE_TYPE(TMS9129, tms9129_device, "tms9129", "TMS9129 VDP") DEFINE_DEVICE_TYPE(EFO90501, efo90501_device, "efo90501", "EFO90501 VDP") // ======= Debugging ========= // Log register accesses #define TRACE_REG 0 // Log mode settings #define TRACE_MODE 0 // =========================== /* The TMS9928 has an own address space. */ void tms9928a_device::memmap(address_map &map) { if (!has_configured_map(0)) map(0x0000, 0x3fff).ram(); } tms9928a_device::tms9928a_device(const machine_config &mconfig, device_type type, const char *tag, device_t *owner, uint32_t clock, uint16_t horz_total, bool is_50hz, bool is_reva, bool is_99) : device_t(mconfig, type, tag, owner, clock) , device_memory_interface(mconfig, *this) , device_palette_interface(mconfig, *this) , device_video_interface(mconfig, *this) , m_vram_size(0) , m_out_int_line_cb(*this) , m_out_gromclk_cb(*this) , m_total_horz(horz_total) , m_50hz(is_50hz) , m_reva(is_reva) , m_99(is_99) , m_space_config("vram", ENDIANNESS_BIG, 8, 14, 0, address_map_constructor(FUNC(tms9928a_device::memmap), this)) { } void tms9928a_device::device_config_complete() { if (!has_screen()) return; if (!screen().has_screen_update()) screen().set_screen_update(*this, FUNC(tms9928a_device::screen_update)); if (!screen().refresh_attoseconds()) { if (m_50hz) screen().set_raw(clock() / 2, m_total_horz, HORZ_DISPLAY_START - 12, HORZ_DISPLAY_START + 256 + 12, TOTAL_VERT_PAL, VERT_DISPLAY_START_PAL - 12, VERT_DISPLAY_START_PAL + 192 + 12); else screen().set_raw(clock() / 2, m_total_horz, HORZ_DISPLAY_START - 12, HORZ_DISPLAY_START + 256 + 12, TOTAL_VERT_NTSC, VERT_DISPLAY_START_NTSC - 12, VERT_DISPLAY_START_NTSC + 192 + 12); } } tms9928a_device::tms9928a_device( const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock) : tms9928a_device(mconfig, TMS9928A, tag, owner, clock, 342, false, true, true) { } tms9129_device::tms9129_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock) : tms9928a_device(mconfig, TMS9129, tag, owner, clock, 342, true, true, false) { } tms9918_device::tms9918_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock) : tms9928a_device(mconfig, TMS9918, tag, owner, clock, 342, false, false, true) { } tms9918a_device::tms9918a_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock) : tms9928a_device(mconfig, TMS9918A, tag, owner, clock, 342, false, true, true) { } tms9118_device::tms9118_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock) : tms9928a_device(mconfig, TMS9118, tag, owner, clock, 342, false, true, false) { } tms9128_device::tms9128_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock) : tms9928a_device(mconfig, TMS9128, tag, owner, clock, 342, false, true, false) { } tms9929_device::tms9929_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock) : tms9928a_device(mconfig, TMS9929, tag, owner, clock, 342, true, false, true) { } tms9929a_device::tms9929a_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock) : tms9928a_device(mconfig, TMS9929A, tag, owner, clock, 342, true, true, true) { } efo90501_device::efo90501_device( const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock) : tms9928a_device(mconfig, EFO90501, tag, owner, clock, 346, true, true, true) { } device_memory_interface::space_config_vector tms9928a_device::memory_space_config() const { return space_config_vector { std::make_pair(AS_DATA, &m_space_config) }; } uint8_t tms9928a_device::read(offs_t offset) { uint8_t value = 0; if ((offset & 1) == 0) value = vram_read(); else value = register_read(); return value; } void tms9928a_device::write(offs_t offset, uint8_t data) { if ((offset & 1) == 0) vram_write(data); else register_write(data); } u8 tms9928a_device::vram_read() { uint8_t data = m_ReadAhead; // prevent debugger from changing the address base if (machine().side_effects_disabled()) return data; m_ReadAhead = m_vram_space->read_byte(m_Addr); m_Addr = (m_Addr + 1) & (m_vram_size - 1); m_latch = 0; return data; } void tms9928a_device::vram_write(u8 data) { m_vram_space->write_byte(m_Addr, data); // prevent debugger from changing the address base if (!machine().side_effects_disabled()) { m_Addr = (m_Addr + 1) & (m_vram_size - 1); m_ReadAhead = data; m_latch = 0; } } u8 tms9928a_device::register_read() { uint8_t data = m_StatusReg; // prevent debugger from changing the internal state if (machine().side_effects_disabled()) return data; m_StatusReg = m_FifthSprite; check_interrupt(); m_latch = 0; return data; } void tms9928a_device::check_interrupt() { // trigger if vblank and interrupt-enable bits are set uint8_t b = (m_StatusReg & 0x80 && m_Regs[1] & 0x20) ? 1 : 0; if (b != m_INT) { m_INT = b; m_out_int_line_cb(m_INT); } } void tms9928a_device::update_backdrop() { // update backdrop colour to transparent if EXTVID bit is set if ((m_Regs[7] & 15) == 0) set_pen_color(0, rgb_t(m_Regs[0] & 1 ? 0 : 255,0,0,0)); } void tms9928a_device::update_table_masks() { m_colourmask = ( (m_Regs[3] & 0x7f) << 3 ) | 7; // on 91xx family, the colour table mask doesn't affect the pattern table mask m_patternmask = ( (m_Regs[4] & 3) << 8 ) | ( m_99 ? (m_colourmask & 0xff) : 0xff ); } void tms9928a_device::change_register(uint8_t reg, uint8_t val) { static const uint8_t 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_t prev = m_Regs[reg]; val &= Mask[reg]; m_Regs[reg] = val; if (TRACE_REG) logerror("TMS9928A('%s'): Reg %d = %02xh\n", tag(), reg, (int)val); switch (reg) { case 0: /* re-calculate masks and pattern generator & colour */ if (val & 2) { m_colour = ((m_Regs[3] & 0x80) * 64) & (m_vram_size - 1); m_pattern = ((m_Regs[4] & 4) * 2048) & (m_vram_size - 1); update_table_masks(); } else { m_colour = (m_Regs[3] * 64) & (m_vram_size - 1); m_pattern = (m_Regs[4] * 2048) & (m_vram_size - 1); } m_mode = ( (m_reva ? (m_Regs[0] & 2) : 0) | ((m_Regs[1] & 0x10)>>4) | ((m_Regs[1] & 8)>>1)); if ((val ^ prev) & 1) update_backdrop(); if (TRACE_MODE) logerror("TMS9928A('%s'): %s\n", tag(), modes[m_mode]); break; case 1: check_interrupt(); m_mode = ( (m_reva ? (m_Regs[0] & 2) : 0) | ((m_Regs[1] & 0x10)>>4) | ((m_Regs[1] & 8)>>1)); if (TRACE_MODE) logerror("TMS9928A('%s'): %s\n", tag(), modes[m_mode]); break; case 2: m_nametbl = (val * 1024) & (m_vram_size - 1); break; case 3: if (m_Regs[0] & 2) { m_colour = ((val & 0x80) * 64) & (m_vram_size - 1); update_table_masks(); } else { m_colour = (val * 64) & (m_vram_size - 1); } break; case 4: if (m_Regs[0] & 2) { m_pattern = ((val & 4) * 2048) & (m_vram_size - 1); update_table_masks(); } else { m_pattern = (val * 2048) & (m_vram_size - 1); } break; case 5: m_spriteattribute = (val * 128) & (m_vram_size - 1); break; case 6: m_spritepattern = (val * 2048) & (m_vram_size - 1); break; case 7: if ((val ^ prev) & 15) update_backdrop(); break; } } void tms9928a_device::register_write(u8 data) { // prevent debugger from changing the internal state if (machine().side_effects_disabled()) return; if (m_latch) { /* set high part of read/write address */ m_Addr = ((data << 8) | (m_Addr & 0xff)) & (m_vram_size - 1); if (data & 0x80) { /* register write */ change_register (data & 7, m_Addr & 0xff); } else { if ( !(data & 0x40) ) { /* read ahead */ vram_read(); } } m_latch = 0; } else { /* set low part of read/write address */ m_Addr = ((m_Addr & 0xff00) | data) & (m_vram_size - 1); m_latch = 1; } } TIMER_CALLBACK_MEMBER(tms9928a_device::clock_grom) { m_out_gromclk_cb(ASSERT_LINE); m_out_gromclk_cb(CLEAR_LINE); } TIMER_CALLBACK_MEMBER(tms9928a_device::update_line) { int raw_vpos = screen().vpos(); int vpos = raw_vpos * m_vertical_size / screen().height(); uint16_t BackColour = m_Regs[7] & 15; uint32_t *p = &m_tmpbmp.pix(vpos); int y = vpos - m_top_border; if ( y < 0 || y >= 192 || ! (m_Regs[1] & 0x40) ) { /* Draw backdrop colour */ for ( int i = 0; i < m_total_horz; i++ ) p[i] = pen(BackColour); /* vblank is set at the last cycle of the first inactive line */ if ( y == 193 ) { m_StatusReg |= 0x80; check_interrupt(); } } else { /* Draw regular line */ /* Left border */ for ( int i = 0; i < HORZ_DISPLAY_START; i++ ) p[i] = pen(BackColour); /* Active display */ switch( m_mode ) { case 0: /* MODE 0 */ // if (vpos==100 ) popmessage("TMS9928A MODE 0"); { uint16_t addr = m_nametbl + ( ( y & 0xF8 ) << 2 ); for ( int x = HORZ_DISPLAY_START; x < HORZ_DISPLAY_START + 256; x+= 8, addr++ ) { uint8_t charcode = m_vram_space->read_byte( addr ); uint8_t pattern = m_vram_space->read_byte( m_pattern + ( charcode << 3 ) + ( y & 7 ) ); uint8_t colour = m_vram_space->read_byte( m_colour + ( charcode >> 3 ) ); rgb_t fg = pen((colour >> 4) ? (colour >> 4) : BackColour); rgb_t bg = pen((colour & 15) ? (colour & 15) : BackColour); for ( int i = 0; i < 8; pattern <<= 1, i++ ) p[x+i] = ( pattern & 0x80 ) ? fg : bg; } } break; case 1: /* MODE 1 */ //if (vpos==100 ) popmessage("TMS9928A MODE 1"); { uint16_t addr = m_nametbl + ( ( y >> 3 ) * 40 ); rgb_t fg = pen((m_Regs[7] >> 4) ? (m_Regs[7] >> 4) : BackColour); rgb_t bg = pen(BackColour); /* Extra 6 pixels left border */ for ( int x = HORZ_DISPLAY_START; x < HORZ_DISPLAY_START + 6; x++ ) p[x] = bg; for ( int x = HORZ_DISPLAY_START + 6; x < HORZ_DISPLAY_START + 246; x+= 6, addr++ ) { uint16_t charcode = m_vram_space->read_byte( addr ); uint8_t pattern = m_vram_space->read_byte( m_pattern + ( charcode << 3 ) + ( y & 7 ) ); for ( int i = 0; i < 6; pattern <<= 1, i++ ) p[x+i] = ( pattern & 0x80 ) ? fg : bg; } /* Extra 10 pixels right border */ for ( int x = HORZ_DISPLAY_START + 246; x < HORZ_DISPLAY_START + 256; x++ ) p[x] = bg; } break; case 2: /* MODE 2 */ //if (vpos==100 ) popmessage("TMS9928A MODE 2"); { uint16_t addr = m_nametbl + ( ( y >> 3 ) * 32 ); for ( int x = HORZ_DISPLAY_START; x < HORZ_DISPLAY_START + 256; x+= 8, addr++ ) { uint16_t charcode = m_vram_space->read_byte( addr ) + ( ( y >> 6 ) << 8 ); uint8_t pattern = m_vram_space->read_byte( m_pattern + ( ( charcode & m_patternmask ) << 3 ) + ( y & 7 ) ); uint8_t colour = m_vram_space->read_byte( m_colour + ( ( charcode & m_colourmask ) << 3 ) + ( y & 7 ) ); rgb_t fg = pen((colour >> 4) ? (colour >> 4) : BackColour); rgb_t bg = pen((colour & 15) ? (colour & 15) : BackColour); for ( int i = 0; i < 8; pattern <<= 1, i++ ) p[x+i] = ( pattern & 0x80 ) ? fg : bg; } } break; case 3: /* MODE 1+2 */ //if (vpos==100) popmessage("TMS9928A MODE1+2"); { uint16_t addr = m_nametbl + ( ( y >> 3 ) * 40 ); rgb_t fg = pen((m_Regs[7] >> 4) ? (m_Regs[7] >> 4) : BackColour); rgb_t bg = pen(BackColour); /* Extra 6 pixels left border */ for ( int x = HORZ_DISPLAY_START; x < HORZ_DISPLAY_START + 6; x++ ) p[x] = bg; for ( int x = HORZ_DISPLAY_START + 6; x < HORZ_DISPLAY_START + 246; x+= 6, addr++ ) { uint16_t charcode = ( m_vram_space->read_byte( addr ) + ( ( y >> 6 ) << 8 ) ) & m_patternmask; uint8_t pattern = m_vram_space->read_byte( m_pattern + ( charcode << 3 ) + ( y & 7 ) ); for ( int i = 0; i < 6; pattern <<= 1, i++ ) p[x+i] = ( pattern & 0x80 ) ? fg : bg; } /* Extra 10 pixels right border */ for ( int x = HORZ_DISPLAY_START + 246; x < HORZ_DISPLAY_START + 256; x++ ) p[x] = bg; } break; case 4: /* MODE 3 */ //if (vpos==100 ) popmessage("TMS9928A MODE 3"); { uint16_t addr = m_nametbl + ( ( y >> 3 ) * 32 ); for ( int x = HORZ_DISPLAY_START; x < HORZ_DISPLAY_START + 256; x+= 8, addr++ ) { uint8_t charcode = m_vram_space->read_byte( addr ); uint8_t colour = m_vram_space->read_byte( m_pattern + ( charcode << 3 ) + ( ( y >> 2 ) & 7 ) ); rgb_t fg = pen((colour >> 4) ? (colour >> 4) : BackColour); rgb_t bg = pen((colour & 15) ? (colour & 15) : BackColour); p[x+0] = p[x+1] = p[x+2] = p[x+3] = fg; p[x+4] = p[x+5] = p[x+6] = p[x+7] = bg; } } break; case 5: case 7: /* MODE bogus */ //if (vpos==100 ) popmessage("TMS9928A MODE bogus"); { rgb_t fg = pen((m_Regs[7] >> 4) ? (m_Regs[7] >> 4) : BackColour); rgb_t bg = pen(BackColour); /* Extra 6 pixels left border */ for ( int x = HORZ_DISPLAY_START; x < HORZ_DISPLAY_START + 6; x++ ) p[x] = bg; for ( int x = HORZ_DISPLAY_START + 6; x < HORZ_DISPLAY_START + 246; x+= 6 ) { p[x+0] = p[x+1] = p[x+2] = p[x+3] = fg; p[x+4] = p[x+5] = bg; } /* Extra 10 pixels right border */ for ( int x = HORZ_DISPLAY_START + 246; x < HORZ_DISPLAY_START + 256; x++ ) p[x] = bg; } break; case 6: /* MODE 2+3 */ //if (vpos==100 ) popmessage("TMS9928A MODE 2+3"); { uint16_t addr = m_nametbl + ( ( y >> 3 ) * 32 ); for ( int x = HORZ_DISPLAY_START; x < HORZ_DISPLAY_START + 256; x+= 8, addr++ ) { uint8_t charcode = m_vram_space->read_byte( addr ); uint8_t colour = m_vram_space->read_byte( m_pattern + ( ( ( charcode + ( ( y >> 2 ) & 7 ) + ( ( y >> 6 ) << 8 ) ) & m_patternmask ) << 3 ) ); rgb_t fg = pen((colour >> 4) ? (colour >> 4) : BackColour); rgb_t bg = pen((colour & 15) ? (colour & 15) : BackColour); p[x+0] = p[x+1] = p[x+2] = p[x+3] = fg; p[x+4] = p[x+5] = p[x+6] = p[x+7] = bg; } } break; } /* Draw sprites */ if ( ( m_Regs[1] & 0x50 ) != 0x40 ) { /* sprites are disabled */ m_FifthSprite = 31; } else { uint8_t sprite_size = ( m_Regs[1] & 0x02 ) ? 16 : 8; uint8_t sprite_mag = m_Regs[1] & 0x01; uint8_t sprite_height = sprite_size * ( sprite_mag + 1 ); uint8_t spr_drawn[32+256+32] = { 0 }; uint8_t num_sprites = 0; bool fifth_encountered = false; for ( uint16_t sprattr = 0; sprattr < 128; sprattr += 4 ) { int spr_y = m_vram_space->read_byte( m_spriteattribute + sprattr + 0 ); m_FifthSprite = sprattr / 4; /* Stop processing sprites */ if ( spr_y == 208 ) break; if ( spr_y > 0xE0 ) spr_y -= 256; /* vert pos 255 is displayed on the first line of the screen */ spr_y++; /* is sprite enabled on this line? */ if ( spr_y <= y && y < spr_y + sprite_height ) { int spr_x = m_vram_space->read_byte( m_spriteattribute + sprattr + 1 ); uint8_t sprcode = m_vram_space->read_byte( m_spriteattribute + sprattr + 2 ); uint8_t sprcol = m_vram_space->read_byte( m_spriteattribute + sprattr + 3 ); uint16_t pataddr = m_spritepattern + ( ( sprite_size == 16 ) ? sprcode & ~0x03 : sprcode ) * 8; num_sprites++; /* Fifth sprite encountered? */ if ( num_sprites == 5 ) { fifth_encountered = true; break; } if ( sprite_mag ) pataddr += ( ( ( y - spr_y ) & 0x1F ) >> 1 ); else pataddr += ( ( y - spr_y ) & 0x0F ); uint8_t pattern = m_vram_space->read_byte( pataddr ); if ( sprcol & 0x80 ) spr_x -= 32; sprcol &= 0x0f; for ( int s = 0; s < sprite_size; s += 8 ) { for ( int i = 0; i < 8; pattern <<= 1, i++ ) { int colission_index = spr_x + ( sprite_mag ? i * 2 : i ) + 32; for ( int z = 0; z <= sprite_mag; colission_index++, z++ ) { /* Check if pixel should be drawn */ if ( pattern & 0x80 ) { if ( colission_index >= 32 && colission_index < 32 + 256 ) { /* Check for colission */ if ( spr_drawn[ colission_index ] ) m_StatusReg |= 0x20; spr_drawn[ colission_index ] |= 0x01; if ( sprcol ) { /* Has another sprite already drawn here? */ if ( ! ( spr_drawn[ colission_index ] & 0x02 ) ) { spr_drawn[ colission_index ] |= 0x02; p[ HORZ_DISPLAY_START + colission_index - 32 ] = pen(sprcol); } } } } } } pattern = m_vram_space->read_byte( pataddr + 16 ); spr_x += sprite_mag ? 16 : 8; } } } /* Update sprite overflow bits */ if (~m_StatusReg & 0x40) { m_StatusReg = (m_StatusReg & 0xe0) | m_FifthSprite; if (fifth_encountered && ~m_StatusReg & 0x80) m_StatusReg |= 0x40; } } /* Right border */ for ( int i = HORZ_DISPLAY_START + 256; i < m_total_horz; i++ ) p[i] = pen(BackColour); } /* Schedule next callback */ m_line_timer->adjust( screen().time_until_pos( ( raw_vpos + 1 ) % screen().height() , HORZ_DISPLAY_START ) ); } uint32_t tms9928a_device::screen_update( screen_device &screen, bitmap_rgb32 &bitmap, const rectangle &cliprect ) { copybitmap( bitmap, m_tmpbmp, 0, 0, 0, 0, cliprect ); return 0; } void tms9928a_device::set_palette() { /* 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[PALETTE_SIZE] = { rgb_t::black(), rgb_t::black(), rgb_t(33, 200, 66), rgb_t(94, 220, 120), rgb_t(84, 85, 237), rgb_t(125, 118, 252), rgb_t(212, 82, 77), rgb_t(66, 235, 245), rgb_t(252, 85, 84), rgb_t(255, 121, 120), rgb_t(212, 193, 84), rgb_t(230, 206, 128), rgb_t(33, 176, 59), rgb_t(201, 91, 186), rgb_t(204, 204, 204), rgb_t::white() }; /* copy default palette into working palette */ for (int i = 0; i < PALETTE_SIZE; i++) { set_pen_color(i, tms9928a_palette[i]); } } void tms9928a_device::device_start() { m_top_border = m_50hz ? VERT_DISPLAY_START_PAL : VERT_DISPLAY_START_NTSC; m_vertical_size = m_50hz ? TOTAL_VERT_PAL : TOTAL_VERT_NTSC; // Video RAM is allocated as an own address space m_vram_space = &space(AS_DATA); /* back bitmap */ m_tmpbmp.allocate(m_total_horz, TOTAL_VERT_PAL); m_line_timer = timer_alloc(FUNC(tms9928a_device::update_line), this); m_gromclk_timer = timer_alloc(FUNC(tms9928a_device::clock_grom), this); m_INT = 1; // force initial update set_palette(); save_item(NAME(m_Regs[0])); save_item(NAME(m_Regs[1])); save_item(NAME(m_Regs[2])); save_item(NAME(m_Regs[3])); save_item(NAME(m_Regs[4])); save_item(NAME(m_Regs[5])); save_item(NAME(m_Regs[6])); save_item(NAME(m_Regs[7])); save_item(NAME(m_StatusReg)); save_item(NAME(m_FifthSprite)); save_item(NAME(m_ReadAhead)); save_item(NAME(m_latch)); save_item(NAME(m_Addr)); save_item(NAME(m_INT)); // save_pointer(NAME(m_vMem), m_vram_size); save_item(NAME(m_colour)); save_item(NAME(m_colourmask)); save_item(NAME(m_pattern)); save_item(NAME(m_patternmask)); save_item(NAME(m_nametbl)); save_item(NAME(m_spriteattribute)); save_item(NAME(m_spritepattern)); save_item(NAME(m_mode)); } void tms9928a_device::device_reset() { for (auto & elem : m_Regs) elem = 0; m_StatusReg = 0; m_FifthSprite = 31; m_nametbl = 0; m_pattern = 0; m_colour = 0; m_spritepattern = 0; m_spriteattribute = 0; m_colourmask = 0x3fff; m_patternmask = 0x3fff; m_Addr = 0; m_ReadAhead = 0; m_latch = 0; m_mode = 0; check_interrupt(); m_line_timer->adjust( screen().time_until_pos( 0, HORZ_DISPLAY_START ) ); // TODO: Check clock freq settings in all drivers if (!m_out_gromclk_cb.isunset() && m_99) m_gromclk_timer->adjust(attotime::zero, 0, clocks_to_attotime(24)); }