øAslickproductions.org/forum/index.php?PHPSESSID=5f0fck550j2m4m2fpbtkj2vkm1&topic=2190.0e:/My Web Sites/Slick Productions - FFIV Message Board/slickproductions.org/forum/index3786.htmlslickproductions.org/forum/index.php?PHPSESSID=5f0fck550j2m4m2fpbtkj2vkm1&board=17.40e:/My Web Sites/Slick Productions - FFIV Message Board/slickproductions.org/forum/index3786.html.zxS9h^ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÈ °VØ&OKtext/htmlISO-8859-1gzip8:ÖØ&ÿÿÿÿÿÿÿÿWed, 11 Mar 2020 01:21:47 GMT0ó°° ®0®P®€§²ð®R9h^ÿÿÿÿÿÿÿÿ·.Ø& Amusing Bit of Timer Code

Author Topic: Amusing Bit of Timer Code  (Read 2899 times)

LightPhoenix

  • FF5 Hacker
  • *
  • Posts: 130
    • View Profile
Amusing Bit of Timer Code
« on: April 03, 2016, 05:46:36 PM »
I've done this before for some CT code, but I came across this gem in the FF5 timer code:

Code: [Select]
C2/2085: A5 0C        LDA $0C (Are we looking at Stop?)
C2/2087: D0 00        BNE $2089 (uhhhh)
C2/2089: E6 08        INC $08 (return TRUE)

Nothing jumps directly to $2087.

Squall

  • Dark Dragon
  • *
  • Posts: 486
    • View Profile
Re: Amusing Bit of Timer Code
« Reply #1 on: April 04, 2016, 02:28:16 AM »
Is it just me or this code does nothing:
C2/2087: D0 00        BNE $2089

That seems as NOP unless there are other places that modify $2089 ... I think it was called self-modifying code or something :D

Jorgur

  • Moderator
  • *
  • Posts: 437
  • Gender: Male
    • View Profile
Re: Amusing Bit of Timer Code
« Reply #2 on: April 04, 2016, 08:06:45 AM »
Beautiful. A classic jump to the next address.

LightPhoenix

  • FF5 Hacker
  • *
  • Posts: 130
    • View Profile
Re: Amusing Bit of Timer Code
« Reply #3 on: April 09, 2016, 05:36:39 PM »
The timer code is an absolute mess.  I started looking into making additional statuses that worked off of timers (Slow/Haste, Breaks, etc).  I don't think it's impossible, and I have some ideas, but it would require a near complete rework of the system.

Timers in FF5 have one of three states (determined by bits in 3CF1): active, ending, and inactive.  This bit of code returns true if the timer is active or ending (ie, "on").  It's run once for each timer; the only time this return is ever checked is if we're looking at Stop or Paralyze.  Stop prevents other timers from changing state, and Paralyze stops just the ATB timer from the same.  Incidentally, this is why the order of timers is what it is: Stop goes first, then the eight timed statuses, then Paralyze, then ATB.  You can just run the same function eleven times, check the return as needed.

If I had to guess, once upon a time this function checked Stop, then checked other statuses.  They realized they could reuse the code with a return, and this little bit escaped notice.

For further speculation, I'm fairly certain the reason the timer system is a bit of a complicated mess is because the VBlank will interrupt it, and so redundancies needed to be built in.

[EDIT] As a point of clarification, the order of the timers is as follows: Stop, Poison, Reflect, Doom, Mute, Leak, Old, Regen, Singing, Paralyze, ATB.  I've seen Charm listed as timer #3, but this isn't correct (for players at least).
« Last Edit: April 09, 2016, 05:50:25 PM by LightPhoenix »

Squall

  • Dark Dragon
  • *
  • Posts: 486
    • View Profile
Re: Amusing Bit of Timer Code
« Reply #4 on: April 11, 2016, 02:23:09 AM »
Speaking for timers ... does Duration=255 from magic/action mean forever?
Also some magic/actions dont even set Duration, just mark a bit (like Poison).

In general what exactly these numbers mean? Like status on heavy can not be more then 30 ... what is 30 - milliseconds, seconds, ..?

x0_000

  • Siren
  • *
  • Posts: 72
    • View Profile
