"Hello World" crashes on i8086/msdos when compiled with every model except Huge.

Summary

Trying to build the simplest possible "Hello World" example with i8086/msdos target using every memory model produced crashing binaries, except -WmHuge.

System Information

Steps to reproduce

I created a test script which produces binaries replicating the reported issue:

#!/usr/bin/env bash

cat << EOF >hello.pas
program hello;

begin
  System.WriteLn('Hello World!');
end.
EOF

rm -rf "out/" && mkdir -p "out/"

for model in "small" "tiny" "medium" "compact" "large" "huge" ; do
  url_suffix="-$model"
  if [ "$model" == "small" ]; then
    url_suffix="" # consistency on CDN could be better, duh
  fi

  rm -rf "out-$model/" "fpc-$model/"
  mkdir -p "fpc-$model/"
  mkdir -p "out-$model/"
  cd "fpc-$model/"

    echo "Downloading FPC for model: $model"

    wget -q "https://downloads.freepascal.org/fpc/snapshot/trunk/i8086-msdos/fpc-3.3.1.msdos.built.on.x86_64-linux${url_suffix}.tar.gz"

    tar xf fpc-*.tar.gz
    rm fpc-*.tar.gz

    echo "Building for model: $model"

    ./bin/x86_64-linux/ppcross8086 \
    -XX -Tmsdos -Mtp -Wm${model} \
    -Fu'./units/i8086-msdos/*' \
    -Fu'./units/msdos/*' \
    -o../out-$model/hello.exe \
    ../hello.pas

    if [ ! -e ../out-$model/hello.exe ]; then
      echo "Building with memory model $model failed to produce an executable."
    else
      mv ../out-$model/hello.exe ../out/$model.exe
    fi
    rm -rf ../out-$model/

    echo

  cd ..
done

echo "Done. Try running 'dosbox-x --debug ./out/hello.<model>.exe' to check each executable manually."

What is the current bug behavior?

These vary between models:

  • Tiny: Compiler throws Internal error 202102001
  • Compact: Does nothing and exits to DOS
  • Small: Most spectacular - long beep and puts lots of trashed random ASCII chars on screen, then exits.
  • Medium: Throws INT 6h (Invalid Opcode) in constant loop, reported by DOSBox-X stderr
  • Large: Same as Medium
  • Huge: Just works, prints Hello World! and exits.

What is the expected (correct) behavior?

Just print Hello World! and exit the program.

Relevant logs and/or screenshots

There are not many, -va on Tiny doesn't print anything suspicious, the full error line is: hello.pas(5,1) Fatal: Internal error 202102001

Possible fixes

As I've encountered this during my Mario & Luigi TP->FPC porting project I found various issues which might be related to data/segments aligning or pointer arithmetic. Seems like every of them has the same root cause, including this. But for the sake of clarity, I wanted to minimize it down to reproducible legacy-free example.

Who would expect that example would be a "Hello World"?

On the other hand, using System.WriteLn() in the game mentioned above seems to work (before i initialize VGA, of course) as I was using it for "print debugging" on initial stage.