VOGONS


First post, by orac81

User metadata
Rank Newbie
Rank
Newbie

I have been posting/releasing the source to a few old DOS games here, and I had a thought: Would it be possible to cross-compile a DOS program written in C that directly accesses VGA hardware to a true WIN32/64 executable that runs at full speed on modern PCs?
DOSBOX emulation is great, and safe too, and most old programs would run too fast, or the source is not available anyway.
I was thinking of programs that could benefit from such speed, computational programs (chess etc), 3d shooters, etc. My own example is DYNAMO DRAUGHTS which runs very slow in emulation.

https://github.com/orac81/Orac-Draughts

Would such a conversion utility be more than just a library for a C compiler? You would need to trap inport/outport statements, memory read/write to $0A000, far/huge pointer arithmetic/kludges, and much more besides.

Any thoughts? This is just speculation on my part at this stage..

(btw i post this here, i didnt see a general "programming" section, please move if wrong..)

https://github.com/orac81

Reply 1 of 11, by jmarsh

User metadata
Rank Oldbie
Rank
Oldbie

Why though, when you could just use DOSBox? It already has the recompiler part built in, and anything that was originally written for DOS is going to be blazing fast in comparison on current CPUs even with the emulation layer.

Reply 2 of 11, by orac81

User metadata
Rank Newbie
Rank
Newbie

Well, for one reason - speed.

The old 8086 code, was compiled for the few 16 bit regs of the 8086, with a not very optimal compiler (ie turbo C) against recompiling with modern optimization and register sets, etc.

Another advantage would be reduced CPU need (battery life , heat etc) even for apps that dont need to be faster.

Dont get me wrong, DOSBOX is great, and provides important safety features too, but it can never quite reach native code speeds.

Thinking some more, C++ can add subroutine code traps for object classes, so that if "far" or "huge" were defined as classes, could memory read/writes through those objects trap references to vga memory ranges? Also ints need to be 16 bit shorts, BIOS calls trapped (int 10 etc). "sleep" statements might be needed for keyboard/mouse input loop, tuned for the speed needs of the app.

Some common libraries could be emulated too, ie Turbo C BGI..

You can imagine just a C++ library doing most of the hard work, with just a few tweaks to the source.

(I understand QB64 does some of this for qbasic code.)

https://github.com/orac81

Reply 3 of 11, by gerry

User metadata
Rank Oldbie
Rank
Oldbie
orac81 wrote on 2024-09-16, 12:34:

I have been posting/releasing the source to a few old DOS games here, and I had a thought: Would it be possible to cross-compile a DOS program written in C that directly accesses VGA hardware to a true WIN32/64 executable that runs at full speed on modern PCs?

only if that compiler were able to capture and translate those hardware access calls and translate them reliably to suitable win32 api calls, not always the easiest thing to do.

i don't know of one that does this out of the box - but i'm sure there would be have been tools at the time that tried to help out

as in your later reply, most games back then used libraries for graphics / sound that acted as a layer between hardware and toolset

if that library was itself rewritten with win32 calls, each function having the same arguments then you could compile the same code to both targets more readily, a bit like allegro did

as for speed, i'd hope even DOS code mapped with several layers and try..catches and so forth to run fast enough on a modern machine without imposing much in the way of 'inefficiency'

Reply 4 of 11, by jmarsh

User metadata
Rank Oldbie
Rank
Oldbie
orac81 wrote on 2024-09-16, 13:48:

Well, for one reason - speed.

The old 8086 code, was compiled for the few 16 bit regs of the 8086, with a not very optimal compiler (ie turbo C) against recompiling with modern optimization and register sets, etc.

Which DOSBox already does - taking advantage of the extra registers available on the host CPU.

Another advantage would be reduced CPU need (battery life , heat etc) even for apps that dont need to be faster.

I don't see how that would be the case - if the original code had no busy loops, the recompiled code also would max out a cpu core. Else it simply wouldn't be making the most of the resources available.

Dont get me wrong, DOSBOX is great, and provides important safety features too, but it can never quite reach native code speeds.

It doesn't need to when the "native code" only runs on dinosaur CPUs.
Any code that was written targetting 16-bit will have a tremendous increase in speed when executed on a modern CPU using DOSBox's x86 recompiler. If you went to the effort of plugging the source code into a modern compiler, then crippled it to be 16-bit compatible, you might end up with something a tiny bit faster. You also might end up with something slower due to all the extra cruft (implementing segment registers in software is sloooow).

Thinking some more, C++ can add subroutine code traps for object classes, so that if "far" or "huge" were defined as classes, could memory read/writes through those objects trap references to vga memory ranges? Also ints need to be 16 bit shorts, BIOS calls trapped (int 10 etc). "sleep" statements might be needed for keyboard/mouse input loop, tuned for the speed needs of the app.

Some common libraries could be emulated too, ie Turbo C BGI..

You can imagine just a C++ library doing most of the hard work, with just a few tweaks to the source.

But then, after you've spent X amount of time doing all that... you will find you've basically reinvented DOSBox.

