⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 gfx.cpp

📁 nes游戏模拟器
💻 CPP
📖 第 1 页 / 共 3 页
字号:
//------------------------------------------------------------------------------
// Name: Gfx.cpp
// Desc: Holds all the graphics related functions. This includes drawing
//       scanlines, directx stuff, etc.
//------------------------------------------------------------------------------

#include "Gfx.h"
#include "Palette.h"
#include "AttributeTable.h"

// Global array of colors.
BYTE abyColors[NUMBER_OF_COLORS][4];

// DirectDraw Surfaces used for the graphics engine.
LPDIRECTDRAW7        lpDD;
LPDIRECTDRAWSURFACE7 lpddsPrimary;
LPDIRECTDRAWSURFACE7 lpddsBack;
LPDIRECTDRAWSURFACE7 lpddsScreen;
LPDIRECTDRAWCLIPPER  lpClipper;
RECT                 rcScreen;

BYTE* pSurfaceMemory; // Pointer to our locked memory to draw our pixels on.
LONG  lBytesPerPixel; // Number of bytes per pixel.
LONG  FourMinusBPP;   // 4 - Number of bytes per pixel (to speed up putpixel loop).
LONG  lSurfacePitch;  // The pitch of the scanline use to move to the next line.

//------------------------------------------------------------------------------
// Name: CreateDirectDraw()
// Desc: Creates DirectDraw and all the surfaces and sets up the colors
//       array with the paletted nes colors.
//------------------------------------------------------------------------------
HRESULT CreateDirectDraw(HWND hwnd)
{
	DDSURFACEDESC2 ddsd;
	DDPIXELFORMAT DDpf;  // DirectDraw pixel format structure.
	HRESULT ddrval;
	RECT rcClient;
   
	// Create the main DirectDraw object.
	ddrval = DirectDrawCreateEx(NULL, (VOID**)&lpDD, IID_IDirectDraw7, NULL);
	if(ddrval != DD_OK)
		return FALSE;

	// Using DDSCL_NORMAL means we will coexist with GDI.
	ddrval = lpDD->SetCooperativeLevel(hwnd, DDSCL_NORMAL);
	if(ddrval != DD_OK)
	{
		lpDD->Release();
		return FALSE;
	}
	
	memset(&ddsd, 0, sizeof(ddsd));
	ddsd.dwSize = sizeof(ddsd);
	ddsd.dwFlags = DDSD_CAPS;
	ddsd.ddsCaps.dwCaps = DDSCAPS_PRIMARYSURFACE;

	// The primary surface is not a page flipping surface.
	ddrval = lpDD->CreateSurface(&ddsd, &lpddsPrimary, NULL);
	if (ddrval != DD_OK)
	{
		lpDD->Release();
		return FALSE;
	}

	// Create a clipper to ensure that our drawing stays inside our window.
	ddrval = lpDD->CreateClipper(0, &lpClipper, NULL);
	if (ddrval != DD_OK)
	{
		lpddsPrimary->Release();
		lpDD->Release();
		return FALSE;
	}

	// Setting it to our hwnd gives the clipper the coordinates from our window.
	ddrval = lpClipper->SetHWnd(0, hwnd);
	if (ddrval != DD_OK)
	{
		lpClipper-> Release();
		lpddsPrimary->Release();
		lpDD->Release();
		return FALSE;
	}

	// Attach the clipper to the primary surface.
	ddrval = lpddsPrimary->SetClipper(lpClipper);
	if (ddrval != DD_OK)
	{
		lpClipper-> Release();
		lpddsPrimary->Release();
		lpDD->Release();
		return FALSE;
	}

	GetClientRect(hwnd, &rcClient);
	memset(&ddsd, 0, sizeof(ddsd));
	ddsd.dwSize = sizeof(ddsd);
	ddsd.dwFlags = DDSD_CAPS | DDSD_HEIGHT | DDSD_WIDTH;
	ddsd.ddsCaps.dwCaps = DDSCAPS_OFFSCREENPLAIN;
	ddsd.dwWidth = rcClient.right - rcClient.left;
	ddsd.dwHeight = rcClient.bottom - rcClient.top;

	// Create the backbuffer separately.
	ddrval = lpDD->CreateSurface(&ddsd, &lpddsBack, NULL);
	if (ddrval != DD_OK)
	{
		lpClipper-> Release();
		lpddsPrimary->Release();
		lpDD->Release();
		return FALSE;
	}
	
	ddsd.dwWidth = SCREEN_WIDTH;
	ddsd.dwHeight = SCREEN_HEIGHT;

	// Create the offscreen surface for drawing our screen to.
	ddrval = lpDD->CreateSurface(&ddsd, &lpddsScreen, NULL);
	if (ddrval != DD_OK)
	{
		lpClipper-> Release();
		lpddsPrimary->Release();
		lpddsBack->Release();
		lpDD->Release();
		return FALSE;
	}

	// Fill the DDpf structure and get the number of bytes per pixel.
	ZeroMemory(&DDpf, sizeof(DDpf));
	DDpf.dwSize = sizeof(DDpf);
	lpddsScreen->GetPixelFormat(&DDpf);
	
	// Save the number of bytes per pixel and 4 - the number of bytes
	// per pixel to speed things up when drawing pixels.
	lBytesPerPixel = DDpf.dwRGBBitCount / 8;
	FourMinusBPP = 4 - lBytesPerPixel;

	// Compile all the pixels.
	for (int i = 0; i < NUMBER_OF_COLORS; i++)
		CompilePixel(lpddsScreen, i, abyNESPalette[(3*i)], abyNESPalette[(3*i)+1], abyNESPalette[(3*i)+2]);

	return TRUE;
} // end CreateDirectDraw()


