From: voz5@elis.elis (Pedro e Paulo)
Newsgroups: alt.sb.programmer
Subject: Info on how to acess SB midi
Date: 8 Apr 92 08:30:42 GMT
Distribution: alt.sb.programmer
Organization: /net/elis/user/p1311/voz5/.organization


      I received many requests on info about the Sound Blaster midi
port, so insted of "emailling" I dicided to post it here.

The ideas expressed here are my own, and they reflect my investigations
on the subject of midi I/O on Sound Blaster versions 1.5 and 2.0.

All info was obtained by following some software in msdos debug and
by performing tests in PASCAL, because I DON'T have any documentation on SB.

These are the major ports for dsp (&midi) commands:

const
    PORT_BASE       = $0220; { may vary if you installed SB on other port }
    DSP_RESET       = PORT_BASE + $0006;
    DSP_COMMAND     = PORT_BASE + $000c;
    DSP_WRITESTATUS = PORT_BASE + $000c;
    DSP_DATAAVAIL   = PORT_BASE + $000e;
    DSP_READDATA    = PORT_BASE + $000a;

	{ the following are necessary for talking to PC's PIC  }
    PIC     = $20;
    EOI     = $20;
    PIC_IMR = $21;

To send a command to the DSP you must do this:

    procedure send(val:byte);

    begin
        { you might want to consider some time-out to prevent crash }
        while( port[DSP_WRITESTATUS] and $80 ) <>0 do;
        port[DSP_COMMAND] := val;
    end;

Reading a byte from midi port (on Sound Blaster v1.5 mode) is something
like this:

    function midi_in:byte;

    begin
        send($30);

        while( port[ DSP_DATAAVAIL ] and $80 ) = 0 do;
        midi_in := port[ DSP_READDATA ];
    end;

For writing a byte to the midi port (on Sound Blaster v1.5 mode) use
something like this:

    procedure midi_out {(b:byte)} ;

    begin
        send($38);
        send(b);
    end;

There is another way to receive data from the midi port, the way is using
hardware interrupts.  The procedure is as follows:
   - Prepare some buffer (probably circular) for holding the receiving data
   - Save the old interrupt vector (in my case int15<>irq7 on SB config.)
   - Point this interrupt vector to your procedure (i.e., midi_rec_isr)
   - Prepare the PC's PIC (Programmable Interrupt Controller) to receive 
     (And enable) IRQ7
   - Do that strange while (if you can figure it out, please tell me)
   - Send command ($31)

    procedure midi_recv_start;

    begin
        midi_buf_in:=0;
        midi_buf_out:=0;

        getintvec($0f,oldint15);
        setintvec($0f,addr(midi_recv_isr));

        port[ PIC_IMR ]:=port[ PIC_IMR ] and $7f;

        while port[ DSP_DATAAVAIL ] and $80<>0 do aux:=port[ DSP_READDATA ];

        send($31);
    end;


Now for the interrupt service routine:

    procedure midi_recv_isr; interrupt;
    var b:byte;
    begin
        b:=0;

        repeat
            while (port[DSP_DATAAVAIL] and $80=0) do; 

	    { Store info in buffer }      
            midi_buf[midi_buf_in] := port[ DSP_READDATA ];
            midi_buf_in := (midi_buf_in + 1) mod MIDI_BUF_LEN;
            inc(b);

        until b>=4; 
         { In mode SBv1.5 only one cycle is necessary <> only 1 byte read 
           per interrupt }

        { next line>> acknowledges the interrupt to the DSP on SB }
        b:=port[ DSP_DATAAVAIL ];

        { Send End Of Interrupt Command to PC's PIC }
        port[ PIC ]:= EOI;
    end;

The necessary cleaning up after reading:

    procedure midi_recv_stop;

    begin
        { reset the SB's DSP , see bellow }
        reset_dsp;

        { Disable IRQ7 interrupts }
        port[ PIC_IMR ]:=port[ PIC_IMR ] or $80;

        { Restore the old interrupt vector }
        setintvec($0f,oldint15);
   end;

   procedure reset_dsp;
   var aux:byte;
   begin
       port[DSP_RESET]:=$01;

       asm  { just wait for a while, probably on 486 this is not enaugh }
          push ax
          pop ax
       end;

       port[DSP_RESET]:=$00;

       while port[DSP_DATAAVAIL] and $80<>0 do aux:=port[DSP_READDATA];
   end;

Now for the problem, the Sound Blaster v1.5 is Half-Duplex in midi
communications so simultaneous "record" and "playback" on midi is not 
possible.

I think Creative Labs was crowded with complains about this so they
decided to lunch Sound Blaster 2.0 (which I now have).  The new features
include: more sampling&recording speed with DSP and full duplex midi with
Time Stamp and 64 byte FIFO.

I believe that Time Stamp is an internal mechanism to stamp incoming bytes
with some time signature for "perfect" recording purposes.

So the following changes can improve the examples I posted before:
   - At midi_recv_start, send command $37 instead of $31 to enter in
     Sound Blaster 2.0 full duplex receive mode.
   - The midi_recv_isr interrupt routine now reads 4 bytes for each
     byte that becomes available at midi port (1 byte data + 3byte 
     time stamp).
   - The only way to stop is to reset the DSP because any of the other
     dsp funcs won't work (sorry, cant play vocs while recording or playing
     midi tunes)
   - One point is important now, for sending bytes to the midi port the
     sequence: send($38) send(byte) at midi_out now changes to send(byte)
     (that's why we can not use any other DSP commands without resetting)

I hope this will help you.  Remember that this was obtained without any
documentation so something may be wrong or work improperly. If PASCAL,
or better Borland's Turbo Pascal 6.0 doesn't meet your ideal language,
email me your doubts and I will try to help you further.
(Sorry about the English)

				Pedro Carvalho
		    	      voz5@elis.inesc.pt

PS: Does anyone nows how we can use the drum secction on SB fm (adlib) ?

------------------------------------------------------------------------
          ___   ___  
\      / /   \     /  ---       :  INESC - Instituto de Engenharia e
 \    /  |   |    /  |___       :  Sistemas de Computadores, Lisboa
  \  /   |   |   /       |      :  Portugal
   \/    \___/  /___  ___|      :  voz5@elis.inesc.pt