Sign in Join
Wiki copytemporary

DOS

Deutsch · English

Note: This page has outdated information. Somebody is updating the information but you can help.

Overview

A DOS (8086) cross compiler is currently being developed in FPC trunk (the development version). It started as a hobby project meant to explore how to port FPC to a new platform.

The number of (FPC on) DOS users is probably quite low, especially since there is an FPC compiler for the more capable GO32V2 DOS extender available which runs on 80386+ processors.

Advantages over FPC for GO32V2

Advantages over Turbo Pascal

Limitations

The DOS platforms brings some limitations, like

Requirements

FPC 3.0.x

The compiler is a cross compiler that runs at least on Windows (both x86 and x64), Linux, and macOS. For compiling programs, it needs:

In theory it should be able to run on any platform, supported by FPC, where NASM and the Open Watcom tools are available. This includes DOS via the GO32V2 extender. However, because the Watcom tools for DOS are compiled with a different extender, there are some issues related to long file names and the passing of long command line arguments. This is resolved in FPC trunk, where there's an internal assembler and linker implemented in the compiler itself.

FPC trunk

FPC trunk contains an internal assembler and linker for DOS, so the Open Watcom tools are no longer required. NASM is still required for building an i8086-msdos snapshot, because of the msdos startup code in the rtl.

Releases

3.0.4

The crosscompiler packages for Windows, Linux - i386 and Linux - x86_64 are also available at the official Free Pascal website: http://freepascal.org/download.var

3.0.2

3.0.0

3.0.0rc2

3.0.0rc1

Snapshots

There are daily snapshots available here:

ftp://ftp.freepascal.org/pub/fpc/snapshot/trunk/i8086-msdos/

There's also a snapshot available here, but it's quite outdated, so it's better to simply use the above snapshot or to build your own:

http://www.bttr-software.de/forum/forum_entry.php?id=12985

Building a snapshot manually

text
svn checkout http://svn.freepascal.org/svn/fpc/trunk fpc
text
make clean all OS_TARGET=msdos CPU_TARGET=i8086 OPT="-CX -XXs" CROSSOPT=-WmSmall BINUTILSPREFIX= PP=/usr/bin/ppc386
text
make crossinstall OS_SOURCE=linux CPU_SOURCE=i386 OS_TARGET=msdos CPU_TARGET=i8086 PP=compiler/ppcross8086 CROSSOPT=-WmSmall \
    BINUTILSPREFIX= INSTALL_PREFIX=/home/blablabla/fpc-i8086/snapshot/small OPT="-CX -XXs"

Updating your fpc.cfg

