voidstar tech
2021-06-07 05:41:54 UTC
I'm mentioning Alex because I think he wrote an article about all this back in 2004! :)
I've written a C game that targets the 6502 (PET/Apple][) and Z80 (TRS80) and fits within 32K.
I'm now trying to target the x86 16-bit (specifically the original IBM PC 5150), so I don't want any 80186, 80286, or 80386 instructions.
I've had success with Turbo C 2.01 in 86BOX. It wasn't exactly trivial and requires various hoops - but it did work. To summarize what I did:
create a 486 DOS image (86BOX) and installed MS-DOS 6.0, Turbo C 2.0, Turbo Assembler 1.01 (to a 20MB virtual HDD). The main.c example included with Turbo C has comments to describe basically exactly what I wanted to do, so I followed those instructions...
tcc -c -ms main
tasm c0 /D__TINY__ /D__NOFLOAT /t/mx;
tasm setargv /D__TINY__ /t/mx;
tlink c0 main setargv /c/m,main
exe2bin main.exe main.com
del main.exe
BUT, if you know MS-DOS -- DOS 6.0 didn't include EXE2BIN, so where did I get it? I copied it from the last version of DOS that did include EXE2BIN, which was MS-DOS 3.31. That was one of the hoops.
The next challenge was how to get the .COM file on a PC-DOS 1.0 image. That's a lot more hoops, because PC-DOS 1.0 only supports single sided 160KB disk (which WinImage doesn't support). Long story short, I just used PC-DOS 2.10 (which supports double sided disk, so I just copied my .COM file to a 360KB disk image). And voila, my TurboC built .COM runs. Yay!!
My goal is to show that, at least conceptually, my binary could be loaded via cassette on an original IBM PC - I don't want it to dependent on MS-DOS. In fact, I believe I should be able to skip BIOS calls, and just do direct OUT instructions (with some inline assembly). But let's not get distracted with all that just yet. First things first, I need to improve the workflow here...
TurboC means I'm limited to 8.3 filenames. My actual source code that I want to compile is 168KB (long story on why). I learned that TurboC has a limit of 64KB per .c file (a limitation of its editor). I can fix all that - I can rename all my files and #include's, and split the .c into smaller files. But my point is, the code-base as-is is compiling for PET, Apple2, TRS80 (using cc65 and z88dk C compilers) -- I'd like to also include x86 IBM PC into that same code-base (obviously I'm using #define macros where needed across the systems, for specific things like polling keyboard and drawing to the screen). Which is part of the point: to show we have "modern" development environments (runs under Win10) that can target these ancient platforms.
So with all those hoops, my test/debug cycle is really inefficient (compile in 86BOX TurboC DOS 6.0 image, use WinImage to export the binary to the PC-DOS 2.x image, use EXE2BIN {either before or after -- that's another reason to use PC-DOS 2.x, as PC-DOS 1.x didn't include EXE2BIN yet}). Plus also just having to re-arrange all the code (filenames and 64KB chunks).
NOTE1: I haven't explored if there is a way to network my 1981 8086 86BOX with my 1993 80486 86BOX together. That doesn't help the 8.3 filename pain.
NOTE2: I think all my code could fit on a single 360K disk. I'm not sure about ALSO fitting all the .obj and final linked program. But if so, then I could share the same floppy image between the "PC-DOS 2.10" machine and the "PC-DOS 6.0" machine that I'm doing the development on (just flip it back and forth). Again, still doesn't help the 8.3 filename pain.
So - I looked for a native Windows 10 compiler that could target 16-bit x86. WATCOM C did compile all my code, that was wonderful - but the .COM file it produced didn't actually work. When that failed, I went down the TurboC path just to verify that it would work -- and it does. I'm not 100% certain if WATCOM C 1.9 is producing 8086/8088 code or not (I'm just using its IDE, haven't scrutinized all the command line options yet).
I tried TurboC 3.X -- that at least adds mouse support and Search, making it a little easier to move around within the 86BOX emulator. But it still doesn't support long filenames (it basically old-school TurboC auto-packaged into an 86BOX).
TinyCC I don't think supports 16-bit x86.
I've tried DigitalMars - which seems like it should have worked (I used "-0 -mt" for 8088 and TINY .COM). DigitalMars includes an EXE2BIN, but it's a 16-bit build that won't run under 64-bit Windows. NOTE: I explored the public MS DOS 6.0 source code and noticed EXE2BIN.ASM source is there -- it is a format conversion tool with no real reason that it needs to built as a 16-bit application, so it seems to me someone should essentially be able to make a C version of the adjustments EXE2BIN is doing to an EXE, and make a 64-bit build of that). ANYWAY, I ran the MS-DOS version of EXE2BIN on the .EXE that DigitalMars produced - that ran and produced a .COM, but it didn't run (under DOS 3.X or DOS 2.X, or 6.X for that matter). So I'm not sure what went wrong there. Basically this is the same as the WATCOM C experience - they both produced a final .COM that just didn't run under the actual 8086 IBM PC. [ but TurboC did, so I know it's possible ]
I don't think C++Builder (i.e. anything past TurboC 3.X) will target 16-bit x86 either.
The next thing I'm going to try is to go to an older version of WATCOM C (1.6) and see if I get any luck there. I've only been exploring this for a day now -- my Apple2 and Z80 TRS80 ports only took a couple days. I'm surprised how much of a hassle real-mode/16-bit x86 support turning out to be (in C). (but I also get that there isn't much reason for it - but let me tell you what my inspiration is: the DOS port of Montezuma’s Revenge, which was a .COM program that could also have technically been loaded from a cassette tape - except I want my game compiled from the C code, not hand-assembled).
If anyone has any other suggestions - TurboC 2.0 is my fallback, but I'd really rather something that can work with my existing long filenames, target 8086/8088, TINY .COM target, and I'd like to do some PORT/OUT calls (inline assembly) and/or do interrupt calls (WATCOM 1.9 has IN/OUT calls in conio.h, and both of them have a version of int86 -- either in i86.h or dos.h; but I think it'd be nice to be able to just inline my own OUT calls for the very few things that my game does).
And if curious, the game (and the PET/Apple2/C64/TRS80 binaries) are at www.destinyhunter.org (and the source is also on github, if anyone else wants to take a crack on an x86 port).
Thanks!
SteveL
I've written a C game that targets the 6502 (PET/Apple][) and Z80 (TRS80) and fits within 32K.
I'm now trying to target the x86 16-bit (specifically the original IBM PC 5150), so I don't want any 80186, 80286, or 80386 instructions.
I've had success with Turbo C 2.01 in 86BOX. It wasn't exactly trivial and requires various hoops - but it did work. To summarize what I did:
create a 486 DOS image (86BOX) and installed MS-DOS 6.0, Turbo C 2.0, Turbo Assembler 1.01 (to a 20MB virtual HDD). The main.c example included with Turbo C has comments to describe basically exactly what I wanted to do, so I followed those instructions...
tcc -c -ms main
tasm c0 /D__TINY__ /D__NOFLOAT /t/mx;
tasm setargv /D__TINY__ /t/mx;
tlink c0 main setargv /c/m,main
exe2bin main.exe main.com
del main.exe
BUT, if you know MS-DOS -- DOS 6.0 didn't include EXE2BIN, so where did I get it? I copied it from the last version of DOS that did include EXE2BIN, which was MS-DOS 3.31. That was one of the hoops.
The next challenge was how to get the .COM file on a PC-DOS 1.0 image. That's a lot more hoops, because PC-DOS 1.0 only supports single sided 160KB disk (which WinImage doesn't support). Long story short, I just used PC-DOS 2.10 (which supports double sided disk, so I just copied my .COM file to a 360KB disk image). And voila, my TurboC built .COM runs. Yay!!
My goal is to show that, at least conceptually, my binary could be loaded via cassette on an original IBM PC - I don't want it to dependent on MS-DOS. In fact, I believe I should be able to skip BIOS calls, and just do direct OUT instructions (with some inline assembly). But let's not get distracted with all that just yet. First things first, I need to improve the workflow here...
TurboC means I'm limited to 8.3 filenames. My actual source code that I want to compile is 168KB (long story on why). I learned that TurboC has a limit of 64KB per .c file (a limitation of its editor). I can fix all that - I can rename all my files and #include's, and split the .c into smaller files. But my point is, the code-base as-is is compiling for PET, Apple2, TRS80 (using cc65 and z88dk C compilers) -- I'd like to also include x86 IBM PC into that same code-base (obviously I'm using #define macros where needed across the systems, for specific things like polling keyboard and drawing to the screen). Which is part of the point: to show we have "modern" development environments (runs under Win10) that can target these ancient platforms.
So with all those hoops, my test/debug cycle is really inefficient (compile in 86BOX TurboC DOS 6.0 image, use WinImage to export the binary to the PC-DOS 2.x image, use EXE2BIN {either before or after -- that's another reason to use PC-DOS 2.x, as PC-DOS 1.x didn't include EXE2BIN yet}). Plus also just having to re-arrange all the code (filenames and 64KB chunks).
NOTE1: I haven't explored if there is a way to network my 1981 8086 86BOX with my 1993 80486 86BOX together. That doesn't help the 8.3 filename pain.
NOTE2: I think all my code could fit on a single 360K disk. I'm not sure about ALSO fitting all the .obj and final linked program. But if so, then I could share the same floppy image between the "PC-DOS 2.10" machine and the "PC-DOS 6.0" machine that I'm doing the development on (just flip it back and forth). Again, still doesn't help the 8.3 filename pain.
So - I looked for a native Windows 10 compiler that could target 16-bit x86. WATCOM C did compile all my code, that was wonderful - but the .COM file it produced didn't actually work. When that failed, I went down the TurboC path just to verify that it would work -- and it does. I'm not 100% certain if WATCOM C 1.9 is producing 8086/8088 code or not (I'm just using its IDE, haven't scrutinized all the command line options yet).
I tried TurboC 3.X -- that at least adds mouse support and Search, making it a little easier to move around within the 86BOX emulator. But it still doesn't support long filenames (it basically old-school TurboC auto-packaged into an 86BOX).
TinyCC I don't think supports 16-bit x86.
I've tried DigitalMars - which seems like it should have worked (I used "-0 -mt" for 8088 and TINY .COM). DigitalMars includes an EXE2BIN, but it's a 16-bit build that won't run under 64-bit Windows. NOTE: I explored the public MS DOS 6.0 source code and noticed EXE2BIN.ASM source is there -- it is a format conversion tool with no real reason that it needs to built as a 16-bit application, so it seems to me someone should essentially be able to make a C version of the adjustments EXE2BIN is doing to an EXE, and make a 64-bit build of that). ANYWAY, I ran the MS-DOS version of EXE2BIN on the .EXE that DigitalMars produced - that ran and produced a .COM, but it didn't run (under DOS 3.X or DOS 2.X, or 6.X for that matter). So I'm not sure what went wrong there. Basically this is the same as the WATCOM C experience - they both produced a final .COM that just didn't run under the actual 8086 IBM PC. [ but TurboC did, so I know it's possible ]
I don't think C++Builder (i.e. anything past TurboC 3.X) will target 16-bit x86 either.
The next thing I'm going to try is to go to an older version of WATCOM C (1.6) and see if I get any luck there. I've only been exploring this for a day now -- my Apple2 and Z80 TRS80 ports only took a couple days. I'm surprised how much of a hassle real-mode/16-bit x86 support turning out to be (in C). (but I also get that there isn't much reason for it - but let me tell you what my inspiration is: the DOS port of Montezuma’s Revenge, which was a .COM program that could also have technically been loaded from a cassette tape - except I want my game compiled from the C code, not hand-assembled).
If anyone has any other suggestions - TurboC 2.0 is my fallback, but I'd really rather something that can work with my existing long filenames, target 8086/8088, TINY .COM target, and I'd like to do some PORT/OUT calls (inline assembly) and/or do interrupt calls (WATCOM 1.9 has IN/OUT calls in conio.h, and both of them have a version of int86 -- either in i86.h or dos.h; but I think it'd be nice to be able to just inline my own OUT calls for the very few things that my game does).
And if curious, the game (and the PET/Apple2/C64/TRS80 binaries) are at www.destinyhunter.org (and the source is also on github, if anyone else wants to take a crack on an x86 port).
Thanks!
SteveL