Home

Awesome

lips

An assembler for the MIPS R4300i processor, written in Lua.

This is not a 'true' assembler; it won't produce executable binary files. This was intended to assist in hacking N64 games. 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

local lips = require "lips.init"

You can then use it as such: example.luaexample.asm

By default, lips will print the assembled word values in hex:

18800017
00001025
2401002F
10810002
0081082A
10200012
2488FFFF
00084080
etc...

Other predefined output formats are available in lips.writers; you can instantiate one and pass it through the second argument of lips.

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 written for the Nintendo 64 Zelda games.

Syntax

lips uses a derivative of CajeASM's syntax. It takes a couple of notes from other assemblers as well.

A run-down of various syntax elements:

// this is a comment.
/* this is a block comment */
; this is a more traditional assembly style of comment.
; we'll be using this so github's syntax highlighting doesn't blow up.

; set a variable. these are inlined in the preprocessing stage.
[my_const]: 0xDEADBEEF
; we can then use it in instructions by adding 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
; instructions may end in an extra comma;
; this may make mass-entry or generation of instructions easier.
li  a0, @my_const,

; instruction/register names are case-insensitive, as are hex digits.
    LI      A0, @my_const
    LuI     a0, 0xDeAd
; however, note that the 'x' in "0x" must be lowercase.
; the same applies for 0b and 0o for binary and octal, respectively.

; 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 a 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

; numbers may be written in eight different ways: two for each base.
    li      t1, 0xDEADBEEF
    li      t2, $DEADBEEF
    li      t3, %11011110101011011011111011101111
    li      t4, 0b11011110101011011011111011101111
    li      t5, 0o33653337357
    li      t6, 033653337357
    li      t7, 3735928559
    li      t8, #3735928559

; though, as a stylistic choice, the preferred way of writing them are as such:
    li      t1, 0xDEADBEEF
    li      t4, 0b11011110101011011011111011101111
    li      t5, 0o33653337357
    li      t7, 3735928559

.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)
    sw      s2, label+4
    sw      s3, label+4(s0)

; relative labels are borrowed from asw, except ours require a suffixing colon:
-:              ; #1
    b       ++  ; branches to #3
    nop
+:
-:              ; #2
    b       --  ; branches to #1
    nop
+:              ; #3
    b       -   ; branches to #2
    nop

; TODO: more examples!

Instructions

Instructions were primarily referenced from the N64 Toolkit: Opcodes and the bass assembler.

An in-depth look at instructions for MIPS IV processors is given by the MIPS IV Instruction Set manual. Most of this applies to our MIPS III architecture.

The MIPS64 Instruction Set manual is sometimes useful. Much of it doesn't apply to our older MIPS III architecture, but it's a little cleaner than the older manuals.

Last, but not least, the R4300i datasheet covers some of the nuances of the processor.

Unimplemented Instructions

As far as I know, all native R4300i instructions have been implemented. Whether or not they output the proper machine code is another thing.

Unimplemented Pseudo-Instructions

Registers

In order of numerical value, with intended usage:

Index     Random    EntryLo0  EntryLo1
Context   PageMask  Wired     Reserved0
BadVAddr  Count     EntryHi   Compare
Status    Cause     EPC       PRevID
Config    LLAddr    WatchLo   WatchHi
XContext  Reserved1 Reserved2 Reserved3
Reserved4 Reserved5 PErr      CacheErr
TagLo     TagHi     ErrorEPC  Reserved6

Directives

butts:  HEX {
    F0 0D
    DE AD BE EF
}
.align

Unimplemented