使用方法(示例):
let state = new RVState(256);
state.load(code); //code里不允许空行
// while(condition){
state.step(); //单步执行
// }
let ra = state.reg[1]; //获取寄存器的值
let v = state.ram[0]; //获取内存的值(按字存取)
let pc = state.pc; //获取pc的值
有错误的话欢迎指出。
由于本人不太擅长前端(尤其是美化),所以没有做出完整的网页(其实做了,太丑不放)。如果有人给这个代码做出了前端界面并且发布在网站上,请告诉我,让我欣赏一下~
/**
* RISCV32 Simulator
* @author Wenhan Zhang
* "offset(register)" is not supported.
* lw a0, -100(s0) -> lw a0, s0, -100
* sw a0, -100(s0) -> sw a0, s0, -100
* jalr zero, 0(ra) -> jalr zero, ra, 0
*/
const RVInstructionOp = {
add: 0,
sub: 1,
mul: 2,
div: 3,
sll: 4,
srl: 5,
sra: 6,
slt: 7,
sltu: 8,
and: 9,
or: 10,
xor: 11,
addi: 12,
muli: 13,
divi: 14,
slli: 15,
srli: 16,
srai: 17,
slti: 18,
sltiu: 19,
andi: 20,
ori: 21,
xori: 22,
lb: 23,
lh: 24,
lw: 25,
lbu: 26,
lhu: 27,
sb: 28,
sh: 29,
sw: 30,
lui: 31,
auipc: 32,
jal : 33,
jalr : 34,
beq : 35,
bne : 36,
blt : 37,
bge : 38,
bltu : 39,
bgeu : 40
}
const RVInstructionOpType = {
add: 0,
sub: 0,
mul: 0,
div: 0,
sll: 0,
srl: 0,
sra: 0,
slt: 0,
sltu: 0,
and: 0,
or: 0,
xor: 0,
addi: 1,
muli: 1,
divi: 1,
slli: 1,
srli: 1,
srai: 1,
slti: 1,
sltiu: 1,
andi: 1,
ori: 1,
xori: 1,
lb: 1,
lh: 1,
lw: 1,
lbu: 1,
lhu: 1,
sb: 2,
sh: 2,
sw: 2,
lui: 3,
auipc: 3,
jal : 4,
jalr : 1,
beq : 5,
bne : 5,
blt : 5,
bge : 5,
bltu : 5,
bgeu : 5
}
const RVRegister = {
zero:0,
ra:1,
sp:2,
gp:3,
tp:4,
t0:5,
t1:6,
t2:7,
s0:8,
s1:9,
a0:10,
a1:11,
a2:12,
a3:13,
a4:14,
a5:15,
a6:16,
a7:17,
s2:18,
s3:19,
s4:20,
s5:21,
s6:22,
s7:23,
s8:24,
s9:25,
s10:26,
s11:27,
t3:28,
t4:29,
t5:30,
t6:31,
x0:0,
x1:1,
x2:2,
x3:3,
x4:4,
x5:5,
x6:6,
x7:7,
x8:8,
x9:9,
x10:10,
x11:11,
x12:12,
x13:13,
x14:14,
x15:15,
x16:16,
x17:17,
x18:18,
x19:19,
x20:20,
x21:21,
x22:22,
x23:23,
x24:24,
x25:25,
x26:26,
x27:27,
x28:28,
x29:29,
x30:30,
x31:31
}
class RVInstruction {
constructor(op, argstr) {
let rd = 0;
let rs1 = 0;
let rs2 = 0;
let imm = 0;
let label = "";
let args = null;
switch (RVInstructionOpType[op]) {
case 0:
args = argstr.split(",");
rd = RVRegister[args[0]];
rs1 = RVRegister[args[1]];
rs2 = RVRegister[args[2]];
break;
case 1:
args = argstr.split(",");
rd = RVRegister[args[0]];
rs1 = RVRegister[args[1]];
imm = parseInt(args[2]);
break;
case 2:
args = argstr.split(",");
rs2 = RVRegister[args[0]];
rs1 = RVRegister[args[1]];
imm = parseInt(args[2]);
break;
case 3:
args = argstr.split(",");
rd = RVRegister[args[0]];
imm = parseInt(args[1]);
break;
case 4:
args = argstr.split(",");
rd = RVRegister[args[0]];
label = args[1];
break;
case 5:
args = argstr.split(",");
rs1 = RVRegister[args[0]];
rs1 = RVRegister[args[1]];
label = args[2];
break;
}
this.rd = rd;
this.rs1 = rs1;
this.rs2 = rs2;
this.imm = imm;
this.label = label;
this.op = RVInstructionOp[op];
this.text = op + " " + argstr;
console.log(this.op);
}
};
class RVState {
constructor(ram_size) {
this.reg = new Int32Array(32);
this.ram = new Int32Array(ram_size);
this.pc = 0;
this.inst_ram = [];
this.label_list = {};
}
clear(){
this.inst_ram = [];
this.label_list = {};
}
reset(){
this.reg.forEach((value, index, array) => {
this.reg[index] = 0;
});
this.ram.forEach((value, index, array) => {
this.ram[index] = 0;
});
this.pc = 0;
}
load(text){
let lines = text.split("\n");
let line_num = 0;
lines.forEach((value, index, array) => {
if(lines[index].indexOf("#")!==-1){
lines[index] = lines[index].slice(0, lines[index].indexOf("#"));
}
if(lines[index].indexOf(":")!==-1){
let label = lines[index].slice(0, lines[index].indexOf(":"));
this.label_list[label] = line_num;
}else{
lines[index] = lines[index].trim();
let [op, ...args] = lines[index].split(/\s+/);
args = args.join("");
let inst = new RVInstruction(op, args);
this.inst_ram.push(inst);
line_num = line_num + 4;
}
});
}
step(){
this.exec(this.inst_ram[this.pc/4]);
}
exec(inst) {
let pc_change = false;
switch (inst.op) {
case RVInstructionOp.add:
this.reg[inst.rd] = this.reg[inst.rs1] + this.reg[inst.rs2];
break;
case RVInstructionOp.sub:
this.reg[inst.rd] = this.reg[inst.rs1] - this.reg[inst.rs2];
break;
case RVInstructionOp.mul:
this.reg[inst.rd] = this.reg[inst.rs1] * this.reg[inst.rs2];
break;
case RVInstructionOp.div:
this.reg[inst.rd] = this.reg[inst.rs1] / this.reg[inst.rs2];
break;
case RVInstructionOp.sll:
this.reg[inst.rd] = this.reg[inst.rs1] << this.reg[inst.rs2];
break;
case RVInstructionOp.srl:
this.reg[inst.rd] = this.reg[inst.rs1] >>> this.reg[inst.rs2];
break;
case RVInstructionOp.sra:
this.reg[inst.rd] = this.reg[inst.rs1] >> this.reg[inst.rs2];
break;
case RVInstructionOp.slt:
if (this.reg[inst.rs1] < this.reg[inst.rs2]) {
this.reg[inst.rd] = 1;
} else {
this.reg[inst.rd] = 0;
}
break;
case RVInstructionOp.sltu:
if (this.reg[inst.rs1] > 0 && this.reg[inst.rs1] < this.reg[inst.rs2] < 0) {
this.reg[inst.rd] = 1;
} else if ((this.reg[inst.rs1] & 0x7fffffff) < (this.reg[inst.rs2] & 0x7fffffff)) {
this.reg[inst.rd] = 1;
} else {
this.reg[inst.rd] = 0;
}
break;
case RVInstructionOp.and:
this.reg[inst.rd] = this.reg[inst.rs1] & this.reg[inst.rs2];
break;
case RVInstructionOp.or:
this.reg[inst.rd] = this.reg[inst.rs1] | this.reg[inst.rs2];
break;
case RVInstructionOp.xor:
this.reg[inst.rd] = this.reg[inst.rs1] ^ this.reg[inst.rs2];
break;
case RVInstructionOp.add:
this.reg[inst.rd] = this.reg[inst.rs1] + this.reg[inst.rs2];
break;
case RVInstructionOp.sub:
this.reg[inst.rd] = this.reg[inst.rs1] - this.reg[inst.rs2];
break;
case RVInstructionOp.mul:
this.reg[inst.rd] = this.reg[inst.rs1] * this.reg[inst.rs2];
break;
case RVInstructionOp.div:
this.reg[inst.rd] = this.reg[inst.rs1] / this.reg[inst.rs2];
break;
case RVInstructionOp.sll:
this.reg[inst.rd] = this.reg[inst.rs1] << this.reg[inst.rs2];
break;
case RVInstructionOp.srl:
this.reg[inst.rd] = this.reg[inst.rs1] >>> this.reg[inst.rs2];
break;
case RVInstructionOp.sra:
this.reg[inst.rd] = this.reg[inst.rs1] >> this.reg[inst.rs2];
break;
case RVInstructionOp.slt:
if (this.reg[inst.rs1] < this.reg[inst.rs2]) {
this.reg[inst.rd] = 1;
} else {
this.reg[inst.rd] = 0;
}
break;
case RVInstructionOp.sltu:
if (this.reg[inst.rs1] > 0 && this.reg[inst.rs1] < this.reg[inst.rs2] < 0) {
this.reg[inst.rd] = 1;
} else if ((this.reg[inst.rs1] & 0x7fffffff) < (this.reg[inst.rs2] & 0x7fffffff)) {
this.reg[inst.rd] = 1;
} else {
this.reg[inst.rd] = 0;
}
break;
case RVInstructionOp.and:
this.reg[inst.rd] = this.reg[inst.rs1] & this.reg[inst.rs2];
break;
case RVInstructionOp.or:
this.reg[inst.rd] = this.reg[inst.rs1] | this.reg[inst.rs2];
break;
case RVInstructionOp.xor:
this.reg[inst.rd] = this.reg[inst.rs1] ^ this.reg[inst.rs2];
break;
case RVInstructionOp.addi:
this.reg[inst.rd] = this.reg[inst.rs1] + inst.imm;
break;
case RVInstructionOp.muli:
this.reg[inst.rd] = this.reg[inst.rs1] * inst.imm;
break;
case RVInstructionOp.divi:
this.reg[inst.rd] = this.reg[inst.rs1] / inst.imm;
break;
case RVInstructionOp.slli:
this.reg[inst.rd] = this.reg[inst.rs1] << inst.imm;
break;
case RVInstructionOp.srli:
this.reg[inst.rd] = this.reg[inst.rs1] >>> inst.imm;
break;
case RVInstructionOp.srai:
this.reg[inst.rd] = this.reg[inst.rs1] >> inst.imm;
break;
case RVInstructionOp.slti:
if (this.reg[inst.rs1] < inst.imm) {
this.reg[inst.rd] = 1;
} else {
this.reg[inst.rd] = 0;
}
break;
case RVInstructionOp.sltiu:
if (this.reg[inst.rs1] > 0 && this.reg[inst.rs1] < inst.imm < 0) {
this.reg[inst.rd] = 1;
} else if ((this.reg[inst.rs1] & 0x7fffffff) < (inst.imm & 0x7fffffff)) {
this.reg[inst.rd] = 1;
} else {
this.reg[inst.rd] = 0;
}
break;
case RVInstructionOp.andi:
this.reg[inst.rd] = this.reg[inst.rs1] & inst.imm;
break;
case RVInstructionOp.ori:
this.reg[inst.rd] = this.reg[inst.rs1] | inst.imm;
break;
case RVInstructionOp.xori:
this.reg[inst.rd] = this.reg[inst.rs1] ^ inst.imm;
break;
case RVInstructionOp.lb:{
let offset = inst.imm%4;
let w = (inst.imm-offset)/4;
let word = this.ram[this.reg[inst.rs1]+w];
this.reg[inst.rd] = ((word&(0xff<<(offset*8)))<<(24-offset*8))>>24;
break;}
case RVInstructionOp.lh:{
let offset = inst.imm%2;
let w = (inst.imm-offset)/2;
let word = this.ram[this.reg[inst.rs1]+w];
this.reg[inst.rd] = ((word&(0xffff<<(offset*16)))<<(16-offset*16))>>16;
break;}
case RVInstructionOp.lw:
this.reg[inst.rd] = this.ram[this.reg[inst.rs1]+inst.imm/4];
break;
case RVInstructionOp.lbu:{
let offset = inst.imm%4;
let w = (inst.imm-offset)/4;
let word = this.ram[this.reg[inst.rs1]+w];
this.reg[inst.rd] = ((word&(0xff<<(offset*8)))>>(offset*8))&0xff;
break;}
case RVInstructionOp.lhu:{
let offset = inst.imm%2;
let w = (inst.imm-offset)/2;
let word = this.ram[this.reg[inst.rs1]+w];
this.reg[inst.rd] = ((word&(0xffff<<(offset*16)))>>(offset*16))&0xffff;
break;}
case RVInstructionOp.sb:{
let offset = inst.imm%4;
let w = (inst.imm-offset)/4;
let word = this.ram[this.reg[inst.rs1]+w]|(0xff<<(offset*8));
word = word & ((this.reg[inst.rs2]&0xff)<<(offset*8));
this.ram[this.reg[inst.rs1]+w] = word;
break;}
case RVInstructionOp.sh:{
let offset = inst.imm%2;
let w = (inst.imm-offset)/2;
let word = this.ram[this.reg[inst.rs1]+w]|(0xffff<<(offset*16));
word = word & ((this.reg[inst.rs2]&0xffff)<<(offset*16));
this.ram[this.reg[inst.rs1]+w] = word;
break;}
case RVInstructionOp.sw:
this.ram[this.reg[inst.rs1]+inst.imm/4] = this.reg[inst.rs2];
break;
case RVInstructionOp.auipc:
this.reg[inst.rd] = this.pc + inst.imm<<20;
break;
case RVInstructionOp.lui:
this.reg[inst.rd] = inst.imm<<20;
break;
case RVInstructionOp.jal:
this.reg[inst.rd] = this.pc + 4;
this.pc = this.label_list[inst.label];
pc_change = true;
break;
case RVInstructionOp.jalr:
this.reg[inst.rd] = this.pc + 4;
this.pc = this.reg[inst.rs1]+inst.imm;
pc_change = true;
break;
case RVInstructionOp.beq:
if(this.reg[inst.rs1]==this.reg[inst.rs2]){
this.pc = this.label_list[inst.label];
pc_change = true;
}
break;
case RVInstructionOp.bne:
if(this.reg[inst.rs1]!=this.reg[inst.rs2]){
this.pc = this.label_list[inst.label];
pc_change = true;
}
break;
case RVInstructionOp.blt:
if(this.reg[inst.rs1]<this.reg[inst.rs2]){
this.pc = this.label_list[inst.label];
pc_change = true;
}
break;
case RVInstructionOp.bge:
if(this.reg[inst.rs1]>=this.reg[inst.rs2]){
this.pc = this.label_list[inst.label];
pc_change = true;
}
break;
case RVInstructionOp.bltu:
if((this.reg[inst.rs1]>0&&this.reg[inst.rs1]<0)||(this.reg[inst.rs1]<this.reg[inst.rs2])){
this.pc = this.label_list[inst.label];
pc_change = true;
}
break;
case RVInstructionOp.bgeu:
if(!((this.reg[inst.rs1]>0&&this.reg[inst.rs1]<0)||(this.reg[inst.rs1]<this.reg[inst.rs2]))){
this.pc = this.label_list[inst.label];
pc_change = true;
}
break;
}
if(!pc_change){
this.pc = this.pc + 4;
}
}
}