/****************************************************************************
    SPHINX Programming (C) 2001.
    NAME:  GRAPH19.H--
    DESCRIPTION: Библиотека для работы с графикой в видеорежиме 19 (13h)
                 320x200x256 цветов.
    LAST MODIFIED:  23-02-2001 01:15:45
 ----------------------------------------------------------------------------
    PROCEDURES DEFINED IN THIS FILE:
     :void fastcall SetVideoMode(AX)
     :void fastcall SetVESAVideoMode(BX)
     :void fastcall WaitRetrace()

     Real Mode:
      :void fastcall PutPixel19(AX,BX,CL)
      :byte fastcall GetPixel19(AX,BX)
      :void Line(word x1,y1,x2,y2; byte color)
      :void fastcall Box(SI,DI,DX,BX,AL)
      :void fastcall Bar(AX,DI,DX,SI,BL)
      :void fastcall Circle(DI,SI,DX,CL)
      :void fastcall FilledCircle(BX,SI,DX,CL)
      :void fastcall SetVgaPalette(BX,CX,DX)
      :void fastcall Fill(DI,SI,CL,AX,BX)
      :void fastcall TexturedFill(DI,SI,AX,BX)
      :void fastcall PutSprite(CX,DI,BX,DX,AX,SI)
      :void fastcall GetSprite(CX,SI,BX,DX,AX,DI)
      :void fastcall PutTrSprite(CX,DI,BX,DX,AX,SI)

     Protect Mode:
      :void fastcall PutPixel19(EAX,EBX,CL)
      :byte fastcall GetPixel19(EAX,EBX)
      :void Line(dword x1,y1,x2,y2; byte color)
      :void fastcall Box(ESI,EDI,EDX,EBX,AL)
      :void fastcall Bar(EAX,EDI,EDX,ESI,BL)
      :void fastcall Circle(EDI,ESI,EDX,CL)
      :void fastcall FilledCircle(EBX,ESI,EDX,CL)
      :void fastcall SetVgaPalette(EBX,ECX,EDX)
      :void fastcall Fill(EDI,ESI,CL,AX,EBX)
      :void fastcall TexturedFill(EDI,ESI,AX,EBX)
      :void fastcall PutSprite(ECX,EDI,EBX,EDX,EAX,ESI)
      :void fastcall GetSprite(ECX,ESI,EBX,EDX,EAX,EDI)
      :void fastcall PutTrSprite(ECX,EDI,EBX,EDX,AX,ESI)
 ----------------------------------------------------------------------------
    Dryamov Alexey (Din)
    Fido:  2:5055/141.11
    Email: _Din_@mail.ru
****************************************************************************/
#ifdef cpu < 3
 #use80386
 #print "Warning (GRAPH19.H--): using 80386\007\n"
 #pause
#endif

void Line(unsigned int x1,y1,x2,y2; byte color);

// Структура VESA буфера информации
struct SVGAINFOREC
{
  word  ModeAttrs;       // Побитовая характеристика режима (bit fields: mode characteristics)
  byte  WinAAttrs;       // Побитовая характеристика окна A (bit fields: windowing system characteristics)
  byte  WinBAttrs;       // Побитовая характеристика окна B (bit fields: windowing system characteristics)
  word  WinGran;         // Гранулировка окна в KB (window granularity, in Kbytes)
  word  WinSize;         // Размер окна в KB (window size, in KB)
  word  WinASeg;         // Сегмент окна A (window A segment address)
  word  WinBSeg;         // Сегмент окна B (window B segment address)
  dword pfWinFnPtr;      // Дальний адрес функций управления окном (far address of window-handling function)
  word  ScanLineSize;    // Кол-во байт в скан линии (bytes per scan line)
//─────────────── Расширенная информация (присутствует, если установлен 1 бит
//                в поле ModeAttrs)
//                Extended/optional information; present only when ModeAttrs bit 1 is 1.
  word  HorisontalRes;   // Горизонтальное разрешение (horizontal resolution, in pixels or char cells)
  word  VerticalRes;     // Вертикальное разрешение (vertical resolution, in pixels or char cells)
  byte  CharWidth;       // Ширина символа (character cell width)
  byte  CharHeight;      // Высота символа (character cell height)
  byte  PlaneCount;      // Количество видео плоскостей (number of video memory planes)
  byte  BitsPerPixel;    // Количество бит на пиксел
  byte  BankCount;       // Количество банков
  byte  MemModel;        // Тим модели памитя (memory model type code)
  byte  BankSize;        // Размер банка (Kbytes)
  byte  res[227];        // Зарезервированно
};



// На входе:
// AX = video mode
inline void fastcall SetVideoMode(AX)
{
 $int 10h
}



// BX = VideoMode
inline void fastcall SetVESAVideoMode(BX)
{
 AX=4F02h; $int 10h
}



// Ожидает перерисовки экрана видеоадаптером
:void fastcall WaitRetrace()
{
 asm{
  mov DX,03DAh
V1:
  in AL,DX
  and AL,08H
  jnz V1
V2:
  in AL,DX
  and AL,08H
  jz V2
 }
}



#ifdef __MSDOS__

// ES - сегмент видео буфера или видео памяти
// На входе:
// BX = x
// AX = y
// CL = color
// На выходе:
inline void fastcall PutPixel19(AX,BX,CL)
{
        AH+=BL;
        BX<<=6;
        BX+=AX;
        ESBYTE[BX]=CL;
}

// ES - сегмент видео буфера или видео памяти
// На входе:
// BX = x
// AX = y
// На выходе:
// AL = color
:byte fastcall GetPixel19(AX,BX)
{
        AH+=BL;
        BX<<=6;
        BX+=AX;
        return ESBYTE[BX];
}

