ROL 00000000 – READY

Like many people my age, my first exposure to a personal computer was a 6502-based system. In my case, it was an Atari 400, paired with a BASIC cartridge. There are a few people on the internet with series about building a retro computer using a 6502 and a handful of other chips. I’ve decided to jump onto the nostalgia bandwagon myself. I’m a software engineer by profession and wanted a hobby that wasn’t also software engineering! So, at the ripe old age of 50-something, I decided to take up electronics.

My goal for this particular project is pretty simple: A straightforward 6502-based computer. I am going to have 32K of RAM, 8K of system ROM, 8K of “application” ROM, a “good number” of GPIOs and serial I/O for communication with the user. So, initially, no keyboard or graphics or character output. Nothing fancy, just serial. The two 8K ROMS will be setup such that the first ROM will be permanent and contain system interface routines—essentially a BIOS like in an old 8086. The second ROM will contain the application to run: it could be a game, a monitor, a simple O/S, a BASIC interpreter or an Assembler and will be removable, like the cartridges for the old 8-bit Ataris. Later, I may get more adventurous and implement some sort of graphics system, keyboard (or general USB) interface.

I decided on the name, “ROL”, which comes from the 6502 instruction set: ROL rotates a byte one bit to the left. This is the same as multiplying by 2. I also like the way it rolls off the tongue!

As the project progresses, I will document it here. Since it’s a relatively simple project, it should be a short series. I will update this post with links to each segment as I complete it:

Check out 6502.org as a good starting point for 6502 hardware design. It has good forums and links to several 6502 projects around the internet.

Building UEFI Applications with Clang and LLD

tldr

Modern PC-compatible systems have replaced the old system BIOS with the Unified Extensible Firmware Interface. This is almost an operating system in itself and makes some of the vagaries of bringing up a system a little more clean than in the past. Several well-defined software interfaces are provided for mapping hardware components, figuring out memory layout, loading kernels, showing startup screens and some other boring junk operating systems have to do to survive. I recently had occasion to do some work in UEFI.

UEFI has actually been around a while, but the platforms I’ve seen still  seem a little immature.  A couple of the better ones are the implementations on my Asus Zenbook Pro and the two Intel NUCs I use for development. Having downloaded a copy of the UEFI specification, I set out to build a Hello World UEFI application.

That’s when things got a little messy. I found out there are basically three options when it comes to building UEFI applications: Tianocore, which is Intel’s UEFI build environment, gnu-efi, and FASM which can natively produce EFI application binaries (crazily enough).

I tried Tianocore first—straight from the horse’s mouth as it were. The application built fine and it worked instantly. The problem is the labyrinthine build environment. They actually recommend sticking your code somewhere in the Tianocore build tree and modifying the build files to include your application code. Sorry, Intel, but I’m not going to change the layout of my source tree to what you think it should be. Actually, with the instructions I’ll provide shortly, you should be able to use Tianocore’s headers and libraries and dump their build system without too much trouble. If you care about correctness (which is generally probably good) and/or aren’t as mental about your build system as I am, then I would recommend going with that option. Also, Tianocore’s github repo has thorough instructions on setting it up on various platforms.

Disillusioned with Tianocore’s messy build environment, I decided to hold my nose and try the gnu-efi way of doing things. There was a package available for it in the Arch repository, so getting it setup was easy enough. It also took minimal effort to build a working application. I was excited it was much more straight-forward than the Tianocore mess, but I was still holding my nose. It wasn’t long before I realized gnu-efi’s build instructions had dumped an unreasonably large binary in my build directory. I couldn’t imagine what all those bytes were doing in a little Hello World EFI application.  Later, I discovered the problem: the gnu-efi application provides binary startup objects and a library for relocation and dynamic resolving of relocatable symbols. Gross.

I decided I’d go with my own UEFI build environment using Clang and LLD as the compiler and linker and using my own code instead of Tianocore or gnu-efi project libraries and headers. As it turns out, I had to do considerable research before I could put all the bits together to make it work.

The first thing you need is a set of header files. I built my own, using the spec. I had a creepy feeling Tianocore and GNU would use their own versions of what a header should be, and I preferred to use something custom. My application would have no startup code or link-libraries. Everything used would be defined in the project itself and linked in if it’s used and rejected if not.

Next, I installed the build tools themselves: clang and lld. It was an easy install on Arch Linux and I had everything up and going without incident. Then it got hard. Clang had to produce Microsoft object files to be compatible with the way UEFI does things. I had to search down the options and fiddle with them until I got them working properly. LLVM itself has to be called as though it were Microsoft’s LINK. Once I got everything together, I captured it in git where others could use it. Take special care to examine the clang and lld commands in the makefile.

Notice the code I make available doesn’t use a runtime library or startup files. The loader will read it into memory and execute it directly. I prefer to access the various functions and structures in their raw form, without a library hiding the details from me. This gives me a more intimate knowledge of the library that I find useful.  As I said earlier, this setup can be modified to use the official Tianocore headers. Indeed, the headers provided here are incomplete.

Finally, the goal with your c compiler is to build an MS object format output file for each of your compiled modules. Those object files will be linked with Clang’s linker in Microsoft LINK mode to generate a PE64 executable.

I’ve used this to successfully build on FASM without using FASM’s built-in PE format generator. I still haven’t quite gotten NASM to work. The binary is recognized and loads fine and, if it does nothing but terminate, it works great! I think there are some position independent code related issues. I investigated it briefly, but didn’t find a solution and had more pressing things to work on. I think NASM is very close to working and I may look into it in the future. For now, I am content to use FASM when necessary. If anyone reading this figures out the secret, I’d definitely be interested in knowing.

I hope this post will find others looking into getting into UEFI and offer them some help in how it fits together. Maybe it will even give enough information to help building UEFI apps on other systems or with other compilers. Good Luck!