Skip to main content
fix a bug, relocate macro should write to different lists (not really needed but was intended this way)
Source Link
ecm
  • 983
  • 2
  • 7
  • 16

; Public Domain

%include "lmacros3.mac"

numdef DOSEXIT, 0
numdef DOSENTRY, 0
numdef LARGEFILL, 0
numdef RELOC, 1
numdef BASESEGMENT, 0
numdef RELOCEXE, 0


        cpu 8086
%if _RELOCEXE
%assign ORIGIN 0
        org ORIGIN
        addsection HEADER, start=0
header_start:
        db "MZ"         ; exeSignature
FILESIZE equ fromparas(header_size_p + BEHINDSEGMENT)
        dw FILESIZE % 512       ; exeExtraBytes
        dw (FILESIZE + 511) / 512       ; exePages
        dw relocationtable.amount       ; exeRelocItems
        dw header_size_p        ; exeHeaderSize
        dw paras(512)           ; exeMinAlloc
        dw paras(512)           ; exeMaxAlloc
        dw +BEHINDSEGMENT       ; exeInitSS
        dw 512                  ; exeInitSP
        dw 0            ; exeChecksum
        dw 0, +0        ; exeInitCSIP
        dw relocationtable      ; exeRelocTable

        addsection FIRST, vstart=0 align=16 follows=HEADER
%elif _DOSENTRY
%assign ORIGIN 256
        org ORIGIN
        addsection FIRST, start=ORIGIN
%else
%assign ORIGIN 0
        org ORIGIN
        addsection FIRST, start=ORIGIN
%endif
first_start:
        addsection SECOND, vstart=0 align=16 follows=FIRST
second_start:
        addsection THIRD, vstart=0 align=16 follows=SECOND
third_start:

first_size equ first_end - first_start
endarea first, 1
second_size equ second_end - second_start
endarea second, 1
third_size equ third_end - third_start
endarea third, 1
%if _RELOCEXE
header_size equ header_end - header_start
endarea header, 1
%endif

FIRSTSEGMENT equ _BASESEGMENT + 0
SECONDSEGMENT equ _BASESEGMENT + paras(ORIGIN) + first_size_p
THIRDSEGMENT equ _BASESEGMENT + paras(ORIGIN) + first_size_p + second_size_p
BEHINDSEGMENT equ _BASESEGMENT + paras(ORIGIN) + first_size_p + second_size_p + third_size_p

%define RELOCATIONFROMFIRST ""
%define RELOCATIONFROMSECOND ""
%define RELOCATIONFROMTHIRD ""
        %imacro relocation 0-1.nolist -2
%%reloc equ $ + %1
%ifidn _CURRENT_SECTION, FIRST
 %xdefine RELOCATIONFROMFIRST RELOCATIONFROMFIRST, %%reloc, FIRSTSEGMENT
%elifidn _CURRENT_SECTION, SECOND
 %xdefine RELOCATIONFROMFIRSTRELOCATIONFROMSECOND RELOCATIONFROMFIRSTRELOCATIONFROMSECOND, %%reloc, SECONDSEGMENT
%elifidn _CURRENT_SECTION, THIRD
 %xdefine RELOCATIONFROMFIRSTRELOCATIONFROMTHIRD RELOCATIONFROMFIRSTRELOCATIONFROMTHIRD, %%reloc, THIRDSEGMENT
%else
 %error Unknown section for relocation
%endif
        %endmacro

        usesection FIRST
start:
        mov dx, cs
        mov ds, dx
%if !_RELOCEXE && _RELOC
        mov si, relocationtable
        mov cx, relocationtable.amount
        jcxz .noreloc
@@:
        lodsw
        xchg bx, ax
        lodsw
        add ax, dx
        mov es, ax
        add word [es:bx], dx
        loop @B
.noreloc:
%endif

displayfirst:
        mov si, firstmsg
        mov bx, 7
        mov ah, 0Eh
        db __TEST_IMM16         ; (skip int 10h)
@@:
        int 10h
        lodsb
        test al, al
        jnz @B

        call SECONDSEGMENT:secondentry