// Рисование линии
:void Line(word x1,y1,x2,y2; byte color)
{
        AL=color;
        CX=y1;
        BX=x1;
        DX=y2;
        BP=x2;
        IF(int CX>DX ){
                CX><DX;
                BX><BP;
        }
        DX-=CX;
        SI=1;
        BP-=BX;
        IF(MINUSFLAG){
#ifdef codesize
                -SI;
#else
                SI-=2;
#endif
                -BP;
        }
        BH+=CL; CX<<=6; BX+=CX;
        ESBYTE[BX]=AL;
        IF(int BP>DX){
                CX=BP;
                DX+=DX;
                DI=DX;
                DI-=BP;
                BP+=BP;
                LOOPNZ(CX){
                        IF(int DI>=0){
                                BX+=320;
                                DI-=BP;
                        }
                        DI+=DX;
                        BX+=SI;
                        ESBYTE[BX]=AL;
                }
        }
        ELSE{
                CX=DX;
                BP+=BP;
                DI=BP;
                DI-=DX;
                DX+=DX;
                LOOPNZ(CX){
                        IF(int DI>=0){
                                BX+=SI;
                                DI-=DX;
                        }
                        DI+=BP;
                        BX+=320;
                        ESBYTE[BX]=AL;
                }
        }
}

:void LINE(BX,CX,DI,DX,AL)
{
        $push BP
        IF(int CX>DX ){
                CX><DX;
                BX><DI;
        }
        DX-=CX;
        SI=1;
        DI-=BX;
        IF(MINUSFLAG){
#ifdef codesize
                -SI;
#else
                SI-=2;
#endif
                -DI;
        }
        BH+=CL; CX<<=6; BX+=CX;
        ESBYTE[BX]=AL;
        IF(int DI>DX){
                CX=DI;
                DX+=DX;
                BP=DX;
                BP-=DI;
                DI+=DI;
                LOOPNZ(CX){
                        IF(int BP>=0){
                                BX+=320;
                                BP-=DI;
                        }
                        BP+=DX;
                        BX+=SI;
                        ESBYTE[BX]=AL;
                }
        }
        ELSE{
                CX=DX;
                DI+=DI;
                BP=DI;
                BP-=DX;
                DX+=DX;
                LOOPNZ(CX){
                        IF(int BP>=0){
                                BX+=SI;
                                BP-=DX;
                        }
                        BP+=DI;
                        BX+=320;
                        ESBYTE[BX]=AL;
                }
        }
        $pop BP
}


#ifdef speed
// Рисование коробки
// ES = Video Buf(Mem) Seg
// SI = x
// DI = y
// DX = Width
// BX = Height
// AL = Color
:void fastcall Box(SI,DI,DX,BX,AL)
{
        IF(BX>1){
                DI=DI*320+SI;
                AH=AL;
                $call HOR
// Рисуем вертикальные линии
                CX=320-DX;
                DI+=CX;
                SI=DX;
                SI-=2;
                BX-=2;
                IF(NOTZEROFLAG){
                        loop(BX){
                                $stosb
                                DI+=SI;
                                $stosb
                                DI+=CX;
                        }
                }
// Рисуем нижнюю горизонтальную линию
HOR:
                CX=DX;
                CX>>=1;
                IF(CARRYFLAG)$stosb
                CX>>=1;
                IF(CARRYFLAG)$stosw
// Рисуем верхнюю горизонтальную линию
                SI=AX;
                EAX<<=16;
                AX=SI;
                $rep $stosd
        }
}
#else
// Рисование коробки
// ES = Video Buf(Mem) Seg
// SI = x
// DI = y
// DX = Width
// BX = Height
// AL = Color
:void fastcall Box(SI,DI,DX,BX,AL)
{
        IF(BX>1){
                DI=DI*320+SI;
                $call HOR
// Рисуем вертикальные линии
                CX=320-DX;
                DI+=CX;
                SI=DX;
                SI-=2;
                BX-=2;
                IF(NOTZEROFLAG){
                        loop(BX){
                                $stosb
                                DI+=SI;
                                $stosb
                                DI+=CX;
                        }
                }
// Рисуем нижнюю горизонтальную линию
HOR:   // Рисуем верхнюю горизонтальную линию
                CX=DX; $rep $stosb
        }
}
#endif

#ifdef speed
// Рисование закрашенного прямоугольника
// ES = Video Buf(Mem) Seg
// AX = x
// CX = y
// DX = Width
// SI = Height
// BL = Color
:void fastcall Bar(AX,CX,DX,SI,BL)
{
 // Вычисляем начальные адрес прямоугольника
        AH+=CL; CX<<=6;
        DI=AX+CX;
 // Рисуем прямоугольник
        BH=BL; AX=BX; EAX<<=16; AX=BX;
        BX=DX>>2;
        FOR (; int SI>0; SI--,DI=DI+320-DX){
                CX=BX; $rep $stosd
                CX=DX&3; $rep $stosb
        }
}
#else
// Рисование закрашенного прямоугольника
// ES = Video Buf(Mem) Seg
// BX = x
// DI = y
// DX = Width
// SI = Height
// AL = Color
:void fastcall Bar(BX,DI,DX,SI,AL)
{
 // Вычисляем начальные адрес прямоугольника
        DI=DI*320+BX;
 // Рисуем прямоугольник
        FOR(;int SI>0;SI--){
    CX=DX; $rep $stosb
    DI=DI+320-DX;
        }
}
#endif

/********************************************
        Вывод пиксела на экран со смещенным центром
        AX = x
        BX = y
        CL = color
        DI = x0
        SI = y
*********************************************/
inline void PUTPIXELC(AX,BX,CL,DI,SI)
{
        AX+=DI;
        BX+=SI;
        PutPixel19(AX,BX,CL);
}

// DI = x
// SI = y
// DX = radius
// CL = color
:void fastcall Circle(DI,SI,DX,CL)
int x,c;
{
        x=0;
        c=1-DX<<1;
        -DX;
        loop(){
                PUTPIXELC(x,DX,CL,DI,SI);
                PUTPIXELC(-x,DX,CL,DI,SI);
                PUTPIXELC(x,-DX,CL,DI,SI);
                PUTPIXELC(-x,-DX,CL,DI,SI);
                IF (DX==0) BREAK;
                BX=c;
                IF (int BX<=x){
                        x++;
                        c=x<<1+c+1;
                }
                IF (int BX<=DX) CONTINUE;
                DX++;
                c=DX<<1+c+1;
        }
}

