diff --git a/src/Common.h b/src/Common.h deleted file mode 100644 index ed246ab..0000000 --- a/src/Common.h +++ /dev/null @@ -1,127 +0,0 @@ -#ifndef COMMON_H -#define COMMON_H - -#include "stdint.h" - -#define LOCAL_PERSIST static -#define FILE_SCOPE static - -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; - -#define INVALID_CODE_PATH 0 - -#define ARRAY_COUNT(array) (sizeof(array) / sizeof(array[0])) - -#ifdef DEBUG_MODE - #define ASSERT(expr) if (!(expr)) { (*((int *)0)) = 0; } -#else - #define ASSERT(expr) -#endif - -#define MATH_ABS(x) (((x) < 0) ? (-(x)) : (x)) - -typedef union v2 { - struct { f32 x, y; }; - struct { f32 w, h; }; -} v2; - -inline FILE_SCOPE v2 V2(f32 x, f32 y) -{ - v2 result = {}; - result.x = x; - result.y = y; - - return result; -} - -inline FILE_SCOPE v2 V2i(i32 x, i32 y) -{ - v2 result = V2((f32)x, (f32)y); - return result; -} - -inline FILE_SCOPE i32 common_strcmp(const char *a, const char *b) -{ - while ((*a) == (*b)) - { - if (!(*a)) return 0; - a++; - b++; - } - - return (((*a) < (*b)) ? -1 : 1); -} - -inline FILE_SCOPE i32 common_wstrcmp(const wchar_t *a, const wchar_t *b) -{ - while ((*a) == (*b)) - { - if (!(*a)) return 0; - a++; - b++; - } - - return (((*a) < (*b)) ? -1 : 1); -} - -inline FILE_SCOPE void common_wstrcat(const wchar_t *a, i32 lenA, - const wchar_t *b, i32 lenB, wchar_t *out, - i32 outLen) -{ - ASSERT((lenA + lenB) < outLen); - - i32 outIndex = 0; - for (i32 i = 0; i < lenA; i++) - out[outIndex++] = a[i]; - - for (i32 i = 0; i < lenB; i++) - out[outIndex++] = b[i]; - - ASSERT(outIndex <= outLen); -} - -inline FILE_SCOPE wchar_t common_wchar_ascii_to_lower(wchar_t character) -{ - if (character >= L'A' && character <= L'Z') - { - i32 shiftOffset = L'a' - L'A'; - character += (wchar_t)shiftOffset; - } - - return character; -} - -inline FILE_SCOPE i32 common_wstrlen(const wchar_t *a) -{ - i32 result = 0; - while ((*a)) - { - result++; - a++; - } - - return result; -} - -inline FILE_SCOPE i32 common_strlen(const char *a) -{ - i32 result = 0; - while ((*a)) - { - result++; - a++; - } - - return result; -} - - -#endif diff --git a/src/dchip8.cpp b/src/dchip8.cpp index 8c78875..1743284 100644 --- a/src/dchip8.cpp +++ b/src/dchip8.cpp @@ -1,5 +1,5 @@ -#include "common.h" #include "dchip8_platform.h" +#include "dqnt.h" typedef struct Chip8CPU { @@ -54,15 +54,13 @@ typedef struct Chip8CPU } 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); + DQNT_ASSERT(indexRegister >= 0 && indexRegister <= 0xFFF); + DQNT_ASSERT(programCounter >= 0 && programCounter <= 0xFFF); - ASSERT(renderBuffer.bytesPerPixel == 4); + DQNT_ASSERT(renderBuffer.bytesPerPixel == 4); const i32 numPixels = renderBuffer.width * renderBuffer.height; u32 *bitmapBuffer = (u32 *)renderBuffer.memory; @@ -86,7 +84,7 @@ void dchip8_update(PlatformRenderBuffer renderBuffer, PlatformInput input, if (!cpu.isInit) { - ASSERT(memory.permanentMemSize == (4096 / 4)); + DQNT_ASSERT(memory.permanentMemSize == (4096 / 4)); cpu.isInit = true; // NOTE: Everything before 0x200 is reserved for the actual emulator @@ -94,8 +92,11 @@ void dchip8_update(PlatformRenderBuffer renderBuffer, PlatformInput input, cpu.programCounter = INIT_ADDRESS; cpu.I = 0; cpu.stackPointer = 0; + + // TODO(doyle): Load rom } +#if 0 u8 *mainMem = (u8 *)memory.permanentMem; u8 opHighByte = mainMem[cpu.programCounter++]; u8 opLowByte = mainMem[cpu.programCounter++]; @@ -121,7 +122,7 @@ void dchip8_update(PlatformRenderBuffer renderBuffer, PlatformInput input, case 0x20: { u16 loc = ((0x0F & opHighByte) << 8) | (0xFF & opLowByte); - ASSERT(loc <= 0x0FFF); + DQNT_ASSERT(loc <= 0x0FFF); // JP addr - 1nnn - Jump to location nnn if (opFirstNibble == 0x10) @@ -131,10 +132,10 @@ void dchip8_update(PlatformRenderBuffer renderBuffer, PlatformInput input, // Call addr - 2nnn - Call subroutine at nnn else { - ASSERT(opFirstNibble == 0x20); + DQNT_ASSERT(opFirstNibble == 0x20); cpu.stackPointer++; - ASSERT(cpu.stackPointer < ARRAY_COUNT(cpu.stack)); + DQNT_ASSERT(cpu.stackPointer < DQNT_ARRAY_COUNT(cpu.stack)); cpu.stack[cpu.stackPointer] = cpu.programCounter; } @@ -146,7 +147,7 @@ void dchip8_update(PlatformRenderBuffer renderBuffer, PlatformInput input, case 0x40: { u8 regNum = (0x0F & opHighByte); - ASSERT(regNum < ARRAY_COUNT(cpu.registerArray)); + DQNT_ASSERT(regNum < DQNT_ARRAY_COUNT(cpu.registerArray)); u8 valToCheck = opLowByte; // SE Vx, byte - 3xkk - Skip next instruction if Vx == kk @@ -158,7 +159,7 @@ void dchip8_update(PlatformRenderBuffer renderBuffer, PlatformInput input, // SNE Vx, byte - 4xkk - Skip next instruction if Vx == kk else { - ASSERT(opFirstNibble == 0x40); + DQNT_ASSERT(opFirstNibble == 0x40); if (cpu.registerArray[regNum] != valToCheck) cpu.programCounter += 2; } @@ -169,10 +170,10 @@ void dchip8_update(PlatformRenderBuffer renderBuffer, PlatformInput input, case 0x50: { u8 firstRegNum = (0x0F & opHighByte); - ASSERT(firstRegNum < ARRAY_COUNT(cpu.registerArray)); + DQNT_ASSERT(firstRegNum < DQNT_ARRAY_COUNT(cpu.registerArray)); u8 secondRegNum = (0xF0 & opLowByte); - ASSERT(secondRegNum < ARRAY_COUNT(cpu.registerArray)); + DQNT_ASSERT(secondRegNum < DQNT_ARRAY_COUNT(cpu.registerArray)); if (cpu.registerArray[firstRegNum] == cpu.registerArray[secondRegNum]) @@ -186,7 +187,7 @@ void dchip8_update(PlatformRenderBuffer renderBuffer, PlatformInput input, case 0x70: { u8 regNum = (0x0F & opHighByte); - ASSERT(regNum < ARRAY_COUNT(cpu.registerArray)); + DQNT_ASSERT(regNum < DQNT_ARRAY_COUNT(cpu.registerArray)); u8 valToOperateOn = opLowByte; // LD Vx, byte - 6xkk - Set Vx = kk @@ -197,7 +198,7 @@ void dchip8_update(PlatformRenderBuffer renderBuffer, PlatformInput input, // ADD Vx, byte - 7xkk - Set Vx = Vx + kk else { - ASSERT(opFirstNibble == 0x70); + DQNT_ASSERT(opFirstNibble == 0x70); cpu.registerArray[regNum] += valToOperateOn; } @@ -207,10 +208,10 @@ void dchip8_update(PlatformRenderBuffer renderBuffer, PlatformInput input, case 0x80: { u8 firstRegNum = (0x0F & opHighByte); - ASSERT(firstRegNum < ARRAY_COUNT(cpu.registerArray)); + DQNT_ASSERT(firstRegNum < DQNT_ARRAY_COUNT(cpu.registerArray)); u8 secondRegNum = (0xF0 & opLowByte); - ASSERT(secondRegNum < ARRAY_COUNT(cpu.registerArray)); + DQNT_ASSERT(secondRegNum < DQNT_ARRAY_COUNT(cpu.registerArray)); u8 *vx = &cpu.registerArray[firstRegNum]; u8 *vy = &cpu.registerArray[secondRegNum]; @@ -280,7 +281,7 @@ void dchip8_update(PlatformRenderBuffer renderBuffer, PlatformInput input, // SHL Vx {, Vy} - 8xyE - Set Vx = SHL 1 else { - ASSERT(opLowByte == 0x0E); + DQNT_ASSERT(opLowByte == 0x0E); if ((*vx >> 7) == 1) cpu.VF = 1; else @@ -295,10 +296,10 @@ void dchip8_update(PlatformRenderBuffer renderBuffer, PlatformInput input, case 0x90: { u8 firstRegNum = (0x0F & opHighByte); - ASSERT(firstRegNum < ARRAY_COUNT(cpu.registerArray)); + DQNT_ASSERT(firstRegNum < DQNT_ARRAY_COUNT(cpu.registerArray)); u8 secondRegNum = (0xF0 & opLowByte); - ASSERT(secondRegNum < ARRAY_COUNT(cpu.registerArray)); + DQNT_ASSERT(secondRegNum < DQNT_ARRAY_COUNT(cpu.registerArray)); u8 *vx = &cpu.registerArray[firstRegNum]; u8 *vy = &cpu.registerArray[secondRegNum]; @@ -327,7 +328,7 @@ void dchip8_update(PlatformRenderBuffer renderBuffer, PlatformInput input, case 0xC0: { u8 firstRegNum = (0x0F & opHighByte); - ASSERT(firstRegNum < ARRAY_COUNT(cpu.registerArray)); + DQNT_ASSERT(firstRegNum < DQNT_ARRAY_COUNT(cpu.registerArray)); u8 andBits = opLowByte; u8 *vx = &cpu.registerArray[firstRegNum]; @@ -360,7 +361,7 @@ void dchip8_update(PlatformRenderBuffer renderBuffer, PlatformInput input, // Vx is not pressed else { - ASSERT(opLowByte == 0xA1); + DQNT_ASSERT(opLowByte == 0xA1); } if (skipNextInstruction) cpu.programCounter += 2; @@ -370,7 +371,7 @@ void dchip8_update(PlatformRenderBuffer renderBuffer, PlatformInput input, case 0xF0: { u8 regNum = (0x0F & opHighByte); - ASSERT(regNum < ARRAY_COUNT(cpu.registerArray)); + DQNT_ASSERT(regNum < DQNT_ARRAY_COUNT(cpu.registerArray)); u8 *vx = &cpu.registerArray[regNum]; // LD Vx, DT - Fx07 - Set Vx = delay timer value @@ -423,7 +424,7 @@ void dchip8_update(PlatformRenderBuffer renderBuffer, PlatformInput input, // starting at location I. else { - ASSERT(opLowByte == 0x65); + DQNT_ASSERT(opLowByte == 0x65); for (u32 regIndex = 0; regIndex <= regNum; regIndex++) { u32 mem_offset = regIndex; @@ -434,4 +435,5 @@ void dchip8_update(PlatformRenderBuffer renderBuffer, PlatformInput input, } break; }; +#endif } diff --git a/src/dchip8_platform.h b/src/dchip8_platform.h index e42f978..a71a5cd 100644 --- a/src/dchip8_platform.h +++ b/src/dchip8_platform.h @@ -1,7 +1,7 @@ #ifndef DCHIP_8_PLATFORM #define DCHIP_8_PLATFORM -#include "Common.h" +#include "dqnt.h" typedef struct PlatformRenderBuffer { diff --git a/src/dqnt.h b/src/dqnt.h new file mode 100644 index 0000000..7db8c1e --- /dev/null +++ b/src/dqnt.h @@ -0,0 +1,235 @@ +#ifndef DQNT_H +#define DQNT_H + +/* + #define DQNT_IMPLEMENTATION + #include "dqnt.h" + */ + +//////////////////////////////////////////////////////////////////////////////// +// General Purpose Operations +//////////////////////////////////////////////////////////////////////////////// +#include "stdint.h" +#define LOCAL_PERSIST static +#define FILE_SCOPE static + +typedef uint64_t u64; +typedef uint32_t u32; +typedef uint16_t u16; +typedef uint8_t u8; + +typedef int32_t i64; +typedef int32_t i32; +typedef int64_t i16; + +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_MATH_ABS(x) (((x) < 0) ? (-(x)) : (x)) + +//////////////////////////////////////////////////////////////////////////////// +// Vec2 +//////////////////////////////////////////////////////////////////////////////// +typedef union v2 { + struct { f32 x, y; }; + struct { f32 w, h; }; +} v2; + +v2 V2(f32 x, f32 y); +// Create a 2d-vector using ints and typecast to floats +v2 V2i(i32 x, i32 y); + +//////////////////////////////////////////////////////////////////////////////// +// String Ops +//////////////////////////////////////////////////////////////////////////////// +i32 dqnt_strcmp(const char *a, const char *b); +i32 dqnt_strlen(const char *a); + +wchar_t dqnt_wchar_ascii_to_lower(wchar_t character); +i32 dqnt_wstrcmp(const wchar_t *a, const wchar_t *b); +void dqnt_wstrcat(const wchar_t *a, i32 lenA, const wchar_t *b, i32 lenB, wchar_t *out, i32 outLen); +i32 dqnt_wstrlen(const wchar_t *a); + +//////////////////////////////////////////////////////////////////////////////// +// PCG (Permuted Congruential Generator) Random Number Generator +//////////////////////////////////////////////////////////////////////////////// +typedef struct RandPCGState +{ + u64 state[2]; +} RandPCGState; + +// Init generator with seed. The generator is not valid until it's been seeded. +void dqnt_rnd_pcg_seed (RandPCGState *pcg, u32 seed); +// Returns a random number N between [0, 0xFFFFFFFF] +u32 dqnt_rnd_pcg_next (RandPCGState *pcg); +// Returns a random integer N between [min, max] +i32 dqnt_rnd_pcg_range(RandPCGState *pcg, i32 min, i32 max); + +#endif /* DQNT_H */ + +#ifdef DQNT_IMPLEMENTATION +#undef DQNT_IMPLEMENTATION +//////////////////////////////////////////////////////////////////////////////// +// Vec2 +//////////////////////////////////////////////////////////////////////////////// +v2 V2(f32 x, f32 y) +{ + v2 result = {}; + result.x = x; + result.y = y; + + return result; +} + +v2 V2i(i32 x, i32 y) +{ + v2 result = V2((f32)x, (f32)y); + return result; +} + +//////////////////////////////////////////////////////////////////////////////// +// String Ops +//////////////////////////////////////////////////////////////////////////////// +i32 dqnt_strcmp(const char *a, const char *b) +{ + while ((*a) == (*b)) + { + if (!(*a)) return 0; + a++; + b++; + } + + return (((*a) < (*b)) ? -1 : 1); +} + +i32 dqnt_wstrcmp(const wchar_t *a, const wchar_t *b) +{ + while ((*a) == (*b)) + { + if (!(*a)) return 0; + a++; + b++; + } + + return (((*a) < (*b)) ? -1 : 1); +} + +void dqnt_wstrcat(const wchar_t *a, i32 lenA, const wchar_t *b, i32 lenB, + wchar_t *out, i32 outLen) +{ + DQNT_ASSERT((lenA + lenB) < outLen); + + i32 outIndex = 0; + for (i32 i = 0; i < lenA; i++) + out[outIndex++] = a[i]; + + for (i32 i = 0; i < lenB; i++) + out[outIndex++] = b[i]; + + DQNT_ASSERT(outIndex <= outLen); +} + +wchar_t dqnt_wchar_ascii_to_lower(wchar_t character) +{ + if (character >= L'A' && character <= L'Z') + { + i32 shiftOffset = L'a' - L'A'; + character += (wchar_t)shiftOffset; + } + + return character; +} + +i32 dqnt_wstrlen(const wchar_t *a) +{ + i32 result = 0; + while ((*a)) + { + result++; + a++; + } + + return result; +} + +i32 dqnt_strlen(const char *a) +{ + i32 result = 0; + while ((*a)) + { + result++; + a++; + } + + return result; +} + +//////////////////////////////////////////////////////////////////////////////// +// PCG (Permuted Congruential Generator) Random Number Generator +//////////////////////////////////////////////////////////////////////////////// +// Public Domain library with thanks to Mattias Gustavsson +// https://github.com/mattiasgustavsson/libs/blob/master/docs/rnd.md + +// Convert a randomized u32 value to a float value x in the range 0.0f <= x +// < 1.0f. Contributed by Jonatan Hedborg +FILE_SCOPE f32 dqnt_rnd_f32_normalized_from_u32_(u32 value) +{ + u32 exponent = 127; + u32 mantissa = value >> 9; + u32 result = (exponent << 23) | mantissa; + f32 fresult = *(f32 *)(&result); + return fresult - 1.0f; +} + +FILE_SCOPE u64 dqnt_rnd_murmur3_avalanche64_(u64 h) +{ + h ^= h >> 33; + h *= 0xff51afd7ed558ccd; + h ^= h >> 33; + h *= 0xc4ceb9fe1a85ec53; + h ^= h >> 33; + return h; +} + +void dqnt_rnd_pcg_seed(RandPCGState *pcg, u32 seed) +{ + u64 value = (((u64)seed) << 1ULL) | 1ULL; + value = dqnt_rnd_murmur3_avalanche64_(value); + pcg->state[0] = 0U; + pcg->state[1] = (value << 1ULL) | 1ULL; + dqnt_rnd_pcg_next(pcg); + pcg->state[0] += dqnt_rnd_murmur3_avalanche64_(value); + dqnt_rnd_pcg_next(pcg); +} + +u32 dqnt_rnd_pcg_next(RandPCGState *pcg) +{ + u64 oldstate = pcg->state[0]; + pcg->state[0] = oldstate * 0x5851f42d4c957f2dULL + pcg->state[1]; + u32 xorshifted = (u32)(((oldstate >> 18ULL) ^ oldstate) >> 27ULL); + u32 rot = (u32)(oldstate >> 59ULL); + return (xorshifted >> rot) | (xorshifted << ((-(i32)rot) & 31)); +} + +f32 dqnt_rnd_pcg_nextf(RandPCGState *pcg) +{ + return dqnt_rnd_f32_normalized_from_u32_(dqnt_rnd_pcg_next(pcg)); +} + +i32 dqnt_rnd_pcg_range(RandPCGState *pcg, i32 min, i32 max) +{ + i32 const range = (max - min) + 1; + if (range <= 0) return min; + i32 const value = (i32)(dqnt_rnd_pcg_nextf(pcg) * range); + return min + value; +} + +#endif /* DQNT_IMPLEMENTATION */ diff --git a/src/unity_build.cpp b/src/unity_build.cpp index 223d70b..0a805f4 100644 --- a/src/unity_build.cpp +++ b/src/unity_build.cpp @@ -1,3 +1,5 @@ -#include "win32_dchip8.cpp" +#define DQNT_IMPLEMENTATION +#include "dqnt.h" +#include "win32_dchip8.cpp" #include "dchip8.cpp" diff --git a/src/win32_dchip8.cpp b/src/win32_dchip8.cpp index 64e77c1..a6ce041 100644 --- a/src/win32_dchip8.cpp +++ b/src/win32_dchip8.cpp @@ -9,9 +9,9 @@ #include #include -#include "common.h" #include "dchip8.h" #include "dchip8_platform.h" +#include "dqnt.h" FILE_SCOPE bool globalRunning = false; FILE_SCOPE LARGE_INTEGER globalQueryPerformanceFrequency; @@ -209,7 +209,7 @@ int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, renderBitmap.width = header.biWidth; renderBitmap.height = header.biHeight; renderBitmap.bytesPerPixel = header.biBitCount / 8; - ASSERT(renderBitmap.bytesPerPixel >= 1); + DQNT_ASSERT(renderBitmap.bytesPerPixel >= 1); HDC deviceContext = GetDC(mainWindow); renderBitmap.handle = @@ -230,7 +230,7 @@ int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, u8 stackMemory[4096] = {}; PlatformMemory platformMemory = {}; platformMemory.permanentMem = &stackMemory; - platformMemory.permanentMemSize = (ARRAY_COUNT(stackMemory) / 4); + platformMemory.permanentMemSize = (DQNT_ARRAY_COUNT(stackMemory) / 4); QueryPerformanceFrequency(&globalQueryPerformanceFrequency); const f32 TARGET_FRAMES_PER_S = 60.0f; @@ -292,9 +292,9 @@ int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, 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); + _snwprintf_s(windowTitleBuffer, DQNT_ARRAY_COUNT(windowTitleBuffer), + DQNT_ARRAY_COUNT(windowTitleBuffer), + L"dchip-8 | %5.2f ms/f", msPerFrame); SetWindowText(mainWindow, windowTitleBuffer); }