--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 <address>
Reads and displays the byte (8 bits) value from RAM at the specified address

poke <address> <data>
Writes the specified byte (8 bits) value to RAM at the specified address

peekw <address>
Reads and displays the word (32 bits) value from RAM starting at the specified address

pokew <address> <data>
Writes the specified word (32 bits) value to RAM starting at the specified address

getreg <register number>
Displays the current value of the specified register number (0-31)

setreg <register number> <value>
Sets the specified register (0-31) to the specified value

getpc
Displays the current value of the program counter

setpc <value>
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 <address>
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.

Base + 2: mtime (see RISC-V Machine Level ISA specification)

Base + 10: mtimecmp (see RISC-V Machine Level ISA specification)

Base + 18 - Base + 255: Reserved

]]

--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] + 17) 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)*2^(8*i)
		elseif offsetaddr == mem.csr[0x801]+1 and mem.csr[0x801] ~= 0 then
			--Mesecons I/O Data
			local bits = {[0] = pin.a,pin.b,pin.c,pin.d}
			if mem.meseconsdir.a then
				bits[0] = mem.meseconsdata.a
			end
			if mem.meseconsdir.b then
				bits[1] = mem.meseconsdata.b
			end
			if mem.meseconsdir.c then
				bits[2] = mem.meseconsdata.c
			end
			if mem.meseconsdir.d then
				bits[3] = mem.meseconsdata.d
			end
			out = out + implodebits(bits,4)*2^(8*i)
		elseif offsetaddr >= mem.csr[0x801]+2 and offsetaddr < mem.csr[0x801]+2+8 and mem.csr[0x801] ~= 0 then
			--mtime
			local byteindex = offsetaddr - (mem.csr[0x801]+2)
			if mem.mbe then byteindex = 7-byteindex end
			local time = mem.csr[0xc01]
			local timeh = mem.csr[0xc81]
			local thisout = 0
			if byteindex == 0 then
				thisout = time%2^8
			elseif byteindex == 1 then
				thisout = math.floor(time/2^8)%2^8
			elseif byteindex == 2 then
				thisout = math.floor(time/2^16)%2^8
			elseif byteindex == 3 then
				thisout = math.floor(time/2^24)
			elseif byteindex == 4 then
				thisout = timeh%2^8
			elseif byteindex == 5 then
				thisout = math.floor(timeh/2^8)%2^8
			elseif byteindex == 6 then
				thisout = math.floor(timeh/2^16)%2^8
			elseif byteindex == 7 then
				thisout = math.floor(timeh/2^24)
			end
			thisout = thisout*2^(i*8)
			out = out + thisout
		elseif offsetaddr >= mem.csr[0x801]+10 and offsetaddr < mem.csr[0x801]+10+8 and mem.csr[0x801] ~= 0 then
			--mtimecmp
			local byteindex = offsetaddr - (mem.csr[0x801]+10)
			if mem.mbe then byteindex = 7-byteindex end
			local thisout = 0
			if byteindex == 0 then
				thisout = mem.mtimecmplow%2^8
			elseif byteindex == 1 then
				thisout = math.floor(mem.mtimecmplow/2^8)%2^8
			elseif byteindex == 2 then
				thisout = math.floor(mem.mtimecmplow/2^16)%2^8
			elseif byteindex == 3 then
				thisout = math.floor(mem.mtimecmplow/2^24)
			elseif byteindex == 4 then
				thisout = mem.mtimecmphigh%2^8
			elseif byteindex == 5 then
				thisout = math.floor(mem.mtimecmphigh/2^8)%2^8
			elseif byteindex == 6 then
				thisout = math.floor(mem.mtimecmphigh/2^16)%2^8
			elseif byteindex == 7 then
				thisout = math.floor(mem.mtimecmphigh/2^24)
			end
			thisout = thisout*2^(i*8)
			out = out + thisout
		elseif offsetaddr < RAM_SIZE then
			local segment = math.floor(offsetaddr/256)
			local offset = offsetaddr%256
			out = out + (string.byte(string.sub(mem.ram[segment],offset+1,offset+1)) or 0) * 2^(8*i)
		end
	end
	return out
end

local function writeram(address,data,bytes,fake)
	if address > RAM_SIZE-1 and not fake and not (address >= mem.csr[0x801] and address <= mem.csr[0x801] + 17) then
		fault = 7
		return
	end
	if mem.reservationset then
		if mem.reservationset + 4 >= address and mem.reservationset <= address+bytes-1 then
			mem.reservationset = nil
			if mem.rswaiting then
				if mem.rswaiting == "nto" then digiline_send("monitordisp","CPU started") end
				mem.rswaiting = false
				mem.running = true
				interrupt(0,"tick")
			end
		end
	end
	if fake then return end
	for i=0,bytes-1 do
		local thisbyte = data % (2^((i+1)*8)) / 2^(i*8)
		thisbyte = math.floor(thisbyte) % 2^32
		local offsetaddr
		if mem.mbe then
			offsetaddr = address+bytes-1-i
		else
			offsetaddr = address+i
		end
		if offsetaddr == mem.csr[0x801] and mem.csr[0x801] ~= 0 then
			--Mesecons I/O Direction
			local bits = explodebits(thisbyte,8)
			mem.meseconsdir = {
				a = bits[0],
				b = bits[1],
				c = bits[2],
				d = bits[3],
			}
			if not bits[0] then
				port.a = false
				mem.meseconsdata.a = pin.a
			else
				port.a = mem.meseconsdata.a
			end
			if not bits[1] then
				port.b = false
				mem.meseconsdata.b = pin.b
			else
				port.b = mem.meseconsdata.b
			end
			if not bits[2] then
				port.c = false
				mem.meseconsdata.c = pin.c
			else
				port.c = mem.meseconsdata.c
			end
			if not bits[3] then
				port.d = false
				mem.meseconsdata.d = pin.d
			else
				port.d = mem.meseconsdata.d
			end
		elseif offsetaddr == mem.csr[0x801] + 1 and mem.csr[0x801] ~= 0 then
			--Mesecons I/O Data
			local bits = explodebits(thisbyte,8)
			if mem.meseconsdir.a then
				mem.meseconsdata.a = bits[0]
				port.a = bits[0]
			end
			if mem.meseconsdir.b then
				mem.meseconsdata.b = bits[1]
				port.b = bits[1]
			end
			if mem.meseconsdir.c then
				mem.meseconsdata.c = bits[2]
				port.c = bits[2]
			end
			if mem.meseconsdir.d then
				mem.meseconsdata.d = bits[3]
				port.d = bits[3]
			end
		elseif offsetaddr >= mem.csr[0x801]+2 and offsetaddr < mem.csr[0x801]+2+8 and mem.csr[0x801] ~= 0 then
			--mtime
			local byteindex = offsetaddr - (mem.csr[0x801]+2)
			if mem.mbe then byteindex = 7-byteindex end
			if byteindex >= 4 then
				local bits = explodebits(mem.csr[0xc81],32)
				local newbits = explodebits(thisbyte,8)
				byteindex = byteindex - 4
				for i=0,7 do
					bits[byteindex*8+i] = newbits[i]
				end
				mem.csr[0xc81] = implodebits(bits,32)
			else
				local bits = explodebits(os.time()-mem.starttime,32)
				local newbits = explodebits(thisbyte,8)
				for i=0,7 do
					bits[byteindex*8+i] = newbits[i]
				end
				local newmtime = implodebits(bits,32)
				mem.starttime = os.time()-newmtime
			end
		elseif offsetaddr >= mem.csr[0x801]+10 and offsetaddr < mem.csr[0x801]+10+8 and mem.csr[0x801] ~= 0 then
			--mtimecmp
			local byteindex = offsetaddr - (mem.csr[0x801]+10)
			if mem.mbe then byteindex = 7-byteindex end
			if byteindex >= 4 then
				local bits = explodebits(mem.mtimecmphigh,32)
				local newbits = explodebits(thisbyte,8)
				byteindex = byteindex - 4
				for i=0,7 do
					bits[byteindex*8+i] = newbits[i]
				end
				mem.mtimecmphigh = implodebits(bits,32)
			else
				local bits = explodebits(mem.mtimecmplow,32)
				local newbits = explodebits(thisbyte,8)
				for i=0,7 do
					bits[byteindex*8+i] = newbits[i]
				end
				mem.mtimecmplow = implodebits(bits,32)
			end
		elseif address < RAM_SIZE then
			local segment = math.floor(offsetaddr/256)
			local offset = offsetaddr%256
			mem.ram[segment] = string.sub(mem.ram[segment],0,offset)..string.char(thisbyte)..string.sub(mem.ram[segment],offset+2,-1)
		end
	end
