PDA

View Full Version : Problems when switching BP7.0 real mode to protected mode



Christian Knudsen
22-08-2007, 04:13 PM
System: Windows 98SE, AMD K6, 124 MB RAM
Compiler/IDE: Borland Pascal 7.0 Protected Mode
Libraries: None

---

Hello all,

I've been working on a game for over a year now and am getting close to completion. However, I've run in to some serious problems. During programming in real mode I've had the game split in two separate programs, that have both functioned perfectly. Now I needed to join these two separate programs to one. I quickly discovered that combined, these two programs took up too much memory and wouldn't compile in real mode. So I switched to protected mode. After installing a new mouse unit that functioned in protected mode, I managed to compile the game, but I keep getting runtime errors, especially the 216 error. I really can't figure out what I'm doing wrong, especially since the two programs ran perfectly in protected mode when they were separate, but now when I'm calling these two programs (now just units) from the main game program, they don't work and crash.

I got some strange results from some of my arrays (the contents of the strings of the arrays would change when the game loop was running, even though nothing in the game loop should alter them!), but when I changed that particular array to a pointer that was allocated with New() and Dispose(), those strange results disappeared. So, I tried to change some other arrays that were behaving strangely, but then I couldn't get the program to run at all.

I'm dumbfounded as to what is causing these errors, as I'm pretty sure I'm allocating everything properly -- and both programs worked when running separately!

Any ideas what's going on? I'm just about to lose my mind here... :(

PS. The game is a console program using only ASCII 'graphics'.

dmantione
23-08-2007, 02:55 PM
Enabled range checking, overflow checking etc. and generate a stack backtrace. That should give an idea as to where the bug is.

Christian Knudsen
23-08-2007, 07:51 PM
Yeah, I'm getting some new errors with range checking and overflow checking enabled, so I'll have to figure out what's causing them.

As to generating a stack backtrace... how do I do this? After some googling I can see that Free Pascal has the two units 'lineinfo' and 'heaptrc' which help with localizing runtime errors in the source code. Does BP7.0 have anything similar? I'm not that experienced with programming and memory addressing, so the memory addresses given with a runtime error is pretty much gibberish to me... and sadly BP7.0 doesn't pinpoint the place of an error in your code in Protected Mode like it does in Real Mode.

Christian Knudsen
23-08-2007, 10:38 PM
I've tried to get parts of my program to run in real mode and then enable range and overflow checking to get BP7.0 to pinpoint where the problems are, but I'm getting errors I can't figure out. For example, the below code excerpt sometimes returns an Error 215: Arithmetic overflow.

The variables X and Y are both Words:

IF (X > 50) AND (Y > 50) AND (X < 50000) AND (Y < 50000) THEN BEGIN
X := X + (20 - Random(40));
Y := Y + (20 - Random(40));
END;


I really don't understand how this could cause an error, since the values assigned to X and Y will always be within the range of a Word.

WILL
23-08-2007, 11:42 PM
You are probably seeing this coming a mile away, but why are you trying to use BP7? Any reason not to switch to using Free Pascal?

Christian Knudsen
23-08-2007, 11:59 PM
Yeah, I was kinda expecting this question to pop up... :)

I wouldn't exactly say that I'm trying to use BP7.0 since I've been programming with that for some years now and have been working on the program in question for more than a year (and was close to completion when I had to switch to Protected Mode and ran into a hornet's nest of runtime errors).

Quite a few units I'm using in my program I've found on the internet and I couldn't get them to run in Free Pascal (such as the GAMES.PAS unit, which allows reading multiple and simultaneous keypresses), so I decided to program it in BP7.0 and stick with what I know.

Christian Knudsen
24-08-2007, 03:14 PM
I've gathered up my code in a zip file and written a short description of the game and the various PAS files. If any kind soul would take a look at it and hopefully tell me where the bugs are, I would be forever in his/her debt!

dmantione
24-08-2007, 07:25 PM
As to generating a stack backtrace... how do I do this?

Press ctrl+F3 when the range check error appears.

dmantione
24-08-2007, 07:27 PM
I really don't understand how this could cause an error, since the values assigned to X and Y will always be within the range of a Word.

Check the intermediate results 20 - random(40) can go below zero, which can cause problems if you mix it with a word. It is probably something like this. Split the expression up in smaller parts, this should give a clue.

Christian Knudsen
24-08-2007, 10:03 PM
I really don't understand how this could cause an error, since the values assigned to X and Y will always be within the range of a Word.

Check the intermediate results 20 - random(40) can go below zero, which can cause problems if you mix it with a word. It is probably something like this. Split the expression up in smaller parts, this should give a clue.

But in the code excerpt I'm making sure that the X and Y values are both higher than 50. Since the lowest value of "20 - Random(40)" is -19, how can this be a problem? 50 - 19 is well within the range of a Word. Is it just that BP7.0 doesn't like mixing Words with arithmetic operations that can go below zero?

dmantione
25-08-2007, 08:14 AM
I recommend to check this. It is not unlikely that the problem is here.

According to the documentation, if you do word+integer, TP converts both to longint before doing the +, then, before doing the assignment, checks if the result is in [0..65535], and then converts back to word.

dmantione
25-08-2007, 08:22 AM
I now understand the problem. According to the TP manual, random is declared like:

function random(range:word):word;

Therefore, the result of "20-random(40)" is considered to be word, and has to be >=0. However when random(40)>20 then this isn't the case, and you get an arithmetic underflow.

If you change your code to:

x:=(x+20)-random(40);

... it should work.

Christian Knudsen
25-08-2007, 09:10 AM
Hey, that actually works now! Great!

I've also been able to find some range checking errors, so now I'm finally getting somewhere! :)

I'm just not completely sure I understand what the problem was... Is it that if you create a arithmetic operation such as the "20 - Random(40)", then the result of that operation must be the same as the result of the function used in the operation (i.e. the Random() function)? I just thought that Random() would return it's value and then that return value would be entered into the arithmetic operation like any other number without affecting it any further, but now it seems that it actually affects what kind of result I can get from the arithmetic operation. Is that understood correctly?

Also, I'm now getting an overflow error in a part of my code used for encrypting and decrypting:

CONST C1 = 52845;
C2 = 11719;


FUNCTION Decrypt(Const S: String; Key: Word): String;
VAR
I: Byte;
TempString : String;
BEGIN
TempString := '';
FOR I := 1 TO Length(S) DO BEGIN
TempString := TempString + Char(Byte(S[I]) XOR (Key SHR 8));
Key := (Byte(S[I]) + Key) * C1 + C2; {<-- getting the overflow error on this line}
END;
Decrypt := TempString;
END;


I found this function on the internet (I actually believed it was downloaded from Borland!) and haven't changed it. I've tried getting the function to output the value of "(Byte(S[I]) + Key) * C1 + C2" to the screen and the result was always with the range Word.

EDIT:
I found some references to this function:
http://www.bsdg.org/SWAG/ENCRYPT/0036.PAS.html
http://www.q3.nu/trucomania/truco.cgi?100&ing

VilleK
25-08-2007, 11:29 AM
Encryption and hashing functions are usually written to intentionally overflow.
It is easy to see that this function will overflow also, the C1 and C2 constants are so high that Key-variable will overflow on first iteration.
So you should allow overflow around this function.
This can be done by using {$Q+} and {$Q-} like this:


&#123;$Q-&#125;
FUNCTION Decrypt&#40;Const S&#58; String; Key&#58; Word&#41;&#58; String;
...
end;
&#123;$Q+&#125;

Christian Knudsen
25-08-2007, 01:18 PM
Ahh, okay. Thanks!

Just out of curiosity, why exactly are they set to intentionally overflow and how come this isn't dangerous to a program?

VilleK
25-08-2007, 03:00 PM
Because numerical overflow can be useful. You basically calculate the value x mod(High(word)+1).


var
x &#58; word; //16 bit unsigned value
y &#58; longword; //16 bit unsigned value
begin
//16 bit
x&#58;=65535; //upper limit for word
x&#58;=x+10; //x overflows and is now nine

//32 bit version of above code
y&#58;=65535;
y&#58;=&#40;y+10&#41; mod 65536; //also nine thanks to mod operation


Hmm... I'm not very good at explaining this. But you see that the 16-bit calculation is simpler and possibly faster.

Christian Knudsen
25-08-2007, 04:38 PM
Ahh, okay. I understand. I was just under the impression that an overflow would cause all kinds of weird stuff, but it actually just makes the variable "start over"?

