VOGONS

Common searches


First post, by serialShinobi

User metadata
Rank Newbie
Rank
Newbie

Hello I would like to understand how to debug a game program written in C. I am using MSVC 1.5 and for debugging I use CodeView 4.10.

I am able to use breakpoints and I will refer to my code. Also I am attaching Tomb3.C, the game I am working on. Source code is from Andre Lamothe's Tricks of the Game Programming Gurus.

My program involves PCX decoding. The main function calls a function to load a PCX and then the program crashes. If I insert a break point in this PCX Loading called function at the point where it begins to load the RGB triple for an MCGA 256 color *.PCX file. The file pointer is opened and the area of memory is loaded with the PCX image data. Then when it is time to load the palette the fseek function has a parameter -768L and SEEK_END so that the file points at the end of the image file and then reverses a number of bytes equal to the sum of the bytes of the RGB triple. A for loop to read this data then ensues.

As I mentioned the program crashes if I step past this breakpoint. I believe I need to find out some way to determine if my file pointer math is off or if the data being read by the for loop is garbage from some out of range / off by one miscalculated memory issue.

I think I need either some way to examine memory or some better way of dealing with this sort of problem. Will anyone teach me something about my options when faced with the problem I have in this situation? I have thought of giving up. But I also wondered what might be out there in the world of debugging since I have never done it and this is probably why I know so little about IBM PC Architecture and the programing of the 8086. One day I want to move on to Michael Abrash's 1995 book Zen of Graphics Programming.

     

line 341: fseek(fp,-768L,SEEK_END)
line 345: for (index=0, index<256; index++)
line 346: {
line 347: // get the red component
line 348: image->palette[index].red = (getc(fp) >> 2);
line 350:
line 351: // get the green component
line 352:
line 353: image->palette[index].green = (getc(fp) >> 2);
line 354:
line 355: // get the blue component
line 356:
line 357: image->palette[index].blue = (getc(fp) >> 2);
line 358:
line 359: } // end for index


Filename
TOMB3.C
File size
19.65 KiB
Downloads
15 downloads
File license
Public domain

Reply 1 of 42, by megatron-uk

User metadata
Rank Oldbie
Rank
Oldbie

If you are doing file io, you should always check that what you have intended to do (seek n bytes, read n bytes) has actually done what you expect.

Seek, read and friends should, depending on their implementation, return the number of bytes actually read, skipped etc. Check those return values!

A decent compiler should warn you that you have not assigned the return values of those calls.

My collection database and technical wiki:
https://www.target-earth.net

Reply 2 of 42, by llm

User metadata
Rank Member
Rank
Member

when i download the files it only contains masses of Ö chars - never got problems downloading any source files

