From 9e4167e04abaaa290c31717e9a91404e9e45adbe Mon Sep 17 00:00:00 2001 From: Connor Olding Date: Mon, 18 Jan 2016 12:56:06 -0800 Subject: [PATCH] add something resembling documentation --- README.md | 98 ++++++++++++++++++++++++++++++++++++++++++++++++-- example.asm | 100 ++++++++++++++++++++++++++++++++++++++++++++++++++++ example.lua | 3 ++ 3 files changed, 198 insertions(+), 3 deletions(-) create mode 100644 example.asm create mode 100644 example.lua diff --git a/README.md b/README.md index bfb0483..0232493 100644 --- a/README.md +++ b/README.md @@ -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] diff --git a/example.asm b/example.asm new file mode 100644 index 0000000..d319dcf --- /dev/null +++ b/example.asm @@ -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 diff --git a/example.lua b/example.lua new file mode 100644 index 0000000..2b09976 --- /dev/null +++ b/example.lua @@ -0,0 +1,3 @@ +package.path = package.path..";./?/init.lua" +local lips = require "lips" +lips('example.asm')