Created
April 30, 2026 09:51
-
-
Save cheapie/fac8b1fad87bd02700b1c2e1ab05fdce to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| 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 registers = {} | |
| for i=0,31 do registers[i] = 0 end | |
| registers.pc = 0 | |
| local ram = {} | |
| local running = false | |
| local function getreg(reg) | |
| return reg == 0 and 0 or registers[reg] | |
| end | |
| local function setreg(reg,val) | |
| if val < 0 then val = val + (2^32) end | |
| if reg ~= 0 then registers[reg] = math.floor(val%(2^32)) end | |
| end | |
| local function readram(address,bytes) | |
| local out = 0 | |
| for i=0,bytes-1 do | |
| out = out + (ram[address+i] or 0) * 2^(8*i) | |
| end | |
| return out | |
| end | |
| local function writeram(address,data,bytes) | |
| for i=0,bytes-1 do | |
| local thisbyte = data % (2^((i+1)*8)) / 2^(i*8) | |
| thisbyte = math.floor(thisbyte) % 2^32 | |
| if thisbyte ~= 0 then | |
| ram[address+i] = thisbyte | |
| else | |
| --nil values are turned back into 0 on read | |
| ram[address+i] = nil | |
| end | |
| end | |
| end | |
| local function printstate() | |
| local buf = "" | |
| for i=0,31 do | |
| buf = buf..string.format("x%02d: %08X ",i,getreg(i)) | |
| if i%4 == 3 then | |
| buf = buf.."\n" | |
| end | |
| end | |
| buf = buf..string.format(" PC: %08X\n",registers.pc) | |
| print(buf) | |
| end | |
| 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, | |
| sll = function(rd,rs1,rs2) | |
| setreg(rd,getreg(rs1)*(2^getreg(rs2))) | |
| end, | |
| srl = function(rd,rs1,rs2) | |
| setreg(rd,getreg(rs1)/(2^getreg(rs2))) | |
| end, | |
| sra = function(rd,rs1,rs2) | |
| local num = getreg(rs1) | |
| local amount = getreg(rs2) | |
| 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) | |
| registers.pc = (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) | |
| registers.pc = (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) | |
| registers.pc = (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) | |
| registers.pc = (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) | |
| registers.pc = (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) | |
| registers.pc = (registers.pc + imm) % 2^32 | |
| return true | |
| end | |
| end, | |
| jal = function(rd,imm) | |
| setreg(rd,registers.pc+4) | |
| local immbits = explodebits(imm,21) | |
| signextend(immbits,21,32) | |
| imm = implodebits(immbits,32,true) | |
| registers.pc = (registers.pc + imm) % 2^32 | |
| return true | |
| end, | |
| jalr = function(rd,rs1,imm) | |
| setreg(rd,registers.pc+4) | |
| local immbits = explodebits(imm,12) | |
| signextend(immbits,12,32) | |
| imm = implodebits(immbits,32,true) | |
| registers.pc = (getreg(rs1) + imm) % 2^32 | |
| return true | |
| end, | |
| lui = function(rd,imm) | |
| setreg(rd,imm) | |
| end, | |
| auipc = function(rd,imm) | |
| setreg(rd,registers.pc+imm) | |
| end, | |
| --TODO: lb/lh/lw/lbu/lhu/sb/sh/sw (need memory first) | |
| ecall = function() | |
| --right now the only call is to print the machine state | |
| print("Software requested register dump") | |
| printstate() | |
| end, | |
| ebreak = function() | |
| --for now this just stops the processor and prints state | |
| running = false | |
| print("Hit ebreak, system halted") | |
| printstate() | |
| 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) | |
| 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 imm/0x20 == 0x0 then | |
| operations.slli(rd,rs1,imm%0x20) | |
| elseif f3 == 0x5 and imm/0x20 == 0x0 then | |
| operations.srli(rd,rs1,imm%0x20) | |
| elseif f3 == 0x5 and 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) | |
| end | |
| elseif opcode == 0x3 then | |
| --TODO: implement lb/lh/lw/lbu/lhu | |
| elseif opcode == 0x67 then | |
| if f3 == 0x0 then | |
| return operations.jalr(rd,rs1,imm) | |
| end | |
| elseif opcode == 0x73 then | |
| if imm == 0x0 then | |
| operations.ecall() | |
| elseif imm == 0x1 then | |
| operations.ebreak() | |
| 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) | |
| --TODO: implement sb/sh/sw | |
| 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) | |
| else | |
| running = false | |
| print(string.format("Invalid opcode %02X, system halted",opcode)) | |
| printstate() | |
| end | |
| end | |
| local function run() | |
| running = true | |
| repeat | |
| local instruction = readram(registers.pc,4) | |
| local jumped = runinst(instruction) | |
| if not jumped then registers.pc = (registers.pc + 4) % 2^32 end | |
| until not running | |
| end | |
| writeram(0x00,0x00100293,4) --addi x5,x0,1 | |
| writeram(0x04,0x3e800e13,4) --addi x28,x0,1000 | |
| writeram(0x08,0x006283b3,4) --loop: add x7,x5,x6 | |
| writeram(0x0c,0x00030293,4) --addi x5,x6,0 | |
| writeram(0x10,0x00038313,4) --addi x6,x7,0 | |
| writeram(0x14,0x00000073,4) --ecall | |
| writeram(0x18,0xffc3e8e3,4) --bltu x7,x28,loop | |
| writeram(0x1c,0x00100073,4) --ebreak | |
| run() | |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment