Fix timer running rate, add controller mapping
This commit is contained in:
parent
f96394dde0
commit
77346c3674
110
src/dchip8.cpp
110
src/dchip8.cpp
@ -15,6 +15,11 @@ enum Chip8State
|
|||||||
chip8state_off,
|
chip8state_off,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
typedef struct Chip8Controller
|
||||||
|
{
|
||||||
|
bool key[0x10];
|
||||||
|
} Chip8Controller;
|
||||||
|
|
||||||
typedef struct Chip8CPU
|
typedef struct Chip8CPU
|
||||||
{
|
{
|
||||||
const u16 INIT_ADDRESS = 0x200;
|
const u16 INIT_ADDRESS = 0x200;
|
||||||
@ -66,7 +71,9 @@ typedef struct Chip8CPU
|
|||||||
u8 stackPointer;
|
u8 stackPointer;
|
||||||
u16 stack[16];
|
u16 stack[16];
|
||||||
|
|
||||||
u8 keyToStoreToRegisterIndex;
|
// Metadata
|
||||||
|
u8 storeKeyToRegisterIndex;
|
||||||
|
f32 elapsedTime;
|
||||||
enum Chip8State state;
|
enum Chip8State state;
|
||||||
} Chip8CPU;
|
} Chip8CPU;
|
||||||
|
|
||||||
@ -267,6 +274,45 @@ FILE_SCOPE void dchip8_init_display(PlatformRenderBuffer renderBuffer)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
FILE_SCOPE Chip8Controller dchip8_controller_map_input(PlatformInput input)
|
||||||
|
{
|
||||||
|
// NOTE: Chip8 Hex Controller to Keyboard Mapping
|
||||||
|
// Keypad Keyboard
|
||||||
|
// +-+-+-+-+ +-+-+-+-+
|
||||||
|
// |1|2|3|C| |1|2|3|4|
|
||||||
|
// +-+-+-+-+ +-+-+-+-+
|
||||||
|
// |4|5|6|D| |Q|W|E|R|
|
||||||
|
// +-+-+-+-+ => +-+-+-+-+
|
||||||
|
// |7|8|9|E| |A|S|D|F|
|
||||||
|
// +-+-+-+-+ +-+-+-+-+
|
||||||
|
// |A|0|B|F| |Z|X|C|V|
|
||||||
|
// +-+-+-+-+ +-+-+-+-+
|
||||||
|
|
||||||
|
Chip8Controller result = {};
|
||||||
|
|
||||||
|
result.key[0x01] = input.key_1.isDown;
|
||||||
|
result.key[0x02] = input.key_2.isDown;
|
||||||
|
result.key[0x03] = input.key_3.isDown;
|
||||||
|
result.key[0x0C] = input.key_4.isDown;
|
||||||
|
|
||||||
|
result.key[0x04] = input.key_q.isDown;
|
||||||
|
result.key[0x05] = input.key_w.isDown;
|
||||||
|
result.key[0x06] = input.key_e.isDown;
|
||||||
|
result.key[0x0D] = input.key_r.isDown;
|
||||||
|
|
||||||
|
result.key[0x07] = input.key_a.isDown;
|
||||||
|
result.key[0x08] = input.key_s.isDown;
|
||||||
|
result.key[0x09] = input.key_d.isDown;
|
||||||
|
result.key[0x0E] = input.key_f.isDown;
|
||||||
|
|
||||||
|
result.key[0x0A] = input.key_z.isDown;
|
||||||
|
result.key[0x00] = input.key_x.isDown;
|
||||||
|
result.key[0x0B] = input.key_c.isDown;
|
||||||
|
result.key[0x0F] = input.key_v.isDown;
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
void dchip8_update(PlatformRenderBuffer renderBuffer, PlatformInput input,
|
void dchip8_update(PlatformRenderBuffer renderBuffer, PlatformInput input,
|
||||||
PlatformMemory memory)
|
PlatformMemory memory)
|
||||||
{
|
{
|
||||||
@ -275,7 +321,8 @@ void dchip8_update(PlatformRenderBuffer renderBuffer, PlatformInput input,
|
|||||||
DQNT_ASSERT(renderBuffer.bytesPerPixel == 4);
|
DQNT_ASSERT(renderBuffer.bytesPerPixel == 4);
|
||||||
DQNT_ASSERT(memory.permanentMemSize == 4096);
|
DQNT_ASSERT(memory.permanentMemSize == 4096);
|
||||||
|
|
||||||
u8 *mainMem = (u8 *)memory.permanentMem;
|
u8 *mainMem = (u8 *)memory.permanentMem;
|
||||||
|
Chip8Controller controller = dchip8_controller_map_input(input);
|
||||||
if (cpu.state == chip8state_init)
|
if (cpu.state == chip8state_init)
|
||||||
{
|
{
|
||||||
dchip8_init_cpu(&cpu);
|
dchip8_init_cpu(&cpu);
|
||||||
@ -283,7 +330,6 @@ void dchip8_update(PlatformRenderBuffer renderBuffer, PlatformInput input,
|
|||||||
dchip8_init_display(renderBuffer);
|
dchip8_init_display(renderBuffer);
|
||||||
}
|
}
|
||||||
|
|
||||||
#if 1
|
|
||||||
if (cpu.state == chip8state_load_file)
|
if (cpu.state == chip8state_load_file)
|
||||||
{
|
{
|
||||||
PlatformFile file = {};
|
PlatformFile file = {};
|
||||||
@ -305,31 +351,27 @@ void dchip8_update(PlatformRenderBuffer renderBuffer, PlatformInput input,
|
|||||||
platform_close_file(&file);
|
platform_close_file(&file);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
#endif
|
|
||||||
|
|
||||||
if (cpu.state == chip8state_await_input)
|
if (cpu.state == chip8state_await_input)
|
||||||
{
|
{
|
||||||
const u32 OFFSET_TO_CHIP_CONTROLS = key_7;
|
for (i32 keyVal = 0; keyVal < DQNT_ARRAY_COUNT(controller.key); keyVal++)
|
||||||
for (i32 i = OFFSET_TO_CHIP_CONTROLS; i < DQNT_ARRAY_COUNT(input.key);
|
|
||||||
i++)
|
|
||||||
{
|
{
|
||||||
KeyState checkKey = input.key[i];
|
if (controller.key[keyVal])
|
||||||
if (checkKey.isDown)
|
|
||||||
{
|
{
|
||||||
u8 regIndex = cpu.keyToStoreToRegisterIndex;
|
u8 regIndex = cpu.storeKeyToRegisterIndex;
|
||||||
u8 keyVal = (u8)i - OFFSET_TO_CHIP_CONTROLS;
|
DQNT_ASSERT(keyVal >= 0 && keyVal < 0x0F);
|
||||||
DQNT_ASSERT(keyVal >= 0 && keyVal <= 0x0F);
|
|
||||||
|
|
||||||
cpu.registerArray[regIndex] = keyVal;
|
cpu.registerArray[regIndex] = (u8)keyVal;
|
||||||
cpu.state = chip8state_running;
|
cpu.state = chip8state_running;
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (cpu.state == chip8state_running)
|
if (cpu.state == chip8state_running)
|
||||||
{
|
{
|
||||||
u8 opHighByte = mainMem[cpu.programCounter++];
|
u8 opHighByte = mainMem[cpu.programCounter++];
|
||||||
u8 opLowByte = mainMem[cpu.programCounter++];
|
u8 opLowByte = mainMem[cpu.programCounter++];
|
||||||
|
|
||||||
u8 opFirstNibble = (opHighByte & 0xF0);
|
u8 opFirstNibble = (opHighByte & 0xF0);
|
||||||
switch (opFirstNibble)
|
switch (opFirstNibble)
|
||||||
@ -655,26 +697,21 @@ void dchip8_update(PlatformRenderBuffer renderBuffer, PlatformInput input,
|
|||||||
DQNT_ASSERT(regNum < DQNT_ARRAY_COUNT(cpu.registerArray));
|
DQNT_ASSERT(regNum < DQNT_ARRAY_COUNT(cpu.registerArray));
|
||||||
u8 vx = cpu.registerArray[regNum];
|
u8 vx = cpu.registerArray[regNum];
|
||||||
DQNT_ASSERT(vx >= 0 && vx <= 0x0F);
|
DQNT_ASSERT(vx >= 0 && vx <= 0x0F);
|
||||||
|
DQNT_ASSERT(vx < DQNT_ARRAY_COUNT(controller.key));
|
||||||
|
|
||||||
const u32 KEY_OFFSET_TO_START_CONTROLS = key_7;
|
|
||||||
DQNT_ASSERT((KEY_OFFSET_TO_START_CONTROLS + vx) <
|
|
||||||
DQNT_ARRAY_COUNT(input.key));
|
|
||||||
|
|
||||||
KeyState checkKey =
|
|
||||||
input.key[KEY_OFFSET_TO_START_CONTROLS + vx];
|
|
||||||
bool skipNextInstruction = false;
|
bool skipNextInstruction = false;
|
||||||
// SKP Vx - Ex9E - Skip next instruction if key with the value
|
// SKP Vx - Ex9E - Skip next instruction if key with the value
|
||||||
// of Vx is pressed
|
// of Vx is pressed
|
||||||
if (opLowByte == 0x9E)
|
if (opLowByte == 0x9E)
|
||||||
{
|
{
|
||||||
skipNextInstruction = checkKey.isDown;
|
skipNextInstruction = controller.key[vx];
|
||||||
}
|
}
|
||||||
// SKNP Vx - ExA1 - Skip next instruction if key with the value
|
// SKNP Vx - ExA1 - Skip next instruction if key with the value
|
||||||
// of Vx is not pressed
|
// of Vx is not pressed
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
DQNT_ASSERT(opLowByte == 0xA1);
|
DQNT_ASSERT(opLowByte == 0xA1);
|
||||||
skipNextInstruction = !checkKey.isDown;
|
skipNextInstruction = !controller.key[vx];
|
||||||
}
|
}
|
||||||
|
|
||||||
if (skipNextInstruction) cpu.programCounter += 2;
|
if (skipNextInstruction) cpu.programCounter += 2;
|
||||||
@ -696,8 +733,8 @@ void dchip8_update(PlatformRenderBuffer renderBuffer, PlatformInput input,
|
|||||||
// the key in Vx
|
// the key in Vx
|
||||||
else if (opLowByte == 0x0A)
|
else if (opLowByte == 0x0A)
|
||||||
{
|
{
|
||||||
cpu.state = chip8state_await_input;
|
cpu.state = chip8state_await_input;
|
||||||
cpu.keyToStoreToRegisterIndex = regNum;
|
cpu.storeKeyToRegisterIndex = regNum;
|
||||||
}
|
}
|
||||||
// LD DT, Vx - Fx15 - Set delay timer = Vx
|
// LD DT, Vx - Fx15 - Set delay timer = Vx
|
||||||
else if (opLowByte == 0x15)
|
else if (opLowByte == 0x15)
|
||||||
@ -770,10 +807,27 @@ void dchip8_update(PlatformRenderBuffer renderBuffer, PlatformInput input,
|
|||||||
break;
|
break;
|
||||||
};
|
};
|
||||||
|
|
||||||
if (cpu.delayTimer > 0) cpu.delayTimer--;
|
// IMPORTANT: Timers need to be decremented at a rate of 60hz. Since we
|
||||||
|
// can run the interpreter faster than that, make sure we decrement
|
||||||
|
// timers at the fixed rate.
|
||||||
|
if (cpu.delayTimer > 0 || cpu.soundTimer > 0)
|
||||||
|
{
|
||||||
|
cpu.elapsedTime += input.deltaForFrame;
|
||||||
|
f32 TIMER_DECREMENT_INTERVAL = 1 / 60.0f;
|
||||||
|
|
||||||
// TODO(doyle): This needs to play a buzzing sound whilst timer > 0
|
if (cpu.elapsedTime >= TIMER_DECREMENT_INTERVAL)
|
||||||
if (cpu.soundTimer > 0) cpu.soundTimer--;
|
{
|
||||||
|
cpu.elapsedTime = 0;
|
||||||
|
if (cpu.delayTimer > 0) cpu.delayTimer--;
|
||||||
|
// TODO(doyle): This needs to play a buzzing sound whilst timer
|
||||||
|
// > 0
|
||||||
|
if (cpu.soundTimer > 0) cpu.soundTimer--;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
cpu.elapsedTime = 0;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -21,25 +21,25 @@ enum Key
|
|||||||
key_right,
|
key_right,
|
||||||
key_escape,
|
key_escape,
|
||||||
|
|
||||||
key_7,
|
key_1,
|
||||||
key_8,
|
key_2,
|
||||||
key_9,
|
key_3,
|
||||||
key_0,
|
key_4,
|
||||||
|
|
||||||
key_U,
|
key_q,
|
||||||
key_I,
|
key_w,
|
||||||
key_O,
|
key_e,
|
||||||
key_P,
|
key_r,
|
||||||
|
|
||||||
key_J,
|
key_a,
|
||||||
key_K,
|
key_s,
|
||||||
key_L,
|
key_d,
|
||||||
key_colon,
|
key_f,
|
||||||
|
|
||||||
key_M,
|
key_z,
|
||||||
key_comma,
|
key_x,
|
||||||
key_dot,
|
key_c,
|
||||||
key_forward_slash,
|
key_v,
|
||||||
|
|
||||||
key_count,
|
key_count,
|
||||||
};
|
};
|
||||||
@ -64,25 +64,25 @@ typedef struct PlatformInput
|
|||||||
KeyState right;
|
KeyState right;
|
||||||
KeyState escape;
|
KeyState escape;
|
||||||
|
|
||||||
KeyState key_7;
|
KeyState key_1;
|
||||||
KeyState key_8;
|
KeyState key_2;
|
||||||
KeyState key_9;
|
KeyState key_3;
|
||||||
KeyState key_0;
|
KeyState key_4;
|
||||||
|
|
||||||
KeyState key_U;
|
KeyState key_q;
|
||||||
KeyState key_I;
|
KeyState key_w;
|
||||||
KeyState key_O;
|
KeyState key_e;
|
||||||
KeyState key_P;
|
KeyState key_r;
|
||||||
|
|
||||||
KeyState key_J;
|
KeyState key_a;
|
||||||
KeyState key_K;
|
KeyState key_s;
|
||||||
KeyState key_L;
|
KeyState key_d;
|
||||||
KeyState key_colon;
|
KeyState key_f;
|
||||||
|
|
||||||
KeyState key_M;
|
KeyState key_z;
|
||||||
KeyState key_comma;
|
KeyState key_x;
|
||||||
KeyState key_dot;
|
KeyState key_c;
|
||||||
KeyState key_forward_slash;
|
KeyState key_v;
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
} PlatformInput;
|
} PlatformInput;
|
||||||
|
@ -117,7 +117,7 @@ inline FILE_SCOPE LARGE_INTEGER win32_query_perf_counter_time()
|
|||||||
|
|
||||||
FILE_SCOPE inline void win32_parse_key_msg(KeyState *key, MSG msg)
|
FILE_SCOPE inline void win32_parse_key_msg(KeyState *key, MSG msg)
|
||||||
{
|
{
|
||||||
WPARAM lParam = msg.lParam;
|
LPARAM lParam = msg.lParam;
|
||||||
bool keyIsDown = ((lParam >> 30) & 1);
|
bool keyIsDown = ((lParam >> 30) & 1);
|
||||||
bool keyTransitioned = ((lParam >> 31) & 1);
|
bool keyTransitioned = ((lParam >> 31) & 1);
|
||||||
|
|
||||||
@ -144,25 +144,25 @@ FILE_SCOPE void win32_process_messages(HWND window, PlatformInput *input)
|
|||||||
case VK_LEFT: win32_parse_key_msg(&input->left, msg); break;
|
case VK_LEFT: win32_parse_key_msg(&input->left, msg); break;
|
||||||
case VK_RIGHT: win32_parse_key_msg(&input->right, msg); break;
|
case VK_RIGHT: win32_parse_key_msg(&input->right, msg); break;
|
||||||
|
|
||||||
case '7': win32_parse_key_msg(&input->key_7, msg); break;
|
case '1': win32_parse_key_msg(&input->key_1, msg); break;
|
||||||
case '8': win32_parse_key_msg(&input->key_8, msg); break;
|
case '2': win32_parse_key_msg(&input->key_2, msg); break;
|
||||||
case '9': win32_parse_key_msg(&input->key_9, msg); break;
|
case '3': win32_parse_key_msg(&input->key_3, msg); break;
|
||||||
case '0': win32_parse_key_msg(&input->key_0, msg); break;
|
case '4': win32_parse_key_msg(&input->key_4, msg); break;
|
||||||
|
|
||||||
case 'U': win32_parse_key_msg(&input->key_U, msg); break;
|
case 'Q': win32_parse_key_msg(&input->key_q, msg); break;
|
||||||
case 'I': win32_parse_key_msg(&input->key_I, msg); break;
|
case 'W': win32_parse_key_msg(&input->key_w, msg); break;
|
||||||
case 'O': win32_parse_key_msg(&input->key_O, msg); break;
|
case 'E': win32_parse_key_msg(&input->key_e, msg); break;
|
||||||
case 'P': win32_parse_key_msg(&input->key_P, msg); break;
|
case 'R': win32_parse_key_msg(&input->key_r, msg); break;
|
||||||
|
|
||||||
case 'J': win32_parse_key_msg(&input->key_J, msg); break;
|
case 'A': win32_parse_key_msg(&input->key_a, msg); break;
|
||||||
case 'K': win32_parse_key_msg(&input->key_K, msg); break;
|
case 'S': win32_parse_key_msg(&input->key_s, msg); break;
|
||||||
case 'L': win32_parse_key_msg(&input->key_L, msg); break;
|
case 'D': win32_parse_key_msg(&input->key_d, msg); break;
|
||||||
case ';': win32_parse_key_msg(&input->key_colon, msg); break;
|
case 'F': win32_parse_key_msg(&input->key_f, msg); break;
|
||||||
|
|
||||||
case 'M': win32_parse_key_msg(&input->key_M, msg); break;
|
case 'Z': win32_parse_key_msg(&input->key_z, msg); break;
|
||||||
case ',': win32_parse_key_msg(&input->key_comma, msg); break;
|
case 'X': win32_parse_key_msg(&input->key_x, msg); break;
|
||||||
case '.': win32_parse_key_msg(&input->key_dot, msg); break;
|
case 'C': win32_parse_key_msg(&input->key_c, msg); break;
|
||||||
case '/': win32_parse_key_msg(&input->key_forward_slash, msg); break;
|
case 'V': win32_parse_key_msg(&input->key_v, msg); break;
|
||||||
|
|
||||||
case VK_ESCAPE:
|
case VK_ESCAPE:
|
||||||
{
|
{
|
||||||
@ -279,7 +279,7 @@ int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
|
|||||||
platformMemory.permanentMemSize = DQNT_ARRAY_COUNT(stackMemory);
|
platformMemory.permanentMemSize = DQNT_ARRAY_COUNT(stackMemory);
|
||||||
|
|
||||||
QueryPerformanceFrequency(&globalQueryPerformanceFrequency);
|
QueryPerformanceFrequency(&globalQueryPerformanceFrequency);
|
||||||
const f32 TARGET_FRAMES_PER_S = 180.0f;
|
const f32 TARGET_FRAMES_PER_S = 540.0f;
|
||||||
f32 targetSecondsPerFrame = 1 / TARGET_FRAMES_PER_S;
|
f32 targetSecondsPerFrame = 1 / TARGET_FRAMES_PER_S;
|
||||||
f32 frameTimeInS = 0.0f;
|
f32 frameTimeInS = 0.0f;
|
||||||
globalRunning = true;
|
globalRunning = true;
|
||||||
|
Loading…
Reference in New Issue
Block a user