blob: c33e2290f34c850b51309807dfd722dfb5924170 [file] [log] [blame] [raw]
#include "serial.h"
#include "iec.h"
#include "tedmem.h"
#include "tcbm.h"
enum {
ST_OK = 0, // No error
ST_EOI = 0x40, // Timeout
ST_NOT_FOUND = 0x80, // File not found error
};
unsigned char CSerial::serialPort[16];
//unsigned char CSerial::Line[16];
class CSerial *CSerial::Devices[16];
unsigned int CSerial::NrOfDevicesAttached;
CSerial *CSerial::RootDevice = 0;
CSerial *CSerial::LastDevice = 0;
CSerial::CSerial()
{
DeviceNr = 0;
}
void CSerial::InitPorts()
{
for (int i=0; i<16; i++)
serialPort[i] = 0xC0;
}
CSerial::CSerial(unsigned int DevNr) : DeviceNr(DevNr)
{
NrOfDevicesAttached++;
if (RootDevice == 0) {
PrevDevice = 0;
NextDevice = 0;
for (int i=0; i<4; i++)
Devices[i] = 0;
RootDevice = this;
LastDevice = this;
} else {
PrevDevice = LastDevice;
LastDevice->NextDevice = this;
LastDevice = this;
NextDevice = 0;
}
Devices[DevNr] = this;
sprintf( Name, "Device #%u", DevNr);
//cerr << Name << " with address " << this << " created." << endl;
}
CSerial::~CSerial()
{
// don't do it for the machine
if (DeviceNr) {
if (!NrOfDevicesAttached)
return;
//cout << "Deleting device #" << DeviceNr << endl;
if (!--NrOfDevicesAttached) {
RootDevice = 0;
LastDevice = 0;
} else if (Devices[DeviceNr]->PrevDevice) {
Devices[DeviceNr]->PrevDevice->NextDevice = Devices[DeviceNr]->NextDevice;
if ( this == LastDevice) {
LastDevice = PrevDevice;
}
}
Devices[DeviceNr] = NULL;
}
}
//-------------------
#define ATN_LO 0
#define CLK_LO 0
#define DATA_LO 0
#define ATN_HI 0x10
#define DATA_HI 0x80
#define CLK_HI 0x40
#define IEC_DEBUG 0
enum {
IEC_STATE_IDLE = 0,
IEC_STATE_ATN = 1,
IEC_STATE_TALKING = 2,
IEC_STATE_LISTENING = 4
};
// Serial bus mode states
enum {
SM_NONE,
SM_WAITTIMEOUT,
SM_WAITCLK0,
SM_RDY_TO_SEND,
SM_READY_FOR_DATA,
SM_EOI,
SM_EOI2,
SM_BITTRANS,
SM_BITTRANS2,
SM_BYTEREADY,
SM_BYTEREADY2,
SM_FERROR0,
SM_FERROR1,
SM_TEMP
};
IecFakeSerial::IecFakeSerial(unsigned int DevNr, CIECDevice *iecDev) : CSerial(DevNr), iecDevice(iecDev)
{
atnInLine = ATN_HI;
oldAtnLine = ATN_HI;
state = IEC_STATE_IDLE;
step = SM_NONE;
eoi = 0;
clkLine = CLK_HI;
dataLine = DATA_HI;
errorState = ST_OK;
dev_nr = DevNr;
dataTransfered = 0;
bitCounter = 0;
cycleCount = 0;
namePtr = nameBuffer;
}
void IecFakeSerial::interpretIecByte()
{
#if IEC_DEBUG >= 1
fprintf(stderr, "Interpreting addresses %02X:%02X.\n", addr, secondaryAddress);
fprintf(stderr, "Previous secondary address %02X.\n", secondaryAddress_prev);
#endif
switch (addr&0x70) {
case IEC_CMD_LISTEN:
state |= IEC_STATE_LISTENING;
state &= ~IEC_STATE_TALKING;
step = SM_WAITCLK0;
dataLine = DATA_LO;
clkLine = CLK_HI;
break;
case IEC_CMD_TALK:
state |= IEC_STATE_TALKING;
state &= ~IEC_STATE_LISTENING;
step = SM_WAITTIMEOUT;
dataLine = DATA_LO;
clkLine = CLK_HI;
eoi = 0;
break;
case IEC_CMD_UNLISTEN:
if ( state & IEC_STATE_LISTENING) {
if ( (secondaryAddress_prev & 0xF0) == IEC_CMD_OPEN && dev_nr >= 8) {
*namePtr = 0;
//errorState = iecDevice->Open(secondaryAddress_prev & 0x0F, nameBuffer);
errorState = iecDevice->Open(secondaryAddress_prev & 0x0F, 0);
}
state &= ~IEC_STATE_LISTENING;
}
break;
case IEC_CMD_UNTALK:
state &= ~IEC_STATE_TALKING;
//state &= ~IEC_STATE_LISTENING;
dataTransfered = 0;
break;
}
switch (secondaryAddress&0xF0) {
case IEC_CMD_OPEN:
if (!dataTransfered && dev_nr >= 8) {
namePtr = nameBuffer;
nameLength = 0;
}
errorState = ST_OK;
break;
case IEC_CMD_CLOSE:
if (dev_nr >= 8)
iecDevice->Close(secondaryAddress & 0x0F);
dataTransfered = 0;
break;
case IEC_CMD_DATA:
dataTransfered = 1;
break;
}
updateBus();
}
void IecFakeSerial::update()
{
#if IEC_DEBUG >= 6
fprintf(stderr, "Device #%i.\n", dev_nr);
#endif
cycleCount = (size_t) TED::instance()->GetClockCount();
if ( !atnInLine && (oldAtnLine&ATN_HI) && !(state & IEC_STATE_ATN)) { // ATN 1 -> 0
dataLine = DATA_LO; // device present
step = SM_WAITTIMEOUT;
addr = 0;
secondaryAddress_prev = secondaryAddress;
secondaryAddress = 0;
state |= IEC_STATE_ATN;
timeout = cycleCount + TED::usec2cycles(100);
updateBus();
return;
} else if (atnInLine && (state & IEC_STATE_ATN)) { // ATN 0 -> 1
state &= ~IEC_STATE_ATN;
eoi = 0;
if ( addr == (IEC_CMD_LISTEN | dev_nr) || addr == (IEC_CMD_TALK | dev_nr)) {
interpretIecByte();
updateBus();
} else if (addr == 0x3F || addr == 0x5F) {
interpretIecByte();
if ( !(state & (IEC_STATE_TALKING | IEC_STATE_LISTENING)) ) {
dataLine = DATA_HI;
clkLine = CLK_HI;
}
#if IEC_DEBUG >= 2
fprintf(stderr, "ATN 0 -> 1.\n");
#endif
updateBus();
}
return;
}
if (state & (IEC_STATE_ATN | IEC_STATE_LISTENING) ) {
switch (step) {
case SM_NONE:
#if IEC_DEBUG >= 2
fprintf(stderr, "Idling.\n");
#endif
break;
case SM_WAITTIMEOUT:
if (cycleCount >= timeout) {
step = SM_WAITCLK0;
#if IEC_DEBUG >= 1
fprintf(stderr, "Resetting bus state\n");
#endif
}
break;
case SM_WAITCLK0:
if (!(readBusWithoutUpdate() & CLK_HI)) {
step = SM_RDY_TO_SEND;
#if IEC_DEBUG >= 2
fprintf(stderr, "Ready to send.\n");
#endif
}
break;
case SM_RDY_TO_SEND:
if (readBusWithoutUpdate() & CLK_HI) {
dataLine = DATA_HI;
clkLine = CLK_HI;
step = SM_READY_FOR_DATA;
timeout = cycleCount + TED::usec2cycles(60); // 200 max, 60 avg
#if IEC_DEBUG >= 2
fprintf(stderr, "DATA -> 1, ready for data, mainClock: %i, timeout:%i.\n",
cycleCount, timeout);
#endif
}
break;
case SM_READY_FOR_DATA:
if (!(readBusWithoutUpdate() & CLK_HI)) {
step = SM_BITTRANS;
bitCounter = 0;
io_byte = 0; // Call function here
#if IEC_DEBUG >= 2
fprintf(stderr, "Waiting on CLK=0.\n");
#endif
} else if (cycleCount > (timeout + TED::usec2cycles(140)) && atnInLine) { // EOI! timeout > 200 us
dataLine = DATA_LO;
clkLine = CLK_HI;
step = SM_EOI;
timeout = cycleCount + TED::usec2cycles(60);
eoi = 1;
#if IEC_DEBUG >= 1
fprintf(stderr, "EOI!\n");
fprintf(stderr, "Bus step:%i.\n", step);
#endif
}
break;
case SM_BITTRANS:
if (readBusWithoutUpdate() & CLK_HI) {
const unsigned int level = readBusWithoutUpdate() & DATA_HI; // 0x80 / 0x00
io_byte >>= 1;
io_byte |= level;
#if IEC_DEBUG >= 2
fprintf(stderr, "Transfering bit #%i:%i.\n", bitCounter, level ? 1 : 0);
#endif
if (bitCounter++ >= 7) {
bitCounter = 0;
step = SM_BYTEREADY;
} else
step = SM_BITTRANS2;
}
break;
case SM_BITTRANS2:
if (!(readBusWithoutUpdate() & CLK_HI)) {
step = SM_BITTRANS;
}
break;
case SM_BYTEREADY:
if (!(readBusWithoutUpdate() & CLK_HI)) {
if (!atnInLine) {
if (!addr)
addr = io_byte;
else if (!secondaryAddress) {
secondaryAddress = io_byte;
// !!! printer only
if (dev_nr < 8)
errorState = iecDevice->Open((secondaryAddress) & 0x0F, 0);
}
// Check device number sent if ours
if (!(addr&0x10) && ((addr&0xF)!=dev_nr) ) {
step = SM_NONE;
dataLine = DATA_HI;
clkLine = CLK_HI;
} else {
dataLine = DATA_LO;
clkLine = CLK_HI;
step = SM_RDY_TO_SEND;
}
} else if ( state & (IEC_STATE_LISTENING| IEC_STATE_ATN)) {
// send byte to higher abstraction layer
dataLine = DATA_LO;
clkLine = CLK_HI;
step = SM_RDY_TO_SEND;
//if ( dataTransfered )
if (state & (IEC_STATE_LISTENING))
{
unsigned int command = dataTransfered ? CIECInterface::CMD_DATA : CIECInterface::CMD_OPEN;
switch (dev_nr) {
case 8:
case 9:
case 10:
case 11:
errorState = iecDevice->Write( secondaryAddress & 0x0F, io_byte, command, eoi != 0);
break;
case 4:
case 5:
errorState = iecDevice->Write( secondaryAddress & 0x0F, io_byte, command, eoi != 0);
//errorState = printer_send_byte( io_byte, eoi)
;
}
}
eoi = 0;
}
#if IEC_DEBUG >= 1
fprintf(stderr, "Byte finished: %02X %c.\n", io_byte, (char) io_byte);
#endif
}
break;
case SM_EOI:
if (cycleCount >= timeout) {
dataLine = DATA_HI;
clkLine = CLK_HI;
step = SM_EOI2;
#if IEC_DEBUG >= 2
fprintf(stderr, "EOI timed out, setting DATA -> 0.\n");
#endif
}
break;
case SM_EOI2:
if (!(readBusWithoutUpdate() & CLK_HI)) {
step = SM_BITTRANS;
#if IEC_DEBUG >= 2
fprintf(stderr, "Starting bit transfer with ATN hi.\n");
#endif
}
break;
}
} else if (state & (IEC_STATE_TALKING)) {
// Device is commanded to TALK
switch( step) {
case SM_WAITTIMEOUT: // LISTEN <-> TALK turnaround
if (readBusWithoutUpdate() & CLK_HI) {
step = SM_WAITCLK0;
// At this time, both the Clock line and the Data line are being held down to the true state
// the talker is holding the Clock line true and
// the listener is holding the Data line true.
clkLine = CLK_LO;
// FILE NOT FOUND ERROR?
if (errorState == ST_NOT_FOUND) {
dataLine = DATA_LO;
} else {
dataLine = DATA_HI;
}
timeout = cycleCount + TED::usec2cycles(80); // 80
#if IEC_DEBUG >= 1
fprintf(stderr, "Turnaround.\n");
#endif
}
break;
case SM_WAITCLK0:
if (cycleCount >= timeout) {
step = SM_RDY_TO_SEND;
// When ready to go, release the Clock line to false
clkLine = CLK_HI;
#if IEC_DEBUG >= 1
fprintf(stderr, "Clock: %llu, Timeout:%llu.\n", cycleCount, timeout);
fprintf(stderr, "Ready to send.\n");
#endif
}
break;
case SM_RDY_TO_SEND:
// When the listener is ready to listen, it releases the Data line to false
// wait endlessly
if (readBusWithoutUpdate() & DATA_HI) { // Listener ready for data
// get byte from higher abstraction layer
if (dataTransfered && dev_nr >= 8) {
errorState = iecDevice->Read(secondaryAddress & 0x0F, &io_byte);
if (errorState == ST_EOI) {
eoi = 1;
#if IEC_DEBUG >= 1
fprintf(stderr, "EOI coming.\n");
#endif
}
}
// the talker will pull the Clock line back to true in less than 200 microseconds
// Non-EOI Response to RFD typ: 40 max: 200
timeout = cycleCount + TED::usec2cycles(eoi ? 350 : 40);
step = SM_READY_FOR_DATA;
#if IEC_DEBUG >= 1
fprintf(stderr, "Ready for data.\n");
#endif
}
break;
case SM_READY_FOR_DATA:
if (cycleCount >= timeout) {
bitCounter = 0;
if (eoi) {
step = SM_EOI;
// EOI-Timeout Handshake 200-250 us
timeout = cycleCount + TED::usec2cycles(250);
} else {
step = SM_BITTRANS;
// the talker controls both lines, Clock and Data. At the beginning of the sequence,
// it is holding the Clock true, while the Data line is released to false
clkLine = CLK_LO;
dataLine = DATA_HI;
timeout = cycleCount + TED::usec2cycles(60);
#if IEC_DEBUG >= 1
fprintf(stderr, "Transfering byte: %02X (%c).\n", io_byte, (char) io_byte);
#endif
}
}
break;
/*
___ _____________________________________________________________________
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 - -
*/
case SM_BITTRANS:
if (cycleCount >= timeout) {
#if IEC_DEBUG >= 2
fprintf(stderr, "Transfering bit #%i.\n", bitCounter);
#endif
// When the talker figures the data has been held for a sufficient length of time, it pulls the Clock line true and
// releases the Data line to false. Then it starts to prepare the next bit
clkLine = CLK_LO;
dataLine = DATA_HI;
step = SM_BITTRANS2;
// bit setup time 70 us
timeout = cycleCount + TED::usec2cycles(70);
}
break;
case SM_BITTRANS2:
if (cycleCount >= timeout) {
// As soon as data line is set, the Clock line is released to false, signalling "data ready."
// The talker will typically have a bit in place and be signalling ready in 70 microseconds or less
dataLine = ((io_byte >> (bitCounter++)) & 1) << 7;
clkLine = CLK_HI;
// FIXME? Bit Valid time is 20 us
timeout = cycleCount + TED::usec2cycles(60);
if (bitCounter >= 8) {
step = SM_BYTEREADY;
#if IEC_DEBUG >= 1
fprintf(stderr, "Byte finished: %02X (%c) (EOI:%i).\n", io_byte, (char) io_byte, eoi);
#endif
} else
step = SM_BITTRANS;
}
break;
case SM_BYTEREADY:
if (cycleCount >= timeout) {
clkLine = CLK_LO;
dataLine = DATA_HI;
// The talker is watching the Data line. The listener should pull the Data line true within one millisecond (typ: 20)
timeout = cycleCount + TED::usec2cycles(20);
step = SM_BYTEREADY2;
}
break;
case SM_BYTEREADY2:
if (!(readBusWithoutUpdate() & DATA_HI)) {
if (ST_EOI == errorState) { // EOI
eoi = 1;
state &= ~IEC_STATE_TALKING;
errorState = 0;
clkLine = CLK_HI;
dataLine = DATA_HI;
} else {
timeout = cycleCount;
step = SM_WAITCLK0;
}
#if IEC_DEBUG >= 1
fprintf(stderr, "Byte acknowledged.\n");
#endif
} else if (cycleCount >= timeout + TED::usec2cycles(1000)) {
dataLine = DATA_HI;
clkLine = CLK_HI; // ???
step = SM_RDY_TO_SEND;
timeout = cycleCount + TED::usec2cycles(100);
#if IEC_DEBUG >= 0
fprintf(stderr, "Timeout, no acknowledge, frame error!\n");
#endif
}
break;
case SM_EOI:
if (cycleCount >= timeout) {
// Listener is pulling the Data line true for at least 60 microseconds...
if (!(readBusWithoutUpdate() & DATA_HI)) {
step = SM_EOI2;
timeout = cycleCount + TED::usec2cycles(60);
} else {
// Listener missed the EOI or did not care...
#if IEC_DEBUG >= 1
fprintf(stderr, "EOI _not_ acknowledged.\n");
#endif
step = SM_BITTRANS;
timeout = cycleCount;
}
}
break;
case SM_EOI2:
// ... and then releasing it.
if (readBusWithoutUpdate() & DATA_HI) {
if (cycleCount >= timeout + TED::usec2cycles(60)) {
#if IEC_DEBUG >= 1
fprintf(stderr, "EOI acknowledged.\n");
#endif
// within 60 microseconds it will pull the Clock line true
timeout = cycleCount + TED::usec2cycles(30);
// back to business
step = SM_BITTRANS;
}
#if IEC_DEBUG >= 1
else
fprintf(stderr, "EOI _not_ acknowledged.\n");
#endif
}
break;
case SM_FERROR0:
if (cycleCount >= timeout) {
clkLine = CLK_LO;
dataLine = DATA_HI;
step = SM_FERROR1;
}
break;
case SM_FERROR1:
if ( !(readBusWithoutUpdate()&DATA_HI) ) {
step = SM_WAITCLK0;
timeout = cycleCount;
}
break;
}
}
updateBus();
}
void IecFakeSerial::writeBus(unsigned char newLines)
{
oldAtnLine = atnInLine;
atnInLine = newLines & ATN_HI;
update();
#if IEC_DEBUG >= 2
fprintf(stderr, "Serial write: DATA -> %i, CLK -> %i, ATN ->%i\n", (newLines&0x10)>0,
(newLines&0x20)>0, atnInLine>0);
#endif
}