diff --git a/5.2/ChangeLog b/5.2/ChangeLog
new file mode 100644
index 0000000..577c7f5
--- /dev/null
+++ b/5.2/ChangeLog
@@ -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
diff --git a/5.2/README b/5.2/README
new file mode 100644
index 0000000..5f0add8
--- /dev/null
+++ b/5.2/README
@@ -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.
diff --git a/5.2/ToDo b/5.2/ToDo
new file mode 100644
index 0000000..9543e01
--- /dev/null
+++ b/5.2/ToDo
@@ -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.
+
diff --git a/5.2/assembler.lua b/5.2/assembler.lua
new file mode 100644
index 0000000..14232ea
--- /dev/null
+++ b/5.2/assembler.lua
@@ -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
diff --git a/5.2/disassembler.lua b/5.2/disassembler.lua
new file mode 100644
index 0000000..f3a19f3
--- /dev/null
+++ b/5.2/disassembler.lua
@@ -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
diff --git a/5.2/doc/fat_rec.asm b/5.2/doc/fat_rec.asm
new file mode 100644
index 0000000..ce93373
--- /dev/null
+++ b/5.2/doc/fat_rec.asm
@@ -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
+
diff --git a/5.2/doc/hello.asm b/5.2/doc/hello.asm
new file mode 100644
index 0000000..57c046c
--- /dev/null
+++ b/5.2/doc/hello.asm
@@ -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
diff --git a/5.2/doc/hello.lua b/5.2/doc/hello.lua
new file mode 100644
index 0000000..a50a5fc
--- /dev/null
+++ b/5.2/doc/hello.lua
@@ -0,0 +1 @@
+print ("hello world!")
diff --git a/5.2/doc/logo1.png b/5.2/doc/logo1.png
new file mode 100644
index 0000000..714be3d
Binary files /dev/null and b/5.2/doc/logo1.png differ
diff --git a/5.2/doc/manual.html b/5.2/doc/manual.html
new file mode 100644
index 0000000..7a2f0f4
--- /dev/null
+++ b/5.2/doc/manual.html
@@ -0,0 +1,869 @@
+
+
+
+
+Lua Assembler/Disassembler
+
+
+
+
+
+

+
+
+
+
+
+
Lua Assembler/Disassembler 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 Lpeg in order to implement the parser
+for our Assembly syntax, which is presented on this document.
+
+
+
+
+
+
Lua Assembler/Disassembler is compatible with Lua version 5.2.0 and Lpeg version 0.10.2.
+
+
+
+
+To run Lua Assembler/Disassembler in your computer, first you need install the
+Lua language and the
+Lpeg library.
+The Lua Assembler/Disassembler may be executed as an application/script and
+can be used as a module in your Lua code too.
+In order to install, please, download the source code, extract it and then
+follow below instructions to have the applications/scripts and modules installed.
+
+
+
+Just copy the following files to a directory in your computer or
+to your $PATH (which will depend on the operating system).
+
+
+
+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.
+
+
+
+
+Just copy the following files to the first directory that is set in package.path.
+
+
+ assembler.lua
+ disassembler.lua
+ ladconf.lua
+
+
+You can also copy above files to other directory, if that's the case, please, do not
+forget to change package.path, in your Lua code, in order to look for the modules
+in the directory that you have installed them.
+
+
+
+
+
+The assembler might be used as a stand alone application/script or as a module that
+you require in your code.
+
+
+
+The stand alone application/script is called
luaa.lua.
+
+The following synopsis should be used:
+
+
+lua luaa.lua [options] [filename]
+
+
+
Available options are listed below and must be separate.
+
+
-h
+
+Just prints a help message listing all available options and a brief explanation about them.
+
+
+
-o file
+
+Output to file, instead of the default luaa.out.
+Be careful to not overwrite precious files since you might specify the output file as an existent source file.
+
+
+
+
+The module is called assembler and something like below line should be used in order to make
+its functions available.
+
+
+local assembler = require("assembler")
+
+
Available functions are listed below.
+
+
assembler.parse(contents)
+
+The function parse gets the contents of an Assembly source code as its only
+argument and returns its Abstract Syntax Tree (AST).
+
+
assembler.traverse(ast)
+
+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.
+
+
assembler.write(filename, parsed)
+
+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.
+
+
assembler.print_ast(ast)
+
+The function print_ast might be used for debug purposes. It receives the AST
+as its only parameters and prints it.
+
+
+
+
+
+
+The disassembler might be used as a stand alone application/script or as a module that
+you require in your code.
+
+
+
+The stand alone application/script is called
luad.lua.
+
+The following synopsis should be used:
+
+
+lua luad.lua [options] [filename]
+
+
+
Available options are listed below and must be separate.
+
+
-h
+
+Just prints a help message listing all available options and a brief explanation about them.
+
+
-l
+
+Produce a listing of the compiled bytecode for Lua's virtual machine on luac -l style.
+Use -l -l for full listing.
+
+
-o file
+
+Output to file, instead of the default luad.asm.
+Be careful to not overwrite precious files since you might specify the output file as an existent source file.
+
+
+
+
+The module is called disassembler and something like below line should be used in order to make
+its functions available.
+
+
+local disassembler = require("disassembler")
+
+
Available functions are listed below.
+
+
disassembler.parse(bytecode)
+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.
+
+
+
disassembler.write(filename, parsed)
+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.
+
+
+
disassembler.print_function(parsed, full)
+
+The function print_function might be used for printing Lua bytecode in luac -l 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.
+
+
+
+
+
+
+
The following command line shows how to use the disassembler in order to generate the assembly code
+for a Lua source file:
+
+$ lua luad.lua -o hello.asm hello.lua
+
+
+The above command line outputs
hello.asm which contains the assembly code for
hello.lua.
+
+It is important to remember that the
disassembler can be used with a Lua bytecode too, as we shown below:
+
+$ lua luad.lua luac.out
+
+
+Notice that in this case we haven't specified the output file, in this way the output will be the default
luadd.asm.
+
+
The following command line shows how to use the assembler in order to generate Lua bytecode from
+our assembly code:
+
+$ lua luaa.lua -o hello.out hello.asm
+
+
+The above command line outputs
hello.out which contains Lua bytecode for
hello.asm and can be executed using lua.
+
+It is important to remember that if you do not specify the output file, then Lua bytecode will be generated as
luaa.out by default.
+
+
+
+
+
+In this section we show some examples of our
assembly language. The complete syntax can be checked
+at
Assembly Syntax as well as opcodes can be checked at
Instructions.
+
+Here we show the assembly code that were generated, on above section, for
hello.lua which is a simple
+
hello world program.
+
+
+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
+
+
+Once we use the
assembler to output the Lua bytecode we can execute it using lua, as follows:
+
+
+$ lua hello.out
+hello world!
+$
+
+
+Now we define a recursive factorial in our Assembly language. Notice that
line number and
+
source line number are not needed when we are writting the Assembly code. However, they are
+generated when the
disassembler process is performed.
+
+
+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
+
+
+Notice that we have used a
label in
fat 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 (
;) and finishes at the end of the line.
+A function
main is necessary for all Assembly codes and the number of
+parameters for the function that is being defined should be specified between
+parenthesis.
+
+
+
+
+
+Here is the complete syntax of Assembly in
lpeg re module.
+
+
+ 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"
+
+
+
+
+
+
+We have four main types of instructions, they are so called iABC, iABx , iAsBx
+and iAx. The instructions should be used as follows:
+
+
+
+
+
+ | Instruction type |
+ Pattern |
+
+
+ | iABC |
+ OPCODE A, B, C |
+
+
+ | iABx |
+ OPCODE A, Bx |
+
+
+ | iAsBx |
+ OPCODE A, sBx |
+
+
+ | iAx |
+ OPCODE Ax |
+
+
+
+
+
+Some iABC instructions do not use argument B or C and below we specify when that is the case.
+Nonetheless, most of the iABC instructions follow the pattern described in above table.
+
+
+
+It is also important to keep in mind that parameters should comply with the Assembly Syntax.
+Here a some examples:
+
+
+
+
+
+ | Parameter type |
+ Example |
+
+
+ | Register |
+ $0, $1, $2, ... |
+
+
+ | Number |
+ 0, 1, 2, ... / -1, -2, ... / 0.1, 0.2, ..., 1.0, 1.1, ... / -0.1, -0.2, ..., -1.0, -1,1, ... |
+
+
+ | Name |
+ F_0_1, blah, foo, ... |
+
+
+
+ | String |
+ "oi, tudo bem?", "hello world", "%s, string", ... |
+
+
+
+
+
+We will use a instruction notation as follows to show how to use each opcode.
+
+
+
+
+
+ | Notation |
+ Meaning |
+
+
+ | R(A), R(B), R(C) |
+ Register specified in field A, B or C. |
+
+
+ | PC |
+ Program Counter. |
+
+
+ | Kst(k) |
+ A constant k that will be translated to a number. |
+
+
+ | Upvalue(name) |
+ An upvalue name that will be translated to a number. |
+
+
+ | RK(B), RK(C) |
+ A field that could be a register or a constant. |
+
+
+ | sBx |
+ Signal displacement for all kinds of jumps. It can be a number or a label. |
+
+
+ | KPROTO(name) |
+ A function name that will be used to create its closure. |
+
+
+
+
+
+Below we show each opcode that is available in our assembly language as well as their respective arguments.
+
+
+
MOVE R(A), R(B)
+R(A) := R(B)
+
+Copies the value of register R(B) into register R(A).
+
+
LOADK R(A), Kst(Bx)
+R(A) := Kst(Bx)
+
+Loads constant Kst(Bx) into register R(A). Constants can be numbers or strings.
+
+
LOADKX R(A)
+R(A) := Kst(extra arg)
+
+Loads extra arg into register R(A).
+The next instruction is always EXTRAARG.
+
+
LOADBOOL R(A), B, C
+R(A) := (Bool)B; if (C) pc++
+
+Loads a boolean value B (1 for TRUE or 0 for FALSE should be used as B) into register R(A).
+If C is not zero then next instruction is skipped.
+
+
LOADNIL R(A), B
+R(A), R(A+1), ..., R(A+B) := nil
+
+Sets to nil a range of registers from register R(A) up to B.
+
+
GETUPVAL R(A), UpValue(B)
+R(A) := UpValue[B]
+
+Copies the value in UpValue[B] into register R(A).
+
+
GETTABUP R(A), UpValue(B), RK(C)
+R(A) := UpValue[B][RK(C)]
+
+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 _ENV.
+
+
GETTABLE R(A), R(B), RK(C)
+R(A) := R(B)[RK(C)]
+
+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).
+
+
SETTABUP UpValue(A), RK(B), RK(C)
+UpValue[A][RK(B)] := RK(C)
+
+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 _ENV.
+
+
SETUPVAL R(A), UpValue(B)
+UpValue[B] := R(A)
+
+Copies the value from register R(A) into UpValue[B].
+
+
SETTABLE R(A), RK(B), RK(C)
+R(A)[RK(B)] := RK(C)
+
+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).
+
+
NEWTABLE R(A), B, C
+R(A) := {} (size = B,C)
+
+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.
+
+
SELF R(A), R(B), RK(C)
+R(A+1) := R(B); R(A) := R(B)[RK(C)]
+
+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).
+
+
ADD R(A), RK(B), RK(C)
+R(A) := RK(B) + RK(C)
+
+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.
+
+
SUB R(A), RK(B), RK(C)
+R(A) := RK(B) - RK(C)
+
+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.
+
+
MUL R(A), RK(B), RK(C)
+R(A) := RK(B) * RK(C)
+
+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.
+
+
DIV R(A), RK(B), RK(C)
+R(A) := RK(B) / RK(C)
+
+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.
+
+
MOD R(A), RK(B), RK(C)
+R(A) := RK(B) % RK(C)
+
+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.
+
+
POW R(A), RK(B), RK(C)
+R(A) := RK(B) ^ RK(C)
+
+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.
+
+
UNM R(A), R(B)
+R(A) := -R(B)
+
+Performs unary minus where register R(B) is negated and the value is placed in register R(A).
+
+
NOT R(A), R(B)
+R(A) := not R(B)
+
+Applies a boolean not to the value in register R(B) and holds the result in register R(A).
+
+
LEN R(A), R(B)
+R(A) := length of R(B)
+
+Returns the length of object in register R(B) and holds the result in register R(A).
+
+
CONCAT R(A), R(B), R(C)
+R(A) := R(B).. ... ..R(C)
+
+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).
+
+
JMP A, sBx
+pc+=sBx; if (A) close all upvalues >= R(A) + 1
+
+Performs a jump to sBx, which should be an instruction number or a label that should jump to.
+If A is true then all upvalues up to R(A) + 1 are closed.
+
+
EQ A, RK(B), RK(C)
+if ((RK(B) == RK(C)) ~= A) then pc++
+
+Performs a equality test between RK(B) and RK(C), wich may be registers or constants.
+If the boolean is not A then next instruction is skipped.
+
+
LT A, RK(B), RK(C)
+if ((RK(B) < RK(C)) ~= A) then pc++
+
+Performs a less than test between RK(B) and RK(C), wich may be registers or constants.
+If the boolean is not A then next instruction is skipped.
+
+
LE A, RK(B), RK(C)
+if ((RK(B) <= RK(C)) ~= A) then pc++
+
+Performs a less than or equal to test between RK(B) and RK(C), wich may be registers or constants.
+If the boolean is not A then next instruction is skipped.
+
+
TEST R(A), C
+if not (R(A) <=> C) then pc++
+
+Can be used to implement and/or 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.
+
+
TESTSET R(A), R(B), C
+if (R(B) <=> C) then R(A) := R(B) else pc++
+
+Also can be used to implement and/or 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).
+
+
CALL R(A), B, C
+R(A), ... ,R(A+C-2) := R(A)(R(A+1), ... ,R(A+B-1))
+
+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).
+
+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.
+
+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.
+
+
TAILCALL R(A), B, C
+return R(A)(R(A+1), ... ,R(A+B-1))
+
+Performs a tail call which happens when a return 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.
+
+
RETURN R(A), B
+return R(A), ... ,R(A+B-2)
+
+Returns to the calling function with options return values.
+
+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.
+
+
FORLOOP R(A) sBx
+R(A)+=R(A+2) ; if R(A) <?= R(A+1) then pc+=sBx; R(A+3)=R(A)
+
+Should be used to perform an iteration of a numeric for 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 for block.
+The argument sBx should be an instruction number or a label that should jump unconditionally to.
+
+
FORPREP R(A), sBx
+R(A)-=R(A+2); pc+=sBx
+
+Should be used to initialize a numeric for loop.
+The argument sBx should be an instruction number or a label that should jump back to the loop body.
+
+
TFORCALL R(A), C
+R(A+3), ... ,R(A+2+C) := R(A)(R(A+1), R(A+2))
+
+Should be used to initialize a generic for 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.
+
+
TFORLOOP R(A), sBx
+if R(A+1) ~= nil then { R(A)=R(A+1); pc += sBx }
+
+Should be used to perform an iteration of a generic for 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.
+
+
SETLIST R(A), B, C
+R(A)[(C-1)*FPF+i] := R(A+i), 1 <= i <= B
+
+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).
+
+
CLOSURE R(A), KPROTO(Bx)
+R(A) := closure(KPROTO[Bx])
+
+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.
+
+
VARARG R(A), B
+R(A), R(A+1), ..., R(A+B-2) = vararg
+
+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.
+
+
EXTRAARG Ax
+
+Sets an extra larger argument for previous opcode.
+If previous opcode is LOADKX then Ax might be any constant, but
+if previous opcode is SETLIST then Ax must be an integer.
+
+
+
+
+
+
+The current version of Lua Assembler/Disassembler is 0.2.
+
+
+
+
+
+Copyright © 2012 Andre Murbach Maidl.
+
+
+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:
+
+
+The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
+
+
+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.
+
+
+
+
+
+
+
diff --git a/5.2/ladconf.lua b/5.2/ladconf.lua
new file mode 100644
index 0000000..0a923a8
--- /dev/null
+++ b/5.2/ladconf.lua
@@ -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
diff --git a/5.2/luaa.lua b/5.2/luaa.lua
new file mode 100755
index 0000000..37e20fb
--- /dev/null
+++ b/5.2/luaa.lua
@@ -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)
diff --git a/5.2/luad.lua b/5.2/luad.lua
new file mode 100755
index 0000000..91f40cd
--- /dev/null
+++ b/5.2/luad.lua
@@ -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)