1
0
forked from GitHub/lad

Uploading Lua Assembler/Disassembler for Lua 5.2

This commit is contained in:
Andre Murbach Maidl
2013-08-04 10:41:28 -03:00
parent 6c9a9f0cd6
commit 5d2eafb879
13 changed files with 2735 additions and 0 deletions
+6
View File
@@ -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
+5
View File
@@ -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.
+22
View File
@@ -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.
+685
View File
@@ -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
+754
View File
@@ -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
+23
View File
@@ -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
+6
View File
@@ -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
+1
View File
@@ -0,0 +1 @@
print ("hello world!")
BIN
View File
Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

+869
View File
@@ -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) <&#63= 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 &copy; 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
View File
@@ -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
View File
@@ -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
View File
@@ -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)