From ae8255d8246cd898a9618f6b88bc344e70c7dd77 Mon Sep 17 00:00:00 2001 From: Kelvin Wu Date: Sun, 9 Jun 2024 12:19:20 +0800 Subject: [PATCH] feat: implement SUB, SHR, SUBN, SHL * feat: implement SUB, SHR, SUBN, SHL * fix: adding remark to SHR and SHL --- arabica/cpu/cpu.cpp | 43 ++++++++ test/cpu/cpu_test_suite.hpp | 191 ++++++++++++++++++++++++++++++++++++ 2 files changed, 234 insertions(+) diff --git a/arabica/cpu/cpu.cpp b/arabica/cpu/cpu.cpp index 2292d9a..1fdbb03 100644 --- a/arabica/cpu/cpu.cpp +++ b/arabica/cpu/cpu.cpp @@ -26,6 +26,10 @@ void CPU::run(const Memory& memory) { case 0x2: opcode = OP_CODE::AND_Vx_Vy; break; case 0x3: opcode = OP_CODE::XOR_Vx_Vy; break; case 0x4: opcode = OP_CODE::ADD_Vx_Vy; break; + case 0x5: opcode = OP_CODE::SUB_Vx_Vy; break; + case 0x6: opcode = OP_CODE::SHR_Vx; break; + case 0x7: opcode = OP_CODE::SUBN_Vx_Vy; break; + case 0xE: opcode = OP_CODE::SHL_Vx; break; default: break; } } break; @@ -99,6 +103,45 @@ void CPU::run(const Memory& memory) { advance_pc(pc); } break; + case OP_CODE::SUB_Vx_Vy: { + uint8_t x = (instruction & 0x0F00) >> 8; + uint8_t y = (instruction & 0x00F0) >> 4; + + registers[0xF] = registers[x] > registers[y]; + registers[x] = registers[x] - registers[y]; + + advance_pc(pc); + } break; + case OP_CODE::SHR_Vx: { + // Remark: historically, the semantics is "right shift V[x] by V[y] amount + // and store the result to V[x]" in the original chip8 implementation, however, most of the game + // after 90s follows the buggy implementation of HP which ignoring V[y], so we just follow the same for now. + + uint8_t x = (instruction & 0x0F00) >> 8; + registers[0xF] = registers[x] & 1; + registers[x] = registers[x] >> 1; + + advance_pc(pc); + } break; + case OP_CODE::SUBN_Vx_Vy: { + uint8_t x = (instruction & 0x0F00) >> 8; + uint8_t y = (instruction & 0x00F0) >> 4; + + registers[0xF] = registers[y] > registers[x]; + registers[x] = registers[y] - registers[x]; + + advance_pc(pc); + } break; + case OP_CODE::SHL_Vx: { + // Remark: historically, the semantics is "left shift V[x] by V[y] amount + // and store the result to V[x]" in the original chip8 implementation, however, most of the game + // after 90s follows the buggy implementation of HP which ignoring V[y], so we just follow the same for now. + uint8_t x = (instruction & 0x0F00) >> 8; + registers[0xF] = (registers[x] >> 7) & 1; + registers[x] = registers[x] << 1; + + advance_pc(pc); + } break; default: { fmt::print("Unknown opcode: 0x{:X}\n", static_cast(opcode)); } break; diff --git a/test/cpu/cpu_test_suite.hpp b/test/cpu/cpu_test_suite.hpp index dc80306..b2bc595 100644 --- a/test/cpu/cpu_test_suite.hpp +++ b/test/cpu/cpu_test_suite.hpp @@ -219,3 +219,194 @@ arabica_cpu_test(test_add_vx_vy, ASSERT_EQ(cpu.registers[0], 0x1); ASSERT_EQ(cpu.registers[0xF], 1); ) + +arabica_cpu_test(test_sub_vx_vy, + // LD V[0], 0x1 + memory.write(0x200, 0x60); + memory.write(0x201, 0x01); + cpu.run(memory); + ASSERT_EQ(cpu.pc, 0x202); + ASSERT_EQ(cpu.registers[0], 0x1); + + // LD V[1], 0x2 + memory.write(0x202, 0x61); + memory.write(0x203, 0x02); + cpu.run(memory); + ASSERT_EQ(cpu.pc, 0x204); + ASSERT_EQ(cpu.registers[1], 0x2); + + // SUB V[0], V[1], expected V[0] = 0xFF + memory.write(0x204, 0x80); + memory.write(0x205, 0x15); + cpu.run(memory); + ASSERT_EQ(cpu.pc, 0x206); + ASSERT_EQ(cpu.registers[0], 0xff); + ASSERT_EQ(cpu.registers[0xF], 0); + + // LD V[0], 0x2 + memory.write(0x206, 0x60); + memory.write(0x207, 0x02); + cpu.run(memory); + ASSERT_EQ(cpu.pc, 0x208); + ASSERT_EQ(cpu.registers[0], 0x2); + + // LD V[1], 0x1 + memory.write(0x208, 0x61); + memory.write(0x209, 0x01); + cpu.run(memory); + ASSERT_EQ(cpu.pc, 0x20A); + ASSERT_EQ(cpu.registers[1], 0x1); + + // SUB V[0], V[1], expected V[0] = 0x00 + memory.write(0x20A, 0x80); + memory.write(0x20B, 0x15); + cpu.run(memory); + ASSERT_EQ(cpu.pc, 0x20C); + ASSERT_EQ(cpu.registers[0], 0x01); + ASSERT_EQ(cpu.registers[0xF], 1); +) + +arabica_cpu_test(test_shr_vx, + // LD V[0], 0xF + memory.write(0x200, 0x60); + memory.write(0x201, 0x0f); + cpu.run(memory); + ASSERT_EQ(cpu.pc, 0x202); + ASSERT_EQ(cpu.registers[0], 0xF); + + // SHR V[0], expected V[0] = 0x7 + memory.write(0x202, 0x80); + memory.write(0x203, 0x06); + cpu.run(memory); + ASSERT_EQ(cpu.pc, 0x204); + ASSERT_EQ(cpu.registers[0], 0x7); + ASSERT_EQ(cpu.registers[0xF], 1); + + // SHR V[0], expected V[0] = 0x3 + memory.write(0x204, 0x80); + memory.write(0x205, 0x06); + cpu.run(memory); + ASSERT_EQ(cpu.pc, 0x206); + ASSERT_EQ(cpu.registers[0], 0x3); + ASSERT_EQ(cpu.registers[0xF], 1); + + // SHR V[0], expected V[0] = 0x1 + memory.write(0x206, 0x80); + memory.write(0x207, 0x06); + cpu.run(memory); + ASSERT_EQ(cpu.pc, 0x208); + ASSERT_EQ(cpu.registers[0], 0x1); + ASSERT_EQ(cpu.registers[0xF], 1); + + // SHR V[0], expected V[0] = 0x0 + memory.write(0x208, 0x80); + memory.write(0x209, 0x06); + cpu.run(memory); + ASSERT_EQ(cpu.pc, 0x20A); + ASSERT_EQ(cpu.registers[0], 0x0); + ASSERT_EQ(cpu.registers[0xF], 1); + + // SHR V[0], expected V[0] = 0x0 + memory.write(0x20A, 0x80); + memory.write(0x20B, 0x06); + cpu.run(memory); + ASSERT_EQ(cpu.pc, 0x20C); + ASSERT_EQ(cpu.registers[0], 0x0); + ASSERT_EQ(cpu.registers[0xF], 0); +) + + +arabica_cpu_test(test_subn_vx_vy, + // LD V[0], 0x2 + memory.write(0x200, 0x60); + memory.write(0x201, 0x02); + cpu.run(memory); + ASSERT_EQ(cpu.pc, 0x202); + ASSERT_EQ(cpu.registers[0], 0x2); + + // LD V[1], 0x1 + memory.write(0x202, 0x61); + memory.write(0x203, 0x01); + cpu.run(memory); + ASSERT_EQ(cpu.pc, 0x204); + ASSERT_EQ(cpu.registers[1], 0x1); + + // SUBN V[0], V[1], expected V[0] = 0xFF + memory.write(0x204, 0x80); + memory.write(0x205, 0x17); + cpu.run(memory); + ASSERT_EQ(cpu.pc, 0x206); + ASSERT_EQ(cpu.registers[0], 0xff); + ASSERT_EQ(cpu.registers[0xF], 0); + + // LD V[0], 0x1 + memory.write(0x206, 0x60); + memory.write(0x207, 0x01); + cpu.run(memory); + ASSERT_EQ(cpu.pc, 0x208); + ASSERT_EQ(cpu.registers[0], 0x1); + + // LD V[1], 0x2 + memory.write(0x208, 0x61); + memory.write(0x209, 0x02); + cpu.run(memory); + ASSERT_EQ(cpu.pc, 0x20A); + ASSERT_EQ(cpu.registers[1], 0x2); + + // SUBN V[0], V[1], expected V[0] = 0x01 + memory.write(0x20A, 0x80); + memory.write(0x20B, 0x17); + cpu.run(memory); + ASSERT_EQ(cpu.pc, 0x20C); + ASSERT_EQ(cpu.registers[0], 0x1); + ASSERT_EQ(cpu.registers[0xF], 1); +) + +arabica_cpu_test(test_shl_vx, + // LD V[0], 0xF + memory.write(0x200, 0x60); + memory.write(0x201, 0x0f); + cpu.run(memory); + ASSERT_EQ(cpu.pc, 0x202); + ASSERT_EQ(cpu.registers[0], 0xF); + + // SHL V[0], expected V[0] = 0x1E + memory.write(0x202, 0x80); + memory.write(0x203, 0x0E); + cpu.run(memory); + ASSERT_EQ(cpu.pc, 0x204); + ASSERT_EQ(cpu.registers[0], 0x1E); + ASSERT_EQ(cpu.registers[0xF], 0); + + // SHL V[0], expected V[0] = 0x3C + memory.write(0x204, 0x80); + memory.write(0x205, 0x0E); + cpu.run(memory); + ASSERT_EQ(cpu.pc, 0x206); + ASSERT_EQ(cpu.registers[0], 0x3C); + ASSERT_EQ(cpu.registers[0xF], 0); + + // SHL V[0], expected V[0] = 0x78 + memory.write(0x206, 0x80); + memory.write(0x207, 0x0E); + cpu.run(memory); + ASSERT_EQ(cpu.pc, 0x208); + ASSERT_EQ(cpu.registers[0], 0x78); + ASSERT_EQ(cpu.registers[0xF], 0); + + // SHL V[0], expected V[0] = 0xF0 + memory.write(0x208, 0x80); + memory.write(0x209, 0x0E); + cpu.run(memory); + ASSERT_EQ(cpu.pc, 0x20A); + ASSERT_EQ(cpu.registers[0], 0xF0); + ASSERT_EQ(cpu.registers[0xF], 0); + + // SHL V[0], expected V[0] = 0xE0 + memory.write(0x20A, 0x80); + memory.write(0x20B, 0x0E); + cpu.run(memory); + ASSERT_EQ(cpu.pc, 0x20C); + ASSERT_EQ(cpu.registers[0], 0xE0); + ASSERT_EQ(cpu.registers[0xF], 1); +)