// license:BSD-3-Clause // copyright-holders:Ryan Holtz /*********************************************************************** Fujitsu MB88303 NMOS Television Display Controller (TVDC) emulation TOOD: - Character-size registers. - Proper hsync/vsync emulation. - Blinking. ***********************************************************************/ #include "emu.h" #include "mb88303.h" //************************************************************************** // DEVICE DEFINITIONS //************************************************************************** // device type definition DEFINE_DEVICE_TYPE(MB88303, mb88303_device, "mb88303", "Fujitsu MB88303") //************************************************************************** // LIVE DEVICE //************************************************************************** /*static*/ const uint8_t mb88303_device::s_character_data[0x40][7] = { { 0x0e, 0x11, 0x11, 0x1f, 0x11, 0x11, 0x11 }, // 0x00 { 0x1e, 0x11, 0x11, 0x1e, 0x11, 0x11, 0x1e }, { 0x0e, 0x11, 0x10, 0x10, 0x10, 0x11, 0x0e }, { 0x1e, 0x09, 0x09, 0x09, 0x09, 0x09, 0x1e }, { 0x1f, 0x10, 0x10, 0x1e, 0x10, 0x10, 0x1f }, { 0x1f, 0x10, 0x10, 0x1e, 0x10, 0x10, 0x10 }, { 0x0f, 0x10, 0x10, 0x13, 0x11, 0x11, 0x0f }, { 0x11, 0x11, 0x11, 0x1f, 0x11, 0x11, 0x11 }, { 0x0e, 0x04, 0x04, 0x04, 0x04, 0x04, 0x0e }, { 0x01, 0x01, 0x01, 0x01, 0x11, 0x11, 0x0e }, { 0x11, 0x12, 0x14, 0x18, 0x14, 0x12, 0x11 }, { 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x1f }, { 0x11, 0x1b, 0x15, 0x15, 0x11, 0x11, 0x11 }, { 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00 }, { 0x00, 0x00, 0x00, 0x00, 0x00, 0x11, 0x1f }, { 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80 }, { 0x11, 0x11, 0x19, 0x15, 0x13, 0x11, 0x11 }, // 0x10 { 0x0e, 0x11, 0x11, 0x11, 0x11, 0x11, 0x0e }, { 0x1e, 0x11, 0x11, 0x1e, 0x10, 0x10, 0x10 }, { 0x0e, 0x11, 0x11, 0x11, 0x15, 0x12, 0x0d }, { 0x1e, 0x11, 0x11, 0x1e, 0x14, 0x12, 0x11 }, { 0x0e, 0x11, 0x10, 0x0e, 0x01, 0x11, 0x0e }, { 0x1f, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04 }, { 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x0e }, { 0x11, 0x11, 0x11, 0x11, 0x11, 0x0a, 0x04 }, { 0x11, 0x11, 0x11, 0x15, 0x15, 0x1b, 0x11 }, { 0x11, 0x11, 0x0a, 0x04, 0x0a, 0x11, 0x11 }, { 0x11, 0x11, 0x0a, 0x04, 0x04, 0x04, 0x04 }, { 0x1f, 0x01, 0x02, 0x04, 0x08, 0x10, 0x1f }, { 0x00, 0x00, 0x04, 0x00, 0x04, 0x00, 0x00 }, { 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f }, { 0x02, 0x04, 0x04, 0x04, 0x04, 0x04, 0x02 }, { 0x0e, 0x11, 0x13, 0x15, 0x19, 0x11, 0x0e }, // 0x20 { 0x04, 0x0c, 0x04, 0x04, 0x04, 0x04, 0x04 }, { 0x0e, 0x11, 0x01, 0x0e, 0x10, 0x10, 0x1f }, { 0x0e, 0x11, 0x01, 0x06, 0x01, 0x11, 0x0e }, { 0x06, 0x0a, 0x12, 0x12, 0x1f, 0x02, 0x02 }, { 0x1f, 0x10, 0x1e, 0x01, 0x01, 0x11, 0x0e }, { 0x0e, 0x11, 0x10, 0x1e, 0x11, 0x11, 0x0e }, { 0x1f, 0x11, 0x01, 0x02, 0x04, 0x04, 0x04 }, { 0x0e, 0x11, 0x11, 0x0e, 0x11, 0x11, 0x0e }, { 0x0e, 0x11, 0x11, 0x0f, 0x01, 0x11, 0x0e }, { 0x0e, 0x11, 0x01, 0x06, 0x04, 0x00, 0x04 }, { 0x04, 0x04, 0x04, 0x04, 0x04, 0x00, 0x04 }, { 0x06, 0x02, 0x04, 0x00, 0x00, 0x00, 0x00 }, { 0x00, 0x00, 0x00, 0x00, 0x00, 0x0c, 0x0c }, { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, { 0x08, 0x04, 0x04, 0x04, 0x04, 0x04, 0x08 }, { 0x00, 0x04, 0x0a, 0x15, 0x04, 0x04, 0x00 }, // 0x30 { 0x00, 0x04, 0x04, 0x15, 0x0a, 0x04, 0x00 }, { 0x00, 0x04, 0x08, 0x17, 0x08, 0x04, 0x00 }, { 0x00, 0x04, 0x02, 0x1d, 0x02, 0x04, 0x00 }, { 0x00, 0x04, 0x04, 0x1f, 0x04, 0x04, 0x00 }, { 0x00, 0x00, 0x00, 0x1f, 0x00, 0x00, 0x00 }, { 0x00, 0x04, 0x15, 0x0e, 0x15, 0x04, 0x00 }, { 0x00, 0x01, 0x02, 0x04, 0x08, 0x10, 0x00 }, { 0x00, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x00 }, { 0x0c, 0x12, 0x14, 0x08, 0x15, 0x12, 0x0d }, { 0x10, 0x1f, 0x02, 0x0f, 0x0a, 0x1f, 0x02 }, { 0x1f, 0x11, 0x1f, 0x11, 0x1f, 0x11, 0x11 }, { 0x1f, 0x11, 0x11, 0x1f, 0x11, 0x11, 0x1f }, { 0x00, 0x00, 0x00, 0x00, 0x0c, 0x04, 0x08 }, { 0x00, 0x00, 0x00, 0x08, 0x15, 0x02, 0x00 }, { 0x0e, 0x11, 0x04, 0x1f, 0x1b, 0x1f, 0x11 } }; //------------------------------------------------- // mb88303_device - constructor //------------------------------------------------- mb88303_device::mb88303_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock) : device_t(mconfig, MB88303, tag, owner, clock) , m_write_vow(*this) , m_write_vobn(*this) , m_write_do(*this) , m_horiz_display_pos(0) , m_vert_display_pos(0) , m_display_ctrl(0) , m_general_out(7) , m_address(0) , m_addr_inc_mode(false) { } //------------------------------------------------- // device_start - device-specific startup //------------------------------------------------- void mb88303_device::device_start() { // resolve callbacks m_write_vow.resolve_safe(); m_write_vobn.resolve_safe(); m_write_do.resolve_safe(); // register for state saving save_item(NAME(m_display_mem)); save_item(NAME(m_horiz_display_pos)); save_item(NAME(m_vert_display_pos)); save_item(NAME(m_display_ctrl)); save_item(NAME(m_general_out)); save_item(NAME(m_address)); save_item(NAME(m_addr_inc_mode)); } //------------------------------------------------- // device_reset - device-specific reset //------------------------------------------------- void mb88303_device::device_reset() { m_horiz_display_pos = 0; m_vert_display_pos = 0; m_display_ctrl = 0; m_general_out = 0x07; m_address = 0; m_addr_inc_mode = false; memset(m_display_mem, 0, sizeof(uint8_t) * 180); m_da = 0; m_adm = 0; m_reset_n = 1; m_ldi = 0; m_hsync_n = 1; m_vsync_n = 1; m_write_vow(CLEAR_LINE); m_write_vobn(ASSERT_LINE); m_write_do(m_general_out); } void mb88303_device::da_w(uint8_t data) { //logerror("%s: mb88303_device::da_w: %02x\n", machine().describe_context(), data); m_da = data; } void mb88303_device::adm_w(int state) { //logerror("%s: mb88303_device::adm_w: %02x\n", machine().describe_context(), state); m_adm = state; } void mb88303_device::reset_n_w(int state) { logerror("%s: mb88303_device::reset_n_w: %02x\n", machine().describe_context(), state); uint8_t old = m_reset_n; m_reset_n = state; if (old != m_reset_n && m_reset_n) { m_horiz_display_pos = 0; m_vert_display_pos = 0; m_display_ctrl = 0; m_general_out = 0x07; m_addr_inc_mode = false; m_write_vow(CLEAR_LINE); m_write_vobn(ASSERT_LINE); m_write_do(m_general_out); } } void mb88303_device::ldi_w(int state) { //logerror("%s: mb88303_device::ldi_w: %02x\n", machine().describe_context(), state); uint8_t old = m_ldi; m_ldi = state; if (m_ldi != old) { if (!m_ldi) { // Direct Address Mode: At the trailing edge of LDI, a 7-bit // data on DA6-DA0 is written into an internal control register // or an internal display memory location that is designated by // the address latched at the leading edge. // Address Increment Mode: At the trailing edge of LDI, a 7-bit // data on DA6-DA0 is written into an internal control register // or a display memory location that is indicated by the // address register. process_data(); } else if (!m_adm) { // Direct Address Mode: At the leading edge of LDI, an 8-bit // address on DA7-DA0 is automatically latched into the // internal address register. //logerror("%s: mb88303_device::ldi_w: new address = %02x\n", machine().describe_context(), m_da); m_address = m_da; } else { // Address Increment Mode: At the leading edge of LDI, the // address register is automatically incremented. m_address++; //logerror("%s: mb88303_device::ldi_w: new address = %02x\n", machine().describe_context(), m_address); } } } void mb88303_device::process_data() { switch (m_address) { case 180: hdpr_w(m_da); break; case 181: vdpr_w(m_da); break; case 182: dcr_w(m_da); break; case 183: gor_w(m_da); break; default: assert(m_address < 180); m_display_mem[m_address] = m_da; break; } } void mb88303_device::hsync_n_w(int state) { m_hsync_n = state; } void mb88303_device::vsync_n_w(int state) { m_vsync_n = state; } void mb88303_device::hdpr_w(uint8_t data) { logerror("%s: mb88303_device::hdpr_w: %02x\n", machine().describe_context(), data); m_horiz_display_pos = data & 0x3f; } void mb88303_device::vdpr_w(uint8_t data) { logerror("%s: mb88303_device::vdpr_w: %02x\n", machine().describe_context(), data); m_vert_display_pos = data & 0x3f; } void mb88303_device::dcr_w(uint8_t data) { logerror("%s: mb88303_device::dcr_w: %02x\n", machine().describe_context(), data); m_display_ctrl = data & 0x7f; } void mb88303_device::gor_w(uint8_t data) { //logerror("%s: mb88303_device::gor_w: %02x\n", machine().describe_context(), data); uint8_t old = m_general_out; m_general_out = data & 0x07; if (m_general_out != old) { m_write_do(m_general_out); } } bool mb88303_device::blank_display() { return !BIT(m_display_ctrl, BLK_BIT); } bool mb88303_device::show_background() { return BIT(m_display_ctrl, BLKB_BIT); } bool mb88303_device::enable_blinking() { return BIT(m_display_ctrl, BLINK_BIT); } void mb88303_device::update_bitmap(bitmap_rgb32 &bitmap, const rectangle &cliprect) { if (blank_display()) { bitmap.fill(0, cliprect); return; } for (unsigned x = 0; x < VISIBLE_COLUMNS; x++) { for (unsigned y = 0; y < VISIBLE_LINES; y++) { uint16_t bitmap_y = y * 9; uint16_t bitmap_x = x * 6; const uint8_t *gfx = s_character_data[m_display_mem[y * VISIBLE_COLUMNS + x] & 0x3f]; for (unsigned row = 0; row < 7; row++) { for (unsigned bit = 0; bit < 6; bit++) { if (gfx[row] & 0x80) continue; const uint32_t color = BIT(gfx[row], bit) ? 0xffffffff : 0; if (!show_background() && !color) continue; bitmap.pix(bitmap_y + row + m_vert_display_pos, bitmap_x + (6 - bit) + m_horiz_display_pos) = color; } } } } }