• 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.

ASM Workshop

Blah

Free supporter
1,924
Posts
11
Years
  • Introduction

    Hey, I decided to make a thread for ASM workshops and such. Hopefully we can get this thing rolling and help people who are interested in ASM hacking to learn how. In the early phases of this thread, the problems and discussion will likely be basic. However, in a couple of weeks, I hope to touch on topics of much more complication.

    We'll be running this thread in a combination of a Skype Room and thread, so posting on whichever you're comfortable with is fine.


    Some things you'll need to get the best benefit from this room:
    - Skype. Add fbi.agent7
    - VBA-SDL-H
    - IDA (I'll link you if you ask me on skype)
    - Knizz's idb (see his signature)

    So far ...


    Week One:
    Spoiler:


    Posting rules


    I'd just like to impose a few rules about posting your solutions here or on the Skype chat. Basically I don't want people openly posting their solutions on this thread and spoiling the fun for others who are still working on it. So if you've finished any of the above routines (fully or partially) and feel like sharing, go ahead and post them, but put spoiler tags.

    I'd also like posted routines to be of this format:
    Partial solution for Task 2; Week 1
    Spoiler:
    Replace the "Partial" with "Full" if your solution is complete. Outside the spoilers, you can have whatever you want that isn't solution code itself, or doesn't describe solution code.

    That's all for the rules. Oh, don't break any PC rules of course :P

    People Participating


    Mentors: FBI, ...maybe more soon?
    Students: -a lot, removed names because FBI is lazy to maintain :)
     
    Last edited:

    Blah

    Free supporter
    1,924
    Posts
    11
    Years
  • Do I need to ask permission to sign up, too? :P
    You don't need permission. You just need to know about the things I mentioned in the first post.
    ex: What is a pointer, basic hex editing, how to insert a routine, ect.

    Nope, you only need to hide your solutions as FBI said in the OP.
    Send them to his skype at fbi.agent7
    No! Please don't PM me solutions lol. I'll make a skype chat room, and you can discuss in a more irc-esque environment there. This thread is probably going to be handled exactly the same as the Skype chat, a big discussion and such.


    We're going to be starting the workshops now. Lets try task #1-2 first.

    Task #1

    1) Give the slot number of first Pokemon who isn't full HP

    First of all, each Pokemon is stored in the RAM. There's a specific data structure in which they're stored in. Here's a picture I took from Bulbapedia of how it looks like in RAM:

    ASM Workshop

    The above is obviously the structure for a single Pokemon. There will be six in your party and their data will be stored in succession. Like this

    [0x64 bytes (First Pokemon data)]
    [0x64 bytes (Second Pokemon data)]
    [0x64 bytes (Third Pokemon data)]
    [0x64 bytes (Fourth Pokemon data)]
    [0x64 bytes (Fifth Pokemon data)]
    [0x64 bytes (Sixth Pokemon data)]

    If you have less than 6 Pokemon in your party, say you have five, then the last 64 bytes will be zeros.

    Getting back to the above picture of the data structure, you'll notice that
    Code:
    Current HP	word	86
    Total HP	word	88
    The Current HP for a Pokemon and the Total HP of a Pokemon is 2 bytes long. It is also the 86-88th byte (for current HP) and 88-90th byte (for total HP).

    The Pokemon Data structure for the first Pokemon starts at 0x2024284.So if we add 86 bytes (0x56 in hex) to 0x2024284, we get the current HP of the first Pokemon at 0x20242DA. Add 2 more bytes to the address to get the total HP of course.

    Here's a screen shot of the Current HP and Total HP highlighted in RAM of my Anorith:
    ASM Workshop


    Now, we know how to get the current HP and Max HP of the first Pokemon, but what about the rest? Well, you just add 0x64 to the address 0x2024284. Since they're right beside each other and the size of each Pokemon is 0x64 bytes.

    Here's some basic starter code you can use to help ease the process:

    Code:
    .text
    .align 2
    .thumb
    .thumb_func
    
    @First of all, our routine will return 0x6 if no Pokemon is less than full HP
    @In the case that a Pokemon is found with less than full HP, we return it's slot number
    @ Slot numbers are 0 - 5. 0 = first 'mon, 1 = second 'mon, ect...
    
    main:
    	push {r0-r3, lr}
    	@first step: Load into a register the Pokemon Table
    	@The above should be one instruction only. 
    	@set slot number to zero (pick a register to be the loop counter)
    	
    loop:
    	@If slot number of the Pokemon we're looking at is < 6 continue, else branch end:
    	@Second step: Load a half-word at Pokemon table + 0x56 for current HP
    	@Third step: Load a half-word at Pokemon table + 0x58 for total HP
    	@In the case Current hp != total HP, branc to section "retVal" and write to lastresult the slot number of the current Pokemon
    	@this part of the code is reached if the Pokemon's Current HP = total HP.
    	@If current HP == Total HP either the Pokemon is full HP, or there isn't a POkemon here
    	@In the above case, we check the next Pokemon. Increment slot number and branch loop
    	
    nextMon:
    	@increment slot counter
    	@branch to loop
    	
    retVal:
    	@store in lastResult the current Pokemon's slot number
    
    end:
    	pop {r0-r3, pc}
    	
    .align 2
    
    .PokemonTable:
    	.word 0x2024284
    	
    .lastResult:
    	.word 0x20370D0

    Fill in the comments with appropriate code. If you have questions ASK! Either in the Skype room or over here.
     
    10,078
    Posts
    15
    Years
    • UK
    • Seen Oct 17, 2023
    MAGIC TASK 1
    Give the slot number of first Pokemon who isn't full HP


    Partial Solution:
    Spoiler:
     
    Last edited:

    Blah

    Free supporter
    1,924
    Posts
    11
    Years
  • MAGIC TASK 1
    Give the slot number of first Pokemon who isn't full HP


    Partial Solution:
    Spoiler:

    A few things really quickly.
    Code:
    CURRENT:
    	.word 0x2024276
    
    TOTAL:
    	.word 0x2024278

    Current HP and Total HP's offsets can be derived from the data structure offset. They're 0x56 and 0x58 bytes from the start of the structure. So if I were to convert your routine from FR -> EM for example, there's more pointers which need changing. Minor, but still a problem. Back to the routine!

    Magic's Routine /w comments:
    Spoiler:


    Pretty close Magic! There's a few things with the algorithm that can use improvement, but the gist of it is good.
     
    10,078
    Posts
    15
    Years
    • UK
    • Seen Oct 17, 2023
    MAGIC TASK 1
    Give the slot number of first Pokemon who isn't full HP


    Partial Solution/Final Solution?:
    Spoiler:
     

    Blah

    Free supporter
    1,924
    Posts
    11
    Years
  • Task #2
    2) Make the Player's name "Baka-san"

    First of all, like the Pokemon data structure the Player also has it's own data structure. Here's something from HackMew's knowledge tutorial:

    Code:
    [Name (8 bytes)] 
    [Gender (1 byte)] 
    [??? (1 byte)]
    [Trainer ID (2 bytes)]
    [Secret ID (2 bytes)]
    [Hours of play (2 bytes)]
    [Minutes (1 byte)]
    [Seconds (1 byte)]
    [Frames (1 byte)]
    [??? (1 byte)]
    [Options (2 bytes)]

    Clearly, the name is the first 8 bytes of the data structure. However, the Player's data structure is a little more peculiar than the Pokemon's data structure. This is because it's dynamic, the game uses a pseudo random algorithm which moves the location of this table to a different location every time it's accessed. That means that there isn't a static address we can use to access the player's name, like we did to access a party Pokemon's current HP.

    However, the game itself needs retrieve things like the player's name, gender, ID ect, all the time. So then, the game needs a way to keep track of where it even put this data. This offset is at 0x300500C. At 0x300500C, the game stores a pointer to where the player's data is currently located.

    You will need to go to 0x300500C, and load the 4 byte address. From there, go to that address, and insert:
    "BCD5DFD5FF" - which is Baka in hex. You'll notice that "Baka" is 4 letters, but it's taking 5 bytes. That is because every string (i.e words) in FR/LG/R/S/E are 0xFF terminated. So you need to add that "FF" ending to tell the game that this is the end of the name.

    Here's some starter code:

    Code:
    .text
    .align 2
    .thumb
    .thumb_func
    
    main:
    	push {r0-r1, lr}
    	@load the playerData symbol
    	@load 4 byte pointer from playerData
    
    	@OK, looping through 0xFF terminated strings are a little hard
    	@Especially if the string is arbitrary, so we'll just hard code it
    	
    	@store byte BC 
    	@add one to current offset
    	@store byte D5
    	@add one to current offset
    	@store byte DF
    	@add one to current offset
    	@store byte D5
    	@add one to current offset
    	@store byte FF
    	
    	pop {r0-r1, pc}
    	
    .align 2
    
    .playerData:
    	.word 0x300500C

    Task #3
    3) Make all Pokemon Shiny

    -Task removed-
    I now see that this is more complicated than what should be covered this week. In time, we'll come back to the famous, "Shiny Pokemon" problem :)


    Challenge task:
    4) Can you delete a Pokemon?

    Recall from task #1's hint, that I said if a Pokemon slot is empty, then that 0x64 chunk of RAM will be set to 00s.
    Basically all we want to do is set an 0x64 block of RAM to 0x0s. No more hints on this one. I think I've made it super easy now. Anyways, there will be a bug, when you open the Pokemon Menu, the missing slot will look weird. We'll fix this problem in a coming session. For now, just try and set the RAM byte using the str command.
     

    Blah

    Free supporter
    1,924
    Posts
    11
    Years
  • MAGIC TASK 1
    Give the slot number of first Pokemon who isn't full HP


    Partial Solution/Final Solution?:
    Spoiler:

    Some minor changes to Magic's solution:
    Spoiler:


    Some things to keep in mind:
    Code:
    bne somewhere
    b somewhereElse
    Code like this is always able to be simplified. Double branching (one branch right next to another), unless they're branch links ("bl") statements, should never be together.

    Code:
    bne somewhere
    b somewhereElse
    
    somewhere:
    	@code here
    
    somewhereElse:
    	@code here

    is the same as

    Code:
    ...
    beq somewhereElse
    @"somewhere" code here
    
    somewhere Else:
    	@code here
    Save yourself some easy bytes :)


    Alright Magic, I approve you to move on to the next task. Also, if you don't mind, I'd like to use your solution for a solutions post I'll make in the future. Yours is a good example of a do while loop, I hope someone makes one of a regular while loop soon too.


    EDIT: I've added some links to some heavy hint posts in the first post~

    Is this week a little too hard? I think it might be, we are doing RAM pointer handling, looping, and writing to RAM.
     
    10,078
    Posts
    15
    Years
    • UK
    • Seen Oct 17, 2023
    MAGIC TASK 2
    Make the Player's name "Baka"


    Partial Solution:
    Spoiler:
     

    Blah

    Free supporter
    1,924
    Posts
    11
    Years
  • MAGIC TASK 2
    Make the Player's name "Baka"


    Partial Solution:
    Spoiler:

    Yeah that's right, though I would switch the registers here.

    Code:
    @Here it's clear that r0 is the address
    @and that r1 is the byte being stored
    @it's just a minor readability thing that I'm nitpicking :P
    
    ldr r0, [r0]
    mov r1, #0xByte
    strb r1, [r0, #0x1]
    ...

    You're ready for task #3 Magic :P
     
    Last edited:

    jiangzhengwenjzw

    now working on katam
    181
    Posts
    11
    Years
    • Seen today
    UPDATED
    TASK 3
    MAKE POKEMON SHINY
    This is an uncomplete solution because I haven't done the trainer and egg part (I really don't have much time). I have only shinyzed wild pokemon.

    Solution so far tested: (If you want it to activate in game, I think it's possibly simple because you can use a variable to do conditional branch, to either run the function or not)
    Spoiler:


    The new solution which also work for givepokemon:

    Spoiler:

    Reference: Hackmew
     
    Last edited:

    kearnseyboy6

    Aussie's Toughest Mudder
    300
    Posts
    15
    Years
    • Seen Jun 22, 2019
    Alrighty, this is happening, thanks FBI firstly. Here I go:

    Spoiler:

    I tried to get maximum efficiency for these routines and minimal register use. Any comment on deletion strategy?

    May I ask:

    - When do we need to push lr and pop pc? It is taught in most tutorials but I never have used them.

    - Also I pushed r2 and assumed this register was 0. Is this correct to assume so or would I have to mov r2, #0x0 at the start?
     

    Blah

    Free supporter
    1,924
    Posts
    11
    Years
  • Alrighty, this is happening, thanks FBI firstly. Here I go:

    Task 1: This loop isn't a loop! It only runs once, because as you can see there's no "b loop" command inside the loop. Another thing is that you're branching to the wrong part of code.

    Remember, that if a Pokemon is not full HP, we return their slot number. Here, you're jumping to the end if they are full HP. Instead of:
    Code:
    cmp r1, r2
    beq done

    You would need to "beq loop". Because if the current HP = Total HP, then the Pokemon is full HP. So we need to go to the next Pokemon. You're also going to need another register to keep track of how many times the loop will execute (it's useful also because this "counter" register will act as a slot identifier for our return. Right now, assuming you fix the non-looping issue, it will loop way past your six Pokemon.
    Finally, the last thing to think about in your solution for task one is this part:
    Code:
    cmp r1, r2
    beq done @obviously you will fix this to beq loop
    add r0, #0x64
    Above, when the branch is changed to beq loop, we'll be stuck in an infinite loop because "add r0" isn't going to happen before the loop. I.E you need to move it to the best location.


    Task number 2 looks very well done. Good job. But you're forgetting to push {lr} and pop {pc}. This routine is going to be called by another routine using bl or a linker. So you need to push the link register and pop pc. Otherwise, as it is now, it will not return and instructions in the next line (beyond this routine) will continue to execute until the whole thing crashes.

    There some things wrong with task 3 in your solution while I will address now. First of all, when you're storing bytes in RAM, you need to use str or strb. In both of your solutions you are trying to load 4 bytes at a time from pkmnData, then you're setting the register to 0 (by using lsl and bit shifting all 32 bits), but you could use mov instead. Anyways, the point is, you're setting the register to zero, not the actual RAM at the offset. You'd need to use str/strh/strb for this. I recommend using strb first, and then expanding your solution.

    Again like the last routine you've forgotten to push/pop lr and pc. Look at the explanation I gave for your task 2 for more info.
    Finally, like Magic had, you also have double branching statements.

    Note that:
    Code:
    cmp r2, #0x64
    beq done
    b loop
    
    done:
    ...
    code
    ...

    is the same as

    Code:
    cmp r2, #0x64
    bne loop
    
    done:
    ...
    code
    ...

    Now to address your questions.

    -When do we need to push lr and pop pc? It is taught in most tutorials but I never have used them.
    Pretty much all the time, except when you're hooking. That's the simple explanation, but in more complication, you should push lr every time you're anticipating your function to be called and expected to be returned from after being called.

    - Also I pushed r2 and assumed this register was 0. Is this correct to assume so or would I have to mov r2, #0x0 at the start?
    No, that is not correct. You need to set r2 to 0.
     

    Blah

    Free supporter
    1,924
    Posts
    11
    Years
  • Alright, that was week one. Last week we didn't really need to do much "finding", debugging or searching. I tried to keep stuff simple, if you knew the OP codes and how to use them, last weeks tasks should've been rather simple. In the case you were unable to finish, and would like more time, then go ahead and try finishing. There's really no due date, as everything is archived so you can attempt/look at hints/solutions whenever you need to.

    Moving on, I will now post solutions of last weeks tasks. Explanations will follow some time tomorrow when I have time for breaking and analyzing thought processes for writing these.

    Task #1: Slot number of a Pokemon who isn't full HP

    Spoiler:



    Task #2: Setting the Player's name

    Spoiler:


    Challenge task:
    Spoiler:



    Challenge task stmia/ldmia edition:

    OK, you don't need to do it this way, and this way is slightly more complicated too. Though faster. We'll look at memcpy, memset, strcpy and such at a later time.

    Code:
    .text
    .align 2
    .thumb
    .thumb_func
    
    main:
    	push {r0-r7, lr}
    	
    	@figure out which 'mon to delete
    	ldr r0, =(0x2024284)
    	ldr r1, =(0x20370B8)
    	ldrb r1, [r1]
    	mov r2, #0x64
    	mul r1, r1, r2
    	add r0, r0, r1
    	
    	@memset r1, #0x0
    	mov r1, #0x0
    	
    	@end offset.
    	add r2, r2, r0
    	sub r2, r2, #0x14
    	
    memset:
    	@set first 20 bytes #0x0
    	stmia r0!, {r1}@4
    	stmia r0!, {r1}@8
    	stmia r0!, {r1}@C
    	stmia r0!, {r1}@10
    	stmia r0!, {r1} @14
    	sub r0, r0, #0x14
    	
    memcpy:
    	@copy previous 20 bytes and overwrite current bytes
    	cmp r2, r0
    	beq end
    	ldmia r0!, {r3-r7}
    	stmia r0!, {r3-r7}
    	sub r0, r0, #0x14
    	b memcpy
    	
    	
    end:
    	pop {r0-r7, pc}
    
    .align 2

    I'll put up a new set of tasks soon too. I need to first finish a mini-tutorial post for explanations for how to do these tasks above. If you're done and just waiting for the next task, take a look at the solutions. There are probably differences between your version and mine. Who's is better, and why?
     
    146
    Posts
    11
    Years
    • CO
    • Seen Mar 16, 2023
    So I practice at task 1 all night (literally haven't went to sleep).

    To no avail I still can't seem to figure out how you would loop it and
    do the comparison of 6 possible Pokemon's CHP and THP.
    Nor could I understand just how to keep track of the slot of the Pokemon ergo 0-5.

    Here is what I came up with
    Partial Routine(if it can be called even that)
    Spoiler:


    I tested it in a clean rom as well and it does not work in any sense... I couldn't even get the game to bug out or freeze >_< sadly hahahaha

    Well any advice and help is much appreciated an thank you for your time

    Ps also thank you FBI Agent for this thread
     
    Back
    Top