Re: Amusing Bit of Timer Code
« Reply #5 on: April 11, 2016, 02:35:50 PM »
It's in-game frames. Duration = 255 just sets it to the max duration, I think.

Squall

  • Dark Dragon
  • *
  • Posts: 486
    • View Profile
Re: Amusing Bit of Timer Code
« Reply #6 on: April 12, 2016, 04:32:41 AM »
Sorry I'm totally confused. There 3 terms used by the community when describing time-events:
- timer (are we talking about hardware or just in-game ones)
- in-game frame
- duration (parameter of some Dmg Formulas)

Could you explain to me (starting from scratch) what is what, how the 3 relate?

P.S. I'm trying to 'implement' songs with description: 'stats gradually increase'.
- increase by 1 or xxx?
- when they increase?
* I'm sure its all connected with the above explanation

x0_000

  • Siren
  • *
  • Posts: 72
    • View Profile
Re: Amusing Bit of Timer Code
« Reply #7 on: April 12, 2016, 03:30:21 PM »
The entire game is controlled by a frame counting function (which is recorded as in-game played time); I haven't done any research in it but I'm pretty sure all real-time functions reference this function for timing; i.e. whenever this function increases by 1 all timer functions are called to do something (usually decrease their timer by 1.)

A frame is just the smallest unit of time the game can track (i.e. what the value "1" corresponds to in the frame counter.)

Whenever a status effect that uses a timer is cast, the game activates that timer for the appropriate actor, and sets a value to start counting down from. That value is the duration from the formulas. So for example, in Stop it has a duration of 180, ignoring the level factors and Heavy flag it will last for 180 frames. In comparison, the average ATB value a player actor will have is ~100-80 ticks depending on the job, so a player actor will usually be able to take two actions before the stop effect wears off the target.

For songs, I haven't looked into it in depth yet but I'm pretty sure the way it works is that whenever a song timer cycle is complete, the stat increases by 1, which is why they would need a timer (if they just increased by 1 every frame you could just make it a permanent status effect instead.) This is the same way Regen/Poison works: every time the Regen/Poison timer reaches zero, the game applies the effect and then resets the timer. I've actually looked up the Regen timer to tweak it for Monks in my hack: the original timer value is (110 - Magic) with a minimum of 30 if 110 - magic is less than 30. So for example a character with 10 Magic has a regen timer of 100 ticks, while a character with 60 magicwould have a regen timer of 50 ticks; this means the 60 Mag character would regen twice as often as the 10 Mag character.

The ATB formulas in the algorithms guide are accurate calculations for its timer to the best of my knowledge (although they count upwards instead of downwards so the N1 value is what FFVA uses) so you can use that as a baseline to compare different timer values.

Squall

  • Dark Dragon
  • *
  • Posts: 486
    • View Profile
Re: Amusing Bit of Timer Code
« Reply #8 on: April 13, 2016, 02:32:07 AM »
Woah x0_000, that is excellent description (so comprehensive and clear), Thank you very much!!!

Pretty much all games use a 'frame' or a cycle as the smallest unit a game ticks, except that as PC developer it should be hardware timer, because of the different speed on different processors this frame/cycle may take in real-time.

So if I understood well that mean that for each participant in a battle the game holds a separate set of timers. That seems to me as a lot of memory ... but that is not important. For status effects that have Duration in their DF things are clear. What about statuses like Slow/Haste (mutually exclusive) or  Mini, Toad, Protect, Shell - their Duration is 0 (like forever). To clarify ... do they use own timers or just a bit set in Status?

Duration for some statuses play a role of a duration - like Stop, when timer reach 0 status is removed. On other cases Duration plays a role of a timer - when it reach 0 it trigger event, like Regen you get HP. What about things like HP leak ... for example - what role a P2 plays in DF $29?

P.S. As implication of what you said about songs, if we get a turn in 80-100 ticks, that mean on next turn our party will have maxed stat for SNES/GBA and around 3 turns for PC/iOS. Thats very important if you do things like low level/stats boss battles :D

x0_000

  • Siren
  • *
  • Posts: 72
    • View Profile
