Increase CPU cycle count per frame to smooth input
This commit is contained in:
parent
77346c3674
commit
4a77719d89
878
src/dchip8.cpp
878
src/dchip8.cpp
@ -290,31 +290,31 @@ FILE_SCOPE Chip8Controller dchip8_controller_map_input(PlatformInput input)
|
|||||||
|
|
||||||
Chip8Controller result = {};
|
Chip8Controller result = {};
|
||||||
|
|
||||||
result.key[0x01] = input.key_1.isDown;
|
result.key[0x01] = input.key_1.endedDown;
|
||||||
result.key[0x02] = input.key_2.isDown;
|
result.key[0x02] = input.key_2.endedDown;
|
||||||
result.key[0x03] = input.key_3.isDown;
|
result.key[0x03] = input.key_3.endedDown;
|
||||||
result.key[0x0C] = input.key_4.isDown;
|
result.key[0x0C] = input.key_4.endedDown;
|
||||||
|
|
||||||
result.key[0x04] = input.key_q.isDown;
|
result.key[0x04] = input.key_q.endedDown;
|
||||||
result.key[0x05] = input.key_w.isDown;
|
result.key[0x05] = input.key_w.endedDown;
|
||||||
result.key[0x06] = input.key_e.isDown;
|
result.key[0x06] = input.key_e.endedDown;
|
||||||
result.key[0x0D] = input.key_r.isDown;
|
result.key[0x0D] = input.key_r.endedDown;
|
||||||
|
|
||||||
result.key[0x07] = input.key_a.isDown;
|
result.key[0x07] = input.key_a.endedDown;
|
||||||
result.key[0x08] = input.key_s.isDown;
|
result.key[0x08] = input.key_s.endedDown;
|
||||||
result.key[0x09] = input.key_d.isDown;
|
result.key[0x09] = input.key_d.endedDown;
|
||||||
result.key[0x0E] = input.key_f.isDown;
|
result.key[0x0E] = input.key_f.endedDown;
|
||||||
|
|
||||||
result.key[0x0A] = input.key_z.isDown;
|
result.key[0x0A] = input.key_z.endedDown;
|
||||||
result.key[0x00] = input.key_x.isDown;
|
result.key[0x00] = input.key_x.endedDown;
|
||||||
result.key[0x0B] = input.key_c.isDown;
|
result.key[0x0B] = input.key_c.endedDown;
|
||||||
result.key[0x0F] = input.key_v.isDown;
|
result.key[0x0F] = input.key_v.endedDown;
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
void dchip8_update(PlatformRenderBuffer renderBuffer, PlatformInput input,
|
void dchip8_update(PlatformRenderBuffer renderBuffer, PlatformInput input,
|
||||||
PlatformMemory memory)
|
PlatformMemory memory, u32 cyclesToEmulate)
|
||||||
{
|
{
|
||||||
DQNT_ASSERT(cpu.indexRegister >= 0 && cpu.indexRegister <= 0xFFF);
|
DQNT_ASSERT(cpu.indexRegister >= 0 && cpu.indexRegister <= 0xFFF);
|
||||||
DQNT_ASSERT(cpu.programCounter >= 0 && cpu.programCounter <= 0xFFF);
|
DQNT_ASSERT(cpu.programCounter >= 0 && cpu.programCounter <= 0xFFF);
|
||||||
@ -333,7 +333,7 @@ void dchip8_update(PlatformRenderBuffer renderBuffer, PlatformInput input,
|
|||||||
if (cpu.state == chip8state_load_file)
|
if (cpu.state == chip8state_load_file)
|
||||||
{
|
{
|
||||||
PlatformFile file = {};
|
PlatformFile file = {};
|
||||||
if (platform_open_file(L"roms/pong", &file))
|
if (platform_open_file(L"roms/brix", &file))
|
||||||
{
|
{
|
||||||
DQNT_ASSERT((cpu.INIT_ADDRESS + file.size) <=
|
DQNT_ASSERT((cpu.INIT_ADDRESS + file.size) <=
|
||||||
memory.permanentMemSize);
|
memory.permanentMemSize);
|
||||||
@ -359,7 +359,7 @@ void dchip8_update(PlatformRenderBuffer renderBuffer, PlatformInput input,
|
|||||||
if (controller.key[keyVal])
|
if (controller.key[keyVal])
|
||||||
{
|
{
|
||||||
u8 regIndex = cpu.storeKeyToRegisterIndex;
|
u8 regIndex = cpu.storeKeyToRegisterIndex;
|
||||||
DQNT_ASSERT(keyVal >= 0 && keyVal < 0x0F);
|
DQNT_ASSERT(keyVal >= 0 && keyVal <= 0x0F);
|
||||||
|
|
||||||
cpu.registerArray[regIndex] = (u8)keyVal;
|
cpu.registerArray[regIndex] = (u8)keyVal;
|
||||||
cpu.state = chip8state_running;
|
cpu.state = chip8state_running;
|
||||||
@ -370,443 +370,468 @@ void dchip8_update(PlatformRenderBuffer renderBuffer, PlatformInput input,
|
|||||||
|
|
||||||
if (cpu.state == chip8state_running)
|
if (cpu.state == chip8state_running)
|
||||||
{
|
{
|
||||||
u8 opHighByte = mainMem[cpu.programCounter++];
|
bool earlyExit = false;
|
||||||
u8 opLowByte = mainMem[cpu.programCounter++];
|
for (u32 opCycle = 0; opCycle < cyclesToEmulate && !earlyExit;
|
||||||
|
opCycle++)
|
||||||
u8 opFirstNibble = (opHighByte & 0xF0);
|
|
||||||
switch (opFirstNibble)
|
|
||||||
{
|
{
|
||||||
case 0x00:
|
u8 opHighByte = mainMem[cpu.programCounter++];
|
||||||
|
u8 opLowByte = mainMem[cpu.programCounter++];
|
||||||
|
u8 opFirstNibble = (opHighByte & 0xF0);
|
||||||
|
switch (opFirstNibble)
|
||||||
{
|
{
|
||||||
// CLS - 00E0 - Clear the display
|
case 0x00:
|
||||||
if (opLowByte == 0xE0)
|
|
||||||
{
|
{
|
||||||
dchip8_init_display(renderBuffer);
|
// 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) | opLowByte;
|
|
||||||
DQNT_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
|
|
||||||
{
|
|
||||||
DQNT_ASSERT(opFirstNibble == 0x20);
|
|
||||||
cpu.stack[cpu.stackPointer++] = cpu.programCounter;
|
|
||||||
DQNT_ASSERT(cpu.stackPointer < DQNT_ARRAY_COUNT(cpu.stack));
|
|
||||||
}
|
|
||||||
|
|
||||||
cpu.programCounter = loc;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 0x30:
|
|
||||||
case 0x40:
|
|
||||||
{
|
|
||||||
u8 regNum = (0x0F & opHighByte);
|
|
||||||
DQNT_ASSERT(regNum < DQNT_ARRAY_COUNT(cpu.registerArray));
|
|
||||||
u8 *vx = &cpu.registerArray[regNum];
|
|
||||||
|
|
||||||
u8 valToCheck = opLowByte;
|
|
||||||
|
|
||||||
// SE Vx, byte - 3xkk - Skip next instruction if Vx == kk
|
|
||||||
if (opFirstNibble == 0x30)
|
|
||||||
{
|
|
||||||
if (*vx == valToCheck) cpu.programCounter += 2;
|
|
||||||
}
|
|
||||||
// SNE Vx, byte - 4xkk - Skip next instruction if Vx == kk
|
|
||||||
else
|
|
||||||
{
|
|
||||||
DQNT_ASSERT(opFirstNibble == 0x40);
|
|
||||||
if (*vx != 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) >> 4;
|
|
||||||
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;
|
|
||||||
|
|
||||||
case 0x60:
|
|
||||||
case 0x70:
|
|
||||||
{
|
|
||||||
u8 regNum = (0x0F & opHighByte);
|
|
||||||
DQNT_ASSERT(regNum < DQNT_ARRAY_COUNT(cpu.registerArray));
|
|
||||||
u8 valToOperateOn = opLowByte;
|
|
||||||
|
|
||||||
u8 *vx = &cpu.registerArray[regNum];
|
|
||||||
// LD Vx, byte - 6xkk - Set Vx = kk
|
|
||||||
if (opFirstNibble == 0x60)
|
|
||||||
{
|
|
||||||
*vx = valToOperateOn;
|
|
||||||
}
|
|
||||||
// ADD Vx, byte - 7xkk - Set Vx = Vx + kk
|
|
||||||
else
|
|
||||||
{
|
|
||||||
DQNT_ASSERT(opFirstNibble == 0x70);
|
|
||||||
*vx += valToOperateOn;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 0x80:
|
|
||||||
{
|
|
||||||
u8 firstRegNum = (0x0F & opHighByte);
|
|
||||||
DQNT_ASSERT(firstRegNum < DQNT_ARRAY_COUNT(cpu.registerArray));
|
|
||||||
|
|
||||||
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 (opFourthNibble == 0x00)
|
|
||||||
{
|
|
||||||
*vx = *vy;
|
|
||||||
}
|
|
||||||
// OR Vx, Vy - 8xy1 - Set Vx = Vx OR Vy
|
|
||||||
else if (opFourthNibble == 0x01)
|
|
||||||
{
|
|
||||||
u8 result = (*vx | *vy);
|
|
||||||
*vx = result;
|
|
||||||
}
|
|
||||||
// AND Vx, Vy - 8xy2 - Set Vx = Vx AND Vy
|
|
||||||
else if (opFourthNibble == 0x02)
|
|
||||||
{
|
|
||||||
u8 result = (*vx & *vy);
|
|
||||||
*vx = result;
|
|
||||||
}
|
|
||||||
// XOR Vx, Vy - 8xy3 - Set Vx = Vx XOR Vy
|
|
||||||
else if (opFourthNibble == 0x03)
|
|
||||||
{
|
|
||||||
u8 result = (*vx ^ *vy);
|
|
||||||
*vx = result;
|
|
||||||
}
|
|
||||||
// ADD Vx, Vy - 8xy4 - Set Vx = Vx + Vy, set VF = carry
|
|
||||||
else if (opFourthNibble == 0x04)
|
|
||||||
{
|
|
||||||
u16 result = (*vx + *vy);
|
|
||||||
*vx = (result > 255) ? (u8)(result - 256) : (u8)result;
|
|
||||||
|
|
||||||
cpu.VF = (result > 255) ? 1 : 0;
|
|
||||||
}
|
|
||||||
// SUB Vx, Vy - 8xy5 - Set Vx = Vx - Vy, set VF = NOT borrow
|
|
||||||
else if (opFourthNibble == 0x05)
|
|
||||||
{
|
|
||||||
if (*vx > *vy)
|
|
||||||
{
|
{
|
||||||
cpu.VF = 1;
|
dchip8_init_display(renderBuffer);
|
||||||
*vx -= *vy;
|
|
||||||
}
|
}
|
||||||
|
// 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) | opLowByte;
|
||||||
|
DQNT_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
|
else
|
||||||
{
|
{
|
||||||
cpu.VF = 0;
|
DQNT_ASSERT(opFirstNibble == 0x20);
|
||||||
*vx = (u8)(256 + *vx - *vy);
|
cpu.stack[cpu.stackPointer++] = cpu.programCounter;
|
||||||
|
DQNT_ASSERT(cpu.stackPointer <
|
||||||
|
DQNT_ARRAY_COUNT(cpu.stack));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
cpu.programCounter = loc;
|
||||||
}
|
}
|
||||||
// SHR Vx {, Vy} - 8xy6 - Set Vx = Vx SHR 1
|
break;
|
||||||
else if (opFourthNibble == 0x06)
|
|
||||||
{
|
|
||||||
if (*vx & 1)
|
|
||||||
cpu.VF = 1;
|
|
||||||
else
|
|
||||||
cpu.VF = 0;
|
|
||||||
|
|
||||||
*vx >>= 1;
|
case 0x30:
|
||||||
}
|
case 0x40:
|
||||||
// SUBN Vx {, Vy} - 8xy7 - Set Vx = Vy - Vx, set VF = NOT borrow
|
|
||||||
else if (opFourthNibble == 0x07)
|
|
||||||
{
|
{
|
||||||
if (*vy > *vx)
|
u8 regNum = (0x0F & opHighByte);
|
||||||
|
DQNT_ASSERT(regNum < DQNT_ARRAY_COUNT(cpu.registerArray));
|
||||||
|
u8 *vx = &cpu.registerArray[regNum];
|
||||||
|
|
||||||
|
u8 valToCheck = opLowByte;
|
||||||
|
|
||||||
|
// SE Vx, byte - 3xkk - Skip next instruction if Vx == kk
|
||||||
|
if (opFirstNibble == 0x30)
|
||||||
{
|
{
|
||||||
cpu.VF = 1;
|
if (*vx == valToCheck) cpu.programCounter += 2;
|
||||||
*vx = *vy - *vx;
|
|
||||||
}
|
}
|
||||||
|
// SNE Vx, byte - 4xkk - Skip next instruction if Vx == kk
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
cpu.VF = 0;
|
DQNT_ASSERT(opFirstNibble == 0x40);
|
||||||
*vx = (u8)(256 + *vy - *vx);
|
if (*vx != valToCheck) cpu.programCounter += 2;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
// SHL Vx {, Vy} - 8xyE - Set Vx = SHL 1
|
break;
|
||||||
else
|
|
||||||
{
|
|
||||||
DQNT_ASSERT(opFourthNibble == 0x0E);
|
|
||||||
if ((*vx >> 7) == 1)
|
|
||||||
cpu.VF = 1;
|
|
||||||
else
|
|
||||||
cpu.VF = 0;
|
|
||||||
|
|
||||||
*vx <<= 1;
|
// 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) >> 4;
|
||||||
|
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;
|
||||||
break;
|
|
||||||
|
|
||||||
// SNE Vx, Vy - 9xy0 - Skip next instruction if Vx != Vy
|
case 0x60:
|
||||||
case 0x90:
|
case 0x70:
|
||||||
{
|
|
||||||
u8 firstRegNum = (0x0F & opHighByte);
|
|
||||||
DQNT_ASSERT(firstRegNum < DQNT_ARRAY_COUNT(cpu.registerArray));
|
|
||||||
|
|
||||||
u8 secondRegNum = (0xF0 & opLowByte) >> 4;
|
|
||||||
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:
|
|
||||||
{
|
|
||||||
u16 valToSet = ((0x0F & opHighByte) << 8) | opLowByte;
|
|
||||||
cpu.indexRegister = valToSet;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
|
|
||||||
// JP V0, addr - Bnnn - Jump to location (nnn + V0)
|
|
||||||
case 0xB0:
|
|
||||||
{
|
|
||||||
u16 addr = (((0x0F & opHighByte) << 8) | opLowByte) + cpu.V0;
|
|
||||||
cpu.programCounter = addr;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
|
|
||||||
// RND Vx, byte - Cxkk - Set Vx = random byte AND kk
|
|
||||||
case 0xC0:
|
|
||||||
{
|
|
||||||
u8 regNum = (0x0F & opHighByte);
|
|
||||||
DQNT_ASSERT(regNum < DQNT_ARRAY_COUNT(cpu.registerArray));
|
|
||||||
u8 *vx = &cpu.registerArray[regNum];
|
|
||||||
|
|
||||||
u8 randNum = (u8)dqnt_rnd_pcg_range(&pcgState, 0, 255);
|
|
||||||
u8 andBits = opLowByte;
|
|
||||||
DQNT_ASSERT(randNum >= 0 && randNum <= 255);
|
|
||||||
|
|
||||||
*vx = (randNum & andBits);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
|
|
||||||
// DRW Vx, Vy, nibble - Dxyn - Display n-byte sprite starting at mem
|
|
||||||
// location I at (Vx, Vy), set VF = collision
|
|
||||||
case 0xD0:
|
|
||||||
{
|
|
||||||
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;
|
|
||||||
|
|
||||||
bool collisionFlag = false;
|
|
||||||
for (i32 i = 0; i < readNumBytesFromMem; i++)
|
|
||||||
{
|
{
|
||||||
u8 spriteBytes = mainMem[cpu.indexRegister + i];
|
u8 regNum = (0x0F & opHighByte);
|
||||||
u8 posY = initPosY + (u8)i;
|
DQNT_ASSERT(regNum < DQNT_ARRAY_COUNT(cpu.registerArray));
|
||||||
if (posY >= renderBuffer.height) posY = 0;
|
u8 valToOperateOn = opLowByte;
|
||||||
|
|
||||||
// NOTE: Flip the Y
|
u8 *vx = &cpu.registerArray[regNum];
|
||||||
posY = ((u8)(renderBuffer.height-1) - posY);
|
// LD Vx, byte - 6xkk - Set Vx = kk
|
||||||
|
if (opFirstNibble == 0x60)
|
||||||
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;
|
*vx = valToOperateOn;
|
||||||
|
}
|
||||||
|
// ADD Vx, byte - 7xkk - Set Vx = Vx + kk
|
||||||
|
else
|
||||||
|
{
|
||||||
|
DQNT_ASSERT(opFirstNibble == 0x70);
|
||||||
|
*vx += valToOperateOn;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
if (posX >= renderBuffer.width) posX = 0;
|
case 0x80:
|
||||||
u32 bitmapOffset =
|
{
|
||||||
(posX * BYTES_PER_PIXEL) + (posY * pitch);
|
u8 firstRegNum = (0x0F & opHighByte);
|
||||||
|
DQNT_ASSERT(firstRegNum <
|
||||||
|
DQNT_ARRAY_COUNT(cpu.registerArray));
|
||||||
|
|
||||||
// NOTE: Since we are using a 4bpp bitmap, let's use the
|
u8 secondRegNum = (0xF0 & opLowByte) >> 4;
|
||||||
// alpha channel to determine if a pixel is on or not.
|
DQNT_ASSERT(secondRegNum <
|
||||||
u32 *pixel = (u32 *)(&renderBitmap[bitmapOffset]);
|
DQNT_ARRAY_COUNT(cpu.registerArray));
|
||||||
u8 alphaBit = (*pixel >> 24);
|
|
||||||
|
|
||||||
DQNT_ASSERT(alphaBit == 0 || alphaBit == 255);
|
u8 *vx = &cpu.registerArray[firstRegNum];
|
||||||
bool pixelWasOn = (alphaBit == 255) ? true : false;
|
u8 *vy = &cpu.registerArray[secondRegNum];
|
||||||
|
|
||||||
i32 bitShift = baseBitShift - shift;
|
u8 opFourthNibble = (opLowByte & 0x0F);
|
||||||
bool spriteBit = ((spriteBytes >> bitShift) & 1);
|
// LD Vx, Vy - 8xy0 - Set Vx = Vy
|
||||||
bool pixelIsOn = (pixelWasOn ^ spriteBit);
|
if (opFourthNibble == 0x00)
|
||||||
|
{
|
||||||
|
*vx = *vy;
|
||||||
|
}
|
||||||
|
// OR Vx, Vy - 8xy1 - Set Vx = Vx OR Vy
|
||||||
|
else if (opFourthNibble == 0x01)
|
||||||
|
{
|
||||||
|
u8 result = (*vx | *vy);
|
||||||
|
*vx = result;
|
||||||
|
}
|
||||||
|
// AND Vx, Vy - 8xy2 - Set Vx = Vx AND Vy
|
||||||
|
else if (opFourthNibble == 0x02)
|
||||||
|
{
|
||||||
|
u8 result = (*vx & *vy);
|
||||||
|
*vx = result;
|
||||||
|
}
|
||||||
|
// XOR Vx, Vy - 8xy3 - Set Vx = Vx XOR Vy
|
||||||
|
else if (opFourthNibble == 0x03)
|
||||||
|
{
|
||||||
|
u8 result = (*vx ^ *vy);
|
||||||
|
*vx = result;
|
||||||
|
}
|
||||||
|
// ADD Vx, Vy - 8xy4 - Set Vx = Vx + Vy, set VF = carry
|
||||||
|
else if (opFourthNibble == 0x04)
|
||||||
|
{
|
||||||
|
u16 result = (*vx + *vy);
|
||||||
|
*vx = (result > 255) ? (u8)(result - 256) : (u8)result;
|
||||||
|
|
||||||
// NOTE: If caused a pixel to XOR into off, then this is
|
cpu.VF = (result > 255) ? 1 : 0;
|
||||||
// known as a "collision" in chip8
|
}
|
||||||
if (pixelWasOn && !pixelIsOn) collisionFlag = true;
|
// SUB Vx, Vy - 8xy5 - Set Vx = Vx - Vy, set VF = NOT borrow
|
||||||
|
else if (opFourthNibble == 0x05)
|
||||||
if (pixelIsOn)
|
{
|
||||||
|
if (*vx > *vy)
|
||||||
{
|
{
|
||||||
*pixel = 0xFFFFFFFF;
|
cpu.VF = 1;
|
||||||
|
*vx -= *vy;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
*pixel = 0;
|
cpu.VF = 0;
|
||||||
|
*vx = (u8)(256 + *vx - *vy);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// SHR Vx {, Vy} - 8xy6 - Set Vx = Vx SHR 1
|
||||||
|
else if (opFourthNibble == 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 (opFourthNibble == 0x07)
|
||||||
|
{
|
||||||
|
if (*vy > *vx)
|
||||||
|
{
|
||||||
|
cpu.VF = 1;
|
||||||
|
*vx = *vy - *vx;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
cpu.VF = 0;
|
||||||
|
*vx = (u8)(256 + *vy - *vx);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// SHL Vx {, Vy} - 8xyE - Set Vx = SHL 1
|
||||||
|
else
|
||||||
|
{
|
||||||
|
DQNT_ASSERT(opFourthNibble == 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) >> 4;
|
||||||
|
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:
|
||||||
|
{
|
||||||
|
u16 valToSet = ((0x0F & opHighByte) << 8) | opLowByte;
|
||||||
|
cpu.indexRegister = valToSet;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
// JP V0, addr - Bnnn - Jump to location (nnn + V0)
|
||||||
|
case 0xB0:
|
||||||
|
{
|
||||||
|
u16 addr =
|
||||||
|
(((0x0F & opHighByte) << 8) | opLowByte) + cpu.V0;
|
||||||
|
cpu.programCounter = addr;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
// RND Vx, byte - Cxkk - Set Vx = random byte AND kk
|
||||||
|
case 0xC0:
|
||||||
|
{
|
||||||
|
u8 regNum = (0x0F & opHighByte);
|
||||||
|
DQNT_ASSERT(regNum < DQNT_ARRAY_COUNT(cpu.registerArray));
|
||||||
|
u8 *vx = &cpu.registerArray[regNum];
|
||||||
|
|
||||||
|
u8 randNum = (u8)dqnt_rnd_pcg_range(&pcgState, 0, 255);
|
||||||
|
u8 andBits = opLowByte;
|
||||||
|
DQNT_ASSERT(randNum >= 0 && randNum <= 255);
|
||||||
|
|
||||||
|
*vx = (randNum & andBits);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
// DRW Vx, Vy, nibble - Dxyn - Display n-byte sprite starting at
|
||||||
|
// mem
|
||||||
|
// location I at (Vx, Vy), set VF = collision
|
||||||
|
case 0xD0:
|
||||||
|
{
|
||||||
|
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;
|
||||||
|
|
||||||
|
bool collisionFlag = false;
|
||||||
|
for (i32 i = 0; i < readNumBytesFromMem; i++)
|
||||||
|
{
|
||||||
|
u8 spriteBytes = mainMem[cpu.indexRegister + i];
|
||||||
|
u8 posY = initPosY + (u8)i;
|
||||||
|
if (posY >= renderBuffer.height) posY = 0;
|
||||||
|
|
||||||
|
// NOTE: Flip the Y
|
||||||
|
posY = ((u8)(renderBuffer.height - 1) - posY);
|
||||||
|
|
||||||
|
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);
|
||||||
|
|
||||||
|
// NOTE: If caused a pixel to XOR into off, then
|
||||||
|
// this is
|
||||||
|
// known as a "collision" in chip8
|
||||||
|
if (pixelWasOn && !pixelIsOn) collisionFlag = true;
|
||||||
|
|
||||||
|
if (pixelIsOn)
|
||||||
|
{
|
||||||
|
*pixel = 0xFFFFFFFF;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
*pixel = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
cpu.VF = collisionFlag;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 0xE0:
|
||||||
|
{
|
||||||
|
u8 regNum = (0x0F & opHighByte);
|
||||||
|
DQNT_ASSERT(regNum < DQNT_ARRAY_COUNT(cpu.registerArray));
|
||||||
|
u8 vx = cpu.registerArray[regNum];
|
||||||
|
DQNT_ASSERT(vx >= 0 && vx <= 0x0F);
|
||||||
|
DQNT_ASSERT(vx < DQNT_ARRAY_COUNT(controller.key));
|
||||||
|
|
||||||
|
bool skipNextInstruction = false;
|
||||||
|
// SKP Vx - Ex9E - Skip next instruction if key with the
|
||||||
|
// value
|
||||||
|
// of Vx is pressed
|
||||||
|
if (opLowByte == 0x9E)
|
||||||
|
{
|
||||||
|
skipNextInstruction = controller.key[vx];
|
||||||
|
}
|
||||||
|
// SKNP Vx - ExA1 - Skip next instruction if key with the
|
||||||
|
// value
|
||||||
|
// of Vx is not pressed
|
||||||
|
else
|
||||||
|
{
|
||||||
|
DQNT_ASSERT(opLowByte == 0xA1);
|
||||||
|
skipNextInstruction = !controller.key[vx];
|
||||||
|
}
|
||||||
|
|
||||||
|
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)
|
||||||
|
{
|
||||||
|
cpu.state = chip8state_await_input;
|
||||||
|
cpu.storeKeyToRegisterIndex = regNum;
|
||||||
|
earlyExit = true;
|
||||||
|
}
|
||||||
|
// 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)
|
||||||
|
{
|
||||||
|
u8 hexCharFromFontSet = *vx;
|
||||||
|
DQNT_ASSERT(hexCharFromFontSet >= 0x00 &&
|
||||||
|
hexCharFromFontSet <= 0x0F);
|
||||||
|
|
||||||
|
const u32 START_ADDR_OF_FONT = 0;
|
||||||
|
const u32 BYTES_PER_FONT = 5;
|
||||||
|
|
||||||
|
cpu.I = START_ADDR_OF_FONT +
|
||||||
|
(hexCharFromFontSet * BYTES_PER_FONT);
|
||||||
|
}
|
||||||
|
// 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.
|
||||||
|
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;
|
||||||
cpu.VF = collisionFlag;
|
};
|
||||||
}
|
}
|
||||||
break;
|
|
||||||
|
|
||||||
case 0xE0:
|
|
||||||
{
|
|
||||||
u8 regNum = (0x0F & opHighByte);
|
|
||||||
DQNT_ASSERT(regNum < DQNT_ARRAY_COUNT(cpu.registerArray));
|
|
||||||
u8 vx = cpu.registerArray[regNum];
|
|
||||||
DQNT_ASSERT(vx >= 0 && vx <= 0x0F);
|
|
||||||
DQNT_ASSERT(vx < DQNT_ARRAY_COUNT(controller.key));
|
|
||||||
|
|
||||||
bool skipNextInstruction = false;
|
|
||||||
// SKP Vx - Ex9E - Skip next instruction if key with the value
|
|
||||||
// of Vx is pressed
|
|
||||||
if (opLowByte == 0x9E)
|
|
||||||
{
|
|
||||||
skipNextInstruction = controller.key[vx];
|
|
||||||
}
|
|
||||||
// SKNP Vx - ExA1 - Skip next instruction if key with the value
|
|
||||||
// of Vx is not pressed
|
|
||||||
else
|
|
||||||
{
|
|
||||||
DQNT_ASSERT(opLowByte == 0xA1);
|
|
||||||
skipNextInstruction = !controller.key[vx];
|
|
||||||
}
|
|
||||||
|
|
||||||
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)
|
|
||||||
{
|
|
||||||
cpu.state = chip8state_await_input;
|
|
||||||
cpu.storeKeyToRegisterIndex = regNum;
|
|
||||||
}
|
|
||||||
// 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)
|
|
||||||
{
|
|
||||||
u8 hexCharFromFontSet = *vx;
|
|
||||||
DQNT_ASSERT(hexCharFromFontSet >= 0x00 &&
|
|
||||||
hexCharFromFontSet <= 0x0F);
|
|
||||||
|
|
||||||
const u32 START_ADDR_OF_FONT = 0;
|
|
||||||
const u32 BYTES_PER_FONT = 5;
|
|
||||||
|
|
||||||
cpu.I = START_ADDR_OF_FONT +
|
|
||||||
(hexCharFromFontSet * BYTES_PER_FONT);
|
|
||||||
}
|
|
||||||
// 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.
|
|
||||||
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;
|
|
||||||
};
|
|
||||||
|
|
||||||
// IMPORTANT: Timers need to be decremented at a rate of 60hz. Since we
|
// IMPORTANT: Timers need to be decremented at a rate of 60hz. Since we
|
||||||
// can run the interpreter faster than that, make sure we decrement
|
// can run the interpreter faster than that, make sure we decrement
|
||||||
// timers at the fixed rate.
|
// timers at the fixed rate.
|
||||||
@ -819,9 +844,16 @@ void dchip8_update(PlatformRenderBuffer renderBuffer, PlatformInput input,
|
|||||||
{
|
{
|
||||||
cpu.elapsedTime = 0;
|
cpu.elapsedTime = 0;
|
||||||
if (cpu.delayTimer > 0) cpu.delayTimer--;
|
if (cpu.delayTimer > 0) cpu.delayTimer--;
|
||||||
// TODO(doyle): This needs to play a buzzing sound whilst timer
|
|
||||||
// > 0
|
if (cpu.soundTimer > 0)
|
||||||
if (cpu.soundTimer > 0) cpu.soundTimer--;
|
{
|
||||||
|
cpu.soundTimer--;
|
||||||
|
if (cpu.soundTimer == 1)
|
||||||
|
{
|
||||||
|
// TODO(doyle): This needs to play a buzzing sound
|
||||||
|
// whilst timer > 0
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
|
@ -4,6 +4,6 @@
|
|||||||
#include "dchip8_platform.h"
|
#include "dchip8_platform.h"
|
||||||
|
|
||||||
void dchip8_update(PlatformRenderBuffer renderBuffer, PlatformInput input,
|
void dchip8_update(PlatformRenderBuffer renderBuffer, PlatformInput input,
|
||||||
PlatformMemory memory);
|
PlatformMemory memory, u32 cyclesToEmulate);
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
@ -46,8 +46,8 @@ enum Key
|
|||||||
|
|
||||||
typedef struct KeyState
|
typedef struct KeyState
|
||||||
{
|
{
|
||||||
bool isDown;
|
bool endedDown;
|
||||||
u32 transitionCount;
|
u32 halfTransitionCount;
|
||||||
} KeyState;
|
} KeyState;
|
||||||
|
|
||||||
typedef struct PlatformInput
|
typedef struct PlatformInput
|
||||||
|
@ -115,14 +115,13 @@ inline FILE_SCOPE LARGE_INTEGER win32_query_perf_counter_time()
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
FILE_SCOPE inline void win32_parse_key_msg(KeyState *key, MSG msg)
|
FILE_SCOPE inline void win32_update_key(KeyState *key, bool isDown)
|
||||||
{
|
{
|
||||||
LPARAM lParam = msg.lParam;
|
if (key->endedDown != isDown)
|
||||||
bool keyIsDown = ((lParam >> 30) & 1);
|
{
|
||||||
bool keyTransitioned = ((lParam >> 31) & 1);
|
key->endedDown = isDown;
|
||||||
|
key->halfTransitionCount++;
|
||||||
key->isDown = keyIsDown;
|
}
|
||||||
if (keyTransitioned) key->transitionCount++;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
FILE_SCOPE void win32_process_messages(HWND window, PlatformInput *input)
|
FILE_SCOPE void win32_process_messages(HWND window, PlatformInput *input)
|
||||||
@ -137,37 +136,38 @@ FILE_SCOPE void win32_process_messages(HWND window, PlatformInput *input)
|
|||||||
case WM_KEYDOWN:
|
case WM_KEYDOWN:
|
||||||
case WM_KEYUP:
|
case WM_KEYUP:
|
||||||
{
|
{
|
||||||
|
bool isDown = (msg.message == WM_KEYDOWN);
|
||||||
switch (msg.wParam)
|
switch (msg.wParam)
|
||||||
{
|
{
|
||||||
case VK_UP: win32_parse_key_msg(&input->up, msg); break;
|
case VK_UP: win32_update_key(&input->up, isDown); break;
|
||||||
case VK_DOWN: win32_parse_key_msg(&input->down, msg); break;
|
case VK_DOWN: win32_update_key(&input->down, isDown); break;
|
||||||
case VK_LEFT: win32_parse_key_msg(&input->left, msg); break;
|
case VK_LEFT: win32_update_key(&input->left, isDown); break;
|
||||||
case VK_RIGHT: win32_parse_key_msg(&input->right, msg); break;
|
case VK_RIGHT: win32_update_key(&input->right, isDown); break;
|
||||||
|
|
||||||
case '1': win32_parse_key_msg(&input->key_1, msg); break;
|
case '1': win32_update_key(&input->key_1, isDown); break;
|
||||||
case '2': win32_parse_key_msg(&input->key_2, msg); break;
|
case '2': win32_update_key(&input->key_2, isDown); break;
|
||||||
case '3': win32_parse_key_msg(&input->key_3, msg); break;
|
case '3': win32_update_key(&input->key_3, isDown); break;
|
||||||
case '4': win32_parse_key_msg(&input->key_4, msg); break;
|
case '4': win32_update_key(&input->key_4, isDown); break;
|
||||||
|
|
||||||
case 'Q': win32_parse_key_msg(&input->key_q, msg); break;
|
case 'Q': win32_update_key(&input->key_q, isDown); break;
|
||||||
case 'W': win32_parse_key_msg(&input->key_w, msg); break;
|
case 'W': win32_update_key(&input->key_w, isDown); break;
|
||||||
case 'E': win32_parse_key_msg(&input->key_e, msg); break;
|
case 'E': win32_update_key(&input->key_e, isDown); break;
|
||||||
case 'R': win32_parse_key_msg(&input->key_r, msg); break;
|
case 'R': win32_update_key(&input->key_r, isDown); break;
|
||||||
|
|
||||||
case 'A': win32_parse_key_msg(&input->key_a, msg); break;
|
case 'A': win32_update_key(&input->key_a, isDown); break;
|
||||||
case 'S': win32_parse_key_msg(&input->key_s, msg); break;
|
case 'S': win32_update_key(&input->key_s, isDown); break;
|
||||||
case 'D': win32_parse_key_msg(&input->key_d, msg); break;
|
case 'D': win32_update_key(&input->key_d, isDown); break;
|
||||||
case 'F': win32_parse_key_msg(&input->key_f, msg); break;
|
case 'F': win32_update_key(&input->key_f, isDown); break;
|
||||||
|
|
||||||
case 'Z': win32_parse_key_msg(&input->key_z, msg); break;
|
case 'Z': win32_update_key(&input->key_z, isDown); break;
|
||||||
case 'X': win32_parse_key_msg(&input->key_x, msg); break;
|
case 'X': win32_update_key(&input->key_x, isDown); break;
|
||||||
case 'C': win32_parse_key_msg(&input->key_c, msg); break;
|
case 'C': win32_update_key(&input->key_c, isDown); break;
|
||||||
case 'V': win32_parse_key_msg(&input->key_v, msg); break;
|
case 'V': win32_update_key(&input->key_v, isDown); break;
|
||||||
|
|
||||||
case VK_ESCAPE:
|
case VK_ESCAPE:
|
||||||
{
|
{
|
||||||
win32_parse_key_msg(&input->escape, msg);
|
win32_update_key(&input->escape, isDown);
|
||||||
if (input->escape.isDown) globalRunning = false;
|
if (input->escape.endedDown) globalRunning = false;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
@ -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 = 540.0f;
|
const f32 TARGET_FRAMES_PER_S = 30.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;
|
||||||
@ -300,13 +300,7 @@ int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
|
|||||||
platformBuffer.height = globalRenderBitmap.height;
|
platformBuffer.height = globalRenderBitmap.height;
|
||||||
platformBuffer.width = globalRenderBitmap.width;
|
platformBuffer.width = globalRenderBitmap.width;
|
||||||
platformBuffer.bytesPerPixel = globalRenderBitmap.bytesPerPixel;
|
platformBuffer.bytesPerPixel = globalRenderBitmap.bytesPerPixel;
|
||||||
dchip8_update(platformBuffer, platformInput, platformMemory);
|
dchip8_update(platformBuffer, platformInput, platformMemory, 15);
|
||||||
|
|
||||||
for (i32 i = 0; i < DQNT_ARRAY_COUNT(platformInput.key); i++)
|
|
||||||
{
|
|
||||||
if (platformInput.key[i].isDown)
|
|
||||||
OutputDebugString(L"Key is down\n");
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////
|
||||||
|
Loading…
Reference in New Issue
Block a user