// triangle.cpp : Defines the entry point for the application.
//

#include "stdafx.h"
#include "resource.h"
#include "math.h"
#include "mmsystem.h"



#define MAX_LOADSTRING 100

// Global Variables:
HINSTANCE hInst;								// current instance
HWND hMainWindow;
TCHAR szTitle[MAX_LOADSTRING];								// The title bar text
TCHAR szWindowClass[MAX_LOADSTRING];								// The title bar text

// Foward declarations of functions included in this code module:
ATOM				MyRegisterClass(HINSTANCE hInstance);
BOOL				InitInstance(HINSTANCE, int);
LRESULT CALLBACK	WndProc(HWND, UINT, WPARAM, LPARAM);
LRESULT CALLBACK	About(HWND, UINT, WPARAM, LPARAM);


int MessageLoop();




typedef struct _BITMAPINFO256 {
  BITMAPINFOHEADER bmiHeader;
  WORD bmiColors[256];
} BITMAPINFO256;

BITMAPINFO256 bmi;

void Display(HDC dc, unsigned short *buf, int x, int y, int w, int h)
{
  bmi.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
  bmi.bmiHeader.biWidth = w;
  bmi.bmiHeader.biHeight = -h;
  bmi.bmiHeader.biPlanes = 1;
  bmi.bmiHeader.biBitCount = 16;
  bmi.bmiHeader.biCompression = BI_RGB;
  bmi.bmiHeader.biSizeImage = 0;
  bmi.bmiHeader.biXPelsPerMeter = 1000;
  bmi.bmiHeader.biYPelsPerMeter = 1000;
  bmi.bmiHeader.biClrUsed = 0;
  bmi.bmiHeader.biClrImportant = 0;

  StretchDIBits(
    dc,
    x, y, w, h,
    0, 0, w, h,
    (LPVOID)buf,
    (LPBITMAPINFO)&bmi,
    DIB_RGB_COLORS, SRCCOPY
  );

}





/*

VERSION 1 - 8 clocks

    ;AGI

    mov eax, ebx
    add edx, 0x00000000       //  SM

START:

    rol eax, width

    and eax, mask
    adc ebx, 0x00000000       //  SM

    add esi, 0x00000000       //  SM

    mov ax, [eax+0x00000000]  //  SM

    mov [edi+ebp], ax

    mov eax, ecx

    rol eax, width

    and eax, mask
    adc ecx, 0x00000000       //  SM

    add edx, 0x00000000       //  SM

    mov ax, [eax+0x00000000]  //  SM

    mov [edi+ebp+1], ax

    mov eax, ebx

    add ebp, 2
    jnc start



*/



//----------------------------------------------------------------------------//

#define SCREEN_W 1024
#define SCREEN_H  768



bool tri_backfacecull = false;

float tri_x1, tri_y1, tri_z1, tri_r1, tri_g1, tri_b1, tri_a1, tri_u1, tri_v1, tri_d1, tri_s1;
float tri_x2, tri_y2, tri_z2, tri_r2, tri_g2, tri_b2, tri_a2, tri_u2, tri_v2, tri_d2, tri_s2;
float tri_x3, tri_y3, tri_z3, tri_r3, tri_g3, tri_b3, tri_a3, tri_u3, tri_v3, tri_d3, tri_s3;

int clip_x1 = 0, clip_y1 = 0, clip_x2 = SCREEN_W, clip_y2 = SCREEN_H;

unsigned int screen_w = SCREEN_W, screen_h = SCREEN_H;
unsigned short screen[SCREEN_H][SCREEN_W];
unsigned short texture[256][256];



//----------------------------------------------------------------------------//



void Tri_Tex16_0001()
{
    int i, j;

    //- Sort points

    float t;

    if(tri_y2 < tri_y1)
    {
        t = tri_x1; tri_x1 = tri_x2; tri_x2 = t;
        t = tri_y1; tri_y1 = tri_y2; tri_y2 = t;
        t = tri_u1; tri_u1 = tri_u2; tri_u2 = t;
        t = tri_v1; tri_v1 = tri_v2; tri_v2 = t;
    }

    if(tri_y3 < tri_y2)
    {
        t = tri_x2; tri_x2 = tri_x3; tri_x3 = t;
        t = tri_y2; tri_y2 = tri_y3; tri_y3 = t;
        t = tri_u2; tri_u2 = tri_u3; tri_u3 = t;
        t = tri_v2; tri_v2 = tri_v3; tri_v3 = t;
    }

    if(tri_y2 < tri_y1)
    {
        t = tri_x1; tri_x1 = tri_x2; tri_x2 = t;
        t = tri_y1; tri_y1 = tri_y2; tri_y2 = t;
        t = tri_u1; tri_u1 = tri_u2; tri_u2 = t;
        t = tri_v1; tri_v1 = tri_v2; tri_v2 = t;
    }

    //- Work out deltas along edges

    float dx12 = (tri_x2-tri_x1)/(tri_y2-tri_y1), dx23 = (tri_x3-tri_x2)/(tri_y3-tri_y2), dx13 = (tri_x3-tri_x1)/(tri_y3-tri_y1);
    float du12 = (tri_u2-tri_u1)/(tri_y2-tri_y1), du23 = (tri_u3-tri_u2)/(tri_y3-tri_y2), du13 = (tri_u3-tri_u1)/(tri_y3-tri_y1);
    float dv12 = (tri_v2-tri_v1)/(tri_y2-tri_y1), dv23 = (tri_v3-tri_v2)/(tri_y3-tri_y2), dv13 = (tri_v3-tri_v1)/(tri_y3-tri_y1);

    //- Round to pixel and clip to top & bottom of screen

    int y1 = (int)tri_y1; if(y1 < tri_y1) y1++;
    int y2 = (int)tri_y2; if(y2 < tri_y2) y2++;
    int y3 = (int)tri_y3; if(y3 < tri_y3) y3++;

    if(y1 < clip_y1) y1 = clip_y1;
    if(y2 < clip_y1) y2 = clip_y1;
    if(y3 < clip_y1) y3 = clip_y1;

    if(y1 > clip_y2) y1 = clip_y2;
    if(y2 > clip_y2) y2 = clip_y2;
    if(y3 > clip_y2) y3 = clip_y2;

    if(y3 <= y1) return;

    //- Calculate horizontal and vertical deltas for parameters

    float d = (tri_x3-tri_x1)*(tri_y2-tri_y1) - (tri_y3-tri_y1)*(tri_x2-tri_x1);

    float dux = ( (tri_y2-tri_y1)*(tri_u3-tri_u1) + (tri_y1-tri_y3)*(tri_u2-tri_u1) ) / d;
    float duy = ( (tri_x1-tri_x2)*(tri_u3-tri_u1) + (tri_x3-tri_x1)*(tri_u2-tri_u1) ) / d;

    float dvx = ( (tri_y2-tri_y1)*(tri_v3-tri_v1) + (tri_y1-tri_y3)*(tri_v2-tri_v1) ) / d;
    float dvy = ( (tri_x1-tri_x2)*(tri_v3-tri_v1) + (tri_x3-tri_x1)*(tri_v2-tri_v1) ) / d;

    //- See if we need to flip triangle horizontally

	bool side13onleft = dx13 < dx12;

    //- Draw top of triangle

    float xa, xb, dxa, dxb;

    if(side13onleft) {
      xa = tri_x1 + dx13 * (y1-tri_y1);
	  xb = tri_x1 + dx12 * (y1-tri_y1);
	  dxa = dx13;
	  dxb = dx12;
	} else {
	  xa = tri_x1 + dx12 * (y1-tri_y1);
      xb = tri_x1 + dx13 * (y1-tri_y1);
	  dxa = dx12;
	  dxb = dx13;
	}

    float ul = tri_u1 - tri_x1*dux + (y1-tri_y1)*duy;
    float vl = tri_v1 - tri_x1*dvx + (y1-tri_y1)*dvy;

    for(j=y1; j<y2; j++)
    {
        int xai = (int)xa; if(xai < xa) xai++;
        int xbi = (int)xb; if(xbi < xb) xbi++;

		if(xai < clip_x1) xai = clip_x1;
		if(xbi < clip_x1) xbi = clip_x1;
        if(xai > clip_x2) xai = clip_x2;
		if(xbi > clip_x2) xbi = clip_x2;

        float u = ul + xai*dux;
        float v = vl + xai*dvx;

        for(i=xai; i<xbi; i++)
        {
            screen[j][i] = texture[(int)v][(int)u];
            u += dux; v += dvx;
        }

        xa += dxa; xb += dxb;

        ul += duy;
        vl += dvy;
    }

    //- Draw bottom of triangle

    if(side13onleft) {
      xb = tri_x2 + dx23 * (y2-tri_y2);
	  dxb = dx23;
	} else {
      xa = tri_x2 + dx23 * (y2-tri_y2);
	  dxa = dx23;
	}

    for(j=y2; j<y3; j++)
    {
        int xai = (int)xa; if(xai < xa) xai++;
        int xbi = (int)xb; if(xbi < xb) xbi++;

		if(xai < clip_x1) xai = clip_x1;
		if(xbi < clip_x1) xbi = clip_x1;
		if(xai > clip_x2) xai = clip_x2;
		if(xbi > clip_x2) xbi = clip_x2;

        float u = ul + xai*dux;
        float v = vl + xai*dvx;

        for(i=xai; i<xbi; i++)
        {
            screen[j][i] = texture[(int)v][(int)u];
            u += dux; v += dvx;
        }

        xa += dxa; xb += dxb;

        ul += duy;
        vl += dvy;
    }

}



