CSBwin bug: attack resistances Antifire/Antimagic/Vitality
Posted: Tue Aug 20, 2013 5:29 pm
There have been several discussion threads in the past about Antifire and Antimagic being ineffective because of a bug. Examples:
http://www.dungeon-master.com/forum/vie ... 27&t=27791
http://www.dungeon-master.com/forum/vie ... 46&t=23134
Today I can say I have the final explanation on this question. I am 100% positive that this bug is found in CSBwin only and not in the original game on Atari ST. Sophia identified this bug in the past but it was never fixed in CSBwin (except in a special test version).
The bug lies in the function named TAG016426, as found in the CSBwin 12.3 source code (in Character.cpp).
Paul, when converting the game you were confused by some strange machine code and added this comment in your source code: "They stored a word and fetch a byte!"
Here is a disassembly of the original machine code for this function as found in CSB 2.0 for Atari ST:
The strange code was not intended by the FTL developers because in fact, the compiler is responsible for it. I know that because I have studied the behavior of the Megamax C compiler that FTL used to develop the game. In fact, I have reverse engineered that function back to C source code. Here is the function as FTL wrote it in C language (except for the function and variable names):
If you compile this function with the Megamax compiler, the resulting machine code is exactly the same as the one found in the original game. I did check this.
As you see, there is only one local variable L0927_i_Factor, stored in a register (the compiler actually assigns d7 to store L0927_i_Factor).
The 10 bytes of local variable storage allocated by the instruction "link a6,#-$000A" are for an additional "hidden" variable added automatically by the compiler and in which the program stores a temporary value with instruction "move.w d0,-$000A(a6)".
Sometimes, when evaluating an expression, the compiler needs to store a temporary value somewhere. In this purpose, the compiler creates an additional local variable with a fixed size of 10 bytes (don't ask me why this size), even if the temporary value to store is smaller. That is why the code looks confusing. Note that there are many other functions in the game where such 10 bytes temporary local variables are present, all added by the compiler.
Consequently, the current code in CSBwin is wrong in function TAG016426:
and it should be replaced by this one because there is no division by 256 nor any word to byte conversion in the original code:
I hope this can convince you to fix this bug.
http://www.dungeon-master.com/forum/vie ... 27&t=27791
http://www.dungeon-master.com/forum/vie ... 46&t=23134
Today I can say I have the final explanation on this question. I am 100% positive that this bug is found in CSBwin only and not in the original game on Atari ST. Sophia identified this bug in the past but it was never fixed in CSBwin (except in a special test version).
The bug lies in the function named TAG016426, as found in the CSBwin 12.3 source code (in Character.cpp).
Paul, when converting the game you were confused by some strange machine code and added this comment in your source code: "They stored a word and fetch a byte!"
Here is a disassembly of the original machine code for this function as found in CSB 2.0 for Atari ST:
Code: Select all
link a6,#-$000A
move.w d7,-(a7)
move.w $000C(a6),d0
movea.l $0008(a6),a0
muls #$0003,d0
lea $0047(a0),a0
adda.w d0,a0
move.b (a0),d0 The byte containing the current value of the statistic (like Antifire or Antimagic) is copied to d0
move.w d0,-$000A(a6) A word is stored in a local variable, with the high byte not properly initialized. Stange, but in fact this is not an issue, see below.
move.w #$00AA,d0
clr.w d3 This high byte of d3 is set to 0
move.b -$000A(a6),d3 Only the low byte of the word is used from the local variable, so we don't care what the value of the high byte was
sub.w d3,d0
move.w d0,d7 d7 now contains 170 - CurrentStatisticValue. There is no division by 256 to be found anywhere.
cmpi.w #$0010,d0
bge.s L16460
move.w $000E(a6),d0
lsr.w #3,d0
bra.s L16470
L16460 move.w d7,-(a7)
move.w #$0007,-(a7)
move.w $000E(a6),-(a7)
jsr $0198(a5)
addq.l #6,a7
L16470 move.w (a7)+,d7
unlk a6
rts
Code: Select all
unsigned int F307_fzzz_CHAMPION_GetStatisticAdjustedAttack(P642_ps_Champion, P643_ui_StatisticIndex, P644_ui_Attack)
CHAMPION* P642_ps_Champion;
unsigned int P643_ui_StatisticIndex;
unsigned int P644_ui_Attack;
{
register int L0927_i_Factor;
if ((L0927_i_Factor = 170 - P642_ps_Champion->Statistics[P643_ui_StatisticIndex][INDEX1_CURRENT]) < 16) {
return P644_ui_Attack >> 3;
}
return F030_aaaW_MAIN_GetScaledProduct(P644_ui_Attack, 7, L0927_i_Factor);
}
As you see, there is only one local variable L0927_i_Factor, stored in a register (the compiler actually assigns d7 to store L0927_i_Factor).
The 10 bytes of local variable storage allocated by the instruction "link a6,#-$000A" are for an additional "hidden" variable added automatically by the compiler and in which the program stores a temporary value with instruction "move.w d0,-$000A(a6)".
Sometimes, when evaluating an expression, the compiler needs to store a temporary value somewhere. In this purpose, the compiler creates an additional local variable with a fixed size of 10 bytes (don't ask me why this size), even if the temporary value to store is smaller. That is why the code looks confusing. Note that there are many other functions in the game where such 10 bytes temporary local variables are present, all added by the compiler.
Consequently, the current code in CSBwin is wrong in function TAG016426:
Code: Select all
D7W = sw(170 - w_10/256);
Code: Select all
D7W = sw(170 - w_10);