[fpc-devel] State of i8086-embedded

nickysn at gmail.com nickysn at gmail.com
Tue Jul 2 22:51:28 CEST 2019


On Mon, 2019-07-01 at 21:52 +0200, Sven Barth via fpc-devel wrote:
> Hello together!
> 
> For a project at work I need to run 16-bit x86 code on a bare x86 
> hardware (more precisely a 8086 VM). Considering that i8086-embedded
> had 
> been added to FPC nearly exactly 3 years ago I though I'd give it a
> try 
> and cross compiled the units for the tiny memory model. However when
> I 
> tried to compile a test program with -Wtcom (cause I've read that
> I'd 
> need to use exe2bin to convert the resulting binary) I get the
> following 
> output:
> 
> === output begin ===
> 
> PS C:\fpc\git> .\compiler\ppcross8086.exe -n -Furtl\units\i8086-
> embedded 
> -viwn -FEtestoutput -Tembedded -CX -XXs -Wmtiny
>   -Wtcom .\fpctests\tembedtest.pp
> Target OS: Embedded
> Compiling .\fpctests\tembedtest.pp
> Linking testoutput\tembedtest.com
> tembedtest.pp(7,1) Warning: Object prt0t.o not found, Linking may
> fail !
> tembedtest.pp(7,1) Warning: Object prt0t.o not found, Linking may
> fail !
> tembedtest.pp(7,1) Error: Can't open object file: prt0t.o
> tembedtest.pp(7,1) Fatal: There were 1 errors compiling module,
> stopping
> Fatal: Compilation aborted
> 
> === output end ===
> 
> Well, prt0t indeed does not exist for embedded (neither source nor 
> object file) and looking at the code of $fpcdir/rtl/msdos/prt0t.asm
> (or 
> more precisely prt0comn.asm) that one seems to be quite a bit
> dependant 
> on DOS.
> 
> So what's the state of i8086-embedded? All I need is doing some 
> calculation code and executing some interrupts (thus I hope that I
> won't 
> need to load the resulting program at address 0, thus overwriting
> the 
> interrupt table ;) ). And of course I'll need to know how I'll need
> to 
> setup the VM's state before jumping into the entry code of the
> program. ;)

I made i8086-embedded for the purpose of writing things like 16-bit MS-
DOS drivers (.sys files), BIOS code, and especially, my planned 16-bit
MS-DOS compatible OS kernel, which hasn't really been started yet, but
eventually I'm planning to make:

https://sourceforge.net/projects/fpcdos/

But it's all future plans and it's not working yet, so actually, i8086-
embedded hasn't been used for anything yet and might need a few tweaks
to get it working :) Having said that, the startup code is
intentionally omitted, because I expected the user to provide that. The
reason is that 16-bit code is a little bit tricky and there are things
you cannot do with FPC yet, so writing an asm startup code is required.
You can probably take the msdos and win16 startup code as a starting
point, but they contain too much dos/win16 specific things, that aren't
needed. But feel free to gut them and leave only the basics. The only
thing you actually need is to provide an entry point (the "..start"
label in nasm) and emit a jump to PASCALMAIN. But, depending on your
use case, you need to setup a stack as well (i.e. initialize SS and
SP).

Highly recommended reading:

https://www.nasm.us/doc/nasmdoc7.html#section-7.4

Pay special attention to the "segment class" and the "group" directive
and make sure you understand how they work. Make sure you use the same
segments and groups like the FPC emitted code for the specific memory
model. It is these segment names, classes and groups that make each of
the memory model to be what it is (in other words, it simply tells the
linker which parts of the program must be combined in a single segment,
and which parts can span multiple segments). Feel free to use
OpenWatcom's "dmpobj" tool to dump FPC's emitted object files, as well
as create linker map files (FPC -Xm) to see what's going on. FPC uses
only one group "DGROUP" to specify segments/sections that are in the
default data segment, pointed to by DS. In the tiny memory model, all
segments are in DGROUP. These things change according to the memory
model, however, and the startup code needs to reflect that. The MS-DOS
startup code already does, so you should be able to reuse its SEGMENT
and GROUP declarations.

Regarding whether using startup offset 0 would overwrite the interrupt
descriptor table - if you use the tiny memory model, this won't
necessarily happen, because tiny model code is inherently position
independent and thus can be loaded at any segment, and it will work,
because it only uses near pointers for everything and everything
(code+data+stack+heap) is in the same segment. In fact DOS does exactly
that with COM files - it loads them at any segment that is available.
They start at offset $100, but that's because puts a special structure,
called PSP at offset 0:

https://en.wikipedia.org/wiki/Program_Segment_Prefix

So, the PSP would overwrite the IDT (the real mode interrupt table), if
it wasn't for DOS loading .COM files at segments different than 0. 

Basically, the tiny memory model is true PIC code, i8086 style :) The
only downside is that all code and data is limited to 64kb :) If it
wasn't for this minor detail, it would be perfect :)

.EXE files can also be loaded at any segment, but they aren't position
independent, since they contain relocations. The relocations are
applied by the MS-DOS .exe loader. Here's all there is to it in a
nutshell (point 9 explains the actual relocation algorithm):

http://www.techhelpmanual.com/354-exe_file_header_layout.html

It's trivially easy to write a program that takes an .exe file and
relocates it and converts it to a flat binary that works at a fixed
address. That would probably be very helpful for i8086-embedded use
(e.g. for a bootloader that is loaded at a fixed starting segment), but
I still haven't found the time to write it :)

Overall, i8086 is tricky, but also a lot of fun :) I suggest you take
the msdos startup code, leave the segment/class/group declarations, but
remove all the DOS-specific code, just leave the jmp to PASCALMAIN. But
make sure that the segment registers (e.g. for tiny model DS=CS is
required) and the stack pointer (SS:SP) are initialized (in the tiny
model SS must be the same as CS and DS), before you jump to PASCALMAIN.
If you can't provide the SS=DS=CS conditions, then you probably need to
use a different memory model and maybe a tool for relocating and
flattening an MZ .exe image, since you can't make .COM files in the
other memory models.

Make sure you try using the external linker (OpenWatcom's wlink,
invoked by FPC -Xe), because you might encounter bugs with the internal
linker if you use segment classes and groups that aren't exactly like
in DOS. In fact, I don't even know if the internal linker is enabled
for i8086-embedded, but it might be. :) In general, the internal linker
should also work, but it might need a few minor tweaks and bug fixes
for i8086-embedded, and these could give you major headaches to find
out, when you're just starting out with 16-bit code and struggling to
find out why your code mysteriously crashes. :)

Hope this helps to get you started and feel free to ask if you have any
further questions. :)

Best regards,
Nikolay



More information about the fpc-devel mailing list