I'm able to use NASM to assemble simple COM files from my modern Linux computer, but how can I create a DOS EXE file while still in Linux?

Specifically, I am interested in a solution that will target 8088 and 80286 processors from a X64 Linux host. Many modern solutions only target 80386 and above.

  • 3
    @Xen2050 - most open source toolchains that target DOS do so on the assumption of a target running a modern processor, so tend to use 386-specific protected mode targets, which is unlikely to be what's wanted here. I'd say this is relevant here.
    – Jules
    Commented Jan 5, 2018 at 9:15
  • 4
    (Also: Wikipedia seems to believe versions of NASM prior to 0.90 never existed. Wikipedia is wrong.)
    – Jules
    Commented Jan 5, 2018 at 10:01
  • 3
    "Wikipedia is wrong." - wouldn't be the first time ;-)
    – Xen2050
    Commented Jan 5, 2018 at 10:02
  • 1
    @Jules edit the article. Commented Jan 5, 2018 at 11:25
  • 2
    @RuiFRibeiro - Wikipedia policy is that you should generally avoid editing articles that are about yourself, and as I'm named in that article I'd probably be cutting it a bit too fine. I also don't have any useful references, which are also generally required for wikipedia policy.
    – Jules
    Commented Jan 5, 2018 at 11:29

5 Answers 5


It turns out that FASM can generate MZ 16-bit DOS executables directly:

format MZ

mov ah, 9h
mov dx, hello
int 21h

mov ax, 4c00h
int 21h

hello db 'Hello, world!', 13, 10, '$'

Which can be assembled and linked automatically:

$ fasm hello.asm

It's not the same format as NASM, but it works for my purposes for now.


NASM can generate OMF-format object files, which are the most common format for DOS programs (although there are other options -- e.g. DJGPP uses COFF format object files, but IIRC the results needs a 386 to run). A search for linkers that support this format turns up few possibilities:

  • JWLink is a fork of the old Watcom linker, which was open-sourced a while back. This is probably your best bet of all of the possibilities I list here because it's a known-working linker that has an officially supported Linux port. I'm including the other options below only for completeness sake, because I found them first. It seems that the only problem with this version is that it doesn't support the DOS/16M DOS extender environment that was usually used with Watcom's tools, so if you wanted to do that you'd probably need to run the official Watcom linker under DOS. I think it does support DOS/4GW, but that requires a 386 to run.

  • WarpLink, which seems to be primarily designed to run in DOS, and is largely written in assembly language modules written using the MASM syntax. You may be able to get this to assemble using JWASM, which is an open-source version of the Watcom assembler, which was somewhat compatible. Some modules are written in C, and if you're lucky this means the IO is handled in those modules so it may be possible to port to Linux. Unfortunately, I have no experience using it, so can't advise beyond this.

  • If you can't get that to run under Linux, another possibility would by JLoc. JLoc is shareware, rather than open source, and the standard distribution is for DOS 386 protected mode. However, its author John Fine was involved in NASM development back in the late 90s and if you can find him (his home page has disappeared, but he may still be contactable via the email address in the linked documentation) may be persuaded to either open source it or release a Linux version. The code is apparently written in C (for DJGPP) so should be somewhat easier to port.

If all else fails, if you have experience working in C writing a linker yourself isn't actually out of the question. The EXE (or "MZ" as it's sometimes known) file format is simple, and there are available libraries that can load object files -- or you can use NASM's "rdf" output format, which was intentionally designed to be as simple as possible in order to support writing custom linkers. There's an example of an extremely simple flat-format output linker in the NASM source code that could server as a starting point for a linker that produces EXE files. You may need to hack the NASM source code a bit to make RDF work in this context -- it doesn't support segment relocations at present, but it should be pretty simple to add them. If you get stuck on that, add a comment here with a way to contact you off this site and I'll try to find some time to assist. It's been a while since I did any hacking on NASM, but I don't think it's changed much since then, so I ought to be able to figure it out reasonably quickly.

(Edit: I previously also sugested 'alink', but having had a look through the source code it seems to only support 32-bit protected mode output using a DPMI DOS extender)

  • Open Watcom C/C++ isn't limited to protected-mode executables, so I don't see why it would be necessary to use an old Watcom version with a DOS/16M license. I have an on-hold project where I'm building an 8086-targeting EXE file that will be used as a custom unzip stub using Open Watcom and the Linux cross-building WLINK lists a DOS/16M target for if you have a license for the stub. (As for your uncertainty about 386 extenders, it includes four choices including DOS/4GW and DOS/4GW's author was looking for a newer version to contribute when he passed away.)
    – ssokolow
    Commented Jul 9, 2020 at 8:29

I think the easiest way is to use DOSBOX like emulator on Linux and use NASM inside it. That way you can test your code directly in MS-DOS environment (even if just emulated).

I am not a Linux user so I do not know if DOSBOX works correctly on Linux but simple google search DOSBox - Runs Old MS-DOS Games/Programs in Linux - Tecmint suggest it does. Just do not forget to increase the number of CPU tics to match desired Speed.

