microvsn: move lcd chip emulation to device (nw)

This commit is contained in:
hap 2020-04-12 15:37:07 +02:00
parent edcee4f8cb
commit 1c728274e9
6 changed files with 228 additions and 68 deletions

View File

@ -399,6 +399,18 @@ if (VIDEOS["HD66421"]~=null) then
} }
end end
--------------------------------------------------
--
--@src/devices/video/hlcd0488.h,VIDEOS["HLCD0488"] = true
--------------------------------------------------
if (VIDEOS["HLCD0488"]~=null) then
files {
MAME_DIR .. "src/devices/video/hlcd0488.cpp",
MAME_DIR .. "src/devices/video/hlcd0488.h",
}
end
-------------------------------------------------- --------------------------------------------------
-- --
--@src/devices/video/hlcd0515.h,VIDEOS["HLCD0515"] = true --@src/devices/video/hlcd0515.h,VIDEOS["HLCD0515"] = true

View File

@ -319,6 +319,7 @@ VIDEOS["HD44780"] = true
VIDEOS["HD61830"] = true VIDEOS["HD61830"] = true
VIDEOS["HD63484"] = true VIDEOS["HD63484"] = true
--VIDEOS["HD66421"] = true --VIDEOS["HD66421"] = true
--VIDEOS["HLCD0488"] = true
--VIDEOS["HLCD0515"] = true --VIDEOS["HLCD0515"] = true
--VIDEOS["HLCD0538"] = true --VIDEOS["HLCD0538"] = true
VIDEOS["HUC6202"] = true VIDEOS["HUC6202"] = true

View File

@ -345,6 +345,7 @@ VIDEOS["HD61603"] = true
VIDEOS["HD61830"] = true VIDEOS["HD61830"] = true
--VIDEOS["HD63484"] = true --VIDEOS["HD63484"] = true
VIDEOS["HD66421"] = true VIDEOS["HD66421"] = true
VIDEOS["HLCD0488"] = true
VIDEOS["HLCD0515"] = true VIDEOS["HLCD0515"] = true
VIDEOS["HLCD0538"] = true VIDEOS["HLCD0538"] = true
VIDEOS["HP1LL3"] = true VIDEOS["HP1LL3"] = true

View File

@ -0,0 +1,104 @@
// license:BSD-3-Clause
// copyright-holders:Wilbert Pol
/*
Hughes HLCD 0488 LCD Driver
16*16 segment outputs, direct drive
TODO:
- should all control be edge-triggered?
- output polarity flip
*/
#include "emu.h"
#include "video/hlcd0488.h"
DEFINE_DEVICE_TYPE(HLCD0488, hlcd0488_device, "hlcd0488", "Hughes HLCD 0488 LCD Driver")
//-------------------------------------------------
// constructor
//-------------------------------------------------
hlcd0488_device::hlcd0488_device(const machine_config &mconfig, const char *tag, device_t *owner, u32 clock) :
device_t(mconfig, HLCD0488, tag, owner, clock),
m_write_cols(*this)
{
memset(m_latch, 0, 8);
memset(m_hold, 0, 8);
}
//-------------------------------------------------
// device_start - device-specific startup
//-------------------------------------------------
void hlcd0488_device::device_start()
{
m_write_cols.resolve_safe();
m_sync_timer = timer_alloc();
// register for savestates
save_item(NAME(m_latch_pulse));
save_item(NAME(m_latch_pulse_prev));
save_item(NAME(m_data_clk));
save_item(NAME(m_data_clk_prev));
save_item(NAME(m_data));
save_item(NAME(m_count));
save_item(NAME(m_latch));
save_item(NAME(m_hold));
}
//-------------------------------------------------
// handlers
//-------------------------------------------------
void hlcd0488_device::device_timer(emu_timer &timer, device_timer_id id, int param, void *ptr)
{
// Latch pulse, when high, resets the %8 latch address counter
if (m_latch_pulse)
m_count = 0;
// The addressed latches load when -Data Clk is low
if (!m_data_clk)
m_latch[m_count] = m_data;
// The latch address counter is incremented on rising edges of -Data Clk
if (!m_data_clk_prev && m_data_clk && !m_latch_pulse)
m_count = (m_count + 1) & 7;
// A parallel transfer of data from the addressed latches to the holding latches occurs
// whenever Latch Pulse is high and -Data Clk is high
if (m_latch_pulse && m_data_clk)
{
for (int i = 0; i < 8; i++)
m_hold[i] = m_latch[i];
u16 row = (m_hold[0] << 12) | (m_hold[1] << 8) | (m_hold[2] << 4) | m_hold[3];
u16 col = (m_hold[4] << 12) | (m_hold[5] << 8) | (m_hold[6] << 4) | m_hold[7];
m_write_cols(row, col);
}
m_latch_pulse_prev = m_latch_pulse;
m_data_clk_prev = m_data_clk;
}
WRITE_LINE_MEMBER(hlcd0488_device::latch_pulse_w)
{
m_latch_pulse = state ? 1 : 0;
m_sync_timer->adjust(attotime::zero);
}
WRITE_LINE_MEMBER(hlcd0488_device::data_clk_w)
{
m_data_clk = state ? 1 : 0;
m_sync_timer->adjust(attotime::zero);
}
void hlcd0488_device::data_w(u8 data)
{
m_data = data & 0xf;
m_sync_timer->adjust(attotime::zero);
}

