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:
Adrian Conlon 2018-09-15 14:35:59 +01:00
parent a9adde6ea5
commit 7d840f1a42
17 changed files with 170 additions and 71 deletions

View File

@ -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:

View File

@ -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:

View File

@ -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

View File

@ -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() {

View File

@ -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:

View File

@ -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);
}

View File

@ -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 };
}

View File

@ -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;

View File

@ -15,5 +15,5 @@ public:
private:
bool m_debugMode = false;
std::string m_romDirectory = "roms\\coco2h";
std::string m_romDirectory = "roms\\searle";
};

View File

@ -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();

View File

@ -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:

View File

@ -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:

View File

@ -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
View 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;
};
}

View File

@ -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);
}

View File

@ -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" />

View File

@ -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">