/***********************************
        BX = x
        DI = y
        CX = len
        AX = color
***********************************/
inline void HORIZONTALDUBLLINE(BX,DI,CX,AX)
{
        DI=DI*320+BX-CX;
        $rep $stosw
        $stosb
}

// BX = x
// SI = y
// DI = radius
// CL = color
:void fastcall FilledCircle(BX,SI,DX,CL)
int x,c;
{
        x=0;
        c=1-DX<<1;
        -DX;
        CH=CL;
        loop(){
                $push CX
                AX=CX;
                HORIZONTALDUBLLINE(BX,SI-DX,x,AX);
                HORIZONTALDUBLLINE(BX,SI+DX,x,AX);
                $pop CX
                IF (DX==0) BREAK;
                DI=c;
                IF (int DI<=x){
                        x++;
                        c=x<<1+c+1;
                }
                IF (int DI<=DX) CONTINUE;
                DX++;
                c=DX<<1+c+1;
        }
}


// Установка палитры
// Пареметры:
// BX - first color number
// CX - number colors to set
// DX - colors buf offs (RGBRGBRGB...)
:void fastcall SetVgaPalette(BX,CX,DX)
{
  $push ES
  ES=DS;
  AX=1012h;
  $int 10h
  $pop ES
}

:void FILLPROC()
{
// Ставим пиксел
        ESBYTE[BX]=CL;
        IF(int DI>0){
                BX--;
                IF (ESBYTE[BX]==CH){
                        DI--;
                        FILLPROC();
                        DI++;
                }
                BX++;
        }
 IF (int DI<319){
    BX++;
     IF (ESBYTE[BX]==CH){
        DI++;
        FILLPROC();
        DI--;
     }
    BX--;
 }

 IF (int SI<199){
    BX+=320;
     IF (ESBYTE[BX]==CH){
        SI++;
        FILLPROC();
        SI--;
     }
    BX-=320;
 }

 IF (int SI>0){
    BX-=320;
     IF (ESBYTE[BX]==CH){
        SI--;
        FILLPROC();
        SI++;
     }
    BX+=320;
 }
}

// Заливка
// Предупреждение: Процедура активно использует stack!
// DI - x
// SI - y
// CL - color
:void fastcall Fill(DI,SI,CL)
{
IF(DI<320)&&(SI<200){
        BX=DI; AX=SI;
// Вычисляем адрес в видео буфере или в видео памяти
        BH+=AL; AX<<=6; BX+=AX;
// Берем цвет по этому адресу
        CH=ESBYTE[BX];
// Делаем заливку, если заливаемый цвет не цвет заливки
        IF (CH!=CL)FILLPROC();
 }
}



:struct _TEXTUREDFILL_
{
 word Seg;           // Сегмент текстуры
 word Offs;          // Смещение текстуры
 word Width,Height;  // Ширина и высота текстуры
 word x,y;           // Координаты в текстуре
}TexturedFillStruct;

// Заливка текстурой
// Предупреждение: Процедура активно использует stack!
// DI - x
// SI - y
// AX - селектор рабочего буфера
// BX - смещение конца рабочено буфера
// Если используешь stack, то SS:[SP-2]
:void fastcall TexturedFill(DI,SI,AX,BX)
{
        IF (DI<320)&&(SI<200){
//    $cli
     $push SS $pop FS DX=SP; SS=AX; SP=BX;
      $push BP,DX,GS
       // Устанавливаем GS на сегмент текстуры
       $push TexturedFillStruct.Seg $pop GS
       // Вычисляем начальный адрес в текстуре
       BP=TexturedFillStruct.y*TexturedFillStruct.Width+TexturedFillStruct.x+TexturedFillStruct.Offs;
       // Вычисляем адрес в видео буфере или в видео памяти
       BX=DI; AX=SI; BH+=AL; AX<<=6; BX+=AX;
       // Берем цвет по этому адресу
       CH=ESBYTE[BX];
       // Устанавливаем AX на текущую координату x в текстуре
       AX=TexturedFillStruct.x;
       // Устанавливаем DX на текущую координату y в текстуре
       DX=TexturedFillStruct.y;
       // Делаем заливку, если заливаемый цвет не цвет заливки
       $call FILLPROCT
      $pop GS,DX,BP
     AX=FS; SS=AX; SP=DX;
//    $sti
 }
 $ret
FILLPROCT:
 // Ставим пиксел
        CL=GSBYTE[BP];
 IF (CL==CH) $ret
 ESBYTE[BX]=CL;

 IF (int DI>0){
    // Делаем проверку на рекурсивный вызов
    BX--;
     IF (ESBYTE[BX]==CH){
        DI--;
         IF (int AX==0){AX=TexturedFillStruct.Width; BP+=AX;}
         BP--;
          AX--;
           $call FILLPROCT
          AX++;
         BP++;
         IF (int AX==TexturedFillStruct.Width){BP-=AX; AX=0;}
        DI++;
     }
    BX++;
 }

 IF (int DI<319){
    BX++;
     IF (ESBYTE[BX]==CH){
        DI++;
         BP++;
          AX++;
          IF (int AX==TexturedFillStruct.Width){BP-=AX; AX=0;}
           $call FILLPROCT
          IF (int AX==0){AX=TexturedFillStruct.Width; BP+=AX;}
          AX--;
         BP--;
        DI--;
     }
    BX--;
 }

 IF (int SI<199){
    BX+=320;
     IF (ESBYTE[BX]==CH){
        SI++;
         BP+=TexturedFillStruct.Width;
          DX++;
          IF (int DX==TexturedFillStruct.Height){DX=0; BP=AX+TexturedFillStruct.Offs;}
           $call FILLPROCT
          IF (int DX==0){DX=TexturedFillStruct.Height; BP=DX*TexturedFillStruct.Width+AX+TexturedFillStruct.Offs;}
          DX--;
         BP-=TexturedFillStruct.Width;
        SI--;
     }
    BX-=320;
 }

 IF (int SI>0){
    BX-=320;
     IF (ESBYTE[BX]==CH){
        SI--;
         IF (int DX==0){DX=TexturedFillStruct.Height; BP=DX*TexturedFillStruct.Width+AX+TexturedFillStruct.Offs;}
         BP-=TexturedFillStruct.Width;
          DX--;
           $call FILLPROCT
          DX++;
         BP+=TexturedFillStruct.Width;
         IF (int DX==TexturedFillStruct.Height){DX=0; BP=AX+TexturedFillStruct.Offs;}
        SI++;
     }
    BX+=320;
 }
}



// ES - сегмент видео буфера или видео памяти
// CX - Sprite X
// DI - Sprite Y
// BX - Sprite Width
// DX - Sprite Height
// AX - Sprite Seg
// SI - Sprite Offs
:void fastcall PutSprite(CX,DI,BX,DX,AX,SI)
{
 // Устанавливаем FS на сегмент спрайта
 FS=AX;

 // Выходим, если спрайт не виден
 IF (int CX>=320)||(int DI>=200)||(int CX+BX<=0)||(int DI+DX<=0) return;

 // Сохраняем ширину спрайта буфера на стеке
 $push BX

 // Делаем клипирование
 IF (int DI<0){
    DX+=DI;
    $push DX
     AX=DI*BX; SI-=AX;
    $pop DX
    DI=0;
 }
 IF (int DI+DX>=200){
    DX=200-DI;
 }
 IF (int CX<0){
    BX+=CX;
    SI-=CX;
    CX=0;
 }
 IF (int CX+BX>=320){
    BX=320-CX;
 }

 // Рисуем спрайт
 // Устанавливаем FS на сегмент буфера спрайта
 // SI - смещение в буфере спрайта
 // DX - высота спрайта
 // DI - смещение в видео буфере
 DI=DI*320+CX;
 // BX - ширина спрайта
 AX=BX&3;
 IF (AX==1){
    // Восстанавливаем ширину буфера спрайта
    $pop AX
    loop(DX){
      CX=BX>>2;
      $rep $FS: $movsd
      $FS: $movsb
      DI=DI+320-BX;
      SI=SI+AX-BX;
    }
 }ELSE IF (AX==2){
    // Восстанавливаем ширину буфера спрайта
    $pop AX
    loop(DX){
      CX=BX>>2;
      $rep $FS: $movsd
      $FS: $movsw
      DI=DI+320-BX;
      SI=SI+AX-BX;
    }
 }ELSE IF (AX==0){
    // Восстанавливаем ширину буфера спрайта
    $pop AX
    loop(DX){
      CX=BX>>2;
      $rep $FS: $movsd
      DI=DI+320-BX;
      SI=SI+AX-BX;
    }
 }ELSE{
    // Восстанавливаем ширину буфера спрайта
    $pop AX
    loop(DX){
      CX=BX>>2;
      $rep $FS: $movsd
      $FS: $movsw
      $FS: $movsb
      DI=DI+320-BX;
      SI=SI+AX-BX;
    }
 }
}



// ES - сегмент видео буфера или видео памяти
// CX - Sprite X
// SI - Sprite Y
// BX - Sprite Width
// DX - Sprite Height
// AX - Sprite Seg
// DI - Sprite Offs
:void fastcall GetSprite(CX,SI,BX,DX,AX,DI)
{
 // Устанавливаем FS на ES (сегмент видео буфера или видео памяти)
 $push ES $pop FS
 // Устанавливаем ES на сегмент спрайта
 ES=AX;

 // Выходим, если спрайт не виден
 IF (int CX>=320)||(int SI>=200)||(int CX+BX<=0)||(int SI+DX<=0){
    ES=FS;
    $ret
 }

 // Сохраняем ширину спрайта буфера на стеке
 $push BX

 // Делаем клипирование
 IF (int SI<0){
    DX+=SI;
    $push DX
     AX=SI*BX; DI-=AX;
    $pop DX
    SI=0;
 }
 IF (int SI+DX>=200){
    DX=200-SI;
 }
 IF (int CX<0){
    BX+=CX;
    DI-=CX;
    CX=0;
 }
 IF (int CX+BX>=320){
    BX=320-CX;
 }

 // Рисуем спрайт
 // Устанавливаем FS на сегмент буфера спрайта
 // SI - смещение в буфере спрайта
 // DX - высота спрайта
 // DI - смещение в видео буфере
 SI=SI*320+CX;
 // BX - ширина спрайта
 AX=BX&3;
 IF (AX==1){
    // Восстанавливаем ширину буфера спрайта
    $pop AX
    loop(DX){
      CX=BX>>2;
      $rep $FS: $movsd
      $FS: $movsb
      SI=SI+320-BX;
      DI=DI+AX-BX;
    }
 }ELSE IF (AX==2){
    // Восстанавливаем ширину буфера спрайта
    $pop AX
    loop(DX){
      CX=BX>>2;
      $rep $FS: $movsd
      $FS: $movsw
      SI=SI+320-BX;
      DI=DI+AX-BX;
    }
 }ELSE IF (AX==0){
    // Восстанавливаем ширину буфера спрайта
    $pop AX
    loop(DX){
      CX=BX>>2;
      $rep $FS: $movsd
      SI=SI+320-BX;
      DI=DI+AX-BX;
    }
 }ELSE{
    // Восстанавливаем ширину буфера спрайта
    $pop AX
    loop(DX){
      CX=BX>>2;
      $rep $FS: $movsd
      $FS: $movsw
      $FS: $movsb
      SI=SI+320-BX;
      DI=DI+AX-BX;
    }
 }

 // Восстанавливаем ES
 ES=FS;
}



