2000 ;====================================================================== ; MIDI ROUTINES FOR BOTH MPU-401 (UART MODE) AND SOUNDBLASTER ;====================================================================== ;***************************************************** ;* MPU MIDI routines * ;* * ;* mpu_init * ;* mpu_exit * ;* mpu_get * ;* mpu_put * ;***************************************************** ;------------------------------------------------------------------------------ ; Equates ;------------------------------------------------------------------------------ reset EQU 0FFh uart EQU 03Fh ready_to_receive? EQU 01000000b ;Mpu data set ready, active low. data_available? EQU 10000000b ;Mpu data read ready, active low. loop_delay EQU 0FFFFh ;Delay for timeouts. DOSSEG .MODEL SMALL .386 .DATA Org_Int08Isr dd ? PUBLIC miditicks miditicks dd 0 tick_cntr dw ? public mpu_data_port,mpu_command_port,mpu_status_port,mpu_irq public irq_mask,eoi mpu_data_port dw 0330h ;Mpu data port address. mpu_command_port LABEL WORD ;Mpu command port address. mpu_status_port dw 0331h ;Mpu status port address. mpu_irq db 071h ;Mpu interrupt level irq_mask db 2 ;8259 mask for IRQ 2 (9) eoi dw 06162h ;End Of Interrupt mask public command,databyte command db ? databyte db ? midibuf db 256 dup (0) bufhead dw 0 buftail dw 0 .CODE PUBLIC mpu_init PUBLIC mpu_exit PUBLIC send_command PUBLIC mpu_put PUBLIC mpu_get old_int_vect dw 0,0 ;Data must be placed here for proper ; access in the ISR ;======================================================================= MPU_ISR PROC FAR push di push ds ;Save used registers push dx push ax mov ax,DGROUP mov ds,ax ;Set DS to data area mov dx,mpu_status_port ;Test to see if interrupt was initiated in al,dx ; by the MPU. test al,data_available? jz valid_mpu_data ;Yes, goto MPU routines pop ax ;No, restore registers pop dx pop ds jmp dword ptr cs:old_int_vect ;Exit out to old interrupt handler ;Get the data byte from the MPU. valid_mpu_data: sti push cx push bx mov dx,mpu_data_port in al,dx mov di,bufhead mov midibuf[di],al ;store byte in buffer inc byte ptr[bufhead] ;Acknowledge interrupt mov al,061h out 0a0h,al mov al,062h out 020h,al mi_exit: pop bx pop cx pop ax pop dx pop ds pop di iret MPU_ISR ENDP TIMER_ISR PROC FAR ASSUME CS:_TEXT,DS:DGROUP push ds push ax push dx mov ax,DGROUP mov ds,ax inc word ptr[miditicks] ;Update tick value cmp word ptr[miditicks],0 jnz dont_inc_hiword inc word ptr[miditicks+2] dont_inc_hiword: inc word ptr[tick_cntr] ;Call original timer ISR cmp word ptr[tick_cntr],55 ;at proper time interval jnz end_of_ISR mov word ptr[tick_cntr],0 pushf call Org_Int08Isr jmp skip_EOI end_of_ISR: mov al,20h ;Signal End of Interrupt out 20h,al skip_EOI: pop dx pop ax pop ds iret TIMER_ISR ENDP ;======================================================================== mpu_init PROC mov al,08h ;H„mta vektorn till INT 08h mov ah,35h int 21h mov word ptr[Org_Int08Isr],bx ;Org_Rutinens OFFSET h„mtas mov word ptr[Org_Int08Isr+2],es ;Segment sparas push ds mov ah,25h ;Install TIMER ISR mov al,08h push cs pop ds mov dx,offset TIMER_ISR int 21h pop ds cli mov dx,43h ;Re-program system timer to mov al,36h ;1 ms resolution out dx,al mov dx,40h ;New divisor mov al,0a9h ;1193180/4a9h = 1000 Hz out dx,al mov al,04h out dx,al sti call set_mpu_vector ;Install MPU interrupt vector (IRQ 2) mov command,reset ;Reset MPU call send_command mov command,uart ;Put it in dumb UART mode call send_command ret mpu_init ENDP ;------------------------------------------------------------------------ mpu_exit PROC mov command,reset call send_command call reset_mpu_vector mov dx,43h mov al,36h out dx,al mov dx,40h ;¸terst„ll divisor f”r Timer 0 mov al,0h out dx,al mov al,0h out dx,al push ds ;Restore old INT 8 mov ah,25h mov al,08h mov dx,word ptr[Org_Int08Isr] mov ds,word ptr[Org_Int08Isr+2] int 21h pop ds ret mpu_exit ENDP ;______________________________________________________________________________ ; ; int send_command(int command); ; Sends a command to the MPU-401. ; ; returns SUCCESS=-1, FAILURE=0, or command response byte ;______________________________________________________________________________ send_command PROC pushf ;Save flags (interrupt flag) ;Wait for MPU to be ready to receive mov cx,loop_delay ;Timeout loop limit mov dx,mpu_status_port sc_1: in al,dx test al,ready_to_receive? jz sc_2 ;Ready, send byte loop sc_1 ;MPU not ready, exit with error xor ax,ax jmp short sc_10 ;Send command sc_2: cli ;Suspend interrupts mov al,command ;Get command byte out dx,al ;Send it ;Wait for the command acknowledge sc_3: mov cx,loop_delay ;Timeout loop limit mov dx,mpu_status_port sc_4: in al,dx test al,data_available? jz sc_5 ;Data available, go get it loop sc_4 ;Keep trying ;Command ack not returned, exit with error. xor ax,ax jmp short sc_10 ;Get data in sc_5: mov dx,mpu_data_port in al,dx cmp al,0FEh ;Is it an acknowledge? je sc_6 ;Yes, continue ; call word ptr data_in_handler ;No, have the main data handler handle ; the data byte jmp short sc_3 ;Try again ;Test to see if command sent requires a returned data byte sc_6: mov ax,-1 ;Successful return code cmp command,0A0h ;Less than A0h? jb sc_10 ;Yes, no data byte returned cmp command,0AFh ;More than AFh? ja sc_10 ;Yes, no data byte returned ;Get returning data byte mov cx,loop_delay ;Timeout loop limit mov dx,mpu_status_port sc_7: in al,dx test al,data_available? jz sc_8 ;Data available, go get it loop sc_7 ;Try again ;Command failed to return a data byte, exit with error xor ax,ax jmp short sc_10 ;Get data byte sc_8: mov dx,mpu_data_port in al,dx xor ah,ah ;Exit with return code in AX sc_10: sti ;Restore interrupts popf ;Restore flags ret send_command ENDP ;______________________________________________________________________________ ; ; int mpu_put(int databyte); ; Writes data to the MPU. ; ; Returns SUCCESS=1, FAILURE=0. ;______________________________________________________________________________ mpu_put PROC pushf 2000 ;Save flags (interrupt flag) ;Wait for MPU to be ready to receive mov cx,loop_delay ;Timeout loop limit mov dx,mpu_status_port sd_1: in al,dx test al,ready_to_receive? jz send_d ;MPU is ready, send data loop sd_1 ;MPU not ready yet, try again ;MPU not ready, exit with error xor ax,ax jmp short sd_exit ;Send the data byte send_d: cli mov al,databyte ;Get data byte mov dx,mpu_data_port out dx,al ;Send it ; in al,dx ;Purge data port mov ax,1 ;Set AX for no error sd_exit: sti popf ;Restore flags ret mpu_put ENDP mpu_get PROC clc mov di,buftail cmp di,bufhead jz notavail stc mov al,midibuf[di] ;return MIDI byte in al inc byte ptr[buftail] mov ah,byte ptr[miditicks] ;return time stamp in DX and AL mov dl,byte ptr[miditicks+1] mov dh,byte ptr[miditicks+2] notavail: ret mpu_get ENDP ;______________________________________________________________________________ ; ; void set_mpu_vector(void); ; Sets the interrupt vector for the MPU-401 ;______________________________________________________________________________ set_mpu_vector PROC ;Get current interrupt vector first and save mov ah,035h mov al,mpu_irq ;IRQ level to get int 21h mov cs:old_int_vect,bx ;Save vector offset. mov cs:old_int_vect+2,es ;Save vector segment. ;Set the IRQ vector to the new Interrupt Service Routine push ds ;Save DS mov ah,025h mov al,mpu_irq mov dx,offset MPU_ISR ;ISR offset mov bx,seg MPU_ISR mov ds,bx ;ISR segment int 21h ;Set new vector pop ds ;Restore DS ;Unmask the proper IRQ line in the Interrupt controller cli in al,0A1h ;Read second 8259 mask jmp short $+2 ;Delay for fast processors mov ah,irq_mask ;Get new mask not ah ;Invert for ANDing and al,ah ;Reset IRQ bit out 0A1h,al ;Enable IRQ smv_2: sti ret ;Exit set_mpu_vector ENDP ;______________________________________________________________________________ ; ; void reset_mpu_vector(void); ; Resets the interrupt vector for the MPU-401 ;______________________________________________________________________________ reset_mpu_vector PROC USES DS ;Mask off the IRQ line first cli in al,0A1h ;Read 8259 mask jmp short $+2 ;Delay for fast processors or al,irq_mask ;Set IRQ mask bit out 0A1h,al ;Mask IRQ off rmv_2: sti ;Then restore the old interrupt vector mov ah,25h mov al,mpu_irq mov dx,old_int_vect ;Put old offset into DX mov ds,old_int_vect+2 ;Put old segment into DS int 21h ;Reset the vector ret ;Exit reset_mpu_vector ENDP ;=========================================================================== ;========================== SOUNDBLASTER MIDI ============================ ;=========================================================================== ;***************************************************** ;* Assembler MIDI routines for SoundBlaster * ;* * ;* sbmidi_init * ;* sbmidi_exit * ;* sbmidi_get if carry set: AL = byte received * ;* if carry clear: no byte waiting * ;* sbmidi_put BL = midi byte to send * ;* * ;***************************************************** .DATA Org_MIDIIntIsr dd ? irqmask_save db ? irqnr db 5 intadr db ? .CODE PUBLIC sbmidi_init PUBLIC sbmidi_exit PUBLIC sbmidi_get PUBLIC sbmidi_put reset_dsp PROC push si mov dx,226h mov al,1 out dx,al mov cx,0ffh del: loop del mov al,0 out dx,al mov dx,22eh poll: in al,dx ;Check for data available or al,al jns poll mov dx,22ah in al,dx ;Check for ready byte cmp al,0aah jnz poll pop si ret reset_dsp ENDP ;========================================================================= SBMIDI_ISR PROC FAR assume CS:_TEXT,DS:@data push ax push dx push ds push di mov ax,@data mov ds,ax mov dx,22ah ;read MIDI byte in al,dx mov di,bufhead mov midibuf[di],al ;store byte in buffer inc byte ptr[bufhead] mov dx,22eh ;Acknowledge DSP interrupt in al,dx mov al,20h ;Signal End of Interrupt cmp irqnr,7 jbe not2 out 0a0h,al not2: out 20h,al pop di pop ds pop dx pop ax iret SBMIDI_ISR ENDP ;======================================================================== sbmidi_init PROC push si push cx call reset_dsp mov al,08h ;IRQ0-7 starts at INT 08h mov dl,irqnr cmp dl,7 jbe not_hi_irq1 add al,068h ;IRQ8-15 start at INT 70h sub dl,8 not_hi_irq1: add al,dl mov intadr,al mov al,intadr ;Fetch INT vector mov ah,35h int 21h mov word ptr[Org_MIDIIntIsr],bx ;Fetch Org_Rutinens OFFSET mov word ptr[Org_MIDIIntIsr+2],es ;Save segment push ds mov ah,25h ;Install MIDI ISR mov al,intadr push cs pop ds mov dx,offset SBMIDI_ISR int 21h pop ds mov al,08h ;H„mta vektorn till INT 08h mov ah,35h int 21h mov word ptr[Org_Int08Isr],bx ;Org_Rutinens OFFSET h„mtas mov word ptr[Org_Int08Isr+2],es ;Segment sparas push ds mov ah,25h ;Install TIMER ISR mov al,08h push cs pop ds mov dx,offset TIMER_ISR int 21h pop ds cli mov dx,43h ;Re-program system timer to mov al,36h ;1 ms resolution out dx,al mov dx,40h ;New divisor mov al,0a9h ;1193180/4a9h = 1000 Hz out dx,al mov al,04h out dx,al sti mov cl,irqnr mov dx,21h cmp cl,7 jbe not_hi_irq2 add dx,80h ;Interrupt mask reg for IRQ8-15 is at A1h sub cl,8 not_hi_irq2: in al,dx mov irqmask_save,al ;store away interrupt mask mov ah,1 shl ah,cl not ah and al,ah ;mask off IRQ out dx,al mov dx,22ch wd1: in al,dx or al,al js wd1 mov al,35h ;MIDI UART interrupt mode command out dx,al pop cx pop si ret sbmidi_init ENDP ;------------------------------------------------------------------------ sbmidi_get PROC clc mov di,buftail ;Byte waiting? cmp di,bufhead jz notavail stc ;Yes! mov al,midibuf[di] ;return MIDI byte in al inc byte ptr[buftail] ;update tail pointer mov ah,byte ptr[miditicks] ;return time stamp in DX and AL mov dl,byte ptr[miditicks+1] mov dh,byte ptr[miditicks+2] notavail: ret sbmidi_get ENDP sbmidi_put PROC mov dx,22ch wd3: in al,dx or al,al js wd3 mov al,bl out dx,al ret sbmidi_put ENDP ;------------------------------------------------------------------------ sbmidi_exit PROC push si call reset_dsp ;stop MIDI UART mode cli mov al,irqmask_save ;Restore interrupt mask mov dx,21h 2e1 cmp irqnr,7 jbe not_hi_irq4 add dx,080h ; Interrupt mask register for IRQ8-15 at A1h not_hi_irq4: out dx,al ; turn off used IRQ mov dx,43h mov al,36h out dx,al mov dx,40h ;¸terst„ll divisor f”r Timer 0 mov al,0h out dx,al mov al,0h out dx,al push ds ;Restore old INT 8 mov ah,25h mov al,08h mov dx,word ptr[Org_Int08Isr] mov ds,word ptr[Org_Int08Isr+2] int 21h pop ds push ds mov ah,25h ;Restore old INT used by MIDI mov al,intadr mov dx,word ptr[Org_MIDIIntIsr] mov ds,word ptr[Org_MIDIIntIsr+2] int 21h pop ds sti pop si ret sbmidi_exit ENDP END . 0