/*************************************************************************
Driver for Midway Zeus games
**************************************************************************/
#include "emu.h"
#include "cpu/tms32031/tms32031.h"
#include "includes/midzeus.h"
#include "video/poly.h"
#include "video/rgbutil.h"
/*************************************
*
* Constants
*
*************************************/
#define DUMP_WAVE_RAM 0
#define TRACK_REG_USAGE 0
#define WAVERAM0_WIDTH 1024
#define WAVERAM0_HEIGHT 2048
#define WAVERAM1_WIDTH 512
#define WAVERAM1_HEIGHT 1024
/*************************************
*
* Type definitions
*
*************************************/
typedef struct _poly_extra_data poly_extra_data;
struct _poly_extra_data
{
const void * palbase;
const void * texbase;
UINT16 solidcolor;
INT16 zoffset;
UINT16 transcolor;
UINT16 texwidth;
UINT16 color;
UINT32 alpha;
};
/*************************************
*
* Global variables
*
*************************************/
static poly_manager *poly;
static UINT8 log_fifo;
static UINT32 zeus_fifo[20];
static UINT8 zeus_fifo_words;
static void *zeus_renderbase;
static rectangle zeus_cliprect;
static float zeus_matrix[3][3];
static float zeus_point[3];
static float zeus_point2[3];
static UINT32 zeus_texbase;
static UINT32 zeus_unknown_40;
static int zeus_quad_size;
static UINT32 *waveram[2];
static emu_timer *int_timer;
static int yoffs;
static int texel_width;
static float zbase;
#if TRACK_REG_USAGE
typedef struct reg_info
{
struct reg_info *next;
UINT32 value;
} reg_info;
static reg_info *regdata[0x80];
static int regdata_count[0x80];
static int regread_count[0x80];
static int regwrite_count[0x80];
static reg_info *subregdata[0x100];
static int subregdata_count[0x80];
static int subregwrite_count[0x100];
#endif
/*************************************
*
* Function prototypes
*
*************************************/
static void exit_handler(running_machine &machine);
static void zeus_register32_w(running_machine &machine, offs_t offset, UINT32 data, int logit);
static void zeus_register_update(running_machine &machine, offs_t offset, UINT32 oldval, int logit);
static void zeus_pointer_write(UINT8 which, UINT32 value);
static int zeus_fifo_process(running_machine &machine, const UINT32 *data, int numwords);
static void zeus_draw_model(running_machine &machine, UINT32 baseaddr, UINT16 count, int logit);
static void zeus_draw_quad(running_machine &machine, const UINT32 *databuffer, UINT32 texoffs, int logit);
static void render_poly_8bit(void *dest, INT32 scanline, const poly_extent *extent, const void *extradata, int threadid);
static void log_fifo_command(const UINT32 *data, int numwords, const char *suffix);
//static void log_waveram(UINT32 base, UINT16 length);
/*************************************
*
* Macros
*
*************************************/
#define WAVERAM_BLOCK0(blocknum) ((void *)((UINT8 *)waveram[0] + 8 * (blocknum)))
#define WAVERAM_BLOCK1(blocknum) ((void *)((UINT8 *)waveram[1] + 12 * (blocknum)))
#define WAVERAM_PTR8(base, bytenum) ((UINT8 *)(base) + BYTE4_XOR_LE(bytenum))
#define WAVERAM_READ8(base, bytenum) (*WAVERAM_PTR8(base, bytenum))
#define WAVERAM_WRITE8(base, bytenum, data) do { *WAVERAM_PTR8(base, bytenum) = (data); } while (0)
#define WAVERAM_PTR16(base, wordnum) ((UINT16 *)(base) + BYTE_XOR_LE(wordnum))
#define WAVERAM_READ16(base, wordnum) (*WAVERAM_PTR16(base, wordnum))
#define WAVERAM_WRITE16(base, wordnum, data) do { *WAVERAM_PTR16(base, wordnum) = (data); } while (0)
#define WAVERAM_PTR32(base, dwordnum) ((UINT32 *)(base) + (dwordnum))
#define WAVERAM_READ32(base, dwordnum) (*WAVERAM_PTR32(base, dwordnum))
#define WAVERAM_WRITE32(base, dwordnum, data) do { *WAVERAM_PTR32(base, dwordnum) = (data); } while (0)
#define PIXYX_TO_DWORDNUM(y, x) (((((y) & 0x1ff) << 8) | (((x) & 0x1fe) >> 1)) * 3 + ((x) & 1))
#define DEPTHYX_TO_DWORDNUM(y, x) (PIXYX_TO_DWORDNUM(y, (x) & ~1) + 2)
#define WAVERAM_PTRPIX(base, y, x) WAVERAM_PTR32(base, PIXYX_TO_DWORDNUM(y, x))
#define WAVERAM_READPIX(base, y, x) (*WAVERAM_PTRPIX(base, y, x))
#define WAVERAM_WRITEPIX(base, y, x, color) do { *WAVERAM_PTRPIX(base, y, x) = (color); } while (0)
#define WAVERAM_PTRDEPTH(base, y, x) WAVERAM_PTR16(base, DEPTHYX_TO_DWORDNUM(y, x) * 2 + (x & 1))
#define WAVERAM_READDEPTH(base, y, x) (*WAVERAM_PTRDEPTH(base, y, x))
#define WAVERAM_WRITEDEPTH(base, y, x, color) do { *WAVERAM_PTRDEPTH(base, y, x) = (color); } while (0)
/*************************************
*
* Inlines for block addressing
*
*************************************/
INLINE void *waveram0_ptr_from_expanded_addr(UINT32 addr)
{
UINT32 blocknum = (addr % WAVERAM0_WIDTH) + ((addr >> 16) % WAVERAM0_HEIGHT) * WAVERAM0_WIDTH;
return WAVERAM_BLOCK0(blocknum);
}
INLINE void *waveram1_ptr_from_expanded_addr(UINT32 addr)
{
UINT32 blocknum = (addr % WAVERAM1_WIDTH) + ((addr >> 16) % WAVERAM1_HEIGHT) * WAVERAM1_WIDTH;
return WAVERAM_BLOCK1(blocknum);
}
#ifdef UNUSED_FUNCTION
INLINE void *waveram0_ptr_from_texture_addr(UINT32 addr, int width)
{
UINT32 blocknum = ((addr & ~1) * width) / 8;
return WAVERAM_BLOCK0(blocknum);
}
#endif
/*************************************
*
* Inlines for rendering
*
*************************************/
#ifdef UNUSED_FUNCTION
INLINE void waveram_plot(int y, int x, UINT32 color)
{
if (x >= 0 && x <= zeus_cliprect.max_x && y >= 0 && y < zeus_cliprect.max_y)
WAVERAM_WRITEPIX(zeus_renderbase, y, x, color);
}
#endif
INLINE void waveram_plot_depth(int y, int x, UINT32 color, UINT16 depth)
{
if (x >= 0 && x <= zeus_cliprect.max_x && y >= 0 && y < zeus_cliprect.max_y)
{
WAVERAM_WRITEPIX(zeus_renderbase, y, x, color);
WAVERAM_WRITEDEPTH(zeus_renderbase, y, x, depth);
}
}
#ifdef UNUSED_FUNCTION
INLINE void waveram_plot_check_depth(int y, int x, UINT32 color, UINT16 depth)
{
if (x >= 0 && x <= zeus_cliprect.max_x && y >= 0 && y < zeus_cliprect.max_y)
{
UINT16 *depthptr = WAVERAM_PTRDEPTH(zeus_renderbase, y, x);
if (depth <= *depthptr)
{
WAVERAM_WRITEPIX(zeus_renderbase, y, x, color);
*depthptr = depth;
}
}
}
#endif
#ifdef UNUSED_FUNCTION
INLINE void waveram_plot_check_depth_nowrite(int y, int x, UINT32 color, UINT16 depth)
{
if (x >= 0 && x <= zeus_cliprect.max_x && y >= 0 && y < zeus_cliprect.max_y)
{
UINT16 *depthptr = WAVERAM_PTRDEPTH(zeus_renderbase, y, x);
if (depth <= *depthptr)
WAVERAM_WRITEPIX(zeus_renderbase, y, x, color);
}
}
#endif
/*************************************
*
* Inlines for texel accesses
*
*************************************/
INLINE UINT8 get_texel_8bit(const void *base, int y, int x, int width)
{
UINT32 byteoffs = (y / 2) * (width * 2) + ((x / 4) << 3) + ((y & 1) << 2) + (x & 3);
return WAVERAM_READ8(base, byteoffs);
}
#ifdef UNUSED_FUNCTION
INLINE UINT8 get_texel_4bit(const void *base, int y, int x, int width)
{
UINT32 byteoffs = (y / 2) * (width * 2) + ((x / 8) << 3) + ((y & 1) << 2) + ((x / 2) & 3);
return (WAVERAM_READ8(base, byteoffs) >> (4 * (x & 1))) & 0x0f;
}
#endif
/*************************************
*
* Video startup
*
*************************************/
static TIMER_CALLBACK( int_timer_callback )
{
cputag_set_input_line(machine, "maincpu", 2, ASSERT_LINE);
}
VIDEO_START( midzeus2 )
{
/* allocate memory for "wave" RAM */
waveram[0] = auto_alloc_array(machine, UINT32, WAVERAM0_WIDTH * WAVERAM0_HEIGHT * 8/4);
waveram[1] = auto_alloc_array(machine, UINT32, WAVERAM1_WIDTH * WAVERAM1_HEIGHT * 12/4);
/* initialize polygon engine */
poly = poly_alloc(machine, 10000, sizeof(poly_extra_data), POLYFLAG_ALLOW_QUADS);
/* we need to cleanup on exit */
machine.add_notifier(MACHINE_NOTIFY_EXIT, machine_notify_delegate(FUNC(exit_handler), &machine));
zbase = 2.0f;
yoffs = 0;
texel_width = 256;
zeus_renderbase = waveram[1];
int_timer = machine.scheduler().timer_alloc(FUNC(int_timer_callback));
/* save states */
state_save_register_global_pointer(machine, waveram[0], WAVERAM0_WIDTH * WAVERAM0_HEIGHT * 8 / sizeof(waveram[0][0]));
state_save_register_global_pointer(machine, waveram[1], WAVERAM1_WIDTH * WAVERAM1_HEIGHT * 12 / sizeof(waveram[1][0]));
state_save_register_global_array(machine, zeus_fifo);
state_save_register_global(machine, zeus_fifo_words);
state_save_register_global(machine, zeus_cliprect.min_x);
state_save_register_global(machine, zeus_cliprect.max_x);
state_save_register_global(machine, zeus_cliprect.min_y);
state_save_register_global(machine, zeus_cliprect.max_y);
state_save_register_global_2d_array(machine, zeus_matrix);
state_save_register_global_array(machine, zeus_point);
state_save_register_global(machine, zeus_texbase);
}
static void exit_handler(running_machine &machine)
{
#if DUMP_WAVE_RAM
FILE *f = fopen("waveram.dmp", "w");
int i;
for (i = 0; i < WAVERAM0_WIDTH * WAVERAM0_HEIGHT; i++)
{
if (i % 4 == 0) fprintf(f, "%03X%03X: ", i / WAVERAM0_WIDTH, i % WAVERAM0_WIDTH);
fprintf(f, " %08X %08X ",
WAVERAM_READ32(waveram[0], i*2+0),
WAVERAM_READ32(waveram[0], i*2+1));
if (i % 4 == 3) fprintf(f, "\n");
}
fclose(f);
#endif
#if TRACK_REG_USAGE
{
reg_info *info;
int regnum;
for (regnum = 0; regnum < 0x80; regnum++)
{
printf("Register %02X\n", regnum);
if (regread_count[regnum] == 0)
printf("\tNever read\n");
else
printf("\tRead %d times\n", regread_count[regnum]);
if (regwrite_count[regnum] == 0)
printf("\tNever written\n");
else
{
printf("\tWritten %d times\n", regwrite_count[regnum]);
for (info = regdata[regnum]; info != NULL; info = info->next)
printf("\t%08X\n", info->value);
}
}
for (regnum = 0; regnum < 0x100; regnum++)
if (subregwrite_count[regnum] != 0)
{
printf("Sub-Register %02X (%d writes)\n", regnum, subregwrite_count[regnum]);
for (info = subregdata[regnum]; info != NULL; info = info->next)
printf("\t%08X\n", info->value);
}
}
#endif
poly_free(poly);
}
/*************************************
*
* Video update
*
*************************************/
SCREEN_UPDATE( midzeus2 )
{
int x, y;
poly_wait(poly, "VIDEO_UPDATE");
if (screen->machine().input().code_pressed(KEYCODE_UP)) { zbase += 1.0f; popmessage("Zbase = %f", zbase); }
if (screen->machine().input().code_pressed(KEYCODE_DOWN)) { zbase -= 1.0f; popmessage("Zbase = %f", zbase); }
/* normal update case */
if (!screen->machine().input().code_pressed(KEYCODE_W))
{
const void *base = waveram1_ptr_from_expanded_addr(zeusbase[0x38]);
int xoffs = screen->visible_area().min_x;
for (y = cliprect->min_y; y <= cliprect->max_y; y++)
{
UINT32 *dest = (UINT32 *)bitmap->base + y * bitmap->rowpixels;
for (x = cliprect->min_x; x <= cliprect->max_x; x++)
dest[x] = WAVERAM_READPIX(base, y, x - xoffs);
}
}
/* waveram drawing case */
else
{
const UINT64 *base;
if (screen->machine().input().code_pressed(KEYCODE_DOWN)) yoffs += screen->machine().input().code_pressed(KEYCODE_LSHIFT) ? 0x40 : 1;
if (screen->machine().input().code_pressed(KEYCODE_UP)) yoffs -= screen->machine().input().code_pressed(KEYCODE_LSHIFT) ? 0x40 : 1;
if (screen->machine().input().code_pressed(KEYCODE_LEFT) && texel_width > 4) { texel_width >>= 1; while (screen->machine().input().code_pressed(KEYCODE_LEFT)) ; }
if (screen->machine().input().code_pressed(KEYCODE_RIGHT) && texel_width < 512) { texel_width <<= 1; while (screen->machine().input().code_pressed(KEYCODE_RIGHT)) ; }
if (yoffs < 0) yoffs = 0;
base = (const UINT64 *)waveram0_ptr_from_expanded_addr(yoffs << 16);
for (y = cliprect->min_y; y <= cliprect->max_y; y++)
{
UINT32 *dest = (UINT32 *)bitmap->base + y * bitmap->rowpixels;
for (x = cliprect->min_x; x <= cliprect->max_x; x++)
{
UINT8 tex = get_texel_8bit(base, y, x, texel_width);
dest[x] = (tex << 16) | (tex << 8) | tex;
}
}
popmessage("offs = %06X", yoffs << 12);
}
return 0;
}
/*************************************
*
* Core read handler
*
*************************************/
READ32_HANDLER( zeus2_r )
{
int logit = (offset != 0x00 && offset != 0x01 && offset != 0x54 && offset != 0x48 && offset != 0x49 && offset != 0x58 && offset != 0x59 && offset != 0x5a);
UINT32 result = zeusbase[offset];
#if TRACK_REG_USAGE
regread_count[offset]++;
#endif
if (logit)
logerror("%06X:zeus2_r(%02X)\n", cpu_get_pc(&space->device()), offset);
switch (offset)
{
case 0x00:
result = 0x20;
break;
case 0x01:
/* bit $000C0070 are tested in a loop until 0 */
/* bits $00080000 is tested in a loop until 0 */
/* bit $00000004 is tested for toggling; probably VBLANK */
result = 0x00;
if (space->machine().primary_screen->vblank())
result |= 0x04;
break;
case 0x07:
/* this is needed to pass the self-test in thegrid */
result = 0x10451998;
break;
case 0x54:
/* both upper 16 bits and lower 16 bits seem to be used as vertical counters */
result = (space->machine().primary_screen->vpos() << 16) | space->machine().primary_screen->vpos();
break;
}
return result;
}
/*************************************
*
* Core write handler
*
*************************************/
WRITE32_HANDLER( zeus2_w )
{
int logit = (offset != 0x08 &&
(offset != 0x20 || data != 0) &&
offset != 0x40 && offset != 0x41 && offset != 0x48 && offset != 0x49 && offset != 0x4e &&
offset != 0x50 && offset != 0x51 && offset != 0x57 && offset != 0x58 && offset != 0x59 && offset != 0x5a && offset != 0x5e);
if (logit)
logerror("%06X:zeus2_w", cpu_get_pc(&space->device()));
zeus_register32_w(space->machine(), offset, data, logit);
}
/*************************************
*
* Handle register writes
*
*************************************/
static void zeus_register32_w(running_machine &machine, offs_t offset, UINT32 data, int logit)
{
UINT32 oldval = zeusbase[offset];
#if TRACK_REG_USAGE
regwrite_count[offset]++;
if (regdata_count[offset] < 256)
{
reg_info **tailptr;
for (tailptr = ®data[offset]; *tailptr != NULL; tailptr = &(*tailptr)->next)
if ((*tailptr)->value == data)
break;
if (*tailptr == NULL)
{
*tailptr = alloc_or_die(reg_info);
(*tailptr)->next = NULL;
(*tailptr)->value = data;
regdata_count[offset]++;
}
}
#endif
/* writes to register $CC need to force a partial update */
// if ((offset & ~1) == 0xcc)
// machine.primary_screen->update_partial(machine.primary_screen->vpos());
/* always write to low word? */
zeusbase[offset] = data;
/* log appropriately */
if (logit)
logerror("(%02X) = %08X\n", offset, data);
/* handle the update */
zeus_register_update(machine, offset, oldval, logit);
}
/*************************************
*
* Update state after a register write
*
*************************************/
static void zeus_register_update(running_machine &machine, offs_t offset, UINT32 oldval, int logit)
{
/* handle the writes; only trigger on low accesses */
switch (offset)
{
case 0x08:
zeus_fifo[zeus_fifo_words++] = zeusbase[0x08];
if (zeus_fifo_process(machine, zeus_fifo, zeus_fifo_words))
zeus_fifo_words = 0;
/* set the interrupt signal to indicate we can handle more */
int_timer->adjust(attotime::from_nsec(500));
break;
case 0x20:
/* toggles between two values based on the page:
Page # zeusbase[0x20] zeusbase[0x38]
------ -------------- --------------
0 $04000190 $00000000
1 $04000000 $01900000
*/
zeus_pointer_write(zeusbase[0x20] >> 24, zeusbase[0x20]);
break;
case 0x33:
case 0x34:
case 0x35:
case 0x36:
case 0x37:
machine.primary_screen->update_partial(machine.primary_screen->vpos());
{
int vtotal = zeusbase[0x37] & 0xffff;
int htotal = zeusbase[0x34] >> 16;
rectangle visarea;
visarea.min_x = zeusbase[0x33] >> 16;
visarea.max_x = (zeusbase[0x34] & 0xffff) - 1;
visarea.min_y = 0;
visarea.max_y = zeusbase[0x35] & 0xffff;
if (htotal > 0 && vtotal > 0 && visarea.min_x < visarea.max_x && visarea.max_y < vtotal)
{
machine.primary_screen->configure(htotal, vtotal, visarea, HZ_TO_ATTOSECONDS((double)MIDZEUS_VIDEO_CLOCK / 4.0 / (htotal * vtotal)));
zeus_cliprect = visarea;
zeus_cliprect.max_x -= zeus_cliprect.min_x;
zeus_cliprect.min_x = 0;
}
}
break;
case 0x38:
{
UINT32 temp = zeusbase[0x38];
zeusbase[0x38] = oldval;
machine.primary_screen->update_partial(machine.primary_screen->vpos());
log_fifo = machine.input().code_pressed(KEYCODE_L);
zeusbase[0x38] = temp;
}
break;
case 0x41:
/* this is the address, except in read mode, where it latches values */
if (zeusbase[0x4e] & 0x10)
{
const void *src = waveram0_ptr_from_expanded_addr(oldval);
zeusbase[0x41] = oldval;
zeusbase[0x48] = WAVERAM_READ32(src, 0);
zeusbase[0x49] = WAVERAM_READ32(src, 1);
if (zeusbase[0x4e] & 0x40)
{
zeusbase[0x41]++;
zeusbase[0x41] += (zeusbase[0x41] & 0x400) << 6;
zeusbase[0x41] &= ~0xfc00;
}
}
break;
case 0x48:
case 0x49:
/* if we're in write mode, process it */
if (zeusbase[0x40] == 0x00890000)
{
/*
zeusbase[0x4e]:
bit 0-1: which register triggers write through
bit 3: enable write through via these registers
bit 4: seems to be set during reads, when 0x41 is used for latching
bit 6: enable autoincrement on write through
*/
if ((zeusbase[0x4e] & 0x08) && (offset & 3) == (zeusbase[0x4e] & 3))
{
void *dest = waveram0_ptr_from_expanded_addr(zeusbase[0x41]);
WAVERAM_WRITE32(dest, 0, zeusbase[0x48]);
WAVERAM_WRITE32(dest, 1, zeusbase[0x49]);
if (zeusbase[0x4e] & 0x40)
{
zeusbase[0x41]++;
zeusbase[0x41] += (zeusbase[0x41] & 0x400) << 6;
zeusbase[0x41] &= ~0xfc00;
}
}
}
/* make sure we log anything else */
else if (logit)
logerror("\t[40]=%08X [4E]=%08X\n", zeusbase[0x40], zeusbase[0x4e]);
break;
case 0x51:
/* in this mode, crusnexo expects the reads to immediately latch */
if (zeusbase[0x50] == 0x00a20000)
oldval = zeusbase[0x51];
/* this is the address, except in read mode, where it latches values */
if ((zeusbase[0x5e] & 0x10) || (zeusbase[0x50] == 0x00a20000))
{
const void *src = waveram1_ptr_from_expanded_addr(oldval);
zeusbase[0x51] = oldval;
zeusbase[0x58] = WAVERAM_READ32(src, 0);
zeusbase[0x59] = WAVERAM_READ32(src, 1);
zeusbase[0x5a] = WAVERAM_READ32(src, 2);
if (zeusbase[0x5e] & 0x40)
{
zeusbase[0x51]++;
zeusbase[0x51] += (zeusbase[0x51] & 0x200) << 7;
zeusbase[0x51] &= ~0xfe00;
}
}
break;
case 0x57:
/* thegrid uses this to write either left or right halves of pixels */
if (zeusbase[0x50] == 0x00e90000)
{
void *dest = waveram1_ptr_from_expanded_addr(zeusbase[0x51]);
if (zeusbase[0x57] & 1)
WAVERAM_WRITE32(dest, 0, zeusbase[0x58]);
if (zeusbase[0x57] & 4)
WAVERAM_WRITE32(dest, 1, zeusbase[0x59]);
}
/* make sure we log anything else */
else if (logit)
logerror("\t[50]=%08X [5E]=%08X\n", zeusbase[0x50], zeusbase[0x5e]);
break;
case 0x58:
case 0x59:
case 0x5a:
/* if we're in write mode, process it */
if (zeusbase[0x50] == 0x00890000)
{
/*
zeusbase[0x5e]:
bit 0-1: which register triggers write through
bit 3: enable write through via these registers
bit 4: seems to be set during reads, when 0x51 is used for latching
bit 5: unknown, currently used to specify ordering, but this is suspect
bit 6: enable autoincrement on write through
*/
if ((zeusbase[0x5e] & 0x08) && (offset & 3) == (zeusbase[0x5e] & 3))
{
void *dest = waveram1_ptr_from_expanded_addr(zeusbase[0x51]);
WAVERAM_WRITE32(dest, 0, zeusbase[0x58]);
if (zeusbase[0x5e] & 0x20)
WAVERAM_WRITE32(dest, 1, zeusbase[0x5a]);
else
{
WAVERAM_WRITE32(dest, 1, zeusbase[0x59]);
WAVERAM_WRITE32(dest, 2, zeusbase[0x5a]);
}
if (zeusbase[0x5e] & 0x40)
{
zeusbase[0x51]++;
zeusbase[0x51] += (zeusbase[0x51] & 0x200) << 7;
zeusbase[0x51] &= ~0xfe00;
}
}
}
/* make sure we log anything else */
else if (logit)
logerror("\t[50]=%08X [5E]=%08X\n", zeusbase[0x50], zeusbase[0x5e]);
break;
}
}
/*************************************
*
* Process the FIFO
*
*************************************/
static void zeus_pointer_write(UINT8 which, UINT32 value)
{
#if TRACK_REG_USAGE
subregwrite_count[which]++;
if (subregdata_count[which] < 256)
{
reg_info **tailptr;
for (tailptr = &subregdata[which]; *tailptr != NULL; tailptr = &(*tailptr)->next)
if ((*tailptr)->value == value)
break;
if (*tailptr == NULL)
{
*tailptr = alloc_or_die(reg_info);
(*tailptr)->next = NULL;
(*tailptr)->value = value;
subregdata_count[which]++;
}
}
#endif
switch (which)
{
case 0x04:
zeus_renderbase = waveram1_ptr_from_expanded_addr(value << 16);
break;
case 0x05:
zeus_texbase = value % (WAVERAM0_HEIGHT * WAVERAM0_WIDTH);
break;
case 0x40:
zeus_unknown_40 = value & 0xffffff;
zeus_quad_size = (zeus_unknown_40 == 0) ? 10 : 14;
break;
}
}
/*************************************
*
* Process the FIFO
*
*************************************/
static int zeus_fifo_process(running_machine &machine, const UINT32 *data, int numwords)
{
int dataoffs = 0;
/* handle logging */
switch (data[0] >> 24)
{
/* 0x05: write 32-bit value to low registers */
case 0x05:
if (numwords < 2)
return FALSE;
if (log_fifo)
log_fifo_command(data, numwords, " -- reg32");
if (((data[0] >> 16) & 0x7f) != 0x08)
zeus_register32_w(machine, (data[0] >> 16) & 0x7f, data[1], log_fifo);
break;
/* 0x08: set matrix and point (thegrid) */
case 0x08:
if (numwords < 14)
return FALSE;
dataoffs = 1;
/* 0x07: set matrix and point (crusnexo) */
case 0x07:
if (numwords < 13)
return FALSE;
/* extract the matrix from the raw data */
zeus_matrix[0][0] = tms3203x_device::fp_to_float(data[dataoffs + 1]);
zeus_matrix[0][1] = tms3203x_device::fp_to_float(data[dataoffs + 2]);
zeus_matrix[0][2] = tms3203x_device::fp_to_float(data[dataoffs + 3]);
zeus_matrix[1][0] = tms3203x_device::fp_to_float(data[dataoffs + 4]);
zeus_matrix[1][1] = tms3203x_device::fp_to_float(data[dataoffs + 5]);
zeus_matrix[1][2] = tms3203x_device::fp_to_float(data[dataoffs + 6]);
zeus_matrix[2][0] = tms3203x_device::fp_to_float(data[dataoffs + 7]);
zeus_matrix[2][1] = tms3203x_device::fp_to_float(data[dataoffs + 8]);
zeus_matrix[2][2] = tms3203x_device::fp_to_float(data[dataoffs + 9]);
/* extract the translation point from the raw data */
zeus_point[0] = tms3203x_device::fp_to_float(data[dataoffs + 10]);
zeus_point[1] = tms3203x_device::fp_to_float(data[dataoffs + 11]);
zeus_point[2] = tms3203x_device::fp_to_float(data[dataoffs + 12]);
if (log_fifo)
{
log_fifo_command(data, numwords, "");
logerror("\n\t\tmatrix ( %8.2f %8.2f %8.2f ) ( %8.2f %8.2f %8.2f ) ( %8.2f %8.2f %8.2f )\n\t\tvector %8.2f %8.2f %8.5f\n",
zeus_matrix[0][0], zeus_matrix[0][1], zeus_matrix[0][2],
zeus_matrix[1][0], zeus_matrix[1][1], zeus_matrix[1][2],
zeus_matrix[2][0], zeus_matrix[2][1], zeus_matrix[2][2],
zeus_point[0],
zeus_point[1],
zeus_point[2]);
}
break;
/* 0x15: set point only (thegrid) */
/* 0x16: set point only (crusnexo) */
case 0x15:
case 0x16:
if (numwords < 4)
return FALSE;
/* extract the translation point from the raw data */
zeus_point[0] = tms3203x_device::fp_to_float(data[1]);
zeus_point[1] = tms3203x_device::fp_to_float(data[2]);
zeus_point[2] = tms3203x_device::fp_to_float(data[3]);
if (log_fifo)
{
log_fifo_command(data, numwords, "");
logerror("\n\t\tvector %8.2f %8.2f %8.5f\n",
zeus_point[0],
zeus_point[1],
zeus_point[2]);
}
break;
/* 0x1c: */
case 0x1c:
if (numwords < 4)
return FALSE;
if (log_fifo)
{
log_fifo_command(data, numwords, " -- unknown control + hack clear screen\n");
logerror("\t\tvector %8.2f %8.2f %8.5f\n",
tms3203x_device::fp_to_float(data[1]),
tms3203x_device::fp_to_float(data[2]),
tms3203x_device::fp_to_float(data[3]));
/* extract the translation point from the raw data */
zeus_point2[0] = tms3203x_device::fp_to_float(data[1]);
zeus_point2[1] = tms3203x_device::fp_to_float(data[2]);
zeus_point2[2] = tms3203x_device::fp_to_float(data[3]);
}
{
/* not right -- just a hack */
int x, y;
for (y = zeus_cliprect.min_y; y <= zeus_cliprect.max_y; y++)
for (x = zeus_cliprect.min_x; x <= zeus_cliprect.max_x; x++)
waveram_plot_depth(y, x, 0, 0x7fff);
}
break;
/* 0x23: render model in waveram (thegrid) */
/* 0x24: render model in waveram (crusnexo) */
case 0x23:
case 0x24:
if (numwords < 2)
return FALSE;
if (log_fifo)
log_fifo_command(data, numwords, "");
zeus_draw_model(machine, data[1], data[0] & 0xffff, log_fifo);
break;
/* 0x31: sync pipeline? (thegrid) */
/* 0x32: sync pipeline? (crusnexo) */
case 0x31:
case 0x32:
if (log_fifo)
log_fifo_command(data, numwords, "\n");
zeus_quad_size = 10;
break;
/* 0x38: direct render quad (crusnexo) */
case 0x38:
if (numwords < 12)
return FALSE;
if (log_fifo)
log_fifo_command(data, numwords, "");
break;
/* 0x40: ???? */
case 0x40:
if (log_fifo)
log_fifo_command(data, numwords, "\n");
break;
default:
if (data[0] != 0x2c0)
{
printf("Unknown command %08X\n", data[0]);
if (log_fifo)
log_fifo_command(data, numwords, "\n");
}
break;
}
return TRUE;
}
/*************************************
*
* Draw a model in waveram
*
*************************************/
static void zeus_draw_model(running_machine &machine, UINT32 baseaddr, UINT16 count, int logit)
{
UINT32 databuffer[32];
int databufcount = 0;
int model_done = FALSE;
UINT32 texoffs = 0;
int quadsize = zeus_quad_size;
if (logit)
logerror(" -- model @ %08X, len %04X\n", baseaddr, count);
if (count > 0x1000)
fatalerror("Extreme count\n");
while (baseaddr != 0 && !model_done)
{
const void *base = waveram0_ptr_from_expanded_addr(baseaddr);
int curoffs;
/* reset the objdata address */
baseaddr = 0;
/* loop until we run out of data */
for (curoffs = 0; curoffs <= count; curoffs++)
{
int countneeded = 2;
UINT8 cmd;
/* accumulate 2 words of data */
databuffer[databufcount++] = WAVERAM_READ32(base, curoffs * 2 + 0);
databuffer[databufcount++] = WAVERAM_READ32(base, curoffs * 2 + 1);
/* if this is enough, process the command */
cmd = databuffer[0] >> 24;
if (cmd == 0x38)
countneeded = quadsize;
if (databufcount == countneeded)
{
/* handle logging of the command */
if (logit)
{
int offs;
logerror("\t");
for (offs = 0; offs < databufcount; offs++)
logerror("%08X ", databuffer[offs]);
logerror("-- ");
}
/* handle the command */
switch (cmd)
{
case 0x21: /* thegrid */
case 0x22: /* crusnexo */
if (((databuffer[0] >> 16) & 0xff) == 0x9b)
{
texoffs = databuffer[1];
if (logit)
logerror("texture offset\n");
}
else if (logit)
logerror("unknown offset\n");
break;
case 0x31: /* thegrid */
if (logit)
logerror("sync?\n");
break;
case 0x35: /* thegrid */
case 0x36: /* crusnexo */
if (logit)
logerror("reg32");
zeus_register32_w(machine, (databuffer[0] >> 16) & 0x7f, databuffer[1], logit);
break;
case 0x38: /* crusnexo/thegrid */
zeus_draw_quad(machine, databuffer, texoffs, logit);
break;
default:
if (quadsize == 10)
{
logerror("Correcting quad size\n");
quadsize = 14;
}
if (logit)
logerror("unknown model data\n");
break;
}
/* reset the count */
databufcount = 0;
}
}
}
}
/*************************************
*
* Draw a quad
*
*************************************/
static void zeus_draw_quad(running_machine &machine, const UINT32 *databuffer, UINT32 texoffs, int logit)
{
poly_draw_scanline_func callback;
poly_extra_data *extra;
poly_vertex clipvert[8];
poly_vertex vert[4];
// float uscale, vscale;
float maxy, maxx;
// int val1, val2, texwshift;
int numverts;
int i;
// INT16 normal[3];
// INT32 rotnormal[3];
int texmode = texoffs & 0xffff;
if (logit)
logerror("quad\n");
if (machine.input().code_pressed(KEYCODE_Q) && (texoffs & 0xffff) == 0x119) return;
if (machine.input().code_pressed(KEYCODE_E) && (texoffs & 0xffff) == 0x01d) return;
if (machine.input().code_pressed(KEYCODE_R) && (texoffs & 0xffff) == 0x11d) return;
if (machine.input().code_pressed(KEYCODE_T) && (texoffs & 0xffff) == 0x05d) return;
if (machine.input().code_pressed(KEYCODE_Y) && (texoffs & 0xffff) == 0x0dd) return;
//if (machine.input().code_pressed(KEYCODE_U) && (texoffs & 0xffff) == 0x119) return;
//if (machine.input().code_pressed(KEYCODE_I) && (texoffs & 0xffff) == 0x119) return;
//if (machine.input().code_pressed(KEYCODE_O) && (texoffs & 0xffff) == 0x119) return;
//if (machine.input().code_pressed(KEYCODE_L) && (texoffs & 0x100)) return;
callback = render_poly_8bit;
/*
0 38800000
1 x2 | x1
2 v1 | u1
3 y2 | y1
4 v2 | u2
5 z2 | z1
6 v3 | u3
7 v4 | u4
8 ???
9 x4 | x3
10 y4 | y3
11 z4 | z3
In memory:
+0 = ???
+1 = set via $05410000/value
+2 = x1
+3 = y1
+4 = z1
+5 = x2
+6 = y2
+7 = z2
+8 = x3
+9 = y3
+10= z3
+11= x4
+12= y4
+13= z4
+14= uv1
+15= uv2
+16= uv3
+17= uv4
+18= set via $05200000/$05000000 | (value << 10) (uvoffset?)
+19= ???
38810000 00000000 00C7|FF38 FF5E|FF5E 15400154 11400114 00000000 00000000 FF38|00C7 00A3|00A3 -- quad
xxxx|xxxx yyyy|yyyy xxxx|xxxx yyyy|yyyy
*/
/* extract raw x,y,z */
vert[0].x = (INT16)databuffer[2];
vert[0].y = (INT16)databuffer[3];
vert[0].p[0] = (INT16)databuffer[6];
vert[0].p[1] = (databuffer[1] >> 2) & 0xff;
vert[0].p[2] = (databuffer[1] >> 18) & 0xff;
vert[1].x = (INT16)(databuffer[2] >> 16);
vert[1].y = (INT16)(databuffer[3] >> 16);
vert[1].p[0] = (INT16)(databuffer[6] >> 16);
vert[1].p[1] = (databuffer[4] >> 2) & 0xff;
vert[1].p[2] = (databuffer[4] >> 12) & 0xff;
vert[2].x = (INT16)databuffer[8];
vert[2].y = (INT16)databuffer[9];
vert[2].p[0] = (INT16)databuffer[7];
vert[2].p[1] = (databuffer[4] >> 22) & 0xff;
vert[2].p[2] = (databuffer[5] >> 2) & 0xff;
vert[3].x = (INT16)(databuffer[8] >> 16);
vert[3].y = (INT16)(databuffer[9] >> 16);
vert[3].p[0] = (INT16)(databuffer[7] >> 16);
vert[3].p[1] = (databuffer[5] >> 12) & 0xff;
vert[3].p[2] = (databuffer[5] >> 22) & 0xff;
/*
vert[0].x = (INT16)databuffer[1];
vert[0].y = (INT16)databuffer[3];
vert[0].p[0] = (INT16)databuffer[5];
vert[0].p[1] = (UINT16)databuffer[2];
vert[0].p[2] = (UINT16)(databuffer[2] >> 16);
vert[1].x = (INT16)(databuffer[1] >> 16);
vert[1].y = (INT16)(databuffer[3] >> 16);
vert[1].p[0] = (INT16)(databuffer[5] >> 16);
vert[1].p[1] = (UINT16)databuffer[4];
vert[1].p[2] = (UINT16)(databuffer[4] >> 16);
vert[2].x = (INT16)databuffer[9];
vert[2].y = (INT16)databuffer[10];
vert[2].p[0] = (INT16)databuffer[11];
vert[2].p[1] = (UINT16)databuffer[6];
vert[2].p[2] = (UINT16)(databuffer[6] >> 16);
vert[3].x = (INT16)(databuffer[9] >> 16);
vert[3].y = (INT16)(databuffer[10] >> 16);
vert[3].p[0] = (INT16)(databuffer[11] >> 16);
vert[3].p[1] = (UINT16)databuffer[7];
vert[3].p[2] = (UINT16)(databuffer[7] >> 16);
*/
for (i = 0; i < 4; i++)
{
float x = vert[i].x;
float y = vert[i].y;
float z = vert[i].p[0];
vert[i].x = x * zeus_matrix[0][0] + y * zeus_matrix[0][1] + z * zeus_matrix[0][2] + zeus_point[0];
vert[i].y = x * zeus_matrix[1][0] + y * zeus_matrix[1][1] + z * zeus_matrix[1][2] + zeus_point[1];
vert[i].p[0] = x * zeus_matrix[2][0] + y * zeus_matrix[2][1] + z * zeus_matrix[2][2] + zeus_point[2];
vert[i].p[0] += zbase;
vert[i].p[2] += texoffs >> 16;
vert[i].p[1] *= 256.0f;
vert[i].p[2] *= 256.0f;
if (logit)
{
logerror("\t\t(%f,%f,%f) (%02X,%02X)\n",
vert[i].x, vert[i].y, vert[i].p[0],
(int)(vert[i].p[1] / 256.0f), (int)(vert[i].p[2] / 256.0f));
}
}
numverts = poly_zclip_if_less(4, &vert[0], &clipvert[0], 4, 1.0f / 512.0f / 4.0f);
if (numverts < 3)
return;
maxx = maxy = -1000.0f;
for (i = 0; i < numverts; i++)
{
// 412.0f here works for crusnexo
float ooz = 512.0f / clipvert[i].p[0];
// ooz *= 1.0f / (512.0f * 512.0f);
clipvert[i].x *= ooz;
clipvert[i].y *= ooz;
clipvert[i].x += 256.5f;
clipvert[i].y += 200.5f;
clipvert[i].p[0] *= 65536.0f * 16.0f;
maxx = MAX(maxx, clipvert[i].x);
maxy = MAX(maxy, clipvert[i].y);
if (logit)
logerror("\t\t\tTranslated=(%f,%f)\n", clipvert[i].x, clipvert[i].y);
}
for (i = 0; i < numverts; i++)
{
if (clipvert[i].x == maxx)
clipvert[i].x += 0.0005f;
if (clipvert[i].y == maxy)
clipvert[i].y += 0.0005f;
}
extra = (poly_extra_data *)poly_get_extra_data(poly);
switch (texmode)
{
case 0x01d: /* crusnexo: RHS of score bar */
case 0x05d: /* crusnexo: background, road */
case 0x0dd: /* crusnexo: license plate letters */
case 0x11d: /* crusnexo: LHS of score bar */
case 0x15d: /* crusnexo */
case 0x85d: /* crusnexo */
case 0x95d: /* crusnexo */
case 0xc1d: /* crusnexo */
case 0xc5d: /* crusnexo */
extra->texwidth = 256;
break;
case 0x059: /* crusnexo */
case 0x0d9: /* crusnexo */
case 0x119: /* crusnexo: license plates */
case 0x159: /* crusnexo */
extra->texwidth = 128;
break;
case 0x055: /* crusnexo */
case 0x155: /* crusnexo */
extra->texwidth = 64;
break;
default:
{
static UINT8 hits[0x10000];
if (!hits[(texoffs & 0xffff)])
{
hits[(texoffs & 0xffff)] = 1;
printf("format = %04X\n", (texoffs & 0xffff));
}
break;
}
}
extra->solidcolor = 0;//zeusbase[0x00] & 0x7fff;
extra->zoffset = 0;//zeusbase[0x7e] >> 16;
extra->alpha = 0;//zeusbase[0x4e];
extra->transcolor = 0x100;//((databuffer[1] >> 16) & 1) ? 0 : 0x100;
extra->texbase = WAVERAM_BLOCK0(zeus_texbase);
extra->palbase = waveram0_ptr_from_expanded_addr(zeusbase[0x41]);
poly_render_quad_fan(poly, NULL, &zeus_cliprect, callback, 4, numverts, &clipvert[0]);
}
/*************************************
*
* Rasterizers
*
*************************************/
static void render_poly_8bit(void *dest, INT32 scanline, const poly_extent *extent, const void *extradata, int threadid)
{
const poly_extra_data *extra = (const poly_extra_data *)extradata;
INT32 curz = extent->param[0].start;
INT32 curu = extent->param[1].start;
INT32 curv = extent->param[2].start;
// INT32 curi = extent->param[3].start;
INT32 dzdx = extent->param[0].dpdx;
INT32 dudx = extent->param[1].dpdx;
INT32 dvdx = extent->param[2].dpdx;
// INT32 didx = extent->param[3].dpdx;
const void *texbase = extra->texbase;
const void *palbase = extra->palbase;
UINT16 transcolor = extra->transcolor;
int texwidth = extra->texwidth;
int x;
for (x = extent->startx; x < extent->stopx; x++)
{
UINT16 *depthptr = WAVERAM_PTRDEPTH(zeus_renderbase, scanline, x);
INT32 depth = (curz >> 16) + extra->zoffset;
if (depth > 0x7fff) depth = 0x7fff;
if (depth >= 0 && depth <= *depthptr)
{
int u0 = (curu >> 8);// & (texwidth - 1);
int v0 = (curv >> 8);// & 255;
int u1 = (u0 + 1);
int v1 = (v0 + 1);
UINT8 texel0 = get_texel_8bit(texbase, v0, u0, texwidth);
UINT8 texel1 = get_texel_8bit(texbase, v0, u1, texwidth);
UINT8 texel2 = get_texel_8bit(texbase, v1, u0, texwidth);
UINT8 texel3 = get_texel_8bit(texbase, v1, u1, texwidth);
if (texel0 != transcolor)
{
rgb_t color0 = WAVERAM_READ16(palbase, texel0);
rgb_t color1 = WAVERAM_READ16(palbase, texel1);
rgb_t color2 = WAVERAM_READ16(palbase, texel2);
rgb_t color3 = WAVERAM_READ16(palbase, texel3);
rgb_t filtered;
color0 = ((color0 & 0x7c00) << 9) | ((color0 & 0x3e0) << 6) | ((color0 & 0x1f) << 3);
color1 = ((color1 & 0x7c00) << 9) | ((color1 & 0x3e0) << 6) | ((color1 & 0x1f) << 3);
color2 = ((color2 & 0x7c00) << 9) | ((color2 & 0x3e0) << 6) | ((color2 & 0x1f) << 3);
color3 = ((color3 & 0x7c00) << 9) | ((color3 & 0x3e0) << 6) | ((color3 & 0x1f) << 3);
filtered = rgb_bilinear_filter(color0, color1, color2, color3, curu, curv);
WAVERAM_WRITEPIX(zeus_renderbase, scanline, x, filtered);
*depthptr = depth;
}
}
curz += dzdx;
curu += dudx;
curv += dvdx;
// curi += didx;
}
}
/*************************************
*
* Debugging tools
*
*************************************/
static void log_fifo_command(const UINT32 *data, int numwords, const char *suffix)
{
int wordnum;
logerror("Zeus cmd %02X :", data[0] >> 24);
for (wordnum = 0; wordnum < numwords; wordnum++)
logerror(" %08X", data[wordnum]);
logerror("%s", suffix);
}
#if 0
static void log_waveram(UINT32 base, UINT16 length)
{
static struct
{
UINT32 base;
UINT16 length;
UINT32 checksum;
} recent_entries[100];
UINT32 numoctets = length + 1;
const void *ptr = waveram0_ptr_from_expanded_addr(base);
UINT32 checksum = base | ((UINT64)length << 32);
int foundit = FALSE;
int i;
for (i = 0; i < numoctets; i++)
checksum += WAVERAM_READ32(ptr, i*2) + WAVERAM_READ32(ptr, i*2+1);
for (i = 0; i < ARRAY_LENGTH(recent_entries); i++)
if (recent_entries[i].base == base && recent_entries[i].length == length && recent_entries[i].checksum == checksum)
{
foundit = TRUE;
break;
}
if (i == ARRAY_LENGTH(recent_entries))
i--;
if (i != 0)
{
memmove(&recent_entries[1], &recent_entries[0], i * sizeof(recent_entries[0]));
recent_entries[0].base = base;
recent_entries[0].length = length;
recent_entries[0].checksum = checksum;
}
if (foundit)
return;
for (i = 0; i < numoctets; i++)
logerror("\t%02X: %08X %08X\n", i, WAVERAM_READ32(ptr, i*2), WAVERAM_READ32(ptr, i*2+1));
}
#endif