diff --git a/src/Common.h b/src/Common.h index a1c8569..ed246ab 100644 --- a/src/Common.h +++ b/src/Common.h @@ -8,13 +8,13 @@ typedef uint32_t u32; typedef uint16_t u16; +typedef uint8_t u8; typedef int64_t i64; typedef int32_t i32; typedef int16_t i16; typedef float f32; -typedef unsigned char u8; #define INVALID_CODE_PATH 0 @@ -88,7 +88,7 @@ inline FILE_SCOPE void common_wstrcat(const wchar_t *a, i32 lenA, ASSERT(outIndex <= outLen); } -inline FILE_SCOPE wchar_t common_wcharAsciiToLowercase(wchar_t character) +inline FILE_SCOPE wchar_t common_wchar_ascii_to_lower(wchar_t character) { if (character >= L'A' && character <= L'Z') { diff --git a/src/dchip8.cpp b/src/dchip8.cpp index 96789b3..8c78875 100644 --- a/src/dchip8.cpp +++ b/src/dchip8.cpp @@ -1,12 +1,71 @@ #include "common.h" #include "dchip8_platform.h" -void dchip8_update(PlatformRenderBuffer *renderBuffer, PlatformInput *input) +typedef struct Chip8CPU { + union { + u8 registerArray[16]; + struct + { + u8 V0; + u8 V1; + u8 V2; + u8 V3; + u8 V4; + u8 V5; + u8 V6; + u8 V7; + u8 V8; + u8 V9; + u8 VA; + u8 VB; + u8 VC; + u8 VD; + u8 VE; + u8 VF; + }; + }; + + + // NOTE: Timer that count at 60hz and when set above 0 will count down to 0. + union { + u8 dt; + u8 delayTimer; + }; + + union { + u8 st; + u8 soundTimer; + }; + + // NOTE: Maximum value is 0xFFF, or 4095 or 12 bits + union { + u16 I; + u16 indexRegister; + }; + + // NOTE: Maximum value is 0xFFF, or 4095 or 12 bits + u16 programCounter; + + u8 stackPointer; + u16 stack[16]; + + bool isInit; +} Chip8CPU; + +FILE_SCOPE Chip8CPU cpu; +FILE_SCOPE u16 opCodes[35]; + +void dchip8_update(PlatformRenderBuffer renderBuffer, PlatformInput input, + PlatformMemory memory) +{ + ASSERT(indexRegister >= 0 && indexRegister <= 0xFFF); + ASSERT(programCounter >= 0 && programCounter <= 0xFFF); + ASSERT(renderBuffer.bytesPerPixel == 4); - const i32 numPixels = renderBuffer->width * renderBuffer->height; - u32 *bitmapBuffer = (u32 *)renderBuffer->memory; + const i32 numPixels = renderBuffer.width * renderBuffer.height; + u32 *bitmapBuffer = (u32 *)renderBuffer.memory; for (i32 i = 0; i < numPixels; i++) { // NOTE: Win32 AlphaBlend requires the RGB components to be @@ -24,4 +83,355 @@ void dchip8_update(PlatformRenderBuffer *renderBuffer, PlatformInput *input) u32 color = (a << 24) | (r << 16) | (g << 8) | (b << 0); bitmapBuffer[i] = color; } + + if (!cpu.isInit) + { + ASSERT(memory.permanentMemSize == (4096 / 4)); + cpu.isInit = true; + + // NOTE: Everything before 0x200 is reserved for the actual emulator + const u32 INIT_ADDRESS = 0x200; + cpu.programCounter = INIT_ADDRESS; + cpu.I = 0; + cpu.stackPointer = 0; + } + + u8 *mainMem = (u8 *)memory.permanentMem; + u8 opHighByte = mainMem[cpu.programCounter++]; + u8 opLowByte = mainMem[cpu.programCounter++]; + + u8 opFirstNibble = (opHighByte & 0xF0); + switch (opFirstNibble) + { + case 0x00: + { + // CLS - 00E0 - Clear the display + if (opLowByte == 0xE0) + { + } + // RET - 00EE - Return from subroutine + else if (opLowByte == 0xEE) + { + cpu.programCounter = cpu.stack[cpu.stackPointer--]; + } + } + break; + + case 0x10: + case 0x20: + { + u16 loc = ((0x0F & opHighByte) << 8) | (0xFF & opLowByte); + ASSERT(loc <= 0x0FFF); + + // JP addr - 1nnn - Jump to location nnn + if (opFirstNibble == 0x10) + { + // NOTE: Jump to loc, as per below + } + // Call addr - 2nnn - Call subroutine at nnn + else + { + ASSERT(opFirstNibble == 0x20); + + cpu.stackPointer++; + ASSERT(cpu.stackPointer < ARRAY_COUNT(cpu.stack)); + cpu.stack[cpu.stackPointer] = cpu.programCounter; + } + + cpu.programCounter = loc; + } + break; + + case 0x30: + case 0x40: + { + u8 regNum = (0x0F & opHighByte); + ASSERT(regNum < ARRAY_COUNT(cpu.registerArray)); + u8 valToCheck = opLowByte; + + // SE Vx, byte - 3xkk - Skip next instruction if Vx == kk + if (opFirstNibble == 0x30) + { + if (cpu.registerArray[regNum] == valToCheck) + cpu.programCounter += 2; + } + // SNE Vx, byte - 4xkk - Skip next instruction if Vx == kk + else + { + ASSERT(opFirstNibble == 0x40); + if (cpu.registerArray[regNum] != valToCheck) + cpu.programCounter += 2; + } + } + break; + + // SE Vx, Vy - 5xy0 - Skip next instruction if Vx = Vy + case 0x50: + { + u8 firstRegNum = (0x0F & opHighByte); + ASSERT(firstRegNum < ARRAY_COUNT(cpu.registerArray)); + + u8 secondRegNum = (0xF0 & opLowByte); + ASSERT(secondRegNum < ARRAY_COUNT(cpu.registerArray)); + + if (cpu.registerArray[firstRegNum] == + cpu.registerArray[secondRegNum]) + { + cpu.programCounter++; + } + } + break; + + case 0x60: + case 0x70: + { + u8 regNum = (0x0F & opHighByte); + ASSERT(regNum < ARRAY_COUNT(cpu.registerArray)); + u8 valToOperateOn = opLowByte; + + // LD Vx, byte - 6xkk - Set Vx = kk + if (opFirstNibble == 0x60) + { + cpu.registerArray[regNum] = valToOperateOn; + } + // ADD Vx, byte - 7xkk - Set Vx = Vx + kk + else + { + ASSERT(opFirstNibble == 0x70); + cpu.registerArray[regNum] += valToOperateOn; + } + + } + break; + + case 0x80: + { + u8 firstRegNum = (0x0F & opHighByte); + ASSERT(firstRegNum < ARRAY_COUNT(cpu.registerArray)); + + u8 secondRegNum = (0xF0 & opLowByte); + ASSERT(secondRegNum < ARRAY_COUNT(cpu.registerArray)); + + u8 *vx = &cpu.registerArray[firstRegNum]; + u8 *vy = &cpu.registerArray[secondRegNum]; + + // LD Vx, Vy - 8xy0 - Set Vx = Vy + if (opLowByte == 0x00) + { + *vx = *vy; + } + // OR Vx, Vy - 8xy1 - Set Vx = Vx OR Vy + else if (opLowByte == 0x01) + { + u8 result = (*vx | *vy); + *vx = result; + } + // AND Vx, Vy - 8xy2 - Set Vx = Vx AND Vy + else if (opLowByte == 0x02) + { + u8 result = (*vx & *vy); + *vx = result; + } + // XOR Vx, Vy - 8xy3 - Set Vx = Vx XOR Vy + else if (opLowByte == 0x03) + { + u8 result = (*vx & *vy); + *vx = result; + } + // ADD Vx, Vy - 8xy4 - Set Vx = Vx + Vy, set VF = carry + else if (opLowByte == 0x04) + { + u16 result = (*vx + *vy); + *vx = (u8)result; + + 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) + { + if (*vx > *vy) + cpu.VF = 1; + else + cpu.VF = 0; + + *vx -= *vy; + } + // SHR Vx {, Vy} - 8xy6 - Set Vx = Vx SHR 1 + else if (opLowByte == 0x06) + { + if (*vx & 1) + cpu.VF = 1; + else + cpu.VF = 0; + + *vx >>= 1; + } + // SUBN Vx {, Vy} - 8xy7 - Set Vx = Vy - Vx, set VF = NOT borrow + else if (opLowByte == 0x07) + { + if (*vy > *vx) + cpu.VF = 1; + else + cpu.VF = 0; + + *vx = *vy - *vx; + } + // SHL Vx {, Vy} - 8xyE - Set Vx = SHL 1 + else + { + ASSERT(opLowByte == 0x0E); + if ((*vx >> 7) == 1) + cpu.VF = 1; + else + cpu.VF = 0; + + *vx <<= 1; + } + } + break; + + // SNE Vx, Vy - 9xy0 - Skip next instruction if Vx != Vy + case 0x90: + { + u8 firstRegNum = (0x0F & opHighByte); + ASSERT(firstRegNum < ARRAY_COUNT(cpu.registerArray)); + + u8 secondRegNum = (0xF0 & opLowByte); + ASSERT(secondRegNum < ARRAY_COUNT(cpu.registerArray)); + + u8 *vx = &cpu.registerArray[firstRegNum]; + u8 *vy = &cpu.registerArray[secondRegNum]; + + if (*vx != *vy) cpu.programCounter+= 2; + } + break; + + // LD I, addr - Annn - Set I = nnn + case 0xA0: + { + u8 valToSet = opLowByte; + cpu.indexRegister = valToSet; + } + break; + + // JP V0, addr - Bnnn - Jump to location (nnn + V0) + case 0xB0: + { + u8 addr = opLowByte + cpu.V0; + cpu.programCounter = addr; + } + break; + + // RND Vx, byte - Cxkk - Set Vx = random byte AND kk + case 0xC0: + { + u8 firstRegNum = (0x0F & opHighByte); + ASSERT(firstRegNum < ARRAY_COUNT(cpu.registerArray)); + + u8 andBits = opLowByte; + u8 *vx = &cpu.registerArray[firstRegNum]; + + // TODO(doyle): Random umber + *vx = (28 & opLowByte); + } + break; + + // DRW Vx, Vy, nibble - Dxyn - Display n-byte sprite starting at mem + // location I at (Vx, Vy), set VF = collision + case 0xD0: + { + // TODO(doyle): Implement drawing + } + break; + + case 0xE0: + { + // TODO(doyle): Implement key checks + u8 checkKey = (0x0F & opHighByte); + + // SKP Vx - Ex9E - Skip next instruction if key with the value of Vx + // is pressed + bool skipNextInstruction = false; + if (opLowByte == 0x9E) + { + } + // SKNP Vx - ExA1 - Skip next instruction if key with the value of + // Vx is not pressed + else + { + ASSERT(opLowByte == 0xA1); + } + + if (skipNextInstruction) cpu.programCounter += 2; + } + break; + + case 0xF0: + { + u8 regNum = (0x0F & opHighByte); + ASSERT(regNum < ARRAY_COUNT(cpu.registerArray)); + u8 *vx = &cpu.registerArray[regNum]; + + // LD Vx, DT - Fx07 - Set Vx = delay timer value + if (opLowByte == 0x07) + { + *vx = cpu.delayTimer; + } + // LD Vx, K - Fx0A - Wait for a key press, store the value of the + // key in Vx + else if (opLowByte == 0x0A) + { + } + // LD DT, Vx - Fx15 - Set delay timer = Vx + else if (opLowByte == 0x15) + { + cpu.delayTimer = *vx; + } + // LD ST, Vx - Fx18 - Set sound timer = Vx + else if (opLowByte == 0x18) + { + cpu.soundTimer = *vx; + } + // ADD I, Vx - Fx1E - Set I = I + Vx + else if (opLowByte == 0x1E) + { + cpu.indexRegister += *vx; + } + // LD F, Vx - Fx29 - Set I = location of sprite for digit Vx + else if (opLowByte == 0x29) + { + // TODO: Implement + } + // LD B, Vx - Fx33 - Store BCD representations of Vx in memory + // locations I, I+1 and I+2 + else if (opLowByte == 0x33) + { + } + // LD [I], Vx - Fx55 - Store register V0 through Vx in memory + // starting at location I. + else if (opLowByte == 0x55) + { + for (u32 regIndex = 0; regIndex <= regNum; regIndex++) + { + u32 mem_offset = regIndex; + mainMem[cpu.indexRegister + mem_offset] = + cpu.registerArray[regIndex]; + } + } + // LD [I], Vx - Fx65 - Read registers V0 through Vx from memory + // starting at location I. + else + { + ASSERT(opLowByte == 0x65); + for (u32 regIndex = 0; regIndex <= regNum; regIndex++) + { + u32 mem_offset = regIndex; + cpu.registerArray[regIndex] = + mainMem[cpu.indexRegister + mem_offset]; + } + } + } + break; + }; } diff --git a/src/dchip8.h b/src/dchip8.h index 30365f8..488ced9 100644 --- a/src/dchip8.h +++ b/src/dchip8.h @@ -3,6 +3,7 @@ #include "dchip8_platform.h" -void dchip8_update(PlatformRenderBuffer *renderBuffer, PlatformInput *input); +void dchip8_update(PlatformRenderBuffer renderBuffer, PlatformInput input, + PlatformMemory memory); #endif diff --git a/src/dchip8_platform.h b/src/dchip8_platform.h index 5c05c6c..e42f978 100644 --- a/src/dchip8_platform.h +++ b/src/dchip8_platform.h @@ -29,6 +29,8 @@ typedef struct KeyState typedef struct PlatformInput { + f32 deltaForFrame; + union { KeyState key[key_count]; struct @@ -42,4 +44,13 @@ typedef struct PlatformInput }; } PlatformInput; +typedef struct PlatformMemory +{ + void *permanentMem; + u32 permanentMemSize; + + void *transientMem; + u32 transientMemSize; +} PlatformMemory; + #endif diff --git a/src/win32_dchip8.cpp b/src/win32_dchip8.cpp index e6caa98..64e77c1 100644 --- a/src/win32_dchip8.cpp +++ b/src/win32_dchip8.cpp @@ -140,6 +140,9 @@ FILE_SCOPE void win32_process_messages(HWND window, PlatformInput *input) int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nShowCmd) { + //////////////////////////////////////////////////////////////////////////// + // Initialise Win32 Window + //////////////////////////////////////////////////////////////////////////// WNDCLASSEX wc = { sizeof(WNDCLASSEX), @@ -224,6 +227,11 @@ int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, //////////////////////////////////////////////////////////////////////////// // Update Loop //////////////////////////////////////////////////////////////////////////// + u8 stackMemory[4096] = {}; + PlatformMemory platformMemory = {}; + platformMemory.permanentMem = &stackMemory; + platformMemory.permanentMemSize = (ARRAY_COUNT(stackMemory) / 4); + QueryPerformanceFrequency(&globalQueryPerformanceFrequency); const f32 TARGET_FRAMES_PER_S = 60.0f; f32 targetSecondsPerFrame = 1 / TARGET_FRAMES_PER_S; @@ -237,14 +245,15 @@ int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, //////////////////////////////////////////////////////////////////////// LARGE_INTEGER startFrameTime = win32_query_perf_counter_time(); { - PlatformInput input = {}; - win32_process_messages(mainWindow, &input); + PlatformInput platformInput = {}; + platformInput.deltaForFrame = frameTimeInS; + win32_process_messages(mainWindow, &platformInput); PlatformRenderBuffer platformBuffer = {}; platformBuffer.memory = renderBitmap.memory; platformBuffer.height = renderBitmap.height; platformBuffer.width = renderBitmap.width; - dchip8_update(&platformBuffer, &input); + dchip8_update(platformBuffer, platformInput, platformMemory); } //////////////////////////////////////////////////////////////////////// @@ -275,18 +284,18 @@ int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, (DWORD)((targetSecondsPerFrame - workTimeInS) * 1000); Sleep(remainingTimeInMs); } - - LARGE_INTEGER endFrameTime = win32_query_perf_counter_time(); - frameTimeInS = - win32_query_perf_counter_get_time(startFrameTime, endFrameTime); - f32 msPerFrame = 1000.0f * frameTimeInS; - - wchar_t windowTitleBuffer[128] = {}; - _snwprintf_s(windowTitleBuffer, ARRAY_COUNT(windowTitleBuffer), - ARRAY_COUNT(windowTitleBuffer), - L"dchip-8 | %5.2f ms/f", msPerFrame); - SetWindowText(mainWindow, windowTitleBuffer); } + + LARGE_INTEGER endFrameTime = win32_query_perf_counter_time(); + frameTimeInS = + win32_query_perf_counter_get_time(startFrameTime, endFrameTime); + f32 msPerFrame = 1000.0f * frameTimeInS; + + wchar_t windowTitleBuffer[128] = {}; + _snwprintf_s(windowTitleBuffer, ARRAY_COUNT(windowTitleBuffer), + ARRAY_COUNT(windowTitleBuffer), L"dchip-8 | %5.2f ms/f", + msPerFrame); + SetWindowText(mainWindow, windowTitleBuffer); } return 0;