diff --git a/src/mame/drivers/k28.cpp b/src/mame/drivers/k28.cpp index dd4f890cebd..d3fea3246b6 100644 --- a/src/mame/drivers/k28.cpp +++ b/src/mame/drivers/k28.cpp @@ -22,7 +22,10 @@ public: m_maincpu(*this, "maincpu"), m_tms6100(*this, "tms6100"), m_speech(*this, "speech"), - m_inp_matrix(*this, "IN") + m_inp_matrix(*this, "IN"), + m_display_wait(33), + m_display_maxy(1), + m_display_maxx(0) { } // devices @@ -31,6 +34,23 @@ public: required_device m_speech; required_ioport_array<7> m_inp_matrix; + // display common + int m_display_wait; // led/lamp off-delay in microseconds (default 33ms) + int m_display_maxy; // display matrix number of rows + int m_display_maxx; // display matrix number of columns (max 31 for now) + + UINT32 m_display_state[0x20]; // display matrix rows data (last bit is used for always-on) + UINT16 m_display_segmask[0x20]; // if not 0, display matrix row is a digit, mask indicates connected segments + UINT32 m_display_cache[0x20]; // (internal use) + UINT8 m_display_decay[0x20][0x20]; // (internal use) + + TIMER_DEVICE_CALLBACK_MEMBER(display_decay_tick); + void display_update(); + void set_display_size(int maxx, int maxy); + void display_matrix(int maxx, int maxy, UINT32 setx, UINT32 sety); + void display_matrix_seg(int maxx, int maxy, UINT32 setx, UINT32 sety, UINT16 segmask); + + bool m_power_on; UINT8 m_inp_mux; UINT8 m_phoneme; int m_speech_strobe; @@ -38,6 +58,7 @@ public: int m_vfd_data_in; int m_vfd_clock; UINT64 m_vfd_shiftreg; + UINT64 m_vfd_shiftreg_out; int m_vfd_shiftcount; DECLARE_WRITE8_MEMBER(mcu_p0_w); @@ -46,14 +67,27 @@ public: DECLARE_WRITE8_MEMBER(mcu_p2_w); DECLARE_WRITE8_MEMBER(mcu_prog_w); DECLARE_READ8_MEMBER(mcu_t1_r); + + DECLARE_INPUT_CHANGED_MEMBER(power_on); + void power_off(); protected: virtual void machine_start() override; + virtual void machine_reset() override; }; + +// machine start/reset/power + void k28_state::machine_start() { // zerofill + memset(m_display_state, 0, sizeof(m_display_state)); + memset(m_display_cache, ~0, sizeof(m_display_cache)); + memset(m_display_decay, 0, sizeof(m_display_decay)); + memset(m_display_segmask, ~0, sizeof(m_display_segmask)); // ! + + m_power_on = false; m_inp_mux = 0; m_phoneme = 0x3f; m_speech_strobe = 0; @@ -61,9 +95,20 @@ void k28_state::machine_start() m_vfd_data_in = 0; m_vfd_clock = 0; m_vfd_shiftreg = 0; + m_vfd_shiftreg_out = 0; m_vfd_shiftcount = 0; // register for savestates + save_item(NAME(m_display_maxy)); + save_item(NAME(m_display_maxx)); + save_item(NAME(m_display_wait)); + + save_item(NAME(m_display_state)); + /* save_item(NAME(m_display_cache)); */ // don't save! + save_item(NAME(m_display_decay)); + save_item(NAME(m_display_segmask)); + + save_item(NAME(m_power_on)); save_item(NAME(m_inp_mux)); save_item(NAME(m_phoneme)); save_item(NAME(m_speech_strobe)); @@ -71,9 +116,132 @@ void k28_state::machine_start() save_item(NAME(m_vfd_data_in)); save_item(NAME(m_vfd_clock)); save_item(NAME(m_vfd_shiftreg)); + save_item(NAME(m_vfd_shiftreg_out)); save_item(NAME(m_vfd_shiftcount)); } +void k28_state::machine_reset() +{ + m_power_on = true; +} + +INPUT_CHANGED_MEMBER(k28_state::power_on) +{ + if (newval && !m_power_on) + { + m_power_on = true; + m_maincpu->set_input_line(INPUT_LINE_RESET, CLEAR_LINE); + } +} + +void k28_state::power_off() +{ + m_power_on = false; + m_maincpu->set_input_line(INPUT_LINE_RESET, ASSERT_LINE); +} + + +/*************************************************************************** + + Helper Functions + +***************************************************************************/ + +// The device may strobe the outputs very fast, it is unnoticeable to the user. +// To prevent flickering here, we need to simulate a decay. + +void k28_state::display_update() +{ + UINT32 active_state[0x20]; + + for (int y = 0; y < m_display_maxy; y++) + { + active_state[y] = 0; + + for (int x = 0; x <= m_display_maxx; x++) + { + // turn on powered segments + if (m_power_on && m_display_state[y] >> x & 1) + m_display_decay[y][x] = m_display_wait; + + // determine active state + UINT32 ds = (m_display_decay[y][x] != 0) ? 1 : 0; + active_state[y] |= (ds << x); + } + } + + // on difference, send to output + for (int y = 0; y < m_display_maxy; y++) + if (m_display_cache[y] != active_state[y]) + { + if (m_display_segmask[y] != 0) + output().set_digit_value(y, active_state[y] & m_display_segmask[y]); + + const int mul = (m_display_maxx <= 10) ? 10 : 100; + for (int x = 0; x <= m_display_maxx; x++) + { + int state = active_state[y] >> x & 1; + char buf1[0x10]; // lampyx + char buf2[0x10]; // y.x + + if (x == m_display_maxx) + { + // always-on if selected + sprintf(buf1, "lamp%da", y); + sprintf(buf2, "%d.a", y); + } + else + { + sprintf(buf1, "lamp%d", y * mul + x); + sprintf(buf2, "%d.%d", y, x); + } + output().set_value(buf1, state); + output().set_value(buf2, state); + } + } + + memcpy(m_display_cache, active_state, sizeof(m_display_cache)); +} + +TIMER_DEVICE_CALLBACK_MEMBER(k28_state::display_decay_tick) +{ + // slowly turn off unpowered segments + for (int y = 0; y < m_display_maxy; y++) + for (int x = 0; x <= m_display_maxx; x++) + if (m_display_decay[y][x] != 0) + m_display_decay[y][x]--; + + display_update(); +} + +void k28_state::set_display_size(int maxx, int maxy) +{ + m_display_maxx = maxx; + m_display_maxy = maxy; +} + +void k28_state::display_matrix(int maxx, int maxy, UINT32 setx, UINT32 sety) +{ + set_display_size(maxx, maxy); + + // update current state + UINT32 mask = (1 << maxx) - 1; + for (int y = 0; y < maxy; y++) + m_display_state[y] = (sety >> y & 1) ? ((setx & mask) | (1 << maxx)) : 0; + + display_update(); +} + +void k28_state::display_matrix_seg(int maxx, int maxy, UINT32 setx, UINT32 sety, UINT16 segmask) +{ + // expects m_display_segmask to be not-0 + for (int y = 0; y < maxy; y++) + m_display_segmask[y] &= segmask; + + display_matrix(maxx, maxy, setx, sety); +} + + /*************************************************************************** @@ -165,15 +333,12 @@ WRITE8_MEMBER(k28_state::mcu_prog_w) // output 16-24: digit select UINT16 digit_sel = (UINT16)(m_vfd_shiftreg >> 10) & 0x1ff; + display_matrix_seg(16, 9, seg_data, digit_sel, 0x3fff); // output 25: power-off request on falling edge - - // update display - for (int i = 0; i < 9; i++) - if (digit_sel >> (8-i) & 1) - { - output().set_digit_value(i, seg_data & 0x3fff); - } + if (~m_vfd_shiftreg & m_vfd_shiftreg_out & 0x200) + power_off(); + m_vfd_shiftreg_out = m_vfd_shiftreg; } else { @@ -207,7 +372,7 @@ ADDRESS_MAP_END ***************************************************************************/ static INPUT_PORTS_START( k28 ) - PORT_START("IN.0") // 0 + PORT_START("IN.0") PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_EQUALS) // YES PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_G) PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_Q) @@ -217,7 +382,7 @@ static INPUT_PORTS_START( k28 ) PORT_BIT( 0x40, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_R) PORT_BIT( 0x80, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_1) // SEL - PORT_START("IN.1") // 1 + PORT_START("IN.1") PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_2) // SCRL PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_F) PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_P) @@ -227,7 +392,7 @@ static INPUT_PORTS_START( k28 ) PORT_BIT( 0x40, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_S) PORT_BIT( 0x80, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_DEL) PORT_CODE(KEYCODE_BACKSPACE) - PORT_START("IN.2") // 2 + PORT_START("IN.2") PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_3) // MENU PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_E) PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_O) @@ -237,27 +402,17 @@ static INPUT_PORTS_START( k28 ) PORT_BIT( 0x40, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_T) PORT_BIT( 0x80, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_ENTER) - PORT_START("IN.6") // 3 - PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_PGDN) // OFF - PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_A) - PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_K) - PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_U) - PORT_BIT( 0x10, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_SLASH_PAD) - PORT_BIT( 0x20, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_ASTERISK) PORT_NAME(UTF8_MULTIPLY) - PORT_BIT( 0x40, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_MINUS_PAD) PORT_NAME(UTF8_DIVIDE) - PORT_BIT( 0x80, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_PLUS_PAD) + PORT_START("IN.3") + PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_5) // PROMPT + PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_D) + PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_N) + PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_X) + PORT_BIT( 0x10, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_7_PAD) + PORT_BIT( 0x20, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_4_PAD) + PORT_BIT( 0x40, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_1_PAD) + PORT_BIT( 0x80, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_0_PAD) - PORT_START("IN.5") // 4 - PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_PGUP) // ON - PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_B) - PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_L) - PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_V) - PORT_BIT( 0x10, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_9_PAD) - PORT_BIT( 0x20, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_6_PAD) - PORT_BIT( 0x40, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_3_PAD) - PORT_BIT( 0x80, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_ENTER_PAD) - - PORT_START("IN.4") // 5 + PORT_START("IN.4") PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_4) // REPT PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_C) PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_M) @@ -267,15 +422,25 @@ static INPUT_PORTS_START( k28 ) PORT_BIT( 0x40, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_2_PAD) PORT_BIT( 0x80, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_DEL_PAD) - PORT_START("IN.3") // 6 - PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_5) // PROMPT - PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_D) - PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_N) - PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_X) - PORT_BIT( 0x10, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_7_PAD) - PORT_BIT( 0x20, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_4_PAD) - PORT_BIT( 0x40, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_1_PAD) - PORT_BIT( 0x80, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_0_PAD) + PORT_START("IN.5") + PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_PGUP) PORT_NAME("On") PORT_CHANGED_MEMBER(DEVICE_SELF, k28_state, power_on, 0) + PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_B) + PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_L) + PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_V) + PORT_BIT( 0x10, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_9_PAD) + PORT_BIT( 0x20, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_6_PAD) + PORT_BIT( 0x40, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_3_PAD) + PORT_BIT( 0x80, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_ENTER_PAD) + + PORT_START("IN.6") + PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_PGDN) PORT_NAME("Off") // -> auto_power_off + PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_A) + PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_K) + PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_U) + PORT_BIT( 0x10, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_SLASH_PAD) + PORT_BIT( 0x20, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_ASTERISK) PORT_NAME(UTF8_MULTIPLY) + PORT_BIT( 0x40, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_MINUS_PAD) PORT_NAME(UTF8_DIVIDE) + PORT_BIT( 0x80, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_PLUS_PAD) INPUT_PORTS_END @@ -293,6 +458,8 @@ static MACHINE_CONFIG_START( k28, k28_state ) MCFG_CPU_IO_MAP(k28_mcu_map) MCFG_DEVICE_ADD("tms6100", TMS6100, XTAL_3_579545MHz) + + MCFG_TIMER_DRIVER_ADD_PERIODIC("display_decay", k28_state, display_decay_tick, attotime::from_msec(1)) MCFG_DEFAULT_LAYOUT(layout_k28) /* sound hardware */ diff --git a/src/mame/layout/k28.lay b/src/mame/layout/k28.lay index 14f53a0704d..70e4c1e0577 100644 --- a/src/mame/layout/k28.lay +++ b/src/mame/layout/k28.lay @@ -28,41 +28,41 @@ - - - + + + - - - + + + - - - + + + - - - + + + - - - + + + - - - + + + - - - + + + - - - + + +