//----------------------------------------------------------------------------//



void Tri_Tex16_0002()
{
    int i, j;

    //- Sort points

    float t;

    if(tri_y2 < tri_y1)
    {
        t = tri_x1; tri_x1 = tri_x2; tri_x2 = t;
        t = tri_y1; tri_y1 = tri_y2; tri_y2 = t;
        t = tri_u1; tri_u1 = tri_u2; tri_u2 = t;
        t = tri_v1; tri_v1 = tri_v2; tri_v2 = t;
    }

    if(tri_y3 < tri_y2)
    {
        t = tri_x2; tri_x2 = tri_x3; tri_x3 = t;
        t = tri_y2; tri_y2 = tri_y3; tri_y3 = t;
        t = tri_u2; tri_u2 = tri_u3; tri_u3 = t;
        t = tri_v2; tri_v2 = tri_v3; tri_v3 = t;
    }

    if(tri_y2 < tri_y1)
    {
        t = tri_x1; tri_x1 = tri_x2; tri_x2 = t;
        t = tri_y1; tri_y1 = tri_y2; tri_y2 = t;
        t = tri_u1; tri_u1 = tri_u2; tri_u2 = t;
        t = tri_v1; tri_v1 = tri_v2; tri_v2 = t;
    }

    //- Work out deltas along edges

    float dx12 = (tri_x2-tri_x1)/(tri_y2-tri_y1), dx23 = (tri_x3-tri_x2)/(tri_y3-tri_y2), dx13 = (tri_x3-tri_x1)/(tri_y3-tri_y1);
    float du12 = (tri_u2-tri_u1)/(tri_y2-tri_y1), du23 = (tri_u3-tri_u2)/(tri_y3-tri_y2), du13 = (tri_u3-tri_u1)/(tri_y3-tri_y1);
    float dv12 = (tri_v2-tri_v1)/(tri_y2-tri_y1), dv23 = (tri_v3-tri_v2)/(tri_y3-tri_y2), dv13 = (tri_v3-tri_v1)/(tri_y3-tri_y1);

    //- Round to pixel and clip to top & bottom of screen

    int y1 = (int)tri_y1; if(y1 < tri_y1) y1++;
    int y2 = (int)tri_y2; if(y2 < tri_y2) y2++;
    int y3 = (int)tri_y3; if(y3 < tri_y3) y3++;

    if(y1 < clip_y1) y1 = clip_y1;
    if(y2 < clip_y1) y2 = clip_y1;
    if(y3 < clip_y1) y3 = clip_y1;

    if(y1 > clip_y2) y1 = clip_y2;
    if(y2 > clip_y2) y2 = clip_y2;
    if(y3 > clip_y2) y3 = clip_y2;

    if(y3 <= y1) return;

    //- Calculate horizontal and vertical deltas for parameters

    float d = (tri_x3-tri_x1)*(tri_y2-tri_y1) - (tri_y3-tri_y1)*(tri_x2-tri_x1);

    float dux = ( (tri_y2-tri_y1)*(tri_u3-tri_u1) + (tri_y1-tri_y3)*(tri_u2-tri_u1) ) / d;
    float duy = ( (tri_x1-tri_x2)*(tri_u3-tri_u1) + (tri_x3-tri_x1)*(tri_u2-tri_u1) ) / d;

    float dvx = ( (tri_y2-tri_y1)*(tri_v3-tri_v1) + (tri_y1-tri_y3)*(tri_v2-tri_v1) ) / d;
    float dvy = ( (tri_x1-tri_x2)*(tri_v3-tri_v1) + (tri_x3-tri_x1)*(tri_v2-tri_v1) ) / d;

    //- See if we need to flip triangle horizontally

	bool side13onleft = dx13 < dx12;

    //- Draw top of triangle

    float xa, xb, dxa, dxb;

    if(side13onleft) {
      xa = tri_x1 + dx13 * (y1-tri_y1);
	  xb = tri_x1 + dx12 * (y1-tri_y1);
	  dxa = dx13;
	  dxb = dx12;
	} else {
	  xa = tri_x1 + dx12 * (y1-tri_y1);
      xb = tri_x1 + dx13 * (y1-tri_y1);
	  dxa = dx12;
	  dxb = dx13;
	}

    float ul = tri_u1 - tri_x1*dux + (y1-tri_y1)*duy;
    float vl = tri_v1 - tri_x1*dvx + (y1-tri_y1)*dvy;

    int fxa  = xa *0x10000, fxb  = xb *0x10000;
    int fdxa = dxa*0x10000, fdxb = dxb*0x10000;
    int ful  = ul *0x10000, fvl  = vl *0x10000;
    int fdux = dux*0x10000, fdvx = dvx*0x10000;
    int fduy = duy*0x10000, fdvy = dvy*0x10000;

    for(j=y1; j<y2; j++)
    {
        int xai = (fxa+0xFFFF) >> 16;  //  int xai = (int)xa; if(xai < xa) xai++;
        int xbi = (fxb+0xFFFF) >> 16;  //  int xbi = (int)xb; if(xbi < xb) xbi++;

		if(xai < clip_x1) xai = clip_x1;
		if(xbi < clip_x1) xbi = clip_x1;
        if(xai > clip_x2) xai = clip_x2;
		if(xbi > clip_x2) xbi = clip_x2;

        int fu = ful + xai*fdux;  //  float u = ul + xai*dux;
        int fv = fvl + xai*fdvx;  //  float v = vl + xai*dvx;

        unsigned short *buf = &screen[j][xai];

        for(i=xai; i<xbi; i++)
        {
            *buf++ = texture[fv>>16][fu>>16];
            fu += fdux; fv += fdvx;
        }

        fxa += fdxa; fxb += fdxb;

        ful += fduy;
		fvl += fdvy;
    }

    //- Draw bottom of triangle

    if(side13onleft) {
      xb = tri_x2 + dx23 * (y2-tri_y2);
	  dxb = dx23;
      fxb  = xb *0x10000;
      fdxb = dxb*0x10000;
	} else {
      xa = tri_x2 + dx23 * (y2-tri_y2);
	  dxa = dx23;
      fxa  = xa *0x10000;
      fdxa = dxa*0x10000;
	}

    for(j=y2; j<y3; j++)
    {
        int xai = (fxa+0xFFFF) >> 16;  //  int xai = (int)xa; if(xai < xa) xai++;
        int xbi = (fxb+0xFFFF) >> 16;  //  int xbi = (int)xb; if(xbi < xb) xbi++;

		if(xai < clip_x1) xai = clip_x1;
		if(xbi < clip_x1) xbi = clip_x1;
		if(xai > clip_x2) xai = clip_x2;
		if(xbi > clip_x2) xbi = clip_x2;

        int fu = ful + xai*fdux;  //  float u = ul + xai*dux;
        int fv = fvl + xai*fdvx;  //  float v = vl + xai*dvx;

        unsigned short *buf = &screen[j][xai];

        for(i=xai; i<xbi; i++)
        {
            *buf++ = texture[fv>>16][fu>>16];
            fu += fdux; fv += fdvx;
        }

        fxa += fdxa; fxb += fdxb;

        ful += fduy;
		fvl += fdvy;
    }

}



