Hacking the SNES ROM

Discuss PoP1 for SNES here.
David
The Prince of Persia
The Prince of Persia
Posts: 2846
Joined: December 11th, 2008, 9:48 pm
Location: Hungary

Re: Hacking the SNES ROM

Post by David »

Shauing wrote: June 13th, 2020, 3:14 am - A hack for at least one type/skill guard not displaying HP.
At 0x5110 write: DA E2 30 AE 19 05 BF 94 80 01 C2 30 FA 29 FF 00 EA EA EA EA EA EA EA EA F0 05

Then in Pr1SnesLevEd, at Guards -> Settings..., set the "?" option to 1 for those guard types which should not show their HP.

Note that this hack overwrites the code which hides the HP for (regular and brown) skeletons, so you should set this option for them as well.
(Unless you want them to show HP, obviously.)


Details:

Code: Select all

At 00:D110 = 0x5110 write:
DA           PHX
E2 30        SEP #$30
AE 19 05     LDX $0519 ; guardprog
BF 94 80 01  LDA $01:8094,X ; specialcolor
C2 30        REP #$30
FA           PLX
29 FF 00     AND #$00ff
EA ×8        NOP ×8
F0 05        BEQ $D12F ; draw if 0
David
The Prince of Persia
The Prince of Persia
Posts: 2846
Joined: December 11th, 2008, 9:48 pm
Location: Hungary

Re: Hacking the SNES ROM

Post by David »

