// 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) { const int i = m_sprites_this_line; if (i < 16) { uint32_t b0, b1, b2, b3; 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 (int 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() { m_sprites_this_line = 0; memset(m_sprite_row, 0, sizeof(m_sprite_row)); m_sprite_row_index = 0x20; for (int i = 0; i < 4 * 64; i += 4) { static const int cgy_table[4] = { 16, 32, 64, 64 }; const int cgy = (m_sat[i + 3] >> 12) & 0x03; const int height = cgy_table[cgy]; // TODO: we are one line off in alignment, is following compensation right? // cfr. rennybla & draculax (at least), they are otherwise offset by 1 // compared to background. int sprite_line = m_raster_count - 1 - m_sat[i]; if (sprite_line >= 0 && sprite_line < height) { int pattern = m_sat[i + 2] >> 1; const bool sat_lsb = BIT(m_sat[i + 2], 0); const int palette = m_sat[i + 3] & 0x0f; const bool priority = BIT(m_sat[i + 3], 7); const bool cgx = BIT(m_sat[i + 3], 8); /* If CGY is set to 1, bit 1 of the sprite pattern index is forced to 0 */ if (BIT(cgy, 0)) pattern &= ~0x0002; /* If CGY is set to 2 or 3, bits 1 and 2 of the sprite pattern index are forced to 0 */ if (BIT(cgy, 1)) pattern &= ~0x0006; /* Recalculate line index when sprite is flipped vertically */ if (BIT(m_sat[i + 3], 15)) 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 (BIT(m_sat[i + 3], 11)) { /* 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, BIT(m_sat[i + 3], 11), 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 (BIT(m_cr, 1)) { m_status |= HUC6270_OR; m_irq_changed_cb(ASSERT_LINE); } } } inline void huc6270_device::handle_vblank() { if (!m_vd_triggered) { if (BIT(m_cr, 3)) { 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 || BIT(m_dcr, 4)) { LOG("SATB transfer from %05x\n", m_dvssr << 1); for (int 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 = false; /* Generate SATB interrupt if requested */ if (BIT(m_dcr, 0)) { m_satb_countdown = 4; // m_status |= HUC6270_DS; // m_irq_changed_cb(ASSERT_LINE); } } m_vd_triggered = true; } } 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 = false; 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 }; const uint16_t v = (m_byr_latched) & (BIT(m_mwr, 6) ? 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) { const uint8_t sprite_data = m_sprite_row[m_sprite_row_index] & 0x00ff; const bool collission = BIT(m_sprite_row[m_sprite_row_index], 15); if (BIT(m_cr, 7)) { data = HUC6270_BACKGROUND | m_bat_tile_row[m_bat_column]; if (sprite_data && BIT(m_cr, 6)) { if (BIT(m_sprite_row[m_sprite_row_index], 14)) { data = HUC6270_SPRITE | sprite_data; } else { if (data == HUC6270_BACKGROUND) { data = HUC6270_SPRITE | sprite_data; } } } } else { if (BIT(m_cr, 6)) { 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 && BIT(m_cr, 0)) { 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; //} void huc6270_device::vsync_changed(int state) { 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; } void huc6270_device::hsync_changed(int state) { 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; // raster count VSW latch happens one line earlier (cfr. +2 on assignment) // This has been confirmed on real HW, where the last possible RCR with // 240 VDW is 0x130 (i.e. 64 + 240). m_vert_to_go == 1 will also // cause several side effects, namely: // - draculax Stage 4' "all blue" Richter; // - faussete Stage 2 excessive slowdown; // - xwiber Stage 2 boss never spawning (MT#07384) if (m_vert_to_go == 2 && 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 && BIT(m_cr, 2)) { 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) { const int desr_inc = (m_dcr & 0x0008) ? -1 : +1; const 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 { // area 0x8000-0xffff cannot be r/w (open bus) const uint16_t data = (m_sour <= m_vram_mask) ? m_vram[m_sour] : 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 (BIT(m_dcr, 1)) { m_status |= HUC6270_DV; m_irq_changed_cb(ASSERT_LINE); } m_dma_enabled = false; } } u8 huc6270_device::read(offs_t offset) { uint8_t data = 0x00; switch (offset & 3) { case 0x00: /* status */ data = m_status; if (!machine().side_effects_disabled()) { 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 (!machine().side_effects_disabled()) { 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 = true; 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 = true; //logerror("DMA is not supported yet.\n"); break; case DVSSR: /* Sprite attribute table MSB */ m_dvssr = (m_dvssr & 0x00ff) | (data << 8); m_dvssr_written = true; break; } break; } LOG("\n"); } void huc6270_device::device_start() { assert(!(m_vram_size & (m_vram_size - 1))); m_vram = make_unique_clear(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 = false; m_dvssr_written = false; 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 = false; m_vsync = false; m_dma_enabled = false; m_byr_latched = 0; memset(m_sat, 0, sizeof(m_sat)); }