home *** CD-ROM | disk | FTP | other *** search
/ Audio 4.94 - Over 11,000 Files / audio-11000.iso / msdos / midi / pc_midi / pc-midi / mpu401.asm < prev    next >
Encoding:
Assembly Source File  |  1988-11-24  |  60.5 KB  |  2,081 lines

  1.      PAGE    50,132    
  2.      TITLE    MIDI Interface Device Driver
  3.      %OUT    MIDI Interface Device Driver
  4.      ;***************************************************************
  5.      ;*                 MPU401                *
  6.      ;*                                *
  7.      ;* This program is loaded as an I/O driver during MSDOS initi-    *
  8.      ;* alization. It contains a driver for a MIDI (Music Instru-    *
  9.      ;* ment Digital Interface) interface, the MPU401 from Roland    *
  10.      ;* Corp. This software has been tsted using an MPU401 with    *
  11.      ;* software revision level 1.5A.                *
  12.      ;*                                *
  13.      ;* This driver acknowledges only three function requests from    *
  14.      ;* DOS, namely INIT, CHIN and CHOUT. INIT is for usage by DOS    *
  15.      ;* during system start-up. CHOUT receives control data setting    *
  16.      ;* the software interrupt number. The CHIN request passes an    *
  17.      ;* infinite stream of characters denoting the current software    *
  18.      ;* interrupt number. Consequently, an application program can    *
  19.      ;* set and read what software interrupt is used.        *
  20.      ;*                                *
  21.      ;* Software interrupts lower than 35H are not allowed, because    *
  22.      ;* they may conflict with values used by MSDOS. Higher values    *
  23.      ;* may also conflict with DOS, but for these it is more unli-    *
  24.      ;* kely. Eventually you will need to find an own soft inter-    *
  25.      ;* rupt number. The default (68H) works nicely on the system    *
  26.      ;* where the driver was developed. You can set the software    *
  27.      ;* interrupt level by editing your CONFIG.SYS file.        *
  28.      ;*                                *
  29.      ;* All access to the driver from a user program (except set-    *
  30.      ;* ting/reading the software interrupt number) is done using    *
  31.      ;* software interrupts, since then you don't have to pass    *
  32.      ;* through DOS's dispatcher etc. Software interrupt is much    *
  33.      ;* faster.                            *
  34.      ;*                                *
  35.      ;* When the application program issues a software interrupt,    *
  36.      ;* register AX should contain the desired 'function code',    *
  37.      ;* similar to a DOS INT21 call. Note that AX, not AH is used.    *
  38.      ;* The following 'function codes' are defined:            *
  39.      ;*                                *
  40.      ;* 0    SINIT    Driver initialize                *
  41.      ;* 1    SFINI    Driver finitialize                *
  42.      ;* 2    SSTAT    Get driver status                *
  43.      ;* 3    SRERR    Reset error flags                *
  44.      ;* 4    SSTIM    Set timer                    *
  45.      ;* 5    SRTIM    Read timer                    *
  46.      ;* 6    SFBUF    Flush input data buffer                *
  47.      ;* 7    SRBUF    Read input data buffer                *
  48.      ;* 8    STBUF    Test input data buffer                *
  49.      ;* 9    SDXBF    Define exclusive message buffer            *
  50.      ;* 10  SFXBF    Flush exclusive message buffer            *
  51.      ;* 11    SRXBF    Read exclusive message buffer            *
  52.      ;* 12    STXBF    Test exclusive message buffer            *
  53.      ;* 13    SOCMD    Send MPU command                *
  54.      ;* 14    SODAT    Send MPU data                    *
  55.      ;*                                *
  56.      ;* Some software interrupts return with a completion code in    *
  57.      ;* register DX and/or a return value in AX. (DX-AX are the    *
  58.      ;* registers for a 'long' return value used by most 'C' com-    *
  59.      ;* pilers - MicroSoft, Wizard, DeSmet, and (yech!) Digital    *
  60.      ;* Research. Lattice uses BX-AX).                *
  61.      ;*                                *
  62.      ;* Data coming from the MPU401 is by default buffered in an    *
  63.      ;* internal buffer ('DBUF') which has a default size of 1 kb.    *
  64.      ;* At driver initialization, another size may be selected for    *
  65.      ;* this buffer. An application program may also allocate it's    *
  66.      ;* own data buffer, which will then be used by the driver in-    *
  67.      ;* stead of the internal one. MIDI exclusive messages are not    *
  68.      ;* buffered by default - they are just ignored. An applica-    *
  69.      ;* tion may set up an own buffer for these.            *
  70.      ;*                                *
  71.      ;* The driver responds to certain parameters in the invoca-    *
  72.      ;* tion line in CONFIG.SYS:                    *
  73.      ;*                                *
  74.      ;* DEVICE=MPU401.DEV /B:nnnn /H:n /P:nnnn /S:nn        *
  75.      ;*                                *
  76.      ;* /B:nnnn    Set Internal Buffer Size to nnnn (200H <= n    *
  77.      ;*        <= 8000H)                    *
  78.      ;* /H:n    Set Hardware Interrupt to n (0-0FH for an AT,    *
  79.      ;*        0-7 for an XT).                    *
  80.      ;* /P:nnnn    Set MPU401 Port Address to nnnn (0-3FFH).    *
  81.      ;* /S:nn    Set Software Interrupt number to nn (35H-0FFH).    *
  82.      ;*        (May also be changed by application.)        *
  83.      ;*                                *
  84.      ;* (n, nn, and nnnn are given in hexadecimal notation.)    *
  85.      ;***************************************************************
  86.      ;*                                *
  87.      ;* Original Author B. Larsson - All wrongs reserved.        *
  88.      ;*                                *
  89.      ;* (Many ideas taken from the CMU MIDI Toolkit sources -    *
  90.      ;*  notably aintr.asm, mpu.c and cintr.c. Regards to the    *
  91.      ;*  authors of all those).                    *
  92.      ;*                                *
  93.      ;***************************************************************
  94.      ;* I herewith place this software is in the PUBLIC DOMAIN. You    *
  95.      ;* are free to make any modifications to it that you want. THE    *
  96.      ;* ONLY RESTRICTION is that you PLEASE leave the credit notes    *
  97.      ;* of all previous authors in the source. Please also maintain    *
  98.      ;* a proper change log, so that followers see what has been    *
  99.      ;* done, and WHY! Thank You!                    *
  100.      ;***************************************************************
  101.      ;*                                *
  102.      ;* 1.0:  Started:                    870105    *
  103.      ;*      Functional and debugged except exclusive        *
  104.      ;*      soft ints:                    870112    *
  105.      ;* 1.1:  Exclusive buffer implemented:            870117    *
  106.      ;* 1.2:  Does 5000 dummy byte reads from MPU401 at        *
  107.      ;*      initialization before it gives up (did just        *
  108.      ;*      200):                        870118    *
  109.      ;* 1.30: Cute little sign-on string:            871109    *
  110.      ;***************************************************************
  111.      ;
  112. CODSEG     SEGMENT PUBLIC PARA 'CODE'
  113.      ASSUME     CS:CODSEG,ES:NOTHING,DS:NOTHING
  114.      ;
  115.      SUBTTL    Equates
  116.      %OUT    Equates
  117.      PAGE
  118.      ;
  119.      ;***************************************************************
  120.      ;* Equates                            *
  121.      ;***************************************************************
  122.      ;
  123.      ;* ASCII Equates.
  124.      ;
  125. ASCICR     EQU    0DH                ;CARRIAGE RETURN
  126. ASCILF     EQU    0AH                ;LINE FEED
  127. UCMASK     EQU    5FH                ;MASKS LOWER CASE TO UPPER
  128.      ;
  129.      ;* System related equates.
  130.      ;
  131. SYSTEM     EQU    21H                ;SYSTEM CALL
  132. PRTSTR     EQU    9                ;PRINT STRING REQUEST
  133. JFAROPC     EQU    0EAH                ;JUMP FAR OP-CODE
  134.      ;
  135.      ;* Device driver equates.
  136.      ;
  137. DEVATR     EQU    1000000000000000B        ;DEVICE ATTRIBUTES
  138. INITRQ     EQU    0                ;INIT FUNCTION REQUEST
  139. CHINRQ     EQU    4                ;CHIN FUNCTION REQUEST
  140. CHOUTRQ     EQU    8                ;CHOUT FUNCTION REQUEST
  141.      ;
  142. RDYSTT     EQU    0000000100000000B        ;READY STATUS     RETURN CODE
  143. WRPSTT     EQU    1000000000000000B        ;WRITE PROTECT     RETURN CODE
  144. ILLSTT     EQU    1000000000000011B        ;BAD REQ STATUS     RETURN CODE
  145.      ;
  146.      ;* I/O Request header offsets.
  147.      ;
  148. CMDOFS     EQU    2                ;REQUEST CODE     REQ HDR OFS
  149. STTOFS     EQU    3                ;RETURN STATUS     REQ HDR OFS
  150. UNCOFS     EQU    13                ;RETURN UNITCODE REQ HDR OFS
  151. EOFOFS     EQU    14                ;END ADDR OFFSET REQ HDR OFS
  152. ESGOFS     EQU    16                ;END ADDR SEG     REQ HDR OFS
  153. PDOOFS     EQU    14                ;PARM WRD OFS     REQ HDR OFS
  154. PDSOFS     EQU    16                ;PARM WRD SEG     REQ HDR OFS
  155. BCTOFS     EQU    18                ;BYTE COUNT     REQ HDR OFS
  156. PMOOFS     EQU    18                ;ARGUMENT OFFS     REQ HDR OFS
  157. PMSOFS     EQU    20                ;ARGUMENT SEG     REQ HDR OFS
  158.      ;
  159.      ;* Default values.
  160.      ;
  161. DDATPORT EQU    330H                ;DEFAULT DATA PORT LOCATION
  162. DCTLPORT EQU    331H                ;DEFAULT CTRL PORT LOCATION
  163. PORTMAX     EQU    7FEH                ;MAX PORT LOCATION
  164. DHRDINT     EQU    2                ;DEFAULT HARD INT TO USE
  165. DSFTINT     EQU    68H                ;DEFAULT SOFT INT TO USE
  166. MINSFTI     EQU    35H                ;MINIMUM SOFT INT TO USE
  167. MAXSFTI     EQU    0FFH                ;MAXIMUM SOFT INT TO USE
  168.      ;
  169.      ;* MIDI port values.
  170.      ;
  171. DSRBIT     EQU    80H                ;DSR BIT MASK
  172. DRRBIT     EQU    40H                ;DRR BIT MASK
  173.      ;
  174.      ;* Buffer size values.
  175.      ;
  176. MINBSIZ     EQU    512                ;MIN BUFFER SIZE
  177. DIBSIZ     EQU    1024                ;DEFAULT 1K INTERNAL BUFFER
  178. MAXBSIZ     EQU    32768                ;MAX BUFFER SIZE
  179.      ;
  180.      ;* Software interrupt completion codes.
  181.      ;
  182. CPLOK     EQU    0                ;OK COMPLETION
  183. CPLIFNC     EQU    1                ;ILLEGAL FUNCTION CODE
  184. CPLPASS     EQU    2                ;DRIVER INACTIVE
  185. CPLMOFL     EQU    3                ;MIDI PROVIDES TOO MUCH DATA
  186. CPLPRTE     EQU    4                ;MIDI PROTOCOL ERROR
  187. CPLBERR     EQU    5                ;INVALID BUFFER SPECIFICATION
  188. CPLTERR     EQU    6                ;MIDI TIMEOUT ERROR
  189. CPLNOXB     EQU    7                ;EXCLUSIVE BUFFER NOT DEFINED
  190.      ;
  191.      ;* MPU401 and MIDI command and status codes.
  192.      ;
  193. MPURES     EQU    0FFH                ;MPU401 RESET COMMAND
  194. MPUACK     EQU    0FEH                ;MPU401 'ACK' BYTE
  195. MPUOFLO     EQU    0F8H                ;INDICATES MPU TIMER OVERFLOW
  196. MPUNOOP     EQU    0F8H                ;NOOP MPU MARK CODE
  197. MPUGRCT     EQU    0ABH                ;GET MPU RECORD COUNTER CMD
  198. FAKEDAT     EQU    0F8H                ;FAKE DATA USED AT TIMEOUT
  199. MPUMAXT     EQU    0EFH                ;MAX TIME BYTE FROM MPU401
  200. MPUSBIT     EQU    80H                ;MASK FOR RUNNING STATUS BIT
  201. MPUSMSK     EQU    0F0H                ;MASK FOR RUN STATUS NIB
  202. MPUSMAX     EQU    0EFH                ;MAX RUN STATUS VALUE
  203. ATCHMSK     EQU    0E0H                ;MASKS Cx AND Dx TO C0
  204. ATOUCH     EQU    0C0H                ;AFTERTOUCH STATUS BYTE
  205. MPUSYSM     EQU    0FFH                ;STATUS BYTE FOR SYSTEM MSG
  206. MPUMIDX     EQU    0F0H                ;YTE FOR SYSTEM EXCLUSIVE MSG
  207.      ;
  208.      ;* Miscellaneous.
  209.      ;
  210. EOICMD     EQU    20H                ;EOI COMMAND FOR 8259 CHIP
  211. OFLTIM     EQU    240                ;MPU TIMEOUT VALUE
  212.      ;
  213.      SUBTTL    Static Data Area
  214.      %OUT    Static Data Area
  215.      PAGE
  216.      ;
  217.      ;***************************************************************
  218.      ;* Static storage                        *
  219.      ;***************************************************************
  220.      ;
  221. DVNXT:     DW    0FFFFH                ;PTR TO NEXT DRIVER HEADER
  222.      DW    0FFFFH
  223. DVATR:     DW    DEVATR                ;DEVICE'S ATTRIBUTES
  224. DVSTRA:     DW    OFFSET STRAT            ;ADDRESS OF STRATEGY ENTRY
  225. DVEXEC:     DW    OFFSET EXEC            ;ADDRESS OF EXEC ('INT') ENTRY
  226. DVNAME:     DB    'MPU401$$'            ;DEVICE NAME
  227.      ;
  228.      ;*  Buffer related values.
  229.      ;
  230. IBSIZ:     DW    0                ;SIZE OF BUILT-IN BUFFER
  231. BFADR:     DD    0                ;BUFFER ADDRESS
  232. BFSIZ:     DW    0                ;SIZE OF CURRENT BUFFER
  233. BFPUT:     DW    0                ;WRITE INDEX INTO BUFFER
  234. BFPIC:     DW    0                ;READ INDEX INTO BUFFER
  235. NENT:     DW    0                ;BYTES IN BUFFER
  236. XBFADR:     DD    0                ;EXCL BUFFER ADDRESS
  237. XBFSIZ:     DW    0                ;SIZE OF EXCLUSIVE BUFFER
  238. XBFPUT:     DW    0                ;WRITE INDEX INTO EXCL BUFFER
  239. XBFPIC:     DW    0                ;READ INDEX INTO EXCL BUFFER
  240. XNENT:     DW    0                ;BYTES IN EXCL BUFER
  241.      ;
  242.      ;* Port addresses etc.
  243.      ;
  244. DATPORT: DW    0                ;MIDI DATA PORT ADDRESS
  245. CTLPORT: DW    0                ;MIDI CONTROL PORT ADDRESS
  246. EOIPORT: DW    0                ;PORT ADDRESS FOR EOI
  247. ENAPORT: DW    0                ;PORT ADDRESS FOR EN/DISABLE
  248.      ;
  249.      ;* Temporary storage locations.
  250.      ;
  251. RHPTR:     DD    0                ;DWORD POINTER TO REQ HDR
  252. TMPSP:     DW    0                ;INTERRUPTED PROGRAM'S SP
  253. TMPSS:     DW    0                ;INTERRUPTED PROGRAM'S SS
  254.      ;
  255.      ;* Interrupt related values.
  256.      ;
  257. HRDINT:     DB    0                ;HARDWARE INTERRUPT NUMBER
  258. SFTINT:     DB    0                ;SOFTWARE INTERRUPT NUMBER
  259. OSWVEC:     DD    0                ;ORIGINAL SW VECTOR
  260. HWVECAD: DW    0                ;HW VECTOR ADDRESS IN SEGM 0
  261. ENAMASK: DB    0                ;INT ENABLE MASK
  262. DISMASK: DB    0                ;INT DISABLE MASK
  263.      ;
  264.      ;* Miscellaneous flags etc.
  265.      ;
  266. STATREC: DW    0                ;STATUS RECORD
  267.                         ;BIT 15 DRIVER ACTIVE
  268.                         ;BIT 14 USED FOR REPORTING
  269.                         ; AT/XT STATUS. NOT WRITTEN
  270.                         ; IN THIS WORD.
  271.                         ;BIT 10 MPU CMD ACK'ED
  272.                         ;BIT 9 DATA IN EXCL BUFFER
  273.                         ;BIT 8 DATA IN BUFFER
  274.                         ;BIT 3 INPUT PROTOCOL ERROR
  275.                         ;BIT 2 TIMEOUT ERROR
  276.                         ;BIT 1 EXCL BUFFER OVERFLOW
  277.                         ;BIT 0 BUFFER OVERFLOW
  278. FNCTAB:     DW    FNCTABI                ;CURRENT FUNCTION JUMP TABLE
  279. MPUDELY: DW    0                ;MAX TIME TO WAIT FOR MPU401
  280. ATFLAG:     DB    0                ;NON-ZERO IF PC-AT
  281. ORGENA:     DB    0                ;ORIGINAL INT ENABLE MASK
  282. LASTTIM: DB    0                ;TIME OF LAST ERROR
  283. LASTERR: DB    0                ;BYTE OF LAST ERROR
  284. MIDISTT: DB    0                ;MIDI RUNNING STATUS BYTE
  285.      ;
  286.      ;* Real-time timer accumulator
  287.      ;
  288. ACKTIME: DB    0                ;FLAGS MPUACK AWAITS TIME BYTE
  289. TIMER:     DD    0                ;32 BITS TIMER ACKUMULATOR
  290.      ;
  291.      ;* Function jump table for active driver.
  292.      ;
  293. FNCTABA: DW    SINITA                ;INITIALIZE
  294.      DW    SFINIA                ;FINITIALIZE
  295.      DW    SSTAT                ;GET DRIVER STATUS
  296.      DW    SRERR                ;RESET ERROR FLAGS
  297.      DW    SSTIM                ;SET TIMER
  298.      DW    SRTIMA                ;READ TIMER
  299.      DW    SFBUF                ;FLUSH INPUT BUFFER
  300.      DW    SRBUFA                ;READ INPUT BUFFER
  301.      DW    STBUFA                ;TEST INPUT BUFFER
  302.      DW    SDXBFA                ;DEFINE EXCLUSIVE BUFFER
  303.      DW    SFXBF                ;FLUSH EXCLUSIVE BUFFER
  304.      DW    SRXBFA                ;READ EXCLUSIVE BUFFER
  305.      DW    STXBFA                ;TEST EXCLUSIVE BUFFER
  306.      DW    SOCMDA                ;SEND COMMAND
  307.      DW    SODATA                ;SEND DATA
  308.      ;
  309.      ;* Function jump table for inactive driver.
  310.      ;
  311. FNCTABI: DW    SINITI                ;INITIALIZE
  312.      DW    SFINII                ;FINITIALIZE
  313.      DW    SSTAT                ;GET DRIVER STATUS
  314.      DW    SRERR                ;RESET ERROR FLAGS
  315.      DW    SSTIM                ;SET TIMER
  316.      DW    SRTIMI                ;READ TIMER
  317.      DW    SFBUF                ;FLUSH INPUT BUFFER
  318.      DW    SRBUFI                ;READ INPUT BUFFER
  319.      DW    STBUFI                ;TEST INPUT BUFFER
  320.      DW    SDXBFI                ;DEFINE EXCLUSIVE BUFFER
  321.      DW    SFXBF                ;FLUSH EXCLUSIVE BUFFER
  322.      DW    SRXBFI                ;READ EXCLUSIVE BUFFER
  323.      DW    STXBFI                ;TEST EXCLUSIVE BUFFER
  324.      DW    SOCMDI                ;SEND COMMAND
  325.      DW    SODATI                ;SEND DATA
  326.      ;
  327. MAXFNC     EQU    ($-FNCTABI) / 2            ;NUMBER OF TABLE ENTRIES
  328.      ;
  329. REVCOD:     DB    'MPU401 v.1.30 - 871109, '
  330.      DB    'Author Bjorn Larsson, Stockholm 1987 '
  331.      ;
  332. STACK:     DB    200 DUP ( 0 )            ;OUR OWN STACK
  333. STKTOP:     DB    0                ;TOP OF IT
  334.      ;
  335.      SUBTTL    Driver Entry Points
  336.      %OUT    Driver Entry Points
  337.      PAGE
  338.      ;
  339.      ;***************************************************************
  340.      ;* Strategy routine.                        *
  341.      ;*                                *
  342.      ;* Just stores away the request header pointer in RHPTR.    *
  343.      ;***************************************************************
  344.      ;
  345. STRAT     proc    far
  346.      ;
  347.      MOV    WORD PTR CS:RHPTR,BX        ;SAVE OFFSET
  348.      MOV    WORD PTR CS:RHPTR+2,ES        ;AND PARAGRAPH
  349.      RET
  350.      ;
  351. STRAT     endp
  352.      ;
  353.      ;***************************************************************
  354.      ;* 'Interrupt' routine.                    *
  355.      ;*                                *
  356.      ;* Performs the action specified at the call to the strategy    *
  357.      ;* routine.                            *
  358.      ;***************************************************************
  359.      ;
  360. EXEC     proc    far
  361.      ;
  362.      PUSH    DS                ;SAVE MACHINE STATE
  363.      PUSH    ES
  364.      PUSH    SI
  365.      PUSH    DI
  366.      PUSH    AX
  367.      PUSH    BX
  368.      PUSH    CX
  369.      PUSH    DX
  370.      ;
  371.      PUSH    CS                ;MAKE DS = CS
  372.      POP    DS
  373.      ;
  374.      MOV    BX,WORD PTR RHPTR        ;PICK UP POINTER TO REQ HDR
  375.      MOV    ES,WORD PTR RHPTR+2
  376.      MOV    AL,ES:[BX]+CMDOFS        ;GET FUNCTION CODE
  377.      ;
  378.      CMP    AL,INITRQ            ;INIT?
  379.      JNZ    EXEC1
  380.      ;
  381.      CALL    INIT                ;DO INIT REQUEST
  382.      JMP    SHORT READY
  383.      ;
  384. EXEC1:     CMP    AL,CHINRQ            ;CHIN?
  385.      JZ    CHIN
  386.      CMP    AL,CHOUTRQ            ;CHOUT?
  387.      JZ    CHOUT
  388.      ;
  389.      NOP
  390.      NOP
  391.      ;
  392. ILLREQ:     MOV    ES:WORD PTR STTOFS[BX],ILLSTT    ;SAY ILLEGAL REQUEST
  393.      JMP    SHORT EXIT
  394.      ;
  395. READY:     MOV    ES:WORD PTR STTOFS[BX],RDYSTT    ;SAY OK
  396.      ;
  397. EXIT:     POP    DX                ;RESTORE MACHINE STATE
  398.      POP    CX
  399.      POP    BX
  400.      POP    AX
  401.      POP    DI
  402.      POP    SI
  403.      POP    ES
  404.      POP    DS
  405.      RET
  406.      ;
  407.      ;***************************************************************
  408.      ;* CHIN (4)                            *
  409.      ;*                                *
  410.      ;* Always sends back the software interrupt number in the form    *
  411.      ;* of one character.                        *
  412.      ;***************************************************************
  413.      ;
  414. CHIN:     MOV    DS,ES:WORD PTR PDSOFS[BX]    ;GET SEGM OF DTA
  415.      MOV    DI,ES:WORD PTR PDOOFS[BX]    ;AND OFFSET
  416.      MOV    AL,CS:BYTE PTR SFTINT        ;GET SOFT INT NUMBER
  417.      MOV    BYTE PTR DS:[DI],AL        ;AND RETURN IT AS A CHAR
  418.      JMP    READY                ;SUCCESSFULL RETURN
  419.      ;
  420.      ;***************************************************************
  421.      ;* CHOUT (8)                            *
  422.      ;*                                *
  423.      ;* Installs the software interrupt number given by the charac-    *
  424.      ;* ter passed.                            *
  425.      ;***************************************************************
  426.      ;
  427. CHOUT:     MOV    DS,ES:WORD PTR PDSOFS[BX]    ;GET SEGM OF DTA
  428.      MOV    DI,ES:WORD PTR PDOOFS[BX]    ;AND OFFSET
  429.      MOV    CL,BYTE PTR DS:[DI]        ;GET SOFT INT NUMBER
  430.      ;
  431.      CMP    CL,MINSFTI            ;CHECK VALID INT NUMBER
  432.      JAE    CHOUTOK                ;MUST BE GREATER
  433.      ;
  434.      MOV    ES:WORD PTR STTOFS[BX],WRPSTT    ;SAY WRITE PROTECT
  435.      JMP    EXIT                ;ERROR RETURN
  436.      ;
  437.      ;* Valid new value. First restore old vector.
  438.      ;
  439. CHOUTOK: XOR    AX,AX                ;CLEAR DS
  440.      MOV    DS,AX
  441.      MOV    AL,CS:BYTE PTR SFTINT        ;GET SOFT INT
  442.      ADD    AX,AX                ;MULT BY 4
  443.      ADD    AX,AX
  444.      MOV    SI,AX
  445.      MOV    AX,CS:WORD PTR OSWVEC        ;GET OLD VECTOR'S OFFSET
  446.      MOV    WORD PTR DS:[SI],AX        ;RESTORE IT
  447.      MOV    AX,CS:WORD PTR OSWVEC+2        ;GET SEGMENT
  448.      MOV    WORD PTR DS:2[SI],AX        ;AND RESTORE
  449.      ;
  450.      ;* Now install the new value.
  451.      ;
  452.      MOV    CS:BYTE PTR SFTINT,CL        ;INSTALL IT
  453.      XOR    CH,CH                ;IN CX
  454.      ADD    CX,CX                ;MULT BY 4
  455.      ADD    CX,CX
  456.      MOV    SI,CX
  457.      ;
  458.      MOV    AX,WORD PTR DS:[SI]        ;GET OLD VECTOR'S OFFSET
  459.      MOV    CS:WORD PTR OSWVEC,AX        ;SAVE IT
  460.      MOV    AX,WORD PTR DS:2[SI]        ;GET SEGMENT
  461.      MOV    CS:WORD PTR OSWVEC+2,AX        ;AND SAVE
  462.      ;
  463.      MOV    DS:WORD PTR [SI],OFFSET SWIHNL    ;INSTALL SOFTWARE INTERRUPT
  464.      MOV    DS:WORD PTR 2[SI],CS        ;HANDLER ADDRESS    
  465.      ;
  466.      JMP    READY                ;SUCCESSFULL RETURN
  467.      ;
  468. EXEC     endp
  469.      ;
  470.      SUBTTL    Soft Interrupt Dispatcher
  471.      %OUT    Soft Interrupt Dispatcher
  472.      PAGE
  473.      ;
  474.      ;***************************************************************
  475.      ;* Software Interrupt Handler.                    *
  476.      ;*                                *
  477.      ;* You come here when an application program wants to do some-    *
  478.      ;* thing with the MPU401, and tries to use the software int.    *
  479.      ;*                                *
  480.      ;* At entry, reg AX contains the Function Code, and any input    *
  481.      ;* parameter is passed in BX, CX and DX.            *
  482.      ;*                                *
  483.      ;* The individal functions return any data in AX.        *
  484.      ;* Functions that can detect errors return an error code in    *
  485.      ;* DX. Functions that do not detecte errors (always success-    *
  486.      ;* full) do not set DX.                    *
  487.      ;*                                *
  488.      ;* Completion code may be                    *
  489.      ;*                                *
  490.      ;* 0:        OK, no error.                    *
  491.      ;* 1:        Illegal Function Code.                *
  492.      ;* 2:        Driver not activated.                *
  493.      ;* 3:        MIDI data overflow error.            *
  494.      ;* 4:        MIDI protocol error.                *
  495.      ;* 5:        Illegal buffer specification.            *
  496.      ;* 6:        MIDI timeout error.                *
  497.      ;*                                *
  498.      ;* This entry point will destroy the AX and DX register only.    *
  499.      ;***************************************************************
  500.      ;
  501. SWIHNL     proc    far
  502.      ;
  503.      STI                    ;RE-ENABLE INTERRUPTS
  504.      CMP    AX,MAXFNC            ;VALID FUNCTION CODE?
  505.      JB    SWIHNL1                ;JUMP IF SO
  506.      ;
  507.      MOV    DX,CPLIFNC            ;ILLEGAL FUNCTION CODE
  508.      IRET
  509.      ;
  510. SWIHNL1: PUSH    DI
  511.      PUSH    DS                ;SAVE DS
  512.      PUSH    CS                ;SET DS=CS
  513.      POP    DS
  514.      ADD    AX,AX                ;SHIFT LEFT FOR WORD INDEX
  515.      MOV    DI,WORD PTR FNCTAB        ;GET THE JUMP TABLE
  516.      ADD    DI,AX
  517.      JMP    WORD PTR [DI]            ;GO TO HANDLER
  518.      ;
  519.      SUBTTL    Soft Interrupt Init
  520.      %OUT    Soft Interrupt Init
  521.      PAGE
  522.      ;
  523.      ;***************************************************************
  524.      ;* 0:    SINIT:                            *
  525.      ;*                                *
  526.      ;* If the driver was currently active, it is set inactive.    *
  527.      ;*                                *
  528.      ;* if BX is non-zero, DX:CX is the address of a non-default    *
  529.      ;* data buffer, and BX is the size of it. If BX is 0, the in-    *
  530.      ;* ternal buffer is used. Then some hardware initialization is    *
  531.      ;* done to the MPU401. Extensive checks are made that the    *
  532.      ;* MPU401 is present and responds correctly. If something does    *
  533.      ;* not work properly, the initialization is not performed and    *
  534.      ;* an error is reported. If all goes well, DX returns CPLOK.    *
  535.      ;***************************************************************
  536.      ;
  537.      ;* For active driver (must finitialize first).
  538.      ;
  539. SINITA:     CALL    FINITIT                ;DO DRIVER FINITIALIZE
  540.      AND    DX,DX                ;SEE IF OK?
  541.      JZ    SINITI                ;THEN JUMP
  542.      ;
  543.      POP    DS                ;RECOVER REGS
  544.      POP    DI
  545.      IRET                    ;RETURN FROM FINITI'S ERROR
  546.      ;
  547.      ;* For inactive driver (normal initialization).
  548.      ;
  549. SINITI:     PUSH    CX
  550.      ;
  551.      ;* First initialize most values to defaults.
  552.      ;
  553.      MOV    WORD PTR BFADR,OFFSET DBUF    ;SET UP TO USE INTERNAL BUFFER
  554.      MOV    WORD PTR BFADR+2,CS        ;SEGMENT
  555.      MOV    AX,WORD PTR IBSIZ        ;BUFFER SIZE
  556.      MOV    WORD PTR BFSIZ,AX
  557.      XOR    AX,AX                ;CLEAR CS
  558.      MOV    WORD PTR BFPIC,AX        ;CLEAR INDICES
  559.      MOV    WORD PTR BFPUT,AX
  560.      MOV    WORD PTR XBFPIC,AX
  561.      MOV    WORD PTR XBFPUT,AX
  562.      MOV    WORD PTR XBFSIZ,AX        ;NO EXCL BUFFER
  563.      MOV    WORD PTR NENT,AX        ;NO ENTRIES IN BUFFER YET
  564.      MOV    WORD PTR XNENT,AX        ;NO EXCL ENTRIES
  565.      MOV    WORD PTR TIMER,AX        ;CLEAR TIME RECORD
  566.      MOV    WORD PTR TIMER+2,AX
  567.      MOV    BYTE PTR ACKTIME,AL        ;CLEAR TIME EXPECTATION FLAG
  568.      MOV    BYTE PTR MIDISTT,90H        ;INITIAL MIDI RUNNING STATUS
  569.      ;
  570.      ;* Check if non-default buffer is to be used.
  571.      ;
  572.      AND    BX,BX                ;CHECK IF NON-DEFAULT BUFFER?
  573.      JZ    SINIT4                ;JUMP IF NOT
  574.      ;
  575.      CMP    BX,MINBSIZ            ;ERROR IF SIZE < MIN
  576.      JB    SINIT1
  577.      ;
  578.      CMP    BX,MAXBSIZ            ;ERROR IF SIZE > MAX
  579.      JBE    SINIT2                ;JUMP IF OK
  580.      ;
  581. SINIT1:     MOV    DX,CPLBERR            ;ERROR CODE
  582.      JMP    SINITX                ;RETURN
  583.      ;
  584. SINIT2:     CMP    CX,10H                ;MAKE OFFSET < 1 PARAGRAPH
  585.      JB    SINIT3
  586.      ;
  587.      SUB    CX,10H                ;STEP OFFSET DOWN
  588.      INC    DX                ;AND SEGMENT UP
  589.      JMP    SINIT2                ;LOOP UNTIL DONE
  590.      ;
  591. SINIT3:     MOV    WORD PTR BFSIZ,BX        ;INSTALL NEW VALUES
  592.      MOV    WORD PTR BFADR,CX        ;ADDRESS OFFSET
  593.      MOV    WORD PTR BFADR+2,DX        ;AND SEGMENT
  594.      ;
  595.      ;* Read any data there is in the MPU401 (max 5000 bytes).
  596.      ;
  597. SINIT4:     MOV    CX,5000                ;MAX 5000 BYTES
  598.      ;
  599. SINIT5:     MOV    DX,WORD PTR CTLPORT        ;STATUS PORT
  600.      IN    AL,DX                ;GET STATUS
  601.      AND    AL,DSRBIT            ;SEE IF DATA THERE?
  602.      JNZ    SINIT7                ;JUMP IF NOT
  603.      ;
  604.      DEC    DX                ;DATA PORT
  605.      IN    AL,DX                ;DO A READ
  606.      INC    DX                ;CTRL PORT
  607.      MOV    AL,200
  608. SINIT6:     DEC    AL                ;SHORT DELAY LOOP
  609.      JNZ    SINIT6
  610.      ;
  611.      DEC    CX                ;DOWN COUNT
  612.      JNZ    SINIT5                ;LOOP IF NOT COUNTED OUT
  613.      ;
  614.      MOV    DX,CPLMOFL            ;TO MUCH DATA ERROR CODE
  615.      JMP    SINITX
  616.      ;
  617.      ;* MPU401 buffer empty. Await MPU401 ready for command.
  618.      ;
  619. SINIT7:     MOV    CX,500                ;TIMEOUT FOR DRR LOW
  620.      ;
  621. SINIT8:     IN    AL,DX                ;WAIT FOR DRR LOW
  622.      AND    AL,DRRBIT            ;CHECK IT
  623.      JZ    SINIT9
  624.      ;
  625.      DEC    CX                ;COUNT DOWN
  626.      JNZ    SINIT8
  627.      ;
  628.      ;* Timeout, MPU401 never gets ready for command. Error return.
  629.      ;
  630.      JMP    SHORT SINIT12
  631.      ;
  632.      ;* MPU401 ready for command. Reset it to known initial state.
  633.      ;
  634. SINIT9:     MOV    AL,MPURES            ;MIDI RESET CODE
  635.      OUT    DX,AL                ;DO IT
  636.      ;
  637.      MOV    CX,WORD PTR MPUDELY        ;TIMEOUT FOR RESPONSE
  638.      ;
  639. SINIT10: IN    AL,DX                ;GET STATUS
  640.      AND    AL,DSRBIT            ;SEE IF DATA THERE?
  641.      JZ    SINIT11                ;THEN JUMP
  642.      ;
  643.      DEC    CX                ;TIME OUT YET?
  644.      JNZ    SINIT10                ;TRY AGAIN IF NOT
  645.      ;
  646.      ;* Timeout, no acknowledge from MPU401. Error return.
  647.      ;
  648.      MOV    DX,CPLTERR            ;TIMEOUT ERROR
  649.      JMP    SINITX
  650.      ;
  651.      ;* MPU401 replied with a byte. Check it's ACK.
  652.      ;
  653. SINIT11: DEC    DX                ;DATA PORT
  654.      IN    AL,DX
  655.      CMP    AL,MPUACK            ;SHOULD BE 'ACKNOWLEDGE'
  656.      JZ    SINIT13                ;JUMP IF OK
  657.      ;
  658. SINIT12: MOV    DX,CPLPRTE            ;PROTOCOL ERROR
  659.      JMP    SINITX
  660.      ;
  661.      ;* Coming here, the MPU401 is present, and responds correctly.
  662.      ;* Now initialize some more values.
  663.      ;
  664. SINIT13: MOV    WORD PTR STATREC,8400H        ;DRIVER ACTIVE, NO ERROR/DATA
  665.      MOV    AX,OFFSET FNCTABA        ;ACTIVE DRIVER JUMP TABLE
  666.      MOV    WORD PTR FNCTAB,AX        ;USE IT IN FUTURE
  667.      ;
  668.      ;* Save original hardware interrupt vector and replace it
  669.      ;* by our own.
  670.      ;
  671.      PUSH    DS
  672.      XOR    AX,AX                ;CLEAR CS
  673.      MOV    DS,AX                ;FOR ACCESS TO SEGM 0
  674.      MOV    DI,CS:WORD PTR HWVECAD        ;VECTOR LOCATION
  675.      CLI                    ;SHUT OFF DOING THIS
  676.      MOV    AX,WORD PTR [DI]        ;GET ORIGINAL VECTOR OFFS
  677.      MOV    CS:WORD PTR OHWVEC,AX        ;SAVE IT
  678.      MOV    AX,WORD PTR 2[DI]        ;GET SEGMENT
  679.      MOV    CS:WORD PTR OHWVEC+2,AX        ;SAVE IT
  680.      MOV    WORD PTR [DI],OFFSET HWIHNL    ;INSTALL OUR OFFSET
  681.      MOV    WORD PTR 2[DI],CS        ;INSTALL OUR SEGMENT
  682.      STI                    ;ALLOW INT BACK ON
  683.      POP    DS                ;RECOVER DS (=CS)
  684.      ;
  685.      ;* Give a (probably unnessecary) EOI to interrupt controller.
  686.      ;
  687.      MOV    DX,WORD PTR EOIPORT        ;PORT TO COMMAND EOI TO
  688.      MOV    AL,EOICMD            ;CLEAR IN-SERVICE FLIP-FLOP
  689.      OUT    DX,AL                ;IN INT CONTROLLER
  690.      ;
  691.      ;* Enable hardware interrupts in 8259.
  692.      ;
  693.      MOV    DX,WORD PTR ENAPORT        ;ENABLE MASK PORT
  694.      IN    AL,DX                ;GET CURRENT SETTINGS
  695.      MOV    BYTE PTR ORGENA,AL        ;KEEP ORIGINAL MASK
  696.      AND    AL,BYTE PTR ENAMASK        ;CLEAR BIT TO ENABLE
  697.      OUT    DX,AL                ;RESTORE TO CONTROLLER
  698.      ;
  699.      ;* All is well, return OK.
  700.      ;
  701.      MOV    DX,CPLOK            ;OK RETURN
  702.      ;
  703. SINITX:     POP    CX                ;RECOVER REGS
  704.      POP    DS
  705.      POP    DI
  706.      IRET
  707.      ;
  708.      SUBTTL    Soft Interrupt Finit
  709.      %OUT    Soft Interrupt Finit
  710.      PAGE
  711.      ;
  712.      ;***************************************************************
  713.      ;* 1:  SFINI                            *
  714.      ;*                                *
  715.      ;* If driver is already inactive, simply returns 'OK' result.    *
  716.      ;* Otherwise, things are set back to the state when it was    *
  717.      ;* initialized. Extensive tests are made that everything works    *
  718.      ;* properly. If some problem is present, the finitialization    *
  719.      ;* not performed and an error is reported in DX. Otherwies, DX    *
  720.      ;* returns CPLOK.                        *
  721.      ;***************************************************************
  722.      ;
  723.      ;* Finitialize inactive driver (dummy operation).
  724.      ;
  725. SFINII:     MOV    DX,CPLOK            ;OK COMPLETION CODE
  726.      JMP    SHORT SFINI0            ;AND RETURN
  727.      ;
  728.      ;* Finitialize active driver (normal finitialization).
  729.      ;
  730. SFINIA:     CALL    FINITIT                ;DO THE JOB
  731. SFINI0:     POP    DS                ;RECOVER REGS
  732.      POP    DI
  733.      IRET
  734.      ;
  735.      ;***************************************************************
  736.      ;* FINITIT                            *
  737.      ;* This one does the real finit work for FINIT routines. Also    *
  738.      ;* called by SINITA.                        *
  739.      ;***************************************************************
  740.      ;
  741. FINITIT     proc near
  742.      ;
  743.      ;* Reset the MPU401, taking all precautions.
  744.      ;
  745.      MOV    DX,WORD PTR ENAPORT        ;ENABLE MASK PORT
  746.      IN    AL,DX                ;GET CURRENT SETTINGS
  747.      OR    AL,BYTE PTR DISMASK        ;DISABLE INTERRUPTS
  748.      OUT    DX,AL                ;RESTORE TO CONTROLLER
  749.      ;
  750.      ;* Read any data there is in the MPU401 (max 200 bytes).
  751.      ;
  752.      MOV    DX,WORD PTR CTLPORT        ;CONTROL PORT ADDRESS
  753.      PUSH    CX                ;SAVE REG USED AS DOWN-COUNTER
  754.      MOV    CX,200                ;MAX 200 BYTE
  755. FINITI1: IN    AL,DX                ;GET STATUS
  756.      AND    AL,DSRBIT            ;SEE IF DATA EMPTY
  757.      JNZ    FINITI3                ;THEN JUMP
  758.      ;
  759.      DEC    CX                ;ADJUST TIMEOUT
  760.      JZ    FINITI2                ;ERROR IF TIMEOUT
  761.      ;
  762.      DEC    DX                ;DATA PORT
  763.      IN    AL,DX                ;READ A BYTE
  764.      INC    DX                ;CTRL PORT
  765.      JMP    FINITI1                ;LOOP AGAIN
  766.      ;
  767. FINITI2: POP    CX
  768.      MOV    DX,CPLMOFL            ;MPU DATA OFLO ERROR CODE
  769.      RET
  770.      ;
  771.      ;* MPU401 buffer empty. Await MPU401 ready for command.
  772.      ;
  773. FINITI3: MOV    CX,WORD PTR MPUDELY        ;TIMEOUT COUNT
  774. FINITI4: IN    AL,DX                ;GET STATUSS
  775.      AND    AL,DRRBIT            ;MPU READY FOR COMMAND
  776.      JZ    FINITI5                ;JUMP IF SO
  777.      ;
  778.      DEC    CX                ;BUMP TIMEOUT
  779.      JNZ    FINITI4                ;LOOP IF NOT
  780.      ;
  781.      JMP    SHORT FINITI7            ;TIMEOUT ERROR
  782.      ;
  783.      ;* MPU401 ready for command. Reset it to known final state.
  784.      ;
  785. FINITI5: MOV    AL,MPURES            ;RESET COMMAND
  786.      OUT    DX,AL
  787.      ;
  788.      MOV    CX,WORD PTR MPUDELY        ;TIMEOUT DELAY
  789. FINITI6: IN    AL,DX                ;GET STATUS
  790.      AND    AL,DSRBIT            ;SEE IF DATA THERE
  791.      JZ    FINITI8                ;THEN JUMP
  792.      ;
  793.      DEC    CX                ;BUMP TIMEOUT
  794.      JNZ    FINITI6                ;LOOP IF NON-0
  795.      ;
  796. FINITI7: POP    CX
  797.      MOV    DX,CPLTERR            ;TIMEOUT ERROR
  798.      RET
  799.      ;
  800.      ;* MPU401 replied with a byte. Check it's ACK.
  801.      ;
  802. FINITI8: DEC    DX                ;DATA PORT
  803.      IN    AL,DX                ;GET BYTE
  804.      CMP    AL,MPUACK            ;SHOULD BE ACKNOWLEDGE
  805.      JZ    FINITI9                ;JUMP IF OK
  806.      ;
  807.      POP    CX                ;RECOVE REG
  808.      MOV    DX,CPLPRTE            ;PROTOCOL ERROR
  809.      RET
  810.      ;
  811. FINITI9: POP    CX                ;RECOVER REG
  812.      ;
  813.      ;* MPU401 properly reset. Now restore hardware int enable
  814.      ;* status in 8259.
  815.      ;
  816.      MOV    AH,BYTE PTR ORGENA        ;GET ORIGINAL SETTING
  817.      AND    AH,BYTE PTR DISMASK        ;ISOLATE OUR BIT
  818.      MOV    DX,WORD PTR ENAPORT        ;ENABLE MASK PORT
  819.      IN    AL,DX                ;GET CURRENT SETTINGS
  820.      AND    AL,BYTE PTR ENAMASK        ;REMOVE OUR BIT
  821.      OR    AL,AH                ;INSERT IT
  822.      OUT    DX,AL                ;RESTORE TO CONTROLLER
  823.      ;
  824.      ;* Restore original hardware interrupt vector to segment 0.
  825.      ;
  826.      PUSH    DS
  827.      XOR    AX,AX                ;ACCESS SEGMENT 0
  828.      MOV    DS,AX
  829.      MOV    DI,CS:WORD PTR HWVECAD        ;VECTOR LOCATION
  830.      MOV    AX,CS:WORD PTR OHWVEC        ;GET OFFSET
  831.      CLI                    ;SHUT OFF DOING THIS
  832.      MOV    WORD PTR [DI],AX        ;RESTORE IT
  833.      MOV    AX,CS:WORD PTR OHWVEC+2        ;GET SEGMENT
  834.      MOV    WORD PTR 2[DI],AX
  835.      STI
  836.      POP    DS
  837.      ;
  838.      ;* Now finitialize some values and return.
  839.      ;
  840.      MOV    AX,OFFSET FNCTABI        ;INACTIVE DRIVER JUMP TABLE
  841.      MOV    WORD PTR FNCTAB,AX        ;USE IT IN FUTURE
  842.      MOV    WORD PTR STATREC,0        ;DRIVER INACTIVE
  843.      ;
  844.      MOV    DX,CPLOK
  845.      RET
  846.      ;
  847. FINITIT     endp
  848.      ;
  849.      SUBTTL    Soft Interrupt Driver Status
  850.      %OUT    Soft Interrupt Driver Status
  851.      PAGE
  852.      ;
  853.      ;***************************************************************
  854.      ;* 2:    SSTAT                            *
  855.      ;*                                *
  856.      ;* Get driver status. Returns status in AX as follows:        *
  857.      ;*                                *
  858.      ;* Bit        Flags                        *
  859.      ;*                                *
  860.      ;* 0    Overflow has occured in the data buffer.        *
  861.      ;* 1    Overflow has occured in the exclusive buffer.        *
  862.      ;* 2    Timeout error has occured in the MPU401 protocol    *
  863.      ;* 3    A protocol error has been found in the input from the    *
  864.      ;*    MPU401 - unexpected bytes arrived. (Note that this    *
  865.      ;*    driver currently does not support all possible valid    *
  866.      ;*    data sequences).                    *
  867.      ;* 8    Unread data in data buffer.                *
  868.      ;* 9    Unread data in exclusive buffer.            *
  869.      ;* 10    Last MPU command code has been acknowledged.        *
  870.      ;* 14    The machine is not a PC-XT, but an AT.            *
  871.      ;* 15    Driver is in the active state.                *
  872.      ;*                                *
  873.      ;* SSTAT does not return any error status. DX returns two    *
  874.      ;* bytes of error data (the two last bytes read from the    *
  875.      ;* MPU401). DX is only relevant if a protocol error has    *
  876.      ;* occured (indicated by bit 3 of AX).                *
  877.      ;***************************************************************
  878.      ;
  879.      ;* Include AT bit by special test.
  880.      ;
  881. SSTAT:     MOV    AL,BYTE PTR ATFLAG        ;SEE IF WE ARE AN AT
  882.      AND    AX,0FFH
  883.      JZ    SSTAT1
  884.      ;
  885.      MOV    AX,4000H            ;SET AT BIT
  886.      ;
  887. SSTAT1:     OR    AX,WORD PTR STATREC        ;ADD OTHER BITS
  888.      POP    DS                ;RECOVER REGS
  889.      POP    DI
  890.      IRET
  891.      ;
  892.      SUBTTL    Soft Interrupt Error Reset
  893.      %OUT    Soft Interrupt Error Reset
  894.      PAGE
  895.      ;
  896.      ;***************************************************************
  897.      ;* 2:    SRERR                            *
  898.      ;*                                *
  899.      ;* Reset all error flags.                    *
  900.      ;* SRERR does not return any error status. DX is not set.    *
  901.      ;***************************************************************
  902.      ;
  903. SRERR:     MOV    BYTE PTR STATREC,0        ;CLEAR ERROR HALF OF STATREC
  904.      POP    DS                ;RECOVER REGS
  905.      POP    DI
  906.      IRET
  907.      ;
  908.      SUBTTL    Soft Interrupt Set Timer
  909.      %OUT    Soft Interrupt Set Timer
  910.      PAGE
  911.      ;
  912.      ;***************************************************************
  913.      ;* 4:  SSTIM    Set timer                    *
  914.      ;*                                *
  915.      ;* The internal timer is initialized with the value in DX and    *
  916.      ;* CX. DX holds the high order word.                 *
  917.      ;* SSTIM does not return any error status. DX is not set.    *
  918.      ;***************************************************************
  919.      ;
  920. SSTIM:     ADD    CX,CX                ;SHIFT UP INPUT VALUE 2 BITS
  921.      ADC    DX,DX
  922.      ADD    CX,CX
  923.      ADC    DX,DX
  924.      CLI                    ;DON'T CHANGE BETWEEN WRITES
  925.      MOV    WORD PTR TIMER,CX        ;SET LOW WORD
  926.      MOV    WORD PTR TIMER+2,DX        ;SET HIGH WORD
  927.      STI                    ;ALLOW CHANGES
  928.      POP    DS                ;RECOVER REGS
  929.      POP    DI
  930.      IRET
  931.      ;
  932.      ;
  933.      SUBTTL    Soft Interrupt Read Timer
  934.      %OUT    Soft Interrupt Read Timer
  935.      PAGE
  936.      ;
  937.      ;***************************************************************
  938.      ;* 5:  SRTIM    Read timer                    *
  939.      ;*                                *
  940.      ;* The internal timer is read (if neccessary MPU401 time is    *
  941.      ;* requested and included), and the value is returned in DX    *
  942.      ;* and AX. DX holds the high order word. Note that this func-    *
  943.      ;* tion does not return any error status, but DX will hold the    *
  944.      ;* high order word of the return value. If an error should    *
  945.      ;* occur, the only way to check it is by reading the status    *
  946.      ;* record (with SSTAT) and check the error flags.        *
  947.      ;***************************************************************
  948.      ;
  949.      ;* Entry for active driver - requst fraction from MPU401
  950.      ;
  951. SRTIMA:     MOV    DL,MPUGRCT            ;GET MPU RECORD COUNTER
  952.      CALL    PUTCMD                ;SEND THE COMMAND
  953.      ;
  954.      ;* Entry for inactive driver
  955.      ;
  956. SRTIMI:     MOV    DX,WORD PTR MPUDELY        ;TIMEOUT DELAY
  957.      ;
  958. SRTIM1:     TEST    BYTE PTR STATREC+1,4        ;LAST CMD ACK'ED?
  959.      JNZ    SRTIM2
  960.      ;
  961.      DEC    DX                ;BUMP TIMEOUT
  962.      JNZ    SRTIM1                ;LOOP IF NO TIMEOUT
  963.      ;
  964.      OR    BYTE PTR STATREC,4        ;SET TIMEOUT ERROR BIT
  965.      ;
  966. SRTIM2:     CLI                    ;DON'T CHANGE BETWEEN READS
  967.      MOV    AX,WORD PTR TIMER        ;READ LOW WORD
  968.      MOV    DX,WORD PTR TIMER+2        ;READ HIGH WORD
  969.      STI                    ;ALLOW CHANGES
  970.      RCR    DX,1                ;SHIFT RESULT RIGHT 2
  971.      RCR    AX,1
  972.      RCR    DX,1
  973.      RCR    AX,1
  974.      AND    DH,3FH                ;CLEAR SHIFTED GARBAGE
  975.      POP    DS                ;RECOVER REGSS
  976.      POP    DI
  977.      IRET
  978.      ;
  979.      SUBTTL    Soft Interrupt Flush Buffer
  980.      %OUT    Soft Interrupt Flush Buffer
  981.      PAGE
  982.      ;
  983.      ;***************************************************************
  984.      ;* 6:  SFBUF    Flush Buffer                    *
  985.      ;*                                *
  986.      ;* Flush input buffer. No error possible, DX not set.        *
  987.      ;***************************************************************
  988.      ;
  989. SFBUF:     XOR    AX,AX                ;CLEAR TO 0
  990.      CLI                    ;NO INT WHILE WORKING
  991.      MOV    WORD PTR BFPUT,AX        ;CLEAR BUFFER POINTERS
  992.      MOV    WORD PTR BFPIC,AX
  993.      MOV    WORD PTR NENT,AX        ;CLEAR BYTE COUNT
  994.      AND    BYTE PTR STATREC+1,0FEH        ;CLEAR DATA FLAG
  995.      STI
  996.      POP    DS                ;RECOVER REGS
  997.      POP    DI
  998.      IRET
  999.      ;
  1000.      SUBTTL    Soft Interrupt Read Buffer
  1001.      %OUT    Soft Interrupt Read Buffer
  1002.      PAGE
  1003.      ;
  1004.      ;***************************************************************
  1005.      ;* 7:  SRBUF    Read Buffer                    *
  1006.      ;*                                *
  1007.      ;* If the driver is inactive, nothing is done and an error is    *
  1008.      ;* returned. If driver is active, waits for a data byte in    *
  1009.      ;* the buffer and then returns it. DX is CPLOK in this case.    *
  1010.      ;***************************************************************
  1011.      ;
  1012.      ;* Read inactive driver is an error
  1013.      ;
  1014. SRBUFI:     MOV    DX,CPLPASS            ;ERROR CODE
  1015.      XOR    AX,AX                ;CLEAR RETURN VALUE
  1016.      POP    DS                ;RECOVER REGS
  1017.      POP    DI
  1018.      IRET
  1019.      ;
  1020.      ;* Read active driver (normal entry)
  1021.      ;
  1022. SRBUFA:     TEST    BYTE PTR STATREC+1,1        ;TEST IF DATA THERE
  1023.      JZ    SRBUFA
  1024.      ;
  1025.      MOV    DX,WORD PTR BFADR        ;GET BUFFER OFFSET
  1026.      MOV    DI,WORD PTR BFPIC        ;GET PICK POINTER
  1027.      ADD    DX,DI                ;GET BUFFER POS
  1028.      XCHG    DI,DX                ;PUT IN INDEX REG
  1029.      PUSH    DS
  1030.      MOV    DS,WORD PTR BFADR+2        ;GET BUFFER SEGMENT
  1031.      MOV    AL,[DI]                ;GET BUFFER DATA BYTE
  1032.      POP    DS
  1033.      ;
  1034.      INC    DX                ;NEXT BUF POS
  1035.      CMP    DX,WORD PTR BFSIZ        ;CHECK IF WRAP AROUND
  1036.      JB    SRBUFA1                ;JUMP IF NOT
  1037.      ;
  1038.      XOR    DX,DX
  1039.      ;
  1040. SRBUFA1: MOV    WORD PTR BFPIC,DX        ;UPDATE POINTER
  1041.      CLI                    ;NO INTS NOW
  1042.      DEC    WORD PTR NENT            ;ADJUST BUFFER CONTENTS
  1043.      JG    SRBUFA2                ;IF STILL MORE
  1044.      ;
  1045.      AND    BYTE PTR STATREC+1,0FEH        ;CLEAR DATA FLAG
  1046.      ;
  1047. SRBUFA2: STI                    ;ALLOW IT NOW
  1048.      XOR    AH,AH                ;CLEAR HIGH RETURN VALUE
  1049.      MOV    DX,CPLOK            ;RETURN CODE
  1050.      POP    DS                ;RECOVER REGS
  1051.      POP    DI
  1052.      IRET
  1053.      ;
  1054.      SUBTTL    Soft Interrupt Test Buffer
  1055.      %OUT    Soft Interrupt Test Buffer
  1056.      PAGE
  1057.      ;
  1058.      ;***************************************************************
  1059.      ;* 8:  STBUF    Test Buffer                    *
  1060.      ;*                                *
  1061.      ;* If the driver is inactive, nothing is done and an error is    *
  1062.      ;* returned. If driver is active, returns 0 in AX if there is    *
  1063.      ;* no data in the buffer waitung to be read. Returns 1 if data    *
  1064.      ;* is available. DX set to CPLOK in this case.            *
  1065.      ;***************************************************************
  1066.      ;
  1067.      ;* Test for inactive driver is an error
  1068.      ;
  1069. STBUFI:     MOV    DX,CPLPASS            ;ERROR CODE
  1070.      MOV    AX,1                ;PRETEND BYTE TO AVOID HANGUP
  1071.      POP    DS                ;RECOVER REGS
  1072.      POP    DI
  1073.      IRET
  1074.      ;
  1075.      ;* Test for activer driver (normal entry)
  1076.      ;
  1077. STBUFA:     XOR    AX,AX                ;ASSUME NO DATA
  1078.      TEST    BYTE PTR STATREC+1,1        ;SEE IF DATA
  1079.      JZ    STBUFA1                ;JUMP IF NOT
  1080.      ;
  1081.      INC    AX                ;SET IT TO 1
  1082.      ;
  1083. STBUFA1: MOV    DX,CPLOK            ;OK RETURN
  1084.      POP    DS                ;RECOVER REGS
  1085.      POP    DI
  1086.      IRET
  1087.      ;
  1088.      SUBTTL    Soft Interrupt Define Exclusive Buffer
  1089.      %OUT    Soft Interrupt Define Exclusive Buffer
  1090.      PAGE
  1091.      ;
  1092.      ;***************************************************************
  1093.      ;* 9:  SDXBF    Define exclusive buffer                *
  1094.      ;*                                *
  1095.      ;* If the driver is inactive, an error is returned. If the    *
  1096.      ;* driver is active, and if if BX is non-zero, then DX:CX is    *
  1097.      ;* the address of a non-default exclusive buffer, and BX is    *
  1098.      ;* the size of it. If BX is 0, the exclusive buffer is un-    *
  1099.      ;* defined, i.e. exclusive data is unbuffered. This is also    *
  1100.      ;* the default after the driver is initialized. The buffer    *
  1101.      ;* size must be >= 512, and <= 32768.                *
  1102.      ;***************************************************************
  1103.      ;
  1104.      ;* Refuse if driver inactive
  1105.      ;
  1106. SDXBFI:     MOV    DX,CPLPASS            ;ERROR RETURN CODE
  1107.      POP    DS                ;RECOVER REGS
  1108.      POP    DI
  1109.      IRET
  1110.      ;
  1111.      ;* Entry for active driver (normal case)
  1112.      ;
  1113. SDXBFA:  OR    BX,BX                ;CHECK IF UNDEF. BUFFER?
  1114.      JZ    SDXBF2
  1115.      ;
  1116.      CMP    BX,MINBSIZ            ;CHECK IF BIG ENOUGH?
  1117.      JB    SDXBF1                ;JUMP IF NOT
  1118.      ;
  1119.      CMP    BX,MAXBSIZ            ;CHECK IF TOO BIG?
  1120.      JBE    SDXBF2                ;JUMP IF OK
  1121.      ;
  1122. SDXBF1:     MOV    DX,CPLBERR            ;BAD BUFFER CPL CODE
  1123.      POP    DS
  1124.      POP    DI
  1125.      IRET                    ;ERROR RETURN
  1126.      ;
  1127. SDXBF2:     MOV    WORD PTR XBFADR,CX        ;INSTALL OFFSET
  1128.      MOV    WORD PTR XBFADR+2,DX        ;INSTALL EGMENT
  1129.      MOV    DX,CPLOK            ;OK COMPLETION CODE
  1130.      ;
  1131.      XOR    AX,AX                ;FOR CLEARING THINGS
  1132.      CLI                    ;NO INT WHILE SETTING
  1133.      MOV    WORD PTR XBFSIZ,BX        ;INSTALL SIZE
  1134.      MOV    WORD PTR XBFPUT,AX        ;CLEAR BUFFER POINTERS
  1135.      MOV    WORD PTR XBFPIC,AX
  1136.      MOV    WORD PTR XNENT,AX        ;CLEAR BUFFER CONTENTS
  1137.      AND    WORD PTR STATREC,0FDFDH        ;CLEAR XBUF RELATED BITS
  1138.      POP    DS
  1139.      POP    DI
  1140.      IRET                    ;RESTORES INT STATUS
  1141.      ;
  1142.      SUBTTL    Soft Interrupt Flush Exclusive Buffer
  1143.      %OUT    Soft Interrupt Flush Exclusive Buffer
  1144.      PAGE
  1145.      ;
  1146.      ;***************************************************************
  1147.      ;* 10: SFXBF    Flush exclusive buffer                *
  1148.      ;*                                *
  1149.      ;* Flush exclusive buffer. No error possible.            *
  1150.      ;***************************************************************
  1151.      ;
  1152.      ;* Clear buffer pointers etc.
  1153.      ;
  1154. SFXBF:     MOV    DX,CPLOK            ;RETURN OK CODE
  1155.      POP    DS                ;RECOVER REGS
  1156.      POP    DI
  1157.      ;
  1158.       XOR    AX,AX                ;CLEAR TO 0
  1159.      CLI                    ;NO INT WHILE WORKING
  1160.      MOV    WORD PTR XBFPUT,AX        ;CLEAR BUFFER POINTERS
  1161.      MOV    WORD PTR XBFPIC,AX
  1162.      MOV    WORD PTR XNENT,AX        ;CLEAR BYTE COUNT
  1163.      AND    BYTE PTR STATREC+1,0FDH        ;CLEAR DATA FLAG
  1164.      IRET                    ;RESTORES INT STATUS
  1165.      ;
  1166.      SUBTTL    Soft Interrupt Read Exclusive Buffer
  1167.      %OUT    Soft Interrupt Read Exclusive Buffer
  1168.      PAGE
  1169.      ;
  1170.      ;***************************************************************
  1171.      ;* 11: SRXBF    Read Exclusive Buffer                *
  1172.      ;*                                *
  1173.      ;* If the driver is inactive, or the driver is active but no    *
  1174.      ;* exclusive buffer is defined, nothing is done and an error    *
  1175.      ;* is returned. If driver is active and has a buffer, waits    *
  1176.      ;* for a data byte and then returns it. DX returns CPLOK in    *
  1177.      ;* this case.                            *
  1178.      ;***************************************************************
  1179.      ;
  1180.      ;* Read inactive driver is an error
  1181.      ;
  1182. SRXBFI:     MOV    DX,CPLPASS            ;ERROR CODE
  1183.      XOR    AX,AX                ;CLEAR RETURN VALUE
  1184.      POP    DS                ;RECOVER REGS
  1185.      POP    DI
  1186.      IRET
  1187.      ;
  1188.      ;* Read from active driver (normal entry)
  1189.      ;
  1190. SRXBFA:     MOV    AX,WORD PTR XBFSIZ        ;SEE IF THERE IS A BUFFER?
  1191.      AND     AX,AX
  1192.      JNZ    SRXBFA1                ;JUMP IF A BUFFER THERE
  1193.      ;
  1194.      ;* Read non-existent buffer is an error
  1195.      ;
  1196.      MOV    DX,CPLNOXB            ;NO EXCLUSIVE BUFFER
  1197.      POP    DS
  1198.      POP    DI
  1199.      IRET                    ;ERROR RETURN
  1200.      ;
  1201. SRXBFA1: TEST    BYTE PTR STATREC+1,2        ;TEST IF DATA THERE
  1202.      JZ    SRXBFA1
  1203.      ;
  1204.      MOV    DX,WORD PTR XBFADR        ;GET BUFFER OFFSET
  1205.      MOV    DI,WORD PTR XBFPIC        ;GET PICK POINTER
  1206.      ADD    DX,DI                ;GET BUFFER POS
  1207.      XCHG    DI,DX                ;PUT IN INDEX REG
  1208.      PUSH    DS
  1209.      MOV    DS,WORD PTR XBFADR+2        ;GET BUFFER SEGMENT
  1210.      MOV    AL,[DI]                ;GET BUFFER DATA BYTE
  1211.      POP    DS
  1212.      ;
  1213.      INC    DX                ;NEXT BUF POS
  1214.      CMP    DX,WORD PTR XBFSIZ        ;CHECK IF WRAP AROUND
  1215.      JB    SRXBFA2                ;JUMP IF NOT
  1216.      ;
  1217.      XOR    DX,DX
  1218.      ;
  1219. SRXBFA2: MOV    WORD PTR XBFPIC,DX        ;UPDATE POINTER
  1220.      CLI                    ;NO INTS NOW
  1221.      DEC    WORD PTR XNENT            ;ADJUST BUFFER CONTENTS
  1222.      JG    SRXBFA3                ;IF STILL MORE
  1223.      ;
  1224.      AND    BYTE PTR STATREC+1,0FDH        ;CLEAR DATA FLAG
  1225.      ;
  1226. SRXBFA3: STI                    ;ALLOW IT NOW
  1227.      XOR    AH,AH                ;CLEAR HIGH RETURN VALUE
  1228.      MOV    DX,CPLOK            ;RETURN CODE
  1229.      POP    DS                ;RECOVER REGS
  1230.      POP    DI
  1231.      IRET
  1232.      ;
  1233.      SUBTTL    Soft Interrupt Test Exclusive Buffer
  1234.      %OUT    Soft Interrupt Test Exclusive Buffer
  1235.      PAGE
  1236.      ;
  1237.      ;***************************************************************
  1238.      ;* 12: STXBF    Test Exclusive Buffer                *
  1239.      ;*                                *
  1240.      ;* If the driver is inactive, nothing is done and an error is    *
  1241.      ;* returned. If driver is active, returns 0 in AX if there is    *
  1242.      ;* no data in the buffer waiting to be read. Returns 1 if data    *
  1243.      ;* is available. DX set to CPLOK in this case.            *
  1244.      ;***************************************************************
  1245.      ;
  1246.      ;* Test for inactive driver is an error
  1247.      ;
  1248. STXBFI:     MOV    DX,CPLPASS            ;ERROR CODE
  1249.      MOV    AX,1                ;PRETEND BYTE TO AVOID HANGUP
  1250.      POP    DS                ;RECOVER REGS
  1251.      POP    DI
  1252.      IRET
  1253.      ;
  1254.      ;* Test for active driver (normal entry)
  1255.      ;
  1256. STXBFA:     XOR    AX,AX                ;ASSUME NO DATA
  1257.      TEST    BYTE PTR STATREC+1,2        ;SEE IF DATA
  1258.      JZ    STXBFA1                ;JUMP IF NOT
  1259.      ;
  1260.      INC    AX                ;SET IT TO 1
  1261.      ;
  1262. STXBFA1: MOV    DX,CPLOK            ;OK RETURN
  1263.      POP    DS                ;RECOVER REGS
  1264.      POP    DI
  1265.      IRET
  1266.      ;
  1267.      SUBTTL    Soft Interrupt Send Command
  1268.      %OUT    Soft Interrupt Send Command
  1269.      PAGE
  1270.      ;
  1271.      ;***************************************************************
  1272.      ;* 13: SOCMD    Send a command byte                *
  1273.      ;*                                *
  1274.      ;* If the driver is inactive, nothing is done and an error is    *
  1275.      ;* returned. If driver is active, waits until the previous    *
  1276.      ;* command has finished processing. Then sends the command    *
  1277.      ;* byte (in DL). Set TIMEACK non-0 if the command requests    *
  1278.      ;* record counter contents (used by SRTIM).            *
  1279.      ;***************************************************************
  1280.      ;
  1281.      ;* Refuse to send if driver inactive.
  1282.      ;
  1283. SOCMDI:     MOV    DX,CPLPASS            ;ERROR RETURN CODE
  1284.      POP    DS                ;RECOVER REGS
  1285.      POP    DI
  1286.      IRET
  1287.      ;
  1288.      ;* Normal entry to send a command byte.
  1289.      ;
  1290. SOCMDA:     CALL    PUTCMD                ;SEND OUT THE COMMAND
  1291.      POP    DS                ;RECOVER REGS
  1292.      POP    DI
  1293.      IRET
  1294.      ;
  1295.      ;***************************************************************
  1296.      ;* PUTCMD                            *
  1297.      ;*                                *
  1298.      ;* Writes a command byte from DL to the MPU401. Returns a    *
  1299.      ;* completion code in DX. DS must be = CS when entering!    *
  1300.      ;***************************************************************
  1301.      ;
  1302. PUTCMD     proc near
  1303.      ;
  1304.      MOV    AX,WORD PTR MPUDELY        ;TIMEOUT COUNT
  1305.      ;
  1306. PUTCMD1: TEST    BYTE PTR STATREC+1,4        ;CHECK IF LAST CMD ACK'ED
  1307.      JNZ    PUTCMD2                ;JUMP IF OK
  1308.      ;
  1309.      DEC    AX                ;BUMP TIMEOUT
  1310.      JNZ    PUTCMD1                ;LOOP IF NO TIMEOUT YET
  1311.      ;
  1312.      MOV    DX,CPLTERR
  1313.      RET
  1314.      ;
  1315. PUTCMD2: PUSH    CX                ;USE AS TIMEOUT COUNTER
  1316.      MOV    AH,DL                ;INPUT DATA BYTE
  1317.      MOV    CX,WORD PTR MPUDELY        ;TIMEOUT COUNT
  1318. PUTCMD3: MOV    DX,WORD PTR CTLPORT        ;GET STATUS
  1319.      IN    AL,DX
  1320.      AND    AL,DRRBIT            ;CHECK IF READY TO RECEIVE
  1321.      JZ    PUTCMD4
  1322.      ;
  1323.      DEC    CX                ;BUMP TIMEOUT
  1324.      JNZ    PUTCMD3                ;LOOP IF TIME LEFT
  1325.      ;
  1326.      POP    CX
  1327.      MOV    DX,CPLTERR            ;TIMEOUT ERROR CODE
  1328.      RET
  1329.      ;
  1330. PUTCMD4: POP    CX                ;BALANCE STACK
  1331.      MOV    BYTE PTR ACKTIME,0        ;CLEAR TIME EXPECTATION FLAG
  1332.      CMP    AH,MPUGRCT            ;WILL WE EXPECT A DATA BYTE?
  1333.      JNZ    PUTCMD5                ;JUMP IF NOT
  1334.      ;
  1335.      MOV    BYTE PTR ACKTIME,0FFH        ;ELSE FLAG WE DO
  1336.      ;
  1337. PUTCMD5: AND    BYTE PTR STATREC+1,0FBH        ;CLEAR ACK'ED BIT
  1338.      MOV    AL,AH                ;SEND THE COMMAND
  1339.      OUT    DX,AL
  1340.      ;
  1341.      MOV    DX,CPLOK            ;ALL WELL
  1342.      RET
  1343.      ;
  1344. PUTCMD     endp
  1345.      ;
  1346.      SUBTTL    Soft Interrupt Send Data
  1347.      %OUT    Soft Interrupt Send Data
  1348.      PAGE
  1349.      ;
  1350.      ;***************************************************************
  1351.      ;* 14: SODAT    Send a data byte                *
  1352.      ;*                                *
  1353.      ;* If the driver is inactive, nothing is done and an error is    *
  1354.      ;* returned. If driver is active, sends the data byte in DL    *
  1355.      ;* to the MPU401. Returns error if output timeout.        *
  1356.      ;***************************************************************
  1357.      ;
  1358.      ;* Refuse to send if driver inactive.
  1359.      ;
  1360. SODATI:     MOV    DX,CPLPASS            ;ERROR RETURN CODE
  1361.      POP    DS                ;RECOVER REGS
  1362.      POP    DI
  1363.      IRET
  1364.      ;
  1365.      ;* Normal entry to send a data byte.
  1366.      ;
  1367. SODATA:     PUSH    CX                ;SAVE TIMEOUT REG
  1368.      MOV    CX,WORD PTR MPUDELY        ;TIMEOUT COUNT
  1369.      ;
  1370.      ;* Don't send data until last cmd has beed acknowledged
  1371.      ;
  1372. SODAT1:     TEST    BYTE PTR STATREC+1,4        ;CHECK IF LAST CMD ACK'ED
  1373.      JNZ    SODAT2                ;JUMP IF OK
  1374.      ;
  1375.      DEC    CX                ;BUMP TIMEOUT
  1376.      JNZ    SODAT1                ;LOOP IF NO TIMEOUT YET
  1377.      ;
  1378.      MOV    DX,CPLTERR            ;TIMEOUT ERROR
  1379.      JMP    SHORT SODAT5
  1380.      ;
  1381. SODAT2:     MOV    AH,DL                ;INPUT DATA
  1382.      MOV    CX,WORD PTR MPUDELY        ;TIMEOUT COUNT
  1383.      MOV    DX,WORD PTR CTLPORT        ;CONTROL PORT
  1384.      ;
  1385. SODAT3:     IN    AL,DX                ;CHECK IF READY TO RECEIVE
  1386.      AND    AL,DRRBIT
  1387.      JZ    SODAT4                ;JUMP IF SO
  1388.      ;
  1389.      DEC    CX                ;BUMP TIMEOUT COUNT
  1390.      JNZ    SODAT3                ;LOOP IF NO TIMEOUT
  1391.      ;
  1392.      MOV    DX,CPLTERR            ;TIMEOUT ERROR
  1393.      JMP    SHORT SODAT5
  1394.      ;
  1395. SODAT4:     DEC    DX                ;DATA PORT
  1396.      MOV    AL,AH                ;SEND DATA
  1397.      OUT    DX,AL
  1398.      MOV    DX,CPLOK            ;OK COMPLETION
  1399.      ;
  1400. SODAT5:     POP    CX
  1401.      POP    DS                ;RECOVER REGS
  1402.      POP    DI
  1403.      IRET
  1404.      ;
  1405. SWIHNL     endp
  1406.      ;
  1407.      SUBTTL    Hard Interrupt Handler
  1408.      %OUT    Hard Interrupt Handler
  1409.      PAGE
  1410.      ;
  1411.      ;***************************************************************
  1412.      ;* Hardware Interrupt Handler.                    *
  1413.      ;*                                *
  1414.      ;* You come here when the hardware pulls the interrupt line.    *
  1415.      ;***************************************************************
  1416.      ;
  1417. HWIHNL     proc far
  1418.      ;
  1419.      ;* First check if it was the MPU401 that interrupted.
  1420.      ;
  1421.      PUSH    AX                ;SAVE REGS ON CALLER STACK
  1422.      PUSH    DX
  1423.      MOV    DX,CS:WORD PTR CTLPORT        ;CHECK IF MPU401 INTERRUPT
  1424.      IN    AL,DX                ;BY POLLING DSRBIT
  1425.      AND    AL,DSRBIT
  1426.      JZ    OURINT                ;JUMP IF KEYBOARD INT
  1427.      ;
  1428.      ;* Interrupt, but not by the MPU401 - go to original
  1429.      ;* interrupt handler (this is abnormal practice on a PC,
  1430.      ;* where each interrupt line should be driven by only
  1431.      ;* one source, but, as we all know... )
  1432.      ;
  1433.      POP    DX                ;RECOVER REGS
  1434.      POP    AX
  1435.      DB    JFAROPC                ;JMP FAR OP-CODE
  1436. OHWVEC     DD    0                ;ORIGINAL HW VECTOR
  1437.      ;
  1438.      ;* Now set up to use own stack - we don't know how much
  1439.      ;* stack the interrupted routine provides.
  1440.      ;
  1441. OURINT:     MOV    CS:WORD PTR TMPSP,SP        ;SAVE CALLER'S SP
  1442.      MOV    CS:WORD PTR TMPSS,SS        ;AND SS
  1443.      CLI                    ;DON'T ALLOW INTERRUPT NOW!
  1444.      MOV    AX,CS
  1445.      MOV    SS,AX
  1446.      MOV    SP,OFFSET STKTOP        ;OUR OWN STACK
  1447.      STI
  1448.      PUSH    DS                ;SAVE REGS
  1449.      PUSH    CX
  1450.      PUSH    CS                ;SET DS = CS
  1451.      POP    DS
  1452.      ;
  1453.      ;* Get the interrupting byte from MPU401 and branch accordingly.
  1454.      ;
  1455. HWILOOP: MOV    DX,WORD PTR DATPORT        ;PORT TO READ
  1456.      IN    AL,DX                ;GET IT
  1457.      ;
  1458.      CMP    AL,MPUOFLO            ;TIMER OVERFLOW?
  1459.      JNZ    NOOFLO
  1460.      ;
  1461.      ;* Timer overflow, adjust our internal 32-bit timer.
  1462.      ;
  1463.      ADD    WORD PTR TIMER,OFLTIM        ;ADD OVERFLOW VALUE
  1464.      ADC    WORD PTR TIMER+2,0        ;32 BIT ADD
  1465.      JMP    GOTMSG                ;DONE
  1466.      ;
  1467.      ;* Not a timer overflow.
  1468.      ;
  1469. NOOFLO:     CMP    AL,MPUACK            ;MPU ACKNOWLEDGE?
  1470.      JNZ    NOACK
  1471.      ;
  1472.      ;* MPU command acknowledge. Flag it in STATREC, and read
  1473.      ;* any extra accompanying time byte.
  1474.      ;
  1475.      OR    BYTE PTR STATREC+1,4        ;SET ACK BIT IN STATUS RECORD
  1476.      AND    AL,BYTE PTR ACKTIME        ;IS DATA ASSOCIATED WITH IT?
  1477.      JNZ    MPUTIM                ;JUMP DISTANCE TOO FAR
  1478.      JMP    GOTMSG
  1479.      ;
  1480. MPUTIM:     CALL    GETDATA                ;GET THE DATA
  1481.      ADD    WORD PTR TIMER,AX        ;ADD TO THE TIMER ACKUMULATOR
  1482.      ADC    WORD PTR TIMER+2,0        ;32 BIT ADD
  1483.      MOV    BYTE PTR ACKTIME,0        ;CLEAR THE FLAG
  1484.      JMP    GOTMSG                ;DONE
  1485.      ;
  1486.      ;* Not an MPU401 command acknowledgement.
  1487.      ;
  1488. NOACK:     CMP    AL,MPUMAXT            ;IS IT A TIME BYTE?
  1489.      JBE    TIMEBYT                ;JUMP IF NOT
  1490.      ;
  1491.      ;* Not a leading time byte.
  1492.      ;
  1493.      CMP    AL,MPUSYSM            ;SYSTEM MESSAGE?
  1494.      JNZ    HWIERR0                ;TREAT ALL OTHERS AS ERROR
  1495.      ;
  1496.      ;* MPU401 sent a system message
  1497.      ;
  1498.      CALL    GETDATA                ;SEE WHAT SYSTEM MESSAGE
  1499.      CMP    AL,MPUMIDX            ;SEE IF MIDI EXCLUSIVE?
  1500. HWIERR0: JNZ    HWIERR                ;TREAT AS ERROR IF NOT
  1501.      ;
  1502.      ;* MPU401 reported a system exclusive message. Read it (and
  1503.      ;* store it if we have a buffer).
  1504.      ;
  1505.      PUSH    DI                ;SAVE REGS
  1506.      PUSH    BX
  1507.      MOV    DX,WORD PTR XBFSIZ        ;BUFFER SIZE
  1508.      MOV    BX,WORD PTR XBFPUT        ;PUTTER INDEX
  1509.      MOV    DI,WORD PTR XBFADR        ;BUFFER ADDRESS OFFSET
  1510.      PUSH    DS
  1511.      MOV    DS,CS:WORD PTR XBFADR+2        ;BUFFER ADDRESS SEGMENT
  1512.      ;
  1513. PUTXMSG: OR    DX,DX                ;SEE IF WE HAVE A BUFFER?
  1514.      JZ    PUTXMS2                ;JUMP IF NOT
  1515.      ;
  1516.      MOV    CH,AL                ;FOR BUFFER PUTTING
  1517.      CALL    PUTBUF                ;PUT IT IN BUFFER
  1518.      OR    CS:BYTE PTR STATREC+1,2        ;SET DATA FLAG IN STATREC
  1519.      ;
  1520. PUTXMS1: MOV    CS:WORD PTR XBFPUT,BX        ;UPDATE BUFFER INDEX
  1521.      INC    CS:WORD PTR XNENT        ;BUMP BYTES IN XBUF
  1522.      CMP    DX,CS:WORD PTR XNENT        ;SEE IF BUFFER OVERFLOW?
  1523.      JA    PUTXMS2                ;JUMP IF NOT
  1524.      ;
  1525.      DEC    CS:WORD PTR XNENT        ;CANNOT INCREASE
  1526.      OR    CS:BYTE PTR STATREC,2        ;SET BUFFER OVERFLOW BIT
  1527.      ;
  1528. PUTXMS2: TEST    AL,MPUSBIT            ;SEE IF IT WAS A STATUS BYTE
  1529.      JZ    PUTXMS3                ;THEN LOOP ON
  1530.      ;
  1531.      CMP    AL,MPUMIDX            ;CHECK IF NEW EXCLUSIVE STATUS
  1532.      JNZ    PUTXMS4                ;BREAK LOOP IF NOT
  1533.      ;
  1534. PUTXMS3: PUSH    DX                ;GETDATA CHANGES DX
  1535.      CALL    GETDATA                ;GET NEXT BYTE
  1536.      POP    DX
  1537.      JMP    PUTXMSG                ;GO STORE IT
  1538.      ;
  1539. PUTXMS4: POP    DS                ;RECOVER REGS
  1540.      POP    BX
  1541.      POP    DI
  1542.      JMP    GOTMSG
  1543.      ;
  1544.      ;* MIDI timing byte
  1545.      ;
  1546. TIMEBYT: MOV    BYTE PTR LASTTIM,AL        ;SAVE IT AS POTENTIAL ERROR
  1547.      XOR    AH,AH                ;CLEAR UPPER BYTE
  1548.      ADD    WORD PTR TIMER,AX        ;ADD TO THE TIMER ACKUMULATOR
  1549.      ADC    WORD PTR TIMER+2,0        ;32 BIT ADD
  1550.      ;
  1551.      CALL    GETDATA                ;GET NEXT BYTE
  1552.      TEST    AL,MPUSBIT            ;SEE IF RUNNING STATUS BYTE
  1553.      JNZ    RSTAT                ;JUMP IF SO
  1554.      ;
  1555.      ;* Message without running status. Use recorded status.    
  1556.      ;
  1557.      MOV    CH,BYTE PTR MIDISTT        ;USE OLD RUNNING STATUS
  1558.      JMP    SHORT GETMSG
  1559.      ;
  1560.      ;* We got time byte + something that can be either running
  1561.      ;* status or an MPU MARK
  1562.      ;
  1563. RSTAT:     CMP    AL,MPUSMAX            ;MAX RUNSTAT VALUE
  1564.      JBE    MPUSTAT                ;IT'S NO MARK, GET RUN STATUS
  1565.      ;
  1566.      ;* MIDI mark came from MPU401. Read and check it.
  1567.      ;
  1568. MPUMARK: CMP    AL,MPUNOOP            ;NOOP WE JUST IGNORE
  1569.      JZ    GOTMSG                ;OTHERS WE TREAT AS ERROR
  1570.      ;
  1571. HWIERR:     MOV    BYTE PTR LASTERR,AL        ;SAVE FOR ERROR REPORTS
  1572.      OR    BYTE PTR STATREC,8        ;SET ERROR BIT IN STATREC
  1573.      JMP    GOTMSG
  1574.      ;
  1575.      ;* We got a running status byte + message. Save status and
  1576.      ;* prepare to get the message.
  1577.      ;
  1578. MPUSTAT: MOV    BYTE PTR MIDISTT,AL        ;STORE AWAY
  1579.      MOV    CH,AL                ;KEEP IT IN BH
  1580.      CALL    GETDATA                ;AND GET NEXT BYTE
  1581.      ;
  1582.      ;* Get the message itself. We already have the first byte
  1583.      ;* in AL, and current running status in CH.
  1584.      ;
  1585. GETMSG:     MOV    CL,AL                ;FIRST BYTE TO CL
  1586.      MOV    AL,CH                ;GET STATUS TO AL
  1587.      AND    AL,ATCHMSK            ;MASK TO CHECK FOR 0CH/0DH
  1588.      SUB    AL,ATOUCH            ;SEE IF ONE OF THEM (0 IF NOT)
  1589.      JZ    PUTMSG                ;THEN GO STORE THEM
  1590.      ;
  1591.      CALL    GETDATA                ;GET 2:ND BYTE TOO
  1592.      ;
  1593.      ;* We now have status in CH, first byte in CL and (if
  1594.      ;* applicable) second byte in AL.
  1595.      ;
  1596. PUTMSG:  MOV    DX,WORD PTR NENT        ;SEE IF BUFFER FULL?
  1597.      ADD    DX,8                ;8 BYTES HEADROOM
  1598.      CMP    DX,WORD PTR BFSIZ        ;CHECK IF TOO MUCH?
  1599.      JB    PUTMSG1                ;JUMP IF ROOM IN BUFFER
  1600.      ;
  1601.      OR    BYTE PTR STATREC,1        ;SET STATREC BUFFER OFLOW BIT
  1602.      JMP    SHORT GOTMSG
  1603.      ;
  1604.      ;* Put the data bytes in the buffer.
  1605.      ;
  1606. PUTMSG1: PUSH    DI                ;SAVE REGS
  1607.      PUSH    BX
  1608.      MOV    DX,WORD PTR BFSIZ        ;BUFFER SIZE
  1609.      MOV    BX,WORD PTR BFPUT        ;PUTTER INDEX
  1610.      MOV    DI,WORD PTR BFADR        ;BUFFER ADDRESS OFFSET
  1611.      PUSH    DS
  1612.      MOV    DS,CS:WORD PTR BFADR+2        ;BUFFER ADDRESS SEGMENT
  1613.      ;
  1614.      CALL    PUTBUF                ;PUT IT IN BUFFER
  1615.      MOV    CH,CL                ;FIRST BYTE
  1616.      CALL    PUTBUF                ;PUT IT IN BUFFER
  1617.      MOV    CH,AL                ;SECOND BYTE
  1618.      CALL    PUTBUF                ;PUT IT IN BUFFER
  1619.      XOR    CH,CH                ;A DUMMY BYTE
  1620.      CALL    PUTBUF                ;PUT IT IN BUFFER
  1621.      POP    DS                ;RECOVER REGISTERS
  1622.      MOV    WORD PTR BFPUT,BX        ;UPDATE BUFFER INDEX
  1623.      POP    BX
  1624.      POP    DI
  1625.      OR    BYTE PTR STATREC+1,1        ;SET DATA FLAG IN STATREC
  1626.      ADD    WORD PTR NENT,4            ;UP BUFFER OCCUPANCY
  1627.      ;
  1628.      ;* MPU Message processed. Check if more data there.
  1629.      ;
  1630. GOTMSG:     MOV    DX,WORD PTR CTLPORT        ;SEE IF ANOTHER MESSAGE?
  1631.      IN    AL,DX
  1632.      AND    AL,DSRBIT            ;CHECK STATUS
  1633.      JNZ    HWIEXIT                ;IF NOTHING THERE, EXIT
  1634.      JMP    HWILOOP                ;ELSE GO GET IT
  1635.      ;
  1636.      ;* Done, Make an EOI, restore the interrupted routine's
  1637.      ;* stack and return to it.
  1638.      ;
  1639. HWIEXIT: MOV    DX,WORD PTR EOIPORT        ;INT CONTROLLER CMD PORT
  1640.      MOV    AL,EOICMD
  1641.      POP    CX                ;RESTORE REGS
  1642.      POP    DS
  1643.      CLI
  1644.      MOV    SP,CS:WORD PTR TMPSP        ;RESTORE CALLER'S SP
  1645.      MOV    SS,CS:WORD PTR TMPSS        ;AND SS
  1646.      OUT    DX,AL                ;SEND THE EOI COMMAND
  1647.      POP    DX                ;RECOVER FROM CALLER STACK
  1648.      POP    AX
  1649.      IRET                    ;RESTORES INITIAL EI STATUS
  1650.      ;
  1651. HWIHNL     endp
  1652.      ;
  1653.      ;***************************************************************
  1654.      ;* GETDATA                            *
  1655.      ;*                                *
  1656.      ;* Read a byte from the data port. Returns the value in AX.    *
  1657.      ;* Sets the timeout error bit in STATREC if no data before    *
  1658.      ;* time-out. If time-out, returns FAKEDAT in AX.        *
  1659.      ;***************************************************************
  1660.      ;
  1661. GETDATA     proc near
  1662.      ;
  1663.      PUSH    CX                ;SAVE REG
  1664.      MOV    CX,CS:WORD PTR MPUDELY        ;TIMEOUT VALUE
  1665.      MOV    DX,CS:WORD PTR CTLPORT
  1666.      ;
  1667. GETDAT1: IN    AL,DX
  1668.      AND    AX,DSRBIT            ;CHECK IF DATA THERE
  1669.      JZ    GETDAT2                ;THEN JUMP
  1670.      ;
  1671.      DEC    CX                ;BUMP TIMEOUT
  1672.      JNZ    GETDAT1                ;LOOP IF NOT TIMED OUT
  1673.      ;
  1674.      POP    CX                ;RECOVER REG
  1675.      OR    CS:BYTE PTR STATREC,4        ;SET TIMEOUT ERROR BIT
  1676.      MOV    AX,FAKEDAT            ;FAKED RETURN DATA
  1677.      RET
  1678.      ;
  1679. GETDAT2: POP    CX                ;RECOVER REG
  1680.      DEC    DX                ;DATA PORT
  1681.      IN    AL,DX                ;GET DATA (AH=0)
  1682.      RET
  1683.      ;
  1684. GETDATA     endp
  1685.      ;
  1686.      ;***************************************************************
  1687.      ;* PUTBUF                            *
  1688.      ;*                                *
  1689.      ;* Put CH at the location addressed by DS:BX[DI], and adjust    *
  1690.      ;* BX properly thereafter. DX holds the buffer size. It is    *
  1691.      ;* assumed that any buffer overflow checks have already been    *
  1692.      ;* made.                            *
  1693.      ;***************************************************************
  1694.      ;
  1695. PUTBUF     proc near
  1696.      ;
  1697. ;* !!! Does not this basic addressing mode exist on the 8088/86???!!:
  1698. ;*     MOV    DS:BYTE PTR BX[DI],CH        ;STORE THE BYTE
  1699. ;* !!! Trivial on 68K [MOVE.B 0(Dn,An),Dm] - This makes me upset!!!
  1700. ;* !!! I'm sure you can do it, but maybe not with any regs?
  1701.      ;
  1702.      PUSH    DI                ;SAVE DI
  1703.      XCHG    BX,DI                ;SAVE BX
  1704.      ADD    BX,DI                ;TOTAL OFFSET INTO SEGMENT
  1705.      XCHG    BX,DI                ;RECOVER BX
  1706.      MOV    DS:[DI],CH                ;PUT IT DOWN
  1707.      POP    DI                ;RECOVER DI
  1708.      ;
  1709.      INC    BX                ;NEXT BUFFER POS.
  1710.      CMP    BX,DX                ;CHECK IF BEYOND BUFFER
  1711.      JB    PUTBUF1
  1712.      ;
  1713.      XOR    BX,BX                ;WRAP TO BUFFER START
  1714.      ;
  1715. PUTBUF1: RET
  1716.      ;
  1717. PUTBUF     endp
  1718.      ;
  1719.      SUBTTL    Driver Init Code / Buffer Area
  1720.      %OUT    Driver Init Code / Buffer Area
  1721.      PAGE
  1722.      ;
  1723.      ;***************************************************************
  1724.      ;* INIT (0)                            *
  1725.      ;*                                *
  1726.      ;* This code performs the INIT request, done only once when    *
  1727.      ;* starting the operating system. This code will be overwrit-    *
  1728.      ;* ten by the MIDI buffer later. Therefore it is located at    *
  1729.      ;* the end of the driver.                    *
  1730.      ;***************************************************************
  1731.      ;
  1732. DBUF     EQU    $                ;START OF MIDI BUFFER
  1733.      ;
  1734. INIT     proc near
  1735.      ;
  1736.      ;* First of all, determine if we are an XT or an AT.
  1737.      ;* Set MPU401 delay accordingly.
  1738.      ;
  1739.      MOV    WORD PTR MPUDELY,2000H        ;TIME FOR AN XT
  1740.      MOV    CX,SP                ;SAVE CURRENT SP
  1741.      PUSH    SP                ;PUSH IT
  1742.      POP    AX                ;GET THE VALUE WE JUST PUSHED
  1743.      CMP    AX,CX                ;IS IT SAME AS BEFORE PUSH?
  1744.      JNE    INITXT                ;NO, WE ARE AN 8086/88
  1745.      ;
  1746.      MOV    CS:BYTE PTR ATFLAG,1        ;80286, FLAG IT
  1747.      MOV    WORD PTR MPUDELY,6000H        ;TIME FOR AN AT
  1748.      ;
  1749.      ;* Now install default values.
  1750.      ;
  1751. INITXT:     MOV    CS:WORD PTR DATPORT,DDATPORT    ;SET UP DEFAULT PORT ADDRESSES
  1752.      MOV    CS:WORD PTR CTLPORT,DCTLPORT
  1753.      MOV    CS:BYTE PTR HRDINT,DHRDINT    ;AND INT NUMBERS
  1754.      MOV    CS:BYTE PTR SFTINT,DSFTINT
  1755.      MOV    CS:WORD PTR IBSIZ,DIBSIZ    ;AND BUILT-IN BUFFER SIZE
  1756.      MOV    CS:WORD PTR FNCTAB,OFFSET FNCTABI ;INACTIVE DRIVER JUMP TABLE
  1757.      ;
  1758.      ;* Now read and act upon any ASCII parameters. They will
  1759.      ;* supercede the defaults.
  1760.      ;
  1761.      MOV    DS,ES:WORD PTR PMSOFS[BX]    ;GET PARAMETER SEGMENT
  1762.      MOV    SI,ES:WORD PTR PMOOFS[BX]    ;GET PARAMETER OFFSET
  1763.      ;
  1764. PARMLP:     MOV    AL,BYTE PTR[SI]            ;GET CHARACTER
  1765.      INC    SI
  1766.      CMP    AL,'/'                ;'/' STARTS ALL PARAMETERS
  1767.      JZ    PARM
  1768.      CMP    AL,ASCILF            ;IS IT AN LF (END OF STRING)?
  1769.      JNZ    PARMLP                ;LOOP IF NOT
  1770.      ;
  1771.      JMP    INIT2                ;END OF PARM STRING
  1772.      ;
  1773. PARM:     MOV    AL,BYTE PTR[SI]            ;GET WHAT PARAM
  1774.      INC    SI
  1775.      AND    AL,UCMASK            ;UPPER CASE IT
  1776.      MOV    DX,OFFSET ERRMSG1        ;ERROR ANTICIPATED
  1777.      MOV    CS:BYTE PTR ERRMS1I,AL        ;PUT PARM NAME THERE
  1778.      CMP    AL,'B'                ;BUILT-IN BUFFER SIZE?
  1779.      JZ    PARM1
  1780.      CMP    AL,'H'                ;HARDWARE INT NUMBER?
  1781.      JZ    PARM1
  1782.      CMP    AL,'P'                ;PORT ADDRESS?
  1783.      JZ    PARM1
  1784.      CMP    AL,'S'                ;SOFTWARE INT NUMBER?
  1785.      JNZ    PARMERR
  1786.      ;
  1787. PARM1:     MOV    AL,BYTE PTR[SI]            ;GET COLON - SHOULD BE ONE!
  1788.      INC    SI
  1789.      MOV    DX,OFFSET ERRMSG2        ;ANTICIPATE ERROR
  1790.      CMP    AL,':'                ;MUST BE A COLON!
  1791.      JNZ    PARMERR
  1792.      ;
  1793.      CALL    GETHEX                ;GET PARAMETER VALUE
  1794.      MOV    AL,CS:BYTE PTR ERRMS1I        ;GET PARM NAME
  1795.      CMP    AL,'P'                ;PORT ADDRESS?
  1796.      JNZ    PARM2
  1797.      ;
  1798.      MOV    DX,OFFSET ERRMSG3        ;ANTICIPATE ERROR
  1799.      CMP    CX,PORTMAX            ;MAX ALLOWED PORT ADDRESS
  1800.      JA    PARMERR
  1801.      ;
  1802.      MOV    CS:WORD PTR DATPORT,CX        ;NEW DATA PORT ADDRESS
  1803.      INC    DX                ;CTRL PORT ADDRES ONE HIGHER
  1804.      MOV    CS:WORD PTR CTLPORT,CX
  1805.      JMP    PARMLP
  1806.      ;
  1807. PARM2:     CMP    AL,'S'                ;SOFTWARE INTERRUPT?
  1808.      JNZ    PARM3
  1809.      ;
  1810.      MOV    DX,OFFSET ERRMSG4        ;ANTICIPATE ERROR
  1811.      CMP    CX,MAXSFTI            ;SEE IF HIGHER THAN ALLOWED
  1812.      JA    PARMERR
  1813.      ;
  1814.      CMP    CX,MINSFTI            ;SEE IF LOWER THAN ALLOWED?
  1815.      JB    PARMERR
  1816.      ;
  1817.      MOV    CS:BYTE PTR SFTINT,CL        ;INSTALL NEW SOFT INT NUMBER
  1818.      JMP    PARMLP
  1819.      ;
  1820. PARM3:     CMP    AL,'H'                ;HARDWARE INTERRUPT?
  1821.      JNZ    PARM5
  1822.      ;
  1823.      MOV    DX,OFFSET ERRMSG5        ;ANTICIPATE ERROR
  1824.      CMP    CX,0FH                ;CHECK INT IS 0-0FH!
  1825.      JA    PARMERR                ;ELSE ERROR!
  1826.      ;
  1827.      MOV    AL,CS:BYTE PTR ATFLAG        ;SEE IF AT
  1828.      AND    AL,AL
  1829.      JNZ    PARM4                ;IF SO JUMP
  1830.      ;
  1831.      MOV    DX,OFFSET ERRMSG6        ;ANTICIPATE ERROR
  1832.      CMP    CL,7                ;FOR XT, ONLY 0-7 ARE ALLOWED
  1833.      JA    PARMERR
  1834.      ;
  1835. PARM4:     MOV    CS:BYTE PTR HRDINT,CL        ;INSTALL NEW HARD INT NUMBER
  1836.      JMP    PARMLP
  1837.      ;
  1838. PARM5:     MOV    DX,OFFSET ERRMSG7        ;BUFSIZ - ANTICIPATE ERROR
  1839.      CMP    CX,MINBSIZ            ;CHECK VALUE LIMITS
  1840.      JB    PARMERR
  1841.      CMP    CX,MAXBSIZ
  1842.      JA    PARMERR
  1843.      ;
  1844.      MOV    CS:WORD PTR IBSIZ,CX        ;INSTALL NEW BUFFER SIZE
  1845.      JMP    PARMLP
  1846.      ;
  1847.      ;* You come here when an invalid parameter has been found.
  1848.      ;* Print an error message and stop scanning parameters.
  1849.      ;* Leave things as they are and continue.
  1850.      ;*
  1851.      ;* (What you would want to do is print the error, then
  1852.      ;* set the driver size to zero to avoid installing it.
  1853.      ;* This is possible according to the PC-DOS tech man.
  1854.      ;* HOWEVER, it *** DOES NOT WORK ***. MicroSoft them-
  1855.      ;* selves do some tricks to overcome this in their
  1856.      ;* Mouse Driver.)
  1857.      ;
  1858. PARMERR: PUSH    CS                ;MAKE DS=CS
  1859.      POP    DS
  1860.      PUSH    DX                ;SAVE MSG ADDRESS
  1861.      MOV    DX,OFFSET ERRMSG0
  1862.      MOV    AH,PRTSTR
  1863.      INT    SYSTEM                ;TYPE DRIVER NAME
  1864.      POP    DX
  1865.      MOV    AH,PRTSTR            ;TYPE IT
  1866.      INT    SYSTEM
  1867.      ;
  1868. ;     ;* Set size of driver to 0 (DOES NOT WORK - crashes the PC
  1869. ;     ;* during boot - bug in MSDOS's initialization code !).
  1870. ;     ;
  1871. ;     MOV    ES:WORD PTR EOFOFS[BX],0    ;SET END OFFSET
  1872. ;     MOV    ES:WORD PTR ESGOFS[BX],CS    ;SET END PARAGRAPH = BEGINING
  1873. ;     RET
  1874.      ;
  1875.      ;* Parameters found and processed. Now install the software
  1876.      ;* interrupt vector after first saving the original value.
  1877.      ;
  1878. INIT2:     XOR    AX,AX                ;CLEAR DS
  1879.      MOV    DS,AX
  1880.      MOV    AL,CS:BYTE PTR SFTINT        ;GET SOFT INT
  1881.      ADD    AX,AX                ;MULT BY 4
  1882.      ADD    AX,AX
  1883.      MOV    SI,AX
  1884.      MOV    AX,WORD PTR DS:[SI]        ;GET OLD VECTOR'S OFFSET
  1885.      MOV    CS:WORD PTR OSWVEC,AX        ;SAVE IT
  1886.      MOV    AX,WORD PTR DS:2[SI]        ;GET SEGMENT
  1887.      MOV    CS:WORD PTR OSWVEC+2,AX        ;AND SAVE
  1888.      ;
  1889.      MOV    DS:WORD PTR [SI],OFFSET SWIHNL    ;INSTALL SOFTWARE INTERRUPT
  1890.      MOV    DS:WORD PTR 2[SI],CS        ;HANDLER ADDRESS    
  1891.      ;
  1892.      ;* Now set up hardware interrupt dependent values.
  1893.      ;
  1894.      MOV    CX,OFFSET XTTAB            ;ASSUME WE ARE AN XT
  1895.      MOV    AL,CS:BYTE PTR ATFLAG        ;CHECK THAT
  1896.      AND    AX,0FFH                ;CLEARS AH FOR LATER
  1897.      JE    INIT3
  1898.      ;
  1899.      MOV    CX,OFFSET ATTAB            ;NO, WE ARE AN AT
  1900.      ;
  1901. INIT3:     MOV    AL,CS:BYTE PTR HRDINT        ;GET INT NUMBER
  1902.      ADD    AX,AX                ;SHIFT UP 2 (AH = 0)
  1903.      ADD    AX,AX
  1904.      ADD    AX,CX                ;POINT TO PROPER TABLE ENTRY
  1905.      MOV    SI,AX
  1906.      MOV    AL,CS:BYTE PTR [SI]        ;GET EOI PORT ADDRESS (8 BITS)
  1907.      MOV    CS:BYTE PTR EOIPORT,AL        ;INSTALL VALUE
  1908.      INC    SI
  1909.      MOV    AL,CS:BYTE PTR [SI]        ;GET ENABLE PORT (8 BITS)
  1910.      MOV    CS:BYTE PTR ENAPORT,AL        ;INSTALL VALUE
  1911.      INC    SI
  1912.      MOV    AL,CS:BYTE PTR [SI]        ;GET BIT MASK (8 BITS)
  1913.      MOV    CS:BYTE PTR DISMASK,AL        ;INSTALL DISABLE MASK
  1914.      NOT    AL                ;INVERT IT
  1915.      MOV    CS:BYTE PTR ENAMASK,AL        ;INSTALL ENABLE MASK
  1916.      INC    SI
  1917.      MOV    AL,CS:BYTE PTR [SI]        ;GET VECTOR ADDR/4
  1918.      XOR    AH,AH
  1919.      ADD    AX,AX
  1920.      ADD    AX,AX
  1921.      MOV    CS:WORD PTR HWVECAD,AX        ;INSTALL VECTOR ADDRESS
  1922.      ;
  1923.      ;* Now finally return driver values.
  1924.      ;
  1925.      PUSH    CS
  1926.      POP    DS
  1927.      MOV    DX,OFFSET SIGNON
  1928.      MOV    AH,PRTSTR
  1929.      INT    SYSTEM
  1930.      ;
  1931.      PUSH    CS                ;GET CODE PARAGRAPH TO DX
  1932.      POP    DX
  1933.      MOV    AX,OFFSET DBUF + 16        ;GET DRIVER'S LENGTH + EXTRA
  1934.      ADD    AX,CS:WORD PTR IBSIZ        ;ADD BUILT-IN BUFFER SIZE
  1935.      MOV    CL,4                ;SHIFT BY 4 TO GET # PARAGR.
  1936.      AND    AL,0F0H                ;CLEAR 4 LOW BITS
  1937.      ROR    AX,CL
  1938.      ADD    DX,AX                ;END PARAGRAPH IN DX NOW
  1939.      MOV    ES:WORD PTR EOFOFS[BX],0    ;SET END OFFSET
  1940.      MOV    ES:WORD PTR ESGOFS[BX],DX    ;SET END PARAGRAPH
  1941.      ;
  1942.      RET
  1943.      ;
  1944. INIT     endp
  1945.      ;
  1946.      ;***************************************************************
  1947.      ;* GETHEX                            *
  1948.      ;*                                *
  1949.      ;* Reads hex characters from string at DS:0[SI] and builds    *
  1950.      ;* value in CX. Terminates on non-hex character. Only used    *
  1951.      ;* during driver initialization. Overwritten by buffer.    *
  1952.      ;***************************************************************
  1953.      ;
  1954. GETHEX     proc near
  1955.      ;
  1956.      XOR    CX,CX                ;CLEAR RESULT
  1957.      ;
  1958. GETHXLP: MOV    AL,BYTE PTR[SI]            ;GET CHAR     
  1959.      CMP    AL,'0'                ;SEE IF < DEC LOW
  1960.      JB    GETHXEX                ;THEN EXIT
  1961.      ;
  1962.      CMP    AL,'9'                ;SEE IF DEC DIGIT
  1963.      JBE    GETHX1                ;THEN JUMP
  1964.      ;
  1965.      AND    AL,UCMASK            ;MASK TO UPPER CASE
  1966.      CMP    AL,'A'                ;SEE IF < HEX DIGIT
  1967.      JB    GETHXEX                ;THEN EXIT
  1968.      ;
  1969.      CMP    AL,'F'                ;SEE IF > HEX DIGIT
  1970.      JA    GETHXEX                ;THEN JUMP
  1971.      ;
  1972.      SUB    AL,7                ;FOR ASCII 'A'-'F' OFFSET 
  1973.      ;
  1974. GETHX1:     AND    AX,0FH                ;MASK DIGIT
  1975.      ADD    CX,CX                ;SHIFT CX UP 4
  1976.      ADD    CX,CX
  1977.      ADD    CX,CX
  1978.      ADD    CX,CX
  1979.      ADD    CX,AX                ;ADD IN LAST DIGIT
  1980.      INC    SI                ;NEXT CHARACTER
  1981.      JMP    GETHXLP
  1982.      ;
  1983. GETHXEX: RET
  1984.      ;
  1985. GETHEX     endp
  1986.      ;
  1987.      SUBTTL    Initialization Tables
  1988.      %OUT    Initialization Tables
  1989.      PAGE
  1990.      ;
  1991.      ;***************************************************************
  1992.      ;* Initialization tables                    *
  1993.      ;***************************************************************
  1994.      ;
  1995.      ;* These tables contain, in each entry:
  1996.      ;*
  1997.      ;* - the EOI command output port address,
  1998.      ;* - the enable/disable interrupt port address,
  1999.      ;* - the interrupt enable bit mask, (bit is a one),
  2000.      ;* - the memory address where to put the vector for the
  2001.      ;*   corresponding interrupt level (divided by 4 to fit
  2002.      ;*   it in a byte).
  2003.      ;
  2004.      ;* First the table for an XT machine.
  2005.      ;
  2006. XTTAB:     DB    20H, 21H, 00000001B,   8        ;DATA FOR XT INT 0
  2007.      DB    20H, 21H, 00000010B,   9        ;FOR XT INT 1
  2008.      DB    20H, 21H, 00000100B, 0AH        ;FOR XT INT 2
  2009.      DB    20H, 21H, 00001000B, 0BH        ;FOR XT INT 3
  2010.      DB    20H, 21H, 00010000B, 0CH        ;FOR XT INT 4
  2011.      DB    20H, 21H, 00100000B, 0DH        ;FOR XT INT 5
  2012.      DB    20H, 21H, 01000000B, 0EH        ;FOR XT INT 6
  2013.      DB    20H, 21H, 10000000B, 0FH        ;FOR XT INT 7
  2014.      ;
  2015.      ;* Now, the table for an AT machine.
  2016.      ;
  2017.      ;* !!!NOTE!!! The data in the ATTAB table is NOT CORRECT!!!
  2018.      ;* (Due to lack of needed information about AT interrupts).
  2019.      ;
  2020. ATTAB:     DB    20H, 21H, 00000001B,   8        ;DATA FOR AT INT 0
  2021.      DB    20H, 21H, 00000010B,   9        ;FOR AT INT 1
  2022.      DB    20H, 21H, 00000100B, 0AH        ;FOR AT INT 2
  2023.      DB    20H, 21H, 00001000B, 0BH        ;FOR AT INT 3
  2024.      DB    20H, 21H, 00010000B, 0CH        ;FOR AT INT 4
  2025.      DB    20H, 21H, 00100000B, 0DH        ;FOR AT INT 5
  2026.      DB    20H, 21H, 01000000B, 0EH        ;FOR AT INT 6
  2027.      DB    20H, 21H, 10000000B, 0FH        ;FOR AT INT 7
  2028.      DB    20H, 21H, 00000001B,   8        ;FOR AT INT 8
  2029.      DB    20H, 21H, 00000010B,   9        ;FOR AT INT 9
  2030.      DB    20H, 21H, 00000100B, 0AH        ;FOR AT INT 10
  2031.      DB    20H, 21H, 00001000B, 0BH        ;FOR AT INT 11
  2032.      DB    20H, 21H, 00010000B, 0CH        ;FOR AT INT 12
  2033.      DB    20H, 21H, 00100000B, 0DH        ;FOR AT INT 13
  2034.      DB    20H, 21H, 01000000B, 0EH        ;FOR AT INT 14
  2035.      DB    20H, 21H, 10000000B, 0FH        ;FOR AT INT 15
  2036.      ;
  2037.      SUBTTL    Error Messages
  2038.      %OUT    Error Messages
  2039.      PAGE
  2040.      ;
  2041.      ;***************************************************************
  2042.      ;* Error messages                        *
  2043.      ;***************************************************************
  2044.      ;
  2045. SIGNON:  DB    '--- Installing CMT MIDI  MPU401   Device Driver'
  2046.      DB    ' v1.30 ---',ASCICR, ASCILF,'$'
  2047.      ;
  2048. ERRMSG0: DB    'MPU401 MIDI Driver: $'
  2049.      ;
  2050. ERRMSG1: DB    'Unknown Init Parameter "'
  2051. ERRMS1I: DB    ' "'                ;CHAR TO BE INSERTED
  2052.      DB    ASCICR,ASCILF,'$'
  2053.      ;
  2054. ERRMSG2: DB    'No ":" After Parm Name'
  2055.      DB    ASCICR,ASCILF,'$'
  2056.      ;
  2057. ERRMSG3: DB    'Illegal Port Address'
  2058.      DB    ASCICR,ASCILF,'$'
  2059.      ;
  2060. ERRMSG4: DB    'Illegal Software Interrupt'
  2061.      DB    ASCICR,ASCILF,'$'
  2062.      ;
  2063. ERRMSG5: DB    'Illegal Hardware Interrupt'
  2064.      DB    ASCICR,ASCILF,'$'
  2065.      ;
  2066. ERRMSG6: DB    'Hard Int 8-15 Allowed On PC-AT Only'
  2067.      DB    ASCICR,ASCILF,'$'
  2068.      ;
  2069. ERRMSG7: DB    'Illegal Buffer Size'
  2070.      DB    ASCICR,ASCILF,'$'
  2071.      ;
  2072. CODSEG     ENDS
  2073.      IF1
  2074.      %OUT    ---------- Pass 1 Completed ----------
  2075.      ENDIF
  2076.      IF2
  2077.      %OUT    ---------- Pass 2 Completed ----------
  2078.      ENDIF
  2079.      SUBTTL    Symbols
  2080.      END
  2081.