mame/src/mess/machine/cbmiec.c

668 lines
18 KiB
C

/**********************************************************************
Commodore IEC Serial Bus emulation
Copyright MESS Team.
Visit http://mamedev.org for licensing and usage restrictions.
**********************************************************************/
/*
C64 SERIAL BUS
Serial Bus Pinouts
Pin Name Description
1 SRQ Serial Service Request In
2 GND Ground
3 ATN Serial Attention In/Out
4 CLK Serial Clock In/Out
5 DATA Serial Data In/Out
6 RESET Serial Reset
All signals are active low.
SRQ: Serial Service Request In
This signal is not used on the C64. On C128 it is replaced with Fast Serial
Clock for the 1571 disk drive.
ATN: Serial Attention In/Out
Sending any byte with the ATN line low (sending under attention) causes it
to be interpreted as a Bus Command for peripherals on the serial bus.
When the C64 brings this signal LOW, all other devices start listening for
it to transmit an address. The device addressed must respond in a preset
period of time; otherwise, the C64 will assume that the device addressed is
not on the bus, and will return an error in the STATUS word.
Usually, the address byte will be followed by one to two commands for the
device addressed, meaning the secondary address and channel on the peripheral.
Such a command can be one of the following:
20
40
60
E0
F0
CLK: Serial Clock In/Out
This signal is for timing the data sent on the serial bus. This signal is
always generated by the active TALKER. RISING EDGE OF THE CLOCK means data
bit is valid.
DATA: Serial Data In/Out
Data on the serial bus is transmitted bit by bit at a time on this line.
RESET: Serial Reset
You may disconnect this line to save your disk drive. The easiest way is to
do that on the cable, thus avoiding any modifications on your peripherals.
Serial Bus Timing
___
CLK |____|~~~~| Ts Bit Set-up time
: Ts : Tv : Tv Bit Valid time
|<--------- Byte sent under attention (to devices) ------------>|
___ ____ _____ _____
ATN |________________________________________________________|
: :
___ ______ ________ ___ ___ ___ ___ ___ ___ ___ ___ :
CLK : |_____| |_| |_| |_| |_| |_| |_| |_| |_| |______________ _____
: : : : :
: Tat : :Th: Tne : : Tf : Tr :
____ ________ : : :___________________________________:____:
DATA ___|\\\\\\\\\\__:__| |__||__||__||__||__||__||__||__| |_________ _____
: 0 1 2 3 4 5 6 7 :
: LSB MSB :
: : :
: : Data Valid Listener: Data Accepted
: Listener READY-FOR-DATA
END-OR-IDENTIFY HANDSHAKE (LAST BYTE IN MESSAGE)
___ _______________________________________________________________________
ATN
___ ___ ___ ________________ ___ ___ ___ ___ ___ ___ ___ ___ __
CLK _| |_| |______| |_| |_| |_| |_| |_| |_| |_| |_| |_______|_
: : : : :
:Tf:Tbb:Th:Tye:Tei:Try: :Tf :Tfr:
____ __________: : :___: :_______________________________________: :_
DATA |__||__| |______| |___| : |___|_
6 7 : : : : : :
MSB : : : : : Talker Sending :
: : : : Listener READY-FOR-DATA System
: : : EOI-Timeout Handshake Line Release
: : Listener READY-FOR-DATA
: Talker Ready-To-Send
TALK-ATTENTION TURN AROUND (TALKER AND LISTENER REVERSED)
___ _________________________________________________________
ATN _____________|
:
___ ___ ___ : _____ ________ ___ ___ ___ ___ ___ ___ ___ ___
CLK _| |_| |_________| |___| |_| |_| |_| |_| |_| |_| |_| |_| |_____
: : : : : : :
:Tf:Tr:Ttk:Tdc:Tda:Th:Try: :Tf :
____ __________: : : : :_______________________________________:
DATA |__||__| |_________________| :|__||__||__||__||__||__||__||__| |_
6 7 : : : : : : 0 1 2 3 4 5 6 7
MSB : : : : : :LSB MSB
: : : : : :
: : : : : : Data Valid
: : : : : Listener READY-FOR-DATA
: : : : Talker Ready-To-Send
: : : Device acknowledges it's now TALKER.
: : Becomes LISTENER, Clock = High, Data = Low
: Talker Ready-To-Send
___ _____________________________________________________________________
ATN
___ _________ ___ ___ ___ ___ ___ ___ ___ ___ ________ ___ ___
CLK ____| |_| |_| |_| |_| |_| |_| |_| |_| |_______| |_| |_| |_
: : : : :
:Th :Tne: :Tf :Tbb:Th:Tne:
____ : :___:___________________________________: :_____________
DATA ________| :|__||__||__||__||__||__||__||__| |______|
: : : 0 1 2 3 4 5 6 7 :
: : :LSB MSB :
: : : :
: : : TALKER SENDING Listener: Data Accepted
: : LISTENER READY-FOR-DATA
: TALKER READY-TO-SEND
Serial Bus Timing
Description Symbol Min Typ Max
ATN Response (required) 1) Tat - - 1000us
Listener Hold-Off Th 0 - oo
Non-EOI Response to RFD 2) Tne - 40us 200us
Bit Set-Up Talker 4) Ts 20us 70us -
Data Valid Tv 20us 20us -
Frame Handshake 3) Tf 0 20 1000us
Frame to Release of ATN Tr 20us - -
Between Bytes Time Tbb 100us - -
EOI Response Time Tye 200us 250us -
EOI Response Hold Time 5) Tei 60us - -
Talker Response Limit Try 0 30us 60us
Byte-Acknowledge 4) Tpr 20us 30us -
Talk-Attention Release Ttk 20us 30us 100us
Talk-Attention Acknowledge Tdc 0 - -
Talk-Attention Ack. Hold Tda 80us - -
EOI Acknowledge Tfr 60us - -
Notes:
1) If maximum time exceeded, device not present error.
2) If maximum time exceeded, EOI response required.
3) If maximum time exceeded, frame error.
4) Tv and Tpr minimum must be 60us for external device to be a talker.
5) Tei minimum must be 80us for external device to be a listener.
*/
/*
TODO:
- refactor into an actual daisy chain instead of this convenient hack
*/
#include "cbmiec.h"
//**************************************************************************
// MACROS / CONSTANTS
//**************************************************************************
#define LOG 0
static const char *const SIGNAL_NAME[] = { "SRQ", "ATN", "CLK", "DATA", "RESET" };
//**************************************************************************
// DEVICE DEFINITIONS
//**************************************************************************
const device_type CBM_IEC = &device_creator<cbm_iec_device>;
const device_type CBM_IEC_SLOT = &device_creator<cbm_iec_slot_device>;
//**************************************************************************
// DEVICE INTERFACE
//**************************************************************************
//-------------------------------------------------
// device_cbm_iec_interface - constructor
//-------------------------------------------------
device_cbm_iec_interface::device_cbm_iec_interface(const machine_config &mconfig, device_t &device)
: device_slot_card_interface(mconfig, device)
{
}
//-------------------------------------------------
// ~device_cbm_iec_interface - destructor
//-------------------------------------------------
device_cbm_iec_interface::~device_cbm_iec_interface()
{
}
//**************************************************************************
// LIVE DEVICE
//**************************************************************************
//-------------------------------------------------
// cbm_iec_slot_device - constructor
//-------------------------------------------------
cbm_iec_slot_device::cbm_iec_slot_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock) :
device_t(mconfig, CBM_IEC_SLOT, "CBM IEC slot", tag, owner, clock),
device_slot_interface(mconfig, *this)
{
}
//-------------------------------------------------
// static_set_slot -
//-------------------------------------------------
void cbm_iec_slot_device::static_set_slot(device_t &device, int address)
{
cbm_iec_slot_device &cbm_iec_card = dynamic_cast<cbm_iec_slot_device &>(device);
cbm_iec_card.m_address = address;
}
//-------------------------------------------------
// device_start - device-specific startup
//-------------------------------------------------
void cbm_iec_slot_device::device_start()
{
m_bus = machine().device<cbm_iec_device>(CBM_IEC_TAG);
device_cbm_iec_interface *dev = dynamic_cast<device_cbm_iec_interface *>(get_card_device());
if (dev) m_bus->add_device(get_card_device(), m_address);
}
//**************************************************************************
// DEVICE CONFIGURATION
//**************************************************************************
//-------------------------------------------------
// device_config_complete - perform any
// operations now that the configuration is
// complete
//-------------------------------------------------
void cbm_iec_device::device_config_complete()
{
// inherit a copy of the static data
const cbm_iec_interface *intf = reinterpret_cast<const cbm_iec_interface *>(static_config());
if (intf != NULL)
*static_cast<cbm_iec_interface *>(this) = *intf;
// or initialize to defaults if none provided
else
{
memset(&m_out_srq_cb, 0, sizeof(m_out_srq_cb));
memset(&m_out_atn_cb, 0, sizeof(m_out_atn_cb));
memset(&m_out_clk_cb, 0, sizeof(m_out_clk_cb));
memset(&m_out_data_cb, 0, sizeof(m_out_data_cb));
memset(&m_out_reset_cb, 0, sizeof(m_out_reset_cb));
}
}
//**************************************************************************
// INLINE HELPERS
//**************************************************************************
//-------------------------------------------------
// set_signal -
//-------------------------------------------------
inline void cbm_iec_device::set_signal(device_t *device, int signal, int state)
{
bool changed = false;
if (device == this)
{
if (m_line[signal] != state)
{
if (LOG) logerror("CBM IEC: '%s' %s %u\n", tag(), SIGNAL_NAME[signal], state);
m_line[signal] = state;
changed = true;
}
}
else
{
daisy_entry *entry = m_device_list.first();
while (entry)
{
if (!strcmp(entry->m_device->tag(), device->tag()))
{
if (entry->m_line[signal] != state)
{
if (LOG) logerror("CBM IEC: '%s' %s %u\n", device->tag(), SIGNAL_NAME[signal], state);
entry->m_line[signal] = state;
changed = true;
}
}
entry = entry->next();
}
}
if (changed)
{
switch (signal)
{
case SRQ: m_out_srq_func(state); break;
case ATN: m_out_atn_func(state); break;
case CLK: m_out_clk_func(state); break;
case DATA: m_out_data_func(state); break;
case RESET: m_out_reset_func(state);break;
}
daisy_entry *entry = m_device_list.first();
while (entry)
{
switch (signal)
{
case SRQ:
entry->m_interface->cbm_iec_srq(state);
break;
case ATN:
entry->m_interface->cbm_iec_atn(state);
break;
case CLK:
entry->m_interface->cbm_iec_clk(state);
break;
case DATA:
entry->m_interface->cbm_iec_data(state);
break;
case RESET:
entry->m_interface->cbm_iec_reset(state);
break;
}
entry = entry->next();
}
if (LOG) logerror("CBM IEC: SRQ %u ATN %u CLK %u DATA %u RESET %u\n",
get_signal(SRQ), get_signal(ATN), get_signal(CLK), get_signal(DATA), get_signal(RESET));
}
}
//-------------------------------------------------
// get_signal -
//-------------------------------------------------
inline int cbm_iec_device::get_signal(int signal)
{
int state = m_line[signal];
if (state)
{
daisy_entry *entry = m_device_list.first();
while (entry)
{
if (!entry->m_line[signal])
{
state = 0;
break;
}
entry = entry->next();
}
}
return state;
}
//**************************************************************************
// LIVE DEVICE
//**************************************************************************
//-------------------------------------------------
// cbm_iec_device - constructor
//-------------------------------------------------
cbm_iec_device::cbm_iec_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock)
: device_t(mconfig, CBM_IEC, "CBM IEC bus", tag, owner, clock)
{
for (int i = 0; i < SIGNAL_COUNT; i++)
{
m_line[i] = 1;
}
}
//-------------------------------------------------
// device_start - device-specific startup
//-------------------------------------------------
void cbm_iec_device::device_start()
{
// resolve callbacks
m_out_srq_func.resolve(m_out_srq_cb, *this);
m_out_atn_func.resolve(m_out_atn_cb, *this);
m_out_clk_func.resolve(m_out_clk_cb, *this);
m_out_data_func.resolve(m_out_data_cb, *this);
m_out_reset_func.resolve(m_out_reset_cb, *this);
}
//-------------------------------------------------
// device_reset - device-specific reset
//-------------------------------------------------
void cbm_iec_device::device_reset()
{
reset_w(0);
reset_w(1);
}
//-------------------------------------------------
// device_stop - device-specific stop
//-------------------------------------------------
void cbm_iec_device::device_stop()
{
m_device_list.reset();
}
//-------------------------------------------------
// add_device -
//-------------------------------------------------
void cbm_iec_device::add_device(device_t *target, int address)
{
daisy_entry *entry = auto_alloc(machine(), daisy_entry(target));
entry->m_interface->m_bus = this;
entry->m_interface->m_address = address;
m_device_list.append(*entry);
}
//-------------------------------------------------
// daisy_entry - constructor
//-------------------------------------------------
cbm_iec_device::daisy_entry::daisy_entry(device_t *device)
: m_next(NULL),
m_device(device),
m_interface(NULL)
{
for (int i = 0; i < SIGNAL_COUNT; i++)
{
m_line[i] = 1;
}
device->interface(m_interface);
}
//-------------------------------------------------
// srq_r -
//-------------------------------------------------
READ_LINE_MEMBER( cbm_iec_device::srq_r )
{
return get_signal(SRQ);
}
//-------------------------------------------------
// atn_r -
//-------------------------------------------------
READ_LINE_MEMBER( cbm_iec_device::atn_r )
{
return get_signal(ATN);
}
//-------------------------------------------------
// clk_r -
//-------------------------------------------------
READ_LINE_MEMBER( cbm_iec_device::clk_r )
{
return get_signal(CLK);
}
//-------------------------------------------------
// data_r -
//-------------------------------------------------
READ_LINE_MEMBER( cbm_iec_device::data_r )
{
return get_signal(DATA);
}
//-------------------------------------------------
// reset_r -
//-------------------------------------------------
READ_LINE_MEMBER( cbm_iec_device::reset_r )
{
return get_signal(RESET);
}
//-------------------------------------------------
// srq_w -
//-------------------------------------------------
WRITE_LINE_MEMBER( cbm_iec_device::srq_w )
{
set_signal(this, SRQ, state);
}
//-------------------------------------------------
// atn_w -
//-------------------------------------------------
WRITE_LINE_MEMBER( cbm_iec_device::atn_w )
{
set_signal(this, ATN, state);
}
//-------------------------------------------------
// clk_w -
//-------------------------------------------------
WRITE_LINE_MEMBER( cbm_iec_device::clk_w )
{
set_signal(this, CLK, state);
}
//-------------------------------------------------
// data_w -
//-------------------------------------------------
WRITE_LINE_MEMBER( cbm_iec_device::data_w )
{
set_signal(this, DATA, state);
}
//-------------------------------------------------
// reset_w -
//-------------------------------------------------
WRITE_LINE_MEMBER( cbm_iec_device::reset_w )
{
set_signal(this, RESET, state);
}
//-------------------------------------------------
// srq_w -
//-------------------------------------------------
void cbm_iec_device::srq_w(device_t *device, int state)
{
set_signal(device, SRQ, state);
}
//-------------------------------------------------
// atn_w -
//-------------------------------------------------
void cbm_iec_device::atn_w(device_t *device, int state)
{
set_signal(device, ATN, state);
}
//-------------------------------------------------
// clk_w -
//-------------------------------------------------
void cbm_iec_device::clk_w(device_t *device, int state)
{
set_signal(device, CLK, state);
}
//-------------------------------------------------
// data_w -
//-------------------------------------------------
void cbm_iec_device::data_w(device_t *device, int state)
{
set_signal(device, DATA, state);
}
//-------------------------------------------------
// reset_w -
//-------------------------------------------------
void cbm_iec_device::reset_w(device_t *device, int state)
{
set_signal(device, RESET, state);
}