CheatASM is a utility to designed to enable more readable cheatcode development for Atmosphere Cheat Codes. CheatASM provides an assembly-like syntax/language and compiles down into the proper cheatcode format used in the Atmosphere CFW. It can also act as a disassembler, converting cheatcodes back into a more readable syntax.
The project is not under active development but as new cheat opcodes are added to Atmosphere, the assembly syntax will be updated to support them.
In order to run the software your machine must have the dotnetcore 2.2+ runtime installed. To build the project you need the have the SDK installed instead.
.NET Core downloads can be found here: (https://dotnet.microsoft.com/download/dotnet-core/2.2)
If running from an official release, simply extract the archive for your architecture and run like any other commmand line utility.
For running from source, see the "Building" section below
11130000 56C04A6C 0000000A
01100000 56C04A6C 0000000A
20000000
becomes
lt.b [HEAP+0x56C04A6C], 0xA
mov.b [HEAP+R0+0x56C04A6C], 0xA
endcond
Here are some useful examples of how to use this utility (please see -h for a full listing of command line arguments)
CheatASM has 2 overall modes -d for disassembly and -a for assembly, you cannot specify both flags at the same time.
Additionally CheatASM provides a REPL using the --repl
flag in which you can test various things. the REPL accepts input until a blank line is sent, then it tries to determine if what was entered is an assembled code (and thus needs to be disassembled) or if it was assembly syntax (and thus needs to be assembled).
If no -o flag is provided CheatASM will output the result to stdout.
CheatASM -d -i 03fd1524e17a841c.txt
By providing an -o flag, the output will be writen to file
CheatASM -d -i 03fd1524e17a841c.txt -o 03fd1524e17a841c_asm.txt
CheatASM -a -i 03fd1524e17a841c_asm.txt -o 03fd1524e17a841c.txt
When specifying a directory for the -i flag a corresponding -o flag MUST be provided. If the -o flag specifies the same folder as -i the files will be overwritten.
CheatASM -d -r -i CheatsFolder -o CheatsDisassembled
CheatASM also allows for the dis/assembly of a opcode or assembly instruction directly using the -t parameter. If -o is not specified the result will be written to stdout, otherwise to the file specified by the -o parameter.
CheatASM -d -t "04010000 006C7634 0098967F"
CheatASM -a -t "mov.d [MAIN+R1+0x6C7634], 0x98967F"
The desire is for the assembly mnemonics to not be something you need to memorize or reference often. The aim is to be as intuitive as possible (assuming you have assembly exposure) and just let you write what you need. There are some general patterns that hte mnemonics follow:
command.bitwidth param1, param2
Command can be something like "mov" or "add", BitWidth is one of the following: "b", "w", "d", "q" for Byte, Word, Dword, Qword respectively.
When using the more complex addressing modes the order that items should be specified is always MemoryType -> Register -> Literal value
.
Cheat ASM allows for you to define mutable variables as well as constant variables. During assembly constant variables will have their literal value substituted, while mutable variables will be assigned a register number. The assigned register number if effectively reserved for use and the assembler will error in the event of an explicit use of a register number that is assigned to a variable.
Register assignment for variables happens per-cheat and they are assigned starting of 0xF and descending. Assignment is done at first-use.
Variables can be of the following types: .u8, .s8,.u16, .s16, .u32, .s32, .u64, .s64, .f32, .f64
where u
is unsigned, s
is signed, and f
is floating point. The number represents the bit width of the value.
Mutable variables will have an initial opcode inserted at the beginning of each cheat to initialize the register with the proper value. This means that at the start of each individual cheat the mutable variable initial value gets restored.
Here is a concrete example of how to use variables with CheatASM:
floatTest: .f32 4.83
mainOffset: .u32 const 0x1234
coinOffset: .u32 const 0x12
ten: .u32 0xA
.cheat master "Setup"
mov.d[R0 + 0x123], floatTest
mov.q R0, [MAIN + mainOffset]
.cheat "Sample"
mov.q [MAIN + 0x432], ten
mov.d[R0 + mainOffset], R7
mov.q R0, [MAIN + mainOffset]
mov.d[R0 + 0x123], floatTest
Assembles to:
[Assembled by CheatASM]
{Setup}
400F0000 00000000 409A8F5C
A4F00200 00000123
58000000 00001234
[Sample]
400F0000 00000000 0000000A
400E0000 00000000 409A8F5C
A8F00400 00000432
A4700200 00001234
58000000 00001234
A4E00200 00000123
Which disassembles to:
[Assembled by CheatASM]
{Setup}
mov.q RF, 0x409A8F5C
mov.d [R0 + 0x123], RF
mov.q R0, [MAIN + 0x1234]
[Sample]
mov.q RF, 0xA
mov.q RE, 0x409A8F5C
mov.q [MAIN + 0x432], RF
mov.d [R0 + 0x1234], R7
mov.q R0, [MAIN + 0x1234]
mov.d [R0 + 0x123], RE
Below you will find examples of every mnemonic supported by CheatASM. To find out what each opcode does, please reference the Atmosphere Cheat docs.
mov.d [MAIN + R2], 0x123
mov.q [MAIN + R2], 0x123
mov.d [MAIN + R8 + 0x7], 0x345
mov.w [HEAP + R7], 0x236
mov.b [HEAP + R7 + 0x9], 0xFE"
Compares the byte at address and enters the conditional block if it is less than value.
gt.d [MAIN + 0x123], 0x456
ge.b [HEAP + 0x123], 0x56
lt.q [MAIN + 0x123456789A], 0x4564123478
le.d [MAIN + 0x123], 0x456
eq.d [MAIN + 0x123], 0x456
ne.d [MAIN + 0x123], 0x456
...
endcond
Terminates a conditional block
endcond
loop R0, 0x23
...
endloop R0
Moves a static value into a register (bitwidth is always q)
mov.b R0, 0x1
mov R2, 0x3
mov.d R7, 0x12345678
mov.q RA, 0x1122334455667788
Move a value from memory into a register
mov.b R0, [MAIN + 0x12]
mov.d R0, [MAIN + 0x0]
mov.w R0, [MAIN]
mov.q R0, [HEAP + 0x1122334455]
mov.b R0, [R0 + 0x12]
mov.d R0, [R0 + 0x0]
mov.w R0, [R0]
mov.b [R7], 0x12
mov.w [R4], 0x12 inc
mov.d [R3 + R2], 0x12
mov.q [RB + R2], 0x1122334455667788
Supported arithmetic methods are add, sub, mul, lsh, rsh
add.b R7, 0x12
sub.w R4, 0x12
mul.d R3, 0x12
lsh.q RB, 0x11223344
rsh.d R4, 0x55667788
Supported keys are: A, B, X, Y, LSP, RSP, L, R, ZL, ZR, PLUS, MINUS, LEFT, UP, RIGHT, DOWN, LSL, LSU, LSR, LSD, RSL, RSU, RSR,RSD, SL, and SR
keycheck <key>
Supported arithmetic methods are add, sub, mul, lsh, rsh, and, or, not, xor, none
add.b R7, R3, 0x12
sub.w R4, R1, 0x12
mul.d R3, R0, 0x12
lsh.q RB, RD, 0x112233445566
rsh.d R4, R7, 0x55667788
and.b R7, R3, 0x12
or.b R7, R3, 0x12
xor.b R7, R3, 0x12
add.b R7, R3, R2
sub.w R4, R1, R2
mul.d R3, R0, R7
lsh.q RB, RD, R8
rsh.d R4, R7, R1
and.b R7, R3, R0
or.b R7, R3, R4
xor.b R7, R3, R6
not.b R7, R3
copy.b R7, R3
mov.d [R0], R1
mov.d [R9], R2 inc
mov.d [RA+ R1], R3
mov.d [RB + R1], R4 inc
mov.d [RC + 0x123], R5
mov.d [RD + 0x123], R6 inc
mov.d [MAIN + RE], R7
mov.d [MAIN + 0x123], R7
mov.d [HEAP + RF + 0x123456789], R8
gt.b R0, R1
ge.w R0, 0x123
lt.d R0, [R1]
le.q R0, [R1 + 0x123]
eq.b R0, [R1 + R2]
ne.w R0, [MAIN + 0x123]
gt.d R0, [HEAP + 0x123]
ge.q R0, [MAIN]
lt.b R0, [MAIN + R0]
save.reg 0x1, R3
load.reg R3, 0x1
clear.reg R3
clear.saved 0x1
save.regs R1
save.regs R1,R2
save.regs R0, R1,R2,R3,R4,R5,R6,R7,R8,R9,RA,RB,RC,RD,RE,RF
load.regs R1
load.regs R1,R2
load.regs R0,R1,R2,R3,R4,R5,R6,R7,R8,R9,RA,RB,RC,RD,RE,RF
clear.regs R1
clear.regs R1,R2
clear.regs R0,R1,R2,R3,R4,R5,R6,R7,R8,R9,RA,RB,RC,RD,RE,RF
Static registers are referenced by SR<hex number>
save.static SR1, R0
load.static R0, SR7
save.static SR7F, R1
load.static R6, SR7F
pause
resume
Format is log.<bit_width> 0xLogID, value
log.b 0x1, [MAIN]
log.w 0x1, [MAIN + 0x123]
log.d 0x1, [MAIN + R3]
log.q 0x1, [HEAP + 0x123]
log.b 0x1, [R2]
log.b 0x1, [R2 + R3]
log.w 0x1, [R4 + 0x123]
log.d 0x1, R7
CheatASM currently supports If/Else has a higher level mnemonic, this assembles into the proper "conditional" and "end conditional" opcodes.
Supported conditional operators are >, >=, <, <=, ==, !=
Example:
.if.b [MAIN + 0x123] == 0x3
mov r0, 0x3
.else
mov r0, 0x1
.fi
.if.b R0 == 0x1
mov r0, 0x3
.fi
.if.b R0 == R2
mov r0, 0x3
.fi
.if.b R0 == [MAIN + 0x123]
mov r0, 0x3
.fi
.if.b R0 == [MAIN + R2]
mov r0, 0x3
.fi
.if.b R0 == [R2]
mov r0, 0x3
.fi
.if.b R0 == [R2 + R3]
mov r0, 0x3
.fi
.if.b R0 == [R2 + 0x123]
mov r0, 0x3
.fi
.if.b R0 == [MAIN]
mov r0, 0x3
.fi
Assembles to:
11050000 00000123 00000003
40000000 00000000 00000003
21000000
40000000 00000000 00000001
20000000
C0150400 00000001
40000000 00000000 00000003
20000000
C0150520
40000000 00000000 00000003
20000000
C0150000 00000123
40000000 00000000 00000003
20000000
C0150102
40000000 00000000 00000003
20000000
C0150220 00000000
40000000 00000000 00000003
20000000
C0150323
40000000 00000000 00000003
20000000
C0150220 00000123
40000000 00000000 00000003
20000000
C0150000 00000000
40000000 00000000 00000003
20000000
Which disassembles to:
eq.b [MAIN + 0x123], 0x3
mov.q R0, 0x3
else
mov.q R0, 0x1
endcond
eq.b R0, 0x1
mov.q R0, 0x3
endcond
eq.b R0, R2
mov.q R0, 0x3
endcond
eq.b R0, [MAIN + 0x123]
mov.q R0, 0x3
endcond
eq.b R0, [MAIN + R2]
mov.q R0, 0x3
endcond
eq.b R0, [R2]
mov.q R0, 0x3
endcond
eq.b R0, [R2 + R3]
mov.q R0, 0x3
endcond
eq.b R0, [R2 + 0x123]
mov.q R0, 0x3
endcond
eq.b R0, [MAIN]
mov.q R0, 0x3
endcond
Instructions taken from (https://dotnet.microsoft.com/download/linux-package-manager/ubuntu18-04/sdk-2.2.104)
$ wget -q https://packages.microsoft.com/config/ubuntu/18.04/packages-microsoft-prod.deb
$ sudo dpkg -i packages-microsoft-prod.deb
$ sudo add-apt-repository universe
$ sudo apt-get install apt-transport-https
$ sudo apt-get update
$ sudo apt-get install dotnet-sdk-2.2
$ git clone https://github.com/tslater2006/CheatASM.git
$ cd CheatASM
$ dotnet publish -c Release -r linux-x64 --self-contained false
$ cd CheatASM/bin/Release/netcoreapp2.2/linux-x64/publish/
$ ./CheatASM -h
This project is licensed under the GNU Public License v2 License - see the LICENSE.md file for details