next_inactive up previous


Cryptic Allusion KallistiOS / Programmer's Manual

(c)2000-2001 Dan Potter


Contents

Legal

``Sega'' and ``Dreamcast'' are registered trademarks of Sega Enterprises, Ltd. This package has no association with Sega or any of its affiliates, besides working on their hardware.

``Gameboy Advance'' is a registered trademark of Nintendo, Inc. This package has no association with Nintendo or any of its affiliates, besides working on some of their hardware.

All other trademarks are owned by their respective trademark holders.

KallistiOS (c)2000-2001 Dan Potter, Jordan DeLong, and others. Other portions (c) their individual authors; please contact the authors for information on using and/or distriburing their code.

Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:

  1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
  2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
  3. Neither the name of Cryptic Allusion nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE AUTHORS AND CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

1. Overview

Welcome to KallistiOS! If this documentation looks familiar to users of libdream, that's because it is - this is basically a ``port'' of the documentation, with new sections added as neccessary, and other sections modified.

The following is copied from the README file:

KallistiOS is a pseudo-real-time pseudo-microkernel operating system for gaming consoles, licensed under the terms of the *new* BSD license (the one without the advertising clause). It has currently been ported to the Dreamcast(tm) and Gameboy Advance(tm) platforms.

Depending on whether you wish to see the fnords, you can pronounce it "kallisti-o's" (like a cereal) or "kallisti o s" (like an operating system). We like the former since it's silly to be so serious most of the time =). "Kallisti" means "to the fairest" in Greek. This was the word (so the story goes) that was inscribed on the golden apple that Eris threw into the banquet of the gods to start the Trojan war. This somehow landed her the title of matriarch of a silly religion called Discordianism, which is what the name pays homage to. If you want a short abbreviation, you can also refer to it as "KOS", which can be pronounced "k-os" (chaos =) or just plain old ``k-o-s''.

Note that this name is _not_ to be confused or associated with either the professional development company Kalisto Software* or the cracking group "Kalisto".

Now that that is cleared up... =)

Like most embedded kernels, KallistiOS has two major modes of operation. In the first mode, it can be linked directly into a program. In this case you have very fine control over what gets included in the binary and how and when it is initialized. It is also by far the simplest way to make use of it (hearken back to the days of libdream...). This mode is now considered the default method of using KallistiOS and will be the best supported in the future.

The second mode is potentially more versatile and fun. You can run the standalone KallistiOS binary and have it load programs and multitask like a regular OS. The virtualized file system is used in this loading process, so the first (and subsequent) loaded binaries can be sitting on a CD, embedded in the kernel, or even come across from a PC debugger host.

Note that although "os mode" encourages the use of threads and a file system, it may be taylored to fit your needs. Likewise, the library mode may use threads and the VFS.

2. Getting Started

We assume that you have gotten a working compiler set up. The compiler you need depends entirely on the platform you wish to use KOS with. At the moment, only the DC platform is very well supported. The others may or may not compile at this time. So this document will focus on that aspect of KOS.

The first thing you'll want to do is get a working binary image of the KOS libraries. You may download KOS in binary or source form.

If you downloaded KOS in binary form, then you'll still need a binary for genromfs if you want to use the ROMFS file system (which will probably come in quite handy for development). Linux tends to come with this binary, BSD users can compile one using the copy included with the KOS sources, and Cygwin users will likewise need to compile it or obtain a binary from the same place you got the KOS package.

The first thing you'll want to do in either case is to customize an ``environ'' script. This script is run inside your shell to set various environment variables so that the KOS build scripts and tools know where to find KOS and the compiler toolchains (for DC, an SH-4 compiler, and optionally, an ARM7 compiler). Look in the doc tree for samples. If you are using bash (the default for Cygwin) then you should start with one ending in ``.sh'' and if you are using tcsh then you should start with one ending in ``.tcsh''. Generally it shouldn't take more than changing a few base directory variables at the top of the file, and the environ script will be ready to go. You then need to either execute this script before working (``source environ.sh'' or ``source environ.tcsh''), or put the appropriate command in your shell's startup script. The instructions on how to do basic shell things like that is beyond the scope of this document, so look to Cygwin and/or *nix help groups if you want to find it.

Now that you think you've got your environ script setup correctly, type set (under bash) or setenv (under tcsh) and look at the environment variables. You should see a whole bunch of them starting with ``KOS''. If that's true then this step is probably complete.

If you downloaded the source package for KOS, then you'll want to compile it now. Just change the directory to the root of the KOS tree (e.g., inside ``kos-1.1.5'') and type make. After a few minutes everything should have completed successfully. If not, then you might need to recheck the above steps and make sure everything is set up properly. If all else fails, try asking on the dcdev or cadcdev-kallistios mailing lists.

Well, now that that is (hopefully) out of the way, we'll proceed on to writing a KallistiOS program.

3. A Basic KOS Program

A KOS program is pretty simple and straightforward. Each C module should include kos.h as a system header (e.g. ``#include <kos.h>''). You should have a C-standard ``main'', which is called main. Inside this main routine you'll do one of two things: initialize only a few subsystems of KOS and use those, or do a full initialization with kos_init_all and go from there. It should be instructive to follow along in one of the Hello World examples for your platform to see how a basic KOS program should look.

The major advantage of initializing only a small portion of the subsystems is if you have a very simple program that will only use a few of them. This might be useful if you are writing some kind of loader, for example. This isn't really a significant advantage though, since including all of the base KOS library generally costs about 80k and manually initializaing is error prone, but caveat emptor; do it however you like.

For the rest of the documentation, I'm going to assume that you are doing the full initialization. Now the full initialization can still selectively initialize subsystems if you don't want them to be active during your program. For example, you might want to use the raw frame buffer, so enabling a 3D system doesn't make a whole lot of sense. On the other hand, you might just want to get everything ready to go. The basic setup call looks like this:

kos_init_all(ALL_ENABLE, romdisk);
The first parameter is a bitmask describing what you want to be initialized by default. The constant ALL_ENABLE is available on every platform as a generic way to initialize as much of KOS at once as is feasible. For example, on the DC, this constant enables IRQs, threads, and 3D support using the TA. Another constant available on all platforms is BASIC_ENABLE. This constant only enables the basic OS features and doesn't initialize any extra platform-specific features. On the DC, this enables IRQs and threads only.

The second parameter is a pointer to a ROMFS image created by genromfs. ROMFS was taken from Linux (the specification, not any code) and the Linux and KOS implementations are 100% compatible, except that KOS doesn't implement a few ROMFS features such as hard links and soft links. You can, for example, mount the same ROMFS image in KOS and under a loopback device in Linux. If you don't want a ROMFS, then you should use the constant ROMDISK_NONE. ROMFS images are generally included in the binary by using the KOS utility bin2o, which is included in the ``utils'' tree. The Hello World Makefiles show how to do this.

Once kos_init_all is called, the basic KOS systems should be ready to go! If you want to use any of the extra / add-on libraries that come aggregated with KOS, then you will need to initialize them seperately. These might include an MP3 playback library, KGL, etc.

Once your program is complete, it should call kos_shutdown_all, but in most cases this is not neccessary.

For more info on the different subsystems, please see the chapter devoted to that system. For examples, see the ``examples'' directory off the KOS root.

Now that you know what a basic KOS program looks like, you'll probably want to start figuring out how to use the various parts of the library. The following chapters are more in the way of a reference manual than a tutorial. The next chapter describes the portable parts of KOS which are present on every platform (though in some cases there are minor differences). The chapters after that describe the various platform-specific parts of KOS, and the final chapters describe the add-on libraries which are aggregated with the KOS distribution.

3.1 A Note on C++ Compatibility

KOS currently does not support compiling with C++ support ``out of the box''. We are examining the various issues involved with making it compatible and expect to have a resolution fairly soon. For now, we're assuming that you are using C for your development work.

4. The Portable Subsystems

4.1 Data Types

Before we get started on the individual systems, I figured a small word about data types is in order. Almost everything in KOS uses the following conventions for data types. KOS' architecture specific type header (arch/type.h) defines types int8, int16, int32, and int64 to match signed integers of the appropriate number of bits. uint8, uint16, uint32, and uint64 are the same, but unsigned. All of those types have corresponding volatile versions with a 'v' preview (vint8, vuint8, etc). Booleans or counts are often stored as ``int'', but counts are also often ``uint32'', depending on the circumstance. Functions that can return errors generally return them as a negative int, and success as a zero int. Strings are passed around as ``const char *''. Individual characters are generally ``int''. Several other misc types are defined, including some ANSI C compatibility types (u_short, etc) but those are the main ones.

4.2 ANSI C library

KOS' built-in libc supports the following functions, which are mostly ported from BSD: abort, byteorder, labs, rindex, strcpy, strncat, strnlen, strspn, abs, bzero, memchr, strcat, strdup, strncmp, strpbrk, strstr, atoi, dtoa, memcmp, memmove, printf, strchr, stricmp (aka strcasecmp), strncpy, strrchr, strtok, bcopy, index, memcpy, memset, qsort, strcmp, strlen, strnicmp (aka, strncasecmp), strsep, vsprintf, malloc, calloc, realloc, and free. Also supported are these ctype functions: isprint, isascii, islower, isupper, isdigit, tolower, toupper.

memcpy2, memset2, memcpy4 and memset4 are hardware helper functions which work like memset and memcpy, but they copy the specified number of bits at a time. memalign is a memory helper function which assures alignment on a certain byte boundary for hardware buffers.

Many people have commented that KOS' libc is somewhat spartan compared to many modern libc's such as glibc, BSD, and newlib. However, we feel like the KOS libc is probably more than sufficient for most console efforts (games, demos, etc). If you want a more complete portability layer then you might be looking in the wrong place - perhaps DC Linux or NetBSD would be better suited to your application. KOS makes no bones about being pretty bare when it comes to standard POSIX style functionality. Still, there's more than enough here to drive the console-intensive parts of your application as long as you're willing to do a bit of porting work.

4.3 VFS (Virtual File System)

KOS contains a reasonably capable VFS (virtual file system), much like a *nix. It provides the basic file operations which you have come to expect as a programmer: open (with path translation, thus the ``virtual'' part of VFS), read, write, seek, tell, readdir, ioctl, rename, unlink, mmap. VFS also provides an unconventional but very helpful function, total, which returns the total file size if possible. Some of these functions may not work on all file systems. For example, you cannot write to a CD or a ROMFS image. But you can write to a VMU on a DC. Failure in these functions is indicated (as mentioned above) by a negative return value, except in the case of open, which may eventually be changed to conform anyway.

4.3.1 file_t fs_open(const char *fn, int mode)

4.3.2 void fs_close(file_t hnd)

4.3.3 ssize_t fs_read(file_t hnd, void *buffer, size_t cnt)

4.3.4 ssize_t fs_write(file_t hnd, const void *buffer, size_t cnt)

4.3.5 off_t fs_seek(file_t hnd, off_t offset, int whence)

4.3.6 off_t fs_tell(file_t hnd)

4.3.7 size_t fs_total(file_t hnd)

4.3.8 dirent_t* fs_readdir(file_t hnd)

4.3.9 int fs_ioctl(file_t hnd, void *data, size_t size)

4.3.10 int fs_rename(const char *fn1, const char *fn2)

4.3.11 int fs_unlink(const char *fn)

4.3.12 int fs_chdir(const char *fn)

4.3.13 void* fs_mmap(file_t hnd)

4.3.14 const char *fs_getwd()

4.3.15 int fs_handler_add(const char *prefix, vfs_handler *hnd)

4.3.16 int fs_handler_remove(const vfs_handler *hnd)

4.4 File System: fs_builtin

4.5 File System: fs_romdisk

4.6 Debug Output

Every architecture supported by KOS supports the concept of a debug output console. Sometimes this may not be connected to anything (especially in the really embedded ports like GBA) but this is where all debug and printf output goes by default.

---KOS/LIBDREAM BARRIER---

4.6.1 void serial_disable()

Disables serial access through the serial module; all serial output will be discarded and serial input will always return ``no character available''. This is generally used before burning the program to a CDR so that it won't hang on a non-development machine waiting for serial output to complete.

4.6.2 void serial_init(int baud)

Initialize the SCIF interface with the given baud rate. Interrupts will be turned off and flow control through the FIFOs will be enabled.

4.6.3 void serial_write(int c)

Write a single character to the SCIF output. Note that the character may or may not be transmitted depending on the state of the transmit FIFO. If you wish instant output, you will need to call serial_flush().

4.6.4 void serial_flush()

Flushes all data remaining in the transmit FIFO. Use this to ensure that all queued serial output is actually sent.

4.6.5 void serial_write_buffer(uint8 *data, int len)

Writes an entire buffer of (potentially) binary data to the SCIF output. This function automatically executes a serial_flush().

4.6.6 void serial_write_str(char *str)

Writes a null-terminated string of data to the SCIF. Uses serial_write_buffer(), so all output is flushed immidiately.

4.6.7 int serial_read()

Reads one character from the SCIF input FIFO if one is waiting, otherwise returns -1.

4.6.8 int serial_printf(char *fmt, ...)

Uses vsprintf() from libc to process format args, then sends the data to the SCIF using serial_write_str(). In dream.h, printf() is an alias for this function.

4.7 Basic Video (video.c)

The basic video subsystem of libdream is designed to facilitate frame buffer setup and access. It does not handle 3D acceleration, that is handled in the ``TA'' module. The basic usage of the video system is to call vid_init with the desired parameters, and then use one of the ``vram'' pointers to access video memory.

The following variables and functions are available in basic video:

4.7.1 uint32 *vram_l

Pointer to video memory (0xa5000000) as a 32-bit unsigned integer array. Use this to access video memory when in RGB888 mode, or when copying large regions (not recommended =).

4.7.2 uint32 *vram_s

Similar to vram_l, but accesses as a 16-bit unsigned integer array. Use this to access video memory when in RGB555 or RGB565 mode.

4.7.3 int vram_config

Stores the first parameter to vid_init, which is the pixel format.

4.7.4 int vram_size

Stores the total size (in pixels) of one page of video frame buffer.

4.7.5 int vid_cable_type

Stores the cable_type parameter to the video init function below.

4.7.6 int vid_check_cable()

Checks and returns the attached video cable type; the three constants matching the return values are CT_VGA, CT_RGB, and CT_COMPOSITE.

4.7.7 int vid_init(int cable_type, int disp_mode, int pixel_mode)

Does full frame buffer initialization with the requested cable type, display mode, and pixel mode. You should pass the return value from vid_check_cable() as the first parameter. dc_setup() does this for you. disp_mode constants are DM_320x240, DM_640x480 and DM_800x608. pixel_mode constants are PM_RGB555, PM_RGB565, and PM_RGB888.

4.7.8 void vid_set_start(uint32 start)

Set the ``start address'' register, which governs where in the frame buffer the output picture comes from. This can be used for ``double buffering'', but it will most commonly be used during 3D acceleration.

4.7.9 void vid_border_color(int r, int g, int b)

Set the border color. The border is the area outside the standard viewing area. On NTSC this is mostly above and below the picture. I'm not sure what it is on PAL. Generally unless you're doing some odd debugging you'll want to set this to black (vid_init does this for you).

4.7.10 void vid_clear(int r, int g, int b)

Clears the first page of frame buffer based on the current video mode, with the given color. This is most useful when using frame buffer access, not 3D access.

4.7.11 void vid_empty()

Clear the entirety of video memory using zeros. This is done using longwords so it's fairly quick. Once again, mainly used with 3D setup.

4.7.12 void vid_waitvbl()

Wait for a vertical blank period. Vertical blank is the period between the time that the scan beam reaches the bottom of the screen and the time that it reaches the top and starts drawing again. This is relevant because this is the best time to draw to the frame buffer without causing ``tearing'' or other artifacts. It's also generally when you want to switch start addresses.

4.8 BIOS Fonts (biosfont.c)

BIOS fonts are the ones you see in the boot manager on the Dreamcast. These are stored in ROM and so are available to any program. You will probably recognize them immidiately since they are used all over the place in official productions. The BIOS font contains European Latin-1 characters (which we support) and Kanji (which we don't support yet but will eventually). The Latin-1 characters are bit masks of size 12x24, so each character uses 36 bytes. I suspect that the Kanji characters are 24x24, but I haven't tested this yet. These functions are frame-buffer agnostic except that they expect a 16-bit pixel size.

The following functions are available:

4.8.1 void* bfont_find_char(int ch)

Returns the address in ROM of the given character, after being mapped to the BIOS font.

4.8.2 void bfont_draw(uint16 *buffer, int bufwidth, int c)

Draws Latin-1 character 'c' at the given location in 'buffer', and assumes that 'buffer' is 'bufwidth' pixels wide. For example, to draw an 'a' at 20,20 in a 640x480 framebuffer, you'd use bfont_draw(vram_s+20*640+20, 640, 'a').

4.8.3 void bfont_draw_str(uint16 *buffer, int bufwidth, char *str)

Exactly like bfont_draw, but it takes a string and draws each character in turn.

4.9 PC Fonts (font.c)

The PC font system handles bitmapped fonts that are 8 pixels wide, and any number of pixels tall. The module is being deprecated in favor of the BIOS font module, so I won't describe it here. If you want more information, please reference font.c itself.

4.10 Sound Processor Unit (spu.c)

The sound processor unit (as mentioned in the README) is a Yamaha(tm) AICA sound system. To use the processor you will need to write a seperate program that runs on the ARM7 RISC core and uses the AICA's own registers. This isn't covered in this document (or anywhere, to my knowledge). For some decent examples, though, take a look at ``s3mplay'' on the Cryptic Allusion DCDev site (see README).

The following defines and functions are available to assist in using the sound processor:

4.10.1 SMP_BASE

All samples loaded to the AICA should proceed from this location relative to sound RAM (which maps to 0xa0810000 in the SH-4). This is mainly used in the S3M player but it's a good guideline to follow in general since it gives you 64k of space for the sound program (which is generally plenty).

4.10.2 void snd_ram_write_wait()

The AICA's RAM is attached to the chip itself rather than the SH-4, and so access proceeds through an ASIC. You must call this function every 8 long-words of written sound memory so that the ASIC's FIFO can catch up. If you don't, the data won't be written accurately.

4.10.3 void snd_load_arm(void *src, int size)

Loads an ARM7 program and starts it executing. The program will be loaded at offset 0, so it needs to begin with reset/exception vectors.

4.10.4 void snd_stop_arm()

Stops execution in the ARM7, and disables all AICA synthesizer channels. This insures that whatever was going on in the SPU is stopped completely.

4.10.5 void snd_init()

Initialize the SPU: disable the ARM7 and clear sound memory.

4.10.6 void snd_load(void *src, int dest, int len)

Load miscellaneous data into the SPU's RAM. 'src' is where to load from, and 'dest' is relative to the SPU based (so you could pass, e.g., SMP_BASE here). 'len' is in bytes but will be rounded up to long-words.

4.11 CD-Rom Access (cdfs.c)

Libdream provides the capability to use CD and CDR discs in the GD-Rom drive using this module.

Note that this file has been specifically crippled (or rather, we just never wrote it in) so that it can't access Sega's GD discs. This means that you can't access the data on any commercial game. There are really only a few legitimate reasons for doing this so we've disabled the feature to avoid coming under fire for assisting with copyright infringement. If you really want to know how, I'm sure you can figure it out =).

The following functions are available:

4.11.1 uint32 iso_open(const char *path, int oflag)

Open a file on the CD, using absolute path ``path'', with open flags ``oflag''. Note that in the current system, ``path'' must use forward slashes for path seperators (but can mix upper and lower case freely with no troubles), and ``oflag'' must be O_RDONLY or (O_RDONLY | O_DIR). A file descriptor will be returned, or a zero on error.

4.11.2 void iso_close(uint32 fd)

Close the file referenced by the given file descriptor.

4.11.3 int iso_read(uint32 fd, void *buf, int count)

Read ``nbyte'' bytes from the file referenced by ``fd'', into buffer ``buf''. Note that this function will always read the surrounding 2048 byte sector before extracting the parts you want, so you should never read less than 2048 bytes unless that's all you want. Reading more than 2048 does work. The number of bytes read will be returned.

4.11.4 long iso_lseek(uint32 fd, long offset, int whence)

Seek in file ``fd'' by ``offset'' bytes, relative to ``whence''. ``whence'' is one of the standard STDIO constants: SEEK_SET, SEEK_CUR, SEEK_END. ``offset'' may be positive or negative depending on the usage. The new file location will be returned.

4.11.5 long iso_tell(uint32 fd)

Returns the current file pointer within ``fd''.

4.11.6 dirent_t *iso_readdir(uint32 dirfd)

Read the next entry (if any) from the opened directory. Returns a pointer to a dirent_t on success (see fs_iso9660.h for more info on dirent_t) or NULL if nothing is left.

4.11.7 int cdrom_init() int iso_init()

Initialize the GD-Rom drive for reading CD/CDR media, and initialize the file system driver.

4.12 Timer Counters (timer.c)

This module supports the SH-4's internal timer perhipherals. Support is provided for TMU0 through TMU2. WDT (watchdog) is defined but not supported yet. TMU0 through TMU2 may all be used independently and count at different rates.

The following defines and functions are available:

4.12.1 TMU0, TMU1, TMU2, WDT

These are constants used to identify which timer you wish to operate on.

4.12.2 int timer_prime(int which, uint32 speed)

Primes a timer, but does not start it. ``which'' is one of the timer constants, and ``speed'' is a times per second rate which the counter will bottom out. So if you set speed to ``1'', then the timer will hit bottom after one second, and start counting again. Returns 0 for success.

4.12.3 int timer_start(int which)

Starts the requested timer counting (after priming it).

4.12.4 int timer_stop(int which)

Stops the requested timer.

4.12.5 uint32 timer_count(int which)

Returns the current timer count. The only way you can really make use of this externally is to get the timer count after priming but before starting, and scale the real-time results.

4.12.6 int timer_clear(int which)

Clears the timer underflow bit and returns what its value was. Underflow is set when the timer counts down. So for example, you could start a timer on a 1HZ cycle and poll this function until it returns true. At that point you'd have waited for a second, and the timer is already counting down again.

4.12.7 void timer_sleep(int ms)

Uses TMU0 to sleep for the given number of milliseconds.

4.12.8 int timer_init()

Setup timers (enable and stop all).

4.13 Maple Access (maple.c)

Libdream 0.7 includes Jordan DeLong's rewritten maple access code. This is a lot more modular and it is setup for future expansion with queueing multiple frames and DMA completion interrupts. For now it basically does the same as Marcus' old maple routines but with cleaner code.

In general using the maple bus consists of finding your peripheral (using DEVINFO queries), and storing this address; when you want to use the peripheral, you send it a condition query message and get a frame back describing the state of the peripheral. Most of the exported functions in maple.c won't be useful to mere mortals =) but that's a good thing since there are specific support modules for each of the major peripherals we have had access to.

The following functions are available:

4.13.1 void maple_init(int quiet)

Initialize the maple bus; if ``quiet'' is non-zero, then the bus scan will not produce any output.

4.13.2 void maple_shutdown()

Shut down all maple bus operations.

4.13.3 uint8 maple_create_addr(uint8 port, uint8 unit)

Create a maple address for the given port and unit.

4.13.4 int maple_docmd_block(...)

Parameters omitted for topic brevity: int8 cmd, uint8 addr, uint8 datalen, void *data, maple_frame_t retframe. This is the main ``work horse'' of the maple system. ``cmd'' should be one of the maple command constants in maple.h; ``addr'' should be created with maple_create_addr (or one of the maple_*_addr functions below); ``datalen'' is the length of the extra data (beyond what's in the frame header), ``data'' is a pointer to extra data (if any) that goes after the frame header; and ``retframe'' is a maple_frame_t that you should pass in to be filled in with return data. Zero is returned on success, and -1 returned on error. For some examples of using docmd_block directly, please check one of the maple peripheral modules.

4.13.5 int maple_rescan_bus(int quiet)

Rescans the maple bus. This will be neccessary if the user swaps out any controllers or memory cards. It also determines what is where and stores that info for later usage. If ``quiet'' is non-zero, it produces no output.

4.13.6 uint8 maple_device_addr(int code)

Pass a maple function code, and it returns the address of the first one that matches it.

4.13.7 uint8 maple_*_addr()

These include controller, mouse, kb, lcd, and vmu currently. Each one searches the maple bus to find the first matching type of peripheral and returns an address.

4.14 Maple Peripheral Support Modules

Support modules are included for standard controllers, keyboards, VMUs, and mice. Most peripherals fit into these molds. Eventually we'll probably add support for more things like the purupuru pack (force feedback) but we don't have one yet, so we can't. =) Notable among this list is the mouse since it just came out. Wow your friends by writing software that uses it before Sega gets a chance! =)

Since these are mostly the same (except for names and structure values) I won't go over them in detail. Each module generally contains a poll function that checks the state of the peripheral and fills in a device-specific structure. See the header files for the specific structure information. I'll list out the poll functions here for convienence though.

4.14.1 int cont_get_cond(uint8 addr, cont_cond_t *cond)

Check controller status. Returns which buttons are pressed and the state of the various analog controls.

4.14.2 int kbd_get_cond(uint8 addr, kbd_cond_t *cond)

Check keyboard status. Returns up to six keys being pressed at once. There are other support functions for the keyboard that you should look up in keyboard.h if you want to use it seriously. These do queueing and buffering for you. If you want this functionality, you should use kbd_poll(uint8 addr) and then kbd_get_key() to get key presses.

4.14.3 int vmu_draw_lcd(uint8 addr, void *bitmap)

Draws the given bitmap to the LCD screen. Generally these are on VMUs (which is why it's part of vmu.c) but it's not required. The bitmap should be a 48x32 bit array. The picture will show up right side up on the VMU itself, so when it's inserted in a controller you'll need to flip it in each direction.

4.14.4 int vmu_block_read(uint8 addr, uint16 blocknum, uint8 *buffer)

Read the requested block of the VMU's flash ram and put it in ``buffer''.

4.14.5 int vmu_block_write(uint8 addr, uint16 blocknum, uint8 *buffer)

Take what's in ``buffer'' and write it to the requested block of the VMU's flash ram.

4.14.6 int mouse_get_cond(uint8 addr, mouse_cond_t *cond)

Gets the condition of the mouse peripheral specified. Returns button states and delta x, y, and z (roller).

4.15 Tile Accelerator (ta.c)

The Tile Accelerator (3D acceleration) really deserves its own book, but for completeness (and my hands are getting tired =) I'm just going to go over the basics of setting it up and the functions you use to do so. For more specific information, look around on the web for various documents describing the TA, and look in the examples. Hopefully this section can be more fleshed out in future versions.

The TA is exactly what it says: the screen in the PVR 3D chip is broken up into 32x32 pixel tiles. So in 640x480, you'd really have a 20x15 tile field, not a 640x480 pixel field. The PVR's 3D magic happens by taking each of these tiles along with a ``display list'' describing what is to be displayed on the screen, and doing internal z-buffering. This means that each polygon is drawn only once, so even though there is not a standard z-buffer present, the end result looks like there is one. Opaque polygons, opaque volume modifiers (fog, etc), translucent polygons, translucent modifiers, and punch-through polygons (which can ``cut'' pieces of other polygons out, I think) must be sent to the TA, in that order. Each list is rendered in that order as well, for each tile, and so the more lists you send, the slower the rendering process gets. Opaque polygons are the fastest obviously, followed by punch-throughs, translucent polygons, and then the volume modifiers.

Because of the tile system, there is no user clipping neccessary: the TA works backwards by intersecting polygons and volumes with each tile before rendering. The end result of all of this is that all you have to do as a user is cull out the completely useless polygons (if you feel like it), arrange things in polygon ``strips'' as much as possible, and then throw the various lists to the TA. Then sit back and wait for it to do its work.

The PVR chip is not magic: it is powerful and can accelerate the drawing process to an amazing degree, but it still draws in terms of screen coordinates. So it is really a fancy 2D accelerator with perspective correction support for textures, and z-buffering.

Coordinates in the PVR start at 0,0 (all coordinates are floating point numbers) and go to 640,480, in the normal mode. Any coordinates outside this will work but will be clipped. Z coordinates start at 0 and move out of the screen towards the viewer. As far as I can tell, in normal mode, it wants Z and not 1/Z (W). I may be wrong of course. I'm no 3D hardware expert.

All that being said, the basic operation goes something like this:

  1. Setup the TA (ta_init); initialize the background plane structure
  2. Load any textures you may want to use
  3. For each frame:
Here are the structures and functions needed to do these things:

4.15.1 struct pv_str ta_page_values[2]

Holds all the internal rendering data for the page flipper and renderer. This is useful mainly if you want to do something like take a screen shot (you can find the current frame buffer).

4.15.2 bkg_poly ta_bkg

The background plane polygon. The background plane is currently automatically a 640x480, three-point opaque polygon. I'm not even sure if you can change this. For the values to load into this, take a look at one of the 3D example programs. If you want to do color shifting you can change this on the fly.

4.15.3 poly_hdr_t

A polygon header; this is always four flag long-words and four dummy words. The four dummy words are actually used with different types of shading and volume modifiers, but these are not supported yet in libdream. You should fill this structure directly (if you know what you're doing) or use ta_build_poly_hdr.

4.15.4 vertex_oc_t

Represents a single opaque/colored vertex with floating point coordinates and ARGB values. Actually it works fine for translucent polygons also but the naming convention stuck.

4.15.5 vertex_ot_t

Represents a single opaque/textured vertex with floating point coordinates and ARGB values. Actually it works fine for translucent polygons also.

4.15.6 int ta_curpage

The current working page (out of ta_page_values above).

4.15.7 void ta_init()

Initializes the TA and prepares for page flipped 3D.

4.15.8 void ta_send_queue(void *sql, int size)

Sends one (or two) store queue(s) full of data to the TA.

4.15.9 void ta_begin_render()

Call before you start drawing a frame.

4.15.10 void ta_commit_poly_hdr(poly_hdr_t *polyhdr)

Sends one polygon header to the TA. This needs to be done when you want to change drawing modes; e.g., opaque color, opaque textured, translucent color, translucent textured.

4.15.11 void ta_commit_vertex(void *vertex, int size)

Sends one vertex to the TA; this can be a vertex_oc_t or vertex_ot_t. Pass along the result of sizeof() on the vertex.

4.15.12 void ta_commit_eol()

Sends the ``end of list'' marker to the TA. This ought to be used after all opaque polygons are sent, and again after all translucent polygons are sent.

4.15.13 void ta_finish_frame()

Call after you've finished sending all data. This completes the rendering process in the alternate screen buffer and then waits for a vertical blank to switch to the new page.

4.15.14 void ta_build_poly_hdr(poly_hdr_t *target, ...)

Parameters omitted for brevity: int translucent, int textureformat, int tw, int th, uint32 textureaddr, int filtering. This builds a polygon header for you so you don't have to diddle with bitfields. Translucent should be one of TA_OPAQUE or TA_TRANSLUCENT. Textureformat needs to be one of the texture format constants or TA_NO_TEXTURE. This includes whether it's twiddled or not (for info on twiddled textures, look for the PVR E3 presentation online). The rest of the parameters are only relevant if textureformat is not TA_NO_TEXTURE. tw and th are the texture width and height, and must be powers of two between 8 and 1024. Textureaddr is the address within the PVR RAM that you loaded the texture, and it must be aligned on an 8-byte boundary. Filtering should be TA_NO_FILTER or TA_BILINEAR_FILTER. Note that bi-linear filtering is a fairly expensive operation unless you store your textures in the PVR RAM in twiddled format, in which case it's free.

4.15.15 void ta_load_texture(uint32 dest, void *src, int size)

Loads a texture into PVR ram at the given offset. ``size'' must be a multiple of 4 and will be rounded up if it's not already. A seperate function is required because the PVR requires you to send all texture data to 0xa4000000, not 0xa5000000. This must also be done after ta_init.

4.15.16 void *ta_texture_map(uint32 loc)

Maps a given PVR offset to a texture space. You should use this if you want to write directly into texture ram. Once again, it must be done after ta_init.

5. About this document

This document was written in a stock LYX 1.1.3fix6 distribution, using no fancy add-ons (so it ought to load ok for you if you want to try it). The PostScript, PDF, HTML, and text versions were generated using the export options in LYX.

About this document ...

Cryptic Allusion KallistiOS / Programmer's Manual

This document was generated using the LaTeX2HTML translator Version 2K.1beta (1.48)

Copyright © 1993, 1994, 1995, 1996, Nikos Drakos, Computer Based Learning Unit, University of Leeds.
Copyright © 1997, 1998, 1999, Ross Moore, Mathematics Department, Macquarie University, Sydney.

The command line arguments were:
latex2html -no_subdir -split 0 -show_section_numbers /tmp/lyx_tmpdir2866dbCZBk/lyx_tmpbuf28668fKSHT/manual.tex

The translation was initiated by Dan Potter on 2001-12-10


next_inactive up previous
Dan Potter 2001-12-10