end

local stdoutdirty = false

local function scrollstdout(lines)
	lines = lines or 1
	for i=1,lines do
		table.remove(mem.stdout,1)
		mem.stdout[6] = ""
	end
end

local function stdout(text)
	if string.len(text) == 0 then return end
	if STDOUT_TO_TERMINAL then print(text) end
	stdoutdirty = true
	for i=1,string.len(text) do
		local thischar = string.sub(text,i,i)
		if string.len(mem.stdout[6]) == 20 or thischar == "\n" then
			scrollstdout(1)
			if thischar ~= "\n" then mem.stdout[6] = mem.stdout[6]..thischar end
		elseif thischar ~= string.char(0) then
			mem.stdout[6] = mem.stdout[6]..thischar
		end
	end
end

local function readcsr(address)
	if address == 0x341 then --mepc
		local ret = mem.csr[0x341]
		--mepc[0] must be 0
		if ret%2 == 1 then ret = ret-1 end
		--mepc[1] must be 0 if IALIGN=32
		if not mem.isa.c and ret%4 >= 2 then
			ret = ret - 2
		end
		return ret
	elseif address == 0x300 then --mstatus
		local bits = {}
		bits[3] = mem.mie
		bits[7] = mem.mpie
		bits[11] = mem.mpp%2 == 1
		bits[12] = mem.mpp >= 2
		return implodebits(bits,32)
	elseif address == 0x310 then --mstatush
		local bits = {}
		bits[5] = mem.mbe
		bits[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
		mem.mtvecwritten = true
	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)
	local oldreason = reason
	if mem.mdt then
		writecsr(0x34b,reason) --mtval2
		reason = 0x10
	end
	writecsr(0x342,reason) --mcause
	writecsr(0x341,mem.registers.pc) --mepc
	writecsr(0x343,0) --mtval
	mem.mpie = mem.mie
	mem.mie = false
	if not mem.mdt then
		mem.mdt = true
		if mem.trapmode == 1 then
			--vectored mode
			local int = reason >= 0x8000000
			reason = reason%0x80000000
			mem.registers.pc = reason*4+mem.trapbase
			return true,false
		else
			--direct or reserved (treated as direct) mode
			mem.registers.pc = mem.trapbase
			return true,false
		end
	else
		digiline_send("monitordisp","Unexpected trap!")
		if not mem.mtvecwritten then
			digiline_send("monitordisp",traptypes[oldreason] or "Unknown reason")
			digiline_send("monitordisp","(no handler present)")
		else
			digiline_send("monitordisp",traptypes[reason] or "Unknown reason")
		end
		digiline_send("monitordisp",string.format("PC: %08X",mem.registers.pc))
		digiline_send("monitordisp","System halted")
		mem.running = false
		return true,true
	end
end

local function checkinterrupts()
	local miebits = explodebits(mem.csr[0x304],32)
	local mipbits = {}
	if mem.mtimecmphigh == mem.csr[0xc81] and (os.time() - mem.starttime) >= mem.mtimecmplow then
		mipbits[7] = true --mtip
	elseif mem.mtimecmphigh < mem.csr[0xc81] then
		mipbits[7] = true --mtip
	end
	mem.csr[0x344] = implodebits(mipbits,32)
	if not mem.mie then return end
	local order = {11,3,7,9,1,5,13}
	for _,i in ipairs(order) do
		if miebits[i] and mipbits[i] then
			return trap(0x80000000+i)
		end
	end
end

local cmpushpopreglists = {
			[4] = {registers = {1},stack = 16},
			[5] = {registers = {1,8},stack = 16},
			[6] = {registers = {1,8,9},stack = 16},
			[7] = {registers = {1,8,9,18},stack = 16},
			[8] = {registers = {1,8,9,18,19},stack = 32},
			[9] = {registers = {1,8,9,18,19,20},stack = 32},
			[10] = {registers = {1,8,9,18,19,20,21},stack = 32},
			[11] = {registers = {1,8,9,18,19,20,21,22},stack = 32},
			[12] = {registers = {1,8,9,18,19,20,21,22,23},stack = 48},
			[13] = {registers = {1,8,9,18,19,20,21,22,23,24},stack = 48},
			[14] = {registers = {1,8,9,18,19,20,21,22,23,24,25},stack = 48},
			[15] = {registers = {1,8,9,18,19,20,21,22,23,24,25,26,27},stack = 64},
		}

