• Our software update is now concluded. You will need to reset your password to log in. In order to do this, you will have to click "Log in" in the top right corner and then "Forgot your password?".
  • Welcome to PokéCommunity! Register now and join one of the best fan communities on the 'net to talk Pokémon and more! We are not affiliated with The Pokémon Company or Nintendo.

Assembly Tutorial.

Full Metal

C(++) Developer.
810
Posts
16
Years

  • [a id]intro[/a id]Intro.
    Before you clog-up this board with annoying questions, I would ask that you read ( and fully comprehend ) this thread. The purpose of this tutorial is to give you a relatively in-depth look at Assembly (Thumb assembly, specifically ) as well as some essential concepts for ROM hacking. I will be the first to say that I'm not the best, and there may be some minor ( or major ) errors in my examples or instructions. If you spot one, do not hesitate to tell me, and I will fix it ASAP. Since this is such a broad-subject, I do not know if it will fit on one post ( I have ran into this problem before, believe it or not ) So eventually, the first post may turn into an index post, and the tutorial will be split up into multiple posts throughout this thread. A large contributor of this information is gbaTek, and is a great resource for any thumb or GBA programmer / hacker.


    [A ID]syn[/A ID]Syntax.
    Assembly syntax is pretty simple and strait forward.
    [command][space][argument1]([comma][argument2]([comma][argument3]))
    ADD rN,rX ; rN += rX;
    Comments however, aren't always consistent. Some like you to use a semicolon to notate a comment, others use '@', and some ( certain gcc builds targeting thumb architecture, like devkitpro ) even allow you to use C-style comments. A command is often referred to as an 'opcode'.


    [A ID]stack[/A ID]The Stack.
    The stack is a LIFO( Last In First Out ) memory management system. LIFO is pretty self explanatory. The last value in will always be the first value out. In thumb assembly, you can put a value into the stack via the 'push' command. To take a value out of the stack ( and store it in a register ) you use 'pop'. But before we look at this, let's look at some registers( DWORD-sized data holders for the processors use ). R13 is the stack pointer. Whenever push is called, the value is stored, and the stack pointer increments by 4 bytes. Basically this means that push is equivelant to:
    str rN,[r13] ; We will cover str and add commands later.
    add r13,4 ; Mathematics commands will be covered as well ( ... )
    However, it's better to just use push/pop syntax.
    To use Push and Pop are special commands with a different syntax.
    push { rX }
    and
    pop { rX }.
    The curly-braces notate a list from my understanding.
    In other words...
    push{rx,ry,rz} is perfectly exceptable.
    as well as
    push{rx-rz}
    or even
    push{rx-rz,rA}
    One thing you want to remember is that you always pop in the reverse order that you push. Consider this:

    push {lr}
    push {r0}
    pop {pc}
    pop {r0}

    That looks fine 'n dandy to the un-trained eye, sure. But what happens here? Let's observe. First, lr ( pointer to the code section to go to when we're done here ) is pushed onto the stack. Then the contents of r0 are placed ontop. Now. R0 is on the top of the stack, and LR is just below it. ( or a copy of their contents-- rather. ) When you pop{pc} you're placing the old contents of r0 onto pc. Now. PC is a special variable. When you change PC's value, the processor interprets it as a 'function pointer' and goes to that address then executes the data there as though it were thumb code. This is a very useful feature, but also a very dangerous thing for push/pop mis-alignments. So, unless r0 contains the address of an actual routine, your routine just failed miserably. PLUS, I would like to point out that the second pop is never reached because PC caused the processor to go elsewhere. Now let's see the fixed routine snippet:

    push {lr}
    push {r0}
    pop {r0}
    pop {pc}

    Now notice- It's okay to use different registers in your push/pop commands. However, you as a programmer ( yes, assembly IS a programming language, believe it or not. :P ) have to be aware of the effects of your pushing and popping.





    [A ID]PCLR[/A ID]The Program Counter and Link Register.
    The processor has a special way of deciding which instruction to process next. This is the 'program counter' or 'pc'. This is register 15 ( r15 ). At any given time pc will be equal to the address of the code being executed, plus two. Then the processor reads pc and adds two to it, then executes the code previously at pc ( Not necessarily in that order though ). Having access to this register allows you to control program flow. you could always
    mov pc,#0xADDRESSTOGOTO ; <- only if the address+1 <= 0x7F
    or add pc,#(numberOfInstructionsToSkip * 2) + 1.
    But there are built-in commands to do this, and it's probably better to use those.
    LinkRegister will be explained in the branching section.


    [A ID]equals[/A ID]Setting Values.

    To set a register to a specific value, there are a few main-stream methods.
    ONE - Using memory access functions
    TWO - The "MOV" command.
    THREE - Using a command like "ADD","MUL","ASL","ASR" followed by the value and an appropriate value that would result in the original value.
    the syntax for "MOV" is as follows:
    mov rn,#VALUE;

    Bit Expl.
    15-13 Must be 001b for this type of instructions
    12-11 Opcode
    00b: MOV Rd,#nn ;move Rd = #nn
    01b: CMP Rd,#nn ;compare Void = Rd - #nn
    10b: ADD Rd,#nn ;add Rd = Rd + #nn
    11b: SUB Rd,#nn ;subtract Rd = Rd - #nn
    10-8 Rd - Destination Register (R0..R7)
    7-0 nn - Unsigned Immediate (0-255)


    Memory Access functions start with 'ld' or 'st'. 'ldr' - LoaD Register; 'str' - STore Register. "str" and "ldr" by default store the entire register (32 bits). But if you append an 'h' to the command, you get 'ldrh' or 'strh' for LoaD/STore Register Half-Word (16 bits). OR you could append a 'b' for a single BYTE.

    Bit Expl.
    15-12 Must be 0101b for this type of instructions
    11-10 Opcode (0-3)
    0: STR Rd,[Rb,Ro] ;store 32bit data WORD[Rb+Ro] = Rd
    1: STRB Rd,[Rb,Ro] ;store 8bit data BYTE[Rb+Ro] = Rd
    2: LDR Rd,[Rb,Ro] ;load 32bit data Rd = WORD[Rb+Ro]
    3: LDRB Rd,[Rb,Ro] ;load 8bit data Rd = BYTE[Rb+Ro]
    9 Must be zero (0) for this type of instructions
    8-6 Ro - Offset Register (R0..R7)
    5-3 Rb - Base Register (R0..R7)
    2-0 Rd - Source/Destination Register (R0..R7)



    [A ID]math[/A ID]Mathematics.

    Mathematics are a necessity for assembly. Basics are supported, like multiply, add, and subtract. However, because decimals are not supported by GBA, division isn't present ( I'll show a workaround -- don't worry -- ).
    Addition, Subtraction, Multiplication,XOR-ing,OR-ing,AND-ing,etc:
    Addition - ADD
    Subtraction - SUB
    Multiplication - MUL
    XOR - XOR
    OR - ORR
    AND - AND
    ^ command names.
    ADD rX,Z
    rX = rX + Z, where Z can be either a constant value, or a register.
    ADD rX,rA,C
    rX = rA + C, where C can be either a constant value or a register.
    Division is a little bit tricky. You can divide by any power of two, however.
    x << N-1 = X * 2N
    So, inversely that means this, right?
    X >> N-1 = X / 2N


    [A ID]logic[/A ID]Logic And Branching.

    When you go to the store, you usually intend to buy something. Unless you don't have any money, then you want to go and drool at the merchandise while your wife and kids nag at you to leave the store. Let's put this in terms of a routine. purchasing is RTPURCHASE, and drooling is RTDROOL.
    Wether or not you have money is stored in r0. A value of 0 is false ( no money ) and a value of not 0 is true ( You have money ).

    <...>
    cmp r0,#0
    beq RTDROOL
    RTPURCHASE:
    <...>

    gbaTek has the different branching commands explained well.

    Bit Expl.
    15-12 Must be 1101b for this type of instructions
    11-8 Opcode/Condition (0-Fh)
    0: BEQ label ;Z=1 ;equal (zero)
    1: BNE label ;Z=0 ;not equal (nonzero)
    2: BCS label ;C=1 ;unsigned higher or same (carry set)
    3: BCC label ;C=0 ;unsigned lower (carry cleared)
    4: BMI label ;N=1 ;negative (minus)
    5: BPL label ;N=0 ;positive or zero (plus)
    6: BVS label ;V=1 ;overflow (V set)
    7: BVC label ;V=0 ;no overflow (V cleared)
    8: BHI label ;C=1 and Z=0 ;unsigned higher
    9: BLS label ;C=0 or Z=1 ;unsigned lower or same
    A: BGE label ;N=V ;greater or equal
    B: BLT label ;N<>V ;less than
    C: BGT label ;Z=0 and N=V ;greater than
    D: BLE label ;Z=1 or N<>V ;less or equal
    E: Undefined, should not be used
    F: Reserved for SWI instruction (see SWI opcode)
    7-0 Signed Offset, step 2 ($+4-256..$+4+254)


    Returning is a very crucial function in programming. It allows you to go back to where you were before reaching a chunk of code. Usually this is called "branch-with-link" or "BL". What this does is store PC+2 ( r15 ) into LR ( r14 ) and THEN branches to the section you've specified. If you've read Hackmew's tutorial on ASM, then you might notice that he did something like this:

    push{lr}
    <...>
    pop{pc}

    That pops lr ( the previous value of PC + 2 ) and then the routine executes. Finally, the value is pushed onto PC and the code there is executed. Effectively creating a "return" statement.


    [A ID]dbg[/A ID]Debugging and Basic Hacking.

    First off, you will need a good debugger. For this tutorial, I will explain things in terms of VBA-SDL-H. Google search for the program, it should be on the first two pages and Zophar's domain. I'm not sure if I'm the only one who has this problem or not but...whenever you close the program, make sure the game isn't frozen or paused, otherwise you need to re-download your SDL.dll and the program will crash. Anyways, here's the *real* basics on using the program:
    GUI:
    F11 - Enter debugging mode.
    Ctrl+P - Pause the game.
    Shift+F(1-9) - Save state into slots 1-9 appropriately.
    F(1-9) - Load state from slots 1-9 appropriately.
    Debug:
    c - continue
    n - next
    h - help
    bt - breakPoint on Thumb
    bpr - breakpoint on read
    bpw - breakPoint on write
    trace [ options ] - tracing functions.

    Another thing you would want to have for hacking is plain old VBA. Things in the tool menu and the cheat search are very useful.
    How to use cheat search:
    Open the dialog via menus.
    Check the box for 'specific value'
    Decide the size of the data you're looking for, and enter your value.
    Click search, a list of offsets and values should appear.
    Click 'ok'. Change the value in-game, then open the dialog again.
    Enter a new value, and click search again. Repeat as necessary until there is only one value listed( If your using a game like FireRed or Emerald, I would write the offset down, and then do a search for a pointer to that offset, because of DRM -- if there is a pointer found, go ahead and write *that* offset down, because it will be much more useful.

    Break-ing on read/write:
    Open your game with vba-sdl-h.
    Enter Debug mode.
    type
    "BPR " -- don't push enter just yet though.
    Breaking on read/write is useful when you know the location of something, and you want to find when it's being modified, and how. There are two things you need to know in order to break on read or write: the size, and location of the item.
    now. type in the location of your item, followed by a space, and the size. Be careful, however. VBA-SDL-H uses decimal by default. If you are entering a hex number, make sure that you have '0' for the first digit. Also -- ensure you have the right location for addresses. 08,09, etc. VBA doesn't assume ROM area searching, because it also supports breaking on a run-time value[ as opposed to a built-in constant of the ROM. ]
    OR type "BPW" -- this is break point on WRITE. this can be used to find where a value is being modified.
    BPC - followed by an index number clears a breakpoint. Useful if you set a breakpoint that becomes annoying.
    BT - followed by an address of a thumb routine. Causes a breakpoint when that instruction is executed. ( well, immediately after, atleast. )
    Now for those who aren't familiar with a typical development environment might be wondering what a "breakpoint" is. A breakpoint is simply what a debugger implements to pause execution after a specific event occurs ( For example reading or writing a value, or execution of a specific instruction. )
    Once you've hit a breakpoint, you can do a few things. Usually you want to watch the registers, and enter 'n' until something interesting happens. ( eg you see the a branching instruction )
    Every cycle, you will see a table of all the registers, and their contents. THEN you see 3 lines. The first line is the previous instruction executed. The next line is indented slightly, and has a '>' on it. This is the current instruction. When you enter 'n' again, this will be executed, and then a new cycle starts. Finally, you see the next instruction.
    If the debug environment doesn't suit your fancy, here's something I like to do, to figure out what a certain routine does. First I set a breakpoint, and activate it. THEN a use the command 'dt ' ( decompile thumb ) followed by the address of the currently executed instruction, minus 10. I continue this until I see something like "push {lr}", as that usually notates the beginning of a function. Then I use the command 'dt' to find the end of the routine ( usually a pop{pc} or mov pc. ). Once I've found the beginning and ending of the routine, I [ once again, I know it's slightly repetitive. haha. ] use "dt" to dis-assemble everything from the beginning to the end, and copy & paste it into notepad. Then I start observing things, and values. Taking note of what happens to certain registers, adding comments, and such until I figure out just what exactly is happening. THEN I think about how this makes sense according to what the breakpoint was set for. ( Like a pokemon's health, a level counter, etc. ). It's a lot to take in, I know. Just play around with VBA-SDL-H and you'll get it eventually. (:

    Now. VBA SDL H isn't the only useful VBA version. Regular VBA is also very useful. One of the most awesome features, in my opinion is the cheat search. There are lots of tutorials, and I plan on adding a tid-bit on this on how later, but for now your assignment is to look it up, and find other useful things of regular VBA.(:

     
    Last edited:
    Back
    Top