relocation

exit:
%if _DOSEXIT
        mov ax, 4C00h
        int 21h
%else
        xor ax, ax
        int 16h
        int 19h
%endif  


bouncetothird:
        jmp THIRDSEGMENT:bounced
relocation


firstmsg:       asciz "Hello from first!",13,10


        usesection SECOND

secondmsg:      asciz "Hello from second!",13,10

secondentry:
displaysecond:
        mov si, secondmsg
        mov bx, 7
        mov ah, 0Eh
        db __TEST_IMM16         ; (skip int 10h)
@@:
        int 10h
        cs lodsb
        test al, al
        jnz @B

        jmp THIRDSEGMENT:thirdentry
relocation


        usesection THIRD

        align 2, db 0
indirect_to_bounce:
                dw bouncetothird
relocation 0
                dw FIRSTSEGMENT

thirdmsg:       asciz "Hello from third!",13,10

thirdentry:
        jmp far [cs:indirect_to_bounce]

bounced:
displaythird:
        mov si, thirdmsg
        mov bx, 7
        mov ah, 0Eh
        db __TEST_IMM16         ; (skip int 10h)
@@:
        int 10h
        cs lodsb
        test al, al
        jnz @B

        retf


%ifn _RELOCEXE
        usesection FIRST
%else
        usesection HEADER
%endif
%if _RELOCEXE || _RELOC
        align 4, db 0
relocationtable:
.:
        dw RELOCATIONFROMFIRST
        dw RELOCATIONFROMSECOND
        dw RELOCATIONFROMTHIRD
.end:
.amount: equ (.end - .) / 4
%endif
%if _RELOCEXE
        align 16
header_end:
%endif

%if _LARGEFILL
        usesection SECOND
        _fill fromkib(64), 0CCh, second_start
%endif

        usesection FIRST
        align 16
first_end:
        usesection SECOND
        align 16
second_end:
        usesection THIRD
        align 16
third_end:

; Public Domain

%include "lmacros3.mac"

numdef DOSEXIT, 0
numdef DOSENTRY, 0
numdef LARGEFILL, 0
numdef RELOC, 1
numdef BASESEGMENT, 0
numdef RELOCEXE, 0


        cpu 8086
%if _RELOCEXE
%assign ORIGIN 0
        org ORIGIN
        addsection HEADER, start=0
header_start:
        db "MZ"         ; exeSignature
FILESIZE equ fromparas(header_size_p + BEHINDSEGMENT)
        dw FILESIZE % 512       ; exeExtraBytes
        dw (FILESIZE + 511) / 512       ; exePages
        dw relocationtable.amount       ; exeRelocItems
        dw header_size_p        ; exeHeaderSize
        dw paras(512)           ; exeMinAlloc
        dw paras(512)           ; exeMaxAlloc
        dw +BEHINDSEGMENT       ; exeInitSS
        dw 512                  ; exeInitSP
        dw 0            ; exeChecksum
        dw 0, +0        ; exeInitCSIP
        dw relocationtable      ; exeRelocTable

        addsection FIRST, vstart=0 align=16 follows=HEADER
%elif _DOSENTRY
%assign ORIGIN 256
        org ORIGIN
        addsection FIRST, start=ORIGIN
%else
%assign ORIGIN 0
        org ORIGIN
        addsection FIRST, start=ORIGIN
%endif
first_start:
        addsection SECOND, vstart=0 align=16 follows=FIRST
second_start:
        addsection THIRD, vstart=0 align=16 follows=SECOND
third_start:

first_size equ first_end - first_start
endarea first, 1
second_size equ second_end - second_start
endarea second, 1
third_size equ third_end - third_start
endarea third, 1
%if _RELOCEXE
header_size equ header_end - header_start
endarea header, 1
%endif

FIRSTSEGMENT equ _BASESEGMENT + 0
SECONDSEGMENT equ _BASESEGMENT + paras(ORIGIN) + first_size_p
THIRDSEGMENT equ _BASESEGMENT + paras(ORIGIN) + first_size_p + second_size_p
BEHINDSEGMENT equ _BASESEGMENT + paras(ORIGIN) + first_size_p + second_size_p + third_size_p

