Skip to content

Commit

Permalink
feat: implement SUB, SHR, SUBN, SHL
Browse files Browse the repository at this point in the history
* feat: implement SUB, SHR, SUBN, SHL

* fix: adding remark to SHR and SHL
  • Loading branch information
Kelvinyu1117 authored Jun 9, 2024
1 parent 6963c7e commit ae8255d
Show file tree
Hide file tree
Showing 2 changed files with 234 additions and 0 deletions.
43 changes: 43 additions & 0 deletions arabica/cpu/cpu.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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<uint16_t>(opcode));
} break;
Expand Down
191 changes: 191 additions & 0 deletions test/cpu/cpu_test_suite.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -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);
)

0 comments on commit ae8255d

Please sign in to comment.