// Из-за невозможности использовать в объявлении процедур одновременно и
// регистры и переменные, придется юзать отдельную переменную
:byte TrColorForPutTrSprite=0FFh;
// Вывод прозрачного спрайта
// ES - сегмент видео буфера или видео памяти
// CX - Sprite X
// DI - Sprite Y
// BX - Sprite Width
// DX - Sprite Height
// AX - Sprite Seg
// SI - Sprite Offs
:void fastcall PutTrSprite(CX,DI,BX,DX,AX,SI)
{
 // Устанавливаем FS на сегмент спрайта
 FS=AX;

 // Выходим, если спрайт не виден
 IF (int CX>=320)||(int DI>=200)||(int CX+BX<=0)||(int DI+DX<=0) return;

 // Сохраняем BP (используется в стековых процедурах)
 $push BP

  // Устанавливаем BP на ширину буфера спрайта
  BP=BX;

  // Делаем клипирование
  IF (int DI<0){
     DX+=DI;
     $push DX
      AX=DI*BX; SI-=AX;
     $pop DX
     DI=0;
  }
  IF (int DI+DX>=200){
     DX=200-DI;
  }
  IF (int CX<0){
     BX+=CX;
     SI-=CX;
     CX=0;
  }
  IF (int CX+BX>=320){
     BX=320-CX;
  }

  // Устанавливаем FS на сегмент буфера спрайта
  // SI - смещение в буфере спрайта
  // DX - высота спрайта
  // DI - смещение в видео буфере
  // BX - ширина спрайта
  // BP - ширина буфера спрайта
  // AH - прозрачный цвет
  AH=TrColorForPutTrSprite;
  // Вычисляем смещение в видео буфере
  DI=DI*320+CX;

  // Рисуем спрайт
  loop(DX){
    // CX - ширана спрайта
    CX=BX;
    loop(CX){
      IF (FSBYTE[SI]!=AH) ESBYTE[DI]=FSBYTE[SI];
      DI++;
      SI++;
    }
    DI=DI+320-BX;
    SI=SI+BP-BX;
  }

 // Восстанавливаем BP
 $pop BP
}
#endif


#ifdef __FLAT__
// ES - сегмент видео буфера или видео памяти
// На входе:
// EBX = x
// EAX = y
// CL = color
// На выходе:
inline void fastcall PutPixel19(EAX,EBX,CL)
{
        AH+=BL; EBX<<=6;
        ESBYTE[EBX+EAX]=CL;
}



// ES - сегмент видео буфера или видео памяти
// На входе:
// EBX = x
// EAX = y
// На выходе:
// AL = color
inline byte fastcall GetPixel19(EAX,EBX)
{
        AH+=BL; EBX<<=6;
        return(ESBYTE[EBX+EAX]);
}


// Рисование линии
:void Line(dword x1,y1,x2,y2; byte color)
{
        AL=color;
        ECX=y1;
        EBX=x1;
        EDX=y2;
        EBP=x2;
        IF(int ECX>EDX ){
                ECX><EDX;
                EBX><EBP;
        }
        EDX-=ECX;
        ESI=1;
        EBP-=EBX;
        IF(MINUSFLAG){
#ifdef codesize
                -ESI;
#else
                ESI-=2;
#endif
                -EBP;
        }
        BH+=CL; ECX<<=6; EBX+=ECX;
        ESBYTE[EBX]=AL;
        IF(int EBP>EDX){
                ECX=EBP;
                EDX+=EDX;
                EDI=EDX;
                EDI-=EBP;
                EBP+=EBP;
                LOOPNZ(ECX){
                        IF(int EDI>=0){
                                EBX+=320;
                                EDI-=EBP;
                        }
                        EDI+=EDX;
                        EBX+=ESI;
                        ESBYTE[EBX]=AL;
                }
        }
        ELSE{
                ECX=EDX;
                EBP+=EBP;
                EDI=EBP;
                EDI-=EDX;
                EDX+=EDX;
                LOOPNZ(ECX){
                        IF(int EDI>=0){
                                EBX+=ESI;
                                EDI-=EDX;
                        }
                        EDI+=EBP;
                        EBX+=320;
                        ESBYTE[EBX]=AL;
                }
        }
}

:void LINE(EBX,ECX,EDI,EDX,AL)
{
        $push EBP
        IF(int ECX>EDX ){
                ECX><EDX;
                EBX><EBP;
        }
        EDX-=ECX;
        ESI=1;
        EBP-=EBX;
        IF(MINUSFLAG){
#ifdef codesize
                -ESI;
#else
                ESI-=2;
#endif
                -EBP;
        }
        BH+=CL; ECX<<=6; EBX+=ECX;
        ESBYTE[EBX]=AL;
        IF(int EBP>EDX){
                ECX=EBP;
                EDX+=EDX;
                EDI=EDX;
                EDI-=EBP;
                EBP+=EBP;
                LOOPNZ(ECX){
                        IF(int EDI>=0){
                                EBX+=320;
                                EDI-=EBP;
                        }
                        EDI+=EDX;
                        EBX+=ESI;
                        ESBYTE[EBX]=AL;
                }
        }
        ELSE{
                ECX=EDX;
                EBP+=EBP;
                EDI=EBP;
                EDI-=EDX;
                EDX+=EDX;
                LOOPNZ(ECX){
                        IF(int EDI>=0){
                                EBX+=ESI;
                                EDI-=EDX;
                        }
                        EDI+=EBP;
                        EBX+=320;
                        ESBYTE[EBX]=AL;
                }
        }
        $pop EBP
}


#ifdef speed
// Рисование коробки
// ES = Video Buf(Mem) Sel
// ESI = x
// EDI = y
// EDX = Width
// EBX = Height
// AL = Color
:void fastcall Box(ESI,EDI,EDX,EBX,AL)
{
        IF(EBX>1){
                EDI=EDI*320+ESI;
                AH=AL;
                $call HOR
// Рисуем вертикальные линии
                ECX=320-EDX;
                EDI+=ECX;
                ESI=EDX;
                ESI-=2;
                EBX-=2;
                IF(NOTZEROFLAG){
                        loop(EBX){
                                $stosb
                                EDI+=ESI;
                                $stosb
                                EDI+=ECX;
                        }
                }
// Рисуем нижнюю горизонтальную линию
HOR:
                ECX=EDX;
                ECX>>=1;
                IF(CARRYFLAG)$stosb
                ECX>>=1;
                IF(CARRYFLAG)$stosw
// Рисуем верхнюю горизонтальную линию
                ESI=EAX;
                EAX<<=16;
                AX=SI;
                $rep $stosd
        }
}
#else
// Рисование коробки
// ES = Video Buf(Mem) Seg
// ESI = x
// EDI = y
// EDX = Width
// EBX = Height
// AL = Color
:void fastcall Box(ESI,EDI,EDX,EBX,AL)
{
        IF(EBX>1){
                EDI=EDI*320+ESI;
                $call HOR
// Рисуем вертикальные линии
                ECX=320-EDX;
                EDI+=ECX;
                ESI=EDX;
                ESI-=2;
                EBX-=2;
                IF(NOTZEROFLAG){
                        loop(EBX){
                                $stosb
                                EDI+=ESI;
                                $stosb
                                EDI+=ECX;
                        }
                }
// Рисуем нижнюю горизонтальную линию
HOR:   // Рисуем верхнюю горизонтальную линию
                ECX=EDX; $rep $stosb
        }
}
#endif