%define RELOCATIONFROMFIRST ""
%define RELOCATIONFROMSECOND ""
%define RELOCATIONFROMTHIRD ""
        %imacro relocation 0-1.nolist -2
%%reloc equ $ + %1
%ifidn _CURRENT_SECTION, FIRST
 %xdefine RELOCATIONFROMFIRST RELOCATIONFROMFIRST, %%reloc, FIRSTSEGMENT
%elifidn _CURRENT_SECTION, SECOND
 %xdefine RELOCATIONFROMFIRST RELOCATIONFROMFIRST, %%reloc, SECONDSEGMENT
%elifidn _CURRENT_SECTION, THIRD
 %xdefine RELOCATIONFROMFIRST RELOCATIONFROMFIRST, %%reloc, THIRDSEGMENT
%else
 %error Unknown section for relocation
%endif
        %endmacro

        usesection FIRST
start:
        mov dx, cs
        mov ds, dx
%if !_RELOCEXE && _RELOC
        mov si, relocationtable
        mov cx, relocationtable.amount
        jcxz .noreloc
@@:
        lodsw
        xchg bx, ax
        lodsw
        add ax, dx
        mov es, ax
        add word [es:bx], dx
        loop @B
.noreloc:
%endif

displayfirst:
        mov si, firstmsg
        mov bx, 7
        mov ah, 0Eh
        db __TEST_IMM16         ; (skip int 10h)
@@:
        int 10h
        lodsb
        test al, al
        jnz @B

        call SECONDSEGMENT:secondentry
relocation

exit:
%if _DOSEXIT
        mov ax, 4C00h
        int 21h
%else
        xor ax, ax
        int 16h
        int 19h
%endif  


bouncetothird:
        jmp THIRDSEGMENT:bounced
relocation


firstmsg:       asciz "Hello from first!",13,10


        usesection SECOND

secondmsg:      asciz "Hello from second!",13,10

secondentry:
displaysecond:
        mov si, secondmsg
        mov bx, 7
        mov ah, 0Eh
        db __TEST_IMM16         ; (skip int 10h)
@@:
        int 10h
        cs lodsb
        test al, al
        jnz @B

        jmp THIRDSEGMENT:thirdentry
relocation


        usesection THIRD

        align 2, db 0
indirect_to_bounce:
                dw bouncetothird
relocation 0
                dw FIRSTSEGMENT

thirdmsg:       asciz "Hello from third!",13,10

thirdentry:
        jmp far [cs:indirect_to_bounce]

bounced:
displaythird:
        mov si, thirdmsg
        mov bx, 7
        mov ah, 0Eh
        db __TEST_IMM16         ; (skip int 10h)
@@:
        int 10h
        cs lodsb
        test al, al
        jnz @B

        retf


%ifn _RELOCEXE
        usesection FIRST
%else
        usesection HEADER
%endif
%if _RELOCEXE || _RELOC
        align 4, db 0
relocationtable:
.:
        dw RELOCATIONFROMFIRST
        dw RELOCATIONFROMSECOND
        dw RELOCATIONFROMTHIRD
.end:
.amount: equ (.end - .) / 4
%endif
%if _RELOCEXE
        align 16
header_end:
%endif

%if _LARGEFILL
        usesection SECOND
        _fill fromkib(64), 0CCh, second_start
%endif

        usesection FIRST
        align 16
first_end:
        usesection SECOND
        align 16
second_end:
        usesection THIRD
        align 16
third_end:

; Public Domain

%include "lmacros3.mac"

numdef DOSEXIT, 0
numdef DOSENTRY, 0
numdef LARGEFILL, 0
numdef RELOC, 1
numdef BASESEGMENT, 0
numdef RELOCEXE, 0


        cpu 8086
%if _RELOCEXE
%assign ORIGIN 0
        org ORIGIN
        addsection HEADER, start=0
header_start:
        db "MZ"         ; exeSignature
FILESIZE equ fromparas(header_size_p + BEHINDSEGMENT)
        dw FILESIZE % 512       ; exeExtraBytes
        dw (FILESIZE + 511) / 512       ; exePages
        dw relocationtable.amount       ; exeRelocItems
        dw header_size_p        ; exeHeaderSize
        dw paras(512)           ; exeMinAlloc
        dw paras(512)           ; exeMaxAlloc
        dw +BEHINDSEGMENT       ; exeInitSS
        dw 512                  ; exeInitSP
        dw 0            ; exeChecksum
        dw 0, +0        ; exeInitCSIP
        dw relocationtable      ; exeRelocTable

        addsection FIRST, vstart=0 align=16 follows=HEADER
%elif _DOSENTRY
%assign ORIGIN 256
        org ORIGIN
        addsection FIRST, start=ORIGIN
%else
%assign ORIGIN 0
        org ORIGIN
        addsection FIRST, start=ORIGIN
%endif
first_start:
        addsection SECOND, vstart=0 align=16 follows=FIRST
second_start:
        addsection THIRD, vstart=0 align=16 follows=SECOND
third_start:

first_size equ first_end - first_start
endarea first, 1
second_size equ second_end - second_start
endarea second, 1
third_size equ third_end - third_start
endarea third, 1
%if _RELOCEXE
header_size equ header_end - header_start
endarea header, 1
%endif

FIRSTSEGMENT equ _BASESEGMENT + 0
SECONDSEGMENT equ _BASESEGMENT + paras(ORIGIN) + first_size_p
THIRDSEGMENT equ _BASESEGMENT + paras(ORIGIN) + first_size_p + second_size_p
BEHINDSEGMENT equ _BASESEGMENT + paras(ORIGIN) + first_size_p + second_size_p + third_size_p

%define RELOCATIONFROMFIRST ""
%define RELOCATIONFROMSECOND ""
%define RELOCATIONFROMTHIRD ""
        %imacro relocation 0-1.nolist -2
%%reloc equ $ + %1
%ifidn _CURRENT_SECTION, FIRST
 %xdefine RELOCATIONFROMFIRST RELOCATIONFROMFIRST, %%reloc, FIRSTSEGMENT
%elifidn _CURRENT_SECTION, SECOND
 %xdefine RELOCATIONFROMSECOND RELOCATIONFROMSECOND, %%reloc, SECONDSEGMENT
%elifidn _CURRENT_SECTION, THIRD
 %xdefine RELOCATIONFROMTHIRD RELOCATIONFROMTHIRD, %%reloc, THIRDSEGMENT
%else
 %error Unknown section for relocation
%endif
        %endmacro

        usesection FIRST
start:
        mov dx, cs
        mov ds, dx
%if !_RELOCEXE && _RELOC
        mov si, relocationtable
        mov cx, relocationtable.amount
        jcxz .noreloc
@@:
        lodsw
        xchg bx, ax
        lodsw
        add ax, dx
        mov es, ax
        add word [es:bx], dx
        loop @B
.noreloc:
%endif

displayfirst:
        mov si, firstmsg
        mov bx, 7
        mov ah, 0Eh
        db __TEST_IMM16         ; (skip int 10h)
@@:
        int 10h
        lodsb
        test al, al
        jnz @B

        call SECONDSEGMENT:secondentry
relocation

exit:
%if _DOSEXIT
        mov ax, 4C00h
        int 21h
%else
        xor ax, ax
        int 16h
        int 19h
%endif  


bouncetothird:
        jmp THIRDSEGMENT:bounced
relocation


firstmsg:       asciz "Hello from first!",13,10


        usesection SECOND

secondmsg:      asciz "Hello from second!",13,10

secondentry:
displaysecond:
        mov si, secondmsg
        mov bx, 7
        mov ah, 0Eh
        db __TEST_IMM16         ; (skip int 10h)
@@:
        int 10h
        cs lodsb
        test al, al
        jnz @B

        jmp THIRDSEGMENT:thirdentry
relocation


        usesection THIRD

        align 2, db 0
indirect_to_bounce:
                dw bouncetothird
relocation 0
                dw FIRSTSEGMENT

