--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, RV32IMACB_Zicntr_Zicond_Zicsr_Zifencei_Zihintpause_Zilsd_Zabha_Zacas_Zcb_Zclsd_Zcmp_Zcmt_Zbkb_Zbkx instruction set, little-endian
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
* "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
* 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
* Zbkb: Extension for Bit-manipulation for Cryptography, Version 1.0.0
* Zbkx: Extension for Crossbar permutations, Version 1.0.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:

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.

]]

--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 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)
	return reg == 0 and 0 or mem.registers[reg]
end

local function setreg(reg,val)
	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)
	if address > RAM_SIZE-1 then
		digiline_send("monitordisp",string.format("Out-of-bounds\nmemory read\nAddress: %08X\nPC: %08X\nSystem halted",address,mem.registers.pc))
		mem.running = false
		return 0
	end
	local out = 0
	for i=0,bytes-1 do
		local offsetaddr = address+i
		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
	return out
end

local function writeram(address,data,bytes)
	if address > RAM_SIZE-1 then
		digiline_send("monitordisp",string.format("Out-of-bounds\nmemory write\nAddress: %08X\nPC: %08X\nSystem halted",address,mem.registers.pc))
		mem.running = false
		return
	end
	if mem.reservationset then
		if mem.reservationset + 4 >= address and mem.reservationset <= address+bytes-1 then
			mem.reservationset = nil
		end
	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 = address+i
		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

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 not mem.csr[address] then
		digiline_send("monitordisp",string.format("W: Attempted read\nfrom unknown\nCSR 0x%03X\nPC: %08X",address,mem.registers.pc))
		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, just ignore the write
		return
	elseif address == 0xf11 or address == 0xf12 or address == 0xf13 or address == 0xf14 or address == 0xf15 or address == 0x300 or address == 0x301 or address == 0x310 then
		--Read-only machine information register, ignore write
		return
	end
	if not mem.csr[address] then
		digiline_send("monitordisp",string.format("W: Attempted write\nto unknown\nCSR 0x%03X\nPC: %08X",address,mem.registers.pc))
		return
	end
	mem.csr[address] = data
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,4))
		setreg(rd+1,readram(address+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,getreg(rs2),4)
		writeram(address+4,getreg(rs2+1),4)
	end,
	ecall = function()
		local func = getreg(17)
		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()
		mem.running = false
		digiline_send("monitordisp","Hit breakpoint")
		digiline_send("montiordisp","System halted")
		digiline_send("monitordisp",string.format("PC:%08X",mem.registers.pc))
	end,
	csrrw = function(rd,rs1,imm)
		setreg(rd,readcsr(imm))
		writecsr(imm,getreg(rs1))
	end,
	csrrs = function(rd,rs1,imm)
		setreg(rd,readcsr(imm))
		if rs1 == 0 then return end
		local csrbits = explodebits(readcsr(imm),32)
		local rs1bits = explodebits(getreg(rs1),32)
		for i=0,31 do
			csrbits[i] = csrbits[i] or rs1bits[i]
		end
		writecsr(imm,implodebits(csrbits,32))
	end,
	csrrc = function(rd,rs1,imm)
		setreg(rd,readcsr(imm))
		if rs1 == 0 then return end
		local csrbits = explodebits(readcsr(imm),32)
		local rs1bits = explodebits(getreg(rs1),32)
		for i=0,31 do
			csrbits[i] = csrbits[i] and not rs1bits[i]
		end
		writecsr(imm,implodebits(csrbits,32))
	end,
	csrrwi = function(rd,rs1,imm)
		setreg(rd,readcsr(imm))
		writecsr(imm,rs1)
	end,
	csrrsi = function(rd,rs1,imm)
		setreg(rd,readcsr(imm))
		if rs1 == 0 then return end
		local csrbits = explodebits(readcsr(imm),32)
		local rs1bits = explodebits(rs1,32)
		for i=0,31 do
			csrbits[i] = csrbits[i] or rs1bits[i]
		end
		writecsr(imm,implodebits(csrbits,32))
	end,
	csrrci = function(rd,rs1,imm)
		setreg(rd,readcsr(imm))
		if rs1 == 0 then return end
		local csrbits = explodebits(readcsr(imm),32)
		local rs1bits = explodebits(rs1,32)
		for i=0,31 do
			csrbits[i] = csrbits[i] and not rs1bits[i]
		end
		writecsr(imm,implodebits(csrbits,32))
	end,
	mul = function(rd,rs1,rs2)
		local plow = mul64(getreg(rs1),getreg(rs2),false,false)
		setreg(rd,plow)
	end,
	mulh = function(rd,rs1,rs2)
		local _,phigh = mul64(getreg(rs1),getreg(rs2),true,true)
		setreg(rd,phigh)
	end,
	mulsu = function(rd,rs1,rs2)
		local _,phigh = mul64(getreg(rs1),getreg(rs2),true,false)
		setreg(rd,phigh)
	end,
	mulu = function(rd,rs1,rs2)
		local _,phigh = mul64(getreg(rs1),getreg(rs2),false,false)
		setreg(rd,phigh)
	end,
	div = function(rd,rs1,rs2)
		local a = getreg(rs1)
		local b = getreg(rs2)
		if b == 0 then
			--Division by zero
			setreg(rd,2^32-1)
			return
		end
		if a >= 2^31 then a = a - 2^32 end
		if b >= 2^31 then b = b - 2^32 end
		if a == -2^31 and b == -1 then
			--Divide overflow
			setreg(rd,a)
		end
		local result = math.floor(a/b)
		if result < 0 then result = result + 2^32 end
		setreg(rd,result)
	end,
	rem = function(rd,rs1,rs2)
		local a = getreg(rs1)
		local b = getreg(rs2)
		if b == 0 then
			--Division by zero
			setreg(rd,a)
			return
		end
		if a >= 2^31 then a = a - 2^32 end
		if b >= 2^31 then b = b - 2^32 end
		if a == -2^31 and b == -1 then
			--Divide overflow
			setreg(rd,0)
		end
		local result = a%b
		setreg(rd,result)
	end,
	divu = function(rd,rs1,rs2)
		local a = getreg(rs1)
		local b = getreg(rs2)
		if b == 0 then
			--Division by zero
			setreg(rd,2^32-1)
			return
		end
		local result = math.floor(a/b)
		setreg(rd,result)
	end,
	remu = function(rd,rs1,rs2)
		local a = getreg(rs1)
		local b = getreg(rs2)
		if b == 0 then
			--Division by zero
			setreg(rd,a)
			return
		end
		local result = a%b
		setreg(rd,result)
	end,
	sh1add = function(rd,rs1,rs2)
		setreg(rd,getreg(rs1)*2+getreg(rs2))
	end,
	sh2add = function(rd,rs1,rs2)
		setreg(rd,getreg(rs1)*4+getreg(rs2))
	end,
	sh3add = function(rd,rs1,rs2)
		setreg(rd,getreg(rs1)*8+getreg(rs2))
	end,
	clz = function(rd,rs1)
		local bits = explodebits(getreg(rs1),32)
		for i=31,0,-1 do
			if bits[i] then
				setreg(rd,31-i)
				return
			end
		end
		setreg(rd,32)
	end,
	ctz = function(rd,rs1)
		local bits = explodebits(getreg(rs1),32)
		for i=0,31 do
			if bits[i] then
				setreg(rd,i)
				return
			end
		end
		setreg(rd,32)
	end,
	cpop = function(rd,rs1)
		local bits = explodebits(getreg(rs1),32)
		local out = 0
		for i=0,31 do
			if bits[i] then
				out = out + 1
			end
		end
		setreg(rd,out)
	end,
	maxu = function(rd,rs1,rs2)
		setreg(rd,math.max(getreg(rs1),getreg(rs2)))
	end,
	minu = function(rd,rs1,rs2)
		setreg(rd,math.min(getreg(rs1),getreg(rs2)))
	end,
	max = function(rd,rs1,rs2)
		local a = getreg(rs1)
		local b = getreg(rs2)
		if a >= 2^31 then a = a - 2^32 end
		if b >= 2^31 then b = b - 2^32 end
		local out = math.max(a,b)
		if out < 0 then out = out + 2^32 end
		setreg(rd,out)
	end,
	min = function(rd,rs1,rs2)
		local a = getreg(rs1)
		local b = getreg(rs2)
		if a >= 2^31 then a = a - 2^32 end
		if b >= 2^31 then b = b - 2^32 end
		local out = math.min(a,b)
		if out < 0 then out = out + 2^32 end
		setreg(rd,out)
	end,
	sextb = function(rd,rs1)
		local bits = explodebits(getreg(rs1),8)
		signextend(bits,8,32)
		setreg(rd,implodebits(bits,32))
	end,
	sexth = function(rd,rs1)
		local bits = explodebits(getreg(rs1),16)
		signextend(bits,16,32)
		setreg(rd,implodebits(bits,32))
	end,
	zexth = function(rd,rs1)
		setreg(rd,getreg(rs1)%2^16)
	end,
	rol = function(rd,rs1,rs2)
		local num = getreg(rs1)
		local amount = getreg(rs2)%32
		if amount == 0 then return end
		for i=1,amount do
			local endbit = num >= 2^31
			num = (num*2)%2^32
			if endbit then num = num + 1 end
		end
		setreg(rd,num)
	end,
	ror = function(rd,rs1,rs2)
		local num = getreg(rs1)
		local amount = getreg(rs2)%32
		if amount == 0 then
			setreg(rd,num)
			return
		end
		for _=1,amount do
			local endbit = num%2 == 1
			num = math.floor(num/2)
			if endbit then num = num + 2^31 end
		end
		setreg(rd,num)
	end,
	rori = function(rd,rs1,imm)
		local num = getreg(rs1)
		local amount = imm%32
		if amount == 0 then
			setreg(rd,num)
			return
		end
		for _=1,amount do
			local endbit = num%2 == 1
			num = math.floor(num/2)
			if endbit then num = num + 2^31 end
		end
		setreg(rd,num)
	end,
	orcb = function(rd,rs1)
		local input = getreg(rs1)
		local b1 = input%2^8 > 0
		local b2 = math.floor(input/2^8)%2^8 > 0
		local b3 = math.floor(input/2^16)%2^8 > 0
		local b4 = math.floor(input/2^24)%2^8 > 0
		local out = 0
		if b1 then out = out + 0xff end
		if b2 then out = out + 0xff00 end
		if b3 then out = out + 0xff0000 end
		if b4 then out = out + 0xff000000 end
		setreg(rd,out)
	end,
	rev8 = function(rd,rs1)
		local input = getreg(rs1)
		local b1 = input%2^8
		local b2 = math.floor(input/2^8)%2^8
		local b3 = math.floor(input/2^16)%2^8
		local b4 = math.floor(input/2^24)%2^8
		local out = b4
		out = out + (b3*2^8)
		out = out + (b2*2^16)
		out = out + (b1*2^24)
		setreg(rd,out)
	end,
	bclr = function(rd,rs1,rs2)
		local bits = explodebits(getreg(rs1),32)
		bits[getreg(rs2)%32] = false
		setreg(rd,implodebits(bits,32))
	end,
	bset = function(rd,rs1,rs2)
		local bits = explodebits(getreg(rs1),32)
		bits[getreg(rs2)%32] = true
		setreg(rd,implodebits(bits,32))
	end,
	binv = function(rd,rs1,rs2)
		local bits = explodebits(getreg(rs1),32)
		bits[getreg(rs2)%32] = not bits[getreg(rs2)%32]
		setreg(rd,implodebits(bits,32))
	end,
	bclri = function(rd,rs1,imm)
		local bits = explodebits(getreg(rs1),32)
		bits[imm%32] = false
		setreg(rd,implodebits(bits,32))
	end,
	bseti = function(rd,rs1,imm)
		local bits = explodebits(getreg(rs1),32)
		bits[imm%32] = true
		setreg(rd,implodebits(bits,32))
	end,
	binvi = function(rd,rs1,imm)
		local bits = explodebits(getreg(rs1),32)
		bits[imm%32] = not bits[imm%32]
		setreg(rd,implodebits(bits,32))
	end,
	bext = function(rd,rs1,rs2)
		local bit = explodebits(getreg(rs1),32)[getreg(rs2)%32]
		setreg(rd,bit and 1 or 0)
	end,
	bexti = function(rd,rs1,imm)
		local bit = explodebits(getreg(rs1),32)[imm%32]
		setreg(rd,bit and 1 or 0)
	end,
	xperm8 = function(rd,rs1,rs2)
		local a = getreg(rs1)
		local outputs = {
			[0] = a%2^8,
			math.floor(a/2^8)%2^8,
			math.floor(a/2^16)%2^8,
			math.floor(a/2^24)%2^8,
		}
		local b = getreg(rs2)
		local selections = {
			[0] = b%2^8,
			math.floor(b/2^8)%2^8,
			math.floor(b/2^16)%2^8,
			math.floor(b/2^24)%2^8,
		}
		local out = 0
		for i=0,3 do
			local num = outputs[selections[i]] or 0
			out = out+(num*2^(i*8))
		end
		setreg(rd,out)
	end,
	xperm4 = function(rd,rs1,rs2)
		local a = getreg(rs1)
		local outputs = {
			[0] = a%2^4,
			math.floor(a/2^4)%2^4,
			math.floor(a/2^8)%2^4,
			math.floor(a/2^12)%2^4,
			math.floor(a/2^16)%2^4,
			math.floor(a/2^20)%2^4,
			math.floor(a/2^24)%2^4,
			math.floor(a/2^28)%2^4,
		}
		local b = getreg(rs2)
		local selections = {
			[0] = b%2^4,
			math.floor(b/2^4)%2^4,
			math.floor(b/2^8)%2^4,
			math.floor(b/2^12)%2^4,
			math.floor(b/2^16)%2^4,
			math.floor(b/2^20)%2^4,
			math.floor(b/2^24)%2^4,
			math.floor(b/2^28)%2^4,
		}
		local out = 0
		for i=0,7 do
			local num = outputs[selections[i]] or 0
			out = out+(num*2^(i*4))
		end
		setreg(rd,out)
	end,
	lr = function(rd,rs1,rs2)
		local address = getreg(rs1)
		setreg(rd,readram(address,4))
		mem.reservationset = address
	end,
	sc = function(rd,rs1,rs2)
		local address = getreg(rs1)
		if mem.reservationset == address then
			writeram(address,getreg(rs2),4)
			setreg(rd,0)
		else
			setreg(rd,1)
		end
		mem.reservationset = nil
	end,
	amoswapw = function(rd,rs1,rs2)
		local address = getreg(rs1)
		local newdata = getreg(rs2)
		setreg(rd,readram(address,4))
		writeram(address,newdata,4)
	end,
	amoaddw = function(rd,rs1,rs2)
		local address = getreg(rs1)
		local data = readram(address,4)
		local newdata = getreg(rs2)
		setreg(rd,data)
		writeram(address,(data+newdata)%2^32,4)
	end,
	amoandw = function(rd,rs1,rs2)
		local address = getreg(rs1)
		local data = readram(address,4)
		local newdata = getreg(rs2)
		setreg(rd,data)
		local bits = explodebits(data,32)
		local newbits = explodebits(newdata,32)
		for i=0,31 do bits[i] = bits[i] and newbits[i] end
		writeram(address,implodebits(bits,32),4)
	end,
	amoorw = function(rd,rs1,rs2)
		local address = getreg(rs1)
		local data = readram(address,4)
		local newdata = getreg(rs2)
		setreg(rd,data)
		local bits = explodebits(data,32)
		local newbits = explodebits(newdata,32)
		for i=0,31 do bits[i] = bits[i] or newbits[i] end
		writeram(address,implodebits(bits,32),4)
	end,
	amoxorw = function(rd,rs1,rs2)
		local address = getreg(rs1)
		local data = readram(address,4)
		local newdata = getreg(rs2)
		setreg(rd,data)
		local bits = explodebits(data,32)
		local newbits = explodebits(newdata,32)
		for i=0,31 do bits[i] = bits[i] ~= newbits[i] end
		writeram(address,implodebits(bits,32),4)
	end,
	amomaxw = function(rd,rs1,rs2)
		local address = getreg(rs1)
		local data = readram(address,4)
		local newdata = getreg(rs2)
		setreg(rd,data)
		if data > 2^31 then data = data - 2^32 end
		if newdata > 2^31 then newdata = newdata - 2^32 end
		newdata = math.max(data,newdata)
		if newdata < 0 then newdata = newdata + 2^32 end
		writeram(address,newdata,4)
	end,
	amomaxuw = function(rd,rs1,rs2)
		local address = getreg(rs1)
		local data = readram(address,4)
		local newdata = getreg(rs2)
		setreg(rd,data)
		newdata = math.max(data,newdata)
		writeram(address,newdata,4)
	end,
	amominw = function(rd,rs1,rs2)
		local address = getreg(rs1)
		local data = readram(address,4)
		local newdata = getreg(rs2)
		setreg(rd,data)
		if data > 2^31 then data = data - 2^32 end
		if newdata > 2^31 then newdata = newdata - 2^32 end
		newdata = math.min(data,newdata)
		if newdata < 0 then newdata = newdata + 2^32 end
		writeram(address,newdata,4)
	end,
	amominuw = function(rd,rs1,rs2)
		local address = getreg(rs1)
		local data = readram(address,4)
		local newdata = getreg(rs2)
		setreg(rd,data)
		newdata = math.min(data,newdata)
		writeram(address,newdata,4)
	end,
	amocasw = function(rd,rs1,rs2)
		local address = getreg(rs1)
		local data = readram(address,4)
		local compare = getreg(rd)
		if data == compare then
			writeram(address,getreg(rs2),4)
		end
		setreg(rd,data)
	end,
	amocasd = function(rd,rs1,rs2)
		if rd == 31 then return end
		if rs2 == 31 then return end
		local address = getreg(rs1)
		local datalow = readram(address,4)
		local datahigh = readram(address+4,4)
		local comparelow = getreg(rd)
		local comparehigh = rd == 0 and 0 or getreg(rd+1)
		if datalow == comparelow and datahigh == comparehigh then
			writeram(address,getreg(rs2),4)
			writeram(address+4,rs2 == 0 and 0 or getreg(rs2+1),4)
		end
		if rd ~= 0 then
			setreg(rd,datalow)
			setreg(rd+1,datahigh)
		end
	end,
	amoswaph = function(rd,rs1,rs2)
		local address = getreg(rs1)
		local newdata = getreg(rs2)%2^16
		local olddata = explodebits(readram(address,2),16)
		signextend(olddata,16,32)
		setreg(rd,implodebits(olddata,32))
		writeram(address,newdata,2)
	end,
	amoaddh = function(rd,rs1,rs2)
		local address = getreg(rs1)
		local data = readram(address,2)
		local newdata = getreg(rs2)%2^16
		local oldbits = explodebits(data,16)
		signextend(oldbits,16,32)
		setreg(rd,implodebits(oldbits,32))
		writeram(address,(data+newdata)%2^16,2)
	end,
	amoandh = function(rd,rs1,rs2)
		local address = getreg(rs1)
		local data = readram(address,2)
		local newdata = getreg(rs2)%2^16
		local bits = explodebits(data,16)
		signextend(bits,16,32)
		setreg(rd,implodebits(bits,32))
		local newbits = explodebits(newdata,16)
		for i=0,15 do bits[i] = bits[i] and newbits[i] end
		writeram(address,implodebits(bits,16),2)
	end,
	amoorh = function(rd,rs1,rs2)
		local address = getreg(rs1)
		local data = readram(address,2)
		local newdata = getreg(rs2)%2^16
		local bits = explodebits(data,16)
		signextend(bits,16,32)
		setreg(rd,implodebits(bits,32))
		local newbits = explodebits(newdata,16)
		for i=0,15 do bits[i] = bits[i] or newbits[i] end
		writeram(address,implodebits(bits,16),2)
	end,
	amoxorh = function(rd,rs1,rs2)
		local address = getreg(rs1)
		local data = readram(address,2)
		local newdata = getreg(rs2)%2^16
		local bits = explodebits(data,16)
		signextend(bits,16,32)
		setreg(rd,implodebits(bits,32))
		local newbits = explodebits(newdata,16)
		for i=0,15 do bits[i] = bits[i] ~= newbits[i] end
		writeram(address,implodebits(bits,16),2)
	end,
	amomaxh = function(rd,rs1,rs2)
		local address = getreg(rs1)
		local data = readram(address,2)
		local newdata = getreg(rs2)%2^16
		local bits = explodebits(data,16)
		signextend(bits,16,32)
		setreg(rd,implodebits(bits,32))
		if data > 2^15 then data = data - 2^16 end
		if newdata > 2^15 then newdata = newdata - 2^16 end
		newdata = math.max(data,newdata)
		if newdata < 0 then newdata = newdata + 2^16 end
		writeram(address,newdata,2)
	end,
	amomaxuh = function(rd,rs1,rs2)
		local address = getreg(rs1)
		local data = readram(address,2)
		local newdata = getreg(rs2)%2^16
		local bits = explodebits(data,16)
		signextend(bits,16,32)
		setreg(rd,implodebits(bits,32))
		newdata = math.max(data,newdata)
		writeram(address,newdata,2)
	end,
	amominh = function(rd,rs1,rs2)
		local address = getreg(rs1)
		local data = readram(address,2)
		local newdata = getreg(rs2)%2^16
		local bits = explodebits(data,16)
		signextend(bits,16,32)
		setreg(rd,implodebits(bits,32))
		if data > 2^15 then data = data - 2^16 end
		if newdata > 2^15 then newdata = newdata - 2^16 end
		newdata = math.min(data,newdata)
		if newdata < 0 then newdata = newdata + 2^16 end
		writeram(address,newdata,2)
	end,
	amominuh = function(rd,rs1,rs2)
		local address = getreg(rs1)
		local data = readram(address,2)
		local newdata = getreg(rs2)%2^16
		local bits = explodebits(data,16)
		signextend(bits,16,32)
		setreg(rd,implodebits(bits,32))
		newdata = math.min(data,newdata)
		writeram(address,newdata,2)
	end,
	amocash = function(rd,rs1,rs2)
		local address = getreg(rs1)
		local data = readram(address,2)
		local compare = getreg(rd)%2^16
		if data == compare then
			writeram(address,getreg(rs2)%2^16,2)
		end
		local bits = explodebits(data,16)
		signextend(bits,16,32)
		setreg(rd,implodebits(bits,32))
	end,
	amoswapb = function(rd,rs1,rs2)
		local address = getreg(rs1)
		local newdata = getreg(rs2)%2^8
		local olddata = explodebits(readram(address,1),8)
		signextend(olddata,8,32)
		setreg(rd,implodebits(olddata,32))
		writeram(address,newdata,1)
	end,
	amoaddb = function(rd,rs1,rs2)
		local address = getreg(rs1)
		local data = readram(address,1)
		local newdata = getreg(rs2)%2^8
		local oldbits = explodebits(data,8)
		signextend(oldbits,8,32)
		setreg(rd,implodebits(oldbits,32))
		writeram(address,(data+newdata)%2^8,1)
	end,
	amoandb = function(rd,rs1,rs2)
		local address = getreg(rs1)
		local data = readram(address,1)
		local newdata = getreg(rs2)%2^8
		local bits = explodebits(data,8)
		signextend(bits,8,32)
		setreg(rd,implodebits(bits,32))
		local newbits = explodebits(newdata,8)
		for i=0,7 do bits[i] = bits[i] and newbits[i] end
		writeram(address,implodebits(bits,8),1)
	end,
	amoorb = function(rd,rs1,rs2)
		local address = getreg(rs1)
		local data = readram(address,1)
		local newdata = getreg(rs2)%2^8
		local bits = explodebits(data,8)
		signextend(bits,8,32)
		setreg(rd,implodebits(bits,32))
		local newbits = explodebits(newdata,8)
		for i=0,7 do bits[i] = bits[i] or newbits[i] end
		writeram(address,implodebits(bits,8),1)
	end,
	amoxorb = function(rd,rs1,rs2)
		local address = getreg(rs1)
		local data = readram(address,1)
		local newdata = getreg(rs2)%2^8
		local bits = explodebits(data,8)
		signextend(bits,8,32)
		setreg(rd,implodebits(bits,32))
		local newbits = explodebits(newdata,8)
		for i=0,7 do bits[i] = bits[i] ~= newbits[i] end
		writeram(address,implodebits(bits,8),1)
	end,
	amomaxb = function(rd,rs1,rs2)
		local address = getreg(rs1)
		local data = readram(address,1)
		local newdata = getreg(rs2)%2^8
		local bits = explodebits(data,8)
		signextend(bits,8,32)
		setreg(rd,implodebits(bits,32))
		if data > 2^7 then data = data - 2^8 end
		if newdata > 2^7 then newdata = newdata - 2^8 end
		newdata = math.max(data,newdata)
		if newdata < 0 then newdata = newdata + 2^8 end
		writeram(address,newdata,1)
	end,
	amomaxub = function(rd,rs1,rs2)
		local address = getreg(rs1)
		local data = readram(address,1)
		local newdata = getreg(rs2)%2^8
		local bits = explodebits(data,8)
		signextend(bits,8,32)
		setreg(rd,implodebits(bits,32))
		newdata = math.max(data,newdata)
		writeram(address,newdata,1)
	end,
	amominb = function(rd,rs1,rs2)
		local address = getreg(rs1)
		local data = readram(address,1)
		local newdata = getreg(rs2)%2^8
		local bits = explodebits(data,8)
		signextend(bits,8,32)
		setreg(rd,implodebits(bits,32))
		if data > 2^7 then data = data - 2^8 end
		if newdata > 2^7 then newdata = newdata - 2^8 end
		newdata = math.min(data,newdata)
		if newdata < 0 then newdata = newdata + 2^8 end
		writeram(address,newdata,1)
	end,
	amominub = function(rd,rs1,rs2)
		local address = getreg(rs1)
		local data = readram(address,1)
		local newdata = getreg(rs2)%2^8
		local bits = explodebits(data,8)
		signextend(bits,8,32)
		setreg(rd,implodebits(bits,8))
		newdata = math.min(data,newdata)
		writeram(address,newdata,1)
	end,
	amocasb = function(rd,rs1,rs2)
		local address = getreg(rs1)
		local data = readram(address,1)
		local compare = getreg(rd)%2^8
		if data == compare then
			writeram(address,getreg(rs2)%2^8,1)
		end
		local bits = explodebits(data,8)
		signextend(bits,8,32)
		setreg(rd,implodebits(bits,32))
	end,
	pack = function(rd,rs1,rs2)
		local lowhalf = getreg(rs1)%2^16
		local highhalf = (getreg(rs2)%2^16)*2^16
		setreg(rd,lowhalf+highhalf)
	end,
	packh = function(rd,rs1,rs2)
		local lowhalf = getreg(rs1)%2^8
		local highhalf = (getreg(rs2)%2^8)*2^8
		setreg(rd,lowhalf+highhalf)
	end,
	brev8 = function(rd,rs1,imm)
		local bits = explodebits(getreg(rs1),32)
		local newbits = {}
		for byte=0,3 do
			for bit=0,7 do
				local source = byte*8+bit
				local destination = byte*8+(7-bit)
				newbits[destination] = bits[source]
			end
		end
		setreg(rd,implodebits(newbits,32))
	end,
	zip = function(rd,rs1,imm)
		local bits = explodebits(getreg(rs1),32)
		local newbits = {}
		for i=0,15 do
			newbits[i*2] = bits[i]
			newbits[i*2+1] = bits[i+16]
		end
		setreg(rd,implodebits(newbits,32))
	end,
	unzip = function(rd,rs1,imm)
		local bits = explodebits(getreg(rs1),32)
		local newbits = {}
		for i=0,15 do
			newbits[i] = bits[i*2]
			newbits[i+16] = bits[i*2+1]
		end
		setreg(rd,implodebits(newbits,32))
	end,
	czeroeqz = function(rd,rs1,rs2)
		if getreg(rs2) == 0 then
			setreg(rd,0)
		else
			setreg(rd,getreg(rs1))
		end
	end,
	czeronez = function(rd,rs1,rs2)
		if getreg(rs2) ~= 0 then
			setreg(rd,0)
		else
			setreg(rd,getreg(rs1))
		end
	end,
}

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 then
			operations.mul(rd,rs1,rs2)
		elseif f3 == 0x1 and f7 == 0x1 then
			operations.mulh(rd,rs1,rs2)
		elseif f3 == 0x2 and f7 == 0x1 then
			operations.mulsu(rd,rs1,rs2)
		elseif f3 == 0x3 and f7 == 0x1 then
			operations.mulu(rd,rs1,rs2)
		elseif f3 == 0x4 and f7 == 0x1 then
			operations.div(rd,rs1,rs2)
		elseif f3 == 0x5 and f7 == 0x1 then
			operations.divu(rd,rs1,rs2)
		elseif f3 == 0x6 and f7 == 0x1 then
			operations.rem(rd,rs1,rs2)
		elseif f3 == 0x7 and f7 == 0x1 then
			operations.remu(rd,rs1,rs2)
		elseif f3 == 0x2 and f7 == 0x10 then
			operations.sh1add(rd,rs1,rs2)
		elseif f3 == 0x4 and f7 == 0x10 then
			operations.sh2add(rd,rs1,rs2)
		elseif f3 == 0x6 and f7 == 0x10 then
			operations.sh3add(rd,rs1,rs2)
		elseif f3 == 0x7 and f7 == 0x20 then
			operations.andn(rd,rs1,rs2)
		elseif f3 == 0x6 and f7 == 0x20 then
			operations.orn(rd,rs1,rs2)
		elseif f3 == 0x4 and f7 == 0x20 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 then
			operations.zexth(rd,rs1)
		elseif f3 == 0x1 and f7 == 0x30 then
			operations.rol(rd,rs1,rs2)
		elseif f3 == 0x5 and f7 == 0x30 then
			operations.ror(rd,rs1,rs2)
		elseif f3 == 0x1 and f7 == 0x24 then
			operations.bclr(rd,rs1,rs2)
		elseif f3 == 0x5 and f7 == 0x24 then
			operations.bext(rd,rs1,rs2)
		elseif f3 == 0x1 and f7 == 0x34 then
			operations.binv(rd,rs1,rs2)
		elseif f3 == 0x1 and f7 == 0x14 then
			operations.bset(rd,rs1,rs2)
		elseif f3 == 0x2 and f7 == 0x14 then
			operations.xperm4(rd,rs1,rs2)
		elseif f3 == 0x4 and f7 == 0x14 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)
		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 then
				operations.clz(rd,rs1)
			elseif f3 == 0x1 and imm == 0x601 then
				operations.ctz(rd,rs1)
			elseif f3 == 0x1 and imm == 0x602 then
				operations.cpop(rd,rs1)
			elseif f3 == 0x1 and imm == 0x604 then
				operations.sextb(rd,rs1)
			elseif f3 == 0x1 and imm == 0x605 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 then
				operations.orcb(rd,rs1)
			elseif f3 == 0x5 and imm == 0x698 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)
			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)
			end
		elseif opcode == 0x67 then
			if f3 == 0x0 then
				return operations.jalr(rd,rs1,imm)
			end
		elseif opcode == 0x73 then
			if f3 == 0x0 then
				if imm == 0x0 then
					operations.ecall()
				elseif imm == 0x1 then
					operations.ebreak()
				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 == 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)
			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)
		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)
		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)
		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 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)
			end
		elseif f3 == 3 then
			if f5 == 5 then
				--amocas.d
				operations.amocasd(rd,rs1,rs2)
			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)
			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)
			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
	else
		mem.running = false
		digiline_send("monitordisp",string.format("Invalid opcode %02X,\nhalted",opcode))
	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)
			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 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)
		else
			--c.lui (CI)
			signextend(immbits,6,20)
			local imm = implodebits(immbits,20)
			imm = imm*2^12
			operations.lui(rd,imm)
		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 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 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 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 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
		mem.running = false
		digiline_send("monitordisp","Invalid compressed\ninstruction, halted")
	end
end

local function run(limit)
	--0xc00 = CYCLE
	--0xc80 = CYCLEH
	mem.csr[0xc00] = mem.csr[0xc00] + 1
	if mem.csr[0xc00] >= 2^32 then
		mem.csr[0xc80] = mem.csr[0xc80] + math.floor(mem.csr[0xc00]/2^32)
		mem.csr[0xc00] = mem.csr[0xc00]%2^32
	end

	--0xc01 = TIME
	--0xc81 = TIMEH
	local timediff = os.time()-mem.starttime
	mem.csr[0xc01] = timediff%2^32
	mem.csr[0xc81] = math.floor(timediff/2^32)

	local first = true
	repeat
		if mem.registers.pc == mem.breakpoint and not first then
			digiline_send("monitordisp","Hit breakpoint")
			mem.running = false
			break
		end
		first = false
		local instruction = readram(mem.registers.pc,4)
		if instruction%4 == 3 then
			local jumped,stop = runinst(instruction)
			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 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,
}

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.stdout = {}
	mem.starttime = os.time()
	mem.csr = {
			[0x017] = 0, --jvt
			[0xf11] = 0, --mvendorid
			[0xf12] = 0, --marchid
			[0xf13] = 0x6f435652, --mimpid ("RVCo")
			[0xf14] = 0, --mhartid
			[0x300] = 0, --mstatus
			[0x301] = 0x40001107, --misa (RV32IMACB)
			[0x310] = 0, --mstatush
			[0x800] = 0, --Lightweight Mode
			[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
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,RAM_SIZE-1)
		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,RAM_SIZE-1)
		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,RAM_SIZE-1)
		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,RAM_SIZE-1)
		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
		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
		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] == "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")
		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")
		else
			digiline_send("monitordisp","No such command or\nno help available")
		end
	else
		digiline_send("monitordisp","Unknown command")
	end
elseif event.channel == "stdin" then
	if mem.inputwaiting then
		--Blocking read in progress
		if mem.inputwaittype == "string" then
			event.msg = string.sub(event.msg,1,mem.inputmax-1)..string.char(0)
			for i=1,string.len(event.msg) do
				writeram(mem.inputaddr+i-1,string.byte(string.sub(event.msg,i,i)),1)
			end
			mem.inputwaiting = false
			mem.running = true
			digiline_send("monitordisp","CPU started")
			run(INSTRUCTIONS_PER_CLOCK)
			interrupt(1/CLOCK_SPEED,"tick")
		elseif mem.inputwaittype == "integer" and tonumber(event.msg) then
			event.msg = math.floor(event.msg)
			if event.msg >= 2^32 then event.msg = 2^32-1 end
			if event.msg < -1*(2^31) then event.msg = -1*(2^31) end
			if event.msg < 0 then event.msg = event.msg + 2^32 end
			setreg(10,event.msg)
			mem.inputwaiting = false
			mem.running = true
			digiline_send("monitordisp","CPU started")
			run(INSTRUCTIONS_PER_CLOCK)
			interrupt(1/CLOCK_SPEED,"tick")
		elseif mem.inputwaittype == "char" then
			event.msg = string.sub(event.msg,1,1)
			if event.msg == "" then event.msg = string.char(0) end
			setreg(10,string.byte(event.msg))
			mem.inputwaiting = false
			mem.running = true
			digiline_send("monitordisp","CPU started")
			run(INSTRUCTIONS_PER_CLOCK)
			interrupt(1/CLOCK_SPEED,"tick")
		end
	else
		--No blocking read in progress, buffer the data for nonblocking reads later
		mem.inputbuf = string.sub(mem.inputbuf..event.msg,1,256)
	end