//------------------------------------------------------------------------------
// Name: DestroyDirectDraw()
// Desc: Cleans up everything that was initialized for DirectDdraw.
//------------------------------------------------------------------------------
HRESULT DestroyDirectDraw()
{
	if(lpDD != NULL)
	{
		if (lpddsPrimary != NULL)
		{
			lpddsPrimary->Release();
			lpddsPrimary = NULL;
		}
		if (lpddsBack != NULL)
		{
			lpddsBack->Release();
			lpddsBack = NULL;
		}
		if (lpddsScreen != NULL)
		{
			lpddsScreen->Release();
			lpddsScreen = NULL;
		}
		if (lpClipper != NULL)
		{
			lpClipper->Release();
			lpClipper = NULL;
		}

		lpDD->Release();
		lpDD = NULL;
	}

	return DD_OK;
} // end DestroyDirectDraw()


//------------------------------------------------------------------------------
// Name: Flip()
// Desc: Blts the nes screen surface to the back surface and then flips
//       the back surface and the primary if in full screen mode. If in
//       windowed mode, it blts the back surface to the primary surface
//       instead of fliping them.
//------------------------------------------------------------------------------
VOID Flip()
{
	//lpddsBack->Blt(NULL, lpddsScreen, NULL, DDBLT_WAIT, NULL);
	//lpddsPrimary->Blt(NULL, lpddsBack, NULL, DDBLT_WAIT, NULL);
	lpddsPrimary->Blt(&rcScreen, lpddsScreen, NULL, DDBLT_WAIT, NULL);
} // end Flip()


//------------------------------------------------------------------------------
// Name: BeginDrawing()
// Desc: Clears the surface to the background color, locks the surface and
//       sets up the pointer to the video memory.
//------------------------------------------------------------------------------
HRESULT BeginDrawing()
{
	HRESULT ddrval;
	DDSURFACEDESC2 ddsdLockedSurf; // Surface description to lock our surface.

	// Clear to the background color which is located in bits 7-5 in reg $2001
	// when bit 0 is set, otherwise is the first entry in the background palette.
	//BYTE byColorIndex = ((CPU.Memory[0x2001] & 0xE0) >> 5) * 3;
	//ClearScreen(RGB(abyNESPalette[byColorIndex], 0, 0));
	ClearScreen(RGB(0, 0, 0));
	
	// Lock the surface.
	ddsdLockedSurf.dwSize = sizeof(DDSURFACEDESC2);
	ddrval = lpddsScreen->Lock(NULL, &ddsdLockedSurf, DDLOCK_WAIT | DDLOCK_NOSYSLOCK, NULL);
	if (ddrval == DD_OK)
	{
		// Initialize the pointer to surface memory to point to the
		// beginning of our scanline.
		pSurfaceMemory = (BYTE*)ddsdLockedSurf.lpSurface;

		// Save the pitch of the surface for later use in other functions.
		lSurfacePitch = ddsdLockedSurf.lPitch;
	}

	return ddrval;
} // end BeginDrawing()


