// 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));
}