Add the following to your fpc.cfg to enable smartlinking and disable the default binutils prefix (so that you don't have to rename nasm, wlink and wlib to msdos-nasm, msdos-wlink and msdos-wlib):

text
#ifdef cpui8086
-CX
-XX
-XP
#endif

To enable building for all the memory models, build snapshots for the tiny, small, medium, compact, large and huge memory model, and then find the lines in your fpc.cfg that specify the path to the RTL:

text
-Fu/usr/lib/fpc/$fpcversion/units/$fpctarget
-Fu/usr/lib/fpc/$fpcversion/units/$fpctarget/*
-Fu/usr/lib/fpc/$fpcversion/units/$fpctarget/rtl

And update them to something similar to:

text
#IFDEF CPUI8086
-Fu/home/blablabla/fpc-i8086/snapshot/$fpcmemorymodel/lib/fpc/$fpcversion/units/$fpctarget
-Fu/home/blablabla/fpc-i8086/snapshot/$fpcmemorymodel/lib/fpc/$fpcversion/units/$fpctarget/*
-Fu/home/blablabla/fpc-i8086/snapshot/$fpcmemorymodel/lib/fpc/$fpcversion/units/$fpctarget/rtl
#ELSE
-Fu/usr/lib/fpc/$fpcversion/units/$fpctarget
-Fu/usr/lib/fpc/$fpcversion/units/$fpctarget/*
-Fu/usr/lib/fpc/$fpcversion/units/$fpctarget/rtl
#ENDIF

This should enable you to build programs in any of the supported memory models with just a compiler switch:

text
ppcross8086 -WmTiny -Wtcom hello.pas
ppcross8086 -WmTiny -Wtexe hello.pas
ppcross8086 -WmSmall hello.pas
ppcross8086 -WmMedium hello.pas
ppcross8086 -WmCompact hello.pas
ppcross8086 -WmLarge hello.pas
ppcross8086 -WmHuge hello.pas

And in case you're wondering - it doesn't matter which ppcross8086 compiler binary you use (i.e. from the tiny, small, medium, compact, large or huge model snapshot). They are identical. Only the compiled units differ.

Status

The compiler has been enabled as a CPU/OS target in FPC.exe since revision 25792 (CPU: i8086, OS: msdos).

The RTL compiles. Most units from Go32v2 have been ported. Just like Go32v2, the RTL supports long file names when run under a Windows 95+/2000+ DOS box or under plain DOS with a long file names driver such as doslfn.exe. FPC demo programs such as fpctris and samegame work in all supported memory models.

Internal linker

The internal linker does not support all records and features of the OMF object file format. It was designed to support only the records produced by the NASM assembler and FPC's own internal object writer. You may encounter problems if you try to link object modules produced by other compilers and assemblers.

Floating point support

Floating point operations require an FPU. Software FPU emulation is not yet implemented and using floating point operations on a real machine without an FPU will lead to a hang.

Nil pointer assignment checking

The small and medium memory models offer rudimentary nil pointer assignment checking. The system RTL puts a pattern of 32 bytes equal to $01 at the beginning of the data segment (DS:0000). At the end of program execution, this pattern is checked and if turns out it has changed, the message:

text
Nil pointer assignment

is printed on the screen. This indicates that a nil pointer assignment has happened at some time during the program execution. Unfortunately, we can't tell when exactly, only that it has happened.

New pointer types

To reflect the i8086 segmented memory model, FPC supports several pointer types:

Near pointers

This is the default pointer type in the tiny, small and medium memory models.

Near pointers can be declared in any memory model, using the near keyword. For example:

pascal
type
  PNearInteger = ^Integer; near;

There's also an untyped near pointer, predefined in the system unit, called NearPointer.

Special versions of near pointers

Near pointers are declared using the near keyword, followed by a segment register, enclosed in single quotes. For example:

pascal
type
  PNearCSInteger = ^Integer; near 'CS';
  PNearDSInteger = ^Integer; near 'DS';
  PNearESInteger = ^Integer; near 'ES';
  PNearSSInteger = ^Integer; near 'SS';
  PNearFSInteger = ^Integer; near 'FS';
  PNearGSInteger = ^Integer; near 'GS';

There are also untyped versions, predefined in the system unit:

text
 NearCSPointer
 NearDSPointer
 NearESPointer
 NearSSPointer
 NearFSPointer
 NearGSPointer

Far pointers

This is the default pointer type in the compact, large and huge memory models.

Far pointers can be declared in any memory model, using the far keyword. For example:

pascal
type
  PFarInteger = ^Integer; far;

There's also an untyped far pointer, predefined in the system unit, called FarPointer.

Huge pointers

Huge pointers are like far pointers that support pointer arithmetic without the 64kb segment wraparound. That means they will automatically switch to a different segment if the offset exceeds 64k or becomes less than 0. Thus, they are very useful for accessing blocks of memory that exceed 64kb. Additionally, they support normalization, which is explained in the next section. Their main disadvantage is that they are quite a bit slower compared to far pointers.

Huge pointers are declared using the huge keyword. For example:

pascal
type
  PHugeInteger = ^Integer; huge;

There's also an untyped huge pointer, predefined in the system unit, called HugePointer.

Huge pointer normalization

In real mode it is possible for several different pointers to point to the same memory address. For example, the following different pointers point to the same memory location:

pascal
Ptr($1000, $2345)
Ptr($1200, $0345)
Ptr($1234, $0005)

This is because they resolve to the same linear address via the formula (segment * 16 + offset):

pascal
Ptr($1000, $2345) // -> $1000 * 16 + $2345 = $12345
Ptr($1200, $0345) // -> $1200 * 16 + $0345 = $12345
Ptr($1234, $0005) // -> $1234 * 16 + $0005 = $12345

A Pointer whose offset is between $0 and $F is called a normalized pointer. That's the pointer with the lowest possible offset among all the pointers that point to the same linear address.

Huge pointers can be automatically normalized by the compiler when doing pointer arithmetic on them or when comparing them. This is controlled by the $HugePointerNormalization, $HugePointerArithmeticNormalization and $HugePointerComparisonNormalization directives. All three of them are local switches, so you can change the modes in the middle of the program.

$HugePointerArithmeticNormalization

This directive controls whether huge pointers are automatically normalized by the compiler after doing pointer arithmetic on them.

$HugePointerComparisonNormalization

This directive controls whether huge pointers are normalized before comparing them.

$HugePointerNormalization

This sets both $HugePointerArithmeticNormalization and $HugePointerComparisonNormalization:

mode arithmetic normalization comparison normalization description
{$HugePointerNormalization BorlandC} On On Like Borland C
{$HugePointerNormalization MicrosoftC} Off Off Like Microsoft C
{$HugePointerNormalization WatcomC} Off On Like Watcom C

Tested machines

Compiled programs have been tested and known to work on the following machines:

Supported memory models

Tiny

Small

Medium

Compact

Large

Huge

Units and programs exceeding 64KB of code

Although the medium, large and huge memory models support up to 1MB of code for the whole program+units, there's still a limit of 64kb code per pascal module (i.e. unit or program). If this limit is exceeded, you can overcome this by splitting the unit or program that exceeded 64KB into smaller units, so that each of them stays within the 64KB limit. But there's also an easier solution. You can put the following directive in the module that exceeds 64KB:

text
{$HUGECODE on}

This forces each procedure and function to be in a separate segment. Note that since the code of the unit is no longer in a single segment, this also disallows mixing near and far procedures in the same unit, so in this mode, 'near' and {$F-} are ignored.

Using {$HUGECODE on} does not count as using a different memory model, because you can freely mix units with {$HUGECODE on} and {$HUGECODE off} in the same program.

Supported calling conventions

Currently, only the Pascal calling convention is supported. All the other calling conventions are entirely untested and should not be used, because they may or may not work and may change at any time.

Pascal

This is the default calling convention. It strives for compatibility with Turbo Pascal 7.

Troubleshooting

Compile error: missing .o files

Problem: If you see errors/warnings like this during compiling & linking:

text
test.pas(12,24) Warning: Object system.o not found, Linking may fail !
Error! E2008: cannot open system.o : No such file or directory

(and indeed there don't appear to be any .o files in your msdos units directory)

Solution: You haven't used smartlinking. You should always compile i8086-msdos programs with smartlinking, i.e. use the -XX and -CX options. Smartlinking uses .a files, nonsmartlinking uses .o files.

The reason the .o files aren't generated is because the system unit exceeds the 64kb code limit for the small and tiny memory models and is therefore impossible to compile even 'hello world' without smartlinking, so creating the .o files is a waste of time. It might work for the medium model, but then you have to build the snapshot without -CX. However, with the tight memory constraints of real mode DOS, there is little reason not to use smartlinking in every memory model. Also, the medium model still has a limit of 64k code per unit, so even there it might not work without smartlinking. It certainly hasn't been tested.

Hint: You can add these options to your fpc.cfg file, as described in the "Updating your fpc.cfg" section.

Running the testsuite

Using DOSBox

bash
#!/bin/sh
 
FPCDIR=/home/blablabla/fpc-i8086/fpc
MEMMODEL=Small
 
export FPCMAKE=$FPCDIR/utils/fpcm/fpcmake
export DOSBOX=/usr/bin/dosbox
export SDL_VIDEODRIVER=dummy
export SDL_AUDIODRIVER=dummy
 
cd $FPCDIR/tests/
 
ulimit -m 256000
#ulimit -v 256000
ulimit -d 256000
 
echo running testsuite
make full FPCFPMAKE=$FPCDIR/compiler/ppcx64 TEST_FPC=$FPCDIR/compiler/ppcross8086 TEST_OPT=-Wm$MEMMODEL EMULATOR=$FPCDIR/tests/utils/dosbox/dosbox_wrapper

See also


From the Free Pascal wiki, page "DOS", as it stood on 2021-04-14 after a change by Ñuño Martínez, out of the wiki's dump of 2024-05-26. Copyright stays with the author who wrote it, according to the old wiki's Licensing page. If you have concerns about this material, message the admin.

Revision 2, last changed by WikiArchive .

Talk about this page

Nobody has opened a bench for this page yet. Sign in to open one.