1

I have a problem with memset and a big for loop in Turbo C.

I was programming a small Graphics library based on Mode 13h with Turbo C++ 3.00 on MS-DOS 6.22 on Virtual Box, when the code started crashing.

I know that Turbo C is really outdated, but I wanted to try the old-style coding on DOS like systems.

Here's my code:

#include <stdlib.h>
#include <conio.h>
#include <stdio.h>
#include <string.h>
#include <dos.h>

int main();

int main(){
  int *buf;
  unsigned int i;

  _AH = 0x0;
  _AL = 0x13;
  geninterrupt(0x10);

  buf = malloc(sizeof(int)*64000);
  memset(buf,15,64000); //can't figure out why memset does not work properly...

  for (i=0;i< (unsigned int)64000;i++){
    buf[i] = 15; //trying to forcely set each byte to 0xf...
    pokeb(0xA000,i,buf[i]); //outputting to VGA

  }
  getch();
  free(buf);
  return(0);

}


What I was trying to do was basically filling the screen with the white color.

Once I started coding I noticed that memset was not setting ALL of the bytes of my heap array to 0xf.

So I decided to set them manually by coding the for loop.

I ran it and it worked like butter. As soon as I poked the byte to the screen, the program crashed, and I couldn't figure out why...

Any help?

PS: I'm pretty new to C.

SlickSpore
  • 31
  • 4
  • 2
    When Turbo C was in common use, software developers were working on computers that allowed direct memory access to the video buffers. I doubt very much that is the case today. The video memory is abstracted from the developer with all sorts of software layers now. – Robert Harvey Apr 26 '23 at 15:43
  • 1
    @RobertHarvey the OP is actually using DOS, probably on an emulator, thus it is possible to write directly into the video buffer. – Jabberwocky Apr 26 '23 at 15:47
  • 1
    Where in the OP does it say that they're using an emulator? – Robert Harvey Apr 26 '23 at 15:51
  • 3
    That `memset` should be `memset(buf,15,sizeof(int)*64000)` like the `malloc` call. – Jason Apr 26 '23 at 15:51
  • 2
    @RobertHarvey OP doesn't say they use an an emulator, that's why I wrote "**probably** on an emulator". Anyway, emulator or not, this code is obviously written for bare bones MSDOS. And OP is using TurboC which is only running on DOS. – Jabberwocky Apr 26 '23 at 15:52
  • It's possible that Turbo C might run in a DOS box under windows. Many things (like this) would not work, however. – Robert Harvey Apr 26 '23 at 16:22
  • 2
    @RobertHarvey: The transparent DOS emulator built-in to some versions of Windows (ntdvm) does emulate a VGA framebuffer, doesn't it? Otherwise it wouldn't be usable for a lot of programs. Having the framebuffer at a known linear address is part of what 16-bit DOS programs can normally assume. Hmm, or maybe not: https://www.vogons.org/viewtopic.php?p=739398#p739398 says NTVDM video card emulation sucks, and maybe you can't write directly to video RAM in at least some versions of it. Anyway, if your program needs that, you might need to run in DOSBox, which can even handle most games. – Peter Cordes Apr 26 '23 at 16:39

1 Answers1

7

In MS-DOS memory is organised in segments of no more than 64K.

Your buffer of 64,000 integers is larger than that.

If you used unsigned char (single bytes) instead of int or unsigned int (which I seem to remember are 16 bits in DOS/Turbo C++) then it would fit into a single "segment" and should work fine.

... And as you're storing bytes to be poked into video memory, you don't need the extra bit-width that integers give you.

#include <stdlib.h>
#include <conio.h>
#include <stdio.h>
#include <string.h>
#include <dos.h>

int main() {
  unsigned char *buf;
  unsigned int i;

  _AH = 0x0;
  _AL = 0x13;
  geninterrupt(0x10);

  buf = malloc(64000);
  memset(buf, 15, 64000);

  for (i=0; i < (unsigned int)64000; i++) {
    pokeb(0xA000, i, buf[i]); //outputting to VGA
  }
  getch();
  free(buf);
  return(0);
}

As @Jabberwocky has mentioned, if you don't plan to do anything else but poke these values directly to video RAM, you can even ignore the buffer entirely:

int main() {
  unsigned int i;

  _AH = 0x0;
  _AL = 0x13;
  geninterrupt(0x10);

  for (i=0; i < (unsigned int)64000; i++) {
    pokeb(0xA000, i, 15); //outputting to VGA
  }
  getch();
  return(0);
}
Andy Preston
  • 779
  • 4
  • 9
  • 23
  • 3
    Actually replacing `buf[i]` with `0xf`and removing all malloc/memset/free should do the same thing. – Jabberwocky Apr 26 '23 at 16:06
  • 1
    you can change to big model where all data accesses are done via far pointers and it'll overcome the 64K issue – phuclv Apr 26 '23 at 16:06
  • 2
    `int` and `unsigned` are definitely 16 bit on the Turbo C environment. – Jabberwocky Apr 26 '23 at 16:07
  • @Jabberwocky (re: don't use malloc at all) I was assuming that OP was going to do something else with his buffer once this issue is resolved... but, otherwise - yes, you're quite right. – Andy Preston Apr 26 '23 at 16:08
  • @phuclv - oh yeah "far pointers" - now that takes me back to my youth - I'd forgotten they even exist... But do'nt you need to compile 16-bit specific code or something like that to use them? – Andy Preston Apr 26 '23 at 16:09
  • 2
    My goal was to make a frame buffer array to store the entire frame in it, making the actual render faster, hopefully... – SlickSpore Apr 26 '23 at 16:14
  • 2
    @SlickSpore: If you want to render into a (write-back cacheable) buffer and then copy to video RAM, you're going to want to copy efficiently with something like `memcpy`, not one byte at a time with potential function-call overhead!! Unless `pokeb` inlines and can hoist the setup of a segment register (like ES) out of the loop. If not, calling it 64k times is going to be way slower than only updating the changed pixels in video RAM. Especially if video ram is uncacheable (without write-combining) so your 1-byte transfers all go separately, not even a 32-bit dword at a time, let along 64B. – Peter Cordes Apr 26 '23 at 16:35
  • 1
    @PeterCordes: If you include `dos.h` which the OP does, and if you don't undef the macro `pokeb` (which they don't) then it resolves down to an inline statement that makes use of MK_FP macro to create a far pointer and sets the value through that. The macro looks something like `void inline _Cdecl pokeb(unsigned __segment, unsigned __offset, char __value ) {( *( (char far* )MK_FP( __segment, __offset )) = __value ); }` – Michael Petch Apr 26 '23 at 21:31
  • @MichaelPetch: Ok, so at least there won't be a function call. Any idea if Turbo C++ would actually manage to hoist the setup of a segment register out of the loop for all those `char far*` derefs with the same `__segment__`? And/or optimize those char loads / stores into word or dword accesses to both `buf` and video RAM, or a `rep movsd`? The fact that `pokeb` doesn't use `volatile` is surprising. It's more likely to allow optimizations like write coalescing. But if it actually works reliably without `volatile` for uses like MMIO, that probably meansthe compiler can't optimize much! – Peter Cordes Apr 27 '23 at 03:34
  • @AndyPreston you don't need to do anything, Turbo C already has built-in support for multiple memory models depending on how much data and code you need: [Memory models for assembly libraries for Turbo C](https://retrocomputing.stackexchange.com/q/12838/1981) – phuclv Apr 28 '23 at 09:16