//----------------------------------------------------------------------------//



void Tri_Tex16_0003()
{
    int i, j;

    //- Sort points

    float t;

    if(tri_y2 < tri_y1)
    {
        t = tri_x1; tri_x1 = tri_x2; tri_x2 = t;
        t = tri_y1; tri_y1 = tri_y2; tri_y2 = t;
        t = tri_u1; tri_u1 = tri_u2; tri_u2 = t;
        t = tri_v1; tri_v1 = tri_v2; tri_v2 = t;
    }

    if(tri_y3 < tri_y2)
    {
        t = tri_x2; tri_x2 = tri_x3; tri_x3 = t;
        t = tri_y2; tri_y2 = tri_y3; tri_y3 = t;
        t = tri_u2; tri_u2 = tri_u3; tri_u3 = t;
        t = tri_v2; tri_v2 = tri_v3; tri_v3 = t;
    }

    if(tri_y2 < tri_y1)
    {
        t = tri_x1; tri_x1 = tri_x2; tri_x2 = t;
        t = tri_y1; tri_y1 = tri_y2; tri_y2 = t;
        t = tri_u1; tri_u1 = tri_u2; tri_u2 = t;
        t = tri_v1; tri_v1 = tri_v2; tri_v2 = t;
    }

    //- Work out deltas along edges

    float dx12 = (tri_x2-tri_x1)/(tri_y2-tri_y1), dx23 = (tri_x3-tri_x2)/(tri_y3-tri_y2), dx13 = (tri_x3-tri_x1)/(tri_y3-tri_y1);
    float du12 = (tri_u2-tri_u1)/(tri_y2-tri_y1), du23 = (tri_u3-tri_u2)/(tri_y3-tri_y2), du13 = (tri_u3-tri_u1)/(tri_y3-tri_y1);
    float dv12 = (tri_v2-tri_v1)/(tri_y2-tri_y1), dv23 = (tri_v3-tri_v2)/(tri_y3-tri_y2), dv13 = (tri_v3-tri_v1)/(tri_y3-tri_y1);

    //- Round to pixel and clip to top & bottom of screen

    int y1 = (int)tri_y1; if(y1 < tri_y1) y1++;
    int y2 = (int)tri_y2; if(y2 < tri_y2) y2++;
    int y3 = (int)tri_y3; if(y3 < tri_y3) y3++;

    if(y1 < clip_y1) y1 = clip_y1;
    if(y2 < clip_y1) y2 = clip_y1;
    if(y3 < clip_y1) y3 = clip_y1;

    if(y1 > clip_y2) y1 = clip_y2;
    if(y2 > clip_y2) y2 = clip_y2;
    if(y3 > clip_y2) y3 = clip_y2;

    if(y3 <= y1) return;

    //- Calculate horizontal and vertical deltas for parameters

    float d = (tri_x3-tri_x1)*(tri_y2-tri_y1) - (tri_y3-tri_y1)*(tri_x2-tri_x1);

    float dux = ( (tri_y2-tri_y1)*(tri_u3-tri_u1) + (tri_y1-tri_y3)*(tri_u2-tri_u1) ) / d;
    float duy = ( (tri_x1-tri_x2)*(tri_u3-tri_u1) + (tri_x3-tri_x1)*(tri_u2-tri_u1) ) / d;

    float dvx = ( (tri_y2-tri_y1)*(tri_v3-tri_v1) + (tri_y1-tri_y3)*(tri_v2-tri_v1) ) / d;
    float dvy = ( (tri_x1-tri_x2)*(tri_v3-tri_v1) + (tri_x3-tri_x1)*(tri_v2-tri_v1) ) / d;

    int fdux = dux*0x10000, fdvx = dvx*0x10000;
    int fduy = duy*0x10000, fdvy = dvy*0x10000;

    //- See if we need to flip triangle horizontally

	bool side13onleft = dx13 < dx12;

    //- Draw top of triangle

    float xa, xb, dxa, dxb;

    //  Swap over if necessary

    if(side13onleft) {
      xa = tri_x1 + dx13 * (y1-tri_y1);
	  xb = tri_x1 + dx12 * (y1-tri_y1);
	  dxa = dx13;
	  dxb = dx12;
	} else {
	  xa = tri_x1 + dx12 * (y1-tri_y1);
      xb = tri_x1 + dx13 * (y1-tri_y1);
	  dxa = dx12;
	  dxb = dx13;
	}

    float ul = tri_u1 - tri_x1*dux + (y1-tri_y1)*duy;
    float vl = tri_v1 - tri_x1*dvx + (y1-tri_y1)*dvy;

    int fxa  = xa *0x10000, fxb  = xb *0x10000;
    int fdxa = dxa*0x10000, fdxb = dxb*0x10000;
    int ful  = ul *0x10000, fvl  = vl *0x10000;

    for(j=y1; j<y2; j++)
    {
        int xai = (fxa+0xFFFF) >> 16;  //  int xai = (int)xa; if(xai < xa) xai++;
        int xbi = (fxb+0xFFFF) >> 16;  //  int xbi = (int)xb; if(xbi < xb) xbi++;

		if(xai < clip_x1) xai = clip_x1;
		if(xbi < clip_x1) xbi = clip_x1;
        if(xai > clip_x2) xai = clip_x2;
		if(xbi > clip_x2) xbi = clip_x2;

        int fu = ful + xai*fdux;  //  float u = ul + xai*dux;
        int fv = fvl + xai*fdvx;  //  float v = vl + xai*dvx;

        unsigned short *buf = &screen[j][xai];

/*
        for(i=xai; i<xbi; i++)
        {
            *buf++ = texture[fv>>16][fu>>16];
            fu += fdux; fv += fdvx;
        }
*/

        _asm {

          mov edi, buf

		  mov ecx, xai
		  sub ecx, xbi
		  jge t_xloop_end

          //  Set up registers for loop

          //  ebx =  vf: ui   edx =  uf: vi
          //  esi = dvf:dui   ebp = duf:dvi

          push ebp

          mov ebx, fv
		  mov edx, fu
		  mov esi, fdvx
		  mov ebp, fdux
		  rol ebx, 16
		  rol edx, 16
		  rol esi, 16
		  rol ebp, 16
		  xchg bx, dx
		  xchg si, bp

          sub edi, ecx
          sub edi, ecx
		  sub edi, 2    //  Make up for putting inc ecx before mov [

          xor eax, eax  //  Make sure top word of eax is clear for texture lookup

		  sub dx, bp    //  Set up carry for loop
		  add edx, ebp

          //  Inner loop

		t_xloop:

          mov al, bl
          adc ebx, esi
		  mov ah, dl
		  adc edx, ebp

          mov ax, texture[eax*2]
		  inc ecx
		  mov [edi+ecx*2], ax

		  jnz t_xloop

          //  Inner loop done

          pop ebp

		t_xloop_end:

		}

        fxa += fdxa; fxb += fdxb;

        ful += fduy;
		fvl += fdvy;
    }

    //- Draw bottom of triangle

    if(side13onleft) {
      xb = tri_x2 + dx23 * (y2-tri_y2);
	  dxb = dx23;
      fxb  = xb *0x10000;
      fdxb = dxb*0x10000;
	} else {
      xa = tri_x2 + dx23 * (y2-tri_y2);
	  dxa = dx23;
      fxa  = xa *0x10000;
      fdxa = dxa*0x10000;
	}

    for(j=y2; j<y3; j++)
    {
        int xai = (fxa+0xFFFF) >> 16;  //  int xai = (int)xa; if(xai < xa) xai++;
        int xbi = (fxb+0xFFFF) >> 16;  //  int xbi = (int)xb; if(xbi < xb) xbi++;

		if(xai < clip_x1) xai = clip_x1;
		if(xbi < clip_x1) xbi = clip_x1;
		if(xai > clip_x2) xai = clip_x2;
		if(xbi > clip_x2) xbi = clip_x2;

        int fu = ful + xai*fdux;  //  float u = ul + xai*dux;
        int fv = fvl + xai*fdvx;  //  float v = vl + xai*dvx;

        unsigned short *buf = &screen[j][xai];
/*
        for(i=xai; i<xbi; i++)
        {
            *buf++ = texture[fv>>16][fu>>16];
            fu += fdux; fv += fdvx;
        }
*/
        _asm {

          mov edi, buf

		  mov ecx, xai
		  sub ecx, xbi
		  jge b_xloop_end

          //  Set up registers for loop

          //  ebx =  vf: xx   edx =  uf: vi
          //  esi = dvf:dui   ebp = duf:dvi

          push ebp

          mov ebx, fv
		  mov edx, fu
		  mov esi, fdvx
		  mov ebp, fdux
		  rol ebx, 16
		  rol edx, 16
		  rol esi, 16
		  rol ebp, 16
		  xchg bx, dx
		  xchg si, bp

          sub edi, ecx
          sub edi, ecx

          xor eax, eax  //  Make sure top word of eax is clear for texture lookup

		  sub dx, bp    //  Set up carry for loop
		  add edx, ebp

          //  Inner loop

		b_xloop:

          mov al, bl
          adc ebx, esi
		  mov ah, dl
		  adc edx, ebp

          mov ax, texture[eax*2]
		  inc ecx
		  mov [edi+ecx*2], ax

		  jnz b_xloop

          //  Inner loop done

          pop ebp

		b_xloop_end:

		}

        fxa += fdxa; fxb += fdxb;

        ful += fduy;
		fvl += fdvy;
    }

}



