djasm is a special-purpose 16-bit assembler used to compile the 16-bit stub that allows DOS to run the COFF files generated by DJGPP.

This is Edition 1.05 of the documentation for djasm, The DJGPP 16-bit Assembler, last updated June 2001, for DJGPP version 2.04. This manual incorporates the information in Bill Currie's LyX documentation for djasm, dated 19 November 1999.

What is djasm?
**************

Disclaimer
==========

     This is an UNREVIEWED and UNGUARANTEED document describing *some* of the aspects of djasm.  We think it might describe djasm, but then it might not.

     ---DJ

Some features described herein may be specific to Bill Currie's latest version of djasm, available at www.taniwha.org, and may not (yet) have found their way into the official DJGPP release. The final authority regarding djasm is the Bison source file djasm.y in the archive djlsrNNN.zip.

Description
===========

djasm is a restricted assembler that was developed for a single purpose: to compile the 16-bit startup stub that is prepended to all DJGPP-built executables. The purpose of the stub is to (i) check for DPMI services, (ii) load CWSDPMI if another DPMI server is not found, (iii) switch into protected mode, and (iv) load and execute a DJGPP-generated COFF image. With djasm the DJGPP distribution can be made completely self-contained; you don't need commercial (or otherwise non-DJGPP) 16-bit tools to build any part of the DJGPP distribution.

Be aware, however, that although djasm can be used in other applications, it is not a full-featured assembler; if you need a sophisticated, general-purpose assembler then consider using AS (the GNU assembler) or NASM (the Netwide Assembler). Give some thought to what you want to do, and choose the right tool for the job. The GNU assembler AS (as.exe) is part of the DJGPP bnuNNNb.zip archive and should be available in the bin subdirectory of your DJGPP tree (i.e., in $DJDIR/bin) if you have installed GCC. The Netwide Assembler NASM lives at www.cryogen.com/Nasm.

A Brief History of djasm
========================

The need for djasm arose when Cygnus, a major user of DJGPP for their DOS-based products, requested a version of DJGPP that was fully independent and self-bootstrapping. The existing DJGPP 1.x system didn't entirely meet their need, however, because a Borland 16-bit compiler (Turbo C 2.0) was required to build the go32.exe program used by DJGPP 1.x.

The first task, therefore, in making a version 2.x of DJGPP that would be entirely self-contained was to write an assembler that could compile the 16-bit stub required by all DJGPP executables. The stub could have been written to use NASM, but (1) NASM wasn't around at the time, and (2) a dedicated 16-bit compiler would make DJGPP independent of any other product. Thus djasm was designed and written for a specific purpose---to compile the 16-bit code for DJGPP's startup stub and enable everything needed to build DJGPP to be distributed freely.

The djasm compiler was to have no other purpose in life, and was never intended to be a generic utility, until some of the developers got carried away and introduced support for many additional opcodes that had not yet been used. As a result, djasm has become sufficiently powerful to be useful for more 16-bit applications than just the DJGPP stub.

An Overview of djasm
********************

What djasm can do
=================

Despite its limitations, djasm does have some nice features:

  * The syntax was designed to be easy to parse, so djasm is relatively easy to learn and use, and code written for djasm tends to be straightforward.

  * djasm is released under the terms of the GPL and comes with DJGPP (djdevNNN.zip).

  * GCC- and AS-generated COFF files can be linked with the code (the linking is done by djasm).

  * djasm produces 16-bit code (currently this is the only available mode).

  * There is support for several output formats, including raw hex output for subsequent use with assembler and C code.

  * C-style structure, union, and enumeration types are supported.

  * Most instructions and addressing modes are supported.

  * Full 32-bit addressing.

Differences between djasm and other assemblers
==============================================

There are some important differences between djasm and other assemblers, such as NASM or the GNU assembler AS.

  * Like most assemblers, djasm has its own syntactic idiosyncrasies. In general, djasm syntax is similar to Intel syntax, and porting code to djasm shouldn't be difficult.

  * djasm generates 16-bit code, AS generates 32-bit code, and NASM can generate both 16- and 32-bit code.

  * djasm is incompletely documented and has incomplete opcode support; AS and NASM are fully supported.

  * djasm does not support macros.

  * djasm is restricted in its output formats.

  * djasm does not support multi-file programs, so all source must be in a single file.

  * djasm builds .exe and other executables directly; there is no linker.

  * Code generated by djasm cannot be linked with DJGPP programs; both NASM and the GNU assembler AS can produce standard COFF objects. However, djasm can build a raw binary image for inclusion in other code.

What's missing from djasm?
==========================

Although djasm can do quite a bit as it stands, there are a number of things that experienced assembly-language programmers will miss. Future versions of djasm may add support for some or all of these items, but again, if you need a sophisticated general-purpose assembler, you should consider AS or NASM.

  * Complete instruction set support.

  * Object files---what format(s)?

  * A companion 16-bit linker.

  * Segments. (Is this really necessary? Is it worth it? Should one just use NASM instead?)

  * Macros.

  * More documentation.

  * Finish support for the Tool Interface Standards (TIS) Relocatable Object Module Format (OMF).

  * Better symbol handling (allow forward references in most expressions).

  * Better section support. At first, just being able to swap between the .text, .data, and .bss sections at will. Later, maybe `real' segment support (especially 16/32-bit control).

  * Finalize instruction support for 486.

  * Pentium* instructions.

When would I want to use djasm?
===============================

Ordinarily, assembly language programmers should use AS, or a 16/32-bit assembler like NASM, for most programming purposes. However, there are a few reasons why you might want to use djasm:

  * If you are looking for a free (sensu GPL) 16-bit assembler.

  * To keep a project compatible with the spirit of DJGPP, without requiring the use of NASM or some other assembler to compile 16-bit code.

  * For small 16-bit projects, such as device drivers, TSRs, boot loaders, or micro-kernels.

Invoking djasm
**************

Invoke djasm as follows:

     djasm INFILE [OUTFILE] [MAPFILE]

where OUTFILE defaults to INFILE.exe after dropping the extension from INFILE, and MAPFILE defaults to INFILE.map after dropping the extension from INFILE.

The extension of outfile determines the format of the generated code. The following output formats are supported:

.exe
     Generate a normal DOS .exe executable (MZ-signature). Does not support more than one segment. This is the only output format that supports the .copyright directive.

.com
     Generate a normal DOS .com file. Program offset is at 0x100.

.bin
.sys
     Generate binary files starting at offset 0. The .sys format is supported so that device drivers can be built directly (i.e., without needing to rename a .bin output file to a .sys file).

.h
     Produce a C-compilable array of bytes (without type declaration or surrounding {}s).

.inc
     Produce a .db array for other djasm .asm files.

.ah
     Produce a .byte array for AS .s or .S files.

For the .h, .inc, and .ah output types, the .type directive is used to determine which binary type to emit (see .type).

The djasm Language
******************

Assembler Syntax
================

As a general rule, djasm uses the same ordering of operands, and usually the same instruction names, as do TASM and MASM.

For ambiguous operand sizes, djasm uses operand suffixes instead of the word ptr, etc., operand modifiers used by other 16-bit assemblers. For example, djasm uses cmpb instead of cmp byte.

Until documentation for djasm is completed, however, you should examine the bison source for djasm (in the file djasm.y) to learn exactly what the instruction mnemonics and their arguments are.

General Directives
==================

.align
     Usage:  .align BOUNDARY [, FILL]

     Emit bytes (if necessary) so that the program counter CS:IP (denoted by .) is a multiple of BOUNDARY (i.e., such that CS:IP % BOUNDARY == 0). The argument FILL specifies what bytes to emit; the default is 0x90 (NOP).

          .align 4       ; align on a 4-byte boundary, pad using 0x90
          .align 16,0    ; align on a 16-byte boundary, pad using 0x00

.bss
     Usage:  .bss

     Signifies the end of generated bytes. No more bytes will be emitted into the image, but space will be reserved so labels can be declared after this point to create uninitialized variables.

.copyright
     Usage:  .copyright STRING

     Inserts the copyright message STRING into the header of the executable, starting approximately at offset 0x61 (there is also some text with the generation details of the file starting at offset 0x28). STRING is a standard C string. Multiple .copyright directives concatenate. This directive is currently supported only for the .exe output format.

.id
     Usage:  .id

     Emit SCCS and RCS identification strings into the instruction stream. These strings have the form:

          $Id: djasm.lyx,v 1.2 1999/06/22 07:08:56 bill Exp $
          @(#) foo.asm built 10/04/97 14:03:30 by djasm

.include
     Usage:  .include "FILE.inc"

     Include a text file for assembly. djasm pushes the current file location, starts processing the included file, then pops the old location when the end of the included file is reached. No path searching is performed to find the .includeed file, but FILE.inc itself can include a path specification.

.linkcoff
     Usage:  .linkcoff "FILE.o"

     Link in an AS-generated 32-bit COFF file (e.g., the output from gcc -c foo.c). Currently supports only i386 COFF files with .text, .data, and .bss sections; these must be the first three sections in the COFF file and must appear in that order. Any other sections will be ignored. These three sections will be placed into the output image in the same order as in the COFF file. The .bss section of the COFF file will be placed into the output image by emitting the appropriate number of zeros---this means that the .bss section from the COFF file will occupy space in the output image rather than being implicit.

     As with .include, no path searching is done for .linkcoff, but FILE.o itself can include a path specification.

.org
     Usage:  .org OFFSET

     Sets the instruction pointer IP to OFFSET. The IP can only be increased using .org, never decreased.

.stack
     Usage:  .stack

     For .exe files only, sets the initial value of the stack pointer SP to the current value of the instruction pointer IP.

.start
     Usage:  .start

     For .exe files only, sets the initial value of the instruction pointer IP to the current value of the program counter CS:IP.

.type
     Usage:  .type "IMAGE"

     where IMAGE is one of com, bin, sys, h, inc, or ah. The following table summarizes the effect of various combinations of output file extension and .type declaration.

          +---------+-----------------------------------------------------+
          | Output  | Value of .type                                    |
          |  file   +-----------------------------------------------------+
          |extension| exe     com     bin     sys(b)  h       inc     ah  |
          +---------+-----------------------------------------------------+
          |  exe(c) | exe     exe(a)  exe     exe     exe     exe     exe |
          |  com    | ---     com     ---     ---     ---     ---     --- |
          |  bin    | bin     bin(a)  bin     bin     bin     bin     bin |
          |  sys(b) | bin     bin(a)  bin     bin     bin     bin     bin |
          |  h      | exe     com     bin     bin     exe     bin     bin |
          |  inc    | exe     com     bin     bin     exe     bin     bin |
          |  ah     | exe     com     bin     bin     exe     bin     bin |
          +---------+-----------------------------------------------------+

      a. The first 0x100 bytes of the program are 0x90 (NOP), followed by the generated code.

      b. The .sys type is a synonym for .bin.

      c. .exe is the default output file extension if no output file (or output file extension) is specified.

Data Directives
===============

The data directives are used to reserve space for byte (8-bit), word (16-bit), and double word (32-bit) data. The allocated space may be initialized or left indeterminate. The general syntax of the data allocation directives is:

     .db [dblist [','dblist...]]
     .dw [dwlist [','dwlist...]]
     .dd [ddlist [','ddlist...]]

.db
     Define a byte of data.

.dd
     Define a word of data.

.dw
     Define a double word of data.

.dblist
.dwlist
.ddlist
     List of byte, word, or double word data.

The data lists dblist, dwlist, and ddlist are constructed from the following :

constant
     A decimal (e.g., 144), hexadecimal (e.g., 0x90), or character (e.g., 'A') constant.

string
     A C-style string constant, e.g., "This is a string.".

UID offset
     This item is available only for the dwlist and ddlist directives.

const .dup const
     Repeat a block of data.

Structures
==========

The following directives provide support for C-style struct, union, and enum constructs.

.struct
     Start a structure.

.union
     Start a union.

.enum
     Define an enumeration type.

.ends
     End a structure, union, or enumeration.

Structures and Unions
---------------------

In their usage, .struct is the same as struct in C, and .union is the same as union in C. For example, the construct

     .struct FARPTR
             off     .dw
             seg     .dw
     .ends

defines the following symbols:

     FARPTR     = 4          ; size of the `FARPTR' structure
     FARPTR.off = 0          ; offset of `off' in the `FARPTR' structure
     FARPTR.seg = 2          ; offset of `seg' in the `FARPTR' structure

If, in the above example, the .union directive had been used instead of .struct, a union would have been defined, capable of holding an offset or a segment, but not both---thus the construct

     .union FARPTR
            off      .dw
            seg      .dw
     .ends

defines the following symbols:

     FARPTR     = 2          ; size of the `FARPTR' union
     FARPTR.off = 0          ; offset of `off' in the `FARPTR' union
     FARPTR.seg = 0          ; offset of `seg' in the `FARPTR' union

Nested structures are supported. Here is an example of a nested structure XMS_MOVE that uses the FARPTR structure from the previous example:

     .struct XMS_MOVE
             length          .dd
             src_handle      .dw
             src_offset      .struct FARPTR
             dst_handle      .dw
             dst_offset      .struct FARPTR
     .ends

This construct defines the following symbols and offsets (the members of each structure have been indented to illustrate the nesting, and the size in bytes of each member is given in brackets):

     XMS_MOVE                    = 16        ; size of the XMS_MOVE structure
     XMS_MOVE.length             =  0  [4]
     XMS_MOVE.src_handle         =  4  [2]
       XMS_MOVE.src_offset       =  6
       XMS_MOVE.src_offset.off   =  6  [2]
       XMS_MOVE.src_offset.seg   =  8  [2]
     XMS_MOVE.dst_handle         = 10  [2]
       XMS_MOVE.dst_offset       = 12
       XMS_MOVE.dst_offset.off   = 12  [2]
       XMS_MOVE.dst_offset.seg   = 14  [2]

Note that XMS_MOVE.src_offset and XMS_MOVE.src_offset.off share the same offset, as do XMS_MOVE.dst_offset and XMS_MOVE.dst_offset.off---this is so that the offset of the nested structure or union can be used directly.

When declaring the members of a structure or union, .struct and .union can be used interchangably without changing the resulting layout of the final construct. In other words, .struct could be used when defining unions and .union can be used when defining structures. This practice is confusing and is not recommended; the appropriate directive should always be used, but this is not enforced (mostly because there is no way of detecting it).

Variables can be declared to be structures or unions. For example, the statement

     xms_move_packet .struct XMS_MOVE

has the same effect as

     xms_move_packet:
             xms_move_packet.length:
             .dd 0
             xms_move_packet.src_handle:
             .dw 0
     xms_move_packet.src_offset:
             xms_move_packet.src_offset.off:
             .dw 0
             xms_move_packet.src_offset.seg:
             .dw 0
             xms_move_packet.dst_handle:
             .dw 0
     xms_move_packet.dst_offset:
             xms_move_packet.dst_offset.off:
             .dw 0
             xms_move_packet.dst_offset.seg:
             .dw 0

It is sometimes desirable to place the structure at a specified location. For this purpose, any valid expression using defined symbols is allowed, not just .token or .token-symbol. For example, the following two lines

     xms_move_packet .struct XMS_MOVE (.) ; doesn't emit any bytes!
     xms_move_packet .struct XMS_MOVE (.-sh_handle_cache)

have the same effect as X=. or X=.-sh_handle_cache, respectively (recall that . denotes the current program counter), and generate the following structure:

     xms_move_packet                = X
     xms_move_packet.length         = X
     xms_move_packet.src_handle     = X +  4
     xms_move_packet.src_offset     = X +  6
     xms_move_packet.src_offset.off = X +  6
     xms_move_packet.src_offset.seg = X +  8
     xms_move_packet.dst_handle     = X + 10
     xms_move_packet.dst_offset     = X + 12
     xms_move_packet.dst_offset.off = X + 12
     xms_move_packet.dst_offset.seg = X + 14