Both NASM and TASM works under Windows DOSBOX so I see no reason why it should not work in Linux either. Here few relate SO/SE QAs of mine (all are *.com however):

You just need NASM binaries for MS-DOS to make this work.

IIRC To create an *.exe file from NASM the code should look a bit differently than the *.com file and also the switches are a bit different.

In NASM the *.com file looks like this:

[BITS 16]
[ORG 100h]

[SEGMENT .text]
    ;here goes the code

And this is my vc.ext entry to compile it in Volcov commander:

    @c:\language\compil\nasm\nasm.exe -ic:\language\source\nasm\inc\ -w+orphan-labels -o !.com !.!

I did not do *.exe files at that time so I do not have any example. All I found is some protected mode demo and from it the source code for exe is different:

;;; Init Flat 32 bit ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
%include "pmode.lib"
;;; Main: ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
bits 32
_main:      sti

        mov     bx,101h
        mov     ax,4f02h
        int     10h

        call    _getselector
        mov     edx,0A0000h
        call    [_setselector]
        mov     es,ax

        mov edi,0
        mov al,13
        mov ecx,65535
        rep stosb

        mov ax,0
        int 16h
        mov ax,3
        int 10h

        mov     ax,es
        call    _freeselector
        mov     es,[_seldata]
        jmp     _exit
;;; End. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

Where pmode.inc contains subroutines macros and stuff defined under:

section code
bits 16

The compilation and link vc.ext entry using turbo assembler linker:

    @c:\language\compil\nasm\nasm.exe -ic:\language\source\nasm\inc\ -w+orphan-labels -f obj !.!
    @c:\language\compil\tasm\tlink.exe /3 /x !.obj , !.exe
    @del !.obj

So without protected mode all the stuff should be in 16 bit I think...

Do not forget to use your own paths in the vc.ext file...

  • Seeing the lack of available linkers as I discovered researching my answer, I'd highly recommend taking this route. Unless you really need to run your build process in Linux, this is likely to be a much better way.
    – Jules
    Commented Jan 5, 2018 at 10:37
  • Another possibility rather than DOSBox would be the older DosEmu -- which just runs a standard DOS installation in the processor's V86 mode, rather than emulating an older processor, so should give faster performance than DOSBox.
    – Jules
    Commented Jan 5, 2018 at 10:39
  • @Jules faster emulation can lead to CRT200 error as many of the older MSDOS tools where Borland CRT based. In such case slow utility sometimes helps. In real MS-DOS I turn off the caches in BIOS but in such case I would not run LINUX ...
    – Spektre
    Commented Jan 5, 2018 at 10:45
  • 1
    I can't remember where I found it now, but I did some work a few years back that involved resurrecting some old Borland Pascal source code, and Borland Pascal had the same issue with timing loops; I found a tool at that time that could patch .EXE files produced using Borland tools and modify the timing loop that overflows so that it can work on modern hardware.
    – Jules
    Commented Jan 5, 2018 at 11:20
  • @Jules Yes I know of that fix but it does not work in all cases (IIRC it got problem with some packed executables). I still got it somewhere on the shelf under serious layer of dust probably.
    – Spektre
    Commented Jan 5, 2018 at 12:24

For completeness, I’ll mention DJLINK, an OMF linker written by DJ Delorie (who also created DJGPP). This is probably not the best option: it never seems to have left proof-of-concept stage, and compiling it on Linux with modern GCC requires a little patching (to add missing const qualifiers and header includes). Nevertheless, I managed to use it with some success on simple NASM-produced object files to generate MZ executables.

  • This is the one I ended up using for my compiler. It seems to work well enough, generating small-mode exe files which work (although getting the magic nasm incantation right to make valid exes wasn't easy). Commented Nov 28, 2020 at 21:54

It's possible to make NASM create a woking DOS .exe file by defining and using a few macros.

Example source file:

%include "fullprog_dosexe.inc.nasm"


mov ah, 9  ; WRITE_STDOUT.
mov dx, msg 
int 0x21  ; http://spike.scu.edu.au/~barry/interrupts.html

fullprog_data  ; This is mandatory for .exe.

msg: db 'Hello, World!', 13, 10, '$'


First download the fullprog_dosexe.inc.nasm.

Compile it with:

$ nasm -f bin -o hedosexe.exe hedosexe.nasm
  • 3
    While this works, there is a big caveat: there's no simple way to produce segment relocation records for the MZ fileheader, so if you need more than 64KiB memory you'll need to make your program auto-relocate. And if you don't need more than 64KiB, why not use a .COM file instead? That said, setting up relocation for a "small" model program (i.e. one code segment and one data+stack segment at different base addresses) isn't too hard, so could be worth doing.
    – occipita
    Commented Jul 13, 2020 at 22:41
  • 2
    @occipita: The solution fullprog_dosexe.inc.nasm does exactly what you write in your comment: setting up relocation for a "small" model program (i.e. one code segment and one data+stack segment at different base addresses).
    – pts
    Commented Jul 13, 2020 at 23:51

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .