I got a Dreamcast (finally) a bit over a week ago and decided to give the homebrew tools a quick try. I ran into a few things...
First, I noticed that screen flipping hadn't been implemented yet, which is odd considering how old KOS is and how easy it was to add. In fact, the current video driver for KOS on the DC is rather limited - you get one frame buffer that is visible and you can only draw to it. That was nice for 1993 on your VGA card with 64 KB of vram, but this is a Dreamcast. Let's look at video.c.
(sorry about the quotes, but code tags STILL don't work on this board!!!)
Quote:
vid_mode_t vid_builtin[DM_MODE_COUNT] = {
/* NULL mode.. */
/* DM_INVALID = 0 */
{ 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, { 0, 0, 0, 0 } },
/* 320x240 VGA 60Hz */
/* DM_320x240_VGA */
{
DM_320x240,
320, 240,
VID_PIXELDOUBLE|VID_LINEDOUBLE,
CT_VGA,
0,
262, 857,
0xAC, 0x28,
0x15, 0x104,
141, 843,
24, 263,
0, 1,
{ 0, 0, 0, 0 }
},
I only show the first entry in the table to demonstrate the first change... see that final "1" right before the array of zeroes? That's how many buffers you get. Change them all like so
Quote:
vid_mode_t vid_builtin[DM_MODE_COUNT] = {
/* NULL mode.. */
/* DM_INVALID = 0 */
{ 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, { 0, 0, 0, 0 } },
/* 320x240 VGA 60Hz */
/* DM_320x240_VGA */
{
DM_320x240,
320, 240,
VID_PIXELDOUBLE|VID_LINEDOUBLE,
CT_VGA,
0,
262, 857,
0xAC, 0x28,
0x15, 0x104,
141, 843,
24, 263,
0, VID_MAX_FB,
{ 0, 0, 0, 0 }
},
Now we need to set the frame buffer offsets. That's those last four zeroes... but we won't set it there as it's easier to do it in the vid_set_mode() function.
Quote:
void vid_set_mode(int dm, int pm) {
static uint8 bpp[4] = { 2, 2, 0, 4 };
vid_mode_t mode;
int i, ct, found;
ct = vid_check_cable();
/* Check to see if we should use a direct mode index, a generic
mode check, or if it's just invalid. */
if (dm > DM_INVALID && dm < DM_SENTINEL) {
memcpy(&mode, &vid_builtin[dm], sizeof(vid_mode_t));
} else if (dm >= DM_GENERIC_FIRST && dm <= DM_GENERIC_LAST) {
found = 0;
for (i=1; i<DM_SENTINEL; i++) {
/* Is it the right generic mode? */
if (vid_builtin[i].generic != dm)
continue;
/* Do we have the right cable type? */
if (vid_builtin[i].cable_type != CT_ANY &&
vid_builtin[i].cable_type != ct)
continue;
/* Ok, nothing else to check right now -- we've got our mode */
memcpy(&mode, &vid_builtin[i], sizeof(vid_mode_t));
found = 1;
break;
}
if (!found) {
dbglog(DBG_ERROR, "vid_set_mode: invalid generic mode %04x\n", dm);
return;
}
} else {
dbglog(DBG_ERROR, "vid_set_mode: invalid mode specifier %04x\n", dm);
return;
}
/* We set this here so actual mode is bit-depth independent.. */
mode.pm = pm;
/* This is also to be generic */
mode.cable_type = ct;
/* Set the framebuffer base array */
mode.fb_base[1] = 0x200000;
mode.fb_base[2] = 0x400000;
mode.fb_base[3] = 0x600000;
/* This will make a private copy of our "mode" */
vid_set_mode_ex(&mode);
}
The new lines are towards the bottom where mode.fb_base[] is set. The offsets are far enough apart for any size display you might set.
Then you need to change
in vid_set_mode_ex() to this
Quote:
vid_mode->fb_curr = -1; /* force update in flip */
vid_flip(0);
/* vid_set_start(0); */
We could leave it there, but now that we have four buffers to play with, we need to add some ability to draw into the offscreen buffers. The current code sets a couple variables called vram_s and vram_l to point to the frame buffer when you set the mode. If yo draw through those variables, you are drawing to the visible screen. So I added a couple new variables, dvram_s and dvram_l that are set by vid_flip() to point to the next buffer after the one you just flipped to.
Quote:
void vid_flip(int fb) {
uint16 oldfb;
uint32 base;
oldfb = vid_mode->fb_curr;
if (fb < 0) {
vid_mode->fb_curr++;
} else {
vid_mode->fb_curr = fb;
}
vid_mode->fb_curr &= (vid_mode->fb_count - 1);
if (vid_mode->fb_curr == oldfb) {
return;
}
/* The next buffer becomes the draw buffer. */
base = vid_mode->fb_base[(vid_mode->fb_curr + 1) & (vid_mode->fb_count - 1)];
dvram_s = (uint16*)(0xA5000000|base);
dvram_l = (uint32*)(0xA5000000|base);
/* Will set vram_s/vram_l to the current buffer. */
vid_set_start(vid_mode->fb_base[vid_mode->fb_curr]);
}
Now if you draw through dvram_s or dvram_l, you'll be drawing to the buffer that will be visible the next time you call vid_flip(-1);
So now you have working screen flipping. Just call vid_set_mode() like normal, then go into your main loop. The loop will draw through dvram_s or dvram_l, then call vid_flip(-1). That's all you need to do, and you have tear-free drawing.
One other change... vid_clear() clears the current frame buffer. We want to clear the drawing buffer, not the frame buffer. We'll leave vid_clear() alone for backwards compatibility with existing software and make a new function - vid_clear_draw().
Quote:
/*-----------------------------------------------------------------------------*/
/* Clears the draw buffer with a given color */
void vid_clear_draw(int r, int g, int b) {
uint16 pixel16;
uint32 pixel32;
switch (vid_mode->pm) {
case PM_RGB555:
pixel16 = ((r >> 3) << 10)
| ((g >> 3) << 5)
| ((b >> 3) << 0);
sq_set16(dvram_s, pixel16, (vid_mode->width * vid_mode->height) * 2);
break;
case PM_RGB565:
pixel16 = ((r >> 3) << 11)
| ((g >> 2) << 5)
| ((b >> 3) << 0);
sq_set16(dvram_s, pixel16, (vid_mode->width * vid_mode->height) * 2);
break;
case PM_RGB888:
pixel32 = (r << 16) | (g << 8) | (b << 0);
sq_set32(dvram_l, pixel32, (vid_mode->width * vid_mode->height) * 4);
break;
}
}
It's EXACTLY the same as vid_clear(), but uses dvram_s/dvram_l instead of vram_s/vram_l as the address to clear. Don't forget to add the proto to the video.h file.