dmantione
25-08-2007, 05:44 PM
That depends on the cpu word size. If the datatype that overflows matches the cpu word size it starts over. If it doesn't match you get an illegal value.

dmantione
25-08-2007, 05:53 PM
Hey, that actually works now! Great!
I'm just not completely sure I understand what the problem was... Is it that if you create a arithmetic operation such as the "20 - Random(40)", then the result of that operation must be the same as the result of the function used in the operation (i.e. the Random() function)?

If you use a binary operator on an integer type, the compiler decides the common type between the left and right type, that is the smallest type that can handle both the left and right operand. I.e. the common type of "0..99" and "byte" is 0..255. The common type of "integer" and "word" is -32768..65535.

Before doing the operation, the compiler converts both operands to the common type. Then the compiler does the operation.

Assume random(40) returns 35. On the left side there is a constant, and on the right side there is a word. The smallest type that can handle both 20 and a word is 0..65535. So, 20 is "converted" to 0..65535, and 35 is "converted" to 0..65535. Then the compiler subtracts: 20-35=-15. -15 is not in the range of the common type 0..65535, therefore, you have an arithmetic overflow.

Christian Knudsen
25-08-2007, 06:04 PM
Okay, I get it now. Thanks for the explanation! :)

I'm getting my program to work as well as learning a bunch of new stuff... this is great!

Christian Knudsen
25-08-2007, 10:04 PM
Now I've run into another problem...

I was using a unit found on the net for playing multiple simultaneous sounds in my game (http://www.programmersheaven.com/download/1157/download.aspx) and it worked like a charm in real mode. However, to get it to work in protected mode, the IDE needs to leave enough extended memory for the sounds it has to load. From the unit's .doc file:


This library will work in the real-mode IDE but requires a little work
to get it to work in the protected mode IDE. By default, the IDE's DPMI
extender uses all extended memory. The demo requires 200k of extended
memory, and any programs using SMIX require extended memory to hold all
the sounds. You can decrease the amount of extended memory used by the
IDE by setting the DPMIMEM environment variable&#58;
SET DPMIMEM=MAXMEM x
where x is the number of kilobytes of extended memory to use for the
IDE. You will have to find a balance between memory left over for your
program and memory available for IDE use. &#40;Source code, online help,
compilation...&#41;


I can't figure out where to set the DPMIMEM environment variable...?

VilleK
25-08-2007, 10:45 PM
Just open a command prompt (cmd.exe) and type:


SET DPMIMEM=MAXMEM 10000

Do this before starting BP.

From the comments it looks like it is only the IDE that use this setting. Have you tried to compile an exe-file of your program and run it after exiting the IDE?

Christian Knudsen
25-08-2007, 11:34 PM
I tried running the compiled exe outside the IDE and the program crashes with a runtime 216 error. I think I'll just try to get everything working without sound and music, and then maybe add it at some later time...

VilleK
26-08-2007, 11:38 AM
That depends on the cpu word size. If the datatype that overflows matches the cpu word size it starts over. If it doesn't match you get an illegal value.

So you mean that Free Pascal does not "start over" Byte and Word-types on a Intel32 cpu?

Christian Knudsen
12-09-2007, 01:21 PM
Okay, then... I've been able to fix most bugs and the program runs fairly stable now. However, there seems to be some crashes occuring when there are many other applications running in Windows and I think it's because of an array of pointers, I'm using. I'm allocating and deallocating them correctly and normally there's no problem, but sometimes they cause an error 216 (mostly when there are a lot of other applications running). I'm thinking that I might now have set the correct memory settings for compiling in protected mode, but I can't figure out what the correct memory setting would be, since there's only the Stack Size to set and it seems that no matter what I set this to, it runs without problems (even though I've tried both setting it to 0 and the max value!). Any ideas what could be going on?

dmantione
12-09-2007, 02:47 PM
So you mean that Free Pascal does not "start over" Byte and Word-types on a Intel32 cpu?

If you have:


var a:word;
b:cardinal;

begin
b:=65535;
b:=a+1;


... there is no start over. However, my comment was mostly targetted at constructions like:

var a:0..9;

begin
a:=9;
a:=a+1;


... there is no start over in these cases.