mirror of
https://github.com/MoleskiCoder/EightBit.git
synced 2025-02-22 05:29:01 +00:00
Modifying the manner in which memory is mapped, allows a fairly clean mechanism for loading Intel "hex" files.
Signed-off-by: Adrian Conlon <Adrian.conlon@gmail.com>
This commit is contained in:
parent
a9adde6ea5
commit
7d840f1a42
@ -20,8 +20,8 @@ public:
|
||||
void initialise();
|
||||
|
||||
protected:
|
||||
virtual uint8_t& reference(uint16_t address) final {
|
||||
return m_ram.reference(address);
|
||||
virtual EightBit::MemoryMapping mapping(uint16_t address) final {
|
||||
return { m_ram, 0x0000, EightBit::MemoryMapping::ReadWrite };
|
||||
}
|
||||
|
||||
private:
|
||||
|
@ -37,8 +37,8 @@ namespace Fuse {
|
||||
EightBit::register16_t actual, EightBit::register16_t expected) const;
|
||||
|
||||
protected:
|
||||
virtual uint8_t& reference(uint16_t address) final {
|
||||
return m_ram.reference(address);
|
||||
virtual EightBit::MemoryMapping mapping(uint16_t address) final {
|
||||
return { m_ram, 0x0000, EightBit::MemoryMapping::ReadWrite };
|
||||
}
|
||||
|
||||
public:
|
||||
|
@ -54,7 +54,7 @@ namespace EightBit {
|
||||
int runVerticalBlankLines();
|
||||
|
||||
protected:
|
||||
virtual uint8_t& reference(uint16_t address) override;
|
||||
virtual MemoryMapping mapping(uint16_t address) override;
|
||||
|
||||
private:
|
||||
LR35902 m_cpu;
|
||||
@ -63,8 +63,10 @@ namespace EightBit {
|
||||
std::vector<Rom> m_gameRomBanks; // 0x0000 - 0x3fff, 0x4000 - 0x7fff (switchable)
|
||||
Ram m_videoRam = 0x2000; // 0x8000 - 0x9fff
|
||||
std::vector<Ram> m_ramBanks; // 0xa000 - 0xbfff (switchable)
|
||||
Rom m_unmapped2000 = 0x2000; // 0xa000 - 0xbfff
|
||||
Ram m_lowInternalRam = 0x2000; // 0xc000 - 0xdfff (mirrored at 0xe000)
|
||||
Ram m_oamRam = 0xa0; // 0xfe00 - 0xfe9f
|
||||
Rom m_unmapped60 = 0x60; // 0xfea0 - 0xfeff
|
||||
IoRegisters m_ioPorts; // 0xff00 - 0xff7f
|
||||
Ram m_highInternalRam = 0x80; // 0xff80 - 0xffff
|
||||
|
||||
|
@ -6,6 +6,18 @@ EightBit::GameBoy::Bus::Bus() noexcept
|
||||
: m_cpu(*this),
|
||||
m_ioPorts(*this) {
|
||||
WrittenByte.connect(std::bind(&GameBoy::Bus::Bus_WrittenByte, this, std::placeholders::_1));
|
||||
|
||||
{
|
||||
std::vector<uint8_t> content(m_unmapped2000.size());
|
||||
std::fill(content.begin(), content.end(), 0xff);
|
||||
m_unmapped2000.load(content);
|
||||
}
|
||||
|
||||
{
|
||||
std::vector<uint8_t> content(m_unmapped60.size());
|
||||
std::fill(content.begin(), content.end(), 0xff);
|
||||
m_unmapped60.load(content);
|
||||
}
|
||||
}
|
||||
|
||||
void EightBit::GameBoy::Bus::reset() {
|
||||
@ -149,30 +161,34 @@ void EightBit::GameBoy::Bus::validateCartridgeType() {
|
||||
}
|
||||
}
|
||||
|
||||
uint8_t& EightBit::GameBoy::Bus::reference(uint16_t address) {
|
||||
EightBit::MemoryMapping EightBit::GameBoy::Bus::mapping(uint16_t address) {
|
||||
|
||||
if ((address < 0x100) && IO().bootRomEnabled())
|
||||
return DATA() = m_bootRom.peek(address);
|
||||
return { m_bootRom, 0x0000, MemoryMapping::ReadOnly };
|
||||
if ((address < 0x4000) && gameRomEnabled())
|
||||
return DATA() = m_gameRomBanks[0].peek(address);
|
||||
return { m_gameRomBanks[0], 0x0000, MemoryMapping::ReadOnly };
|
||||
if ((address < 0x8000) && gameRomEnabled())
|
||||
return DATA() = m_gameRomBanks[m_romBank].peek(address - 0x4000);
|
||||
return { m_gameRomBanks[m_romBank], 0x4000, MemoryMapping::ReadOnly };
|
||||
|
||||
if (address < 0xa000)
|
||||
return VRAM().reference(address - 0x8000);
|
||||
if (address < 0xc000)
|
||||
return m_ramBanks.size() == 0 ? DATA() = 0xff : m_ramBanks[m_ramBank].reference(address - 0xa000);
|
||||
return { VRAM(), 0x8000, MemoryMapping::ReadWrite };
|
||||
if (address < 0xc000) {
|
||||
if (m_ramBanks.size() == 0)
|
||||
return { m_unmapped2000, 0xa000, MemoryMapping::ReadOnly };
|
||||
else
|
||||
return { m_ramBanks[m_ramBank], 0xa000, MemoryMapping::ReadWrite };
|
||||
}
|
||||
if (address < 0xe000)
|
||||
return m_lowInternalRam.reference(address - 0xc000);
|
||||
return { m_lowInternalRam, 0xc000, MemoryMapping::ReadWrite };
|
||||
if (address < 0xfe00)
|
||||
return m_lowInternalRam.reference(address - 0xe000); // Low internal RAM mirror
|
||||
return { m_lowInternalRam, 0xe000, MemoryMapping::ReadWrite }; // Low internal RAM mirror
|
||||
if (address < 0xfea0)
|
||||
return OAMRAM().reference(address - 0xfe00);
|
||||
return { OAMRAM(), 0xfe00, MemoryMapping::ReadWrite };
|
||||
if (address < IoRegisters::BASE)
|
||||
return DATA() = 0xff;
|
||||
return { m_unmapped60, 0xfea0, MemoryMapping::ReadOnly };
|
||||
if (address < 0xff80)
|
||||
return IO().reference(address - IoRegisters::BASE);
|
||||
return m_highInternalRam.reference(address - 0xff80);
|
||||
return { IO(), IoRegisters::BASE, MemoryMapping::ReadWrite };
|
||||
return { m_highInternalRam, 0xff80, MemoryMapping::ReadWrite };
|
||||
}
|
||||
|
||||
int EightBit::GameBoy::Bus::runRasterLines() {
|
||||
|
@ -20,8 +20,8 @@ public:
|
||||
void initialise();
|
||||
|
||||
protected:
|
||||
virtual uint8_t& reference(uint16_t address) final {
|
||||
return m_ram.reference(address);
|
||||
virtual EightBit::MemoryMapping mapping(uint16_t address) final {
|
||||
return { m_ram, 0x0000, EightBit::MemoryMapping::ReadWrite };
|
||||
}
|
||||
|
||||
private:
|
||||
|
@ -9,8 +9,6 @@ EightBit::mc6809::mc6809(Bus& bus)
|
||||
|
||||
void EightBit::mc6809::powerOn() {
|
||||
Processor::powerOn();
|
||||
raise(NMI());
|
||||
raise(FIRQ());
|
||||
lower(BA());
|
||||
lower(BS());
|
||||
}
|
||||
@ -50,7 +48,8 @@ void EightBit::mc6809::handleRESET() {
|
||||
lower(BA());
|
||||
raise(BS());
|
||||
DP() = 0;
|
||||
setFlag(CC(), IF | FF); // Disable all IRQs
|
||||
setFlag(CC(), IF); // Disable IRQ
|
||||
setFlag(CC(), FF); // Disable FIRQ
|
||||
jump(getWordPaged(0xff, RESETvector));
|
||||
}
|
||||
|
||||
@ -59,7 +58,8 @@ void EightBit::mc6809::handleNMI() {
|
||||
lower(BA());
|
||||
raise(BS());
|
||||
saveEntireRegisterState();
|
||||
setFlag(CC(), IF | FF); // Disable all IRQs
|
||||
setFlag(CC(), IF); // Disable IRQ
|
||||
setFlag(CC(), FF); // Disable FIRQ
|
||||
jump(getWordPaged(0xff, NMIvector));
|
||||
addCycles(21);
|
||||
}
|
||||
@ -81,7 +81,8 @@ void EightBit::mc6809::handleFIRQ() {
|
||||
clearFlag(CC(), EF);
|
||||
pushWordS(PC());
|
||||
pushS(CC());
|
||||
setFlag(CC(), IF | FF); // Disable all IRQs
|
||||
setFlag(CC(), IF); // Disable IRQ
|
||||
setFlag(CC(), FF); // Disable FIRQ
|
||||
jump(getWordPaged(0xff, FIRQvector));
|
||||
addCycles(12);
|
||||
}
|
||||
|
@ -1,11 +1,6 @@
|
||||
#include "stdafx.h"
|
||||
#include "Board.h"
|
||||
|
||||
#include <iostream>
|
||||
#include <sstream>
|
||||
#include <iomanip>
|
||||
#include <assert.h>
|
||||
|
||||
Board::Board(const Configuration& configuration)
|
||||
: m_configuration(configuration),
|
||||
m_cpu(EightBit::mc6809(*this)),
|
||||
@ -15,9 +10,7 @@ void Board::initialise() {
|
||||
|
||||
const auto directory = m_configuration.getRomDirectory() + "\\";
|
||||
|
||||
m_extendedBasic.load(directory + "extbas11.rom");
|
||||
m_colorBasic.load(directory + "bas12.rom");
|
||||
m_diskBasic.load(directory + "disk11.rom");
|
||||
loadHexFile(directory + "ExBasROM.hex");
|
||||
|
||||
if (m_configuration.isDebugMode()) {
|
||||
CPU().ExecutingInstruction.connect(std::bind(&Board::Cpu_ExecutingInstruction_Debug, this, std::placeholders::_1));
|
||||
@ -25,6 +18,8 @@ void Board::initialise() {
|
||||
}
|
||||
|
||||
CPU().powerOn();
|
||||
CPU().raise(CPU().NMI());
|
||||
CPU().raise(CPU().FIRQ());
|
||||
CPU().reset();
|
||||
}
|
||||
|
||||
@ -38,33 +33,13 @@ void Board::Cpu_ExecutedInstruction_Debug(EightBit::mc6809&) {
|
||||
std::cout << m_disassembler.trace(m_disassembleAt) << std::endl;
|
||||
}
|
||||
|
||||
uint8_t& Board::reference(uint16_t address) {
|
||||
EightBit::MemoryMapping Board::mapping(uint16_t address) {
|
||||
|
||||
// 0x0000 - 0x7fff
|
||||
if (address < 0x8000)
|
||||
return m_ram.reference(address);
|
||||
return { m_ram, 0x0000, EightBit::MemoryMapping::ReadWrite };
|
||||
|
||||
// 0x8000 - 0x9fff
|
||||
if (address < 0xa000)
|
||||
return DATA() = m_extendedBasic.peek(address - 0x8000);
|
||||
|
||||
// 0xa000 - 0xbfff
|
||||
if (address < 0xc000)
|
||||
return DATA() = m_colorBasic.peek(address - 0xa000);
|
||||
return { m_io, 0x8000, EightBit::MemoryMapping::ReadWrite };
|
||||
|
||||
// 0xc000 - 0xdfff
|
||||
if (address < 0xe000)
|
||||
return DATA() = m_diskBasic.peek(address - 0xc000);
|
||||
|
||||
// 0xe000 - 0xfeff
|
||||
if (address < 0xff00)
|
||||
return DATA() = 0xff;
|
||||
|
||||
// 0xe000 - 0xfeff
|
||||
if (address < 0xfff0)
|
||||
return m_io.reference(address - 0xff00);
|
||||
|
||||
// 0xfff0 - 0xffff
|
||||
const auto offset = address - 0xfff0;
|
||||
return DATA() = m_colorBasic.peek(0x1ff0 + offset);
|
||||
return { m_rom, 0xc000, EightBit::MemoryMapping::ReadOnly };
|
||||
}
|
||||
|
@ -18,18 +18,15 @@ public:
|
||||
void initialise();
|
||||
|
||||
protected:
|
||||
virtual uint8_t& reference(uint16_t address) final;
|
||||
virtual EightBit::MemoryMapping mapping(uint16_t address) final;
|
||||
|
||||
private:
|
||||
const Configuration& m_configuration;
|
||||
EightBit::Ram m_ram = 0x8000; // 0000 - 7FFF, 32K RAM
|
||||
EightBit::Rom m_extendedBasic = 0x2000; // 8000 - 9FFF, 8K Extended BASIC ROM
|
||||
EightBit::Rom m_colorBasic = 0x2000; // A000 - BFFF, 8K Color BASIC ROM
|
||||
EightBit::Rom m_cartridge = 0x2000; // C000 - DFFF, 8K Cartridge ROM
|
||||
EightBit::Rom m_diskBasic = 0x2000; // C000 - DFFF, 8K Disk BASIC ROM
|
||||
// E000 - FEFF, Unused
|
||||
EightBit::Ram m_io = 0xf0; // FF00 - FFEF, I/O Registers
|
||||
// FFF0 - FFFF, Interrupt vectors (mapped from BFF0-BFFF)
|
||||
// 8000 - 9FFF, 8K unused
|
||||
EightBit::Ram m_io = 0x2000; // A000 - BFFF, 8K serial interface, minimally decoded
|
||||
EightBit::Rom m_rom = 0x4000; // C000 - FFFF, 16K ROM
|
||||
|
||||
EightBit::mc6809 m_cpu;
|
||||
EightBit::Disassembly m_disassembler;
|
||||
|
||||
|
@ -15,5 +15,5 @@ public:
|
||||
|
||||
private:
|
||||
bool m_debugMode = false;
|
||||
std::string m_romDirectory = "roms\\coco2h";
|
||||
std::string m_romDirectory = "roms\\searle";
|
||||
};
|
||||
|
@ -13,6 +13,7 @@ int main(int argc, char* argv[]) {
|
||||
#ifdef _DEBUG
|
||||
configuration.setDebugMode(true);
|
||||
#endif
|
||||
configuration.setDebugMode(true);
|
||||
|
||||
EightBit::TestHarness<Configuration, Board> harness(configuration);
|
||||
harness.initialise();
|
||||
|
@ -36,8 +36,8 @@ namespace Fuse {
|
||||
EightBit::register16_t actual, EightBit::register16_t expected) const;
|
||||
|
||||
protected:
|
||||
virtual uint8_t& reference(uint16_t address) final {
|
||||
return m_ram.reference(address);
|
||||
virtual EightBit::MemoryMapping mapping(uint16_t address) final {
|
||||
return { m_ram, 0x0000, EightBit::MemoryMapping::ReadWrite };
|
||||
}
|
||||
|
||||
public:
|
||||
|
@ -21,8 +21,8 @@ public:
|
||||
void initialise();
|
||||
|
||||
protected:
|
||||
virtual uint8_t& reference(uint16_t address) final {
|
||||
return m_ram.reference(address);
|
||||
virtual EightBit::MemoryMapping mapping(uint16_t address) final {
|
||||
return { m_ram, 0x0000, EightBit::MemoryMapping::ReadWrite };
|
||||
}
|
||||
|
||||
private:
|
||||
|
10
inc/Bus.h
10
inc/Bus.h
@ -1,10 +1,14 @@
|
||||
#pragma once
|
||||
|
||||
#include <cstdint>
|
||||
#include <string>
|
||||
#include <map>
|
||||
#include <vector>
|
||||
|
||||
#include "Signal.h"
|
||||
#include "Register.h"
|
||||
#include "EventArgs.h"
|
||||
#include "MemoryMapping.h"
|
||||
|
||||
namespace EightBit {
|
||||
class Bus {
|
||||
@ -44,10 +48,14 @@ namespace EightBit {
|
||||
}
|
||||
|
||||
protected:
|
||||
virtual uint8_t& reference(uint16_t address) = 0;
|
||||
virtual MemoryMapping mapping(uint16_t address) = 0;
|
||||
uint8_t& reference(uint16_t address);
|
||||
uint8_t& reference(register16_t address) { return reference(address.word); }
|
||||
uint8_t& reference() { return reference(ADDRESS()); }
|
||||
|
||||
static std::map<uint16_t, std::vector<uint8_t>> parseHexFile(std::string path);
|
||||
void loadHexFile(std::string path);
|
||||
|
||||
private:
|
||||
uint8_t m_data = 0xff;
|
||||
register16_t m_address = 0xffff;
|
||||
|
16
inc/MemoryMapping.h
Normal file
16
inc/MemoryMapping.h
Normal file
@ -0,0 +1,16 @@
|
||||
#pragma once
|
||||
|
||||
#include <cstdint>
|
||||
|
||||
#include "Memory.h"
|
||||
|
||||
namespace EightBit {
|
||||
struct MemoryMapping {
|
||||
|
||||
enum AccessLevel { Unknown, ReadWrite, ReadOnly };
|
||||
|
||||
Memory& memory;
|
||||
uint16_t begin = 0xffff;
|
||||
AccessLevel access = Unknown;
|
||||
};
|
||||
}
|
79
src/Bus.cpp
79
src/Bus.cpp
@ -1,5 +1,12 @@
|
||||
#include "stdafx.h"
|
||||
#include "Bus.h"
|
||||
#include "Ram.h"
|
||||
#include "EightBitCompilerDefinitions.h"
|
||||
|
||||
#include <fstream>
|
||||
#include <cstdlib>
|
||||
#include <stdexcept>
|
||||
#include <cassert>
|
||||
|
||||
uint8_t EightBit::Bus::read() {
|
||||
ReadingByte.fire(EventArgs::empty());
|
||||
@ -18,3 +25,75 @@ void EightBit::Bus::write(const uint8_t value) {
|
||||
DATA() = value;
|
||||
write();
|
||||
}
|
||||
|
||||
void EightBit::Bus::loadHexFile(const std::string path) {
|
||||
const auto chunks = parseHexFile(path);
|
||||
for (const auto& chunk : chunks) {
|
||||
const auto address = chunk.first;
|
||||
const auto content = chunk.second;
|
||||
for (size_t i = 0; i != content.size(); ++i)
|
||||
write(address + i, content[i]);
|
||||
const auto mapped = mapping(address);
|
||||
mapped.memory.load(content, address - mapped.begin);
|
||||
}
|
||||
}
|
||||
|
||||
std::map<uint16_t, std::vector<uint8_t>> EightBit::Bus::parseHexFile(const std::string path) {
|
||||
|
||||
std::ifstream file;
|
||||
file.open(path);
|
||||
|
||||
std::map<uint16_t, std::vector<uint8_t>> returned;
|
||||
|
||||
bool eof = false;
|
||||
while (!file.eof() && !eof) {
|
||||
|
||||
std::string line;
|
||||
std::getline(file, line);
|
||||
|
||||
const auto colon = line.substr(0, 1);
|
||||
if (colon != ":")
|
||||
throw std::out_of_range("Invalid hex file: line does not begin with a colon");
|
||||
|
||||
const auto countString = line.substr(1, 2);
|
||||
const auto count = (uint8_t)strtoul(countString.c_str(), nullptr, 16);
|
||||
|
||||
const auto addressString = line.substr(3, 4);
|
||||
const auto address = (uint16_t)strtoul(addressString.c_str(), nullptr, 16);
|
||||
|
||||
const auto recordTypeString = line.substr(7, 2);
|
||||
const auto recordType = strtoul(recordTypeString.c_str(), nullptr, 16);
|
||||
|
||||
switch (recordType) {
|
||||
case 0x00: {
|
||||
std::vector<uint8_t> data(count);
|
||||
const auto requiredLength = 9 + 2 + (count * 2);
|
||||
if (line.length() != requiredLength)
|
||||
throw std::out_of_range("Invalid hex file: line is not the required length");
|
||||
for (int i = 0; i < count; ++i) {
|
||||
const auto position = 9 + i * 2;
|
||||
const auto datumString = line.substr(position, 2);
|
||||
const auto datum = (uint8_t)strtoul(datumString.c_str(), nullptr, 16);
|
||||
data[i] = datum;
|
||||
}
|
||||
returned[address] = data;
|
||||
}
|
||||
break;
|
||||
case 0x01:
|
||||
eof = true;
|
||||
break;
|
||||
default:
|
||||
throw std::out_of_range("Unhandled hex file record.");
|
||||
}
|
||||
}
|
||||
return returned;
|
||||
}
|
||||
|
||||
uint8_t& EightBit::Bus::reference(uint16_t address) {
|
||||
const auto mapped = mapping(address);
|
||||
const uint16_t offset = address - mapped.begin;
|
||||
if (mapped.access == MemoryMapping::ReadOnly)
|
||||
return DATA() = mapped.memory.peek(offset);
|
||||
Ram& ram = (Ram&)(mapped.memory);
|
||||
return ram.reference(offset);
|
||||
}
|
||||
|
@ -144,6 +144,7 @@
|
||||
<ClInclude Include="..\inc\IntelProcessor.h" />
|
||||
<ClInclude Include="..\inc\LittleEndianProcessor.h" />
|
||||
<ClInclude Include="..\inc\Memory.h" />
|
||||
<ClInclude Include="..\inc\MemoryMapping.h" />
|
||||
<ClInclude Include="..\inc\Processor.h" />
|
||||
<ClInclude Include="..\inc\Ram.h" />
|
||||
<ClInclude Include="..\inc\Register.h" />
|
||||
|
@ -53,6 +53,9 @@
|
||||
<ClInclude Include="..\inc\BigEndianProcessor.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\inc\MemoryMapping.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClCompile Include="stdafx.cpp">
|
||||
|
Loading…
x
Reference in New Issue
Block a user