You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

994 lines
26 KiB

;
;==================================================================================================
; SIO 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) --
;
; FOR THE ECB-ZILOG-PERIPHERALS BOARD, INFORMATION ON JUMPER SETTINGS
; AND BAUD RATES CAN BE FOUND HERE:
; https://www.retrobrewcomputers.org/doku.php?id=boards:ecb:zilog-peripherals:clock-divider
;
; SIO PORT A (COM1:) and SIO PORT B (COM2:) ARE MAPPED TO DEVICE UC1: AND UL1: IN CP/M.
;
SIO_BUFSZ .EQU 32 ; RECEIVE RING BUFFER SIZE
;
SIO_NONE .EQU 0
SIO_SIO .EQU 1
;
SIO_RTSON .EQU $EA
SIO_RTSOFF .EQU $E8
;
#IF (INTMODE == 0)
SIO_WR1VAL .EQU $00 ; WR1 VALUE FOR NO INTS
#ELSE
SIO_WR1VAL .EQU $18 ; WR1 VALUE FOR INT ON RECEIVED CHARS
#ENDIF
;
#IF (INTMODE == 2)
;
SIO0_IVT .EQU IVT(INT_SIO0)
SIO1_IVT .EQU IVT(INT_SIO1)
SIO0_VEC .EQU VEC(INT_SIO0)
SIO1_VEC .EQU VEC(INT_SIO1)
;
#ENDIF
;
#IF (SIO0MODE == SIOMODE_RC)
SIO0A_CMD .EQU SIO0BASE + $00
SIO0A_DAT .EQU SIO0BASE + $01
SIO0B_CMD .EQU SIO0BASE + $02
SIO0B_DAT .EQU SIO0BASE + $03
#ENDIF
;
#IF (SIO0MODE == SIOMODE_SMB)
SIO0A_CMD .EQU SIO0BASE + $02
SIO0A_DAT .EQU SIO0BASE + $00
SIO0B_CMD .EQU SIO0BASE + $03
SIO0B_DAT .EQU SIO0BASE + $01
#ENDIF
;
#IF (SIO0MODE == SIOMODE_ZP)
SIO0A_CMD .EQU SIO0BASE + $06
SIO0A_DAT .EQU SIO0BASE + $04
SIO0B_CMD .EQU SIO0BASE + $07
SIO0B_DAT .EQU SIO0BASE + $05
#ENDIF
;
#IF (SIO0MODE == SIOMODE_EZZ80)
SIO0A_CMD .EQU SIO0BASE + $01
SIO0A_DAT .EQU SIO0BASE + $00
SIO0B_CMD .EQU SIO0BASE + $03
SIO0B_DAT .EQU SIO0BASE + $02
#ENDIF
;
#IF (SIOCNT >= 2)
;
#IF (SIO1MODE == SIOMODE_RC)
SIO1A_CMD .EQU SIO1BASE + $00
SIO1A_DAT .EQU SIO1BASE + $01
SIO1B_CMD .EQU SIO1BASE + $02
SIO1B_DAT .EQU SIO1BASE + $03
#ENDIF
;
#IF (SIO1MODE == SIOMODE_SMB)
SIO1A_CMD .EQU SIO1BASE + $02
SIO1A_DAT .EQU SIO1BASE + $00
SIO1B_CMD .EQU SIO1BASE + $03
SIO1B_DAT .EQU SIO1BASE + $01
#ENDIF
;
#IF (SIO1MODE == SIOMODE_ZP)
SIO1A_CMD .EQU SIO1BASE + $06
SIO1A_DAT .EQU SIO1BASE + $04
SIO1B_CMD .EQU SIO1BASE + $07
SIO1B_DAT .EQU SIO1BASE + $05
#ENDIF
;
#IF (SIO1MODE == SIOMODE_EZZ80)
SIO1A_CMD .EQU SIO1BASE + $01
SIO1A_DAT .EQU SIO1BASE + $00
SIO1B_CMD .EQU SIO1BASE + $03
SIO1B_DAT .EQU SIO1BASE + $02
#ENDIF
;
#ENDIF
;
SIO_PREINIT:
;
; SETUP THE DISPATCH TABLE ENTRIES
; NOTE: INTS WILL BE DISABLED WHEN PREINIT IS CALLED AND THEY MUST REMIAIN
; DISABLED.
;
CALL SIO_PROBE ; PROBE FOR CHIPS
;
LD B,SIO_CFGCNT ; LOOP CONTROL
XOR A ; ZERO TO ACCUM
LD (SIO_DEV),A ; CURRENT DEVICE NUMBER
LD IY,SIO_CFG ; POINT TO START OF CFG TABLE
SIO_PREINIT0:
PUSH BC ; SAVE LOOP CONTROL
CALL SIO_INITUNIT ; HAND OFF TO GENERIC INIT CODE
POP BC ; RESTORE LOOP CONTROL
;
LD A,(IY+1) ; GET THE SIO TYPE DETECTED
OR A ; SET FLAGS
JR Z,SIO_PREINIT2 ; SKIP IT IF NOTHING FOUND
;
PUSH BC ; SAVE LOOP CONTROL
PUSH IY ; CFG ENTRY ADDRESS
POP DE ; ... TO DE
LD BC,SIO_FNTBL ; BC := FUNCTION TABLE ADDRESS
CALL NZ,CIO_ADDENT ; ADD ENTRY IF SIO FOUND, BC:DE
POP BC ; RESTORE LOOP CONTROL
;
SIO_PREINIT2:
LD DE,SIO_CFGSIZ ; SIZE OF CFG ENTRY
ADD IY,DE ; BUMP IY TO NEXT ENTRY
DJNZ SIO_PREINIT0 ; LOOP UNTIL DONE
;
#IF (INTMODE >= 1)
; SETUP INT VECTORS AS APPROPRIATE
LD A,(SIO_DEV) ; GET DEVICE COUNT
OR A ; SET FLAGS
JR Z,SIO_PREINIT3 ; IF ZERO, NO SIO DEVICES, ABORT
;
#IF (INTMODE == 1)
; ADD IM1 INT CALL LIST ENTRY
LD HL,SIO_INT ; GET INT VECTOR
CALL HB_ADDIM1 ; ADD TO IM1 CALL LIST
#ENDIF
;
#IF (INTMODE == 2)
; SETUP IM2 VECTORS
LD HL,SIO_INT0
LD (SIO0_IVT),HL ; IVT INDEX
;
#IF (SIOCNT >= 2)
LD HL,SIO_INT1
LD (SIO1_IVT),HL ; IVT INDEX
#ENDIF
;
#ENDIF
;
#ENDIF
;
SIO_PREINIT3:
XOR A ; SIGNAL SUCCESS
RET ; AND RETURN
;
; SIO INITIALIZATION ROUTINE
;
SIO_INITUNIT:
CALL SIO_DETECT ; DETERMINE SIO TYPE
LD (IY+1),A ; SAVE IN CONFIG TABLE
OR A ; SET FLAGS
RET Z ; ABORT IF NOTHING THERE
; UPDATE WORKING SIO DEVICE NUM
LD HL,SIO_DEV ; POINT TO CURRENT UART DEVICE NUM
LD A,(HL) ; PUT IN ACCUM
INC (HL) ; INCREMENT IT (FOR NEXT LOOP)
LD (IY),A ; UPDATE UNIT NUM
; IT IS EASY TO SPECIFY A SERIAL CONFIG THAT CANNOT BE IMPLEMENTED
; DUE TO THE CONSTRAINTS OF THE SIO. HERE WE FORCE A GENERIC
; FAILSAFE CONFIG ONTO THE CHANNEL. IF THE SUBSEQUENT "REAL"
; CONFIG FAILS, AT LEAST THE CHIP WILL BE ABLE TO SPIT DATA OUT
; AT A RATIONAL BAUD/DATA/PARITY/STOP CONFIG.
CALL SIO_INITSAFE
;
; SET DEFAULT CONFIG
LD DE,-1 ; LEAVE CONFIG ALONE
; CALL INITDEVX TO IMPLEMENT CONFIG, BUT NOTE THAT WE CALL
; THE INITDEVX ENTRY POINT THAT DOES NOT ENABLE/DISABLE INTS!
JP SIO_INITDEVX ; IMPLEMENT IT AND RETURN
;
;
;
SIO_INIT:
LD B,SIO_CFGCNT ; COUNT OF POSSIBLE SIO UNITS
LD IY,SIO_CFG ; POINT TO START OF CFG TABLE
SIO_INIT1:
PUSH BC ; SAVE LOOP CONTROL
LD A,(IY+1) ; GET SIO TYPE
OR A ; SET FLAGS
CALL NZ,SIO_PRTCFG ; PRINT IF NOT ZERO
POP BC ; RESTORE LOOP CONTROL
LD DE,SIO_CFGSIZ ; SIZE OF CFG ENTRY
ADD IY,DE ; BUMP IY TO NEXT ENTRY
DJNZ SIO_INIT1 ; LOOP TILL DONE
;
XOR A ; SIGNAL SUCCESS
RET ; DONE
;
; RECEIVE INTERRUPT HANDLER
;
#IF (INTMODE > 0)
;
; IM1 ENTRY POINT
;
SIO_INT:
; CHECK/HANDLE FIRST CARD (SIO0) IF IT EXISTS
LD A,(SIO0A_CFG + 1) ; GET SIO TYPE FOR FIRST CHANNEL OF FIRST SIO
OR A ; SET FLAGS
CALL NZ,SIO_INT0 ; CALL IF CARD EXISTS
RET NZ ; DONE IF INT HANDLED
;
#IF (SIOCNT >= 2)
; CHECK/HANDLE SECOND CARD (SIO1) IF IT EXISTS
LD A,(SIO1A_CFG + 1) ; GET SIO TYPE FOR FIRST CHANNEL OF SECOND SIO
OR A ; SET FLAGS
CALL NZ,SIO_INT1 ; CALL IF CARD EXISTS
#ENDIF
;
RET ; DONE
;
; IM2 ENTRY POINTS
;
SIO_INT0:
; INTERRUPT HANDLER FOR FIRST SIO (SIO0)
LD IY,SIO0A_CFG ; POINT TO SIO0A CFG
CALL SIO_INTRCV ; TRY TO RECEIVE FROM IT
RET NZ ; DONE IF INT HANDLED
LD IY,SIO0B_CFG ; POINT TO SIO0B CFG
JR SIO_INTRCV ; TRY TO RECEIVE FROM IT AND RETURN
;
#IF (SIOCNT >= 2)
;
SIO_INT1:
; INTERRUPT HANDLER FOR SECOND SIO (SIO1)
LD IY,SIO1A_CFG ; POINT TO SIO1A CFG
CALL SIO_INTRCV ; TRY TO RECEIVE FROM IT
RET NZ ; DONE IF INT HANDLED
LD IY,SIO1B_CFG ; POINT TO SIO1B CFG
JR SIO_INTRCV ; TRY TO RECEIVE FROM IT AND RETURN
;
#ENDIF
;
; HANDLE INT FOR A SPECIFIC CHANNEL
; BASED ON UNIT CFG POINTED TO BY IY
;
SIO_INTRCV:
; CHECK TO SEE IF SOMETHING IS ACTUALLY THERE
LD C,(IY+3) ; CMD/STAT PORT TO C
XOR A ; A := 0
OUT (C),A ; ADDRESS RD0
IN A,(C) ; GET RD0
AND $01 ; ISOLATE RECEIVE READY BIT
RET Z ; NOTHING AVAILABLE ON CURRENT CHANNEL
;
SIO_INTRCV1:
; RECEIVE CHARACTER INTO BUFFER
LD C,(IY+4) ; DATA PORT TO C
IN A,(C) ; READ PORT
LD B,A ; SAVE BYTE READ
LD L,(IY+7) ; SET HL TO
LD H,(IY+8) ; ... START OF BUFFER STRUCT
LD A,(HL) ; GET COUNT
CP SIO_BUFSZ ; COMPARE TO BUFFER SIZE
JR Z,SIO_INTRCV4 ; BAIL OUT IF BUFFER FULL, RCV BYTE DISCARDED
INC A ; INCREMENT THE COUNT
LD (HL),A ; AND SAVE IT
CP SIO_BUFSZ / 2 ; BUFFER GETTING FULL?
JR NZ,SIO_INTRCV2 ; IF NOT, BYPASS CLEARING RTS
LD C,(IY+3) ; CMD/STAT PORT TO C
LD A,5 ; RTS IS IN WR5
OUT (C),A ; ADDRESS WR5
LD A,SIO_RTSOFF ; VALUE TO CLEAR RTS
OUT (C),A ; DO IT
SIO_INTRCV2:
INC HL ; HL NOW HAS ADR OF HEAD PTR
PUSH HL ; SAVE ADR OF HEAD PTR
LD A,(HL) ; DEREFERENCE HL
INC HL
LD H,(HL)
LD L,A ; HL IS NOW ACTUAL HEAD PTR
LD (HL),B ; SAVE CHARACTER RECEIVED IN BUFFER AT HEAD
INC HL ; BUMP HEAD POINTER
POP DE ; RECOVER ADR OF HEAD PTR
LD A,L ; GET LOW BYTE OF HEAD PTR
SUB SIO_BUFSZ+4 ; SUBTRACT SIZE OF BUFFER AND POINTER
CP E ; IF EQUAL TO START, HEAD PTR IS PAST BUF END
JR NZ,SIO_INTRCV3 ; IF NOT, BYPASS
LD H,D ; SET HL TO
LD L,E ; ... HEAD PTR ADR
INC HL ; BUMP PAST HEAD PTR
INC HL
INC HL
INC HL ; ... SO HL NOW HAS ADR OF ACTUAL BUFFER START
SIO_INTRCV3:
EX DE,HL ; DE := HEAD PTR VAL, HL := ADR OF HEAD PTR
LD (HL),E ; SAVE UPDATED HEAD PTR
INC HL
LD (HL),D
; CHECK FOR MORE PENDING...
LD C,(IY+3) ; CMD/STAT PORT TO C
XOR A ; A := 0
OUT (C),A ; ADDRESS RD0
IN A,(C) ; GET RD0
RRA ; READY BIT TO CF
JR C,SIO_INTRCV1 ; IF SET, DO SOME MORE
SIO_INTRCV4:
OR $FF ; NZ SET TO INDICATE INT HANDLED
RET ; AND RETURN
;
#ENDIF
;
; DRIVER FUNCTION TABLE
;
SIO_FNTBL:
.DW SIO_IN
.DW SIO_OUT
.DW SIO_IST
.DW SIO_OST
.DW SIO_INITDEV
.DW SIO_QUERY
.DW SIO_DEVICE
#IF (($ - SIO_FNTBL) != (CIO_FNCNT * 2))
.ECHO "*** INVALID SIO FUNCTION TABLE ***\n"
#ENDIF
;
;
;
#IF (INTMODE == 0)
;
SIO_IN:
CALL SIO_IST ; CHAR WAITING?
JR Z,SIO_IN ; LOOP IF NOT
LD C,(IY+4) ; DATA PORT
IN E,(C) ; GET CHAR
XOR A ; SIGNAL SUCCESS
RET
;
#ELSE
;
SIO_IN:
CALL SIO_IST ; SEE IF CHAR AVAILABLE
JR Z,SIO_IN ; LOOP UNTIL SO
HB_DI ; AVOID COLLISION WITH INT HANDLER
LD L,(IY+7) ; SET HL TO
LD H,(IY+8) ; ... START OF BUFFER STRUCT
LD A,(HL) ; GET COUNT
DEC A ; DECREMENT COUNT
LD (HL),A ; SAVE UPDATED COUNT
CP SIO_BUFSZ / 4 ; BUFFER LOW THRESHOLD
JR NZ,SIO_IN1 ; IF NOT, BYPASS SETTING RTS
LD C,(IY+3) ; C IS CMD/STATUS PORT ADR
LD A,5 ; RTS IS IN WR5
OUT (C),A ; ADDRESS WR5
LD A,SIO_RTSON ; VALUE TO SET RTS
OUT (C),A ; DO IT
SIO_IN1:
INC HL
INC HL
INC HL ; HL NOW HAS ADR OF TAIL PTR
PUSH HL ; SAVE ADR OF TAIL PTR
LD A,(HL) ; DEREFERENCE HL
INC HL
LD H,(HL)
LD L,A ; HL IS NOW ACTUAL TAIL PTR
LD C,(HL) ; C := CHAR TO BE RETURNED
INC HL ; BUMP TAIL PTR
POP DE ; RECOVER ADR OF TAIL PTR
LD A,L ; GET LOW BYTE OF TAIL PTR
SUB SIO_BUFSZ+2 ; SUBTRACT SIZE OF BUFFER AND POINTER
CP E ; IF EQUAL TO START, TAIL PTR IS PAST BUF END
JR NZ,SIO_IN2 ; IF NOT, BYPASS
LD H,D ; SET HL TO
LD L,E ; ... TAIL PTR ADR
INC HL ; BUMP PAST TAIL PTR
INC HL ; ... SO HL NOW HAS ADR OF ACTUAL BUFFER START
SIO_IN2:
EX DE,HL ; DE := TAIL PTR VAL, HL := ADR OF TAIL PTR
LD (HL),E ; SAVE UPDATED TAIL PTR
INC HL
LD (HL),D
LD E,C ; MOVE CHAR TO RETURN TO E
HB_EI ; INTERRUPTS OK AGAIN
XOR A ; SIGNAL SUCCESS
RET ; AND DONE
#ENDIF
;
;
;
SIO_OUT:
CALL SIO_OST ; READY FOR CHAR?
JR Z,SIO_OUT ; LOOP IF NOT
LD C,(IY+4) ; DATA PORT
OUT (C),E ; SEND CHAR FROM E
XOR A ; SIGNAL SUCCESS
RET
;
;
;
#IF (INTMODE == 0)
;
SIO_IST:
LD C,(IY+3) ; CMD PORT
XOR A ; WR0
OUT (C),A ; DO IT
IN A,(C) ; GET STATUS
AND $01 ; ISOLATE BIT 0 (RX READY)
JP Z,CIO_IDLE ; NOT READY, RETURN VIA IDLE PROCESSING
XOR A ; ZERO ACCUM
INC A ; ASCCUM := 1 TO SIGNAL 1 CHAR WAITING
RET ; DONE
;
#ELSE
;
SIO_IST:
LD L,(IY+7) ; GET ADDRESS
LD H,(IY+8) ; ... OF RECEIVE BUFFER
LD A,(HL) ; BUFFER UTILIZATION COUNT
OR A ; SET FLAGS
JP Z,CIO_IDLE ; NOT READY, RETURN VIA IDLE PROCESSING
RET
;
#ENDIF
;
;
;
SIO_OST:
LD C,(IY+3) ; CMD PORT
XOR A ; WR0
OUT (C),A ; DO IT
IN A,(C) ; GET STATUS
AND $04 ; ISOLATE BIT 2 (TX EMPTY)
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
;
; AT INITIALIZATION THE SETUP PARAMETER WORD IS TRANSLATED TO THE FORMAT
; REQUIRED BY THE SIO AND STORED IN A PORT/REGISTER INITIALIZATION TABLE,
; WHICH IS THEN LOADED INTO THE SIO.
;
; RTS, DTR AND XON SETTING IS NOT CURRENTLY SUPPORTED.
; MARK & SPACE PARITY AND 1.5 STOP BITS IS NOT SUPPORTED BY THE SIO.
; INITIALIZATION WILL NOT BE COMPLETED IF AN INVALID SETTING IS DETECTED.
;
; NOTE THAT THERE ARE TWO ENTRY POINTS. INITDEV WILL DISABLE/ENABLE INTS
; AND INITDEVX WILL NOT. THIS IS DONE SO THAT THE PREINIT ROUTINE ABOVE
; CAN AVOID ENABLING/DISABLING INTS.
;
SIO_INITDEV:
HB_DI ; DISABLE INTS
CALL SIO_INITDEVX ; DO THE WORK
HB_EI ; INTS BACK ON
RET ; DONE
;
SIO_INITDEVX:
;
; THIS ENTRY POINT BYPASSES DISABLING/ENABLING INTS WHICH IS REQUIRED BY
; PREINIT ABOVE. PREINIT IS NOT ALLOWED TO ENABLE INTS!
;
#IF (SIODEBUG)
CALL NEWLINE
PRTS("SIO$")
LD A,(IY+2)
SRL A
CALL PRTDECB
LD A,(IY+2)
AND $01
ADD A,'A'
CALL COUT
CALL PC_COLON
#ENDIF
;
; 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,SIO_INITDEV1 ; IF DE == -1, REINIT CURRENT CONFIG
;
; LOAD EXISTING CONFIG TO REINIT
LD E,(IY+5) ; LOW BYTE
LD D,(IY+6) ; HIGH BYTE
;
SIO_INITDEV1:
;
#IF (SIODEBUG)
PUSH DE
POP BC
PRTS(" CFG=$")
CALL PRTHEXWORD
#ENDIF
;
LD A,E ; GET CONFIG LSB
AND $E0 ; CHECK FOR DTR, XON, PARITY=MARK/SPACE
JR NZ,SIO_INITFAIL ; IF ANY BIT SET, FAIL, NOT SUPPORTED
;
LD A,D ; GET CONFIG MSB
AND $1F ; ISOLATE ENCODED BAUD RATE
;
#IF (SIODEBUG)
PRTS(" ENC=$")
CALL PRTHEXBYTE
#ENDIF
;
PUSH DE ; SAVE REQUESTED CONFIG
LD L,(IY+9) ; LOAD CLK FREQ
LD H,(IY+10) ; ... INTO DE:HL
LD E,(IY+11) ; ... "
LD D,(IY+12) ; ... "
LD C,75 ; BAUD RATE ENCODING CONSTANT
CALL ENCODE ; C = TEST BAUD RATE (ENCODED) = BAUDTST
POP DE ; GET REQ CONFIG BACK, D = BAUDREQ
;
; BIT 4 (DIV 3) OF BAUDREQ AND BAUDTST MUST MATCH!
LD A,C ; A = BAUDTST
XOR D ; XOR WITH BAUDREQ
BIT 4,A ; DO BIT 4 VALS MATCH?
JR NZ,SIO_INITFAIL ; IF NOT, BAIL OUT
;
LD A,C ; BAUDTST TO A
AND $0F ; ISOLATE DIV 2 BAUD BITS
LD C,A ; C = BAUDTST
;
LD A,D ; MSB W/ BAUD RATE TO A
AND $0F ; ISOLATE DIV 2 BAUD BITS
LD L,A ; L = BAUDREQ
;
; PUSH AF ; *DEBUG*
; CALL NEWLINE ; *DEBUG*
; LD A,L ; *DEBUG*
; CALL PRTHEXBYTE ; *DEBUG*
; LD A,C ; *DEBUG*
; CALL PRTHEXBYTE ; *DEBUG*
; CALL NEWLINE ; *DEBUG*
; POP AF ; *DEBUG*
;
LD A,C ; A = BAUDTST
LD B,$04 ; SIO R4 VAL FOR DIV 1
CP L ; BAUDTST = BAUDREQ?
JR Z,SIO_INITBROK ; IF MATCH, WE ARE DONE
;
SUB 4 ; DIVIDE BY 16 (NOW DIV 16 TOT)
JR C,SIO_INITFAIL ; FAIL IF UNDERFLOW
LD B,$44 ; SIO R4 VAL FOR DIV 16
CP L ; BAUDTST = BAUDREQ?
JR Z,SIO_INITBROK ; IF MATCH, WE ARE DONE
;
SUB 1 ; DIVIDE BY 2 (NOW DIV 32 TOT)
JR C,SIO_INITFAIL ; FAIL IF UNDERFLOW
LD B,$84 ; SIO R4 VAL FOR DIV 32
CP L ; BAUDTST = BAUDREQ?
JR Z,SIO_INITBROK ; IF MATCH, WE ARE DONE
;
SUB 1 ; DIVIDE BY 2 (NOW DIV 64 TOT)
JR C,SIO_INITFAIL ; FAIL IF UNDERFLOW
LD B,$C4 ; SIO R4 VAL FOR DIV 64
CP L ; BAUDTST = BAUDREQ?
JR Z,SIO_INITBROK ; IF MATCH, WE ARE DONE
;
SIO_INITFAIL:
;
#IF (SIODEBUG)
PRTS(" BAD CFG$")
#ENDIF
;
OR $FF
RET ; NZ status here indicating fail / invalid baud rate.
;
SIO_INITBROK:
LD A,E ; set stop bit (d3) and add divider
AND $04
RLA
OR B ; carry gets reset here
LD L,A ; save in L
LD A,E ; get the parity bits
SRL A ; move them to bottom two bits
SRL A ; we know top bits are zero from previous test
SRL A ; add stop bits
OR L ; carry = 0
;
; SET DIVIDER, STOP AND PARITY WR4
;
LD (SIO_WR4),A
;
LD A,E ; 112233445566d1d0 CC
RRA ; CC112233445566d1 d0
RRA ; d0CC112233445566 d1
RRA ; d1d0CC1122334455 66
LD L,A
RRA ; 66d1d0CC11223344 55
AND $60 ; 0011110000000000 00
OR $8A
;
; SET TRANSMIT DATA BITS WR5
;
LD (SIO_WR5),A
;
; SET RECEIVE DATA BITS WR3
;
LD A,L ; DATA BITS
AND $C0 ; CLEAR OTHER BITS
OR $21 ; CTS/DCD AUTO, RX ENABLE
;
LD (SIO_WR3),A
;
; SAVE CONFIG PERMANENTLY NOW
;
LD (IY+5),E ; SAVE LOW WORD
LD (IY+6),D ; SAVE HI WORD
;
JR SIO_INITGO ; GO TO SEND INIT
;
; ENTER HERE TO PERFORM A "SAFE" INITIALIZTION. I.E., INIT THE
; CHANNEL USING THE DEFAULT, GENERIC REGISTER VALUES. THIS CAN BE
; USED TO ENSURE INITIALIZATION IF THE FULL CONFIGURATION ABOVE
; FAILS.
;
SIO_INITSAFE:
LD HL,SIO_INITDEFS
LD DE,SIO_INITVALS
LD BC,SIO_INITLEN
LDIR
;
SIO_INITGO:
;
; SET INTERRUPT VECTOR OFFSET WR2
;
#IF (INTMODE == 2)
LD A,(IY+2) ; CHIP / CHANNEL
SRL A ; SHIFT AWAY CHANNEL BIT
LD L,SIO0_VEC ; ASSUME CHIP 0
JR Z,SIO_INITIVT ; IF SO, DO IT
LD L,SIO1_VEC ; ASSUME CHIP 1
DEC A ; CHIP 1?
JR Z,SIO_INITIVT ; IF SO, DO IT
CALL PANIC ; IMPOSSIBLE SITUATION
SIO_INITIVT:
LD A,L ; VALUE TO A
LD (SIO_WR2),A ; SAVE IT
;
#ENDIF
;
#IF (SIODEBUG)
LD HL,SIO_INITVALS
LD B,SIO_INITLEN/2
SIO_INITPRT:
PRTS(" WR$")
LD A,(HL)
CALL PRTHEXBYTE
INC HL
LD A,'='
CALL COUT
LD A,(HL)
CALL PRTHEXBYTE
INC HL
DJNZ SIO_INITPRT
LD DE,65
CALL VDELAY ; WAIT FOR FINAL CHAR TO SEND
#ENDIF
;
; PROGRAM THE SIO CHIP CHANNEL
LD C,(IY+3) ; COMMAND PORT
LD HL,SIO_INITVALS ; POINT TO INIT VALUES
LD B,SIO_INITLEN ; COUNT OF BYTES TO WRITE
OTIR ; WRITE ALL VALUES
;
#IF (INTMODE > 0)
;
; RESET THE RECEIVE BUFFER
LD E,(IY+7)
LD D,(IY+8) ; DE := _CNT
XOR A ; A := 0
LD (DE),A ; _CNT = 0
INC DE ; DE := ADR OF _HD
PUSH DE ; SAVE IT
INC DE
INC DE
INC DE
INC DE ; DE := ADR OF _BUF
POP HL ; HL := ADR OF _HD
LD (HL),E
INC HL
LD (HL),D ; _HD := _BUF
INC HL
LD (HL),E
INC HL
LD (HL),D ; _TL := _BUF
;
#ENDIF
;
XOR A ; SIGNAL SUCCESS
RET ; RETURN
;
; THE SIO IS A LITTLE PRICKLY ABOUT THE ORDER IN WHICH REGSITERS ARE
; WRITTEN DURING CONFIGURATION. THE TABLE BELOW IS USED TO SETUP
; THE REGISTER VALUES AND THEN THE ENTIRE TABLE CAN BE SPIT OUT.
;
SIO_INITVALS:
.DB $00, $18 ; WR0: CHANNEL RESET CMD
SIO_WR4 .EQU $+1
.DB $04, $C4 ; WR4: CLK BAUD PARITY STOP BIT
SIO_WR1 .EQU $+1
.DB $01, SIO_WR1VAL ; WR1: INTERRUPT STYLE
SIO_WR2 .EQU $+1
.DB $02, $00 ; WR2: IM2 VEC OFFSET, SET DYNAMICALLY
SIO_WR3 .EQU $+1
.DB $03, $E1 ; WR3: 8 BIT RCV, CTS/DCD AUTO, RX ENABLE
SIO_WR5 .EQU $+1
.DB $05, SIO_RTSON ; WR5: DTR, 8 BITS SEND, TX ENABLE, RTS 1 11 0 1 0 1 0 (1=DTR,11=8bits,0=sendbreak,1=TxEnable,0=sdlc,1=RTS,0=txcrc)
;
SIO_INITLEN .EQU $ - SIO_INITVALS
;
; THE FOLLOWING TABLE IS A GENERIC, STATIC SET OF CONFIG VALUES THAT CAN
; BE USED TO INITIALIZE THE WORKING TABLE ABOVE.
;
SIO_INITDEFS:
.DB $00, $18 ; WR0: CHANNEL RESET CMD
.DB $04, $C4 ; WR4: CLK BAUD PARITY STOP BIT
.DB $01, SIO_WR1VAL ; WR1: INTERRUPT STYLE
.DB $02, $00 ; WR2: IM2 VEC OFFSET
.DB $03, $E1 ; WR3: 8 BIT RCV, CTS/DCD AUTO, RX ENABLE
.DB $05, SIO_RTSON ; WR5: DTR, 8 BITS SEND, TX ENABLE, RTS 1 11 0 1 0 1 0 (1=DTR,11=8bits,0=sendbreak,1=TxEnable,0=sdlc,1=RTS,0=txcrc)
;
#IF (($ - SIO_INITDEFS) != SIO_INITLEN)
.ECHO "*** ERROR: SIO_INITDEFS TABLE IS NOT THE SAME SIZE AS SIO_INITVALS TABLE!!!\n"
!!! ; FORCE AN ASSEMBLY ERROR
#ENDIF
;
;
;
SIO_QUERY:
LD E,(IY+5) ; FIRST CONFIG BYTE TO E
LD D,(IY+6) ; SECOND CONFIG BYTE TO D
XOR A ; SIGNAL SUCCESS
RET ; DONE
;
;
;
SIO_DEVICE:
LD D,CIODEV_SIO ; D := DEVICE TYPE
LD E,(IY) ; E := PHYSICAL UNIT
LD C,$00 ; C := DEVICE TYPE, 0x00 IS RS-232
XOR A ; SIGNAL SUCCESS
RET
;
; SIO CHIP PROBE
; CHECK FOR PRESENCE OF SIO CHIPS AND POPULATE THE
; SIO_MAP BITMAP (ONE BIT PER CHIP). THIS DETECTS
; CHIPS, NOT CHANNELS. EACH CHIP HAS 2 CHANNELS.
; MAX OF TWO CHIPS CURRENTLY. INT VEC VALUE IS TRASHED!
;
SIO_PROBE:
; CLEAR THE PRESENCE BITMAP
LD HL,SIO_MAP ; HL POINTS TO BITMAP
XOR A ; ZERO
LD (SIO_MAP),A ; CLEAR CHIP PRESENT BITMAP
; INIT THE INT VEC REGISTER OF ALL POSSIBLE CHIPS
; TO ZERO. A IS STILL ZERO.
LD B,2 ; WR2 REGISTER (INT VEC)
LD C,SIO0B_CMD ; FIRST CHIP
CALL SIO_WR ; WRITE ZERO TO CHIP REG
#IF (SIOCNT >= 2)
LD C,SIO1B_CMD ; SECOND CHIP
CALL SIO_WR ; WRITE ZERO TO CHIP REG
#ENDIF
; FIRST POSSIBLE CHIP
LD C,SIO0B_CMD ; FIRST CHIP CMD/STAT PORT
CALL SIO_PROBECHIP ; PROBE IT
JR NZ,SIO_PROBE1 ; IF NOT ZERO, NOT FOUND
SET 0,(HL) ; SET BIT FOR FIRST CARD
SIO_PROBE1:
;
#IF (SIOCNT >= 2)
LD C,SIO1B_CMD ; SECOND CHIP CMD/STAT PORT
CALL SIO_PROBECHIP ; PROBE IT
JR NZ,SIO_PROBE2 ; IF NOT ZERO, NOT FOUND
SET 1,(HL) ; SET BIT FOR SECOND CARD
SIO_PROBE2:
#ENDIF
;
RET
;
SIO_PROBECHIP:
; READ WR2 TO ENSURE IT IS ZERO (AVOID PHANTOM PORTS)
CALL SIO_RD ; GET VALUE
AND $F0 ; ONLY TOP NIBBLE
RET NZ ; ABORT IF NOT ZERO
; WRITE INT VEC VALUE TO WR2
LD A,$FF ; TEST VALUE
CALL SIO_WR ; WRITE IT
; READ WR2 TO CONFIRM VALUE WRITTEN
CALL SIO_RD ; REREAD VALUE
AND $F0 ; ONLY TOP NIBBLE
CP $F0 ; COMPARE
RET ; DONE, Z IF FOUND, NZ IF MISCOMPARE
;
; READ/WRITE CHIP REGISTER. ENTER CHIP CMD/STAT PORT ADR IN C
; AND CHIP REGISTER NUMBER IN B. VALUE TO WRITE IN A OR VALUE
; RETURNED IN A.
;
SIO_WR:
OUT (C),B ; SELECT CHIP REGISTER
OUT (C),A ; WRITE VALUE
RET
;
SIO_RD:
OUT (C),B ; SELECT CHIP REGISTER
IN A,(C) ; GET VALUE
RET
;
; SIO DETECTION ROUTINE
; THERE IS ONLY ONE VARIATION OF SIO CHIP, SO HERE WE JUST CHECK THE
; CHIP PRESENCE BITMAP TO SET THE CHIP TYPE OF EITHER NONE OR SIO.
;
SIO_DETECT:
LD B,(IY+2) ; GET CHIP/CHANNEL
SRL B ; SHIFT AWAY THE CHANNEL BIT
INC B ; NUMBER OF TIMES TO ROTATE BITS
LD A,(SIO_MAP) ; BIT MAP IN A
SIO_DETECT1:
; ROTATE DESIRED CHIP BIT INTO CF
RRA ; ROTATE NEXT BIT INTO CF
DJNZ SIO_DETECT1 ; DO THIS UNTIL WE HAVE DESIRED BIT
; RETURN CHIP TYPE
LD A,SIO_NONE ; ASSUME NOTHING HERE
RET NC ; IF CF NOT SET, RETURN
LD A,SIO_SIO ; CHIP TYPE IS SIO
RET ; DONE
;
;
;
SIO_PRTCFG:
; ANNOUNCE PORT
CALL NEWLINE ; FORMATTING
PRTS("SIO$") ; FORMATTING
LD A,(IY) ; DEVICE NUM
CALL PRTDECB ; PRINT DEVICE NUM
PRTS(": IO=0x$") ; FORMATTING
LD A,(IY+3) ; GET BASE PORT
CALL PRTHEXBYTE ; PRINT BASE PORT
; PRINT THE SIO TYPE
CALL PC_SPACE ; FORMATTING
LD A,(IY+1) ; GET SIO TYPE BYTE
RLCA ; MAKE IT A WORD OFFSET
LD HL,SIO_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 SIO WAS DETECTED
LD A,(IY+1) ; GET SIO TYPE BYTE
OR A ; SET FLAGS
RET Z ; IF ZERO, NOT PRESENT
;
PRTS(" MODE=$") ; FORMATTING
LD E,(IY+5) ; LOAD CONFIG
LD D,(IY+6) ; ... WORD TO DE
CALL PS_PRTSC0 ; PRINT CONFIG
;
XOR A
RET
;
;
;
SIO_TYPE_MAP:
.DW SIO_STR_NONE
.DW SIO_STR_SIO
SIO_STR_NONE .DB "<NOT PRESENT>$"
SIO_STR_SIO .DB "SIO$"
;
; WORKING VARIABLES
;
SIO_DEV .DB 0 ; DEVICE NUM USED DURING INIT
SIO_MAP .DB 0 ; CHIP PRESENCE BITMAP
;
#IF (INTMODE == 0)
;
SIO0A_RCVBUF .EQU 0
SIO0B_RCVBUF .EQU 0
;
#IF (SIOCNT >= 2)
SIO1A_RCVBUF .EQU 0
SIO1B_RCVBUF .EQU 0
#ENDIF
;
#ELSE
;
; SIO0 CHANNEL A RECEIVE BUFFER
SIO0A_RCVBUF:
SIO0A_CNT .DB 0 ; CHARACTERS IN RING BUFFER
SIO0A_HD .DW SIO0A_BUF ; BUFFER HEAD POINTER
SIO0A_TL .DW SIO0A_BUF ; BUFFER TAIL POINTER
SIO0A_BUF .FILL SIO_BUFSZ,0 ; RECEIVE RING BUFFER
;
; SIO0 CHANNEL B RECEIVE BUFFER
SIO0B_RCVBUF:
SIO0B_CNT .DB 0 ; CHARACTERS IN RING BUFFER
SIO0B_HD .DW SIO0B_BUF ; BUFFER HEAD POINTER
SIO0B_TL .DW SIO0B_BUF ; BUFFER TAIL POINTER
SIO0B_BUF .FILL SIO_BUFSZ,0 ; RECEIVE RING BUFFER
;
#IF (SIOCNT >= 2)
;
; SIO1 CHANNEL A RECEIVE BUFFER
SIO1A_RCVBUF:
SIO1A_CNT .DB 0 ; CHARACTERS IN RING BUFFER
SIO1A_HD .DW SIO1A_BUF ; BUFFER HEAD POINTER
SIO1A_TL .DW SIO1A_BUF ; BUFFER TAIL POINTER
SIO1A_BUF .FILL SIO_BUFSZ,0 ; RECEIVE RING BUFFER
;
; SIO1 CHANNEL B RECEIVE BUFFER
SIO1B_RCVBUF:
SIO1B_CNT .DB 0 ; CHARACTERS IN RING BUFFER
SIO1B_HD .DW SIO1B_BUF ; BUFFER HEAD POINTER
SIO1B_TL .DW SIO1B_BUF ; BUFFER TAIL POINTER
SIO1B_BUF .FILL SIO_BUFSZ,0 ; RECEIVE RING BUFFER
;
#ENDIF
;
#ENDIF
;
; SIO PORT TABLE
;
SIO_CFG:
; SIO0 CHANNEL A
SIO0A_CFG:
.DB 0 ; DEVICE NUMBER (SET DURING INIT)
.DB 0 ; SIO TYPE (SET DURING INIT)
.DB $00 ; CHIP 0 / CHANNEL A (LOW BIT IS CHANNEL)
.DB SIO0A_CMD ; CMD/STATUS PORT
.DB SIO0A_DAT ; DATA PORT
.DW SIO0ACFG ; LINE CONFIGURATION
.DW SIO0A_RCVBUF ; POINTER TO RCV BUFFER STRUCT
.DW (SIO0ACLK / SIO0ADIV) & $FFFF ; CLOCK FREQ AS
.DW (SIO0ACLK / SIO0ADIV) >> 16 ; ... DWORD VALUE
;
SIO_CFGSIZ .EQU $ - SIO_CFG ; SIZE OF ONE CFG TABLE ENTRY
;
; SIO0 CHANNEL B
SIO0B_CFG:
.DB 0 ; DEVICE NUMBER (SET DURING INIT)
.DB 0 ; SIO TYPE (SET DURING INIT)
.DB $01 ; CHIP 0 / CHANNEL B (LOW BIT IS CHANNEL)
.DB SIO0B_CMD ; CMD/STATUS PORT
.DB SIO0B_DAT ; DATA PORT
.DW SIO0BCFG ; LINE CONFIGURATION
.DW SIO0B_RCVBUF ; POINTER TO RCV BUFFER STRUCT
.DW (SIO0BCLK / SIO0BDIV) & $FFFF ; CLOCK FREQ AS
.DW (SIO0BCLK / SIO0BDIV) >> 16 ; ... DWORD VALUE
;
#IF (SIOCNT >= 2)
;
; SIO1 CHANNEL A
SIO1A_CFG:
.DB 0 ; DEVICE NUMBER (SET DURING INIT)
.DB 0 ; SIO TYPE (SET DURING INIT)
.DB $02 ; CHIP 1 / CHANNEL A (LOW BIT IS CHANNEL)
.DB SIO1A_CMD ; CMD/STATUS PORT
.DB SIO1A_DAT ; DATA PORT
.DW SIO1ACFG ; LINE CONFIGURATION
.DW SIO1A_RCVBUF ; POINTER TO RCV BUFFER STRUCT
.DW (SIO1ACLK / SIO1ADIV) & $FFFF ; CLOCK FREQ AS
.DW (SIO1ACLK / SIO1ADIV) >> 16 ; ... DWORD VALUE
;
; SIO1 CHANNEL B
SIO1B_CFG:
.DB 0 ; DEVICE NUMBER (SET DURING INIT)
.DB 0 ; SIO TYPE (SET DURING INIT)
.DB $03 ; CHIP 1 / CHANNEL B (LOW BIT IS CHANNEL)
.DB SIO1B_CMD ; CMD/STATUS PORT
.DB SIO1B_DAT ; DATA PORT
.DW SIO1BCFG ; LINE CONFIGURATION
.DW SIO1B_RCVBUF ; POINTER TO RCV BUFFER STRUCT
.DW (SIO1BCLK / SIO1BDIV) & $FFFF ; CLOCK FREQ AS
.DW (SIO1BCLK / SIO1BDIV) >> 16 ; ... DWORD VALUE
;
#ENDIF
;
SIO_CFGCNT .EQU ($ - SIO_CFG) / SIO_CFGSIZ