/***************************************************************************** * * * Program : PD module player * * By : Marq/F­t * * Version : 1.0 * * Description : Plays ST, NST & PT MODules with SB or DAC * * Implemented * * commands : Arpeggio (0), * * Porta Up (1), * * Porta Down (2), * * Tone Portamento (3), * * Vibrato (4), * * Tone & Volume Slide (5), * * Vibrato & Volume Slide (6), * * Tremolo (7), * * Sample offset (9), * * Volume Slide (A), * * Jump (B), * * Volume Set (C), * * Break Pattern (D), * * FineSlideUp (E1), * * FineSlideDn (E2), * * FineVolSlideUp (EA), * * FineVolSlideDn (EB), * * Speed Set (F) * * Other : Very much thanx to YZ­ & Pete/F­t! * * * *****************************************************************************/ #pragma inline /* Inline assembly used! */ #include /* Standard thingies */ #include /* Memory allocation */ #include /* Keyboard functions */ #include /* Port handling etc. */ #include /* File type definitions */ #include /* File handling */ #include /* Random numbers */ #define DEVICE 1 /* 1 for SB, 0 for DAC */ #define DEFAULT_VBMAX 240l /* DEFAULT_FREQ/50 */ #define DEFAULT_FREQ 12000l /* Default output frequency */ #define MAST_CLK 3583216l /* Amiga master clock speed */ #define VERSION "PD Player 1.0 by Marq/Fit" /* Version info */ unsigned port_add=0x220; /* Base addres of SB, change this for DAC or other SB address */ int modlencnt, /* Counts ptn positions */ prd_dest[4]={0,0,0,0}, /* Destination for tone portamento */ tei, period[4], /* Current Amiga period */ vol[4], /* Current volume */ SB=DEVICE, inst; /* # of instruments (15 or 31) */ char nimi[21], /* Module internal name */ snimi[31][23], /* Sample names */ vse[4]={0,0,0,0}, /* Volume slide */ fsp[4]={0,0,0,0}, /* Portamento */ mk[5]; unsigned char svol[31], /* Sample volumes */ modlen, /* Module length */ pos[128], /* Position numbers */ pmaara, /* # of patterns */ loop[31], *pdata[64], /* Pattern data */ *sdata[31], /* Sample data */ *modhead, /* Module header */ cmd, /* Used to store command number */ prm, /* - " - argument */ arp[4]={0,0,0,0}, /* Arpeggio */ on_vib[4]={0,0,0,0}, /* Vibrato on ? */ on_tre[4]={0,0,0,0}, /* Tremolo on ? */ on_ton[4]={0,0,0,0}, /* Tone portamento on ? */ toneport[4]={0,0,0,0}, /* Tone portamento */ trek[4]={0,0,0,0}, /* Tremolo amplitude */ tren[4]={0,0,0,0}, /* Tremolo speed */ vibk[4]={0,0,0,0}, /* Vibrato amplitude */ vibn[4]={0,0,0,0}, /* Vibrato speed */ finetune[31]; unsigned VBcnt=0, /* Counts from 0 to VBMAX */ lcnt, /* Counts rows */ pnt, /* Current pattern */ speed=6, /* Blanks per row (Def 6) */ spd, /* Again one output freq variable */ slen[31], /* Sample length */ roff[31], /* Repeat offset */ cslen[4], /* Current sample length */ rlen[31], /* Repeat length */ help, niin=0, /* Counts Amiga vertical blanks */ VBMAX, /* Timer ticks per Amiga VB */ sdec[4], /* Sample offset decimal part */ sseg[31], /* Sample segments */ OLD_VBMAX, cc, /* Channel counter */ temp, vib_off[4], /* Vibrato index */ tre_off[4], /* Tremolo index */ snum[4], /* Current sample number */ add_dec[5], /* Decimal addition of offset */ add_off[5], /* Addition of offset */ note[4], /* Current note */ p_off[1024], /* Add_offs for Amiga periods */ p_dec[1024], /* Add_decs for Amiga periods */ SB_DAC; /* SB's DAC address */ unsigned long sadd[4]; /* Sample pointers */ /* Amiga's periods */ unsigned a_period[]={ 856,808,762,720,678,640,604,570,538,508,480,453, /* 0 */ 428,404,381,360,339,320,302,285,269,254,240,226, 214,202,190,180,170,160,151,143,135,127,120,113, 850,802,757,715,674,637,601,567,535,505,477,450, /* 1 */ 425,401,379,357,337,318,300,284,268,253,239,225, 213,201,189,179,169,159,150,142,134,126,119,113, 844,796,752,709,670,632,597,563,532,502,474,447, /* 2 */ 422,398,376,355,335,316,298,282,266,251,237,224, 211,199,188,177,167,158,149,141,133,125,118,112, 838,791,746,704,665,628,592,559,528,498,470,444, /* 3 */ 419,395,373,352,332,314,296,280,264,249,235,222, 209,198,187,176,166,157,148,140,132,125,118,111, 832,785,741,699,660,623,588,555,524,495,467,441, /* 4 */ 416,392,370,350,330,312,294,278,262,247,233,220, 208,196,185,175,165,156,147,139,131,124,117,110, 826,779,736,694,655,619,584,551,520,491,463,437, /* 5 */ 413,390,368,347,328,309,292,276,260,245,232,219, 206,195,184,174,164,155,146,138,130,123,116,109, 820,774,730,689,651,614,580,547,516,487,460,434, /* 6 */ 410,387,365,345,325,307,290,274,258,244,230,217, 205,193,183,172,163,154,145,137,129,122,115,109, 814,768,725,684,646,610,575,543,513,484,457,431, /* 7 */ 407,384,363,342,323,305,288,272,256,242,228,216, 204,192,181,171,161,152,144,136,128,121,114,108, 907,856,808,762,720,678,640,604,570,538,508,480, /* -8 */ 453,428,404,381,360,339,320,302,285,269,254,240, 226,214,202,190,180,170,160,151,143,135,127,120, 900,850,802,757,715,675,636,601,567,535,505,477, /* -7 */ 450,425,401,379,357,337,318,300,284,268,253,238, 225,212,200,189,179,169,159,150,142,134,126,119, 894,844,796,752,709,670,632,597,563,532,502,474, /* -6 */ 447,422,398,376,355,335,316,298,282,266,251,237, 223,211,199,188,177,167,158,149,141,133,125,118, 887,838,791,746,704,665,628,592,559,528,498,470, /* -5 */ 444,419,395,373,352,332,314,296,280,264,249,235, 222,209,198,187,176,166,157,148,140,132,125,118, 881,832,785,741,699,660,623,588,555,524,494,467, /* -4 */ 441,416,392,370,350,330,312,294,278,262,247,233, 220,208,196,185,175,165,156,147,139,131,123,117, 875,826,779,736,694,655,619,584,551,520,491,463, /* -3 */ 437,413,390,368,347,328,309,292,276,260,245,232, 219,206,195,184,174,164,155,146,138,130,123,116, 868,820,774,730,689,651,614,580,547,516,487,460, /* -2 */ 434,410,387,365,345,325,307,290,274,258,244,230, 217,205,193,183,172,163,154,145,137,129,122,115, 862,814,768,725,684,646,610,575,543,513,484,457, /* -1 */ 431,407,384,363,342,323,305,288,272,256,242,228, 216,203,192,181,171,161,152,144,136,128,121,114}; /* Vibrato array */ int vib_table[64]={ 0,24,49,74,97,120,141,161,180,197,212,224,235,244,250,253, 255,253,250,244,235,224,212,197,180,161,141,120,97,74,49,24, 0,-24,-49,-74,-97,-120,-141,-161,-180,-197,-212,-224,-235,-244, -250,-253,-255,-253,-250,-244,-235,-224,-212,-197,-180,-161, -141,-120,-97,-74,-49,-24}; void main(int argc,char *argv[]); /* Main function */ void interrupt (*oldfunc)(); /* Old int 8h handler */ void DAC_handler(void); void SB_handler(void); void count_them(void); /* Precalculates p_off & p_dec */ void settimer(unsigned Hertz); /* Sets timer speed */ void resettimer(void); /* Resets - " - */ void timeback(void); /* Corrects DOS clock time */ unsigned bcd2d(unsigned bcd); /* BCD -> decimal */ int resetSB(int ba); void main(int argc,char *argv[]) { int n, /* Counter!!!! (!) */ hand; /* File handle */ unsigned l, /* Counter ... */ po, cnt; unsigned char temp[4], /* Used in pattern conversion */ calc[4], /* - " - */ c; printf("%s\n",VERSION); if(argc!=2 && argc!=3) { printf("Invalid parameters!\n"); return; } hand=open(argv[1],O_BINARY|O_RDONLY); /* Open the file */ if(hand==-1) { printf("File not found!\n"); return; } if(argv[2]!=NULL) /* If 2nd parameter: */ spd=atoi(argv[2]); /* Freq=parameter 2 */ else spd=DEFAULT_FREQ; /* Freq=default */ if(SB) /* If device is SB */ { if(spd>=4*1024u && spd<22*1024u) VBMAX=DEFAULT_VBMAX*(long)spd/DEFAULT_FREQ; else { printf("Invalid rate!\n"); return; } if(!resetSB(port_add)) { printf("No SB found!\n"); return; } SB_DAC=port_add+0xc; /* SB speaker ON */ while(inp(SB_DAC) & 0x80) ; outp(SB_DAC,0xd1); } else /* If device is DAC */ { if(spd>=4*1024u && spd<63*1024u) VBMAX=DEFAULT_VBMAX*(long)spd/DEFAULT_FREQ; else { printf("Invalid rate!\n"); return; } } printf("Loading and preparing..."); /* Check for the # of the instruments */ lseek(hand,1080,SEEK_SET); read(hand,mk,4); mk[4]=0; if(!stricmp(mk,"M.K.")) inst=31; else inst=15; lseek(hand,0,SEEK_SET); /* Get module name */ read(hand,nimi,20); nimi[20]=0; modhead=malloc(931); read(hand,modhead,inst*30); /* Read Sample info to memory */ for(n=0,help=0;n63) /* Volume */ svol[n]=64; roff[n]=modhead[help+26]; /* Repeat offset */ roff[n]<<=8; roff[n]+=modhead[help+27]; roff[n]*=2; rlen[n]=modhead[help+28]; /* Repeat length */ rlen[n]<<=8; rlen[n]+=modhead[help+29]; rlen[n]*=2; } read(hand,&modlen,1); /* Module length */ read(hand,NULL,1); /* NST repeat point */ read(hand,pos,128); /* Positions */ if(inst==31) read(hand,NULL,4); /* If 31 instruments, skip M.K. */ free(modhead); pmaara=pos[0]; /* Find the highest ptn number */ for(n=1;n<128;n++) if(pos[n]>pmaara) pmaara=pos[n]; pmaara++; for(n=0;n>4); po=(temp[0] & 0xf); po<<=8; po+=temp[1]; calc[1]=0; for(cnt=0;cnt<36;cnt++) if(po==a_period[cnt]) { calc[1]=cnt+1; break; } memmove(&pdata[n][l],calc,4); } } /* Bytes after the conversion: 1. Sample number 2. Note number 3. Command 4. Cmd parameter */ if(modlen==0) /* Check length */ { printf("Heh! A bit odd MOD!\n"); return; } VBcnt=VBMAX; /* Initialize some variables */ niin=5; /* for the first row & pos */ lcnt=63; modlencnt=-1; pnt=0; for(n=0;n<4;n++) /* All channels shut up */ { vol[n]=0; sadd[n]=65535u; } OLD_VBMAX=VBMAX; printf("\n"); oldfunc=getvect(0x8); /* Get old interrupt handler */ settimer(spd); /* Set timer speed */ disable(); if(SB) SB_handler(); /* Install new handler 4 int 8h */ else DAC_handler(); /* Install new handler 4 int 8h */ enable(); while(!kbhit()) /* Wait for a keystroke */ { outp(0x3c8,0); outp(0x3c9,random(64)); /* Make some action to screen */ outp(0x3c9,random(32)); outp(0x3c9,random(16)); } getch(); outp(0x3c8,0); /* Reset color 0 */ outp(0x3c9,0); outp(0x3c9,0); outp(0x3c9,0); resettimer(); setvect(0x8,oldfunc); /* Return the old int 8h handler */ timeback(); if(SB) resetSB(port_add); /* SB has to be reset */ } void count_them(void) { unsigned prd; unsigned long trap; for(prd=1;prd<1024;prd++) { trap=MAST_CLK/prd; trap<<=16; trap/=spd; p_off[prd]=trap>>16; p_dec[prd]=trap; } /* p_off[n]=word to add to the sample offset at period n, p_dec[n]=word to add to the sample offset decimal part at period n */ } void DAC_handler(void) { asm push ds asm push dx asm push ax /* Set a new handler for int 8h */ asm mov dx,seg handst asm mov ds,dx asm mov dx,offset handst asm mov al,8 asm mov ah,025h asm int 021h asm pop ax asm pop dx asm pop ds if(modlencnt!=-2) /* We don't need warnings */ return; pilvi: asm jmp uusiaika asm handst proc far asm push ax asm push bx asm push dx asm push es asm push ds asm mov dx,DGROUP /* DS points to data segment */ asm mov ds,dx asm mov ax,VBcnt /* Check for a new Amiga VB */ asm inc ax asm mov VBcnt,ax asm cmp ax,VBMAX asm jnb pilvi soittosoi: /* Update sample offsets */ asm xor dx,dx kan0: asm les bx,dword ptr sadd asm cmp bx,65535 asm je kan1 /* If sadd[n]=65535 then channel n is not on */ asm mov al,byte ptr es:[bx] /* Get sample byte */ asm imul byte ptr vol /* Multiply with volume */ asm add dx,ax /* Increase sample offset & decimal part */ asm mov ax,word ptr add_dec asm add word ptr sdec,ax asm adc bx,word ptr add_off asm mov word ptr sadd,bx asm cmp bx,cslen /* If the sample is not over */ asm jb kan1 /* Jump to next channel */ /* If there's no loop: shut up */ if(!loop[snum[0]]) asm mov word ptr sadd,65535 else { /* Else set sample offset to the repeat start */ asm mov bx,snum asm shl bx,1 asm mov ax,roff[bx] asm mov word ptr sadd,ax } kan1: asm les bx,dword ptr sadd+4 asm cmp bx,65535 asm je kan2 asm mov al,byte ptr es:[bx] asm imul byte ptr vol+2 asm add dx,ax asm mov ax,word ptr add_dec+2 asm add word ptr sdec+2,ax asm adc bx,word ptr add_off+2 asm mov word ptr sadd+4,bx asm cmp bx,cslen+2 asm jb kan2 if(!loop[snum[1]]) asm mov word ptr sadd+4,65535 else { asm mov bx,snum+2 asm shl bx,1 asm mov ax,roff[bx] asm mov word ptr sadd+4,ax } kan2: asm les bx,dword ptr sadd+8 asm cmp bx,65535 asm je kan3 asm mov al,byte ptr es:[bx] asm imul byte ptr vol+4 asm add dx,ax asm mov ax,word ptr add_dec+4 asm add word ptr sdec+4,ax asm adc bx,word ptr add_off+4 asm mov word ptr sadd+8,bx asm cmp bx,cslen+4 asm jb kan3 if(!loop[snum[2]]) asm mov word ptr sadd+8,65535 else { asm mov bx,snum+4 asm shl bx,1 asm mov ax,roff[bx] asm mov word ptr sadd+8,ax } kan3: asm les bx,dword ptr sadd+12 asm cmp bx,65535 asm je lyki_tavu asm mov al,byte ptr es:[bx] asm imul byte ptr vol+6 asm add dx,ax asm mov ax,word ptr add_dec+6 asm add word ptr sdec+6,ax asm adc bx,word ptr add_off+6 asm mov word ptr sadd+12,bx asm cmp bx,cslen+6 asm jb lyki_tavu if(!loop[snum[3]]) asm mov word ptr sadd+12,65535 else { asm mov bx,snum+6 asm shl bx,1 asm mov ax,roff[bx] asm mov word ptr sadd+12,ax } lyki_tavu: asm xor dh,128 /* Play calculated sample */ asm mov al,dh /* byte */ asm mov dx,port_add asm out dx,al asm mov al,020h /* Kiss PIC so that it knows */ asm out 020h,al /* the interrupt is over */ asm pop ds asm pop es asm pop dx asm pop bx asm pop ax asm iret /* Exit from the handler */ uusiaika: asm push cx asm push si asm push di asm push bp asm mov bp,sp VBcnt=0; niin++; if(niin!=speed) /* Update continuous commands */ { /* ie. new Amiga VB */ for(cc=0;cc<4;cc++) { asm mov bx,cc /* Check if the channel is on */ asm shl bx,1 asm shl bx,1 asm mov ax,word ptr sadd[bx] asm cmp ax,65535 asm jne onkylla asm jmp jaajaa onkylla: /* Channel is on */ if(vse[cc]) /* Volume slide */ { if(vol[cc]+vse[cc]<0) { vol[cc]=0; goto jaajaa; } if(vol[cc]+vse[cc]>64) vol[cc]=64; else vol[cc]+=vse[cc]; } if(on_ton[cc]) /* Tone portamento */ { if(prd_dest[cc]prd_dest[cc]) { period[cc]=prd_dest[cc]; on_ton[cc]=0; } } add_off[cc]=p_off[period[cc]]; add_dec[cc]=p_dec[period[cc]]; goto jaajaa; } if(on_vib[cc]) /* Vibrato */ { vib_off[cc]+=vibn[cc]; vib_off[cc]&=63; tei=vib_table[vib_off[cc]]*(int)(vibk[cc]); tei/=128; help=period[cc]+tei; add_off[cc]=p_off[help]; add_dec[cc]=p_dec[help]; goto jaajaa; } if(on_tre[cc]) /* Tremolo */ { tre_off[cc]+=tren[cc]; tre_off[cc]&=63; tei=vib_table[tre_off[cc]]*(int)(trek[cc]); tei/=64; vol[cc]=svol[cc]+tei; if(vol[cc]<0) vol[cc]=0; if(vol[cc]>63) vol[cc]=64; goto jaajaa; } if(fsp[cc]) /* Portamento */ { period[cc]-=fsp[cc]; if(period[cc]<113) period[cc]=113; if(period[cc]>856) period[cc]=856; add_off[cc]=p_off[period[cc]]; add_dec[cc]=p_dec[period[cc]]; goto jaajaa; } if(arp[cc]) /* Arpeggio */ { switch(niin%3) { case 0: help=period[cc]; break; case 1: help=note[cc]+36*finetune[snum[cc]] +(arp[cc]>>4); help=a_period[help]; break; case 2: help=note[cc]+36*finetune[snum[cc]] +(arp[cc] & 0xf); help=a_period[help]; break; } add_off[cc]=p_off[help]; add_dec[cc]=p_dec[help]; goto jaajaa; } jaajaa: ; } } else /* New row */ { niin=0; lcnt++; if(lcnt>63) /* New pattern ? */ { lcnt-=64; modlencnt++; if(modlencnt==modlen) /* End of module ? */ { for(cc=0;cc<4;cc++) sadd[cc]=65535; modlencnt=0; /* Start again */ } pnt=pos[modlencnt]; } temp=lcnt<<4; memset(&arp[0],0,4*4); /* Stop arp, vib, tre & tone prt */ for(cc=0;cc<4;cc++) { vse[cc]=0; /* Stop volume slide */ fsp[cc]=0; /* Stop portamento */ cmd=pdata[pnt][temp+2]; /* Get command */ prm=pdata[pnt][temp+3]; /* Get parameter */ if(cmd==0x5) { if(prm & 0xf) { if(prm>>4) vse[cc]=-(prm & 0xf)+prm>>4; else vse[cc]=-(prm & 0xf); } else vse[cc]=prm>>4; cmd=0x3; prm=0; goto eklipse; } if(cmd==0x6) { if(prm & 0xf) { if(prm>>4) vse[cc]=-(prm & 0xf)+prm>>4; else vse[cc]=-(prm & 0xf); } else vse[cc]=prm>>4; cmd=0x4; prm=0; } eklipse: if(pdata[pnt][temp+1]!=0) /* New note! */ { if(cmd!=0x3) /* If not tone port */ { note[cc]=pdata[pnt][temp+1]-1; sdec[cc]=0; asm mov bx,cc asm shl bx,1 asm shl bx,1 asm mov word ptr sadd[bx],0 /* If note & sample */ if(pdata[pnt][temp]) { snum[cc]=pdata[pnt][temp]-1; help=note[cc]+36*finetune[snum[cc]]; cslen[cc]=slen[snum[cc]]; asm mov bx,cc asm shl bx,1 asm mov ax,snum[bx] asm mov bx,ax asm shl bx,1 asm mov ax,sseg[bx] asm mov bx,cc asm shl bx,1 asm shl bx,1 asm mov word ptr sadd[bx+2],ax } else /* If only note */ { help=note[cc]+36*finetune[snum[cc]]; } period[cc]=a_period[help]; add_off[cc]=p_off[period[cc]]; add_dec[cc]=p_dec[period[cc]]; } else /* If tone portamento */ { help=pdata[pnt][temp+1]-1; prd_dest[cc]=a_period[help]; } } /* If sample specified, set channel volume */ if(pdata[pnt][temp]) vol[cc]=svol[pdata[pnt][temp]-1]; /* Check for the commands */ if(cmd || prm) { switch(cmd) { /* Arpeggio */ case 0 : arp[cc]=prm; break; /* Portamento Up & Dn */ case 0x1 : fsp[cc]=prm; break; case 0x2 : fsp[cc]=-prm; break; /* Tone portamento */ case 0x3 : on_ton[cc]=1; if(prm) toneport[cc]=prm; break; /* Vibrato */ case 0x4 : on_vib[cc]=1; if(prm>>4) vibn[cc]=((prm>>4) & 0xf); if(prm & 0xf) vibk[cc]=prm & 0xf; break; /* Tremolo */ case 0x7 : on_tre[cc]=1; if(prm>>4) tren[cc]=((prm>>4) & 0xf); if(prm & 0xf) trek[cc]=prm & 0xf; break; /* Sample offset */ case 0x9 : sadd[cc]&=0xffff0000; sadd[cc]+=((unsigned)prm)<<8u; if(sadd[cc]&0xffff>=cslen[cc]) sadd[cc]&=0xffff0000; break; /* Volume slide */ case 0xa : if(prm & 0xf) { if(prm>>4) vse[cc]=-(prm & 0xf)+prm>>4; else vse[cc]=-(prm & 0xf); } else vse[cc]=prm>>4; break; /* Jump */ case 0xb : lcnt=63; modlencnt=prm-1; break; /* Volume set */ case 0xc : vol[cc]=prm; break; /* Pattern break */ case 0xd : lcnt=63+(prm & 0xf)+(prm>>4)*10; break; /* Extended commands */ case 0xe : switch(prm>>4) { case 0x1: help=period[cc]-(prm & 0xf); add_off[cc]=p_off[help]; add_dec[cc]=p_dec[help]; break; case 0x2: help=period[cc]+(prm & 0xf); add_off[cc]=p_off[help]; add_dec[cc]=p_dec[help]; break; case 0xb: vol[cc]-=prm&0xf; if(vol[cc]<0) vol[cc]=0; break; case 0xa: vol[cc]+=prm&0xf; if(vol[cc]>64) vol[cc]=64; break; } break; /* Speed & Tempo command */ case 0xf : if(prm<=0x20) speed=prm; else VBMAX=OLD_VBMAX*125u/(unsigned)prm; break; } } uus: temp+=4; } } asm mov sp,bp asm pop bp asm pop di asm pop si asm pop cx asm jmp soittosoi asm handst endp } void SB_handler(void) { asm push ds asm push dx asm push ax /* Set a new handler for int 8h */ asm mov dx,seg SB_handst asm mov ds,dx asm mov dx,offset SB_handst asm mov al,8 asm mov ah,025h asm int 021h asm pop ax asm pop dx asm pop ds if(modlencnt!=-2) /* We don't want to get warnings... */ return; pilvi: asm jmp uusiaika asm SB_handst proc far asm push ax asm push dx asm push ds asm mov dx,DGROUP /* DS points to data segment */ asm mov ds,dx asm mov dx,SB_DAC hidas: asm in al,dx asm rcl al,1 asm jc hidas asm mov al,010h asm out dx,al asm push bx asm push es asm mov ax,VBcnt /* Check for a new Amiga VB */ asm inc ax asm mov VBcnt,ax asm cmp ax,VBMAX asm jnb pilvi soittosoi: /* Update sample offsets */ asm xor dx,dx kan0: asm les bx,dword ptr sadd asm cmp bx,65535 asm je kan1 /* If sadd[n]=65535 then channel n is not on */ asm mov al,byte ptr es:[bx] /* Get sample byte */ asm imul byte ptr vol /* Multiply with volume */ asm add dx,ax /* Increase sample offset & decimal part */ asm mov ax,word ptr add_dec asm add word ptr sdec,ax asm adc bx,word ptr add_off asm mov word ptr sadd,bx asm cmp bx,cslen /* If the sample is not over */ asm jb kan1 /* Jump to next channel */ /* If there's no loop: shut up */ if(!loop[snum[0]]) asm mov word ptr sadd,65535 else { /* Else set sample offset to the repeat start */ asm mov bx,snum asm shl bx,1 asm mov ax,roff[bx] asm mov word ptr sadd,ax } kan1: asm les bx,dword ptr sadd+4 asm cmp bx,65535 asm je kan2 asm mov al,byte ptr es:[bx] asm imul byte ptr vol+2 asm add dx,ax asm mov ax,word ptr add_dec+2 asm add word ptr sdec+2,ax asm adc bx,word ptr add_off+2 asm mov word ptr sadd+4,bx asm cmp bx,cslen+2 asm jb kan2 if(!loop[snum[1]]) asm mov word ptr sadd+4,65535 else { asm mov bx,snum+2 asm shl bx,1 asm mov ax,roff[bx] asm mov word ptr sadd+4,ax } kan2: asm les bx,dword ptr sadd+8 asm cmp bx,65535 asm je kan3 asm mov al,byte ptr es:[bx] asm imul byte ptr vol+4 asm add dx,ax asm mov ax,word ptr add_dec+4 asm add word ptr sdec+4,ax asm adc bx,word ptr add_off+4 asm mov word ptr sadd+8,bx asm cmp bx,cslen+4 asm jb kan3 if(!loop[snum[2]]) asm mov word ptr sadd+8,65535 else { asm mov bx,snum+4 asm shl bx,1 asm mov ax,roff[bx] asm mov word ptr sadd+8,ax } kan3: asm les bx,dword ptr sadd+12 asm cmp bx,65535 asm je lyki_tavu asm mov al,byte ptr es:[bx] asm imul byte ptr vol+6 asm add dx,ax asm mov ax,word ptr add_dec+6 asm add word ptr sdec+6,ax asm adc bx,word ptr add_off+6 asm mov word ptr sadd+12,bx asm cmp bx,cslen+6 asm jb lyki_tavu if(!loop[snum[3]]) asm mov word ptr sadd+12,65535 else { asm mov bx,snum+6 asm shl bx,1 asm mov ax,roff[bx] asm mov word ptr sadd+12,ax } lyki_tavu: asm mov al,020h /* Inform PIC of the end */ asm out 020h,al /* of the interrupt */ asm pop es asm pop bx asm xor dh,128 /* Play calculated sample */ asm mov al,dh /* byte */ asm mov dx,SB_DAC asm out dx,al asm pop ds asm pop dx asm pop ax asm iret /* Exit from the handler */ uusiaika: asm push cx asm push si asm push di asm push bp asm mov bp,sp VBcnt=0; niin++; if(niin!=speed) /* Update continuous commands */ { /* ie. new Amiga VB */ for(cc=0;cc<4;cc++) { asm mov bx,cc /* Check if the channel is on */ asm shl bx,1 asm shl bx,1 asm mov ax,word ptr sadd[bx] asm cmp ax,65535 asm jne onkylla asm jmp jaajaa onkylla: /* Channel is on */ if(vse[cc]) /* Volume slide */ { if(vol[cc]+vse[cc]<0) { vol[cc]=0; goto jaajaa; } if(vol[cc]+vse[cc]>64) vol[cc]=64; else vol[cc]+=vse[cc]; } if(on_ton[cc]) /* Tone portamento */ { if(prd_dest[cc]prd_dest[cc]) { period[cc]=prd_dest[cc]; on_ton[cc]=0; } } add_off[cc]=p_off[period[cc]]; add_dec[cc]=p_dec[period[cc]]; goto jaajaa; } if(on_vib[cc]) /* Vibrato */ { vib_off[cc]+=vibn[cc]; vib_off[cc]&=63; tei=vib_table[vib_off[cc]]*(int)(vibk[cc]); tei/=128; help=period[cc]+tei; add_off[cc]=p_off[help]; add_dec[cc]=p_dec[help]; goto jaajaa; } if(on_tre[cc]) /* Tremolo */ { tre_off[cc]+=tren[cc]; tre_off[cc]&=63; tei=vib_table[tre_off[cc]]*(int)(trek[cc]); tei/=64; vol[cc]=svol[cc]+tei; if(vol[cc]<0) vol[cc]=0; if(vol[cc]>63) vol[cc]=64; goto jaajaa; } if(fsp[cc]) /* Portamento */ { period[cc]-=fsp[cc]; if(period[cc]<113) period[cc]=113; if(period[cc]>856) period[cc]=856; add_off[cc]=p_off[period[cc]]; add_dec[cc]=p_dec[period[cc]]; goto jaajaa; } if(arp[cc]) /* Arpeggio */ { switch(niin%3) { case 0: help=period[cc]; break; case 1: help=note[cc]+36*finetune[snum[cc]] +(arp[cc]>>4); help=a_period[help]; break; case 2: help=note[cc]+36*finetune[snum[cc]] +(arp[cc] & 0xf); help=a_period[help]; break; } add_off[cc]=p_off[help]; add_dec[cc]=p_dec[help]; goto jaajaa; } jaajaa: ; } } else /* New row */ { niin=0; lcnt++; if(lcnt>63) /* New pattern ? */ { lcnt-=64; modlencnt++; if(modlencnt==modlen) /* End of module ? */ { for(cc=0;cc<4;cc++) sadd[cc]=65535; modlencnt=0; /* Start again */ } pnt=pos[modlencnt]; } temp=lcnt<<4; memset(&arp[0],0,4*4); /* Stop arp, vib, tre & tone prt */ for(cc=0;cc<4;cc++) { vse[cc]=0; /* Stop volume slide */ fsp[cc]=0; /* Stop portamento */ cmd=pdata[pnt][temp+2]; /* Get command */ prm=pdata[pnt][temp+3]; /* Get parameter */ if(cmd==0x5) { if(prm & 0xf) { if(prm>>4) vse[cc]=-(prm & 0xf)+prm>>4; else vse[cc]=-(prm & 0xf); } else vse[cc]=prm>>4; cmd=0x3; prm=0; goto eklipse; } if(cmd==0x6) { if(prm & 0xf) { if(prm>>4) vse[cc]=-(prm & 0xf)+prm>>4; else vse[cc]=-(prm & 0xf); } else vse[cc]=prm>>4; cmd=0x4; prm=0; } eklipse: if(pdata[pnt][temp+1]!=0) /* New note! */ { if(cmd!=0x3) /* If not tone port */ { note[cc]=pdata[pnt][temp+1]-1; sdec[cc]=0; asm mov bx,cc asm shl bx,1 asm shl bx,1 asm mov word ptr sadd[bx],0 /* If note & sample */ if(pdata[pnt][temp]) { snum[cc]=pdata[pnt][temp]-1; help=note[cc]+36*finetune[snum[cc]]; cslen[cc]=slen[snum[cc]]; asm mov bx,cc asm shl bx,1 asm mov ax,snum[bx] asm mov bx,ax asm shl bx,1 asm mov ax,sseg[bx] asm mov bx,cc asm shl bx,1 asm shl bx,1 asm mov word ptr sadd[bx+2],ax } else /* If only note */ { help=note[cc]+36*finetune[snum[cc]]; } period[cc]=a_period[help]; add_off[cc]=p_off[period[cc]]; add_dec[cc]=p_dec[period[cc]]; } else /* If tone portamento */ { help=pdata[pnt][temp+1]-1; prd_dest[cc]=a_period[help]; } } /* If sample specified, set channel volume */ if(pdata[pnt][temp]) vol[cc]=svol[pdata[pnt][temp]-1]; /* Check for the commands */ if(cmd || prm) { switch(cmd) { /* Arpeggio */ case 0 : arp[cc]=prm; break; /* Portamento Up & Dn */ case 0x1 : fsp[cc]=prm; break; case 0x2 : fsp[cc]=-prm; break; /* Tone portamento */ case 0x3 : on_ton[cc]=1; if(prm) toneport[cc]=prm; break; /* Vibrato */ case 0x4 : on_vib[cc]=1; if(prm>>4) vibn[cc]=((prm>>4) & 0xf); if(prm & 0xf) vibk[cc]=prm & 0xf; break; /* Tremolo */ case 0x7 : on_tre[cc]=1; if(prm>>4) tren[cc]=((prm>>4) & 0xf); if(prm & 0xf) trek[cc]=prm & 0xf; break; /* Sample offset */ case 0x9 : sadd[cc]&=0xffff0000; sadd[cc]+=((unsigned)prm)<<8u; if(sadd[cc]&0xffff>=cslen[cc]) sadd[cc]&=0xffff0000; break; /* Volume slide */ case 0xa : if(prm & 0xf) { if(prm>>4) vse[cc]=-(prm & 0xf)+prm>>4; else vse[cc]=-(prm & 0xf); } else vse[cc]=prm>>4; break; /* Jump */ case 0xb : lcnt=63; modlencnt=prm-1; break; /* Volume set */ case 0xc : vol[cc]=prm; break; /* Pattern break */ case 0xd : lcnt=63+(prm & 0xf)+(prm>>4)*10; break; /* Extended commands */ case 0xe : switch(prm>>4) { case 0x1: help=period[cc]-(prm & 0xf); add_off[cc]=p_off[help]; add_dec[cc]=p_dec[help]; break; case 0x2: help=period[cc]+(prm & 0xf); add_off[cc]=p_off[help]; add_dec[cc]=p_dec[help]; break; case 0xb: vol[cc]-=prm&0xf; if(vol[cc]<0) vol[cc]=0; break; case 0xa: vol[cc]+=prm&0xf; if(vol[cc]>64) vol[cc]=64; break; } break; /* Speed & Tempo command */ case 0xf : if(prm<=0x20) speed=prm; else VBMAX=OLD_VBMAX*125u/(unsigned)prm; break; } } uus: temp+=4; } } asm mov sp,bp asm pop bp asm pop di asm pop si asm pop cx asm jmp soittosoi asm SB_handst endp } void settimer(unsigned Hertz) { unsigned help; help=(unsigned)(1193182/(long)Hertz); outp(0x40,help & 0xff); outp(0x40,help >> 8); outp(0x43,0); } void resettimer(void) { outp(0x40,0xff); outp(0x40,0xff); outp(0x43,0); } void timeback(void) { struct REGPACK reg1,reg2; /* Pete coded this function so I don't really know how it works... It just works! */ reg1.r_ax=0x0200; intr(0x1a,®1); reg1.r_cx=(bcd2d(reg1.r_cx>>8)<<8)+bcd2d(reg1.r_cx&0xff); reg1.r_dx=bcd2d(reg1.r_dx>>8)<<8; reg2.r_ax=0x0400; intr(0x1a,®2); reg2.r_cx=bcd2d(reg2.r_cx>>8)*100+bcd2d(reg2.r_cx&0xff); reg2.r_dx=(bcd2d(reg2.r_dx>>8)<<8)+bcd2d(reg2.r_dx&0xff); reg2.r_ax=0x2b00; intr(0x21,®2); reg1.r_ax=0x2d00; intr(0x21,®1); } unsigned bcd2d(unsigned bcd) { unsigned apu; apu=bcd/16; apu=bcd-16*apu+apu*10; return(apu); } int resetSB(int ba) { unsigned tries=100; /* Try 100 times before exit */ outp(ba+6,1); delay(1); outp(ba+6,0); delay(1); /* SB will return Ah if it is reset */ while(inp(ba+0xa)!=0xaa && tries) { outp(ba+6,1); delay(1); outp(ba+6,0); delay(1); tries--; } if(!tries) return(0); /* SB Not found */ else return(1); /* SB found */ } /* EOS */