VOGONS

Common searches


First post, by videogamer555

User metadata
Rank Member
Rank
Member

I've put Turbo C 2.01 on my DosBox. However I need to know a bit more about how it works with far pointers. I can use MK_FP to initialize a variable declared as a far pointer, such as

unsigned char far *FarPointerToByte = MK_FP(segment,offset);

However, once I've initialized it, how do I update its offset (while keeping the same segment, and not having to call MK_FP again)? I need to use it in a loop to reference all the VGA ram for rapidly drawing an image (must be able to write an entire image to the screen in less then 1/30th of a second, the duration of a video frame). I've tried pokeb, which allows a segment, offset, and value to be specified on each call of the function. But that's too slow. It takes about 1/10th of a second to draw, and as such I can actually see the image before it's complete. I should be able to see a black screen before drawing the image, and then the image after it's drawn, and not see (even for a fraction of a second) a partially drawn image.

Thus I've decided to skip using pokeb, and go straight to directly writing to memory with a far pointer. However, I need to know how to (after having initialized the pointer with MK_FP) actually update the offset portion of that far pointer, so as to scan through all of VGA memory, so as to nearly instantly write a full image into VGA memory.

However, I can't figure out how to update the offset of the the pointer.
I thought I could do this:

unsigned char far *FarPointerToByte = MK_FP(segment,offset);
FarPointerToByte=FarPointerToByte+1;

But doing this gives me a "Non-portable pointer assignment" warning. It doesn't rise to the level of an error, so compiling completes, but when I run the compiled EXE file, I end up having the program lock up. Remove the line FarPointerToByte=FarPointerToByte+1; and the program completes and returns to DOS, just like it should.

I've spent well over an hour looking on Google for how to increment a Turbo C far pointer's offset, using many different search terms that I thought described my problem, and haven't found anything whatsoever. I can't even find forums where others are asking how to do this particular thing. So I've decided to post my question here. Hope you guys can help me.

Reply 1 of 15, by Jepael

User metadata
Rank Oldbie
Rank
Oldbie

I have not tried, but would this work?

FarPointerToByte++;

But I don't think you want to increase the pointer like that.

Instead, you can use the pointer to a byte as an array, so you can just access offset x in video memory like this:

FarPointerToByte[x]=y;
videogamer555 wrote:

I need to use it in a loop to reference all the VGA ram for rapidly drawing an image (must be able to write an entire image to the screen in less then 1/30th of a second, the duration of a video frame).

I don't know how are you drawing the image, but if you are copying an image from main memory to video memory, a for loop to copy 64000 bytes would not be as fast as you need so you have a wrong approach. Have you checked out memcpy() or inline assembly with to copy blocks of data?

Reply 2 of 15, by videogamer555

User metadata
Rank Member
Rank
Member
Jepael wrote:
I have not tried, but would this work? […]
Show full quote

I have not tried, but would this work?

FarPointerToByte++;

But I don't think you want to increase the pointer like that.

Instead, you can use the pointer to a byte as an array, so you can just access offset x in video memory like this:

FarPointerToByte[x]=y;
videogamer555 wrote:

I need to use it in a loop to reference all the VGA ram for rapidly drawing an image (must be able to write an entire image to the screen in less then 1/30th of a second, the duration of a video frame).

I don't know how are you drawing the image, but if you are copying an image from main memory to video memory, a for loop to copy 64000 bytes would not be as fast as you need so you have a wrong approach. Have you checked out memcpy() or inline assembly with to copy blocks of data?

Does memcpy work with far pointers? I can't seem to get it to work.

Reply 3 of 15, by vladstamate

User metadata
Rank Oldbie
Rank
Oldbie

Rather than write 1 byte at a time you can write 4 bytes at a time, since you have a 32bit machine.

unsigned int *pPtrTo32 = (unsigned int*)PtrToByte;
for all my pixels
pPtrTo32[x] = value

Also optimizing anything on DosBox is not really ideal as DosBox is nothing close to a real HW from performance point of view. You can simply try to increase the DosBOX speed in terms of kicks until you reach a desired FPS. Saying that you want to reach at least 30 FPS but not saying what is your target machine does not get you far. Is that a 386/486/Pentium?

YouTube channel: https://www.youtube.com/channel/UC7HbC_nq8t1S9l7qGYL0mTA
Collection: http://www.digiloguemuseum.com/index.html
Emulator: https://sites.google.com/site/capex86/
Raytracer: https://sites.google.com/site/opaqueraytracer/