but my tip: always use recent compilers - even if not capable to build for DOS - im using latest LLVM/clang to compile my DOS stuff and find errors (by using #ifdef __clang__ disabling some of the DOS-only code stuff)

Reply 4 of 42, by konc

User metadata
Rank l33t
Rank
l33t

A general advice for "inexplicable" failures is to create a test case that you can follow/debug each step manually. For example a very small file that you will step into every loop iteration and observe all variables involved. A modern IDE can make this significantly easier.

Reply 5 of 42, by serialShinobi

User metadata
Rank Newbie
Rank
Newbie

Thanks j Marsh for finding that. I was sniffing out some kind of count of bytes problem and there is already an improperly terminated counter in the for loop.

But special thanks also for the great ideas from lim and konc. I was way to overwhelmed by this problem. It was a big jumping off point. I will keep the idea of cross compiling with modern compilers and trying to break a problem down to a smaller test case.

Megatron-uk: I have a lot of time ahead of me trying to master file I/o. Thanks for helping me locate this topic of computer programming.

Thanks.

Reply 6 of 42, by llm

User metadata
Rank Member
Rank
Member

one of my projects were im using current clang to compile (not link - because its makes no sense due tos clangs absense of DOS support) a DOS MT32 driver code for Stunts: https://github.com/LowLevelMahn/UnifiedMT15/blob/main/drv.c
see the empty stubs im using when compiling with #ifdef __clang__

im also using clang-tidy for much deeper static anlysis - see this batch-file for clang and clang-tidy call: https://github.com/LowLevelMahn/UnifiedMT15/b … /clang_test.cmd

clang for example don't like your code

<source>:5:35: error: expected ';' in 'for' statement specifier
5 | for (index=0, index<256; index++)
| ^
<source>:5:22: warning: expression result unused [-Wunused-value]
5 | for (index=0, index<256; index++)
| ~~~~~^~~~
1 warning and 1 error generated.
Compiler returned: 1

Reply 7 of 42, by serialShinobi

User metadata
Rank Newbie
Rank
Newbie
llm wrote on 2024-04-17, 14:12:

clang for example don't like your code

To do what you seem to me to be doing, cross compiling, this is going to be a major step up for me. I will definitely work on it. But I really can't understand without starting off a several weeks long journey into the topic of cross compiling for MSDOS programming. Very fascinating idea. Never thought about it even while I had learned of the subject of cross compiling for other platforms such as commodore and better yet is Atari ST cross compiling which has more support from community.

It turns out that my error was not omitting a semicolon in the for loop. I had mistyped while trying to copy my output from MS Codeview. It was on my 80486 and I typed what I had on the old computer to my laptop where I was messaging this forum. I did in fact have a semicolon in my original source. The real problem is unknown to me but seems like I am blindly reading a file without verifying what has been read.

So I am working on teaching myself file I/O to self correct problem with my PCX load function in C. I believe I have a miscalculation when the fseek(); is invoked I think it's not getting the intended pallette data from the file. I need to learn of a way to verify things. If anyone knows how I should be verifying do tell. But for now I am learning.

Right now I need to break my problem down into a self contained test case.

I am reading "Beej's Guide to C programming" by Brian "Beej Jorgensen" Hall.

Section 9.6 Binary File I/O

Example Read Bytes


#include <stdio.h>

int main(void)
{
FILE *fp;
unsigned char bytes[6] = {5, 37, 0, 88, 255, 12};

fp = fopen("output.bin", "wb"); // wb mode for "write binary"!

// In the call to fwrite, the arguments are:
//
// * Pointer to data to write
// * Size of each "piece" of data
// * Count of each "piece" of data
// * FILE*

fwrite(bytes, sizeof(char), 6, fp);

fclose(fp);
}

Example Print bytes to screen


#include <stdio.h>

int main(void)
{
FILE *fp;
unsigned char c;

fp = fopen("output.bin", "rb"); // rb for "read binary"!

while (fread(&c, sizeof(char), 1, fp) > 0)
printf("%d\n", c);
}

I intend to learn how fread(); works and later work on fseek(); I may discover perhaps my "miscalculation" is an EOF byte of type int. So maybe I should back up another byte with fseek(); I still need to try this code but the idea is to find some way to indicate byte that *FILE is on in my program. This byte might indicate for example that it is not the first color byte in the palette of my PCX file and is instead out of the range of bytes for the pallette.

Or maybe I should see if there is an offending byte in the VGA palette registers? Is that hard for one who is new to debugging to learn to check?

Reply 8 of 42, by llm

User metadata
Rank Member
Rank
Member
serialShinobi wrote on 2024-04-18, 14:03:
llm wrote on 2024-04-17, 14:12:

clang for example don't like your code

To do what you seem to me to be doing, cross compiling, this is going to be a major step up for me. I will definitely work on it. But I really can't understand without starting off a several weeks long journey into the topic of cross compiling for MSDOS programming.

im not cross compiling with clang - only with watcom in this project

with clang and clang-tidy in only compile (not link to create an executable or something) to check if my source code contains errors/flaws that decates old compiler just ignore - to get it compile im "dummy" the DOS-only code to make it compile-able

cross compiling with watcom is easy - just call the watcom compiler like you do under dos (or with the target specified for dos) on dos,win16,win32,win64 or linux and the result will be the same exe - no magic
im never compile directly on DOS - only debugging

Reply 9 of 42, by serialShinobi

User metadata
Rank Newbie
Rank
Newbie

im not cross compiling with clang - only with watcom in this project

with clang and clang-tidy in only compile (not link to create an executable or something) to check if my source code contains errors/flaws that decates old compiler just ignore - to get it compile im "dummy" the DOS-only code to make it compile-able

So with clang and clang-tidy I can compile a C program and it's header files into an 16-bit 8086 object code file?

I would tell clang to use what memory model for 16-bit? Tiny model?

And then I can take my object file and headers and link it on an older 16/32 bit machine running dos?

I feel like I have to get steps as to how to do this. Taking it step by step would greatly simplify my efforts.

Thanks

Reply 10 of 42, by llm

User metadata
Rank Member
Rank
Member
serialShinobi wrote on 2024-04-18, 16:42:
So with clang and clang-tidy I can compile a C program and it's header files into an 16-bit 8086 object code file? […]
Show full quote

im not cross compiling with clang - only with watcom in this project

with clang and clang-tidy in only compile (not link to create an executable or something) to check if my source code contains errors/flaws that decates old compiler just ignore - to get it compile im "dummy" the DOS-only code to make it compile-able

So with clang and clang-tidy I can compile a C program and it's header files into an 16-bit 8086 object code file?

I would tell clang to use what memory model for 16-bit? Tiny model?

And then I can take my object file and headers and link it on an older 16/32 bit machine running dos?

I feel like I have to get steps as to how to do this. Taking it step by step would greatly simplify my efforts.

Thanks

no you can't use clang for compiling into anything near to DOS compatible obj files - no way - clang only support 32/64bit and not the OMF obj format thats needed by the most real dos compilers - sorry for not explaining it clearly

im only compiling for syntax and semantic check - the result obj file is of no use for DOS - but the warnings, errors the newer compilers gives are 30 years more developed 😀

but you can use Open Watcom V2 (https://github.com/open-watcom/open-watcom-v2)
current release: https://github.com/open-watcom/open-watcom-v2 … g/Current-build

for building dos exes under DOS/Windows and Linux - for example: https://github.com/LowLevelMahn/NoUniVBE
at this line im running the Open Watcom V2 compiler from Windows 10: https://github.com/LowLevelMahn/NoUniVBE/blob … 9/build.bat#L28
works also this way from linux or DOS itself

Reply 11 of 42, by megatron-uk

User metadata
Rank Oldbie
Rank
Oldbie

Open watcom or GCC (aka djgpp). GCC is probably your more 'modern' of the two.

GCC is protected more only though... I do recall some branch which targeted 16bit code, but it's not a very common variant.

My collection database and technical wiki:
https://www.target-earth.net

Reply 12 of 42, by serialShinobi

User metadata
Rank Newbie
Rank
Newbie
megatron-uk wrote on 2024-04-18, 18:02:

Open watcom or GCC (aka djgpp). GCC is probably your more 'modern' of the two.

GCC is protected more only though... I do recall some branch which targeted 16bit code, but it's not a very common variant.

Open Watcom recently re-emerged after a long retirement. djgpp hasn't seen an update in a long time. Is djgpp library & GCC better than open Watcom for cross compiling? Edit: I just looked at steps to get djgpp installed and they were very tedious. We are talking one newbie mistake and it won't work. Open Watcom is newer and uses git to install.

Clang/LLVM (to me) had an overwhelming number of features great for today's "superscalar" architectures as it was elsewhere stated. What feature should I be looking for to examine my problem with fstream() in C? Some way to help me understand my file processing? Remember my program crashes shortly after the palette bytes are retrieved by fstream().

Last edited by serialShinobi on 2024-04-19, 14:07. Edited 1 time in total.

Reply 13 of 42, by megatron-uk

User metadata
Rank Oldbie
Rank
Oldbie
serialShinobi wrote on 2024-04-19, 13:49:
megatron-uk wrote on 2024-04-18, 18:02:

Open watcom or GCC (aka djgpp). GCC is probably your more 'modern' of the two.

GCC is protected more only though... I do recall some branch which targeted 16bit code, but it's not a very common variant.

Open Watcom recently re-emerged after a long retirement. djgpp hasn't seen an update in a long time. Is djgpp library & GCC better than open Watcom for cross compiling? Which should I try first?

Clang/LLVM (to me) had an overwhelming number of features great for today's "superscalar" architectures as it was elsewhere stated. What feature should I be looking for to examine my problem with fstream() in C? Some way to help me understand my file processing? Remember my program crashes shortly after the palette bytes are retrieved by fstream().

Forget about the ancient version of GCC/DJGPP 2.x .... that's dinosaur technology. Modern DJGPP distributions are built on current-day versions of GCC (last time I looked it was using GCC 12), so you have the benefit of all the advancements in compiler technology over the past 25+ years.

https://github.com/andrewwutw/build-djgpp/releases

Both OpenWatcom and DJGPP work brilliantly as cross compilers - I have no complaints about either. I have both running regularly on Linux, targeting DOS (protected mode via GCC/DJGPP and protected/real via OpenWatcom).

I find GCC a more usable toolset, but then I'm primarily a unix/linux person, and use it all the time elsewhere, so the compiler options, makefile syntax, etc is much more familiar to me. But it doesn't have an easy way to target real mode in DOS, at least not in the main versions which are distributed (such as above).

My collection database and technical wiki:
https://www.target-earth.net

Reply 14 of 42, by megatron-uk

User metadata
Rank Oldbie
Rank
Oldbie

Is the PCX file you are opening one you have constructed yourself? The reason I ask is that the wikipedia definition of the PCX file format says:

If a PCX file has a 256-color palette, it is found 768 bytes from the end of the file. In this case the value in the byte preceding the palette should be 12 (0x0C). The palette is stored as a sequence of RGB triples; its usable length is defined by the number of colors in the image. Colors values in a PCX palette always use 8 bits, regardless of the bit depth of the image.

In your initial test you are seeking -768 bytes from the end of the file, but you don't test for the palette marker byte. You should seek to 768+1 bytes and test for the palette start.

I think what you need to do is something like:

if (fseek(fp, -769, SEEK_END) != 0){
// Handle fseek errors
}

if (getc(fp) != 0x0C){
// Handle a missing palette table
}

for (index=0, index<256, index++){
// You don't need to check getc(), since
// we know you seeked -768 bytes, which
// covers the 3x256 getc() calls.
image->palette[index].red = getc(fp);
image->palette[index].green = getc(fp);
image->palette[index].blue = getc(fp);
}

You need to get into the habit of checking for errors or unexpected values when reading data from files... especially, like with image files, there can be multiple variations and internal structures of the same file format.

Also, another tip, if you don't need buffered IO, and want to save a little bit of memory both at runtime and in the size of the compiled binary, you can use open, lseek, read and similar functions which are direct IO, and not buffered like the f-named equivalents. If you are not using file streams, then you could do away with them if you wanted.

My collection database and technical wiki:
https://www.target-earth.net

Reply 15 of 42, by llm

User metadata
Rank Member
Rank
Member
serialShinobi wrote on 2024-04-19, 13:49:

Is djgpp library & GCC better than open Watcom for cross compiling?

what means better for you? ease of installation, standard support, speed of generated code?...

serialShinobi wrote on 2024-04-19, 13:49:

Open Watcom is newer and uses git to install.

git is a version control system - its not used for installing - or do you mean its hostet at github?

serialShinobi wrote on 2024-04-19, 13:49:

Clang/LLVM (to me) had an overwhelming number of features great for today's "superscalar" architectures as it was elsewhere stated.

i don't know were you get that from - gcc and clang are more or less equal - but clang is much easier to install on windows
im using clang just for compilation and statical analysis - so diagnostic at compilation time - nothing more - and i still don't know if you can differ between compilation and linking 😀

i just said that the clang compiler (gcc would also do) had tell you directly whats wrong with your first loop - no tips about fixing you PCX reading problem in whole

and all this isn't even dos related - why don't you write this PCX reader firstly on Windows using VS2022 Community or something (aka todays compiler diagnostic to prevent silly errors from your side) - if you are working on windows
and port that over to DOS after finishing it

i can't give you others tips because the attached file contains only hundreds of 'Ö' chars for me - as i said never happend before using this forum and attched files - maybe you can re-upload?

Reply 16 of 42, by wbahnassi

User metadata
Rank Member
Rank
Member

I think the tools of the time were perfectly capable of allowing you to discover the problem.
As others mentioned, first file I/O should be always guarded with checks because that data could easily be corrupted or invalidated.
Second, once you are at the crash site with CodeView, it would have sufficed to look up the value of i to figure out it became out of range, thus crashing on write.

FWIW, I use VS2022 and debug Windows 11 drivers and 3D graphics apps as my daily job, and I also use Microsoft C 6 and PWB/CodeView on DOS to build a CGA adventure game. I found CodeView's debugging capabilities quite satisfying once you get used to the interface. My only wish is to add a secondary monitor to DOS to make debugging DOS graphics easier.

Reply 17 of 42, by weedeewee

User metadata
Rank l33t
Rank
l33t
wbahnassi wrote on 2024-04-20, 08:47:

My only wish is to add a secondary monitor to DOS to make debugging DOS graphics easier.

Re: Emulating Monochrome/VGA Multi-Screen setup?

Right to repair is fundamental. You own it, you're allowed to fix it.
How To Ask Questions The Smart Way
Do not ask Why !
https://www.vogonswiki.com/index.php/Serial_port

Reply 18 of 42, by serialShinobi

User metadata
Rank Newbie
Rank
Newbie

I have appended a part of my PCX decoder where I commented out the decoding part and I got my program to run without hanging. It follows at the end of this message. When I run this program the on screen images were, rainbow colored, uncompressed blocks that moved about because the program as a whole includes sprite animation functions.

I am realizing that my problem might be caused by a lack of understanding of fundamental computer organization and architecture of the PC. Where does one learn the kind of advanced programming that goes into graphics programming in c? Do you study assembly code first and then move back to c programming? Is assembler a language of computer organization?

I get an error from code view when I try to run my complete program (unless I have the aforementioned portion commented out)


"The application has violated system integrity due to execution of an invalid instruction and will be terminated"


Does the following code create the condition of moving bytes of type char from a normal area of memory to bytes of a special type far area of memory? Could this condition be the invalid instruction executed by the cpu that code view seemed to complain about?


image->buffer[++count] = data;

And where could I learn about things that related to organization and architecture so that I can, for example use code view to debug a problem where my program is crashed somewhere in the part that I commented out. Can you look an tell me how that part could be better understood by myself if I were not missing some additional training in computer organization?

//////////////////////////////////////////////////////////Begin My PCX Decoder With Trouble Area Commented Out/////////////////////////////////////////////////////////////


void PCX_Init(pcx_picture_ptr image)
{

// this function allocates the buffer region needed to load a pcx file

if (!image->buffer = (char far *)malloc(SCREEN_WIDTH * SCREEN_HEIGHT + 1)))

printf("/ncouldn't allocate screen buffer");
}


void PCX_Load(char *filename, pcx_picture_ptr image, int enable_palette)
{

FILE *fp, *fopen();
int num_bytes,index;
long count;
unsigned char data;
char far *temp_buffer;

fp = fopen(filename, "rb");

// load the header

temp_buffer = (char far *)image;

for (index=0; index<128; index++)
{
temp_buffer[index] = getc(fp);
}

//load the data and decompress into the buffer

/* <--------------------------------------Begin multi-line comment---------------------------------

count = 0;

while(count<=SCREEN_WIDTH * SCREEN_HEIGHT)
{
// get the first piece of data

data = getc(fp);

// is this rle?

if (data>=192 && data<=255)
{
// how many bytes in run?
num_bytes = data-192;

// get the actual data for the run

data = getc(fp);

// replicate data in buffer num_bytes times

while(num_bytes-->0)
{
image->buffer[count++] = data;
Show last 51 lines

} //end while
} // end if rle

else
{
// actual data, just copy it into buffer at next location

image->buffer[++count] = data;

} //end else not rle

} // end while

*/ <----------------------------------End Multi-line comment--------------------------------------------

// move to end of file then back up 768 bytes i.e. to beginning of palette

fseek(fp, -768L, SEEK_CUR);

// load the pallette into the pallette

// get the red component

image->palette[index].red = (getc(fp) >> 2);

// get the green component

image->palette[index].green = (getc(fp) >> 2);

// get the blue component

image->palette[index].blue = (getc(fp) >> 2);

} //end for index

fclose(fp);

// change the palette to newl loaded palette if commanded to do so

if (enable_palette)
{

for (index=0; index<256; index++)
{

Set_Palette_Register(index,(RGB_color_ptr)&image->palette[index]);
} // end if chage palette

} // end PCX_Load

Reply 19 of 42, by llm

User metadata
Rank Member
Rank
Member
serialShinobi wrote on 2024-04-23, 01:19:

I am realizing that my problem might be caused by a lack of understanding of fundamental computer organization and architecture of the PC. Where does one learn the kind of advanced programming that goes into graphics programming in c? Do you study assembly code first and then move back to c programming? Is assembler a language of computer organization?

assembler can help to get into the lowest level details (or maybe get some more speed in the older times) but its definitily not needed to understand and use the computers resources - C is fully capable doing it (with super small exceptions like writing a boot-sector or very few other situations were you need very detailed control about program or code layouting etc.) - so forget assembler for a little time - its like opening a new door with another 1000 things to learn and understand and in the end you will see that C is already enough doing all this

you should first gives us a clear overview what you expirience is
be talky! its not very easy to give you any hints/tips not knowing what your familiar with

1. how many years of C or C++ development?
2. do you understand pointer arithmetic, arrays and how "memory" is used?
3. heap, stack?

very DOS related:
4. do you know the "DOS" memory models? tiny,small,medium
5. do you know what segment:offset pointers are - difference between near/far pointers?
6. do you know what io-ports are (maybe relevant)

are you someway expirienced or a complete beginner with absolute no knowledge

you should learn first C basics on a todays platorm if you struggle with it - it is much harder to learn C in a DOS environment

if you can't explain (in detail) every line of the shown code - then you're struggeling with the basics - is that the case?