The (.) form is useful for initializing structures---just follow the declaration line by anonymous .db or .dw or .dd lines. Unfortunately this is prone to error, but it is currently the only way of doing this. Any expression that djasm can handle as a relocation is permitted in the parentheses; this can be useful for declaring structures at some specific address (e.g., the PSP structure or some of the DOS system tables).

Enumerations
------------

The enumeration type allows arbitrary mnemonic identifiers (the enumeration constants) to be mapped to a set of integer values. The classical textbook example is that of defining a boolean variable. The following construct defines boolean as an enumeration type that can take the values 0 (false) or 1 (true).

     .enum   boolean
             false
             true
     .ends

In this example, pets is defined to be an enumeration type having integral values 0 (bird), 1 (cat), 2 (dog), or 3 (rock).

     .enum   pets
             bird
             cat
             dog
             rock
     .ends

In a djasm program the above enumeration types could be referenced as follows:

     .start
             nop
             mov     ax,true                 ; ax <- 1
             mov     bx,pets.dog             ; bx <- 2
             mov     cx,pets.rock            ; cx <- 3
             mov     dx,pets.bird            ; dx <- 0
             int     0x20

If the enumeration constants are not explicitly initialized, then they are assigned consecutive integer values starting with zero. Explicitly and implicitly initialized enumeration constants can be used within the same enumeration type, in which case an enumeration constant without an initializer will be assigned the successor of the previous enumeration constant.

In this example, the enumeration constants foo, bar, and e are implicitly initialized. A variable of enumeration type snafu therefore takes one of the following integer values: 0 (foo), 1 (bar), 1 (a), 2 (b), 4 (c), 8 (d), or 9 (e).

     .enum   snafu
             foo
             bar
             a=1
             b=2
             c=4
             d=8
             e
     .ends

Segment Override Prefixes
=========================

Each prefix directive causes the appropriate instruction prefix byte to be emitted into the instruction stream.

Usage:

     prefix
     instruction

where prefix can be one of the following:

.addrsize
     toggles selection of 16- or 32-bit addressing

     The address size prefix byte (0x67) is emitted---this tells djasm to interpret the address as either a 32- or a 16-bit value. For instance, it determines whether di or edi is used as the index register for a string instruction.

.opsize
     toggles selection of 16- or 32-bit operands

     The data size prefix byte (0x66) is emitted. This determines the size of the operands, distinguishing between mov ax,bx and mov eax,ebx, for example.

     The .addrsize and .opsize opcode prefix bytes are used by djasm to express the 386's confusing opcode prefix scheme. For backward compatibility with the 8086, instructions have both 16-bit and 32-bit forms. But, their opcodes are identical. So in a 32-bit segment the 32-bit forms are used by default, and in 16-bit segments the 16-bit forms are used by default. These prefix bytes are used to force the compiler to use the non-default form (e.g., issue a mov ax,bx within a 32-bit segment).

.segcs
     code segment override

.segds
     data segment override

.seges
     extra segment override

.segss
     stack segment override

.segfs
     user segment `f' override

.seggs
     user segment `g' override

The .seg?? prefixes seem to be most appropriate when used with the string instructions such as movs, stos, etc.).

Mnemonics
=========

This section documents and describes the djasm assembler mnemonics. For convenience, the organization and classification of the mnemonics generally follows that of Triebel (1992) (see Triebel-1992).

Data Transfer Instructions
--------------------------

     [file xfer.txi not included]

Arithmetic Instructions
-----------------------

     [file add.txi not included]
     [file sub.txi not included]
     [file muldiv.txi not included]

Logic Instructions
------------------

     [file logic.txi not included]

Shift Instructions
------------------

     [file shift.txi not included]

Rotate Instructions
-------------------

     [file rotate.txi not included]

Bit Test and Bit Scan Instructions
----------------------------------

     [file bit.txi not included]

Flag Control Instructions
-------------------------

     [file flag.txi not included]

Compare and Set Instructions
----------------------------

     [file cmp.txi not included]
     [file set.txi not included]

Jump Instructions
-----------------

     [file jmp.txi not included]
     [file jcc.txi not included]