#ifdef speed
// Рисование закрашенного прямоугольника
// ES = Video Buf(Mem) Seg
// EAX = x
// EDI = y
// EDX = Width
// ESI = Height
// BL = Color
:void fastcall Bar(EAX,ECX,EDX,ESI,BL)
{
 // Вычисляем начальные адрес прямоугольника
        AH+=CL; ECX<<=6;
        EDI=EAX+ECX;
 // Рисуем прямоугольник
        BH=BL; EAX=EBX; EAX<<=16; AX=BX;
        EBX=EDX>>2;
        FOR (; int ESI>0; ESI--,EDI=EDI+320-EDX){
                ECX=EBX; $rep $stosd
                ECX=EDX&3; $rep $stosb
        }
}
#else
// Рисование закрашенного прямоугольника
// ES = Video Buf(Mem) Seg
// EBX = x
// EDI = y
// EDX = Width
// ESI = Height
// AL = Color
:void fastcall Bar(EBX,EDI,EDX,ESI,AL)
{
 // Вычисляем начальные адрес прямоугольника
        EDI=EDI*320+EBX;
 // Рисуем прямоугольник
        FOR(;int ESI>0;ESI--){
    ECX=EDX; $rep $stosb
    EDI=EDI+320-EDX;
        }
}
#endif



/********************************************
        Вывод пиксела на экран со смещенным центром
        EAX = x
        EBX = y
        CL = color
        EDI = x0
        ESI = y
*********************************************/
inline void PUTPIXELC(EAX,EBX,CL,EDI,ESI)
{
        EAX+=EDI;
        EBX+=ESI;
        PutPixel19(EAX,EBX,CL);
}

// EDI = x
// ESI = y
// EDX = radius
// CL = color
:void fastcall Circle(EDI,ESI,EDX,CL)
int x,c;
{
        x=0;
        c=1-EDX<<1;
        -EDX;
        loop(){
                PUTPIXELC(x,EDX,CL,EDI,ESI);
                PUTPIXELC(-x,EDX,CL,EDI,ESI);
                PUTPIXELC(x,-EDX,CL,EDI,ESI);
                PUTPIXELC(-x,-EDX,CL,EDI,ESI);
                IF (EDX==0) BREAK;
                EBX=c;
                IF (int EBX<=x){
                        x++;
                        c=x<<1+c+1;
                }
                IF (int EBX<=EDX) CONTINUE;
                EDX++;
                c=EDX<<1+c+1;
        }
}


/***********************************
        EBX = x
        EDI = y
        ECX = len
        AX = color
***********************************/
inline void HORIZONTALDUBLLINE(EBX,EDI,ECX,AX)
{
        EDI=EDI*320+EBX-ECX;
        $rep $stosw
        $stosb
}

// EBX = x
// ESI = y
// EDI = radius
// CL = color
:void fastcall FilledCircle(EBX,ESI,EDX,CL)
int x,c;
{
        x=0;
        c=1-EDX<<1;
        -EDX;
        CH=CL;
        loop(){
                $push ECX
                EAX=ECX;
                HORIZONTALDUBLLINE(EBX,ESI-EDX,x,AX);
                HORIZONTALDUBLLINE(EBX,ESI+EDX,x,AX);
                $pop ECX
                IF (EDX==0) BREAK;
                EDI=c;
                IF (int EDI<=x){
                        x++;
                        c=x<<1+c+1;
                }
                IF (int EDI<=EDX) CONTINUE;
                EDX++;
                c=EDX<<1+c+1;
        }
}




// Установка палитры
// Пареметры:
// EBX - first color
// ECX - number colors to set
// EDX - buf offs
:void fastcall SetVgaPalette(EBX,ECX,EDX)
{
  $push ES
  ES=DS;
  AX=1012h;
  $int 10h
  $pop ES
}



// Заливка
// Предупреждение: Процедура активно использует stack!
// EDI - x
// ESI - y
// CL - color
// AX - селектор рабочего буфера
// EBX - смещение конца рабочено буфера
// Если используешь stack, то SS:[ESP-4]
:void fastcall Fill(EDI,ESI,CL,AX,EBX)
{
 IF (EDI<320)&&(ESI<200){
//    $cli
     $push SS $pop FS EDX=ESP; SS=AX; ESP=EBX;
      EBX=EDI; EAX=ESI;
      // Вычисляем адрес в видео буфере или в видео памяти
      BH+=AL; EAX<<=6; EBX+=EAX;
      // Берем цвет по этому адресу
      CH=ESBYTE[EBX];
      // Делаем заливку, если заливаемый цвет не цвет заливки
      IF (CH!=CL) $call FILLPROC
     AX=FS; SS=AX; ESP=EDX;
//    $sti
 }
 $ret
FILLPROC:
 // Ставим пиксел
 ESBYTE[EBX]=CL;

 IF (long EDI>0){
    // Делаем проверку на рекурсивный вызов
    EBX--;
     IF (ESBYTE[EBX]==CH){
        EDI--;
         $call FILLPROC
        EDI++;
     }
    EBX++;
 }

 IF (long EDI<319){
    EBX++;
     IF (ESBYTE[EBX]==CH){
        EDI++;
         $call FILLPROC
        EDI--;
     }
    EBX--;
 }

 IF (long ESI<199){
    EBX+=320;
     IF (ESBYTE[EBX]==CH){
        ESI++;
         $call FILLPROC
        ESI--;
     }
    EBX-=320;
 }

 IF (long SI>0){
    EBX-=320;
     IF (ESBYTE[EBX]==CH){
        ESI--;
         $call FILLPROC
        ESI++;
     }
    EBX+=320;
 }
}



