Home
Admin | Edit

Dreamcast low-level dev.

Contents

Introduction


The Dreamcast can be a challenging target for codegolfing, the SH-4 CPU is a traditional RISC to the core with 16-bit instruction size, this is good for codegolfing but being RISC and without the "hacks" of similar RISC CPU such as ARM (barrel shifter) there is constraints such as :
All these constraints are somehow balanced by the 16-bit instruction size but some of these can still become a pain for codegolfing...

The good stuff for codegolfing is relative to the amount of available registers and instructions such as the floating-point instructions (2-way superscalar architecture, SIMD), there is also no headers. (unless you count the IP.BIN boot program or image file as header)

How to run old intros


There is some 256b and 128b for Dreamcast but they don't run anymore on the emulators i tried, to run them you may have to use official BIOS file and change VIDEO_RAM (and also VIDEO_BASE) constant to 0xa5200000 instead of 0xa5000000 then assemble. (see setup below)

The reason is that the Dreamcast BootROM sets video framebuffer base to 0xa5200000 (a 2MB offset from base VRAM), the 2MB below is reserved for a copy of the BootROM to pass bootup checks, it is likely that this wasn't emulated at the time of these intros or that the framebuffer base was already changed to 0xa5000000 due to the method by which they ran. Modifying the framebuffer base to be 0xa5000000 after boot is expected so that all the VRAM is available without fragmentation, this can be safely ignored for code golfing purposes though.

It is not a perfect solution anyway to run these intros because they will run in a tiny area of the screen after this fix, this might be due to the intro being made for a different video mode... didn't look yet but what i know is that after boot the display mode is 640*480 ARGB8888, this might depend on BIOS / video connection type though (cable type in Redream) which was set to RGB for my test.

The alternative to run the original code of these intros is to setup the framebuffer start address and a video mode, this is much bigger obviously, it can be done by looking at dreamhal videos mode setup.

Documentation

Setup (on LINUX)


Here is a guide to setup a Dreamcast development environment, note that i was only interested in running SH4 assembly code for raw graphics stuff so i didn't test anything else :

Emulators

  • Redream seems accurate so i use this, there is also Reicast which works fine

Toolchain

I basically followed this guide up to building KOS examples to build the toolchain on my machine.

Build may fail due to unavailable GNU mirror, this can be easily fixed by using another one by editing the file scripts/download.mk.

The steps i added afterward to be able to build images so the program can be run on the Dreamcast is related to compiling mkdcdisc tool :
  • sudo apt install git meson build-essential pkg-config libisofs-dev
  • git clone https://gitlab.com/simulant/mkdcdisc /opt/toolchains/dc/mkdcdisc
  • cd /opt/toolchains/dc/mkdcdisc
  • CC=gcc-13 CXX=g++-13 meson setup builddir note : you might have to replace gcc and g++ version, i used this because mine were old and compilation failed
  • CC=gcc-13 CXX=g++-13 meson compile -C builddir

Assembly and running your program

/opt/toolchains/dc/sh-elf/bin/sh-elf-as -little test.s -o test.o
/opt/toolchains/dc/sh-elf/bin/sh-elf-ld --oformat elf32-shl -Ttext 0x8c010000 test.o -o test.elf
/opt/toolchains/dc/mkdcdisc/builddir/mkdcdisc -e test.elf -o test.cdi -n "my test disc"

Then you can just call either reicast or redream with the .cdi file as input on the command-line.

Note that you may need the original BIOS file. (emulator provided one didn't work for me so i use a 1998 v1.01d EU BIOS)

Note : There is also the old way (without mkdcdisc) with mkisofs and cdi4dc tools detailed here. cdi4dc tool can be downloaded here for Linux and you will need to generate a valid IP.BIN file as well for this process, makeip is able to do that.

Assembly build shell script

Here is a shell script to assemble a .s, build the .cdi and run it with redream, pass the .s filename without the extension to assemble your code (may want to change redream path as well), it will also output a raw .bin and output the disassembly :

#!/bin/sh
set -eu
/opt/toolchains/dc/sh-elf/bin/sh-elf-as -little $1.s -o $1.o
/opt/toolchains/dc/sh-elf/bin/sh-elf-ld --oformat elf32-shl -Ttext 0x8c010000 $1.o -o $1.elf
/opt/toolchains/dc/mkdcdisc/builddir/mkdcdisc -v 3 -e $1.elf -o $1.cdi -n "" -m
/opt/toolchains/dc/sh-elf/bin/sh-elf-objcopy -R .stack -O binary $1.elf $1.bin
/opt/toolchains/dc/sh-elf/bin/sh-elf-objdump -m sh4 -EL -b binary --adjust-vma=0x8c010000 -D $1.bin
wc -c $1.bin
~/redream.x86_64-linux-v1.5.0-1106-g01ef607/redream $1.cdi

The raw binary size is already shown without wc (as hexadecimal) but i added this call so that it is shown as decimal.

Old versions of mkdcdisc doesn't clean the /tmp directory so might be preferable to check mkdcdisc output to delete the temporary directory that mkdcdisc generate, note that newer versions now clean the temporary files except when the command is interrupted, some CD sized data might still stay in /tmp and fill the drive so might be worth to check out...

This command list the huge temporary files that mkdcdisc might generate (-delete can be added to remove them) :

cd /tmp && find . -maxdepth 2 -name '0.0'

SH-4 basic template for pixels write after boot


This code clear the screen and plot a white centered pixel right after boot, the video mode after boot is 640*480 ARGB8888 but might depend on BIOS / video connection type ? (cable type in Redream), mine was set to RGB here. (composite and VGA also works though)

Note that accessing VRAM directly is slow on the Dreamcast but is handy for raw stuff !

mov.l VIDEO_BASE,r0
mov #0,r1
mov.l CLRCOUNT,r2
clr:
    mov.l r1,@r0
    dt r2
    bf/s clr
    add    #4,r0
    bra    main
    nop

.align 2
    CLRCOUNT: .long 640*480

main:
    mov #-1,r1 ! color; ARGB8888
    mov.l CENTERED_PIXEL_ADDR,r0
    mov.l r1,@r0
    bra main
    nop

.align 2
    VIDEO_BASE: .long 0xa5200000
    CENTERED_PIXEL_ADDR: .long 0xa5200000 + (320 + (240 * 640)) * 4

As a small "trick" you can use r0 to reuse the VIDEO_BASE with indexed register indirect addressing mode :

...

mov.l VIDEO_BASE,r0
main:
    mov #-1,r2
    mov.l CENTERED_PIXEL_ADDR,r1
    mov.l r2,@(r0,r1)
    bra main
    nop

.align 2
    VIDEO_BASE: .long 0xa5200000
    CENTERED_PIXEL_ADDR: .long (320 + (340 * 640)) * 4

back to topLicence Creative Commons