VOGONS


First post, by WhiteFalcon

User metadata
Rank Newbie
Rank
Newbie

Not sure if this is the right place, but it has to be connected with DosBox. After more than 10 years I have come back to try DOS C (with a bit of asm) programming, remembering more than I expected. But I got stuck not far from the beginning - I can init the clasic mode 13h and put some pixels, yet when I try to allocate a 64000-byte pointer for double buffering, it wont let me.
My IDE of choice is Borland Turbo C++ 3.0 in DosBox 0.74, the project is now set to Large (1MB code, 1MB data). I even found snippets of my ages old code and it worked without a problem, now in DoxBox there is not enough memory, not even if I compile it and run from the command line. The gist of the code is this:

unsigned char *vga_buff;

vga_buff = (unsigned char *) malloc(320 * 200);

Allocating 320 * 190 seems to work though. Also, its recommended to get a piece of memory starting at offset 0x0000, how can I achieve that?
At the moment I have no means to test the code on an old DOS machine, but I am sure it used to simply work and now in DosBox it wont.

Olivetti M4 P75, 32MB RAM, 4GB HDD, SoundBlaster AWE 64, Gravis Ultrasound MAX, Roland SCC-1, Roland MT-32, Roland CM-64.

Reply 1 of 12, by ripsaw8080

User metadata
Rank DOSBox Author
Rank
DOSBox Author
#include <alloc.h>

Try adding that to the top of your source file if you haven't already, as I think it's needed to get malloc() to use the chosen memory model.

Reply 2 of 12, by Scali

User metadata
Rank l33t
Rank
l33t

Note that in DOS, you are working with 16-bit segmented mode. This means that the memory model you choose affects how your malloc()-functions work.
Namely, if you use a tiny or small model, your malloc() will allocate 'near' memory, as in: memory in a single 64k data segment. In this case, malloc() will likely fail with a size of 64000 bytes, because there is not enough memory available.
In that case, you need a farmalloc() call (or _fmalloc() or whatever your compiler calls it).
If you want a memory block at offset 0, the easiest way is to use the DOS alloc functionality directly. It returns memory in paragraphs, giving you a base segment.
farmalloc() calls are generally wrappers around this functionality, and will return a normalized far-pointer, which usually boils down to a segment and an offset of 0.

As an aside, the most awesome way of double-buffering on VGA is using mode X or mode 0Dh (EGA 16-colour mode). This allows you to use multiple display pages in video memory, and performing a pageflip to switch buffers, rather than requiring an expensive copy operation.

My preferred way of using C in DOS is to take a small memory model, and manually managing any far memory I require.

http://scalibq.wordpress.com/just-keeping-it- … ro-programming/

Reply 3 of 12, by WhiteFalcon

User metadata
Rank Newbie
Rank
Newbie
ripsaw8080 wrote:
Try adding that to the top of your source file if you haven't already, as I think it's needed to get malloc() to use the chosen […]
Show full quote
#include <alloc.h>

Try adding that to the top of your source file if you haven't already, as I think it's needed to get malloc() to use the chosen memory model.

Thanks, I tried that when testing farmalloc, for malloc it doesnt seem necessary though.

Scali wrote:

Note that in DOS, you are working with 16-bit segmented mode. This means that the memory model you choose affects how your malloc()-functions work.
Namely, if you use a tiny or small model, your malloc() will allocate 'near' memory, as in: memory in a single 64k data segment. In this case, malloc() will likely fail with a size of 64000 bytes, because there is not enough memory available.
In that case, you need a farmalloc() call (or _fmalloc() or whatever your compiler calls it).

Thank you. I have always been unsure about the near/far memory so I have always used at least the large model that is supposed to make everything "far" when needed. I tried farmalloc explicitly too and it did not work either.

Scali wrote:

If you want a memory block at offset 0, the easiest way is to use the DOS alloc functionality directly. It returns memory in paragraphs, giving you a base segment.
farmalloc() calls are generally wrappers around this functionality, and will return a normalized far-pointer, which usually boils down to a segment and an offset of 0.

Did not relalize that, will check the DOS functions. It boggles me that it really used to work on real DOS though, have to try it when I get to my classic old PC in a few weeks.

Scali wrote:

As an aside, the most awesome way of double-buffering on VGA is using mode X or mode 0Dh (EGA 16-colour mode). This allows you to use multiple display pages in video memory, and performing a pageflip to switch buffers, rather than requiring an expensive copy operation.

My preferred way of using C in DOS is to take a small memory model, and manually managing any far memory I require.

