/****************************************************************************
   DirectDraw example
****************************************************************************/

#pragma option LST
#pragma option w32

#jumptomain NONE

#include "windows.h"
#include "ddraw.h"

//*******************************************************************************
// Simple DirectDraw Interface
//*******************************************************************************

dword pdd = 0;
dword pPrimarySurface = 0;
dword pBackBuffer = 0;
dword pOffScreen = 0;
dword pPalette = 0;

// offscreen buffer
byte Array[640*480];

// palette
PALETTEENTRY Palette[256] = FROM "palette.pal";

// just to be sure
void _ZeroMemory(dword dest, dword size)
{
	asm {
		push edi
		xor  al, al
		mov  ecx, size
		mov  edi, dest
		rep  stosb
		pop  edi
	}
}

long CreateSurfaces()
{
	DDSURFACEDESC2 ddSurfaceDesc;
	DDSCAPS2 ddsCaps;
	//long hRet;

	// Create primary surface
	_ZeroMemory(#ddSurfaceDesc, sizeof(ddSurfaceDesc));

	ddSurfaceDesc.dwSize = sizeof(ddSurfaceDesc);
	ddSurfaceDesc.dwFlags = DDSD_CAPS | DDSD_BACKBUFFERCOUNT;
	ddSurfaceDesc.ddsCaps.dwCaps = DDSCAPS_PRIMARYSURFACE | DDSCAPS_FLIP | DDSCAPS_COMPLEX;
	ddSurfaceDesc.dwBackBufferCount = 1;

	EBX = pdd; EBX = DSDWORD[EBX];
	EBX.IDirectDraw.CreateSurface(pdd, #ddSurfaceDesc, #pPrimarySurface, 0);

	// Create backbuffer
	_ZeroMemory(#ddsCaps, sizeof(ddsCaps));
	ddsCaps.dwCaps = DDSCAPS_BACKBUFFER;

	EBX = pPrimarySurface; EBX = DSDWORD[EBX];
	EBX.IDirectDrawSurface.GetAttachedSurface(pPrimarySurface, #ddsCaps, #pBackBuffer);

	// Create offscreen surface
	_ZeroMemory(#ddSurfaceDesc, sizeof(ddSurfaceDesc));
	_ZeroMemory(#Array, sizeof(Array));

	// Watch for correct flags! Consult the SDK!
	ddSurfaceDesc.dwSize = sizeof(ddSurfaceDesc);
	// What parameters will be set manually
	ddSurfaceDesc.dwFlags = DDSD_CAPS |
				DDSD_HEIGHT |
				DDSD_WIDTH |
				DDSD_LPSURFACE |
				DDSD_PITCH |
				DDSD_PIXELFORMAT;
	// And very important...
	ddSurfaceDesc.ddsCaps.dwCaps = DDSCAPS_OFFSCREENPLAIN | DDSCAPS_SYSTEMMEMORY;
	ddSurfaceDesc.dwWidth = 640;
	ddSurfaceDesc.dwHeight = 480;
	ddSurfaceDesc.lPitch = 640;
	ddSurfaceDesc.lpSurface = #Array;

	// Color will be 8-bit
	ddSurfaceDesc.ddpfPixelFormat.dwSize = sizeof(DDPIXELFORMAT);
	ddSurfaceDesc.ddpfPixelFormat.dwFlags = DDPF_PALETTEINDEXED8 | DDPF_RGB;
	ddSurfaceDesc.ddpfPixelFormat.dwRGBBitCount = 8;

	EBX = pdd; EBX = DSDWORD[EBX];
	EBX.IDirectDraw.CreateSurface(pdd, #ddSurfaceDesc, #pOffScreen, 0);

	// bmp palette is BGRA but DDraw needs RGBA, so swap blue and red
	FOR(ECX = 0; ECX < 256; ECX++) Palette[ECX].peRed >< Palette[ECX].peBlue;

	EBX = pdd; EBX = DSDWORD[EBX];
	EBX.IDirectDraw.CreatePalette(pdd, DDPCAPS_8BIT|DDPCAPS_ALLOW256, #Palette, #pPalette, 0);

	EBX = pPrimarySurface; EBX = DSDWORD[EBX];
	EBX.IDirectDrawSurface.SetPalette(pPrimarySurface, pPalette);
	//IF(EAX != DD_OK) Beep(440, 1000);
}

long InitDD(long hMainWnd)
{
	DirectDrawCreateEx(0, #pdd, #IID_IDirectDraw7, 0);

	$push ebx

	EBX = pdd; EBX = DSDWORD[EBX];
	EBX.IDirectDraw.SetCooperativeLevel(pdd, hMainWnd, DDSCL_EXCLUSIVE | DDSCL_FULLSCREEN);

	EBX = pdd; EBX = DSDWORD[EBX];
	EBX.IDirectDraw.SetDisplayMode(pdd, 640, 480, 8, 0, 0);

	CreateSurfaces();

	$pop ebx
}

long RemoveDD()
{
	$push ebx

	IF(pdd != 0) {
		IF(pPrimarySurface != 0) {
			EBX = pPrimarySurface; EBX = DSDWORD[EBX];
			EBX.IDirectDrawSurface.Release(pPrimarySurface);
			pPrimarySurface = 0;
		}

		IF(pOffScreen != 0) {
			EBX = pOffScreen; EBX = DSDWORD[EBX];
			EBX.IDirectDrawSurface.Release(pOffScreen);
		}

		EBX = pdd; EBX = DSDWORD[EBX];
		EBX.IDirectDraw.Release(pdd);
		pdd = 0;
	}

	$pop ebx
}


// Returns pointer to the surface
:dword ScreenLock() uses EBX
{
    DDSURFACEDESC2 ddSurfaceDesc;

    ddSurfaceDesc = 0;
    ddSurfaceDesc.dwSize = sizeof(ddSurfaceDesc);

    // lock the offscreen surface
    EBX = pOffScreen; EBX = DSDWORD[EBX];
    EBX.IDirectDrawSurface.Lock(pOffScreen, 0, #ddSurfaceDesc,
				DDLOCK_SURFACEMEMORYPTR | DDLOCK_WAIT, 0);

    // get the pointer to offscreen surface
    return ddSurfaceDesc.lpSurface;
}

:void ScreenUnlock() uses EBX
{
    // unlock the surface
    EBX = pOffScreen;
    IF(EBX !=0) {
	EBX = DSDWORD[EBX];
	EBX.IDirectDrawSurface.Unlock(pOffScreen, 0);
    }
}

// write data to the screen
void BlitScreen() uses EBX
{
    EBX = pPrimarySurface;
    IF(EBX != 0 ) {
	// blit it
	EBX = DSDWORD[EBX];
	EBX.IDirectDrawSurface.BltFast(pPrimarySurface, 0, 0, pOffScreen, 0, DDBLTFAST_NOCOLORKEY);
    }
}

void WaitVBlank() uses EBX
{

    EBX = pdd;
    IF(EBX != 0) {
	EBX = DSDWORD[EBX];
	EBX.IDirectDraw.WaitForVerticalBlank(pdd, DDWAITVB_BLOCKBEGIN, 0);
    }

}
//*********************************************************************************************
// simple port of _line19() from vga.h--
// all regs just made 32 bits wide
void line(int x1, y1, x2, y2, byte color) uses ESI, EDI, EBX
{
	EAX = 640;
	EDI = color;
	ECX = y1;
	EBX = x1;
	EDX = y2;
	EBP = x2;
	IF(int ECX > EDX){
		ECX >< EDX;
		EBX >< EBP;
	}
	ESI = EDX;
	EAX = EAX * ECX + EBX;
	EDX = ESI;
	EAX >< EDI;
	EBP -= EBX;
	EDX -= ECX;
	ESI = 1;
	IF(int EBP <= 0){
		ESI--;
		ESI--;
		-EBP;
	}
	IF(int EBP > EDX){
		ECX = EBP;
		EDX += EDX;
		EBX = EDX;
		EBX -= EBP;
		EBP += EBP;
		DSBYTE[#Array + EDI] = AL;
		IF(ECX != 0){
			loop(ECX){
				IF(int EBX >= 0){
					EDI += 640;
					EBX -= EBP;
				}
				EBX += EDX;
				EDI += ESI;
				DSBYTE[#Array + EDI] = AL;
			}
		}
		RETURN;
	}
	ECX = EDX;
	EBP += EBP;
	EBX = EBP;
	EBX -= EDX;
	EDX += EDX;
	DSBYTE[#Array + EDI] = AL;
	IF(ECX != 0){
		loop(ECX){
			IF(int EBX >= 0){
				EDI += ESI;
				EBX -= EDX;
			}
			EBX += EBP;
			EDI += 640;
			DSBYTE[#Array + EDI] = AL;
		}
	}
}

//*********************************************************************************************
dword hInst;
dword hWnd;
MSG msg;
char classTitle = "DDraw demo";


void main()
{

    WNDCLASSEX wnd;


    hInst = GetModuleHandle(0);

    wnd.cbSize = sizeof(wnd);
    wnd.style = CS_HREDRAW | CS_VREDRAW;
    wnd.lpfnWndProc = #WndProc;
    wnd.cbWndExtra = 0;
    wnd.cbClsExtra = 0;
    wnd.hInstance = hInst;
    wnd.hIcon = LoadIconA(0, IDC_ARROW);
    wnd.hCursor = LoadCursorA(0, IDC_ARROW);
    wnd.hbrBackground = 0;
    wnd.lpszMenuName = 0;
    wnd.lpszClassName = #classTitle;
    wnd.hIconSm = 0;

    RegisterClassExA(#wnd);


    hWnd = CreateWindowExA(0,
			   #classTitle,
			   "Direct Draw Demo in Sphinx C-- v0.239",
			   WS_POPUP,
			   0,
			   0,
			   640,
			   480,
			   NULL,
			   NULL,
			   hInst, NULL);

    ShowWindow(hWnd, SW_SHOW);
    UpdateWindow(hWnd);

    // Init DirectDraw
    InitDD(hWnd);

    do {
	IF(PeekMessage(#msg, NULL, 0, 0, PM_REMOVE)) {
	    TranslateMessage(#msg);
	    DispatchMessageA(#msg);
	}

	// this does all the graphics update
	UpdateScreen();

    } while(msg.message != WM_QUIT);

    // Deinit DirectDraw
    RemoveDD();

    ExitProcess(0);
}


dword	WndProc(dword hwnd, msg, wPar, lPar)
{
	SWITCH(msg){
		CASE WM_PAINT:
			EAX = 0;
			BREAK;
		CASE WM_DESTROY:
			PostQuitMessage(0);
			EAX = 0;
			BREAK;
		CASE WM_KEYDOWN:
			IF(wPar == VK_ESCAPE) DestroyWindow(hwnd);
			EAX = 0;
			BREAK;
		default:
			DefWindowProcA(hwnd, msg, wPar, lPar);
	}
}

//*************************************************************************************************

int y2 = 300, x2 = 200,
    x3 = 600, y3 = 200, y4 = 300, x4 = 600,
    xv2 = 2, xv3 = -2, xv4 = 2,
    yv2 = 2, yv3 = -2, yv4 = 2, icolor = 0, idir = 1;

dword seed = 0x12345678;

void UpdateScreen() uses EBX
{

    // Some static noise
    FOR(EBX = 0; EBX < 640*240/4; EBX++) {
	seed = seed >> 2 ^ seed + GetTickCount();
	DSDWORD[#Array+EBX*4] = seed;
    }

    // Show palette
    FOR(EBX = 0; EBX < 256; EBX++) line(EBX, 0, EBX, 479, EBX);

    line(x2, y2, x3, y3, icolor);
    line(x3, y3, x4, y4, icolor);
    line(x4, y4, x2, y2, icolor);

    icolor += idir;
    IF(icolor >= 255) || (icolor <= 0) -idir;


    x2 += xv2;
    x2 += xv2;
    x3 += xv3;
    x4 += xv4;
    y2 += yv2;
    y2 += yv2;
    y3 += yv3;
    y4 += yv4;

    IF(x2 <= 0) xv2 = 2;
    IF(x2 >= 640) xv2 = -2;
    IF(x2 <= 0) xv2 = 2;
    IF(x2 >= 640) xv2 = -2;
    IF(x3 <= 0) xv3 = 2;
    IF(x3 >= 640) xv3 = -2;
    IF(x4 <= 0) xv4 = 2;
    IF(x4 >= 640) xv4 = -2;

    IF(y2 <= 0) yv2 = 2;
    IF(y2 >= 480) yv2 = -2;
    IF(y2 <= 0) yv2 = 2;
    IF(y2 >= 480) yv2 = -2;
    IF(y3 <= 0) yv3 = 2;
    IF(y3 >= 480) yv3 = -2;
    IF(y4 <= 0) yv4 = 2;
    IF(y4 >= 480) yv4 = -2;


    // Wait for vertical retrace and blit
    WaitVBlank();
    BlitScreen();

    //for(ECX = 0; ECX < 256; ECX++) Palette[ECX].peRed = icolor;
    //EBX = pPalette; EBX = DSDWORD[EBX];
    //EBX.IDirectDrawPalette.SetEntries(pPalette, 0, 0, 1, #Palette);


}
