Intro
It is over a month since my last entry on bare metal programming. At the time I was struggling to work out how to implement file access. My programs could use terminal input/output and libraries, we had made a start on coding replacement for OS calls in syscalls.c. This allows us to write programs which don't have storage requirements but this tends to limit application to mathematical type calculations, or perhaps text based games.
Searching through github for RPi bare metal sdcard i/o programs yields a few possibilities. Chan has written FatFs, a filesystem module for small embedded systems, including ARM. John Cronin has written an RPI second stage bootloader which contains FatFs. It uses file access to load a program from disk, rather than having to set up a kernel.img file for each test. I found it a little difficult to understand how to apply this. Finally I settled on Marco Maccaferri's rpi bare metal repository. Not only does he implement fileio but also has frame buffer and USB functions. I decided to concentrate on Maccasoft initially.
Maccasoft investigation
Maccasoft separates FatFs into a directory and implements RPI specific functions in a file emmc.c. In fact he has done a grand job in setting up a kernel folder containing sub-folders for USB, FatFs and drivers for frame buffer, HDMI console, png images, Simple DirectMedia graphics, audio, GPIO and compression. This is really what we are aiming for, a bare metal kernel, not dependent on linux, which allows us to write programs using a variety of drivers.
I started by testing the "template" application Maccasoft provided. I ran make on the kernel folder, then make on the template folder and copied the resulting kernel.img to SDcard. I connected an HDMI screen and a USB keyboard and booted. Very impressively I get a low-res screen showing messages, the ability to type in and the ability to move the cursor around the screen.
Optimistically I then ran make on the "abbaye des morts" game. I had to change "uint" variables to "unsigned int" but compilation was successful and I copied the image to SD card. The game is excellent, a properly playable platform game with graphics and sound. Marco has come up with a very good product.
Maccasoft File Access
At this stage I had made no further progress on file i/o as maccasoft doesn't seem to use fles although the template app does mount the SD card. I looked at John Cronin's github and I was able to get it to read and write files in the root directory.
I then realised that Maccasoft has a reasonably complete syscalls.c with open/close/read/write functions implemented so I should be able to use standard fopen/fclose/fread/fwrite functions to use the SD card.
This proved to be the case we can open/close/read/write files in the root directory using standard C functions. This is a big step forward for us.
Bootloader integration
David Welch's bootloader is indispensible for loading and testing new versions of programs. It expects application programs to be loaded at 0x8000 and is itself loaded at 0x200000. Maccasoft link script does load at 0x8000 and uses the space above for heap. When we try to load a Maccasoft kernel.img using the bootloader it works fine. This is great, we now have file access and a bootloader.
UART stdin/stdout
Maccasoft sets up a framebuffer to allow writing to an HDMI screen and configures USB for a keyboard input. In fact I much prefer using a putty console for stdio. I take my previous termio program and add uart_get and uart_put to main.c. This is straightforward. Now I add a function uart_init() and put all uart functions in uart.c so that I have added them to the kernel. This also works fine.
Looking at syscalls.c we notice that the file pointers 0,1 and 2 which refer to stdin, stdout, stderr respectively are excluded from processing in _write and _read. I add code to _write so that uart_putc or uart_puts is called if fp=1. Now putchar, puts and printf all use UART output by default and I have a properly working stdout device. This is great progress.
I have so far struggled to work out the details to make _read work for both character and string input so stdin is not fully working, but I wont worry about this for the moment as I can use uart_gets and ssprintf for stdin functions.
Summary
We have made great progress using Maccasoft's work. We have working file access, standard output and (almost) standard input. We have a working boot loader and a separate "kernel" to implement standard functions and drivers. This gives us the capability to write a variety of standalone applications. Maccasoft also provides us with a frame buffer, USB devices, audio, image processing and GPIO controls if we want them.
In fact I did try out USB drivers a bit further. RPI mouse, USB stick and Ethernet interfaces are all correctly detected. However Marco hasn't needed to progress them further. You could read a file sector or an ethernet frame but would have to write the higher levels yourself. There is some discussion on the RPI forum. Rene Stange wrote the USPI RPI bare metal drivers and has gone further in his Circle C++ bare metal OS project.