255 lines
5.1 KiB
D
255 lines
5.1 KiB
D
module riscd.memory;
|
|
|
|
import std.format;
|
|
import std.file : readFile = read;
|
|
import std.traits;
|
|
|
|
|
|
interface MemoryDevice
|
|
{
|
|
@safe:
|
|
bool get(size_t addr, ref ubyte value);
|
|
bool set(size_t addr, ubyte value);
|
|
}
|
|
|
|
|
|
class RAM : MemoryDevice
|
|
{
|
|
@safe:
|
|
public:
|
|
this(size_t size)
|
|
{
|
|
data.length = size;
|
|
}
|
|
|
|
this(void[] data, bool copy = true) @system
|
|
{
|
|
if (copy)
|
|
this.data = cast(ubyte[]) data.dup;
|
|
else
|
|
this.data = cast(ubyte[]) data;
|
|
}
|
|
|
|
static RAM loadFile(string filename) @system
|
|
{
|
|
return new RAM(readFile(filename), false);
|
|
}
|
|
|
|
bool get(size_t addr, ref ubyte value) @nogc nothrow pure
|
|
{
|
|
if (data.length <= addr)
|
|
return false;
|
|
|
|
value = data[addr];
|
|
return true;
|
|
}
|
|
|
|
bool set(size_t addr, ubyte value) @nogc nothrow pure
|
|
{
|
|
if (data.length <= addr)
|
|
return false;
|
|
|
|
data[addr] = value;
|
|
return true;
|
|
}
|
|
|
|
protected:
|
|
ubyte[] data;
|
|
}
|
|
|
|
|
|
class ROM : MemoryDevice
|
|
{
|
|
@safe:
|
|
public:
|
|
this(const(void)[] data, bool copy = true)
|
|
{
|
|
if (copy)
|
|
this.data = cast(const(ubyte)[]) data.idup;
|
|
else
|
|
this.data = cast(const(ubyte)[]) data;
|
|
}
|
|
|
|
static ROM loadFile(string filename)
|
|
{
|
|
return new ROM(readFile(filename), false);
|
|
}
|
|
|
|
bool get(size_t addr, ref ubyte value) @nogc nothrow pure
|
|
{
|
|
if (data.length <= addr)
|
|
return false;
|
|
|
|
value = data[addr];
|
|
return true;
|
|
}
|
|
|
|
bool set(size_t addr, ubyte value) @nogc nothrow pure
|
|
{
|
|
return false;
|
|
}
|
|
|
|
protected:
|
|
const(ubyte)[] data;
|
|
}
|
|
|
|
|
|
class MMIO : MemoryDevice
|
|
{
|
|
@safe:
|
|
public:
|
|
this()
|
|
{
|
|
}
|
|
|
|
void map(MemoryDevice device, size_t origin, size_t length)
|
|
{
|
|
// Check for colliding mappings
|
|
foreach (mapping; mappings)
|
|
if (
|
|
// Is mapping.origin inside new mapping?
|
|
(mapping.origin > origin && mapping.origin < origin + length)
|
|
||
|
|
// Is origin inside existing mapping?
|
|
(origin > mapping.origin && origin < mapping.origin + mapping.length)
|
|
)
|
|
// We collide, throw
|
|
throw new Exception(
|
|
format("Trying to map %p over existing %p", origin, mapping.origin)
|
|
);
|
|
|
|
// No collisons? Cool, craete a new mapping
|
|
mappings ~= MemoryMapping(device, origin, length);
|
|
}
|
|
|
|
void unmap(size_t origin)
|
|
{
|
|
size_t index = findMapping(origin);
|
|
|
|
// Could not find the mapping
|
|
if (index == -1)
|
|
throw new Exception(format("Nothing mapped at %p", origin));
|
|
|
|
if (mappings.length != 1)
|
|
mappings[index] = mappings[$];
|
|
|
|
mappings.length -= 1;
|
|
}
|
|
|
|
bool get(size_t addr, ref ubyte value)
|
|
{
|
|
size_t index = findMapping(addr);
|
|
|
|
if (index == -1)
|
|
return false;
|
|
|
|
return mappings[index].device.get( addr - mappings[index].origin, value);
|
|
}
|
|
|
|
bool set(size_t addr, ubyte value)
|
|
{
|
|
size_t index = findMapping(addr);
|
|
|
|
if (index == -1)
|
|
return false;
|
|
|
|
return mappings[index].device.set( addr - mappings[index].origin, value);
|
|
|
|
}
|
|
|
|
protected:
|
|
struct MemoryMapping
|
|
{
|
|
MemoryDevice device;
|
|
size_t origin, length;
|
|
}
|
|
|
|
size_t findMapping(size_t origin) @nogc pure nothrow
|
|
{
|
|
// Is there only one mapping, and it's the correct one?
|
|
if (mappings.length == 1 && mappings[0].origin == origin)
|
|
return 0;
|
|
|
|
// Look for the mapping
|
|
for (size_t i = 0; i < mappings.length; i++)
|
|
if (mappings[i].origin == origin)
|
|
return i;
|
|
|
|
// Could not find the mapping
|
|
return -1;
|
|
}
|
|
|
|
MemoryMapping[] mappings;
|
|
}
|
|
|
|
|
|
// TODO: Add endianess support
|
|
class Memory
|
|
{
|
|
@safe:
|
|
public:
|
|
this(MemoryDevice device)
|
|
{
|
|
this.device = device;
|
|
}
|
|
|
|
bool get(T)(size_t addr, ref T value) @trusted
|
|
if (isBasicType!T)
|
|
{
|
|
return get(addr, (&value)[0..1]);
|
|
}
|
|
|
|
bool set(T)(size_t addr, T value) @trusted
|
|
if (isBasicType!T)
|
|
{
|
|
return set(addr, (&value)[0..1]);
|
|
}
|
|
|
|
bool get(T)(size_t addr, ref T value) @system
|
|
if (!isBasicType!T)
|
|
{
|
|
return get(addr, (&value)[0..1]);
|
|
}
|
|
|
|
bool set(T)(size_t addr, T value) @system
|
|
if (!isBasicType!T)
|
|
{
|
|
return set(addr, (&value)[0..1]);
|
|
}
|
|
|
|
bool get(size_t addr, void[] buffer) @system
|
|
{
|
|
ubyte[] buff = cast(ubyte[])buffer;
|
|
|
|
for (size_t i = 0; i < buff.length; i++)
|
|
if (!get(addr+i, buff[i]))
|
|
return false;
|
|
|
|
return true;
|
|
}
|
|
|
|
bool set(size_t addr, const(void)[] buffer) @system
|
|
{
|
|
const(ubyte)[] buff = cast(const(ubyte)[])buffer;
|
|
|
|
for (size_t i = 0; i < buff.length; i++)
|
|
if (!set(addr+i, buff[i]))
|
|
return false;
|
|
|
|
return true;
|
|
}
|
|
|
|
bool get(size_t addr, ref ubyte value)
|
|
{
|
|
return device.get(addr, value);
|
|
}
|
|
|
|
bool set(size_t addr, ubyte value)
|
|
{
|
|
return device.set(addr, value);
|
|
}
|
|
|
|
protected:
|
|
MemoryDevice device;
|
|
}
|