;___XIO________________________________________________________________________________________________________________ ; ; DIRECT SERIAL I/O ; ; PROVIDES INTERFACE TO PLATFORM BASE SERIAL I/O DEVICE ; ALLOWS USER MESSAGING/INTERACTION PRIOR TO AND DURING HBIOS INIT ;______________________________________________________________________________________________________________________ ; ; #IF ((PLATFORM == PLT_SBC) | (PLATFORM == PLT_ZETA) | (PLATFORM == PLT_ZETA2)) ; SIO_RBR .EQU UART0IOB + 0 ; DLAB=0: RCVR BUFFER REG (READ ONLY) SIO_THR .EQU UART0IOB + 0 ; DLAB=0: XMIT HOLDING REG (WRITE ONLY) SIO_IER .EQU UART0IOB + 1 ; DLAB=0: INT ENABLE REG SIO_IIR .EQU UART0IOB + 2 ; INT IDENT REGISTER (READ ONLY) SIO_FCR .EQU UART0IOB + 2 ; FIFO CONTROL REG (WRITE ONLY) SIO_LCR .EQU UART0IOB + 3 ; LINE CONTROL REG SIO_MCR .EQU UART0IOB + 4 ; MODEM CONTROL REG SIO_LSR .EQU UART0IOB + 5 ; LINE STATUS REG SIO_MSR .EQU UART0IOB + 6 ; MODEM STATUS REG SIO_SCR .EQU UART0IOB + 7 ; SCRATCH REGISTER SIO_DLL .EQU UART0IOB + 0 ; DLAB=1: DIVISOR LATCH (LS) SIO_DLM .EQU UART0IOB + 1 ; DLAB=1: DIVISOR LATCH (MS) ; #ENDIF XIO_INIT: ; MINIMAL UART INIT #IF (PLATFORM == PLT_UNA) ; SHOULD UNA SERIAL I/O BE RESET HERE??? #ENDIF #IF ((PLATFORM == PLT_N8) | (PLATFORM == PLT_MK4)) ; INIT ASCI0 WITH BASIC VALUES AND FAILSAFE DIVISOR LD A,$66 ; IGNORE CTS/DCD, NO BREAK DETECT OUT0 (Z180_ASEXT0),A ; -> ASEXT0 LD A,$64 ; ENABLE XMT/RCV, 8 DATA, NO PARITY, 1 STOP OUT0 (Z180_CNTLA0),A ; -> CNTLA0 LD A,$22 ; FAILSAFE VALUE, 9600 BAUD AT 18.432 MHZ OUT0 (Z180_CNTLB0),A ; -> CNTLB0 ; TRY TO IMPLEMENT CONFIGURED BAUD RATE LD HL,ASCI0CFG ; SERIAL CONFIG WORD LD A,H ; BYTE W/ ENCODED BAUD RATE AND $1F ; ISOLATE BITS LD L,A ; MOVE TO L LD H,0 ; CLEAR MSB CALL XIO_CNTLB ; DERIVE CNTLB VALUE JR NZ,XIO_INIT1 ; BYPASS ON FAILURE LD A,C ; PUT INT C OUT0 (Z180_CNTLB0),A ; AND SET THE VALUE #ENDIF #IF ((PLATFORM == PLT_SBC) | (PLATFORM == PLT_ZETA) | (PLATFORM == PLT_ZETA2)) ;XIO_DIV .EQU (UARTOSC / (16 * CONBAUD)) LD DE,UART0CFG ; SERIAL CONFIG WORD CALL XIO_COMPDIV ; COMPUTE DIVISOR TO BC LD A,$80 ; LCR := DLAB ON OUT (SIO_LCR),A ; SET LCR ;LD A,XIO_DIV % $100 ; BAUD RATE DIVISOR (LSB) LD A,C ; LOW BYTE OF DIVISOR OUT (SIO_DLL),A ; SET DIVISOR (LSB) ;LD A,XIO_DIV / $100 ; BAUD RATE DIVISOR (MSB) LD A,B ; HIGH BYTE OF DIVISOR OUT (SIO_DLM),A ; SET DIVISOR (MSB) LD A,03H ; VALUE FOR LCR AND MCR OUT (SIO_LCR),A ; LCR := 3, DLAB OFF, 8 DATA, 1 STOP, NO PARITY OUT (SIO_MCR),A ; MCR := 3, DTR ON, RTS ON LD A,6 ; DISABLE & RESET FIFO'S OUT (SIO_FCR),A ; DO IT #ENDIF XIO_INIT1: RET ; XIO_CRLF: ; OUTPUT A NEWLINE LD A,13 ; A = CR CALL XIO_OUTC ; WRITE IT LD A,10 ; A = LF JR XIO_OUTC ; WRITE IT ; XIO_SPACE: ; OUTPUT A SPACE CHARACTER LD A,' ' JR XIO_OUTC ; XIO_DOT: ; OUTPUT A DOT (MARK PROGRESS) LD A,'.' ; XIO_OUTC: ; OUTPUT BYTE IN A #IF (PLATFORM == PLT_UNA) PUSH DE ; PRESERVE DE LD BC,$0012 ; UNA UNIT = 0, FUNC = WRITE CHAR LD E,A ; CHAR TO E CALL $FFFD ; DO IT (RST 08 NOT SETUP YET) POP DE ; RESTORE DE RET ; DONE #ENDIF #IF ((PLATFORM == PLT_N8) | (PLATFORM == PLT_MK4)) PUSH AF ; SAVE INCOMING BYTE XIO_OUTC1: IN0 A,(Z180_STAT0) AND $02 JR Z,XIO_OUTC1 POP AF OUT0 (Z180_TDR0),A RET #ENDIF #IF ((PLATFORM == PLT_SBC) | (PLATFORM == PLT_ZETA) | (PLATFORM == PLT_ZETA2)) PUSH AF ; SAVE INCOMING BYTE XIO_OUTC1: IN A,(SIO_LSR) ; READ LINE STATUS REGISTER AND $20 ; ISOLATE THRE JR Z,XIO_OUTC1 ; LOOP TILL READY (EMPTY) POP AF ; RECOVER BYTE TO WRITE OUT (SIO_THR),A ; WRITE THE CHAR TO UART RET #ENDIF ; XIO_OUTS: ; OUTPUT '$' TERMINATED STRING AT ADDRESS IN HL LD A,(HL) ; GET NEXT BYTE CP '$' ; END OF STRING? RET Z ; YES, GET OUT CALL XIO_OUTC ; OTHERWISE, WRITE IT INC HL ; POINT TO NEXT BYTE JR XIO_OUTS ; AND LOOP #IF ((PLATFORM == PLT_SBC) | (PLATFORM == PLT_ZETA) | (PLATFORM == PLT_ZETA2)) ; ; COMPUTE DIVISOR TO BC ; XIO_COMPDIV: ; WE WANT TO DETERMINE A DIVISOR FOR THE UART CLOCK ; THAT RESULTS IN THE DESIRED BAUD RATE. ; BAUD RATE = UART CLK / DIVISOR, OR TO SOLVE FOR DIVISOR ; DIVISOR = UART CLK / BAUDRATE. ; THE UART CLOCK IS THE UART OSC PRESCALED BY 16. ALSO, WE CAN ; TAKE ADVANTAGE OF ENCODED BAUD RATES ALWAYS BEING A FACTOR OF 75. ; SO, WE CAN USE (UART OSC / 16 / 75) / (BAUDRATE / 75) ; ; FIRST WE 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 EX DE,HL ; DE := (BAUDRATE / 75), DISCARD HL LD HL,UARTOSC / 16 / 75 ; HL := (UART OSC / 16 / 75) JP XIO_DIV16 ; BC := HL/DE == DIVISOR AND RETURN ; #ENDIF ; ; COMPUTE HL / DE = BC W/ REMAINDER IN HL & ZF ; XIO_DIV16: LD A,H ; HL -> AC LD C,L ; ... LD HL,0 ; INIT HL LD B,16 ; INIT LOOP COUNT XIO_DIV16A: SCF RL C RLA ADC HL,HL SBC HL,DE JR NC,XIO_DIV16B ADD HL,DE DEC C XIO_DIV16B: DJNZ XIO_DIV16A ; LOOP AS NEEDED LD B,A ; AC -> BC LD A,H ; SET ZF OR L ; ... BASED ON REMAINDER RET ; DONE ; ; ; #IF ((PLATFORM == PLT_N8) | (PLATFORM == PLT_MK4)) ; ; DERIVE A CNTLB VALUE BASED ON AN ENCODED BAUD RATE AND CURRENT CPU SPEED ; ENTRY: HL = ENCODED BAUD RATE ; EXIT: C = CNTLB VALUE, A=0, Z SET INDICATES SUCCESS ; ; GIVEN DIVISOR = CLK HZ / BAUD ; LET LOOKUP = DIVISOR / 160 ; LOOKUP = CLK / BAUD / 160 ; LET KCLK = CLK / 1000, SO CLK = KCLK * 1000 ; LOOKUP = KCLK * 1000 / BAUD / 160 ; OR, LOOKUP = (KCLK / 12) / (BAUD / 75) ; ; SO, WE USE (CPUKHZ / 12) / (BAUD RATE / 75) TO GET LOOKUP VALUE ; THEN LOOKUP THE CORRECT CNTLB0 VALUE ; XIO_CNTLB: LD DE,1 ; USE DECODE CONSTANT OF 1 TO GET BAUD RATE ALREADY DIVIDED BY 75 CALL DECODE ; DECODE THE BAUDATE INTO DE:HL, DE IS DISCARDED RET NZ ; ABORT ON ERROR PUSH HL ; HL HAS (BAUD / 75), SAVE IT ;LD HL,(HCB + HCB_CPUKHZ) ; GET CPU CLK IN KHZ LD HL,CPUKHZ ; CPU CLK IN KHZ LD DE,12 ; PREPARE TO DIVIDE BY 12 CALL XIO_DIV16 ; BC := (CPU CLK KHZ / 12), REMAINDER IN HL, ZF POP DE ; RESTORE DENOMINATOR JR NZ,XIO_CNTLB2 ; ABORT IF REMAINDER PUSH BC ; MOVE VALUE POP HL ; ... TO HL NUMERATOR CALL XIO_DIV16 ; BC := LOOKUP VALUE, REMAINDER IN HL, ZF JR NZ,XIO_CNTLB2 ; ABORT IF REMAINDER PUSH BC ; MOVE LOOKUP VALUE POP DE ; TO DE LD B,XIO_LKUPCNT ; INIT LOOP COUNT LD HL,XIO_LKUP ; POINT TO START OF TABLE XIO_CNTLB0: LD A,(HL) ; GET BYTE TO COMPARE INC HL ; INCREMENT HL FOR NEXT CP E ; COMPARE LSB JR NZ,XIO_CNTLB1 ; NO MATCH, LOOP LD A,(HL) ; GET BYTE TO COMPARE CP D ; COMPARE MSB JR NZ,XIO_CNTLB1 ; NO MATCH, LOOP ; MATCH FOUND INC HL ; POINT TO CNTLB VALUE LD C,(HL) ; LOAD IN C XOR A ; SIGNAL SUCCESS RET ; AND DONE XIO_CNTLB1: INC HL ; BUMP TO INC HL ; ... NEXT ENTRY DJNZ XIO_CNTLB0 ; LOOP IF MORE TO CHECK XIO_CNTLB2: OR $FF ; NOT FOUND, SET ERROR RET ; AND RETURN ; ; LOOKUP PS BIT PS DIV DR BIT DR DIV SS BITS SS DIV DIVISOR CNTLB ; ------ ------ ------ ------ ------ ------- ------ ------- -------- ; 1 0 10 0 16 0 1 160 XX0X0000 ; 2 0 10 0 16 1 2 320 XX0X0001 ; 3 1 30 0 16 0 1 480 XX1X0000 ; 4 0 10 0 16 2 4 640 XX0X0010 ; 6 1 30 0 16 1 2 960 XX1X0001 ; 8 0 10 0 16 3 8 1280 XX0X0011 ; 12 1 30 0 16 2 4 1920 XX1X0010 ; 16 0 10 0 16 4 16 2560 XX0X0100 ; 24 1 30 0 16 3 8 3840 XX1X0011 ; 32 0 10 0 16 5 32 5120 XX0X0101 ; 48 1 30 0 16 4 16 7680 XX1X0100 ; 64 0 10 0 16 6 64 10240 XX0X0110 ; 96 1 30 0 16 5 32 15360 XX1X0101 ; 128 0 10 1 64 5 32 20480 XX0X1101 ; 192 1 30 0 16 6 64 30720 XX1X0110 ; 256 0 10 1 64 6 64 40960 XX0X1110 ; 384 1 30 1 64 5 32 61440 XX1X1101 ; 768 1 30 1 64 6 64 122880 XX1X1110 ; XIO_LKUP: ; LOOKUP CNTLB VAL .DW 1 \ .DB %00000000 .DW 2 \ .DB %00000001 .DW 3 \ .DB %00100000 .DW 4 \ .DB %00000010 .DW 6 \ .DB %00100001 .DW 8 \ .DB %00000011 .DW 12 \ .DB %00100010 .DW 16 \ .DB %00000100 .DW 24 \ .DB %00100011 .DW 32 \ .DB %00000101 .DW 48 \ .DB %00100100 .DW 64 \ .DB %00000110 .DW 96 \ .DB %00100101 .DW 128 \ .DB %00001101 .DW 192 \ .DB %00100110 .DW 256 \ .DB %00001110 .DW 384 \ .DB %00101101 .DW 768 \ .DB %00101110 ; XIO_LKUPCNT .EQU ($ - XIO_LKUP) / 3 ; #ENDIF