/****************************** PLAY AMD *************************************
         Библиотека для проигрывания музыкальных файлов формата *.amd на SB.
 Детектирование параметров SB и его присутствие не производится.
 Предполагается, что Ваш SB имеет port=220h. Если Вы хотите использовать
 другой порт, то надо изменить константу ADLIBPORT на real_port_SB + 8.
 Если же вы используете только Adlib, то эту константу надо установить
 равной 388h.
         По умолчанию музыкальный файл должен быть загружен в текущий сегмент
 памяти (model tiny CS=DS). Если Вам надо загрузить музыкальный файл в
 другой сегмент, то перед включением в программу этого модуля, надо объявить
 константу _FARMODULE_. Пример:
 #define _FARMODULE_ TRUE
 #include "amd.h--"
-----------------------------------------------------------------------------
         Library for playing musical files of a format *.amd on SB.
 The detecting of parameters SB and it presence is not made.
 It is supposed, that yours of SB has port=220h. If you want to use other
 port, it is necessary changed a constant ADLIBPORT on real_port_SB + 8.
 If you use only Adlib, this constant should be placed equal 388h.
         By default the musical file should be loaded in current segment of
 memory (model tiny CS = DS). If you should load the musical file in other
 segment, before inclusion in the program of this module, it is necessary
 to declare a constant _FARMODULE_. An example:
 #define _FARMODULE_ TRUE
 #include "amd.h--"
 **************************************************************************/

#define ADLIBPORT 0x228
#define ResetDSPPort ADLIBPORT-2
#define FreqStart 0x156  // low end of frequency in each octave
#define FreqRange 0x2AE-FreqStart
#define WrkBuff_TrackPtr   0x480 //Offsets in current tracks
#define WrkBuff_TrackDelay 0x492 //Delay for current tracks
#define WrkBuff_CurrPtn    0x49B
#define WrkBuff_CurrLine   0x49C
#define WrkBuff_SongDelay  0x49D
#define WrkBuff_CurrSpeed  0x49E
#define WrkBuff_CurrEvent  0x49F //privat Buffer!!!
#define WrkBuff_LastEvent  0x4A4
#define WrkBuff_CarrVol    0x510
#define WrkBuff_ModVol     0x519
#define WrkBuffSize        0x522
#define ev_pitch      0
#define ev_oct        1
#define ev_sam        2
#define ev_cmd        3
#define ev_params     4
#define ev_vol        5   //lastvol
#define ev_arpeggio   6
#define ev_pitching   7
#define ev_tpdest     9
#define ev_lastparams 11  //for TonePortamento and stuff!!!
#define LastEventSize 12

byte WrkBuff[WrkBuffSize];
byte AdlibState=0;      //set to zero if adlib not detected!!!
byte breakstate=0;
byte breakline=0;
byte breakptn=0;
byte Adlib_BD=0;   //contents of register BDh
byte ChannelOffs={20h,21h,22h,28h,29h,2ah,30h,31h,32h};
byte VolTab={0,95/31,182/31,262/31,336/31,405/31,470/31,530/31,587/31,641/31,
        693/31,741/31,788/31,832/31,875/31,916/31,955/31,993/31,1029/31,
        1064/31,1098/31,1131/31,1163/31,1193/31,1223/31,1252/31,1280/31,
        1308/31,1335/31,1360/31,1386/31,1410/31,1435/31,1458/31,1481/31,
        1504/31,1526/31,1547/31,1568/31,1589/31,1609/31,1629/31,1648/31,
        1667/31,1686/31,1704/31,1722/31,1740/31,1757/31,1774/31,1791/31,
        1808/31,1824/31,1840/31,
        1856/31,1871/31,1887/31,1902/31,1916/31,1931/31,1945/31,1960/31,
        1974/31,1980/31};
