ssystem3: added chess unit lcd svg screen [hap, Berger, Achim]

This commit is contained in:
hap 2020-07-29 20:16:59 +02:00
parent fac0622f07
commit 23b6a0c6cc
5 changed files with 188 additions and 69 deletions

View File

@ -57,7 +57,7 @@ public:
uint8_t read_alt(offs_t offset) { return read(((offset << 1) & 0x02) | ((offset >> 1) & 0x01)); }
void write_alt(offs_t offset, uint8_t data) { write(((offset << 1) & 0x02) | ((offset >> 1) & 0x01), data); }
uint8_t port_b_z_mask() const { return ~m_ddr_b; } // see first note in .c
uint8_t port_b_z_mask() const { return ~m_ddr_b; } // see notes
void porta_w(uint8_t data);
void write_porta_line(int line, bool state);

View File

@ -5,8 +5,7 @@
Hughes HLCD 0438 LCD Driver
32 segment outputs, may also be used as a column driver
TODO:
- OSC (LCD phi pin)
LCD pin can be driven manually, or oscillating.
*/
@ -32,15 +31,23 @@ hlcd0438_device::hlcd0438_device(const machine_config &mconfig, const char *tag,
void hlcd0438_device::device_start()
{
// resolve callbacks
m_write_segs.resolve_safe();
m_write_data.resolve_safe();
// timer (when LCD pin is oscillator)
m_lcd_timer = timer_alloc();
attotime period = (clock() != 0) ? attotime::from_hz(2 * clock()) : attotime::never;
m_lcd_timer->adjust(period, 0, period);
// register for savestates
save_item(NAME(m_data_in));
save_item(NAME(m_data_out));
save_item(NAME(m_clk));
save_item(NAME(m_load));
save_item(NAME(m_lcd));
save_item(NAME(m_shift));
save_item(NAME(m_latch));
}
@ -48,13 +55,6 @@ void hlcd0438_device::device_start()
// handlers
//-------------------------------------------------
void hlcd0438_device::update_output()
{
// load output latches while LOAD pin is high
if (m_load)
m_write_segs(0, m_shift);
}
void hlcd0438_device::clock_w(int state)
{
state = state ? 1 : 0;
@ -67,10 +67,29 @@ void hlcd0438_device::clock_w(int state)
m_shift = m_shift << 1 | m_data_in;
// output
m_write_data(m_data_out);
update_output();
load_w(m_load);
}
m_clk = state;
}
void hlcd0438_device::load_w(int state)
{
m_load = state ? 1 : 0;
// load to output latches while LOAD pin is high
if (m_load)
m_latch = m_shift;
}
void hlcd0438_device::lcd_w(int state)
{
state = state ? 1 : 0;
// LCD pin drives backplate
if (state != m_lcd)
m_write_segs(m_lcd, m_latch);
m_lcd = state;
}

View File

@ -24,7 +24,7 @@
SEG 28 7 | | 34 DATA IN
SEG 27 8 | | 33 SEG 4
SEG 26 9 | | 32 SEG 5
SEG 25 10 | HLCD 0438 | 31 LCD phi
SEG 25 10 | HLCD 0438 | 31 LCD
SEG 24 11 | | 30 BP
SEG 23 12 | | 29 SEG 6
SEG 22 13 | | 28 SEG 7
@ -45,29 +45,33 @@ public:
hlcd0438_device(const machine_config &mconfig, const char *tag, device_t *owner, u32 clock);
// configuration helpers
auto write_segs() { return m_write_segs.bind(); } // SEG pins
auto write_segs() { return m_write_segs.bind(); } // BP pin in offset, SEG pins in data
auto write_data() { return m_write_data.bind(); } // DATA OUT pin
hlcd0438_device &set_load(int state) { m_load = state ? 1 : 0; return *this; } // if hardwired, can just set LOAD pin state here
void data_w(int state) { m_data_in = state ? 1 : 0; }
int data_r() { return m_data_out; }
void load_w(int state) { m_load = state ? 1 : 0; update_output(); }
void clock_w(int state);
void load_w(int state);
void lcd_w(int state);
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 { lcd_w(!m_lcd); }
private:
emu_timer *m_lcd_timer;
// pin state
int m_data_in = 0;
int m_data_out = 0;
int m_clk = 0;
int m_load = 0;
int m_lcd = 0;
u32 m_shift = 0;
void update_output();
u32 m_latch = 0;
devcb_write32 m_write_segs;
devcb_write_line m_write_data;

