Cy-Chan
GSC Hacker
- 152
- Posts
- 20
- Years
- Age 33
- UK, England.
- Seen May 9, 2015
To start with, the one people are probably more interested in...
What are pointers? Well, an equivalent in other programming languages would be a "goto", "gosub" or perhaps a "call" command. They make the code start reading from a new area.
In hex, these areas are known as addresses, of such range from $000000 to $FFFFFF (in a standard GBA rom), obviously "0" being the lowest number in the hexadecimal format, and "F" being the highest.
In the GBA games, pointers are very simple. ASM reads bytes backwards, so hex address $7A6B5C becomes 5C6B7A08.
But wait! Reversed would mean "$7A6B5C" becomes "C5B6A7"! Cy-Chan, you suck! Ahah, but that is majorly incorrect! You see, a single digit value is known as a "bit", and the hexadecimal format works with "bytes", that is, the combination of two values.
Now, what about that 08? Well, the memory bank for the GBA is made up of several sections. For example, 02 is RAM, 03 is DMA; check the memory viewer in VBA for more detail. So, when referring to ROM information (the bit we hack), the full address will start with 08. In an extended ROM, any addresses past 08FFFFFF simply loop back to 0, but use 09 as the indentifier instead.
So that's why pointing to $7A6B5C in ROM would be done with the pointer 5C6B7A08.
And now for something awesome!
Oh gosh, I couldn't resist. After recently hacking a bit of Gold for fun, I learnt about some of the architecture there too, and found that the pointer hacking guides currently out there are a bit vague.
So, to start, there's a 3-bit pointer system. Let's say we want to point to $3B1C2. The simple part; take the last two bytes and reverse them. Currently, our pointer is C2B1.
The last value in the pointer needs to then be between 40 and 7F, so, subtract or add 40 until the byte is between them. B1 - 40 = 71, so our pointer stands at C271.
Then, divide the address by 4000. This gives us 0E. This is our bank number. We place this at the beginning of the pointer (so, 0EC271). Ta-dah! 3-byte pointer.
Now, what is a bank number, you ask? Well, the GBC is much smaller than the GBA, and can only keep a certain amount of data loaded at one time (8000 bytes, to be exact). 4000 of these bytes are known as bank 0, basically the default bank that contains all the really important stuff.
The other 4000 are used for bank switching. So, if a pointer says to use bank 31 (31*4000 = C4000), then that bank is loaded, and it is pointed to.
What about the last byte in the pointer; why does this have to be between 40 and 7F? Ahah, well the switch bank is stored between $4000 and $7FFF. If we take the last two bytes of our previous example pointer (2C71), and re-reverse them (712C)... well, look at that. It sits nicely within the switch bank.
If we hadn't made that last byte fit between 40 and 7F, it would instead point to C271, a location within RAM!
That brings us to another problem though. A lot of the time, the bank number is omitted from the pointer, making a 2-byte pointer instead. This is because, if the bank is already loaded, then it doesn't need to be re-loaded, does it?
However, what if we wanted to switch bank? Sadly, this is where many people get frustrated. Changing a 2-byte pointer to a 3-byte pointer requires ASM. This is because the bank has already been loaded by an ASM command (to register A, to those who know their business), while the pointer is loaded by another command (in hex "21", usually).
Changing the bank number could potentially screw up several routines, so one needs to be careful when messing around with it.
Cy-Chan's Guide to Memory Maps
Just a quick addition here. On the GBA (and GBC) there's things known as memory maps. These are what contain all the data. Different sections are allocated to different portions of memory, so that's why $08000000 contains all the ROM (read-only memory) information, and why $02000000 contains RAM.
A full memory map can be found in the GBATek, alongside a load of other useful information, if anyone wants to Google it.
Code runs sequentially. This is, it goes from start to end (at least, in does at assembly level). Pointers are used in what's known as a JMP command, or a "jump" (or a "branch"; an if statement). This is where a different part of code is loaded, from a different area.
So, when we tell a pointer to go to $A01010 (1010A008), we're telling it to read information, or continue running the code in that area.
Cy-Chan's Guide to Repointing
I've mentioned pointers, and how to repoint, but there's no clear definition on what we're actually doing here, is there?
Well, sure, the rom is made up of assembly-level code. However, we're not changing that; we're just changing what it does with the code. For example, Fire Red's type weakness/resistance table is located at $24F050. We'll reverse that to make 50F02408.
Search the rom using your favorite hex editor (I personally suggest 010 Editor) for our pointer, and gosh, what do you know? Several results appear. These are all pointers to the type chart.
We know that there should be some free space around $720000, so we'll make our pointer go there. Replace all of the 50F02408 results with 00007208 (replace all is a useful function...)
Then, go to $24F050. Here is our type chart. Copy all of the information from $24F050 to $24F19E, and paste it into the blank area at $720000.
And we're done. To conserve space, you can go back and remove the data at $24F050 to $24F19E (perhaps for future use), but as far as we're concerned, the information has been transferred. Try it out; do all the types still work correctly? And now we have a ton of space to work with, so adding new weaknesses and resistances is no problem!
I personally like the idea of Water being weak to Poison, but that's just the tip of the iceberg. Adding new (special) types is quite easy to do...
---
Hope you enjoyed this tutorial, y'all! If you did, don't just let it die; leave comments. Feedback or whatever.
Cy-Chan's Quickie Guide to GBA Pointers!
What are pointers? Well, an equivalent in other programming languages would be a "goto", "gosub" or perhaps a "call" command. They make the code start reading from a new area.
In hex, these areas are known as addresses, of such range from $000000 to $FFFFFF (in a standard GBA rom), obviously "0" being the lowest number in the hexadecimal format, and "F" being the highest.
In the GBA games, pointers are very simple. ASM reads bytes backwards, so hex address $7A6B5C becomes 5C6B7A08.
But wait! Reversed would mean "$7A6B5C" becomes "C5B6A7"! Cy-Chan, you suck! Ahah, but that is majorly incorrect! You see, a single digit value is known as a "bit", and the hexadecimal format works with "bytes", that is, the combination of two values.
Now, what about that 08? Well, the memory bank for the GBA is made up of several sections. For example, 02 is RAM, 03 is DMA; check the memory viewer in VBA for more detail. So, when referring to ROM information (the bit we hack), the full address will start with 08. In an extended ROM, any addresses past 08FFFFFF simply loop back to 0, but use 09 as the indentifier instead.
So that's why pointing to $7A6B5C in ROM would be done with the pointer 5C6B7A08.
And now for something awesome!
Cy-Chan's Quickie Guide to GB/GBC Pointers!
Oh gosh, I couldn't resist. After recently hacking a bit of Gold for fun, I learnt about some of the architecture there too, and found that the pointer hacking guides currently out there are a bit vague.
So, to start, there's a 3-bit pointer system. Let's say we want to point to $3B1C2. The simple part; take the last two bytes and reverse them. Currently, our pointer is C2B1.
The last value in the pointer needs to then be between 40 and 7F, so, subtract or add 40 until the byte is between them. B1 - 40 = 71, so our pointer stands at C271.
Then, divide the address by 4000. This gives us 0E. This is our bank number. We place this at the beginning of the pointer (so, 0EC271). Ta-dah! 3-byte pointer.
Now, what is a bank number, you ask? Well, the GBC is much smaller than the GBA, and can only keep a certain amount of data loaded at one time (8000 bytes, to be exact). 4000 of these bytes are known as bank 0, basically the default bank that contains all the really important stuff.
The other 4000 are used for bank switching. So, if a pointer says to use bank 31 (31*4000 = C4000), then that bank is loaded, and it is pointed to.
What about the last byte in the pointer; why does this have to be between 40 and 7F? Ahah, well the switch bank is stored between $4000 and $7FFF. If we take the last two bytes of our previous example pointer (2C71), and re-reverse them (712C)... well, look at that. It sits nicely within the switch bank.
If we hadn't made that last byte fit between 40 and 7F, it would instead point to C271, a location within RAM!
That brings us to another problem though. A lot of the time, the bank number is omitted from the pointer, making a 2-byte pointer instead. This is because, if the bank is already loaded, then it doesn't need to be re-loaded, does it?
However, what if we wanted to switch bank? Sadly, this is where many people get frustrated. Changing a 2-byte pointer to a 3-byte pointer requires ASM. This is because the bank has already been loaded by an ASM command (to register A, to those who know their business), while the pointer is loaded by another command (in hex "21", usually).
Changing the bank number could potentially screw up several routines, so one needs to be careful when messing around with it.
Cy-Chan's Guide to Memory Maps
Just a quick addition here. On the GBA (and GBC) there's things known as memory maps. These are what contain all the data. Different sections are allocated to different portions of memory, so that's why $08000000 contains all the ROM (read-only memory) information, and why $02000000 contains RAM.
A full memory map can be found in the GBATek, alongside a load of other useful information, if anyone wants to Google it.
Code runs sequentially. This is, it goes from start to end (at least, in does at assembly level). Pointers are used in what's known as a JMP command, or a "jump" (or a "branch"; an if statement). This is where a different part of code is loaded, from a different area.
So, when we tell a pointer to go to $A01010 (1010A008), we're telling it to read information, or continue running the code in that area.
Cy-Chan's Guide to Repointing
I've mentioned pointers, and how to repoint, but there's no clear definition on what we're actually doing here, is there?
Well, sure, the rom is made up of assembly-level code. However, we're not changing that; we're just changing what it does with the code. For example, Fire Red's type weakness/resistance table is located at $24F050. We'll reverse that to make 50F02408.
Search the rom using your favorite hex editor (I personally suggest 010 Editor) for our pointer, and gosh, what do you know? Several results appear. These are all pointers to the type chart.
We know that there should be some free space around $720000, so we'll make our pointer go there. Replace all of the 50F02408 results with 00007208 (replace all is a useful function...)
Then, go to $24F050. Here is our type chart. Copy all of the information from $24F050 to $24F19E, and paste it into the blank area at $720000.
And we're done. To conserve space, you can go back and remove the data at $24F050 to $24F19E (perhaps for future use), but as far as we're concerned, the information has been transferred. Try it out; do all the types still work correctly? And now we have a ton of space to work with, so adding new weaknesses and resistances is no problem!
I personally like the idea of Water being weak to Poison, but that's just the tip of the iceberg. Adding new (special) types is quite easy to do...
---
Hope you enjoyed this tutorial, y'all! If you did, don't just let it die; leave comments. Feedback or whatever.
Last edited by a moderator: