home *** CD-ROM | disk | FTP | other *** search
- page 58,132
- ; file: MIDI.ASM
- TITLE Z8470 MIDI driver for MSDOS
- SUBTTL DESCRIPTION
- ;THINGS TO ADD: (DELETE THESE LINES AS YOU IMPLIMENT THEM)
- ;data errors reported at the time you get to that byte in read buffer
- ;
- ;
- ; Loadable midi device driver for msdos.
- ; Written by: Mike Higgins
- ; Copyright (c) April 1984 by The Computer Entomologist.
- ;
- ;************************************************************************
- ; This file implements an interrupt driven character device driver
- ;for the midi board described in the article: "A MIDI Project", by Jay
- ;Kupicky, BYTE Magazine, June 1986, V.11 N.6. The driver will work on any
- ;similar board that uses the Z8470 Z80 DART chip. This code should work
- ;with several DART chips on one board, if you change the appropriate
- ;counters, add structures and buffers for each UART, and check the interrupt
- ;and initialization code.
- ; Why write or use such a driver? Because the code written by
- ;Jay Kubicky could only be compiled on one specific C compiler, and
- ;required non-standard communication channels between the main code and
- ;the assembly driver.
- ; This driver implements the MIDI board as a standard MSDOS character
- ;device that can be called from any program written in any language. Even
- ;interpretative BASIC can open this device and perform any function that
- ;the board is capable of. All the communication with the driver is
- ;done with standard reads,writes, and IOCTL calls. The character input/
- ;output is the MIDI data, and the IOCTL data allows a program to find the
- ;state of the MIDI UARTS, and to perform initialization functions. (Test
- ;for data available or error status, flush buffers, change echo/thru
- ;modes). Additionally, there is a read-with-timeout feature that
- ;allows the driver to return to you if no data arrives after a specified
- ;number of milliseconds.
- ; This driver implements a 128 byte buffer on input and output to
- ;make catching all the MIDI data as easy as possible for you programs.
- ;The output buffer allows your programs to send multiple byte commands
- ;without having to wait on each byte: your processing can continue while
- ;the driver sends the bytes out one at a time for you.
- ; There is also a "MIDI out/through" option in this driver that
- ;will copy, if enabled, any incoming bytes immediately to the associated
- ;MIDI out port. I call this a "poor man's MIDI merge" because it will
- ;scramble MIDI commands together if they arrive at the output buffer
- ;too close together. But it does have some limited applications. Note
- ;that a very intelligent MIDI merge program would be easy to write as an
- ;application on top of this board and driver. (Requires both UARTs hooked
- ;up with MIDI drivers and receivers, like mine is).
- ; While initializing the uarts, this driver also initializes the on
- ;board clock chip. You can get direct access to this clock for timing midi
- ;events, and the driver uses this clock to count time for the read-with-
- ;timeout feature. The delta times are in "digital" milliseconds, so the
- ;value changes 1024 times each second. The two byte clock value from timer2
- ;will allow you to time anything that is 64 seconds or less, which should be
- ;long enough for any typical MIDI use. (You will have to store delta times,
- ;not absolute time). On my version of the board, timer2 overflows into
- ;timer3, so this can be considered a 16bit extension of the regular timer.
- ;This results in a four byte clock that only overflows after 136
- ;years. If you can keep your PC running continuously that long (without
- ;re-booting) I'll be very impressed!
- ;
- ;************************************************************************
- ;
- ; Permission is hereby 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 responsibility for
- ;any damage this software may do to his system.
- ;
- ; Nasty notices aside, if you have any questions about this software,
- ;you can reach me at the address below. If you implement 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
- ;
- ;
- ; ASSEMBLY INSTRUCTIONS:
- ; MASM MIDI,MIDI,MIDI,NUL
- ; LINK MIDI,MIDI,MIDI,NUL
- ; EXE2BIN MIDI
- ; COPY MIDI.BIN A: (IF NOT THERE ALREADY)
- ; ADD THE FOLLOWING LINE TO A:CONFIG.SYS:
- ; DRIVER=MIDI.BIN
- ; RE-BOOT YOUR SYSTEM AND IT'S THERE!
- ;
- ; NOTE:
- ;
- ; There are hooks in the driver for features that do not work
- ; yet. 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 implement 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 PORT OFFSETS AND IMPORTANT BOARD CONSTANTS
- MIDDAT EQU 0 ;DATA REGESTER
- MIDCON EQU 2 ;CONTROL REGESTER
- MIDB EQU 1 ;BITS TO FORCE IO ADDRESS TO UART B WITH OR
- MIDA EQU NOT MIDB ;MASK TO FORCE IO ADDR TO UART A WITH AND
- ;
- ; BIT ASSIGNMENTS FOR THE STATUS WORD IN EACH UNIT STRUCTURE
- OUTINT EQU 1 ;CHARACTER SENT OUT, INTERUPT SHOULD BE COMMING
- OUTHRU EQU 2 ;INPUT IS ECHOED THRU OUTPUT (POOR MANS MIDI OUT/THRU
- ;
- ; BIT ASSIGNMENTS FOR THE ERVAL WORD IN EACH UNIT STRUCTURE
- ERITMO EQU 1 ;INPUT TIMEOUT ERROR
- EROTMO EQU 2 ;OUTPUT TIMEOUT ERROR
- ERIBFO EQU 4 ;INPUT BUFFER OVERFLOW
- EROBFO EQU 8 ;OUPUT BUFFER OVERFLOW
- ERPARE EQU 10H ;PARITY ERROR (NOT ENABLED)
- ERFRAM EQU 20H ;FRAMING ERROR
- ERRXOV EQU 40H ;RECEVER OVERRUN (NOT LIKELY)
- ;
- ;BIT DEFINITIONS FOR ALL THE BITS/REGESTERS IN THE Z8470 ZILOG Z80 DART CHIP
- ;
- ;WRITE REGESTER 0 BITS
- ZRESET EQU 010H ;RESET EXT/STATUS INTERUPTS
- ZCHANR EQU 018H ;CHANNEL RESET
- ZRXINT EQU 020H ;ENABLE INT ON NEXT Rx CHARACTER
- ZTXINT EQU 028H ;RESET Tx INT PENDING
- ZERRES EQU 030H ;ERROR RESET
- ZREINT EQU 038H ;RETURN FROM INT (CHANNEL A ONLY)
- ;WRITE REGESTER 1 BITS
- W1EXTI EQU 001H ;EXT INT ENABLE
- W1TXINT EQU 002H ;Tx INT ENABLE
- W1STAT EQU 004H ;STATUS EFFECTS VECTOR (CHANNEL B ONLY)
- W1RXDIS EQU 000H ;Rx INT DISABLE
- W1RX1ST EQU 008H ;Rx INT ON FIRST CHARACTER
- W1RXALP EQU 010H ;Rx INT ON ALL CHARACTERS (PARITY AFFECTS VECTOR)
- W1RXALL EQU 018H ;Rx INT ON ALL CHARACTERS (PARITY DOESN'T AFF. VEC)
- W1WAITR EQU 020H ;WAIT/READY ON R/T
- W1WAITF EQU 040H ;WAIT/READY FUNCTION
- W1WAITE EQU 080H ;WAIT/READY ENABLE
- ;WRITE REGESTER 3 BITS
- W3RXEN EQU 001H ;Rx ENABLE
- W3AUTO EQU 020H ;AUTO ENABLES
- W3RX5 EQU 000H ;Rx 5 BITS/CHAR
- W3RX7 EQU 040H ;Rx 7 BITS/CHAR
- W3RX6 EQU 080H ;Rx 6 BITS/CHAR
- W3RX8 EQU 0C0H ;Rx 8 BITS/CHAR
- ;WRITE REGESTER 4 BITS
- W4PARE EQU 001H ;PARITY ENABLE
- W4PEVN EQU 002H ;PARITY IS EVEN
- W4PODD EQU 000H ;PARITY IS ODD
- W4STOP1 EQU 004H ;ONE STOP BITS
- W4STP15 EQU 008H ;1.5 STOP BITS
- W4STOP2 EQU 00CH ;TWO STOP BITS
- W4X1 EQU 000H ;X1 CLOCK MODE
- W4X16 EQU 040H ;X16 CLOCK MODE
- W4X32 EQU 080H ;X32 CLOCK MODE
- W4X64 EQU 0C0H ;X64 CLOCK MODE
- ;WRITE REGESTER 5 BITS
- W5RTS EQU 002H ;SET RTS BIT IN OUTPUT
- W5TXEN EQU 008H ;Tx ENABLE
- W5BREAK EQU 010H ;SEND BREAK
- W5TX5 EQU 000H ;Tx 5 BITS/CHARACTER
- W5TX7 EQU 020H ;Tx 7 BITS/CHAR
- W5TX6 EQU 040H ;Tx 6 BITS/CHAR
- W5TX8 EQU 060H ;Tx 8 BITS/CHAR
- W5DTR EQU 080H ;SET DTR BIT IN OUTPUT
- ;READ REGESTER 0 BITS
- RXCHAR EQU 001H ;CHARACTER HAS BEEN RECEIVED
- INTPEND EQU 002H ;INTERUPT IS PENDING (CHANNEL A ONLY)
- TXFREE EQU 004H ;Tx BUFFER IS EMPTY
- CD EQU 008H ;CARRIER DETECT LINE IS ON
- RI EQU 010H ;RING INDICATOR LINE IS ON
- CTS EQU 020H ;CLEAR TO SEND LINE IS ON
- BREAK EQU 080H ;BREAK HAS BEEN DETECTED
- ;READ REGESTER 1 BITS
- R1ALL EQU 001H ;ALL SENT (Tx BUFFER?)
- R1PERR EQU 010H ;PARRITY ERROR
- R1RXOVR EQU 020H ;Rx OFERRUN ERROR
- R1FRAME EQU 040H ;FRAMING ERROR
- ;READ REGESTER 2 BITS
- VECMASK EQU 006H ;REASON-FOR-INTERUPT BITS
- VECB EQU 008H ;UART B FIRED INTERUPT
- ;
- ;REGESTER OFFSETS AND BIT ASSIGNMENTS FOR THE CLOCK TIMER CHIP
- TIMER1 EQU -3 ;OFFSET FROM CONTROL REGESTER TO TIMER DATA 1
- TIMER2 EQU -2 ;TIMER 2 DATA REGESTER
- TIMER3 EQU -1 ;TIMER 3
- T1 EQU 0 ;CONTROLLER TIMER 1 SELECT VALUE
- T2 EQU 040H ;SELECT TIMER 2
- T3 EQU 080H ;SELECT TIMER 3
- LMSB EQU 030H ;READ TIMER REGESTERS IN LITTLE ENDIAN MODE
- SHOOT EQU 002H ;ONE SHOT MODE
- RATE EQU 004H ;RATE GENERATOR MODE
- SQUARE EQU 006H ;SQUARE WAVE MODE
- LATCH EQU 0 ;LATCH THE CURRENT VALUES FOR READING
- ;
- SUBTTL DRIVER LIST HEAD
- PAGE
- ;*************************************************************************
- ;
- ; BEGENING OF DRIVER CODE.
- ;
- DRIVER SEGMENT
- ASSUME CS:DRIVER,DS:DRIVER,ES:DRIVER
- ; ORG 0 ;DRIVERS START AT 0
- MIDIB:
- DW MIDIA,-1 ;POINTER TO NEXT DEVICE: DOS FILLS IN SEG.
- DW DEVCHR OR DEVIOC ;CHARACTER DEVICE, IOCTL ALLOWED
- DW STRATEGY ;OFFSET TO STRATEGY ROUTINE.
- DW REQUESTB ;OFFSET TO "INTERUPT" ENTRYPOINT.
- DB "MIDIB " ;DEVICE NAME.
- MIDIA:
- 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 REQUESTA ;I/O REQUEST ROUTINT
- DB "MIDIA "
-
- debug dd 0b0000000h
- notify macro char
- ; local store
- ; push es
- ; push di
- ; push ax
- ; mov al,'&char'
- ; les di,CS:debug
- ; stos byte ptr [di]
- ; inc di
- ; cmp di,80*25*2
- ; jl store
- ; xor di,di
- ;store:
- ; mov word ptr CS:debug,di
- ; mov al,'<'
- ; stos byte ptr es:[di]
- ; pop ax
- ; pop di
- ; pop es
- endm
-
- SUBTTL DRIVER INTERNAL DATA STRUCTURES
- PAGE
- ;
- MIDI_UNITS EQU 2 ;NUMBER OF UNITS THIS DRIVER IS BUILT FOR
- MIDI_VEC DW 028H ;INTERUPT VECTOR ADDRESS (NOT VECTOR NUMBER)
- CLOCK_PORT DW 0FFA7H ;PORT OF CLOCK CONTROL REGESTER
- ;
- UNIT STRUC ;EACH UNIT HAS A STRUCTURE DEFINING IT'S STATE:
- PORT DW ? ;I/O PORT ADDRESS
- STATUS DW ? ;STATUS BITS
- TIMEOUT DW ? ;TIMEOUT CONSTANT ON READ OR WRITE REQUEST
- ERVAL DW ? ;BIT ENCODED ERRORS SINCE LAST YOU LOOKED
- EROFF DW ? ;OFFSET TO FIRST CHARACTER AFTER ERROR
- 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 MIDI UNIT
- ; THE STRUCTURES FOR ALL THE UARTS MUST BE ORGANIZED TOGETHER INTO
- ; A CONTIGUOUS TABLE, BECAUSE THE INTERUPT SERVICE ROUTINE SCANS
- ; THROUGH THEM WHILE DETERMINING WHICH DART FIRED THE INTERUPT.
- ; SIMILARLY, THE STRUCTURES FOR THE TWO UARTS IN EACH DART MUST BI
- ; IN A-B ORDER IN THIS TABLE.
- ;
- MIDI_TABA:
- UNIT <0FFA0H,0,-1,0,-1,0,0,INABUF,0,0,OUTABUF>
- UNIT_SIZE EQU $-MIDI_TABA
- MIDI_TABB:
- UNIT <0FFA1H,0,-1,0,-1,0,0,INBBUF,0,0,OUTBBUF>
-
- ;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
- INABUF DB BUFSIZ DUP (?)
- INBBUF DB BUFSIZ DUP (?)
- OUTABUF DB BUFSIZ DUP (?)
- OUTBBUF DB BUFSIZ DUP (?)
- ;
- ; 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.
- PUBLIC STRATEGY
- 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
-
- ; I/O REQUEST ROUTINES
- PUBLIC REQUESTA
- REQUESTA: ;MIDIA HAS BEEN REQUESTED
- PUSH SI ;SAVE SI SO YOU CAN
- MOV SI,OFFSET MIDI_TABA ;GET THE DEVICE UNIT TABLE ADDRESS.
- JMP GEN_REQUEST ;THE GENERIC DRIVER DOES THE REST.
- REQUESTB: ;MIDIB HAS BEEN REQUESTED TO DO SOMETHING
- PUSH SI ;SAVE SI
- MOV SI,OFFSET MIDI_TABB ;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 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,
- MOV DI,AX ;AND ADD TO THE TABLE START ADDRESS
- MOV AX,STSERR OR ERRUC ;SEND UNKNOWN COMMAND ERROR FOR EXIT
- JMP MIDI_FUNCS[DI] ;JUMP TO THE APPROPRIATE ROUTINE
- ;
- ; TABLE OF OFFSETS TO ALL THE DRIVER FUNCTIONS
- ;
- MIDI_FUNCS DW MIDI_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 EXIT ;NON-DESTRUCTIVE READ
- DW EXIT ;INPUT STATUS
- DW EXIT ;FLUSH INPUT BUFFER
- DW WRITE ;WRITE
- DW WRITE ;WRITE WITH VERIFY
- DW EXIT ;OUTPUT STATUS
- DW EXIT ;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:
- CALL GET_CLOCK ;GET CLOCK CURRENT VALUE FOR READ-
- MOV DX,AX ;WITH-TIMEOUT TEST, AND STORE IT IN DX
- MOV CX,ES:COUNT[BX] ;GET THE REQUESTED NUMBER OF BYTES
- LES DI,DWORD PTR ES:XFER[BX] ;DI IS OFFSET TO USER BUFFER
- MOV BX,TIMEOUT[SI] ;GET THE TIMEOUT LIMIT.
- RLUP:
- MOV AX,EROFF[SI] ;CHECK TO SEE IF IT'S TIME
- CMP AX,IFIRST[SI] ;TO REPORT AN ERROR
- JE ERR_READ
- CALL GET_IN ;GET NEXT CHAR FROM INPUT BUFFER
- CMP AH,0 ;WAS THERE ONE?
- JNE READ_WAIT ;NO, TEST FOR TIMEOUT
- STOS BYTE PTR[DI] ;YES,WRITE THIS BYTE OUT
- ;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 AX,STSDNE ;RETURN NO ERRORS IN AX IF DONE.
- JMP EXIT
- ERR_READ: ;NOW IS THE TIME TO REPORT AN INPUT ERROR
- MOV AX,-1 ;DISABLE THE ERROR CHECKING BY
- MOV EROFF[SI],AX ;SETTING OFFSET TO FFFF
- OR ERVAL[SI],ERIBFO ;SET INPUT BUFFER OVERFLOW BIT
- JMP ERR_RET
- READ_WAIT:
- CMP BX,0FFFFH ;IF THE TIMEOUT VALUE IS MAX INT,
- JE RLUP ;LOOP BACK UP FOREVER
- CALL GET_CLOCK
- SUB AX,DX ;CALCULATE THE DELTA TIME
- NEG AX
- CMP AX,BX ;COMPARE AGAINST THE TIMOUT LIMIT
- JB RLUP ;LOOP BACK IF TIME IS STILL OK
- OR ERVAL[SI],ERITMO ;TELL THEM A TIMEOUT ERROR HAPPENED
- ERR_RET:
- LES BX,PACKHEAD ;GET IO PACKET BACK
- SUB ES:COUNT[BX],CX ;TELL HIM HOW MANY BYTES MADE IT, AND
- MOV AX,STSBSY ;RETURN A READ
- JMP EXIT ;FAULT ERROR.
- ;
- SUBTTL WRITE REQUEST ROUTINE
- PAGE
- ; OUTPUT DATA TO DEVICE
- ;
- WRITE:
- CALL GET_CLOCK ;GET CLOCK VALUE FOR TIMEOUT TEST
- MOV DX,AX ;DX WILL CONTAIN STARTING TIME
- MOV CX,ES:COUNT[BX] ;GET BYTE COUNT,
- LES DI,DWORD PTR ES:XFER[BX] ;GET XFER ADDRESS
- MOV BX,TIMEOUT[SI] ;GET TIMEOUT LIMIT
- WLUP:
- MOV AL,ES:[DI] ;GET THE NEXT CHAR DOS SENT YOU
- CALL PUT_OUT ;ATTEMPT TO PUT IN IN OUTPUT BUFFER
- CMP AH,0 ;DID IT WORK?
- JNE WWAIT ;NO, GO TEST FOR TIMEOUT
- INC DI ;SKIP TO NEXT BYTE
- CALL START_OUTPUT ;START THE XMITTER IF NECC.
- LOOP WLUP ;YES, GO GET NEXT CHAR.
- MOV AX,STSDNE ;RETURN SUCCESS
- JMP EXIT
- WWAIT:
- CALL GET_CLOCK ;GET CURRENT CLOCK TIME
- SUB AX,DX ;CALCULATE NUMBER OF MILISECONDS SINCE START
- NEG AX
- CMP AX,BX ;HAVE WE WAITED LONG ENUF?
- JB WLUP ;NO, KEEP TRYING
- OR ERVAL[SI],EROTMO ;TELL THEM OUTPUT TIMED OUT
- LES BX,PACKHEAD ;GET IO PACKET BACK
- SUB ES:COUNT[BX],CX ;TELL HIM HOW MANY BYTES MADE IT, AND
- MOV AX,STSBSY ;YES, RETURN A WRITE FAULT ERROR
- JMP EXIT
- ;
- SUBTTL I/O CONTROL READ REQUEST
- PAGE
- ;
- ; IOCONTROL READ REQUEST, RETURN INTERNAL STRUCTURE
- ;
- 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,UNIT_SIZE ;ONLY WORKS WHEN YOU GIVE ME A
- JE DOIOCIN ;RIGHT SIZED BUFFER TO STOMP ON.
- MOV AX,STSERR OR ERRBSL ;RETURN AN ERROR IF NOT RIGHT.
- JMP EXIT
- DOIOCIN:
- REP MOVSB ;JUST COPY THE STRUCTURE TO CALLER
- SUB SI,UNIT_SIZE ;POINT BACK AT UNIT STRUCTURE
- MOV AX,0 ;SO YOU CAN ZERO THE
- MOV ERVAL[SI],AX ;ERROR WORD EVERY TIME THEY READ IT
- MOV AX,STSDNE ;RETURN NO ERRORS
- JMP EXIT
- ;
- SUBTTL I/O CONTROL WRITE REQUEST ROUTINE
- PAGE
- ;
- ; I USE THIS COMMAND TO COMMUNICATE VARIOUS SPECIAL COMMANDS
- ; TO THE DRIVER. EACH BYTE SENT IS A SEPARATE COMMAND TO FLUSH A
- ; BUFFER, CLEAR STATUS, SET SPECIAL FLAGS, ETC.
- ;
- IOCTLOUT:
- MOV CX,ES:COUNT[BX] ;GET THE BYTE COUNT
- LES DI,DWORD PTR ES:XFER[BX] ;AND THE ADDRESS
- IOWRITE: ;LOOP FOR ALL IO CONTROL WRITE BYTES
- MOV BL,ES:[DI] ;GET NEXT BYTE
- INC DI ;SKIP TO FOLLOWING ONE
- AND BX,7 ;CHOP OUT LOWER 3 BITS
- SHL BX,1 ;CONVERT TO WORD INDEX
- CMP BL,IOWSIZE ;IS THIS A LEGAL REQUEST?
- JG IOWERR ;NO RETURN A WRITE ERROR
- JMP WORD PTR CS:IOTAB[BX] ;YES, EXECUTE APPROPRIATE CODE
- IOTAB DW IOWFLI ;FLUSH INPUT BUFFER
- DW IOWFLO ;FLUSH OUTPUT BUFFER
- DW IOWTIM ;SET IO TIMEOUT LIMIT
- DW IOWSEO ;SET BYTEWISE MIDI OUTPUT/THRU FLAG
- DW IOWCLO ;CLEAR POOR MANS OUTPUT/THRU MERGE FLAG
- IOWSIZE EQU $-IOTAB
-
- IOWFLI: ;FLUSH INPUT BUFFER
- ;PUSHF
- CLI ;CLEAR INTERUPTS WHILE YOU
- MOV AX,IFIRST[SI] ;MANIPULATE THE BUFFER POINTERS
- MOV IAVAIL[SI],AX ;SET AVAIL EQUAL FIRST TO EMPTY
- STI
- ;POPF ;ALL DONE AND CLEAR
- MOV AX,0 ;EVERY TIME YOU FLUSH INPUT BUFFER,
- MOV ERVAL[SI],AX ;YOU CAN CLEAR THE ERROR BITS
- DEC AX ;AND RESET THE
- MOV EROFF[SI],AX ;OFFSET TO ERROR TO AN ILEGAL VALUE
- JMP IOWNEXT
- IOWFLO: ;FLUSH OUTPUT BUFFER
- ;PUSHF
- CLI ;CLEAR INTERUPTS WHILE YOU
- MOV AX,OFIRST[SI] ;MANIPULATE THE BUFFER POINTERS
- MOV OAVAIL[SI],AX ;SET AVAIL EQUAL FIRST TO EMPTY
- STI
- ;POPF ;ALL DONE AND CLEAR
- JMP IOWNEXT
- IOWTIM: ;LOAD TIMEOUT LIMIT
- MOV AX,ES:[DI] ;GET NEXT WORD FROM INPUT
- ADD DI,2 ;SKIP WORD IN COMMAND STRING
- SUB CX,2 ;COUNT THOSE BYTES OUT OF LOOP
- JLE IOWERR ;IF THERE WEREN'T ENUF, SCREAM
- MOV TIMEOUT[SI],AX ;STORE THEM IN STRUCTURE
- JMP IOWNEXT
- IOWSEO: ;SET BYTEWISE MIDI OUTPUT/THRU FLAG
- OR STATUS[SI],OUTHRU ;SET THE OUT/THRU BIT IN STATUS
- JMP IOWNEXT
- IOWCLO: ;CLEAR POOR MANS MIDI OPUTPUT/THRU MERGE
- AND STATUS[SI],NOT OUTHRU ;CLEAR THE BIT
- JMP IOWNEXT
- IOWNEXT:
- LOOP IOWRITE ;LOOP FOR ALL IO CONTROL WRITE BYTES
- MOV AX,STSDNE ;RETURN 0 FOR SUCCESS
- JMP EXIT
- IOWERR: ;I/O CONTROL WRITE ERROR RETURN
- MOV AX,STSERR OR ERRBSL ;RETURN WRITE FAULT FOR THESE
- 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:
- STI
- ;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
- STI
- 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
- STI
- 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:
- STI
- ;POPF ;RE-ENABLE INTERUPTS BEFORE YOU RETURN
- POP DI
- POP CX
- RET
- GET_IN ENDP
- SUBTTL INTERUPT SERVICE ROUTINES
- PAGE
- ;
- ; THESE ROUTINES ARE ONLY CALLED WHEN AN INTERUPT IS GENERATED
- ; BY THE UART.
- ; 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.
-
- MIDI_INT:
- PUSH AX ;SAVE AX FOR THE USER,
- MOV AL,020H ;OUTPUT A 20H TO THE UNDOCUMENTED INTERUPT
- OUT 020H,AL ;CONTROL CHIP.
- STI ;AND LET THE PROCESSOR INTERUPT ME.
- PUSH BX ;SAVE THE REST OF THE REGESTERS.
- PUSH CX
- PUSH DX
- PUSH SI
- PUSH DI
- PUSH DS ;SAVE THE DATA SEGMENT
- PUSH CS ;SO YOU CAN LOAD CS
- POP DS ;INTO DS AND FIND YOUR OWN STRUCTURES.
- notify I
- ;
- ; THE FOLLOWING CODE FIGURES OUT WHICH (IF SEVERAL) DAISY-CHAINED
- ; DART CHIP FIRED THE INTERUPT, WHICH UART IN THE DART IT WAS,
- ; AND THE REASON FOR THE INTERUPT (RECEIVE, XMIT, ERROR).
- ;
- MOV CX,MIDI_UNITS ;GET THE TOTAL NUMBER OF UARTS
- MOV SI,OFFSET MIDI_TABA ;ADDRESS OF FIRST UARTS UNIT TABLE
- INT_LUP: ;HEAD OF LOOP TO CHECK ALL UARTS
- MOV DX,PORT[SI] ;GET THE PORT ADDRESS
- ADD DX,MIDCON ;SLIDE UP TO COMMAND/STATUS REGESTER
- MOV AL,0 ;MAKE SURE WE ARE TALKING TO REGESTER 0
- OUT DX,AL
- JMP SHORT $+2
- IN AL,DX ;READ REGESTER 0 STATUS
- TEST AL,INTPEND ;IS THIS DART CHIP THE ONE?
- JNE THIS_DART ;YES, GO ON TO FIND THE PORT
- ADD SI,UNIT_SIZE*2 ;NO, SKIP TWO UARTS TO NEXT WHOLE DART
- SUB CX,2
- JG INT_LUP ;IF THERE ARE ANY LEFT, KEEP TRYING
- JMP INT_EXIT ;IF NOT, YOU'RE IN TROUBLE, BUT I IGNORE
- THIS_DART:
- OR DX,MIDB ;SLIDE UP TO UART B ON THIS DART
- MOV AL,2 ;SET UP TO READ REGISTER 2
- OUT DX,AL
- JMP SHORT $+2
- IN AL,DX ;READ IN VECTOR VALUE
- TEST AL,VECB ;WAS THIS INTERUPT CAUSED BY UART B?
- JNE A_INT ;NO, IT MUST BE UART A
- DEC CX ;YES, MAKE SURE THERE IS A UART B STRUCTURE
- JCXZ INT_EXIT ;IGNORE INTERUPTS FROM UNUSED UARTS
- ADD SI,UNIT_SIZE ;GET UART B'S UNIT STRUCTURE
- JMP INT_REASON ;WE'RE READY TO GO FIND OUT WHY
- A_INT:
- AND DX,MIDA ;CONVERT PORT NUMBER BACK TO UART A
- INT_REASON:
- AND AX,VECMASK ;WHACK OUT ONLY THE BITS THAT MATTER
- MOV BX,AX ;PUT IT INTO AN INDEX REGESTER
- JMP INT_FTAB[BX] ;JUMP TO THE APPROPRIATE ROUTINE
- ;
- INT_FTAB DW INT_TXMIT ;TRANSMITTER HOLDING REGESTER EMPTY
- DW INT_ERROR ;MODEM LINE INTERUPTS NOT IMPLIMENTED
- DW INT_RECEIVE ;RECEIVER BUFFER HAS A CHARACTER
- DW INT_ERROR ;RECEIVER FRAMING, OVER-RUN ERROR
-
- INT_ERROR: ;ENTRYPOINT FOR SETTING BREAKPOINTS
- notify E
- NOP ;FOR ILLEGAL VECTOR VALUES
- INT_EXIT:
- MOV DX,PORT[SI] ;GET THE PORT NUMBER AGAIN
- ADD DX,MIDCON ;CHANGE TO CONTROL PORT
- AND DX,MIDA ;MAKE SURE IT'S CHANNEL A OF DART
- MOV AL,ZREINT ;SEND THE SIMULATED Z80 RETURN
- OUT DX,AL ;FROM INTERUPT COMMAND TO DART
- ;INTERUPT STUF WAS HERE
- POP DS ;RECOVER ALL THE REGESTERS
- POP DI
- POP SI
- POP DX
- POP CX
- POP BX
- POP AX
- IRET
- ;
- ; THE FOLLOWING INTERUPT SERVICE ROUTINES ALL HAVE THE
- ; SAME CONTEXT:
- ; -CS AND DS POINT TO THE DRIVER SEGMENT.
- ; -DS:[SI] POINTS TO THE UNIT STRUCTURE FOR THE ASYNC LINE THAT
- ; FIRED THE INTERUPT.
- ; -DX POINTS TO THE CONTROL REGESTER OF THE DART
- ; -AX BX CX 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
- PAGE
- INT_RECEIVE: ;THE PORT HAS RECEIVED A NEW CHARACTER, I MUST COPY IT
- ;INTO THE INPUT TYPEAHEAD BUFFER.
- notify R
- SUB DX,MIDCON ;POINT AT THE DATA REGESTER
- IN AL,DX ;GET THE CHARACTER
- CALL PUT_IN ;PUT THE CHARACTER IN THE RING BUFFER
- CMP AH,0 ;WAS THERE ROOM?
- JE REC_OK ;NO, SET FLAGS BEFORE RETURNING
- notify O
- OR ERVAL[SI],ERIBFO ;NO, SET THE OVERFLOW BIT
- CMP EROFF[SI],-1 ;IF YOU HAVN'T INDICATED ANY ERRORS YET
- JNE REC_OK
- PUSH AX
- MOV AX,IAVAIL[SI] ;THEN INDICATE THE POSITION THAT THE
- MOV EROFF[SI],AX ;ERROR HAPPENED
- POP AX
- REC_OK:
- TEST STATUS[SI],OUTHRU ;POOR MANS MIDI MERGE TURNED ON?
- JZ NEXT_REC ;NOPE, DON'T ECHI THIS CHAR
- CALL PUT_OUT ;YUP, SEND THE CHARACTER TO OUTPUT
- CALL START_OUTPUT ;AND START THE TRANSMIT IF NECC.
- NEXT_REC: ;CHECK TO SEE IF THERE ARE MORE CHARS READY
- ADD DX,MIDCON ;POINT BACK AT THE COMMAND REGESTER
- IN AL,DX ;READ REGESTER 0
- TEST AL,RXCHAR ;IS THERE ANOTHER CHARACTER READY?
- JNZ INT_RECEIVE ;YES, GO READ IT IN ALSO
- MOV AL,ZRXINT ;NO, ENABLE INTERUPTS ON NEXT CHAR
- OUT DX,AL ;THAT DOES COME IN.
- JMP INT_EXIT
- ;
- ;
- SUBTTL SPECIAL RECEIVE CONDITION INTERUPT
- PAGE
- ;
- ; PARITY, FRAMING, OR OVERRUN ERROR INTERUPT
- INT_RXSTAT:
- notify S
- MOV AL,1 ;REQUEST READ REGESTER 1
- OUT DX,AL
- JMP SHORT $+2
- IN AL,DX ;READ IN THE VALUE
- AND AX,070H ;CHOP OUT THE ERROR BITS
- OR ERVAL[DI],AX ;ADD THOSE BITS TO THE ERROR WORD
- CMP EROFF[SI],-1 ;CHECK TO SEE IF YOU'VE ALREADY
- JNE RXSTAT_DONE ;TRAPPED ONE UNREPORTED ERROR
- MOV AX,IAVAIL[SI] ;IF NOT, THEN POINT THE
- MOV EROFF[SI],AX ;ERROR OFFSET AT NEXT AVAIL BYTE
- RXSTAT_DONE:
- MOV AL,ZERRES ;RESET THE ERROR BITS IN THE
- OUT DX,AL ;UART.
- 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.
- notify T
- AND STATUS[SI],NOT OUTINT ;CLEAR INTERUPT EXPECTED BIT.
- CALL START_OUTPUT ;START THE NEXT CHARACTER
- MOV AL,ZTXINT ;RESET THE UART TRANSMITTER
- OUT DX,AL ;PENDING BIT
- JMP INT_EXIT
-
- ;ROUTINE TO START THE NEXT CHARACTER PRINTING ON THE PORT, 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.
- ; SI MUST POINT AT THE UNIT STRUCTURE.
- START_OUTPUT PROC NEAR
- ;PUSHF ;SAVE THE FLAGS SO I CAN
- CLI ;DISABLE INTERUPTS
- TEST STATUS[SI],OUTINT ;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 STATUS[SI],OUTINT ;WARN EVERYBODY THAT I'M BUSY.
- DONT_START:
- ;POPF
- STI
- RET
- START_OUTPUT ENDP
- ;;
- SUBTTL READ CLOCK CURRENT VALUE
- PAGE
- ;
- ; THE GET_CLOCK ROUTINE RETURNS THE CURRENT CLOCK VALUE
- ; IN AX. REGESTER DX IS PRESERVED.
- ; This routine returns the "unsullied" clock value, so beware! The
- ; timer chip is a count DOWN timer, and you should calculate delta
- ; values accordingly. The C routines that read this clock invert
- ; the current value to make it look like an increasing time count,
- ; but down here in the driver I left the values raw.
- ;
- GET_CLOCK PROC NEAR
- PUSH DX
- MOV DX,CLOCK_PORT ;GET PORT OF CLOCK CHIP
- MOV AL,T2 OR LATCH ;TELL HIM YOU WANT TO LATCH TIMER 2
- OUT DX,AL
- JMP SHORT $+2
- ADD DX,TIMER2 ;POINT DX AT TIMER2
- IN AL,DX ;READ TIMER2 LSB
- JMP SHORT $+2
- MOV AH,AL
- IN AL,DX ;READ TIMER2 MSB
- XCHG AH,AL ;SWAP THE BYTES TO NORMAL
- POP DX
- RET
- GET_CLOCK ENDP
- ;
- ;
- ; THE FOLLOWING LABEL DEFINES THE END OF THE DRIVER, SO I
- ; CAN TELL DOS HOW BIG I AM.
- MIDI_END:
- SUBTTL MIDI INITIALIZATION REQUEST ROUTINE
- PAGE
- ;
- ; THE INITIALIZE DRIVER ROUTINES ARE 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 MIDI UNITS THAT THIS DRIVER
- ; CONTAINS. (APPARENTLY, MSDOS DOESN'T WRITE ANYTHING ON TOP OF
- ; THIS CODE UNTIL ALL UNITS ARE INITIALIZED.
- ; THE CONTEXT OF THE INITIALIZE CODE BELOW IS THE SAME AS
- ; ALL THE OTHER REQUEST ROUTINES EARLIER IN THE DRIVER.
- ;
- INIT_TAB DB ZCHANR ;TABLE OF INITIALIZATION BYTES
- DB 1,W1TXINT OR W1RX1ST OR W1STAT ;ENABLEL XMITR INTS
- DB 3,W3RXEN OR W3RX8 ;ENABLE RECEIVING, 8 BITS/CHARACTER
- DB 4,W4STOP1 OR W4X64 ;1 STOP BIT, X64 CLOCK MODE
- DB 5,W5TXEN OR W5TX8 ;8 BITS/CHARACTER ON INPUT
- DB ZRXINT ;ALLOW NEXT CHAR INPUT TO INTERUPT
- INIT_SIZE EQU $-INIT_TAB ;COUNT THE BYTES FOR ME
- ;
- ; TABLE OF REGESTER OFFSETS AND VALUES FOR INITIALIZING THE CLOCK
- CLK_TAB DB 0,T1 OR LMSB OR RATE ;TIMER ONE IS A RATE GENERATOR
- DB TIMER1,0,0,8 ;THAT DIVIDES CLOCK BY 2048 (800H)
- DB -TIMER1,T2 OR LMSB OR RATE ;TIMER TWO IS ALSO A RATE GENERATOR
- DB TIMER2,0FFH,0,0FFH ;THAT DIVIDES BY THE LARGEST INT.
- DB -TIMER2,T3 OR LMSB OR RATE ;TIMER 3 JUST COUNTS DOWN
- DB TIMER3,0FFH,0,0FFH ;FROM LARGEST INTEGER (64K)
- CLK_SIZE = $-CLK_TAB ;SIZE OF THIS TABLE
- ;
- ; INITIALIZE THE DRIVER AND DEVICE
- ;
- MIDI_INIT:
- MOV AX,OFFSET MIDI_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 DX,PORT[SI] ;GET THE PORT ADDRESS OF THIS LINE
- ADD DX,MIDCON ;SLIDE UP TO THE COMMAND REGESTER
- ; THE FOLLOWING CODE INITIALIZES THE Z8470 DART
- ; FOR INTERUPT DRIVEN MIDI OPERATION
- MOV BX,OFFSET INIT_TAB ;GET ADDRESS OF TABLE
- MOV CX,INIT_SIZE ;SIZE OF TABLE
- INIT_PORT:
- MOV AL,[BX] ;GET NEXT PORT CONTROL BYTE
- OUT DX,AL ;SEND IT TO CONTROL REGESTER
- JMP SHORT $+2
- INC BX ;INCRIMENT TO NEXT BYTE
- LOOP INIT_PORT ;LOOP UNTIL DONE
-
- TEST DX,MIDB ;IF THIS IS UART B OF THE DART,
- JNE DONE_INIT ;THEN YOU ARE DONE
- ;ONLY ONCE PER DART IS IT NESESSARY TO INITIALIZE
- ;THE VECTOR IN REGESTER 2
- OR DX,MIDB ;SLIDE UP TO UART B
- MOV AL,2 ;AND THEN INITIALIZE THE
- OUT DX,AL ;WRITE REGESTER 2 WITH
- JMP SHORT $+2
- MOV AL,0 ;A ZERO VECTOR NUMBER
- OUT DX,AL
- ;IT'S ONLY NECESSARY TO INITIALIZE THE INTERUPT
- ;VECTOR AND MASK ONCE, BUT I ALLOW THE FOLLOWING
- ;CODE TO EXECUTE ONCE FOR EVERY DART CHIP ON YOUR
- ;BOARD. IT CAN'T HURT.
- MOV AX,0 ;POINT ES AT THE VECTOR SEGMENT
- MOV ES,AX ;SO CAN INITIALIZE THE VECTORS
- MOV AX,OFFSET MIDI_INT ;GET ADRS OF INTERUPT SERVICE ROUTINE
- MOV DI,MIDI_VEC ;GET ADRS OF VECTOR
- STOS WORD PTR [DI] ;STORE THE OFFSET THERE, THEN
- MOV ES:[DI],CS ;THE SEGMENT IN THE FOLLOWING WORD.
- MOV CX,DI ;CALCULATE 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
- JMP SHORT $+2
- AND AL,AH ;AND MY BIT OUT OF IT,
- OUT 021H,AL ;WRITE IT BACK OUT AGAIN.
- ;
- ; THIS CODE INITIALIZES THE ON-BOARD CLOCK CHIP TO USEFUL
- ; MODES AND RATES FOR MIDI USE. TIMER1 IS USED TO DIVIDE THE CLOCK
- ; PULSE DOWN BY 2048 TO A 1024 TICKS PER SECOND (ABOUT 1 MILISECOND)
- ; RATE. THAT RATE FEEDS TIMER2 WHICH IS THE FIRST CLOCK VALUE READ.
- ; FOR LONG DELAY PERIODS, TIMER2 IS ASSUMED TO FEED TIMER3, SO IT
- ; WILL CONTAIN THE OVERFLOW, ALLOWING COUNTS UP TO 137 YEARS!
- ;
- MOV DX,CLOCK_PORT ;GET CLOCK CONTROL REGESTER PORT
- MOV BX,OFFSET CLK_TAB ;GET ADDRS OF TABLE OF COMMANDS
- MOV CX,CLK_SIZE/2 ;SEND THIS MANY COMMANDS TO CLOCK
- ; SAR CX,1
- CLK_LUP:
- MOV AL,[BX] ;GET NEXT REGESTER OFFSET
- CBW
- ADD DX,AX ;AND OFFSET FROM IT
- INC BX ;SLIDE UP TO NEXT VALUE
- MOV AL,[BX] ;GET IT, AND
- INC BX ;DON'T FORGET TO SKIP PAST
- OUT DX,AL ;SEND VALUE TO CLOCK REGESTER
- LOOP CLK_LUP
- ; THE FOLLOWING LOOP IS SUGESTED BY INTEL IN THE
- ; CLOCK CHIP SPEC.'S TO MAKE SURE THE CLOCK HAS
- ; SETTLED BEFORE YOU FIRST READ IT. I ONLY WATCH
- ; TIMER2 SETTLE, AND ASSUME THE REST ARE LONG GONE
- MOV DX,CLOCK_PORT ;MAKE SURE YOU'RE LOOKING AT CONTROL
- CLK_START:
- MOV AL,T2 OR LATCH ;LATCH TIMER TWO
- OUT DX,AL
- JMP SHORT $+2
- ADD DX,TIMER2 ;SLIDE UP TO IT'S DATA REGESTER
- IN AL,DX ;READ AND
- JMP SHORT $+2
- MOV AH,AL ;SAVE LEASTMOST BYTE
- IN AL,DX ;READ MOST SIGNIFICANT BYTE
- SUB DX,TIMER2 ;SLIDE BACK TO CONTROL REGESTER
- XCHG AL,AH ;MAKE INTO A BINARY NUMBER
- NOT AX ;REVERSE INTO A UP-COUNTING NUMBER
- OR AX,AX ;AND CHECK FOR ZERO
- JNZ CLK_START ;WAIT FOR IT TO SETTLE DOWN
- DONE_INIT:
- MOV AX,STSDNE ;RETURN NO ERRORS.
- JMP EXIT
-
- DRIVER ENDS
- END
-