diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 7b36314..7e489db 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -29,7 +29,7 @@ jobs: - name: Build with Makefile run: make -C build unit_tests - name: Execute tests - run: build/unit_tests + run: cd build && ./unit_tests - name: Output coverage run: gcovr -e tests/ -o coverage.xml --xml - name: Publish to CodeCov diff --git a/CMakeLists.txt b/CMakeLists.txt index 073df8f..445cd1f 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -56,7 +56,7 @@ add_executable(unit_tests sources/APU/Instructions/Standbys.cpp tests/APU/testAPUInstructions.cpp sources/APU/Instructions/ProgramStatusWord.cpp -) + tests/APU/testAPU.cpp sources/APU/Instructions/Bit.cpp) # include criterion & coverage target_link_libraries(unit_tests criterion -lgcov) @@ -112,7 +112,7 @@ add_executable(ComSquare sources/CPU/Instructions/MathematicalOperations.cpp sources/APU/Instructions/Standbys.cpp sources/APU/Instructions/ProgramStatusWord.cpp -) + sources/APU/Instructions/Bit.cpp) target_link_libraries(ComSquare sfml-graphics diff --git a/main.cpp b/main.cpp index 36cc9e6..d9be8d4 100644 --- a/main.cpp +++ b/main.cpp @@ -4,7 +4,6 @@ #include #include -#include "sources/Renderer/IRenderer.hpp" #include "sources/SNES.hpp" #include "sources/Renderer/SFRenderer.hpp" @@ -16,29 +15,18 @@ int main(int argc, char **argv) std::cout << "ComSquare:" << std::endl << "\tUsage: " << argv[0] << " rom_path" << std::endl; return 1; } - Memory::MemoryBus bus; - Renderer::SFRenderer renderer(600, 800, 60); - SNES snes(std::make_shared(bus), argv[1], renderer); - bus.mapComponents(snes); - int incx = 0; - int incy = 0; - uint32_t pixel = 0x000000FF; + try { + Renderer::SFRenderer renderer(600, 800, 60); + SNES snes(std::make_shared(), argv[1], renderer); + while (!renderer.shouldExit) { + unsigned cycleCount = snes.cpu->update(); + snes.ppu->update(cycleCount); + snes.apu->update(cycleCount); - while (!renderer.shouldExit) { - renderer.putPixel(incy, incx++, pixel); - if (incx >= 800) { - incx = 0; - incy++; + renderer.getEvents(); } - if (incy >= 600) { - incy = 0; - } - if (incx == 0) { - renderer.drawScreen(); - pixel += 0xFF00FF00; - } - renderer.getEvents(); + } catch (std::exception &e) { + std::cerr << "An error occurred: " << e.what() << std::endl; } - return 0; } \ No newline at end of file diff --git a/sources/APU/APU.cpp b/sources/APU/APU.cpp index 5fdd94a..4c2d895 100644 --- a/sources/APU/APU.cpp +++ b/sources/APU/APU.cpp @@ -10,7 +10,109 @@ namespace ComSquare::APU { APU::APU() : _dsp(new DSP::DSP) - { } + { + this->_map.Page0 = std::make_shared(Ram::Ram(0x00F0)); + this->_map.Page1 = std::make_shared(Ram::Ram(0x0100)); + this->_map.Memory = std::make_shared(Ram::Ram(0xFDC0)); + this->_map.IPL = std::make_shared(Ram::Ram(0x0040)); + } + + uint8_t APU::_internalRead(uint24_t addr) { + switch (addr) { + case 0x0000 ... 0x00EF: + return this->_map.Page0->read_internal(addr); + case 0xF0: + return this->_registers.unknown; + case 0xF2: + return this->_registers.dspregAddr; + case 0xF3: + return this->_registers.dspregData; + case 0xF4: + return this->_registers.port0; + case 0xF5: + return this->_registers.port1; + case 0xF6: + return this->_registers.port2; + case 0xF7: + return this->_registers.port3; + case 0xF8: + return this->_registers.regmem1; + case 0xF9: + return this->_registers.regmem2; + case 0xFD: + return this->_registers.counter0; + case 0xFE: + return this->_registers.counter1; + case 0xFF: + return this->_registers.counter2; + case 0x0100 ... 0x01FF: + return this->_map.Page1->read_internal(addr - 0x0100); + case 0x0200 ... 0xFFBF: + return this->_map.Memory->read_internal(addr - 0x200); + case 0xFFC0 ... 0xFFFF: + return this->_map.IPL->read_internal(addr - 0xFFC0); + default: + throw InvalidAddress("APU Registers read", addr); + } + } + + void APU::_internalWrite(uint24_t addr, uint8_t data) { + switch (addr) { + case 0x0000 ... 0x00EF: + this->_map.Page0->write_internal(addr, data); + break; + case 0xF0: + this->_registers.unknown = data; + break; + case 0xF1: + this->_registers.ctrlreg = data; + break; + case 0xF2: + this->_registers.dspregAddr = data; + break; + case 0xF3: + this->_registers.dspregData = data; + break; + case 0xF4: + this->_registers.port0 = data; + break; + case 0xF5: + this->_registers.port1 = data; + break; + case 0xF6: + this->_registers.port2 = data; + break; + case 0xF7: + this->_registers.port3 = data; + break; + case 0xF8: + this->_registers.regmem1 = data; + break; + case 0xF9: + this->_registers.regmem2 = data; + break; + case 0xFA: + this->_registers.timer0 = data; + break; + case 0xFB: + this->_registers.timer1 = data; + break; + case 0xFC: + this->_registers.timer2 = data; + break; + case 0x0100 ... 0x01FF: + this->_map.Page1->write_internal(addr - 0x0100, data); + break; + case 0x0200 ... 0xFFBF: + this->_map.Memory->write_internal(addr - 0x200, data); + break; + case 0xFFC0 ... 0xFFFF: + this->_map.IPL->write_internal(addr - 0xFFC0, data); + break; + default: + throw InvalidAddress("APU Registers write", addr); + } + } uint8_t APU::read(uint24_t addr) { @@ -50,41 +152,41 @@ namespace ComSquare::APU int APU::executeInstruction() { - uint8_t opcode = read(this->_internalRegisters.pc++); + uint8_t opcode = this->_internalRead(this->_internalRegisters.pc++); switch (opcode) { case 0x00: - return NOP(); + return this->NOP(); case 0x20: - return CLRP(); + return this->CLRP(); case 0x40: - return SETP(); + return this->SETP(); case 0x60: - return CLRC(); + return this->CLRC(); case 0x80: - return SETC(); + return this->SETC(); case 0xA0: - return EI(); + return this->EI(); case 0xC0: - return DI(); + return this->DI(); case 0xED: - return NOTC(); + return this->NOTC(); case 0xEF: - return SLEEP(); + return this->SLEEP(); case 0xFF: - return STOP(); + return this->STOP(); default: throw InvalidOpcode("APU", opcode); } } - void APU::update(uint8_t cycles) + void APU::update(unsigned cycles) { - int total = 0; + unsigned total = 0; cycles -= this->_paddingCycles; while (total < cycles && this->_state == Running) - total += executeInstruction(); + total += this->executeInstruction(); if (this->_state == Running) this->_paddingCycles = total - cycles; } diff --git a/sources/APU/APU.hpp b/sources/APU/APU.hpp index e22cf73..7c940b4 100644 --- a/sources/APU/APU.hpp +++ b/sources/APU/APU.hpp @@ -8,6 +8,7 @@ #include #include "DSP/DSP.hpp" #include "../Memory/IMemory.hpp" +#include "../Ram/Ram.hpp" namespace ComSquare::APU { @@ -28,7 +29,7 @@ namespace ComSquare::APU }; //! @brief The Stack pointer register - uint8_t sp; + uint8_t sp = 0x00FF; //! @brief The Program counter register union { @@ -113,6 +114,18 @@ namespace ComSquare::APU Stopped }; + struct MemoryMap + { + //! @brief Zero page memory + std::shared_ptr Page0; + //! @brief Stack space memory + std::shared_ptr Page1; + //! @brief Any-use memory + std::shared_ptr Memory; + //! @brief IPL ROM + std::shared_ptr IPL; + }; + class APU : public Memory::IMemory { private: //! @brief All the registers of the APU CPU @@ -120,14 +133,28 @@ namespace ComSquare::APU //! @brief Internal registers of the CPU (accessible from the bus via addr $4200 to $421F). InternalRegisters _internalRegisters{}; + //! @brief Internal APU memory separated according to their utility + MemoryMap _map; + //! @brief The DSP component used to produce sound std::shared_ptr _dsp; + //! @brief Read from the APU ram. + //! @param addr The address to read from. The address 0x0000 should refer to the first byte of the register. + //! @throw InvalidAddress will be thrown if the address is more than $FFFF (the number of register). + //! @return Return the data. + uint8_t _internalRead(uint24_t addr); + //! @brief Write data to the APU ram. + //! @param addr The address to write to. The address 0x0000 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 $FFFF (the number of register). + void _internalWrite(uint24_t addr, uint8_t data); + //! @brief Current state of APU CPU StateMode _state = Running; //! @brief Keep the number of excess cycles executed to pad the next update - int _paddingCycles = 0; + unsigned int _paddingCycles = 0; //! @brief Execute a single instruction. //! @return The number of cycles that the instruction took. @@ -160,18 +187,18 @@ namespace ComSquare::APU explicit APU(); //! @brief Read from the internal APU register. - //! @param addr The address to read from. The address 0xF0 should refer to the first byte of the register. - //! @throw InvalidAddress will be thrown if the address is more than $FF (the number of register). + //! @param addr The address to read from. The address 0x00 should refer to the first byte of the register. + //! @throw InvalidAddress will be thrown if the address is more than $0F (the number of register). //! @return Return the value of the register. uint8_t read(uint24_t addr) override; //! @brief Write data to the internal APU register. - //! @param addr The address to write to. The address 0xF0 should refer to the first byte of register. + //! @param addr The address to write to. The address 0x00 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 $FF (the number of register). + //! @throw InvalidAddress will be thrown if the address is more than $0F (the number of register). void write(uint24_t addr, uint8_t data) override; //! @brief This function execute the instructions received until the maximum number of cycles is reached. //! @return The number of cycles that elapsed. - void update(uint8_t cycles); + void update(unsigned cycles); }; } diff --git a/sources/APU/Instructions/Bit.cpp b/sources/APU/Instructions/Bit.cpp new file mode 100644 index 0000000..f34dfa4 --- /dev/null +++ b/sources/APU/Instructions/Bit.cpp @@ -0,0 +1,13 @@ +// +// Created by Melefo on 12/02/2020. +// + +#include "../APU.hpp" + +namespace ComSquare::APU +{ + int SET1() + { + + } +} \ No newline at end of file diff --git a/sources/CPU/CPU.cpp b/sources/CPU/CPU.cpp index b0ed691..e40669f 100644 --- a/sources/CPU/CPU.cpp +++ b/sources/CPU/CPU.cpp @@ -5,6 +5,7 @@ #include "CPU.hpp" #include +#include #include "../Exceptions/NotImplementedException.hpp" #include "../Exceptions/InvalidAddress.hpp" #include "../Exceptions/InvalidOpcode.hpp" @@ -184,9 +185,9 @@ namespace ComSquare::CPU } } - int CPU::update() + unsigned CPU::update() { - int cycles = 0; + unsigned cycles = 0; for (int i = 0; i < 0xFF; i++) cycles += this->executeInstruction(); @@ -198,26 +199,27 @@ namespace ComSquare::CPU uint8_t opcode = this->_bus->read(this->_registers.pc); switch (opcode) { - case 0x0: - return this->BRK(); - case 0x61: - case 0x63: - case 0x65: - case 0x67: - case 0x69: - case 0x6D: - case 0x6F: - case 0x71: - case 0x72: - case 0x73: - case 0x75: - case 0x77: - case 0x79: - case 0x7D: - case 0x7F: - return this->ADC(); + case Instructions::BRK: return this->BRK(); + + case Instructions::ADC_DPXi: return this->ADC(this->_getDirectIndirectIndexedXAddr()); + case Instructions::ADC_SR: return this->ADC(this->_getStackRelativeAddr()); + case Instructions::ADC_DP: return this->ADC(this->_getDirectAddr()); + case Instructions::ADC_DPil: return this->ADC(this->_getDirectIndirectLongAddr()); + case Instructions::ADC_IM: return this->ADC(this->_getImmediateAddr()); + case Instructions::ADC_ABS: return this->ADC(this->_getAbsoluteAddr()); + case Instructions::ADC_ABSl: return this->ADC(this->_getAbsoluteLongAddr()); + case Instructions::ADC_DPYi: return this->ADC(this->_getDirectIndirectIndexedYAddr()); + case Instructions::ADC_DPi: return this->ADC(this->_getDirectIndirectAddr()); + case Instructions::ADC_SRYi: return this->ADC(this->_getStackRelativeIndirectIndexedYAddr()); + case Instructions::ADC_DPX: return this->ADC(this->_getDirectIndexedByXAddr()); + case Instructions::ADC_DPYil:return this->ADC(this->_getDirectIndirectIndexedYLongAddr()); + case Instructions::ADC_ABSY: return this->ADC(this->_getAbsoluteIndexedByYAddr()); + case Instructions::ADC_ABSX: return this->ADC(this->_getAbsoluteIndexedByXAddr()); + case Instructions::ADC_ABSXl:return this->ADC(this->_getAbsoluteIndexedByXLongAddr()); + default: - throw InvalidOpcode("CPU", opcode); + return 0; + //throw InvalidOpcode("CPU", opcode); } } @@ -225,27 +227,161 @@ namespace ComSquare::CPU /// Addressing modes //////////////////////////////////////////////////////////////////// - uint24_t CPU::_GetImmediateAddr() + uint24_t CPU::_getImmediateAddr() { return this->_registers.pac++; } - uint24_t CPU::_GetDirectAddr() + uint24_t CPU::_getDirectAddr() { uint8_t addr = this->_bus->read(this->_registers.pac++); return this->_registers.d + addr; } - uint24_t CPU::_GetAbsoluteAddr() + uint24_t CPU::_getAbsoluteAddr() { uint24_t addr = this->_registers.dbr << 16u; - addr += this->_bus->read(this->_registers.pac++) << 8u; addr += this->_bus->read(this->_registers.pac++); + addr += this->_bus->read(this->_registers.pac++) << 8u; return addr; } - uint24_t CPU::_GetAbsoluteLongAddr() + uint24_t CPU::_getAbsoluteLongAddr() { - return 0; + uint24_t addr = this->_bus->read(this->_registers.pac++); + addr += this->_bus->read(this->_registers.pac++) << 8u; + addr += this->_bus->read(this->_registers.pac++) << 16u; + return addr; + } + + uint24_t CPU::_getDirectIndirectIndexedYAddr() + { + uint16_t dp = this->_bus->read(this->_registers.pac++) + this->_registers.d; + uint24_t base = this->_bus->read(dp); + base += this->_bus->read(dp + 1) << 8u; + base += this->_registers.dbr << 16u; + return base + this->_registers.y; + } + + uint24_t CPU::_getDirectIndirectIndexedYLongAddr() + { + uint16_t dp = this->_bus->read(this->_registers.pac++) + this->_registers.d; + uint24_t base = this->_bus->read(dp); + base += this->_bus->read(dp + 1) << 8u; + base += this->_bus->read(dp + 2) << 16u; + return base; + } + + uint24_t CPU::_getDirectIndirectIndexedXAddr() + { + uint16_t dp = this->_bus->read(this->_registers.pac++) + this->_registers.d; + dp += this->_registers.x; + uint24_t base = this->_bus->read(dp); + base += this->_bus->read(dp + 1) << 8u; + base += this->_registers.dbr << 16u; + return base; + } + + uint24_t CPU::_getDirectIndexedByXAddr() + { + uint16_t dp = this->_bus->read(this->_registers.pac++) + this->_registers.d; + dp += this->_registers.x; + return dp; + } + + uint24_t CPU::_getDirectIndexedByYAddr() + { + uint16_t dp = this->_bus->read(this->_registers.pac++) + this->_registers.d; + dp += this->_registers.y; + return dp; + } + + uint24_t CPU::_getAbsoluteIndexedByXAddr() + { + uint16_t abs = this->_bus->read(this->_registers.pac++); + abs += this->_bus->read(this->_registers.pac++) << 8u; + uint24_t effective = abs + (this->_registers.dbr << 16u); + return effective + this->_registers.x; + } + + uint24_t CPU::_getAbsoluteIndexedByYAddr() + { + uint16_t abs = this->_bus->read(this->_registers.pac++); + abs += this->_bus->read(this->_registers.pac++) << 8u; + uint24_t effective = abs + (this->_registers.dbr << 16u); + return effective + this->_registers.y; + } + + uint24_t CPU::_getAbsoluteIndexedByXLongAddr() + { + uint24_t lng = this->_bus->read(this->_registers.pac++); + lng += this->_bus->read(this->_registers.pac++) << 8u; + lng += this->_bus->read(this->_registers.pac++) << 16u; + return lng + this->_registers.x; + } + + uint24_t CPU::_getProgramCounterRelativeAddr() + { + uint24_t pc = this->_registers.pac; + int8_t mod = this->_bus->read(this->_registers.pac++); + return pc + mod; + } + + uint24_t CPU::_getProgramCounterRelativeLongAddr() + { + uint24_t pc = this->_registers.pac; + uint8_t val1 = this->_bus->read(this->_registers.pac++); + uint8_t val2 = this->_bus->read(this->_registers.pac++); + int16_t mod = val2 > 0x7F ? (static_cast(val2) * 256 - val1) : (val1 | val2 << 8u); + return pc + mod; + } + + uint24_t CPU::_getAbsoluteIndirectAddr() + { + uint16_t abs = this->_bus->read(this->_registers.pac++); + abs += this->_bus->read(this->_registers.pac++) << 8u; + uint24_t effective = this->_bus->read(abs); + effective += this->_bus->read(abs + 1) << 8u; + return effective; + } + + uint24_t CPU::_getAbsoluteIndexedIndirectAddr() + { + uint24_t abs = this->_bus->read(this->_registers.pac++); + abs += this->_bus->read(this->_registers.pac++) << 8u; + abs += this->_registers.x; + uint24_t effective = this->_bus->read(abs); + effective += this->_bus->read(abs + 1) << 8u; + return effective; + } + + uint24_t CPU::_getDirectIndirectAddr() + { + uint16_t dp = this->_bus->read(this->_registers.pac++) + this->_registers.d; + uint24_t effective = this->_bus->read(dp); + effective += this->_bus->read(dp + 1) << 8u; + effective += this->_registers.dbr << 16u; + return effective; + } + + uint24_t CPU::_getDirectIndirectLongAddr() + { + uint16_t dp = this->_bus->read(this->_registers.pac++) + this->_registers.d; + uint24_t effective = this->_bus->read(dp); + effective += this->_bus->read(++dp) << 8u; + effective += this->_bus->read(++dp) << 16u; + return effective; + } + + uint24_t CPU::_getStackRelativeAddr() + { + return this->_bus->read(this->_registers.pac++) + this->_registers.s; + } + + uint24_t CPU::_getStackRelativeIndirectIndexedYAddr() + { + uint24_t base = this->_bus->read(this->_registers.pac++) + this->_registers.s; + base += this->_registers.dbr << 16u; + return base + this->_registers.y; } } \ No newline at end of file diff --git a/sources/CPU/CPU.hpp b/sources/CPU/CPU.hpp index 13977a6..7cd63b2 100644 --- a/sources/CPU/CPU.hpp +++ b/sources/CPU/CPU.hpp @@ -180,6 +180,30 @@ namespace ComSquare::CPU uint8_t joy4h; }; + //! @brief All the instructions opcode of the main CPI. + //! @info The name of the instruction followed by their parameters (after an underscore) if any. + //! @info Addr mode with an i at the end means indirect. + //! @info Addr mode with an l at the end means long. + enum Instructions + { + BRK = 0x00, + ADC_DPXi = 0x61, + ADC_SR = 0x63, + ADC_DP = 0x65, + ADC_DPil = 0x67, + ADC_IM = 0x69, + ADC_ABS = 0x6D, + ADC_ABSl = 0x6F, + ADC_DPYi = 0x71, + ADC_DPi = 0x72, + ADC_SRYi = 0x73, + ADC_DPX = 0x75, + ADC_DPYil = 0x77, + ADC_ABSY = 0x79, + ADC_ABSX = 0x7D, + ADC_ABSXl = 0x7F + }; + //! @brief The main CPU class CPU : public CommonInstructions, public Memory::IMemory { private: @@ -195,28 +219,60 @@ namespace ComSquare::CPU Cartridge::Header &_cartridgeHeader; //! @brief Immediate address mode is specified with a value. (This functions returns the 24bit space address of the value). - uint24_t _GetImmediateAddr(); + uint24_t _getImmediateAddr(); //! @brief The destination is formed by adding the direct page register with the 8-bit address to form an effective address. (This functions returns the 24bit space address of the value). - uint24_t _GetDirectAddr(); + uint24_t _getDirectAddr(); //! @brief The effective address is formed by DBR:<16-bit exp>. (This functions returns the 24bit space address of the value). - uint24_t _GetAbsoluteAddr(); + uint24_t _getAbsoluteAddr(); //! @brief The effective address is the expression. (This functions returns the 24bit space address of the value). - uint24_t _GetAbsoluteLongAddr(); + uint24_t _getAbsoluteLongAddr(); + //! @brief The address is DBR:$(read($($Value + D)) + Y). (This functions returns the 24bit space address of the value). + uint24_t _getDirectIndirectIndexedYAddr(); + //! @brief This mode is like the previous addressing mode, but the difference is that rather than pulling 2 bytes from the DP address, it pulls 3 bytes to form the effective address. + uint24_t _getDirectIndirectIndexedYLongAddr(); + //! @brief The direct page address is calculated and added with x. 2 bytes from the dp address combined with DBR will form the effective address. + uint24_t _getDirectIndirectIndexedXAddr(); + //! @brief The DP address is added to X to form the effective address. The effective address is always in bank 0. + uint24_t _getDirectIndexedByXAddr(); + //! @brief The DP address is added to Y to form the effective address. The effective address is always in bank 0. + uint24_t _getDirectIndexedByYAddr(); + //! @brief The absolute expression is added with X and combined with DBR to form the effective address. + uint24_t _getAbsoluteIndexedByXAddr(); + //! @brief The absolute expression is added with Y and combined with DBR to form the effective address. + uint24_t _getAbsoluteIndexedByYAddr(); + //! @brief The effective address is formed by adding the with X. + uint24_t _getAbsoluteIndexedByXLongAddr(); + //! @brief The <8-bit signed exp> is added to PC (program counter) to form the new location. + uint24_t _getProgramCounterRelativeAddr(); + //! @brief The <16-bit signed exp> is added to PC (program counter) to form the new location. + uint24_t _getProgramCounterRelativeLongAddr(); + //! @brief 2 bytes are pulled from the to form the effective address. + uint24_t _getAbsoluteIndirectAddr(); + //! @brief The is added with X, then 2 bytes are pulled from that address to form the new location. + uint24_t _getAbsoluteIndexedIndirectAddr(); + //! @brief 2 bytes are pulled from the direct page address to form the 16-bit address. It is combined with DBR to form a 24-bit effective address. + uint24_t _getDirectIndirectAddr(); + //! @brief 3 bytes are pulled from the direct page address to form an effective address. + uint24_t _getDirectIndirectLongAddr(); + //! @brief The stack register is added to the <8-bit exp> to form the effective address. + uint24_t _getStackRelativeAddr(); + //! @brief The <8-bit exp> is added to S and combined with DBR to form the base address. Y is added to the base address to form the effective address. + uint24_t _getStackRelativeIndirectIndexedYAddr(); //! @brief Execute a single instruction. //! @return The number of CPU cycles that the instruction took. int executeInstruction(); - //! @brief Break instruction (0x00) - Causes a software break. The PC is loaded from a vector table. + //! @brief Break instruction - Causes a software break. The PC is loaded from a vector table. int BRK(); - //! @brief Add with carry (0x61, 0x63, 0x65, 0x67, 0x69, 0x6D, 0x6F, 0x71, 0x72, 0x73, 0x75, 0x77, 0x79, 0x7D, 0x7F) - Adds operand to the Accumulator; adds an additional 1 if carry is set. - int ADC(); + //! @brief Add with carry - Adds operand to the Accumulator; adds an additional 1 if carry is set. + int ADC(uint24_t valueAddr); public: 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(); + unsigned 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). diff --git a/sources/CPU/Instructions/MathematicalOperations.cpp b/sources/CPU/Instructions/MathematicalOperations.cpp index fb53ff2..e1b205c 100644 --- a/sources/CPU/Instructions/MathematicalOperations.cpp +++ b/sources/CPU/Instructions/MathematicalOperations.cpp @@ -6,8 +6,10 @@ namespace ComSquare::CPU { - int CPU::ADC() + int CPU::ADC(uint24_t valueAddr) { // this->_registers.a += + (void)valueAddr; + return (0); } } \ No newline at end of file diff --git a/sources/Exceptions/InvalidOpcode.hpp b/sources/Exceptions/InvalidOpcode.hpp index 6b02c29..00447c4 100644 --- a/sources/Exceptions/InvalidOpcode.hpp +++ b/sources/Exceptions/InvalidOpcode.hpp @@ -7,19 +7,19 @@ #include #include -#include +#include namespace ComSquare { //! @brief Exception thrown when someone tries to load an invalid rom. - class InvalidOpcode : std::exception { + class InvalidOpcode : public 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; + stream << "The " + pu + " got an invalid opcode: 0x" << std::hex << opcode; this->_msg = stream.str(); } const char *what() const noexcept override { return this->_msg.c_str(); } diff --git a/sources/Memory/IRectangleMemory.cpp b/sources/Memory/IRectangleMemory.cpp index 2ecb1b3..df5e7ef 100644 --- a/sources/Memory/IRectangleMemory.cpp +++ b/sources/Memory/IRectangleMemory.cpp @@ -2,6 +2,7 @@ // Created by anonymus-raccoon on 1/29/20. // +#include #include "IRectangleMemory.hpp" #include "../Exceptions/InvalidAddress.hpp" @@ -14,9 +15,9 @@ namespace ComSquare::Memory unsigned bankCount = bank - this->_startBank; unsigned pageCount = this->_endPage - this->_startPage; - if (bank < this->_startBank || bank >= this->_endBank) + if (bank < this->_startBank || bank > this->_endBank) throw InvalidAddress("Rectangle memory read Invalid Bank", addr); - if (page < this->_startPage || page >= this->_endPage) + if (page < this->_startPage || page > this->_endPage) throw InvalidAddress("Rectangle memory read Invalid Page", addr); page -= this->_startPage; page += pageCount * bankCount; diff --git a/sources/Memory/MemoryBus.cpp b/sources/Memory/MemoryBus.cpp index ba26e3d..bb9229f 100644 --- a/sources/Memory/MemoryBus.cpp +++ b/sources/Memory/MemoryBus.cpp @@ -46,12 +46,12 @@ namespace ComSquare::Memory handler->write(addr - handler->getStart(), data); } - void MemoryBus::_mirrorComponents(SNES &console, int i) + void MemoryBus::_mirrorComponents(SNES &console, unsigned i) { - this->_memoryAccessors.emplace_back(new Memory::MemoryShadow(console.wram, i, i + 0x1FFF)); - this->_memoryAccessors.emplace_back(new Memory::MemoryShadow(console.ppu, i + 0x2100, i + 0x213F)); - this->_memoryAccessors.emplace_back(new Memory::MemoryShadow(console.apu, i + 0x2140, i + 0x2143)); - this->_memoryAccessors.emplace_back(new Memory::MemoryShadow(console.cpu, i + 0x4200, i + 0x421F)); + this->_memoryAccessors.emplace_back(new Memory::RectangleShadow(console.wram, i, i, 0x0000, 0x1FFF)); + this->_memoryAccessors.emplace_back(new Memory::MemoryShadow(console.ppu, (i << 16u) + 0x2100, (i << 16u) + 0x213F)); + this->_memoryAccessors.emplace_back(new Memory::MemoryShadow(console.apu, (i << 16u) + 0x2140, (i << 16u) + 0x2143)); + this->_memoryAccessors.emplace_back(new Memory::MemoryShadow(console.cpu, (i << 16u) + 0x4200, (i << 16u) + 0x421F)); } void MemoryBus::mapComponents(SNES &console) @@ -73,10 +73,10 @@ namespace ComSquare::Memory // TODO implement Joys. // Mirror to the quarter 1. - for (uint24_t i = 0; i < 0x400000; i += 0x010000) + for (uint8_t i = 0x00; i < 0x40; i += 0x01) this->_mirrorComponents(console, i); // Mirror to the quarter 3. - for (uint24_t i = 0x800000; i < 0xC00000; i += 0x10000) + for (uint8_t i = 0x80; i < 0xC0; i += 0x01) this->_mirrorComponents(console, i); if (console.cartridge->header.mappingMode & Cartridge::LoRom) { diff --git a/sources/Memory/MemoryBus.hpp b/sources/Memory/MemoryBus.hpp index e2af811..b7689eb 100644 --- a/sources/Memory/MemoryBus.hpp +++ b/sources/Memory/MemoryBus.hpp @@ -33,7 +33,7 @@ namespace ComSquare //! @brief WRam, CPU, PPU & APU registers are mirrored to all banks of Q1 & Q3. This function is used for the mirroring. //! @param console All the components. //! @param i Base address for the mirrors. - inline void _mirrorComponents(SNES &console, int i); + inline void _mirrorComponents(SNES &console, unsigned i); public: //! @brief Read data at a global address. diff --git a/sources/Memory/RectangleShadow.cpp b/sources/Memory/RectangleShadow.cpp index 2da6c26..f336848 100644 --- a/sources/Memory/RectangleShadow.cpp +++ b/sources/Memory/RectangleShadow.cpp @@ -5,6 +5,7 @@ #include "RectangleShadow.hpp" #include +#include namespace ComSquare::Memory { diff --git a/sources/PPU/PPU.cpp b/sources/PPU/PPU.cpp index 32bd47a..df40b39 100644 --- a/sources/PPU/PPU.cpp +++ b/sources/PPU/PPU.cpp @@ -70,7 +70,7 @@ namespace ComSquare::PPU } } - void PPU::update(int cycles) + void PPU::update(unsigned cycles) { (void)cycles; } diff --git a/sources/PPU/PPU.hpp b/sources/PPU/PPU.hpp index 702b7bc..0537100 100644 --- a/sources/PPU/PPU.hpp +++ b/sources/PPU/PPU.hpp @@ -392,7 +392,7 @@ namespace ComSquare::PPU void write(uint24_t addr, uint8_t data) override; //! @brief Update the PPU of n cycles. //! @param The number of cycles to update. - void update(int cycles); + void update(unsigned cycles); }; } #endif //COMSQUARE_PPU_HPP diff --git a/tests/APU/testAPU.cpp b/tests/APU/testAPU.cpp new file mode 100644 index 0000000..f38a192 --- /dev/null +++ b/tests/APU/testAPU.cpp @@ -0,0 +1,220 @@ +// +// Created by Melefo on 12/02/2020. +// + +#include +#include "../tests.hpp" +#include "../../sources/SNES.hpp" +#include "../../sources/APU/APU.hpp" +#include "../../sources/Exceptions/InvalidAddress.hpp" +#include "../../sources/Exceptions/InvalidOpcode.hpp" + +using namespace ComSquare; + +////////////////////////////// +// // +// APU::_internalRead tests // +// // +////////////////////////////// + +Test(_internalRead, register) +{ + auto apu = Init().second.apu; + uint8_t result = 0; + + apu->_registers.counter0 = 123; + result = apu->_internalRead(0x00FD); + cr_assert_eq(result, 123); +} + +Test(_internalRead, Page0) +{ + auto apu = Init().second.apu; + uint8_t result = 0; + + apu->_map.Page0->_data[0x0010] = 123; + result = apu->_internalRead(0x0010); + cr_assert_eq(result, 123); +} + +Test(_internalRead, Page1) +{ + auto apu = Init().second.apu; + uint8_t result = 0; + + apu->_map.Page1->_data[0x0042] = 123; + result = apu->_internalRead(0x0142); + cr_assert_eq(result, 123); +} + +Test(_internalRead, Memory) +{ + auto apu = Init().second.apu; + uint8_t result = 0; + + apu->_map.Memory->_data[0xFCDC] = 123; + result = apu->_internalRead(0xFEDC); + cr_assert_eq(result, 123); +} + +Test(_internalRead, IPL) +{ + auto apu = Init().second.apu; + uint8_t result = 0; + + apu->_map.IPL->_data[0x001F] = 123; + result = apu->_internalRead(0xFFDF); + cr_assert_eq(result, 123); +} + +Test(_internalRead, Invalid) +{ + auto apu = Init().second.apu; + + cr_assert_throw(apu->_internalRead(0x10000), InvalidAddress); +} + +/////////////////////////////// +// // +// APU::_internalWrite tests // +// // +/////////////////////////////// + +Test(_internalWrite, Page0) +{ + auto apu = Init().second.apu; + + apu->_internalWrite(0x0001, 123); + cr_assert_eq(apu->_map.Page0->_data[0x0001], 123); +} + +Test(_internalWrite, register) +{ + auto apu = Init().second.apu; + + apu->_internalWrite(0x00F4, 123); + cr_assert_eq(apu->_registers.port0, 123); +} + +Test(_internalWrite, Page1) +{ + auto apu = Init().second.apu; + + apu->_internalWrite(0x01FF, 123); + cr_assert_eq(apu->_map.Page1->_data[0x00FF], 123); +} + +Test(_internalWrite, Memory) +{ + auto apu = Init().second.apu; + + apu->_internalWrite(0x0789, 123); + cr_assert_eq(apu->_map.Memory->_data[0x0589], 123); +} + +Test(_internalWrite, IPL) +{ + auto apu = Init().second.apu; + + apu->_internalWrite(0xFFF0, 123); + cr_assert_eq(apu->_map.Memory->_data[0x0030], 123); +} + +Test(_internalWrite, Invalid) +{ + auto apu = Init().second.apu; + + cr_assert_throw(apu->_internalWrite(0x10000, 123), InvalidAddress); +} + +///////////////////// +// // +// APU::read tests // +// // +///////////////////// + +Test(read, Valid) +{ + auto apu = Init().second.apu; + uint8_t result = 0; + + apu->_registers.port2 = 123; + result = apu->read(0x02); + cr_assert_eq(result, 123); +} + +Test(read, Invalid) +{ + auto apu = Init().second.apu; + + cr_assert_throw(apu->read(0x10000), InvalidAddress); +} + +////////////////////// +// // +// APU::write tests // +// // +////////////////////// + +Test(write, Valid) +{ + auto apu = Init().second.apu; + + apu->write(0x03, 123); + cr_assert_eq(apu->_registers.port3, 123); +} + +Test(write, Invalid) +{ + auto apu = Init().second.apu; + + cr_assert_throw(apu->write(0x04, 123), InvalidAddress); +} + +/////////////////////////////////// +// // +// APU::executeInstruction tests // +// // +/////////////////////////////////// + +Test(executeInstruction, Valid) +{ + auto apu = Init().second.apu; + uint8_t result = 0; + + apu->_internalRegisters.pc = 0x00; + result = apu->executeInstruction(); + cr_assert_eq(result, 2); +} + +Test(executeInstruction, Invalid) +{ + auto apu = Init().second.apu; + + apu->_internalRegisters.pc = 0xFFFF; + cr_assert_throw(apu->executeInstruction(), InvalidOpcode); +} + +/////////////////////// +// // +// APU::update tests // +// // +/////////////////////// + +Test(update, running) +{ + auto apu = Init().second.apu; + + apu->_internalRegisters.pc = 0x00; + apu->update(1); + cr_assert_eq(apu->_paddingCycles, 1); +} + +Test(update, stopped) +{ + auto apu = Init().second.apu; + + apu->_state = APU::Stopped; + apu->update(1); + cr_assert_eq(apu->_paddingCycles, 0); +} \ No newline at end of file diff --git a/tests/CPU/testAddressingMode.cpp b/tests/CPU/testAddressingMode.cpp index 250359a..a9c6326 100644 --- a/tests/CPU/testAddressingMode.cpp +++ b/tests/CPU/testAddressingMode.cpp @@ -4,6 +4,7 @@ #include #include +#include #include "../tests.hpp" #include "../../sources/SNES.hpp" using namespace ComSquare; @@ -19,7 +20,7 @@ Test(AddrMode, Immediate) { auto pair = Init(); pair.second.cpu->_registers.pac = 0x000015; - cr_assert_eq(pair.second.cpu->_GetImmediateAddr(), 0x000015, "Got %i, Expected 0x000015"); + cr_assert_eq(pair.second.cpu->_getImmediateAddr(), 0x000015, "Got %x, Expected 0x000015"); cr_assert_eq(pair.second.cpu->_registers.pac, 0x000016); } @@ -27,16 +28,261 @@ Test(AddrMode, ImmediateBankChange) { auto pair = Init(); pair.second.cpu->_registers.pac = 0x00FFFF; - cr_assert_eq(pair.second.cpu->_GetImmediateAddr(), 0x00FFFF); + cr_assert_eq(pair.second.cpu->_getImmediateAddr(), 0x00FFFF); cr_assert_eq(pair.second.cpu->_registers.pac, 0x010000); } -//Test(AddrMode, Direct) -//{ -// auto pair = Init(); -// pair.second.cartridge->_data[0] = 0x15; -// pair.second.cpu->_registers.pac = 0x808000; -// pair.second.cpu->_registers.d = 0x1000; -// cr_assert_eq(pair.second.cpu->_GetDirectAddr(), 0x1015, "Returned address was %i but was expecting 0x1015."); -// cr_assert_eq(pair.second.cpu->_registers.pac, 0x808001); -//} \ No newline at end of file +Test(AddrMode, Direct) +{ + auto pair = Init(); + pair.second.cartridge->_data[0] = 0x15; + pair.second.cpu->_registers.pac = 0x808000; + pair.second.cpu->_registers.d = 0x1000; + cr_assert_eq(pair.second.cpu->_getDirectAddr(), 0x1015, "Returned address was %x but was expecting 0x1015.", pair.second.cpu->_getDirectAddr()); + cr_assert_eq(pair.second.cpu->_registers.pac, 0x808001); +} + +Test(AddrMode, Absolute) +{ + auto pair = Init(); + pair.second.cartridge->_data[0] = 0x1C; + pair.second.cartridge->_data[1] = 0x90; + pair.second.cpu->_registers.pac = 0x808000; + pair.second.cpu->_registers.dbr = 0x88; + cr_assert_eq(pair.second.cpu->_getAbsoluteAddr(), 0x88901C, "Returned address was %x but was expecting 0x88901C.", pair.second.cpu->_getAbsoluteAddr()); + cr_assert_eq(pair.second.cpu->_registers.pac, 0x808002); +} + +Test(AddrMode, AbsoluteLong) +{ + auto pair = Init(); + pair.second.cartridge->_data[0] = 0x1C; + pair.second.cartridge->_data[1] = 0x90; + pair.second.cartridge->_data[2] = 0xFF; + pair.second.cpu->_registers.pac = 0x808000; + pair.second.cpu->_registers.dbr = 0x88; + cr_assert_eq(pair.second.cpu->_getAbsoluteLongAddr(), 0xFF901C, "Returned address was %x but was expecting 0xFF901C.", pair.second.cpu->_getAbsoluteLongAddr()); + cr_assert_eq(pair.second.cpu->_registers.pac, 0x808003); +} + +Test(AddrMode, DirectIndirectIndexed) +{ + auto pair = Init(); + pair.second.cartridge->_data[0] = 0x10; + pair.second.wram->_data[0x1010] = 0x30; + pair.second.wram->_data[0x1011] = 0x40; + pair.second.cpu->_registers.pac = 0x808000; + pair.second.cpu->_registers.dbr = 0x80; + pair.second.cpu->_registers.y = 0x0001; + pair.second.cpu->_registers.d = 0x1000; + cr_assert_eq(pair.second.cpu->_getDirectIndirectIndexedYAddr(), 0x804031, "Returned address was %x but was expecting 0x804031.", + pair.second.cpu->_getDirectIndirectIndexedYAddr()); + cr_assert_eq(pair.second.cpu->_registers.pac, 0x808001); +} + +Test(AddrMode, DirectIndirectIndexedLong) +{ + auto pair = Init(); + pair.second.cpu->_registers.pac = 0x808000; + pair.second.cpu->_registers.d = 0x1000; + pair.second.cartridge->_data[0] = 0x10; + pair.second.wram->_data[0x1010] = 0x30; + pair.second.wram->_data[0x1011] = 0x40; + pair.second.wram->_data[0x1012] = 0x23; + cr_assert_eq(pair.second.cpu->_getDirectIndirectIndexedYLongAddr(), 0x234030, "Returned address was %x but was expecting 0x234030.", + pair.second.cpu->_getDirectIndirectIndexedYLongAddr()); + cr_assert_eq(pair.second.cpu->_registers.pac, 0x808001); +} + +Test(AddrMode, DirectIndexedIndirect) +{ + auto pair = Init(); + pair.second.cartridge->_data[0] = 0x10; + pair.second.cpu->_registers.d = 0x1000; + pair.second.cpu->_registers.x = 0x0002; + pair.second.wram->_data[0x1012] = 0x30; + pair.second.wram->_data[0x1013] = 0x40; + pair.second.cpu->_registers.dbr = 0x80; + pair.second.cpu->_registers.pac = 0x808000; + cr_assert_eq(pair.second.cpu->_getDirectIndirectIndexedXAddr(), 0x804030, "Returned address was %x but was expecting 0x804030.", + pair.second.cpu->_getDirectIndirectIndexedXAddr()); + cr_assert_eq(pair.second.cpu->_registers.pac, 0x808001); +} + +Test(AddrMode, DirectIndexedByX) +{ + auto pair = Init(); + pair.second.cartridge->_data[0] = 0x10; + pair.second.cpu->_registers.d = 0x1000; + pair.second.cpu->_registers.x = 0x0002; + pair.second.cpu->_registers.pac = 0x808000; + cr_assert_eq(pair.second.cpu->_getDirectIndexedByXAddr(), 0x1012, "Returned address was %x but was expecting 0x1012.", pair.second.cpu->_getDirectIndexedByXAddr()); + cr_assert_eq(pair.second.cpu->_registers.pac, 0x808001); +} + +Test(AddrMode, DirectIndexedByY) +{ + auto pair = Init(); + pair.second.cartridge->_data[0] = 0x10; + pair.second.cpu->_registers.d = 0x1000; + pair.second.cpu->_registers.y = 0x0002; + pair.second.cpu->_registers.pac = 0x808000; + cr_assert_eq(pair.second.cpu->_getDirectIndexedByYAddr(), 0x1012, "Returned address was %x but was expecting 0x1012.", pair.second.cpu->_getDirectIndexedByYAddr()); + cr_assert_eq(pair.second.cpu->_registers.pac, 0x808001); +} + +Test(AddrMode, AbsoluteIndexByX) +{ + auto pair = Init(); + pair.second.cpu->_registers.pac = 0x808000; + pair.second.cartridge->_data[0] = 0x10; + pair.second.cartridge->_data[1] = 0xAC; + pair.second.cpu->_registers.dbr = 0xEF; + pair.second.cpu->_registers.x = 0x0005; + cr_assert_eq(pair.second.cpu->_getAbsoluteIndexedByXAddr(), 0xEFAC15, "Returned address was %x but was expecting 0xEFAC15.", pair.second.cpu->_getAbsoluteIndexedByXAddr()); + cr_assert_eq(pair.second.cpu->_registers.pac, 0x808002); +} + +Test(AddrMode, AbsoluteIndexByY) +{ + auto pair = Init(); + pair.second.cpu->_registers.pac = 0x808000; + pair.second.cartridge->_data[0] = 0x10; + pair.second.cartridge->_data[1] = 0xAC; + pair.second.cpu->_registers.dbr = 0xEF; + pair.second.cpu->_registers.y = 0x0005; + cr_assert_eq(pair.second.cpu->_getAbsoluteIndexedByYAddr(), 0xEFAC15, "Returned address was %x but was expecting 0xEFAC15.", pair.second.cpu->_getAbsoluteIndexedByYAddr()); + cr_assert_eq(pair.second.cpu->_registers.pac, 0x808002); +} + +Test(AddrMode, AbsoluteLongIndexByX) +{ + auto pair = Init(); + pair.second.cpu->_registers.pac = 0x808000; + pair.second.cartridge->_data[0] = 0x10; + pair.second.cartridge->_data[1] = 0xAC; + pair.second.cartridge->_data[2] = 0xEF; + pair.second.cpu->_registers.x = 0x0005; + cr_assert_eq(pair.second.cpu->_getAbsoluteIndexedByXLongAddr(), 0xEFAC15, "Returned address was %x but was expecting 0xEFAC15.", + pair.second.cpu->_getAbsoluteIndexedByXLongAddr()); + cr_assert_eq(pair.second.cpu->_registers.pac, 0x808003); +} + +Test(AddrMode, ProgramCounterRelativePositive) +{ + auto pair = Init(); + pair.second.cpu->_registers.pac = 0x808010; + pair.second.cartridge->_data[0x10] = 0x15; + cr_assert_eq(pair.second.cpu->_getProgramCounterRelativeAddr(), 0x808025, "Returned address was %x but was expecting 0x808025.", pair.second.cpu->_getProgramCounterRelativeAddr()); + cr_assert_eq(pair.second.cpu->_registers.pac, 0x808011); +} + +Test(AddrMode, ProgramCounterRelativeNegative) +{ + auto pair = Init(); + pair.second.cpu->_registers.pac = 0x808010; + pair.second.cartridge->_data[0x10] = -0x15; + cr_assert_eq(pair.second.cpu->_getProgramCounterRelativeAddr(), 0x807FFB, "Returned address was %x but was expecting 0x807FFB.", pair.second.cpu->_getProgramCounterRelativeAddr()); + cr_assert_eq(pair.second.cpu->_registers.pac, 0x808011); +} + +Test(AddrMode, ProgramCounterRelativeLongPositive) +{ + auto pair = Init(); + pair.second.cpu->_registers.pac = 0x808010; + pair.second.cartridge->_data[0x10] = 0x15; + pair.second.cartridge->_data[0x11] = 0x10; + auto addr = pair.second.cpu->_getProgramCounterRelativeLongAddr(); + cr_assert_eq(addr, 0x809025, "Returned address was %x but was expecting 0x809025.", addr); + cr_assert_eq(pair.second.cpu->_registers.pac, 0x808012); +} + +Test(AddrMode, ProgramCounterRelativeLongNegative) +{ + auto pair = Init(); + pair.second.cpu->_registers.pac = 0x808010; + pair.second.cartridge->_data[0x10] = 0x10; + pair.second.cartridge->_data[0x11] = -0x15; + auto addr = pair.second.cpu->_getProgramCounterRelativeLongAddr(); + cr_assert_eq(addr, 0x806B00, "Returned address was %x but was expecting 0x806B00.", addr); + cr_assert_eq(pair.second.cpu->_registers.pac, 0x808012); +} + +Test(AddrMode, AbsoluteIndirect) +{ + auto pair = Init(); + pair.second.cpu->_registers.pac = 0x808000; + pair.second.cartridge->_data[0] = 0xAB; + pair.second.cartridge->_data[1] = 0x01; + pair.second.wram->_data[0x01AB] = 0xEF; + pair.second.wram->_data[0x01AC] = 0x01; + auto addr = pair.second.cpu->_getAbsoluteIndirectAddr(); + cr_assert_eq(addr, 0x01EF, "Returned address was %x but was expecting 0x01EF.", addr); + cr_assert_eq(pair.second.cpu->_registers.pac, 0x808002); +} + +Test(AddrMode, AbsoluteIndexedIndirect) +{ + auto pair = Init(); + pair.second.cpu->_registers.pac = 0x808000; + pair.second.cartridge->_data[0] = 0xAB; + pair.second.cartridge->_data[1] = 0x01; + pair.second.cpu->_registers.x = 2; + pair.second.wram->_data[0x01AD] = 0xEF; + pair.second.wram->_data[0x01AE] = 0x01; + auto addr = pair.second.cpu->_getAbsoluteIndexedIndirectAddr(); + cr_assert_eq(addr, 0x01EF, "Returned address was %x but was expecting 0x01EF.", addr); + cr_assert_eq(pair.second.cpu->_registers.pac, 0x808002); +} + +Test(AddrMode, DirectIndirect) +{ + auto pair = Init(); + pair.second.cpu->_registers.pac = 0x808000; + pair.second.cartridge->_data[0] = 0x01; + pair.second.cpu->_registers.d = 0x1010; + pair.second.wram->_data[0x1011] = 0xEF; + pair.second.wram->_data[0x1012] = 0x01; + pair.second.cpu->_registers.dbr = 0x88; + auto addr = pair.second.cpu->_getDirectIndirectAddr(); + cr_assert_eq(addr, 0x8801EF, "Returned address was %x but was expecting 0x8801EF.", addr); + cr_assert_eq(pair.second.cpu->_registers.pac, 0x808001); +} + +Test(AddrMode, DirectIndirectLong) +{ + auto pair = Init(); + pair.second.cpu->_registers.pac = 0x808000; + pair.second.cartridge->_data[0] = 0x06; + pair.second.cpu->_registers.d = 0x1010; + pair.second.wram->_data[0x1016] = 0xEF; + pair.second.wram->_data[0x1017] = 0x01; + pair.second.wram->_data[0x1018] = 0x88; + auto addr = pair.second.cpu->_getDirectIndirectLongAddr(); + cr_assert_eq(addr, 0x8801EF, "Returned address was %x but was expecting 0x8801EF.", addr); + cr_assert_eq(pair.second.cpu->_registers.pac, 0x808001); +} + +Test(AddrMode, StackRelative) +{ + auto pair = Init(); + pair.second.cpu->_registers.pac = 0x808000; + pair.second.cartridge->_data[0] = 0x06; + pair.second.cpu->_registers.s = 0x1010; + auto addr = pair.second.cpu->_getStackRelativeAddr(); + cr_assert_eq(addr, 0x1016, "Returned address was %x but was expecting 0x1016.", addr); + cr_assert_eq(pair.second.cpu->_registers.pac, 0x808001); +} + +Test(AddrMode, StackRelativeIndirectIndexed) +{ + auto pair = Init(); + pair.second.cpu->_registers.pac = 0x808000; + pair.second.cartridge->_data[0] = 0x06; + pair.second.cpu->_registers.s = 0x1010; + pair.second.cpu->_registers.y = 0x5; + pair.second.cpu->_registers.dbr = 0x88; + auto addr = pair.second.cpu->_getStackRelativeIndirectIndexedYAddr(); + cr_assert_eq(addr, 0x88101B, "Returned address was %x but was expecting 0x88101B.", addr); + cr_assert_eq(pair.second.cpu->_registers.pac, 0x808001); +} \ No newline at end of file diff --git a/tests/testMemoryBus.cpp b/tests/testMemoryBus.cpp index cf7dc38..ed9488d 100644 --- a/tests/testMemoryBus.cpp +++ b/tests/testMemoryBus.cpp @@ -45,9 +45,30 @@ Test(BusAccessor, GetWramEnd) Test(BusAccessor, GetWramMirror) { auto pair = Init(); - std::shared_ptr accessor = nullptr; + std::shared_ptr accessor = nullptr; - accessor = std::static_pointer_cast(pair.first->getAccessor(0x2F11FF)); + accessor = std::static_pointer_cast(pair.first->getAccessor(0x2F11FF)); + cr_assert_neq(accessor, nullptr); + cr_assert_eq(accessor->_initial.get(), pair.second.wram.get()); +} + +Test(BusAccessor, GetWramMirror2) +{ + auto pair = Init(); + std::shared_ptr accessor = nullptr; + + accessor = std::static_pointer_cast(pair.first->getAccessor(0x100000)); + cr_assert_neq(accessor, nullptr); + cr_assert_eq(accessor->_initial.get(), pair.second.wram.get()); +} + +Test(BusAccessor, GetWramMirror3) +{ + auto pair = Init(); + std::shared_ptr accessor = nullptr; + + accessor = std::static_pointer_cast(pair.first->getAccessor(0x1010)); + cr_assert_neq(accessor, nullptr); cr_assert_eq(accessor->_initial.get(), pair.second.wram.get()); } @@ -123,6 +144,15 @@ Test(BusAccessor, GetAPUMirror) cr_assert_eq(accessor->_initial.get(), pair.second.apu.get()); } +Test(BusAccessor, GetAPUMirrorFirstHalf) +{ + auto pair = Init(); + std::shared_ptr accessor = nullptr; + + accessor = std::static_pointer_cast(pair.first->getAccessor(0x052143)); + cr_assert_eq(accessor->_initial.get(), pair.second.apu.get()); +} + Test(BusAccessor, GetCPUStart) { auto pair = Init(); @@ -337,6 +367,27 @@ Test(BusRead, ReadWRAM) cr_assert_eq(data, 123); } +Test(BusRead, ReadWRAM2) +{ + auto pair = Init(); + uint8_t data; + + pair.second.wram->_data[0x1010] = 123; + data = pair.first->read(0x7E1010); + cr_assert_eq(data, 123); +} + + +Test(BusRead, ReadWRAMMirror) +{ + auto pair = Init(); + uint8_t data; + + pair.second.wram->_data[0x1010] = 123; + data = pair.first->read(0x1010); + cr_assert_eq(data, 123); +} + //////////////////////////// // // // MemoryBus::write tests // diff --git a/tests/tests.cpp b/tests/tests.cpp index f6ff907..c9a3bd7 100644 --- a/tests/tests.cpp +++ b/tests/tests.cpp @@ -19,8 +19,8 @@ std::pair, SNES> Init() snes.cartridge->_size = 100; snes.cartridge->_data = new uint8_t[snes.cartridge->_size]; snes.cartridge->header.mappingMode = Cartridge::LoRom; - snes.sram->_size = 10; + snes.sram->_size = 100; snes.sram->_data = new uint8_t[snes.cartridge->_size]; - bus->mapComponents(snes); +// bus->mapComponents(snes); return std::make_pair(bus, snes); } \ No newline at end of file