word FreqParams={343,363,385,408,432,458,485,514,544,577,611,647};
word oldint08[2];
#ifdef _FARMODULE_
word ModPtr[2];
#else
word ModPtr;
#endif
word CmdPtrTab1={#InitArpeggio,#SlideFreqUp,#SlideFreqDown,0,#SetInsVol,
                #JumpPattern,#PtnBreak,#SetSongSpeed,#InitTonePortamento,
                #ExtCmds,0,0,0,0,0,0};
word CmdPtrTab2={#Arpeggio,#SlideFreqUp,#SlideFreqDown,0,0,0,0,0,
                #TonePortamento,#ExtCmds2,0,0,0,0,0,0};
word ExtCmdsTab={#CellTrem,#CellVib,#SlideVolUp,#SlideVolDown,#SlideVolUp,
                #SlideVolDown,0,0,0,0};
word ExtCmdsTab2={0,0,#SlideVolUp,#SlideVolDown,0,0,0,0,0,0};
word timercnt;
byte AdlibPlayerStatus=0; // (0 - плейер не работает, 1 - работает)

void DataSegment()
inline
{
        $DW 0
}

/*----- InitAdlib ----------
 This proc fills the work buffer with pointers, defaults and stuff.
 Call this to create a buffer for a tune!!!

ES:SI = Modulepointer   if far buffer
SI = Modulepointer if near buffer */
void fastcall InitAdlib(SI)
{
        $pusha
        ModPtr=SI;
#ifdef _FARMODULE_
        ModPtr[2]=ES;
#endif
        DSWORD[#DataSegment]=DS;
        Adlib_Reset();
//---- Clear Workbuffer
#ifdef _FARMODULE_
        $push DS
        DS><ES;
#else
        ES=DS;
#endif
        DI=#WrkBuff;
        AX=0;
        CX=WrkBuffSize/2;
        $rep $stosw
//---- Init trackpointer
        BX=DSBYTE[SI+933]+1;          //number of patterns
        $imul BX,18
        $lea SI,DSWORD[SI+BX+1072];   //offset of the tracks
        $lodsw
#ifdef codesize
        AX><CX;
#else
        CX=AX;
#endif
        loop(CX){
                $lodsw                        //read trackposition
#ifdef codesize
                AX><BX;
#else
                BX=AX;
#endif
                BX+=BX;
#ifdef _FARMODULE_
                ESWORD[#WrkBuff+BX]=SI; //store pointer into trackptrtab!
#else
                DSWORD[#WrkBuff+BX]=SI;
#endif
                DL=0;
                do{
                        $lodsb
                        $test AL,128
                        IF(ZEROFLAG){
                                $lodsw
                                DL++;
                        }
                        ELSE{
                                AL&=127;
                                DL+=AL;
                        }
                }while(DL<64);
        }
#ifdef _FARMODULE_
        $pop DS
#endif
        DSWORD[#WrkBuff+WrkBuff_SongDelay]=0x606;//default tempo!!!
        timercnt=3;
        $cli
        ES=0;
        DI=32;
        DSDWORD[#oldint08]=ESDWORD[DI];
        AX=#TimerProc;
        $stosw
        AX=CS;
        $stosw
        AX=0x5D37;        //50Hz
        outp(AL,0x40);
        outp(AH,0x40);
        $sti
        $popa
 // Плейер активен
 AdlibPlayerStatus=TRUE;
}

interrupt TimerProc()
{
        $push AX,DS,SI
        DS=CSWORD[#DataSegment];
#ifdef _FARMODULE_
        $push ES
        $les SI,DSDWORD[#ModPtr]
#else
        SI=ModPtr;
#endif
        AdlibPlayer();
        timercnt--;
        IF(ZEROFLAG){
                $PUSHF
                $CALL FAR oldint08      //вызов старого ОП
                timercnt=3;
        }ELSE @EOI();
#ifdef _FARMODULE_
        $pop ES
#endif
        $pop SI,DS,AX
}

/*------ Adlib Player --------
 This is the mainfunction of the player jump into this code 50 times a sec.
 (or what ever your songtempo is)

ES:SI = Modulepointer   if far buffer
SI = Modulepointer if near buffer */
void AdlibPlayer()
{
        if(AdlibState){
                $pusha
                AL=WrkBuff[WrkBuff_SongDelay];
                AL--;
                if(ZEROFLAG){
                        WrkBuff[WrkBuff_SongDelay]=WrkBuff[WrkBuff_CurrSpeed];
                        IF(WrkBuff[WrkBuff_CurrLine]==0)CalcTrackPtr();
                        breakstate=0;
                        CX=0;
                        do{
                                BX=CX;
                                AL=WrkBuff[WrkBuff_TrackDelay+BX]&127;
                                IF(NOTZEROFLAG){
                                        WrkBuff[WrkBuff_TrackDelay+BX]--;
                                        goto ap_StillWaiting;
                                }
                                WrkBuff[WrkBuff_TrackDelay+BX]=0;
                                BX+=BX;
                                DI=DSWORD[WrkBuff_TrackPtr+#WrkBuff+BX];
#ifdef _FARMODULE_
                                AL=ESBYTE[DI];  //Is it a delay??? Then wait!!!
#else
                                AL=DSBYTE[DI];
#endif
                                $test AL,128
                                IF(NOTZEROFLAG){
                                        AL--;
                                        DSWORD[WrkBuff_TrackPtr+#WrkBuff+BX]++;
                                        BX=CX;
                                        WrkBuff[WrkBuff_TrackDelay+BX]=AL;
                                        goto ap_StillWaiting;
                                }
                                DSWORD[WrkBuff_TrackPtr+#WrkBuff+BX]+=3;
//---- Decode current event
                                WrkBuff[WrkBuff_CurrEvent+ev_params]=AL;
#ifdef _FARMODULE_
                                AX=ESWORD[DI+1];
#else
                                AX=DSWORD[DI+1];
#endif
                                DL=AL&0xF;
                                WrkBuff[WrkBuff_CurrEvent+ev_cmd]=DL;
                                DX=AX>>4;
                                DL&=0b11111;
                                WrkBuff[WrkBuff_CurrEvent+ev_sam]=DL;
                                AL=AH>>1&7;
                                WrkBuff[WrkBuff_CurrEvent+ev_oct]=AL;
                                AH>>=4;
                                WrkBuff[WrkBuff_CurrEvent+ev_pitch]=AH;
//############# update channel !! ###########
                                $imul DI,CX,LastEventSize
//--- change instrument???
                                AL=WrkBuff[WrkBuff_CurrEvent+ev_sam];
                                IF(AL!=0){
                                        WrkBuff[WrkBuff_LastEvent+ev_sam+DI]=AL;  //update instrument
                                        WrkBuff[WrkBuff_LastEvent+ev_vol+DI]=0x3F;
                                }
//--- change the pitch???
                                AL=WrkBuff[WrkBuff_CurrEvent+ev_pitch];
                                IF(AL!=0)&&(WrkBuff[WrkBuff_CurrEvent+ev_cmd]!=8){
                                        WrkBuff[WrkBuff_LastEvent+ev_pitch+DI]=AL;    //update pitch
                                        WrkBuff[WrkBuff_LastEvent+ev_oct+DI]=WrkBuff[WrkBuff_CurrEvent+ev_oct];
//--- play the new note!
                                DSWORD[#WrkBuff+WrkBuff_LastEvent+ev_pitching+DI]=0;
                                }
                                ELSE SetChannelVol();
//ready with updating the channel!!!
//---- Check the FX
                                BX=DSWORD[#WrkBuff+WrkBuff_CurrEvent+ev_cmd];
                                DSWORD[#WrkBuff+WrkBuff_LastEvent+ev_cmd+DI]=BX;
                                IF(BH!=0)WrkBuff[WrkBuff_LastEvent+ev_lastparams+DI]=BH;
                                AX=BX;
                                BX&=0xF;
                                BX+=BX;
                                DX=CmdPtrTab1[BX];
                                IF(DX!=0){
                                        $push CX
                                        DX();
                                        $pop CX
                                }
                                IF(WrkBuff[WrkBuff_CurrEvent+ev_cmd]!=8)&&
                                        (WrkBuff[WrkBuff_CurrEvent+ev_pitch]!=0)PlayNote();
ap_StillWaiting:
                                CX++;
                        }while(CX<9);
//---- Go next line !
                        IF(breakstate!=0){
                                WrkBuff[WrkBuff_CurrPtn]=breakptn;
                                WrkBuff[WrkBuff_CurrLine]=breakline;
                                IF(AL==0)GOTO ap_CheckSongLoop;
//--- Calculate offset into new pattern
                                CalcTrackPtr();
                                BX=0;
                                do{
                                        DI=DSWORD[#WrkBuff+WrkBuff_TrackPtr+BX];
                                        CX=WrkBuff[WrkBuff_CurrLine];
                                        DH=0;             //dh=trackdelay
                                        do{
                                                IF(DH!=0)DH--;
                                                ELSE{
#ifdef _FARMODULE_
                                                        AL=ESBYTE[DI];
#else
                                                        AL=DSBYTE[DI];
#endif
                                                        $test AL,128
                                                        IF(ZEROFLAG){
                                                                DI+=3;
                                                                DH=0;
                                                        }
                                                        ELSE{
                                                                DI++;
                                                                AL&=127;
                                                                AL--;
                                                                DH=AL;
                                                        }
                                                }
                                                CH++;
                                        }while(CH<CL);
                                        DSWORD[#WrkBuff+WrkBuff_TrackPtr+BX]=DI;
                                        DI=BX;
                                        DI>>=1;
                                        DH|=128;
                                        WrkBuff[WrkBuff_TrackDelay+DI]=DH;
                                        BL+=2;
                                }while(BL<18);
                        }
                        ELSE{
                                WrkBuff[WrkBuff_CurrLine]++;
                                IF(WrkBuff[WrkBuff_CurrLine]<64)GOTO ap_ByeBye;
                                WrkBuff[WrkBuff_CurrLine]=0;
                                WrkBuff[WrkBuff_CurrPtn]++;
                        }
ap_CheckSongLoop:
                        AL=WrkBuff[WrkBuff_CurrPtn];
#ifdef _FARMODULE_
                        IF(ESBYTE[SI+932]<=AL)DSWORD[#WrkBuff+WrkBuff_CurrPtn]=0;
#else
                        IF(DSBYTE[SI+932]<=AL)DSWORD[#WrkBuff+WrkBuff_CurrPtn]=0;
#endif
                }
                ELSE{
                        WrkBuff[WrkBuff_SongDelay]=AL;
//---- Check the FX
                        CX=0;
                        DI=0;
                        do{
                                BX=CX;
                                $test WrkBuff[WrkBuff_TrackDelay+BX],128
                                IF(ZEROFLAG){
                                        BX=DSWORD[#WrkBuff+WrkBuff_LastEvent+ev_cmd+DI];
                                        AX=BX;
                                        BX&=0xF;
                                        BX+=BX;
                                        DX=CmdPtrTab2[BX];
                                        IF(DX!=0){
                                                $push CX
                                                DX();
                                                $pop CX
                                        }
                                }
                                DI+=LastEventSize;
                                CX++;
                        }while(CX<9);
                }
ap_ByeBye:
                $popa
        }
}

void StopAdlib()
inline
{
        $cli
        AL=-1;
        outp(AL,0x40);
        outp(AL,0x40);
        ES=0;
        DI=32;
        SI=#oldint08;
        $movsw
        $movsw
        $sti

 // Плейер не активен
 AdlibPlayerStatus=FALSE;
}

void Adlib_Reset()
{
//*** Check Adlib ***
        outp(2,ADLIBPORT);
        DL++;
        outp(1);   ;//set timer#1
        DL--;
        inp();
        AL&=0b1000000;    //received??
        IF(NOTZEROFLAG)return;
        AdlibState=1;
        BX=1;
        do{
                AX=BX;
                AL&=0xE0;
                IF(AL==0x40)AH=63;
                AL=BL;
                Adlib_OUT();
                BL++;
        }while(NOTZEROFLAG);
        AX=0x2001;
        Adlib_OUT();
}

void CalcTrackPtr()
{
        BX=WrkBuff[WrkBuff_CurrPtn];
#ifdef _FARMODULE_
        BL=ESBYTE[SI+BX+0x3A6];      //current pattern
#else
        BL=DSBYTE[SI+BX+0x3A6];
#endif
        $imul BX,18
        CX=9;
        $lea DI,DSWORD[WrkBuff_TrackPtr+#WrkBuff]
        loop(CX){
                $push BX
#ifdef _FARMODULE_
                BX=ESWORD[SI+BX+0x430];      //current track
#else
                BX=DSWORD[SI+BX+0x430];
#endif
                BX+=BX;
                AX=DSWORD[#WrkBuff+BX];
                BX=CX;
                WrkBuff[WrkBuff_TrackDelay-1+BX]=0;
                $pop BX
                DSWORD[DI]=AX;      //store trackptr
                DI+=2;
                BX+=2;
        }
}

/*###### Commands Type#1 #######
AH = Parameter of the Command
[DI] = LastEvent        */
void InitArpeggio()
{
        WrkBuff[WrkBuff_LastEvent+ev_arpeggio+DI]=0;
}

void SetInsVol()
{
        WrkBuff[WrkBuff_LastEvent+ev_vol+DI]=AH;
        SetChannelVol();
}

void SetSongSpeed()
{
        IF(AH!=0){
                IF(AH<32){
                        AL=AH;
                        DSWORD[#WrkBuff+WrkBuff_SongDelay]=AX;
                        return;
                }
//change interrupt frequence
                CX=AH;
                DX=0x12;
                AX=0x34DC;
                $div CX
        }
        ELSE AX=-1;
        outp(AL,0x40);
        outp(AH,0x40);
}

void SlideFreqDown()
{
        SlideFreq();
        DSWORD[#WrkBuff+WrkBuff_LastEvent+ev_pitching+DI]-=CX;
        AX+=DSWORD[#WrkBuff+WrkBuff_LastEvent+ev_pitching+DI];
        AX|=AX;
        IF(MINUSFLAG){
                $not DX
                DSWORD[#WrkBuff+WrkBuff_LastEvent+ev_pitching+DI]=DX;
                AX=0;
        }
        SetFreq();
}

void SlideFreq()
{
        BL=CL;
        AL=WrkBuff[WrkBuff_LastEvent+ev_pitch+DI];
        DH=WrkBuff[WrkBuff_LastEvent+ev_oct+DI];
        GetFreq();
#ifdef codesize
        AX><DX;
#else
        DX=AX;
#endif
        CX=WrkBuff[WrkBuff_LastEvent+ev_params+DI];
}

void SetCarrModVol()
{
        AX=WrkBuff[WrkBuff_CurrEvent+ev_params];
        DL=10;
        $div DL
        IF(AL!=0){
                DH=AH;
                AH=0;
                DL=7;
                $mul DL
                $xor AL,0x3F;
                WrkBuff[WrkBuff_CarrVol+BX]=AL;
                AH=DH;
        }
        IF(AH!=0){
                AL=AH;
                AH=0;
                DL=7;
                $mul DL
                $xor AL,0x3F
                WrkBuff[WrkBuff_ModVol+BX]=AL;
        }
}

void InitTonePortamento()
{
        AL=WrkBuff[WrkBuff_CurrEvent+ev_pitch];
        IF(AL!=0){
                DH=WrkBuff[WrkBuff_CurrEvent+ev_oct];
                DSWORD[#WrkBuff+WrkBuff_LastEvent+ev_tpdest+DI]=GetFreq();
                TonePortamento();
        }
}

void TonePortamento()
{
        $push SI,CX
        SI=DSWORD[#WrkBuff+WrkBuff_LastEvent+ev_tpdest+DI];
        AX=WrkBuff[WrkBuff_LastEvent+ev_pitch+DI];
        DH=WrkBuff[WrkBuff_LastEvent+ev_oct+DI];
        GetFreq();
#ifdef codesize
        AX><DX;
#else
        DX=AX;
#endif
        BX=DSWORD[#WrkBuff+WrkBuff_LastEvent+ev_pitching+DI];
        DX+=BX;
        CX=WrkBuff[WrkBuff_LastEvent+ev_lastparams+DI];
        IF(DX>SI){
                BX-=CX;
                DX-=CX;
                IF(DX<SI){
                        DX=SI;
                        DX-=AX;
                        BX=DX;
                        DX=SI;
                }
        }
        ELSE IF(CARRYFLAG){
                BX+=CX;
                DX+=CX;
                IF(DX>SI){
                        DX=SI;
                        CX=DX;
                        CX-=AX;
                        BX=CX;
                }
        }
        DSWORD[#WrkBuff+WrkBuff_LastEvent+ev_pitching+DI]=BX;
#ifdef codesize
        AX><DX;
#else
        AX=DX;
#endif
        $pop BX,SI
        SetFreq();
}

void ExtCmds()
{
        AX=WrkBuff[WrkBuff_CurrEvent+ev_params];
        DL=10;
        $div DL
        AL+=AL;
        BX=AL;
        AX=ExtCmdsTab[BX];
        IF(AX!=0)AX();
}

void CellTrem()
{
        IF(AH!=0)Adlib_BD|=128;
        ELSE Adlib_BD&=!128;
        AH=Adlib_BD;
        AL=0xBD;
        Adlib_OUT();
}

void CellVib()
{
        IF(AH!=0)Adlib_BD|=64;
        ELSE Adlib_BD&=!64;
        AH=Adlib_BD;
        AL=0xBD;
        Adlib_OUT();
}

void PtnBreak()
{
        breakline=WrkBuff[WrkBuff_CurrEvent+ev_params]&0x3F;
        breakptn=WrkBuff[WrkBuff_CurrPtn]+1;
        breakstate=1;
}

void JumpPattern()
{
        breakptn=WrkBuff[WrkBuff_CurrEvent+ev_params];
        breakline=0;
        breakstate=1;
}

/*###### Commands Type#2 #######
AH = Parameter of the Command
[DI] = LastEvent */
void Arpeggio()
{
        AL=AH;
        AX&=0x7F;
        IF(NOTZEROFLAG){
                BL=10;
                $div BL
                BH=WrkBuff[WrkBuff_LastEvent+ev_arpeggio+DI];
                BL=BH+1;
                IF(BL>=3)BL=0;
                WrkBuff[WrkBuff_LastEvent+ev_arpeggio+DI]=BL;
                DH=WrkBuff[WrkBuff_LastEvent+ev_pitch+DI];
                IF(BH==1)AL+=DH;
                ELSE IF(BH==2)AL=DH+AH;
                ELSE AL=DH;
//Play the Arpeggio!!!
                DH=WrkBuff[WrkBuff_LastEvent+ev_oct+DI];
                IF(AL>12){
                        AL-=12;
                        AH++;
                }
                IF(DH>=8){
                        DH=7;
                        AL=12;
                }
                BL=DL;
                GetFreq();
                AX+=DSWORD[#WrkBuff+WrkBuff_LastEvent+ev_pitching+DI];
                BL=CL;
                SetFreq();
        }
}

void ExtCmds2()
{
        AX=WrkBuff[WrkBuff_LastEvent+ev_params+DI];
        DL=10;
        $div DL
        AL+=AL;
        BX=AL;
        AX=ExtCmdsTab2[BX];
        IF(AX!=0)AX();
}

/*
AL=note
DH=octave
->AX is absolute freq
*/
void GetFreq()
{
        $push SI,CX
        AL--;
        AL+=AL;
        SI=AL;
        CX=FreqParams[SI]-FreqStart;
        AL=DH;
        AX&=7;
        DX=FreqRange;
        $mul DX
        AX+=CX;
        $pop CX,SI
}

void SlideFreqUp()
inline
{
        SlideFreq();
        DSWORD[#WrkBuff+WrkBuff_LastEvent+ev_pitching+DI]+=CX;
        AX+=DSWORD[#WrkBuff+WrkBuff_LastEvent+ev_pitching+DI];
        IF(AX>=FreqRange*7+305){
                DSWORD[#WrkBuff+WrkBuff_LastEvent+ev_pitching+DI]=FreqRange*7+305-DX;
                AX=FreqRange*7+305;
        }
}

/*
;ax=absolute freq
;bl=channel
*/
void SetFreq()
inline
{
        AX|=AX;
        IF(MINUSFLAG)AX=0;
        IF(AX>=FreqRange*7+305)AX=FreqRange*7+305;
        $push SI,CX
        CX=FreqRange;
        DX=0;
        $div CX              // extracts octave in AX and freq. in DX
        DX+=FreqStart;
        CL=AL;
        AL=0xA0;
        AL+=BL;
        AH=DL;
        Adlib_OUT();
        AL=0xB0;
        AL+=BL;
        AH=CL;
        AH<<=2;
        AH=AH|DH|0b100000;
        $pop CX,SI
}

/*
AL=register
AH=value
*/
void Adlib_OUT()
{
        $push AX,DX,CX
        outp(AL,ADLIBPORT);
        CX=6;
        loop(CX)inp();
        DL++;
        outp(AH);
        DL--;
        CL=22;
        loop(CX)inp();
        $pop CX,DX,AX
}

/*****************************************************************
 Plays the current event

[di+WrkBuff_CurrEvent
CL=Channel
******************************************************************/
void PlayNote()
{
        $push DI
//--- get the pointer to current instrument in ds:bp !
        BX=WrkBuff[WrkBuff_CurrEvent+ev_sam]-1;
        if(PLUSFLAG){
                $imul BX,34
                $lea DI,DSWORD[BX+SI+0x30+23]
                BX=CX;
#ifdef _FARMODULE_
                WrkBuff[WrkBuff_ModVol+BX]=ESBYTE[DI+1]&0x3F;
                WrkBuff[WrkBuff_CarrVol+BX]=ESBYTE[DI+6]&0x3F;
#else
                WrkBuff[WrkBuff_ModVol+BX]=DSBYTE[DI+1]&0x3F;
                WrkBuff[WrkBuff_CarrVol+BX]=DSBYTE[DI+6]&0x3F;
#endif
                IF(WrkBuff[WrkBuff_CurrEvent+ev_cmd]==3)SetCarrModVol();
                AL=ChannelOffs[BX];
                DL=4;
                loop(DL){
#ifdef _FARMODULE_
                        AH=ESBYTE[DI];
#else
                        AH=DSBYTE[DI];
#endif
                        Adlib_OUT();
                        AL+=3;
#ifdef _FARMODULE_
                        AH=ESBYTE[DI+5];
#else
                        AH=DSBYTE[DI+5];
#endif
                        Adlib_OUT();
                        AL+=0x20-3;
                        DI++;
                }
                AL+=0x40;          // do E0 range now
#ifdef _FARMODULE_
                AH=ESBYTE[DI];
#else
                AH=DSBYTE[DI];
#endif
                Adlib_OUT();
                AL+=3;
#ifdef _FARMODULE_
                AH=ESBYTE[DI+5];
#else
                AH=DSBYTE[DI+5];
#endif
                Adlib_OUT();
                SetChannelVol();
                AL=0xC0+BL;
#ifdef _FARMODULE_
                AH=ESBYTE[DI+6];
#else
                AH=DSBYTE[DI+6];
#endif
                Adlib_OUT();
                DI=WrkBuff[WrkBuff_CurrEvent+ev_pitch]-1<<1;
                DX=FreqParams[DI];
                AL=0xA0+BL;
                AH=DL;
                Adlib_OUT();
                AL=0xB0+BL;
                AH=DH;
                Adlib_OUT();
                DL=WrkBuff[WrkBuff_CurrEvent+ev_oct];
                DL+=DL;
                DL+=DL;
                AH=AH|DL|0b100000;
                Adlib_OUT();       //...und mit neuen Daten anschalten
        }
        $pop DI
}

void SlideVolUp()
{
        DL=0;
        SlideVol();
}

void SlideVolDown()
inline
{
        DL=1;
}

void SlideVol()
inline
{
        BH=WrkBuff[WrkBuff_LastEvent+ev_vol+DI];
        AX=WrkBuff[WrkBuff_LastEvent+ev_params+DI];
        BL=10;
        $div BL
        IF(DL==0){
                BH+=AH;
                IF(BH>63)BH=63;
        }
        ELSE{
                BH-=AH;
                IF(MINUSFLAG)BH=0;
        }
        WrkBuff[WrkBuff_LastEvent+ev_vol+DI]=BH;
}

//Sets the lastvol of channel cl
void SetChannelVol()
{
        $pusha
//--- get the pointer to current instrument in ds:bp !
        $imul BX,CX,LastEventSize
        DX=WrkBuff[WrkBuff_LastEvent+ev_sam+BX]-1;
        IF(PLUSFLAG){
                $imul DX,34
                DX><BX;
                $lea DI,DSWORD[BX+SI+0x30+23]
                BX=DX;
                BX=WrkBuff[WrkBuff_LastEvent+ev_vol+BX];
                DL=VolTab[BX];
                BX=CX;
                DH=ChannelOffs[BX]+0x20;
                AH=DL;
                $xor AH,0x3F
                AL=WrkBuff[WrkBuff_CarrVol+BX]&0x3F;
                AH+=AL;
                IF(AH>0x3F)AH=0x3F;
#ifdef _FARMODULE_
                AL=ESBYTE[DI+6];           //Carrier
#else
                AL=DSBYTE[DI+6];
#endif
                AL&=!0x3F;
                AH|=AL;
                AL=DH+3;
                Adlib_OUT();
#ifdef _FARMODULE_
                $test ESBYTE[DI+10],1
#else
                $test DSBYTE[DI+10],1
#endif
                IF(NOTZEROFLAG){
//Connection is ON!!!
                        AH=DL;
                        $xor AH,0x3F
                        AL=WrkBuff[WrkBuff_ModVol+BX]&0x3F;
                        AH+=AL;
                        IF(AH>0x3F)AH=0x3F;
#ifdef _FARMODULE_
                        AL=ESBYTE[DI+1];                   //Mod
#else
                        AL=DSBYTE[DI+1];
#endif
                        AL&=!0x3F;
                        AH|=AL;
                }
//Connection is OFF!!!
                ELSE{
#ifdef _FARMODULE_
                        AH=ESBYTE[DI+1];                   //Mod
#else
                        AH=DSBYTE[DI+1];
#endif
                        AH&=!0x3F;
                        AH|=WrkBuff[WrkBuff_ModVol+BX];
                }
                AL=DH;
                Adlib_OUT();
        }
        $popa
}

// DX - BaseIO
:byte fastcall ResetDsp(DX)
{
 // Сброс DSP
 outp(1,DX+6);
 inp(DX);
 inp(DX);
 inp(DX);
 inp(DX);
 outp(0);
 DL+=4; // 022Ah порт
 CX=100;
 loop(CX){
   AL=inp(DX);
   IF(AL==0AAh)return(TRUE);
 }
 return(FALSE);
}