Re: Amusing Bit of Timer Code
« Reply #9 on: April 13, 2016, 02:19:03 PM »
[EDIT] As a point of clarification, the order of the timers is as follows: Stop, Poison, Reflect, Doom, Mute, Leak, Old, Regen, Singing, Paralyze, ATB.  I've seen Charm listed as timer #3, but this isn't correct (for players at least).
These are the only status effects that require a timer, every other status effect is permanent until it's dispelled. HP Leak expires when its timer expires, this is noticeable in the Galura fight for example.

And to be clear about songs, songs use the same system as Regen, i.e. you don't get 1 stat/tick, you get 1 stat every time the timer reaches 0 (I don't know what the duration is set to.)

Squall

  • Dark Dragon
  • *
  • Posts: 486
    • View Profile
Re: Amusing Bit of Timer Code
« Reply #10 on: April 14, 2016, 02:28:48 AM »
All clear now.

I guess that formula 110-Magic relate to that code (SNES):
Code: [Select]
C2/25A0: 38           SEC
C2/25A1: A9 6E        LDA #$6E
C2/25A3: ED E4 7B     SBC $7BE4
C2/25A6: 90 04        BCC $25AC
C2/25A8: C9 1E        CMP #$1E
C2/25AA: B0 02        BCS $25AE
C2/25AC: A9 1E        LDA #$1E
C2/25AE: 60           RTS

C2/25AF: A9 1E        LDA #$1E
C2/25B1: 60           RTS

C2/25B2: AD E4 7B     LDA $7BE4
C2/25B5: 4A           LSR
C2/25B6: 85 0E        STA $0E     (Magic Power/2)
C2/25B8: 38           SEC
C2/25B9: AD D8 3E     LDA $3ED8   (Status Duration = Status Duration - (Magic Power/2))
C2/25BC: E5 0E        SBC $0E
C2/25BE: B0 02        BCS $25C2
C2/25C0: A9 01        LDA #$01
C2/25C2: 60           RTS
Do you know the code below - Status Duration = Status Duration - (Magic Power/2), when is performed (because I cant find implicit jump/branch) ? Is it used for all timers or just specific statuses?

According to the description: '$7BE4 = Attacker's Magic Power', so these corrections are made vs Magic of the one that perform the action, not the one receiving? ... What happen when things come from an item like 'Protect Ring' (Auto-Regen)?

P.S. If above assumption is true then its better to get Regen from somebody with highest Magic, because we will get timer event more often :D

x0_000

  • Siren
  • *
  • Posts: 72
    • View Profile
Re: Amusing Bit of Timer Code
« Reply #11 on: April 14, 2016, 01:57:08 PM »
All the repeating timers are caster agnostic so if they run off any stats it should be the target's stats. Not sure what the formula for each timer is, I haven't looked at any of the timers other than Regen.

Edit: Had a look at the song timer, looks like it's a fixed 30 ticks (appropriately modified by Haste/Slow.)

Edit2: I'm not a gigantic octomammoth!!!
« Last Edit: April 14, 2016, 08:43:07 PM by x0_000 »

Squall

  • Dark Dragon
  • *
  • Posts: 486
    • View Profile