//------------------------------------------------------------------------------
// Name: EndDrawing()
// Desc: Unlocks the drawing surface and flips the surfaces.
//------------------------------------------------------------------------------
VOID EndDrawing()
{
	// Unlock the drawing surface.
	lpddsScreen->Unlock(NULL);

	// Flip the surfaces to display what we've drawn.
	Flip();
} // end EndDrawing()


//------------------------------------------------------------------------------
// Name: DrawScanline()
// Desc: The main routine for drawing a scanline. Locks and unlocks the
//       surface and calls the scanline drawing routing for the type
//       of mirroring.
//------------------------------------------------------------------------------
VOID DrawScanline()
{
	// Save the pointer to video memory so we don't permanently modify it.
	BYTE* pTemp = pSurfaceMemory;
	// Now move the pointer to surface memory to the correct scanline.
	pSurfaceMemory += (lSurfacePitch * wScanline);

	// Draw our scanline if its enabled.
	if (CPU.Memory[0x2001] & 0x08)
		DrawScanlineH();

	// Draw all the sprites that are on the scanline if they are enabled.
	//if (CPU.Memory[0x2001] & 0x10)
	//	DrawScanlineSprites();

	// Restore our pointer to memory.
	pSurfaceMemory = pTemp;
} // end DrawScanline()


//------------------------------------------------------------------------------
// Name: DrawScanlineH()
// Desc: Draws a scanline using horizontal mirroring.
//------------------------------------------------------------------------------
VOID DrawScanlineH()
{
	BYTE*  pabyNameTables;
	BYTE*  pbyNameTableTile;
	BYTE*  pbyAttributeTable;
	BYTE*  pbyCurPatternTableAddr;
	BYTE   byCounter = 8;
	BYTE   byUpperColor = 0;
	WORD   wTileNum = 0;
	WORD   wBegTileNum = 0;
	WORD   relx = byHScroll[0];
	WORD   x = 0;
	DWORD  byScanlineMOD8 = wScanline % 8;

	__asm
	{
		// Save the registers
		pushad
	
		// The pointer to the current nametable tile will always
		// start with the first name table.
		
		// Get the bits that determine what nametable to use.
		xor ebx, ebx
		mov bl, [CPU.Memory+02000h]
		and bl, 03
		
		// Get the address of the name table and save it for later.
		lea eax, [PPU.apbyNameTables+ebx*4]
		mov pabyNameTables, eax

		// Get the pointer to the current name table and store
		// it in the variables for later. Also, store the
		// the attribute table.
		mov eax, [eax]
		mov pbyNameTableTile, eax
		mov pbyAttributeTable, eax
		add eax, 03C0h
		mov pbyAttributeTable, eax

		// Get the address of the background pattern table
		xor ebx, ebx
		mov bl, BYTE PTR [CPU.Memory+02000h]   // Address of the PPU control register #1
		and bl, PATTERN_TABLE_ADDRESS
		shr bx, 2                              // Which pattern table (0,1)
		mov ebx, [PPU.apbyPatternTables+ebx]   // Move the address of the pattern table array into ebx
		mov pbyCurPatternTableAddr, ebx        // Save the address of the pattern table for later

		//--------------------------------------------------------
		// Get the address of the tile number in the ppu memory
		// by (TILENUM * 16) + PATTERNTABLE (Yoshi)
		//--------------------------------------------------------

		and eax, 000000FFh
		mov ax, wScanline
		mov cx, relx

		// TILENUM = ((Scanline / 8) * 32) + (HScroll / 8)
		shr ax, 3        // Scanline / 8
		shl ax, 5        // Multiply by 32

		// Save the beginning tile number so we don't have
		// to recalculate it when we change name tables.
		mov wBegTileNum, ax

		shr cx, 3        // HScroll / 8
		add ax, cx       // ax=TILENUM
		
		// Save the tile number so we can increment it.
		mov wTileNum, ax

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -