diff --git a/dchip-8.vcxproj b/dchip-8.vcxproj index 169c81f..0772fc9 100644 --- a/dchip-8.vcxproj +++ b/dchip-8.vcxproj @@ -73,7 +73,7 @@ $(SolutionDir)\bin\ - $(SolutionDir)\bin\ + $(SolutionDir) diff --git a/src/dchip8.cpp b/src/dchip8.cpp index 1743284..e131dbf 100644 --- a/src/dchip8.cpp +++ b/src/dchip8.cpp @@ -1,8 +1,23 @@ +#ifndef _CRT_SECURE_NO_WARNINGS + #define _CRT_SECURE_NO_WARNINGS +#endif + #include "dchip8_platform.h" #include "dqnt.h" +#include "stdio.h" + +enum Chip8State +{ + chip8state_init, + chip8state_load_file, + chip8state_running, + chip8state_off, +}; typedef struct Chip8CPU { + const u16 INIT_ADDRESS = 0x200; + union { u8 registerArray[16]; struct @@ -50,10 +65,12 @@ typedef struct Chip8CPU u8 stackPointer; u16 stack[16]; - bool isInit; + enum Chip8State state; } Chip8CPU; -FILE_SCOPE Chip8CPU cpu; +FILE_SCOPE Chip8CPU cpu; +FILE_SCOPE RandPCGState pcgState; + void dchip8_update(PlatformRenderBuffer renderBuffer, PlatformInput input, PlatformMemory memory) { @@ -82,358 +99,383 @@ void dchip8_update(PlatformRenderBuffer renderBuffer, PlatformInput input, bitmapBuffer[i] = color; } - if (!cpu.isInit) + u8 *mainMem = (u8 *)memory.permanentMem; + if (cpu.state == chip8state_init) { DQNT_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.programCounter = cpu.INIT_ADDRESS; cpu.I = 0; cpu.stackPointer = 0; - // TODO(doyle): Load rom + const u32 SEED = 0x8293A8DE; + dqnt_rnd_pcg_seed(&pcgState, SEED); + + cpu.state = chip8state_load_file; } -#if 0 - u8 *mainMem = (u8 *)memory.permanentMem; - u8 opHighByte = mainMem[cpu.programCounter++]; - u8 opLowByte = mainMem[cpu.programCounter++]; - - u8 opFirstNibble = (opHighByte & 0xF0); - switch (opFirstNibble) + if (cpu.state == chip8state_load_file) { - case 0x00: + PlatformFile file = {}; + if (platform_open_file(L"roms/PONG", &file)) { - // CLS - 00E0 - Clear the display - if (opLowByte == 0xE0) - { - } - // RET - 00EE - Return from subroutine - else if (opLowByte == 0xEE) - { - cpu.programCounter = cpu.stack[cpu.stackPointer--]; - } - } - break; + DQNT_ASSERT((cpu.INIT_ADDRESS + file.size) <= + DQNT_ARRAY_COUNT(mainMem)); - case 0x10: - case 0x20: - { - u16 loc = ((0x0F & opHighByte) << 8) | (0xFF & opLowByte); - DQNT_ASSERT(loc <= 0x0FFF); - - // JP addr - 1nnn - Jump to location nnn - if (opFirstNibble == 0x10) + void *loadToAddr = (void *)(&mainMem[cpu.INIT_ADDRESS]); + if (platform_read_file(file, loadToAddr, (u32)file.size)) { - // NOTE: Jump to loc, as per below + cpu.state = chip8state_running; } - // Call addr - 2nnn - Call subroutine at nnn else { - DQNT_ASSERT(opFirstNibble == 0x20); - - cpu.stackPointer++; - DQNT_ASSERT(cpu.stackPointer < DQNT_ARRAY_COUNT(cpu.stack)); - cpu.stack[cpu.stackPointer] = cpu.programCounter; + cpu.state = chip8state_off; } - cpu.programCounter = loc; + platform_close_file(&file); } - break; + } - case 0x30: - case 0x40: + if (cpu.state == chip8state_running) + { + u8 opHighByte = mainMem[cpu.programCounter++]; + u8 opLowByte = mainMem[cpu.programCounter++]; + + u8 opFirstNibble = (opHighByte & 0xF0); + switch (opFirstNibble) { - u8 regNum = (0x0F & opHighByte); - DQNT_ASSERT(regNum < DQNT_ARRAY_COUNT(cpu.registerArray)); - u8 valToCheck = opLowByte; - - // SE Vx, byte - 3xkk - Skip next instruction if Vx == kk - if (opFirstNibble == 0x30) + case 0x00: { - if (cpu.registerArray[regNum] == valToCheck) - cpu.programCounter += 2; - } - // SNE Vx, byte - 4xkk - Skip next instruction if Vx == kk - else - { - DQNT_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); - DQNT_ASSERT(firstRegNum < DQNT_ARRAY_COUNT(cpu.registerArray)); - - u8 secondRegNum = (0xF0 & opLowByte); - DQNT_ASSERT(secondRegNum < DQNT_ARRAY_COUNT(cpu.registerArray)); - - if (cpu.registerArray[firstRegNum] == - cpu.registerArray[secondRegNum]) - { - cpu.programCounter++; - } - } - break; - - case 0x60: - case 0x70: - { - u8 regNum = (0x0F & opHighByte); - DQNT_ASSERT(regNum < DQNT_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 - { - DQNT_ASSERT(opFirstNibble == 0x70); - cpu.registerArray[regNum] += valToOperateOn; - } - - } - break; - - case 0x80: - { - u8 firstRegNum = (0x0F & opHighByte); - DQNT_ASSERT(firstRegNum < DQNT_ARRAY_COUNT(cpu.registerArray)); - - u8 secondRegNum = (0xF0 & opLowByte); - DQNT_ASSERT(secondRegNum < DQNT_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 - { - DQNT_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); - DQNT_ASSERT(firstRegNum < DQNT_ARRAY_COUNT(cpu.registerArray)); - - u8 secondRegNum = (0xF0 & opLowByte); - DQNT_ASSERT(secondRegNum < DQNT_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); - DQNT_ASSERT(firstRegNum < DQNT_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 - { - DQNT_ASSERT(opLowByte == 0xA1); - } - - if (skipNextInstruction) cpu.programCounter += 2; - } - break; - - case 0xF0: - { - u8 regNum = (0x0F & opHighByte); - DQNT_ASSERT(regNum < DQNT_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++) + // CLS - 00E0 - Clear the display + if (opLowByte == 0xE0) { - u32 mem_offset = regIndex; - mainMem[cpu.indexRegister + mem_offset] = - cpu.registerArray[regIndex]; + } + // RET - 00EE - Return from subroutine + else if (opLowByte == 0xEE) + { + cpu.programCounter = cpu.stack[cpu.stackPointer--]; } } - // LD [I], Vx - Fx65 - Read registers V0 through Vx from memory - // starting at location I. - else + break; + + case 0x10: + case 0x20: { - DQNT_ASSERT(opLowByte == 0x65); - for (u32 regIndex = 0; regIndex <= regNum; regIndex++) + u16 loc = ((0x0F & opHighByte) << 8) | (0xFF & opLowByte); + DQNT_ASSERT(loc <= 0x0FFF); + + // JP addr - 1nnn - Jump to location nnn + if (opFirstNibble == 0x10) { - u32 mem_offset = regIndex; - cpu.registerArray[regIndex] = - mainMem[cpu.indexRegister + mem_offset]; + // NOTE: Jump to loc, as per below + } + // Call addr - 2nnn - Call subroutine at nnn + else + { + DQNT_ASSERT(opFirstNibble == 0x20); + + cpu.stackPointer++; + DQNT_ASSERT(cpu.stackPointer < DQNT_ARRAY_COUNT(cpu.stack)); + cpu.stack[cpu.stackPointer] = cpu.programCounter; + } + + cpu.programCounter = loc; + } + break; + + case 0x30: + case 0x40: + { + u8 regNum = (0x0F & opHighByte); + DQNT_ASSERT(regNum < DQNT_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 + { + DQNT_ASSERT(opFirstNibble == 0x40); + if (cpu.registerArray[regNum] != valToCheck) + cpu.programCounter += 2; } } - } - break; - }; -#endif + break; + + // SE Vx, Vy - 5xy0 - Skip next instruction if Vx = Vy + case 0x50: + { + u8 firstRegNum = (0x0F & opHighByte); + DQNT_ASSERT(firstRegNum < DQNT_ARRAY_COUNT(cpu.registerArray)); + + u8 secondRegNum = (0xF0 & opLowByte); + DQNT_ASSERT(secondRegNum < DQNT_ARRAY_COUNT(cpu.registerArray)); + + if (cpu.registerArray[firstRegNum] == + cpu.registerArray[secondRegNum]) + { + cpu.programCounter++; + } + } + break; + + case 0x60: + case 0x70: + { + u8 regNum = (0x0F & opHighByte); + DQNT_ASSERT(regNum < DQNT_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 + { + DQNT_ASSERT(opFirstNibble == 0x70); + cpu.registerArray[regNum] += valToOperateOn; + } + } + break; + + case 0x80: + { + u8 firstRegNum = (0x0F & opHighByte); + DQNT_ASSERT(firstRegNum < DQNT_ARRAY_COUNT(cpu.registerArray)); + + u8 secondRegNum = (0xF0 & opLowByte); + DQNT_ASSERT(secondRegNum < DQNT_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 + { + DQNT_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); + DQNT_ASSERT(firstRegNum < DQNT_ARRAY_COUNT(cpu.registerArray)); + + u8 secondRegNum = (0xF0 & opLowByte); + DQNT_ASSERT(secondRegNum < DQNT_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); + DQNT_ASSERT(firstRegNum < DQNT_ARRAY_COUNT(cpu.registerArray)); + + u8 andBits = opLowByte; + u8 *vx = &cpu.registerArray[firstRegNum]; + + u8 randNum = (u8)dqnt_rnd_pcg_range(&pcgState, 0, 255); + *vx = (randNum & 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 + { + DQNT_ASSERT(opLowByte == 0xA1); + } + + if (skipNextInstruction) cpu.programCounter += 2; + } + break; + + case 0xF0: + { + u8 regNum = (0x0F & opHighByte); + DQNT_ASSERT(regNum < DQNT_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 + { + DQNT_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_platform.h b/src/dchip8_platform.h index a71a5cd..1908822 100644 --- a/src/dchip8_platform.h +++ b/src/dchip8_platform.h @@ -53,4 +53,16 @@ typedef struct PlatformMemory u32 transientMemSize; } PlatformMemory; +typedef struct PlatformFile +{ + void *handle; + u64 size; +} PlatformFile; + +// Return true if successful, false if not +bool platform_open_file (const wchar_t *const file, PlatformFile *platformFile); +// Return the number of bytes read +u32 platform_read_file (PlatformFile file, void *buffer, u32 numBytesToRead); +void platform_close_file(PlatformFile *file); + #endif diff --git a/src/dqnt.h b/src/dqnt.h index 7db8c1e..8414e99 100644 --- a/src/dqnt.h +++ b/src/dqnt.h @@ -50,8 +50,17 @@ v2 V2i(i32 x, i32 y); //////////////////////////////////////////////////////////////////////////////// // String Ops //////////////////////////////////////////////////////////////////////////////// -i32 dqnt_strcmp(const char *a, const char *b); -i32 dqnt_strlen(const char *a); +bool dqnt_char_is_digit (char c); +bool dqnt_char_is_alpha (char c); +bool dqnt_char_is_alphanum(char c); + +i32 dqnt_strcmp (const char *a, const char *b); +// Returns the length without the null terminator +i32 dqnt_strlen (const char *a); +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); wchar_t dqnt_wchar_ascii_to_lower(wchar_t character); i32 dqnt_wstrcmp(const wchar_t *a, const wchar_t *b); @@ -70,6 +79,8 @@ typedef struct RandPCGState 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 float N between [0.0, 1.0f] +f32 dqnt_rnd_pcg_nextf(RandPCGState *pcg); // Returns a random integer N between [min, max] i32 dqnt_rnd_pcg_range(RandPCGState *pcg, i32 min, i32 max); @@ -98,6 +109,24 @@ v2 V2i(i32 x, i32 y) //////////////////////////////////////////////////////////////////////////////// // String Ops //////////////////////////////////////////////////////////////////////////////// +bool dqnt_char_is_digit(char c) +{ + if (c >= '0' && c <= '9') return true; + return false; +} + +bool dqnt_char_is_alpha(char c) +{ + if ((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z')) return true; + return false; +} + +bool dqnt_char_is_alphanum(char c) +{ + if (dqnt_char_is_alpha(c) || dqnt_char_is_digit(c)) return true; + return false; +} + i32 dqnt_strcmp(const char *a, const char *b) { while ((*a) == (*b)) @@ -110,6 +139,86 @@ i32 dqnt_strcmp(const char *a, const char *b) return (((*a) < (*b)) ? -1 : 1); } +i32 dqnt_strlen(const char *a) +{ + i32 result = 0; + while (a[result]) result++; + + return result; +} + +char *dqnt_strncpy(char *dest, const char *src, i32 numChars) +{ + if (!dest) return NULL; + if (!src) return dest; + + for (i32 i = 0; i < numChars; i++) + dest[i] = src[i]; + + return dest; +} + +bool dqnt_str_reverse(char *const buf, const i32 bufSize) +{ + if (!buf) return false; + i32 mid = bufSize / 2; + + for (i32 i = 0; i < mid; i++) + { + char tmp = buf[i]; + buf[i] = buf[(bufSize - 1) - i]; + buf[(bufSize - 1) - i] = tmp; + } + + return true; +} + +i32 dqnt_str_to_i32(char *const buf, const i32 bufSize) +{ + if (!buf || bufSize == 0) return 0; + + i32 index = 0; + bool isNegative = false; + if (buf[index] == '-' || buf[index] == '+') + { + if (buf[index] == '-') isNegative = true; + index++; + } + else if (!dqnt_char_is_digit(buf[index])) + { + return 0; + } + + i32 result = 0; + for (i32 i = index; i < bufSize; i++) + { + if (dqnt_char_is_digit(buf[i])) + { + result *= 10; + result += (buf[i] - '0'); + } + else + { + break; + } + } + + if (isNegative) result *= -1; + + return result; +} + +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_wstrcmp(const wchar_t *a, const wchar_t *b) { while ((*a) == (*b)) @@ -137,17 +246,6 @@ void dqnt_wstrcat(const wchar_t *a, i32 lenA, const wchar_t *b, i32 lenB, 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; @@ -160,18 +258,6 @@ i32 dqnt_wstrlen(const wchar_t *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 //////////////////////////////////////////////////////////////////////////////// @@ -180,7 +266,7 @@ i32 dqnt_strlen(const char *a) // 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) +FILE_SCOPE f32 dqnt_rnd_f32_normalized_from_u32_internal(u32 value) { u32 exponent = 127; u32 mantissa = value >> 9; @@ -189,7 +275,7 @@ FILE_SCOPE f32 dqnt_rnd_f32_normalized_from_u32_(u32 value) return fresult - 1.0f; } -FILE_SCOPE u64 dqnt_rnd_murmur3_avalanche64_(u64 h) +FILE_SCOPE u64 dqnt_rnd_murmur3_avalanche64_internal(u64 h) { h ^= h >> 33; h *= 0xff51afd7ed558ccd; @@ -202,11 +288,11 @@ FILE_SCOPE u64 dqnt_rnd_murmur3_avalanche64_(u64 h) void dqnt_rnd_pcg_seed(RandPCGState *pcg, u32 seed) { u64 value = (((u64)seed) << 1ULL) | 1ULL; - value = dqnt_rnd_murmur3_avalanche64_(value); + value = dqnt_rnd_murmur3_avalanche64_internal(value); pcg->state[0] = 0U; pcg->state[1] = (value << 1ULL) | 1ULL; dqnt_rnd_pcg_next(pcg); - pcg->state[0] += dqnt_rnd_murmur3_avalanche64_(value); + pcg->state[0] += dqnt_rnd_murmur3_avalanche64_internal(value); dqnt_rnd_pcg_next(pcg); } @@ -221,7 +307,7 @@ u32 dqnt_rnd_pcg_next(RandPCGState *pcg) f32 dqnt_rnd_pcg_nextf(RandPCGState *pcg) { - return dqnt_rnd_f32_normalized_from_u32_(dqnt_rnd_pcg_next(pcg)); + return dqnt_rnd_f32_normalized_from_u32_internal(dqnt_rnd_pcg_next(pcg)); } i32 dqnt_rnd_pcg_range(RandPCGState *pcg, i32 min, i32 max) diff --git a/src/unity_build.cpp b/src/unity_build.cpp index 0a805f4..256a00d 100644 --- a/src/unity_build.cpp +++ b/src/unity_build.cpp @@ -1,3 +1,5 @@ +#define _CRT_SECURE_NO_WARNINGS + #define DQNT_IMPLEMENTATION #include "dqnt.h" diff --git a/src/win32_dchip8.cpp b/src/win32_dchip8.cpp index a6ce041..65e4733 100644 --- a/src/win32_dchip8.cpp +++ b/src/win32_dchip8.cpp @@ -300,3 +300,55 @@ int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, return 0; } + +void platform_close_file(PlatformFile *file) +{ + CloseHandle(file->handle); + file->handle = NULL; + file->size = 0; +} + +u32 platform_read_file(PlatformFile file, void *buffer, u32 numBytesToRead) +{ + DWORD numBytesRead = 0; + if (file.handle && buffer) + { + HANDLE win32Handle = file.handle; + BOOL result = + ReadFile(win32Handle, buffer, numBytesToRead, &numBytesRead, NULL); + + // TODO(doyle): 0 also means it is completing async, but still valid + if (result == 0) + { + win32_error_box(L"ReadFile() failed.", nullptr); + } + + } + + return numBytesRead; +} + +FILE_SCOPE u32 buffer[15000]; +bool platform_open_file(const wchar_t *const file, PlatformFile *platformFile) +{ + HANDLE handle = CreateFile(file, GENERIC_READ | GENERIC_WRITE, 0, NULL, + OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); + + if (handle == INVALID_HANDLE_VALUE) + { + win32_error_box(L"CreateFile() failed.", nullptr); + return false; + } + + LARGE_INTEGER size; + if (GetFileSizeEx(handle, &size) == 0) + { + win32_error_box(L"GetFileSizeEx() failed.", nullptr); + return false; + } + + platformFile->handle = handle; + platformFile->size = size.QuadPart; + + return true; +}