thirdmsg:       asciz "Hello from third!",13,10

thirdentry:
        jmp far [cs:indirect_to_bounce]

bounced:
displaythird:
        mov si, thirdmsg
        mov bx, 7
        mov ah, 0Eh
        db __TEST_IMM16         ; (skip int 10h)
@@:
        int 10h
        cs lodsb
        test al, al
        jnz @B

        retf


%ifn _RELOCEXE
        usesection FIRST
%else
        usesection HEADER
%endif
%if _RELOCEXE || _RELOC
        align 4, db 0
relocationtable:
.:
        dw RELOCATIONFROMFIRST
        dw RELOCATIONFROMSECOND
        dw RELOCATIONFROMTHIRD
.end:
.amount: equ (.end - .) / 4
%endif
%if _RELOCEXE
        align 16
header_end:
%endif

%if _LARGEFILL
        usesection SECOND
        _fill fromkib(64), 0CCh, second_start
%endif

        usesection FIRST
        align 16
first_end:
        usesection SECOND
        align 16
second_end:
        usesection THIRD
        align 16
third_end:
add boot test using qemu, ldosboot, bootimg, and ldebug
Source Link
ecm
  • 983
  • 2
  • 7
  • 16

As an addition, I tested loading the test.bin file as a kernel from my bootable debugger. These are the commands I used:

nasm -l boot12.lst ~/proj/ldosboot/boot.asm -I ~/proj/lmacros/ -D_LOAD_NAME="'LDEBUG'" -o boot12.bin; nasm ~/proj/bootimg/bootimg.asm -I ~/proj/lmacros/ -o disk12.img -D_BOOTPATCHFILE=boot12.bin -D_PAYLOADFILE=../../../proj/ldebug/bin/ldebug.com,ldebug.sld,test.bin

qemu-system-i386 -fda disk12.img -boot order=a -display curses

This is the ldebug.sld file:

:bootstartup
boot protocol freedos segment=3000 entry=0:0 test.bin

This boot command modifies the FreeDOS load protocol (which says the whole file must be loaded) to load at the same segment we specified as base address, and sets the entry parameter as well for good measure. The filename points to our test binary.

The bootimg and ldosboot repos are found at https://hg.pushbx.org/ecm/ as well. The debugger is hosted at https://pushbx.org/ecm/web/#projects-ldebug


As an addition, I tested loading the test.bin file as a kernel from my bootable debugger. These are the commands I used:

nasm -l boot12.lst ~/proj/ldosboot/boot.asm -I ~/proj/lmacros/ -D_LOAD_NAME="'LDEBUG'" -o boot12.bin; nasm ~/proj/bootimg/bootimg.asm -I ~/proj/lmacros/ -o disk12.img -D_BOOTPATCHFILE=boot12.bin -D_PAYLOADFILE=../../../proj/ldebug/bin/ldebug.com,ldebug.sld,test.bin

qemu-system-i386 -fda disk12.img -boot order=a -display curses

This is the ldebug.sld file:

:bootstartup
boot protocol freedos segment=3000 entry=0:0 test.bin

This boot command modifies the FreeDOS load protocol (which says the whole file must be loaded) to load at the same segment we specified as base address, and sets the entry parameter as well for good measure. The filename points to our test binary.

The bootimg and ldosboot repos are found at https://hg.pushbx.org/ecm/ as well. The debugger is hosted at https://pushbx.org/ecm/web/#projects-ldebug

Source Link
ecm
  • 983
  • 2
  • 7
  • 16

I wanted to make a few examples of what I mentioned in a comment. That is, to create an MZ .EXE header or to manually implement relocations in a flat format binary, all using only NASM's multi-section -f bin format.

To save on work, I actually created a single example with multiple build options to select what layout to use exactly.

Some macros used in this example are from my 8086 macro collection found at https://hg.pushbx.org/ecm/lmacros/ and all other files are in https://pushbx.org/ecm/test/20230702/

This is the main sources, test.asm:


; Public Domain

%include "lmacros3.mac"

numdef DOSEXIT, 0
numdef DOSENTRY, 0
numdef LARGEFILL, 0
numdef RELOC, 1
numdef BASESEGMENT, 0
numdef RELOCEXE, 0


        cpu 8086
%if _RELOCEXE
%assign ORIGIN 0
        org ORIGIN
        addsection HEADER, start=0
header_start:
        db "MZ"         ; exeSignature
FILESIZE equ fromparas(header_size_p + BEHINDSEGMENT)
        dw FILESIZE % 512       ; exeExtraBytes
        dw (FILESIZE + 511) / 512       ; exePages
        dw relocationtable.amount       ; exeRelocItems
        dw header_size_p        ; exeHeaderSize
        dw paras(512)           ; exeMinAlloc
        dw paras(512)           ; exeMaxAlloc
        dw +BEHINDSEGMENT       ; exeInitSS
        dw 512                  ; exeInitSP
        dw 0            ; exeChecksum
        dw 0, +0        ; exeInitCSIP
        dw relocationtable      ; exeRelocTable

        addsection FIRST, vstart=0 align=16 follows=HEADER
%elif _DOSENTRY
%assign ORIGIN 256
        org ORIGIN
        addsection FIRST, start=ORIGIN
%else
%assign ORIGIN 0
        org ORIGIN
        addsection FIRST, start=ORIGIN
%endif
first_start:
        addsection SECOND, vstart=0 align=16 follows=FIRST
second_start:
        addsection THIRD, vstart=0 align=16 follows=SECOND
third_start:

first_size equ first_end - first_start
endarea first, 1
second_size equ second_end - second_start
endarea second, 1
third_size equ third_end - third_start
endarea third, 1
%if _RELOCEXE
header_size equ header_end - header_start
endarea header, 1
%endif

FIRSTSEGMENT equ _BASESEGMENT + 0
SECONDSEGMENT equ _BASESEGMENT + paras(ORIGIN) + first_size_p
THIRDSEGMENT equ _BASESEGMENT + paras(ORIGIN) + first_size_p + second_size_p
BEHINDSEGMENT equ _BASESEGMENT + paras(ORIGIN) + first_size_p + second_size_p + third_size_p

%define RELOCATIONFROMFIRST ""
%define RELOCATIONFROMSECOND ""
%define RELOCATIONFROMTHIRD ""
        %imacro relocation 0-1.nolist -2
%%reloc equ $ + %1
%ifidn _CURRENT_SECTION, FIRST
 %xdefine RELOCATIONFROMFIRST RELOCATIONFROMFIRST, %%reloc, FIRSTSEGMENT
%elifidn _CURRENT_SECTION, SECOND
 %xdefine RELOCATIONFROMFIRST RELOCATIONFROMFIRST, %%reloc, SECONDSEGMENT
%elifidn _CURRENT_SECTION, THIRD
 %xdefine RELOCATIONFROMFIRST RELOCATIONFROMFIRST, %%reloc, THIRDSEGMENT
%else
 %error Unknown section for relocation
%endif
        %endmacro

        usesection FIRST
start:
        mov dx, cs
        mov ds, dx
%if !_RELOCEXE && _RELOC
        mov si, relocationtable
        mov cx, relocationtable.amount
        jcxz .noreloc
@@:
        lodsw
        xchg bx, ax
        lodsw
        add ax, dx
        mov es, ax
        add word [es:bx], dx
        loop @B
.noreloc:
%endif

displayfirst:
        mov si, firstmsg
        mov bx, 7
        mov ah, 0Eh
        db __TEST_IMM16         ; (skip int 10h)
@@:
        int 10h
        lodsb
        test al, al
        jnz @B

        call SECONDSEGMENT:secondentry
relocation

exit:
%if _DOSEXIT
        mov ax, 4C00h
        int 21h
%else
        xor ax, ax
        int 16h
        int 19h
%endif  


bouncetothird:
        jmp THIRDSEGMENT:bounced
relocation


firstmsg:       asciz "Hello from first!",13,10


        usesection SECOND

secondmsg:      asciz "Hello from second!",13,10

