Page 1 of 1

Champion's Max Load question

Posted: Sat Oct 29, 2016 7:49 pm
by Erik Bauer
There is something I can't understand reguarding the Max Load and how it is handled.
It seems that whenever a champion gains a level up that increases his strength, the max load value increases exceeding the strength boost, and then decreases over time reaching the correct number. Is that correct, or am I missing something?

Thank you

Re: Champion's Max Load question

Posted: Sat Oct 29, 2016 9:14 pm
by Jan
I've never noticed that. I don't know but Max Load is influenced not only by current strength but also by current and maximum stamina (see http://dmweb.free.fr/?q=node/691) so maybe the answer lies there. Dunno.

Re: Champion's Max Load question

Posted: Sun Oct 30, 2016 7:00 am
by Saumun
Never noticed it either. I usually see the opposite due to stamina not being at max, then the max load increases when the stamina is regained.
Maybe Paul S or Sophia could explain it.

Re: Champion's Max Load question

Posted: Sun Oct 30, 2016 11:45 am
by Erik Bauer
It's even stranger than I tought... watching my videos I'm seeing this:
Stamm can load 52Kg... I fight against a rock monster, suddenly Stamm hits the Rock Monster and deals a huge amount of damage, reequipping him I can see he can load 75Kg without gaining levels, then a few minutes later, his max load is back to 52.
I'm starting to think there is some item he is wearing that has some hidden effect. Maybe the Berzerker helm (In most RPG games the Berserker skill increases strength and fighting related skills of the character using it trading off with a decrease in defensive skills, making him a fearsome opponent). I'll investigate and report back.

Re: Champion's Max Load question

Posted: Sun Oct 30, 2016 1:23 pm
by Erik Bauer
Nope... Berzerker helm is not influencing it, I've seen the same thing happening to Zed, and he is wearing a regular helmet.
Both times the champions had their stamina vaule near the half of it's maximum. If the math formula contained in the link provided by Saumun is correct, then there must be another thing that is able to change max load. Otherwise there is a small bug in the code handling the formula.

Re: Champion's Max Load question

Posted: Sun Oct 30, 2016 8:27 pm
by Sophia
What version are you playing?

This post by Gambit mentions what you seem to be encountering (in the DOS version of DM, I think?) but doesn't go into detail. I took a quick look at the source code to CSBwin and it seems like it does the right thing, so if you're playing an original version it could be a compiler problem or something.

EDIT: Looking at Christophe's source code, which is the closest to the original source code we're ever going to get, there's this line (line 2043 of CHAMPION.C):

Code: Select all

return (P641_i_Value >>= 1) + (int)(((long)P641_i_Value * (long)L0925_i_CurrentStamina) / L0926_i_HalfMaximumStamina);
There may be some sort of order of operations issue here, and/or the compiler doing something wrong... because if P641_i_Value gets used in the stamina calculation before the new halved value is stored, this would produce the bug you're seeing.

Re: Champion's Max Load question

Posted: Mon Oct 31, 2016 12:08 am
by Paul Stevens
I am not sure that there is any requirement that the operands
of commutative operations such as addition are guaranteed to be
evaluated in any particular order.
But I confess ignorance of the rules governing this.

Re: Champion's Max Load question

Posted: Mon Oct 31, 2016 8:37 am
by Erik Bauer
I'm playing Amiga version, the one republished by Psygnosis after CSB went out, emulated through WinUae. I'm not 100% sure, but I recall noticing the same thing back in the days when I played it unemulated. Can't recall if it happens also in the vanilla Amiga version

Re: Champion's Max Load question

Posted: Mon Oct 31, 2016 10:55 am
by ChristopheF
Congratulations, you have found a new bug in Dungeon Master!
Sophia and Paul are correct. The order of evaluation of the operands depends on the compiler implementation and is not guaranteed by the C language.
I have checked the machine code produced by the various compilers and I have added the following comment in my source code next to the line quoted by Sophia:

BUG: In order to compute the correct adjusted value, the first operand of the addition must be executed first so that its updated value is used in the second operand. However, there is no guarantee that the code produced by the compiler will execute the operands in this particular order. The following compilers produce 'correct' code where the first operand of the addition is executed first: Megamax C (DM 1.x and CSB 2.x for Atari ST), High C (DM 2.0 and CSB 3.1 for FM-Towns), THINK C 4.0 (CSB 3.x for Amiga and X68000, DM 3.0 for X68000). The following compilers produce 'incorrect' code where the second operand of the addition is executed first: Aztec C 3.6a (DM 2.x for Amiga), APW (DM 2.x for Apple IIGS), Turbo C 2.0 (DM 2.0 and CSB 3.1 for PC-98), Turbo C++ 1.01 (DM 3.4 for PC), THINK C 5.0 (DM 3.6 Amiga)

Re: Champion's Max Load question

Posted: Mon Oct 31, 2016 11:11 am
by Erik Bauer
Whew! I'm a bit ahsamed in speaking this, as I spent the last 30 years learning computer programming and then programming by job (not much C and C++ tho), but I did not know that different C compilers could handle operations order in different ways.
I confirm I'm using version 3.6 of DM for my current walktrough, so this must be as you say.

Thank you all for your time and your kind explanations, it's a pleasure to be part of this community :)

Re: Champion's Max Load question

Posted: Mon Oct 31, 2016 3:54 pm
by Paul Stevens
Christophe:

I know you put the words 'correct' and 'incorrect' in quotes. But I think
it would be better to use words such as 'expected' or 'unexpected' so as
to emphasize the fact that either result is 'correct'.

Wow! You have all those compilers at your disposal? That is impressive
all by itself. Thanks and Great Work!

Re: Champion's Max Load question

Posted: Tue Nov 01, 2016 2:40 am
by ChristopheF
You are quite right that "expected/unexpected" is a better choice of words because there is no issue on the compiler side.
The C language does not specify an order of evaluation, therefore each compiler does it in its own way (and all are correct as there is no specification to follow). It is the developer's responsibility to avoid such ambiguous expressions when writing code that is expected to be portable. But it is so easy to write non portable code withoug noticing: even after testing the program on Atari ST, the results were correct as the compiler does things as expected. Because the bug only has minor consequences, they probably never noticed it on other platforms and we had to wait until 2016 for someone to notice and report it! (I personnaly never noticed this while playing the PC version years ago).

About compilers: I don't have the APW compiler for Apple IIGS (not even sure to this day which version/variant was used). I also don't have the FM-Towns compiler (although I have one version that is not the exact one that FTL used).

Re: Champion's Max Load question

Posted: Tue Nov 01, 2016 9:50 am
by Erik Bauer
ChristopheF wrote:You are quite right that "expected/unexpected" is a better choice of words because there is no issue on the compiler side.
The C language does not specify an order of evaluation, therefore each compiler does it in its own way (and all are correct as there is no specification to follow). It is the developer's responsibility to avoid such ambiguous expressions when writing code that is expected to be portable. But it is so easy to write non portable code withoug noticing: even after testing the program on Atari ST, the results were correct as the compiler does things as expected. Because the bug only has minor consequences, they probably never noticed it on other platforms and we had to wait until 2016 for someone to notice and report it! (I personnaly never noticed this while playing the PC version years ago). [...]
Uh, in my ignorance about ANSI (and not ANSI) C standards I'd expect the compiler to use the same order of evaluation that is used in Math, as it should be an universal standard, AFAIK.
But as a computer programmer I'm quite fascinated about this

Re: Champion's Max Load question

Posted: Tue Nov 01, 2016 3:10 pm
by Paul Stevens
universal standard
I've heard that in a far-away galaxy creatures think backwards.

Re: Champion's Max Load question

Posted: Tue Nov 01, 2016 4:42 pm
by Erik Bauer
Paul Stevens wrote: I've heard that in a far-away galaxy creatures think backwards.
LOL that made my day! :D

Re: Champion's Max Load question

Posted: Wed Nov 02, 2016 6:55 am
by terkio
http://en.cppreference.com/w/c/language ... precedence

As is the rule in maths, * / has higher priority than + -, and evaluation is left to right at a same level priority.

Re: Champion's Max Load question

Posted: Wed Nov 02, 2016 7:44 am
by ChristopheF
terkio, the first note on the page you mention leads here: http://en.cppreference.com/w/c/language/eval_order

Re: Champion's Max Load question

Posted: Wed Nov 02, 2016 8:43 am
by Erik Bauer
So there is an important yet (at least to me) subtle difference between operand priority and expression evaluation order in C and C++... now my mind seems to start remembering some info I studied while at school more than 20 years ago and then never used (as I mostly programmed in Visual Basic and VB.Net).
Well, cross compiling a videogame must have been a fascinating yet frustrating hell of a job right in the '80s and '90s :D


EDIT:
Gosh... that grows more fascinating as I think about it... somehow I Wish I had learnt how to program an Amiga back then... well... why not? Retro Programming is an hobby as good as anyother :D

Re: Champion's Max Load question

Posted: Wed Nov 02, 2016 3:31 pm
by Paul Stevens
compiling a videogame must have been ...hell of a job
One quickly learns that when evaluation has side-effects that
you force the compiler to do things in the order you want them
done. One does not do things like

Code: Select all

a = (b++) + c - 2 * b
Whoever wrote that line of code in the Dungeon Master engine
must have forgotten this.

Re: Champion's Max Load question

Posted: Wed Nov 02, 2016 8:17 pm
by Sophia
What I find most interesting and fascinating about this is how the source code we had to work with was close enough to the original source code that we can use it to explain bugs, because the original code almost certainly contained a similar construction that caused a similar bug!

Just for fun I wrote two test programs, which were identical except one featured the lines:

Code: Select all

a >>= 1;
c = a + ((a * b) / b+1);
And the other was:

Code: Select all

c = (a >>= 1) + ((a * b) / b+1);
When I compiled them with clang, they produced identical assembly language code except if I told it to compile without any optimizations whatsoever. The Atari ST compiler used by the DM team must not have optimized particularly much, either, or done something else that wasn't too clever. Of course, the compiler I'm using now is much newer, so I'd certainly hope that C compilers had gotten better in the last 30 years.

Still, it makes the reverse engineering all the more amazing. Christophe, how did you know to write the line that way? :shock:

Re: Champion's Max Load question

Posted: Wed Nov 02, 2016 10:04 pm
by Paul Stevens
I, too, was surprised.
Christophe, how did you know to write the line that way?
You must have had some mechanical code generation. Was it able to do
this particular line without any help? If so, I will add 'amazed' to my
list of adjectives.

Re: Champion's Max Load question

Posted: Thu Nov 03, 2016 8:04 am
by ChristopheF
I did not use any mechanical code generation, I did it all manually. Luckily, and as Sophia suspects, the Megamax C compiler on Atari ST does not perform any kind of optimization at all. The resulting machine code is then really straightforward in almost all cases, so it was relatively easy to understand what it did and write the corresponding C code. There is no reordering of statements, no automatic removal of redundant instructions, etc. so it is quite easy to identify each set of machine instructions that correspond to a single C statement/expression (or even a subexpression).
I check (and fix as necessary) the C code I write so that once compiled, it produces the same machine code as in the original. This step is very important as it ensures my source code is close enough to the original source code. This is a trial and error process. Tedious and time consuming, but really not that hard.
While working on other platforms/compilers, I had to make lots of little changes to the source code to ensure it would work with multiple compilers. One simple example: with Megamax C, most 'while' and 'for' loops produce the same machine code: you can use while or for interchangeably in the source code and it will produce exactly the same machine code. However, this is not true with the Aztec C compiler on Amiga. So when working on the Amiga versions I had to replace some 'while' statements by 'for' statements, and some 'for' by 'while' so that the same source code works fine with both compilers. In a way, this helped me ensure which type of loop the original developers have used.
In the particular case of the function we discuss in this thread, the C code I wrote from the Atari ST machine code just compiled fine without any change for all other versions I've studied. That is why I did not notice this bug: I never compared the machine code on any other version with the Atari ST one, as long as it compiled identically to all the original executables.

Re: Champion's Max Load question

Posted: Thu Nov 03, 2016 10:40 am
by Gambit37
Interesting thread. I don't understand all the tech speak, but I had forgotten about reporting this bug all those years ago. I can't remember if I wrote that based on personal experience, or if it was something Doug told me about when I interviewed him. I'll see if I can find the original interview...!

Re: Champion's Max Load question

Posted: Thu Nov 03, 2016 5:24 pm
by Paul Stevens
I did not use any mechanical code generation, I did it all manually....... Tedious and time consuming
Having done something similar but less difficult, I can say that this
is quite an understatement. Good grief; what a task.

Re: Champion's Max Load question

Posted: Thu Nov 03, 2016 7:05 pm
by ChristopheF
I'll see if I can find the original interview
Will you share it after all those years? ;-)

Re: Champion's Max Load question

Posted: Thu Nov 03, 2016 7:07 pm
by Gambit37
Yes, if I can find it, but they would still need some work as there was personal stuff in there that Doug didn't want me to share.

Re: Champion's Max Load question

Posted: Sun Nov 06, 2016 7:21 am
by Sphenx
Just for reference, here is the corresponding code from SkWin (STAMINA_ADJUSTED_ATTR function). I can't tell how close it is from original DM2 code, but at least this adaptation does not contain the bug, since the 'halve and assign' operation is done separately.

Code: Select all

	i16 si = quantity;
	i16 bp02 = ref->curStamina();
	U16 di = ref->maxStamina() >> 1;
	if (bp02 < di) {
		si >>= 1;
		return (i32(si) * i32(bp02)) / i32(di) +si;
	}
	return si;