home *** CD-ROM | disk | FTP | other *** search
/ Chestnut's Multimedia Mania / MM_MANIA.ISO / midi / cmtcmu / aintr.asm < prev    next >
Encoding:
Assembly Source File  |  1990-06-28  |  15.2 KB  |  485 lines

  1. ;***************************************************************************** 
  2. ;        Change Log 
  3. ;  Date        | Change 
  4. ;-----------+----------------------------------------------------------------- 
  5. ; 31-Dec-85 | Created change log 
  6. ; 31-Dec-85 | Make sure DS: register is set properly! 
  7. ;        | Note: Why the CLD at the start of the routine?  CLI? 
  8. ;  1-Jan-86 | Removed CLD.  Roger suggests this was carryover from 6502 code 
  9. ;        | where CLD is clear-decimal-mode. 
  10. ;        | Change 62H EOI code to 20H EOI code like everything else that 
  11. ;        | talks to interrupt chip.    Note that we are tweaking the primary 
  12. ;        | interrupt controller chip on a /AT, but that is OK because the 
  13. ;        | EOI was sent to the secondary controller via the RE_DIRECT code 
  14. ;        | (PC/AT Tech Ref page 5-71) 
  15. ;        | This is the same mechanism used on both the /XT and /AT, e.g. 
  16. ;        | PC/XT Tech Ref page A-80, lines 5729-5730 
  17. ;  5-Feb-86 | Keep interrupts off during interrupt handler 
  18. ;  8-Feb-86 | Added code to capture system exclusive messages 
  19. ;        | Removed some debugging stores into d0,d1,d2,d3 
  20. ;        | Removed interrupt nesting counter -- if interrupts nest, you'll 
  21. ;        | crash before you can print the error report 
  22. ; 13-Feb-86 | Changed DATA macro to a more sane name: GETMIDI 
  23. ; 14-Feb-86 | Moved all variables to DSEG (they were in PSEG -- why did this 
  24. ;        | ever work before?) 
  25. ;  5-May-86 | Optimized input for better transcription speed 
  26. ;  9-Jul-86 | Added loop to avoid exiting interrupts with more data available 
  27. ; 18-Jul-86 | Fixed a running status bug and cleaned up some debugging code 
  28. ;***************************************************************************** 
  29. ;; 
  30. ;; MPU-401 interrupt handler 
  31. ;;    modelled after MPU-401 manual, pages 55-56 
  32. ;;    except that Ack commands are handled by setting 
  33. ;;    a flag and other commands are handled by 
  34. ;;    putting data into a buffer.  Once things are 
  35. ;;    initialized, this is the only place that should 
  36. ;;    read data from the MPU-401.  All writes (commands) 
  37. ;;    are issued from C routines. 
  38. ;; 
  39. ;; Notes:  (Joe Newcomer, 31-Dec-85) 
  40. ;;   Because an interrupt can occur from anywhere, including DOS and 
  41. ;;   the BIOS, we cannot, repeat CANNOT assume the validity of any 
  42. ;;   register except CS:.  In particular, SS:SP is quite possibly a 
  43. ;;   BIOS stack segment which are infinitesmally small.     We CANNOT 
  44. ;;   push anything onto the BIOS stack segment without risking severe 
  45. ;;   damage to the integrity of the system.  So we have here a large 
  46. ;;   private stack segement; we switch attention to it, *very carefully* 
  47. ;;   save our state on it, and then call the code which handles our 
  48. ;;   MPU-401 interrupt.     Upon return, we *very carefully* reset the stack 
  49. ;;   and return to our caller.    Since we need to address the C data segment, 
  50. ;;   we must also load DS:, which we need to set intnest and various buffer 
  51. ;;   headers.  See the note associated with the setting of DS:; this 
  52. ;;   code works only in the small data model. 
  53.  
  54. include dos.mac
  55.  
  56. ; DEBUG = 1        ;; define DEBUG to enable some extra record keeping 
  57.  
  58. DSEG 
  59. extrn _interror:word    ;; report errors up to C handlers 
  60. extrn _timeerr:word    ;; reports timeout errors to C handlers 
  61. extrn _time_req:word    ;; set to true if next Ack will be timing byte 
  62.  
  63. IFDEF DEBUG 
  64. extrn _loop_cnt:word    ;; count loop interations 
  65. extrn _loop_max:word    ;; max value of loop interations 
  66. extrn _intcnt:word    ;; count of interrupts taken 
  67. ENDIF 
  68.  
  69. extrn _buff:byte        ;; data from mpu401 
  70. extrn _buffhead:word    ;; data is removed from head offset 
  71. extrn _bufftail:word    ;; data is inserted at the tail offset 
  72.  
  73. extrn _xbuff:word    ;; system exclusive buffer pointer 
  74. extrn _xbuffhea:word 
  75. extrn _xbufftai:word 
  76. extrn _xbuffmas:word 
  77.  
  78. ;; 
  79. ;; Globals used in communication with mpu.c 
  80. ;; 
  81.  
  82. extrn _Ack:word        ;; set if ack received 
  83. extrn _Unknown:word        ;; set for unknown command (for debugging) 
  84. extrn _Ticks:dword        ;; Clock ticks (400 = 1 second) 
  85.  
  86. ;;Midi information 
  87. extrn _MidiTime:byte    ;; extra timing byte 
  88. extrn _MidiStat:byte    ;; Running status 
  89. extrn _Midi1:byte    ;; First arg 
  90. extrn _Midi2:byte    ;; Second arg 
  91. extrn _Midi3:byte    ;; Third arg (not used) 
  92.  
  93. extrn _rd_delay:word    ;; counts down wait for mpu data 
  94.  
  95. ENDDS 
  96.  
  97. ;_TEXT    SEGMENT 
  98. PSEG 
  99. public _a_intr, _init_asm 
  100.  
  101. ; These must be in the pseg because on entry only the CS: is addressible 
  102.  
  103. DASEG    DW    0 
  104.  
  105. OldAX    DW    ? 
  106. OldSS    DW    ?        ; old stack segment 
  107. OldSP    DW    ?        ; old stack pointer 
  108.     DW    512 DUP(?)    ; local stack space for intercept routine 
  109. STACK    label    WORD 
  110.  
  111.  
  112. NESTERR = 1        ;;nested interrupt error 
  113. BUFFERR = 2        ;;input buffer overflow error 
  114. CMDERR = 3        ;;unknown command 
  115. TIMEOUT = 4        ;;timeout waiting to read data 
  116.  
  117. BUFFMASK = 3FFH        ;; buffer size is 1024 bytes, 3FF=1023 
  118.  
  119. ;; Status byte masks 
  120. ;; 
  121.     DRR    =    40h    ;; Data Receive Ready 
  122.     DSR    =    80h    ;; Data Send Ready 
  123.  
  124.     STATPORT = 331H        ;; MPU-401 Status port 
  125.     DATAPORT = 330H        ;; MPU-401 Data (MPU to PC) port 
  126.  
  127. ;***************************************************************************** 
  128. ; _init_asm(): called to save the data segment into a place where 
  129. ;          the interupt routine can get at it. 
  130.  
  131. _init_asm proc near 
  132.  
  133.     push    bp        ;save bp 
  134.     mov    bp,sp        ;move sp into bp 
  135.     mov    cs:DASEG,ds    ;save the ds in DASEG 
  136.     pop    bp 
  137.     ret 
  138.  
  139. _init_asm    endp 
  140.  
  141. ;***************************************************************************** 
  142. ;                    _a_intr 
  143. ; Called via: 
  144. ;    far call from interrupt handler.  NOTE: proc is declared 'near' so 
  145. ;    that funny fixups are not required when linking it into C small model 
  146. ;    code.  Since we return via IRET, the near/far distinction does not 
  147. ;    matter.     HOWEVER if one were to play funny games with doing returns 
  148. ;    and twiddling flags (unlikely) the near/far distinction would matter 
  149. ;***************************************************************************** 
  150. _a_intr    proc    near 
  151. ; Establish a stack for us 
  152.     mov    OldSS,SS    ; save old stack 
  153.     mov    OldSP,SP    ; ... 
  154.     mov    OldAX,AX    ; and scratch register 
  155.     cli            ; don't play with fire, turn 'em off 
  156.     mov    AX,CS        ; our new stack segment is addressible by CS: 
  157.     mov    SS,AX        ; .. 
  158.     mov    SP,offset STACK ; always change SS,SP in adjacent instructions 
  159.  
  160.                 ; In principle, we didn't need to turn  
  161.                 ; interrupts off because doing it in that  
  162.                 ; order guarantees that no interrupt will  
  163.                 ; occur between mov SS and mov SP, but early 
  164.                 ; 8088s had a bug and it didn't work. 
  165.                 ; Better safe than sorry.  An /XT could be 
  166.                 ; repaired with one of these bogus chips 
  167.  
  168. ;    sti            ; allow interrupts again 
  169. ; Save state 
  170.     push    ds        ; save state 
  171.     push    es 
  172.     push    ax 
  173.     push    bx 
  174.     push    cx 
  175.     push    dx 
  176.     push    di 
  177.     push    si 
  178.     push    ds 
  179. ; begin body 
  180.  
  181. ;    Restore DS from value  saved in pgroup:DASEG. 
  182.     mov    bx,offset PGROUP:DASEG 
  183.     mov    ds,cs:[bx]    ; now DS has the offset of dgroup segment 
  184.     assume    ds:DGROUP 
  185.  
  186. ;    mov    ax,DGROUP 
  187. ;    mov    ax,SEG intnest    ; make DS be correct 
  188.                 ; note: All variables have the same DS 
  189.                 ; so doing it for one will do it for 
  190.                 ; all 
  191.                 ; This trick will not work in the large 
  192.                 ; memory model; there we have to load 
  193.                 ; DS: for each variable, because they 
  194.                 ; could be in different segments 
  195.                 ; No, I don't know how to handle the 
  196.                 ; case where a long vector falls across 
  197.                 ; a segment boundary 
  198.  
  199. ; at this point we may now validly address data 
  200. IFDEF DEBUG 
  201.     inc    intcnt        ; up interrupt count 
  202.     mov    _loop_cnt, 0    ; initialize iteration counter 
  203. ENDIF 
  204.  
  205. readit:    call    mpu_aintr 
  206. ; end body 
  207. IFDEF DEBUG 
  208.     inc    _loop_cnt 
  209. ENDIF 
  210.     mov    al,20h        ;;; EOI code 
  211.     out    20h,al        ;;; Announce end of interrupt 
  212.     mov    dx,STATPORT    ;; load port number 
  213.     in    al,dx        ;; read in char from port     
  214.     test    al,DSR        ; 
  215.     jz    readit        ;loop to handle next data byte 
  216. ;;                See note about the fact that we are 
  217. ;;                twiddling the primary interrupt controller 
  218. ;;                chip on an /AT, but this is no different 
  219. ;;                than what is required on the /XT 
  220. IFDEF DEBUG 
  221.     mov    ax, _loop_cnt    ;; _loop_max = max(_loop_max, _loop_cnt) 
  222.     cmp    ax, _loop_max 
  223.     jb    leave 
  224.     mov    _loop_max, ax 
  225. ENDIF 
  226. leave:    pop    ds 
  227.     pop    si 
  228.     pop    di 
  229.     pop    dx 
  230.     pop    cx 
  231.     pop    bx 
  232.     pop    ax 
  233.     pop    es 
  234.     pop    ds 
  235. ; Now restore our old stack 
  236.     cli            ; do it safely... 
  237.     mov    SS,OldSS    ; restore SS 
  238.     mov    SP,OldSP    ; restore SP 
  239. ;    sti            ; allow them again 
  240.     mov    AX,OldAX    ; restore AX 
  241.     iret 
  242. _a_intr    endp 
  243.  
  244. ;; 
  245.  
  246. ;; 
  247. ;; Data from mpu-401 
  248. ;; 
  249.  
  250.     MPU_ACK        =    0feh    ;; acknowledgment of end of command 
  251.     ABOVE_TIMING_BYTE =    0f0h    ;; 1st value greater than legal timing 
  252.                     ;;  byte values (0 - 0efh) 
  253.     TIMER_OVERFLOW    =    0f8h    ;; record timer reached 240 
  254.     TIMER_INCR    =    240d    ;; add when TIMER_OVERFLOW comes 
  255.     SYSTEM_MESSAGE    =    0ffh    ;; MIDI system message 
  256.     MIDI_EXCLUSIVE    =    0f0h    ;; MIDI exclusive message 
  257.     MIDI_EOX    =    0f7h    ;; MIDI EOX (end of MIDI exclusive) 
  258.     MPUNOOP        =    0f8h    ;; MPU Mark: No Operation 
  259.  
  260. ;; 
  261. ;; midi codes 
  262. ;; high order 4 bits (of 8) give command 
  263. ;; low order 4 bits give midi channel number 
  264. ;; 
  265.     MCOMMASK    =    0f0h    ;; These bits give MIDI command 
  266.  
  267.     MSTATUSMASK    =    080h    ;; This bit set if MIDI status byte 
  268.     MCHANMASK    =    00fh    ;; These bits give MIDI channel number 
  269.  
  270.     NOTEOFF        =    080h    ;; status,pitch,veloc 
  271.     NOTEON        =    090h    ;; status,pitch,veloc (=0 means off) 
  272.     NOTEAFTERTOUCH    =    0a0h    ;; status,pitch,arg2 
  273.     CONTROLCHANGE    =    0b0h    ;; status,arg1,arg2 
  274.     PROGRAMCHANGE    =    0c0h    ;; status,program 
  275.     CHAFTERTOUCH    =    0d0h    ;; status,arg 
  276.     PITCHWHEEL    =    0e0h    ;; status,arg1,arg2 
  277.     MPUCOM        =    0f0h    ;; fake midi command, really mpu401 
  278.      
  279. MAXDELAY = 20000    ;; mpu_get times out after this many tries 
  280.  
  281.   
  282. ;***************************************************************************** 
  283. ;                    mpu_get 
  284. ;***************************************************************************** 
  285. mpu_get proc near        ;; read data from mpu 401 
  286.     mov _rd_delay,MAXDELAY 
  287. tryagain: 
  288.     mov    dx,STATPORT    ;; read status port 
  289.     in    al,dx 
  290.     test    al,DSR        ;; data ready to send? 
  291.     jz    gotit        ;;   yes - read the data 
  292.     dec    _rd_delay    ;;   no - test for timeout 
  293.     jnz    tryagain    ;; timed out? no - repeat 
  294.     mov    _timeerr,TIMEOUT ;;  yes - report error, 
  295.     mov    al,0f8h        ;;  and return innocuous (I hope) data 
  296.     ret 
  297. gotit:    mov    dx,DATAPORT    ;; load port number 
  298.     in    al,dx        ;; read in char from port     
  299.     ret 
  300. mpu_get endp 
  301.  
  302.   
  303. ;***************************************************************************** 
  304. ;                    putbuf 
  305. ;***************************************************************************** 
  306. putbuf proc near        ;; put data into buffer 
  307.     mov    dx,_bufftail 
  308.     add    dx,4 
  309.     and    dx,BUFFMASK    ;; wrap around ( dx = dx mod buffersize ) 
  310.     cmp    dx,_buffhead 
  311.     je    bufferfull 
  312. ;; save new _bufftail in dx, copy bytes 
  313.     mov    si,_bufftail 
  314.     mov    bl, _MidiStat 
  315.     mov    byte ptr _buff[si],bl 
  316.     inc    si 
  317.     mov    bl, _Midi1 
  318.     mov    byte ptr _buff[si],bl 
  319.     inc    si 
  320.     mov    bl,_Midi2 
  321.     mov    byte ptr _buff[si],bl 
  322.     mov    _bufftail,dx 
  323.     ret 
  324. bufferfull: 
  325.     mov    _interror,BUFFERR 
  326.     ret 
  327. putbuf endp 
  328.  
  329. GETMIDI macro ;; read the mpu 401 data port into al 
  330.         call    mpu_get 
  331.     endm 
  332.  
  333.   
  334. ;***************************************************************************** 
  335. ;                   mpu_aintr 
  336. ;***************************************************************************** 
  337. mpu_aintr proc    near 
  338.  
  339.     GETMIDI 1,gm1        ;; get what 401 want us to get 
  340.  
  341.     mov    ah,0            ;; several places assume ax = al 
  342.     cmp    ax,ABOVE_TIMING_BYTE    ;; Timing byte? 
  343.     jb    l_timing_byte        ;; (usually followed by midi data) 
  344.     cmp    al, TIMER_OVERFLOW 
  345.     je    l_timer_overflow 
  346.     cmp    al,MPU_ACK        ;; Ack? 
  347.     je    l_mpu_ack 
  348.     cmp    al,SYSTEM_MESSAGE    ;; Midi system message? 
  349.     jne    bad 
  350.     jmp    l_system_message 
  351.  
  352.  
  353. ;; 
  354. ;; This routine does not handle: 
  355. ;;    Track data requests 
  356. ;;    Conductor requests 
  357. ;;    Clock to host 
  358. ;; musicinit() initializes the MPU-401 in such a way so that these bytes 
  359. ;; are never sent.  If they do appear, they end up here. 
  360. ;; 
  361.  
  362. bad: 
  363.     mov    _Unknown,ax 
  364.     mov    _interror,CMDERR 
  365.     jmp    bye 
  366. ;; 
  367. ;; Handle each class of 401 message 
  368. ;; 
  369.  
  370. ;; An ack, set Ack so that mpu_wait() can see it. 
  371. l_mpu_ack: 
  372.     inc    _Ack 
  373.     cmp    _time_req, 0    ;; Does this command return timing data? 
  374.     je    ack_done    ;; if not, just return 
  375.     GETMIDI 2,gm2        ;; otherwise, read one more byte 
  376.     mov    ah, 0        ;; increment Ticks by result 
  377.     add    WORD PTR _Ticks, ax 
  378.     adc    WORD PTR _Ticks+2, 0 
  379.     mov    _time_req, 0 
  380. ack_done: 
  381.     jmp    bye 
  382.  
  383. ;; A timer overflow, increment clock by appropriate number of ticks 
  384. l_timer_overflow: 
  385.     add    WORD PTR _Ticks,TIMER_INCR ;; yes, do 32 bit incr of clock 
  386.     adc    WORD PTR _Ticks+2,0 
  387.     jmp    bye 
  388.  
  389. ;; A timing byte - the hard case 
  390. ;; There are a number of possibilities, on which we branch 
  391. l_timing_byte: 
  392.     mov    _MidiTime,al        ;; save timing byte 
  393.     add    WORD PTR _Ticks,ax    ;; yes, do 32 bit incr of clock 
  394.     adc    WORD PTR _Ticks+2,0 
  395.     GETMIDI 3,gm3            ;; get next byte 
  396.     test    al,MSTATUSMASK        ;; It's midi, is it a status byte? 
  397.     je    runstat 
  398.  
  399. ;; Here we have new midi status byte.  Stash it and read in first data 
  400.  
  401.     mov    _MidiStat,al 
  402.     mov    bl,al            ;; copy command to bl 
  403.      
  404.     and    bl,MCOMMASK        ;; "And" off channel bits 
  405.     cmp    bl,MPUCOM        ;; Is it an MPU command in disguise? 
  406.     je    l_mpucom        ;; Yes, deal with it. 
  407.  
  408.     GETMIDI 4,gm4            ;; read in first data byte 
  409.     jmp    decode            ;; decide whether 1 or 2 data bytes 
  410.  
  411. runstat: 
  412.     mov    bl,_MidiStat        ;; no, use previous (running) status 
  413.     and    bl,MCOMMASK 
  414.  
  415. ;; Commands 0c0h (program change) and 0d0h (channel after touch) have 2 bytes 
  416. ;; at this point, al has 1st data byte, bl has upper four bits of status byte 
  417.  
  418. decode:    mov    _Midi1,al        ;; save first data byte 
  419.     cmp    bl,CHAFTERTOUCH 
  420.     je    gotmsg 
  421.     cmp    bl,PROGRAMCHANGE 
  422.     je    gotmsg 
  423.  
  424.     GETMIDI 5,gm5            ;; read second data byte 
  425.     mov    _Midi2,al        ;; save second data bytes 
  426. gotmsg: 
  427.  
  428. ;; 
  429. ;; Here the midi command is contained in the (2 or) 3 bytes 
  430. ;; MidiStat, Midi1, and Midi2 
  431. ;; 
  432.     call    putbuf        ;; put the data in the buffer 
  433.                 ;; optimization note: only one call to putbuf 
  434. gobye:    jmp    bye 
  435.  
  436.  
  437. ;; 
  438. ;; MPU-401 marks  
  439. ;;    These shouldn't happen and are ignored.  The NOOP mark IS sent 
  440. ;;    when recording, contrary to the MPU401 manual.  Since it seems 
  441. ;;    harmless, no error is reported if a NOOP is sent.  Otherwise, 
  442. ;;    report a bad command with the timing byte in the high-order byte 
  443. ;;    of the error data (Unknown) to distinguish the data as mark data. 
  444. ;; 
  445. l_mpucom: 
  446.     cmp    al,MPUNOOP 
  447.     je    gobye        ;; MPU-401 manual is wrong!  The  
  448.     mov    ah,_MidiTime    ;; report two bytes as unknown 
  449.     jmp    bad 
  450.  
  451. ;; A MIDI system message, currently only read sys. exclusive messages 
  452. l_system_message: 
  453.     ;; see what the message is 
  454.     GETMIDI 6,gm6 
  455.     cmp    al,MIDI_EXCLUSIVE 
  456.     je    store_x 
  457.     jmp    bad            ;; only handle MIDI_EXCLUSIVE 
  458.  
  459. store_x:    ;; put data in buffer until MIDI_EOX read     
  460.     mov    bx,_xbuff        ;; do not store if _xbuff is NULL 
  461.     cmp    bx,0 
  462.     je    nobuff 
  463.  
  464.     add    bx,_xbufftai        ;; add index 
  465.     mov    byte ptr [bx],al    ;; and store midi data 
  466.     mov    dx,_xbufftai        ;; increment with wrap-around 
  467.     add    dx,1 
  468.     and    dx,_xbuffmas 
  469.     mov    _xbufftai,dx 
  470. nobuff:    test    al,MSTATUSMASK        ;; are we done?  
  471.     je    ex_continue        ;; stop on any status byte ... 
  472.     cmp    al,MIDI_EXCLUSIVE    ;; ... except midi exclusive 
  473.     jne    bye 
  474. ex_continue: 
  475.     GETMIDI 7,gm7 
  476.     jmp    store_x 
  477. ;; common return point 
  478.      
  479. bye:    ret 
  480. mpu_aintr endp 
  481.     ENDPS 
  482. ;_TEXT    ENDS 
  483.     END 
  484.