Of course, I have read on the unchained mode a lot, but the simplicity of mode 13h is a big bonus. afaik, mode-x was used for games that needed fast fullscreen scrolling, in other cases it is not supposed to be faster (or not significantly) while being more difficult to manage. Have to check on it again.

Olivetti M4 P75, 32MB RAM, 4GB HDD, SoundBlaster AWE 64, Gravis Ultrasound MAX, Roland SCC-1, Roland MT-32, Roland CM-64.

Reply 4 of 12, by root42

User metadata
Rank Oldbie
Rank
Oldbie

Mode X and Y are already better as soon as you want to use double buffering. It’s really hard to imagine hiw slow the ISA bus actually is. Even without smooth scrolling, the page flipping feature usually makes those modes worthwile.

YouTube and Bonus
80486DX@33 MHz, 16 MiB RAM, Tseng ET4000 1 MiB, SnarkBarker & GUSar Lite, PC MIDI Card+X2+SC55+MT32, OSSC

Reply 5 of 12, by Scali

User metadata
Rank l33t
Rank
l33t
WhiteFalcon wrote:

Thank you. I have always been unsure about the near/far memory so I have always used at least the large model that is supposed to make everything "far" when needed. I tried farmalloc explicitly too and it did not work either.

Yea, making everything 'far' (or 'huge') is a bruteforce way of using the full 640k memory (you still won't be able to take advantage of XMS or EMS this way). However, the code it generates is quite inefficient, because the compiler has to generate a lot of extra code to switch segments all the time. Pointer arithmetic will also be less efficient.

WhiteFalcon wrote:

Of course, I have read on the unchained mode a lot, but the simplicity of mode 13h is a big bonus. afaik, mode-x was used for games that needed fast fullscreen scrolling, in other cases it is not supposed to be faster (or not significantly) while being more difficult to manage. Have to check on it again.

Yes, it is a bit complicated to use, because you cannot freely access all pixels on screen. You have to switch between the 4 planes. Depending on what kind of drawing operations you require, this may or may not be very complex or inefficient.
For example, Wolfenstein 3D renders everything in vertical columns. It can render these columns in any order. This means that you can easily render all columns for plane 0, then switch to plane 1, render all columns for plane 1, etc.
This makes it a very good fit.

http://scalibq.wordpress.com/just-keeping-it- … ro-programming/

Reply 6 of 12, by WhiteFalcon

User metadata
Rank Newbie
Rank
Newbie

I am going to run it on a Pentium 75MHz so it would be fine, but it would be nice if I was able to run it on a, lets say, a 486DX/40 or similar machine too.

Scali wrote:

Yea, making everything 'far' (or 'huge') is a bruteforce way of using the full 640k memory (you still won't be able to take advantage of XMS or EMS this way). However, the code it generates is quite inefficient, because the compiler has to generate a lot of extra code to switch segments all the time. Pointer arithmetic will also be less efficient.

Right, I remember having to write functions for XMS and calling the driver anytime I needed access to a particular bank, very slow.

Scali wrote:

Yes, it is a bit complicated to use, because you cannot freely access all pixels on screen. You have to switch between the 4 planes. Depending on what kind of drawing operations you require, this may or may not be very complex or inefficient.
For example, Wolfenstein 3D renders everything in vertical columns. It can render these columns in any order. This means that you can easily render all columns for plane 0, then switch to plane 1, render all columns for plane 1, etc.
This makes it a very good fit.

Interesting, would you believe I actually BOUGHT the source code for Wolfenstein like 20 years ago on a 5 1/4 floppy disk? 😀 From what I have read on mode-x, this really seems the way to go, draw from top-to bottom and then left-to right, unlike mode 13. The 320x240 resolution has its allures too so will give it a try again. But anyway, I have tried allocating memory using the DOS function and it still wont allocate 64000 bytes. Funnily enough, allocating 67200 bytes returns no error, which should not be possible if it crosses a segment boundary I assume.

Here is the function:

void *MEM_Alloc(unsigned int size)
{
int num_para;

num_para = (size / 16) + 1;

asm {
mov ah, 0x48
mov bx, [num_para]
int 0x21
}

if (_FLAGS & 0x0001) return(NULL);

return(MK_FP(_AX, 0x0000));
}

EDIT: Scali, I just found out you are the author of the nice tuts I read through some monts ago, on EGA and mode-x, thanks for those 😀

Olivetti M4 P75, 32MB RAM, 4GB HDD, SoundBlaster AWE 64, Gravis Ultrasound MAX, Roland SCC-1, Roland MT-32, Roland CM-64.

Reply 7 of 12, by TheGreatCodeholio

User metadata
Rank Oldbie
Rank
Oldbie

At least from my experience with the Open Watcom C compiler, malloc and _fmalloc() take a parameter that's only 16 bits wide on 16 bit targets. So the largest value possible is 65535. If you specify a value larger than that, the value will be truncated to 16 bits and you'll allocate (N % 65536) bytes instead of N bytes.

DOSBox-X project: more emulation better accuracy.
DOSLIB and DOSLIB2: Learn how to tinker and hack hardware and software from DOS.

Reply 8 of 12, by Scali

User metadata
Rank l33t
Rank
l33t
TheGreatCodeholio wrote:

At least from my experience with the Open Watcom C compiler, malloc and _fmalloc() take a parameter that's only 16 bits wide on 16 bit targets. So the largest value possible is 65535. If you specify a value larger than that, the value will be truncated to 16 bits and you'll allocate (N % 65536) bytes instead of N bytes.

That is correct. Watcom offers the 'halloc' function to allocate larger chunks:
https://github.com/open-watcom/open-watcom-v2 … /heap/c/haloc.c

The difference between malloc() and _fmalloc() in Watcom is that malloc does an allocation on the near heap, and _fmalloc() allocates a new segment. So _fmalloc() will usually succeed when you want a chunk of 65535 bytes or less.
Depending on your needs, an array of _fmalloc()-ed elements may be more convenient than a single huge array with halloc().

http://scalibq.wordpress.com/just-keeping-it- … ro-programming/

Reply 9 of 12, by WhiteFalcon

User metadata
Rank Newbie
Rank
Newbie

To ressurrect this thread with at least a partial solution 😀 Last time I managed to solve it, perhaps by some setting in the Borland C++ IDE, but then forgot it again 🙁 Now, toying again with the old PC and some C I have found out, that it maybe has to do with the version of the IDE. When I compile he code and run it, it fails to malloc a 64kB array. But when I exit the IDE and run the compiled .exe, it works fine! So maybe back in the days when it worked I used a newer (or older) version of BC IDE that did not take so much conventional memory.

As a side note, Inow started to learn how to use EMS (used only XMS before) and learned that it has a whole segment for its uses, as the 4 pages mapping area. So maybe I could use that for a virtual screen and use XMS. Mode X still eludes me with its coplexity, but I plan to harness it too one day 😀

Olivetti M4 P75, 32MB RAM, 4GB HDD, SoundBlaster AWE 64, Gravis Ultrasound MAX, Roland SCC-1, Roland MT-32, Roland CM-64.

Reply 10 of 12, by root42

User metadata
Rank Oldbie
Rank
Oldbie
WhiteFalcon wrote on 2020-05-26, 10:23:

As a side note, Inow started to learn how to use EMS (used only XMS before) and learned that it has a whole segment for its uses, as the 4 pages mapping area. So maybe I could use that for a virtual screen and use XMS. Mode X still eludes me with its coplexity, but I plan to harness it too one day 😀

Simplest solution: use Mode X/Y only as a framebuffer with two pages for quick page flipping and copy your screen using fast blitting into the backbuffer. I did a video about that earlier this year:

https://youtu.be/8e3ocjtWKac

YouTube and Bonus
80486DX@33 MHz, 16 MiB RAM, Tseng ET4000 1 MiB, SnarkBarker & GUSar Lite, PC MIDI Card+X2+SC55+MT32, OSSC

Reply 11 of 12, by WhiteFalcon

User metadata
Rank Newbie
Rank
Newbie

Will check that, thanks root42.

Olivetti M4 P75, 32MB RAM, 4GB HDD, SoundBlaster AWE 64, Gravis Ultrasound MAX, Roland SCC-1, Roland MT-32, Roland CM-64.

Reply 12 of 12, by mkarcher

User metadata
Rank Member
Rank
Member
WhiteFalcon wrote on 2020-05-26, 10:23:

To ressurrect this thread with at least a partial solution 😀 Last time I managed to solve it, perhaps by some setting in the Borland C++ IDE, but then forgot it again 🙁 Now, toying again with the old PC and some C I have found out, that it maybe has to do with the version of the IDE. When I compile he code and run it, it fails to malloc a 64kB array. But when I exit the IDE and run the compiled .exe, it works fine! So maybe back in the days when it worked I used a newer (or older) version of BC IDE that did not take so much conventional memory.

There is a setting "Program heap size" or something like that in the debugger options. You need to increase it if your program wants to allocate a bigger amount of memory.