Reply 4 of 15, by videogamer555

User metadata
Rank Member
Rank
Member

Since nobody has yet answered my question about memcpy, let me repeat it.
Does memcpy work with far pointers? Is it supposed to? Is the fact that it's not working an indication that DOSbox just has a glitch?

Reply 5 of 15, by badmojo

User metadata
Rank l33t
Rank
l33t
videogamer555 wrote:

Since nobody has yet answered my question about memcpy, let me repeat it.
Does memcpy work with far pointers? Is it supposed to? Is the fact that it's not working an indication that DOSbox just has a glitch?

I'm pretty sure this isn't a C programming forum, maybe that's why people aren't replying to your posts quickly enough for your liking? You're doing well to get any responses at all, says I.

Life? Don't talk to me about life.

Reply 6 of 15, by reenigne

User metadata
Rank Oldbie
Rank
Oldbie
videogamer555 wrote:

Since nobody has yet answered my question about memcpy, let me repeat it.
Does memcpy work with far pointers? Is it supposed to? Is the fact that it's not working an indication that DOSbox just has a glitch?

In small-data memory models (tiny, small and medium) memcpy (and other standard library functions taking pointers) take near pointers so you can't use them with far pointers. Some compilers have a farmemcpy or equivalent (Watcom has _fmemcpy) but it doesn't look like Turbo C 2.01 does. What it does have is movedata which takes segments and offsets instead of far pointers - it looks like that should do what you want.

If you really care about speed, though, at some point you'll have to drop down to assembler code for the bits that need to be fast. For native development, I used to use the A86 assembler in conjunction with Turbo C 2.01 and it worked really well. Later Borland compilers had their own assemblers.

Reply 7 of 15, by Azarien

User metadata
Rank Oldbie
Rank
Oldbie

unsigned char far *FarPointerToByte = MK_FP(segment,offset);
FarPointerToByte=FarPointerToByte+1;

What about incrementing the offset, and doing MK_FP again? This may (or may not) have performance penalty, though. But try it.

Reply 8 of 15, by videogamer555

User metadata
Rank Member
Rank
Member
reenigne wrote:
videogamer555 wrote:

Since nobody has yet answered my question about memcpy, let me repeat it.
Does memcpy work with far pointers? Is it supposed to? Is the fact that it's not working an indication that DOSbox just has a glitch?

In small-data memory models (tiny, small and medium) memcpy (and other standard library functions taking pointers) take near pointers so you can't use them with far pointers. Some compilers have a farmemcpy or equivalent (Watcom has _fmemcpy) but it doesn't look like Turbo C 2.01 does. What it does have is movedata which takes segments and offsets instead of far pointers - it looks like that should do what you want.

If you really care about speed, though, at some point you'll have to drop down to assembler code for the bits that need to be fast. For native development, I used to use the A86 assembler in conjunction with Turbo C 2.01 and it worked really well. Later Borland compilers had their own assemblers.

But how to you write half of a program in one language (C) and the other half of your program in another language (assembly), and then combine both halves of the program when compiling?

As for using memcpy, can't I just switch the C compiler to use "large" memory mode?

Reply 9 of 15, by ripa

User metadata
Rank Oldbie
Rank
Oldbie
videogamer555 wrote:

But how to you write half of a program in one language (C) and the other half of your program in another language (assembly), and then combine both halves of the program when compiling?

Inline assembly or link your assembled code with your C code.

Reply 10 of 15, by reenigne

User metadata
Rank Oldbie
Rank
Oldbie
videogamer555 wrote:

But how to you write half of a program in one language (C) and the other half of your program in another language (assembly), and then combine both halves of the program when compiling?

As ripa said, use the linker. The Turbo C 2.01 IDE doesn't have support for using external build tools directly, but what you can do is assemble your .asm file to a .obj file and then add the .obj file to your project file. If you prefer working from the command line, you can use a batch file or a makefile that calls tcc.exe (to compile the C files to obj files), a86.com (to assemble the .asm files to obj files) and tlink.exe (to link the obj and lib files together to make an exe file). Either way you can still use the IDE to debug the C parts of the program, if debug information is enabled.

videogamer555 wrote:

As for using memcpy, can't I just switch the C compiler to use "large" memory mode?

