From 36d615ba64d7c5d132976c2a60f25c455e8e2001 Mon Sep 17 00:00:00 2001 From: AnonymusRaccoon Date: Fri, 7 Feb 2020 17:24:15 +0100 Subject: [PATCH] Finishing the first opcode --- CMakeLists.txt | 10 +++- sources/CPU/CPU.cpp | 29 ++++++++-- sources/CPU/CPU.hpp | 54 ++++++++++--------- .../CommonInstructions.cpp} | 2 +- .../CommonInstructions.hpp} | 8 +-- sources/Exceptions/InvalidOpcode.hpp | 28 ++++++++++ sources/SNES.cpp | 4 +- sources/SNES.hpp | 4 +- tests/testCPU.cpp | 33 ++++++++++++ 9 files changed, 133 insertions(+), 39 deletions(-) rename sources/CPU/{CommonCpu.cpp => Instructions/CommonInstructions.cpp} (69%) rename sources/CPU/{CommonCpu.hpp => Instructions/CommonInstructions.hpp} (50%) create mode 100644 sources/Exceptions/InvalidOpcode.hpp diff --git a/CMakeLists.txt b/CMakeLists.txt index 502c8e1..b2788c3 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -46,7 +46,10 @@ add_executable(unit_tests sources/Cartridge/InterruptVectors.hpp sources/Memory/RectangleShadow.cpp sources/Memory/RectangleShadow.hpp - sources/CPU/CommonCpu.cpp sources/CPU/CommonCpu.hpp) + sources/CPU/Instructions/CommonInstructions.cpp + sources/CPU/Instructions/CommonInstructions.hpp + sources/Exceptions/InvalidOpcode.hpp + ) # include criterion & coverage target_link_libraries(unit_tests criterion -lgcov) @@ -95,7 +98,10 @@ add_executable(ComSquare sources/Cartridge/InterruptVectors.hpp sources/Memory/RectangleShadow.cpp sources/Memory/RectangleShadow.hpp - sources/CPU/CommonCpu.cpp sources/CPU/CommonCpu.hpp) + sources/CPU/Instructions/CommonInstructions.cpp + sources/CPU/Instructions/CommonInstructions.hpp + sources/Exceptions/InvalidOpcode.hpp + ) target_link_libraries(ComSquare sfml-graphics diff --git a/sources/CPU/CPU.cpp b/sources/CPU/CPU.cpp index d3714f8..c47bb26 100644 --- a/sources/CPU/CPU.cpp +++ b/sources/CPU/CPU.cpp @@ -7,12 +7,15 @@ #include #include "../Exceptions/NotImplementedException.hpp" #include "../Exceptions/InvalidAddress.hpp" +#include "../Exceptions/InvalidOpcode.hpp" namespace ComSquare::CPU { - CPU::CPU(std::shared_ptr bus) - : _bus(std::move(bus)) - { } + CPU::CPU(std::shared_ptr bus, Cartridge::Header &cartridgeHeader) + : _bus(std::move(bus)), _cartridgeHeader(cartridgeHeader) + { + this->_registers.pc = this->_cartridgeHeader.emulationInterrupts.reset; + } //! @bref The CPU's internal registers starts at $4200 and finish at $421F. uint8_t CPU::read(uint24_t addr) @@ -192,6 +195,24 @@ namespace ComSquare::CPU int CPU::executeInstruction() { - throw NotImplementedException(); + uint8_t opcode = this->_bus->read(this->_registers.pc++); + + switch (opcode) { + case 0x0: return this->BRK(); + default: + throw InvalidOpcode("CPU", opcode); + } + } + + + int CPU::BRK() + { + this->_registers.p.i = true; + if (this->_isEmulationMode) + this->_registers.pc = this->_cartridgeHeader.emulationInterrupts.brk; + else + this->_registers.pc = this->_cartridgeHeader.nativeInterrupts.brk; + this->_registers.p.d = false; + return 7 + !this->_isEmulationMode; } } \ No newline at end of file diff --git a/sources/CPU/CPU.hpp b/sources/CPU/CPU.hpp index 0c8d9ee..0605852 100644 --- a/sources/CPU/CPU.hpp +++ b/sources/CPU/CPU.hpp @@ -8,7 +8,8 @@ #include "../Memory/IMemory.hpp" #include "../Memory/MemoryBus.hpp" #include "../Models/Ints.hpp" -#include "CommonCpu.hpp" +#include "Instructions/CommonInstructions.hpp" +#include "../Cartridge/Cartridge.hpp" namespace ComSquare::CPU { @@ -68,28 +69,27 @@ namespace ComSquare::CPU }; //! @brief The Processor status register; - union p { - //! @brief The Negative flag - bool n : 1; - //! @brief The oVerflow flag - bool v : 1; - //! @brief The accumulator and Memory width flag (in native mode only) - bool m : 1; - union { - //! @brief The indeX register width flag (in native mode only) - bool x : 1; - //! @brief The Break flag (in emulation mode only) - bool b : 1; + union { + struct { + //! @brief The Negative flag + bool n : 1; + //! @brief The oVerflow flag + bool v : 1; + //! @brief The accumulator and Memory width flag (in native mode only) + bool m : 1; + //! @brief The indeX register width flag (in native mode only) OR the Break flag (in emulation mode only) + bool x_b : 1; + //! @brief The Decimal mode flag + bool d : 1; + //! @brief The Interrupt request disable flag + bool i : 1; + //! @brief The Zero flag + bool z : 1; + //! @brief The Carry flag + bool c : 1; }; - //! @brief The Decimal mode flag - bool d : 1; - //! @brief The Interrupt disable flag - bool i : 1; - //! @brief The Zero flag - bool z : 1; - //! @brief The Carry flag - bool c : 1; - }; + uint8_t flags; + } p; }; //! @brief Struct containing internal registers of the CPU. @@ -175,7 +175,7 @@ namespace ComSquare::CPU }; //! @brief The main CPU - class CPU : public CommonCPU, public Memory::IMemory { + class CPU : public CommonInstructions, public Memory::IMemory { private: //! @brief All the registers of the CPU Registers _registers{}; @@ -185,12 +185,14 @@ namespace ComSquare::CPU InternalRegisters _internalRegisters{}; //! @brief The memory bus to use for read/write. std::shared_ptr _bus; + //! @brief The cartridge header (stored for interrupt vectors.. + Cartridge::Header &_cartridgeHeader; //! @brief Execute a single instruction. //! @return The number of CPU cycles that the instruction took. int executeInstruction(); public: - explicit CPU(std::shared_ptr bus); + explicit CPU(std::shared_ptr bus, Cartridge::Header &cartridgeHeader); //! @brief This function continue to execute the Cartridge code. //! @return The number of CPU cycles that elapsed int update(); @@ -204,6 +206,10 @@ namespace ComSquare::CPU //! @param data The new value of the register. //! @throw InvalidAddress will be thrown if the address is more than $1F (the number of register). void write(uint24_t addr, uint8_t data) override; + + private: + //! @brief Break instruction (0x00) - Causes a software break. The PC is loaded from a vector table. + int BRK(); }; } diff --git a/sources/CPU/CommonCpu.cpp b/sources/CPU/Instructions/CommonInstructions.cpp similarity index 69% rename from sources/CPU/CommonCpu.cpp rename to sources/CPU/Instructions/CommonInstructions.cpp index 732c482..de8ee00 100644 --- a/sources/CPU/CommonCpu.cpp +++ b/sources/CPU/Instructions/CommonInstructions.cpp @@ -2,7 +2,7 @@ // Created by anonymus-raccoon on 2/5/20. // -#include "CommonCpu.hpp" +#include "CommonInstructions.hpp" namespace ComSquare::CPU { diff --git a/sources/CPU/CommonCpu.hpp b/sources/CPU/Instructions/CommonInstructions.hpp similarity index 50% rename from sources/CPU/CommonCpu.hpp rename to sources/CPU/Instructions/CommonInstructions.hpp index 03ba660..a8e1e41 100644 --- a/sources/CPU/CommonCpu.hpp +++ b/sources/CPU/Instructions/CommonInstructions.hpp @@ -2,15 +2,15 @@ // Created by anonymus-raccoon on 2/5/20. // -#ifndef COMSQUARE_COMMONCPU_HPP -#define COMSQUARE_COMMONCPU_HPP +#ifndef COMSQUARE_COMMONINSTRUCTIONS_HPP +#define COMSQUARE_COMMONINSTRUCTIONS_HPP namespace ComSquare::CPU { //! @brief The shared states of the Main's CPU and the APU's CPU. - class CommonCPU { + class CommonInstructions { }; } -#endif //COMSQUARE_COMMONCPU_HPP +#endif //COMSQUARE_COMMONINSTRUCTIONS_HPP diff --git a/sources/Exceptions/InvalidOpcode.hpp b/sources/Exceptions/InvalidOpcode.hpp new file mode 100644 index 0000000..6b02c29 --- /dev/null +++ b/sources/Exceptions/InvalidOpcode.hpp @@ -0,0 +1,28 @@ +// +// Created by anonymus-raccoon on 1/30/20. +// + +#ifndef COMSQUARE_INVALIDACTION_HPP +#define COMSQUARE_INVALIDACTION_HPP + +#include +#include +#include + +namespace ComSquare +{ + //! @brief Exception thrown when someone tries to load an invalid rom. + class InvalidOpcode : std::exception { + private: + std::string _msg; + public: + explicit InvalidOpcode(const std::string &pu, unsigned opcode) + { + std::stringstream stream; + stream << "The " + pu + ": 0x" << std::hex << opcode; + this->_msg = stream.str(); + } + const char *what() const noexcept override { return this->_msg.c_str(); } + }; +} +#endif //COMSQUARE_INVALIDACTION_HPP diff --git a/sources/SNES.cpp b/sources/SNES.cpp index ed543fd..49e7137 100644 --- a/sources/SNES.cpp +++ b/sources/SNES.cpp @@ -7,10 +7,10 @@ namespace ComSquare { SNES::SNES(const std::shared_ptr &bus, const std::string &romPath, Renderer::IRenderer &renderer) : - cpu(new CPU::CPU(bus)), + cartridge(new Cartridge::Cartridge(romPath)), + cpu(new CPU::CPU(bus, cartridge->header)), ppu(new PPU::PPU()), apu(new APU::APU()), - cartridge(new Cartridge::Cartridge(romPath)), wram(new Ram::Ram(16384)), sram(new Ram::Ram(this->cartridge->header.sramSize)) { diff --git a/sources/SNES.hpp b/sources/SNES.hpp index 8bb865b..996cb8a 100644 --- a/sources/SNES.hpp +++ b/sources/SNES.hpp @@ -18,14 +18,14 @@ namespace ComSquare //! @brief Container of all the components of the SNES. struct SNES { public: + //! @brief Cartridge containing instructions (ROM). + std::shared_ptr cartridge; //! @brief Central Processing Unit of the SNES. std::shared_ptr cpu; //! @brief Picture Processing Unit of the SNES std::shared_ptr ppu; //! @brief Audio Processing Unit if the SNES std::shared_ptr apu; - //! @brief Cartridge containing instructions (ROM). - std::shared_ptr cartridge; //! @brief Work Ram shared by all the components. std::shared_ptr wram; //! @brief Save Ram residing inside the Cartridge in a real SNES. diff --git a/tests/testCPU.cpp b/tests/testCPU.cpp index 2ab4ec8..92ab3fe 100644 --- a/tests/testCPU.cpp +++ b/tests/testCPU.cpp @@ -2,3 +2,36 @@ // Created by anonymus-raccoon on 1/24/20. // +#include +#include +#include +#include "communism.hpp" +#include "../sources/SNES.hpp" +#include "../sources/Memory/MemoryBus.hpp" +using namespace ComSquare; + +std::pair Init(); + +Test(CPU_emulated, BRK) +{ + auto pair = Init(); + pair.second.cartridge->header.emulationInterrupts.brk = 0x123u; + pair.second.cpu->_registers.p.d = true; + pair.second.cpu->_registers.pc = 0x156u; + cr_assert_eq(pair.second.cpu->BRK(), 7); + cr_assert_eq(pair.second.cpu->_registers.pc, 0x123u); + cr_assert_eq(pair.second.cpu->_registers.p.i, 1, "pair.second.cpu->_registers.p.i mmust be equal to 1 but it was %d", pair.second.cpu->_registers.p.i); + cr_assert_eq(pair.second.cpu->_registers.p.d, false); +} + +Test(CPU_native, BRK) +{ + auto pair = Init(); + pair.second.cpu->_isEmulationMode = false; + pair.second.cartridge->header.nativeInterrupts.brk = 0x123u; + pair.second.cpu->_registers.pc = 0x156u; + cr_assert_eq(pair.second.cpu->BRK(), 8); + cr_assert_eq(pair.second.cpu->_registers.pc, 0x123u); + cr_assert_eq(pair.second.cpu->_registers.p.i, true); + cr_assert_eq(pair.second.cpu->_registers.p.d, false); +} \ No newline at end of file