home *** CD-ROM | disk | FTP | other *** search
- PAGE 50,132
- TITLE MIDI Interface Device Driver
- %OUT MIDI Interface Device Driver
- ;***************************************************************
- ;* MPU401 *
- ;* *
- ;* This program is loaded as an I/O driver during MSDOS initi- *
- ;* alization. It contains a driver for a MIDI (Music Instru- *
- ;* ment Digital Interface) interface, the MPU401 from Roland *
- ;* Corp. This software has been tsted using an MPU401 with *
- ;* software revision level 1.5A. *
- ;* *
- ;* This driver acknowledges only three function requests from *
- ;* DOS, namely INIT, CHIN and CHOUT. INIT is for usage by DOS *
- ;* during system start-up. CHOUT receives control data setting *
- ;* the software interrupt number. The CHIN request passes an *
- ;* infinite stream of characters denoting the current software *
- ;* interrupt number. Consequently, an application program can *
- ;* set and read what software interrupt is used. *
- ;* *
- ;* Software interrupts lower than 35H are not allowed, because *
- ;* they may conflict with values used by MSDOS. Higher values *
- ;* may also conflict with DOS, but for these it is more unli- *
- ;* kely. Eventually you will need to find an own soft inter- *
- ;* rupt number. The default (68H) works nicely on the system *
- ;* where the driver was developed. You can set the software *
- ;* interrupt level by editing your CONFIG.SYS file. *
- ;* *
- ;* All access to the driver from a user program (except set- *
- ;* ting/reading the software interrupt number) is done using *
- ;* software interrupts, since then you don't have to pass *
- ;* through DOS's dispatcher etc. Software interrupt is much *
- ;* faster. *
- ;* *
- ;* When the application program issues a software interrupt, *
- ;* register AX should contain the desired 'function code', *
- ;* similar to a DOS INT21 call. Note that AX, not AH is used. *
- ;* The following 'function codes' are defined: *
- ;* *
- ;* 0 SINIT Driver initialize *
- ;* 1 SFINI Driver finitialize *
- ;* 2 SSTAT Get driver status *
- ;* 3 SRERR Reset error flags *
- ;* 4 SSTIM Set timer *
- ;* 5 SRTIM Read timer *
- ;* 6 SFBUF Flush input data buffer *
- ;* 7 SRBUF Read input data buffer *
- ;* 8 STBUF Test input data buffer *
- ;* 9 SDXBF Define exclusive message buffer *
- ;* 10 SFXBF Flush exclusive message buffer *
- ;* 11 SRXBF Read exclusive message buffer *
- ;* 12 STXBF Test exclusive message buffer *
- ;* 13 SOCMD Send MPU command *
- ;* 14 SODAT Send MPU data *
- ;* *
- ;* Some software interrupts return with a completion code in *
- ;* register DX and/or a return value in AX. (DX-AX are the *
- ;* registers for a 'long' return value used by most 'C' com- *
- ;* pilers - MicroSoft, Wizard, DeSmet, and (yech!) Digital *
- ;* Research. Lattice uses BX-AX). *
- ;* *
- ;* Data coming from the MPU401 is by default buffered in an *
- ;* internal buffer ('DBUF') which has a default size of 1 kb. *
- ;* At driver initialization, another size may be selected for *
- ;* this buffer. An application program may also allocate it's *
- ;* own data buffer, which will then be used by the driver in- *
- ;* stead of the internal one. MIDI exclusive messages are not *
- ;* buffered by default - they are just ignored. An applica- *
- ;* tion may set up an own buffer for these. *
- ;* *
- ;* The driver responds to certain parameters in the invoca- *
- ;* tion line in CONFIG.SYS: *
- ;* *
- ;* DEVICE=MPU401.DEV /B:nnnn /H:n /P:nnnn /S:nn *
- ;* *
- ;* /B:nnnn Set Internal Buffer Size to nnnn (200H <= n *
- ;* <= 8000H) *
- ;* /H:n Set Hardware Interrupt to n (0-0FH for an AT, *
- ;* 0-7 for an XT). *
- ;* /P:nnnn Set MPU401 Port Address to nnnn (0-3FFH). *
- ;* /S:nn Set Software Interrupt number to nn (35H-0FFH). *
- ;* (May also be changed by application.) *
- ;* *
- ;* (n, nn, and nnnn are given in hexadecimal notation.) *
- ;***************************************************************
- ;* *
- ;* Original Author B. Larsson - All wrongs reserved. *
- ;* *
- ;* (Many ideas taken from the CMU MIDI Toolkit sources - *
- ;* notably aintr.asm, mpu.c and cintr.c. Regards to the *
- ;* authors of all those). *
- ;* *
- ;***************************************************************
- ;* I herewith place this software is in the PUBLIC DOMAIN. You *
- ;* are free to make any modifications to it that you want. THE *
- ;* ONLY RESTRICTION is that you PLEASE leave the credit notes *
- ;* of all previous authors in the source. Please also maintain *
- ;* a proper change log, so that followers see what has been *
- ;* done, and WHY! Thank You! *
- ;***************************************************************
- ;* *
- ;* 1.0: Started: 870105 *
- ;* Functional and debugged except exclusive *
- ;* soft ints: 870112 *
- ;* 1.1: Exclusive buffer implemented: 870117 *
- ;* 1.2: Does 5000 dummy byte reads from MPU401 at *
- ;* initialization before it gives up (did just *
- ;* 200): 870118 *
- ;* 1.30: Cute little sign-on string: 871109 *
- ;***************************************************************
- ;
- CODSEG SEGMENT PUBLIC PARA 'CODE'
- ASSUME CS:CODSEG,ES:NOTHING,DS:NOTHING
- ;
- SUBTTL Equates
- %OUT Equates
- PAGE
- ;
- ;***************************************************************
- ;* Equates *
- ;***************************************************************
- ;
- ;* ASCII Equates.
- ;
- ASCICR EQU 0DH ;CARRIAGE RETURN
- ASCILF EQU 0AH ;LINE FEED
- UCMASK EQU 5FH ;MASKS LOWER CASE TO UPPER
- ;
- ;* System related equates.
- ;
- SYSTEM EQU 21H ;SYSTEM CALL
- PRTSTR EQU 9 ;PRINT STRING REQUEST
- JFAROPC EQU 0EAH ;JUMP FAR OP-CODE
- ;
- ;* Device driver equates.
- ;
- DEVATR EQU 1000000000000000B ;DEVICE ATTRIBUTES
- INITRQ EQU 0 ;INIT FUNCTION REQUEST
- CHINRQ EQU 4 ;CHIN FUNCTION REQUEST
- CHOUTRQ EQU 8 ;CHOUT FUNCTION REQUEST
- ;
- RDYSTT EQU 0000000100000000B ;READY STATUS RETURN CODE
- WRPSTT EQU 1000000000000000B ;WRITE PROTECT RETURN CODE
- ILLSTT EQU 1000000000000011B ;BAD REQ STATUS RETURN CODE
- ;
- ;* I/O Request header offsets.
- ;
- CMDOFS EQU 2 ;REQUEST CODE REQ HDR OFS
- STTOFS EQU 3 ;RETURN STATUS REQ HDR OFS
- UNCOFS EQU 13 ;RETURN UNITCODE REQ HDR OFS
- EOFOFS EQU 14 ;END ADDR OFFSET REQ HDR OFS
- ESGOFS EQU 16 ;END ADDR SEG REQ HDR OFS
- PDOOFS EQU 14 ;PARM WRD OFS REQ HDR OFS
- PDSOFS EQU 16 ;PARM WRD SEG REQ HDR OFS
- BCTOFS EQU 18 ;BYTE COUNT REQ HDR OFS
- PMOOFS EQU 18 ;ARGUMENT OFFS REQ HDR OFS
- PMSOFS EQU 20 ;ARGUMENT SEG REQ HDR OFS
- ;
- ;* Default values.
- ;
- DDATPORT EQU 330H ;DEFAULT DATA PORT LOCATION
- DCTLPORT EQU 331H ;DEFAULT CTRL PORT LOCATION
- PORTMAX EQU 7FEH ;MAX PORT LOCATION
- DHRDINT EQU 2 ;DEFAULT HARD INT TO USE
- DSFTINT EQU 68H ;DEFAULT SOFT INT TO USE
- MINSFTI EQU 35H ;MINIMUM SOFT INT TO USE
- MAXSFTI EQU 0FFH ;MAXIMUM SOFT INT TO USE
- ;
- ;* MIDI port values.
- ;
- DSRBIT EQU 80H ;DSR BIT MASK
- DRRBIT EQU 40H ;DRR BIT MASK
- ;
- ;* Buffer size values.
- ;
- MINBSIZ EQU 512 ;MIN BUFFER SIZE
- DIBSIZ EQU 1024 ;DEFAULT 1K INTERNAL BUFFER
- MAXBSIZ EQU 32768 ;MAX BUFFER SIZE
- ;
- ;* Software interrupt completion codes.
- ;
- CPLOK EQU 0 ;OK COMPLETION
- CPLIFNC EQU 1 ;ILLEGAL FUNCTION CODE
- CPLPASS EQU 2 ;DRIVER INACTIVE
- CPLMOFL EQU 3 ;MIDI PROVIDES TOO MUCH DATA
- CPLPRTE EQU 4 ;MIDI PROTOCOL ERROR
- CPLBERR EQU 5 ;INVALID BUFFER SPECIFICATION
- CPLTERR EQU 6 ;MIDI TIMEOUT ERROR
- CPLNOXB EQU 7 ;EXCLUSIVE BUFFER NOT DEFINED
- ;
- ;* MPU401 and MIDI command and status codes.
- ;
- MPURES EQU 0FFH ;MPU401 RESET COMMAND
- MPUACK EQU 0FEH ;MPU401 'ACK' BYTE
- MPUOFLO EQU 0F8H ;INDICATES MPU TIMER OVERFLOW
- MPUNOOP EQU 0F8H ;NOOP MPU MARK CODE
- MPUGRCT EQU 0ABH ;GET MPU RECORD COUNTER CMD
- FAKEDAT EQU 0F8H ;FAKE DATA USED AT TIMEOUT
- MPUMAXT EQU 0EFH ;MAX TIME BYTE FROM MPU401
- MPUSBIT EQU 80H ;MASK FOR RUNNING STATUS BIT
- MPUSMSK EQU 0F0H ;MASK FOR RUN STATUS NIB
- MPUSMAX EQU 0EFH ;MAX RUN STATUS VALUE
- ATCHMSK EQU 0E0H ;MASKS Cx AND Dx TO C0
- ATOUCH EQU 0C0H ;AFTERTOUCH STATUS BYTE
- MPUSYSM EQU 0FFH ;STATUS BYTE FOR SYSTEM MSG
- MPUMIDX EQU 0F0H ;YTE FOR SYSTEM EXCLUSIVE MSG
- ;
- ;* Miscellaneous.
- ;
- EOICMD EQU 20H ;EOI COMMAND FOR 8259 CHIP
- OFLTIM EQU 240 ;MPU TIMEOUT VALUE
- ;
- SUBTTL Static Data Area
- %OUT Static Data Area
- PAGE
- ;
- ;***************************************************************
- ;* Static storage *
- ;***************************************************************
- ;
- DVNXT: DW 0FFFFH ;PTR TO NEXT DRIVER HEADER
- DW 0FFFFH
- DVATR: DW DEVATR ;DEVICE'S ATTRIBUTES
- DVSTRA: DW OFFSET STRAT ;ADDRESS OF STRATEGY ENTRY
- DVEXEC: DW OFFSET EXEC ;ADDRESS OF EXEC ('INT') ENTRY
- DVNAME: DB 'MPU401$$' ;DEVICE NAME
- ;
- ;* Buffer related values.
- ;
- IBSIZ: DW 0 ;SIZE OF BUILT-IN BUFFER
- BFADR: DD 0 ;BUFFER ADDRESS
- BFSIZ: DW 0 ;SIZE OF CURRENT BUFFER
- BFPUT: DW 0 ;WRITE INDEX INTO BUFFER
- BFPIC: DW 0 ;READ INDEX INTO BUFFER
- NENT: DW 0 ;BYTES IN BUFFER
- XBFADR: DD 0 ;EXCL BUFFER ADDRESS
- XBFSIZ: DW 0 ;SIZE OF EXCLUSIVE BUFFER
- XBFPUT: DW 0 ;WRITE INDEX INTO EXCL BUFFER
- XBFPIC: DW 0 ;READ INDEX INTO EXCL BUFFER
- XNENT: DW 0 ;BYTES IN EXCL BUFER
- ;
- ;* Port addresses etc.
- ;
- DATPORT: DW 0 ;MIDI DATA PORT ADDRESS
- CTLPORT: DW 0 ;MIDI CONTROL PORT ADDRESS
- EOIPORT: DW 0 ;PORT ADDRESS FOR EOI
- ENAPORT: DW 0 ;PORT ADDRESS FOR EN/DISABLE
- ;
- ;* Temporary storage locations.
- ;
- RHPTR: DD 0 ;DWORD POINTER TO REQ HDR
- TMPSP: DW 0 ;INTERRUPTED PROGRAM'S SP
- TMPSS: DW 0 ;INTERRUPTED PROGRAM'S SS
- ;
- ;* Interrupt related values.
- ;
- HRDINT: DB 0 ;HARDWARE INTERRUPT NUMBER
- SFTINT: DB 0 ;SOFTWARE INTERRUPT NUMBER
- OSWVEC: DD 0 ;ORIGINAL SW VECTOR
- HWVECAD: DW 0 ;HW VECTOR ADDRESS IN SEGM 0
- ENAMASK: DB 0 ;INT ENABLE MASK
- DISMASK: DB 0 ;INT DISABLE MASK
- ;
- ;* Miscellaneous flags etc.
- ;
- STATREC: DW 0 ;STATUS RECORD
- ;BIT 15 DRIVER ACTIVE
- ;BIT 14 USED FOR REPORTING
- ; AT/XT STATUS. NOT WRITTEN
- ; IN THIS WORD.
- ;BIT 10 MPU CMD ACK'ED
- ;BIT 9 DATA IN EXCL BUFFER
- ;BIT 8 DATA IN BUFFER
- ;BIT 3 INPUT PROTOCOL ERROR
- ;BIT 2 TIMEOUT ERROR
- ;BIT 1 EXCL BUFFER OVERFLOW
- ;BIT 0 BUFFER OVERFLOW
- FNCTAB: DW FNCTABI ;CURRENT FUNCTION JUMP TABLE
- MPUDELY: DW 0 ;MAX TIME TO WAIT FOR MPU401
- ATFLAG: DB 0 ;NON-ZERO IF PC-AT
- ORGENA: DB 0 ;ORIGINAL INT ENABLE MASK
- LASTTIM: DB 0 ;TIME OF LAST ERROR
- LASTERR: DB 0 ;BYTE OF LAST ERROR
- MIDISTT: DB 0 ;MIDI RUNNING STATUS BYTE
- ;
- ;* Real-time timer accumulator
- ;
- ACKTIME: DB 0 ;FLAGS MPUACK AWAITS TIME BYTE
- TIMER: DD 0 ;32 BITS TIMER ACKUMULATOR
- ;
- ;* Function jump table for active driver.
- ;
- FNCTABA: DW SINITA ;INITIALIZE
- DW SFINIA ;FINITIALIZE
- DW SSTAT ;GET DRIVER STATUS
- DW SRERR ;RESET ERROR FLAGS
- DW SSTIM ;SET TIMER
- DW SRTIMA ;READ TIMER
- DW SFBUF ;FLUSH INPUT BUFFER
- DW SRBUFA ;READ INPUT BUFFER
- DW STBUFA ;TEST INPUT BUFFER
- DW SDXBFA ;DEFINE EXCLUSIVE BUFFER
- DW SFXBF ;FLUSH EXCLUSIVE BUFFER
- DW SRXBFA ;READ EXCLUSIVE BUFFER
- DW STXBFA ;TEST EXCLUSIVE BUFFER
- DW SOCMDA ;SEND COMMAND
- DW SODATA ;SEND DATA
- ;
- ;* Function jump table for inactive driver.
- ;
- FNCTABI: DW SINITI ;INITIALIZE
- DW SFINII ;FINITIALIZE
- DW SSTAT ;GET DRIVER STATUS
- DW SRERR ;RESET ERROR FLAGS
- DW SSTIM ;SET TIMER
- DW SRTIMI ;READ TIMER
- DW SFBUF ;FLUSH INPUT BUFFER
- DW SRBUFI ;READ INPUT BUFFER
- DW STBUFI ;TEST INPUT BUFFER
- DW SDXBFI ;DEFINE EXCLUSIVE BUFFER
- DW SFXBF ;FLUSH EXCLUSIVE BUFFER
- DW SRXBFI ;READ EXCLUSIVE BUFFER
- DW STXBFI ;TEST EXCLUSIVE BUFFER
- DW SOCMDI ;SEND COMMAND
- DW SODATI ;SEND DATA
- ;
- MAXFNC EQU ($-FNCTABI) / 2 ;NUMBER OF TABLE ENTRIES
- ;
- REVCOD: DB 'MPU401 v.1.30 - 871109, '
- DB 'Author Bjorn Larsson, Stockholm 1987 '
- ;
- STACK: DB 200 DUP ( 0 ) ;OUR OWN STACK
- STKTOP: DB 0 ;TOP OF IT
- ;
- SUBTTL Driver Entry Points
- %OUT Driver Entry Points
- PAGE
- ;
- ;***************************************************************
- ;* Strategy routine. *
- ;* *
- ;* Just stores away the request header pointer in RHPTR. *
- ;***************************************************************
- ;
- STRAT proc far
- ;
- MOV WORD PTR CS:RHPTR,BX ;SAVE OFFSET
- MOV WORD PTR CS:RHPTR+2,ES ;AND PARAGRAPH
- RET
- ;
- STRAT endp
- ;
- ;***************************************************************
- ;* 'Interrupt' routine. *
- ;* *
- ;* Performs the action specified at the call to the strategy *
- ;* routine. *
- ;***************************************************************
- ;
- EXEC proc far
- ;
- PUSH DS ;SAVE MACHINE STATE
- PUSH ES
- PUSH SI
- PUSH DI
- PUSH AX
- PUSH BX
- PUSH CX
- PUSH DX
- ;
- PUSH CS ;MAKE DS = CS
- POP DS
- ;
- MOV BX,WORD PTR RHPTR ;PICK UP POINTER TO REQ HDR
- MOV ES,WORD PTR RHPTR+2
- MOV AL,ES:[BX]+CMDOFS ;GET FUNCTION CODE
- ;
- CMP AL,INITRQ ;INIT?
- JNZ EXEC1
- ;
- CALL INIT ;DO INIT REQUEST
- JMP SHORT READY
- ;
- EXEC1: CMP AL,CHINRQ ;CHIN?
- JZ CHIN
- CMP AL,CHOUTRQ ;CHOUT?
- JZ CHOUT
- ;
- NOP
- NOP
- ;
- ILLREQ: MOV ES:WORD PTR STTOFS[BX],ILLSTT ;SAY ILLEGAL REQUEST
- JMP SHORT EXIT
- ;
- READY: MOV ES:WORD PTR STTOFS[BX],RDYSTT ;SAY OK
- ;
- EXIT: POP DX ;RESTORE MACHINE STATE
- POP CX
- POP BX
- POP AX
- POP DI
- POP SI
- POP ES
- POP DS
- RET
- ;
- ;***************************************************************
- ;* CHIN (4) *
- ;* *
- ;* Always sends back the software interrupt number in the form *
- ;* of one character. *
- ;***************************************************************
- ;
- CHIN: MOV DS,ES:WORD PTR PDSOFS[BX] ;GET SEGM OF DTA
- MOV DI,ES:WORD PTR PDOOFS[BX] ;AND OFFSET
- MOV AL,CS:BYTE PTR SFTINT ;GET SOFT INT NUMBER
- MOV BYTE PTR DS:[DI],AL ;AND RETURN IT AS A CHAR
- JMP READY ;SUCCESSFULL RETURN
- ;
- ;***************************************************************
- ;* CHOUT (8) *
- ;* *
- ;* Installs the software interrupt number given by the charac- *
- ;* ter passed. *
- ;***************************************************************
- ;
- CHOUT: MOV DS,ES:WORD PTR PDSOFS[BX] ;GET SEGM OF DTA
- MOV DI,ES:WORD PTR PDOOFS[BX] ;AND OFFSET
- MOV CL,BYTE PTR DS:[DI] ;GET SOFT INT NUMBER
- ;
- CMP CL,MINSFTI ;CHECK VALID INT NUMBER
- JAE CHOUTOK ;MUST BE GREATER
- ;
- MOV ES:WORD PTR STTOFS[BX],WRPSTT ;SAY WRITE PROTECT
- JMP EXIT ;ERROR RETURN
- ;
- ;* Valid new value. First restore old vector.
- ;
- CHOUTOK: XOR AX,AX ;CLEAR DS
- MOV DS,AX
- MOV AL,CS:BYTE PTR SFTINT ;GET SOFT INT
- ADD AX,AX ;MULT BY 4
- ADD AX,AX
- MOV SI,AX
- MOV AX,CS:WORD PTR OSWVEC ;GET OLD VECTOR'S OFFSET
- MOV WORD PTR DS:[SI],AX ;RESTORE IT
- MOV AX,CS:WORD PTR OSWVEC+2 ;GET SEGMENT
- MOV WORD PTR DS:2[SI],AX ;AND RESTORE
- ;
- ;* Now install the new value.
- ;
- MOV CS:BYTE PTR SFTINT,CL ;INSTALL IT
- XOR CH,CH ;IN CX
- ADD CX,CX ;MULT BY 4
- ADD CX,CX
- MOV SI,CX
- ;
- MOV AX,WORD PTR DS:[SI] ;GET OLD VECTOR'S OFFSET
- MOV CS:WORD PTR OSWVEC,AX ;SAVE IT
- MOV AX,WORD PTR DS:2[SI] ;GET SEGMENT
- MOV CS:WORD PTR OSWVEC+2,AX ;AND SAVE
- ;
- MOV DS:WORD PTR [SI],OFFSET SWIHNL ;INSTALL SOFTWARE INTERRUPT
- MOV DS:WORD PTR 2[SI],CS ;HANDLER ADDRESS
- ;
- JMP READY ;SUCCESSFULL RETURN
- ;
- EXEC endp
- ;
- SUBTTL Soft Interrupt Dispatcher
- %OUT Soft Interrupt Dispatcher
- PAGE
- ;
- ;***************************************************************
- ;* Software Interrupt Handler. *
- ;* *
- ;* You come here when an application program wants to do some- *
- ;* thing with the MPU401, and tries to use the software int. *
- ;* *
- ;* At entry, reg AX contains the Function Code, and any input *
- ;* parameter is passed in BX, CX and DX. *
- ;* *
- ;* The individal functions return any data in AX. *
- ;* Functions that can detect errors return an error code in *
- ;* DX. Functions that do not detecte errors (always success- *
- ;* full) do not set DX. *
- ;* *
- ;* Completion code may be *
- ;* *
- ;* 0: OK, no error. *
- ;* 1: Illegal Function Code. *
- ;* 2: Driver not activated. *
- ;* 3: MIDI data overflow error. *
- ;* 4: MIDI protocol error. *
- ;* 5: Illegal buffer specification. *
- ;* 6: MIDI timeout error. *
- ;* *
- ;* This entry point will destroy the AX and DX register only. *
- ;***************************************************************
- ;
- SWIHNL proc far
- ;
- STI ;RE-ENABLE INTERRUPTS
- CMP AX,MAXFNC ;VALID FUNCTION CODE?
- JB SWIHNL1 ;JUMP IF SO
- ;
- MOV DX,CPLIFNC ;ILLEGAL FUNCTION CODE
- IRET
- ;
- SWIHNL1: PUSH DI
- PUSH DS ;SAVE DS
- PUSH CS ;SET DS=CS
- POP DS
- ADD AX,AX ;SHIFT LEFT FOR WORD INDEX
- MOV DI,WORD PTR FNCTAB ;GET THE JUMP TABLE
- ADD DI,AX
- JMP WORD PTR [DI] ;GO TO HANDLER
- ;
- SUBTTL Soft Interrupt Init
- %OUT Soft Interrupt Init
- PAGE
- ;
- ;***************************************************************
- ;* 0: SINIT: *
- ;* *
- ;* If the driver was currently active, it is set inactive. *
- ;* *
- ;* if BX is non-zero, DX:CX is the address of a non-default *
- ;* data buffer, and BX is the size of it. If BX is 0, the in- *
- ;* ternal buffer is used. Then some hardware initialization is *
- ;* done to the MPU401. Extensive checks are made that the *
- ;* MPU401 is present and responds correctly. If something does *
- ;* not work properly, the initialization is not performed and *
- ;* an error is reported. If all goes well, DX returns CPLOK. *
- ;***************************************************************
- ;
- ;* For active driver (must finitialize first).
- ;
- SINITA: CALL FINITIT ;DO DRIVER FINITIALIZE
- AND DX,DX ;SEE IF OK?
- JZ SINITI ;THEN JUMP
- ;
- POP DS ;RECOVER REGS
- POP DI
- IRET ;RETURN FROM FINITI'S ERROR
- ;
- ;* For inactive driver (normal initialization).
- ;
- SINITI: PUSH CX
- ;
- ;* First initialize most values to defaults.
- ;
- MOV WORD PTR BFADR,OFFSET DBUF ;SET UP TO USE INTERNAL BUFFER
- MOV WORD PTR BFADR+2,CS ;SEGMENT
- MOV AX,WORD PTR IBSIZ ;BUFFER SIZE
- MOV WORD PTR BFSIZ,AX
- XOR AX,AX ;CLEAR CS
- MOV WORD PTR BFPIC,AX ;CLEAR INDICES
- MOV WORD PTR BFPUT,AX
- MOV WORD PTR XBFPIC,AX
- MOV WORD PTR XBFPUT,AX
- MOV WORD PTR XBFSIZ,AX ;NO EXCL BUFFER
- MOV WORD PTR NENT,AX ;NO ENTRIES IN BUFFER YET
- MOV WORD PTR XNENT,AX ;NO EXCL ENTRIES
- MOV WORD PTR TIMER,AX ;CLEAR TIME RECORD
- MOV WORD PTR TIMER+2,AX
- MOV BYTE PTR ACKTIME,AL ;CLEAR TIME EXPECTATION FLAG
- MOV BYTE PTR MIDISTT,90H ;INITIAL MIDI RUNNING STATUS
- ;
- ;* Check if non-default buffer is to be used.
- ;
- AND BX,BX ;CHECK IF NON-DEFAULT BUFFER?
- JZ SINIT4 ;JUMP IF NOT
- ;
- CMP BX,MINBSIZ ;ERROR IF SIZE < MIN
- JB SINIT1
- ;
- CMP BX,MAXBSIZ ;ERROR IF SIZE > MAX
- JBE SINIT2 ;JUMP IF OK
- ;
- SINIT1: MOV DX,CPLBERR ;ERROR CODE
- JMP SINITX ;RETURN
- ;
- SINIT2: CMP CX,10H ;MAKE OFFSET < 1 PARAGRAPH
- JB SINIT3
- ;
- SUB CX,10H ;STEP OFFSET DOWN
- INC DX ;AND SEGMENT UP
- JMP SINIT2 ;LOOP UNTIL DONE
- ;
- SINIT3: MOV WORD PTR BFSIZ,BX ;INSTALL NEW VALUES
- MOV WORD PTR BFADR,CX ;ADDRESS OFFSET
- MOV WORD PTR BFADR+2,DX ;AND SEGMENT
- ;
- ;* Read any data there is in the MPU401 (max 5000 bytes).
- ;
- SINIT4: MOV CX,5000 ;MAX 5000 BYTES
- ;
- SINIT5: MOV DX,WORD PTR CTLPORT ;STATUS PORT
- IN AL,DX ;GET STATUS
- AND AL,DSRBIT ;SEE IF DATA THERE?
- JNZ SINIT7 ;JUMP IF NOT
- ;
- DEC DX ;DATA PORT
- IN AL,DX ;DO A READ
- INC DX ;CTRL PORT
- MOV AL,200
- SINIT6: DEC AL ;SHORT DELAY LOOP
- JNZ SINIT6
- ;
- DEC CX ;DOWN COUNT
- JNZ SINIT5 ;LOOP IF NOT COUNTED OUT
- ;
- MOV DX,CPLMOFL ;TO MUCH DATA ERROR CODE
- JMP SINITX
- ;
- ;* MPU401 buffer empty. Await MPU401 ready for command.
- ;
- SINIT7: MOV CX,500 ;TIMEOUT FOR DRR LOW
- ;
- SINIT8: IN AL,DX ;WAIT FOR DRR LOW
- AND AL,DRRBIT ;CHECK IT
- JZ SINIT9
- ;
- DEC CX ;COUNT DOWN
- JNZ SINIT8
- ;
- ;* Timeout, MPU401 never gets ready for command. Error return.
- ;
- JMP SHORT SINIT12
- ;
- ;* MPU401 ready for command. Reset it to known initial state.
- ;
- SINIT9: MOV AL,MPURES ;MIDI RESET CODE
- OUT DX,AL ;DO IT
- ;
- MOV CX,WORD PTR MPUDELY ;TIMEOUT FOR RESPONSE
- ;
- SINIT10: IN AL,DX ;GET STATUS
- AND AL,DSRBIT ;SEE IF DATA THERE?
- JZ SINIT11 ;THEN JUMP
- ;
- DEC CX ;TIME OUT YET?
- JNZ SINIT10 ;TRY AGAIN IF NOT
- ;
- ;* Timeout, no acknowledge from MPU401. Error return.
- ;
- MOV DX,CPLTERR ;TIMEOUT ERROR
- JMP SINITX
- ;
- ;* MPU401 replied with a byte. Check it's ACK.
- ;
- SINIT11: DEC DX ;DATA PORT
- IN AL,DX
- CMP AL,MPUACK ;SHOULD BE 'ACKNOWLEDGE'
- JZ SINIT13 ;JUMP IF OK
- ;
- SINIT12: MOV DX,CPLPRTE ;PROTOCOL ERROR
- JMP SINITX
- ;
- ;* Coming here, the MPU401 is present, and responds correctly.
- ;* Now initialize some more values.
- ;
- SINIT13: MOV WORD PTR STATREC,8400H ;DRIVER ACTIVE, NO ERROR/DATA
- MOV AX,OFFSET FNCTABA ;ACTIVE DRIVER JUMP TABLE
- MOV WORD PTR FNCTAB,AX ;USE IT IN FUTURE
- ;
- ;* Save original hardware interrupt vector and replace it
- ;* by our own.
- ;
- PUSH DS
- XOR AX,AX ;CLEAR CS
- MOV DS,AX ;FOR ACCESS TO SEGM 0
- MOV DI,CS:WORD PTR HWVECAD ;VECTOR LOCATION
- CLI ;SHUT OFF DOING THIS
- MOV AX,WORD PTR [DI] ;GET ORIGINAL VECTOR OFFS
- MOV CS:WORD PTR OHWVEC,AX ;SAVE IT
- MOV AX,WORD PTR 2[DI] ;GET SEGMENT
- MOV CS:WORD PTR OHWVEC+2,AX ;SAVE IT
- MOV WORD PTR [DI],OFFSET HWIHNL ;INSTALL OUR OFFSET
- MOV WORD PTR 2[DI],CS ;INSTALL OUR SEGMENT
- STI ;ALLOW INT BACK ON
- POP DS ;RECOVER DS (=CS)
- ;
- ;* Give a (probably unnessecary) EOI to interrupt controller.
- ;
- MOV DX,WORD PTR EOIPORT ;PORT TO COMMAND EOI TO
- MOV AL,EOICMD ;CLEAR IN-SERVICE FLIP-FLOP
- OUT DX,AL ;IN INT CONTROLLER
- ;
- ;* Enable hardware interrupts in 8259.
- ;
- MOV DX,WORD PTR ENAPORT ;ENABLE MASK PORT
- IN AL,DX ;GET CURRENT SETTINGS
- MOV BYTE PTR ORGENA,AL ;KEEP ORIGINAL MASK
- AND AL,BYTE PTR ENAMASK ;CLEAR BIT TO ENABLE
- OUT DX,AL ;RESTORE TO CONTROLLER
- ;
- ;* All is well, return OK.
- ;
- MOV DX,CPLOK ;OK RETURN
- ;
- SINITX: POP CX ;RECOVER REGS
- POP DS
- POP DI
- IRET
- ;
- SUBTTL Soft Interrupt Finit
- %OUT Soft Interrupt Finit
- PAGE
- ;
- ;***************************************************************
- ;* 1: SFINI *
- ;* *
- ;* If driver is already inactive, simply returns 'OK' result. *
- ;* Otherwise, things are set back to the state when it was *
- ;* initialized. Extensive tests are made that everything works *
- ;* properly. If some problem is present, the finitialization *
- ;* not performed and an error is reported in DX. Otherwies, DX *
- ;* returns CPLOK. *
- ;***************************************************************
- ;
- ;* Finitialize inactive driver (dummy operation).
- ;
- SFINII: MOV DX,CPLOK ;OK COMPLETION CODE
- JMP SHORT SFINI0 ;AND RETURN
- ;
- ;* Finitialize active driver (normal finitialization).
- ;
- SFINIA: CALL FINITIT ;DO THE JOB
- SFINI0: POP DS ;RECOVER REGS
- POP DI
- IRET
- ;
- ;***************************************************************
- ;* FINITIT *
- ;* This one does the real finit work for FINIT routines. Also *
- ;* called by SINITA. *
- ;***************************************************************
- ;
- FINITIT proc near
- ;
- ;* Reset the MPU401, taking all precautions.
- ;
- MOV DX,WORD PTR ENAPORT ;ENABLE MASK PORT
- IN AL,DX ;GET CURRENT SETTINGS
- OR AL,BYTE PTR DISMASK ;DISABLE INTERRUPTS
- OUT DX,AL ;RESTORE TO CONTROLLER
- ;
- ;* Read any data there is in the MPU401 (max 200 bytes).
- ;
- MOV DX,WORD PTR CTLPORT ;CONTROL PORT ADDRESS
- PUSH CX ;SAVE REG USED AS DOWN-COUNTER
- MOV CX,200 ;MAX 200 BYTE
- FINITI1: IN AL,DX ;GET STATUS
- AND AL,DSRBIT ;SEE IF DATA EMPTY
- JNZ FINITI3 ;THEN JUMP
- ;
- DEC CX ;ADJUST TIMEOUT
- JZ FINITI2 ;ERROR IF TIMEOUT
- ;
- DEC DX ;DATA PORT
- IN AL,DX ;READ A BYTE
- INC DX ;CTRL PORT
- JMP FINITI1 ;LOOP AGAIN
- ;
- FINITI2: POP CX
- MOV DX,CPLMOFL ;MPU DATA OFLO ERROR CODE
- RET
- ;
- ;* MPU401 buffer empty. Await MPU401 ready for command.
- ;
- FINITI3: MOV CX,WORD PTR MPUDELY ;TIMEOUT COUNT
- FINITI4: IN AL,DX ;GET STATUSS
- AND AL,DRRBIT ;MPU READY FOR COMMAND
- JZ FINITI5 ;JUMP IF SO
- ;
- DEC CX ;BUMP TIMEOUT
- JNZ FINITI4 ;LOOP IF NOT
- ;
- JMP SHORT FINITI7 ;TIMEOUT ERROR
- ;
- ;* MPU401 ready for command. Reset it to known final state.
- ;
- FINITI5: MOV AL,MPURES ;RESET COMMAND
- OUT DX,AL
- ;
- MOV CX,WORD PTR MPUDELY ;TIMEOUT DELAY
- FINITI6: IN AL,DX ;GET STATUS
- AND AL,DSRBIT ;SEE IF DATA THERE
- JZ FINITI8 ;THEN JUMP
- ;
- DEC CX ;BUMP TIMEOUT
- JNZ FINITI6 ;LOOP IF NON-0
- ;
- FINITI7: POP CX
- MOV DX,CPLTERR ;TIMEOUT ERROR
- RET
- ;
- ;* MPU401 replied with a byte. Check it's ACK.
- ;
- FINITI8: DEC DX ;DATA PORT
- IN AL,DX ;GET BYTE
- CMP AL,MPUACK ;SHOULD BE ACKNOWLEDGE
- JZ FINITI9 ;JUMP IF OK
- ;
- POP CX ;RECOVE REG
- MOV DX,CPLPRTE ;PROTOCOL ERROR
- RET
- ;
- FINITI9: POP CX ;RECOVER REG
- ;
- ;* MPU401 properly reset. Now restore hardware int enable
- ;* status in 8259.
- ;
- MOV AH,BYTE PTR ORGENA ;GET ORIGINAL SETTING
- AND AH,BYTE PTR DISMASK ;ISOLATE OUR BIT
- MOV DX,WORD PTR ENAPORT ;ENABLE MASK PORT
- IN AL,DX ;GET CURRENT SETTINGS
- AND AL,BYTE PTR ENAMASK ;REMOVE OUR BIT
- OR AL,AH ;INSERT IT
- OUT DX,AL ;RESTORE TO CONTROLLER
- ;
- ;* Restore original hardware interrupt vector to segment 0.
- ;
- PUSH DS
- XOR AX,AX ;ACCESS SEGMENT 0
- MOV DS,AX
- MOV DI,CS:WORD PTR HWVECAD ;VECTOR LOCATION
- MOV AX,CS:WORD PTR OHWVEC ;GET OFFSET
- CLI ;SHUT OFF DOING THIS
- MOV WORD PTR [DI],AX ;RESTORE IT
- MOV AX,CS:WORD PTR OHWVEC+2 ;GET SEGMENT
- MOV WORD PTR 2[DI],AX
- STI
- POP DS
- ;
- ;* Now finitialize some values and return.
- ;
- MOV AX,OFFSET FNCTABI ;INACTIVE DRIVER JUMP TABLE
- MOV WORD PTR FNCTAB,AX ;USE IT IN FUTURE
- MOV WORD PTR STATREC,0 ;DRIVER INACTIVE
- ;
- MOV DX,CPLOK
- RET
- ;
- FINITIT endp
- ;
- SUBTTL Soft Interrupt Driver Status
- %OUT Soft Interrupt Driver Status
- PAGE
- ;
- ;***************************************************************
- ;* 2: SSTAT *
- ;* *
- ;* Get driver status. Returns status in AX as follows: *
- ;* *
- ;* Bit Flags *
- ;* *
- ;* 0 Overflow has occured in the data buffer. *
- ;* 1 Overflow has occured in the exclusive buffer. *
- ;* 2 Timeout error has occured in the MPU401 protocol *
- ;* 3 A protocol error has been found in the input from the *
- ;* MPU401 - unexpected bytes arrived. (Note that this *
- ;* driver currently does not support all possible valid *
- ;* data sequences). *
- ;* 8 Unread data in data buffer. *
- ;* 9 Unread data in exclusive buffer. *
- ;* 10 Last MPU command code has been acknowledged. *
- ;* 14 The machine is not a PC-XT, but an AT. *
- ;* 15 Driver is in the active state. *
- ;* *
- ;* SSTAT does not return any error status. DX returns two *
- ;* bytes of error data (the two last bytes read from the *
- ;* MPU401). DX is only relevant if a protocol error has *
- ;* occured (indicated by bit 3 of AX). *
- ;***************************************************************
- ;
- ;* Include AT bit by special test.
- ;
- SSTAT: MOV AL,BYTE PTR ATFLAG ;SEE IF WE ARE AN AT
- AND AX,0FFH
- JZ SSTAT1
- ;
- MOV AX,4000H ;SET AT BIT
- ;
- SSTAT1: OR AX,WORD PTR STATREC ;ADD OTHER BITS
- POP DS ;RECOVER REGS
- POP DI
- IRET
- ;
- SUBTTL Soft Interrupt Error Reset
- %OUT Soft Interrupt Error Reset
- PAGE
- ;
- ;***************************************************************
- ;* 2: SRERR *
- ;* *
- ;* Reset all error flags. *
- ;* SRERR does not return any error status. DX is not set. *
- ;***************************************************************
- ;
- SRERR: MOV BYTE PTR STATREC,0 ;CLEAR ERROR HALF OF STATREC
- POP DS ;RECOVER REGS
- POP DI
- IRET
- ;
- SUBTTL Soft Interrupt Set Timer
- %OUT Soft Interrupt Set Timer
- PAGE
- ;
- ;***************************************************************
- ;* 4: SSTIM Set timer *
- ;* *
- ;* The internal timer is initialized with the value in DX and *
- ;* CX. DX holds the high order word. *
- ;* SSTIM does not return any error status. DX is not set. *
- ;***************************************************************
- ;
- SSTIM: ADD CX,CX ;SHIFT UP INPUT VALUE 2 BITS
- ADC DX,DX
- ADD CX,CX
- ADC DX,DX
- CLI ;DON'T CHANGE BETWEEN WRITES
- MOV WORD PTR TIMER,CX ;SET LOW WORD
- MOV WORD PTR TIMER+2,DX ;SET HIGH WORD
- STI ;ALLOW CHANGES
- POP DS ;RECOVER REGS
- POP DI
- IRET
- ;
- ;
- SUBTTL Soft Interrupt Read Timer
- %OUT Soft Interrupt Read Timer
- PAGE
- ;
- ;***************************************************************
- ;* 5: SRTIM Read timer *
- ;* *
- ;* The internal timer is read (if neccessary MPU401 time is *
- ;* requested and included), and the value is returned in DX *
- ;* and AX. DX holds the high order word. Note that this func- *
- ;* tion does not return any error status, but DX will hold the *
- ;* high order word of the return value. If an error should *
- ;* occur, the only way to check it is by reading the status *
- ;* record (with SSTAT) and check the error flags. *
- ;***************************************************************
- ;
- ;* Entry for active driver - requst fraction from MPU401
- ;
- SRTIMA: MOV DL,MPUGRCT ;GET MPU RECORD COUNTER
- CALL PUTCMD ;SEND THE COMMAND
- ;
- ;* Entry for inactive driver
- ;
- SRTIMI: MOV DX,WORD PTR MPUDELY ;TIMEOUT DELAY
- ;
- SRTIM1: TEST BYTE PTR STATREC+1,4 ;LAST CMD ACK'ED?
- JNZ SRTIM2
- ;
- DEC DX ;BUMP TIMEOUT
- JNZ SRTIM1 ;LOOP IF NO TIMEOUT
- ;
- OR BYTE PTR STATREC,4 ;SET TIMEOUT ERROR BIT
- ;
- SRTIM2: CLI ;DON'T CHANGE BETWEEN READS
- MOV AX,WORD PTR TIMER ;READ LOW WORD
- MOV DX,WORD PTR TIMER+2 ;READ HIGH WORD
- STI ;ALLOW CHANGES
- RCR DX,1 ;SHIFT RESULT RIGHT 2
- RCR AX,1
- RCR DX,1
- RCR AX,1
- AND DH,3FH ;CLEAR SHIFTED GARBAGE
- POP DS ;RECOVER REGSS
- POP DI
- IRET
- ;
- SUBTTL Soft Interrupt Flush Buffer
- %OUT Soft Interrupt Flush Buffer
- PAGE
- ;
- ;***************************************************************
- ;* 6: SFBUF Flush Buffer *
- ;* *
- ;* Flush input buffer. No error possible, DX not set. *
- ;***************************************************************
- ;
- SFBUF: XOR AX,AX ;CLEAR TO 0
- CLI ;NO INT WHILE WORKING
- MOV WORD PTR BFPUT,AX ;CLEAR BUFFER POINTERS
- MOV WORD PTR BFPIC,AX
- MOV WORD PTR NENT,AX ;CLEAR BYTE COUNT
- AND BYTE PTR STATREC+1,0FEH ;CLEAR DATA FLAG
- STI
- POP DS ;RECOVER REGS
- POP DI
- IRET
- ;
- SUBTTL Soft Interrupt Read Buffer
- %OUT Soft Interrupt Read Buffer
- PAGE
- ;
- ;***************************************************************
- ;* 7: SRBUF Read Buffer *
- ;* *
- ;* If the driver is inactive, nothing is done and an error is *
- ;* returned. If driver is active, waits for a data byte in *
- ;* the buffer and then returns it. DX is CPLOK in this case. *
- ;***************************************************************
- ;
- ;* Read inactive driver is an error
- ;
- SRBUFI: MOV DX,CPLPASS ;ERROR CODE
- XOR AX,AX ;CLEAR RETURN VALUE
- POP DS ;RECOVER REGS
- POP DI
- IRET
- ;
- ;* Read active driver (normal entry)
- ;
- SRBUFA: TEST BYTE PTR STATREC+1,1 ;TEST IF DATA THERE
- JZ SRBUFA
- ;
- MOV DX,WORD PTR BFADR ;GET BUFFER OFFSET
- MOV DI,WORD PTR BFPIC ;GET PICK POINTER
- ADD DX,DI ;GET BUFFER POS
- XCHG DI,DX ;PUT IN INDEX REG
- PUSH DS
- MOV DS,WORD PTR BFADR+2 ;GET BUFFER SEGMENT
- MOV AL,[DI] ;GET BUFFER DATA BYTE
- POP DS
- ;
- INC DX ;NEXT BUF POS
- CMP DX,WORD PTR BFSIZ ;CHECK IF WRAP AROUND
- JB SRBUFA1 ;JUMP IF NOT
- ;
- XOR DX,DX
- ;
- SRBUFA1: MOV WORD PTR BFPIC,DX ;UPDATE POINTER
- CLI ;NO INTS NOW
- DEC WORD PTR NENT ;ADJUST BUFFER CONTENTS
- JG SRBUFA2 ;IF STILL MORE
- ;
- AND BYTE PTR STATREC+1,0FEH ;CLEAR DATA FLAG
- ;
- SRBUFA2: STI ;ALLOW IT NOW
- XOR AH,AH ;CLEAR HIGH RETURN VALUE
- MOV DX,CPLOK ;RETURN CODE
- POP DS ;RECOVER REGS
- POP DI
- IRET
- ;
- SUBTTL Soft Interrupt Test Buffer
- %OUT Soft Interrupt Test Buffer
- PAGE
- ;
- ;***************************************************************
- ;* 8: STBUF Test Buffer *
- ;* *
- ;* If the driver is inactive, nothing is done and an error is *
- ;* returned. If driver is active, returns 0 in AX if there is *
- ;* no data in the buffer waitung to be read. Returns 1 if data *
- ;* is available. DX set to CPLOK in this case. *
- ;***************************************************************
- ;
- ;* Test for inactive driver is an error
- ;
- STBUFI: MOV DX,CPLPASS ;ERROR CODE
- MOV AX,1 ;PRETEND BYTE TO AVOID HANGUP
- POP DS ;RECOVER REGS
- POP DI
- IRET
- ;
- ;* Test for activer driver (normal entry)
- ;
- STBUFA: XOR AX,AX ;ASSUME NO DATA
- TEST BYTE PTR STATREC+1,1 ;SEE IF DATA
- JZ STBUFA1 ;JUMP IF NOT
- ;
- INC AX ;SET IT TO 1
- ;
- STBUFA1: MOV DX,CPLOK ;OK RETURN
- POP DS ;RECOVER REGS
- POP DI
- IRET
- ;
- SUBTTL Soft Interrupt Define Exclusive Buffer
- %OUT Soft Interrupt Define Exclusive Buffer
- PAGE
- ;
- ;***************************************************************
- ;* 9: SDXBF Define exclusive buffer *
- ;* *
- ;* If the driver is inactive, an error is returned. If the *
- ;* driver is active, and if if BX is non-zero, then DX:CX is *
- ;* the address of a non-default exclusive buffer, and BX is *
- ;* the size of it. If BX is 0, the exclusive buffer is un- *
- ;* defined, i.e. exclusive data is unbuffered. This is also *
- ;* the default after the driver is initialized. The buffer *
- ;* size must be >= 512, and <= 32768. *
- ;***************************************************************
- ;
- ;* Refuse if driver inactive
- ;
- SDXBFI: MOV DX,CPLPASS ;ERROR RETURN CODE
- POP DS ;RECOVER REGS
- POP DI
- IRET
- ;
- ;* Entry for active driver (normal case)
- ;
- SDXBFA: OR BX,BX ;CHECK IF UNDEF. BUFFER?
- JZ SDXBF2
- ;
- CMP BX,MINBSIZ ;CHECK IF BIG ENOUGH?
- JB SDXBF1 ;JUMP IF NOT
- ;
- CMP BX,MAXBSIZ ;CHECK IF TOO BIG?
- JBE SDXBF2 ;JUMP IF OK
- ;
- SDXBF1: MOV DX,CPLBERR ;BAD BUFFER CPL CODE
- POP DS
- POP DI
- IRET ;ERROR RETURN
- ;
- SDXBF2: MOV WORD PTR XBFADR,CX ;INSTALL OFFSET
- MOV WORD PTR XBFADR+2,DX ;INSTALL EGMENT
- MOV DX,CPLOK ;OK COMPLETION CODE
- ;
- XOR AX,AX ;FOR CLEARING THINGS
- CLI ;NO INT WHILE SETTING
- MOV WORD PTR XBFSIZ,BX ;INSTALL SIZE
- MOV WORD PTR XBFPUT,AX ;CLEAR BUFFER POINTERS
- MOV WORD PTR XBFPIC,AX
- MOV WORD PTR XNENT,AX ;CLEAR BUFFER CONTENTS
- AND WORD PTR STATREC,0FDFDH ;CLEAR XBUF RELATED BITS
- POP DS
- POP DI
- IRET ;RESTORES INT STATUS
- ;
- SUBTTL Soft Interrupt Flush Exclusive Buffer
- %OUT Soft Interrupt Flush Exclusive Buffer
- PAGE
- ;
- ;***************************************************************
- ;* 10: SFXBF Flush exclusive buffer *
- ;* *
- ;* Flush exclusive buffer. No error possible. *
- ;***************************************************************
- ;
- ;* Clear buffer pointers etc.
- ;
- SFXBF: MOV DX,CPLOK ;RETURN OK CODE
- POP DS ;RECOVER REGS
- POP DI
- ;
- XOR AX,AX ;CLEAR TO 0
- CLI ;NO INT WHILE WORKING
- MOV WORD PTR XBFPUT,AX ;CLEAR BUFFER POINTERS
- MOV WORD PTR XBFPIC,AX
- MOV WORD PTR XNENT,AX ;CLEAR BYTE COUNT
- AND BYTE PTR STATREC+1,0FDH ;CLEAR DATA FLAG
- IRET ;RESTORES INT STATUS
- ;
- SUBTTL Soft Interrupt Read Exclusive Buffer
- %OUT Soft Interrupt Read Exclusive Buffer
- PAGE
- ;
- ;***************************************************************
- ;* 11: SRXBF Read Exclusive Buffer *
- ;* *
- ;* If the driver is inactive, or the driver is active but no *
- ;* exclusive buffer is defined, nothing is done and an error *
- ;* is returned. If driver is active and has a buffer, waits *
- ;* for a data byte and then returns it. DX returns CPLOK in *
- ;* this case. *
- ;***************************************************************
- ;
- ;* Read inactive driver is an error
- ;
- SRXBFI: MOV DX,CPLPASS ;ERROR CODE
- XOR AX,AX ;CLEAR RETURN VALUE
- POP DS ;RECOVER REGS
- POP DI
- IRET
- ;
- ;* Read from active driver (normal entry)
- ;
- SRXBFA: MOV AX,WORD PTR XBFSIZ ;SEE IF THERE IS A BUFFER?
- AND AX,AX
- JNZ SRXBFA1 ;JUMP IF A BUFFER THERE
- ;
- ;* Read non-existent buffer is an error
- ;
- MOV DX,CPLNOXB ;NO EXCLUSIVE BUFFER
- POP DS
- POP DI
- IRET ;ERROR RETURN
- ;
- SRXBFA1: TEST BYTE PTR STATREC+1,2 ;TEST IF DATA THERE
- JZ SRXBFA1
- ;
- MOV DX,WORD PTR XBFADR ;GET BUFFER OFFSET
- MOV DI,WORD PTR XBFPIC ;GET PICK POINTER
- ADD DX,DI ;GET BUFFER POS
- XCHG DI,DX ;PUT IN INDEX REG
- PUSH DS
- MOV DS,WORD PTR XBFADR+2 ;GET BUFFER SEGMENT
- MOV AL,[DI] ;GET BUFFER DATA BYTE
- POP DS
- ;
- INC DX ;NEXT BUF POS
- CMP DX,WORD PTR XBFSIZ ;CHECK IF WRAP AROUND
- JB SRXBFA2 ;JUMP IF NOT
- ;
- XOR DX,DX
- ;
- SRXBFA2: MOV WORD PTR XBFPIC,DX ;UPDATE POINTER
- CLI ;NO INTS NOW
- DEC WORD PTR XNENT ;ADJUST BUFFER CONTENTS
- JG SRXBFA3 ;IF STILL MORE
- ;
- AND BYTE PTR STATREC+1,0FDH ;CLEAR DATA FLAG
- ;
- SRXBFA3: STI ;ALLOW IT NOW
- XOR AH,AH ;CLEAR HIGH RETURN VALUE
- MOV DX,CPLOK ;RETURN CODE
- POP DS ;RECOVER REGS
- POP DI
- IRET
- ;
- SUBTTL Soft Interrupt Test Exclusive Buffer
- %OUT Soft Interrupt Test Exclusive Buffer
- PAGE
- ;
- ;***************************************************************
- ;* 12: STXBF Test Exclusive Buffer *
- ;* *
- ;* If the driver is inactive, nothing is done and an error is *
- ;* returned. If driver is active, returns 0 in AX if there is *
- ;* no data in the buffer waiting to be read. Returns 1 if data *
- ;* is available. DX set to CPLOK in this case. *
- ;***************************************************************
- ;
- ;* Test for inactive driver is an error
- ;
- STXBFI: MOV DX,CPLPASS ;ERROR CODE
- MOV AX,1 ;PRETEND BYTE TO AVOID HANGUP
- POP DS ;RECOVER REGS
- POP DI
- IRET
- ;
- ;* Test for active driver (normal entry)
- ;
- STXBFA: XOR AX,AX ;ASSUME NO DATA
- TEST BYTE PTR STATREC+1,2 ;SEE IF DATA
- JZ STXBFA1 ;JUMP IF NOT
- ;
- INC AX ;SET IT TO 1
- ;
- STXBFA1: MOV DX,CPLOK ;OK RETURN
- POP DS ;RECOVER REGS
- POP DI
- IRET
- ;
- SUBTTL Soft Interrupt Send Command
- %OUT Soft Interrupt Send Command
- PAGE
- ;
- ;***************************************************************
- ;* 13: SOCMD Send a command byte *
- ;* *
- ;* If the driver is inactive, nothing is done and an error is *
- ;* returned. If driver is active, waits until the previous *
- ;* command has finished processing. Then sends the command *
- ;* byte (in DL). Set TIMEACK non-0 if the command requests *
- ;* record counter contents (used by SRTIM). *
- ;***************************************************************
- ;
- ;* Refuse to send if driver inactive.
- ;
- SOCMDI: MOV DX,CPLPASS ;ERROR RETURN CODE
- POP DS ;RECOVER REGS
- POP DI
- IRET
- ;
- ;* Normal entry to send a command byte.
- ;
- SOCMDA: CALL PUTCMD ;SEND OUT THE COMMAND
- POP DS ;RECOVER REGS
- POP DI
- IRET
- ;
- ;***************************************************************
- ;* PUTCMD *
- ;* *
- ;* Writes a command byte from DL to the MPU401. Returns a *
- ;* completion code in DX. DS must be = CS when entering! *
- ;***************************************************************
- ;
- PUTCMD proc near
- ;
- MOV AX,WORD PTR MPUDELY ;TIMEOUT COUNT
- ;
- PUTCMD1: TEST BYTE PTR STATREC+1,4 ;CHECK IF LAST CMD ACK'ED
- JNZ PUTCMD2 ;JUMP IF OK
- ;
- DEC AX ;BUMP TIMEOUT
- JNZ PUTCMD1 ;LOOP IF NO TIMEOUT YET
- ;
- MOV DX,CPLTERR
- RET
- ;
- PUTCMD2: PUSH CX ;USE AS TIMEOUT COUNTER
- MOV AH,DL ;INPUT DATA BYTE
- MOV CX,WORD PTR MPUDELY ;TIMEOUT COUNT
- PUTCMD3: MOV DX,WORD PTR CTLPORT ;GET STATUS
- IN AL,DX
- AND AL,DRRBIT ;CHECK IF READY TO RECEIVE
- JZ PUTCMD4
- ;
- DEC CX ;BUMP TIMEOUT
- JNZ PUTCMD3 ;LOOP IF TIME LEFT
- ;
- POP CX
- MOV DX,CPLTERR ;TIMEOUT ERROR CODE
- RET
- ;
- PUTCMD4: POP CX ;BALANCE STACK
- MOV BYTE PTR ACKTIME,0 ;CLEAR TIME EXPECTATION FLAG
- CMP AH,MPUGRCT ;WILL WE EXPECT A DATA BYTE?
- JNZ PUTCMD5 ;JUMP IF NOT
- ;
- MOV BYTE PTR ACKTIME,0FFH ;ELSE FLAG WE DO
- ;
- PUTCMD5: AND BYTE PTR STATREC+1,0FBH ;CLEAR ACK'ED BIT
- MOV AL,AH ;SEND THE COMMAND
- OUT DX,AL
- ;
- MOV DX,CPLOK ;ALL WELL
- RET
- ;
- PUTCMD endp
- ;
- SUBTTL Soft Interrupt Send Data
- %OUT Soft Interrupt Send Data
- PAGE
- ;
- ;***************************************************************
- ;* 14: SODAT Send a data byte *
- ;* *
- ;* If the driver is inactive, nothing is done and an error is *
- ;* returned. If driver is active, sends the data byte in DL *
- ;* to the MPU401. Returns error if output timeout. *
- ;***************************************************************
- ;
- ;* Refuse to send if driver inactive.
- ;
- SODATI: MOV DX,CPLPASS ;ERROR RETURN CODE
- POP DS ;RECOVER REGS
- POP DI
- IRET
- ;
- ;* Normal entry to send a data byte.
- ;
- SODATA: PUSH CX ;SAVE TIMEOUT REG
- MOV CX,WORD PTR MPUDELY ;TIMEOUT COUNT
- ;
- ;* Don't send data until last cmd has beed acknowledged
- ;
- SODAT1: TEST BYTE PTR STATREC+1,4 ;CHECK IF LAST CMD ACK'ED
- JNZ SODAT2 ;JUMP IF OK
- ;
- DEC CX ;BUMP TIMEOUT
- JNZ SODAT1 ;LOOP IF NO TIMEOUT YET
- ;
- MOV DX,CPLTERR ;TIMEOUT ERROR
- JMP SHORT SODAT5
- ;
- SODAT2: MOV AH,DL ;INPUT DATA
- MOV CX,WORD PTR MPUDELY ;TIMEOUT COUNT
- MOV DX,WORD PTR CTLPORT ;CONTROL PORT
- ;
- SODAT3: IN AL,DX ;CHECK IF READY TO RECEIVE
- AND AL,DRRBIT
- JZ SODAT4 ;JUMP IF SO
- ;
- DEC CX ;BUMP TIMEOUT COUNT
- JNZ SODAT3 ;LOOP IF NO TIMEOUT
- ;
- MOV DX,CPLTERR ;TIMEOUT ERROR
- JMP SHORT SODAT5
- ;
- SODAT4: DEC DX ;DATA PORT
- MOV AL,AH ;SEND DATA
- OUT DX,AL
- MOV DX,CPLOK ;OK COMPLETION
- ;
- SODAT5: POP CX
- POP DS ;RECOVER REGS
- POP DI
- IRET
- ;
- SWIHNL endp
- ;
- SUBTTL Hard Interrupt Handler
- %OUT Hard Interrupt Handler
- PAGE
- ;
- ;***************************************************************
- ;* Hardware Interrupt Handler. *
- ;* *
- ;* You come here when the hardware pulls the interrupt line. *
- ;***************************************************************
- ;
- HWIHNL proc far
- ;
- ;* First check if it was the MPU401 that interrupted.
- ;
- PUSH AX ;SAVE REGS ON CALLER STACK
- PUSH DX
- MOV DX,CS:WORD PTR CTLPORT ;CHECK IF MPU401 INTERRUPT
- IN AL,DX ;BY POLLING DSRBIT
- AND AL,DSRBIT
- JZ OURINT ;JUMP IF KEYBOARD INT
- ;
- ;* Interrupt, but not by the MPU401 - go to original
- ;* interrupt handler (this is abnormal practice on a PC,
- ;* where each interrupt line should be driven by only
- ;* one source, but, as we all know... )
- ;
- POP DX ;RECOVER REGS
- POP AX
- DB JFAROPC ;JMP FAR OP-CODE
- OHWVEC DD 0 ;ORIGINAL HW VECTOR
- ;
- ;* Now set up to use own stack - we don't know how much
- ;* stack the interrupted routine provides.
- ;
- OURINT: MOV CS:WORD PTR TMPSP,SP ;SAVE CALLER'S SP
- MOV CS:WORD PTR TMPSS,SS ;AND SS
- CLI ;DON'T ALLOW INTERRUPT NOW!
- MOV AX,CS
- MOV SS,AX
- MOV SP,OFFSET STKTOP ;OUR OWN STACK
- STI
- PUSH DS ;SAVE REGS
- PUSH CX
- PUSH CS ;SET DS = CS
- POP DS
- ;
- ;* Get the interrupting byte from MPU401 and branch accordingly.
- ;
- HWILOOP: MOV DX,WORD PTR DATPORT ;PORT TO READ
- IN AL,DX ;GET IT
- ;
- CMP AL,MPUOFLO ;TIMER OVERFLOW?
- JNZ NOOFLO
- ;
- ;* Timer overflow, adjust our internal 32-bit timer.
- ;
- ADD WORD PTR TIMER,OFLTIM ;ADD OVERFLOW VALUE
- ADC WORD PTR TIMER+2,0 ;32 BIT ADD
- JMP GOTMSG ;DONE
- ;
- ;* Not a timer overflow.
- ;
- NOOFLO: CMP AL,MPUACK ;MPU ACKNOWLEDGE?
- JNZ NOACK
- ;
- ;* MPU command acknowledge. Flag it in STATREC, and read
- ;* any extra accompanying time byte.
- ;
- OR BYTE PTR STATREC+1,4 ;SET ACK BIT IN STATUS RECORD
- AND AL,BYTE PTR ACKTIME ;IS DATA ASSOCIATED WITH IT?
- JNZ MPUTIM ;JUMP DISTANCE TOO FAR
- JMP GOTMSG
- ;
- MPUTIM: CALL GETDATA ;GET THE DATA
- ADD WORD PTR TIMER,AX ;ADD TO THE TIMER ACKUMULATOR
- ADC WORD PTR TIMER+2,0 ;32 BIT ADD
- MOV BYTE PTR ACKTIME,0 ;CLEAR THE FLAG
- JMP GOTMSG ;DONE
- ;
- ;* Not an MPU401 command acknowledgement.
- ;
- NOACK: CMP AL,MPUMAXT ;IS IT A TIME BYTE?
- JBE TIMEBYT ;JUMP IF NOT
- ;
- ;* Not a leading time byte.
- ;
- CMP AL,MPUSYSM ;SYSTEM MESSAGE?
- JNZ HWIERR0 ;TREAT ALL OTHERS AS ERROR
- ;
- ;* MPU401 sent a system message
- ;
- CALL GETDATA ;SEE WHAT SYSTEM MESSAGE
- CMP AL,MPUMIDX ;SEE IF MIDI EXCLUSIVE?
- HWIERR0: JNZ HWIERR ;TREAT AS ERROR IF NOT
- ;
- ;* MPU401 reported a system exclusive message. Read it (and
- ;* store it if we have a buffer).
- ;
- PUSH DI ;SAVE REGS
- PUSH BX
- MOV DX,WORD PTR XBFSIZ ;BUFFER SIZE
- MOV BX,WORD PTR XBFPUT ;PUTTER INDEX
- MOV DI,WORD PTR XBFADR ;BUFFER ADDRESS OFFSET
- PUSH DS
- MOV DS,CS:WORD PTR XBFADR+2 ;BUFFER ADDRESS SEGMENT
- ;
- PUTXMSG: OR DX,DX ;SEE IF WE HAVE A BUFFER?
- JZ PUTXMS2 ;JUMP IF NOT
- ;
- MOV CH,AL ;FOR BUFFER PUTTING
- CALL PUTBUF ;PUT IT IN BUFFER
- OR CS:BYTE PTR STATREC+1,2 ;SET DATA FLAG IN STATREC
- ;
- PUTXMS1: MOV CS:WORD PTR XBFPUT,BX ;UPDATE BUFFER INDEX
- INC CS:WORD PTR XNENT ;BUMP BYTES IN XBUF
- CMP DX,CS:WORD PTR XNENT ;SEE IF BUFFER OVERFLOW?
- JA PUTXMS2 ;JUMP IF NOT
- ;
- DEC CS:WORD PTR XNENT ;CANNOT INCREASE
- OR CS:BYTE PTR STATREC,2 ;SET BUFFER OVERFLOW BIT
- ;
- PUTXMS2: TEST AL,MPUSBIT ;SEE IF IT WAS A STATUS BYTE
- JZ PUTXMS3 ;THEN LOOP ON
- ;
- CMP AL,MPUMIDX ;CHECK IF NEW EXCLUSIVE STATUS
- JNZ PUTXMS4 ;BREAK LOOP IF NOT
- ;
- PUTXMS3: PUSH DX ;GETDATA CHANGES DX
- CALL GETDATA ;GET NEXT BYTE
- POP DX
- JMP PUTXMSG ;GO STORE IT
- ;
- PUTXMS4: POP DS ;RECOVER REGS
- POP BX
- POP DI
- JMP GOTMSG
- ;
- ;* MIDI timing byte
- ;
- TIMEBYT: MOV BYTE PTR LASTTIM,AL ;SAVE IT AS POTENTIAL ERROR
- XOR AH,AH ;CLEAR UPPER BYTE
- ADD WORD PTR TIMER,AX ;ADD TO THE TIMER ACKUMULATOR
- ADC WORD PTR TIMER+2,0 ;32 BIT ADD
- ;
- CALL GETDATA ;GET NEXT BYTE
- TEST AL,MPUSBIT ;SEE IF RUNNING STATUS BYTE
- JNZ RSTAT ;JUMP IF SO
- ;
- ;* Message without running status. Use recorded status.
- ;
- MOV CH,BYTE PTR MIDISTT ;USE OLD RUNNING STATUS
- JMP SHORT GETMSG
- ;
- ;* We got time byte + something that can be either running
- ;* status or an MPU MARK
- ;
- RSTAT: CMP AL,MPUSMAX ;MAX RUNSTAT VALUE
- JBE MPUSTAT ;IT'S NO MARK, GET RUN STATUS
- ;
- ;* MIDI mark came from MPU401. Read and check it.
- ;
- MPUMARK: CMP AL,MPUNOOP ;NOOP WE JUST IGNORE
- JZ GOTMSG ;OTHERS WE TREAT AS ERROR
- ;
- HWIERR: MOV BYTE PTR LASTERR,AL ;SAVE FOR ERROR REPORTS
- OR BYTE PTR STATREC,8 ;SET ERROR BIT IN STATREC
- JMP GOTMSG
- ;
- ;* We got a running status byte + message. Save status and
- ;* prepare to get the message.
- ;
- MPUSTAT: MOV BYTE PTR MIDISTT,AL ;STORE AWAY
- MOV CH,AL ;KEEP IT IN BH
- CALL GETDATA ;AND GET NEXT BYTE
- ;
- ;* Get the message itself. We already have the first byte
- ;* in AL, and current running status in CH.
- ;
- GETMSG: MOV CL,AL ;FIRST BYTE TO CL
- MOV AL,CH ;GET STATUS TO AL
- AND AL,ATCHMSK ;MASK TO CHECK FOR 0CH/0DH
- SUB AL,ATOUCH ;SEE IF ONE OF THEM (0 IF NOT)
- JZ PUTMSG ;THEN GO STORE THEM
- ;
- CALL GETDATA ;GET 2:ND BYTE TOO
- ;
- ;* We now have status in CH, first byte in CL and (if
- ;* applicable) second byte in AL.
- ;
- PUTMSG: MOV DX,WORD PTR NENT ;SEE IF BUFFER FULL?
- ADD DX,8 ;8 BYTES HEADROOM
- CMP DX,WORD PTR BFSIZ ;CHECK IF TOO MUCH?
- JB PUTMSG1 ;JUMP IF ROOM IN BUFFER
- ;
- OR BYTE PTR STATREC,1 ;SET STATREC BUFFER OFLOW BIT
- JMP SHORT GOTMSG
- ;
- ;* Put the data bytes in the buffer.
- ;
- PUTMSG1: PUSH DI ;SAVE REGS
- PUSH BX
- MOV DX,WORD PTR BFSIZ ;BUFFER SIZE
- MOV BX,WORD PTR BFPUT ;PUTTER INDEX
- MOV DI,WORD PTR BFADR ;BUFFER ADDRESS OFFSET
- PUSH DS
- MOV DS,CS:WORD PTR BFADR+2 ;BUFFER ADDRESS SEGMENT
- ;
- CALL PUTBUF ;PUT IT IN BUFFER
- MOV CH,CL ;FIRST BYTE
- CALL PUTBUF ;PUT IT IN BUFFER
- MOV CH,AL ;SECOND BYTE
- CALL PUTBUF ;PUT IT IN BUFFER
- XOR CH,CH ;A DUMMY BYTE
- CALL PUTBUF ;PUT IT IN BUFFER
- POP DS ;RECOVER REGISTERS
- MOV WORD PTR BFPUT,BX ;UPDATE BUFFER INDEX
- POP BX
- POP DI
- OR BYTE PTR STATREC+1,1 ;SET DATA FLAG IN STATREC
- ADD WORD PTR NENT,4 ;UP BUFFER OCCUPANCY
- ;
- ;* MPU Message processed. Check if more data there.
- ;
- GOTMSG: MOV DX,WORD PTR CTLPORT ;SEE IF ANOTHER MESSAGE?
- IN AL,DX
- AND AL,DSRBIT ;CHECK STATUS
- JNZ HWIEXIT ;IF NOTHING THERE, EXIT
- JMP HWILOOP ;ELSE GO GET IT
- ;
- ;* Done, Make an EOI, restore the interrupted routine's
- ;* stack and return to it.
- ;
- HWIEXIT: MOV DX,WORD PTR EOIPORT ;INT CONTROLLER CMD PORT
- MOV AL,EOICMD
- POP CX ;RESTORE REGS
- POP DS
- CLI
- MOV SP,CS:WORD PTR TMPSP ;RESTORE CALLER'S SP
- MOV SS,CS:WORD PTR TMPSS ;AND SS
- OUT DX,AL ;SEND THE EOI COMMAND
- POP DX ;RECOVER FROM CALLER STACK
- POP AX
- IRET ;RESTORES INITIAL EI STATUS
- ;
- HWIHNL endp
- ;
- ;***************************************************************
- ;* GETDATA *
- ;* *
- ;* Read a byte from the data port. Returns the value in AX. *
- ;* Sets the timeout error bit in STATREC if no data before *
- ;* time-out. If time-out, returns FAKEDAT in AX. *
- ;***************************************************************
- ;
- GETDATA proc near
- ;
- PUSH CX ;SAVE REG
- MOV CX,CS:WORD PTR MPUDELY ;TIMEOUT VALUE
- MOV DX,CS:WORD PTR CTLPORT
- ;
- GETDAT1: IN AL,DX
- AND AX,DSRBIT ;CHECK IF DATA THERE
- JZ GETDAT2 ;THEN JUMP
- ;
- DEC CX ;BUMP TIMEOUT
- JNZ GETDAT1 ;LOOP IF NOT TIMED OUT
- ;
- POP CX ;RECOVER REG
- OR CS:BYTE PTR STATREC,4 ;SET TIMEOUT ERROR BIT
- MOV AX,FAKEDAT ;FAKED RETURN DATA
- RET
- ;
- GETDAT2: POP CX ;RECOVER REG
- DEC DX ;DATA PORT
- IN AL,DX ;GET DATA (AH=0)
- RET
- ;
- GETDATA endp
- ;
- ;***************************************************************
- ;* PUTBUF *
- ;* *
- ;* Put CH at the location addressed by DS:BX[DI], and adjust *
- ;* BX properly thereafter. DX holds the buffer size. It is *
- ;* assumed that any buffer overflow checks have already been *
- ;* made. *
- ;***************************************************************
- ;
- PUTBUF proc near
- ;
- ;* !!! Does not this basic addressing mode exist on the 8088/86???!!:
- ;* MOV DS:BYTE PTR BX[DI],CH ;STORE THE BYTE
- ;* !!! Trivial on 68K [MOVE.B 0(Dn,An),Dm] - This makes me upset!!!
- ;* !!! I'm sure you can do it, but maybe not with any regs?
- ;
- PUSH DI ;SAVE DI
- XCHG BX,DI ;SAVE BX
- ADD BX,DI ;TOTAL OFFSET INTO SEGMENT
- XCHG BX,DI ;RECOVER BX
- MOV DS:[DI],CH ;PUT IT DOWN
- POP DI ;RECOVER DI
- ;
- INC BX ;NEXT BUFFER POS.
- CMP BX,DX ;CHECK IF BEYOND BUFFER
- JB PUTBUF1
- ;
- XOR BX,BX ;WRAP TO BUFFER START
- ;
- PUTBUF1: RET
- ;
- PUTBUF endp
- ;
- SUBTTL Driver Init Code / Buffer Area
- %OUT Driver Init Code / Buffer Area
- PAGE
- ;
- ;***************************************************************
- ;* INIT (0) *
- ;* *
- ;* This code performs the INIT request, done only once when *
- ;* starting the operating system. This code will be overwrit- *
- ;* ten by the MIDI buffer later. Therefore it is located at *
- ;* the end of the driver. *
- ;***************************************************************
- ;
- DBUF EQU $ ;START OF MIDI BUFFER
- ;
- INIT proc near
- ;
- ;* First of all, determine if we are an XT or an AT.
- ;* Set MPU401 delay accordingly.
- ;
- MOV WORD PTR MPUDELY,2000H ;TIME FOR AN XT
- MOV CX,SP ;SAVE CURRENT SP
- PUSH SP ;PUSH IT
- POP AX ;GET THE VALUE WE JUST PUSHED
- CMP AX,CX ;IS IT SAME AS BEFORE PUSH?
- JNE INITXT ;NO, WE ARE AN 8086/88
- ;
- MOV CS:BYTE PTR ATFLAG,1 ;80286, FLAG IT
- MOV WORD PTR MPUDELY,6000H ;TIME FOR AN AT
- ;
- ;* Now install default values.
- ;
- INITXT: MOV CS:WORD PTR DATPORT,DDATPORT ;SET UP DEFAULT PORT ADDRESSES
- MOV CS:WORD PTR CTLPORT,DCTLPORT
- MOV CS:BYTE PTR HRDINT,DHRDINT ;AND INT NUMBERS
- MOV CS:BYTE PTR SFTINT,DSFTINT
- MOV CS:WORD PTR IBSIZ,DIBSIZ ;AND BUILT-IN BUFFER SIZE
- MOV CS:WORD PTR FNCTAB,OFFSET FNCTABI ;INACTIVE DRIVER JUMP TABLE
- ;
- ;* Now read and act upon any ASCII parameters. They will
- ;* supercede the defaults.
- ;
- MOV DS,ES:WORD PTR PMSOFS[BX] ;GET PARAMETER SEGMENT
- MOV SI,ES:WORD PTR PMOOFS[BX] ;GET PARAMETER OFFSET
- ;
- PARMLP: MOV AL,BYTE PTR[SI] ;GET CHARACTER
- INC SI
- CMP AL,'/' ;'/' STARTS ALL PARAMETERS
- JZ PARM
- CMP AL,ASCILF ;IS IT AN LF (END OF STRING)?
- JNZ PARMLP ;LOOP IF NOT
- ;
- JMP INIT2 ;END OF PARM STRING
- ;
- PARM: MOV AL,BYTE PTR[SI] ;GET WHAT PARAM
- INC SI
- AND AL,UCMASK ;UPPER CASE IT
- MOV DX,OFFSET ERRMSG1 ;ERROR ANTICIPATED
- MOV CS:BYTE PTR ERRMS1I,AL ;PUT PARM NAME THERE
- CMP AL,'B' ;BUILT-IN BUFFER SIZE?
- JZ PARM1
- CMP AL,'H' ;HARDWARE INT NUMBER?
- JZ PARM1
- CMP AL,'P' ;PORT ADDRESS?
- JZ PARM1
- CMP AL,'S' ;SOFTWARE INT NUMBER?
- JNZ PARMERR
- ;
- PARM1: MOV AL,BYTE PTR[SI] ;GET COLON - SHOULD BE ONE!
- INC SI
- MOV DX,OFFSET ERRMSG2 ;ANTICIPATE ERROR
- CMP AL,':' ;MUST BE A COLON!
- JNZ PARMERR
- ;
- CALL GETHEX ;GET PARAMETER VALUE
- MOV AL,CS:BYTE PTR ERRMS1I ;GET PARM NAME
- CMP AL,'P' ;PORT ADDRESS?
- JNZ PARM2
- ;
- MOV DX,OFFSET ERRMSG3 ;ANTICIPATE ERROR
- CMP CX,PORTMAX ;MAX ALLOWED PORT ADDRESS
- JA PARMERR
- ;
- MOV CS:WORD PTR DATPORT,CX ;NEW DATA PORT ADDRESS
- INC DX ;CTRL PORT ADDRES ONE HIGHER
- MOV CS:WORD PTR CTLPORT,CX
- JMP PARMLP
- ;
- PARM2: CMP AL,'S' ;SOFTWARE INTERRUPT?
- JNZ PARM3
- ;
- MOV DX,OFFSET ERRMSG4 ;ANTICIPATE ERROR
- CMP CX,MAXSFTI ;SEE IF HIGHER THAN ALLOWED
- JA PARMERR
- ;
- CMP CX,MINSFTI ;SEE IF LOWER THAN ALLOWED?
- JB PARMERR
- ;
- MOV CS:BYTE PTR SFTINT,CL ;INSTALL NEW SOFT INT NUMBER
- JMP PARMLP
- ;
- PARM3: CMP AL,'H' ;HARDWARE INTERRUPT?
- JNZ PARM5
- ;
- MOV DX,OFFSET ERRMSG5 ;ANTICIPATE ERROR
- CMP CX,0FH ;CHECK INT IS 0-0FH!
- JA PARMERR ;ELSE ERROR!
- ;
- MOV AL,CS:BYTE PTR ATFLAG ;SEE IF AT
- AND AL,AL
- JNZ PARM4 ;IF SO JUMP
- ;
- MOV DX,OFFSET ERRMSG6 ;ANTICIPATE ERROR
- CMP CL,7 ;FOR XT, ONLY 0-7 ARE ALLOWED
- JA PARMERR
- ;
- PARM4: MOV CS:BYTE PTR HRDINT,CL ;INSTALL NEW HARD INT NUMBER
- JMP PARMLP
- ;
- PARM5: MOV DX,OFFSET ERRMSG7 ;BUFSIZ - ANTICIPATE ERROR
- CMP CX,MINBSIZ ;CHECK VALUE LIMITS
- JB PARMERR
- CMP CX,MAXBSIZ
- JA PARMERR
- ;
- MOV CS:WORD PTR IBSIZ,CX ;INSTALL NEW BUFFER SIZE
- JMP PARMLP
- ;
- ;* You come here when an invalid parameter has been found.
- ;* Print an error message and stop scanning parameters.
- ;* Leave things as they are and continue.
- ;*
- ;* (What you would want to do is print the error, then
- ;* set the driver size to zero to avoid installing it.
- ;* This is possible according to the PC-DOS tech man.
- ;* HOWEVER, it *** DOES NOT WORK ***. MicroSoft them-
- ;* selves do some tricks to overcome this in their
- ;* Mouse Driver.)
- ;
- PARMERR: PUSH CS ;MAKE DS=CS
- POP DS
- PUSH DX ;SAVE MSG ADDRESS
- MOV DX,OFFSET ERRMSG0
- MOV AH,PRTSTR
- INT SYSTEM ;TYPE DRIVER NAME
- POP DX
- MOV AH,PRTSTR ;TYPE IT
- INT SYSTEM
- ;
- ; ;* Set size of driver to 0 (DOES NOT WORK - crashes the PC
- ; ;* during boot - bug in MSDOS's initialization code !).
- ; ;
- ; MOV ES:WORD PTR EOFOFS[BX],0 ;SET END OFFSET
- ; MOV ES:WORD PTR ESGOFS[BX],CS ;SET END PARAGRAPH = BEGINING
- ; RET
- ;
- ;* Parameters found and processed. Now install the software
- ;* interrupt vector after first saving the original value.
- ;
- INIT2: XOR AX,AX ;CLEAR DS
- MOV DS,AX
- MOV AL,CS:BYTE PTR SFTINT ;GET SOFT INT
- ADD AX,AX ;MULT BY 4
- ADD AX,AX
- MOV SI,AX
- MOV AX,WORD PTR DS:[SI] ;GET OLD VECTOR'S OFFSET
- MOV CS:WORD PTR OSWVEC,AX ;SAVE IT
- MOV AX,WORD PTR DS:2[SI] ;GET SEGMENT
- MOV CS:WORD PTR OSWVEC+2,AX ;AND SAVE
- ;
- MOV DS:WORD PTR [SI],OFFSET SWIHNL ;INSTALL SOFTWARE INTERRUPT
- MOV DS:WORD PTR 2[SI],CS ;HANDLER ADDRESS
- ;
- ;* Now set up hardware interrupt dependent values.
- ;
- MOV CX,OFFSET XTTAB ;ASSUME WE ARE AN XT
- MOV AL,CS:BYTE PTR ATFLAG ;CHECK THAT
- AND AX,0FFH ;CLEARS AH FOR LATER
- JE INIT3
- ;
- MOV CX,OFFSET ATTAB ;NO, WE ARE AN AT
- ;
- INIT3: MOV AL,CS:BYTE PTR HRDINT ;GET INT NUMBER
- ADD AX,AX ;SHIFT UP 2 (AH = 0)
- ADD AX,AX
- ADD AX,CX ;POINT TO PROPER TABLE ENTRY
- MOV SI,AX
- MOV AL,CS:BYTE PTR [SI] ;GET EOI PORT ADDRESS (8 BITS)
- MOV CS:BYTE PTR EOIPORT,AL ;INSTALL VALUE
- INC SI
- MOV AL,CS:BYTE PTR [SI] ;GET ENABLE PORT (8 BITS)
- MOV CS:BYTE PTR ENAPORT,AL ;INSTALL VALUE
- INC SI
- MOV AL,CS:BYTE PTR [SI] ;GET BIT MASK (8 BITS)
- MOV CS:BYTE PTR DISMASK,AL ;INSTALL DISABLE MASK
- NOT AL ;INVERT IT
- MOV CS:BYTE PTR ENAMASK,AL ;INSTALL ENABLE MASK
- INC SI
- MOV AL,CS:BYTE PTR [SI] ;GET VECTOR ADDR/4
- XOR AH,AH
- ADD AX,AX
- ADD AX,AX
- MOV CS:WORD PTR HWVECAD,AX ;INSTALL VECTOR ADDRESS
- ;
- ;* Now finally return driver values.
- ;
- PUSH CS
- POP DS
- MOV DX,OFFSET SIGNON
- MOV AH,PRTSTR
- INT SYSTEM
- ;
- PUSH CS ;GET CODE PARAGRAPH TO DX
- POP DX
- MOV AX,OFFSET DBUF + 16 ;GET DRIVER'S LENGTH + EXTRA
- ADD AX,CS:WORD PTR IBSIZ ;ADD BUILT-IN BUFFER SIZE
- MOV CL,4 ;SHIFT BY 4 TO GET # PARAGR.
- AND AL,0F0H ;CLEAR 4 LOW BITS
- ROR AX,CL
- ADD DX,AX ;END PARAGRAPH IN DX NOW
- MOV ES:WORD PTR EOFOFS[BX],0 ;SET END OFFSET
- MOV ES:WORD PTR ESGOFS[BX],DX ;SET END PARAGRAPH
- ;
- RET
- ;
- INIT endp
- ;
- ;***************************************************************
- ;* GETHEX *
- ;* *
- ;* Reads hex characters from string at DS:0[SI] and builds *
- ;* value in CX. Terminates on non-hex character. Only used *
- ;* during driver initialization. Overwritten by buffer. *
- ;***************************************************************
- ;
- GETHEX proc near
- ;
- XOR CX,CX ;CLEAR RESULT
- ;
- GETHXLP: MOV AL,BYTE PTR[SI] ;GET CHAR
- CMP AL,'0' ;SEE IF < DEC LOW
- JB GETHXEX ;THEN EXIT
- ;
- CMP AL,'9' ;SEE IF DEC DIGIT
- JBE GETHX1 ;THEN JUMP
- ;
- AND AL,UCMASK ;MASK TO UPPER CASE
- CMP AL,'A' ;SEE IF < HEX DIGIT
- JB GETHXEX ;THEN EXIT
- ;
- CMP AL,'F' ;SEE IF > HEX DIGIT
- JA GETHXEX ;THEN JUMP
- ;
- SUB AL,7 ;FOR ASCII 'A'-'F' OFFSET
- ;
- GETHX1: AND AX,0FH ;MASK DIGIT
- ADD CX,CX ;SHIFT CX UP 4
- ADD CX,CX
- ADD CX,CX
- ADD CX,CX
- ADD CX,AX ;ADD IN LAST DIGIT
- INC SI ;NEXT CHARACTER
- JMP GETHXLP
- ;
- GETHXEX: RET
- ;
- GETHEX endp
- ;
- SUBTTL Initialization Tables
- %OUT Initialization Tables
- PAGE
- ;
- ;***************************************************************
- ;* Initialization tables *
- ;***************************************************************
- ;
- ;* These tables contain, in each entry:
- ;*
- ;* - the EOI command output port address,
- ;* - the enable/disable interrupt port address,
- ;* - the interrupt enable bit mask, (bit is a one),
- ;* - the memory address where to put the vector for the
- ;* corresponding interrupt level (divided by 4 to fit
- ;* it in a byte).
- ;
- ;* First the table for an XT machine.
- ;
- XTTAB: DB 20H, 21H, 00000001B, 8 ;DATA FOR XT INT 0
- DB 20H, 21H, 00000010B, 9 ;FOR XT INT 1
- DB 20H, 21H, 00000100B, 0AH ;FOR XT INT 2
- DB 20H, 21H, 00001000B, 0BH ;FOR XT INT 3
- DB 20H, 21H, 00010000B, 0CH ;FOR XT INT 4
- DB 20H, 21H, 00100000B, 0DH ;FOR XT INT 5
- DB 20H, 21H, 01000000B, 0EH ;FOR XT INT 6
- DB 20H, 21H, 10000000B, 0FH ;FOR XT INT 7
- ;
- ;* Now, the table for an AT machine.
- ;
- ;* !!!NOTE!!! The data in the ATTAB table is NOT CORRECT!!!
- ;* (Due to lack of needed information about AT interrupts).
- ;
- ATTAB: DB 20H, 21H, 00000001B, 8 ;DATA FOR AT INT 0
- DB 20H, 21H, 00000010B, 9 ;FOR AT INT 1
- DB 20H, 21H, 00000100B, 0AH ;FOR AT INT 2
- DB 20H, 21H, 00001000B, 0BH ;FOR AT INT 3
- DB 20H, 21H, 00010000B, 0CH ;FOR AT INT 4
- DB 20H, 21H, 00100000B, 0DH ;FOR AT INT 5
- DB 20H, 21H, 01000000B, 0EH ;FOR AT INT 6
- DB 20H, 21H, 10000000B, 0FH ;FOR AT INT 7
- DB 20H, 21H, 00000001B, 8 ;FOR AT INT 8
- DB 20H, 21H, 00000010B, 9 ;FOR AT INT 9
- DB 20H, 21H, 00000100B, 0AH ;FOR AT INT 10
- DB 20H, 21H, 00001000B, 0BH ;FOR AT INT 11
- DB 20H, 21H, 00010000B, 0CH ;FOR AT INT 12
- DB 20H, 21H, 00100000B, 0DH ;FOR AT INT 13
- DB 20H, 21H, 01000000B, 0EH ;FOR AT INT 14
- DB 20H, 21H, 10000000B, 0FH ;FOR AT INT 15
- ;
- SUBTTL Error Messages
- %OUT Error Messages
- PAGE
- ;
- ;***************************************************************
- ;* Error messages *
- ;***************************************************************
- ;
- SIGNON: DB '--- Installing CMT MIDI MPU401 Device Driver'
- DB ' v1.30 ---',ASCICR, ASCILF,'$'
- ;
- ERRMSG0: DB 'MPU401 MIDI Driver: $'
- ;
- ERRMSG1: DB 'Unknown Init Parameter "'
- ERRMS1I: DB ' "' ;CHAR TO BE INSERTED
- DB ASCICR,ASCILF,'$'
- ;
- ERRMSG2: DB 'No ":" After Parm Name'
- DB ASCICR,ASCILF,'$'
- ;
- ERRMSG3: DB 'Illegal Port Address'
- DB ASCICR,ASCILF,'$'
- ;
- ERRMSG4: DB 'Illegal Software Interrupt'
- DB ASCICR,ASCILF,'$'
- ;
- ERRMSG5: DB 'Illegal Hardware Interrupt'
- DB ASCICR,ASCILF,'$'
- ;
- ERRMSG6: DB 'Hard Int 8-15 Allowed On PC-AT Only'
- DB ASCICR,ASCILF,'$'
- ;
- ERRMSG7: DB 'Illegal Buffer Size'
- DB ASCICR,ASCILF,'$'
- ;
- CODSEG ENDS
- IF1
- %OUT ---------- Pass 1 Completed ----------
- ENDIF
- IF2
- %OUT ---------- Pass 2 Completed ----------
- ENDIF
- SUBTTL Symbols
- END
-