You can, but then all your pointers will be "far" pointers and your program will be much slower. Unless you have more than 64kB of code and more than 64kB of data in your program you should avoid this (and even if you do, it's not a step to take lightly). None of my Turbo C programs were large enough to need 64kB of code, and even though I frequently had more than 64kB of data I always used farmalloc, farfree, movedata, FP_SEG and FP_OFF to handle the big arrays so that all the small stuff (which did fit in 64kB) would use "near" pointers.

Reply 11 of 15, by Zup

User metadata
Rank Oldbie
Rank
Oldbie
reenigne wrote:
videogamer555 wrote:

But how to you write half of a program in one language (C) and the other half of your program in another language (assembly), and then combine both halves of the program when compiling?

As ripa said, use the linker. The Turbo C 2.01 IDE doesn't have support for using external build tools directly, but what you can do is assemble your .asm file to a .obj file and then add the .obj file to your project file. If you prefer working from the command line, you can use a batch file or a makefile that calls tcc.exe (to compile the C files to obj files), a86.com (to assemble the .asm files to obj files) and tlink.exe (to link the obj and lib files together to make an exe file). Either way you can still use the IDE to debug the C parts of the program, if debug information is enabled.

I used once Turbo Assembler to make a SVGA library for Turbo Pascal and Turbo C. Maybe you'd want to see that code (look into the calls and routine naming, the code is not a very good example).

I have traveled across the universe and through the years to find Her.
Sometimes going all the way is just a start...

I'm selling some stuff!

Reply 12 of 15, by K1n9_Duk3

User metadata
Rank Member
Rank
Member
videogamer555 wrote:
I thought I could do this: […]
Show full quote

I thought I could do this:

unsigned char far *FarPointerToByte = MK_FP(segment,offset);
FarPointerToByte=FarPointerToByte+1;

But doing this gives me a "Non-portable pointer assignment" warning. It doesn't rise to the level of an error, so compiling completes, but when I run the compiled EXE file, I end up having the program lock up. Remove the line FarPointerToByte=FarPointerToByte+1; and the program completes and returns to DOS, just like it should.

That code should work. I tested this with Borland C++ 3.1 and it compiles perfectly fine without any warnings if you don't forget to include DOS.H, which contains the declaration of the MK_FP macro. If I don't include DOS.H, I get the "Non-portable pointer assignment" warning (at the line with the MK_FP) and I also get another warning that says "Call to funtion 'MK_FP' with no prototype" and a linker error saying "Undefined symbol _MK_FP".

Key Info:

  • Increasing a far pointer works just like increasing any other pointer. Using "pointer++;" or "pointer=pointer+1;" will give you the same result (I think the former might be more efficient, depending on your compiler optimization settings).
  • Increasing (or decreasing) a far pointer will only change the offset part. You need to use huge pointers if you want the compiler to adjust the segment as well.
  • You're usually better off using memcpy or memset instead of incrementing pointers. Borland C++ 3.1 has "far" versions of these, called _fmemcpy and _fmemset, but I don't know if these are also present in Turbo C.
  • Writing your own assembly code will probably generate the fastest code. But if you can't figure out how to perform the task in C, you probably won't be able to do that in assembly either. First, write code that actually gets the job done. You can worry about performance and possible optimizations later.

Reply 13 of 15, by videogamer555

User metadata
Rank Member
Rank
Member
K1n9_Duk3 wrote:
That code should work. I tested this with Borland C++ 3.1 and it compiles perfectly fine without any warnings if you don't forge […]
Show full quote
videogamer555 wrote:
I thought I could do this: […]
Show full quote

I thought I could do this:

unsigned char far *FarPointerToByte = MK_FP(segment,offset);
FarPointerToByte=FarPointerToByte+1;

But doing this gives me a "Non-portable pointer assignment" warning. It doesn't rise to the level of an error, so compiling completes, but when I run the compiled EXE file, I end up having the program lock up. Remove the line FarPointerToByte=FarPointerToByte+1; and the program completes and returns to DOS, just like it should.

That code should work. I tested this with Borland C++ 3.1 and it compiles perfectly fine without any warnings if you don't forget to include DOS.H, which contains the declaration of the MK_FP macro. If I don't include DOS.H, I get the "Non-portable pointer assignment" warning (at the line with the MK_FP) and I also get another warning that says "Call to funtion 'MK_FP' with no prototype" and a linker error saying "Undefined symbol _MK_FP".

Key Info:

  • Increasing a far pointer works just like increasing any other pointer. Using "pointer++;" or "pointer=pointer+1;" will give you the same result (I think the former might be more efficient, depending on your compiler optimization settings).
  • Increasing (or decreasing) a far pointer will only change the offset part. You need to use huge pointers if you want the compiler to adjust the segment as well.
  • You're usually better off using memcpy or memset instead of incrementing pointers. Borland C++ 3.1 has "far" versions of these, called _fmemcpy and _fmemset, but I don't know if these are also present in Turbo C.
  • Writing your own assembly code will probably generate the fastest code. But if you can't figure out how to perform the task in C, you probably won't be able to do that in assembly either. First, write code that actually gets the job done. You can worry about performance and possible optimizations later.

For pointer++ to behave the same as pointer=pointer+1, when the pointer type is "far pointer", do I need the compiler to be in "large memory model" mode? I did not explicitly set it to "large memory model" mode, and pointer++ does not behave the same as pointer=pointer+1. Ponter++ does not produce any warnings and when the compiled program is run it works, while pointer=pointer+1 triggers a warning when compiling about it being "non portable" code and even worse it locks up the compiled program when it's actually run.

Reply 14 of 15, by reenigne

User metadata
Rank Oldbie
Rank
Oldbie
K1n9_Duk3 wrote:

That code should work. I tested this with Borland C++ 3.1 and it compiles perfectly fine without any warnings if you don't forget to include DOS.H, which contains the declaration of the MK_FP macro. If I don't include DOS.H, I get the "Non-portable pointer assignment" warning (at the line with the MK_FP) and I also get another warning that says "Call to funtion 'MK_FP' with no prototype" and a linker error saying "Undefined symbol _MK_FP".

I get the same result with Turbo C 2.01, which is what videogamer555 said they're using. So perhaps they're using a buggy version? The one I have has a file date of 11/05/1989 10:01 and tc.exe is 290525 bytes long. Or perhaps the bug goes away when the testcase is simplified like this. Or perhaps the bug is in videogamer555's code (e.g. a typo like "pointer = ponter + 1", where there is another variable called ponter, or memory is getting stomped elsewhere causing unpredictable behaviour).

videogamer555 wrote:

For pointer++ to behave the same as pointer=pointer+1, when the pointer type is "far pointer", do I need the compiler to be in "large memory model" mode? I did not explicitly set it to "large memory model" mode, and pointer++ does not behave the same as pointer=pointer+1. Ponter++ does not produce any warnings and when the compiled program is run it works, while pointer=pointer+1 triggers a warning when compiling about it being "non portable" code and even worse it locks up the compiled program when it's actually run.

They both work for me in the small model.

Reply 15 of 15, by K1n9_Duk3

User metadata
Rank Member
Rank
Member
videogamer555 wrote:

pointer++ does not behave the same as pointer=pointer+1. Ponter++ does not produce any warnings and when the compiled program is run it works, while pointer=pointer+1 triggers a warning when compiling about it being "non portable" code and even worse it locks up the compiled program when it's actually run.

"Non-portable pointer conversion" usually indicates that your code is actually doing one of the following:

  • converting a number into a pointer
  • converting a pointer to a signed variable into a pointer to an unsigned variable
  • converting a pointer into a pointer of a different type (like byte<->int<->long<->float)

Are you absolutely sure that your code says "pointer = pointer+1;" and not something like "pointer = *pointer+1;"? Because that's the only reason I could imagine for the warnings and errors you are getting.

If you want to get people to help you, you should take the actual code you're trying to compile and copy & paste it in here. Otherwise you're just wasting our time (and yours as well).

Edit:
For future reference:

#include <DOS.H>
int main(void)
{
unsigned char far *pointer = MK_FP(0xA000, 0);
pointer = pointer+1;
pointer++;
return *pointer;
}

will produce something similar to the following assembly code

asm {
mov word ptr [pointer+2], 0A000h
mov word ptr [pointer], 0

mov ax, word ptr [pointer+2]
mov dx, word ptr [pointer]
inc dx
mov word ptr [pointer+2], ax
mov word ptr [pointer], dx

inc word ptr [pointer]

les bx, dword ptr [pointer]
mov al, es:[bx]
mov ah, 0
ret
}

This illustrates that "pointer++;" will in fact generate faster code than "pointer = pointer+1;", but is has the same effect, which is that the offset part (and only the offset part) of the far pointer is increased.