Reply 5 of 11, by DaveDDS

User metadata
Rank Oldbie
Rank
Oldbie

I doubt this will help directly, but perhaps it will give you some ideas to
hear how I solved the DOS->Win64 problem:

-- History --

In the early 80's I had upgraded from a home build 8080 machine to an Altair.
I was working with the 6809, and liked it enough that I had build a 6809 based
system. I was big into assembly language programming on these. About this time
I learned about 'C' and liked the idea of being able to do/access fairly low-
level things while not having to keep track of a myriad of "little details".

AND ... the 6809 looked like it would be a good candidate for C.
In mid-80s I wrote my own C compiler (Micro-C), initially targeting the 6809
and the 8080 shortly after. Over the next few years I wrote "code generators"
and low-level libraries for about a dozen other processor families for various
systems I was building/playing with. Micro-C proved quite popular with embedded
developers and I formed DDS and made a decent living selling my tools.

These were all very small system architectures and my compiler was designed
"from the ground up" as 16-bit.

As I moved into PCs, I implemented Micro-C for the 80x86/PC, including fairly
substantial libraries, and it became my toolset of choice on that platform!

I've always been fairly "prolific" and over the years I've written hundreds
of applications, tools and "just for fun" stuff - just scanned my system and
found: 3436.C files and 1389.ASM files - I rarely use sources by others and
almost all of this is stuff I'd written and use under DOS.

-- The problem --

Along came "Winblows" - at first not a problem as Windows had decent DOS
emulation and ran 16-bit code quite well.

But.. when Windows moved to 64-bit, 16-bit code was no longer supported.

I do use DosBox quite a bit - I have my own older/smaller edition with some
fundamental bugs fixed... but it still presents a couple of problems to an
end user of my tools:

- It doesn't provide direct access to host system, files etc.

- Its big and doesn't completely clean up after itself (leaving things in
places like appdata)

As I often write and give away simple programs/tools for others to use, it's
a "big ask" to require them to learn how to install/remove (and clean up after)
DosBox, not to mention learning how to "mount" virtual drives to provide access
to things they might want to access with my tools.

-- My solution --

In late 2019 I was laid up due to serious injury and the "covid" pandemic had
pretty much stopped "most everything" - so I had a LOT of time on my hands!

I created DVM (Dunfield Virtual Machine) which is a single small (17k) .EXE
which doesn't need to be installed, does't require "support files" and can run
most of my DOS programs directly on the host OS requiring only recompilation.
I did versions for Windows and Linux - and presumably anyone who wanted it
elsewhere could port it to that platform).

DVM provides pretty decent DOS emulation as it implements the (substantial)
capabilities for my Micro-C/PC library. It runs it's applications just as if
they were running natively on the host (full file access etc). What it doesn't
do is emulate an 80x86 processor. Some years ago I "invented" my own CPU called
"C-FLEA" which was a fairly ideal target for my compiler. I created it so that
you could use Micro-C on tiny architectures which were not really suitable as
a C target (eg: no stack).

