rc2014: de-buffer MC6850 and SIO drivers
The buffer's implementation wasn't buying us much in exchange for its complexity. A modern machine was still too fast for it (copy/pasting text from a modern machine would send bytes too fast for the RC2014) and in the (theoretical so far) case of COS-to-COS communication, the buffer didn't help in cases where the baud rate was faster than the processing of each byte received (for example, if the byte was written directly to EEPROM). I'm scrapping it and, instead, use the RTS flag to signal the other side when we're ready to receive a new byte. Also, implement driver for channel B in SIO. I will need it to talk to my TRS-80 4P.
This commit is contained in:
parent
5ca99ad410
commit
3581beace0
@ -1,174 +1,111 @@
|
|||||||
( ----- 600 )
|
( ----- 600 )
|
||||||
601 ACIA 606 Zilog SIO driver
|
601 MC6850 driver 606 Zilog SIO driver
|
||||||
615 SPI relay 619 Xcomp unit
|
615 SPI relay 619 Xcomp unit
|
||||||
( ----- 601 )
|
( ----- 601 )
|
||||||
ACIA driver
|
( MC6850 Driver. Load range B601-B603. Requires:
|
||||||
|
6850_CTL for control register
|
||||||
Manage I/O from an asynchronous communication interface adapter
|
6850_IO for data register.
|
||||||
(ACIA). provides "(emit)" to put c char on the ACIA as well as
|
CTL numbers used: 0x16 = no interrupt, 8bit words, 1 stop bit
|
||||||
an input buffer from which a provided "(key)" reads. This driver
|
64x divide. 0x56 = RTS high )
|
||||||
installs an interrupt handler at RST38 to handle RX.
|
CODE 6850<
|
||||||
|
A 0x16 ( RTS low ) LDri, 6850_CTL OUTiA,
|
||||||
To use, begin by loading declarations (B582) before xcomp is
|
BEGIN,
|
||||||
loaded. These declarations provide default values for ports and
|
6850_CTL INAi, 0x01 ANDi, ( is ACIA rcv buf full? )
|
||||||
memory offsets that you can override. See B582.
|
JRZ, ( no, loop ) AGAIN,
|
||||||
|
A 0x56 ( RTS high ) LDri, 6850_CTL OUTiA,
|
||||||
Then, in the driver part, load range 583-588.
|
( we have data, fetch and push )
|
||||||
|
6850_IO INAi, PUSHA,
|
||||||
|
;CODE
|
||||||
( ----- 602 )
|
( ----- 602 )
|
||||||
0x80 CONSTANT ACIA_CTL ( IO port for ACIA's control register )
|
CODE 6850>
|
||||||
0x81 CONSTANT ACIA_IO ( IO port for ACIA's data registers )
|
HL POP, chkPS,
|
||||||
0x20 CONSTANT ACIA_BUFSZ ( SZ-1 must be a mask )
|
BEGIN,
|
||||||
( Address in memory that can be used variables shared
|
6850_CTL INAi, 0x02 ANDi, ( are we transmitting? )
|
||||||
with ACIA's native words. 4 bytes used. )
|
JRZ, ( yes, loop ) AGAIN,
|
||||||
CREATE ACIA_MEM SYSVARS 0x70 + ,
|
A L LDrr, 6850_IO OUTiA,
|
||||||
( Points to ACIA buf )
|
;CODE
|
||||||
: ACIA( ACIA_MEM @ 2+ ;
|
|
||||||
( Read buf idx Pre-inc )
|
|
||||||
: ACIAR> ACIA_MEM @ ;
|
|
||||||
( Write buf idx Post-inc )
|
|
||||||
: ACIAW> ACIA_MEM @ 1+ ;
|
|
||||||
( This means that if W> == R>, buffer is full.
|
|
||||||
If R>+1 == W>, buffer is empty. )
|
|
||||||
( ----- 603 )
|
( ----- 603 )
|
||||||
( ACIA INT handler, read into ACIAW> )
|
: (key) 6850< ;
|
||||||
( Set RST 38 jump ) PC ORG @ 0x39 + !
|
: (emit) 6850> ;
|
||||||
AF PUSH,
|
: 6850$ 0x56 ( RTS high ) [ 6850_CTL LITN ] PC! ;
|
||||||
ACIA_CTL INAi, 0x01 ANDi, ( is ACIA rcv buf full? )
|
|
||||||
IFZ, ( no, abort ) AF POP, EI, RETI, THEN,
|
|
||||||
HL PUSH,
|
|
||||||
HL ACIAW> LDdi, A (HL) LDrr,
|
|
||||||
HL DECd, (HL) CPr, ( W> == R> ? )
|
|
||||||
IFNZ, ( buffer not full )
|
|
||||||
( get wr ptr ) HL ACIA( LDd(i),
|
|
||||||
L ADDr, IFC, H INCr, THEN, L A LDrr,
|
|
||||||
( fetch/write ) ACIA_IO INAi, (HL) A LDrr,
|
|
||||||
( advance W> ) ACIAW> LDA(i), A INCr,
|
|
||||||
ACIA_BUFSZ 1- ANDi, ACIAW> LD(i)A,
|
|
||||||
THEN,
|
|
||||||
HL POP, AF POP, EI, RETI,
|
|
||||||
( ----- 604 )
|
|
||||||
: (key)
|
|
||||||
( inc then fetch )
|
|
||||||
[ ACIAR> LITN ] C@ 1+ [ ACIA_BUFSZ 1- LITN ] AND
|
|
||||||
( As long as R> == W>-1, it means that buffer is empty )
|
|
||||||
BEGIN DUP [ ACIAW> LITN ] C@ = NOT UNTIL
|
|
||||||
DUP [ ACIA( LITN ] @ + C@ ( ridx c )
|
|
||||||
SWAP [ ACIAR> LITN ] C! ( c )
|
|
||||||
;
|
|
||||||
: (emit)
|
|
||||||
( As long at CTL bit 1 is low, we are transmitting. wait )
|
|
||||||
BEGIN [ ACIA_CTL LITN ] PC@ 0x02 AND UNTIL
|
|
||||||
( The way is clear, go! )
|
|
||||||
[ ACIA_IO LITN ] PC!
|
|
||||||
;
|
|
||||||
( ----- 605 )
|
( ----- 605 )
|
||||||
: ACIA$
|
( Zilog SIO driver. Load range B605-607. Requires:
|
||||||
H@ [ ACIA( LITN ] ! 0 [ ACIAR> LITN ] C!
|
SIOA_CTL for ch A control register
|
||||||
1 [ ACIAW> LITN ] C! ( write index starts one pos later )
|
SIOA_DATA for ch A data register
|
||||||
[ ACIA_BUFSZ LITN ] ALLOT
|
SIOB_CTL for ch B control register
|
||||||
( setup ACIA
|
SIOB_DATA for ch B data register )
|
||||||
CR7 (1) - Receive Interrupt enabled
|
CODE SIOA<
|
||||||
CR6:5 (00) - RTS low, transmit interrupt disabled.
|
A 0x05 ( PTR5 ) LDri, SIOA_CTL OUTiA,
|
||||||
CR4:2 (101) - 8 bits + 1 stop bit
|
A 0b01101000 ( De-assert RTS ) LDri, SIOA_CTL OUTiA,
|
||||||
CR1:0 (10) - Counter divide: 64 )
|
BEGIN,
|
||||||
0b10010110 [ ACIA_CTL LITN ] PC!
|
SIOA_CTL ( RR0 ) INAi, 0x01 ANDi, ( is rcv buf full? )
|
||||||
(im1) ;
|
JRZ, ( no, loop ) AGAIN,
|
||||||
|
A 0x05 ( PTR5 ) LDri, SIOA_CTL OUTiA,
|
||||||
|
A 0b01101010 ( Assert RTS ) LDri, SIOA_CTL OUTiA,
|
||||||
|
( we have data, fetch and push )
|
||||||
|
SIOA_DATA INAi, PUSHA,
|
||||||
|
;CODE
|
||||||
( ----- 606 )
|
( ----- 606 )
|
||||||
Zilog SIO driver
|
CODE SIOA>
|
||||||
|
HL POP, chkPS,
|
||||||
Declarations at B607
|
BEGIN,
|
||||||
|
SIOA_CTL INAi, 0x04 ANDi, ( are we transmitting? )
|
||||||
Driver load range at B608-B610
|
JRZ, ( yes, loop ) AGAIN,
|
||||||
|
A L LDrr, SIOA_DATA OUTiA,
|
||||||
|
;CODE
|
||||||
|
CREATE _ ( init data ) 0x18 C, ( CMD3 )
|
||||||
|
0x24 C, ( CMD2/PTR4 ) 0b11000100 C, ( WR4/64x/1stop/nopar )
|
||||||
|
0x03 C, ( PTR3 ) 0b11000001 C, ( WR3/RXen/8char )
|
||||||
|
0x05 C, ( PTR5 ) 0b01101010 C, ( WR5/TXen/8char/RTS )
|
||||||
|
0x21 C, ( CMD2/PTR1 ) 0 C, ( WR1/Rx no INT )
|
||||||
|
: SIOA$ 9 0 DO _ I + C@ [ SIOA_CTL LITN ] PC! LOOP ;
|
||||||
( ----- 607 )
|
( ----- 607 )
|
||||||
0x80 CONSTANT SIO_ACTL 0x81 CONSTANT SIO_ADATA
|
CODE SIOB<
|
||||||
0x82 CONSTANT SIO_BCTL 0x83 CONSTANT SIO_BDATA
|
BEGIN,
|
||||||
0x20 CONSTANT SIO_BUFSZ ( SZ-1 must be a mask )
|
SIOB_CTL ( RR0 ) INAi, 0x01 ANDi, ( is rcv buf full? )
|
||||||
( Address in memory that can be used variables shared
|
JRZ, ( no, loop ) AGAIN,
|
||||||
with SIO native words. 4 bytes used. )
|
( we have data, fetch and push )
|
||||||
CREATE SIO_MEM SYSVARS 0x70 + ,
|
SIOB_DATA INAi, PUSHA,
|
||||||
( Points to SIO buf )
|
;CODE
|
||||||
: SIO( SIO_MEM @ 2+ ;
|
CODE SIOB>
|
||||||
( Read buf idx Pre-inc )
|
HL POP, chkPS,
|
||||||
: SIOR> SIO_MEM @ ;
|
BEGIN,
|
||||||
( Write buf idx Post-inc )
|
SIOB_CTL INAi, 0x04 ANDi, ( are we transmitting? )
|
||||||
: SIOW> SIO_MEM @ 1+ ;
|
JRZ, ( yes, loop ) AGAIN,
|
||||||
( This means that if W> == R>, buffer is full.
|
A L LDrr, SIOB_DATA OUTiA,
|
||||||
If R>+1 == W>, buffer is empty. )
|
;CODE
|
||||||
( ----- 608 )
|
: SIOB$ 9 0 DO _ I + C@ [ SIOB_CTL LITN ] PC! LOOP ;
|
||||||
( INT handler. Set RST 38 jump ) PC ORG @ 0x39 + !
|
|
||||||
AF PUSH, BEGIN,
|
|
||||||
SIO_ACTL INAi, ( RR0 ) 0x01 ANDi, ( is recv buf full? )
|
|
||||||
IFZ, ( nope, exit ) A 0x20 ( CMD 4 ) LDri, SIO_ACTL OUTiA,
|
|
||||||
AF POP, EI, RETI, THEN,
|
|
||||||
HL PUSH,
|
|
||||||
HL SIOW> LDdi, A (HL) LDrr,
|
|
||||||
HL DECd, (HL) CPr, ( W> == R> ? )
|
|
||||||
IFNZ, ( buffer not full )
|
|
||||||
( get wr ptr ) HL SIO( LDd(i),
|
|
||||||
L ADDr, IFC, H INCr, THEN, L A LDrr,
|
|
||||||
( fetch/write ) SIO_ADATA INAi, (HL) A LDrr,
|
|
||||||
( advance W> ) SIOW> LDA(i), A INCr,
|
|
||||||
SIO_BUFSZ 1- ANDi, SIOW> LD(i)A,
|
|
||||||
THEN, HL POP, JR, AGAIN,
|
|
||||||
( ----- 609 )
|
|
||||||
: (key)
|
|
||||||
( inc then fetch )
|
|
||||||
[ SIOR> LITN ] C@ 1+ [ SIO_BUFSZ 1- LITN ] AND
|
|
||||||
( As long as R> == W>-1, it means that buffer is empty )
|
|
||||||
BEGIN DUP [ SIOW> LITN ] C@ = NOT UNTIL
|
|
||||||
DUP [ SIO( LITN ] @ + C@ ( ridx c )
|
|
||||||
SWAP [ SIOR> LITN ] C! ( c )
|
|
||||||
;
|
|
||||||
: (emit)
|
|
||||||
( As long at CTL bit 2 is low, we are transmitting. wait )
|
|
||||||
BEGIN [ SIO_ACTL LITN ] PC@ 0x04 AND UNTIL
|
|
||||||
( The way is clear, go! )
|
|
||||||
[ SIO_ADATA LITN ] PC!
|
|
||||||
;
|
|
||||||
( ----- 610 )
|
|
||||||
: _ [ SIO_ACTL LITN ] PC! ;
|
|
||||||
: SIO$
|
|
||||||
H@ [ SIO( LITN ] ! 0 [ SIOR> LITN ] C!
|
|
||||||
1 [ SIOW> LITN ] C! ( write index starts one pos later )
|
|
||||||
[ SIO_BUFSZ LITN ] ALLOT
|
|
||||||
0x18 _ ( CMD3 )
|
|
||||||
0x24 _ ( CMD2/PTR4 ) 0b11000100 _ ( WR4/64x/1stop/nopar )
|
|
||||||
0x03 _ ( PTR3 ) 0b11000001 _ ( WR3/RXen/8char )
|
|
||||||
0x05 _ ( PTR5 ) 0b01101000 _ ( WR5/TXen/8char )
|
|
||||||
0x21 _ ( CMD2/PTR1 ) 0b00011000 _ ( WR1/Rx INT all chars )
|
|
||||||
(im1)
|
|
||||||
;
|
|
||||||
( ----- 619 )
|
( ----- 619 )
|
||||||
( RC2014 classic with ACIA )
|
( RC2014 classic with MC6850 )
|
||||||
0xff00 CONSTANT RS_ADDR 0xfffa CONSTANT PS_ADDR
|
0xff00 CONSTANT RS_ADDR 0xfffa CONSTANT PS_ADDR
|
||||||
RS_ADDR 0x80 - CONSTANT SYSVARS
|
RS_ADDR 0x80 - CONSTANT SYSVARS
|
||||||
0x8000 CONSTANT HERESTART
|
0x8000 CONSTANT HERESTART
|
||||||
|
0x80 CONSTANT 6850_CTL 0x81 CONSTANT 6850_IO
|
||||||
4 CONSTANT SPI_DATA 5 CONSTANT SPI_CTL 1 CONSTANT SDC_DEVID
|
4 CONSTANT SPI_DATA 5 CONSTANT SPI_CTL 1 CONSTANT SDC_DEVID
|
||||||
602 LOAD ( acia decl )
|
|
||||||
5 LOAD ( z80 assembler )
|
5 LOAD ( z80 assembler )
|
||||||
262 LOAD ( xcomp ) 282 LOAD ( boot.z80.decl )
|
262 LOAD ( xcomp ) 282 LOAD ( boot.z80.decl )
|
||||||
270 LOAD ( xcomp overrides ) 283 335 LOADR ( boot.z80 )
|
270 LOAD ( xcomp overrides ) 283 335 LOADR ( boot.z80 )
|
||||||
353 LOAD ( xcomp core low ) 603 605 LOADR ( acia )
|
353 LOAD ( xcomp core low ) 601 603 LOADR ( MC6850 )
|
||||||
419 LOAD ( SPI relay ) 423 436 LOADR ( SD Card )
|
419 LOAD ( SPI relay ) 423 436 LOADR ( SD Card )
|
||||||
400 LOAD ( AT28 )
|
400 LOAD ( AT28 )
|
||||||
390 LOAD ( xcomp core high )
|
390 LOAD ( xcomp core high )
|
||||||
(entry) _
|
(entry) _
|
||||||
PC ORG @ 8 + ! ( Update LATEST )
|
PC ORG @ 8 + ! ( Update LATEST )
|
||||||
," ACIA$ BLK$ " EOT,
|
," 6850$ BLK$ " EOT,
|
||||||
( ----- 620 )
|
( ----- 620 )
|
||||||
( RC2014 classic with SIO )
|
( RC2014 classic with SIO )
|
||||||
0xff00 CONSTANT RS_ADDR 0xfffa CONSTANT PS_ADDR
|
0xff00 CONSTANT RS_ADDR 0xfffa CONSTANT PS_ADDR
|
||||||
RS_ADDR 0x80 - CONSTANT SYSVARS
|
RS_ADDR 0x80 - CONSTANT SYSVARS
|
||||||
0x8000 CONSTANT HERESTART
|
0x8000 CONSTANT HERESTART
|
||||||
|
0x80 CONSTANT SIOA_CTL 0x81 CONSTANT SIOA_DATA
|
||||||
|
0x82 CONSTANT SIOB_CTL 0x83 CONSTANT SIOB_DATA
|
||||||
4 CONSTANT SPI_DATA 5 CONSTANT SPI_CTL 1 CONSTANT SDC_DEVID
|
4 CONSTANT SPI_DATA 5 CONSTANT SPI_CTL 1 CONSTANT SDC_DEVID
|
||||||
607 LOAD ( SIO decl )
|
|
||||||
5 LOAD ( z80 assembler )
|
5 LOAD ( z80 assembler )
|
||||||
262 LOAD ( xcomp ) 282 LOAD ( boot.z80.decl )
|
262 LOAD ( xcomp ) 282 LOAD ( boot.z80.decl )
|
||||||
270 LOAD ( xcomp overrides ) 283 335 LOADR ( boot.z80 )
|
270 LOAD ( xcomp overrides ) 283 335 LOADR ( boot.z80 )
|
||||||
353 LOAD ( xcomp core low ) 608 610 LOADR ( SIO )
|
353 LOAD ( xcomp core low ) 605 607 LOADR ( SIO )
|
||||||
419 LOAD ( SPI relay ) 423 436 LOADR ( SD Card )
|
419 LOAD ( SPI relay ) 423 436 LOADR ( SD Card )
|
||||||
400 LOAD ( AT28 )
|
400 LOAD ( AT28 ) : (key) SIOB< ; : (emit) SIOB> ;
|
||||||
390 LOAD ( xcomp core high )
|
390 LOAD ( xcomp core high )
|
||||||
(entry) _
|
(entry) _ PC ORG @ 8 + ! ( Update LATEST )
|
||||||
PC ORG @ 8 + ! ( Update LATEST )
|
," SIOB$ BLK$ " EOT,
|
||||||
," SIO$ BLK$ " EOT,
|
|
||||||
|
30
doc/hw/acia.txt
Normal file
30
doc/hw/acia.txt
Normal file
@ -0,0 +1,30 @@
|
|||||||
|
# Asynchronous Communications Interface Adapters
|
||||||
|
|
||||||
|
Machines talking to each other is generally useful and they
|
||||||
|
often use ACIA devices to do so. Collapse OS has drivers for
|
||||||
|
a few chips of this type and they all have a similar approach:
|
||||||
|
unbuffered communication using RTS/CTS handshaking as flow con-
|
||||||
|
trol.
|
||||||
|
|
||||||
|
The reason for being unbuffered is simplicity and RAM. The logic
|
||||||
|
to implement input buffering is non-trivial and, alone, doesn't
|
||||||
|
buy us much in terms of reliability: you still have to signal
|
||||||
|
the other side when your buffer is nearly full.
|
||||||
|
|
||||||
|
Because we don't really need speed, we adopt a one-byte-at-once
|
||||||
|
approach: The RTS flag is always high (signalling that it's not
|
||||||
|
ready for communication) *except* when calling the ACIA driver's
|
||||||
|
"read" word, which is blocking.
|
||||||
|
|
||||||
|
That "read" word will pull RTS low, wait for a byte, then pull
|
||||||
|
it high again.
|
||||||
|
|
||||||
|
This slows down communication, but it's simple and reliable.
|
||||||
|
|
||||||
|
Note that this doesn't help making communications with modern
|
||||||
|
systems (which are much faster than a typical Collapse OS
|
||||||
|
machine and have their buffer output faster than the RTS flag
|
||||||
|
can be raised) very much. We have to take extra care, when
|
||||||
|
communicating from modern system, not to send too much data too
|
||||||
|
fast. But for COS-to-COS communication, this simple system
|
||||||
|
works.
|
@ -1,3 +1,4 @@
|
|||||||
|
#include <stdio.h>
|
||||||
#include "acia.h"
|
#include "acia.h"
|
||||||
|
|
||||||
static void _check_irq(ACIA *acia)
|
static void _check_irq(ACIA *acia)
|
||||||
@ -49,6 +50,10 @@ uint8_t acia_read(ACIA *acia)
|
|||||||
|
|
||||||
void acia_write(ACIA *acia, uint8_t val)
|
void acia_write(ACIA *acia, uint8_t val)
|
||||||
{
|
{
|
||||||
|
if (acia->control & 0x40) { // RTS high
|
||||||
|
fprintf(stderr, "ACIA RTS high: can't send byte\n");
|
||||||
|
return;
|
||||||
|
}
|
||||||
acia->status |= 0x01; // RDRF high
|
acia->status |= 0x01; // RDRF high
|
||||||
acia->rx = val;
|
acia->rx = val;
|
||||||
_check_irq(acia);
|
_check_irq(acia);
|
||||||
|
Loading…
Reference in New Issue
Block a user