// license:BSD-3-Clause
// copyright-holders:Wilbert Pol
/**********************************************************************
NEC HuC6270 Video Display Controller
The HuC6270 basically outputs a 9-bit stream of pixel data which
holds a color index, a palette index, and an indication whether
the pixel contains background data or from sprite data.
This data can be used by a colour encoder to output graphics.
A regular screen is displayed as follows:
|<- HDS ->|<-- HDW -->|<- HDE ->|<- HSW ->|
|---------|-----------------------|---------|---------|
VSW | |
|---------|-----------------------|---------|---------|
VDS | |
| overscan |
|---------|-----------------------|---------|---------|
| | | |
| | | |
| | | |
| | | |
VDW | overscan| active display | overscan |
| | | |
| | | |
| | | |
| | | |
|---------|-----------------------|---------|---------|
VCR | overscan |
| |
|---------|-----------------------|---------|---------|
^end hsync
^start vsync (30 cycles after hsync)
KNOWN ISSUES
- Violent Soldier (probably connected):
- In the intro some artefacts appear at the top of the
screen every now and then.
- In ccovell's splitres test not all sections seem to be aligned properly.
- Side Arms: Seems to be totally broken.
TODO
- Fix timing of VRAM-SATB DMA
- Implement VRAM-VRAM DMA
- DMA speeds differ depending on the dot clock selected in the huc6270
- Convert VRAM bus to actual space address (optimization)
**********************************************************************/
#include "emu.h"
#include "huc6270.h"
//#define VERBOSE 1
#include "logmacro.h"
enum {
MAWR = 0x00,
MARR = 0x01,
VxR = 0x02,
CR = 0x05,
RCR = 0x06,
BXR = 0x07,
BYR = 0x08,
MWR = 0x09,
HSR = 0x0A,
HDR = 0x0B,
VPR = 0x0C,
VDW = 0x0D,
VCR = 0x0E,
DCR = 0x0F,
SOUR = 0x10,
DESR = 0x11,
LENR = 0x12,
DVSSR = 0x13
};
ALLOW_SAVE_TYPE(huc6270_device::v_state);
ALLOW_SAVE_TYPE(huc6270_device::h_state);
/* Bits in the VDC status register */
#define HUC6270_BSY 0x40 /* Set when the VDC accesses VRAM */
#define HUC6270_VD 0x20 /* Set when in the vertical blanking period */
#define HUC6270_DV 0x10 /* Set when a VRAM > VRAM DMA transfer is done */
#define HUC6270_DS 0x08 /* Set when a VRAM > SATB DMA transfer is done */
#define HUC6270_RR 0x04 /* Set when the current scanline equals the RCR register */
#define HUC6270_OR 0x02 /* Set when there are more than 16 sprites on a line */
#define HUC6270_CR 0x01 /* Set when sprite #0 overlaps with another sprite */
DEFINE_DEVICE_TYPE(HUC6270, huc6270_device, "huc6270", "Hudson HuC6270 VDC")
constexpr uint8_t huc6270_device::vram_increments[4];
huc6270_device::huc6270_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock)
: device_t(mconfig, HUC6270, tag, owner, clock)
, m_vram_size(0)
, m_irq_changed_cb(*this)
{
}
/*
Read one row of tile data from video ram
*/
inline void huc6270_device::fetch_bat_tile_row()
{
const uint16_t bat_data = m_vram[ m_bat_address & m_vram_mask ];
const uint16_t tile_palette = ( bat_data >> 8 ) & 0xF0;
uint16_t data1 = m_vram[ ( ( ( bat_data & 0x0FFF ) << 4 ) + m_bat_row + 0 ) & m_vram_mask ];
uint16_t data2 = ( data1 >> 7 ) & 0x1FE;
uint16_t data3 = m_vram[ ( ( ( bat_data & 0x0FFF ) << 4 ) + m_bat_row + 8 ) & m_vram_mask ];
uint16_t data4 = ( data3 >> 5 ) & 0x7F8;
data3 <<= 2;
for ( int i = 7; i >= 0; i-- )
{
uint16_t c = ( data1 & 0x01 ) | ( data2 & 0x02 ) | ( data3 & 0x04 ) | ( data4 & 0x08 );
/* Colour 0 for background tiles is always taken from palette 0 */
if ( c )
c |= tile_palette;
m_bat_tile_row[i] = c;
data1 >>= 1;
data2 >>= 1;
data3 >>= 1;
data4 >>= 1;
}
}
void huc6270_device::add_sprite( int index, int x, int pattern, int line, int flip_x, int palette, int priority, int sat_lsb )
{
int i = m_sprites_this_line;
if ( i < 16 )
{
uint32_t b0, b1, b2, b3;
int j;
if ( flip_x )
flip_x = 0x0F;
pattern += ( ( line >> 4 ) << 1 );
if ( ( m_mwr & 0x0c ) == 0x04 )
{
if ( ! sat_lsb )
{
b0 = m_vram[ ( ( pattern * 0x40 ) + ( line & 0x0F ) + 0x00 ) & m_vram_mask ];
b1 = m_vram[ ( ( pattern * 0x40 ) + ( line & 0x0F ) + 0x10 ) & m_vram_mask ] << 1;
}
else
{
b0 = m_vram[ ( ( pattern * 0x40 ) + ( line & 0x0F ) + 0x20 ) & m_vram_mask ];
b1 = m_vram[ ( ( pattern * 0x40 ) + ( line & 0x0F ) + 0x30 ) & m_vram_mask ] << 1;
}
b2 = 0;
b3 = 0;
}
else
{
b0 = m_vram[ ( ( pattern * 0x40 ) + ( line & 0x0F ) + 0x00 ) & m_vram_mask ];
b1 = m_vram[ ( ( pattern * 0x40 ) + ( line & 0x0F ) + 0x10 ) & m_vram_mask ] << 1;
b2 = m_vram[ ( ( pattern * 0x40 ) + ( line & 0x0F ) + 0x20 ) & m_vram_mask ] << 2;
b3 = m_vram[ ( ( pattern * 0x40 ) + ( line & 0x0F ) + 0x30 ) & m_vram_mask ] << 3;
}
for ( j = 15; j >= 0; j-- )
{
uint8_t data = ( b3 & 0x08 ) | ( b2 & 0x04 ) | ( b1 & 0x02 ) | ( b0 & 0x01 );
if ( data )
{
data |= palette << 4;
if ( x + ( j ^ flip_x ) < 1024 )
{
if (! m_sprite_row[ x + ( j ^ flip_x ) ] )
{
m_sprite_row[ x + ( j ^ flip_x ) ] = ( priority ? 0x4000 : 0x0000 ) | ( index << 8 ) | data;
}
else
{
if ( ! ( m_sprite_row[ x + ( j ^ flip_x ) ] & 0xFF00 ) )
{
/* Sprite 0 collission */
m_sprite_row[ x + ( j ^ flip_x ) ] |= 0x8000;
}
}
}
}
b0 >>= 1;
b1 >>= 1;
b2 >>= 1;
b3 >>= 1;
}
m_sprites_this_line += 1;
}
}
void huc6270_device::select_sprites()
{
int i;
m_sprites_this_line = 0;
memset( m_sprite_row, 0, sizeof( m_sprite_row ) );
m_sprite_row_index = 0x20;
for ( i = 0; i < 4 * 64; i += 4 )
{
static const int cgy_table[4] = { 16, 32, 64, 64 };
int cgy = ( m_sat[i+3] >> 12 ) & 0x03;
int height = cgy_table[ cgy ];
int sprite_line = m_raster_count - m_sat[i];
if ( sprite_line >= 0 && sprite_line < height )
{
int pattern = m_sat[i+2] >> 1;
int sat_lsb = m_sat[i+2] & 0x01;
int palette = m_sat[i+3] & 0x0F;
int priority = m_sat[i+3] & 0x80;
int cgx = m_sat[i+3] & 0x0100;
/* If CGY is set to 1, bit 1 of the sprite pattern index is forced to 0 */
if ( cgy & 1 )
pattern &= ~0x0002;
/* If CGY is set to 2 or 3, bits 1 and 2 of the sprite pattern index are forced to 0 */
if ( cgy & 2 )
pattern &= ~0x0006;
/* Recalculate line index when sprite is flipped vertically */
if ( m_sat[i+3] & 0x8000 )
sprite_line = ( height - 1 ) - sprite_line;
/* Is the sprite 32 pixels wide */
if ( cgx )
{
/* If CGX is set, bit 0 of the sprite pattern index is forced to 0 */
pattern &= ~0x0001;
/* Check for horizontal flip */
if ( m_sat[i+3] & 0x0800 )
{
/* Add to our list of sprites for this line */
add_sprite( i/4, m_sat[i+1], pattern + 1, sprite_line, 1, palette, priority, sat_lsb );
add_sprite( i/4, m_sat[i+1] + 16, pattern, sprite_line, 1, palette, priority, sat_lsb );
}
else
{
/* Add to our list of sprites for this line */
add_sprite( i/4, m_sat[i+1], pattern, sprite_line, 0, palette, priority, sat_lsb );
add_sprite( i/4, m_sat[i+1] + 16, pattern + 1, sprite_line, 0, palette, priority, sat_lsb );
}
}
else
{
/* Add to our list of sprites for this line */
add_sprite( i/4, m_sat[i+1], pattern, sprite_line, m_sat[i+3] & 0x0800, palette, priority, sat_lsb );
}
}
}
/* Check for sprite overflow */
if ( m_sprites_this_line >= 16 )
{
/* note: flag is set only if irq is taken, Mizubaku Daibouken relies on this behaviour */
if ( m_cr & 0x02 )
{
m_status |= HUC6270_OR;
m_irq_changed_cb( ASSERT_LINE );
}
}
}
inline void huc6270_device::handle_vblank()
{
if ( ! m_vd_triggered )
{
if ( m_cr & 0x08 )
{
m_status |= HUC6270_VD;
m_irq_changed_cb( ASSERT_LINE );
}
/* Should we initiate a VRAM->SATB DMA transfer.
The timing for this is incorrect.
*/
if ( m_dvssr_written || ( m_dcr & 0x10 ) )
{
int i;
LOG("SATB transfer from %05x\n", m_dvssr << 1 );
for ( i = 0; i < 4 * 64; i += 4 )
{
m_sat[i + 0] = m_vram[ ( m_dvssr + i + 0 ) & m_vram_mask ] & 0x03FF;
m_sat[i + 1] = m_vram[ ( m_dvssr + i + 1 ) & m_vram_mask ] & 0x03FF;
m_sat[i + 2] = m_vram[ ( m_dvssr + i + 2 ) & m_vram_mask ] & 0x07FF;
m_sat[i + 3] = m_vram[ ( m_dvssr + i + 3 ) & m_vram_mask ];
}
m_dvssr_written = 0;
/* Generate SATB interrupt if requested */
if ( m_dcr & 0x01 )
{
m_satb_countdown = 4;
// m_status |= HUC6270_DS;
// m_irq_changed_cb( ASSERT_LINE );
}
}
m_vd_triggered = 1;
}
}
inline void huc6270_device::next_vert_state()
{
switch ( m_vert_state )
{
case v_state::VSW:
m_vert_state = v_state::VDS;
m_vert_to_go = ( ( m_vpr >> 8 ) & 0xFF ) + 2;
break;
case v_state::VDS:
m_vert_state = v_state::VDW;
m_vert_to_go = ( m_vdw & 0x1FF ) + 1;
m_byr_latched = m_byr;
m_vd_triggered = 0;
break;
case v_state::VDW:
m_vert_state = v_state::VCR;
m_vert_to_go = ( m_vcr & 0xFF );
handle_vblank();
break;
case v_state::VCR:
m_vert_state = v_state::VSW;
m_vert_to_go = ( m_vpr & 0x1F ) + 1;
break;
}
}
inline void huc6270_device::next_horz_state()
{
switch ( m_horz_state )
{
case h_state::HDS:
m_bxr_latched = m_bxr;
m_horz_state = h_state::HDW;
m_horz_to_go = ( m_hdr & 0x7F ) + 1;
{
static const int width_shift[4] = { 5, 6, 7, 7 };
uint16_t v;
v = ( m_byr_latched ) & ( ( m_mwr & 0x40 ) ? 0x1FF : 0xFF );
m_bat_row = v & 7;
m_bat_address_mask = ( 1 << width_shift[ ( m_mwr >> 4 ) & 0x03 ] ) - 1;
m_bat_address = ( ( v >> 3 ) << ( width_shift[ ( m_mwr >> 4 ) & 0x03 ] ) )
| ( ( m_bxr_latched >> 3 ) & m_bat_address_mask );
m_bat_column = m_bxr & 7;
fetch_bat_tile_row();
}
break;
case h_state::HDW:
m_horz_state = h_state::HDE;
m_horz_to_go = ( ( m_hdr >> 8 ) & 0x7F ) + 1;
break;
case h_state::HDE:
m_horz_state = h_state::HSW;
m_horz_to_go = ( m_hsr & 0x1F ) + 1;
break;
case h_state::HSW:
m_horz_state = h_state::HDS;
m_horz_to_go = std::max( ( ( m_hsr >> 8 ) & 0x7F ), 2 ) + 1;
/* If section has ended, advance to next vertical state */
while ( m_vert_to_go == 0 )
next_vert_state();
/* Select sprites for the coming line */
select_sprites();
break;
}
m_horz_steps = 0;
}
u16 huc6270_device::next_pixel()
{
uint16_t data = HUC6270_SPRITE;
/* Check if we're on an active display line */
if ( m_vert_state == v_state::VDW )
{
/* Check if we're in active display area */
if ( m_horz_state == h_state::HDW )
{
uint8_t sprite_data = m_sprite_row[ m_sprite_row_index ] & 0x00FF;
int collission = ( m_sprite_row[ m_sprite_row_index ] & 0x8000 ) ? 1 : 0;
if ( m_cr & 0x80 )
{
data = HUC6270_BACKGROUND | m_bat_tile_row[ m_bat_column ];
if ( sprite_data && ( m_cr & 0x40 ) )
{
if ( m_sprite_row[ m_sprite_row_index ] & 0x4000 )
{
data = HUC6270_SPRITE | sprite_data;
}
else
{
if ( data == HUC6270_BACKGROUND )
{
data = HUC6270_SPRITE | sprite_data;
}
}
}
}
else
{
if ( m_cr & 0x40 )
{
data = HUC6270_SPRITE | sprite_data;
}
}
m_sprite_row_index = m_sprite_row_index + 1;
m_bat_column += 1;
if ( m_bat_column >= 8 )
{
m_bat_address = ( m_bat_address & ~m_bat_address_mask )
| ( ( m_bat_address + 1 ) & m_bat_address_mask );
m_bat_column = 0;
fetch_bat_tile_row();
}
if ( collission && ( m_cr & 0x01 ) )
{
m_status |= HUC6270_CR;
m_irq_changed_cb( ASSERT_LINE );
}
}
}
m_horz_steps++;
if ( m_horz_steps == 8 )
{
m_horz_to_go -= 1;
m_horz_steps = 0;
while ( m_horz_to_go == 0 )
next_horz_state();
}
return data;
}
//inline u16 huc6270_device::time_until_next_event()
//{
// return m_horz_to_go * 8 + m_horz_steps;
//}
WRITE_LINE_MEMBER( huc6270_device::vsync_changed )
{
state &= 0x01;
if ( m_vsync != state )
{
/* Check for high->low VSYNC transition */
if ( !state )
{
m_vert_state = v_state::VCR;
m_vert_to_go = 0;
while ( m_vert_to_go == 0 )
next_vert_state();
}
else
{
/* Check for low->high VSYNC transition */
// VBlank IRQ happens at the beginning of HDW period after VDW ends
handle_vblank();
}
}
m_vsync = state;
}
WRITE_LINE_MEMBER( huc6270_device::hsync_changed )
{
state &= 0x01;
if(m_hsync != state)
{
/* Check for low->high HSYNC transition */
if(state)
{
if ( m_satb_countdown )
{
m_satb_countdown--;
if ( m_satb_countdown == 0 )
{
m_status |= HUC6270_DS;
m_irq_changed_cb( ASSERT_LINE );
}
}
m_horz_state = h_state::HSW;
m_horz_to_go = 0;
m_horz_steps = 0;
m_byr_latched += 1;
m_raster_count += 1;
if ( m_vert_to_go == 1 && m_vert_state == v_state::VDS )
{
m_raster_count = 0x40;
}
m_vert_to_go -= 1;
while ( m_horz_to_go == 0 )
next_horz_state();
handle_dma();
}
else
{
/* Check for high->low HSYNC transition */
// RCR IRQ happens near the end of the HDW period
if ( m_raster_count == m_rcr && ( m_cr & 0x04 ) )
{
m_status |= HUC6270_RR;
m_irq_changed_cb( ASSERT_LINE );
}
}
}
m_hsync = state;
}
inline void huc6270_device::handle_dma()
{
/* Should we perform VRAM-VRAM dma.
The timing for this is incorrect.
*/
if ( m_dma_enabled )
{
int desr_inc = ( m_dcr & 0x0008 ) ? -1 : +1;
int sour_inc = ( m_dcr & 0x0004 ) ? -1 : +1;
LOG("doing dma sour = %04x, desr = %04x, lenr = %04x\n", m_sour, m_desr, m_lenr );
do {
uint16_t data;
// area 0x8000-0xffff cannot be r/w (open bus)
if(m_sour <= m_vram_mask)
data = m_vram[ m_sour ];
else
data = 0;
if(m_desr <= m_vram_mask)
m_vram[ m_desr ] = data;
m_sour += sour_inc;
m_desr += desr_inc;
m_lenr -= 1;
} while ( m_lenr != 0xFFFF );
if ( m_dcr & 0x0002 )
{
m_status |= HUC6270_DV;
m_irq_changed_cb( ASSERT_LINE );
}
m_dma_enabled = 0;
}
}
u8 huc6270_device::read(offs_t offset)
{
uint8_t data = 0x00;
switch ( offset & 3 )
{
case 0x00: /* status */
data = m_status;
m_status &= ~( HUC6270_VD | HUC6270_DV | HUC6270_RR | HUC6270_CR | HUC6270_OR | HUC6270_DS );
m_irq_changed_cb( CLEAR_LINE );
break;
case 0x02:
data = m_vrr & 0xFF;
break;
case 0x03:
data = m_vrr >> 8;
if ( m_register_index == VxR )
{
m_marr += vram_increments[ ( m_cr >> 11 ) & 3 ];
if(m_marr <= m_vram_mask)
m_vrr = m_vram[ m_marr ];
else
{
// TODO: test with real HW
m_vrr = 0;
logerror("%s Open Bus VRAM read (register read) %04x\n",this->tag(),m_marr);
}
}
break;
}
return data;
}
void huc6270_device::write(offs_t offset, u8 data)
{
LOG("%s: huc6270 write %02x <- %02x ", machine().describe_context(), offset, data);
switch ( offset & 3 )
{
case 0x00: /* VDC register select */
m_register_index = data & 0x1F;
break;
case 0x02: /* VDC data LSB */
switch ( m_register_index )
{
case MAWR: /* memory address write register LSB */
m_mawr = ( m_mawr & 0xFF00 ) | data;
break;
case MARR: /* memory address read register LSB */
m_marr = ( m_marr & 0xFF00 ) | data;
if(m_marr <= m_vram_mask)
m_vrr = m_vram[ m_marr ];
else
{
// TODO: test with real HW
m_vrr = 0;
logerror("%s Open Bus VRAM read (memory address) %04x\n",this->tag(),m_marr);
}
break;
case VxR: /* vram write data LSB */
m_vwr = ( m_vwr & 0xFF00 ) | data;
break;
case CR: /* control register LSB */
m_cr = ( m_cr & 0xFF00 ) | data;
break;
case RCR: /* raster compare register LSB */
m_rcr = ( m_rcr & 0x0300 ) | data;
// if ( m_raster_count == m_rcr && m_cr & 0x04 )
// {
// m_status |= HUC6270_RR;
// m_irq_changed_cb( ASSERT_LINE );
// }
break;
case BXR: /* background x-scroll register LSB */
m_bxr = ( m_bxr & 0x0300 ) | data;
break;
case BYR: /* background y-scroll register LSB */
m_byr = ( m_byr & 0x0100 ) | data;
m_byr_latched = m_byr;
break;
case MWR: /* memory width register LSB */
m_mwr = ( m_mwr & 0xFF00 ) | data;
break;
case HSR: /* horizontal sync register LSB */
m_hsr = ( m_hsr & 0xFF00 ) | data;
break;
case HDR: /* horizontal display register LSB */
m_hdr = ( m_hdr & 0xFF00 ) | data;
break;
case VPR: /* vertical sync register LSB */
m_vpr = ( m_vpr & 0xFF00 ) | data;
break;
case VDW: /* vertical display register LSB */
m_vdw = ( m_vdw & 0xFF00 ) | data;
break;
case VCR: /* vertical display end position register LSB */
m_vcr = ( m_vcr & 0xFF00 ) | data;
break;
case DCR: /* DMA control register LSB */
m_dcr = ( m_dcr & 0xFF00 ) | data;
break;
case SOUR: /* DMA source address register LSB */
m_sour = ( m_sour & 0xFF00 ) | data;
break;
case DESR: /* DMA destination address register LSB */
m_desr = ( m_desr & 0xFF00 ) | data;
break;
case LENR: /* DMA length register LSB */
m_lenr = ( m_lenr & 0xFF00 ) | data;
break;
case DVSSR: /* Sprite attribute table LSB */
m_dvssr = ( m_dvssr & 0xFF00 ) | data;
m_dvssr_written = 1;
break;
}
break;
case 0x03: /* VDC data MSB */
switch ( m_register_index )
{
case MAWR: /* memory address write register MSB */
m_mawr = ( m_mawr & 0x00FF ) | ( data << 8 );
break;
case MARR: /* memory address read register MSB */
m_marr = ( m_marr & 0x00FF ) | ( data << 8 );
if(m_marr <= m_vram_mask)
m_vrr = m_vram[ m_marr ];
else
m_vrr = 0;
break;
case VxR: /* vram write data MSB */
m_vwr = ( m_vwr & 0x00FF ) | ( data << 8 );
// area 0x8000-0xffff is NOP and cannot be written to.
if(m_mawr <= m_vram_mask)
m_vram[ m_mawr ] = m_vwr;
m_mawr += vram_increments[ ( m_cr >> 11 ) & 3 ];
break;
case CR: /* control register MSB */
m_cr = ( m_cr & 0x00FF ) | ( data << 8 );
break;
case RCR: /* raster compare register MSB */
m_rcr = ( m_rcr & 0x00FF ) | ( ( data & 0x03 ) << 8 );
//printf("%s: RCR set to %03x\n", machine().describe_context().c_str(), m_rcr);
// if ( m_raster_count == m_rcr && m_cr & 0x04 )
// {
// m_status |= HUC6270_RR;
// m_irq_changed_cb( ASSERT_LINE );
// }
break;
case BXR: /* background x-scroll register MSB */
m_bxr = ( m_bxr & 0x00FF ) | ( ( data & 0x03 ) << 8 );
break;
case BYR: /* background y-scroll register MSB */
m_byr = ( m_byr & 0x00FF ) | ( ( data & 0x01 ) << 8 );
m_byr_latched = m_byr;
break;
case MWR: /* memory width register MSB */
m_mwr = ( m_mwr & 0x00FF ) | ( data << 8 );
break;
case HSR: /* horizontal sync register MSB */
m_hsr = ( m_hsr & 0x00FF ) | ( data << 8 );
break;
case HDR: /* horizontal display register MSB */
m_hdr = ( m_hdr & 0x00FF ) | ( data << 8 );
break;
case VPR: /* vertical sync register MSB */
m_vpr = ( m_vpr & 0x00FF ) | ( data << 8 );
break;
case VDW: /* vertical display register MSB */
m_vdw = ( m_vdw & 0x00FF ) | ( data << 8 );
break;
case VCR: /* vertical display end position register MSB */
m_vcr = ( m_vcr & 0x00FF ) | ( data << 8 );
break;
case DCR: /* DMA control register MSB */
m_dcr = ( m_dcr & 0x00FF ) | ( data << 8 );
break;
case SOUR: /* DMA source address register MSB */
m_sour = ( m_sour & 0x00FF ) | ( data << 8 );
break;
case DESR: /* DMA destination address register MSB */
m_desr = ( m_desr & 0x00FF ) | ( data << 8 );
break;
case LENR: /* DMA length register MSB */
m_lenr = ( m_lenr & 0x00FF ) | ( data << 8 );
m_dma_enabled = 1;
//logerror("DMA is not supported yet.\n");
break;
case DVSSR: /* Sprite attribute table MSB */
m_dvssr = ( m_dvssr & 0x00FF ) | ( data << 8 );
m_dvssr_written = 1;
break;
}
break;
}
LOG("\n");
}
void huc6270_device::device_start()
{
/* Resolve callbacks */
m_irq_changed_cb.resolve_safe();
m_vram = make_unique_clear<uint16_t[]>(m_vram_size/sizeof(uint16_t));
m_vram_mask = (m_vram_size >> 1) - 1;
save_pointer(NAME(m_vram), m_vram_size/sizeof(uint16_t));
save_item(NAME(m_register_index));
save_item(NAME(m_mawr));
save_item(NAME(m_marr));
save_item(NAME(m_vrr));
save_item(NAME(m_vwr));
save_item(NAME(m_cr));
save_item(NAME(m_rcr));
save_item(NAME(m_bxr));
save_item(NAME(m_byr));
save_item(NAME(m_mwr));
save_item(NAME(m_hsr));
save_item(NAME(m_hdr));
save_item(NAME(m_vpr));
save_item(NAME(m_vdw));
save_item(NAME(m_vcr));
save_item(NAME(m_dcr));
save_item(NAME(m_sour));
save_item(NAME(m_desr));
save_item(NAME(m_lenr));
save_item(NAME(m_dvssr));
save_item(NAME(m_status));
save_item(NAME(m_hsync));
save_item(NAME(m_vsync));
save_item(NAME(m_vert_state));
save_item(NAME(m_horz_state));
save_item(NAME(m_vd_triggered));
save_item(NAME(m_vert_to_go));
save_item(NAME(m_horz_to_go));
save_item(NAME(m_horz_steps));
save_item(NAME(m_raster_count));
save_item(NAME(m_dvssr_written));
save_item(NAME(m_satb_countdown));
save_item(NAME(m_dma_enabled));
save_item(NAME(m_byr_latched));
save_item(NAME(m_bxr_latched));
save_item(NAME(m_bat_address));
save_item(NAME(m_bat_address_mask));
save_item(NAME(m_bat_row));
save_item(NAME(m_bat_column));
save_item(NAME(m_bat_tile_row));
save_item(NAME(m_sat));
save_item(NAME(m_sprites_this_line));
save_item(NAME(m_sprite_row_index));
save_item(NAME(m_sprite_row));
}
void huc6270_device::device_reset()
{
m_mawr = 0;
m_marr = 0;
m_vrr = 0;
m_vwr = 0;
m_cr = 0;
m_rcr = 0;
m_bxr = 0;
m_byr = 0;
m_mwr = 0;
m_hsr = 0x0202; /* Take some defaults for horizontal timing */
m_hdr = 0x041f;
m_vpr = 0x0f02; /* Take some defaults for vertical timing */
m_vdw = 0x00ef;
m_vcr = 0x0004;
m_dcr = 0;
m_sour = 0;
m_lenr = 0;
m_dvssr = 0;
m_status = 0;
m_vd_triggered = 0;
m_dvssr_written = 0;
m_satb_countdown = 0;
m_raster_count = 0x4000;
m_vert_to_go = 0;
m_vert_state = v_state::VSW;
m_horz_steps = 0;
m_horz_to_go = 0;
m_horz_state = h_state::HDS;
m_hsync = 0;
m_vsync = 0;
m_dma_enabled = 0;
m_byr_latched = 0;
memset(m_sat, 0, sizeof(m_sat));
}