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
This commit is contained in:
Vas Crabb 2017-03-01 16:25:12 +11:00
parent b28ede22ef
commit 73e2a3f542
15 changed files with 1700 additions and 133 deletions

View File

@ -2288,6 +2288,26 @@ if (BUSES["SMS_EXP"]~=null) then
}
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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

213
src/devices/bus/ti8x/ti8x.h Normal file
View File

@ -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 10 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

View File

@ -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

View File

@ -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

View File

@ -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 )

View File

@ -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];

View File

@ -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)