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