From 6de62423e8b3088258ed2d361701cdb158f110b3 Mon Sep 17 00:00:00 2001 From: Redo Date: Mon, 12 Aug 2024 22:52:33 -0600 Subject: [PATCH] more emulator features, sub # instr --- 8608-instructions.lua | 6 +- 8608.asm | 2 + emulator/8608emulator.dll | Bin 164104 -> 165264 bytes emulator/8608emulator.lua | 331 ++++++++++++++++++++++++++++-------- emulator/instructions_gen.c | 12 +- emulator/keycodes.lua | 48 +++--- emulator/temp/temp.bin | Bin 65536 -> 65536 bytes emulator/temp/temp.lis | 274 +++++++++++++++++++++++++++-- instruction-map.ods | Bin 45795 -> 45662 bytes instructionList.txt | 6 +- readme.md | 6 +- 11 files changed, 563 insertions(+), 122 deletions(-) diff --git a/8608-instructions.lua b/8608-instructions.lua index 80568ae..8e2aece 100644 --- a/8608-instructions.lua +++ b/8608-instructions.lua @@ -65,7 +65,9 @@ local instructions = { { mnem="ADD #" , opcode=0x6B, {"loadImmedT","instrSub1"}, {"aluA", "alurT","aluOpAdd" ,"instrNext"}, desc="Add %imm8 to %A, %flags" , ccode={"loadimmedt","addf(cpu.a,cpu.t); lni;"} }, -- { mnem="ADB #" , opcode=0x , {"loadImmedT","instrSub1"}, {"aluB", "alurT","aluOpAdd" ,"instrNext"}, desc="" , ccode={"loadimmedt","addf(cpu.b,cpu.t); lni;"} }, -- { mnem="ADC #" , opcode=0x , {"loadImmedT","instrSub1"}, {"aluC", "alurT","aluOpAdd" ,"instrNext"}, desc="" , ccode={"loadimmedt","addf(cpu.c,cpu.t); lni;"} }, + { mnem="SUB #" , opcode=0xEB, {"loadImmedT","instrSub1"}, {"aluA", "alurT","aluOpSub" ,"instrNext"}, desc="Subtract %imm8 from %A, %flags" , ccode={"loadimmedt","subf(cpu.a,cpu.t); lni;"} }, { mnem="ADC #" , opcode=0x69, {"loadImmedT","instrSub1"}, {"aluA", "alurT","aluOpAddC","instrNext"}, desc="Add %imm8 plus the Carry Flag to %A, %flags" , ccode={"loadimmedt","addf(cpu.a,cpu.t+cpu.cf); lni;"} }, + { mnem="SBC #" , opcode=0xE9, {"loadImmedT","instrSub1"}, {"aluA", "alurT","aluOpSubC","instrNext"}, desc="Subtract %imm8 from %A including the Carry Flag, %flags" , ccode={"loadimmedt","addf(cpu.a,~cpu.t+cpu.cf); lni;"} }, { mnem="CMP #" , opcode=0xC9, {"loadImmedT","instrSub1"}, {"alulA","alurT","aluOpSub" ,"instrNext"}, desc="%cmp %imm8 from %A" , ccode={"loadimmedt","cmpf(cpu.a,cpu.t); lni;"} }, { mnem="AND #" , opcode=0x29, {"loadImmedT","instrSub1"}, {"aluA", "alurT","aluOpAnd" ,"instrNext"}, desc="Bitwise AND %A with %imm8, %zflag" , ccode={"loadimmedt","cpu.a&=cpu.t; setzf(cpu.a); lni;"} }, { mnem="ORA #" , opcode=0x09, {"loadImmedT","instrSub1"}, {"aluA", "alurT","aluOpIor" ,"instrNext"}, desc="Bitwise OR %A with %imm8, %zflag" , ccode={"loadimmedt","cpu.a|=cpu.t; setzf(cpu.a); lni;"} }, @@ -82,7 +84,7 @@ local instructions = { -- { mnem="sbb zpg" , opcode=0x , {"loadImmedT","instrSub1"}, {"loadStackRelT","instrSub2"}, {"aluB", "alurT","aluOpSub" ,"instrNext"}, desc="B-=*(S+imm8), set flags" , ccode={"loadimmedt","loadstackrelu","subf(cpu.b,cpu.u); lni;"} }, -- { mnem="sbc zpg" , opcode=0x , {"loadImmedT","instrSub1"}, {"loadStackRelT","instrSub2"}, {"aluC", "alurT","aluOpSub" ,"instrNext"}, desc="C-=*(S+imm8), set flags" , ccode={"loadimmedt","loadstackrelu","subf(cpu.c,cpu.u); lni;"} }, { mnem="ADC zpg" , opcode=0x65, {"loadImmedT","instrSub1"}, {"loadStackRelT","instrSub2"}, {"aluA", "alurT","aluOpAddC","instrNext"}, desc="Add %zpgIn plus the Carry Flag to %A, %flags" , ccode={"loadimmedt","loadstackrelu","addf(cpu.a,cpu.u+cpu.cf); lni;"} }, - { mnem="SBC zpg" , opcode=0xE5, {"loadImmedT","instrSub1"}, {"loadStackRelT","instrSub2"}, {"aluA", "alurT","aluOpSubC","instrNext"}, desc="Subtract %zpgIn from %A including the Carry Flag, %flags", ccode={"loadimmedt","loadstackrelu","addf(cpu.a,-cpu.u+cpu.cf); lni;"} }, + { mnem="SBC zpg" , opcode=0xE5, {"loadImmedT","instrSub1"}, {"loadStackRelT","instrSub2"}, {"aluA", "alurT","aluOpSubC","instrNext"}, desc="Subtract %zpgIn from %A including the Carry Flag, %flags", ccode={"loadimmedt","loadstackrelu","addf(cpu.a,~cpu.u+cpu.cf); lni;"} }, { mnem="CMP zpg" , opcode=0xC5, {"loadImmedT","instrSub1"}, {"loadStackRelT","instrSub2"}, {"alulA","alurT","aluOpSub" ,"instrNext"}, desc="%cmp %zpgIn from %A" , ccode={"loadimmedt","loadstackrelu","cmpf(cpu.a,cpu.u); lni;"} }, { mnem="AND zpg" , opcode=0x25, {"loadImmedT","instrSub1"}, {"loadStackRelT","instrSub2"}, {"aluA", "alurT","aluOpAnd" ,"instrNext"}, desc="Bitwise AND %A with %zpgIn, %flags" , ccode={"loadimmedt","loadstackrelu","cpu.a&=cpu.u; setzf(cpu.a); lni;"} }, { mnem="ORA zpg" , opcode=0x05, {"loadImmedT","instrSub1"}, {"loadStackRelT","instrSub2"}, {"aluA", "alurT","aluOpIor" ,"instrNext"}, desc="Bitwise OR %A with %zpgIn, %flags" , ccode={"loadimmedt","loadstackrelu","cpu.a|=cpu.u; setzf(cpu.a); lni;"} }, @@ -112,7 +114,7 @@ local instructions = { { mnem="SUB C" , opcode=0xFB, {"aluA", "alurC","aluOpSub" ,"instrNext"}, desc="Subtract %C from %A, %flags" , ccode={"subf(cpu.a,cpu.c); lni;"} }, -- { mnem="sbb C" , opcode=0x , {"aluB", "alurC","aluOpSub" ,"instrNext"}, desc="B-=C, set flags" , ccode={"subf(cpu.b,cpu.c); lni;"} }, { mnem="ADC C" , opcode=0x79, {"aluA", "alurC","aluOpAddC","instrNext"}, desc="Add %C plus the Carry Flag to %A, %flags" , ccode={"addf(cpu.a,cpu.c+cpu.cf); lni;"} }, - { mnem="SBC C" , opcode=0xF9, {"aluA", "alurC","aluOpSubC","instrNext"}, desc="Subtract %C form %A including the Carry Flag, %flags" , ccode={"addf(cpu.a,-cpu.c+cpu.cf); lni;"} }, + { mnem="SBC C" , opcode=0xF9, {"aluA", "alurC","aluOpSubC","instrNext"}, desc="Subtract %C form %A including the Carry Flag, %flags" , ccode={"addf(cpu.a,~cpu.c+cpu.cf); lni;"} }, { mnem="CMP C" , opcode=0xD9, {"alulA","alurC","aluOpSub" ,"instrNext"}, desc="%cmp %C from %A" , ccode={"cmpf(cpu.a,cpu.c); lni;"} }, { mnem="AND C" , opcode=0x39, {"aluA", "alurC","aluOpAnd" ,"instrNext"}, desc="Bitwise AND %A with %C, %flags" , ccode={"cpu.a&=cpu.c; setzf(cpu.a); lni;"} }, { mnem="ORA C" , opcode=0x19, {"aluA", "alurC","aluOpIor" ,"instrNext"}, desc="Bitwise OR %A with %C, %flags" , ccode={"cpu.a|=cpu.c; setzf(cpu.a); lni;"} }, diff --git a/8608.asm b/8608.asm index fedb659..8f169b3 100644 --- a/8608.asm +++ b/8608.asm @@ -41,7 +41,9 @@ DEC {value: u8} => $C6 @ value ICC {value: u8} => $47 @ value ADD #{value: i8} => $6B @ value + SUB #{value: i8} => $EB @ value ADC #{value: i8} => $69 @ value + SBC #{value: i8} => $E9 @ value CMP #{value: i8} => $C9 @ value AND #{value: i8} => $29 @ value ORA #{value: i8} => $09 @ value diff --git a/emulator/8608emulator.dll b/emulator/8608emulator.dll index 30a5627ec2ebafc11a315d22f8e04d7bfc8f68e2..a505abe083fe7f96258810ec34e442ffd20ab319 100644 GIT binary patch delta 25136 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 Kw&h1;y#627+#mA* delta 24918 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 diff --git a/emulator/8608emulator.lua b/emulator/8608emulator.lua index 08e5d9c..baf0f2c 100644 --- a/emulator/8608emulator.lua +++ b/emulator/8608emulator.lua @@ -4,7 +4,12 @@ -- This file deals mostly with the user interface, controls, and high-level functions. -- Emulation of the CPU internals is done by the 8608emulator.dll library. See 8608emulator.c for details. +-- Number of frames to highlight memory and instructions when they're accessed local MainHighlightTime = 8 +-- If true, char display renders in default print fallback mode; otherwise, terminal print mode is used +local PrintMode = true +-- Maximum number of CPU cycles per frame +local MaxTicksPerFrame = 555 require("colorset") local ffi = require("ffi") @@ -17,6 +22,11 @@ local lk = love.keyboard local la = love.audio local PlaySound +local CPURequestInterrupt +local TimerSchedule +local TickCPU + +local jsrInstrs = {0x20, 0x2C, 0x0C} ---- local function InitColorset() @@ -220,15 +230,17 @@ local ProgramDisplay = { fontHeight = 12, numLines = 34, highlightTime = MainHighlightTime, + maxChars = 30, } local function toPrintableChar(v) if v>=0x20 and v<=0x7E then return string.char(v) else return "\\x"..string.format("%02X", v) end end -local function pdLinesFromDasm(dasm) - local div = " | " +local function pdLinesFromDasm(dasm, maxChars) + local div = "|" local lines, addrLines, lineAddrs = {}, {}, {} local function addDasmLine(addr, data, text) + text = text:sub(1, maxChars - #div*2 - 8) local dataStr = "" if data then local dt = {} @@ -274,7 +286,7 @@ local function pdLinesFromDasm(dasm) local datastr = {} for v in datah:gfind("[0-9a-fA-F][0-9a-fA-F]") do if #data>=maxlen then - addDasmLine(addr+len-#data, data, "\""..table.concat(datastr.."\"")) + addDasmLine(addr+len-#data, data, "\""..table.concat(datastr).."\"") data = {} datastr = {} end @@ -285,7 +297,7 @@ local function pdLinesFromDasm(dasm) if len<=maxlen then addDasmLine(addr, data, text) elseif #data>0 then - addDasmLine(addr+len-#data, data, "\""..table.concat(datastr.."\"")) + addDasmLine(addr+len-#data, data, "\""..table.concat(datastr).."\"") end end end @@ -298,7 +310,7 @@ local function InitProgramDisplay(pd, dasmText) lg.print("Program", pd.scrX, pd.scrY-12) lg.rectangle("line", pd.scrX, pd.scrY, pd.width, pd.height) pd.firstLine = 1 - pd.lines, pd.addrLines, pd.lineAddrs = pdLinesFromDasm(dasmText) + pd.lines, pd.addrLines, pd.lineAddrs = pdLinesFromDasm(dasmText, pd.maxChars) pd.midLine = math.floor(pd.numLines/2) pd.init = true end @@ -368,7 +380,7 @@ local function InitCharDisplay(cd) end local Keyboard = { - addrRange = {0x0500, 0x05FF}, + addrRange = {0xF100, 0xF1FF}, queueSize = 16, queue = {}, interrupts = false, @@ -392,7 +404,6 @@ local function KeyboardOnWrite(addr, cpu, mem, kb) 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 @@ -431,10 +442,11 @@ local function RedrawKeyInfo(x, y, uk, run) printHighlight("[R] "..(run and "Stop" or "Run "), 23, lk.isDown("r"), x, y) if not run then printHighlight("[S] Step" , 33, lk.isDown("s"), x, y) - end -- printHighlight("[T] Tick once" , 48, lk.isDown("t"), x, y) - printHighlight("[I] Interrupt" , 43, lk.isDown("i"), x, y) - printHighlight("[Q] Quit" , 70, lk.isDown("q"), x, y) + printHighlight("[X] Step Over" , 43, lk.isDown("x"), x, y) + end + printHighlight("[I] Interrupt" , 58, lk.isDown("i"), x, y) + printHighlight("[Q] Quit" , 73, lk.isDown("q"), x, y) end end @@ -471,12 +483,15 @@ local function gpioDiv(addr, cpu, mem, gpio) WriteMemory(mem, base+0x02, math.floor(gpio.divLeft/gpio.divRight)) WriteMemory(mem, base+0x03, gpio.divLeft%gpio.divRight) end +local function gpioPopcount(v) + return 0 -- todo +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, + [0x04] = function(addr, cpu, mem, gpio) WriteMemory(mem, addr, gpioPopcount(ReadMemory(mem, addr))) end, [0x05] = function(addr, cpu, mem, gpio) gpio.timerCount = 60/10; WriteMemory(mem, addr, 0); end } local function GPIOOnWrite(addr, cpu, mem, gpio) @@ -516,31 +531,124 @@ local function InitConsole(con) con.prevInputIdx = 0 con.tempError = nil end -local function RedrawConsole(con) +local function RedrawConsole(con, kb) lg.setColor(0,0,0) lg.rectangle("fill", con.scrX, con.scrY, con.width, con.height) lg.setColor(1,1,1) lg.rectangle("line", con.scrX, con.scrY, con.width, con.height) - if con.blinkFrame < con.blinkFrames/2 then - local curx, cury = con.scrX + #con.input*7 + 2, con.scrY+3 - lg.rectangle("fill", curx, cury, 8, 12) - end - con.blinkFrame = con.blinkFrame + 1 - if con.blinkFrame >= con.blinkFrames then con.blinkFrame = 0 end - if #con.input==0 then - lg.setColor(0.5,0.5,0.5) - lg.print(con.tempError or "Type a Command (@addr | addr=byte | addr=byte1 byte2 ...)", con.scrX+2+8, con.scrY+4) - else - lg.print(con.input, con.scrX+2, con.scrY+4) + if not kb then + if con.blinkFrame < con.blinkFrames/2 then + local curx, cury = con.scrX + #con.input*7 + 2, con.scrY+3 + lg.rectangle("fill", curx, cury, 8, 12) + end + con.blinkFrame = con.blinkFrame + 1 + if con.blinkFrame >= con.blinkFrames then con.blinkFrame = 0 end + if #con.input==0 then + lg.setColor(0.5,0.5,0.5) + lg.print(con.tempError or "Enter Command (>ticks | !addr | @addr | addr=byte | addr=byte1 byte2...)", con.scrX+2+8, con.scrY+4) + else + lg.print(con.input, con.scrX+2, con.scrY+4) + end end end local function ConsoleError(con, err) con.tempError = err end -local function ConsoleExec(con, mem) +local function conWriteDataBlock(con, mem, addr, data) + local writeTime = 2 + ConsoleError(con, "Writing") + for i, byte in ipairs(data) do + TimerSchedule((i-1)*writeTime, function() + if con.tempError ~= nil then + con.tempError = con.tempError .. "." + end + mem.c.data[(addr+i-1)%65536] = byte + PlaySound("write") + end) + end + TimerSchedule((#data-1)*writeTime, function() + PlaySound("writeDone") + PlaySound("success") + ConsoleError(con, "Wrote " .. #data .. " byte" .. (#data>1 and "s" or "") .. " to address $" .. string.format("%04X", addr)) + end) +end +local function tickCpuOnce(cpu, mem, byInstr) + TickCPU(cpu, mem, 1, byInstr, nil) +end +local function tickCpuUntil(con, cpu, mem, contfunc, strfunc, donestr, step) + con.tempError = "" + local tickTime = 1 + local tickStep, byInstr + if step then + tickStep = step + byInstr = true + else + tickStep = MaxTicksPerFrame + byInstr = false + end + local i = 1 + local recFunc + recFunc = function() + for j = 1, tickStep do + if contfunc(i) then + tickCpuOnce(cpu, mem, byInstr) + else + PlaySound("writeDone") + PlaySound("success") + ConsoleError(con, donestr) + return + end + i = i+1 + end + if con.tempError ~= nil then + con.tempError = strfunc(i) + end + PlaySound("write") + TimerSchedule(tickTime, recFunc) + end + recFunc() +end +local function tickCpuStepOver(con, cpu, mem, pd) + local stepOver = false + for _, v in ipairs(jsrInstrs) do + if cpu.c.instr == v then + stepOver = true + end + end + if not stepOver then + tickCpuOnce(cpu, mem, true) + return + end + + local line = pd.addrLines and pd.addrLines[(cpu.c.i-1)%65536] + if not line then + tickCpuOnce(cpu, mem, true) + return + end + local addr + while not addr do + line = line+1 + local addrs = pd.lineAddrs[line] + if not addrs then + tickCpuOnce(cpu, mem, true) + return + end + addr = addrs[1] + end + --print(string.format("%04X", addr)) + tickCpuUntil(con, cpu, mem, + --function() return (cpu.c.i ~= (addr+1)%65536) and (cpu.c.rfg==1) end, + function() return (cpu.c.i ~= (addr+1)%65536) end, + function() return "Running to address $" .. string.format("%04X", addr) end, + nil, + nil + ) +end +local function ConsoleExec(con, cpu, mem) PlaySound("enter") if con.input=="" then PlaySound("error") + con.tempError = nil return end @@ -564,6 +672,69 @@ local function ConsoleExec(con, mem) MemoryDisplays[1].addr = addr ConsoleError(con, "Memory display set to address $" .. string.format("%04X", addr)) PlaySound("success") + elseif ip:sub(1, 1)=="!" then + if RunCPU then + ConsoleError(con, "CPU must be paused to change execution") + PlaySound("error") + return + end + local rest = ip:sub(2, #ip) + local addr = tonumber(rest, 16) + if not addr then + ConsoleError(con, "Error: Expected a hex number") + PlaySound("error") + return + end + if addr<0 then addr = 0 end + if addr>65535 then addr = 65535 end + cpu.c.i = (addr+1)%65536 + cpu.c.cycle = 0 + cpu.c.instr = ReadMemory(mem, addr) + ConsoleError(con, "CPU instruction pointer set to $" .. string.format("%04X", addr)) + PlaySound("success") + elseif ip:sub(1, 2)==">>" then + if RunCPU then + ConsoleError(con, "CPU must be paused to step") + PlaySound("error") + return + end + local rest = ip:sub(3, #ip) + local addr = tonumber(rest, 16) + if not addr then + ConsoleError(con, "Error: Expected a hex number") + PlaySound("error") + return + end + if addr<0 then addr = 0 end + if addr>65535 then addr = 65535 end + tickCpuUntil(con, cpu, mem, + function() return cpu.c.i ~= (addr+1)%65536 end, + function() return "Running to address $" .. string.format("%04X", addr) end, + "Ran to address " .. string.format("%04X", addr), + nil + ) + elseif ip:sub(1, 1)==">" then + if RunCPU then + ConsoleError(con, "CPU must be paused to single-step") + PlaySound("error") + return + end + local rest = ip:sub(2, #ip) + local steps = tonumber(rest, 16) + if rest=="" then steps = 1 end + if not steps then + ConsoleError(con, "Error: Expected a hex number") + PlaySound("error") + return + end + if steps<1 then steps = 1 end + if steps>65535 then steps = 65535 end + tickCpuUntil(con, cpu, mem, + function(i) return i<=steps; end, + function(i) return "Step $"..string.format("%X", i).."/$"..string.format("%X", steps) end, + "Ran $" .. string.format("%02X", steps) .. " steps", + steps<(MaxTicksPerFrame/4) and steps or nil + ) elseif ip:find("=") then local addrS, rest = ip:match("^ *([0-9a-fA-F]+) *= *([0-9a-fA-F ]+)$") local addr = tonumber(addrS or "", 16) @@ -582,27 +753,27 @@ local function ConsoleExec(con, mem) end table.insert(data, byte) end - for i, byte in ipairs(data) do - mem.c.data[(addr+i-1)%65536] = byte - PlaySound("write", i) - end - PlaySound("writeDone", #data) - PlaySound("success", #data) - ConsoleError(con, "Wrote " .. #data .. " byte" .. (#data>1 and "s" or "") .. " to address $" .. string.format("%04X", addr)) + + conWriteDataBlock(con, mem, addr, data) + else + ConsoleError(con, "Error: Unknown command") + PlaySound("error") end end local function shiftDown() return lk.isDown("lshift") or lk.isDown("rshift") end -local function ConsoleKey(con, k, mem) +local function ConsoleKey(con, k, cpu, mem) local add if k=="backspace" then con.input = con.input:sub(1, #con.input-1) + elseif shiftDown() and k=="1" then add = "!" elseif shiftDown() and k=="2" then add = "@" + elseif shiftDown() and k=="." then add = ">" elseif k>="0" and k<="9" then add = k elseif #k==1 and k>="a" and k<="f" then add = k:upper() elseif k=="=" then add = "=" elseif k=="space" then add = " " - elseif k=="return" then ConsoleExec(con, mem) + elseif k=="return" then ConsoleExec(con, cpu, mem) elseif k=="up" and con.prevInputs[con.prevInputIdx+1] then if con.prevInputIdx==0 then con.prevInputs[0] = con.input end con.prevInputIdx = con.prevInputIdx + 1 @@ -637,6 +808,10 @@ local soundNames = { "success", "write","writeDone", } +local soundCounts = { + --["step"] = 100, + ["write"] = 100, +} local function InitSound() for _, name in ipairs(soundNames) do local sound = { @@ -645,33 +820,36 @@ local function InitSound() lastPlayed = 0, } local fn = "content/" .. name .. ".wav" - for i = 1, 4 do + for i = 1, soundCounts[name] or 4 do table.insert(sound.sources, la.newSource(fn, "static")) end Sounds[name] = sound end end -local SoundFrame = 0 -local QueuedSounds = {} -PlaySound = function(name, delay) - if (not delay) or delay==0 then - local sound = Sounds[name] or error("no sound with name: " .. name) - sound.lastPlayed = (sound.lastPlayed % #sound.sources) + 1 - sound.sources[sound.lastPlayed]:play() - else - local time = SoundFrame + delay - QueuedSounds[time] = QueuedSounds[time] or {} - table.insert(QueuedSounds[time], name) - end +PlaySound = function(name) + local sound = Sounds[name] or error("no sound with name: " .. name) + sound.lastPlayed = (sound.lastPlayed % #sound.sources) + 1 + sound.sources[sound.lastPlayed]:play() end -local function UpdateSound() - if QueuedSounds[SoundFrame] then - for _, name in ipairs(QueuedSounds[SoundFrame]) do - PlaySound(name) + +---- +-- Timer + +local TimerFrame = 0 +local TimerSchedules = {} +TimerSchedule = function(time, func) + local frame = TimerFrame + time + TimerSchedules[frame] = TimerSchedules[frame] or {} + table.insert(TimerSchedules[frame], func) +end +local function UpdateTimer() + if TimerSchedules[TimerFrame] then + for _, func in ipairs(TimerSchedules[TimerFrame]) do + func() end - QueuedSounds[SoundFrame] = nil + TimerSchedules[TimerFrame] = nil end - SoundFrame = SoundFrame + 1 + TimerFrame = TimerFrame + 1 end @@ -777,7 +955,7 @@ local CPU = { c = ffi.new("struct CPU"), } local cpuDll = ffi.load("8608emulator.dll") -local function TickCPU(cpu, mem, count, countinstrs, breakaddr) +TickCPU = function(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) @@ -792,10 +970,6 @@ CPURequestInterrupt = function(cpu) cpu.c.irq = 1; end -function RunToNextInstr(cpu) - -end - ---- --local function RedrawVideoDisplay(vd, mem) @@ -825,20 +999,32 @@ local function RedrawCharDisplay(cd, mem) local acolor = cd.addrColor + abase local colormem = ReadMemory(mem, acolor) local colorid = colormem%64 - local highlight = colormem>=128 + local highlight = PrintMode or colormem>=128 lg.setColor(ColorSet[colorid]) + local scrx = cd.scrX + cx*cd.fontWidth + local scry = cd.scrY + cy*cd.fontHeight + local scrw = cd.fontWidth + local scrh = cd.fontHeight 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) + lg.rectangle("fill", scrx, scry, scrw, scrh) + if PrintMode then lg.setColor(1,1,1) else lg.setColor(0,0,0) end end local val = ReadMemory(mem, achar)%128 - if val>=32 then + if val>=0x20 and val<=0x7F then -- printable ascii 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 + lg.print(char, scrx, scry) + elseif val>=0x10 and val<=0x17 then -- solid color 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) + lg.rectangle("fill", scrx, scry, scrw, scrh) + elseif val>=0x80 and val<=0x8F then -- 2x2 pixels + lg.setColor(0,0,0) + if val %2==1 then lg.rectangle("fill", scrx+scrw/2, scry+scrh/2, scrw/2, scrh/2) end -- bottom right + if math.floor(val/2)%2==1 then lg.rectangle("fill", scrx , scry+scrh/2, scrw/2, scrh/2) end -- bottom left + if math.floor(val/4)%2==1 then lg.rectangle("fill", scrx+scrw/2, scry , scrw/2, scrh/2) end -- top right + if math.floor(val/4)%2==1 then lg.rectangle("fill", scrx , scry , scrw/2, scrh/2) end -- top left + elseif val>=0xA0 and val<=0xDF then -- kana + -- todo end end end @@ -861,9 +1047,9 @@ local function RedrawWindow(usekeyboard, runcpu) 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) - RedrawConsole(Console) + RedrawFPSCounter(128, 4) + RedrawKeyInfo(128+64+16, 4, usekeyboard, runcpu) + RedrawConsole(Console, usekeyboard) lg.setCanvas() end @@ -955,13 +1141,14 @@ local function endFrame() end local RunCPU = false -local CPUSpeed = 555 +local CPUSpeed = MaxTicksPerFrame local UseKeyboard = false function love.draw() startFrame() + UpdateTimer() + UpdateGPIO(GPIO, CPU, Memory) - UpdateSound() CPU.c.frame = CPU.c.frame + 1 if RunCPU then @@ -984,14 +1171,14 @@ function love.keypressed(k) KeyboardOnKey(Keyboard, k, true, CPU, Memory) else if k=="q" then le.quit() - elseif k=="s" and not RunCPU then TickCPU(CPU, Memory, 1, true , nil); PlaySound("step"); + elseif k=="s" and (not RunCPU) then tickCpuOnce(CPU, Memory, true); PlaySound("step"); + elseif k=="x" and (not RunCPU) then tickCpuStepOver(Console, CPU, Memory, ProgramDisplay); PlaySound("step"); --elseif k=="t" then TickCPU(CPU, Memory, 1, false, nil) - --elseif k=="o" then RunToNextInstr(cpu) elseif k=="r" then RunCPU = not RunCPU; PlaySound(RunCPU and "runOn" or "runOff"); elseif k=="i" then CPU.c.irq = 1; PlaySound("interrupt"); --elseif k=="u" then CPU.c.rfg = 1 else - ConsoleKey(Console, k, Memory) + ConsoleKey(Console, k, CPU, Memory) end end end diff --git a/emulator/instructions_gen.c b/emulator/instructions_gen.c index 643a30d..13af309 100644 --- a/emulator/instructions_gen.c +++ b/emulator/instructions_gen.c @@ -414,7 +414,7 @@ void cpu_instr_227_0(struct CPU* const cpu, struct Memory* const mem) { cpu->b=l void cpu_instr_227_1(struct CPU* const cpu, struct Memory* const mem) { lni; } void cpu_instr_229_0(struct CPU* const cpu, struct Memory* const mem) { loadimmedt cpu->cycle++; } void cpu_instr_229_1(struct CPU* const cpu, struct Memory* const mem) { loadstackrelu cpu->cycle++; } -void cpu_instr_229_2(struct CPU* const cpu, struct Memory* const mem) { addf(cpu->a,-cpu->u+cpu->cf); lni; } +void cpu_instr_229_2(struct CPU* const cpu, struct Memory* const mem) { addf(cpu->a,~cpu->u+cpu->cf); lni; } void cpu_instr_230_0(struct CPU* const cpu, struct Memory* const mem) { loadimmedt cpu->cycle++; } void cpu_instr_230_1(struct CPU* const cpu, struct Memory* const mem) { loadstackrelu cpu->cycle++; } void cpu_instr_230_2(struct CPU* const cpu, struct Memory* const mem) { instrpreload; addf(cpu->u, 1 ); cpu->cycle++; } @@ -424,7 +424,11 @@ void cpu_instr_231_1(struct CPU* const cpu, struct Memory* const mem) { loadstac void cpu_instr_231_2(struct CPU* const cpu, struct Memory* const mem) { subf(cpu->a,cpu->u); lni; } void cpu_instr_232_0(struct CPU* const cpu, struct Memory* const mem) { loadimmedt cpu->cycle++; } void cpu_instr_232_1(struct CPU* const cpu, struct Memory* const mem) { cpu->p=(cpu->p+signed8(cpu->t))%65536; lni; } +void cpu_instr_233_0(struct CPU* const cpu, struct Memory* const mem) { loadimmedt cpu->cycle++; } +void cpu_instr_233_1(struct CPU* const cpu, struct Memory* const mem) { addf(cpu->a,~cpu->t+cpu->cf); lni; } void cpu_instr_234_0(struct CPU* const cpu, struct Memory* const mem) { lni; } +void cpu_instr_235_0(struct CPU* const cpu, struct Memory* const mem) { loadimmedt cpu->cycle++; } +void cpu_instr_235_1(struct CPU* const cpu, struct Memory* const mem) { subf(cpu->a,cpu->t); lni; } void cpu_instr_237_0(struct CPU* const cpu, struct Memory* const mem) { loadimm161 cpu->cycle++; } void cpu_instr_237_1(struct CPU* const cpu, struct Memory* const mem) { loadimm162 cpu->cycle++; } void cpu_instr_237_2(struct CPU* const cpu, struct Memory* const mem) { cpu->a=loadut; setzf(cpu->a); cpu->cycle++; } @@ -444,7 +448,7 @@ void cpu_instr_243_0(struct CPU* const cpu, struct Memory* const mem) { cpu->b=l void cpu_instr_243_1(struct CPU* const cpu, struct Memory* const mem) { lni; } void cpu_instr_246_0(struct CPU* const cpu, struct Memory* const mem) { addf(cpu->a, 1 ); lni; } void cpu_instr_247_0(struct CPU* const cpu, struct Memory* const mem) { cpu->c=cpu->b; lni; } -void cpu_instr_249_0(struct CPU* const cpu, struct Memory* const mem) { addf(cpu->a,-cpu->c+cpu->cf); lni; } +void cpu_instr_249_0(struct CPU* const cpu, struct Memory* const mem) { addf(cpu->a,~cpu->c+cpu->cf); lni; } void cpu_instr_250_0(struct CPU* const cpu, struct Memory* const mem) { addf(cpu->c, 1 ); lni; } void cpu_instr_251_0(struct CPU* const cpu, struct Memory* const mem) { subf(cpu->a,cpu->c); lni; } void cpu_instr_252_0(struct CPU* const cpu, struct Memory* const mem) { cpu->p=(cpu->p+signed8(cpu->a))%65536; lni; } @@ -686,9 +690,9 @@ CPUInstruction CPUInstructions[256][8] = { {cpu_instr_230_0,cpu_instr_230_1,cpu_instr_230_2,cpu_instr_230_3,0,0,0,0}, {cpu_instr_231_0,cpu_instr_231_1,cpu_instr_231_2,0,0,0,0,0}, {cpu_instr_232_0,cpu_instr_232_1,0,0,0,0,0,0}, - {0,0,0,0,0,0,0,0}, + {cpu_instr_233_0,cpu_instr_233_1,0,0,0,0,0,0}, {cpu_instr_234_0,0,0,0,0,0,0,0}, - {0,0,0,0,0,0,0,0}, + {cpu_instr_235_0,cpu_instr_235_1,0,0,0,0,0,0}, {0,0,0,0,0,0,0,0}, {cpu_instr_237_0,cpu_instr_237_1,cpu_instr_237_2,cpu_instr_237_3,0,0,0,0}, {0,0,0,0,0,0,0,0}, diff --git a/emulator/keycodes.lua b/emulator/keycodes.lua index 256dedd..795b0fb 100644 --- a/emulator/keycodes.lua +++ b/emulator/keycodes.lua @@ -1,18 +1,19 @@ --- These are the keycodes provided by the keyboard peripheral to the CPU within the emulator. + -- copied from Brick_LuaLogic/bricks/input/keyboard-global.lua +-- with some key names changed from Torque format to LOVE format. return { ["backspace"] = 8, ["tab"] = 9, ["return"] = 13, - ["lshift"] = 16, - ["lcontrol"] = 17, + ["lshift"] = 16, -- 0x10 + ["lctrl"] = 17, --["lcontrol"] = 17, ["lalt"] = 18, -- this block does not match vkey codes - ["rshift"] = 20, - ["rcontrol"] = 21, + ["rshift"] = 20, -- 0x14 + ["rctrl"] = 21, --["rcontrol"] = 21, ["ralt"] = 22, -- this block does not match vkey codes @@ -20,6 +21,7 @@ return { ["="] = 25, [","] = 26, ["."] = 27, + ["-"] = 28, -- not in bl ["/"] = 29, ["`"] = 30, @@ -50,7 +52,7 @@ return { ["["] = 60, ["\\"] = 61, ["]"] = 62, - ["apostrophe"] = 63, + ["\'"] = 63, --["apostrophe"] = 63, ["a"] = 65, ["b"] = 66, @@ -79,22 +81,22 @@ return { ["y"] = 89, ["z"] = 90, - ["numpad0"] = 96, - ["numpad1"] = 97, - ["numpad2"] = 98, - ["numpad3"] = 99, - ["numpad4"] = 100, - ["numpad5"] = 101, - ["numpad6"] = 102, - ["numpad7"] = 103, - ["numpad8"] = 104, - ["numpad9"] = 105, - ["*"] = 106, - ["+"] = 107, - ["numpadenter"] = 108, - ["minus"] = 109, - ["numpaddecimal"] = 110, - --["/"] = 111, -- already 29 + ["kp0"] = 96, --["numpad0"] = 96, + ["kp1"] = 97, --["numpad1"] = 97, + ["kp2"] = 98, --["numpad2"] = 98, + ["kp3"] = 99, --["numpad3"] = 99, + ["kp4"] = 100, --["numpad4"] = 100, + ["kp5"] = 101, --["numpad5"] = 101, + ["kp6"] = 102, --["numpad6"] = 102, + ["kp7"] = 103, --["numpad7"] = 103, + ["kp8"] = 104, --["numpad8"] = 104, + ["kp9"] = 105, --["numpad9"] = 105, + ["kp*"] = 106, --["*"] = 106, + ["kp+"] = 107, --["+"] = 107, + ["kpenter"] = 108, --["numpadenter"] = 108, + ["kp-"] = 109, --["minus"] = 109, + ["kp."] = 110, --["numpaddecimal"] = 110, + ["kp/"] = 111, --["/"] = 111, ["f1"] = 112, ["f2"] = 113, @@ -109,5 +111,5 @@ return { ["f11"] = 122, ["f12"] = 123, - ["invalid"] = 127, + --["invalid"] = 127, } diff --git a/emulator/temp/temp.bin b/emulator/temp/temp.bin index a13e26f115cc9cc843d1a7337028bc620723479b..87d663107c4e9de799df6b9a2c7c0f413dce9121 100644 GIT binary patch delta 450 zcmZo@U}d;_Ui?EvKbv8l;0nX%d<E3~pcSN6P21t%RsI!T#wvDN=i>hfLj&k7 zkf$Dg;Q0K3j\x1C-_\x1D/?\x1E`~" + fd05:0 | fd05 | 3c 5b 7b 3d 5c 7c 3e 5d 7d 3f 27 22 ; "\x3C[{\x3D\\|\x3E]}\x3F'\x22" + fd11:0 | fd11 | 0d 0a 0a ; "\x0d\x0A\x0A" + fd14:0 | fd14 | 20 20 20 08 08 08 ; " \x08\x08\x08" + fd1a:0 | fd1a | 00 ; "\0" + fd1b:0 | fd1b | ; GETCHAR: + fd1b:0 | fd1b | 5c ; PHB + fd1c:0 | fd1c | ; GETCHAR_RESTART: + fd1c:0 | fd1c | 20 fc cc ; JSR GETKEY + fd1f:0 | fd1f | b7 ; TAB + fd20:0 | fd20 | 29 7b ; AND #$7B + fd22:0 | fd22 | c9 10 ; CMP #$10 + fd24:0 | fd24 | f0 2c ; BEQ SHIFTKEY + fd26:0 | fd26 | 97 ; TBA + fd27:0 | fd27 | c9 80 ; CMP #$80 + fd29:0 | fd29 | 90 f1 ; BLT GETCHAR_RESTART + fd2b:0 | fd2b | 29 7f ; AND #$7F + fd2d:0 | fd2d | c9 41 ; CMP #$41 + fd2f:0 | fd2f | 90 04 ; BLT NOT_LETTER + fd31:0 | fd31 | c9 5a ; CMP #$5A + fd33:0 | fd33 | 10 1b ; BLE GETCHAR_RETURN + fd35:0 | fd35 | ; NOT_LETTER: + fd35:0 | fd35 | 54 ; PHX + fd36:0 | fd36 | aa fc d2 ; LDX #KEY_SYMBOLS + fd39:0 | fd39 | ; KEY_SYM_LP: + fd39:0 | fd39 | e3 ; LDB X+ + fd3a:0 | fd3a | f0 11 ; BEQ KEY_SYM_DONE + fd3c:0 | fd3c | ; KEY_SYM_CONTINUE: + fd3c:0 | fd3c | dd ; CMP B + fd3d:0 | fd3d | f0 04 ; BEQ KEY_SYM_MATCH + fd3f:0 | fd3f | e8 02 ; ADX #2 + fd41:0 | fd41 | 98 f6 ; BRA KEY_SYM_LP + fd43:0 | fd43 | ; KEY_SYM_MATCH: + fd43:0 | fd43 | a7 02 ; LDB SHIFTDOWN + fd45:0 | fd45 | f0 02 ; BEQ KEY_SYM_NOSHIFT + fd47:0 | fd47 | e8 01 ; ADX #1 + fd49:0 | fd49 | ; KEY_SYM_NOSHIFT: + fd49:0 | fd49 | a1 ; LDA X + fd4a:0 | fd4a | 74 ; PLX + fd4b:0 | fd4b | 98 03 ; BRA GETCHAR_RETURN + fd4d:0 | fd4d | ; KEY_SYM_DONE: + fd4d:0 | fd4d | 74 ; PLX + fd4e:0 | fd4e | 98 cc ; BRA GETCHAR_RESTART + fd50:0 | fd50 | ; GETCHAR_RETURN: + fd50:0 | fd50 | 7c ; PLB + fd51:0 | fd51 | 60 ; RTS + fd52:0 | fd52 | ; SHIFTKEY: + fd52:0 | fd52 | 97 ; TBA + fd53:0 | fd53 | 29 80 ; AND #$80 + fd55:0 | fd55 | 85 02 ; STA SHIFTDOWN + fd57:0 | fd57 | 98 c3 ; BRA GETCHAR_RESTART + fd59:0 | fd59 | ; GETSTRING: + fd59:0 | fd59 | 48 ; PHA + fd5a:0 | fd5a | 54 ; PHX + fd5b:0 | fd5b | ; GETSTRING_LOOP: + fd5b:0 | fd5b | 20 fd 1b ; JSR GETCHAR + fd5e:0 | fd5e | 20 fc 20 ; JSR PRINTCHAR + fd61:0 | fd61 | c1 ; STA X+ + fd62:0 | fd62 | c9 08 ; CMP #$08 + fd64:0 | fd64 | d0 02 ; BNE GETSTRING_NOBK + fd66:0 | fd66 | e8 fe ; ADX #-2 + fd68:0 | fd68 | ; GETSTRING_NOBK: + fd68:0 | fd68 | c9 0a ; CMP #$0A + fd6a:0 | fd6a | d0 ef ; BNE GETSTRING_LOOP + fd6c:0 | fd6c | e8 ff ; ADX #-1 + fd6e:0 | fd6e | a9 00 ; LDA #$00 + fd70:0 | fd70 | 81 ; STA X + fd71:0 | fd71 | 74 ; PLX + fd72:0 | fd72 | 68 ; PLA + fd73:0 | fd73 | 60 ; RTS + fd74:0 | fd74 | ; STRCMP: + fd74:0 | fd74 | 54 ; PHX + fd75:0 | fd75 | 14 ; PHY + fd76:0 | fd76 | 48 ; PHA + fd77:0 | fd77 | 5c ; PHB + fd78:0 | fd78 | ; STRCMP_NEXTCHAR: + fd78:0 | fd78 | e1 ; LDA X+ + fd79:0 | fd79 | f0 08 ; BEQ STRCMP_XOVER + fd7b:0 | fd7b | f3 ; LDB Y+ + fd7c:0 | fd7c | f0 08 ; BEQ STRCMP_YOVER + fd7e:0 | fd7e | dd ; CMP B + fd7f:0 | fd7f | f0 f7 ; BEQ STRCMP_NEXTCHAR + fd81:0 | fd81 | 98 06 ; BRA STRCMP_RETURN + fd83:0 | fd83 | ; STRCMP_XOVER: + fd83:0 | fd83 | b1 ; LDA Y + fd84:0 | fd84 | 98 03 ; BRA STRCMP_RETURN + fd86:0 | fd86 | ; STRCMP_YOVER: + fd86:0 | fd86 | e8 ff ; ADX #-1 + fd88:0 | fd88 | a1 ; LDA X + fd89:0 | fd89 | ; STRCMP_RETURN: + fd89:0 | fd89 | 7c ; PLB + fd8a:0 | fd8a | 68 ; PLA + fd8b:0 | fd8b | 34 ; PLY + fd8c:0 | fd8c | 74 ; PLX + fd8d:0 | fd8d | 60 ; RTS + fd8e:0 | fd8e | ; SLEEP: + fd8e:0 | fd8e | cd f0 05 ; STA TIMER + fd91:0 | fd91 | 18 ; HLT + fd92:0 | fd92 | 60 ; RTS + fd93:0 | fd93 | ; STR_HI: + fd93:0 | fd93 | 48 49 00 ; "HI\0" + fd96:0 | fd96 | ; STR_HIRES: + fd96:0 | fd96 | 48 45 4c 4c 4f 21 0a 00 ; "HELLO!\n\0" + fd9e:0 | fd9e | ; RESET: + fd9e:0 | fd9e | 20 fc a0 ; JSR CLEARSCREEN + fda1:0 | fda1 | 58 ; CLI + fda2:0 | fda2 | ; INPUTLP: + fda2:0 | fda2 | aa 02 00 ; LDX #$0200 + fda5:0 | fda5 | 20 fd 59 ; JSR GETSTRING + fda8:0 | fda8 | a8 fd 93 ; LDY #STR_HI + fdab:0 | fdab | 20 fd 74 ; JSR STRCMP + fdae:0 | fdae | d0 f2 ; BNE INPUTLP + fdb0:0 | fdb0 | aa fd 96 ; LDX #STR_HIRES + fdb3:0 | fdb3 | 20 fc 92 ; JSR PRINTSTRING + fdb6:0 | fdb6 | 98 ea ; BRA INPUTLP + fdb8:0 | fdb8 | 18 ; HLT + fdb9:0 | fdb9 | 41 ; RST + fdba:0 | fdba | ; INTERRUPT: + fdba:0 | fdba | 38 ; RUN + fdbb:0 | fdbb | 40 ; RTI + fffc:0 | fffc | fd 9e ; RESET + fffe:0 | fffe | fd ba ; INTERRUPT diff --git a/instruction-map.ods b/instruction-map.ods index 5d7d6324f18945bff24258aa6cb2417cf549e224..8da23106adaf6735a6035e6e34c51b8d64d2293f 100644 GIT binary patch delta 5944 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 G#{U7#u}dKU diff --git a/instructionList.txt b/instructionList.txt index 43516f3..ef7614e 100644 --- a/instructionList.txt +++ b/instructionList.txt @@ -32,7 +32,9 @@ ICC zpg 47 4 Add the Carrry Flag to the value at an given 8-bit address, 8-bit Arithmetic and Logic (A): ADD # 6B 2 Add a given 8-bit value to the value in the A register, and set the Carry Flag and Zero Flag according to the result +SUB # EB 2 Subtract a given 8-bit value from the value in the A register, and set the Carry Flag and Zero Flag according to the result ADC # 69 2 Add a given 8-bit value plus the Carry Flag to the value in the A register, and set the Carry Flag and Zero Flag according to the result +SBC # E9 2 Subtract a given 8-bit value from the value in the A register including the Carry Flag, and set the Carry Flag and Zero Flag according to the result CMP # C9 2 Set the Carry Flag and Zero Flag according to the result of subtracting a given 8-bit value from the value in the A register AND # 29 2 Bitwise AND the value in the A register with a given 8-bit value, and set the Zero Flag according to the result ORA # 09 2 Bitwise OR the value in the A register with a given 8-bit value, and set the Zero Flag according to the result @@ -205,7 +207,7 @@ TXV 96 1 Copy the 16-bit value in the X register into the 16-bit Inte TAS 95 1 Copy the value in the A register into the 8-bit Stack Pointer register TSA B5 1 Copy the 8-bit Stack Pointer into the A register -Opcodes used: 189/256 +Opcodes used: 191/256 0123456789ABCDEF 00 | CC-BJA-BSA--JJ-- 10 | B--BSA-MCA-BSA-B @@ -221,5 +223,5 @@ A0 | WBWBWBWBWBWBWWWW B0 | BBWBMMMMWBWBWBWB C0 | WB-B-AU-XA--JB-B D0 | BBWB--UM-AU-XAU- -E0 | WBWB-AUAX-C--B-B +E0 | WBWB-AUAXACA-B-B F0 | BBWB--UM-AUAXAUA diff --git a/readme.md b/readme.md index 5eec793..bce650a 100644 --- a/readme.md +++ b/readme.md @@ -3,12 +3,12 @@ For a quick guide on how to use this project, see [getting-started.md](getting-started.md)
For a detailed description of the architecture, see [architecture.md](architecture.md)
-For a list of instructions, see [instructionList.txt](instructionList.txt) +For a list of instructions, see [instructionList.txt](instructionList.txt)
For a memory map of the 8608-based computer, see [memoryMap.md](memoryMap.md) -To use the emulator, simply drag-and-drop an assembly code file onto [emulator/8608-emulator.bat](emulator/8608-emulator.bat) +To use the emulator, simply drag-and-drop an assembly code file onto [emulator/8608-emulator.bat](emulator/8608-emulator.bat)
You must include the 8608 architecture file into your assembly code: [8608.asm](8608.asm)
-`#include "8608/8608.asm"`
+`#include "8608/8608.asm"` Note: This project depends on several pieces of free and open-source software,
whose binaries are included in the repository: