Compare commits

..
21 Commits
Author SHA1 Message Date
Riyyi 3b132a6820 Test: Add way to run opcodes in test cases, add test for ADC, POP, PUSH 2022-09-01 18:05:33 +02:00
Riyyi b63f0d3b52 Test: Update CPU test for new carry logic 2022-09-01 17:34:48 +02:00
Riyyi b48f308bfc Emulator: Simplify isCarry(Subtraction) function 2022-09-01 17:23:07 +02:00
Riyyi 059441c30a Emulator: Fix ADC/CP/DEC/SBC/SUB opcode: 0x8e, 0xbe, 0x35, 0x93, 0x96
Most of these were broken on the carry and half-carry calculation.
2022-09-01 16:59:27 +02:00
Riyyi a4743fa77b Emulator: Fix LD A,(HL-) opcode: 0x3a 2022-09-01 14:26:58 +02:00
Riyyi 70778840c7 Emulator: Fix SLA/SRA/SRL opcodes: 0x26, 0x2e, 0x3e 2022-09-01 14:26:58 +02:00
Riyyi c396e7fcf6 Emulator: Fix LD L,(HL) opcode 0x6e 2022-09-01 14:26:58 +02:00
Riyyi 92c0236f2a Emulator: Rename ldffi8 => ldff8 and move two opcodes to it 2022-09-01 14:26:58 +02:00
Riyyi f56984b555 Emulator: Fix LD A,(0xff00 + C) and its reverse, opcodes: 0xe2, 0xf2 2022-09-01 14:26:58 +02:00
Riyyi 1dd9a2c16e Doc: Add definition for lsb/msb, (least|most) significant byte 2022-09-01 14:26:58 +02:00
Riyyi 1c0809b611 Emulator: Fix LD a16,SP opcode: 0x08 2022-09-01 14:26:58 +02:00
Riyyi c8ad40c128 Emulator: Fix LD A,a16 opcode: 0xfa 2022-09-01 14:26:58 +02:00
Riyyi cc46a45a73 Emulator: Print blargg's test ROM output 2022-09-01 14:26:58 +02:00
Riyyi ed045a72b2 Emulator: Prepare for running blargg's test ROMs 2022-09-01 14:20:57 +02:00
Riyyi 38b740856e Emulator: Fix 2 opcodes ending up in the wrong location 2022-09-01 00:51:35 +02:00
Riyyi e1eed33410 Emulator: Fake screen scrolling to pass the bootrom 2022-09-01 00:51:35 +02:00
Riyyi 2b7d4dee3d Emulator: Add LCDC register to PPU 2022-08-31 21:43:56 +02:00
Riyyi 5831a23b02 Meta: Update ruc library 2022-08-31 21:33:09 +02:00
Riyyi c7903c0a50 Emulator: Fix address pushed onto stack in CALL opcode 2022-08-31 20:30:23 +02:00
Riyyi 5d054c90f8 Emulator: Update comment wording 2022-08-31 20:21:44 +02:00
Riyyi 5a690a12d2 Meta: Add script to check for missing opcodes 2022-08-31 10:16:11 +02:00
10 changed files with 332 additions and 131 deletions
+5 -2
View File
@@ -66,12 +66,15 @@ Absolute fixed address, possible values are[[#references][(4)]]: ~0x00~, ~0x08~,
*** Variables *** Variables
| Name | Meaning | | Name | Meaning |
|--------+--------------------| |--------+------------------------|
| length | amount of elements | | length | amount of elements |
| size | amount of bytes | | size | amount of bytes |
|--------+--------------------| |--------+------------------------|
| lhs | left hand side | | lhs | left hand side |
| rhs | right hand side | | rhs | right hand side |
|--------+------------------------|
| lsb | least significant byte |
| msb | most significant byte |
** References ** References
+23
View File
@@ -0,0 +1,23 @@
#!/bin/sh
# Get the full path to this script while handling spaces and symlinks correctly
scriptPath="$(cd -P -- "$(dirname -- "$0")" && pwd -P)"
cd "$scriptPath/.." || exit 1
opcode_check() {
path="$1"
file="$(basename "$path")"
max=255
for i in $(seq 1 "$max")
do
opcode=$(printf "0x%.2x\n" "$i")
# Pattern: case 0x??:
if ! grep -q "case $opcode:" "$path"; then
echo "missing opcode: $opcode in $file"
fi
done
}
opcode_check "src/cpu.cpp"
opcode_check "src/cpu-prefix.cpp"
+14 -14
View File
@@ -19,7 +19,7 @@ void CPU::prefix()
// Read next opcode // Read next opcode
uint8_t opcode = read(m_pc); uint8_t opcode = read(m_pc);
print("running opcode: {:#04x} @ ({:#06x})\n", opcode, m_pc); // print("running opcode: {:#04x} @ ({:#06x})\n", opcode, m_pc);
if (opcode <= 0x07) { if (opcode <= 0x07) {
rlc(); rlc();
} }
@@ -369,7 +369,7 @@ void CPU::swap()
register_ = (register_ >> 4) | (register_ << 4); register_ = (register_ >> 4) | (register_ << 4);
// Set flags // Set flags
m_zf = register_ == 0; m_zf = (register_ == 0);
m_nf = 0; m_nf = 0;
m_hf = 0; m_hf = 0;
m_cf = 0; m_cf = 0;
@@ -412,7 +412,7 @@ void CPU::rl()
register_ = (old_carry | (register_ << 1)) & 0xff; register_ = (old_carry | (register_ << 1)) & 0xff;
// Set other flags // Set other flags
m_zf = register_ == 0; m_zf = (register_ == 0);
m_nf = 0; m_nf = 0;
m_hf = 0; m_hf = 0;
}; };
@@ -452,7 +452,7 @@ void CPU::rlc()
register_ = ((register_ >> 7) | (register_ << 1)) & 0xff; register_ = ((register_ >> 7) | (register_ << 1)) & 0xff;
// Set other flags // Set other flags
m_zf = register_ == 0; m_zf = (register_ == 0);
m_nf = 0; m_nf = 0;
m_hf = 0; m_hf = 0;
}; };
@@ -488,13 +488,13 @@ void CPU::rr()
// Copy bit 0 into carry flag // Copy bit 0 into carry flag
uint32_t old_carry = m_cf != 0; uint32_t old_carry = m_cf != 0;
m_cf = (register_ & 0x01) == 0x01; m_cf = (register_ & 0x1) == 0x1;
// Rotate register r8 right through carry // Rotate register r8 right through carry
register_ = ((register_ >> 1) | (old_carry << 7)) & 0xff; register_ = ((register_ >> 1) | (old_carry << 7)) & 0xff;
// Set other flags // Set other flags
m_zf = register_ == 0; m_zf = (register_ == 0);
m_nf = 0; m_nf = 0;
m_hf = 0; m_hf = 0;
}; };
@@ -534,7 +534,7 @@ void CPU::rrc()
register_ = ((register_ >> 1) | (register_ << 7)) & 0xff; register_ = ((register_ >> 1) | (register_ << 7)) & 0xff;
// Set other flags // Set other flags
m_zf = register_ == 0; m_zf = (register_ == 0);
m_nf = 0; m_nf = 0;
m_hf = 0; m_hf = 0;
}; };
@@ -575,13 +575,13 @@ void CPU::sla()
// r8 // r8
// Copy bit 7 into carry flag // Copy bit 7 into carry flag
m_cf = (m_a & 0x80) == 0x80; m_cf = (register_ & 0x80) == 0x80;
// Shift Left Arithmetically register r8 // Shift Left Arithmetically register r8
register_ = (register_ << 1) & 0xff; register_ = (register_ << 1) & 0xff;
// Set other flags // Set other flags
m_zf = register_ == 0; m_zf = (register_ == 0);
m_nf = 0; m_nf = 0;
m_hf = 0; m_hf = 0;
}; };
@@ -623,13 +623,13 @@ void CPU::sra()
// └──┘ // └──┘
// Copy bit 0 into carry flag // Copy bit 0 into carry flag
m_cf = (m_a & 0x01) == 0x01; m_cf = (register_ & 0x1) == 0x1;
// Shift Right Arithmatically register r8 // Shift Right Arithmatically register r8
register_ = (register_ >> 1) | (register_ & 0x80); // Note: bit 7 remains register_ = (register_ >> 1) | (register_ & 0x80); // Note: bit 7 remains
// Set other flags // Set other flags
m_zf = register_ == 0; m_zf = (register_ == 0);
m_nf = 0; m_nf = 0;
m_hf = 0; m_hf = 0;
}; };
@@ -670,13 +670,13 @@ void CPU::srl()
// r8 // r8
// Copy bit 0 into carry flag // Copy bit 0 into carry flag
m_cf = (m_a & 0x01) == 0x01; m_cf = (register_ & 0x1) == 0x1;
// Shift Right Locically register r8 // Shift Right Locically register r8
register_ = (register_ >> 1) & 0x7f; // Note: bit 7 is set to 0 register_ = (register_ >> 1) & 0x7f; // Note: bit 7 is set to 0
// Set other flags // Set other flags
m_zf = register_ == 0; m_zf = (register_ == 0);
m_nf = 0; m_nf = 0;
m_hf = 0; m_hf = 0;
}; };
@@ -692,7 +692,7 @@ void CPU::srl()
case 0x3e: /* SRL (HL) */ { case 0x3e: /* SRL (HL) */ {
m_wait_cycles += 8; // + 8 = 16 total m_wait_cycles += 8; // + 8 = 16 total
// Rotate the byte pointed to by HL right // Shift right logically the byte pointed to by HL
uint32_t data = read(hl()); uint32_t data = read(hl());
shift_right_logically(data); shift_right_logically(data);
write(hl(), data); write(hl(), data);
+90 -86
View File
@@ -58,15 +58,20 @@ CPU::~CPU()
void CPU::update() void CPU::update()
{ {
m_wait_cycles--; m_wait_cycles--;
// TODO: convert to early-return
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); // 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: {:#04x} @ ({:#06x})\n", opcode, m_pc); // print("running opcode: {:#04x} @ ({:#06x})\n", opcode, m_pc);
switch (opcode) { switch (opcode) {
case 0x10: /* TODO */ m_pc += 2; break;
case 0xf3: /* TODO */ m_pc += 1; break;
case 0xfb: /* TODO */ m_pc += 1; break;
case 0x00: nop(); break; case 0x00: nop(); break;
case 0x01: ldi16(); break; case 0x01: ldi16(); break;
case 0x02: ldr8(); break; case 0x02: ldr8(); break;
@@ -124,7 +129,7 @@ void CPU::update()
case 0x37: misc(); break; case 0x37: misc(); break;
case 0x38: jrs8(); break; case 0x38: jrs8(); break;
case 0x39: addr16(); break; case 0x39: addr16(); break;
case 0x3a: ldr8(); break; case 0x3a: lda8(); break;
case 0x3b: dec16(); break; case 0x3b: dec16(); break;
case 0x3c: inc8(); break; case 0x3c: inc8(); break;
case 0x3d: dec8(); break; case 0x3d: dec8(); break;
@@ -281,13 +286,14 @@ void CPU::update()
case 0xd6: sub8(); break; case 0xd6: sub8(); break;
case 0xd7: rst(); break; case 0xd7: rst(); break;
case 0xd8: ret(); break; case 0xd8: ret(); break;
case 0xd9: ret(); break;
case 0xda: jp16(); break; case 0xda: jp16(); break;
case 0xdc: call(); break; case 0xdc: call(); break;
case 0xde: sbc8(); break; case 0xde: sbc8(); break;
case 0xdf: rst(); break; case 0xdf: rst(); break;
case 0xe0: ldffi8(); break; case 0xe0: ldff8(); break;
case 0xe1: pop(); break; case 0xe1: pop(); break;
case 0xe2: ldr8(); break; case 0xe2: ldff8(); break;
case 0xe5: push(); break; case 0xe5: push(); break;
case 0xe6: and8(); break; case 0xe6: and8(); break;
case 0xe7: rst(); break; case 0xe7: rst(); break;
@@ -296,9 +302,9 @@ void CPU::update()
case 0xea: ldr8(); break; case 0xea: ldr8(); break;
case 0xee: xor8(); break; case 0xee: xor8(); break;
case 0xef: rst(); break; case 0xef: rst(); break;
case 0xf0: ldffi8(); break; case 0xf0: ldff8(); break;
case 0xf1: pop(); break; case 0xf1: pop(); break;
case 0xf2: lda8(); break; case 0xf2: ldff8(); break;
case 0xf5: push(); break; case 0xf5: push(); break;
case 0xf6: or8(); break; case 0xf6: or8(); break;
case 0xf7: rst(); break; case 0xf7: rst(); break;
@@ -337,16 +343,18 @@ void CPU::adc8()
// ADC A,r8, flags: Z 0 H C // ADC A,r8, flags: Z 0 H C
m_wait_cycles += 4; m_wait_cycles += 4;
uint32_t old_carry = m_cf != 0;
// Set flags // Set flags
m_nf = 0; m_nf = 0;
m_hf = isCarry(m_a, register_, 0x10); m_hf = isCarry(0xf, m_a, register_, old_carry);
m_cf = isCarry(m_a, register_, 0x100); m_cf = isCarry(0xff, m_a, register_, old_carry);
// Add the value in r8 plus the carry flag to A // Add the value in r8 plus the carry flag to A
m_a = (m_a + register_ + (m_cf) ? 0x1 : 0) & 0xff; m_a = (m_a + register_ + old_carry) & 0xff;
// Zero flag // Zero flag
m_zf = m_a == 0; m_zf = (m_a == 0);
}; };
uint8_t opcode = pcRead(); uint8_t opcode = pcRead();
@@ -383,8 +391,8 @@ void CPU::add8()
// Set flags // Set flags
m_nf = 0; m_nf = 0;
m_hf = isCarry(m_a, register_, 0x10); m_hf = isCarry(0xf, m_a, register_);
m_cf = isCarry(m_a, register_, 0x100); m_cf = isCarry(0xff, m_a, register_);
// Add the value in r8 to A // Add the value in r8 to A
m_a = (m_a + register_) & 0xff; m_a = (m_a + register_) & 0xff;
@@ -478,9 +486,8 @@ void CPU::cp()
// Zero flag // Zero flag
m_zf = ((m_a - register_) & 0xff) == 0; m_zf = ((m_a - register_) & 0xff) == 0;
m_nf = 1; m_nf = 1;
// Note: for negation between two positive numbers the carry result is flipped! m_hf = isCarrySubtraction(0xf, m_a, register_);
m_hf = !isCarry(m_a, register_, 0x10); m_cf = isCarrySubtraction(0xff, m_a, register_);
m_cf = !isCarry(m_a, register_, 0x100);
}; };
uint8_t opcode = pcRead(); uint8_t opcode = pcRead();
@@ -580,13 +587,13 @@ void CPU::dec8()
// Set flags // Set flags
m_nf = 1; m_nf = 1;
m_hf = isCarry(register_, -1, 0x10); m_hf = isCarrySubtraction(0xf, register_, 1);
// Decrement value in register r8 by 1 // Decrement value in register r8 by 1
register_ = (register_ - 1) & 0xff; register_ = (register_ - 1) & 0xff;
// Zero flag // Zero flag
m_zf = register_ == 0; m_zf = (register_ == 0);
}; };
uint8_t opcode = pcRead(); uint8_t opcode = pcRead();
@@ -620,7 +627,7 @@ void CPU::inc8()
// Set flags // Set flags
m_nf = 0; m_nf = 0;
m_hf = isCarry(register_, 1, 0x10); m_hf = isCarry(0xf, register_, 1);
// Increment value in register r8 by 1 // Increment value in register r8 by 1
register_ = (register_ + 1) & 0xff; register_ = (register_ + 1) & 0xff;
@@ -706,15 +713,14 @@ void CPU::sbc8()
// Set flags // Set flags
m_nf = 1; m_nf = 1;
// Note: for negation between two positive numbers the carry result is flipped! m_hf = isCarrySubtraction(0xf, m_a, register_, old_carry);
m_hf = !isCarry(m_a, register_, 0x10); m_cf = isCarrySubtraction(0xff, m_a, register_, old_carry);
m_cf = !isCarry(m_a, (register_ + (old_carry) ? 1 : 0), 0x100);
// Subtract the value in r8 and the carry flag from A // Subtract the value in r8 and the carry flag from A
m_a = (m_a - register_ - (old_carry) ? 1 : 0) & 0xff; m_a = (m_a - register_ - old_carry) & 0xff;
// Zero flag // Zero flag
m_zf = m_a == 0; m_zf = (m_a == 0);
}; };
uint8_t opcode = pcRead(); uint8_t opcode = pcRead();
@@ -753,9 +759,8 @@ void CPU::sub8()
// Set flags // Set flags
m_nf = 1; m_nf = 1;
// Note: for negation between two positive numbers the carry result is flipped! m_hf = isCarrySubtraction(0xf, m_a, register_);
m_hf = !isCarry(m_a, register_, 0x10); m_cf = isCarrySubtraction(0xff, m_a, register_);
m_cf = !isCarry(m_a, register_, 0x100);
// Subtract the value in r8 from A // Subtract the value in r8 from A
m_a = (m_a - register_) & 0xff; m_a = (m_a - register_) & 0xff;
@@ -843,7 +848,7 @@ void CPU::lda8()
case 0x2a: { // LD A,(HL+) == LD A,(HLI) == LDI A,(HL) 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 // Load value into register A from the byte pointed by HL
uint32_t address = hl(); uint32_t address = hl();
m_a = read(address); m_a = read(address);
@@ -856,23 +861,21 @@ void CPU::lda8()
case 0x3a: { // LD A,(HL-) == LD A,(HLD) == LDD A,(HL) case 0x3a: { // LD A,(HL-) == LD A,(HLD) == LDD A,(HL)
m_wait_cycles += 8; m_wait_cycles += 8;
// Put value at address in HL into A // Load value into register A from the byte pointed by HL
uint32_t address = hl(); uint32_t address = hl();
m_a = read(address); m_a = read(address);
// Decrement HL // Decrement HL
address = (address + 1) & 0xffff; address = (address - 1) & 0xffff;
m_l = address & 0xff; m_l = address & 0xff;
m_h = address >> 8; m_h = address >> 8;
break; break;
} }
case 0xf2: // LD A,(C)
m_wait_cycles += 8;
m_a = read(m_c);
break;
case 0xfa: // LD A,a16 case 0xfa: // LD A,a16
m_wait_cycles += 16; m_wait_cycles += 16;
m_a = pcRead16();
// Load value into register A from the byte pointed to by register r16
m_a = read(pcRead16());
break; break;
default: default:
VERIFY_NOT_REACHED(); VERIFY_NOT_REACHED();
@@ -887,8 +890,8 @@ void CPU::addr16()
// Set flags // Set flags
m_nf = 0; m_nf = 0;
m_hf = isCarry(hl(), register_, 0x1000); m_hf = isCarry(0xfff, hl(), register_);
m_cf = isCarry(hl(), register_, 0x10000); m_cf = isCarry(0xffff, hl(), register_);
// Add the value in r16 to HL // Add the value in r16 to HL
uint32_t data = (hl() + register_) & 0xffff; uint32_t data = (hl() + register_) & 0xffff;
@@ -919,8 +922,8 @@ void CPU::adds8()
// Set flags // Set flags
m_zf = 0; m_zf = 0;
m_nf = 0; m_nf = 0;
m_hf = isCarry(m_sp, signed_data, 0x10); m_hf = isCarry(0xf, m_sp, signed_data);
m_cf = isCarry(m_sp, signed_data, 0x100); m_cf = isCarry(0xff, m_sp, signed_data);
// Add the signed value s8 to SP // Add the signed value s8 to SP
m_sp = m_sp + signed_data; m_sp = m_sp + signed_data;
@@ -953,7 +956,7 @@ void CPU::inc16()
case 0x03: /* INC BC */ setBC(bc() + 1); break; case 0x03: /* INC BC */ setBC(bc() + 1); break;
case 0x13: /* INC DE */ setDE(de() + 1); break; case 0x13: /* INC DE */ setDE(de() + 1); break;
case 0x23: /* INC HL */ setHL(hl() + 1); break; case 0x23: /* INC HL */ setHL(hl() + 1); break;
case 0x24: /* INC SP */ m_sp = (m_sp + 1) & 0xffff; break; case 0x33: /* INC SP */ m_sp = (m_sp + 1) & 0xffff; break;
default: default:
VERIFY_NOT_REACHED(); VERIFY_NOT_REACHED();
} }
@@ -1103,7 +1106,7 @@ void CPU::ldr8()
case 0x6d: /* LD L,L m_l = m_l; */ break; case 0x6d: /* LD L,L m_l = m_l; */ break;
case 0x6e: /* LD L,(HL) */ { case 0x6e: /* LD L,(HL) */ {
m_wait_cycles += 4; // + 4 = 8 total m_wait_cycles += 4; // + 4 = 8 total
m_e = read(hl()); m_l = read(hl());
break; break;
} }
case 0x6f: /* LD L,A */ m_l = m_a; break; case 0x6f: /* LD L,A */ m_l = m_a; break;
@@ -1154,13 +1157,10 @@ void CPU::ldr8()
break; break;
} }
case 0x7f: /* LD A,A m_a = m_a; */ break; case 0x7f: /* LD A,A m_a = m_a; */ break;
case 0xe2: /* LD (C),A */ {
m_wait_cycles += 4; // + 4 = 8 total
write(m_c, m_a);
break;
}
case 0xea: /* LD a16,A */ { case 0xea: /* LD a16,A */ {
m_wait_cycles += 12; // + 4 = 16 total m_wait_cycles += 12; // + 4 = 16 total
// Store value in register A into the byte at address a16
write(pcRead16(), m_a); write(pcRead16(), m_a);
break; break;
} }
@@ -1256,22 +1256,34 @@ void CPU::ra()
m_hf = 0; m_hf = 0;
} }
void CPU::ldffi8() void CPU::ldff8()
{ {
uint8_t opcode = pcRead(); uint8_t opcode = pcRead();
switch (opcode) { switch (opcode) {
case 0xe0: // LD ($ff00 + i8),A == LDH (io8),A case 0xe0: // LD (0xff00 + s8),A == LDH (io8),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: // LD A,($ff00 + i8) == LDH A,(io8) case 0xe2: // LD (0xff00 + C),A == LDH (C),A
m_wait_cycles += 8;
// Store value in register A into the byte at address 0xff00 + C
ffWrite(m_c, m_a);
break;
case 0xf0: // LD A,(0xff00 + s8) == LDH A,(io8)
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
m_a = ffRead(pcRead()); m_a = ffRead(pcRead());
break; break;
case 0xf2: // LD A,(0xff00 + C) == LDH A,(C)
m_wait_cycles += 8;
// Load value into register A from the byte at address 0xff00 + C
m_a = ffRead(m_c);
break;
default: default:
VERIFY_NOT_REACHED(); VERIFY_NOT_REACHED();
} }
@@ -1311,7 +1323,9 @@ void CPU::ldr16()
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
write(pcRead16(), m_sp); uint32_t address = pcRead16();
write(address, m_sp & 0xff); // lsb(SP)
write((address + 1) & 0xffff, m_sp >> 8); // msb(SP)
break; break;
} }
case 0xf8: { // LD HL,SP + s8 == LDHL SP,s8, flags: 0 0 H C case 0xf8: { // LD HL,SP + s8 == LDHL SP,s8, flags: 0 0 H C
@@ -1320,18 +1334,20 @@ void CPU::ldr16()
// Put SP + next (signed) byte in memory into HL // Put SP + next (signed) byte in memory into HL
uint32_t signed_data = (pcRead() ^ 0x80) - 0x80; uint32_t signed_data = (pcRead() ^ 0x80) - 0x80;
uint32_t sum = m_sp + signed_data; uint32_t sum = m_sp + signed_data;
m_h = sum >> 8;
m_l = sum & 0xff; m_l = sum & 0xff;
m_h = sum >> 8;
// Set flags // Set flags
m_zf = 0; m_zf = 0;
m_nf = 0; m_nf = 0;
m_hf = isCarry(m_sp, signed_data, 0x10); m_hf = isCarry(0xf, m_sp, signed_data);
m_cf = isCarry(m_sp, signed_data, 0x100); m_cf = isCarry(0xff, m_sp, signed_data);
break; break;
} }
case 0xf9: { // LD SP,HL case 0xf9: { // LD SP,HL
m_wait_cycles += 8; m_wait_cycles += 8;
// Load register HL into register SP
m_sp = hl(); m_sp = hl();
break; break;
} }
@@ -1347,9 +1363,9 @@ void CPU::pop()
m_wait_cycles += 12; m_wait_cycles += 12;
// Pop register r16 from stack // Pop register r16 from stack
register_low = read(m_sp); register_low = read(m_sp); // lsb(r16)
m_sp = (m_sp + 1) & 0xffff; m_sp = (m_sp + 1) & 0xffff;
register_high = read(m_sp); register_high = read(m_sp); // msb(r16)
m_sp = (m_sp + 1) & 0xffff; m_sp = (m_sp + 1) & 0xffff;
}; };
@@ -1382,9 +1398,9 @@ void CPU::push()
// Push register r16 into stack // Push register r16 into stack
m_sp = (m_sp - 1) & 0xffff; m_sp = (m_sp - 1) & 0xffff;
write(m_sp, register_ >> 8); write(m_sp, register_ >> 8); // msb(r16)
m_sp = (m_sp - 1) & 0xffff; m_sp = (m_sp - 1) & 0xffff;
write(m_sp, register_ & 0xff); write(m_sp, register_ & 0xff); // lsb(r16)
}; };
uint8_t opcode = pcRead(); uint8_t opcode = pcRead();
@@ -1411,13 +1427,13 @@ void CPU::call()
} }
m_wait_cycles += 24; m_wait_cycles += 24;
// Push address of next 2 bytes in memory onto stack // Push address of the instruction after the CALL on the stack, such that RET can pop it later
m_sp = (m_sp - 1) & 0xffff; m_sp = (m_sp - 1) & 0xffff;
write(m_sp, data >> 8); write(m_sp, m_pc >> 8);
m_sp = (m_sp - 1) & 0xffff; m_sp = (m_sp - 1) & 0xffff;
write(m_sp, data & 0xff); write(m_sp, m_pc & 0xff);
// Jump to this address // Jump to operand address
m_pc = data; m_pc = data;
}; };
@@ -1525,7 +1541,12 @@ void CPU::ret()
case 0xc9: /* RET i16 */ function_return(true); break; case 0xc9: /* RET i16 */ function_return(true); break;
case 0xd0: /* RET NC,i16 */ function_return(!m_cf); break; case 0xd0: /* RET NC,i16 */ function_return(!m_cf); break;
case 0xd8: /* RET C,i16 */ function_return(m_cf); break; case 0xd8: /* RET C,i16 */ function_return(m_cf); break;
// case 0xd9: /* RETI */ FIXME break; case 0xd9: /* RETI */ {
// Return from subroutine
// TODO: and enable interrupts.
function_return(true);
break;
}
default: default:
VERIFY_NOT_REACHED(); VERIFY_NOT_REACHED();
} }
@@ -1641,7 +1662,8 @@ void CPU::write(uint32_t address, uint32_t value)
uint32_t CPU::read(uint32_t address) uint32_t CPU::read(uint32_t address)
{ {
return Emu::the().readMemory(address) & 0xff; // FIXME: Figure out where HL gets set to above 0xffff
return Emu::the().readMemory(address & 0xffff) & 0xff;
} }
void CPU::ffWrite(uint32_t address, uint32_t value) void CPU::ffWrite(uint32_t address, uint32_t value)
@@ -1654,32 +1676,14 @@ uint32_t CPU::ffRead(uint32_t address)
return Emu::the().readMemory(address | (0xff << 8)) & 0xff; return Emu::the().readMemory(address | (0xff << 8)) & 0xff;
} }
bool CPU::isCarry(uint32_t lhs, uint32_t rhs, uint32_t limit_bit) bool CPU::isCarry(uint32_t limit_bit, uint32_t first, uint32_t second, uint32_t third)
{ {
// limit_bit values: return (first & limit_bit) + (second & limit_bit) + (third & limit_bit) > limit_bit;
// 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: bool CPU::isCarrySubtraction(uint32_t limit_bit, uint32_t lhs, uint32_t rhs, uint32_t rhs_extra)
// 0b00111110 62 | 0b00111000 56 {
// 0b00100010 34 + | 0b00010001 17 + return (lhs & limit_bit) < (rhs & limit_bit) + (rhs_extra & limit_bit);
// --------------- | ---------------
// 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;
} }
// ----------------------------------------- // -----------------------------------------
+3 -2
View File
@@ -71,7 +71,7 @@ public:
// 8-bit // 8-bit
void lda8(); void lda8();
void ldffi8(); void ldff8();
void ldi8(); void ldi8();
void ldr8(); void ldr8();
@@ -123,7 +123,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); bool isCarry(uint32_t limit_bit, uint32_t first, uint32_t second, uint32_t third = 0x0);
bool isCarrySubtraction(uint32_t limit_bit, uint32_t lhs, uint32_t rhs, uint32_t rhs_extra = 0x0);
// Registers // Registers
uint32_t m_a { 0 }; // Accumulator uint32_t m_a { 0 }; // Accumulator
+13 -4
View File
@@ -66,16 +66,25 @@ void Emu::writeMemory(uint32_t address, uint32_t value)
{ {
for (auto& memory_space : m_memory_spaces) { for (auto& memory_space : m_memory_spaces) {
auto& memory = memory_space.second; auto& memory = memory_space.second;
if (address == 0xff50) { if (address >= memory.start_address && address <= memory.end_address) {
Loader::the().disableBootrom();
}
else if (address >= memory.start_address && address <= memory.end_address) {
// Note: ECHO RAM hack // Note: ECHO RAM hack
if (address >= 0xc000 && address <= 0xddff) { if (address >= 0xc000 && address <= 0xddff) {
writeMemory(address + (0xe000 - 0xc000), value); writeMemory(address + (0xe000 - 0xc000), value);
} }
memory.memory[memory.active_bank][address - memory.start_address] = value; memory.memory[memory.active_bank][address - memory.start_address] = value;
if (address == 0xff50) {
print("DISABLING BOOTROM\n");
Loader::the().disableBootrom();
}
// Write serial data from linkport I/O, used for blargg's test ROMs
if (address == 0xff02 && value == 0x81) {
uint32_t data = readMemory(0xff01);
print("{:c}", (data >= 58 && data <= 64) ? data + 7 : data);
}
return; return;
} }
} }
+17 -3
View File
@@ -5,11 +5,13 @@
* SPDX-License-Identifier: MIT * SPDX-License-Identifier: MIT
*/ */
#include <cstdint> // uint32_t
#include "emu.h"
#include "ppu.h" #include "ppu.h"
#include "ruc/format/print.h" #include "ruc/format/print.h"
#include <iostream>
PPU ::PPU(unsigned int frequency) PPU ::PPU(uint32_t frequency)
: ProcessingUnit(frequency) : ProcessingUnit(frequency)
{ {
} }
@@ -20,5 +22,17 @@ PPU ::~PPU()
void PPU::update() void PPU::update()
{ {
// print("ppu update\n"); LCDC lcd_control = static_cast<LCDC>(Emu::the().readMemory(0xff40));
if (!(lcd_control & LCDC::LCDandPPUEnable)) {
return;
}
// print("PPU update\n");
// Increment LY (LCD Y Coordinate)
uint32_t ly_register = Emu::the().readMemory(0xff44) + 1;
if (ly_register >= 154) {
ly_register = 0;
}
Emu::the().writeMemory(0xff44, ly_register);
} }
+16 -1
View File
@@ -7,11 +7,26 @@
#pragma once #pragma once
#include <cstdint> // uint8_t, uint32_t
#include "processing-unit.h" #include "processing-unit.h"
#include "ruc/meta/core.h"
enum LCDC : uint8_t {
None = 0,
BGandWindowEnable = BIT(0),
OBJEnable = BIT(1),
OBJSize = BIT(2), // 0 = 8x8, 1 = 8x16
BGTileMapArea = BIT(3), // 0 = 9800-9bff, 1 = 9c00-9fff
BGandWindowTileDataArea = BIT(4), // 0 = 8800-97ff, 1 = 8000-8fff
WindowEnable = BIT(5), //
WindowTileMapArea = BIT(6), // 0 = 9800-9bff, 1 = 9c00-9fff
LCDandPPUEnable = BIT(7),
};
class PPU final : public ProcessingUnit { class PPU final : public ProcessingUnit {
public: public:
PPU(unsigned int frequency); PPU(uint32_t frequency);
virtual ~PPU(); virtual ~PPU();
virtual void update() override; virtual void update() override;
+145 -13
View File
@@ -4,7 +4,10 @@
* SPDX-License-Identifier: MIT * SPDX-License-Identifier: MIT
*/ */
#include <vector>
#include "cpu.h" #include "cpu.h"
#include "emu.h"
#include "macro.h" #include "macro.h"
#include "testcase.h" #include "testcase.h"
#include "testsuite.h" #include "testsuite.h"
@@ -12,9 +15,39 @@
struct CPUTest { struct CPUTest {
CPU cpu { 0 }; CPU cpu { 0 };
bool isCarry(uint32_t lhs, uint32_t rhs, uint32_t limit_bit) { return cpu.isCarry(lhs, rhs, limit_bit); } bool isCarry(uint32_t limit_bit, uint32_t first, uint32_t second, uint32_t third = 0x0)
{
return cpu.isCarry(limit_bit, first, second, third);
}
bool isCarrySubtraction(uint32_t limit_bit, uint32_t lhs, uint32_t rhs, uint32_t rhs_extra = 0x0)
{
return cpu.isCarrySubtraction(limit_bit, lhs, rhs, rhs_extra);
}
}; };
CPU runCPUTest(std::vector<uint8_t> test)
{
CPU cpu(4000000);
Emu::the().destroy();
Emu::the().init(8000000);
Emu::the().addProcessingUnit("cpu", &cpu);
Emu::the().addMemorySpace("FULL", 0x0000, 0xffff);
// Load the test
for (size_t i = 0; i < test.size(); ++i) {
Emu::the().writeMemory(i, test[i]);
}
// Run the test
while (cpu.pc() < test.size()) {
Emu::the().update();
}
return cpu;
}
// ----------------------------------------- // -----------------------------------------
TEST_CASE(CPUIsCarry) TEST_CASE(CPUIsCarry)
@@ -23,27 +56,126 @@ TEST_CASE(CPUIsCarry)
// 8-bit Half-carry false // 8-bit Half-carry false
EXPECT_EQ(test.isCarry(56, 17, 0x10), false); EXPECT(test.isCarry(0xf, 56, 17) == false);
EXPECT_EQ(test.isCarry(16, -1, 0x10), false); EXPECT(test.isCarrySubtraction(0xf, 136, 1) == false);
EXPECT_EQ(test.isCarry(32, -1, 0x10), false); EXPECT(test.isCarrySubtraction(0xf, 219, 20) == false);
EXPECT_EQ(test.isCarry(144, -1, 0x10), false); EXPECT(test.isCarrySubtraction(0xf, 250, 20) == false);
EXPECT_EQ(test.isCarry(211, -20, 0x10), false);
// 8-bit Half-carry true // 8-bit Half-carry true
EXPECT_EQ(test.isCarry(46, 34, 0x10), true); EXPECT(test.isCarry(0xf, 46, 34) == true);
EXPECT_EQ(test.isCarry(62, 34, 0x10), true); EXPECT(test.isCarry(0xf, 62, 34) == true);
EXPECT_EQ(test.isCarry(136, -1, 0x10), true); EXPECT(test.isCarrySubtraction(0xf, 16, 1) == true);
EXPECT_EQ(test.isCarry(219, -20, 0x10), true); EXPECT(test.isCarrySubtraction(0xf, 32, 1) == true);
EXPECT_EQ(test.isCarry(250, -20, 0x10), true); EXPECT(test.isCarrySubtraction(0xf, 144, 1) == true);
EXPECT(test.isCarrySubtraction(0xf, 211, 20) == true);
// 8-bit Full carry false // 8-bit Full carry false
EXPECT_EQ(test.isCarry(254, 1, 0x100), false); EXPECT(test.isCarry(0xff, 254, 1) == false);
// 8-bit Full carry true // 8-bit Full carry true
EXPECT_EQ(test.isCarry(254, 2, 0x100), true); EXPECT(test.isCarry(0xff, 254, 2) == true);
}
TEST_CASE(CPUAddPlusCarry)
{
CPU cpu(0);
// ADC A,E
std::vector<uint8_t> adc_r8 = {
// clang-format off
0x3e, 0xe1, // LD A,i8
0x1e, 0x0f, // LD E,i8
0x37, // SCF
0x8b, // ADC A,E
// clang-format on
};
cpu = runCPUTest(adc_r8);
EXPECT_EQ(cpu.a(), 0xf1);
EXPECT_EQ(cpu.zf(), 0x0);
EXPECT_EQ(cpu.nf(), 0x0);
EXPECT_EQ(cpu.hf(), 0x1);
EXPECT_EQ(cpu.cf(), 0x0);
// ADC A,i8
std::vector<uint8_t> adc_i8 = {
// clang-format off
0x3e, 0xe1, // LD A,i8
0x37, // SCF
0xce, 0x3b, // ADC A,i8
// clang-format on
};
cpu = runCPUTest(adc_i8);
EXPECT_EQ(cpu.a(), 0x1d);
EXPECT_EQ(cpu.zf(), 0x0);
EXPECT_EQ(cpu.nf(), 0x0);
EXPECT_EQ(cpu.hf(), 0x0);
EXPECT_EQ(cpu.cf(), 0x1);
// ADC A,(HL)
std::vector<uint8_t> adc_hl = {
// clang-format off
0x3e, 0xe1, // LD A,i8
0x36, 0x1e, // LD (HL),i8
0x37, // SCF
0x8e, // ADC A,(HL)
// clang-format on
};
cpu = runCPUTest(adc_hl);
EXPECT_EQ(cpu.a(), 0x0);
EXPECT_EQ(cpu.zf(), 0x1);
EXPECT_EQ(cpu.nf(), 0x0);
EXPECT_EQ(cpu.hf(), 0x1);
EXPECT_EQ(cpu.cf(), 0x1);
}
TEST_CASE(CPUSetStackPointer)
{
std::vector<uint8_t> test = { 0x31, 0xfe, 0xff }; // LD SP,i16
CPU cpu = runCPUTest(test);
EXPECT_EQ(cpu.sp(), 0xfffe);
}
TEST_CASE(CPUPushToStack)
{
// PUSH BC
std::vector<uint8_t> push_bc = {
// clang-format off
0x31, 0xfe, 0xff, // LD SP,i16
0x01, 0xfc, 0xff, // LD BC,i16
0xc5, // PUSH BC
// clang-format on
};
CPU cpu = runCPUTest(push_bc);
EXPECT_EQ(cpu.bc(), 0xfffc);
EXPECT_EQ(cpu.sp(), 0xfffc);
EXPECT_EQ(Emu::the().readMemory(0xfffd), 0xff);
EXPECT_EQ(Emu::the().readMemory(0xfffc), 0xfc);
}
TEST_CASE(CPUPopFromStack)
{
// POP BC
std::vector<uint8_t> pop_bc = {
// clang-format off
0x31, 0xfe, 0xff, // LD SP,i16
0x11, 0x5f, 0x3c, // LD DE,i16
0xd5, // PUSH DE
0xc1, // POP BC
// clang-format on
};
CPU cpu = runCPUTest(pop_bc);
EXPECT_EQ(cpu.bc(), 0x3c5f);
EXPECT_EQ(cpu.sp(), 0xfffe);
EXPECT_EQ(Emu::the().readMemory(0xfffd), 0x3c);
EXPECT_EQ(Emu::the().readMemory(0xfffc), 0x5f);
} }
Vendored
+1 -1
Submodule vendor/ruc updated: f31f7feb5e...d3de1fb635