Writting software for the Sega Dreamcast using free tools


Version 0.01 -- May 09, 2002

http://www.atani-software.net/

Contents

1 Introduction

First and foremost, there are references in the document to copywritten materials. These materials are summarized later in the document. All references to copywritten materials are copyright their respective owners.

This document is divided up into 4 major segments, each covering a different aspect of developing software for the Sega Dreamcast. These segments are: Introduction, Using KOS, Using libronin, and Writing your own library.

1.1 Getting start with development

The first requirement in starting to write software is to know the C or C++ language. If you do not know the C or C++ language it is recommended that you stop reading this document and proceed directly to learning how to read/write C or C++.

1.2 Building the GCC tool chain

In order to develop software for the Sega Dreamcast it is required that you have a correctly configured GCC compiler for the SH4 and ARM processors. This section documents what and how to build each component and where you can download pre-built versions of the libraries and or compilers.

1.2.1 For Microsoft Windows systems

When developing on a Windows system you have two options for your development environment. The first option is to use Cygwin. While this option is the most common it has been reported that you have the ability to use a pre-compiled version of the tool-chain available http://www.kpit.com/products/support.htm

If you choose to not use the pre-built tool-chain above you should download and install the Cygwin environment on your PC. Once you have a working Cygwin environment you should then follow the UNIX instructions below for using a ``build script'' to download, build, and install the compiler for you. This is the fastest/easiest way to get up and running on a Windows system with Cygwin

1.2.2 For UNIX systems

On UNIX based systems it is extremely simple to build the tool-chain. It is best to use a compilation script that will generate both the SH4 and ARM compilers for you. There are many of these available on the net, they all work about the same and as such I am not going to go into much documentation other than provide links to them:

1.3 Testing your software on the Sega Dreamcast

In order to test your software on the Sega Dreamcast you must send it over the serial cable, broadband adapter/LAN adapter, or burn it to a CD-R/CD-RW.

1.3.1 dc-tool

Andrew Kieschnick is the author of many tools that are in heavy use today. These tools include both dc-tool and dc-load. dc-tool is the serial upload slave and dc-load is the Broadband adapter upload slave. You can download the latest version of dc-tool or dc-load from the following site:

http://www.cerc.utexas.edu/andrewk/dc/

1.3.2 Marcus Comstedt's serial slave

Marcus wrote one of the first tools for sending your program over to the Sega Dreamcast. This tool is available from:

http://mc.pp.se/dc/serslave.html

1.3.3 Purchasing a Serial Upload cable

To purchase a pre-built cable please visit one of the following links:

1.3.4 Building a Serial Upload Cable

Building your own serial cable can be a fun project. There are a handful of sites that will teach you how to build a cable from a NeoGeo Pocket to Dreamcast link cable and also a couple on how to use two video cables to create a serial cable.

1.3.5 What about the Broadband adapter/LAN Adapter?

While a serial cable is the most ideal solution, and most economical for most people, the Broadband Adapter/LAN Adapter will also work for sending programs to the Dreamcast. The added bonus of using a Broadband Adapter/LAN Adapter is that you have added speed, 10Mbps vs 56kbps. While the speed is a nice thing, the price of a Broadband/LAN Adapter will likely make this option a bit out of range for most people. The typical price for the Broadband Adapter/LAN Adapter is anywhere from $60 to $150 USD. Many people do not make any distinguishment between the Broadband Adapter and the LAN Adapter but there are some fairly major differences.

1.3.6 Purchasing a Broadband Adapter/LAN Adapter

If you are still inclined to purchase a Broadband Adapter/LAN Adapter then you will want to purchase one from the following links:

I would recommend using Lik-Sang as it can be more reliable for purchases than eBay. I have not personally had any problems with eBay but there are reports of others having problems. Broadband Adapters/LAN Adapters are a very fast moving commodity on eBay so be sure to check the reported feedback for anyone you are thinking of purchasing one from on eBay. If they do not have any feedback or have some negative feedback that is not justified I would strongly urge you not to purchase from them. Sorry for those who are trying to sell items on eBay and fall into this category.

2 Using KOS as your development library

2.1 What is KOS

KOS is one of the more complete solutions for developers wanting to write software for the Sega Dreamcast. While there are a few missing features this is by far the fastest way for many people to get some software to work on the Sega Dreamcast.

2.2 Obtaining the KOS source code

KOS can be obtained in source form in two ways:

  1. Using the latest CVS version.
  2. Latest ``stable'' release posted on Sourceforge.net
If you use the CVS version it is wise to always work on the latest version. To do this you should use the following commands to setup your KOS CVS tree:

  1. cvs -d:pserver:[email protected]:/cvsroot/cadcdev login
  2. cvs -z6 -d:pserver:ano[email protected]:/cvsroot/cadcdev checkout kos
  3. cd kos/kos
  4. make
Once you have a working KOS CVS tree you should follow these steps to ``update'' it:

  1. cd kos/kos
  2. make clean
  3. cvs -d:pserver:[email protected]:/cvsroot/cadcdev login
  4. cvs -z6 -d:pserver:a[email protected]:/cvsroot/cadcdev update -Pd
  5. make

2.3 Obtaining pre-built KOS binaries

Binaries are available from http://sourceforge.net/projects/cadcdev/under the ``files'' section. Simply download the ``stable'' version you are interested in, extract and then start working with it. More information on this will be given in the next segment.

2.4 Configuring the KOS environment

KOS has to be one of the easiest libraries to configure. There is a shell script which you can run in either Cygwin or on any UNIX system. The script will require a few modifications to tailor it to your system. Below is a listing of the variables that you will likely need to modify:

You can find a template file to modify in: kos/kos/doc/environ-dc.sh.sample (or as environ-dc.tcsh.sample). If you are on a windows system and are using the pre-built toolchain you will need to open up the sample script and set environment variables for each entry listed in the sample file.

2.5 Your first program

The first program you will likely create will be a hello world program. Here is the source code you would write and an example of its usage:

#include <kos.h>;

KOS_INIT_FLAGS(INIT_DEFAULT);

int main(int argc, char **argv)
{
        printf("\nHello world!\n\n");

        return 0;

}

The output of this program should be nothing to the the TV screen but it should print ``Hello world!'' to your console window which started the program.

2.6 More advanced uses of KOS

KOS includes a fairly large set of example programs so examples of KOS' usage is not being done in this document except for the example above.

3 Using libronin as your development library

3.1 What is libronin

libronin is a derivative effort of the DreamSNES team to produce a second library which provides functionality to developers for writting software for the Sega Dreamcast. libronin has two forms, libronin.a and libronin-noserial.a. The libronin.a is best suited for development and testing of your software, however, when you release your software to the general public it is advised that you use the libronin-noserial.a file as it will render a faster program in the end due to not having serial output enabled.

3.2 Obtaining libronin

The binaries are currently all that is publicly distributed. These can be obtained from:

http://peter.bortas.org/scumm/

3.3 Your first program

This section will be broken down to many smaller pieces which will go over specifics of the library.

3.3.1 Hello World

The first example program to be created is as always a hello world program:

#include ``ronin/ronin.h''
/* This is the default entry point for your program */
int main(int argc, char **argv)
{
        serial_init(57600);
        usleep(200000);
        report("\nHello world!\n\n");
        return 0;
}

The ``serial_init(57600);'' line will initialize the serial port for read/write at 57600 bps. After you initialize the serial port it is mandatory that you sleep for at least two seconds to ensure stability of the line. The ``report'' call is essentially just a call to ``printf(<string>);'' except that it is sent across the serial line to the PC. This also is short circuited when you are not using the serial version of libronin.

3.3.2 Initialize and read from the cdrom

In this example we will initialize and read from the cdrom the contents of the root directory.

#include ``ronin/ronin.h''

/* This is the default entry point for your program */
int main(int argc, char **argv)
{
        serial_init(57600);
        usleep(200000);
        cdfs_init();

        DIR *dir = opendir(``/'');
        if(dir != null)
        {
                struct dirent *ent;
                ent = readdir(dir);
                while(ent != null)
                {
                reportf(``File is: %s [%u]\r\n'', ent->d_name,ent->d_size);
                ent = readdir(dir);
                }
                closedir(dir);
        }
        return 0;
}

The call to ``cdfs_init();'' will initialize the cdrom functions within the libronin library. The call to ``opendir(<path>)'', ``readdir(<DIR>)'', ``closedir(<DIR>)'' are all identical to that of the same calls on a linux/POSIX system. The path you pass to ``opendir(<path>)'' starts at the root of the cdrom. Be sure to check the return values from ``opendir(<path>)'' and ``readir(<DIR>)'' as exceptions can be thrown with invalid pointer accesses.

3.3.3 Using the Tile Accelerator

In this example we will begin to use the Tile Accelerator to put a colored box on the screen.

#include ``ronin/ronin.h''
/* This is the default entry point for your program */
int render_solid_box(float x1, float y1, float x2, float y2, int color)
{
        struct polygon_list poly;
        struct packed_colour_vertex_list vert;
        // Initialize to some sane values: it is an opaque colored polygon, no texture.
        poly.cmd = TA_CMD_POLYGON | TA_CMD_POLYGON_TYPE_OPAQUE |
                TA_CMD_POLYGON_STRIPLENGTH_2 | TA_CMD_POLYGON_PACKED_COLOUR |
                TA_CMD_POLYGON_GOURAUD_SHADING;
        poly.mode1 = TA_POLYMODE1_Z_ALWAYS | TA_POLYMODE1_NO_Z_UPDATE;
        poly.mode2 = TA_POLYMODE2_BLEND_SRC | TA_POLYMODE2_FOG_DISABLED;

        // Default the texture to no texture
        poly.texture = 0;
        // Set the polygon color to 0 (black)
        poly.red = poly.green = poly.blue = poly.alpha = 0;
        // Commit this polygon to the Tile Accelerator
        ta_commit_list(&poly);
        // Default the vertex information to something sensable.
        vertex.cmd = TA_CMD_VERTEX;
        vertex.ocolour = 0;
        vertex.z = 0.5;
        vertex.u = 0.0;
        vertex.v = 0.0;
        vertex.colour = color;

        // submit the upper left corner
        vertex.x = x1;
        vertex.y = y1;
        ta_commit_list(&vertex);

        // submit the upper right corner
        vertex.x = x2;
        ta_commit_list(&vertex);

        // submit the lower left corner
        vertex.x = x1;
        vertex.y = y2;
        ta_commit_list(&vertex);

        // submit the lower right corner
        vertex.x = x2;
        vertex.cmd |= TA_CMD_VERTEX_EOS;
        ta_commit_list(&vertex);
}

/* This is the default entry point for your program */
int main(int argc, char **argv)
{
        serial_init(57600);
        usleep(200000);
        dc_setup_ta();
        for(;;)
        {
                ta_sync();
                ta_begin_frame();
                // begin opaque poly list
                render_solid_box(20.0, 20.0, 620.0, 460.0, 0xFFFFFFFF); // render a white box to the screen
                ta_commit_end();

                // begin translucent poly list
                commit_dummy_transpoly();
                ta_commit_end();
                // commit the frame to the Tile Accelerator
                ta_commit_frame();
        }
        return 0;
}

The ``dc_setup_ta()'' call will initialize and configure the libronin library for using the Tile Accelerator chip in the Sega Dreamcast. The call to ``ta_sync()'' will ensure the Tile Accelerator is ready for a new frame of data to be written. The call to ``ta_begin_frame()'' initializes the frame buffers for the lists of opaque and transparent polygons which are to be rendered.

When you use the Tile Accelerator it is mandatory that you do not send it an empty list. It is rare that you will not have at least one opaque polygon during a given frame so there is no function provided for submitting an empty polygon for the opaque list, however, in the case of translucent polygons there is a function ``commit_dummy_transpoly()'' which will generate and submit an empty translucent polygon.

