Last active
September 11, 2024 16:36
-
-
Save MCJack123/8e1451fd34986f7cef1eb20af872a420 to your computer and use it in GitHub Desktop.
Dawn emulation for CraftOS-PC Accelerated
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 args = {...} | |
| if args[1] == nil then error("Usage: subleq <dawn.bin>") end | |
| if term.drawPixels == nil then error("This program requires CraftOS-PC v2.2 or later.") end | |
| local ok, err = pcall(function() | |
| if _G.dawn_chunks == nil then | |
| local file = fs.open(args[1], "rb") | |
| if file == nil then error("Could not open file at " .. args[1]) end | |
| _G.dawn_chunks = {} | |
| for i = 1, 640 do dawn_chunks[i] = file.read(1048576) end | |
| file.close() | |
| end | |
| local function bswap(n) return (n % 256) * 0x1000000 + (math.floor(n / 0x100) % 256) * 0x10000 + (math.floor(n / 0x10000) % 256) * 0x100 + math.floor(n / 0x1000000) % 256 end | |
| local resX, resY = term.getSize(2) | |
| local mem, mem32 | |
| if ffi then | |
| mem = ffi.new("uint8_t[536870912]") | |
| mem32 = { | |
| [0] = ffi.cast("uint32_t *", mem), | |
| [1] = ffi.cast("uint32_t *", mem+1), | |
| [2] = ffi.cast("uint32_t *", mem+2), | |
| [3] = ffi.cast("uint32_t *", mem+3) | |
| } | |
| for i = 1, 256 do ffi.copy(mem + (i-1)*1048576, dawn_chunks[i], 1048576) end | |
| else | |
| mem = setmetatable({}, {__index = function(self, idx) | |
| if idx < 256*1048576 then return dawn_chunks[math.floor(idx / 1048576)+1]:byte(idx % 1048576 + 1) end | |
| return 0 | |
| end}) | |
| end | |
| local addresses = { | |
| processor_name = 335413288, | |
| clock_execution_window = 334364664, | |
| processor_registers = 334364672, | |
| vga_registers = 335540096, | |
| key_register = 335542256, | |
| mouse_registers = 335542176, | |
| time_register = 335544304, | |
| io_registers = 334361440, | |
| framebuffer = 268435456 | |
| } | |
| local keymap = { | |
| [keys.home] = 2, | |
| [keys["end"]] = 3, | |
| [keys.backspace] = 8, | |
| [keys.pageUp] = 9, | |
| [keys.pageDown] = 10, | |
| [keys.enter] = 13, | |
| [keys.up] = 14, | |
| [keys.left] = 15, | |
| [keys.down] = 16, | |
| [keys.right] = 17, | |
| [keys.delete] = 29, | |
| [keys.f5] = 1 | |
| } | |
| local dirtyRect = {minX = 0, minY = 0, maxX = resX, maxY = resY} | |
| local function read32x2(addr) | |
| if addr > 640*1048576 then error("Address out of range: " .. addr) end | |
| if mem32 then | |
| local a, o = math.floor(addr / 4), addr % 4 | |
| return bswap(mem32[o][a]), bswap(mem32[o][a + 1]) | |
| end | |
| return (mem[addr] * 0x1000000) + (mem[addr+1] * 0x10000) + (mem[addr+2] * 0x100) + mem[addr+3], (mem[addr+4] * 0x1000000) + (mem[addr+5] * 0x10000) + (mem[addr+6] * 0x100) + mem[addr+7] | |
| end | |
| local function write32x2(addr, hi, lo) | |
| if addr > 640*1048576 then error("Address out of range: " .. addr) end | |
| if addr >= addresses.framebuffer and addr < addresses.framebuffer + resY*resX*4 then | |
| local x, y = ((addr - addresses.framebuffer) / 4) % resX, math.floor(((addr - addresses.framebuffer) / 4) / resX) | |
| dirtyRect.minX = math.min(dirtyRect.minX, x) | |
| dirtyRect.minY = math.min(dirtyRect.minY, y) | |
| dirtyRect.maxX = math.max(dirtyRect.maxX, x) | |
| dirtyRect.maxY = math.max(dirtyRect.maxY, y) | |
| end | |
| if mem32 then | |
| local a, o = math.floor(addr / 4), addr % 4 | |
| mem32[o][a] = bswap(hi) | |
| mem32[o][a + 1] = bswap(lo) | |
| return | |
| end | |
| mem[addr] = bit32.band(bit32.rshift(hi, 24), 0xFF) | |
| mem[addr+1] = bit32.band(bit32.rshift(hi, 16), 0xFF) | |
| mem[addr+2] = bit32.band(bit32.rshift(hi, 8), 0xFF) | |
| mem[addr+3] = bit32.band(hi, 0xFF) | |
| mem[addr+4] = bit32.band(bit32.rshift(lo, 24), 0xFF) | |
| mem[addr+5] = bit32.band(bit32.rshift(lo, 16), 0xFF) | |
| mem[addr+6] = bit32.band(bit32.rshift(lo, 8), 0xFF) | |
| mem[addr+7] = bit32.band(lo, 0xFF) | |
| end | |
| local function strcpy(addr, str) | |
| if ffi then ffi.copy(mem + addr, str) else | |
| for i = 1, #str do mem[addr+i-1] = str:byte(i) end | |
| mem[addr+#str] = 0 | |
| end | |
| end | |
| local function subleq(ip, a, b, c) | |
| local borrow = 0 | |
| local ah, al = read32x2(a) | |
| local bh, bl = read32x2(b) | |
| bl = bl - al | |
| if bl < 0 then | |
| bl = bl + 0x100000000 | |
| borrow = 1 | |
| end | |
| bh = bh - ah - borrow | |
| write32x2(b, bh, bl) | |
| if bit32.btest(bh, 0x80000000) or (bh == 0 and bl == 0) then return c | |
| else return ip + 24 end | |
| end | |
| --local flip = false | |
| local function render() | |
| --local time = os.epoch("utc") | |
| write32x2(addresses.vga_registers, 0, resX) | |
| write32x2(addresses.vga_registers + 8, 0, resY) | |
| write32x2(addresses.vga_registers + 16, 0, 32) | |
| if dirtyRect.minX > dirtyRect.maxX or dirtyRect.minY > dirtyRect.maxY then return end | |
| local buf = {} | |
| local stride = resX | |
| local i = 1 | |
| for y = dirtyRect.minY, dirtyRect.maxY + 1 do | |
| buf[i] = "" | |
| for x = dirtyRect.minX, dirtyRect.maxX + 1 do | |
| buf[i] = buf[i] .. string.char(bit32.band(mem[addresses.framebuffer + ((y-1)*stride+x)*4], 0xE0) + bit32.band(bit32.rshift(mem[addresses.framebuffer + ((y-1)*stride+x)*4 + 1], 3), 0x1C) + bit32.rshift(mem[addresses.framebuffer + ((y-1)*stride+x)*4 + 2], 6)) | |
| end | |
| i=i+1 | |
| end | |
| term.drawPixels(dirtyRect.minX, dirtyRect.minY, buf) | |
| dirtyRect = {minX = math.huge, minY = math.huge, maxX = 0, maxY = 0} | |
| --term.setPixel(0, 0, flip and 255 or 0) | |
| --flip = not flip | |
| --print("Render took " .. (os.epoch("utc") - time) .. " ms") | |
| end | |
| local vsync_counter = {0xFFFFFFFF, 0xFFFFFFFF} | |
| local skipped_frames = 10000 | |
| local total_instructions = 0 | |
| local function cpuloop(ip) | |
| ip = ip or 0 | |
| local ticks = 0 | |
| --local debugger = peripheral.find("debugger") or {print = print} | |
| while true do | |
| local _, a = read32x2(ip) | |
| local _, b = read32x2(ip + 8) | |
| local _, c = read32x2(ip + 16) | |
| ip = subleq(ip, a, b, c) | |
| total_instructions = total_instructions + 1 | |
| ticks = ticks + 1 | |
| if ticks >= 163840 then | |
| --debugger.print("Total instructions: " .. total_instructions) | |
| local vsh, vsl = read32x2(addresses.vga_registers) | |
| if vsh ~= vsync_counter[1] or vsl ~= vsync_counter[2] or skipped_frames > 30 then | |
| vsync_counter = {vsh, vsl} | |
| skipped_frames = 0 | |
| render() | |
| else skipped_frames = skipped_frames + 1 end | |
| local sth, stl = read32x2(addresses.processor_registers) | |
| if sth == 0 and stl == 8 then break end | |
| local iofh, iofl = read32x2(addresses.io_registers + 16) | |
| if iofh == 0 and iofl == 2 then | |
| local addrh, addrl = read32x2(addresses.io_registers + 8) | |
| if addrh < 0 or addrl <= 640*1048576 then | |
| write32x2(addresses.io_registers, 0xFFFFFFFF, 0xFFFFFFFF) | |
| write32x2(addresses.io_registers + 16, 0, 3) | |
| else | |
| write32x2(addresses.io_registers, 0, dawn_chunks[math.floor(idx / 1048576)+1]:byte(idx % 1048576 + 1)) | |
| write32x2(addresses.io_registers + 16, 0, 3) | |
| end | |
| end | |
| ticks = 0 | |
| write32x2(addresses.time_register, math.floor((os.epoch("local") - 1451606400000) / 1000), (os.epoch("local") % 1000) * 4294967) | |
| os.queueEvent("getinput") | |
| local ev | |
| repeat | |
| ev = {os.pullEvent()} | |
| if ev[1] == "key" and keymap[ev[2]] then | |
| write32x2(addresses.key_register, 0, keymap[ev[2]]) | |
| elseif ev[1] == "char" then | |
| write32x2(addresses.key_register, 0, ev[2]:byte()) | |
| elseif ev[1] == "mouse_click" then | |
| write32x2(addresses.mouse_registers + (ev[2] - 1)*8, 0, 1) | |
| write32x2(addresses.mouse_registers + 48, 0, math.floor((ev[3] / resX) * 2^32)) | |
| write32x2(addresses.mouse_registers + 56, 0, math.floor((ev[4] / resY) * 2^32)) | |
| elseif ev[1] == "mouse_up" then | |
| write32x2(addresses.mouse_registers + (ev[2] - 1)*8, 0, 0) | |
| write32x2(addresses.mouse_registers + 48, 0, math.floor((ev[3] / resX) * 2^32)) | |
| write32x2(addresses.mouse_registers + 56, 0, math.floor((ev[4] / resY) * 2^32)) | |
| elseif ev[1] == "mouse_drag" or (ev[1] == "mouse_move" and ev[3] ~= nil and ev[4] ~= nil) then | |
| write32x2(addresses.mouse_registers + 48, 0, math.floor((ev[3] / resX) * 2^32)) | |
| write32x2(addresses.mouse_registers + 56, 0, math.floor((ev[4] / resY) * 2^32)) | |
| elseif ev[1] == "mouse_scroll" then | |
| write32x2(addresses.mouse_registers + 72, ev[2] == -1 and 0xFFFFFFFF or 0, ev[2] == -1 and 0xFFFFFFFF or 1) | |
| write32x2(addresses.mouse_registers + 48, 0, math.floor((ev[3] / resX) * 2^32)) | |
| write32x2(addresses.mouse_registers + 56, 0, math.floor((ev[4] / resY) * 2^32)) | |
| end | |
| until ev[1] == "getinput" | |
| end | |
| end | |
| end | |
| term.setGraphicsMode(2) | |
| for r = 0, 7 do for g = 0, 7 do for b = 0, 3 do term.setPaletteColor(r * 32 + g * 4 + b, r / 7, g / 7, b / 3) end end end | |
| term.clear() | |
| term.drawPixels(0, 0, 0, resX, resY) | |
| for i = 0, 1048576 do mem[383*1048576+i] = 255 end | |
| strcpy(addresses.processor_name, "CraftOS-PC subleq") | |
| write32x2(addresses.processor_registers, 0, 1) | |
| write32x2(addresses.vga_registers, 0, resX) | |
| write32x2(addresses.vga_registers + 8, 0, resY) | |
| write32x2(addresses.vga_registers + 16, 0, 32) | |
| write32x2(addresses.vga_registers + 24, 0, 2) | |
| cpuloop() | |
| end) | |
| term.clear() | |
| term.setGraphicsMode(0) | |
| for i = 0, 15 do term.setPaletteColor(2^i, term.nativePaletteColor(2^i)) end | |
| if not ok then printError(err) end |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment