get rid of memory.cc/.hh
also fix bus' shared pointer in cpu TODO: put cpu in bus not the other way around Signed-off-by: Amneesh Singh <natto@weirdnatto.in>
This commit is contained in:
		@@ -1,8 +1,8 @@
 | 
			
		||||
#include "bus.hh"
 | 
			
		||||
#include "cpu/cpu.hh"
 | 
			
		||||
#include "memory.hh"
 | 
			
		||||
#include "util/loglevel.hh"
 | 
			
		||||
#include <array>
 | 
			
		||||
#include <chrono>
 | 
			
		||||
#include <cstdlib>
 | 
			
		||||
#include <fstream>
 | 
			
		||||
#include <iostream>
 | 
			
		||||
@@ -15,7 +15,7 @@
 | 
			
		||||
int
 | 
			
		||||
main(int argc, const char* argv[]) {
 | 
			
		||||
    std::vector<uint8_t> rom;
 | 
			
		||||
    std::array<uint8_t, matar::Memory::BIOS_SIZE> bios = { 0 };
 | 
			
		||||
    std::array<uint8_t, matar::Bus::BIOS_SIZE> bios = { 0 };
 | 
			
		||||
 | 
			
		||||
    auto usage = [argv]() {
 | 
			
		||||
        std::cerr << "Usage: " << argv[0] << " <file> [-b <bios>]" << std::endl;
 | 
			
		||||
@@ -65,7 +65,7 @@ main(int argc, const char* argv[]) {
 | 
			
		||||
        ifile.seekg(0, std::ios::end);
 | 
			
		||||
        bios_size = ifile.tellg();
 | 
			
		||||
 | 
			
		||||
        if (bios_size != matar::Memory::BIOS_SIZE) {
 | 
			
		||||
        if (bios_size != matar::Bus::BIOS_SIZE) {
 | 
			
		||||
            throw std::ios::failure("BIOS file has invalid size",
 | 
			
		||||
                                    std::error_code());
 | 
			
		||||
        }
 | 
			
		||||
@@ -87,8 +87,8 @@ main(int argc, const char* argv[]) {
 | 
			
		||||
    matar::set_log_level(matar::LogLevel::Debug);
 | 
			
		||||
 | 
			
		||||
    try {
 | 
			
		||||
        matar::Memory memory(std::move(bios), std::move(rom));
 | 
			
		||||
        matar::Bus bus(memory);
 | 
			
		||||
        std::shared_ptr<matar::Bus> bus(
 | 
			
		||||
          new matar::Bus(std::move(bios), std::move(rom)));
 | 
			
		||||
        matar::Cpu cpu(bus);
 | 
			
		||||
        while (true) {
 | 
			
		||||
            cpu.step();
 | 
			
		||||
 
 | 
			
		||||
@@ -1,13 +1,16 @@
 | 
			
		||||
#pragma once
 | 
			
		||||
 | 
			
		||||
#include "header.hh"
 | 
			
		||||
#include "io/io.hh"
 | 
			
		||||
#include "memory.hh"
 | 
			
		||||
#include <memory>
 | 
			
		||||
#include <optional>
 | 
			
		||||
#include <span>
 | 
			
		||||
#include <vector>
 | 
			
		||||
 | 
			
		||||
namespace matar {
 | 
			
		||||
class Bus {
 | 
			
		||||
  public:
 | 
			
		||||
    Bus(const Memory& memory);
 | 
			
		||||
    static constexpr uint32_t BIOS_SIZE = 1024 * 16;
 | 
			
		||||
    Bus(std::array<uint8_t, BIOS_SIZE>&& bios, std::vector<uint8_t>&& rom);
 | 
			
		||||
 | 
			
		||||
    uint8_t read_byte(uint32_t address);
 | 
			
		||||
    void write_byte(uint32_t address, uint8_t byte);
 | 
			
		||||
@@ -19,7 +22,48 @@ class Bus {
 | 
			
		||||
    void write_word(uint32_t address, uint32_t word);
 | 
			
		||||
 | 
			
		||||
  private:
 | 
			
		||||
    template<unsigned int N>
 | 
			
		||||
    std::optional<std::span<const uint8_t>> read(uint32_t address) const;
 | 
			
		||||
 | 
			
		||||
    template<unsigned int N>
 | 
			
		||||
    std::optional<std::span<uint8_t>> write(uint32_t address);
 | 
			
		||||
 | 
			
		||||
#define MEMORY_REGION(name, start)                                             \
 | 
			
		||||
    static constexpr uint32_t name##_START = start;
 | 
			
		||||
 | 
			
		||||
#define DECL_MEMORY(name, ident, start, end)                                   \
 | 
			
		||||
    MEMORY_REGION(name, start)                                                 \
 | 
			
		||||
    std::array<uint8_t, end - start + 1> ident;
 | 
			
		||||
 | 
			
		||||
    MEMORY_REGION(BIOS, 0x00000000)
 | 
			
		||||
    std::array<uint8_t, BIOS_SIZE> bios;
 | 
			
		||||
 | 
			
		||||
    // board working RAM
 | 
			
		||||
    DECL_MEMORY(BOARD_WRAM, board_wram, 0x02000000, 0x0203FFFF)
 | 
			
		||||
 | 
			
		||||
    // chip working RAM
 | 
			
		||||
    DECL_MEMORY(CHIP_WRAM, chip_wram, 0x03000000, 0x03007FFF)
 | 
			
		||||
 | 
			
		||||
    // palette RAM
 | 
			
		||||
    DECL_MEMORY(PALETTE_RAM, palette_ram, 0x05000000, 0x050003FF)
 | 
			
		||||
 | 
			
		||||
    // video RAM
 | 
			
		||||
    DECL_MEMORY(VRAM, vram, 0x06000000, 0x06017FFF)
 | 
			
		||||
 | 
			
		||||
    // OAM OBJ attributes
 | 
			
		||||
    DECL_MEMORY(OAM_OBJ_ATTR, oam_obj_attr, 0x07000000, 0x070003FF)
 | 
			
		||||
 | 
			
		||||
#undef DECL_MEMORY
 | 
			
		||||
 | 
			
		||||
    MEMORY_REGION(ROM_0, 0x08000000)
 | 
			
		||||
    MEMORY_REGION(ROM_1, 0x0A000000)
 | 
			
		||||
    MEMORY_REGION(ROM_2, 0x0C000000)
 | 
			
		||||
 | 
			
		||||
#undef MEMORY_REGION
 | 
			
		||||
    std::vector<uint8_t> rom;
 | 
			
		||||
    Header header;
 | 
			
		||||
    void parse_header();
 | 
			
		||||
 | 
			
		||||
    IoDevices io;
 | 
			
		||||
    std::shared_ptr<Memory> memory;
 | 
			
		||||
};
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -10,7 +10,7 @@
 | 
			
		||||
namespace matar {
 | 
			
		||||
class Cpu {
 | 
			
		||||
  public:
 | 
			
		||||
    Cpu(const Bus& bus) noexcept;
 | 
			
		||||
    Cpu(std::shared_ptr<Bus>) noexcept;
 | 
			
		||||
 | 
			
		||||
    void step();
 | 
			
		||||
    void chg_mode(const Mode to);
 | 
			
		||||
 
 | 
			
		||||
@@ -1,58 +0,0 @@
 | 
			
		||||
#pragma once
 | 
			
		||||
 | 
			
		||||
#include "header.hh"
 | 
			
		||||
#include <array>
 | 
			
		||||
#include <cstddef>
 | 
			
		||||
#include <cstdint>
 | 
			
		||||
#include <unordered_map>
 | 
			
		||||
#include <vector>
 | 
			
		||||
 | 
			
		||||
namespace matar {
 | 
			
		||||
class Memory {
 | 
			
		||||
  public:
 | 
			
		||||
    static constexpr uint32_t BIOS_SIZE = 1024 * 16;
 | 
			
		||||
 | 
			
		||||
    Memory(std::array<uint8_t, BIOS_SIZE>&& bios, std::vector<uint8_t>&& rom);
 | 
			
		||||
 | 
			
		||||
    uint8_t read(uint32_t address) const;
 | 
			
		||||
    void write(uint32_t address, uint8_t byte);
 | 
			
		||||
 | 
			
		||||
  private:
 | 
			
		||||
#define MEMORY_REGION(name, start)                                             \
 | 
			
		||||
    static constexpr uint32_t name##_START = start;
 | 
			
		||||
 | 
			
		||||
#define DECL_MEMORY(name, ident, start, end)                                   \
 | 
			
		||||
    MEMORY_REGION(name, start)                                                 \
 | 
			
		||||
    std::array<uint8_t, end - start + 1> ident;
 | 
			
		||||
 | 
			
		||||
    MEMORY_REGION(BIOS, 0x00000000)
 | 
			
		||||
    std::array<uint8_t, BIOS_SIZE> bios;
 | 
			
		||||
 | 
			
		||||
    // board working RAM
 | 
			
		||||
    DECL_MEMORY(BOARD_WRAM, board_wram, 0x02000000, 0x0203FFFF)
 | 
			
		||||
 | 
			
		||||
    // chip working RAM
 | 
			
		||||
    DECL_MEMORY(CHIP_WRAM, chip_wram, 0x03000000, 0x03007FFF)
 | 
			
		||||
 | 
			
		||||
    // palette RAM
 | 
			
		||||
    DECL_MEMORY(PALETTE_RAM, palette_ram, 0x05000000, 0x050003FF)
 | 
			
		||||
 | 
			
		||||
    // video RAM
 | 
			
		||||
    DECL_MEMORY(VRAM, vram, 0x06000000, 0x06017FFF)
 | 
			
		||||
 | 
			
		||||
    // OAM OBJ attributes
 | 
			
		||||
    DECL_MEMORY(OAM_OBJ_ATTR, oam_obj_attr, 0x07000000, 0x070003FF)
 | 
			
		||||
 | 
			
		||||
#undef DECL_MEMORY
 | 
			
		||||
 | 
			
		||||
    MEMORY_REGION(ROM_0, 0x08000000)
 | 
			
		||||
    MEMORY_REGION(ROM_1, 0x0A000000)
 | 
			
		||||
    MEMORY_REGION(ROM_2, 0x0C000000)
 | 
			
		||||
 | 
			
		||||
#undef MEMORY_REGION
 | 
			
		||||
    std::unordered_map<uint32_t, uint8_t> invalid_mem;
 | 
			
		||||
    std::vector<uint8_t> rom;
 | 
			
		||||
    Header header;
 | 
			
		||||
    void parse_header();
 | 
			
		||||
};
 | 
			
		||||
}
 | 
			
		||||
@@ -1,5 +1,4 @@
 | 
			
		||||
headers = files(
 | 
			
		||||
  'memory.hh',
 | 
			
		||||
  'bus.hh',
 | 
			
		||||
  'header.hh',
 | 
			
		||||
)
 | 
			
		||||
@@ -10,4 +9,4 @@ subdir('cpu')
 | 
			
		||||
subdir('util')
 | 
			
		||||
subdir('io')
 | 
			
		||||
 | 
			
		||||
install_headers(headers, subdir: meson.project_name(), preserve_path: true)
 | 
			
		||||
install_headers(headers, subdir: meson.project_name(), preserve_path: true)
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										218
									
								
								src/bus.cc
									
									
									
									
									
								
							
							
						
						
									
										218
									
								
								src/bus.cc
									
									
									
									
									
								
							@@ -1,21 +1,87 @@
 | 
			
		||||
#include "bus.hh"
 | 
			
		||||
#include "util/crypto.hh"
 | 
			
		||||
#include "util/log.hh"
 | 
			
		||||
#include <memory>
 | 
			
		||||
 | 
			
		||||
namespace matar {
 | 
			
		||||
 | 
			
		||||
static constexpr uint32_t IO_START = 0x4000000;
 | 
			
		||||
static constexpr uint32_t IO_END   = 0x40003FE;
 | 
			
		||||
 | 
			
		||||
Bus::Bus(const Memory& memory)
 | 
			
		||||
  : memory(std::make_shared<Memory>(memory)) {}
 | 
			
		||||
Bus::Bus(std::array<uint8_t, BIOS_SIZE>&& bios, std::vector<uint8_t>&& rom)
 | 
			
		||||
  : bios(std::move(bios))
 | 
			
		||||
  , board_wram({ 0 })
 | 
			
		||||
  , chip_wram({ 0 })
 | 
			
		||||
  , palette_ram({ 0 })
 | 
			
		||||
  , vram({ 0 })
 | 
			
		||||
  , oam_obj_attr({ 0 })
 | 
			
		||||
  , rom(std::move(rom)) {
 | 
			
		||||
    std::string bios_hash = crypto::sha256(this->bios);
 | 
			
		||||
    static constexpr std::string_view expected_hash =
 | 
			
		||||
      "fd2547724b505f487e6dcb29ec2ecff3af35a841a77ab2e85fd87350abd36570";
 | 
			
		||||
 | 
			
		||||
    if (bios_hash != expected_hash) {
 | 
			
		||||
        glogger.warn("BIOS hash failed to match, run at your own risk"
 | 
			
		||||
                     "\nExpected : {} "
 | 
			
		||||
                     "\nGot      : {}",
 | 
			
		||||
                     expected_hash,
 | 
			
		||||
                     bios_hash);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    parse_header();
 | 
			
		||||
 | 
			
		||||
    glogger.info("Memory successfully initialised");
 | 
			
		||||
    glogger.info("Cartridge Title: {}", header.title);
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
template<unsigned int N>
 | 
			
		||||
std::optional<std::span<const uint8_t>>
 | 
			
		||||
Bus::read(uint32_t address) const {
 | 
			
		||||
#define MATCHES(AREA, area)                                                    \
 | 
			
		||||
    if (address >= AREA##_START && address < AREA##_START + area.size())       \
 | 
			
		||||
        return std::span<const uint8_t>(&area[address - AREA##_START], N);
 | 
			
		||||
 | 
			
		||||
    MATCHES(BIOS, bios)
 | 
			
		||||
    MATCHES(BOARD_WRAM, board_wram)
 | 
			
		||||
    MATCHES(CHIP_WRAM, chip_wram)
 | 
			
		||||
    MATCHES(PALETTE_RAM, palette_ram)
 | 
			
		||||
    MATCHES(VRAM, vram)
 | 
			
		||||
    MATCHES(OAM_OBJ_ATTR, oam_obj_attr)
 | 
			
		||||
    MATCHES(ROM_0, rom)
 | 
			
		||||
    MATCHES(ROM_1, rom)
 | 
			
		||||
    MATCHES(ROM_2, rom)
 | 
			
		||||
 | 
			
		||||
#undef MATCHES
 | 
			
		||||
 | 
			
		||||
    glogger.error("Invalid memory region read");
 | 
			
		||||
    return {};
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
template<unsigned int N>
 | 
			
		||||
std::optional<std::span<uint8_t>>
 | 
			
		||||
Bus::write(uint32_t address) {
 | 
			
		||||
#define MATCHES(AREA, area)                                                    \
 | 
			
		||||
    if (address >= AREA##_START && address < AREA##_START + area.size())       \
 | 
			
		||||
        return std::span<uint8_t>(&area[address - AREA##_START], N);
 | 
			
		||||
 | 
			
		||||
    MATCHES(BOARD_WRAM, board_wram)
 | 
			
		||||
    MATCHES(CHIP_WRAM, chip_wram)
 | 
			
		||||
    MATCHES(PALETTE_RAM, palette_ram)
 | 
			
		||||
    MATCHES(VRAM, vram)
 | 
			
		||||
    MATCHES(OAM_OBJ_ATTR, oam_obj_attr)
 | 
			
		||||
 | 
			
		||||
#undef MATCHES
 | 
			
		||||
 | 
			
		||||
    glogger.error("Invalid memory region written");
 | 
			
		||||
    return {};
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
uint8_t
 | 
			
		||||
Bus::read_byte(uint32_t address) {
 | 
			
		||||
    if (address >= IO_START && address <= IO_END)
 | 
			
		||||
        return io.read_byte(address);
 | 
			
		||||
 | 
			
		||||
    return memory->read(address);
 | 
			
		||||
    auto data = read<1>(address);
 | 
			
		||||
    return data.transform([](auto value) { return value[0]; }).value_or(0xFF);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void
 | 
			
		||||
@@ -25,7 +91,10 @@ Bus::write_byte(uint32_t address, uint8_t byte) {
 | 
			
		||||
        return;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    memory->write(address, byte);
 | 
			
		||||
    auto data = write<1>(address);
 | 
			
		||||
 | 
			
		||||
    if (data.has_value())
 | 
			
		||||
        data.value()[0] = byte;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
uint16_t
 | 
			
		||||
@@ -36,7 +105,9 @@ Bus::read_halfword(uint32_t address) {
 | 
			
		||||
    if (address >= IO_START && address <= IO_END)
 | 
			
		||||
        return io.read_halfword(address);
 | 
			
		||||
 | 
			
		||||
    return read_byte(address) | read_byte(address + 1) << 8;
 | 
			
		||||
    return read<2>(address)
 | 
			
		||||
      .transform([](auto value) { return value[0] | value[1] << 8; })
 | 
			
		||||
      .value_or(0xFFFF);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void
 | 
			
		||||
@@ -49,8 +120,13 @@ Bus::write_halfword(uint32_t address, uint16_t halfword) {
 | 
			
		||||
        return;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    write_byte(address, halfword & 0xFF);
 | 
			
		||||
    write_byte(address + 1, halfword >> 8 & 0xFF);
 | 
			
		||||
    auto data = write<2>(address);
 | 
			
		||||
 | 
			
		||||
    if (data.has_value()) {
 | 
			
		||||
        auto value = data.value();
 | 
			
		||||
        value[0]   = halfword & 0xFF;
 | 
			
		||||
        value[1]   = halfword >> 8 & 0xFF;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
uint32_t
 | 
			
		||||
@@ -61,8 +137,11 @@ Bus::read_word(uint32_t address) {
 | 
			
		||||
    if (address >= IO_START && address <= IO_END)
 | 
			
		||||
        return io.read_word(address);
 | 
			
		||||
 | 
			
		||||
    return read_byte(address) | read_byte(address + 1) << 8 |
 | 
			
		||||
           read_byte(address + 2) << 16 | read_byte(address + 3) << 24;
 | 
			
		||||
    return read<4>(address)
 | 
			
		||||
      .transform([](auto value) {
 | 
			
		||||
          return value[0] | value[1] << 8 | value[2] << 16 | value[3] << 24;
 | 
			
		||||
      })
 | 
			
		||||
      .value_or(0xFFFFFFFF);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void
 | 
			
		||||
@@ -75,9 +154,120 @@ Bus::write_word(uint32_t address, uint32_t word) {
 | 
			
		||||
        return;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    write_byte(address, word & 0xFF);
 | 
			
		||||
    write_byte(address + 1, word >> 8 & 0xFF);
 | 
			
		||||
    write_byte(address + 2, word >> 16 & 0xFF);
 | 
			
		||||
    write_byte(address + 3, word >> 24 & 0xFF);
 | 
			
		||||
    auto data = write<4>(address);
 | 
			
		||||
 | 
			
		||||
    if (data.has_value()) {
 | 
			
		||||
        auto value = data.value();
 | 
			
		||||
        value[0]   = word & 0xFF;
 | 
			
		||||
        value[1]   = word >> 8 & 0xFF;
 | 
			
		||||
        value[2]   = word >> 16 & 0xFF;
 | 
			
		||||
        value[3]   = word >> 24 & 0xFF;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void
 | 
			
		||||
Bus::parse_header() {
 | 
			
		||||
    if (rom.size() < header.HEADER_SIZE) {
 | 
			
		||||
        throw std::out_of_range(
 | 
			
		||||
          "ROM is not large enough to even have a header");
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // entrypoint
 | 
			
		||||
    header.entrypoint =
 | 
			
		||||
      rom[0x00] | rom[0x01] << 8 | rom[0x02] << 16 | rom[0x03] << 24;
 | 
			
		||||
 | 
			
		||||
    // nintendo logo
 | 
			
		||||
    if (rom[0x9C] != 0x21)
 | 
			
		||||
        glogger.info("HEADER: BIOS debugger bits not set to 0");
 | 
			
		||||
 | 
			
		||||
    // game info
 | 
			
		||||
    header.title = std::string(&rom[0xA0], &rom[0xA0 + 12]);
 | 
			
		||||
 | 
			
		||||
    switch (rom[0xAC]) {
 | 
			
		||||
        case 'A':
 | 
			
		||||
            header.unique_code = Header::UniqueCode::Old;
 | 
			
		||||
            break;
 | 
			
		||||
        case 'B':
 | 
			
		||||
            header.unique_code = Header::UniqueCode::New;
 | 
			
		||||
            break;
 | 
			
		||||
        case 'C':
 | 
			
		||||
            header.unique_code = Header::UniqueCode::Newer;
 | 
			
		||||
            break;
 | 
			
		||||
        case 'F':
 | 
			
		||||
            header.unique_code = Header::UniqueCode::Famicom;
 | 
			
		||||
            break;
 | 
			
		||||
        case 'K':
 | 
			
		||||
            header.unique_code = Header::UniqueCode::YoshiKoro;
 | 
			
		||||
            break;
 | 
			
		||||
        case 'P':
 | 
			
		||||
            header.unique_code = Header::UniqueCode::Ereader;
 | 
			
		||||
            break;
 | 
			
		||||
        case 'R':
 | 
			
		||||
            header.unique_code = Header::UniqueCode::Warioware;
 | 
			
		||||
            break;
 | 
			
		||||
        case 'U':
 | 
			
		||||
            header.unique_code = Header::UniqueCode::Boktai;
 | 
			
		||||
            break;
 | 
			
		||||
        case 'V':
 | 
			
		||||
            header.unique_code = Header::UniqueCode::DrillDozer;
 | 
			
		||||
            break;
 | 
			
		||||
 | 
			
		||||
        default:
 | 
			
		||||
            glogger.error("HEADER: invalid unique code: {}", rom[0xAC]);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    header.title_code = std::string(&rom[0xAD], &rom[0xAE]);
 | 
			
		||||
 | 
			
		||||
    switch (rom[0xAF]) {
 | 
			
		||||
        case 'J':
 | 
			
		||||
            header.i18n = Header::I18n::Japan;
 | 
			
		||||
            break;
 | 
			
		||||
        case 'P':
 | 
			
		||||
            header.i18n = Header::I18n::Europe;
 | 
			
		||||
            break;
 | 
			
		||||
        case 'F':
 | 
			
		||||
            header.i18n = Header::I18n::French;
 | 
			
		||||
            break;
 | 
			
		||||
        case 'S':
 | 
			
		||||
            header.i18n = Header::I18n::Spanish;
 | 
			
		||||
            break;
 | 
			
		||||
        case 'E':
 | 
			
		||||
            header.i18n = Header::I18n::Usa;
 | 
			
		||||
            break;
 | 
			
		||||
        case 'D':
 | 
			
		||||
            header.i18n = Header::I18n::German;
 | 
			
		||||
            break;
 | 
			
		||||
        case 'I':
 | 
			
		||||
            header.i18n = Header::I18n::Italian;
 | 
			
		||||
            break;
 | 
			
		||||
 | 
			
		||||
        default:
 | 
			
		||||
            glogger.error("HEADER: invalid destination/language: {}",
 | 
			
		||||
                          rom[0xAF]);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (rom[0xB2] != 0x96)
 | 
			
		||||
        glogger.error("HEADER: invalid fixed byte at 0xB2");
 | 
			
		||||
 | 
			
		||||
    for (uint32_t i = 0xB5; i < 0xBC; i++) {
 | 
			
		||||
        if (rom[i] != 0x00)
 | 
			
		||||
            glogger.error("HEADER: invalid fixed bytes at 0xB5");
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    header.version = rom[0xBC];
 | 
			
		||||
 | 
			
		||||
    // checksum
 | 
			
		||||
    {
 | 
			
		||||
        uint32_t i = 0xA0, chk = 0;
 | 
			
		||||
        while (i <= 0xBC)
 | 
			
		||||
            chk -= rom[i++];
 | 
			
		||||
        chk -= 0x19;
 | 
			
		||||
        chk &= 0xFF;
 | 
			
		||||
 | 
			
		||||
        if (chk != rom[0xBD])
 | 
			
		||||
            glogger.error("HEADER: checksum does not match");
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // multiboot not required right now
 | 
			
		||||
}
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -7,8 +7,8 @@
 | 
			
		||||
#include <cstdio>
 | 
			
		||||
 | 
			
		||||
namespace matar {
 | 
			
		||||
Cpu::Cpu(const Bus& bus) noexcept
 | 
			
		||||
  : bus(std::make_shared<Bus>(bus))
 | 
			
		||||
Cpu::Cpu(std::shared_ptr<Bus> bus) noexcept
 | 
			
		||||
  : bus(bus)
 | 
			
		||||
  , gpr({ 0 })
 | 
			
		||||
  , cpsr(0)
 | 
			
		||||
  , spsr(0)
 | 
			
		||||
@@ -19,6 +19,10 @@ Cpu::Cpu(const Bus& bus) noexcept
 | 
			
		||||
    cpsr.set_irq_disabled(true);
 | 
			
		||||
    cpsr.set_fiq_disabled(true);
 | 
			
		||||
    cpsr.set_state(State::Arm);
 | 
			
		||||
    uint32_t a = 4444;
 | 
			
		||||
    dbg(this->bus->read_word(0x2000000));
 | 
			
		||||
    this->bus->write_word(0x2000000, a);
 | 
			
		||||
    dbg(this->bus->read_word(0x2000000));
 | 
			
		||||
    glogger.info("CPU successfully initialised");
 | 
			
		||||
 | 
			
		||||
    // PC always points to two instructions ahead
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										181
									
								
								src/memory.cc
									
									
									
									
									
								
							
							
						
						
									
										181
									
								
								src/memory.cc
									
									
									
									
									
								
							@@ -1,181 +0,0 @@
 | 
			
		||||
#include "memory.hh"
 | 
			
		||||
#include "header.hh"
 | 
			
		||||
#include "util/crypto.hh"
 | 
			
		||||
#include "util/log.hh"
 | 
			
		||||
#include <stdexcept>
 | 
			
		||||
 | 
			
		||||
namespace matar {
 | 
			
		||||
Memory::Memory(std::array<uint8_t, BIOS_SIZE>&& bios,
 | 
			
		||||
               std::vector<uint8_t>&& rom)
 | 
			
		||||
  : bios(std::move(bios))
 | 
			
		||||
  , board_wram({ 0 })
 | 
			
		||||
  , chip_wram({ 0 })
 | 
			
		||||
  , palette_ram({ 0 })
 | 
			
		||||
  , vram({ 0 })
 | 
			
		||||
  , oam_obj_attr({ 0 })
 | 
			
		||||
  , rom(std::move(rom)) {
 | 
			
		||||
    std::string bios_hash = crypto::sha256(this->bios);
 | 
			
		||||
    static constexpr std::string_view expected_hash =
 | 
			
		||||
      "fd2547724b505f487e6dcb29ec2ecff3af35a841a77ab2e85fd87350abd36570";
 | 
			
		||||
 | 
			
		||||
    if (bios_hash != expected_hash) {
 | 
			
		||||
        glogger.warn("BIOS hash failed to match, run at your own risk"
 | 
			
		||||
                     "\nExpected : {} "
 | 
			
		||||
                     "\nGot      : {}",
 | 
			
		||||
                     expected_hash,
 | 
			
		||||
                     bios_hash);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    parse_header();
 | 
			
		||||
 | 
			
		||||
    glogger.info("Memory successfully initialised");
 | 
			
		||||
    glogger.info("Cartridge Title: {}", header.title);
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
uint8_t
 | 
			
		||||
Memory::read(uint32_t address) const {
 | 
			
		||||
#define MATCHES(AREA, area)                                                    \
 | 
			
		||||
    if (address >= AREA##_START && address < AREA##_START + area.size())       \
 | 
			
		||||
        return area[address - AREA##_START];
 | 
			
		||||
 | 
			
		||||
    MATCHES(BIOS, bios)
 | 
			
		||||
    MATCHES(BOARD_WRAM, board_wram)
 | 
			
		||||
    MATCHES(CHIP_WRAM, chip_wram)
 | 
			
		||||
    MATCHES(PALETTE_RAM, palette_ram)
 | 
			
		||||
    MATCHES(VRAM, vram)
 | 
			
		||||
    MATCHES(OAM_OBJ_ATTR, oam_obj_attr)
 | 
			
		||||
    MATCHES(ROM_0, rom)
 | 
			
		||||
    MATCHES(ROM_1, rom)
 | 
			
		||||
    MATCHES(ROM_2, rom)
 | 
			
		||||
 | 
			
		||||
    glogger.error("Invalid memory region accessed");
 | 
			
		||||
    return 0xFF;
 | 
			
		||||
 | 
			
		||||
#undef MATCHES
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void
 | 
			
		||||
Memory::write(uint32_t address, uint8_t byte) {
 | 
			
		||||
#define MATCHES(AREA, area)                                                    \
 | 
			
		||||
    if (address >= AREA##_START && address < AREA##_START + area.size()) {     \
 | 
			
		||||
        area[address - AREA##_START] = byte;                                   \
 | 
			
		||||
        return;                                                                \
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    MATCHES(BOARD_WRAM, board_wram)
 | 
			
		||||
    MATCHES(CHIP_WRAM, chip_wram)
 | 
			
		||||
    MATCHES(PALETTE_RAM, palette_ram)
 | 
			
		||||
    MATCHES(VRAM, vram)
 | 
			
		||||
    MATCHES(OAM_OBJ_ATTR, oam_obj_attr)
 | 
			
		||||
 | 
			
		||||
    glogger.error("Invalid memory region accessed");
 | 
			
		||||
 | 
			
		||||
#undef MATCHES
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void
 | 
			
		||||
Memory::parse_header() {
 | 
			
		||||
    if (rom.size() < header.HEADER_SIZE) {
 | 
			
		||||
        throw std::out_of_range(
 | 
			
		||||
          "ROM is not large enough to even have a header");
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // entrypoint
 | 
			
		||||
    header.entrypoint =
 | 
			
		||||
      rom[0x00] | rom[0x01] << 8 | rom[0x02] << 16 | rom[0x03] << 24;
 | 
			
		||||
 | 
			
		||||
    // nintendo logo
 | 
			
		||||
    if (rom[0x9C] != 0x21)
 | 
			
		||||
        glogger.info("HEADER: BIOS debugger bits not set to 0");
 | 
			
		||||
 | 
			
		||||
    // game info
 | 
			
		||||
    header.title = std::string(&rom[0xA0], &rom[0xA0 + 12]);
 | 
			
		||||
 | 
			
		||||
    switch (rom[0xAC]) {
 | 
			
		||||
        case 'A':
 | 
			
		||||
            header.unique_code = Header::UniqueCode::Old;
 | 
			
		||||
            break;
 | 
			
		||||
        case 'B':
 | 
			
		||||
            header.unique_code = Header::UniqueCode::New;
 | 
			
		||||
            break;
 | 
			
		||||
        case 'C':
 | 
			
		||||
            header.unique_code = Header::UniqueCode::Newer;
 | 
			
		||||
            break;
 | 
			
		||||
        case 'F':
 | 
			
		||||
            header.unique_code = Header::UniqueCode::Famicom;
 | 
			
		||||
            break;
 | 
			
		||||
        case 'K':
 | 
			
		||||
            header.unique_code = Header::UniqueCode::YoshiKoro;
 | 
			
		||||
            break;
 | 
			
		||||
        case 'P':
 | 
			
		||||
            header.unique_code = Header::UniqueCode::Ereader;
 | 
			
		||||
            break;
 | 
			
		||||
        case 'R':
 | 
			
		||||
            header.unique_code = Header::UniqueCode::Warioware;
 | 
			
		||||
            break;
 | 
			
		||||
        case 'U':
 | 
			
		||||
            header.unique_code = Header::UniqueCode::Boktai;
 | 
			
		||||
            break;
 | 
			
		||||
        case 'V':
 | 
			
		||||
            header.unique_code = Header::UniqueCode::DrillDozer;
 | 
			
		||||
            break;
 | 
			
		||||
 | 
			
		||||
        default:
 | 
			
		||||
            glogger.error("HEADER: invalid unique code: {}", rom[0xAC]);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    header.title_code = std::string(&rom[0xAD], &rom[0xAE]);
 | 
			
		||||
 | 
			
		||||
    switch (rom[0xAF]) {
 | 
			
		||||
        case 'J':
 | 
			
		||||
            header.i18n = Header::I18n::Japan;
 | 
			
		||||
            break;
 | 
			
		||||
        case 'P':
 | 
			
		||||
            header.i18n = Header::I18n::Europe;
 | 
			
		||||
            break;
 | 
			
		||||
        case 'F':
 | 
			
		||||
            header.i18n = Header::I18n::French;
 | 
			
		||||
            break;
 | 
			
		||||
        case 'S':
 | 
			
		||||
            header.i18n = Header::I18n::Spanish;
 | 
			
		||||
            break;
 | 
			
		||||
        case 'E':
 | 
			
		||||
            header.i18n = Header::I18n::Usa;
 | 
			
		||||
            break;
 | 
			
		||||
        case 'D':
 | 
			
		||||
            header.i18n = Header::I18n::German;
 | 
			
		||||
            break;
 | 
			
		||||
        case 'I':
 | 
			
		||||
            header.i18n = Header::I18n::Italian;
 | 
			
		||||
            break;
 | 
			
		||||
 | 
			
		||||
        default:
 | 
			
		||||
            glogger.error("HEADER: invalid destination/language: {}",
 | 
			
		||||
                          rom[0xAF]);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (rom[0xB2] != 0x96)
 | 
			
		||||
        glogger.error("HEADER: invalid fixed byte at 0xB2");
 | 
			
		||||
 | 
			
		||||
    for (uint32_t i = 0xB5; i < 0xBC; i++) {
 | 
			
		||||
        if (rom[i] != 0x00)
 | 
			
		||||
            glogger.error("HEADER: invalid fixed bytes at 0xB5");
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    header.version = rom[0xBC];
 | 
			
		||||
 | 
			
		||||
    // checksum
 | 
			
		||||
    {
 | 
			
		||||
        uint32_t i = 0xA0, chk = 0;
 | 
			
		||||
        while (i <= 0xBC)
 | 
			
		||||
            chk -= rom[i++];
 | 
			
		||||
        chk -= 0x19;
 | 
			
		||||
        chk &= 0xFF;
 | 
			
		||||
 | 
			
		||||
        if (chk != rom[0xBD])
 | 
			
		||||
            glogger.error("HEADER: checksum does not match");
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // multiboot not required right now
 | 
			
		||||
}
 | 
			
		||||
}
 | 
			
		||||
@@ -1,5 +1,4 @@
 | 
			
		||||
lib_sources = files(
 | 
			
		||||
  'memory.cc',
 | 
			
		||||
  'bus.cc',
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										107
									
								
								tests/bus.cc
									
									
									
									
									
								
							
							
						
						
									
										107
									
								
								tests/bus.cc
									
									
									
									
									
								
							@@ -8,20 +8,111 @@ using namespace matar;
 | 
			
		||||
class BusFixture {
 | 
			
		||||
  public:
 | 
			
		||||
    BusFixture()
 | 
			
		||||
      : bus(Memory(std::array<uint8_t, Memory::BIOS_SIZE>(),
 | 
			
		||||
                   std::vector<uint8_t>(Header::HEADER_SIZE))) {}
 | 
			
		||||
      : bus(std::array<uint8_t, Bus::BIOS_SIZE>(),
 | 
			
		||||
            std::vector<uint8_t>(Header::HEADER_SIZE)) {}
 | 
			
		||||
 | 
			
		||||
  protected:
 | 
			
		||||
    Bus bus;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
TEST_CASE_METHOD(BusFixture, "Byte", TAG) {
 | 
			
		||||
    CHECK(bus.read_byte(0x30001A9) == 0);
 | 
			
		||||
TEST_CASE("bios", TAG) {
 | 
			
		||||
    std::array<uint8_t, Bus::BIOS_SIZE> bios = { 0 };
 | 
			
		||||
 | 
			
		||||
    bus.write_byte(0x30001A9, 0xEC);
 | 
			
		||||
    CHECK(bus.read_byte(0x30001A9) == 0xEC);
 | 
			
		||||
    CHECK(bus.read_word(0x30001A9) == 0xEC);
 | 
			
		||||
    CHECK(bus.read_halfword(0x30001A9) == 0xEC);
 | 
			
		||||
    // populate bios
 | 
			
		||||
    bios[0]      = 0xAC;
 | 
			
		||||
    bios[0x3FFF] = 0x48;
 | 
			
		||||
    bios[0x2A56] = 0x10;
 | 
			
		||||
 | 
			
		||||
    Bus bus(std::move(bios), std::vector<uint8_t>(Header::HEADER_SIZE));
 | 
			
		||||
 | 
			
		||||
    CHECK(bus.read_byte(0) == 0xAC);
 | 
			
		||||
    CHECK(bus.read_byte(0x3FFF) == 0x48);
 | 
			
		||||
    CHECK(bus.read_byte(0x2A56) == 0x10);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
TEST_CASE_METHOD(BusFixture, "board wram", TAG) {
 | 
			
		||||
    bus.write_byte(0x2000000, 0xAC);
 | 
			
		||||
    CHECK(bus.read_byte(0x2000000) == 0xAC);
 | 
			
		||||
 | 
			
		||||
    bus.write_byte(0x203FFFF, 0x48);
 | 
			
		||||
    CHECK(bus.read_byte(0x203FFFF) == 0x48);
 | 
			
		||||
 | 
			
		||||
    bus.write_byte(0x2022A56, 0x10);
 | 
			
		||||
    CHECK(bus.read_byte(0x2022A56) == 0x10);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
TEST_CASE_METHOD(BusFixture, "chip wram", TAG) {
 | 
			
		||||
    bus.write_byte(0x3000000, 0xAC);
 | 
			
		||||
    CHECK(bus.read_byte(0x3000000) == 0xAC);
 | 
			
		||||
 | 
			
		||||
    bus.write_byte(0x3007FFF, 0x48);
 | 
			
		||||
    CHECK(bus.read_byte(0x3007FFF) == 0x48);
 | 
			
		||||
 | 
			
		||||
    bus.write_byte(0x3002A56, 0x10);
 | 
			
		||||
    CHECK(bus.read_byte(0x3002A56) == 0x10);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
TEST_CASE_METHOD(BusFixture, "palette ram", TAG) {
 | 
			
		||||
    bus.write_byte(0x5000000, 0xAC);
 | 
			
		||||
    CHECK(bus.read_byte(0x5000000) == 0xAC);
 | 
			
		||||
 | 
			
		||||
    bus.write_byte(0x50003FF, 0x48);
 | 
			
		||||
    CHECK(bus.read_byte(0x50003FF) == 0x48);
 | 
			
		||||
 | 
			
		||||
    bus.write_byte(0x5000156, 0x10);
 | 
			
		||||
    CHECK(bus.read_byte(0x5000156) == 0x10);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
TEST_CASE_METHOD(BusFixture, "video ram", TAG) {
 | 
			
		||||
    bus.write_byte(0x6000000, 0xAC);
 | 
			
		||||
    CHECK(bus.read_byte(0x6000000) == 0xAC);
 | 
			
		||||
 | 
			
		||||
    bus.write_byte(0x6017FFF, 0x48);
 | 
			
		||||
    CHECK(bus.read_byte(0x6017FFF) == 0x48);
 | 
			
		||||
 | 
			
		||||
    bus.write_byte(0x6012A56, 0x10);
 | 
			
		||||
    CHECK(bus.read_byte(0x6012A56) == 0x10);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
TEST_CASE_METHOD(BusFixture, "oam obj ram", TAG) {
 | 
			
		||||
    bus.write_byte(0x7000000, 0xAC);
 | 
			
		||||
    CHECK(bus.read_byte(0x7000000) == 0xAC);
 | 
			
		||||
 | 
			
		||||
    bus.write_byte(0x70003FF, 0x48);
 | 
			
		||||
    CHECK(bus.read_byte(0x70003FF) == 0x48);
 | 
			
		||||
 | 
			
		||||
    bus.write_byte(0x7000156, 0x10);
 | 
			
		||||
    CHECK(bus.read_byte(0x7000156) == 0x10);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
TEST_CASE("rom", TAG) {
 | 
			
		||||
    std::vector<uint8_t> rom(32 * 1024 * 1024, 0);
 | 
			
		||||
 | 
			
		||||
    // populate rom
 | 
			
		||||
    rom[0]         = 0xAC;
 | 
			
		||||
    rom[0x1FFFFFF] = 0x48;
 | 
			
		||||
    rom[0x0EF0256] = 0x10;
 | 
			
		||||
 | 
			
		||||
    // 32 megabyte ROM
 | 
			
		||||
    Bus bus(std::array<uint8_t, Bus::BIOS_SIZE>(), std::move(rom));
 | 
			
		||||
 | 
			
		||||
    SECTION("ROM1") {
 | 
			
		||||
        CHECK(bus.read_byte(0x8000000) == 0xAC);
 | 
			
		||||
        CHECK(bus.read_byte(0x9FFFFFF) == 0x48);
 | 
			
		||||
        CHECK(bus.read_byte(0x8EF0256) == 0x10);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    SECTION("ROM2") {
 | 
			
		||||
        CHECK(bus.read_byte(0xA000000) == 0xAC);
 | 
			
		||||
        CHECK(bus.read_byte(0xBFFFFFF) == 0x48);
 | 
			
		||||
        CHECK(bus.read_byte(0xAEF0256) == 0x10);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    SECTION("ROM3") {
 | 
			
		||||
        CHECK(bus.read_byte(0xC000000) == 0xAC);
 | 
			
		||||
        CHECK(bus.read_byte(0xDFFFFFF) == 0x48);
 | 
			
		||||
        CHECK(bus.read_byte(0xCEF0256) == 0x10);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
TEST_CASE_METHOD(BusFixture, "Halfword", TAG) {
 | 
			
		||||
 
 | 
			
		||||
@@ -182,13 +182,13 @@ TEST_CASE_METHOD(CpuFixture, "Single Data Swap", TAG) {
 | 
			
		||||
    setr(9, 0x3003FED);
 | 
			
		||||
    setr(3, 94235087);
 | 
			
		||||
    setr(3, -259039045);
 | 
			
		||||
    bus.write_word(getr(9), 3241011111);
 | 
			
		||||
    bus->write_word(getr(9), 3241011111);
 | 
			
		||||
 | 
			
		||||
    SECTION("word") {
 | 
			
		||||
        exec(data);
 | 
			
		||||
 | 
			
		||||
        CHECK(getr(4) == 3241011111);
 | 
			
		||||
        CHECK(bus.read_word(getr(9)) == static_cast<uint32_t>(-259039045));
 | 
			
		||||
        CHECK(bus->read_word(getr(9)) == static_cast<uint32_t>(-259039045));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    SECTION("byte") {
 | 
			
		||||
@@ -196,7 +196,7 @@ TEST_CASE_METHOD(CpuFixture, "Single Data Swap", TAG) {
 | 
			
		||||
        exec(data);
 | 
			
		||||
 | 
			
		||||
        CHECK(getr(4) == (3241011111 & 0xFF));
 | 
			
		||||
        CHECK(bus.read_byte(getr(9)) ==
 | 
			
		||||
        CHECK(bus->read_byte(getr(9)) ==
 | 
			
		||||
              static_cast<uint8_t>(-259039045 & 0xFF));
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -226,7 +226,7 @@ TEST_CASE_METHOD(CpuFixture, "Single Data Transfer", TAG) {
 | 
			
		||||
    // shifted register (immediate)
 | 
			
		||||
    {
 | 
			
		||||
        // 0x31E + 0x3000004
 | 
			
		||||
        bus.write_word(0x30031E4, 95995);
 | 
			
		||||
        bus->write_word(0x30031E4, 95995);
 | 
			
		||||
        exec(data);
 | 
			
		||||
 | 
			
		||||
        CHECK(getr(5) == 95995);
 | 
			
		||||
@@ -244,7 +244,7 @@ TEST_CASE_METHOD(CpuFixture, "Single Data Transfer", TAG) {
 | 
			
		||||
 | 
			
		||||
        setr(12, 2);
 | 
			
		||||
        // 6384 + 0x3000004
 | 
			
		||||
        bus.write_word(0x30018F4, 3948123487);
 | 
			
		||||
        bus->write_word(0x30018F4, 3948123487);
 | 
			
		||||
        exec(data);
 | 
			
		||||
 | 
			
		||||
        CHECK(getr(5) == 3948123487);
 | 
			
		||||
@@ -254,7 +254,7 @@ TEST_CASE_METHOD(CpuFixture, "Single Data Transfer", TAG) {
 | 
			
		||||
    {
 | 
			
		||||
        data_transfer->offset = static_cast<uint16_t>(0xDA1);
 | 
			
		||||
        // 0xDA1 + 0x3000004
 | 
			
		||||
        bus.write_word(0x3000DA5, 68795467);
 | 
			
		||||
        bus->write_word(0x3000DA5, 68795467);
 | 
			
		||||
 | 
			
		||||
        exec(data);
 | 
			
		||||
 | 
			
		||||
@@ -266,7 +266,7 @@ TEST_CASE_METHOD(CpuFixture, "Single Data Transfer", TAG) {
 | 
			
		||||
        setr(7, 0x3005E0D);
 | 
			
		||||
        data_transfer->up = false;
 | 
			
		||||
        // 0x3005E0D - 0xDA1
 | 
			
		||||
        bus.write_word(0x300506C, 5949595);
 | 
			
		||||
        bus->write_word(0x300506C, 5949595);
 | 
			
		||||
 | 
			
		||||
        exec(data);
 | 
			
		||||
 | 
			
		||||
@@ -279,7 +279,7 @@ TEST_CASE_METHOD(CpuFixture, "Single Data Transfer", TAG) {
 | 
			
		||||
    {
 | 
			
		||||
        data_transfer->write = true;
 | 
			
		||||
        // 0x3005E0D - 0xDA1
 | 
			
		||||
        bus.write_word(0x300506C, 967844);
 | 
			
		||||
        bus->write_word(0x300506C, 967844);
 | 
			
		||||
 | 
			
		||||
        exec(data);
 | 
			
		||||
 | 
			
		||||
@@ -292,7 +292,7 @@ TEST_CASE_METHOD(CpuFixture, "Single Data Transfer", TAG) {
 | 
			
		||||
    {
 | 
			
		||||
        data_transfer->write = false;
 | 
			
		||||
        data_transfer->pre   = false;
 | 
			
		||||
        bus.write_word(0x300506C, 61119);
 | 
			
		||||
        bus->write_word(0x300506C, 61119);
 | 
			
		||||
 | 
			
		||||
        exec(data);
 | 
			
		||||
 | 
			
		||||
@@ -307,7 +307,7 @@ TEST_CASE_METHOD(CpuFixture, "Single Data Transfer", TAG) {
 | 
			
		||||
 | 
			
		||||
        exec(data);
 | 
			
		||||
 | 
			
		||||
        CHECK(bus.read_word(0x30042CB) == 61119);
 | 
			
		||||
        CHECK(bus->read_word(0x30042CB) == 61119);
 | 
			
		||||
        // 0x30042CB - 0xDA1
 | 
			
		||||
        CHECK(getr(7) == 0x300352A);
 | 
			
		||||
    }
 | 
			
		||||
@@ -319,7 +319,7 @@ TEST_CASE_METHOD(CpuFixture, "Single Data Transfer", TAG) {
 | 
			
		||||
 | 
			
		||||
        exec(data);
 | 
			
		||||
 | 
			
		||||
        CHECK(bus.read_word(0x300352A) == 61119);
 | 
			
		||||
        CHECK(bus->read_word(0x300352A) == 61119);
 | 
			
		||||
        // 0x300352A - 0xDA1
 | 
			
		||||
        CHECK(getr(15) == 0x3002789);
 | 
			
		||||
 | 
			
		||||
@@ -334,7 +334,7 @@ TEST_CASE_METHOD(CpuFixture, "Single Data Transfer", TAG) {
 | 
			
		||||
 | 
			
		||||
        exec(data);
 | 
			
		||||
 | 
			
		||||
        CHECK(bus.read_word(0x300352A + INSTRUCTION_SIZE) == 444444);
 | 
			
		||||
        CHECK(bus->read_word(0x300352A + INSTRUCTION_SIZE) == 444444);
 | 
			
		||||
        // 0x300352A - 0xDA1
 | 
			
		||||
        CHECK(getr(7) == 0x3002789 + INSTRUCTION_SIZE);
 | 
			
		||||
 | 
			
		||||
@@ -351,7 +351,7 @@ TEST_CASE_METHOD(CpuFixture, "Single Data Transfer", TAG) {
 | 
			
		||||
 | 
			
		||||
        exec(data);
 | 
			
		||||
 | 
			
		||||
        CHECK(bus.read_word(0x3002789) == (458267584 & 0xFF));
 | 
			
		||||
        CHECK(bus->read_word(0x3002789) == (458267584 & 0xFF));
 | 
			
		||||
        // 0x3002789 - 0xDA1
 | 
			
		||||
        CHECK(getr(7) == 0x30019E8);
 | 
			
		||||
    }
 | 
			
		||||
@@ -377,7 +377,7 @@ TEST_CASE_METHOD(CpuFixture, "Halfword Transfer", TAG) {
 | 
			
		||||
    // register offset
 | 
			
		||||
    {
 | 
			
		||||
        //  0x300611E  + 0x384
 | 
			
		||||
        bus.write_word(0x30064A2, 3948123487);
 | 
			
		||||
        bus->write_word(0x30064A2, 3948123487);
 | 
			
		||||
        exec(data);
 | 
			
		||||
 | 
			
		||||
        CHECK(getr(11) == (3948123487 & 0xFFFF));
 | 
			
		||||
@@ -388,7 +388,7 @@ TEST_CASE_METHOD(CpuFixture, "Halfword Transfer", TAG) {
 | 
			
		||||
        hw_transfer->imm    = true;
 | 
			
		||||
        hw_transfer->offset = 0xA7;
 | 
			
		||||
        // 0x300611E + 0xA7
 | 
			
		||||
        bus.write_word(0x30061C5, 594633302);
 | 
			
		||||
        bus->write_word(0x30061C5, 594633302);
 | 
			
		||||
        exec(data);
 | 
			
		||||
 | 
			
		||||
        CHECK(getr(11) == (594633302 & 0xFFFF));
 | 
			
		||||
@@ -398,7 +398,7 @@ TEST_CASE_METHOD(CpuFixture, "Halfword Transfer", TAG) {
 | 
			
		||||
    {
 | 
			
		||||
        hw_transfer->up = false;
 | 
			
		||||
        // 0x300611E - 0xA7
 | 
			
		||||
        bus.write_word(0x3006077, 222221);
 | 
			
		||||
        bus->write_word(0x3006077, 222221);
 | 
			
		||||
 | 
			
		||||
        exec(data);
 | 
			
		||||
 | 
			
		||||
@@ -411,7 +411,7 @@ TEST_CASE_METHOD(CpuFixture, "Halfword Transfer", TAG) {
 | 
			
		||||
    {
 | 
			
		||||
        hw_transfer->write = true;
 | 
			
		||||
        // 0x300611E - 0xA7
 | 
			
		||||
        bus.write_word(0x3006077, 100000005);
 | 
			
		||||
        bus->write_word(0x3006077, 100000005);
 | 
			
		||||
 | 
			
		||||
        exec(data);
 | 
			
		||||
 | 
			
		||||
@@ -423,7 +423,7 @@ TEST_CASE_METHOD(CpuFixture, "Halfword Transfer", TAG) {
 | 
			
		||||
    {
 | 
			
		||||
        hw_transfer->pre   = false;
 | 
			
		||||
        hw_transfer->write = false;
 | 
			
		||||
        bus.write_word(0x3006077, 6111909);
 | 
			
		||||
        bus->write_word(0x3006077, 6111909);
 | 
			
		||||
 | 
			
		||||
        exec(data);
 | 
			
		||||
 | 
			
		||||
@@ -438,7 +438,7 @@ TEST_CASE_METHOD(CpuFixture, "Halfword Transfer", TAG) {
 | 
			
		||||
 | 
			
		||||
        exec(data);
 | 
			
		||||
 | 
			
		||||
        CHECK(bus.read_halfword(0x3005FD0) == (6111909 & 0xFFFF));
 | 
			
		||||
        CHECK(bus->read_halfword(0x3005FD0) == (6111909 & 0xFFFF));
 | 
			
		||||
        // 0x3005FD0 - 0xA7
 | 
			
		||||
        CHECK(getr(10) == 0x3005F29);
 | 
			
		||||
    }
 | 
			
		||||
@@ -450,7 +450,7 @@ TEST_CASE_METHOD(CpuFixture, "Halfword Transfer", TAG) {
 | 
			
		||||
 | 
			
		||||
        exec(data);
 | 
			
		||||
 | 
			
		||||
        CHECK(bus.read_halfword(0x3005F29 - 2 * INSTRUCTION_SIZE) ==
 | 
			
		||||
        CHECK(bus->read_halfword(0x3005F29 - 2 * INSTRUCTION_SIZE) ==
 | 
			
		||||
              (6111909 & 0xFFFF));
 | 
			
		||||
        // 0x3005F29 - 0xA7
 | 
			
		||||
        CHECK(getr(15) == 0x3005E82 - 2 * INSTRUCTION_SIZE);
 | 
			
		||||
@@ -466,7 +466,7 @@ TEST_CASE_METHOD(CpuFixture, "Halfword Transfer", TAG) {
 | 
			
		||||
 | 
			
		||||
        exec(data);
 | 
			
		||||
 | 
			
		||||
        CHECK(bus.read_halfword(0x3005F29 + INSTRUCTION_SIZE) == 224);
 | 
			
		||||
        CHECK(bus->read_halfword(0x3005F29 + INSTRUCTION_SIZE) == 224);
 | 
			
		||||
        // 0x3005F29 - 0xA7
 | 
			
		||||
        CHECK(getr(10) == 0x3005E82 + INSTRUCTION_SIZE);
 | 
			
		||||
 | 
			
		||||
@@ -479,7 +479,7 @@ TEST_CASE_METHOD(CpuFixture, "Halfword Transfer", TAG) {
 | 
			
		||||
    {
 | 
			
		||||
        hw_transfer->load = true;
 | 
			
		||||
        hw_transfer->sign = true;
 | 
			
		||||
        bus.write_halfword(0x3005E82, -12345);
 | 
			
		||||
        bus->write_halfword(0x3005E82, -12345);
 | 
			
		||||
 | 
			
		||||
        exec(data);
 | 
			
		||||
 | 
			
		||||
@@ -491,7 +491,7 @@ TEST_CASE_METHOD(CpuFixture, "Halfword Transfer", TAG) {
 | 
			
		||||
    // signed byte
 | 
			
		||||
    {
 | 
			
		||||
        hw_transfer->half = false;
 | 
			
		||||
        bus.write_byte(0x3005DDB, -56);
 | 
			
		||||
        bus->write_byte(0x3005DDB, -56);
 | 
			
		||||
 | 
			
		||||
        exec(data);
 | 
			
		||||
 | 
			
		||||
@@ -517,14 +517,14 @@ TEST_CASE_METHOD(CpuFixture, "Block Data Transfer", TAG) {
 | 
			
		||||
    SECTION("load") {
 | 
			
		||||
        static constexpr uint32_t address = 0x3000D78;
 | 
			
		||||
        // populate memory
 | 
			
		||||
        bus.write_word(address, 38947234);
 | 
			
		||||
        bus.write_word(address + alignment, 237164);
 | 
			
		||||
        bus.write_word(address + alignment * 2, 679785111);
 | 
			
		||||
        bus.write_word(address + alignment * 3, 905895898);
 | 
			
		||||
        bus.write_word(address + alignment * 4, 131313333);
 | 
			
		||||
        bus.write_word(address + alignment * 5, 131);
 | 
			
		||||
        bus.write_word(address + alignment * 6, 989231);
 | 
			
		||||
        bus.write_word(address + alignment * 7, 6);
 | 
			
		||||
        bus->write_word(address, 38947234);
 | 
			
		||||
        bus->write_word(address + alignment, 237164);
 | 
			
		||||
        bus->write_word(address + alignment * 2, 679785111);
 | 
			
		||||
        bus->write_word(address + alignment * 3, 905895898);
 | 
			
		||||
        bus->write_word(address + alignment * 4, 131313333);
 | 
			
		||||
        bus->write_word(address + alignment * 5, 131);
 | 
			
		||||
        bus->write_word(address + alignment * 6, 989231);
 | 
			
		||||
        bus->write_word(address + alignment * 7, 6);
 | 
			
		||||
 | 
			
		||||
        auto checker = [this](uint32_t rnval = 0) {
 | 
			
		||||
            CHECK(getr(0) == 237164);
 | 
			
		||||
@@ -613,16 +613,16 @@ TEST_CASE_METHOD(CpuFixture, "Block Data Transfer", TAG) {
 | 
			
		||||
        setr(15, 6);
 | 
			
		||||
 | 
			
		||||
        auto checker = [this]() {
 | 
			
		||||
            CHECK(bus.read_word(address + alignment) == 237164);
 | 
			
		||||
            CHECK(bus.read_word(address + alignment * 2) == 679785111);
 | 
			
		||||
            CHECK(bus.read_word(address + alignment * 3) == 905895898);
 | 
			
		||||
            CHECK(bus.read_word(address + alignment * 4) == 131313333);
 | 
			
		||||
            CHECK(bus.read_word(address + alignment * 5) == 131);
 | 
			
		||||
            CHECK(bus.read_word(address + alignment * 6) == 989231);
 | 
			
		||||
            CHECK(bus.read_word(address + alignment * 7) == 6);
 | 
			
		||||
            CHECK(bus->read_word(address + alignment) == 237164);
 | 
			
		||||
            CHECK(bus->read_word(address + alignment * 2) == 679785111);
 | 
			
		||||
            CHECK(bus->read_word(address + alignment * 3) == 905895898);
 | 
			
		||||
            CHECK(bus->read_word(address + alignment * 4) == 131313333);
 | 
			
		||||
            CHECK(bus->read_word(address + alignment * 5) == 131);
 | 
			
		||||
            CHECK(bus->read_word(address + alignment * 6) == 989231);
 | 
			
		||||
            CHECK(bus->read_word(address + alignment * 7) == 6);
 | 
			
		||||
 | 
			
		||||
            for (uint8_t i = 1; i < 8; i++)
 | 
			
		||||
                bus.write_word(address + alignment * i, 0);
 | 
			
		||||
                bus->write_word(address + alignment * i, 0);
 | 
			
		||||
        };
 | 
			
		||||
 | 
			
		||||
        setr(10, address); // base
 | 
			
		||||
@@ -657,7 +657,7 @@ TEST_CASE_METHOD(CpuFixture, "Block Data Transfer", TAG) {
 | 
			
		||||
        block_transfer->s = true;
 | 
			
		||||
        exec(data);
 | 
			
		||||
        // User's R13 is different (unset at this point)
 | 
			
		||||
        CHECK(bus.read_word(address + alignment * 6) == 0);
 | 
			
		||||
        CHECK(bus->read_word(address + alignment * 6) == 0);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -43,7 +43,7 @@ uint32_t
 | 
			
		||||
CpuFixture::getr_(uint8_t r, Cpu& cpu) {
 | 
			
		||||
    uint32_t addr  = 0x02000000;
 | 
			
		||||
    uint8_t offset = r == 15 ? 4 : 0;
 | 
			
		||||
    uint32_t word  = bus.read_word(addr + offset);
 | 
			
		||||
    uint32_t word  = bus->read_word(addr + offset);
 | 
			
		||||
    Cpu tmp        = cpu;
 | 
			
		||||
    uint32_t ret   = 0xFFFFFFFF;
 | 
			
		||||
    uint8_t base   = r ? 0 : 1;
 | 
			
		||||
@@ -74,9 +74,9 @@ CpuFixture::getr_(uint8_t r, Cpu& cpu) {
 | 
			
		||||
 | 
			
		||||
    addr += offset;
 | 
			
		||||
 | 
			
		||||
    ret = bus.read_word(addr);
 | 
			
		||||
    ret = bus->read_word(addr);
 | 
			
		||||
 | 
			
		||||
    bus.write_word(addr, word);
 | 
			
		||||
    bus->write_word(addr, word);
 | 
			
		||||
 | 
			
		||||
    return ret;
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -5,8 +5,9 @@ using namespace matar;
 | 
			
		||||
class CpuFixture {
 | 
			
		||||
  public:
 | 
			
		||||
    CpuFixture()
 | 
			
		||||
      : bus(Memory(std::array<uint8_t, Memory::BIOS_SIZE>(),
 | 
			
		||||
                   std::vector<uint8_t>(Header::HEADER_SIZE)))
 | 
			
		||||
      : bus(std::shared_ptr<Bus>(
 | 
			
		||||
          new Bus(std::array<uint8_t, Bus::BIOS_SIZE>(),
 | 
			
		||||
                  std::vector<uint8_t>(Header::HEADER_SIZE))))
 | 
			
		||||
      , cpu(bus) {}
 | 
			
		||||
 | 
			
		||||
  protected:
 | 
			
		||||
@@ -30,7 +31,7 @@ class CpuFixture {
 | 
			
		||||
 | 
			
		||||
    void set_psr(Psr psr, bool spsr = false);
 | 
			
		||||
 | 
			
		||||
    Bus bus;
 | 
			
		||||
    std::shared_ptr<Bus> bus;
 | 
			
		||||
    Cpu cpu;
 | 
			
		||||
 | 
			
		||||
  private:
 | 
			
		||||
 
 | 
			
		||||
@@ -532,7 +532,7 @@ TEST_CASE_METHOD(CpuFixture, "PC Relative Load", TAG) {
 | 
			
		||||
    setr(15, 0x3003FD5);
 | 
			
		||||
    // resetting bit 0 for 0x3003FD5, we get 0x3003FD4
 | 
			
		||||
    // 0x3003FD4 + 0x578
 | 
			
		||||
    bus.write_word(0x300454C, 489753492);
 | 
			
		||||
    bus->write_word(0x300454C, 489753492);
 | 
			
		||||
 | 
			
		||||
    CHECK(getr(0) == 0);
 | 
			
		||||
    exec(data);
 | 
			
		||||
@@ -551,20 +551,20 @@ TEST_CASE_METHOD(CpuFixture, "Load/Store with Register Offset", TAG) {
 | 
			
		||||
 | 
			
		||||
    SECTION("store") {
 | 
			
		||||
        // 0x3003000 + 0x332
 | 
			
		||||
        CHECK(bus.read_word(0x3003332) == 0);
 | 
			
		||||
        CHECK(bus->read_word(0x3003332) == 0);
 | 
			
		||||
        exec(data);
 | 
			
		||||
        CHECK(bus.read_word(0x3003332) == 389524259);
 | 
			
		||||
        CHECK(bus->read_word(0x3003332) == 389524259);
 | 
			
		||||
 | 
			
		||||
        // byte
 | 
			
		||||
        load->byte = true;
 | 
			
		||||
        bus.write_word(0x3003332, 0);
 | 
			
		||||
        bus->write_word(0x3003332, 0);
 | 
			
		||||
        exec(data);
 | 
			
		||||
        CHECK(bus.read_word(0x3003332) == 35);
 | 
			
		||||
        CHECK(bus->read_word(0x3003332) == 35);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    SECTION("load") {
 | 
			
		||||
        load->load = true;
 | 
			
		||||
        bus.write_word(0x3003332, 11123489);
 | 
			
		||||
        bus->write_word(0x3003332, 11123489);
 | 
			
		||||
        exec(data);
 | 
			
		||||
        CHECK(getr(3) == 11123489);
 | 
			
		||||
 | 
			
		||||
@@ -588,21 +588,21 @@ TEST_CASE_METHOD(CpuFixture, "Load/Store Sign Extended Byte/Halfword", TAG) {
 | 
			
		||||
 | 
			
		||||
    SECTION("SH = 00") {
 | 
			
		||||
        // 0x3003000 + 0x332
 | 
			
		||||
        CHECK(bus.read_word(0x3003332) == 0);
 | 
			
		||||
        CHECK(bus->read_word(0x3003332) == 0);
 | 
			
		||||
        exec(data);
 | 
			
		||||
        CHECK(bus.read_word(0x3003332) == 43811);
 | 
			
		||||
        CHECK(bus->read_word(0x3003332) == 43811);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    SECTION("SH = 01") {
 | 
			
		||||
        load->h = true;
 | 
			
		||||
        bus.write_word(0x3003332, 11123489);
 | 
			
		||||
        bus->write_word(0x3003332, 11123489);
 | 
			
		||||
        exec(data);
 | 
			
		||||
        CHECK(getr(3) == 47905);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    SECTION("SH = 10") {
 | 
			
		||||
        load->s = true;
 | 
			
		||||
        bus.write_word(0x3003332, 34521594);
 | 
			
		||||
        bus->write_word(0x3003332, 34521594);
 | 
			
		||||
        exec(data);
 | 
			
		||||
        // sign extended 250 byte (0xFA)
 | 
			
		||||
        CHECK(getr(3) == 4294967290);
 | 
			
		||||
@@ -611,7 +611,7 @@ TEST_CASE_METHOD(CpuFixture, "Load/Store Sign Extended Byte/Halfword", TAG) {
 | 
			
		||||
    SECTION("SH = 11") {
 | 
			
		||||
        load->s = true;
 | 
			
		||||
        load->h = true;
 | 
			
		||||
        bus.write_word(0x3003332, 11123489);
 | 
			
		||||
        bus->write_word(0x3003332, 11123489);
 | 
			
		||||
        // sign extended 47905 halfword (0xBB21)
 | 
			
		||||
        exec(data);
 | 
			
		||||
        CHECK(getr(3) == 4294949665);
 | 
			
		||||
@@ -630,20 +630,20 @@ TEST_CASE_METHOD(CpuFixture, "Load/Store with Immediate Offset", TAG) {
 | 
			
		||||
 | 
			
		||||
    SECTION("store") {
 | 
			
		||||
        // 0x30066A + 0x6E
 | 
			
		||||
        CHECK(bus.read_word(0x30066D8) == 0);
 | 
			
		||||
        CHECK(bus->read_word(0x30066D8) == 0);
 | 
			
		||||
        exec(data);
 | 
			
		||||
        CHECK(bus.read_word(0x30066D8) == 389524259);
 | 
			
		||||
        CHECK(bus->read_word(0x30066D8) == 389524259);
 | 
			
		||||
 | 
			
		||||
        // byte
 | 
			
		||||
        load->byte = true;
 | 
			
		||||
        bus.write_word(0x30066D8, 0);
 | 
			
		||||
        bus->write_word(0x30066D8, 0);
 | 
			
		||||
        exec(data);
 | 
			
		||||
        CHECK(bus.read_word(0x30066D8) == 35);
 | 
			
		||||
        CHECK(bus->read_word(0x30066D8) == 35);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    SECTION("load") {
 | 
			
		||||
        load->load = true;
 | 
			
		||||
        bus.write_word(0x30066D8, 11123489);
 | 
			
		||||
        bus->write_word(0x30066D8, 11123489);
 | 
			
		||||
        exec(data);
 | 
			
		||||
        CHECK(getr(3) == 11123489);
 | 
			
		||||
 | 
			
		||||
@@ -664,14 +664,14 @@ TEST_CASE_METHOD(CpuFixture, "Load/Store Halfword", TAG) {
 | 
			
		||||
 | 
			
		||||
    SECTION("store") {
 | 
			
		||||
        // 0x300666A + 0x6E
 | 
			
		||||
        CHECK(bus.read_word(0x30066D8) == 0);
 | 
			
		||||
        CHECK(bus->read_word(0x30066D8) == 0);
 | 
			
		||||
        exec(data);
 | 
			
		||||
        CHECK(bus.read_word(0x30066D8) == 43811);
 | 
			
		||||
        CHECK(bus->read_word(0x30066D8) == 43811);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    SECTION("load") {
 | 
			
		||||
        load->load = true;
 | 
			
		||||
        bus.write_word(0x30066D8, 11123489);
 | 
			
		||||
        bus->write_word(0x30066D8, 11123489);
 | 
			
		||||
        exec(data);
 | 
			
		||||
        CHECK(getr(3) == 47905);
 | 
			
		||||
    }
 | 
			
		||||
@@ -688,14 +688,14 @@ TEST_CASE_METHOD(CpuFixture, "SP Relative Load", TAG) {
 | 
			
		||||
 | 
			
		||||
    SECTION("store") {
 | 
			
		||||
        // 0x3004A8A + 0x328
 | 
			
		||||
        CHECK(bus.read_word(0x3004DB2) == 0);
 | 
			
		||||
        CHECK(bus->read_word(0x3004DB2) == 0);
 | 
			
		||||
        exec(data);
 | 
			
		||||
        CHECK(bus.read_word(0x3004DB2) == 2349505744);
 | 
			
		||||
        CHECK(bus->read_word(0x3004DB2) == 2349505744);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    SECTION("load") {
 | 
			
		||||
        load->load = true;
 | 
			
		||||
        bus.write_word(0x3004DB2, 11123489);
 | 
			
		||||
        bus->write_word(0x3004DB2, 11123489);
 | 
			
		||||
        exec(data);
 | 
			
		||||
        CHECK(getr(1) == 11123489);
 | 
			
		||||
    }
 | 
			
		||||
@@ -761,11 +761,11 @@ TEST_CASE_METHOD(CpuFixture, "Push/Pop Registers", TAG) {
 | 
			
		||||
 | 
			
		||||
        auto checker = [this]() {
 | 
			
		||||
            // address
 | 
			
		||||
            CHECK(bus.read_word(address) == 237164);
 | 
			
		||||
            CHECK(bus.read_word(address + alignment) == 679785111);
 | 
			
		||||
            CHECK(bus.read_word(address + alignment * 2) == 905895898);
 | 
			
		||||
            CHECK(bus.read_word(address + alignment * 3) == 131313333);
 | 
			
		||||
            CHECK(bus.read_word(address + alignment * 4) == 131);
 | 
			
		||||
            CHECK(bus->read_word(address) == 237164);
 | 
			
		||||
            CHECK(bus->read_word(address + alignment) == 679785111);
 | 
			
		||||
            CHECK(bus->read_word(address + alignment * 2) == 905895898);
 | 
			
		||||
            CHECK(bus->read_word(address + alignment * 3) == 131313333);
 | 
			
		||||
            CHECK(bus->read_word(address + alignment * 4) == 131);
 | 
			
		||||
        };
 | 
			
		||||
 | 
			
		||||
        // set stack pointer to top of stack
 | 
			
		||||
@@ -785,7 +785,7 @@ TEST_CASE_METHOD(CpuFixture, "Push/Pop Registers", TAG) {
 | 
			
		||||
            setr(13, address + alignment * 6);
 | 
			
		||||
            exec(data);
 | 
			
		||||
 | 
			
		||||
            CHECK(bus.read_word(address + alignment * 5) == 999304);
 | 
			
		||||
            CHECK(bus->read_word(address + alignment * 5) == 999304);
 | 
			
		||||
            checker();
 | 
			
		||||
            CHECK(getr(13) == address);
 | 
			
		||||
        }
 | 
			
		||||
@@ -795,11 +795,11 @@ TEST_CASE_METHOD(CpuFixture, "Push/Pop Registers", TAG) {
 | 
			
		||||
        push->load = true;
 | 
			
		||||
 | 
			
		||||
        // populate memory
 | 
			
		||||
        bus.write_word(address, 237164);
 | 
			
		||||
        bus.write_word(address + alignment, 679785111);
 | 
			
		||||
        bus.write_word(address + alignment * 2, 905895898);
 | 
			
		||||
        bus.write_word(address + alignment * 3, 131313333);
 | 
			
		||||
        bus.write_word(address + alignment * 4, 131);
 | 
			
		||||
        bus->write_word(address, 237164);
 | 
			
		||||
        bus->write_word(address + alignment, 679785111);
 | 
			
		||||
        bus->write_word(address + alignment * 2, 905895898);
 | 
			
		||||
        bus->write_word(address + alignment * 3, 131313333);
 | 
			
		||||
        bus->write_word(address + alignment * 4, 131);
 | 
			
		||||
 | 
			
		||||
        auto checker = [this]() {
 | 
			
		||||
            CHECK(getr(0) == 237164);
 | 
			
		||||
@@ -828,7 +828,7 @@ TEST_CASE_METHOD(CpuFixture, "Push/Pop Registers", TAG) {
 | 
			
		||||
        SECTION("with SP") {
 | 
			
		||||
            push->pclr = true;
 | 
			
		||||
            // populate next address
 | 
			
		||||
            bus.write_word(address + alignment * 5, 93333912);
 | 
			
		||||
            bus->write_word(address + alignment * 5, 93333912);
 | 
			
		||||
            exec(data);
 | 
			
		||||
 | 
			
		||||
            CHECK(getr(15) == 93333912);
 | 
			
		||||
@@ -860,11 +860,11 @@ TEST_CASE_METHOD(CpuFixture, "Multiple Load/Store", TAG) {
 | 
			
		||||
 | 
			
		||||
        exec(data);
 | 
			
		||||
 | 
			
		||||
        CHECK(bus.read_word(address) == 237164);
 | 
			
		||||
        CHECK(bus.read_word(address + alignment) == address + alignment * 5);
 | 
			
		||||
        CHECK(bus.read_word(address + alignment * 2) == 905895898);
 | 
			
		||||
        CHECK(bus.read_word(address + alignment * 3) == 131313333);
 | 
			
		||||
        CHECK(bus.read_word(address + alignment * 4) == 131);
 | 
			
		||||
        CHECK(bus->read_word(address) == 237164);
 | 
			
		||||
        CHECK(bus->read_word(address + alignment) == address + alignment * 5);
 | 
			
		||||
        CHECK(bus->read_word(address + alignment * 2) == 905895898);
 | 
			
		||||
        CHECK(bus->read_word(address + alignment * 3) == 131313333);
 | 
			
		||||
        CHECK(bus->read_word(address + alignment * 4) == 131);
 | 
			
		||||
        // write back
 | 
			
		||||
        CHECK(getr(2) == address);
 | 
			
		||||
    }
 | 
			
		||||
@@ -873,11 +873,11 @@ TEST_CASE_METHOD(CpuFixture, "Multiple Load/Store", TAG) {
 | 
			
		||||
        push->load = true;
 | 
			
		||||
 | 
			
		||||
        // populate memory
 | 
			
		||||
        bus.write_word(address, 237164);
 | 
			
		||||
        bus.write_word(address + alignment, 679785111);
 | 
			
		||||
        bus.write_word(address + alignment * 2, 905895898);
 | 
			
		||||
        bus.write_word(address + alignment * 3, 131313333);
 | 
			
		||||
        bus.write_word(address + alignment * 4, 131);
 | 
			
		||||
        bus->write_word(address, 237164);
 | 
			
		||||
        bus->write_word(address + alignment, 679785111);
 | 
			
		||||
        bus->write_word(address + alignment * 2, 905895898);
 | 
			
		||||
        bus->write_word(address + alignment * 3, 131313333);
 | 
			
		||||
        bus->write_word(address + alignment * 4, 131);
 | 
			
		||||
 | 
			
		||||
        // base
 | 
			
		||||
        setr(2, address);
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										118
									
								
								tests/memory.cc
									
									
									
									
									
								
							
							
						
						
									
										118
									
								
								tests/memory.cc
									
									
									
									
									
								
							@@ -1,118 +0,0 @@
 | 
			
		||||
#include "memory.hh"
 | 
			
		||||
#include <catch2/catch_test_macros.hpp>
 | 
			
		||||
 | 
			
		||||
#define TAG "[memory]"
 | 
			
		||||
 | 
			
		||||
using namespace matar;
 | 
			
		||||
 | 
			
		||||
class MemFixture {
 | 
			
		||||
  public:
 | 
			
		||||
    MemFixture()
 | 
			
		||||
      : memory(std::array<uint8_t, Memory::BIOS_SIZE>(),
 | 
			
		||||
               std::vector<uint8_t>(Header::HEADER_SIZE)) {}
 | 
			
		||||
 | 
			
		||||
  protected:
 | 
			
		||||
    Memory memory;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
TEST_CASE("bios", TAG) {
 | 
			
		||||
    std::array<uint8_t, Memory::BIOS_SIZE> bios = { 0 };
 | 
			
		||||
 | 
			
		||||
    // populate bios
 | 
			
		||||
    bios[0]      = 0xAC;
 | 
			
		||||
    bios[0x3FFF] = 0x48;
 | 
			
		||||
    bios[0x2A56] = 0x10;
 | 
			
		||||
 | 
			
		||||
    Memory memory(std::move(bios), std::vector<uint8_t>(Header::HEADER_SIZE));
 | 
			
		||||
 | 
			
		||||
    CHECK(memory.read(0) == 0xAC);
 | 
			
		||||
    CHECK(memory.read(0x3FFF) == 0x48);
 | 
			
		||||
    CHECK(memory.read(0x2A56) == 0x10);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
TEST_CASE_METHOD(MemFixture, "board wram", TAG) {
 | 
			
		||||
    memory.write(0x2000000, 0xAC);
 | 
			
		||||
    CHECK(memory.read(0x2000000) == 0xAC);
 | 
			
		||||
 | 
			
		||||
    memory.write(0x203FFFF, 0x48);
 | 
			
		||||
    CHECK(memory.read(0x203FFFF) == 0x48);
 | 
			
		||||
 | 
			
		||||
    memory.write(0x2022A56, 0x10);
 | 
			
		||||
    CHECK(memory.read(0x2022A56) == 0x10);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
TEST_CASE_METHOD(MemFixture, "chip wram", TAG) {
 | 
			
		||||
    memory.write(0x3000000, 0xAC);
 | 
			
		||||
    CHECK(memory.read(0x3000000) == 0xAC);
 | 
			
		||||
 | 
			
		||||
    memory.write(0x3007FFF, 0x48);
 | 
			
		||||
    CHECK(memory.read(0x3007FFF) == 0x48);
 | 
			
		||||
 | 
			
		||||
    memory.write(0x3002A56, 0x10);
 | 
			
		||||
    CHECK(memory.read(0x3002A56) == 0x10);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
TEST_CASE_METHOD(MemFixture, "palette ram", TAG) {
 | 
			
		||||
    memory.write(0x5000000, 0xAC);
 | 
			
		||||
    CHECK(memory.read(0x5000000) == 0xAC);
 | 
			
		||||
 | 
			
		||||
    memory.write(0x50003FF, 0x48);
 | 
			
		||||
    CHECK(memory.read(0x50003FF) == 0x48);
 | 
			
		||||
 | 
			
		||||
    memory.write(0x5000156, 0x10);
 | 
			
		||||
    CHECK(memory.read(0x5000156) == 0x10);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
TEST_CASE_METHOD(MemFixture, "video ram", TAG) {
 | 
			
		||||
    memory.write(0x6000000, 0xAC);
 | 
			
		||||
    CHECK(memory.read(0x6000000) == 0xAC);
 | 
			
		||||
 | 
			
		||||
    memory.write(0x6017FFF, 0x48);
 | 
			
		||||
    CHECK(memory.read(0x6017FFF) == 0x48);
 | 
			
		||||
 | 
			
		||||
    memory.write(0x6012A56, 0x10);
 | 
			
		||||
    CHECK(memory.read(0x6012A56) == 0x10);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
TEST_CASE_METHOD(MemFixture, "oam obj ram", TAG) {
 | 
			
		||||
    memory.write(0x7000000, 0xAC);
 | 
			
		||||
    CHECK(memory.read(0x7000000) == 0xAC);
 | 
			
		||||
 | 
			
		||||
    memory.write(0x70003FF, 0x48);
 | 
			
		||||
    CHECK(memory.read(0x70003FF) == 0x48);
 | 
			
		||||
 | 
			
		||||
    memory.write(0x7000156, 0x10);
 | 
			
		||||
    CHECK(memory.read(0x7000156) == 0x10);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
TEST_CASE("rom", TAG) {
 | 
			
		||||
    std::vector<uint8_t> rom(32 * 1024 * 1024, 0);
 | 
			
		||||
 | 
			
		||||
    // populate rom
 | 
			
		||||
    rom[0]         = 0xAC;
 | 
			
		||||
    rom[0x1FFFFFF] = 0x48;
 | 
			
		||||
    rom[0x0EF0256] = 0x10;
 | 
			
		||||
 | 
			
		||||
    // 32 megabyte ROM
 | 
			
		||||
    Memory memory(std::array<uint8_t, Memory::BIOS_SIZE>(), std::move(rom));
 | 
			
		||||
 | 
			
		||||
    SECTION("ROM1") {
 | 
			
		||||
        CHECK(memory.read(0x8000000) == 0xAC);
 | 
			
		||||
        CHECK(memory.read(0x9FFFFFF) == 0x48);
 | 
			
		||||
        CHECK(memory.read(0x8EF0256) == 0x10);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    SECTION("ROM2") {
 | 
			
		||||
        CHECK(memory.read(0xA000000) == 0xAC);
 | 
			
		||||
        CHECK(memory.read(0xBFFFFFF) == 0x48);
 | 
			
		||||
        CHECK(memory.read(0xAEF0256) == 0x10);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    SECTION("ROM3") {
 | 
			
		||||
        CHECK(memory.read(0xC000000) == 0xAC);
 | 
			
		||||
        CHECK(memory.read(0xDFFFFFF) == 0x48);
 | 
			
		||||
        CHECK(memory.read(0xCEF0256) == 0x10);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#undef TAG
 | 
			
		||||
@@ -6,8 +6,7 @@ src = include_directories('../src')
 | 
			
		||||
 | 
			
		||||
tests_sources = files(
 | 
			
		||||
  'main.cc',
 | 
			
		||||
  'bus.cc',
 | 
			
		||||
  'memory.cc'
 | 
			
		||||
  'bus.cc'
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
subdir('cpu')
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user