Initial commit
This commit is contained in:
10
.editorconfig
Normal file
10
.editorconfig
Normal file
@ -0,0 +1,10 @@
|
||||
# Top-most EditorConfig file
|
||||
root = true
|
||||
|
||||
# Use same style for all files
|
||||
[*]
|
||||
indent_style = space
|
||||
indent_size = 4
|
||||
end_of_line = lf
|
||||
insert_final_newline = true
|
||||
trim_trailing_whitespace = true
|
||||
16
.gitignore
vendored
Normal file
16
.gitignore
vendored
Normal file
@ -0,0 +1,16 @@
|
||||
.dub
|
||||
docs.json
|
||||
__dummy.html
|
||||
docs/
|
||||
/riscd
|
||||
riscd.so
|
||||
riscd.dylib
|
||||
riscd.dll
|
||||
riscd.a
|
||||
riscd.lib
|
||||
riscd-test-*
|
||||
*.exe
|
||||
*.pdb
|
||||
*.o
|
||||
*.obj
|
||||
*.lst
|
||||
6
dub.sdl
Normal file
6
dub.sdl
Normal file
@ -0,0 +1,6 @@
|
||||
name "riscd"
|
||||
description "A simple RISC-V emulator"
|
||||
authors "Tomas"
|
||||
copyright "Copyright © 2025, Tomas"
|
||||
license "MIT"
|
||||
targetType "staticLibrary"
|
||||
493
source/riscd/cpu.d
Normal file
493
source/riscd/cpu.d
Normal file
@ -0,0 +1,493 @@
|
||||
module riscd.cpu;
|
||||
|
||||
import riscd.memory;
|
||||
import std.traits;
|
||||
|
||||
|
||||
private enum OPCODE
|
||||
{
|
||||
// Referenced from:
|
||||
// The RISC-V Instruction Set Manual Volume I: Unprivileged Architecture
|
||||
// Chapter 34. RV32/64G Instruction Set Listings
|
||||
|
||||
// 00
|
||||
LOAD = 0b0000011,
|
||||
LOAD_FP = 0b0000111,
|
||||
CUSTOM0 = 0b0001011,
|
||||
MISCMEM = 0b0001111,
|
||||
OPIMM = 0b0010011,
|
||||
AUIPC = 0b0010111,
|
||||
OPIMM32 = 0b0011011,
|
||||
|
||||
// 01
|
||||
STORE = 0b0100011,
|
||||
STORE_FP = 0b0100111,
|
||||
CUSTOM1 = 0b0101011,
|
||||
AMO = 0b0101111,
|
||||
OP = 0b0110011,
|
||||
LUI = 0b0110111,
|
||||
OP32 = 0b0111011,
|
||||
|
||||
// 10
|
||||
MADD = 0b1000011,
|
||||
MSUB = 0b1000111,
|
||||
NMSUB = 0b1001011,
|
||||
NMADD = 0b1001111,
|
||||
OP_FP = 0b1010011,
|
||||
OP_V = 0b1010111,
|
||||
CUSTOM2 = 0b1011011,
|
||||
|
||||
// 11
|
||||
BRANCH = 0b1100011,
|
||||
JALR = 0b1100111,
|
||||
_RESERVED = 0b1101011,
|
||||
JAL = 0b1101111,
|
||||
SYSTEM = 0b1110011,
|
||||
OP_VE = 0b1110111,
|
||||
CUSTOM3 = 0b1111011,
|
||||
}
|
||||
|
||||
|
||||
enum Cause
|
||||
{
|
||||
MISALIGNED_FETCH = 0x00,
|
||||
FETCH_ACCESS = 0x01,
|
||||
ILLEGAL_INSTRUCTION = 0x02,
|
||||
BREAKPOINT = 0x03,
|
||||
MISALIGNED_LOAD = 0x04,
|
||||
LOAD_ACCESS = 0x05,
|
||||
MISALIGNED_STORE = 0x06,
|
||||
STORE_ACCESS = 0x07,
|
||||
USER_ECALL = 0x08,
|
||||
SUPERVISOR_ECALL = 0x09,
|
||||
VIRTUAL_SUPERVISOR_ECALL = 0x0A,
|
||||
MACHINE_ECALL = 0x0B,
|
||||
FETCH_PAGE_FAULT = 0x0C,
|
||||
LOAD_PAGE_FAULT = 0x0D,
|
||||
STORE_PAGE_FAULT = 0x0F,
|
||||
DOUBLE_TRAP = 0x10,
|
||||
SOFTWARE_CHECK_FAULT = 0x12,
|
||||
HARDWARE_ERROR_FAULT = 0x13,
|
||||
FETCH_GUEST_PAGE_FAULT = 0x14,
|
||||
LOAD_GUEST_PAGE_FAULT = 0x15,
|
||||
VIRTUAL_INSTRUCTION = 0x16,
|
||||
STORE_GUEST_PAGE_FAULT = 0x17,
|
||||
}
|
||||
|
||||
|
||||
private T signExtend(T)(T num, uint bits) @safe @nogc pure nothrow
|
||||
{
|
||||
alias U = Unsigned!T;
|
||||
U one = 1;
|
||||
bool ngt = (num & (one << (bits-1))) != 0;
|
||||
if (ngt)
|
||||
return (U.max >> bits << bits) | num;
|
||||
else
|
||||
return (U.max >> (U.sizeof*8 - bits)) & num;
|
||||
}
|
||||
static assert(signExtend!int(0b0110, 4) == 6);
|
||||
static assert(signExtend!int(0b1010, 4) == -6);
|
||||
|
||||
|
||||
class CPU
|
||||
{
|
||||
@safe:
|
||||
public:
|
||||
alias WORD = uint;
|
||||
enum XLEN = WORD.sizeof;
|
||||
|
||||
this(Memory memory)
|
||||
{
|
||||
this.memory = memory;
|
||||
}
|
||||
|
||||
void tick()
|
||||
{
|
||||
static assert(WORD.sizeof == 4, "Only 32 bit architecture is supported");
|
||||
|
||||
// Zero register
|
||||
x[0] = 0;
|
||||
|
||||
// Jumped internal flag
|
||||
jumped = false;
|
||||
|
||||
// Common variables
|
||||
bool mems; // Memory read/write status
|
||||
|
||||
// Fetch instruction
|
||||
WORD inst = fetch(pc);
|
||||
|
||||
// Ensure instruction is 32bit
|
||||
if (!((inst & 0b11) == 0b11 && (inst & 0b11111) != 0b11111))
|
||||
{
|
||||
// It is not!
|
||||
exception(Cause.ILLEGAL_INSTRUCTION);
|
||||
}
|
||||
if (pc & 3)
|
||||
exception(Cause.MISALIGNED_FETCH);
|
||||
|
||||
// Decode instruction
|
||||
ubyte opcode = inst & 0x7F;
|
||||
ubyte rd = (inst >> 7) & 0x1F;
|
||||
ubyte funct3 = (inst >> 12) & 0x07;
|
||||
ubyte funct7 = (inst >> 25) & 0x7F;
|
||||
ubyte rs1 = (inst >> 15) & 0x1F;
|
||||
ubyte rs2 = (inst >> 20) & 0x1F;
|
||||
WORD i_imm = (inst >> 20) & 0xFFF;
|
||||
WORD u_imm = inst & 0xFFFFF000;
|
||||
|
||||
WORD s_imm;
|
||||
s_imm |= (inst >> 7) & 0x0001F; // [4:0]
|
||||
s_imm |= (inst >> 20) & 0x00FE0; // [11:5]
|
||||
|
||||
WORD b_imm;
|
||||
b_imm |= (inst >> 7) & 0x0001E; // [4:1]
|
||||
b_imm |= (inst >> 20) & 0x007E0; // [10:5]
|
||||
b_imm |= (inst << 4) & 0x00800; // [11]
|
||||
b_imm |= (inst >> 19) & 0x01000; // [12]
|
||||
|
||||
WORD j_imm;
|
||||
j_imm |= (inst >> 20) & 0x0007FE; // [10:1]
|
||||
j_imm |= (inst >> 9) & 0x000800; // [11]
|
||||
j_imm |= inst & 0x0FF000; // [19:12]
|
||||
j_imm |= (inst >> 11) & 0x100000; // [20]
|
||||
|
||||
WORD i_immex = signExtend!WORD(i_imm, 12);
|
||||
WORD s_immex = signExtend!WORD(s_imm, 12);
|
||||
WORD b_immex = signExtend!WORD(b_imm, 12);
|
||||
WORD u_immex = signExtend!WORD(u_imm, 20);
|
||||
WORD j_immex = signExtend!WORD(j_imm, 20);
|
||||
|
||||
// Debug logging
|
||||
debug
|
||||
{
|
||||
import std.stdio;
|
||||
writeln();
|
||||
writefln(
|
||||
"\x1B[35m%06X\x1B[0m: \x1B[96m%s\x1B[0m, funct7: \x1B[33m%d\x1B[0m, funct3: \x1B[33m%d\x1B[0m " ~
|
||||
"rs2: \x1B[36m%s\x1B[0m, rs1: \x1B[36m%s\x1B[0m, rd: \x1B[91m%d\x1B[0m",
|
||||
pc,
|
||||
cast(OPCODE)opcode,
|
||||
funct7,
|
||||
funct3,
|
||||
rs2,
|
||||
rs1,
|
||||
rd,
|
||||
);
|
||||
auto binstr = (WORD num, int bits){
|
||||
import std.format;
|
||||
string img = "";
|
||||
foreach (c; format("%0*b", bits, num))
|
||||
if (c=='1')
|
||||
img ~= "\x1B[32m1";
|
||||
else
|
||||
img ~= "\x1B[31m0";
|
||||
return img~"\x1B[0m";
|
||||
};
|
||||
writefln("I-IMM : %s | \x1B[94m%d\x1B[0m", binstr(i_imm&0xFFF, 12), cast(int)i_immex);
|
||||
writefln("S-IMM : %s | \x1B[94m%d\x1B[0m", binstr(s_imm&0xFFF, 12), cast(int)s_immex);
|
||||
writefln("B-IMM : %s | \x1B[94m%d\x1B[0m", binstr(b_imm&0xFFF, 12), cast(int)b_immex);
|
||||
writefln("U-IMM : %s | \x1B[94m%d\x1B[0m >> %d", binstr(u_imm&0xFFFFF, 20), cast(int)u_immex, cast(int)u_immex >> 12);
|
||||
writefln("J-IMM : %s | \x1B[94m%d\x1B[0m", binstr(j_imm&0xFFFFF, 20), cast(int)j_immex);
|
||||
}
|
||||
|
||||
// Do instruction
|
||||
with (OPCODE) switch (opcode)
|
||||
{
|
||||
case OPIMM:
|
||||
switch (funct3)
|
||||
{
|
||||
case 0: // ADDI
|
||||
x[rd] = x[rs1] + i_immex;
|
||||
break;
|
||||
case 1: // SLLI
|
||||
x[rd] = x[rs1] << (i_imm & 0x1F);
|
||||
break;
|
||||
case 2: // SLTI
|
||||
x[rd] = cast(Signed!WORD)x[rs1] < cast(Signed!WORD)i_immex;
|
||||
break;
|
||||
case 3: // SLTU
|
||||
x[rd] = x[rs1] < i_immex;
|
||||
break;
|
||||
case 4: // XORI
|
||||
x[rd] = x[rs1] ^ i_immex;
|
||||
break;
|
||||
case 5: // SRLI/SRAI
|
||||
if ((i_imm & 0x400) == 0) // SRLI
|
||||
x[rd] = x[rs1] >> (i_imm & 0x1F);
|
||||
else // SRAI
|
||||
x[rd] = cast(Signed!WORD)x[rs1] >> (i_imm & 0x1F);
|
||||
break;
|
||||
case 6: // ORI
|
||||
x[rd] = x[rs1] | i_immex;
|
||||
break;
|
||||
case 7: // ANDI
|
||||
x[rd] = x[rs1] & i_immex;
|
||||
break;
|
||||
default:
|
||||
exception(Cause.ILLEGAL_INSTRUCTION);
|
||||
}
|
||||
break;
|
||||
|
||||
case OP:
|
||||
switch (funct3)
|
||||
{
|
||||
case 0: // ADD
|
||||
if (funct7 == 0)
|
||||
x[rd] = x[rs1] + x[rs2];
|
||||
else if (funct7 == 32)
|
||||
x[rd] = x[rs1] - x[rs2];
|
||||
else
|
||||
goto default;
|
||||
break;
|
||||
case 1: // SLL
|
||||
if (funct7 == 0)
|
||||
x[rd] = x[rs1] << (x[rs2] & 0x1F);
|
||||
else
|
||||
goto default;
|
||||
break;
|
||||
case 2: // SLT
|
||||
if (funct7 == 0)
|
||||
x[rd] = cast(Signed!WORD)x[rs1] < cast(Signed!WORD)x[rs2];
|
||||
else
|
||||
goto default;
|
||||
break;
|
||||
case 3: // SLTU
|
||||
if (funct7 == 0)
|
||||
x[rd] = x[rs1] < x[rs2];
|
||||
else
|
||||
goto default;
|
||||
break;
|
||||
case 4: // XOR
|
||||
if (funct7 == 0)
|
||||
x[rd] = x[rs1] ^ x[rs2];
|
||||
else
|
||||
goto default;
|
||||
break;
|
||||
case 5: // SRL/SRA
|
||||
if (funct7 == 0) // SRL
|
||||
x[rd] = x[rs1] >> (x[rs2] & 0x1F);
|
||||
else if (funct7 == 32) // SRA
|
||||
x[rd] = cast(Signed!WORD)x[rs1] >> (x[rs2] & 0x1F);
|
||||
else
|
||||
goto default;
|
||||
break;
|
||||
case 6: // OR
|
||||
if (funct7 == 0)
|
||||
x[rd] = x[rs1] | x[rs2];
|
||||
else
|
||||
goto default;
|
||||
break;
|
||||
case 7: // AND
|
||||
if (funct7 == 0)
|
||||
x[rd] = x[rs1] & x[rs2];
|
||||
else
|
||||
goto default;
|
||||
break;
|
||||
default:
|
||||
exception(Cause.ILLEGAL_INSTRUCTION);
|
||||
}
|
||||
break;
|
||||
|
||||
case LUI:
|
||||
x[rd] = u_imm >> 12;
|
||||
break;
|
||||
|
||||
case AUIPC:
|
||||
pc += u_imm >> 12;
|
||||
x[rd] = pc;
|
||||
break;
|
||||
|
||||
case JAL:
|
||||
x[rd] = pc + 4;
|
||||
|
||||
jump(pc + cast(Signed!WORD)j_immex);
|
||||
break;
|
||||
|
||||
case JALR:
|
||||
if (funct3)
|
||||
exception(Cause.ILLEGAL_INSTRUCTION);
|
||||
x[rd] = pc + 4;
|
||||
jump((x[rs1] + cast(Signed!WORD)i_immex) << 1 >> 1);
|
||||
break;
|
||||
|
||||
case BRANCH:
|
||||
switch (funct3)
|
||||
{
|
||||
case 0: // BEQ
|
||||
if (x[rs1] == x[rs2])
|
||||
jump(pc + cast(Signed!WORD)b_immex);
|
||||
break;
|
||||
case 1: // BNE
|
||||
if (x[rs1] != x[rs2])
|
||||
jump(pc + cast(Signed!WORD)b_immex);
|
||||
break;
|
||||
case 4: // BLT
|
||||
if (cast(Signed!WORD)x[rs1] < cast(Signed!WORD)x[rs2])
|
||||
jump(pc + cast(Signed!WORD)b_immex);
|
||||
break;
|
||||
case 5: // BGE
|
||||
if (cast(Signed!WORD)x[rs1] > cast(Signed!WORD)x[rs2])
|
||||
jump(pc + cast(Signed!WORD)b_immex);
|
||||
break;
|
||||
case 6: // BLTU
|
||||
if (x[rs1] < x[rs2])
|
||||
jump(pc + cast(Signed!WORD)b_immex);
|
||||
break;
|
||||
case 7: // BGEU
|
||||
if (x[rs1] > x[rs2])
|
||||
jump(pc + cast(Signed!WORD)b_immex);
|
||||
break;
|
||||
default:
|
||||
exception(Cause.ILLEGAL_INSTRUCTION);
|
||||
break;
|
||||
}
|
||||
break;
|
||||
|
||||
case LOAD:
|
||||
switch (funct3)
|
||||
{
|
||||
case 0: // LB
|
||||
x[rd] = load!byte(x[rs1] + cast(Signed!WORD)i_immex);
|
||||
break;
|
||||
case 1: // LH
|
||||
x[rd] = load!short(x[rs1] + cast(Signed!WORD)i_immex);
|
||||
break;
|
||||
case 2: // LW
|
||||
x[rd] = load!int(x[rs1] + cast(Signed!WORD)i_immex);
|
||||
break;
|
||||
case 4: // LBU
|
||||
x[rd] = load!ubyte(x[rs1] + cast(Signed!WORD)i_immex);
|
||||
break;
|
||||
case 5: // LHU
|
||||
x[rd] = load!ushort(x[rs1] + cast(Signed!WORD)i_immex);
|
||||
break;
|
||||
default:
|
||||
exception(Cause.ILLEGAL_INSTRUCTION);
|
||||
break;
|
||||
}
|
||||
break;
|
||||
|
||||
case STORE:
|
||||
switch (funct3)
|
||||
{
|
||||
case 0: // SB
|
||||
store!ubyte(x[rs1] + s_immex, cast(ubyte)x[rs2]);
|
||||
break;
|
||||
case 1: // SH
|
||||
store!ushort(x[rs1] + s_immex, cast(ushort)x[rs2]);
|
||||
break;
|
||||
case 2: // SW
|
||||
store!uint(x[rs1] + s_immex, cast(uint)x[rs2]);
|
||||
break;
|
||||
default:
|
||||
exception(Cause.ILLEGAL_INSTRUCTION);
|
||||
break;
|
||||
}
|
||||
break;
|
||||
|
||||
case MISCMEM:
|
||||
switch (funct3)
|
||||
{
|
||||
case 0: // FENCE
|
||||
if (rs1 != 0 || rd != 0)
|
||||
exception(Cause.ILLEGAL_INSTRUCTION);
|
||||
if (i_imm ^ 0x0FF) // && i_imm ^ 0x8FF)
|
||||
exception(Cause.ILLEGAL_INSTRUCTION);
|
||||
fence();
|
||||
break;
|
||||
default:
|
||||
exception(Cause.ILLEGAL_INSTRUCTION);
|
||||
}
|
||||
break;
|
||||
|
||||
case SYSTEM:
|
||||
switch (funct3)
|
||||
{
|
||||
case 0: // PRIV
|
||||
if (rs1 != 0 || rd != 0)
|
||||
exception(Cause.ILLEGAL_INSTRUCTION);
|
||||
switch (i_imm)
|
||||
{
|
||||
case 0: // ECALL
|
||||
exception(Cause.MACHINE_ECALL);
|
||||
break;
|
||||
|
||||
case 1: // EBREAK
|
||||
exception(Cause.BREAKPOINT);
|
||||
break;
|
||||
|
||||
default:
|
||||
exception(Cause.ILLEGAL_INSTRUCTION);
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
exception(Cause.ILLEGAL_INSTRUCTION);
|
||||
break;
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
exception(Cause.ILLEGAL_INSTRUCTION);
|
||||
break;
|
||||
}
|
||||
|
||||
// Increment program counter
|
||||
if (!jumped)
|
||||
pc += XLEN;
|
||||
}
|
||||
|
||||
void exception(Cause cause)
|
||||
{
|
||||
import std.conv;
|
||||
throw new Exception(cause.to!string);
|
||||
}
|
||||
|
||||
void interrupt(Cause cause)
|
||||
{
|
||||
import std.conv;
|
||||
throw new Exception(cause.to!string);
|
||||
}
|
||||
|
||||
void fence()
|
||||
{
|
||||
}
|
||||
|
||||
WORD[32] x;
|
||||
WORD pc;
|
||||
|
||||
Memory memory;
|
||||
|
||||
private:
|
||||
void jump(WORD addr)
|
||||
{
|
||||
pc = addr;
|
||||
jumped = true;
|
||||
}
|
||||
|
||||
WORD fetch(WORD addr)
|
||||
{
|
||||
WORD value;
|
||||
if (!memory.get!WORD(addr, value))
|
||||
exception(Cause.FETCH_ACCESS);
|
||||
return value;
|
||||
}
|
||||
|
||||
T load(T)(WORD addr)
|
||||
{
|
||||
T value;
|
||||
if (!memory.get!T(addr, value))
|
||||
exception(Cause.LOAD_ACCESS);
|
||||
return value;
|
||||
}
|
||||
|
||||
T store(T)(WORD addr, T value)
|
||||
{
|
||||
if (!memory.set!T(addr, value))
|
||||
exception(Cause.STORE_ACCESS);
|
||||
return value;
|
||||
}
|
||||
|
||||
bool jumped = false;
|
||||
}
|
||||
254
source/riscd/memory.d
Normal file
254
source/riscd/memory.d
Normal file
@ -0,0 +1,254 @@
|
||||
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;
|
||||
}
|
||||
4
source/riscd/package.d
Normal file
4
source/riscd/package.d
Normal file
@ -0,0 +1,4 @@
|
||||
module riscd;
|
||||
|
||||
public import riscd.cpu;
|
||||
public import riscd.memory;
|
||||
Reference in New Issue
Block a user