# Project64 JS API

Project64 Javascript API
mem
mem.u8|u16|u32|s8|s16|s32|float|double
Arrays for reading and modifying values in virtual memory. Virtual addresses are always used for indeces regardless of type size.

    var addr_power = 0x8033B21E

    if(mem.u8[addr_power] < 1)
    {
        mem.u8[addr_power] = 8
    }

mem.getblock(baseAddr, size)
Returns a Buffer object from a block of data of size bytes at baseAddr in virtual memory.
mem.getstring(baseAddr[, maxLen])
Returns a string from a zero-terminated ASCII string at baseAddr in virtual memory.
mem.bindvar(obj, baseAddr, name, type)
Adds property name to obj and "binds" it to the virtual address specified by baseAddr. Valid types are: u8, u16, u32, s8, s16, s32, float, and double.

    mem.bindvar(this, 0x8033B21E, 'power', u8)

    if(power < 1)
    {
        power = 8
    }

mem.bindvars(obj, vars)
Adds multiple virtual memory-bound properties to obj. Returns obj.

    var mario = mem.bindvars({},
    [
        [0x8033B1B0, 'y', float],
        [0x8033B21E, 'power', u8]
    ])

    mario.power = 5
    mario.y = 500.00

mem.bindstruct(obj, baseAddr, props)
Adds multiple virtual memory-bound properties to obj. Addresses are determined by type sizes. Returns obj.

    var marioPos = mem.bindstruct(this, 0x8033B1AC,
    {
        x: float,
        y: float,
        z: float
    })

mem.typedef(props)
Returns a "struct" class that may be constructed using the address of a real struct in virtual memory.

    const Player = mem.typedef(
    {
        health: u32,
        x: float,
        y: float,
        z: float
    })

    Player.prototype.move = function(x, y, z)
    {
        this.x = x
        this.y = y
        this.z = z
    }

    Player.prototype.heal = function()
    {
        this.health = 100
    }

    var player = new Player(0x8033B1AC)

    player.move(100, 200, 300)
    player.heal()

rom
rom.u8|u16|u32|s8|s16|s32|float|double
Arrays for reading values in cartridge ROM. Indexing works in a similar manner to mem's.

    var crc1 = rom.u32[0x00000010]
    var crc2 = rom.u32[0x00000014]

rom.getblock(baseAddr, size)
Returns a Buffer object from a block of data of size bytes at baseAddr in cartridge ROM.
rom.getstring(baseAddr[, maxLen])
Returns a string from a zero-terminated ASCII string at baseAddr in cartridge ROM.

    var romName = rom.getstring(0x00000020)
    console.log('Internal ROM name: ' + romName)

events
emulation thread interpreter mode only
events.onexec(address, callback)
Adds a CPU execution callback for a virtual address or AddressRange and returns a callback ID. callback will be invoked at the beginning of a CPU step if the program counter is at address. callback receives the program counter address at which the event is fired.

    events.onexec(0x802CB1C0, function()
    {
        console.log('CPU is calling 0x802CB1C0')
    })

    events.onexec(ADDR_ANY, function(pc))
    {
        // Log every step!
        console.log('CPU is executing 0x' + pc.hex())
    })

emulation thread interpreter mode only
events.onread(address, callback)
Adds a CPU read callback for a virtual address or AddressRange and returns a callback ID. callback will be invoked at the beginning of a CPU step if the current instruction is going to read from address. callback receives the virtual address that the CPU is going to read.

    events.onread(0x8033B1B0, function()
    {
        console.log('CPU is reading 0x8033B1B0')
    })

    events.onread(ADDR_ANY_CART_ROM_UNC, function(addr)
    {
        console.log('CPU is reading ROM 0x' + addr.hex())
    })

emulation thread interpreter mode only
events.onwrite(address, callback)
Adds a CPU write callback for a virtual address or AddressRange and returns a callback ID. callback will be invoked at the beginning of a CPU step if the current instruction is going to write to address. callback receives the virtual address that the CPU is going to write to.

    events.onwrite(0x8033B1B0, function()
    {
        console.log('CPU is modifying 0x8033B1B0')
    })

    events.onwrite(ADDR_ANY_CART_ROM_UNC, function(addr)
    {
        console.log(gpr.pc.hex() + ': wrote to cartridge ' + addr.hex())
    })

