That's right, there has been a GNU C compiler (gcc-ia16) that generates code for the 16 bit x86 CPUs for quite a few years now, but it's due to the fork by TK Chia that much progress to a usable tool has been made in recent years. It's already in use by various projects, including the ELKS project.
Why would one use gcc-ia16 in preference to other existing free compilers such as Turbo-C (running under DOS, but you could run it in a VM) and Open Watcom, also enjoying a revival in interest? Well with gcc you get the prospect of compliance with a more up to date C standard, plus the possibility of someday compiling C++.
Why am I even tangling with this? I haven't had any 16-bit PCs for I forget how many years. Not even 32-bit only PCs, they are all 64-bit capable. I do have some 8088 chips and might get a round tuit making a SBC board for them. The real reason is I'm bored as I temporarily don't have access to my hardware toys. So I decided to see if I could install gcc-ia16 on my OpenSUSE Linux system.
Now that's definitely possible, because one can compile gcc-ia16 from source. But I'm lazy and decided to see if could adapt the Ubuntu packages, which are in DEB format. I remembered there is a utility called alien, which can convert between various package formats.
First of all alien is not in the official OpenSUSE repos, so I had to go to software.opensuse.org to find a contributed package. That went ok.
Next, I downloaded the DEB packages from the Ubuntu PPA for gcc-ia16. There are heaps of packages there, including versions for 3 Ubuntu releases. Cut to the chase, these are the ones that are needed: gcc-ia16-elf, binutils-ia16-elf, libi86-ia16-elf, and libnewlib-ia16-elf.
So I ran alien on one like this
sudo alien -v -k -r gcc-ia16-elf_6.3.0-20230219.07-ppa230219074~jammy_amd64.deb
Alien needs to be run as root or it will not be able to assign the correct ownership to the files in the resulting package. The -v shows progress messages, -k preserves the version number, and -r means the output should be a RPM package. Basically alien unpacks the DEB and reassembles it as a RPM. But it also checks the runtime dependencies, such as libraries. Despite being a complex tool, a command line driven compiler usually doesn't need much more than standard C libraries.
First problem, alien couldn't handle the Zstd compression of elements of the DEB. Zstd is a new-fangled compression scheme used in Ubuntu DEBs which replaces the old-fangled Gzip scheme.
I need a newer alien but when I tried to install that, it turns out my Perl isn't recent enough. Grr.
Ok, try another tack. Unpack the DEB, which is actually just a Linux ar archive, and convert the .zst files to .gz files and repack. That worked.
Oops, now it complains that the glibc version that I have on my system, 2.31, isn't recent enough for this application which wanted 2.32. Ok, instead of using the Jammy (Ubuntu 22.04) package, I'll drop back to the Focal (Ubuntu 20.04) package.
sudo alien -v -k -r gcc-ia16-elf_6.3.0-20230219.07-ppa230219074~focal_amd64.deb
And that converted ok.
One of the packages wanted libisl22 when I only had libisl15. This turned out to be the Integer Set Library originally from INRIA. Compilers have to manipulate sets at points during their operation. It turned out that the package comprises a dynamically loaded library and a symbolic link. And nothing required libisl15 so it would be quite safe to install libisl22 side by side and not worry about another application loading the wrong version.
After I converted all the packages just because I'm a bit obsessive, I changed the ownership of the resulting RPMs from root to myself, and made their mtimes the same as the corresponding DEB packages.
Now, would the RPM packages install correctly? I tried:
sudo zypper in gcc-ia16-elf-6.3.0_20230219.07-ppa230219074~focal.x86_64.rpm
Notice I used zypper and not rpm, because zypper will detect any dependencies and satisfy them.
One other thing I had to do was accept that the RPM packages I was installing had no signing key. They were homebrew so that's expected.
That worked. So with the packages installed, time to try a small program. By default, the compiler generates a DOS COM executable.
ia16-elf-gcc -o hello.com hello.c
dosbox hello.com
And that worked. Curious about the generated code, I did:
ia16-elf-gcc -S hello.c
and the resulting hello.s was:
.arch i8086,jumps .code16 .att_syntax prefix #NO_APP .section .rodata .LC0: .string "Hello world" .text .global main .type main, @function main: pushw %bp movw %sp, %bp movw $.LC0, %ax pushw %ax pushw %ss popw %ds call puts addw $2, %sp movw $0, %ax movw %ax, %ax movw %ax, %ax movw %bp, %sp popw %bp pushw %ss popw %ds ret .size main, .-main .ident "GCC: (GNU) 6.3.0"
Ah not so good code, for example the 2 redundant movw %ax,%ax stand out like a sore thumb. Try again with -O specified, and I get:
.arch i8086,jumps .code16 .att_syntax prefix #NO_APP .section .rodata.str1.1,"aMS",@progbits,1 .LC0: .string "Hello world" .text .global main .type main, @function main: pushw %bp movw %sp, %bp movw $.LC0, %ax pushw %ax call puts addw $2, %sp movw $0, %ax movw %bp, %sp popw %bp ret .size main, .-main .ident "GCC: (GNU) 6.3.0"
Much better. The other interesting thing is that the compiler seems to have detected that printf was used without any formatting directives, so changed the function to puts.
But I don't want to generate DOS COM executables. I would like someday to write code for a 8088 SBC, in other words, embedded ROMable code. A trawl through the issues list for the Github repo turned up this thread: ia16-elf-gcc generating ROM code? Long story short, you need to write a linker script that places all the various sections at the appropriate addresses in the resulting executable. After that it's a matter of converting it to your desired format, Intel Hex probably, for downloading to your (E)EPROM burner. There were a couple of links to interesting projects requiring embedded code: SYS86 and the BIOS for a 80186 compatible SystemVerilog CPU core and FPGA reference design where linker scripts can be perused. The latter project is interesting; it seems to be reimplementing a 80186 in a FPGA, including some peripherals like a timer. That could be useful for keeping hardware that was developed for the 80186 platform alive.
In the next period of boredom I might do a proper build from source to generate a RPM and get a RPM spec file out of it that others can use as a basis for other RPM based systems.
I'm also looking into how AppImages, Snaps and Flatpaks are built so that I can build one such package which can be used by people on potentially any Linux distro.
And that's how I distracted myself for a couple of hours. 🤗 It was fun to do sysadmin type things again for a while.
Discussions
Become a Hackaday.io Member
Create an account to leave a comment. Already have an account? Log In.