--[[ load-prog.lua blah 2024 This is 2 Lua scripts, originally written for thl, combined into one file for convenience and modified to run in Golly. The first is asm.lua, which takes a .323 (assembly source code) file and produces a save-state (.3ss) file. The second is load-state.lua, which loads that save-state and writes it to the machine itself. ]] local g = golly() asmFilename=g.opendialog("Load 323 assembly programme", "323 asm (*.323)|*.323") --g.warn(asmFilename) -- The original scripts were written for Lua 5.2, with its bit32 functions. -- Golly uses Lua 5.4, which doesn't have those, so I have to re-implement them. bit32 = {} function bit32.extract(x, bit) return (x >> bit) & 1 end function bit32.band(x, y) return x & y end function bit32.bor(x, y) return x | y end function bit32.lshift(x, bits) return x << bits end function bit32.rshift(x, bits) return x >> bits end function bit32.rrotate(x, bits) bits = bits % 32 return (x >> bits) | (x << (32 - bits)) end function bit32.lrotate(x, bits) bits = bits % 32 return (x << bits) | (x >> (32 - bits)) end -- asm.lua --------------------------------------------------------------------- -- Generate .3ss data from assembly. --[[ INFORMAL GRAMMAR {} is just grouping # is a comment ?, +, and [] are as in Lua regexes | is OR . is any char programme ::= line* line ::= eline comment # if I add strings, they may contain ';'... eline ::= {label ':'}? {'\t' instruction|command WHITESPACE*}? command ::= 'rep' WHITESPACE+ integer ' ' unsigned-integer command ::= 'align' WHITESPACE+ unsigned-integer # val must be >=2 instruction ::= opcode-menmonic-rrr ' ' register ',' register ',' register instruction ::= opcode-menmonic-rr ' ' register ',' register instruction ::= opcode-menmonic-ir ' ' immediate ',' register instruction ::= 'hlt' register ::= 'x' [0-9A-F] immediate ::= label | integer label ::= '@'? [a-zA-Z_][a-zA-Z_0-9]* # up to 8 chars integer ::= '-'? {'0x'[0-9a-fA-F]+} | {[0-9]+} comment ::= ';' .* ]] function asmErr(s) error("ERROR: "..fileName..":"..lineNo..": "..s, 0) end -- ASSEMBLY -------------------------------------------------------------------- function argType(s) if s:match("^x[0-9A-F]$") then return "register" elseif s:match("^[@a-zA-Z_]") then return "label" elseif s:match("^%-?[0-9]") then return "number" else return "unknown" end end function split(s) -- split on whitespace, return array s = s:gsub("%s+", " ") s = s:gsub("^ ", "") s = s:gsub(" $", "") local ret = {} for elem in s:gmatch("%S+") do ret[#ret + 1] = elem end return ret end --[[ The assembler is 2-pass. The first pass produces object code, in the form of the "obj" table, with the following format. Each element of the table is either a number, representing a hword, a label prefixed with "1" or "2" to represent how many hwords it consumes, or a label definition, which ends in a colon. "obj" is in general based on hwords, not words. ]] function emit(out) obj[#obj + 1] = out if type(out) == "number" then -- straight data addrAt = addrAt + 1 elseif out:match("^1") then -- hword label use addrAt = addrAt + 1 elseif out:match("^2") then -- word label use addrAt = addrAt + 2 elseif out:match(":$") then -- label defined here syms[out:sub(1,-1)] = addrAt end end --[[ The instruction set is encoded with: 1. The numeric representation of the opcode hword with its argument spaces unfilled. 2. A string that has an R for a register operand, or an I for an immediate one. 3. A series of numbers indicating the nybble into which the corresponding argument should be placed (if it is a register argument), or its size in hwords (if it in an immediate argument). Nybbles are numbered left-to-right starting at 1; e.g. nybble 1 is the most significant 4 bits. Sometimes multiple instructions use the same mnemonic. In this case, they are all placed in a sub-array. ]] instruction_set = { ["add"] = {0x0000, "RRR", 2, 3, 4}, ["sub"] = {0x1000, "RRR", 3, 2, 4}, ["mul"] = {0x2000, "RRR", 2, 3, 4}, ["div"] = {0x3000, "RRR", 2, 3, 4}, ["mod"] = {0x4000, "RRR", 2, 3, 4}, ["shf"] = {0x7000, "RRR", 2, 3, 4}, ["rot"] = {0x8000, "RRR", 2, 3, 4}, ["sha"] = {0x9000, "RRR", 2, 3, 4}, ["or"] = {0xb000, "RRR", 2, 3, 4}, ["and"] = {0xc000, "RRR", 2, 3, 4}, ["xor"] = {0xd000, "RRR", 2, 3, 4}, ["in"] = {0xe300, "RR", 3, 4}, ["out"] = {0xa001, "RR", 2, 3}, ["st"] = {0xa000, "RR", 3, 2}, ["ld"] = {{0xe100, "RR", 3, 4}, {0xe010, "IR", 2, 4}}, ["jmp"] = {{0xe000, "I", 1}, {0xe200, "R", 3}}, ["jnf"] = {{0xe001, "I", 1}, {0xe201, "R", 3}}, ["jf"] = {{0xe002, "I", 1}, {0xe202, "R", 3}}, ["hlt"] = {0xeeee, ""} } function handlePseudoImmediates(instr) if instruction_set[instr[1]][2] == "RRR" then if instr[4] and instr[4]:sub(1,1) == "!" then asmErr("Incorrect use of pseudo-immediates.") end elseif instr[1] == "in" or instr[1] == "out" or instr[1] == "st" then -- allow 0, 1, or 2 pseudo-imms -- no checking needed elseif instr[1] == "ld" then if instr[3] and instr[3]:sub(1,1) == "!" then asmErr("Second argument of ld cannot be pseudo-".. "immediate.") end else for i = 2, #instr do if instr[i]:sub(1,1) == "!" then asmErr("Pseudo-immediates cannot be used for "..instr[1] .." instructions.") end end end local numPIs = 0 for i = 2, #instr do if instr[i]:sub(1,1) == "!" then emit(0xe01e - numPIs) emit("2"..instr[i]:sub(2)) instr[i] = string.format("x%X", 0xe - numPIs) numPIs = numPIs + 1 end end end function parseInstruction(instr) instr = instr:gsub(",", " , ") instr = split(instr) -- instr should consist of an opcode mnemonic, followed by REG or IMM -- arguments with commas between them. if not instruction_set[instr[1]] then asmErr("Unrecognised opcode mnemonic '"..instr[1].."'") end -- we will ensure that commas are properly placed, and then remove them: for i = 2, #instr, 2 do if instr[i] == "," or (instr[i + 1] ~= "," and instr[i + 1] ~= nil) then asmErr("Malformed instruction.") end end for i = 1, #instr do if instr[i] == "," then table.remove(instr, i) end end local opm = instruction_set[instr[1]] if type(opm[1]) ~= "table" then opm = {opm} -- for simplicity; so we can always use a for loop end for _, op in ipairs(opm) do -- for all instructions with this mnemonic if #op[2] ~= #instr - 1 then goto next end isMatch = true for i = 1, #op[2] do -- confirm all operands are correct local type if argType(instr[i + 1]) == "register" or instr[i + 1]:sub(1,1) == "!" then type = "R" else type = "I" end if type ~= op[2]:sub(i,i) then isMatch = false break end end if isMatch then handlePseudoImmediates(instr) -- now we finally output an instruction: emitInstruction(instr, op) return end ::next:: end asmErr("Invalid operands to '"..instr[1].."'") end function emitInstruction(instr, prot) local opcodeHword = prot[1] for i = 1, #prot[2] do -- handle register operands first if prot[2]:sub(i,i) == "R" then local operand = tonumber(instr[i + 1]:sub(2,2), 16) local shiftedOperand = bit32.lshift(operand, (4-prot[i+2])*4) opcodeHword = bit32.bor(opcodeHword, shiftedOperand) end end emit(opcodeHword) for i = 1, #prot[2] do -- then immediates if prot[2]:sub(i,i) == "I" then emit(tostring(prot[i + 2])..instr[i + 1]) end end end local strLitChars = "ABCDEFGHIJKLMNOPQRSTUVWXYZ,.;:~0123456789()[]?!<>/\\+*%^'£_- |nt" function parseLine(line) line = line:gsub(";.*", "") -- remove comment, if present local lbl = line:match("%a%w*:") local cmd = line:sub(#(lbl or "") + 1) if lbl then emit(lbl) end if cmd:match("^\t?align ") then local n = cmd:gmatch("align%s+(%S+)%s*$")() if not n then asmErr("Invalid arguments to 'align'.") end n = parseUnsignedVal(n) if n < 2 then asmErr("Argument to align must be at least 2.") end for i = 1, n - ((addrAt - 1) % n) - 1 do emit(0) end elseif cmd:match("^\t?d[hw] ") then local val = cmd:gmatch("d[hw]%s+(%S+)%s*$")() if not val then asmErr("Invalid argument to dh/dw.") end local size if cmd:match("^\t?dh ") then -- hword (dh) size = "1" else -- word (dw) size = "2" end emit(size .. val) elseif cmd:match("^\t?rep ") then local val, amt = cmd:gmatch("rep%s+(%S+)%s+(%S+)%s*$")() if (not val) or (not amt) then asmErr("Invalid arguments to 'rep'.") end amt = parseUnsignedVal(amt) for i = 1, amt do emit("2"..val) end elseif cmd:match("^\t?str ") then -- A character is 6 bits; 5 of them are packed in a word, with -- the high 2 bits zeroed. The characters progress from the LSB -- towards the MSB. parseLine("align 2") --local str = cmd:gmatch('^\t?str (.*)')() local str = cmd:gmatch('\t?str (.*)')() -- for now, only handle chars [A-Z] while #str > 0 do local w = 0 for i=1, math.min(5, #str) do local charCode = strLitChars:find(str:sub(i,i)) if charCode == nil then asmErr("Character '"..str:sub(i,i).. "' is not supported in strings.") end -- bitwise operations are wrong and immoral w = w + charCode * 2^(6*(i-1)) end -- Lua 5.4 puts a ".0" at the end here, probably to -- indicate that it became a float via some conversion. -- We need to remove it. if string.sub(tostring(w), -2) == ".0" then w = string.sub(tostring(w), 1, -3) end emit("2"..w) str = str:sub(6) end elseif cmd:match("%S") then -- if an instruction is present parseInstruction(cmd) end end function asm2obj(s) -- generate object code from plaintext asm obj = {} syms = {} -- symbol table addrAt = 256 lineNo = 1 --for line in s:gmatch("[^\n\r]+") do for line in s:gmatch("[^\n]*\n") do parseLine(line:sub(1,-2)) lineNo = lineNo + 1 end end function printObj() print("Object code:") for i, v in ipairs(obj) do local stringed if type(v) == "number" then stringed = "0x" .. string.format("%04x", v) else stringed = v end print(i, stringed) end end function printSyms() print("Symbol table contents:") for k, v in pairs(syms) do print(k, v) end end -- LINKING --------------------------------------------------------------------- function parseUnsignedVal(v) -- values are all <=32-bit, and Lua uses 64-bit doubles with 48-bit -- mantissas, so it can always fit as a number. if v:sub(1,2) == "0x" then -- hexadecimal if #v > 10 then error("Hexadecimal value '"..v.."' too long.") end if v:match("^0x.*[^0-9a-fA-F]") then error("Invalid character in hexadecimal value '" ..v.."'.") end return tonumber(v:sub(3), 16) elseif v:match("^[0-9]+$") then -- decimal return tonumber(v, 10) else error("'"..v.."' is not a valid unsigned number.") end end function parseInteger(v) if v:sub(1,1) == "-" then -- The AND here is because Lua 5.4 bitwise operations are 64 -- bit, whilst the Lua 5.2 ones are 32-bit. return (-parseUnsignedVal(v:sub(2))) & 0xffffffff else return parseUnsignedVal(v) end end function resolveLabel(lbl) if lbl:sub(1,1) == "@" then lbl = lbl:sub(2) if not syms[lbl..":"] then error("Label "..lbl.." is undefined.") end return bit32.rshift(syms[lbl..":"], 1) else if not syms[lbl..":"] then -- somewhat repetitive, but oh well error("Label "..lbl.." is undefined.") end return syms[lbl..":"] end end function parseImmediate(imm) local typ = argType(imm) if typ == "label" then return resolveLabel(imm) else return parseInteger(imm) end end function link() -- apply symbol table for i, v in ipairs(obj) do if type(v) == "string" then if v:sub(-1) == ":" then goto next end local len = tonumber(v:sub(1,1)) local val = parseImmediate(v:sub(2)) if len == 1 then -- JMP IMM (hword addr) if val >= 0x10000 then error("Value "..v:sub(2).. " too large to be hword") end outputHword(val) else -- LDI outputHword(bit32.band(val, 0xffff)) outputHword(bit32.rshift(val, 16)) end else outputHword(v) end ::next:: end outputHword(0) -- just to be sure the last hword gets flushed end function outputHword(h) -- buffer to produce 2 hwords at a time if not outHw then outHw = h else ram[#ram + 1] = bit32.bor(outHw, bit32.lshift(h,16)) outHw = nil end end function outb(x) -- output number x as byte ss = ss .. string.char(x) -- inefficient; use towers of hanoi algorithm? end function outw(x) -- output number x as word outb(bit32.band(x, 255)) outb(bit32.band(bit32.rshift(x, 8), 255)) outb(bit32.band(bit32.rshift(x, 16), 255)) outb(bit32.band(bit32.rshift(x, 24), 255)) end -- Run assembler. file, e = io.open(asmFilename) if not file then error(e) end s = file:read("*a") if not s then error("Can't read file :(.") end ram = {} -- 2 pass assembler. Each pass is a function: asm2obj(s) link() -- Trim 0s at the end. for i = #ram, 1, -1 do if ram[i] == 0 then ram[i] = nil else break end end -- now generate the .3ss output: ss = "323" -- header outb(23) outb(0) -- flag outw(syms["start:"] or 256) -- pc for i = 1, 15 do -- registers outw(0) end for i = 1, 256 do -- cache data outb(0) end for i = 1, 4 do -- cache addrs outw(0) end outb(0xe4) -- cache order (3210, can't just be 0) outw(#ram + 128) -- ram size (+128 since col. 0 is included) for i = 1, 128 do -- fill out col. 0 outw(0) end for i = 1, #ram do -- ram outw(ram[i]) end -- load-state.lua -------------------------------------------------------------- -- For this to make sense, size should be a power of 2. I could've just used -- depths, but then we'd be doing a bunch of pointless exponentiations. function getNode(rle, size) return {cells = g.parse(rle), size = size} end function setNode(x, y, n, mode) if type(n) ~= "table" then g.setcell(x - 2^18, y - 2^18 + 1, n) else -- Convert from thl to Golly coordinates. x, y = x*n.size - 2^18, y*n.size - 2^18 + 1 -- Write. if n == blank then g.select{x, y, 8, 8} g.clear(0) g.select{} else mode = mode or "copy" if mode == "andnot" then mode = "not" end -- ??????? g.putcells(n.cells, x, y, 1, 0, 0, 1, mode) end end end -- NODES (CONVERTED FROM THL TO GOLLY STYLE) blank = getNode("!", 8) gLWSSLeft = getNode("3$7bo$7bo$7bo$7bo!", 8) -- green gLWSSRight = getNode("3$2o$2bo3$obo!", 8) oLWSSLeft = getNode("5bobo3$5bo$6b2o!", 8) -- orange oLWSSRight = getNode("$o$o$o$o!", 8) rLWSSTop = getNode("6$bo2bo$5bo!", 8) -- red rLWSSBot = getNode("bo3bo$2b4o!", 8) dlGliderTop = getNode("7$3bo!", 8) -- down-left-travelling glider dlGliderBot = getNode("2bo$2b3o!", 8) bLWSSTop = getNode("5$4bo2bo$3bo$3bo3bo!", 8) -- blue bLWSSBot = getNode("3b4o!", 8) urGlider = getNode("3$4b2o$5b2o$4bo!", 8) -- up-right-travelling glider ulGlider = getNode("3$5b2o$5bobo$5bo!", 8) -- up-left-travelling glider --[[ -- the rLWSSTop location overlaps the bottom of a glider gun -- save the relevant parts to copy it back: ggClipFull = getNode([[[M2] (manual) .*....*$...*$....**$$$$.*..*$.....*$ ]-]) ggClip1 = ggClipFull.a ggClip2 = ggClipFull.b ]] ggClip = getNode("bo4bo$3bo$4b2o4$bo2bo$5bo!", 8) function r(x, y) -- place right-facing LWSS setNode(x,y,rLWSSTop) setNode(x,y+1,rLWSSBot) end function b(x, y) -- place left-facing LWSS setNode(x,y,bLWSSTop) setNode(x,y+1,bLWSSBot) end function dl(x, y) setNode(x,y,dlGliderTop) setNode(x,y+1,dlGliderBot) end function clear2(x, y) setNode(x,y,blank) setNode(x,y+1,blank) end function bitTest(x, bitPosition) return bit32.extract(x, 31 - bitPosition) -- set() needs them backwards end function reset(x, y) -- prepare for writing by erasing gliders and LWSSes -- note that this does not actually set the stored value to 0 for i = 0, 8 do clear2(x+23 + i*4, y) end for i = 0, 6 do clear2(x+54 - i*2, y+2 + i*2) end for i = 0, 10 do clear2(x + i*4, y+16) end for i = 0, 6 do setNode(x+11 + i*2, y+16 - i*2, blank) end setNode(x+24, y+2, blank) end function set(x, y, bits) for i = 0, 8 do if bitTest(bits, i) == 1 then r(x+23 + i*4, y) end end setNode(x+27, y, ggClip, "or") --setNode((x+27)*2, y*2, ggClip1) --setNode((x+27)*2 + 1, y*2, ggClip2) for i = 0, 6 do if bitTest(bits, i + 9) == 0 then dl(x+54 - i*2, y+2 + i*2) end end for i = 0, 7 do if bitTest(bits, i + 16) == 1 then b(x + 40 - i*4, y+16) end end for i = 0, 6 do if bitTest(bits, i + 24) == 0 then setNode(x+11 + i*2, y+16 - i*2, urGlider) end end if bitTest(bits, 31) == 0 then setNode(x+24, y+2, ulGlider) end end -- Load state from .3ss file. -------------------------------------------------- if ss:sub(1,4) ~= "323" .. string.char(23) then error("Invalid header.") end if #ss < 346 then error(".3ss too small.") end ind = 4 -- Index into .3ss data. Starts at 0. We use these functions to read: function readByte() ind = ind + 1 return ss:byte(ind) end function readWord() local ret = readByte() ret = bit32.bor(ret, bit32.lshift(readByte(), 8)) ret = bit32.bor(ret, bit32.lshift(readByte(), 16)) ret = bit32.bor(ret, bit32.lshift(readByte(), 24)) return ret end flag = readByte() pc = readWord() regs = {} for i = 1, 15 do regs[i] = readWord() end cache = {} for i = 1, 4 do cache[i] = {} for j = 1, 16 do cache[i][j] = readWord() end end cacheAddrs = {} for i = 1, 4 do cacheAddrs[i] = readWord() end cacheOrderByte = readByte() cacheOrder = {} for i = 0, 3 do cacheOrder[i + 1] = bit32.band(3, bit32.rshift(cacheOrderByte, i*2)) -- cacheOrder[1] is most recently used. end ramSize = readWord() if ramSize < (#ss - 346)/4 then error("Invalid RAM size; possible truncated file.") end if ramSize > 128 * 64 then error("RAM size too big.") end ram = {} -- Skip over column 0. ramSize = ramSize - 128 ind = ind + 512 -- If ramSize is <=0, for loops will just run for 0 iterations. for i = 1, ramSize do ram[i] = readWord() end -- Uncomment this if you want to clear the upper areas of RAM. --[[ for i = ramSize + 1, 128 * 64 do ram[i] = 0 end ]] --ramSize = ramSize + 512 -- Write state into machine. --------------------------------------------------- -- Write flag. setNode(21405, 31702, flag) setNode(21403, 31703, flag) setNode(21405, 31703, flag) setNode(21404, 31704, flag) setNode(21405, 31704, flag) flag = (flag + 1) % 2 setNode(23652, 31700, flag) setNode(23651, 31701, flag) setNode(23651, 31702, flag) setNode(23652, 31702, flag) setNode(23653, 31702, flag) --[[for i = 1, 4 do -- SPONGE cacheAddrs[i] = 0xfff00f end]] -- Write PC. for i = 0, 13 do -- PC in the actual machine only stores 14 useful bits. if bit32.extract(pc, i) == 1 then setNode(3135 - i*4, 3415, rLWSSTop) setNode(3135 - i*4, 3416, rLWSSBot) else setNode(3135 - i*4, 3415, blank) setNode(3135 - i*4, 3416, blank) end end -- Write cache addresses into selectors. -- Cache line 3. for i = 0, 8 do -- Cache address size is normal PC addr. size minus 5. if bit32.extract(cacheAddrs[2], i + 5) == 1 then setNode(3508 + i*4, 2839, bLWSSTop) setNode(3508 + i*4, 2840, bLWSSBot) else setNode(3508 + i*4, 2839, blank) setNode(3508 + i*4, 2840, blank) end end if cacheAddrs[1] == 0 then -- Make null cache lines un-hittable. setNode(3508 - 4, 2839, bLWSSTop) setNode(3508 - 4, 2840, bLWSSBot) else setNode(3508 - 4, 2839, blank) setNode(3508 - 4, 2840, blank) end -- Cache line 1. for i = 0, 4 do if bit32.extract(cacheAddrs[3], i + 5) == 1 then setNode(3463 - i*4, 2951, blank) setNode(3463 - i*4, 2952, blank) else setNode(3463 - i*4, 2951, rLWSSTop) setNode(3463 - i*4, 2952, rLWSSBot) end end if cacheAddrs[1] == 0 then -- Make null cache lines un-hittable. setNode(3463 + 4, 2951, blank) setNode(3463 + 4, 2952, blank) else setNode(3463 + 4, 2951, rLWSSTop) setNode(3463 + 4, 2952, rLWSSBot) end --setNode(3464 - 4*4,2951, blank) if bit32.extract(cacheAddrs[3], 9) == 1 then setNode(27584, 23615, 1) else setNode(27584, 23615, 0) end for i = 0, 3 do if bit32.extract(cacheAddrs[3], i + 10) == 1 then setNode(3447, 2949 - i*4, oLWSSLeft) if i == 1 then for j = 0, 3 do setNode(27584, 23561 + j, 1) end else setNode(3448, 2949 - i*4, oLWSSRight) end else setNode(3447, 2949 - i*4, blank) if i == 1 then for j = 0, 3 do setNode(27584, 23561 + j, 0) end else setNode(3448, 2949 - i*4, blank) end end end -- Cache line 2. for i = 0, 8 do if bit32.extract(cacheAddrs[1], i + 5) == 0 then setNode(3527 - i*4, 2759, rLWSSTop) setNode(3527 - i*4, 2760, rLWSSBot) else setNode(3527 - i*4, 2759, blank) setNode(3527 - i*4, 2760, blank) end end if cacheAddrs[1] ~= 0 then -- Make null cache lines un-hittable. setNode(3527 + 4, 2759, rLWSSTop) setNode(3527 + 4, 2760, rLWSSBot) else setNode(3527 + 4, 2759, blank) setNode(3527 + 4, 2760, blank) end -- Cache line 0. if bit32.extract(cacheAddrs[4], 5) == 1 then setNode(3555, 3046, gLWSSLeft) setNode(3556, 3046, gLWSSRight) else setNode(3555, 3046, blank) setNode(3556, 3046, blank) end if cacheAddrs[4] == 0 then setNode(3555, 3046 - 4, gLWSSLeft) setNode(3556, 3046 - 4, gLWSSRight) else setNode(3555, 3046 - 4, blank) setNode(3556, 3046 - 4, blank) end if bit32.extract(cacheAddrs[4], 6) == 1 then -- draw a glider setNode(28434, 24383, 1) setNode(28436, 24383, 1) setNode(28436, 24384, 1) setNode(28435, 24384, 1) setNode(28435, 24385, 1) else setNode(28434, 24383, 0) setNode(28436, 24383, 0) setNode(28436, 24384, 0) setNode(28435, 24384, 0) setNode(28435, 24385, 0) end for i = 0, 6 do if bit32.extract(cacheAddrs[4], i + 7) == 1 then setNode(3551 - i*4, 3047, blank) setNode(3551 - i*4, 3048, blank) else setNode(3551 - i*4, 3047, rLWSSTop) setNode(3551 - i*4, 3048, rLWSSBot) end end -- Write cache. --[[for i = 0, 3 do for j = 1, 16 do -- SPONGE cache[4-i][j] = ram[i*16 + j] or 0 end end]] function rotateTable(tab, n) if n < 0 then n = #tab + n end if n < 0 or n > #tab then error("n out of bounds for tab.") end local prefix = {} for i = 1, n do prefix[i] = tab[i] end for i = 1, #tab - n do tab[i] = tab[i + n] end for i = 1, #prefix do tab[i + #tab - n] = prefix[i] end end cache = {cache[4], cache[3], cache[2], cache[1]} cacheRots = {64, -12, 0, 354} for cacheLine = 1, 4 do local o = (cacheLine - 3) * 7 * 2^4 -- vertical offset between £ lines local p = o * 8 -- offset for cells local bitTable = {} for i = 1, 16 do local word = cache[cacheLine][i] for j = 0, 31 do bitTable[#bitTable + 1] = bit32.extract(word, j) end end rotateTable(bitTable, cacheRots[cacheLine]) for i = 0, 99 do if bitTable[i + 1] == 1 then setNode(4884 + i*4, 2359+o, bLWSSTop) setNode(4884 + i*4, 2360+o, bLWSSBot) else setNode(4884 + i*4, 2359+o, blank) setNode(4884 + i*4, 2360+o, blank) end end if bitTable[100] == 0 then -- For save-state.lua. setNode(42248, 19780, 1) else setNode(42248, 19780, 0) end for i = 0, 15 do if bitTable[i + 101] == 1 then setNode(5279, 2362 + i*4 + o, gLWSSLeft, "or") setNode(5280, 2362 + i*4 + o, gLWSSRight, "or") else setNode(5279, 2362 + i*4 + o, gLWSSLeft, "andnot") setNode(5280, 2362 + i*4 + o, gLWSSRight, "andnot") end end if bitTable[117] == 1 then setNode(42226, 19391+p, 1) setNode(42228, 19391+p, 1) setNode(42227, 19392+p, 1) setNode(42228, 19392+p, 1) setNode(42227, 19393+p, 1) else setNode(42226, 19391+p, 0) setNode(42228, 19391+p, 0) setNode(42227, 19392+p, 0) setNode(42228, 19392+p, 0) setNode(42227, 19393+p, 0) end for i = 0, 112 do if bitTable[i + 118] == 0 then setNode(5275 - i*4, 2423+o, rLWSSTop, "or") setNode(5275 - i*4, 2424+o, rLWSSBot, "or") else setNode(5275 - i*4, 2423+o, rLWSSTop, "andnot") setNode(5275 - i*4, 2424+o, rLWSSBot, "andnot") end end if bitTable[231] == 0 then setNode(5275 - 113*4, 2423+o, rLWSSTop) setNode(5275 - 113*4, 2424+o, rLWSSBot) else setNode(5275 - 113*4, 2423+o, blank) setNode(5275 - 113*4, 2424+o, blank) end for i = 0, 3 do if bitTable[i + 232] == 1 then setNode(4823, 2421 - i*4 + o, oLWSSLeft, "or") setNode(4824, 2421 - i*4 + o, oLWSSRight, "or") else setNode(4823, 2421 - i*4 + o, oLWSSLeft, "andnot") setNode(4824, 2421 - i*4 + o, oLWSSRight, "andnot") end end if bitTable[236] == 0 then setNode(38602, 19264+p, 1) setNode(38603, 19264+p, 1) setNode(38604, 19264+p, 1) setNode(38602, 19265+p, 1) setNode(38603, 19266+p, 1) else setNode(38602, 19264+p, 0) setNode(38603, 19264+p, 0) setNode(38604, 19264+p, 0) setNode(38602, 19265+p, 0) setNode(38603, 19266+p, 0) end for i = 0, 122 do if bitTable[i + 237] == 1 then setNode(4828 + i*4, 2407+o, bLWSSTop, "or") setNode(4828 + i*4, 2408+o, bLWSSBot, "or") else setNode(4828 + i*4, 2407+o, bLWSSTop, "andnot") setNode(4828 + i*4, 2408+o, bLWSSBot, "andnot") end end if bitTable[360] == 1 then setNode(4828 + 123*4, 2407+o, bLWSSTop) setNode(4828 + 123*4, 2408+o, bLWSSBot) else setNode(4828 + 123*4, 2407+o, blank) setNode(4828 + 123*4, 2408+o, blank) end for i = 0, 7 do if bitTable[i + 361] == 1 then setNode(5319, 2405 - i*4 + o, oLWSSLeft, "or") setNode(5320, 2405 - i*4 + o, oLWSSRight, "or") else setNode(5319, 2405 - i*4 + o, oLWSSLeft, "andnot") setNode(5320, 2405 - i*4 + o, oLWSSRight, "andnot") end end if bitTable[369] == 0 then tmp = 1 else tmp = 0 end setNode(42547, 19008+p, tmp) setNode(42548, 19008+p, tmp) setNode(42549, 19008+p, tmp) setNode(42549, 19009+p, tmp) setNode(42548, 19010+p, tmp) for i = 0, 122 do if bitTable[i + 370] == 1 then setNode(5315 - i*4, 2375+o, rLWSSTop, "or") setNode(5315 - i*4, 2376+o, rLWSSBot, "or") else setNode(5315 - i*4, 2375+o, rLWSSTop, "andnot") setNode(5315 - i*4, 2376+o, rLWSSBot, "andnot") end end --AAAAAAAAAAAAAAUUIAEUHTUAEIHH GUIGYAERILGAERG --AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA GUIAERHGEGUIAEHLIUAEG if bitTable[493] == 1 then setNode(4823, 2375+o, rLWSSTop) setNode(4823, 2376+o, rLWSSBot) else setNode(4823, 2375+o, blank) setNode(4823, 2376+o, blank) end for i = 0, 3 do if bitTable[i + 494] == 1 then setNode(4823, 2373 - i*4 + o, oLWSSLeft) setNode(4824, 2373 - i*4 + o, oLWSSRight) else setNode(4823, 2373 - i*4 + o, blank) setNode(4824, 2373 - i*4 + o, blank) end end if bitTable[498] == 0 then tmp = 1 else tmp = 0 end setNode(38602, 18880+p, tmp) setNode(38603, 18880+p, tmp) setNode(38604, 18880+p, tmp) setNode(38602, 18881+p, tmp) setNode(38603, 18882+p, tmp) for i = 0, 13 do if bitTable[i + 499] == 1 then setNode(4828 + i*4, 2359+o, bLWSSTop) setNode(4828 + i*4, 2360+o, bLWSSBot) else setNode(4828 + i*4, 2359+o, blank) setNode(4828 + i*4, 2360+o, blank) end end end -- Write 2BA (cache order). local xoff = 2^4 * 11 -- x offset between 2BA columns (adjusted to d3 nodes). local yoff = 2^4 * 9 for i = 0, 3 do -- the 4 2-bit indices for j = 0, 1 do -- for each bit local bit = bit32.band(1, bit32.rshift(cacheOrder[i + 1], 1-j)) if bit == 1 then setNode(5792 - i * xoff, 1911 - j * yoff, blank) setNode(5792 - i * xoff, 1912 - j * yoff, blank) setNode(5767 - i * xoff, 1926 - j * yoff, gLWSSLeft) setNode(5768 - i * xoff, 1926 - j * yoff, gLWSSRight) else setNode(5767 - i * xoff, 1926 - j * yoff, blank) setNode(5768 - i * xoff, 1926 - j * yoff, blank) setNode(5792 - i * xoff, 1911 - j * yoff, bLWSSTop) setNode(5792 - i * xoff, 1912 - j * yoff, bLWSSBot) end end end -- Write registers (GPRs). for i = 1, 15 do reset(3116, 4447 - (i-1)*32) set(3116, 4447 - (i-1)*32, bit32.rrotate(regs[i], 8 + i*8)) end -- Write RAM. for i = 0, ramSize-1, 128 do local colSize if i + 128 <= #ram then colSize = 127 else colSize = (#ram % 128) - 1 end for j = 0, colSize do local word = ram[i + j + 1] local yPos = 4031 + (127-j)*32 local m local a if i < 4*128 then -- first few columns are further apart m = 8 a = 0 else m = 4 a = 2048 - 512 end reset(4588+2048+i*m+a, yPos) set(4588+2048+i*m+a, yPos, bit32.lrotate(word, 11 + j*8)) end end