

Cryptic Allusion Libdream 0.95 / Programmer's Manual

(c)2000 Dan Potter

\tableofcontents{}

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. All other trademarks are owned
by their respective trademark holders.

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

Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the ``Software''),
to deal in the Software without restriction, including without limitation
the rights to use, copy, modify, merge, publish, distribute, sublicense,
and/or sell copies of the Software, and to permit persons to whom
the Software is furnished to do so, subject to the following conditions:

The above copyright notice and the following permission notice shall
be included in all copies of the Software:

THE SOFTWARE IS PROVIDED AS IS, WITHOUT WARRANTY OF ANY KIND, EXPRESS
OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT
SHALL THE COPYRIGHT HOLDERS BE HELD LIABLE FOR ANY CLAIM, DAMAGES
OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
ARISING FROM, OUT OF, OR IN CONNECTION WITH THE SOFTWARE OR THE USE
OF OTHER DEALINGS IN THE SOFTWARE.

Except as contained in this notice, the name of the copyright holders
shall not be used in advertising or otherwise to promote the use or
other dealings in this Software without prior written authorization
from the copyright holders.

Getting Started

Welcome to Libdream 0.95! This new and improved version has lots of
extra goodies that people have been asking for for a good long time.
For a better explanation of what Libdream is, please take a look at
the README file in the root of the package directory.

The first thing you'll want to do is compile Libdream. Included in
the package is a pre-compiled library with the default options, and
you can use this to compile the examples. But compiling the library
itself will help make sure that your compiler environment is setup
properly and it allows you to make changes if you need them for your
program. We've tried to make sure that everything you'll need for
most programs is available at runtime, but we don't always think of
everything. Thus: open source!

If you do not have a proper SH-4 compilation environment, then you'll
probably need to start by getting that setup. That's a bit out of
scope for this libdream document, so you'll want to take a look at,
e.g., HitMen's DC site for more info on getting it.

To begin, edit the file Makefile.config in the libdream root. You'll
find a set of default compiler paths that match my system. You should
change these to match your system appropriately.

Once Makefile.config is edited, if everything is well, you should be
able to type 'make' and have everything built (including the examples).
On BSD, you'll need to use gmake, or potentially ``MAKE=gmake gmake''
(in bash parlance). Cygwin users ought to be able to use ``make''
and have it compile ok.

If you'd like to compile just the examples and skip the libdream step,
then edit Makefile.config like before, but change into ``examples''
and then do the make/gmake step. This will build all of the examples,
leaving only a bin and srec in each directory. These can be loaded
using dc-tool/DDH or Marcus Comstedt's serial slave (respectively).
Each example program has notes with it on operation which you should
look at before running it. Most of them have serial output by default,
so you'll need to add serial_disable before dc_setup if you want to
run them using a CDR.

To compile your own libdream-based programs you'll need to add the
appropriate include and lib paths to your CC and LD lines. These are
the ``include'' and ``lib'' subdirectories of the libdream root.

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

A Basic Libdream Program

A libdream program is pretty simple and straightforward -- you should
have a ``main'', which is called ``main'' if you're using a default
crt0, or ``dc_main'' if you use my (Dan's) crt0. Inside this main
routine you'll do one of two things: initialize only a few subsystems
of libdream and use those, or do a full initialization with dc_setup
and go from there.

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.
For example, you might want to make a program that displays a menu
over the serial port to the user, and lets them manage VMU files.
In this case you'd only need maple support and serial support, so
you could save about 8k of binary code in the finished program. This
isn't really a significant advantage though, since including all of
libdream generally costs about 20k 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. This full initialization can take two
forms as well: a normal and a quiet form. Normal form is good for
debugging because it gives you some information on startup saying
that it's alive, and about what is attached to your maple bus. Quiet
form is useful if you want to do something that implements a serial
protocol and so you don't want to disable serial entirely, but you
don't want garbage at the front either.

The basic normal call looks something like this:

dc_setup(DM_640x480, PM_RGB565);

This call will setup the video with a 640x480 mode, color depth 16
with a pixel format of R/G/B 5/6/5. Other choices are DM_320x240 and
DM_800x608 for the first parameter, and PM_RGB555 and PM_RGB888 for
the second. You should always check dream.h for the most up-to-date
information.

Once dc_setup is called, all hardware should be initialized and ready
to go with the major exception of the Tile Accelerator. The TA is
special in that it pretty much requires clobbering a lot of assumptions
that the normal video setup makes, and it takes a bit longer. The
code is also fairly large compared to the other subsystems. For these
reasons, ta_init is not called by default but you must call it before
you use any portion of the TA library (texture loading, polygons,
etc).

Congratulations! ;-) You've now written your first libdream program.
For more info on the different subsystems, please see the chapter
devoted to that system. For examples, see the ``examples'' directory
off the libdream root.

The Subsystems

Now that you know what a basic libdream program looks like, you'll
probably want to start figuring out how to use the various parts of
the library. The following chapter is more in the way of a reference
manual than a tutorial, which is kind of what I intended to begin
with. ^_^;

3.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 libdream uses
the following conventions for data types. dream.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. Booleans or counts are often stored as ``int'', but counts
are usually ``uint32''. Functions that can return errors generally
return them as a negative int, and success as a zero int. Strings
are stored as ``char *''. Indivudual characters are generally ``int''.

3.2 Serial (serial.c)

The serial system accesses the ``SCIF'' (serial communications interface
w/FIFO) of the SH-4 CPU. This port of the CPU is attached to the external
``Serial I/O'' port of the Dreamcast.

The various functions of the serial module are pretty straightforward.
Here is a short explanation of each:

3.2.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.

3.2.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.

3.2.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().

3.2.4 void serial_flush()

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

3.2.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().

3.2.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.

3.2.7 int serial_read()

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

3.2.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.

3.3 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:

3.3.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 =).

3.3.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.

3.3.3 int vram_config

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

3.3.4 int vram_size

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

3.3.5 int vid_cable_type

Stores the cable_type parameter to the video init function below.

3.3.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.

3.3.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.

3.3.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.

3.3.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).

3.3.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.

3.3.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.

3.3.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.

3.4 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:

3.4.1 void* bfont_find_char(int ch)

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

3.4.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').

3.4.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.

3.5 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.

3.6 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:

3.6.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).

3.6.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.

3.6.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.

3.6.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.

3.6.5 void snd_init()

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

3.6.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.

3.7 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:

3.7.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.

3.7.2 void iso_close(uint32 fd)

Close the file referenced by the given file descriptor.

3.7.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.

3.7.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.

3.7.5 long iso_tell(uint32 fd)

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

3.7.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.

3.7.7 int cdrom_init() int iso_init()

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

3.8 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:

3.8.1 TMU0, TMU1, TMU2, WDT

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

3.8.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.

3.8.3 int timer_start(int which)

Starts the requested timer counting (after priming it).

3.8.4 int timer_stop(int which)

Stops the requested timer.

3.8.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.

3.8.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.

3.8.7 void timer_sleep(int ms)

Uses TMU0 to sleep for the given number of milliseconds.

3.8.8 int timer_init()

Setup timers (enable and stop all).

3.9 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:

3.9.1 void maple_init(int quiet)

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

3.9.2 void maple_shutdown()

Shut down all maple bus operations.

3.9.3 uint8 maple_create_addr(uint8 port, uint8 unit)

Create a maple address for the given port and unit.

3.9.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.

3.9.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.

3.9.6 uint8 maple_device_addr(int code)

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

3.9.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.

3.10 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.

3.10.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.

3.10.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.

3.10.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.

3.10.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''.

3.10.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.

3.10.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).

3.11 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:

* Call ta_begin_render to initialize the rendering process.

* Construct and send one or more polygon headers for opaque polygons,
  each followed by zero or more verteces; each vertex strip must end
  with an ``end of list'' marker.

* Call ta_commit_eol to finish the opaque list.

* Construct and send one or more polygon headers for translucent polygons
  (same process as above).

* Call ta_commit_eol to finish the translucent list.

* Call ta_finish_frame to finish the rendering process and wait for
  a vertical blank to flip pages.

Here are the structures and functions needed to do these things:

3.11.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).

3.11.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.

3.11.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.

3.11.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.

3.11.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.

3.11.6 int ta_curpage

The current working page (out of ta_page_values above).

3.11.7 void ta_init()

Initializes the TA and prepares for page flipped 3D.

3.11.8 void ta_send_queue(void *sql, int size)

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

3.11.9 void ta_begin_render()

Call before you start drawing a frame.

3.11.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.

3.11.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.

3.11.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.

3.11.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.

3.11.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.

3.11.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.

3.11.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.

About this document

This document was written in a stock LyX 1.0.0 distribution, using
no fancy add-ons (so it ought to load ok for you if you want to try
it). The PostScript was generated using the TeX tools that come with
Debian 2.2. The HTML output was produced by exporting a LaTeX file
from LyX and using latex2html on the result.
