function vt100emu(gpu) -- takes GPU component proxy *gpu* and returns a function to write to it in a manner like an ANSI terminal local colours = {0x0,0xFF0000,0x00FF00,0xFFFF00,0x0000FF,0xFF00FF,0x00B6FF,0xFFFFFF} local mx, my = gpu.maxResolution() local cx, cy = 1, 1 local pc = " " local lc = "" local mode = "n" local lw = true local sx, sy = 1,1 local cs = "" local bg, fg = 0, 0xFFFFFF -- setup gpu.setResolution(mx,my) gpu.fill(1,1,mx,my," ") local function termwrite(s) local rs = "" s=s:gsub("\8","\27[D") pc = gpu.get(cx,cy) gpu.setForeground(fg) gpu.setBackground(bg) gpu.set(cx,cy,pc) for i = 1, s:len() do local cc = s:sub(i,i) if mode == "n" then if cc == "\n" then -- line feed cx, cy = 1, cy+1 elseif cc == "\r" then -- cursor home cx = 1 elseif cc == "\27" then -- escape mode = "e" elseif cc == "\t" then cx = 8*((cx+9)//8) elseif string.byte(cc) > 31 and string.byte(cc) < 127 then -- printable, I guess gpu.set(cx, cy, cc) cx = cx + 1 end elseif mode == "e" then if cc == "[" then mode = "v" cs = "" elseif cc == "D" then -- scroll down gpu.copy(1,2,mx,my-1,0,-1) gpu.fill(1,my,mx,1," ") cy=cy+1 mode = "n" elseif cc == "M" then -- scroll up gpu.copy(1,1,mx,my-1,0,1) gpu.fill(1,1,mx,1," ") mode = "n" else mode = "n" end elseif mode == "v" then mode = "n" if cc == "s" then -- save cursor sx, sy = cx, cy elseif cc == "u" then -- restore cursor cx, cy = sx, sy elseif cc == "H" then -- cursor home or to local tx, ty = cs:match("(%d+);(%d+)") tx, ty = tx or "1", ty or "1" cx, cy = tonumber(tx), tonumber(ty) elseif cc == "A" then -- cursor up cy = cy - (tonumber(cs) or 1) elseif cc == "B" then -- cursor down cy = cy + (tonumber(cs) or 1) elseif cc == "C" then -- cursor right cx = cx + (tonumber(cs) or 1) elseif cc == "D" then -- cursor left cx = cx - (tonumber(cs) or 1) elseif cc == "h" and lc == "7" then -- enable line wrap lw = true elseif cc == "l" and lc == "7" then -- disable line wrap lw = false elseif cc == "c" then rs = string.format("%s\27[%d;%d0c",rs,mx,my) elseif cc == "n" and lc == "6" then rs = string.format("%s\27[%d;%dR",rs,cx,cy) elseif cc == "K" then if lc == "1" then gpu.fill(1,cy,cx,1," ") elseif lc == "2" then gpu.fill(cx,cy,mx,1," ") else gpu.fill(1,cy,mx,1," ") end elseif cc == "J" then if lc == "1" then gpu.fill(1,1,mx,cy," ") elseif lc == "2" then gpu.full(1,1,mx,my," ") cx,cy = 1, 1 else gpu.fill(1,cy,mx,my," ") end elseif cc == "m" then for num in cs:gmatch("%d+") do num=tonumber(num) if num == 0 then fg,bg = 0xFFFFFF,0 elseif num == 7 then local nfg,nbg = bg, fg fg, bg = nfg, nbg elseif num > 29 and num < 38 then fg = colours[num-29] elseif num > 39 and num < 48 then bg = colours[num-39] end end gpu.setForeground(fg) gpu.setBackground(bg) else cs = cs .. cc if cc:match("[%d;]") then mode = "v" end end end if cx > mx and lw then cx, cy = 1, cy+1 end if cy > my then gpu.copy(1,2,mx,my-1,0,-1) gpu.fill(1,my,mx,1," ") cy=my end if cy < 1 then cy = 1 end if cx < 1 then cx = 1 end lc = cc end pc = gpu.get(cx,cy) gpu.setForeground(bg) gpu.setBackground(fg) gpu.set(cx,cy,pc) gpu.setForeground(fg) gpu.setBackground(bg) return rs end return termwrite end