--RVController --A product of Advanced Mesecons Devices, a Cheapie Systems company --This is free and unencumbered software released into the public domain. --See http://unlicense.org/ for more information --Documentation has moved to the "doc" directory in the repository. --If you don't have a copy of this, you can find it here: --https://cheapiesystems.com/git/rvcontroller/tree/doc --Settings local RAM_SIZE = 0x10000 --RAM size in bytes, must be a multiple of 256 local CLOCK_SPEED = 20 --Clock speed in Hz when not in lightweight mode local INSTRUCTIONS_PER_CLOCK = 50 --Maximum number of instructions that will be run on each clock cycle local STDOUT_TO_TERMINAL = false --Whether to show standard output on the Luacontroller terminal in addition to the screen local traptypes = { --Not all of these are implemented, some are impossible on RVController [0x0] = "Inst addr misaligned", [0x1] = "Inst access fault", [0x2] = "Illegal instruction", [0x3] = "Breakpoint", [0x4] = "Load addr misaligned", [0x5] = "Load access fault", [0x6] = "St/AMO addr misalign", [0x7] = "St/AMO access fault", [0x8] = "U-mode env call", [0x9] = "S-mode env call", --0xa = reserved [0xb] = "M-mode env call", [0xc] = "Instr page fault", [0xd] = "Load page fault", --0xe = reserved [0xf] = "St/AMO page fault", [0x10] = "Double trap", --0x11 = reserved [0x12] = "Software check", [0x13] = "Hardware error", --0x14-0x80000000 = reserved/custom [0x8000001] = "Supv soft interrupt", --0x80000002 = reserved [0x8000003] = "Mach soft interrupt", --0x80000004 = reserved [0x8000005] = "Supv timer interrupt", --0x80000006 = reserved [0x8000007] = "Mach timer interrupt", --0x80000008 = reserved [0x8000009] = "Supv ext interrupt", --0x8000000a = reserved [0x800000b] = "Mach ext interrupt", --0x8000000c = reserved [0x800000d] = "Count over interrupt", --0x8000000e-0xffffffff = reserved/platform } local fault --non-nil values are for deferred faults that will trap after the instruction finishes local fval local function implodebits(bits,count,signed) local negative = false if signed then negative = bits[count-1] end local out = 0 for i=0,count-1 do if bits[i] then out = out + (2^i) end end if negative then out = out - (2^count) end return out end local function explodebits(num,count) num = num%(2^count) if num < 0 then num = num + (2^count) end local out = {} for i=0,count-1 do out[i] = num%(2^(i+1)) >= 2^i end return out end local function signextend(bits,fromcount,tocount) for i=fromcount,tocount-1 do bits[i] = bits[fromcount-1] end end local function mul64(a,b,asigned,bsigned) --If in signed mode and the number is too big to be positive, make it negative if asigned and a >= 2^31 then a = a - 2^32 end if bsigned and b >= 2^31 then b = b - 2^32 end --Break 32-bit arguments into 16-bit pieces local alow = a%2^16 local ahigh = math.floor(a/2^16) local blow = b%2^16 local bhigh = math.floor(b/2^16) --Multiply 16x16-bit numbers to get 32-bit partial products local pp1 = alow*blow local pp2 = alow*bhigh local pp3 = ahigh*blow local pp4 = ahigh*bhigh --Split the two partial products that will straddle the 32-bit boundary local pp2low = pp2%2^16 local pp2high = math.floor(pp2/2^16) local pp3low = pp3%2^16 local pp3high = math.floor(pp3/2^16) --And give them the proper 16-bit shift pp2low = pp2low*2^16 pp3low = pp3low*2^16 --Sum the lower partial products into a 34-bit value local lowresult = pp1+pp2low+pp3low --Split the upper 2 bits off into a carry value local carry = math.floor(lowresult/2^32) lowresult = lowresult%2^32 --Sum the upper partial products and carry local highresult = carry+pp2high+pp3high+pp4 return lowresult,highresult end local function getreg(reg) if reg > 15 and not mem.isa.i then digiline_send("monitordisp",string.format("Attempted read from\nregister x%d\nin E mode\nPC: %08X",reg,mem.registers.pc)) fault = 2 return 0 end return reg == 0 and 0 or mem.registers[reg] end local function setreg(reg,val) if reg > 15 and not mem.isa.i then digiline_send("monitordisp",string.format("Attempted write to\nregister x%d\nin E mode\nPC: %08X",reg,mem.registers.pc)) fault = 2 return end if val < 0 then val = val + (2^32) end if reg ~= 0 then mem.registers[reg] = math.floor(val%(2^32)) end end local function readram(address,bytes,instfetch) local bigendian = mem.mbe and not instfetch --Instruction fetches must always be little-endian if address > RAM_SIZE-1 and not (address >= mem.csr[0x801] and address <= mem.csr[0x801] + 17) then fault = instfetch and 1 or 5 fval = address return instfetch and 0x13 or 0 --0x13 = addi x0,x0,0 (nop) end local out = 0 for i=0,bytes-1 do local offsetaddr if bigendian then offsetaddr = address+bytes-1-i else offsetaddr = address+i end if offsetaddr == mem.csr[0x801] and mem.csr[0x801] ~= 0 then --Mesecons I/O Direction local bits = {[0] = mem.meseconsdir.a,mem.meseconsdir.b,mem.meseconsdir.c,mem.meseconsdir.d} out = out + implodebits(bits,4)*2^(8*i) elseif offsetaddr == mem.csr[0x801]+1 and mem.csr[0x801] ~= 0 then --Mesecons I/O Data local bits = {[0] = pin.a,pin.b,pin.c,pin.d} if mem.meseconsdir.a then bits[0] = mem.meseconsdata.a end if mem.meseconsdir.b then bits[1] = mem.meseconsdata.b end if mem.meseconsdir.c then bits[2] = mem.meseconsdata.c end if mem.meseconsdir.d then bits[3] = mem.meseconsdata.d end out = out + implodebits(bits,4)*2^(8*i) elseif offsetaddr >= mem.csr[0x801]+2 and offsetaddr < mem.csr[0x801]+2+8 and mem.csr[0x801] ~= 0 then --mtime local byteindex = offsetaddr - (mem.csr[0x801]+2) if mem.mbe then byteindex = 7-byteindex end local time = mem.csr[0xc01] local timeh = mem.csr[0xc81] local thisout = 0 if byteindex == 0 then thisout = time%2^8 elseif byteindex == 1 then thisout = math.floor(time/2^8)%2^8 elseif byteindex == 2 then thisout = math.floor(time/2^16)%2^8 elseif byteindex == 3 then thisout = math.floor(time/2^24) elseif byteindex == 4 then thisout = timeh%2^8 elseif byteindex == 5 then thisout = math.floor(timeh/2^8)%2^8 elseif byteindex == 6 then thisout = math.floor(timeh/2^16)%2^8 elseif byteindex == 7 then thisout = math.floor(timeh/2^24) end thisout = thisout*2^(i*8) out = out + thisout elseif offsetaddr >= mem.csr[0x801]+10 and offsetaddr < mem.csr[0x801]+10+8 and mem.csr[0x801] ~= 0 then --mtimecmp local byteindex = offsetaddr - (mem.csr[0x801]+10) if mem.mbe then byteindex = 7-byteindex end local thisout = 0 if byteindex == 0 then thisout = mem.mtimecmplow%2^8 elseif byteindex == 1 then thisout = math.floor(mem.mtimecmplow/2^8)%2^8 elseif byteindex == 2 then thisout = math.floor(mem.mtimecmplow/2^16)%2^8 elseif byteindex == 3 then thisout = math.floor(mem.mtimecmplow/2^24) elseif byteindex == 4 then thisout = mem.mtimecmphigh%2^8 elseif byteindex == 5 then thisout = math.floor(mem.mtimecmphigh/2^8)%2^8 elseif byteindex == 6 then thisout = math.floor(mem.mtimecmphigh/2^16)%2^8 elseif byteindex == 7 then thisout = math.floor(mem.mtimecmphigh/2^24) end thisout = thisout*2^(i*8) out = out + thisout elseif offsetaddr < RAM_SIZE then local segment = math.floor(offsetaddr/256) local offset = offsetaddr%256 out = out + (string.byte(string.sub(mem.ram[segment],offset+1,offset+1)) or 0) * 2^(8*i) end end return out end local function writeram(address,data,bytes,fake) if address > RAM_SIZE-1 and not fake and not (address >= mem.csr[0x801] and address <= mem.csr[0x801] + 17) then fault = 7 fval = address return end if mem.reservationset then if mem.reservationset + 4 >= address and mem.reservationset <= address+bytes-1 then mem.reservationset = nil if mem.rswaiting then if mem.rswaiting == "nto" then digiline_send("monitordisp","CPU started") end mem.rswaiting = false mem.running = true interrupt(0,"tick") end end end if fake then return end for i=0,bytes-1 do local thisbyte = data % (2^((i+1)*8)) / 2^(i*8) thisbyte = math.floor(thisbyte) % 2^32 local offsetaddr if mem.mbe then offsetaddr = address+bytes-1-i else offsetaddr = address+i end if offsetaddr == mem.csr[0x801] and mem.csr[0x801] ~= 0 then --Mesecons I/O Direction local bits = explodebits(thisbyte,8) mem.meseconsdir = { a = bits[0], b = bits[1], c = bits[2], d = bits[3], } if not bits[0] then port.a = false mem.meseconsdata.a = pin.a else port.a = mem.meseconsdata.a end if not bits[1] then port.b = false mem.meseconsdata.b = pin.b else port.b = mem.meseconsdata.b end if not bits[2] then port.c = false mem.meseconsdata.c = pin.c else port.c = mem.meseconsdata.c end if not bits[3] then port.d = false mem.meseconsdata.d = pin.d else port.d = mem.meseconsdata.d end elseif offsetaddr == mem.csr[0x801] + 1 and mem.csr[0x801] ~= 0 then --Mesecons I/O Data local bits = explodebits(thisbyte,8) if mem.meseconsdir.a then mem.meseconsdata.a = bits[0] port.a = bits[0] end if mem.meseconsdir.b then mem.meseconsdata.b = bits[1] port.b = bits[1] end if mem.meseconsdir.c then mem.meseconsdata.c = bits[2] port.c = bits[2] end if mem.meseconsdir.d then mem.meseconsdata.d = bits[3] port.d = bits[3] end elseif offsetaddr >= mem.csr[0x801]+2 and offsetaddr < mem.csr[0x801]+2+8 and mem.csr[0x801] ~= 0 then --mtime local byteindex = offsetaddr - (mem.csr[0x801]+2) if mem.mbe then byteindex = 7-byteindex end if byteindex >= 4 then local bits = explodebits(mem.csr[0xc81],32) local newbits = explodebits(thisbyte,8) byteindex = byteindex - 4 for i=0,7 do bits[byteindex*8+i] = newbits[i] end mem.csr[0xc81] = implodebits(bits,32) else local bits = explodebits(os.time()-mem.starttime,32) local newbits = explodebits(thisbyte,8) for i=0,7 do bits[byteindex*8+i] = newbits[i] end local newmtime = implodebits(bits,32) mem.starttime = os.time()-newmtime end elseif offsetaddr >= mem.csr[0x801]+10 and offsetaddr < mem.csr[0x801]+10+8 and mem.csr[0x801] ~= 0 then --mtimecmp local byteindex = offsetaddr - (mem.csr[0x801]+10) if mem.mbe then byteindex = 7-byteindex end if byteindex >= 4 then local bits = explodebits(mem.mtimecmphigh,32) local newbits = explodebits(thisbyte,8) byteindex = byteindex - 4 for i=0,7 do bits[byteindex*8+i] = newbits[i] end mem.mtimecmphigh = implodebits(bits,32) else local bits = explodebits(mem.mtimecmplow,32) local newbits = explodebits(thisbyte,8) for i=0,7 do bits[byteindex*8+i] = newbits[i] end mem.mtimecmplow = implodebits(bits,32) end elseif address < RAM_SIZE then local segment = math.floor(offsetaddr/256) local offset = offsetaddr%256 mem.ram[segment] = string.sub(mem.ram[segment],0,offset)..string.char(thisbyte)..string.sub(mem.ram[segment],offset+2,-1) end end end local stdoutdirty = false local function scrollstdout(lines) lines = lines or 1 for i=1,lines do table.remove(mem.stdout,1) mem.stdout[6] = "" end end local function stdout(text) if string.len(text) == 0 then return end if STDOUT_TO_TERMINAL then print(text) end stdoutdirty = true for i=1,string.len(text) do local thischar = string.sub(text,i,i) if string.len(mem.stdout[6]) == 20 or thischar == "\n" then scrollstdout(1) if thischar ~= "\n" then mem.stdout[6] = mem.stdout[6]..thischar end elseif thischar ~= string.char(0) then mem.stdout[6] = mem.stdout[6]..thischar end end end local function readcsr(address) if address == 0x341 then --mepc local ret = mem.csr[0x341] --mepc[0] must be 0 if ret%2 == 1 then ret = ret-1 end --mepc[1] must be 0 if IALIGN=32 if not mem.isa.c and ret%4 >= 2 then ret = ret - 2 end return ret elseif address == 0x300 then --mstatus local bits = {} bits[3] = mem.mie bits[7] = mem.mpie bits[11] = mem.mpp%2 == 1 bits[12] = mem.mpp >= 2 return implodebits(bits,32) elseif address == 0x310 then --mstatush local bits = {} bits[5] = mem.mbe bits[9] = mem.mpelp bits[10] = mem.mdt return implodebits(bits,32) elseif address == 0x747 then --mseccfg local bits = {} bits[10] = mem.mlpe return implodebits(bits,32) elseif not mem.csr[address] then digiline_send("monitordisp",string.format("Attempted read\nfrom unknown\nCSR 0x%03X\nPC: %08X",address,mem.registers.pc)) fault = 2 fval = readram(mem.registers.pc,4) return 0 elseif address == 0xb00 or address == 0xb02 or address == 0xb80 or address == 0xb82 then --mcycle(h)/minstret(h) - redirect to cycle(h)/instret(h) address = address + 0x100 end return mem.csr[address] end local function writecsr(address,data) if address == 0x17 then --jvt data = math.floor(data/2^6)*2^6 --WARL, and mode is required to be 0 elseif address == 0xc00 or address == 0xc01 or address == 0xc02 or address == 0xc80 or address == 0xc81 or address == 0xc82 then --One of the read-only CSRs from Zicntr fault = 2 fval = readram(mem.registers.pc,4) return elseif address == 0xf11 or address == 0xf12 or address == 0xf13 or address == 0xf14 or address == 0xf15 then --Read-only machine information register fault = 2 fval = readram(mem.registers.pc,4) return elseif address == 0x300 then --mstatus local bits = explodebits(data,32) mem.mie = bits[3] and not mem.mdt mem.mpie = bits[7] return elseif address == 0x310 then --mstatush local bits = explodebits(data,32) mem.mbe = bits[5] mem.mpelp = bits[9] mem.mdt = bits[10] return elseif address == 0x301 then --misa local bits = explodebits(data,32) if mem.registers.pc % 4 == 2 and not bits[2] then digiline_send("monitordisp",string.format("Attempted to disable\nC extension but\ninstruction would be\nmisaligned,\nwrite ignored\nPC: %08X",mem.registers.pc)) end mem.isa.a = bits[0] mem.isa.b = bits[1] mem.isa.c = bits[2] mem.isa.i = bits[8] mem.isa.m = bits[12] mem.isa.x = bits[23] local newbits = {[0] = mem.isa.a,mem.isa.b,mem.isa.c,[4] = not mem.isa.i,[8] = mem.isa.i,[12] = mem.isa.m,[23] = mem.isa.x,[30] = true} mem.csr[0x301] = implodebits(newbits,32) return elseif address == 0x801 then --MMIO base address local oldaddr = readcsr(0x801) if oldaddr ~= 0 then writeram(oldaddr,0,1,true) end --Invalidate reservation set at old address mem.csr[0x801] = data if data ~= 0 then writeram(data,0,1,true) --Invalidate reservation set at new address end return elseif address == 0x305 then --mtvec mem.trapmode = data%4 mem.trapbase = data-mem.trapmode mem.mtvecwritten = true return elseif address == 0xb00 or address == 0xb02 or address == 0xb80 or address == 0xb82 then --mcycle(h)/minstret(h) - redirect to cycle(h)/instret(h) address = address + 0x100 elseif address == 0x747 then --mseccfg then local bits = explodebits(data,32) mem.mlpe = bits[10] return end if not mem.csr[address] then digiline_send("monitordisp",string.format("Attempted write\nto unknown\nCSR 0x%03X\nPC: %08X",address,mem.registers.pc)) fault = 2 fval = readram(mem.registers.pc,4) return end mem.csr[address] = data end local function trap(reason,val) local oldreason = reason if mem.mdt then writecsr(0x34b,reason) --mtval2 reason = 0x10 end writecsr(0x342,reason) --mcause writecsr(0x341,mem.registers.pc) --mepc writecsr(0x343,val or 0) --mtval mem.mpie = mem.mie mem.mie = false mem.mpelp = mem.mlpe and mem.elp mem.elp = false if not mem.mdt then mem.mdt = true if mem.trapmode == 1 then --vectored mode local int = reason >= 0x8000000 reason = reason%0x80000000 mem.registers.pc = reason*4+mem.trapbase return true,false else --direct or reserved (treated as direct) mode mem.registers.pc = mem.trapbase return true,false end else digiline_send("monitordisp","Unexpected trap!") if not mem.mtvecwritten then digiline_send("monitordisp",traptypes[oldreason] or "Unknown reason") if oldreason == 18 then local subtypes = { [2] = "Landing pad fault", [3] = "Shadow stack fault", } digiline_send("monitordisp",subtypes[val] or "Unknown cause") end digiline_send("monitordisp","(no handler present)") else digiline_send("monitordisp",traptypes[reason] or "Unknown reason") if reason == 18 then local subtypes = { [2] = "Landing pad fault", [3] = "Shadow stack fault", } digiline_send("monitordisp",subtypes[val] or "Unknown cause") end end digiline_send("monitordisp",string.format("PC: %08X",mem.registers.pc)) digiline_send("monitordisp","System halted") mem.running = false return true,true end end local function checkinterrupts() local miebits = explodebits(mem.csr[0x304],32) local mipbits = explodebits(mem.csr[0x344],32) if mem.mtimecmphigh == mem.csr[0xc81] and (os.time() - mem.starttime) >= mem.mtimecmplow then mipbits[7] = true --mtip elseif mem.mtimecmphigh < mem.csr[0xc81] then mipbits[7] = true --mtip else mipbits[7] = false end mem.csr[0x344] = implodebits(mipbits,32) if not mem.mie then return end local order = {11,3,7,9,1,5,13,16,17} for _,i in ipairs(order) do if miebits[i] and mipbits[i] then return trap(0x80000000+i) end end end local cmpushpopreglists = { [4] = {registers = {1},stack = 16}, [5] = {registers = {1,8},stack = 16}, [6] = {registers = {1,8,9},stack = 16}, [7] = {registers = {1,8,9,18},stack = 16}, [8] = {registers = {1,8,9,18,19},stack = 32}, [9] = {registers = {1,8,9,18,19,20},stack = 32}, [10] = {registers = {1,8,9,18,19,20,21},stack = 32}, [11] = {registers = {1,8,9,18,19,20,21,22},stack = 32}, [12] = {registers = {1,8,9,18,19,20,21,22,23},stack = 48}, [13] = {registers = {1,8,9,18,19,20,21,22,23,24},stack = 48}, [14] = {registers = {1,8,9,18,19,20,21,22,23,24,25},stack = 48}, [15] = {registers = {1,8,9,18,19,20,21,22,23,24,25,26,27},stack = 64}, } local operations = { add = function(rd,rs1,rs2) setreg(rd,getreg(rs1)+getreg(rs2)) end, sub = function(rd,rs1,rs2) setreg(rd,getreg(rs1)-getreg(rs2)) end, xor = function(rd,rs1,rs2) local rs1bits = explodebits(getreg(rs1),32) local rs2bits = explodebits(getreg(rs2),32) local rdbits = {} for i=0,31 do rdbits[i] = rs1bits[i] ~= rs2bits[i] end setreg(rd,implodebits(rdbits,32,false)) end, orr = function(rd,rs1,rs2) --instruction is "or" but that's a reserved word local rs1bits = explodebits(getreg(rs1),32) local rs2bits = explodebits(getreg(rs2),32) local rdbits = {} for i=0,31 do rdbits[i] = rs1bits[i] or rs2bits[i] end setreg(rd,implodebits(rdbits,32,false)) end, andr = function(rd,rs1,rs2) --instruction is "and" but that's a reserved word local rs1bits = explodebits(getreg(rs1),32) local rs2bits = explodebits(getreg(rs2),32) local rdbits = {} for i=0,31 do rdbits[i] = rs1bits[i] and rs2bits[i] end setreg(rd,implodebits(rdbits,32,false)) end, xnor = function(rd,rs1,rs2) local rs1bits = explodebits(getreg(rs1),32) local rs2bits = explodebits(getreg(rs2),32) local rdbits = {} for i=0,31 do rdbits[i] = rs1bits[i] == rs2bits[i] end setreg(rd,implodebits(rdbits,32,false)) end, orn = function(rd,rs1,rs2) local rs1bits = explodebits(getreg(rs1),32) local rs2bits = explodebits(getreg(rs2),32) local rdbits = {} for i=0,31 do rdbits[i] = rs1bits[i] or not rs2bits[i] end setreg(rd,implodebits(rdbits,32,false)) end, andn = function(rd,rs1,rs2) local rs1bits = explodebits(getreg(rs1),32) local rs2bits = explodebits(getreg(rs2),32) local rdbits = {} for i=0,31 do rdbits[i] = rs1bits[i] and not rs2bits[i] end setreg(rd,implodebits(rdbits,32,false)) end, sll = function(rd,rs1,rs2) setreg(rd,getreg(rs1)*(2^(getreg(rs2)%2^5))) end, srl = function(rd,rs1,rs2) setreg(rd,getreg(rs1)/(2^(getreg(rs2)%2^5))) end, sra = function(rd,rs1,rs2) local num = getreg(rs1) local amount = getreg(rs2)%2^5 local sign = num>=(2^31) if amount == 0 then return end for _=1,amount do num = num/2 if sign then num = num+(2^31) end end setreg(rd,num) end, slt = function(rd,rs1,rs2) local num1 = getreg(rs1) local num2 = getreg(rs2) if num1 >= 2^31 then num1 = num1-(2^32) end if num2 >= 2^31 then num2 = num2-(2^32) end setreg(rd,num1 < num2 and 1 or 0) end, sltu = function(rd,rs1,rs2) local num1 = getreg(rs1) local num2 = getreg(rs2) setreg(rd,num1 < num2 and 1 or 0) end, addi = function(rd,rs1,imm) local immbits = explodebits(imm,12) signextend(immbits,12,32) imm = implodebits(immbits,32,false) setreg(rd,getreg(rs1)+imm) end, xori = function(rd,rs1,imm) local rs1bits = explodebits(getreg(rs1),32) local immbits = explodebits(imm,12) signextend(immbits,12,32) local rdbits = {} for i=0,31 do rdbits[i] = rs1bits[i] ~= immbits[i] end setreg(rd,implodebits(rdbits,32,false)) end, ori = function(rd,rs1,imm) local rs1bits = explodebits(getreg(rs1),32) local immbits = explodebits(imm,12) signextend(immbits,12,32) local rdbits = {} for i=0,31 do rdbits[i] = rs1bits[i] or immbits[i] end setreg(rd,implodebits(rdbits,32,false)) end, andi = function(rd,rs1,imm) local rs1bits = explodebits(getreg(rs1),32) local immbits = explodebits(imm,12) signextend(immbits,12,32) local rdbits = {} for i=0,31 do rdbits[i] = rs1bits[i] and immbits[i] end setreg(rd,implodebits(rdbits,32,false)) end, slli = function(rd,rs1,imm) setreg(rd,getreg(rs1)*(2^imm)) end, srli = function(rd,rs1,imm) setreg(rd,getreg(rs1)/(2^imm)) end, srai = function(rd,rs1,imm) local num = getreg(rs1) local sign = num>=(2^31) if imm == 0 then return end for _=1,imm do num = num/2 if sign then num = num+(2^31) end end setreg(rd,num) end, slti = function(rd,rs1,imm) local num1 = getreg(rs1) local immbits = explodebits(imm,12) signextend(immbits,12,32) local num2 = implodebits(immbits,32,false) if num1 >= 2^31 then num1 = num1-(2^32) end if num2 >= 2^31 then num2 = num2-(2^32) end setreg(rd,num1 < num2 and 1 or 0) end, sltiu = function(rd,rs1,imm) local num1 = getreg(rs1) local immbits = explodebits(imm,12) signextend(immbits,12,32) local num2 = implodebits(immbits,32,false) setreg(rd,num1 < num2 and 1 or 0) end, beq = function(rs1,rs2,imm) local num1 = getreg(rs1) local num2 = getreg(rs2) if num1 == num2 then local immbits = explodebits(imm,13) signextend(immbits,13,32) imm = implodebits(immbits,13,true) mem.registers.pc = (mem.registers.pc + imm) % 2^32 return true end end, bne = function(rs1,rs2,imm) local num1 = getreg(rs1) local num2 = getreg(rs2) if num1 ~= num2 then local immbits = explodebits(imm,13) signextend(immbits,13,32) imm = implodebits(immbits,13,true) mem.registers.pc = (mem.registers.pc + imm) % 2^32 return true end end, blt = function(rs1,rs2,imm) local num1 = getreg(rs1) local num2 = getreg(rs2) if num1 >= 2^31 then num1 = num1-(2^32) end if num2 >= 2^31 then num2 = num2-(2^32) end if num1 < num2 then local immbits = explodebits(imm,13) signextend(immbits,13,32) imm = implodebits(immbits,13,true) mem.registers.pc = (mem.registers.pc + imm) % 2^32 return true end end, bge = function(rs1,rs2,imm) local num1 = getreg(rs1) local num2 = getreg(rs2) if num1 >= 2^31 then num1 = num1-(2^32) end if num2 >= 2^31 then num2 = num2-(2^32) end if num1 >= num2 then local immbits = explodebits(imm,13) signextend(immbits,13,32) imm = implodebits(immbits,13,true) mem.registers.pc = (mem.registers.pc + imm) % 2^32 return true end end, bltu = function(rs1,rs2,imm) local num1 = getreg(rs1) local num2 = getreg(rs2) if num1 < num2 then local immbits = explodebits(imm,13) signextend(immbits,13,32) imm = implodebits(immbits,13,true) mem.registers.pc = (mem.registers.pc + imm) % 2^32 return true end end, bgeu = function(rs1,rs2,imm) local num1 = getreg(rs1) local num2 = getreg(rs2) if num1 >= num2 then local immbits = explodebits(imm,13) signextend(immbits,13,32) imm = implodebits(immbits,13,true) mem.registers.pc = (mem.registers.pc + imm) % 2^32 return true end end, jal = function(rd,imm,compressed) setreg(rd,mem.registers.pc+(compressed and 2 or 4)) local immbits = explodebits(imm,21) signextend(immbits,21,32) imm = implodebits(immbits,32,true) local oldpc = mem.registers.pc mem.registers.pc = (mem.registers.pc + imm) % 2^32 return true end, jalr = function(rd,rs1,imm,compressed) local oldpc = mem.registers.pc local immbits = explodebits(imm,12) signextend(immbits,12,32) imm = implodebits(immbits,32,true) mem.registers.pc = (getreg(rs1) + imm) % 2^32 if mem.registers.pc%2 == 1 then mem.registers.pc = mem.registers.pc - 1 end setreg(rd,oldpc+(compressed and 2 or 4)) return true end, lui = function(rd,imm) setreg(rd,imm) end, auipc = function(rd,imm) setreg(rd,mem.registers.pc+imm) end, lb = function(rd,rs1,imm) if imm >= 2^11 then imm = imm - 2^12 end local data = readram(getreg(rs1)+imm,1) data = explodebits(data,8) signextend(data,8,32) data = implodebits(data,32,false) setreg(rd,data) end, lh = function(rd,rs1,imm) if imm >= 2^11 then imm = imm - 2^12 end local data = readram(getreg(rs1)+imm,2) data = explodebits(data,16) signextend(data,16,32) data = implodebits(data,32,false) setreg(rd,data) end, lw = function(rd,rs1,imm) if imm >= 2^11 then imm = imm - 2^12 end local data = readram(getreg(rs1)+imm,4) setreg(rd,data) end, ld = function(rd,rs1,imm) if rd == 0 or rd == 31 then return end if imm >= 2^11 then imm = imm - 2^12 end local address = getreg(rs1)+imm setreg(rd,readram(address+(mem.mbe and 4 or 0),4)) setreg(rd+1,readram(address+(mem.mbe and 0 or 4),4)) end, lhu = function(rd,rs1,imm) if imm >= 2^11 then imm = imm - 2^12 end local data = readram(getreg(rs1)+imm,2) setreg(rd,data) end, lbu = function(rd,rs1,imm) if imm >= 2^11 then imm = imm - 2^12 end local data = readram(getreg(rs1)+imm,1) setreg(rd,data) end, sb = function(rs1,rs2,imm) if imm >= 2^11 then imm = imm - 2^12 end writeram(getreg(rs1)+imm,getreg(rs2)%0x100,1) end, sh = function(rs1,rs2,imm) if imm >= 2^11 then imm = imm - 2^12 end writeram(getreg(rs1)+imm,getreg(rs2)%0x10000,2) end, sw = function(rs1,rs2,imm) if imm >= 2^11 then imm = imm - 2^12 end writeram(getreg(rs1)+imm,getreg(rs2),4) end, sd = function(rs1,rs2,imm) if rs2 == 31 then return end if imm >= 2^11 then imm = imm - 2^12 end local address = getreg(rs1)+imm writeram(address+(mem.mbe and 4 or 0),getreg(rs2),4) writeram(address+(mem.mbe and 0 or 4),getreg(rs2+1),4) end, ecall = function() local func = mem.isa.i and getreg(17) or getreg(15) if mem.isa.i and func == 0 then func = getreg(15) end local param1 = getreg(10) local param2 = getreg(11) if func == 1 then --a7=1 - Print Integer if param1 > 2^31 then param1 = param1-2^32 end stdout(string.format("%d",param1)) elseif func == 4 then --a7=4 - Print String local str = "" for i=0,255 do local byte = readram(param1+i,1) if byte == 0 then break end str = str..string.char(byte) end stdout(str) elseif func == 5 then --a7=5 - Read Integer digiline_send("monitordisp","Waiting for input") mem.running = false mem.inputwaiting = true mem.inputwaittype = "integer" return false,true elseif func == 8 then --a7=8 - Read String digiline_send("monitordisp","Waiting for input") mem.running = false mem.inputwaiting = true mem.inputwaittype = "string" mem.inputaddr = getreg(10) mem.inputmax = getreg(11) return false,true elseif func == 10 then --a7=10 - Exit Program mem.running = false digiline_send("monitordisp","Program\nexited normally") digiline_send("monitordisp","System halted") digiline_send("monitordisp",string.format("PC:%08X",mem.registers.pc)) elseif func == 11 then --a7=11 - Print Character stdout(string.char(getreg(10))) elseif func == 12 then --a7=12 - Read Character digiline_send("monitordisp","Waiting for input") mem.running = false mem.inputwaiting = true mem.inputwaittype = "char" return false,true elseif func == 128 then --a7=128 - Get Random Integer local lowlimit = getreg(10) local highlimit = getreg(11) if lowlimit >= 2^31 then lowlimit = lowlimit - 2^32 end if highlimit >= 2^31 then highlimit = highlimit - 2^32 end local result = math.random(lowlimit,highlimit) if result < 0 then result = result + 2^32 end setreg(10,result) elseif func == 129 then --a7=129 - Send digilines string message local channel = "" for i=0,255 do local byte = readram(param1+i,1) if byte == 0 then break end channel = channel..string.char(byte) end local message = "" for i=0,255 do local byte = readram(param2+i,1) if byte == 0 then break end message = message..string.char(byte) end digiline_send(channel,message) elseif func == 130 then --a7=130 - Get Input Buffer Level setreg(10,string.len(mem.inputbuf)) elseif func == 131 then --a7=131 - Clear Input Buffer mem.inputbuf = "" elseif func == 132 then --a7=132 -- Read Character From Input Buffer if mem.inputbuf == "" then setreg(10,0) else setreg(10,string.byte(string.sub(mem.inputbuf,1,1))) mem.inputbuf = string.sub(mem.inputbuf,2,-1) end elseif func == 133 then --a7=133 - Get Digilines Buffer Level setreg(10,#mem.digilinesqueue) elseif func == 134 then --a7=134 - Clear Digilines Buffer mem.digilinesqueue = {} elseif func == 135 then --a7=135 - Read Digilines Message local dlevent = mem.digilinesqueue[1] if not dlevent then dlevent = {channel = "",msg = ""} end dlevent.channel = string.sub(dlevent.channel,1,getreg(11)-1).."\0" dlevent.msg = string.sub(dlevent.msg,1,getreg(13)-1).."\0" local channelstart = getreg(10) for i=1,string.len(dlevent.channel) do local address = channelstart+i-1 writeram(address,string.byte(string.sub(dlevent.channel,i,i)),1) end local msgstart = getreg(12) for i=1,string.len(dlevent.msg) do local address = msgstart+i-1 writeram(address,string.byte(string.sub(dlevent.msg,i,i)),1) end table.remove(mem.digilinesqueue,1) end end, ebreak = function() return trap(3,0) end, csrrw = function(rd,rs1,imm) local out = readcsr(imm) writecsr(imm,getreg(rs1)) setreg(rd,out) return checkinterrupts() end, csrrs = function(rd,rs1,imm) local out = readcsr(imm) if rs1 == 0 then setreg(rd,out) return end local csrbits = explodebits(readcsr(imm),32) local rs1bits = explodebits(getreg(rs1),32) for i=0,31 do csrbits[i] = csrbits[i] or rs1bits[i] end writecsr(imm,implodebits(csrbits,32)) setreg(rd,out) return checkinterrupts() end, csrrc = function(rd,rs1,imm) local out = readcsr(imm) if rs1 == 0 then setreg(rd,out) return end local csrbits = explodebits(readcsr(imm),32) local rs1bits = explodebits(getreg(rs1),32) for i=0,31 do csrbits[i] = csrbits[i] and not rs1bits[i] end writecsr(imm,implodebits(csrbits,32)) setreg(rd,out) return checkinterrupts() end, csrrwi = function(rd,rs1,imm) setreg(rd,readcsr(imm)) writecsr(imm,rs1) return checkinterrupts() end, csrrsi = function(rd,rs1,imm) setreg(rd,readcsr(imm)) if rs1 == 0 then return end local csrbits = explodebits(readcsr(imm),32) local rs1bits = explodebits(rs1,32) for i=0,31 do csrbits[i] = csrbits[i] or rs1bits[i] end writecsr(imm,implodebits(csrbits,32)) return checkinterrupts() end, csrrci = function(rd,rs1,imm) setreg(rd,readcsr(imm)) if rs1 == 0 then return end local csrbits = explodebits(readcsr(imm),32) local rs1bits = explodebits(rs1,32) for i=0,31 do csrbits[i] = csrbits[i] and not rs1bits[i] end writecsr(imm,implodebits(csrbits,32)) return checkinterrupts() end, mul = function(rd,rs1,rs2) local plow = mul64(getreg(rs1),getreg(rs2),false,false) setreg(rd,plow) end, mulh = function(rd,rs1,rs2) local _,phigh = mul64(getreg(rs1),getreg(rs2),true,true) setreg(rd,phigh) end, mulsu = function(rd,rs1,rs2) local _,phigh = mul64(getreg(rs1),getreg(rs2),true,false) setreg(rd,phigh) end, mulu = function(rd,rs1,rs2) local _,phigh = mul64(getreg(rs1),getreg(rs2),false,false) setreg(rd,phigh) end, div = function(rd,rs1,rs2) local a = getreg(rs1) local b = getreg(rs2) if b == 0 then --Division by zero setreg(rd,2^32-1) return end if a >= 2^31 then a = a - 2^32 end if b >= 2^31 then b = b - 2^32 end if a == -2^31 and b == -1 then --Divide overflow setreg(rd,a) end local result = math.floor(a/b) if result < 0 then result = result + 2^32 end setreg(rd,result) end, rem = function(rd,rs1,rs2) local a = getreg(rs1) local b = getreg(rs2) if b == 0 then --Division by zero setreg(rd,a) return end if a >= 2^31 then a = a - 2^32 end if b >= 2^31 then b = b - 2^32 end if a == -2^31 and b == -1 then --Divide overflow setreg(rd,0) end local result = a%b setreg(rd,result) end, divu = function(rd,rs1,rs2) local a = getreg(rs1) local b = getreg(rs2) if b == 0 then --Division by zero setreg(rd,2^32-1) return end local result = math.floor(a/b) setreg(rd,result) end, remu = function(rd,rs1,rs2) local a = getreg(rs1) local b = getreg(rs2) if b == 0 then --Division by zero setreg(rd,a) return end local result = a%b setreg(rd,result) end, sh1add = function(rd,rs1,rs2) setreg(rd,getreg(rs1)*2+getreg(rs2)) end, sh2add = function(rd,rs1,rs2) setreg(rd,getreg(rs1)*4+getreg(rs2)) end, sh3add = function(rd,rs1,rs2) setreg(rd,getreg(rs1)*8+getreg(rs2)) end, clz = function(rd,rs1) local bits = explodebits(getreg(rs1),32) for i=31,0,-1 do if bits[i] then setreg(rd,31-i) return end end setreg(rd,32) end, ctz = function(rd,rs1) local bits = explodebits(getreg(rs1),32) for i=0,31 do if bits[i] then setreg(rd,i) return end end setreg(rd,32) end, cpop = function(rd,rs1) local bits = explodebits(getreg(rs1),32) local out = 0 for i=0,31 do if bits[i] then out = out + 1 end end setreg(rd,out) end, maxu = function(rd,rs1,rs2) setreg(rd,math.max(getreg(rs1),getreg(rs2))) end, minu = function(rd,rs1,rs2) setreg(rd,math.min(getreg(rs1),getreg(rs2))) end, max = function(rd,rs1,rs2) local a = getreg(rs1) local b = getreg(rs2) if a >= 2^31 then a = a - 2^32 end if b >= 2^31 then b = b - 2^32 end local out = math.max(a,b) if out < 0 then out = out + 2^32 end setreg(rd,out) end, min = function(rd,rs1,rs2) local a = getreg(rs1) local b = getreg(rs2) if a >= 2^31 then a = a - 2^32 end if b >= 2^31 then b = b - 2^32 end local out = math.min(a,b) if out < 0 then out = out + 2^32 end setreg(rd,out) end, sextb = function(rd,rs1) local bits = explodebits(getreg(rs1),8) signextend(bits,8,32) setreg(rd,implodebits(bits,32)) end, sexth = function(rd,rs1) local bits = explodebits(getreg(rs1),16) signextend(bits,16,32) setreg(rd,implodebits(bits,32)) end, zexth = function(rd,rs1) setreg(rd,getreg(rs1)%2^16) end, rol = function(rd,rs1,rs2) local num = getreg(rs1) local amount = getreg(rs2)%32 if amount == 0 then return end for i=1,amount do local endbit = num >= 2^31 num = (num*2)%2^32 if endbit then num = num + 1 end end setreg(rd,num) end, ror = function(rd,rs1,rs2) local num = getreg(rs1) local amount = getreg(rs2)%32 if amount == 0 then setreg(rd,num) return end for _=1,amount do local endbit = num%2 == 1 num = math.floor(num/2) if endbit then num = num + 2^31 end end setreg(rd,num) end, rori = function(rd,rs1,imm) local num = getreg(rs1) local amount = imm%32 if amount == 0 then setreg(rd,num) return end for _=1,amount do local endbit = num%2 == 1 num = math.floor(num/2) if endbit then num = num + 2^31 end end setreg(rd,num) end, orcb = function(rd,rs1) local input = getreg(rs1) local b1 = input%2^8 > 0 local b2 = math.floor(input/2^8)%2^8 > 0 local b3 = math.floor(input/2^16)%2^8 > 0 local b4 = math.floor(input/2^24)%2^8 > 0 local out = 0 if b1 then out = out + 0xff end if b2 then out = out + 0xff00 end if b3 then out = out + 0xff0000 end if b4 then out = out + 0xff000000 end setreg(rd,out) end, rev8 = function(rd,rs1) local input = getreg(rs1) local b1 = input%2^8 local b2 = math.floor(input/2^8)%2^8 local b3 = math.floor(input/2^16)%2^8 local b4 = math.floor(input/2^24)%2^8 local out = b4 out = out + (b3*2^8) out = out + (b2*2^16) out = out + (b1*2^24) setreg(rd,out) end, bclr = function(rd,rs1,rs2) local bits = explodebits(getreg(rs1),32) bits[getreg(rs2)%32] = false setreg(rd,implodebits(bits,32)) end, bset = function(rd,rs1,rs2) local bits = explodebits(getreg(rs1),32) bits[getreg(rs2)%32] = true setreg(rd,implodebits(bits,32)) end, binv = function(rd,rs1,rs2) local bits = explodebits(getreg(rs1),32) bits[getreg(rs2)%32] = not bits[getreg(rs2)%32] setreg(rd,implodebits(bits,32)) end, bclri = function(rd,rs1,imm) local bits = explodebits(getreg(rs1),32) bits[imm%32] = false setreg(rd,implodebits(bits,32)) end, bseti = function(rd,rs1,imm) local bits = explodebits(getreg(rs1),32) bits[imm%32] = true setreg(rd,implodebits(bits,32)) end, binvi = function(rd,rs1,imm) local bits = explodebits(getreg(rs1),32) bits[imm%32] = not bits[imm%32] setreg(rd,implodebits(bits,32)) end, bext = function(rd,rs1,rs2) local bit = explodebits(getreg(rs1),32)[getreg(rs2)%32] setreg(rd,bit and 1 or 0) end, bexti = function(rd,rs1,imm) local bit = explodebits(getreg(rs1),32)[imm%32] setreg(rd,bit and 1 or 0) end, xperm8 = function(rd,rs1,rs2) local a = getreg(rs1) local outputs = { [0] = a%2^8, math.floor(a/2^8)%2^8, math.floor(a/2^16)%2^8, math.floor(a/2^24)%2^8, } local b = getreg(rs2) local selections = { [0] = b%2^8, math.floor(b/2^8)%2^8, math.floor(b/2^16)%2^8, math.floor(b/2^24)%2^8, } local out = 0 for i=0,3 do local num = outputs[selections[i]] or 0 out = out+(num*2^(i*8)) end setreg(rd,out) end, xperm4 = function(rd,rs1,rs2) local a = getreg(rs1) local outputs = { [0] = a%2^4, math.floor(a/2^4)%2^4, math.floor(a/2^8)%2^4, math.floor(a/2^12)%2^4, math.floor(a/2^16)%2^4, math.floor(a/2^20)%2^4, math.floor(a/2^24)%2^4, math.floor(a/2^28)%2^4, } local b = getreg(rs2) local selections = { [0] = b%2^4, math.floor(b/2^4)%2^4, math.floor(b/2^8)%2^4, math.floor(b/2^12)%2^4, math.floor(b/2^16)%2^4, math.floor(b/2^20)%2^4, math.floor(b/2^24)%2^4, math.floor(b/2^28)%2^4, } local out = 0 for i=0,7 do local num = outputs[selections[i]] or 0 out = out+(num*2^(i*4)) end setreg(rd,out) end, lr = function(rd,rs1,rs2) local address = getreg(rs1) setreg(rd,readram(address,4)) mem.reservationset = address end, sc = function(rd,rs1,rs2) local address = getreg(rs1) if mem.reservationset == address then writeram(address,getreg(rs2),4) setreg(rd,0) else setreg(rd,1) end mem.reservationset = nil end, amoswapw = function(rd,rs1,rs2) local address = getreg(rs1) local newdata = getreg(rs2) setreg(rd,readram(address,4)) writeram(address,newdata,4) end, amoaddw = function(rd,rs1,rs2) local address = getreg(rs1) local data = readram(address,4) local newdata = getreg(rs2) setreg(rd,data) writeram(address,(data+newdata)%2^32,4) end, amoandw = function(rd,rs1,rs2) local address = getreg(rs1) local data = readram(address,4) local newdata = getreg(rs2) setreg(rd,data) local bits = explodebits(data,32) local newbits = explodebits(newdata,32) for i=0,31 do bits[i] = bits[i] and newbits[i] end writeram(address,implodebits(bits,32),4) end, amoorw = function(rd,rs1,rs2) local address = getreg(rs1) local data = readram(address,4) local newdata = getreg(rs2) setreg(rd,data) local bits = explodebits(data,32) local newbits = explodebits(newdata,32) for i=0,31 do bits[i] = bits[i] or newbits[i] end writeram(address,implodebits(bits,32),4) end, amoxorw = function(rd,rs1,rs2) local address = getreg(rs1) local data = readram(address,4) local newdata = getreg(rs2) setreg(rd,data) local bits = explodebits(data,32) local newbits = explodebits(newdata,32) for i=0,31 do bits[i] = bits[i] ~= newbits[i] end writeram(address,implodebits(bits,32),4) end, amomaxw = function(rd,rs1,rs2) local address = getreg(rs1) local data = readram(address,4) local newdata = getreg(rs2) setreg(rd,data) if data > 2^31 then data = data - 2^32 end if newdata > 2^31 then newdata = newdata - 2^32 end newdata = math.max(data,newdata) if newdata < 0 then newdata = newdata + 2^32 end writeram(address,newdata,4) end, amomaxuw = function(rd,rs1,rs2) local address = getreg(rs1) local data = readram(address,4) local newdata = getreg(rs2) setreg(rd,data) newdata = math.max(data,newdata) writeram(address,newdata,4) end, amominw = function(rd,rs1,rs2) local address = getreg(rs1) local data = readram(address,4) local newdata = getreg(rs2) setreg(rd,data) if data > 2^31 then data = data - 2^32 end if newdata > 2^31 then newdata = newdata - 2^32 end newdata = math.min(data,newdata) if newdata < 0 then newdata = newdata + 2^32 end writeram(address,newdata,4) end, amominuw = function(rd,rs1,rs2) local address = getreg(rs1) local data = readram(address,4) local newdata = getreg(rs2) setreg(rd,data) newdata = math.min(data,newdata) writeram(address,newdata,4) end, amocasw = function(rd,rs1,rs2) local address = getreg(rs1) local data = readram(address,4) local compare = getreg(rd) if data == compare then writeram(address,getreg(rs2),4) end setreg(rd,data) end, amocasd = function(rd,rs1,rs2) if rd == 31 then return end if rs2 == 31 then return end local address = getreg(rs1) local datalow = readram(address,4) local datahigh = readram(address+4,4) local comparelow = getreg(rd) local comparehigh = rd == 0 and 0 or getreg(rd+1) if datalow == comparelow and datahigh == comparehigh then writeram(address,getreg(rs2),4) writeram(address+4,rs2 == 0 and 0 or getreg(rs2+1),4) end if rd ~= 0 then setreg(rd,datalow) setreg(rd+1,datahigh) end end, amoswaph = function(rd,rs1,rs2) local address = getreg(rs1) local newdata = getreg(rs2)%2^16 local olddata = explodebits(readram(address,2),16) signextend(olddata,16,32) setreg(rd,implodebits(olddata,32)) writeram(address,newdata,2) end, amoaddh = function(rd,rs1,rs2) local address = getreg(rs1) local data = readram(address,2) local newdata = getreg(rs2)%2^16 local oldbits = explodebits(data,16) signextend(oldbits,16,32) setreg(rd,implodebits(oldbits,32)) writeram(address,(data+newdata)%2^16,2) end, amoandh = function(rd,rs1,rs2) local address = getreg(rs1) local data = readram(address,2) local newdata = getreg(rs2)%2^16 local bits = explodebits(data,16) signextend(bits,16,32) setreg(rd,implodebits(bits,32)) local newbits = explodebits(newdata,16) for i=0,15 do bits[i] = bits[i] and newbits[i] end writeram(address,implodebits(bits,16),2) end, amoorh = function(rd,rs1,rs2) local address = getreg(rs1) local data = readram(address,2) local newdata = getreg(rs2)%2^16 local bits = explodebits(data,16) signextend(bits,16,32) setreg(rd,implodebits(bits,32)) local newbits = explodebits(newdata,16) for i=0,15 do bits[i] = bits[i] or newbits[i] end writeram(address,implodebits(bits,16),2) end, amoxorh = function(rd,rs1,rs2) local address = getreg(rs1) local data = readram(address,2) local newdata = getreg(rs2)%2^16 local bits = explodebits(data,16) signextend(bits,16,32) setreg(rd,implodebits(bits,32)) local newbits = explodebits(newdata,16) for i=0,15 do bits[i] = bits[i] ~= newbits[i] end writeram(address,implodebits(bits,16),2) end, amomaxh = function(rd,rs1,rs2) local address = getreg(rs1) local data = readram(address,2) local newdata = getreg(rs2)%2^16 local bits = explodebits(data,16) signextend(bits,16,32) setreg(rd,implodebits(bits,32)) if data > 2^15 then data = data - 2^16 end if newdata > 2^15 then newdata = newdata - 2^16 end newdata = math.max(data,newdata) if newdata < 0 then newdata = newdata + 2^16 end writeram(address,newdata,2) end, amomaxuh = function(rd,rs1,rs2) local address = getreg(rs1) local data = readram(address,2) local newdata = getreg(rs2)%2^16 local bits = explodebits(data,16) signextend(bits,16,32) setreg(rd,implodebits(bits,32)) newdata = math.max(data,newdata) writeram(address,newdata,2) end, amominh = function(rd,rs1,rs2) local address = getreg(rs1) local data = readram(address,2) local newdata = getreg(rs2)%2^16 local bits = explodebits(data,16) signextend(bits,16,32) setreg(rd,implodebits(bits,32)) if data > 2^15 then data = data - 2^16 end if newdata > 2^15 then newdata = newdata - 2^16 end newdata = math.min(data,newdata) if newdata < 0 then newdata = newdata + 2^16 end writeram(address,newdata,2) end, amominuh = function(rd,rs1,rs2) local address = getreg(rs1) local data = readram(address,2) local newdata = getreg(rs2)%2^16 local bits = explodebits(data,16) signextend(bits,16,32) setreg(rd,implodebits(bits,32)) newdata = math.min(data,newdata) writeram(address,newdata,2) end, amocash = function(rd,rs1,rs2) local address = getreg(rs1) local data = readram(address,2) local compare = getreg(rd)%2^16 if data == compare then writeram(address,getreg(rs2)%2^16,2) end local bits = explodebits(data,16) signextend(bits,16,32) setreg(rd,implodebits(bits,32)) end, amoswapb = function(rd,rs1,rs2) local address = getreg(rs1) local newdata = getreg(rs2)%2^8 local olddata = explodebits(readram(address,1),8) signextend(olddata,8,32) setreg(rd,implodebits(olddata,32)) writeram(address,newdata,1) end, amoaddb = function(rd,rs1,rs2) local address = getreg(rs1) local data = readram(address,1) local newdata = getreg(rs2)%2^8 local oldbits = explodebits(data,8) signextend(oldbits,8,32) setreg(rd,implodebits(oldbits,32)) writeram(address,(data+newdata)%2^8,1) end, amoandb = function(rd,rs1,rs2) local address = getreg(rs1) local data = readram(address,1) local newdata = getreg(rs2)%2^8 local bits = explodebits(data,8) signextend(bits,8,32) setreg(rd,implodebits(bits,32)) local newbits = explodebits(newdata,8) for i=0,7 do bits[i] = bits[i] and newbits[i] end writeram(address,implodebits(bits,8),1) end, amoorb = function(rd,rs1,rs2) local address = getreg(rs1) local data = readram(address,1) local newdata = getreg(rs2)%2^8 local bits = explodebits(data,8) signextend(bits,8,32) setreg(rd,implodebits(bits,32)) local newbits = explodebits(newdata,8) for i=0,7 do bits[i] = bits[i] or newbits[i] end writeram(address,implodebits(bits,8),1) end, amoxorb = function(rd,rs1,rs2) local address = getreg(rs1) local data = readram(address,1) local newdata = getreg(rs2)%2^8 local bits = explodebits(data,8) signextend(bits,8,32) setreg(rd,implodebits(bits,32)) local newbits = explodebits(newdata,8) for i=0,7 do bits[i] = bits[i] ~= newbits[i] end writeram(address,implodebits(bits,8),1) end, amomaxb = function(rd,rs1,rs2) local address = getreg(rs1) local data = readram(address,1) local newdata = getreg(rs2)%2^8 local bits = explodebits(data,8) signextend(bits,8,32) setreg(rd,implodebits(bits,32)) if data > 2^7 then data = data - 2^8 end if newdata > 2^7 then newdata = newdata - 2^8 end newdata = math.max(data,newdata) if newdata < 0 then newdata = newdata + 2^8 end writeram(address,newdata,1) end, amomaxub = function(rd,rs1,rs2) local address = getreg(rs1) local data = readram(address,1) local newdata = getreg(rs2)%2^8 local bits = explodebits(data,8) signextend(bits,8,32) setreg(rd,implodebits(bits,32)) newdata = math.max(data,newdata) writeram(address,newdata,1) end, amominb = function(rd,rs1,rs2) local address = getreg(rs1) local data = readram(address,1) local newdata = getreg(rs2)%2^8 local bits = explodebits(data,8) signextend(bits,8,32) setreg(rd,implodebits(bits,32)) if data > 2^7 then data = data - 2^8 end if newdata > 2^7 then newdata = newdata - 2^8 end newdata = math.min(data,newdata) if newdata < 0 then newdata = newdata + 2^8 end writeram(address,newdata,1) end, amominub = function(rd,rs1,rs2) local address = getreg(rs1) local data = readram(address,1) local newdata = getreg(rs2)%2^8 local bits = explodebits(data,8) signextend(bits,8,32) setreg(rd,implodebits(bits,8)) newdata = math.min(data,newdata) writeram(address,newdata,1) end, amocasb = function(rd,rs1,rs2) local address = getreg(rs1) local data = readram(address,1) local compare = getreg(rd)%2^8 if data == compare then writeram(address,getreg(rs2)%2^8,1) end local bits = explodebits(data,8) signextend(bits,8,32) setreg(rd,implodebits(bits,32)) end, pack = function(rd,rs1,rs2) local lowhalf = getreg(rs1)%2^16 local highhalf = (getreg(rs2)%2^16)*2^16 setreg(rd,lowhalf+highhalf) end, packh = function(rd,rs1,rs2) local lowhalf = getreg(rs1)%2^8 local highhalf = (getreg(rs2)%2^8)*2^8 setreg(rd,lowhalf+highhalf) end, brev8 = function(rd,rs1,imm) local bits = explodebits(getreg(rs1),32) local newbits = {} for byte=0,3 do for bit=0,7 do local source = byte*8+bit local destination = byte*8+(7-bit) newbits[destination] = bits[source] end end setreg(rd,implodebits(newbits,32)) end, zip = function(rd,rs1,imm) local bits = explodebits(getreg(rs1),32) local newbits = {} for i=0,15 do newbits[i*2] = bits[i] newbits[i*2+1] = bits[i+16] end setreg(rd,implodebits(newbits,32)) end, unzip = function(rd,rs1,imm) local bits = explodebits(getreg(rs1),32) local newbits = {} for i=0,15 do newbits[i] = bits[i*2] newbits[i+16] = bits[i*2+1] end setreg(rd,implodebits(newbits,32)) end, czeroeqz = function(rd,rs1,rs2) if getreg(rs2) == 0 then setreg(rd,0) else setreg(rd,getreg(rs1)) end end, czeronez = function(rd,rs1,rs2) if getreg(rs2) ~= 0 then setreg(rd,0) else setreg(rd,getreg(rs1)) end end, mopr = function(rd,rs1,imm) setreg(rd,0) end, moprr = function(rd,rs1,rs2,imm) setreg(rd,0) end, cmop = function(rd,imm) --Does nothing until/unless redefined by another extension end, wrsnto = function(rd,rs1) mem.rswaiting = "nto" mem.running = false digiline_send("monitordisp","Waiting for\nreservation set") return false,true end, wrssto = function(rd,rs1) mem.rswaiting = "sto" return false,true end, h3bextm = function(rd,rs1,rs2,size) local bits = explodebits(getreg(rs1),32) local shamt = getreg(rs2) local outbits = {} for i=0,size-1 do outbits[i] = bits[i+shamt] --May be nil if out of bounds but that's OK end setreg(rd,implodebits(outbits,32)) end, h3bextmi = function(rd,rs1,rs2,size) local bits = explodebits(getreg(rs1),32) local shamt = rs2 --Register number field actually contains an immediate here local outbits = {} for i=0,size-1 do outbits[i] = bits[i+shamt] --May be nil if out of bounds but that's OK end setreg(rd,implodebits(outbits,32)) end, mret = function() mem.registers.pc = readcsr(0x341) mem.mdt = false mem.mie = mem.mpie mem.mpie = true mem.elp = mem.mlpe and mem.mpelp mem.mpelp = false mem.mpp = 3 checkinterrupts() return true end, wfi = function() mem.interruptwaiting = true mem.running = false digiline_send("monitordisp","Waiting for\ninterrupt") interrupt(1,"checkinterrupt") return false,true end, } local function runinst(instruction) local bits = explodebits(instruction,32) local opcode = implodebits(bits,7) if opcode == 0x33 then --R-type --This is spectaularly inefficient and will probably need to be optimized later. --Really, the whole program is. --Focus is on making it work first... local f3bits = {[0] = bits[12],bits[13],bits[14]} local f3 = implodebits(f3bits,3) local f7bits = {[0] = bits[25],bits[26],bits[27],bits[28],bits[29],bits[30],bits[31]} local f7 = implodebits(f7bits,7) local rdbits = {[0] = bits[7],bits[8],bits[9],bits[10],bits[11]} local rd = implodebits(rdbits,5) local rs1bits = {[0] = bits[15],bits[16],bits[17],bits[18],bits[19]} local rs1 = implodebits(rs1bits,5) local rs2bits = {[0] = bits[20],bits[21],bits[22],bits[23],bits[24]} local rs2 = implodebits(rs2bits,5) if f3 == 0x0 and f7 == 0x0 then operations.add(rd,rs1,rs2) elseif f3 == 0x0 and f7 == 0x20 then operations.sub(rd,rs1,rs2) elseif f3 == 0x4 and f7 == 0x0 then operations.xor(rd,rs1,rs2) elseif f3 == 0x6 and f7 == 0x0 then operations.orr(rd,rs1,rs2) elseif f3 == 0x7 and f7 == 0x0 then operations.andr(rd,rs1,rs2) elseif f3 == 0x1 and f7 == 0x0 then operations.sll(rd,rs1,rs2) elseif f3 == 0x5 and f7 == 0x0 then operations.srl(rd,rs1,rs2) elseif f3 == 0x5 and f7 == 0x20 then operations.sra(rd,rs1,rs2) elseif f3 == 0x2 and f7 == 0x0 then operations.slt(rd,rs1,rs2) elseif f3 == 0x3 and f7 == 0x0 then operations.sltu(rd,rs1,rs2) elseif f3 == 0x0 and f7 == 0x1 and mem.isa.m then operations.mul(rd,rs1,rs2) elseif f3 == 0x1 and f7 == 0x1 and mem.isa.m then operations.mulh(rd,rs1,rs2) elseif f3 == 0x2 and f7 == 0x1 and mem.isa.m then operations.mulsu(rd,rs1,rs2) elseif f3 == 0x3 and f7 == 0x1 and mem.isa.m then operations.mulu(rd,rs1,rs2) elseif f3 == 0x4 and f7 == 0x1 and mem.isa.m then operations.div(rd,rs1,rs2) elseif f3 == 0x5 and f7 == 0x1 and mem.isa.m then operations.divu(rd,rs1,rs2) elseif f3 == 0x6 and f7 == 0x1 and mem.isa.m then operations.rem(rd,rs1,rs2) elseif f3 == 0x7 and f7 == 0x1 and mem.isa.m then operations.remu(rd,rs1,rs2) elseif f3 == 0x2 and f7 == 0x10 and mem.isa.b then operations.sh1add(rd,rs1,rs2) elseif f3 == 0x4 and f7 == 0x10 and mem.isa.b then operations.sh2add(rd,rs1,rs2) elseif f3 == 0x6 and f7 == 0x10 and mem.isa.b then operations.sh3add(rd,rs1,rs2) elseif f3 == 0x7 and f7 == 0x20 and mem.isa.b then operations.andn(rd,rs1,rs2) elseif f3 == 0x6 and f7 == 0x20 and mem.isa.b then operations.orn(rd,rs1,rs2) elseif f3 == 0x4 and f7 == 0x20 and mem.isa.b then operations.xnor(rd,rs1,rs2) elseif f3 == 0x6 and f7 == 0x5 then operations.max(rd,rs1,rs2) elseif f3 == 0x7 and f7 == 0x5 then operations.maxu(rd,rs1,rs2) elseif f3 == 0x4 and f7 == 0x5 then operations.min(rd,rs1,rs2) elseif f3 == 0x5 and f7 == 0x5 then operations.minu(rd,rs1,rs2) elseif f3 == 0x4 and f7 == 0x4 and rs2 == 0x0 and mem.isa.b then operations.zexth(rd,rs1) elseif f3 == 0x1 and f7 == 0x30 and mem.isa.b then operations.rol(rd,rs1,rs2) elseif f3 == 0x5 and f7 == 0x30 and mem.isa.b then operations.ror(rd,rs1,rs2) elseif f3 == 0x1 and f7 == 0x24 and mem.isa.b then operations.bclr(rd,rs1,rs2) elseif f3 == 0x5 and f7 == 0x24 and mem.isa.b then operations.bext(rd,rs1,rs2) elseif f3 == 0x1 and f7 == 0x34 and mem.isa.b then operations.binv(rd,rs1,rs2) elseif f3 == 0x1 and f7 == 0x14 and mem.isa.b then operations.bset(rd,rs1,rs2) elseif f3 == 0x2 and f7 == 0x14 and mem.isa.b then operations.xperm4(rd,rs1,rs2) elseif f3 == 0x4 and f7 == 0x14 and mem.isa.b then operations.xperm8(rd,rs1,rs2) elseif f3 == 0x4 and f7 == 0x4 then operations.pack(rd,rs1,rs2) elseif f3 == 0x7 and f7 == 0x4 then operations.packh(rd,rs1,rs2) elseif f3 == 0x5 and f7 == 0x7 then operations.czeroeqz(rd,rs1,rs2) elseif f3 == 0x7 and f7 == 0x7 then operations.czeronez(rd,rs1,rs2) else return trap(2,instruction) end elseif opcode == 0x13 or opcode == 0x3 or opcode == 0x67 or opcode == 0x73 then --I-type local f3bits = {[0] = bits[12],bits[13],bits[14]} local f3 = implodebits(f3bits,3) local rdbits = {[0] = bits[7],bits[8],bits[9],bits[10],bits[11]} local rd = implodebits(rdbits,5) local rs1bits = {[0] = bits[15],bits[16],bits[17],bits[18],bits[19]} local rs1 = implodebits(rs1bits,5) local immbits = {[0] = bits[20],bits[21],bits[22],bits[23],bits[24],bits[25],bits[26],bits[27],bits[28],bits[29],bits[30],bits[31]} local imm = implodebits(immbits,12) if opcode == 0x13 then if f3 == 0x0 then operations.addi(rd,rs1,imm) elseif f3 == 0x4 then operations.xori(rd,rs1,imm) elseif f3 == 0x6 then operations.ori(rd,rs1,imm) elseif f3 == 0x7 then operations.andi(rd,rs1,imm) elseif f3 == 0x1 and math.floor(imm/0x20) == 0x0 then operations.slli(rd,rs1,imm%0x20) elseif f3 == 0x5 and math.floor(imm/0x20) == 0x0 then operations.srli(rd,rs1,imm%0x20) elseif f3 == 0x5 and math.floor(imm/0x20) == 0x20 then operations.srai(rd,rs1,imm%0x20) elseif f3 == 0x2 then operations.slti(rd,rs1,imm) elseif f3 == 0x3 then operations.sltiu(rd,rs1,imm) elseif f3 == 0x1 and imm == 0x600 and mem.isa.b then operations.clz(rd,rs1) elseif f3 == 0x1 and imm == 0x601 and mem.isa.b then operations.ctz(rd,rs1) elseif f3 == 0x1 and imm == 0x602 and mem.isa.b then operations.cpop(rd,rs1) elseif f3 == 0x1 and imm == 0x604 and mem.isa.b then operations.sextb(rd,rs1) elseif f3 == 0x1 and imm == 0x605 and mem.isa.b then operations.sexth(rd,rs1) elseif f3 == 0x5 and math.floor(imm/2^5) == 0x30 then operations.rori(rd,rs1,imm) elseif f3 == 0x5 and imm == 0x287 and mem.isa.b then operations.orcb(rd,rs1) elseif f3 == 0x5 and imm == 0x698 and mem.isa.b then operations.rev8(rd,rs1) elseif f3 == 0x1 and math.floor(imm/2^5) == 0x24 then operations.bclri(rd,rs1,imm) elseif f3 == 0x5 and math.floor(imm/2^5) == 0x24 then operations.bexti(rd,rs1,imm) elseif f3 == 0x1 and math.floor(imm/2^5) == 0x34 then operations.binvi(rd,rs1,imm) elseif f3 == 0x1 and math.floor(imm/2^5) == 0x24 then operations.bseti(rd,rs1,imm) elseif f3 == 0x5 and imm == 0x687 then operations.brev8(rd,rs1,imm) elseif f3 == 0x1 and imm == 0x8f then operations.zip(rd,rs1,imm) elseif f3 == 0x5 and imm == 0x8f then operations.unzip(rd,rs1,imm) else return trap(2,instruction) end elseif opcode == 0x3 then if f3 == 0x0 then operations.lb(rd,rs1,imm) elseif f3 == 0x1 then operations.lh(rd,rs1,imm) elseif f3 == 0x2 then operations.lw(rd,rs1,imm) elseif f3 == 0x3 then operations.ld(rd,rs1,imm) elseif f3 == 0x4 then operations.lbu(rd,rs1,imm) elseif f3 == 0x5 then operations.lhu(rd,rs1,imm) else return trap(2,instruction) end elseif opcode == 0x67 then if f3 == 0x0 then return operations.jalr(rd,rs1,imm) else return trap(2,instruction) end elseif opcode == 0x73 then if f3 == 0x0 then if imm == 0x0 then operations.ecall() elseif imm == 0x1 then return operations.ebreak() elseif rs1 == 0 and rd == 0 and imm == 0x0d and mem.isa.a then return operations.wrsnto() elseif rs1 == 0 and rd == 0 and imm == 0x1d and mem.isa.a then return operations.wrssto() elseif imm == 0x302 and rd == 0 and rs1 == 0 then return operations.mret() elseif imm == 0x105 and rd == 0 and rs1 == 0 then return operations.wfi() else return trap(2,instruction) end elseif f3 == 0x1 then operations.csrrw(rd,rs1,imm) elseif f3 == 0x2 then operations.csrrs(rd,rs1,imm) elseif f3 == 0x3 then operations.csrrc(rd,rs1,imm) elseif f3 == 0x4 and bits[31] and bits[24] and bits[23] and bits[22] and not (bits[29] or bits[28] or bits[25]) then local immbits = implodebits({[0] = bits[20],bits[21],bits[26],bits[27],bits[30]},5) operations.mopr(rd,rs1,imm) elseif f3 == 0x4 and bits[31] and bits[25] and not (bits[28] or bits[29]) then local immbits = implodebits({[0] = bits[26],bits[27],bits[30]},3) operations.moprr(rd,rs1,rs2,imm) elseif f3 == 0x5 then operations.csrrwi(rd,rs1,imm) elseif f3 == 0x6 then operations.csrrsi(rd,rs1,imm) elseif f3 == 0x7 then operations.csrrci(rd,rs1,imm) else return trap(2,instruction) end end elseif opcode == 0x23 then --S-type local f3bits = {[0] = bits[12],bits[13],bits[14]} local f3 = implodebits(f3bits,3) local rs1bits = {[0] = bits[15],bits[16],bits[17],bits[18],bits[19]} local rs1 = implodebits(rs1bits,5) local rs2bits = {[0] = bits[20],bits[21],bits[22],bits[23],bits[24]} local rs2 = implodebits(rs2bits,5) local immbits = {[0] = bits[7],bits[8],bits[9],bits[10],bits[11],bits[25],bits[26],bits[27],bits[28],bits[29],bits[30],bits[31]} local imm = implodebits(immbits,12) if f3 == 0x0 then operations.sb(rs1,rs2,imm) elseif f3 == 0x1 then operations.sh(rs1,rs2,imm) elseif f3 == 0x2 then operations.sw(rs1,rs2,imm) elseif f3 == 0x3 then operations.sd(rs1,rs2,imm) else return trap(2,instruction) end elseif opcode == 0x63 then --B-type local f3bits = {[0] = bits[12],bits[13],bits[14]} local f3 = implodebits(f3bits,3) local rs1bits = {[0] = bits[15],bits[16],bits[17],bits[18],bits[19]} local rs1 = implodebits(rs1bits,5) local rs2bits = {[0] = bits[20],bits[21],bits[22],bits[23],bits[24]} local rs2 = implodebits(rs2bits,5) local immbits = {[0] = false,bits[8],bits[9],bits[10],bits[11],bits[25],bits[26],bits[27],bits[28],bits[29],bits[30],bits[7],bits[31]} local imm = implodebits(immbits,13) if f3 == 0x0 then return operations.beq(rs1,rs2,imm) elseif f3 == 0x1 then return operations.bne(rs1,rs2,imm) elseif f3 == 0x4 then return operations.blt(rs1,rs2,imm) elseif f3 == 0x5 then return operations.bge(rs1,rs2,imm) elseif f3 == 0x6 then return operations.bltu(rs1,rs2,imm) elseif f3 == 0x7 then return operations.bgeu(rs1,rs2,imm) else return trap(2,instruction) end elseif opcode == 0x37 or opcode == 0x17 then --U-type local rdbits = {[0] = bits[7],bits[8],bits[9],bits[10],bits[11]} local rd = implodebits(rdbits,5) --Immediate bits in this type actually all line up between the instruction and their actual value(!) --This means it's way easier to just mask out rd/opcode local imm = instruction - (instruction % 0x1000) if opcode == 0x37 then operations.lui(rd,imm) elseif opcode == 0x17 then operations.auipc(rd,imm) else return trap(2,instruction) end elseif opcode == 0x6f then --J-type local rdbits = {[0] = bits[7],bits[8],bits[9],bits[10],bits[11]} local rd = implodebits(rdbits,5) --Something had to give after how easy the U-type immediates were local immbits = {[0] = false,bits[21],bits[22],bits[23],bits[24],bits[25],bits[26],bits[27],bits[28],bits[29],bits[30],bits[20],bits[12],bits[13],bits[14],bits[15],bits[16],bits[17],bits[18],bits[19],bits[31]} local imm = implodebits(immbits,21) return operations.jal(rd,imm) elseif opcode == 0x2f and mem.isa.a then --Atomic memory operation local f5 = math.floor(instruction/2^27) local f3 = implodebits({[0] = bits[12],bits[13],bits[14]},3) local rd = implodebits({[0] = bits[7],bits[8],bits[9],bits[10],bits[11]},5) local rs1 = implodebits({[0] = bits[15],bits[16],bits[17],bits[18],bits[19]},5) local rs2 = implodebits({[0] = bits[20],bits[21],bits[22],bits[23],bits[24]},5) --Due to the way the memory model is implemented, aq and rl bits can be ignored if f3 == 2 then if f5 == 2 then --lr.w operations.lr(rd,rs1,rs2) elseif f5 == 3 then --sc.w operations.sc(rd,rs1,rs2) elseif f5 == 1 then --amoswap.w operations.amoswapw(rd,rs1,rs2) elseif f5 == 0 then --amoadd.w operations.amoaddw(rd,rs1,rs2) elseif f5 == 4 then --amoxor.w operations.amoxorw(rd,rs1,rs2) elseif f5 == 12 then --amoand.w operations.amoandw(rd,rs1,rs2) elseif f5 == 8 then --amoor.w operations.amoorw(rd,rs1,rs2) elseif f5 == 16 then --amomin.w operations.amominw(rd,rs1,rs2) elseif f5 == 20 then --amomax.w operations.amomaxw(rd,rs1,rs2) elseif f5 == 24 then --amominu.w operations.amominuw(rd,rs1,rs2) elseif f5 == 28 then --amomaxu.w operations.amomaxuw(rd,rs1,rs2) elseif f5 == 5 then --amocas.w operations.amocasw(rd,rs1,rs2) elseif f5 == 6 and rs2 == 0 then --lw.aq/lw.aqrl --The normal lw instruction implementation already meets these requirements operations.lw(rd,rs1,0) elseif f5 == 7 and rd == 0 then --sw.rl/sw.aqrl --The normal sw instruction implementation already meets these requirements operations.sw(rs1,rs2,0) else return trap(2,instruction) end elseif f3 == 3 then if f5 == 5 then --amocas.d operations.amocasd(rd,rs1,rs2) else return trap(2,instruction) end elseif f3 == 0 then if f5 == 1 then --amoswap.b operations.amoswapb(rd,rs1,rs2) elseif f5 == 0 then --amoadd.b operations.amoaddb(rd,rs1,rs2) elseif f5 == 4 then --amoxor.b operations.amoxorb(rd,rs1,rs2) elseif f5 == 12 then --amoamd.b operations.amoandb(rd,rs1,rs2) elseif f5 == 8 then --amoor.b operations.amoorb(rd,rs1,rs2) elseif f5 == 16 then --amomin.b operations.amominb(rd,rs1,rs2) elseif f5 == 20 then --amomax.b operations.amomaxb(rd,rs1,rs2) elseif f5 == 24 then --amominu.b operations.amominub(rd,rs1,rs2) elseif f5 == 28 then --amomaxu.b operations.amomaxub(rd,rs1,rs2) elseif f5 == 5 then --amocas.b operations.amocasb(rd,rs1,rs2) elseif f5 == 6 and rs2 == 0 then --lb.aq/lb.aqrl --The normal lb instruction implementation already meets these requirements operations.lb(rd,rs1,0) elseif f5 == 7 and rd == 0 then --sb.rl/sb.aqrl --The normal sb instruction implementation already meets these requirements operations.sb(rs1,rs2,0) else return trap(2,instruction) end elseif f3 == 1 then if f5 == 1 then --amoswap.h operations.amoswaph(rd,rs1,rs2) elseif f5 == 0 then --amoadd.h operations.amoaddh(rd,rs1,rs2) elseif f5 == 4 then --amoxor.h operations.amoxorh(rd,rs1,rs2) elseif f5 == 12 then --amoamd.h operations.amoandh(rd,rs1,rs2) elseif f5 == 8 then --amoor.h operations.amoorh(rd,rs1,rs2) elseif f5 == 16 then --amomin.h operations.amominh(rd,rs1,rs2) elseif f5 == 20 then --amomax.h operations.amomaxh(rd,rs1,rs2) elseif f5 == 24 then --amominu.h operations.amominuh(rd,rs1,rs2) elseif f5 == 28 then --amomaxu.h operations.amomaxuh(rd,rs1,rs2) elseif f5 == 5 then --amocas.h operations.amocash(rd,rs1,rs2) elseif f5 == 6 and rs2 == 0 then --lh.aq/lh.aqrl --The normal lh instruction implementation already meets these requirements operations.lh(rd,rs1,0) elseif f5 == 7 and rd == 0 then --sh.rl/sh.aqrl --The normal sh instruction implementation already meets these requirements operations.sh(rs1,rs2,0) else return trap(2,instruction) end end elseif opcode == 0x0f then --Memory is always consistent on this implementation, --so all fences can just turn into NOPs if instruction == 0x0100000f then --pause return false,true end elseif opcode == 0x0b and mem.isa.x then --Custom0 instruction (currently all R-type) local f3 = implodebits({[0] = bits[12],bits[13],bits[14]},3) local xh3bextmsize = implodebits({[0] = bits[26],bits[27],bits[28]},3)+1 local rd = implodebits({[0] = bits[7],bits[8],bits[9],bits[10],bits[11]},5) local rs1 = implodebits({[0] = bits[15],bits[16],bits[17],bits[18],bits[19]},5) local rs2 = implodebits({[0] = bits[20],bits[21],bits[22],bits[23],bits[24]},5) if f3 == 0 then operations.h3bextm(rd,rs1,rs2,xh3bextmsize) elseif f3 == 4 then operations.h3bextmi(rd,rs1,rs2,xh3bextmsize) else return trap(2,instruction) end else trap(2,instruction) end end local function runcinst(instruction) local bits = explodebits(instruction,16) local opcode = instruction%2^2 local f8 = math.floor(instruction/2^8) local f6 = math.floor(f8/2^2) local f4 = math.floor(f6/2^2) local f3 = math.floor(f4/2) if opcode == 2 and f3 == 2 then --c.lwsp (CI) local imm = implodebits({[0] = false,false,bits[4],bits[5],bits[6],bits[12],bits[2],bits[3]},8) local rd = implodebits({[0] = bits[7],bits[8],bits[9],bits[10],bits[11]},5) operations.lw(rd,2,imm) elseif opcode == 2 and f3 == 3 then --c.ldsp local imm = implodebits({[0] = false,false,false,bits[5],bits[6],bits[12],bits[2],bits[3],bits[4]},9) local rd = implodebits({[0] = bits[7],bits[8],bits[9],bits[10],bits[11]},5) operations.ld(rd,2,imm) elseif opcode == 2 and f3 == 6 then --c.swsp (CSS) local imm = implodebits({[0] = false,false,bits[9],bits[10],bits[11],bits[12],bits[7],bits[8]},8) local rs2 = implodebits({[0] = bits[2],bits[3],bits[4],bits[5],bits[6]},5) operations.sw(2,rs2,imm) elseif opcode == 2 and f3 == 7 then --c.sdsp local imm = implodebits({[0] = false,false,false,bits[10],bits[11],bits[12],bits[7],bits[8],bits[9]},9) local rs2 = implodebits({[0] = bits[2],bits[3],bits[4],bits[5],bits[6]},5) operations.sd(2,rs2,imm) elseif opcode == 0 and f3 == 2 then --c.lw (CL) local imm = implodebits({[0] = false,false,bits[6],bits[10],bits[11],bits[12],bits[5]},7) local rd = implodebits({[0] = bits[2],bits[3],bits[4]},3)+8 local rs1 = implodebits({[0] = bits[7],bits[8],bits[9]},3)+8 operations.lw(rd,rs1,imm) elseif opcode == 0 and f3 == 3 then --c.ld local imm = implodebits({[0] = false,false,false,bits[6],bits[10],bits[11],bits[12],bits[5]},8) local rd = implodebits({[0] = bits[2],bits[3],bits[4]},3)+8 local rs1 = implodebits({[0] = bits[7],bits[8],bits[9]},3)+8 operations.ld(rd,rs1,imm) elseif opcode == 0 and f3 == 6 then --c.sw (CS) local imm = implodebits({[0] = false,false,bits[6],bits[10],bits[11],bits[12],bits[5]},7) local rs2 = implodebits({[0] = bits[2],bits[3],bits[4]},3)+8 local rs1 = implodebits({[0] = bits[7],bits[8],bits[9]},3)+8 operations.sw(rs1,rs2,imm) elseif opcode == 0 and f3 == 7 then --c.sd local imm = implodebits({[0] = false,false,false,bits[6],bits[10],bits[11],bits[12],bits[5]},8) local rs2 = implodebits({[0] = bits[2],bits[3],bits[4]},3)+8 local rs1 = implodebits({[0] = bits[7],bits[8],bits[9]},3)+8 operations.sd(rs1,rs2,imm) elseif opcode == 1 and f3 == 5 then --c.j (CJ) local immbits = {[0] = false,bits[3],bits[4],bits[5],bits[11],bits[2],bits[7],bits[6],bits[9],bits[10],bits[8],bits[12]} signextend(immbits,12,21) local offset = implodebits(immbits,21) return operations.jal(0,offset,true) elseif opcode == 1 and f3 == 1 then --c.jal (CJ) local immbits = {[0] = false,bits[3],bits[4],bits[5],bits[11],bits[2],bits[7],bits[6],bits[9],bits[10],bits[8],bits[12]} signextend(immbits,12,21) local offset = implodebits(immbits,21) return operations.jal(1,offset,true) elseif opcode == 2 and f4 == 8 and math.floor(instruction/2^2)%2^5 == 0 then --c.jr (CR) local rs1 = implodebits({[0] = bits[7],bits[8],bits[9],bits[10],bits[11]},5) return operations.jalr(0,rs1,0,true) elseif opcode == 2 and f4 == 9 then local rd = implodebits({[0] = bits[7],bits[8],bits[9],bits[10],bits[11]},5) local rs1 = implodebits({[0] = bits[7],bits[8],bits[9],bits[10],bits[11]},5) local rs2 = implodebits({[0] = bits[2],bits[3],bits[4],bits[5],bits[6]},5) if rd == 0 and rs2 == 0 then --c.ebreak (CR) return operations.ebreak() elseif rs2 == 0 then --c.jalr (CR) return operations.jalr(1,rs1,0,true) else --c.add (CR) operations.add(rd,rd,rs2) end elseif opcode == 1 and f3 == 6 then --c.beqz (CB) local rs1 = implodebits({[0] = bits[7],bits[8],bits[9]},3)+8 local imm = implodebits({[0] = false,bits[3],bits[4],bits[10],bits[11],bits[2],bits[5],bits[6],bits[12]},9,true) return operations.beq(rs1,0,imm) elseif opcode == 1 and f3 == 7 then --c.bnez (CB) local rs1 = implodebits({[0] = bits[7],bits[8],bits[9]},3)+8 local imm = implodebits({[0] = false,bits[3],bits[4],bits[10],bits[11],bits[2],bits[5],bits[6],bits[12]},9,true) if imm >= 2^8 then imm = imm - 2^9 end return operations.bne(rs1,0,imm) elseif opcode == 1 and f3 == 2 then local imm = implodebits({[0] = bits[2],bits[3],bits[4],bits[5],bits[6],bits[12]},6,true) local rd = implodebits({[0] = bits[7],bits[8],bits[9],bits[10],bits[11]},5) if imm >= 2^5 then imm = imm - 2^6 end --c.li (CI) operations.addi(rd,0,imm) elseif opcode == 1 and f3 == 3 then local immbits = {[0] = bits[2],bits[3],bits[4],bits[5],bits[6],bits[12]} local imm = implodebits(immbits,6) local rd = implodebits({[0] = bits[7],bits[8],bits[9],bits[10],bits[11]},5) if rd == 2 then --c.addi16sp (CI) --For whatever reason this has a completely different immediate format than c.lui imm = implodebits({[0] = false,false,false,false,bits[6],bits[2],bits[5],bits[3],bits[4],bits[12]},10,true) operations.addi(2,2,imm) elseif imm ~= 0 then --c.lui (CI) signextend(immbits,6,20) local imm = implodebits(immbits,20) imm = imm*2^12 operations.lui(rd,imm) elseif rd%2 == 1 and rd <= 15 then --c.mop operations.cmop(rd,imm) else return trap(2,instruction%2^16) end elseif opcode == 1 and f3 == 0 then --c.addi (CI) local imm = implodebits({[0] = bits[2],bits[3],bits[4],bits[5],bits[6],bits[12]},6,true) local rd = implodebits({[0] = bits[7],bits[8],bits[9],bits[10],bits[11]},5) operations.addi(rd,rd,imm) elseif opcode == 0 and f3 == 0 and instruction ~= 0 then --c.addi4spn (CIW) local imm = implodebits({[0] = false,false,bits[6],bits[5],bits[11],bits[12],bits[7],bits[8],bits[9],bits[10]},10) local rd = (math.floor(instruction/2^2)%2^3)+8 operations.addi(rd,2,imm) elseif opcode == 2 and f3 == 0 then --c.slli (CI) local imm = implodebits({[0] = bits[2],bits[3],bits[4],bits[5],bits[6],bits[12]},6) local rd = implodebits({[0] = bits[7],bits[8],bits[9],bits[10],bits[11]},5) operations.slli(rd,rd,imm) elseif opcode == 1 and f6 == 0x20 then --c.srli (CB) local rd = implodebits({[0] = bits[7],bits[8],bits[9]},3)+8 local imm = implodebits({[0] = bits[2],bits[3],bits[4],bits[5],bits[6],bits[12]},6) operations.srli(rd,rd,imm) elseif opcode == 1 and f6 == 0x21 then --c.srai (CB) local rd = implodebits({[0] = bits[7],bits[8],bits[9]},3)+8 local imm = implodebits({[0] = bits[2],bits[3],bits[4],bits[5],bits[6],bits[12]},6) operations.srai(rd,rd,imm) elseif opcode == 1 and (f6 == 0x22 or f6 == 0x26) then --c.andi (CB) local rd = implodebits({[0] = bits[7],bits[8],bits[9]},3)+8 local immbits = {[0] = bits[2],bits[3],bits[4],bits[5],bits[6],bits[12]} signextend(immbits,6,12) local imm = implodebits(immbits,12) operations.andi(rd,rd,imm) elseif opcode == 2 and f4 == 8 then --c.mv (CR) local rd = implodebits({[0] = bits[7],bits[8],bits[9],bits[10],bits[11]},5) local rs2 = implodebits({[0] = bits[2],bits[3],bits[4],bits[5],bits[6]},5) operations.add(rd,0,rs2) elseif opcode == 1 and f6 == 0x23 and bits[5] and bits[6] then --c.and (CS) local rs2 = implodebits({[0] = bits[2],bits[3],bits[4]},3)+8 local rd = implodebits({[0] = bits[7],bits[8],bits[9]},3)+8 operations.andr(rd,rd,rs2) elseif opcode == 1 and f6 == 0x23 and bits[6] and not bits[5] then --c.or (CS) local rs2 = implodebits({[0] = bits[2],bits[3],bits[4]},3)+8 local rd = implodebits({[0] = bits[7],bits[8],bits[9]},3)+8 operations.orr(rd,rd,rs2) elseif opcode == 1 and f6 == 0x23 and bits[5] and not bits[6] then --c.xor (CS) local rs2 = implodebits({[0] = bits[2],bits[3],bits[4]},3)+8 local rd = implodebits({[0] = bits[7],bits[8],bits[9]},3)+8 operations.xor(rd,rd,rs2) elseif opcode == 1 and f6 == 0x23 and not (bits[5] or bits[6]) then --c.sub (CS) local rs2 = implodebits({[0] = bits[2],bits[3],bits[4]},3)+8 local rd = implodebits({[0] = bits[7],bits[8],bits[9]},3)+8 operations.sub(rd,rd,rs2) elseif opcode == 0 and f6 == 0x20 then --c.lbu local rd = implodebits({[0] = bits[2],bits[3],bits[4]},3)+8 local rs1 = implodebits({[0] = bits[7],bits[8],bits[9]},3)+8 local imm = implodebits({[0] = bits[6],bits[5]},2) operations.lbu(rd,rs1,imm) elseif opcode == 0 and f6 == 0x21 and not bits[6] then --c.lhu local rd = implodebits({[0] = bits[2],bits[3],bits[4]},3)+8 local rs1 = implodebits({[0] = bits[7],bits[8],bits[9]},3)+8 local imm = implodebits({[0] = bits[5],bits[6]},2) operations.lhu(rd,rs1,imm) elseif opcode == 0 and f6 == 0x21 and bits[6] then --c.lh local rd = implodebits({[0] = bits[2],bits[3],bits[4]},3)+8 local rs1 = implodebits({[0] = bits[7],bits[8],bits[9]},3)+8 local imm = implodebits({[0] = false,bits[5]},2) operations.lh(rd,rs1,imm) elseif opcode == 0 and f6 == 0x22 then --c.sb local rs2 = implodebits({[0] = bits[2],bits[3],bits[4]},3)+8 local rs1 = implodebits({[0] = bits[7],bits[8],bits[9]},3)+8 local imm = implodebits({[0] = bits[6],bits[5]},2) operations.sb(rs1,rs2,imm) elseif opcode == 0 and f6 == 0x23 and not bits[6] then --c.sh local rs2 = implodebits({[0] = bits[2],bits[3],bits[4]},3)+8 local rs1 = implodebits({[0] = bits[7],bits[8],bits[9]},3)+8 local imm = implodebits({[0] = false,bits[5]},2) operations.sh(rs1,rs2,imm) elseif opcode == 1 and f6 == 0x27 and bits[5] and bits[6] and not (bits[2] or bits[3] or bits[4]) then --c.zext.b local rd = implodebits({[0] = bits[7],bits[8],bits[9]},3)+8 operations.andi(rd,rd,0xff) elseif opcode == 1 and f6 == 0x27 and bits[5] and bits[6] and bits[2] and mem.isa.b and not (bits[3] or bits[4]) then --c.sext.b local rd = implodebits({[0] = bits[7],bits[8],bits[9]},3)+8 operations.sextb(rd,rd) elseif opcode == 1 and f6 == 0x27 and bits[5] and bits[6] and bits[3] and mem.isa.b and not (bits[2] or bits[4]) then --c.zext.h local rd = implodebits({[0] = bits[7],bits[8],bits[9]},3)+8 operations.zexth(rd,rd) elseif opcode == 1 and f6 == 0x27 and bits[5] and bits[6] and bits[2] and bits[3] and mem.isa.b and not bits[4] then --c.sext.h local rd = implodebits({[0] = bits[7],bits[8],bits[9]},3)+8 operations.sexth(rd,rd) elseif opcode == 1 and f6 == 0x27 and bits[5] and bits[6] and bits[2] and bits[4] and not bits[3] then --c.not local rd = implodebits({[0] = bits[7],bits[8],bits[9]},3)+8 operations.xori(rd,rd,0xffffffff) elseif opcode == 1 and f6 == 0x27 and bits[6] and mem.isa.m and not bits[5] then --c.mul local rd = implodebits({[0] = bits[7],bits[8],bits[9]},3)+8 local rs2 = implodebits({[0] = bits[2],bits[3],bits[4]},3)+8 operations.mul(rd,rd,rs2) elseif opcode == 2 and f8 == 0xb8 then --cm.push --This one decodes into possibly a whole bunch of instructions local rlist = cmpushpopreglists[implodebits({[0] = bits[4],bits[5],bits[6],bits[7]},4)] if not rlist then return end stack = implodebits({[0] = bits[2],bits[3]},2) * 16 + rlist.stack for i,reg in ipairs(rlist.registers) do operations.sw(2,reg,-4*i) end operations.addi(2,2,-1 * stack) elseif opcode == 2 and f8 == 0xba then --cm.pop --This one decodes into possibly a whole bunch of instructions local rlist = cmpushpopreglists[implodebits({[0] = bits[4],bits[5],bits[6],bits[7]},4)] if not rlist then return end stack = implodebits({[0] = bits[2],bits[3]},2) * 16 + rlist.stack operations.addi(2,2,stack) for i,reg in ipairs(rlist.registers) do operations.lw(reg,2,-4*i) end elseif opcode == 2 and f8 == 0xbc then --cm.popretz --This one decodes into a whole bunch of instructions local rlist = cmpushpopreglists[implodebits({[0] = bits[4],bits[5],bits[6],bits[7]},4)] if not rlist then return end local stack = implodebits({[0] = bits[2],bits[3]},2) * 16 + rlist.stack operations.addi(2,2,stack) for i,reg in ipairs(rlist.registers) do operations.lw(reg,2,-4*i) end operations.addi(10,0,0) return operations.jalr(0,1,0) --more like CISC-V amirite? elseif opcode == 2 and f8 == 0xbe then --cm.popret --This one decodes into a whole bunch of instructions local rlist = cmpushpopreglists[implodebits({[0] = bits[4],bits[5],bits[6],bits[7]},4)] if not rlist then return end local stack = implodebits({[0] = bits[2],bits[3]},2) * 16 + rlist.stack operations.addi(2,2,stack) for i,reg in ipairs(rlist.registers) do operations.lw(reg,2,-4*i) end return operations.jalr(0,1,0) elseif opcode == 2 and f6 == 0x2b and bits[5] and not bits[6] then --cm.mvsa01 --This one decodes into multiple instructions local sreg = {[0] = 8,9,18,19,20,21,22,23} local r1 = sreg[implodebits({[0] = bits[7],bits[8],bits[9]},3)] local r2 = sreg[implodebits({[0] = bits[2],bits[3],bits[4]},3)] operations.addi(r1,10,0) operations.addi(r2,11,0) elseif opcode == 2 and f6 == 0x2b and bits[5] and bits[6] then --cm.mva01s --This one decodes into multiple instructions local sreg = {[0] = 8,9,18,19,20,21,22,23} local r1 = sreg[implodebits({[0] = bits[7],bits[8],bits[9]},3)] local r2 = sreg[implodebits({[0] = bits[2],bits[3],bits[4]},3)] operations.addi(10,r1,0) operations.addi(11,r2,0) elseif opcode == 2 and f6 == 0x28 then local index = implodebits({[0] = bits[2],bits[3],bits[4],bits[5],bits[6],bits[7],bits[8],bits[9]},8) if index < 32 then --cm.jt local address = readcsr(0x17) --jvt address = address + 4*index local target = readram(address,4) mem.registers.pc = target return true else --cm.jalt local address = readcsr(0x17) --jvt address = address + 4*index local target = readram(address,4) setreg(1,mem.registers.pc+2) mem.registers.pc = target return true end else return trap(2,instruction%2^16) end end local function checklpe(instruction) if not mem.mlpe then return false elseif instruction%4 == 3 then --Uncompressed local opcode = instruction%0x100 local bits = explodebits(instruction,32) if opcode == 0x67 and not (bits[12] or bits[13] or bits[14]) then --jalr local rs1 = math.floor(instruction/2^15)%2^5 return (rs1 ~= 1 and rs1 ~= 5 and rs1 ~= 7) end else --Compressed local f4 = math.floor(instruction/2^12) if instruction %4 == 2 and (f4 == 8 or f4 == 9) then --c.jr or c.jalr local rs1 = math.floor(instruction/2^7)%2^5 return (rs1 ~= 1 and rs1 ~= 5 and rs1 ~= 7) end end return false end local function run(limit) --0xc00 = CYCLE --0xc80 = CYCLEH mem.csr[0xc00] = mem.csr[0xc00] + 1 if mem.csr[0xc00] >= 2^32 then mem.csr[0xc80] = mem.csr[0xc80] + math.floor(mem.csr[0xc00]/2^32) mem.csr[0xc00] = mem.csr[0xc00]%2^32 end --0xc01 = TIME local timediff = os.time()-mem.starttime mem.csr[0xc01] = timediff%2^32 local first = true repeat fault = nil if mem.registers.pc == mem.breakpoint and not first then digiline_send("monitordisp","Hit breakpoint") mem.running = false break end if first then checkinterrupts() end first = false local instruction = readram(mem.registers.pc,4,true) if mem.lpe and mem.mlpe then if instruction%2^12 == 0x017 and mem.registers.pc%4 == 0 and (math.floor(instruction/2^12) == mem.lplabel or math.floor(instruction/2^12) == 0) then mem.lpe = false else trap(18,2) end end mem.lpe = mem.mlpe and checklpe(instruction) if mem.lpe then mem.lplabel = math.floor(getreg(7)/2^12) end if instruction%4 == 3 or not mem.isa.c then if mem.registers.pc%4 ~= 0 and not mem.isa.c then local _,stop = trap(0) if stop then break end end local jumped,stop = runinst(instruction) if fault then jumped,stop = trap(fault,fval) end if not jumped then mem.registers.pc = (mem.registers.pc + 4) % 2^32 end if stop then break end else local jumped,stop = runcinst(instruction%2^16) if fault then jumped,stop = trap(fault,fval) end if not jumped then mem.registers.pc = (mem.registers.pc + 2) % 2^32 end if stop then break end end --0xc02 = INSTRET --0xc82 = INSTRETH mem.csr[0xc02] = mem.csr[0xc02] + 1 if mem.csr[0xc02] >= 2^32 then mem.csr[0xc82] = mem.csr[0xc82] + math.floor(mem.csr[0xc02]/2^32) mem.csr[0xc02] = mem.csr[0xc02]%2^32 end limit = limit - 1 until (not mem.running) or limit <= 0 end local regaliases = { zero = 0, ra = 1, sp = 2, gp = 3, tp = 4, t0 = 5, t1 = 6, t2 = 7, fp = 8, 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, } local csraliases = { cycle = 0xc00, time = 0xc01, instret = 0xc02, cycleh = 0xc80, timeh = 0xc81, instreth = 0xc82, mvendorid = 0xf11, marchid = 0xf12, mimpid = 0xf13, mhartid = 0xf14, mstatus = 0x300, misa = 0x301, medeleg = 0x302, mideleg = 0x303, mie = 0x304, mtvec = 0x305, mcounteren = 0x306, mstatush = 0x310, medelegh = 0x312, mscratch = 0x340, mepc = 0x341, mcause = 0x342, mtval = 0x343, mip = 0x344, mtinst = 0x34a, mtval2 = 0x34b, mcycle = 0xb00, minstret = 0xb02, mcycleh = 0xb80, minstreth = 0xb82, mseccfg = 0x747, } if event.type == "program" or event.iid == "reset" then mem.ram = {} for i=0,(RAM_SIZE/256)-1 do mem.ram[i] = string.rep(string.char(0),256) end mem.registers = {} for i=0,31 do mem.registers[i] = 0 end mem.registers.pc = 0 mem.running = false mem.inputwaiting = false mem.rswaiting = false mem.stdout = {} mem.starttime = os.time() mem.csr = { [0x017] = 0, --jvt [0xf11] = 0, --mvendorid [0xf12] = 53, --marchid (53 is RVController's officially assigned value, see https://github.com/riscv/riscv-isa-manual/blob/main/marchid.md ) [0xf13] = 0x6f435652, --mimpid ("RVCo") [0xf14] = 0, --mhartid [0x300] = 0, --mstatus [0x301] = 0x40801107, --misa (RV32IMACBX) [0x304] = 0, --mie [0x305] = 0, --mtvec [0x340] = 0, --mscratch [0x341] = 0, --mepc [0x342] = 0, --mcause [0x343] = 0, --mtval [0x344] = 0, --mip [0x34b] = 0, --mtval2 [0x800] = 0, --Clock Controls [0x801] = 0, --MMIO Base Address [0xc00] = 0, --CYCLE [0xc01] = 0, --TIME [0xc02] = 0, --INSTRET [0xc80] = 0, --CYCLEH [0xc81] = 0, --TIMEH [0xc82] = 0, --INSTRETH } for i=1,6 do mem.stdout[i] = "" end local mdisp = event.type == "program" and "\nReset: Cold" or "\nReset: Warm" digiline_send("monitordisp",mdisp.."\n\nRVController Monitor\n\nType 'help' for help\nReady\n") digiline_send("stdout","\n\n\n\n\n\n\n") mem.inputbuf = "" mem.digilinesqueue = {} mem.breakpoint = -1 mem.hexloading = false mem.reservationset = nil mem.mbe = false mem.isa = { a = true, b = true, c = true, i = true, m = true, x = true, } mem.meseconsdir = {} mem.meseconsdata = {} port = {} mem.trapbase = 0 mem.trapmode = 0 mem.mtvecwritten = false mem.mie = false mem.mpie = true mem.mpp = 3 mem.mdt = true mem.mtimecmplow = 0 mem.mtimecmphigh = 0 mem.mtimehigh = 0 mem.interruptwaiting = false mem.mlpe = false mem.elp = false mem.mpelp = false elseif event.type == "on" or event.type == "off" then local pname = string.lower(event.pin.name) local pstate = pin[pname] if not mem.meseconsdir[pname] then --Pin is input mem.meseconsdata[pname] = pstate if readcsr(0x801) ~= 0 then --MMIO is enabled writeram(readcsr(0x801)+1,0,1,true) --Invalidate any existing reservation set end local mipbits = explodebits(mem.csr[0x344],32) mipbits[16] = true mem.csr[0x344] = implodebits(mipbits,32) local miebits = explodebits(mem.csr[0x304],32) if miebits[16] and mem.interruptwaiting then mem.interruptwaiting = false mem.running = true digiline_send("monitordisp","CPU started") interrupt(1/CLOCK_SPEED,"tick") end end elseif event.channel == "reset" then mem.running = false digiline_send("monitordisp","\n\n\n\n\n\n\n") digiline_send("stdout","\n\n\n\n\n\n\n") interrupt(0.5,"reset") elseif event.channel == "monitorkb" then digiline_send("monitordisp","> "..event.msg) local function validateandclamp(value,min,max,base) base = base or 16 value = tonumber(value or "",base) if (not value) or value < min or value > max or value ~= math.floor(value) then return end return value end local argv = {} while true do local spacepos = string.find(event.msg," ",nil,true) or string.len(event.msg)+1 table.insert(argv,string.sub(event.msg,1,spacepos-1)) event.msg = string.sub(event.msg,spacepos+1,-1) if event.msg == "" then break end end local argc = #argv if argv[1] == "peek" then local address = validateandclamp(argv[2],0,0xffffffff) if not address then digiline_send("monitordisp","Bad address") return end local data = readram(address,1) digiline_send("monitordisp",string.format("%08X:%02X",address,data)) elseif argv[1] == "peekw" then local address = validateandclamp(argv[2],0,0xffffffff) if not address then digiline_send("monitordisp","Bad address") return end local data = readram(address,4) digiline_send("monitordisp",string.format("%08X:%08X",address,data)) elseif argv[1] == "poke" then local address = validateandclamp(argv[2],0,0xffffffff) if not address then digiline_send("monitordisp","Bad address") return end local data = validateandclamp(argv[3],0,0xff) if not data then digiline_send("monitordisp","Bad data") return end writeram(address,data,1) digiline_send("monitordisp",string.format("%08X:%02X",address,data)) elseif argv[1] == "pokew" then local address = validateandclamp(argv[2],0,0xffffffff) if not address then digiline_send("monitordisp","Bad address") return end local data = validateandclamp(argv[3],0,0xffffffff) if not data then digiline_send("monitordisp","Bad data") return end writeram(address,data,4) digiline_send("monitordisp",string.format("%08X:%08X",address,data)) elseif argv[1] == "setreg" then argv[2] = regaliases[argv[2]] or argv[2] local reg = validateandclamp(argv[2],0,0x1f,10) if not reg then digiline_send("monitordisp","Bad register") return end local data = validateandclamp(argv[3],0,0xffffffff) if not data then digiline_send("monitordisp","Bad data") return end setreg(reg,data) digiline_send("monitordisp",string.format("x%02d:%08X",reg,data)) elseif argv[1] == "getreg" then argv[2] = regaliases[argv[2]] or argv[2] local reg = validateandclamp(argv[2],0,0x1f,10) if not reg then digiline_send("monitordisp","Bad register") return end local data = getreg(reg) digiline_send("monitordisp",string.format("x%02d:%08X",reg,data)) elseif argv[1] == "getpc" then digiline_send("monitordisp",string.format("PC:%08X",mem.registers.pc)) elseif argv[1] == "setpc" then local address = validateandclamp(argv[2],0,0xffffffff) if not address then digiline_send("monitordisp","Bad address") return end mem.registers.pc = address digiline_send("monitordisp",string.format("PC:%08X",address)) elseif argv[1] == "reset" then mem.running = false digiline_send("monitordisp","\n\n\n\n\n\n\n") digiline_send("stdout","\n\n\n\n\n\n\n") interrupt(0.5,"reset") elseif argv[1] == "step" then if mem.inputwaiting then digiline_send("monitordisp","Unable to step,\nwaiting for input") return elseif mem.rswaiting then digiline_send("monitordisp","Unable to step,\nwaiting for\nreservation set") return elseif mem.running then digiline_send("monitordisp","Already running") return end run(1) digiline_send("monitordisp",string.format("PC:%08X",mem.registers.pc)) elseif argv[1] == "run" then mem.starttime = os.time() mem.running = true digiline_send("monitordisp","CPU started\n\n") run(50) interrupt(0.1,"tick") elseif argv[1] == "stop" then mem.running = false mem.inputwaiting = false mem.rswaiting = false digiline_send("monitordisp","Stopped by user") elseif argv[1] == "h" then --Easter egg digiline_send("monitordisp","h") elseif argv[1] == "setbreak" then local address = validateandclamp(argv[2],0,RAM_SIZE-1) if not address then digiline_send("monitordisp","Bad address") return end mem.breakpoint = address elseif argv[1] == "clearbreak" then mem.breakpoint = -1 elseif argv[1] == "rdcsr" then local address = csraliases[argv[2]] or validateandclamp(argv[2],0,0xfff) if not address then digiline_send("monitordisp","Bad CSR address") return end digiline_send("monitordisp",string.format("%03X: %08X",address,readcsr(address))) elseif argv[1] == "wrcsr" then local address = csraliases[argv[2]] or validateandclamp(argv[2],0,0xfff) if not address then digiline_send("monitordisp","Bad CSR address") return end local data = validateandclamp(argv[3],0,0xffffffff) if not data then digiline_send("monitordisp","Bad data") return end writecsr(address,data) --Read it back in case the target field was RO or WARL digiline_send("monitordisp",string.format("%03X: %08X",address,readcsr(address))) elseif argv[1] == "endian" then if argc == 1 then digiline_send("monitordisp",mem.mbe and "Big-endian" or "Little-endian") elseif argv[2] == "b" or argv[2] == "big" then local bits = explodebits(readcsr(0x310),32) --mstatush bits[5] = true --MBE writecsr(0x310,implodebits(bits,32)) digiline_send("monitordisp","Big-endian") elseif argv[2] == "l" or argv[2] == "little" then local bits = explodebits(readcsr(0x310),32) --mstatush bits[5] = false --MBE writecsr(0x310,implodebits(bits,32)) digiline_send("monitordisp","Little-endian") else digiline_send("monitordisp","Invalid argument") end elseif argv[1] == "help" then if argc == 1 or argv[2] == "1" then digiline_send("monitordisp","Use: help ") digiline_send("monitordisp","Commands:\npoke pokew peek\npeekw setreg getreg\ngetpc setpc step run\n'help 2' for page 2") elseif argv[2] == "poke" then digiline_send("monitordisp","poke \nWrites the specified\nbyte (8 bits) to\nmemory at the\nspecified address") elseif argv[2] == "pokew" then digiline_send("monitordisp","pokew \nWrites the specified\nword (32 bits) to\nmemory at the\nspecified address") elseif argv[2] == "peek" then digiline_send("monitordisp","peek
\nReads the byte (8\nbits) value from\nmemory at the\nspecified address") elseif argv[2] == "peekw" then digiline_send("monitordisp","peekw
\nReads the word (32\nbits) value from\nmemory at the\nspecified address") elseif argv[2] == "setreg" then digiline_send("monitordisp","setreg \nWrites the specified\nword (32 bits) data\nto the specified\nregister") elseif argv[2] == "getreg" then digiline_send("monitordisp","getreg \nReads the word (32\nbits) data from the\nspecified register") elseif argv[2] == "getpc" then digiline_send("monitordisp","getpc\nReads the current\nvalue of the program\ncounter") elseif argv[2] == "setpc" then digiline_send("monitordisp","setpc \nWrites the specified\nvalue into the\nprogram counter") elseif argv[2] == "step" then digiline_send("monitordisp","step\nAllows the CPU to\nrun one instruction,\nthen halts") elseif argv[2] == "run" then digiline_send("monitordisp","run\nAllows the CPU to\nrun indefinitely") elseif argv[2] == "stop" then digiline_send("monitordisp","stop\nHalts the CPU") elseif argv[2] == "reset" then digiline_send("monitordisp","reset\nStops the CPU and\nclears RAM and all\nregisters") elseif argv[2] == "help" then digiline_send("monitordisp","help [command]\nShows information\nabout the specified\ncommand, or a list\nof commands if none\nis supplied") elseif argv[2] == "2" then digiline_send("monitordisp","Page 2\nreset stop help\nsetbreak clearbreak\nrdcsr wrcsr endian") elseif argv[2] == "h" then digiline_send("monitordisp","h\nhhhhhhhhhh") elseif argv[2] == "setbreak" then digiline_send("monitordisp","setbreak
\nSets a breakpoint\nat the specified\naddress") elseif argv[2] == "clearbreak" then digiline_send("monitordisp","clearbreak\nClears any set\nbreakpoint") elseif argv[2] == "rdcsr" then digiline_send("monitordisp","rdcsr
\nReads the value of\nthe specified CSR") elseif argv[2] == "wrcsr" then digiline_send("monitordisp","wrcsr \nWrites the specifed\nvalue into the\nspecified CSR") elseif argv[2] == "endian" then digiline_send("monitordisp","endian [b | e]\nDisplays or changes\nthe current\nendianness") else digiline_send("monitordisp","No such command or\nno help available") end else digiline_send("monitordisp","Unknown command") end elseif event.channel == "stdin" then if mem.inputwaiting then --Blocking read in progress if mem.inputwaittype == "string" then event.msg = string.sub(event.msg,1,mem.inputmax-1)..string.char(0) for i=1,string.len(event.msg) do writeram(mem.inputaddr+i-1,string.byte(string.sub(event.msg,i,i)),1) end mem.inputwaiting = false mem.running = true digiline_send("monitordisp","CPU started") run(INSTRUCTIONS_PER_CLOCK) interrupt(1/CLOCK_SPEED,"tick") elseif mem.inputwaittype == "integer" and tonumber(event.msg) then event.msg = math.floor(event.msg) if event.msg >= 2^32 then event.msg = 2^32-1 end if event.msg < -1*(2^31) then event.msg = -1*(2^31) end if event.msg < 0 then event.msg = event.msg + 2^32 end setreg(10,event.msg) mem.inputwaiting = false mem.running = true digiline_send("monitordisp","CPU started") run(INSTRUCTIONS_PER_CLOCK) interrupt(1/CLOCK_SPEED,"tick") elseif mem.inputwaittype == "char" then event.msg = string.sub(event.msg,1,1) if event.msg == "" then event.msg = string.char(0) end setreg(10,string.byte(event.msg)) mem.inputwaiting = false mem.running = true digiline_send("monitordisp","CPU started") run(INSTRUCTIONS_PER_CLOCK) interrupt(1/CLOCK_SPEED,"tick") end else --No blocking read in progress, buffer the data for nonblocking reads later mem.inputbuf = string.sub(mem.inputbuf..event.msg,1,256) end elseif event.channel == "load" then if event.msg.done then mem.hexloading = false digiline_send("monitordisp","Load complete\n") if event.msg.autorun then mem.running = true digiline_send("monitordisp","CPU started\n") run(INSTRUCTIONS_PER_CLOCK) interrupt(1/CLOCK_SPEED,"tick") end else if not mem.hexloading then if mem.running then digiline_send("monitordisp","System halted\n") end mem.running = false mem.hexloading = true digiline_send("monitordisp","Loading Intel\nHEX data") digiline_send("stdout","\n\n\n\n\n\n\n") mem.registers.pc = 0 end local address = event.msg.address local data = event.msg.data local size = event.msg.size digiline_send("monitordisp",string.format("%08X\n",address)) for i=0,size-1 do local thisdata = tonumber(string.sub(data,i*2+1,i*2+2),16) local thisaddress = address + i writeram(thisaddress,thisdata,1) end end elseif event.type == "digiline" then --Unrecognized digilines signals get forwarded on to the program event.channel = string.sub(event.channel,1,256) event.msg = string.sub(tostring(event.msg),1,256) if #mem.digilinesqueue < 16 then table.insert(mem.digilinesqueue,{channel = event.channel, msg = event.msg}) end if mem.csr[0x800]%2 == 1 then interrupt(0,"tick") end local mipbits = explodebits(mem.csr[0x344],32) mipbits[17] = true mem.csr[0x344] = implodebits(mipbits,32) local miebits = explodebits(mem.csr[0x304],32) if miebits[17] and mem.interruptwaiting then mem.interruptwaiting = false mem.running = true digiline_send("monitordisp","CPU started") interrupt(1/CLOCK_SPEED,"tick") end elseif event.iid == "tick" and mem.running then run(INSTRUCTIONS_PER_CLOCK) if mem.csr[0x800]%2 == 1 then interrupt(1,"tick",true) else interrupt(1/CLOCK_SPEED,"tick") end elseif event.iid == "checkinterrupt" and mem.interruptwaiting then local restarted = checkinterrupts() if restarted then mem.interruptwaiting = false mem.running = true digiline_send("monitordisp","CPU started") interrupt(1/CLOCK_SPEED,"tick") else local miebits = explodebits(mem.csr[0x304],32) if not miebits[7] then return end -- No need to keep checking the timer if it's not even on interrupt(1,"checkinterrupt",mem.csr[0x800]%2 == 1) end end if stdoutdirty then local text = table.concat(mem.stdout,"\n") digiline_send("stdout",text.."\n") stdoutdirty = false end