emulation thread interpreter mode only
events.onopcode(address, opcode, callback)
Adds a CPU execution callback for a virtual address or AddressRange and returns a callback ID. callback will be invoked at the beginning of a CPU step if the program counter is at address and opcode is equal to the opcode to be executed. callback receives the program counter address at which the event is fired.

    const JR_RA = 0x03E00008 // 'jr ra' command

    events.onopcode(ADDR_ANY, JR_RA, function(pc)
    {
        console.log(pc.hex()) // log pc at every 'jr ra'
    })

emulation thread interpreter mode only
events.onopcode(address, opcode, mask, callback)
Adds a CPU execution callback for a virtual address or AddressRange and returns a callback ID. callback will be invoked at the beginning of a CPU step if the program counter is at address and opcode is equal to the opcode to be executed ANDed with mask. callback receives the program counter address at which the event is fired.

    const ADDIU_SP_SP = 0x27BD0000 // 'addiu sp, sp, 0x0000'
    const NO_IMM16 = 0xFFFF0000 // mask off immediate field

    events.onopcode(ADDR_ANY, ADDIU_SP_SP, NO_IMM16, function(pc)
    {
        // log pc at every 'addiu sp, sp, x' regardless of the immediate value
        console.log(pc.hex())
    })

    const JAL = 0x0C000000 // 'jal 0x00000000'
    const NO_TARGET = 0xFC000000 // mask off target field

    events.onopcode(ADDR_ANY, JAL, NO_TARGET, function(pc)
    {
        // log pc at every 'jal' regardless of the target address
        console.log(pc.hex())
    })

emulation thread interpreter mode only
events.ongprvalue(address, registers, value, callback)
Adds a CPU execution callback for a virtual address or AddressRange and returns a callback ID. callback will be invoked at the beginning of a CPU step if the program counter is at address and the lower 32 bits of the general purpose registers specified by registers are equal to value. callback receives the program counter address at which the event is fired, and the index of the first register that contains the value.

    events.ongprvalue(ADDR_ANY, GPR_ANY, 0x49533634, function(pc, reg)
    {
        // log pc whenever any general purpose register contains 0x49533634
        console.log(pc.hex(), asm.gprname(reg))
    })

    events.ongprvalue(ADDR_ANY, GPR_A0 | GPR_A1, 0x00001234, function(pc, reg)
    {
        // log pc whenever A0 or A1 contains 0x00001234
        console.log(pc.hex(), asm.gprname(reg))
    })

Valid registers:

    GPR_R0 GPR_AT GPR_V0 GPR_V1 GPR_A0 GPR_A1 GPR_A2 GPR_A3
    GPR_T0 GPR_T1 GPR_T2 GPR_T3 GPR_T4 GPR_T5 GPR_T6 GPR_T7
    GPR_S0 GPR_S1 GPR_S2 GPR_S3 GPR_S4 GPR_S5 GPR_S6 GPR_S7
    GPR_T8 GPR_T9 GPR_K0 GPR_K1 GPR_GP GPR_SP GPR_FP GPR_RA

    GPR_ANY_ARG  -- Any of the 'A' registers
    GPR_ANY_TEMP -- Any of the 'T' registers
    GPR_ANY_SAVE -- Any of the 'S' registers
    GPR_ANY      -- Any register

emulation thread
events.ondraw(callback)
Adds a callback which will be invoked immediately after Project64 requests a screen update from the graphics plugin. Returns a callback ID.

    events.ondraw(function()
    {
        console.log('Frame drawn')
    })

events.remove(callbackId)
Removes a registered callback by its ID.

    var callbackId = events.onexec(0x80000180, function()
    {
        // Only invoked once
        console.log('Interrupt fired')
        events.remove(callbackId)
    })