View File

@ -8,7 +8,7 @@ both SciSys and Novag. Which company was responsible for which part of the
manufacturing chain is unknown. The software is by SciSys (no mention of Novag
in the ROM, it has "COPYRIGHT SCISYS LTD 1979").
This is their 1st original product. MK II was licensed from Commodore, and
This is their 1st original product. MK II was licensed from Peter Jennings, and
MK I was, to put it bluntly, a bootleg. The chess engine is by Mike Johnson,
with support from David Levy.
@ -25,7 +25,7 @@ Master Unit:
Chess Unit:
- PCB label: Radofin XM-2057-0C
- Fairchild F6808P CPU @ ~6MHz (M6808 compatible)
- Fairchild F6808P CPU @ ~6.8MHz (M6808 compatible)
- Fairchild F6821P PIA
- 2KB ROM(2316), 128x8 RAM(F6810P)
- 2*HLCD0438, chessboard LCD
@ -54,7 +54,9 @@ TODO:
Should be doable to add, but 6522 device doesn't support live clock changes.
- LCD TC pin? connects to the display, source is a 50hz timer(from power supply),
probably to keep refreshing the LCD when inactive, there is no need to emulate it
- finish chess unit lcd emulation
- chess unit screen briefly shows glitches when the subcpu receives an NMI in the
middle of updating the LCD, BTANB?
- add chessboard screen to artwork
- dump/add printer unit
- dump/add ssystem3 1980 program revision, were the BTANB fixed?
- ssystem4 softwarelist if a prototype cartridge is ever dumped
@ -97,15 +99,20 @@ public:
ssystem3_state(const machine_config &mconfig, device_type type, const char *tag) :
driver_device(mconfig, type, tag),
m_maincpu(*this, "maincpu"),
m_subcpu(*this, "subcpu"),
m_via(*this, "via"),
m_pia(*this, "pia"),
m_lcd(*this, "lcd"),
m_display(*this, "display"),
m_lcd1(*this, "lcd1"),
m_lcd2(*this, "lcd2_%u", 0),
m_display(*this, "display%u", 0),
m_dac(*this, "dac"),
m_nvram(*this, "nvram"),
m_inputs(*this, "IN.%u", 0)
m_inputs(*this, "IN.%u", 0),
m_out_lcd2(*this, "s%u.%u", 0U, 0U)
{ }
DECLARE_INPUT_CHANGED_MEMBER(cu_plug);
// machine configs
void ssystem3(machine_config &config);
void ssystem4(machine_config &config);
@ -118,13 +125,16 @@ protected:
private:
// devices/pointers
required_device<cpu_device> m_maincpu;
optional_device<cpu_device> m_subcpu;
required_device<via6522_device> m_via;
optional_device<pia6821_device> m_pia;
required_device<md4332b_device> m_lcd;
required_device<pwm_display_device> m_display;
required_device<md4332b_device> m_lcd1;
optional_device_array<hlcd0438_device, 2> m_lcd2;
optional_device_array<pwm_display_device, 2> m_display;
required_device<dac_bit_interface> m_dac;
optional_shared_ptr<u8> m_nvram;
required_ioport_array<4+2> m_inputs;
optional_ioport_array<4+3> m_inputs;
output_finder<8, 48> m_out_lcd2;
// address maps
void ssystem3_map(address_map &map);
@ -132,7 +142,7 @@ private:
void chessunit_map(address_map &map);
// I/O handlers
void lcd_q_w(u32 data) { m_lcd_q = data; }
void lcd1_output_w(u32 data) { m_lcd1_data = data; }
void input_w(u8 data);
u8 input_r();
void control_w(u8 data);
@ -141,26 +151,34 @@ private:
void nvram_w(offs_t offset, u8 data);
u8 nvram_r(offs_t offset);
void pia_a_w(u8 data);
u8 pia_a_r();
void pia_b_w(u8 data);
u8 pia_b_r();
void pia_cb2_w(int state);
void lcd2_pwm_w(offs_t offset, u8 data);
void lcd2_update();
template<int N> void lcd2_output_w(offs_t offset, u32 data);
void cu_pia_a_w(u8 data);
u8 cu_pia_a_r();
void cu_pia_b_w(u8 data);
u8 cu_pia_b_r();
u8 m_inp_mux = 0;
u8 m_control = 0;
u8 m_shift = 0;
u32 m_lcd_q = 0;
u32 m_lcd1_data = 0;
u64 m_lcd2_data = 0;
u8 m_lcd2_select = 0;
bool m_xor_kludge = false;
};
void ssystem3_state::machine_start()
{
m_out_lcd2.resolve();
// register for savestates
save_item(NAME(m_inp_mux));
save_item(NAME(m_control));
save_item(NAME(m_shift));
save_item(NAME(m_lcd_q));
save_item(NAME(m_lcd1_data));
save_item(NAME(m_lcd2_data));
save_item(NAME(m_lcd2_select));
}
@ -206,28 +224,28 @@ void ssystem3_state::control_w(u8 data)
// PB1: LCD DI
// PB2: LCD CLK
m_lcd->di_w(BIT(data, 1));
m_lcd->clk_w(BIT(data, 2));
m_lcd1->di_w(BIT(data, 1));
m_lcd1->clk_w(BIT(data, 2));
// PB2 also clocks a 4015B
// DA: LCD DO, DB: Q3A
if (data & ~m_control & 4)
{
m_shift = m_shift << 1 | m_lcd->do_r();
m_shift = m_shift << 1 | m_lcd1->do_r();
// weird TTL maze, I assume it's a hw kludge to fix a bug after the maskroms were already manufactured
u8 xorval = m_xor_kludge && (BIT(m_shift, 3) & ~(BIT(m_shift, 1) ^ BIT(m_shift, 4)) & ~(BIT(m_lcd_q, 7) & BIT(m_lcd_q, 23))) ? 0x12 : 0;
u8 xorval = m_xor_kludge && (BIT(m_shift, 3) & ~(BIT(m_shift, 1) ^ BIT(m_shift, 4)) & ~(BIT(m_lcd1_data, 7) & BIT(m_lcd1_data, 23))) ? 0x12 : 0;
// update display
for (int i = 0; i < 4; i++)
m_display->write_row(i, m_lcd_q >> (8*i) & 0xff);
m_display->write_row(4, (m_shift ^ xorval) | 0x100);
m_display->update();
m_display[0]->write_row(i, m_lcd1_data >> (8*i) & 0xff);
m_display[0]->write_row(4, (m_shift ^ xorval) | 0x100);
m_display[0]->update();
}
// PB3: device serial out
if (m_inputs[5]->read() & 2)
m_pia->ca1_w(BIT(data, 3));
m_pia->ca1_w(BIT(~data, 3));
// PB7: tied to PB6 (pulse timer 2)
m_via->write_pb6(BIT(data, 7));
@ -239,9 +257,10 @@ u8 ssystem3_state::control_r()
{
u8 data = 0;
// PB4: device busy
// PB5: device attached?
//data ^= 0x30;
// PB4: device busy (unused on chess unit)
// PB5: device attached
if (m_inputs[5]->read() & 2)
data ^= 0x30;
return ~data;
}
@ -264,26 +283,74 @@ u8 ssystem3_state::nvram_r(offs_t offset)
// Chess Unit
void ssystem3_state::pia_a_w(u8 data)
INPUT_CHANGED_MEMBER(ssystem3_state::cu_plug)
{
m_subcpu->set_input_line(INPUT_LINE_RESET, newval ? CLEAR_LINE : ASSERT_LINE);
if (newval)
m_pia->reset();
else
m_display[1]->clear();
}
u8 ssystem3_state::pia_a_r()
void ssystem3_state::lcd2_pwm_w(offs_t offset, u8 data)
{
return 0;
m_out_lcd2[offset & 0x3f][offset >> 6] = data;
}
void ssystem3_state::pia_b_w(u8 data)
void ssystem3_state::lcd2_update()
{
if (m_inputs[5]->read() & 2)
m_display[1]->matrix(1 << m_lcd2_select, m_lcd2_data);
}
u8 ssystem3_state::pia_b_r()
template<int N>
void ssystem3_state::lcd2_output_w(offs_t offset, u32 data)
{
return 0;
if (!offset)
data = 0;
m_lcd2_data = u64(data) << 32 | m_lcd2_data >> 32;
if (N == 1)
lcd2_update();
}
void ssystem3_state::pia_cb2_w(int state)
void ssystem3_state::cu_pia_a_w(u8 data)
{
// PA0-PA2: CD4051 to LCD column
m_lcd2_select = data & 7;
lcd2_update();
}
u8 ssystem3_state::cu_pia_a_r()
{
// PA7: serial data in
return ~m_control << 4 & 0x80;
}
void ssystem3_state::cu_pia_b_w(u8 data)
{
// PB3: LCD LOAD (both)
// PB4: LCD LCD (both) + CD4051 COM OUT/IN
// PB6: LCD CLOCK (both)
// PB7: LCD DATA IN (1st)
m_lcd2[0]->data_w(BIT(data, 7));
for (int i = 0; i < 2; i++)
m_lcd2[i]->clock_w(BIT(data, 6));
for (int i = 0; i < 2; i++)
m_lcd2[i]->load_w(BIT(~data, 3));
for (int i = 0; i < 2; i++)
m_lcd2[i]->lcd_w(BIT(data, 4));
}
u8 ssystem3_state::cu_pia_b_r()
{
// PB5: look switch
return m_inputs[6]->read() << 5 & 0x20;
}
@ -310,9 +377,9 @@ void ssystem3_state::ssystem4_map(address_map &map)
void ssystem3_state::chessunit_map(address_map &map)
{
map(0x3600, 0x367f).ram();
map(0x3000, 0x307f).mirror(0x0f80).ram();
map(0x4000, 0x47ff).mirror(0xb800).rom();
map(0xa000, 0xa003).rw(m_pia, FUNC(pia6821_device::read), FUNC(pia6821_device::write));
map(0x8000, 0x8003).mirror(0x3ffc).rw(m_pia, FUNC(pia6821_device::read), FUNC(pia6821_device::write));
}
@ -350,7 +417,7 @@ static INPUT_PORTS_START( ssystem4 )
PORT_CONFNAME( 0x01, 0x01, "Sound" )
PORT_CONFSETTING( 0x00, DEF_STR( Off ) )
PORT_CONFSETTING( 0x01, DEF_STR( On ) )
PORT_CONFNAME( 0x02, 0x02, "LCD Light" )
PORT_CONFNAME( 0x02, 0x02, "LCD 1 Light" )
PORT_CONFSETTING( 0x00, DEF_STR( Off ) )
PORT_CONFSETTING( 0x02, DEF_STR( On ) )
@ -368,14 +435,22 @@ static INPUT_PORTS_START( ssystem3 )
PORT_MODIFY("IN.3")
PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_T) PORT_NAME("Time") // spring-loaded
PORT_MODIFY("IN.4") // switches
PORT_CONFNAME( 0x04, 0x04, "LCD 2 Light" )
PORT_CONFSETTING( 0x00, DEF_STR( Off ) )
PORT_CONFSETTING( 0x04, DEF_STR( On ) )
PORT_MODIFY("IN.5") // accessories/diodes
PORT_CONFNAME( 0x01, 0x01, "Memory Unit" )
PORT_CONFSETTING( 0x00, DEF_STR( Off ) )
PORT_CONFSETTING( 0x01, DEF_STR( On ) )
PORT_CONFNAME( 0x02, 0x02, "Chess Unit" )
PORT_CONFNAME( 0x02, 0x02, "Chess Unit" ) PORT_CHANGED_MEMBER(DEVICE_SELF, ssystem3_state, cu_plug, 0)
PORT_CONFSETTING( 0x00, DEF_STR( Off ) )
PORT_CONFSETTING( 0x02, DEF_STR( On ) )
PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_CUSTOM)
PORT_START("IN.6") // chess unit
PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYPAD) PORT_TOGGLE PORT_CODE(KEYCODE_O) PORT_NAME("Look")
INPUT_PORTS_END
@ -397,16 +472,16 @@ void ssystem3_state::ssystem4(machine_config &config)
m_via->readpb_handler().set(FUNC(ssystem3_state::control_r));
/* video hardware */
MD4332B(config, m_lcd);
m_lcd->write_q().set(FUNC(ssystem3_state::lcd_q_w));
MD4332B(config, m_lcd1);
m_lcd1->write_q().set(FUNC(ssystem3_state::lcd1_output_w));
screen_device &screen(SCREEN(config, "screen", SCREEN_TYPE_SVG));
screen.set_refresh_hz(60);
screen.set_size(1920/2, 729/2);
screen.set_visarea_full();
PWM_DISPLAY(config, m_display).set_size(5, 9);
m_display->set_bri_levels(0.25);
PWM_DISPLAY(config, m_display[0]).set_size(5, 9);
m_display[0]->set_bri_levels(0.25);
config.set_default_layout(layout_saitek_ssystem4);
@ -423,22 +498,39 @@ void ssystem3_state::ssystem3(machine_config &config)
/* basic machine hardware */
m_maincpu->set_addrmap(AS_PROGRAM, &ssystem3_state::ssystem3_map);
m6808_cpu_device &subcpu(M6808(config, "subcpu", 6000000)); // LC circuit
subcpu.set_addrmap(AS_PROGRAM, &ssystem3_state::chessunit_map);
M6808(config, m_subcpu, 6800000); // LC circuit, measured
m_subcpu->set_addrmap(AS_PROGRAM, &ssystem3_state::chessunit_map);
config.set_perfect_quantum(m_maincpu);
PIA6821(config, m_pia, 0);
m_pia->irqa_handler().set_inputline("subcpu", INPUT_LINE_NMI);
m_pia->writepa_handler().set(FUNC(ssystem3_state::pia_a_w));
m_pia->readpa_handler().set(FUNC(ssystem3_state::pia_a_r));
m_pia->writepb_handler().set(FUNC(ssystem3_state::pia_b_w));
m_pia->readpb_handler().set(FUNC(ssystem3_state::pia_b_r));
m_pia->cb2_handler().set(FUNC(ssystem3_state::pia_cb2_w));
m_pia->irqa_handler().set_inputline(m_subcpu, INPUT_LINE_NMI);
m_pia->writepa_handler().set(FUNC(ssystem3_state::cu_pia_a_w));
m_pia->readpa_handler().set(FUNC(ssystem3_state::cu_pia_a_r));
m_pia->writepb_handler().set(FUNC(ssystem3_state::cu_pia_b_w));
m_pia->readpb_handler().set(FUNC(ssystem3_state::cu_pia_b_r));
m_pia->cb2_handler().set(m_pia, FUNC(pia6821_device::ca2_w));
NVRAM(config, "nvram", nvram_device::DEFAULT_ALL_0);
m_display->set_segmask(0xf, 0x7f); // 7segs are at expected positions
/* video hardware */
HLCD0438(config, m_lcd2[0], 0);
m_lcd2[0]->write_segs().set(FUNC(ssystem3_state::lcd2_output_w<0>));
m_lcd2[0]->write_data().set(m_lcd2[1], FUNC(hlcd0438_device::data_w));
HLCD0438(config, m_lcd2[1], 0);
m_lcd2[1]->write_segs().set(FUNC(ssystem3_state::lcd2_output_w<1>));
screen_device &screen(SCREEN(config, "chessunit", SCREEN_TYPE_SVG));
screen.set_refresh_hz(60);
screen.set_size(1060, 1080);
screen.set_visarea_full();
PWM_DISPLAY(config, m_display[1]).set_size(8, 48);
m_display[1]->set_refresh(attotime::from_hz(30));
m_display[1]->output_x().set(FUNC(ssystem3_state::lcd2_pwm_w));
m_display[0]->set_segmask(0xf, 0x7f); // 7segs are at expected positions
config.set_default_layout(layout_saitek_ssystem3);
}
@ -464,6 +556,9 @@ ROM_START( ssystem3 )
ROM_REGION(53552, "screen", 0)
ROM_LOAD("ssystem3.svg", 0, 53552, CRC(6047f88f) SHA1(2ff9cfce01cd3811a3f46f84b47fdc4ea2cf2ba8) )
ROM_REGION(748939, "chessunit", 0)
ROM_LOAD("chessunit.svg", 0, 748939, CRC(713a46fd) SHA1(6119162fb7c00f81aeca0dfe274475dc8575dd70) )
ROM_END
ROM_START( ssystem4 )

View File

@ -1,5 +1,6 @@
// license:BSD-3-Clause
// copyright-holders:hap
// thanks-to:digshadow, segher
/***************************************************************************
Bandai Tamagotchi generation 1 hardware