Compare commits

..

No commits in common. "cdb260fc58a579f92e84f90086b9ba98391c2fdd" and "f0678f1e5b244603ee6e86c2ac5feed60220da6b" have entirely different histories.

18 changed files with 136 additions and 570 deletions

295
README.md
View File

@ -1,263 +1,94 @@
# ALARM H700 ES-DE
# Alpine Linux for the Allwinner H700 SoC
This project provides a custom SD card image for Allwinner H700 based devices
running Alpine Linux (ALARM) with ES-DE (EmulationStation Desktop Edition)
for retro gaming emulation.
This repository contains a collection of scripts to create a custom Alpine
Linux based SD card image bootable on Allwinner H700 based devices.
## What This Is
In the standard configuration, a very basic system is installed, providing
a login prompt on `/dev/ttyS0`, automatic connection to a WiFi netowk on boot
and an SSH daemon running.
This is a customized Alpine Linux ARM (ALARM) distribution for the Allwinner
H700 SoC, pre-configured with:
The scripts have been tested on a Fedora Linux 40 system and the generated
image has been tested on an Anbernic RG35XX Plus portable console.
- **Alpine Linux ARM (ALARM)** - A lightweight Linux distribution
- **ES-DE** - EmulationStation Desktop Edition, a frontend for browsing and
launching retro games
- **RetroArch** - A flexible emulator frontend with libretro cores
- **60+ libretro cores** - Emulators for classic gaming systems including:
- Nintendo (NES, SNES, Game Boy, Game Boy Color, Game Boy Advance, N64, DS)
- Sega (Master System, Genesis/Mega Drive, Game Gear, Saturn, Dreamcast)
- Sony (PlayStation, PSP)
- Arcade (MAME, FBNeo, Final Burn Neo)
- Commodore (C64, Amiga, VIC-20)
- Atari (2600, 7800, Lynx, Jaguar)
- NEC (PC Engine, TurboGrafx-16)
- And many more...
## Requirements
Tested on Anbernic RG35XX Plus and other H700-based devices.
The scripts require the following software:
## Prerequisites
- `make` (specifically, GNU Make) to orchestrate the build and track
dependencies
- `python3` to run some of the provided scripts; only modules from the
standard library are used
- `sgdisk` to manipulate GPT partition tables
- `guestfish` to manipulate filesystem images without root privileges
- `fakeroot` to inject custom configuration files into the rootfs image
without root privileges
- `podman` to provide an Alpine Linux environment
- `qemu-user-static-aarch64` to enable `podman` to run aarch64 container
images on a different architecture like x86_64
The build system requires the following software:
An image of a stock SD card is required in order to extract components that
are specific to the H700 SoC that do not have open source alternatives yet;
those components are:
- **podman** - To provide an Alpine/Arch Linux ARM environment for cross-compilation
- **qemu-user-static** - To enable running aarch64 container images on x86_64
- **sgdisk** - To manipulate GPT partition tables
- **guestfish** - To manipulate filesystem images without root privileges
- **fakeroot** - To inject custom configuration files into the rootfs image
- **make** - GNU Make to orchestrate the build
- **wget** - To download ES-DE and libretro cores
- **unzip** - To extract downloaded archives
- **python3** - For helper scripts
- **abootimg** - For creating boot images
- the SPL
- the U-Boot bootloader
- the kernel
- the kernel's modules
- various firmware blobs
On Fedora, you can install most dependencies with:
```bash
sudo dnf install podman qemu-user-static sgdisk guestfish fakeroot make wget unzip python3 abootimg
```
## Preparation
You will also need a **stock factory SD card image** from your device. This is
required to extract the proprietary H700 components (SPL, U-Boot, kernel,
modules, firmware) that cannot be open-sourced.
### Configuration
## Build Instructions
Some extra configuration must be provided to connect to a WiFi network and
allow SSH connections.
### 1. Obtain a Factory Image
Anything under the `config` directory will be injected in the rootfs image.
Download the stock firmware image for your device. Place it in the project
root directory and name it `factory.img`, or note its path for the next step.
A good staring point would be something like this (replace `$ssid` and
`$password` with the correct network name and password for your WiFi network):
### 2. Build the Image
Run the build with:
```bash
make FACTORY_IMAGE=/path/to/factory.img
```
If you named your factory image `factory.img` in the project root, you can
simply run:
```bash
make
```
### 3. Flash the Image
The resulting image will be saved to `./artifacts/alarm-h700.img`. Flash it
to an SD card (replace `/dev/sde` with your SD card device):
```bash
dd if=artifacts/alarm-h700.img of=/dev/sde bs=1M oflag=dsync status=progress
```
## Default Credentials
The built image includes a default user for SSH access:
- **Username:** emulation
- **Password:** emulation
The root user is disabled by default. To gain root access, SSH in as the
`emulation` user and use `sudo`:
```bash
ssh emulation@<device-ip>
sudo su -
```
## Adding ROMs
### Method 1: Mount the Data Partition
The image includes a data partition that can be mounted to copy ROMs:
1. SSH into the device
2. Mount the data partition:
```bash
sudo mount /dev/mmcblk0p5 /mnt
```
3. Copy ROMs to the appropriate system directories under `/mnt/roms/`:
- `nes/` - Nintendo Entertainment System
- `snes/` - Super Nintendo
- `genesis/` - Sega Genesis / Mega Drive
- `gba/` - Game Boy Advance
- `n64/` - Nintendo 64
- `psx/` - PlayStation
- And many more (see `scripts/rom-dirs.txt` for the full list)
4. Unmount when done:
```bash
sudo umount /mnt
```
### Method 2: Use a Second SD Card
Insert a second SD card formatted as exFAT or ext4. The system will
automatically mount it (if configured in `/etc/fstab` or via udev rules)
and you can copy ROMs to it.
## Adding BIOS Files
Some emulators require BIOS files to function. Place BIOS files in the
appropriate locations:
- **RetroArch system directory:** `/home/emulation/.config/retroarch/system/`
- **ES-DE bios directory:** `/home/emulation/.emulationstation/bios/`
Common BIOS file locations by system:
- PSX: `scph5500.bin`, `scph7001.bin`, etc.
- NeoGeo: `neogeo.zip`
- Amiga: `kick31.rom` (Amiga 500), `kick31.rom` (Amiga 1200)
- Genesis: `mvsf.bin`, `us_scd1_9210.bin` (for Sega CD)
## Connecting to WiFi
Connect to WiFi using NetworkManager via SSH:
1. SSH into the device:
```bash
ssh emulation@<device-ip>
```
2. Use nmtui to connect to WiFi:
```bash
sudo nmtui
```
3. Select "Activate a connection" and choose your WiFi network
4. Enter your WiFi password when prompted
Alternatively, you can configure WiFi manually:
```bash
# Create WiFi configuration
sudo nmcli dev wifi connect "YourSSID" password "YourPassword"
```
## Configuration
### Customizing WiFi
Place your WiFi configuration in the `config` directory before building:
```bash
```shell
mkdir -p config/etc/wpa_supplicant
wpa_passphrase 'YourSSID' 'YourPassword' > config/etc/wpa_supplicant/wpa_supplicant.conf
wpa_passphrase '$ssid' '$password' >config/etc/wpa_supplicant/wpa_supplicant.conf
chmod 600 config/etc/wpa_supplicant/wpa_supplicant.conf
```
### SSH Keys
Add your SSH public key for passwordless login:
```bash
mkdir -p config/root/.ssh
cat ~/.ssh/id_*.pub > config/root/.ssh/authorized_keys
cat ~/.ssh/id_*.pub >config/root/.ssh/authorized_keys
chmod 700 config/root config/root/.ssh
```
### Customizing the Build
### Stock SD card image
The Makefile supports several tunable parameters:
The scripts expect to find an image of the stock SD card in a file called
`factory.img` in the root of the repository. It can be either an image file or
a link to the actual image file or device, as long as the user running the
scripts has permission to read it. The name of the image can be overridden by
specifying a different path in the `FACTORY_IMAGE` variable when calling the
makefile.
- `FACTORY_IMAGE` - Path to the stock factory image (required)
- `SD_SIZE` - Size of the final SD card image (default: 4G)
- `ROOTFS_SIZE` - Size of the root filesystem (default: 2G)
- `DATA_SIZE` - Size of the data partition (default: 512M)
## Usage
Example with custom sizes:
To build an image, just run:
```bash
make FACTORY_IMAGE=/path/to/factory.img SD_SIZE=8G ROOTFS_SIZE=4G DATA_SIZE=2G
```shell
make
```
## Partition Layout
Or, to use a path to the stock image other than the default:
The resulting SD card image has the following partition layout:
| Partition | Size | Filesystem | Description |
|-----------|--------|------------|--------------------------------|
| 1 | 1MB | - | MBR + partition table |
| 2 | 1MB | - | SPL (Second Program Loader) |
| 3 | 2MB | - | U-Boot bootloader |
| 4 | 16MB | vfat | Boot resources (logos, fonts) |
| 5 | 64MB | vfat | Boot partition (kernel, initrd) |
| 6 | 2GB | ext4 | Root filesystem |
| 7 | 512MB | exfat | Data partition (ROMs) |
## Troubleshooting
### Image Does Not Boot
- Ensure you extracted the correct factory image for your specific device
- Verify the SD card was flashed correctly with `dd`
- Try a different SD card (some cheap cards have issues)
### Cannot Connect to WiFi
- Check that the WiFi antenna is properly connected (if applicable)
- Verify your WiFi credentials are correct
- Try using `nmtui` to manually connect
### Emulators Not Working
- Ensure ROM files are in the correct directories
- Check that required BIOS files are present
- Verify file permissions (ROMs should be readable)
### SSH Connection Refused
- Ensure the device is on the same network
- Check that NetworkManager connected to WiFi
- Verify SSH is running: `sudo systemctl status sshd`
## Project Structure
```
.
├── artifacts/ # Build output directory
├── config/ # Files to inject into the rootfs
├── resources/ # Boot resources (logos, fonts)
├── scripts/ # Build and installation scripts
│ ├── install-cores.sh # Downloads libretro cores
│ ├── install-esde.sh # Downloads ES-DE
│ ├── _build-alarm.sh # Creates ALARM rootfs
│ └── ...
├── README.md # This file
├── Makefile # Build orchestration
└── env.txt # U-Boot environment variables
```shell
make FACTORY_IMAGE=/tmp/RG35XX+-P-V1.1.3-EN16GB-240614.IMG
```
## License
The resulting image will be saved to `./artifacts/alpine-h700.img` and can be
flashed to an SD card, for example, if the card is presented to the system as
`/dev/sde`:
See the UNLICENSE file for details.
```shell
dd if=artifacts/alpine-h700.img of=/dev/sde bs=1M oflag=dsync status=progress
```
There are other tunable settings: for an exaustive list, see the top of
`Makefile`. Keep in mind that tweaking those values between builds may require
forcing a clean build by issuing a `make clean`.

View File

@ -1,3 +0,0 @@
[Service]
ExecStart=
ExecStart=-/sbin/agetty --autologin emulation --noclear %I $TERM

View File

@ -1,12 +0,0 @@
[Unit]
Description=ROM Storage (Primary SD)
After=local-fs.target
[Mount]
What=/dev/mmcblk0p5
Where=/roms
Type=auto
Options=defaults,noatime
[Install]
WantedBy=multi-user.target

View File

@ -1,11 +0,0 @@
[Unit]
Description=ROM Storage (Second SD Card)
[Mount]
What=/dev/mmcblk1p1
Where=/roms2
Type=auto
Options=defaults,noatime,nofail
[Install]
WantedBy=multi-user.target

View File

@ -1,2 +0,0 @@
# Auto-detect second SD card (mmcblk1p1) and trigger mount
ACTION=="add", KERNEL=="mmcblk1p1", TAG+="systemd", ENV{SYSTEMD_WANTS}="roms2.mount"

View File

@ -1,4 +0,0 @@
# Auto-start X on tty1
if [ -z "$DISPLAY" ] && [ "$(tty)" = "/dev/tty1" ]; then
exec startx
fi

View File

@ -1,22 +0,0 @@
# RetroArch configuration for H700
video_driver = "gl"
video_fullscreen = "true"
video_vsync = "true"
# Input
input_autodetect_enable = "true"
input_joypad_driver = "udev"
# Directories
system_directory = "/roms/bios"
savefile_directory = "/roms/saves"
savestate_directory = "/roms/saves"
screenshot_directory = "/roms/screenshots"
libretro_directory = "/home/emulation/.config/retroarch/cores"
libretro_info_path = "/usr/share/libretro/info"
# Audio
audio_driver = "alsa"
# Menu
menu_driver = "ozone"

View File

@ -1,8 +0,0 @@
#!/bin/bash
# Disable screen blanking
xset s off
xset -dpms
xset s noblank
# Launch EmulationStation-DE
exec /usr/local/bin/EmulationStation-DE.AppImage --no-sandbox

1
mkinitfs.conf Normal file
View File

@ -0,0 +1 @@
features="base"

49
scripts/_build.sh Executable file
View File

@ -0,0 +1,49 @@
#!/bin/sh
# This script runs inside an aarch64 alpine linux container and creates a
# base rootfs tarball with wifi support and an ssh daemon active at boot.
set -e
target="$(mktemp -d)"
trap 'rm -rf "$target"' 0
apk update
apk -X "$(awk '/\/alpine\/[^\/]+\/main$/{print;exit}' /etc/apk/repositories)" \
--keys-dir /etc/apk/keys -U -p "$target/" --initdb \
add alpine-base alpine-release ca-certificates wpa_supplicant dropbear dropbear-scp
cp /etc/apk/repositories "$target/etc/apk/"
cat >"$target/etc/motd" <<__EOF__
Welcome to Alpine!
This is an unofficial port to the Allwinner H700 SoC: please report
issues to https://github.com/mporrato/alpine-h700 .
The Alpine Wiki contains a large amount of how-to guides and general
information about administrating Alpine systems.
See <https://wiki.alpinelinux.org/>.
You may change this message by editing /etc/motd.
__EOF__
echo "nameserver 8.8.8.8" >"$target/etc/resolv.conf"
sed -Ei 's/^(tty[0-9]+:)/#\1/;s/^#ttyS0:/ttyS0:/' "$target/etc/inittab"
echo h700 >"$target/etc/hostname"
echo 8821cs >"$target/etc/modules"
cat >"$target/etc/network/interfaces" <<__EOF__
auto lo
iface lo inet loopback
auto wlan0
iface wlan0 inet dhcp
__EOF__
for svc in devfs dmesg mdev ; do
chroot "$target" rc-update add "$svc" sysinit
done
for svc in hwclock modules sysctl hostname bootmisc syslog wpa_supplicant networking ; do
chroot "$target" rc-update add "$svc" boot
done
for svc in mount-ro killprocs savecache ; do
chroot "$target" rc-update add "$svc" shutdown
done
chroot "$target" rc-update add dropbear default
tar cf "${1:-/tmp/rootfs.tar}" -C "$target" .

7
scripts/build-rootfs.sh Executable file
View File

@ -0,0 +1,7 @@
#!/bin/bash
podman run --arch=aarch64 --security-opt=label=disable \
-v "${2:-./artifacts}:/artifacts" \
-v ./scripts/_build.sh:/usr/local/bin/_build.sh \
--rm "docker.io/library/alpine:${1:-latest}" \
sh /usr/local/bin/_build.sh /artifacts/rootfs.tar

View File

@ -1,60 +0,0 @@
81_libretro.so
atari800_libretro.so
bluemsx_libretro.so
cap32_libretro.so
daphne_libretro.so
dosbox_pure_libretro.so
easyrpg_libretro.so
ecwolf_libretro.so
fake08_libretro.so
fbneo_libretro.so
fceumm_libretro.so
flycast_libretro.so
freechaf_libretro.so
freeintv_libretro.so
fuse_libretro.so
gambatte_libretro.so
gearcoleco_libretro.so
genesis_plus_gx_libretro.so
handy_libretro.so
hatari_libretro.so
lowresnx_libretro.so
mame_libretro.so
mednafen_pce_fast_libretro.so
mednafen_pcfx_libretro.so
mednafen_supergrafx_libretro.so
mednafen_vb_libretro.so
mednafen_wswan_libretro.so
melonds_libretro.so
mgba_libretro.so
mupen64plus_next_libretro.so
neocd_libretro.so
nestopia_libretro.so
np2kai_libretro.so
o2em_libretro.so
opera_libretro.so
pcsx_rearmed_libretro.so
picodrive_libretro.so
pokemini_libretro.so
potator_libretro.so
ppsspp_libretro.so
prboom_libretro.so
prosystem_libretro.so
puae_libretro.so
px68k_libretro.so
race_libretro.so
sameduck_libretro.so
scummvm_libretro.so
snes9x_libretro.so
stella_libretro.so
tic80_libretro.so
uzem_libretro.so
vecx_libretro.so
vemulator_libretro.so
vice_x128_libretro.so
vice_x64_libretro.so
vice_xplus4_libretro.so
vice_xvic_libretro.so
virtualjaguar_libretro.so
x1_libretro.so
yabasanshiro_libretro.so

View File

@ -1,28 +0,0 @@
#!/bin/bash
# Download pre-compiled aarch64 libretro cores and package them as a tarball.
set -e
outdir="${1:-./artifacts}"
cores_dir="$(mktemp -d)"
trap 'rm -rf "$cores_dir"' 0
script_dir="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
base_url="https://raw.githubusercontent.com/christianhaitian/retroarch-cores/master/aarch64"
target_dir="$cores_dir/home/emulation/.config/retroarch/cores"
mkdir -p "$target_dir"
while IFS= read -r core; do
[ -z "$core" ] && continue
[[ "$core" =~ ^# ]] && continue
echo "Downloading $core..."
if wget -q "$base_url/${core}.zip" -O "/tmp/${core}.zip" 2>/dev/null; then
unzip -o -q "/tmp/${core}.zip" -d "$target_dir/"
rm -f "/tmp/${core}.zip"
else
echo "Warning: Failed to download $core, skipping"
fi
done < "$script_dir/core-list.txt"
tar cf "$outdir/cores.tar" -C "$cores_dir" .
echo "Cores tarball created at $outdir/cores.tar"

View File

@ -1,45 +0,0 @@
#!/bin/bash
# Download and package EmulationStation-DE for aarch64.
set -e
outdir="${1:-./artifacts}"
esde_dir="$(mktemp -d)"
trap 'rm -rf "$esde_dir"' 0
script_dir="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
# Download the latest ES-DE AppImage for aarch64
esde_url="https://gitlab.com/es-de/emulationstation-de/-/package_files/latest"
# Use the releases page to find the latest aarch64 AppImage
echo "Fetching latest ES-DE release info..."
# Try to get the latest release from GitLab
# Fallback: use a known stable version
ESDE_VERSION="${ESDE_VERSION:-3.1.1}"
ESDE_APPIMAGE="EmulationStation-DE-x64_SteamDeck-${ESDE_VERSION}.AppImage"
ESDE_URL="https://gitlab.com/es-de/emulationstation-de/-/releases/v${ESDE_VERSION}/downloads/${ESDE_APPIMAGE}"
# For aarch64, we need the Linux build
# ES-DE provides aarch64 AppImages
ESDE_APPIMAGE_ARM="EmulationStation-DE-aarch64-${ESDE_VERSION}.AppImage"
ESDE_URL_ARM="https://gitlab.com/es-de/emulationstation-de/-/releases/v${ESDE_VERSION}/downloads/${ESDE_APPIMAGE_ARM}"
mkdir -p "$esde_dir/usr/local/bin"
mkdir -p "$esde_dir/home/emulation/.emulationstation"
echo "Downloading ES-DE ${ESDE_VERSION} for aarch64..."
if ! wget -q "$ESDE_URL_ARM" -O "$esde_dir/usr/local/bin/EmulationStation-DE.AppImage" 2>/dev/null; then
echo "AppImage download failed. Will need to build from source."
echo "Creating placeholder script..."
cat > "$esde_dir/usr/local/bin/emulationstation" << 'EOF'
#!/bin/bash
echo "ES-DE not installed. Please install manually or rebuild with ESDE_BUILD=source"
sleep 5
EOF
fi
chmod +x "$esde_dir/usr/local/bin/"*
mkdir -p "$outdir"
tar cf "$outdir/esde.tar" -C "$esde_dir" .
echo "ES-DE tarball created at $outdir/esde.tar"

View File

@ -1,37 +0,0 @@
#!/bin/bash
# Create the data partition image with ROM directory structure.
set -e
script_dir="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
image="$1"
if [ -z "$image" ]; then
echo "Usage: $0 IMAGE"
exit 1
fi
# Build guestfish commands to create exFAT filesystem with ROM dirs
script=(
"run"
"mkfs vfat /dev/sda label:ROMS"
"mount /dev/sda /"
)
# Create ROM directories
while IFS= read -r dir; do
[ -z "$dir" ] && continue
[[ "$dir" =~ ^# ]] && continue
script+=("mkdir \"/roms/$dir\"")
done < "$script_dir/rom-dirs.txt"
# Create support directories
script+=("mkdir \"/roms/bios\"")
script+=("mkdir \"/roms/saves\"")
script+=("mkdir \"/roms/screenshots\"")
for cmd in "${script[@]}"; do
echo "$cmd"
done | guestfish --format=raw -a "$image"
echo "Data partition image created at $image"

View File

@ -5,7 +5,6 @@
set -e
outdir="${1:-./artifacts}"
mkdir -p "$outdir"
tmp="$(mktemp -d)"
trap 'rm -rf "$tmp"' 0

View File

@ -1,18 +1,18 @@
#!/bin/bash
# Assemble SPL, U-Boot, U-Boot environment, boot-resources, boot, rootfs
# and data images into a final H700 bootable sdcard image.
# Assemble SPL, U-Boot, U-Boot environment, boot-resources, boot and rootfs
# images into a final H700 bootable sdcard image.
set -e
if [[ $# -lt 8 ]] ; then
echo "Usage: $0 SDCARD SPL UBOOT ENV BOOT-RESOURCE BOOT ROOTFS DATA"
exit 0
if [[ $# -lt 7 ]] ; then
echo "Usage: $0 SDCARD SPL UBOOT ENV BOOT-RESOURCE BOOT ROOTFS"
exit 0
fi
mb() {
# Return the file size in MiB (rounded up)
local file_size
file_size="$(stat -c "%s" "$1")"
echo "$(( (file_size + 1024 ** 2 - 1) / 1024 ** 2 ))M"
# Return the file size in MiB (rounded up)
local file_size
file_size="$(stat -c "%s" "$1")"
echo "$(( (file_size + 1024 ** 2 - 1) / 1024 ** 2 ))M"
}
sgdisk --move-main-table=73696 -I \
@ -20,19 +20,18 @@ sgdisk --move-main-table=73696 -I \
--new="0:0:+$(mb "$5")" --change-name=0:boot-resource --attributes=0:=:c000000000000001 --typecode=0:0700 \
--new="0:0:+$(mb "$6")" --change-name=0:boot --attributes=0:=:c000000000000001 --typecode=0:a002 \
--new="0:0:+$(mb "$7")" --change-name=0:rootfs --attributes=0:=:c000000000000001 --typecode=0:8305 \
--new="0:0:+$(mb "$8")" --change-name=0:data --attributes=0:=:c000000000000001 --typecode=0:0700 \
"$1"
part_table="$(sgdisk -p "$1" | awk '/^ +[0-9]+ +[0-9]+ +[0-9]+/{print $2 / 2}' | xargs)"
part_table="$(sgdisk -p artifacts/alpine-h700.img | awk '/^ +[0-9]+ +[0-9]+ +[0-9]+/{print $2 / 2}' | xargs)"
read -r -a offsets <<<"$part_table"
images=("$2" "$3" "$4" "$5" "$6" "$7" "$8")
images=("$2" "$3" "$4" "$5" "$6" "$7")
offsets=(8 16400 "${offsets[@]}")
for i in "${!images[@]}"; do
img="${images[$i]}"
off="${offsets[$i]}"
echo "Writing $img at offset ${off}KiB"
dd if="$img" of="$1" bs=1K seek="${off}" conv=sparse,notrunc status=none
img="${images[$i]}"
off="${offsets[$i]}"
echo "Writing $img at offset ${off}KiB"
dd if="$img" of="$1" bs=1K seek="${off}" conv=sparse,notrunc status=none
done
echo "Your bootable SD image is $1"

View File

@ -1,88 +0,0 @@
3do
amiga
amigacd32
amstradcpc
amstradgx4000
arcade
astrocde
atari2600
atari5200
atari7800
atari800
atarijaguar
atarilynx
atarist
atarixegs
atomiswave
c128
c16
c64
channelf
coleco
daphne
doom
dos
dreamcast
easyrpg
famicom
fbneo
fds
gamegear
gb
gba
gbc
genesis
intellivision
lowresnx
mame
mastersystem
megadrive
megaduck
msx
msx2
n64
naomi
nds
neogeo
neogeocd
nes
ngp
ngpc
odyssey2
openbor
pc
pc98
pcengine
pcenginecd
pcfx
pico8
pokemini
ports
psp
psx
satellaview
saturn
scummvm
sega32x
segacd
sfc
sg1000
snes
sufami
supergrafx
supervision
tg16
tg16cd
tic80
uzebox
vectrex
vic20
videopac
virtualboy
wonderswan
wonderswancolor
wolf3d
x1
x68000
zx81
zxspectrum