Subroutine Handling Instructions
--------------------------------

     [file subr.txi not included]

Loop Handling Instructions
--------------------------

     [file loop.txi not included]

String Handling Instructions
----------------------------

     [file string.txi not included]

Programming Examples
********************

Some examples of programming with djasm.

``Hello, world'' demo program
=============================

     .type   "com"
     .org    0x100                   ; origin for .COM program

             mov dx, msg             ; point DX to message
             mov ah, 0x9             ; DOS print string function
             int 0x21
             mov ax, 0x4c00          ; DOS exit with errorlevel
             int 0x21
     msg:
             .db "hello, world\r\n$"

Enter the above code into a file hello.asm, then make a DOS .com executable using the command

     djasm hello.asm hello.com hello.map

Examples of enumeration types
=============================

Here are a few examples of using enumeration types in djasm. These are taken directly from the section on enumeration types (see Enumerations).

 1. Use an enumeration to define a new type, boolean. Load register AX with the value false and register BX with the value true.

          .type   "com"
          .org    0x100
          .enum   boolean
                  false
                  true
          .ends
                  mov     ax,boolean.false
                  mov     bx,boolean.true
                  int     0x20

     If you assemble this program with djasm,

          djasm.exe bool.asm bool.com bool.map

     and inspect the resulting .map file, you will find the following enumeration definition:

          Address    Symbols by Value

          0000:0000  __zero__ (?)
          0000:0000  boolean.false (?)
          0000:0001  boolean.true (?)

     If you inspect the assembled code using DOS debug, you will see that the registers are indeed loaded with the proper values:

          % debug bool.exe
          -u 0100 l 08
          1600:0100 B80000        MOV     AX,0000
          1600:0103 BB0100        MOV     BX,0001
          1600:0106 CD20          INT     20
          -q

 2. This example defines an enumeration type snafu and demonstrates both implicit and explicit initialization of the enumeration constants. In this example, the enumeration constants foo, bar, and e are implicitly initialized with the values 0, 1, and 9, respectively.

          .type   "com"
          .org    0x100
          .enum   snafu
                  foo
                  bar
                  a=1
                  b=2
                  c=4
                  d=8
                  e
          .ends
                  int     0x20

     The .map file generated by djasm shows the enumeration constants and how they were defined:

          Address    Symbols by Value

          0000:0000  __zero__ (?)
          0000:0000  snafu.foo (?)
          0000:0001  snafu.a (?)
          0000:0001  snafu.bar (?)
          0000:0002  snafu.b (?)
          0000:0004  snafu.c (?)
          0000:0008  snafu.d (?)
          0000:0009  snafu.e (?)

Example of opcode prefix usage
==============================

The following code fragment, taken from stub.asm in the djlsrNNN.zip archive, illustrates the use of the .addrsize and .opsize opcode prefixes.

               :
             [32-bit instructions]
               :
             .addrsize
             rep
             stosd
               :
             [more 16-bit instructions]
               :
             .opsize
             jmpf    fs:[start_eip]          ; start program

Here, .addrsize toggles from the 32-bit addressing mode to the 16-bit mode, and forces the stosd instruction to use di instead of edi as the index register. The .opsize directive toggles between 16- and 32-bit operand sizes. In this example it is being used to jump to a 32-bit address from a 16-bit segment.

Contributors to djasm
*********************

The following individuals (in alphabetical order) have contributed to djasm, or to its documentation, or both. If you have contributed to djasm in some way and are not listed here, please make yourself known. You can also let us know if your e-mail address is okay.

  * John M. Aldrich

  * Bill Currie

  * DJ Delorie

  * Nate Eldredge

  * Charles Sandmann

  * Margo Schulter

  * Martin Strömberg

  * Aaron Ucko

  * Vic

  * Morten Welinder

  * Jeff Williams

  * Eli Zaretskii

References
**********

 1. Triebel, Walter A. 1992. The 80386DX microprocessor:  hardware, software, and interfacing. Englewood Cliffs, NJ: Prentice-Hall. ISBN 0-13-249566-X.