Binary ROM HackingNeed a helping hand or just want to talk about binary ROM hacks? Get comments and answers to any ROM Hacking-related problems, questions or thoughts you have here.
Well, taking a look at this code atm, I can tell that it doesn't have a return value. Instead, it seems to write into some sort of data structure/RAM pointer.
You'd be best off setting break points at these hard coded RAM offsets and seeing what they are. When they're written directly like this, rather than loaded from pointers, it means the RAM addresses are static. So if you can see what they hold, you can generalize what that RAM offset will always hold (in 99% of cases).
Thank's a lot for the time that you take
But I actually found a post of Chaos Rush and he said he tried to right value/change pointer and so on and nothing happen, it's probably hard coded as he said.
Sorry to make you waste your time :s
Ok now I have other question, see this post in the spoiler. I would like to adapt the following routine to emerald but if only I could understand why it exactly doing in Firered it would be very very useful. So, the routine :
Spoiler:
Quote:
Originally Posted by Chaos Rush
Natural Gift for FireRed:
Step 1: First, add this command in commands.bsh for Battle Script Pro (credit to KDS for first discovering this when he posted his Incinerate script):
Spoiler:
#command removeitem 0x6A 0x1 "Bank" 0x1
(this command to remove items is already in the game so you don't need to add code or anything, it's just unidentified by BSP)
Step 2:
Compile the following table and put it into free space, make sure you take note of the offset you put it in. We'll refer to this table as NATURAL GIFT TABLE:
When compiled, the format of this table will be [XX XX] [YY] [ZZ], with XXXX being the index number of the item, YY being the type that Natural Gift will change into, and ZZ being the base power. This table only covers the berries that are in Gen 3, so if you've added new berries then you'll have to add them into the table. MAKE SURE YOU LEAVE THE FOUR FF BYTES AT THE END, DO NOT OVERWRITE IT. (or change them to FE bytes, and in Step 3 modify the line "mov r4, 0xFF" to "mov r4, 0xFE").
Step 3:
Insert this routine into free space, and take note of its offset. We'll refer to this as ASM ROUTINE 1:
Spoiler:
.text
.align 2
.thumb
.thumb_func
.global test
main:
push {lr} Why don't we push r0-r5 ? since we are going to use these ?
ldr r0, .UserBank
ldr r1, .BattleData
ldrb r0, [r0] Here we load a byte from the 0x02023D6B, right ? But it's always 00 in battle when we attack, 01 when it's the opponent
mov r2, #0x58
mul r2, r0 So, if we mul r2 (=0x58) by r0 (=0, because we are attacking now), it's equal to 0, doesn't it ?
add r1, #0x2E Here okay, we looking for the address of the held item
add r1, r2 Why we're doing that here ?
ldrh r0, [r1] /*r0 should now have held item. r1 is the RAM offset of the held item*/ Ok too
cmp r0, #0x0 Check if don't holding item
beq Abort Go to abort and then fail script if so
ldr r2, .NaturalGiftTable Ok
mov r4, #0xFF Ok
loop:
ldrh r3, [r2] /*item ID*/ Load half word to take item ID in our table as Chaos Rush mentioned
ldrb r5, [r2, #0x3] Load type that correspond
cmp r5, r4
beq Abort /*fail if 4th byte is FF*/ Already explained
cmp r0, r3 COmpare if we olding the same item as the one loaded
beq Confirmed If so, go to confirmed
add r2, #0x4 Incrementation (is it the good term btw ?) to redo the loop
b loop
Confirmed:
ldrb r4, [r2, #0x2] /*Type*/ Here r4 is equal to FF, so, why are we doing that ? Ok I'm a dumb, but it should be ldrb r4, [r2, #0x3] or am I wrong ?
ldrb r5, [r2, #0x3] /*Power*/ Aren't we supposed to do ldrb r5, [r2, #0x4] ? Since the type was ldrb r5, [r2, #0x3]
ldr r0, .MemAddress Here we have loaded the .MemAddress, add 13, it seems to be just before the number of the attack of the pokemon (attack 1, attack 2, attack 3, attack 4) EDIT : According to JPAN's battle script document, it's indeed the location of current attack type, or rather the pointer to the address of type
ldr r0, [r0]
add r0, #0x13 So why add 0x13 ? as I said it's one byte before the number of our selected attack
strb r4, [r0] /*change Type*/ From what I found, this byte (MemAddress +0x13) is always 00, so why doing this would change the type of the attack ? EDIT : Ok, it's a pointer, not the direct address.
add r0, #0x7B The memaddress is load in r0, so we're going to change the type, but it's not 0x7B, the type's byte is further
strb r4, [r0]
ldr r0, .BasePower
strb r5, [r0] Is it here that we change base power ? Why use the same register ?
b Attack
Make sure you put in the offset of the Natural Gift table at the last line.
Step 4:
Now, insert this routine into free space as well, and take note of its offset. We'll refer to this as ASM ROUTINE 2:
Spoiler:
.text
.align 2
.thumb
.thumb_func
.global test
GetOpponentType:
push {lr}
ldr r0, .TargetBank
ldr r1, .BattleData
ldrb r0, [r0]
mov r2, #0x58
mul r2, r0
add r1, #0x21
add r1, r2
ldrb r0, [r1] /*r0 should now have opponent Type 1*/
ldrb r2, [r1, #0x1] /*r2 should now have opponent Type 2*/
GetNaturalGiftType:
ldr r1, .MemAddress
ldr r1, [r1]
add r1, #0x13
ldrb r1, [r1] /*r1 should now have modified Natural Gift type*/
ldr r3, .TypeChart
/*check attacking type*/
Check1:
ldrb r4, [r3] /*r4 has Type Chart attacking type*/
ldrb r5, [r3, #0x1] /*r5 has Type Chart defending type*/
ldrb r6, [r3, #0x2] /*r6 has Type Chart effectiveness*/
cmp r4, #0xFE
beq Abort
cmp r1, r4 /*check NG type with Attacking type*/
bne Recheck
cmp r0, r5 /*check opponent Type 1 with Defending type*/
beq Type1Match
cmp r2, r5 /*check opponent Type 2 with Defending type*/
beq OkNowCheckType1
add r3, #0x3
b Check1
OkNowCheckType1:
ldr r3, .TypeChart
b StillCheckinglol
/*we should be here if Type 2 was a match but Type 1 wasn't*/
StillCheckinglol:
ldrb r4, [r3] /*r4 has Type Chart attacking type*/
ldrb r5, [r3, #0x1] /*r5 has Type Chart defending type*/
ldrb r7, [r3, #0x2] /*r7 has Type Chart effectiveness*/
cmp r4, #0xFE
beq SameOutcome
cmp r1, r4 /*check NG type with Attacking type*/
bne Recheck3
cmp r0, r5 /*check opponent Type 1 Defending type*/
beq Type2Match
add r3, #0x3
b Check2
Type1Match:
ldr r3, .TypeChart
b Check2
Check2:
ldrb r4, [r3] /*r4 has Type Chart attacking type*/
ldrb r5, [r3, #0x1] /*r5 has Type Chart defending type*/
ldrb r7, [r3, #0x2] /*r7 has Type Chart effectiveness*/
cmp r4, #0xFE
beq SameOutcome
cmp r1, r4 /*check NG type with Attacking type*/
bne Recheck2
cmp r2, r5 /*check opponent Type 2 Defending type*/
beq Type2Match
add r3, #0x3
b Check2
WeakDamage:
ldr r0, .Outcome
mov r1, #0x04
strb r1, [r0]
b End
NoDamage:
ldr r0, .Outcome
mov r1, #0x08
strb r1, [r0]
b End
SuperDamage:
ldr r0, .Outcome
mov r1, #0x02
strb r1, [r0]
b End
Abort:
End:
pop {r0}
bx r0
.align 2
.Outcome: .word 0x02023DCC .TypeChart: .word 0x0824F050 /*make sure you replace this with the offset of your own type chart*/
.BattleData: .word 0x02023BE4
.TargetBank: .word 0x02023D6C
.MemAddress: .word 0x02023FE8
Note: If you have repointed your type chart, then you will have to put its new offset where I've bolded above.
Here's an explanation as to how this works:
So as you can see, "ASM ROUTINE 1" looks up the custom table we inserted and matches your held item to it (the move will fail if you're not holding an item or if you're holding an item that's not in the table), and then updates the type and base power accordingly. Now, with this routine alone, the damage and type calculations are correct, but the outcome message will always act as a regular move (regular as in not super-effective or not not-very-effective) unless if you don't have an item in the table (then it will just fail), and turns out the reason why is for some reason the outcome RAM offset (02023DCC) wasn't getting updated even though the battle engine should theoretically already have done so (if you have the routine be the first thing in the script). So what "ASM ROUTINE 2" does is that it looks up the type chart and the opponent's types and determines the outcome that way (and yes, I checked and double checked and triple checked with various berry & type combinations, so it works). It wasn't ideal, but it was necessary in order to get the correct message to show up.
"But Chaos, couldn't you just look at how Weather Ball and Hidden Power does it"? Well, I did (for anyone curious, the Weather Ball command's routine is at 0802D090, while the Hidden Power command's routine is at 0802B678), and what both do is that they update the value held by the pointer at 02023FE8(a dynamic memory location) + 0x13, usually the pointer at 02023FE8 leads to 02000010, so the type value is (usually) at 02000023. No matter what I tried with updating that value, it always played the normal hit sound (but would still do more or less damage depending on type, such as Electric type Natural Gift doing more damage to Pidgey than Water type Natural Gift), unless if it was a type immunity in which case it worked as expected. For testing reasons I tried giving a new test move Hidden Power's battle script ID, and turns out the correct sound effect doesn't play either (but it does with Hidden Power itself, even though I gave them the same battle script ID), and turns out the outcome offset was only being updated when using the actual Hidden Power, which leads me to believe that Hidden Power and Weather Ball's move ID's are hard-coded to update the outcome offset. So the whole purpose of "ASM ROUTINE 2" is to circumvent that. "ASM ROUTINE 2" manually looks up the type chart and what outcome it should be, and updates the outcome value at 02023DCC accordingly.
My question are principally for the first routine, I mean transform type/get base power. Not the table item.
In all likelihood I'll have question for the second one but for the moment, I'm sure the first one doesn't work correctly for me.
The most important to me is understand what is happening, so I put my question/how I understand in bold.
Last thing, I tried to find the RAM address for emerald and this is what I have
It does absolutly nothing, the attack remind regular
There is one good thing in this one, it actually change the type of the attack, but it's a little bit random. Didn't manage to do super effective damages.
EDIT : Actually damages are always neutral, and it's not a question of string displayed. I tested the damage and they are really neutral. I guess it load a value that is too high but can't figured out how to load the good (primary type of my poke) :
main:
push {lr}
ldr r0, .UserBank
ldr r1, .BattleData
ldrb r0, [r0]
mov r2, #0x58
mul r2, r0
add r1, #0x21
add r1, r0
ldrh r0, [r1]
ldrb r1, [r1]
ldr r0, .MemAddress
ldr r0, [r0]
add r0, #0x13 I said I don't know because of this part, to me it should be 0x8E, to go to the type of the attack used, if someone could explain me it would be awesome
strb r1, [r0]
pop {r0}
bx r0
I'm only trying to add five levels to the Pokémon's levels(in this instance youngster ben), but after running the script it wasn't working, and his levels remained what they were before. Any idea what's going wrong?
I'm only trying to add five levels to the Pokémon's levels(in this instance youngster ben), but after running the script it wasn't working, and his levels remained what they were before. Any idea what's going wrong?
I can't even tell what you are trying to do, but I'll do my best. It looks as though you are loading a ROM offset rather than an address. ROM is prefixed by 08/09, you have prefixed it with 00 (BIOS). In fact this address is invalid and will be ignored by VBA - the bios is only 16kb. Hardware will do weird things.
Next, you load two half words, add 5, then load the addresses again? Why? I think what you are trying to do is store to those addresses, but you'd need str, strh or strb to do that. If I'm right about these being ROM offsets, then you clearly don't understand the concept of ROM. You can't right to it. Ever. There are a few bytes of ROM address space reserved for GPIO for sensors, and this is the only exception.
Lastly, you're wasting stack space. Neither R0, R1 or LR need to be pushed.
I suggest you read some tutorials and look at existing code so you actually understand what you're doing, rather than fumbling around in the dark.
__________________
A Pokemon that is discriminated!
Support squirtle and make it everyone's favourite.
I can't even tell what you are trying to do, but I'll do my best. It looks as though you are loading a ROM offset rather than an address. ROM is prefixed by 08/09, you have prefixed it with 00 (BIOS). In fact this address is invalid and will be ignored by VBA - the bios is only 16kb. Hardware will do weird things.
Next, you load two half words, add 5, then load the addresses again? Why? I think what you are trying to do is store to those addresses, but you'd need str, strh or strb to do that. If I'm right about these being ROM offsets, then you clearly don't understand the concept of ROM. You can't right to it. Ever. There are a few bytes of ROM address space reserved for GPIO for sensors, and this is the only exception.
Lastly, you're wasting stack space. Neither R0, R1 or LR need to be pushed.
I suggest you read some tutorials and look at existing code so you actually understand what you're doing, rather than fumbling around in the dark.
Ah well, had to start somewhere, even if it was 'get good scrub'.
Thank you for the information on the ROM offsets. I actually had no idea that it had to be 08. And I think I understand that more now, thanks.
I inserted FBI's set party level routine in my rom and it worked perfectly.
Here's the routine:
Spoiler:
Quote:
Originally Posted by FBI agent
Set static party level
First of all, I need to mention that this routine works off percentage increases and decreases. So if you're planning to go from level 1 to 100 all in one go for 6 Pokemon it will take around 5 seconds to increment about 35 million experience points. The reason for the percentage increase is so that we don't go over. This was a pain to test, as in some occasions the percentage between levels 1-2 would be enough to set negative exp, and sometimes the levels 99 -100 would set exp over 100...it was just a huge pain. It now works off a lookup table and is quite spiffy now :3
How to insert:
Compile and insert into free space the following routine:
Usage:
setvar 0x8000 0x[Level you want Pokemon to be set to (in hex)]
callasm 0x[this routine +1]
That's it. I should mention, you should apply this as often as possible to speed up the computation time for next time.
However, I would like to create a variation of this.
I would simply like to be able to set level of pokes individually.
I am re-learning asm, had it in college, and I do understand most of it, but still inexperienced to do a whole routine on my own.
The way to solve my prob is simply have 2 inputs
1 - the index of the poke to set the level
2 - the level it shall be set to
And, I can't follow FBI's routine since I don't know where the pointers point to.
And also, would it be possible to set by experience points?
Ex. Lv 5 Pikachu with 50 extra exp
-set to Lv 30 Pikachu (0 extra exp)
-set back to Lv 5 with the 50 extra exp
Idea is temporarily set the Poke to a certain level.
I inserted FBI's set party level routine in my rom and it worked perfectly.
Here's the routine:
Spoiler:
However, I would like to create a variation of this.
I would simply like to be able to set level of pokes individually.
I am re-learning asm, had it in college, and I do understand most of it, but still inexperienced to do a whole routine on my own.
The way to solve my prob is simply have 2 inputs
1 - the index of the poke to set the level
2 - the level it shall be set to
And, I can't follow FBI's routine since I don't know where the pointers point to.
And also, would it be possible to set by experience points?
Ex. Lv 5 Pikachu with 50 extra exp
-set to Lv 30 Pikachu (0 extra exp)
-set back to Lv 5 with the 50 extra exp
Idea is temporarily set the Poke to a certain level.
Get yourself a copy of IDA and knizz's IDB for FireRed if you want to see what those addresses are. You can set Exp using the same function he uses there actually.
You just need to remove the loop in that code to do what you want.
FBI doesn't seem to label his addresses ever so it's understandable that you got stuck
0x2024029 - Number of pokemon in the party
0x2024284 - First party pokemon. This is an array of up to 6 entries long. Each entry is 100 (0x64) bytes
I assume the rest figures out the EXP needed to attain the target level and then recalculates the level based on EXP curves, but I haven't read it.
__________________
A Pokemon that is discriminated!
Support squirtle and make it everyone's favourite.
I modified FBI's routine to the one below and got it to work. However, I still would like to learn what the computation does, what does linker link to, etc, if anyone can elaborate. It doesn't feel comfortable running a program that works but you don't know how ~_~
Also, I'd like to have another input for the index of the pokemon to be changed (highlighted in bold)
What are the possible variables that can be used as input? aside from 0x8000 and what are their addresses?
Spoiler:
.text
.align 2
.thumb
.thumb_func
main:
push {r0-r7, lr} mov r7, #0x1 @load index 0-5 from a variable
I modified FBI's routine to the one below and got it to work. However, I still would like to learn what the computation does, what does linker link to, etc, if anyone can elaborate. It doesn't feel comfortable running a program that works but you don't know how ~_~
Also, I'd like to have another input for the index of the pokemon to be changed (highlighted in bold)
What are the possible variables that can be used as input? aside from 0x8000 and what are their addresses?
Spoiler:
.text
.align 2
.thumb
.thumb_func
main:
push {r4-r7, lr}
@figure out slot address of Pokemon given slot number
@r7 contains slot number 0-5
mov r7, #0x1
ldr r0, =(0x2024284)
mov r1, #0x64
mul r1, r1, r7
add r0, r0, [email protected]
mov r4, r0 @save address in r4
set_level:
@retrieve Pokemon's species, given address (calculated above)
mov r1, #0xB
ldr r3, = (0x803FBE8 +1) @get species
bl linker
Commented, and removed stupid pushes/pops. For explanations of what bl linker is, read my ASM tutorial about function calling :)
Please. Please. Please. Never use MUL unless you really need to. Powers of two can be expressed as LSL, which is both shorter and faster.
Code:
@ BAD FBI
mov r3, #0x4
mul r1, r1, r3
@ Good
lsl r1, #2
Quote:
Originally Posted by crehym
I modified FBI's routine to the one below and got it to work. However, I still would like to learn what the computation does, what does linker link to, etc, if anyone can elaborate. It doesn't feel comfortable running a program that works but you don't know how ~_~
Also, I'd like to have another input for the index of the pokemon to be changed (highlighted in bold)
What are the possible variables that can be used as input? aside from 0x8000 and what are their addresses?
Spoiler:
.text
.align 2
.thumb
.thumb_func
main:
push {r0-r7, lr} mov r7, #0x1 @load index 0-5 from a variable
The variables 0x8000 - 0x800F can be referenced by their memory address as expressed in the literal pool there (0x020270B8 + (0x8000 *2)). This can calculate the correct address for those variables. Other variables are DMA protected and thus it is recommended that you use the engine function to get their values. For simple script input, the 0x8000 series is better.
Again, look at the IDB - it will explain most of these questions about the locations of stuff.
__________________
A Pokemon that is discriminated!
Support squirtle and make it everyone's favourite.
Please. Please. Please. Never use MUL unless you really need to. Powers of two can be expressed as LSL, which is both shorter and faster.
Code:
@ BAD FBI
mov r3, #0x4
mul r1, r1, r3
@ Good
lsl r1, #2
The variables 0x8000 - 0x800F can be referenced by their memory address as expressed in the literal pool there (0x020270B8 + (0x8000 *2)). This can calculate the correct address for those variables. Other variables are DMA protected and thus it is recommended that you use the engine function to get their values. For simple script input, the 0x8000 series is better.
Again, look at the IDB - it will explain most of these questions about the locations of stuff.
Don't blame me, this routine was written back in my early days :P
Touched,
Ya, I've read that too in one of FBI's tutorial, about lsl/lsr being faster when multiplying by powers of two.
So I can just replace 0x8000 with any 0x8 series variable? Are all of them temporary ones? How about the addresses of 0x4011 onward variables, the safe ones?
Also I downloaded the IDB in your sig but I don't know how to use it -_- it opens in Visual Studio but displays hex values.
I want to know the other routines FBI's routine links to because I would like to make a variation of it that sets the exp not the level. And also the I want to know the addresses of tables such as the experience table. If it's in the IDB, how do you use it?
Touched,
Ya, I've read that too in one of FBI's tutorial, about lsl/lsr being faster when multiplying by powers of two.
So I can just replace 0x8000 with any 0x8 series variable? Are all of them temporary ones? How about the addresses of 0x4011 onward variables, the safe ones?
Also I downloaded the IDB in your sig but I don't know how to use it -_- it opens in Visual Studio but displays hex values.
I want to know the other routines FBI's routine links to because I would like to make a variation of it that sets the exp not the level. And also the I want to know the addresses of tables such as the experience table. If it's in the IDB, how do you use it?
Again, thanks
The 0x8000 series are special, and seem to be designed to be accessed easily from code without the need for a function call. The other variables need this function call. It's the same function used by the code for setvar and other variable related script functions.
As daniilS said, you need to download IDA to open the IDB.
__________________
A Pokemon that is discriminated!
Support squirtle and make it everyone's favourite.
So I'm having issues regarding a custom evolution routine I made. All it does is first checks the flag 0x4a1 to see if its not set. If it is it quits. Then it checks the Pokemon's capture location for 0xa1 or 161. Then if its anything but that it will quit. Then finally it will do the basic level check of a normal evolution. ATM it freezes the game upon leveling up to the proper level,
Hi I need help with my own day night system. Everythings works fine and it works by writing a step number into RAM and after a certain amount of steps a script is executed which clears some flags like beery flags. The problem is that I want the light on the maps change depending on the current step number. So all I have to do is insert a light changing routine in my already working routine.
There is some information about setting brightness on this page: http://problemkaputt.de/gbatek.htm#lcdiocolorspecialeffects
Problem again is that I dont know how to use that information. I searched everywhere with google but I cant find a similar problem or someone using that feature.
So I'm having issues regarding a custom evolution routine I made. All it does is first checks the flag 0x4a1 to see if its not set. If it is it quits. Then it checks the Pokemon's capture location for 0xa1 or 161. Then if its anything but that it will quit. Then finally it will do the basic level check of a normal evolution. ATM it freezes the game upon leveling up to the proper level,
You branch to levelcheckloc without doing pop {r0-r7} before that, causing stack corruption.
You should also specify an alignment for the routine and the literal pool, I don't think it defaults to 2. You should also use the .thumb directive.
Quote:
Originally Posted by MisterJoJo
Hi I need help with my own day night system. Everythings works fine and it works by writing a step number into RAM and after a certain amount of steps a script is executed which clears some flags like beery flags. The problem is that I want the light on the maps change depending on the current step number. So all I have to do is insert a light changing routine in my already working routine.
There is some information about setting brightness on this page: http://problemkaputt.de/gbatek.htm#lcdiocolorspecialeffects
Problem again is that I dont know how to use that information. I searched everywhere with google but I cant find a similar problem or someone using that feature.
Changing the IO registers is something which is pretty unpredictable within the context of the engine. They get constantly overwritten, so it's better to use the engine functions. Anyway, filtering palettes is probably a better way to go than using the blending features, as the former is more customisable. You should look at existing Day/Night systems if you have no idea how to do this.
__________________
A Pokemon that is discriminated!
Support squirtle and make it everyone's favourite.
Two issues with this - first, there is no Pokemon data structure in r0. Second, #0x37 doesn't fetch the capture location - it's the status ailment. 0x23 is the capture location.
Right before you call the decrypter you call 806E6D0. Then you cmp r0 ... 806E6D0 doesn't have a return value so what are you trying to do?
__________________
A Pokemon that is discriminated!
Support squirtle and make it everyone's favourite.
Two issues with this - first, there is no Pokemon data structure in r0. Second, #0x37 doesn't fetch the capture location - it's the status ailment. 0x23 is the capture location.
Right before you call the decrypter you call 806E6D0. Then you cmp r0 ... 806E6D0 doesn't have a return value so what are you trying to do?
Yea I fixed managed to fix everything but that 0x806E6D0 call. I read online that, that was the flagcheck routine for Emerald. Is it incorrect? Cause that would explain that.
Changing the IO registers is something which is pretty unpredictable within the context of the engine. They get constantly overwritten, so it's better to use the engine functions. Anyway, filtering palettes is probably a better way to go than using the blending features, as the former is more customisable. You should look at existing Day/Night systems if you have no idea how to do this.
Currently im trying to make it similar to the darkness of for example rain. I cant figure out though how the darkness there works and I thought it must work with the IO registers. I guess working with palettes is just like adding new darker pals and use them during night ?
Currently im trying to make it similar to the darkness of for example rain. I cant figure out though how the darkness there works and I thought it must work with the IO registers. I guess working with palettes is just like adding new darker pals and use them during night ?
Yeah, I could be wrong, but I think the rain and stuff works similarly to the sepia and greyscale functions - that is they manually blend the palettes. I think the only time those blend functions are used is in fade screens and stuff.
__________________
A Pokemon that is discriminated!
Support squirtle and make it everyone's favourite.
Maybe I can find the corresponding ASM code by debugging the fadescreen script. I'm coming again if I find something useful.
(Trying to debug the doweather script ended with crashs)
Fade screen used a different method. Look at palette filters or at the cloudy weather, or use special effects. There's a function to modify IO regs, can't look up the address today though.