secondentry:
displaysecond:
        mov si, secondmsg
        mov bx, 7
        mov ah, 0Eh
        db __TEST_IMM16         ; (skip int 10h)
@@:
        int 10h
        cs lodsb
        test al, al
        jnz @B

        jmp THIRDSEGMENT:thirdentry
relocation


        usesection THIRD

        align 2, db 0
indirect_to_bounce:
                dw bouncetothird
relocation 0
                dw FIRSTSEGMENT

thirdmsg:       asciz "Hello from third!",13,10

thirdentry:
        jmp far [cs:indirect_to_bounce]

bounced:
displaythird:
        mov si, thirdmsg
        mov bx, 7
        mov ah, 0Eh
        db __TEST_IMM16         ; (skip int 10h)
@@:
        int 10h
        cs lodsb
        test al, al
        jnz @B

        retf


%ifn _RELOCEXE
        usesection FIRST
%else
        usesection HEADER
%endif
%if _RELOCEXE || _RELOC
        align 4, db 0
relocationtable:
.:
        dw RELOCATIONFROMFIRST
        dw RELOCATIONFROMSECOND
        dw RELOCATIONFROMTHIRD
.end:
.amount: equ (.end - .) / 4
%endif
%if _RELOCEXE
        align 16
header_end:
%endif

%if _LARGEFILL
        usesection SECOND
        _fill fromkib(64), 0CCh, second_start
%endif

        usesection FIRST
        align 16
first_end:
        usesection SECOND
        align 16
second_end:
        usesection THIRD
        align 16
third_end:

How does it work?

  • NASM allows to create far jumps and calls with a hardcoded immediate address, in the format jmp SEGMENT:OFFSET (no size keyword, two colon-separated immediate numbers).
  • The relocation macro defaults to creating an equate for $ - 2 (current output address minus 2), which points into the segment immediate of a preceding instruction. This reference is used to make Self-Modifying Code.
  • The macro can be used as relocation 0 to create a relocation table entry for the next word.
  • The relocation entries are emitted into a table. For simplicity this table is here always in the same format as used by the MZ .EXE header.
  • The segment values (FIRSTSEGMENT and so on) are defined using a base segment plus a displacement, in paragraphs, to the particular segment in our program image. The displacements are calculated from the label deltas that give the length of each segment in the program image.
  • The base segment can be nonzero to support loading at a fixed address.

Here are the commands I used to create three different builds of this program:

nasm -I ~/proj/lmacros/ test.asm -o test.com -l testcom.lst -D_DOSENTRY -D_DOSEXIT && dosemu -K "$PWD" -E "ldebug test.com" -dumb -td -kt

Build a flat format .COM file with DOS entry (origin = 256) and DOS termination call. (All I/O is done using the ROM-BIOS interfaces, unconditionally.)

nasm -I ~/proj/lmacros/ test.asm -o test.exe -l testexe.lst -D_DOSENTRY -D_DOSEXIT -D_RELOCEXE -D_LARGEFILL && dosemu -K "$PWD" -E "ldebug test.exe" -dumb -td -kt

Build an MZ .EXE file placing and using the relocation table within the MZ .EXE header. Still use DOS termination call. For fun, fill the SECOND segment to 64 KiB. This requires an MZ .EXE file under DOS, or alternatively what I call a .BIG file. (.BIG files are flat format files similar to .COM files but may exceed 64 KiB and the initial stack is set up in another segment. They're used internally by my debugger's build process.)

nasm -I ~/proj/lmacros/ test.asm -o test.bin -l testbin.lst -D_DOSENTRY=0 -D_RELOC=0 -D_BASESEGMENT=3000h -D_DOSEXIT=0 -D_RELOCEXE=0 -D_LARGEFILL && dosemu -K "$PWD" -E "ldebug /crcsip=3000_0000;lcs:ip /f test.bin" -dumb -td -kt

Build a flat format binary file, no DOS entry, use ROM-BIOS termination calls, and do not relocate at run time. The segment base is instead fixed to a particular address. The debugger command loads the file to that address. (Must be free!) For fun, do also fill the SECOND segment to 64 KiB.