// license:BSD-3-Clause // copyright-holders:Curt Coder /********************************************************************** Seiko-Epson SED1330 LCD Controller emulation **********************************************************************/ #include "emu.h" #include "sed1330.h" #include "screen.h" //#define VERBOSE 1 #include "logmacro.h" //************************************************************************** // MACROS / CONSTANTS //************************************************************************** #define INSTRUCTION_SYSTEM_SET 0x40 #define INSTRUCTION_SLEEP_IN 0x53 // unimplemented #define INSTRUCTION_DISP_ON 0x59 #define INSTRUCTION_DISP_OFF 0x58 #define INSTRUCTION_SCROLL 0x44 #define INSTRUCTION_CSRFORM 0x5d #define INSTRUCTION_CGRAM_ADR 0x5c #define INSTRUCTION_CSRDIR_RIGHT 0x4c #define INSTRUCTION_CSRDIR_LEFT 0x4d #define INSTRUCTION_CSRDIR_UP 0x4e #define INSTRUCTION_CSRDIR_DOWN 0x4f #define INSTRUCTION_HDOT_SCR 0x5a #define INSTRUCTION_OVLAY 0x5b #define INSTRUCTION_CSRW 0x46 #define INSTRUCTION_CSRR 0x47 #define INSTRUCTION_MWRITE 0x42 #define INSTRUCTION_MREAD 0x43 #define CSRDIR_RIGHT 0x00 #define CSRDIR_LEFT 0x01 #define CSRDIR_UP 0x02 #define CSRDIR_DOWN 0x03 #define MX_OR 0x00 #define MX_XOR 0x01 // unimplemented #define MX_AND 0x02 // unimplemented #define MX_PRIORITY_OR 0x03 // unimplemented #define FC_OFF 0x00 #define FC_SOLID 0x01 #define FC_FLASH_32 0x02 // unimplemented #define FC_FLASH_64 0x03 // unimplemented #define FP_OFF 0x00 #define FP_SOLID 0x01 #define FP_FLASH_32 0x02 // unimplemented #define FP_FLASH_4 0x03 // unimplemented //************************************************************************** // GLOBAL VARIABLES //************************************************************************** // device type definition DEFINE_DEVICE_TYPE(SED1330, sed1330_device, "sed1330", "Epson SED1330") // default address map void sed1330_device::sed1330(address_map &map) { if (!has_configured_map(0)) map(0x0000, 0xffff).ram(); } // internal character generator ROM ROM_START( sed1330 ) ROM_REGION( 0x5c0, "gfx1", 0 ) // internal chargen ROM ROM_LOAD( "sed1330.bin", 0x000, 0x5c0, NO_DUMP ) ROM_END //************************************************************************** // INLINE HELPERS //************************************************************************** //------------------------------------------------- // readbyte - read a byte at the given address //------------------------------------------------- inline uint8_t sed1330_device::readbyte(offs_t address) { return space().read_byte(address); } //------------------------------------------------- // writebyte - write a byte at the given address //------------------------------------------------- inline void sed1330_device::writebyte(offs_t address, uint8_t data) { space().write_byte(address, data); } //------------------------------------------------- // increment_csr - increment cursor address //------------------------------------------------- inline void sed1330_device::increment_csr() { switch (m_cd) { case CSRDIR_RIGHT: m_csr++; break; case CSRDIR_LEFT: m_csr--; break; case CSRDIR_UP: m_csr -= m_ap; break; case CSRDIR_DOWN: m_csr += m_ap; break; } } //************************************************************************** // LIVE DEVICE //************************************************************************** //------------------------------------------------- // sed1330_device - constructor //------------------------------------------------- sed1330_device::sed1330_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock) : device_t(mconfig, SED1330, tag, owner, clock), device_memory_interface(mconfig, *this), device_video_interface(mconfig, *this), m_bf(0), m_space_config("videoram", ENDIANNESS_LITTLE, 8, 16, 0, address_map_constructor(FUNC(sed1330_device::sed1330), this)) { } //------------------------------------------------- // rom_region - device-specific ROM region //------------------------------------------------- const tiny_rom_entry *sed1330_device::device_rom_region() const { return ROM_NAME( sed1330 ); } //------------------------------------------------- // device_start - device-specific startup //------------------------------------------------- void sed1330_device::device_start() { m_cache = space().cache<0, 0, ENDIANNESS_LITTLE>(); // register for state saving save_item(NAME(m_bf)); save_item(NAME(m_ir)); save_item(NAME(m_dor)); save_item(NAME(m_pbc)); save_item(NAME(m_d)); save_item(NAME(m_sleep)); save_item(NAME(m_sag)); save_item(NAME(m_m0)); save_item(NAME(m_m1)); save_item(NAME(m_m2)); save_item(NAME(m_ws)); save_item(NAME(m_iv)); save_item(NAME(m_wf)); save_item(NAME(m_fx)); save_item(NAME(m_fy)); save_item(NAME(m_cr)); save_item(NAME(m_tcr)); save_item(NAME(m_lf)); save_item(NAME(m_ap)); save_item(NAME(m_sad1)); save_item(NAME(m_sad2)); save_item(NAME(m_sad3)); save_item(NAME(m_sad4)); save_item(NAME(m_sl1)); save_item(NAME(m_sl2)); save_item(NAME(m_hdotscr)); save_item(NAME(m_csr)); save_item(NAME(m_cd)); save_item(NAME(m_crx)); save_item(NAME(m_cry)); save_item(NAME(m_cm)); save_item(NAME(m_fc)); save_item(NAME(m_fp)); save_item(NAME(m_mx)); save_item(NAME(m_dm)); save_item(NAME(m_ov)); } //------------------------------------------------- // device_reset - device-specific reset //------------------------------------------------- void sed1330_device::device_reset() { } //------------------------------------------------- // memory_space_config - return a description of // any address spaces owned by this device //------------------------------------------------- device_memory_interface::space_config_vector sed1330_device::memory_space_config() const { return space_config_vector { std::make_pair(0, &m_space_config) }; } //------------------------------------------------- // status_r - //------------------------------------------------- READ8_MEMBER( sed1330_device::status_r ) { if (!machine().side_effects_disabled()) LOG("SED1330 Status Read: %s\n", m_bf ? "busy" : "ready"); return m_bf << 6; } //------------------------------------------------- // command_w - //------------------------------------------------- WRITE8_MEMBER( sed1330_device::command_w ) { m_ir = data; m_pbc = 0; switch (m_ir) { #if 0 case INSTRUCTION_SLEEP_IN: break; #endif case INSTRUCTION_CSRDIR_RIGHT: case INSTRUCTION_CSRDIR_LEFT: case INSTRUCTION_CSRDIR_UP: case INSTRUCTION_CSRDIR_DOWN: m_cd = data & 0x03; switch (m_cd) { case CSRDIR_RIGHT: LOG("SED1330 Cursor Direction: Right\n"); break; case CSRDIR_LEFT: LOG("SED1330 Cursor Direction: Left\n"); break; case CSRDIR_UP: LOG("SED1330 Cursor Direction: Up\n"); break; case CSRDIR_DOWN: LOG("SED1330 Cursor Direction: Down\n"); break; } break; } } //------------------------------------------------- // data_r - //------------------------------------------------- READ8_MEMBER( sed1330_device::data_r ) { uint8_t data = 0; switch (m_ir) { case INSTRUCTION_MREAD: data = readbyte(m_csr); if (!machine().side_effects_disabled()) { LOG("SED1330 Memory Read %02x from %04x\n", data, m_csr); increment_csr(); } break; case INSTRUCTION_CSRR: switch (m_pbc) { case 0: data = m_csr & 0xff; break; case 1: data = (m_csr & 0xff00) >> 8; break; default: logerror("SED1330 Invalid parameter byte %02x\n", data); } if (!machine().side_effects_disabled()) { LOG("SED1330 Cursor Byte %d Read %02x\n", m_pbc, data); m_pbc++; } break; default: logerror("SED1330 Unsupported instruction %02x\n", m_ir); break; } return data; } //------------------------------------------------- // data_w - //------------------------------------------------- WRITE8_MEMBER( sed1330_device::data_w ) { switch (m_ir) { case INSTRUCTION_SYSTEM_SET: switch (m_pbc) { case 0: m_m0 = BIT(data, 0); m_m1 = BIT(data, 1); m_m2 = BIT(data, 2); m_ws = BIT(data, 3); m_iv = BIT(data, 5); LOG("SED1330 %s CG ROM\n", BIT(data, 0) ? "External" : "Internal"); LOG("SED1330 D6 Correction: %s\n", BIT(data, 1) ? "enabled" : "disabled"); LOG("SED1330 Character Height: %u\n", BIT(data, 2) ? 16 : 8); LOG("SED1330 %s Panel Drive\n", BIT(data, 3) ? "Dual" : "Single"); LOG("SED1330 Screen Top-Line Correction: %s\n", BIT(data, 5) ? "disabled" : "enabled"); break; case 1: m_fx = (data & 0x07) + 1; m_wf = BIT(data, 7); LOG("SED1330 Horizontal Character Size: %u\n", m_fx); LOG("SED1330 %s AC Drive\n", BIT(data, 7) ? "2-frame" : "16-line"); break; case 2: m_fy = (data & 0x0f) + 1; LOG("SED1330 Vertical Character Size: %u\n", m_fy); break; case 3: m_cr = data + 1; LOG("SED1330 Visible Characters Per Line: %u\n", m_cr); break; case 4: m_tcr = data + 1; LOG("SED1330 Total Characters Per Line: %u\n", m_tcr); break; case 5: m_lf = data + 1; LOG("SED1330 Frame Height: %u\n", m_lf); if (clock() != 0) { attotime fr = clocks_to_attotime(m_tcr * m_lf * 9); screen().configure(m_tcr * m_fx, m_lf, screen().visible_area(), fr.as_attoseconds()); LOG("SED1330 Frame Rate: %.1f Hz\n", fr.as_hz()); } break; case 6: m_ap = (m_ap & 0xff00) | data; break; case 7: m_ap = (data << 8) | (m_ap & 0xff); LOG("SED1330 Virtual Screen Width: %u\n", m_ap); break; default: logerror("SED1330 Invalid parameter byte %02x\n", data); } break; case INSTRUCTION_DISP_ON: case INSTRUCTION_DISP_OFF: m_d = BIT(m_ir, 0); m_fc = data & 0x03; m_fp = data >> 2; LOG("SED1330 Display: %s\n", BIT(m_ir, 0) ? "enabled" : "disabled"); switch (m_fc) { case FC_OFF: LOG("SED1330 Cursor: disabled\n"); break; case FC_SOLID: LOG("SED1330 Cursor: solid\n"); break; case FC_FLASH_32: LOG("SED1330 Cursor: fFR/32\n"); break; case FC_FLASH_64: LOG("SED1330 Cursor: fFR/64\n"); break; } switch (m_fp & 0x03) { case FP_OFF: LOG("SED1330 Display Page 1: disabled\n"); break; case FP_SOLID: LOG("SED1330 Display Page 1: enabled\n"); break; case FP_FLASH_32: LOG("SED1330 Display Page 1: flash fFR/32\n"); break; case FP_FLASH_4: LOG("SED1330 Display Page 1: flash fFR/4\n"); break; } switch ((m_fp >> 2) & 0x03) { case FP_OFF: LOG("SED1330 Display Page 2/4: disabled\n"); break; case FP_SOLID: LOG("SED1330 Display Page 2/4: enabled\n"); break; case FP_FLASH_32: LOG("SED1330 Display Page 2/4: flash fFR/32\n"); break; case FP_FLASH_4: LOG("SED1330 Display Page 2/4: flash fFR/4\n"); break; } switch ((m_fp >> 4) & 0x03) { case FP_OFF: LOG("SED1330 Display Page 3: disabled\n"); break; case FP_SOLID: LOG("SED1330 Display Page 3: enabled\n"); break; case FP_FLASH_32: LOG("SED1330 Display Page 3: flash fFR/32\n"); break; case FP_FLASH_4: LOG("SED1330 Display Page 3: flash fFR/4\n"); break; } break; case INSTRUCTION_SCROLL: switch (m_pbc) { case 0: m_sad1 = (m_sad1 & 0xff00) | data; break; case 1: m_sad1 = (data << 8) | (m_sad1 & 0xff); LOG("SED1330 Display Page 1 Start Address: %04x\n", m_sad1); break; case 2: m_sl1 = data + 1; LOG("SED1330 Display Block 1 Screen Lines: %u\n", m_sl1); break; case 3: m_sad2 = (m_sad2 & 0xff00) | data; break; case 4: m_sad2 = (data << 8) | (m_sad2 & 0xff); LOG("SED1330 Display Page 2 Start Address: %04x\n", m_sad2); break; case 5: m_sl2 = data + 1; LOG("SED1330 Display Block 2 Screen Lines: %u\n", m_sl2); break; case 6: m_sad3 = (m_sad3 & 0xff00) | data; break; case 7: m_sad3 = (data << 8) | (m_sad3 & 0xff); LOG("SED1330 Display Page 3 Start Address: %04x\n", m_sad3); break; case 8: m_sad4 = (m_sad4 & 0xff00) | data; break; case 9: m_sad4 = (data << 8) | (m_sad4 & 0xff); LOG("SED1330 Display Page 4 Start Address: %04x\n", m_sad4); break; default: logerror("SED1330 Invalid parameter byte %02x\n", data); } break; case INSTRUCTION_CSRFORM: switch (m_pbc) { case 0: m_crx = (data & 0x0f) + 1; LOG("SED1330 Horizontal Cursor Size: %u\n", m_crx); break; case 1: m_cry = (data & 0x0f) + 1; m_cm = BIT(data, 7); LOG("SED1330 Vertical Cursor Location: %u\n", m_cry); LOG("SED1330 Cursor Shape: %s\n", BIT(data, 7) ? "Block" : "Underscore"); break; default: logerror("SED1330 Invalid parameter byte %02x\n", data); } break; case INSTRUCTION_CGRAM_ADR: switch (m_pbc) { case 0: m_sag = (m_sag & 0xff00) | data; break; case 1: m_sag = (data << 8) | (m_sag & 0xff); LOG("SED1330 Character Generator RAM Start Address: %04x\n", m_sag); break; default: logerror("SED1330 Invalid parameter byte %02x\n", data); } break; case INSTRUCTION_HDOT_SCR: m_hdotscr = data & 0x07; LOG("SED1330 Horizontal Dot Scroll: %u\n", m_hdotscr); break; case INSTRUCTION_OVLAY: m_mx = data & 0x03; m_dm = (data >> 2) & 0x03; m_ov = BIT(data, 4); switch (m_mx) { case MX_OR: LOG("SED1330 Display Composition Method: OR\n"); break; case MX_XOR: LOG("SED1330 Display Composition Method: Exclusive-OR\n"); break; case MX_AND: LOG("SED1330 Display Composition Method: AND\n"); break; case MX_PRIORITY_OR: LOG("SED1330 Display Composition Method: Priority-OR\n"); break; } LOG("SED1330 Display Page 1 Mode: %s\n", BIT(data, 2) ? "Graphics" : "Text"); LOG("SED1330 Display Page 3 Mode: %s\n", BIT(data, 3) ? "Graphics" : "Text"); LOG("SED1330 Display Composition Layers: %u\n", BIT(data, 4) ? 3 : 2); break; case INSTRUCTION_CSRW: switch (m_pbc) { case 0: m_csr = (m_csr & 0xff00) | data; break; case 1: m_csr = (data << 8) | (m_csr & 0xff); LOG("SED1330 Cursor Address %04x\n", m_csr); break; default: logerror("SED1330 Invalid parameter byte %02x\n", data); } break; #if 0 case INSTRUCTION_CSRR: break; #endif case INSTRUCTION_MWRITE: LOG("SED1330 Memory Write %02x to %04x (row %u col %u line %u)\n", data, m_csr, m_csr/80/8, m_csr%80, m_csr/80); writebyte(m_csr, data); increment_csr(); break; #if 0 case INSTRUCTION_MREAD: break; #endif default: logerror("SED1330 Unsupported instruction %02x\n", m_ir); } m_pbc++; } //------------------------------------------------- // draw_text_scanline - //------------------------------------------------- void sed1330_device::draw_text_scanline(bitmap_ind16 &bitmap, const rectangle &cliprect, int y, int r, uint16_t va, bool cursor) { uint16_t *p = &bitmap.pix16(y, 0); for (int sx = 0; sx < m_cr; sx++, p += m_fx) { if (m_m0 && !m_m1) { uint8_t c = m_cache->read_byte(va + sx); uint8_t data = m_cache->read_byte(0xf000 | (m_m2 ? u16(c) << 4 | r : u16(c) << 3 | (r & 7))); for (int x = 0; x < m_fx; x++, data <<= 1) if (BIT(data, 7)) p[x] = 1; } if (cursor && (va + sx) == m_csr) { if (m_cm) { // block cursor if (r < m_cry) { std::fill_n(p, m_crx, 1); } } else { // underscore cursor if (r == m_cry) { std::fill_n(p, m_crx, 1); } } } } } //------------------------------------------------- // draw_graphics_scanline - //------------------------------------------------- void sed1330_device::draw_graphics_scanline(bitmap_ind16 &bitmap, const rectangle &cliprect, int y, uint16_t va) { for (int sx = 0; sx < m_cr; sx++) { uint8_t data = readbyte(va++); for (int x = 0; x < m_fx; x++) { bitmap.pix16(y, (sx * m_fx) + x) = BIT(data, 7); data <<= 1; } } } //------------------------------------------------- // update_graphics - //------------------------------------------------- void sed1330_device::update_graphics(bitmap_ind16 &bitmap, const rectangle &cliprect) { } //------------------------------------------------- // update_text - //------------------------------------------------- void sed1330_device::update_text(bitmap_ind16 &bitmap, const rectangle &cliprect) { uint8_t attr1 = m_fp & 0x03; uint8_t attr2 = (m_fp >> 2) & 0x03; uint8_t attr3 = (m_fp >> 4) & 0x03; for (int y = 0; y < m_lf; y++) { if (y >= m_sl1) { if (attr3 != FP_OFF) { uint16_t sad3 = m_sad3 + (((y - m_sl1) / m_fy) * m_ap); // draw text display page 3 scanline draw_text_scanline(bitmap, cliprect, y, (y - m_sl1) % m_fy, sad3, m_ov && m_fc != FC_OFF); } } else { if (attr1 != FP_OFF) { uint16_t sad1 = m_sad1 + ((y / m_fy) * m_ap); // draw text display page 1 scanline draw_text_scanline(bitmap, cliprect, y, y % m_fy, sad1, !m_ov && m_fc != FC_OFF); } } if (attr2 != FP_OFF) { if (m_ws && y >= m_sl2) { uint16_t sad4 = m_sad4 + ((y - m_sl2) * m_ap); // draw graphics display page 4 scanline draw_graphics_scanline(bitmap, cliprect, y, sad4); } else { uint16_t sad2 = m_sad2 + (y * m_ap); // draw graphics display page 2 scanline draw_graphics_scanline(bitmap, cliprect, y, sad2); } } } } //------------------------------------------------- // screen_update - //------------------------------------------------- uint32_t sed1330_device::screen_update(screen_device &screen, bitmap_ind16 &bitmap, const rectangle &cliprect) { bitmap.fill(0, cliprect); if (m_d) { if (m_dm) { update_graphics(bitmap, cliprect); } else { update_text(bitmap, cliprect); } } return 0; }