forked from GitHub/lad
Uploading Lua Assembler/Disassembler for Lua 5.1
This commit is contained in:
@@ -0,0 +1,844 @@
|
||||
--[[
|
||||
Lua Assembler
|
||||
Version 0.1
|
||||
Author: Andre Murbach Maidl
|
||||
]]
|
||||
|
||||
-- requires LPeg re module
|
||||
require "re"
|
||||
|
||||
-- usage message
|
||||
USAGE = [[
|
||||
usage: %s [options] [filename]
|
||||
Available options are:
|
||||
-h print this help
|
||||
-b big endian (default is little endian)
|
||||
-o name output to file name (default is %s)
|
||||
]]
|
||||
|
||||
-- global settings
|
||||
config = {}
|
||||
config.SIGNATURE = "\27Lua"
|
||||
config.VERSION = 81
|
||||
config.FORMAT = 0
|
||||
config.ENDIANNESS = 1
|
||||
config.INT = 4
|
||||
config.SIZE_T = 8
|
||||
config.INSTRUCTION = 4
|
||||
config.LUA_NUMBER = 8
|
||||
config.INTEGRAL = 0
|
||||
config.SIZE_C = 9
|
||||
config.SIZE_B = 9
|
||||
config.SIZE_Bx = config.SIZE_C + config.SIZE_B
|
||||
config.SIZE_A = 8
|
||||
config.SIZE_OP = 6
|
||||
config.POS_OP = 0
|
||||
config.POS_A = config.POS_OP + config.SIZE_OP
|
||||
config.POS_C = config.POS_A + config.SIZE_A
|
||||
config.POS_B = config.POS_C + config.SIZE_C
|
||||
config.POS_Bx = config.POS_C
|
||||
config.iABC = { config.SIZE_OP, config.SIZE_A, config.SIZE_C, config.SIZE_B }
|
||||
config.MASK_OP = math.ldexp(1, config.SIZE_OP)
|
||||
config.MASK_A = math.ldexp(1, config.SIZE_A)
|
||||
config.MASK_B = math.ldexp(1, config.SIZE_B)
|
||||
config.MASK_C = math.ldexp(1, config.SIZE_C)
|
||||
config.MASK_Bx = math.ldexp(1, config.SIZE_Bx)
|
||||
config.MAXARG_sBx = math.floor((config.MASK_Bx - 1) / 2)
|
||||
config.BITRK = math.ldexp(1, config.SIZE_B - 1)
|
||||
|
||||
-- global types
|
||||
types = {}
|
||||
types.LUA_TNIL = 0
|
||||
types.LUA_TBOOLEAN = 1
|
||||
types.LUA_TNUMBER = 3
|
||||
types.LUA_TSTRING = 4
|
||||
|
||||
-- opcodes
|
||||
opcode = {}
|
||||
opcode = { ["MOVE"] = 0, ["LOADK"] = 1, ["LOADBOOL"] = 2, ["LOADNIL"] = 3,
|
||||
["GETUPVAL"] = 4, ["GETGLOBAL"] = 5, ["GETTABLE"] = 6,
|
||||
["SETGLOBAL"] = 7, ["SETUPVAL"] = 8, ["SETTABLE"] = 9,
|
||||
["NEWTABLE"] = 10, ["SELF"] = 11, ["ADD"] = 12, ["SUB"] = 13,
|
||||
["MUL"] = 14, ["DIV"] = 15, ["MOD"] = 16, ["POW"] = 17,
|
||||
["UNM"] = 18, ["NOT"] = 19, ["LEN"] = 20, ["CONCAT"] = 21,
|
||||
["JMP"] = 22, ["EQ"] = 23, ["LT"] = 24, ["LE"] = 25, ["TEST"] = 26,
|
||||
["TESTSET"] = 27, ["CALL"] = 28, ["TAILCALL"] = 29, ["RETURN"] = 30,
|
||||
["FORLOOP"] = 31, ["FORPREP"] = 32, ["TFORLOOP"] = 33, ["SETLIST"] = 34,
|
||||
["CLOSE"] = 35, ["CLOSURE"] = 36, ["VARARG"] = 37 }
|
||||
|
||||
-- opcode modes
|
||||
opmode = {}
|
||||
opmode.iABC = 0
|
||||
opmode.iABx = 1
|
||||
opmode.iAsBx = 2
|
||||
|
||||
-- global options
|
||||
options = {}
|
||||
options.INPUT = nil
|
||||
options.OUTPUT = "d.out"
|
||||
options.GENOUT = true
|
||||
|
||||
-- table to store parsed data
|
||||
parsed = {}
|
||||
|
||||
num_par = {}
|
||||
num_par["main"] = 0
|
||||
|
||||
-- Assembly grammar
|
||||
grammar = [[
|
||||
prog <- ( <function> )* -> {}
|
||||
function <- <header> ( <instruction> )+ -> {}
|
||||
header <- ( %nl )* <s> (<user> / <main>) <s> ( %nl )+
|
||||
user <- ( "function" <s> {<name>} <s> ":" )
|
||||
main <- ( {"main"} <s> "function" <s> ":" )
|
||||
number <- <hex> / <float> / <int>
|
||||
int <- "-"? [0-9]+
|
||||
e <- [eE][+-]?[0-9]+
|
||||
float <- "-"? ([0-9]+"."[0-9]* / "."[0-9]+) <e>? / "-"? [0-9]+ <e>
|
||||
hex <- "-"? "0"[xX][0-9a-fA-F]+
|
||||
name <- [a-zA-Z_][a-zA-Z0-9_]*
|
||||
label <- <name> ":"
|
||||
string <- '"' ('\\' / '\"' / !'"' .)* '"'
|
||||
instruction <- ( <s> ( {<label>} / <number>)? <s> <ln>? <s> {<op>} ( <s> {<param>} )+ <s> ( %nl )+ ) -> {}
|
||||
op <- [A-Z]+
|
||||
param <- <register> / <number> / <name> / <string>
|
||||
register <- ( "R[" <number> "]" )
|
||||
s <- ( !%nl %s )*
|
||||
ln <- "[" <number> "]"
|
||||
]]
|
||||
|
||||
-- prints usage message
|
||||
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], options.OUTPUT))
|
||||
os.exit(1)
|
||||
end
|
||||
|
||||
-- parse args
|
||||
function doargs()
|
||||
local i = 1
|
||||
while i <= #arg do
|
||||
if string.find(arg[i], "^-") == nil then
|
||||
options.INPUT = arg[i]
|
||||
break
|
||||
elseif arg[i] == "-h" then usage()
|
||||
elseif arg[i] == "-b" then config.ENDIANNESS = 0
|
||||
elseif arg[i] == "-o" then
|
||||
i = i + 1
|
||||
if arg[i] == nil then usage("'-o' needs argument") else options.OUTPUT = arg[i] end
|
||||
else usage(string.format("'%s' unkown option", arg[i]))
|
||||
end
|
||||
i = i + 1
|
||||
end
|
||||
end
|
||||
|
||||
-- print table - debug only
|
||||
function print_table(table)
|
||||
for k,v in pairs(table) do if k > 2 then print_table (table[k]) end end
|
||||
end
|
||||
|
||||
-- print error message
|
||||
function print_error(msg)
|
||||
io.write(string.format("ERROR: %s\n", msg))
|
||||
end
|
||||
|
||||
-- check if is function or instructions table
|
||||
function is_func_name(n)
|
||||
if n % 2 ~= 0 then return true end
|
||||
return false
|
||||
end
|
||||
|
||||
-- parse a register
|
||||
function parse_reg(r)
|
||||
local pattern = re.compile [[ reg <- "R[" {%d+} "]" -> "" ]]
|
||||
return (pattern:match(r))
|
||||
end
|
||||
|
||||
-- parse constant type
|
||||
function parse_const_type(c)
|
||||
local pattern = re.compile [[ c <- <nil> / <n> / <b> / <s>
|
||||
nil <- "nil" -> "0"
|
||||
b <- t / f / tt / ff
|
||||
t <- "TRUE" -> "1"
|
||||
f <- "FALSE" -> "1"
|
||||
tt <- "true" -> "1"
|
||||
ff <- "false" -> "1"
|
||||
n <- float / int
|
||||
int <- "-"? [0-9]+ -> "3"
|
||||
float <- "-"? [0-9]+? "."? [0-9]+ ( [eE] "-"? [0-9]+)? -> "3"
|
||||
s <- nc / bc
|
||||
nc <- %w+ -> "4"
|
||||
bc <- '"' ( !'"' . / '\"' .)* '"' -> "4"
|
||||
]]
|
||||
return (pattern:match(c))
|
||||
end
|
||||
|
||||
-- parse boolean
|
||||
function parse_bool(b)
|
||||
if b == "TRUE" then return 1 end
|
||||
return 0
|
||||
end
|
||||
|
||||
-- get opcode mode
|
||||
function get_op_mode(o)
|
||||
if o == "JMP" or o == "FORLOOP" or o == "FORPREP" then
|
||||
return opmode.iAsBx
|
||||
elseif o == "LOADK" or o == "GETGLOBAL" or o == "SETGLOBAL" or o == "CLOSURE" then
|
||||
return opmode.iABx
|
||||
end
|
||||
return opmode.iABC
|
||||
end
|
||||
|
||||
-- set a register if it doesn't exist on table parsed
|
||||
function set_reg(r, parsed)
|
||||
if not parsed["register"] then
|
||||
parsed["max_stack_size"] = 0
|
||||
parsed["register"] = {}
|
||||
end
|
||||
if not parsed["register"][r] then
|
||||
local n = parsed["max_stack_size"]
|
||||
if tonumber(r) >= parsed["max_stack_size"] then parsed["max_stack_size"] = tonumber(r)+1 end
|
||||
parsed["register"][r] = n
|
||||
end
|
||||
end
|
||||
|
||||
-- set an opcode for instruction n
|
||||
function set_op(o, parsed, n)
|
||||
parsed["instruction"][n]["O"] = o
|
||||
end
|
||||
|
||||
-- set argument A for instruction n
|
||||
function set_arg_a(a, parsed, n)
|
||||
parsed["instruction"][n]["A"] = a
|
||||
end
|
||||
|
||||
-- set argument B for instruction n
|
||||
function set_arg_b(b, parsed, n)
|
||||
parsed["instruction"][n]["B"] = b
|
||||
end
|
||||
|
||||
-- set argument C for instruction n
|
||||
function set_arg_c(c, parsed, n)
|
||||
parsed["instruction"][n]["C"] = c
|
||||
end
|
||||
|
||||
-- set argument Bx for instruction n
|
||||
function set_arg_bx(bx, parsed, n)
|
||||
parsed["instruction"][n]["Bx"] = bx
|
||||
end
|
||||
|
||||
-- set argument sBx for instrction n
|
||||
function set_arg_sbx(bx, parsed, n)
|
||||
parsed["instruction"][n]["sBx"] = bx
|
||||
end
|
||||
|
||||
-- set a constant if it doesn't exist
|
||||
function set_const(c, parsed)
|
||||
if not parsed["number_of_constants"] then
|
||||
parsed["number_of_constants"] = 0
|
||||
parsed["const"] = {}
|
||||
parsed["constant"] = {}
|
||||
end
|
||||
|
||||
local t = parse_const_type(c)
|
||||
|
||||
if not parsed["const"][c] then
|
||||
local n = parsed["number_of_constants"] + 1
|
||||
parsed["number_of_constants"] = n
|
||||
parsed["const"][c] = n
|
||||
parsed["constant"][n] = {}
|
||||
parsed["constant"][n]["c_type"] = t
|
||||
parsed["constant"][n]["value"] = c
|
||||
end
|
||||
end
|
||||
|
||||
-- set a function if it doesn't exist
|
||||
function set_func(f, parsed)
|
||||
if not parsed["number_of_functions"] then
|
||||
parsed["number_of_functions"] = 0
|
||||
parsed["func"] = {}
|
||||
parsed["function"] = {}
|
||||
end
|
||||
|
||||
if not parsed["func"][f] then
|
||||
local n = parsed["number_of_functions"] + 1
|
||||
parsed["number_of_functions"] = n
|
||||
parsed["func"][f] = n
|
||||
parsed["function"][n] = f
|
||||
end
|
||||
end
|
||||
|
||||
-- set an upvalue if it doesn't exist
|
||||
function set_upval(u, parsed)
|
||||
if not parsed["number_of_upvalues"] then
|
||||
parsed["number_of_upvalues"] = 0
|
||||
parsed["upval"] = {}
|
||||
parsed["upvalue"] = {}
|
||||
end
|
||||
|
||||
if not parsed["upval"][u] then
|
||||
local n = parsed["number_of_upvalues"] + 1
|
||||
parsed["number_of_upvalues"] = n
|
||||
parsed["upval"][u] = n
|
||||
parsed["upvalue"][n] = u
|
||||
end
|
||||
end
|
||||
|
||||
-- check if a string is a label
|
||||
function check_label(x)
|
||||
return string.find(x,":")
|
||||
end
|
||||
|
||||
-- set a label if it doesn't exist
|
||||
function set_label(label, parsed, n)
|
||||
if not parsed["label"] then parsed["label"] = {} end
|
||||
|
||||
if not parsed["label"][label] then
|
||||
parsed["label"][label] = n
|
||||
else
|
||||
options.GENOUT = false
|
||||
print_error("label " .. label .. " already defined at " .. parsed["id"])
|
||||
end
|
||||
end
|
||||
|
||||
-- parse an instruction
|
||||
function parse_inst(table, parsed, n)
|
||||
local op = ""
|
||||
local a = 2
|
||||
local b = 3
|
||||
local c = 4
|
||||
if check_label(table[1]) == nil then
|
||||
op = table[1]
|
||||
else
|
||||
set_label(table[1], parsed, n)
|
||||
op = table[2]
|
||||
a = 3
|
||||
b = 4
|
||||
c = 5
|
||||
end
|
||||
|
||||
set_op(op, parsed, n)
|
||||
|
||||
if op ~= "JMP" then
|
||||
set_reg(parse_reg(table[a]), parsed)
|
||||
set_arg_a(parse_reg(table[a]), parsed, n)
|
||||
end
|
||||
|
||||
if op == "MOVE" or op == "LOADNIL" or
|
||||
op == "UNM" or op == "NOT" or op == "LEN" then
|
||||
set_reg(parse_reg(table[b]), parsed)
|
||||
set_arg_b(parse_reg(table[b]), parsed, n)
|
||||
set_arg_c(0, parsed, n)
|
||||
elseif op == "LOADK" then
|
||||
set_const(table[b], parsed)
|
||||
set_arg_bx(parsed["const"][table[b]], parsed, n)
|
||||
elseif op == "LOADBOOL" then
|
||||
set_const(table[b], parsed)
|
||||
set_arg_b(parse_bool(table[b]), parsed, n)
|
||||
set_arg_c(table[c], parsed, n)
|
||||
elseif op == "GETUPVAL" or op == "SETUPVAL" then
|
||||
set_upval(table[b], parsed)
|
||||
set_arg_b(parsed["upval"][table[b]]-1, parsed, n)
|
||||
set_arg_c(0, parsed, n)
|
||||
elseif op == "GETGLOBAL" or op == "SETGLOBAL" then
|
||||
set_const(table[b], parsed)
|
||||
set_arg_bx(parsed["const"][table[b]], parsed, n)
|
||||
elseif op == "GETTABLE" or op == "SETTABLE" or op == "SELF" or
|
||||
op == "ADD" or op == "SUB" or op == "MUL" or
|
||||
op == "DIV" or op == "MOD" or op == "POW" or
|
||||
op == "EQ" or op == "LT" or op == "LE" then
|
||||
if not parse_reg(table[b]) then
|
||||
set_const(table[b], parsed)
|
||||
set_arg_b(max_int() + (parsed["const"][table[b]]-1), parsed, n)
|
||||
else
|
||||
set_reg(parse_reg(table[b]), parsed)
|
||||
set_arg_b(parse_reg(table[b]), parsed, n)
|
||||
end
|
||||
if not parse_reg(table[c]) then
|
||||
set_const(table[c], parsed)
|
||||
set_arg_c(max_int() + (parsed["const"][table[c]]-1), parsed, n)
|
||||
else
|
||||
set_reg(parse_reg(table[c]), parsed)
|
||||
set_arg_c(parse_reg(table[c]), parsed, n)
|
||||
end
|
||||
elseif op == "NEWTABLE" or op == "CALL" or op == "TAILCALL" or
|
||||
op == "SETLIST" then
|
||||
set_arg_b(table[b], parsed, n)
|
||||
set_arg_c(table[c], parsed, n)
|
||||
elseif op == "CONCAT" then
|
||||
set_reg(parse_reg(table[b]), parsed)
|
||||
set_arg_b(parse_reg(table[b]), parsed, n)
|
||||
set_reg(parse_reg(table[c]), parsed)
|
||||
set_arg_c(parse_reg(table[c]), parsed, n)
|
||||
elseif op == "JMP" then
|
||||
set_arg_a(0, parsed, n)
|
||||
set_arg_sbx(table[a], parsed, n)
|
||||
elseif op == "FORLOOP" or op == "FORPREP" then
|
||||
set_arg_sbx(table[b], parsed, n)
|
||||
elseif op == "TEST" or op == "TESTSET" then
|
||||
set_reg(parse_reg(table[b]), parsed)
|
||||
set_arg_b(parse_reg(table[b]), parsed, n)
|
||||
set_arg_c(parse_bool(table[c]), parsed, n)
|
||||
elseif op == "RETURN" then
|
||||
set_arg_b(table[b], parsed, n)
|
||||
set_arg_c(0, parsed, n)
|
||||
elseif op == "TFORLOOP" then
|
||||
set_arg_b(0, parsed, n)
|
||||
set_arg_c(table[b], parsed, n)
|
||||
elseif op == "CLOSE" then
|
||||
set_arg_b(0, parsed, n)
|
||||
set_arg_c(0, parsed, n)
|
||||
elseif op == "CLOSURE" then
|
||||
set_func(table[b], parsed)
|
||||
set_arg_bx(parsed["func"][table[b]], parsed, n)
|
||||
num_par[table[b]] = tonumber(table[c])
|
||||
elseif op == "VARARG" then
|
||||
set_arg_b(table[b], parsed, n)
|
||||
set_arg_c(0, parsed, n)
|
||||
parsed["is_vararg"] = table[b] - 1
|
||||
num_par[parsed["id"]] = 0
|
||||
else
|
||||
-- shouldn't enter here
|
||||
options.GENOUT = false
|
||||
print_error(op .. " : not a valid operator")
|
||||
end
|
||||
end
|
||||
|
||||
-- parse a function
|
||||
function parse_func(table, parsed)
|
||||
parsed["instruction"] = {}
|
||||
local n = 0
|
||||
for k in pairs (table) do
|
||||
parsed["instruction"][k] = {}
|
||||
parse_inst(table[k], parsed, k)
|
||||
n = k
|
||||
end
|
||||
parsed["number_of_instructions"] = n
|
||||
end
|
||||
|
||||
-- set a vararg function
|
||||
function set_vararg(parsed, f)
|
||||
if f == "main" then parsed["is_vararg"] = 2 else parsed["is_vararg"] = 0 end
|
||||
end
|
||||
|
||||
-- fix function values
|
||||
function fix_function_values(parsed)
|
||||
if parsed["max_stack_size"] < 2 then parsed["max_stack_size"] = 2 end
|
||||
if not parsed["number_of_parameters"] then parsed["number_of_parameters"] = 0 end
|
||||
if not parsed["number_of_upvalues"] then parsed["number_of_upvalues"] = 0 end
|
||||
end
|
||||
|
||||
-- start parsing process
|
||||
function parse_all(table, parsed)
|
||||
local f
|
||||
for k,v in pairs (table) do
|
||||
if is_func_name(k) then
|
||||
f = v
|
||||
parsed[f] = {}
|
||||
parsed[f]["id"] = f
|
||||
else
|
||||
set_vararg(parsed[f], f)
|
||||
parse_func(table[k], parsed[f])
|
||||
fix_function_values(parsed[f])
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
-- write a byte to output
|
||||
function write_byte(output, byte)
|
||||
if byte ~= 0 then
|
||||
output:write(string.format("%c", byte))
|
||||
else
|
||||
output:write('\0')
|
||||
end
|
||||
end
|
||||
|
||||
-- write source name to output
|
||||
function write_source_name(output, name)
|
||||
write_str(output, name)
|
||||
end
|
||||
|
||||
-- write header to output
|
||||
function write_header(output)
|
||||
for i=1,string.len(config.SIGNATURE) do
|
||||
write_byte (output, string.byte(config.SIGNATURE,i))
|
||||
end
|
||||
write_byte (output, config.VERSION)
|
||||
write_byte (output, config.FORMAT)
|
||||
write_byte (output, config.ENDIANNESS)
|
||||
write_byte (output, config.INT)
|
||||
write_byte (output, config.SIZE_T)
|
||||
write_byte (output, config.INSTRUCTION)
|
||||
write_byte (output, config.LUA_NUMBER)
|
||||
write_byte (output, config.INTEGRAL)
|
||||
end
|
||||
|
||||
-- gen max int
|
||||
function max_int()
|
||||
return ( 2 ^ 8 )
|
||||
end
|
||||
|
||||
-- converts int to little endian
|
||||
function gen_int(n)
|
||||
local t = {}
|
||||
n = math.floor(n)
|
||||
if n >= 0 then
|
||||
for i=1,config.INT do
|
||||
t[i] = n % max_int()
|
||||
n = math.floor(n / max_int())
|
||||
end
|
||||
end
|
||||
return t
|
||||
end
|
||||
|
||||
-- writes an integer
|
||||
function write_int(output, n)
|
||||
local i = gen_int(n)
|
||||
if config.ENDIANNESS == 1 then
|
||||
for k=1,config.INT do write_byte(output, i[k]) end
|
||||
else
|
||||
for k=config.INT,1,-1 do write_byte(output, i[k]) end
|
||||
end
|
||||
end
|
||||
|
||||
-- write function values
|
||||
function write_function_values(output, parsed)
|
||||
parsed["number_of_parameters"] = num_par[parsed["id"]]
|
||||
write_byte(output,parsed["number_of_upvalues"])
|
||||
write_byte(output,parsed["number_of_parameters"])
|
||||
write_byte(output,parsed["is_vararg"])
|
||||
write_byte(output,parsed["max_stack_size"])
|
||||
end
|
||||
|
||||
-- gen iABC instruction
|
||||
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 < config.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 + config.iABC[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
|
||||
|
||||
-- gen iABx instruction
|
||||
function gen_iABx(O, A, Bx)
|
||||
return gen_iABC(O, A, math.floor(Bx / config.MASK_C), (Bx % config.MASK_C))
|
||||
end
|
||||
|
||||
-- gen iAsBx instruction
|
||||
function gen_iAsBx(O, A, sBx)
|
||||
return gen_iABx(O, A, (sBx + config.MAXARG_sBx))
|
||||
end
|
||||
|
||||
-- write instruction to output
|
||||
function write_instruction(output, parsed, n)
|
||||
local i = parsed["instruction"][n]
|
||||
local o = opcode[i["O"]]
|
||||
local m = get_op_mode(i["O"])
|
||||
local t = {}
|
||||
|
||||
if m == opmode.iABC then
|
||||
t = gen_iABC(o,i["A"],i["B"],i["C"])
|
||||
elseif m == opmode.iABx then
|
||||
t = gen_iABx(o,i["A"],i["Bx"] - 1)
|
||||
elseif m == opmode.iAsBx then
|
||||
local sbx
|
||||
if tonumber(parse_const_type(i["sBx"])) == types.LUA_TSTRING then
|
||||
local label = i["sBx"] .. ":"
|
||||
if not parsed["label"][label] then
|
||||
sbx = 1
|
||||
print_error("label " .. label .. " not defined at " .. parsed["id"])
|
||||
else
|
||||
sbx = (parsed["label"][label] - n) - 1
|
||||
end
|
||||
else
|
||||
sbx = (i["sBx"] - n) - 1
|
||||
end
|
||||
t = gen_iAsBx(o,i["A"], sbx)
|
||||
end
|
||||
|
||||
if config.ENDIANNESS == 1 then
|
||||
for k=1,config.INSTRUCTION do write_byte(output, t[k]) end
|
||||
else
|
||||
for k=config.INSTRUCTION,1,-1 do write_byte(output, t[k]) end
|
||||
end
|
||||
end
|
||||
|
||||
-- write all instructions
|
||||
function write_instructions(output, parsed)
|
||||
-- number of instructions
|
||||
write_int(output, parsed["number_of_instructions"])
|
||||
for i=1,parsed["number_of_instructions"] do
|
||||
write_instruction(output, parsed, i)
|
||||
end
|
||||
end
|
||||
|
||||
-- gen a string length
|
||||
function gen_len(n)
|
||||
local t = {}
|
||||
n = math.floor(n)
|
||||
if n >= 0 then
|
||||
for i=1,config.SIZE_T do
|
||||
t[i] = n % max_int()
|
||||
n = math.floor(n / max_int())
|
||||
end
|
||||
end
|
||||
return t
|
||||
end
|
||||
|
||||
-- write string length to output
|
||||
function write_len(output, n)
|
||||
local l = gen_len(n)
|
||||
if config.ENDIANNESS == 1 then
|
||||
for k=1,config.SIZE_T do write_byte(output, l[k]) end
|
||||
else
|
||||
for k=config.SIZE_T,1,-1 do write_byte(output, l[k]) end
|
||||
end
|
||||
end
|
||||
|
||||
-- fix a string
|
||||
function fix_str(str)
|
||||
if string.find(str, "\\\"") then str = string.gsub(str, "\\\"", '"') end
|
||||
if string.find(str, "\\\\") then str = string.gsub(str, "\\\\", '\\') end
|
||||
if string.find(str, "\\a") then str = string.gsub(str, "\\a", '\a') end
|
||||
if string.find(str, "\\b") then str = string.gsub(str, "\\b", '\b') end
|
||||
if string.find(str, "\\f") then str = string.gsub(str, "\\f", '\f') end
|
||||
if string.find(str, "\\n") then str = string.gsub(str, "\\n", '\n') end
|
||||
if string.find(str, "\\r") then str = string.gsub(str, "\\r", '\r') end
|
||||
if string.find(str, "\\t") then str = string.gsub(str, "\\t", '\t') end
|
||||
if string.find(str, "\\v") then str = string.gsub(str, "\\v", '\v') end
|
||||
if string.find(str, "\\0") then str = string.gsub(str, "\\0", "\0") end
|
||||
if string.find(str, "\\[0-9]+") then str = string.gsub(str, "\\([0-9]+)", function (s) return string.format("%c", tonumber(s)) end) end
|
||||
return str
|
||||
end
|
||||
|
||||
-- write a string to output
|
||||
function write_str(output, str)
|
||||
if (string.byte(str, 1) == 34 or string.byte(str, 1) == 39) and
|
||||
(string.byte(str, string.len(str)) == 34 or string.byte(str, string.len(str)) == 39) then
|
||||
str = string.sub(str, 2, string.len(str) - 1)
|
||||
str = fix_str(str)
|
||||
end
|
||||
local len = string.len(str)
|
||||
write_len(output, len + 1)
|
||||
if config.ENDIANNESS == 1 then
|
||||
for i=1,len do write_byte(output, string.byte(str, i)) end
|
||||
write_byte(output, 0)
|
||||
else
|
||||
wirte_byte(output, 0)
|
||||
for i=len,1,-1 do write_byte(output, string.byte(str, i)) end
|
||||
end
|
||||
end
|
||||
|
||||
-- get a byte
|
||||
function get_byte(v)
|
||||
return math.floor(v / 256), string.char(math.floor(v) % 256)
|
||||
end
|
||||
|
||||
-- converts float to little endian
|
||||
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
|
||||
|
||||
-- write a float to output
|
||||
function write_number(output, n)
|
||||
local t = convert_number(tonumber(n))
|
||||
if config.ENDIANNESS == 1 then
|
||||
for k=1,config.LUA_NUMBER do write_byte(output, t[k]) end
|
||||
else
|
||||
for k=config.LUA_NUMBER,1,-1 do write_byte(output, t[k]) end
|
||||
end
|
||||
end
|
||||
|
||||
-- write a constant to output
|
||||
function write_constant(output, parsed)
|
||||
local t = tonumber(parsed["c_type"])
|
||||
write_byte(output, t)
|
||||
if t == types.LUA_TNIL then
|
||||
-- do not need to right anything
|
||||
elseif t == types.LUA_TBOOLEAN then
|
||||
if parsed["value"] == "TRUE" or parsed["value"] == "true" then write_byte(output, 1) else write_byte(output, 0) end
|
||||
elseif t == types.LUA_TNUMBER then
|
||||
write_number(output, parsed["value"])
|
||||
elseif t == types.LUA_TSTRING then
|
||||
write_str(output, parsed["value"])
|
||||
end
|
||||
end
|
||||
|
||||
-- write all constants
|
||||
function write_constants(output, parsed)
|
||||
if not parsed["number_of_constants"] then parsed["number_of_constants"] = 0 end
|
||||
write_int(output, parsed["number_of_constants"])
|
||||
for i=1,parsed["number_of_constants"] do
|
||||
write_constant(output, parsed["constant"][i])
|
||||
end
|
||||
end
|
||||
|
||||
-- write locals to output
|
||||
function write_locals(output, parsed)
|
||||
if not parsed["number_of_locals"] then parsed["number_of_locals"] = 0 end
|
||||
write_int(output, parsed["number_of_locals"])
|
||||
for i=1,parsed["number_of_locals"] do
|
||||
write_str(output, parsed["local"][i])
|
||||
end
|
||||
end
|
||||
|
||||
-- write upvalues to output
|
||||
function write_upvalues(output, parsed)
|
||||
if not parsed["number_of_upvalues"] then parsed["number_of_upvalues"] = 0 end
|
||||
write_int(output, parsed["number_of_upvalues"])
|
||||
for i=1,parsed["number_of_upvalues"] do
|
||||
write_str(output, parsed["upvalue"][i])
|
||||
end
|
||||
end
|
||||
|
||||
-- write a function block
|
||||
function write_function_block(output, parsed, to_be_parsed)
|
||||
local nf = 0
|
||||
-- line defined
|
||||
write_int(output, 0)
|
||||
-- last line defined
|
||||
write_int(output, 0)
|
||||
write_function_values(output, parsed)
|
||||
write_instructions(output, parsed)
|
||||
write_constants(output, parsed)
|
||||
if parsed["number_of_functions"] then nf = parsed["number_of_functions"] end
|
||||
write_int(output, nf)
|
||||
for n=1,nf do
|
||||
write_len(output, 0)
|
||||
write_function_block(output, to_be_parsed[parsed["function"][n]], to_be_parsed)
|
||||
end
|
||||
-- source line positions
|
||||
write_int(output, 0)
|
||||
-- locals
|
||||
write_int(output, 0)
|
||||
-- upvalues
|
||||
write_int(output, 0)
|
||||
end
|
||||
|
||||
-- write binary file
|
||||
function write_bin(output, parsed)
|
||||
write_header(output)
|
||||
write_source_name(output, "@")
|
||||
write_function_block(output, parsed["main"], parsed)
|
||||
end
|
||||
|
||||
-- do a pre scan at file to be parsed
|
||||
function scan_file(file)
|
||||
local scan = {}
|
||||
local f
|
||||
local main = true
|
||||
for line in io.lines(file) do
|
||||
if not string.byte(line) then -- new line, do nothing
|
||||
elseif string.find(line, "main function:") and main then
|
||||
f = "main"
|
||||
scan[f] = {}
|
||||
scan[f]["line"] = {}
|
||||
scan[f]["noi"] = 0
|
||||
main = false
|
||||
elseif string.find(line, "function [a-zA-Z_][a-zA-Z0-9_]*:") then
|
||||
f = string.gsub(string.gsub(string.gsub(line,"function", ""),":",""),"%s+","")
|
||||
scan[f] = {}
|
||||
scan[f]["line"] = {}
|
||||
scan[f]["noi"] = 0
|
||||
else
|
||||
if f then scan[f]["noi"] = scan[f]["noi"] + 1 ; scan[f]["line"][scan[f]["noi"]] = line end
|
||||
end
|
||||
end
|
||||
return scan
|
||||
end
|
||||
|
||||
-- check if file could be parsed
|
||||
function check_table(table, scan)
|
||||
local f
|
||||
if not table[1] then
|
||||
print_error("main could not be parsed")
|
||||
return false
|
||||
end
|
||||
for k,v in pairs (table) do
|
||||
if is_func_name(k) then
|
||||
f = v
|
||||
else
|
||||
if scan[f]["noi"] ~= #v then
|
||||
print_error("at function " .. f .. " could not parse instruction " .. #v + 1)
|
||||
print (scan[f]["line"][#v + 1])
|
||||
return false
|
||||
end
|
||||
end
|
||||
end
|
||||
return true
|
||||
end
|
||||
|
||||
-- check if file exists
|
||||
function exists(file)
|
||||
local f = io.open(file, "r")
|
||||
if f then
|
||||
f:close()
|
||||
return true
|
||||
end
|
||||
return false
|
||||
end
|
||||
|
||||
if #arg < 1 then
|
||||
usage("no input file given");
|
||||
end
|
||||
|
||||
doargs()
|
||||
|
||||
if options.INPUT == nil then
|
||||
usage("no input file given")
|
||||
end
|
||||
|
||||
if not exists(options.INPUT) then
|
||||
usage(options.INPUT .. " not found")
|
||||
end
|
||||
|
||||
local parser = re.compile(grammar)
|
||||
|
||||
input = io.open(options.INPUT, "r")
|
||||
contents = input:read("*a")
|
||||
input:close()
|
||||
|
||||
local scan = scan_file(options.INPUT)
|
||||
|
||||
local table = parser:match(contents)
|
||||
|
||||
if check_table(table, scan) then parse_all(table, parsed) else options.GENOUT = false end
|
||||
|
||||
if options.GENOUT then
|
||||
output = io.open(options.OUTPUT, "wb")
|
||||
write_bin(output, parsed)
|
||||
output:close()
|
||||
end
|
||||
|
||||
os.exit(0)
|
||||
File diff suppressed because it is too large
Load Diff
Binary file not shown.
|
After Width: | Height: | Size: 11 KiB |
@@ -0,0 +1,739 @@
|
||||
<!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_options">Assembler Options</a> | <a href="#disassembler_options">Disassembler Options</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 set of two applications. The former is the disassembler, which
|
||||
can be used to generate assembly code from Lua code or Lua bytecode, while the latter is the assembler, which
|
||||
can be used to generate Lua byte code from assembly code.
|
||||
|
||||
The assembler uses <a href="http://www.inf.puc-rio.br/~roberto/lpeg/">Lpeg</a> in order to implement the parser
|
||||
for the 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.1.4 and Lpeg version 0.10.
|
||||
<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/manual/5.1/manual.html#pdf-package.path">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> is an application composed by 2 files.
|
||||
To install you need to <a href="">download</a> and just copy the following files
|
||||
to a directory in your computer or to your $PATH (which will depend on the operating system).
|
||||
|
||||
<ul>
|
||||
<li><code>assembler.lua</code></li>
|
||||
<li><code>disassembler.lua</code></li>
|
||||
</ul>
|
||||
|
||||
</p>
|
||||
|
||||
<hr />
|
||||
|
||||
<h3><a name="assembler_options">Assembler Options</a></h3>
|
||||
|
||||
The following synopsis should be used:
|
||||
|
||||
<pre class="lua-code">
|
||||
lua assembler.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>d.out</b>.
|
||||
Be careful to not overwrite precious files since you might specify the output file as an existent source file.
|
||||
</p>
|
||||
|
||||
<hr />
|
||||
|
||||
<h3><a name="disassembler_options">Disassembler Options</a></h3>
|
||||
|
||||
The following synopsis should be used:
|
||||
|
||||
<pre class="lua-code">
|
||||
lua disassembler.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.
|
||||
</p>
|
||||
<a name="op-ver"><h3 style="color:#00008B;">-o file</h3></a>
|
||||
<p>
|
||||
Output to <b>file</b>, instead of the default <b>d.asm</b>.
|
||||
Be careful to not overwrite precious files since you might specify the output file as an existent source file.
|
||||
</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 disassembler.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 disassembler.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>d.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 assembler.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>d.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">
|
||||
main function:
|
||||
1 [1] GETGLOBAL R[0] print
|
||||
2 [1] LOADK R[1] "hello world!"
|
||||
3 [1] CALL R[0] 2 1
|
||||
4 [1] RETURN R[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 although <b>line number</b> and
|
||||
<b>source line number</b> are optionals when we are writting the assembly code, <b>instructions</b>
|
||||
as well as its arguments are still mandatory.
|
||||
|
||||
<pre class="lua-code">
|
||||
main function:
|
||||
CLOSURE R[0] fat_rec 1
|
||||
SETGLOBAL R[0] fat
|
||||
GETGLOBAL R[0] print
|
||||
GETGLOBAL R[1] fat
|
||||
LOADK R[2] 5
|
||||
CALL R[1] 2 0
|
||||
CALL R[0] 0 1
|
||||
RETURN R[0] 1
|
||||
|
||||
function fat_rec:
|
||||
EQ R[0] R[0] 0
|
||||
JMP label1
|
||||
LOADK R[1] 0
|
||||
RETURN R[1] 2
|
||||
JMP label3
|
||||
label1: EQ R[0] R[0] 1
|
||||
JMP label2
|
||||
LOADK R[1] 1
|
||||
RETURN R[1] 2
|
||||
JMP label3
|
||||
label2: GETGLOBAL R[1] fat
|
||||
SUB R[2] R[0] 1
|
||||
CALL R[1] 2 2
|
||||
MUL R[1] R[0] R[1]
|
||||
RETURN R[1] 2
|
||||
label3: RETURN R[0] 1
|
||||
</pre>
|
||||
|
||||
In this example we haven't used <b>line number</b> or <b>source line number</b>.
|
||||
However, we have used <b>labels</b> on <b>fat_rec</b> function since it is easier to check where to jump.
|
||||
|
||||
<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 <- ( function )*
|
||||
function <- header ( instruction )+
|
||||
header <- ( %nl )* s (user / main) s ( %nl )+
|
||||
user <- ( "function" s name s ":" )
|
||||
main <- ( "main" s "function" s ":" )
|
||||
number <- hex / float / int
|
||||
int <- "-"? [0-9]+
|
||||
float <- "-"? [0-9]+? "."? [0-9]+ ( [eE] ("-" / "+")? [0-9]+)? /
|
||||
"-"? ([0-9]+ ".")? [0-9]+ ( [eE] ("-" / "+")? [0-9]+)?
|
||||
hex <- "0x" [a-zA-Z0-9]+
|
||||
name <- [a-zA-Z_][a-zA-Z0-9_]*
|
||||
label <- name ":"
|
||||
string <- '"' ('\\' / '\"' / !'"' .)* '"'
|
||||
instruction <- ( s ( label / number)? s ln? s op ( s param )+ s ( %nl )+ )
|
||||
op <- [A-Z]+
|
||||
param <- register / number / name / string
|
||||
register <- ( "R[" number "]" )
|
||||
s <- ( !%nl %s )*
|
||||
ln <- "[" number "]"
|
||||
</pre>
|
||||
<hr />
|
||||
|
||||
<h3><a name="opcodes">Instructions</a></h3>
|
||||
|
||||
<p>
|
||||
We have three main types of instructions, they are so called <b>iABC</b>, <b>iABx</b> and <b>iAsBx</b>
|
||||
and 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>
|
||||
</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>R[0], R[1], R[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(n)</td>
|
||||
<td>A constant that will be translated to a number n.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Upvalue[n]</td>
|
||||
<td>An upvalue name that will be translated to a number n.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Gbl[sym]</td>
|
||||
<td>A global variable.</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>
|
||||
</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 A 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 A 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;">LOADBOOL A B C</h3></a>
|
||||
R(A) := (Bool)B; if (C) pc++
|
||||
<p>
|
||||
Loads a boolean value B (<b>TRUE</b> or <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 A B</h3></a>
|
||||
R(A) := ... := R(B) := nil
|
||||
<p>
|
||||
Sets a range of registers from R(A) to R(B) to <b>nil</b>.
|
||||
</p>
|
||||
<a name="op-com"><h3 style="color:#00008B;">GETUPVAL A 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;">GETGLOBAL A Bx</h3></a>
|
||||
R(A) := Gbl[Kst(Bx)]
|
||||
<p>
|
||||
Copies the value of a global variable Gbl[Kst(Bx)] into register R(A).
|
||||
</p>
|
||||
<a name="op-com"><h3 style="color:#00008B;">GETTABLE A B 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;">SETGLOBAL A Bx</h3></a>
|
||||
Gbl[Kst(Bx)] := R(A)
|
||||
<p>
|
||||
Copies the value from register R(A) to a global variable Gbl[Kst(Bx)].
|
||||
</p>
|
||||
<a name="op-com"><h3 style="color:#00008B;">SETUPVAL A 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 A B 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 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 A B 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 A B 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 A B 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 A B 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 A B 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 A B 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 A B 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 A 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 A 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 A 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 A B 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 sBx</h3></a>
|
||||
pc+=sBx
|
||||
<p>
|
||||
Performs an unconditional jump to sBx, which should be the instruction number that should jump to.
|
||||
</p>
|
||||
<a name="op-com"><h3 style="color:#00008B;">EQ A B 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 B 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 B 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 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 a assignment operation is not needed and works same way TESTSET.
|
||||
For more details, please, look at TESTSET.
|
||||
</p>
|
||||
<a name="op-com"><h3 style="color:#00008B;">TESTSET A 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 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 are C-1 results saved.
|
||||
If C is 0 then multiple return results are saved.
|
||||
</p>
|
||||
<a name="op-com"><h3 style="color:#00008B;">TAILCALL 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 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 A sBx</h3></a>
|
||||
R(A)+=R(A+2) ; if R(A) <?= R(A+1) then { pc+=sBx; R(A+3)=R(A)
|
||||
<p>
|
||||
Should be used to initialize 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 the instruction number that should jump unconditionally to FORLOOP.
|
||||
</p>
|
||||
<a name="op-com"><h3 style="color:#00008B;">FORPREP A sBx</h3></a>
|
||||
R(A)-=R(A+2); pc+=sBx
|
||||
<p>
|
||||
Should be used to perform an iteration of a numeric <b>for</b> loop.
|
||||
The argument sBx should be the instruction number that should jump back to the loop body.
|
||||
</p>
|
||||
<a name="op-com"><h3 style="color:#00008B;">TFORLOOP A C</h3></a>
|
||||
R(A+3), ... ,R(A+2+C) := R(A)(R(A+1), R(A+2)) ; if R(A+3) ~= nil then R(A+2)=R(A+3) else pc++
|
||||
<p>
|
||||
Should be used to perform an iteration of 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;">SETLIST 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.
|
||||
</p>
|
||||
<a name="op-com"><h3 style="color:#00008B;">CLOSE A</h3></a>
|
||||
close all variables in the stack up to (>=) R(A)
|
||||
<p>
|
||||
Closes all local variables in the stack up to register R(A).
|
||||
</p>
|
||||
<a name="op-com"><h3 style="color:#00008B;">CLOSURE A Bx N</h3></a>
|
||||
R(A) := closure(KPROTO[Bx], R(A), ... ,R(A+n))
|
||||
<p>
|
||||
Should be used to create an instance of a closure of a function where Bx is the
|
||||
function name and R(A) is the register that assigns the reference to the instantiated
|
||||
function object. Although it is a <b>iABx</b> instruction the parameter N should specify
|
||||
the number of parameters for the function that is being defined.
|
||||
</p>
|
||||
<a name="op-com"><h3 style="color:#00008B;">VARARG A B</h3></a>
|
||||
R(A), R(A+1), ..., R(A+B-1) = vararg
|
||||
<p>
|
||||
Copies B-1 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>
|
||||
|
||||
<hr />
|
||||
|
||||
<h3><a name="notes">Notes</a></h3>
|
||||
<p>
|
||||
The current version of <code>Lua Assembler/Disassembler</code> is 0.1.
|
||||
</p>
|
||||
<hr />
|
||||
|
||||
<h3><a name="licence">Licence</a></h3>
|
||||
<p>
|
||||
Copyright © 2010 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>
|
||||
Reference in New Issue
Block a user