:struct _TEXTUREDFILL_
{
 word Seg;            // Сегмент текстуры
 dword Offs;          // Смещение текстуры
 dword Width,Height;  // Ширина и высота текстуры
 dword x,y;           // Координаты в текстуре
}TexturedFillStruct;

// Заливка текстурой
// Предупреждение: Процедура активно использует stack!
// EDI - x
// ESI - y
// AX - селектор рабочего буфера
// EBX - смещение конца рабочено буфера
// Если используешь stack, то SS:[ESP-4]
:void fastcall TexturedFill(EDI,ESI,AX,EBX)
{
        IF (EDI<320)&&(ESI<200){
//    $cli
     $push SS $pop FS EDX=ESP; SS=AX; ESP=EBX;
      $push EBP,EDX,GS

       // Устанавливаем GS на сегмент текстуры
       GS=TexturedFillStruct.Seg;
       // Вычисляем начальный адрес в текстуре
       EBP=TexturedFillStruct.y*TexturedFillStruct.Width+TexturedFillStruct.x+TexturedFillStruct.Offs;
       // Вычисляем адрес в видео буфере или в видео памяти
       EBX=EDI; EAX=ESI; BH+=AL; EAX<<=6; EBX+=EAX;
       // Берем цвет по этому адресу
       CH=ESBYTE[EBX];
       // Устанавливаем EAX на текущую координату x в текстуре
       EAX=TexturedFillStruct.x;
       // Устанавливаем EDX на текущую координату y в текстуре
       EDX=TexturedFillStruct.y;
       // Делаем заливку, если заливаемый цвет не цвет заливки
       $call FILLPROC

      $pop GS,EDX,EBP
     AX=FS; SS=AX; ESP=EDX;
//    $sti
 }
 $ret
FILLPROC:
 // Ставим пиксел
 CL=GSBYTE[EBP];
 IF (CL==CH) $ret
 ESBYTE[EBX]=CL;

 IF (long EDI>0){
    // Делаем проверку на рекурсивный вызов
    EBX--;
     IF (ESBYTE[EBX]==CH){
        EDI--;
         IF (long EAX==0){EAX=TexturedFillStruct.Width; EBP+=EAX;}
         EBP--;
          EAX--;
           $call FILLPROC
          EAX++;
         EBP++;
         IF (long EAX==TexturedFillStruct.Width){EBP-=EAX; EAX=0;}
        EDI++;
     }
    EBX++;
 }

 IF (long EDI<319){
    EBX++;
     IF (ESBYTE[EBX]==CH){
        EDI++;
         EBP++;
          EAX++;
          IF (long EAX==TexturedFillStruct.Width){EBP-=EAX; EAX=0;}
           $call FILLPROC
          IF (long EAX==0){EAX=TexturedFillStruct.Width; EBP+=EAX;}
          EAX--;
         EBP--;
        EDI--;
     }
    EBX--;
 }

 IF (long ESI<199){
    EBX+=320;
     IF (ESBYTE[EBX]==CH){
        ESI++;
         EBP+=TexturedFillStruct.Width;
          EDX++;
          IF (long EDX==TexturedFillStruct.Height){EDX=0; EBP=EAX+TexturedFillStruct.Offs;}
           $call FILLPROC
          IF (long EDX==0){EDX=TexturedFillStruct.Height; EBP=EDX*TexturedFillStruct.Width+EAX+TexturedFillStruct.Offs;}
          EDX--;
         EBP-=TexturedFillStruct.Width;
        ESI--;
     }
    EBX-=320;
 }

 IF (long ESI>0){
    EBX-=320;
     IF (ESBYTE[EBX]==CH){
        ESI--;
         IF (long EDX==0){EDX=TexturedFillStruct.Height; EBP=EDX*TexturedFillStruct.Width+EAX+TexturedFillStruct.Offs;}
         EBP-=TexturedFillStruct.Width;
          EDX--;
           $call FILLPROC
          EDX++;
         EBP+=TexturedFillStruct.Width;
         IF (long EDX==TexturedFillStruct.Height){EDX=0; EBP=EAX+TexturedFillStruct.Offs;}
        ESI++;
     }
    EBX+=320;
 }
}



// ES - селектор видео буфера или видео памяти
// ECX - Sprite X
// EDI - Sprite Y
// EBX - Sprite Width
// EDX - Sprite Height
// EAX - Sprite Seg
// ESI - Sprite Offs
:void fastcall PutSprite(ECX,EDI,EBX,EDX,EAX,ESI)
{
 // Устанавливаем FS на селектор спрайта
 FS=AX;

 // Выходим, если спрайт не виден
 IF (long ECX>=320)||(long EDI>=200)||(long ECX+EBX<=0)||(long EDI+EDX<=0) return;

 // Сохраняем ширину буфера спрайта на стеке
 $push EBX

 // Делаем клипирование
 IF (long EDI<0){
    EDX+=EDI;
    $push EDX
     EAX=EDI*EBX; ESI-=EAX;
    $pop EDX
    EDI=0;
 }
 IF (long EDI+EDX>=200) EDX=200-EDI;
 IF (long ECX<0){
    EBX+=ECX;
    ESI-=ECX;
    ECX=0;
 }
 IF (long ECX+EBX>=320) EBX=320-ECX;

 // Рисуем спрайт
 // Устанавливаем FS на селектор буфера спрайта
 // ESI - смещение в буфере спрайта
 // EDX - высота спрайта
 // EDI - смещение в видео буфере
 EDI=EDI*320+ECX;
 // EBX - ширина спрайта
 EAX=EBX&3;
 IF (EAX==1){
    // Восстанавливаем ширину буфера спрайта
    $pop EAX
    loop(EDX){
      ECX=EBX>>2;
      $rep $FS: $movsd
      $FS: $movsb
      EDI=EDI+320-EBX;
      ESI=ESI+EAX-EBX;
    }
 }ELSE IF (EAX==2){
    // Восстанавливаем ширину буфера спрайта
    $pop EAX
    loop(EDX){
      ECX=EBX>>2;
      $rep $FS: $movsd
      $FS: $movsw
      EDI=EDI+320-EBX;
      ESI=ESI+EAX-EBX;
    }
 }ELSE IF (EAX==0){
    // Восстанавливаем ширину буфера спрайта
    $pop EAX
    loop(EDX){
      ECX=EBX>>2;
      $rep $FS: $movsd
      EDI=EDI+320-EBX;
      ESI=ESI+EAX-EBX;
    }
 }ELSE{
    // Восстанавливаем ширину буфера спрайта
    $pop EAX
    loop(EDX){
      ECX=EBX>>2;
      $rep $FS: $movsd
      $FS: $movsw
      $FS: $movsb
      EDI=EDI+320-EBX;
      ESI=ESI+EAX-EBX;
    }
 }
}



