Getting started
Write PowerPC in the editor and hit Assemble (or Ctrl+Enter). The assembled words show on the right; pick a shape from the format dropdown: hex, raw bytes, C#/C arrays, RPCS3 patch.yml, or NetCheat. Save a file with Ctrl+S.
li r3, 0x1blr
Addresses & hooks
address sets where the following words live in memory. The address-based output (RPCS3, NetCheat, hex blocks) needs it. hook writes a branch at an address into the next block.
address 0x00A5EA30li r3, 0blr
Register aliases
Name a register so you can read the code instead of the number. set binds a name, unset drops one, clearset clears them all.
set r31, baselwz r3, 0x10(base)unset base
Structs
A struct names a memory layout. Fields lay out by their type size (or an explicit @ offset). Give it an @ address and field values and it also emits that data at the address, zeroing any unset fields.
struct Player @ 0x016C7C30 {name : string[16]health : f32 = 150ammo : i32}
To read fields in code, get the base into a register. Use loadstruct rX, Type(loads the struct's address), or set rX, name as Type to type a pointer a caller already gave you. Then Type.field resolves to offset(rX).
loadstruct r3, Playerlfs f1, Player.health // -> lfs f1, 0x10(r3)lbz r4, Player.ammo
Fields align naturally by default, matching how a game struct was compiled, so overlaying an existing structure lines up for free. Add packed after the name or address to lay fields end to end with no padding, for data you own. An explicit @ offset still wins either way.
struct SaveHeader @ 0x100 packed {version : u8 // 0x0flags : u8 // 0x1score : i32 // 0x2, not padded to 0x4}
Functions
func (or sub) declares a function. Header params bind register names with the same grammar as struct fields, name : Type @ register. So the params double as the function's contract, and player.field resolves in the body. Params can also be written as param lines at the top of the function.
func applyDamage: player: Player @ r3, amount: i32 @ r4lfs f1, player.healthfsubs f1, f1, f2.clamp:fcmpu cr0, f1, f2bge .store.store:stfs f1, player.healthblr
A leading dot marks a local label (.clamp, .store), scoped to its function, so you can reuse .loop in every func. global name: Type @ rX binds a name visible in every function.
global save: SaveData @ r31func a:lwz r3, save.goldblr
Imports
importpulls another saved file's exports (struct types, register aliases, and function addresses) into the current one. It's transitive, and it never copies code bytes.
import combatbl applyDamage
A file is referenced by its name, not its folder, so import combat works wherever combat sits in the Files rail. Start typing after import and your saved files autocomplete.
Debugging with traps
A trap halts the CPU where it fires, so you can drop one into game code and read the register state when it hits (in the RPCS3 debugger, or a crash log). trap (or a bare tw) is unconditional. To fire only on a condition, compare two registers with tweq / twne / twlt / twgt / twle / twge, or a register against a constant with the …i forms.
trap // stop here, alwaystweq r3, r4 // stop if r3 == r4tweqi r0, 10 // stop if r0 == 10
The reference lists every form. The raw tw TO, rA, rBtakes a 5-bit condition mask (16 lt, 8 gt, 4 eq, 2 ltu, 1 gtu, added together) for a combination the aliases don't cover.
Disassembler
Open Disassemble hex in the Tools list. Paste big-endian words almost any way (plain, 0x/\\x/$ prefixes, byte streams, objdump or hexdump output, memory dumps with addresses) and it strips the noise. Set a base address to get PC-relative branches right, then Promote to file to drop the result into the editor.