RNG Manipulation in FireRed/LeafGreen: Wild Pokémon Supported in RNG Reporter 9.93

Status
Not open for further replies.

RockinX

My goal in life is to make everyone cringe with my puns
is a Contributor Alumnus
Just wondering, is there a chance that timing is different in Japanese carts? I may want to try it in my Japanese LeafGreen cartridge.
 

Zari

What impossible odds?
is a Contributor Alumnus
Just wondering, is there a chance timing is different in Japanese carts? I may want to try it in my Japanese LeafGreen cartridge.
My guess is that they would be the same, but I can't say with 100% certainty that they (since I own only american retail and no japanese carts :/)
 

RockinX

My goal in life is to make everyone cringe with my puns
is a Contributor Alumnus
Well, given that the way the game seeds the RNG is the same in Japanese 4th gen carts, it's more than likely that this is the case for FRLG as well.
 
I remember back in Emerald research there was speculation that the timer ran slightly differently on different systems, so it's interesting to see something like this emerge! I take it from your latest post that theoretically, different seed groupings should be available on the DS as opposed to the GBA due to the difference in the system clock? If so, that widens the total pool of seed groupings available, which is certainly advantageous for manipulators with more than one system.
 

Zari

What impossible odds?
is a Contributor Alumnus
they're all exactly the same. if it's different it's a result of your gba acting strangely. or if you're using a ds you'll get different results too. timer1 runs at 16.78mhz on a gba and 33.something mhz on a ds.

e: the F(system clock) that timer1 runs against is the speed of the arm7 cpu in both cases. so.. that's why it's different. it's moving at the speed of the cpu.
I've tested both and the results are mostly the same (some seeds are easier to hit than others and vice versa). I think this is because the ds clock moves at double the speed of the gba clock (or whatever, arm7 cpu), so hitting the same seeds for both shouldn't be a problem.

-------


As I've already caught mewtwo, I've had to make do with zapdos, but that's not troubling me in the slightest. One thing that IS troubling me, however, is that my frame seems to be dependent upon how long I stay in the "recollection of your past events" scenes. I dunno if this has happened to anyone else, but once I get out of the recollections my frame seems to just stop. It's not the quiet/noisy area problem, I know that much, but getting a frame of 308 when my target is 1852 (timed with eontimer) is just plain weird.

On another note, I've discovered some additional seed groupings (and hit some old ones as well), but I'll edit those in later when I have more data.
 

Bond697

Dies, died, will die.
You know what, nevermind about the changed timer. The info I was given was wrong. The NDS' ARM7TDMI is clocked at 16.78mhz in GBA mode. Unless GBATek is wrong, which I doubt.
 
How do I quickly find my seed from the information I have about the Pokemon I caught?

The FRLGSeedFinder requires me to type in a Seed. So I am confused.
 
How do I quickly find my seed from the information I have about the Pokemon I caught?

The FRLGSeedFinder requires me to type in a Seed. So I am confused.
does inputting the necessary criteria in pokerng old version then transferring the seed results along with the nature and ivs on rngreporter work? if not, idk.
 
Well I worked out how to do it. It's just confusing how the word Seed means 2 different things.

So when trying to find an initial seed you commonly hit do this (v9.81):

1. Capture a high level Pokemon.
2. Check it's IVs using Metal kid.
3. Go on to the RNG Reporter (9.81).
4. Click '4th Gen Tools' at the top.
5. Click 'Calculate PID from IVs'.
6. Type in the Nature and IVs of the Pokemon you caught.
7. Find which of the results it the correct Method. (Method 1 for stationary Pokemon as in first post, Method 2 for wild Pokemon)
8. Type out the Seed that was generated into the App attached to the first post.
9. One of the new 4 hex-digit long Seeds is your Initial Seed. (4 hex-digits is 2-bytes). The Seeds starting: 54... 30... 0b... or 9e... are probably yours.

imo Smogon has good guides but need to be written simpler.

e.g. The bit about catching a Pokemon and checking it's IVs with the equations and calling it TCheck is basically this:

If you're trying to get a high frame and have time to check IVs then:
1. Capture a wild Pokemon with Sweet Scent while your timer is running waiting for the right frame.
2. Follow the steps above for finding your Initial Seed.
3. If you hit the right Seed then carry on waiting. If it's wrong don't wait around and start again.

Note: It might be a good idea to see how long it takes you to check IVs so you know if you have enough time to verify your Initial Seed before your Target Frame appears.
 