// ES - селектор видео буфера или видео памяти
// ECX - Sprite X
// ESI - Sprite Y
// EBX - Sprite Width
// EDX - Sprite Height
// EAX - Sprite Seg
// EDI - Sprite Offs
:void fastcall GetSprite(ECX,ESI,EBX,EDX,EAX,EDI)
{
 // Устанавливаем FS на ES (селектор видео буфера или видео памяти)
 $push ES $pop FS
 // Устанавливаем ES на селектор спрайта
 ES=AX;

 // Выходим, если спрайт не виден
 IF (long ECX>=320)||(long ESI>=200)||(long ECX+EBX<=0)||(long ESI+EDX<=0){
    ES=FS;
    $ret
 }

 // Сохраняем ширину буфера спрайта на стеке
 $push EBX

 // Делаем клипирование
 IF (long ESI<0){
    EDX+=ESI;
    $push EDX
     EAX=ESI*EBX; EDI-=EAX;
    $pop EDX
    ESI=0;
 }
 IF (long ESI+EDX>=200){
    EDX=200-ESI;
 }
 IF (long ECX<0){
    EBX+=ECX;
    EDI-=ECX;
    ECX=0;
 }
 IF (long ECX+EBX>=320){
    EBX=320-ECX;
 }

 // Рисуем спрайт
 // ESI - смещение в видео буфере
 // EDX - высота спрайта
 // EDI - смещение в буфере спрайта
 ESI=ESI*320+ECX;
 // EBX - ширина спрайта
 EAX=EBX&3;
 IF (EAX==1){
    // Восстанавливаем ширину буфера спрайта
    $pop EAX
    loop(EDX){
      ECX=EBX>>2;
      $rep $FS: $movsd
      $FS: $movsb
      ESI=ESI+320-EBX;
      EDI=EDI+EAX-EBX;
    }
 }ELSE IF (EAX==2){
    // Восстанавливаем ширину буфера спрайта
    $pop EAX
    loop(EDX){
      ECX=EBX>>2;
      $rep $FS: $movsd
      $FS: $movsw
      ESI=ESI+320-EBX;
      EDI=EDI+EAX-EBX;
    }
 }ELSE IF (EAX==0){
    // Восстанавливаем ширину буфера спрайта
    $pop EAX
    loop(EDX){
      ECX=EBX>>2;
      $rep $FS: $movsd
      ESI=ESI+320-EBX;
      EDI=EDI+EAX-EBX;
    }
 }ELSE{
    // Восстанавливаем ширину буфера спрайта
    $pop EAX
    loop(EDX){
      ECX=EBX>>2;
      $rep $FS: $movsd
      $FS: $movsw
      $FS: $movsb
      ESI=ESI+320-EBX;
      EDI=EDI+EAX-EBX;
    }
 }

 // Восстанавливаем ES
 ES=FS;
}



// Из-за невозможности использовать в объявлении процедур одновременно и
// регистры и переменные, придется юзать отдельную переменную
:byte TrColorForPutTrSprite=0FFh;
// Вывод прозрачного спрайта
// ES - сегмент видео буфера или видео памяти
// ECX - Sprite X
// EDI - Sprite Y
// EBX - Sprite Width
// EDX - Sprite Height
// AX -  Sprite Sel
// ESI - Sprite Offs
:void fastcall PutTrSprite(ECX,EDI,EBX,EDX,AX,ESI)
{
 // Устанавливаем FS на селектор спрайта
 FS=AX;

 // Выходим, если спрайт не виден
 IF (long ECX>=320)||(long EDI>=200)||(long ECX+EBX<=0)||(long EDI+EDX<=0) return;

 // Сохраняем EBP (используется в стековых процедурах)
 $push EBP

  // Устанавливаем EBP на ширину буфера спрайта
  EBP=EBX;

  // Делаем клипирование
  IF (long EDI<0){
     EDX+=EDI;
     $push EDX
      EAX=EDI*EBX; ESI-=EAX;
     $pop EDX
     EDI=0;
  }
  IF (long EDI+EDX>=200){
     EDX=200-EDI;
  }
  IF (long ECX<0){
     EBX+=ECX;
     ESI-=ECX;
     ECX=0;
  }
  IF (long ECX+EBX>=320){
     EBX=320-ECX;
  }

  // Устанавливаем FS на селектор буфера спрайта
  // ESI - смещение в буфере спрайта
  // EDX - высота спрайта
  // EDI - смещение в видео буфере
  // EBX - ширина спрайта
  // EBP - ширина буфера спрайта
  // AH - прозрачный цвет
  AH=TrColorForPutTrSprite;
  // Вычисляем смещение в видео буфере
  EDI=EDI*320+ECX;

  // Рисуем спрайт
  loop(EDX){
    // ECX - ширана спрайта
    ECX=EBX;
    loop(ECX){
      IF (FSBYTE[ESI]!=AH) ESBYTE[EDI]=FSBYTE[ESI];
      EDI++;
      ESI++;
    }
    EDI=EDI+320-EBX;
    ESI=ESI+EBP-EBX;
  }

 // Восстанавливаем EBP
 $pop EBP
}
#endif
