mirror of
https://github.com/zoriya/ComSquare.git
synced 2026-05-23 14:58:36 +00:00
Merge branch 'master' of https://github.com/AnonymusRaccoon/ComSquare into PPU
This commit is contained in:
@@ -4,7 +4,6 @@
|
||||
|
||||
#include <iostream>
|
||||
#include <string>
|
||||
#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<Memory::MemoryBus>(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<Memory::MemoryBus>(), 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;
|
||||
}
|
||||
+2
-2
@@ -47,8 +47,8 @@ namespace ComSquare::APU
|
||||
}
|
||||
}
|
||||
|
||||
bool APU::update()
|
||||
void APU::update(unsigned cycles)
|
||||
{
|
||||
throw NotImplementedException();
|
||||
(void)cycles;
|
||||
}
|
||||
}
|
||||
|
||||
+1
-1
@@ -125,7 +125,7 @@ namespace ComSquare::APU
|
||||
//! @param data The new value of the register.
|
||||
//! @throw InvalidAddress will be thrown if the address is more than $FF (the number of register).
|
||||
void write(uint24_t addr, uint8_t data) override;
|
||||
bool update();
|
||||
void update(unsigned cycles);
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
+163
-27
@@ -5,6 +5,7 @@
|
||||
#include "CPU.hpp"
|
||||
|
||||
#include <utility>
|
||||
#include <iostream>
|
||||
#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<char>(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;
|
||||
}
|
||||
}
|
||||
+64
-8
@@ -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 <long exp> 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 <abs exp> to form the effective address.
|
||||
uint24_t _getAbsoluteIndirectAddr();
|
||||
//! @brief The <abs exp> 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<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();
|
||||
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).
|
||||
|
||||
@@ -6,8 +6,10 @@
|
||||
|
||||
namespace ComSquare::CPU
|
||||
{
|
||||
int CPU::ADC()
|
||||
int CPU::ADC(uint24_t valueAddr)
|
||||
{
|
||||
// this->_registers.a +=
|
||||
(void)valueAddr;
|
||||
return (0);
|
||||
}
|
||||
}
|
||||
@@ -7,19 +7,19 @@
|
||||
|
||||
#include <exception>
|
||||
#include <string>
|
||||
#include <ios>
|
||||
#include <sstream>
|
||||
|
||||
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(); }
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
// Created by anonymus-raccoon on 1/29/20.
|
||||
//
|
||||
|
||||
#include <iostream>
|
||||
#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;
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
#include "RectangleShadow.hpp"
|
||||
|
||||
#include <utility>
|
||||
#include <iostream>
|
||||
|
||||
namespace ComSquare::Memory
|
||||
{
|
||||
|
||||
+1
-1
@@ -66,7 +66,7 @@ namespace ComSquare::PPU
|
||||
}
|
||||
}
|
||||
|
||||
void PPU::update(int cycles)
|
||||
void PPU::update(unsigned cycles)
|
||||
{
|
||||
(void)cycles;
|
||||
}
|
||||
|
||||
+1
-1
@@ -425,7 +425,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
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
|
||||
#include <criterion/criterion.h>
|
||||
#include <bitset>
|
||||
#include <iostream>
|
||||
#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);
|
||||
//}
|
||||
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);
|
||||
}
|
||||
+53
-2
@@ -45,9 +45,30 @@ Test(BusAccessor, GetWramEnd)
|
||||
Test(BusAccessor, GetWramMirror)
|
||||
{
|
||||
auto pair = Init();
|
||||
std::shared_ptr<Memory::MemoryShadow> accessor = nullptr;
|
||||
std::shared_ptr<Memory::RectangleShadow> accessor = nullptr;
|
||||
|
||||
accessor = std::static_pointer_cast<Memory::MemoryShadow>(pair.first->getAccessor(0x2F11FF));
|
||||
accessor = std::static_pointer_cast<Memory::RectangleShadow>(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<Memory::RectangleShadow> accessor = nullptr;
|
||||
|
||||
accessor = std::static_pointer_cast<Memory::RectangleShadow>(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<Memory::RectangleShadow> accessor = nullptr;
|
||||
|
||||
accessor = std::static_pointer_cast<Memory::RectangleShadow>(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<Memory::MemoryShadow> accessor = nullptr;
|
||||
|
||||
accessor = std::static_pointer_cast<Memory::MemoryShadow>(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 //
|
||||
|
||||
+2
-2
@@ -19,8 +19,8 @@ std::pair<std::shared_ptr<Memory::MemoryBus>, 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);
|
||||
}
|
||||
Reference in New Issue
Block a user