1
0
Fork 0
mirror of https://github.com/notwa/lips synced 2024-11-14 09:39:03 -08:00

add something resembling documentation

This commit is contained in:
Connor Olding 2016-01-18 12:56:06 -08:00
parent e7fe9d972c
commit 9e4167e04a
3 changed files with 198 additions and 3 deletions

View file

@ -9,14 +9,106 @@ It does little more than output hex.
Not for production. Much of the code and syntax is untested and likely to change.
Even this README is incomplete.
## Usage
Copy the lips directory to somewhere Lua's `package.path` can find it.
If you're using it locally, you will need to write something like:
```
package.path = package.path..";./?/init.lua"
```
You can then use it as such:
[example.lua][elua] — [example.asm][easm]
[elua]: ./example.lua
[easm]: ./example.asm
By default, lips will print the assembled word values in hex:
```
18800017
00001025
2401002F
10810002
0081082A
10200012
2488FFFF
00084080
etc...
```
Since lips is designed to assist with ROM/RAM hacking,
it cannot produce executable files on its own.
Instead, it is meant to be integrated with an existing executable or memory dump.
For instance, consider [this injection routine][inject.lua]
written for the Nintendo 64 Zelda games.
[inject.lua]: https://github.com/notwa/mm/blob/master/Lua/inject.lua
## Syntax
(TODO)
A derivative of [CajeASM's][caje] syntax.
lips uses a derivative of [CajeASM's][caje] syntax.
It takes a couple notes from more traditional assemblers as well.
[caje]: https://github.com/Tarek701/CajeASM/
A run-down of various syntax elements:
```
// this is a comment
/* this is a block comment */
// this is comparible to C's #define my_const 0xDEADBEEF
[my_const]: 0xDEADBEEF
// we can then use it in instructions with a @ prefix
li a0, @my_const
// whitespace is optional
li a0,@myconst
// commas can be optional too,
// but this feature will likely be removed in the future.
li a0 @myconst
// instruction/register names are case-insensitive, as are hex digits
LI A0, @my_const
LuI a0, 0xDeAd
// coprocessor 0 registers are case-insensitive as well,
// though this may change in the future.
mfc0 a1, CouNT
// labels are defined with a colon and referenced without prefix, as such:
my_label:
b my_label
nop
// directives are prefixed with a dot.
// also, labels may be used in .word directives.
.word my_label, 1, 2, 3, 0x4567
// octal numbers are supported
.short 0177, 0404
.align // implied argument of 2, for a 2**n=4 byte alignment
// loading and storing can be written in several ways (addressing modes)
lw s0, label
lw s1, (s0)
lw s2, 256(s0)
lw s3, label(s0)
// this is currently unsupported however
sw s2, label+4
sw s3, label+4(s0)
// relative labels, borrowed from asw (except ours require a suffixing colon)
-:
b ++
nop
+:
-:
b --
nop
+:
b -
nop
// TODO: more examples!
```
## Instructions
Instructions were primarily referenced from [the N64 Toolkit: Opcodes.][n64op]

100
example.asm Normal file
View file

@ -0,0 +1,100 @@
/* lips example code: fibonacci numbers
* this isn't a particularily useful or realistic example,
* but it demonstrates syntax and various features in lips.
*/
[max_n]: 47
fib:
// calculate the nth fibonacci number, caching results 1 to 47 to a table
// only valid for values of n between 0 and 47 inclusive.
// a0: n
// v0: Fn
// branch to return 0 if a0 <= 0.
// the + refers to the next + label, relative to here.
// ++ would refer to the + label after that, and so on.
blez a0, +
// note that this executes even if the branch is taken,
// due to the single delay slot of this MIPS CPU.
// pseudo-instruction clears (sets to 0) the 32-bit value of a register:
cl v0
// check if the input is within the bounds specified earlier.
// pseudo-instruction to branch if register > immediate:
bgti a0, @max_n, +
// offset the input for use with the look-up table.
// note that this executes even if the branch is taken,
// but won't break the functionality of the routine either way.
// pseudo-instruction translates into an addiu with a negated immediate:
subiu t0, a0, 1
// multiply by sizeof(word) which is 4, or 1 << 2.
sll t0, t0, 2
// load the value from the look-up table.
// pseudo-instruction utilizing addressing modes:
lw t9, fib_cache(t0)
// branch to return the look-up value if it's non-zero, meaning it has been cached.
bnez t9, +
// once again, note that this is the delay slot of the branch instruction.
// pseudo-instruction to copy the 32-bit value of one register to another:
mov v0, t9
// set up the following loop to calculate the fibonacci number.
// pseudo-instruction to load a 32-bit value into a register:
li t1, 0 // F(0)
li t2, 1 // F(1)
-: // here's a - label referred to later.
// - labels are like + labels, except
// they look upwards in the file instead of downwards.
// calculate the next fibonacci number.
addu t3, t1, t2
// push the previous values back, part 1.
mov t1, t2
// iterate to the next number.
subiu a0, a0, 1
// loop if it hasn't yet reached the nth fibonacci number.
bnez a0, -
// push the previous values back, part 2.
// this is put in the branch delay as a simple optimization.
mov t2, t3
// loop finished, copy the result to return.
mov v0, t1
// cache the result for next time.
// pseudo-instruction not unlike the previous lw:
sw v0, fib_cache(t0)
// here's the + label used at the start of the routine.
+:
// return to the function that called this routine.
// when jr is given without any arguments, `jr ra` is implied.
jr
// there's nothing to do in the delay slot, so don't do anything.
// this is necessary, otherwise the next instruction or data
// following the routine would be executed.
// pseudo-instruction to do nothing:
nop
// set up initial values in the look-up table.
fib_cache:
// lips doesn't yet have a way to specify "x, n times",
// so this will do for now.
.word 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
.word 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
.word 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
.word 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
.word 0, 0, 0, 0, 0, 0, 0

3
example.lua Normal file
View file

@ -0,0 +1,3 @@
package.path = package.path..";./?/init.lua"
local lips = require "lips"
lips('example.asm')