1

I´m working on a program to create a basic image editor (like paint) on DOSBOX with C using BORLAND 3.1. Right now I´m trying to implement a simple undo button. For this I created a double array to store 10 times the drawing area (which is 288*180). However, when adding the line of the array intialization, I cannot allocate memory for a double buffer I use in other functionalities of the program.

Is there any way I can get more memory in DOSBOX or another implementation that doesn´t give me this problem?

I compile my program like this:

bcc -mh paint.c

This is my code:

byte huge undo[51840][10];  // This is the array that is giving me problems
void welcome_screen(){
    BITMAP fondo_inicio,normal_ptr_image,boton_inicio,boton_salir;
    MOUSE  mouse_welcome;
    unsigned long int  h;
    int a[2];
    byte *double_buffer;
    sword new_x, new_y;
    word redraw,press,release;
    sword dx,dy=0;
    MOUSEBITMAP *normal_pointer=NULL;
    MOUSEBITMAP *mouse_new=NULL;
    word last_time;

    int i,done = 0;

    filled=-1;

    /* allocate memory for double buffer and background image SCREEN_SIZE = 320*200 */
    if ((double_buffer = (byte *) malloc(SCREEN_SIZE)) == NULL)
    {
        printf("Not enough memory for double buffer.\n"); // ---> This is the error I get when adding the above line
        exit(1);
    }
Pablo Estrada
  • 3,182
  • 4
  • 30
  • 74
  • 1
    Note: they say [you shouldn't cast the result of `malloc()` in C](http://stackoverflow.com/questions/605845/do-i-cast-the-result-of-malloc). – MikeCAT Dec 27 '15 at 01:48
  • 4
    I would strongly suggest using a compiler that's a bit less ancient. – David Schwartz Dec 27 '15 at 01:56
  • 1
    Will I be able to run the program oin DOSBOX using a more recent compiler? What compiler can you suggest that is DOSBOX compatible? – Pablo Estrada Dec 27 '15 at 01:59
  • 1
    You’re using Borland Turbo C, I take it? Use a DOS extender with XMS support and set DOSBOX to allocate sufficient extended memory. In 1995, as games were moving from DOS to Windows, 8MB was standard, but 4MB was still common. 16MB would have been more than enough to run any program written for DOS. – Davislor Dec 27 '15 at 02:01
  • 2
    10 * 51840 + 320 * 200 = 582,400 bytes of memory for just those two allocations. That pretty much all of the conventional memory that's available to MS-DOS programs. So it not at all surprising you're running out of memory. – Ross Ridge Dec 27 '15 at 02:05
  • 1
    DJGPP is a port of GCC for MS-DOS, and is the most modern DOS compiler I’m aware of. You might also try OpenWatcom. – Davislor Dec 27 '15 at 02:07
  • I remember having used Watcom (when it was still not open software) and with the DOS/4G manager (the same that runs Doom) and a 80386 with XMS memory, I was able to run programs in protected mode, and in a linear memory space, so a malloc of more than 1MB and beyond was possible. – mcleod_ideafix Dec 27 '15 at 02:51

2 Answers2

4

If you want to use memory above 640K, what you want to do is compile using DPMI, with a DOS extender such as CWSDMPI, and allocate memory through it. DJGPP, the DOS port of GCC, is the most modern compiler available for DOS, and is still being developed. (Edit: According to Ross Ridge, DJGPP uses extended memory automatically with the standard library calls.) Another option, which is what most commercial software at the time used, is Watcom C with DOS4G/W. This is available today as OpenWatcom and the DOS/32 extender. You will also need to configure DOSBOX to make XMS available; 16MiB was a hefty amount back in 1995.

Davislor
  • 14,674
  • 2
  • 34
  • 49
2

Rather than keeping your undo buffer as an array of 10 full screen bitmaps, why not just preserve one screen and the 10 subsequent operations that followed on it? Then an undo is just restoring the screen buffer and then applying the 9 remembered operations that followed.

That is...

byte undo[288*180]; // one screen of memory representing what the screen looked like 10 operations ago

struct Operation undo_ops[10]; // the last 10 operations since the undo buffer was formed
int undo_ops_count; // number of elements in above array

Where "Operation" is something like this:

struct Operation
{
    int x1;
    int y1;
    int x2;
    int y2;
    int size;
    int extra;
    int color;
    enum OpCode code; // Circle, Rectangle, Line, Fill, whatever...
};

And then some pseudo-code for what an "Undo" actually does

void Undo()
{
    if (undo_ops_count > 0)
    {
        undo_ops_count--;  // forget the last operation

        // restore the screen
        memcpy(g_screen_buffer, undo, sizeof(undo));

        // apply the last operations before the last user operation
        for (int x = 0; x < undo_ops_count; x++)
        {
            ApplyOperation(g_screen_buffer, &undo_ops[x]); // re-draw what the user did on top of the restored screen buffer
        }
        RepaintScreen(g_screen_buffer);
    }
}

Each user action of drawing something new gets appended to the undo_ops array. If you run out of array space for undo_ops, you merely redraw a new screen buffer and drop the oldest operation. I'll leave that as an exercise for you. Seeing how this frees up a lot more memory, I'll bet you can remember a lot more than 10 operations without running out of memory.

selbie
  • 100,020
  • 15
  • 103
  • 173
  • Thank you very much! This is a much better choice because of the lack of memory I have – Pablo Estrada Dec 27 '15 at 02:18
  • I want to thank you again for your solution. I solved the issue with the djgpp compiler, but your proposal would sure have worked using less memory. I marked the other answer because it is what I used, but this is a great idea if there are still problems with memory. – Pablo Estrada Dec 27 '15 at 03:34