Skeetendo

’Cause all games were better on the GBC

You are not logged in.

#1 2012-09-11 00:16:36

Danny-E 33
Administrator
Registered: 2012-06-09
Post 115/1,030

How Do I Go About Fixing This Error?

So this happened with a somewhat cleaner rom that doesn't have alot changed about it, but it is gonna be used for an ips I wanted to release shortly for a secret project I'm almost finished with. Yesterday I realized something that I clearly didn't intend on doing, and I don't know how to trace my mistake.

Whenever the game loads the hiro sprite when you name your player at the start of a game, I have apparently made it so that the game flips the sprite during decompression. Also, when the sprite pans across the screen, they intentionally don't transfer the bottom-right tile since it is supposed to be blank. But now, his toe is cut off...
Does anyone know how to determine what I accidentally changed?
Thanks :)

Last edited by Danny-E 33 (2012-10-15 05:34:17)


Red Hack: Pokémon Prototype

Total number of registered users: 7000+
Total number of active users: ~12

Offline

#2 2012-10-06 05:51:20

Danny-E 33
Administrator
Registered: 2012-06-09
Post 144/1,030

Re: How Do I Go About Fixing This Error?

I really rather not start this project all over again, but it's lookin like I might have to...
And I have no idea how early on in the process this error occured.
Does any one know of a way of tracking down the mistake I made?


Red Hack: Pokémon Prototype

Total number of registered users: 7000+
Total number of active users: ~12

Offline

#3 2012-10-06 12:03:53

Sawakita
Administrator
Registered: 2010-10-16
Post 313/364

Re: How Do I Go About Fixing This Error?

It might be a bit hard saying without knowing what modifications were made. One thing you could try is compare your hack against the original ROM (Hex Workshop has a neat highlighting comparison feature, but there are probably many other, maybe more lightweight, tools that do the job): so you can spot some changes you don't recognize.

Also, did you just hex edit some data or write some custom code too? In case you made some ASM hacking that involve the sprite loading routines or the "intro" routine, you could double-check those.

Offline

#4 2012-10-06 21:37:48

Danny-E 33
Administrator
Registered: 2012-06-09
Post 149/1,030

Re: How Do I Go About Fixing This Error?

Wow, that's very interesting. So in Hex Workshop, in the compare tab I compared my hack with a clean Red ROM and in the dropdown arrow I selected "Replacements" so it only showed the differences between the two and not the similarities.

It came up with a very nice, short list of all the locations that the two files were different and through process of elimination, I found out that the chunk of code that caused this error was from 0x41024 to 0x410E1.
This is the table that associates the Pokemon's internal ID with its Pokedex number. Why would editing this table mirror the sprite when it's loaded during a new game?
(and although I didn't specify, the rival's sprite that is loaded a few moments later when you are naming your rival is also mirrored)

EDIT: I also just took a clean Red ROM and only changed the table at 0x41024 to make sure it was the only thing that took part in the error, and it was.
I have no idea why the Pokedex numbers of the internal IDs makes such a difference though... I'm baffled.

Last edited by Danny-E 33 (2012-10-06 21:48:35)


Red Hack: Pokémon Prototype

Total number of registered users: 7000+
Total number of active users: ~12

Offline

#5 2012-10-10 00:35:48

Danny-E 33
Administrator
Registered: 2012-06-09
Post 159/1,030

Re: How Do I Go About Fixing This Error?

So I've discovered it is the byte at 0x410CA that caused this problem when I changed its value to 00.

This is the byte for the Pokedex number of Nidorino, internal id 0xA7, Pokedex number 0x21 (33).

When I changed the byte at 0x616D to 0x21 (Nidorino's original Pokedex number and new internal id number for this hack) instead of 0xA7 (which is now an unused internal id) it fixed the intro Pokemon shown while Oak is talking to correctly display Nidorino's sprite (which I knew I would have to do eventually when I go through the game and correct every time a Pokemon's old id is referenced) and it then correctly displayed the player sprite and the rival sprite later.

So I've fixed my problem now, but I have no idea why an unused internal id as the intro sprite mirrors the player and rival sprite during the intro :P
But thank you for teaching me about the compare feature in Hex Workshop, otherwise I might never have figured out what caused this problem. :)


Red Hack: Pokémon Prototype

Total number of registered users: 7000+
Total number of active users: ~12

Offline

#6 2012-10-10 03:34:11

stag019
Idea Killer
Registered: 2011-01-05
Post 221/630

Re: How Do I Go About Fixing This Error?

So wait, what you're saying is, before you fixed this, instead of loading Nidorino, it loaded an unused ID?  Because the graphical system can sometimes glitch whenever it tries to load a sprite that isn't correct. Like when you view MissingNo., and the other sprites start reversing themselves. Is that what caused it?


You can try to hide yourself in this world of pretend; when the paper's crumpled up, it can't be perfect again.

Offline

#7 2012-10-10 04:22:46

Danny-E 33
Administrator
Registered: 2012-06-09
Post 160/1,030

Re: How Do I Go About Fixing This Error?

I guess it's sort of like that, but not exactly.

Even on a perfectly clean rom, if you change just that one byte at 0x410CA to anything else, then Nidorino still has the same internal id of A7 meaning most everything associated with a Pokemon will stay the same. The name, cry, evolution data, the pages in the Pokedex. But its number in the Pokedex will be different.

When you change Nidorino's Pokedex number to 00, the only thing that changes is the base stats, since that array is Pokedex based, not internal id based.
And of course, the base stats includes the pointer to the sprites. (pretty sure that bit is irrelevant)

So it's not that it was loading an unused id. The id still technically belonged to Nidorino. But it was loading the sprite for an id with no Pokedex number. Which is pretty much what Missingno. is.
Missingno.s do use an internal id, just not a Pokedex number.

And that's why loading Missingno. sprites glitches up the graphics. It's not a problem of loading an internal id that isn't really used in the game, but more specifically loading an id that doesn't belong to the Pokedex. Not that there's much of a difference. :P

Last edited by Danny-E 33 (2012-10-14 10:10:21)


Red Hack: Pokémon Prototype

Total number of registered users: 7000+
Total number of active users: ~12

Offline

#8 2012-10-10 08:29:07

stag019
Idea Killer
Registered: 2011-01-05
Post 222/630

Re: How Do I Go About Fixing This Error?

Okay.
I see what you did there.


You can try to hide yourself in this world of pretend; when the paper's crumpled up, it can't be perfect again.

Offline

#9 2012-10-10 17:36:11

Sawakita
Administrator
Registered: 2010-10-16
Post 315/364

Re: How Do I Go About Fixing This Error?

Danny-E 33 wrote:

why an unused internal id as the intro sprite mirrors the player and rival sprite during the intro :P

The mirroring is caused by the routine at 0x1384 that doesn't "clean up" the state it modifies before returning. RAM:d0aa is used as a flag to tell the sprite loading routine whether it should flip the sprite or not:

ROM0:1384 3e 01            ld   a,01
ROM0:1386 ea aa d0         ld   (d0aa),a    ; set flip = True
ROM0:1389 e5               push hl
ROM0:138a fa 1e d1         ld   a,(d11e)    ; [d11e] contains ID (internal order)
ROM0:138d f5               push af
ROM0:138e fa 91 cf         ld   a,(cf91)
ROM0:1391 ea 1e d1         ld   (d11e),a
ROM0:1394 3e 3a            ld   a,3a
ROM0:1396 cd 6d 3e         call 3e6d        ; convert internal ID to dex ID
ROM0:1399 21 1e d1         ld   hl,d11e     ; (the result is stored in [d11e])
ROM0:139c 7e               ld   a,(hl)
ROM0:139d c1               pop  bc
ROM0:139e 70               ld   (hl),b
ROM0:139f a7               and  a
ROM0:13a0 e1               pop  hl
ROM0:13a1 28 04            jr   z,13a7      ; check if ID is NULL
ROM0:13a3 fe 98            cp   a,98
ROM0:13a5 38 06            jr   c,13ad      ; check if ID is valid pokemon (1 ≤ valid pokemon ≤ 151)
ROM0:13a7 3e 01            ld   a,01        ; otherwise return early
ROM0:13a9 ea 91 cf         ld   (cf91),a
ROM0:13ac c9               ret

As you can see when the ID is not valid (e.g. ID = 0) the routine returns without resetting mirrorring-flag [$D0AA], which causes the next loaded sprite to be mirrored (often incorrectly).

When the ID is between 1 and 0x97 (inclusive), the routine behaves correctly, it displays the sprite and reset [D0AA] before returning.

; display valid pokemon sprite (flipped)
ROM0:13ad e5               push hl
ROM0:13ae 11 00 90         ld   de,9000
ROM0:13b1 cd 65 16         call 1665        ; decompress and load sprite to VRAM starting at [DE]
ROM0:13b4 e1               pop  hl
ROM0:13b5 f0 b8            ld   a,(ff00+b8)
ROM0:13b7 f5               push af
ROM0:13b8 3e 0f            ld   a,0f
ROM0:13ba e0 b8            ld   (ff00+b8),a
ROM0:13bc ea 00 20         ld   (2000),a
ROM0:13bf af               xor  a
ROM0:13c0 e0 e1            ld   (ff00+e1),a
ROM0:13c2 cd d0 70         call 70d0        ; arrange sprite on a 7x7 tiles square starting at [HL]
ROM0:13c5 af               xor  a
ROM0:13c6 ea aa d0         ld   (d0aa),a    ; set flip = False
ROM0:13c9 f1               pop  af
ROM0:13ca e0 b8            ld   (ff00+b8),a
ROM0:13cc ea 00 20         ld   (2000),a
ROM0:13cf c9               ret

This is arguably one of the reasons why this game's code is considered "messy".

Offline

#10 2012-10-11 15:41:37

Danny-E 33
Administrator
Registered: 2012-06-09
Post 165/1,030

Re: How Do I Go About Fixing This Error?

Sawakita wrote:

when the ID is not valid (e.g. ID = 0)

Sawakita wrote:

When the ID is between 1 and 0x97 (inclusive)

So here, we're strickly talking about Pokedex id, right? Because by this point, all internal id's have been converted to the Pokedex id?

Sawakita wrote:

The mirroring is caused by the routine at 0x1384 that doesn't "clean up" the state it modifies before returning. RAM:d0aa is used as a flag to tell the sprite loading routine whether it should flip the sprite or not:

ROM0:1384 3e 01            ld   a,01
ROM0:1386 ea aa d0         ld   (d0aa),a    ; set flip = True
ROM0:1389 e5               push hl
ROM0:138a fa 1e d1         ld   a,(d11e)    ; [d11e] contains ID (internal order)
ROM0:138d f5               push af
ROM0:138e fa 91 cf         ld   a,(cf91)
ROM0:1391 ea 1e d1         ld   (d11e),a
ROM0:1394 3e 3a            ld   a,3a
ROM0:1396 cd 6d 3e         call 3e6d        ; convert internal ID to dex ID
ROM0:1399 21 1e d1         ld   hl,d11e     ; (the result is stored in [d11e])
ROM0:139c 7e               ld   a,(hl)
ROM0:139d c1               pop  bc
ROM0:139e 70               ld   (hl),b
ROM0:139f a7               and  a
ROM0:13a0 e1               pop  hl
ROM0:13a1 28 04            jr   z,13a7      ; check if ID is NULL
ROM0:13a3 fe 98            cp   a,98
ROM0:13a5 38 06            jr   c,13ad      ; check if ID is valid pokemon (1 ≤ valid pokemon ≤ 151)
ROM0:13a7 3e 01            ld   a,01        ; otherwise return early
ROM0:13a9 ea 91 cf         ld   (cf91),a
ROM0:13ac c9               ret

As you can see when the ID is not valid (e.g. ID = 0) the routine returns without resetting mirrorring-flag [$D0AA], which causes the next loaded sprite to be mirrored (often incorrectly).

So can we fix this error, by changing the code at 0x13A7 to

call xxxx ; where xxxx is the beginning of free space in the bank (I'm at school and don't have this location at the moment)
ret

and change the code at xxxx to

ld a, $00
ld [$D0AA], a
ld a, $01
ld [$CF91], a
ret
Sawakita wrote:

When the ID is between 1 and 0x97 (inclusive), the routine behaves correctly, it displays the sprite and reset [D0AA] before returning.

; display valid pokemon sprite (flipped)
ROM0:13ad e5               push hl
ROM0:13ae 11 00 90         ld   de,9000
ROM0:13b1 cd 65 16         call 1665        ; decompress and load sprite to VRAM starting at [DE]
ROM0:13b4 e1               pop  hl
ROM0:13b5 f0 b8            ld   a,(ff00+b8)
ROM0:13b7 f5               push af
ROM0:13b8 3e 0f            ld   a,0f
ROM0:13ba e0 b8            ld   (ff00+b8),a
ROM0:13bc ea 00 20         ld   (2000),a
ROM0:13bf af               xor  a
ROM0:13c0 e0 e1            ld   (ff00+e1),a
ROM0:13c2 cd d0 70         call 70d0        ; arrange sprite on a 7x7 tiles square starting at [HL]
ROM0:13c5 af               xor  a
ROM0:13c6 ea aa d0         ld   (d0aa),a    ; set flip = False
ROM0:13c9 f1               pop  af
ROM0:13ca e0 b8            ld   (ff00+b8),a
ROM0:13cc ea 00 20         ld   (2000),a
ROM0:13cf c9               ret

What part of this code set/resets the mirror flag exactly? I can see the line that says "ld (D0AA), a" is used to set or unset the flag, but how is this determined with the operations done to register a previously?


Red Hack: Pokémon Prototype

Total number of registered users: 7000+
Total number of active users: ~12

Offline

#11 2012-10-11 18:05:01

Sawakita
Administrator
Registered: 2010-10-16
Post 316/364

Re: How Do I Go About Fixing This Error?

Danny-E 33 wrote:
Sawakita wrote:

when the ID is not valid (e.g. ID = 0)

Sawakita wrote:

When the ID is between 1 and 0x97 (inclusive)

So here, we're strickly talking about Pokedex id, right? Because by this point, all internal id's have been converted to the Pokedex id?

It's correct.

Danny-E 33 wrote:

So can we fix this error, by changing the code at 0x13A7 to

call xxxx ; where xxxx is the beginning of free space in the bank (I'm at school and don't have this location at the moment)
ret

and change the code at xxxx to

ld a, $00
ld [$D0AA], a
ld a, $01
ld [$CF91], a
ret

It's good, your custom code is fine. I'll just throw in a couple of suggestions about optimization, it doesn't hurt.

  • a call followed by a ret can be replaced by a jp (the effect is the same, while the pros are that you use one byte less and don't push anything on the stack, which is not bad)

  • ld a, $00, unless you need to preserve flags for some reason, can be replaced by xor a, which uses one byte instead of two.

  • ld a, $01, can be replaced by inc a, since we know that at this point a = 0

So, we'd have something like this:

jp xxxx
xor a
ld [$d0aa],a
inc a
ld [$cf91],a
ret

Anyway, your code is ok that way too, since you probably don't have space issues. It might be handy in other situations (like when you try to add code to bank 0, where free space is not so much).

Danny-E 33 wrote:

What part of this code set/resets the mirror flag exactly? I can see the line that says "ld (D0AA), a" is used to set or unset the flag, but how is this determined with the operations done to register a previously?

The instruction that preceds "ld (D0AA), a" is "xor a", which resets register a (I think we talked about this, before  ;-)

Offline

#12 2012-10-11 19:00:24

Tauwasser
Member
Registered: 2010-10-16
Post 308/448

Re: How Do I Go About Fixing This Error?

Actually, you should probably fix your code and use a valid id!

The reason why the id check is in there in the first place is probably because the routine can be used for multiple purposes. And when you don't decompress a Pokemon sprite, you might not want the routine to touch the mirror flag.
Consider

...
setSpritesMirrored();
decompressPKMN(0x01)
decompressOtherGraphics(11:2233)
decompressPKMN(0x02)
...

You would expect PKMN sprites 0x01 and 0x02 both to be mirrored. Yet, when you fix the decompress code to ignore the id and nix the mirror flag anyway, only the first sprite (0x01) will be mirrored.

cYa,

Tauwasser

Offline

#13 2012-10-11 19:46:38

Sawakita
Administrator
Registered: 2010-10-16
Post 317/364

Re: How Do I Go About Fixing This Error?

Tauwasser wrote:

Actually, you should probably fix your code and use a valid id!

I agree with the first line of your post, but not with the following.

Tauwasser wrote:

The reason why the id check is in there in the first place is probably because the routine can be used for multiple purposes. And when you don't decompress a Pokemon sprite, you might not want the routine to touch the mirror flag.
Consider

...
setSpritesMirrored();
decompressPKMN(0x01)
decompressOtherGraphics(11:2233)
decompressPKMN(0x02)
...

I wouldn't label routine $1384 as "setSpritesMirrored()" (because that's only a part of the routine's behaviour). It sets the mirroring and, if the passed ID is valid, it decompresses the corresponding sprite, loads it to VRAM:9000 and unsets mirroring flag, else it puts 1 in $cf91 (usually used to store various kind of ID, e.g. a pokemon's or an item's) and exits early. Honestly, a "setSpritesMirrored()" routine would look like this, in my opinion:

ld a,1
ld [$d0aa],a

Maybe wrapped with a push af/pop af, but nothing more.

The original game implementation is rather different than your pseudo-code. A routine for loading specifically flipped pokemon's frontsprites is provided. Each time you want to print a compressed flipped frontsprite you put your internal ID into [$d11e] and call $1384. If you want to load some unrelated graphics as mirrored you don't call it. I'm saying all this because I miss the point of doing all the internal-to-dex-order conversion just to end up with a mirroring flag set when all I needed was modifying a single RAM value.

Tauwasser wrote:

You would expect PKMN sprites 0x01 and 0x02 both to be mirrored. Yet, when you fix the decompress code to ignore the id and nix the mirror flag anyway, only the first sprite (0x01) will be mirrored.

Sprite 0x02 will be mirrored too, because [$d0aa] gets set every time (at the beginning of routine $1384) before doing any decompression work.

Anway, since I don't remember a case when you were wrong about this kind of stuff, I'm not sure I'm not completely missing something.

Offline

#14 2012-10-12 06:56:30

Tauwasser
Member
Registered: 2010-10-16
Post 309/448

Re: How Do I Go About Fixing This Error?

Sawakita wrote:

I wouldn't label routine $1384 as "setSpritesMirrored()" (because that's only a part of the routine's behaviour). It sets the mirroring and, if the passed ID is valid, it decompresses the corresponding sprite, loads it to VRAM:9000 and unsets mirroring flag, else it puts 1 in $cf91 (usually used to store various kind of ID, e.g. a pokemon's or an item's) and exits early.

That was not supposed to be pseudo-code for the routine but for some other routines in the game that might rely on the behavior of the decompression routine setting the mirror flag.

Sawakita wrote:

A routine for loading specifically flipped pokemon's frontsprites is provided. Each time you want to print a compressed flipped frontsprite you put your internal ID into [$d11e] and call $1384. If you want to load some unrelated graphics as mirrored you don't call it. I'm saying all this because I miss the point of doing all the internal-to-dex-order conversion just to end up with a mirroring flag set when all I needed was modifying a single RAM value.

The programmers weren't as good in coding straight-forward as one might hope. Rumors are that the source code was so broken that it barely worked for the Japanese version. Then it stopped working altogether once it was translated to (US-)English so large parts had to be recoded. I wouldn't put it past them to call this routine somewhere just to set the mirror flag.

Anyway, I was just saying you might inadvertently break mirroring behavior when "fixing" this routine. However, I took a quick look at the ROM now and it seems to be safe to do. I wanted to remind everybody that you have to look at the whole picture and actually check things out and not just reason along the lines of "They would never have called that routine with an invalid PokéID just to set the mirroring flag".

cYa,

Tauwasser

Offline

#15 2012-10-12 14:15:29

Danny-E 33
Administrator
Registered: 2012-06-09
Post 166/1,030

Re: How Do I Go About Fixing This Error?

Hmm, I can see both sides of the argument.
It could be a way of exiting the routine if a glitch Pokemon is being loaded.
Or it could be a routine with a dual purpose of setting the mirror flag by running the routine when a=0.
But theoretically, I don't even haaave to fix this code as long as I make sure I don't make any Missingno.s accessible. Since this error only happened because the Oak intro Pokemon's Pokedex number was 0.


Red Hack: Pokémon Prototype

Total number of registered users: 7000+
Total number of active users: ~12

Offline

#16 2012-10-12 14:28:05

Danny-E 33
Administrator
Registered: 2012-06-09
Post 167/1,030

Re: How Do I Go About Fixing This Error?

Sawakita wrote:

It's good, your custom code is fine. I'll just throw in a couple of suggestions about optimization, it doesn't hurt.

  • a call followed by a ret can be replaced by a jp (the effect is the same, while the pros are that you use one byte less and don't push anything on the stack, which is not bad)

  • ld a, $00, unless you need to preserve flags for some reason, can be replaced by xor a, which uses one byte instead of two.

  • ld a, $01, can be replaced by inc a, since we know that at this point a = 0

So, we'd have something like this:

jp xxxx
xor a
ld [$d0aa],a
inc a
ld [$cf91],a
ret

I definitely always appreciate the optimization! :)
But I think I must have an unclear understanding of the differences between calls and jumps...
I thought returns only applied to calls because they begin executing from the location in the stack pointer that was pushed there when the call was executed? Am I wrong here? You suggest a jump cause it doesn't push anything on the stack. If this is so, then how does a return work properly? And why is using a jump one byte shorter? Wouldn't the jump still take 3 bytes, and the return take 1? Same as a call and a return?

Sawakita wrote:

The instruction that preceds "ld (D0AA), a" is "xor a", which resets register a (I think we talked about this, before  ;-)

Haha, right.

Last edited by Danny-E 33 (2012-10-14 10:05:55)


Red Hack: Pokémon Prototype

Total number of registered users: 7000+
Total number of active users: ~12

Offline

#17 2012-10-12 14:38:06

Miksy91
Member
Registered: 2010-10-16
Post 1,164/2,311

Re: How Do I Go About Fixing This Error?

call command decrements stack pointer by two and puts the program counter in the address loaded in it. After that, the two bytes following the call command are loaded in program counter. jp simply only does the latter one and does not affect stack in any way. I wouldn't worry about stack too much though.

ret is used to load the two first bytes from the address determined by stack pointer, into program counter. After that, it increments stack pointer by two.

To make more sense, stack is "located" between ram area: DF00 and DFFF. The game starts filling the stack from DFFF and going "backwards" towards DF00. If the stack would grow to hold more bytes than 0x100 (until DF00), it would probably continue filling the stack from DEFF and so on affecting unwanted part of WRAM.

Danny-E 33 wrote:

And why is using a jump one byte shorter? Wouldn't the jump still take 3 bytes, and the return take 1? Same as a call and a return?

It's probably self-explanotary now but anyways:

jp $xxyy

call $xxyy
ret

Have a "similar" effect and jp takes one byte less space.

Last edited by Miksy91 (2012-10-12 14:54:58)

Offline

#18 2012-10-12 14:53:21

Danny-E 33
Administrator
Registered: 2012-06-09
Post 168/1,030

Re: How Do I Go About Fixing This Error?

Oh.. Well now I'm questioning what I thought the stack pointer and the program counter mean..
What exactly does the stack pointer/program counter represent and how are they manipulated and used?
Just to clear up all the confusion and misunderstandings I have right now. Sorry.


Red Hack: Pokémon Prototype

Total number of registered users: 7000+
Total number of active users: ~12

Offline

#19 2012-10-12 14:57:26

Miksy91
Member
Registered: 2010-10-16
Post 1,165/2,311

Re: How Do I Go About Fixing This Error?

According to what I believe, program counter is a 2-byte value that's used to address the current ram location the processor is executing code at. Whenever a command is executed, program counter is incremented by as many bytes as that command takes so that processor is able to execute the next command.

Stack pointer is also a 2-byte value that contains the address where the top of the stack is located. And the top of the stack itself contains the 2-byte value of the address that is put into program counter whenever the next "ret" command is executed.

Last edited by Miksy91 (2012-10-12 15:04:33)

Offline

#20 2012-10-12 15:05:14

Danny-E 33
Administrator
Registered: 2012-06-09
Post 169/1,030

Re: How Do I Go About Fixing This Error?

Okay okay. So is it because of the stack pointer that you can use a call insdie of a call, and then return twice and each return knows to return to their own unique address?


Red Hack: Pokémon Prototype

Total number of registered users: 7000+
Total number of active users: ~12

Offline

#21 2012-10-12 15:09:44

Miksy91
Member
Registered: 2010-10-16
Post 1,166/2,311

Re: How Do I Go About Fixing This Error?

Yeah, that's how it goes. You can call as many sub-routines by main routines as you like.
It's hardly a possibility that the stack pointer would ever "grow too small", hahha. But you can always debug the routine with BGB and see what values are loaded in stack pointer to make sure you stay on the safe grounds.

What I don't know for sure is if the stack pointer will always be same when called with the same routines in different occasions during the game. But I've never figured why it wouldn't be...

Last edited by Miksy91 (2012-10-12 15:13:51)

Offline

#22 2012-10-14 10:12:26

Danny-E 33
Administrator
Registered: 2012-06-09
Post 170/1,030

Re: How Do I Go About Fixing This Error?

Sawakita wrote:
jp xxxx
xor a
ld [$d0aa],a
inc a
ld [$cf91],a
ret

I'm still confused how using a jump is supposed to save one byte if a jump command uses 3 bytes just like a call?


Red Hack: Pokémon Prototype

Total number of registered users: 7000+
Total number of active users: ~12

Offline

#23 2012-10-14 13:10:03

Sawakita
Administrator
Registered: 2010-10-16
Post 320/364

Re: How Do I Go About Fixing This Error?

Because you're not replacing any call with a jump. You're replacing a call+ret with just a jump.
We will have:

jp $XXYY      C3 YY XX

instead of:

call $XXYY    CD YY XX
ret           C9

You can see, 3 bytes are used if we use a jump, four bytes are used if we use a call+ret.

And what follows is my attempt at explaining why we can use jump instead of call+ret.

First:
- PC (Program Counter) is a CPU register that contains the address of the next instruction to execute.
- SP (Stack Pointer) is a register that points to the top of the stack (i.e. to the address that will be popped from stack on a ret, reti or pop instruction)

Consider three nested routines: Func1, Func2 and Func3.

Func1:
[4000]    ; do some stuff
[4100]    call Func2
[4103]    ; do more stuff
[4200]    ret

Func2:
[5000]    ; do some stuff
[5100]    call Func3
[5103]    ret

Func3:
[6000]    ; do some stuff
[6100]    ret

When Func1 calls Func2 the address 4103 is pushed onto the stack:

PC = 5000

Stack:
[DFF2-DFF3]    xxxx
[DFF0-DFF1]    4103    ← SP points here

When Func2 calls Func3 the address 5103 is pushed onto the stack:

PC = 6000

Stack:
[DFF2-DFF3]    xxxx
[DFF0-DFF1]    4103
[DFEE-DFEF]    5103    ← SP points here

When Func3 returns to Func2 the address 5103 is popped from the stack:

PC = 5103

Stack:
[DFF2-DFF3]    xxxx
[DFF0-DFF1]    4103    ← SP points here
[DFEE-DFEF]    5103

The instruction that immediately follows is ret. So, again, when Func2 returns to Func1 the address 4103 is popped from the stack:

PC = 4103

Stack:
...
[DFF2-DFF3]    xxxx    ← SP points here
[DFF0-DFF1]    4103
[DFEE-DFEF]    5103

Now, sinse the only code of Func2 after call Func3 is ret we can simplify the code to just jump to Func3. What are the consequences? When Func3 will return it will not return to Func2, but to Func2's caller, i.e. Func1.

STEP 1: Func1 calls Func2.
PC = 5000
Stack:
[DFF2-DFF3]    xxxx
[DFF0-DFF1]    4103    ← SP points here


STEP 2: Func2 jumps to Func3.
PC = 6000
Stack:
[DFF2-DFF3]    xxxx
[DFF0-DFF1]    4103    ← SP still points here


STEP 3: Func3 returns.
PC = 4103
Stack:
[DFF2-DFF3]    xxxx    ← SP points here
[DFF0-DFF1]    4103

Note: all values used are just examples.

Offline

#24 2012-10-14 15:26:43

Danny-E 33
Administrator
Registered: 2012-06-09
Post 171/1,030

Re: How Do I Go About Fixing This Error?

Oh, gosh. That makes perfect sense! Thank you.
Sorry for not seeing it clearly earlier.


Red Hack: Pokémon Prototype

Total number of registered users: 7000+
Total number of active users: ~12

Offline

Board footer

Powered by FluxBB