Nix_Hex

Uangaana kasuttortunga!
is a Site Content Manager Alumnusis a Forum Moderator Alumnusis a Researcher Alumnusis a Top Contributor Alumnusis a Battle Simulator Moderator Alumnus
Those instructions are actually stated quite clearly in the OP. Just wanted to add, however, that if you get a seed that's not in one of those groups, pay attention to the frame. Usually, if you mash through the windows and encounter the pokemon right away, it's between 500 to 600 frames. It's not unusual to find whack ass seeds, and they're pretty discouraging to get, but yes, most of the times you will find seeds in the groupings you listed.
 
I don't think sweet scenting to check your seed before hitting your target frame will work.

While you're sweet scenting, several frames are skipped, and then the game enters noisy mode when you enter a battle. So using this method to check your seed before a long wait is unfeasible.

Zari picked a small enough frame so that he wouldn't have to check his seed before battling Mewtwo, so he probably didn't run into this problem.
 

Kaphotics

Remodeling Kitchens
is a Top Researcher Alumnusis a Top Contributor Alumnus
I don't think sweet scenting to check your seed before hitting your target frame will work.

While you're sweet scenting, several frames are skipped, and then the game enters noisy mode when you enter a battle. So using this method to check your seed before a long wait is unfeasible.

Zari picked a small enough frame so that he wouldn't have to check his seed before battling Mewtwo, so he probably didn't run into this problem.
Not exactly, the whole "Noisy" phenomenon is the Method H at work, essentially Method K.

It's problematic to trace back to what frame you had originally hit, but with enough work it's possible.

edit: fine, "noisy" just means that the vblank advancement isnt the only thing advancing. There's NPCs, battles, wild cries, etc.
 
What I understand by "noisy" is that every other frame is skipped. As in, the game is running at pseudo-120 fps. I do see where Method H comes in, since the frames do jump when I use sweet scent, but during a battle, the game seems to be skipping every other frame, what I understand to be "noisy."

My point was that if you're trying this on a cart, and you have a 16 min spread, you can't check your seed, and then expect to hit the right frame 16 minutes in. Sure, you'll know your seed, but you'll probably be several thousand frames off.
 

Bond697

Dies, died, will die.
Not exactly, the whole "Noisy" phenomenon is the Method H at work, essentially Method K.

It's problematic to trace back to what frame you had originally hit, but with enough work it's possible.
no it's not at all. pid restrictions don't cause the rng to advance 2 at a time when in battle/not doing anything but walking around.
 

Bond697

Dies, died, will die.
RSE/FRLG Method Determination
-------------------------------

So I've been sitting on this for about a week tryng to decide how to go about making this post and having it be at least mostly understandable to everyone. I don;t think that I quite can, so we're just going to have to go with this.

First, let's consider pokemon creation:

This is Method H in a nutshell:(the numbers are the locations in the ROM where the routines run from)
-------------------------------
080B5144 Battle test
080B4AC8 Encounter slot
080B4C74 Set level
080B4E2A Sync(bool)
080B4E4C Nature setup/decision(skip if(sync() == TRUE))
08067E96 PKM building routine(test temp PIDs, make IVs on success)

Now, obviously, all of those decisions need to be made using the RNG. The way Gamefreak decided to handle that was through a very simple method of taking the current result of their rand() function and using modulus to produce a number within the range they wanted. A number mod another number produces a value between 0 and the second number minus 1. So, a %(mod) b will result in a value between 0 and b-1, so it's very simple and it works. If you need a number between 1 and 100. just do a % 100 and you get results between 0 and 99, so 100 numbers. Anyway, this is used for basically everything. And all of these modulus operations are done with one specific routine:

082E7BE0 Modulus routine- r0 % r1, return result in r0

**As a quick refresher, the way Method H/J/K(basically) work is that the game takes a seed and does (seed >> 16) % 0x19. Then it starts making PIDs and doing PID % 0x19 and it keeps making PIDs until it gets a match to that original (seed >> 16) % 0x19.**

What this would look like in C(the game is written in the C programming language) is something like:

ushort getMod(ushort random, int m)
{return (ushort)(random % m);}

