DOS on WII
Tags: dos, linux
Published: 2020-05-22 14:21:53


---
title: DOS on WII
published: true
description: How to revive an old WII using even older DOS games
tags: DOS, LINUX
cover_image: https://dev-to-uploads.s3.amazonaws.com/i/xw9j1xc58eimhym90gxi.jpg
---

So, the kids out-grew their Nintendo WII and moved on to Xbox (or Netflix). What's an electronics-hoarder like myself supposed to do?

There is an old saying: "If you love it then you should have run DOOM on it", so I set out to do that, and more. And while I'm at it, why not take the scenic route?

NOTE: I will not cover how to jailbreak and install homebrew.

# Choosing a Linux distribution

Looking at the raw computing power, the WII should have no problem running Linux:

  * PowerPC CPU running at 729 MHz
  * ATI Hollywood GPU running at 243 MHz
  * 88 MB of memory (ouch!)
  * WiFi 802.11b/g
  * USB, Audio, Video, blah blah blah

There are a few linux distributions ported to the WII, and I went through a couple before choosing.
The 1st time I got the WII to boot was using WiiToo. It is a Gentoo based system, so it is very flexible.
However, I found that it requires quite a bit of work to update it on the little WII, both on my side (it's a very old version, dating back to 2009) and on Wii's side, since Gentoo is all about compiling-from-source.

So I moved on, looking for a binary-package-based distribution. and I found Whiite, which is a Debian 5 (Lenny) distribution. But soon I found, again, that it is hard to get a 2009-ish system up-to-date.

After taking a short break, I came back to the project and found Wii-Linux-NGX and I knew this was the one!
Based on Debian 8, updated in 2017 - finally, something to work with.

We will start with the base system, as detailed in [Wii-Linux-NGX](https://neagix.github.io/wii-linux-ngx/).
After we successfully boot, we will start the post-installation setup.

change default root password
   ```$ passwd```
configure Wifi
   ```$ /root/whiite-ez-wifi-config```
rename the network adapter device name
* edit /etc/network/interfaces
  * wlan0 -> wlan1

we really need to enable swap, because compilation, which we will get to a little later, won't work with 88MB or RAM. also, we will use the actual /tmp partition on the sd-card.

enable swap (in /etc/fstab)
```/dev/mmcblk0p3 swap swap defaults 0 0```
disable tmpfs on /tmp by commenting the line */tmpfs . . .* in /etc/fstab
and enable swap
```$ swapon -a```
next, enable remote ssh access, because working with a keyboard in front of the TV is sub-optimal.
we'll use RSA keys and not passwords. feel free to skip this part
* allow root remote login with password
    * edit /etc/ssh/sshd_config
  PermitRootLogin yes
       ```$ service ssh restart```
* login remotely
  * create /root/.ssh/authorized_keys
  * copy RSA/DSA public keys to it
* revert changes to /etc/ssh/sshd_config
       ```$ service ssh restart```
   
now we can log out, login remotely from a normal laptop, and move-on.

next step - update the system. a lot has changed since 2017.
for starters - the PPC repositories have been archived, so we need to point our system to the new location

PowerPC APT repositories have moved, so add the new locations to /etc/apt/sources.list
```console
  deb http://archive.debian.org/debian/ jessie main
  deb http://ftp.ports.debian.org/debian-ports unstable main
  deb http://ftp.ports.debian.org/debian-ports unreleased main
  deb http://ftp.ports.debian.org/debian-ports experimental main
```
and engage
```console
  $ apt update
  $ apt upgrade
```
next, install additional packages.
specifically, xorg and build tools, since we're going to have to compile stuff from source code
```$ apt install libpng12-0 libstdc++6 libx11-6 zlib1g gcc patch autoconf automake libtool libsdl1.2-dev libsdl-sound1.2-dev libsdl-net1.2-dev make g++ xorg lightdm git```
thinking ahead, we're going to have a few non-root users for running our software.
we will have a separate user for each software: DOSBox, ScummVM and DOOM.

since most of these are SDL based, we're going to have to grant these users low-level access to the keyboard, mouse, audio and video.
1st, allow console access to dedicated group
```console
  $ addgroup --system console 
  $ chgrp console /dev/console
  $ chmod g+rw /dev/console
```
and add our dosbox user
```console
  $ useradd -m -U dosbox
  $ usermod -a -G console dosbox
  $ usermod -a -G tty dosbox
  $ usermod -a -G video dosbox
  $ usermod -a -G input dosbox
  $ usermod -a -G audio dosbox
```
# Building dosbox
building dosbox from source is pretty straightforward.
the only caveat is the build time on the WII hardware: ~1.5 hours.
it is also the reason we had to enable swap, or gcc would fail compiling some of the more complex source files.
```console
  $ wget https://netix.dl.sourceforge.net/project/dosbox/dosbox/0.74-3/dosbox-0.74-3.tar.gz
  $ tar -xzf dosbox-0.74-3.tar.gz
  $ cd dosbox-0.74-3
  $ ./autogen
  $ ./configure
  $ make install
```
# Building ScummVM
as with dosbox, this is pretty straightforward but takes (a little more than) a while.
```console
  $ wget https://www.scummvm.org/frs/scummvm/2.1.1/scummvm-2.1.1.tar.gz
  $ LDFLAGS="-Wl,--no-keep-memory"  ./configure
  $ make install
```

# Running DOOM
Now, of course I could just run DOOM via dosbox, but since the source code is available, I decided to compile and run it.

I started with the Linux SDL port of the doom code, but it turned out to have 2 major issues, and a minor one:
1. it would not compile on a PPC CPU
2. it was not full-screen
3. it would not compile and run on a pure 64bit system

while #1 and #2 are crucial for our end goal, issue #3 was for debugging purposes only - it was easier developing the code on my laptop than on the WII, mainly due to compilation times.
the code changes can be seen in detail in the github repo, but I will explain the changes in high level.
#### adding PPC support
This was a small issue with the code when \_\_*BIG_ENDIAN*\_\_ precompiler directive is defined.
#### 64bit support
This task turned out to be more than "just compile as 64bit".
The code was very old and new GCC versions throw warnings and errors about
* implicit _int_ variables
* string literals being cast to pointers

the 64bit part was a simple matter of not assuming 
```sizeof(void*) == sizeof(int) == 4```

#### full-screen
the SDL 1.2 port was running doom in its native, awesome 90's resolution of 320x200, but the WII's video is aimed at pre-HDMI televisions, so the native resolution is 576x516.
the code changes are localized to a single file *i_sdl_video.c*.
the original code in *I_InitGraphics* used 
```SDL_SetVideoMode(320, 200, 8, SDL_SWSURFACE)```
to initialize the the display-buffer, and then pointed the doom code to draw directly into it
```screens[0] = (byte*) (screen->pixels);```
we will modify the code to use 2 separate buffers - the screen will be initialized to our native resolution, and another buffer for doom to draw into, which is 320x200.
1st, modify the video mode
```c
screen = SDL_SetVideoMode(576, 516, 8, SDL_SWSURFACE);
...
virt_screen = SDL_CreateRGBSurface(SDL_SWSURFACE, 320, 200, 8, 0, 0, 0, 0);
screens[0] = (byte*) (virt_screen->pixels);
```
now that we have both screens set up, we will need to modify the screen update routine *I_FinishUpdate*.
right before the original code, we will copy the image into the screen with scaling
```c
scale(virt_screen, screen);
SDL_Flip(screen);
```
for the scaling algorithm, I decided to go with a very simple algorithm, for 2 reasons:
1. speed
2. consistency

I wanted the copying to be as computationally-simple as possible, and give consistent results (e.g. no "random" offsets).
the scaling math is pretty simple:
	576 = (4*2+1)*64
	516 ~= 500 = (3+2)*100
this means that horizontally, we will copy
4 times: 1 bytes --> 2 bytes, and another 1-->1
vertically, we will duplicate 1 line 3 times, then 1 line 2 times.
```c
static const int xmap[] = {2, 2, 2, 2, 1};
static const int ymap[] = {3, 2};
// *i - axis index
// *d - axis destination
// *s - axis source
for(yi = yd = ys = 0; ys < 200; ++ys) {
   for(xi = xd = xs = 0; xs < 320; ++xs) {
      // duplicate source pixel into destination line
      memset(PIXEL(dst, xd, yd), *PIXEL(src, xs, ys), xmap[xi]);
      xd += xmap[xi];
      // point to next destination pixel (after duplications)
      xi = (xi + 1) % SIZEOF_ARRAY(xmap);
   }
   // duplicate line as many times as required
   // -1 for the line we drew above
   for (y = 1; y < ymap[yi]; ++y)
      memcpy(PIXEL(dst, 0, yd+y), PIXEL(dst, 0, yd), 576);
   // point to next line (after duplications)
   yd += ymap[yi];
   yi = (yi + 1) % SIZEOF_ARRAY(ymap);
}
```

in order to run doom you'll need to download the WAD files, and then
```console
export DOOMWADDIR=~/doom.wad.directory/
./sdl_doom/linux/sdl_doom
```

and voila!

![WII running doom on Linux, 40" TV](https://dev-to-uploads.s3.amazonaws.com/i/s6r24x40oypjwdu4sffp.jpeg)

![old-skool DOS tools](https://dev-to-uploads.s3.amazonaws.com/i/p7mrzj5tr1gbi6stfsnx.jpg)

![ScummVM running all the oldies](https://dev-to-uploads.s3.amazonaws.com/i/h2jww6p0necavqinl2yk.jpg)

# finishing touches
for each application, we will have a dedicated user that will auto-run the application when logging on.

in order to accomplish that, we will simply add the relevant lines to each user's ~/.bashrc , e.g.
```console
dosbox
exit
```

the modified code is available here {% github zvikam/sdl_doom %}