honzo@4gl.UUCP (Honzo Svasek) (05/01/87)
Ok Ok Ok ...Here it is, a PD com port device driver for MessDos #! /bin/sh # This is a shell archive, meaning: # 1. Remove everything above the #! /bin/sh line. # 2. Save the resulting text in a file. # 3. Execute the file with /bin/[k]sh to create the files: # async.asm # ioctl.asm # term.c # This archive created: Fri May 1 13:20:14 1987 # By: honzo export PATH; PATH=/bin:$PATH if test -f 'async.asm' then echo shar: will not over-write existing file "'async.asm'" else cat > 'async.asm' << \S!H!A!R page 58,132 ; file: ASYNC.ASM TITLE ASYNC DRIVER FOR MSDOS SUBTTL DESCRIPTION ; ; Loadable asyncrounous device driver for msdos. ; Written by: Mike Higgins ; Copyright (c) April 1984 by The Computer Entomologist. ; ; Permission is hearby granted to use or distribute this software ;without any restrictions. You may make copies for yourself or your ;friends. You may include it in any hardware or software product that you ;sell for profit. ; ; This software is distributed as is, and is not guaranteed to work ;on any particular hardware/software configuration. Furthermore, no ;liability is granted with this software: the user takes responcibility for ;any damage this software may do to his system. ; ; Nasy notices aside, if you have any questions about this software, you ;can reach me at the address below. If you impliment any new features or ;find (and fix!) any bugs, I would be happy to hear from you. ; ; Mike Higgins ; The Computer Entomologist ; P.O. Box 197 ; Duncans Mills, CA 95430 ; ; -Impliments FULL RS232 support for IBM PC and compatable async cards. ; -Includes 128-byte buffers on input and output. ; -Supports Xon/Xoff or hardware handshake. Hardware handshake uses ; DSR or CTS (to throttle output data) All handshake modes are ; treated separately, an can be used in combinations. ; -Bells can be set to echo when the input buffer overflows. ; -An inteligent half-duplex mode is available that echoes characters ; only after MSDOS has requested them. (Typed-ahead answers to ; questions apear AFTER the questions, not as you type them). ; -The 8th bit (parity) can optionally be stripped off on input ; and/or output. ; -Lower case characters can optionally be translated to upper case ; on input and/or output. ; -Control-c and control-s characters are optionally passed through ; the buffer on non-destructive reads so that the buffer does not ; defeat the break command to MSDOS when the buffer is full. ; -Several escape sequences are optionally scanned for on input that ; perform useful tasks when used as a console terminal: ; 1) A soft reset sequence that re-boots the system in the same way ; ALT-SHIFT-DEL does. (Default: control-C control-R control-B). ; 2) A flush input buffer sequence that removes all characters in ; the 128 byte typeahead buffer that have not been read by ; MSDOS yet (Default: control-X). ; 3) A re-draw the last output line sequence. (Default: control-R). ; 4) A delete line sequence that will delete all input back to the ; begening of the last line. (Default: control-U). ; 5) A skip output sequence turns this driver into a NUL device, ; (output data disapears) until the next skip-output sequence is ; types, or until input is requested. (Default: control-O). ; -The IOCTRL read and write function is used to query or change ; baud rates, bits/byte, parity, as well as enabling/disabling all ; the optional features mentioned above. This eliminates the ; necesity of a program that has special knowledge of the system. ; A program to change these features need not know the location of ; the driver or special words in low memory, or even the port ; address of the UART. Instead, all that is needed is the name of ; the device, and the format of an IOCTL write to this driver. ; ; ASSEMBLY INSTRUCTIONS: ; MASM ASYNC,ASYNC,ASYNC,NUL ; LINK ASYNC,ASYNC,ASYNC,NUL ; EXE2BIN ASYNC ; COPY ASYNC.BIN A: (IF NOT THERE ALREADY) ; ADD THE FOLLOWING LINE TO A:CONFIG.SYS: ; DRIVER=ASYNC.BIN ; RE-BOOT YOUR SYSTEM AND IT'S THERE! NOTE: THIS DRIVER ; DOES NOT GET ALONG AT ALL WITH BASICA OR MODE, AND POSSIBLY ; MANY OTHER PCDOS PROGRAMS THAT DO NOT CONFORM TO THE MSDOS ; STANDARDS. BASICA IN PARTICULAR MUCKS UP ALL THE ASYNC CARD ; INTERNAL REGESTERS, REDIRECTS THE HARDWARE VECTOR, AND IT ; NEVER PUTS THEM BACK THE WAY THEY WERE. IF YOU TRY TO USE ; THIS DRIVER AFTER BASICA HAS BEEN IN MEMORY, THE DRIVER WILL ; PROBABLY HANG YOUR SYSTEM. ; ; ***BUGS*** ; ; If your RS232 device continually sends data to the driver, the driver ; will hang when your system boots. (The EPSON RX80 serial card sends ; ^Q's constantly and causes this). The current solution is to leave ; your device turned off until the system boots completely. Then turn ; the RS232 device on after the driver is ready for it, and everything ; works as expected. ; ; There are a lot of hooks in the driver for features that do not work ; yet. (For example, input data throttleing, output conversion of ; tabs to spaces). So don't expect all of these things to work just ; because there are comments about them or bits to set for them. ; Write me if you do impliment any of this stuff. SUBTTL DEFINITIONS PAGE ; ; DEVICE TYPE CODES DEVCHR EQU 08000h ;THIS IS A CHARACTER DEVICE DEVBLK EQU 0H ;THIS IS A BLOCK (DISK) DEVICE DEVIOC EQU 04000H ;THIS DEVICE ACCEPTS IOCTRL REQUESTS DEVNON EQU 02000H ;NON IBM DISK DRIVER DEVSPC EQU 010H ;CONSOLE ACCEPTS SPECIAL INTERUPT 29 DEVCLK EQU 08H ;THIS IS THE CLOCK DEVICE DEVNUL EQU 04H ;THIS IS THE NUL DEVICE DEVSTO EQU 02H ;THIS IS THE CURRENT STANDARD OUTPUT DEVICE DEVSTI EQU 01H ;THIS IS THE STANDARD INPUT DEVICE ; ; ERROR STATUS BITS STSERR EQU 08000H ;GENERAL ERROR, SEE LOWER ORDER BITS FOR REASON STSBSY EQU 0200H ;DEVICE IS BUISY STSDNE EQU 0100H ;REQUEST IS COMPLETED ; ERROR REASON VALUES FOR LOWER ORDER BITS. ERRWP EQU 0 ;WRITE PROTECT ERROR ERRUU EQU 1 ;UNKNOWN UNIT ERRDNR EQU 2 ;DRIVE NOT READY ERRUC EQU 3 ;UNKNOWN COMMAND ERRCRC EQU 4 ;CYCLIC REDUNDANCY CHECK ERROR ERRBSL EQU 5 ;BAD DRIVE REQUEST STRUCTURE LENGTH ERRSL EQU 6 ;SEEK ERROR ERRUM EQU 7 ;UNKNOWN MEDIA ERRSNF EQU 8 ;SECTOR NOT FOUND ERRPOP EQU 9 ;PRINTER OUT OF PAPER ERRWF EQU 10 ;WRITE FAULT ERRRF EQU 11 ;READ FAULT ERRGF EQU 12 ;GENERAL FAILURE ; ; DEFINE THE BIT MEANINGS OF THE OUTPUT STATUS BYTE ; LINIDL EQU 0FFH ;IF ALL BITS OFF, XMITTER IS IDLE. LINXOF EQU 1 ;OUTPUT IS SUSPENDED BY XOFF LINEXP EQU 2 ;XMITTER IS BUISY, INTERUPT EXPECTED. LINSKP EQU 8 ;SKIP OUTPUT UNTIL NEXT SKIPO SEQUENC. LINDSR EQU 10H ;OUTPUT IS SUSPENDED UNTIL DSR COMES ON AGAIN LINCTS EQU 20H ;OUTPUT IS SUSPENDED UNTIL CTS COMES ON AGAIN ; ; BIT DEFINITIONS OF THE INPUT STATUS BYTE ; MODERR EQU 1 ;INPUT LINE ERRORS HAVE BEEN DETECTED. MODOVR EQU 4 ;RECEIVER BUFFER OVERFLOWED, DATA LOST. MODOFF EQU 2 ;DEVICE IS OFFLINE NOW. ; ; DEFINE THE BIT MEANINGS IN THE SPECIAL CHARACTERISTICS WORDS ; ; THE FIRST SPECIAL WORD CONTROLS HOW THE INPUT FROM THE UART IS TREATED ; INDTR EQU 2 ;DTR IS DATA-THROTTLE SIGNAL. INRTS EQU 4 ;RTS IS DATA-THROTTLE SIGNAL. INXON EQU 8 ;XON/XOFF IS USED TO THROTTLE INPUT DATA. INBEL EQU 010H ;BELLS ARE ECHOED WHEN BUFFER OVERFLOWS. INHDP EQU 020H ;HALF DUPLEX: INPUT CHARS ARE ECHOED. INCTX EQU 040H ;FLUSH INPUT BUFFER SEQUENCE RECOGNIZED. INCTR EQU 080H ;RE-DRAW CURRENT LINE ENABLED. INCTU EQU 0100H ;DELETE CURRENT LINE ENABLED. INCTO EQU 0200H ;SKIP OUTPUT SEQUENCE INABLED. INEST EQU 0400H ;ERRORS CAUSE STATUS RETURNS. INEPC EQU 0800H ;ERRORS TRANSLATE TO CODES WITH PARITY BIT ON. INSTP EQU 01000H ;STRIP PARITY BIT OFF ON INPUT INUPR EQU 02000H ;FORCE CHARACTERS TO UPPER CASE. INCTC EQU 04000H ;CONTROL-C LEAPS INPUT BUFFER ON N.D. READ. INRES EQU 08000H ;RESET SEQUENCE CAUSES SYSTEM RE-BOOT. ; ; THE SECOND SPECIAL WORD CONTROLS HOW THE OUTPUT TO THE UART IS TREATED ; OUTDSR EQU 2 ;DSR IS USED TO THROTTLE OUTPUT DATA. OUTCTS EQU 4 ;CTS IS USED TO THROTTLE OUTPUT DATA. OUTXON EQU 8 ;XON/XOFF IS USED TO THROTTLE OUTPUT DATA. OUTCSF EQU 010H ;CTS IS OFFLINE SIGNAL. OUTCDF EQU 020H ;CARRIER DETECT IS OFFLINE SIGNAL OUTDRF EQU 040H ;DSR IS OFFLINE SIGNAL. OUTSTP EQU 01000H ;STRIP PARITY OFF ON OUTPUT. OUTUPR EQU 02000H ;LOWER CASE XLATES TO UPPER ON OUTPUT. OUTTAB EQU 04000H ;TABS ARE TRANSLATED TO SPACES ON OUTPUT. ; ; DEFINE THE PORT OFFSETS AND IMPORTANT ASYNC BOARD CONSTANTS DLAB EQU 080H ;DIVISOR LATCH ACCESS BIT ALLINT EQU 01111B ;ENABLE ALL INTERUPTS IN INTEN REGESTER. TXBUF EQU 0 ;OFFSET TO TRANSMITTER BUFFER REGESTER RXBUF EQU 0 ;DITO FOR RECEIVER (DIRECTION DIFERENTIATES FUNCS) BAUD0 EQU 0 ;BAUD DIVISOR REG (DLAB IN LCTRL DIFFERENCIATES) BAUD1 EQU 1 ;BAUD DIVISOR HIGH BYTE INTEN EQU 1 ;INTERUPT ENABLE REGESTER INTID EQU 2 ;INTERUPT IDENTIFICATION REGESTER. LCTRL EQU 3 ;LINE CONTROL REGESTER MCTRL EQU 4 ;MODEM CONTROL REGESTER LSTAT EQU 5 ;LINE STATUS REGESTER MSTAT EQU 6 ;MODEM STATUS REGESTER BOOTS SEGMENT AT 0FFFFH ;SEGMENT FOR FAR JUMP TO WARM BOOT RE_BOOT LABEL FAR ;ADDRESS. THIS IS USED BY THE BOOTS ENDS ;RE-BOOT ESCAPE SEQUENCE, IF ENABLED. VECS SEGMENT AT 0 ;ON AN IBM PC, YOU CAN REQUEST A "QUICK" BOOT ORG 72H ;BY STORING 1234H IN LOCATION 0:72. THIS IS FLAG LABEL WORD ;THE MOST IBM SPECIFIC THING IN THIS WHOLE VECS ENDS ;DRIVER, YOU MAY HAVE TO CHANGE THE RE-BOOT ;LOGIC FOR OTHER SYSTEMS. ON THE OTHER HAND, ;WRITING TO 0:72 BEFORE RESETTING THE 8088 ;CAN'T DO ANY HARM, SO YOU CAN LEAVE THIS IN. SUBTTL DRIVER LIST HEAD PAGE ;************************************************************************* ; ; BEGENING OF DRIVER CODE. ; DRIVER SEGMENT ASSUME CS:DRIVER,DS:DRIVER,ES:DRIVER ; ORG 0 ;DRIVERS START AT 0 ASYNC2: DW ASYNC1,-1 ;POINTER TO NEXT DEVICE: DOS FILLS THIS IN. DW DEVCHR OR DEVIOC ;CHARACTER DEVICE DW STRATEGY ;OFFSET TO STRATEGY ROUTINE. DW REQUEST2 ;OFFSET TO "INTERUPT" ENTRYPOINT. DB "ASYNC2 " ;DEVICE NAME. ASYNC1: DW -1,-1 ;POINTER TO NEXT DEVICE: END OF LINKED LIST. DW DEVCHR OR DEVIOC ;THIS DEVICE IS CHARACTER IOCTL DW STRATEGY ;STRATEGY ROUTINE DW REQUEST1 ;I/O REQUEST ROUTINT DB "ASYNC1 " debug dd 0b8000000h onotify macro char push es push di push ax mov ax,'&char' les di,CS:debug stos byte ptr [di] inc di mov word ptr CS:debug,di pop ax pop di pop es endm SUBTTL DRIVER INTERNAL DATA STRUCTURES PAGE ; ASY_UNITS EQU 2 ;NUMBER OF UNITS THIS DRIVER IS BUILT FOR UNIT STRUC ;EACH UNIT HAS A STRUCTURE DEFINING IT'S STATE: PORT DW ? ;I/O PORT ADDRESS VECT DW ? ;INTERUPT VECTOR OFFSET (NOT INTERUPT NUMBER!) ISRADR DW ? ;OFFSET TO INTERUPT SERVICE ROUTINE LINE DB ? ;DEFAULT LINE CONTROL BIT SETTINGS DURING INIT, ;OUTPUT STATUS BITS AFTERWORDS. MODEM DB ? ;MODEM CONTROL BIT SETTINGS DURING INIT, ;INPUT STATUS BITS AFTERWARDS. INSPEC DW ? ;SPECIAL CHAR INPUT TREATMENT, HANDSHAKING MODE. OUTSPEC DW ? ;SPECIAL MODE BITS FOR OUTPUT BAUD DW ? ;CURRENT BAUD RATE DIVISOR VALUE FLSHP DW ? ;OFFSET INTO FLUSH BUFFER COMMAND STRING. REDRP DW ? ;OFFSET INTO RE-DRAW LINE COMMAND STRING. BACKP DW ? ;OFFSET INTO DELETE CURRENT LINE COMMAND STRING. SKIPP DW ? ;OFFSET INTO SKIP OUTPUT SEQUENCE. REBTP DW ? ;OFFSET INTO RE-BOOT SYSTEM COMMAND STRING. IFIRST DW ? ;OFFSET TO FIRST CHARACTER IN INPUT BUFFER. IAVAIL DW ? ;OFFSET TO NEXT AVAILABLE BYTE. IBUF DW ? ;POINTER TO 128 BYTE INPUT BUFFER. OFIRST DW ? ;OFFSET INTO FIRST CHARACTER IN OUTPUT BUFFER OAVAIL DW ? ;OFFSET INTO NEXT AVAIL BYTE IN OUTPUT BUFFER OBUF DW ? ;POINTER TO 128 BYTE OUTPUT BUFFER UNIT ENDS ;TABLE OF STRUCTURES FOR EACH ASYNCROUNOUS UNIT ;ASYNC1 DEFAULTS TO THE COM1 PORT AND VECTOR, ;NO PARITY, 8 DATA BITS, 1 STOP BIT,AND 9600 BAUD. ASY_TAB1: UNIT <3F8H,30H,ASY1ISR,3,0BH,0D3D0H,8,12,0,0,0,0,0,0,0,IN1BUF,0,0,OUT1BUF> ;ASYNC2 DEFAULTS TO THE COM2 PORT AND VECTOR, ;NO PARITY, 8 DATA BITS, 1 STOP BIT, ;AND 9600 BAUD. ASY_TAB2: UNIT <2F8H,2CH,ASY2ISR,3,0BH,0D3D0H,8,12,0,0,0,0,0,0,0,IN2BUF,0,0,OUT2BUF> ;IF THE BUFFER SIZE IS A POWER OF TWO, THE PROCESS OF KEEPING ;THE OFSETTS WITHIN THE BOUNDS OF THE BUFFER IS GREATLY ;SIMPLIFIED. IF YOU MODIFY THE BUFFER SIZE, KEEP IT A ;POWER OF 2, AND MODIFY THE MASK ACCORDINGLY. BUFSIZ EQU 128 ;INPUT BUFFER SIZE BUFMSK EQU 127 ;MASK FOR CALCULATING OFFSETS MODULO BUFSIZ IN1BUF DB BUFSIZ DUP (?) IN2BUF DB BUFSIZ DUP (?) OUT1BUF DB BUFSIZ DUP (?) OUT2BUF DB BUFSIZ DUP (?) ; ; BAUD RATE CONVERSION TABLE ASY_BAUDT DW 50,2304 ;FIRST VALUE IS DESIRED BAUD RATE, DW 75,1536 ;SECOND IS DIVISOR REGISTER VALUE. DW 110,1047 DW 134,857 DW 150,786 DW 300,384 DW 600,192 DW 1200,96 DW 1800,64 DW 2000,58 DW 2400,48 DW 3600,32 DW 4800,24 DW 7200,16 DW 9600,12 ; ; DEFINE THE SEQUENCES OF CHARACTERS THAT ARE SCANNED FOR ON ; INPUT, USED TO FLUSH BUFFER, RE-BOOT, RE-DRAW LINES, ETC. ; EACH OF THESE SEQUENCES IS A STRING OF THREE OR LESS ; BYTES TERMINATED WITH A NUL. HERE THEY ARE PADDED WITH NULS ; TO 4 BYTES EACH SO THAT ALTERNATE SEQUENCES CAN BE PATCHED IN ; WITH DEBUG. ; FLUSH DB 'X' AND 1FH,0,0,0 ;FLUSH INPUT BUFFER REDRAW DB 'R' AND 1FH,0,0,0 ;RE-DRAW LAST LINE BACKL DB 'U' AND 1FH,0,0,0 ;DELETE LAST LINE SKIPO DB 'O' AND 1FH,0,0,0 ;SKIP OUTPUT REBOOT DB 'C' AND 1FH,'R' AND 1FH,'B' AND 1FH,0 ;RE-BOOT SYSTEM ; ; STRUCTURE OF AN I/O REQUEST PACKET STATIC HEADER ; PACK STRUC LEN DB ? ;LENGTH OF RECORD PRTNO DB ? ;UNIT CODE CODE DB ? ;COMMAND CODE STAT DW ? ;RETURN STATUS DOSQ DD ? ;UNUSED DOS QUE LINK POINTER DEVQ DD ? ;UNUSED DRIVER QUE LINK POINTER MEDIA DB ? ;MEDIA CODE ON READ/WRITE XFER DW ? ;XFER ADDRESS OFFSET XSEG DW ? ;XFER ADDRESS SEGMENT COUNT DW ? ;TRANSFER BYTE COUNT. PACK ENDS ; ; THE FOLLOWING TWO WORDS IS THE STORAGE AREA FOR THE REQUEST PACKET ; ADDRESS, SENT TO ME BY A STRATEGY ROUTINE CALL. ; AS REQUESTED BY THE MSDOS DRIVER MANUAL, I AM "THINKING ; ABOUT" THE FUTURE, SO I`M DESIGNATING THIS POINTER AS THE QUEUE ; LIST HEAD FOR REQUESTS TO THIS DRIVER. ; PACKHEAD DD 0 ; ; THE STRATEGY ROUTINE ITSELF. STRATEGY PROC FAR ;SQUIRREL AWAY THE POINTER FOR LATER. MOV WORD PTR CS:PACKHEAD,BX ;STORE THE OFFSET, MOV WORD PTR CS:PACKHEAD+2,ES ;AND THE SEGMENT. RET STRATEGY ENDP SUBTTL REQUEST ROUTINES PAGE ; PHYLOSOPHICAL RUMINATIONS: ; Why does MicroSoft INSIST on choosing names for things that ; already have firmly defined meanings for OTHER things? Take for ; example, the MASM definition of a SEGMENT: It bears little relation ; to the deffinition of a segment in the intel 8088 processor handbook. ; This leads to a GREAT DEAL of confusion. Many other assemblers on ; other systems have constructs that are equivalent to MASM's SEGMENT, ; they are often called PSECTS for Program SECTionS. Perhaps the ; people at Microsoft wanted a word that made more sence in English, ; but I wish they had chosen SECTION instead of SEGMENT. ; The example that it bringing all this to mind now is the ; MicroSoft device driver documentation, which insists on calling ; the following routine an "interupt routine". Go read the intel ; manual, you will find that an interupt routine is defined THERE as ; a bunch of code that is jumped to by a special kind of event in ; the hardware. That is NOT what the people at MicroSquishy mean ; this time either. Depending on weather you describe these routines ; in terms of what they do now, or in the "future", the following ; routine should be called the "I/O request routine" or the "I/O ; completion routine". But NO, they had to deside to call this ; the "interupt routine", and create another layer of confusion for ; those of us who already know the traditional deffinition of this ; relatively well known phrase. ; ; I am herby refering to the "interupt routine" as the ; "request routine", and nameing all my labels accordingly. ; ; I/O REQUEST ROUTINES REQUEST1: ;ASYNC1 HAS BEEN REQUESTED PUSH SI ;SAVE SI SO YOU CAN MOV SI,OFFSET ASY_TAB1 ;GET THE DEVICE UNIT TABLE ADDRESS. JMP GEN_REQUEST ;THE GENERIC DRIVER DOES THE REST. REQUEST2: ;ASYNC2 HAS BEEN REQUESTED TO DO SOMETHING PUSH SI ;SAVE SI MOV SI,OFFSET ASY_TAB2 ;GET UNIT TABLE TWO`S ADDRESS GEN_REQUEST: PUSHF ;I REQUIRE DIRECTION FLAG CLEARED, SO I SAVE CLD ;THE FLAGS AND CLEAR THEM HERE. PUSH AX ;SAVE ALL THE REGESTERS, YOU MAY NOT PUSH BX ;NEED THEM ALL, BUT YOU WILL REGRET IT PUSH CX ;IF YOU FORGET TO SAVE JUST ONE OF THEM. PUSH DX PUSH DI PUSH BP PUSH DS PUSH ES PUSH CS ;COPY THE CS REGESTER POP DS ;INTO THE DS TO ACCESS MY DATA LES BX,PACKHEAD ;RECOVER THE POINTER TO THE PACKET. MOV DI,OFFSET ASY_FUNCS ;GET THE POINTER TO THE DISPATCH TABLE MOV AL,ES:CODE[BX] ;GET THE FUNCTION REQUEST CODE, MOV AH,0 ;MAKE IT INTO A WORD, SAL AX,1 ;CONVERT TO A WORD OFFSET, ADD DI,AX ;AND ADD TO THE TABLE START ADDRESS JMP [DI] ;JUMP TO THE APPROPRIATE ROUTINE ; ; TABLE OF OFFSETS TO ALL THE DRIVER FUNCTIONS ASY_FUNCS: DW ASYNC_INIT ;INITIALIZE DRIVER DW EXIT ;MEDIA CHECK (BLOCK DEVICES ONLY) DW EXIT ;BUILD BPB (BLOCK DEVICES ONLY) DW IOCTLIN ;IOCTL INPUT DW READ ;READ DW NDREAD ;NON-DESTRUCTIVE READ DW RXSTAT ;INPUT STATUS DW INFLUSH ;FLUSH INPUT BUFFER DW WRITE ;WRITE DW WRITE ;WRITE WITH VERIFY DW TXSTAT ;OUTPUT STATUS DW TXFLUSH ;FLUSH OUTPUT BUFFER DW IOCTLOUT ;IOCTL OUTPUT ; ; EXIT FROM DRIVER REQUEST ; CALL WITH AX= RETURN STATUS VALUE EXITP PROC FAR EXIT: LES BX,PACKHEAD ;RETREIVE POINTER TO PACKET OR AX,STSDNE ;SET THE DONE BIT IN IT. MOV ES:STAT[BX],AX ;STORE THE STATUS BACK IN THE PACKET. POP ES ;RESTORE ALL THE REGESTERS POP DS POP BP POP DI POP DX POP CX POP BX POP AX POPF POP SI RET EXITP ENDP SUBTTL READ DATA REQUEST ROUTINE PAGE ; ; ALL THE FOLLOWING ROUTINES ARE CALLED WITH THE SAME CONTEXT ; FROM THE REQUEST ROUTINE: ; - ES:BX POINTS TO THE I/O PACKET. ; ROUTINES CAN MUCK UP THESE TWO REGESTERS IF THEY WANT, AS EXIT ; WILL RESTORE THEM BEFORE IT TRIES TO SEND THE STATUS WORD BACK. ; - CS: AND DS: POINT TO THE BEGENING OF THE DRIVER SEGMENT. ; - DS:SI POINTS TO THE DEVICE UNIT TABLE DESCRIBING THE PARTICULAR ; PORT BEING ACCESSED. ; - ALL OTHER REGESTERS ARE AVAILABLE FOR USE, THE EXIT ROUTINE ; RESTORES THEM ALL BEFORE RETURNING TO MSDOS. ; ALL THE FOLLOWING ROUTINES SHOULD EXIT BY DOING A JMP ; TO THE EXIT ROUTINE. EXIT ASSUMES THAT AX ; CONTAINS EITHER ZERO, OR THE ERROR BIT SET AND A VALID ERROR ; RETURN VALUE IN THE LOW ORDER BITS. EXIT SETS THE DONE BIT IN ; THIS VALUE FOR YOU BEFORE IT RETURNS TO MSDOS. ; SUBTTL READ REQUEST ROUTINE ; READ DATA FROM DEVICE ; READ: MOV CX,ES:COUNT[BX] ;GET THE REQUESTED NUMBER OF BYTES MOV DI,ES:XFER[BX] ;DI IS OFFSET TO USER BUFFER MOV DX,ES:XSEG[BX] ;SEGMENT IS LAST I NEED FROM PACKET, MOV ES,DX ;NOW ES:DI POINTS TO USER BUFFER. TEST LINE[SI],LINSKP ;IF THERE IS A SKIP OUTPUT IN JE NO_SKIP ;PROGRESS, I MUST CANCEL IT NOW. AND LINE[SI],NOT LINSKP ;CLEAR THE SKIP OUTPUT BIT. CALL START_OUTPUT ;START THE XMITTER GOING AGAIN. NO_SKIP: TEST MODEM[SI],MODERR OR MODOVR ;HAVE ANY LINE ERRORS OCCURED? JE NO_LERR ;NOT LATELY. AND MODEM[SI],NOT ( MODERR OR MODOVR ) ;YES, CLEAR THE BITS, MOV AX,ERRRF ;AND RETURN ERROR INDICATION TO DOS JMP EXIT NO_LERR: RLUP: CALL GET_IN ;GET NEXT CHAR FROM INPUT BUFFER CMP AH,0 ;WAS THERE ONE? JE TEST_READ ;YES, TEST FOR SPECIAL BITS JMP RLUP ;NO, WAIT FOR A CHAR TO ARRIVE. TEST_READ: ;BEFORE RETURNING A CHARACTER TO MSDOS, I CHECK FOR ANY ;SPECIAL PROCESSING I AM REQUIRED TO DO. I DO AS MANY ;OF THESE FUNCTIONS HERE AS POSSIBLE, TO SAVE THE ;RECEIVER INTERUPT ROUTINE FROM HAVING TO DO THEM, AND ;TO ALLOW INTELIGENT USE OF THE RING BUFFER. FOR EXAMPLE, ;CHARACTERS TO NOT ECHO-HALF-DUPLEX UNTIL THEY ARE READ ;FROM THE BUFFER HERE, SO A PROGRAM THAT DISABLES ECHO ;WHILE READING A PASSWORD WILL WORK EVEN IF THE USER TYPED ;THE PASWORD IN BEFORE BEING PROMPTED FOR IT. ;************************************ ;TEST FOR STRIPPING PARITY BIT TEST INSPEC[SI],INSTP ;SHOULD I STRIP PARITY? JE NO_STRIP ;NOPE. AND AL,NOT 080H ;YES. NO_STRIP: ;********************************* ;TEST FOR UPPER-CASE CONVERSION TEST INSPEC[SI],INUPR ;IS LOWER TO UPPER CONV. ENABLED? JE NO_UPPER ;NOPE, SKIP THIS. MOV AH,AL ;MAKE A COPY IN AH, AND AH,07FH ;STRIP PARITY TO MAKE TEST VALID, CMP AH,'a' ;IS IT < A LOWER CASE a? JL NO_UPPER ;YES, THEN IT'S NOT LOWER. CMP AH,'z' ;IS IT > A LOWER CASE z? JG NO_UPPER ;YES, THEN IT'S STILL NOT LOWER. SUB AL,020H ;NO, THEN CONVERT THE ONE IN AL. NO_UPPER: ;**********************************8 ;TEST FOR HALF-DUPLEX MODE STOS BYTE PTR [DI] ;BUT FIRST STORE CHAR IN USER BUFFER. TEST INSPEC[SI],INHDP ;AM I IN HALF DUPLEX MODE? JE NO_HALF ;NO. HALF_WAIT: CALL PUT_OUT ;YES, PUT THE CHARACTER IN OUTBUF. CMP AH,0 ;WAS THERE ROOM? JNE HALF_WAIT ;NO, WAIT FOR IT. CALL START_OUTPUT ;AND MAKE SURE THE XMITTER STARTS NO_HALF: ;ALTHOUGH MSDOS NEVER, TO MY KNOWLEDGE, ASKS FOR MORE THAN ;ONE STUPID CHARACTER AT A TIME, I LOOP ON THE REQUEST SIZE ;SO THAT THIS DRIVER WILL STILL WORK ON THAT GLORIOUS DAY ;WHEN SOMEBODY ASKS FOR MORE THAN ONE. LOOP RLUP ;KEEP GOING IF YOU WERE REQUESTED. MOV AL,0 ;RETURN NO ERRORS IN AX IF DONE. JMP EXIT SUBTTL NON-DESTRUCTIVE READ REQUEST ROUTINE PAGE ; NON-DESTRUCTIVE READ FROM DEVICE ; NDREAD: MOV DI,IFIRST[SI] ;GET POINTER TO FIRST CHAR CMP DI,IAVAIL[SI] ;IS THE BUFFER EMPTY? JNE NDGET ;NO, GET ONE NON DESTRUCTIVELY. MOV AX,STSBSY ;YES, RETURN DEVICE BUISY JMP EXIT NDGET: PUSH BX ;SAVE AN EXTRA COPY OF BX. MOV BX,IBUF[SI] ;GET BUFFER ADDRESS TEST INSPEC[SI],INCTC ;SHOULD I CHECK FOR IMBEDDED ^C'S? JE NONDCTC ;NO. ;YES, SCAN BUFFER FOR CONTROL-C. CTCLUP: CMP BYTE PTR [BX+DI],'C' AND 1FH ;IS THIS A CONTROL-C? JNE NXTCTC ;NO, GO TRY AGAIN YESCTC: MOV IFIRST[SI],DI ;YES, DESTRUCTIVELY READ TO HERE, JMP NONDCTC ;AND RETURN THE CONTROL-C. NXTCTC: CMP BYTE PTR [BX+DI],'S' AND 1FH ;DO THE SAME TEST FOR ^S JE YESCTC ;IN CASE XON/XOFF IS DISABLED. INC DI ;INCRIMENT THE POINTER, AND DI,BUFMSK ;MODULO BUFSIZ, CMP DI,IAVAIL[SI] ;STOP IF YOU BUMP INTO YOURSELF, JNE CTCLUP ;ELSE KEEP LOOKING. MOV DI,IFIRST[SI] ;IF NO, CONTROL-C, RETURN FIRST CHAR NONDCTC: MOV AL,[BX+DI] ;GET THE CHARACTER, POP BX ;RECOVER BX AGAIN. MOV ES:MEDIA[BX],AL ;RETURN IT IN THE REQUEST PACKET. MOV AX,0 ;RETURN NO ERRORS IN AX. JMP EXIT SUBTTL INPUT STATUS REQUEST ROUTINE PAGE ; INPUT STATUS REQUEST ; RXSTAT: MOV DI,IFIRST[SI] ;GET POINTER TO FIRST CHAR CMP DI,IAVAIL[SI] ;IS THE BUFFER EMPTY? JNE RXFUL MOV AX,STSBSY ;NO, RETURN STATUS BUISY. JMP EXIT RXFUL: MOV AX,0 ;YES, RETURN STATUS ZERO. JMP EXIT SUBTTL INPUT FLUSH REQUEST ROUTINE ; INPUT FLUSH REQUEST ; INFLUSH: MOV AX,IAVAIL[SI] ;GET THE POINTER TO THE NEXT EMPTY MOV IFIRST[SI],AX ;CHAR AND POINT THE FIRST AT IT. MOV AX,0 ;AND RETURN DONE. JMP EXIT SUBTTL WRITE REQUEST ROUTINE PAGE ; OUTPUT DATA TO DEVICE ; WRITE: MOV CX,ES:COUNT[BX] ;GET BYTE COUNT, MOV DI,ES:XFER[BX] ;GET XFER ADDRESS OFFSET, MOV AX,ES:XSEG[BX] ;GET XFER SEGMENT. MOV ES,AX ;STORE IN ES NOW. TEST LINE[SI],LINSKP ;SHOULD I BE SKIPPING OUTPUT? JNE WRSUCC WLUP: MOV AL,ES:[DI] ;GET THE NEXT CHAR, INC DI ;AND INC DI PAST IT. ; ; CHECK FOR STRIP PARITY ON OUTPUT. ; TEST OUTSPEC[SI],OUTSTP ;IS THIS ENABLED? JE NOOSTP ;NOPE AND AL,NOT 080H ;YES, WACK OFF BIT SEVEN! NOOSTP: ; ; CHECK FOR LOWER-TO-UPPER CONVERSION ON OUTPUT ; TEST OUTSPEC[SI],OUTUPR ;IS THIS ENABLED? JE NOOUPR ;NOPE MOV AH,AL ;DO COMPARISON IN AH AND AH,NOT 080H ;SO YOU CAN STRIP PARITY CMP AH,'a' ;IS IT GREATER THAN LOWER A? JL NOOUPR ;NOPE, IT'S NOT LOWER CMP AH,'z' ;IS IT GREATER THAN LOWER Z? JG NOOUPR ;NOPE, STILL NOT LOWER AND AL,NOT 020H ;YES, WHACK OUT THE LOWER BIT. NOOUPR: ; ; AFTER ALL THE SPECIAL OUTPUT PROCESSING, I DO A HARD WAIT ; FOR A SLOT IN THE OUTPUT BUFFER. WWAIT: CALL PUT_OUT ;ATTEMPT TO PUT IN IN OUTPUT BUFFER CMP AH,0 ;DID IT WORK? JNE WWAIT ;NO, KEEP TRYING. LOOP WLUP ;YES, GO GET NEXT CHAR. CALL START_OUTPUT ;START THE XMITTER IF NECC. WRSUCC: MOV AX,0 ;RETURN SUCCESS JMP EXIT SUBTTL OUTPUT STATUS REQUEST ROUTINE ; OUTPUT STATUS REQUEST ; TXSTAT: MOV AX,OFIRST[SI] ;GET POINTER TO NEXT CHAR OUT DEC AX ;SUBTRACT ONE FROM IT, AND AX,BUFMSK ;MODULO 128. CMP AX,OAVAIL[SI] ;IF THAT EQUALS THE INPUT PNTR, JNE TXROOM MOV AX,STSBSY ;THEN THE DEVICE IS BUISY. JMP EXIT TXROOM: MOV AX,0 ;OTHERWIZE THE DEVICE IS OK. JMP EXIT SUBTTL I/O CONTROL READ REQUEST PAGE ; ; IOCONTROL READ REQUEST, RETURN LINE PARAMETERS ; IOCTLIN: MOV CX,ES:COUNT[BX] ;GET THE REQUESTED NUMBER OF BYTES MOV DI,ES:XFER[BX] ;DI IS OFFSET TO USER BUFFER MOV DX,ES:XSEG[BX] ;SEGMENT IS LAST I NEED FROM PACKET, MOV ES,DX ;NOW ES:DI POINTS TO USER BUFFER. CMP CX,10 ;ONLY WORKS WHEN YOU GIVE ME AN JE DOIOCIN ;10 BYTE BUFFER TO STOMP ON. MOV AX,ERRBSL ;RETURN AN ERROR IF NOT 10 BYTES. JMP EXIT DOIOCIN: MOV DX,PORT[SI] ;GET PORT NUMBER ADD DX,LCTRL ;SLIDE UP TO LINE CONTROL MOV CX,4 ;SET UP FOR PORT LOOP. GETPORT: IN AL,DX ;GET NEXT BYTE FROM DEVICE STOS BYTE PTR [DI] ;STORE THEM IN USER BUFFER INC DX ;SKIP TO NEXT BYTE LOOP GETPORT ;READ AND STORE 4 BYTES OF INFO MOV AX,INSPEC[SI] ;GET THE SPECIAL INPUT BITS STOS WORD PTR [DI] ;SEND BACK TO USER BUFFER MOV AX,OUTSPEC[SI] ;GET THE SPECIAL OUTPUT BITS STOS WORD PTR [DI] ;SEND BACK TO USER BUFFER MOV AX,BAUD[SI] ;GET BAUD RATE DIVISOR MOV BX,DI ;SAVE DI FOR A WHILE. MOV DI,OFFSET ASY_BAUDT+2 ;POINT AT BAUD RATE CONVERSION. MOV CX,15 ;JUST IN CASE, STOP AT 15 BAUDS BAUDCIN: CMP [DI],AX ;IS THIS THE BAUD I AM USING? JE YESINB ;YES, RETURN THAT ADD DI,4 ;NO, SKIP TO NEXT ONE LOOP BAUDCIN ;KEEP LOOKING. YESINB: ;SEARCH SHOULD ALWAYS TERMINATE ON COMPARE MOV AX,-2[DI] ;GET THE ASSOCIATED BAUD RATE MOV DI,BX ;GET DI'S OLD VALUE BACK STOS WORD PTR [DI] ;STORE THE BAUD RATE BACK. MOV AX,0 ;RETURN NO ERRORS JMP EXIT ; ; FLUSH OUTPUT BUFFER REQUEST ; TXFLUSH: MOV AX,OAVAIL[SI] ;GET NEXT FREE BYTE OFFSET, MOV OFIRST[SI],AX ;POINT THE FIRST BYTE OFFSET AT IT. MOV AX,0 JMP EXIT SUBTTL I/O CONTROL WRITE REQUEST ROUTINE PAGE ; IOCONTROL REQUEST: CHANGE LINE PARAMETERS FOR THIS DRIVER ; IOCTLOUT: MOV CX,ES:COUNT[BX] ;GET THE REQUESTED NUMBER OF BYTES MOV DI,ES:XFER[BX] ;DI IS OFFSET TO USER BUFFER MOV DX,ES:XSEG[BX] ;SEGMENT IS LAST I NEED FROM PACKET, MOV ES,DX ;NOW ES:DI POINTS TO USER BUFFER. CMP CX,10 ;ONLY WORKS WHEN YOU GIVE ME A JE DOIOCOUT ;10 BYTE BUFFER TO READ FROM MOV AX,ERRBSL ;RETURN AN ERROR IF NOT 10 BYTES. JMP EXIT DOIOCOUT: MOV DX,PORT[SI] ;GET PORT NUMBER ADD DX,LCTRL ;SLIDE UP TO LINE CONTROL MOV AL,ES:[DI] ;GET LINE CONTROL FROM USER BUF. INC DI OR AL,080H ;SET DLAB BIT FOR BAUD RATE OUT DX,AL ;OUTPUT TO DEVICE INC DX ;SKIP TO NEXT BYTE MOV AL,ES:[DI] ;GET MODEM CONTROL FROM USER BUF. INC DI OR AL,08H ;MAKE SURE INTERUPTS ARE ENABLED. OUT DX,AL ;SEND IT TO DEVICE. ADD DI,2 ;SKIP OVER THE STATUS BYTES MOV AX,ES:[DI] ;GET THE SPECIAL INPUT BITS ADD DI,2 MOV INSPEC[SI],AX ;STORE THE NEW BITS IN UNIT MOV AX,ES:[DI] ;GET THE OUTPUT SPECIAL BITS ADD DI,2 MOV OUTSPEC[SI],AX ;STORE THEM ALSO. MOV AX,ES:[DI] ;GET THE REQUESTED BAUD RATE MOV BX,DI ;SAVE DI FOR A WHILE. MOV DI,OFFSET ASY_BAUDT ;POINT AT BAUD RATE CONVERSION MOV CX,15 ;JUST IN CASE, STOP AT 15 BAUDS BAUDCOUT: CMP [DI],AX ;IS THIS THE BAUD I AM USING? JE YESOUTB ;YES, RETURN THAT ADD DI,4 ;NO, SKIP TO NEXT ONE LOOP BAUDCOUT ;KEEP LOOKING. IN AL,DX ;GET LINE CONTROL REGESTER AGAIN, AND AL,NOT 080H ;CLEAR DLAB BIT. DEC DX OUT DX,AL ;AND WRITE IT BACK OUT. MOV AX,ERRUM ;RETURN AN ERROR NUMBER IF JMP EXIT ;BAUD RATE IS NOT IN TABLE. YESOUTB: MOV AX,2[DI] ;GET THE ASSOCIATED BAUD RATE MOV BAUD[SI],AX ;STORE IT IN UNIT TABLE MOV DX,PORT[SI] ;GET PORT ADDRESS AGAIN, OUT DX,AL ;WRITE THE LOW BYTE, INC DX ;SKIP TO NEXT ONE, MOV AL,AH ;GET HIGH BYTE INTO AL OUT DX,AL ;OUTPUT IT AS WELL. ADD DX,LCTRL-BAUD1 ;POINT AT THE LINE CONTROL REG. IN AL,DX ;READ IT IN, AND AL,NOT 080H ;CLEAR THE DLAB BIT. OUT DX,AL ;OUTPUT IT BACK. MOV AX,0 ;RETURN NO ERROR JMP EXIT SUBTTL RING BUFFER ROUTINES PAGE ; LOCAL ROUTINES FOR MANAGING THE RING BUFFERS ON INPUT ; AND OUTPUT. THE FOLLOWING FOUR ROUTINES ARE ALL CALLED WITH THE ; SAME CONTEXT: ; ; DS:SI POINTS TO THE UNIT STRUCTURE FOR THIS UNIT ; AL IS THE CHARACTER TO BE PLACED IN OR REMOVED FROM A BUFFER ; AH IS THE RETURN STATUS FLAG: 0=SUCESS, -1=FAILURE ; ; ALL OTHER REGESTERS ARE PRESERVED. ; PUT_OUT PROC NEAR ;PUTS AL INTO THE OUTPUT RING BUFFER PUSH CX PUSH DI PUSHF CLI ;DISABLE INTERUPTS WHILE I HAVE OAVAIL MOV CX,OAVAIL[SI] ;GET POINTER TO NEXT AVAILABLE BYTE IN MOV DI,CX ;OUTPUT BUFFER. INC CX ;INCRIMENT A COPY OF IT TO SEE IF THE AND CX,BUFMSK ;BUFFER IS FULL. CMP CX,OFIRST[SI] ;IS IT? JE POERR ;YES, RETURN AN ERROR ADD DI,OBUF[SI] ;NO, CALCULATE ACTUAL OFFSET OF CHAR MOV [DI],AL ;AND STUFF THE CHARACTER INTO BUFFER MOV OAVAIL[SI],CX ;UPDATE THE POINTER MOV AH,0 ;INDICATE SUCCESS JMP PORET ;AND RETURN POERR: MOV AH,-1 ;INDICATE FAILURE. PORET: POPF ;RE-ENABLE INTERUPTS POP DI POP CX RET PUT_OUT ENDP GET_OUT PROC NEAR ;GETS THE NEXT CHARACTER FROM OUTPUT RING BUFFER ;SURE YOU DISABLE INTERUPTS FIRST. PUSH CX PUSH DI PUSHF ;JUST IN CASE, DISABLE INTERUPTS CLI ;WHILE IN THIS ROUTINE. MOV DI,OFIRST[SI] ;GET POINTER TO FIRST CHARACTER TO OUTPUT CMP DI,OAVAIL[SI] ;IS THE BUFFER EMPTY? JNE NGOERR ;NO. MOV AH,-1 ;YES, INDICATE FAILURE JMP GORET ;AND RETURN NGOERR: MOV CX,DI ;SAVE A COPY OF THE POINTER ADD DI,OBUF[SI] ;CALCULATE ACTUAL ADDRESS MOV AL,[DI] ;GET THE CHAR INTO AL MOV AH,0 ;INDICATE SUCCESS. INC CX ;INCRIMENT THE OFFSET AND CX,BUFMSK ;MODULO 128 MOV OFIRST[SI],CX ;STORE BACK IN UNIT TABLE. GORET: POPF POP DI POP CX RET GET_OUT ENDP PUT_IN PROC NEAR ;PUT THE CHAR FROM AL INTO INPUT RING BUFFER PUSH CX PUSH DI PUSHF ;DISABLE INTS WHILE IN THIS ROUTINE CLI MOV DI,IAVAIL[SI] ;GET POINTER TO NEXT AVAILABLE SLOT IN BUFFER MOV CX,DI ;SAVE A COPY OF IT, INC CX ;AND INCRIMENT THAT COPY (MODULO AND CX,BUFMSK ;128) TO SEE IF THE BUFFER IS FULL. CMP CX,IFIRST[SI] ;WELL, IS IT? JNE NPIERR ;NO, THERE`S ROOM. MOV AH,-1 ;YES, INDICATE FAILURE JMP PIRET ;AND RETURN NPIERR: ADD DI,IBUF[SI] ;CALCULATE ACTUAL ADDRES, MOV [DI],AL ;STORE THE CHARACTER THERE MOV IAVAIL[SI],CX ;UPDATE THE POINTER. MOV AH,0 ;AND INDICATE SUCCESS. PIRET: POPF POP DI POP CX RET PUT_IN ENDP GET_IN PROC NEAR ;GETS ONE CARACTER FROM INPUT RING BUFFER INTO AL PUSH CX PUSH DI PUSHF CLI ;DISABLE INTERUPTS WHILE I LOOK AT IFIRST. MOV DI,IFIRST[SI] ;GET POINTER TO FIRST CHAR TO READ CMP DI,IAVAIL[SI] ;IS THE BUFFER EMPTY? JE GIERR ;THEN YOU CAN`T VERY WELL SQUEEZE WATER OUT OF IT MOV CX,DI ;MAKE A COPY OF POINTER, ADD DI,IBUF[SI] ;CALCULATE ACTUAL ADDRESS OF CHAR MOV AL,[DI] ;GET THE CHAR INTO AL MOV AH,0 ;INDICATE SUCCESS INC CX ;INCRIMENT THAT COPY OF YOUR POINTER, AND CX,BUFMSK ;MODULO THE BUFFER SIZE, MOV IFIRST[SI],CX ;SO YOU CAN UPDATE THE POINTER. JMP GIRET GIERR: MOV AH,-1 ;RETURN FAILURE INDICATOR GIRET: POPF ;RE-ENABLE INTERUPTS BEFORE YOU RETURN POP DI POP CX RET GET_IN ENDP SUBTTL INTERUPT SERVICE ROUTINES PAGE ; THE FOLLOWING ROUTINES ARE WHAT I REALLY CALL AN INTERUPT ; ROUTINE! THESE ROUTINES ARE ONLY CALLED WHEN AN INTERUPT IS GENERATED ; BY THE 8088, NOT BY A SOFWARE CALL THROUGH A LINKED LIST! ONE EASY ; WAY TO TELL A REAL INTERUPT ROUTINE WHEN YOU SEE IT IS TO LOOK AT THE ; LAST INSTRUCTION, WHICH IS AN "INTERUPT RETURN", NOT A FAR RETURN ; LIKE THE SO-CALLED MSDOS "INTERUPT ROUTINE" DOES. ; THESE INTERUPT ROUTINES ARE ENVOKED WHENEVER A CHAR ARRIVES IN THE ; UART, THE UART FINISHES SENDING A CHARACTER OUT, AN ERROR OCCURS ; WHILE READING A CHARACTER INTO THE UART, OR THE MODEM STATUS LINES ; CHANGE. ASY1ISR: CLI PUSH SI LEA SI,ASY_TAB1 ;POINT AT THE CORRECT TABLE FOR THIS UART, JMP INT_SERVE ;JUMP INTO THE COMMON INTERUPT SERVER CODE. ASY2ISR: CLI PUSH SI LEA SI,ASY_TAB2 ;GET UNIT TABLE ; ; IF YOU ADD MORE UNITS, YOU CAN ADD THEM HERE, ; BUT DON'T FORGET TO ADD A JUMP INT_SERVE AFTER ; ASY2ISR'S LEA INSTRUCTION! ; ; THE FOLLOWING CODE IS THE COMMON SHARED CODE THAT ALL THE ; ASYNC PORTS SHARE. IT "KNOWS" WHICH ONE TO TALK TO BY REFERENCING ; THE STRUCTURE POINTED TO BY CS:SI. INT_SERVE: PUSH AX ;PUSH ALL THE GP REGESTERS PUSH BX PUSH CX PUSH DX PUSH DI PUSH DS ;SAVE THE DATA SEGMENT PUSH CS ;SO YOU CAN LOAD CS POP DS ;INTO DS AND FIND YOUR OWN STRUCTURES. INT_EXIT: MOV DX,PORT[SI] ;GET THE PORT ADDRESS OF THIS DEVICE ADD DX,INTID ;SLIDE UP TO THE INTERUPT ID REGISTER IN AL,DX ;GET THE INTERUPT REASON TEST AL,1 ;MAKE SURE IT IS VALID JNE INT_DONE ;IT IS NOT. MOV AH,0 ;CONVERT IT TO A 16 BIT NUMBER ADD AX,OFFSET INT_REASON ;ADD IT TO THE JUMP TABLE ADDRESS MOV DI,AX ;PUT IT IN AN INDEX REGESTER, JMP [DI] ;AND GO PROCESS THAT TYPE OF INTERUPT INT_DONE: ADD DX,LSTAT-INTID ;BECAUSE IT SEEMS TO BE NECESSARY, IN Al,DX ;READ THE STATUS PORT BEFORE YOU EXIT. MOV AL,020H ;OUTPUT A 20H TO THE UNDOCUMENTED INTERUPT OUT 020H,AL ;COMMAND PORT TO ENABLE FURTHER INTERUPTS? POP DS ;RECOVER ALL THE REGESTERS POP DI POP DX POP CX POP BX POP AX POP SI IRET ; ; JUMP TABLE OF INTERUPT REASONS. INTERUPT ID REGISTER ; IS USED AS INDEX TO THIS TABLE. INT_REASON: DW INT_MODEM ;INT WAS CAUSED BY A MODEM LINE TRANSITION DW INT_TXMIT ;INT WAS CAUSED BY A TX REGISTER EMPTY DW INT_RECEIVE ;NEW CHARACTER AVAILABLE IN UART DW INT_RXSTAT ;CHANGE IN RECEIVER STATUS REGESTER SUBTTL RECEIVER INTERUPT SERVICE ROUTINE PAGE ; ; THE INTERUPT SERVICE ROUTINES BELOW ALL HAVE THE ; FOLLOWING CONTEXT: ; -CS AND DS POINT TO THE DRIVER SEGMENT. ; -DS:[SI] POINTS TO THE UNIT STRUCTURE FOR THE ASYNC LINE THAT ; FIRED THE INTERUPT. ; -AX BX CX DX AND DI ARE AVAILABLE FOR SCRATCH. ALL OTHERS ; MUST BE LEFT ALONE OR SAVED AND RECOVERED. ; TO EXIT FROM AN INTERUPT SERVICE ROUTINE, THESE SERVERS MUST ; JUMP TO INT_EXIT. ; SUBTTL RECEIVER INTERUPT SERVICE ROUTINE INT_RECEIVE: ;THE UART HAS RECEIVED A NEW CHARACTER, I MUST COPY IT ;INTO THE INPUT TYPEAHEAD BUFFER. MOV DX,PORT[SI] ;POINT DX BACK AT THE RXBUF REGESTER IN AL,DX ;GET THE CHARACTER ; ; BEFORE I STORE THE CHARACTER BACK IN THE RING ; BUFFER, I CHECK TO SEE IF ANY OF THE SPECIAL ; INPUT SEQUENCES ARE ENABLED, AND IF THEY ; HAVE BEEN FOUND IN THE INPUT. ; ; ; CHECK FOR XON/XOFF ; TEST OUTSPEC[SI],OUTXON ;IS XON/XOFF ENABLED? JE NOXON ;NO, SKIP THIS WHOLE SECTION CMP AL,'S' AND 01FH ;IS THIS A CONTROL-S? JNE ISQ ;NO, CHECK FOR CONTROL-Q OR LINE[SI],LINXOF ;DISABLE OUTPUT. JMP INT_EXIT ;DON'T STORE THIS CHAR. ISQ: CMP AL,'Q' AND 01FH ;IS THIS A CONTROL-Q? JNE NOXON ;NO, SKIP TO THE NEXT TEST. TEST LINE[SI],LINXOF ;AM I WAITING FOR THIS ^Q? JE INT_EXIT ;NO, DON'T STIR UP DRIVER. AND LINE[SI],NOT LINXOF ;CLEAR THE XOFF BIT, CALL START_OUTPUT JMP INT_EXIT ;DON'T BUFFER ^Q'S NOXON: ; ; CHECK FOR THE SKIP OUTPUT ESCAPE SEQUENCE ; TEST INSPEC[SI],INCTO ;IS THIS ENABLED? JE NOCTO ;NOPE. MOV DI,SKIPP[SI] ;GET OFFSET TO NEXT CHAR MOV SKIPP[SI],0 ;RESET RECOG. OF THIS SEQ. CMP SKIPO[DI],AL ;THIS IT? JNE NOCTO ;NOPE, GO CHECK FOR OTHERS. INC DI ;YES, INCRIMENT TO NEXT CHAR. CMP SKIPO[DI],0 ;WAS THAT THE LAST ONE? JNE NEXTO ;NO, PASS THIS CHAR ON. XOR LINE[SI],LINSKP ;YES, TOGGLE THE SKIP BIT. CALL START_OUTPUT ;RATTLE THE XMITTERS CAGE. INT_EXIT3: JMP INT_EXIT ;AND EXIT. NEXTO: MOV SKIPP[SI],DI ;SET OFFSET TO NEXT CHAR. NOCTO: ; ; CHECK FOR THE FLUSH INPUT BUFFER SEQUENCE. ; TEST INSPEC[SI],INCTX ;IS THIS ENABLED? JE NOCTX ;NOPE. MOV DI,FLSHP[SI] ;GET OFFSET TO NEXT CHAR MOV FLSHP[SI],0 ;RESET RECOG. OF THIS SEQ. CMP FLUSH[DI],AL ;THIS IT? JNE NOCTX ;NOPE, GO CHECK FOR OTHERS. INC DI ;YES, INCRIMENT TO NEXT CHAR. CMP FLUSH[DI],0 ;WAS THAT THE LAST ONE? JNE NEXTX ;NO, PASS THIS CHAR ON. MOV AX,IFIRST[SI] ;YES! FLUSH THE BUFFER BY MOV IAVAIL[SI],AX ;POINTING INPUT TO OUTPUT. JMP INT_EXIT ;AND EXIT. NEXTX: MOV FLSHP[SI],DI ;SET OFFSET TO NEXT CHAR. NOCTX: ; ; CHECK FOR THE RE-BOOT SYSTEM ESCAPE SEQUENCE. ; TEST INSPEC[SI],INRES ;IS THIS ENABLED NOW? JE NOREB ;NO. MOV DI,REBTP[SI] ;GET OFFSET TO NEXT CHAR MOV REBTP[SI],0 ;RESET RECOG. OF THIS SEQ. CMP REBOOT[DI],AL ;THIS IT? JNE NOREB ;NOPE, GO CHECK FOR OTHERS. INC DI ;YES, INCRIMENT TO NECT CHAR. CMP REBOOT[DI],0 ;WAS THAT THE LAST ONE? JNE NEXTB ;NO, PASS THIS CHAR ON. MOV AX,0 ;YES, SET UP TO DO A KEYBOARD MOV DS,AX ;RESET. ASSUME DS:VECS MOV AX,1232H ;GET THE QUICK BOOT FLAG, MOV FLAG,AX ;WARN RESET THIS IS QUICK BOOT JMP RE_BOOT ;JUMP TO THE RE-BOOT VECTOR. NEXTB: MOV REBTP[SI],DI ;SET OFFSET TO NEXT CHAR. NOREB: ; ; CHECK FOR THE RE-DRAW LAST LINE SEQUENCE. ; TEST INSPEC[SI],INCTR ;IS THIS ENABLED? JE NOCTR ;NOPE. MOV DI,REDRP[SI] ;YES, GET POINTER TO NEXT MOV REDRP[SI],0 ;RESET IN CASE THIS ISNT IT. CMP REDRAW[DI],AL ;IS THIS THE NEXT CHAR? JNE NOCTR ;NO. INC DI ;YES, INCRIMENT TO NEXT CMP REDRAW[DI],0 ;WAS THAT THE LAST ONE? JNE NEXTR ;NO, PASS THIS CHAR ON. MOV DI,OFIRST[SI] ;YES, THEN SEARCH BACKWARDS MOV BX,OBUF[SI] ;FOR THE LAST CR. CRLUP: DEC DI ;STEP BACK ONE CHAR IN TIME AND DI,BUFMSK ;(MODULO THE BUFFER SIZE) CMP DI,OAVAIL[SI] ;BUT STOP IF YOU BACK INTO JE NOCTR ;THE OTHER END OF BUFFER CMP BYTE PTR [BX+DI],0DH ;IS THIS A CR? JNE CRLUP ;NO, KEEP LOOKING. MOV OFIRST[SI],DI ;YES, SET BUFFER BACK. CALL START_OUTPUT ;AND MAKE SURE XMITTER SEES IT. INT_EXIT2: JMP INT_EXIT NEXTR: MOV REDRP[SI],DI ;UPDATE PTR TO PARTIAL SEQ. NOCTR: ; ; CHECK FOR THE ERASE TO BEGENING OF LINE ESCAPE SEQ. ; TEST INSPEC[SI],INCTU ;IS THIS ENABLED? JE NOCTU ;NOPE. MOV DI,BACKP[SI] ;YES, GET POINTER TO NEXT MOV BACKP[SI],0 ;RESET IN CASE THIS ISNT IT. CMP BACKL[DI],AL ;IS THIS THE NEXT CHAR? JNE NOCTU ;NO. INC DI ;YES, INCRIMENT TO NEXT CMP BACKL[DI],0 ;WAS THAT THE LAST ONE? JNE NEXTU ;NO, PASS THIS CHAR ON. MOV DI,IFIRST[SI] ;YES, THEN SEARCH BACKWARDS MOV BX,IBUF[SI] ;FOR THE LAST CR. MOV CX,-1 ;COUNTING THEM IN CX. BCRLUP: INC CX DEC DI ;STEP BACK ONE CHAR IN TIME AND DI,BUFMSK ;(MODULO THE BUFFER SIZE) CMP DI,IAVAIL[SI] ;BUT STOP IF YOU BACK INTO JE NOCTU ;THE OTHER END OF BUFFER CMP BYTE PTR [BX+DI],0DH ;IS THIS A CR? JNE BCRLUP ;NO, KEEP LOOKING. ;NOW CX CONTAINS THE NUMBER OF CHARACTERS TO ;DELETE. I DO THIS THE DUMB WAY: BY SENDING ;THAT MANY BACKSPACE CHARACTERS MOV AL,8 ;PUT A BACKSPACE IN AL BAKLUP: CALL PUT_IN ;PUT IT IN THE INPUT BUFFER LOOP BAKLUP ;PUT IN A BUNCH OF THEM JMP INT_EXIT ;WON'T MSDOS BE SUPRISED? NEXTU: MOV BACKP[SI],DI ;UPDATE PTR TO PARTIAL SEQ. NOCTU: ; ; NONE OF THE SPECIAL INPUT SEQUENCES PANNED OUT, SO ; I'LL JUST STUFF THE CHARACTER INTO THE INPUT RING ; BUFFER THIS TIME. STUFF_IN: CALL PUT_IN ;PUT THE CHARACTER IN THE RING BUFFER CMP AH,0 ;WAS THERE ROOM? JE INT_EXIT2 ;YES, JUST RETURN OR MODEM[SI],MODOVR ;NO, SET THE OVERFLOW BIT TEST INSPEC[SI],INBEL ;ARE OVERFLOW BELLS ENABLED? JE INT_EXIT2 ;NO, SKIP IT. TEST LINE[SI],LINEXP ;IS XMITTER EXPECTING AN INTERUPT? JNE INT_EXIT2 ;YES, LEAVE HIM ALONE. MOV AL,7 ;NO, SEND A BELL IMMEDIATELY MOV DX,PORT[SI] ;GET PORT ADDRESS, OUT DX,AL ;AND SEND A CHAR TO XMITTER TO START OR LINE[SI],LINEXP ;SET INTERUPT EXPECTED BIT. JMP INT_EXIT SUBTTL RECEIVER LINE STATUS INTERUPT ROUTINE PAGE ; THE LSTAT REGISTER DETECTED A RECEIVER ERROR CONDITION INT_RXSTAT: ADD DX,LSTAT-INTID ;READ THE REGESTER AND FIND OUT WHY IN AL,DX TEST INSPEC[SI],INEPC ;DO I RETURN THEM AS CODES? JE NOCODE ;NO, WHAT ELSE? AND AL,01EH ;YES, MASK OFF ALL BUT ERROR BITS, OR AL,080H ;SET THE PARITY BIT TO MAKE IT ;AN ILLEGAL CHARACTER, JMP STUFF_IN ;AND PUT IT IN THE INPUT BUFFER. NOCODE: OR MODEM[SI],MODERR ;SET A STATUS BIT THAT WILL ;NOTIFY MSDOS ON THE NEXT REQUEST JMP INT_EXIT PAGE SUBTTL MODEM STATUS INTERUPT SERVICE ROUTINE ; THE MODEM STATUS REGESTER DETECTED A CHANGE IN ONE OF THE ; MODEM LINES. I COULD CHECK THE "DELTA BITS" TO SEE EXACTLY WHICH ; LINE TRIGGERED THIS INTERUPT, BUT I JUST CHECK ALL OF THEM WHEN ; I GET ANY MODEM STATUS INTERUPT. INT_MODEM: ADD DX,MSTAT-INTID ;READ THE MODEM STATUS REGESTER IN AL,DX ;********************************* ;CHECK THE CARIER-DETECT BIT (CD) TEST AL,080H ;IS CARRIER DETECT OFF? JNE MSDSR ;NO, CHECK DSR NEXT TEST OUTSPEC[SI],OUTCDF ;IS CD THE OFF-LINE SIGNAL? JE MSDSR ;NO, IGNORE CD THEN. OR MODEM[SI],MODOFF ;YES,SET OFLINE FOR NEXT READ REQUEST ;************************************** ;CHECK THE DATA-SET-READY BIT (DSR) MSDSR: TEST AL,020H ;IS DSR OFF? JNE DSRON ;NO, GO CHECK TO SEE IF I WAS WAITING ON IT. TEST OUTSPEC[SI],OUTDSR ;IS DSR THE OUTPUT DATA THROTTLE FLG? JE DSROFF ;NO, MABY IT'S OFFLINE SIGNAL OR LINE[SI],LINDSR ;YES, SUSPEND OUTPUT WAITING ON DSR DSROFF: TEST OUTSPEC[SI],OUTDRF ;IS DSR THE OFFLINE SIGNAL? JE MSCTS ;NOPE. OR MODEM[SI],MODOFF ;YES, SET FLAG FOR NEXT READ. JMP MSCTS DSRON: TEST LINE[SI],LINDSR ;WAS I WAITING FOR DSR TO COME ON? JE MSCTS ;NO, IGNORE IT. AND LINE[SI],NOT LINDSR ;YES, CLEAR THE BIT, AND CALL START_OUTPUT ;START OUTPUT BACK UP AGAIN. ;**************************************** ;CHECK THE CLEAR-TO-SEND BIT (CTS) MSCTS: TEST AL,010H ;IS CTS OFF? JNE CTSON ;NO, GO CHECK TO SEE IF ITS EXPECTED TEST OUTSPEC[SI],OUTCTS ;IS CSR THE OUTPUT DATA THROTTLER? JE CTSOFF ;NO, MABY IT'S OFFLINE SIGNAL OR LINE[SI],LINCTS ;YES, SUSPEND OUTPUT. CTSOFF: TEST OUTSPEC[SI],OUTCTS ;IS CTS THE OFF-LINE SIGNAL? JE INT_EXIT4 ;NOPE. OR MODEM[SI],MODOFF ;YES, SET FLAG FOR NEXT READ. JMP INT_EXIT CTSON: TEST LINE[SI],LINCTS ;WAS I WAITING FOR THIS CTS? JE INT_EXIT4 ;NO, THERE'S NOTHING LEFT TO CHECK. AND LINE[SI],NOT LINCTS ;YES, CLEAR THE BIT, AND CALL START_OUTPUT ;START OUTPUT UP AGAIN. INT_EXIT4: JMP INT_EXIT SUBTTL TRANSMITTER INTERUPT SERVICE ROUTINE PAGE ; THE TRANSMITTER HOLDING REGESTER IS EMPTY, LOOK TO SEE IF INT_TXMIT: ;THERE ARE MORE CHARS TO PRINT NOW. AND LINE[SI],NOT LINEXP ;CLEAR INTERUPT EXPECTED BIT. CALL START_OUTPUT ;START THE NEXT CHARACTER JMP INT_EXIT ;ROUTINE TO START THE NEXT CHARACTER PRINTING ON THE UART, IF OUTPUT ;IS NOT BEING SUSPENDED FOR ONE REASON OR ANOTHER. ;THIS ROUTINE MAY BE CALLED FROM REQUEST ROUTINES, OR FROM INTERUPT ;SEVICE ROUTINES. ; THIS ROUTINE DESTROYS AX AND DX. ; SX MUST POINT AT THE UNIT STRUCTURE. START_OUTPUT PROC NEAR PUSHF ;SAVE THE FLAGS SO I CAN CLI ;DISABLE INTERUPTS TEST LINE[SI],LINIDL ;AM I IN HOLD OUTPUT MODE? JNE DONT_START ;YES, DON'T SEND ANY MORE CHARS. CALL GET_OUT ;CHECK TO SEE IF THERE IS A CHAR IN THE BUF CMP AH,0 ;WELL, WAS THERE? JNE DONT_START ;NO, BUFFER IS EMPTY MOV DX,PORT[SI] ;YES, POINT DX AT THE TX OUT REGISTER OUT DX,AL ;SEND HIM THE CHARACTER OR LINE[SI],LINEXP ;WARN EVERYBODY THAT I'M BUSY. DONT_START: POPF RET START_OUTPUT ENDP ; THE FOLLOWING LABEL DEFINES THE END OF THE DRIVER, SO I ; CAN TELL DOS HOW BIG I AM. ASYNC_END: SUBTTL INITIALIZATION REQUEST ROUTINE PAGE ; ; THE INITIALIZE DRIVER ROUTINE IS STORED AFTER THE "END" ; OF THE DRIVER HERE SO THAT THIS CODE CAN BE THROWN AWAY AFTER ; THE DEVICE HAS BEEN INITIALIZED. THIS CODE IS ONLY CALLED TWICE: ; ONCE TO INITIALIZE EACH OF THE ASYNC UNITS THAT THIS DRIVER ; CONTAINS. (HOPEFULLY, MSDOS DOESN'T WRITE ANYTHING ON TOP OF ; THIS CODE UNIT BOTH UNITS ARE INITIALIZED. ; THE CONTEXT OF THE INITIALIZE CODE BELOW IS THE SAME AS ; ALL THE OTHER REQUEST ROUTINES EARLIER IN THE DRIVER. ; ; INITIALIZE THE DRIVER AND DEVICE ; ASYNC_INIT: MOV AX,OFFSET ASYNC_END ;GET THE SIZE OF THE DRIVER MOV ES:XFER[BX],AX ;SEND THAT BACK IN PACKET MOV ES:XSEG[BX],CS ;SEND THE CODE SEGMENT ALSO. ;I HAVE SATISFIED ALL THE REQIREMENTS OF THE ;INIT FUNCTION TO RETURN IN THE I/O PACKET, SO ;I CAN DESTROY THE CONTENTS OF ES:BX AND USE ;THEM FOR OTHER THINGS. MOV AX,0 ;POINT ES AT THE VECTOR SEGMENT MOV ES,AX ;SO CAN INITIALIZE THE VECTORS MOV AX,ISRADR[SI] ;GET ADRS OF INTERUPT SERVICE ROUTINE ;THE FOLLOWING CODE IS SPECIFIC TO THE IBM: ;BASIC USES LOCATIONS 400 AND 402 TO FIND ;THE PORT ADDRESSES OF COM1 AND COM2. IF I ;ZERO THESE, THEN BASIC CANNOT MUCK UP THE ;REGESTERS ON ME ANY MORE! (IT STILL ;DISABLES INTERUPTS, THOUGH. BUMMER!) MOV DI,400 ;POINT AT THE ASYNC ADDRESS LIST CLD STOS WORD PTR [DI] ;CLOBBER THE FIRST ONE, STOS WORD PTR [DI] ;AND THE SECOND ONE. ;NOW WE'RE BACK ON THE GENERIC MSDOS TRACK. MOV DI,VECT[SI] ;GET ADRS OF VECOTR, STOS WORD PTR [DI] ;STORE THE OFFSET THERE, THEN MOV ES:[DI],CS ;THE SEGMENT IN THE FOLLOWING WORD. MOV CX,DI ;GET THE VECTOR NUMBER, SUB CL,022H ;SUBTRACT BIAS TO HARDWARE INTS, SAR CL,1 ;DIVIDE BY 4 TO CONVERT TO SAR CL,1 ;HARDWARE INTERUPT NUMBER. MOV AH,1 ;SHIFT A MASK BY THAT MUCH TO SAL AH,CL ;CREATE INTERUPT ENABLE MASK BIT, NOT AH ;WHICH IS ACTIVE LOW... IN AL,021H ;GET SYSTEM HARDWARE INTERUPT MASK AND AL,AH ;AND MY BIT OUT OF IT, OUT 021H,AL ;WRITE IT BACK OUT AGAIN. MOV DX,PORT[SI] ;GET THE PORT ADDRESS OF THIS LINE ADD DX,INTID INT_CAN: IN AL,DX ;GET INTERUPT ID REGESTER TEST AL,1 ;IF HE HAS ANYTHING TO COMPLAINE JNZ INT_NONE ;ABOUT, READ THEM ALL AND IGNORE. ADD DX,LSTAT-INTID ;JUST TO MAKE UART HAPPY, READ THE IN AL,DX ;LINE STATUS AND ADD DX,MSTAT-LSTAT ;THE MODEM STATUS TO IN AL,DX ;"RESET INTERUPT CONTROL" ADD DX,RXBUF-MSTAT ;READING THE RECEIVER MIGHT IN AL,DX ;HELP ALSO. ADD DX,INTID-RXBUF ;POINT BACK AT INTERUPT ID, JMP INT_CAN ;AND DO THIS AGAIN. INT_NONE: ADD DX,LSTAT-INTID ;CALC ADDRESS OF LINE STATUS, IN AL,DX ;INPUT IT OR SOMETIMES IT DOESN'T WORK ADD DX,LCTRL-LSTAT ;CALC ADDRESS OF LINE CONTROL REG MOV AL,LINE[SI] ;GET THE DEFAULT LINE OR AL,DLAB ;SET THE DIVISOR LATCH BIT OUT DX,AL ;SET UP DEFAULT LINE CHARS SUB DX,LCTRL ;POINT BACK AT FIRST PORT MOV AX,BAUD[SI] ;GET DIVISOR VALUE FOR BAUD RATE OUT DX,AL ;SEND LOW BYTE INC DX ;INC TO HIGH BYTE PORT MOV AL,AH ;GET HIGH BYTE, OUT DX,AL ;AND SEND TO BOARD. ADD DX,LCTRL-1 ;POINT AT LINE CONTROL AGAIN, MOV AL,LINE[SI] ;GET DEFAULT LINE CONTROL BITS AGAIN OUT DX,AL ;SET THEM WITHOUT DLAB ON. MOV LINE[SI],0 ;RE-USE LINE OFFSET AS STATUS SUB DX,LCTRL-INTEN ;POINT DX AT INTERUPT ENABLE PORT MOV AL,ALLINT ;SET UP TO GET ALL POSSIBLE INTS OUT DX,AL ;IN THE INTEN REGESTER. ADD DX,MCTRL-INTEN ;POINT AT MODEM STATUS REGESTER MOV AL,MODEM[SI] ;GET THE DEFAULT MODEM STATUS BITS, OUT DX,AL ;AND SET THEM IN MCTRL. MOV MODEM[SI],0 ;RE-USE THIS BYTE FOR INPUT STATUS. TEST INSPEC[SI],INXON ;IS INPUT THROTTLED WITH XON? JE DONE_INIT ;NO, LEAVE HIM BE. MOV AL,'Q' AND 01FH ;YES, SEND A CONTROL-Q MOV DX,PORT[SI] ;TO THE DEVICE AT INIT TIME, OUT DX,AL ;TO MAKE SURE IT WAKES UP. DONE_INIT: MOV AX,0 ;RETURN NO ERRORS. JMP EXIT DRIVER ENDS END S!H!A!R fi # end of overwriting check if test -f 'ioctl.asm' then echo shar: will not over-write existing file "'ioctl.asm'" else cat > 'ioctl.asm' << \S!H!A!R PAGE 56,132 TITLE IOCTL PERFORM IOCTL CALL FOR C MAIN PROGRAMS ; (c) Copyright 1984 by The Computer Entomologist ; ; Permission is hearby granted to use or distribute this software ;without any restrictions. You may make copies for yourself or your ;friends. You may include it in any hardware or software product that you ;sell for profit. ; ; This software is distributed as is, and is not guaranteed to work ;on any particular hardware/software configuration. Furthermore, no ;liability is granted with this software: the user takes responcibility for ;any damage this software may do to his system. ; ; Nasy notices aside, if you have any questions about this software, you ;can reach me at the address below. If you impliment any new features or ;find (and fix!) any bugs, I would be happy to hear from you. ; ; Mike Higgins ; The Computer Entomologist ; P.O. Box 197 ; Duncans Mills, CA 95430 PUBLIC IOCTL ;(ASYNC,BUF,LEN,FUNC) @CODE SEGMENT PUBLIC 'CODE' ASSUME CS:@CODE IOCTL PROC NEAR ;ARGUMENTS: ASYNC=4 ;ADDRESS OF ASCIZ DEVICE NAME. BUF=6 ;ADDRESS OF A BUFFER TO READ/WRITE FROM LEN=8 ;THE NUMBER OF BYTES IN BUFFER FUNC=10 ;2 FOR IOCTL READ, 3 FOR IOCTL WRITE PUSH BP ;DO THE NORMAL C LINKAGE MOV BP,SP MOV DX,ASYNC[BP] ;GET NAME OF DEVICE TO OPEN MOV AX,03D02H ;OPEN IT FOR READ/WRITE. INT 021H ;WELL, CAN DOS DO IT? MOV BX,AX ;STORE HANDLE IN BX MOV DX,BUF[BP] ;GET BUFFER ADDRESS MOV CX,LEN[BP] ;AND LENGTH. MOV AX,FUNC[BP] ;GET FUNCTION REQUESTED, MOV AH,68 ;TELL DOS THIS IS IOCTL CALL INT 021H ;WAKE UP DOS. MOV AH,62 ;SET UP TO CLOSE HANDLE WHEN DONE INT 021H ;ASK DOS REAL NICE. POP BP ;RECOVER BP RET ;RETURN TO C IOCTL ENDP @CODE ENDS END S!H!A!R fi # end of overwriting check if test -f 'term.c' then echo shar: will not over-write existing file "'term.c'" else cat > 'term.c' << \S!H!A!R /* FILE: TERM Program to set all the settable features of the async deivce driver on an MSDOS system. ; (c) Copyright 1984 by The Computer Entomologist ; ; Permission is hearby granted to use or distribute this software ;without any restrictions. You may make copies for yourself or your ;friends. You may include it in any hardware or software product that you ;sell for profit. ; ; This software is distributed as is, and is not guaranteed to work ;on any particular hardware/software configuration. Furthermore, no ;liability is granted with this software: the user takes responcibility for ;any damage this software may do to his system. ; ; Nasy notices aside, if you have any questions about this software, you ;can reach me at the address below. If you impliment any new features or ;find (and fix!) any bugs, I would be happy to hear from you. ; ; Mike Higgins ; The Computer Entomologist ; P.O. Box 197 ; Duncans Mills, CA 95430 **WARNING**** Not all of the features below are implimented yet in the driver itself. Do not expect them to work unless you see the code in the driver first. In particular, input data throttling and output tab conversions are not implimented yet. */ char *innam[]={"dtr is input data throttle signal\n", "rts is input data throttle signal\n", "inxon -- xon/xoff is used to throttle input data\n", "bell is echoed on input buffer full\n", "half duplex -- input chars are echoed when read\n", "flush sequence is recognized (control-x)\n", "redraw sequence is recognized (control-r)\n", "delete line sequence is recognized (control-u)\n", "skip output sequence is recognized (contril-o)\n", "errstat -- line errors produce MSDOS error status returns\n", "errcodes -- line errors return codes with parity bit on\n", "strip parity on input characters\n", "upper case all input characters\n", "control-c passes through input buffer on non-destructive read\n", "reboot sequence is recognized (control-c control-r control-b\n",0}; int inval[]={2,4,8,0x10,0x20,0x40,0x80, 0x100,0x200,0x400,0x800,0x1000,0x2000, 0x4000,0x8000,0}; char *outnam[]={"dsr is output data throttle signal\n", "cts is output data throttle signal\n", "xonxoff throttles output data\n", "offcts -- cts is ofline signal\n", "offcd -- carrier detect is offline signal\n", "offdsr -- dsr is offline signal\n", "outstrip -- strip parity on all output characters\n", "outupper -- convert output chars to upper case\n", "tabs are converted to spaces on output\n",0}; int outval[]={2,4,8,0x10,0x20,0x40,0x1000,0x2000,0x4000,0}; char defname[]="ASYNC1"; struct asy_tab /*structure of the return from the ioctl to async*/ { char lctrl; /*line control byte*/ char mctrl; /*modem control*/ char lstat; /*line status*/ char mstat; /*modem status*/ int inspec; /*input special processing bits*/ int outspec; /*output special processing bits*/ int baud; /*baud rate*/ } buf; int i,j,l; /*pointers and loop counters*/ int change=0; /*flag set to 1 if any bits are changed*/ int flag=0; /*flag set to 1 if option is recognized*/ int nope=0; /*flag set to 1 if option is negated*/ char *dev=defname; /*pointer to device name*/ char *ptr; /*general pointer to strings*/ main(argc,argv) int argc; char *argv[]; { int baud=0; int setin=0; /*bits to set in input mask*/ int clrin=0; /*bits to clear in input mask*/ int setout=0; /*bits to set in output mask*/ int clrout=0; /*bits to clear in output mask*/ j=0; i=0; while(++i < argc) /*for all input arguments,*/ { nope=0; flag=0; /*initialize flags*/ dolower(argv[i]); /*convert them to upper case*/ l=strlen(argv[i]); if (argv[i][l-1] == ':') /*is this a device name?*/ { dev=argv[i]; /*then remember it's address*/ dev[l-1]=0; /*and get rid of that colon!*/ flag=1; /*mark this as a legal command*/ } if (strncmp(argv[i],"spe",3) == 0) /*is this a baud change?*/ { ptr=argv[i]; while (*ptr != 0) /*scan forward looking for =*/ { if (*ptr++ == '=') { if (sscanf(ptr,"%d",&baud) == 1) { change=1; /*set flag forcing ioctlwrite*/ flag=1; /*flag this as legal command*/ buf.baud=baud; /*store the new baud rate*/ } break; } } } if (strncmp(argv[i],"help",l) == 0) /*asking for help?*/ { printf("This command accepts many options to control the\n"); printf("behavior of the ASYNC drivers.\n"); printf("Any option terminated with : is assumed to be the\n"); printf("name of the ASYNC drive to operate on. ASYNC1: is default.\n"); printf("The option SPEED=nnn will set the baud rate of the driver\n"); printf("to nnn. Illegal baud rates are ignored.\n"); printf("All the following ASYNC driver options can be set by\n"); printf("typing the first few letters of the option. To turn\n"); printf("an option off, prefix it with NO.\n"); j=0; /*loop for all the output names*/ while (outnam[j] != 0) printf(outnam[j++]); j=0; /*loop for all the input names*/ while (innam[j] != 0) printf(innam[j++]); flag=1; /*mark this a legal flag*/ } if (strncmp(argv[i],"no",2) == 0) /*is this a negation?*/ { argv[i] += 2; /*skip over the "no"*/ l -= 2; /*take "no" off the length*/ nope=1; /*set flag*/ } if (flag == 0) { j=0; /*loop for all the input names*/ while (innam[j] != 0) { if (strncmp(argv[i],innam[j],l) == 0) { flag=1; /*recognise this command*/ if (nope == 0) setin |= inval[j]; /*set this bit*/ else clrin |= inval[j]; /*or clear it*/ change=1; /*set the changed bit*/ break; } j += 1; } } if (flag == 0) { j=0; /*loop for all the output names*/ while (outnam[j] != 0) { if (strncmp(argv[i],outnam[j],l) == 0) { flag=1; /*i recognise this command,*/ if (nope == 0) /*so set the bit that this option*/ setout |= outval[j]; /*corresponds to*/ else clrout |= outval[j]; /*or clear it*/ change=1; break; } j += 1; } } if (flag == 0) /*did anybody clame this command?*/ printf("Illegal option: %s\n",argv[i]); } ioctl(dev,&buf,10,2); /*call the assembly ioctl for read*/ if (change != 0) /*were you changing any of the options?*/ { if (baud != 0) buf.baud=baud; buf.inspec |= setin; /*set any requested bits*/ buf.outspec |= setout; buf.inspec &= ~clrin; /*clear any requested bits*/ buf.outspec &= ~clrout; ioctl(dev,&buf,10,3); /*request the ioctl write*/ ioctl(dev,&buf,10,2); /*and read it back to doublecheck*/ } printf("status of device %s: Baud rate: %d\n",dev,buf.baud); /* printf("Line control: %x Modem control: %x\n",buf.lctrl,buf.mctrl); printf("Line status: %x Modem status: %x\n",buf.lstat,buf.mstat); */ printf(" >>>>Special treatment on input:\n"); outstr(buf.inspec,inval,innam); /*print all the input bits*/ printf(" >>>>Special treatment on output:\n"); outstr(buf.outspec,outval,outnam); /*dito for all the output bits*/ exit(1); } /*subroutine to print all the strings who's bits are set*/ outstr(bits,vals,nams) int bits; /*mask with bits set in it*/ int vals[]; /*array of bit values*/ char *nams[]; /*array of pointers to bit name strings*/ { i=0; /*initialize index to array*/ while (vals[i] != 0) { if ((vals[i] & bits) != 0) /*is this bit set?*/ printf(nams[i]); /*yes, print the string*/ i++; } return; } /*subroutine to lowercase a string*/ dolower(str) char *str; { while(*str != 0) { if ((*str >= 'A') && (*str <= 'Z')) *str |= 040; /*and in the lower case bit*/ str++; } return; } S!H!A!R fi # end of overwriting check # End of shell archive exit 0 -- I_I( _ UUCP: ..mcvax!honzo@4gl I I ) Honzo Svasek @ 4GL Consultants b.v. FIDO: Honzo Svasek @ 500/333 ----------------------------------------------------------------------------- You, In front of a terminal? . . . . . . . . . . . I'd rather be hanggliding!