Unlike most CPUs which have many internal registers and flags, C-FLEA is much
simpler to implement virtually - the CPU in DVM is implemented in about 200
lines of C (which doesn't use libraries).

DVM doesn't provide a lot of hardware emulation, which is the main reason it
wouldn't been suitable for games etc. ... it does give a pretty good 80x25
text screen emulation - which is what most of my tools needed!

Dave ::: https://dunfield.themindfactory.com ::: "Daves Old Computers"->Personal

Dave ::: https://dunfield.themindfactory.com ::: "Daves Old Computers"->Personal

Reply 6 of 11, by DaveDDS

User metadata
Rank Oldbie
Rank
Oldbie
DaveDDS wrote on 2024-09-16, 17:30:

In the early 80's I had upgraded from a home build 8080 machine to an Altair.
I was working with the 6809, and liked it enough that I had build a 6809 based
system.

Btw, just in case anyone reading this is actually interested in such ancient systems..

You can experience actually booting and using the above mentioned Altair and homebuilt
6809 systems!

At one time I wrote accurate emulations of my exact systems (complete with most of my
disks) and they are published on my site - These are DOS programs but they run well in
DosBox!

Dave ::: https://dunfield.themindfactory.com ::: "Daves Old Computers"->Personal

Dave ::: https://dunfield.themindfactory.com ::: "Daves Old Computers"->Personal

Reply 7 of 11, by orac81

User metadata
Rank Newbie
Rank
Newbie

Well, thats really good, thanks everyone for all the ideas!

I think i did try davids microC a long time back, you also did some sort of custom boot system replacing dos, if i recall, which was interesting.

As for DOSBOX, well ok, as I said its very good, it does such a wide range of complex emulation, and for cases where there is no source its the best way to go.
But the "out of the box" default interpreter mode was much slower compared to DOS on the same machine, when i first tried DYNAMO. On some fixed searches (same node count) over 50x slower!
When I first enabled dynamic core DYNAMO threw an error, which might be something in my setup (older version wine/linux). I need to do some more testing, its probably something i did wrong.

But i did want something that compiled DYNAMO to a simple win32 native binary..

So, I think I may have a go at this sometime.

I will call the project DOS2WIN32.

It would target win32 api to start, since i have written a fair bit for it, and it can run under wine for all other PC systems. But it should be possible to extend the same API to compile for MacOS, Linux, etc.

It would be a library with routines starting dos_.. or macros DOS_..
It would be possible to set TARGET_SYSTEM to WIN32 or DOS, conditionally compiling as appropriate.

Macros such as USE_VGA_PLANAR, USE_VGA_MODE13, USE_CGA, USE_MOUSE etc could be set to selectively enable and compile the parts of the library needed for your code, trapping int86 calls and video memory writes.
To start I will implement the bits I need for my stuff, but in a way that is extendable.

dos_init() can malloc 256k for the emulated vga output. It can setup a server thread (running at user set refresh rate) that starts up a window (ie 640x480), and tracks/updates/refreshes vdu redrawing.

A wrapper DOS_VPTR(ptr) will add an offset to ptr to point to the ram to be used, and could trap writes for updating the right area of the screen.

DOS_INP(), DOS_OUTP(), dos_int86() etc, can mirror those functions accordingly.

Now if the compiler can trap the original DOS code syntax, or privide runtime support (mmap etc) it would do so and pass the calls to the above routines. Otherwise the user has to search/replace and modify the source (in the long term, the best thing to do).

But importantly the code will still compile the same way for DOS, under whatever original DOS C compiler (Turbo C, MSVC 1.52, Watcom etc) simply by setting the target macro to DOS.

Does this all sound like a good idea?

And to start it all off, a link:
https://github.com/orac81/miniapples/raw/main/dos2win32.zip

(just a stub with this text for now)

https://github.com/orac81

Reply 8 of 11, by stsp

User metadata
Rank Newbie
Rank
Newbie
orac81 wrote on 2024-09-17, 12:26:

Does this all sound like a good idea?

This sounds like an implemented idea. dj64dev
allows you to build djgpp-buildable DOS programs for
64bit platforms, while using dosemu2 (via its plugin
system) to handle screen output, port I/O and so on.
Its not a native app after all, as you need to run it via
dosemu2, but it runs natively in 64bits, so its very
close to be a native app. It can even be a native ELF
file.
There is a tool
to make such things look complately like a native
app, but of course it still execs dosemu2 internally.

Now if you are interested only in realmode DOS apps
(djgpp-buildable apps are initially prot-mode oriented),
then such experiment was also done with dosemu2: I built
freedos sources for 64bits and hooked that as a dosemu2
plugin, which is what now called fdpp.
But, unlike with dj64dev, realmode apps took too
much penalties for frequent bios calls or far pointer
dereferencing, so the end results were not faster
than the original freedos, and I decided that turning
this into a "toolchain" would be a waste of time.
Basically, while dj64dev provides a very thin layer
between the native code and emulator, fdpp inserts
yet another emulation layer in between to handle
FAR pointers and other oddities in 64bits, so it can
hardly be called a "native app".

So overall, there are tools that do mostly what you
say. But whether they match your desired native/emulation
balance, is a different question. And in particular
for realmode-only apps I've found this not worth
an efforts as the results are not faster than their
16bit counterparts.

Reply 9 of 11, by jakethompson1

User metadata
Rank Oldbie
Rank
Oldbie

I think the cleanest way to do this would be to just produce a PE EXE, and use one of those DOS extenders that supports a subset of the Win32 API.
Then, abstract your screen I/O stuff to a DLL, and have two versions of the DLL: one that does direct hardware I/O, and another that does the equivalent on Win32. You conditionally load the correct one when you start up depending on the environment.

Reply 10 of 11, by stsp

User metadata
Rank Newbie
Rank
Newbie
jakethompson1 wrote on 2025-01-16, 19:45:

I think the cleanest way to do this would be to just produce a PE EXE, and use one of those DOS extenders that supports a subset of the Win32 API.
Then, abstract your screen I/O stuff to a DLL, and have two versions of the DLL: one that does direct hardware I/O, and another that does the equivalent on Win32. You conditionally load the correct one when you start up depending on the environment.

That would be limited to 32bits,
and for 32bits other solutions
(like djgpp) already exist.
When you want native app, you
need to consider not only 32bit/x86,
but also x86_64, aarch64 and other
64bit systems.

Reply 11 of 11, by stsp

User metadata
Rank Newbie
Rank
Newbie

This thread actually motivated me
to try to come up with the full-blown
solution and compile into the native
64bit ELF files.
It took quite a few efforts to code up,
but the result is here:
https://github.com/stsp/dj64dev-host-addon
And the introductory video is here:
https://www.patreon.com/posts/dj64dev-host-123968591

In short, there is a host add-on
for dj64dev now, which allows
to compile the weirdest DOS code
for the host platform natively.
In the above video I compile the
example that install the custom
mouse handler with int33h using
the DPMI realmode callbacks.
Obviously this is absolutely unportable,
and yet it works on host.