View File

@ -0,0 +1,78 @@
// license:BSD-3-Clause
// copyright-holders:Wilbert Pol
/*
Hughes HLCD 0488 LCD Driver
*/
#ifndef MAME_VIDEO_HLCD0488_H
#define MAME_VIDEO_HLCD0488_H
#pragma once
// pinout reference
/*
____ ____
ROW 4 1 |* \_/ | 40 VDD
ROW 5 2 | | 39 ROW 1
ROW 6 3 | | 38 ROW 2
/DATA CLK 4 | | 37 ROW 3
LATCH PULSE 5 | | 36 COL 1
DATA 0 6 | | 35 COL 2
DATA 1 7 | | 34 COL 3
DATA 2 8 | | 33 COL 4
DATA 3 9 | | 32 COL 5
ROW 16 10 | HLCD 0488 | 31 COL 6
ROW 15 11 | | 30 COL 7
ROW 14 12 | | 29 COL 8
ROW 13 13 | | 28 COL 9
ROW 12 14 | | 27 COL 10
ROW 11 15 | | 26 COL 11
ROW 10 16 | | 25 COL 12
ROW 9 17 | | 24 COL 13
ROW 8 18 | | 23 COL 14
ROW 7 19 | | 22 COL 15
GND 20 |___________| 21 COL 16
*/
class hlcd0488_device : public device_t
{
public:
hlcd0488_device(const machine_config &mconfig, const char *tag, device_t *owner, u32 clock = 0);
// configuration helpers
auto write_cols() { return m_write_cols.bind(); } // COL pins in data, ROW pins in offset
DECLARE_WRITE_LINE_MEMBER(latch_pulse_w);
DECLARE_WRITE_LINE_MEMBER(data_clk_w);
void data_w(u8 data);
protected:
// device-level overrides
virtual void device_start() override;
virtual void device_timer(emu_timer &timer, device_timer_id id, int param, void *ptr) override;
private:
// pin state
int m_latch_pulse = 0;
int m_latch_pulse_prev = 0;
int m_data_clk = 0;
int m_data_clk_prev = 0;
u8 m_data = 0;
u8 m_count = 0;
u8 m_latch[8];
u8 m_hold[8];
emu_timer *m_sync_timer;
devcb_write16 m_write_cols;
};
DECLARE_DEVICE_TYPE(HLCD0488, hlcd0488_device)
#endif // MAME_VIDEO_HLCD0488_H

