mirror of
https://github.com/holub/mame
synced 2025-06-01 10:31:48 +03:00
668 lines
18 KiB
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);
|
|
}
|