debug
debug.breakhere()
Pauses emulation and opens the debugger window. Useful for creating conditional breakpoints.

    events.onexec(0x802CB1C0, function()
    {
        if(gpr.a0 == 0)
        {
            console.log('0 passed to 0x802CB1C0, breaking')
            debug.breakhere()
        }
    })

asm
asm.gprname(regIndex)
Returns the name of the general purpose register specified by regIndex.

    asm.gprname(4) // 'a0'

fs
fs.open(path, mode)
Opens the file pointed to by path in the mode specified by mode. Returns a file descriptor on success or false if the operation failed.

    var fd = fs.open('log.txt', 'w')

Valid modes:

    r or rb           Open file for reading.
    w or wb           Truncate to zero length or create file for writing.
    a or ab           Append; open or create file for writing at end-of-file.
    r+ or rb+ or r+b  Open file for update (reading and writing).
    w+ or wb+ or w+b  Truncate to zero length or create file for update.
    a+ or ab+ or a+b  Append; open or create file for update, writing at end-of-file.

    http://pubs.opengroup.org/onlinepubs/009695399/functions/fopen.html

fs.close(fd)
Closes the file referenced by fd.
fs.write(fd, buffer[, offset[, length[, position]]])
Writes buffer to the file referenced by fd. Returns the number of bytes written.

    var fd = fs.open('log.txt', 'w')
    fs.write(fd, 'Hello world!\n')
    fs.close(fd)

    var buf = mem.getblock(0x8033B400, 4 * 32)
    var fd = fs.open('data.bin', 'wb')
    fs.write(fd, buf)
    fs.close(fd)

fs.writeFile(path, buffer)
Writes buffer to the file pointed to by path. Returns true if the operation was successful or false if the operation failed.
fs.read(fd, buffer, offset, length, position)
Reads the file referenced by fd into buffer. Returns the number of bytes read.

    var buf = new Buffer(128)
    var fd = fs.open('data.bin', 'rb')
    var nBytesRead = fs.read(fd, buf, 0, buf.length, 0)
    fs.close(fd)

fs.readFile(path)
Reads the file pointed to by path. Returns a Buffer object representing the file, or false if the operation failed.
fs.fstat(fd)
Returns an fs.Stats object describing the file referenced by fd.

    var stats = fs.fstat(fd)
    console.log('size: ' + stats.size)

fs.stat(path)
Returns an fs.Stats object describing the file or directory pointed to by path. Returns false if the operation failed.
fs.unlink(path)
Deletes a file. Returns true if the operation was successful.
fs.mkdir(path)
Creates a directory. Returns true if the operation was successful.
fs.rmdir(path)
Removes a directory. The directory must be empty. Returns true if the operation was successful.
fs.readdir(path)
Returns an array of file names in the directory pointed to by path. Returns false if the operation failed.
fs.Stats

stats.dev | ID of the device the file resides on
stats.ino | inode number
stats.mode | File permissions
stats.nlink | Number of links to the file
stats.uid | User ID
stats.gid | Group ID
stats.rdev | Device ID (if file is character or block special)
stats.size | Size of the file in bytes
stats.atimeMs | Last access timestamp in milliseconds
stats.mtimeMs | Last modification timestamp in milliseconds
stats.ctimeMs | Creation timestamp in milliseconds
stats.atime | JS Date object representing the last access time
stats.mtime | JS Date object representing the last modification time
stats.ctime | JS Date object representing the creation time

stats.isDirectory()
Returns true if the fs.Stats object describes a directory.
stats.isFile()
Returns true if the fs.Stats object describes a regular file.
console
console.print(text)
Prints text to the script console.

    console.print('Hello world\n')

console.log(text[, text2, ...])
Concatenates all provided text arguments with spaces and prints the result to the script console with a trailing newline.

    console.log('Hello', 'world')

console.clear()
Clears all previously printed text from the script console.
alert
alert(message[, caption])
Shows a message box with an optional caption. The calling thread will be blocked until the message box is dismissed.

    alert('Hello world') // Blocks the script's thread

    events.onexec(0x80000180, function()
    {
        alert('Interrupt fired!') // Blocks the emulation thread
    })

