--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
--[[
Emulated system specifications:
Single RISC-V core, RV32IMACBZicntr_Zicond_Zicsr_Zifencei_Zihintpause_Zilsd_Zimop_Zabha_Zacas_Zalasr_Zawrs_Zcb_Zclsd_Zcmop_Zcmp_Zcmt_Zbkb_Zbkx_Xh3bextm instruction set, switchable endianness (little-endian default)
65536 bytes of RAM (configurable in settings below) starting at base address 0
Intended to be compliant with the following specifications:
* RV32I Base Integer Instruction Set, Version 2.1
* "Zifencei" Extension for Instruction-Fetch Fence, Version 2.0
- Note: Implemented as a 'nop' instruction as memory is always consistent (see note on RVWMO below)
* "Zicsr" Extension for Control and Status Register (CSR) Instructions, Version 2.0
* "Zicond" Extension for Integer Conditional Operations, Version 1.0.0
* "Zicntr" Extension for Counters, Version 2.0
* "Zimop" Extension for May-Be-Operations, Version 1.0
* "M" Extension for Integer Multiplication and Division, Version 2.0
- Note: This implies the following:
- * Zmmul Extension, Version 1.0
* "A" Extension for Atomic Instructions, Version 2.1
- Note: This implies the following:
- * "Zalrsc" Extension for Load-Reserved/Store-Conditional Instructions
- * "Zaamo" Extension for Atomic Memory Operations
* RVWMO Memory Consistency Model, Version 2.0
- Note: The actual implemented memory model is significantly more strict (memory is always consistent, there is no cache)
* "Ztso" Extension for Total Store Ordering, Version 1.0
* "C" Extension for Compressed Instructions, Version 2.0
- Note: This implies the following:
- * "Zca" Extension for Code Size Reduction, Version 1.0.0
* Zcb: Extension for Code Size Reduction, Version 1.0.0
* "Zcmop" Compressed May-Be-Operations Extension, Version 1.0
* Zcmp: Extension for Code Size Reduction, Version 1.0.0
* Zcmt: Extension for Code Size Reduction, Version 1.0.0
* "B" Extension for Bit Manipulation, Version 1.0.0
- Note: This implies the following:
- * Zba: Extension for Address generation, Version 1.0.0
- * Zbb: Extension for Basic bit-manipulation, Version 1.0.0
- * Zbs: Extension for Single-bit instructions, Version 1.0.0
* "Zihintpause" Extension for Pause Hint, Version 2.0
* Zilsd, Zclsd: Extensions for Load/Store pair for RV32, Version 1.0
* "Zabha" Extension for Byte and Halfword Atomic Memory Operations, Version 1.0
* "Zacas" Extension for Atomic Compare-and-Swap (CAS) Instructions, Version 1.0.0
* "Zalasr" Atomic Load-Acquire and Store-Release Instructions, Version 1.0
* "Zawrs" Extension for Wait-on-Reservation-Set instructions, Version 1.01
* Zbkb: Extension for Bit-manipulation for Cryptography, Version 1.0.0
* Zbkx: Extension for Crossbar permutations, Version 1.0.0
* Xh3bextm: Hazard3 bit extract multiple
- Note: Defined in the Hazard3 reference manual, available at https://github.com/Wren6991/Hazard3/releases/download/v1.1/hazard3.pdf
* Machine-Level ISA, Version 1.13
* "Smdbltrp" Double Trap Extension, Version 1.0
See https://docs.riscv.org/reference/isa/unpriv/unpriv-index.html for full specifications.
Monitor commands:
peek
Reads and displays the byte (8 bits) value from RAM at the specified address
poke
Writes the specified byte (8 bits) value to RAM at the specified address
peekw
Reads and displays the word (32 bits) value from RAM starting at the specified address
pokew
Writes the specified word (32 bits) value to RAM starting at the specified address
getreg
Displays the current value of the specified register number (0-31)
setreg
Sets the specified register (0-31) to the specified value
getpc
Displays the current value of the program counter
setpc
Sets the program counter to the specified value
reset
Stops the CPU, resets all registers to zero, and clears all RAM
step
Allows the CPU to run for one instruction, then halts
run
Allows the CPU to run indefinitely
stop
Halts the CPU
setbreak
Sets a breakpoint on the specified address.
Note that the breakpoint triggers just *before* the instruction fetch,
as in the instruction with the breakpoint on it will not have executed yet.
There can only be one breakpoint at a time. If one is already set, the new one will replace it.
clearbreak
Clears any set breakpoint. Note that an ebreak instruction will still cause a break.
help [command]
Shows information on a monitor command, or a list of commands if none is specified
ecall operations:
NOTE: When in RV32E mode, replace a7 with a5. a5 can also be used in place of a7 in I mode if a7 is 0.
a7 = 0
Run the operation from the a5 register instead, unless a5 is also 0 in which case no operation occurs
a7 = 1
Prints the integer value from register a0
a7 = 4
Prints the null-terminated string from the address specified by register a0
a7 = 5
Reads an integer from the console and stores it into register a0
This will block until the user enters a valid number
a7 = 8
Reads a string from the console and stores it (with a null terminator) into the address pointed to by register a0
Will not read more than the length specified in register a1, anything more is discarded
This will block until the user types something
a7 = 10
Exits the program (halts the CPU)
a7 = 11
Prints the character stored in the register a0
a7 = 12
Reads one character from the console (any more characters on the line are discarded) and stores it into register a0
a7 = 128
Gets a random integer (between the values in registers a0 and a1) and stores it into register a0
a7 = 129
Sends a digilines string message:
* channel is specified by the null-terminated string at the address specified by register a0
* message is specified by the null-terminated string at the address specified by register a1
a7 = 130
Gets the number of characters available to read from the console input buffer
Result is stored in register a0
a7 = 131
Clears the console input buffer
a7 = 132
Reads one character from the console input buffer and stores it into register a0
This will not block - if no data is available to read, a NUL character (0) is returned
The input buffer can store up to 256 characters - if full, incoming characters are dropped
a7 = 133
Gets the number of messages in the digilines receive buffer
Result is stored in register a0
a7 = 134
Clears the digilines receive buffer
a7 = 135
Reads one message from the digilines receive buffer, returns channel and message as null-terminated strings
This will not block - if no data is available to read, zero-length strings will be returned
Arguments:
a0 - Address that the channel string will be written to
a1 - Size of the buffer that the channel string will be written into
a2 - Address that the message will be written to
a3 - Size of the buffer that the message will be written into
Custom CSRs:
0x800[0]: Lightweight mode, controls processor clock speed and behavior when mapblock is unloaded:
0: 10 Hz (adjustable in settings below), processor does not stop when mapblock is unloaded
1: 1 Hz, processor stops when mapblock is unloaded
Note that when set to 0, a digilines message being received will cause the processor to run for one cycle.
This is intended to be used to check if the digilines message needs action to be taken in response,
and the program can then turn lightweight mode off if so.
0x801: MMIO Base Address (see MMIO section below)
May be set to any address with no alignment restrictions.
0 to disable, all other values enable.
MMIO:
Base address is selected by CSR 0x801 above.
Base + 0: Mesecons I/O Direction
Format: [0000dcba] (WARL)
0 in the bit corresponding to an I/O pin selects input, 1 selects output.
Changing a pin from output to input while the output is active may cause unusual behavior due to Luacontroller limitations.
Base + 1: Mesecons I/O Data
Format: [0000dcba] (Input bits are RO, output bits are RW)
For inputs: Each bit contains the state of the corresponding input. Writes are ignored.
For outputs: Each bit contains the state of the corresponding output. Writing to these bits turns the output on/off.
]]
--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 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] + 1) then
fault = instfetch and 1 or 5
return instfetch and 0x13 or 0 --0x13 = addi x0,x0,0
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)
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)
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] + 1) then
fault = 7
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(data,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(data,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 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[10] = mem.mdt
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
return 0
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
return
elseif address == 0xf11 or address == 0xf12 or address == 0xf13 or address == 0xf14 or address == 0xf15 then
--Read-only machine information register
fault = 2
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.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]
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,[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
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
return
end
mem.csr[address] = data
end
local function trap(reason)
writecsr(0x342,reason) --mcause
writecsr(0x341,mem.registers.pc) --mepc
writecsr(0x343,0) --mtval
mem.mpie = mem.mie
mem.mie = false
if not mem.mdt then
mem.mdt = 1
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!")
digiline_send("monitordisp",traptypes[reason] or "Unknown reason")
digiline_send("monitordisp",string.format("PC: %08X",mem.registers.pc))
digiline_send("monitordisp","System halted")
mem.running = false
return true,true
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)
end,
csrrw = function(rd,rs1,imm)
setreg(rd,readcsr(imm))
writecsr(imm,getreg(rs1))
end,
csrrs = function(rd,rs1,imm)
setreg(rd,readcsr(imm))
if rs1 == 0 then 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))
end,
csrrc = function(rd,rs1,imm)
setreg(rd,readcsr(imm))
if rs1 == 0 then 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))
end,
csrrwi = function(rd,rs1,imm)
setreg(rd,readcsr(imm))
writecsr(imm,rs1)
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))
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))
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.mie = mem.mpie
mem.mpie = true
mem.mpp = 3
mem.mdt = false
return 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)
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)
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)
end
elseif opcode == 0x67 then
if f3 == 0x0 then
return operations.jalr(rd,rs1,imm)
else
return trap(2)
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()
else
return trap(2)
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)
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)
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)
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)
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)
end
elseif f3 == 3 then
if f5 == 5 then
--amocas.d
operations.amocasd(rd,rs1,rs2)
else
return trap(2)
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)
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)
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 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)
end
else
trap(2)
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)
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
trap(2)
end
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
--0xc81 = TIMEH
local timediff = os.time()-mem.starttime
mem.csr[0xc01] = timediff%2^32
mem.csr[0xc81] = math.floor(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
first = false
local instruction = readram(mem.registers.pc,4,true)
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)
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)
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,
}
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] = 0x40001107, --misa (RV32IMACB)
[0x305] = 0, --mtvec
[0x340] = 0, --mscratch
[0x341] = 0, --mepc
[0x342] = 0, --mcause
[0x343] = 0, --mtval
[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,
}
mem.meseconsdir = {}
mem.meseconsdata = {}
port = {}
mem.trapbase = 0
mem.trapmode = 0
mem.mie = false
mem.mpie = true
mem.mpp = 3
mem.mdt = true
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
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
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
end
if stdoutdirty then
local text = table.concat(mem.stdout,"\n")
digiline_send("stdout",text.."\n")
stdoutdirty = false
end