elseif event.channel == "load" then
	if event.msg.done then
		mem.hexloading = false
		digiline_send("monitordisp","Load complete\n")
		if event.msg.autorun then
			mem.running = true
			digiline_send("monitordisp","CPU started\n")
			run(INSTRUCTIONS_PER_CLOCK)
			interrupt(1/CLOCK_SPEED,"tick")
		end
	else
		if not mem.hexloading then
			if mem.running then digiline_send("monitordisp","System halted\n") end
			mem.running = false
			mem.hexloading = true
			digiline_send("monitordisp","Loading Intel\nHEX data")
			digiline_send("stdout","\n\n\n\n\n\n\n")
			mem.registers.pc = 0
		end
		local address = event.msg.address
		local data = event.msg.data
		local size = event.msg.size
		digiline_send("monitordisp",string.format("%08X\n",address))
		for i=0,size-1 do
			local thisdata = tonumber(string.sub(data,i*2+1,i*2+2),16)
			local thisaddress = address + i
			writeram(thisaddress,thisdata,1)
		end
	end
elseif event.type == "digiline" then
	--Unrecognized digilines signals get forwarded on to the program
	event.channel = string.sub(event.channel,1,256)
	event.msg = string.sub(tostring(event.msg),1,256)
	if #mem.digilinesqueue < 16 then
		table.insert(mem.digilinesqueue,{channel = event.channel, msg = event.msg})
	end
	if mem.csr[0x800]%2 == 1 then
		interrupt(0,"tick")
	end
elseif event.iid == "tick" and mem.running then
	run(INSTRUCTIONS_PER_CLOCK)
	if mem.csr[0x800]%2 == 1 then
		interrupt(1,"tick",true)
	else
		interrupt(1/CLOCK_SPEED,"tick")
	end
end

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