Re: Amusing Bit of Timer Code
« Reply #12 on: April 18, 2016, 02:15:48 AM »
So things are just as I thought initially - regardless how/who cast a buff, the benefit depends on the target stats :(

Anyway, congrats x0_000 for the new ... picture :D

LightPhoenix

  • FF5 Hacker
  • *
  • Posts: 130
    • View Profile
Re: Amusing Bit of Timer Code
« Reply #13 on: April 18, 2016, 01:59:40 PM »
Quote from: Squall
So if I understood well that mean that for each participant in a battle the game holds a separate set of timers. That seems to me as a lot of memory ... but that is not important. For status effects that have Duration in their DF things are clear. What about statuses like Slow/Haste (mutually exclusive) or  Mini, Toad, Protect, Shell - their Duration is 0 (like forever). To clarify ... do they use own timers or just a bit set in Status?

Correct - as x0_000 mentioned all of those last until dispelled or until the end of the battle.  They do not have timers, just bits set.

This was my impetus for looking into investigating and expanding the timer code.  The amount of memory (and how it is laid out) is important in that adding new timers is, under the current code, practically impossible.

Quote from: x0_000
All the repeating timers are caster agnostic so if they run off any stats it should be the target's stats. Not sure what the formula for each timer is, I haven't looked at any of the timers other than Regen.

Timer duration formulas can be found at C2/2521 (via indirect jumps), and is called by C2/24FD when a timer is initialized.  I haven't looked at this fully.

End of timer effects are at C2/21B5 (in the previously posted order).  While they generally use the target's data, there's no reason other than finding spare bytes in RAM they couldn't use something else.  From what I can tell, Poison and Regen get dealt with here (but not Leak).

My notes are as follows:

Code: [Select]
(CalculateTimerDuration)
C2/2521: 20 3F 25     JSR $253F CalculateTimerOffset
C2/2524: 8A           TXA (timer index * 2) + atkoffset
C2/2525: 0A           ASL
C2/2526: 18           CLC
C2/2527: 6D D7 3E     ADC $3ED7 (set by various attack formulae, usually to 1)
C2/252A: 0A           ASL
C2/252B: AA           TAX
C2/252C: BF 4A 25 C2  LDA $C2254A,X
C2/2530: 85 08        STA $08
C2/2532: BF 4B 25 C2  LDA $C2254B,X
C2/2536: 85 09        STA $09
C2/2538: A9 C2        LDA #$C2
C2/253A: 85 0A        STA $0A
C2/253C: DC 08 00     JMP [$0008] GetDurationFromFormula

(CalculateTimerOffset)
C2/253F: 8A           TXA (holds timer index)
C2/2540: C2 20        REP #$20
C2/2542: 18           CLC
C2/2543: 65 36        ADC $36 Y = (timer idx) + (targnum * 11)
C2/2545: A8           TAY
C2/2546: 7B           TDC
C2/2547: E2 20        SEP #$20
C2/2549: 60           RTS

Quote from: Squall
According to the description: '$7BE4 = Attacker's Magic Power', so these corrections are made vs Magic of the one that perform the action, not the one receiving? ... What happen when things come from an item like 'Protect Ring' (Auto-Regen)?

Nothing.  That is used to determine duration.  Items that are equipped set the appropriate auto-status bytes, and checks for those statuses generally check both.  That's why you'll see code like LDA $201C,X | ORA $2072,X - it's checking both the regular and auto status bytes.

x0_000

  • Siren
  • *
  • Posts: 72
    • View Profile
Re: Amusing Bit of Timer Code
« Reply #14 on: April 18, 2016, 09:38:54 PM »
I had a look at the timer code in FFVA and have most of the stuff documented now. Note that the following data is the initialization of the timer. After looking at it some more there's RAM space for what value the timer resets to which I haven't looked at yet. Note that there's a bunch of paired entries (for example the ??? after stop is also stop), I just don't know what causes the game to use that branch yet. When I write spellpower that means the value that the game calculates in the damage calculations (something like base spellpower + caster level/4 - defender level/4 + misc). @11082 is a reference to a RAM location that I currently don't remember.

Code: [Select]
080E17D8 - sets timers
080E1884 - branch table that determines formula
8 - sing
7 - regen
6 - old
5 - bio
4 - silence
3 - probably doom?
2 - reflect
1 - poison
0 - stop
080e1b80 stop - spellpower
080e1b7c ??? - 120
080e1b68 poison - stamina + 20 (calls 080e1b4c)
080e1b4c poison? - stamina + 20
080e1b40 reflect - spellpower
080e1b3c ??? - 120
080e1b30 ??? - spellpower (doom?)
080e1b2c doom - 59
080e1b20 silence - spellpower
080e1b00 ??? - 180 - @11082/2
080e1af4 sap (bio) - spellpower
080e1af0 ??? - 180
080e1adc old - 10 (calls 080e1ad8)
080e1ad8 ??? - 10
080e1ac4 calls 080e1aa4
080e1aa4 regen - (110 - magic)
080e1a90 sing - 30 (calls 080e1a8c)
080e1a8c - 30
080e1a64 paralyze? - (spellpower - @11082/2)
080e1a44 ??? - 120 - @11082/2
« Last Edit: April 19, 2016, 01:22:05 PM by x0_000 »