Finishing the first opcode

This commit is contained in:
AnonymusRaccoon
2020-02-07 17:24:15 +01:00
parent 8ff4c0ac11
commit 36d615ba64
9 changed files with 133 additions and 39 deletions

View File

@@ -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

View File

@@ -7,12 +7,15 @@
#include <utility>
#include "../Exceptions/NotImplementedException.hpp"
#include "../Exceptions/InvalidAddress.hpp"
#include "../Exceptions/InvalidOpcode.hpp"
namespace ComSquare::CPU
{
CPU::CPU(std::shared_ptr<Memory::MemoryBus> bus)
: _bus(std::move(bus))
{ }
CPU::CPU(std::shared_ptr<Memory::MemoryBus> 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;
}
}

View File

@@ -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 {
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;
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;
};
//! @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 disable flag
//! @brief The Interrupt request 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<Memory::MemoryBus> _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<Memory::MemoryBus> bus);
explicit CPU(std::shared_ptr<Memory::MemoryBus> 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();
};
}

View File

@@ -2,7 +2,7 @@
// Created by anonymus-raccoon on 2/5/20.
//
#include "CommonCpu.hpp"
#include "CommonInstructions.hpp"
namespace ComSquare::CPU
{

View File

@@ -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

View File

@@ -0,0 +1,28 @@
//
// Created by anonymus-raccoon on 1/30/20.
//
#ifndef COMSQUARE_INVALIDACTION_HPP
#define COMSQUARE_INVALIDACTION_HPP
#include <exception>
#include <string>
#include <ios>
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

View File

@@ -7,10 +7,10 @@
namespace ComSquare
{
SNES::SNES(const std::shared_ptr<Memory::MemoryBus> &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))
{

View File

@@ -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::Cartridge> cartridge;
//! @brief Central Processing Unit of the SNES.
std::shared_ptr<CPU::CPU> cpu;
//! @brief Picture Processing Unit of the SNES
std::shared_ptr<PPU::PPU> ppu;
//! @brief Audio Processing Unit if the SNES
std::shared_ptr<APU::APU> apu;
//! @brief Cartridge containing instructions (ROM).
std::shared_ptr<Cartridge::Cartridge> cartridge;
//! @brief Work Ram shared by all the components.
std::shared_ptr<Ram::Ram> wram;
//! @brief Save Ram residing inside the Cartridge in a real SNES.

View File

@@ -2,3 +2,36 @@
// Created by anonymus-raccoon on 1/24/20.
//
#include <criterion/criterion.h>
#include <iostream>
#include <bitset>
#include "communism.hpp"
#include "../sources/SNES.hpp"
#include "../sources/Memory/MemoryBus.hpp"
using namespace ComSquare;
std::pair<Memory::MemoryBus, SNES> 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);
}