forked from GitHub/lad
Uploading Lua Assembler/Disassembler for Lua 5.2
This commit is contained in:
@@ -0,0 +1,6 @@
|
||||
Version 0.2 - 28/03/2012
|
||||
* Ported code from Lua 5.1.4 to Lua 5.2.0
|
||||
+ Support to Lua 5.2.0
|
||||
- Support to Lua 5.1.4
|
||||
Version 0.1 - 19/07/2011
|
||||
* Initial version for Lua 5.1.4
|
||||
@@ -0,0 +1,5 @@
|
||||
Lua Assembler/Disassembler 0.2, relased on March 28 2012.
|
||||
|
||||
For further information about Lua Assembler/Disassembler,
|
||||
as well as installion instructions and license details,
|
||||
please, see doc/manual.html.
|
||||
@@ -0,0 +1,22 @@
|
||||
|
||||
Assembler
|
||||
|
||||
- Fix RK to work with constants larger than 2^8.
|
||||
- Add following command line options:
|
||||
- endianness;
|
||||
- size of string constants;
|
||||
- size of ints;
|
||||
- type of lua numbers.
|
||||
- Insert line counter to improve error messages.
|
||||
- Add operator '...' to allow the definition of vararg functions.
|
||||
- Add following instructions: JNE, JG, JL, JGE, JLE and JE.
|
||||
- Add below constants/parameters if necessary:
|
||||
- TRUE;
|
||||
- FALSE;
|
||||
- NIL.
|
||||
|
||||
Disassembler
|
||||
|
||||
- Make it work with big endian code.
|
||||
- Write '(...)' instead of '(0)' when a function is vararg.
|
||||
|
||||
@@ -0,0 +1,685 @@
|
||||
local re = require("re")
|
||||
local ladconf = require("ladconf")
|
||||
|
||||
local OPCODE = { MOVE = 0, LOADK = 1, LOADKX = 2, LOADBOOL = 3, LOADNIL = 4,
|
||||
GETUPVAL = 5, GETTABUP = 6, GETTABLE = 7, SETTABUP = 8,
|
||||
SETUPVAL = 9, SETTABLE = 10, NEWTABLE = 11, SELF = 12,
|
||||
ADD = 13, SUB = 14, MUL = 15, DIV = 16, MOD = 17, POW = 18,
|
||||
UNM = 19, NOT = 20, LEN = 21, CONCAT = 22, JMP = 23,
|
||||
EQ = 24, LT = 25, LE = 26, TEST = 27, TESTSET = 28,
|
||||
CALL = 29, TAILCALL = 30, RETURN = 31, FORLOOP = 32,
|
||||
FORPREP = 33, TFORCALL = 34, TFORLOOP = 35, SETLIST = 36,
|
||||
CLOSURE = 37, VARARG = 38, EXTRAARG = 39 }
|
||||
|
||||
local grammar = [[
|
||||
prog <- s ( {:tag: '' -> 'prog':} function )* -> {} !.
|
||||
function <- ( {:tag: '' -> 'func':} {:header: header:} {:codelist: codelist:}) -> {}
|
||||
header <- ("function" s {:name: name:} s "(" s {:numparams: n:} s ")" s ":" s) -> {}
|
||||
codelist <- code+ -> {}
|
||||
code <- autocode / manualcode
|
||||
autocode <- ( {:tag: '' -> 'code':} n s ln s op s param s ("," s param s)* ) -> {}
|
||||
manualcode <- ( {:tag: '' -> 'code':} ({:label: label:} s )? op s param s ("," s param s)* ) -> {}
|
||||
n <- %d+
|
||||
label <- name ":"
|
||||
name <- ( !reserved {[a-zA-Z_][a-zA-Z0-9_]*} )
|
||||
ln <- "[" %d+ "]"
|
||||
op <- {:op: !reserved %a+ -> to_upper:}
|
||||
param <- register / number / string
|
||||
register <- ( {:tag: '' -> 'reg':} ("$" {n}) -> to_number ) -> {}
|
||||
number <- ( {:tag: '' -> 'num':} ( hex / float / int ) ) -> {}
|
||||
string <- ( {:tag: '' -> 'str':} ( name / shortstr ) ) -> {}
|
||||
shortstr <- ( '"' {('\\' / '\"' / !'"' .)*} '"' / "'" {("\\" / "\'" / !"'" .)*} "'" ) -> to_string
|
||||
hex <- ( {:tag: '' -> 'int':} {"-"? "0" [xX] %x+} -> to_number ) -> {}
|
||||
float <- ( {:tag: '' -> 'float':} {"-"? ( (%d+ "." %d* / "." %d+) e? / %d+ e )} -> to_number ) -> {}
|
||||
e <- [eE] [+-]? n
|
||||
int <- ( {:tag: '' -> 'int':} {"-"? n} -> to_number ) -> {}
|
||||
s <- (space / comment)*
|
||||
space <- %s+
|
||||
comment <- ";" (!%nl .)*
|
||||
reserved <- "function"
|
||||
]]
|
||||
|
||||
local function fixed_string(s)
|
||||
s = string.gsub(s, "\\\"", '\"')
|
||||
s = string.gsub(s, "\\\\", '\\')
|
||||
s = string.gsub(s, "\\a", '\a')
|
||||
s = string.gsub(s, "\\b", '\b')
|
||||
s = string.gsub(s, "\\f", '\f')
|
||||
s = string.gsub(s, "\\n", '\n')
|
||||
s = string.gsub(s, "\\r", '\r')
|
||||
s = string.gsub(s, "\\t", '\t')
|
||||
s = string.gsub(s, "\\v", '\v')
|
||||
s = string.gsub(s, "\\(%d+)", function (s) return string.char (tonumber(s)) end )
|
||||
return s
|
||||
end
|
||||
|
||||
local defs = {
|
||||
to_number = function (n) return tonumber(n) end,
|
||||
to_string = function (s) return fixed_string(s) end,
|
||||
to_upper = function (s) return string.upper(s) end,
|
||||
}
|
||||
|
||||
local parser = re.compile(grammar, defs)
|
||||
|
||||
local function parse(contents)
|
||||
return parser:match(contents)
|
||||
end
|
||||
|
||||
local function print_header(t)
|
||||
io.write(string.format("\nfunction %s(%s):\n", t.name, t.numparams))
|
||||
end
|
||||
|
||||
local function print_param(t)
|
||||
if t.tag == 'reg' then
|
||||
io.write(string.format("$%d", t[1]))
|
||||
elseif t.tag == 'num' then
|
||||
io.write(string.format("%s", tostring(t[1])))
|
||||
elseif t.tag == 'str' then
|
||||
io.write(string.format("%s", t[1]))
|
||||
end
|
||||
end
|
||||
|
||||
local function print_sep()
|
||||
io.write(string.format(", "))
|
||||
end
|
||||
|
||||
local function print_code(t, i)
|
||||
if t.tag == 'code' then
|
||||
if t.label then io.write(string.format("%s:", t.label)) end
|
||||
io.write(string.format("\t%d", i))
|
||||
io.write(string.format("\t%-9s\t", t.op))
|
||||
for i=1,#t-1 do
|
||||
print_param(t[i])
|
||||
print_sep()
|
||||
end
|
||||
print_param(t[#t])
|
||||
io.write(string.format("\n"))
|
||||
end
|
||||
end
|
||||
|
||||
local function print_codelist(t)
|
||||
for i=1,#t do
|
||||
print_code(t[i], i)
|
||||
end
|
||||
end
|
||||
|
||||
local function print_func(t)
|
||||
if t.tag == 'func' then
|
||||
print_header(t.header)
|
||||
print_codelist(t.codelist)
|
||||
end
|
||||
end
|
||||
|
||||
local function print_ast(t)
|
||||
if t.tag == 'prog' then
|
||||
for k,v in ipairs(t) do
|
||||
print_func(v)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
local function converge(ast)
|
||||
if ast.tag ~= 'prog' then return nil end
|
||||
local t = {}
|
||||
for k,v in ipairs(ast) do
|
||||
if v.tag ~= 'func' then return nil end
|
||||
local name = v.header.name
|
||||
t[name] = {}
|
||||
t[name].id = k
|
||||
end
|
||||
return t
|
||||
end
|
||||
|
||||
local function R(t, f)
|
||||
if t.tag ~= 'reg' then error("Register expected") end
|
||||
local r = t[1]
|
||||
if not f.register[r] then
|
||||
f.register[r] = r
|
||||
f.maxstacksize = f.maxstacksize + 1
|
||||
end
|
||||
return r
|
||||
end
|
||||
|
||||
local function Int(t)
|
||||
if t.tag ~= 'num' or t[1].tag ~= 'int' then
|
||||
error("Integer expected")
|
||||
end
|
||||
local i = t[1][1]
|
||||
if i < 0 then error("Positive integer expected") end
|
||||
return i
|
||||
end
|
||||
|
||||
local function Label(t, f)
|
||||
local l = t[1]
|
||||
if not f.label[l] then error(string.format("Label %s not defined", l)) end
|
||||
return f.label[l]
|
||||
end
|
||||
|
||||
local function Kst(t, f)
|
||||
local k, k_type
|
||||
|
||||
if t.tag == 'num' then
|
||||
k = t[1][1]
|
||||
k_type = ladconf.LUA_TNUMBER
|
||||
elseif t.tag == 'str' then
|
||||
k = t[1]
|
||||
k_type = ladconf.LUA_TSTRING
|
||||
else
|
||||
error ("Constant not implemented")
|
||||
end
|
||||
|
||||
if not f.const[k] then
|
||||
local n = f.sizek + 1
|
||||
f.sizek = n
|
||||
f.const[k] = n
|
||||
f.k[n] = {}
|
||||
f.k[n].k_type = k_type
|
||||
f.k[n].value = k
|
||||
end
|
||||
|
||||
return f.const[k]
|
||||
end
|
||||
|
||||
local function KPROTO(t, f, a)
|
||||
if t.tag ~= 'str' then error("Function name expected") end
|
||||
|
||||
local name = t[1]
|
||||
|
||||
if not a[name] then
|
||||
error(string.format("Function %s not defined", name))
|
||||
end
|
||||
|
||||
if not f.func[name] then
|
||||
local n = f.sizep + 1
|
||||
f.sizep = n
|
||||
f.func[name] = n
|
||||
f.p[n] = {}
|
||||
f.p[n] = name
|
||||
end
|
||||
|
||||
return f.func[name]
|
||||
end
|
||||
|
||||
local function UpValue(t, f)
|
||||
if t.tag ~= 'str' then error("String/Name expected") end
|
||||
|
||||
local name = t[1]
|
||||
|
||||
if not f.upval[name] then
|
||||
local n = f.sizeupvalues + 1
|
||||
f.sizeupvalues = n
|
||||
f.upval[name] = n
|
||||
f.upvalues[n] = {}
|
||||
if name == "_ENV" then
|
||||
f.upvalues[n].instack = 0
|
||||
else
|
||||
f.upvalues[n].instack = 1
|
||||
end
|
||||
f.upvalues[n].idx = 0
|
||||
f.upvalues[n].name = name
|
||||
end
|
||||
|
||||
return f.upval[name]
|
||||
end
|
||||
|
||||
local function RK(t, f)
|
||||
if t.tag == 'reg' then
|
||||
return R(t, f)
|
||||
end
|
||||
-- TODO: fix it (does not work if sizek > 2^8)
|
||||
local k = Kst(t, f)
|
||||
if k < ladconf.MAXINDEXRK then
|
||||
return k + ladconf.MAXINDEXRK
|
||||
end
|
||||
return k
|
||||
end
|
||||
|
||||
local function SBX(t, f, n)
|
||||
if t.tag == 'num' then
|
||||
return Int(t) - n
|
||||
elseif t.tag == 'str' then
|
||||
return Label(t, f) - n
|
||||
end
|
||||
end
|
||||
|
||||
local function sew_label(i, t, n)
|
||||
if i.label then
|
||||
local l = i.label
|
||||
if not t[l] then t[l] = n
|
||||
else error (string.format("Label %s already defined\n", l))
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
local function sew_code(t, ast, all)
|
||||
local codelist = ast[t.id].codelist
|
||||
t.sizecode = #codelist
|
||||
-- first check for labels
|
||||
t.label = {}
|
||||
for k,v in ipairs(codelist) do
|
||||
sew_label(v, t.label, k)
|
||||
end
|
||||
-- after that check code
|
||||
t.code = {}
|
||||
for k,v in ipairs(codelist) do
|
||||
if v.tag ~= 'code' then return nil end
|
||||
local f,i = t,{}
|
||||
local op = v.op
|
||||
t.code[k] = i
|
||||
i.O = OPCODE[op]
|
||||
if op == "MOVE" or
|
||||
op == "UNM" or
|
||||
op == "NOT" or
|
||||
op == "LEN" then
|
||||
-- R(A) R(B)
|
||||
i.A = R(v[1], f)
|
||||
i.B = R(v[2], f)
|
||||
i.C = 0
|
||||
elseif op == "LOADK" then
|
||||
-- R(A) Kst(Bx)
|
||||
i.A = R(v[1], f)
|
||||
i.Bx = Kst(v[2], f)
|
||||
elseif op == "LOADKX" then
|
||||
-- R(A)
|
||||
if codelist[k+1].op ~= "EXTRAARG" then
|
||||
error("For LOADKX next instruction is always EXTRAARG")
|
||||
end
|
||||
i.A = R(v[1], f)
|
||||
i.Bx = 0
|
||||
elseif op == "LOADBOOL" or
|
||||
op == "NEWTABLE" or
|
||||
op == "CALL" or
|
||||
op == "TAILCALL" or
|
||||
op == "SETLIST" then
|
||||
-- R(A) B C
|
||||
i.A = R(v[1], f)
|
||||
i.B = Int(v[2])
|
||||
i.C = Int(v[3])
|
||||
elseif op == "LOADNIL" or
|
||||
op == "RETURN" then
|
||||
-- R(A) B
|
||||
i.A = R(v[1], f)
|
||||
i.B = Int(v[2])
|
||||
i.C = 0
|
||||
elseif op == "GETUPVAL" or
|
||||
op == "SETUPVAL" then
|
||||
-- R(A) UpValue(B)
|
||||
i.A = R(v[1], f)
|
||||
i.B = UpValue(v[2], f) - 1
|
||||
i.C = 0
|
||||
elseif op == "GETTABUP" then
|
||||
-- R(A) UpValue(B) RK(C)
|
||||
i.A = R(v[1], f)
|
||||
i.B = UpValue(v[2], f) - 1
|
||||
i.C = RK(v[3], f)
|
||||
elseif op == "GETTABLE" or
|
||||
op == "SELF" then
|
||||
-- R(A) R(B) RK(C)
|
||||
i.A = R(v[1], f)
|
||||
i.B = R(v[2], f)
|
||||
i.C = RK(v[3], f)
|
||||
elseif op == "SETTABUP" then
|
||||
-- UpValue(A) RK(B) RK(C)
|
||||
i.A = UpValue(v[1], f) - 1
|
||||
i.B = RK(v[2], f)
|
||||
i.C = RK(v[3], f)
|
||||
elseif op == "SETTABLE" or
|
||||
op == "ADD" or
|
||||
op == "SUB" or
|
||||
op == "MUL" or
|
||||
op == "DIV" or
|
||||
op == "MOD" or
|
||||
op == "POW" then
|
||||
-- R(A) RK(B) RK(C)
|
||||
i.A = R(v[1], f)
|
||||
i.B = RK(v[2], f)
|
||||
i.C = RK(v[3], f)
|
||||
elseif op == "CONCAT" then
|
||||
-- R(A) R(B) R(C)
|
||||
i.A = R(v[1], f)
|
||||
i.B = R(v[2], f)
|
||||
i.C = R(v[3], f)
|
||||
elseif op == "JMP" then
|
||||
-- A sBx
|
||||
i.A = Int(v[1])
|
||||
i.sBx = SBX(v[2], f, k)
|
||||
elseif op == "EQ" or
|
||||
op == "LT" or
|
||||
op == "LE" then
|
||||
-- A RK(B) RK(C)
|
||||
i.A = Int(v[1])
|
||||
i.B = RK(v[2], f)
|
||||
i.C = RK(v[3], f)
|
||||
elseif op == "TEST" or
|
||||
op == "TFORCALL" then
|
||||
-- R(A) C
|
||||
i.A = R(v[1], f)
|
||||
i.B = 0
|
||||
i.C = Int(v[2])
|
||||
elseif op == "TESTSET" then
|
||||
-- R(A) R(B) C
|
||||
i.A = R(v[1], f)
|
||||
i.B = R(v[2], f)
|
||||
i.C = Int(v[3])
|
||||
elseif op == "FORLOOP" or
|
||||
op == "FORPREP" or
|
||||
op == "TFORLOOP" then
|
||||
-- R(A) sBx
|
||||
i.A = R(v[1], f)
|
||||
i.sBx = SBX(v[2], f, k)
|
||||
elseif op == "CLOSURE" then
|
||||
-- R(A) KPROTO(Bx)
|
||||
i.A = R(v[1], f)
|
||||
i.Bx = KPROTO(v[2], f, all)
|
||||
elseif op == "VARARG" then
|
||||
-- R(A) B
|
||||
i.A = R(v[1], f)
|
||||
i.B = Int(v[2])
|
||||
i.C = 0
|
||||
f.is_vararg = 1
|
||||
f.numparams = i.B
|
||||
elseif op == "EXTRAARG" then
|
||||
-- Ax
|
||||
local pop = codelist[k-1].op
|
||||
if pop == "LOADKX" then
|
||||
i.Ax = Kst(v[1], f) - 1
|
||||
elseif pop == "SETLIST" then
|
||||
i.Ax = Int(v[1])
|
||||
else
|
||||
error ("EXTRAARG not expected")
|
||||
end
|
||||
else
|
||||
local str = string.format("%s not implemented\n", op)
|
||||
error (str)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
local function sew_function(t, ast, all)
|
||||
local name = ast[t.id].header.name
|
||||
t.linedefined = 0
|
||||
t.lastlinedefined = 0
|
||||
t.numparams = ast[t.id].header.numparams
|
||||
if name ~= "main" then
|
||||
t.is_vararg = 0
|
||||
end
|
||||
-- registers 0/1 are always valid
|
||||
t.register = {}
|
||||
t.register[0] = 0
|
||||
t.register[1] = 1
|
||||
t.maxstacksize = 2
|
||||
-- constants
|
||||
t.sizek = 0
|
||||
t.k = {}
|
||||
t.const = {}
|
||||
-- functions
|
||||
t.sizep = 0
|
||||
t.p = {}
|
||||
t.func = {}
|
||||
-- upvalues
|
||||
if name ~= "main" then
|
||||
t.sizeupvalues = 0
|
||||
t.upvalues = {}
|
||||
t.upval = {}
|
||||
end
|
||||
t.sizelineinfo = 0
|
||||
t.sizelocvars = 0
|
||||
sew_code(t, ast, all)
|
||||
end
|
||||
|
||||
local function sew(t, ast)
|
||||
if not t.main then error ("main not defined") end
|
||||
-- main function always have upvalue _ENV defined
|
||||
t.main.sizeupvalues = 1
|
||||
t.main.upval = {}
|
||||
t.main.upval[ladconf.LUA_ENV] = 1
|
||||
t.main.upvalues = {}
|
||||
t.main.upvalues[1] = {}
|
||||
t.main.upvalues[1].instack = 1
|
||||
t.main.upvalues[1].idx = 0
|
||||
t.main.upvalues[1].name = ladconf.LUA_ENV
|
||||
-- main is always vararg
|
||||
t.main.is_vararg = 1
|
||||
for k,v in pairs(t) do
|
||||
sew_function(v, ast, t)
|
||||
end
|
||||
end
|
||||
|
||||
local function traverse(ast)
|
||||
local t = converge(ast)
|
||||
if not t then error("Empty File") end
|
||||
sew(t, ast)
|
||||
return t
|
||||
end
|
||||
|
||||
local function write_byte(output, byte)
|
||||
if byte ~= 0 then
|
||||
output:write(string.format("%c", byte))
|
||||
else
|
||||
output:write('\0')
|
||||
end
|
||||
end
|
||||
|
||||
local function get_hex(n)
|
||||
return string.format("0x%x", n)
|
||||
end
|
||||
|
||||
local function get_int(i, s)
|
||||
local x = get_hex(i)
|
||||
local a,b = 0,8
|
||||
local t = {}
|
||||
for k=1,s do
|
||||
t[k] = ladconf.get_bit(x, a, b)
|
||||
a = a + 8
|
||||
b = b + 8
|
||||
end
|
||||
return t
|
||||
end
|
||||
|
||||
local function write_int(output, n, s)
|
||||
local i = get_int(n, s)
|
||||
for k=1,s do
|
||||
write_byte(output, i[k])
|
||||
end
|
||||
end
|
||||
|
||||
local function write_string(output, str)
|
||||
local len = string.len(str)
|
||||
write_int(output, len + 1, ladconf.SIZE_T)
|
||||
for i=1,len do
|
||||
write_byte(output, string.byte(str, i))
|
||||
end
|
||||
write_byte(output, 0)
|
||||
end
|
||||
|
||||
local function write_source_name(output, name)
|
||||
write_string(output, name)
|
||||
end
|
||||
|
||||
local function get_byte(v)
|
||||
return math.floor(v / 256), string.char(math.floor(v) % 256)
|
||||
end
|
||||
|
||||
local function convert_number(x)
|
||||
local sign = 0
|
||||
if x < 0 then sign = 1; x = -x end
|
||||
local mantissa, exponent = math.frexp(x)
|
||||
if x == 0 then -- zero
|
||||
mantissa, exponent = 0, 0
|
||||
else
|
||||
mantissa = (mantissa * 2 - 1) * math.ldexp(0.5, 53)
|
||||
exponent = exponent + 1022
|
||||
end
|
||||
local v, byte = {}, "" -- convert to bytes
|
||||
x = mantissa
|
||||
for i = 1,6 do
|
||||
x, byte = get_byte(x); v[i] = string.byte(byte)
|
||||
end
|
||||
x, byte = get_byte(exponent * 16 + x); v[7] = string.byte(byte)
|
||||
x, byte = get_byte(sign * 128 + x); v[8] = string.byte(byte)
|
||||
return v
|
||||
end
|
||||
|
||||
local function write_number(output, n)
|
||||
local t = convert_number(n)
|
||||
for k=1,ladconf.LUA_NUMBER do write_byte(output, t[k]) end
|
||||
end
|
||||
|
||||
local function write_header(output)
|
||||
for i=1,string.len(ladconf.LUA_SIGNATURE) do
|
||||
write_byte(output, string.byte(ladconf.LUA_SIGNATURE, i))
|
||||
end
|
||||
write_byte(output, ladconf.LUA_VERSION)
|
||||
write_byte(output, ladconf.LUA_FORMAT)
|
||||
write_byte(output, ladconf.ENDIANNESS)
|
||||
write_byte(output, ladconf.INT)
|
||||
write_byte(output, ladconf.SIZE_T)
|
||||
write_byte(output, ladconf.INSTRUCTION)
|
||||
write_byte(output, ladconf.LUA_NUMBER)
|
||||
write_byte(output, ladconf.INTEGRAL)
|
||||
for i=1,string.len(ladconf.LUAC_TAIL) do
|
||||
write_byte(output, string.byte(ladconf.LUAC_TAIL, i))
|
||||
end
|
||||
end
|
||||
|
||||
local function write_function_values(output, f)
|
||||
write_byte(output, f.numparams)
|
||||
write_byte(output, f.is_vararg)
|
||||
write_byte(output, f.maxstacksize)
|
||||
end
|
||||
|
||||
local function gen_iABC(O, A, B, C)
|
||||
local field = {O, A, C, B}
|
||||
local v, i = {}, 0
|
||||
local cValue, cBits, cPos = 0, 0, 1
|
||||
-- encode an instruction
|
||||
while i < ladconf.INSTRUCTION do
|
||||
-- if need more bits, suck in a field at a time
|
||||
while cBits < 8 do
|
||||
cValue = field[cPos] * math.ldexp(1, cBits) + cValue
|
||||
cBits = cBits + ladconf.ABC[cPos]; cPos = cPos + 1
|
||||
end
|
||||
-- extract bytes to instruction string
|
||||
while cBits >= 8 do
|
||||
v[i+1] = (cValue % 256)
|
||||
cValue = math.floor(cValue / 256)
|
||||
cBits = cBits - 8; i = i + 1
|
||||
end
|
||||
end
|
||||
return v
|
||||
end
|
||||
|
||||
local function gen_iABx(O, A, Bx)
|
||||
return gen_iABC(O, A, math.floor(Bx / ladconf.MASK_C), (Bx % ladconf.MASK_C))
|
||||
end
|
||||
|
||||
local function gen_iAsBx(O, A, sBx)
|
||||
return gen_iABx(O, A, (sBx + ladconf.MAXARG_sBx))
|
||||
end
|
||||
|
||||
local function gen_iAx(O, Ax)
|
||||
return gen_iABC(O, Ax, 0, 0)
|
||||
end
|
||||
|
||||
local function write_instruction(output, i)
|
||||
local m = ladconf.get_op_mode(i.O + 1)
|
||||
local t = {}
|
||||
|
||||
if m == ladconf.iABC then
|
||||
t = gen_iABC(i.O, i.A, i.B, i.C)
|
||||
elseif m == ladconf.iABx then
|
||||
t = gen_iABx(i.O, i.A, i.Bx - 1)
|
||||
elseif m == ladconf.iAsBx then
|
||||
t = gen_iAsBx(i.O, i.A, i.sBx - 1)
|
||||
elseif m == ladconf.iAx then
|
||||
t = gen_iAx(i.O, i.Ax)
|
||||
end
|
||||
|
||||
for k=1,ladconf.INSTRUCTION do write_byte(output, t[k]) end
|
||||
end
|
||||
|
||||
local function write_code(output, f)
|
||||
local n = f.sizecode
|
||||
write_int(output, n, ladconf.INT)
|
||||
for i=1,n do
|
||||
write_instruction(output, f.code[i])
|
||||
end
|
||||
end
|
||||
|
||||
local function write_constant(output, k)
|
||||
local t = k.k_type
|
||||
write_byte(output, t)
|
||||
if t == ladconf.LUA_TNIL then
|
||||
-- do not need to write anything
|
||||
elseif t == ladconf.LUA_TBOOLEAN then
|
||||
elseif t == ladconf.LUA_TNUMBER then
|
||||
write_number(output, k.value)
|
||||
elseif t == ladconf.LUA_TSTRING then
|
||||
write_string(output, k.value)
|
||||
end
|
||||
end
|
||||
|
||||
local function write_constants(output, f)
|
||||
local n
|
||||
n = f.sizek
|
||||
write_int(output, n, ladconf.INT)
|
||||
for i=1,n do
|
||||
write_constant(output, f.k[i])
|
||||
end
|
||||
end
|
||||
|
||||
local function write_upvalues(output, f)
|
||||
local n = f.sizeupvalues
|
||||
write_int(output, n, ladconf.INT)
|
||||
for i=1,n do
|
||||
write_byte(output, f.upvalues[i].instack)
|
||||
write_byte(output, f.upvalues[i].idx)
|
||||
end
|
||||
end
|
||||
|
||||
local function write_debug(output, f)
|
||||
write_string(output, ladconf.LUA_SOURCE)
|
||||
write_int(output, f.sizelineinfo, ladconf.INT)
|
||||
write_int(output, f.sizelocvars, ladconf.INT)
|
||||
local n = f.sizeupvalues
|
||||
write_int(output, n, ladconf.INT)
|
||||
for k=1,n do
|
||||
write_string(output, f.upvalues[k].name)
|
||||
end
|
||||
end
|
||||
|
||||
local function write_function(output, current, parsed)
|
||||
write_int(output, current.linedefined, ladconf.INT) -- line defined
|
||||
write_int(output, current.lastlinedefined, ladconf.INT) -- last line defined
|
||||
write_function_values(output, current)
|
||||
write_code(output, current)
|
||||
write_constants(output, current, parsed)
|
||||
n = current.sizep
|
||||
write_int(output, n, ladconf.INT)
|
||||
for i=1,n do
|
||||
local name = current.p[i]
|
||||
write_function(output, parsed[name], parsed)
|
||||
end
|
||||
write_upvalues(output, current)
|
||||
write_debug(output, current)
|
||||
end
|
||||
|
||||
local function write_bytecode(output, parsed)
|
||||
write_header(output)
|
||||
write_function(output, parsed.main, parsed)
|
||||
end
|
||||
|
||||
local function write(filename, parsed)
|
||||
local output = assert(io.open(filename, "wb"))
|
||||
write_bytecode(output, parsed)
|
||||
output:close()
|
||||
end
|
||||
|
||||
local assembler = {
|
||||
parse = parse,
|
||||
traverse = traverse,
|
||||
write = write,
|
||||
print_ast = print_ast,
|
||||
}
|
||||
|
||||
return assembler
|
||||
@@ -0,0 +1,754 @@
|
||||
local ladconf = require("ladconf")
|
||||
|
||||
local function check_signature(bytecode)
|
||||
local sig = string.format(string.format("%o", string.byte(bytecode, 1)))
|
||||
sig = sig .. string.char(string.byte(bytecode, 2))
|
||||
sig = sig .. string.char(string.byte(bytecode, 3))
|
||||
sig = sig .. string.char(string.byte(bytecode, 4))
|
||||
sig = string.gsub(sig, "33", "\033")
|
||||
if sig ~= ladconf.LUA_SIGNATURE then
|
||||
error ("Not a Lua bytecode!")
|
||||
end
|
||||
end
|
||||
|
||||
local function check_version(bytecode)
|
||||
if string.byte(bytecode, 5) ~= ladconf.LUA_VERSION then
|
||||
error ("Disassembler works only with Lua 5.2!")
|
||||
end
|
||||
end
|
||||
|
||||
local function check_header(bytecode)
|
||||
check_signature(bytecode)
|
||||
check_version(bytecode)
|
||||
end
|
||||
|
||||
local function set_auto_flags(bytecode)
|
||||
ladconf.ENDIANNESS = string.byte(bytecode, 7)
|
||||
ladconf.INT = string.byte(bytecode, 8)
|
||||
ladconf.SIZE_T = string.byte(bytecode, 9)
|
||||
ladconf.INSTRUCTION = string.byte(bytecode, 10)
|
||||
ladconf.LUA_NUMBER = string.byte(bytecode, 11)
|
||||
ladconf.INTEGRAL = string.byte(bytecode, 12)
|
||||
end
|
||||
|
||||
local function parse_int(bytecode, k, n)
|
||||
local i = "0x"
|
||||
local s,e = k + (n-1), k + 1
|
||||
for p=s,e,-1 do
|
||||
i = i .. string.format("%02x", string.byte(bytecode, p))
|
||||
end
|
||||
i = i .. string.format("%02x", string.byte(bytecode, k))
|
||||
return tonumber(i),k + (n-1)
|
||||
end
|
||||
|
||||
local function parse_instruction(bytecode, k)
|
||||
local code = "0x"
|
||||
local n = k + (ladconf.INSTRUCTION - 1)
|
||||
for i=n,k,-1 do
|
||||
code = code .. string.format("%02x", string.byte(bytecode, i))
|
||||
end
|
||||
return code,k + (ladconf.INSTRUCTION - 1)
|
||||
end
|
||||
|
||||
local function parse_code(bytecode, k, parsed)
|
||||
parsed.sizecode,k = parse_int(bytecode, k, ladconf.INT)
|
||||
parsed.code = {}
|
||||
for i=1,parsed.sizecode do
|
||||
parsed.code[i],k = parse_instruction(bytecode, k + 1)
|
||||
end
|
||||
return k
|
||||
end
|
||||
|
||||
local function setnilvalue(parsed)
|
||||
parsed.type = ladconf.LUA_TNIL
|
||||
parsed.value = 'nil'
|
||||
end
|
||||
|
||||
local function setbvalue(parsed, boolean)
|
||||
parsed.type = ladconf.LUA_TBOOLEAN
|
||||
if boolean == 0 then
|
||||
parsed.value = 'false'
|
||||
end
|
||||
parsed.value = 'true'
|
||||
end
|
||||
|
||||
local function setnvalue(parsed, number)
|
||||
parsed.type = ladconf.LUA_TNUMBER
|
||||
parsed.value = number
|
||||
end
|
||||
|
||||
local function iscntrl(x)
|
||||
if (x >= 0 and x <= 31) or (x == 127) then return true end
|
||||
return false
|
||||
end
|
||||
|
||||
local function isprint(x)
|
||||
return not iscntrl(x)
|
||||
end
|
||||
|
||||
local function fixed_string(str)
|
||||
local new_str = ""
|
||||
for i=1,string.len(str) do
|
||||
char = string.byte(str, i)
|
||||
if char == 34 then new_str = new_str .. string.format("\\\"")
|
||||
elseif char == 92 then new_str = new_str .. string.format("\\\\")
|
||||
elseif char == 7 then new_str = new_str .. string.format("\\a")
|
||||
elseif char == 8 then new_str = new_str .. string.format("\\b")
|
||||
elseif char == 12 then new_str = new_str .. string.format("\\f")
|
||||
elseif char == 10 then new_str = new_str .. string.format("\\n")
|
||||
elseif char == 13 then new_str = new_str .. string.format("\\r")
|
||||
elseif char == 9 then new_str = new_str .. string.format("\\t")
|
||||
elseif char == 11 then new_str = new_str .. string.format("\\v")
|
||||
else
|
||||
if isprint(char) then
|
||||
new_str = new_str .. string.format("%c", char)
|
||||
else
|
||||
new_str = new_str .. string.format("\\%03d", char)
|
||||
end
|
||||
end
|
||||
end
|
||||
return new_str
|
||||
end
|
||||
|
||||
local function setsvalue(parsed, string)
|
||||
parsed.type = ladconf.LUA_TSTRING
|
||||
parsed.value = fixed_string(string)
|
||||
end
|
||||
|
||||
local function parse_number(bytecode, k)
|
||||
sign = 1
|
||||
mantissa = string.byte(bytecode, k+6) % 16
|
||||
for i=k+5,k,-1 do mantissa = mantissa * 256 + string.byte(bytecode, i) end
|
||||
if string.byte(bytecode,k+7) > 127 then sign = -1 end
|
||||
exponent = (string.byte(bytecode, k+7) % 128) * 16 + math.floor(string.byte(bytecode, k+6) / 16)
|
||||
if exponent == 0 then return 0 end
|
||||
mantissa = (math.ldexp(mantissa, -52) + 1) * sign
|
||||
return math.ldexp(mantissa, exponent - 1023)
|
||||
end
|
||||
|
||||
local function parse_string(bytecode, k, len)
|
||||
local str = ""
|
||||
for i=k+1,k+(len-1) do
|
||||
local byte = string.byte(bytecode, i)
|
||||
if byte ~= 0 then
|
||||
str = str .. string.char(byte)
|
||||
else
|
||||
str = str .. '\0'
|
||||
end
|
||||
end
|
||||
return str
|
||||
end
|
||||
|
||||
local function parse_constants(bytecode, k, parsed)
|
||||
parsed.sizek,k = parse_int(bytecode, k, ladconf.INT)
|
||||
parsed.k = {}
|
||||
for i=1,parsed.sizek do
|
||||
k = k + 1
|
||||
local t = string.byte(bytecode, k)
|
||||
parsed.k[i] = {}
|
||||
if t == ladconf.LUA_TNIL then
|
||||
setnilvalue(parsed.k[i])
|
||||
elseif t == ladconf.LUA_TBOOLEAN then
|
||||
k = k + 1
|
||||
setbvalue(parsed.k[i], string.byte(bytecode, k))
|
||||
elseif t == ladconf.LUA_TNUMBER then
|
||||
setnvalue(parsed.k[i], parse_number(bytecode, k + 1))
|
||||
k = k + ladconf.LUA_NUMBER
|
||||
elseif t == ladconf.LUA_TSTRING then
|
||||
local len
|
||||
len,k = parse_int(bytecode, k + 1, ladconf.SIZE_T)
|
||||
setsvalue(parsed.k[i], parse_string(bytecode, k, len))
|
||||
k = k + len
|
||||
end
|
||||
end
|
||||
return k
|
||||
end
|
||||
|
||||
local function parse_upvalues(bytecode, k, parsed)
|
||||
parsed.sizeupvalues,k = parse_int(bytecode, k, ladconf.INT)
|
||||
parsed.upvalues = {}
|
||||
for i=1,parsed.sizeupvalues do
|
||||
parsed.upvalues[i] = {}
|
||||
parsed.upvalues[i].instack = string.byte(bytecode, k + 1)
|
||||
parsed.upvalues[i].idx = string.byte(bytecode, k + 2)
|
||||
k = k + 2
|
||||
end
|
||||
return k
|
||||
end
|
||||
|
||||
local function parse_debug(bytecode, k, parsed)
|
||||
local len,n
|
||||
len,k = parse_int(bytecode, k, ladconf.SIZE_T)
|
||||
parsed.source = parse_string(bytecode, k + 1, len)
|
||||
k = k + len
|
||||
parsed.sizelineinfo,k = parse_int(bytecode, k + 1, ladconf.INT)
|
||||
parsed.lineinfo = {}
|
||||
for i=1,parsed.sizelineinfo do
|
||||
parsed.lineinfo[i],k = parse_int(bytecode, k + 1, ladconf.INT)
|
||||
end
|
||||
parsed.sizelocvars,k = parse_int(bytecode, k + 1, ladconf.INT)
|
||||
parsed.locvars = {}
|
||||
for i=1,parsed.sizelocvars do
|
||||
parsed.locvars[i] = {}
|
||||
len,k = parse_int(bytecode, k + 1, ladconf.SIZE_T)
|
||||
parsed.locvars[i].varname = parse_string(bytecode, k, len)
|
||||
k = k + len
|
||||
parsed.locvars[i].startpc,k = parse_int(bytecode, k + 1, ladconf.INT)
|
||||
parsed.locvars[i].endpc,k = parse_int(bytecode, k + 1, ladconf.INT)
|
||||
end
|
||||
n,k = parse_int(bytecode, k + 1, ladconf.INT)
|
||||
for i=1,n do
|
||||
len,k = parse_int(bytecode, k + 1, ladconf.SIZE_T)
|
||||
parsed.upvalues[i].name = parse_string(bytecode, k, len)
|
||||
k = k + len
|
||||
end
|
||||
return k
|
||||
end
|
||||
|
||||
local function parse_function(bytecode, k, parsed)
|
||||
parsed.linedefined,k = parse_int(bytecode, k + 1, ladconf.INT)
|
||||
parsed.lastlinedefined,k = parse_int(bytecode, k + 1, ladconf.INT)
|
||||
parsed.numparams,k = string.byte(bytecode, k + 1), k + 1
|
||||
parsed.is_vararg,k = string.byte(bytecode, k + 1), k + 1
|
||||
parsed.maxstacksize,k = string.byte(bytecode, k + 1), k + 1
|
||||
k = parse_code(bytecode, k + 1, parsed)
|
||||
k = parse_constants(bytecode, k + 1, parsed)
|
||||
parsed.sizep,k = parse_int(bytecode, k + 1, ladconf.INT)
|
||||
parsed.p = {}
|
||||
for i=1,parsed.sizep do
|
||||
parsed.p[i] = {}
|
||||
parsed.p[i].parent = parsed.id
|
||||
parsed.p[i].id = i
|
||||
k = parse_function(bytecode, k, parsed.p[i])
|
||||
end
|
||||
k = parse_upvalues(bytecode, k + 1, parsed)
|
||||
k = parse_debug(bytecode, k + 1, parsed)
|
||||
return k
|
||||
end
|
||||
|
||||
local function parse(bytecode)
|
||||
local parsed = {}
|
||||
set_auto_flags(bytecode)
|
||||
parsed.id = 0
|
||||
parse_function(bytecode, ladconf.LUAC_HEADERSIZE, parsed)
|
||||
return parsed
|
||||
end
|
||||
|
||||
local function get_opcode(i)
|
||||
return ladconf.get_bit(i, ladconf.POS_OP, ladconf.SIZE_OP) + 1
|
||||
end
|
||||
|
||||
local function getarg_A(i)
|
||||
return ladconf.get_bit(i, ladconf.POS_A, ladconf.SIZE_A)
|
||||
end
|
||||
|
||||
local function getarg_B(i)
|
||||
return ladconf.get_bit(i, ladconf.POS_B, ladconf.SIZE_B)
|
||||
end
|
||||
|
||||
local function getarg_C(i)
|
||||
return ladconf.get_bit(i, ladconf.POS_C, ladconf.SIZE_C)
|
||||
end
|
||||
|
||||
local function getarg_Bx(i)
|
||||
return ladconf.get_bit(i, ladconf.POS_Bx, ladconf.SIZE_Bx)
|
||||
end
|
||||
|
||||
local function getarg_Ax(i)
|
||||
return ladconf.get_bit(i, ladconf.POS_Ax, ladconf.SIZE_Ax)
|
||||
end
|
||||
|
||||
local function getarg_sBx(i)
|
||||
return getarg_Bx(i) - ladconf.MAXARG_sBx
|
||||
end
|
||||
|
||||
local function get_b_mode(o)
|
||||
return ladconf.opmodes[o].B
|
||||
end
|
||||
|
||||
local function get_c_mode(o)
|
||||
return ladconf.opmodes[o].C
|
||||
end
|
||||
|
||||
local function ISK(x)
|
||||
return ladconf.ISK(x)
|
||||
end
|
||||
|
||||
local function INDEXK(x)
|
||||
return ladconf.INDEXK(x)
|
||||
end
|
||||
|
||||
local function MYK(x)
|
||||
return (-1-(x))
|
||||
end
|
||||
|
||||
local function UPVALNAME(parsed, x)
|
||||
if parsed.upvalues[x].name then
|
||||
return parsed.upvalues[x].name
|
||||
end
|
||||
return "-"
|
||||
end
|
||||
|
||||
local function get_funcline(parsed, pc)
|
||||
if parsed.sizelineinfo > 0 then
|
||||
return parsed.lineinfo[pc]
|
||||
end
|
||||
return 0
|
||||
end
|
||||
|
||||
local function print_header(parsed)
|
||||
local function SS(x) if x == 1 then return "" else return "s" end end
|
||||
local function S(x) return x,SS(x) end
|
||||
|
||||
local func_name = "function"
|
||||
local is_vararg = ""
|
||||
|
||||
if parsed.linedefined == 0 and parsed.id == 0 then
|
||||
func_name = "main"
|
||||
end
|
||||
if parsed.is_vararg ~= 0 then
|
||||
is_vararg = "+"
|
||||
end
|
||||
|
||||
io.write(string.format("\n%s <%s:%d,%d> (%d instruction%s at %d)\n",
|
||||
func_name, parsed.source,
|
||||
parsed.linedefined, parsed.lastlinedefined,
|
||||
parsed.sizecode, SS(parsed.sizecode), 0))
|
||||
io.write(string.format("%d%s param%s, %d slot%s, %d upvalue%s, ",
|
||||
parsed.numparams, is_vararg, SS(parsed.numparams),
|
||||
parsed.maxstacksize, SS(parsed.maxstacksize),
|
||||
parsed.sizeupvalues, SS(parsed.sizeupvalues)))
|
||||
io.write(string.format("%d local%s, %d constant%s, %d function%s\n",
|
||||
parsed.sizelocvars, SS(parsed.sizelocvars),
|
||||
parsed.sizek, SS(parsed.sizek),
|
||||
parsed.sizep, SS(parsed.sizep)))
|
||||
end
|
||||
|
||||
local function print_string(str)
|
||||
io.write(string.format('"%s"', str))
|
||||
end
|
||||
|
||||
local function print_constant(parsed, i)
|
||||
if not parsed.k[i] then
|
||||
io.write("nil")
|
||||
return
|
||||
end
|
||||
local t = parsed.k[i].type
|
||||
local v = parsed.k[i].value
|
||||
if t == ladconf.LUA_TNIL or
|
||||
t == ladconf.LUA_TBOOLEAN or
|
||||
t == ladconf.LUA_TNUMBER then
|
||||
io.write(string.format("%s", v))
|
||||
elseif t == ladconf.LUA_TSTRING then
|
||||
print_string(v)
|
||||
end
|
||||
end
|
||||
|
||||
local function print_code(parsed)
|
||||
local n = parsed.sizecode
|
||||
local pc = 1
|
||||
while pc <= n do
|
||||
local i = parsed.code[pc]
|
||||
local o = get_opcode(i)
|
||||
local a = getarg_A(i)
|
||||
local b = getarg_B(i)
|
||||
local c = getarg_C(i)
|
||||
local ax = getarg_Ax(i)
|
||||
local bx = getarg_Bx(i)
|
||||
local sbx = getarg_sBx(i)
|
||||
local line = get_funcline(parsed, pc)
|
||||
local opcode = ladconf.OPCODE[o]
|
||||
|
||||
io.write(string.format("\t%d\t", pc))
|
||||
if line > 0 then
|
||||
io.write(string.format("[%d]\t", line))
|
||||
else
|
||||
io.write(string.format("[-]\t"))
|
||||
end
|
||||
io.write(string.format("%-9s\t", opcode))
|
||||
|
||||
local opmode = ladconf.get_op_mode(o)
|
||||
if opmode == ladconf.iABC then
|
||||
io.write(string.format("%d", a))
|
||||
local bmode = get_b_mode(o)
|
||||
local cmode = get_c_mode(o)
|
||||
if bmode ~= ladconf.OpArgN then
|
||||
if ISK(b) then
|
||||
io.write(string.format(" %d", MYK(INDEXK(b))))
|
||||
else
|
||||
io.write(string.format(" %d", b))
|
||||
end
|
||||
end
|
||||
if cmode ~= ladconf.OpArgN then
|
||||
if ISK(c) then
|
||||
io.write(string.format(" %d", MYK(INDEXK(c))))
|
||||
else
|
||||
io.write(string.format(" %d", c))
|
||||
end
|
||||
end
|
||||
elseif opmode == ladconf.iABx then
|
||||
io.write(string.format("%d", a))
|
||||
local bmode = get_b_mode(o)
|
||||
if bmode == ladconf.OpArgK then
|
||||
io.write(string.format(" %d", MYK(bx)))
|
||||
end
|
||||
if bmode == ladconf.OpArgU then
|
||||
io.write(string.format(" %d", bx))
|
||||
end
|
||||
elseif opmode == ladconf.iAsBx then
|
||||
io.write(string.format("%d %d", a, sbx))
|
||||
elseif opmode == ladconf.iAx then
|
||||
io.write(string.format("%d", MYK(ax)))
|
||||
end
|
||||
|
||||
if opcode == "LOADK" then
|
||||
io.write(string.format("\t; "))
|
||||
print_constant(parsed, bx+1)
|
||||
elseif opcode == "GETUPVAL" or
|
||||
opcode == "SETUPVAL" then
|
||||
io.write(string.format("\t; %s", UPVALNAME(parsed, b+1)))
|
||||
elseif opcode == "GETTABUP" then
|
||||
io.write(string.format("\t; %s", UPVALNAME(parsed, b+1)))
|
||||
if ISK(c) then
|
||||
io.write(string.format(" "))
|
||||
print_constant(parsed, INDEXK(c+1))
|
||||
end
|
||||
elseif opcode == "SETTABUP" then
|
||||
io.write(string.format("\t; %s", UPVALNAME(parsed, a+1)))
|
||||
if ISK(b) then
|
||||
io.write(string.format(" "))
|
||||
print_constant(parsed, INDEXK(b+1))
|
||||
end
|
||||
if ISK(c) then
|
||||
io.write(string.format(" "))
|
||||
print_constant(parsed, INDEXK(c+1))
|
||||
end
|
||||
elseif opcode == "GETTABLE" or
|
||||
opcode == "SELF" then
|
||||
if ISK(c) then
|
||||
io.write(string.format("\t; "))
|
||||
print_constant(parsed, INDEXK(c+1))
|
||||
end
|
||||
elseif opcode == "SETTABLE" or
|
||||
opcode == "ADD" or
|
||||
opcode == "SUB" or
|
||||
opcode == "MUL" or
|
||||
opcode == "DIV" or
|
||||
opcode == "POW" or
|
||||
opcode == "EQ" or
|
||||
opcode == "LT" or
|
||||
opcode == "LE" then
|
||||
if ISK(b) or ISK(c) then
|
||||
io.write(string.format("\t; "))
|
||||
if ISK(b) then
|
||||
print_constant(parsed, INDEXK(b+1))
|
||||
else
|
||||
io.write(string.format("-"))
|
||||
end
|
||||
io.write(string.format(" "))
|
||||
if ISK(c) then
|
||||
print_constant(parsed, INDEXK(c+1))
|
||||
else
|
||||
io.write(string.format("-"))
|
||||
end
|
||||
end
|
||||
elseif opcode == "JMP" or
|
||||
opcode == "FORLOOP" or
|
||||
opcode == "FORPREP" or
|
||||
opcode == "TFORLOOP" then
|
||||
io.write(string.format("\t; to %d", sbx+pc+1))
|
||||
elseif opcode == "CLOSURE" then
|
||||
io.write(string.format("\t; 0x"))
|
||||
elseif opcode == "SETLIST" then
|
||||
if c == 0 then
|
||||
pc = pc + 1
|
||||
io.write(string.format("\t; %d", parsed.code[pc]))
|
||||
else
|
||||
io.write(string.format("\t; %d", c))
|
||||
end
|
||||
elseif opcode == "EXTRAARG" then
|
||||
io.write(string.format("\t; "))
|
||||
print_constant(parsed, ax+1)
|
||||
end
|
||||
io.write(string.format("\n"))
|
||||
pc = pc + 1
|
||||
end
|
||||
end
|
||||
|
||||
local function print_debug(parsed)
|
||||
local n
|
||||
n = parsed.sizek
|
||||
io.write(string.format("constants (%d) for 0:\n", n))
|
||||
for i=1,n do
|
||||
io.write(string.format("\t%d\t", i))
|
||||
print_constant(parsed, i)
|
||||
io.write(string.format("\n"))
|
||||
end
|
||||
n = parsed.sizelocvars
|
||||
io.write(string.format("locals (%d) for 0:\n", n))
|
||||
for i=1,n do
|
||||
io.write(string.format("\t%d\t%s\t%d\t%d\n", i-1,
|
||||
parsed.locvars[i].varname,
|
||||
parsed.locvars[i].startpc+1,
|
||||
parsed.locvars[i].endpc+1))
|
||||
end
|
||||
n = parsed.sizeupvalues
|
||||
io.write(string.format("upvalues (%d) for 0:\n", n))
|
||||
for i=1,n do
|
||||
io.write(string.format("\t%d\t%s\t%d\t%d\n", i-1,
|
||||
UPVALNAME(parsed, i),
|
||||
parsed.upvalues[i].instack,
|
||||
parsed.upvalues[i].idx))
|
||||
end
|
||||
end
|
||||
|
||||
local function print_function(parsed, full)
|
||||
local n = parsed.sizep
|
||||
print_header(parsed)
|
||||
print_code(parsed)
|
||||
if full then print_debug(parsed) end
|
||||
for i=1,n do
|
||||
print_function(parsed.p[i], full)
|
||||
end
|
||||
end
|
||||
|
||||
local function write_header(output, parsed)
|
||||
local fname
|
||||
local np = parsed.numparams
|
||||
if parsed.linedefined == 0 and parsed.id == 0 then
|
||||
fname = "main"
|
||||
else
|
||||
fname = string.format("F_%s_%s", parsed.parent, parsed.id)
|
||||
end
|
||||
output:write(string.format("\nfunction %s(%s):\n", fname, np))
|
||||
end
|
||||
|
||||
local function write_par(output, x)
|
||||
output:write(string.format("%s", x))
|
||||
end
|
||||
|
||||
local function write_sep(output)
|
||||
output:write(string.format(", "))
|
||||
end
|
||||
|
||||
local function R(x)
|
||||
return "$" .. x
|
||||
end
|
||||
|
||||
local function UpValue(parsed, x)
|
||||
return parsed.upvalues[x+1].name
|
||||
end
|
||||
|
||||
local function KST(parsed, x)
|
||||
return parsed.k[x+1].value
|
||||
end
|
||||
|
||||
local function Kst(parsed, x)
|
||||
local t = parsed.k[x+1].type
|
||||
local v = parsed.k[x+1].value
|
||||
if t == ladconf.LUA_TNIL or
|
||||
t == ladconf.LUA_TBOOLEAN or
|
||||
t == ladconf.LUA_TNUMBER then
|
||||
return v
|
||||
elseif t == ladconf.LUA_TSTRING then
|
||||
return '"' .. v .. '"'
|
||||
end
|
||||
end
|
||||
|
||||
local function RKST(parsed, x)
|
||||
if ISK(x) then
|
||||
return KST(parsed, INDEXK(x))
|
||||
else
|
||||
return R(x)
|
||||
end
|
||||
end
|
||||
|
||||
local function RK(parsed, x)
|
||||
if ISK(x) then
|
||||
return Kst(parsed, INDEXK(x))
|
||||
else
|
||||
return R(x)
|
||||
end
|
||||
end
|
||||
|
||||
local function KPROTO(parsed, x)
|
||||
local parent = parsed.p[x+1].parent
|
||||
local id = parsed.p[x+1].id
|
||||
return string.format("F_%s_%s", parent, id)
|
||||
end
|
||||
|
||||
local function write_code(output, parsed)
|
||||
local n = parsed.sizecode
|
||||
for pc=1,n do
|
||||
local i = parsed.code[pc]
|
||||
local o = get_opcode(i)
|
||||
local a = getarg_A(i)
|
||||
local b = getarg_B(i)
|
||||
local c = getarg_C(i)
|
||||
local ax = getarg_Ax(i)
|
||||
local bx = getarg_Bx(i)
|
||||
local sbx = getarg_sBx(i)
|
||||
local line = get_funcline(parsed, pc)
|
||||
local opcode = ladconf.OPCODE[o]
|
||||
|
||||
output:write(string.format("\t%d\t", pc))
|
||||
output:write(string.format("[%d]\t", line))
|
||||
output:write(string.format("%-9s\t", opcode))
|
||||
|
||||
if opcode == "MOVE" or
|
||||
opcode == "UNM" or
|
||||
opcode == "NOT" or
|
||||
opcode == "LEN" then
|
||||
-- R(A) R(B)
|
||||
write_par(output, R(a))
|
||||
write_sep(output)
|
||||
write_par(output, R(b))
|
||||
elseif opcode == "LOADK" then
|
||||
-- R(A) Kst(Bx)
|
||||
write_par(output, R(a))
|
||||
write_sep(output)
|
||||
write_par(output, Kst(parsed, bx))
|
||||
elseif opcode == "LOADKX" then
|
||||
-- R(A)
|
||||
write_par(output, R(a))
|
||||
elseif opcode == "LOADBOOL" or
|
||||
opcode == "NEWTABLE" or
|
||||
opcode == "CALL" or
|
||||
opcode == "TAILCALL" or
|
||||
opcode == "SETLIST" then
|
||||
-- R(A) B C
|
||||
write_par(output, R(a))
|
||||
write_sep(output)
|
||||
write_par(output, b)
|
||||
write_sep(output)
|
||||
write_par(output, c)
|
||||
elseif opcode == "LOADNIL" or
|
||||
opcode == "RETURN" or
|
||||
opcode == "VARARG" then
|
||||
-- R(A) B
|
||||
write_par(output, R(a))
|
||||
write_sep(output)
|
||||
write_par(output, b)
|
||||
elseif opcode == "GETUPVAL" or
|
||||
opcode == "SETUPVAL" then
|
||||
-- R(A) UpValue(B)
|
||||
write_par(output, R(a))
|
||||
write_sep(output)
|
||||
write_par(output, UpValue(parsed, b))
|
||||
elseif opcode == "GETTABUP" then
|
||||
-- R(A) UpValue(B) RK(C)
|
||||
write_par(output, R(a))
|
||||
write_sep(output)
|
||||
write_par(output, UpValue(parsed, b))
|
||||
write_sep(output)
|
||||
write_par(output, RK(parsed, c))
|
||||
elseif opcode == "GETTABLE" or
|
||||
opcode == "SELF" then
|
||||
-- R(A) R(B) RK(C)
|
||||
write_par(output, R(a))
|
||||
write_sep(output)
|
||||
write_par(output, R(b))
|
||||
write_sep(output)
|
||||
write_par(output, RK(parsed, c))
|
||||
elseif opcode == "SETTABUP" then
|
||||
-- UpValue(A) RK(B) RK(C)
|
||||
write_par(output, UpValue(parsed, a))
|
||||
write_sep(output)
|
||||
write_par(output, RK(parsed, b))
|
||||
write_sep(output)
|
||||
write_par(output, RK(parsed, c))
|
||||
elseif opcode == "SETTABLE" or
|
||||
opcode == "ADD" or
|
||||
opcode == "SUB" or
|
||||
opcode == "MUL" or
|
||||
opcode == "DIV" or
|
||||
opcode == "MOD" or
|
||||
opcode == "POW" then
|
||||
-- R(A) RK(B) RK(C)
|
||||
write_par(output, R(a))
|
||||
write_sep(output)
|
||||
write_par(output, RK(parsed, b))
|
||||
write_sep(output)
|
||||
write_par(output, RK(parsed, c))
|
||||
elseif opcode == "CONCAT" then
|
||||
-- R(A) R(B) R(C)
|
||||
write_par(output, R(a))
|
||||
write_sep(output)
|
||||
write_par(output, R(b))
|
||||
write_sep(output)
|
||||
write_par(output, R(c))
|
||||
elseif opcode == "JMP" then
|
||||
-- A sBx
|
||||
write_par(output, a)
|
||||
write_sep(output)
|
||||
write_par(output, sbx + pc + 1)
|
||||
elseif opcode == "EQ" or
|
||||
opcode == "LT" or
|
||||
opcode == "LE" then
|
||||
-- A RK(B) RK(C)
|
||||
write_par(output, a)
|
||||
write_sep(output)
|
||||
write_par(output, RK(parsed, b))
|
||||
write_sep(output)
|
||||
write_par(output, RK(parsed, c))
|
||||
elseif opcode == "TEST" or
|
||||
opcode == "TFORCALL" then
|
||||
-- R(A) C
|
||||
write_par(output, R(a))
|
||||
write_sep(output)
|
||||
write_par(output, c)
|
||||
elseif opcode == "TESTSET" then
|
||||
-- R(A) R(B) C
|
||||
write_par(output, R(a))
|
||||
write_sep(output)
|
||||
write_par(output, R(b))
|
||||
write_sep(output)
|
||||
write_par(output, c)
|
||||
elseif opcode == "FORLOOP" or
|
||||
opcode == "FORPREP" or
|
||||
opcode == "TFORLOOP" then
|
||||
-- R(A) sBx
|
||||
write_par(output, R(a))
|
||||
write_sep(output)
|
||||
write_par(output, sbx + pc + 1)
|
||||
elseif opcode == "CLOSURE" then
|
||||
-- R(A) KPROTO(Bx)
|
||||
write_par(output, R(a))
|
||||
write_sep(output)
|
||||
write_par(output, KPROTO(parsed, bx))
|
||||
elseif opcode == "EXTRAARG" then
|
||||
-- Ax
|
||||
po = get_opcode(parsed.code[pc-1])
|
||||
popcode = ladconf.OPCODE[po]
|
||||
if popcode == "LOADKX" then
|
||||
write_par(output, Kst(parsed, ax))
|
||||
elseif popcode == "SETLIST" then
|
||||
write_par(output, ax)
|
||||
end
|
||||
end
|
||||
output:write(string.format("\n"))
|
||||
end
|
||||
end
|
||||
|
||||
local function write_function(output, parsed)
|
||||
local n = parsed.sizep
|
||||
write_header(output, parsed)
|
||||
write_code(output, parsed)
|
||||
for i=1,n do
|
||||
write_function(output, parsed.p[i])
|
||||
end
|
||||
end
|
||||
|
||||
local function write(filename, parsed)
|
||||
local output = assert(io.open(filename, "w"))
|
||||
write_function(output, parsed)
|
||||
output:close()
|
||||
end
|
||||
|
||||
local disassembler = {
|
||||
parse = parse,
|
||||
write = write,
|
||||
print_function = print_function,
|
||||
}
|
||||
|
||||
return disassembler
|
||||
@@ -0,0 +1,23 @@
|
||||
|
||||
function main(0):
|
||||
CLOSURE $0, fat
|
||||
SETTABUP _ENV, fat, $0
|
||||
GETTABUP $0, _ENV, print
|
||||
GETTABUP $1, _ENV, fat
|
||||
LOADK $2, 5
|
||||
CALL $1, 2, 0
|
||||
CALL $0, 0, 1
|
||||
RETURN $0, 1
|
||||
|
||||
function fat(1):
|
||||
EQ 0, $0, 0
|
||||
JMP 0, label1 ; jump to label1
|
||||
LOADK $1, 1
|
||||
RETURN $1, 2
|
||||
label1: GETTABUP $1, _ENV, fat ; create label1
|
||||
SUB $2, $0, 1
|
||||
CALL $1, 2, 2
|
||||
MUL $1, $0, $1
|
||||
RETURN $1, 2
|
||||
RETURN $0, 1
|
||||
|
||||
@@ -0,0 +1,6 @@
|
||||
|
||||
function main(0):
|
||||
1 [1] GETTABUP $0, _ENV, "print"
|
||||
2 [1] LOADK $1, "hello world!"
|
||||
3 [1] CALL $0, 2, 1
|
||||
4 [1] RETURN $0, 1
|
||||
@@ -0,0 +1 @@
|
||||
print ("hello world!")
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 11 KiB |
@@ -0,0 +1,869 @@
|
||||
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
|
||||
<html>
|
||||
<head>
|
||||
<meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1">
|
||||
<title>Lua Assembler/Disassembler</title>
|
||||
<style type=text/css>
|
||||
<!--
|
||||
hr {
|
||||
background-color: #BCAE79;
|
||||
border-width: 1px;
|
||||
color: #BCAE79;
|
||||
height: 1px;
|
||||
padding: 0;
|
||||
width: 98%;
|
||||
}
|
||||
|
||||
a {
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
h3 {
|
||||
color: #3175A2;
|
||||
}
|
||||
|
||||
body {
|
||||
background: #F4F4F4;
|
||||
font-size: 12px;
|
||||
line-height: 150%;
|
||||
font-family: verdana, sans-serif
|
||||
}
|
||||
|
||||
div#container {
|
||||
width: 80%;
|
||||
margin-left: 10%;
|
||||
margin-right: 10%;
|
||||
border: 1px solid #F0F0F0;
|
||||
background: white;
|
||||
}
|
||||
|
||||
div#logo {
|
||||
margin: 2px 2px 2px 2px;
|
||||
}
|
||||
|
||||
div#content {
|
||||
margin: 20px 10px 10px 10px;
|
||||
border: 1px solid #F0F0F0;
|
||||
padding-left: 10px;
|
||||
padding-right: 10px;
|
||||
text-align: justify;
|
||||
}
|
||||
|
||||
div#menu {
|
||||
margin: 2px 2px 2px 20px;
|
||||
}
|
||||
|
||||
div#footer {
|
||||
margin: 2px 10px 20px 10px;
|
||||
clear: both;
|
||||
border: 1px solid #F0F0F0;
|
||||
}
|
||||
|
||||
.lua-code {
|
||||
background-color: #F4F4F4;
|
||||
border: 1px solid silver;
|
||||
font-family: "Andale Mono", monospace;
|
||||
margin-left: 1em;
|
||||
margin-right: 1em;
|
||||
padding: 1em;
|
||||
}
|
||||
|
||||
pre span {color:gray}
|
||||
|
||||
code {
|
||||
font-size: medium;
|
||||
font-weight: bold;
|
||||
color: gray;
|
||||
}
|
||||
|
||||
table#solid {
|
||||
border: 1px solid black;
|
||||
border-collapse: collapse;
|
||||
margin-left: auto;
|
||||
margin-right: auto;
|
||||
}
|
||||
|
||||
table#solid th {
|
||||
border: 1px solid black;
|
||||
padding: 0.5em;
|
||||
}
|
||||
|
||||
table#solid td {
|
||||
border: 1px solid black;
|
||||
padding: 0.5em;
|
||||
}
|
||||
|
||||
span.function {
|
||||
color: #00008BA2;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
table.func-parts {
|
||||
padding-left: 10px;
|
||||
padding-bottom: 10px;
|
||||
}
|
||||
|
||||
table.func-parts td.part {
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
div.api {
|
||||
padding-left: 10px;
|
||||
padding-top: 10px;
|
||||
}
|
||||
|
||||
//
|
||||
-->
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div id="container">
|
||||
<div id="logo">
|
||||
<center><img alt="xmlparser logo" src="logo1.png"></center>
|
||||
<div id="menu">
|
||||
<center>
|
||||
<h3>Lua Assembler/Disassembler</h3>
|
||||
<a href="#description">Description</a> | <a href="#requirements">Requirements</a> | <a href="#install">Install</a> |
|
||||
<a href="#assembler">Assembler</a> | <a href="#disassembler">Disassembler</a> |
|
||||
<a href="#how-to-use">How To Use</a> | <a href="#examples">Examples</a> | <a href="#syntax">Assembly Syntax</a> |
|
||||
<a href="#opcodes">Instructions</a> | <a href="#notes">Notes</a> | <a href="#licence">Licence</a>
|
||||
</center>
|
||||
</div>
|
||||
</div>
|
||||
<div id="content">
|
||||
<h3><a name="description">Description</a></h3>
|
||||
|
||||
<p><code>Lua Assembler/Disassembler</code> is a tool that can be used to generate Lua bytecode and Assembly code.
|
||||
Lua bytecode is generated through the assembler provided, while Assembly code is generated by the disassembler,
|
||||
which inspects Lua bytecode in order to reproduce it into an Assembly language.
|
||||
|
||||
The assembler uses <a href="http://www.inf.puc-rio.br/~roberto/lpeg/">Lpeg</a> in order to implement the parser
|
||||
for our Assembly syntax, which is presented on this document.
|
||||
</p>
|
||||
|
||||
<hr>
|
||||
|
||||
<h3><a name="requirements">Requirements</a></h3>
|
||||
<code>Lua Assembler/Disassembler</code> is compatible with Lua version 5.2.0 and Lpeg version 0.10.2.
|
||||
<hr />
|
||||
|
||||
<h3><a name="install">Install</a></h3>
|
||||
<p>
|
||||
To run <code>Lua Assembler/Disassembler</code> in your computer, first you need install the
|
||||
<a href="http://www.lua.org/download.html">Lua language</a> and the
|
||||
<a href="http://www.inf.puc-rio.br/~roberto/lpeg/#download">Lpeg</a> library.
|
||||
The <code>Lua Assembler/Disassembler</code> may be executed as an application/script and
|
||||
can be used as a module in your Lua code too.
|
||||
In order to install, please, <a href="">download</a> the source code, extract it and then
|
||||
follow below instructions to have the applications/scripts and modules installed.
|
||||
</p>
|
||||
<h4><a name="install_script">Installing scripts</a></h4>
|
||||
<p>
|
||||
Just copy the following files to a directory in your computer or
|
||||
to your $PATH (which will depend on the operating system).
|
||||
</p>
|
||||
<ul>
|
||||
<li><code>luaa.lua</code></li>
|
||||
<li><code>luad.lua</code></li>
|
||||
</ul>
|
||||
<p>
|
||||
Since the applications/scripts use the modules to run assembler and disassembler
|
||||
functions, please, do not forget to follow below steps in order to get them installed.
|
||||
</p>
|
||||
|
||||
<h4><a name="install_module">Installing modules</a></h4>
|
||||
<p>
|
||||
Just copy the following files to the first directory that is set in <b>package.path</b>.
|
||||
</p>
|
||||
<ul>
|
||||
<li><code>assembler.lua</code></li>
|
||||
<li><code>disassembler.lua</code></li>
|
||||
<li><code>ladconf.lua</code></li>
|
||||
</ul>
|
||||
<p>
|
||||
You can also copy above files to other directory, if that's the case, please, do not
|
||||
forget to change <b>package.path</b>, in your Lua code, in order to look for the modules
|
||||
in the directory that you have installed them.
|
||||
</p>
|
||||
<hr />
|
||||
|
||||
<h3><a name="assembler">Assembler</a></h3>
|
||||
<p>
|
||||
The assembler might be used as a stand alone application/script or as a module that
|
||||
you require in your code.
|
||||
</p>
|
||||
|
||||
<h4><a name="assembler_script">Assembler script</a></h4>
|
||||
The stand alone application/script is called <b>luaa.lua</b>.
|
||||
|
||||
The following synopsis should be used:
|
||||
|
||||
<pre class="lua-code">
|
||||
lua luaa.lua [options] [filename]
|
||||
</pre>
|
||||
|
||||
<p>Available options are listed below and must be separate.</p>
|
||||
|
||||
<a name="op-com"><h3 style="color:#00008B;">-h</h3></a>
|
||||
<p>
|
||||
Just prints a help message listing all available options and a brief explanation about them.
|
||||
</p>
|
||||
<!--
|
||||
<a name="op-mat"><h3 style="color:#00008B;">-b</h3></a>
|
||||
<p>
|
||||
Output file on <b>big endian</b>, instead of the default <b>little endian</b>.
|
||||
</p>
|
||||
-->
|
||||
<a name="op-ver"><h3 style="color:#00008B;">-o file</h3></a>
|
||||
<p>
|
||||
Output to <b>file</b>, instead of the default <b>luaa.out</b>.
|
||||
Be careful to not overwrite precious files since you might specify the output file as an existent source file.
|
||||
</p>
|
||||
|
||||
<h4><a name="assembler_module">Assembler module</a></h4>
|
||||
<p>
|
||||
The module is called <b>assembler</b> and something like below line should be used in order to make
|
||||
its functions available.
|
||||
</p>
|
||||
<pre class="lua-code">
|
||||
local assembler = require("assembler")
|
||||
</pre>
|
||||
<p>Available functions are listed below.</p>
|
||||
|
||||
<a name="op-com"><h3 style="color:#00008B;">assembler.parse(contents)</h3></a>
|
||||
<p>
|
||||
The function parse gets the contents of an Assembly source code as its only
|
||||
argument and returns its Abstract Syntax Tree (AST).
|
||||
</p>
|
||||
<a name="op-mat"><h3 style="color:#00008B;">assembler.traverse(ast)</h3></a>
|
||||
<p>
|
||||
The function traverse gets the AST, returned by parse function, and inspects
|
||||
the AST to find errors. If no errors were found, than all information inside
|
||||
the AST are combined into a table that will be used to write the correspondent
|
||||
Lua bytecode.
|
||||
</p>
|
||||
<a name="op-ver"><h3 style="color:#00008B;">assembler.write(filename, parsed)</h3></a>
|
||||
<p>
|
||||
The function write gets two parameters: the first one is the filename were
|
||||
Lua bytecode should be written, while the second one is the table returned
|
||||
by traverse function.
|
||||
</p>
|
||||
<a name="op-ver"><h3 style="color:#00008B;">assembler.print_ast(ast)</h3></a>
|
||||
<p>
|
||||
The function print_ast might be used for debug purposes. It receives the AST
|
||||
as its only parameters and prints it.
|
||||
</p>
|
||||
|
||||
<hr />
|
||||
|
||||
<h3><a name="disassembler">Disassembler</a></h3>
|
||||
<p>
|
||||
The disassembler might be used as a stand alone application/script or as a module that
|
||||
you require in your code.
|
||||
</p>
|
||||
|
||||
<h4><a name="disassembler_script">Disassembler script</a></h4>
|
||||
The stand alone application/script is called <b>luad.lua</b>.
|
||||
|
||||
The following synopsis should be used:
|
||||
|
||||
<pre class="lua-code">
|
||||
lua luad.lua [options] [filename]
|
||||
</pre>
|
||||
|
||||
<p>Available options are listed below and must be separate.</p>
|
||||
|
||||
<a name="op-com"><h3 style="color:#00008B;">-h</h3></a>
|
||||
<p>
|
||||
Just prints a help message listing all available options and a brief explanation about them.
|
||||
</p>
|
||||
<a name="op-mat"><h3 style="color:#00008B;">-l</h3></a>
|
||||
<p>
|
||||
Produce a listing of the compiled bytecode for Lua's virtual machine on <b>luac -l</b> style.
|
||||
Use -l -l for full listing.
|
||||
</p>
|
||||
<a name="op-ver"><h3 style="color:#00008B;">-o file</h3></a>
|
||||
<p>
|
||||
Output to <b>file</b>, instead of the default <b>luad.asm</b>.
|
||||
Be careful to not overwrite precious files since you might specify the output file as an existent source file.
|
||||
</p>
|
||||
|
||||
<h4><a name="disassembler_module">Disassembler module</a></h4>
|
||||
<p>
|
||||
The module is called <b>disassembler</b> and something like below line should be used in order to make
|
||||
its functions available.
|
||||
</p>
|
||||
<pre class="lua-code">
|
||||
local disassembler = require("disassembler")
|
||||
</pre>
|
||||
<p>Available functions are listed below.</p>
|
||||
|
||||
<a name="op-com"><h3 style="color:#00008B;">disassembler.parse(bytecode)</h3></a>
|
||||
The function parse gets a chunk of Lua bytecode as its only
|
||||
argument and returns a table containing all information that
|
||||
are necessary to write its correspondent Assembly code.
|
||||
<p>
|
||||
</p>
|
||||
<a name="op-mat"><h3 style="color:#00008B;">disassembler.write(filename, parsed)</h3></a>
|
||||
The function write gets two parameters: the first one is the filename were
|
||||
the Assembly code should be written, while the second one is the table returned
|
||||
by parse function.
|
||||
<p>
|
||||
</p>
|
||||
<a name="op-ver"><h3 style="color:#00008B;">disassembler.print_function(parsed, full)</h3></a>
|
||||
<p>
|
||||
The function print_function might be used for printing Lua bytecode in <b>luac -l</b> style.
|
||||
It has two parameters: the first one is the table returned by parse function and
|
||||
the second one is a boolean value indication if full listing is expected or not.
|
||||
</p>
|
||||
|
||||
<hr/>
|
||||
|
||||
<h3><a name="how-to-use">How To Use</a></h3>
|
||||
|
||||
<p>The following command line shows how to use the <code>disassembler</code> in order to generate the assembly code
|
||||
for a Lua source file:
|
||||
<pre class="lua-code">
|
||||
$ lua luad.lua -o hello.asm hello.lua
|
||||
</pre>
|
||||
|
||||
The above command line outputs <b>hello.asm</b> which contains the assembly code for <b>hello.lua</b>.
|
||||
<br><br>
|
||||
It is important to remember that the <code>disassembler</code> can be used with a Lua bytecode too, as we shown below:
|
||||
<pre class="lua-code">
|
||||
$ lua luad.lua luac.out
|
||||
</pre>
|
||||
|
||||
Notice that in this case we haven't specified the output file, in this way the output will be the default <b>luadd.asm</b>.
|
||||
<br><br>
|
||||
<p>The following command line shows how to use the <code>assembler</code> in order to generate Lua bytecode from
|
||||
our assembly code:
|
||||
<pre class="lua-code">
|
||||
$ lua luaa.lua -o hello.out hello.asm
|
||||
</pre>
|
||||
|
||||
The above command line outputs <b>hello.out</b> which contains Lua bytecode for <b>hello.asm</b> and can be executed using lua.
|
||||
<br><br>
|
||||
It is important to remember that if you do not specify the output file, then Lua bytecode will be generated as <b>luaa.out</b> by default.
|
||||
|
||||
<hr />
|
||||
|
||||
<h3><a name="examples">Examples</a></h3>
|
||||
|
||||
In this section we show some examples of our <b>assembly</b> language. The complete syntax can be checked
|
||||
at <a href="#syntax">Assembly Syntax</a> as well as opcodes can be checked at <a href="#opcodes">Instructions</a>.
|
||||
|
||||
Here we show the assembly code that were generated, on above section, for <b>hello.lua</b> which is a simple
|
||||
<b>hello world</b> program.
|
||||
|
||||
<pre class="lua-code">
|
||||
function main(0):
|
||||
1 [1] GETTABUP $0, _ENV, "print"
|
||||
2 [1] LOADK $1, "hello world!"
|
||||
3 [1] CALL $0, 2, 1
|
||||
4 [1] RETURN $0, 1
|
||||
</pre>
|
||||
|
||||
Once we use the <code>assembler</code> to output the Lua bytecode we can execute it using lua, as follows:
|
||||
|
||||
<pre class="lua-code">
|
||||
$ lua hello.out
|
||||
hello world!
|
||||
$
|
||||
</pre>
|
||||
|
||||
Now we define a recursive factorial in our Assembly language. Notice that <b>line number</b> and
|
||||
<b>source line number</b> are not needed when we are writting the Assembly code. However, they are
|
||||
generated when the <b>disassembler</b> process is performed.
|
||||
|
||||
<pre class="lua-code">
|
||||
function main(0):
|
||||
CLOSURE $0, fat
|
||||
SETTABUP _ENV, fat, $0
|
||||
GETTABUP $0, _ENV, print
|
||||
GETTABUP $1, _ENV, fat
|
||||
LOADK $2, 5
|
||||
CALL $1, 2, 0
|
||||
CALL $0, 0, 1
|
||||
RETURN $0, 1
|
||||
|
||||
function fat(1):
|
||||
EQ 0, $0, 0
|
||||
JMP 0, label1 ; jump to label1
|
||||
LOADK $1, 1
|
||||
RETURN $1, 2
|
||||
label1: GETTABUP $1, _ENV, fat ; create label1
|
||||
SUB $2, $0, 1
|
||||
CALL $1, 2, 2
|
||||
MUL $1, $0, $1
|
||||
RETURN $1, 2
|
||||
RETURN $0, 1
|
||||
</pre>
|
||||
|
||||
Notice that we have used a <b>label</b> in <b>fat</b> function since it is easier to check where to jump
|
||||
when we are writing manual assembly code.
|
||||
Also notice that a comment starts with a semicolon (<b>;</b>) and finishes at the end of the line.
|
||||
A function <b>main</b> is necessary for all Assembly codes and the number of
|
||||
parameters for the function that is being defined should be specified between
|
||||
parenthesis.
|
||||
|
||||
<hr />
|
||||
|
||||
<h3><a name="syntax">Assembly Syntax</a></h3>
|
||||
|
||||
Here is the complete syntax of Assembly in <code>lpeg re</code> module.
|
||||
|
||||
<pre class="lua-code">
|
||||
prog <- s function* !.
|
||||
function <- header codelist
|
||||
header <- "function" s name s "(" s n s ")" s ":" s
|
||||
codelist <- code+
|
||||
code <- autocode / manualcode
|
||||
autocode <- n s ln s op s param s ("," s param s)*
|
||||
manualcode <- (label s)? op s param s ("," s param s)*
|
||||
n <- %d+
|
||||
label <- name ":"
|
||||
name <- !reserved {[a-zA-Z_][a-zA-Z0-9_]*}
|
||||
ln <- "[" %d+ "]"
|
||||
op <- !reserved %a+
|
||||
param <- register / number / string
|
||||
register <- "$" n
|
||||
number <- hex / float / int
|
||||
string <- name / shortstr
|
||||
shortstr <- '"' ('\\' / '\"' / !'"' .)* '"' / "'" ("\\" / "\'" / !"'" .)* "'"
|
||||
hex <- "-"? "0" [xX] %x+
|
||||
float <- "-"? ( (%d+ "." %d* / "." %d+) e? / %d+ e )
|
||||
e <- [eE] [+-]? n
|
||||
int <- "-"? n
|
||||
s <- (space / comment)*
|
||||
space <- %s+
|
||||
comment <- ";" (!%nl .)*
|
||||
reserved <- "function"
|
||||
</pre>
|
||||
<hr />
|
||||
|
||||
<h3><a name="opcodes">Instructions</a></h3>
|
||||
|
||||
<p>
|
||||
We have four main types of instructions, they are so called <b>iABC</b>, <b>iABx</b> , <b>iAsBx</b>
|
||||
and <b>iAx</b>. The instructions should be used as follows:
|
||||
</p>
|
||||
|
||||
<center>
|
||||
<table id='solid'>
|
||||
<tr>
|
||||
<td><strong>Instruction type</strong></td>
|
||||
<td><strong>Pattern</strong></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>iABC</td>
|
||||
<td>OPCODE A, B, C</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>iABx</td>
|
||||
<td>OPCODE A, Bx</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>iAsBx</td>
|
||||
<td>OPCODE A, sBx</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>iAx</td>
|
||||
<td>OPCODE Ax</td>
|
||||
</tr>
|
||||
</table>
|
||||
</center>
|
||||
|
||||
<p>
|
||||
Some <b>iABC</b> instructions do not use argument B or C and below we specify when that is the case.
|
||||
Nonetheless, most of the <b>iABC</b> instructions follow the pattern described in above table.
|
||||
</p>
|
||||
|
||||
<p>
|
||||
It is also important to keep in mind that parameters should comply with the <a href="#syntax">Assembly Syntax</a>.
|
||||
Here a some examples:
|
||||
</p>
|
||||
|
||||
<center>
|
||||
<table id='solid'>
|
||||
<tr>
|
||||
<td><strong>Parameter type</strong></td>
|
||||
<td><strong>Example</strong></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Register</td>
|
||||
<td>$0, $1, $2, ...</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Number</td>
|
||||
<td>0, 1, 2, ... / -1, -2, ... / 0.1, 0.2, ..., 1.0, 1.1, ... / -0.1, -0.2, ..., -1.0, -1,1, ...</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Name</td>
|
||||
<td>F_0_1, blah, foo, ...</td>
|
||||
</tr>
|
||||
<!--
|
||||
<tr>
|
||||
<td>Nil</td>
|
||||
<td>NIL</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Boolean</td>
|
||||
<td>TRUE / FALSE</td>
|
||||
</tr>
|
||||
-->
|
||||
<tr>
|
||||
<td>String</td>
|
||||
<td>"oi, tudo bem?", "hello world", "%s, string", ...</td>
|
||||
</tr>
|
||||
</table>
|
||||
</center>
|
||||
|
||||
<p>
|
||||
We will use a instruction notation as follows to show how to use each opcode.
|
||||
</p>
|
||||
|
||||
<center>
|
||||
<table id='solid'>
|
||||
<tr>
|
||||
<td><strong>Notation</strong></td>
|
||||
<td><strong>Meaning</strong></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>R(A), R(B), R(C)</td>
|
||||
<td>Register specified in field A, B or C.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>PC</td>
|
||||
<td>Program Counter.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Kst(k)</td>
|
||||
<td>A constant <b>k</b> that will be translated to a number.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Upvalue(name)</td>
|
||||
<td>An upvalue <b>name</b> that will be translated to a number.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>RK(B), RK(C)</td>
|
||||
<td>A field that could be a register or a constant.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>sBx</td>
|
||||
<td>Signal displacement for all kinds of jumps. It can be a number or a label.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>KPROTO(name)</td>
|
||||
<td>A function <b>name</b> that will be used to create its closure.</td>
|
||||
</tr>
|
||||
</table>
|
||||
</center>
|
||||
|
||||
<p>
|
||||
Below we show each opcode that is available in our assembly language as well as their respective arguments.
|
||||
</p>
|
||||
|
||||
<a name="op-com"><h3 style="color:#00008B;">MOVE R(A), R(B)</h3></a>
|
||||
R(A) := R(B)
|
||||
<p>
|
||||
Copies the value of register R(B) into register R(A).
|
||||
</p>
|
||||
<a name="op-com"><h3 style="color:#00008B;">LOADK R(A), Kst(Bx)</h3></a>
|
||||
R(A) := Kst(Bx)
|
||||
<p>
|
||||
Loads constant Kst(Bx) into register R(A). Constants can be numbers or strings.
|
||||
</p>
|
||||
<a name="op-com"><h3 style="color:#00008B;">LOADKX R(A)</h3></a>
|
||||
R(A) := Kst(extra arg)
|
||||
<p>
|
||||
Loads extra arg into register R(A).
|
||||
The next instruction is always EXTRAARG.
|
||||
</p>
|
||||
<a name="op-com"><h3 style="color:#00008B;">LOADBOOL R(A), B, C</h3></a>
|
||||
R(A) := (Bool)B; if (C) pc++
|
||||
<p>
|
||||
Loads a boolean value B (<b>1</b> for <b>TRUE</b> or <b>0</b> for <b>FALSE</b> should be used as B) into register R(A).
|
||||
If C is not zero then next instruction is skipped.
|
||||
</p>
|
||||
<a name="op-com"><h3 style="color:#00008B;">LOADNIL R(A), B</h3></a>
|
||||
R(A), R(A+1), ..., R(A+B) := nil
|
||||
<p>
|
||||
Sets to <b>nil</b> a range of registers from register R(A) up to B.
|
||||
</p>
|
||||
<a name="op-com"><h3 style="color:#00008B;">GETUPVAL R(A), UpValue(B)</h3></a>
|
||||
R(A) := UpValue[B]
|
||||
<p>
|
||||
Copies the value in UpValue[B] into register R(A).
|
||||
</p>
|
||||
<a name="op-com"><h3 style="color:#00008B;">GETTABUP R(A), UpValue(B), RK(C)</h3></a>
|
||||
R(A) := UpValue[B][RK(C)]
|
||||
<p>
|
||||
Copies the value of field RK(C), from table UpValue[B], into register R(A).
|
||||
Note that if you are getting globals, then UpValue(B) should be <b>_ENV</b>.
|
||||
</p>
|
||||
<a name="op-com"><h3 style="color:#00008B;">GETTABLE R(A), R(B), RK(C)</h3></a>
|
||||
R(A) := R(B)[RK(C)]
|
||||
<p>
|
||||
Copies the value from a table element into register R(A).
|
||||
The table is referenced by register R(B), while the index to the table is given by
|
||||
RK(C), which may be register R(C) or a constant Kst(C).
|
||||
</p>
|
||||
<a name="op-com"><h3 style="color:#00008B;">SETTABUP UpValue(A), RK(B), RK(C)</h3></a>
|
||||
UpValue[A][RK(B)] := RK(C)
|
||||
<p>
|
||||
Copies the value of RK(C) to the field RK(B) of table UpValue[A].
|
||||
Note that if you are setting globals, then UpValue(A) should be <b>_ENV</b>.
|
||||
</p>
|
||||
<a name="op-com"><h3 style="color:#00008B;">SETUPVAL R(A), UpValue(B)</h3></a>
|
||||
UpValue[B] := R(A)
|
||||
<p>
|
||||
Copies the value from register R(A) into UpValue[B].
|
||||
</p>
|
||||
<a name="op-com"><h3 style="color:#00008B;">SETTABLE R(A), RK(B), RK(C)</h3></a>
|
||||
R(A)[RK(B)] := RK(C)
|
||||
<p>
|
||||
Copies the value from register R(C) or constant Kst(C) into a table element.
|
||||
The table is referenced by register R(A), while the index to the table is given by
|
||||
RK(B), which may be register R(B) or a constant Kst(B).
|
||||
</p>
|
||||
<a name="op-com"><h3 style="color:#00008B;">NEWTABLE R(A), B, C</h3></a>
|
||||
R(A) := {} (size = B,C)
|
||||
<p>
|
||||
Creates a new empty table at register R(A).
|
||||
Argument B is the size of the array part, while C is the size of the hash part.
|
||||
</p>
|
||||
<a name="op-com"><h3 style="color:#00008B;">SELF R(A), R(B), RK(C)</h3></a>
|
||||
R(A+1) := R(B); R(A) := R(B)[RK(C)]
|
||||
<p>
|
||||
It is used for object-oriented programming using tables.
|
||||
Retrieves a function reference from a table element and places it in register R(A),
|
||||
then a reference to the table itself is placed in the next register R(A+1).
|
||||
R(B) is the register holding the reference to the table with the method,
|
||||
while the method function is found using the table index RK(C), that can be
|
||||
a register R(C) or a constant Kst(C).
|
||||
</p>
|
||||
<a name="op-com"><h3 style="color:#00008B;">ADD R(A), RK(B), RK(C)</h3></a>
|
||||
R(A) := RK(B) + RK(C)
|
||||
<p>
|
||||
Adds RK(B) and RK(C) and holds the result into register R(A).
|
||||
Both RK(B) and RK(C) may be either registers or constants.
|
||||
</p>
|
||||
<a name="op-com"><h3 style="color:#00008B;">SUB R(A), RK(B), RK(C)</h3></a>
|
||||
R(A) := RK(B) - RK(C)
|
||||
<p>
|
||||
Subtracts RK(B) and RK(C) and holds the result into register R(A).
|
||||
Both RK(B) and RK(C) may be either registers or constants.
|
||||
</p>
|
||||
<a name="op-com"><h3 style="color:#00008B;">MUL R(A), RK(B), RK(C)</h3></a>
|
||||
R(A) := RK(B) * RK(C)
|
||||
<p>
|
||||
Multiplies RK(B) and RK(C) and holds the result into register R(A).
|
||||
Both RK(B) and RK(C) may be either registers or constants.
|
||||
</p>
|
||||
<a name="op-com"><h3 style="color:#00008B;">DIV R(A), RK(B), RK(C)</h3></a>
|
||||
R(A) := RK(B) / RK(C)
|
||||
<p>
|
||||
Divides RK(B) and RK(C) and holds the result into register R(A).
|
||||
Both RK(B) and RK(C) may be either registers or constants.
|
||||
</p>
|
||||
<a name="op-com"><h3 style="color:#00008B;">MOD R(A), RK(B), RK(C)</h3></a>
|
||||
R(A) := RK(B) % RK(C)
|
||||
<p>
|
||||
Performs modulus between RK(B) and RK(C) and holds the result into register R(A).
|
||||
Both RK(B) and RK(C) may be either registers or constants.
|
||||
</p>
|
||||
<a name="op-com"><h3 style="color:#00008B;">POW R(A), RK(B), RK(C)</h3></a>
|
||||
R(A) := RK(B) ^ RK(C)
|
||||
<p>
|
||||
Performs exponentiation between RK(B) and RK(C) and holds the result into register R(A).
|
||||
Both RK(B) and RK(C) may be either registers or constants.
|
||||
</p>
|
||||
<a name="op-com"><h3 style="color:#00008B;">UNM R(A), R(B)</h3></a>
|
||||
R(A) := -R(B)
|
||||
<p>
|
||||
Performs unary minus where register R(B) is negated and the value is placed in register R(A).
|
||||
</p>
|
||||
<a name="op-com"><h3 style="color:#00008B;">NOT R(A), R(B)</h3></a>
|
||||
R(A) := not R(B)
|
||||
<p>
|
||||
Applies a boolean <b>not</b> to the value in register R(B) and holds the result in register R(A).
|
||||
</p>
|
||||
<a name="op-com"><h3 style="color:#00008B;">LEN R(A), R(B)</h3></a>
|
||||
R(A) := length of R(B)
|
||||
<p>
|
||||
Returns the length of object in register R(B) and holds the result in register R(A).
|
||||
</p>
|
||||
<a name="op-com"><h3 style="color:#00008B;">CONCAT R(A), R(B), R(C)</h3></a>
|
||||
R(A) := R(B).. ... ..R(C)
|
||||
<p>
|
||||
Performs the concatenation among two or more strings. The start register is R(B) and the
|
||||
final register is R(C), meaning that R(C) should always be greater then R(B).
|
||||
The result is stored in register R(A).
|
||||
</p>
|
||||
<a name="op-com"><h3 style="color:#00008B;">JMP A, sBx</h3></a>
|
||||
pc+=sBx; if (A) close all upvalues >= R(A) + 1
|
||||
<p>
|
||||
Performs a jump to sBx, which should be an instruction number or a label that should jump to.
|
||||
If A is <b>true</b> then all upvalues up to R(A) + 1 are closed.
|
||||
</p>
|
||||
<a name="op-com"><h3 style="color:#00008B;">EQ A, RK(B), RK(C)</h3></a>
|
||||
if ((RK(B) == RK(C)) ~= A) then pc++
|
||||
<p>
|
||||
Performs a <b>equality</b> test between RK(B) and RK(C), wich may be registers or constants.
|
||||
If the boolean is not A then next instruction is skipped.
|
||||
</p>
|
||||
<a name="op-com"><h3 style="color:#00008B;">LT A, RK(B), RK(C)</h3></a>
|
||||
if ((RK(B) < RK(C)) ~= A) then pc++
|
||||
<p>
|
||||
Performs a <b>less than</b> test between RK(B) and RK(C), wich may be registers or constants.
|
||||
If the boolean is not A then next instruction is skipped.
|
||||
</p>
|
||||
<a name="op-com"><h3 style="color:#00008B;">LE A, RK(B), RK(C)</h3></a>
|
||||
if ((RK(B) <= RK(C)) ~= A) then pc++
|
||||
<p>
|
||||
Performs a <b>less than or equal to</b> test between RK(B) and RK(C), wich may be registers or constants.
|
||||
If the boolean is not A then next instruction is skipped.
|
||||
</p>
|
||||
<a name="op-com"><h3 style="color:#00008B;">TEST R(A), C</h3></a>
|
||||
if not (R(A) <=> C) then pc++
|
||||
<p>
|
||||
Can be used to implement <b>and</b>/<b>or</b> logical operators, or for testing
|
||||
a single register in a conditional statement.
|
||||
TEST should be used when an assignment operation is not needed and works same way as TESTSET.
|
||||
For more details, please, look at TESTSET.
|
||||
</p>
|
||||
<a name="op-com"><h3 style="color:#00008B;">TESTSET R(A), R(B), C</h3></a>
|
||||
if (R(B) <=> C) then R(A) := R(B) else pc++
|
||||
<p>
|
||||
Also can be used to implement <b>and</b>/<b>or</b> logical operators, or for testing
|
||||
a single register in a conditional statement.
|
||||
Register R(B) is coerced into a boolean and compared to the boolean field C.
|
||||
If R(B) matches C then next instruction is skipped, otherwise R(B) is assigned to R(A).
|
||||
</p>
|
||||
<a name="op-com"><h3 style="color:#00008B;">CALL R(A), B, C</h3></a>
|
||||
R(A), ... ,R(A+C-2) := R(A)(R(A+1), ... ,R(A+B-1))
|
||||
<p>
|
||||
Performs a function call. R(A) holds the reference to the function object to be called.
|
||||
Parameters to the function should be placed in the registers following R(A).
|
||||
<br><br>
|
||||
If B is 1 the function has no parameters. If B is 2 or more there are B-1 parameters.
|
||||
If B is 0 the function parameters range from R(A+1) to the top of the stack.
|
||||
<br><br>
|
||||
Results returned by the function call are placed in a range of registers starting from R(A).
|
||||
If C is 1 no return results. If C is 2 or more there will be C-1 results saved.
|
||||
If C is 0 then multiple return results are saved.
|
||||
</p>
|
||||
<a name="op-com"><h3 style="color:#00008B;">TAILCALL R(A), B, C</h3></a>
|
||||
return R(A)(R(A+1), ... ,R(A+B-1))
|
||||
<p>
|
||||
Performs a tail call which happens when a <b>return</b> statement has a single function call as the expression.
|
||||
Exactly like CALL, register R(A) is the reference to the function object to be called,
|
||||
while B encodes the number of paramenters. However, even tough C is not used by TAILCALL, 0 should be
|
||||
used to denote multiple return results.
|
||||
</p>
|
||||
<a name="op-com"><h3 style="color:#00008B;">RETURN R(A), B</h3></a>
|
||||
return R(A), ... ,R(A+B-2)
|
||||
<p>
|
||||
Returns to the calling function with options return values.
|
||||
<br><br>
|
||||
If B is 1 there are no return values. If B is 2 or more, there are B-1 return values.
|
||||
If B is 0 the set of values from R(A) to the top of the stack is returned.
|
||||
</p>
|
||||
<a name="op-com"><h3 style="color:#00008B;">FORLOOP R(A) sBx</h3></a>
|
||||
R(A)+=R(A+2) ; if R(A) <?= R(A+1) then pc+=sBx; R(A+3)=R(A)
|
||||
<p>
|
||||
Should be used to perform an iteration of a numeric <b>for</b> loop.
|
||||
A numeric for loop requires 4 registers on the stack where R(A) hold the initial value,
|
||||
R(A+1) is the limit, R(A+2) is the stepping value and R(A+3) is the actual loop variable
|
||||
that is local to the <b>for</b> block.
|
||||
The argument sBx should be an instruction number or a label that should jump unconditionally to.
|
||||
</p>
|
||||
<a name="op-com"><h3 style="color:#00008B;">FORPREP R(A), sBx</h3></a>
|
||||
R(A)-=R(A+2); pc+=sBx
|
||||
<p>
|
||||
Should be used to initialize a numeric <b>for</b> loop.
|
||||
The argument sBx should be an instruction number or a label that should jump back to the loop body.
|
||||
</p>
|
||||
<a name="op-com"><h3 style="color:#00008B;">TFORCALL R(A), C</h3></a>
|
||||
R(A+3), ... ,R(A+2+C) := R(A)(R(A+1), R(A+2))
|
||||
<p>
|
||||
Should be used to initialize a generic <b>for</b> loop, where
|
||||
R(A) is the iterator function, R(A+1) is the state and R(A+2) is the enumeration index.
|
||||
The loop variables are specified at locations R(A+3) and their count is defined by operand C,
|
||||
which should be at least 1.
|
||||
</p>
|
||||
<a name="op-com"><h3 style="color:#00008B;">TFORLOOP R(A), sBx</h3></a>
|
||||
if R(A+1) ~= nil then { R(A)=R(A+1); pc += sBx }
|
||||
<p>
|
||||
Should be used to perform an iteration of a generic <b>for</b> loop.
|
||||
If register R(A+1) is not nil then it is stored in R(A) and jump back to sBx.
|
||||
The argument sBx should be an instruction number or a label that should jump to.
|
||||
</p>
|
||||
<a name="op-com"><h3 style="color:#00008B;">SETLIST R(A), B, C</h3></a>
|
||||
R(A)[(C-1)*FPF+i] := R(A+i), 1 <= i <= B
|
||||
<p>
|
||||
Sets the values for a range of arrays elements in a table referenced by R(A),
|
||||
argument B is the number of elements to set and argument C is the number of blocks
|
||||
to be initialized.
|
||||
If B is zero then B is the top. If C is zero then next instruction should be
|
||||
EXTRAARG(real C).
|
||||
</p>
|
||||
<a name="op-com"><h3 style="color:#00008B;">CLOSURE R(A), KPROTO(Bx)</h3></a>
|
||||
R(A) := closure(KPROTO[Bx])
|
||||
<p>
|
||||
Should be used to create an instance of a closure of a function where KPROTO[Bx] is the
|
||||
function name and R(A) is the register that assigns the reference to the instantiated
|
||||
function object.
|
||||
</p>
|
||||
<a name="op-com"><h3 style="color:#00008B;">VARARG R(A), B</h3></a>
|
||||
R(A), R(A+1), ..., R(A+B-2) = vararg
|
||||
<p>
|
||||
Copies B-2 parameters into a number of registers starting from R(A).
|
||||
If B is 0, VARARG copies as many values as it can based on the number of parameters passed.
|
||||
If a fixed number of values is required, B is a value greater than 1.
|
||||
If any number of values is required then B is 0.
|
||||
</p>
|
||||
<a name="op-com"><h3 style="color:#00008B;">EXTRAARG Ax</h3></a>
|
||||
<p>
|
||||
Sets an extra larger argument for previous opcode.
|
||||
If previous opcode is <b>LOADKX</b> then Ax might be any constant, but
|
||||
if previous opcode is <b>SETLIST</b> then Ax must be an integer.
|
||||
</p>
|
||||
|
||||
<hr />
|
||||
|
||||
<h3><a name="notes">Notes</a></h3>
|
||||
<p>
|
||||
The current version of <code>Lua Assembler/Disassembler</code> is 0.2.
|
||||
</p>
|
||||
<hr />
|
||||
|
||||
<h3><a name="licence">Licence</a></h3>
|
||||
<p>
|
||||
Copyright © 2012 Andre Murbach Maidl.
|
||||
</p>
|
||||
<p>
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to
|
||||
deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
||||
sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
|
||||
</p>
|
||||
<p>
|
||||
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
|
||||
</p>
|
||||
<p>
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
||||
</p>
|
||||
|
||||
</div>
|
||||
<div id="footer">
|
||||
<center>
|
||||
<small>Last modified by Andre Murbach Maidl</small>
|
||||
</center>
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
+230
@@ -0,0 +1,230 @@
|
||||
local OPCODE = { "MOVE", "LOADK", "LOADKX", "LOADBOOL", "LOADNIL",
|
||||
"GETUPVAL", "GETTABUP", "GETTABLE", "SETTABUP",
|
||||
"SETUPVAL", "SETTABLE", "NEWTABLE", "SELF",
|
||||
"ADD", "SUB", "MUL", "DIV", "MOD", "POW",
|
||||
"UNM", "NOT", "LEN", "CONCAT", "JMP",
|
||||
"EQ", "LT", "LE", "TEST", "TESTSET",
|
||||
"CALL", "TAILCALL", "RETURN", "FORLOOP",
|
||||
"FORPREP", "TFORCALL", "TFORLOOP", "SETLIST",
|
||||
"CLOSURE", "VARARG", "EXTRAARG" }
|
||||
|
||||
local LUA_ENV = "_ENV"
|
||||
local LUA_SOURCE = "@Lua Assembler/Disassembler"
|
||||
|
||||
local LUA_SIGNATURE = "\27Lua"
|
||||
local LUAC_TAIL = "\x19\x93\r\n\x1a\n"
|
||||
local LUAC_HEADERSIZE = string.len(LUA_SIGNATURE) + 2 + 6 + string.len(LUAC_TAIL)
|
||||
local LUA_VERSION = 82
|
||||
local LUA_FORMAT = 0
|
||||
|
||||
local ENDIANNESS = 1
|
||||
local INT = 4
|
||||
local SIZE_T = 8
|
||||
local INSTRUCTION = 4
|
||||
local LUA_NUMBER = 8
|
||||
local INTEGRAL = 0
|
||||
|
||||
local LUA_TNIL = 0
|
||||
local LUA_TBOOLEAN = 1
|
||||
local LUA_TNUMBER = 3
|
||||
local LUA_TSTRING = 4
|
||||
|
||||
local iABC = 0
|
||||
local iABx = 1
|
||||
local iAsBx = 2
|
||||
local iAx = 3
|
||||
|
||||
local SIZE_C = 9
|
||||
local SIZE_B = 9
|
||||
local SIZE_Bx = (SIZE_C + SIZE_B)
|
||||
local SIZE_A = 8
|
||||
local SIZE_Ax = (SIZE_C + SIZE_B + SIZE_A)
|
||||
|
||||
local SIZE_OP = 6
|
||||
|
||||
local POS_OP = 0
|
||||
local POS_A = (POS_OP + SIZE_OP)
|
||||
local POS_C = (POS_A + SIZE_A)
|
||||
local POS_B = (POS_C + SIZE_C)
|
||||
local POS_Bx = POS_C
|
||||
local POS_Ax = POS_A
|
||||
|
||||
local MASK_OP = math.ldexp(1, SIZE_OP)
|
||||
local MASK_A = math.ldexp(1, SIZE_A)
|
||||
local MASK_B = math.ldexp(1, SIZE_B)
|
||||
local MASK_C = math.ldexp(1, SIZE_C)
|
||||
local MASK_Bx = math.ldexp(1, SIZE_Bx)
|
||||
local MASK_Ax = math.ldexp(1, SIZE_Ax)
|
||||
|
||||
local ABC = { SIZE_OP, SIZE_A, SIZE_C, SIZE_B }
|
||||
|
||||
local INT_MAX = (2 ^ ((SIZE_T * INT) - 1)) - 1
|
||||
local LUAI_BITSINT
|
||||
if INT_MAX-20 < 32760 then
|
||||
LUAI_BITSINT = 16
|
||||
elseif INT_MAX > 2147483640 then
|
||||
LUAI_BITSINT = 32
|
||||
else
|
||||
error ("you must define LUAI_BITSINT with number of bits in an integer")
|
||||
end
|
||||
|
||||
local MAX_INT = INT_MAX - 2
|
||||
|
||||
local MAXARG_Bx, MAXARG_sBx, MAXARG_Ax
|
||||
|
||||
if SIZE_Bx < LUAI_BITSINT-1 then
|
||||
MAXARG_Bx = bit32.lshift(1,SIZE_Bx)-1
|
||||
MAXARG_sBx = bit32.rshift(MAXARG_Bx,1)
|
||||
else
|
||||
MAXARG_Bx = MAX_INT
|
||||
MAXARG_sBx = MAX_INT
|
||||
end
|
||||
|
||||
if SIZE_Ax < LUAI_BITSINT-1 then
|
||||
MAXARG_Ax = bit32.lshift(1,SIZE_Ax)-1
|
||||
else
|
||||
MAXARG_Ax = MAX_INT
|
||||
end
|
||||
|
||||
local MAXARG_A = bit32.lshift(1,SIZE_A)-1
|
||||
local MAXARG_B = bit32.lshift(1,SIZE_B)-1
|
||||
local MAXARG_C = bit32.lshift(1,SIZE_C)-1
|
||||
|
||||
local BITRK = bit32.lshift(1, (SIZE_B - 1))
|
||||
|
||||
local function ISK(x)
|
||||
return bit32.btest(bit32.band(x, BITRK))
|
||||
end
|
||||
|
||||
local function INDEXK(r)
|
||||
return bit32.band(r, bit32.bnot(BITRK))
|
||||
end
|
||||
|
||||
local MAXINDEXRK = BITRK - 1
|
||||
|
||||
local function RKASK(x)
|
||||
return bit32.bor(x, BITRK)
|
||||
end
|
||||
|
||||
local function get_bit(x,a,b)
|
||||
return (math.floor((x / 2^a) % 2^b))
|
||||
end
|
||||
|
||||
local OpArgN = 0
|
||||
local OpArgU = 1
|
||||
local OpArgR = 2
|
||||
local OpArgK = 3
|
||||
|
||||
local opmodes = {
|
||||
{ T = 0, A = 1, B = OpArgR, C = OpArgN, mode = iABC }, -- MOVE
|
||||
{ T = 0, A = 1, B = OpArgK, C = OpArgN, mode = iABx }, -- OP_LOADK
|
||||
{ T = 0, A = 1, B = OpArgN, C = OpArgN, mode = iABx }, -- OP_LOADKX
|
||||
{ T = 0, A = 1, B = OpArgU, C = OpArgU, mode = iABC }, -- OP_LOADBOOL
|
||||
{ T = 0, A = 1, B = OpArgU, C = OpArgN, mode = iABC }, -- OP_LOADNIL
|
||||
{ T = 0, A = 1, B = OpArgU, C = OpArgN, mode = iABC }, -- OP_GETUPVAL
|
||||
{ T = 0, A = 1, B = OpArgU, C = OpArgK, mode = iABC }, -- OP_GETTABUP
|
||||
{ T = 0, A = 1, B = OpArgR, C = OpArgK, mode = iABC }, -- OP_GETTABLE
|
||||
{ T = 0, A = 0, B = OpArgK, C = OpArgK, mode = iABC }, -- OP_SETTABUP
|
||||
{ T = 0, A = 0, B = OpArgU, C = OpArgN, mode = iABC }, -- OP_SETUPVAL
|
||||
{ T = 0, A = 0, B = OpArgK, C = OpArgK, mode = iABC }, -- OP_SETTABLE
|
||||
{ T = 0, A = 1, B = OpArgU, C = OpArgU, mode = iABC }, -- OP_NEWTABLE
|
||||
{ T = 0, A = 1, B = OpArgR, C = OpArgK, mode = iABC }, -- OP_SELF
|
||||
{ T = 0, A = 1, B = OpArgK, C = OpArgK, mode = iABC }, -- OP_ADD
|
||||
{ T = 0, A = 1, B = OpArgK, C = OpArgK, mode = iABC }, -- OP_SUB
|
||||
{ T = 0, A = 1, B = OpArgK, C = OpArgK, mode = iABC }, -- OP_MUL
|
||||
{ T = 0, A = 1, B = OpArgK, C = OpArgK, mode = iABC }, -- OP_DIV
|
||||
{ T = 0, A = 1, B = OpArgK, C = OpArgK, mode = iABC }, -- OP_MOD
|
||||
{ T = 0, A = 1, B = OpArgK, C = OpArgK, mode = iABC }, -- OP_POW
|
||||
{ T = 0, A = 1, B = OpArgR, C = OpArgN, mode = iABC }, -- OP_UNM
|
||||
{ T = 0, A = 1, B = OpArgR, C = OpArgN, mode = iABC }, -- OP_NOT
|
||||
{ T = 0, A = 1, B = OpArgR, C = OpArgN, mode = iABC }, -- OP_LEN
|
||||
{ T = 0, A = 1, B = OpArgR, C = OpArgR, mode = iABC }, -- OP_CONCAT
|
||||
{ T = 0, A = 0, B = OpArgR, C = OpArgN, mode = iAsBx }, -- OP_JMP
|
||||
{ T = 1, A = 0, B = OpArgK, C = OpArgK, mode = iABC }, -- OP_EQ
|
||||
{ T = 1, A = 0, B = OpArgK, C = OpArgK, mode = iABC }, -- OP_LT
|
||||
{ T = 1, A = 0, B = OpArgK, C = OpArgK, mode = iABC }, -- OP_LE
|
||||
{ T = 1, A = 0, B = OpArgN, C = OpArgU, mode = iABC }, -- OP_TEST
|
||||
{ T = 1, A = 1, B = OpArgR, C = OpArgU, mode = iABC }, -- OP_TESTSET
|
||||
{ T = 0, A = 1, B = OpArgU, C = OpArgU, mode = iABC }, -- OP_CALL
|
||||
{ T = 0, A = 1, B = OpArgU, C = OpArgU, mode = iABC }, -- OP_TAILCALL
|
||||
{ T = 0, A = 0, B = OpArgU, C = OpArgN, mode = iABC }, -- OP_RETURN
|
||||
{ T = 0, A = 1, B = OpArgR, C = OpArgN, mode = iAsBx }, -- OP_FORLOOP
|
||||
{ T = 0, A = 1, B = OpArgR, C = OpArgN, mode = iAsBx }, -- OP_FORPREP
|
||||
{ T = 0, A = 0, B = OpArgN, C = OpArgU, mode = iABC }, -- OP_TFORCALL
|
||||
{ T = 0, A = 1, B = OpArgR, C = OpArgN, mode = iAsBx }, -- OP_TFORLOOP
|
||||
{ T = 0, A = 0, B = OpArgU, C = OpArgU, mode = iABC }, -- OP_SETLIST
|
||||
{ T = 0, A = 1, B = OpArgU, C = OpArgN, mode = iABx }, -- OP_CLOSURE
|
||||
{ T = 0, A = 1, B = OpArgU, C = OpArgN, mode = iABC }, -- OP_VARARG
|
||||
{ T = 0, A = 0, B = OpArgU, C = OpArgU, mode = iAx }, -- OP_EXTRAARG
|
||||
}
|
||||
|
||||
local function get_op_mode(o)
|
||||
return opmodes[o].mode
|
||||
end
|
||||
|
||||
local ladconf = {
|
||||
OPCODE = OPCODE,
|
||||
LUA_ENV = LUA_ENV,
|
||||
LUA_SOURCE = LUA_SOURCE,
|
||||
LUA_SIGNATURE = LUA_SIGNATURE,
|
||||
LUAC_TAIL = LUAC_TAIL,
|
||||
LUAC_HEADERSIZE = LUAC_HEADERSIZE,
|
||||
LUA_VERSION = LUA_VERSION,
|
||||
LUA_FORMAT = LUA_FORMAT,
|
||||
ENDIANNESS = ENDIANNESS,
|
||||
INT = INT,
|
||||
SIZE_T = SIZE_T,
|
||||
INSTRUCTION = INSTRUCTION,
|
||||
LUA_NUMBER = LUA_NUMBER,
|
||||
INTEGRAL = INTEGRAL,
|
||||
LUA_TNIL = LUA_TNIL,
|
||||
LUA_TBOOLEAN = LUA_TBOOLEAN,
|
||||
LUA_TNUMBER = LUA_TNUMBER,
|
||||
LUA_TSTRING = LUA_TSTRING,
|
||||
iABC = iABC,
|
||||
iABx = iABx,
|
||||
iAsBx = iAsBx,
|
||||
iAx = iAx,
|
||||
SIZE_C = SIZE_C,
|
||||
SIZE_B = SIZE_B,
|
||||
SIZE_Bx = SIZE_Bx,
|
||||
SIZE_A = SIZE_A,
|
||||
SIZE_Ax = SIZE_Ax,
|
||||
SIZE_OP = SIZE_OP,
|
||||
POS_OP = POS_OP,
|
||||
POS_A = POS_A,
|
||||
POS_C = POS_C,
|
||||
POS_B = POS_B,
|
||||
POS_Bx = POS_Bx,
|
||||
POS_Ax = POS_Ax,
|
||||
MASK_OP = MASK_OP,
|
||||
MASK_A = MASK_A,
|
||||
MASK_B = MASK_B,
|
||||
MASK_C = MASK_C,
|
||||
MASK_Bx = MASK_Bx,
|
||||
MASK_Ax = MASK_Ax,
|
||||
ABC = ABC,
|
||||
INT_MAX = INT_MAX,
|
||||
LUAI_BITSINT = LUAI_BITSINT,
|
||||
MAX_INT = MAX_INT,
|
||||
MAXARG_Bx = MAXARG_Bx,
|
||||
MAXARG_sBx = MAXARG_sBx,
|
||||
MAXARG_Ax = MAXARG_Ax,
|
||||
MAXARG_A = MAXARG_A,
|
||||
MAXARG_B = MAXARG_B,
|
||||
MAXARG_C = MAXARG_C,
|
||||
BITRK = BITRK,
|
||||
ISK = ISK,
|
||||
INDEXK = INDEXK,
|
||||
MAXINDEXRK = MAXINDEXRK,
|
||||
RKASK = RKASK,
|
||||
get_bit = get_bit,
|
||||
OpArgN = OpArgN,
|
||||
OpArgU = OpArgU,
|
||||
OpArgR = OpArgR,
|
||||
OpArgK = OpArgK,
|
||||
opmodes = opmodes,
|
||||
get_op_mode = get_op_mode,
|
||||
}
|
||||
|
||||
return ladconf
|
||||
Executable
+72
@@ -0,0 +1,72 @@
|
||||
#!/usr/bin/env lua
|
||||
|
||||
--[[
|
||||
Lua Assembler for Lua 5.2.0
|
||||
Version 0.2
|
||||
Author: Andre Murbach Maidl
|
||||
]]
|
||||
|
||||
INPUT = "luad.asm"
|
||||
OUTPUT = "luaa.out"
|
||||
DEBUG = false
|
||||
|
||||
USAGE = [[
|
||||
usage: %s [options] [filename]
|
||||
Available options are:
|
||||
-h print this help
|
||||
-o name output to file name (default is %s)
|
||||
]]
|
||||
|
||||
function usage(msg)
|
||||
if msg ~= nil then
|
||||
io.write(string.format("%s: %s\n", arg[0], msg))
|
||||
end
|
||||
io.write(string.format(USAGE, arg[0], OUTPUT))
|
||||
os.exit(1)
|
||||
end
|
||||
|
||||
function doargs()
|
||||
local i = 1
|
||||
while i <= #arg do
|
||||
if string.find(arg[i], "^-") == nil then
|
||||
INPUT = arg[i]
|
||||
break
|
||||
elseif arg[i] == "-h" then usage()
|
||||
elseif arg[i] == "-o" then i = i + 1
|
||||
if arg[i] == nil then usage("'-o' needs argument") else OUTPUT = arg[i] end
|
||||
else usage(string.format("'%s' unkown option", arg[i]))
|
||||
end
|
||||
i = i + 1
|
||||
end
|
||||
end
|
||||
|
||||
function read_input(filename)
|
||||
local input = assert(io.open(filename, "r"))
|
||||
local contents = input:read("*a")
|
||||
input:close()
|
||||
return contents
|
||||
end
|
||||
|
||||
if #arg < 1 then
|
||||
usage ("no input file given")
|
||||
end
|
||||
|
||||
doargs()
|
||||
|
||||
local assembler = require("assembler")
|
||||
local contents = read_input(INPUT)
|
||||
local ast = assembler.parse(contents)
|
||||
|
||||
if not ast then
|
||||
print ("syntax error")
|
||||
os.exit (1)
|
||||
end
|
||||
|
||||
if DEBUG then assembler.print_ast(ast) end
|
||||
local parsed = assembler.traverse(ast)
|
||||
if parsed == nil then
|
||||
error ("could not generate bytecode")
|
||||
end
|
||||
assembler.write(OUTPUT, parsed)
|
||||
|
||||
os.exit(0)
|
||||
Executable
+62
@@ -0,0 +1,62 @@
|
||||
#!/usr/bin/env lua
|
||||
|
||||
--[[
|
||||
Lua Disassembler for Lua 5.2.0
|
||||
Version 0.2
|
||||
Author: Andre Murbach Maidl
|
||||
]]
|
||||
|
||||
INPUT = "luac.out"
|
||||
OUTPUT = "luad.asm"
|
||||
LISTING = 0
|
||||
DUMPING = true
|
||||
|
||||
USAGE = [[
|
||||
usage: %s [options] [filename]
|
||||
Available options are:
|
||||
-h print this help
|
||||
-l list on luac style (use -l -l for full listing)
|
||||
-o name output to file name (default is %s)
|
||||
-p parse only
|
||||
]]
|
||||
|
||||
function usage(msg)
|
||||
if msg ~= nil then
|
||||
io.write(string.format("%s: %s\n", arg[0], msg))
|
||||
end
|
||||
io.write(string.format(USAGE, arg[0], OUTPUT))
|
||||
os.exit(1)
|
||||
end
|
||||
|
||||
function doargs()
|
||||
local i = 1
|
||||
while i <= #arg do
|
||||
if string.find(arg[i], "^-") == nil then
|
||||
INPUT = arg[i]
|
||||
break
|
||||
elseif arg[i] == "-h" then usage()
|
||||
elseif arg[i] == "-l" then LISTING = LISTING + 1
|
||||
elseif arg[i] == "-o" then i = i + 1
|
||||
if arg[i] == nil then usage("'-o' needs argument") else OUTPUT = arg[i] end
|
||||
elseif arg[i] == "-p" then DUMPING = false
|
||||
else usage(string.format("'%s' unkown option", arg[i]))
|
||||
end
|
||||
i = i + 1
|
||||
end
|
||||
end
|
||||
|
||||
if #arg < 1 then
|
||||
usage ("no input file given")
|
||||
end
|
||||
|
||||
doargs()
|
||||
|
||||
local chunk = assert(loadfile(INPUT))
|
||||
local bytecode = string.dump(chunk)
|
||||
local disassembler = require("disassembler")
|
||||
|
||||
local parsed = disassembler.parse(bytecode)
|
||||
if LISTING > 0 then disassembler.print_function(parsed, LISTING > 1) end
|
||||
if DUMPING then disassembler.write(OUTPUT, parsed) end
|
||||
|
||||
os.exit(0)
|
||||
Reference in New Issue
Block a user