Compare commits

...
6 Commits
Author SHA1 Message Date
Riyyi ec0f8bf02e Emulator: Implement SLA/SRA/SRL opcodes 2022-08-27 13:43:15 +02:00
Riyyi ac03d70d83 fixup! Emulator: Implement SWAP opcodes 2022-08-27 13:38:04 +02:00
Riyyi 99c768d400 Emulator: Rename reg => register_ 2022-08-27 13:36:07 +02:00
Riyyi fb6ba43ff1 Emulator: Implement RL/RR opcodes 2022-08-27 13:36:07 +02:00
Riyyi 275cab4da4 Emulator: Implement RLC/RRC opcodes 2022-08-27 13:08:21 +02:00
Riyyi d53f4759e3 Emulator: Implement SWAP opcodes 2022-08-27 12:43:01 +02:00
3 changed files with 409 additions and 31 deletions
+376 -4
View File
@@ -14,13 +14,39 @@
void CPU::prefix() void CPU::prefix()
{ {
// Skip 0xcb opcode // Note: All these opcodes are considered 2 bytes, as the prefix is included
// Skip 0xcb prefix opcode
m_pc = (m_pc + 1) & 0xffff; m_pc = (m_pc + 1) & 0xffff;
// 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 >= 0x40 && opcode <= 0x7f) { if (opcode >= 0x00 && opcode <= 0x07) {
rlc();
}
else if (opcode >= 0x08 && opcode <= 0x0f) {
rrc();
}
else if (opcode >= 0x10 && opcode <= 0x17) {
rl();
}
else if (opcode >= 0x18 && opcode <= 0x1f) {
rr();
}
else if (opcode >= 0x20 && opcode <= 0x27) {
sla();
}
else if (opcode >= 0x28 && opcode <= 0x2f) {
sra();
}
else if (opcode >= 0x30 && opcode <= 0x37) {
swap();
}
else if (opcode >= 0x38 && opcode <= 0x3f) {
srl();
}
else if (opcode >= 0x40 && opcode <= 0x7f) {
bit(); bit();
} }
else if (opcode >= 0x80 && opcode <= 0xbf) { else if (opcode >= 0x80 && opcode <= 0xbf) {
@@ -31,7 +57,7 @@ void CPU::prefix()
} }
else { else {
print("opcode {:#04x} not implemented\n", opcode); print("opcode {:#04x} not implemented\n", opcode);
print("immediate: {:#04x}\n", m_pc, pcRead()); print("immediate: {:#04x}\n", pcRead());
VERIFY_NOT_REACHED(); VERIFY_NOT_REACHED();
} }
} }
@@ -328,7 +354,353 @@ void CPU::set()
case 0x7c: /* SET 7,H */ set_bit(0x80, m_h); break; case 0x7c: /* SET 7,H */ set_bit(0x80, m_h); break;
case 0x7d: /* SET 7,L */ set_bit(0x80, m_l); break; case 0x7d: /* SET 7,L */ set_bit(0x80, m_l); break;
case 0x7e: /* SET 7,(HL) */ set_bit_hl(0x80); break; case 0x7e: /* SET 7,(HL) */ set_bit_hl(0x80); break;
case 0x7f: /* RES 7,A */ set_bit(0x80, m_a); break; case 0x7f: /* SET 7,A */ set_bit(0x80, m_a); break;
default:
VERIFY_NOT_REACHED();
}
}
void CPU::swap()
{
auto swap_bits = [this](uint32_t& register_) -> void {
// SWAP r8, flags: Z 0 0 0
m_wait_cycles += 8;
// Swap upper 4 bits in register r8 with lower 4 bits
register_ = register_ & 0xff;
register_ = (register_ >> 4) | (register_ << 4);
// Set flags
m_zf = register_ == 0;
m_nf = 0;
m_hf = 0;
m_cf = 0;
};
uint8_t opcode = pcRead();
switch (opcode) {
case 0x78: /* SWAP B */ swap_bits(m_b); break;
case 0x79: /* SWAP C */ swap_bits(m_c); break;
case 0x7a: /* SWAP D */ swap_bits(m_d); break;
case 0x7b: /* SWAP E */ swap_bits(m_e); break;
case 0x7c: /* SWAP H */ swap_bits(m_h); break;
case 0x7d: /* SWAP L */ swap_bits(m_l); break;
case 0x7e: /* SWAP (HL) */ {
m_wait_cycles += 8; // + 8 = 16 total
// Swap upper 4 bits in the byte pointed by HL with lower 4 bits
uint32_t data = read(hl());
swap_bits(data);
write(hl(), data);
break;
}
case 0x7f: /* SWAP A */ swap_bits(m_a); break;
default:
VERIFY_NOT_REACHED();
}
}
void CPU::rl()
{
auto rotate_left_carry = [this](uint32_t& register_) -> void {
// RL r8, flags: Z 0 0 C
m_wait_cycles += 8;
// Copy bit 7 into carry flag
uint32_t old_carry = m_cf != 0;
m_cf = (register_ & 0x80) == 0x80;
// Rotate register r8 left through carry
register_ = (old_carry | (register_ << 1)) & 0xff;
// Set other flags
m_zf = register_ == 0;
m_nf = 0;
m_hf = 0;
};
uint8_t opcode = pcRead();
switch (opcode) {
case 0x10: /* RL B */ rotate_left_carry(m_b); break;
case 0x11: /* RL C */ rotate_left_carry(m_c); break;
case 0x12: /* RL D */ rotate_left_carry(m_d); break;
case 0x13: /* RL E */ rotate_left_carry(m_e); break;
case 0x14: /* RL H */ rotate_left_carry(m_h); break;
case 0x15: /* RL L */ rotate_left_carry(m_l); break;
case 0x16: /* RL (HL) */ {
m_wait_cycles += 8; // + 8 = 16 total
uint32_t data = read(hl());
rotate_left_carry(data);
write(hl(), data);
break;
}
case 0x17: /* RL A */ rotate_left_carry(m_a); break;
default:
VERIFY_NOT_REACHED();
}
}
void CPU::rlc()
{
auto rotate_left = [this](uint32_t& register_) -> void {
// RLC r8, flags: Z 0 0 C
m_wait_cycles += 8;
// Copy bit 7 into carry flag
m_cf = (register_ & 0x80) == 0x80;
// Rotate register r8 left
register_ = ((register_ >> 7) | (register_ << 1)) & 0xff;
// Set other flags
m_zf = register_ == 0;
m_nf = 0;
m_hf = 0;
};
uint8_t opcode = pcRead();
switch (opcode) {
case 0x00: /* RLC B */ rotate_left(m_b); break;
case 0x01: /* RLC C */ rotate_left(m_c); break;
case 0x02: /* RLC D */ rotate_left(m_d); break;
case 0x03: /* RLC E */ rotate_left(m_e); break;
case 0x04: /* RLC H */ rotate_left(m_h); break;
case 0x05: /* RLC L */ rotate_left(m_l); break;
case 0x06: /* RLC (HL) */ {
m_wait_cycles += 8; // + 8 = 16 total
// Rotate the byte pointed to by HL left
uint32_t data = read(hl());
rotate_left(data);
write(hl(), data);
break;
}
case 0x07: /* RLC A */ rotate_left(m_a); break;
default:
VERIFY_NOT_REACHED();
}
}
void CPU::rr()
{
auto rotate_right_carry = [this](uint32_t& register_) -> void {
// RR r8, flags: Z 0 0 C
m_wait_cycles += 8;
// Copy bit 0 into carry flag
uint32_t old_carry = m_cf != 0;
m_cf = (register_ & 0x01) == 0x01;
// Rotate register r8 right through carry
register_ = ((register_ >> 1) | (old_carry << 7)) & 0xff;
// Set other flags
m_zf = register_ == 0;
m_nf = 0;
m_hf = 0;
};
uint8_t opcode = pcRead();
switch (opcode) {
case 0x18: /* RR B */ rotate_right_carry(m_b); break;
case 0x19: /* RR C */ rotate_right_carry(m_c); break;
case 0x1a: /* RR D */ rotate_right_carry(m_d); break;
case 0x1b: /* RR E */ rotate_right_carry(m_e); break;
case 0x1c: /* RR H */ rotate_right_carry(m_h); break;
case 0x1d: /* RR L */ rotate_right_carry(m_l); break;
case 0x1e: /* RR (HL) */ {
m_wait_cycles += 8; // + 8 = 16 total
uint32_t data = read(hl());
rotate_right_carry(data);
write(hl(), data);
break;
}
case 0x1f: /* RR A */ rotate_right_carry(m_a); break;
default:
VERIFY_NOT_REACHED();
}
}
void CPU::rrc()
{
auto rotate_right = [this](uint32_t& register_) -> void {
// RRC r8, flags: Z 0 0 C
m_wait_cycles += 8;
// Copy bit 0 into carry flag
m_cf = (register_ & 0x1) == 0x1;
// Rotate register r8 right
register_ = ((register_ >> 1) | (register_ << 7)) & 0xff;
// Set other flags
m_zf = register_ == 0;
m_nf = 0;
m_hf = 0;
};
uint8_t opcode = pcRead();
switch (opcode) {
case 0x08: /* RRC B */ rotate_right(m_b); break;
case 0x09: /* RRC C */ rotate_right(m_c); break;
case 0x0a: /* RRC D */ rotate_right(m_d); break;
case 0x0b: /* RRC E */ rotate_right(m_e); break;
case 0x0c: /* RRC H */ rotate_right(m_h); break;
case 0x0d: /* RRC L */ rotate_right(m_l); break;
case 0x0e: /* RRC (HL) */ {
m_wait_cycles += 8; // + 8 = 16 total
// Rotate the byte pointed to by HL right
uint32_t data = read(hl());
rotate_right(data);
write(hl(), data);
break;
}
case 0x0f: /* RRC A */ rotate_right(m_a); break;
default:
VERIFY_NOT_REACHED();
}
}
void CPU::sla()
{
auto shift_left_arithmatically = [this](uint32_t& register_) -> void {
// SLA r8, flags: Z 0 0 C
m_wait_cycles += 8;
// Shift Left Arithmetically register r8
// ┌─────────┐
// C <─│7 <── 0│<─ 0
// └─────────┘
// r8
// Copy bit 7 into carry flag
m_cf = (m_a & 0x80) == 0x80;
// Shift Left Arithmetically register r8
register_ = (register_ << 1) & 0xff;
// Set other flags
m_zf = register_ == 0;
m_nf = 0;
m_hf = 0;
};
uint8_t opcode = pcRead();
switch (opcode) {
case 0x20: /* SLA B */ shift_left_arithmatically(m_b); break;
case 0x21: /* SLA C */ shift_left_arithmatically(m_c); break;
case 0x22: /* SLA D */ shift_left_arithmatically(m_d); break;
case 0x23: /* SLA E */ shift_left_arithmatically(m_e); break;
case 0x24: /* SLA H */ shift_left_arithmatically(m_h); break;
case 0x25: /* SLA L */ shift_left_arithmatically(m_l); break;
case 0x26: /* SLA (HL) */ {
m_wait_cycles += 8; // + 8 = 16 total
// Rotate the byte pointed to by HL right
uint32_t data = read(hl());
shift_left_arithmatically(data);
write(hl(), data);
break;
}
case 0x27: /* SLA A */ shift_left_arithmatically(m_a); break;
default:
VERIFY_NOT_REACHED();
}
}
void CPU::sra()
{
auto shift_right_arithmatically = [this](uint32_t& register_) -> void {
// SRL r8, flags: Z 0 0 C
m_wait_cycles += 8;
// Shift Right Arithmatically register r8
// ┌─────────┐
// ┌─│7 ──> 0│─> C
// │ └─────────┘
// │ ^ r8
// └──┘
// Copy bit 0 into carry flag
m_cf = (m_a & 0x01) == 0x01;
// Shift Right Arithmatically register r8
register_ = (register_ >> 1) | (register_ & 0x80); // Note: bit 7 remains
// Set other flags
m_zf = register_ == 0;
m_nf = 0;
m_hf = 0;
};
uint8_t opcode = pcRead();
switch (opcode) {
case 0x28: /* SRA B */ shift_right_arithmatically(m_b); break;
case 0x29: /* SRA C */ shift_right_arithmatically(m_c); break;
case 0x2a: /* SRA D */ shift_right_arithmatically(m_d); break;
case 0x2b: /* SRA E */ shift_right_arithmatically(m_e); break;
case 0x2c: /* SRA H */ shift_right_arithmatically(m_h); break;
case 0x2d: /* SRA L */ shift_right_arithmatically(m_l); break;
case 0x2e: /* SRA (HL) */ {
m_wait_cycles += 8; // + 8 = 16 total
// Rotate the byte pointed to by HL right
uint32_t data = read(hl());
shift_right_arithmatically(data);
write(hl(), data);
break;
}
case 0x2f: /* SRA A */ shift_right_arithmatically(m_a); break;
default:
VERIFY_NOT_REACHED();
}
}
void CPU::srl()
{
auto shift_right_logically = [this](uint32_t& register_) -> void {
// SRL r8, flags: Z 0 0 C
m_wait_cycles += 8;
// Shift Right Locically register r8
// ┌─────────┐
// 0 ─>│7 ──> 0│─> C
// └─────────┘
// r8
// Copy bit 0 into carry flag
m_cf = (m_a & 0x01) == 0x01;
// Shift Right Locically register r8
register_ = (register_ >> 1) & 0x7f; // Note: bit 7 is set to 0
// Set other flags
m_zf = register_ == 0;
m_nf = 0;
m_hf = 0;
};
uint8_t opcode = pcRead();
switch (opcode) {
case 0x38: /* SRL B */ shift_right_logically(m_b); break;
case 0x39: /* SRL C */ shift_right_logically(m_c); break;
case 0x3a: /* SRL D */ shift_right_logically(m_d); break;
case 0x3b: /* SRL E */ shift_right_logically(m_e); break;
case 0x3c: /* SRL H */ shift_right_logically(m_h); break;
case 0x3d: /* SRL L */ shift_right_logically(m_l); break;
case 0x3e: /* SRL (HL) */ {
m_wait_cycles += 8; // + 8 = 16 total
// Rotate the byte pointed to by HL right
uint32_t data = read(hl());
shift_right_logically(data);
write(hl(), data);
break;
}
case 0x3f: /* SRL A */ shift_right_logically(m_a); break;
default: default:
VERIFY_NOT_REACHED(); VERIFY_NOT_REACHED();
} }
+25 -27
View File
@@ -228,7 +228,7 @@ void CPU::update()
default: default:
print("opcode {:#x} not implemented\n", opcode); print("opcode {:#x} not implemented\n", opcode);
print("immediate: {:#04x}\n", m_pc, pcRead()); print("immediate: {:#04x}\n", pcRead());
VERIFY_NOT_REACHED(); VERIFY_NOT_REACHED();
} }
} }
@@ -308,19 +308,19 @@ void CPU::and8()
void CPU::dec8() void CPU::dec8()
{ {
auto decrement = [this](uint32_t& reg) -> void { auto decrement = [this](uint32_t& register_) -> void {
// DEC r8, flags: Z 1 H - // DEC r8, flags: Z 1 H -
m_wait_cycles += 4; m_wait_cycles += 4;
// Set flags // Set flags
m_nf = 1; m_nf = 1;
m_hf = isCarry(reg, -1, 0x10); m_hf = isCarry(register_, -1, 0x10);
// Decrement value in register r8 by 1 // Decrement value in register r8 by 1
reg = (reg - 1) & 0xff; register_ = (register_ - 1) & 0xff;
// Zero flag // Zero flag
m_zf = reg == 0; m_zf = register_ == 0;
}; };
uint8_t opcode = pcRead(); uint8_t opcode = pcRead();
@@ -410,17 +410,17 @@ void CPU::lda8()
void CPU::addr16() void CPU::addr16()
{ {
auto add = [this](uint32_t reg) -> void { auto add = [this](uint32_t register_) -> void {
// ADD HL,r16, flags: - 0 H C // ADD HL,r16, flags: - 0 H C
m_wait_cycles += 8; m_wait_cycles += 8;
// Set flags // Set flags
m_nf = 0; m_nf = 0;
m_hf = isCarry(hl(), reg, 0x1000); m_hf = isCarry(hl(), register_, 0x1000);
m_cf = isCarry(hl(), reg, 0x10000); m_cf = isCarry(hl(), register_, 0x10000);
// Add the value in r16 to HL // Add the value in r16 to HL
uint32_t data = (hl() + reg) & 0xffff; uint32_t data = (hl() + register_) & 0xffff;
m_l = data & 0xff; m_l = data & 0xff;
m_h = data >> 8; m_h = data >> 8;
}; };
@@ -666,8 +666,7 @@ void CPU::ra()
switch (opcode) { switch (opcode) {
case 0x07: // RLCA case 0x07: // RLCA
// Rotates A to the left with bit 7 being moved to bit 0 and also stored // Rotates A to the left
// into the carry
// ┌──────────────┐ // ┌──────────────┐
// │ ┌─────────┐ │ // │ ┌─────────┐ │
// C <─┴─│7 <── 0│<─┘ // C <─┴─│7 <── 0│<─┘
@@ -678,12 +677,11 @@ void CPU::ra()
m_cf = (m_a & 0x80) == 0x80; m_cf = (m_a & 0x80) == 0x80;
// Rotate register A left // Rotate register A left
m_a = (m_a << 1) | (m_a >> 7); m_a = (m_a >> 7) | (m_a << 1);
break; break;
case 0x0f: // RRCA case 0x0f: // RRCA
// Rotates A to the right with bit 0 being moved to bit 7 and also // Rotates A to the right
// stored into the carry
// ┌──────────────┐ // ┌──────────────┐
// │ ┌─────────┐ │ // │ ┌─────────┐ │
// └─>│7 ──> 0│─┴─> C // └─>│7 ──> 0│─┴─> C
@@ -694,40 +692,40 @@ void CPU::ra()
m_cf = (m_a & 0x1) == 0x1; m_cf = (m_a & 0x1) == 0x1;
// Rotate register A right // Rotate register A right
m_a = (m_a << 7) | (m_a >> 1); m_a = (m_a >> 1) | (m_a << 7);
break; break;
case 0x17: { // RLA case 0x17: { // RLA
// Rotates A to the left with the carry's value put into bit 0 and bit 7 // Rotate register A left through carry
// is put into the carry
// ┌────────────────────┐ // ┌────────────────────┐
// │ ┌─────────┐ │ // │ ┌─────────┐ │
// └─ C <──│7 <── 0│<─┘ // └─ C <──│7 <── 0│<─┘
// └─────────┘ // └─────────┘
// A // A
// Copy bit 7 into carry flag
uint32_t old_carry = m_cf != 0; uint32_t old_carry = m_cf != 0;
m_cf = (m_a & 0x80) == 0x80; // Copy bit 7 into carry flag m_cf = (m_a & 0x80) == 0x80;
// Rotate register A left through carry // Rotate register A left through carry
m_a = (m_a << 1) | old_carry; m_a = old_carry | (m_a << 1);
break; break;
} }
case 0x1f: { // RRA case 0x1f: { // RRA
// Rotates A to the right with the carry's value put into bit 7 and bit 0 // Rotate register A right through carry
// is put into the carry
// ┌────────────────────┐ // ┌────────────────────┐
// │ ┌─────────┐ │ // │ ┌─────────┐ │
// └─>│7 ──> 0│──> C ─┘ // └─>│7 ──> 0│──> C ─┘
// └─────────┘ // └─────────┘
// A // A
// Copy bit 0 into carry flag
uint32_t old_carry = m_cf != 0; uint32_t old_carry = m_cf != 0;
m_cf = (m_a & 0x1) == 0x1; // Copy bit 0 into carry flag m_cf = (m_a & 0x1) == 0x1;
// Rotate register A right through carry // Rotate register A right through carry
m_a = (old_carry << 7) | (m_a >> 1); m_a = (m_a >> 1) | (old_carry << 7);
break; break;
} }
default: default:
@@ -784,19 +782,19 @@ void CPU::cp()
void CPU::inc() void CPU::inc()
{ {
auto increment = [this](uint32_t& reg) -> void { auto increment = [this](uint32_t& register_) -> void {
// INC r8, flags: Z 0 H - // INC r8, flags: Z 0 H -
m_wait_cycles += 4; m_wait_cycles += 4;
// Set flags // Set flags
m_nf = 0; m_nf = 0;
m_hf = isCarry(reg, 1, 0x10); m_hf = isCarry(register_, 1, 0x10);
// Increment value in register r8 by 1 // Increment value in register r8 by 1
reg = (reg + 1) & 0xff; register_ = (register_ + 1) & 0xff;
// Zero flag // Zero flag
m_zf = reg == 0; m_zf = register_ == 0;
}; };
uint8_t opcode = pcRead(); uint8_t opcode = pcRead();
+8
View File
@@ -44,11 +44,19 @@ public:
void bit(); void bit();
void res(); void res();
void set(); void set();
void swap();
// ------------------------------------- // -------------------------------------
// Bit Shift Instructions // Bit Shift Instructions
void ra(); void ra();
void rl();
void rlc();
void rr();
void rrc();
void sla();
void sra();
void srl();
// ------------------------------------- // -------------------------------------
// Load Instructions // Load Instructions