diff --git a/blog/super-bootable-64-2020-05-06.markdown b/blog/super-bootable-64-2020-05-06.markdown
new file mode 100644
index 0000000..4b7ef4e
--- /dev/null
+++ b/blog/super-bootable-64-2020-05-06.markdown
@@ -0,0 +1,380 @@
+---
+title: Super Bootable 64
+date: 2020-05-06
+series: howto
+tags:
+ - witchcraft
+ - supermario64
+ - nixos
+---
+
+# Super Bootable 64
+
+[Super Mario 64][sm64] was the launch title of the [Nintendo 64][n64] in 1996.
+This game revolutionized an entire generation and everything following it by
+delivering fast, smooth and fun 3d platforming gameplay to gamers all over the
+world. This game is still played today by speedrunners, who do everything from
+beating it while collecting every star, the minimum amount of stars normally
+required, 0 stars and [without pressing the A jump button][wfrrpannenkoek].
+
+[sm64]: https://en.wikipedia.org/wiki/Super_Mario_64
+[n64]: https://en.wikipedia.org/wiki/Nintendo_64
+[wfrrpannenkoek]: https://youtu.be/kpk2tdsPh0A
+
+This game was the launch title of the Nintendo 64. As such, the SDK used to
+develop it was pre-release and [had an optimization bug that forced the game to
+be shipped without optimizations due to random crashiness issues][mvgo0] (watch
+the linked video for more information on this than I can summarize here).
+Remember that the Nintendo 64 shipped games on write-once ROM cartridges, so any
+bug that could cause the game to crash randomly was fatal.
+
+[mvgo0]: https://youtu.be/NKlbE2eROC0
+
+When compiling something _without_ optimizations, the output binary is
+effectively a 1:1 copy of the input source code. This means that exceptionally
+clever people could theoretically go in, decompile your code and then create
+identical source code that could be used to create a byte-for-byte identical
+copy of your program's binary. But surely nobody would do that, that would be
+crazy, wouldn't it?
+
+
![Noooo! You can't just port a Nintendo 64 game to LibGL! They're
+completely different hardware! It wouldn't respect the wishes of the creators!
+Hahaha porting machine go brrrrrrrr](/static/blog/portingmachinegobrrr.png)
+
+Someone did. The fruits of this effort are available [here][sm64dc]. This was
+mostly a proof of concept and is a masterpiece in its own right. However,
+because it was decompiled, this means that the engine itself could theoretically
+be ported to run on any other platform such as Windows, Linux, the Nintendo
+Switch or even a [browser][sm64browser].
+
+[sm64dc]: https://github.com/n64decomp/sm64
+[sm64browser]: https://froggi.es/mario/
+
+[Someone did this][sm64pcnews] and ended up posting it on 4chan. Thanks to a
+friend, I got my hands on the Linux-compatible source code of this port and made
+an archive of it [on my git server][sm66pcsauce]. My fork of it has only
+minimal changes needed for it to build in NixOS.
+
+[sm64pcnews]: https://www.videogameschronicle.com/news/a-full-mario-64-pc-port-has-been-released/
+[sm66pcsauce]: https://tulpa.dev/saved/sm64pc
+
+[nixos-generators][nixosgenerators] is a tool that lets you create custom NixOS
+system definitions based on a NixOS module as input. So, let's create a bootable
+ISO of Super Mario 64 running on Linux!
+
+[nixosgenerators]: https://github.com/nix-community/nixos-generators
+
+## Setup
+
+You will need an amd64 Linux system. NixOS is preferable, but any Linux system
+should _theoretically_ work. You will also need the following things:
+
+- `sm64.us.z64` (the release rom of Super Mario 64 in the US version 1.0) with
+ an sha1 sum of `9bef1128717f958171a4afac3ed78ee2bb4e86ce`
+- nixos-generators installed (`nix-env -f
+ https://github.com/nix-community/nixos-generators/archive/master.tar.gz -i`)
+
+So, let's begin by creating a folder named `boot2sm64`:
+
+```console
+$ mkdir ~/code/boot2sm64
+```
+
+Then let's create a file called `configuration.nix` and put some standard
+boilerplate into it:
+
+```nix
+# configuration.nix
+
+{ pkgs, lib, ... }:
+
+{
+ networking.hostName = "its-a-me";
+}
+```
+
+And then let's add [dwm][dwm] as the window manager. This setup will be a little
+bit more complicated because we are going to need to add a custom configuration
+as well as a patch to the source code for auto-starting Super Mario 64. Create a
+folder called `dwm` and run the following commands in it to download the config
+we need and the autostart patch:
+
+[dwm]: https://dwm.suckless.org/
+
+```console
+$ mkdir dwm
+$ cd dwm
+$ wget -O autostart.patch https://dwm.suckless.org/patches/autostart/dwm-autostart-20161205-bb3bd6f.diff
+$ wget -O config.h https://gist.githubusercontent.com/Xe/f5fae8b7a0d996610707189d2133041f/raw/7043ca2ab5f8cf9d986aaa79c5c505841945766c/dwm_config.h
+```
+
+And then add the following before the opening curly brace:
+
+```nix
+
+{ pkgs, lib, ... }:
+
+let
+ dwm = with pkgs;
+ let name = "dwm-6.2";
+ in stdenv.mkDerivation {
+ inherit name;
+
+ src = fetchurl {
+ url = "https://dl.suckless.org/dwm/${name}.tar.gz";
+ sha256 = "03hirnj8saxnsfqiszwl2ds7p0avg20izv9vdqyambks00p2x44p";
+ };
+
+ buildInputs = with pkgs; [ xorg.libX11 xorg.libXinerama xorg.libXft ];
+
+ prePatch = ''sed -i "s@/usr/local@$out@" config.mk'';
+
+ postPatch = ''
+ cp ${./dwm/config.h} ./config.h
+ '';
+
+ patches = [ ./dwm/autostart.patch ];
+
+ buildPhase = " make ";
+
+ meta = {
+ homepage = "https://suckless.org/";
+ description = "Dynamic window manager for X";
+ license = stdenv.lib.licenses.mit;
+ maintainers = with stdenv.lib.maintainers; [ viric ];
+ platforms = with stdenv.lib.platforms; all;
+ };
+ };
+in {
+ environment.systemPackages = with pkgs; [ hack-font st dwm ];
+
+ networking.hostName = "its-a-me";
+}
+```
+
+Now let's create the mario user:
+
+```nix
+{
+ # ...
+ users.users.mario = { isNormalUser = true; };
+
+ system.activationScripts = {
+ base-dirs = {
+ text = ''
+ mkdir -p /nix/var/nix/profiles/per-user/mario
+ '';
+ deps = [ ];
+ };
+ };
+
+ services.xserver.windowManager.session = lib.singleton {
+ name = "dwm";
+ start = ''
+ ${dwm}/bin/dwm &
+ waitPID=$!
+ '';
+ };
+
+ services.xserver.enable = true;
+ services.xserver.displayManager.defaultSession = "none+dwm";
+ services.xserver.displayManager.lightdm.enable = true;
+ services.xserver.displayManager.lightdm.autoLogin.enable = true;
+ services.xserver.displayManager.lightdm.autoLogin.user = "mario";
+}
+```
+
+The autostart file is going to be located in `/home/mario/.dwm/autostart.sh`. We
+could try and place it manually on the filesystem with a NixOS module, or we
+could use [home-manager][hm] to do this for us. Let's have home-manager do this
+for us. First, install home-manager:
+
+[hm]: https://rycee.gitlab.io/home-manager/
+
+```console
+$ nix-channel --add https://github.com/rycee/home-manager/archive/release-20.03.tar.gz home-manager
+$ nix-channel --update
+```
+
+Then let's add home-manager to this config:
+
+```nix
+{
+ # ...
+
+ imports = [ ];
+
+ home-manager.users.mario = { config, pkgs, ... }: {
+ home.file = {
+ ".dwm/autostart.sh" = {
+ executable = true;
+ text = ''
+ #!/bin/sh
+ export LIBGL_ALWAYS_SOFTWARE=1 # will be relevant later
+ '';
+ };
+ };
+ };
+}
+```
+
+Now, for the creme de la creme of this project, let's build Super Mario 64. You
+will need to get the base rom into your system's Nix store somehow. A half
+decent way to do this is with [quickserv][quickserv]:
+
+[quickserv]: https://tulpa.dev/Xe/quickserv
+
+```console
+$ nix-env -if https://tulpa.dev/Xe/quickserv/archive/master.tar.gz
+$ cd /path/to/folder/with/baserom.us.z64
+$ quickserv -dir . -port 9001 &
+$ nix-prefetch-url http://127.0.0.1:9001/baserom.us.z64
+```
+
+This will pre-populate your Nix store with the rom and should return the
+following hash:
+
+```
+148xna5lq2s93zm0mi2pmb98qb5n9ad6sv9dky63y4y68drhgkhp
+```
+
+If this hash is wrong, then you need to find the correct rom. I cannot help you
+with this.
+
+Now, let's create a simple derivation for the Super Mario 64 PC port. I have a
+tweaked version that is optimized for NixOS, which we will use for this. Add the
+following between the `dwm` package define and the `in` statement:
+
+```nix
+# ...
+ sm64pc = with pkgs;
+ let
+ baserom = fetchurl {
+ url = "http://127.0.0.1:9001/baserom.us.z64";
+ sha256 = "148xna5lq2s93zm0mi2pmb98qb5n9ad6sv9dky63y4y68drhgkhp";
+ };
+ in stdenv.mkDerivation rec {
+ pname = "sm64pc";
+ version = "latest";
+
+ buildInputs = [
+ gnumake
+ python3
+ audiofile
+ pkg-config
+ SDL2
+ libusb1
+ glfw3
+ libgcc
+ xorg.libX11
+ xorg.libXrandr
+ libpulseaudio
+ alsaLib
+ glfw
+ libGL
+ unixtools.hexdump
+ ];
+
+ src = fetchgit {
+ url = "https://tulpa.dev/saved/sm64pc";
+ rev = "c69c75bf9beed9c7f7c8e9612e5e351855065120";
+ sha256 = "148pk9iqpcgzwnxlcciqz0ngy6vsvxiv5lp17qg0bs7ph8ly3k4l";
+ };
+
+ buildPhase = ''
+ chmod +x ./extract_assets.py
+ cp ${baserom} ./baserom.us.z64
+ make
+ '';
+
+ installPhase = ''
+ mkdir -p $out/bin
+ cp ./build/us_pc/sm64.us.f3dex2e $out/bin/sm64pc
+ '';
+
+ meta = with stdenv.lib; {
+ description = "Super Mario 64 PC port, requires rom :)";
+ };
+ };
+# ...
+```
+
+And then add `sm64pc` to the system packages:
+
+```nix
+{
+ # ...
+ environment.systemPackages = with pkgs; [ st hack-font dwm sm64pc ];
+ # ...
+}
+```
+
+As well as to the autostart script from before:
+
+```nix
+{
+ # ...
+ home-manager.users.mario = { config, pkgs, ... }: {
+ home.file = {
+ ".dwm/autostart.sh" = {
+ executable = true;
+ text = ''
+ #!/bin/sh
+ export LIBGL_ALWAYS_SOFTWARE=1
+ ${sm64pc}/bin/sm64pc
+ '';
+ };
+ };
+ };
+
+}
+```
+
+Finally let's enable some hardware support so it's easier to play this bootable
+game:
+
+```nix
+{
+ # ...
+
+ hardware.pulseaudio.enable = true;
+ virtualisation.virtualbox.guest.enable = true;
+ virtualisation.vmware.guest.enable = true;
+}
+```
+
+Altogether you should have a `configuration.nix` that looks like
+[this][confignix].
+
+[confignix]: https://gist.github.com/Xe/935920193cfac70c718b657a088f3417#file-configuration-nix
+
+So let's build the ISO!
+
+```console
+$ nixos-generate -f iso -c configuration.nix
+```
+
+Much output later, you will end up with a path that will look something like
+this:
+
+```
+/nix/store/fzk3psrd3m6x437m6xh9pc7bnv2v44ax-nixos.iso/iso/nixos.iso
+```
+
+This is your bootable image of Super Mario 64. Copy it to a good temporary
+folder (like your downloads folder):
+
+```console
+cp /nix/store/fzk3psrd3m6x437m6xh9pc7bnv2v44ax-nixos.iso/iso/nixos.iso ~/Downloads/mario64.iso
+```
+
+Now you are free to do whatever you want with this, including [booting it in a
+virtual machine][bootinvmmp4].
+
+[bootinvmmp4]: /static/blog/boot2mario.mp4
+
+This is why I use NixOS. It enables me to do absolutely crazy things like
+creating a bootable ISO of Super Mario 64 without having to understand how to
+create ISO files by hand or how bootloaders on Linux work in ISO files.
+
+It Just Works.
diff --git a/go.sum b/go.sum
index d32b261..967ae53 100644
--- a/go.sum
+++ b/go.sum
@@ -31,6 +31,7 @@ github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi
github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA=
github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs=
github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w=
+github.com/golang/protobuf v1.4.0 h1:oOuy+ugB+P/kBdUnG5QaMXSIyJ1q38wWSojYCb3z5VQ=
github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0=
github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
github.com/google/go-cmp v0.3.1 h1:Xye71clBPdm5HgqGwUkwhbynsUJZhDbS20FvLhQ2izg=
@@ -100,6 +101,7 @@ github.com/prometheus/procfs v0.0.2 h1:6LJUbpNm42llc4HRCuvApCSWB/WfhuNo9K98Q9sNG
github.com/prometheus/procfs v0.0.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA=
github.com/prometheus/procfs v0.0.8 h1:+fpWZdT24pJBiqJdAwYBjPSk+5YmQzYNPYzQsdzLkt8=
github.com/prometheus/procfs v0.0.8/go.mod h1:7Qr8sr6344vo1JqZ6HhLceV9o3AJ1Ff+GxbHq6oeK9A=
+github.com/prometheus/procfs v0.0.11 h1:DhHlBtkHWPYi8O2y31JkK0TF+DGM+51OopZjH/Ia5qI=
github.com/prometheus/procfs v0.0.11/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4OA4YeYWdaU=
github.com/russross/blackfriday v2.0.0+incompatible h1:cBXrhZNUf9C+La9/YpS+UHpUT8YD6Td9ZMSU9APFcsk=
github.com/russross/blackfriday v2.0.0+incompatible/go.mod h1:JO/DiYxRf+HjHt06OyowR9PTA263kcR/rfWxYHBV53g=
@@ -151,6 +153,7 @@ golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7w
golang.org/x/sys v0.0.0-20200106162015-b016eb3dc98e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200122134326-e047566fdf82 h1:ywK/j/KkyTHcdyYSZNXGjMwgmDSfjglYZ3vStQ/gSCU=
golang.org/x/sys v0.0.0-20200122134326-e047566fdf82/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20200420163511-1957bb5e6d1f h1:gWF768j/LaZugp8dyS4UwsslYCYz9XgFxvlgsn0n9H8=
golang.org/x/sys v0.0.0-20200420163511-1957bb5e6d1f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4=
@@ -161,6 +164,7 @@ google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLY
google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0=
google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM=
google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE=
+google.golang.org/protobuf v1.21.0 h1:qdOKuR/EIArgaWNjetjgTzgVTAZ+S/WXVrq9HW9zimw=
google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo=
gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
diff --git a/nix/deps.nix b/nix/deps.nix
index d2ff85d..0bc6d12 100644
--- a/nix/deps.nix
+++ b/nix/deps.nix
@@ -113,8 +113,8 @@
fetch = {
type = "git";
url = "https://github.com/golang/protobuf";
- rev = "v1.3.2";
- sha256 = "1k1wb4zr0qbwgpvz9q5ws9zhlal8hq7dmq62pwxxriksayl6hzym";
+ rev = "v1.4.0";
+ sha256 = "1fjvl5n77abxz5qsd4mgyvjq19x43c5bfvmq62mq3m5plx6zksc8";
};
}
{
@@ -338,8 +338,8 @@
fetch = {
type = "git";
url = "https://github.com/prometheus/client_golang";
- rev = "v1.5.1";
- sha256 = "0nkhjpwpqr3iz2jsqrl37qkj1g4i8jvi5smgbvhxcpyinjj00067";
+ rev = "v1.6.0";
+ sha256 = "0wwkx69in9dy5kzd3z6rrqf5by8cwl9r7r17fswcpx9rl3g61x1l";
};
}
{
@@ -365,8 +365,8 @@
fetch = {
type = "git";
url = "https://github.com/prometheus/procfs";
- rev = "v0.0.8";
- sha256 = "076wblhz8fjdc73fmz1lg0hafbwg1xv8hszm78lbg9anjpfgacvq";
+ rev = "v0.0.11";
+ sha256 = "1msc8bfywsmrgr2ryqjdqwkxiz1ll08r3qgvaka2507z1wpcpj2c";
};
}
{
@@ -491,8 +491,8 @@
fetch = {
type = "git";
url = "https://go.googlesource.com/sys";
- rev = "e047566fdf82";
- sha256 = "1xazqxggwb834clbdqxl65xkbb45jich0nb09b4gynrp27wyy7h4";
+ rev = "1957bb5e6d1f";
+ sha256 = "0imqk4l9785rw7ddvywyf8zn7k3ga6f17ky8rmf8wrri7nknr03f";
};
}
{
@@ -522,6 +522,15 @@
sha256 = "06zl7w4sxgdq2pl94wy9ncii6h0z3szl4xpqds0sv3b3wbdlhbnn";
};
}
+ {
+ goPackagePath = "google.golang.org/protobuf";
+ fetch = {
+ type = "git";
+ url = "https://go.googlesource.com/protobuf";
+ rev = "v1.21.0";
+ sha256 = "12bwln8z1lf9105gdp6ip0rx741i4yfz1520gxnp8861lh9wcl63";
+ };
+ }
{
goPackagePath = "gopkg.in/alecthomas/kingpin.v2";
fetch = {
diff --git a/static/blog/boot2mario.mp4 b/static/blog/boot2mario.mp4
new file mode 100644
index 0000000..259902a
Binary files /dev/null and b/static/blog/boot2mario.mp4 differ
diff --git a/static/blog/portingmachinegobrrr.png b/static/blog/portingmachinegobrrr.png
new file mode 100644
index 0000000..874f11a
Binary files /dev/null and b/static/blog/portingmachinegobrrr.png differ