diff --git a/arch/z80/rc2014/blk.fs b/arch/z80/rc2014/blk.fs index 781c154..c478ee9 100644 --- a/arch/z80/rc2014/blk.fs +++ b/arch/z80/rc2014/blk.fs @@ -1,174 +1,111 @@ ( ----- 600 ) -601 ACIA 606 Zilog SIO driver +601 MC6850 driver 606 Zilog SIO driver 615 SPI relay 619 Xcomp unit ( ----- 601 ) -ACIA driver - -Manage I/O from an asynchronous communication interface adapter -(ACIA). provides "(emit)" to put c char on the ACIA as well as -an input buffer from which a provided "(key)" reads. This driver -installs an interrupt handler at RST38 to handle RX. - -To use, begin by loading declarations (B582) before xcomp is -loaded. These declarations provide default values for ports and -memory offsets that you can override. See B582. - -Then, in the driver part, load range 583-588. +( MC6850 Driver. Load range B601-B603. Requires: + 6850_CTL for control register + 6850_IO for data register. + CTL numbers used: 0x16 = no interrupt, 8bit words, 1 stop bit + 64x divide. 0x56 = RTS high ) +CODE 6850< + A 0x16 ( RTS low ) LDri, 6850_CTL OUTiA, + BEGIN, + 6850_CTL INAi, 0x01 ANDi, ( is ACIA rcv buf full? ) + JRZ, ( no, loop ) AGAIN, + A 0x56 ( RTS high ) LDri, 6850_CTL OUTiA, + ( we have data, fetch and push ) + 6850_IO INAi, PUSHA, +;CODE ( ----- 602 ) -0x80 CONSTANT ACIA_CTL ( IO port for ACIA's control register ) -0x81 CONSTANT ACIA_IO ( IO port for ACIA's data registers ) -0x20 CONSTANT ACIA_BUFSZ ( SZ-1 must be a mask ) -( Address in memory that can be used variables shared - with ACIA's native words. 4 bytes used. ) -CREATE ACIA_MEM SYSVARS 0x70 + , -( Points to ACIA buf ) -: 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. ) +CODE 6850> + HL POP, chkPS, + BEGIN, + 6850_CTL INAi, 0x02 ANDi, ( are we transmitting? ) + JRZ, ( yes, loop ) AGAIN, + A L LDrr, 6850_IO OUTiA, +;CODE ( ----- 603 ) -( ACIA INT handler, read into ACIAW> ) -( Set RST 38 jump ) PC ORG @ 0x39 + ! - AF PUSH, - 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! -; +: (key) 6850< ; +: (emit) 6850> ; +: 6850$ 0x56 ( RTS high ) [ 6850_CTL LITN ] PC! ; ( ----- 605 ) -: ACIA$ - H@ [ ACIA( LITN ] ! 0 [ ACIAR> LITN ] C! - 1 [ ACIAW> LITN ] C! ( write index starts one pos later ) - [ ACIA_BUFSZ LITN ] ALLOT -( setup ACIA - CR7 (1) - Receive Interrupt enabled - CR6:5 (00) - RTS low, transmit interrupt disabled. - CR4:2 (101) - 8 bits + 1 stop bit - CR1:0 (10) - Counter divide: 64 ) - 0b10010110 [ ACIA_CTL LITN ] PC! - (im1) ; +( Zilog SIO driver. Load range B605-607. Requires: + SIOA_CTL for ch A control register + SIOA_DATA for ch A data register + SIOB_CTL for ch B control register + SIOB_DATA for ch B data register ) +CODE SIOA< + A 0x05 ( PTR5 ) LDri, SIOA_CTL OUTiA, + A 0b01101000 ( De-assert RTS ) LDri, SIOA_CTL OUTiA, + BEGIN, + SIOA_CTL ( RR0 ) INAi, 0x01 ANDi, ( is rcv buf full? ) + 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 ) -Zilog SIO driver - -Declarations at B607 - -Driver load range at B608-B610 +CODE SIOA> + HL POP, chkPS, + BEGIN, + SIOA_CTL INAi, 0x04 ANDi, ( are we transmitting? ) + 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 ) -0x80 CONSTANT SIO_ACTL 0x81 CONSTANT SIO_ADATA -0x82 CONSTANT SIO_BCTL 0x83 CONSTANT SIO_BDATA -0x20 CONSTANT SIO_BUFSZ ( SZ-1 must be a mask ) -( Address in memory that can be used variables shared - with SIO native words. 4 bytes used. ) -CREATE SIO_MEM SYSVARS 0x70 + , -( Points to SIO buf ) -: SIO( SIO_MEM @ 2+ ; -( Read buf idx Pre-inc ) -: SIOR> SIO_MEM @ ; -( Write buf idx Post-inc ) -: SIOW> SIO_MEM @ 1+ ; -( This means that if W> == R>, buffer is full. - If R>+1 == W>, buffer is empty. ) -( ----- 608 ) -( 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) -; +CODE SIOB< + BEGIN, + SIOB_CTL ( RR0 ) INAi, 0x01 ANDi, ( is rcv buf full? ) + JRZ, ( no, loop ) AGAIN, + ( we have data, fetch and push ) + SIOB_DATA INAi, PUSHA, +;CODE +CODE SIOB> + HL POP, chkPS, + BEGIN, + SIOB_CTL INAi, 0x04 ANDi, ( are we transmitting? ) + JRZ, ( yes, loop ) AGAIN, + A L LDrr, SIOB_DATA OUTiA, +;CODE +: SIOB$ 9 0 DO _ I + C@ [ SIOB_CTL LITN ] PC! LOOP ; ( ----- 619 ) -( RC2014 classic with ACIA ) +( RC2014 classic with MC6850 ) 0xff00 CONSTANT RS_ADDR 0xfffa CONSTANT PS_ADDR RS_ADDR 0x80 - CONSTANT SYSVARS 0x8000 CONSTANT HERESTART +0x80 CONSTANT 6850_CTL 0x81 CONSTANT 6850_IO 4 CONSTANT SPI_DATA 5 CONSTANT SPI_CTL 1 CONSTANT SDC_DEVID -602 LOAD ( acia decl ) 5 LOAD ( z80 assembler ) 262 LOAD ( xcomp ) 282 LOAD ( boot.z80.decl ) 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 ) 400 LOAD ( AT28 ) 390 LOAD ( xcomp core high ) (entry) _ PC ORG @ 8 + ! ( Update LATEST ) -," ACIA$ BLK$ " EOT, +," 6850$ BLK$ " EOT, ( ----- 620 ) ( RC2014 classic with SIO ) 0xff00 CONSTANT RS_ADDR 0xfffa CONSTANT PS_ADDR RS_ADDR 0x80 - CONSTANT SYSVARS 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 -607 LOAD ( SIO decl ) 5 LOAD ( z80 assembler ) 262 LOAD ( xcomp ) 282 LOAD ( boot.z80.decl ) 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 ) -400 LOAD ( AT28 ) +400 LOAD ( AT28 ) : (key) SIOB< ; : (emit) SIOB> ; 390 LOAD ( xcomp core high ) -(entry) _ -PC ORG @ 8 + ! ( Update LATEST ) -," SIO$ BLK$ " EOT, +(entry) _ PC ORG @ 8 + ! ( Update LATEST ) +," SIOB$ BLK$ " EOT, diff --git a/doc/hw/acia.txt b/doc/hw/acia.txt new file mode 100644 index 0000000..add53d8 --- /dev/null +++ b/doc/hw/acia.txt @@ -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. diff --git a/emul/z80/acia.c b/emul/z80/acia.c index abe79de..05d0832 100644 --- a/emul/z80/acia.c +++ b/emul/z80/acia.c @@ -1,3 +1,4 @@ +#include #include "acia.h" static void _check_irq(ACIA *acia) @@ -49,6 +50,10 @@ uint8_t acia_read(ACIA *acia) 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->rx = val; _check_irq(acia);