#TITLE "Serial I/O for the NEC-APC" #SUBTTL "Equates and Definitions" #PAGE 132,66 ; ; SIOAPC.A ; Written by: John E. Lauber ; MODULE "SIOAPC" ; module ID ; #INCLUDE "APCEQU" ; common APC equivalences #INCLUDE "DREQUATE" ; common driver equivalences ; ; Bit definition of the status word. ; TXRDY == 1<<0 ; transmitter ready RXRDY == 1<<1 ; receiver ready TXE == 1<<2 ; transmitter empty PE == 1<<3 ; parity error OE == 1<<4 ; overrun error FE == 1<<5 ; framing error SYNDET == 1<<6 ; sync detect DSR == 1<<7 ; data set ready ; ; Befault configuration of the mode instruction word. ; M8251 == 0X4E ; 1 stop, no parity, 8-bit word, X16 ; ; Bit definition of the command instruction word. ; TXEN == 1<<0 ; transmit enable DTR == 1<<1 ; data terminal ready RXE == 1<<2 ; receiver enable SBRK == 1<<3 ; send break ER == 1<<4 ; error reset RTS == 1<<5 ; request to send IR == 1<<6 ; internal reset EH == 1<<7 ; enter hunt mode ; ; Default configuration of the command instruction word. ; C8251 == RTS|ER|RXE|DTR|TXEN ; refer to above symbols ; #SUBTTL "Driver Data Area" #PAGE ; LOC Data# ; locate in data segment ; FCNTBL == . ; driver function lookup table WORD SERST ; serial status WORD SERIN ; serial input WORD SEROUT ; serial output WORD SERSBR ; serial set baud rate WORD SERRBR ; serial return baud rate WORD SERSMC ; serial set MODEM controls WORD SERRMC ; serial return MODEM controls NMBFCN == (.-FCNTBL)/2 ; number of valid functions ; LOC 0 ; IOTMOD == . ; I/O table entry model DATA: RES WORD 1 ; uart data port addr STAT: RES WORD 1 ; uart status port addr MSKRS: RES WORD 1 ; uart mask/read signal port addr WSIG: RES WORD 1 ; uart write signal port addr TMRMOD: RES WORD 1 ; Timer port address TIMER: RES WORD 1 ; timer port addr TMRSET: RES BYTE 1 ; set timer value for mode port RDAMSK: RES BYTE 1 ; receive data available mask RDACP: RES BYTE 1 ; receive data available ready TBEMSK: RES BYTE 1 ; trans buffer empty mask TBECP: RES BYTE 1 ; trans buffer empty ready SINSPH: RES WORD 3 ; serial input semaphore SINPOL: RES WORD 1 ; serial input poll routine addr BUFSIZ: RES WORD 1 ; serial buffer size BUFPNT: RES WORD 1 ; base of serial input buffer SINPNT: RES WORD 1 ; serial buffer in pointer SOTPNT: RES WORD 1 ; serial buffer out pointer SOTSPH: RES WORD 3 ; serial output semaphore SOTPOL: RES WORD 1 ; serial output poll routine addr SOCHAR: RES BYTE 1 ; output char buffer SERBRT: RES BYTE 1 ; current baud rate setting CURMOD: RES BYTE 1 ; current device operating mode SFLAGS: RES BYTE 1 ; serial channel flags IOTLEN == .-IOTMOD ; length of one entry ; ; Bit definition of serial channel flag byte. ; UACT == 1<<0 ; uart active flag ; RELOC ; ; Base of serial I/O tables. ; IOTBL == . ; SIOCH0 == . ; serial channel 0 WORD S0_DATA,S0_STAT ; data and status/command ports WORD S0_RS,S0_WS ; mask/read signal and write signal WORD IT_MOD0 ; Timer mode port address WORD IT_CNT0 ; timer port addr BYTE 0X76 ; set timer value for mode port BYTE RXRDY,RXRDY ; mask, value BYTE TXRDY,TXRDY ; mask, value WORD 0 ; input semaphore WORD . WORD .-2 WORD SI0POL ; input poll routine S0BSZ: WORD 64 ; buffer size WORD 0,0,0 ; base, in and out pointers WORD 0 ; output semaphore WORD . WORD .-2 WORD SO0POL ; output poll routine BYTE 0 ; output char BYTE 0XAF ; current baud rate S0MODE: BYTE M8251 ; line control value BYTE 0 ; serial channel flags ; ; SIOCH1 == . ; serial channel 1 WORD S1_DATA,S1_STAT ; data and status/command ports WORD S1_RS,S1_WS ; mask/read signal and write signal WORD IT_MOD1 ; Timer mode port address WORD IT_CNT1 ; timer port addr WORD 0X36 ; set timer value for mode port BYTE RXRDY,RXRDY ; mask, value BYTE TXRDY,TXRDY ; mask, value WORD 0 ; input semaphore WORD . WORD .-2 WORD SI1POL ; serial input poll routine S1BSZ: WORD 64 ; buffer size WORD 0,0,0 ; base, in and out pointers WORD 0 ; output semaphore WORD . WORD .-2 WORD SO1POL ; output poll routine BYTE 0 ; output char BYTE 0XAF ; current baud rate S1MODE: BYTE M8251 ; line control value BYTE 0 ; serial channel flags ; ; STMAP maps the NEC SIO status into the TurboDOS modem status. It ; is assumed that the NEC SIO status has CTS in bit 3, RI in bit 2, ; CD in bit 1, and DSR in bit 0. ; STMAP: BYTE 0 ; nothing on BYTE 4 ; DSR BYTE 2 ; CD BYTE 6 ; DSR, CD BYTE 1 ; RI BYTE 5 ; DSR, RI BYTE 3 ; CD, RI BYTE 7 ; DSR, CD, RI BYTE 8 ; CTS BYTE 12 ; CTS, DSR BYTE 10 ; CTS, CD BYTE 14 ; CTS, DSR, CD BYTE 9 ; CTS, RI BYTE 13 ; CTS, DSR, RI BYTE 11 ; CTS, CD, RI BYTE 15 ; CTS, CD, RI, CD ; NMBSIO == (.-IOTBL)/IOTLEN ; number of channels supported ; #SUBTTL "Driver Routines" #PAGE ; LOC Code# ; locate in code segment ; SPINIT:: PUSH DI ; save index reg MOV DI,&IOTBL ; get I/O table base MOV CX,=NMBSIO ; get number of serial channels __LP: PUSH CX ; save count reg CALL INIT8251 ; initialize the channel MOV DX,STAT[DI] ; get uart status port addr IN AL,DX ; read it AND AL,TBEMSK[DI] ; Check to see if device there CMP AL,TBECP[DI] JNE __C ; if not, continue OR SFLAGS[DI],=UACT ; set uart active flag MOV BX,BUFSIZ[DI] ; get buffer size CALL ALLOC# ; allocate some memory MOV BUFPNT[DI],BX ; set buffer base address MOV SINPNT[DI],BX ; set buffer input pointer MOV SOTPNT[DI],BX ; set buffer output pointer MOV DX,SINPOL[DI] ; get input poll routine addr CALL LNKPOL# ; and attach to poll list __C: POP CX ; restore the count ADD DI,=IOTLEN ; index to next table entry LOOP __LP ; and loop for number of channels POP DI ; restore index reg RET ; and exit ; INIT8251: MOV DX,MSKRS[DI] ; get uart mask port addr MOV AL,=0X07 ; Set mask for xmit, rcv, ctl OUT DX,AL ; dissable all interrupts CALL DELAY MOV DX,STAT[DI] ; get uart command/mode port addr MOV AL,=0X80 ; get dummy command OUT DX,AL ; send it CALL DELAY OUT DX,AL CALL DELAY MOV AL,=IR ; get internal reset bit OUT DX,AL ; clear device to known state CALL DELAY MOV AL,CURMOD[DI] ; get current operating mode OUT DX,AL ; and send it CALL DELAY MOV AL,=C8251 ; get default command word OUT DX,AL ; and send it CALL DELAY MOV DX,WSIG[DI] ; Clear out write signal XOR AL,AL OUT DX,AL ;;; RET ; all done ; ; Delay for a few microseconds. This saves the poor 8251 from being ; overrun by the big, fast 8086 at init time. God helps those who ; help themselves. (This does not seem necessary for the SIO on ; the main I/O board, but the SIO on the auxilary board gets very ; sick without this!) ; DELAY: MOV CX,=0X100 ; This is plenty long LOOP $ RET ; ; ; Driver entry point. ; COMDRV:: SERIAL:: XOR AL,AL ; Setup bad status, in case CMP CH,=NMBSIO ; test for valid channel number JAE __X ; if invalid, continue PUSH DI ; save index reg MOV DI,=IOTBL ; get base of tables MOV AL,=IOTLEN ; get entry length MUL CH ; index as per channel ADD DI,AX ; point to channel's entry TEST SFLAGS[DI],=UACT ; Is this port actually there ? JZ __X CALL __O ; do the operation POP DI ; restore index reg __X: RET ; all done ; __O: CMP DL,=10 ; function = 10? JZ SEROUT ; if so, continue CMP DL,=NMBFCN ; check for valid function JAE __X ; if invalid, continue MOV BL,DL ; move to reg XOR BH,BH ; make double length ADD BX,BX ; times 2 for addr table JMPI FCNTBL[BX] ; execute function ; ; Serial status entry point. ; SERST: CALL CHKBUF ; check the buffer RET ; done ; ; Serial input entry point. ; SERIN: MOV BX,&SINSPH ; get input semaphore offset ADD BX,DI ; point to it CALL WAIT# ; dispatch if neccessary CALL CHKBUF ; check the buffer OR AL,AL ; set flags JZ SERIN ; if false alarm, try again MOV SOTPNT[DI],BX ; update output pointer MOV AL,CL ; input char to reg RET ; done ; ; Serial output entry point. ; SEROUT: MOV BX,DX ; Save function code TEST SERBRT[DI],=0X40 ; Should we wait for CTS? JZ __1 MOV DX,MSKRS[DI] ; Yes, check real CTS value IN AL,DX TEST AL,=4 ; Is CTS on ? JZ __2 __1: MOV DX,STAT[DI] ; get uart status port addr IN AL,DX ; and read it AND AL,TBEMSK[DI] ; mask status CMP AL,TBECP[DI] ; check ready value JNZ __2 ; if not ready, continue MOV DX,DATA[DI] ; get uart data port addr MOV AL,CL ; get output char OUT DX,AL ; and send it OR AL,=0XFF ; set good status __X: RET ; and exit ; __2: MOV AL,=10 ; load constant SUB AL,BL ; function = 10? JZ __X ; if so, exit with AL=0 MOV SOCHAR[DI],CL ; save output char MOV DX,SOTPOL[DI] ; get output poll routine addr CALL LNKPOL# ; and attack to poll list MOV BX,&SOTSPH ; get semaphore offset ADD BX,DI ; point to it JMP WAIT# ; and dispatch untill ready ; ; Serial output polling routines. ; SO0POL: WORD 0 WORD 0 ; PUSH DI ; save reg MOV DI,&SIOCH0 ; load channel table addr JMPS SOPCOM ; and join common ; SO1POL: WORD 0 WORD 0 ; PUSH DI ; save reg MOV DI,&SIOCH1 ; load channel table addr ; SOPCOM: MOV DX,STAT[DI] ; get uart status port addr IN AL,DX ; read it AND AL,TBEMSK[DI] ; mask it CMP AL,TBECP[DI] ; compare with ready value JNZ __X ; if not ready, continue MOV DX,DATA[DI] ; get uart data port addr MOV AL,SOCHAR[DI] ; get waiting char OUT DX,AL ; and send it MOV BX,SOTPOL[DI] ; get poll routine addr CALL UNLINK# ; and remove from poll list MOV BX,&SOTSPH ; get semaphore offset ADD BX,DI ; point to it CALL SIGNAL# ; and signal the event __X: POP DI ; restore index reg RET ; and exit ; ; Check the input buffer. ; CHKBUF: MOV BX,SOTPNT[DI] ; get output pointer MOV AX,SINPNT[DI] ; and input pointer SUB AX,BX ; compare the values JZ __X ; if equal, continue INC BX ; bump the pointer MOV AX,BUFPNT[DI] ; get buffer base addr ADD AX,BUFSIZ[DI] ; point to last addr+1 CMP AX,BX ; buffer wrap? JNZ __1 ; if not, continue MOV BX,BUFPNT[DI] ; else, wrap the buffer __1: MOV CL,[BX] ; get the char OR AL,=0XFF ; set ready status __X: RET ; done ; ; Serial input polling routines. ; SI0POL: WORD 0 WORD 0 ; PUSH DI ; save reg MOV DI,=SIOCH0 ; load table addr JMPS SIPCOM ; and join common code ; SI1POL: WORD 0 WORD 0 ; PUSH DI ; save reg MOV DI,=SIOCH1 ; load table addr ; SIPCOM: MOV DX,STAT[DI] ; get uart status port addr IN AL,DX ; read it AND AL,RDAMSK[DI] ; mask it CMP AL,RDACP[DI] ; compare to ready value JNZ __X ; if not ready, continue MOV DX,DATA[DI] ; get uart data port addr IN AL,DX ; read the data MOV CL,AL ; hold in reg TEST SERBRT[DI],=1<<5 ; transmit only device? JNZ __X ; if so, just eat the char MOV BX,SINPNT[DI] ; get buffer input pointer TEST SERBRT[DI],=1<<7 ; attention char enabled? JZ __1 ; if not, continue AND CL,=0X7F ; strip parity bit CMP CL,ATNCHR# ; attention request? JNZ __1 ; if not, continue MOV SOTPNT[DI],BX ; flush the buffer __1: INC BX ; bump input pointer MOV AX,BUFPNT[DI] ; get buffer base addr ADD AX,BUFSIZ[DI] ; calc last addr+1 CMP AX,BX ; buffer wrap? JNZ __2 ; if not, continue MOV BX,BUFPNT[DI] ; else, wrap the buffer __2: MOV AX,SOTPNT[DI] ; get output pointer CMP AX,BX ; buffer full? JZ __X ; if so, exit without signalling MOV [BX],CL ; write char into buffer MOV SINPNT[DI],BX ; update input pointer MOV BX,&SINSPH ; get semaphore offset addr ADD BX,DI ; point to it CALL SIGNAL# ; signal the char __X: POP DI ; restore reg RET ; and exit ; ; Set serial baut rate entry point. ; SERSBR: MOV SERBRT[DI],CL ; set table variable ; ; Set uPD8253 counter routine. ; MOV BL,CL ; get baud rate variable AND BL,=0X0F ; mask to baud rate bits XOR BH,BH ; make double length ADD BX,BX ; times 2 for table lookup CS MOV BX,BRTBL[BX] ; load timer constant MOV DX,TMRMOD[DI] ; get timer mode set reg MOV AL,TMRSET[DI] ; get timer set value OUT DX,AL ; and send it MOV DX,TIMER[DI] ; get timer port addr MOV AL,BL ; get LSB of count OUT DX,AL ; send it MOV AL,BH ; get MSB of count OUT DX,AL ; send it RET ; all done ; ; Baud rate divisor constants table. ; BRTBL: WORD 3072 ; 50 baud WORD 2048 ; 75 baud WORD 1396 ; 110 baud WORD 1142 ; 134.5 baud WORD 1024 ; 150 baud WORD 512 ; 300 baud WORD 256 ; 600 baud WORD 128 ; 1200 baud WORD 85 ; 1800 baud WORD 76 ; 2000 baud WORD 64 ; 2400 baud WORD 42 ; 3600 baud WORD 32 ; 4800 baud WORD 21 ; 7200 baud WORD 16 ; 9600 baud WORD 8 ; 19200 baud ; ; Return serial baud rate entry point. ; SERRBR: MOV AL,SERBRT[DI] ; get current setting RET ; all done ; ; Set MODEM controls entry point. ; SERSMC: MOV DX,STAT[DI] ; get uart status port addr MOV AL,=C8251 ; get current uart mode setting AND AL,=~(DTR|RTS) ; clear current bits TEST CL,=1<<7 ; RTS flaged? JZ __1 ; if not, continue OR AL,=RTS ; set RTS bit __1: TEST CL,=1<<6 ; DTR flaged? JZ __2 ; if not, continue OR AL,=DTR ; set DTR bit __2: OUT DX,AL ; update the uart RET ; all done ; ; Return MODEM controls entry point. ; SERRMC: MOV DX,STAT[DI] ; get uart status port addr IN AL,DX ; read it SHL AL,=1 ; Save DSR bit in carry MOV DX,MSKRS[DI] ; Get auxilary status IN AL,DX RCL AL,=1 ; Shift DSR into low bit AND AL,=0X0F ; Kill insigificant bits LEA BX,STMAP ; Get status for TDos XLAT RET ; END