diff --git a/assembler-8608.lua b/assembler-8608.lua index 912566d..8969f13 100644 --- a/assembler-8608.lua +++ b/assembler-8608.lua @@ -14,9 +14,9 @@ end local function decodeNumber(n) n = trim(n) local sign = 1; if n:sub(1, 1)=="-" then sign = -1; n = n:sub(2, #n); end; - if n:sub(1, 1)=="$" then return sign*tonumber(n:sub(2, #n ), 16), math.ceil((#n-1)/2) - elseif n:sub(1, 2)=="0x" then return sign*tonumber(n:sub(3, #n ), 16), math.ceil((#n-2)/2) - elseif n:sub(#n, #n)=="h" then return sign*tonumber(n:sub(1, #n-1), 16), math.ceil((#n-1)/2) + if n:sub(1, 1)=="$" then return sign*(tonumber(n:sub(2, #n ), 16) or error("invalid number "..n)), math.ceil((#n-1)/2) + elseif n:sub(1, 2)=="0x" then return sign*(tonumber(n:sub(3, #n ), 16) or error("invalid number "..n)), math.ceil((#n-2)/2) + elseif n:sub(#n, #n)=="h" then return sign*(tonumber(n:sub(1, #n-1), 16) or error("invalid number "..n)), math.ceil((#n-1)/2) else local v = tonumber(n) or error("invalid number "..n) if v>=-128 and v<=255 then return v, 1 @@ -34,13 +34,15 @@ local function mnemFromLine(line, instrs, validWords) end local function addLabel(n) local len = 2 - if instrs[line:gsub(trim(n), "imm8", 1, true)] then len = 1 end + local linei8 = line:gsub(trim(n), "imm8", 1, true) + if instrs[linei8] then len = 1 end n = trim(n) table.insert(imms, { label = n, len = len } ) return " imm"..(len*8).." " end local mnem = " "..line:gsub(" ", " ").." " + mnem = mnem:gsub("([%*%+%-])", " %1 ") mnem = mnem:gsub(" %-?%$[0-9a-fA-F]+ " , function(n) return addNum (n) end) mnem = mnem:gsub(" %-?0x[0-9a-fA-F]+ " , function(n) return addNum (n) end) mnem = mnem:gsub(" %-?[0-9a-fA-F]+h " , function(n) if not validWords[trim(n)] then return addNum (n) end end) @@ -70,7 +72,7 @@ local function addSpace(state, len) end local function assembleInstruction(line, state, instrs, validWords) local mnem, imms = mnemFromLine(line, instrs, validWords) - local opcode = instrs[mnem] or error("invalid instruction "..line.." (mnem "..mnem..")") + local opcode = instrs[mnem] or error("invalid instruction \""..line.."\" (mnem \""..mnem.."\")") local writeimms = true local padlen = 0 if type(opcode)=="function" then @@ -116,6 +118,7 @@ local function assembleCode(code, instrs) } for line in code:gmatch("[^\n]+") do + line = trim(line) if line:sub(1, 1)=="." then -- directive local dir, rest = line:match("^%.([^ ]+) *(.*)$") assert(dir and rest, "no directive on line "..line) @@ -151,6 +154,16 @@ end local function separateCommas(l) local c = {}; for a in l:gmatch("[^,]+") do table.insert(c, trim(a)) end; return c; end +local function evaluateExpression(expr) + expr = expr:gsub("[^%+%-%*%/]+", function(word) + local val = decodeNumber(word) or error("invalid number in expression: "..word) + return val + end) + assert(not expr:find("[^a-zA-Z0-9_%(%)%+%-%*%/ \t\r\n]"), "invalid char in expression: "..expr) + local exprf = loadstring("return "..expr) + local eval = exprf() or error("invalid expr: "..expr) + return eval +end local function preprocessCode(code) local funcmacros = {} code = code:gsub(".define ([a-zA-Z0-9_]+)%(([^%)]+)%) ([^\n]+)", function(name, args, repl) @@ -171,15 +184,36 @@ local function preprocessCode(code) for name, replf in pairs(funcmacros) do code = code:gsub(name.." *%(([^%)]+)%)", replf) end local simplemacros = {} - code = code:gsub("%.define ([a-zA-Z0-9_]+) ([^\n]+)", function(name, repl) + code = code:gsub("%.define +([a-zA-Z0-9_]+) +([^\n]+)", function(name, repl) assert(not simplemacros[name], "Redefinition of macro "..name) simplemacros[name] = repl return "" end) - for name, repl in pairs(simplemacros) do code = code:gsub(name, repl, 1, true) end + --for name, repl in pairs(simplemacros) do code = code:gsub(name, repl, 1, true) end + for name, repl in pairs(simplemacros) do code = code:gsub(name, repl) end code = code:gsub("\\", "\n") + local codet = {} + local exprt = {} + local parenLevel = 0 + for i = 1, #code do + local c = code:sub(i, i) + if c=="(" then + parenLevel = parenLevel+1 + elseif c==")" then + parenLevel = parenLevel-1 + if parenLevel==0 then + table.insert(codet, evaluateExpression(table.concat(exprt))) + exprt = {} + end + else + if parenLevel==0 then table.insert(codet, c) + else table.insert(exprt, c) end + end + end + code = table.concat(codet) + return code end local function fixCode(code) @@ -192,9 +226,13 @@ local function fixCode(code) code = code:gsub("\n[ \t\r\n]*", "\n") code = code:gsub(" +", " ") local curScope = "" - code = code:gsub("([a-zA-Z_%.][a-zA-Z0-9_%.]*)(:?)", function(name, colon) - if name:sub(1, 1)=="." and not directiveFunctions[name:sub(2, #name)] then name = curScope..name end - if colon==":" then curScope = name:match("^[^%.]+") end + code = code:gsub("(%.?)([a-zA-Z_][a-zA-Z0-9_%.]*)(:?)", function(dot, name, colon) + if directiveFunctions[name] then return dot..name..colon end + if dot=="." then + assert(curScope~="", "scoped label before any unscoped label: "..name) + name = curScope.."."..name + end + if colon==":" and not name:find("^_BRACE_") then curScope = name:match("^[^%.]+") end return name..colon end) @@ -203,10 +241,12 @@ end local stringEscapes = { ["\\"] = "\\", ["n"] = "\n", ["r"] = "\r", ["t"] = "\t", ["0"] = "\0", ["\""] = "\"", ["\'"] = "\'", } local function prefixCode(code, fn) -- fix strings, add line numbers local outt = {} + local outnextnl = {} local linenum = 1 local function last() return outt[#outt] end local function out(c) assert(type(c)=="string"); table.insert(outt, c); end local function outn(n) out("$"..string.format("%02X", n).."\n"); end + local function outnext(c) assert(type(c)=="string"); table.insert(outnextnl, c); end local state = "code" -- code, comment, string, stringesc local skipnl = false @@ -219,27 +259,29 @@ local function prefixCode(code, fn) -- fix strings, add line numbers out(".ln 1"); out("\n"); for i = 1, #code do local c = code:sub(i, i) + local cn = code:sub(i+1, i+1) if state=="code" then if c=="\r" then - elseif c=="\n" then + elseif c=="\n" or c=="/" then linenum = linenum+1 - if skipnl then out("\\"); skipnl = false; + if skipnl then skipnl = false else out("\n") out(".ln "..linenum); out("\n"); end lastnl = true - elseif c=="\t" or c==" " then out(" ") - elseif c=="#" or c==";" or c=="/" then state = "comment" - elseif c=="\"" then state = "string" lastnl = false - elseif c=="\\" then skipnl = true - elseif c:find("^[a-zA-Z0-9_%.:%$%(%)%*,%[%]]$") then out(c) lastnl = false + for _, v in ipairs(outnextnl) do out(v) end; outnextnl = {}; + elseif c=="#" or c==";" or (c=="/" and cn=="/") then state = "comment" + elseif c=="\t" or c==" " then if (not lastnl) then out(" ") end + elseif c=="\"" then state = "string" lastnl = false + elseif c=="\\" then skipnl = true; out("\\"); + elseif c:find("^[a-zA-Z0-9_%.:%$%(%)%*,%[%]%+%-%*%/]$") then out(c); lastnl = false elseif c=="{" then table.insert(bracestack, bracelabel()) - if not lastnl then out(bracestack[#bracestack].."MID") out("\n") end - out(bracestack[#bracestack].."START:") out("\n") + if not lastnl then out(bracestack[#bracestack].."MID") end + outnext(bracestack[#bracestack].."START:"); outnext("\n") elseif c=="}" then - if not lastnl then out(bracestack[#bracestack].."START") out("\n") end - out(bracestack[#bracestack].."END:") out("\n") - if not bracehasmid[#bracestack] then out(bracestack[#bracestack].."MID:") out("\n") end + if not lastnl then out(bracestack[#bracestack].."START") end + if not bracehasmid[#bracestack] then outnext(bracestack[#bracestack].."MID:") outnext("\n") end + outnext(bracestack[#bracestack].."END:") bracehasmid[#bracestack] = nil bracestack[#bracestack] = nil elseif c=="|" then @@ -248,7 +290,7 @@ local function prefixCode(code, fn) -- fix strings, add line numbers bracehasmid[#bracestack] = true else error("invalid char "..c) end elseif state=="comment" then - if c=="\n" then state = "code" out("\n") end + if c=="\n" then state = "code" out("\n") lastnl = true end elseif state=="string" then if c=="\\" then state = "stringesc" elseif c=="\"" then state = "code" @@ -259,15 +301,23 @@ local function prefixCode(code, fn) -- fix strings, add line numbers end assert(#bracestack==0, "unclosed brace") local code2 = table.concat(outt) + + print(code2) + return code2 end +local function fixFilename(fn) + fn = fn:gsub("[^a-zA-Z0-9_]", "_") + return fn +end local function includeFile(fn) fn = fn:gsub("\\", "/") local code = readFile(fn) code = prefixCode(code, fn) - code = ".fn "..fn.."\n"..code + local fnf = fixFilename(fn) + code = ".fn "..fnf.."\n"..code code = code:gsub(".include ([^\r\n]+)", function(fn2) - return "\n"..includeFile(fn2).."\n"..".fn "..fn.."\n" + return "\n"..includeFile(fn2).."\n"..".fn "..fnf.."\n" end) return code end @@ -288,7 +338,8 @@ local function instrsFromArch(arch) for _, instr in ipairs(arch.instructions) do if instr.mnem then local mnem = instr.mnem - mnem = mnem:gsub("%*([^ ])", "%* %1") + mnem = mnem:gsub("([%*%+%-])", " %1 ") + mnem = trim(mnem):gsub(" +", " ") instrs[mnem] = instr.opcode if mnem:find("%*") then instrs[mnem:gsub("%*", "%[").." ]"] = instr.opcode end end @@ -314,6 +365,7 @@ local function mnemsFromArch(arch) return mnems end local function disassembleMemory(mem, arch) + print("Disassembly:") local mnems = mnemsFromArch(arch) local addr = 0 local function nextByte() local b = mem[addr]; addr = addr+1; return b; end @@ -359,12 +411,14 @@ local function disassembleHex(hex, arch) end local function printMemory(mem) + print("Memory Dump:") local anynonempty = false local lastbase = -16 for base = 0, 0xFFF0, 16 do - local line = { string.format("%04X", base), " | " } + local line = { string.format("%04X", base), " |" } local nonempty = false for addr = base, base+15 do + if addr%4==0 then table.insert(line, " ") end if mem[addr] then nonempty = true table.insert(line, string.format("%02X", mem[addr]).." ") @@ -379,7 +433,7 @@ local function printMemory(mem) anynonempty = true end end - if not anynonempty then print("Memory empty") end + if not anynonempty then print("Empty") end print() end @@ -435,4 +489,4 @@ ts.eval [[ function AssembleFile(%fn, %romsize, %offset, %len) { luacall("AssembleFile", %fn, %romsize, %offset, %len); } ]] -if arg then AssembleFile(arg[1] or "../8608programs/keyboard.asm", "16 16 8", "0", "256") end +if arg then AssembleFile(arg[1] or "../8608programs/redos.asm", "16 16 8", "0", "256") end diff --git a/rom-8608-defs.lua b/rom-8608-defs.lua index 27f51e7..229522b 100644 --- a/rom-8608-defs.lua +++ b/rom-8608-defs.lua @@ -186,8 +186,8 @@ instructions = { { mnem="jpr imm8" , opcode=0x31, ncycles=2, {"loadImmed","memSaveT","instrSub1"}, {"jmpRelT"}, desc="I+=imm8" }, { mnem="jnz imm8" , opcode=0x30, ncycles=2, {"loadImmed","memSaveT","instrSub23Cond","instrNext0NZ" }, {}, {"instrNext"}, {"jmpRelT"}, desc="I+=imm8 if !Zero" }, { mnem="jpz imm8" , opcode=0x32, ncycles=2, {"loadImmed","memSaveT","instrSub23Cond","instrNext0Z" }, {}, {"instrNext"}, {"jmpRelT"}, desc="I+=imm8 if Zero" }, - { mnem="jlt imm8" , opcode=0x33, ncycles=2, {"loadImmed","memSaveT","instrSub23Cond","instrNext0NC" }, {}, {"instrNext"}, {"jmpRelT"}, desc="I+=imm8 if Carry" }, - { mnem="jge imm8" , opcode=0x34, ncycles=2, {"loadImmed","memSaveT","instrSub23Cond","instrNext0C" }, {}, {"instrNext"}, {"jmpRelT"}, desc="I+=imm8 if !Carry" }, + { mnem="jlt imm8" , opcode=0x33, ncycles=2, {"loadImmed","memSaveT","instrSub23Cond","instrNext0NC" }, {}, {"instrNext"}, {"jmpRelT"}, desc="I+=imm8 if !Carry" }, + { mnem="jge imm8" , opcode=0x34, ncycles=2, {"loadImmed","memSaveT","instrSub23Cond","instrNext0C" }, {}, {"instrNext"}, {"jmpRelT"}, desc="I+=imm8 if Carry" }, { mnem="jle imm8" , opcode=0x35, ncycles=2, {"loadImmed","memSaveT","instrSub23Cond","instrNext0C","instrNext0Z"}, {}, {"jmpRelT"}, {"instrNext"}, desc="I+=imm8 if !Zero&!Carry" }, { mnem="jgt imm8" , opcode=0x36, ncycles=2, {"loadImmed","memSaveT","instrSub23Cond","instrNext0C","instrNext0Z"}, {}, {"instrNext"}, {"jmpRelT"}, desc="I+=imm8 if Zero|Carry" },