8608/emulator/8608emulator.lua

679 lines
19 KiB
Lua

--love 1
require("colorset")
local ffi = require("ffi")
RelPath = "../"
AsmIncluded = true
local asm = dofile("../assembler-8608.lua")
local Arch = dofile("../rom-8608-defs.lua")
local lg = love.graphics
local li = love.image
local le = love.event
local lt = love.timer
local lk = love.keyboard
----
local function InitColorset()
for i = 0, 63 do
local c = ColorSet[i+1]
ColorSet[i] = {
c[1]/255,
c[2]/255,
c[3]/255,
}
ColorSet[i+1] = nil
end
end
local eventTypes = {}
local function registerEvent(mem, addrFirst, addrLast, read, write, func)
table.insert(eventTypes, func)
local id = #eventTypes
for addr = addrFirst, addrLast do
if read then mem.c.onread [addr] = id end
if write then mem.c.onwrite[addr] = id end
end
end
local function handleEvents(cpu, mem)
for i = 0, mem.c.numevents-1 do
local event = mem.c.events[i]
eventTypes[event.id](event.addr, cpu, mem)
end
mem.c.numevents = 0
end
local RegDisplay = {
scrX = 384+16, scrY = 32+8,
width = 128, height = 192,
fontWidth = 8, fontHeight = 12,
registers = {
{ name = "A" , idx = "a" , x=8,y=8 ,w=2},
{ name = "B" , idx = "b" , x=8,y=8+16 ,w=2},
{ name = "C" , idx = "c" , x=8,y=8+16* 2,w=2},
{ name = "U" , idx = "u" , x=8,y=8+16* 3,w=2},
{ name = "T" , idx = "t" , x=8,y=8+16* 4,w=2},
{ name = "P" , idx = "p" , x=8,y=8+16* 5,w=4},
{ name = "Q" , idx = "q" , x=8,y=8+16* 6,w=4},
{ name = "S" , idx = "s" , x=8,y=8+16* 7,w=4},
{ name = "V" , idx = "v" , x=8,y=8+16* 8,w=4},
{ name = "I" , idx = "i" , x=8,y=8+16* 9,w=4},
{ name = "Ins", idx = "instr", x=8,y=8+16*10,w=2},
{ name = "Cyc", idx = "cycle", x=64+8-16,y=8+16*10,w=2},
},
flags = {
{ name = "CF" , idx = "cf" , x=64+8,y=8 },
{ name = "NZ" , idx = "nz" , x=64+8,y=8+16 },
{ name = "IRq", idx = "irq", x=64+8,y=8+16*2},
{ name = "IEn", idx = "ien", x=64+8,y=8+16*5},
{ name = "Int", idx = "ifg", x=64+8,y=8+16*3},
{ name = "Run", idx = "rfg", x=64+8,y=8+16*4},
},
}
local function InitRegDisplay(rd)
lg.print("Registers", rd.scrX, rd.scrY-16)
lg.rectangle("line", rd.scrX, rd.scrY, rd.width, rd.height+1)
for i, reg in ipairs(rd.registers) do
lg.rectangle("line", rd.scrX+reg.x+(rd.fontWidth*(4-reg.w)), rd.scrY+reg.y, rd.fontWidth*reg.w+1, rd.fontHeight+1)
lg.print(reg.name, rd.scrX+reg.x+32+4, reg.y + rd.scrY)
end
for i, flg in pairs(rd.flags) do
lg.rectangle("line", rd.scrX+flg.x, rd.scrY+flg.y, rd.fontHeight+1, rd.fontHeight+1)
lg.print(flg.name, rd.scrX+flg.x+rd.fontHeight+4, flg.y+rd.scrY)
end
end
local function printValue(val, w, x, y, fw)
for i = 1, w do
local v = math.floor(val/math.pow(16,i-1))%16
lg.print(string.format("%01X", v), x+(fw*(4-i)), y+1)
end
end
local function RedrawRegDisplay(rd, cpu, mem)
for i, reg in ipairs(rd.registers) do
lg.setColor(0,0,0)
lg.rectangle("fill", rd.scrX+reg.x+(rd.fontWidth*(4-reg.w)), rd.scrY+reg.y, rd.fontWidth*reg.w, rd.fontHeight)
lg.setColor(1,1,1)
local val = cpu.c[reg.idx]%(math.pow(16, reg.w))
printValue(val, reg.w, rd.scrX+reg.x, rd.scrY+reg.y, rd.fontWidth)
end
for i, flg in pairs(rd.flags) do
local val = cpu.c[flg.idx]
if val~=0 then
lg.setColor(1,1,1)
else
lg.setColor(0,0,0)
end
lg.rectangle("fill", rd.scrX+flg.x, rd.scrY+flg.y, rd.fontHeight, rd.fontHeight)
end
lg.setColor(1,1,1)
end
local ReadMemory
local WriteMemory
local StackDisplay = {
scrX = 8+384+32+6, scrY = 32+192+32-4,
lines = 16, fontHeight = 12,
}
local function InitStackDisplay(sd)
sd.width = 96+1
sd.height = 12+(sd.lines*sd.fontHeight)
lg.print("Stack", sd.scrX, sd.scrY-16)
lg.rectangle("line", sd.scrX, sd.scrY, sd.width+1, sd.height+1)
end
local function RedrawStackDisplay(sd, cpu, mem)
lg.setColor(0,0,0)
lg.rectangle("fill", sd.scrX, sd.scrY, sd.width, sd.height)
lg.setColor(1,1,1)
for i = 1, sd.lines do
local addr = (cpu.c.s-i)%65536
local val = ReadMemory(mem, addr)
lg.print(string.format("%04X -%2i %02X", addr, i, val), sd.scrX+8, sd.scrY+8+(12*(i-1)))
end
end
local MemoryDisplays = {
{
scrX = 8, scrY = 32+192+32-4,
columns = 16, columnSpace = 4, rows = 16,
fontHeight = 12,
showAscii = false,
addr = 0x3000,
highlightTime = 8,
},
}
local function InitMemoryDisplay(md)
local cw = 7
md.mainCols = 9+md.columns*3+md.columnSpace-1
md.width = cw*(md.mainCols)+cw*(md.showAscii and (md.columns+5) or 0)-8
md.height = 12+md.fontHeight*md.rows
lg.print("Memory", md.scrX, md.scrY-16)
lg.rectangle("line", md.scrX, md.scrY, md.width+1, md.height+1)
end
local function RedrawMemoryDisplay(md, cpu, mem)
lg.setColor(0,0,0)
lg.rectangle("fill", md.scrX, md.scrY, md.width, md.height)
lg.setColor(1,1,1)
local highlightAddrs = {}
highlightAddrs[cpu.c.p] = "P"
highlightAddrs[cpu.c.q] = "Q"
highlightAddrs[cpu.c.s] = "S"
highlightAddrs[cpu.c.v] = "V"
highlightAddrs[(cpu.c.i-1)%65536] = "I"
local cw = 7
local tickDraw = cpu.c.frame-md.highlightTime
for l = 1, md.rows do
local addr = md.addr + (l-1)*md.columns
local lx = md.scrX+8
local ly = md.scrY+8+(md.fontHeight*(l-1))
lg.print(string.format("%04X", addr).." | ", lx, ly)
lx = lx+(cw*7)
for x = 1, md.columns do
local a = addr + (x-1)
local v = ReadMemory(mem, a)
if highlightAddrs[a] then
lg.line(lx, ly, lx, ly+11)
lg.line(lx-1, ly, lx+15, ly)
lg.line(lx-1, ly+11, lx+15, ly+11)
lg.print(highlightAddrs[a], lx-cw, ly)
end
if mem.c.writes[a] > tickDraw then
lg.rectangle("fill",lx-1,ly,cw*2+2,11)
lg.setColor(0,0,0)
end
lg.print(string.format("%02X", v), lx, ly)
lg.setColor(1,1,1)
if mem.c.reads[a] > tickDraw then
lg.rectangle("line", lx-1, ly, cw*2+2+1, 11+1)
end
lx = lx+(cw*3)
if x%md.columnSpace==0 then lx = lx+cw end
end
if md.showAscii then
lg.print("|", lx, ly)
lx = lx+cw
end
if md.showAscii then
for x = 1, md.columns do
local a = addr + (x-1)
local v = ReadMemory(mem, a)
if v>=128 then v = 0 end
local c = string.char(v)
lg.print(c, md.scrX+lx+((x-1)*cw), md.scrY+8+(12*(l-1)))
end
end
end
end
local ProgramDisplay = {
scrX = 8+384+8+128+8, scrY = 32+8,
fontHeight = 12,
numLines = 34,
highlightTime = 8,
}
local function pdLinesFromDasm(dasm)
local lines, addrLines, lineAddrs = {}, {}, {}
for line in dasm:gmatch("[^\n]+") do
if line~="..." then
local addrh, datah, rest = line:match("^(.+) | (.+) | +(.+)$")
local text = rest
local label = nil
if rest:find(":") then label, text = rest:match("^(.+): (.+)$") end
local addr = tonumber(addrh, 16)
if label then
--table.insert(lines, " | "..label..":")
table.insert(lines, " | "..label..":")
lineidx = #lines
end
--table.insert(lines, addrh.." | "..text)
table.insert(lines, addrh.." | "..datah.." | "..text)
local lineidx = #lines
local len = 0; for _ in datah:gfind("[0-9a-fA-F][0-9a-fA-F]") do len = len+1 end;
for i = 1, len do addrLines[addr+i-1] = lineidx end
lineAddrs[lineidx] = addr
else
table.insert(lines, "")
end
end
return lines, addrLines, lineAddrs
end
local function InitProgramDisplay(pd, arch, data, code, symbols)
pd.width = 256
pd.height = 12+pd.fontHeight*pd.numLines-3
lg.print("Program", pd.scrX, pd.scrY-16)
lg.rectangle("line", pd.scrX, pd.scrY, pd.width, pd.height)
pd.firstLine = 1
pd.data = data
pd.code = code
local dasm = asm.disassembleMemory(arch, data, code, symbols)
pd.lines, pd.addrLines, pd.lineAddrs = pdLinesFromDasm(dasm)
pd.midLine = math.floor(pd.numLines/2)
end
local function RedrawProgramDisplay(pd, cpu, mem)
lg.setColor(0,0,0)
lg.rectangle("fill", pd.scrX, pd.scrY, pd.width-1, pd.height-1)
lg.setColor(1,1,1)
local rectwidth = pd.width-18
local rectheight = pd.fontHeight-1
local tickDraw = cpu.c.frame-pd.highlightTime
local instrAddr = (cpu.c.i-1)%65536
local instrLine = pd.addrLines[instrAddr]
if instrLine then
if pd.firstLine > instrLine then pd.firstLine = math.max(instrLine-pd.midLine, 1)
elseif pd.firstLine+pd.numLines-1 < instrLine then pd.firstLine = math.min(instrLine-pd.numLines+pd.midLine, #pd.lines-pd.numLines+1)
end
end
local screenlineidx = 0
for lineidx = pd.firstLine, pd.firstLine+pd.numLines-1 do
local line = pd.lines[lineidx]
local lineaddr = pd.lineAddrs[lineidx]
if line then
local x, y = pd.scrX+8, pd.scrY+4+screenlineidx*pd.fontHeight
if lineaddr and mem.c.reads[lineaddr] > tickDraw then
lg.rectangle("line", x, y, rectwidth, rectheight)
end
if instrLine==lineidx then
lg.rectangle("fill", x, y, rectwidth, rectheight)
lg.setColor(0,0,0)
end
lg.print(line, x, y)
lg.setColor(1,1,1)
end
screenlineidx = screenlineidx + 1
end
end
local VideoDisplay = {
width = 256, height = 128,
scrX = 8, scrY = 256,
addr = 0x8000,
}
local function InitVideoDisplay(vd)
lg.print("Video Display", vd.scrX, vd.scrY-16)
vd.imageData = li.newImageData(vd.width, vd.height)
lg.setColor(1,1,1)
lg.rectangle("line", vd.scrX, vd.scrY, vd.width+1, vd.height+1)
end
local CharDisplay = {
width = 64*6, height = 16*12,
rows = 16, cols = 64,
fontWidth = 6, fontHeight = 12,
scrX = 8, scrY = 32+8,
addrChar = 0x0800,
addrColor = 0x0C00,
}
local function InitCharDisplay(cd)
lg.print("Char Display", cd.scrX, cd.scrY-16)
cd.font = lg.newFont("consola.ttf", cd.fontHeight-1, "mono")
end
local Keyboard = {
addrRange = {0x0500, 0x05FF},
queueSize = 16,
queue = {},
interrupts = false,
queueEmpty = true,
}
local function kbSetNext(kb, cpu, mem)
local newval = kb.queue[1] or 0
if mem.c.data[kb.addrRange[1]] ~= newval then
for a = kb.addrRange[1], kb.addrRange[2] do
mem.c.data[a] = newval
end
end
end
local function KeyboardOnRead(addr, cpu, mem, kb)
table.remove(kb.queue, 1)
kbSetNext(kb, cpu, mem)
end
local function KeyboardOnWrite(addr, cpu, mem, kb)
local val = mem.c.data[addr]
kb.interrupts = val~=0
mem.c.data[addr] = kb.queue[1] or 0
end
local keycodes = require("keycodes")
local CPURequestInterrupt
local function KeyboardOnKey(kb, key, press, cpu, mem)
local code = keycodes[key] or keycodes["invalid"]
if code==0x7F then print("invalid key: "..key) end
table.insert(kb.queue, code + (press and 128 or 0))
if #kb.queue > kb.queueSize then table.remove(kb.queue, 1) end
kb.queueEmpty = false
kbSetNext(kb, cpu, mem)
if kb.interrupts then CPURequestInterrupt(cpu) end
end
local function RedrawFPSCounter(x, y)
lg.setColor(0,0,0)
lg.rectangle("fill",x,y,64,12)
lg.setColor(1,1,1)
lg.print("FPS: "..lt.getFPS(), x,y)
end
local function printHighlight(s, o, h, x, y)
x = x+o*7
local w = 7*#s
lg.setColor(1,1,1)
if h then
lg.rectangle("fill", x, y, w, 12)
lg.setColor(0,0,0)
end
lg.print(s, x, y)
end
local function RedrawKeyInfo(x, y, uk, run)
lg.setColor(0,0,0)
lg.rectangle("fill",x,y,768,12)
lg.setColor(1,1,1)
printHighlight("[F4] Toggle keyboard", 0, lk.isDown("f4"), x, y)
lg.setColor(1,1,1)
if uk then
printHighlight("Keystrokes passed to device", 23, false, x, y)
else
printHighlight("[R] "..(run and "Stop" or "Run "), 23, lk.isDown("r"), x, y)
printHighlight("[T] Tick once", 33, lk.isDown("t"), x, y)
printHighlight("[S] Step once", 48, lk.isDown("s"), x, y)
printHighlight("[Q] Quit", 63, lk.isDown("q"), x, y)
end
end
local GPIO = {}
local function InitGPIO(gpio)
gpio.mulLeft = 0
gpio.mulRight = 0
gpio.divLeft = 0
gpio.divRight = 0
gpio.intQueued = false
end
local function UpdateGPIO(gpio, cpu, mem)
if gpio.intQueued then
gpio.intQueued = false
CPURequestInterrupt(cpu)
end
end
local function gpioSetValue(name, func)
return function(addr, cpu, mem, gpio)
gpio[name] = ReadMemory(mem, addr)
func(addr, cpu, mem, gpio)
end
end
local function gpioMul(addr, cpu, mem, gpio)
local base = math.floor(addr/256)*256
local res = gpio.mulLeft*gpio.mulRight
WriteMemory(mem, base+0x00, math.floor(res/256))
WriteMemory(mem, base+0x01, res%256)
end
local function gpioDiv(addr, cpu, mem, gpio)
local base = math.floor(addr/256)*256
WriteMemory(mem, base+0x02, math.floor(gpio.divLeft/gpio.divRight))
WriteMemory(mem, base+0x03, gpio.divLeft%gpio.divRight)
end
local gpioFunctions = {
[0x00] = gpioSetValue("mulLeft" , gpioMul),
[0x01] = gpioSetValue("mulRight", gpioMul),
[0x02] = gpioSetValue("divLeft" , gpioDiv),
[0x03] = gpioSetValue("divRight", gpioDiv),
[0x04] = function(addr, cpu, mem, gpio) WriteMemory(mem, addr, gpioPopcount(readMemory(mem, addr))) end,
[0x05] = function(addr, cpu, mem, gpio) gpio.intQueued = true; WriteMemory(mem, addr, 0); end
}
local function GPIOOnWrite(addr, cpu, mem, gpio)
local offset = addr%256
local func = gpioFunctions[offset]
if func then
func(addr, cpu, mem, gpio)
end
end
local peripherals = {
CharDisplay = { range = {0x0800, 0x0FFF}, write = true },
BootROM = { range = {0x0000, 0x03FF}, write = false },
SystemRAM = { range = {0x1000, 0x1FFF}, write = true },
UserROM = { range = {0x2000, 0x2FFF}, write = false },
UserRAM = { range = {0x3000, 0x3FFF}, write = true },
VideoDisplay = { range = {0x8000, 0xFFFF}, write = true },
Keyboard = { range = {0x0500, 0x05FF}, write = true,
onread = function(addr, cpu, mem) KeyboardOnRead (addr, cpu, mem, Keyboard) end,
onwrite = function(addr, cpu, mem) KeyboardOnWrite(addr, cpu, mem, Keyboard) end,
},
GPIO = { range = {0x0400, 0x04FF}, write = true,
onwrite = function(addr, cpu, mem) GPIOOnWrite(addr, cpu, mem, GPIO) end,
},
}
----
ffi.cdef [[
struct Event {
int id;
int addr;
};
struct Memory {
int data[65536];
int canwrite[65536];
int writes[65536];
int reads[65536];
int onread[65536];
int onwrite[65536];
struct Event events[4096];
int numevents;
};
]]
local Memory = {
c = ffi.new("struct Memory"),
}
local function InitMemory(mem, pers)
for i = 0, 65535 do
mem.c.data[i] = 0
mem.c.canwrite[i] = 0
mem.c.writes[i] = 0
mem.c.reads[i] = 0
mem.c.onread[i] = 0
mem.c.onwrite[i] = 0
end
for k, per in pairs(pers) do
if per.onread then registerEvent(mem, per.range[1], per.range[2], true , false, per.onread ) end
if per.onwrite then registerEvent(mem, per.range[1], per.range[2], false, true , per.onwrite) end
for a = per.range[1], per.range[2] do
mem.c.canwrite[a] = (per.write and 1 or 0)
end
end
end
ReadMemory = function(mem, addr)
return mem.c.data[addr%65536]%256
end
WriteMemory = function(mem, addr, val)
if mem.c.canwrite[addr%65536]~=0 then
mem.c.data[addr%65536] = val%256
end
end
local function AssembleToMemory(mem, fn, arch)
local data, code, symbols = asm.assembleFile(fn)
for addr = 0, 65535 do
if data[addr] then
mem.c.data[addr] = data[addr]
end
end
return data, code, symbols
end
ffi.cdef [[
struct CPU {
int a;
int b;
int c;
int u;
int t;
int p;
int q;
int s;
int v;
int i;
int cf;
int nz;
int irq;
int ifg;
int rfg;
int ien;
int instr;
int cycle;
int instrpre;
int frame;
};
int TickCPU(struct CPU* const cpu, struct Memory* const mem, const int count, const int countinstrs, const int breakaddr);
]]
local CPU = {
c = ffi.new("struct CPU"),
}
local cpuDll = ffi.load("8608emulator.dll")
local function TickCPU(cpu, mem, count, countinstrs, breakaddr)
local countleft = count
while countleft>0 do
countleft = cpuDll.TickCPU(cpu.c, mem.c, countleft, countinstrs and 1 or 0, breakaddr or 0xFFFFFFFF)
handleEvents(cpu, mem)
end
end
local function InitCPU(cpu)
cpu.c.rfg = 1;
end
CPURequestInterrupt = function(cpu)
cpu.c.irq = 1;
end
function RunToNextInstr(cpu)
end
----
local function RedrawVideoDisplay(vd, mem)
local vd = VideoDisplay
for y = 0, vd.height-1 do
for x = 0, vd.width-1 do
local a = vd.addr + y*vd.width + x
local colorid = ReadMemory(mem, a)%64
local color = ColorSet[colorid]
vd.imageData:setPixel(x, y, color[1], color[2], color[3])
end
end
local img = lg.newImage(vd.imageData)
lg.setColor(1,1,1)
lg.draw(img, vd.scrX, vd.scrY)
end
local function RedrawCharDisplay(cd, mem)
lg.rectangle("line", cd.scrX, cd.scrY, cd.width+1, cd.height+1)
lg.setColor(0,0,0)
lg.rectangle("fill", cd.scrX, cd.scrY, cd.width, cd.height)
lg.setFont(cd.font)
local cd = CharDisplay
for cy = 0, cd.rows-1 do
for cx = 0, cd.cols-1 do
local abase = cy*cd.cols + cx
local achar = cd.addrChar + abase
local acolor = cd.addrColor + abase
local colormem = ReadMemory(mem, acolor)
local colorid = colormem%64
local highlight = colormem>=128
lg.setColor(ColorSet[colorid])
if highlight then
lg.rectangle("fill", cd.scrX + cx*cd.fontWidth, cd.scrY + cy*cd.fontHeight, cd.fontWidth, cd.fontHeight)
lg.setColor(0, 0, 0)
end
local val = ReadMemory(mem, achar)%128
if val>=32 then
local char = string.char(val)
lg.print(char, cd.scrX + cx*cd.fontWidth, cd.scrY + cy*cd.fontHeight)
elseif val>=16 and val<=31 then
local r, g, b = math.floor(val/4)%2, math.floor(val/2)%2, val%2
lg.setColor(r, g, b)
lg.rectangle("fill", cd.scrX + cx*cd.fontWidth, cd.scrY + cy*cd.fontHeight, cd.fontWidth, cd.fontHeight)
end
end
end
lg.setColor(1,1,1)
lg.setFont(InfoFont)
end
local function InitWindowCanvas()
WindowCanvas = lg.newCanvas(WindowX, WindowY)
lg.setCanvas(WindowCanvas)
lg.setColor(1,1,1)
lg.setFont(InfoFont)
lg.print("8608 CPU Emulator", 4, 4)
end
local function RedrawWindow(usekeyboard, runcpu)
lg.setCanvas(WindowCanvas)
lg.setFont(InfoFont)
RedrawCharDisplay(CharDisplay, Memory)
--RedrawVideoDisplay(VideoDisplay, Memory)
RedrawRegDisplay(RegDisplay, CPU, Memory)
RedrawStackDisplay(StackDisplay, CPU, Memory)
RedrawProgramDisplay(ProgramDisplay, CPU, Memory)
for _, md in ipairs(MemoryDisplays) do RedrawMemoryDisplay(md, CPU, Memory) end
RedrawFPSCounter(128+32, 4)
RedrawKeyInfo(128+32+64+16, 4, usekeyboard, runcpu)
lg.setCanvas()
end
function love.load()
InitColorset()
lg.setDefaultFilter("nearest", "nearest")
lg.setLineWidth(1)
lg.setLineStyle("rough")
InfoFont = lg.newFont("consola.ttf", 12, "mono")
InitMemory(Memory, peripherals)
InitWindowCanvas()
InitCPU(CPU)
InitGPIO(GPIO)
--InitVideoDisplay(VideoDisplay)
InitCharDisplay(CharDisplay)
InitRegDisplay(RegDisplay)
InitStackDisplay(StackDisplay)
local data, code, symbols = AssembleToMemory(Memory, arg[2] or "../../8608programs/emutest.asm", Arch)
InitProgramDisplay(ProgramDisplay, Arch, data, code, symbols)
for _, md in ipairs(MemoryDisplays) do InitMemoryDisplay(md) end
RedrawWindow()
lg.setCanvas()
end
local RunCPU = false
local CPUSpeed = 4999
local UseKeyboard = false
function love.draw()
UpdateGPIO(GPIO, CPU, Memory)
CPU.c.frame = CPU.c.frame + 1
if RunCPU then
TickCPU(CPU, Memory, CPUSpeed, false, nil)
end
RedrawWindow(UseKeyboard, RunCPU)
lg.setColor(1,1,1)
lg.draw(WindowCanvas, 0, 0, 0, 2, 2)
end
function love.keypressed(k)
if k=="f4" then
UseKeyboard = not UseKeyboard
else
if UseKeyboard then
KeyboardOnKey(Keyboard, k, true, CPU, Memory)
else
if k=="q" then le.quit()
elseif k=="s" then TickCPU(CPU, Memory, 1, true , nil)
elseif k=="t" then TickCPU(CPU, Memory, 1, false, nil)
elseif k=="o" then RunToNextInstr(cpu)
elseif k=="r" then RunCPU = not RunCPU
elseif k=="i" then CPU.c.irq = 1
elseif k=="u" then CPU.c.rfg = 1
end
end
end
end
function love.keyreleased(k)
if k~="f4" and UseKeyboard then KeyboardOnKey(Keyboard, k, false, CPU, Memory) end
end