I've been doing a bit more investigation about working with the 6551 as used on the BenEater 6502 SBC. This is all considering polled I/O. Does the following make sense?
Transmitting:
With TxRDY (TxEmpty) always being stuck ON, the recommended workaround is to use a software delay long enough for the shift register to move the data out. That seems a waste of CPU cyles to me, and I looked for ways to have some other "poll until ready" method that would allow the CPU to be doing something else between each transmission.
The BenEater also has a 6522 VIA, so my first thought was to program that to provide a centisecond counter - a common interupt task - and have that decrement a counter that the transmitter code could check. However, a 1cs tick at 1MHz is 10,000 cycles - 20 times longer than the 520 ticks that 19,200 at 1MHz needs. Maxing out at 115,200 just needs 87 ticks which you get from only 12 counts of a loop. So, reluctantly, at this point, sticking with a hard transmission delay.
The other thing that BenEater doesn't seem to have addressed yet is handshaking. Before transmissing you should check that the other end isn't saying "woah! I'm busy!". Here we hit another hardware problem.
Normally, CTS is used for this. But on the 6551 if CTS is lowered when a byte is being transmitted the TxData is corrupted. Documentation recommends CTS be tied to ground. However there is another input which can be monitored manually, DSR. So, tie DCD and CTS to ground, and use DSR as the incoming CTS line.
So, this gives me this code for polled data transmission:Code:
; DCD and CTS tied to ground
; DSR used as incoming CTS
;
TxDELAY: EQU ((MHZ*10000)/(BAUD/1000)-41)/5+1
; Minimum baud: 1MHz = 9600
; 2MHz = 19200
; 4MHz = 38400
; Modified code can be used for slower speed/faster CPU
TxBYTE: ; 6 for JSR
PHA ; 3
SEC ; 2
LDA #DELAY ; 2
TxLOOP:
SBC #1 ; 2
BNE TxLOOP ; 3,2 ; Loop to delay
TxWAIT:
LDA ACIA_STATUS ; 4
ASL A ; 2 ; Move DSR to bit 7
BPL TxWAIT ; 3,2 ; Loop until other end says Ok to transmit
PLA ; 4 ; Get byte back
STA ACIA_DATA ; 4 ; Send it
RTS ; 6
Receiving:
With receiving, we are the end saying "woah! I'm busy!" To do this we need to raise RTS or DTR depending on how we rise the output to the outgoing RTS line. Also, we need to ensure that we only lower RTS when we are able to take a byte from the incoming RxData. With polled input that means we must only lower RTS in the read routine itself to prevent something being sent when we are busy in some other code.
Something easy to miss is that even when we have raised RTS there may still be data "on the line" which will get overwritten if we lower RTS and don't get the previous byte of soon enough. So, we should check if there's a byte waiting for use before telling the other end it can start sending.
So, this gives me this code for polled data reception:Code:
; DTR or RTS used as outgoing RTS
;
RxBYTE:
LDA ACIA_STATUS
AND #&08 ; Check RxRDY (RxFull)
BNE RxREAD ; Already a byte waiting
LDA #&09 ; RTS=0, DTR=0
STA ACIA_COMMAND ; Allow other end to transmit
RxWAIT:
LDA ACIA_STATUS
AND #&08 ; Check RxRDY (RxFull)
BEQ RxWAIT
LDA #&00 ; RTS=1, DTR=1
STA ACIA_COMMAND ; Stop other end transmitting
RxREAD:
LDA ACIA_DATA
RTS
All this needs initialisation code along these lines:
Code:
RxTxINIT:
STA ACIA_RESET ; Reset ACIA
LDA #&00 ; RTS=1, DTR=1
STA ACIA_COMMAND ; Prevent other end transmitting
LDA #&1F ; Eg: 19,200 with 1.8MHz clock
STA ACIA_CONTROL
RTS
A TxDATA routine to allow a wider range of baud rates and CPU speed.
Code:
TxDELAY: EQU ((MHZ*10000)/(BAUD/1000)-37)/86+1
; Minimum baud: 1MHz = 600
; 2MHz = 1200
; 4MHz = 2400
TxDATA: ; 6 for JSR
PHA ; 3
TXA ; 2
PHA ; 3
SEC ; 2
LDA #DELAY ; 2
TxLOOP1:
LDX #16 ; 2
TxLOOP2:
DEX ; 2
BNE TxLOOP2 ; 3,2
SBC #1 ; 2
BNE TxLOOP1 ; 3,2 ; Loop to delay
PLA ; 4 ; Get X back
TAX ; 2
PLA ; 4 ; Get byte back
STA ACIA_DATA ; 4 ; Send it
RTS ; 6