; ;================================================================================================== ; 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 ((SIOINTS) & (INTMODE > 0)) SIO_WR1VAL .EQU $18 ; WR1 VALUE FOR INT ON RECEIVED CHARS #ELSE SIO_WR1VAL .EQU $00 ; WR1 VALUE FOR NO INTS #ENDIF ; #IF ((SIOINTS) & (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_STD) SIO0A_CMD .EQU SIO0BASE + $01 SIO0A_DAT .EQU SIO0BASE + $00 SIO0B_CMD .EQU SIO0BASE + $03 SIO0B_DAT .EQU SIO0BASE + $02 #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_Z80R) SIO0A_CMD .EQU SIO0BASE + $03 SIO0A_DAT .EQU SIO0BASE + $01 SIO0B_CMD .EQU SIO0BASE + $02 SIO0B_DAT .EQU SIO0BASE + $00 #ENDIF ; #IF (SIOCNT >= 2) ; #IF (SIO1MODE == SIOMODE_STD) SIO1A_CMD .EQU SIO1BASE + $01 SIO1A_DAT .EQU SIO1BASE + $00 SIO1B_CMD .EQU SIO1BASE + $03 SIO1B_DAT .EQU SIO1BASE + $02 #ENDIF ; #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_Z80R) SIO1A_CMD .EQU SIO1BASE + $03 SIO1A_DAT .EQU SIO1BASE + $01 SIO1B_CMD .EQU SIO1BASE + $02 SIO1B_DAT .EQU SIO1BASE + $00 #ENDIF ; #ENDIF ; ;-------------------------------------------------------------------------------------------------- ; HBIOS MODULE HEADER ;-------------------------------------------------------------------------------------------------- ; ORG_SIO .EQU $ ; .DW SIZ_SIO ; MODULE SIZE .DW SIO_INITPHASE ; ADR OF INIT PHASE HANDLER ; SIO_INITPHASE: ; INIT PHASE HANDLER, A=PHASE CP HB_PHASE_PREINIT ; PREINIT PHASE? JP Z,SIO_PREINIT ; DO PREINIT CP HB_PHASE_INIT ; INIT PHASE? JP Z,SIO_INIT ; DO INIT RET ; DONE ; 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 ((SIOINTS) & (INTMODE > 0)) ; 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) | (INTMODE == 3)) ; SETUP IM2/3 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 JP SIO_INITDEV ; 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 ((SIOINTS) & (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 #IF (SIOBOOT != 0) CP SIOBOOT ; REBOOT REQUEST? JP Z,SYS_RESCOLD ; IF SO, DO IT, NO RETURN #ENDIF 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 ((SIOINTS) & (INTMODE > 0)) ; 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 ; #ELSE ; 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 ; #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 ((SIOINTS) & (INTMODE > 0)) ; 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 ; #ELSE ; 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 ; #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. ; SIO_INITDEV: ; INITDEV CAN BE CALLED PRIOR TO INTERRUPTS BEING ENABLED. WE ; NEED TO LEAVE INTERRUPTS ALONE IN THIS SCENARIO LD A,(INTSENAB) ; INTS ENABLED? OR A ; TEST VALUE JR Z,SIO_INITDEV0 ; BYPASS DI/EI IF NOT ENABLED ; ; INTERRUPTS DISABLED DURING INIT HB_DI ; DISABLE INTS CALL SIO_INITDEV0 ; DO THE WORK HB_EI ; INTS BACK ON RET ; DONE ; SIO_INITDEV0: ; ; 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 ; PUSH DE ; SAVE TARGET CONFIG ; ; WE WANT TO DETERMINE A DIVISOR FOR THE SIO CLOCK ; THAT RESULTS IN THE DESIRED BAUD RATE. ; BAUD RATE = SIO CLK / DIVISOR, OR TO SOLVE FOR DIVISOR ; DIVISOR = SIO CLK / BAUDRATE. ; TAKE ADVANTAGE OF ENCODED BAUD RATES ALWAYS BEING A FACTOR OF 75. ; SO, WE CAN USE (SIO OSC / 75) / (BAUDRATE / 75) ; ; GET SERIAL CLOCK VALUE AND DIVIDE IT BY 75 PUSH IY ; GET CONFIG TABLE ENTRY PTR POP HL ; MOVE TO HL LD A,9 ; OFFSET TO CLK VALUE CALL ADDHLA ; HL IS NOW PTR TO 32 BIT CLK CALL LD32 ; LOAD DE:HL W/ RAW CLK VAL LD C,75 ; DIVIDE BY 75 LIKE BAUD RATE CALL DIV32X8 ; HL NOW HAS (CLK / 75) ; #IF (SIODEBUG) PRTS(" CLK75=$") CALL PRTHEX32 #ENDIF ; ; SCALE DOWN THE 32 BIT VALUE TO FIT IN 16 BITS KEEPING ; TRACK OF THE NUMBER OF BITS SHIFTED OUT IN B LD B,0 ; SHIFT COUNTER SIO_INITDEV1A: LD A,D ; TEST MSB OR E ; ... FOR ZERO JR Z,SIO_INITDEV1B ; IF SO, DONE SRL D ; 32 BIT RIGHT SHIFT RR E ; ... RR H ; ... RR L ; ... INC B ; INCREMENT SHIFT COUNTER JR SIO_INITDEV1A ; AND LOOP SIO_INITDEV1B: ; #IF (SIODEBUG) PRTS(" CLK=$") CALL PRTHEX32 #ENDIF ; POP DE ; RECOVER INCOMING TARGET CFG PUSH DE ; RESAVE IT PUSH HL ; SAVE CLK VALUE PUSH BC ; SAVE BITS SHIFTED ; NOW DECODE THE BAUDRATE, BUT WE USE A CONSTANT OF 1 INSTEAD ; OF THE NORMAL 75. THIS PRODUCES (BAUDRATE / 75). LD A,D ; GET CONFIG MSB AND $1F ; ISOLATE ENCODED BAUD RATE LD L,A ; PUT IN L LD H,0 ; H IS ALWAYS ZERO LD DE,1 ; USE 1 FOR ENCODING CONSTANT CALL DECODE ; DE:HL := BAUD RATE, ERRORS IGNORED ; #IF (SIODEBUG) PRTS(" BAUD75=$") CALL PRTHEX32 #ENDIF ; ; SCALE DOWN CLK BY SAME AMOUNT AS BAUD RATE POP BC ; RESTORE BITS TO SHIFT LD A,B ; PUT IN ACCUM OR A ; TEST FOR ZERO JR Z,SIO_INITDEV1D ; IF ZERO, NO SHIFT, SKIP SIO_INITDEV1C: SRL D ; 32 BIT RIGHT SHIFT RR E ; ... RR H ; ... RR L ; ... DJNZ SIO_INITDEV1C ; LOOP UNTIL DONE SHIFTING SIO_INITDEV1D: ; #IF (SIODEBUG) PRTS(" BAUD=$") CALL PRTHEX32 #ENDIF ; POP DE ; RECOVER CLOCK EX DE,HL ; SWAP CLOCK & BAUD FOR DIV ; *** HANDLE DIVIDE BY ZERO??? *** CALL DIV16 ; BC := HL/DE == TARGET DIVISOR ; #IF (SIODEBUG) PRTS(" DIV=$") CALL PRTHEXWORD #ENDIF ; #IF (CTCENABLE) ; LD A,(IY+13) ; GET CTC CHANNEL INC A ; $FF -> 0 JR Z,SIO_ADJDONE ; NO CTC CHANNEL, BYPASS ; ; HERE WE NEED TO ACCOUNT FOR A SPECIAL CASE OF THE CTC. ; IF THE CTC TRIGGER RATE IS MORE THAN HALF OF THE CTC CLOCK, ; THEN THE CTC WILL ONLY COUNT EVERY OTHER TRIGGER PULSE. ; IN THIS SITUATION, WE NEED TO CUT THE DIVISOR IN HALF ; TO ACCOUNT FOR THIS. ; FOR NOW, I JUST TEST TO SEE IF THE CTC TRIGGER AND THE CTC ; CLOCK ARE THE SAME. I DOUBT THERE IS ANY REALISTIC ; SCENARIO WHERE THE TRIGGER IS GREATER THAN HALF THE ; CLOCK BUT ALSO NOT EQUAL TO THE CLOCK. ; I DON'T DEFINITELY KNOW THE CTC CLOCK FREQ, BUT ASSUME IT ; IS THE SAME AS THE CPU CLOCK, WHICH IT SHOULD BE. ; FINALLY, NOTE THAT I AM COMPARING AGAINST THE CPU SPEED ; DECLARED IN THE BUILD CONFIG, NOT THE DYNAMICALLY MEASURED ; CPU SPEED. THIS IS CORRECT BECAUSE WE ARE REALLY TRYING TO ; TEST IF THE CPU CLOCK AND THE TRIGGER FREQ ARE THE *SAME*. ; ONLY COMPARING THE HIGH WORD VALUES, THAT SHOULD BE ENOUGH. ; LD A,$FF & (CPUOSC >> 24) ; HIGH BYTE OF CPU FREQ CP (IY+12) ; CP TO HIGH BYTE OF TRG JR NZ,SIO_ADJDONE ; IF NE, SKIP ADJUSTMENT LD A,$FF & (CPUOSC >> 16) ; HIGH BYTE OF CPU FREQ CP (IY+11) ; CP TO HIGH BYTE OF TRG JR NZ,SIO_ADJDONE ; IF NE, SKIP ADJUSTMENT ; SRL B ; RIGHT SHIFT HL RR C ; ... TO DIVIDE BY 2 JR NC,SIO_ADJDONE ; DONE IF NO CARRY ; ; IF CARRY, RESULTANT DIVISOR IS UNWORKABLE POP DE ; POP STACK JR SIO_INITFAIL ; AND FAIL ; *** CHECK FOR CARRY??? *** ; #IF (SIODEBUG) PRTS(" DIV=$") CALL PRTHEXWORD #ENDIF ; SIO_ADJDONE: ; #ENDIF ; ; NOW THAT WE HAVE THE TARGET BAUD RATE DIVISOR, WE WILL ; ATTEMPT TO IMPLEMENT IT. THE SIO ITSELF CAN APPLY ; A DIVISOR OF 1, 16, 32, OR 64. IF A CTC CHANNEL IS ; CONFIGURED FOR THIS SERIAL PORT, THEN WE CAN ADDITIONALLY ; APPLY A SCALER OF 1-256. ; ; WE START BY DETERMINING THE MAXIMUM POSSIBLE SIO ; SCALING. ; ; WARNING: IF THE INCOMING SIO CLOCK IS THE SAME AS THE ; CPU CLOCK AND WE USE THE 1:1 DIVISOR, THE SIO WILL NOT ; WORK WELL. ; PUSH BC ; MOVE WORKING DIVISOR VALUE POP HL ; ... TO HL LD A,L ; LOAD LSB OF DIVISOR LD BC,$0004 ; SHIFT 0 BITS / SIO WR4 DIV 1 LD A,L ; LOAD LSB OF DIVISOR AND %00001111 ; DIV 16 POSSIBLE? JR NZ,SIO_INITDEV2 ; NOPE, DONE TRYING LD BC,$0444 ; SHIFT 4 BITS / SIO WR4 DIV 16 LD A,L ; LOAD LSB OF DIVISOR AND %00011111 ; DIV 32 POSSIBLE? JR NZ,SIO_INITDEV2 ; NOPE, DONE TRYING LD BC,$0584 ; SHIFT 5 BITS / SIO WR4 DIV 32 LD A,L ; LOAD LSB OF DIVISOR AND %00111111 ; DIV 32 POSSIBLE? JR NZ,SIO_INITDEV2 ; NOPE, DONE TRYING LD BC,$06C4 ; SHIFT 6 BITS / SIO WR4 DIV 64 ; ; NOW APPLY THE SIO DIVISOR TO THE WORKING DIVISOR ; AND SAVE THE RESULTANT SIO REGISTER VALUE TO APPLY LATER. SIO_INITDEV2: ; SHIFT BITS XOR A ; ZERO ACCUM OR B ; ZERO BITS TO SHIFT? JR Z,SIO_INITDEV4 ; BYPASS SHIFTING IF SO SIO_INITDEV3: SRL H ; SHIFT HL RIGHT BY RR L ; ONE BIT DJNZ SIO_INITDEV3 ; UNTIL ALL BITS DONE SIO_INITDEV4: LD B,C ; MOVE SIO WR4 VALUE TO B ; POP DE ; RESTORE DE = SERIAL CONFIG ; #IF (SIODEBUG) PUSH BC PUSH HL POP BC PRTS(" CTCDIV=$") CALL PRTHEXWORD POP BC #ENDIF ; #IF (CTCENABLE) ; LD A,(IY+13) ; GET CTC CHANNEL INC A ; $FF -> 0 JR Z,SIO_NOCTC ; NO CTC CHANNEL, BYPASS ; ; HL HAS THE DIVISOR THAT WE WANT TO PROGRAM INTO THE ; DESIGNATED CTC CHANNEL. HOWEVER, THE CTC REGISTER IS ONE ; BYTE. A VALUE OF 0 MEANS 256. SO WE NEED TO VALIDATE ; THAT HL IS BETWEEN 1 AND 256. DEC HL ; 1-256 -> 0-255 LD A,H ; MSB NOW MUST BE ZERO OR A ; SET FLAGS JR NZ,SIO_INITFAIL ; IF ANY BIT SET, FAIL INC HL ; RESTORE HL ; ; ALL GOOD. PROGRAM THE CTC CHANNEL LD A,(IY+13) ; GET CTC CHANNEL ADD A,CTCBASE ; ADD TO CTC BASE PORT ADR #IF (SIODEBUG) PRTS(" CTC=$") CALL PRTHEXBYTE #ENDIF LD C,A ; AND PUT IN C FOR I/O LD A,%01010111 ; CTCC CONTROL WORD VALUE ; |||||||+-- 1=CONTROL WORD FLAG ; ||||||+--- 1=SOFTWARE RESET ; |||||+---- 1=TIME CONSTANT FOLLOWS ; ||||+----- 0=AUTO TRIGGER WHEN TIME CONST LOADED ; |||+------ 1=RISING EDGE TRIGGER ; ||+------- 0=PRESCALER OF 16 (NOT USED) ; |+-------- 1=COUNTER MODE ; +--------- 0=NO INTERRUPTS OUT (C),A ; PREP CTC CHANNEL OUT (C),L ; SET CTC TIMER CONSTANT JR SIO_INITBROK ; AND REJOIN MAIN SETUP ; #ENDIF ; SIO_NOCTC: ; IF THERE IS NO CTC, THEN THE REMAINING DIVISOR ; NEEDS TO BE EXACTLY 1 OR WE HAVE A PROBLEM. LD A,L ; GET REMAINING DIVISOR DEC A ; 1 -> 0 JR Z,SIO_INITBROK ; FAIL IF NOT 1 ; 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,D ; HI WORD OF CONFIG AND %00100000 ; BIT 5 IS AUTO-CTS LD H,A ; SAVE IN H LD A,L ; DATA BITS AND $C0 ; CLEAR OTHER BITS OR $01 ; RX ENABLE OR H ; COMBINE WITH AUTO-CTS ; 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: ; #IF (CTCENABLE) ; ; CHECK IF A CTC CHANNEL IS CONFIGURED LD A,(IY+13) ; GET CTC CHANNEL INC A ; $FF -> 0 JR Z,SIO_INITSAFE2 ; NO CTC CHANNEL, BYPASS ; ; IF A CTC CHANNEL IS CONFIGURED, PROGRAM IT FOR ; SIMPLE 1:1 SCALING. LD A,(IY+13) ; GET CTC CHANNEL ADD A,CTCBASE ; ADD TO CTC BASE PORT ADR LD C,A ; AND PUT IN C FOR I/O LD A,%01010111 ; CTCC CONTROL WORD VALUE OUT (C),A ; PREP CTC CHANNEL LD A,1 ; TIMER CONSTANT IS 1 OUT (C),A ; SET CTC TIMER CONSTANT ; #ENDIF ; SIO_INITSAFE2: ; SETUP DEFAULT VALUES FOR SIO REGISTERS LD HL,SIO_INITDEFS LD DE,SIO_INITVALS LD BC,SIO_INITLEN LDIR ; SIO_INITGO: ; ; SET INTERRUPT VECTOR OFFSET WR2 ; #IF ((SIOINTS) & (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 SYSCHKERR(ERR_NOUNIT) ; IMPOSSIBLE SITUATION RET 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 ((SIOINTS) & (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 REGISTERS 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 LD H,(IY+14) ; H := MODE LD L,(IY+3) ; L := BASE I/O ADDRESS 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 $F1 ; AVOID VECTOR STATUS BITS 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 $F1 ; AVOID VECTOR STATUS BITS CP $FF & $F1 ; COMPARE RET NZ ; ABORT IF NOT ZERO ; AVOID SCC (SIO HAS NO R12) PUSH BC ; SAVE PORT/REG LD B,12 ; R12 (LOW BYTE OF BRG) LD A,$A5 ; TEST VAL CALL SIO_WR ; WRITE IT CALL SIO_RD ; READ IT POP BC ; RESTORE PORT/REG CP $A5 ; CORRECT VALUE RETURNED? JR NZ,SIO_PROBECHIP1 ; MISMATCH EXPECTED OR $FF ; SIGNAL FAILURE RET ; RETURN WITH NZ SIO_PROBECHIP1: XOR A ; SIGNAL SUCCESS RET ; DONE ; ; 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 "$" SIO_STR_SIO .DB "8440$" ; ; WORKING VARIABLES ; SIO_DEV .DB 0 ; DEVICE NUM USED DURING INIT SIO_MAP .DB 0 ; CHIP PRESENCE BITMAP ; #IF ((SIOINTS) & (INTMODE > 0)) ; ; 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 ; #ELSE ; SIO0A_RCVBUF .EQU 0 SIO0B_RCVBUF .EQU 0 ; #IF (SIOCNT >= 2) SIO1A_RCVBUF .EQU 0 SIO1B_RCVBUF .EQU 0 #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 & $FFFF ; CLOCK FREQ AS .DW SIO0ACLK >> 16 ; ... DWORD VALUE .DB SIO0ACTCC ; CTC CHANNEL .DB SIO0MODE ; MODE ; DEVECHO "SIO MODE=" #IF (SIO0MODE == SIOMODE_STD) DEVECHO "STD" #ENDIF #IF (SIO0MODE == SIOMODE_RC) DEVECHO "RC" #ENDIF #IF (SIO0MODE == SIOMODE_SMB) DEVECHO "SMB" #ENDIF #IF (SIO0MODE == SIOMODE_ZP) DEVECHO "ZP" #ENDIF #IF (SIO0MODE == SIOMODE_Z80R) DEVECHO "Z80R" #ENDIF DEVECHO ", IO=" DEVECHO SIO0BASE DEVECHO ", CHANNEL A" #IF ((SIOINTS) & (INTMODE > 0)) DEVECHO ", INTERRUPTS ENABLED" #ENDIF DEVECHO "\n" ; 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 & $FFFF ; CLOCK FREQ AS .DW SIO0BCLK >> 16 ; ... DWORD VALUE .DB SIO0BCTCC ; CTC CHANNEL .DB SIO0MODE ; MODE ; DEVECHO "SIO MODE=" #IF (SIO0MODE == SIOMODE_STD) DEVECHO "STD" #ENDIF #IF (SIO0MODE == SIOMODE_RC) DEVECHO "RC" #ENDIF #IF (SIO0MODE == SIOMODE_SMB) DEVECHO "SMB" #ENDIF #IF (SIO0MODE == SIOMODE_ZP) DEVECHO "ZP" #ENDIF #IF (SIO0MODE == SIOMODE_Z80R) DEVECHO "Z80R" #ENDIF DEVECHO ", IO=" DEVECHO SIO0BASE DEVECHO ", CHANNEL B" #IF ((SIOINTS) & (INTMODE > 0)) DEVECHO ", INTERRUPTS ENABLED" #ENDIF DEVECHO "\n" ; #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 & $FFFF ; CLOCK FREQ AS .DW SIO1ACLK >> 16 ; ... DWORD VALUE .DB SIO1ACTCC ; CTC CHANNEL .DB SIO1MODE ; MODE ; DEVECHO "SIO MODE=" #IF (SIO1MODE == SIOMODE_STD) DEVECHO "STD" #ENDIF #IF (SIO1MODE == SIOMODE_RC) DEVECHO "RC" #ENDIF ; #IF (SIO1MODE == SIOMODE_SMB) DEVECHO "SMB" #ENDIF #IF (SIO1MODE == SIOMODE_ZP) DEVECHO "ZP" #ENDIF #IF (SIO1MODE == SIOMODE_Z80R) DEVECHO "Z80R" #ENDIF DEVECHO ", IO=" DEVECHO SIO1BASE DEVECHO ", CHANNEL A" #IF ((SIOINTS) & (INTMODE > 0)) DEVECHO ", INTERRUPTS ENABLED" #ENDIF DEVECHO "\n" ; ; 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 & $FFFF ; CLOCK FREQ AS .DW SIO1BCLK >> 16 ; ... DWORD VALUE .DB SIO1BCTCC ; CTC CHANNEL .DB SIO1MODE ; MODE ; DEVECHO "SIO MODE=" #IF (SIO1MODE == SIOMODE_STD) DEVECHO "STD" #ENDIF #IF (SIO1MODE == SIOMODE_RC) DEVECHO "RC" #ENDIF #IF (SIO1MODE == SIOMODE_SMB) DEVECHO "SMB" #ENDIF #IF (SIO1MODE == SIOMODE_ZP) DEVECHO "ZP" #ENDIF #IF (SIO1MODE == SIOMODE_Z80R) DEVECHO "Z80R" #ENDIF DEVECHO ", IO=" DEVECHO SIO1BASE DEVECHO ", CHANNEL B" #IF ((SIOINTS) & (INTMODE > 0)) DEVECHO ", INTERRUPTS ENABLED" #ENDIF DEVECHO "\n" ; #ENDIF ; SIO_CFGCNT .EQU ($ - SIO_CFG) / SIO_CFGSIZ ; ;-------------------------------------------------------------------------------------------------- ; HBIOS MODULE TRAILER ;-------------------------------------------------------------------------------------------------- ; END_SIO .EQU $ SIZ_SIO .EQU END_SIO - ORG_SIO ; MEMECHO "SIO occupies " MEMECHO SIZ_SIO MEMECHO " bytes.\n"