diff --git a/scripts/src/video.lua b/scripts/src/video.lua index 6d4dc7e74ea..fd16d493c0d 100644 --- a/scripts/src/video.lua +++ b/scripts/src/video.lua @@ -603,6 +603,18 @@ if (VIDEOS["LC7582"]~=null) then } end +-------------------------------------------------- +-- +--@src/devices/video/lc7985.h,VIDEOS["LC7985"] = true +-------------------------------------------------- + +if (VIDEOS["LC7985"]~=null) then + files { + MAME_DIR .. "src/devices/video/lc7985.cpp", + MAME_DIR .. "src/devices/video/lc7985.h", + } +end + -------------------------------------------------- -- --@src/devices/video/m50458.h,VIDEOS["M50458"] = true diff --git a/scripts/target/mame/arcade.lua b/scripts/target/mame/arcade.lua index 720f15a5bd9..5839a45486a 100644 --- a/scripts/target/mame/arcade.lua +++ b/scripts/target/mame/arcade.lua @@ -336,6 +336,7 @@ VIDEOS["I4100"] = true VIDEOS["I8275"] = true VIDEOS["JANGOU_BLITTER"] = true --VIDEOS["LC7582"] = true +--VIDEOS["LC7985"] = true VIDEOS["M50458"] = true VIDEOS["MB90082"] = true VIDEOS["MB_VCU"] = true diff --git a/scripts/target/mame/mess.lua b/scripts/target/mame/mess.lua index 2cb7f0b6701..0e6b6082916 100644 --- a/scripts/target/mame/mess.lua +++ b/scripts/target/mame/mess.lua @@ -366,6 +366,7 @@ VIDEOS["I82730"] = true VIDEOS["I8275"] = true VIDEOS["IMS_CVC"] = true VIDEOS["LC7582"] = true +VIDEOS["LC7985"] = true --VIDEOS["M50458"] = true --VIDEOS["MB90082"] = true --VIDEOS["MB_VCU"] = true diff --git a/src/devices/video/lc7985.cpp b/src/devices/video/lc7985.cpp new file mode 100644 index 00000000000..4fe06f2f086 --- /dev/null +++ b/src/devices/video/lc7985.cpp @@ -0,0 +1,302 @@ +// license:BSD-3-Clause +// copyright-holders:Olivier Galibert +/*************************************************************************** + + Sanyo LC7985NA/LC7985ND LCD controller + +***************************************************************************/ + +#include "emu.h" +#include "lc7985.h" + +DEFINE_DEVICE_TYPE(LC7985, lc7985_device, "lc7985", "Sanyo LC7985NA/LC7985ND LCD controller") + + +ROM_START( lc7985 ) + ROM_REGION( 0x1000, "cgrom", 0 ) + ROM_LOAD( "lc7985.bin", 0x0000, 0x1000, BAD_DUMP CRC(fdc64160) SHA1(8e6b54f8fb7c4c15aab2e65dd1a44729b97423b1)) // from page 12 of the LC7985D datasheet +ROM_END + +lc7985_device::lc7985_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock) + : device_t(mconfig, LC7985, tag, owner, clock), + m_cgrom_region(*this, DEVICE_SELF) +{ +} + +const tiny_rom_entry *lc7985_device::device_rom_region() const +{ + return ROM_NAME(lc7985); +} + +void lc7985_device::device_start() +{ + m_cgrom = m_cgrom_region.found() ? m_cgrom_region : memregion("cgrom")->base(); + m_busy_timer = timer_alloc(0); + + save_item(NAME(m_ddram)); + save_item(NAME(m_cgram)); + save_item(NAME(m_busy_flag)); + save_item(NAME(m_ddac)); + save_item(NAME(m_cgac)); + save_item(NAME(m_shift)); + save_item(NAME(m_access_ddram)); + save_item(NAME(m_function)); + save_item(NAME(m_cds)); +} + +void lc7985_device::device_reset() +{ + memset(m_ddram, 0x20, sizeof(m_ddram)); // filled with SPACE char + memset(m_cgram, 0, sizeof(m_cgram)); + m_ddac = 0x00; + m_cgac = 0x00; + m_shift = 0x00; + m_access_ddram = false; + m_function = 0x00; + m_cds = 0x00; + m_display = 0x00; + m_entry = 0x02; + + busy(attotime::from_msec(10)); +} + +void lc7985_device::busy(attotime time) +{ + m_busy_flag = true; + m_busy_timer->adjust(time); +} + +void lc7985_device::device_timer(emu_timer &, device_timer_id, int, void *) +{ + m_busy_flag = false; +} + +void lc7985_device::inc_ddac() +{ + if(m_function & 0x08) { // 2 lines + if(m_ddac == 39) + m_ddac = 64; + else if(m_ddac == 64+39) + m_ddac = 0; + else + m_ddac++; + } else { + if(m_ddac == 79) + m_ddac = 0; + else + m_ddac++; + } +} + +void lc7985_device::dec_ddac() +{ + if(m_function & 0x08) { // 2 lines + if(m_ddac == 64) + m_ddac = 39; + else if(m_ddac == 0) + m_ddac = 64+39; + else + m_ddac--; + } else { + if(m_ddac == 0) + m_ddac = 79; + else + m_ddac--; + } +} + +void lc7985_device::shift_left() +{ + if(m_shift == 79) + m_shift = 0; + else + m_shift++; +} + +void lc7985_device::shift_right() +{ + if(m_shift == 0) + m_shift = 79; + else + m_shift--; +} + +void lc7985_device::ir_w(u8 data) +{ + if(m_busy_flag) + return; + + if(data & 0x80) { + // Set DDRAM address + m_ddac = data & 0x7f; + m_access_ddram = true; + busy(attotime::from_usec(40)); + + } else if(data & 0x40) { + // Set CGRAM address + m_cgac = data & 0x3f; + m_access_ddram = false; + busy(attotime::from_usec(40)); + + } else if(data & 0x20) { + // Set Function + m_function = data; + busy(attotime::from_usec(40)); + + } else if(data & 0x10) { + // Cursor/Display Shift + m_access_ddram = true; + switch((data >> 2) & 3) { + case 0: dec_ddac(); break; + case 1: inc_ddac(); break; + case 2: shift_left(); break; + case 3: shift_right(); break; + } + + busy(attotime::from_usec(40)); + + } else if(data & 0x08) { + // Display On/Off + m_display = data; + busy(attotime::from_usec(40)); + + } else if(data & 0x04) { + // Set Entry Mode + m_entry = data; + busy(attotime::from_usec(40)); + + } else if(data & 0x02) { + // Cursor home + m_ddac = 0; + m_shift = 0; + m_access_ddram = true; + busy(attotime::from_usec(16400)); + + } else if(data & 0x01) { + // Display clear + memset(m_ddram, 0x20, sizeof(m_ddram)); + m_ddac = 0x00; + m_entry |= 0x02; + busy(attotime::from_usec(16400)); + } +} + +u8 lc7985_device::status_r() +{ + return (m_access_ddram ? m_ddac : m_cgac) | (m_busy_flag ? 0x80 : 0x00); +} + +void lc7985_device::dr_w(u8 data) +{ + if(m_access_ddram) { + m_ddram[(m_function & 0x08) && m_ddac >= 64 ? m_ddac - (64-40) : m_ddac] = data; + switch(m_entry & 0x03) { + case 0: dec_ddac(); break; + case 1: dec_ddac(); shift_right(); break; + case 2: inc_ddac(); break; + case 3: inc_ddac(); shift_left(); break; + } + + } else { + m_cgram[m_cgac] = data; + if(m_entry & 0x02) + m_cgac = (m_cgac + 1) & 0x3f; + else + m_cgac = (m_cgac - 1) & 0x3f; + } +} + +u8 lc7985_device::dr_r() +{ + u8 res; + if(m_access_ddram) { + res = m_ddram[(m_function & 0x08) && m_ddac >= 64 ? m_ddac - (64-40) : m_ddac]; + if(m_entry & 0x02) + inc_ddac(); + else + dec_ddac(); + + } else { + res = m_cgram[m_cgac]; + if(m_entry & 0x02) + m_cgac = (m_cgac + 1) & 0x3f; + else + m_cgac = (m_cgac - 1) & 0x3f; + } + return res; +} + +const u8 *lc7985_device::render() +{ + memset(m_render_buffer, 0, sizeof(m_render_buffer)); + if(!(m_display & 0x04)) + return m_render_buffer; + + if(m_function & 0x08) { + for(int y = 0; y != 2; y++) { + for(int x = 0; x != 40; x ++) { + u8 c = m_ddram[((x + 80 - m_shift) % 40) + 40*y]; + const u8 *src = c < 32 ? m_cgram + 8*(c & 7) : m_cgrom + 16 * c; + u8 *dest = m_render_buffer + 8 * y + 16 * x; + for(int z = 0; z != 8; z ++) + *dest++ = *src++ & 0x1f; + } + } + if(m_display & 0x03) { + int cx = ((m_ddac & 0x3f) + 80 - m_shift) % 80; + u8 *dest = m_render_buffer + (m_ddac >= 0x40 ? 8 : 0) + 16*cx; + if(m_display & 0x02) + dest[7] = 0x1f; + if(m_display & 0x01) { + bool on = int(machine().time().as_double() / 0.409) & 1; + if(on) + for(int z = 0; z != 8; z ++) + *dest++ = 0x1f; + } + } + + } else if(m_function & 0x04) { + for(int x = 0; x != 80; x ++) { + u8 c = m_ddram[(x + 80 - m_shift) % 80]; + const u8 *src = c < 32 ? m_cgram + 8*(c & 6) : m_cgrom + 16 * c; + u8 *dest = m_render_buffer + 16 * x; + for(int z = 0; z != 11; z ++) + *dest++ = *src++ & 0x1f; + } + if(m_display & 0x03) { + int cx = (m_ddac + 80 - m_shift) % 80; + u8 *dest = m_render_buffer + 16*cx; + if(m_display & 0x02) + dest[10] = 0x1f; + if(m_display & 0x01) { + bool on = int(machine().time().as_double() / 0.409) & 1; + if(on) + for(int z = 0; z != 11; z ++) + *dest++ = 0x1f; + } + } + + } else { + for(int x = 0; x != 80; x ++) { + u8 c = m_ddram[(x + 80 - m_shift) % 80]; + const u8 *src = c < 32 ? m_cgram + 8*(c & 7) : m_cgrom + 16 * c; + u8 *dest = m_render_buffer + 16 * x; + for(int z = 0; z != 8; z ++) + *dest++ = *src++ & 0x1f; + } + if(m_display & 0x03) { + int cx = (m_ddac + 80 - m_shift) % 80; + u8 *dest = m_render_buffer + 16*cx; + if(m_display & 0x02) + dest[7] = 0x1f; + if(m_display & 0x01) { + bool on = int(machine().time().as_double() / 0.409) & 1; + if(on) + for(int z = 0; z != 8; z ++) + *dest++ = 0x1f; + } + } + } + return m_render_buffer; +} + diff --git a/src/devices/video/lc7985.h b/src/devices/video/lc7985.h new file mode 100644 index 00000000000..14e2005dcd9 --- /dev/null +++ b/src/devices/video/lc7985.h @@ -0,0 +1,61 @@ +// license:BSD-3-Clause +// copyright-holders:Olivier Galibert +/*************************************************************************** + + Sanyo LC7985NA/LC7985ND LCD controller + +***************************************************************************/ + +#ifndef MAME_VIDEO_LC7985_H +#define MAME_VIDEO_LC7985_H + +#pragma once + +class lc7985_device : public device_t +{ +public: + lc7985_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock = 0); + + void ir_w(u8 data); + u8 status_r(); + void dr_w(u8 data); + u8 dr_r(); + + // 5 bits used per byte, blocks of 16 lines, 80 blocks + const u8 *render(); + +protected: + virtual void device_start() override; + virtual void device_reset() override; + virtual void device_timer(emu_timer &timer, device_timer_id id, int param, void *ptr) override; + virtual const tiny_rom_entry *device_rom_region() const override; + +private: + optional_region_ptr m_cgrom_region; // internal chargen ROM + u8 m_render_buffer[16*40]; + u8 m_ddram[80]; + u8 m_cgram[64]; + const u8 *m_cgrom; + emu_timer *m_busy_timer; + u8 m_ddac; + u8 m_cgac; + u8 m_shift; + u8 m_function; + u8 m_cds; + u8 m_display; + u8 m_entry; + + bool m_busy_flag; + bool m_access_ddram; + + void inc_ddac(); + void dec_ddac(); + void shift_left(); + void shift_right(); + + void busy(attotime tm); +}; + +DECLARE_DEVICE_TYPE(LC7985, lc7985_device) + +#endif // MAME_VIDEO_LC7985_H diff --git a/src/mame/drivers/ymmu5.cpp b/src/mame/drivers/ymmu5.cpp index e1f1c72fdcb..7e4f3ad0980 100644 --- a/src/mame/drivers/ymmu5.cpp +++ b/src/mame/drivers/ymmu5.cpp @@ -12,6 +12,7 @@ #include "cpu/h8/h83002.h" #include "sound/multipcm.h" +#include "video/lc7985.h" #include "screen.h" #include "speaker.h" @@ -23,21 +24,39 @@ public: driver_device(mconfig, type, tag), m_maincpu(*this, "maincpu"), m_ymw258(*this, "ymw258"), + m_lcd(*this, "lcd"), m_key(*this, "S%c", 'A'), + m_outputs(*this, "%x.%x.%x.%x", 0U, 0U, 0U, 0U), m_matrixsel(0) { } void mu5(machine_config &config); +protected: + virtual void machine_start() override; + virtual void machine_reset() override; + private: required_device m_maincpu; required_device m_ymw258; + required_device m_lcd; required_ioport_array<6> m_key; + output_finder<2, 8, 8, 5> m_outputs; void mu5_map(address_map &map); void mu5_io_map(address_map &map); void ymw258_map(address_map &map); + u8 m_lcd_ctrl; + u8 m_lcd_data; + + void lcd_ctrl_w(u16 data); + u16 lcd_ctrl_r(); + void lcd_data_w(u16 data); + u16 lcd_data_r(); + + DECLARE_WRITE_LINE_MEMBER(render_w); + u8 m_matrixsel; u8 matrix_r() { @@ -68,7 +87,9 @@ void mu5_state::mu5_io_map(address_map &map) { map(h8_device::PORT_4, h8_device::PORT_4).lr8(NAME([this]() -> u8 { return m_matrixsel; })); map(h8_device::PORT_4, h8_device::PORT_4).lw8(NAME([this](u8 data) { m_matrixsel = data; })); + map(h8_device::PORT_6, h8_device::PORT_6).rw(FUNC(mu5_state::lcd_ctrl_r), FUNC(mu5_state::lcd_ctrl_w)); map(h8_device::PORT_7, h8_device::PORT_7).r(FUNC(mu5_state::matrix_r)); + map(h8_device::PORT_B, h8_device::PORT_B).rw(FUNC(mu5_state::lcd_data_r), FUNC(mu5_state::lcd_data_w)); } void mu5_state::ymw258_map(address_map &map) @@ -76,6 +97,78 @@ void mu5_state::ymw258_map(address_map &map) map(0x000000, 0x1fffff).rom(); } +void mu5_state::machine_start() +{ + m_outputs.resolve(); + + save_item(NAME(m_lcd_ctrl)); + save_item(NAME(m_lcd_data)); + save_item(NAME(m_matrixsel)); +} + +void mu5_state::machine_reset() +{ + m_lcd_ctrl = 0; + m_lcd_data = 0; + m_matrixsel = 0; +} + +void mu5_state::lcd_ctrl_w(u16 data) +{ + // bit 2 = rs + // bit 1 = r/w + // bit 0 = e + + bool e_edge = (data ^ m_lcd_ctrl) & 1; + m_lcd_ctrl = data; + if(e_edge) { + switch(m_lcd_ctrl & 7) { + case 0: + m_lcd->ir_w(m_lcd_data); + break; + case 3: + m_lcd_data = m_lcd->status_r(); + break; + case 4: + m_lcd->dr_w(m_lcd_data); + break; + case 7: + m_lcd_data = m_lcd->dr_r(); + break; + } + } +} + +u16 mu5_state::lcd_ctrl_r() +{ + return m_lcd_ctrl; +} + +void mu5_state::lcd_data_w(u16 data) +{ + m_lcd_data = data; +} + +u16 mu5_state::lcd_data_r() +{ + return m_lcd_data; +} + +WRITE_LINE_MEMBER(mu5_state::render_w) +{ + if(!state) + return; + + const u8 *render = m_lcd->render(); + for(int y=0; y != 2; y++) + for(int x=0; x != 8; x++) + for(int yy=0; yy != 8; yy++) { + u8 v = render[8 * y + 16 * x + yy]; + for(int xx=0; xx != 5; xx++) + m_outputs[y][x][yy][xx] = (v >> xx) & 1; + } +} + static INPUT_PORTS_START(mu5) PORT_START("SA") PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("Part Down") PORT_CODE(KEYCODE_Z) @@ -137,6 +230,15 @@ void mu5_state::mu5(machine_config &config) m_ymw258->set_addrmap(0, &mu5_state::ymw258_map); m_ymw258->add_route(0, "lspeaker", 1.0); m_ymw258->add_route(1, "rspeaker", 1.0); + + LC7985(config, m_lcd); + + auto &screen = SCREEN(config, "screen", SCREEN_TYPE_SVG); + screen.set_refresh_hz(60); + screen.set_size(800, 435); + screen.set_visarea_full(); + screen.screen_vblank().set(FUNC(mu5_state::render_w)); + } ROM_START( mu5 ) @@ -145,6 +247,9 @@ ROM_START( mu5 ) ROM_REGION(0x200000, "ymw258", 0) ROM_LOAD("yamaha_mu5_waverom_xp50280-801.bin", 0x000000, 0x200000, CRC(e0913030) SHA1(369f8df4942b6717c142ca8c4913e556dafae187)) + + ROM_REGION(257524, "screen", 0) + ROM_LOAD("mu5lcd.svg", 0, 257524, CRC(a9a6f561) SHA1(c90d973bfb12755e99cf54d9323a54b773b48bba)) ROM_END CONS(1994, mu5, 0, 0, mu5, mu5, mu5_state, empty_init, "Yamaha", "MU-5", MACHINE_NOT_WORKING )