Shauing wrote: July 5th, 2020, 1:12 am as I'm planning to remove the graphical effects and password/time texts,
can the level ending jingle be removed from all levels and the level music fade-out when the Prince reaches an exit rather than abruptly cut off?
EDIT: Did an attempt of this by writing at 0x9EFA: A9 FF 22 DF F8 00, taking cues from previous hacks we did, and it mostly works,
except if a jingle is playing (such as the victory ones or Shadow's theme)
where the Prince keeps falling from bottom to top of the same room until it ends.
We did fix this but this fix abruptly cuts the jingle instead of fading it out.
Also, if the next level triggers a Princess cutscene, the fade-out is cut-off by said cutscene.
I tried the hack you wrote.
This is needed in addition:
At 0xEFAB write: 80 03

Details:

Code: Select all

can go to next level?:
01:EF9D: ad 79 05    LDA $0579 ; current level
01:EFA0: c9 80       CMP #$80
01:EFA2: f0 0c       BEQ $efb0
01:EFA4: c9 fe       CMP #$fe
01:EFA6: f0 08       BEQ $efb0
01:EFA8: ad 41 05    LDA $0541 ; SongCue
01:EFAB: 10 03       BPL $efb0 ; <-- change to BRA (don't wait for song cue to end)
01:EFAD: a9 00       LDA #$00
01:EFAF: 60          RTS

01:EFB0: a9 01       LDA #$01
01:EFB2: 60          RTS
User avatar
Shauing
Calif
Calif
Posts: 431
Joined: April 5th, 2018, 10:38 pm
Contact:

Re: Hacking the SNES ROM

Post by Shauing »

Spoiler: show
David wrote: July 7th, 2020, 9:59 am Sorry, I somehow ignored your remark in parentheses when I was testing it!
Indeed, if I change FAC2 to 00 then the checkpoint and the special exit will have the same triggering level, room, and direction; and the checkpoint will override the special exit.

The question is, what are you trying to achieve?
If exiting room 3 to the left should take the prince to level 3, then why do you want to set a checkpoint at the same place?
If your goal is to start level 3 from a checkpoint then it's enough to configure that in the code starting at 0xFD00.
That's what I tried to say here, but maybe I wasn't clear enough?
Yeah, these values I described were inserted before you brought up the CC value so that it would set up a checkpoint on the same exit to Level 3 so that when the Prince comes back, he returns to room 3 of Level 1 and not at the normal level start.

Spoiler: show
David wrote: July 7th, 2020, 9:59 am
Shauing wrote: July 5th, 2020, 11:31 am Ok, it works beautifully. I've not altered any of the loose floor modifiers yet, and I like how it looks as of now.
You don't need to alter their modifiers, that's done by the hack.
Ah, I misunderstood that last part, thank you for clarifying that.

Spoiler: show
David wrote: July 7th, 2020, 11:33 am
Shauing wrote: July 5th, 2020, 11:31 am Unrelated to this hack but something I noticed while testing this and it's related to the hack of the level end music starting when Prince starts going upstairs: On Level 13 he starts going upstairs but the level ends abruptly due to this hack we did as the cue is muted for this level. Can this be fixed?
Luckily I had an alternate version of the hack which starts the level end music earlier.
Back then I decided to publish a different version which requires fewer edits.
But it seems we need this alternate version after all.

First undo the old version of the hack:
At 0x174CB write: F1
You don't need to undo the edits at 0x1749B and 0xFF96 because the new hack changes them as well.

Now, the new hack:

Detach going to the next level from the level ending music.
At 0x9EFE write: 4C

When it's time to enter the next level, trigger specifically going to the next level, not the music before it.
At 0x8313 write: 20 01 9F
At 0x8367 write: 20 01 9F
At 0x964A write: 20 01 9F

Change the unused sequence table sound 7 into the level ending music.
At 0x96BF write: 4C FA 9E

Use sound 7 in the sequence table instead of one of the footsteps.
At 0x1749B write: F2 07
(If this is too early, you can use one of these later offsets instead: 0x174A1, 0x174A8, 0x174B0.)

Don't stop the prince while the music is playing.
At 0xFF96 write: A2 00
It works, but unfortunately it brings back the Level 12 bug, plus a debris tile appears where the Shadow will land to fight you.

Spoiler: show
David wrote: July 7th, 2020, 5:53 pm
Shauing wrote: June 13th, 2020, 3:14 am (Maybe passwords can use the unused checkpoint feature that would restore you at the checkpoint, or the code isn't complete?)
Shauing wrote: June 29th, 2020, 9:44 pm Also, it seems that choosing to ''Game End'' and then choosing continue restarts you to the level door start room, rather than on the checkpoint, that's why I was asking if passwords could have that unused checkpoint flag activated (if the code's complete/still there).
I made two variants.
The first variant was easier to make, but I wasn't sure if its limitations are acceptable to you, so I made a more complicated but less limited second variant.


Second variant: Use two bits for the checkpoint.
The first bit is the same as in the first variant.
The second bit is bit 4 of character 1, marked as "0" in the documentation.
This means the passwords can bring the prince to the default starting place of the level ("checkpoint #0") or checkpoint #1, #2, or #3 of the level.
Wonderful. Simply amazing. I decided to use the second variant as indeed some levels have more than one checkpoint. So the limit is 3 checkpoints (4 with checkpoint 0)? Can it be extended to five? Still not sure, but some levels will have in addition to the normal level door exit, four exits, one per direction (up, down, left and right); I'm still coming up with ideas for the levels and deciding how many exits each one will have (from 1 to 5).

Spoiler: show
David wrote: July 7th, 2020, 9:52 pm
Shauing wrote: June 13th, 2020, 3:14 am - Best time menu should only display the total final time.
At 0x1583B write: AD 44 05 C9 LL D0 4C C2 30 AD 2D 05 8D 4B 05 A9 00 00 4C 5D D8
Where LL is a level number.
When the player gets to this level, the total elapsed time is written to the best time slot of level 1 (if it's better than what was there previously).
It should generally be the level which triggers the good ending when you enter it.

I don't know if you changed (or will change) the starting time in this mod.
If you did (or will) then this hack needs to be adjusted for that.
I am planning to change the time limit to this mod, taking into account that all 27 levels will be playable, so I'm still not sure what the time limit will be, but probably and easily more than 2 hours. So, what needs to be adjusted when I change the time limit?
NEW UPDATE! Prince Of Persia: 30th Anniversary Port v1.1.5. Download it today!: viewtopic.php?p=29053#p29053
NEW UPDATE! Prince Of Persia: The Queen Of Light v2.6. Download it today! viewtopic.php?p=33174#p33174
David
The Prince of Persia
The Prince of Persia
Posts: 2846
Joined: December 11th, 2008, 9:48 pm
Location: Hungary

Re: Hacking the SNES ROM

Post by David »

Shauing wrote: July 10th, 2020, 9:11 am It works, but unfortunately it brings back the Level 12 bug,
To fix the level 12 bug:
At 0x9EFE write: 20
At 0x8313 write: 20 FA 9E
At 0x8367 write: 20 FA 9E -- This one is actually for level 6, but it seems it's not needed because we already fixed this elsewhere.

At 0x96BF write: 4C 90 FF

Shauing wrote: July 10th, 2020, 9:11 am plus a debris tile appears where the Shadow will land to fight you.
I couldn't reproduce this, but it might be caused by the following mistake:
David wrote: July 5th, 2020, 10:52 am Loose floors on level 13 should not shake when you land on their row.
At 0xDBAF write: 4C 50 FC
At 0xFC50 write: AD 79 05 C9 0D F0 03 4C FA DB 60
Sorry, I made mistake in this one.

The correct hack is:
At 0xFC50 write: AD 79 05 C9 0C F0 03 4C FA DC 60

Code: Select all

At 0xFC50 write:
AD 79 05  LDA $0579 ; current level
C9 0C     CMP #$0C ; level 13
F0 03     BEQ :1
4C FA DC  JMP $DCFA ; add trob
          :1
60        RTS
User avatar
Shauing
Calif
Calif
Posts: 431
Joined: April 5th, 2018, 10:38 pm
Contact:

Re: Hacking the SNES ROM

Post by Shauing »

Spoiler: show
David wrote: July 11th, 2020, 9:34 am
Shauing wrote: July 10th, 2020, 9:11 am It works, but unfortunately it brings back the Level 12 bug,
To fix the level 12 bug:
At 0x9EFE write: 20
At 0x8313 write: 20 FA 9E
At 0x8367 write: 20 FA 9E -- This one is actually for level 6, but it seems it's not needed because we already fixed this elsewhere.

At 0x96BF write: 4C 90 FF

Shauing wrote: July 10th, 2020, 9:11 am plus a debris tile appears where the Shadow will land to fight you.
I couldn't reproduce this, but it might be caused by the following mistake:
David wrote: July 5th, 2020, 10:52 am Loose floors on level 13 should not shake when you land on their row.
At 0xDBAF write: 4C 50 FC
At 0xFC50 write: AD 79 05 C9 0D F0 03 4C FA DB 60
Sorry, I made mistake in this one.

The correct hack is:
At 0xFC50 write: AD 79 05 C9 0C F0 03 4C FA DC 60

Code: Select all

At 0xFC50 write:
AD 79 05  LDA $0579 ; current level
C9 0C     CMP #$0C ; level 13
F0 03     BEQ :1
4C FA DC  JMP $DCFA ; add trob
          :1
60        RTS
That solved it. Thank you. I will ran the game one more time in full and see if anything else is off.

EDIT: One thing. The Shadow is not displaying a hit star/splash sprite when the Prince hits him. And the center of that star is gray; perhaps we can change the white color in the palette to gray and use that, as it is otherwise unused.
David wrote: June 28th, 2020, 9:44 pm Maybe the three layers could be loaded from different environments, but anything finer than that is too hard.
How can we do this?
Last edited by Shauing on July 12th, 2020, 12:41 am, edited 2 times in total.
NEW UPDATE! Prince Of Persia: 30th Anniversary Port v1.1.5. Download it today!: viewtopic.php?p=29053#p29053
NEW UPDATE! Prince Of Persia: The Queen Of Light v2.6. Download it today! viewtopic.php?p=33174#p33174
User avatar
spartacus735
Wise Scribe
Wise Scribe
Posts: 217
Joined: November 20th, 2011, 12:41 pm
Location: France

Re: Hacking the SNES ROM

Post by spartacus735 »

It will great if you add all your research in the main post of Kaslghnoon !
>>>>>>>>>>>>>>>>>>>> http://www.youtube.com/user/spartacus735 <<<<<<<<<<<<<<<<<<<< the channel of prince of persia snes mod
David
The Prince of Persia
The Prince of Persia
Posts: 2846
Joined: December 11th, 2008, 9:48 pm
Location: Hungary

Re: Hacking the SNES ROM

Post by David »

Shauing wrote: July 10th, 2020, 9:11 am Wonderful. Simply amazing. I decided to use the second variant as indeed some levels have more than one checkpoint.
So the limit is 3 checkpoints (4 with checkpoint 0)? Can it be extended to five?
There are no more unused bits in the password, so we can only reuse an already used bit.
It could be one of the checksum bits, or the least significant bit of the elapsed time counter.

Shauing wrote: July 10th, 2020, 9:11 am I am planning to change the time limit to this mod, taking into account that all 27 levels will be playable,
so I'm still not sure what the time limit will be, but probably and easily more than 2 hours.
So, what needs to be adjusted when I change the time limit?
Adjusting is needed only if you use the hack which changes the starting value of the elapsed time counter: viewtopic.php?p=24222#p24222
But that hack cannot increase the remaining time, so you need to use this hack instead: viewtopic.php?p=24207#p24207

While at it, the maximum time you can have is 65535 ticks, which is 65535/425 = 154,2 minutes.
(Or, if you're using the EU version, you could reduce the number of ticks per minute to 375 so that it matches an actual minute. See here.)

Shauing wrote: July 11th, 2020, 7:31 pm EDIT: One thing. The Shadow is not displaying a hit star/splash sprite when the Prince hits him.
Did you change 4C F0 FC to 4C F0 FA as I wrote here?
(It's at offset 0xFCA7.)
Shauing wrote: July 11th, 2020, 7:31 pm And the center of that star is gray; perhaps we can change the white color in the palette to gray and use that, as it is otherwise unused.
There is one problem: When the shadow is hurt, the prince is also hurt, so two starts will appear on the screen.
PoP SNES uses a single palette for stars, which means it's not possible to display two stars with different palettes at the same time, unless I change the code to load two copies of that palette.

Shauing wrote: July 11th, 2020, 7:31 pm
David wrote: June 28th, 2020, 9:44 pm Maybe the three layers could be loaded from different environments, but anything finer than that is too hard.
How can we do this?
We need to change this part:

Code: Select all

00:B691: 20 c8 b6    JSR $b6c8 ; determine environment

00:B694: ad 4e 02    LDA $024e ; level type*4+1
00:B697: 20 9d aa    JSR $aa9d ; load layer 1 graphics resource A

00:B69A: ad 4e 02    LDA $024e ; level type*4+1
00:B69D: 18          CLC
00:B69E: 69 02       ADC #$02
00:B6A0: 20 09 af    JSR $af09 ; load layer 3 graphics resource A

00:B6A3: ad 4e 02    LDA $024e ; level type*4+1
00:B6A6: 18          CLC
00:B6A7: 69 01       ADC #$01
00:B6A9: 20 b8 b2    JSR $b2b8 ; load layer 2 graphics resource A

00:B6AC: 8b          PHB
00:B6AD: ad 4e 02    LDA $024e ; level type*4+1
00:B6B0: 18          CLC
00:B6B1: 69 03       ADC #$03
00:B6B3: 20 f2 98    JSR $98f2 ; find resource A, bank->B, offset->X (table=0x20000)
00:B6B6: 20 09 b7    JSR $b709 ; load level palette?
00:B6B9: ab          PLB ; B=0x00
The specific changes would depend on what you want to achieve.

To see what a level looks like with mixed graphics, change one of the AD 4E 02 bytes to A9 TT EA,
where TT is level type*4+1 (i.e. one of: 01,05,09,0D,11,15,19,1D,21,25).
The offsets are 0x3694 for layer 1 (background), 0x369A for layer 3 (front), 0x36A3 for layer 2 (middle), (and 0x36AD for the palette).

Of course this will change the graphics on all levels, but you will have a specific idea on what would you like to change on which level.

A warning, though: Changing these will display the layer in question with messed up graphics, just like when you change the level type in Pr1SnesLevEd.
That's because different level types might use the same tile index for different graphics.
David
The Prince of Persia
The Prince of Persia
Posts: 2846
Joined: December 11th, 2008, 9:48 pm
Location: Hungary

Re: Hacking the SNES ROM

Post by David »

Shauing wrote: July 10th, 2020, 9:11 am So the limit is 3 checkpoints (4 with checkpoint 0)? Can it be extended to five?
This hack adds a third bit so the checkpoint can go from 0 to 7.

Use this in addition to the two-bit variant of the previous checkpoint-in-password hack.

At 0x158B3 write: 20 80 FF EA EA
At 0x17F80 write: AD 4E 05 29 FE 85 57 AD 27 06 4A 4A 29 01 04 57 60
At 0x15B5B write: 20 70 FF
At 0x17F70 write: 8D 60 0E 29 01 0A 0A 85 56 60
At 0x15B77 write: A5 56
At 0x17FA0 write: A5 5B 29 02 14 5B 4A 46 5F 2A 05 56 60

(At 0x17FA0 we intentionally overwrite part of the previous hack.)


Details:

Code: Select all

When the game makes a password:

At 02:D8B3 = 0x158B3 write:
20 80 FF  JSR $FF80
EA        NOP
EA        NOP

At 02:FF80 = 0x17F80 write:
AD 4E 05  LDA $054E ; timeL at beginning of level
29 FE     AND #$FE ; for more bits: #$FC, #$F8, ...
85 57     STA $57 ; password timeL
AD 27 06  LDA $0627
4A        LSR
4A        LSR
29 01     AND #$01 ; for more bits: #$03, #$07, ...
04 57     TSB $57 ; password timeL
60        RTS

When the the game decodes a password:

At 02:DB5B = 0x15B5B write:
20 70 FF  JSR $FF70

At 02:FF70 = 0x17F70 write:
8D 60 0E  STA $0E60 ; decoded timeL
29 01     AND #$01 ; for more bits: #$03, #$07, ...
0A        ASL
0A        ASL
85 56     STA $56 ; store the high bits
60        RTS

At 02:DB77 = 0x15B77 write:
A5 56     LDA $56

At 02:FFA0 = 0x17FA0 write:
A5 5B     LDA $5B ; char 1
29 02     AND #$02 ; we need only bit 1 (shifted from bit 4 by the previous reads)
14 5B     TRB $5B ; clear this bit from char 1 (because it's originally an always-zero bit of the CRC)
4A        LSR ; shift bit 1 to bit 0
46 5F     LSR $5F ; char 5
2A        ROL
05 56     ORA $56 ; use the high bits
60        RTS

User avatar
Shauing
Calif
Calif
Posts: 431
Joined: April 5th, 2018, 10:38 pm
Contact:

Re: Hacking the SNES ROM

Post by Shauing »

David wrote: July 12th, 2020, 8:31 am
Shauing wrote: July 10th, 2020, 9:11 am I am planning to change the time limit to this mod, taking into account that all 27 levels will be playable,
so I'm still not sure what the time limit will be, but probably and easily more than 2 hours.
So, what needs to be adjusted when I change the time limit?
Adjusting is needed only if you use the hack which changes the starting value of the elapsed time counter: viewtopic.php?p=24222#p24222
But that hack cannot increase the remaining time, so you need to use this hack instead: viewtopic.php?p=24207#p24207

While at it, the maximum time you can have is 65535 ticks, which is 65535/425 = 154,2 minutes.
(Or, if you're using the EU version, you could reduce the number of ticks per minute to 375 so that it matches an actual minute. See here.)
Ah, so it uses the longer, first version of the hack that I used on The Queen Of Light.
Why is the time limit a maximum of 65535 ticks?
In the US version can't the ticks be reduced to whatever matches an actual minute? Yeah, I know the EU version will give more time overall, but it plays slower. And how can this be done?
David wrote: July 12th, 2020, 8:31 am
Shauing wrote: July 11th, 2020, 7:31 pm EDIT: One thing. The Shadow is not displaying a hit star/splash sprite when the Prince hits him.
Did you change 4C F0 FC to 4C F0 FA as I wrote here?
(It's at offset 0xFCA7.)
Oh, seems I didn't notice that ''FC'' changed to ''FA''.
David wrote: July 12th, 2020, 8:31 am
Shauing wrote: July 11th, 2020, 7:31 pm And the center of that star is gray; perhaps we can change the white color in the palette to gray and use that, as it is otherwise unused.
There is one problem: When the shadow is hurt, the prince is also hurt, so two starts will appear on the screen.
PoP SNES uses a single palette for stars, which means it's not possible to display two stars with different palettes at the same time, unless I change the code to load two copies of that palette.
I see. Is it too complex to add that? Or is there not enough space to insert it?
David wrote: July 12th, 2020, 8:31 am
Shauing wrote: July 11th, 2020, 7:31 pm
David wrote: June 28th, 2020, 9:44 pm Maybe the three layers could be loaded from different environments, but anything finer than that is too hard.
How can we do this?
We need to change this part:

Code: Select all

00:B691: 20 c8 b6    JSR $b6c8 ; determine environment

00:B694: ad 4e 02    LDA $024e ; level type*4+1
00:B697: 20 9d aa    JSR $aa9d ; load layer 1 graphics resource A

00:B69A: ad 4e 02    LDA $024e ; level type*4+1
00:B69D: 18          CLC
00:B69E: 69 02       ADC #$02
00:B6A0: 20 09 af    JSR $af09 ; load layer 3 graphics resource A

00:B6A3: ad 4e 02    LDA $024e ; level type*4+1
00:B6A6: 18          CLC
00:B6A7: 69 01       ADC #$01
00:B6A9: 20 b8 b2    JSR $b2b8 ; load layer 2 graphics resource A

00:B6AC: 8b          PHB
00:B6AD: ad 4e 02    LDA $024e ; level type*4+1
00:B6B0: 18          CLC
00:B6B1: 69 03       ADC #$03
00:B6B3: 20 f2 98    JSR $98f2 ; find resource A, bank->B, offset->X (table=0x20000)
00:B6B6: 20 09 b7    JSR $b709 ; load level palette?
00:B6B9: ab          PLB ; B=0x00
The specific changes would depend on what you want to achieve.

To see what a level looks like with mixed graphics, change one of the AD 4E 02 bytes to A9 TT EA,
where TT is level type*4+1 (i.e. one of: 01,05,09,0D,11,15,19,1D,21,25).
The offsets are 0x3694 for layer 1 (background), 0x369A for layer 3 (front), 0x36A3 for layer 2 (middle), (and 0x36AD for the palette).

Of course this will change the graphics on all levels, but you will have a specific idea on what would you like to change on which level.

A warning, though: Changing these will display the layer in question with messed up graphics, just like when you change the level type in Pr1SnesLevEd.
That's because different level types might use the same tile index for different graphics.
Nice. I'll check this.

I think somewhere here is a hack to have the slow potion activated for one entire level, but could the Prince use a transparent palette? Also on that same level, can a specific potion trigger a screen flashes and that his palette returns back to the normal one (perhaps removing the slow effect, though this last part is optional)?
Spoiler: show
Also can a specific guard on one room of that level be lying dead without having to display the falling down to ground animation first and display a Prince sprite?
NEW UPDATE! Prince Of Persia: 30th Anniversary Port v1.1.5. Download it today!: viewtopic.php?p=29053#p29053
NEW UPDATE! Prince Of Persia: The Queen Of Light v2.6. Download it today! viewtopic.php?p=33174#p33174
David
The Prince of Persia
The Prince of Persia
Posts: 2846
Joined: December 11th, 2008, 9:48 pm
Location: Hungary

Re: Hacking the SNES ROM

Post by David »

Shauing wrote: July 12th, 2020, 5:27 pm Also can a specific guard on one room of that level be lying dead without having to display the falling down to ground animation first and display a Prince sprite?
At 0x874C write: 4C D0 FF
At 0xFFD0 write: AD 79 05 C9 LL D0 20 AD 06 05 C9 RR D0 19 20 7A 87 A9 00 8D 64 04 A9 73 20 42 AE 20 8A 95 9C 0B 05 9C 66 04 4C 89 B5 AD 79 05 4C 51 87
Where LL is the level and RR is the room.
In the level editor, you need to place a guard in that room where you want the dead prince.

This hack will use the guard's palette for the prince sprite (i.e. it doesn't change which palette to use, because that's decided elsewhere).
If your mod has an unused guard type (e.g. dead purple) then you could use a guard of that type in the room, and set his palette to 0 (prince).

Details:

Code: Select all

At 0x874C write:
4C D0 FF  JMP $FFD0

At 0xFFD0 write:
AD 79 05  LDA $0579 ; current level
C9 LL     CMP #$LL ; level
D0 20     BNE :1 ; load guard regularly
AD 06 05  LDA $0506 ; shown room ; VisScrn
C9 RR     CMP #$RR ; room
D0 19     BNE :1 ; load guard regularly
          ;
20 7A 87  JSR $877A ; load guard regularly
A9 00     LDA #$00 ; kid
8D 64 04  STA $0464 ; Char.chtype ; CharID
A9 73     LDA #$73 ; dead
20 42 AE  JSR $AE42 ; char sequence ; jumpseq
20 8A 95  JSR $958A ; play char sequence ; animchar
9C 0B 05  STZ $050B ; guard current HP ; OppStrength
9C 66 04  STZ $0466 ; Char.alive? ; CharLife
4C 89 B5  JMP $B589 ; save Shad? ; SAVESHAD
          :1
AD 79 05  LDA $0579 ; current level
4C 51 87  JMP $8751
David
The Prince of Persia
The Prince of Persia
Posts: 2846
Joined: December 11th, 2008, 9:48 pm
Location: Hungary

Re: Hacking the SNES ROM

Post by David »

Shauing wrote: July 12th, 2020, 5:27 pm Ah, so it uses the longer, first version of the hack that I used on The Queen Of Light.
Why is the time limit a maximum of 65535 ticks?
Because the elapsed time counter is stored in 16 bits (2 bytes), both in the RAM and in the password.

Though, there is another counter which measures time spent on the current level, and that one has an overflow area for more significant bits.
Actually, both counters have an extra separate bit which is less significant than the main 16 bits, but this bit is not recorded in the password.

The bits are stored in the following RAM addresses:

elapsed time since game start:
$052F -- bit 0 is the least significant bit of the counter
$052D -- the next 8 bits
$052E -- the next 8 bits

elapsed time on this level:
$054D -- bit 0 is the least significant bit of the counter
$054B -- the next 8 bits
$054C -- the next 8 bits
$054D -- bits 1..7 are the next 7 bits

However, for the password and the best times list, only the main 8+8 bits are used.


It's possible to tell the game to put the *two* least significant bits into the separate byte, which means the main 16 bits will count twice as slower.
At 0xE9CE write: 29 03
At 0xF97E write: A9 04
At 0xF96B write: 20 C0 FF
At 0xFFC0 write: AD 4D 05 1A 89 03 D0 03 38 E9 04 60
At 0xF971 write: 29 03
At 0x15870 write: C9 04
At 0x15CA1 write: C9 04

After this you need to halve the number of ticks in a minute and in a second. (See the bottom of this post.)
You also need to adjust the starting minutes and other things as described here: viewtopic.php?p=24207#p24207
For the formulas in that post, you need to use the new number of ticks in a minute instead of 425.

Details:

Code: Select all

01:E9CA: ad 2f 05    LDA $052f ; ? elapsed time low bit?
01:E9CD: 1a          INC
01:E9CE: 29 01       AND #$01 ; <-- change to #$03
01:E9D0: 8d 2f 05    STA $052f ; ? elapsed time low bit?

01:F969: a9 01       LDA #$01
01:F96B: 4d 4d 05    EOR $054d ; elapsed time on this level low bit and high bits
01:F96E: 8d 4d 05    STA $054d ; elapsed time on this level low bit and high bits
01:F971: 29 01       AND #$01 ; <-- change to #$03
[...]
01:F97E: a9 02       LDA #$02 ; <-- change to #$04
01:F980: 18          CLC
01:F981: 6d 4d 05    ADC $054d ; elapsed time on this level low bit and high bits

At 0xF96B write:
20 C0 FF  JSR $FFC0

At 0xFFC0 write:
AD 4D 05  LDA $054D ; elapsed time on this level low bit and high bits
1A        INC
89 03     BIT #$03
D0 03     BNE :1
38        SEC
E9 04     SBC $#04
          :1
60        RTS

02:D86D: ad 4d 05    LDA $054d ; elapsed time on this level low bit and high bits
02:D870: c9 02       CMP #$02 ; <-- change to #$04

02:DC9E: ad 4d 05    LDA $054d ; elapsed time on this level low bit and high bits
02:DCA1: c9 02       CMP #$02 ; <-- change to #$04

But then there is *another* problem I just found, namely that the game can't display more than 159 minutes correctly.
For example, when 160 minutes are left, it thinks 0 minutes are left and displays the seconds counting down.
(It's only a display problem, the time won't expire yet at this point.)

That happens because of how the game converts numbers to decimal:
The ones are on the bottom 4 bits of a byte, and the tens and hundreds are together on the top 4 bits.
This means that the tens and hundreds together can't be above 15.

If you really need more than 159 minutes, I might try and search a solution for this.


Shauing wrote: July 12th, 2020, 5:27 pm In the US version can't the ticks be reduced to whatever matches an actual minute?
Yeah, I know the EU version will give more time overall, but it plays slower.
In the US version, an in-game minute (425 ticks) is shorter than an actual minute (450 ticks).
So there its length can be increased to match an actual minute.
See here: viewtopic.php?p=24918#p24918
Shauing wrote: July 12th, 2020, 5:27 pm And how can this be done?
Number of ticks in a minute: Two bytes at 0xE946, 0x15694, 0x15699, 0x15CC3, 0x15CC8. (Default: 0x01A9 = 425)
Number of ticks in a second: Two bytes at 0xE951, 0x156BD, 0x156C2, 0x15CEC, 0x15CF1. (Default: 0x0007 = 7)
User avatar
Shauing
Calif
Calif
Posts: 431
Joined: April 5th, 2018, 10:38 pm
Contact:

Re: Hacking the SNES ROM

Post by Shauing »

Spoiler: show
David wrote: July 15th, 2020, 10:30 am
Shauing wrote: July 12th, 2020, 5:27 pm Ah, so it uses the longer, first version of the hack that I used on The Queen Of Light.
Why is the time limit a maximum of 65535 ticks?
Because the elapsed time counter is stored in 16 bits (2 bytes), both in the RAM and in the password.

Though, there is another counter which measures time spent on the current level, and that one has an overflow area for more significant bits.
Actually, both counters have an extra separate bit which is less significant than the main 16 bits, but this bit is not recorded in the password.

The bits are stored in the following RAM addresses:

elapsed time since game start:
$052F -- bit 0 is the least significant bit of the counter
$052D -- the next 8 bits
$052E -- the next 8 bits

elapsed time on this level:
$054D -- bit 0 is the least significant bit of the counter
$054B -- the next 8 bits
$054C -- the next 8 bits
$054D -- bits 1..7 are the next 7 bits

However, for the password and the best times list, only the main 8+8 bits are used.


It's possible to tell the game to put the *two* least significant bits into the separate byte, which means the main 16 bits will count twice as slower.
At 0xE9CE write: 29 03
At 0xF97E write: A9 04
At 0xF96B write: 20 C0 FF
At 0xFFC0 write: AD 4D 05 1A 89 03 D0 03 38 E9 04 60
At 0xF971 write: 29 03
At 0x15870 write: C9 04
At 0x15CA1 write: C9 04

After this you need to halve the number of ticks in a minute and in a second. (See the bottom of this post.)
You also need to adjust the starting minutes and other things as described here: viewtopic.php?p=24207#p24207
For the formulas in that post, you need to use the new number of ticks in a minute instead of 425.

Details:

Code: Select all

01:E9CA: ad 2f 05    LDA $052f ; ? elapsed time low bit?
01:E9CD: 1a          INC
01:E9CE: 29 01       AND #$01 ; <-- change to #$03
01:E9D0: 8d 2f 05    STA $052f ; ? elapsed time low bit?

01:F969: a9 01       LDA #$01
01:F96B: 4d 4d 05    EOR $054d ; elapsed time on this level low bit and high bits
01:F96E: 8d 4d 05    STA $054d ; elapsed time on this level low bit and high bits
01:F971: 29 01       AND #$01 ; <-- change to #$03
[...]
01:F97E: a9 02       LDA #$02 ; <-- change to #$04
01:F980: 18          CLC
01:F981: 6d 4d 05    ADC $054d ; elapsed time on this level low bit and high bits

At 0xF96B write:
20 C0 FF  JSR $FFC0

At 0xFFC0 write:
AD 4D 05  LDA $054D ; elapsed time on this level low bit and high bits
1A        INC
89 03     BIT #$03
D0 03     BNE :1
38        SEC
E9 04     SBC $#04
          :1
60        RTS

02:D86D: ad 4d 05    LDA $054d ; elapsed time on this level low bit and high bits
02:D870: c9 02       CMP #$02 ; <-- change to #$04

02:DC9E: ad 4d 05    LDA $054d ; elapsed time on this level low bit and high bits
02:DCA1: c9 02       CMP #$02 ; <-- change to #$04

But then there is *another* problem I just found, namely that the game can't display more than 159 minutes correctly.
For example, when 160 minutes are left, it thinks 0 minutes are left and displays the seconds counting down.
(It's only a display problem, the time won't expire yet at this point.)

That happens because of how the game converts numbers to decimal:
The ones are on the bottom 4 bits of a byte, and the tens and hundreds are together on the top 4 bits.
This means that the tens and hundreds together can't be above 15.

If you really need more than 159 minutes, I might try and search a solution for this.
All right, I'll check this out to see if I understood how it works. And yes, see if it can be extended to possibly 240-300 minutes.
Spoiler: show
David wrote: July 15th, 2020, 10:30 am
Shauing wrote: July 12th, 2020, 5:27 pm In the US version can't the ticks be reduced to whatever matches an actual minute?
Yeah, I know the EU version will give more time overall, but it plays slower.
In the US version, an in-game minute (425 ticks) is shorter than an actual minute (450 ticks).
So there its length can be increased to match an actual minute.
See here: viewtopic.php?p=24918#p24918
Shauing wrote: July 12th, 2020, 5:27 pm And how can this be done?
Number of ticks in a minute: Two bytes at 0xE946, 0x15694, 0x15699, 0x15CC3, 0x15CC8. (Default: 0x01A9 = 425)
Number of ticks in a second: Two bytes at 0xE951, 0x156BD, 0x156C2, 0x15CEC, 0x15CF1. (Default: 0x0007 = 7)
Cool. I've always wondered why the original in-game minutes didn't match an actual minute.
NEW UPDATE! Prince Of Persia: 30th Anniversary Port v1.1.5. Download it today!: viewtopic.php?p=29053#p29053
NEW UPDATE! Prince Of Persia: The Queen Of Light v2.6. Download it today! viewtopic.php?p=33174#p33174
David
The Prince of Persia
The Prince of Persia
Posts: 2846
Joined: December 11th, 2008, 9:48 pm
Location: Hungary

Re: Hacking the SNES ROM

Post by David »

Shauing wrote: July 12th, 2020, 5:27 pm I think somewhere here is a hack to have the slow potion activated for one entire level, but could the Prince use a transparent palette?
Also on that same level, can a specific potion trigger a screen flashes and that his palette returns back to the normal one
(perhaps removing the slow effect, though this last part is optional)?
In all of the following, replace LL with the number of the level where you want these effects.

Start the level with slow fall enabled:
At 0xE0AE write: 20 90 FF
At 0xFF90 write: 8D 00 05 48 AD 79 05 C9 LL D0 03 CE 00 05 68 60

Keep the slow fall from expiring on the level until the new potion stops it:
At 0xF8AF write: 20 A0 FF
At 0xFFA0 write: AD 79 05 C9 LL D0 08 AD 00 05 F0 02 A9 FF 60 AD 00 05 60

Make the prince transparent while slowfall is enabled:
At 0x74DC write: C9 LL D0 07 AD 00 05 EA EA D0 05

Add the potion which ends the slowfall and restores the palette:
Here we turn one of the existing potion types into this new potion.
The offset of the first part depends on which potion type do you want to override.
Pick one which you are not using in your mod for its original purpose.
* potion of warp: 0xD0A3
* kill potion: 0xD097
* upside-down potion: 0xD072
* slow-fall potion: 0xD060
At the offset write: 4C 70 FF
At 0xFF70 write: 9C 00 05 9C 40 05 A9 00 8D 31 21 A9 6F 8D C4 04 A9 05 8D C3 04 60

Force the guards to be non-transparent:
(Because by default, transparency affects all guards as well. Remember that the dead prince from the other hack is a guard as well.)
(And allow the non-slow-fall prince to be made transparent because of limitations, see Details for an explanation.)
At 0x73CD write: A9 02
At 0x7435 write: A9 02
At 0x748D write: A9 01

But only on that one level:
(If the prince meets the shadow on another level, the prince should not turn transparent.)
At 0x73CF, 0x7437, 0x748F write: 20 B0 FF EA
At 0x7FB0 write: AE 79 05 E0 LL 00 F0 02 A9 00 22 22 9A 00 60


Details:

Code: Select all

Start the level with slow fall enabled:

At 0xE0AE write:
20 90 FF  JSR $FF90

At 0xFF90 write:
8D 00 05  STA $0500 ; slow fall remaining time
48        PHA
AD 79 05  LDA $0579 ; current level
C9 LL     CMP #$LL ; level
D0 03     BNE :1
CE 00 05  DEC $0500 ; #$00 -> #$FF
          :1
68        PLA
60        RTS


Keep the slow fall from expiring on the level until the new potion stops it:

At 0xF8AF write:
20 A0 FF  JSR $FFA0

At 0xFFA0 write:
AD 79 05  LDA $0579 ; current level
C9 LL     CMP #$LL ; level
D0 08     BNE :1
AD 00 05  LDA $0500 ; slow fall remaining time
F0 02     BEQ :2 ; if zero, don't do anything (it was zeroed by the new potion)
A9 FF     LDA #$FF ; if not zero, refill
          :2
60        RTS
          :1
AD 00 05  LDA $0500 ; slow fall remaining time
60        RTS


Make the prince transparent while slowfall is enabled:
(This code runs when entering a new room.)

At 00:F4DC = 0x74DC write:
C9 LL     CMP #$LL ; level
D0 07     BNE $F4E7 ; shadow is transparent
AD 00 05  LDA $0500 ; slow fall remaining time
EA        NOP
EA        NOP
D0 05     BNE $F4EC ; transparency on


Add the potion which ends the slowfall and restores the palette:

The offset of the first part depends on which potion type do you want to override.
* potion of warp: 0xD0A3
* kill potion: 0xD097
* upside-down potion: 0xD072
* slow-fall potion: 0xD060
At the offset write:
4C 70 FF JMP $FF70

At 0xFF70 write:
9C 00 05  STZ $0500 ; slow fall remaining time ; weightless
9C 40 05  STZ $0540 ; slow fall blue
A9 00     LDA #$00 ; disable transparency
8D 31 21  STA $2131 ; CGADSUB
A9 6F     LDA #$6F ; the first digit determines the flash color
8D C4 04  STA $04C4 ; flash color ; lightcolor
A9 05     LDA #$05
8D C3 04  STA $04C3 ; flash count ; lightning
60        RTS


You need to know the following about sprite transparency:

On the SNES, there are 8 rows of sprite palette RAM, into which the game can load palette rows from ROM.
Each row can store 16 colors, as you can see in Pr1SnesLevEd.
Sprites using the first half (4 rows) are never transparent.
Sprites using the second half (4 rows) can be made transparent, but only together.

In the original game, opponents and the slow-fall prince use one row from the second half.
This means that the shadow, as an opponent, can be made transparent in the original game.
But this also means that if I make the slow-fall prince transparent, then any guards you meet during slow-fall will also be transparent.
The following part fixes this.

Also, the first half contains four palettes: prince, sword, flame, potion.
So if I move the guard palette there, I need to move something from there into the second half.
I chose to move the prince palette.


Force the guards to be non-transparent:
(Because by default, transparency affects all guards.)

At 00:F3CD = 0x73CD write: A9 02
At 00:F435 = 0x7435 write: A9 02
At 00:F48D = 0x748D write: A9 01

that is:
00:F3CD: a9 00       LDA #$00 ; <-- change to 02, use the second half of sprite palettes, only because the guard palette takes the place of the prince palette in the first half
00:F3CF: 22 22 9a 00 JSR $00:9a22 ; search next free palette row -> A
00:F3D3: 8f 66 02 00 STA $00:0266 ; prince palette row

00:F435: a9 00       LDA #$00 ; <-- change to 02, use the second half of sprite palettes, which can be made transparent
00:F437: 22 22 9a 00 JSR $00:9a22 ; search next free palette row -> A
00:F43B: 8f 6f 02 00 STA $00:026f ; slowfall prince palette row

00:F48D: a9 00       LDA #$00 ; <-- change to 01, use the first half of sprite palettes, which cannot be made transparent
00:F48F: 22 22 9a 00 JSR $00:9a22 ; search next free palette row -> A
00:F493: 8f 67 02 00 STA $00:0267 ; opponent palette row


But only on that one level:
(If the prince meets the shadow on another level, the prince should not turn transparent.)

At 00:F3CF, 00:F437, 00:F48F (= 0x73CF, 0x7437, 0x748F) write:
20 B0 FF  JSR $FFB0
EA        NOP

At 00:FFB0 = 0x7FB0 write:
AE 79 05     LDX $0579 ; current level
E0 LL 00     CPX #$00LL ; level
F0 02        BEQ :1
A9 00        LDA #$00
             :1
22 22 9A 00  JSR $00:9A22
60           RTS
User avatar
Shauing
Calif
Calif
Posts: 431
Joined: April 5th, 2018, 10:38 pm
Contact:

Re: Hacking the SNES ROM

Post by Shauing »

Spoiler: show
David wrote: July 8th, 2020, 11:34 am
Shauing wrote: June 13th, 2020, 3:14 am - A hack for at least one type/skill guard not displaying HP.
At 0x5110 write: DA E2 30 AE 19 05 BF 94 80 01 C2 30 FA 29 FF 00 EA EA EA EA EA EA EA EA F0 05

Then in Pr1SnesLevEd, at Guards -> Settings..., set the "?" option to 1 for those guard types which should not show their HP.

Note that this hack overwrites the code which hides the HP for (regular and brown) skeletons, so you should set this option for them as well.
(Unless you want them to show HP, obviously.)


Details:

Code: Select all

At 00:D110 = 0x5110 write:
DA           PHX
E2 30        SEP #$30
AE 19 05     LDX $0519 ; guardprog
BF 94 80 01  LDA $01:8094,X ; specialcolor
C2 30        REP #$30
FA           PLX
29 FF 00     AND #$00ff
EA ×8        NOP ×8
F0 05        BEQ $D12F ; draw if 0
Can this specific guard or a specific room of a specific level force the Prince to do a short run?
NEW UPDATE! Prince Of Persia: 30th Anniversary Port v1.1.5. Download it today!: viewtopic.php?p=29053#p29053
NEW UPDATE! Prince Of Persia: The Queen Of Light v2.6. Download it today! viewtopic.php?p=33174#p33174
David
The Prince of Persia
The Prince of Persia
Posts: 2846
Joined: December 11th, 2008, 9:48 pm
Location: Hungary

Re: Hacking the SNES ROM

Post by David »

Shauing wrote: July 19th, 2020, 10:22 am Can this specific guard or a specific room of a specific level force the Prince to do a short run?
This hack edits the amazon intro.
At 0x8AAC write the guard type.
At 0x8ABE write: A9 4D (use default animation for guard)
At 0x8AB2 write: EA EA (don't check row of guard)
At 0x8AB8 write the music index (or keep the original).

Optionally, you can disable stunning of the prince:
At 0x8B33 write EA EA EA
And then you can also disable the music:
At 0x8ABB write EA EA EA

Details:

Code: Select all

We edit this part:

01:8AAB: c9 09       CMP #$09 ; <-- change to guard type
01:8AAD: d0 13       BNE $8ac2
amazon intro:
01:8AAF: ad 5c 04    LDA $045c ; Char.row ; CharBlockY
01:8AB2: d0 17       BNE $8acb ; <-- removed
01:8AB4: 20 2d 8b    JSR $8b2d ; force a short run (save+load shad)
01:8AB7: a9 19       LDA #$19
01:8AB9: a2 00       LDX #$00
01:8ABB: 20 4d ea    JSR $ea4d ; play music A ; cuesong ; <-- optionally removed
01:8ABE: a9 87       LDA #$87 ; <-- change to #$4D
01:8AC0: 80 0b       BRA $8acd

force a short run (save+load shad):
01:8B2D: 20 89 b5    JSR $b589 ; save Shad? ; SAVESHAD
01:8B30: 20 ff 8a    JSR $8aff ; force a short run
01:8B33: 20 04 d2    JSR $d204 ; stun? on ; <-- optionally removed
01:8B36: 4c 7d b5    JMP $b57d ; load Shad? ; LOADSHAD
Post Reply