News: 11 March 2016 - Forum Rules
Current Moderators - DarkSol, KingMike, MathOnNapkins, Azkadellia, Danke

Author Topic: How do I multiply a JSR  (Read 1369 times)

joe73ffdq

  • Full Member
  • ***
  • Posts: 201
    • View Profile
How do I multiply a JSR
« on: May 06, 2021, 06:17:49 pm »
I got back into DW2, and I am actually having success this time around, but there is one thing I cant figure out.

The routine for Wizards Ring is in bank 4... Creating new items is in bank 6... There is a JSR in bank F that uses a routine from 4... The MMC5 patch doesnt work with my mod, because of too much code changed is my guess. MMC1 doesnt allow 6 to F to 4, and RTS in 6.

a597 2022f7 a599

I can use this to create Magic Water... Then I double it for the Wizards Ring.

There are a bunch of 2 and 3 byte code combinations that I tried, and nothing works placed before this code. Using 21 bytes for 2 items works fine, but there isnt enough space to create the full MP restore the Elfin Elixir. With the base power at 0C in Bank 4, I would need to repeat those 7 bytes over 20 times, and that would be 150+ bytes.

Is there a multiplier that can be placed in tandem with a JSR...

Cyneprepou4uk

  • Hero Member
  • *****
  • Posts: 634
  • I am the baldest romhacker
    • View Profile
Re: How do I multiply a JSR
« Reply #1 on: May 06, 2021, 10:28:06 pm »
Here's a basic multiplier routine (original game probably has a better one though). You load 2 values into A and X beforehand which you want to multiply, then JSR to the routine, then read temporary addresses to get the result.

Code: [Select]
sub_multiply:
TAY
LDA #$00
STA tmp_lo
STA tmp_hi
@loop:
TYA
CLC
ADC tmp_lo
STA tmp_lo
BCC @skip
INC tmp_hi
@skip:
DEX
BNE @loop
RTS
« Last Edit: May 07, 2021, 09:37:53 am by Cyneprepou4uk »

abw

  • Hero Member
  • *****
  • Posts: 580
    • View Profile
Re: How do I multiply a JSR
« Reply #2 on: May 06, 2021, 10:51:21 pm »
I got back into DW2, and I am actually having success this time around, but there is one thing I cant figure out.
You do realize that your post is basically unintelligible to almost everyone, right? :P

Here's what I think you meant, with a bunch of extra details:

In Dragon Warrior II (NES), the routine that is called to restore a character's MP by a small amount when the Wizard's Ring item is used is located in bank 4 ($04:$A49E-$04:$A4C3) but the code for using items is located in bank 6 (the relevant section is $06:$97F4-$06:$980C); the bank 6 code reaches the bank 4 code via a JSR to the fixed bank at $0F:$F722, which swaps in bank 4, JSRs to the bank 4 code, then swaps bank 6 back in before its RTS.

Code: [Select]
0x01980C|$06:$97FC:A5 97    LDA $97    ; subject hero ID $97
; call to code in a different bank ($0F:$F722)
0x01980E|$06:$97FE:20 22 F7 JSR $F722  ; restore the hero ID in A's MP by a random amount based on the Wizard's Ring's power; returns a random number between $03 and #$0A in A and $99
0x019811|$06:$9801:A5 99    LDA $99    ; chance for Wizard's Ring to break

You want to create a new item named Magic Water which uses the original Wizard's Ring code to restore a character's MP by a small amount and double the effect of the existing Wizard's Ring item by calling the original routine twice.

You tried some stuff that didn't do what you wanted (I'm not sure exactly what). You're also trying to create a second new item named Elfin Elixir that will fully restore a character's MP, and you plan to make that happen by calling the Wizard Ring's routine for restoring a small amount of MP over and over again, but that takes up more free space than you have available, so you're looking for a more compact way of executing the same code over and over again.


Assuming I guessed right, what you're looking for is called a loop. The basic idea looks like this:
Code: [Select]
LDX ?? ; the number of times you want to execute the code
@loop:
... ; the code you want to execute over and over again
DEX ; decrease the remaining number of times you want to execute the code by 1
BNE @loop ; if you still want to execute the code more than 0 times, execute it again
but in this particular case all of the 6502 registers get clobbered by $F722, so you need to save/restore X and reset A between calls to $F722, so you'd end up with something like this:
Code: [Select]
LDX #$07 ; the number of times you want to execute the code
@loop:
TXA ; save the number of times you want to execute the code on the stack
PHA
LDA $97 ; subject hero ID $97
JSR $F722 ; restore the hero ID in A's MP by a random amount based on the Wizard's Ring's power; returns a random number between $03 and #$0A in A and $99
PLA ; restore the number of times you want to execute the code from the stack
TAX
DEX ; decrease the remaining number of times you want to execute the code by 1
BNE @loop ; if you still want to execute the code more than 0 times, execute it again

However, since the code for using items is already in bank 6, a slightly shorter and very much faster approach would be to just fully restore the character's MP directly, making use of one of the existing hero ID * #$12 lookup tables in bank 6. Something like this should work quite nicely:
Code: [Select]
LDX $97 ; subject hero ID $97
LDY $9D55,X ; pre-computed offsets for the start of each hero's data at $062D
LDA $0632,Y ; character's Max MP
STA $063D,Y ; character's Current MP

Here's a basic multiplier routine (original game probably has a better one though). You load 2 values into A and X beforehand which you want to multiply, then JSR to the routine, then read temporary addresses to get the result.
The game actually has several multiplication routines, the most generally useful of which is probably the one at $0F:$C339, though depending on where your values are already stored and which bank is currently loaded, $0F:$F0F4 or one of the routines in bank 2 or 4 might be a better fit.

joe73ffdq

  • Full Member
  • ***
  • Posts: 201
    • View Profile
Re: How do I multiply a JSR
« Reply #3 on: May 08, 2021, 03:56:58 am »
Magic Water

202efa 1e - display string ID specified by next byte + #$0100
a597 2022f7 - restore MP based on the Wizard's Ring's power
201d96 - remove item
4c4895 - end TALK/ITEM routines


Wizards Ring - Inject a597 2022f7 a 2nd time into the Wizards Ring routine


Elfin Elixir

202efa 1e - display string ID specified by next byte + #$0100
LDX - a697 - subject hero ID $97
LDY,X - bc559d - pre-computed offsets for the start of each hero's data at $062D
LDA,Y - b93206 - character's Max MP
STA,Y - 993d06 - character's Current MP
201d96 - remove item
4c4895 - end TALK/ITEM routines

---

abw... Thank you for the detailed information  :beer: I dint know how to describe what I wanted to do, and you presented it verbatim :)

When I made the post, I didnt think there was a need to show code structure... Doubling the Wizards ring was easy, and making the Magic Water was easy... I was looking for that magical LDA a9 xx, so I didnt have to repeat a597 2022f7 over and over...

LDX xx - the number of times you want to execute the code
@loop
... - the code you want to execute over and over again
DEX   - decrease the remaining number of times you want to execute the code by 1
BNE @loop - if you still want to execute the code more than 0 times, execute it again

LDX xx - the number of times you want to execute the code ... This was the exact question I was asking, and didnt know how to phrase it...

207bc7 - restore full HP/MP to all living party members
20cc8d - restore full HP to all living party members
20ec8d - restore full MP to all living party members
2022f7 - restore MP based on the Wizard's Ring's power
20cf8b - Heal
20d78b - Healmore
20df8b - Healall
4c9f8c - Revive

abw... You gave a detailed disassambly for DW2, and it has helped me create and iron out a few things... Healing items are easy, and everything is just bank 6... Panacea is Healall + Antidote... Herb, Lunch, Amitsnack, (havent decided what to call it), is from the Healmore routine, which is unused... Yggdrasil Dew is Healall + Antidote + Revive.

There are no single options for mp with the above JSR's, except the one fixed item Wizards Ring.

Herb, Lunch, Amitsnack - small hp gain using the unused healmore routine
Medical Herb
Panacea - healall + antidote
Yggdrasil Dew - healall + antidote + revive

Magic Water - 1/2 Wizards Ring power
Wizards Ring
Elfin Elixir - full mp

As you see, it was that last item, and I was looking for that magical A9, A2, or A0, so I could multiply a597 2022f7 for max mp restore... I did correct myself on the last bytes though. a5 99 is the chance for Wizards Ring to break. Even with just 5 bytes to repeat, there was no way for max mp.

Thank you for the help and clarification  :banghead::beer::crazy::)



« Last Edit: May 08, 2021, 04:43:45 am by joe73ffdq »

abw

  • Hero Member
  • *****
  • Posts: 580
    • View Profile
Re: How do I multiply a JSR
« Reply #4 on: May 10, 2021, 11:29:35 pm »
Providing more information in your posts is usually helpful for others, especially when you're talking about things that only exist in your hack or that nobody else is likely to know about ;).


Actually, since the original code preserves both the X and Y registers from bank 6 all the way until you reach $04:$A4A1, there's another more flexible way that you could handle having the Wizard's Ring heal double the amount of Magic Water: have each item's code set the desired power in X or Y before calling $F722 and then make the code for restoring MP read the power from that register. So instead of
Code: [Select]
;$06:$97FC
LDA $97 ; subject hero ID $97
JSR $F722 ; restore the hero ID in A's MP by a random amount based on the Wizard's Ring's power; returns a random number between $03 and #$0A in A and $99
...
;$04:$A4A1
LDA #$2C ; power for Item ID #$3D: Wizard's Ring
JSR $AE8C ; $B0 = random number tightly distributed around A / 2, $B1 = #$00
you could do
Code: [Select]
;$06:$97FC
LDA $97 ; subject hero ID $97
LDY #$58 ; new power for Item ID #$3D: Wizard's Ring
JSR $F722 ; restore the hero ID in A's MP by a random amount based on the Wizard's Ring's power; returns a random number between $03 and #$0A in A and $99
...
;$04:$A4A1
TYA ; item's power
JSR $AE8C ; $B0 = random number tightly distributed around A / 2, $B1 = #$00
and then for Magic Water you'd just use LDY #$2C instead. Or if you want to keep the Wizard's Ring at its original power of #$2C, use #$16 for Magic Water.