diff options
Diffstat (limited to 'src/devices/bus/heathzenith/h8/h_8_5.cpp')
| -rw-r--r-- | src/devices/bus/heathzenith/h8/h_8_5.cpp | 239 |
1 files changed, 239 insertions, 0 deletions
diff --git a/src/devices/bus/heathzenith/h8/h_8_5.cpp b/src/devices/bus/heathzenith/h8/h_8_5.cpp new file mode 100644 index 00000000000..1f4efd1e45e --- /dev/null +++ b/src/devices/bus/heathzenith/h8/h_8_5.cpp @@ -0,0 +1,239 @@ +// license:BSD-3-Clause +// copyright-holders:Mark Garlanger +/*************************************************************************** + + Heathkit H-8-5 Serial I/O and Cassette Interface + +****************************************************************************/ + +#include "emu.h" + +#include "h_8_5.h" + +#include "imagedev/cassette.h" +#include "machine/clock.h" +#include "machine/i8251.h" +#include "machine/timer.h" +#include "bus/rs232/rs232.h" +#include "formats/h8_cas.h" + +#include "speaker.h" + +#define LOG_LINES (1U << 1) +#define LOG_CASS (1U << 2) +#define LOG_FUNC (1U << 3) +#define VERBOSE (0) + +#include "logmacro.h" + +#define LOGLINES(...) LOGMASKED(LOG_LINES, __VA_ARGS__) +#define LOGCASS(...) LOGMASKED(LOG_CASS, __VA_ARGS__) +#define LOGFUNC(...) LOGMASKED(LOG_FUNC, __VA_ARGS__) + +#ifdef _MSC_VER +#define FUNCNAME __func__ +#else +#define FUNCNAME __PRETTY_FUNCTION__ +#endif + +namespace { + +class h_8_5_device : public device_t, public device_h8bus_card_interface +{ +public: + + h_8_5_device(const machine_config &mconfig, const char *tag, device_t *owner, u32 clock = 0); + +protected: + + virtual void device_start() override ATTR_COLD; + virtual void device_reset() override ATTR_COLD; + virtual void device_add_mconfig(machine_config &config) override ATTR_COLD; + + void uart_rts(u8 data); + void uart_tx_empty(u8 data); + void irq_callback(int state); + + TIMER_DEVICE_CALLBACK_MEMBER(kansas_r); + TIMER_DEVICE_CALLBACK_MEMBER(kansas_w); + + required_device<i8251_device> m_uart; + required_device<i8251_device> m_console; + required_device<cassette_image_device> m_cass_player; + required_device<cassette_image_device> m_cass_recorder; + + bool m_installed; + + u8 m_cass_data[4]; + bool m_cassbit; + bool m_cassold; +}; + +h_8_5_device::h_8_5_device(const machine_config &mconfig, const char *tag, device_t *owner, u32 clock) + : device_t(mconfig, H8BUS_H_8_5, tag, owner, 0) + , device_h8bus_card_interface(mconfig, *this) + , m_uart(*this, "uart") + , m_console(*this, "console") + , m_cass_player(*this, "cassette_player") + , m_cass_recorder(*this, "cassette_recorder") +{ +} + +TIMER_DEVICE_CALLBACK_MEMBER(h_8_5_device::kansas_w) +{ + m_cass_data[3]++; + + if (m_cassbit != m_cassold) + { + LOGCASS("%s: m_cassbit changed : %d\n", FUNCNAME, m_cassbit); + m_cass_data[3] = 0; + m_cassold = m_cassbit; + } + + LOGCASS("%s: m_cassbit: %d\n", FUNCNAME, m_cassbit); + // 2400Hz -> 0 + // 1200Hz -> 1 + const int bit_pos = m_cassbit ? 0 : 1; + + m_cass_recorder->output(BIT(m_cass_data[3], bit_pos) ? -1.0 : +1.0); +} + +TIMER_DEVICE_CALLBACK_MEMBER(h_8_5_device::kansas_r) +{ + // cassette - turn 1200/2400Hz to a bit + m_cass_data[1]++; + u8 cass_ws = (m_cass_player->input() > +0.03) ? 1 : 0; + + LOGCASS("%s: cass_ws: %d\n", FUNCNAME, cass_ws); + + if (cass_ws != m_cass_data[0]) + { + LOGCASS("%s: cass_ws has changed value\n", FUNCNAME); + m_cass_data[0] = cass_ws; + m_uart->write_rxd((m_cass_data[1] < 12) ? 1 : 0); + m_cass_data[1] = 0; + } +} + +void h_8_5_device::uart_rts(u8 data) +{ + LOGLINES("%s: data: %d\n", FUNCNAME, data); + + m_cass_player->change_state(bool(data) ? CASSETTE_STOPPED : CASSETTE_PLAY, CASSETTE_MASK_UISTATE); +} + +void h_8_5_device::uart_tx_empty(u8 data) +{ + LOGLINES("%s: data: %d\n", FUNCNAME, data); + + m_cass_recorder->change_state(bool(data) ? CASSETTE_STOPPED : CASSETTE_RECORD, CASSETTE_MASK_UISTATE); +} + +void h_8_5_device::irq_callback(int state) +{ + LOGFUNC("%s: state: %d\n", FUNCNAME, state); + + set_slot_int3(state); +} + +void h_8_5_device::device_start() +{ + m_installed = false; + + save_item(NAME(m_installed)); + save_item(NAME(m_cass_data)); + save_item(NAME(m_cassbit)); + save_item(NAME(m_cassold)); +} + +void h_8_5_device::device_reset() +{ + LOGFUNC("%s\n", FUNCNAME); + + if (!m_installed) + { + h8bus().space(AS_IO).install_readwrite_handler(0xf8, 0xf9, + read8sm_delegate(m_uart, FUNC(i8251_device::read)), + write8sm_delegate(m_uart, FUNC(i8251_device::write))); + + h8bus().space(AS_IO).install_readwrite_handler(0xfa, 0xfb, + read8sm_delegate(m_console, FUNC(i8251_device::read)), + write8sm_delegate(m_console, FUNC(i8251_device::write))); + + m_installed = true; + } + // cassette + m_cassbit = 1; + m_cassold = 0; + m_cass_data[0] = 0; + m_cass_data[1] = 0; + m_cass_data[2] = 0; + m_cass_data[3] = 0; + + m_uart->write_cts(0); + m_uart->write_dsr(0); + m_uart->write_rxd(0); +} + +// The usual baud rate is 600. The H8 supported baud rates from 110 to +// 9600. You can change the baud rate if it is changed here and in the +// other places that specify 600 baud. +static DEVICE_INPUT_DEFAULTS_START(terminal) + DEVICE_INPUT_DEFAULTS("RS232_RXBAUD", 0xff, RS232_BAUD_600) + DEVICE_INPUT_DEFAULTS("RS232_TXBAUD", 0xff, RS232_BAUD_600) + DEVICE_INPUT_DEFAULTS("RS232_DATABITS", 0xff, RS232_DATABITS_8) + DEVICE_INPUT_DEFAULTS("RS232_PARITY", 0xff, RS232_PARITY_NONE) + DEVICE_INPUT_DEFAULTS("RS232_STOPBITS", 0xff, RS232_STOPBITS_1) +DEVICE_INPUT_DEFAULTS_END + +void h_8_5_device::device_add_mconfig(machine_config &config) +{ + I8251(config, m_uart, 0); + m_uart->txd_handler().set([this] (bool state) { m_cassbit = state; }); + m_uart->rts_handler().set(FUNC(h_8_5_device::uart_rts)); + m_uart->txempty_handler().set(FUNC(h_8_5_device::uart_tx_empty)); + + clock_device &cassette_clock(CLOCK(config, "cassette_clock", 4800)); + cassette_clock.signal_handler().set(m_uart, FUNC(i8251_device::write_txc)); + cassette_clock.signal_handler().append(m_uart, FUNC(i8251_device::write_rxc)); + + I8251(config, m_console, 0); + + m_console->txd_handler().set("rs232", FUNC(rs232_port_device::write_txd)); + m_console->rts_handler().set("rs232", FUNC(rs232_port_device::write_rts)); + m_console->dtr_handler().set("rs232", FUNC(rs232_port_device::write_dtr)); + // The RxRdy pin on the 8251 USART is normally jumpered to generate a level 3 i/o interrupt. + m_console->rxrdy_handler().set(FUNC(h_8_5_device::irq_callback)); + + // Console UART clock is 16X the baud rate. + clock_device &console_clock(CLOCK(config, "console_clock", 600*16)); + console_clock.signal_handler().set(m_console, FUNC(i8251_device::write_txc)); + console_clock.signal_handler().append(m_console, FUNC(i8251_device::write_rxc)); + + rs232_port_device &rs232(RS232_PORT(config, "rs232", default_rs232_devices, "terminal")); + rs232.rxd_handler().set(m_console, FUNC(i8251_device::write_rxd)); + rs232.cts_handler().set(m_console, FUNC(i8251_device::write_cts)); + rs232.dsr_handler().set(m_console, FUNC(i8251_device::write_dsr)); + rs232.set_option_device_input_defaults("terminal", DEVICE_INPUT_DEFAULTS_NAME(terminal)); + + SPEAKER(config, "mono").front_right(); + + CASSETTE(config, m_cass_player); + m_cass_player->set_formats(h8_cassette_formats); + m_cass_player->set_default_state(CASSETTE_STOPPED | CASSETTE_MOTOR_ENABLED | CASSETTE_SPEAKER_ENABLED); + m_cass_player->add_route(ALL_OUTPUTS, "mono", 0.15); + m_cass_player->set_interface("h8_cass_player"); + + CASSETTE(config, m_cass_recorder); + m_cass_recorder->set_formats(h8_cassette_formats); + m_cass_recorder->set_default_state(CASSETTE_STOPPED | CASSETTE_MOTOR_ENABLED | CASSETTE_SPEAKER_ENABLED); + m_cass_recorder->add_route(ALL_OUTPUTS, "mono", 0.15); + m_cass_recorder->set_interface("h8_cass_recorder"); + + TIMER(config, "kansas_w").configure_periodic(FUNC(h_8_5_device::kansas_w), attotime::from_hz(4800)); + TIMER(config, "kansas_r").configure_periodic(FUNC(h_8_5_device::kansas_r), attotime::from_hz(40000)); +} + +} // anonymous namespace + +DEFINE_DEVICE_TYPE_PRIVATE(H8BUS_H_8_5, device_h8bus_card_interface, h_8_5_device, "h8_h_8_5", "Heath H-8-5 Serial/Casette Card"); |
