[Analogy of some yogurt fermentation process where old material is used to make new material].
Fermentation is similar to building compilers. New languages are written with old languages.
https://en.wikipedia.org/wiki/ALGOL#/media/File:Algol&Fortran_family-by-Borkowski.svg
- Algol 60 was extended to create the language AED.
- AED was used to write the first BCPL compiler.
- BCPL was used to create the first B compiler
- B was used to re-implement the B compiler
- The B compiler/langauge was iteratively transformed into the C language/compiler.
The typical "hello world" operating system starts with the C language, the fancy yogurt. We plan to create our yogurt from scratch.
Goals
- continue the "expanding toolset" feeling of nand2tetris
- provide readers with plenty of documentation
- leave non-frustrating exercises to the reader
Anti-Goals
- accurately recreating the historical path to contemporary OSes
- re-implementing existing languages
What we'll do
- setup hardware/software
- manually write kernel8.img file that sends "x" to UART
- implement echoing user input
User Features
- ethernet
- netcat io
- tls
- v1.3
- chacha20
- poly1305
- sha256
- gemini client
- gemini server
Use Nix.
Machine code: the binary 1s and 0s seen flying across monitors in movies.
This will probably be the most frustrating part of this course, but I think it should be fun to see hand-written binary come to life.
To write our initial machine code, I recommend the following workflow:
- Write the 1s and 0s in a text file with lots of comments and whitespace
- Duplicate the file
- Manually remove all comments and whitespace from the file copy
- Run
cat ones-and-zeros.txt | binify > kernel8.img
to convert those ASCII 1s and 0s into a real binary file - Emulate the raspi4 by running
emulate kernel8.img
Note that, for now, we manually remove comments (instead of using sed
/grep
to do so). That's intentional. One of the first compiler functionalities we'll implement will be automatically removing comments and whitespace from a file then parsing the 1s and 0s.
Write machine code to print x
to UART.
See the provided documentation for information on how to do this. At first, I recommend only implemeting what's required to run on the emulator. Once you verify that you see UART output in the emulator, you should implement the required UART setup steps for real hardware.
If you do this correctly, you should see xxxx
from both the hardware and emulator, even though you only printed one x
. This is intentional! We'll fix this in the next section.
The duplicated output is because the raspi4 has 4 cores. We need to put all but one core to sleep. All the relevant information should be in the provided documentation.
For each character sent to UART, send it back.
Store a bunch of characters to memory, then send them back all at once upon EOT (ASCII character 0x04
)
https://web.archive.org/web/20140130143820/http://www.robinhoksbergen.com/papers/howto_elf.html
https://www.raspberrypi.org/forums/viewtopic.php?t=244479 https://elinux.org/RPi_Low-level_peripherals https://cs140e.sergio.bz/docs/BCM2837-ARM-Peripherals.pdf https://www.raspberrypi.org/documentation/hardware/raspberrypi/ https://www.elinux.org/RPi_Low-level_peripherals https://raspberrypi.kavadocs.com/hardware/raspberrypi/bcm2711 https://www.raspberrypi.org/documentation/hardware/raspberrypi/bcm2711/README.md https://github.com/rsta2/uspi https://github.com/Chadderz121/csud
https://github.com/rsta2/circle/blob/master/lib/bcm54213.cpp https://en.wikipedia.org/wiki/Media-independent_interface#Reduced_gigabit_media-independent_interface
expression → literal | unary | binary | grouping ;
literal → INT | HEX | CHAR | "false" | "true" ; grouping → "(" expression ")" ; unary → ( "-" | "!" ) expression ; binary → expression operator expression ; operator → "==" | "!=" | "<" | "<=" | ">" | ">=" | "+" | "-" | "*" | "/" ;