#define MAXX 320
#define MAXY 200
#define SIZESCR MAXX*MAXY

:short scrseg;
:char tbuf[SIZESCR];
:int x320[MAXX];
:dword buf;
:struct RGB
{
	char R,G,B;
}pal[256];


void set_rgb(char c,R,G,B);
void putpixel(int x,int y,byte c);

:void initvideo()
{
	scrseg=getseg(0xA000);
	FOR(ESI=0;ESI<MAXY;ESI++)x320[ESI*4]=ESI*MAXX;
	buf=#tbuf;
}

//Рисует точку в видеобуфер с проверкой на границы
:void putpixel(int x,int y,byte c)
{
	$push ECX
	EAX=x; ECX=y;
	IF(EAX<MAXX)&&(ECX<MAXY){
		ECX=x320[ECX*4]+EAX+buf;
		DSBYTE[ECX]=c;
	}
	$pop ECX
}

// функция установки RGB заданного цвета в палитре
:void set_rgb(char c,R,G,B)
{
	EDX=0x3c8;
	outp(c,EDX);
	EDX++;
	outp(R,EDX);
	outp(G,EDX);
	outp(B,EDX);
}

// функция чтения RGB заданного цвета в палитре
:void get_rgb(char c;dword R,G,B)
{
	EDX=0x3c7;
	outp(c,EDX);
	EDX+=2;
	inp(EDX);
	ECX=R;
	DSBYTE[ECX]=AL;
	inp(EDX);
	ECX=G;
	DSBYTE[ECX]=AL;
	inp(EDX);
	ECX=B;
	DSBYTE[ECX]=AL;
}

// градиентная палитра
// интерполирует между начальными и конечными RGB
// на заданном интервале
:void set_gradient_palette(int i1,i2,R1,G1,B1,R2,G2,B2)
{
int i,k;
char R,G,B;
	IF(i2==i1)k=1;
	ELSE k=i2-i1;
	FOR(i=i1;i<=i2;i++){
		ECX=i-i1;
		EDX=R2-R1;
		R=long ECX*EDX/k+R1;
		EDX=G2-G1;
		G=long ECX*EDX/k+G1;
		EDX=B2-B1;
		B=long ECX*EDX/k+B1;
		set_rgb(i,R,G,B);
	}
}

:void getpalette()
{
	$push ESI
	FOR(ESI=0;ESI<256*3;ESI+=3)get_rgb(ESI/3,#pal.R[ESI],#pal.G[ESI],#pal.B[ESI]);
	$pop ESI
}

:void fade(char grad)
{
char R,G,B;
int i;
	$push ESI
	for(ESI=0,CH=0;ESI<256;ESI+=3,CH++){
		R=DSBYTE[#pal.R+ESI];
		G=DSBYTE[#pal.G+ESI];
		B=DSBYTE[#pal.B+ESI];
		CL=grad;
		IF(CL>128){
			CL-=128;
			DL=63-R;
			R=CL*DL/128+R;
			DL=63-G;
			G=CL*DL/128+G;
			DL=63-B;
			B=CL*DL/128+B;
		}
		ELSE IF(CL<128){
		  R=CL*R/128;
		  G=CL*G/128;
			B=CL*B/128;
		}
		set_rgb(CH,R,G,B);
	}
	$pop ESI
}

// Процедура рисования линии по Брезенхейму
inline int fastcall abs(EAX)
{
	if(int EAX<0)-EAX;
}

:void line (int x1, int y1, int x2, int y2, char color)
{
// никаких ограничений на входные данные, так что x1>x2 без проблем :)
int d, ax, ay, sx, sy;
	$push EBX
// Здесь мы будем использовать только целочисленную арифметику
	EBX=x2-x1;
	ax=abs(EBX) << 1;
	IF(long EBX<0)sx=-1;
	ELSE sx=1;
	EBX=y2-y1;
	ay=abs(EBX)<<1;
	IF(long EBX<0)sy=-1;
	ELSE sy=1;
	putpixel (x1, y1, color);
	IF(ax>ay) {
		EAX=ax>>1-ay;
		d=-EAX;
		WHILE(x1!=x2) {
			IF(d>=0){
				y1+=sy;
				d-=ax;
			}
			x1+=sx;
			d+=ay;
			putpixel(x1,y1,color);
		}
	}
	ELSE{
		EAX=ay>>1-ax;
		d=-EAX;
		WHILE(y1!=y2) {
			IF(d>=0){
				x1+=sx;
				d-=ay;
			}
			y1+=sy;
			d+=ax;
			putpixel (x1, y1, color);
		}
	}
	$pop EBX
}

//Функция ожидания обратного хода луча
:void waitvsync()
{
	EDX=0x3DA;
	do{
		inp();
		AL&=8;
	}while(NOTZEROFLAG);
	do{
		inp();
		AL&=8;
	}while(ZEROFLAG);
}

//ставит 4 пиксела в углы прямоугольника
:void Set4Pixels(int x,y,xc,yc;char n)
{
	putpixel( xc+x, yc+y, n );
	putpixel( xc-x, yc+y, n );
	putpixel( xc+x, yc-y, n );
	putpixel( xc-x, yc-y, n );
}

//рисует эллипс по Брезенхейму
:void ellipse( int xc,yc,a0,b0;char color){
int x = 0;
int y = b0;
int a = a0;
int b = b0;
int Asquared = a * a;
int TwoAsquared = 2 * Asquared;
int Bsquared = b * b;
int TwoBsquared = 2 * Bsquared;
int d;
int dx,dy;
	ECX=Asquared*b;
	EDX=Asquared>>2;
	d = Bsquared - ECX + EDX;
	dx = 0;
	dy = TwoAsquared * b;
	while (dx<dy){
		Set4Pixels(x,y,xc,yc,color);
		IF(d>0){
			y--;
			dy -= TwoAsquared;
			d -= dy;
		}
		x++;
		dx += TwoBsquared;
		d += Bsquared + dx;
	}
	d+=Asquared-Bsquared*3/2-dx-dy/2;
	while (y>=0){
		Set4Pixels( x, y, xc, yc, color );
		if (d<0){
			x++;
			dx += TwoBsquared;
			d += dx;
		}
		y--;
		dy -= TwoAsquared;
		d += Asquared - dy;
	}
}

:void blur()
{
	$push ESI,EBX
	ESI=MAXX+1;
	ECX=buf;
	FOR(EDX=1;EDX<MAXY-1;EDX++){
		FOR(EBX=1;EBX<MAXX-1;EBX++,ESI++){
			DSBYTE[ECX+ESI]=int DSBYTE[ECX+ESI-MAXX]+DSBYTE[ECX+ESI-1]+
				DSBYTE[ECX+ESI+1]+DSBYTE[ECX+ESI+MAXX]>>2;
		}
		ESI+=2;
	}
	$pop EBX,ESI
}

:void copy_to_screen()
{
	waitvsync();
	ES=scrseg;
	memcpyd(0,buf,SIZESCR/4);
}
