Compare commits

...
14 Commits
6 changed files with 435 additions and 213 deletions
+9 -6
View File
@@ -19,10 +19,12 @@ GameBoy terminology.
*** Flags *** Flags
The Half Cary (H) flag differs between 8-bit or 16-bit operations. The 8-bit The half-carry flag ~H~ is set when an arithmetic operation on two numbers
variant indicates carry for the lower 4 bits of the result, or overflow from bit causes a carry from bit #3 to bit #4 for 8-bit operations or a carry from bit
4 to bit 5. The 16-bit variant indicates carry for the lower 4 bits of the #11 to bit #12 for 16-bit operations. For 16-bit operation this indicates the
*second* byte of the result, or overflow from bit 12 to bit 13. lower 4 bits of the *second* byte[[#references][(3)]].
We start counting from the right as bit *#0*!
*** Opcode *** Opcode
@@ -54,5 +56,6 @@ n = constant
** References ** References
- https://gbdev.io/pandocs/Memory_Map.html 1. https://gbdev.io/pandocs/Memory_Map.html
- https://gbdev.io/pandocs/Specifications.html 2. https://gbdev.io/pandocs/Specifications.html
3. [[https://robdor.com/2016/08/10/gameboy-emulator-half-carry-flag/]["A Visual Guide to the Gameboy's Half-Carry Flag"]]. robdor.com. Retrieved 24 Aug 2022.
+364 -185
View File
@@ -5,7 +5,7 @@
* SPDX-License-Identifier: MIT * SPDX-License-Identifier: MIT
*/ */
#include <cstdint> // int32_t, uint8_t, uint32_t #include <cstdint> // uint8_t, uint32_t
#include "cpu.h" #include "cpu.h"
#include "emu.h" #include "emu.h"
@@ -32,6 +32,8 @@ CPU::CPU(uint32_t frequency)
, m_hf(0x0) , m_hf(0x0)
, m_cf(0x0) , m_cf(0x0)
{ {
// FIXME: Figure out if other ProcessingUnits require access to these registers,
// delete this functionality if they dont
m_shared_registers.emplace("a", &m_a); m_shared_registers.emplace("a", &m_a);
m_shared_registers.emplace("b", &m_b); m_shared_registers.emplace("b", &m_b);
m_shared_registers.emplace("c", &m_c); m_shared_registers.emplace("c", &m_c);
@@ -57,47 +59,112 @@ void CPU::update()
{ {
m_wait_cycles--; m_wait_cycles--;
if (m_wait_cycles <= 0) { if (m_wait_cycles <= 0) {
// print(ruc::format::Emphasis::Underline | ruc::format::Emphasis::Bold | fg(ruc::format::TerminalColor::Blue), "{:#06x}\n", *this);
// Read next opcode // Read next opcode
uint8_t opcode = read(m_pc); uint8_t opcode = read(m_pc);
print("running opcode: {:#x}\n", opcode); print("running opcode: {:#x}\n", opcode);
switch (opcode) { switch (opcode) {
case 0x20: jr8(); break; case 0x01: ldi16(); break;
case 0x01: ld16(); break; case 0x02: ldr8(); break;
case 0x02: ld8(); break; case 0x06: ldi8(); break;
case 0x06: ld8(); break; case 0x08: ldr16(); break;
case 0x08: ld16(); break; case 0x0a: ldr8(); break;
case 0x0a: ld8(); break;
case 0x0d: dec8(); break; case 0x0d: dec8(); break;
case 0x0e: ld8(); break; case 0x0e: ldi8(); break;
case 0x11: ld16(); break; case 0x11: ldi16(); break;
case 0x12: ld8(); break; case 0x12: ldr8(); break;
case 0x16: ld8(); break; case 0x16: ldi8(); break;
case 0x1a: ld8(); break; case 0x1a: ldr8(); break;
case 0x1e: ld8(); break; case 0x1e: ldi8(); break;
case 0x21: ld16(); break; case 0x20: jr8(); break;
case 0x22: ld8(); break; case 0x21: ldi16(); break;
case 0x26: ld8(); break; case 0x22: ldr8(); break;
case 0x2a: ld8(); break; case 0x26: ldi8(); break;
case 0x2e: ld8(); break; case 0x2a: ldr8(); break;
case 0x2e: ldi8(); break;
case 0x2f: misc(); break; case 0x2f: misc(); break;
case 0x31: ld16(); break; case 0x31: ldi16(); break;
case 0x32: ld8(); break; case 0x32: ldr8(); break;
case 0x36: ld8(); break; case 0x36: ldi8(); break;
case 0x3a: ld8(); break; case 0x3a: ldr8(); break;
case 0x3e: ld8(); break; case 0x3e: ldi8(); break;
case 0x40: ldr8(); break;
case 0x41: ldr8(); break;
case 0x42: ldr8(); break;
case 0x43: ldr8(); break;
case 0x44: ldr8(); break;
case 0x45: ldr8(); break;
case 0x47: ldr8(); break;
case 0x48: ldr8(); break;
case 0x49: ldr8(); break;
case 0x4a: ldr8(); break;
case 0x4b: ldr8(); break;
case 0x4c: ldr8(); break;
case 0x4d: ldr8(); break;
case 0x4f: ldr8(); break;
case 0x50: ldr8(); break;
case 0x51: ldr8(); break;
case 0x52: ldr8(); break;
case 0x53: ldr8(); break;
case 0x54: ldr8(); break;
case 0x55: ldr8(); break;
case 0x57: ldr8(); break;
case 0x58: ldr8(); break;
case 0x59: ldr8(); break;
case 0x5a: ldr8(); break;
case 0x5b: ldr8(); break;
case 0x5c: ldr8(); break;
case 0x5d: ldr8(); break;
case 0x5f: ldr8(); break;
case 0x60: ldr8(); break;
case 0x61: ldr8(); break;
case 0x62: ldr8(); break;
case 0x63: ldr8(); break;
case 0x64: ldr8(); break;
case 0x65: ldr8(); break;
case 0x67: ldr8(); break;
case 0x68: ldr8(); break;
case 0x69: ldr8(); break;
case 0x6a: ldr8(); break;
case 0x6b: ldr8(); break;
case 0x6c: ldr8(); break;
case 0x6d: ldr8(); break;
case 0x6f: ldr8(); break;
case 0x70: ldr8(); break;
case 0x71: ldr8(); break;
case 0x72: ldr8(); break;
case 0x73: ldr8(); break;
case 0x74: ldr8(); break;
case 0x75: ldr8(); break;
case 0x77: ldr8(); break;
case 0x78: ldr8(); break;
case 0x79: ldr8(); break;
case 0x7a: ldr8(); break;
case 0x7b: ldr8(); break;
case 0x7c: ldr8(); break;
case 0x7d: ldr8(); break;
case 0x7e: ldr8(); break;
case 0x7f: ldr8(); break;
case 0xa8: xor8(); break; case 0xa8: xor8(); break;
case 0xaf: xor8(); break; case 0xaf: xor8(); break;
case 0xc3: jp16(); break; case 0xc3: jp16(); break;
case 0xc6: add(); break; case 0xc6: add(); break;
case 0xcd: call(); break; case 0xcd: call(); break;
case 0xe0: ldh8(); break; case 0xe0: ldffi8(); break;
case 0xf0: ldh8(); break; case 0xe2: ldr8(); break;
case 0xf8: ld16(); break; case 0xea: ldr8(); break;
case 0xf9: ld16(); break; case 0xf0: ldffi8(); break;
case 0xf2: lda8(); break;
case 0xf8: ldr16(); break;
case 0xf9: ldr16(); break;
case 0xfa: lda8(); break;
default: default:
print("opcode {:#x} not implemented\n", opcode); print("opcode {:#x} not implemented\n", opcode);
print("pc: {:#x}, immediate: {:#x}\n", m_pc, pcRead());
VERIFY_NOT_REACHED(); VERIFY_NOT_REACHED();
} }
} }
@@ -108,18 +175,19 @@ void CPU::add()
uint8_t opcode = pcRead(); uint8_t opcode = pcRead();
uint8_t immediate = pcRead(); uint8_t immediate = pcRead();
switch (opcode) { switch (opcode) {
case 0xc6: case 0xc6: // ADD A,i8, flags: Z 0 H C
// ADD A,d8, flags: Z 0 H C
m_wait_cycles += 8; m_wait_cycles += 8;
// Set flags // Set flags
m_zf = m_a + immediate == 0;
m_nf = 0; m_nf = 0;
m_hf = m_a + immediate > 16; m_hf = isCarry(m_a, immediate, 0x10);
m_cf = m_a + immediate > 255; m_cf = isCarry(m_a, immediate, 0x100);
// A = A + r // A = A + r
m_a = (m_a + immediate) & 0x00ff; m_a = (m_a + immediate) & 0xff;
// Zero flag
m_zf = m_a == 0;
break; break;
default: default:
VERIFY_NOT_REACHED(); VERIFY_NOT_REACHED();
@@ -130,17 +198,18 @@ void CPU::dec8()
{ {
uint8_t opcode = pcRead(); uint8_t opcode = pcRead();
switch (opcode) { switch (opcode) {
case 0x0d: { case 0x0d: { // DEC C, flags: Z 1 H -
// DEC C, flags: Z 1 H -
m_wait_cycles += 4; m_wait_cycles += 4;
m_c = (m_c - 1) & 0x00ff;
// Set flags // Set flags
// TODO
// m_zf = ?
m_nf = 1; m_nf = 1;
// m_hf = ? m_hf = isCarry(m_c, -1, 0x10);
// C = C - 1
m_c = (m_c - 1) & 0xff;
// Zero flag
m_zf = m_c == 0;
break; break;
} }
default: default:
@@ -152,15 +221,13 @@ void CPU::xor8()
{ {
uint8_t opcode = pcRead(); uint8_t opcode = pcRead();
switch (opcode) { switch (opcode) {
case 0xa8: case 0xa8: // XOR B, flags: Z 0 0 0
// XOR B, flags: Z 0 0 0
m_nf = m_hf = m_cf = 0; m_nf = m_hf = m_cf = 0;
m_a ^= m_b; m_a ^= m_b;
m_zf = m_a == 0; m_zf = m_a == 0;
break; break;
case 0xaf: case 0xaf: // XOR A, flags: 1 0 0 0
// XOR A, flags: 1 0 0 0 // A ^ A will always be 0
// A^A will always be 0
m_a = m_nf = m_hf = m_cf = 0; m_a = m_nf = m_hf = m_cf = 0;
m_zf = 1; m_zf = 1;
break; break;
@@ -169,71 +236,11 @@ void CPU::xor8()
} }
} }
void CPU::ld8() void CPU::lda8()
{ {
uint8_t opcode = pcRead(); uint8_t opcode = pcRead();
switch (opcode) { switch (opcode) {
case 0x02: case 0x2a: { // LD A,(HL+) == LD A,(HLI) == LDI A,(HL)
// LD (BC),A
m_wait_cycles += 8;
write(bc(), m_a);
break;
case 0x06:
// LD B,n
m_wait_cycles += 8;
m_b = pcRead();
break;
case 0x0a:
// LD A,(BC)
m_wait_cycles += 8;
m_a = read(bc());
break;
case 0x0e:
// LD C,n
m_wait_cycles += 8;
m_c = pcRead();
break;
case 0x12:
// LD (DE),A
m_wait_cycles += 8;
write(de(), m_a);
break;
case 0x16:
// LD D,n
m_wait_cycles += 8;
m_d = pcRead();
break;
case 0x1a:
// LD A,(DE)
m_wait_cycles += 8;
m_a = read(de());
break;
case 0x1e:
// LD E,n
m_wait_cycles += 8;
m_e = pcRead();
break;
case 0x22: {
// LD (HL+),A == LD (HLI),A == LDI (HL),A
m_wait_cycles += 8;
// Put A into memory address in HL
uint32_t address = hl();
write(address, m_a);
// Increment HL
address = (address + 1) & 0xffff;
m_l = address & 0x00ff;
m_h = address >> 8;
break;
}
case 0x26:
// LD H,n
m_wait_cycles += 8;
m_h = pcRead();
break;
case 0x2a: {
// LD A,(HL+) == LD A,(HLI) == LDI A,(HL)
m_wait_cycles += 8; m_wait_cycles += 8;
// Put value at address in HL into A // Put value at address in HL into A
@@ -242,18 +249,93 @@ void CPU::ld8()
// Increment HL // Increment HL
address = (address + 1) & 0xffff; address = (address + 1) & 0xffff;
m_l = address & 0x00ff; m_l = address & 0xff;
m_h = address >> 8; m_h = address >> 8;
break; break;
} }
case 0x2e: case 0x3a: { // LD A,(HL-) == LD A,(HLD) == LDD A,(HL)
// LD L,n
m_wait_cycles += 8; m_wait_cycles += 8;
m_l = pcRead();
// Put value at address in HL into A
uint32_t address = hl();
m_a = read(address);
// Decrement HL
address = (address + 1) & 0xffff;
m_l = address & 0xff;
m_h = address >> 8;
break; break;
case 0x32: { }
// LD (HL-),A == LD (HLD),A == LDD (HL),A case 0xf2: // LD A,(C)
m_wait_cycles += 8; m_wait_cycles += 8;
m_a = read(m_c);
break;
case 0xfa: // LD A,a16
m_wait_cycles += 16;
m_a = pcRead16();
break;
default:
VERIFY_NOT_REACHED();
}
}
void CPU::ldi8()
{
uint8_t opcode = pcRead();
switch (opcode) {
case 0x06: /* LD B,i8 */ m_b = pcRead(); break;
case 0x0e: /* LD C,i8 */ m_c = pcRead(); break;
case 0x16: /* LD D,i8 */ m_d = pcRead(); break;
case 0x1e: /* LD E,i8 */ m_e = pcRead(); break;
case 0x26: /* LD H,i8 */ m_h = pcRead(); break;
case 0x2e: /* LD L,i8 */ m_l = pcRead(); break;
case 0x36: // LD (HL),i8
m_wait_cycles += 4;
write(hl(), pcRead());
break;
case 0x3e: /* LD A,i8 */ m_a = pcRead(); break;
default:
VERIFY_NOT_REACHED();
}
m_wait_cycles += 8;
}
void CPU::ldr8()
{
uint8_t opcode = pcRead();
switch (opcode) {
case 0x02: // LD (BC),A
m_wait_cycles += 4;
write(bc(), m_a);
break;
case 0x0a: // LD A,(BC)
m_wait_cycles += 4;
m_a = read(bc());
break;
case 0x12: // LD (DE),A
m_wait_cycles += 4;
write(de(), m_a);
break;
case 0x1a: // LD A,(DE)
m_wait_cycles += 4;
m_a = read(de());
break;
case 0x22: { // LD (HL+),A == LD (HLI),A == LDI (HL),A
m_wait_cycles += 4;
// Put A into memory address in HL
uint32_t address = hl();
write(address, m_a);
// Increment HL
address = (address + 1) & 0xffff;
m_l = address & 0xff;
m_h = address >> 8;
break;
}
case 0x32: { // LD (HL-),A == LD (HLD),A == LDD (HL),A
m_wait_cycles += 4;
// Put A into memory address in HL // Put A into memory address in HL
uint32_t address = hl(); uint32_t address = hl();
@@ -261,52 +343,117 @@ void CPU::ld8()
// Decrement HL // Decrement HL
address = (address - 1) & 0xffff; address = (address - 1) & 0xffff;
m_l = address & 0x00ff; m_l = address & 0xff;
m_h = address >> 8; m_h = address >> 8;
break; break;
} }
case 0x36: case 0x40: /* LD B,B m_b = m_b; */ break;
// LD (HL),n case 0x41: /* LD B,C */ m_b = m_c; break;
m_wait_cycles += 12; case 0x42: /* LD B,D */ m_b = m_d; break;
write(hl(), pcRead()); case 0x43: /* LD B,E */ m_b = m_e; break;
case 0x44: /* LD B,H */ m_b = m_h; break;
case 0x45: /* LD B,L */ m_b = m_l; break;
case 0x47: /* LD B,A */ m_b = m_a; break;
case 0x48: /* LD C,B */ m_c = m_b; break;
case 0x49: /* LD C,C m_c = m_c; */ break;
case 0x4a: /* LD C,D */ m_c = m_d; break;
case 0x4b: /* LD C,E */ m_c = m_e; break;
case 0x4c: /* LD C,H */ m_c = m_h; break;
case 0x4d: /* LD C,L */ m_c = m_l; break;
case 0x4f: /* LD C,A */ m_c = m_a; break;
case 0x50: /* LD D,B */ m_d = m_b; break;
case 0x51: /* LD D,C */ m_d = m_c; break;
case 0x52: /* LD D,D m_d = m_d; */ break;
case 0x53: /* LD D,E */ m_d = m_e; break;
case 0x54: /* LD D,H */ m_d = m_h; break;
case 0x55: /* LD D,L */ m_d = m_l; break;
case 0x57: /* LD D,A */ m_d = m_a; break;
case 0x58: /* LD E,B */ m_e = m_b; break;
case 0x59: /* LD E,C */ m_e = m_c; break;
case 0x5a: /* LD E,D */ m_e = m_d; break;
case 0x5b: /* LD E,E m_e = m_e; */ break;
case 0x5c: /* LD E,H */ m_e = m_h; break;
case 0x5d: /* LD E,L */ m_e = m_l; break;
case 0x5f: /* LD E,A */ m_e = m_a; break;
case 0x60: /* LD H,B */ m_h = m_b; break;
case 0x61: /* LD H,C */ m_h = m_c; break;
case 0x62: /* LD H,D */ m_h = m_d; break;
case 0x63: /* LD H,E */ m_h = m_e; break;
case 0x64: /* LD H,H m_h = m_h; */ break;
case 0x65: /* LD H,L */ m_h = m_l; break;
case 0x67: /* LD H,A */ m_h = m_a; break;
case 0x68: /* LD L,B */ m_l = m_b; break;
case 0x69: /* LD L,C */ m_l = m_c; break;
case 0x6a: /* LD L,D */ m_l = m_d; break;
case 0x6b: /* LD L,E */ m_l = m_e; break;
case 0x6c: /* LD L,H */ m_l = m_h; break;
case 0x6d: /* LD L,L m_l = m_l; */ break;
case 0x6f: /* LD L,A */ m_l = m_a; break;
case 0x70: // LD (HL),B
m_wait_cycles += 4;
write(hl(), m_b);
break; break;
case 0x3a: { case 0x71: // LD (HL),C
// LD A,(HL-) == LD A,(HLD) == LDD A,(HL) m_wait_cycles += 4;
m_wait_cycles += 8; write(hl(), m_c);
// Put value at address in HL into A
uint32_t address = hl();
m_a = read(address);
// Decrement HL
address = (address + 1) & 0xffff;
m_l = address & 0x00ff;
m_h = address >> 8;
break; break;
} case 0x72: // LD (HL),D
case 0x3e: m_wait_cycles += 4;
// LD A,n write(hl(), m_d);
m_wait_cycles += 8; break;
m_a = pcRead(); case 0x73: // LD (HL),E
m_wait_cycles += 4;
write(hl(), m_e);
break;
case 0x74: // LD (HL),H
m_wait_cycles += 4;
write(hl(), m_h);
break;
case 0x75: // LD (HL),L
m_wait_cycles += 4;
write(hl(), m_l);
break;
case 0x77: // LD (HL),A
m_wait_cycles += 4;
write(hl(), m_a);
break;
case 0x78: /* LD A,B */ m_a = m_b; break;
case 0x79: /* LD A,B */ m_a = m_c; break;
case 0x7a: /* LD A,D */ m_a = m_d; break;
case 0x7b: /* LD A,E */ m_a = m_e; break;
case 0x7c: /* LD A,H */ m_a = m_h; break;
case 0x7d: /* LD A,L */ m_a = m_l; break;
case 0x7e: // LD A,(HL)
m_wait_cycles += 4;
m_a = read(hl());
break;
case 0x7f: /* LD A,A m_a = m_a; */ break;
case 0xe2: // LD (C),A
m_wait_cycles += 4;
write(m_c, m_a);
break;
case 0xea: // LD a16,A
m_wait_cycles += 16;
write(pcRead16(), m_a);
break; break;
default: default:
VERIFY_NOT_REACHED(); VERIFY_NOT_REACHED();
} }
m_wait_cycles += 4;
} }
void CPU::ldh8() void CPU::ldffi8()
{ {
uint8_t opcode = pcRead(); uint8_t opcode = pcRead();
switch (opcode) { switch (opcode) {
case 0xe0: case 0xe0: // LD ($ff00 + i8),A == LDH (io8),A
// LD ($ff00 + n),A == LDH (n),A
m_wait_cycles += 12; m_wait_cycles += 12;
// Put value in A into address (0xff00 + next byte in memory) // Put value in A into address (0xff00 + next byte in memory)
ffWrite(pcRead(), m_a); ffWrite(pcRead(), m_a);
break; break;
case 0xf0: case 0xf0: // LD A,($ff00 + i8) == LDH A,(io8)
// LD A,($ff00 + n) == LDH A,(n)
m_wait_cycles += 12; m_wait_cycles += 12;
// Put value at address (0xff00 + next byte in memory) into A // Put value at address (0xff00 + next byte in memory) into A
@@ -317,52 +464,60 @@ void CPU::ldh8()
} }
} }
void CPU::ld16() void CPU::ldi16()
{ {
uint8_t opcode = pcRead(); uint8_t opcode = pcRead();
switch (opcode) { switch (opcode) {
case 0x01: { case 0x01: /* LD BC,i16 */
m_wait_cycles += 12; m_c = pcRead();
write(bc(), pcRead16()); m_b = pcRead();
break; break;
case 0x11: /* LD DE,i16 */
m_e = pcRead();
m_d = pcRead();
break;
case 0x21: /* LD HL,i16 */
m_l = pcRead();
m_h = pcRead();
break;
case 0x31: /* LD SP,i16 */
m_sp = pcRead16();
break;
default:
VERIFY_NOT_REACHED();
} }
case 0x08: {
// LD (nn),SP m_wait_cycles += 12;
}
void CPU::ldr16()
{
uint8_t opcode = pcRead();
switch (opcode) {
case 0x08: { // LD a16,SP
m_wait_cycles += 20; m_wait_cycles += 20;
// Put value of SP into address given by next 2 bytes in memory // Put value of SP into address given by next 2 bytes in memory
// TODO write(pcRead16(), m_sp);
break; break;
} }
case 0x11: case 0xf8: { // LD HL,SP + s8 == LDHL SP,s8, flags: 0 0 H C
// LD DE,nn
m_wait_cycles += 12;
write(de(), pcRead16());
break;
case 0x21:
// LD HL,nn
m_wait_cycles += 12;
write(hl(), pcRead16());
break;
case 0x31: {
// LD SP,nn
m_wait_cycles += 12;
m_sp = pcRead16();
break;
}
case 0xf8: {
// LD HL,SP + e8 == LDHL SP,e8
m_wait_cycles += 12; m_wait_cycles += 12;
// Put SP + next (signed) byte in memory into HL // Put SP + next (signed) byte in memory into HL
// TODO uint32_t signed_data = (pcRead() ^ 0x80) - 0x80;
uint32_t sum = m_sp + signed_data;
m_h = sum >> 8;
m_l = sum & 0xff;
// Unsets ZF and NF, may enable HF and CF // Set flags
// TODO flags m_zf = 0;
m_nf = 0;
m_hf = isCarry(m_sp, signed_data, 0x10);
m_cf = isCarry(m_sp, signed_data, 0x100);
break; break;
} }
case 0xf9: { case 0xf9: { // LD SP,HL
// LD SP,HL
m_wait_cycles += 8; m_wait_cycles += 8;
m_sp = hl(); m_sp = hl();
break; break;
@@ -376,8 +531,7 @@ void CPU::call()
{ {
uint8_t opcode = pcRead(); uint8_t opcode = pcRead();
switch (opcode) { switch (opcode) {
case 0xcd: { case 0xcd: { // CALL a16
// CALL nn
m_wait_cycles += 24; m_wait_cycles += 24;
uint32_t data = pcRead16(); uint32_t data = pcRead16();
@@ -386,7 +540,7 @@ void CPU::call()
m_sp = (m_sp - 1) & 0xffff; m_sp = (m_sp - 1) & 0xffff;
write(m_sp, data >> 8); write(m_sp, data >> 8);
m_sp = (m_sp - 1) & 0xffff; m_sp = (m_sp - 1) & 0xffff;
write(m_sp, data & 0x00ff); write(m_sp, data & 0xff);
// Jump to this address // Jump to this address
m_pc = data; m_pc = data;
@@ -401,8 +555,7 @@ void CPU::jp16()
{ {
uint8_t opcode = pcRead(); uint8_t opcode = pcRead();
switch (opcode) { switch (opcode) {
case 0xc3: case 0xc3: // JP a16
// JP nn
m_wait_cycles += 16; m_wait_cycles += 16;
m_pc = pcRead16(); m_pc = pcRead16();
break; break;
@@ -416,8 +569,7 @@ void CPU::jr8()
{ {
uint8_t opcode = pcRead(); uint8_t opcode = pcRead();
switch (opcode) { switch (opcode) {
case 0x20: { case 0x20: { // JR NZ,s8
// JR NZ,e8
m_wait_cycles += 8; m_wait_cycles += 8;
if (!m_zf) { if (!m_zf) {
@@ -435,12 +587,11 @@ void CPU::misc()
{ {
uint8_t opcode = pcRead(); uint8_t opcode = pcRead();
switch (opcode) { switch (opcode) {
case 0x2f: case 0x2f: // CPL, flags: - 1 1 -
// CPL, flags: - 1 1 -
m_wait_cycles += 4; m_wait_cycles += 4;
// Complement register A (flip all bits) // Complement register A (flip all bits)
m_a ^= 0x00ff; // equivalent to: m_a = ~m_a & 0x00ff m_a ^= 0xff; // equivalent to: m_a = ~m_a & 0xff
// Set flags // Set flags
m_nf = m_hf = 1; m_nf = m_hf = 1;
@@ -454,29 +605,57 @@ void CPU::misc()
uint32_t CPU::pcRead() uint32_t CPU::pcRead()
{ {
uint32_t data = Emu::the().readMemory(m_pc) & 0x00ff; uint32_t data = Emu::the().readMemory(m_pc) & 0xff;
m_pc = (m_pc + 1) & 0xffff; m_pc = (m_pc + 1) & 0xffff;
return data; return data;
} }
void CPU::write(uint32_t address, uint32_t value) void CPU::write(uint32_t address, uint32_t value)
{ {
Emu::the().writeMemory(address, value & 0x00ff); Emu::the().writeMemory(address, value & 0xff);
} }
uint32_t CPU::read(uint32_t address) uint32_t CPU::read(uint32_t address)
{ {
return Emu::the().readMemory(address) & 0x00ff; return Emu::the().readMemory(address) & 0xff;
} }
void CPU::ffWrite(uint32_t address, uint32_t value) void CPU::ffWrite(uint32_t address, uint32_t value)
{ {
Emu::the().writeMemory(address | (0xff << 8), value & 0x00ff); Emu::the().writeMemory(address | (0xff << 8), value & 0xff);
} }
uint32_t CPU::ffRead(uint32_t address) uint32_t CPU::ffRead(uint32_t address)
{ {
return Emu::the().readMemory(address | (0xff << 8)) & 0x00ff; return Emu::the().readMemory(address | (0xff << 8)) & 0xff;
}
bool CPU::isCarry(uint32_t lhs, uint32_t rhs, uint32_t limit_bit)
{
// limit_bit values:
// 8-bit half-carry = 0x10 or 16
// 8-bit carry = 0x100 or 256
// 16-bit half-carry = 0x1000 or 4096
// 16-bit carry = 0x10000 or 65536
// Example for 8-bit half-carry:
// 0b00111110 62 | 0b00111000 56
// 0b00100010 34 + | 0b00010001 17 +
// --------------- | ---------------
// 0b01100000 96 | 0b01001001 73
// |
// 0b00111110 62 | 0b00111000 56
// 0b00100010 34 ^ | 0b00010001 17 ^
// --------------- | ---------------
// 0b00011100 | 0b00101001
// 0b01100000 96 ^ | 0b01001001 73 ^
// --------------- | ---------------
// 0b01111100 | 0b01100000
// 0b00010000 16 & | 0b00010000 16 &
// --------------- | ---------------
// 0b00010000 = true! | 0b00000000 = false!
return (lhs ^ rhs ^ (lhs + rhs)) & limit_bit;
} }
// ----------------------------------------- // -----------------------------------------
+12 -4
View File
@@ -7,7 +7,7 @@
#pragma once #pragma once
#include <cstdint> // int8_t, int32_t, uint8_t, uint32_t #include <cstdint> // int8_t, uint8_t, uint32_t
#include <functional> // std::function #include <functional> // std::function
#include <unordered_map> #include <unordered_map>
@@ -15,6 +15,9 @@
#include "ruc/format/formatter.h" #include "ruc/format/formatter.h"
class CPU final : public ProcessingUnit { class CPU final : public ProcessingUnit {
private:
friend struct CPUTest;
public: public:
explicit CPU(uint32_t frequency); explicit CPU(uint32_t frequency);
virtual ~CPU(); virtual ~CPU();
@@ -41,11 +44,14 @@ public:
// Load Instructions // Load Instructions
// 8-bit // 8-bit
void ld8(); void ldffi8();
void ldh8(); void lda8();
void ldi8();
void ldr8();
// 16-bit // 16-bit
void ld16(); void ldi16();
void ldr16();
// ------------------------------------- // -------------------------------------
// Jumps and Subroutines // Jumps and Subroutines
@@ -81,6 +87,8 @@ private:
void ffWrite(uint32_t address, uint32_t value); void ffWrite(uint32_t address, uint32_t value);
uint32_t ffRead(uint32_t address); uint32_t ffRead(uint32_t address);
bool isCarry(uint32_t lhs, uint32_t rhs, uint32_t limit_bit);
// Registers // Registers
uint32_t m_a { 0 }; // Accumulator uint32_t m_a { 0 }; // Accumulator
uint32_t m_b { 0 }; // B uint32_t m_b { 0 }; // B
+49
View File
@@ -0,0 +1,49 @@
/*
* Copyright (C) 2022 Riyyi
*
* SPDX-License-Identifier: MIT
*/
#include "cpu.h"
#include "macro.h"
#include "testcase.h"
#include "testsuite.h"
struct CPUTest {
CPU cpu { 0 };
bool isCarry(uint32_t lhs, uint32_t rhs, uint32_t limit_bit) { return cpu.isCarry(lhs, rhs, limit_bit); }
};
// -----------------------------------------
TEST_CASE(CPUIsCarry)
{
CPUTest test;
// 8-bit Half-carry false
EXPECT_EQ(test.isCarry(56, 17, 0x10), false);
EXPECT_EQ(test.isCarry(16, -1, 0x10), false);
EXPECT_EQ(test.isCarry(32, -1, 0x10), false);
EXPECT_EQ(test.isCarry(144, -1, 0x10), false);
EXPECT_EQ(test.isCarry(211, -20, 0x10), false);
// 8-bit Half-carry true
EXPECT_EQ(test.isCarry(46, 34, 0x10), true);
EXPECT_EQ(test.isCarry(62, 34, 0x10), true);
EXPECT_EQ(test.isCarry(136, -1, 0x10), true);
EXPECT_EQ(test.isCarry(219, -20, 0x10), true);
EXPECT_EQ(test.isCarry(250, -20, 0x10), true);
// 8-bit Full carry false
EXPECT_EQ(test.isCarry(254, 1, 0x100), false);
// 8-bit Full carry true
EXPECT_EQ(test.isCarry(254, 2, 0x100), true);
}
-17
View File
@@ -1,17 +0,0 @@
/*
* Copyright (C) 2022 Riyyi
*
* SPDX-License-Identifier: MIT
*/
#include "macro.h"
#include "testcase.h"
#include "testsuite.h"
// -----------------------------------------
TEST_CASE(Thing)
{
int a = 5;
EXPECT_EQ(a, 5);
}
Vendored
+1 -1
Submodule vendor/ruc updated: 27489c6cb4...f31f7feb5e