home *** CD-ROM | disk | FTP | other *** search
/ Sound Sensations! / sound_sensations.iso / midifile / midily / mpu401.pas < prev    next >
Pascal/Delphi Source File  |  1989-08-30  |  8KB  |  225 lines

  1. Unit MPU401;
  2.  
  3. {
  4. ****************************************************************************
  5. This Unit is derived from an article written in the May 1989 issue of
  6. Electronic Musician Magazine entitled 'Handling MPU-401 Interrupts with
  7. Turbo Pascal' by William Millar.   The Code was coded into Unit form by
  8. John Sloan CIS. 71310,2267.
  9. ****************************************************************************
  10. }
  11.  
  12. Interface
  13.  
  14. Uses DOS;
  15.  
  16. Const
  17.      MPU_reset     = $ff;
  18.      MPU_UART_Mode = $3f;
  19.      MPU_ack       = $fe;
  20.      MPU_Dataport  =$330;
  21.      MPU_Statport  =$331;
  22.      MPU_Comport   =$331;
  23.      MPU_Data_Available_Mask = $80;
  24.      MPU_Data_Ready_Mask     = $40;
  25.      MPU_Interrupt_Number    = 10;
  26.      Buffer_start = 1;
  27.      Buffer_End   =512; {increase this if there appears to be data loss with
  28.                          long messages ie. make it 1024, 2048 etc.}
  29.  
  30. Type
  31.      MPU_Comdata_Send_Buffer = ARRAY[1..128] of Byte;
  32.      Data_Buffer = Array[Buffer_Start..Buffer_End] of Byte;
  33.  
  34. Var
  35.      MPU_Indata_Buffer : Data_Buffer;
  36.      MPU_in_UART_mode : Boolean;
  37.      buffer_Head, Buffer_Tail : Word;
  38.      Old_Int_Vec : Pointer;
  39.      ExitSave:pointer;
  40.  
  41. Procedure Disable_CPU_Interrupts;
  42.      INLINE($FA);{CLI}
  43.  
  44. Procedure Enable_CPU_Interrupts;
  45.      INLINE($FB); {STI}
  46.  
  47. Procedure Disable_MPU_Interrupts;
  48.      INLINE($FA/$BA/$21/$00/$EC/$0C/$04/$EE/$FB);
  49.  
  50. Procedure Enable_MPU_Interrupts;
  51.      INLINE($FA/$BA/$21/$00/$EC/$24/$FB/$EE/$FB);
  52.  
  53. Procedure Ack_Int_to_PIC;
  54.      INLINE($BA/$20/$00/$B0/$20/$EE);
  55.  
  56. Function Get_Data_From_MPU(Var data:Byte):Boolean;
  57. Procedure Send_Command_to_MPU(Command:Byte);
  58.  
  59. Implementation
  60.  
  61. Procedure MPU_Interrupt_Handler;
  62.  
  63. {***************************************************************************
  64. This routine is called every time the hardware IRQ2 toggles as a result of
  65. data being received by the MPU-401.  The hardware vector to this routine is
  66. entered into the PC's interrupt vector table in the initialization section
  67. of this Unit.  The pseudocode is something like:
  68.  
  69.                Disable any further interrupts from bothering us
  70.                If the status port data available bit is reset then
  71.                   Write the data to the next location in the buffer
  72.                   If the pointer to this data location hasn't reached the
  73.                       end of the buffer then increment it, otherwise make
  74.                       it point to the beginning of the buffer
  75.                Enable the CPU interrupt system
  76.                Acknowledge to the PIC chip that we responded to this IRQ
  77.  
  78. *****************************************************************************
  79. }
  80.  
  81. Interrupt;
  82. Begin
  83.      Disable_MPU_Interrupts;
  84.      While((Port[MPU_statport] AND MPU_Data_Available_Mask)=0) DO
  85.           Begin
  86.                MPU_Indata_BUffer[Buffer_Head]:=Port[MPU_DataPort];
  87.                If(Buffer_Head < Buffer_End) Then
  88.                    Inc(buffer_Head) Else
  89.                    Buffer_Head:=Buffer_Start
  90.           End;
  91.       Enable_MPU_Interrupts;
  92.       ack_Int_to_PIC;
  93. End;
  94.  
  95. Function Get_Data_From_MPU;
  96. {
  97. ****************************************************************************
  98. Disable CPU interrupts
  99. If the buffer beginning pointer and the end pointer are different then we
  100.    must have data therefore:
  101.       Renable interrupts so we don't lose any data
  102.       Get the data from the buffer
  103.       If the pointer to this data isn't at the end of buffer increment it
  104.          otherwise reset it to point to the buffer's beginning
  105.       If data was available, pass the data back to the calling routine
  106.            as well as a boolean true (data available) otherwise
  107.            pass a boolean false (data not available)
  108.  
  109. *****************************************************************************
  110. }
  111.  
  112. Var
  113.    Valid:boolean;
  114.  
  115. Begin
  116.    Disable_MPU_Interrupts;
  117.    Valid:=(Buffer_Head <> Buffer_Tail);
  118.    Enable_MPU_Interrupts;
  119.    If (Valid) then
  120.       begin
  121.            Data:=MPU_Indata_Buffer[buffer_Tail];
  122.            If (Buffer_Tail < Buffer_End)
  123.            Then Inc(buffer_Tail)
  124.            Else buffer_Tail:=Buffer_Start;
  125.       end;
  126.    Get_data_From_MPU:=valid
  127. end;{Get..}
  128.  
  129. Procedure Send_Command_to_MPU;
  130. {
  131. *****************************************************************************
  132. Wait until there is no data in the MPU's buffer then:
  133.    Disable interrupts
  134.    Send a command byte (ie. MPU reset) to the MPU
  135.    If the command was a reset and the MPU was in UART mode
  136.         Then set UART_MODE flag false otherwise
  137.            Get data from MPU, buffer it as in Get_Data_From_MPU function
  138.            until an ACK byte from the MPU comes in
  139.    If the command was to put the MPU in UART mode then set UART_MODE flag true;
  140.    Enable interrupts
  141.    Get out of here
  142.  
  143. *****************************************************************************
  144. }
  145.  
  146. Var
  147.    Data:Byte;
  148.    a:byte;
  149.  
  150. Begin
  151.      Repeat
  152.      Until((Port[MPU_Statport] And MPU_Data_Ready_Mask)=0);
  153.      Disable_MPU_Interrupts;
  154.      Port[MPU_Comport]:=Command;
  155.      If (Command = MPU_Reset) and (MPU_in_UART_Mode)
  156.         Then MPU_in_UART_Mode:=False
  157.         Else Repeat
  158.                  Repeat
  159.                  a:=port[mpu_statport];
  160.                  Until ((Port[MPU_Statport] and MPU_Data_Available_Mask)=0);
  161.                  Data:=Port[MPU_Dataport];
  162.                  If (Data <> MPU_ack) Then
  163.                     Begin
  164.                        MPU_Indata_buffer[buffer_head]:=data;
  165.                        If(Buffer_head < buffer_end)
  166.                        Then Inc(buffer_Head)
  167.                        Else Buffer_head:=buffer_start;
  168.                     End;
  169.                  Until (Data = MPU_ack);
  170.         If (Command = MPU_UART_Mode) then MPU_in_UART_Mode:=True;
  171.         Enable_MPU_Interrupts
  172. end; {Send_Command..}
  173.  
  174. {$F+} Procedure Uninstall_MPU401; {$F-}
  175. {
  176. ****************************************************************************
  177. This procedure gains control when the program terminates, normal or otherwise
  178. The code is:
  179.          Disable interrupts
  180.          Put the old IRQ2 interrupt back where it was before we started
  181.          Put the pointer to the next exit procedure (if any) back
  182.              in Turbo's internal ExitProc pointer
  183. *****************************************************************************
  184. }
  185.  
  186. Begin
  187.      Disable_MPU_Interrupts;
  188.      SetIntVec(MPU_Interrupt_Number,Old_Int_Vec);
  189.      ExitProc:=ExitSave;
  190. End;
  191.  
  192. {
  193. *****************************************************************************
  194. This code gets called at the Begin statement of the Main program.  The code is
  195. as follows:
  196.            Save any Previously stored Exit code address
  197.            Put our exit procedure's code into Turbo's intrinsic pointer
  198.            Initialize our data buffer to empty
  199.            Get the existing IRQ2 interrupt vector and save it
  200.            Put in its place the address to our Interrupt service routine
  201.                (MPU_Interrupt_Handler)
  202.            Enable from the MPU
  203.            Set the UART_Mode flag to false
  204.            Go and execute the Main Program
  205.  
  206. *****************************************************************************
  207. }
  208.  
  209. Begin {initialization Code}
  210.  
  211.      ExitSave:=ExitProc;
  212.      ExitProc:=@Uninstall_MPU401;
  213.  
  214. {Procedure Install_MPU401;}
  215.  
  216.      Buffer_Head := Buffer_Start;
  217.      Buffer_Tail := Buffer_Start;
  218.      GetIntVec(MPU_Interrupt_Number,Old_Int_Vec);
  219.      SetIntVec(MPU_Interrupt_Number,@MPU_Interrupt_Handler);
  220.      Enable_MPU_Interrupts;
  221.      MPU_In_UART_Mode := False;
  222.  
  223. End. {Init Code}
  224.  
  225.