diff options
author | Vas Crabb <vas@vastheman.com> | 2017-03-01 16:25:12 +1100 |
---|---|---|
committer | Vas Crabb <vas@vastheman.com> | 2017-03-01 16:36:42 +1100 |
commit | 73e2a3f542e868149f3b0be6c1bf395283711f33 (patch) | |
tree | f9b167d4550c6ba8d2840b0ee53ca6bdf43e1a1c | |
parent | b28ede22ef274037c5ecba26d4e5d66198555b2a (diff) |
ti85.cpp: Convert TI-8x link port to a bus with emulated peripherals, should work with TI-82 and TI-85.
* tee allows two peripherals to be connected in parallel
* glinkhle is an RS232 (9600 8N1) adaptor
* bitsock sends raw assert/release line signals to/from a bitbanger device
* monospkr is a speaker connected between tip/ring in parallel and sleeve
* stereospkr is two speakers: left across tip and sleeve, right across ring and sleeve
Use glinkhle to make emulated calculators talk with cooked sockets, e.g.
mame ti82 -linkport glinkhle -linkport:glinkhle:rs232 null_modem -bitb socket.127.0.0.1:2345
Use bitsock to make emulated calculators talk with cooked sockets, e.g.
mame ti82 -linkport bitsock -bitb socket.127.0.0.1:2345
You can use tee to do stuff like listen to data activity for debugging purposes, e.g.
mame ti82 -linkport tee -linkport:tee:a stereospkr -linkport:tee:b glinkhle -linkport:tee:b:glinkhle:rs232 null_modem -bitb socket.127.0.0.1:2345
-rw-r--r-- | scripts/src/bus.lua | 20 | ||||
-rw-r--r-- | scripts/target/mame/mess.lua | 1 | ||||
-rw-r--r-- | src/devices/bus/ti8x/bitsocket.cpp | 89 | ||||
-rw-r--r-- | src/devices/bus/ti8x/bitsocket.h | 55 | ||||
-rw-r--r-- | src/devices/bus/ti8x/graphlinkhle.cpp | 162 | ||||
-rw-r--r-- | src/devices/bus/ti8x/graphlinkhle.h | 66 | ||||
-rw-r--r-- | src/devices/bus/ti8x/teeconn.cpp | 113 | ||||
-rw-r--r-- | src/devices/bus/ti8x/teeconn.h | 45 | ||||
-rw-r--r-- | src/devices/bus/ti8x/ti8x.cpp | 622 | ||||
-rw-r--r-- | src/devices/bus/ti8x/ti8x.h | 213 | ||||
-rw-r--r-- | src/devices/bus/ti8x/tispeaker.cpp | 117 | ||||
-rw-r--r-- | src/devices/bus/ti8x/tispeaker.h | 55 | ||||
-rw-r--r-- | src/mame/drivers/ti85.cpp | 49 | ||||
-rw-r--r-- | src/mame/includes/ti85.h | 123 | ||||
-rw-r--r-- | src/mame/machine/ti85.cpp | 103 |
15 files changed, 1700 insertions, 133 deletions
diff --git a/scripts/src/bus.lua b/scripts/src/bus.lua index a21315344d8..b05cc795e85 100644 --- a/scripts/src/bus.lua +++ b/scripts/src/bus.lua @@ -2290,6 +2290,26 @@ end --------------------------------------------------- -- +--@src/devices/bus/ti8x/ti8x.h,BUSES["TI8X"] = true +--------------------------------------------------- + +if (BUSES["TI8X"]~=null) then + files { + MAME_DIR .. "src/devices/bus/ti8x/bitsocket.cpp", + MAME_DIR .. "src/devices/bus/ti8x/bitsocket.h", + MAME_DIR .. "src/devices/bus/ti8x/graphlinkhle.cpp", + MAME_DIR .. "src/devices/bus/ti8x/graphlinkhle.h", + MAME_DIR .. "src/devices/bus/ti8x/teeconn.cpp", + MAME_DIR .. "src/devices/bus/ti8x/teeconn.h", + MAME_DIR .. "src/devices/bus/ti8x/ti8x.cpp", + MAME_DIR .. "src/devices/bus/ti8x/ti8x.h", + MAME_DIR .. "src/devices/bus/ti8x/tispeaker.cpp", + MAME_DIR .. "src/devices/bus/ti8x/tispeaker.h", + } +end + +--------------------------------------------------- +-- --@src/devices/bus/ti99x/990_dk.h,BUSES["TI99X"] = true --@src/devices/bus/ti99x/990_tap.h,BUSES["TI99X"] = true --@src/devices/bus/ti99x/990_hd.h,BUSES["TI99X"] = true diff --git a/scripts/target/mame/mess.lua b/scripts/target/mame/mess.lua index a3f6f9ea260..cbca0919f1a 100644 --- a/scripts/target/mame/mess.lua +++ b/scripts/target/mame/mess.lua @@ -700,6 +700,7 @@ BUSES["SPC1000"] = true BUSES["SUNKBD"] = true BUSES["SVI_EXPANDER"] = true BUSES["SVI_SLOT"] = true +BUSES["TI8X"] = true BUSES["TI99PEB"] = true BUSES["TI99X"] = true BUSES["TIKI100"] = true diff --git a/src/devices/bus/ti8x/bitsocket.cpp b/src/devices/bus/ti8x/bitsocket.cpp new file mode 100644 index 00000000000..71a153b2562 --- /dev/null +++ b/src/devices/bus/ti8x/bitsocket.cpp @@ -0,0 +1,89 @@ +// license:BSD-3-Clause +// copyright-holders:Vas Crabb + +#include "bitsocket.h" + + +device_type const TI8X_BIT_SOCKET = device_creator<bus::ti8x::bit_socket_device>; + + +namespace bus { namespace ti8x { + +namespace { + +MACHINE_CONFIG_FRAGMENT(bit_socket) + MCFG_DEVICE_ADD("stream", BITBANGER, 0) +MACHINE_CONFIG_END + +} // anonymous namespace + + +bit_socket_device::bit_socket_device( + machine_config const &mconfig, + char const *tag, + device_t *owner, + uint32_t clock) + : device_t(mconfig, TI8X_BIT_SOCKET, "TI-8x Bit Socket", tag, owner, clock, "ti8xbitsock", __FILE__) + , device_ti8x_link_port_interface(mconfig, *this) + , m_stream(*this, "stream") + , m_poll_timer(nullptr) + , m_tip_in(true) + , m_ring_in(true) +{ +} + + +machine_config_constructor bit_socket_device::device_mconfig_additions() const +{ + return MACHINE_CONFIG_NAME(bit_socket); +} + + +void bit_socket_device::device_start() +{ + m_poll_timer = timer_alloc(TIMER_ID_POLL); + + save_item(NAME(m_tip_in)); + save_item(NAME(m_ring_in)); + + m_tip_in = m_ring_in = true; + + m_poll_timer->adjust(attotime::from_hz(200000), 0, attotime::from_hz(200000)); +} + + +void bit_socket_device::device_timer(emu_timer &timer, device_timer_id id, int param, void *ptr) +{ + switch (id) + { + case TIMER_ID_POLL: + { + u8 data; + while (m_stream->input(&data, 1)) + { + if (BIT(data, 1)) output_tip(BIT(data, 0)); + if (BIT(data, 2)) output_ring(BIT(data, 0)); + } + } + break; + + default: + break; + } +} + + +WRITE_LINE_MEMBER(bit_socket_device::input_tip) +{ + m_tip_in = bool(state); + m_stream->output((m_tip_in ? 0x01 : 0x00) | 0x02); +} + + +WRITE_LINE_MEMBER(bit_socket_device::input_ring) +{ + m_ring_in = bool(state); + m_stream->output((m_ring_in ? 0x01 : 0x00) | 0x04); +} + +} } // namespace bus::ti8x diff --git a/src/devices/bus/ti8x/bitsocket.h b/src/devices/bus/ti8x/bitsocket.h new file mode 100644 index 00000000000..f4c635c9dfc --- /dev/null +++ b/src/devices/bus/ti8x/bitsocket.h @@ -0,0 +1,55 @@ +// license:BSD-3-Clause +// copyright-holders:Vas Crabb +/* + Sends raw assert/release signals over a socket. Seriously limits + transfer rates and probably won't work if there's much latency, but it + allows communication between instances using non-standard protocols. + + bit 0 = data + bit 1 = set tip + bit 2 = set ring + */ +#ifndef MAME_DEVICES_BUS_TI8X_BITSOCKET_H +#define MAME_DEVICES_BUS_TI8X_BITSOCKET_H + +#pragma once + +#include "ti8x.h" + +#include "imagedev/bitbngr.h" + + +extern device_type const TI8X_BIT_SOCKET; + + +namespace bus { namespace ti8x { + +class bit_socket_device + : public device_t + , public device_ti8x_link_port_interface +{ +public: + bit_socket_device(machine_config const &mconfig, char const *tag, device_t *owner, uint32_t clock); + +protected: + enum + { + TIMER_ID_POLL = 1 + }; + + virtual machine_config_constructor device_mconfig_additions() const override; + virtual void device_start() override; + virtual void device_timer(emu_timer &timer, device_timer_id id, int param, void *ptr) override; + + virtual DECLARE_WRITE_LINE_MEMBER(input_tip) override; + virtual DECLARE_WRITE_LINE_MEMBER(input_ring) override; + +private: + required_device<bitbanger_device> m_stream; + emu_timer * m_poll_timer; + bool m_tip_in, m_ring_in; +}; + +} } // namespace bus::ti8x + +#endif // MAME_DEVICES_BUS_TI8X_BITSOCKET_H
\ No newline at end of file diff --git a/src/devices/bus/ti8x/graphlinkhle.cpp b/src/devices/bus/ti8x/graphlinkhle.cpp new file mode 100644 index 00000000000..bcfcbd9fcb4 --- /dev/null +++ b/src/devices/bus/ti8x/graphlinkhle.cpp @@ -0,0 +1,162 @@ +// license:BSD-3-Clause +// copyright-holders:Vas Crabb + +#include "graphlinkhle.h" + + +device_type const TI8X_GRAPH_LINK_HLE = device_creator<bus::ti8x::graph_link_hle_device>; + + +namespace bus { namespace ti8x { + +namespace { + +MACHINE_CONFIG_FRAGMENT(graph_link_hle) + MCFG_RS232_PORT_ADD("rs232", default_rs232_devices, nullptr) + MCFG_RS232_RXD_HANDLER(WRITELINE(graph_link_hle_device, rx_w)) +MACHINE_CONFIG_END + +} // anonymous namespace + + +graph_link_hle_device::graph_link_hle_device( + machine_config const &mconfig, + char const *tag, + device_t *owner, + uint32_t clock) + : device_t(mconfig, TI8X_GRAPH_LINK_HLE, "TI-Graph Link (grey, HLE)", tag, owner, clock, "glinkhle", __FILE__) + , device_ti8x_link_port_byte_interface(mconfig, *this) + , device_serial_interface(mconfig, *this) + , m_serial_port(*this, "rs232") + , m_buffer() + , m_head(0) + , m_tail(0) + , m_empty(true) + , m_ready(true) +{ +} + + +void graph_link_hle_device::device_start() +{ + device_serial_interface::register_save_state(machine().save(), this); + + m_buffer = std::make_unique<u8 []>(BUFLEN); + + save_pointer(NAME(m_buffer.get()), BUFLEN); +} + + +void graph_link_hle_device::device_reset() +{ + set_data_frame(1, 8, PARITY_NONE, STOP_BITS_1); + set_rate(9600); + receive_register_reset(); + transmit_register_reset(); + + m_head = m_tail = 0; + m_empty = true; + m_ready = true; +} + + +void graph_link_hle_device::device_timer(emu_timer &timer, device_timer_id id, int param, void *ptr) +{ + device_ti8x_link_port_byte_interface::device_timer(timer, id, param, ptr); + device_serial_interface::device_timer(timer, id, param, ptr); +} + + +machine_config_constructor graph_link_hle_device::device_mconfig_additions() const +{ + return MACHINE_CONFIG_NAME(graph_link_hle); +} + + +void graph_link_hle_device::byte_collision() +{ + if (m_empty) + { + m_ready = true; + } + else + { + send_byte(m_buffer[m_tail]); + m_tail = (m_tail + 1) % BUFLEN; + m_empty = m_head == m_tail; + } +} + + +void graph_link_hle_device::byte_send_timeout() +{ + if (m_empty) + { + m_ready = true; + } + else + { + send_byte(m_buffer[m_tail]); + m_tail = (m_tail + 1) % BUFLEN; + m_empty = m_head == m_tail; + } +} + + +void graph_link_hle_device::byte_receive_timeout() +{ +} + + +void graph_link_hle_device::byte_sent() +{ + if (m_empty) + { + m_ready = true; + } + else + { + send_byte(m_buffer[m_tail]); + m_tail = (m_tail + 1) % BUFLEN; + m_empty = m_head == m_tail; + } +} + + +void graph_link_hle_device::byte_received(u8 data) +{ + transmit_register_setup(data); +} + + +void graph_link_hle_device::rcv_complete() +{ + receive_register_extract(); + if (m_ready) + { + assert(m_empty); + + send_byte(get_received_char()); + m_ready = false; + } + else + { + m_buffer[m_head] = get_received_char(); + m_head = (m_head + 1) % BUFLEN; + m_empty = false; + } +} + + +void graph_link_hle_device::tra_callback() +{ + m_serial_port->write_txd(transmit_register_get_data_bit()); +} + + +void graph_link_hle_device::tra_complete() +{ + accept_byte(); +} + +} } // namespace bus::ti8x diff --git a/src/devices/bus/ti8x/graphlinkhle.h b/src/devices/bus/ti8x/graphlinkhle.h new file mode 100644 index 00000000000..8f2d0eaf242 --- /dev/null +++ b/src/devices/bus/ti8x/graphlinkhle.h @@ -0,0 +1,66 @@ +// license:BSD-3-Clause +// copyright-holders:Vas Crabb +/* + High-level emulation of a somewhat idealised TI-Graph Link "grey" + model. Translates byte-oriented TI-8x link port protocol to/from + 9600 Baud RS232. A real TI-Graph Link requires some delay between + bytes. + + The buffer is there so that if you connect two emulated calculators + together with these it has some chance of working. The receiving + calculator can't slow the sending calculator down like it would be able + to in real life, so you get inevitable overruns without the buffer. + */ +#ifndef MAME_DEVICES_BUS_TI8X_GRAPHLINKHLE_H +#define MAME_DEVICES_BUS_TI8X_GRAPHLINKHLE_H + +#pragma once + +#include "ti8x.h" + +#include "bus/rs232/rs232.h" + +#include <memory> + + +extern device_type const TI8X_GRAPH_LINK_HLE; + + +namespace bus { namespace ti8x { + +class graph_link_hle_device + : public device_t + , public device_ti8x_link_port_byte_interface + , public device_serial_interface +{ +public: + graph_link_hle_device(machine_config const &mconfig, char const *tag, device_t *owner, uint32_t clock); + +protected: + virtual machine_config_constructor device_mconfig_additions() const override; + virtual void device_start() override; + virtual void device_reset() override; + virtual void device_timer(emu_timer &timer, device_timer_id id, int param, void *ptr) override; + + virtual void byte_collision() override; + virtual void byte_send_timeout() override; + virtual void byte_receive_timeout() override; + virtual void byte_sent() override; + virtual void byte_received(u8 data) override; + + virtual void rcv_complete() override; + virtual void tra_callback() override; + virtual void tra_complete() override; + +private: + static constexpr unsigned BUFLEN = 1U << 16; + + required_device<rs232_port_device> m_serial_port; + std::unique_ptr<u8 []> m_buffer; + unsigned m_head, m_tail; + bool m_empty, m_ready; +}; + +} } // namespace bus::ti8x + +#endif // MAME_DEVICES_BUS_TI8X_GRAPHLINKHLE_H
\ No newline at end of file diff --git a/src/devices/bus/ti8x/teeconn.cpp b/src/devices/bus/ti8x/teeconn.cpp new file mode 100644 index 00000000000..d887f1fd85a --- /dev/null +++ b/src/devices/bus/ti8x/teeconn.cpp @@ -0,0 +1,113 @@ +// license:BSD-3-Clause +// copyright-holders:Vas Crabb + +#include "teeconn.h" + + +device_type const TI8X_TEE_CONNECTOR = device_creator<bus::ti8x::tee_connector_device>; + + +namespace bus { namespace ti8x { + +namespace { + +MACHINE_CONFIG_FRAGMENT(tee_connector) + MCFG_TI8X_LINK_PORT_ADD("a", default_ti8x_link_devices, nullptr) + MCFG_TI8X_LINK_TIP_HANDLER(WRITELINE(tee_connector_device, tip_a_w)) + MCFG_TI8X_LINK_RING_HANDLER(WRITELINE(tee_connector_device, ring_a_w)) + + MCFG_TI8X_LINK_PORT_ADD("b", default_ti8x_link_devices, nullptr) + MCFG_TI8X_LINK_TIP_HANDLER(WRITELINE(tee_connector_device, tip_b_w)) + MCFG_TI8X_LINK_RING_HANDLER(WRITELINE(tee_connector_device, ring_b_w)) +MACHINE_CONFIG_END + +} // anonymous namespace + + +tee_connector_device::tee_connector_device( + machine_config const &mconfig, + char const *tag, + device_t *owner, + uint32_t clock) + : device_t(mconfig, TI8X_TEE_CONNECTOR, "T-connector", tag, owner, clock, "ti8xtconn", __FILE__) + , device_ti8x_link_port_interface(mconfig, *this) + , m_port_a(*this, "a") + , m_port_b(*this, "b") + , m_tip_host(true) + , m_tip_a(true) + , m_tip_b(true) + , m_ring_host(true) + , m_ring_a(true) + , m_ring_b(true) +{ +} + + +WRITE_LINE_MEMBER(tee_connector_device::tip_a_w) +{ + m_tip_a = bool(state); + output_tip((m_tip_a && m_tip_b) ? 1 : 0); + m_port_b->tip_w((m_tip_host && m_tip_a) ? 1 : 0); +} + + +WRITE_LINE_MEMBER(tee_connector_device::ring_a_w) +{ + m_ring_a = bool(state); + output_ring((m_ring_a && m_ring_b) ? 1 : 0); + m_port_b->ring_w((m_ring_host && m_ring_a) ? 1 : 0); +} + + +WRITE_LINE_MEMBER(tee_connector_device::tip_b_w) +{ + m_tip_b = bool(state); + output_tip((m_tip_a && m_tip_b) ? 1 : 0); + m_port_a->tip_w((m_tip_host && m_tip_b) ? 1 : 0); +} + + +WRITE_LINE_MEMBER(tee_connector_device::ring_b_w) +{ + m_ring_b = bool(state); + output_ring((m_ring_a && m_ring_b) ? 1 : 0); + m_port_a->ring_w((m_ring_host && m_ring_b) ? 1 : 0); +} + + +machine_config_constructor tee_connector_device::device_mconfig_additions() const +{ + return MACHINE_CONFIG_NAME(tee_connector); +} + + +void tee_connector_device::device_start() +{ + save_item(NAME(m_tip_host)); + save_item(NAME(m_tip_a)); + save_item(NAME(m_tip_b)); + save_item(NAME(m_ring_host)); + save_item(NAME(m_ring_a)); + save_item(NAME(m_ring_b)); + + m_tip_host = m_tip_a = m_tip_b = true; + m_ring_host = m_ring_a = m_ring_b = true; +} + + +WRITE_LINE_MEMBER(tee_connector_device::input_tip) +{ + m_tip_host = bool(state); + m_port_a->tip_w((m_tip_host && m_tip_b) ? 1 : 0); + m_port_b->tip_w((m_tip_host && m_tip_a) ? 1 : 0); +} + + +WRITE_LINE_MEMBER(tee_connector_device::input_ring) +{ + m_ring_host = bool(state); + m_port_a->ring_w((m_ring_host && m_ring_b) ? 1 : 0); + m_port_b->ring_w((m_ring_host && m_ring_a) ? 1 : 0); +} + +} } // namespace bus::ti8x diff --git a/src/devices/bus/ti8x/teeconn.h b/src/devices/bus/ti8x/teeconn.h new file mode 100644 index 00000000000..3c6beb31b46 --- /dev/null +++ b/src/devices/bus/ti8x/teeconn.h @@ -0,0 +1,45 @@ +// license:BSD-3-Clause +// copyright-holders:Vas Crabb +/* + A T-connector, strangely enough. + */ +#ifndef MAME_DEVICES_BUS_TI8X_TEECONN_H +#define MAME_DEVICES_BUS_TI8X_TEECONN_H + +#pragma once + +#include "ti8x.h" + + +extern device_type const TI8X_TEE_CONNECTOR; + + +namespace bus { namespace ti8x { + +class tee_connector_device : public device_t, public device_ti8x_link_port_interface +{ +public: + tee_connector_device(machine_config const &mconfig, char const *tag, device_t *owner, uint32_t clock); + + DECLARE_WRITE_LINE_MEMBER(tip_a_w); + DECLARE_WRITE_LINE_MEMBER(ring_a_w); + DECLARE_WRITE_LINE_MEMBER(tip_b_w); + DECLARE_WRITE_LINE_MEMBER(ring_b_w); + +protected: + virtual machine_config_constructor device_mconfig_additions() const override; + virtual void device_start() override; + + virtual DECLARE_WRITE_LINE_MEMBER(input_tip) override; + virtual DECLARE_WRITE_LINE_MEMBER(input_ring) override; + + required_device<ti8x_link_port_device> m_port_a; + required_device<ti8x_link_port_device> m_port_b; + + bool m_tip_host, m_tip_a, m_tip_b; + bool m_ring_host, m_ring_a, m_ring_b; +}; + +} } // namespace bus::ti8x + +#endif // MAME_DEVICES_BUS_TI8X_TEECONN_H diff --git a/src/devices/bus/ti8x/ti8x.cpp b/src/devices/bus/ti8x/ti8x.cpp new file mode 100644 index 00000000000..0fa99837288 --- /dev/null +++ b/src/devices/bus/ti8x/ti8x.cpp @@ -0,0 +1,622 @@ +// license:BSD-3-Clause +// copyright-holders:Vas Crabb + +#include "emu.h" +#include "ti8x.h" + +#define LOG_GENERAL (1U << 0) +#define LOG_BITPROTO (1U << 1) +#define LOG_BYTEPROTO (1U << 2) + +//#define VERBOSE (LOG_GENERAL | LOG_BITPROTO | LOG_BYTEPROTO) +#define LOG_OUTPUT_FUNC device().logerror +#include "logmacro.h" + +#define LOGBITPROTO(...) LOGMASKED(LOG_BITPROTO, __VA_ARGS__) +#define LOGBYTEPROTO(...) LOGMASKED(LOG_BYTEPROTO, __VA_ARGS__) + + + +device_type const TI8X_LINK_PORT = device_creator<ti8x_link_port_device>; + + +ti8x_link_port_device::ti8x_link_port_device( + machine_config const &mconfig, + char const *tag, + device_t *owner, + uint32_t clock) + : ti8x_link_port_device(mconfig, TI8X_LINK_PORT, "TI-8x Link Port", tag, owner, clock, "ti8xlink", __FILE__) +{ +} + + +ti8x_link_port_device::ti8x_link_port_device( + machine_config const &mconfig, + device_type type, + char const *name, + char const *tag, + device_t *owner, + uint32_t clock, + char const *shortname, + char const *source) + : device_t(mconfig, type, name, tag, owner, clock, shortname, source) + , device_slot_interface(mconfig, *this) + , m_tip_handler(*this) + , m_ring_handler(*this) + , m_dev(nullptr) + , m_tip_in(true) + , m_tip_out(true) + , m_ring_in(true) + , m_ring_out(true) +{ +} + + +WRITE_LINE_MEMBER(ti8x_link_port_device::tip_w) +{ + if (bool(state) != m_tip_out) + { + m_tip_out = bool(state); + if (m_dev) + m_dev->input_tip(m_tip_out ? 1 : 0); + } +} + + +WRITE_LINE_MEMBER(ti8x_link_port_device::ring_w) +{ + if (bool(state) != m_ring_out) + { + m_ring_out = bool(state); + if (m_dev) + m_dev->input_ring(m_ring_out ? 1 : 0); + } +} + + +void ti8x_link_port_device::device_start() +{ + m_tip_handler.resolve_safe(); + m_ring_handler.resolve_safe(); + + save_item(NAME(m_tip_in)); + save_item(NAME(m_tip_out)); + save_item(NAME(m_ring_in)); + save_item(NAME(m_ring_out)); + + m_tip_in = m_tip_out = true; + m_ring_in = m_ring_out = true; +} + + +void ti8x_link_port_device::device_config_complete() +{ + m_dev = dynamic_cast<device_ti8x_link_port_interface *>(get_card_device()); +} + + + +device_ti8x_link_port_interface::device_ti8x_link_port_interface( + machine_config const &mconfig, + device_t &device) + : device_slot_card_interface(mconfig, device) + , m_port(dynamic_cast<ti8x_link_port_device *>(device.owner())) +{ +} + + + +device_ti8x_link_port_bit_interface::device_ti8x_link_port_bit_interface( + machine_config const &mconfig, + device_t &device) + : device_ti8x_link_port_interface(mconfig, device) + , m_error_timer(nullptr) + , m_bit_phase(IDLE) + , m_tx_bit_buffer(EMPTY) + , m_tip_in(true) + , m_ring_in(true) +{ +} + + +void device_ti8x_link_port_bit_interface::interface_pre_start() +{ + device_ti8x_link_port_interface::interface_pre_start(); + + m_error_timer = device().timer_alloc(TIMER_ID_BIT_TIMEOUT); + + device().save_item(NAME(m_bit_phase)); + device().save_item(NAME(m_tx_bit_buffer)); + device().save_item(NAME(m_tip_in)); + device().save_item(NAME(m_ring_in)); + + m_bit_phase = IDLE; + m_tx_bit_buffer = EMPTY; + m_tip_in = m_ring_in = true; +} + + +void device_ti8x_link_port_bit_interface::interface_pre_reset() +{ + device_ti8x_link_port_interface::interface_pre_reset(); + + m_error_timer->reset(); + m_bit_phase = (m_tip_in && m_ring_in) ? IDLE : WAIT_IDLE; + m_tx_bit_buffer = EMPTY; + + output_tip(1); + output_ring(1); +} + + +void device_ti8x_link_port_bit_interface::device_timer( + emu_timer &timer, + device_timer_id id, + int param, void *ptr) +{ + switch (id) + { + case TIMER_ID_BIT_TIMEOUT: + switch (m_bit_phase) + { + // something very bad happened (heap smash?) + case IDLE: + case HOLD_0: + case HOLD_1: + default: + throw false; + + // receive timeout + case ACK_0: + case ACK_1: + LOGBITPROTO("timeout acknowledging %d bit\n", (ACK_0 == m_bit_phase) ? 0 : 1); + output_tip(1); + output_ring(1); + if (m_tip_in && m_ring_in) + { + check_tx_bit_buffer(); + } + else + { + LOGBITPROTO("waiting for bus idle\n"); + m_error_timer->reset((EMPTY == m_tx_bit_buffer) ? attotime::never : attotime(1, 0)); // TODO: configurable timeout + m_bit_phase = WAIT_IDLE; + } + bit_receive_timeout(); + break; + + // send timeout: + case WAIT_IDLE: + assert(EMPTY != m_tx_bit_buffer); + case WAIT_ACK_0: + case WAIT_ACK_1: + case WAIT_REL_0: + case WAIT_REL_1: + LOGBITPROTO("timeout sending bit\n"); + m_error_timer->reset(); + m_bit_phase = (m_tip_in && m_ring_in) ? IDLE : WAIT_IDLE; + m_tx_bit_buffer = EMPTY; + output_tip(1); + output_ring(1); + bit_send_timeout(); + break; + } + break; + + default: + break; + } +} + + +void device_ti8x_link_port_bit_interface::send_bit(bool data) +{ + LOGBITPROTO("queue %d bit\n", data ? 1 : 0); + + if (EMPTY != m_tx_bit_buffer) + device().logerror("device_ti8x_link_port_bit_interface: warning: transmit buffer overrun\n"); + + m_tx_bit_buffer = data ? PENDING_1 : PENDING_0; + if (IDLE == m_bit_phase) + check_tx_bit_buffer(); + else if (WAIT_IDLE == m_bit_phase) + m_error_timer->reset(attotime(1, 0)); // TODO: configurable timeout +} + + +void device_ti8x_link_port_bit_interface::accept_bit() +{ + switch (m_bit_phase) + { + // can't accept a bit that isn't being held + case IDLE: + case WAIT_ACK_0: + case WAIT_ACK_1: + case WAIT_REL_0: + case WAIT_REL_1: + case ACK_0: + case ACK_1: + case WAIT_IDLE: + fatalerror("device_ti8x_link_port_bit_interface: attempt to accept bit when not holding"); + break; + + // release the acknowledgement - if the ring doesn't rise we've lost sync + case HOLD_0: + assert(m_tip_in); + + output_ring(1); + if (m_ring_in) + { + LOGBITPROTO("accepted 0 bit\n"); + check_tx_bit_buffer(); + } + else + { + LOGBITPROTO("accepted 0 bit, ring low (collision) - waiting for bus idle\n"); + m_error_timer->reset((EMPTY == m_tx_bit_buffer) ? attotime::never : attotime(1, 0)); // TODO: configurable timeout + m_bit_phase = WAIT_IDLE; + bit_collision(); + } + break; + + // release the acknowledgement - if the tip doesn't rise we've lost sync + case HOLD_1: + assert(m_ring_in); + + output_tip(1); + if (m_tip_in) + { + LOGBITPROTO("accepted 1 bit\n"); + check_tx_bit_buffer(); + } + else + { + LOGBITPROTO("accepted 1 bit, tip low (collision) - waiting for bus idle\n"); + m_error_timer->reset((EMPTY == m_tx_bit_buffer) ? attotime::never : attotime(1, 0)); // TODO: configurable timeout + m_bit_phase = WAIT_IDLE; + bit_collision(); + } + break; + + // something very bad happened (heap smash?) + default: + throw false; + } +} + + +WRITE_LINE_MEMBER(device_ti8x_link_port_bit_interface::input_tip) +{ + m_tip_in = bool(state); + switch (m_bit_phase) + { + // if tip falls while idle, it's the beginning of an incoming 0 + case IDLE: + if (!m_tip_in) + { + LOGBITPROTO("falling edge on tip, acknowledging 0 bit\n"); + m_error_timer->reset(attotime(1, 0)); // TODO: configurable timeout + m_bit_phase = ACK_0; + output_ring(0); + } + break; + + // we're driving tip low in this state, ignore it + case WAIT_ACK_0: + case ACK_1: + case HOLD_1: + break; + + // tip must fall to acknowledge outgoing 1 + case WAIT_ACK_1: + if (!m_tip_in) + { + LOGBITPROTO("falling edge on tip, 1 bit acknowledged, confirming\n"); + m_error_timer->reset(attotime(1, 0)); // TODO: configurable timeout + m_bit_phase = WAIT_REL_1; + output_ring(1); + } + break; + + // if tip falls now, we've lost sync + case WAIT_REL_0: + case HOLD_0: + if (!m_tip_in) + { + LOGBITPROTO("falling edge on tip, lost sync, waiting for bus idle\n"); + m_error_timer->reset((EMPTY == m_tx_bit_buffer) ? attotime::never : attotime(1, 0)); // TODO: configurable timeout + m_bit_phase = WAIT_IDLE; + output_ring(1); + bit_collision(); + } + break; + + // tip must rise to complete outgoing 1 sequence + case WAIT_REL_1: + if (m_tip_in) + { + assert(!m_ring_in); + + LOGBITPROTO("rising edge on tip, 1 bit sent\n"); + check_tx_bit_buffer(); + bit_sent(); + } + break; + + // tip must rise to accept our acknowledgement + case ACK_0: + if (m_tip_in) + { + LOGBITPROTO("rising edge on tip, 0 bit acknowledge confirmed, holding\n"); + m_error_timer->reset(); + m_bit_phase = HOLD_0; + bit_received(false); + } + break; + + // if the bus is available, check for bit to send + case WAIT_IDLE: + if (m_tip_in && m_ring_in) + { + LOGBITPROTO("rising edge on tip, bus idle detected\n"); + check_tx_bit_buffer(); + } + break; + + // something very bad happened (heap smash?) + default: + throw false; + } +} + + +WRITE_LINE_MEMBER(device_ti8x_link_port_bit_interface::input_ring) +{ + m_ring_in = bool(state); + switch (m_bit_phase) + { + // if ring falls while idle, it's the beginning of an incoming 1 + case IDLE: + if (!m_ring_in) + { + LOGBITPROTO("falling edge on ring, acknowledging 1 bit\n"); + m_error_timer->reset(attotime(1, 0)); // TODO: configurable timeout + m_bit_phase = ACK_1; + output_tip(0); + } + break; + + // ring must fall to acknowledge outgoing 0 + case WAIT_ACK_0: + if (!m_ring_in) + { + LOGBITPROTO("falling edge on ring, 0 bit acknowledged, confirming\n"); + m_error_timer->reset(attotime(1, 0)); // TODO: configurable timeout + m_bit_phase = WAIT_REL_0; + output_tip(1); + } + break; + + // we're driving ring low in this state, ignore it + case WAIT_ACK_1: + case ACK_0: + case HOLD_0: + break; + + // ring must rise to complete outgoing 0 sequence + case WAIT_REL_0: + if (m_ring_in) + { + assert(!m_tip_in); + + LOGBITPROTO("rising edge on ring, 0 bit sent\n"); + check_tx_bit_buffer(); + bit_sent(); + } + break; + + // if ring falls now, we've lost sync + case WAIT_REL_1: + case HOLD_1: + if (!m_ring_in) + { + LOGBITPROTO("falling edge on ring, lost sync, waiting for bus idle\n"); + m_error_timer->reset((EMPTY == m_tx_bit_buffer) ? attotime::never : attotime(1, 0)); // TODO: configurable timeout + m_bit_phase = WAIT_IDLE; + output_tip(1); + bit_collision(); + } + break; + + // ring must rise to accept our acknowledgement + case ACK_1: + if (m_ring_in) + { + LOGBITPROTO("rising edge on ring, 1 bit acknowledge confirmed, holding\n"); + m_error_timer->reset(); + m_bit_phase = HOLD_1; + bit_received(true); + } + break; + + // if the bus is available, check for bit to send + case WAIT_IDLE: + if (m_tip_in && m_ring_in) + { + LOGBITPROTO("rising edge on tip, bus idle detected\n"); + check_tx_bit_buffer(); + } + break; + + // something very bad happened (heap smash?) + default: + throw false; + } +} + + +void device_ti8x_link_port_bit_interface::check_tx_bit_buffer() +{ + assert(m_tip_in); + assert(m_ring_in); + + switch (m_tx_bit_buffer) + { + // nothing to do + case EMPTY: + LOGBITPROTO("no pending bit, entering idle state\n"); + m_error_timer->reset(); + m_bit_phase = IDLE; + break; + + // pull tip low and wait for acknowledgement + case PENDING_0: + LOGBITPROTO("sending 0 bit, pulling tip low\n"); + m_error_timer->reset(attotime(1, 0)); // TODO: configurable timeout + m_bit_phase = WAIT_ACK_0; + m_tx_bit_buffer = EMPTY; + output_tip(0); + break; + + // pull ring low and wait for acknowledgement + case PENDING_1: + LOGBITPROTO("sending 1 bit, pulling ring low\n"); + m_error_timer->reset(attotime(1, 0)); // TODO: configurable timeout + m_bit_phase = WAIT_ACK_1; + m_tx_bit_buffer = EMPTY; + output_ring(0); + break; + + // something very bad happened (heap smash?) + default: + throw false; + } +} + + + +device_ti8x_link_port_byte_interface::device_ti8x_link_port_byte_interface( + machine_config const &mconfig, + device_t &device) + : device_ti8x_link_port_bit_interface(mconfig, device) + , m_tx_byte_buffer(0U) + , m_rx_byte_buffer(0U) +{ +} + + +void device_ti8x_link_port_byte_interface::interface_pre_start() +{ + device_ti8x_link_port_bit_interface::interface_pre_start(); + + device().save_item(NAME(m_tx_byte_buffer)); + device().save_item(NAME(m_rx_byte_buffer)); + + m_tx_byte_buffer = m_rx_byte_buffer = 0U; +} + + +void device_ti8x_link_port_byte_interface::interface_pre_reset() +{ + device_ti8x_link_port_bit_interface::interface_pre_reset(); + + m_tx_byte_buffer = m_rx_byte_buffer = 0U; +} + + +void device_ti8x_link_port_byte_interface::send_byte(u8 data) +{ + if (m_tx_byte_buffer) + device().logerror("device_ti8x_link_port_byte_interface: warning: transmit buffer overrun\n"); + + LOGBYTEPROTO("sending byte 0x%02X\n", data); + m_tx_byte_buffer = 0x0080 | u16(data >> 1); + send_bit(BIT(data, 0)); +} + + +void device_ti8x_link_port_byte_interface::accept_byte() +{ + assert(BIT(m_rx_byte_buffer, 8)); + + LOGBYTEPROTO("accepting final bit of byte\n"); + m_rx_byte_buffer = 0U; + accept_bit(); +} + + +void device_ti8x_link_port_byte_interface::bit_collision() +{ + LOGBYTEPROTO("bit collection, clearing byte buffers\n"); + m_tx_byte_buffer = m_rx_byte_buffer = 0U; + byte_collision(); +} + + +void device_ti8x_link_port_byte_interface::bit_send_timeout() +{ + LOGBYTEPROTO("bit send timeout, clearing send byte buffer\n"); + m_tx_byte_buffer = 0U; + byte_send_timeout(); +} + + +void device_ti8x_link_port_byte_interface::bit_receive_timeout() +{ + LOGBYTEPROTO("bit receive timeout, clearing receive byte buffer\n"); + m_rx_byte_buffer = 0U; + byte_receive_timeout(); +} + + +void device_ti8x_link_port_byte_interface::bit_sent() +{ + assert(m_tx_byte_buffer); + + bool const data(BIT(m_tx_byte_buffer, 0)); + if (m_tx_byte_buffer >>= 1) + { + LOGBYTEPROTO("bit sent, sending next bit of byte\n"); + send_bit(data); + } + else + { + assert(data); + + LOGBYTEPROTO("final bit of byte sent\n"); + byte_sent(); + } +} + + +void device_ti8x_link_port_byte_interface::bit_received(bool data) +{ + assert(!BIT(m_rx_byte_buffer, 8)); + + m_rx_byte_buffer = (!m_rx_byte_buffer ? 0x8000 : (m_rx_byte_buffer >> 1)) | (data ? 0x0080U : 0x0000U); + if (BIT(m_rx_byte_buffer, 8)) + { + LOGBYTEPROTO("received final bit of byte 0x%02X\n", u8(m_rx_byte_buffer)); + byte_received(u8(m_rx_byte_buffer)); + } + else + { + LOGBYTEPROTO("bit received, accepting\n"); + accept_bit(); + } +} + + + +#include "bitsocket.h" +#include "graphlinkhle.h" +#include "teeconn.h" +#include "tispeaker.h" + +SLOT_INTERFACE_START(default_ti8x_link_devices) + SLOT_INTERFACE("bitsock", TI8X_BIT_SOCKET) + SLOT_INTERFACE("glinkhle", TI8X_GRAPH_LINK_HLE) + SLOT_INTERFACE("tee", TI8X_TEE_CONNECTOR) + SLOT_INTERFACE("monospkr", TI8X_SPEAKER_MONO) + SLOT_INTERFACE("stereospkr", TI8X_SPEAKER_STEREO) +SLOT_INTERFACE_END diff --git a/src/devices/bus/ti8x/ti8x.h b/src/devices/bus/ti8x/ti8x.h new file mode 100644 index 00000000000..fdb998cb66c --- /dev/null +++ b/src/devices/bus/ti8x/ti8x.h @@ -0,0 +1,213 @@ +// license:BSD-3-Clause +// copyright-holders:Vas Crabb +/* + TI-8x calculator link port. + + 2.5mm TRS connector on each device, tip and ring both used as I/O + lines. Each device has passive pull-ups and software-controlled active + pull-downs. In TI products, the tip is connected to a red wire and the + ring is connected to a white wire. + + The link ports are only intended to allow linking two devices together, + but there is open source software implementing multi-master protocols + including I2C. The number of devices that can be connected together is + limited in practice by the fact that every device provides pull-ups and + there's a limit to the amount of current any device can sink. + + TI's link port protocol uses 8-bit bytes transmitted LSB first with no + markers for beginning/end of a byte or parity. To transfer a bit, the + transmitting device pulls down a line (tip for 0, ring for 1), waits + for the receiving device to pull down the other line, releases its + line, and waits for the receiver to release the other line. This + ensures software-based implementations don't drop bits. The 6 MHz + Z80-based calculators can manage up to about 50 kbps. + + In a TI-82, each line is tied to the supply rail via a 10kΩ resistor + in series with a signal diode, and can be pulled low by an NPN + transistor in series with a 470Ω resistor. + + This bus implementation works with logic levels (1 = pulled up, + 0 = driven down). The port device just gives you the drive level from + the opposite side of the port which you need to mix with your device's + output. This makes implementing things like the tee connector easier. + */ + +#ifndef MAME_DEVICES_BUS_TI8X_TI8X_H +#define MAME_DEVICES_BUS_TI8X_TI8X_H + +#pragma once + + + +extern device_type const TI8X_LINK_PORT; + + +#define MCFG_TI8X_LINK_PORT_ADD(tag, slot_intf, def_slot) \ + MCFG_DEVICE_ADD(tag, TI8X_LINK_PORT, 0) \ + MCFG_DEVICE_SLOT_INTERFACE(slot_intf, def_slot, false) + +#define MCFG_TI8X_LINK_TIP_HANDLER(cb) \ + devcb = &ti8x_link_port_device::set_tip_handler(*device, DEVCB_##cb); + +#define MCFG_TI8X_LINK_RING_HANDLER(cb) \ + devcb = &ti8x_link_port_device::set_ring_handler(*device, DEVCB_##cb); + + +class device_ti8x_link_port_interface; + + +class ti8x_link_port_device : public device_t, public device_slot_interface +{ +public: + ti8x_link_port_device(machine_config const &mconfig, char const *tag, device_t *owner, u32 clock); + + // static configuration helpers + template <class Object> static devcb_base &set_tip_handler(device_t &device, Object &&cb) + { return downcast<ti8x_link_port_device &>(device).m_tip_handler.set_callback(std::forward<Object>(cb)); } + template <class Object> static devcb_base &set_ring_handler(device_t &device, Object &&cb) + { return downcast<ti8x_link_port_device &>(device).m_ring_handler.set_callback(std::forward<Object>(cb)); } + + DECLARE_WRITE_LINE_MEMBER(tip_w); + DECLARE_WRITE_LINE_MEMBER(ring_w); + + DECLARE_READ_LINE_MEMBER(tip_r) { return m_tip_in ? 1 : 0; } + DECLARE_READ_LINE_MEMBER(ring_r) { return m_ring_in ? 1 : 0; } + +protected: + ti8x_link_port_device( + machine_config const &mconfig, + device_type type, + char const *name, + char const *tag, + device_t *owner, + u32 clock, + char const *shortname, + char const *source); + + virtual void device_start() override; + virtual void device_config_complete() override; + + devcb_write_line m_tip_handler; + devcb_write_line m_ring_handler; + +private: + friend class device_ti8x_link_port_interface; + + device_ti8x_link_port_interface *m_dev; + + bool m_tip_in, m_tip_out, m_ring_in, m_ring_out; +}; + + +class device_ti8x_link_port_interface : public device_slot_card_interface +{ +public: + DECLARE_WRITE_LINE_MEMBER(output_tip) + { if (bool(state) != m_port->m_tip_in) m_port->m_tip_handler((m_port->m_tip_in = bool(state)) ? 1 : 0); } + DECLARE_WRITE_LINE_MEMBER(output_ring) + { if (bool(state) != m_port->m_ring_in) m_port->m_ring_handler((m_port->m_ring_in = bool(state)) ? 1 : 0); } + +protected: + device_ti8x_link_port_interface(machine_config const &mconfig, device_t &device); + + ti8x_link_port_device &port() { return *m_port; } + +private: + virtual DECLARE_WRITE_LINE_MEMBER(input_tip) = 0; + virtual DECLARE_WRITE_LINE_MEMBER(input_ring) = 0; + + friend class ti8x_link_port_device; + + ti8x_link_port_device *m_port; +}; + + +class device_ti8x_link_port_bit_interface : public device_ti8x_link_port_interface +{ +protected: + device_ti8x_link_port_bit_interface(machine_config const &mconfig, device_t &device); + + virtual void interface_pre_start() override; + virtual void interface_pre_reset() override; + + virtual void device_timer(emu_timer &timer, device_timer_id id, int param, void *ptr); + + void send_bit(bool data); + void accept_bit(); + +private: + enum + { + TIMER_ID_BIT_TIMEOUT = 20000 // ensure this doesn't clash with device_serial_interface + }; + + enum bit_phase + { + IDLE, + WAIT_ACK_0, + WAIT_ACK_1, + WAIT_REL_0, + WAIT_REL_1, + ACK_0, + ACK_1, + HOLD_0, + HOLD_1, + WAIT_IDLE + }; + + enum bit_buffer + { + EMPTY, + PENDING_0, + PENDING_1 + }; + + virtual DECLARE_WRITE_LINE_MEMBER(input_tip) override; + virtual DECLARE_WRITE_LINE_MEMBER(input_ring) override; + + virtual void bit_collision() = 0; + virtual void bit_send_timeout() = 0; + virtual void bit_receive_timeout() = 0; + virtual void bit_sent() = 0; + virtual void bit_received(bool data) = 0; + + void check_tx_bit_buffer(); + + emu_timer * m_error_timer; + u8 m_bit_phase; + u8 m_tx_bit_buffer; + bool m_tip_in, m_ring_in; +}; + + +class device_ti8x_link_port_byte_interface : public device_ti8x_link_port_bit_interface +{ +protected: + device_ti8x_link_port_byte_interface(machine_config const &mconfig, device_t &device); + + virtual void interface_pre_start() override; + virtual void interface_pre_reset() override; + + void send_byte(u8 data); + void accept_byte(); + +private: + virtual void bit_collision() override; + virtual void bit_send_timeout() override; + virtual void bit_receive_timeout() override; + virtual void bit_sent() override; + virtual void bit_received(bool data) override; + + virtual void byte_collision() = 0; + virtual void byte_send_timeout() = 0; + virtual void byte_receive_timeout() = 0; + virtual void byte_sent() = 0; + virtual void byte_received(u8 data) = 0; + + u16 m_tx_byte_buffer, m_rx_byte_buffer; +}; + + +SLOT_INTERFACE_EXTERN( default_ti8x_link_devices ); + +#endif // MAME_DEVICES_BUS_TI8X_TI8X_H diff --git a/src/devices/bus/ti8x/tispeaker.cpp b/src/devices/bus/ti8x/tispeaker.cpp new file mode 100644 index 00000000000..c62ae827573 --- /dev/null +++ b/src/devices/bus/ti8x/tispeaker.cpp @@ -0,0 +1,117 @@ +// license:BSD-3-Clause +// copyright-holders:Vas Crabb + +#include "tispeaker.h" + +#include "speaker.h" + + +device_type const TI8X_SPEAKER_STEREO = device_creator<bus::ti8x::stereo_speaker_device>; +device_type const TI8X_SPEAKER_MONO = device_creator<bus::ti8x::mono_speaker_device>; + + +namespace bus { namespace ti8x { + +namespace { + +MACHINE_CONFIG_FRAGMENT(ti_speaker_stereo) + MCFG_SPEAKER_STANDARD_STEREO("outl", "outr") + + MCFG_SOUND_ADD("lspkr", SPEAKER_SOUND, 0) + MCFG_SOUND_ROUTE(ALL_OUTPUTS, "outl", 0.50) + + MCFG_SOUND_ADD("rspkr", SPEAKER_SOUND, 0) + MCFG_SOUND_ROUTE(ALL_OUTPUTS, "outr", 0.50) +MACHINE_CONFIG_END + + +MACHINE_CONFIG_FRAGMENT(ti_speaker_mono) + MCFG_SPEAKER_STANDARD_MONO("mono") + + MCFG_SOUND_ADD("spkr", SPEAKER_SOUND, 0) + MCFG_SOUND_ROUTE(ALL_OUTPUTS, "mono", 0.50) +MACHINE_CONFIG_END + +} // anonymous namespace + + +stereo_speaker_device::stereo_speaker_device( + machine_config const &mconfig, + char const *tag, + device_t *owner, + uint32_t clock) + : device_t(mconfig, TI8X_SPEAKER_STEREO, "TI-8x Speaker (Stereo)", tag, owner, clock, "ti8xstspkr", __FILE__) + , device_ti8x_link_port_interface(mconfig, *this) + , m_left_speaker(*this, "lspkr") + , m_right_speaker(*this, "rspkr") +{ +} + + +machine_config_constructor stereo_speaker_device::device_mconfig_additions() const +{ + return MACHINE_CONFIG_NAME(ti_speaker_stereo); +} + + +void stereo_speaker_device::device_start() +{ +} + + +WRITE_LINE_MEMBER(stereo_speaker_device::input_tip) +{ + m_left_speaker->level_w(state); +} + + +WRITE_LINE_MEMBER(stereo_speaker_device::input_ring) +{ + m_right_speaker->level_w(state); +} + + + +mono_speaker_device::mono_speaker_device( + machine_config const &mconfig, + char const *tag, + device_t *owner, + uint32_t clock) + : device_t(mconfig, TI8X_SPEAKER_MONO, "TI-8x Speaker (Mono)", tag, owner, clock, "ti8xmspkr", __FILE__) + , device_ti8x_link_port_interface(mconfig, *this) + , m_speaker(*this, "spkr") + , m_tip_state(true) + , m_ring_state(true) +{ +} + + +machine_config_constructor mono_speaker_device::device_mconfig_additions() const +{ + return MACHINE_CONFIG_NAME(ti_speaker_mono); +} + + +void mono_speaker_device::device_start() +{ + save_item(NAME(m_tip_state)); + save_item(NAME(m_ring_state)); + + m_tip_state = m_ring_state = true; +} + + +WRITE_LINE_MEMBER(mono_speaker_device::input_tip) +{ + m_tip_state = bool(state); + m_speaker->level_w((m_tip_state || m_ring_state) ? 1 : 0); +} + + +WRITE_LINE_MEMBER(mono_speaker_device::input_ring) +{ + m_ring_state = bool(state); + m_speaker->level_w((m_tip_state || m_ring_state) ? 1 : 0); +} + +} } // namespace bus::ti8x diff --git a/src/devices/bus/ti8x/tispeaker.h b/src/devices/bus/ti8x/tispeaker.h new file mode 100644 index 00000000000..308b316b3bf --- /dev/null +++ b/src/devices/bus/ti8x/tispeaker.h @@ -0,0 +1,55 @@ +// license:BSD-3-Clause +// copyright-holders:Vas Crabb +#ifndef MAME_DEVICES_BUS_TI8X_TISPEAKER_H +#define MAME_DEVICES_BUS_TI8X_TISPEAKER_H + +#pragma once + +#include "ti8x.h" +#include "sound/spkrdev.h" + + +extern device_type const TI8X_SPEAKER_STEREO; +extern device_type const TI8X_SPEAKER_MONO; + + +namespace bus { namespace ti8x { + +class stereo_speaker_device : public device_t, public device_ti8x_link_port_interface +{ +public: + stereo_speaker_device(machine_config const &mconfig, char const *tag, device_t *owner, uint32_t clock); + +protected: + virtual machine_config_constructor device_mconfig_additions() const override; + virtual void device_start() override; + + virtual DECLARE_WRITE_LINE_MEMBER(input_tip) override; + virtual DECLARE_WRITE_LINE_MEMBER(input_ring) override; + + required_device<speaker_sound_device> m_left_speaker; + required_device<speaker_sound_device> m_right_speaker; +}; + + +class mono_speaker_device : public device_t, public device_ti8x_link_port_interface +{ +public: + mono_speaker_device(machine_config const &mconfig, char const *tag, device_t *owner, uint32_t clock); + +protected: + virtual machine_config_constructor device_mconfig_additions() const override; + virtual void device_start() override; + + virtual DECLARE_WRITE_LINE_MEMBER(input_tip) override; + virtual DECLARE_WRITE_LINE_MEMBER(input_ring) override; + + required_device<speaker_sound_device> m_speaker; + +private: + bool m_tip_state, m_ring_state; +}; + +} } // namespace bus::ti8x + +#endif // MAME_DEVICES_BUS_TI8X_TISPEAKER_H diff --git a/src/mame/drivers/ti85.cpp b/src/mame/drivers/ti85.cpp index fc8aab49ddf..1cadc0ca12c 100644 --- a/src/mame/drivers/ti85.cpp +++ b/src/mame/drivers/ti85.cpp @@ -208,7 +208,6 @@ TI-86 ports: #include "imagedev/snapquik.h" #include "machine/bankdev.h" #include "screen.h" -#include "speaker.h" /* port i/o functions */ @@ -599,10 +598,7 @@ static MACHINE_CONFIG_DERIVED( ti85, ti81 ) MCFG_SCREEN_SIZE(128, 64) MCFG_SCREEN_VISIBLE_AREA(0, 128-1, 0, 64-1) - /* sound hardware */ - MCFG_SPEAKER_STANDARD_MONO("mono") - MCFG_SOUND_ADD("speaker", SPEAKER_SOUND, 0) - MCFG_SOUND_ROUTE(ALL_OUTPUTS, "mono", 0.50) + MCFG_TI8X_LINK_PORT_ADD("linkport", default_ti8x_link_devices, nullptr) MACHINE_CONFIG_END @@ -629,18 +625,14 @@ static MACHINE_CONFIG_DERIVED( ti82, ti81 ) MCFG_DEVICE_ADD("t6a04", T6A04, 0) MCFG_T6A04_SIZE(96, 64) - MCFG_SPEAKER_STANDARD_MONO("mono") - MCFG_SOUND_ADD("speaker", SPEAKER_SOUND, 0) - MCFG_SOUND_ROUTE(ALL_OUTPUTS, "mono", 0.50) - - //MCFG_TI82SERIAL_ADD( "tiserial" ) + MCFG_TI8X_LINK_PORT_ADD("linkport", default_ti8x_link_devices, nullptr) MACHINE_CONFIG_END static MACHINE_CONFIG_DERIVED( ti81v2, ti82 ) MCFG_CPU_MODIFY("maincpu") MCFG_CPU_IO_MAP(ti81v2_io) - //MCFG_DEVICE_REMOVE( "tiserial" ) + MCFG_DEVICE_REMOVE("linkport") MACHINE_CONFIG_END static MACHINE_CONFIG_DERIVED( ti83, ti81 ) @@ -670,7 +662,6 @@ static MACHINE_CONFIG_DERIVED( ti86, ti85 ) MCFG_MACHINE_RESET_OVERRIDE(ti85_state, ti85 ) MCFG_SNAPSHOT_ADD("snapshot", ti85_state, ti8x, "sav", 0) - //MCFG_TI86SERIAL_ADD( "tiserial" ) MACHINE_CONFIG_END static MACHINE_CONFIG_DERIVED( ti83p, ti81 ) @@ -716,13 +707,9 @@ static MACHINE_CONFIG_DERIVED( ti83p, ti81 ) MCFG_DEVICE_ADD("t6a04", T6A04, 0) MCFG_T6A04_SIZE(96, 64) - MCFG_SPEAKER_STANDARD_MONO("mono") - MCFG_SOUND_ADD("speaker", SPEAKER_SOUND, 0) - MCFG_SOUND_ROUTE(ALL_OUTPUTS, "mono", 0.50) + MCFG_TI8X_LINK_PORT_ADD("linkport", default_ti8x_link_devices, nullptr) MCFG_AMD_29F400T_ADD("flash") - - //MCFG_TI83PSERIAL_ADD( "tiserial" ) MACHINE_CONFIG_END static MACHINE_CONFIG_DERIVED( ti83pse, ti83p ) @@ -744,8 +731,6 @@ static MACHINE_CONFIG_DERIVED( ti83pse, ti83p ) MCFG_MACHINE_START_OVERRIDE(ti85_state, ti83pse ) MCFG_DEVICE_REPLACE("flash", FUJITSU_29F160T, 0) - - //MCFG_TI83PSERIAL_ADD( "tiserial" ) MACHINE_CONFIG_END static MACHINE_CONFIG_DERIVED( ti84p, ti83pse ) @@ -770,7 +755,7 @@ static MACHINE_CONFIG_DERIVED( ti84pse, ti83pse ) MACHINE_CONFIG_END static MACHINE_CONFIG_DERIVED( ti73, ti83p ) - //MCFG_DEVICE_REMOVE( "tiserial" ) + MCFG_DEVICE_REMOVE("linkport") //MCFG_TI73SERIAL_ADD( "tiserial" ) MACHINE_CONFIG_END @@ -921,15 +906,15 @@ ROM_START (ti84p) ROMX_LOAD( "ti84pb100v255mp.bin", 0x00000, 0x100000, CRC(4af31251) SHA1(8f67269346644b87e7cd0f353f5f4030e787cf57), ROM_BIOS(1) ) ROM_END -/* YEAR NAME PARENT COMPAT MACHINE INPUT INIT COMPANY FULLNAME FLAGS */ -COMP( 1990, ti81, 0, 0, ti81, ti81, driver_device, 0, "Texas Instruments", "TI-81", MACHINE_NO_SOUND ) -COMP( 1992, ti85, 0, 0, ti85d, ti85, driver_device, 0, "Texas Instruments", "TI-85", MACHINE_NO_SOUND ) -COMP( 1993, ti82, 0, 0, ti82, ti82, driver_device, 0, "Texas Instruments", "TI-82", MACHINE_NO_SOUND ) -COMP( 1994, ti81v2, ti81, 0, ti81v2, ti81, driver_device, 0, "Texas Instruments", "TI-81 v2.0", MACHINE_NO_SOUND ) -COMP( 1996, ti83, 0, 0, ti83, ti83, driver_device, 0, "Texas Instruments", "TI-83", MACHINE_NO_SOUND ) -COMP( 1997, ti86, 0, 0, ti86, ti85, driver_device, 0, "Texas Instruments", "TI-86", MACHINE_NO_SOUND ) -COMP( 1998, ti73, 0, 0, ti73, ti82, driver_device, 0, "Texas Instruments", "TI-73", MACHINE_NO_SOUND ) -COMP( 1999, ti83p, 0, 0, ti83p, ti82, driver_device, 0, "Texas Instruments", "TI-83 Plus", MACHINE_NO_SOUND ) -COMP( 2001, ti83pse, 0, 0, ti83pse, ti82, driver_device, 0, "Texas Instruments", "TI-83 Plus Silver Edition", MACHINE_NO_SOUND ) -COMP( 2004, ti84p, 0, 0, ti84p, ti82, driver_device, 0, "Texas Instruments", "TI-84 Plus", MACHINE_NO_SOUND ) -COMP( 2004, ti84pse, 0, 0, ti84pse, ti82, driver_device, 0, "Texas Instruments", "TI-84 Plus Silver Edition", MACHINE_NO_SOUND ) +// YEAR NAME PARENT COMPAT MACHINE INPUT INIT COMPANY FULLNAME FLAGS +COMP( 1990, ti81, 0, 0, ti81, ti81, driver_device, 0, "Texas Instruments", "TI-81", MACHINE_NO_SOUND_HW ) +COMP( 1992, ti85, 0, 0, ti85d, ti85, driver_device, 0, "Texas Instruments", "TI-85", MACHINE_NO_SOUND_HW ) +COMP( 1993, ti82, 0, 0, ti82, ti82, driver_device, 0, "Texas Instruments", "TI-82", MACHINE_NO_SOUND_HW ) +COMP( 1994, ti81v2, ti81, 0, ti81v2, ti81, driver_device, 0, "Texas Instruments", "TI-81 v2.0", MACHINE_NO_SOUND_HW ) +COMP( 1996, ti83, 0, 0, ti83, ti83, driver_device, 0, "Texas Instruments", "TI-83", MACHINE_NO_SOUND_HW ) +COMP( 1997, ti86, 0, 0, ti86, ti85, driver_device, 0, "Texas Instruments", "TI-86", MACHINE_NO_SOUND_HW ) +COMP( 1998, ti73, 0, 0, ti73, ti82, driver_device, 0, "Texas Instruments", "TI-73", MACHINE_NO_SOUND_HW ) +COMP( 1999, ti83p, 0, 0, ti83p, ti82, driver_device, 0, "Texas Instruments", "TI-83 Plus", MACHINE_NO_SOUND_HW ) +COMP( 2001, ti83pse, 0, 0, ti83pse, ti82, driver_device, 0, "Texas Instruments", "TI-83 Plus Silver Edition", MACHINE_NO_SOUND_HW ) +COMP( 2004, ti84p, 0, 0, ti84p, ti82, driver_device, 0, "Texas Instruments", "TI-84 Plus", MACHINE_NO_SOUND_HW ) +COMP( 2004, ti84pse, 0, 0, ti84pse, ti82, driver_device, 0, "Texas Instruments", "TI-84 Plus Silver Edition", MACHINE_NO_SOUND_HW ) diff --git a/src/mame/includes/ti85.h b/src/mame/includes/ti85.h index 0e8a62e9b80..7f07288b66c 100644 --- a/src/mame/includes/ti85.h +++ b/src/mame/includes/ti85.h @@ -11,11 +11,11 @@ #pragma once +#include "bus/ti8x/ti8x.h" #include "imagedev/snapquik.h" #include "machine/bankdev.h" #include "machine/intelfsh.h" #include "machine/nvram.h" -#include "sound/spkrdev.h" #include "video/t6a04.h" @@ -56,22 +56,21 @@ class ti85_state : public driver_device { public: ti85_state(const machine_config &mconfig, device_type type, const char *tag) - : driver_device(mconfig, type, tag), - m_maincpu(*this, "maincpu"), - m_speaker(*this, "speaker"), -// m_serial(*this, "tiserial"), - m_nvram(*this, "nvram"), - m_flash(*this, "flash"), - m_membank1(*this, "membank1"), - m_membank2(*this, "membank2"), - m_membank3(*this, "membank3"), - m_membank4(*this, "membank4") - { } + : driver_device(mconfig, type, tag) + , m_maincpu(*this, "maincpu") + , m_link_port(*this, "linkport") + , m_nvram(*this, "nvram") + , m_flash(*this, "flash") + , m_membank1(*this, "membank1") + , m_membank2(*this, "membank2") + , m_membank3(*this, "membank3") + , m_membank4(*this, "membank4") + { + } required_device<cpu_device> m_maincpu; - optional_device<speaker_sound_device> m_speaker; - //optional_device<> m_serial; - optional_shared_ptr<uint8_t> m_nvram; + optional_device<ti8x_link_port_device> m_link_port; + optional_shared_ptr<uint8_t> m_nvram; optional_device<intelfsh_device> m_flash; optional_device<address_map_bank_device> m_membank1; optional_device<address_map_bank_device> m_membank2; @@ -104,8 +103,6 @@ public: uint8_t m_ti81_port_7_data; std::unique_ptr<uint8_t[]> m_ti8x_ram; uint8_t m_PCR; - uint8_t m_red_out; - uint8_t m_white_out; uint8_t m_ti8x_port2; uint8_t m_ti83p_port4; uint8_t m_ti83pse_port21; @@ -150,24 +147,24 @@ public: DECLARE_WRITE8_MEMBER(ti83p_port_0014_w); DECLARE_WRITE8_MEMBER(ti83pse_port_0020_w); DECLARE_WRITE8_MEMBER(ti83pse_port_0021_w); - DECLARE_READ8_MEMBER( ti85_port_0002_r ); - DECLARE_READ8_MEMBER( ti85_port_0003_r ); - DECLARE_READ8_MEMBER( ti85_port_0004_r ); - DECLARE_READ8_MEMBER( ti85_port_0005_r ); - DECLARE_READ8_MEMBER( ti86_port_0006_r ); - DECLARE_READ8_MEMBER( ti82_port_0002_r ); - DECLARE_READ8_MEMBER( ti83_port_0002_r ); - DECLARE_READ8_MEMBER( ti83_port_0003_r ); - DECLARE_READ8_MEMBER( ti83p_port_0002_r ); - DECLARE_READ8_MEMBER( ti83p_port_0004_r ); - DECLARE_READ8_MEMBER( ti83pse_port_0002_r ); - DECLARE_READ8_MEMBER( ti83pse_port_0005_r ); - DECLARE_READ8_MEMBER( ti83pse_port_0009_r ); - DECLARE_READ8_MEMBER( ti83pse_port_0015_r ); - DECLARE_READ8_MEMBER( ti83pse_port_0020_r ); - DECLARE_READ8_MEMBER( ti83pse_port_0021_r ); - DECLARE_READ8_MEMBER( ti84pse_port_0055_r ); - DECLARE_READ8_MEMBER( ti84pse_port_0056_r ); + DECLARE_READ8_MEMBER(ti85_port_0002_r); + DECLARE_READ8_MEMBER(ti85_port_0003_r); + DECLARE_READ8_MEMBER(ti85_port_0004_r); + DECLARE_READ8_MEMBER(ti85_port_0005_r); + DECLARE_READ8_MEMBER(ti86_port_0006_r); + DECLARE_READ8_MEMBER(ti82_port_0002_r); + DECLARE_READ8_MEMBER(ti83_port_0002_r); + DECLARE_READ8_MEMBER(ti83_port_0003_r); + DECLARE_READ8_MEMBER(ti83p_port_0002_r); + DECLARE_READ8_MEMBER(ti83p_port_0004_r); + DECLARE_READ8_MEMBER(ti83pse_port_0002_r); + DECLARE_READ8_MEMBER(ti83pse_port_0005_r); + DECLARE_READ8_MEMBER(ti83pse_port_0009_r); + DECLARE_READ8_MEMBER(ti83pse_port_0015_r); + DECLARE_READ8_MEMBER(ti83pse_port_0020_r); + DECLARE_READ8_MEMBER(ti83pse_port_0021_r); + DECLARE_READ8_MEMBER(ti84pse_port_0055_r); + DECLARE_READ8_MEMBER(ti84pse_port_0056_r); virtual void machine_start() override; virtual void video_start() override; DECLARE_PALETTE_INIT(ti85); @@ -188,41 +185,41 @@ public: //crystal timers virtual void device_timer(emu_timer &timer, device_timer_id id, int param, void *ptr) override; - void ti83pse_count( uint8_t timer, uint8_t data); + void ti83pse_count(uint8_t timer, uint8_t data); emu_timer *m_crystal_timer1; emu_timer *m_crystal_timer2; emu_timer *m_crystal_timer3; - DECLARE_READ8_MEMBER( ti83pse_ctimer1_setup_r ); - DECLARE_WRITE8_MEMBER( ti83pse_ctimer1_setup_w ); - DECLARE_READ8_MEMBER( ti83pse_ctimer1_loop_r ); - DECLARE_WRITE8_MEMBER( ti83pse_ctimer1_loop_w ); - DECLARE_READ8_MEMBER( ti83pse_ctimer1_count_r ); - DECLARE_WRITE8_MEMBER( ti83pse_ctimer1_count_w ); - DECLARE_READ8_MEMBER( ti83pse_ctimer2_setup_r ); - DECLARE_WRITE8_MEMBER( ti83pse_ctimer2_setup_w ); - DECLARE_READ8_MEMBER( ti83pse_ctimer2_loop_r ); - DECLARE_WRITE8_MEMBER( ti83pse_ctimer2_loop_w ); - DECLARE_READ8_MEMBER( ti83pse_ctimer2_count_r ); - DECLARE_WRITE8_MEMBER( ti83pse_ctimer2_count_w ); - DECLARE_READ8_MEMBER( ti83pse_ctimer3_setup_r ); - DECLARE_WRITE8_MEMBER( ti83pse_ctimer3_setup_w ); - DECLARE_READ8_MEMBER( ti83pse_ctimer3_loop_r ); - DECLARE_WRITE8_MEMBER( ti83pse_ctimer3_loop_w ); - DECLARE_READ8_MEMBER( ti83pse_ctimer3_count_r ); - DECLARE_WRITE8_MEMBER( ti83pse_ctimer3_count_w ); + DECLARE_READ8_MEMBER(ti83pse_ctimer1_setup_r); + DECLARE_WRITE8_MEMBER(ti83pse_ctimer1_setup_w); + DECLARE_READ8_MEMBER(ti83pse_ctimer1_loop_r); + DECLARE_WRITE8_MEMBER(ti83pse_ctimer1_loop_w); + DECLARE_READ8_MEMBER(ti83pse_ctimer1_count_r); + DECLARE_WRITE8_MEMBER(ti83pse_ctimer1_count_w); + DECLARE_READ8_MEMBER(ti83pse_ctimer2_setup_r); + DECLARE_WRITE8_MEMBER(ti83pse_ctimer2_setup_w); + DECLARE_READ8_MEMBER(ti83pse_ctimer2_loop_r); + DECLARE_WRITE8_MEMBER(ti83pse_ctimer2_loop_w); + DECLARE_READ8_MEMBER(ti83pse_ctimer2_count_r); + DECLARE_WRITE8_MEMBER(ti83pse_ctimer2_count_w); + DECLARE_READ8_MEMBER(ti83pse_ctimer3_setup_r); + DECLARE_WRITE8_MEMBER(ti83pse_ctimer3_setup_w); + DECLARE_READ8_MEMBER(ti83pse_ctimer3_loop_r); + DECLARE_WRITE8_MEMBER(ti83pse_ctimer3_loop_w); + DECLARE_READ8_MEMBER(ti83pse_ctimer3_count_r); + DECLARE_WRITE8_MEMBER(ti83pse_ctimer3_count_w); void ti8x_update_bank(address_space &space, uint8_t bank, uint8_t *base, uint8_t page, bool is_ram); - void update_ti85_memory (); - void update_ti83p_memory (); - void update_ti83pse_memory (); - void update_ti86_memory (); - void ti8x_snapshot_setup_registers (uint8_t * data); - void ti85_setup_snapshot (uint8_t * data); - void ti86_setup_snapshot (uint8_t * data); - DECLARE_SNAPSHOT_LOAD_MEMBER( ti8x ); - DECLARE_DIRECT_UPDATE_MEMBER( ti83p_direct_update_handler ); + void update_ti85_memory(); + void update_ti83p_memory(); + void update_ti83pse_memory(); + void update_ti86_memory(); + void ti8x_snapshot_setup_registers(uint8_t *data); + void ti85_setup_snapshot(uint8_t *data); + void ti86_setup_snapshot(uint8_t *data); + DECLARE_SNAPSHOT_LOAD_MEMBER(ti8x); + DECLARE_DIRECT_UPDATE_MEMBER(ti83p_direct_update_handler); ti83pse_timer m_ctimer[3]; diff --git a/src/mame/machine/ti85.cpp b/src/mame/machine/ti85.cpp index 654244bc102..678b2783894 100644 --- a/src/mame/machine/ti85.cpp +++ b/src/mame/machine/ti85.cpp @@ -265,8 +265,6 @@ void ti85_state::machine_start() MACHINE_RESET_MEMBER(ti85_state,ti85) { - m_red_out = 0x00; - m_white_out = 0x00; m_PCR = 0xc0; } @@ -286,9 +284,7 @@ DIRECT_UPDATE_MEMBER(ti85_state::ti83p_direct_update_handler) MACHINE_RESET_MEMBER(ti85_state,ti83p) { - m_red_out = 0x00; - m_white_out = 0x00; - m_PCR = 0xc0; + m_PCR = 0x00; m_ti8x_memory_page_1 = 0; @@ -520,13 +516,19 @@ READ8_MEMBER(ti85_state::ti85_port_0006_r) READ8_MEMBER(ti85_state::ti8x_serial_r) { - //ti85_update_serial(m_serial); - return (m_white_out<<3) - | (m_red_out<<2) - //| ((ti85serial_white_in(m_serial,0)&(1-m_white_out))<<1) - //| (ti85serial_red_in(m_serial,0)&(1-m_red_out)) - | 0x03 // no link cable - | m_PCR; + // 7: unknown (ROM always sets to 1) + // 6: unknown (ROM always sets to 1) + // 5: enable ring output + // 4: enable tip output + // 3: ring output + // 2: tip output + // 1: ring input + // 0: tip input + // If the calculator is driving a line low it will always read low + + uint8_t const tip_in((!m_link_port || m_link_port->tip_r()) ? 0x03 : 0x02); + uint8_t const ring_in((!m_link_port || m_link_port->ring_r()) ? 0x03 : 0x01); + return (~((m_PCR >> 2) & (m_PCR >> 4)) & tip_in & ring_in) | (m_PCR & 0xfc); } READ8_MEMBER(ti85_state::ti82_port_0002_r ) @@ -575,13 +577,19 @@ READ8_MEMBER(ti85_state::ti83_port_0003_r ) READ8_MEMBER(ti85_state::ti8x_plus_serial_r) { - //ti85_update_serial(m_serial); - return (m_white_out<<3) - | (m_red_out<<2) - //| ((ti85serial_white_in(m_serial,0)&(1-m_white_out))<<1) - //| (ti85serial_red_in(m_serial,0)&(1-m_red_out)) - | 0x03 // no link cable - | m_PCR; + // 7: unknown + // 6: byte receive in progress (TI-83+ only) + // 5: ring output + // 4: tip output + // 3: received byte ready (cleared by reading port 0x05, TI-83+ only) + // 2: enable byte receive (TI-83+ only) + // 1: ring input + // 0: tip input + // Note that tip/ring outputs are inverted by an NPN transistor + + uint8_t const tip_in((!m_link_port || m_link_port->tip_r()) ? 0x03 : 0x02); + uint8_t const ring_in((!m_link_port || m_link_port->ring_r()) ? 0x03 : 0x01); + return (~(m_PCR >> 4) & tip_in & ring_in) | (m_PCR & 0xfc); } READ8_MEMBER(ti85_state::ti83p_port_0002_r ) @@ -682,13 +690,23 @@ WRITE8_MEMBER(ti85_state::ti85_port_0006_w) WRITE8_MEMBER(ti85_state::ti8x_serial_w) { - m_speaker->level_w(BIT(data, 2) | BIT(data, 3)); - m_red_out = BIT(data, 2); - m_white_out = BIT(data, 3); - //ti85serial_red_out( m_serial, 0, m_red_out ); - //ti85serial_white_out( m_serial, 0, m_white_out ); - //ti85_update_serial(m_serial); - m_PCR = data & 0xf0; + // 7: unknown (ROM always sets to 1) + // 6: unknown (ROM always sets to 1) + // 5: enable ring output + // 4: enable tip output + // 3: ring output + // 2: tip output + // 1: unused + // 0: unused + // Note that tip/ring outputs are inverted by an NPN transistor + // In practice, the ROM only uses values 0xc0, 0xd4 and 0xe8 + + m_PCR = data & 0xfc; + if (m_link_port) + { + m_link_port->tip_w(BIT(~data, 2) | BIT(~data, 4)); + m_link_port->ring_w(BIT(~data, 3) | BIT(~data, 5)); + } } WRITE8_MEMBER(ti85_state::ti86_port_0005_w) @@ -725,22 +743,31 @@ WRITE8_MEMBER(ti85_state::ti83_port_0002_w) WRITE8_MEMBER(ti85_state::ti83_port_0003_w) { - if (m_LCD_status && !(data&0x08)) m_timer_interrupt_mask = 0; - m_ON_interrupt_mask = data&0x01; - //m_timer_interrupt_mask = data&0x04; - m_LCD_mask = data&0x02; - m_LCD_status = data&0x08; + if (m_LCD_status && !(data&0x08)) m_timer_interrupt_mask = 0; + m_ON_interrupt_mask = data&0x01; + //m_timer_interrupt_mask = data&0x04; + m_LCD_mask = data&0x02; + m_LCD_status = data&0x08; } WRITE8_MEMBER(ti85_state::ti8x_plus_serial_w) { - m_speaker->level_w(BIT(data, 0) | BIT(data, 1)); - m_red_out = BIT(data, 0); - m_white_out = BIT(data, 1); - //ti85serial_red_out( m_serial, 0, m_red_out ); - //ti85serial_white_out( m_serial, 0, m_white_out ); - //ti85_update_serial(m_serial); - m_PCR = data & 0xf0; + // 7: unknown + // 6: unknown + // 5: unknown + // 4: unknown + // 3: unknown + // 2: enable byte receive (TI-83+ only) + // 1: ring output + // 0: tip output + // Note that tip/ring outputs are inverted by an NPN transistor + + m_PCR = (m_PCR & 0xc8) | (data & 0x04) | ((data << 4) & 0x30); + if (m_link_port) + { + m_link_port->tip_w(BIT(~data, 2) | BIT(~data, 4)); + m_link_port->ring_w(BIT(~data, 3) | BIT(~data, 5)); + } } WRITE8_MEMBER(ti85_state::ti83pse_int_ack_w) |