hd6301x, hd6301y & derivatives: Updates to internal peripheral emulation

- Add Timer 2 and its associated interrupt and allow it to clock the serial port
- Change divider for external serial clock to 16
- Remove timer hack in supremo
This commit is contained in:
AJR 2021-05-08 10:54:37 -04:00
parent 9e0e9fb978
commit c137f89f86
5 changed files with 209 additions and 16 deletions

View File

@ -118,7 +118,7 @@ void epson_pf10_device::device_start()
void epson_pf10_device::device_reset()
{
m_timer->adjust(attotime::zero, 0, attotime::from_hz(38400 * 8));
m_timer->adjust(attotime::zero, 0, attotime::from_hz(38400 * 16));
}
//-------------------------------------------------

View File

@ -82,6 +82,7 @@ enum
#define TAKE_OCI enter_interrupt("take OCI\n",0xfff4)
#define TAKE_TOI enter_interrupt("take TOI\n",0xfff2)
#define TAKE_SCI enter_interrupt("take SCI\n",0xfff0)
#define TAKE_CMI enter_interrupt("take CMI\n",0xffec)
/* mnemonics for the Timer Control and Status Register bits */
#define TCSR_OLVL 0x01
@ -276,9 +277,9 @@ void hd6301x_cpu_device::hd6301x_io(address_map &map)
map(0x0018, 0x0018).rw(FUNC(hd6301x_cpu_device::p7_data_r), FUNC(hd6301x_cpu_device::p7_data_w)); // TODO: external except in single-chip mode
map(0x0019, 0x0019).rw(FUNC(hd6301x_cpu_device::ocr2h_r), FUNC(hd6301x_cpu_device::ocr2h_w));
map(0x001a, 0x001a).rw(FUNC(hd6301x_cpu_device::ocr2l_r), FUNC(hd6301x_cpu_device::ocr2l_w));
//map(0x001b, 0x001b).rw(FUNC(hd6301x_cpu_device::tcsr3_r), FUNC(hd6301x_cpu_device::tcsr3_w));
//map(0x001c, 0x001c).rw(FUNC(hd6301x_cpu_device::ff_r), FUNC(hd6301x_cpu_device::tconr_w));
//map(0x001d, 0x001d).rw(FUNC(hd6301x_cpu_device::t2cnt_r), FUNC(hd6301x_cpu_device::t2cnt_w));
map(0x001b, 0x001b).rw(FUNC(hd6301x_cpu_device::tcsr3_r), FUNC(hd6301x_cpu_device::tcsr3_w));
map(0x001c, 0x001c).rw(FUNC(hd6301x_cpu_device::ff_r), FUNC(hd6301x_cpu_device::tconr_w));
map(0x001d, 0x001d).rw(FUNC(hd6301x_cpu_device::t2cnt_r), FUNC(hd6301x_cpu_device::t2cnt_w));
//map(0x001f, 0x001f).rw(FUNC(hd6301x_cpu_device::tstreg_r), FUNC(hd6301x_cpu_device::tstreg_w));
}
@ -318,6 +319,7 @@ m6801_cpu_device::m6801_cpu_device(const machine_config &mconfig, device_type ty
, m_out_port_func(*this)
, m_out_sc2_func(*this)
, m_out_sertx_func(*this)
, m_sclk_divider(8)
{
}
@ -356,6 +358,7 @@ hd6301x_cpu_device::hd6301x_cpu_device(const machine_config &mconfig, device_typ
, m_in_portx_func(*this)
, m_out_portx_func(*this)
{
m_sclk_divider = 16;
}
hd6301x0_cpu_device::hd6301x0_cpu_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock)
@ -433,6 +436,10 @@ void hd6301x_cpu_device::m6800_check_irq2()
{
TAKE_TOI;
}
else if ((m_tcsr3 & 0xc0) == 0xc0)
{
TAKE_CMI;
}
else if (((m_trcsr & (M6801_TRCSR_RIE|M6801_TRCSR_RDRF)) == (M6801_TRCSR_RIE|M6801_TRCSR_RDRF)) ||
((m_trcsr & (M6801_TRCSR_RIE|M6801_TRCSR_ORFE)) == (M6801_TRCSR_RIE|M6801_TRCSR_ORFE)) ||
((m_trcsr & (M6801_TRCSR_TIE|M6801_TRCSR_TDRE)) == (M6801_TRCSR_TIE|M6801_TRCSR_TDRE)))
@ -572,7 +579,7 @@ void hd6301x_cpu_device::check_timer_event()
modified_tcsr();
}
if (m_irq2 & (TCSR_OCF | TCSR_TOF))
if ((m_irq2 & (TCSR_OCF | TCSR_TOF)) || (m_tcsr3 & 0xc0) == 0xc0)
{
if (m_wai_state & M6800_SLP)
m_wai_state &= ~M6800_SLP;
@ -591,6 +598,40 @@ void m6801_cpu_device::increment_counter(int amount)
check_timer_event();
}
void hd6301x_cpu_device::increment_counter(int amount)
{
m6800_cpu_device::increment_counter(amount);
if (m_t2cnt_written)
m_t2cnt_written = false;
else if (BIT(m_tcsr3, 4))
{
switch (m_tcsr3 & 0x03)
{
case 0x00:
// Timer 2 input = E clock
increment_t2cnt(amount);
break;
case 0x01:
// Timer 2 input = E clock/8
increment_t2cnt((amount + (CTD & 0x0007)) >> 3);
break;
case 0x02:
// Timer 2 input = E clock/128
increment_t2cnt((amount + (CTD & 0x007f)) >> 7);
break;
case 0x03:
// Timer 2 input = external Tclk
break;
}
}
CTD += amount;
if (CTD >= m_timer_next || (m_tcsr3 & 0xc0) == 0xc0)
check_timer_event();
}
void m6801_cpu_device::EAT_CYCLES()
{
int cycles_to_eat = std::min(int(m_timer_next - CTD), m_icount);
@ -648,6 +689,44 @@ void m6801_cpu_device::set_rmcr(uint8_t data)
}
}
void hd6301x_cpu_device::set_rmcr(uint8_t data)
{
if (m_rmcr == data) return;
m_rmcr = data;
switch ((m_rmcr & 0x1c) >> 2)
{
case 0: // TODO: clock sync
case 3:
case 7: // external clock
LOGSER("SCI: Using external serial clock: true\n");
m_use_ext_serclock = true;
m_sci_timer->enable(false);
break;
case 1:
case 2:
case 4: // TODO: clock sync
case 5:
case 6:
if (BIT(m_rmcr, 5))
{
LOGSER("SCI: Using Timer 2 clock\n");
m_sci_timer->enable(false);
}
else
{
int divisor = M6801_RMCR_SS[m_rmcr & M6801_RMCR_SS_MASK];
attotime period = cycles_to_attotime(divisor);
LOGSER("SCI: Setting serial rate, Divisor: %d Hz: %d\n", divisor, period.as_hz());
m_sci_timer->adjust(period, 0, period);
}
m_use_ext_serclock = false;
break;
}
}
int m6801_cpu_device::m6800_rx()
{
return (m_in_port_func[1]() & M6801_PORT2_IO3) >> 3;
@ -991,6 +1070,12 @@ void hd6301x_cpu_device::device_start()
save_item(NAME(m_tcsr2));
save_item(NAME(m_pending_tcsr2));
save_item(NAME(m_output_compare2.d));
save_item(NAME(m_t2cnt));
save_item(NAME(m_tconr));
save_item(NAME(m_tcsr3));
save_item(NAME(m_tout3));
save_item(NAME(m_t2cnt_written));
}
void m6801_cpu_device::device_reset()
@ -1043,6 +1128,12 @@ void hd6301x_cpu_device::device_reset()
m_tcsr2 = 0x00;
m_pending_tcsr2 = 0x00;
OC2D = 0xffff;
m_t2cnt = 0x00;
m_tconr = 0xff;
m_tcsr3 = 0x00;
m_tout3 = false;
m_t2cnt_written = false;
}
@ -1086,6 +1177,11 @@ void hd6301x_cpu_device::write_port2()
data = (data & 0xef) | (m_tx << 4);
ddr |= 0x10;
}
if ((m_tcsr3 & 0x0c) != 0)
{
data = (data & 0xbf) | (m_tout3 << 6);
ddr |= 0x40;
}
m_out_port_func[1](0, data, ddr);
}
@ -1540,6 +1636,93 @@ void hd6301x_cpu_device::ocr2l_w(uint8_t data)
}
void hd6301x_cpu_device::increment_t2cnt(int amount)
{
if (amount > uint8_t(m_tconr - m_t2cnt))
{
if (m_t2cnt > m_tconr)
{
amount -= 256 - m_t2cnt;
m_t2cnt = 0;
}
m_t2cnt = (m_t2cnt + amount) % (m_tconr + 1);
if (BIT(m_tcsr3, 3))
{
if (m_tout3 != BIT(m_tcsr3, 2))
{
m_tout3 = BIT(m_tcsr3, 2);
m_port2_written = true;
write_port2();
}
}
else if (BIT(m_tcsr3, 2))
{
m_tout3 = !m_tout3;
m_port2_written = true;
write_port2();
}
if (BIT(m_rmcr, 5) && !m_use_ext_serclock)
{
if (m_ext_serclock + amount >= 32)
{
m_ext_serclock = (m_ext_serclock + amount) % 32;
serial_transmit();
serial_receive();
}
else
m_ext_serclock += amount;
}
m_tcsr3 |= 0x80;
m_timer_next = 0; // HACK
}
else
m_t2cnt += amount;
}
uint8_t hd6301x_cpu_device::t2cnt_r()
{
return m_t2cnt;
}
void hd6301x_cpu_device::t2cnt_w(uint8_t data)
{
m_t2cnt = data;
m_t2cnt_written = true;
}
void hd6301x_cpu_device::tconr_w(uint8_t data)
{
m_tconr = data;
}
uint8_t hd6301x_cpu_device::tcsr3_r()
{
return m_tcsr3;
}
void hd6301x_cpu_device::tcsr3_w(uint8_t data)
{
uint8_t tout3_last_enable = (m_tcsr3 & 0x0c) != 0;
// Bit 5 does not exist and Bit 7 can only be written with 0
m_tcsr3 = data & (0x5f | (m_tcsr3 & 0x80));
if (m_tout3 && !BIT(data, 4))
{
m_tout3 = false;
write_port2();
}
else if (tout3_last_enable ? (data & 0x0c) == 0 : (data & 0x0c) != 0)
{
m_port2_written = true;
write_port2();
}
}
uint8_t m6801_cpu_device::sci_rmcr_r()
{
return m_rmcr;
@ -1655,7 +1838,7 @@ void m6801_cpu_device::m6801_clock_serial()
{
m_ext_serclock++;
if (m_ext_serclock >= 8)
if (m_ext_serclock >= m_sclk_divider)
{
m_ext_serclock = 0;
serial_transmit();

View File

@ -124,6 +124,8 @@ protected:
devcb_write_line m_out_sc2_func;
devcb_write_line m_out_sertx_func;
int m_sclk_divider;
/* internal registers */
uint8_t m_port_ddr[4];
uint8_t m_port_data[4];
@ -167,7 +169,7 @@ protected:
virtual void set_timer_event();
virtual void modified_counters();
virtual void check_timer_event();
void set_rmcr(uint8_t data);
virtual void set_rmcr(uint8_t data);
virtual void write_port2();
int m6800_rx();
void serial_transmit();
@ -281,12 +283,21 @@ protected:
uint8_t ocr2l_r();
void ocr2l_w(uint8_t data);
void increment_t2cnt(int amount);
uint8_t t2cnt_r();
void t2cnt_w(uint8_t data);
void tconr_w(uint8_t data);
uint8_t tcsr3_r();
void tcsr3_w(uint8_t data);
virtual void m6800_check_irq2() override;
virtual void modified_tcsr() override;
virtual void set_timer_event() override;
virtual void modified_counters() override;
virtual void increment_counter(int amount) override;
virtual void check_timer_event() override;
virtual void CLEANUP_COUNTERS() override;
virtual void set_rmcr(uint8_t data) override;
devcb_read8::array<2> m_in_portx_func;
devcb_write8::array<3> m_out_portx_func;
@ -297,6 +308,12 @@ protected:
uint8_t m_tcsr2;
uint8_t m_pending_tcsr2;
PAIR m_output_compare2;
uint8_t m_t2cnt;
uint8_t m_tconr;
uint8_t m_tcsr3;
bool m_tout3;
bool m_t2cnt_written;
};

View File

@ -7,7 +7,6 @@ Novag Super Nova & related chess computers. I believe the series started with
Primo. The chess engine is by David Kittinger.
TODO:
- remove timer hack for supremo (missing extra timer emulation in MCU core)
- NMI on power-off switch, it sets 0x14 bit 7 for standby power (see below)
- add nvram, MCU is missing standby power emulation
- beeps are glitchy, as if interrupted for too long
@ -324,10 +323,6 @@ void snova_state::supremo(machine_config &config)
m_maincpu->set_clock(8_MHz_XTAL);
m_maincpu->set_addrmap(AS_PROGRAM, &snova_state::supremo_map);
// THIS IS A HACK, vector @ 0xffec, use ROM_COPY
const attotime irq_period = attotime::from_ticks(4 * 128 * 11, 8_MHz_XTAL);
m_maincpu->set_periodic_int(FUNC(snova_state::irq0_line_hold), irq_period);
config.set_default_layout(layout_novag_supremo);
config.device_remove("rs232");
@ -351,8 +346,6 @@ ROM_START( supremo )
ROM_REGION( 0x10000, "maincpu", 0 )
ROM_LOAD("sp_a10.u5", 0x8000, 0x8000, CRC(1db63786) SHA1(4f24452ed8955b31ba88f68cc95c357660930aa4) )
ROM_COPY("maincpu", 0xffec, 0xfff8, 2) // HACK
ROM_REGION( 50926, "screen", 0 )
ROM_LOAD("nsnova.svg", 0, 50926, CRC(5ffa1b53) SHA1(8b1f862bfdf0be837a4e8dc94fea592d6ffff629) )
ROM_END

View File

@ -41,7 +41,7 @@ private:
u8 p2_r();
WRITE_LINE_MEMBER(midi_rx_r) { m_rx_data = state; }
WRITE_LINE_MEMBER(midiclock_w) { if (state == ASSERT_LINE) m_maincpu->m6801_clock_serial();}
WRITE_LINE_MEMBER(midiclock_w) { if (state) m_maincpu->m6801_clock_serial(); }
required_device<hd6303x_cpu_device> m_maincpu;
required_ioport m_port2;
@ -130,7 +130,7 @@ void ymtx81z_state::tx81z(machine_config &config)
NVRAM(config, "nvram", nvram_device::DEFAULT_ALL_0); // TC5564PL-15/-20 + CR2032 battery
auto &midiclock(CLOCK(config, "midiclock", 500_kHz_XTAL / 2)); // divider not verified
auto &midiclock(CLOCK(config, "midiclock", 500_kHz_XTAL));
midiclock.signal_handler().set(FUNC(ymtx81z_state::midiclock_w));
MIDI_PORT(config, "mdin", midiin_slot, "midiin").rxd_handler().set(FUNC(ymtx81z_state::midi_rx_r));