diff --git a/CMakeLists.txt b/CMakeLists.txt index 3d3a007..d7cec3d 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -36,4 +36,4 @@ add_executable(ComSquare sources/PPU/Ppu.hpp sources/APU/APU.hpp sources/APU/APU.cpp - sources/Exceptions/InvalidAddress.hpp) + sources/Exceptions/InvalidAddress.hpp sources/Exceptions/InvalidRom.hpp sources/Models/Ints.hpp sources/Models/Ints.hpp) diff --git a/sources/CPU/CPU.cpp b/sources/CPU/CPU.cpp index 36291b3..81d88d3 100644 --- a/sources/CPU/CPU.cpp +++ b/sources/CPU/CPU.cpp @@ -3,10 +3,34 @@ // #include "CPU.hpp" +#include "../Exceptions/NotImplementedException.hpp" namespace ComSquare::CPU { CPU::CPU(std::shared_ptr bus) : _bus(bus) { } + + uint8_t CPU::read(uint24_t addr) + { + (void)addr; + throw NotImplementedException(); + } + + void CPU::write(uint24_t addr, uint8_t data) + { + (void)addr; + (void)data; + throw NotImplementedException(); + } + + int CPU::update() + { + throw NotImplementedException(); + } + + int CPU::executeInstruction() + { + throw NotImplementedException(); + } } \ No newline at end of file diff --git a/sources/CPU/CPU.hpp b/sources/CPU/CPU.hpp index dab9a25..d64008c 100644 --- a/sources/CPU/CPU.hpp +++ b/sources/CPU/CPU.hpp @@ -7,6 +7,7 @@ #include "../Memory/IMemory.hpp" #include "../Memory/MemoryBus.hpp" +#include "../Models/Ints.hpp" namespace ComSquare::CPU { @@ -15,54 +16,54 @@ namespace ComSquare::CPU //! @brief The Accumulator union { struct { - unsigned char ah; - unsigned char al; + uint8_t ah; + uint8_t al; }; - unsigned short a; + uint16_t a; }; //! @brief The Data Bank Register; - unsigned char dbr; + uint8_t dbr; //! @brief The Direct register; union { struct { - unsigned char dh; - unsigned char dl; + uint8_t dh; + uint8_t dl; }; - unsigned short d; + uint16_t d; }; //! @brief The program banK register; - unsigned char k; + uint8_t k; //! @brief The Program Counter; union { struct { - unsigned char pch; - unsigned char pcl; + uint8_t pch; + uint8_t pcl; }; - unsigned short pc; + uint16_t pc; }; //! @brief The Stack pointer union { struct { - unsigned char sh; - unsigned char sl; + uint8_t sh; + uint8_t sl; }; - unsigned short s; + uint16_t s; }; //! @brief The X index register union { struct { - unsigned char xh; - unsigned char xl; + uint8_t xh; + uint8_t xl; }; - unsigned short x; + uint16_t x; }; //! @brief The Y index register union { struct { - unsigned char yh; - unsigned char yl; + uint8_t yh; + uint8_t yl; }; - unsigned short y; + uint16_t y; }; //! @brief The Processor status register; @@ -90,24 +91,118 @@ namespace ComSquare::CPU }; }; + //! @brief Struct containing internal registers of the CPU. + struct InternalRegisters + { + //! @brief Interrupt Enable Register + uint8_t nmitimen; + + //! @brief IO Port Write Register + uint8_t wrio; + + //! @brief Multiplicand Register A + uint8_t wrmpya; + //! @brief Multiplicand Register B + uint8_t wrmpyb; + + //! @brief Divisor & Dividend Registers (A - Low) + uint8_t wrdivl; + //! @brief Divisor & Dividend Registers (A - High) + uint8_t wrdivh; + //! @brief Divisor & Dividend Registers (B) + uint8_t wrdivb; + + //! @brief IRQ Timer Registers (Horizontal - Low) + uint8_t htimel; + //! @brief IRQ Timer Registers (Horizontal - High) + uint8_t htimeh; + + //! @brief IRQ Timer Registers (Vertical - Low) + uint8_t vtimel; + //! @brief IRQ Timer Registers (Vertical - High) + uint8_t vtimeh; + + //! @brief DMA Enable Register + uint8_t mdmaen; + + //! @brief HDMA Enable Register + uint8_t hdmaen; + + //! @brief ROM Speed Register + uint8_t memsel; + + //! @brief Interrupt Flag Registers + uint8_t rdnmi; + //! @brief Interrupt Flag Registers - TimeUp + uint8_t timeup; + + //! @brief PPU Status Register + uint8_t hvbjoy; + + //! @brief IO Port Read Register + uint8_t rdio; + + //! @brief Divide Result Registers (can sometimes be used as multiplication result register) - LOW + uint8_t rddivl; + //! @brief Divide Result Registers (can sometimes be used as multiplication result register) - HIGH + uint8_t rddivh; + + //! @brief Multiplication Result Registers (can sometimes be used as divide result register) - LOW + uint8_t rdmpyl; + //! @brief Multiplication Result Registers (can sometimes be used as divide result register) - HIGH + uint8_t rdmpyh; + + //! @brief Controller Port Data Registers (Pad 1 - Low) + uint8_t joy1l; + //! @brief Controller Port Data Registers (Pad 1 - High) + uint8_t joy1h; + + //! @brief Controller Port Data Registers (Pad 2 - Low) + uint8_t joy2l; + //! @brief Controller Port Data Registers (Pad 2 - High) + uint8_t joy2h; + + //! @brief Controller Port Data Registers (Pad 3 - Low) + uint8_t joy3l; + //! @brief Controller Port Data Registers (Pad 3 - High) + uint8_t joy3h; + + //! @brief Controller Port Data Registers (Pad 4 - Low) + uint8_t joy4l; + //! @brief Controller Port Data Registers (Pad 4 - High) + uint8_t joy4h; + }; + //! @brief The main CPU - class CPU { + class CPU : IMemory { private: //! @brief All the registers of the CPU Registers _registers; //! @brief Is the CPU running in emulation mode (in 8bits) bool _isEmulationMode; + //! @brief Internal registers of the CPU (accessible from the bus via addr $4200 to $421F). + InternalRegisters _internalRegisters; //! @brief The memory bus to use for read/write. std::shared_ptr _bus; //! @brief Execute a single instruction. //! @return The number of CPU cycles that the instruction took. - int executreInstruction(); + int executeInstruction(); public: explicit CPU(std::shared_ptr bus); //! @brief This function continue to execute the Cartridge code. //! @return The number of CPU cycles that elapsed int update(); + //! @brief Read from the internal CPU register. + //! @param addr The address to read from. The address 0x0 should refer to the first byte of the register. + //! @throw InvalidAddress will be thrown if the address is more than $1F (the number of register). + //! @return Return the value of the register. + uint8_t read(uint24_t addr) override; + //! @brief Write data to the internal CPU register. + //! @param addr The address to write to. The address 0x0 should refer to the first byte of register. + //! @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; }; } diff --git a/sources/Cartridge/Cartridge.cpp b/sources/Cartridge/Cartridge.cpp index 2f11dd8..96dbe2f 100644 --- a/sources/Cartridge/Cartridge.cpp +++ b/sources/Cartridge/Cartridge.cpp @@ -6,7 +6,8 @@ #include #include #include "Cartridge.hpp" -#include "../Exceptions/NotImplementedException.hpp" +#include "../Exceptions/InvalidAddress.hpp" +#include "../Exceptions/InvalidRom.hpp" namespace ComSquare::Cartridge { @@ -36,16 +37,17 @@ namespace ComSquare::Cartridge } } - uint8_t Cartridge::read(uint32_t addr) + uint8_t Cartridge::read(uint24_t addr) { - (void)addr; - throw NotImplementedException(); + if (addr >= this->_size) + throw InvalidAddress(addr); + return this->_data[addr]; } - void Cartridge::write(uint32_t addr, uint8_t data) + void Cartridge::write(uint24_t addr, uint8_t data) { - (void)addr; - (void)data; - throw NotImplementedException(); + if (addr >= this->_size) + throw InvalidAddress(addr); + this->_data[addr] = data; } } \ No newline at end of file diff --git a/sources/Cartridge/Cartridge.hpp b/sources/Cartridge/Cartridge.hpp index 584e285..59ad0f7 100644 --- a/sources/Cartridge/Cartridge.hpp +++ b/sources/Cartridge/Cartridge.hpp @@ -7,23 +7,15 @@ #include #include "../Memory/IMemory.hpp" +#include "../Models/Ints.hpp" namespace ComSquare::Cartridge { - //! @brief Exception thrown when someone tries to load an invalid rom. - class InvalidRomException : std::exception { - private: - std::string _msg; - public: - explicit InvalidRomException(const std::string &msg) : _msg(msg) {} - const char *what() const noexcept override { return this->_msg.c_str(); } - }; - //! @brief Contains the rom's memory/instructions. class Cartridge : IMemory { private: //! @brief The rom data (contains all the instructions). - unsigned char *_data; + uint8_t *_data; //! @brief The size of the rom data. size_t _size; //! @brief Get the size of a rom from it's path. @@ -35,14 +27,14 @@ namespace ComSquare::Cartridge explicit Cartridge(const std::string &romPath); //! @brief Read from the rom. //! @param addr The address to read from. The address 0x0 should refer to the first byte of the rom's memory. - //! @throw InvalidAddress will be thrown if the address is less than 0 or more than the size of the rom's memory. + //! @throw InvalidAddress will be thrown if the address is more than the size of the rom's memory. //! @return Return the data at the address. - uint8_t read(uint32_t addr) override; + uint8_t read(uint24_t addr) override; //! @brief Write data to the rom. //! @param addr The address to write to. The address 0x0 should refer to the first byte of the rom's memory. //! @param data The data to write. - //! @throw InvalidAddress will be thrown if the address is less than 0 or more than the size of the rom's memory. - void write(uint32_t addr, uint8_t data) override; + //! @throw InvalidAddress will be thrown if the address is more than the size of the rom's memory. + void write(uint24_t addr, uint8_t data) override; }; } diff --git a/sources/Exceptions/InvalidAddress.hpp b/sources/Exceptions/InvalidAddress.hpp index b044967..95e2b47 100644 --- a/sources/Exceptions/InvalidAddress.hpp +++ b/sources/Exceptions/InvalidAddress.hpp @@ -7,6 +7,8 @@ #include #include +#include +#include namespace ComSquare { @@ -15,7 +17,12 @@ namespace ComSquare private: std::string _msg; public: - explicit InvalidAddress(const std::string &msg) : _msg(msg) {} + explicit InvalidAddress(int32_t addr) + { + std::stringstream stream; + stream << "Could not read/write data at address: 0x" << std::hex << addr; + this->_msg = stream.str(); + } const char *what() const noexcept override { return this->_msg.c_str(); } }; } diff --git a/sources/Exceptions/InvalidRom.hpp b/sources/Exceptions/InvalidRom.hpp new file mode 100644 index 0000000..acbf0b3 --- /dev/null +++ b/sources/Exceptions/InvalidRom.hpp @@ -0,0 +1,23 @@ +// +// Created by anonymus-raccoon on 1/28/20. +// + +#ifndef COMSQUARE_INVALIDROM_HPP +#define COMSQUARE_INVALIDROM_HPP + +#include +#include + +namespace ComSquare +{ + //! @brief Exception thrown when someone tries to load an invalid rom. + class InvalidRomException : std::exception { + private: + std::string _msg; + public: + explicit InvalidRomException(const std::string &msg) : _msg(msg) {} + const char *what() const noexcept override { return this->_msg.c_str(); } + }; +} + +#endif //COMSQUARE_INVALIDROM_HPP diff --git a/sources/Memory/IMemory.cpp b/sources/Memory/IMemory.cpp index d692bed..7f0a380 100644 --- a/sources/Memory/IMemory.cpp +++ b/sources/Memory/IMemory.cpp @@ -7,13 +7,13 @@ namespace ComSquare { - void IMemory::setMemoryRegion(uint32_t start, uint32_t end) + void IMemory::setMemoryRegion(uint24_t start, uint24_t end) { this->_start = start; this->_end = end; } - bool IMemory::hasMemoryAt(uint32_t addr) + bool IMemory::hasMemoryAt(uint24_t addr) { return this->_start <= addr && addr <= this->_end; } diff --git a/sources/Memory/IMemory.hpp b/sources/Memory/IMemory.hpp index 694d932..4ae25ed 100644 --- a/sources/Memory/IMemory.hpp +++ b/sources/Memory/IMemory.hpp @@ -8,19 +8,20 @@ #include #include +#include "../Models/Ints.hpp" namespace ComSquare { class IMemory { private: - uint32_t _start = 0; - uint32_t _end = 0; + uint24_t _start = 0; + uint24_t _end = 0; public: - virtual uint8_t read(uint32_t addr) = 0; - virtual void write(uint32_t addr, uint8_t data) = 0; - void setMemoryRegion(uint32_t start, uint32_t end); - bool hasMemoryAt(uint32_t addr); - uint32_t getStart(); + virtual uint8_t read(uint24_t addr) = 0; + virtual void write(uint24_t addr, uint8_t data) = 0; + void setMemoryRegion(uint24_t start, uint24_t end); + bool hasMemoryAt(uint24_t addr); + uint24_t getStart(); }; }; diff --git a/sources/Memory/MemoryBus.cpp b/sources/Memory/MemoryBus.cpp index f790c88..1350b6a 100644 --- a/sources/Memory/MemoryBus.cpp +++ b/sources/Memory/MemoryBus.cpp @@ -8,7 +8,7 @@ namespace ComSquare { - std::shared_ptr MemoryBus::getAccessor(uint32_t addr) + std::shared_ptr MemoryBus::getAccessor(uint24_t addr) { return *std::find_if(this->_memoryAccessors.begin(), this->_memoryAccessors.end(), [addr](std::shared_ptr &accessor) { @@ -16,7 +16,7 @@ namespace ComSquare }); } - uint8_t MemoryBus::read(uint32_t addr) + uint8_t MemoryBus::read(uint24_t addr) { std::shared_ptr handler = this->getAccessor(addr); @@ -29,7 +29,7 @@ namespace ComSquare return data; } - void MemoryBus::write(uint32_t addr, uint8_t data) + void MemoryBus::write(uint24_t addr, uint8_t data) { std::shared_ptr handler = this->getAccessor(addr); diff --git a/sources/Memory/MemoryBus.hpp b/sources/Memory/MemoryBus.hpp index ff300a5..70ae1ff 100644 --- a/sources/Memory/MemoryBus.hpp +++ b/sources/Memory/MemoryBus.hpp @@ -15,11 +15,11 @@ namespace ComSquare class MemoryBus { private: std::vector> _memoryAccessors; - std::shared_ptr getAccessor(uint32_t addr); + std::shared_ptr getAccessor(uint24_t addr); uint8_t _openbus; public: - uint8_t read(uint32_t addr); - void write(uint32_t addr, uint8_t data); + uint8_t read(uint24_t addr); + void write(uint24_t addr, uint8_t data); }; } diff --git a/sources/Models/Ints.hpp b/sources/Models/Ints.hpp new file mode 100644 index 0000000..58f6c01 --- /dev/null +++ b/sources/Models/Ints.hpp @@ -0,0 +1,10 @@ +// +// Created by anonymus-raccoon on 1/28/20. +// + +#ifndef COMSQUARE_INTS_HPP +#define COMSQUARE_INTS_HPP + +typedef unsigned uint24_t; + +#endif //COMSQUARE_INTS_HPP