Implement most of the drawing routine

This commit is contained in:
Doyle Thai 2017-04-05 23:08:40 +10:00
parent b30613e98d
commit ac6d32d1b9
5 changed files with 201 additions and 58 deletions

View File

@ -35,8 +35,9 @@ REM WX treat warnings as errors
REM wd4100 ignore: unused argument parameters
REM wd4201 ignore: nonstandard extension used: nameless struct/union
REM wd4189 ignore: local variable is initialised but not referenced
REM wd4505 ignore: unreference local functions that will be removed
set CompileFlags=-EHa- -GR- -Oi -MT -Z7 -W4 -WX -wd4100 -wd4201 -wd4189
set CompileFlags=-EHa- -GR- -Oi -MT -Z7 -W4 -WX -wd4100 -wd4201 -wd4189 -wd4505
REM Include directories
set IncludeFlags=

View File

@ -73,7 +73,6 @@ FILE_SCOPE RandPCGState pcgState;
FILE_SCOPE void dchip8_init_memory(u8 *memory)
{
DQNT_ASSERT(memory.permanentMemSize == 4096);
const u8 PRESET_FONTS[] =
{
// "0"
@ -206,14 +205,10 @@ FILE_SCOPE void dchip8_init_cpu(Chip8CPU *chip8CPU)
chip8CPU->state = chip8state_load_file;
}
void dchip8_update(PlatformRenderBuffer renderBuffer, PlatformInput input,
PlatformMemory memory)
FILE_SCOPE
void dchip8_debug_draw_half_colored_screen_internal(
PlatformRenderBuffer renderBuffer)
{
DQNT_ASSERT(indexRegister >= 0 && indexRegister <= 0xFFF);
DQNT_ASSERT(programCounter >= 0 && programCounter <= 0xFFF);
DQNT_ASSERT(renderBuffer.bytesPerPixel == 4);
const i32 numPixels = renderBuffer.width * renderBuffer.height;
u32 *bitmapBuffer = (u32 *)renderBuffer.memory;
for (i32 i = 0; i < numPixels; i++)
@ -251,22 +246,49 @@ void dchip8_update(PlatformRenderBuffer renderBuffer, PlatformInput input,
bitmapBuffer[i] = color;
}
}
}
FILE_SCOPE void dchip8_init_display(PlatformRenderBuffer renderBuffer)
{
// Init screen to 0 alpha, and let alpha simulate "turning on a pixel"
i32 numPixels = renderBuffer.width * renderBuffer.height;
u32 *bitmapBuffer = (u32 *)renderBuffer.memory;
for (i32 i = 0; i < numPixels; i++)
{
u8 r = (u8)(0.0f);
u8 g = (u8)(0.0f);
u8 b = (u8)(0.0f);
u8 a = (u8)(0.0f);
u32 color = (a << 24) | (r << 16) | (g << 8) | (b << 0);
bitmapBuffer[i] = color;
}
}
void dchip8_update(PlatformRenderBuffer renderBuffer, PlatformInput input,
PlatformMemory memory)
{
DQNT_ASSERT(cpu.indexRegister >= 0 && cpu.indexRegister <= 0xFFF);
DQNT_ASSERT(cpu.programCounter >= 0 && cpu.programCounter <= 0xFFF);
DQNT_ASSERT(renderBuffer.bytesPerPixel == 4);
DQNT_ASSERT(memory.permanentMemSize == 4096);
u8 *mainMem = (u8 *)memory.permanentMem;
if (cpu.state == chip8state_init)
{
dchip8_init_cpu(&cpu);
dchip8_init_memory(mainMem);
dchip8_init_display(renderBuffer);
}
#if 0
#if 1
if (cpu.state == chip8state_load_file)
{
PlatformFile file = {};
if (platform_open_file(L"roms/PONG", &file))
if (platform_open_file(L"roms/BLITZ", &file))
{
DQNT_ASSERT((cpu.INIT_ADDRESS + file.size) <=
DQNT_ARRAY_COUNT(mainMem));
memory.permanentMemSize);
void *loadToAddr = (void *)(&mainMem[cpu.INIT_ADDRESS]);
if (platform_read_file(file, loadToAddr, (u32)file.size))
@ -296,15 +318,12 @@ void dchip8_update(PlatformRenderBuffer renderBuffer, PlatformInput input,
// CLS - 00E0 - Clear the display
if (opLowByte == 0xE0)
{
u32 numBytesToClear = renderBuffer.width *
renderBuffer.height *
renderBuffer.bytesPerPixel;
memset(renderBuffer.memory, 0, numBytesToClear);
dchip8_init_display(renderBuffer);
}
// RET - 00EE - Return from subroutine
else if (opLowByte == 0xEE)
{
cpu.programCounter = cpu.stack[cpu.stackPointer--];
cpu.programCounter = cpu.stack[--cpu.stackPointer];
}
}
break;
@ -312,7 +331,7 @@ void dchip8_update(PlatformRenderBuffer renderBuffer, PlatformInput input,
case 0x10:
case 0x20:
{
u16 loc = ((0x0F & opHighByte) << 8) | (0xFF & opLowByte);
u16 loc = ((0x0F & opHighByte) << 8) | opLowByte;
DQNT_ASSERT(loc <= 0x0FFF);
// JP addr - 1nnn - Jump to location nnn
@ -324,10 +343,8 @@ void dchip8_update(PlatformRenderBuffer renderBuffer, PlatformInput input,
else
{
DQNT_ASSERT(opFirstNibble == 0x20);
cpu.stackPointer++;
cpu.stack[cpu.stackPointer++] = cpu.programCounter;
DQNT_ASSERT(cpu.stackPointer < DQNT_ARRAY_COUNT(cpu.stack));
cpu.stack[cpu.stackPointer] = cpu.programCounter;
}
cpu.programCounter = loc;
@ -363,7 +380,7 @@ void dchip8_update(PlatformRenderBuffer renderBuffer, PlatformInput input,
u8 firstRegNum = (0x0F & opHighByte);
DQNT_ASSERT(firstRegNum < DQNT_ARRAY_COUNT(cpu.registerArray));
u8 secondRegNum = (0xF0 & opLowByte);
u8 secondRegNum = (0xF0 & opLowByte) >> 4;
DQNT_ASSERT(secondRegNum < DQNT_ARRAY_COUNT(cpu.registerArray));
if (cpu.registerArray[firstRegNum] ==
@ -400,37 +417,38 @@ void dchip8_update(PlatformRenderBuffer renderBuffer, PlatformInput input,
u8 firstRegNum = (0x0F & opHighByte);
DQNT_ASSERT(firstRegNum < DQNT_ARRAY_COUNT(cpu.registerArray));
u8 secondRegNum = (0xF0 & opLowByte);
u8 secondRegNum = (0xF0 & opLowByte) >> 4;
DQNT_ASSERT(secondRegNum < DQNT_ARRAY_COUNT(cpu.registerArray));
u8 *vx = &cpu.registerArray[firstRegNum];
u8 *vy = &cpu.registerArray[secondRegNum];
u8 opFourthNibble = (opLowByte & 0x0F);
// LD Vx, Vy - 8xy0 - Set Vx = Vy
if (opLowByte == 0x00)
if (opFourthNibble == 0x00)
{
*vx = *vy;
}
// OR Vx, Vy - 8xy1 - Set Vx = Vx OR Vy
else if (opLowByte == 0x01)
else if (opFourthNibble == 0x01)
{
u8 result = (*vx | *vy);
*vx = result;
}
// AND Vx, Vy - 8xy2 - Set Vx = Vx AND Vy
else if (opLowByte == 0x02)
else if (opFourthNibble == 0x02)
{
u8 result = (*vx & *vy);
*vx = result;
}
// XOR Vx, Vy - 8xy3 - Set Vx = Vx XOR Vy
else if (opLowByte == 0x03)
else if (opFourthNibble == 0x03)
{
u8 result = (*vx & *vy);
*vx = result;
}
// ADD Vx, Vy - 8xy4 - Set Vx = Vx + Vy, set VF = carry
else if (opLowByte == 0x04)
else if (opFourthNibble == 0x04)
{
u16 result = (*vx + *vy);
*vx = (u8)result;
@ -438,7 +456,7 @@ void dchip8_update(PlatformRenderBuffer renderBuffer, PlatformInput input,
if (result > 255) cpu.VF = (result > 255) ? 1 : 0;
}
// SUB Vx, Vy - 8xy5 - Set Vx = Vx - Vy, set VF = NOT borrow
else if (opLowByte == 0x05)
else if (opFourthNibble == 0x05)
{
if (*vx > *vy)
cpu.VF = 1;
@ -448,7 +466,7 @@ void dchip8_update(PlatformRenderBuffer renderBuffer, PlatformInput input,
*vx -= *vy;
}
// SHR Vx {, Vy} - 8xy6 - Set Vx = Vx SHR 1
else if (opLowByte == 0x06)
else if (opFourthNibble == 0x06)
{
if (*vx & 1)
cpu.VF = 1;
@ -458,7 +476,7 @@ void dchip8_update(PlatformRenderBuffer renderBuffer, PlatformInput input,
*vx >>= 1;
}
// SUBN Vx {, Vy} - 8xy7 - Set Vx = Vy - Vx, set VF = NOT borrow
else if (opLowByte == 0x07)
else if (opFourthNibble == 0x07)
{
if (*vy > *vx)
cpu.VF = 1;
@ -470,7 +488,7 @@ void dchip8_update(PlatformRenderBuffer renderBuffer, PlatformInput input,
// SHL Vx {, Vy} - 8xyE - Set Vx = SHL 1
else
{
DQNT_ASSERT(opLowByte == 0x0E);
DQNT_ASSERT(opFourthNibble == 0x0E);
if ((*vx >> 7) == 1)
cpu.VF = 1;
else
@ -487,7 +505,7 @@ void dchip8_update(PlatformRenderBuffer renderBuffer, PlatformInput input,
u8 firstRegNum = (0x0F & opHighByte);
DQNT_ASSERT(firstRegNum < DQNT_ARRAY_COUNT(cpu.registerArray));
u8 secondRegNum = (0xF0 & opLowByte);
u8 secondRegNum = (0xF0 & opLowByte) >> 4;
DQNT_ASSERT(secondRegNum < DQNT_ARRAY_COUNT(cpu.registerArray));
u8 *vx = &cpu.registerArray[firstRegNum];
@ -500,7 +518,7 @@ void dchip8_update(PlatformRenderBuffer renderBuffer, PlatformInput input,
// LD I, addr - Annn - Set I = nnn
case 0xA0:
{
u8 valToSet = opLowByte;
u16 valToSet = ((0x0F & opHighByte) << 8) | opLowByte;
cpu.indexRegister = valToSet;
}
break;
@ -531,14 +549,73 @@ void dchip8_update(PlatformRenderBuffer renderBuffer, PlatformInput input,
// location I at (Vx, Vy), set VF = collision
case 0xD0:
{
// TODO(doyle): Implement drawing
u8 posX = (0x0F & opHighByte);
u8 posY = (0xF0 & opLowByte);
u8 xRegister = (0x0F & opHighByte);
u8 yRegister = (0xF0 & opLowByte) >> 4;
DQNT_ASSERT(xRegister < DQNT_ARRAY_COUNT(cpu.registerArray) &&
yRegister < DQNT_ARRAY_COUNT(cpu.registerArray));
u8 initPosX = cpu.registerArray[xRegister];
u8 initPosY = cpu.registerArray[yRegister];
u8 readNumBytesFromMem = (0x0F & opLowByte);
// NOTE: can't be more than 16 in Y according to specs.
DQNT_ASSERT(readNumBytesFromMem < 16);
u8 *renderBitmap = (u8 *)renderBuffer.memory;
const i32 BYTES_PER_PIXEL = renderBuffer.bytesPerPixel;
i32 pitch = renderBuffer.width * BYTES_PER_PIXEL;
for (i32 i = 0; i < readNumBytesFromMem; i++)
{
u8 *memPtr = &mainMem[cpu.indexRegister + i];
u8 spriteBytes = mainMem[cpu.indexRegister + i];
u8 posY = initPosY + (u8)i;
if (posY >= renderBuffer.height) posY = 0;
const i32 ALPHA_BYTE_INTERVAL = renderBuffer.bytesPerPixel;
const i32 BITS_IN_BYTE = 8;
i32 baseBitShift = BITS_IN_BYTE - 1;
for (i32 shift = 0; shift < BITS_IN_BYTE; shift++)
{
u8 posX = initPosX + (u8)shift;
if (posX >= renderBuffer.width) posX = 0;
u32 bitmapOffset =
(posX * BYTES_PER_PIXEL) + (posY * pitch);
// NOTE: Since we are using a 4bpp bitmap, let's use the
// alpha channel to determine if a pixel is on or not.
u32 *pixel = (u32 *)(&renderBitmap[bitmapOffset]);
u8 alphaBit = (*pixel >> 24);
DQNT_ASSERT(alphaBit == 0 || alphaBit == 255);
bool pixelWasOn = (alphaBit == 255) ? true : false;
i32 bitShift = baseBitShift - shift;
bool spriteBit = ((spriteBytes >> bitShift) & 1);
bool pixelIsOn = (pixelWasOn ^ spriteBit);
// TODO(doyle): wrap pixels around
// NOTE: If caused a pixel to XOR into off, then this is
// known as a "collision" in chip8
if (pixelWasOn && !pixelIsOn)
{
cpu.VF = 1;
}
else
{
cpu.VF = 0;
}
if (pixelIsOn)
{
*pixel = 0xFFFFFFFF;
}
else
{
*pixel = 0;
}
}
}
}
break;
@ -599,12 +676,31 @@ void dchip8_update(PlatformRenderBuffer renderBuffer, PlatformInput input,
// LD F, Vx - Fx29 - Set I = location of sprite for digit Vx
else if (opLowByte == 0x29)
{
// TODO: Implement
u8 hexCharFromFontSet = (0x0F & opHighByte);
DQNT_ASSERT(hexCharFromFontSet >= 0x00 &&
hexCharFromFontSet <= 0x0F);
const u8 BITS_IN_BYTE = 8;
u8 fontSizeInMem = BITS_IN_BYTE * 5;
u16 startMemAddrOfFontSet = 0;
cpu.I = (hexCharFromFontSet * fontSizeInMem) *
startMemAddrOfFontSet;
}
// LD B, Vx - Fx33 - Store BCD representations of Vx in memory
// locations I, I+1 and I+2
else if (opLowByte == 0x33)
{
DQNT_ASSERT(regNum < DQNT_ARRAY_COUNT(cpu.registerArray));
u8 vxVal = *vx;
const i32 NUM_DIGITS_IN_HUNDREDS = 3;
for (i32 i = 0; i < NUM_DIGITS_IN_HUNDREDS; i++)
{
u8 rem = vxVal % 10;
vxVal /= 10;
mainMem[cpu.I + ((NUM_DIGITS_IN_HUNDREDS-1) - i)] = rem;
}
}
// LD [I], Vx - Fx55 - Store register V0 through Vx in memory
// starting at location I.
@ -632,6 +728,11 @@ void dchip8_update(PlatformRenderBuffer renderBuffer, PlatformInput input,
}
break;
};
if (cpu.delayTimer > 0) cpu.delayTimer--;
// TODO(doyle): This needs to play a buzzing sound whilst timer > 0
if (cpu.soundTimer > 0) cpu.soundTimer--;
}
}

View File

@ -3,8 +3,8 @@
#include "dqnt.h"
// NOTE: Platform buffers are expected to be bottom to top! I.e. origin is
// bottom left.
// NOTE: Platform buffers are expected to be top to bottom! I.e. origin is top
// left, also 4 bytes per pixel with packing order A R G B
typedef struct PlatformRenderBuffer
{
i32 width;

View File

@ -26,13 +26,7 @@ typedef float f32;
#define DQNT_INVALID_CODE_PATH 0
#define DQNT_ARRAY_COUNT(array) (sizeof(array) / sizeof(array[0]))
#ifdef DEBUG_MODE
#define DQNT_ASSERT(expr) if (!(expr)) { (*((i32 *)0)) = 0; }
#else
#define DQNT_ASSERT(expr)
#endif
#define DQNT_ASSERT(expr) if (!(expr)) { (*((i32 *)0)) = 0; }
#define DQNT_MATH_ABS(x) (((x) < 0) ? (-(x)) : (x))
////////////////////////////////////////////////////////////////////////////////
@ -61,6 +55,7 @@ char *dqnt_strncpy(char *dest, const char *src, i32 numChars);
bool dqnt_str_reverse(char *const buf, const i32 bufSize);
i32 dqnt_str_to_i32 (char *const buf, const i32 bufSize);
void dqnt_i32_to_str (i32 value, char *buf, i32 bufSize);
wchar_t dqnt_wchar_ascii_to_lower(wchar_t character);
i32 dqnt_wstrcmp(const wchar_t *a, const wchar_t *b);
@ -208,6 +203,43 @@ i32 dqnt_str_to_i32(char *const buf, const i32 bufSize)
return result;
}
void dqnt_i32_to_str(i32 value, char *buf, i32 bufSize)
{
if (!buf || bufSize == 0) return;
if (value == 0)
{
buf[0] = '0';
return;
}
// NOTE(doyle): Max 32bit integer (+-)2147483647
i32 charIndex = 0;
bool negative = false;
if (value < 0) negative = true;
if (negative) buf[charIndex++] = '-';
i32 val = DQNT_MATH_ABS(value);
while (val != 0 && charIndex < bufSize)
{
i32 rem = val % 10;
buf[charIndex++] = (u8)rem + '0';
val /= 10;
}
// NOTE(doyle): If string is negative, we only want to reverse starting
// from the second character, so we don't put the negative sign at the end
if (negative)
{
dqnt_str_reverse(buf + 1, charIndex - 1);
}
else
{
dqnt_str_reverse(buf, charIndex);
}
}
wchar_t dqnt_wchar_ascii_to_lower(wchar_t character)
{
if (character >= L'A' && character <= L'Z')

View File

@ -71,20 +71,28 @@ FILE_SCOPE void win32_display_render_bitmap(Win32RenderBitmap renderBitmap,
HDC deviceContext, LONG width,
LONG height)
{
HDC stretchDC = CreateCompatibleDC(deviceContext);
SelectObject(stretchDC, renderBitmap.handle);
StretchBlt(deviceContext, 0, 0, width, height, stretchDC, 0,
0, renderBitmap.width, renderBitmap.height, SRCCOPY);
// NOTE: Win32 AlphaBlend requires the RGB components to be premultiplied
// with alpha.
#if 0
BLENDFUNCTION blend = {};
blend.BlendOp = AC_SRC_OVER;
blend.SourceConstantAlpha = 255;
blend.AlphaFormat = AC_SRC_ALPHA;
HDC alphaBlendDC = CreateCompatibleDC(deviceContext);
SelectObject(alphaBlendDC, renderBitmap.handle);
if (!AlphaBlend(deviceContext, 0, 0, width, height, deviceContext, 0, 0,
width, height, blend))
{
OutputDebugString(L"AlphaBlend() failed.");
}
#endif
AlphaBlend(deviceContext, 0, 0, width, height, alphaBlendDC, 0, 0,
renderBitmap.width, renderBitmap.height, blend);
StretchBlt(deviceContext, 0, 0, width, height, deviceContext, 0, 0,
renderBitmap.width, renderBitmap.height, SRCCOPY);
DeleteDC(alphaBlendDC);
DeleteDC(stretchDC);
}
FILE_SCOPE inline void win32_parse_key_msg(KeyState *key, MSG msg)
@ -172,8 +180,8 @@ int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
// is slightly smaller than 800x600. Windows provides a function to help
// calculate the size you'd need by accounting for the window style.
RECT rect = {};
rect.right = 128;
rect.bottom = 64;
rect.right = 256;
rect.bottom = 128;
DWORD windowStyle =
WS_OVERLAPPEDWINDOW | WS_VISIBLE | WS_CLIPSIBLINGS | WS_CLIPCHILDREN;
@ -253,6 +261,7 @@ int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
platformBuffer.memory = renderBitmap.memory;
platformBuffer.height = renderBitmap.height;
platformBuffer.width = renderBitmap.width;
platformBuffer.bytesPerPixel = renderBitmap.bytesPerPixel;
dchip8_update(platformBuffer, platformInput, platformMemory);
}