// Проигрыватель MOD файлов
#resize TRUE
#pragma option argc



// Включаем поддержку модов
#define __MODPLAYER__ TRUE



// Инклуды
#include "graph19.h--"  // Библа для работы с графикой
#include "keycodes.h--" // Библа с #define-ами кодов клавиш
#include "write.h--"    // Библа для вывода данных на экран
#include "sb.h--"       // Библа для работы с Sound Blaster-ом
#include "wave.h--"     // Библа для подгрузки семплов в формате WAV
#include "modplay.h--"  // Библа для проигрывания MOD-модулей
#include "other.h--"    // Библа с различными дополнительными процедурами
#include "din.inc"      // Мои копирайты



// Кое-какие дополнительные коды клавиш
#define k_p                   1970h
#define k_f                   2166h
#define k_minus               0C2Dh
#define k_plus                0D3Dh
#define k_grey_minus          18989
#define k_grey_plus           20011
#define k_grey_rightslash     57391
#define k_grey_star           14122
#define k_grey_pageup         18912
#define k_grey_pagedown       20960


word VideoBuf;
byte ShowFps=0;
dword Fps=0;
dword Frames=0;
dword Tics=0;


// В пояснениях не нуждается ;)
main()
{
 // Есть параметры в коммандной строке ?
 IF (PARAMCOUNT()<2) return;

 // Инициализация всего
 InitAll();

  // Подгружаем мод-файл
  LoadModFile(PARAMSTR(1));

  // Начинаем воспроизведение мод-модуля
  PlayModModule=TRUE;

  // Выделяем память под видеобуфер
  VideoBuf=GETMEM(64000/16);

  // Создаем и устанавливаем палитру
  MakeAndSetPal();

  // Очищаем видеобуфер
  ES=VideoBuf; DI=0; EAX=0; CX=64000/4; $rep $stosd

  loop(){
    // Устанавливаем ES на сегмент видеобуфера
    ES=VideoBuf;

    // Рисуем осцилограмму
    for (CX=0,BX=5,DI=5,SI=#ModVoice; CL<ModModule.NumVoices; BX+=32,CX++,SI+=sizeof(MODVOICE)){
        $push CX,SI
         // Рассчитываем положение осцилограммы на экране
         IF (CX!=0)&&(!CX%6){
            DI+=100;
            BX=5;
         }
         // Канал активен ?
         IF (SI.MODVOICE.Active){
            // Устанавливаем FS на сегмент рабочего буфера канала
            FS=SI.MODVOICE.ModWorkBufSeg;
            // Рассчитываем приращение в рабочем буфере канала
            $push BX
             ESI=@FDIV_16_16(SbWorkBufSize>>1,100);
            $pop BX
            // Меняем местами дробную и целую части
            $ror EBP,16
            $ror ESI,16
            // Рисуем осцилограмму
            FOR (DX=0,EBP=0; DX<99; EBP+=ESI,DX++){
                $push BX,DI,ESI,EBP,DX
                 Line(DX+DI,char FSCHAR[BP]>>3+16+BX,DX+DI+1,char FSCHAR[BP+1]>>3+16+BX,BX>>5+56);
                $pop DX,EBP,ESI,DI,BX
            }
         }ELSE{
            // Рисуем горизонтальную линию
            $push BX,DI
             Line(DI,BX+16,DI+99,16+BX,BX>>5+56);
            $pop DI,BX
         }
        $pop SI,CX
    }

    // Делаем блюр
    CX=64000-640; BH=0; DI=320;
    do{
     AX=ESBYTE[DI-320];
     BL=ESBYTE[DI+320]; AX+=BX;
     BL=ESBYTE[DI-1]; AX+=BX;
     BL=ESBYTE[DI+1]; AX+=BX;
     AX>>=2;
     $stosb
     CX--;
    }while (!ZEROFLAG);

    // Надо показывать количество кадров в секунду (FPS) ?
    IF (ShowFps){
       $push ES
        ES=0;
        IF (Tics+18<=ESDWORD[46Ch]){
           Tics=ESDWORD[46Ch];
           Fps=Frames;
           Frames=0;
        }ELSE Frames++;
       $pop ES
       WriteDWordNDigits19(235,5,63,Fps,10);
    }

    // Делаем Fade out
/*    CX=64000; DI=0;
    do{
     AL=ESBYTE[DI]-1;
     IF (CARRYFLAG) AL=0;
     $stosb
     CX--;
    }while (!ZEROFLAG);*/


    // Ждем обратного хода луча
    WaitRetrace();

    // Выводим видеобуфер на экран
    $push DS
     ES=0A000h; DI=0; DS=VideoBuf; SI=0; CX=64000/4; $rep $movsd
    $pop DS

    WriteStr19(-sizeof(CopyRight1)*8/2+160,7*10,63,CopyRight1);
    WriteStr19(-sizeof(CopyRight2)*8/2+160,8*10,63,CopyRight2);
    WriteStr19(-sizeof(CopyRight3)*8/2+160,9*10,63,CopyRight3);
    WriteStr19(-sizeof(CopyRight4)*8/2+160,10*10,63,CopyRight4);

/*     $push DS
      ES=0A000h; DI=0;
      DS=VideoBuf; SI=0;
      DX=200;
      EBX=000FF00FFh;
      do{
        CX=320/4;
        do{
          $lodsd
           EAX&=EBX;
          $stosd
          CX--;
        }while (!ZEROFLAG);
//        EBX^=0FF00FF00h;
        !EBX;
        DX--;
      }while (!ZEROFLAG);
     $pop DS*/

    // Нажата клавиша ?
    IF (@BIOSEKEYCHECK()){
       // Какая это клавиша ?
       SWITCH(@BIOSEREADKEY()){
        CASE k_p:
         PlayModModule=FALSE;
          WHILE (@BIOSEREADKEY()!=k_p);
         PlayModModule=TRUE;
         BREAK;
        CASE k_f:
         ShowFps^=1;
         BREAK;
        CASE k_esc:
         BREAK 1;
        DEFAULT:
         BREAK;
       }
    }
  }


 // Деинициализация всего
 DeInitAll();
}



// Инициализация всего
InitAll()
{
 WriteStr("\nАвтоопределение конфигурации звуковой карты...");
 IF (!SbAutoDetect()){
// IF (!SbGetEnvirenmentConfig()){
    WriteStr("\nSound Blaster не найден или занят другим приложением");
    // Вывести все, что удалось определить
    WriteStr("\nBase Address : "); WriteWordHex(SbIoAddr); Write('h');
    WriteStr("\nIRQ          : "); WriteByte(SbIrq);
    WriteStr("\nDMA          : "); WriteByte(SbDma);
    // Обратно в DOS
    EXIT();
 }ELSE WriteStr("\nSound Blaster найден");

 WriteStr("\nBase Address : "); WriteWordHex(SbIoAddr); Write('h');
 WriteStr("\nIRQ          : "); WriteByte(SbIrq);
 WriteStr("\nDMA          : "); WriteByte(SbDma);

 WriteStr("\n\nИнициализация SB...");
 InitSb();

 WriteStr("\nТип         : Sound Blaster ");
 AL=SbType;
 IF (AL==SoundBlaster10) WriteStr("1.0");
 ELSE IF (AL==SoundBlaster20) WriteStr("2.0");
 ELSE IF (AL==SoundBlasterPro) WriteStr("Pro");
 ELSE IF (AL==SoundBlaster16) WriteStr("16");
 ELSE IF (AL==SoundBlasterAwe32) WriteStr("Awe 32");
 ELSE IF (AL==SoundBlasterEss) WriteStr("ESS");
 WriteStr("\nSbRate      : "); WriteDWord(SbRate);
 WriteStr("\nSbStereo    : "); WriteByte(SbStereo);
 WriteStr("\nSbAutoInit  : "); WriteByte(SbAutoInit);

// WriteStr("\n\nDSP Version : "); WriteByte(word GetDspVersion()>>8); Write('.'); WriteByte(GetDspVersion());

 // Инициализируем мод-систему
 InitModSystem();

 // Видео режим 320x200x256
 SetVideoMode(13h);
}



// Деинициализация всего
DeInitAll()
{
 // Текстовый видео режим 80x25
 SetVideoMode(3);

 WriteStr("\n\nДеинициализация SB... ");
  DeInitSb();
 WriteStr("OK");

 // Инициализируем мод-систему
 DeInitModSystem();
}


MakeAndSetPal()
{
 FOR (CX=1; CX<256; CX++){
     outp(CL,3C8h);
//     outp(CL,DX+1);
//     outp(CL&31);
//     outp(CL);
     outp(0,DX+1);
     outp(CL);
     outp(0);
 }
}