Enable the import of files
parent
bee2943e05
commit
7fd7fb36bb
199
asm/asm.lua
199
asm/asm.lua
|
@ -7,24 +7,21 @@
|
|||
|
||||
AGPL v3
|
||||
See LICENSE.txt for more information
|
||||
|
||||
The Assembler accepts a list with following code items:
|
||||
|
||||
{CTYPE, LINENO, CODESTR, ADDRESS, OPCODES}
|
||||
{"code", 2, "move A, #1", 10, {0x1020, 0x0001}}
|
||||
|
||||
]]--
|
||||
|
||||
local version = "2.2"
|
||||
|
||||
-- Tok Elems {1, "add A, 1", "add A, 1 ; start value", CODESEC, 10, {0x1234, 0x001}}
|
||||
local LINENO = 1
|
||||
local CODESTR = 2
|
||||
local TXTLINE = 3
|
||||
local SECTION = 4
|
||||
local ADDRESS = 5
|
||||
local OPCODES = 6
|
||||
|
||||
-- Sections
|
||||
local DATASEC = 1
|
||||
local CODESEC = 2
|
||||
local TEXTSEC = 3
|
||||
local CTEXTSEC = 4
|
||||
local COMMENT = 5
|
||||
local CTYPE = 1
|
||||
local LINENO = 2
|
||||
local CODESTR = 3
|
||||
local ADDRESS = 4
|
||||
local OPCODES = 5
|
||||
|
||||
local tOpcodes = {}
|
||||
local tOperands = {}
|
||||
|
@ -74,19 +71,15 @@ for idx,s in pairs(Operands) do
|
|||
end
|
||||
|
||||
local tSections = {
|
||||
[".data"] = DATASEC,
|
||||
[".code"] = CODESEC,
|
||||
[".text"] = TEXTSEC,
|
||||
[".ctext"] = CTEXTSEC,
|
||||
[".data"] = true,
|
||||
[".code"] = true,
|
||||
[".text"] = true,
|
||||
[".ctext"] = true,
|
||||
}
|
||||
|
||||
-------------------------------------------------------------------------------
|
||||
-- Helper functions
|
||||
-------------------------------------------------------------------------------
|
||||
local strfind = string.find
|
||||
local strsub = string.sub
|
||||
local tinsert = table.insert
|
||||
|
||||
local function strsplit(s)
|
||||
local words = {}
|
||||
string.gsub(s, "([^%s,]+)", function(w)
|
||||
|
@ -95,23 +88,6 @@ local function strsplit(s)
|
|||
return words
|
||||
end
|
||||
|
||||
local function linessplit(text)
|
||||
local list = {}
|
||||
local pos = 1
|
||||
|
||||
while true do
|
||||
local first, last = strfind(text, "\n", pos)
|
||||
if first then -- found?
|
||||
tinsert(list, strsub(text, pos, first-1))
|
||||
pos = last+1
|
||||
else
|
||||
tinsert(list, strsub(text, pos))
|
||||
break
|
||||
end
|
||||
end
|
||||
return list
|
||||
end
|
||||
|
||||
local function constant(s)
|
||||
if s and string.sub(s, 1, 1) == "#" then
|
||||
if string.sub(s, 2, 2) == "$" then
|
||||
|
@ -188,12 +164,10 @@ local Asm = {}
|
|||
|
||||
function Asm:new(o)
|
||||
o = o or {}
|
||||
o.section = o.section or CODESEC
|
||||
o.address = o.address or 0
|
||||
o.support_namespaces = o.support_namespaces
|
||||
o.globals = {}
|
||||
o.symbols = {}
|
||||
o.errors = {}
|
||||
o.namespace_cnt = 1
|
||||
setmetatable(o, self)
|
||||
self.__index = self
|
||||
|
@ -201,26 +175,28 @@ function Asm:new(o)
|
|||
end
|
||||
|
||||
function Asm:err_msg(err)
|
||||
local s = string.format("%s(%d): %s!", self.filename, self.lineno or 0, err)
|
||||
append(self.errors, s)
|
||||
self.error = true
|
||||
error(string.format("%s(%d): %s!", self.filename, self.lineno or 0, err))
|
||||
end
|
||||
|
||||
function Asm:scanner(text)
|
||||
function Asm:scanner(text, filename)
|
||||
local lOut = {} -- {lineno, codestr, txtline}
|
||||
self.filename = filename
|
||||
self.lineno = 0
|
||||
|
||||
if not vm16.is_ascii(text) then
|
||||
return nil, "Error: Invalid ASCII file format!"
|
||||
self:err_msg("Invalid ASCII file format!")
|
||||
end
|
||||
for lineno, txtline in ipairs(linessplit(text)) do
|
||||
|
||||
append(lOut, {"file", 0, "test.c"})
|
||||
self.ctype = "code"
|
||||
|
||||
for lineno, txtline in ipairs(vm16.splitlines(text)) do
|
||||
local _, _, codestr = txtline:find("(.+);")
|
||||
codestr = string.trim(codestr or txtline)
|
||||
if string.byte(codestr, 1) == 59 then -- ';'
|
||||
append(lOut, {lineno, "", txtline})
|
||||
elseif codestr == "" then
|
||||
append(lOut, {lineno, "", txtline})
|
||||
else
|
||||
append(lOut, {lineno, codestr, txtline})
|
||||
if tSections[codestr] then
|
||||
self.ctype = string.sub(codestr, 2)
|
||||
elseif codestr ~= "" and string.byte(codestr, 1) ~= 59 then -- ';'
|
||||
append(lOut, {self.ctype, lineno, codestr})
|
||||
end
|
||||
end
|
||||
return lOut
|
||||
|
@ -246,6 +222,7 @@ function Asm:address_label(tok)
|
|||
self.symbols[self:postfix(label)] = self.address
|
||||
end
|
||||
tok[CODESTR] = codestr:sub(pos+1, -1)
|
||||
self.label = label
|
||||
end
|
||||
return tok
|
||||
end
|
||||
|
@ -263,16 +240,6 @@ function Asm:global_def(tok)
|
|||
return tok
|
||||
end
|
||||
|
||||
-- New assembler section
|
||||
function Asm:section_def(tok)
|
||||
local codestr = tok[CODESTR]
|
||||
if tSections[codestr] then
|
||||
self.section = tSections[codestr]
|
||||
tok[CODESTR] = ""
|
||||
end
|
||||
return tok
|
||||
end
|
||||
|
||||
function Asm:org_directive(tok)
|
||||
local codestr = tok[CODESTR]
|
||||
local _, _, addr = codestr:find("^%.org +([%$%x]+)$")
|
||||
|
@ -307,24 +274,12 @@ function Asm:operand(s)
|
|||
return
|
||||
end
|
||||
|
||||
function Asm:no_code(tok)
|
||||
local pos1, pos2 = tok[TXTLINE]:find(";")
|
||||
if pos1 == 1 and pos2 == 1 then
|
||||
return {tok[LINENO], tok[CODESTR], tok[TXTLINE], COMMENT, self.address, {}}
|
||||
else
|
||||
return {tok[LINENO], tok[CODESTR], tok[TXTLINE], self.section, self.address, {}}
|
||||
end
|
||||
end
|
||||
|
||||
function Asm:decode_code(tok)
|
||||
local codestr = tok[CODESTR]
|
||||
local words = strsplit(codestr)
|
||||
if codestr == "" then
|
||||
return self:no_code(tok)
|
||||
end
|
||||
if codestr == "namespace" then
|
||||
self.namespace_cnt = self.namespace_cnt + 1
|
||||
return self:no_code(tok)
|
||||
return
|
||||
end
|
||||
-- Aliases
|
||||
if words[2] == "=" then
|
||||
|
@ -337,7 +292,7 @@ function Asm:decode_code(tok)
|
|||
else
|
||||
self:err_msg("Invalid left value")
|
||||
end
|
||||
return self:no_code(tok)
|
||||
return
|
||||
end
|
||||
|
||||
-- Opcodes
|
||||
|
@ -346,7 +301,6 @@ function Asm:decode_code(tok)
|
|||
opcode = tOpcodes[words[1]]
|
||||
if not opcode then
|
||||
self:err_msg("Syntax error")
|
||||
return self:no_code(tok)
|
||||
end
|
||||
if #words == 2 and opcode < 4 then
|
||||
local num = constant(words[2]) % 1024
|
||||
|
@ -357,13 +311,8 @@ function Asm:decode_code(tok)
|
|||
opnd2, val2 = self:operand(words[3])
|
||||
end
|
||||
-- some checks
|
||||
-- if val1 and val2 then
|
||||
-- self:err_msg("Syntax error")
|
||||
-- return self:no_code(tok)
|
||||
-- end
|
||||
if not opnd1 and not opnd2 then
|
||||
self:err_msg("Syntax error")
|
||||
return self:no_code(tok)
|
||||
end
|
||||
-- code correction for all jump/branch opcodes: from '0' to '#0'
|
||||
if JumpInst[words[1]] then
|
||||
|
@ -375,16 +324,13 @@ function Asm:decode_code(tok)
|
|||
if val1 then tbl[#tbl+1] = val1 end
|
||||
if val2 then tbl[#tbl+1] = val2 end
|
||||
|
||||
tok = {tok[LINENO], tok[CODESTR], tok[TXTLINE], self.section, self.address, tbl}
|
||||
tok = {"code", tok[LINENO], tok[CODESTR], self.address, tbl}
|
||||
self.address = self.address + #tbl
|
||||
return tok
|
||||
end
|
||||
|
||||
function Asm:decode_data(tok)
|
||||
local codestr = tok[CODESTR]
|
||||
if codestr == "" then
|
||||
return self:no_code(tok)
|
||||
end
|
||||
local words = strsplit(codestr)
|
||||
local tbl = {}
|
||||
for _,word in ipairs(words) do
|
||||
|
@ -392,16 +338,14 @@ function Asm:decode_data(tok)
|
|||
append(tbl, value(word))
|
||||
end
|
||||
end
|
||||
tok = {tok[LINENO], tok[CODESTR], tok[TXTLINE], self.section, self.address, tbl}
|
||||
-- restore original label
|
||||
tok = {"data", tok[LINENO], self.label, self.address, tbl}
|
||||
self.address = self.address + #tbl
|
||||
return tok
|
||||
end
|
||||
|
||||
function Asm:decode_text(tok)
|
||||
local codestr = tok[CODESTR]
|
||||
if codestr == "" then
|
||||
return {self:no_code(tok)}
|
||||
end
|
||||
if codestr:byte(1) == 34 and codestr:byte(-1) == 34 then
|
||||
codestr = codestr:gsub("\\0", "\0")
|
||||
codestr = codestr:gsub("\\n", "\n")
|
||||
|
@ -414,22 +358,18 @@ function Asm:decode_text(tok)
|
|||
for i = idx, math.min(idx + 7, ln) do
|
||||
append(tbl, codestr:byte(i))
|
||||
end
|
||||
tok = {tok[LINENO], codestr:sub(idx, idx + 7), tok[TXTLINE], self.section, self.address, tbl}
|
||||
tok = {"text", tok[LINENO], codestr:sub(idx, idx + 7), self.address, tbl}
|
||||
self.address = self.address + #tbl
|
||||
append(out, tok)
|
||||
end
|
||||
return out
|
||||
else
|
||||
self:err_msg("Invalid string")
|
||||
return {self:no_code(tok)}
|
||||
end
|
||||
end
|
||||
|
||||
function Asm:decode_ctext(tok)
|
||||
local codestr = tok[CODESTR]
|
||||
if codestr == "" then
|
||||
return {self:no_code(tok)}
|
||||
end
|
||||
if codestr:byte(1) == 34 and codestr:byte(-1) == 34 then
|
||||
codestr = codestr:gsub("\\0", "\0\0")
|
||||
codestr = codestr:gsub("\\n", "\n")
|
||||
|
@ -442,14 +382,13 @@ function Asm:decode_ctext(tok)
|
|||
for i = idx, math.min(idx + 15, ln), 2 do
|
||||
append(tbl, word_val(codestr, i))
|
||||
end
|
||||
tok = {tok[LINENO], codestr:sub(idx, idx + 7), tok[TXTLINE], self.section, self.address, tbl}
|
||||
tok = {"ctext", tok[LINENO], codestr:sub(idx, idx + 7), self.address, tbl}
|
||||
self.address = self.address + #tbl
|
||||
append(out, tok)
|
||||
end
|
||||
return out
|
||||
else
|
||||
self:err_msg("Invalid string")
|
||||
return {self:no_code(tok)}
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -483,19 +422,26 @@ function Asm:assembler(filename, lToken)
|
|||
self.filename = filename
|
||||
self.namespace_cnt = 1
|
||||
for _,tok in ipairs(lToken or {}) do
|
||||
self.ctype = tok[CTYPE]
|
||||
self.lineno = tok[LINENO]
|
||||
tok = self:section_def(tok)
|
||||
tok = self:org_directive(tok)
|
||||
tok = self:address_label(tok)
|
||||
|
||||
if self.section == CODESEC then
|
||||
append(lOut, self:decode_code(tok))
|
||||
elseif self.section == DATASEC then
|
||||
append(lOut, self:decode_data(tok))
|
||||
elseif self.section == TEXTSEC then
|
||||
extend(lOut, self:decode_text(tok))
|
||||
elseif self.section == CTEXTSEC then
|
||||
extend(lOut, self:decode_ctext(tok))
|
||||
if tok[CODESTR] ~= "" then
|
||||
if self.ctype == "code" then
|
||||
append(lOut, self:decode_code(tok))
|
||||
elseif self.ctype == "data" then
|
||||
append(lOut, self:decode_data(tok))
|
||||
elseif self.ctype == "text" then
|
||||
extend(lOut, self:decode_text(tok))
|
||||
elseif self.ctype == "ctext" then
|
||||
extend(lOut, self:decode_ctext(tok))
|
||||
elseif self.ctype == "endf" then
|
||||
tok[ADDRESS] = self.address
|
||||
append(lOut, tok)
|
||||
else
|
||||
append(lOut, tok)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -505,26 +451,30 @@ function Asm:assembler(filename, lToken)
|
|||
self.lineno = tok[LINENO]
|
||||
if tok[CODESTR] == "namespace" then
|
||||
self.namespace_cnt = self.namespace_cnt + 1
|
||||
end
|
||||
for i, opc in ipairs(tok[OPCODES] or {}) do
|
||||
if type(opc) == "string" then
|
||||
if not self:handle_rip_label(tok, i, opc) then
|
||||
self:handle_label(tok, i, opc)
|
||||
elseif tok[CTYPE] == "func" then
|
||||
local addr = self.symbols[tok[CODESTR]] or 0
|
||||
tok[ADDRESS] = addr
|
||||
elseif tok[CTYPE] == "call" then
|
||||
local addr = self.symbols[tok[CODESTR]] or 0
|
||||
tok[ADDRESS] = addr
|
||||
else
|
||||
for i, opc in ipairs(tok[OPCODES] or {}) do
|
||||
if type(opc) == "string" then
|
||||
if not self:handle_rip_label(tok, i, opc) then
|
||||
self:handle_label(tok, i, opc)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
if self.error then
|
||||
return nil, table.concat(self.errors, "\n")
|
||||
end
|
||||
return lOut
|
||||
end
|
||||
|
||||
function Asm:listing(lToken)
|
||||
local mydump = function(tbl)
|
||||
local t = {}
|
||||
for _,e in ipairs(tbl) do
|
||||
for _,e in ipairs(tbl or {}) do
|
||||
if type(e) == "number" then
|
||||
table.insert(t, string.format("%04X", e))
|
||||
else
|
||||
|
@ -536,13 +486,7 @@ function Asm:listing(lToken)
|
|||
|
||||
local out = {}
|
||||
for _,tok in ipairs(lToken) do
|
||||
if #tok[OPCODES] > 3 then
|
||||
append(out, string.format(" %-15s %s\n%04X: %s", "", tok[TXTLINE], tok[ADDRESS], mydump(tok[OPCODES])))
|
||||
elseif #tok[OPCODES] > 0 then
|
||||
append(out, string.format("%04X: %-15s %s", tok[ADDRESS], mydump(tok[OPCODES]), tok[TXTLINE]))
|
||||
else
|
||||
append(out, string.format(" %-15s %s", "", tok[TXTLINE]))
|
||||
end
|
||||
append(out, string.format("%5s %3d %04X: %-15s %s", tok[CTYPE], tok[LINENO] or 0, tok[ADDRESS] or 0, tok[CODESTR], mydump(tok[OPCODES])))
|
||||
end
|
||||
return table.concat(out, "\n")
|
||||
end
|
||||
|
@ -550,16 +494,9 @@ end
|
|||
vm16.Asm = Asm
|
||||
|
||||
vm16.Asm.version = version
|
||||
vm16.Asm.CTYPE = CTYPE
|
||||
vm16.Asm.LINENO = LINENO
|
||||
vm16.Asm.CODESTR = CODESTR
|
||||
vm16.Asm.TXTLINE = TXTLINE
|
||||
vm16.Asm.SECTION = SECTION
|
||||
vm16.Asm.ADDRESS = ADDRESS
|
||||
vm16.Asm.OPCODES = OPCODES
|
||||
|
||||
vm16.Asm.DATASEC = DATASEC
|
||||
vm16.Asm.CODESEC = CODESEC
|
||||
vm16.Asm.TEXTSEC = TEXTSEC
|
||||
vm16.Asm.CTEXTSEC = CTEXTSEC
|
||||
vm16.Asm.COMMENT = COMMENT
|
||||
|
||||
|
|
|
@ -13,6 +13,7 @@
|
|||
The compiler generates a list with tokens according to:
|
||||
|
||||
{<type>, <lineno>, <asm-code>}
|
||||
{"file", 0, "test.c"} -- File info
|
||||
{"code", 2, "move A, #1"} -- ASM code
|
||||
{"data", 3, "va1: 0"} -- Variable
|
||||
{"ctext", 4, "str: 'Hallo'"} -- String
|
||||
|
@ -23,61 +24,16 @@
|
|||
And a table with local variable definitions:
|
||||
|
||||
{foo = {<name> = <offs>, ...} -- positive offs = parameter, negative offs = stack variable
|
||||
|
||||
The assembler adds address and opcode information:
|
||||
|
||||
{CTYPE, LINENO, CODESTR, ADDRESS, OPCODES}
|
||||
{"code", 2, "move A, #1", 10, {0x1020, 0x0001}}
|
||||
|
||||
--]]
|
||||
|
||||
local version = "1.1"
|
||||
|
||||
local function extend(into, from)
|
||||
if into and from then
|
||||
for _, t in ipairs(from or {}) do
|
||||
into[#into + 1] = t
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
local function gen_comp_output(lCode, lData, lString)
|
||||
local out = {}
|
||||
|
||||
for idx,line in ipairs(lCode) do
|
||||
table.insert(out, line)
|
||||
end
|
||||
table.insert(out, "")
|
||||
for idx,line in ipairs(lData) do
|
||||
table.insert(out, line)
|
||||
end
|
||||
if #lString > 1 then
|
||||
table.insert(out, "")
|
||||
for idx,line in ipairs(lString) do
|
||||
table.insert(out, line)
|
||||
end
|
||||
end
|
||||
return table.concat(out, "\n")
|
||||
end
|
||||
|
||||
local function get_glob_variables(prs, symbols)
|
||||
local out = {}
|
||||
for ident,addr in pairs(symbols or {}) do
|
||||
if prs:is_global_var(ident) then
|
||||
out[ident] = addr
|
||||
end
|
||||
end
|
||||
return out
|
||||
end
|
||||
|
||||
local function lineno_to_Function(prs, lToken)
|
||||
local out = {}
|
||||
local fname = ""
|
||||
for _, tok in ipairs(lToken) do
|
||||
if tok.lineno and tok.address then
|
||||
fname = prs.tLineno2Func[tok.lineno] or fname
|
||||
out[tok.lineno] = fname
|
||||
end
|
||||
end
|
||||
return out
|
||||
end
|
||||
|
||||
|
||||
local function error_msg(err)
|
||||
local t = string.split(err, "\001")
|
||||
if t and #t > 1 then
|
||||
|
@ -86,158 +42,32 @@ local function error_msg(err)
|
|||
return err
|
||||
end
|
||||
|
||||
local function format_output_for_sourcecode_debugging(lToken)
|
||||
local out = {}
|
||||
local tok
|
||||
local lineno
|
||||
local inline_asm = false
|
||||
for _,item in ipairs(lToken) do
|
||||
if item[vm16.Asm.SECTION] == vm16.Asm.COMMENT then
|
||||
inline_asm = string.find(item[vm16.Asm.TXTLINE], "_asm_")
|
||||
if tok then
|
||||
out[#out + 1] = tok
|
||||
tok = nil
|
||||
end
|
||||
lineno = tonumber(item[vm16.Asm.TXTLINE]:sub(2,5))
|
||||
tok = {lineno = lineno}
|
||||
elseif inline_asm then
|
||||
-- Add each line until the next comment line
|
||||
lineno = lineno + 1
|
||||
out[#out + 1] = {lineno = lineno, address = item[vm16.Asm.ADDRESS], opcodes = item[vm16.Asm.OPCODES]}
|
||||
else
|
||||
if tok and tok.address then
|
||||
extend(tok.opcodes, item[vm16.Asm.OPCODES])
|
||||
local function comp_code_listing(lCode, filename)
|
||||
local mydump = function(tbl)
|
||||
local t = {}
|
||||
for _,e in ipairs(tbl or {}) do
|
||||
if type(e) == "number" then
|
||||
table.insert(t, string.format("%04X", e))
|
||||
else
|
||||
tok = tok or {}
|
||||
tok.address = item[vm16.Asm.ADDRESS]
|
||||
tok.opcodes = item[vm16.Asm.OPCODES]
|
||||
table.insert(t, "'"..e.."'")
|
||||
end
|
||||
end
|
||||
return table.concat(t, " ")
|
||||
end
|
||||
out[#out + 1] = tok
|
||||
return out
|
||||
|
||||
local out = {"##### " .. vm16.file_base(filename) .. ".lst" .. " #####"}
|
||||
for _,item in ipairs(lCode) do
|
||||
local ctype, lineno, scode, address, opcodes = unpack(item)
|
||||
table.insert(out, string.format("%5s %3d %04X: %-15s %s", ctype, lineno, address or 0, scode, mydump(opcodes)))
|
||||
end
|
||||
return table.concat(out, "\n")
|
||||
end
|
||||
|
||||
local function format_output_for_assembler_debugging(lToken)
|
||||
local out = {}
|
||||
for _,item in ipairs(lToken) do
|
||||
if item[vm16.Asm.SECTION] ~= vm16.Asm.COMMENT then
|
||||
out[#out + 1] = {
|
||||
lineno = item[vm16.Asm.LINENO],
|
||||
address = item[vm16.Asm.ADDRESS],
|
||||
opcodes = item[vm16.Asm.OPCODES],
|
||||
}
|
||||
end
|
||||
end
|
||||
return out
|
||||
end
|
||||
|
||||
function vm16.gen_obj_code(filename, code)
|
||||
local out = {}
|
||||
local prs = vm16.BPars:new({text = code})
|
||||
prs.filename = filename
|
||||
prs:bpars_init()
|
||||
local status, err = pcall(prs.main, prs)
|
||||
if not err then
|
||||
local asm = vm16.Asm:new({})
|
||||
local lToken = gen_asm_token_list(prs.lCode, prs.lData, prs.lString)
|
||||
lToken, err = asm:assembler(file_base(filename) .. ".asm", lToken)
|
||||
if lToken then
|
||||
local output = format_output_for_sourcecode_debugging(lToken)
|
||||
return {
|
||||
locals = prs.all_locals,
|
||||
output = output,
|
||||
globals = get_glob_variables(prs, asm.symbols),
|
||||
functions = lineno_to_Function(prs, output)}
|
||||
end
|
||||
return {
|
||||
locals = {},
|
||||
output = {},
|
||||
globals = {},
|
||||
functions = {},
|
||||
errors = err}
|
||||
end
|
||||
local fname = prs.filename or ""
|
||||
local lineno = prs.lineno or "0"
|
||||
local errors = string.format("%s(%d): %s", fname, lineno, error_msg(err))
|
||||
return {
|
||||
locals = {},
|
||||
output = {},
|
||||
globals = {},
|
||||
functions = {},
|
||||
errors = errors}
|
||||
end
|
||||
|
||||
function vm16.gen_asm_code(filename, code)
|
||||
local out = {}
|
||||
local prs = vm16.BPars:new({text = code, add_sourcecode = true})
|
||||
prs.filename = filename
|
||||
prs:bpars_init()
|
||||
local status, err = pcall(prs.main, prs)
|
||||
if not err then
|
||||
return gen_comp_output(prs.lCode, prs.lData, prs.lString)
|
||||
else
|
||||
local fname = prs.filename or ""
|
||||
local lineno = prs.lineno or "0"
|
||||
return gen_comp_output(prs.lCode, prs.lData, prs.lString),
|
||||
string.format("%s(%d): %s", fname, lineno, error_msg(err))
|
||||
end
|
||||
end
|
||||
|
||||
function vm16.assemble(filename, code)
|
||||
local a = vm16.Asm:new({})
|
||||
code = code:gsub("\t", " ")
|
||||
local lToken, err = a:scanner(code)
|
||||
if lToken then
|
||||
lToken, err = a:assembler(file_base(filename) .. ".asm", lToken)
|
||||
if lToken then
|
||||
local output = format_output_for_assembler_debugging(lToken)
|
||||
return {
|
||||
locals = {},
|
||||
output = output,
|
||||
globals = {},
|
||||
functions = {}}
|
||||
end
|
||||
return {
|
||||
locals = {},
|
||||
output = {},
|
||||
globals = {},
|
||||
functions = {},
|
||||
errors = err}
|
||||
end
|
||||
return {
|
||||
locals = {},
|
||||
output = {},
|
||||
globals = {},
|
||||
functions = {},
|
||||
errors = err}
|
||||
end
|
||||
|
||||
function vm16.compile(pos, filename, readfile, debug)
|
||||
local prs = vm16.BPars:new({pos = pos, readfile = readfile})
|
||||
prs:bpars_init()
|
||||
|
||||
local sts, res = pcall(prs.scanner, prs, filename)
|
||||
if not sts then
|
||||
return false, error_msg(res)
|
||||
end
|
||||
|
||||
sts, res = pcall(prs.main, prs)
|
||||
if not sts then
|
||||
return false, error_msg(res)
|
||||
end
|
||||
|
||||
if debug then
|
||||
local output = prs:gen_output()
|
||||
return true, prs:gen_dbg_dump(output)
|
||||
end
|
||||
|
||||
return true, prs:gen_output()
|
||||
end
|
||||
|
||||
function vm16.gen_asm_code(output, sourcecode)
|
||||
local out = {}
|
||||
local function gen_asm_code(output, text, filename)
|
||||
local out = {";##### " .. vm16.file_base(filename) .. ".asm" .. " #####"}
|
||||
local oldlineno = 0
|
||||
local sourcecode = vm16.splitlines(text)
|
||||
|
||||
local add_src_code = function(lineno)
|
||||
for no = oldlineno + 1, lineno do
|
||||
if sourcecode[no] and sourcecode[no] ~= "" then
|
||||
|
@ -285,3 +115,71 @@ function vm16.gen_asm_code(output, sourcecode)
|
|||
|
||||
return table.concat(out, "\n")
|
||||
end
|
||||
|
||||
;------------------------------------------------------------------------------
|
||||
;-- API
|
||||
;------------------------------------------------------------------------------
|
||||
function vm16.assemble(pos, filename, readfile, debug)
|
||||
local code = readfile(pos, filename)
|
||||
code = code:gsub("\t", " ")
|
||||
|
||||
local a = vm16.Asm:new({})
|
||||
local sts, res = pcall(a.scanner, a, code, filename)
|
||||
if not sts then
|
||||
return false, error_msg(res)
|
||||
end
|
||||
|
||||
sts, res = pcall(a.assembler, a, filename, res)
|
||||
if not sts then
|
||||
return false, error_msg(res)
|
||||
end
|
||||
|
||||
if debug then
|
||||
return true, a:listing(res)
|
||||
end
|
||||
|
||||
return true, res
|
||||
end
|
||||
|
||||
function vm16.compile(pos, filename, readfile, output_format)
|
||||
local prs = vm16.BPars:new({pos = pos, readfile = readfile})
|
||||
prs:bpars_init()
|
||||
|
||||
local sts, res = pcall(prs.scanner, prs, filename)
|
||||
if not sts then
|
||||
return false, error_msg(res)
|
||||
end
|
||||
|
||||
if output_format == "tok" then
|
||||
return true, prs:scan_dbg_dump()
|
||||
end
|
||||
|
||||
sts, res = pcall(prs.main, prs)
|
||||
if not sts then
|
||||
return false, error_msg(res)
|
||||
end
|
||||
|
||||
local output = prs:gen_output()
|
||||
|
||||
if output_format == "dbg" then
|
||||
return true, prs:gen_dbg_dump(output)
|
||||
end
|
||||
|
||||
if output_format == "asm" then
|
||||
local text = readfile(pos, filename)
|
||||
return true, gen_asm_code(output, text, filename)
|
||||
end
|
||||
|
||||
local asm = vm16.Asm:new({})
|
||||
sts, res = pcall(asm.assembler, asm, filename, output.lCode)
|
||||
if not sts then
|
||||
return false, error_msg(res)
|
||||
end
|
||||
|
||||
if output_format == "lst" then
|
||||
return true, comp_code_listing(res, filename)
|
||||
end
|
||||
|
||||
return true, {lCode = res, locals = output.locals}
|
||||
end
|
||||
|
||||
|
|
|
@ -21,6 +21,13 @@ local CLOSING_INSTR = {move=1, push=1, add=1, addc=1, mul=1, mulc=1, div=1, sub=
|
|||
mod=1, ["and"]=1, ["or"]=1, xor=1, ["not"]=1, ["in"]=1, out=1,
|
||||
push=1, shl=1, shr=1}
|
||||
|
||||
local tSections = {
|
||||
[".data"] = true,
|
||||
[".code"] = true,
|
||||
[".text"] = true,
|
||||
[".ctext"] = true,
|
||||
}
|
||||
|
||||
local BGen = {}
|
||||
|
||||
function BGen:new(o)
|
||||
|
@ -28,6 +35,7 @@ function BGen:new(o)
|
|||
o.label_cnt = 0
|
||||
o.string_cnt = 0
|
||||
o.reg_cnt = 0
|
||||
o.ctype = "code"
|
||||
o.reg_cnt_stack = {}
|
||||
o.lInit = {}
|
||||
o.lCode = {}
|
||||
|
@ -157,8 +165,24 @@ function BGen:add_label(lbl)
|
|||
table.insert(self.lCode, {"code", self.lineno, lbl .. ":"})
|
||||
end
|
||||
|
||||
function BGen:add_asm_code(code)
|
||||
table.insert(self.lCode, {"code", self.lineno, code})
|
||||
function BGen:add_asm_token(tok)
|
||||
local _, _, codestr = tok.val:find("(.+);")
|
||||
codestr = string.trim(codestr or "")
|
||||
if tSections[codestr] then
|
||||
self.ctype = string.sub(codestr, 2)
|
||||
elseif codestr ~= "" and string.byte(codestr, 1) ~= 59 then -- ';'
|
||||
if self.ctype == "code" then
|
||||
table.insert(self.lCode, {"code", tok.lineno, codestr})
|
||||
elseif self.ctype == "data" then
|
||||
table.insert(self.lData, {"data", tok.lineno, codestr})
|
||||
elseif self.ctype == "ctext" then
|
||||
table.insert(self.lText, {"ctext", tok.lineno, codestr})
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function BGen:end_asm_code()
|
||||
self.ctype = "code"
|
||||
end
|
||||
|
||||
function BGen:add_then_label()
|
||||
|
@ -209,7 +233,7 @@ function BGen:switch_to_func_def()
|
|||
end
|
||||
|
||||
function BGen:gen_output()
|
||||
local out = {{"code", 0, ".code"}}
|
||||
local out = {}
|
||||
|
||||
if #self.lInit > 0 then
|
||||
for _,item in ipairs(self.lInit) do
|
||||
|
@ -226,13 +250,11 @@ function BGen:gen_output()
|
|||
end
|
||||
end
|
||||
if #self.lData > 0 then
|
||||
table.insert(out, {"data", 999, ".data"})
|
||||
for _,item in ipairs(self.lData) do
|
||||
table.insert(out, item)
|
||||
end
|
||||
end
|
||||
if #self.lText > 0 then
|
||||
table.insert(out, {"ctext", 999, ".ctext"})
|
||||
for _,item in ipairs(self.lText) do
|
||||
table.insert(out, item)
|
||||
end
|
||||
|
@ -243,13 +265,13 @@ end
|
|||
function BGen:gen_dbg_dump(output)
|
||||
local out = {}
|
||||
|
||||
out[#out + 1] = "############ Code ###########"
|
||||
out[#out + 1] = "#### Code ####"
|
||||
for idx,tok in ipairs(output.lCode) do
|
||||
local ctype, lineno, code = tok[1], tok[2], tok[3]
|
||||
out[#out + 1] = string.format('%5s: (%d) "%s"', ctype, lineno, code)
|
||||
end
|
||||
|
||||
out[#out + 1] = "############ Locals ###########"
|
||||
out[#out + 1] = "#### Locals ####"
|
||||
for func, item in pairs(output.locals) do
|
||||
out[#out + 1] = string.format('function %s (%d):', func, item["@nsv@"]) -- number of variables
|
||||
for id, offs in pairs(item) do
|
||||
|
|
|
@ -59,8 +59,9 @@ function BPars:definition()
|
|||
elseif tok.type == T_NEWFILE then
|
||||
self:add_item("file", tok.lineno, tok.val)
|
||||
self:tk_match(T_NEWFILE)
|
||||
self:end_asm_code()
|
||||
elseif tok.type == T_ASMCODE then
|
||||
self:add_item("code", tok.lineno, tok.val)
|
||||
self:add_asm_token(tok)
|
||||
self:tk_match(T_ASMCODE)
|
||||
elseif tok.val ~= nil then
|
||||
error(string.format("Unexpected item '%s'", tok.val))
|
||||
|
@ -379,9 +380,10 @@ function BPars:asm_declaration()
|
|||
local tok = self:tk_peek()
|
||||
while tok.type == T_ASMCODE do
|
||||
self:tk_match()
|
||||
self:add_asm_code(tok.val)
|
||||
self:add_asm_token(tok)
|
||||
tok = self:tk_peek()
|
||||
end
|
||||
self:end_asm_code()
|
||||
end
|
||||
|
||||
--[[
|
||||
|
|
|
@ -46,32 +46,11 @@ local lTypeString = {"ident", "number", "operand", "brace", "string", "asm code"
|
|||
local lToken = {}
|
||||
local tScannedFiles = {}
|
||||
|
||||
local strfind = string.find
|
||||
local strsub = string.sub
|
||||
local tinsert = table.insert
|
||||
|
||||
local function file_ext(filename)
|
||||
local _, ext = unpack(string.split(filename, ".", true, 1))
|
||||
return ext
|
||||
end
|
||||
|
||||
local function split_into_lines(text)
|
||||
local list = {}
|
||||
local pos = 1
|
||||
|
||||
while true do
|
||||
local first, last = strfind(text, "\n", pos)
|
||||
if first then -- found?
|
||||
tinsert(list, strsub(text, pos, first-1))
|
||||
pos = last+1
|
||||
else
|
||||
tinsert(list, strsub(text, pos))
|
||||
break
|
||||
end
|
||||
end
|
||||
return list
|
||||
end
|
||||
|
||||
local function char_to_val(char)
|
||||
if #char == 2 then
|
||||
return char:byte(1) * 256 + char:byte(2)
|
||||
|
@ -80,7 +59,6 @@ local function char_to_val(char)
|
|||
end
|
||||
end
|
||||
|
||||
|
||||
local BScan = vm16.BGen:new({})
|
||||
|
||||
function BScan:bscan_init()
|
||||
|
@ -178,7 +156,7 @@ function BScan:scanner(filename)
|
|||
table.insert(lToken, {type = T_NEWFILE, val = filename, lineno = 0})
|
||||
|
||||
local text = self.readfile(self.pos, filename)
|
||||
for lineno, line in ipairs(split_into_lines(text)) do
|
||||
for lineno, line in ipairs(vm16.splitlines(text)) do
|
||||
self.lineno = lineno
|
||||
if self.is_asm_code then
|
||||
line = line:trim()
|
||||
|
@ -222,7 +200,7 @@ function BScan:scan_dbg_dump()
|
|||
|
||||
for idx,tok in ipairs(self.lTok) do
|
||||
if tok.type == T_NEWFILE then
|
||||
out[idx] = string.format('%8s: ######## "%s" ########', lTypeString[tok.type], tok.val)
|
||||
out[idx] = string.format('%8s: #### "%s" ####', lTypeString[tok.type], tok.val)
|
||||
else
|
||||
out[idx] = string.format('%8s: (%d) "%s"', lTypeString[tok.type], tok.lineno, tok.val)
|
||||
end
|
||||
|
|
32
lib.lua
32
lib.lua
|
@ -8,6 +8,10 @@
|
|||
See LICENSE.txt for more information
|
||||
]]--
|
||||
|
||||
local strfind = string.find
|
||||
local strsub = string.sub
|
||||
local tinsert = table.insert
|
||||
|
||||
-- Returns the number of operands (0,1,2) based on the given opcode
|
||||
function vm16.num_operands(opcode)
|
||||
if opcode then
|
||||
|
@ -25,3 +29,31 @@ function vm16.hex2number(s)
|
|||
if not addr or addr == "" then addr = "0" end
|
||||
return (tonumber(addr, 16) % 0x10000) or 0
|
||||
end
|
||||
|
||||
-- Split a multi-line string into a list of lines
|
||||
function vm16.splitlines(text)
|
||||
local list = {}
|
||||
local pos = 1
|
||||
|
||||
while true do
|
||||
local first, last = strfind(text, "\n", pos)
|
||||
if first then -- found?
|
||||
tinsert(list, strsub(text, pos, first-1))
|
||||
pos = last+1
|
||||
else
|
||||
tinsert(list, strsub(text, pos))
|
||||
break
|
||||
end
|
||||
end
|
||||
return list
|
||||
end
|
||||
|
||||
function vm16.file_ext(filename)
|
||||
local _, ext = unpack(string.split(filename, ".", true, 1))
|
||||
return ext
|
||||
end
|
||||
|
||||
function vm16.file_base(filename)
|
||||
local name, _ = unpack(string.split(filename, ".", true, 1))
|
||||
return name
|
||||
end
|
||||
|
|
|
@ -12,9 +12,6 @@
|
|||
|
||||
-- for lazy programmers
|
||||
local M = minetest.get_meta
|
||||
local strfind = string.find
|
||||
local strsub = string.sub
|
||||
local tinsert = table.insert
|
||||
|
||||
local Cache = {} -- [hash] = {}
|
||||
|
||||
|
@ -41,23 +38,6 @@ function vm16.prog.get_linenum(lToken, addr)
|
|||
return 0
|
||||
end
|
||||
|
||||
function vm16.prog.strsplit(text)
|
||||
local list = {}
|
||||
local pos = 1
|
||||
|
||||
while true do
|
||||
local first, last = strfind(text, "\n", pos)
|
||||
if first then -- found?
|
||||
tinsert(list, strsub(text, pos, first-1))
|
||||
pos = last+1
|
||||
else
|
||||
tinsert(list, strsub(text, pos))
|
||||
break
|
||||
end
|
||||
end
|
||||
return list
|
||||
end
|
||||
|
||||
function vm16.prog.to_char(val)
|
||||
if val >= 32 and val <= 127 then
|
||||
return string.char(val)
|
||||
|
@ -80,13 +60,3 @@ function vm16.prog.get_cpu_def(cpu_pos)
|
|||
return ndef.vm16_cpu
|
||||
end
|
||||
end
|
||||
|
||||
function vm16.prog.file_ext(filename)
|
||||
local _, ext = unpack(string.split(filename, ".", true, 1))
|
||||
return ext
|
||||
end
|
||||
|
||||
function vm16.prog.file_base(filename)
|
||||
local name, _ = unpack(string.split(filename, ".", true, 1))
|
||||
return name
|
||||
end
|
||||
|
|
Loading…
Reference in New Issue