Description
Lua Assembler/Disassembler 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 Lpeg in order to implement the parser
for the assembly syntax, which is presented on this document.
Requirements
Lua Assembler/Disassembler is compatible with Lua version 5.1.4 and Lpeg version 0.10.
Install
To run Lua Assembler/Disassembler in your computer, first you need install the
Lua language and the
Lpeg library.
The Lua Assembler/Disassembler is an application composed by 2 files.
To install you need to download and just copy the following files
to a directory in your computer or to your $PATH (which will depend on the operating system).
assembler.luadisassembler.lua
Assembler Options
The following synopsis should be used:lua assembler.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.
-b
Output file on big endian, instead of the default little endian.
-o file
Output to file, instead of the default d.out. Be careful to not overwrite precious files since you might specify the output file as an existent source file.
Disassembler Options
The following synopsis should be used:lua disassembler.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.
-o file
Output to file, instead of the default d.asm. Be careful to not overwrite precious files since you might specify the output file as an existent source file.
How To Use
The following command line shows how to use the disassembler in order to generate the assembly code
for a Lua source file:
$ lua disassembler.lua -o hello.asm hello.luaThe 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 disassembler.lua luac.outNotice that in this case we haven't specified the output file, in this way the output will be the default d.asm.
The following command line shows how to use the assembler in order to generate Lua bytecode from
our assembly code:
$ lua assembler.lua -o hello.out hello.asmThe 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 d.out by default.
Examples
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.
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
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 although line number and source line number are optionals when we are writting the assembly code, instructions as well as its arguments are still mandatory.
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
In this example we haven't used line number or source line number.
However, we have used labels on fat_rec function since it is easier to check where to jump.
Assembly Syntax
Here is the complete syntax of Assembly inlpeg re module.
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 "]"
Instructions
We have three main types of instructions, they are so called iABC, iABx and iAsBx and should be used as follows:
| Instruction type | Pattern |
| iABC | OPCODE A B C |
| iABx | OPCODE A Bx |
| iAsBx | OPCODE A sBx |
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 | R[0], R[1], R[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, ... |
| Nil | NIL |
| Boolean | TRUE / FALSE |
| 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(n) | A constant that will be translated to a number n. |
| Upvalue[n] | An upvalue name that will be translated to a number n. |
| Gbl[sym] | A global variable. |
| 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. |
Below we show each opcode that is available in our assembly language as well as their respective arguments.
MOVE A B
R(A) := R(B)Copies the value of register R(B) into register R(A).
LOADK A Bx
R(A) := Kst(Bx)Loads constant Kst(Bx) into register R(A). Constants can be numbers or strings.
LOADBOOL A B C
R(A) := (Bool)B; if (C) pc++Loads a boolean value B (TRUE or FALSE should be used as B) into register R(A). If C is not zero then next instruction is skipped.
LOADNIL A B
R(A) := ... := R(B) := nilSets a range of registers from R(A) to R(B) to nil.
GETUPVAL A B
R(A) := UpValue[B]Copies the value in UpValue[B] into register R(A).
GETGLOBAL A Bx
R(A) := Gbl[Kst(Bx)]Copies the value of a global variable Gbl[Kst(Bx)] into register R(A).
GETTABLE A B 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).
SETGLOBAL A Bx
Gbl[Kst(Bx)] := R(A)Copies the value from register R(A) to a global variable Gbl[Kst(Bx)].
SETUPVAL A B
UpValue[B] := R(A)Copies the value from register R(A) into UpValue[B].
SETTABLE A B 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 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 A B 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 A B 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 A B 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 A B 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 A B 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 A B 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 A B 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 A B
R(A) := -R(B)Performs unary minus where register R(B) is negated and the value is placed in register R(A).
NOT A 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 A 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 A B 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 sBx
pc+=sBxPerforms an unconditional jump to sBx, which should be the instruction number that should jump to.
EQ A B 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 B 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 B 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 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 a assignment operation is not needed and works same way TESTSET. For more details, please, look at TESTSET.
TESTSET A 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 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 are C-1 results saved.
If C is 0 then multiple return results are saved.
TAILCALL 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 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 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 initialize 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 the instruction number that should jump unconditionally to FORLOOP.
FORPREP A sBx
R(A)-=R(A+2); pc+=sBxShould be used to perform an iteration of a numeric for loop. The argument sBx should be the instruction number that should jump back to the loop body.
TFORLOOP A C
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++Should be used to perform an iteration of 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.
SETLIST A B C
R(A)[(C-1)*FPF+i] := R(A+i), 1 <= i <= BSets 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.
CLOSE A
close all variables in the stack up to (>=) R(A)Closes all local variables in the stack up to register R(A).
CLOSURE A Bx N
R(A) := closure(KPROTO[Bx], R(A), ... ,R(A+n))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 iABx instruction the parameter N should specify the number of parameters for the function that is being defined.
VARARG A B
R(A), R(A+1), ..., R(A+B-1) = varargCopies 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.
Notes
The current version of Lua Assembler/Disassembler is 0.1.
Licence
Copyright © 2010 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.
