2017-03-24 10:44:04 +00:00
|
|
|
#include <stdio.h>
|
|
|
|
#include <stdint.h>
|
|
|
|
#include "scc.h"
|
|
|
|
#include "m68k.h"
|
2017-09-04 01:25:24 +00:00
|
|
|
#include "hexdump.h"
|
2017-10-17 13:43:20 +00:00
|
|
|
#include "network/localtalk.h"
|
|
|
|
#include <string.h>
|
2017-03-24 10:44:04 +00:00
|
|
|
/*
|
2017-09-04 01:25:24 +00:00
|
|
|
Emulation of the Zilog 8530 SCC.
|
|
|
|
|
2017-10-17 13:43:20 +00:00
|
|
|
Supports basic mouse pins plus hacked in LocalTalk
|
2017-03-24 10:44:04 +00:00
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
|
|
void sccIrq(int ena);
|
|
|
|
|
2017-09-04 01:25:24 +00:00
|
|
|
#define BUFLEN 8192
|
2017-10-17 13:43:20 +00:00
|
|
|
#define NO_RXBUF 4
|
|
|
|
|
|
|
|
typedef struct {
|
2017-10-18 07:20:12 +00:00
|
|
|
int delay; //-1 if buffer is free
|
2017-10-17 13:43:20 +00:00
|
|
|
int len;
|
|
|
|
uint8_t data[BUFLEN];
|
|
|
|
} RxBuf;
|
2017-09-04 01:25:24 +00:00
|
|
|
|
|
|
|
typedef struct {
|
|
|
|
int dcd;
|
|
|
|
int cts;
|
2017-10-17 13:43:20 +00:00
|
|
|
int wr1, wr15;
|
2017-09-04 01:25:24 +00:00
|
|
|
int sdlcaddr;
|
|
|
|
int hunting;
|
|
|
|
int txTimer;
|
|
|
|
uint8_t txData[BUFLEN];
|
2017-10-17 13:43:20 +00:00
|
|
|
RxBuf rx[NO_RXBUF];
|
|
|
|
int txPos;
|
|
|
|
int rxPos;
|
|
|
|
int rxBufCur;
|
2017-09-04 01:25:24 +00:00
|
|
|
int rxDelay;
|
2017-10-18 07:20:12 +00:00
|
|
|
int eofDelay;
|
|
|
|
int eofIntPending;
|
2017-10-19 07:53:10 +00:00
|
|
|
int rr0Latched;
|
|
|
|
int rr0Prev;
|
|
|
|
int rxAbrtTimer;
|
|
|
|
int rxEom;
|
2017-09-04 01:25:24 +00:00
|
|
|
} SccChan;
|
|
|
|
|
2017-03-24 10:44:04 +00:00
|
|
|
typedef struct {
|
|
|
|
int regptr;
|
|
|
|
int intpending;
|
|
|
|
int intpendingOld;
|
2017-09-04 01:25:24 +00:00
|
|
|
SccChan chan[2];
|
2017-10-17 13:43:20 +00:00
|
|
|
int wr2, wr9;
|
2017-03-24 10:44:04 +00:00
|
|
|
} Scc;
|
|
|
|
|
|
|
|
static Scc scc;
|
|
|
|
|
2017-10-17 13:43:20 +00:00
|
|
|
|
2017-10-18 07:20:12 +00:00
|
|
|
/*
|
|
|
|
For SCC, Special conditions are:
|
|
|
|
- Receive overrun
|
|
|
|
- Framing Error (async)
|
|
|
|
- End of Frame
|
|
|
|
- Optional: Parity error
|
|
|
|
|
|
|
|
For the emu (where we atm only do sldc for appletalk) only End Of Frame is important.
|
|
|
|
*/
|
|
|
|
|
2017-10-17 13:43:20 +00:00
|
|
|
static void triggerRx(int chan);
|
|
|
|
|
|
|
|
|
|
|
|
static int rxHasByte(int chan) {
|
2017-10-19 07:53:10 +00:00
|
|
|
return (scc.chan[chan].rx[scc.chan[chan].rxBufCur].delay==0)?1:0;
|
2017-10-17 13:43:20 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static void rxBufIgnoreRest(int chan) {
|
2017-10-19 07:53:10 +00:00
|
|
|
if (scc.chan[chan].rxPos==0) return; //already at new buff
|
|
|
|
printf("RxBuff: Skipping to next buff\n");
|
2017-10-17 13:43:20 +00:00
|
|
|
scc.chan[chan].rx[scc.chan[chan].rxBufCur].delay=-1;
|
|
|
|
scc.chan[chan].rxBufCur++;
|
|
|
|
if (scc.chan[chan].rxBufCur>=NO_RXBUF) scc.chan[chan].rxBufCur=0;
|
|
|
|
scc.chan[chan].rxPos=0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int rxByte(int chan, int *bytesLeftInBuf) {
|
|
|
|
int ret;
|
|
|
|
int curbuf=scc.chan[chan].rxBufCur;
|
|
|
|
printf("RxBuf: bufid %d byte %d/%d\n", curbuf, scc.chan[chan].rxPos, scc.chan[chan].rx[curbuf].len);
|
|
|
|
if (scc.chan[chan].rx[curbuf].delay!=0) return 0;
|
|
|
|
if (bytesLeftInBuf) *bytesLeftInBuf=scc.chan[chan].rx[curbuf].len-scc.chan[chan].rxPos-1;
|
|
|
|
ret=scc.chan[chan].rx[curbuf].data[scc.chan[chan].rxPos++];
|
|
|
|
if (scc.chan[chan].rxPos==scc.chan[chan].rx[curbuf].len) {
|
|
|
|
rxBufIgnoreRest(chan);
|
|
|
|
}
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int rxBytesLeft(int chan) {
|
|
|
|
if (scc.chan[chan].rx[scc.chan[chan].rxBufCur].delay!=0) return 0;
|
|
|
|
return scc.chan[chan].rx[scc.chan[chan].rxBufCur].len-scc.chan[chan].rxPos;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int rxBufTick(int chan) {
|
2017-10-19 07:53:10 +00:00
|
|
|
if (scc.chan[chan].rx[scc.chan[chan].rxBufCur].delay > 0) {
|
2017-10-17 13:43:20 +00:00
|
|
|
scc.chan[chan].rx[scc.chan[chan].rxBufCur].delay--;
|
|
|
|
if (scc.chan[chan].rx[scc.chan[chan].rxBufCur].delay==0) {
|
|
|
|
printf("Feeding buffer %d into SCC\n", scc.chan[chan].rxBufCur);
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2017-03-24 10:44:04 +00:00
|
|
|
//WR15 is the External/Status Interrupt Control and has the interrupt enable bits.
|
|
|
|
#define SCC_WR15_BREAK (1<<7)
|
|
|
|
#define SCC_WR15_TXU (1<<6)
|
|
|
|
#define SCC_WR15_CTS (1<<5)
|
|
|
|
#define SCC_WR15_SYNC (1<<4)
|
|
|
|
#define SCC_WR15_DCD (1<<3)
|
|
|
|
#define SCC_WR15_ZCOUNT (1<<1)
|
|
|
|
|
2017-10-18 03:11:15 +00:00
|
|
|
//RR3, when read, gives the Interrupt Pending status.
|
2017-03-24 10:44:04 +00:00
|
|
|
//This is reflected in scc.intpending.
|
2017-10-18 03:11:15 +00:00
|
|
|
#define SCC_RR3_CHB_EXT (1<<0)
|
|
|
|
#define SCC_RR3_CHB_TX (1<<1)
|
|
|
|
#define SCC_RR3_CHB_RX (1<<2)
|
|
|
|
#define SCC_RR3_CHA_EXT (1<<3)
|
|
|
|
#define SCC_RR3_CHA_TX (1<<4)
|
|
|
|
#define SCC_RR3_CHA_RX (1<<5)
|
2017-03-24 10:44:04 +00:00
|
|
|
|
2017-10-19 07:53:10 +00:00
|
|
|
#define SCC_R0_RX (1<<0)
|
|
|
|
#define SCC_R0_ZEROCOUNT (1<<1)
|
|
|
|
#define SCC_R0_TXE (1<<2)
|
|
|
|
#define SCC_R0_DCD (1<<3)
|
|
|
|
#define SCC_R0_SYNCHUNT (1<<4)
|
|
|
|
#define SCC_R0_CTS (1<<5)
|
|
|
|
#define SCC_R0_EOM (1<<6)
|
|
|
|
#define SCC_R0_BREAKABRT (1<<7)
|
|
|
|
|
2017-10-18 07:20:12 +00:00
|
|
|
static void explainWrite(int reg, int chan, int val) {
|
|
|
|
const static char *cmdLo[]={"null", "point_high", "reset_ext_status_int", "send_ABORT",
|
|
|
|
"ena_int_on_next_char", "reset_tx_pending", "error_reset", "reset_highest_ius"};
|
|
|
|
const static char *cmdHi[]={"null", "reset_rx_crc", "reset_tx_crc", "reset_tx_underrun_EOM_latch"};
|
|
|
|
const static char *intEna[]={"RxIntDisabled", "RxInt1stCharOrSpecial", "RxIntAllCharOrSpecial", "RxIntSpecial"};
|
|
|
|
const static char *rstDesc[]={"NoReset", "ResetChB", "ResetChA", "HwReset"};
|
2017-10-19 07:53:10 +00:00
|
|
|
if (reg==0) {
|
2017-10-18 07:20:12 +00:00
|
|
|
if (((val&0xF8)!=0) && ((val&0xF8)!=0x08)) {
|
|
|
|
printf("Write reg 0; CmdHi=%s CmdLo=%s\n", cmdHi[(val>>6)&3], cmdLo[(val>>3)&7]);
|
|
|
|
}
|
|
|
|
} else if (reg==1) {
|
|
|
|
printf("Write reg 1 for chan %d: ", chan);
|
|
|
|
if (val&0x80) printf("WaitDmaReqEn ");
|
|
|
|
if (val&0x40) printf("WaitDmaReqFn ");
|
|
|
|
if (val&0x20) printf("WaitDmaOnRxTx ");
|
|
|
|
if (val&0x04) printf("ParityIsSpecial ");
|
|
|
|
if (val&0x02) printf("TxIntEna ");
|
|
|
|
if (val&0x01) printf("RxIntEna ");
|
|
|
|
printf("%s\n", intEna[(val>>3)&3]);
|
|
|
|
} else if (reg==9) {
|
|
|
|
printf("Write reg 9: cmd=%s ", rstDesc[(val>>6)&3]);
|
|
|
|
if (val&0x01) printf("VIS ");
|
|
|
|
if (val&0x02) printf("NV ");
|
|
|
|
if (val&0x04) printf("DLC ");
|
|
|
|
if (val&0x08) printf("MIE ");
|
|
|
|
if (val&0x10) printf("StatusHi ");
|
|
|
|
if (val&0x20) printf("RESVD ");
|
|
|
|
printf("\n");
|
|
|
|
} else if (reg==15) {
|
|
|
|
printf("Write reg 15: ");
|
|
|
|
if (val&0x02) printf("ZeroCountIE ");
|
|
|
|
if (val&0x08) printf("DcdIE ");
|
|
|
|
if (val&0x10) printf("SyncHuntIE ");
|
|
|
|
if (val&0x20) printf("CtsIE ");
|
|
|
|
if (val&0x40) printf("TxUnderrunIE ");
|
|
|
|
if (val&0x80) printf("BreakAbortIE ");
|
|
|
|
printf("\n");
|
|
|
|
} else {
|
|
|
|
printf("Write chan %d reg %d val 0x%02X\n", chan, reg, val);
|
|
|
|
}
|
|
|
|
}
|
2017-09-04 01:25:24 +00:00
|
|
|
|
2017-10-19 07:53:10 +00:00
|
|
|
void explainRead(int reg, int chan, int val) {
|
|
|
|
const static char *intRsn[]={
|
|
|
|
"ChBTxBufEmpty", "ChBExtOrStatusChange", "ChBRecvCharAvail", "ChBSpecRecvCond",
|
|
|
|
"ChATxBufEmpty", "ChAExtOrStatusChange", "ChARecvCharAvail", "ChASpecRecvCond",
|
|
|
|
};
|
|
|
|
if (reg==0) {
|
|
|
|
printf("Read chan %d reg 0:", chan);
|
|
|
|
if (val&0x01) printf("RxCharAvailable ");
|
|
|
|
if (val&0x02) printf("ZeroCount ");
|
|
|
|
if (val&0x04) printf("TxBufEmpty ");
|
|
|
|
if (val&0x08) printf("DCD ");
|
|
|
|
if (val&0x10) printf("SyncHunt ");
|
|
|
|
if (val&0x20) printf("CTS ");
|
|
|
|
if (val&0x40) printf("TxUnderrunEOM ");
|
|
|
|
if (val&0x80) printf("BreakAbort ");
|
|
|
|
printf("\n");
|
|
|
|
} else if (reg==2) {
|
|
|
|
printf("Read reg 2: ");
|
|
|
|
printf("IntRsn=%s\n", intRsn[(val>>1)&7]);
|
|
|
|
} else {
|
|
|
|
printf("Read chan %d reg %d val 0x%X\n", chan, reg, val);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-10-18 07:20:12 +00:00
|
|
|
//Raises an interrupt if needed, specifically when intpending indicates so. Need to
|
|
|
|
//change intpending beforehand.
|
2017-03-24 10:44:04 +00:00
|
|
|
static void raiseInt(int chan) {
|
2017-10-18 07:20:12 +00:00
|
|
|
const char *desc[]={"CHB_EXT", "CHB_TX", "CHB_RX", "CHA_EXT", "CHA_TX", "CHA_RX"};
|
2017-10-17 13:43:20 +00:00
|
|
|
if ((scc.chan[chan].wr1&1) ){ //&& (scc.intpending&(~scc.intpendingOld))) {
|
2017-03-24 10:44:04 +00:00
|
|
|
scc.intpendingOld=scc.intpending;
|
2017-10-18 07:20:12 +00:00
|
|
|
printf("SCC int, pending %x: ", scc.intpending);
|
|
|
|
for (int i=0; i<6; i++) {
|
|
|
|
if (scc.intpending&(1<<i)) printf("%s ", desc[i]);
|
|
|
|
}
|
|
|
|
printf("\n");
|
2017-03-24 10:44:04 +00:00
|
|
|
sccIrq(1);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-10-19 07:53:10 +00:00
|
|
|
|
|
|
|
static int calcRr0(int chan) {
|
|
|
|
int val=0;
|
|
|
|
if (rxHasByte(chan)) val|=SCC_R0_RX;
|
|
|
|
//Bit 1 is zero count - never set
|
|
|
|
val|=SCC_R0_TXE; //tx buffer always empty
|
|
|
|
val|=SCC_R0_CTS;
|
|
|
|
if (scc.chan[chan].dcd) val|=SCC_R0_DCD;
|
|
|
|
if (scc.chan[chan].hunting) val|=SCC_R0_SYNCHUNT;
|
|
|
|
if (scc.chan[chan].cts) val|=SCC_R0_CTS;
|
|
|
|
if (scc.chan[chan].txTimer==0) val|=SCC_R0_EOM;
|
|
|
|
if (scc.chan[chan].rxAbrtTimer>0 && scc.chan[chan].rxAbrtTimer<20) val|=SCC_R0_BREAKABRT; //abort
|
|
|
|
//if (rxBytesLeft(chan)==1) val|=SCC_R0_BREAKABRT; //abort
|
|
|
|
//if (scc.chan[chan].rxEom) val|=SCC_R0_BREAKABRT; //abort
|
|
|
|
return val;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void checkExtInt(int chan) {
|
|
|
|
int rr0=calcRr0(chan);
|
|
|
|
int dif=(rr0^scc.chan[chan].rr0Prev);
|
|
|
|
int wr15=scc.chan[chan].wr15;
|
|
|
|
int triggered=0;
|
|
|
|
if ((dif&SCC_R0_BREAKABRT) && (wr15&SCC_WR15_BREAK)) triggered=1;
|
|
|
|
if ((dif&SCC_R0_CTS) && (wr15&SCC_WR15_CTS)) triggered=1;
|
|
|
|
if ((dif&SCC_R0_DCD) && (wr15&SCC_WR15_DCD)) triggered=1;
|
|
|
|
if ((dif&SCC_R0_SYNCHUNT) && (wr15&SCC_WR15_SYNC)) triggered=1;
|
|
|
|
if ((dif&SCC_R0_EOM) && (wr15&SCC_WR15_TXU)) triggered=1;
|
|
|
|
if (triggered) {
|
|
|
|
if (chan==0 && ((scc.intpending&SCC_RR3_CHA_EXT)==0)) {
|
|
|
|
scc.chan[chan].rr0Latched=rr0;
|
|
|
|
scc.intpending|=SCC_RR3_CHA_EXT;
|
|
|
|
raiseInt(chan);
|
|
|
|
}
|
|
|
|
if (chan==1 && ((scc.intpending&SCC_RR3_CHB_EXT)==0)) {
|
|
|
|
scc.chan[chan].rr0Latched=rr0;
|
|
|
|
scc.intpending|=SCC_RR3_CHB_EXT;
|
|
|
|
raiseInt(chan);
|
2017-03-24 10:44:04 +00:00
|
|
|
}
|
|
|
|
}
|
2017-10-19 07:53:10 +00:00
|
|
|
scc.chan[chan].rr0Prev=rr0;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void sccSetDcd(int chan, int val) {
|
|
|
|
val=val?1:0;
|
2017-09-04 01:25:24 +00:00
|
|
|
scc.chan[chan].dcd=val;
|
2017-10-19 07:53:10 +00:00
|
|
|
checkExtInt(chan);
|
2017-09-04 01:25:24 +00:00
|
|
|
}
|
|
|
|
|
2017-10-17 13:43:20 +00:00
|
|
|
void sccRecv(int chan, uint8_t *data, int len, int delay) {
|
|
|
|
int bufid=scc.chan[chan].rxBufCur;
|
|
|
|
int n=0;
|
|
|
|
do {
|
|
|
|
if (scc.chan[chan].rx[bufid].delay==-1) break;
|
|
|
|
bufid++;
|
|
|
|
if (bufid>=NO_RXBUF) bufid=0;
|
|
|
|
n++;
|
|
|
|
} while(bufid!=scc.chan[chan].rxBufCur);
|
|
|
|
|
|
|
|
//check if all buffers are full
|
|
|
|
if (scc.chan[chan].rx[bufid].delay!=-1) {
|
|
|
|
printf("Eek! Can't queue buffer: full!\n");
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
printf("Serial transmit for chan %d queued; bufidx %d, len=%d delay=%d, %d other buffers in queue\n", chan, bufid, len, delay, n);
|
|
|
|
memcpy(scc.chan[chan].rx[bufid].data, data, len);
|
|
|
|
scc.chan[chan].rx[bufid].data[len]=0xA5; //crc1
|
|
|
|
scc.chan[chan].rx[bufid].data[len+1]=0xA5; //crc2
|
2017-10-19 07:53:10 +00:00
|
|
|
// scc.chan[chan].rx[bufid].data[len+2]=0; //end flag
|
|
|
|
// scc.chan[chan].rx[bufid].data[len+3]=0; //dummy
|
|
|
|
scc.chan[chan].rx[bufid].len=len+2;
|
2017-10-17 13:43:20 +00:00
|
|
|
scc.chan[chan].rx[bufid].delay=delay;
|
|
|
|
}
|
|
|
|
|
2017-09-04 01:25:24 +00:00
|
|
|
void sccTxFinished(int chan) {
|
|
|
|
hexdump(scc.chan[chan].txData, scc.chan[chan].txPos);
|
|
|
|
localtalkSend(scc.chan[chan].txData, scc.chan[chan].txPos);
|
2017-10-17 13:43:20 +00:00
|
|
|
//Echo back data over Rx of same channel
|
|
|
|
// sccRecv(chan, scc.chan[chan].txData, scc.chan[chan].txPos, 0);
|
|
|
|
// triggerRx(chan);
|
|
|
|
|
2017-09-04 01:25:24 +00:00
|
|
|
scc.chan[chan].txPos=0;
|
2017-10-19 07:53:10 +00:00
|
|
|
//scc.chan[chan].hunting=1;
|
2017-09-04 01:25:24 +00:00
|
|
|
}
|
|
|
|
|
2017-10-19 07:53:10 +00:00
|
|
|
static void checkRxIntPending(int chan) {
|
|
|
|
int rxintena=scc.chan[chan].wr1&0x18;
|
|
|
|
if (!rxHasByte(chan) || rxintena==0) { //todo: better rxintena handling
|
|
|
|
scc.intpending&=~((chan==0)?SCC_RR3_CHA_RX:SCC_RR3_CHB_RX);
|
|
|
|
printf("Resetting int pending for channel %d\n", chan);
|
|
|
|
} else {
|
|
|
|
scc.intpending|=((chan==0)?SCC_RR3_CHA_RX:SCC_RR3_CHB_RX);
|
|
|
|
}
|
|
|
|
raiseInt(chan);
|
|
|
|
}
|
2017-09-04 01:25:24 +00:00
|
|
|
|
|
|
|
static void triggerRx(int chan) {
|
2017-10-19 07:53:10 +00:00
|
|
|
// if (!scc.chan[chan].hunting) return;
|
2017-10-17 13:43:20 +00:00
|
|
|
int bufid=scc.chan[chan].rxBufCur;
|
|
|
|
printf("SCC: Receiving bufid %d:\n", bufid);
|
|
|
|
hexdump(scc.chan[chan].rx[bufid].data, scc.chan[chan].rx[bufid].len);
|
|
|
|
if (scc.chan[chan].rx[bufid].data[0]==0xFF || scc.chan[chan].rx[bufid].data[0]==scc.chan[chan].sdlcaddr) {
|
2017-09-04 01:25:24 +00:00
|
|
|
scc.chan[chan].rxPos=0;
|
2017-10-17 13:43:20 +00:00
|
|
|
printf("WR15: 0x%X WR1: %X\n", scc.chan[chan].wr15, scc.chan[chan].wr1);
|
2017-09-04 01:25:24 +00:00
|
|
|
//Sync int
|
|
|
|
if (scc.chan[chan].wr15&SCC_WR15_SYNC) {
|
2017-10-19 07:53:10 +00:00
|
|
|
scc.intpending|=((chan==0)?SCC_RR3_CHA_EXT:SCC_RR3_CHB_EXT);
|
2017-09-04 01:25:24 +00:00
|
|
|
raiseInt(chan);
|
|
|
|
}
|
|
|
|
//RxD int
|
2017-10-19 07:53:10 +00:00
|
|
|
checkRxIntPending(chan);
|
2017-09-04 01:25:24 +00:00
|
|
|
scc.chan[chan].hunting=0;
|
2017-10-18 07:20:12 +00:00
|
|
|
scc.chan[chan].eofDelay=scc.chan[chan].rx[bufid].len*3;
|
2017-09-04 01:25:24 +00:00
|
|
|
} else {
|
2017-10-17 13:43:20 +00:00
|
|
|
printf("...Not for us, ignoring.\n");
|
|
|
|
rxBufIgnoreRest(chan);
|
2017-09-04 01:25:24 +00:00
|
|
|
}
|
2017-03-24 10:44:04 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void sccWrite(unsigned int addr, unsigned int val) {
|
|
|
|
int chan, reg;
|
|
|
|
if (addr & (1<<1)) chan=SCC_CHANA; else chan=SCC_CHANB;
|
|
|
|
if (addr & (1<<2)) {
|
|
|
|
//Data
|
|
|
|
reg=8;
|
|
|
|
} else {
|
|
|
|
//Control
|
|
|
|
reg=scc.regptr;
|
|
|
|
scc.regptr=0;
|
|
|
|
}
|
2017-10-18 07:20:12 +00:00
|
|
|
explainWrite(reg, chan, val);
|
2017-03-24 10:44:04 +00:00
|
|
|
if (reg==0) {
|
|
|
|
scc.regptr=val&0x7;
|
|
|
|
if ((val&0x38)==0x8) scc.regptr|=8;
|
|
|
|
if ((val&0x38)==0x10) {
|
2017-10-19 07:53:10 +00:00
|
|
|
//Reset ext/status int latch
|
|
|
|
scc.chan[chan].rr0Latched=-1;
|
2017-03-24 10:44:04 +00:00
|
|
|
}
|
2017-09-04 01:25:24 +00:00
|
|
|
if ((val&0x38)==0x18) {
|
|
|
|
//SCC abort: parse whatever we sent
|
2017-10-17 13:43:20 +00:00
|
|
|
// printf("SCC ABORT: Sent data\n");
|
2017-09-04 01:25:24 +00:00
|
|
|
sccTxFinished(chan);
|
2017-10-19 07:53:10 +00:00
|
|
|
checkRxIntPending(chan);
|
|
|
|
}
|
|
|
|
if ((val&0x38)==0x30) {
|
|
|
|
//Error Reset: kills special condition bytes from fifo
|
|
|
|
rxBufIgnoreRest(chan);
|
|
|
|
checkRxIntPending(chan);
|
|
|
|
printf("Error Reset Finished, pending=%x\n", scc.intpending);
|
2017-09-04 01:25:24 +00:00
|
|
|
}
|
|
|
|
if ((val&0xc0)==0xC0) {
|
|
|
|
//reset tx underrun latch
|
2017-10-17 13:43:20 +00:00
|
|
|
// sccTxFinished(chan);
|
2017-10-19 07:53:10 +00:00
|
|
|
scc.chan[chan].txTimer=10;
|
2017-10-17 13:43:20 +00:00
|
|
|
//if (scc.chan[chan].txTimer==0) scc.chan[chan].txTimer=-1;
|
2017-09-04 01:25:24 +00:00
|
|
|
}
|
2017-03-24 10:44:04 +00:00
|
|
|
} else if (reg==1) {
|
2017-09-04 01:25:24 +00:00
|
|
|
scc.chan[chan].wr1=val;
|
2017-10-17 13:43:20 +00:00
|
|
|
} else if (reg==2) {
|
|
|
|
scc.wr2=val;
|
2017-09-04 01:25:24 +00:00
|
|
|
} else if (reg==3) {
|
|
|
|
//bitsperchar1, bitsperchar0, autoenables, enterhuntmode, rxcrcen, addresssearch, synccharloadinh, rxena
|
|
|
|
//autoenable: cts = tx ena, dcd = rx ena
|
|
|
|
if (val&0x10) scc.chan[chan].hunting=1;
|
|
|
|
} else if (reg==6) {
|
|
|
|
scc.chan[chan].sdlcaddr=val;
|
|
|
|
} else if (reg==8) {
|
|
|
|
scc.chan[chan].txData[scc.chan[chan].txPos++]=val;
|
|
|
|
scc.chan[chan].txTimer+=30;
|
2017-10-19 07:53:10 +00:00
|
|
|
printf("TX! Pos %d timer set to %d\n", scc.chan[chan].txPos, scc.chan[chan].txTimer);
|
2017-10-17 13:43:20 +00:00
|
|
|
} else if (reg==9) {
|
|
|
|
scc.wr9=val;
|
2017-03-24 10:44:04 +00:00
|
|
|
} else if (reg==15) {
|
2017-09-04 01:25:24 +00:00
|
|
|
scc.chan[chan].wr15=val;
|
2017-10-17 13:43:20 +00:00
|
|
|
raiseInt(chan);
|
2017-03-24 10:44:04 +00:00
|
|
|
}
|
2017-10-19 07:53:10 +00:00
|
|
|
checkExtInt(chan);
|
2017-10-18 07:20:12 +00:00
|
|
|
// printf("SCC: write to addr %x chan %d reg %d val %x\n", addr, chan, reg, val);
|
2017-03-24 10:44:04 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
unsigned int sccRead(unsigned int addr) {
|
|
|
|
int chan, reg, val=0xff;
|
|
|
|
if (addr & (1<<1)) chan=SCC_CHANA; else chan=SCC_CHANB;
|
|
|
|
if (addr & (1<<2)) {
|
|
|
|
//Data
|
|
|
|
reg=8;
|
|
|
|
} else {
|
|
|
|
//Control
|
|
|
|
reg=scc.regptr;
|
|
|
|
scc.regptr=0;
|
|
|
|
}
|
|
|
|
if (reg==0) {
|
2017-10-19 07:53:10 +00:00
|
|
|
if (scc.chan[chan].rr0Latched==-1) {
|
|
|
|
val=calcRr0(chan);
|
|
|
|
} else {
|
|
|
|
val=scc.chan[chan].rr0Latched;
|
|
|
|
scc.chan[chan].rr0Latched=-1;
|
|
|
|
}
|
2017-03-24 10:44:04 +00:00
|
|
|
} else if (reg==1) {
|
2017-10-17 13:43:20 +00:00
|
|
|
//Actually, these come out of the same fifo as the data, so this status should be for the fifo
|
|
|
|
//val available.
|
2017-09-04 01:25:24 +00:00
|
|
|
//EndOfFrame, CRCErr, RXOverrun, ParityErr, Residue0, Residue1, Residue2, AllSent
|
|
|
|
val=0x7; //residue code 011, all sent
|
2017-10-19 07:53:10 +00:00
|
|
|
//if (rxBytesLeft(chan)==1) val|=(1<<7); //end of frame
|
|
|
|
if (scc.chan[chan].rxEom) val|=(1<<7); //end of frame
|
2017-03-24 10:44:04 +00:00
|
|
|
} else if (reg==2 && chan==SCC_CHANB) {
|
|
|
|
//We assume this also does an intack.
|
|
|
|
int rsn=0;
|
2017-10-19 07:53:10 +00:00
|
|
|
|
|
|
|
printf("Pending: 0x%X\n", scc.intpending);
|
2017-10-18 03:11:15 +00:00
|
|
|
if (scc.intpending & SCC_RR3_CHB_EXT) {
|
2017-03-24 10:44:04 +00:00
|
|
|
rsn=1;
|
2017-10-18 03:11:15 +00:00
|
|
|
scc.intpending&=~SCC_RR3_CHB_EXT;
|
|
|
|
} else if (scc.intpending & SCC_RR3_CHA_EXT) {
|
2017-03-24 10:44:04 +00:00
|
|
|
rsn=5;
|
2017-10-18 03:11:15 +00:00
|
|
|
scc.intpending&=~SCC_RR3_CHA_EXT;
|
2017-10-19 07:53:10 +00:00
|
|
|
} else if (scc.intpending & SCC_RR3_CHA_RX) {
|
2017-10-17 13:43:20 +00:00
|
|
|
rsn=6;
|
2017-10-19 07:53:10 +00:00
|
|
|
checkRxIntPending(0);
|
|
|
|
} else if (scc.intpending & SCC_RR3_CHB_RX) {
|
2017-10-17 13:43:20 +00:00
|
|
|
rsn=2;
|
2017-10-19 07:53:10 +00:00
|
|
|
checkRxIntPending(1);
|
2017-10-17 13:43:20 +00:00
|
|
|
}
|
|
|
|
|
2017-10-19 07:53:10 +00:00
|
|
|
printf("Rsn: %d\n", rsn);
|
2017-10-17 13:43:20 +00:00
|
|
|
val=scc.wr2;
|
|
|
|
if (scc.wr9&0x10) { //hi/lo
|
|
|
|
val=(scc.wr2&~0x70)|rsn<<4;
|
|
|
|
} else {
|
2017-10-19 07:53:10 +00:00
|
|
|
val=(scc.wr2&~0xE)|rsn<<1;
|
2017-03-24 10:44:04 +00:00
|
|
|
}
|
|
|
|
if (scc.intpending&0x38) raiseInt(SCC_CHANA);
|
|
|
|
if (scc.intpending&0x07) raiseInt(SCC_CHANB);
|
|
|
|
} else if (reg==3) {
|
|
|
|
if (chan==SCC_CHANA) val=scc.intpending; else val=0;
|
2017-09-04 01:25:24 +00:00
|
|
|
} else if (reg==8) {
|
|
|
|
//rx buffer
|
2017-10-17 13:43:20 +00:00
|
|
|
|
|
|
|
if (rxHasByte(chan)) {
|
|
|
|
int left;
|
|
|
|
val=rxByte(chan, &left);
|
2017-10-19 07:53:10 +00:00
|
|
|
printf("SCC READ DATA val %x, %d bytes left\n", val, left);
|
|
|
|
if (left==1) { //because status goes with byte *to be read*, the last byte here is the EOM byte
|
|
|
|
printf("Setting EOM\n");
|
|
|
|
scc.chan[chan].rxEom=1;
|
|
|
|
scc.chan[chan].rxAbrtTimer=20;
|
|
|
|
} else {
|
|
|
|
scc.chan[chan].rxEom=0;
|
2017-09-04 01:25:24 +00:00
|
|
|
}
|
2017-10-19 07:53:10 +00:00
|
|
|
// if (left==1) scc.chan[chan].hunting=1;
|
2017-09-04 01:25:24 +00:00
|
|
|
} else {
|
2017-10-17 13:43:20 +00:00
|
|
|
printf("SCC READ but no data?\n");
|
2017-10-19 07:53:10 +00:00
|
|
|
scc.chan[chan].rxEom=0;
|
2017-09-04 01:25:24 +00:00
|
|
|
val=0;
|
|
|
|
}
|
2017-10-19 07:53:10 +00:00
|
|
|
checkRxIntPending(chan);
|
2017-09-04 01:25:24 +00:00
|
|
|
} else if (reg==10) {
|
|
|
|
//Misc status (mostly SDLC)
|
|
|
|
val=0;
|
2017-03-24 10:44:04 +00:00
|
|
|
} else if (reg==15) {
|
2017-09-04 01:25:24 +00:00
|
|
|
val=scc.chan[chan].wr15;
|
2017-03-24 10:44:04 +00:00
|
|
|
}
|
2017-10-19 07:53:10 +00:00
|
|
|
checkExtInt(chan);
|
|
|
|
explainRead(reg, chan, val);
|
2017-03-24 10:44:04 +00:00
|
|
|
return val;
|
|
|
|
}
|
|
|
|
|
2017-09-04 01:25:24 +00:00
|
|
|
//Called at about 800KHz
|
|
|
|
void sccTick() {
|
|
|
|
for (int n=0; n<2; n++) {
|
|
|
|
if (scc.chan[n].txTimer>0) {
|
|
|
|
scc.chan[n].txTimer--;
|
|
|
|
if (scc.chan[n].txTimer==0) {
|
2017-10-17 13:43:20 +00:00
|
|
|
// printf("Tx buffer empty: Sent data\n");
|
2017-09-04 01:25:24 +00:00
|
|
|
sccTxFinished(n);
|
|
|
|
}
|
|
|
|
}
|
2017-10-17 13:43:20 +00:00
|
|
|
if (rxBufTick(n)) {
|
|
|
|
triggerRx(n);
|
2017-09-04 01:25:24 +00:00
|
|
|
}
|
2017-10-18 07:20:12 +00:00
|
|
|
if (scc.chan[n].eofDelay>0) {
|
|
|
|
scc.chan[n].eofDelay--;
|
|
|
|
if (scc.chan[n].eofDelay==0 && (scc.chan[n].wr1&0x10)!=0) {
|
|
|
|
//Int mode is recv char or special / special only
|
|
|
|
printf("Raise EOF int for channel %d\n", n);
|
|
|
|
scc.chan[n].eofIntPending=1;
|
|
|
|
scc.intpending|=((n==0)?SCC_RR3_CHA_RX:SCC_RR3_CHB_RX);
|
|
|
|
raiseInt(n);
|
|
|
|
}
|
|
|
|
}
|
2017-10-19 07:53:10 +00:00
|
|
|
if (scc.chan[n].rxAbrtTimer>0) scc.chan[n].rxAbrtTimer--;
|
|
|
|
checkExtInt(n);
|
2017-09-04 01:25:24 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void sccInit() {
|
2017-10-19 07:53:10 +00:00
|
|
|
sccSetDcd(0, 1);
|
2017-09-04 01:25:24 +00:00
|
|
|
sccSetDcd(1, 1);
|
2017-10-19 07:53:10 +00:00
|
|
|
// scc.chan[0].txTimer=-1;
|
|
|
|
// scc.chan[1].txTimer=-1;
|
|
|
|
scc.chan[0].rr0Latched=-1;
|
|
|
|
scc.chan[1].rr0Latched=-1;
|
2017-10-17 13:43:20 +00:00
|
|
|
for (int x=0; x<NO_RXBUF; x++) {
|
|
|
|
scc.chan[0].rx[x].delay=-1;
|
|
|
|
scc.chan[1].rx[x].delay=-1;
|
|
|
|
}
|
2017-09-04 01:25:24 +00:00
|
|
|
}
|
|
|
|
|