screen
screen.print(x, y, text)
Prints text to the screen at the provided x and y coordinates. Should be called from an events.ondraw callback. (Unstable!)

    events.ondraw(function()
    {
        screen.print(20, 20, 'power: ' + mem.u8[0x8033B21E])
    })

gpr
gpr.r0|at|v0|v1|a0 ...
gpr[0|1|2 ...]
Variables representing the lower 32 bits of each general purpose register.

    events.onexec(0x802CB1C0, function()
    {
        if(gpr.a0 == 2)
        {
            gpr.a0 = 3
        }
    })

gpr.pc
Variable representing the CPU's program counter.
gpr.hi
Variable representing the lower 32 bits of the HI register.
gpr.lo
Variable representing the lower 32 bits of the LO register.
ugpr
ugpr.r0|at|v0|v1|a0 ...
ugpr[0|1|2 ...]
Variables representing the upper 32 bits of each general purpose register.
ugpr.hi
Variable representing the upper 32 bits of the HI register.
ugpr.lo
Variable representing the upper 32 bits of the LO register.
fpr
fpr.f0|f1|f2|f3|f4 ...
fpr[0|1|2 ...]
Variables representing the 32-bit floating point registers.

    events.onexec(0x802CB1C0, function()
    {
        if(gpr.f0 == 2.0)
        {
            gpr.f0 = 3.0
        }
    })

dfpr
dfpr.f0|f2|f4|f6 ...
dfpr[0|2|4 ...]
Variables representing the 64-bit floating point registers.
cop0
cop0.cause
Variable representing the Cause register. Updates interrupts when written.
Server
new Server(settings)
Creates a new server socket. If port is provided in settings, the server will start listening immediately.

    var server = new Server({port: 80})

server.listen(port)
Binds the server socket to a port and starts listening.
server.on('connection', callback)
Starts accepting clients. When a client is accepted, a Socket object for it is created and passed to callback.

    server.on('connection', function(socket)
    {
        socket.on('data', function(data)
        {
            socket.write('hello')
        })
    })

Socket
new Socket([fd])
Creates a new socket object.
socket.connect(settings[, callback])
Connects the socket to the host and port specified by the settings object. The default values for the host and port are "127.0.0.1" and 80 respectively.
socket.write(data[, callback])
Writes data to the socket.
socket.on('data', callback)
Starts reading data from the socket asynchronously. When data arrives, it is passed to callback as a Buffer object.
socket.on('close', callback)
AddressRange
new AddressRange(start, end)
Creates an immutable object with start and end address properties.
The following AddressRange objects are defined globally:

    ADDR_ANY              0x00000000 : 0xFFFFFFFF Any 32-bit address

    ADDR_ANY_KUSEG        0x00000000 : 0x7FFFFFFF  MIPS user mode TLB mapped segment
    ADDR_ANY_KSEG0        0x80000000 : 0x9FFFFFFF  MIPS cached unmapped segment
    ADDR_ANY_KSEG1        0xA0000000 : 0xBFFFFFFF  MIPS uncached unmapped segment
    ADDR_ANY_KSEG2        0xC0000000 : 0xFFFFFFFF MIPS kernel mode TLB mapped segment

    ADDR_ANY_RDRAM        0x80000000 : 0x807FFFFF Cached RDRAM
    ADDR_ANY_RDRAM_UNC    0xA0000000 : 0xA07FFFFF Uncached RDRAM

    ADDR_ANY_CART_ROM     0x90000000 : 0x95FFFFFF Cached cartridge ROM
    ADDR_ANY_CART_ROM_UNC 0xB0000000 : 0xB5FFFFFF Uncached cartridge ROM

Number
number.hex([nChars])
Returns a hexadecimal string representation of the number object. The resulting string is prepended with zeroes until its character length meets nChars or 8 by default.

    var sm64EntryPC = rom.u32[0x00000008]
    console.log('Entry: ' + sm64EntryPC.hex()) // "Entry: 80246000"