?
Solved

Visual C++ and 16bit DOS (That's all the points I have left)

Posted on 2003-03-17
9
Medium Priority
?
408 Views
Last Modified: 2013-12-14
I am trying to write a 16bit DOS app from Visual C++ 6 is this even possible?  If so how? I am getting errors right now about how the program isn't able to run in DOS outside of the emulated DOS Enviroment in Windows.  If not, what can I use to compile C++ code to a 16 bit console app, thank you.
0
Comment
Question by:Narcisius
[X]
Welcome to Experts Exchange

Add your voice to the tech community where 5M+ people just like you are talking about what matters.

  • Help others & share knowledge
  • Earn cash & points
  • Learn & ask questions
9 Comments
 

Author Comment

by:Narcisius
ID: 8150511
I know this isn't many points but it is all I have left... please someone answer me..
0
 

Expert Comment

by:nistorm
ID: 8150537
If you want to make a 16bit DOS application try compiling with Borland C++ 3.1
0
 

Expert Comment

by:johanhz
ID: 8150646
You can also use Visual C++ 1.52C
I think it must be somewhere in the MSDN package ...

See also the followin link :

http://www.experts-exchange.com/Programming/Q_10858901.html
0
What does it mean to be "Always On"?

Is your cloud always on? With an Always On cloud you won't have to worry about downtime for maintenance or software application code updates, ensuring that your bottom line isn't affected.

 
LVL 12

Expert Comment

by:Salte
ID: 8150719
No, it is not possible to make a 16 bit DOS application using VC++ 6.0.

Use Borland C++ 3.1 or some similar compiler for that purpose.

Borland C++ 3.1 is free and available for download so as nistorm suggest you should try to use that compiler.

Note that Borland C++ 3.1 is NOT standard C++. It is from the days before the standard was made. No templates, not even sure if it has exceptions (doubt it - at least it is not implemented the same way modern exception is implemented) and it certainly have no STL. It has the old #include <iostream.h> though.

Some other old stuff is there but I do believe they have overload of new() and delete() so you don't do the ancient thing of assigning this pointer in constructor. So at least on that point they are in line with modern C++ compilers.

I think they even have overloads on return type for new:

void __near * operator new(size_t sz);

void __far * operator new(size_t sz);

are two separate overloads. It is the only function/operator that overloads on return type and as such is an exception to the rule that you don't overload on return type.

void * operator new(size_t sz);

is not a separate overload, it is one of the two above depending on your MODEL. The MODEL is important and determine the size of your pointers and how the segment registers are used.

Tiny model - everything in one segment, all pointers are near pointers and all segment registers have the same value. You can use far pointers but the C library only support near pointers.

Small model - one data segment and one separate code segment. All pointers are near pointers but CS and DS have different value. SS == ES == DS.

Medium model - one data segment but several code segments. Data pointers are near pointers but function pointers are far pointers. DS SS and ES has the same value but CS is set to a separate value and will from time to time change value, all - or most - function calls are far calls that causes the CS to change value.

Compact model - several data segments but only one code segment. Data pointers are far pointers and function pointers are near pointers. DS has one value, SS has probably another value and ES often change value. DS also some times change value. All function calls are near calls but data loads are typically done using far pointers and segment registers.

Large model - several data segments and several code segments. All pointers are far pointers and you frequently switch segment registers. All function calls are far calls. All data loads are using far pointers and data segment registers. Any data element must fit inside one 64K bytes segment though.

Huge model - Just like large model but in addition one single array or other data structure can span several segments. So you can make an array like:

int arr[100000];

which would be too big for a large model.

The huge heap (hmalloc) can also allocate data elements bigger than a segment.

The hmalloc function is also available in other models but beware that only in huge model will the systme do proper pointer arithmetic when calculating position.

The difference in pointer arithmetic is as follows:

If the pointer is near pointer it is only a 16 bit value and only that 16 bit value is modified.

If the pointer is far pointer then the pointer is 32 bit and is made up of a 16 bit segment and a 16 bit near pointer. You can extract the near pointer by simply casting the pointer to a near pointer. The segment can also be extract using the 'data type' _segment. __segment is a 16 bit value that can be stored in a segment register or used together with a near pointer to make a far pointer.

In all models where far pointers are used except for huge, the pointer arithmetic only affect the near pointer part of the far pointer. The segment is not modified. So if p is s:0xffff where s is some segment value and the near pointer is 0xffff then ++p will produce a value of s:0x0000 which really isn't the next position in memory.

In huge model the segment is taken into account and the next value is (s + 0x1000):0x0000.

This also implies that pointer arithmetic is slower in huge model compared to the other models.

All in all, the 32 bit code is much easier than these silly models. In 32 bit mode you essentially run Tiny model with CS, DS, ES and SS all pointing to the same segment but this one segment is 4Gb and not 64Kb.

Also, modern CPU's (386 and later) have two additional segment registers which you can use named FS and GS. The runtime system for MS-DOS doesn't do anything with them though but you can use them whenever you run out of segment registers to use. So, if ES isn't enough for you, specify FS and GS. Just remember to load them first :-)

Hope this is of help.

Alf
0
 

Author Comment

by:Narcisius
ID: 8153422
Would it be better to use Pascal to write these programs then? I can program in that as well.  If so, I tried using Code Warrior to compile in Pascal and I had the same problem with it not working in the old MSDOS enviroment?  I am trying to write a bunch of recovery CD's for work and for friends so I don't have to drop everything I am doing when they have problems to fix thier computer.  I am trying to write some CD's that can diagnose and fix small problems.
0
 
LVL 12

Accepted Solution

by:
Salte earned 120 total points
ID: 8153675
old Borland C++ 3.1 works for old MS-DOS environment.

If you happen to run on a modern pentium you can also use inline assembly and write '32-bit' code if you need to. The compiler should understand 32 bit and will insert prefixes so that you can address 32 bit addresses. This is handy incase you in assembly need access to 32 bit registers etc.

It is hard to combine with C++ code though since C++ will assume 16 bit addresses always, but if the C++ code just handle it as an opaque data type the value it hold in a class can actually be a 32 bit pointer and accessed as such.  Making tricks with segment registers can even make such a pointer class work well with C++ code:

class ptr32 {
private:
   long ptr_; // 32 bit pointer.....

public:
   operator void _far * ();
};

ptr32::operator void _far * ()
{
   union {
      struct {
         short off;
         short seg;
      } s;
      void _far * fp;
   };
     

   // convert a 32 bit linear pointer into
   // segment:pointer value.
   // The address must be in the lower megabyte for
   // this to work
   if (ptr_ >= 0x100000)
      panic("pointer value too large!");
   seg = (short)((ptr_ >> 4) & 0xf000);
   short nptr = (short)ptr_;
   // I remember borland had a method to combine a
   // segment and near pointer into a far pointer
   // but can't remember how it is done...
   return fp;
}

Using such a pointer you can at least address the lower megabyte as one linear address, you can also address beyond that but only in inline assembly I am afraid:

int read_short(long addr)
{
   int ret;
   __asm  {
      mov eax,addr
      mov fs,0
      mov ebx,fs:[eax]
      mov ret,bx // actually only needed the lower 16 bits.
   }
   return ret;
}

long read_long(long addr)
{
   long ret;
   _asm {
      mov eax,addr
      mov fs,0
      mov ebx,fs:[eax]
      mov ret,ebx
    }
    return ret
}

size_t read_buf(char _far * buf, size_t n, long addr)
{
   _asm {
      mov cx,n
      jcxz  done
      mov fs,0
      les edi,buf
      // if buf is near pointer use:
      //    push ds
      //    pop es
      //    mov edi,buf instead.
      // first copy until addr is a multiple of 4.
      mov esi,addr
      test esi,3
      jz  loop1
      lodsb al,fs:[esi]
      stosb es:[di],al
      loopz done // decrement cx and jump if 0.
      test esi,3
      jz  loop1
      lodsb al,fs:[esi]
      stosb es:[di],al
      loopz done
      test esi,3
      jz   loop1
      lodsb al,fs:[esi]
      stosb es:[di],al
      loopz done

 loop1: // as long as we have at least 4 bytes to copy...
      mov bx,cx // save the two lowest bits of count.
      shr cx,2 // shift two bits down. cx is now num dword
      jcxz loop1done
loop1c:
      lodsd eax,fs:[esi]
      stosw es:[di],ax
      shr eax,16
      stosw es:[di],ax
      loopz loop1c
loop1done:
      // copy the last few bytes
      mov cx,bx
      and cx,3
      jcxz done
      lodsb al,fs:[esi]
      stosb es:[di],al
      loopz done
      lodsb al,fs:[esi]
      stosb es:[di],al
      loopz done
      lodsb al,fs:[esi]
      stosb es:[di],al
done:
    }
    return n;
}

Similar functions to copy the other way can also be made and then you have your own functions to read/write any 32 bit address if you want.

Note, the thing with the fs register there is that ES and DS are probably already set to indicate the data segment you are using and so you need to use some other segment register which is set to 0 so that you access physical memory address x using the value x in register.

Note that 32 bit addresses do not use the page tables while in real mode so if you load ESI with 0x123456 and fs with 0 and do a:

   lodsb al,fs:[esi]

you read a byte at address 0x123456 in your RAM.

Setting segment registers to any non-zero value while reading 32 bit memory addresses is only confusing. I am frankly not sure what a value of say FS = 0x1000 would mean if you do a mov eax,FS:[EBX] if EBX has the value 0x123456. Would it do the same address arithmetic and make address become 0x133456 or what?

Safest is probably to set segment registers to 0 I guess.

Above all that will not work if you run in protected mode, using a segment register that has the value 0 will cause an exception (not C++ exception but a pentium hardware exception). Protected mode allows you to load a segment register with 0 but you can't use it to compute an address while it is 0.

Alf
0
 

Author Comment

by:Narcisius
ID: 8153809
Ok, will someone respond to my question about Pascal then?
0
 
LVL 12

Expert Comment

by:Salte
ID: 8153934
If you feel comfortable with pascal you can use it, but Borland C++ 3.1 is just as well. It all depends on what you prefer. They are identical as far as feature list goes.

Alf
0
 

Author Comment

by:Narcisius
ID: 8154382
Ok Salte I will give you the points even though my problem isn't really solved but thank you for the effort.
0

Featured Post

[Webinar] Lessons on Recovering from Petya

Skyport is working hard to help customers recover from recent attacks, like the Petya worm. This work has brought to light some important lessons. New malware attacks like this can take down your entire environment. Learn from others mistakes on how to prevent Petya like worms.

Question has a verified solution.

If you are experiencing a similar issue, please ask a related question

  Included as part of the C++ Standard Template Library (STL) is a collection of generic containers. Each of these containers serves a different purpose and has different pros and cons. It is often difficult to decide which container to use and …
Container Orchestration platforms empower organizations to scale their apps at an exceptional rate. This is the reason numerous innovation-driven companies are moving apps to an appropriated datacenter wide platform that empowers them to scale at a …
The viewer will learn how to use and create keystrokes in Netbeans IDE 8.0 for Windows.
The goal of the tutorial is to teach the user how to use functions in C++. The video will cover how to define functions, how to call functions and how to create functions prototypes. Microsoft Visual C++ 2010 Express will be used as a text editor an…

762 members asked questions and received personalized solutions in the past 7 days.

Join the community of 500,000 technology professionals and ask your questions.

Join & Ask a Question