Skip to content

Instantly share code, notes, and snippets.

@MCJack123
Last active September 11, 2024 16:36
Show Gist options
  • Save MCJack123/8e1451fd34986f7cef1eb20af872a420 to your computer and use it in GitHub Desktop.
Save MCJack123/8e1451fd34986f7cef1eb20af872a420 to your computer and use it in GitHub Desktop.
Dawn emulation for CraftOS-PC Accelerated
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