Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Quick Start

Let’s create your first Dreamcast Go program.

Create a Project

mkdir myproject && cd myproject
godc init

Example output:

$ godc init
go: found kos in kos v0.0.0-00010101000000-000000000000

This creates go.mod and go.work files that configure your project to use the kos package from your libgodc installation.

Project Structure

A minimal project looks like this:

myproject/
├── go.mod            # Module definition with kos dependency
├── go.work           # Workspace configuration
└── main.go           # Your code

The go.mod file (paths will match your libgodc location):

module myproject

go 1.25.3

replace kos => /path/to/your/libgodc/kos

require kos v0.0.0-00010101000000-000000000000

The go.work file:

go 1.25.3

use (
        /path/to/your/libgodc
        .
)

Note: The paths in go.mod and go.work will automatically point to your libgodc installation location.

Hello, Dreamcast!

Create main.go:

package main

import "kos"

func main() {
    kos.PvrInitDefaults()
    println("Hello, Dreamcast!")
    for {}
}

Build and Run

Using godc:

godc build            # Compile to .elf
godc run              # Launch in emulator

Or manually with sh-elf-gccgo:

sh-elf-gccgo -O2 -ml -m4-single -fno-split-stack -mfsrra -mfsca \
    -I$KOS_BASE/lib -L$KOS_BASE/lib \
    -c main.go -o main.o

kos-cc -o myproject.elf main.o \
    -L$KOS_BASE/lib -Wl,--whole-archive -lgodcbegin \
    -Wl,--no-whole-archive -lkos -lgodc

Your First Graphics

Let’s draw something on screen:

package main

import "kos"

func main() {
    kos.PvrInitDefaults()
    
    for {
        kos.PvrWaitReady()
        kos.PvrSceneBegin()
        
        // Draw opaque geometry
        kos.PvrListBegin(kos.PVR_LIST_OP_POLY)
        drawTriangle()
        kos.PvrListFinish()
        
        kos.PvrSceneFinish()
    }
}

func drawTriangle() {
    // Create and submit polygon header
    var hdr kos.PvrPolyHdr
    var ctx kos.PvrPolyCxt
    kos.PvrPolyCxtCol(&ctx, kos.PVR_LIST_OP_POLY)
    kos.PvrPolyCompile(&hdr, &ctx)
    kos.PvrPrim(&hdr)  // Submit header
    
    // Submit vertices (use PvrPrimVertex for vertices)
    v := kos.PvrVertex{
        Flags: kos.PVR_CMD_VERTEX,
        X: 320, Y: 100, Z: 1,
        ARGB: 0xFFFF0000,  // Red
    }
    kos.PvrPrimVertex(&v)
    
    v.X, v.Y = 200, 400
    v.ARGB = 0xFF00FF00  // Green
    kos.PvrPrimVertex(&v)
    
    v.X, v.Y = 440, 400
    v.Flags = kos.PVR_CMD_VERTEX_EOL  // End of strip
    v.ARGB = 0xFF0000FF  // Blue
    kos.PvrPrimVertex(&v)
}

Using Goroutines

Goroutines work on Dreamcast:

package main

import "kos"

func main() {
    kos.PvrInitDefaults()
    
    // Start a background goroutine
    go func() {
        counter := 0
        for {
            counter++
            println("Background:", counter)
            select {}  // Yield to scheduler
        }
    }()
    
    // Main loop
    for {
        kos.PvrWaitReady()
        kos.PvrSceneBegin()
        render()
        kos.PvrSceneFinish()
    }
}

Using Channels

Channels enable communication between goroutines:

package main

import "kos"

func main() {
    kos.PvrInitDefaults()
    
    // Create a buffered channel
    scores := make(chan int, 10)
    
    // Score counter goroutine
    go func() {
        total := 0
        for score := range scores {
            total += score
            println("Total score:", total)
        }
    }()
    
    // Main game loop
    for {
        // Game logic
        if playerScored() {
            scores <- 100  // Send score
        }
        render()
    }
}

Next Steps