mirror of
https://github.com/MoleskiCoder/EightBit.git
synced 2025-07-24 20:24:06 +00:00
Attempted move to a "BUS" oriented memory architecture (TBC!)
Signed-off-by: Adrian.Conlon <adrian.conlon@arup.com>
This commit is contained in:
@@ -16,7 +16,7 @@ namespace EightBit {
|
||||
CF = Bit0,
|
||||
};
|
||||
|
||||
Intel8080(Memory& memory, InputOutput& ports);
|
||||
Intel8080(Bus& bus, InputOutput& ports);
|
||||
|
||||
Signal<Intel8080> ExecutingInstruction;
|
||||
|
||||
|
@@ -168,8 +168,8 @@ std::string EightBit::Disassembler::disassemble(Intel8080& cpu) {
|
||||
|
||||
void EightBit::Disassembler::disassemble(std::ostringstream& output, Intel8080& cpu, uint16_t pc) {
|
||||
|
||||
auto& memory = cpu.getMemory();
|
||||
auto opcode = memory.peek(pc);
|
||||
auto& bus = cpu.BUS();
|
||||
auto opcode = bus.peek(pc);
|
||||
|
||||
output << hex(opcode);
|
||||
|
||||
@@ -180,11 +180,11 @@ void EightBit::Disassembler::disassemble(std::ostringstream& output, Intel8080&
|
||||
auto p = (y & 0b110) >> 1;
|
||||
auto q = (y & 1);
|
||||
|
||||
auto immediate = memory.peek(pc + 1);
|
||||
auto absolute = memory.peekWord(pc + 1);
|
||||
auto immediate = bus.peek(pc + 1);
|
||||
auto absolute = bus.peekWord(pc + 1);
|
||||
auto displacement = (int8_t)immediate;
|
||||
auto relative = pc + displacement + 2;
|
||||
auto indexedImmediate = memory.peek(pc + 1);
|
||||
auto indexedImmediate = bus.peek(pc + 1);
|
||||
|
||||
auto dumpCount = 0;
|
||||
|
||||
@@ -196,7 +196,7 @@ void EightBit::Disassembler::disassemble(std::ostringstream& output, Intel8080&
|
||||
x, y, z, p, q);
|
||||
|
||||
for (int i = 0; i < dumpCount; ++i)
|
||||
output << hex(memory.peek(pc + i + 1));
|
||||
output << hex(bus.peek(pc + i + 1));
|
||||
|
||||
output << '\t';
|
||||
m_formatter.parse(specification);
|
||||
|
@@ -1,8 +1,8 @@
|
||||
#include "stdafx.h"
|
||||
#include "Intel8080.h"
|
||||
|
||||
EightBit::Intel8080::Intel8080(Memory& memory, InputOutput& ports)
|
||||
: IntelProcessor(memory),
|
||||
EightBit::Intel8080::Intel8080(Bus& bus, InputOutput& ports)
|
||||
: IntelProcessor(bus),
|
||||
m_interrupt(false),
|
||||
m_ports(ports) {
|
||||
bc.word = de.word = hl.word = Mask16;
|
||||
@@ -225,7 +225,7 @@ void EightBit::Intel8080::xhtl() {
|
||||
MEMPTR().low = getByte(SP());
|
||||
setByte(L());
|
||||
L() = MEMPTR().low;
|
||||
m_memory.ADDRESS().word++;
|
||||
BUS().ADDRESS().word++;
|
||||
MEMPTR().high = getByte();
|
||||
setByte(H());
|
||||
H() = MEMPTR().high;
|
||||
|
101
inc/Bus.h
Normal file
101
inc/Bus.h
Normal file
@@ -0,0 +1,101 @@
|
||||
#pragma once
|
||||
|
||||
#include <cstdint>
|
||||
|
||||
#include "Signal.h"
|
||||
#include "AddressEventArgs.h"
|
||||
#include "Register.h"
|
||||
|
||||
namespace EightBit {
|
||||
class Bus {
|
||||
public:
|
||||
Bus() {}
|
||||
|
||||
Signal<AddressEventArgs> WrittenByte;
|
||||
Signal<AddressEventArgs> ReadByte;
|
||||
|
||||
register16_t& ADDRESS() { return m_address; }
|
||||
uint8_t& DATA() { return *m_data; }
|
||||
|
||||
uint8_t& placeDATA(uint8_t value) {
|
||||
m_temporary = value;
|
||||
m_data = &m_temporary;
|
||||
return DATA();
|
||||
}
|
||||
|
||||
uint8_t& referenceDATA(uint8_t& value) {
|
||||
m_data = &value;
|
||||
return DATA();
|
||||
}
|
||||
|
||||
uint8_t peek(uint16_t address) {
|
||||
bool rom;
|
||||
return reference(address, rom);
|
||||
}
|
||||
|
||||
void poke(uint16_t address, uint8_t value) {
|
||||
bool rom;
|
||||
reference(address, rom) = value;
|
||||
}
|
||||
|
||||
uint16_t peekWord(uint16_t address) {
|
||||
register16_t returned;
|
||||
returned.low = peek(address);
|
||||
returned.high = peek(address + 1);
|
||||
return returned.word;
|
||||
}
|
||||
|
||||
uint8_t read() {
|
||||
auto content = reference();
|
||||
fireReadBusEvent();
|
||||
return content;
|
||||
}
|
||||
|
||||
uint8_t read(uint16_t offset) {
|
||||
ADDRESS().word = offset;
|
||||
return read();
|
||||
}
|
||||
|
||||
uint8_t read(register16_t address) {
|
||||
ADDRESS() = address;
|
||||
return read();
|
||||
}
|
||||
|
||||
void write(uint8_t value) {
|
||||
reference() = value;
|
||||
fireWriteBusEvent();
|
||||
}
|
||||
|
||||
void write(uint16_t offset, uint8_t value) {
|
||||
ADDRESS().word = offset;
|
||||
write(value);
|
||||
}
|
||||
|
||||
void write(register16_t address, uint8_t value) {
|
||||
ADDRESS() = address;
|
||||
write(value);
|
||||
}
|
||||
|
||||
protected:
|
||||
void fireReadBusEvent() {
|
||||
ReadByte.fire(AddressEventArgs(ADDRESS().word, DATA()));
|
||||
}
|
||||
|
||||
void fireWriteBusEvent() {
|
||||
WrittenByte.fire(AddressEventArgs(ADDRESS().word, DATA()));
|
||||
}
|
||||
|
||||
virtual uint8_t& reference(uint16_t address, bool& rom) = 0;
|
||||
|
||||
uint8_t& reference() {
|
||||
bool rom;
|
||||
auto& value = reference(ADDRESS().word, rom);
|
||||
return rom ? placeDATA(value) : referenceDATA(value);
|
||||
}
|
||||
|
||||
private:
|
||||
uint8_t m_temporary; // Used to simulate ROM
|
||||
register16_t m_address;
|
||||
uint8_t* m_data;
|
||||
};
|
||||
}
|
@@ -56,7 +56,7 @@ namespace EightBit {
|
||||
uint8_t& L() { return HL().low; }
|
||||
|
||||
protected:
|
||||
IntelProcessor(Memory& memory);
|
||||
IntelProcessor(Bus& bus);
|
||||
|
||||
template<class T> static void adjustSign(uint8_t& f, uint8_t value) {
|
||||
setFlag(f, T::SF, value & T::SF);
|
||||
@@ -124,21 +124,21 @@ namespace EightBit {
|
||||
//
|
||||
|
||||
void memptrReference() {
|
||||
m_memory.ADDRESS() = MEMPTR();
|
||||
BUS().ADDRESS() = MEMPTR();
|
||||
MEMPTR().word++;
|
||||
}
|
||||
|
||||
virtual void getWordViaMemptr(register16_t& value) {
|
||||
memptrReference();
|
||||
value.low = getByte();
|
||||
m_memory.ADDRESS().word++;
|
||||
BUS().ADDRESS().word++;
|
||||
value.high = getByte();
|
||||
}
|
||||
|
||||
virtual void setWordViaMemptr(register16_t value) {
|
||||
memptrReference();
|
||||
setByte(value.low);
|
||||
m_memory.ADDRESS().word++;
|
||||
BUS().ADDRESS().word++;
|
||||
setByte(value.high);
|
||||
}
|
||||
|
||||
|
140
inc/Memory.h
140
inc/Memory.h
@@ -1,143 +1,31 @@
|
||||
#pragma once
|
||||
|
||||
#include <array>
|
||||
#include <cstdint>
|
||||
#include <string>
|
||||
|
||||
#include "Signal.h"
|
||||
#include "AddressEventArgs.h"
|
||||
|
||||
#ifdef __BYTE_ORDER__
|
||||
# if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__
|
||||
# define HOST_LITTLE_ENDIAN
|
||||
# endif
|
||||
# if __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__
|
||||
# define HOST_BIG_ENDIAN
|
||||
# endif
|
||||
#else
|
||||
# if defined(_M_X64) || defined(_M_IX86)
|
||||
# define HOST_LITTLE_ENDIAN
|
||||
# else
|
||||
# define HOST_BIG_ENDIAN
|
||||
# endif
|
||||
#endif
|
||||
#include <vector>
|
||||
|
||||
namespace EightBit {
|
||||
|
||||
typedef union {
|
||||
struct {
|
||||
#ifdef HOST_LITTLE_ENDIAN
|
||||
uint8_t low;
|
||||
uint8_t high;
|
||||
#endif
|
||||
#ifdef HOST_BIG_ENDIAN
|
||||
uint8_t high;
|
||||
uint8_t low;
|
||||
#endif
|
||||
};
|
||||
uint16_t word;
|
||||
} register16_t;
|
||||
|
||||
class Memory {
|
||||
public:
|
||||
Memory(uint16_t addressMask);
|
||||
|
||||
// Only fired with read/write methods
|
||||
Signal<AddressEventArgs> WrittenByte;
|
||||
Signal<AddressEventArgs> ReadByte;
|
||||
|
||||
register16_t& ADDRESS() { return m_address; }
|
||||
uint8_t& DATA() { return *m_data; }
|
||||
|
||||
uint8_t& placeDATA(uint8_t value) {
|
||||
m_temporary = value;
|
||||
m_data = &m_temporary;
|
||||
return DATA();
|
||||
Memory(size_t size)
|
||||
: m_bytes(size) {
|
||||
}
|
||||
|
||||
uint8_t& referenceDATA(uint8_t& value) {
|
||||
m_data = &value;
|
||||
return DATA();
|
||||
}
|
||||
|
||||
uint8_t peek(uint16_t address);
|
||||
void poke(uint16_t address, uint8_t value);
|
||||
|
||||
uint16_t peekWord(uint16_t address);
|
||||
|
||||
virtual int effectiveAddress(int address) const {
|
||||
return address & m_addressMask;
|
||||
}
|
||||
|
||||
uint8_t read() {
|
||||
auto content = reference();
|
||||
fireReadBusEvent();
|
||||
return content;
|
||||
}
|
||||
|
||||
uint8_t read(uint16_t offset) {
|
||||
ADDRESS().word = offset;
|
||||
return read();
|
||||
}
|
||||
|
||||
uint8_t read(register16_t address) {
|
||||
ADDRESS() = address;
|
||||
return read();
|
||||
}
|
||||
|
||||
void write(uint8_t value) {
|
||||
reference() = value;
|
||||
fireWriteBusEvent();
|
||||
}
|
||||
|
||||
void write(uint16_t offset, uint8_t value) {
|
||||
ADDRESS().word = offset;
|
||||
write(value);
|
||||
}
|
||||
|
||||
void write(register16_t address, uint8_t value) {
|
||||
ADDRESS() = address;
|
||||
write(value);
|
||||
}
|
||||
|
||||
virtual void clear();
|
||||
void loadRom(const std::string& path, uint16_t offset);
|
||||
void loadRam(const std::string& path, uint16_t offset);
|
||||
|
||||
void lock(int address, int size) {
|
||||
std::fill(m_locked.begin() + address, m_locked.begin() + address + size, true);
|
||||
void load(const std::string& path, uint16_t offset = 0) {
|
||||
loadBinary(path, m_bytes, offset, m_bytes.size() - offset);
|
||||
}
|
||||
|
||||
protected:
|
||||
std::vector<uint8_t> m_bus;
|
||||
std::vector<bool> m_locked;
|
||||
std::vector<uint8_t> m_bytes;
|
||||
|
||||
uint16_t m_addressMask; // Mirror
|
||||
uint8_t m_temporary; // Used to simulate ROM
|
||||
register16_t m_address;
|
||||
uint8_t* m_data;
|
||||
uint8_t read(uint16_t address) const {
|
||||
return m_bytes[address];
|
||||
}
|
||||
|
||||
void write(uint16_t address, uint8_t value) {
|
||||
m_bytes[address] = value;
|
||||
}
|
||||
|
||||
private:
|
||||
static int loadBinary(const std::string& path, std::vector<uint8_t>& output, int offset = 0, int maximumSize = -1);
|
||||
|
||||
void fireReadBusEvent() {
|
||||
ReadByte.fire(AddressEventArgs(ADDRESS().word, DATA()));
|
||||
}
|
||||
|
||||
void fireWriteBusEvent() {
|
||||
WrittenByte.fire(AddressEventArgs(ADDRESS().word, DATA()));
|
||||
}
|
||||
|
||||
virtual uint8_t& reference(uint16_t address, bool& rom) {
|
||||
rom = m_locked[address];
|
||||
return m_bus[address];
|
||||
}
|
||||
|
||||
uint8_t& reference() {
|
||||
bool rom;
|
||||
auto& value = reference(ADDRESS().word, rom);
|
||||
return rom ? placeDATA(value) : referenceDATA(value);
|
||||
}
|
||||
|
||||
int loadMemory(const std::string& path, uint16_t offset);
|
||||
};
|
||||
}
|
@@ -2,7 +2,8 @@
|
||||
|
||||
#include <cstdint>
|
||||
|
||||
#include "Memory.h"
|
||||
#include "Bus.h"
|
||||
#include "Register.h"
|
||||
|
||||
#ifdef _MSC_VER
|
||||
# define UNREACHABLE __assume(0)
|
||||
@@ -61,7 +62,7 @@ namespace EightBit {
|
||||
static int promoteNibble(int value) { return value << 4; }
|
||||
static int demoteNibble(int value) { return highNibble(value); }
|
||||
|
||||
Memory& getMemory() { return m_memory; }
|
||||
Bus& BUS() { return m_bus; }
|
||||
|
||||
register16_t& PC() { return pc; }
|
||||
register16_t& MEMPTR() { return m_memptr; }
|
||||
@@ -95,9 +96,9 @@ namespace EightBit {
|
||||
static void clearFlag(uint8_t& f, int flag, uint32_t condition) { clearFlag(f, flag, condition != 0); }
|
||||
static void clearFlag(uint8_t& f, int flag, bool condition) { condition ? clearFlag(f, flag) : setFlag(f, flag); }
|
||||
|
||||
Processor(Memory& memory);
|
||||
Processor(Bus& memory);
|
||||
|
||||
Memory& m_memory;
|
||||
Bus& m_bus;
|
||||
int cycles;
|
||||
|
||||
virtual uint8_t fetchByte() {
|
||||
@@ -119,11 +120,11 @@ namespace EightBit {
|
||||
return execute(fetchByte());
|
||||
}
|
||||
|
||||
uint8_t getByte() { return m_memory.read(); }
|
||||
template<class T> uint8_t getByte(T offset) { return m_memory.read(offset); }
|
||||
uint8_t getByte() { return BUS().read(); }
|
||||
template<class T> uint8_t getByte(T offset) { return BUS().read(offset); }
|
||||
|
||||
void setByte(uint8_t value) { m_memory.write(value); }
|
||||
template<class T> void setByte(T offset, uint8_t value) { m_memory.write(offset, value); }
|
||||
void setByte(uint8_t value) { BUS().write(value); }
|
||||
template<class T> void setByte(T offset, uint8_t value) { BUS().write(offset, value); }
|
||||
|
||||
virtual void push(uint8_t value) = 0;
|
||||
virtual uint8_t pop() = 0;
|
||||
|
25
inc/Ram.h
Normal file
25
inc/Ram.h
Normal file
@@ -0,0 +1,25 @@
|
||||
#pragma once
|
||||
|
||||
#include <cstdint>
|
||||
#include "Memory.h"
|
||||
|
||||
namespace EightBit {
|
||||
class Ram : public Memory {
|
||||
public:
|
||||
Ram(size_t size)
|
||||
: Memory(size) {
|
||||
}
|
||||
|
||||
uint8_t peek(uint16_t address) const {
|
||||
return read(address);
|
||||
}
|
||||
|
||||
void poke(uint16_t address, uint8_t value) {
|
||||
write(address, value);
|
||||
}
|
||||
|
||||
uint8_t& reference(uint16_t address) {
|
||||
return m_bytes[address];
|
||||
}
|
||||
};
|
||||
}
|
34
inc/Register.h
Normal file
34
inc/Register.h
Normal file
@@ -0,0 +1,34 @@
|
||||
#pragma once
|
||||
|
||||
#include <cstdint>
|
||||
|
||||
#ifdef __BYTE_ORDER__
|
||||
# if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__
|
||||
# define HOST_LITTLE_ENDIAN
|
||||
# endif
|
||||
# if __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__
|
||||
# define HOST_BIG_ENDIAN
|
||||
# endif
|
||||
#else
|
||||
# if defined(_M_X64) || defined(_M_IX86)
|
||||
# define HOST_LITTLE_ENDIAN
|
||||
# else
|
||||
# define HOST_BIG_ENDIAN
|
||||
# endif
|
||||
#endif
|
||||
|
||||
namespace EightBit {
|
||||
typedef union {
|
||||
struct {
|
||||
#ifdef HOST_LITTLE_ENDIAN
|
||||
uint8_t low;
|
||||
uint8_t high;
|
||||
#endif
|
||||
#ifdef HOST_BIG_ENDIAN
|
||||
uint8_t high;
|
||||
uint8_t low;
|
||||
#endif
|
||||
};
|
||||
uint16_t word;
|
||||
} register16_t;
|
||||
}
|
21
inc/Rom.h
Normal file
21
inc/Rom.h
Normal file
@@ -0,0 +1,21 @@
|
||||
#pragma once
|
||||
|
||||
#include <cstdint>
|
||||
#include "Memory.h"
|
||||
|
||||
namespace EightBit {
|
||||
class Rom : public Memory {
|
||||
public:
|
||||
Rom(size_t size)
|
||||
: Memory(size) {
|
||||
}
|
||||
|
||||
uint8_t peek(uint16_t address) const {
|
||||
return read(address);
|
||||
}
|
||||
|
||||
uint8_t& reference(uint16_t address) {
|
||||
return m_bytes[address];
|
||||
}
|
||||
};
|
||||
}
|
@@ -139,12 +139,16 @@
|
||||
</ItemDefinitionGroup>
|
||||
<ItemGroup>
|
||||
<ClInclude Include="..\inc\AddressEventArgs.h" />
|
||||
<ClInclude Include="..\inc\Bus.h" />
|
||||
<ClInclude Include="..\inc\EventArgs.h" />
|
||||
<ClInclude Include="..\inc\InputOutput.h" />
|
||||
<ClInclude Include="..\inc\IntelProcessor.h" />
|
||||
<ClInclude Include="..\inc\Memory.h" />
|
||||
<ClInclude Include="..\inc\PortEventArgs.h" />
|
||||
<ClInclude Include="..\inc\Processor.h" />
|
||||
<ClInclude Include="..\inc\Ram.h" />
|
||||
<ClInclude Include="..\inc\Register.h" />
|
||||
<ClInclude Include="..\inc\Rom.h" />
|
||||
<ClInclude Include="..\inc\Signal.h" />
|
||||
<ClInclude Include="..\inc\TestHarness.h" />
|
||||
<ClInclude Include="stdafx.h" />
|
||||
|
@@ -41,6 +41,18 @@
|
||||
<ClInclude Include="..\inc\PortEventArgs.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\inc\Bus.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\inc\Ram.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\inc\Rom.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\inc\Register.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClCompile Include="stdafx.cpp">
|
||||
|
@@ -1,8 +1,8 @@
|
||||
#include "stdafx.h"
|
||||
#include "IntelProcessor.h"
|
||||
|
||||
EightBit::IntelProcessor::IntelProcessor(Memory& memory)
|
||||
: Processor(memory) {
|
||||
EightBit::IntelProcessor::IntelProcessor(Bus& bus)
|
||||
: Processor(bus) {
|
||||
SP().word = Mask16;
|
||||
}
|
||||
|
||||
|
@@ -1,55 +1,8 @@
|
||||
#include "stdafx.h"
|
||||
#include "Memory.h"
|
||||
#include "Processor.h"
|
||||
|
||||
#include <iostream>
|
||||
#include <fstream>
|
||||
#include <algorithm>
|
||||
|
||||
EightBit::Memory::Memory(uint16_t addressMask)
|
||||
: m_addressMask(addressMask),
|
||||
m_temporary(0),
|
||||
m_data(nullptr) {
|
||||
clear();
|
||||
m_address.word = 0;
|
||||
m_bus.resize(0x10000);
|
||||
m_locked.resize(0x10000);
|
||||
}
|
||||
|
||||
uint8_t EightBit::Memory::peek(uint16_t address) {
|
||||
bool rom;
|
||||
return reference(address, rom);
|
||||
}
|
||||
|
||||
void EightBit::Memory::poke(uint16_t address, uint8_t value) {
|
||||
bool rom;
|
||||
reference(address, rom) = value;
|
||||
}
|
||||
|
||||
uint16_t EightBit::Memory::peekWord(uint16_t address) {
|
||||
register16_t returned;
|
||||
returned.low = peek(address);
|
||||
returned.high = peek(address + 1);
|
||||
return returned.word;
|
||||
}
|
||||
|
||||
void EightBit::Memory::clear() {
|
||||
std::fill(m_bus.begin(), m_bus.end(), 0);
|
||||
std::fill(m_locked.begin(), m_locked.end(), false);
|
||||
}
|
||||
|
||||
void EightBit::Memory::loadRom(const std::string& path, uint16_t offset) {
|
||||
auto size = loadMemory(path, offset);
|
||||
lock(offset, size);
|
||||
}
|
||||
|
||||
void EightBit::Memory::loadRam(const std::string& path, uint16_t offset) {
|
||||
loadMemory(path, offset);
|
||||
}
|
||||
|
||||
int EightBit::Memory::loadMemory(const std::string& path, uint16_t offset) {
|
||||
return loadBinary(path, m_bus, offset, 0x10000 - offset);
|
||||
}
|
||||
|
||||
int EightBit::Memory::loadBinary(const std::string& path, std::vector<uint8_t>& output, int offset, int maximumSize) {
|
||||
std::ifstream file;
|
||||
|
@@ -1,8 +1,8 @@
|
||||
#include "stdafx.h"
|
||||
#include "Processor.h"
|
||||
|
||||
EightBit::Processor::Processor(Memory& memory)
|
||||
: m_memory(memory),
|
||||
EightBit::Processor::Processor(Bus& bus)
|
||||
: m_bus(bus),
|
||||
cycles(0),
|
||||
m_halted(false),
|
||||
m_power(false) {
|
||||
|
Reference in New Issue
Block a user