//----------------------------------------------------------------------------//



void Tri_Tex16_256()
{
    int i, j;

    //- Sort points

    float t;

    if(tri_y2 < tri_y1)
    {
        t = tri_x1; tri_x1 = tri_x2; tri_x2 = t;
        t = tri_y1; tri_y1 = tri_y2; tri_y2 = t;
        t = tri_u1; tri_u1 = tri_u2; tri_u2 = t;
        t = tri_v1; tri_v1 = tri_v2; tri_v2 = t;
    }

    if(tri_y3 < tri_y2)
    {
        t = tri_x2; tri_x2 = tri_x3; tri_x3 = t;
        t = tri_y2; tri_y2 = tri_y3; tri_y3 = t;
        t = tri_u2; tri_u2 = tri_u3; tri_u3 = t;
        t = tri_v2; tri_v2 = tri_v3; tri_v3 = t;
    }

    if(tri_y2 < tri_y1)
    {
        t = tri_x1; tri_x1 = tri_x2; tri_x2 = t;
        t = tri_y1; tri_y1 = tri_y2; tri_y2 = t;
        t = tri_u1; tri_u1 = tri_u2; tri_u2 = t;
        t = tri_v1; tri_v1 = tri_v2; tri_v2 = t;
    }

    //- Work out deltas along edges

    float dx12 = (tri_x2-tri_x1)/(tri_y2-tri_y1), dx23 = (tri_x3-tri_x2)/(tri_y3-tri_y2), dx13 = (tri_x3-tri_x1)/(tri_y3-tri_y1);
    float du12 = (tri_u2-tri_u1)/(tri_y2-tri_y1), du23 = (tri_u3-tri_u2)/(tri_y3-tri_y2), du13 = (tri_u3-tri_u1)/(tri_y3-tri_y1);
    float dv12 = (tri_v2-tri_v1)/(tri_y2-tri_y1), dv23 = (tri_v3-tri_v2)/(tri_y3-tri_y2), dv13 = (tri_v3-tri_v1)/(tri_y3-tri_y1);

    //- Round to pixel and clip to top & bottom of screen

    int y1 = (int)tri_y1; if(y1 < tri_y1) y1++;
    int y2 = (int)tri_y2; if(y2 < tri_y2) y2++;
    int y3 = (int)tri_y3; if(y3 < tri_y3) y3++;

    if(y1 < clip_y1) y1 = clip_y1;
    if(y2 < clip_y1) y2 = clip_y1;
    if(y3 < clip_y1) y3 = clip_y1;

    if(y1 > clip_y2) y1 = clip_y2;
    if(y2 > clip_y2) y2 = clip_y2;
    if(y3 > clip_y2) y3 = clip_y2;

    if(y3 <= y1) return;

    //- Calculate horizontal and vertical deltas for parameters

    float d = (tri_x3-tri_x1)*(tri_y2-tri_y1) - (tri_y3-tri_y1)*(tri_x2-tri_x1);

    float dux = ( (tri_y2-tri_y1)*(tri_u3-tri_u1) + (tri_y1-tri_y3)*(tri_u2-tri_u1) ) / d;
    float duy = ( (tri_x1-tri_x2)*(tri_u3-tri_u1) + (tri_x3-tri_x1)*(tri_u2-tri_u1) ) / d;

    float dvx = ( (tri_y2-tri_y1)*(tri_v3-tri_v1) + (tri_y1-tri_y3)*(tri_v2-tri_v1) ) / d;
    float dvy = ( (tri_x1-tri_x2)*(tri_v3-tri_v1) + (tri_x3-tri_x1)*(tri_v2-tri_v1) ) / d;

    int fdux = dux*0x10000, fdvx = dvx*0x10000;
    int fduy = duy*0x10000, fdvy = dvy*0x10000;

    //- See if we need to flip triangle horizontally

	bool side13onleft = dx13 < dx12;

    //- Draw top of triangle

    float xa, xb, dxa, dxb;

    //  Swap over if necessary

    if(side13onleft) {
        xa = tri_x1 + dx13 * (y1-tri_y1);
        xb = tri_x1 + dx12 * (y1-tri_y1);
        dxa = dx13;
        dxb = dx12;
	} else {
	    xa = tri_x1 + dx12 * (y1-tri_y1);
        xb = tri_x1 + dx13 * (y1-tri_y1);
	    dxa = dx12;
	    dxb = dx13;
	}

    float ul = tri_u1 - tri_x1*dux + (y1-tri_y1)*duy;
    float vl = tri_v1 - tri_x1*dvx + (y1-tri_y1)*dvy;

    int fxa  = xa *0x10000, fxb  = xb *0x10000;
    int fdxa = dxa*0x10000, fdxb = dxb*0x10000;
    int ful  = ul *0x10000, fvl  = vl *0x10000;

    for(j=y1; j<y2; j++)
    {
        int xai = (fxa+0xFFFF) >> 16;  //  int xai = (int)xa; if(xai < xa) xai++;
        int xbi = (fxb+0xFFFF) >> 16;  //  int xbi = (int)xb; if(xbi < xb) xbi++;

		if(xai < clip_x1) xai = clip_x1;
		if(xbi < clip_x1) xbi = clip_x1;
        if(xai > clip_x2) xai = clip_x2;
		if(xbi > clip_x2) xbi = clip_x2;

        int fu = ful + xai*fdux;  //  float u = ul + xai*dux;
        int fv = fvl + xai*fdvx;  //  float v = vl + xai*dvx;

        unsigned short *buf = &screen[j][xai];
/*
        for(i=xai; i<xbi; i++)
        {
            *buf++ = texture[fv>>16][fu>>16];
            fu += fdux; fv += fdvx;
        }
*/
        _asm {

          //  Set up loop registers

		  mov edi, xai
		  sub edi, xbi
		  jge t_xloop_end

          //  Set up delta registers

          //  ebx = [  Ui  Uf  Uf  Vi ]
		  //  ecx = [ dUi dUf   0 dVi ]
		  //  edx = [  Vf  Vf   0   0 ]
		  //  esi = [ dVf dVf   0   0 ]

          mov esi, fdvx
		  mov edx, fv
		  shl esi, 16
		  shl edx, 16

		  mov ebx, fu
		  mov eax, fv
		  shl ebx, 8
		  shr eax, 16
		  and eax, 0x000000FF
		  add ebx, eax

		  mov ecx, fdux
		  mov eax, fdvx
		  shl ecx, 8
		  shr eax, 16
		  and eax, 0x000000FF
		  add ecx, eax

          //  Set up loop registers

          mov eax, edi
		  mov edi, buf
		  sub edi, eax
		  sub edi, eax

          push ebp
		  mov ebp, eax

          //  Inner loop

          mov eax, ebx
          rol eax, 8

        t_xloop:

          and eax, 0x0000FFFF
          add edx, esi

          adc ebx, ecx
          mov ax, texture[eax*2]

          mov [edi+ebp*2], ax
          mov eax, ebx

          rol eax, 8

          inc ebp
          jnz t_xloop

          //  Inner loop done

          pop ebp

		t_xloop_end:

		}

        fxa += fdxa; fxb += fdxb;

        ful += fduy;
		fvl += fdvy;

    }

    //- Draw bottom of triangle

    if(side13onleft) {
        xb = tri_x2 + dx23 * (y2-tri_y2);
        dxb = dx23;
        fxb  = xb *0x10000;
        fdxb = dxb*0x10000;
	} else {
        xa = tri_x2 + dx23 * (y2-tri_y2);
        dxa = dx23;
        fxa  = xa *0x10000;
        fdxa = dxa*0x10000;
	}

    for(j=y2; j<y3; j++)
    {
        int xai = (fxa+0xFFFF) >> 16;  //  int xai = (int)xa; if(xai < xa) xai++;
        int xbi = (fxb+0xFFFF) >> 16;  //  int xbi = (int)xb; if(xbi < xb) xbi++;

		if(xai < clip_x1) xai = clip_x1;
		if(xbi < clip_x1) xbi = clip_x1;
		if(xai > clip_x2) xai = clip_x2;
		if(xbi > clip_x2) xbi = clip_x2;

        int fu = ful + xai*fdux;  //  float u = ul + xai*dux;
        int fv = fvl + xai*fdvx;  //  float v = vl + xai*dvx;

        unsigned short *buf = &screen[j][xai];
/*
        for(i=xai; i<xbi; i++)
        {
            *buf++ = texture[fv>>16][fu>>16];
            fu += fdux; fv += fdvx;
        }
*/
        _asm {

          //  Set up loop registers

		  mov edi, xai
		  sub edi, xbi
		  jge b_xloop_end

          //  Set up delta registers

          //  ebx = [  Ui  Uf  Uf  Vi ]
		  //  ecx = [ dUi dUf   0 dVi ]
		  //  edx = [  Vf  Vf   0   0 ]
		  //  esi = [ dVf dVf   0   0 ]

          mov esi, fdvx
		  mov edx, fv
		  shl esi, 16
		  shl edx, 16

		  mov ebx, fu
		  mov eax, fv
		  shl ebx, 8
		  shr eax, 16
		  and eax, 0x000000FF
		  add ebx, eax

		  mov ecx, fdux
		  mov eax, fdvx
		  shl ecx, 8
		  shr eax, 16
		  and eax, 0x000000FF
		  add ecx, eax

          //  Set up loop registers

          mov eax, edi
		  mov edi, buf
		  sub edi, eax
		  sub edi, eax

          push ebp
		  mov ebp, eax

          //  Inner loop

          mov eax, ebx
          rol eax, 8

        b_xloop:

          and eax, 0x0000FFFF
          add edx, esi

          adc ebx, ecx
          mov ax, texture[eax*2]

          mov [edi+ebp*2], ax
          mov eax, ebx

          rol eax, 8

          inc ebp
          jnz b_xloop

          //  Inner loop done

          pop ebp

		b_xloop_end:

		}

        fxa += fdxa; fxb += fdxb;

        ful += fduy;
		fvl += fdvy;
    }

}



//----------------------------------------------------------------------------//

/*


  7 6 5 4 3 2 1 0   7 6 5 4 3 2 1 0
  r r r r r g g g   g g g b b b b b


  8-bit lighting value ?








*/



void Tri_SpecularTex16()
{
  int i, j;

  //- Sort points

  float t;

  if(tri_y2 < tri_y1)
  {
    t = tri_x1; tri_x1 = tri_x2; tri_x2 = t;
    t = tri_y1; tri_y1 = tri_y2; tri_y2 = t;
    t = tri_u1; tri_u1 = tri_u2; tri_u2 = t;
    t = tri_v1; tri_v1 = tri_v2; tri_v2 = t;
    t = tri_s1; tri_s1 = tri_s2; tri_s2 = t;
  }

  if(tri_y3 < tri_y2)
  {
    t = tri_x2; tri_x2 = tri_x3; tri_x3 = t;
    t = tri_y2; tri_y2 = tri_y3; tri_y3 = t;
    t = tri_u2; tri_u2 = tri_u3; tri_u3 = t;
    t = tri_v2; tri_v2 = tri_v3; tri_v3 = t;
    t = tri_s2; tri_s2 = tri_s3; tri_s3 = t;
  }

  if(tri_y2 < tri_y1)
  {
    t = tri_x1; tri_x1 = tri_x2; tri_x2 = t;
    t = tri_y1; tri_y1 = tri_y2; tri_y2 = t;
    t = tri_u1; tri_u1 = tri_u2; tri_u2 = t;
    t = tri_v1; tri_v1 = tri_v2; tri_v2 = t;
    t = tri_s1; tri_s1 = tri_s2; tri_s2 = t;
  }

  //- Work out deltas along edges

  float dx12 = (tri_x2-tri_x1)/(tri_y2-tri_y1), dx23 = (tri_x3-tri_x2)/(tri_y3-tri_y2), dx13 = (tri_x3-tri_x1)/(tri_y3-tri_y1);
  float du12 = (tri_u2-tri_u1)/(tri_y2-tri_y1), du23 = (tri_u3-tri_u2)/(tri_y3-tri_y2), du13 = (tri_u3-tri_u1)/(tri_y3-tri_y1);
  float dv12 = (tri_v2-tri_v1)/(tri_y2-tri_y1), dv23 = (tri_v3-tri_v2)/(tri_y3-tri_y2), dv13 = (tri_v3-tri_v1)/(tri_y3-tri_y1);
  float ds12 = (tri_s2-tri_s1)/(tri_y2-tri_y1), ds23 = (tri_s3-tri_s2)/(tri_y3-tri_y2), ds13 = (tri_s3-tri_s1)/(tri_y3-tri_y1);

  //- Round to pixel and clip to top & bottom of screen

  int y1 = (int)tri_y1; if(y1 < tri_y1) y1++;
  int y2 = (int)tri_y2; if(y2 < tri_y2) y2++;
  int y3 = (int)tri_y3; if(y3 < tri_y3) y3++;

  if(y1 < clip_y1) y1 = clip_y1;
  if(y2 < clip_y1) y2 = clip_y1;
  if(y3 < clip_y1) y3 = clip_y1;

  if(y1 > clip_y2) y1 = clip_y2;
  if(y2 > clip_y2) y2 = clip_y2;
  if(y3 > clip_y2) y3 = clip_y2;

  if(y3 <= y1) return;

  //- Calculate horizontal and vertical deltas for parameters

  float d = (tri_x3-tri_x1)*(tri_y2-tri_y1) - (tri_y3-tri_y1)*(tri_x2-tri_x1);

  float dux = ( (tri_y2-tri_y1)*(tri_u3-tri_u1) + (tri_y1-tri_y3)*(tri_u2-tri_u1) ) / d;
  float duy = ( (tri_x1-tri_x2)*(tri_u3-tri_u1) + (tri_x3-tri_x1)*(tri_u2-tri_u1) ) / d;

  float dvx = ( (tri_y2-tri_y1)*(tri_v3-tri_v1) + (tri_y1-tri_y3)*(tri_v2-tri_v1) ) / d;
  float dvy = ( (tri_x1-tri_x2)*(tri_v3-tri_v1) + (tri_x3-tri_x1)*(tri_v2-tri_v1) ) / d;

  float dsx = ( (tri_y2-tri_y1)*(tri_s3-tri_s1) + (tri_y1-tri_y3)*(tri_s2-tri_s1) ) / d;
  float dsy = ( (tri_x1-tri_x2)*(tri_s3-tri_s1) + (tri_x3-tri_x1)*(tri_s2-tri_s1) ) / d;

  int fdux = dux*0x10000, fduy = duy*0x10000;
  int fdvx = dvy*0x10000, fdvy = dvy*0x10000;
  int fdsx = dsx*0x10000, fdsy = dsy*0x10000;

  //- See if we need to flip triangle horizontally

	bool side13onleft = dx13 < dx12;

  //- Draw top of triangle

  float xa, xb, dxa, dxb;

  //  Swap over if necessary

  if(side13onleft) {
    xa = tri_x1 + dx13 * (y1-tri_y1);
	  xb = tri_x1 + dx12 * (y1-tri_y1);
	  dxa = dx13;
	  dxb = dx12;
	}
  else
  {
	  xa = tri_x1 + dx12 * (y1-tri_y1);
    xb = tri_x1 + dx13 * (y1-tri_y1);
	  dxa = dx12;
	  dxb = dx13;
	}

  float ul = tri_u1 - tri_x1*dux + (y1-tri_y1)*duy;
  float vl = tri_v1 - tri_x1*dvx + (y1-tri_y1)*dvy;
  float sl = tri_s1 - tri_x1*dsx + (y1-tri_y1)*dsy;

  int fxa  = xa *0x10000, fxb  = xb *0x10000;
  int fdxa = dxa*0x10000, fdxb = dxb*0x10000;
  int ful  = ul *0x10000;
	int fvl  = vl *0x10000;
	int fsl  = sl *0x10000;

  for(j=y1; j<y2; j++)
  {
    int xai = (fxa+0xFFFF) >> 16;  //  int xai = (int)xa; if(xai < xa) xai++;
    int xbi = (fxb+0xFFFF) >> 16;  //  int xbi = (int)xb; if(xbi < xb) xbi++;

		if(xai < clip_x1) xai = clip_x1;
		if(xbi < clip_x1) xbi = clip_x1;
    if(xai > clip_x2) xai = clip_x2;
		if(xbi > clip_x2) xbi = clip_x2;

    int fu = ful + xai*fdux;  //  float u = ul + xai*dux;
    int fv = fvl + xai*fdvx;  //  float v = vl + xai*dvx;
    int fs = fsl + xai*fdsx;

    unsigned short *buf = &screen[j][xai];

    for(i=xai; i<xbi; i++)
    {
      *buf++ = texture[fv>>16][fu>>16];
      fu += fdux; fv += fdvx;
    }

    fxa += fdxa; fxb += fdxb;

    ful += fduy;
		fvl += fdvy;
		fsl += fdsy;
  }

  //- Draw bottom of triangle

  if(side13onleft)
  {
    xb = tri_x2 + dx23 * (y2-tri_y2);
	  dxb = dx23;
    fxb  = xb *0x10000;
    fdxb = dxb*0x10000;
	}
  else
  {
    xa = tri_x2 + dx23 * (y2-tri_y2);
	  dxa = dx23;
    fxa  = xa *0x10000;
    fdxa = dxa*0x10000;
	}

  for(j=y2; j<y3; j++)
  {
    int xai = (fxa+0xFFFF) >> 16;  //  int xai = (int)xa; if(xai < xa) xai++;
    int xbi = (fxb+0xFFFF) >> 16;  //  int xbi = (int)xb; if(xbi < xb) xbi++;

		if(xai < clip_x1) xai = clip_x1;
		if(xbi < clip_x1) xbi = clip_x1;
		if(xai > clip_x2) xai = clip_x2;
		if(xbi > clip_x2) xbi = clip_x2;

    int fu = ful + xai*fdux;  //  float u = ul + xai*dux;
    int fv = fvl + xai*fdvx;  //  float v = vl + xai*dvx;

    unsigned short *buf = &screen[j][xai];

    for(i=xai; i<xbi; i++)
    {
      *buf++ = texture[fv>>16][fu>>16];
      fu += fdux; fv += fdvx;
    }

    fxa += fdxa; fxb += fdxb;

    ful += fduy;
		fvl += fdvy;
  }

}



//----------------------------------------------------------------------------//



void Tri_PerspTex16()
{
  int i, j;

  //- Sort points

  float t;

  if(tri_y2 < tri_y1)
  {
    t = tri_x1; tri_x1 = tri_x2; tri_x2 = t;
    t = tri_y1; tri_y1 = tri_y2; tri_y2 = t;
    t = tri_z1; tri_z1 = tri_z2; tri_z2 = t;
    t = tri_u1; tri_u1 = tri_u2; tri_u2 = t;
    t = tri_v1; tri_v1 = tri_v2; tri_v2 = t;
  }

  if(tri_y3 < tri_y2)
  {
    t = tri_x2; tri_x2 = tri_x3; tri_x3 = t;
    t = tri_y2; tri_y2 = tri_y3; tri_y3 = t;
    t = tri_z2; tri_z2 = tri_z3; tri_z3 = t;
    t = tri_u2; tri_u2 = tri_u3; tri_u3 = t;
    t = tri_v2; tri_v2 = tri_v3; tri_v3 = t;
  }

  if(tri_y2 < tri_y1)
  {
    t = tri_x1; tri_x1 = tri_x2; tri_x2 = t;
    t = tri_y1; tri_y1 = tri_y2; tri_y2 = t;
    t = tri_z1; tri_z1 = tri_z2; tri_z2 = t;
    t = tri_u1; tri_u1 = tri_u2; tri_u2 = t;
    t = tri_v1; tri_v1 = tri_v2; tri_v2 = t;
  }

  //- Divide by z to do perspective correction

  tri_u1 /= tri_z1; tri_u2 /= tri_z2; tri_u3 /= tri_z3;
  tri_v1 /= tri_z1; tri_v2 /= tri_z2; tri_v3 /= tri_z3;

  tri_z1 = 1.0/tri_z1; tri_z2 = 1.0/tri_z2; tri_z3 = 1.0/tri_z3;

  //- Work out deltas along edges

  float dx12 = (tri_x2-tri_x1)/(tri_y2-tri_y1), dx23 = (tri_x3-tri_x2)/(tri_y3-tri_y2), dx13 = (tri_x3-tri_x1)/(tri_y3-tri_y1);
  float dz12 = (tri_z2-tri_z1)/(tri_y2-tri_y1), dz23 = (tri_z3-tri_z2)/(tri_y3-tri_y2), dz13 = (tri_z3-tri_z1)/(tri_y3-tri_y1);
  float du12 = (tri_u2-tri_u1)/(tri_y2-tri_y1), du23 = (tri_u3-tri_u2)/(tri_y3-tri_y2), du13 = (tri_u3-tri_u1)/(tri_y3-tri_y1);
  float dv12 = (tri_v2-tri_v1)/(tri_y2-tri_y1), dv23 = (tri_v3-tri_v2)/(tri_y3-tri_y2), dv13 = (tri_v3-tri_v1)/(tri_y3-tri_y1);

  //- Round to pixel and clip to top & bottom of screen

  int y1 = (int)tri_y1; if(y1 < tri_y1) y1++;
  int y2 = (int)tri_y2; if(y2 < tri_y2) y2++;
  int y3 = (int)tri_y3; if(y3 < tri_y3) y3++;

  if(y1 < clip_y1) y1 = clip_y1;
  if(y2 < clip_y1) y2 = clip_y1;
  if(y3 < clip_y1) y3 = clip_y1;

  if(y1 > clip_y2) y1 = clip_y2;
  if(y2 > clip_y2) y2 = clip_y2;
  if(y3 > clip_y2) y3 = clip_y2;

  if(y3 <= y1) return;

  //- Calculate horizontal and vertical deltas for parameters

  float d = (tri_x3-tri_x1)*(tri_y2-tri_y1) - (tri_y3-tri_y1)*(tri_x2-tri_x1);

  float dzx = ( (tri_y2-tri_y1)*(tri_z3-tri_z1) + (tri_y1-tri_y3)*(tri_z2-tri_z1) ) / d;
  float dzy = ( (tri_x1-tri_x2)*(tri_z3-tri_z1) + (tri_x3-tri_x1)*(tri_z2-tri_z1) ) / d;

  float dux = ( (tri_y2-tri_y1)*(tri_u3-tri_u1) + (tri_y1-tri_y3)*(tri_u2-tri_u1) ) / d;
  float duy = ( (tri_x1-tri_x2)*(tri_u3-tri_u1) + (tri_x3-tri_x1)*(tri_u2-tri_u1) ) / d;

  float dvx = ( (tri_y2-tri_y1)*(tri_v3-tri_v1) + (tri_y1-tri_y3)*(tri_v2-tri_v1) ) / d;
  float dvy = ( (tri_x1-tri_x2)*(tri_v3-tri_v1) + (tri_x3-tri_x1)*(tri_v2-tri_v1) ) / d;

  //- See if we need to flip triangle horizontally

  bool side13onleft = dx13 < dx12;

  //- Draw top of triangle

  float xa, xb, dxa, dxb;

  if(side13onleft)
  {
    xa = tri_x1 + dx13 * (y1-tri_y1);
    xb = tri_x1 + dx12 * (y1-tri_y1);
    dxa = dx13;
    dxb = dx12;
  }
  else
  {
    xa = tri_x1 + dx12 * (y1-tri_y1);
    xb = tri_x1 + dx13 * (y1-tri_y1);
	  dxa = dx12;
	  dxb = dx13;
  }

  float zl = tri_z1 - tri_x1*dzx + (y1-tri_y1)*dzy;
  float ul = tri_u1 - tri_x1*dux + (y1-tri_y1)*duy;
  float vl = tri_v1 - tri_x1*dvx + (y1-tri_y1)*dvy;

  for(j=y1; j<y2; j++)
  {
    int xai = (int)xa; if(xai < xa) xai++;
    int xbi = (int)xb; if(xbi < xb) xbi++;

  	if(xai < clip_x1) xai = clip_x1;
	  if(xbi < clip_x1) xbi = clip_x1;
    if(xai > clip_x2) xai = clip_x2;
	  if(xbi > clip_x2) xbi = clip_x2;

    float z = zl + xai*dzx;
    float u = ul + xai*dux;
    float v = vl + xai*dvx;

    for(i=xai; i<xbi; i++)
    {
      screen[j][i] = texture[(int)(v/z)][(int)(u/z)];
      z += dzx;
		  u += dux;
		  v += dvx;
    }

    xa += dxa; xb += dxb;

    zl += dzy;
    ul += duy;
    vl += dvy;
  }

  //- Draw bottom of triangle

  if(side13onleft)
  {
    xb = tri_x2 + dx23 * (y2-tri_y2);
	  dxb = dx23;
  }
  else
  {
    xa = tri_x2 + dx23 * (y2-tri_y2);
    dxa = dx23;
  }

  for(j=y2; j<y3; j++)
  {
    int xai = (int)xa; if(xai < xa) xai++;
    int xbi = (int)xb; if(xbi < xb) xbi++;

	  if(xai < clip_x1) xai = clip_x1;
	  if(xbi < clip_x1) xbi = clip_x1;
	  if(xai > clip_x2) xai = clip_x2;
	  if(xbi > clip_x2) xbi = clip_x2;

    float z = zl + xai*dzx;
    float u = ul + xai*dux;
    float v = vl + xai*dvx;

    for(i=xai; i<xbi; i++)
    {
      screen[j][i] = texture[(int)(v/z)][(int)(u/z)];
      z += dzx;
		  u += dux;
		  v += dvx;
    }

    xa += dxa; xb += dxb;

    zl += dzy;
    ul += duy;
    vl += dvy;
  }

}



//----------------------------------------------------------------------------//

bool init = false;

void Init()
{
  if(init) return;

  int i, j;

  for(j=0; j<256; j++) for(i=0; i<256; i++) {
    texture[j][i] = ((i>>3)*951)+((j>>3)*91);
  }
}



//----------------------------------------------------------------------------//

BOOL Idle(unsigned long time)
{
  Init();

  int i, j;

  //- Clear buffer

//  ZeroMemory(screen, sizeof(screen));

  //- Draw triangle
/*
  float a = time*3.14159/15000.0;
  float d = 3.14159*2.0/3.0;

  tri_x1 = 320.33 + 200.33 * sin(a+d*0); tri_y1 = 240.33 + 200.33 * cos(a+d*0);
  tri_x2 = 320.33 + 200.33 * sin(a+d*1); tri_y2 = 240.33 + 200.33 * cos(a+d*1);
  tri_x3 = 320.33 + 200.33 * sin(a+d*2); tri_y3 = 240.33 + 200.33 * cos(a+d*2);

  tri_u1 = 128 + 32 * sin(d*0); tri_v1 = 128 + 32 * cos(d*0);
  tri_u2 = 128 + 32 * sin(d*1); tri_v2 = 128 + 32 * cos(d*1);
  tri_u3 = 128 + 32 * sin(d*2); tri_v3 = 128 + 32 * cos(d*2);

  Tri_Tex16();
*/
  //- Draw tiled triangles

  float a = time*3.14159/16000.0;
  float d = 3.14159*2.0/3.0;
  float s = sqrt(3);

  //  Create array of points

  float tx[16][16], ty[16][16];

  for(j=-3; j<=3; j++) for(i=-3; i<=3; i++) {

    float y = j*s;
	float x = i*s - (j&1)*0.5*s;

	tx[3+j][3+i] = (x*cos(a) - y*sin(a))/s;
	ty[3+j][3+i] = (x*sin(a) + y*cos(a))/s;

  }

  //  Draw array of triangles

  float aa;

  float scale = 200;

  float ttx, tty;
  float ccx = SCREEN_W*0.5 + 0.33, ccy = SCREEN_H*0.5 + 0.33;

  for(j=0; j<6; j++) for(i=0; i<6; i++) {

//    if(j & 1) {

      tri_x1 = ccx + scale * tx[j+0][i+0]; tri_y1 = ccy + scale * ty[j+0][i+0];
      tri_x2 = ccx + scale * tx[j+0][i+1]; tri_y2 = ccy + scale * ty[j+0][i+1];
      tri_x3 = ccx + scale * tx[j+1][i+1]; tri_y3 = ccy + scale * ty[j+1][i+1];
      aa = 0.2781 - 0.7218*i + 1.8724*j + 0.1919*(i^j);
	  ttx = 128 + 90*sin(1.2874-i*0.2847+j*0.1912+(i^j)*0.7821);
	  tty = 128 + 90*sin(0.1551+i*1.1511-j*1.5215-(i^j)*1.5128);
      tri_u1 = ttx + 32 * sin(aa+d*0); tri_v1 = tty + 32 * cos(aa+d*0);
      tri_u2 = ttx + 32 * sin(aa+d*1); tri_v2 = tty + 32 * cos(aa+d*1);
      tri_u3 = ttx + 32 * sin(aa+d*2); tri_v3 = tty + 32 * cos(aa+d*2);

      tri_z1 = 10; tri_z2 =  6; tri_z3 =  2;
      Tri_Tex16();

      tri_x1 = ccx + scale * tx[j+1][i+1]; tri_y1 = ccy + scale * ty[j+1][i+1];
      tri_x2 = ccx + scale * tx[j+1][i+0]; tri_y2 = ccy + scale * ty[j+1][i+0];
      tri_x3 = ccx + scale * tx[j+0][i+0]; tri_y3 = ccy + scale * ty[j+0][i+0];
      aa = 1.2614 + 1.3281*i + 0.6117*j - 0.9162*(i^j);
	  ttx = 128 + 90*sin(0.3265+i*0.6611+j*1.3262-(i^j)*1.6121);
	  tty = 128 + 90*sin(0.1551-i*1.7188-j*0.5215+(i^j)*0.1171);
      tri_u1 = ttx + 32 * sin(aa+d*0); tri_v1 = tty + 32 * cos(aa+d*0);
      tri_u2 = ttx + 32 * sin(aa+d*1); tri_v2 = tty + 32 * cos(aa+d*1);
      tri_u3 = ttx + 32 * sin(aa+d*2); tri_v3 = tty + 32 * cos(aa+d*2);

      tri_z1 = 10; tri_z2 =  6; tri_z3 =  2;
      Tri_Tex16();
/*
	} else {

      tri_x1 = 320.33 + scale * tx[j+0][i+0]; tri_y1 = 240.33 + scale * ty[j+0][i+0];
      tri_x2 = 320.33 + scale * tx[j+1][i+0]; tri_y2 = 240.33 + scale * ty[j+1][i+0];
      tri_x3 = 320.33 + scale * tx[j+1][i+1]; tri_y3 = 240.33 + scale * ty[j+1][i+1];
      aa = 0.2781 - 0.7218*i + 1.8724*j + 0.1919*(i^j);
	  ttx = 128 + 90*sin(1.2874-i*0.2847+j*0.1912+(i^j)*0.7821);
	  tty = 128 + 90*sin(0.1551+i*1.1511-j*1.5215-(i^j)*1.5128);
      tri_u1 = ttx + 32 * sin(aa+d*0); tri_v1 = tty + 32 * cos(aa+d*0);
      tri_u2 = ttx + 32 * sin(aa+d*1); tri_v2 = tty + 32 * cos(aa+d*1);
      tri_u3 = ttx + 32 * sin(aa+d*2); tri_v3 = tty + 32 * cos(aa+d*2);
      Tri_Tex16();

      tri_x1 = 320.33 + scale * tx[j+1][i+1]; tri_y1 = 240.33 + scale * ty[j+1][i+1];
      tri_x2 = 320.33 + scale * tx[j+0][i+1]; tri_y2 = 240.33 + scale * ty[j+0][i+1];
      tri_x3 = 320.33 + scale * tx[j+0][i+0]; tri_y3 = 240.33 + scale * ty[j+0][i+0];
      aa = 1.2614 + 1.3281*i + 0.6117*j - 0.9162*(i^j);
	  ttx = 128 + 90*sin(0.3265+i*0.6611+j*1.3262-(i^j)*1.6121);
	  tty = 128 + 90*sin(0.1551-i*1.7188-j*0.5215+(i^j)*0.1171);
      tri_u1 = ttx + 32 * sin(aa+d*0); tri_v1 = tty + 32 * cos(aa+d*0);
      tri_u2 = ttx + 32 * sin(aa+d*1); tri_v2 = tty + 32 * cos(aa+d*1);
      tri_u3 = ttx + 32 * sin(aa+d*2); tri_v3 = tty + 32 * cos(aa+d*2);
      Tri_Tex16();

	}
*/
  }

  //- Draw stuff

/*
  for(j=0; j<256; j++) for(i=0; i<256; i++) {
    screen[j][i] = texture[j][i];
  }
*/

  for(j=0; j<8; j++) for(i=0; i<8; i++) {
    screen[j][i] = GetTickCount()+i*99+j*17;
  }

  //- Display

  HDC dc = GetDC(hMainWindow);
  Display(dc, (unsigned short *)screen, 0, 0, SCREEN_W, SCREEN_H);
  ReleaseDC(hMainWindow, dc);

  return TRUE;
}



