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