View File

@ -1,6 +1,6 @@
// license:BSD-3-Clause // license:BSD-3-Clause
// copyright-holders:Wilbert Pol, hap // copyright-holders:Wilbert Pol, hap
// thanks-to:Kevin Horton, Sean Riddle // thanks-to:Dan Boris, Kevin Horton, Sean Riddle
/*************************************************************************** /***************************************************************************
Milton Bradley MicroVision, handheld game console Milton Bradley MicroVision, handheld game console
@ -19,7 +19,8 @@ The Connect Four I8021 game is clocked at around 2MHz. The TMS1100 versions
of the games were clocked at around 500KHz, 550KHz, or 350KHz. of the games were clocked at around 500KHz, 550KHz, or 350KHz.
Each game had a screen- and keypad overlay attached to it, MAME external Each game had a screen- and keypad overlay attached to it, MAME external
artwork is recommended. artwork is recommended. It's also advised to disable screen filtering,
eg. with -prescale, or on Windows simply -video gdi.
TODO: TODO:
- Finish support for i8021 based cartridges - Finish support for i8021 based cartridges
@ -34,6 +35,7 @@ TODO:
#include "cpu/tms1000/tms1100.h" #include "cpu/tms1000/tms1100.h"
#include "sound/dac.h" #include "sound/dac.h"
#include "sound/volt_reg.h" #include "sound/volt_reg.h"
#include "video/hlcd0488.h"
#include "emupal.h" #include "emupal.h"
#include "softlist.h" #include "softlist.h"
@ -54,6 +56,7 @@ public:
m_dac( *this, "dac" ), m_dac( *this, "dac" ),
m_i8021( *this, "i8021_cpu" ), m_i8021( *this, "i8021_cpu" ),
m_tms1100( *this, "tms1100_cpu" ), m_tms1100( *this, "tms1100_cpu" ),
m_lcd(*this, "lcd"),
m_cart(*this, "cartslot"), m_cart(*this, "cartslot"),
m_inputs(*this, "COL%u", 0), m_inputs(*this, "COL%u", 0),
m_paddle(*this, "PADDLE"), m_paddle(*this, "PADDLE"),
@ -96,6 +99,7 @@ private:
required_device<dac_byte_interface> m_dac; required_device<dac_byte_interface> m_dac;
optional_device<i8021_device> m_i8021; optional_device<i8021_device> m_i8021;
optional_device<tms1100_cpu_device> m_tms1100; optional_device<tms1100_cpu_device> m_tms1100;
required_device<hlcd0488_device> m_lcd;
required_device<generic_slot_device> m_cart; required_device<generic_slot_device> m_cart;
required_ioport_array<3> m_inputs; required_ioport_array<3> m_inputs;
required_ioport m_paddle; required_ioport m_paddle;
@ -116,7 +120,7 @@ private:
// generic variables // generic variables
void update_lcd(); void update_lcd();
void lcd_write(uint8_t control, uint8_t data); DECLARE_WRITE16_MEMBER(lcd_output_w);
void apply_settings(void); void apply_settings(void);
@ -126,11 +130,9 @@ private:
bool m_paddle_auto; bool m_paddle_auto;
bool m_paddle_on; bool m_paddle_on;
uint8_t m_lcd_latch[8]; uint8_t m_lcd_data[16][16];
uint8_t m_lcd_holding_latch[8]; u16 m_lcd_row;
uint8_t m_lcd_latch_index; u16 m_lcd_col;
uint8_t m_lcd[16][16];
uint8_t m_lcd_control_old;
}; };
@ -165,20 +167,14 @@ void microvision_state::machine_start()
save_item(NAME(m_t1)); save_item(NAME(m_t1));
save_item(NAME(m_r)); save_item(NAME(m_r));
save_item(NAME(m_o)); save_item(NAME(m_o));
save_item(NAME(m_lcd_latch));
save_item(NAME(m_lcd_latch_index));
save_item(NAME(m_lcd));
save_item(NAME(m_lcd_control_old));
save_item(NAME(m_lcd_holding_latch));
} }
void microvision_state::machine_reset() void microvision_state::machine_reset()
{ {
apply_settings(); apply_settings();
std::fill(std::begin(m_lcd_latch), std::end(m_lcd_latch), 0);
for (auto &elem : m_lcd) for (auto &elem : m_lcd_data)
std::fill(std::begin(elem), std::end(elem), 0); std::fill(std::begin(elem), std::end(elem), 0);
m_o = 0; m_o = 0;
@ -193,15 +189,15 @@ void microvision_state::machine_reset()
void microvision_state::update_lcd() void microvision_state::update_lcd()
{ {
uint16_t row = ( m_lcd_holding_latch[0] << 12 ) | ( m_lcd_holding_latch[1] << 8 ) | ( m_lcd_holding_latch[2] << 4 ) | m_lcd_holding_latch[3]; uint16_t row = m_lcd_row;
uint16_t col = ( m_lcd_holding_latch[4] << 12 ) | ( m_lcd_holding_latch[5] << 8 ) | ( m_lcd_holding_latch[6] << 4 ) | m_lcd_holding_latch[7]; uint16_t col = m_lcd_col;
LOG( "row = %04x, col = %04x\n", row, col ); LOG( "row = %04x, col = %04x\n", row, col );
for ( int i = 0; i < 16; i++ ) for ( int i = 0; i < 16; i++ )
{ {
uint16_t temp = row; uint16_t temp = row;
for (auto & elem : m_lcd) for (auto & elem : m_lcd_data)
{ {
if ( ( temp & col ) & 0x8000 ) if ( ( temp & col ) & 0x8000 )
{ {
@ -220,7 +216,7 @@ uint32_t microvision_state::screen_update(screen_device &screen, bitmap_ind16 &b
{ {
for ( uint8_t j = 0; j < 16; j++ ) for ( uint8_t j = 0; j < 16; j++ )
{ {
bitmap.pix16(i,j) = m_lcd [i] [j]; bitmap.pix16(i,j) = m_lcd_data [i] [j];
} }
} }
@ -232,7 +228,7 @@ WRITE_LINE_MEMBER(microvision_state::screen_vblank)
{ {
if ( state ) if ( state )
{ {
for (auto & elem : m_lcd) for (auto & elem : m_lcd_data)
{ {
for ( int j= 0; j < 16; j++ ) for ( int j= 0; j < 16; j++ )
{ {
@ -246,47 +242,11 @@ WRITE_LINE_MEMBER(microvision_state::screen_vblank)
} }
} }
WRITE16_MEMBER( microvision_state::lcd_output_w )
/*
control is signals LCD5 LCD4
LCD5 = -Data Clk on 0488
LCD4 = Latch pulse on 0488
LCD3 = Data 0
LCD2 = Data 1
LCD1 = Data 2
LCD0 = Data 3
data is signals LCD3 LCD2 LCD1 LCD0
*/
void microvision_state::lcd_write(uint8_t control, uint8_t data)
{ {
// Latch pulse, when high, resets the %8 latch address counter m_lcd_row = offset;
if ( control & 0x01 ) { m_lcd_col = data;
m_lcd_latch_index = 0;
}
// The addressed latches load when -Data Clk is low
if ( ! ( control & 0x02 ) ) {
m_lcd_latch[ m_lcd_latch_index & 0x07 ] = data & 0x0f;
}
// The latch address counter is incremented on rising edges of -Data Clk
if ( ( ! ( m_lcd_control_old & 0x02 ) ) && ( control & 0x02 ) ) {
// Check if Latch pule is low
if ( ! ( control & 0x01 ) ) {
m_lcd_latch_index++;
}
}
// A parallel transfer of data from the addressed latches to the holding latches occurs
// whenever Latch Pulse is high and -Data Clk is high
if ( control == 3 ) {
for ( int i = 0; i < 8; i++ ) {
m_lcd_holding_latch[i] = m_lcd_latch[i];
}
update_lcd(); update_lcd();
}
m_lcd_control_old = control;
} }
@ -331,7 +291,9 @@ WRITE8_MEMBER( microvision_state::i8021_p1_write )
{ {
LOG( "p1_write: %02x\n", data ); LOG( "p1_write: %02x\n", data );
lcd_write( data & 0x03, data >> 4 ); m_lcd->data_w(data >> 4 & 0xf);
m_lcd->latch_pulse_w(BIT(data, 0));
m_lcd->data_clk_w(BIT(data, 1));
} }
@ -433,9 +395,7 @@ WRITE16_MEMBER( microvision_state::tms1100_write_o )
LOG("write_o: %04x\n", data); LOG("write_o: %04x\n", data);
// O0-O3: LCD data // O0-O3: LCD data
m_o = data; m_lcd->data_w(data & 0xf);
lcd_write( ( m_r >> 6 ) & 0x03, m_o & 0x0f );
} }
@ -449,8 +409,8 @@ WRITE16_MEMBER( microvision_state::tms1100_write_r )
{ {
// range is ~360us to ~2663us (measured on 4952-79 REV B PCB) // range is ~360us to ~2663us (measured on 4952-79 REV B PCB)
// note that the games don't use the whole range, so there's a deadzone around the edges // note that the games don't use the whole range, so there's a deadzone around the edges
float step = (2663 - 360) / 255.0; float step = (2000 - 500) / 255.0; // approximate it
m_paddle_timer->adjust(attotime::from_usec(360 + m_paddle->read() * step)); m_paddle_timer->adjust(attotime::from_usec(500 + m_paddle->read() * step));
} }
// R0: speaker lead 2 // R0: speaker lead 2
@ -459,7 +419,8 @@ WRITE16_MEMBER( microvision_state::tms1100_write_r )
// R6: LCD latch pulse // R6: LCD latch pulse
// R7: LCD data clock // R7: LCD data clock
lcd_write((data >> 6) & 0x03, m_o & 0x0f); m_lcd->latch_pulse_w(BIT(data, 6));
m_lcd->data_clk_w(BIT(data, 7));
// R8-R10: input mux // R8-R10: input mux
m_r = data; m_r = data;
@ -640,13 +601,16 @@ void microvision_state::microvision(machine_config &config)
m_tms1100->r().set(FUNC(microvision_state::tms1100_write_r)); m_tms1100->r().set(FUNC(microvision_state::tms1100_write_r));
/* video hardware */ /* video hardware */
HLCD0488(config, m_lcd);
m_lcd->write_cols().set(FUNC(microvision_state::lcd_output_w));
screen_device &screen(SCREEN(config, "screen", SCREEN_TYPE_LCD)); screen_device &screen(SCREEN(config, "screen", SCREEN_TYPE_LCD));
screen.set_refresh_hz(60); screen.set_refresh_hz(60);
screen.set_vblank_time(0); screen.set_vblank_time(0);
screen.set_screen_update(FUNC(microvision_state::screen_update)); screen.set_screen_update(FUNC(microvision_state::screen_update));
screen.screen_vblank().set(FUNC(microvision_state::screen_vblank)); screen.screen_vblank().set(FUNC(microvision_state::screen_vblank));
screen.set_size(16, 16); screen.set_size(16, 16);
screen.set_visarea(0, 15, 0, 15); screen.set_visarea_full();
screen.set_palette("palette"); screen.set_palette("palette");
PALETTE(config, "palette", FUNC(microvision_state::microvision_palette), 16); PALETTE(config, "palette", FUNC(microvision_state::microvision_palette), 16);