3.3.4 Using Maple devices

libronin has one drawback on it that KOS doesn't have, macros for the directions, buttons, etc on the controller. Other than this the maple implementation is very solid and works flawlessly for every application I could think of.. Here are the defines you will need to have to make life and code easier to handle:

/* Buttons bitfield defines */
#define CONT_C                  (1 << 0)
#define CONT_B                  (1 << 1)
#define CONT_A                  (1 << 2)
#define CONT_START              (1 << 3)
#define CONT_DPAD_UP            (1 << 4)
#define CONT_DPAD_DOWN          (1 << 5)
#define CONT_DPAD_LEFT          (1 << 6)
#define CONT_DPAD_RIGHT         (1 << 7)
#define CONT_Z                  (1 << 8)
#define CONT_Y                  (1 << 9)
#define CONT_X                  (1 << 10)
#define CONT_D                  (1 << 11)
#define CONT_DPAD2_UP           (1 << 12)
#define CONT_DPAD2_DOWN         (1 << 13)
#define CONT_DPAD2_LEFT         (1 << 14)
#define CONT_DPAD2_RIGHT        (1 << 15)

Without these defines you will end up having to use cryptic code like : ``if(buttons & 4) do something''. Now with these defines you can write it as ``if (buttons & CONT_A) do something''

Here is an example using the above defines to read controller input:

/* This is the default entry point for your program */
int main(int argc, char **argv)
{
        int mask, i;
        struct mapledev *pads;

        serial_init(57600);
        usleep(200000);

        maple_init();

        for(;;)
        {
                mask = getimask();
                setimask(15);
                pads = get_locked_pads();
                for(i = 0; i < 4; i++)
                {
                        if(pads[i]->func & MAPLE_FUNC_CONTROLLER)
                        {
                                int buttons = pads[i]->cond.controller.buttons;
                                reportf(``Controller %u state:\r\n'', i);
                                reportf(``UP: %u DOWN: %u LEFT: %u RIGHT: %u\r\n'',
                                        buttons & CONT_DPAD_UP,
                                        buttons & CONT_DPAD_DOWN,
                                        buttons & CONT_DPAD_LEFT,
                                        buttons & CONT_DPAD_RIGHT);
                                reportf(``UP2: %u DOWN2: %u LEFT2: %u RIGHT2: %u\r\n'',
                                        buttons & CONT_DPAD2_UP,
                                        buttons & CONT_DPAD2_DOWN,
                                        buttons & CONT_DPAD2_LEFT,
                                        buttons & CONT_DPAD2_RIGHT);
                                reportf(``A: %u B: %u X: %u Y: %u\r\n'',
                                        buttons & CONT_A,
                                        buttons & CONT_B,
                                        buttons & CONT_X,
                                        buttons & CONT_Y);
                        }
                }
                setimask(mask);
        }
}

The above example is a fairly simple example showing how you would read the state of the controllers and dump it to the console. The usage of ``setimask'' and ``getimask'' is required to ensure that we are not interrupted while we have the pad struct locked.

3.3.5 Using the VMU for save data

This section of the doc is not available at this time. More information will be given shortly.

3.3.6 Using the sound system

This section of the doc is not available at this time. More information will be given shortly.

4 Writting your own library

Writing your own library to drive the Sega Dreamcast hardware is somewhat difficult and long process of developing re-usable pieces of code that do direct hardware interaction. This is generally not recommended for beginning developers but more for the advanced developers who have lots of system level knowledge and/or have access to documentation which provides accurate information regarding the hardware. Some documentation is available from:

http://mc.pp.se/dc/hw.html

5 Copyright information

There are many references within this document to copywritten materials, this section lists the references and who owns the copywrite for the references.

http://www.sega.com/

http://www.cygwin.com/ http://www.redhat.com/software/tools/cygwin/

http://sourceforge.net/

http://www.microsoft.com/

http://peter.bortas.org/scumm/

http://dcdev.allusion.net/

http://www.snk.com/