org 100h ;-----------------------------------------------; ; TSR 64-byte MIDI tracker MP-401 GM1 Soundfonts; ; ver. 0.97 ; ;-----------------------------------------------; ; patterns in reverse order and nibble-crunched ; ;-----------------------------------------------; ; ax=general,dx=MPU data @ ,si=RTC delay ; ; bx=general,cx=pattern idx,di=track idx ; ; on entry : ds=cs,fs=0,ah=0,ch=0,di=0 ;-----------------------------------------------; loadsong:mov dx,playsong ; set tsr isr vector lsw segment mov ax,251ch ; ah=function set interrupt vector, al=interrupt number=rtc system timer tick ds:dx=new isr vector int 21h ; general dos interrupt jmp $ playsong:push word cs ; isr called by cmos rtc tick 1024 times/second pop ds ; reset ds=cs in isr for correct data indexing mov si,songdata xor byte [si],1 ; set playing speed to 9.3 khz (18.2*1024/2) jz exitsong ; odd/even flipflop says not time to play another note lodsw ; ah=16 al=fliflop (don't care=3fh) si=tracks index (was mov si,traxidx) mov di,pattidx ; di=pattern index (base adressing size optimization) dec byte [di] ; [->test pattern end] check if switch track number required (was dec byte [pattidx]) jnz track ; else skip module: inc byte [si] ; increment track index (was inc word [traxidx]) mov byte [di],ah ; and reset pattern index (was mov byte [di],16 / mov byte [pattidx],16) track: mov bx,tracks ; [->get pattern from track] bl=track index (was mov bx,byte [cs:trackidx]) lodsb ; ax=track index (was mov ax,[si] / mov ax,[traxidx]) ! si now points to MIDI command note on 7fh xlatb ; al=track index (was mov bx,byte [cs:trackidx]) al=track value shl al,3 ; al=relative offset of pattern position value mov bl,byte [di] ; [->get 2 notes from from pattern index] (was mov bl,byte [pattidx]) shr bl,1 ; divide by 2 for dual note 2 nibbles pushf;lahf ; pushf ; [->get odd/even note index from pattern index] save CF (was test byte [cs:patternidx],1) add al,bl ; bl=absolute note value nibble pair mov bx,patterns ; bx=absolute offset of pattern position value xlatb ; [get 2 notes from from pattern index->] al=instrument index popf;sahf ; popf ; [get odd/even note index from pattern index->] restore CF (was test byte [cs:patternidx],1) jc decrunch ; if pattern index odd then get note from MSNibble shr al,4 ; else if pattern index odd then get note from LSNibble decrunch: ;---- Load MIDI --------------------------------; <---- prequisites : none ----> al=099h/09ah loadmidi:mov bx,instrmt ; ds:bx=address of instruments list mov bp,mididata ; bp=mididata+000h ; and al,00fh ; al=note/instrument nibble index from even/odd pattern index (aas ?:) ; jz sendsapi ; 000h=instrmt+000h=sapi vocoder xlatb ; al=instrument index (al=[bx+ax]) shr al,1 ; al=instrument value, cf=instrument index LSB (select channel part 1/4) mov [bp+005h],al ; note value status data=al salc ; al=255 if cf=1, al=0 if cf=0 (select channel part 2/4) sub al,066h ; al=099h/09ah (select channel part 3/4) mov [bp+004h],al ; channel select status command=al add al,030h ; al=0c9h/0cah (select channel part 4/4) mov [bp+002h],al ; program change status command=al sendmidi:mov dx,00331h ; dx=midi command port mov si,bp ; ds:si=address of midi data message outsb ; output midi command dec dx ; dx=midi data/status port 00330h mov cl,7 ; cl=length-1 of midi data message rep outsb ; output data/status to midi exitsong:iret ; return to caller ;-----------------------------------------------; b7b6b5b4b3b2b1<<1=note/instrument, b0=channel# (b0=0=>channel#11, b1=1=>channel#10) instrmt db 000h ; instrmt+000h 00000000b ; 00h,98h/00,152 ; #0 (n/a 32 of*98h) SAPI vocoder db 3ch ; instrmt+001h to tweak 10100000b ; 50h,98h/80,152 ; #1 (string 80 of 98h) GM1 synth db 2 ; instrmt+002h was 001h ; 00000001b ; 00h,99h/00,153 ; #2 (percussion 00 of*99h) GM1 silence db 049h ; instrmt+003h 01001001b ; 24h,99h/36,153 ; #3 (percussion 36 of 99h) GM1 Bass Drum 1 db 04bh ; instrmt+004h 01001011b ; 25h,99h/37,153 ; #4 (percussion 37 of 99h) GM1 Side Stick/Rimshot db 04dh ; instrmt+005h 01001101b ; 26h,99h/38,153 ; #5 (percussion 38 of 99h) GM1 Snare Drum 1 db 055h ; instrmt+006h 01010101b ; 2ah,99h/42,153 ; #6 (percussion 42 of 99h) GM1 Closed Hi-hat db 0a5h ; instrmt+007h 10100101b ; 52h,99h/82,153 ; #7 (percussion 82 of 99h) GM1 Shaker ;-----------------------------------------------; byte offset nibble description mididata db 03fh ; mididata+000h cstcst set midi mode status command (03fh=set UART mode on) db 000h ; mididata+001h cstcst bank select status command db 000h ; mididata+002h cstvar program change status command (0c9h=channel#10, 0cah=channel#11) db 020h ; mididata+003h varvar program number status data=acoustic base (cf. en.wikipedia.org/wiki/General_MIDI) db 000h ; mididata+004h cstvar channel select status command (099h=channel#10, 09ah=channel#11) db 000h ; mididata+005h varvar note value status data (C/do C#/do# D/re D#/re# E/mi F/fa F#/fa# G/sol G#/sol# A/la A#/la# B/si) db 040h ; mididata+006h varvar note volume status data (ranges from 000h to 040h) db 07fh ; mididata+007h cstcst note one status command ;-----------------------------------------------; byte offset description songdata db 01h ; songdata+000h binary flipflop pattsize db 10h ; pattern #0 (dummy placeholder) size of one uncrunched pattern=16 traxidx db 00h ; pattern #0 (dummy placeholder) track index pattidx db 01h ; pattern #0 (dummy placeholder) pattern index (off by 1 for pre-emptive dec @ start of isr ie: while vs for) patterns db 72h,32h,72h,32h,72h,32h,72h,31h ; pattern #1 nibble crunched (ie: pattern length=16!=8) and reversed from right to left (ie: first note=3!=7) db 37h,32h,72h,32h,72h,32h,72h,32h ; pattern #2 nibble crunched (ie: pattern length=16!=8) and reversed from right to left tracks db 0,1,0,1,0,0,0