Important changes to repositories hosted on mbed.com
Mbed hosted mercurial repositories are deprecated and are due to be permanently deleted in July 2026.
To keep a copy of this software download the repository Zip archive or clone locally using Mercurial.
It is also possible to export all your personal repositories from the account settings page.
Dependencies: mbed SDFileSystem2
Revision 17:c9afe1a7b423, committed 2019-01-13
- Comitter:
- dicarloj
- Date:
- Sun Jan 13 19:00:10 2019 +0000
- Parent:
- 16:1f728d08b3a7
- Commit message:
- a
Changed in this revision
diff -r 1f728d08b3a7 -r c9afe1a7b423 cpu.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/cpu.cpp Sun Jan 13 19:00:10 2019 +0000 @@ -0,0 +1,2921 @@ +// Implementation of OPCODES and cpu step function + +#define NDEBUG +#include <assert.h> + +#include "cpu.h" +#include "mem.h" + +// cpu registers, halted state, and timers +CpuState globalState; + +typedef void OpcodeHandler(u8 opcode); + +// the GB has several invalid opcodes +void invHandler(u8 opcode) { + printf("got invalid opcode 0x%x\n", opcode); + assert(false); +} + +//////////////////////////////// +// CB Opcode Helper Functions // +//////////////////////////////// + +u8 rlcReg(u8 value, bool isA) { + u8 result = value; + clearAllFlags(); + if((result & 0x80) != 0) { + setCarryFlag(); + result <<= 1; + result |= 0x1; + } else { + result <<= 1; + } + if(!isA) { + if((u8)result == 0) { + setZeroFlag(); + } + } + return result; +} + +u8 rlReg(u8 value, bool isA) { + u8 carry = getCarryFlag() ? (u8)1 : (u8)0; + u8 result = value; + clearAllFlags(); + if((result & 0x80) != 0) { + setCarryFlag(); + } + result <<= 1; + result |= carry; + if(!isA) { + if((u8)result == 0) { + setZeroFlag(); + } + } + return result; +} + +u8 rrc(u8 value, bool isA) { + u8 result = value; + clearAllFlags(); + if((result & 1) != 0) { + setCarryFlag(); + result >>= 1; + result |= 0x80; + } else { + result >>= 1; + } + if(!isA) { + if((u8)result == 0) { + setZeroFlag(); + } + } + return result; +} + +u8 rr(u8 value, bool isA) { + u8 carry = getCarryFlag() ? (u8)0x80 : (u8)0x00; + u8 result = value; + clearAllFlags(); + if((result & 1) != 0) { + setCarryFlag(); + } + result >>= 1; + result |= carry; + if(!isA) { + if((u8)result == 0) { + setZeroFlag(); + } + } + return result; +} + +u8 srl(u8 value) { + u8 result = value; + clearAllFlags(); + if(result & 1) { + setCarryFlag(); + } + result >>= 1; + if(result == 0) { + setZeroFlag(); + } + return result; +} + +u8 sla(u8 value) { + clearAllFlags(); + if(value & 0x80) { + setCarryFlag(); + } + u8 result = value << 1; + if(result == 0) { + setZeroFlag(); + } + return result; +} + +u8 sra(u8 value) { + u8 result = value; + clearAllFlags(); + if(result & 1) { + setCarryFlag(); + } + if((result & 0x80)) { + result >>= 1; + result |= 0x80; + } else { + result >>= 1; + } + if(result == 0) { + setZeroFlag(); + } + return result; +} + +u8 swapRegister(u8 value) { + u8 low = value & 0xf; + u8 hi = (value >> 4) & 0xf; + u8 result = (low << 4) + hi; + clearAllFlags(); + if((u8)result == 0) { + setZeroFlag(); + } + return result; +} + + +//////////////////////////////// +// CB Opcodes // +//////////////////////////////// + +void SLA_A(u8 opcode) { // 0x27 + globalState.pc++; + globalState.cycleCount += 8; + globalState.a = sla(globalState.a); +} + +void SLA_B(u8 opcode) { // 0x20 + globalState.pc++; + globalState.cycleCount += 8; + globalState.bc.hi = sla(globalState.bc.hi); +} + +void SLA_C(u8 opcode) { // 0x21 + globalState.pc++; + globalState.cycleCount += 8; + globalState.bc.lo = sla(globalState.bc.lo); +} + +void SLA_D(u8 opcode) { // 0x22 + globalState.pc++; + globalState.cycleCount += 8; + globalState.de.hi = sla(globalState.de.hi); +} + +void SLA_E(u8 opcode) { // 0x23 + globalState.pc++; + globalState.cycleCount += 8; + globalState.de.lo = sla(globalState.de.lo); +} + +void SLA_H(u8 opcode) { // 0x24 + globalState.pc++; + globalState.cycleCount += 8; + globalState.hl.hi = sla(globalState.hl.hi); +} + +void SLA_L(u8 opcode) { // 0x25 + globalState.pc++; + globalState.cycleCount += 8; + globalState.hl.lo = sla(globalState.hl.lo); +} + +void SLA_DHL(u8 opcode) { // 0x26 + globalState.pc++; + globalState.cycleCount += 16; + writeByte(sla(readByte(globalState.hl.v)), globalState.hl.v); +} + +void SRA_A(u8 opcode) { // 0x2f + globalState.pc++; + globalState.cycleCount += 8; + globalState.a = sra(globalState.a); +} + +void SRA_B(u8 opcode) { // 0x28 + globalState.pc++; + globalState.cycleCount += 8; + globalState.bc.hi = sra(globalState.bc.hi); +} + +void SRA_C(u8 opcode) { // 0x29 + globalState.pc++; + globalState.cycleCount += 8; + globalState.bc.lo = sra(globalState.bc.lo); +} + +void SRA_D(u8 opcode) { // 0x2a + globalState.pc++; + globalState.cycleCount += 8; + globalState.de.hi = sra(globalState.de.hi); +} + +void SRA_E(u8 opcode) { // 0x2b + globalState.pc++; + globalState.cycleCount += 8; + globalState.de.lo = sra(globalState.de.lo); +} + +void SRA_H(u8 opcode) { // 0x2c + globalState.pc++; + globalState.cycleCount += 8; + globalState.hl.hi = sra(globalState.hl.hi); +} + +void SRA_L(u8 opcode) { // 0x2d + globalState.pc++; + globalState.cycleCount += 8; + globalState.hl.lo = sra(globalState.hl.lo); +} + +void SRA_DHL(u8 opcode) { // 0x2e + globalState.pc++; + globalState.cycleCount += 16; + writeByte(sra(readByte(globalState.hl.v)), globalState.hl.v); +} + + +void SRL_A(u8 opcode) { // 0x3f + globalState.pc++; + globalState.cycleCount += 8; + globalState.a = srl(globalState.a); +} + +void SRL_B(u8 opcode) { // 0x38 + globalState.pc++; + globalState.cycleCount += 8; + globalState.bc.hi = srl(globalState.bc.hi); +} + +void SRL_C(u8 opcode) { // 0x39 + globalState.pc++; + globalState.cycleCount += 8; + globalState.bc.lo = srl(globalState.bc.lo); +} + +void SRL_D(u8 opcode) { // 0x3a + globalState.pc++; + globalState.cycleCount += 8; + globalState.de.hi = srl(globalState.de.hi); +} + +void SRL_E(u8 opcode) { // 0x3b + globalState.pc++; + globalState.cycleCount += 8; + globalState.de.lo = srl(globalState.de.lo); +} + +void SRL_H(u8 opcode) { // 0x3c + globalState.pc++; + globalState.cycleCount += 8; + globalState.hl.hi = srl(globalState.hl.hi); +} + +void SRL_L(u8 opcode) { // 0x3d + globalState.pc++; + globalState.cycleCount += 8; + globalState.hl.lo = srl(globalState.hl.lo); +} + +void SRL_DHL(u8 opcode) { // 0x3e + globalState.pc++; + globalState.cycleCount += 16; + writeByte(srl(readByte(globalState.hl.v)), globalState.hl.v); +} + +void RR_A(u8 opcode) { // 0x1f + globalState.pc++; + globalState.cycleCount += 8; + globalState.a = rr(globalState.a, false); +} + +void RR_B(u8 opcode) { // 0x18 + globalState.pc++; + globalState.cycleCount += 8; + globalState.bc.hi = rr(globalState.bc.hi, false); +} + +void RR_C(u8 opcode) { // 0x19 + globalState.pc++; + globalState.cycleCount += 8; + globalState.bc.lo = rr(globalState.bc.lo, false); +} + +void RR_D(u8 opcode) { // 0x1a + globalState.pc++; + globalState.cycleCount += 8; + globalState.de.hi = rr(globalState.de.hi, false); +} + +void RR_E(u8 opcode) { // 0x1b + globalState.pc++; + globalState.cycleCount += 8; + globalState.de.lo = rr(globalState.de.lo, false); +} + +void RR_H(u8 opcode) { // 0x1c + globalState.pc++; + globalState.cycleCount += 8; + globalState.hl.hi = rr(globalState.hl.hi, false); +} + +void RR_L(u8 opcode) { // 0x1d + globalState.pc++; + globalState.cycleCount += 8; + globalState.hl.lo = rr(globalState.hl.lo, false); +} + +void RR_DHL(u8 opcode) { // 0x1e + globalState.pc++; + globalState.cycleCount += 16; + writeByte(rr(readByte(globalState.hl.v), false), globalState.hl.v); +} + +void RL_A(u8 opcode) { // 0x17 + globalState.pc++; + globalState.cycleCount += 8; + globalState.a = rlReg(globalState.a, false); +} + +void RL_B(u8 opcode) { // 0x10 + globalState.pc++; + globalState.cycleCount += 8; + globalState.bc.hi = rlReg(globalState.bc.hi, false); +} + +void RL_C(u8 opcode) { // 0x11 + globalState.pc++; + globalState.cycleCount += 8; + globalState.bc.lo = rlReg(globalState.bc.lo, false); +} + +void RL_D(u8 opcode) { // 0x12 + globalState.pc++; + globalState.cycleCount += 8; + globalState.de.hi = rlReg(globalState.de.hi, false); +} + +void RL_E(u8 opcode) { // 0x13 + globalState.pc++; + globalState.cycleCount += 8; + globalState.de.lo = rlReg(globalState.de.lo, false); +} + +void RL_H(u8 opcode) { // 0x14 + globalState.pc++; + globalState.cycleCount += 8; + globalState.hl.hi = rlReg(globalState.hl.hi, false); +} + +void RL_L(u8 opcode) { // 0x15 + globalState.pc++; + globalState.cycleCount += 8; + globalState.hl.lo = rlReg(globalState.hl.lo, false); +} + +void RL_DHL(u8 opcode) { // 0x16 + globalState.pc++; + globalState.cycleCount += 16; + writeByte(rlReg(readByte(globalState.hl.v), false), globalState.hl.v); +} + +void SWAP_A(u8 opcode) { // 37 + globalState.pc++; + globalState.cycleCount += 8; + globalState.a = swapRegister(globalState.a); +} + +void SWAP_B(u8 opcode) { // 30 + globalState.pc++; + globalState.cycleCount += 8; + globalState.bc.hi = swapRegister(globalState.bc.hi); +} + +void SWAP_C(u8 opcode) { // 31 + globalState.pc++; + globalState.cycleCount += 8; + globalState.bc.lo = swapRegister(globalState.bc.lo); +} + +void SWAP_D(u8 opcode) { // 32 + globalState.pc++; + globalState.cycleCount += 8; + globalState.de.hi = swapRegister(globalState.de.hi); +} + +void SWAP_E(u8 opcode) { // 33 + globalState.pc++; + globalState.cycleCount += 8; + globalState.de.lo = swapRegister(globalState.de.lo); +} + +void SWAP_H(u8 opcode) { // 34 + globalState.pc++; + globalState.cycleCount += 8; + globalState.hl.hi = swapRegister(globalState.hl.hi); +} + +void SWAP_L(u8 opcode) { // 35 + globalState.pc++; + globalState.cycleCount += 8; + globalState.hl.lo = swapRegister(globalState.hl.lo); +} + +void SWAP_DHL(u8 opcode) { // 36 + globalState.pc++; + globalState.cycleCount += 16; + writeByte(swapRegister(readByte(globalState.hl.v)), globalState.hl.v); +} + +void RLC_A(u8 opcode) { // 07 + globalState.pc++; + globalState.cycleCount += 8; + globalState.a = rlcReg(globalState.a, false); +} + +void RLC_B(u8 opcode) { // 00 + globalState.pc++; + globalState.cycleCount += 8; + globalState.bc.hi = rlcReg(globalState.bc.hi, false); +} + +void RLC_C(u8 opcode) { // 01 + globalState.pc++; + globalState.cycleCount += 8; + globalState.bc.lo = rlcReg(globalState.bc.lo, false); +} + +void RLC_D(u8 opcode) { // 02 + globalState.pc++; + globalState.cycleCount += 8; + globalState.de.hi = rlcReg(globalState.de.hi, false); +} + +void RLC_E(u8 opcode) { // 03 + globalState.pc++; + globalState.cycleCount += 8; + globalState.de.lo = rlcReg(globalState.de.lo, false); +} + +void RLC_H(u8 opcode) { // 04 + globalState.pc++; + globalState.cycleCount += 8; + globalState.hl.hi = rlcReg(globalState.hl.hi, false); +} + +void RLC_L(u8 opcode) { // 05 + globalState.pc++; + globalState.cycleCount += 8; + globalState.hl.lo = rlcReg(globalState.hl.lo, false); +} + +void RLC_DHL(u8 opcode) { // 06 + globalState.pc++; + globalState.cycleCount += 16; + u8 result = rlcReg(readByte(globalState.hl.v), false); + writeByte(result, globalState.hl.v); +} + + +void RRC_A(u8 opcode) { // 0f + globalState.pc++; + globalState.cycleCount += 8; + globalState.a = rrc(globalState.a, false); +} + +void RRC_B(u8 opcode) { // 08 + globalState.pc++; + globalState.cycleCount += 8; + globalState.bc.hi = rrc(globalState.bc.hi, false); +} + +void RRC_C(u8 opcode) { // 09 + globalState.pc++; + globalState.cycleCount += 8; + globalState.bc.lo = rrc(globalState.bc.lo, false); +} + +void RRC_D(u8 opcode) { // 0a + globalState.pc++; + globalState.cycleCount += 8; + globalState.de.hi = rrc(globalState.de.hi, false); +} + +void RRC_E(u8 opcode) { // 0b + globalState.pc++; + globalState.cycleCount += 8; + globalState.de.lo = rrc(globalState.de.lo, false); +} + +void RRC_H(u8 opcode) { // 0c + globalState.pc++; + globalState.cycleCount += 8; + globalState.hl.hi = rrc(globalState.hl.hi, false); +} + +void RRC_L(u8 opcode) { // 0d + globalState.pc++; + globalState.cycleCount += 8; + globalState.hl.lo = rrc(globalState.hl.lo, false); +} + +void RRC_DHL(u8 opcode) { // 0e + globalState.pc++; + globalState.cycleCount += 16; + u8 result = rrc(readByte(globalState.hl.v), false); + writeByte(result, globalState.hl.v); +} + + +void bit_B_set(u8 opcode) { + u8 bitID = (opcode - (u8)0xC0) >> 3; + assert(bitID < 8); + globalState.bc.hi |= (1 << bitID); + globalState.pc += 1; + globalState.cycleCount += 8; +} + +void bit_C_set(u8 opcode) { + u8 bitID = (opcode - (u8)0xC1) >> 3; + assert(bitID < 8); + globalState.bc.lo |= (1 << bitID); + globalState.pc += 1; + globalState.cycleCount += 8; +} + +void bit_D_set(u8 opcode) { + u8 bitID = (opcode - (u8)0xC2) >> 3; + assert(bitID < 8); + globalState.de.hi |= (1 << bitID); + globalState.pc += 1; + globalState.cycleCount += 8; +} + +void bit_E_set(u8 opcode) { + u8 bitID = (opcode - (u8)0xC3) >> 3; + assert(bitID < 8); + globalState.de.lo |= (1 << bitID); + globalState.pc += 1; + globalState.cycleCount += 8; +} + +void bit_H_set(u8 opcode) { + u8 bitID = (opcode - (u8)0xC4) >> 3; + assert(bitID < 8); + globalState.hl.hi |= (1 << bitID); + globalState.pc += 1; + globalState.cycleCount += 8; +} + +void bit_L_set(u8 opcode) { + u8 bitID = (opcode - (u8)0xC5) >> 3; + assert(bitID < 8); + globalState.hl.lo |= (1 << bitID); + globalState.pc += 1; + globalState.cycleCount += 8; +} + +void bit_DHL_set(u8 opcode) { + u8 bitID = (opcode - (u8)0xC6) >> 3; + assert(bitID < 8); + u8 value = readByte(globalState.hl.v); + value |= (1 << bitID); + writeByte(value, globalState.hl.v); + globalState.pc += 1; + globalState.cycleCount += 16; +} + +void bit_A_set(u8 opcode) { + u8 bitID = (opcode - (u8)0xC7) >> 3; + assert(bitID < 8); + globalState.a |= (1 << bitID); + globalState.pc += 1; + globalState.cycleCount += 8; +} + +void bit_B_res(u8 opcode) { + u8 bitID = (opcode - (u8)0x80) >> 3; + assert(bitID < 8); + globalState.bc.hi &= ~(1 << bitID); + globalState.pc += 1; + globalState.cycleCount += 8; +} + +void bit_C_res(u8 opcode) { + u8 bitID = (opcode - (u8)0x81) >> 3; + assert(bitID < 8); + globalState.bc.lo &= ~(1 << bitID); + globalState.pc += 1; + globalState.cycleCount += 8; +} + +void bit_D_res(u8 opcode) { + u8 bitID = (opcode - (u8)0x82) >> 3; + assert(bitID < 8); + globalState.de.hi &= ~(1 << bitID); + globalState.pc += 1; + globalState.cycleCount += 8; +} + +void bit_E_res(u8 opcode) { + u8 bitID = (opcode - (u8)0x83) >> 3; + assert(bitID < 8); + globalState.de.lo &= ~(1 << bitID); + globalState.pc += 1; + globalState.cycleCount += 8; +} + +void bit_H_res(u8 opcode) { + u8 bitID = (opcode - (u8)0x84) >> 3; + assert(bitID < 8); + globalState.hl.hi &= ~(1 << bitID); + globalState.pc += 1; + globalState.cycleCount += 8; +} + +void bit_L_res(u8 opcode) { + u8 bitID = (opcode - (u8)0x85) >> 3; + assert(bitID < 8); + globalState.hl.lo &= ~(1 << bitID); + globalState.pc += 1; + globalState.cycleCount += 8; +} + +void bit_DHL_res(u8 opcode) { + u8 bitID = (opcode - (u8)0x86) >> 3; + assert(bitID < 8); + u8 value = readByte(globalState.hl.v); + value &= ~(1 << bitID); + writeByte(value, globalState.hl.v); + globalState.pc += 1; + globalState.cycleCount += 16; +} + +void bit_A_res(u8 opcode) { + u8 bitID = (opcode - (u8)0x87) >> 3; + assert(bitID < 8); + globalState.a &= ~(1 << bitID); + globalState.pc += 1; + globalState.cycleCount += 8; +} + +void bit_A_test(u8 opcode) { + u8 bitID = (opcode - (u8)0x47) >> 3; + assert(bitID < 8); + //printf("check bit %d of A\n", bitID); + u8 val = globalState.a; + if(((val >> bitID) & 1) == 0) { + setZeroFlag(); + } else { + clearZeroFlag(); + } + setHalfCarryFlag(); + clearSubtractFlag(); + globalState.pc += 1; + globalState.cycleCount += 8; +} + +void bit_B_test(u8 opcode) { + u8 bitID = (opcode - (u8)0x40) >> 3; + //printf("B opcode 0x%x bitId %d\n", opcode, bitID); + assert(bitID < 8); + //printf("check bit %d of B\n", bitID); + u8 val = globalState.bc.hi; + if(((val >> bitID) & 1) == 0) { + setZeroFlag(); + } else { + clearZeroFlag(); + } + setHalfCarryFlag(); + clearSubtractFlag(); + globalState.pc += 1; + globalState.cycleCount += 8; +} + + +void bit_C_test(u8 opcode) { + u8 bitID = (opcode - (u8)0x41) >> 3; + assert(bitID < 8); + //printf("check bit %d of C\n", bitID); + u8 val = globalState.bc.lo; + if(((val >> bitID) & 1) == 0) { + setZeroFlag(); + } else { + clearZeroFlag(); + } + setHalfCarryFlag(); + clearSubtractFlag(); + globalState.pc += 1; + globalState.cycleCount += 8; +} + +void bit_D_test(u8 opcode) { + u8 bitID = (opcode - (u8)0x42) >> 3; + assert(bitID < 8); + //printf("check bit %d of D\n", bitID); + u8 val = globalState.de.hi; + if(((val >> bitID) & 1) == 0) { + setZeroFlag(); + } else { + clearZeroFlag(); + } + setHalfCarryFlag(); + clearSubtractFlag(); + globalState.pc += 1; + globalState.cycleCount += 8; +} + +void bit_E_test(u8 opcode) { + u8 bitID = (opcode - (u8)0x43) >> 3; + assert(bitID < 8); + //printf("check bit %d of E\n", bitID); + u8 val = globalState.de.lo; + if(((val >> bitID) & 1) == 0) { + setZeroFlag(); + } else { + clearZeroFlag(); + } + setHalfCarryFlag(); + clearSubtractFlag(); + globalState.pc += 1; + globalState.cycleCount += 8; +} + +void bit_H_test(u8 opcode) { + u8 bitID = (opcode - (u8)0x44) >> 3; + assert(bitID < 8); + //printf("check bit %d of H\n", bitID); + u8 val = globalState.hl.hi; + if(((val >> bitID) & 1) == 0) { + setZeroFlag(); + } else { + clearZeroFlag(); + } + setHalfCarryFlag(); + clearSubtractFlag(); + globalState.pc += 1; + globalState.cycleCount += 8; +} + +void bit_L_test(u8 opcode) { + u8 bitID = (opcode - (u8)0x45) >> 3; + assert(bitID < 8); + //printf("check bit %d of L\n", bitID); + u8 val = globalState.hl.lo; + if(((val >> bitID) & 1) == 0) { + setZeroFlag(); + } else { + clearZeroFlag(); + } + setHalfCarryFlag(); + clearSubtractFlag(); + globalState.pc += 1; + globalState.cycleCount += 8; +} + +void bit_DHL_test(u8 opcode) { + u8 bitID = (opcode - (u8)0x45) >> 3; + assert(bitID < 8); + //printf("check bit %d of L\n", bitID); + u8 val = readByte(globalState.hl.v); + if(((val >> bitID) & 1) == 0) { + setZeroFlag(); + } else { + clearZeroFlag(); + } + setHalfCarryFlag(); + clearSubtractFlag(); + globalState.pc += 1; + globalState.cycleCount += 16; +} + +// CB-prefixed opcode handler table +static OpcodeHandler* opcode_cbs[256] = + {RLC_B, RLC_C, RLC_D, RLC_E, RLC_H, RLC_L, RLC_DHL, RLC_A, // 0x0 - 0x7 + RRC_B, RRC_C, RRC_D, RRC_E, RRC_H, RRC_L, RRC_DHL, RRC_A, // 0x8 - 0xf + RL_B, RL_C, RL_D, RL_E, RL_H, RL_L, RL_DHL, RL_A, // 0x10 - 0x17 + RR_B, RR_C, RR_D, RR_E, RR_H, RR_L, RR_DHL, RR_A, // 0x18 - 0x1f + SLA_B, SLA_C, SLA_D, SLA_E, SLA_H, SLA_L, SLA_DHL, SLA_A, // 0x20 - 0x27 + SRA_B, SRA_C, SRA_D, SRA_E, SRA_H, SRA_L, SRA_DHL, SRA_A, // 0x28 - 0x2f + SWAP_B, SWAP_C, SWAP_D, SWAP_E, SWAP_H, SWAP_L, SWAP_DHL, SWAP_A, // 0x30 - 0x37 + SRL_B, SRL_C, SRL_D, SRL_E, SRL_H, SRL_L, SRL_DHL, SRL_A, // 0x38 - 0x3f + bit_B_test, bit_C_test, bit_D_test, bit_E_test, bit_H_test, bit_L_test, bit_DHL_test, bit_A_test, // 0x40 - 0x47 + bit_B_test, bit_C_test, bit_D_test, bit_E_test, bit_H_test, bit_L_test, bit_DHL_test, bit_A_test, // 0x48 - 0x4f + bit_B_test, bit_C_test, bit_D_test, bit_E_test, bit_H_test, bit_L_test, bit_DHL_test, bit_A_test, // 0x50 - 0x57 + bit_B_test, bit_C_test, bit_D_test, bit_E_test, bit_H_test, bit_L_test, bit_DHL_test, bit_A_test, // 0x58 - 0x5f + bit_B_test, bit_C_test, bit_D_test, bit_E_test, bit_H_test, bit_L_test, bit_DHL_test, bit_A_test, // 0x60 - 0x67 + bit_B_test, bit_C_test, bit_D_test, bit_E_test, bit_H_test, bit_L_test, bit_DHL_test, bit_A_test, // 0x68 - 0x6f + bit_B_test, bit_C_test, bit_D_test, bit_E_test, bit_H_test, bit_L_test, bit_DHL_test, bit_A_test, // 0x70 - 0x77 + bit_B_test, bit_C_test, bit_D_test, bit_E_test, bit_H_test, bit_L_test, bit_DHL_test, bit_A_test, // 0x78 - 0x7f + bit_B_res, bit_C_res, bit_D_res, bit_E_res, bit_H_res, bit_L_res, bit_DHL_res, bit_A_res, // 0x80 - 0x8 + bit_B_res, bit_C_res, bit_D_res, bit_E_res, bit_H_res, bit_L_res, bit_DHL_res, bit_A_res, // 0x88 - 0x8f + bit_B_res, bit_C_res, bit_D_res, bit_E_res, bit_H_res, bit_L_res, bit_DHL_res, bit_A_res, // 0x90 - 0x97 + bit_B_res, bit_C_res, bit_D_res, bit_E_res, bit_H_res, bit_L_res, bit_DHL_res, bit_A_res, // 0x98 - 0x9f + bit_B_res, bit_C_res, bit_D_res, bit_E_res, bit_H_res, bit_L_res, bit_DHL_res, bit_A_res, // 0xa0 - 0xa7 + bit_B_res, bit_C_res, bit_D_res, bit_E_res, bit_H_res, bit_L_res, bit_DHL_res, bit_A_res, // 0xa8 - 0xaf + bit_B_res, bit_C_res, bit_D_res, bit_E_res, bit_H_res, bit_L_res, bit_DHL_res, bit_A_res, // 0xb0 - 0xb7 + bit_B_res, bit_C_res, bit_D_res, bit_E_res, bit_H_res, bit_L_res, bit_DHL_res, bit_A_res, // 0xb8 - 0xbf + bit_B_set, bit_C_set, bit_D_set, bit_E_set, bit_H_set, bit_L_set, bit_DHL_set, bit_A_set , // 0xc0 - 0xc7 + bit_B_set, bit_C_set, bit_D_set, bit_E_set, bit_H_set, bit_L_set, bit_DHL_set, bit_A_set , // 0xc8 - 0xcf + bit_B_set, bit_C_set, bit_D_set, bit_E_set, bit_H_set, bit_L_set, bit_DHL_set, bit_A_set , // 0xd0 - 0xd7 + bit_B_set, bit_C_set, bit_D_set, bit_E_set, bit_H_set, bit_L_set, bit_DHL_set, bit_A_set , // 0xd8 - 0xdf + bit_B_set, bit_C_set, bit_D_set, bit_E_set, bit_H_set, bit_L_set, bit_DHL_set, bit_A_set , // 0xe0 - 0xe7 + bit_B_set, bit_C_set, bit_D_set, bit_E_set, bit_H_set, bit_L_set, bit_DHL_set, bit_A_set , // 0xe8 - 0xef + bit_B_set, bit_C_set, bit_D_set, bit_E_set, bit_H_set, bit_L_set, bit_DHL_set, bit_A_set , // 0xf0 - 0xf7 + bit_B_set, bit_C_set, bit_D_set, bit_E_set, bit_H_set, bit_L_set, bit_DHL_set, bit_A_set }; // 0xf8 - 0xff + + +//////////////////////////////// +// Opcodes // +//////////////////////////////// +void LD_B_n(u8 opocde) { // 06 + u8 imm = readByte(++globalState.pc); + globalState.pc++; + globalState.bc.hi = imm; + globalState.cycleCount += 8; +} + +void LD_C_n(u8 opocde) { // 0E + u8 imm = readByte(++globalState.pc); + globalState.pc++; + globalState.bc.lo = imm; + globalState.cycleCount += 8; +} + +void LD_D_n(u8 opocde) { // 16 + u8 imm = readByte(++globalState.pc); + globalState.pc++; + globalState.de.hi = imm; + globalState.cycleCount += 8; +} + +void LD_E_n(u8 opocde) { // 1e + u8 imm = readByte(++globalState.pc); + globalState.pc++; + globalState.de.lo = imm; + globalState.cycleCount += 8; +} + +void LD_H_n(u8 opocde) { // 26 + u8 imm = readByte(++globalState.pc); + globalState.pc++; + globalState.hl.hi = imm; + globalState.cycleCount += 8; +} + +void LD_L_n(u8 opocde) { // 2e + u8 imm = readByte(++globalState.pc); + globalState.pc++; + globalState.hl.lo = imm; + globalState.cycleCount += 8; +} + +// REGISTER-REGISTER LOADS (REGISTER A) +void LD_A_A(u8 opcode) { // 0x7f + globalState.pc++; + globalState.cycleCount += 4; + globalState.a = globalState.a; +} + +void LD_A_B(u8 opcode) { // 0x78 + globalState.pc++; + globalState.cycleCount += 4; + globalState.a = globalState.bc.hi; +} + +void LD_A_C(u8 opcode) { // 0x79 + globalState.pc++; + globalState.cycleCount += 4; + globalState.a = globalState.bc.lo; +} + +void LD_A_D(u8 opcode) { // 0x7a + globalState.pc++; + globalState.cycleCount += 4; + globalState.a = globalState.de.hi; +} + +void LD_A_E(u8 opcode) { // 0x7b + globalState.pc++; + globalState.cycleCount += 4; + globalState.a = globalState.de.lo; +} + +void LD_A_H(u8 opcode) { // 0x7c + globalState.pc++; + globalState.cycleCount += 4; + globalState.a = globalState.hl.hi; +} + +void LD_A_L(u8 opcode) { // 0x7d + globalState.pc++; + globalState.cycleCount += 4; + globalState.a = globalState.hl.lo; +} + +void LD_A_DHL(u8 opcode) { // 0x7e + globalState.pc++; + globalState.cycleCount += 8; + globalState.a = readByte(globalState.hl.v); +} + + + +// REGISTER-REGISTER LOADS (REGISTER B) +void LD_B_B(u8 opcode) { // 0x40 + globalState.pc++; + globalState.cycleCount += 4; + globalState.bc.hi = globalState.bc.hi; +} + +void LD_B_C(u8 opcode) { // 0x41 + globalState.pc++; + globalState.cycleCount += 4; + globalState.bc.hi = globalState.bc.lo; +} + +void LD_B_D(u8 opcode) { // 0x42 + globalState.pc++; + globalState.cycleCount += 4; + globalState.bc.hi = globalState.de.hi; +} + +void LD_B_E(u8 opcode) { // 0x43 + globalState.pc++; + globalState.cycleCount += 4; + globalState.bc.hi = globalState.de.lo; +} + +void LD_B_H(u8 opcode) { // 0x44 + globalState.pc++; + globalState.cycleCount += 4; + globalState.bc.hi = globalState.hl.hi; +} + +void LD_B_L(u8 opcode) { // 0x45 + globalState.pc++; + globalState.cycleCount += 4; + globalState.bc.hi = globalState.hl.lo; +} + +void LD_B_DHL(u8 opcode) { // 0x46 + globalState.pc++; + globalState.cycleCount += 8; + globalState.bc.hi = readByte(globalState.hl.v); +} + + + +// REGISTER-REGISTER LOADS (REGISTER C) +void LD_C_B(u8 opcode) { // 0x48 + globalState.pc++; + globalState.cycleCount += 4; + globalState.bc.lo = globalState.bc.hi; +} + +void LD_C_C(u8 opcode) { // 0x49 + globalState.pc++; + globalState.cycleCount += 4; + globalState.bc.lo = globalState.bc.lo; +} + +void LD_C_D(u8 opcode) { // 0x4a + globalState.pc++; + globalState.cycleCount += 4; + globalState.bc.lo = globalState.de.hi; +} + +void LD_C_E(u8 opcode) { // 0x4b + globalState.pc++; + globalState.cycleCount += 4; + globalState.bc.lo = globalState.de.lo; +} + +void LD_C_H(u8 opcode) { // 0x4c + globalState.pc++; + globalState.cycleCount += 4; + globalState.bc.lo = globalState.hl.hi; +} + +void LD_C_L(u8 opcode) { // 0x4d + globalState.pc++; + globalState.cycleCount += 4; + globalState.bc.lo = globalState.hl.lo; +} + +void LD_C_DHL(u8 opcode) { // 0x4e + globalState.pc++; + globalState.cycleCount += 8; + globalState.bc.lo = readByte(globalState.hl.v); +} + + + +// REGISTER-REGISTER LOADS (REGISTER D) +void LD_D_B(u8 opcode) { // 0x50 + globalState.pc++; + globalState.cycleCount += 4; + globalState.de.hi = globalState.bc.hi; +} + +void LD_D_C(u8 opcode) { // 0x51 + globalState.pc++; + globalState.cycleCount += 4; + globalState.de.hi = globalState.bc.lo; +} + +void LD_D_D(u8 opcode) { // 0x52 + globalState.pc++; + globalState.cycleCount += 4; + globalState.de.hi = globalState.de.hi; +} + +void LD_D_E(u8 opcode) { // 0x53 + globalState.pc++; + globalState.cycleCount += 4; + globalState.de.hi = globalState.de.lo; +} + +void LD_D_H(u8 opcode) { // 0x54 + globalState.pc++; + globalState.cycleCount += 4; + globalState.de.hi = globalState.hl.hi; +} + +void LD_D_L(u8 opcode) { // 0x55 + globalState.pc++; + globalState.cycleCount += 4; + globalState.de.hi = globalState.hl.lo; +} + +void LD_D_DHL(u8 opcode) { // 0x56 + globalState.pc++; + globalState.cycleCount += 8; + globalState.de.hi = readByte(globalState.hl.v); +} + + +// REGISTER-REGISTER LOADS (REGISTER E) +void LD_E_B(u8 opcode) { // 0x58 + globalState.pc++; + globalState.cycleCount += 4; + globalState.de.lo = globalState.bc.hi; +} + +void LD_E_C(u8 opcode) { // 0x59 + globalState.pc++; + globalState.cycleCount += 4; + globalState.de.lo = globalState.bc.lo; +} + +void LD_E_D(u8 opcode) { // 0x5a + globalState.pc++; + globalState.cycleCount += 4; + globalState.de.lo = globalState.de.hi; +} + +void LD_E_E(u8 opcode) { // 0x5b + globalState.pc++; + globalState.cycleCount += 4; + globalState.de.lo = globalState.de.lo; +} + +void LD_E_H(u8 opcode) { // 0x5c + globalState.pc++; + globalState.cycleCount += 4; + globalState.de.lo = globalState.hl.hi; +} + +void LD_E_L(u8 opcode) { // 0x5d + globalState.pc++; + globalState.cycleCount += 4; + globalState.de.lo = globalState.hl.lo; +} + +void LD_E_DHL(u8 opcode) { // 0x5e + globalState.pc++; + globalState.cycleCount += 8; + globalState.de.lo = readByte(globalState.hl.v); +} + +// REGISTER-REGISTER LOADS (REGISTER H) +void LD_H_B(u8 opcode) { // 0x60 + globalState.pc++; + globalState.cycleCount += 4; + globalState.hl.hi = globalState.bc.hi; +} + +void LD_H_C(u8 opcode) { // 0x61 + globalState.pc++; + globalState.cycleCount += 4; + globalState.hl.hi = globalState.bc.lo; +} + +void LD_H_D(u8 opcode) { // 0x62 + globalState.pc++; + globalState.cycleCount += 4; + globalState.hl.hi = globalState.de.hi; +} + +void LD_H_E(u8 opcode) { // 0x63 + globalState.pc++; + globalState.cycleCount += 4; + globalState.hl.hi = globalState.de.lo; +} + +void LD_H_H(u8 opcode) { // 0x64 + globalState.pc++; + globalState.cycleCount += 4; + globalState.hl.hi = globalState.hl.hi; +} + +void LD_H_L(u8 opcode) { // 0x65 + globalState.pc++; + globalState.cycleCount += 4; + globalState.hl.hi = globalState.hl.lo; +} + +void LD_H_DHL(u8 opcode) { // 0x66 + globalState.pc++; + globalState.cycleCount += 8; + globalState.hl.hi = readByte(globalState.hl.v); +} + +// REGISTER-REGISTER LOADS (REGISTER L) +void LD_L_B(u8 opcode) { // 0x60 + globalState.pc++; + globalState.cycleCount += 4; + globalState.hl.lo = globalState.bc.hi; +} + +void LD_L_C(u8 opcode) { // 0x61 + globalState.pc++; + globalState.cycleCount += 4; + globalState.hl.lo = globalState.bc.lo; +} + +void LD_L_D(u8 opcode) { // 0x62 + globalState.pc++; + globalState.cycleCount += 4; + globalState.hl.lo = globalState.de.hi; +} + +void LD_L_E(u8 opcode) { // 0x63 + globalState.pc++; + globalState.cycleCount += 4; + globalState.hl.lo = globalState.de.lo; +} + +void LD_L_H(u8 opcode) { // 0x64 + globalState.pc++; + globalState.cycleCount += 4; + globalState.hl.lo = globalState.hl.hi; +} + +void LD_L_L(u8 opcode) { // 0x65 + globalState.pc++; + globalState.cycleCount += 4; + globalState.hl.lo = globalState.hl.lo; +} + +void LD_L_DHL(u8 opcode) { // 0x66 + globalState.pc++; + globalState.cycleCount += 8; + globalState.hl.lo = readByte(globalState.hl.v); +} + +// REGISTER-REGISTER LOADS (REGISTER (HL)) +void LD_DHL_B(u8 opcode) { // 0x70 + globalState.pc++; + globalState.cycleCount += 8; + writeByte(globalState.bc.hi, globalState.hl.v); +} + +void LD_DHL_C(u8 opcode) { // 0x71 + globalState.pc++; + globalState.cycleCount += 8; + writeByte(globalState.bc.lo, globalState.hl.v); +} + +void LD_DHL_D(u8 opcode) { // 0x72 + globalState.pc++; + globalState.cycleCount += 8; + writeByte(globalState.de.hi, globalState.hl.v); +} + +void LD_DHL_E(u8 opcode) { // 0x73 + globalState.pc++; + globalState.cycleCount += 8; + writeByte(globalState.de.lo, globalState.hl.v); +} + +void LD_DHL_H(u8 opcode) { // 0x74 + globalState.pc++; + globalState.cycleCount += 8; + writeByte(globalState.hl.hi, globalState.hl.v); +} + +void LD_DHL_L(u8 opcode) { // 0x75 + globalState.pc++; + globalState.cycleCount += 8; + writeByte(globalState.hl.lo, globalState.hl.v); +} + +// Random +void LD_DHL_n(u8 opcode) { // 0x36 + u8 imm = readByte(++globalState.pc); + globalState.pc++; + globalState.cycleCount += 12; + writeByte(imm, globalState.hl.v); +} + +// Load Into Register A Specials +void LD_A_DBC(u8 opcode) { // 0A + globalState.pc++; + globalState.cycleCount += 8; + globalState.a = readByte(globalState.bc.v); +} + +void LD_A_DDE(u8 opcode) { // 1A + globalState.pc++; + globalState.cycleCount += 8; + globalState.a = readByte(globalState.de.v); +} + + +void LD_A_Dnn(u8 opcode) { // FA + globalState.pc++; + globalState.cycleCount += 16; + globalState.a = readByte(readU16(globalState.pc)); + globalState.pc += 2; +} + +void LD_A_n(u8 opocde) { // 0x3e + u8 imm = readByte(++globalState.pc); + globalState.pc++; + globalState.a = imm; + globalState.cycleCount += 8; +} + +// LOAD FROM REGISTER A +void LD_B_A(u8 opcode) { // 0x47 + globalState.pc++; + globalState.cycleCount += 4; + globalState.bc.hi = globalState.a; +} + +void LD_C_A(u8 opcode) { // 0x4f + globalState.pc++; + globalState.cycleCount += 4; + globalState.bc.lo = globalState.a; +} + +void LD_D_A(u8 opcode) { // 0x57 + globalState.pc++; + globalState.cycleCount += 4; + globalState.de.hi = globalState.a; +} + +void LD_E_A(u8 opcode) { // 0x5f + globalState.pc++; + globalState.cycleCount += 4; + globalState.de.lo = globalState.a; +} + +void LD_H_A(u8 opcode) { // 0x67 + globalState.pc++; + globalState.cycleCount += 4; + globalState.hl.hi = globalState.a; +} + +void LD_L_A(u8 opcode) { // 0x6f + globalState.pc++; + globalState.cycleCount += 4; + globalState.hl.lo = globalState.a; +} + +void LD_DBC_A(u8 opcode) { // 0x02 + globalState.pc++; + globalState.cycleCount += 8; + writeByte(globalState.a, globalState.bc.v); +} + +void LD_DDE_A(u8 opcode) { // 0x12 + globalState.pc++; + globalState.cycleCount += 8; + writeByte(globalState.a, globalState.de.v); +} + +void LD_DHL_A(u8 opcode) { // 0x77 + globalState.pc++; + globalState.cycleCount += 8; + writeByte(globalState.a, globalState.hl.v); +} + +void LD_Dnn_A(u8 opcode) { // 0xea + globalState.pc++; + globalState.cycleCount += 16; + writeByte(globalState.a, readU16(globalState.pc)); + globalState.pc += 2; +} + +// Load A with memory offset from 0xff00 +void LD_A_FF00_C(u8 opcode) { //0xf2 + globalState.pc++; + globalState.cycleCount += 8; + globalState.a = readByte((u16)0xff00 + globalState.bc.lo); +} + +// Store A with memory offset from 0xff00 +void LD_FF00_C_A(u8 opcode) { // 0xe2 + globalState.pc++; + writeByte(globalState.a, (u16)0xff00 + globalState.bc.lo); + globalState.cycleCount += 8; +} + +// Load (HL) into A, decrement HL +void LDD_A_DHL(u8 opcode) { // 0x3a + globalState.pc++; + globalState.cycleCount += 8; + globalState.a = readByte(globalState.hl.v); + globalState.hl.v--; +} + +// Load A into (HL), decrement HL +void LDD_DHL_A(u8 opcode) { // 0x32 + writeByte(globalState.a, globalState.hl.v); + globalState.hl.v--; + globalState.pc += 1; + globalState.cycleCount += 8; +} + +// Load (HL) into A, increment HL +void LDI_A_DHL(u8 opcode){ // 0x2a + globalState.pc += 1; + globalState.cycleCount += 8; + globalState.a = readByte(globalState.hl.v); + globalState.hl.v++; +} + +// Load A into (HL), increment HL +void LDI_DHL_A(u8 opcode) { // 0x22 + writeByte(globalState.a, globalState.hl.v); + globalState.hl.v++; + globalState.pc += 1; + globalState.cycleCount += 8; +} +// page 75 + +// Store A into FF00 + n +void LD_FF00_n_A(u8 opcode) { //0xe0 + u8 imm = readByte(++globalState.pc); + globalState.pc++; + writeByte(globalState.a, (u16)0xff00 + imm); + globalState.cycleCount += 12; +} + +// Load A with FF00 + n +void LD_A_FF00_n(u8 opcode) { // 0xf0 + u8 imm = readByte(++globalState.pc); + globalState.pc++; + globalState.a = readByte((u16)0xff00 + imm); + globalState.cycleCount += 12; +} + +// 16-bit loads +void LD_BC_nn(u8 opcode) { // 0x01 + u16 imm = readU16(globalState.pc + (u16)1); + globalState.bc.v = imm; + globalState.pc += 3; + globalState.cycleCount += 12; +} + +void LD_DE_nn(u8 opcode) { // 0x11 + u16 imm = readU16(globalState.pc + (u16)1); + globalState.de.v = imm; + globalState.pc += 3; + globalState.cycleCount += 12; +} + +void LD_HL_nn(u8 opcode) { // 0x21 + u16 imm = readU16(globalState.pc + (u16)1); + globalState.hl.v = imm; + globalState.pc += 3; + globalState.cycleCount += 12; +} + +void LD_SP_nn(u8 opcode) { // 0x31 + u16 imm = readU16(globalState.pc + (u16)1); + globalState.sp = imm; + globalState.pc += 3; + globalState.cycleCount += 12; +} + +void LD_SP_HL(u8 opcode) { // 0xf9 + globalState.sp = globalState.hl.v; + globalState.pc++; + globalState.cycleCount += 8; +} + +// load effective address relative to stack pointer +void LD_HL_SP_n(u8 opcode) { //0xf8 + globalState.pc++; + globalState.cycleCount += 12; + s8 imm = readByte(globalState.pc); + globalState.pc++; + clearAllFlags(); + u16 result = globalState.sp + imm; + if(((globalState.sp ^ imm ^ result) & 0x100) == 0x100) { + setCarryFlag(); + } + if(((globalState.sp ^ imm ^ result) & 0x10) == 0x10) { + setHalfCarryFlag(); + } + globalState.hl.v = result; +} + +// store stack pointer +void LD_Dnn_SP(u8 opcode) { // 08 + globalState.pc++; + u16 addr = readU16(globalState.pc); + globalState.pc+=2; + globalState.cycleCount += 20; + writeU16(globalState.sp, addr); +} + +// push U16 register onto stack +void PUSH_AF(u8 opcode) { // 0xf5 + globalState.pc++; + globalState.cycleCount += 16; + writeU16(globalState.f + (((u16)globalState.a) << 8), globalState.sp - (u16)2); + globalState.sp -= 2; +} + +void PUSH_BC(u8 opcode) { // 0xc5 + globalState.pc++; + globalState.cycleCount += 16; + writeU16(globalState.bc.v, globalState.sp - (u16)2); + globalState.sp -= 2; +} + +void PUSH_DE(u8 opcode) { // 0xd5 + globalState.pc++; + globalState.cycleCount += 16; + writeU16(globalState.de.v, globalState.sp - (u16)2); + globalState.sp -= 2; +} + +void PUSH_HL(u8 opcode) { // 0xe5 + globalState.pc++; + globalState.cycleCount += 16; + writeU16(globalState.hl.v, globalState.sp - (u16)2); + globalState.sp -= 2; +} + +// POP U16 register from stack +void POP_AF(u8 opcode) { // 0xf1 + globalState.pc++; + globalState.cycleCount += 12; + u16 v = readU16(globalState.sp); + globalState.sp += 2; + globalState.a = (u8)(v >> 8); + globalState.f = (u8)(v & 0xf0); +} + +void POP_BC(u8 opcode) { // 0xc1 + globalState.pc++; + globalState.cycleCount += 12; + u16 v = readU16(globalState.sp); + globalState.sp += 2; + globalState.bc.v = v; +} + +void POP_DE(u8 opcode) { // 0xd1 + globalState.pc++; + globalState.cycleCount += 12; + u16 v = readU16(globalState.sp); + globalState.sp += 2; + globalState.de.v = v; +} + +void POP_HL(u8 opcode) { // 0xe1 + globalState.pc++; + globalState.cycleCount += 12; + u16 v = readU16(globalState.sp); + globalState.sp += 2; + globalState.hl.v = v; +} + +void addToA(u8 number) { + int result = globalState.a + number; + int carryBits = globalState.a ^ number ^ result; + globalState.a = (u8)result; + clearAllFlags(); + + if((u8)result == 0){ + setZeroFlag(); + } + + if((carryBits & 0x100) != 0) { + setCarryFlag(); + } + if((carryBits & 0x10) != 0) { + setHalfCarryFlag(); + } +} + +// ADD Opcodes +void ADD_A(u8 opcode) { // 0x87 + globalState.pc++; + globalState.cycleCount += 4; + addToA(globalState.a); +} + +void ADD_B(u8 opcode) { // 0x80 + globalState.pc++; + globalState.cycleCount += 4; + addToA(globalState.bc.hi); +} + +void ADD_C(u8 opcode) { // 0x81 + globalState.pc++; + globalState.cycleCount += 4; + addToA(globalState.bc.lo); +} + +void ADD_D(u8 opcode) { // 0x82 + globalState.pc++; + globalState.cycleCount += 4; + addToA(globalState.de.hi); +} + +void ADD_E(u8 opcode) { // 0x83 + globalState.pc++; + globalState.cycleCount += 4; + addToA(globalState.de.lo); +} + +void ADD_H(u8 opcode) { // 0x84 + globalState.pc++; + globalState.cycleCount += 4; + addToA(globalState.hl.hi); +} + +void ADD_L(u8 opcode) { // 0x85 + globalState.pc++; + globalState.cycleCount += 4; + addToA(globalState.hl.lo); +} + +void ADD_DHL(u8 opcode) { // 0x86 + globalState.pc++; + globalState.cycleCount += 8; + addToA(readByte(globalState.hl.v)); +} + +void ADD_n(u8 opcode) { // 0xC6 + u8 imm = readByte(++globalState.pc); + globalState.pc++; + globalState.cycleCount += 8; + addToA(imm); +} + +void adcToA(u8 number) { + int carry = getCarryFlag() ? 1 : 0; + int result = globalState.a + number + carry; + clearAllFlags(); + if((u8)result == 0) { + setZeroFlag(); + } + if(result > 0xff) { + setCarryFlag(); + } + if(((globalState.a & 0xf) + (number & 0xf) + carry) > 0xf) { + setHalfCarryFlag(); + } + globalState.a = (u8) result; +} + +// ADC Opcodes +void ADC_A(u8 opcode) { // 0x8f + globalState.pc++; + globalState.cycleCount += 4; + adcToA(globalState.a); +} + +void ADC_B(u8 opcode) { // 0x88 + globalState.pc++; + globalState.cycleCount += 4; + adcToA(globalState.bc.hi); +} + +void ADC_C(u8 opcode) { // 0x89 + globalState.pc++; + globalState.cycleCount += 4; + adcToA(globalState.bc.lo); +} + +void ADC_D(u8 opcode) { // 0x8a + globalState.pc++; + globalState.cycleCount += 4; + adcToA(globalState.de.hi); +} + +void ADC_E(u8 opcode) { // 0x8b + globalState.pc++; + globalState.cycleCount += 4; + adcToA(globalState.de.lo); +} + +void ADC_H(u8 opcode) { // 0x8c + globalState.pc++; + globalState.cycleCount += 4; + adcToA(globalState.hl.hi); +} + +void ADC_L(u8 opcode) { // 0x8d + globalState.pc++; + globalState.cycleCount += 4; + adcToA(globalState.hl.lo); +} + +void ADC_DHL(u8 opcode) { // 0x8e + globalState.pc++; + globalState.cycleCount += 8; + adcToA(readByte(globalState.hl.v)); +} + +void ADC_n(u8 opcode) { // 0xCe + u8 imm = readByte(++globalState.pc); + globalState.pc++; + globalState.cycleCount += 8; + adcToA(imm); +} + +void subToA(u8 number) { + int result = globalState.a - number; + int carryBits = globalState.a ^ number ^ result; + globalState.a = (u8)result; + clearAllFlags(); + setSubtractFlag(); + if((u8)result == 0) { + setZeroFlag(); + } + if((carryBits & 0x100) != 0) { + setCarryFlag(); + } + if((carryBits & 0x10) != 0) { + setHalfCarryFlag(); + } +} + +// SUB Opcodes +void SUB_A(u8 opcode) { // 0x97 + globalState.pc++; + globalState.cycleCount += 4; + subToA(globalState.a); +} + +void SUB_B(u8 opcode) { // 0x90 + globalState.pc++; + globalState.cycleCount += 4; + subToA(globalState.bc.hi); +} + +void SUB_C(u8 opcode) { // 0x91 + globalState.pc++; + globalState.cycleCount += 4; + subToA(globalState.bc.lo); +} + +void SUB_D(u8 opcode) { // 0x92 + globalState.pc++; + globalState.cycleCount += 4; + subToA(globalState.de.hi); +} + +void SUB_E(u8 opcode) { // 0x93 + globalState.pc++; + globalState.cycleCount += 4; + subToA(globalState.de.lo); +} + +void SUB_H(u8 opcode) { // 0x94 + globalState.pc++; + globalState.cycleCount += 4; + subToA(globalState.hl.hi); +} + +void SUB_L(u8 opcode) { // 0x95 + globalState.pc++; + globalState.cycleCount += 4; + subToA(globalState.hl.lo); +} + +void SUB_DHL(u8 opcode) { // 0x96 + globalState.pc++; + globalState.cycleCount += 8; + subToA(readByte(globalState.hl.v)); +} + +void SUB_n(u8 opcode) { // 0xD6 + u8 imm = readByte(++globalState.pc); + globalState.pc++; + globalState.cycleCount += 8; + subToA(imm); +} + +void sbcToA(u8 number) { + int carry = getCarryFlag() ? 1 : 0; + int result = globalState.a - number - carry; + clearAllFlags(); + setSubtractFlag(); + if((u8)result == 0) { + setZeroFlag(); + } + if(result < 0) { + setCarryFlag(); + } + if(((globalState.a & 0xf) - (number & 0xf) - carry) < 0) { + setHalfCarryFlag(); + } + globalState.a = (u8)result; +} + +// SBC Opcodes +void SBC_A(u8 opcode) { // 0x9f + globalState.pc++; + globalState.cycleCount += 4; + sbcToA(globalState.a); +} + +void SBC_B(u8 opcode) { // 0x98 + globalState.pc++; + globalState.cycleCount += 4; + sbcToA(globalState.bc.hi); +} + +void SBC_C(u8 opcode) { // 0x99 + globalState.pc++; + globalState.cycleCount += 4; + sbcToA(globalState.bc.lo); +} + +void SBC_D(u8 opcode) { // 0x9a + globalState.pc++; + globalState.cycleCount += 4; + sbcToA(globalState.de.hi); +} + +void SBC_E(u8 opcode) { // 0x9b + globalState.pc++; + globalState.cycleCount += 4; + sbcToA(globalState.de.lo); +} + +void SBC_H(u8 opcode) { // 0x9c + globalState.pc++; + globalState.cycleCount += 4; + sbcToA(globalState.hl.hi); +} + +void SBC_L(u8 opcode) { // 0x9d + globalState.pc++; + globalState.cycleCount += 4; + sbcToA(globalState.hl.lo); +} + +void SBC_DHL(u8 opcode) { // 0x9e + globalState.pc++; + globalState.cycleCount += 8; + sbcToA(readByte(globalState.hl.v)); +} + +void SBC_n(u8 opcode) { // 0xDe + u8 imm = readByte(++globalState.pc); + globalState.pc++; + globalState.cycleCount += 8; + sbcToA(imm); +} + +void andtoA(u8 number) { + u8 result = globalState.a & number; + globalState.a = result; + clearAllFlags(); + setHalfCarryFlag(); + if((u8)result == 0) { + setZeroFlag(); + } +} + +// AND Opcodes +void AND_A(u8 opcode) { // 0xa7 + globalState.pc++; + globalState.cycleCount += 4; + andtoA(globalState.a); +} + +void AND_B(u8 opcode) { // 0xa0 + globalState.pc++; + globalState.cycleCount += 4; + andtoA(globalState.bc.hi); +} + +void AND_C(u8 opcode) { // 0xa1 + globalState.pc++; + globalState.cycleCount += 4; + andtoA(globalState.bc.lo); +} + +void AND_D(u8 opcode) { // 0xa2 + globalState.pc++; + globalState.cycleCount += 4; + andtoA(globalState.de.hi); +} + +void AND_E(u8 opcode) { // 0xa3 + globalState.pc++; + globalState.cycleCount += 4; + andtoA(globalState.de.lo); +} + +void AND_H(u8 opcode) { // 0xa4 + globalState.pc++; + globalState.cycleCount += 4; + andtoA(globalState.hl.hi); +} + +void AND_L(u8 opcode) { // 0xa5 + globalState.pc++; + globalState.cycleCount += 4; + andtoA(globalState.hl.lo); +} + +void AND_DHL(u8 opcode) { // 0xa6 + globalState.pc++; + globalState.cycleCount += 8; + andtoA(readByte(globalState.hl.v)); +} + +void AND_n(u8 opcode) { // 0xe6 + u8 imm = readByte(++globalState.pc); + globalState.pc++; + globalState.cycleCount += 8; + andtoA(imm); +} + +void xorToA(u8 number) { + u8 result = globalState.a ^ number; + globalState.a = result; + clearAllFlags(); + if((u8)result == 0) { + setZeroFlag(); + } +} + +// XOR Opcodes +void XOR_A(u8 opcode) { // 0xaf + globalState.pc++; + globalState.cycleCount += 4; + xorToA(globalState.a); +} + +void XOR_B(u8 opcode) { // 0xa8 + globalState.pc++; + globalState.cycleCount += 4; + xorToA(globalState.bc.hi); +} + +void XOR_C(u8 opcode) { // 0xa9 + globalState.pc++; + globalState.cycleCount += 4; + xorToA(globalState.bc.lo); +} + +void XOR_D(u8 opcode) { // 0xaa + globalState.pc++; + globalState.cycleCount += 4; + xorToA(globalState.de.hi); +} + +void XOR_E(u8 opcode) { // 0xab + globalState.pc++; + globalState.cycleCount += 4; + xorToA(globalState.de.lo); +} + +void XOR_H(u8 opcode) { // 0xac + globalState.pc++; + globalState.cycleCount += 4; + xorToA(globalState.hl.hi); +} + +void XOR_L(u8 opcode) { // 0xad + globalState.pc++; + globalState.cycleCount += 4; + xorToA(globalState.hl.lo); +} + +void XOR_DHL(u8 opcode) { // 0xae + globalState.pc++; + globalState.cycleCount += 8; + xorToA(readByte(globalState.hl.v)); +} + +void XOR_n(u8 opcode) { // 0xee + u8 imm = readByte(++globalState.pc); + globalState.pc++; + globalState.cycleCount += 8; + xorToA(imm); +} + +void orToA(u8 number) { + u8 result = globalState.a | number; + globalState.a = result; + clearAllFlags(); + if((u8)result == 0) { + setZeroFlag(); + } +} + +// OR Opcodes +void OR_A(u8 opcode) { // 0xb7 + globalState.pc++; + globalState.cycleCount += 4; + orToA(globalState.a); +} + +void OR_B(u8 opcode) { // 0xb0 + globalState.pc++; + globalState.cycleCount += 4; + orToA(globalState.bc.hi); +} + +void OR_C(u8 opcode) { // 0xb1 + globalState.pc++; + globalState.cycleCount += 4; + orToA(globalState.bc.lo); +} + +void OR_D(u8 opcode) { // 0xb2 + globalState.pc++; + globalState.cycleCount += 4; + orToA(globalState.de.hi); +} + +void OR_E(u8 opcode) { // 0xb3 + globalState.pc++; + globalState.cycleCount += 4; + orToA(globalState.de.lo); +} + +void OR_H(u8 opcode) { // 0xb4 + globalState.pc++; + globalState.cycleCount += 4; + orToA(globalState.hl.hi); +} + +void OR_L(u8 opcode) { // 0xb5 + globalState.pc++; + globalState.cycleCount += 4; + orToA(globalState.hl.lo); +} + +void OR_DHL(u8 opcode) { // 0xb6 + globalState.pc++; + globalState.cycleCount += 8; + orToA(readByte(globalState.hl.v)); +} + +void OR_n(u8 opcode) { // 0xf6 + u8 imm = readByte(++globalState.pc); + globalState.pc++; + globalState.cycleCount += 8; + orToA(imm); +} + +void cpToA(u8 number) { + clearAllFlags(); + setSubtractFlag(); + if(globalState.a < number) { + setCarryFlag(); + } + if(globalState.a == number) { + setZeroFlag(); + } + if(((globalState.a - number) & 0xf) > (globalState.a & 0xf)) { + setHalfCarryFlag(); + } +} + +// CP Opcodes +void CP_A(u8 opcode) { // 0xbf + globalState.pc++; + globalState.cycleCount += 4; + cpToA(globalState.a); +} + +void CP_B(u8 opcode) { // 0xb8 + globalState.pc++; + globalState.cycleCount += 4; + cpToA(globalState.bc.hi); +} + +void CP_C(u8 opcode) { // 0xb9 + globalState.pc++; + globalState.cycleCount += 4; + cpToA(globalState.bc.lo); +} + +void CP_D(u8 opcode) { // 0xba + globalState.pc++; + globalState.cycleCount += 4; + cpToA(globalState.de.hi); +} + +void CP_E(u8 opcode) { // 0xbb + globalState.pc++; + globalState.cycleCount += 4; + cpToA(globalState.de.lo); +} + +void CP_H(u8 opcode) { // 0xbc + globalState.pc++; + globalState.cycleCount += 4; + cpToA(globalState.hl.hi); +} + +void CP_L(u8 opcode) { // 0xbd + globalState.pc++; + globalState.cycleCount += 4; + cpToA(globalState.hl.lo); +} + +void CP_DHL(u8 opcode) { // 0xbe + globalState.pc++; + globalState.cycleCount += 8; + cpToA(readByte(globalState.hl.v)); +} + +void CP_n(u8 opcode) { // 0xfe + u8 imm = readByte(++globalState.pc); + globalState.pc++; + globalState.cycleCount += 8; + cpToA(imm); +} + +void DecodeCB(u8 opcode) { + globalState.pc += 1; + u8 newOpcode = readByte(globalState.pc); + opcode_cbs[newOpcode](newOpcode); +} + +u8 increment(u8 in) { + u8 result = in + (u8)1; + if(getCarryFlag()) { + clearAllFlags(); + setCarryFlag(); + } else { + clearAllFlags(); + } + if(result == 0) { + setZeroFlag(); + } + if((result & 0x0f) == 0x00) { + setHalfCarryFlag(); + } + return result; +} + +void INC_A(u8 opcode) { // 0x3c + globalState.a = increment(globalState.a); + globalState.pc++; + globalState.cycleCount += 4; +} + +void INC_B(u8 opcode) { // 0x04 + globalState.bc.hi = increment(globalState.bc.hi); + globalState.pc++; + globalState.cycleCount += 4; +} + +void INC_C(u8 opcode) { // 0x0c + globalState.bc.lo = increment(globalState.bc.lo); + globalState.pc++; + globalState.cycleCount += 4; +} + +void INC_D(u8 opcode) { // 0x14 + globalState.de.hi = increment(globalState.de.hi); + globalState.pc++; + globalState.cycleCount += 4; +} + +void INC_E(u8 opcode) { // 0x1c + globalState.de.lo = increment(globalState.de.lo); + globalState.pc++; + globalState.cycleCount += 4; +} + +void INC_H(u8 opcode) { // 0x24 + globalState.hl.hi = increment(globalState.hl.hi); + globalState.pc++; + globalState.cycleCount += 4; +} + +void INC_L(u8 opcode) { // 0x2c + globalState.hl.lo = increment(globalState.hl.lo); + globalState.pc++; + globalState.cycleCount += 4; +} + +void INC_DHL(u8 opcode) { // 0x34 + // todo this one is hard. + u8 v = readByte(globalState.hl.v); + writeByte(increment(v), globalState.hl.v); + globalState.pc++; + globalState.cycleCount += 12; +} + +u8 decrement(u8 in) { + u8 result = in - (u8)1; + if(getCarryFlag()) { + clearAllFlags(); + setCarryFlag(); + } else { + clearAllFlags(); + } + setSubtractFlag(); + if(result == 0) { + setZeroFlag(); + } + if((result & 0xf) == 0xf) { + setHalfCarryFlag(); + } + return result; +} + +void DEC_A(u8 opcode) { // 0x3d + globalState.a = decrement(globalState.a); + globalState.pc++; + globalState.cycleCount += 4; +} + +void DEC_B(u8 opcode) { // 0x05 + globalState.bc.hi = decrement(globalState.bc.hi); + globalState.pc++; + globalState.cycleCount += 4; +} + +void DEC_C(u8 opcode) { // 0x0d + globalState.bc.lo = decrement(globalState.bc.lo); + globalState.pc++; + globalState.cycleCount += 4; +} + +void DEC_D(u8 opcode) { // 0x15 + globalState.de.hi = decrement(globalState.de.hi); + globalState.pc++; + globalState.cycleCount += 4; +} + +void DEC_E(u8 opcode) { // 0x1d + globalState.de.lo = decrement(globalState.de.lo); + globalState.pc++; + globalState.cycleCount += 4; +} + +void DEC_H(u8 opcode) { // 0x25 + globalState.hl.hi = decrement(globalState.hl.hi); + globalState.pc++; + globalState.cycleCount += 4; +} + +void DEC_L(u8 opcode) { // 0x2d + globalState.hl.lo = decrement(globalState.hl.lo); + globalState.pc++; + globalState.cycleCount += 4; +} + +void DEC_DHL(u8 opcode) { // 0x35 + // todo this one is hard. + u8 v = readByte(globalState.hl.v); + writeByte(decrement(v), globalState.hl.v); + globalState.pc++; + globalState.cycleCount += 12; +} + +void addToHl(u16 number) { + int result = globalState.hl.v + number; + if(getZeroFlag()) { + clearAllFlags(); + setZeroFlag(); + } else { + clearAllFlags(); + } + if(result & 0x10000) { + setCarryFlag(); + } + if((globalState.hl.v ^ number ^ (result & 0xffff)) & 0x1000) { + setHalfCarryFlag(); + } + globalState.hl.v = (u16)result; +} + +void ADD_HL_BC(u8 opcode) { // 09 + globalState.pc++; + globalState.cycleCount += 8; + addToHl(globalState.bc.v); +} + +void ADD_HL_DE(u8 opcode) { // 19 + globalState.pc++; + globalState.cycleCount += 8; + addToHl(globalState.de.v); +} + +void ADD_HL_HL(u8 opcode) { // 29 + globalState.pc++; + globalState.cycleCount += 8; + addToHl(globalState.hl.v); +} + +void ADD_HL_SP(u8 opcode) { // 39 + globalState.pc++; + globalState.cycleCount += 8; + addToHl(globalState.sp); +} + +void ADD_SP_n(u8 opcode) { // E8 + s8 number = readByte(++globalState.pc); + globalState.pc++; + globalState.cycleCount += 16; + int result = globalState.sp + number; + clearAllFlags(); + if(((globalState.sp ^ number ^ (result & 0xffff)) & 0x100) == 0x100) { + setCarryFlag(); + } + if(((globalState.sp ^ number ^ (result & 0xffff)) & 0x10) == 0x10) { + setHalfCarryFlag(); + } + globalState.sp = (u16)result; +} + +// 16 bit incs +void INC_BC(u8 opcode) { // 03 + globalState.pc++; + globalState.cycleCount += 8; + globalState.bc.v++; +} + +void INC_DE(u8 opcode) { // 13 + globalState.pc++; + globalState.cycleCount += 8; + globalState.de.v++; +} + +void INC_HL(u8 opcode) { // 23 + globalState.pc++; + globalState.cycleCount += 8; + globalState.hl.v++; +} + +void INC_SP(u8 opcode) { // 33 + globalState.pc++; + globalState.cycleCount += 8; + globalState.sp++; +} + +// 16 bit decs +void DEC_BC(u8 opcode) { // 0B + globalState.pc++; + globalState.cycleCount += 8; + globalState.bc.v--; +} + +void DEC_DE(u8 opcode) { // 1B + globalState.pc++; + globalState.cycleCount += 8; + globalState.de.v--; +} + +void DEC_HL(u8 opcode) { // 2B + globalState.pc++; + globalState.cycleCount += 8; + globalState.hl.v--; +} + +void DEC_SP(u8 opcode) { // 3B + globalState.pc++; + globalState.cycleCount += 8; + globalState.sp--; +} + +void DAA(u8 opcode) { // 0x27 + globalState.pc++; + globalState.cycleCount += 4; + u8 a = globalState.a; + + if(!getSubtractFlag()) { + if(getCarryFlag() || a > 0x99) { + a += 0x60; + setCarryFlag(); + } + if(getHalfCarryFlag() || (a & 0x0f) > 0x09) { + a += 0x6; + } + } else { + if(getCarryFlag()) { + a -= 0x60; + } + if(getHalfCarryFlag()) { + a -= 0x6; + } + } + clearZeroFlag(); + clearHalfCarryFlag(); + if(a == 0) { + setZeroFlag(); + } + globalState.a = (u8)a; +} + +void CPL(u8 opcode) { // 0x2f + globalState.pc++; + globalState.cycleCount += 4; + globalState.a = ~globalState.a; + setHalfCarryFlag(); + setSubtractFlag(); +} + +void CCF(u8 opcode) { // 0x3f + globalState.pc++; + globalState.cycleCount += 4; + if(getCarryFlag()) { + clearCarryFlag(); + } else { + setCarryFlag(); + } + clearHalfCarryFlag(); + clearSubtractFlag(); +} + +void SCF(u8 opcode) { // 0x37 + globalState.pc++; + globalState.cycleCount += 4; + clearHalfCarryFlag(); + clearSubtractFlag(); + setCarryFlag(); +} + +void NOP(u8 opcode) { // 00 + globalState.pc++; + globalState.cycleCount += 4; +} + + +void HALT(u8 opcode) { // 76 + globalState.cycleCount += 4; + globalState.pc++; + //if(globalState.ime) { + globalState.halt = true; + //} + +} + +void STOP(u8 opcode) { // 10 (00) + printf("stop not yet implemented\n"); + assert(false); +} + +void DI(u8 opcode) { // F3 + // todo instruction after bs + globalState.ime = 0; + globalState.cycleCount += 4; + globalState.pc++; +// printf("di not yet implemented\n"); +// assert(false); +} + +void EI(u8 opcode) { // FB + globalState.ime = 1; + globalState.cycleCount += 4; + globalState.pc++; +} + +void RLCA(u8 opcode) { // 07 + globalState.pc++; + globalState.cycleCount += 4; + globalState.a = rlcReg(globalState.a, true); +} + +void RLA(u8 opcode) { // 17 + globalState.pc++; + globalState.cycleCount += 4; + globalState.a = rlReg(globalState.a, true); +} + +void RRCA(u8 opcode) { // 0x0f + globalState.pc++; + globalState.cycleCount += 4; + globalState.a = rrc(globalState.a, true); +} + +void RRA(u8 opcode) { // 0x1f + globalState.pc++; + globalState.cycleCount += 4; + globalState.a = rr(globalState.a, true); +} + +void JP_nn(u8 opcodes) { // 0xc3 + globalState.pc++; + u16 addr = readU16(globalState.pc); + globalState.pc += 2; + globalState.cycleCount += 12; + globalState.pc = addr; +} + +void JP_NZ_nn(u8 opcodes) { // 0xc2 + globalState.pc++; + u16 addr = readU16(globalState.pc); + globalState.pc += 2; + if(!getZeroFlag()) { + globalState.pc = addr; + } + globalState.cycleCount += 12; +} + +void JP_Z_nn(u8 opcodes) { // 0xca + globalState.pc++; + u16 addr = readU16(globalState.pc); + globalState.pc += 2; + if(getZeroFlag()) { + globalState.pc = addr; + } + globalState.cycleCount += 12; +} + +void JP_NC_nn(u8 opcodes) { // 0xd2 + globalState.pc++; + u16 addr = readU16(globalState.pc); + globalState.pc += 2; + if(!getCarryFlag()) { + globalState.pc = addr; + } + globalState.cycleCount += 12; +} + +void JP_C_nn(u8 opcodes) { // 0xda + globalState.pc++; + u16 addr = readU16(globalState.pc); + globalState.pc += 2; + if(getCarryFlag()) { + globalState.pc = addr; + } + globalState.cycleCount += 12; +} + +void JP_HL(u8 opcodes) { // 0xe9 + globalState.pc = globalState.hl.v; + globalState.cycleCount += 4; +} + +void JR_n(u8 opcode) { // 18 + s8 imm = readByte(++globalState.pc); + globalState.pc++; + globalState.pc += imm; + globalState.cycleCount += 8; +} + +void JR_NZ(u8 opcode) { // 20 + s8 imm = readByte(++globalState.pc); + globalState.pc++; + if(!getZeroFlag()){ + globalState.pc += imm; + } + globalState.cycleCount += 8; +} + +void JR_Z(u8 opcode) { // 28 + s8 imm = readByte(++globalState.pc); + globalState.pc++; + if(getZeroFlag()){ + globalState.pc += imm; + } + globalState.cycleCount += 8; +} + +void JR_NC(u8 opcode) { // 30 + s8 imm = readByte(++globalState.pc); + globalState.pc++; + if(!getCarryFlag()){ + globalState.pc += imm; + } + globalState.cycleCount += 8; +} + +void JR_C(u8 opcode) { // 38 + s8 imm = readByte(++globalState.pc); + globalState.pc++; + if(getCarryFlag()){ + globalState.pc += imm; + } + globalState.cycleCount += 8; +} + + +void CALL_nn(u8 opcode) { // 0xCD + globalState.pc++; + u16 addr = readU16(globalState.pc); + //printf("call address 0x%x\n", addr); + globalState.pc += 2; + writeU16(globalState.pc, globalState.sp - (u16)2); + globalState.sp -= 2; + globalState.pc = addr; + globalState.cycleCount += 12; +} + +void CALL_NZ(u8 opcode) { // 0xC4 + globalState.pc++; + u16 addr = readU16(globalState.pc); + globalState.pc += 2; + if(!getZeroFlag()) { + writeU16(globalState.pc, globalState.sp - (u16)2); + globalState.sp -= 2; + globalState.pc = addr; + } + globalState.cycleCount += 12; +} + +void CALL_Z(u8 opcode) { // 0xCc + globalState.pc++; + u16 addr = readU16(globalState.pc); + globalState.pc += 2; + if(getZeroFlag()) { + writeU16(globalState.pc, globalState.sp - (u16)2); + globalState.sp -= 2; + globalState.pc = addr; + } + globalState.cycleCount += 12; +} + + +void CALL_NC(u8 opcode) { // 0d4 + globalState.pc++; + u16 addr = readU16(globalState.pc); + globalState.pc += 2; + if(!getCarryFlag()) { + writeU16(globalState.pc, globalState.sp - (u16)2); + globalState.sp -= 2; + globalState.pc = addr; + } + globalState.cycleCount += 12; +} + +void CALL_C(u8 opcode) { // 0xdc + globalState.pc++; + u16 addr = readU16(globalState.pc); + globalState.pc += 2; + if(getCarryFlag()) { + writeU16(globalState.pc, globalState.sp - (u16)2); + globalState.sp -= 2; + globalState.pc = addr; + } + globalState.cycleCount += 12; +} + +void REST_00(u8 opcode) { // 0xc7 + globalState.pc++; + writeU16(globalState.pc, globalState.sp - (u16)2); + globalState.sp -= 2; + globalState.pc = 0x0000; + globalState.cycleCount += 32; +} + +void REST_08(u8 opcode) { // 0xcf + globalState.pc++; + writeU16(globalState.pc, globalState.sp - (u16)2); + globalState.sp -= 2; + globalState.pc = 0x0008; + globalState.cycleCount += 32; +} + +void REST_10(u8 opcode) { // 0xd7 + globalState.pc++; + writeU16(globalState.pc, globalState.sp - (u16)2); + globalState.sp -= 2; + globalState.pc = 0x0010; + globalState.cycleCount += 32; +} + +void REST_18(u8 opcode) { // 0xdf + globalState.pc++; + writeU16(globalState.pc, globalState.sp - (u16)2); + globalState.sp -= 2; + globalState.pc = 0x0018; + globalState.cycleCount += 32; +} + +void REST_20(u8 opcode) { // 0xe7 + globalState.pc++; + writeU16(globalState.pc, globalState.sp - (u16)2); + globalState.sp -= 2; + globalState.pc = 0x0020; + globalState.cycleCount += 32; +} + +void REST_28(u8 opcode) { // 0xef + globalState.pc++; + writeU16(globalState.pc, globalState.sp - (u16)2); + globalState.sp -= 2; + globalState.pc = 0x0028; + globalState.cycleCount += 32; +} + +void REST_30(u8 opcode) { // 0xf7 + globalState.pc++; + writeU16(globalState.pc, globalState.sp - (u16)2); + globalState.sp -= 2; + globalState.pc = 0x0030; + globalState.cycleCount += 32; +} + +void REST_38(u8 opcode) { // 0xff + globalState.pc++; + writeU16(globalState.pc, globalState.sp - (u16)2); + globalState.sp -= 2; + globalState.pc = 0x0038; + globalState.cycleCount += 32; +} + +void RET(u8 opcode) { // 0xc9 + globalState.pc++; + globalState.cycleCount += 8; + globalState.pc = readU16(globalState.sp); + globalState.sp += 2; +} + +void RET_NZ(u8 opcode) { // 0xc0 + globalState.pc++; + globalState.cycleCount += 8; + if(!getZeroFlag()) { + globalState.pc = readU16(globalState.sp); + globalState.sp += 2; + } +} + +void RET_Z(u8 opcode) { // 0xc8 + globalState.pc++; + globalState.cycleCount += 8; + if(getZeroFlag()) { + globalState.pc = readU16(globalState.sp); + globalState.sp += 2; + } +} + +void RET_NC(u8 opcode) { // 0xd0 + globalState.pc++; + globalState.cycleCount += 8; + if(!getCarryFlag()) { + globalState.pc = readU16(globalState.sp); + globalState.sp += 2; + } +} + +void RET_C(u8 opcode) { // 0xd8 + globalState.pc++; + globalState.cycleCount += 8; + if(getCarryFlag()) { + globalState.pc = readU16(globalState.sp); + globalState.sp += 2; + } +} + +void RETI(u8 opcode) { // 0xd9 + globalState.pc++; + globalState.cycleCount += 8; + globalState.pc = readU16(globalState.sp); + globalState.sp += 2; + globalState.ime = 1; +} + +// normal opcode handler table +static OpcodeHandler* opcodes[256] = + {NOP, LD_BC_nn, LD_DBC_A, INC_BC, INC_B, DEC_B, LD_B_n, RLCA, // 0x0 - 0x7 + LD_Dnn_SP, ADD_HL_BC, LD_A_DBC, DEC_BC, INC_C, DEC_C, LD_C_n, RRCA, // 0x8 - 0xf + STOP, LD_DE_nn, LD_DDE_A, INC_DE, INC_D, DEC_D, LD_D_n, RLA, // 0x10 - 0x17 + JR_n, ADD_HL_DE, LD_A_DDE, DEC_DE, INC_E, DEC_E, LD_E_n, RRA, // 0x18 - 0x1f + JR_NZ, LD_HL_nn, LDI_DHL_A, INC_HL, INC_H, DEC_H, LD_H_n, DAA, // 0x20 - 0x27 + JR_Z, ADD_HL_HL, LDI_A_DHL, DEC_HL, INC_L, DEC_L, LD_L_n, CPL, // 0x28 - 0x2f + JR_NC, LD_SP_nn, LDD_DHL_A, INC_SP, INC_DHL, DEC_DHL, LD_DHL_n, SCF, // 0x30 - 0x37 + JR_C, ADD_HL_SP, LDD_A_DHL, DEC_SP, INC_A, DEC_A, LD_A_n, CCF, // 0x38 - 0x3f + LD_B_B, LD_B_C, LD_B_D, LD_B_E, LD_B_H, LD_B_L, LD_B_DHL, LD_B_A, // 0x40 - 0x47 + LD_C_B, LD_C_C, LD_C_D, LD_C_E, LD_C_H, LD_C_L, LD_C_DHL, LD_C_A, // 0x48 - 0x4f + LD_D_B, LD_D_C, LD_D_D, LD_D_E, LD_D_H, LD_D_L, LD_D_DHL, LD_D_A, // 0x50 - 0x57 + LD_E_B, LD_E_C, LD_E_D, LD_E_E, LD_E_H, LD_E_L, LD_E_DHL, LD_E_A, // 0x58 - 0x5f + LD_H_B, LD_H_C, LD_H_D, LD_H_E, LD_H_H, LD_H_L, LD_H_DHL, LD_H_A, // 0x60 - 0x67 + LD_L_B, LD_L_C, LD_L_D, LD_L_E, LD_L_H, LD_L_L, LD_L_DHL, LD_L_A, // 0x68 - 0x6f + LD_DHL_B, LD_DHL_C, LD_DHL_D, LD_DHL_E, LD_DHL_H, LD_DHL_L, HALT, LD_DHL_A, // 0x70 - 0x77 + LD_A_B, LD_A_C, LD_A_D, LD_A_E, LD_A_H, LD_A_L, LD_A_DHL, LD_A_A , // 0x78 - 0x7f + ADD_B, ADD_C, ADD_D, ADD_E, ADD_H, ADD_L, ADD_DHL, ADD_A, // 0x80 - 0x87 + ADC_B, ADC_C, ADC_D, ADC_E, ADC_H, ADC_L, ADC_DHL, ADC_A, // 0x88 - 0x8f + SUB_B, SUB_C, SUB_D, SUB_E, SUB_H, SUB_L, SUB_DHL, SUB_A, // 0x90 - 0x97 + SBC_B, SBC_C, SBC_D, SBC_E, SBC_H, SBC_L, SBC_DHL, SBC_A, // 0x98 - 0x9f + AND_B, AND_C, AND_D, AND_E, AND_H, AND_L, AND_DHL, AND_A, // 0xa0 - 0xa7 + XOR_B, XOR_C, XOR_D, XOR_E, XOR_H, XOR_L, XOR_DHL, XOR_A, // 0xa8 - 0xaf + OR_B, OR_C, OR_D, OR_E, OR_H, OR_L, OR_DHL, OR_A, // 0xb0 - 0xb7 + CP_B, CP_C, CP_D, CP_E, CP_H, CP_L, CP_DHL, CP_A, // 0xb8 - 0xbf + RET_NZ, POP_BC, JP_NZ_nn, JP_nn, CALL_NZ, PUSH_BC, ADD_n, REST_00, // 0xc0 - 0xc7 + RET_Z, RET, JP_Z_nn, DecodeCB, CALL_Z, CALL_nn, ADC_n, REST_08, // 0xc8 - 0xcf + RET_NC, POP_DE, JP_NC_nn, invHandler, CALL_NC, PUSH_DE, SUB_n, REST_10, // 0xd0 - 0xd7 + RET_C, RETI, JP_C_nn, invHandler, CALL_C, invHandler, SBC_n, REST_18, // 0xd8 - 0xdf + LD_FF00_n_A, POP_HL, LD_FF00_C_A, invHandler, invHandler, PUSH_HL, AND_n, REST_20, // 0xe0 - 0xe7 + ADD_SP_n, JP_HL, LD_Dnn_A, invHandler, invHandler, invHandler, XOR_n, REST_28, // 0xe8 - 0xef + LD_A_FF00_n, POP_AF, LD_A_FF00_C, DI, invHandler, PUSH_AF, OR_n, REST_30, // 0xf0 - 0xf7 + LD_HL_SP_n, LD_SP_HL, LD_A_Dnn, EI, invHandler, invHandler, CP_n, REST_38}; // 0xf8 - 0xff + + +// reset the globalState structure, including registers and timers +void resetCpu() { + globalState.f = 0x1; // or 0x11? ( + globalState.a = 0xb0; // maybe f, a swap?? + globalState.bc.v = 0x0013; + globalState.de.v = 0x00d8; + globalState.hl.v = 0x014d; + globalState.sp = 0xfffe; + globalState.pc = 0x0; + globalState.halt = false; + globalState.cycleCount = 0; + globalState.divOffset = 0; + globalState.timSubcount = 0; +} + +// set the globalState so the next call to step() will run an ISR +void interrupt(u16 addr) { + globalState.ime = 0; // disable interrupts + writeU16(globalState.pc, globalState.sp - (u16)2); // push pc to stack + globalState.sp -= 2; // push pc to stack + globalState.cycleCount += 12; // timing + globalState.pc = addr; // jump to ISR +} + +// timer speeds: once this number of clock cycles has elapsed, the timer ticks. +static u32 timReset[4] = {(1 << 10), (1 << 4), (1 << 6), (1 << 8)}; + +// step the CPU 1 instruction +// returns number of clock cycles +u32 cpuStep() { + uint64_t oldCycleCount = globalState.cycleCount; + + // update div register + globalMemState.ioRegs[IO_DIV] = (u8)((globalState.cycleCount - globalState.divOffset) >> 8); + + // execute, if we aren't halted. + if(!globalState.halt) { + // fetch opcode + u8 opcode = readByte(globalState.pc); + // execute opcode + opcodes[opcode](opcode); + } + + assert(false); + // interrupts + if(globalState.ime && globalMemState.upperRam[0x7f] && globalMemState.ioRegs[IO_IF]) { + + // mask interrupts with the interrupt enable register at 0xffff. + u8 interrupts = globalMemState.upperRam[0x7f] & globalMemState.ioRegs[IO_IF]; + + if(interrupts & 0x01) { + globalMemState.ioRegs[IO_IF] &= ~1; + interrupt(VBLANK_INTERRUPT); + globalState.halt = false; + } else if(interrupts & 0x02) { + globalMemState.ioRegs[IO_IF] &= ~2; + interrupt(LCDC_INTERRUPT); + globalState.halt = false; + } else if(interrupts & 0x04) { + globalMemState.ioRegs[IO_IF] &= ~4; + interrupt(TIMER_INTERRUPT); + globalState.halt = false; + } else if(interrupts & 0x08) { + globalMemState.ioRegs[IO_IF] &= ~8; + interrupt(SERIAL_INTERRUPT); + globalState.halt = false; + } else if(interrupts & 0x10) { + globalMemState.ioRegs[IO_IF] &= ~0x10; + interrupt(HIGH_TO_LOW_P10_P13); + globalState.halt = false; + } + } + + // even if we have IME off, and we're halted, we're supposed to check IF and IE register + // this won't fire an interrupt, but will get us out of halt + // this behavior isn't well documented, but was required to pass cpu_instr.gb + // (though none of the games seem to need it...) + if(globalState.halt) { + globalState.cycleCount += 800; // just to keep ticking the timer... + u8 interrupts = globalMemState.upperRam[0x7f] & globalMemState.ioRegs[IO_IF]; + if(interrupts & 0x01) { + globalState.halt = false; + } else if(interrupts & 0x02) { + globalState.halt = false; + } else if(interrupts & 0x04) { + globalState.halt = false; + } else if(interrupts & 0x08) { + globalState.halt = false; + } else if(interrupts & 0x10) { + globalState.halt = false; + } + } + + + // cycle count + uint64_t cyclesThisIteration = globalState.cycleCount - oldCycleCount; + + // update timer + u8 tac = globalMemState.ioRegs[IO_TAC]; + bool ten = ((tac >> 2) & 1) != 0; // timer enable? + if(ten) { + u8 tclk = (tac & (u8)3); // timer speed + globalState.timSubcount += cyclesThisIteration; + if(globalState.timSubcount >= timReset[tclk]) { // timer tick + globalState.timSubcount = 0; + u8 timv = globalMemState.ioRegs[IO_TIMA]; // check for overflow + if(timv == 255) { + globalMemState.ioRegs[IO_IF] |= 4; // set interrupt + globalMemState.ioRegs[IO_TIMA] = globalMemState.ioRegs[IO_TMA]; // reset + } else { + globalMemState.ioRegs[IO_TIMA] = timv + (u8)1; // increment. + } + } + } + + return (u32)cyclesThisIteration; +} + +bool getZeroFlag() { + return (globalState.f & 0x80) != 0; +} + +bool getSubtractFlag() { + return (globalState.f & 0x40) != 0; +} + +bool getHalfCarryFlag() { + return (globalState.f & 0x20) != 0; +} + +bool getCarryFlag() { + return (globalState.f & 0x10) != 0; +} + +void setZeroFlag() { + globalState.f = globalState.f | (u8)0x80; +} + +void setSubtractFlag() { + globalState.f = globalState.f | (u8)0x40; +} + +void setHalfCarryFlag() { + globalState.f = globalState.f | (u8)0x20; +} + +void setCarryFlag() { + globalState.f = globalState.f | (u8)0x10; +} + +void clearZeroFlag(){ + globalState.f = globalState.f & ~((u8)0x80); +} + +void clearSubtractFlag(){ + globalState.f = globalState.f & ~((u8)0x40); +} + +void clearHalfCarryFlag(){ + globalState.f = globalState.f & ~((u8)0x20); +} + +void clearCarryFlag(){ + globalState.f = globalState.f & ~((u8)0x10); +} + +void clearAllFlags() { + clearZeroFlag(); + clearSubtractFlag(); + clearHalfCarryFlag(); + clearCarryFlag(); +} \ No newline at end of file
diff -r 1f728d08b3a7 -r c9afe1a7b423 cpu.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/cpu.h Sun Jan 13 19:00:10 2019 +0000 @@ -0,0 +1,49 @@ +#ifndef GBC_CPU_H +#define GBC_CPU_H + +#include "types.h" + +// 16 bit register +union reg { + u16 v; + struct { + u8 lo; + u8 hi; + }; +}; + +struct CpuState { + // registers + reg bc, de, hl; + u8 f; + u8 a; + u16 sp, pc; + + bool halt; + uint64_t cycleCount; + uint64_t divOffset; + u8 ime; + u32 timSubcount; +}; + +// external interface: +extern CpuState globalState; +void resetCpu(); // reinitialize the cpu +u32 cpuStep(); // step 1 instruction, returns number of clock cycles elapsed + + +bool getZeroFlag(); +bool getSubtractFlag(); +bool getHalfCarryFlag(); +bool getCarryFlag(); +void setZeroFlag(); +void clearZeroFlag(); +void setSubtractFlag(); +void clearSubtractFlag(); +void setHalfCarryFlag(); +void clearHalfCarryFlag(); +void setCarryFlag(); +void clearCarryFlag(); +void clearAllFlags(); + +#endif //GBC_CPU_H \ No newline at end of file
diff -r 1f728d08b3a7 -r c9afe1a7b423 graphics_display.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/graphics_display.cpp Sun Jan 13 19:00:10 2019 +0000 @@ -0,0 +1,42 @@ +// SDL display code + +#include "types.h" +//static SDL_Window* window; +//static SDL_Renderer* renderer; +//static SDL_Texture* tex; +static u8* fb; + +#define SCALE 4 // pixel scaling + +void initGraphics(u8* frameBuffer) { +// SDL_Init(SDL_INIT_VIDEO); +// window = SDL_CreateWindow("WORM-BOY", SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, 160 * SCALE, 144 * SCALE, SDL_WINDOW_OPENGL); +// renderer = SDL_CreateRenderer(window, -1, SDL_RENDERER_ACCELERATED); +// tex = SDL_CreateTexture(renderer, SDL_PIXELFORMAT_BGRA8888, SDL_TEXTUREACCESS_STREAMING, 160 * SCALE, 144 * SCALE); + fb = frameBuffer; +} + +void updateGraphics() { +// int pitch; +// u8* sdlFB; +// SDL_LockTexture(tex, nullptr, (void**)(&sdlFB), &pitch); +// for(u32 xIn = 0; xIn < 160; xIn++) { +// for(u32 yIn = 0; yIn < 144; yIn++) { +// u32 outCoord = (xIn * SCALE) + (yIn * SCALE * 160 * SCALE); +// u32 inCoord = xIn + yIn * 160; +// for(u32 xScale = 0; xScale < SCALE; xScale++) { +// for(u32 yScale = 0; yScale < SCALE; yScale++) { +// u32 outScaled = outCoord + xScale + (yScale * SCALE * 160); +// sdlFB[outScaled*4 + 0] = 255; +// sdlFB[outScaled*4 + 1] = fb[inCoord]; +// sdlFB[outScaled*4 + 2] = fb[inCoord]; +// sdlFB[outScaled*4 + 3] = fb[inCoord]; +// } +// } +// } +// } +// SDL_UnlockTexture(tex); +// SDL_RenderCopy(renderer, tex, nullptr, nullptr); +// SDL_RenderPresent(renderer); +} +
diff -r 1f728d08b3a7 -r c9afe1a7b423 graphics_display.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/graphics_display.h Sun Jan 13 19:00:10 2019 +0000 @@ -0,0 +1,9 @@ +#ifndef GBC_GRAPHICS_DISPLAY_H +#define GBC_GRAPHICS_DISPLAY_H +#include "types.h" + +void initGraphics(u8* frameBuffer); // initialize graphics +void updateGraphics(); // draw whatever is in the fb. format is array of u8's, 160x140, intensity. + + +#endif //GBC_GRAPHICS_DISPLAY_H
diff -r 1f728d08b3a7 -r c9afe1a7b423 main.cpp --- a/main.cpp Thu Oct 04 12:37:25 2018 +0000 +++ b/main.cpp Sun Jan 13 19:00:10 2019 +0000 @@ -2,6 +2,12 @@ #include <math.h> #include "main.h" #include "SDFileSystem.h" +#include "platform.h" +#include "types.h" +#include "mem.h" +#include "cpu.h" +#include "graphics_display.h" +#include "video.h" #define TEXT_LEVEL 5 @@ -26,6 +32,8 @@ //DigitalOut sout(D8); //sync PA_9 //DigitalOut vout(D7); //video PA_8 +bool wantSwap = false; + DigitalOut dac0(PA_4); DigitalOut dac1(PA_5); DigitalOut dac2(PA_6); @@ -34,9 +42,9 @@ DigitalOut user_led(PB_14); int led_state = 1; -// trigger horizontal line draw -Ticker t; +//uint8_t im_line_va_1[H_RES*V_RES]; +uint8_t* im_line_va_1; uint8_t bl_line_s[H_RES]; //blank line sync buffer uint8_t bl_line_v[H_RES]; //blank line video buffer uint8_t vb_line_s[H_RES]; //vertical sync, sync buffer @@ -45,12 +53,13 @@ //buffers uint8_t current_frame[YL][XL / 2]; -uint8_t im_line_va_1[H_RES*V_RES]; + +volatile u8 bufferSelect = 0; uint8_t im_line_va_2[H_RES*V_RES]; +//uint8_t* im_line_va_2; //double buffered drawing -uint8_t* im_line_va; -uint8_t* im_back; +u8* im_line_vas[2]; uint16_t l=0; //current line of scan uint32_t tics = 0; //timer @@ -68,7 +77,7 @@ { for(int i = 0; i < H_RES; i++) for(int j = 0; j < V_RES; j++) - im_line_va[i+j*H_RES] = 0; + im_line_vas[bufferSelect][i+j*H_RES] = 0; } // initialize video buffers @@ -101,36 +110,49 @@ vb_line_s[2] = DAC_SYNC; } +volatile uint8_t drawing = 0; // video interrupt -void isr() -{ + +//void isr() __attribute__ ((section ("rw"))); +void isr() { uint8_t nop = 0, img = 0; //use nops or use wait_us uint8_t* sptr; //pointer to sync buffer for line uint8_t* vptr; //pointer to video buffer for line uint8_t* pptr; //porch + // drawing = (l > 30 && l < (30 + YL)); + + //if(l > 180) { +// drawing = 1; +// } if (l < V_PORCH_SIZE) { + vptr = bl_line_v; sptr = im_line_s; nop = 1; } - else if (l < YL + V_PORCH_SIZE) { - vptr = im_line_va + (l - 30) * H_RES; + else if ((l < YL + V_PORCH_SIZE)) { + //drawing = l - V_PORCH_SIZE + 1; + //drawing = 1; + vptr = im_line_vas[bufferSelect] + (l - 30) * H_RES; sptr = im_line_s; nop = 1; img = 1; } - else if (l < 254) { + else if (l < 255) { + //drawing = 0; vptr = bl_line_v; sptr = bl_line_s; nop = 0; } else { + vptr = vb_line_v; sptr = vb_line_s; nop = 1; } + pptr = img ? bl_line_v : vptr; if (nop) { @@ -162,7 +184,8 @@ //move to next line l++; tics++; - if(l > 255) l = 0; + if(l > 255) {l = 0;} + } // draw letter @@ -175,7 +198,7 @@ { for(uint16_t yp = 0; yp < 8; yp++) { - im_line_va[H_RES*(yp+y0) + xp + x0] = CHECK_BIT(letter[yp],8-xp)?TEXT_LEVEL:0; + im_line_vas[bufferSelect][H_RES*(yp+y0) + xp + x0] = CHECK_BIT(letter[yp],8-xp)?TEXT_LEVEL:0; } } } @@ -215,11 +238,37 @@ char_row = char_row_backup; } +extern "C" void TIM1_UP_TIM10_IRQHandler(void) { + + if (TIM1->SR & TIM_SR_UIF ) { + isr(); + //printf("oof\n"); + } + TIM1->SR = 0x00; +} + +void brems_init() { + RCC->APB2ENR |= RCC_APB2ENR_TIM1EN; + NVIC_EnableIRQ(TIM1_UP_TIM10_IRQn); //Enable TIM1 IRQ + + TIM1->DIER |= TIM_DIER_UIE; //enable update interrupt + TIM1->CR1 = 0x00; //CMS = 10, interrupt only when counting up + TIM1->CR1 |= TIM_CR1_ARPE; //autoreload on, + TIM1->RCR |= 0x00; //update event once per up/down count of tim1 + TIM1->EGR |= TIM_EGR_UG; + + TIM1->PSC = 0x00; //no prescaler, timer counts up in sync with the peripheral clock + TIM1->ARR = (int) (2 * (float) 9e7 / 15625); + TIM1->CCER |= (TIM_CCER_CC1NP); //rising edge aligned, non-inverting + TIM1->CR1 |= TIM_CR1_CEN; +} + int main() { - uint8_t *buf_swap_tmp; - im_line_va = im_line_va_1; - im_back = im_line_va_2; + //im_line_va_1 = (u8*)malloc(V_RES * H_RES); + //im_line_va_2 = im_line_va_1 + (V_RES*H_RES); + im_line_vas[0] = im_line_va_2; + im_line_vas[1] = im_line_va_1; //init serial pc.baud(115200); @@ -228,7 +277,8 @@ //init buffers init_buffers(); //init timer - t.attach_us(&isr,64); + //t.attach_us(&isr,64); + brems_init(); /*init SD card, wait for card insertion*/ SDFileSystem sd(DI, DO, SCK, CS, "sd"); @@ -237,35 +287,72 @@ wait(0.5); } - FILE *fp; - - fp = fopen("/sd/man.worm.starwars", "rb"); + printf("screen %d x %d (buffer %d x %d, %d bytes)\n", + XL,YL,H_RES,V_RES,H_RES*V_RES); + printf("starting the WORM BOY\r\n"); + FileLoadData file = loadFile("/sd/mario.gb"); + printf("done with loadfile\r\n"); + initMem(file); + printf("done with initMem\n"); + initVideo(nullptr); + printf("done with initvideo\n"); +// FILE *fp; +// +// fp = fopen("/sd/man.worm.mc", "rb"); uint32_t old_tics = tics; + u32 icount = 0; for(;;) { - if (feof(fp)) rewind(fp); - fread(current_frame, 1, VIDEO_FRAME_SIZE, fp); - for(int x = 0; x < XL; x += 2) - { - for(int y = 0; y < YL; y++) - { - im_back[H_RES*(y+Y0) + x + X0] = current_frame[YL - y][x >> 1] >> 4; - im_back[H_RES*(y+Y0) + x + 1 + X0] = current_frame[YL - y][x >> 1]; - } - } + + icount++; +// if(icount > 0x40000) { +// icount = 0; +// bufferSelect = !bufferSelect; +// } + + if((globalState.cycleCount&0xfffff) > 0x80000 & globalState.cycleCount < 0x12fffff) { + keyboard.start = true; + } else { + keyboard.start = false; + } + + u32 cpuCycles = cpuStep(); + stepVideo(cpuCycles); + +// wait_us(20); +// for(int x = 0; x < XL; x++) { +// for(int y = 0; y < YL; y++) { +// im_back[H_RES*(y+Y0) + x + X0] = 6 * (((x+y)>>1)&1); +// } +// } + +// if (feof(fp)) rewind(fp); +// fread(current_frame, 1, VIDEO_FRAME_SIZE, fp); +// for(int x = 0; x < XL; x += 2) +// { +// for(int y = 0; y < YL; y++) +// { +// im_back[H_RES*(y+Y0) + x + X0] = current_frame[YL - y][x >> 1] >> 4; +// im_back[H_RES*(y+Y0) + x + 1 + X0] = current_frame[YL - y][x >> 1]; +// } +// } - buf_swap_tmp = im_line_va; - im_line_va = im_back; - im_back = buf_swap_tmp; +// buf_swap_tmp = im_line_va; +// im_line_va = im_back; +// im_back = buf_swap_tmp; + + - led_state = !led_state; - user_led = led_state; - - char status_string[20]; - sprintf(status_string, "%.1f", 15625.f / (float)(tics - old_tics)); - old_tics = tics; - set_status_string(status_string); +// if(globalState.cycleCount > 1000000) { +// printf("instr 100000\n"); +// } +// +// char status_string[20]; +// sprintf(status_string, "%d", globalState.cycleCount); +//// sprintf(status_string, "%.1f", 15625.f / (float)(tics - old_tics)); +//// old_tics = tics; +// set_status_string(status_string); } }
diff -r 1f728d08b3a7 -r c9afe1a7b423 main.h --- a/main.h Thu Oct 04 12:37:25 2018 +0000 +++ b/main.h Sun Jan 13 19:00:10 2019 +0000 @@ -5,10 +5,10 @@ void new_line(); void clear_all_text(); void draw_gfx_line(float x0, float y0, float x1, float y1); - +extern volatile uint8_t drawing; // Buffer sizes -#define V_RES 96 -#define H_RES 279 +#define V_RES 144 +#define H_RES (279 - 80 + 20 - 4) // Porches #define V_PORCH_SIZE 30 @@ -17,8 +17,8 @@ // good new stuff #define X0 50 // start of image in X #define Y0 0 // start of image in Y -#define XL 224 // 25 chars -#define YL 96 // 20 chars +#define XL (224 - 80 + 20 - 4) // 25 chars +#define YL 144 // 20 chars //video #define VIDEO_FRAME_SIZE (XL * YL / 2) @@ -29,7 +29,9 @@ #define SCK PB_10 #define CS PB_12 -extern uint8_t *im_line_va; +extern uint8_t *im_line_vas[]; +extern volatile uint8_t bufferSelect; +extern uint32_t tics; #define set_pixel(x, y, color) im_line_va[H_RES*((y)+Y0) + (x) + X0] = (color)
diff -r 1f728d08b3a7 -r c9afe1a7b423 mem.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/mem.cpp Sun Jan 13 19:00:10 2019 +0000 @@ -0,0 +1,721 @@ +#define NDEBUG +#include <assert.h> +#include <stdlib.h> +#include <mbed.h> + +#include "mem.h" +#include "platform.h" + +#define DANGER_MODE + +MemState globalMemState; + +void CartInfo::print() { + printf("Gameboy Cartridge\n" + "\ttitle: %s\n" + "\tisColor: 0x%x\n" + "\tSGB: 0x%x\n" + "\tcartType: 0x%x\n" + "\tromSize: 0x%x\n" + "\tramSize: 0x%x\n" + "\tnotJapan: 0x%x\n", + title, isColor, SGB, (u8)cartType, (u8)romSize, (u8)ramSize, notJapan); +} + +// number of banks for given cartridge types +static const u8 romSizeIDToNBanks[7] = {2,4,8,16,32,64,128}; +static const u8 ramSizeIDToNBanks[5] = {0,1,1,4,4}; +static u8* fileData = nullptr; + +void saveGame() { + FileLoadData fld; + fld.size = 0x2000 * globalMemState.nRamBanks; + fld.data = globalMemState.mappedRamAllocation; + saveFile("savegame.gam", fld); +} + +void loadGame() { + FileLoadData fld = loadFile("savegame.gam"); + memcpy(globalMemState.mappedRamAllocation, fld.data, 0x2000 * globalMemState.nRamBanks); +} + + + + +void initMem(FileLoadData file) { + CartInfo* cartInfo = (CartInfo*)(file.data + CART_INFO_ADDR); + cartInfo->print(); + fileData = file.data; + printf("ROM size: 0x%x bytes\n", file.size); + + + globalMemState.inBios = true; // start in BIOS mode + globalMemState.rom0 = file.data; // ROM-bank 0 is the bottom of the cartridge + + // initialize everything to zero + globalMemState.mappedRom = nullptr; + globalMemState.nRamBanks = 0; + globalMemState.nRomBanks = 0; + globalMemState.vram = nullptr; + globalMemState.mappedRam = nullptr; + globalMemState.disabledMappedRam = nullptr; + globalMemState.mappedRamAllocation = nullptr; + globalMemState.internalRam = nullptr; + globalMemState.upperRam = nullptr; + + switch(cartInfo->cartType) { + case ROM_ONLY: + globalMemState.mappedRom = file.data + 0x4000; // maps in upper ROM by default + break; + + case ROM_MBC1: + globalMemState.mappedRom = file.data + 0x4000; // maps in upper ROM by default + globalMemState.mbcType = 1; + if((u8)cartInfo->romSize < 7) { + globalMemState.nRomBanks = romSizeIDToNBanks[(u8)cartInfo->romSize]; // has ROM banks + } else { + printf("unknown number of rom banks\n"); + assert(false); + } + break; + + case ROM_MBC1_RAM: + globalMemState.mappedRom = file.data + 0x4000; // maps in upper ROM by default + globalMemState.mbcType = 1; + if((u8)cartInfo->romSize < 7) { + globalMemState.nRomBanks = romSizeIDToNBanks[(u8)cartInfo->romSize]; // has ROM banks + } else { + printf("unknown number of rom banks\n"); + assert(false); + } + + if((u8)cartInfo->ramSize < 5) { + globalMemState.nRamBanks = ramSizeIDToNBanks[(u8)cartInfo->ramSize]; // has RAM banks + } else { + printf("unknown number of ram banks\n"); + assert(false); + } + break; + + case ROM_MBC3_RAM_BATT: + case ROM_MBC3_TIMER_RAM_BATT: + globalMemState.mappedRom = file.data + 0x4000; // maps in upper ROM by default + globalMemState.mbcType = 3; + if((u8)cartInfo->romSize < 7) { + globalMemState.nRomBanks = romSizeIDToNBanks[(u8)cartInfo->romSize]; // has ROM banks + } else { + printf("unknown number of rom banks\n"); + assert(false); + } + + if((u8)cartInfo->ramSize < 5) { + globalMemState.nRamBanks = ramSizeIDToNBanks[(u8)cartInfo->ramSize]; // has RAM banks + } else { + printf("unknown number of ram banks\n"); + } + break; + default: + printf("unknown cart type 0x%x\n", (u8)cartInfo->cartType); + assert(false); + break; + } + + printf("mbc%d\n", globalMemState.mbcType); + printf("rom-banks: %d\n", globalMemState.nRomBanks); + printf("ram-banks: %d\n", globalMemState.nRamBanks); + + // allocate cartridge RAM (8 KB * # of banks) + if(globalMemState.nRamBanks) { + globalMemState.mappedRamAllocation = (u8*)malloc(0x2000 * globalMemState.nRamBanks); + memset((void*)globalMemState.mappedRamAllocation, 0, 0x2000 * globalMemState.nRamBanks); + globalMemState.disabledMappedRam = globalMemState.mappedRamAllocation; + } else { + + } + + // allocate memories: + // internal RAM + globalMemState.internalRam = (u8*)badalloc_check(0x2000, "internal_ram"); + globalMemState.vram = (u8*)badalloc_check(0x2000, "vram"); + globalMemState.upperRam = (u8*)badalloc_check(0x80, "upper-regs"); + globalMemState.ioRegs = (u8*)badalloc_check(0x80, "io-regs"); + + printf("[initRam] ioRegs: 0x%x\n", globalMemState.ioRegs); + + // clear RAMs + memset(globalMemState.internalRam, 0, 0x2000); + memset(globalMemState.vram, 0, 0x2000); + memset(globalMemState.upperRam, 0, 0x80); + memset(globalMemState.ioRegs, 0, 0x80); + + // setup i/o regs and friends + u8* io = globalMemState.ioRegs; + io[IO_TIMA] = 0; // reset TIMER COUNT to 0 + io[IO_TMA] = 0; // TIMER RELOAD + io[IO_TAC] = 0; // TIMER STOP + io[IO_NR10] = 0x80; + io[IO_NR11] = 0xbf; + io[IO_NR12] = 0xf3; + io[IO_NR14] = 0xbf; + io[IO_NR21] = 0x3f; + io[IO_NR22] = 0x00; + io[IO_NR24] = 0xbf; + io[IO_NR30] = 0x7f; + io[IO_NR31] = 0xff; + io[IO_NR32] = 0x9f; + io[IO_NR34] = 0xbf; + io[IO_NR41] = 0xff; + io[IO_NR42] = 0x00; + io[IO_NR43] = 0x00; + io[IO_NR44] = 0xbf; + io[IO_NR50] = 0x77; + io[IO_NR51] = 0xf3; + io[IO_NR52] = 0xf1; + io[IO_LCDC] = 0x91; + io[IO_SCROLLY] = 0x00; + io[IO_SCROLLX] = 0x00; + io[IO_LYC] = 0x00; + io[IO_BGP] = 0xfc; + io[IO_OBP0] = 0xff; + io[IO_OBP1] = 0xff; + io[IO_WINY] = 0x00; + io[IO_WINX] = 0x00; + + // turn off interrupts + globalMemState.upperRam[0x7f] = 0; +} + + +// boot ROM +static const u8 bios[256] = {0x31, 0xFE, 0xFF, // LD, SP, $fffe 0 + 0xAF, // XOR A 3 + 0x21, 0xFF, 0x9F, // LD HL, $9fff 4 + 0x32, // LD (HL--), A 7 + 0xCB, 0x7C, // BIT 7, H 8 + 0x20, 0xFB, // JR NZ 7 a + 0x21, 0x26, 0xFF, // LD HL, $ff26 c + 0x0E, 0x11, // LD c,$11 f + 0x3E, 0x80, // LD a,$80 11 + 0x32, // LD (HL--), A 13 + 0xE2, // LD($FF00+C),A 14 + 0x0C, // INC C 15 + 0x3E, 0xF3, // LD A, $f3 16 + 0xE2, // LD (HL--), A 18 + 0x32, // LD($FF00+C),A 19 + 0x3E, 0x77, // LD A,$77 1a + 0x77, 0x3E, 0xFC, 0xE0, + 0x47, 0x11, 0x04, 0x01, 0x21, 0x10, 0x80, 0x1A, 0xCD, 0x95, 0x00, 0xCD, 0x96, 0x00, 0x13, 0x7B, + 0xFE, 0x34, 0x20, 0xF3, 0x11, 0xD8, 0x00, 0x06, 0x08, 0x1A, 0x13, 0x22, 0x23, 0x05, 0x20, 0xF9, + 0x3E, 0x19, 0xEA, 0x10, 0x99, 0x21, 0x2F, 0x99, 0x0E, 0x0C, 0x3D, 0x28, 0x08, 0x32, 0x0D, 0x20, + 0xF9, 0x2E, 0x0F, 0x18, 0xF3, 0x67, 0x3E, 0x64, 0x57, 0xE0, 0x42, 0x3E, 0x91, 0xE0, 0x40, 0x04, + 0x1E, 0x02, 0x0E, 0x0C, 0xF0, 0x44, 0xFE, 0x90, 0x20, 0xFA, 0x0D, 0x20, 0xF7, 0x1D, 0x20, 0xF2, + 0x0E, 0x13, 0x24, 0x7C, 0x1E, 0x83, 0xFE, 0x62, 0x28, 0x06, 0x1E, 0xC1, 0xFE, 0x64, 0x20, 0x06, + 0x7B, 0xE2, 0x0C, 0x3E, 0x87, 0xF2, 0xF0, 0x42, 0x90, 0xE0, 0x42, 0x15, 0x20, 0xD2, 0x05, 0x20, + 0x4F, 0x16, 0x20, 0x18, 0xCB, 0x4F, 0x06, 0x04, 0xC5, 0xCB, 0x11, 0x17, 0xC1, 0xCB, 0x11, 0x17, + 0x05, 0x20, 0xF5, 0x22, 0x23, 0x22, 0x23, 0xC9, 0xCE, 0xED, 0x66, 0x66, 0xCC, 0x0D, 0x00, 0x0B, + 0x03, 0x73, 0x00, 0x83, 0x00, 0x0C, 0x00, 0x0D, 0x00, 0x08, 0x11, 0x1F, 0x88, 0x89, 0x00, 0x0E, + 0xDC, 0xCC, 0x6E, 0xE6, 0xDD, 0xDD, 0xD9, 0x99, 0xBB, 0xBB, 0x67, 0x63, 0x6E, 0x0E, 0xEC, 0xCC, + 0xDD, 0xDC, 0x99, 0x9F, 0xBB, 0xB9, 0x33, 0x3E, 0x3c, 0x42, 0xB9, 0xA5, 0xB9, 0xA5, 0x42, 0x4C, + 0x21, 0x04, 0x01, 0x11, 0xA8, 0x00, 0x1A, 0x13, 0xBE, 0x20, 0xFE, 0x23, 0x7D, 0xFE, 0x34, 0x20, + 0xF5, 0x06, 0x19, 0x78, 0x86, 0x23, 0x05, 0x20, 0xFB, 0x86, 0x20, 0xFE, 0x3E, 0x01, 0xE0, 0x50}; + +// handler for MBC0 switch +void mbc0Handler(u16 addr, u8 value) { + + // it looks like tetris tries to select ROM 1 for banked ROM, so we need to allow this: + if(addr >= 0x2000 && addr < 0x3fff) { + if(value == 0 || value == 1) { + // nothing to do! + } else { + assert(false); + } + } else { + assert(false); + } + +} + +// handler for MBC1 switch (doesn't handle everything yet...) +void mbc1Handler(u16 addr, u8 value) { + if(addr >= 0x2000 && addr < 0x3fff) { + // ROM bank switch + if(value >= globalMemState.nRomBanks) { + printf("\trequested rom bank %d when there are only %d banks!\n", value, globalMemState.nRomBanks); + assert(false); + } + + if(value == 0) value = 1; + if(value == 0x21) value = 0x20; + if(value == 0x41) value = 0x40; + globalMemState.mappedRom = fileData + 0x4000 * value; + } else if(addr >= 0 && addr < 0x1fff) { + // enable RAM + if(value == 0) { + globalMemState.disabledMappedRam = globalMemState.mappedRam; + globalMemState.mappedRam = nullptr; + } else if(value == 0xa) { + globalMemState.mappedRam = globalMemState.disabledMappedRam; + } else { + assert(false); + } + } else { + assert(false); + } + +} + +// handler for MBC2 switch (doesn't handle anything yet...) +void mbc2Handler(u16 addr, u8 value) { + assert(false); +} + +// handler for MBC3 switch (doesn't handle anything yet...) +void mbc3Handler(u16 addr, u8 value) { + + if(addr >= 0x2000 && addr < 0x3fff) { + // ROM bank switch + if(value >= globalMemState.nRomBanks) { + printf("\trequested rom bank %d when there are only %d banks!\n", value, globalMemState.nRomBanks); + assert(false); + } + + if(value == 0) value = 1; + globalMemState.mappedRom = fileData + 0x4000 * value; + } else if(addr >= 0 && addr < 0x1fff) { + // RAM enable/disable + if(value == 0) { + globalMemState.disabledMappedRam = globalMemState.mappedRam; + globalMemState.mappedRam = nullptr; + } else if(value == 0xa) { + globalMemState.mappedRam = globalMemState.disabledMappedRam; + } else { + //assert(false); + } + } else if(addr >= 0x4000 && addr < 0x5fff) { + // RAM bank switch + if(value < globalMemState.nRamBanks) { + globalMemState.mappedRam = globalMemState.mappedRamAllocation + 0x2000 * value; + } else { + //assert(false); + } + } else if(addr == 0x6000) { + // ?? RTC latch nonsense + } else { + assert(false); + } +} + +// handler for all MBC switches +void mbcHandler(u16 addr, u8 value) { + switch(globalMemState.mbcType) { + case 0: + mbc0Handler(addr, value); + break; + case 1: + mbc1Handler(addr, value); + break; + case 2: + mbc2Handler(addr, value); + break; + case 3: + mbc3Handler(addr, value); + break; + default: + assert(false); + break; + } +} + +// read a u16 from game memory +u16 readU16(u16 addr) { + return (u16)readByte(addr) + ((u16)(readByte(addr+(u16)1)) << 8); +} + +// write a u16 to game memory +void writeU16(u16 mem, u16 addr) { + writeByte((u8)(mem & 0xff), addr); + writeByte((u8)(mem >> 8), addr + (u16)1); +} + +// read byte from memory +u8 readByte(u16 addr) { + switch(addr & 0xf000) { + case 0x0000: // either BIOS or ROM 0: + if(globalMemState.inBios) { + if(addr < 0x100) { + return bios[addr]; + } else if(addr == 0x100) { + printf("EXIT BIOS ERROR\n"); + assert(false); + } else { + return globalMemState.rom0[addr]; // todo <- change this for stm32 + } + } else { + return globalMemState.rom0[addr]; // todo <- change this for stm32 + } + + case 0x1000: // ROM 0 + case 0x2000: // ROM 0 + case 0x3000: // ROM 0 + return globalMemState.rom0[addr]; // todo <- change this for stm32 + + case 0x4000: // banked ROM + case 0x5000: + case 0x6000: + case 0x7000: + return globalMemState.mappedRom[addr & 0x3fff]; // todo <- change this for stm32 + + case 0x8000: // VRAM + case 0x9000: + return globalMemState.vram[addr & 0x1fff]; + + case 0xa000: // mapped RAM + case 0xb000: + if(!globalMemState.mappedRam) { +#ifndef DANGER_MODE + assert(false); +#endif + return 0xff; + } + return globalMemState.mappedRam[addr & 0x1fff]; + + case 0xc000: // internal RAM + case 0xd000: + return globalMemState.internalRam[addr & 0x1fff]; + + case 0xe000: // interal RAM copy + return globalMemState.internalRam[addr & 0x1fff]; + + case 0xf000: // either internal RAM copy or I/O or top-ram + switch(addr & 0x0f00) { + case 0x000: + case 0x100: + case 0x200: + case 0x300: + case 0x400: + case 0x500: + case 0x600: + case 0x700: + case 0x800: + case 0x900: + case 0xa00: + case 0xb00: + case 0xc00: + case 0xd00: + case 0xe00: + return globalMemState.internalRam[addr & 0x1fff]; + + + case 0xf00: + if(addr >= 0xff80) { + return globalMemState.upperRam[addr & 0x7f]; + } else { + u8 lowAddr = (u8)(addr & 0xff); + switch(lowAddr) { + case IO_LY: + case IO_SCROLLX: + case IO_SCROLLY: + case IO_NR10: // nyi + case IO_NR11: // nyi + case IO_NR12: // nyi + case IO_NR13: // nyi + case IO_NR14: // nyi + case IO_NR21: // nyi + case IO_NR22: // nyi + case IO_NR23: // nyi + case IO_NR24: // nyi + case IO_NR30: // nyi + case IO_NR31: // nyi + case IO_NR32: // nyi + case IO_NR33: // nyi + case IO_NR34: // nyi + case IO_NR41: // nyi + case IO_NR42: // nyi + case IO_NR43: // nyi + case IO_NR44: // nyi + case IO_NR50: // nyi + case IO_NR51: // nyi + case IO_NR52: // nyi + case IO_STAT: // nyi + case IO_WAVE_PATTERN: // nyi + case IO_LCDC: // nyi + case IO_BGP: + case IO_OBP0: + case IO_OBP1: + case IO_SERIAL_SB: + case IO_SERIAL_SC: + case IO_DIV: + case IO_TIMA: + case IO_TMA: + case IO_TAC: + case IO_WINY: + case IO_WINX: + return globalMemState.ioRegs[lowAddr]; + break; + + case IO_IF: + printf("read if: 0x%x\n", globalMemState.ioRegs[lowAddr]); + printf("timer value: 0x%x\n", globalMemState.ioRegs[IO_TIMA]); + return globalMemState.ioRegs[lowAddr]; + break; + + + case IO_P1: + { + u8 regP1 = globalMemState.ioRegs[IO_P1]; + //printf("ireg: 0x%x\n", regP1); + u8 joypad_data = 0; + if(regP1 & 0x10) { + if(keyboard.a) joypad_data += 0x1; + if(keyboard.b) joypad_data += 0x2; + if(keyboard.select) joypad_data += 0x4; + if(keyboard.start) joypad_data += 0x8; + } + if(regP1 & 0x20) { + if(keyboard.r) joypad_data += 0x1; + if(keyboard.l) joypad_data += 0x2; + if(keyboard.u) joypad_data += 0x4; + if(keyboard.d) joypad_data += 0x8; + } + + regP1 = (regP1 & 0xf0); + joypad_data = ~joypad_data; + joypad_data = regP1 + (joypad_data & 0xf); + //globalMemState.ioRegs[IO_P1] = joypad_data; + //printf("jpd: 0x%x\n", joypad_data); + return joypad_data; + } + + break; + + case IO_GBCSPEED: + return 0xff; + + case IO_LYC: + case IO_DMA: + + printf("unhandled I/O read @ 0x%x\n", addr); +#ifndef DANGER_MODE + assert(false); +#endif + break; + default: + printf("unknown I/O read @ 0x%x\n", addr); +#ifndef DANGER_MODE + assert(false); +#endif + break; + } + + } + default: +#ifndef DANGER_MODE + assert(false); +#endif + break; + + } + break; + + default: +#ifndef DANGER_MODE + assert(false); +#endif + break; + } + assert(false); + return 0xff; +} + + +void writeByte(u8 byte, u16 addr) { + switch(addr & 0xf000) { + case 0x0000: // ROM 0, but possibly the BIOS area + if(globalMemState.inBios) { + printf("ERROR: tried to write into ROM0 or BIOS (@ 0x%04x) during BIOS!\n", addr); +#ifndef DANGER_MODE + assert(false); +#endif + } else { + mbcHandler(addr, byte); + } + break; + + + case 0x1000: // ROM 0 + case 0x2000: // ROM 0 + case 0x3000: // ROM 0 + case 0x4000: // ROM 1 + case 0x5000: // ROM 1 + case 0x6000: // ROM 1 + case 0x7000: // ROM 1 + mbcHandler(addr, byte); + break; + + case 0x8000: // VRAM + case 0x9000: + globalMemState.vram[addr & 0x1fff] = byte; + break; + + case 0xa000: // mapped RAM + case 0xb000: + if(!globalMemState.mappedRam) { + //printf("write to unmapped ram @ 0x%x value 0x%x\n", addr, byte); +//#ifndef DANGER_MODE +// assert(false); +//#endif + break; + } + globalMemState.mappedRam[addr & 0x1fff] = byte; + break; + + case 0xc000: // internal RAM + case 0xd000: + globalMemState.internalRam[addr & 0x1fff] = byte; + break; + + case 0xe000: // interal RAM copy + globalMemState.internalRam[addr & 0x1fff] = byte; + break; + + case 0xf000: // either internal RAM copy or I/O or top-ram + switch(addr & 0x0f00) { + case 0x000: + case 0x100: + case 0x200: + case 0x300: + case 0x400: + case 0x500: + case 0x600: + case 0x700: + case 0x800: + case 0x900: + case 0xa00: + case 0xb00: + case 0xc00: + case 0xd00: + case 0xe00: + globalMemState.internalRam[addr & 0x1fff] = byte; + break; + + + case 0xf00: + if(addr >= 0xff80) { + globalMemState.upperRam[addr & 0x7f] = byte; + break; + } else { + u16 maskedAddress = addr & 0x7f; + globalMemState.ioRegs[maskedAddress] = byte; + u8 lowAddr = (u8)(addr & 0xff); + switch(lowAddr) { + + case IO_NR10: + case IO_NR11: + case IO_NR12: + case IO_NR13: + case IO_NR14: + case IO_NR21: + case IO_NR22: + case IO_NR23: + case IO_NR24: + case IO_NR30: + case IO_NR31: + case IO_NR32: + case IO_NR33: + case IO_NR34: + case IO_NR41: + case IO_NR42: + case IO_NR43: + case IO_NR44: + case IO_NR50: + case IO_NR51: + case IO_NR52: + case IO_WAVE_PATTERN: + case 0x31: + case 0x32: + case 0x33: + case 0x34: + case 0x35: + case 0x36: + case 0x37: + case 0x38: + case 0x39: + case 0x3a: + case 0x3b: + case 0x3c: + case 0x3d: + case 0x3e: + case 0x3f: + case IO_BGP: + case IO_SCROLLX: + case IO_SCROLLY: + case IO_LCDC: + case IO_STAT: + case IO_OBP0: + case IO_OBP1: + case IO_P1: + case IO_IF: + case IO_TAC: + case IO_TIMA: + case IO_TMA: + case IO_SERIAL_SB: + case IO_SERIAL_SC: + case IO_WINY: + case IO_WINX: + case IO_LYC: + break; + + case IO_EXIT_BIOS: + if(globalMemState.inBios) { + printf("EXIT BIOS by write 0x%x to 0x%x", byte, addr); + globalMemState.inBios = false; + break; + } else { + printf("tried to write to 0xff50 when not in bios?\n"); + break; + } + break; + + case IO_DMA: + { + u16 dmaAddr = ((u16)byte) << 8; + for(u16 i = 0; i < 160; i++) { + writeByte(readByte(dmaAddr + i), (u16)0xfe00 + i); + } + break; + } + + case 0x7f: + printf("OOPS\n"); + break; + + + case IO_LY: + printf("unhandled I/O write @ 0x%x\n", addr); +#ifndef DANGER_MODE + assert(false); +#endif + break; + default: + printf("unknown I/O write @ 0x%x\n", addr); +#ifndef DANGER_MODE + assert(false); +#endif + break; + } + break; + } + default: +#ifndef DANGER_MODE + assert(false); +#endif + break; + } + break; + default: +#ifndef DANGER_MODE + assert(false); +#endif + break; + } +} \ No newline at end of file
diff -r 1f728d08b3a7 -r c9afe1a7b423 mem.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/mem.h Sun Jan 13 19:00:10 2019 +0000 @@ -0,0 +1,176 @@ +#ifndef GBC_MEM_H +#define GBC_MEM_H + +#include "types.h" +#include "stdio.h" +#include "platform.h" + +// interrupt flags +#define INTERRUPT_KEY 0x10; + +// interrupt vector locations +#define VBLANK_INTERRUPT 0x0040 +#define LCDC_INTERRUPT 0x0048 +#define TIMER_INTERRUPT 0x0050 +#define SERIAL_INTERRUPT 0x0058 +#define HIGH_TO_LOW_P10_P13 0x0060 + +// IO Regs (offset from 0xff00) +#define IO_P1 0x0 // joystick (needs keyboard handler) +#define IO_SERIAL_SB 0x01 // serial transfer data (don't support) +#define IO_SERIAL_SC 0x02 // serial control (don't support) +#define IO_DIV 0x04 // div +#define IO_TIMA 0x05 // timer value (nyi) +#define IO_TMA 0x06 // timer reload (nyi) +#define IO_TAC 0x07 // TIMER CONTROL (enable, speed) (nyi) +#define IO_IF 0x0f + +#define IO_NR10 0x10 +#define IO_NR11 0x11 +#define IO_NR12 0x12 +#define IO_NR13 0x13 +#define IO_NR14 0x14 + +#define IO_NR21 0x16 +#define IO_NR22 0x17 +#define IO_NR23 0x18 +#define IO_NR24 0x19 + +#define IO_NR30 0x1a +#define IO_NR31 0x1b +#define IO_NR32 0x1c +#define IO_NR33 0x1d +#define IO_NR34 0x1e + +#define IO_NR41 0x20 +#define IO_NR42 0x21 +#define IO_NR43 0x22 +#define IO_NR44 0x23 + +#define IO_NR50 0x24 +#define IO_NR51 0x25 +#define IO_NR52 0x26 + +#define IO_WAVE_PATTERN 0x30 // this is 16 bytes + +#define IO_LCDC 0x40 // yes, revisit +#define IO_STAT 0x41 // no +#define IO_SCROLLY 0x42 +#define IO_SCROLLX 0x43 +#define IO_LY 0x44 +#define IO_LYC 0x45 // no + +#define IO_DMA 0x46 // yes +#define IO_BGP 0x47 // ?? +#define IO_OBP0 0x48 // ?? +#define IO_OBP1 0x49 // ?? +#define IO_WINY 0x4a // no +#define IO_WINX 0x4b // no + +#define IO_GBCSPEED 0x4d + +#define IO_EXIT_BIOS 0x50 + + +#define CART_INFO_ADDR 0x0100 + + +// cartridge types +enum CartType { + ROM_ONLY = 0, + ROM_MBC1 = 1, + ROM_MBC1_RAM = 2, + ROM_MBC1_RAM_BATT = 3, + ROM_MBC2 = 5, + ROM_MBC2_BATT = 6, + ROM_RAM = 8, + ROM_RAM_BATT = 9, + ROM_MM01 = 0xb, + ROM_MM01_SRAM = 0xc, + ROM_MMM01_SRAM_BATT = 0xd, + ROM_MBC3_TIMER_BATT = 0xf, + ROM_MBC3_TIMER_RAM_BATT = 0x10, // pkmn crystal + ROM_MBC3 = 0x11, + ROM_MBC3_RAM = 0x12, + ROM_MBC3_RAM_BATT = 0x13, // pkmn red + ROM_MBC5 = 0x19, + ROM_MBC_RAM = 0x1a, + ROM_MBC5_RAM_BATT = 0x1b, + ROM_MBC5_RUMBLE = 0x1c, + ROM_MBC5_RUMBLE_SRAM = 0x1d, + ROM_MBC5_RUMBLE_SRAM_BATT = 0x1e, + POCKET_CAMERA = 0x1f, + BANDAI_TAMA5 = 0xfd, + HUDSON_HUC3 = 0xfe, + HUDSON_HUC1 = 0xff +}; + +// in banks, of 16 KByte each +enum RomSize { + BANK_2 = 0, + BANK_4 = 1, + BANK_8 = 2, + BANK_16 = 3, + BANK_32 = 4, + BANK_64 = 5, // pkmn red: 1 MB + BANK_128 = 6, // pkmn crystal + BANK_72 = 0x52, + BANK_80 = 0x53, + BANK_96 = 0x54 +}; + +enum RamSize { + NONE = 0, + SIZE_2KB = 1, // 1 bank + SIZE_8KB = 2, // 1 bank + SIZE_32KB = 3, // 4 banks, pkmn red, crystal + SIZE_128KB = 4, // 16 banks +}; + +struct CartInfo { + u8 beginExec[4]; // 0x0 -> 0x3 + u8 nintendoGraphic[48]; // 0x3 -> + char title[15]; + u8 isColor; // compare against CARTRIDGE_IS_COLOR + u8 license1; + u8 license2; + u8 SGB; + CartType cartType; + RomSize romSize; + RamSize ramSize; + u8 notJapan; + u8 license3; + u8 maskRomVersion; + + void print(); +}; + +struct MemState { + bool inBios; + u8 mbcType; + u8 nRomBanks; + u8 nRamBanks; + u8* rom0; + u8* mappedRom; + u8* vram; + u8* mappedRam; + u8* disabledMappedRam; + u8* internalRam; + u8* mappedRamAllocation; + u8* ioRegs; + u8* upperRam; + u8* spriteAttribute; +}; + +// external interface +extern MemState globalMemState; +void initMem(FileLoadData file); // intialize memory for a cartridge +u8 readByte(u16 addr); // read a byte from memory +u16 readU16(u16 addr); // read 16-bits from memory +void writeByte(u8 byte, u16 addr); // write a byte to memory +void writeU16(u16 mem, u16 addr); // read 16-bits from memory +void saveGame(); +void loadGame(); + + +#endif //GBC_MEM_H
diff -r 1f728d08b3a7 -r c9afe1a7b423 platform.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/platform.cpp Sun Jan 13 19:00:10 2019 +0000 @@ -0,0 +1,78 @@ +// Linux specific code (excluding graphics, those are in graphics_display.cpp) +#include "platform.h" + +#include <mbed.h> + +// global keyboard +KeyState keyboard; + +void* badalloc_check(u32 size, const char* alloc_name) { + void* ptr = malloc(size); + if(ptr) { + printf("[badalloc] success %s\n", alloc_name); + } else { + printf("~!~!~!1!!!!!-----> [badalloc] fail %s\n", alloc_name); + } + return ptr; +} + +// open a file +FileLoadData loadFile(const char* name) { + FILE* fp = fopen(name, "rb"); + FileLoadData loadData; + + if(!fp) { + printf("loadFile(%s) failed!\r\n", name); + loadData.data = nullptr; + loadData.size = 0; + return loadData; + } + printf("loadfile fp is good\r\n"); + + fseek(fp, 0, SEEK_END); + u32 fileSize = (u32)ftell(fp); + printf("loadFile 0x%x bytes\r\n", fileSize); + fseek(fp, 0, SEEK_SET); + u8* fileData = (u8*)badalloc_check(fileSize, name); + if(fileData) { + printf("allocation for loadFile succes\r\n"); + } else { + printf("allocation for loadfile fail\r\n"); + } + fread(fileData, 1, fileSize, fp); + fclose(fp); + + printf("loadfile(%s) has loaded %d bytes (%.3f MB)\r\n", name, fileSize, (float)fileSize / (1 << 20)); + + loadData.size = fileSize; + loadData.data = fileData; + return loadData; +} + +void saveFile(const char* name, FileLoadData info) { +// FILE* fp = fopen(name, "wb"); +// printf("save 0x%x bytes 0x%llx\n", info.size, info.data); +// fwrite(info.data, info.size, 1, fp); +// fclose(fp); +} + +// update keyboard. Also checks to see if it's time to quit. +void updateKeyboard(KeyState* keys) { +// SDL_Event e; +// SDL_PollEvent(&e); +// if(e.type == SDL_QUIT) { +// exit(0); +// } +// const u8* keyStats = SDL_GetKeyboardState(nullptr); +// keys->a = keyStats[SDL_SCANCODE_A]; +// keys->b = keyStats[SDL_SCANCODE_B]; +// keys->u = keyStats[SDL_SCANCODE_UP]; +// keys->d = keyStats[SDL_SCANCODE_DOWN]; +// keys->l = keyStats[SDL_SCANCODE_LEFT]; +// keys->r = keyStats[SDL_SCANCODE_RIGHT]; +// keys->save = keyStats[SDL_SCANCODE_S]; +// keys->load = keyStats[SDL_SCANCODE_D]; +// keys->start = keyStats[SDL_SCANCODE_P]; +// keys->select = keyStats[SDL_SCANCODE_L]; +// keys->turbo = keyStats[SDL_SCANCODE_SPACE]; +} \ No newline at end of file
diff -r 1f728d08b3a7 -r c9afe1a7b423 platform.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/platform.h Sun Jan 13 19:00:10 2019 +0000 @@ -0,0 +1,16 @@ +#ifndef GBC_PLATFORM_H +#define GBC_PLATFORM_H + +#include "types.h" + +struct FileLoadData { + u8* data; + u32 size; +}; + +void* badalloc_check(u32 size, const char* alloc_name); +FileLoadData loadFile(const char* name); +void saveFile(const char* name, FileLoadData info); +void updateKeyboard(KeyState* keys); + +#endif //GBC_PLATFORM_H \ No newline at end of file
diff -r 1f728d08b3a7 -r c9afe1a7b423 types.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/types.h Sun Jan 13 19:00:10 2019 +0000 @@ -0,0 +1,28 @@ +#ifndef GBC_TYPES_H +#define GBC_TYPES_H + +#include <stdint.h> + +#define u8 uint8_t +#define u16 uint16_t +#define u32 uint32_t +#define s8 int8_t +#define s16 int16_t +#define s32 int32_t +#define nullptr NULL + +//using u8 = uint8_t; +//using u16 = uint16_t; +//using u32 = uint32_t; +// +//using s8 = int8_t; +//using s16 = int16_t; +//using s32 = int32_t; + +struct KeyState { + bool a,b,u,d,l,r,start,select,turbo, save, load; +}; + +extern KeyState keyboard; + +#endif //GBC_TYPES_H
diff -r 1f728d08b3a7 -r c9afe1a7b423 video.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/video.cpp Sun Jan 13 19:00:10 2019 +0000 @@ -0,0 +1,390 @@ +#include <stdlib.h> +#include <mbed.h> +#define NDEBUG +#include <assert.h> +#include "video.h" +#include "mem.h" +#include "graphics_display.h" +#include "main.h" + +u32 frameCount = 0; + +// the implementation of sprites is poor. +// pick a value for this that is between 0 and 256 and not equal to one of the colors +#define TRANSPARENT_SPRITE 37 + +#define BRIGHTEST_COLOR 6 + +VideoState globalVideoState; + +// colors on screen (white, light gray, dark gray, black) +// the first one must be "BRIGHTEST_COLOR" - other code depends on this! +static const u8 colors[4] = {BRIGHTEST_COLOR, 4, 2, 0}; + + + + +void initVideo(u8* frameBuffer) { + globalVideoState.mode = 0; + globalVideoState.modeClock = 0; + globalVideoState.line = 0; + globalVideoState.frameBuffer = frameBuffer; // <- todo change for STM32 + +// for(u32 i = 0; i < 160*144; i++) { +// globalVideoState.frameBuffer[i] = 255; +// } + +} + +inline u32 xy2px(u8 x, u8 y) { + return H_RES*(y+Y0) + x + X0; +} + +void dumpVideo() { + for(u16 i = 0x8000; i < 0x87ff; i++) { + if((i%8) == 1) { + printf("@ 0x%04x: ", i); + } + printf("0x%02x ", readByte(i)); + if(!(i%8)) printf("\n"); + } + printf("\n"); + assert(false); +} + +// read a pixel out of a tile and apply the given palette +u8 readTile(u16 tileAddr, u8 x, u8 y, u8 palette) { + assert(x <= 8); + assert(y <= 8); + x = (7 - x); + u16 loAddr = tileAddr + (y*(u16)2); + u16 hiAddr = loAddr + (u16)1; + u8 lo = readByte(loAddr); + u8 hi = readByte(hiAddr); + u8 loV = (lo >> x) & (u8)1; + u8 hiV = (hi >> x) & (u8)1; + //u8 result = loV * 120 + hiV * 60; + u8 colorIdx = loV + 2 * hiV; + u8 colorID = (palette >> (2 * colorIdx)) & 3; + return colors[colorID]; +} + +u8 readTilePtr(u8* tileAddr, u8 x, u8 y, u8 palette) { + assert(x <= 8); + assert(y <= 8); + x = (7 - x); + u8* loAddr = tileAddr + (y*(u16)2); + u8* hiAddr = loAddr + (u16)1; + u8 lo = *(loAddr); + u8 hi = *(hiAddr); + u8 loV = (lo >> x) & (u8)1; + u8 hiV = (hi >> x) & (u8)1; + //u8 result = loV * 120 + hiV * 60; + u8 colorIdx = loV + 2 * hiV; + u8 colorID = (palette >> (2 * colorIdx)) & 3; + return colors[colorID]; +} + +// read a pixel out of a tile and apply the given palette +// returns TRANSPARENT_SPRITE if the sprite should be transparent +u8 readSpriteTile(u16 tileAddr, u8 x, u8 y, u8 palette) { + //tileAddr = 0x8180; + assert(x <= 8); + assert(y <= 8); + x = (7 - x); + u16 loAddr = tileAddr + (y*(u16)2); + u16 hiAddr = loAddr + (u16)1; + u8 lo = readByte(loAddr); + u8 hi = readByte(hiAddr); + u8 loV = (lo >> x) & (u8)1; + u8 hiV = (hi >> x) & (u8)1; + u8 colorIdx = loV + 2 * hiV; + if(colorIdx == 0) { + return TRANSPARENT_SPRITE; + } + u8 colorID = (palette >> (2 * colorIdx)) & 3; + return colors[colorID]; +} + +u8 readSpriteTileAddr(u8* tileAddr, u8 x, u8 y, u8 palette) { + //tileAddr = 0x8180; + assert(x <= 8); + assert(y <= 8); + x = (7 - x); + u8* loAddr = tileAddr + (y*(u16)2); + u8* hiAddr = loAddr + (u16)1; + u8 lo = *(loAddr); + u8 hi = *(hiAddr); + u8 loV = (lo >> x) & (u8)1; + u8 hiV = (hi >> x) & (u8)1; + u8 colorIdx = loV + 2 * hiV; + if(colorIdx == 0) { + return TRANSPARENT_SPRITE; + } + u8 colorID = (palette >> (2 * colorIdx)) & 3; + return colors[colorID]; +} + +// compute the address of the tile from the tile's index +// this is confusing because depending on the tileData selected, +// the tileIdx is either signed or unsigned +u16 computeTileAddr(u8 tileIdx, bool tileData) { + if(tileData) { + return 0x8000 + 16 * tileIdx; + } else { + if(tileIdx <= 127) { + return 0x9000 + 16 * tileIdx; + } else { + return 0x8000 + 16 * (tileIdx); + } + } +} + +u8* computeTileAddrPtr(u8 tileIdx, bool tileData) { + if(tileData) { + return globalMemState.vram + 16 * tileIdx; + } else { + if(tileIdx <= 127) { + return globalMemState.vram + 0x1000 + 16 * tileIdx; + } else { + return globalMemState.vram + 16 * (tileIdx); + } + } +} + +// main function to render a line of the display. +// this implementation is missing a number of things, including (but not limited to) +// -- proper position of the WINDOW +// -- 16x8 sprites +// -- sprite sorting +// -- 10 sprite limit +void renderLine() { + if(frameCount % 3) return; + //return; + //if(drawing) return; + //printf("%x %x\n", im_line_va, im_back); + globalVideoState.frameBuffer = im_line_vas[bufferSelect]; + u8 lcdc = globalMemState.ioRegs[IO_LCDC]; // lcd control register + bool lcdOn = (lcdc >> 7) & (u8)1; // lcd display on? + bool windowTileMap = (lcdc >> 6) & (u8)1; // select tilemap source for window + bool windowEnable = (lcdc >> 5) & (u8)1; // draw window? + bool tileData = (lcdc >> 4) & (u8)1; // select tile data source + bool bgTileMap = (lcdc >> 3) & (u8)1; // select tilemap source for background + bool objSize = (lcdc >> 2) & (u8)1; // pick sprite size (nyi) + bool objEnable = (lcdc >> 1) & (u8)1; // enable sprite renderer + bool bgWinEnable = (lcdc >> 0) & (u8)1; // enable background and window renderer? + + u16 windowMapAddr = (u16)(windowTileMap ? 0x9c00 : 0x9800); + u16 bgTileMapAddr = (u16)(bgTileMap ? 0x9c00 : 0x9800); + + // background renderer + if(lcdOn && bgWinEnable) { + // render background onto framebuffer + u8 pal = globalMemState.ioRegs[IO_BGP]; // color palette + u16 tileMapRowAddr = (u16)(bgTileMapAddr + 32*((((u16)globalVideoState.line + + globalMemState.ioRegs[IO_SCROLLY]) & (u16)255) >> 3)); // address of the row of the tilemap + u8 tileMapColIdx = globalMemState.ioRegs[IO_SCROLLX] >> 3; // column index of the tilemap + u8 yPixOffset = ((u8)globalVideoState.line + globalMemState.ioRegs[IO_SCROLLY]) & (u8)7; // y-pixel of tile + u8 xPixOffset = globalMemState.ioRegs[IO_SCROLLX] & (u8)7; // x-pixel of tile + //u8* linePtr = globalVideoState.frameBuffer + 160 * globalVideoState.line; // frame buffer pointer + u8 tileIdx = readByte(tileMapRowAddr + tileMapColIdx); // tile index + + // loop over pixels in the line + for(u8 px = 0; px < 160; px++) { + globalVideoState.frameBuffer = im_line_vas[bufferSelect]; + globalVideoState.frameBuffer[xy2px(px,globalVideoState.line)] = + readTilePtr(computeTileAddrPtr(tileIdx, tileData), xPixOffset, yPixOffset, pal); + //readTile(computeTileAddr(tileIdx, tileData), xPixOffset, yPixOffset, pal); // set the frame buffer + + xPixOffset++; // increment tile pixel + //linePtr++; // increment frame buffer pixel + if(xPixOffset == 8) { // if we have overflowed the tile + xPixOffset = 0; // go to the beginning + tileMapColIdx = (tileMapColIdx + 1) & 31; // of the next tile (allow wraparound) + tileIdx = readByte(tileMapRowAddr + tileMapColIdx); // and look up the tile index in the tile map + } + } + } + +// // window renderer +// if(windowEnable) { +// u8 pal = globalMemState.ioRegs[IO_BGP]; // palette +// u8 wx = globalMemState.ioRegs[IO_WINX]; // location of the window (nyi) +// u8 wy = globalMemState.ioRegs[IO_WINY]; // location of the window (nyi) +// if(wx > 166 || wy > 143) { +// // if the window is out of this range, it is disabled too. +// } else { +// u16 tileMapRowAddr = windowMapAddr + 32*((((u16)globalVideoState.line)) >> 3); // address of the row of the tilemap +// u8 tileMapColIdx = 0; // column index of the tilemap +// u8 yPixOffset = ((u8)globalVideoState.line) & (u8)7; // y-pixel of tile +// u8 xPixOffset = 0; // x-pixel of tile +// u8* linePtr = globalVideoState.frameBuffer + 160 * globalVideoState.line; // frame buffer pointer +// u8 tileIdx = readByte(tileMapRowAddr + tileMapColIdx); // tile index +// +// // loop over pixels in the line +// for(u8 px = 0; px < 160; px++) { +// *linePtr = readTile(computeTileAddr(tileIdx, tileData), xPixOffset, yPixOffset, pal); // set the frame buffer +// +// xPixOffset++; // increment tile pixel +// linePtr++; // increment frame buffer pixel +// if(xPixOffset == 8) { // if we have overflowed the tile +// xPixOffset = 0; // go to the beginning +// tileMapColIdx = (tileMapColIdx + 1) & 31; // of the next tile (allow wraparound, but it shouldn't happen?) +// tileIdx = readByte(tileMapRowAddr + tileMapColIdx); // and look up the tile index in the tile map +// } +// } +// } +// } +// +// + // sprite renderer + if(objEnable) { + for(u16 spriteID = 0; spriteID < 40; spriteID++) { + u16 oamPtr = 0xfe00 + 4 * spriteID; // sprite information table + u8 spriteY = readByte(oamPtr); // y-coordinate of sprite + u8 spriteX = readByte(oamPtr + 1); // x-coordinate of sprite + u8 patternIdx = readByte(oamPtr + 2); // sprite pattern + u8 flags = readByte(oamPtr + 3); // flag bits + + bool pri = (flags >> 7) & (u8)1; // priority (transparency stuff) + bool yFlip = (flags >> 6) & (u8)1; // flip around y? + bool xFlip = (flags >> 5) & (u8)1; // flip around x? + bool palID = (flags >> 4) & (u8)1; // palette ID (OBP0/OBP2) + + u8 pal = palID ? globalMemState.ioRegs[IO_OBP1] : globalMemState.ioRegs[IO_OBP0]; + + + if(spriteX | spriteY) { + // the sprite coordinates have an offset + u8 spriteStartY = spriteY - 16; + u8 spriteLastY = spriteStartY + 8; // todo 16 row sprites + + // reject based on y if the sprite won't be visible in the current line + if(globalVideoState.line < spriteStartY || globalVideoState.line >= spriteLastY) { + continue; + } + + // get y px relative to the sprite pattern + u8 tileY = globalVideoState.line - spriteStartY; + if(yFlip) { + tileY = 7 - tileY; + } + + assert(tileY < 8); + + // loop over the 8 pixels that the sprite is on: + for(u8 tileX = 0; tileX < 8; tileX++) { + + u8 xPos = spriteX - 8 + tileX; // position on the screen + if(xPos >= 160) continue; // reject if we go off the end, don't wrap around + + u32 fbIdx = xy2px(xPos, globalVideoState.line); + + globalVideoState.frameBuffer = im_line_vas[bufferSelect]; + + + // current color at the screen + u8 old = globalVideoState.frameBuffer[fbIdx]; + + // get the pixel from the sprite pattern data + u8 tileLookupX = tileX; + if(xFlip) { + tileLookupX = 7 - tileX; + } + //u8 tileValue = readSpriteTile(0x8000 + patternIdx * 16, tileLookupX, tileY, pal); + u8 tileValue = readSpriteTileAddr(globalMemState.vram + patternIdx * 16, tileLookupX, tileY, pal); + //u8 tileValue = readSpriteTileAddr(globalMemState.vram + patternIdx*16, tileLookupX, tileY, pal); + //u8 tileValue = 4; + // don't draw transparent + if(tileValue == TRANSPARENT_SPRITE) continue; // (transparent sprites) + + // not sure this is 100% right... + if(!pri) { + globalVideoState.frameBuffer[fbIdx] = tileValue; + } else { + if(old == BRIGHTEST_COLOR) { + globalVideoState.frameBuffer[fbIdx] = tileValue; + } + } + } + } + } + } +} + +static u32 oldTics = 0; +float filteredFrameRate = 0.f; + +// step the video by a number of clock cycles +void stepVideo(u32 cycles) { + globalVideoState.modeClock += cycles; + switch(globalVideoState.mode) { + case 2: // OAM read, scanning + + if(globalVideoState.modeClock >= 80) { + globalVideoState.modeClock = 0; + globalVideoState.mode = 3; // VRAM read, scanning + } + break; + case 3: // VRAM read, scanning + if(globalVideoState.modeClock >= 172) { + globalVideoState.modeClock = 0; + globalVideoState.mode = 0; // hblank + renderLine(); // draw line into framebuffer + } + break; + case 0: // hblank + if(globalVideoState.modeClock >= 204) { + globalVideoState.modeClock = 0; + globalVideoState.line++; + + if(globalVideoState.line == 143) { + + globalVideoState.mode = 1; // vblank + globalMemState.ioRegs[IO_IF] |= 0x1; // set interrupt for vblank + if(!keyboard.turbo) // if we are in "turbo" mode, don't update graphics + updateGraphics(); // display framebuffer on screen + //bufferSelect = !bufferSelect; + + + u32 dTics = tics - oldTics; + float frameTime = 64.0e-6 * (float)dTics; + filteredFrameRate = (0.94 * filteredFrameRate) + (0.06 * frameTime); + printf("%d %f %f\n", dTics, 1.f/frameTime, 1.f/filteredFrameRate); + frameCount++; + oldTics = tics; + + } else { + globalVideoState.mode = 2; // oam + } + } + break; + case 1: // vblank + if(globalVideoState.modeClock >= 456) { + globalVideoState.modeClock = 0; + globalVideoState.line++; + + if(globalVideoState.line > 153) { + globalVideoState.mode = 2; + globalVideoState.line = 0; + } + } + break; + default: + assert(false); + } + + globalMemState.ioRegs[IO_LY] = (u8)globalVideoState.line; // update current line + + + // this is likely somewhat wrong. + u8 stat = globalMemState.ioRegs[IO_STAT]; // update STAT register (this is likely the source of issue on bubble bobble) + stat &= ~7; // clear mode, coincidence + stat += globalVideoState.mode; // set current mode + if(globalMemState.ioRegs[IO_LYC] == globalMemState.ioRegs[IO_LY]) { // check coincidence + stat += 4; + if((stat >> 6) & 1) { + globalMemState.ioRegs[IO_IF] |= 2; // stat interrupt + } + } +}
diff -r 1f728d08b3a7 -r c9afe1a7b423 video.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/video.h Sun Jan 13 19:00:10 2019 +0000 @@ -0,0 +1,20 @@ +#ifndef GBC_VIDEO_H +#define GBC_VIDEO_H + +#include "types.h" + +struct VideoState { + u32 mode; + u32 modeClock; + u32 line; + u8* frameBuffer; +}; + +extern VideoState globalVideoState; + +void initVideo(u8* frameBuffer); +void stepVideo(u32 cycles); +void renderLine(); + + +#endif //GBC_VIDEO_H