; ;================================================================================================== ; DUART DRIVER (SERIAL PORT) ;================================================================================================== ; ; SETUP PARAMETER WORD: ; +-------+---+-------------------+ +---+---+-----------+---+-------+ ; | |RTS| ENCODED BAUD RATE | |DTR|XON| PARITY |STP| 8/7/6 | ; +-------+---+---+---------------+ ----+---+-----------+---+-------+ ; F E D C B A 9 8 7 6 5 4 3 2 1 0 ; -- MSB (D REGISTER) -- -- LSB (E REGISTER) -- ; ; MODE REGISTER 1 ; ; D7 D6 D5 D4 D3 D2 D1 D0 ; +-------+-------+-------+-------+-------+-------+-------+-------+ ; | RXRTS | RXINT | EMODE | PARITY MODE | SEL | BITS/CHAR | ; +-------+-------+-------+-------+-------+-------+-------+-------+ ; ; RXRTS: AUTOMATIC CONTROL OF /RTS BY RECEIVER ; 0 = NO ; 1 = YES ; RXINT: RECEIVE INTERRUPT SELECT ; 0 = RXRDY ; 1 = FFULL ; EMODE: ERROR MODE ; 0 = BY CHARACTER ; 1 = BY BLOCK ; PARITY MODE: ; 00 = WITH PARITY ; 01 = FORCE PARITY ; 10 = NO PARITY ; 11 = MULTIDROP MODE ; SEL: PARITY TYPE ; 0 = EVEN / SPACE ; 1 = ODD / MARK ; BITS/CHAR: ; 00 = 5 ; 01 = 6 ; 10 = 7 ; 11 = 8 ; ; MODE REGISTER 2 ; ; D7 D6 D5 D4 D3 D2 D1 D0 ; +-------+-------+-------+-------+-------+-------+-------+-------+ ; | CHANNEL MODE | TXRTS | TXCTS | STOP BIT LENGTH | ; +-------+-------+-------+-------+-------+-------+-------+-------+ ; ; CHANNEL MODE: ; 00 = NORMAL ; 01 = AUTO-ECHO ; 10 = LOCAL LOOP ; 11 = REMOTE LOOP ; TXRTS: AUTOMATIC CONTROL OF /RTS BY TRANSMITTER ; 0 = NO ; 1 = YES ; TXCTS: AUTOMATIC CONTROL OF TRANSMITTER BY /CTS ; 0 = NO ; 1 = YES ; STOP BIT LENGTH: ; 0 = 9/16 ; 1 = 10/16 = 5/8 ; 2 = 11/16 ; 3 = 12/16 = 3/4 ; 4 = 13/16 ; 5 = 14/16 = 7/8 ; 6 = 15/16 ; 7 = 16/16 = 1 ; 8-F = LENGTHS OF 0-7 PLUS ONE ; IF BITS/CHAR = 5 THEN ADD AN ADDITIONAL HALF BIT ; DUART_DEBUG .EQU FALSE ; DUART_NONE .EQU 0 ; UNKNOWN OR NOT PRESENT DUART_2681 .EQU 1 ; OLD '681 WITHOUT IVR/GPR DUART_26C92 .EQU 2 ; '92 WITH MR0 DUART_XR88C681 .EQU 3 ; EXAR/MAXLINEAR CHIP WITH Z-MODE ; DUART_BAUD_INV .EQU $FF ; INVALID BAUD RATE DUART_BAUD_ACR7 .EQU %10000000 ; ACR BIT 7 = 1 DUART_BAUD_X1 .EQU %01000000 ; BRG EXTEND BIT = 1 ('681) DUART_BAUD_EXT1 .EQU %00100000 ; EXTENDED TABLE 1 ('92) DUART_BAUD_EXT2 .EQU %00010000 ; EXTENDED TABLE 2 ('92) ; ; PER CHANNEL REGISTERS (CHANNEL A AT OFFSET 0, CHANNEL B AT OFFSET 8) ; DUART_MR .EQU $00 ; MODE REGISTER (R/W) DUART_SR .EQU $01 ; STATUS REGISTER (READ) DUART_CSR .EQU $01 ; CLOCK SELECT REGISTER (WRITE) DUART_CR .EQU $02 ; COMMAND REGISTER (WRITE) DUART_RX .EQU $03 ; RECEIVER HOLDING REGISTER (READ) DUART_TX .EQU $03 ; TRANSMITTER HOLDING REGISTER (WRITE) ; ; PER CHIP REGISTERS ; DUART_IPCR .EQU $04 ; INPUT PORT CHANGE REGISTER (READ) DUART_ACR .EQU $04 ; AUXILLIARY CONTROL REGISTER (WRITE) DUART_ISR .EQU $05 ; INTERRUPT STATUS REGISTER (READ) DUART_IMR .EQU $05 ; INTERRUPT MASK REGISTER (WRITE) DUART_CTU .EQU $06 ; COUNTER/TIMER UPPER BYTE REGISTER (R/W) DUART_CTL .EQU $07 ; COUNTER/TIMER LOWER BYTE REGISTER (R/W) DUART_GPR .EQU $0C ; GENERAL PURPOSE REGISTER (R/W) DUART_IVR .EQU $0C ; INTERRUPT VECTOR REGISTER (R/W) DUART_IPR .EQU $0D ; INPUT PORT REGISTER (READ) DUART_OPCR .EQU $0D ; OUTPUT PORT CONFIGURATION REGISTER (WRITE) DUART_STCR .EQU $0E ; START COUNTER/TIMER COMMAND (READ) DUART_SOPR .EQU $0E ; SET OUTPUT PORT REGISTER (WRITE) DUART_SPCR .EQU $0F ; STOP COUNTER/TIMER COMMAND (READ) DUART_ROPR .EQU $0F ; RESET OUTPUT PORT REGISTER (WRITE) ; ; COMMAND REGISTER ; DUART_CR_ENA_RX .EQU %00000100 ; ENABLE RECEIVER DUART_CR_DIS_RX .EQU %00001000 ; DISABLE RECEIVER DUART_CR_ENA_TX .EQU %00000001 ; ENABLE TRANSMITTER DUART_CR_DIS_TX .EQU %00000010 ; DISABLE TRANSMITTER DUART_CR_NOP .EQU $00 ; NULL COMMAND DUART_CR_MR1 .EQU $10 ; RESET MR POINTER TO MR1 DUART_CR_RESET_RX .EQU $20 ; RESET RECEIVER DUART_CR_RESET_TX .EQU $30 ; RESET TRANSMITTER DUART_CR_RESET_ERR .EQU $40 ; RESET ERROR STATUS DUART_CR_RESET_BRK .EQU $50 ; RESET BREAK STATUS DUART_CR_START_BRK .EQU $60 ; START BREAK DUART_CR_STOP_BRK .EQU $70 ; STOP BREAK DUART_CR_SET_RX_X .EQU $80 ; SET RECEIVER BRG EXTEND BIT (X=1) DUART_CR_CLR_RX_X .EQU $90 ; CLEAR RECEIVER BRG EXTEND BIT (X=0) DUART_CR_SET_TX_X .EQU $A0 ; SET TRANSMITTER BRG EXTEND BIT (X=1) DUART_CR_CLR_TX_X .EQU $B0 ; CLEAR TRANSMITTER BRG EXTEND BIT (X=0) DUART_CR_MR0 .EQU $B0 ; RESET MR POINTER TO MR0 (26C92 ONLY) DUART_CR_STANDBY .EQU $C0 ; SET STANDBY MODE (CHANNEL A ONLY) DUART_CR_RESET_IUS .EQU $C0 ; RESET IUS LATCH (CHANNEL B ONLY) DUART_CR_ACTIVE .EQU $D0 ; SET ACTIVE MODE (CHANNEL A ONLY) DUART_CR_ZMODE .EQU $D0 ; SET Z-MODE (CHANNEL B ONLY) ; ; DUART STATUS REGISTER ; DUART_SR_RXRDY .EQU %00000001 ; RECEIVER READY DUART_SR_RXFULL .EQU %00000010 ; RECEIVE FIFO FULL DUART_SR_TXRDY .EQU %00000100 ; TRANSMITTER READY DUART_SR_TXEMPTY .EQU %00001000 ; TRANSMITTER FIFO EMPTY DUART_SR_OVERRUN .EQU %00010000 ; OVERRUN ERROR DUART_SR_PARITY .EQU %00100000 ; PARITY ERROR DUART_SR_FRAMING .EQU %01000000 ; FRAMING ERROR DUART_SR_BREAK .EQU %10000000 ; RECEIVED BREAK ; ; DUART MODE REGISTER 0 ; DUART_MR0_NORMAL .EQU %00000000 ; NORMAL BAUD RATE TABLE DUART_MR0_EXT1 .EQU %00000001 ; EXTENDED BAUD RATE TABLE 1 DUART_MR0_EXT2 .EQU %00000100 ; EXTENDED BAUD RATE TABLE 2 ; ; DUART MODE REGISTER 1 ; DUART_MR1_RXRTS .EQU %10000000 ; RECEIVER CONTROLS RTS DUART_MR1_PARNONE .EQU %00010000 ; NO PARITY DUART_MR1_PARODD .EQU %00000100 ; ODD PARITY DUART_MR1_PAREVEN .EQU %00000000 ; EVEN PARITY DUART_MR1_PARMARK .EQU %00001100 ; MARK PARITY DUART_MR1_PARSPACE .EQU %00001000 ; SPACE PARITY ; ; DUART MODE REGISTER 2 ; DUART_MR2_TXCTS .EQU %00010000 ; CTS CONTROLS TRANSMITTER DUART_MR2_STOP1 .EQU %00000111 ; 1 STOP BIT (1.5 IF 5 BITS/CHAR) DUART_MR2_STOP2 .EQU %00001111 ; 2 STOP BITS (2.5 IF 5 BITS/CHAR) ; ; #DEFINE DUART_INP(RID) CALL DUART_INP_IMP \ .DB RID #DEFINE DUART_OUTP(RID) CALL DUART_OUTP_IMP \ .DB RID ; ;-------------------------------------------------------------------------------------------------- ; HBIOS MODULE HEADER ;-------------------------------------------------------------------------------------------------- ; ORG_DUART .EQU $ ; .DW SIZ_DUART ; MODULE SIZE .DW DUART_INITPHASE ; ADR OF INIT PHASE HANDLER ; DUART_INITPHASE: ; INIT PHASE HANDLER, A=PHASE CP HB_PHASE_PREINIT ; PREINIT PHASE? JP Z,DUART_PREINIT ; DO PREINIT CP HB_PHASE_INIT ; INIT PHASE? JP Z,DUART_INIT ; DO INIT RET ; DONE ; ; ; DUART_PREINIT: ; ; SETUP THE DISPATCH TABLE ENTRIES ; LD B,DUART_CFGCNT ; LOOP CONTROL XOR A ; ZERO TO ACCUM LD (DUART_DEV),A ; CURRENT DEVICE NUMBER LD IY,DUART_CFG ; POINT TO START OF CFG TABLE DUART_PREINIT0: PUSH BC ; SAVE LOOP CONTROL CALL DUART_DETECT ; DETERMINE DUART TYPE POP BC ; RESTORE LOOP CONTROL LD (IY + 1),A ; SAVE TYPE IN CONFIG TABLE OR A ; SET FLAGS JR Z,DUART_PREINIT1 ; SKIP IT IF NOTHING FOUND ; PUSH BC ; SAVE LOOP CONTROL PUSH IY POP DE ; DE := UNIT INSTANCE TABLE ADDRESS LD BC,DUART_FNTBL ; BC := FUNCTION TABLE ADDRESS CALL NZ,CIO_ADDENT ; ADD ENTRY IF DUART FOUND, BC:DE POP BC ; RESTORE LOOP CONTROL ; DUART_PREINIT1: LD DE,DUART_CFGSIZ ; SIZE OF CFG ENTRY ADD IY,DE ; BUMP IY TO NEXT ENTRY DJNZ DUART_PREINIT0 ; LOOP UNTIL DONE ; LD B,DUART_CFGCNT ; LOOP CONTROL LD IY,DUART_CFG ; POINT TO START OF CFG TABLE DUART_PREINIT2: PUSH BC ; SAVE LOOP CONTROL CALL DUART_INITUNIT ; INITIALIZE UNIT POP BC ; RESTORE LOOP CONTROL LD DE,DUART_CFGSIZ ; SIZE OF CFG ENTRY ADD IY,DE ; BUMP IY TO NEXT ENTRY DJNZ DUART_PREINIT2 ; LOOP UNTIL DONE ; XOR A ; SIGNAL SUCCESS RET ; AND RETURN ; ; DUART INITIALIZATION ROUTINE ; DUART_INITUNIT: ; CHECK IF PORT IS PRESENT LD A,(IY + 1) ; GET TYPE FROM CONFIG TABLE OR A ; SET FLAGS RET Z ; ABORT IF NOTHING THERE ; UPDATE WORKING DUART DEVICE NUM LD HL,DUART_DEV ; POINT TO CURRENT DUART DEVICE NUM LD A,(HL) ; PUT IN ACCUM INC (HL) ; INCREMENT IT (FOR NEXT LOOP) LD (IY),A ; UDPATE UNIT NUM ; SET DEFAULT CONFIG LD DE,-1 ; LEAVE CONFIG ALONE JP DUART_INITDEV ; IMPLEMENT IT AND RETURN ; ; ; DUART_INIT: LD B,DUART_CFGCNT ; COUNT OF POSSIBLE DUART UNITS LD IY,DUART_CFG ; POINT TO START OF CFG TABLE DUART_INIT1: PUSH BC ; SAVE LOOP CONTROL LD A,(IY + 1) ; GET DUART TYPE OR A ; SET FLAGS CALL NZ,DUART_PRTCFG ; PRINT IF NOT ZERO POP BC ; RESTORE LOOP CONTROL LD DE,DUART_CFGSIZ ; SIZE OF CFG ENTRY ADD IY,DE ; BUMP IY TO NEXT ENTRY DJNZ DUART_INIT1 ; LOOP TILL DONE ; XOR A ; SIGNAL SUCCESS RET ; DONE ; ; DRIVER FUNCTION TABLE ; DUART_FNTBL: .DW DUART_IN .DW DUART_OUT .DW DUART_IST .DW DUART_OST .DW DUART_INITDEV .DW DUART_QUERY .DW DUART_DEVICE #IF (($ - DUART_FNTBL) != (CIO_FNCNT * 2)) .ECHO "*** INVALID DUART FUNCTION TABLE ***\n" #ENDIF ; ; ; DUART_IN: CALL DUART_IST ; RECEIVED CHAR READY? JR Z,DUART_IN ; LOOP IF NOT DUART_INP(DUART_RX) ; GET CHAR READ IN A LD E,A ; CHAR READ TO E XOR A ; SIGNAL SUCCESS RET ; AND DONE ; ; ; DUART_OUT: CALL DUART_OST ; READY FOR CHAR? JR Z,DUART_OUT ; LOOP IF NOT LD A,E ; GET CHAR TO SEND IN A DUART_OUTP(DUART_TX) ; SEND CHAR FROM A XOR A ; SIGNAL SUCCESS RET ; ; ; DUART_IST: DUART_INP(DUART_SR) ; GET CHANNEL STATUS REGISTER IN A AND DUART_SR_RXRDY ; ISOLATE RXRDY BIT JP Z,CIO_IDLE ; NOT READY, RETURN VIA IDLE PROCESSING XOR A ; ZERO ACCUM INC A ; ACCUM := 1 TO SIGNAL 1 CHAR WAITING RET ; DONE ; ; ; DUART_OST: DUART_INP(DUART_SR) ; GET CHANNEL STATUS REGISTER IN A AND DUART_SR_TXRDY ; ISOLATE TXRDY BIT JP Z,CIO_IDLE ; NOT READY, RETURN VIA IDLE PROCESSING XOR A ; ZERO ACCUM INC A ; ACCUM := 1 TO SIGNAL 1 BUFFER POSITION RET ; DONE ; ; ; DUART_INITDEV: ; TEST FOR -1 WHICH MEANS USE CURRENT CONFIG (JUST REINIT) LD A,D ; TEST DE FOR AND E ; ... VALUE OF -1 INC A ; ... SO Z SET IF -1 JR NZ,DUART_INITDEV1 ; IF DE == -1, REINIT CURRENT CONFIG ; ; LOAD EXISTING CONFIG TO REINIT LD E,(IY + 8) ; LOW BYTE LD D,(IY + 9) ; HIGH BYTE ; DUART_INITDEV1: ; GET CLOCK SELECT FROM TABLE LD HL,DUART_BAUDTBL_681 ; GET START OF XR88C681 TABLE IN HL LD A,(IY + 1) ; GET DUART TYPE CP DUART_26C92 ; IS IT A 26C92? JR NZ,DUART_INITDEV1A ; NO, SKIP NEXT INSTRUCTION LD HL,DUART_BAUDTBL_92 ; GET START OF SC26C92 TABLE IN HL ; DUART_INITDEV1A: LD A,D ; GET CONFIG MSB AND $1F ; ISOLATE ENCODED BAUD RATE CALL ADDHLA ; HL -> ENTRY LD A,(HL) ; A = ENTRY INC A ; A = $FF? JP Z,DUART_INITDEVZ ; INVALID RATE, ERROR OUT DEC A ; GET ORIGINAL VALUE BACK ; ; GOT A VALID RATE, COMMIT NEW CONFIG LD (IY + 8),E ; SAVE LOW WORD LD (IY + 9),D ; SAVE HI WORD ; ; START OF ACTUAL DUART CHANNEL CONFIGURATION LD L,A ; SAVE BAUD TABLE ENTRY IN L LD A,DUART_CR_DIS_RX | DUART_CR_DIS_TX DUART_OUTP(DUART_CR) ; DISABLE RECEIVER AND TRANSMITTER LD A,DUART_CR_RESET_RX DUART_OUTP(DUART_CR) ; RESET RECEIVER LD A,DUART_CR_RESET_TX DUART_OUTP(DUART_CR) ; RESET TRANSMITTER LD A,DUART_CR_RESET_ERR DUART_OUTP(DUART_CR) ; RESET ERROR STATUS LD A,(IY + 1) ; GET DUART TYPE CP DUART_26C92 ; IS IT A 26C92? JR Z,DUART_INITDEV1B ; YES CALL DUART_SETBAUD_681 ; NO, CALL '681 BRG SETUP JR DUART_INITDEV2 ; DUART_INITDEV1B: CALL DUART_SETBAUD_92 ; CALL '92 BRG SETUP ; DUART_INITDEV2: ; ; SET PARITY AND WORD SIZE LD A,DUART_CR_MR1 DUART_OUTP(DUART_CR) ; SET MR POINTER TO MR1 LD A,E ; GET LOW WORD OF CONFIG IN A AND %00111000 ; KEEP ONLY PARITY BITS RRA RRA RRA ; SHIFT PARITY BITS INTO AN INDEX LD HL,DUART_PARTBL ; GET START OF TABLE IN HL CALL ADDHLA ; HL -> ENTRY LD B,(HL) ; BUILD MR1 IN B LD A,E ; GET LOW WORD OF CONFIG IN A AND %00000011 ; WORD LENGTH BITS ARE THE SAME OR B ; MERGE PARITY BITS OR DUART_MR1_RXRTS ; ALWAYS ENABLE RECEIVER CONTROL OF RTS DUART_OUTP(DUART_MR) ; WRITE MR1 (AND SET MR POINTER TO MR2) ; ; SET STOP BITS LD A,E ; GET LOW WORD OF CONFIG IN A LD B,DUART_MR2_STOP1 ; BUILD MR2 IN B AND %00000100 ; KEEP ONLY STOP BITS JR Z,DUART_INITDEV4 ; 1 STOP BIT LD B,DUART_MR2_STOP2 ; 2 STOP BITS, REPLACE B ; DUART_INITDEV4: LD A,B ; GET MR2 IN A ;OR DUART_MR2_TXCTS ; ALWAYS ENABLE CTS CONTROL OF TRANSMITTER DUART_OUTP(DUART_MR) ; WRITE MR2 ; ; RE-ENABLE RECEIVER AND TRANSMITTER LD A,DUART_CR_ENA_RX | DUART_CR_ENA_TX DUART_OUTP(DUART_CR) ; ENABLE RECEIVER AND TRANSMITTER ; ; EXPLICITLY ASSERT RTS (SEEMS TO BE REQUIRED FOR SOME CHIPS TO DO AUTO-RTS) LD L,%00000001 ; RTS FOR CHANNEL A IS IN BIT 0 LD A,(IY) ; GET UNIT NUMBER IN A AND L ; MASK ALL BUT CHANNEL JR Z,DUART_INITDEV5 ; ZERO INDICATES CHANNEL A SLA L ; MOVE INTO BIT 1, RTS FOR CHANNEL B ; DUART_INITDEV5: LD A,(IY + 2) ; GET BASE ADDRESS OF CHIP ADD A,DUART_SOPR ; SET OUTPUT BITS LD C,A ; GET PORT IN C OUT (C),L ; OUTPUT PORT IS INVERTED BUT SO IS RTS ; XOR A ; SIGNAL SUCCESS RET ; DUART_INITDEVZ: ; ; INVALID BAUD RATE DEC A ; A WAS $00, GET BACK $FF RET ; RETURN ERROR STATUS ; ; INITIALIZE BRG FOR '681 DUART ; DUART_SETBAUD_681: ; SET ACR LD C,(IY + 6) ; GET SHADOW ACR FOR THIS CHIP LD B,(IY + 7) ; BC IS POINTER LD A,(BC) ; GET SHADOW ACR IN A AND %01111111 ; MASK OUT BIT 7 LD H,A ; SAVE IT IN H LD A,L ; TABLE ENTRY IS IN L, GET IT IN A AND DUART_BAUD_ACR7 ; SEE IF ACR[7] SHOULD BE SET (BIT MASK SHOULD ACTUALLY _BE_ BIT 7) OR H ; MERGE IN REST OF ACR LD H,A ; SAVE IT IN H LD A,(IY + 2) ; GET CHIP BASE IN A ADD A,DUART_ACR ; ADD OFFSET OF ACR LD C,A ; C = ACR PORT ; YES, THIS OVERWRITES ACR[7] REGARDLESS OF THE OTHER CHANNEL, ; BUT CURRENTLY THE TABLE IS SET SO EVERY VALID RATE HAS ACR[7] SET OUT (C),H ; WRITE VALUE ; SELECT PER-CHANNEL EXTENDED TABLE LD A,L ; CALLED WITH TABLE ENTRY IN L, MOVE IT TO A AND DUART_BAUD_X1 ; SEE IF SELECT EXTEND BIT SHOULD BE SET JR Z,DUART_SETBAUD_681A ; NO, CLEAR IT LD A,DUART_CR_SET_RX_X ; YES, SET EXTEND BIT DUART_OUTP(DUART_CR) ; SET FOR RECEIVER LD A,DUART_CR_SET_TX_X DUART_OUTP(DUART_CR) ; SET FOR TRANSMITTER JR DUART_SETBAUD_681B ; DUART_SETBAUD_681A: ; CLEAR EXTEND BIT LD A,DUART_CR_CLR_RX_X DUART_OUTP(DUART_CR) ; CLEAR FOR RECEIVER LD A,DUART_CR_CLR_TX_X DUART_OUTP(DUART_CR) ; CLEAR FOR TRANSMITTER ; DUART_SETBAUD_681B: ; SET BRG CLOCK SELECT LD A,L ; GET BAUD TABLE ENTRY IN A AND $0F ; GET CLOCK SELECT BITS LD L,A ; SAVE IT IN L RLA RLA RLA RLA ; MOVE IT INTO THE HIGH NIBBLE OR L ; AND MERGE BACK IN LOW NIBBLE DUART_OUTP(DUART_CSR) ; SET CLOCK SELECT RET ; DUART_BAUDTBL_681: ; ASSUME XR88C681 RUNS AT 3.6864MHZ .DB %0000 | DUART_BAUD_X1 ; 75 .DB %0011 | DUART_BAUD_X1 ; 150 .DB %0100 ; 300 .DB %0101 ; 600 .DB %0110 ; 1200 .DB %1000 ; 2400 .DB %1001 ; 4800 .DB %1011 ; 9600 .DB %1100 | DUART_BAUD_X1 ; 19200 .DB %1100 ; 38400 .DB DUART_BAUD_INV ; 76800 .DB DUART_BAUD_INV ; 153600 .DB DUART_BAUD_INV ; 307200 .DB DUART_BAUD_INV ; 614400 .DB DUART_BAUD_INV ; 1228800 .DB DUART_BAUD_INV ; 2457600 .DB DUART_BAUD_INV ; 225 .DB DUART_BAUD_INV ; 450 .DB DUART_BAUD_INV ; 900 .DB %1010 | DUART_BAUD_X1 ; 1800 .DB %0100 | DUART_BAUD_X1 ; 3600 .DB %1010 ; 7200 .DB %0101 | DUART_BAUD_X1 ; 14400 .DB %0110 | DUART_BAUD_X1 ; 28800 .DB %0111 | DUART_BAUD_X1 ; 57600 .DB %1000 | DUART_BAUD_X1 ; 115200 .DB DUART_BAUD_INV ; 230400 .DB DUART_BAUD_INV ; 460800 .DB DUART_BAUD_INV ; 921600 .DB DUART_BAUD_INV ; 1843200 .DB DUART_BAUD_INV ; 3686400 .DB DUART_BAUD_INV ; 7372800 ; ; INITIALIZE BRG FOR '92 DUART ; DUART_SETBAUD_92: ; SET ACR LD C,(IY + 6) ; GET SHADOW ACR FOR THIS CHIP LD B,(IY + 7) ; BC IS POINTER LD A,(BC) ; GET SHADOW ACR IN A AND %01111111 ; MASK OUT BIT 7 LD H,A ; SAVE IT IN H LD A,L ; TABLE ENTRY IS IN L, GET IT IN A AND DUART_BAUD_ACR7 ; SEE IF ACR[7] SHOULD BE SET (BIT MASK SHOULD ACTUALLY _BE_ BIT 7) OR H ; MERGE IN REST OF ACR LD H,A ; SAVE IT IN H LD A,(IY + 2) ; GET CHIP BASE IN A ADD A,DUART_ACR ; ADD OFFSET OF ACR LD C,A ; C = ACR PORT ; YES, THIS OVERWRITES ACR[7] REGARDLESS OF THE OTHER CHANNEL, ; BUT CURRENTLY THE TABLE IS SET SO EVERY VALID RATE HAS ACR[7] SET OUT (C),H ; WRITE VALUE ; SELECT NORMAL OR EXTENDED BAUD RATE TABLES LD H,DUART_MR0_NORMAL ; ASSUME NORMAL LD A,L ; GET TABLE ENTRY IN A AGAIN AND DUART_BAUD_EXT1 ; SHOULD EXT1 BE SET? JR Z,DUART_SETBAUD_92A ; NO, CHECK NEXT VALUE LD H,DUART_MR0_EXT1 ; YES, SET IT JR DUART_SETBAUD_92C ; DUART_SETBAUD_92A: LD A,L ; GET TABLE ENTRY IN A ONCE MORE AND DUART_BAUD_EXT2 ; SHOULD EXT2 BE SET? JR Z,DUART_SETBAUD_92C ; NO, CONTINUE LD H,DUART_MR0_EXT2 ; YES, SET IT ; DUART_SETBAUD_92C: ; H NOW CONTAINS MR0 LD A,(IY + 2) ; GET CHIP BASE IN A ADD A,DUART_CR ; WE WANT TO WRITE THE COMMAND REGISTER OF CHANNEL A, EVEN IF WE'RE CHANNEL B LD C,A ; C = CRA LD A,DUART_CR_MR0 ; RESET MR POINTER TO MR0 OUT (C),A ; WRITE COMMAND LD A,(IY + 2) ; GET CHIP BASE IN A ADD A,DUART_MR ; NOW WE WANT TO WRITE TO MR0 OF CHANNEL A LD C,A ; C = MRA ; AS WITH ACR[7] THE TABLE IS SET SO EVERY VALID RATE IS FROM ; THE SAME TABLE OUT (C),H ; SET BRG CLOCK SELECT LD A,L ; GET BAUD TABLE ENTRY IN A YET AGAIN AND $0F ; GET CLOCK SELECT BITS LD L,A ; SAVE IT IN L RLA RLA RLA RLA ; MOVE IT INTO THE HIGH NIBBLE OR L ; AND MERGE BACK IN LOW NIBBLE DUART_OUTP(DUART_CSR) ; SET CLOCK SELECT OF CURRENT CHANNEL RET ; DUART_BAUDTBL_92: ; ASSUME SC26C92 RUNS AT 7.3728MHZ .DB DUART_BAUD_INV ; 75 .DB DUART_BAUD_INV ; 150 .DB DUART_BAUD_INV ; 300 .DB DUART_BAUD_INV ; 600 .DB DUART_BAUD_INV ; 1200 .DB DUART_BAUD_INV ; 2400 .DB DUART_BAUD_INV ; 4800 .DB %1001 | DUART_BAUD_EXT2 | DUART_BAUD_ACR7 ; 9600 .DB %1011 | DUART_BAUD_EXT2 | DUART_BAUD_ACR7 ; 19200 .DB %1100 | DUART_BAUD_EXT2 | DUART_BAUD_ACR7 ; 38400 .DB DUART_BAUD_INV ; 76800 .DB DUART_BAUD_INV ; 153600 .DB DUART_BAUD_INV ; 307200 .DB DUART_BAUD_INV ; 614400 .DB DUART_BAUD_INV ; 1228800 .DB DUART_BAUD_INV ; 2457600 .DB DUART_BAUD_INV ; 225 .DB DUART_BAUD_INV ; 450 .DB DUART_BAUD_INV ; 900 .DB DUART_BAUD_INV ; 1800 .DB DUART_BAUD_INV ; 3600 .DB DUART_BAUD_INV ; 7200 .DB %0000 | DUART_BAUD_EXT2 | DUART_BAUD_ACR7 ; 14400 .DB %0011 | DUART_BAUD_EXT2 | DUART_BAUD_ACR7 ; 28800 .DB %0100 | DUART_BAUD_EXT2 | DUART_BAUD_ACR7 ; 57600 .DB %0101 | DUART_BAUD_EXT2 | DUART_BAUD_ACR7 ; 115200 .DB %0110 | DUART_BAUD_EXT2 | DUART_BAUD_ACR7 ; 230400 .DB DUART_BAUD_INV ; 460800 .DB DUART_BAUD_INV ; 921600 .DB DUART_BAUD_INV ; 1843200 .DB DUART_BAUD_INV ; 3686400 .DB DUART_BAUD_INV ; 7372800 ; DUART_PARTBL: .DB DUART_MR1_PARNONE ; 0 = NO PARITY (ALSO ALL EVEN ENTRIES) .DB DUART_MR1_PARODD ; 1 = ODD PARITY .DB DUART_MR1_PARNONE .DB DUART_MR1_PAREVEN ; 3 = EVEN PARITY .DB DUART_MR1_PARNONE .DB DUART_MR1_PARMARK ; 5 = MARK PARITY .DB DUART_MR1_PARNONE .DB DUART_MR1_PARSPACE ; 7 = SPACE PARITY ; ; ; DUART_QUERY: LD E,(IY + 8) ; FIRST CONFIG BYTE TO E LD D,(IY + 9) ; SECOND CONFIG BYTE TO D XOR A ; SIGNAL SUCCESS RET ; DONE ; ; ; DUART_DEVICE: LD D,CIODEV_DUART ; D := DEVICE TYPE LD E,(IY) ; E := PHYSICAL UNIT LD C,$00 ; C := DEVICE TYPE, 0x00 IS RS-232 LD H,0 ; H := 0, DRIVER HAS NO MODES LD L,(IY+3) ; L := BASE I/O ADDRESS XOR A ; SIGNAL SUCCESS RET ; ; DUART DETECTION ROUTINE ; DUART_DETECT: ; ; FIRST SEE IF IT LOOKS LIKE A 16X50-STYLE UART LD A,(IY + 2) ; GET BASE PORT OF CHIP ADD A,4 ; BASE + 4 = ACR (DUART), MCR (1ST 16X50) LD H,A ; H := ACR/MCR PORT ADDRESS ADD A,2 ; BASE + 6 = CTU (DUART), MSR (1ST 16X50) LD B,A ; B := CTU/MSR PORT ADDRESS INC A ; BASE + 7 = CTL (DUART), SCR (1ST 16X50) LD D,A ; D := CTL/SCR PORT ADDRESS ADD A,7 ; BASE + 14 = STCR (DUART), MSR (2ND 16X50) LD E,A ; E := STCR/MSR PORT ADDRESS INC A ; BASE + 15 = SPCR (DUART), SCR (2ND 16X50) LD C,A ; SPCR IN A,(C) ; STOP COUNTER/TIMER, JUST IN CASE LD C,H ; ACR/MCR IN L,(C) ; GET ORIGINAL VALUE (ACTUALLY IPCR ON DUART) IN L LD A,$30 ; ENABLE A SOURCE FOR THE COUNTER/TIMER OUT (C),A ; WRITE TO ACR/MCR LD A,$A5 ; TEST VALUE LD C,B ; CTU OUT (C),A ; WRITE TEST VALUE TO CTU LD A,$FF ; LARGE VALUE TO PREVENT CTL FROM ROLLING OVER WHILE WE TEST LD C,D ; CTL OUT (C),A ; WRITE LARGE VALUE TO CTL LD C,E ; STCR IN A,(C) ; START COUNTER/TIMER (LATCH CTU, CTL) INC C ; C := SPCR IN A,(C) ; STOP COUNTER/TIMER LD C,H ; ACR/MCR OUT (C),L ; WRITE ORIGINAL VALUE OF MCR (ACR GETS SET ON DUART LATER) LD C,B ; CTU IN A,(C) ; READ BACK TEST VALUE CP $A5 ; CHECK FOR TEST VALUE JR NZ,DUART_DETECT_NONE ; NO, PROBABLY NOT A DUART IN A,(C) ; CHECK TEST VALUE AGAIN, CP $A5 ; ... IN RARE CASE DELTAS IN MSR WERE SET TO OUR TEST JR NZ,DUART_DETECT_NONE ; ALMOST CERTAINLY NOT A DUART ; SEE IF MR1 AND MR2 ARE DISTINCT LD A,DUART_CR_MR1 ; SET MR POINTER TO MR1 DUART_OUTP(DUART_CR) ; SEND COMMAND LD A,1 ; WRITE TEST VALUE TO MR1 DUART_OUTP(DUART_MR) ; WRITE MR1 AND SET POINTER TO MR2 XOR A ; WRITE 0 TO MR2 DUART_OUTP(DUART_MR) ; WRITE MR2 AND KEEP POINTER TO MR2 LD A,DUART_CR_MR1 ; SET MR POINTER TO MR1 (AGAIN) DUART_OUTP(DUART_CR) ; SEND COMMAND DUART_INP(DUART_MR) ; GET VALUE OF MR1 IN A CP 1 ; CHECK FOR TEST VALUE JR NZ,DUART_DETECT_NONE ; NOPE, UNKNOWN DEVICE OR NOT PRESENT ; ; TEST FOR FUNCTIONAL GENERAL PURPOSE REG, IF NOT, WE HAVE A 2681 LD A,$5A ; LOAD TEST VALUE DUART_OUTP(DUART_GPR) ; PUT IT IN GENERAL PURPOSE REGISTER DUART_INP(DUART_GPR) ; READ IT BACK CP $5A ; CHECK IT JR NZ,DUART_DETECT_2681 ; OLD CHIP ; ; TEST FOR MR0 REGISTER, IN WHICH CASE WE HAVE A 26C92 OF SOME SORT LD A,DUART_CR_MR0 ; SET MR POINTER TO MR0 DUART_OUTP(DUART_CR) ; THIS IS HARMLESS ON OTHER CHIPS LD A,1 ; WRITE TEST VALUE TO MR0 DUART_OUTP(DUART_MR) ; WRITE TO MR0 ON 26C92, MR2 STILL SET ON OTHERS LD A,DUART_CR_MR1 ; SET MR POINTER TO MR1 DUART_OUTP(DUART_CR) ; THIS WORKS ON ALL CHIPS XOR A ; WRITE 0 TO MR1 DUART_OUTP(DUART_MR) ; WRITE MR1 AND SET POINTER TO MR2 XOR A ; ALSO WRITE 0 TO MR2 DUART_OUTP(DUART_MR) ; WRITE MR2 AND KEEP POINTER TO MR2 LD A,DUART_CR_MR0 ; SET POINTER TO MR0 DUART_OUTP(DUART_CR) ; POINTER IS STILL MR2 ON OTHER CHIPS DUART_INP(DUART_MR) ; GET VALUE OF MR0 IN A AND 1 ; MASK TEST VALUE IN BIT 1 JR NZ,DUART_DETECT_26C92 ; IF IT'S SET, THIS MUST BE A '92 WITH MR0 JR DUART_DETECT_XR88C681 ; ASSUME WE HAVE A FANCY EXAR CHIP ; DUART_DETECT_NONE: LD A,DUART_NONE RET ; DUART_DETECT_2681: LD A,DUART_2681 RET ; DUART_DETECT_26C92: LD A,DUART_26C92 RET ; DUART_DETECT_XR88C681 LD A,DUART_XR88C681 RET ; ; ; DUART_PRTCFG: ; ANNOUNCE PORT CALL NEWLINE ; FORMATTING PRTS("DUART$") ; FORMATTING LD A,(IY) ; DEVICE NUM CALL PRTDECB ; PRINT DEVICE NUM PRTS(": IO=0x$") ; FORMATTING LD A,(IY + 3) ; GET CHANNEL BASE PORT CALL PRTHEXBYTE ; PRINT BASE PORT ; PRINT THE DUART TYPE CALL PC_SPACE ; FORMATTING LD A,(IY + 1) ; GET DUART TYPE BYTE RLCA ; MAKE IT A WORD OFFSET LD HL,DUART_TYPE_MAP ; POINT HL TO TYPE MAP TABLE CALL ADDHLA ; HL := ENTRY LD E,(HL) ; DEREFERENCE INC HL ; ... LD D,(HL) ; ... TO GET STRING POINTER CALL WRITESTR ; PRINT IT ; ; ALL DONE IF NO DUART WAS DETECTED LD A,(IY + 1) ; GET DUART TYPE BYTE OR A ; SET FLAGS RET Z ; IF ZERO, NOT PRESENT ; PRTS(" MODE=$") ; FORMATTING LD E,(IY + 8) ; LOAD CONFIG LD D,(IY + 9) ; ... WORD TO DE CALL PS_PRTSC0 ; PRINT CONFIG ; XOR A RET ; ; ROUTINES TO READ/WRITE PORTS INDIRECTLY ; ; READ VALUE OF DUART PORT ON TOS INTO REGISTER A ; DUART_INP_IMP: EX (SP),HL ; SWAP HL AND TOS PUSH BC ; PRESERVE BC LD A,(IY + 3) ; GET DUART IO BASE PORT OR (HL) ; OR IN REGISTER ID BITS LD C,A ; C := PORT IN A,(C) ; READ PORT INTO A POP BC ; RESTORE BC INC HL ; BUMP HL PAST REG ID PARM EX (SP),HL ; SWAP BACK HL AND TOS RET ; ; WRITE VALUE IN REGISTER A TO DUART PORT ON TOS ; DUART_OUTP_IMP: EX (SP),HL ; SWAP HL AND TOS PUSH BC ; PRESERVE BC LD B,A ; PUT VALUE TO WRITE IN B LD A,(IY + 3) ; GET DUART IO BASE PORT OR (HL) ; OR IN REGISTER ID BITS LD C,A ; C := PORT OUT (C),B ; WRITE VALUE TO PORT POP BC ; RESTORE BC INC HL ; BUMP HL PAST REG ID PARM EX (SP),HL ; SWAP BACK HL AND TOS RET ; ; ; DUART_TYPE_MAP: .DW DUART_STR_NONE .DW DUART_STR_2681 .DW DUART_STR_26C92 .DW DUART_STR_XR88C681 DUART_STR_NONE .DB "$" DUART_STR_2681 .DB "2681$" DUART_STR_26C92 .DB "26C92$" DUART_STR_XR88C681 .DB "XR88C681$" ; ; WORKING VARIABLES ; DUART_DEV .DB 0 ; DEVICE NUM USED DURING INIT ; ; PER-CHIP VARIABLES ; DUART0_ACR .DB 0 ; SHADOW ACR (DUART 0) ; #IF (DUARTCNT >= 2) ; DUART1_ACR .DB 0 ; SHADOW ACR (DUART 1) ; #ENDIF ; ; DUART PORT TABLE ; DUART_CFG: ; DUART0A_CFG: ; 1ST DUART MODULE CHANNEL A .DB 0 ; IY DEVICE NUMBER (SET DURING INIT) .DB 0 ; IY+1 DUART TYPE (SET DURING INIT) .DB DUART0BASE ; IY+2 BASE PORT (CHIP) .DB DUART0BASE + $00 ; IY+3 BASE PORT (CHANNEL) .DW DUART0B_CFG ; IY+4 POINTER TO OTHER CHANNEL .DW DUART0_ACR ; IY+6 POINTER TO SHADOW ACR FOR THIS CHIP .DW DUART0ACFG ; IY+8 LINE CONFIGURATION .DB 1 ; IY+10 MULTIPLIER WRT 3.6864MHZ CLOCK ; DEVECHO "DUART: IO=" DEVECHO DUART0BASE + $00 DEVECHO ", CHANNEL A\n" ; DUART_CFGSIZ .EQU $ - DUART_CFG ; SIZE OF ONE CFG TABLE ENTRY ; DUART0B_CFG: ; 1ST DUART MODULE CHANNEL B .DB 0 ; DEVICE NUMBER (SET DURING INIT) .DB 0 ; DUART TYPE (SET DURING INIT) .DB DUART0BASE ; BASE PORT (CHIP) .DB DUART0BASE + $08 ; BASE PORT (CHANNEL) .DW DUART0A_CFG ; POINTER TO OTHER CHANNEL .DW DUART0_ACR ; POINTER TO SHADOW ACR FOR THIS CHIP .DW DUART0BCFG ; LINE CONFIGURATION .DB 1 ; MULTIPLIER WRT 3.6864MHZ CLOCK ; DEVECHO "DUART: IO=" DEVECHO DUART0BASE + $08 DEVECHO ", CHANNEL B\n" ; #IF (DUARTCNT >= 2) ; DUART1A_CFG: ; 2ND DUART MODULE CHANNEL A .DB 0 ; DEVICE NUMBER (SET DURING INIT) .DB 0 ; DUART TYPE (SET DURING INIT) .DB DUART1BASE ; BASE PORT (CHIP) .DB DUART1BASE + $00 ; BASE PORT (CHANNEL) .DW DUART1B_CFG ; POINTER TO OTHER CHANNEL .DW DUART1_ACR ; POINTER TO SHADOW ACR FOR THIS CHIP .DW DUART1ACFG ; LINE CONFIGURATION .DB 1 ; MULTIPLIER WRT 3.6864MHZ CLOCK ; DEVECHO "DUART: IO=" DEVECHO DUART1BASE + $00 DEVECHO ", CHANNEL A\n" ; DUART1B_CFG: ; 2ND DUART MODULE CHANNEL B .DB 0 ; DEVICE NUMBER (SET DURING INIT) .DB 0 ; DUART TYPE (SET DURING INIT) .DB DUART1BASE ; BASE PORT (CHIP) .DB DUART1BASE + $08 ; BASE PORT (CHANNEL) .DW DUART1A_CFG ; POINTER TO OTHER CHANNEL .DW DUART1_ACR ; POINTER TO SHADOW ACR FOR THIS CHIP .DW DUART1BCFG ; LINE CONFIGURATION .DB 1 ; MULTIPLIER WRT 3.6864MHZ CLOCK ; DEVECHO "DUART: IO=" DEVECHO DUART1BASE + $08 DEVECHO ", CHANNEL B\n" ; #ENDIF ; DUART_CFGCNT .EQU ($ - DUART_CFG) / DUART_CFGSIZ ; ;-------------------------------------------------------------------------------------------------- ; HBIOS MODULE TRAILER ;-------------------------------------------------------------------------------------------------- ; END_DUART .EQU $ SIZ_DUART .EQU END_DUART - ORG_DUART ; MEMECHO "DUART occupies " MEMECHO SIZ_DUART MEMECHO " bytes.\n"