Active Time Battle Code and Delay

General Information[edit]

Thanks to Pseudoarc and inuksuk.

Chrono Trigger[edit]

While playing Chrono Trigger, I noticed that ATB recharge time was unusual with dual and triple techs. You can see this in action at (not my video) https://www.twitch.tv/videos/937335977. Google search gave a few results with others noticing the same phenomenon but no description of what the exact penalty is.

The relevant code is in the subroutine beginning at $C1BD6F. A relevant code fragment is below: Code: [Select]

$C1/BE6D AD 8C B1    LDA $B18C  [$7E:B18C]   
$C1/BE70 AA          TAX                     
$C1/BE71 BF DC 2B CC LDA $CC2BDC,x[$CC:2BE2]
$C1/BE75 29 0F       AND #$0F               
$C1/BE77 AA          TAX                     
$C1/BE78 B9 AB AF    LDA $AFAB,y[$7E:AFB1]   
$C1/BE7B C9 FF       CMP #$FF               
$C1/BE7D F0 25       BEQ $25    [$BEA4]     
$C1/BE7F 7B          TDC                     
$C1/BE80 86 28       STX $28    [$00:0028]   
$C1/BE82 B9 AB AF    LDA $AFAB,y[$7E:AFB1]   
$C1/BE85 AA          TAX                     
$C1/BE86 86 2A       STX $2A    [$00:002A]   
$C1/BE88 20 0B C9    JSR $C90B  [$C1:C90B]   
$C1/BE8B A6 2C       LDX $2C    [$00:002C]   
$C1/BE8D 86 28       STX $28    [$00:0028]   
$C1/BE8F A2 0A 00    LDX #$000A             
$C1/BE92 86 2A       STX $2A    [$00:002A]   
$C1/BE94 20 2A C9    JSR $C92A  [$C1:C92A]   
$C1/BE97 A5 2C       LDA $2C    [$00:002C]   
$C1/BE99 18          CLC                     
$C1/BE9A 79 AB AF    ADC $AFAB,y
$C1/BE9D 90 02       BCC $02    [$BEA1]     
$C1/BE9F A9 FF       LDA #$FF               
$C1/BEA1 99 AB AF    STA $AFAB,y
$C1/BEA4 99 DD 99    STA $99DD,y
$C1/BEA7 99 22 9F    STA $9F22,y

At the start of this fragment, Y holds the index of the player character (0,1, or 2) whose ATB is being reset after a tech. The tech id is in $B18C. The tech id is used as an index into memory beginning at $CC2BDC. Only the four low order bits of this number are kept. Call this atb_pen.

At the point in the subroutine, the character's normal max_atb is sitting in $AFAB+Y. For the record, I chased this down, and max_atb=(1+battle_speed)(25-char_speed). This is the number of frames it takes for the atb to fill up. What happens next is that max_atb is rewritten with max_atb + (max_atb*atb_pen)/10. In other words, an increase of atb_pen*10 percent. The final value is stored back in $AFAB+y as well as a few other places.

The story gets a little bit more interesting. Here's a view of the range beginning at $CC2BDC. Code: [Select]

00 10 20 30 30 50 30 50 60 10 10 20 20 10 20 30
30 10 10 10 20 20 30 40 50 10 10 20 20 20 30 30
40 10 10 20 20 30 30 30 50 10 10 20 20 10 30 40
50 20 20 20 20 20 10 30 40 11 32 53 11 31 53 11
23 54 11 23 35 11 23 23 21 33 35 11 22 22 22 33
23 11 22 43 11 23 43 11 33 43 21 22 32 12 12 23
11 12 33 31 32 55 53 31 33 35 23 53 35 12 12 21
33 52 32 11 53 03 02 03 03 03 03 03 01 04 01 04
02 03 03 02 FF FF FF FF FF FF FF FF FF FF FF FF


These numbers are the atb_pen values we described above in tech_id order. The basic attack is first with a 00 penalty. Then come each character's single techs. These all have the form X0, which might seem weird because it was the *low* order bits that were used above. It turns out while the low order bits are used as the penalty for some, the high order bits are used for the others. From what I can gather, whoever is first in the performance group gets the high order bits. Second and third seem to get the lower order four bits.

Here's the corresponding code for first in the performance group. Code: [Select]

$C1/BDF1 AD 8C B1    LDA $B18C  [$7E:B18C]   
$C1/BDF4 AA          TAX                     
$C1/BDF5 BF DC 2B CC LDA $CC2BDC,x[$CC:2BE2]
$C1/BDF9 4A          LSR A                   
$C1/BDFA 4A          LSR A                   
$C1/BDFB 4A          LSR A                   
$C1/BDFC 4A          LSR A                   
$C1/BDFD 29 0F       AND #$0F               
$C1/BDFF AA          TAX                     
$C1/BE00 B9 AB AF    LDA $AFAB,y
$C1/BE03 C9 FF       CMP #$FF               
$C1/BE05 F0 23       BEQ $23    [$BE2A]     
$C1/BE07 7B          TDC                     
$C1/BE08 86 28       STX $28    [$00:0028]   
$C1/BE0A B9 AB AF    LDA $AFAB,y
$C1/BE0D AA          TAX                     
$C1/BE0E 86 2A       STX $2A    [$00:002A]   
$C1/BE10 20 0B C9    JSR $C90B  [$C1:C90B]   
$C1/BE13 A6 2C       LDX $2C    [$00:002C]   
$C1/BE15 86 28       STX $28    [$00:0028]   
$C1/BE17 A2 0A 00    LDX #$000A             
$C1/BE1A 20 2A C9    JSR $C92A  [$C1:C92A]   
$C1/BE1D A5 2C       LDA $2C    [$00:002C]   
$C1/BE1F 18          CLC                     
$C1/BE20 79 AB AF    ADC $AFAB,y[$7E:AFB1]
$C1/BE23 90 02       BCC $02    [$BE27]     
$C1/BE25 A9 FF       LDA #$FF               
$C1/BE27 99 AB AF    STA $AFAB,y
$C1/BE2A 99 22 9F    STA $9F22,y

Looks good, right? The value is shifted right four times so we use the high bits instead of the low ones. One crucial difference is that the value 10 for atb_pen/10 is never actually stored. After the LDX #$000A there should be STX $28 to pass into the division subroutine. Because this is missing, the division errors out and returns 0. There is no resulting atb penalty.

This is not too hard to fix. Fixing it does introduce another bug though. Because the new atb is not stored in $99DD+y like in the first code fragment, the atb bar bugs out for the first few frames. Adding in a STA $99DD,y instruction after computation fixes this bug. Be warned that everyone will be a big sluggish if you fix this. Luminaire, for example, carries a hefty 60% max_atb penalty. If you don't want any atb penalties, you can just NOP out the STX at $C1BE92 (edit: safer to change to STX #$00).

While I hope this is of intrinsic interest, it may be of some pratical use for rom hackers. If you expand the tech list, you may find yourself going beyond the expected range into those FFs and load up crazy atb penalties for your double techs and triple techs. This also may give some design space for balancing out powerful techs with more wild atb penalties.

Enjoy!

This great write-up helped find where Haste and Slow get calculated, too. Double thanks! (That subroutine is at $01BD6F.)

From: Modification