//----------------------------------------------------------------------------//


int APIENTRY WinMain(HINSTANCE hInstance,
                     HINSTANCE hPrevInstance,
                     LPSTR     lpCmdLine,
                     int       nCmdShow)
{
 	// TODO: Place code here.
	MSG msg;
	HACCEL hAccelTable;

	// Initialize global strings
	LoadString(hInstance, IDS_APP_TITLE, szTitle, MAX_LOADSTRING);
	LoadString(hInstance, IDC_TRIANGLE, szWindowClass, MAX_LOADSTRING);
	MyRegisterClass(hInstance);

	// Perform application initialization:
	if (!InitInstance (hInstance, nCmdShow)) 
	{
		return FALSE;
	}

	hAccelTable = LoadAccelerators(hInstance, (LPCTSTR)IDC_TRIANGLE);

	// Main message loop:
    MessageLoop();

	return msg.wParam;
}



//
//  FUNCTION: MyRegisterClass()
//
//  PURPOSE: Registers the window class.
//
//  COMMENTS:
//
//    This function and its usage is only necessary if you want this code
//    to be compatible with Win32 systems prior to the 'RegisterClassEx'
//    function that was added to Windows 95. It is important to call this function
//    so that the application will get 'well formed' small icons associated
//    with it.
//
ATOM MyRegisterClass(HINSTANCE hInstance)
{
	WNDCLASSEX wcex;

	wcex.cbSize = sizeof(WNDCLASSEX); 

	wcex.style			= CS_HREDRAW | CS_VREDRAW;
	wcex.lpfnWndProc	= (WNDPROC)WndProc;
	wcex.cbClsExtra		= 0;
	wcex.cbWndExtra		= 0;
	wcex.hInstance		= hInstance;
	wcex.hIcon			= LoadIcon(hInstance, (LPCTSTR)IDI_TRIANGLE);
	wcex.hCursor		= LoadCursor(NULL, IDC_ARROW);
	wcex.hbrBackground	= (HBRUSH)(COLOR_WINDOW+1);
	wcex.lpszMenuName	= (LPCSTR)IDC_TRIANGLE;
	wcex.lpszClassName	= szWindowClass;
	wcex.hIconSm		= LoadIcon(wcex.hInstance, (LPCTSTR)IDI_SMALL);

	return RegisterClassEx(&wcex);
}

//
//   FUNCTION: InitInstance(HANDLE, int)
//
//   PURPOSE: Saves instance handle and creates main window
//
//   COMMENTS:
//
//        In this function, we save the instance handle in a global variable and
//        create and display the main program window.
//
BOOL InitInstance(HINSTANCE hInstance, int nCmdShow)
{
   HWND hWnd;

   hInst = hInstance; // Store instance handle in our global variable

   hWnd = CreateWindow(szWindowClass, szTitle, WS_OVERLAPPEDWINDOW,
      CW_USEDEFAULT, 0, CW_USEDEFAULT, 0, NULL, NULL, hInstance, NULL);

   if (!hWnd)
   {
      return FALSE;
   }

   hMainWindow = hWnd;

   ShowWindow(hWnd, nCmdShow);
   UpdateWindow(hWnd);

   return TRUE;
}

//
//  FUNCTION: WndProc(HWND, unsigned, WORD, LONG)
//
//  PURPOSE:  Processes messages for the main window.
//
//  WM_COMMAND	- process the application menu
//  WM_PAINT	- Paint the main window
//  WM_DESTROY	- post a quit message and return
//
//
LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
	int wmId, wmEvent;
	PAINTSTRUCT ps;
	HDC hdc;
	TCHAR szHello[MAX_LOADSTRING];
	LoadString(hInst, IDS_HELLO, szHello, MAX_LOADSTRING);

	switch (message) 
	{
		case WM_COMMAND:
			wmId    = LOWORD(wParam); 
			wmEvent = HIWORD(wParam); 
			// Parse the menu selections:
			switch (wmId)
			{
				case IDM_ABOUT:
				   DialogBox(hInst, (LPCTSTR)IDD_ABOUTBOX, hWnd, (DLGPROC)About);
				   break;
				case IDM_EXIT:
				   DestroyWindow(hWnd);
				   break;
				default:
				   return DefWindowProc(hWnd, message, wParam, lParam);
			}
			break;
		case WM_PAINT:
			hdc = BeginPaint(hWnd, &ps);
			// TODO: Add any drawing code here...
			EndPaint(hWnd, &ps);
			break;
		case WM_DESTROY:
			PostQuitMessage(0);
			break;
		default:
			return DefWindowProc(hWnd, message, wParam, lParam);
   }
   return 0;
}

// Mesage handler for about box.
LRESULT CALLBACK About(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
{
	switch (message)
	{
		case WM_INITDIALOG:
				return TRUE;

		case WM_COMMAND:
			if (LOWORD(wParam) == IDOK || LOWORD(wParam) == IDCANCEL) 
			{
				EndDialog(hDlg, LOWORD(wParam));
				return TRUE;
			}
			break;
	}
    return FALSE;
}



int MessageLoop()
{
  MSG msg;

  BOOL idle = TRUE;

  while(TRUE) {

    //  Idle handler loop

    while(idle) {

      while(PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) {
        if (msg.message == WM_QUIT) return TRUE;
        TranslateMessage(&msg);
        DispatchMessage(&msg);
      }

      unsigned long time = timeGetTime();//GetTickCount();

	  idle = Idle(time);
    }

    //  Blocking (non-idle) loop

    while(!idle) {
      GetMessage(&msg, NULL, 0, 0);
      if (msg.message == WM_QUIT) return msg.wParam;
      if (msg.message == WM_USER) idle = TRUE;
      TranslateMessage(&msg);
      DispatchMessage(&msg);
    }

  }

  return msg.wParam;
}