So, knowing that, why do we need to dwell on the formulas and modulus? Because they themselves are the root of the issue that causes the methods to occur. This is an excerpt from an ARM Application Note(#34, ARM DAI #0034A) regarding using division and (by association) the modulus operation:


The ARM instruction set does not provide integer division. Divisions are typically
implemented by calling a C-library function (__rt_sdiv for signed and __rt_udiv for
unsigned division). Depending on the numerator and denominator, a 32-bit division takes
20-140 cycles. The division function takes a constant time plus a time for each bit to
divide:

Time(numerator / denominator)
= C0 + C1 * log2(numerator / denominator) =
= C0 + C1 * (log2(numerator) - log2(denominator)).
The current version takes about 20 + 4.3N cycles.

As division is an expensive operation, it is desirable to avoid it where possible. Sometimes
expressions can be rewritten so that a division becomes a multiplication. For example,
(x / y) > z can be rewritten as x > (z * y) if it is known that y is positive and y * z
fits in an integer.

It is best to use unsigned division by ensuring that one of the operands is unsigned, as this
is faster than signed division. This applies both to the division subroutines and to divisions
by a power of two (see 3.2 Division and remainder by powers of two).
Now, from looking over the code, we can conclude that even though Gamefreak ignored ARM's advice and used modulus anyway, they didn't take precautions to keep the code up to speed. They also likely didn't use those library functions that would make 32-bit division only 20-140 cycles. Their mod function uses(on average) 270 instructions to do PID % 0x19. If you follow the routine out and watch the number of cycles used, it comes out to roughly 750(!!) on average.

That's right, 4 times as many cycles used (on average) per computation when doing out Method H and checking to see if PID % 0x19 matches (seed >> 16) % 0x19.

Now, to further compare how bloated this is, let's compare the gen 3 modulus function with the divmod used in gen 4 and 5

Gen 3 modulus function:

Code:
082E7BE0 2900     cmp     r1,#0x0
082E7BE2 D058     beq     #0x82E7C96
082E7BE4 2301     mov     r3,#0x1
082E7BE6 4288     cmp     r0,r1
082E7BE8 D200     bcs     #0x82E7BEC
082E7BEA 46F7     mov     r15,r14
082E7BEC B410     push    {r4}
082E7BEE 2401     mov     r4,#0x1
082E7BF0 0724     lsl     r4,r4,#0x1C
082E7BF2 42A1     cmp     r1,r4
082E7BF4 D204     bcs     #0x82E7C00
082E7BF6 4281     cmp     r1,r0
082E7BF8 D202     bcs     #0x82E7C00
082E7BFA 0109     lsl     r1,r1,#0x4
082E7BFC 011B     lsl     r3,r3,#0x4
082E7BFE E7F8     b       #0x82E7BF2
082E7C00 00E4     lsl     r4,r4,#0x3
082E7C02 42A1     cmp     r1,r4
082E7C04 D204     bcs     #0x82E7C10
082E7C06 4281     cmp     r1,r0
082E7C08 D202     bcs     #0x82E7C10
082E7C0A 0049     lsl     r1,r1,#0x1
082E7C0C 005B     lsl     r3,r3,#0x1
082E7C0E E7F8     b       #0x82E7C02
082E7C10 2200     mov     r2,#0x0
082E7C12 4288     cmp     r0,r1
082E7C14 D300     bcc     #0x82E7C18
082E7C16 1A40     sub     r0,r0,r1
082E7C18 084C     lsr     r4,r1,#0x1
082E7C1A 42A0     cmp     r0,r4
082E7C1C D305     bcc     #0x82E7C2A
082E7C1E 1B00     sub     r0,r0,r4
082E7C20 469C     mov     r12,r3
082E7C22 2401     mov     r4,#0x1
082E7C24 41E3     ror     r3,r4
082E7C26 431A     orr     r2,r3
082E7C28 4663     mov     r3,r12
082E7C2A 088C     lsr     r4,r1,#0x2
082E7C2C 42A0     cmp     r0,r4
082E7C2E D305     bcc     #0x82E7C3C
082E7C30 1B00     sub     r0,r0,r4
082E7C32 469C     mov     r12,r3
082E7C34 2402     mov     r4,#0x2
082E7C36 41E3     ror     r3,r4
082E7C38 431A     orr     r2,r3
082E7C3A 4663     mov     r3,r12
082E7C3C 08CC     lsr     r4,r1,#0x3
082E7C3E 42A0     cmp     r0,r4
082E7C40 D305     bcc     #0x82E7C4E
082E7C42 1B00     sub     r0,r0,r4
082E7C44 469C     mov     r12,r3
082E7C46 2403     mov     r4,#0x3
082E7C48 41E3     ror     r3,r4
082E7C4A 431A     orr     r2,r3
082E7C4C 4663     mov     r3,r12
082E7C4E 469C     mov     r12,r3
082E7C50 2800     cmp     r0,#0x0
082E7C52 D003     beq     #0x82E7C5C
082E7C54 091B     lsr     r3,r3,#0x4
082E7C56 D001     beq     #0x82E7C5C
082E7C58 0909     lsr     r1,r1,#0x4
082E7C5A E7D9     b       #0x82E7C10
082E7C5C 240E     mov     r4,#0xE
082E7C5E 0724     lsl     r4,r4,#0x1C
082E7C60 4022     and     r2,r4
082E7C62 D101     bne     #0x82E7C68
082E7C64 BC10     pop     {r4}
082E7C66 46F7     mov     r15,r14
Notice how it relies on expensive(in cycles) loops and branches in order to accomplish its goal.


Gen 4/5 divmod:

Code:
:0209C2C8 E3510000 cmp r1,#0x0
:0209C2CC 012FFF1E bxeq r14 
:0209C2D0 E1500001 cmp r0,r1
:0209C2D4 31A01000 movcc  r1,r0			
:0209C2D8 33A00000 movcc  r0,#0x0		
:0209C2DC 312FFF1E bxcc r14 
:0209C2E0 E3A0201C mov  r2,#0x1C		
:0209C2E4 E1A03220 mov  r3,r0,lsr #0x4		
:0209C2E8 E1510623 cmp r1,r3,lsr #0xc
:0209C2EC D2422010 suble  r2,r2,#0x10
:0209C2F0 D1A03823 movle  r3,r3,lsr #0x10		
:0209C2F4 E1510223 cmp r1,r3,lsr #0x4
:0209C2F8 D2422008 suble  r2,r2,#0x8
:0209C2FC D1A03423 movle  r3,r3,lsr #0x8		
:0209C300 E1510003 cmp r1,r3
:0209C304 D2422004 suble  r2,r2,#0x4
:0209C308 D1A03223 movle  r3,r3,lsr #0x4		
:0209C30C E1A00210 mov  r0,r0,lsl r2		
:0209C310 E2611000 rsb  r1,r1,#0x0
:0209C314 E0900000 adds r0,r0,r0
:0209C318 E0822082 add  r2,r2,r2,lsl #0x1
:0209C31C E08FF102 add  r15,r15,r2,lsl #0x2
:0209C320 E1A00000 nop(mov r0,r0)mov  r0,r0			
:0209C324 E0B13083 adcs r3,r1,r3,lsl #0x1
:0209C328 30433001 subcc  r3,r3,r1
:0209C32C E0B00000 adcs r0,r0,r0
:0209C330 E0B13083 adcs r3,r1,r3,lsl #0x1
:0209C334 30433001 subcc  r3,r3,r1
:0209C338 E0B00000 adcs r0,r0,r0
:0209C33C E0B13083 adcs r3,r1,r3,lsl #0x1
:0209C340 30433001 subcc  r3,r3,r1
:0209C344 E0B00000 adcs r0,r0,r0
:0209C348 E0B13083 adcs r3,r1,r3,lsl #0x1
:0209C34C 30433001 subcc  r3,r3,r1
:0209C350 E0B00000 adcs r0,r0,r0
:0209C354 E0B13083 adcs r3,r1,r3,lsl #0x1
:0209C358 30433001 subcc  r3,r3,r1
:0209C35C E0B00000 adcs r0,r0,r0
:0209C360 E0B13083 adcs r3,r1,r3,lsl #0x1
:0209C364 30433001 subcc  r3,r3,r1
:0209C368 E0B00000 adcs r0,r0,r0
:0209C36C E0B13083 adcs r3,r1,r3,lsl #0x1
:0209C370 30433001 subcc  r3,r3,r1
:0209C374 E0B00000 adcs r0,r0,r0
:0209C378 E0B13083 adcs r3,r1,r3,lsl #0x1
:0209C37C 30433001 subcc  r3,r3,r1
:0209C380 E0B00000 adcs r0,r0,r0
:0209C384 E0B13083 adcs r3,r1,r3,lsl #0x1
:0209C388 30433001 subcc  r3,r3,r1
:0209C38C E0B00000 adcs r0,r0,r0
:0209C390 E0B13083 adcs r3,r1,r3,lsl #0x1
:0209C394 30433001 subcc  r3,r3,r1
:0209C398 E0B00000 adcs r0,r0,r0
:0209C39C E0B13083 adcs r3,r1,r3,lsl #0x1
:0209C3A0 30433001 subcc  r3,r3,r1
:0209C3A4 E0B00000 adcs r0,r0,r0
:0209C3A8 E0B13083 adcs r3,r1,r3,lsl #0x1
:0209C3AC 30433001 subcc  r3,r3,r1
:0209C3B0 E0B00000 adcs r0,r0,r0
:0209C3B4 E0B13083 adcs r3,r1,r3,lsl #0x1
:0209C3B8 30433001 subcc  r3,r3,r1
:0209C3BC E0B00000 adcs r0,r0,r0
:0209C3C0 E0B13083 adcs r3,r1,r3,lsl #0x1
:0209C3C4 30433001 subcc  r3,r3,r1
:0209C3C8 E0B00000 adcs r0,r0,r0
:0209C3CC E0B13083 adcs r3,r1,r3,lsl #0x1
:0209C3D0 30433001 subcc  r3,r3,r1
:0209C3D4 E0B00000 adcs r0,r0,r0
:0209C3D8 E0B13083 adcs r3,r1,r3,lsl #0x1
:0209C3DC 30433001 subcc  r3,r3,r1
:0209C3E0 E0B00000 adcs r0,r0,r0
:0209C3E4 E0B13083 adcs r3,r1,r3,lsl #0x1
:0209C3E8 30433001 subcc  r3,r3,r1
:0209C3EC E0B00000 adcs r0,r0,r0
:0209C3F0 E0B13083 adcs r3,r1,r3,lsl #0x1
:0209C3F4 30433001 subcc  r3,r3,r1
:0209C3F8 E0B00000 adcs r0,r0,r0
:0209C3FC E0B13083 adcs r3,r1,r3,lsl #0x1
:0209C400 30433001 subcc  r3,r3,r1
:0209C404 E0B00000 adcs r0,r0,r0
:0209C408 E0B13083 adcs r3,r1,r3,lsl #0x1
:0209C40C 30433001 subcc  r3,r3,r1
:0209C410 E0B00000 adcs r0,r0,r0
:0209C414 E0B13083 adcs r3,r1,r3,lsl #0x1
:0209C418 30433001 subcc  r3,r3,r1
:0209C41C E0B00000 adcs r0,r0,r0
:0209C420 E0B13083 adcs r3,r1,r3,lsl #0x1
:0209C424 30433001 subcc  r3,r3,r1
:0209C428 E0B00000 adcs r0,r0,r0
:0209C42C E0B13083 adcs r3,r1,r3,lsl #0x1
:0209C430 30433001 subcc  r3,r3,r1
:0209C434 E0B00000 adcs r0,r0,r0
:0209C438 E0B13083 adcs r3,r1,r3,lsl #0x1
:0209C43C 30433001 subcc  r3,r3,r1
:0209C440 E0B00000 adcs r0,r0,r0
:0209C444 E0B13083 adcs r3,r1,r3,lsl #0x1
:0209C448 30433001 subcc  r3,r3,r1
:0209C44C E0B00000 adcs r0,r0,r0
:0209C450 E0B13083 adcs r3,r1,r3,lsl #0x1
:0209C454 30433001 subcc  r3,r3,r1
:0209C458 E0B00000 adcs r0,r0,r0
:0209C45C E0B13083 adcs r3,r1,r3,lsl #0x1
:0209C460 30433001 subcc  r3,r3,r1
:0209C464 E0B00000 adcs r0,r0,r0
:0209C468 E0B13083 adcs r3,r1,r3,lsl #0x1
:0209C46C 30433001 subcc  r3,r3,r1
:0209C470 E0B00000 adcs r0,r0,r0
:0209C474 E0B13083 adcs r3,r1,r3,lsl #0x1
:0209C478 30433001 subcc  r3,r3,r1
:0209C47C E0B00000 adcs r0,r0,r0
:0209C480 E0B13083 adcs r3,r1,r3,lsl #0x1
:0209C484 30433001 subcc  r3,r3,r1
:0209C488 E0B00000 adcs r0,r0,r0
:0209C48C E0B13083 adcs r3,r1,r3,lsl #0x1
:0209C490 30433001 subcc  r3,r3,r1
:0209C494 E0B00000 adcs r0,r0,r0
:0209C498 E0B13083 adcs r3,r1,r3,lsl #0x1
:0209C49C 30433001 subcc  r3,r3,r1
:0209C4A0 E0B00000 adcs r0,r0,r0
:0209C4A4 E1A01003 mov  r1,r3			
:0209C4A8 E12FFF1E bx r14
Notice that this one uses a switch statement to decide how many iterations to go through and then only starts where it needs to and uses cheap add with carry(and set status flags) and subtract if carry clear instructions to accomplish the division. This one also returns the result of r0/r1 and r0%r1, which is more functionality than the one in gen 3 as well.Also, if you recall, I said that the gen 3 mod function used roughly 270 instructions on average and 750 cycles.

The gen 4/5 divmod uses roughly 47-53 instructions on average and roughly 50-55 cycles total. For both division and modulus together. Each of those adcs and subcc instructions is only 1 cycle vs. the 3 for each branch/loop in gen 3. It also never loops, cutting out expensive branching. I'm of the opinion that this is an unrolled loop with a switch statement in front for proper setup and is quite likely custom assembly written by someone at Gamefreak. It's extremely efficient, possibly because the one in gen 3 was so bad.

So now we know that the functions to create test PIDs in gen 3 was extremely poor in terms of speed. We also know how Method H works and why it causes so much slowdown.(slow calculation function) So knowing all of that, how does this relate to methods 2/3/4, etc? It isn't fast enough to always finish before video rendering functions(interrupts, really) break up the PID building function.
 

Bond697

Dies, died, will die.
Video
------

The GBA has some video functions that run 60x/second. They clear the screen, draw the screen, render video, etc. They are also interrupts and can run at any time if the GBA needs to.

One is called VBlank. Wikipedia defines VBlank as "The vertical blanking interval (VBI), also known as the vertical interval or VBLANK, is the time difference between the last line of one frame or field of a raster display, and the beginning of the first line of the next frame." Basically, it's an interval of time when nothing is drawn to the screen, though functiuons can continue to run. It isn't a thing, just a poeriod of time. In fact, the RNG runs once per VBlank interrupt period as part of the VBlank callback function. This means that the RNG runs(needlessly) 60x/second, not just when it is needed.

VCOUNT is an important I/O register that keeps track of the current vertical line that is being drawn to render the screen. The GBA's LCD screen is 240px wide by 160px high, so VCOUNT would be counting 160 lines from 0-159. Once VCOUNT is filled up, the screen must be cleared to render the next frame.

Let's have a look at a diagram:




This is a chart of the LCD and the blanking intervals. As we can see the GBA is 240x160, but there looks to be extra room around the screen to the right and below. That is the period of time when VBlank/HBlank are running. Lines are still being drawn, they just aren't being rendered onscreen. As we can see in the red-boxed items, the normal number of lines is 160(thus what VCOUNT normally counts to), but there are 68 extra horizontal lines being drawn that aren't showing up on the screen because nothing is being written to it. During this 4.994 milliseconds, the VBlank function is running. The RNG advances once, as well. Since the screen is 160px tall and 68 extra lines are being drawn, we can conclude that the total number of horizontal lines being drawn per vertical interval is 228 and it occurs in 16.743ms(11.749 + 4.994)

Now, as I said, VBlank is an interrupt. That means that it can run at any time. What triggers it, though? VCOUNT is the sort of "monitor" for the horizontal lines being drawn vertically. When it hits 0xA0(160), that means the screen is full and VBlank must occur to clear the screen for the next frame of video. So, VCOUNT hits 160, the interrupt handler fires, VBlank runs no matter what to keep everything onscreen moving fluidly, and then it bounces back to what it was doing. ,This means that the game has 11.749ms to fully create a pokemon using those extremely slow PID creation functions and all the modulus calculations. If that doesn't happen, VBlank will occur and the RNG will advance once and throw off the pokemon being made.

So now we know about the VBlank period and VCOUNT and interrupts and the IRQ handler, etc. Let's go ahead and tie this all together.

If you look in a debugger(VBA-SDL-H, VBA-M, or No$GBA) and keep up, you'll notice that during the PID creation function where the game is wasting huge numbers of cycles, the VBlank interrupt will occasionally fire and advance the RNG by 1. If you notice that happening, stop the debugger and have a look at the VCOUNT register and you will see that it is always 0xA0). So then, what precisely is happening?

Due to the slow speed of the function used for basically all of Method H(mainly the PID creation, though- little numbers into big numbers is very rough on it), VCOUNT fills up and thus, another video frame must be rendered. Before the new frame is rendered, the VBlank interrupt fires and wipes the screen, **also causing the RNG to advance.** Occasionally, VCOUNT will fill up as the final PID is being built and will cause an advancement between PID halves or between PID and IVs or IV halves. This is how the other methods are created. If the pokemon creation function isn't able to finish in 11.749ms(provided it has the full interval to work with), then the RNG will advance with VBlank, causing one of the other methods.

In conclusion, we can say that the other methods(2-4, at least) are caused by Gamefreak's own poor programming practice and technique. The Application Note I referred to(#34) is from roughly 1998, so there is no reason to not have read it and implemented it ASAP. It is clear that gamefreak was aware of the issue, as gen 4 and 5 have an extremely fast(probably custom) div/mod algorithm implemented and it is 8-10 times as fast.



For reference
-------------

The offsets for Method H came from my work here:

http://projectpokemon.org/wiki/Notable_Breakpoints


GBATek is still the best and biggest source of info for the GBA and NDS:

http://pokemon.thundaga.com/research/gbatek.htm


The diagram and other great info came from romhacking.net's GBA programming manual:

http://www.romhacking.net/documents/228/


GBADev and its forums is a great source of dev info(and general info) for both the GBA and NDS:

http://gbadev.org/, http://forum.gbadev.org/

e:
How could I forget the ARM Application Note set?

http://www.nalanda.nitc.ac.in/industry/appnotes/arm/appsnotes/index.htm
 

Bond697

Dies, died, will die.
i just want to make a note here that i was wrong about the gba routine using 540 cycles on average. i was using the wrong clock cycle filter in no$debug. if you set it to "cycles"
(and not "detailed formula") to show the number of cycles the current instruction uses, you can get a more accurate count. long story short, i was undercounting the number of cycles that function uses.

the gen 3 modulus function seems to use about 750(!!) cycles per run, while the gen 4/5 divmod uses 55 or so. so the gen 3 mod function was taking even more time than i thought, which just makes my point even stronger.
 

Bond697

Dies, died, will die.
and while i'm thinking of it, those times when the rng seems to be skipping a seed, it actually isn't. the rng is doing 2 worthless advancements per frame instead of 1 at those times. some genius at gamefreak thought that adding another worthless rand() call at the top of a different function was a great idea.

can someone name a place where the rng advances double-time outside of battle? i'd like to see if it's the same for that and (preferably) what exactly causes it.
 
and while i'm thinking of it, those times when the rng seems to be skipping a seed, it actually isn't. the rng is doing 2 worthless advancements per frame instead of 1 at those times. some genius at gamefreak thought that adding another worthless rand() call at the top of a different function was a great idea.

can someone name a place where the rng advances double-time outside of battle? i'd like to see if it's the same for that and (preferably) what exactly causes it.

Apparently, Cerulean Cave is a "noisy" area as long as you don't load your game from inside the cave. I'm not 100% sure so it's worth double checking.
 
Hi again, Bond697, and thanks once more for your information-rich posts. School has started once again, so I don't have the time to parse these latest developments, so I have instead put a prominent link in the first post to your two "mega" posts. If there continue to be large posts like this during the school year, I may continue posting links in this fashion instead of attempting to pick out the relevant information—unless anyone feels that would be doing the thread too much of a disservice, that is.
 

Bond697

Dies, died, will die.
i should also probably mention that those walls of text above are also responsible for split breeding spreads.

also, the pokemon gba games were gamefreak's first foray into arm/programming for the arm cpu series, so i guess it's sort of understandable that it didn't come out great?
 
i should also probably mention that those walls of text above are also responsible for split breeding spreads.

also, the pokemon gba games were gamefreak's first foray into arm/programming for the arm cpu series, so i guess it's sort of understandable that it didn't come out great?
Well, they got something really freakin' random alright, but not in the way they intended. :)

On a less related note, I'm having trouble getting my seed from FRLG Seedfinder, and I was wondering if anyone could either point me towards a written guide for it (if there is one) or give me a set of steps to go through to get accurate results. The problem I'm having is that the seed output that Seedfinder gives me doesn't fit with the spreads RNG reporter says I should be getting and the results I'm actually getting on. Could someone maybe pm me about this? (I don't wanna hijack the thread.)
 

Bond697

Dies, died, will die.
i just noticed that the gen 4/5 divmod uses a custom assembly routine that checks 1 bit at a time starting at 31 and ending at 0. they use a switch statement to decide which bit to start on, then go a bit at a time. i'm not sure that gamefreak wrote this, though. have a look here:

http://www.peter-teichmann.de/adiv1e.html

that's their divmod routine, in arm, just without the switch and written for strongARM, not arm7tdmi or arm946-es.
 

Bond697

Dies, died, will die.
i think this should help (some) people understand what's going on:

Code:
#include <nds.h>
#include <stdio.h>
#include <timers.h>

#define REG_GXSTAT (*(vu32*)0x04000600)
#define REG_T0	   (*(vu16*)0x04000100)
#define REG_T1     (*(vu16*)0x04000104)
#define REG_T2     (*(vu16*)0x04000108)
#define REG_T3     (*(vu16*)0x0400010C)

volatile int frame = 0;

typedef unsigned short     u16_t;
typedef unsigned int       u32_t;
typedef unsigned long long u64_t;

u32_t rand64();
u64_t mul64(u64_t num1, u64_t num2);
u16_t rand32();

u64_t seed = 0;	
u32_t seed32 = 0;
	
[b][i]
//---------------------------------------------------------------------------------
void Vblank() {
//---------------------------------------------------------------------------------
	frame++;
	rand32();	
	rand64();
}
[/b][/i]
	
//---------------------------------------------------------------------------------
int main(void) {
//---------------------------------------------------------------------------------
lcdSwap();


	irqSet(IRQ_VBLANK, Vblank);
	
	consoleDemoInit();
	
	int i = rand() % 4;
	
	switch(i)
		{
		case 0:
		TIMER_CR(0) = TIMER_ENABLE | TIMER_DIV_1;
		break;
		case 1:
		TIMER_CR(1) = TIMER_ENABLE | TIMER_DIV_64;
		break;
		case 2:
		TIMER_CR(2) = TIMER_ENABLE | TIMER_DIV_256;
		break;
		case 3:
		TIMER_CR(3) = TIMER_ENABLE | TIMER_DIV_1024;
		}
	
	iprintf("      Test\n\n");

	//iprintf("      %X \n", t0);
	
		u32_t gx = REG_GXSTAT + rand();
		seed = (u64_t)gx;
 
	while(1) {
			
		//timerStop(i);
		

		u16_t lc32 = rand32();
		u32_t lc64 = rand64();
		
		iprintf("\x1b[10;0HFrame: %u", frame);
	
		iprintf("\x1b[12;0H64-bit: %16llX", seed);
		
		iprintf("\x1b[14;0H64-bit return: %08X", lc64);
		
		iprintf("\x1b[16;0H32-bit: %08X", seed32);
		
		iprintf("\x1b[18;0H32-bit return: %04X", lc32);
	
	swiWaitForVBlank();
	}

	return 0;
}


u32_t rand64()
{

const u64_t a = 0x5d588b656c078965;
const u64_t c = 0x0000000000269ec3;

seed = mul64(seed, a) + c;

return (u32_t)(seed >> 32);
}

 
u64_t mul64(u64_t num1, u64_t num2)
{
	u64_t result = (num1 * num2);
	
	return result;
}

u16_t rand32()
{

const u32_t a = 0x41C64E6D;
const u32_t c = 0x00006073;
	
seed32 = (a * seed32) + c;	

return (u16_t)(seed32 >> 16);

}
this is just a small program that runs both the 3/4th gen rng and the 5th gen rng. if you notice, there'a a function there called vblank. anything in there will run in order when the vblank period starts. here's a link to the rom:

http://pokemon.thundaga.com/research/apps/vblank.nds

(note for mods: this is a program that i wrote and compiled using a free toolchain. no illegal/confidential applications were used to build this rom image. feel free to pm me for further clarification.)

now, if you run it, you'll see a frame counter, and both rngs running along with output to cut them in half like gamefreak does. what you're seeing here is vblank running at 60fps(since it's clearing the screen to let the next frame of video be drawn) and every time it runs, the rng advances once. this is exactly what gamefreak did and how the rng advances quickly in gen 3.
 
Status
Not open for further replies.

Users Who Are Viewing This Thread (Users: 1, Guests: 0)

Top