local operations = {
	add = function(rd,rs1,rs2)
		setreg(rd,getreg(rs1)+getreg(rs2))
	end,
	sub = function(rd,rs1,rs2)
		setreg(rd,getreg(rs1)-getreg(rs2))
	end,
	xor = function(rd,rs1,rs2)
		local rs1bits = explodebits(getreg(rs1),32)
		local rs2bits = explodebits(getreg(rs2),32)
		local rdbits = {}
		for i=0,31 do rdbits[i] = rs1bits[i] ~= rs2bits[i] end
		setreg(rd,implodebits(rdbits,32,false))
	end,
	orr = function(rd,rs1,rs2) --instruction is "or" but that's a reserved word
		local rs1bits = explodebits(getreg(rs1),32)
		local rs2bits = explodebits(getreg(rs2),32)
		local rdbits = {}
		for i=0,31 do rdbits[i] = rs1bits[i] or rs2bits[i] end
		setreg(rd,implodebits(rdbits,32,false))
	end,
	andr = function(rd,rs1,rs2) --instruction is "and" but that's a reserved word
		local rs1bits = explodebits(getreg(rs1),32)
		local rs2bits = explodebits(getreg(rs2),32)
		local rdbits = {}
		for i=0,31 do rdbits[i] = rs1bits[i] and rs2bits[i] end
		setreg(rd,implodebits(rdbits,32,false))
	end,
	xnor = function(rd,rs1,rs2)
		local rs1bits = explodebits(getreg(rs1),32)
		local rs2bits = explodebits(getreg(rs2),32)
		local rdbits = {}
		for i=0,31 do rdbits[i] = rs1bits[i] == rs2bits[i] end
		setreg(rd,implodebits(rdbits,32,false))
	end,
	orn = function(rd,rs1,rs2)
		local rs1bits = explodebits(getreg(rs1),32)
		local rs2bits = explodebits(getreg(rs2),32)
		local rdbits = {}
		for i=0,31 do rdbits[i] = rs1bits[i] or not rs2bits[i] end
		setreg(rd,implodebits(rdbits,32,false))
	end,
	andn = function(rd,rs1,rs2)
		local rs1bits = explodebits(getreg(rs1),32)
		local rs2bits = explodebits(getreg(rs2),32)
		local rdbits = {}
		for i=0,31 do rdbits[i] = rs1bits[i] and not rs2bits[i] end
		setreg(rd,implodebits(rdbits,32,false))
	end,
	sll = function(rd,rs1,rs2)
		setreg(rd,getreg(rs1)*(2^(getreg(rs2)%2^5)))
	end,
	srl = function(rd,rs1,rs2)
		setreg(rd,getreg(rs1)/(2^(getreg(rs2)%2^5)))
	end,
	sra = function(rd,rs1,rs2)
		local num = getreg(rs1)
		local amount = getreg(rs2)%2^5
		local sign = num>=(2^31)
		if amount == 0 then return end
		for _=1,amount do
			num = num/2
			if sign then num = num+(2^31) end
		end
		setreg(rd,num)
	end,
	slt = function(rd,rs1,rs2)
		local num1 = getreg(rs1)
		local num2 = getreg(rs2)
		if num1 >= 2^31 then num1 = num1-(2^32) end
		if num2 >= 2^31 then num2 = num2-(2^32) end
		setreg(rd,num1 < num2 and 1 or 0)
	end,
	sltu = function(rd,rs1,rs2)
		local num1 = getreg(rs1)
		local num2 = getreg(rs2)
		setreg(rd,num1 < num2 and 1 or 0)
	end,
	addi = function(rd,rs1,imm)
		local immbits = explodebits(imm,12)
		signextend(immbits,12,32)
		imm = implodebits(immbits,32,false)
		setreg(rd,getreg(rs1)+imm)
	end,
	xori = function(rd,rs1,imm)
		local rs1bits = explodebits(getreg(rs1),32)
		local immbits = explodebits(imm,12)
		signextend(immbits,12,32)
		local rdbits = {}
		for i=0,31 do
			rdbits[i] = rs1bits[i] ~= immbits[i]
		end
		setreg(rd,implodebits(rdbits,32,false))
	end,
	ori = function(rd,rs1,imm)
		local rs1bits = explodebits(getreg(rs1),32)
		local immbits = explodebits(imm,12)
		signextend(immbits,12,32)
		local rdbits = {}
		for i=0,31 do
			rdbits[i] = rs1bits[i] or immbits[i]
		end
		setreg(rd,implodebits(rdbits,32,false))
	end,
	andi = function(rd,rs1,imm)
		local rs1bits = explodebits(getreg(rs1),32)
		local immbits = explodebits(imm,12)
		signextend(immbits,12,32)
		local rdbits = {}
		for i=0,31 do
			rdbits[i] = rs1bits[i] and immbits[i]
		end
		setreg(rd,implodebits(rdbits,32,false))
	end,
	slli = function(rd,rs1,imm)
		setreg(rd,getreg(rs1)*(2^imm))
	end,
	srli = function(rd,rs1,imm)
		setreg(rd,getreg(rs1)/(2^imm))
	end,
	srai = function(rd,rs1,imm)
		local num = getreg(rs1)
		local sign = num>=(2^31)
		if imm == 0 then return end
		for _=1,imm do
			num = num/2
			if sign then num = num+(2^31) end
		end
		setreg(rd,num)
	end,
	slti = function(rd,rs1,imm)
		local num1 = getreg(rs1)
		local immbits = explodebits(imm,12)
		signextend(immbits,12,32)
		local num2 = implodebits(immbits,32,false)
		if num1 >= 2^31 then num1 = num1-(2^32) end
		if num2 >= 2^31 then num2 = num2-(2^32) end
		setreg(rd,num1 < num2 and 1 or 0)
	end,
	sltiu = function(rd,rs1,imm)
		local num1 = getreg(rs1)
		local immbits = explodebits(imm,12)
		signextend(immbits,12,32)
		local num2 = implodebits(immbits,32,false)
		setreg(rd,num1 < num2 and 1 or 0)
	end,
	beq = function(rs1,rs2,imm)
		local num1 = getreg(rs1)
		local num2 = getreg(rs2)
		if num1 == num2 then
			local immbits = explodebits(imm,13)
			signextend(immbits,13,32)
			imm = implodebits(immbits,13,true)
			mem.registers.pc = (mem.registers.pc + imm) % 2^32
			return true
		end
	end,
	bne = function(rs1,rs2,imm)
		local num1 = getreg(rs1)
		local num2 = getreg(rs2)
		if num1 ~= num2 then
			local immbits = explodebits(imm,13)
			signextend(immbits,13,32)
			imm = implodebits(immbits,13,true)
			mem.registers.pc = (mem.registers.pc + imm) % 2^32
			return true
		end
	end,
	blt = function(rs1,rs2,imm)
		local num1 = getreg(rs1)
		local num2 = getreg(rs2)
		if num1 >= 2^31 then num1 = num1-(2^32) end
		if num2 >= 2^31 then num2 = num2-(2^32) end
		if num1 < num2 then
			local immbits = explodebits(imm,13)
			signextend(immbits,13,32)
			imm = implodebits(immbits,13,true)
			mem.registers.pc = (mem.registers.pc + imm) % 2^32
			return true
		end
	end,
	bge = function(rs1,rs2,imm)
		local num1 = getreg(rs1)
		local num2 = getreg(rs2)
		if num1 >= 2^31 then num1 = num1-(2^32) end
		if num2 >= 2^31 then num2 = num2-(2^32) end
		if num1 >= num2 then
			local immbits = explodebits(imm,13)
			signextend(immbits,13,32)
			imm = implodebits(immbits,13,true)
			mem.registers.pc = (mem.registers.pc + imm) % 2^32
			return true
		end
	end,
	bltu = function(rs1,rs2,imm)
		local num1 = getreg(rs1)
		local num2 = getreg(rs2)
		if num1 < num2 then
			local immbits = explodebits(imm,13)
			signextend(immbits,13,32)
			imm = implodebits(immbits,13,true)
			mem.registers.pc = (mem.registers.pc + imm) % 2^32
			return true
		end
	end,
	bgeu = function(rs1,rs2,imm)
		local num1 = getreg(rs1)
		local num2 = getreg(rs2)
		if num1 >= num2 then
			local immbits = explodebits(imm,13)
			signextend(immbits,13,32)
			imm = implodebits(immbits,13,true)
			mem.registers.pc = (mem.registers.pc + imm) % 2^32
			return true
		end
	end,
	jal = function(rd,imm,compressed)
		setreg(rd,mem.registers.pc+(compressed and 2 or 4))
		local immbits = explodebits(imm,21)
		signextend(immbits,21,32)
		imm = implodebits(immbits,32,true)
		local oldpc = mem.registers.pc
		mem.registers.pc = (mem.registers.pc + imm) % 2^32
		return true
	end,
	jalr = function(rd,rs1,imm,compressed)
		local oldpc = mem.registers.pc
		local immbits = explodebits(imm,12)
		signextend(immbits,12,32)
		imm = implodebits(immbits,32,true)
		mem.registers.pc = (getreg(rs1) + imm) % 2^32
		if mem.registers.pc%2 == 1 then mem.registers.pc = mem.registers.pc - 1 end
		setreg(rd,oldpc+(compressed and 2 or 4))
		return true
	end,
	lui = function(rd,imm)
		setreg(rd,imm)
	end,
	auipc = function(rd,imm)
		setreg(rd,mem.registers.pc+imm)
	end,
	lb = function(rd,rs1,imm)
		if imm >= 2^11 then imm = imm - 2^12 end
		local data = readram(getreg(rs1)+imm,1)
		data = explodebits(data,8)
		signextend(data,8,32)
		data = implodebits(data,32,false)
		setreg(rd,data)
	end,
	lh = function(rd,rs1,imm)
		if imm >= 2^11 then imm = imm - 2^12 end
		local data = readram(getreg(rs1)+imm,2)
		data = explodebits(data,16)
		signextend(data,16,32)
		data = implodebits(data,32,false)
		setreg(rd,data)
	end,
	lw = function(rd,rs1,imm)
		if imm >= 2^11 then imm = imm - 2^12 end
		local data = readram(getreg(rs1)+imm,4)
		setreg(rd,data)
	end,
	ld = function(rd,rs1,imm)
		if rd == 0 or rd == 31 then return end
		if imm >= 2^11 then imm = imm - 2^12 end
		local address = getreg(rs1)+imm
		setreg(rd,readram(address+(mem.mbe and 4 or 0),4))
		setreg(rd+1,readram(address+(mem.mbe and 0 or 4),4))
	end,
	lhu = function(rd,rs1,imm)
		if imm >= 2^11 then imm = imm - 2^12 end
		local data = readram(getreg(rs1)+imm,2)
		setreg(rd,data)
	end,
	lbu = function(rd,rs1,imm)
		if imm >= 2^11 then imm = imm - 2^12 end
		local data = readram(getreg(rs1)+imm,1)
		setreg(rd,data)
	end,
	sb = function(rs1,rs2,imm)
		if imm >= 2^11 then imm = imm - 2^12 end
		writeram(getreg(rs1)+imm,getreg(rs2)%0x100,1)
	end,
	sh = function(rs1,rs2,imm)
		if imm >= 2^11 then imm = imm - 2^12 end
		writeram(getreg(rs1)+imm,getreg(rs2)%0x10000,2)
	end,
	sw = function(rs1,rs2,imm)
		if imm >= 2^11 then imm = imm - 2^12 end
		writeram(getreg(rs1)+imm,getreg(rs2),4)
	end,
	sd = function(rs1,rs2,imm)
		if rs2 == 31 then return end
		if imm >= 2^11 then imm = imm - 2^12 end
		local address = getreg(rs1)+imm
		writeram(address+(mem.mbe and 4 or 0),getreg(rs2),4)
		writeram(address+(mem.mbe and 0 or 4),getreg(rs2+1),4)
	end,
	ecall = function()
		local func = mem.isa.i and getreg(17) or getreg(15)
		if mem.isa.i and func == 0 then func = getreg(15) end
		local param1 = getreg(10)
		local param2 = getreg(11)
		if func == 1 then
			--a7=1 - Print Integer
			if param1 > 2^31 then param1 = param1-2^32 end
			stdout(string.format("%d",param1))
		elseif func == 4 then
			--a7=4 - Print String
			local str = ""
			for i=0,255 do
				local byte = readram(param1+i,1)
				if byte == 0 then break end
				str = str..string.char(byte)
			end
			stdout(str)
		elseif func == 5 then
			--a7=5 - Read Integer
			digiline_send("monitordisp","Waiting for input")
			mem.running = false
			mem.inputwaiting = true
			mem.inputwaittype = "integer"
			return false,true
		elseif func == 8 then
			--a7=8 - Read String
			digiline_send("monitordisp","Waiting for input")
			mem.running = false
			mem.inputwaiting = true
			mem.inputwaittype = "string"
			mem.inputaddr = getreg(10)
			mem.inputmax = getreg(11)
			return false,true
		elseif func == 10 then
			--a7=10 - Exit Program
			mem.running = false
			digiline_send("monitordisp","Program\nexited normally")
			digiline_send("monitordisp","System halted")
			digiline_send("monitordisp",string.format("PC:%08X",mem.registers.pc))
		elseif func == 11 then
			--a7=11 - Print Character
			stdout(string.char(getreg(10)))
		elseif func == 12 then
			--a7=12 - Read Character
			digiline_send("monitordisp","Waiting for input")
			mem.running = false
			mem.inputwaiting = true
			mem.inputwaittype = "char"
			return false,true
		elseif func == 128 then
			--a7=128 - Get Random Integer
			local lowlimit = getreg(10)
			local highlimit = getreg(11)
			if lowlimit >= 2^31 then lowlimit = lowlimit - 2^32 end
			if highlimit >= 2^31 then highlimit = highlimit - 2^32 end
			local result = math.random(lowlimit,highlimit)
			if result < 0 then result = result + 2^32 end
			setreg(10,result)
		elseif func == 129 then
			--a7=129 - Send digilines string message
			local channel = ""
			for i=0,255 do
				local byte = readram(param1+i,1)
				if byte == 0 then break end
				channel = channel..string.char(byte)
			end
			local message = ""
			for i=0,255 do
				local byte = readram(param2+i,1)
				if byte == 0 then break end
				message = message..string.char(byte)
			end
			digiline_send(channel,message)
		elseif func == 130 then
			--a7=130 - Get Input Buffer Level
			setreg(10,string.len(mem.inputbuf))
		elseif func == 131 then
			--a7=131 - Clear Input Buffer
			mem.inputbuf = ""
		elseif func == 132 then
			--a7=132 -- Read Character From Input Buffer
			if mem.inputbuf == "" then
				setreg(10,0)
			else
				setreg(10,string.byte(string.sub(mem.inputbuf,1,1)))
				mem.inputbuf = string.sub(mem.inputbuf,2,-1)
			end
		elseif func == 133 then
			--a7=133 - Get Digilines Buffer Level
			setreg(10,#mem.digilinesqueue)
		elseif func == 134 then
			--a7=134 - Clear Digilines Buffer
			mem.digilinesqueue = {}
		elseif func == 135 then
			--a7=135 - Read Digilines Message
			local dlevent = mem.digilinesqueue[1]
			if not dlevent then dlevent = {channel = "",msg = ""} end
			dlevent.channel = string.sub(dlevent.channel,1,getreg(11)-1).."\0"
			dlevent.msg = string.sub(dlevent.msg,1,getreg(13)-1).."\0"
			local channelstart = getreg(10)
			for i=1,string.len(dlevent.channel) do
				local address = channelstart+i-1
				writeram(address,string.byte(string.sub(dlevent.channel,i,i)),1)
			end
			local msgstart = getreg(12)
			for i=1,string.len(dlevent.msg) do
				local address = msgstart+i-1
				writeram(address,string.byte(string.sub(dlevent.msg,i,i)),1)
			end
			table.remove(mem.digilinesqueue,1)
		end
	end,
	ebreak = function()
		return trap(3)
	end,
	csrrw = function(rd,rs1,imm)
		setreg(rd,readcsr(imm))
		writecsr(imm,getreg(rs1))
		return checkinterrupts()
	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))
		return checkinterrupts()
	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))
		return checkinterrupts()
	end,
	csrrwi = function(rd,rs1,imm)
		setreg(rd,readcsr(imm))
		writecsr(imm,rs1)
		return checkinterrupts()
	end,
	csrrsi = function(rd,rs1,imm)
		setreg(rd,readcsr(imm))
		if rs1 == 0 then return end
		local csrbits = explodebits(readcsr(imm),32)
		local rs1bits = explodebits(rs1,32)
		for i=0,31 do
			csrbits[i] = csrbits[i] or rs1bits[i]
		end
		writecsr(imm,implodebits(csrbits,32))
		return checkinterrupts()
	end,
	csrrci = function(rd,rs1,imm)
		setreg(rd,readcsr(imm))
		if rs1 == 0 then return end
		local csrbits = explodebits(readcsr(imm),32)
		local rs1bits = explodebits(rs1,32)
		for i=0,31 do
			csrbits[i] = csrbits[i] and not rs1bits[i]
		end
		writecsr(imm,implodebits(csrbits,32))
		return checkinterrupts()
	end,
	mul = function(rd,rs1,rs2)
		local plow = mul64(getreg(rs1),getreg(rs2),false,false)
		setreg(rd,plow)
	end,
	mulh = function(rd,rs1,rs2)
		local _,phigh = mul64(getreg(rs1),getreg(rs2),true,true)
		setreg(rd,phigh)
	end,
	mulsu = function(rd,rs1,rs2)
		local _,phigh = mul64(getreg(rs1),getreg(rs2),true,false)
		setreg(rd,phigh)
	end,
	mulu = function(rd,rs1,rs2)
		local _,phigh = mul64(getreg(rs1),getreg(rs2),false,false)
		setreg(rd,phigh)
	end,
	div = function(rd,rs1,rs2)
		local a = getreg(rs1)
		local b = getreg(rs2)
		if b == 0 then
			--Division by zero
			setreg(rd,2^32-1)
			return
		end
		if a >= 2^31 then a = a - 2^32 end
		if b >= 2^31 then b = b - 2^32 end
		if a == -2^31 and b == -1 then
			--Divide overflow
			setreg(rd,a)
		end
		local result = math.floor(a/b)
		if result < 0 then result = result + 2^32 end
		setreg(rd,result)
	end,
	rem = function(rd,rs1,rs2)
		local a = getreg(rs1)
		local b = getreg(rs2)
		if b == 0 then
			--Division by zero
			setreg(rd,a)
			return
		end
		if a >= 2^31 then a = a - 2^32 end
		if b >= 2^31 then b = b - 2^32 end
		if a == -2^31 and b == -1 then
			--Divide overflow
			setreg(rd,0)
		end
		local result = a%b
		setreg(rd,result)
	end,
	divu = function(rd,rs1,rs2)
		local a = getreg(rs1)
		local b = getreg(rs2)
		if b == 0 then
			--Division by zero
			setreg(rd,2^32-1)
			return
		end
		local result = math.floor(a/b)
		setreg(rd,result)
	end,
	remu = function(rd,rs1,rs2)
		local a = getreg(rs1)
		local b = getreg(rs2)
		if b == 0 then
			--Division by zero
			setreg(rd,a)
			return
		end
		local result = a%b
		setreg(rd,result)
	end,
	sh1add = function(rd,rs1,rs2)
		setreg(rd,getreg(rs1)*2+getreg(rs2))
	end,
	sh2add = function(rd,rs1,rs2)
		setreg(rd,getreg(rs1)*4+getreg(rs2))
	end,
	sh3add = function(rd,rs1,rs2)
		setreg(rd,getreg(rs1)*8+getreg(rs2))
	end,
	clz = function(rd,rs1)
		local bits = explodebits(getreg(rs1),32)
		for i=31,0,-1 do
			if bits[i] then
				setreg(rd,31-i)
				return
			end
		end
		setreg(rd,32)
	end,
	ctz = function(rd,rs1)
		local bits = explodebits(getreg(rs1),32)
		for i=0,31 do
			if bits[i] then
				setreg(rd,i)
				return
			end
		end
		setreg(rd,32)
	end,
	cpop = function(rd,rs1)
		local bits = explodebits(getreg(rs1),32)
		local out = 0
		for i=0,31 do
			if bits[i] then
				out = out + 1
			end
		end
		setreg(rd,out)
	end,
	maxu = function(rd,rs1,rs2)
		setreg(rd,math.max(getreg(rs1),getreg(rs2)))
	end,
	minu = function(rd,rs1,rs2)
		setreg(rd,math.min(getreg(rs1),getreg(rs2)))
	end,
	max = function(rd,rs1,rs2)
		local a = getreg(rs1)
		local b = getreg(rs2)
		if a >= 2^31 then a = a - 2^32 end
		if b >= 2^31 then b = b - 2^32 end
		local out = math.max(a,b)
		if out < 0 then out = out + 2^32 end
		setreg(rd,out)
	end,
	min = function(rd,rs1,rs2)
		local a = getreg(rs1)
		local b = getreg(rs2)
		if a >= 2^31 then a = a - 2^32 end
		if b >= 2^31 then b = b - 2^32 end
		local out = math.min(a,b)
		if out < 0 then out = out + 2^32 end
		setreg(rd,out)
	end,
	sextb = function(rd,rs1)
		local bits = explodebits(getreg(rs1),8)
		signextend(bits,8,32)
		setreg(rd,implodebits(bits,32))
	end,
	sexth = function(rd,rs1)
		local bits = explodebits(getreg(rs1),16)
		signextend(bits,16,32)
		setreg(rd,implodebits(bits,32))
	end,
	zexth = function(rd,rs1)
		setreg(rd,getreg(rs1)%2^16)
	end,
	rol = function(rd,rs1,rs2)
		local num = getreg(rs1)
		local amount = getreg(rs2)%32
		if amount == 0 then return end
		for i=1,amount do
			local endbit = num >= 2^31
			num = (num*2)%2^32
			if endbit then num = num + 1 end
		end
		setreg(rd,num)
	end,
	ror = function(rd,rs1,rs2)
		local num = getreg(rs1)
		local amount = getreg(rs2)%32
		if amount == 0 then
			setreg(rd,num)
			return
		end
		for _=1,amount do
			local endbit = num%2 == 1
			num = math.floor(num/2)
			if endbit then num = num + 2^31 end
		end
		setreg(rd,num)
	end,
	rori = function(rd,rs1,imm)
		local num = getreg(rs1)
		local amount = imm%32
		if amount == 0 then
			setreg(rd,num)
			return
		end
		for _=1,amount do
			local endbit = num%2 == 1
			num = math.floor(num/2)
			if endbit then num = num + 2^31 end
		end
		setreg(rd,num)
	end,
	orcb = function(rd,rs1)
		local input = getreg(rs1)
		local b1 = input%2^8 > 0
		local b2 = math.floor(input/2^8)%2^8 > 0
		local b3 = math.floor(input/2^16)%2^8 > 0
		local b4 = math.floor(input/2^24)%2^8 > 0
		local out = 0
		if b1 then out = out + 0xff end
		if b2 then out = out + 0xff00 end
		if b3 then out = out + 0xff0000 end
		if b4 then out = out + 0xff000000 end
		setreg(rd,out)
	end,
	rev8 = function(rd,rs1)
		local input = getreg(rs1)
		local b1 = input%2^8
		local b2 = math.floor(input/2^8)%2^8
		local b3 = math.floor(input/2^16)%2^8
		local b4 = math.floor(input/2^24)%2^8
		local out = b4
		out = out + (b3*2^8)
		out = out + (b2*2^16)
		out = out + (b1*2^24)
		setreg(rd,out)
	end,
	bclr = function(rd,rs1,rs2)
		local bits = explodebits(getreg(rs1),32)
		bits[getreg(rs2)%32] = false
		setreg(rd,implodebits(bits,32))
	end,
	bset = function(rd,rs1,rs2)
		local bits = explodebits(getreg(rs1),32)
		bits[getreg(rs2)%32] = true
		setreg(rd,implodebits(bits,32))
	end,
	binv = function(rd,rs1,rs2)
		local bits = explodebits(getreg(rs1),32)
		bits[getreg(rs2)%32] = not bits[getreg(rs2)%32]
		setreg(rd,implodebits(bits,32))
	end,
	bclri = function(rd,rs1,imm)
		local bits = explodebits(getreg(rs1),32)
		bits[imm%32] = false
		setreg(rd,implodebits(bits,32))
	end,
	bseti = function(rd,rs1,imm)
		local bits = explodebits(getreg(rs1),32)
		bits[imm%32] = true
		setreg(rd,implodebits(bits,32))
	end,
	binvi = function(rd,rs1,imm)
		local bits = explodebits(getreg(rs1),32)
		bits[imm%32] = not bits[imm%32]
		setreg(rd,implodebits(bits,32))
	end,
	bext = function(rd,rs1,rs2)
		local bit = explodebits(getreg(rs1),32)[getreg(rs2)%32]
		setreg(rd,bit and 1 or 0)
	end,
	bexti = function(rd,rs1,imm)
		local bit = explodebits(getreg(rs1),32)[imm%32]
		setreg(rd,bit and 1 or 0)
	end,
	xperm8 = function(rd,rs1,rs2)
		local a = getreg(rs1)
		local outputs = {
			[0] = a%2^8,
			math.floor(a/2^8)%2^8,
			math.floor(a/2^16)%2^8,
			math.floor(a/2^24)%2^8,
		}
		local b = getreg(rs2)
		local selections = {
			[0] = b%2^8,
			math.floor(b/2^8)%2^8,
			math.floor(b/2^16)%2^8,
			math.floor(b/2^24)%2^8,
		}
		local out = 0
		for i=0,3 do
			local num = outputs[selections[i]] or 0
			out = out+(num*2^(i*8))
		end
		setreg(rd,out)
	end,
	xperm4 = function(rd,rs1,rs2)
		local a = getreg(rs1)
		local outputs = {
			[0] = a%2^4,
			math.floor(a/2^4)%2^4,
			math.floor(a/2^8)%2^4,
			math.floor(a/2^12)%2^4,
			math.floor(a/2^16)%2^4,
			math.floor(a/2^20)%2^4,
			math.floor(a/2^24)%2^4,
			math.floor(a/2^28)%2^4,
		}
		local b = getreg(rs2)
		local selections = {
			[0] = b%2^4,
			math.floor(b/2^4)%2^4,
			math.floor(b/2^8)%2^4,
			math.floor(b/2^12)%2^4,
			math.floor(b/2^16)%2^4,
			math.floor(b/2^20)%2^4,
			math.floor(b/2^24)%2^4,
			math.floor(b/2^28)%2^4,
		}
		local out = 0
		for i=0,7 do
			local num = outputs[selections[i]] or 0
			out = out+(num*2^(i*4))
		end
		setreg(rd,out)
	end,
	lr = function(rd,rs1,rs2)
		local address = getreg(rs1)
		setreg(rd,readram(address,4))
		mem.reservationset = address
	end,
	sc = function(rd,rs1,rs2)
		local address = getreg(rs1)
		if mem.reservationset == address then
			writeram(address,getreg(rs2),4)
			setreg(rd,0)
		else
			setreg(rd,1)
		end
		mem.reservationset = nil
	end,
	amoswapw = function(rd,rs1,rs2)
		local address = getreg(rs1)
		local newdata = getreg(rs2)
		setreg(rd,readram(address,4))
		writeram(address,newdata,4)
	end,
	amoaddw = function(rd,rs1,rs2)
		local address = getreg(rs1)
		local data = readram(address,4)
		local newdata = getreg(rs2)
		setreg(rd,data)
		writeram(address,(data+newdata)%2^32,4)
	end,
	amoandw = function(rd,rs1,rs2)
		local address = getreg(rs1)
		local data = readram(address,4)
		local newdata = getreg(rs2)
		setreg(rd,data)
		local bits = explodebits(data,32)
		local newbits = explodebits(newdata,32)
		for i=0,31 do bits[i] = bits[i] and newbits[i] end
		writeram(address,implodebits(bits,32),4)
	end,
	amoorw = function(rd,rs1,rs2)
		local address = getreg(rs1)
		local data = readram(address,4)
		local newdata = getreg(rs2)
		setreg(rd,data)
		local bits = explodebits(data,32)
		local newbits = explodebits(newdata,32)
		for i=0,31 do bits[i] = bits[i] or newbits[i] end
		writeram(address,implodebits(bits,32),4)
	end,
	amoxorw = function(rd,rs1,rs2)
		local address = getreg(rs1)
		local data = readram(address,4)
		local newdata = getreg(rs2)
		setreg(rd,data)
		local bits = explodebits(data,32)
		local newbits = explodebits(newdata,32)
		for i=0,31 do bits[i] = bits[i] ~= newbits[i] end
		writeram(address,implodebits(bits,32),4)
	end,
	amomaxw = function(rd,rs1,rs2)
		local address = getreg(rs1)
		local data = readram(address,4)
		local newdata = getreg(rs2)
		setreg(rd,data)
		if data > 2^31 then data = data - 2^32 end
		if newdata > 2^31 then newdata = newdata - 2^32 end
		newdata = math.max(data,newdata)
		if newdata < 0 then newdata = newdata + 2^32 end
		writeram(address,newdata,4)
	end,
	amomaxuw = function(rd,rs1,rs2)
		local address = getreg(rs1)
		local data = readram(address,4)
		local newdata = getreg(rs2)
		setreg(rd,data)
		newdata = math.max(data,newdata)
		writeram(address,newdata,4)
	end,
	amominw = function(rd,rs1,rs2)
		local address = getreg(rs1)
		local data = readram(address,4)
		local newdata = getreg(rs2)
		setreg(rd,data)
		if data > 2^31 then data = data - 2^32 end
		if newdata > 2^31 then newdata = newdata - 2^32 end
		newdata = math.min(data,newdata)
		if newdata < 0 then newdata = newdata + 2^32 end
		writeram(address,newdata,4)
	end,
	amominuw = function(rd,rs1,rs2)
		local address = getreg(rs1)
		local data = readram(address,4)
		local newdata = getreg(rs2)
		setreg(rd,data)
		newdata = math.min(data,newdata)
		writeram(address,newdata,4)
	end,
	amocasw = function(rd,rs1,rs2)
		local address = getreg(rs1)
		local data = readram(address,4)
		local compare = getreg(rd)
		if data == compare then
			writeram(address,getreg(rs2),4)
		end
		setreg(rd,data)
	end,
	amocasd = function(rd,rs1,rs2)
		if rd == 31 then return end
		if rs2 == 31 then return end
		local address = getreg(rs1)
		local datalow = readram(address,4)
		local datahigh = readram(address+4,4)
		local comparelow = getreg(rd)
		local comparehigh = rd == 0 and 0 or getreg(rd+1)
		if datalow == comparelow and datahigh == comparehigh then
			writeram(address,getreg(rs2),4)
			writeram(address+4,rs2 == 0 and 0 or getreg(rs2+1),4)
		end
		if rd ~= 0 then
			setreg(rd,datalow)
			setreg(rd+1,datahigh)
		end
	end,
	amoswaph = function(rd,rs1,rs2)
		local address = getreg(rs1)
		local newdata = getreg(rs2)%2^16
		local olddata = explodebits(readram(address,2),16)
		signextend(olddata,16,32)
		setreg(rd,implodebits(olddata,32))
		writeram(address,newdata,2)
	end,
	amoaddh = function(rd,rs1,rs2)
		local address = getreg(rs1)
		local data = readram(address,2)
		local newdata = getreg(rs2)%2^16
		local oldbits = explodebits(data,16)
		signextend(oldbits,16,32)
		setreg(rd,implodebits(oldbits,32))
		writeram(address,(data+newdata)%2^16,2)
	end,
	amoandh = function(rd,rs1,rs2)
		local address = getreg(rs1)
		local data = readram(address,2)
		local newdata = getreg(rs2)%2^16
		local bits = explodebits(data,16)
		signextend(bits,16,32)
		setreg(rd,implodebits(bits,32))
		local newbits = explodebits(newdata,16)
		for i=0,15 do bits[i] = bits[i] and newbits[i] end
		writeram(address,implodebits(bits,16),2)
	end,
	amoorh = function(rd,rs1,rs2)
		local address = getreg(rs1)
		local data = readram(address,2)
		local newdata = getreg(rs2)%2^16
		local bits = explodebits(data,16)
		signextend(bits,16,32)
		setreg(rd,implodebits(bits,32))
		local newbits = explodebits(newdata,16)
		for i=0,15 do bits[i] = bits[i] or newbits[i] end
		writeram(address,implodebits(bits,16),2)
	end,
	amoxorh = function(rd,rs1,rs2)
		local address = getreg(rs1)
		local data = readram(address,2)
		local newdata = getreg(rs2)%2^16
		local bits = explodebits(data,16)
		signextend(bits,16,32)
		setreg(rd,implodebits(bits,32))
		local newbits = explodebits(newdata,16)
		for i=0,15 do bits[i] = bits[i] ~= newbits[i] end
		writeram(address,implodebits(bits,16),2)
	end,
	amomaxh = function(rd,rs1,rs2)
		local address = getreg(rs1)
		local data = readram(address,2)
		local newdata = getreg(rs2)%2^16
		local bits = explodebits(data,16)
		signextend(bits,16,32)
		setreg(rd,implodebits(bits,32))
		if data > 2^15 then data = data - 2^16 end
		if newdata > 2^15 then newdata = newdata - 2^16 end
		newdata = math.max(data,newdata)
		if newdata < 0 then newdata = newdata + 2^16 end
		writeram(address,newdata,2)
	end,
	amomaxuh = function(rd,rs1,rs2)
		local address = getreg(rs1)
		local data = readram(address,2)
		local newdata = getreg(rs2)%2^16
		local bits = explodebits(data,16)
		signextend(bits,16,32)
		setreg(rd,implodebits(bits,32))
		newdata = math.max(data,newdata)
		writeram(address,newdata,2)
	end,
	amominh = function(rd,rs1,rs2)
		local address = getreg(rs1)
		local data = readram(address,2)
		local newdata = getreg(rs2)%2^16
		local bits = explodebits(data,16)
		signextend(bits,16,32)
		setreg(rd,implodebits(bits,32))
		if data > 2^15 then data = data - 2^16 end
		if newdata > 2^15 then newdata = newdata - 2^16 end
		newdata = math.min(data,newdata)
		if newdata < 0 then newdata = newdata + 2^16 end
		writeram(address,newdata,2)
	end,
	amominuh = function(rd,rs1,rs2)
		local address = getreg(rs1)
		local data = readram(address,2)
		local newdata = getreg(rs2)%2^16
		local bits = explodebits(data,16)
		signextend(bits,16,32)
		setreg(rd,implodebits(bits,32))
		newdata = math.min(data,newdata)
		writeram(address,newdata,2)
	end,
	amocash = function(rd,rs1,rs2)
		local address = getreg(rs1)
		local data = readram(address,2)
		local compare = getreg(rd)%2^16
		if data == compare then
			writeram(address,getreg(rs2)%2^16,2)
		end
		local bits = explodebits(data,16)
		signextend(bits,16,32)
		setreg(rd,implodebits(bits,32))
	end,
	amoswapb = function(rd,rs1,rs2)
		local address = getreg(rs1)
		local newdata = getreg(rs2)%2^8
		local olddata = explodebits(readram(address,1),8)
		signextend(olddata,8,32)
		setreg(rd,implodebits(olddata,32))
		writeram(address,newdata,1)
	end,
	amoaddb = function(rd,rs1,rs2)
		local address = getreg(rs1)
		local data = readram(address,1)
		local newdata = getreg(rs2)%2^8
		local oldbits = explodebits(data,8)
		signextend(oldbits,8,32)
		setreg(rd,implodebits(oldbits,32))
		writeram(address,(data+newdata)%2^8,1)
	end,
	amoandb = function(rd,rs1,rs2)
		local address = getreg(rs1)
		local data = readram(address,1)
		local newdata = getreg(rs2)%2^8
		local bits = explodebits(data,8)
		signextend(bits,8,32)
		setreg(rd,implodebits(bits,32))
		local newbits = explodebits(newdata,8)
		for i=0,7 do bits[i] = bits[i] and newbits[i] end
		writeram(address,implodebits(bits,8),1)
	end,
	amoorb = function(rd,rs1,rs2)
		local address = getreg(rs1)
		local data = readram(address,1)
		local newdata = getreg(rs2)%2^8
		local bits = explodebits(data,8)
		signextend(bits,8,32)
		setreg(rd,implodebits(bits,32))
		local newbits = explodebits(newdata,8)
		for i=0,7 do bits[i] = bits[i] or newbits[i] end
		writeram(address,implodebits(bits,8),1)
	end,
	amoxorb = function(rd,rs1,rs2)
		local address = getreg(rs1)
		local data = readram(address,1)
		local newdata = getreg(rs2)%2^8
		local bits = explodebits(data,8)
		signextend(bits,8,32)
		setreg(rd,implodebits(bits,32))
		local newbits = explodebits(newdata,8)
		for i=0,7 do bits[i] = bits[i] ~= newbits[i] end
		writeram(address,implodebits(bits,8),1)
	end,
	amomaxb = function(rd,rs1,rs2)
		local address = getreg(rs1)
		local data = readram(address,1)
		local newdata = getreg(rs2)%2^8
		local bits = explodebits(data,8)
		signextend(bits,8,32)
		setreg(rd,implodebits(bits,32))
		if data > 2^7 then data = data - 2^8 end
		if newdata > 2^7 then newdata = newdata - 2^8 end
		newdata = math.max(data,newdata)
		if newdata < 0 then newdata = newdata + 2^8 end
		writeram(address,newdata,1)
	end,
	amomaxub = function(rd,rs1,rs2)
		local address = getreg(rs1)
		local data = readram(address,1)
		local newdata = getreg(rs2)%2^8
		local bits = explodebits(data,8)
		signextend(bits,8,32)
		setreg(rd,implodebits(bits,32))
		newdata = math.max(data,newdata)
		writeram(address,newdata,1)
	end,
	amominb = function(rd,rs1,rs2)
		local address = getreg(rs1)
		local data = readram(address,1)
		local newdata = getreg(rs2)%2^8
		local bits = explodebits(data,8)
		signextend(bits,8,32)
		setreg(rd,implodebits(bits,32))
		if data > 2^7 then data = data - 2^8 end
		if newdata > 2^7 then newdata = newdata - 2^8 end
		newdata = math.min(data,newdata)
		if newdata < 0 then newdata = newdata + 2^8 end
		writeram(address,newdata,1)
	end,
	amominub = function(rd,rs1,rs2)
		local address = getreg(rs1)
		local data = readram(address,1)
		local newdata = getreg(rs2)%2^8
		local bits = explodebits(data,8)
		signextend(bits,8,32)
		setreg(rd,implodebits(bits,8))
		newdata = math.min(data,newdata)
		writeram(address,newdata,1)
	end,
	amocasb = function(rd,rs1,rs2)
		local address = getreg(rs1)
		local data = readram(address,1)
		local compare = getreg(rd)%2^8
		if data == compare then
			writeram(address,getreg(rs2)%2^8,1)
		end
		local bits = explodebits(data,8)
		signextend(bits,8,32)
		setreg(rd,implodebits(bits,32))
	end,
	pack = function(rd,rs1,rs2)
		local lowhalf = getreg(rs1)%2^16
		local highhalf = (getreg(rs2)%2^16)*2^16
		setreg(rd,lowhalf+highhalf)
	end,
	packh = function(rd,rs1,rs2)
		local lowhalf = getreg(rs1)%2^8
		local highhalf = (getreg(rs2)%2^8)*2^8
		setreg(rd,lowhalf+highhalf)
	end,
	brev8 = function(rd,rs1,imm)
		local bits = explodebits(getreg(rs1),32)
		local newbits = {}
		for byte=0,3 do
			for bit=0,7 do
				local source = byte*8+bit
				local destination = byte*8+(7-bit)
				newbits[destination] = bits[source]
			end
		end
		setreg(rd,implodebits(newbits,32))
	end,
	zip = function(rd,rs1,imm)
		local bits = explodebits(getreg(rs1),32)
		local newbits = {}
		for i=0,15 do
			newbits[i*2] = bits[i]
			newbits[i*2+1] = bits[i+16]
		end
		setreg(rd,implodebits(newbits,32))
	end,
	unzip = function(rd,rs1,imm)
		local bits = explodebits(getreg(rs1),32)
		local newbits = {}
		for i=0,15 do
			newbits[i] = bits[i*2]
			newbits[i+16] = bits[i*2+1]
		end
		setreg(rd,implodebits(newbits,32))
	end,
	czeroeqz = function(rd,rs1,rs2)
		if getreg(rs2) == 0 then
			setreg(rd,0)
		else
			setreg(rd,getreg(rs1))
		end
	end,
	czeronez = function(rd,rs1,rs2)
		if getreg(rs2) ~= 0 then
			setreg(rd,0)
		else
			setreg(rd,getreg(rs1))
		end
	end,
	mopr = function(rd,rs1,imm)
		setreg(rd,0)
	end,
	moprr = function(rd,rs1,rs2,imm)
		setreg(rd,0)
	end,
	cmop = function(rd,imm)
		--Does nothing until/unless redefined by another extension
	end,
	wrsnto = function(rd,rs1)
		mem.rswaiting = "nto"
		mem.running = false
		digiline_send("monitordisp","Waiting for\nreservation set")
		return false,true
	end,
	wrssto = function(rd,rs1)
		mem.rswaiting = "sto"
		return false,true
	end,
	h3bextm = function(rd,rs1,rs2,size)
		local bits = explodebits(getreg(rs1),32)
		local shamt = getreg(rs2)
		local outbits = {}
		for i=0,size-1 do
			outbits[i] = bits[i+shamt] --May be nil if out of bounds but that's OK
		end
		setreg(rd,implodebits(outbits,32))
	end,
	h3bextmi = function(rd,rs1,rs2,size)
		local bits = explodebits(getreg(rs1),32)
		local shamt = rs2 --Register number field actually contains an immediate here
		local outbits = {}
		for i=0,size-1 do
			outbits[i] = bits[i+shamt] --May be nil if out of bounds but that's OK
		end
		setreg(rd,implodebits(outbits,32))
	end,
	mret = function()
		mem.registers.pc = readcsr(0x341)
		mem.mdt = false
		mem.mie = mem.mpie
		mem.mpie = true
		mem.mpp = 3
		checkinterrupts()
		return true
	end,
	wfi = function()
		mem.interruptwaiting = true
		mem.running = false
		digiline_send("monitordisp","Waiting for\ninterrupt")
		interrupt(1,"checkinterrupt")
		return false,true
	end,
}

local function runinst(instruction)
	local bits = explodebits(instruction,32)
	local opcode = implodebits(bits,7)
	if opcode == 0x33 then
		--R-type
		--This is spectaularly inefficient and will probably need to be optimized later.
		--Really, the whole program is.
		--Focus is on making it work first...
		local f3bits = {[0] = bits[12],bits[13],bits[14]}
		local f3 = implodebits(f3bits,3)
		local f7bits = {[0] = bits[25],bits[26],bits[27],bits[28],bits[29],bits[30],bits[31]}
		local f7 = implodebits(f7bits,7)
		local rdbits = {[0] = bits[7],bits[8],bits[9],bits[10],bits[11]}
		local rd = implodebits(rdbits,5)
		local rs1bits = {[0] = bits[15],bits[16],bits[17],bits[18],bits[19]}
		local rs1 = implodebits(rs1bits,5)
		local rs2bits = {[0] = bits[20],bits[21],bits[22],bits[23],bits[24]}
		local rs2 = implodebits(rs2bits,5)
		if f3 == 0x0 and f7 == 0x0 then
			operations.add(rd,rs1,rs2)
		elseif f3 == 0x0 and f7 == 0x20 then
			operations.sub(rd,rs1,rs2)
		elseif f3 == 0x4 and f7 == 0x0 then
			operations.xor(rd,rs1,rs2)
		elseif f3 == 0x6 and f7 == 0x0 then
			operations.orr(rd,rs1,rs2)
		elseif f3 == 0x7 and f7 == 0x0 then
			operations.andr(rd,rs1,rs2)
		elseif f3 == 0x1 and f7 == 0x0 then
			operations.sll(rd,rs1,rs2)
		elseif f3 == 0x5 and f7 == 0x0 then
			operations.srl(rd,rs1,rs2)
		elseif f3 == 0x5 and f7 == 0x20 then
			operations.sra(rd,rs1,rs2)
		elseif f3 == 0x2 and f7 == 0x0 then
			operations.slt(rd,rs1,rs2)
		elseif f3 == 0x3 and f7 == 0x0 then
			operations.sltu(rd,rs1,rs2)
		elseif f3 == 0x0 and f7 == 0x1 and mem.isa.m then
			operations.mul(rd,rs1,rs2)
		elseif f3 == 0x1 and f7 == 0x1 and mem.isa.m then
			operations.mulh(rd,rs1,rs2)
		elseif f3 == 0x2 and f7 == 0x1 and mem.isa.m then
			operations.mulsu(rd,rs1,rs2)
		elseif f3 == 0x3 and f7 == 0x1 and mem.isa.m then
			operations.mulu(rd,rs1,rs2)
		elseif f3 == 0x4 and f7 == 0x1 and mem.isa.m then
			operations.div(rd,rs1,rs2)
		elseif f3 == 0x5 and f7 == 0x1 and mem.isa.m then
			operations.divu(rd,rs1,rs2)
		elseif f3 == 0x6 and f7 == 0x1 and mem.isa.m then
			operations.rem(rd,rs1,rs2)
		elseif f3 == 0x7 and f7 == 0x1 and mem.isa.m then
			operations.remu(rd,rs1,rs2)
		elseif f3 == 0x2 and f7 == 0x10 and mem.isa.b then
			operations.sh1add(rd,rs1,rs2)
		elseif f3 == 0x4 and f7 == 0x10 and mem.isa.b then
			operations.sh2add(rd,rs1,rs2)
		elseif f3 == 0x6 and f7 == 0x10 and mem.isa.b then
			operations.sh3add(rd,rs1,rs2)
		elseif f3 == 0x7 and f7 == 0x20 and mem.isa.b then
			operations.andn(rd,rs1,rs2)
		elseif f3 == 0x6 and f7 == 0x20 and mem.isa.b then
			operations.orn(rd,rs1,rs2)
		elseif f3 == 0x4 and f7 == 0x20 and mem.isa.b then
			operations.xnor(rd,rs1,rs2)
		elseif f3 == 0x6 and f7 == 0x5 then
			operations.max(rd,rs1,rs2)
		elseif f3 == 0x7 and f7 == 0x5 then
			operations.maxu(rd,rs1,rs2)
		elseif f3 == 0x4 and f7 == 0x5 then
			operations.min(rd,rs1,rs2)
		elseif f3 == 0x5 and f7 == 0x5 then
			operations.minu(rd,rs1,rs2)
		elseif f3 == 0x4 and f7 == 0x4 and rs2 == 0x0 and mem.isa.b then
			operations.zexth(rd,rs1)
		elseif f3 == 0x1 and f7 == 0x30 and mem.isa.b then
			operations.rol(rd,rs1,rs2)
		elseif f3 == 0x5 and f7 == 0x30 and mem.isa.b then
			operations.ror(rd,rs1,rs2)
		elseif f3 == 0x1 and f7 == 0x24 and mem.isa.b then
			operations.bclr(rd,rs1,rs2)
		elseif f3 == 0x5 and f7 == 0x24 and mem.isa.b then
			operations.bext(rd,rs1,rs2)
		elseif f3 == 0x1 and f7 == 0x34 and mem.isa.b then
			operations.binv(rd,rs1,rs2)
		elseif f3 == 0x1 and f7 == 0x14 and mem.isa.b then
			operations.bset(rd,rs1,rs2)
		elseif f3 == 0x2 and f7 == 0x14 and mem.isa.b then
			operations.xperm4(rd,rs1,rs2)
		elseif f3 == 0x4 and f7 == 0x14 and mem.isa.b then
			operations.xperm8(rd,rs1,rs2)
		elseif f3 == 0x4 and f7 == 0x4 then
			operations.pack(rd,rs1,rs2)
		elseif f3 == 0x7 and f7 == 0x4 then
			operations.packh(rd,rs1,rs2)
		elseif f3 == 0x5 and f7 == 0x7 then
			operations.czeroeqz(rd,rs1,rs2)
		elseif f3 == 0x7 and f7 == 0x7 then
			operations.czeronez(rd,rs1,rs2)
		else
			return trap(2)
		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()
				elseif imm == 0x105 and rd == 0 and rs1 == 0 then
					return operations.wfi()
				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
		return 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
	local timediff = os.time()-mem.starttime
	mem.csr[0xc01] = timediff%2^32

	local first = true
	repeat
		fault = nil
		if mem.registers.pc == mem.breakpoint and not first then
			digiline_send("monitordisp","Hit breakpoint")
			mem.running = false
			break
		end
		if first then
			checkinterrupts()
		end
		first = false
		local instruction = readram(mem.registers.pc,4,true)
		if 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)
			[0x304] = 0, --mie
			[0x305] = 0, --mtvec
			[0x340] = 0, --mscratch
			[0x341] = 0, --mepc
			[0x342] = 0, --mcause
			[0x343] = 0, --mtval
			[0x344] = 0, --mip
			[0x34b] = 0, --mtval2
			[0x800] = 0, --Clock Controls
			[0x801] = 0, --MMIO Base Address
			[0xc00] = 0, --CYCLE
			[0xc01] = 0, --TIME
			[0xc02] = 0, --INSTRET
			[0xc80] = 0, --CYCLEH
			[0xc81] = 0, --TIMEH
			[0xc82] = 0, --INSTRETH
	}
	for i=1,6 do mem.stdout[i] = "" end
	local mdisp = event.type == "program" and "\nReset: Cold" or "\nReset: Warm"
	digiline_send("monitordisp",mdisp.."\n\nRVController Monitor\n\nType 'help' for help\nReady\n")
	digiline_send("stdout","\n\n\n\n\n\n\n")
	mem.inputbuf = ""
	mem.digilinesqueue = {}
	mem.breakpoint = -1
	mem.hexloading = false
	mem.reservationset = nil
	mem.mbe = false
	mem.isa = {
		a = true,
		b = true,
		c = true,
		i = true,
		m = true,
	}
	mem.meseconsdir = {}
	mem.meseconsdata = {}
	port = {}
	mem.trapbase = 0
	mem.trapmode = 0
	mem.mtvecwritten = false
	mem.mie = false
	mem.mpie = true
	mem.mpp = 3
	mem.mdt = true
	mem.mtimecmplow = 0
	mem.mtimecmphigh = 0
	mem.mtimehigh = 0
	mem.interruptwaiting = false
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 <command>")
			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 <addr> <data>\nWrites the specified\nbyte (8 bits) to\nmemory at the\nspecified address")
		elseif argv[2] == "pokew" then
			digiline_send("monitordisp","pokew <addr> <data>\nWrites the specified\nword (32 bits) to\nmemory at the\nspecified address")
		elseif argv[2] == "peek" then
			digiline_send("monitordisp","peek <address>\nReads the byte (8\nbits) value from\nmemory at the\nspecified address")
		elseif argv[2] == "peekw" then
			digiline_send("monitordisp","peekw <address>\nReads the word (32\nbits) value from\nmemory at the\nspecified address")
		elseif argv[2] == "setreg" then
			digiline_send("monitordisp","setreg <reg> <data>\nWrites the specified\nword (32 bits) data\nto the specified\nregister")
		elseif argv[2] == "getreg" then
			digiline_send("monitordisp","getreg <register>\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 <value>\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 <address>\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 <address>\nReads the value of\nthe specified CSR")
		elseif argv[2] == "wrcsr" then
			digiline_send("monitordisp","wrcsr <addr> <data>\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
elseif event.iid == "checkinterrupt" and mem.interruptwaiting then
	local restarted = checkinterrupts()
	if restarted then
		mem.interruptwaiting = false
		mem.running = true
		digiline_send("monitordisp","CPU started")
		interrupt(1/CLOCK_SPEED,"tick")
	else
		interrupt(1,"checkinterrupt",mem.csr[0x800]%2 == 1)
	end
end

if stdoutdirty then
	local text = table.concat(mem.stdout,"\n")
	digiline_send("stdout",text.."\n")
	stdoutdirty = false
end
