mirror of
https://github.com/RetroDECK/Supermodel.git
synced 2024-11-22 22:05:38 +00:00
938 lines
25 KiB
C
938 lines
25 KiB
C
/*
|
|
* Sega Model 3 Emulator
|
|
* Copyright (C) 2003 Bart Trzynadlowski, Ville Linde, Stefano Teso
|
|
*
|
|
* This program is free software; you can redistribute it and/or modify it
|
|
* under the terms of the GNU General Public License Version 2 as published
|
|
* by the Free Software Foundation.
|
|
*
|
|
* This program is distributed in the hope that it will be useful, but
|
|
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
|
|
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
|
* for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License along
|
|
* with this program (license.txt); if not, write to the Free Software
|
|
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
|
*/
|
|
|
|
/*
|
|
* r3d.c
|
|
*
|
|
* Real3D Model 3 graphics system emulation. The 3D hardware in Model 3 is
|
|
* supposedly based on the Pro-1000.
|
|
*
|
|
* To-Do List:
|
|
* -----------
|
|
* - In VS2_98, in the attract mode, when the camera zooms in on the score
|
|
* board for South Africa vs. Nigeria, the "Nigeria" text texture is
|
|
* garbage.
|
|
* - RAM texture uploading is too weird to be true.
|
|
*/
|
|
|
|
/*
|
|
* RAM Size:
|
|
* ---------
|
|
*
|
|
* It appears that there is 2MB total of "culling RAM." 1MB appears at
|
|
* 0x8C000000 and the other at 0x8E000000. Step 1.0 and 1.5 appear to have
|
|
* 1MB of polygon RAM, but Step 2.0 (and probably 2.1) clearly uses 2MB.
|
|
*/
|
|
|
|
#include "model3.h"
|
|
|
|
extern UINT8 *ram;
|
|
|
|
/******************************************************************/
|
|
/* Privates */
|
|
/******************************************************************/
|
|
|
|
static UINT8 *culling_ram_8e; // culling RAM at 0x8E000000
|
|
static UINT8 *culling_ram_8c; // culling RAM at 0x8C000000
|
|
static UINT8 *polygon_ram; // polygon RAM at 0x98000000
|
|
static UINT8 *texture_ram; // texture RAM
|
|
static UINT8 *vrom;
|
|
|
|
static UINT8 texture_buffer_ram[1*1024*1024];
|
|
|
|
static UINT32 vrom_texture_address;
|
|
static UINT32 vrom_texture_header;
|
|
|
|
static UINT32 texture_last_addr = 0;
|
|
static UINT32 texture_header = 0;
|
|
|
|
/******************************************************************/
|
|
/* Interface */
|
|
/******************************************************************/
|
|
|
|
/*
|
|
* void r3d_init(UINT8 *culling_ram_8e_ptr, UINT8 *culling_ram_8c_ptr,
|
|
* UINT8 *polygon_ram_ptr, UINT8 *texture_ram, UINT8 *vrom_ptr);
|
|
*
|
|
* Initializes the Real3D graphics emulation.
|
|
*
|
|
* Parameters:
|
|
* culling_ram_8e_ptr = Pointer to 0x8E000000 culling RAM.
|
|
* culling_ram_8c_ptr = Pointer to 0x8C000000 culling RAM.
|
|
* polygon_ram_ptr = Pointer to polygon RAM.
|
|
* texture_ram = Pointer to texture RAM.
|
|
* vrom_ptr = Pointer to VROM.
|
|
*/
|
|
|
|
void r3d_init(UINT8 *culling_ram_8e_ptr, UINT8 *culling_ram_8c_ptr,
|
|
UINT8 *polygon_ram_ptr, UINT8 *texture_ram_ptr, UINT8 *vrom_ptr)
|
|
{
|
|
culling_ram_8e = culling_ram_8e_ptr;
|
|
culling_ram_8c = culling_ram_8c_ptr;
|
|
polygon_ram = polygon_ram_ptr;
|
|
texture_ram = texture_ram_ptr;
|
|
vrom = vrom_ptr;
|
|
|
|
LOG_INIT("texture.log");
|
|
}
|
|
|
|
/*
|
|
* void r3d_shutdown(void);
|
|
*
|
|
* Shuts down the Real3D emulation.
|
|
*/
|
|
|
|
void r3d_shutdown(void)
|
|
{
|
|
}
|
|
|
|
/*
|
|
* void r3d_reset(void);
|
|
*
|
|
* Resets the Real3D graphics hardware. RAM is cleared in order to prevent
|
|
* the renderer from drawing garbage and possibly locking up as a result.
|
|
*/
|
|
|
|
void r3d_reset(void)
|
|
{
|
|
memset(culling_ram_8e, 0, 1*1024*1024);
|
|
memset(culling_ram_8c, 0, 4*1024*1024);
|
|
memset(polygon_ram, 0, 2*1024*1024);
|
|
memset(texture_ram, 0, 2048*2048*2);
|
|
tap_reset();
|
|
}
|
|
|
|
/*
|
|
* void r3d_save_state(FILE *fp);
|
|
*
|
|
* Saves the state of the Real3D graphics hardware to a file.
|
|
*
|
|
* Parameters:
|
|
* fp = File to save to.
|
|
*/
|
|
|
|
void r3d_save_state(FILE *fp)
|
|
{
|
|
fwrite(culling_ram_8e, sizeof(UINT8), 1*1024*1024, fp);
|
|
fwrite(culling_ram_8c, sizeof(UINT8), 4*1024*1024, fp);
|
|
fwrite(polygon_ram, sizeof(UINT8), 2*1024*1024, fp);
|
|
fwrite(texture_ram, sizeof(UINT8), 2048*2048*2, fp);
|
|
}
|
|
|
|
/*
|
|
* void r3d_load_state(FILE *fp);
|
|
*
|
|
* Loads the state of the Real3D graphics hardware from a file.
|
|
*
|
|
* Parameters:
|
|
* fp = File to load from.
|
|
*/
|
|
|
|
void r3d_load_state(FILE *fp)
|
|
{
|
|
fread(culling_ram_8e, sizeof(UINT8), 1*1024*1024, fp);
|
|
fread(culling_ram_8c, sizeof(UINT8), 4*1024*1024, fp);
|
|
fread(polygon_ram, sizeof(UINT8), 2*1024*1024, fp);
|
|
fread(texture_ram, sizeof(UINT8), 2048*2048*2, fp);
|
|
|
|
osd_renderer_invalidate_textures(0, 0, 0, 0, 2048, 2048, texture_ram, 0);
|
|
}
|
|
|
|
/******************************************************************/
|
|
/* Texture Memory Management */
|
|
/******************************************************************/
|
|
|
|
static const INT decode[64] =
|
|
{
|
|
0, 1, 4, 5, 8, 9,12,13,
|
|
2, 3, 6, 7,10,11,14,15,
|
|
16,17,20,21,24,25,28,29,
|
|
18,19,22,23,26,27,30,31,
|
|
32,33,36,37,40,41,44,45,
|
|
34,35,38,39,42,43,46,47,
|
|
48,49,52,53,56,57,60,61,
|
|
50,51,54,55,58,59,62,63
|
|
};
|
|
|
|
/*
|
|
* store_texture_tile():
|
|
*
|
|
* Writes a single 8x8 texture tile into the appropriate part of the texture
|
|
* sheet.
|
|
*/
|
|
|
|
static void store_texture_tile(UINT x, UINT y, UINT8 *src, UINT bpp, BOOL little_endian)
|
|
{
|
|
UINT xi, yi, pixel_offs;
|
|
UINT16 rgb16;
|
|
UINT8 gray8;
|
|
|
|
for (yi = 0; yi < 8; yi++)
|
|
{
|
|
for (xi = 0; xi < 8; xi++)
|
|
{
|
|
/*
|
|
* Grab the pixel offset from the decode[] array and fetch the
|
|
* pixel word
|
|
*/
|
|
|
|
if (little_endian)
|
|
{
|
|
if (bpp == 2)
|
|
{
|
|
/*
|
|
* XOR with 1 in little endian mode -- every word contains
|
|
* 2 16-bit pixels, thus they are swapped
|
|
*/
|
|
|
|
pixel_offs = decode[(yi * 8 + xi) ^ 1] * 2;
|
|
rgb16 = *(UINT16 *) &src[pixel_offs];
|
|
}
|
|
else
|
|
{
|
|
pixel_offs = decode[((yi ^ 1) * 8 + (xi ^ 1))];
|
|
gray8 = src[pixel_offs];
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (bpp == 2)
|
|
{
|
|
pixel_offs = decode[yi * 8 + xi] * 2;
|
|
rgb16 = (src[pixel_offs + 0] << 8) | src[pixel_offs + 1];
|
|
}
|
|
else
|
|
{
|
|
pixel_offs = decode[yi * 8 + xi];
|
|
gray8 = src[pixel_offs + 0];
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Store within the texture sheet
|
|
*/
|
|
|
|
if (bpp == 2)
|
|
*(UINT16 *) &texture_ram[((y + yi) * 2048 + (x + xi)) * 2] = rgb16;
|
|
else
|
|
texture_ram[(((y + yi) * 2048 + x) * 2) + xi] = gray8;
|
|
}
|
|
}
|
|
}
|
|
|
|
/*
|
|
* store_texture():
|
|
*
|
|
* Writes a texture into the texture sheet. The pixel words are not decoded,
|
|
* but the 16-bit pixels are converted into a common endianness (little.)
|
|
* 8-bit pixels are not expanded into 16-bits.
|
|
*
|
|
* bpp (bytes per pixel) must be 1 or 2.
|
|
*/
|
|
|
|
static void store_texture(UINT x, UINT y, UINT w, UINT h, UINT8 *src, UINT bpp, BOOL little_endian)
|
|
{
|
|
UINT xi, yi;
|
|
UINT bw;
|
|
|
|
if(bpp == 2)
|
|
bw = 1;
|
|
else
|
|
bw = 2;
|
|
|
|
for (yi = 0; yi < h; yi += 8)
|
|
{
|
|
for (xi = 0; xi < w; xi += 8)
|
|
{
|
|
store_texture_tile(x + (xi / bw), y + yi, src, bpp, little_endian);
|
|
src += 8 * 8 * bpp; // each texture tile is 8x8 and 16-bit color
|
|
}
|
|
}
|
|
}
|
|
|
|
/*
|
|
* upload_texture():
|
|
*
|
|
* Uploads a texture to texture memory.
|
|
*/
|
|
|
|
// Mipmap starting positions for each level
|
|
static const int mipmap_xpos[11] =
|
|
{ 1024, 1536, 1792, 1920, 1984, 2016, 2032, 2040, 2044, 2046, 2047 };
|
|
static const int mipmap_ypos[11] =
|
|
{ 512, 768, 896, 960, 992, 1008, 1016, 1020, 1022, 1023, 0 };
|
|
|
|
// Mipmap size dividers
|
|
static const int mipmap_size[9] =
|
|
{ 2, 4, 8, 16, 32, 64, 128, 256, 512 };
|
|
|
|
static void upload_texture(UINT32 header, UINT8 *src, BOOL little_endian)
|
|
{
|
|
UINT size_x, size_y, xpos, ypos, bit_depth, mip_ypos, page;
|
|
int mipmap_num = 0;
|
|
int mxpos, mypos, msize_x, msize_y;
|
|
|
|
PROFILE_SECT_ENTRY("real3d");
|
|
|
|
/*
|
|
* Model 3 texture RAM appears as 2 2048x1024 textures. When textures are
|
|
* uploaded, their size and position within a sheet is given. I treat the
|
|
* texture sheet selection bit as an additional bit to the Y coordinate.
|
|
*/
|
|
|
|
size_x = (header >> 14) & 7;
|
|
size_y = (header >> 17) & 7;
|
|
size_x = (32 << size_x); // width in pixels
|
|
size_y = (32 << size_y); // height
|
|
|
|
ypos = (((header >> 7) & 0x1F) | ((header >> 15) & 0x20)) * 32;
|
|
xpos = ((header >> 0) & 0x3F) * 32;
|
|
|
|
mip_ypos = ((header >> 7) & 0x1F) * 32;
|
|
page = (header >> 15 & 0x20) ? 1 : 0;
|
|
|
|
if( header & 0x00800000 )
|
|
bit_depth = 2; // 16-bit texture
|
|
else
|
|
bit_depth = 1; // 8-bit texture
|
|
|
|
LOG("texture.log", "%08X %d,%d\t%dx%d\n", header, xpos, ypos, size_x, size_y);
|
|
|
|
/*
|
|
* Render the texture into the texture buffer
|
|
*/
|
|
|
|
switch( (header >> 24) & 0xF )
|
|
{
|
|
case 0: // Texture with mipmaps
|
|
store_texture(xpos, ypos, size_x, size_y, src, bit_depth, little_endian);
|
|
osd_renderer_invalidate_textures(xpos, ypos, xpos, ypos, size_x, size_y, texture_ram, 0);
|
|
|
|
msize_x = size_x;
|
|
msize_y = size_y;
|
|
|
|
// Store mipmaps
|
|
while( msize_y > 8 && msize_x > 8 ) {
|
|
|
|
src += (msize_x * msize_y * bit_depth);
|
|
msize_x /= 2;
|
|
msize_y /= 2;
|
|
|
|
mxpos = mipmap_xpos[mipmap_num] + (xpos / mipmap_size[mipmap_num]);
|
|
mypos = mipmap_ypos[mipmap_num] + (mip_ypos / mipmap_size[mipmap_num]);
|
|
if(page)
|
|
mypos += 1024;
|
|
mipmap_num++;
|
|
|
|
store_texture(mxpos, mypos, msize_x, msize_y, src, bit_depth, little_endian);
|
|
//osd_renderer_invalidate_textures(mxpos, mypos, xpos, ypos, size_x, size_y, texture_ram, mipmap_num);
|
|
}
|
|
break;
|
|
|
|
case 1: // Texture without mipmaps
|
|
store_texture(xpos, ypos, size_x, size_y, src, bit_depth, little_endian);
|
|
osd_renderer_invalidate_textures(xpos, ypos, xpos, ypos, size_x, size_y, texture_ram, 0);
|
|
break;
|
|
|
|
case 2: // Only mipmaps
|
|
msize_x = size_x;
|
|
msize_y = size_y;
|
|
while( msize_y > 8 && msize_x > 8 ) {
|
|
|
|
msize_x /= 2;
|
|
msize_y /= 2;
|
|
|
|
mxpos = mipmap_xpos[mipmap_num] + (xpos / mipmap_size[mipmap_num]);
|
|
mypos = mipmap_ypos[mipmap_num] + (mip_ypos / mipmap_size[mipmap_num]);
|
|
if(page)
|
|
mypos += 1024;
|
|
mipmap_num++;
|
|
|
|
store_texture(mxpos, mypos, msize_x, msize_y, src, bit_depth, little_endian);
|
|
//osd_renderer_invalidate_textures(mxpos, mypos, xpos, ypos, size_x, size_y, texture_ram, mipmap_num);
|
|
|
|
src += (msize_x * msize_y * bit_depth);
|
|
}
|
|
break;
|
|
}
|
|
|
|
/*
|
|
* Remove any existing textures that may have been overwritten
|
|
*/
|
|
|
|
//osd_renderer_invalidate_textures(xpos, ypos, size_x, size_y);
|
|
|
|
PROFILE_SECT_EXIT("real3d");
|
|
}
|
|
|
|
/******************************************************************/
|
|
/* Access */
|
|
/******************************************************************/
|
|
|
|
static BOOL trigger = 0;
|
|
static UINT64 trigger_time;
|
|
|
|
/*
|
|
* UINT32 r3d_read_32(UINT32 a);
|
|
*
|
|
* Reads a 32-bit word from the Real3D regions.
|
|
*
|
|
* Parameters:
|
|
* a = Address.
|
|
*
|
|
* Returns:
|
|
* Data read.
|
|
*/
|
|
|
|
UINT32 r3d_read_32(UINT32 a)
|
|
{
|
|
static UINT32 _84000000 = 0;
|
|
|
|
// message(0, "%08X (%08X): Real3D read32 to %08X", PPC_PC, PPC_LR, a);
|
|
|
|
switch (a)
|
|
{
|
|
/*
|
|
* In Lost World, routine at 0x1174E0 reads all the 0x840000XX status
|
|
* registers and at 0x117A30, bit 0x02000000 is checked for.
|
|
*/
|
|
|
|
case 0x84000000:
|
|
return (_84000000 ^= 0xFFFFFFFF);
|
|
case 0x84000004: // unknown
|
|
case 0x84000008:
|
|
case 0x8400000C:
|
|
case 0x84000010:
|
|
case 0x84000014:
|
|
case 0x84000018:
|
|
case 0x8400001C:
|
|
case 0x84000020:
|
|
return 0xFFFFFFFF;
|
|
}
|
|
|
|
error("Unknown R3D read: %08X: %08X\n", ppc_get_pc(), a);
|
|
return(0);
|
|
}
|
|
|
|
/*
|
|
* void r3d_write_32(UINT32 a, UINT32 d);
|
|
*
|
|
* Writes a 32-bit word to the Real3D regions.
|
|
*
|
|
* Parameters:
|
|
* a = Address.
|
|
* d = Data to write.
|
|
*/
|
|
|
|
UINT32 _9C000000, _9C000004, _9C000008;
|
|
static int texture_start_pos = 8;
|
|
static int texture_ram_ptr = 0;
|
|
|
|
void r3d_write_32(UINT32 a, UINT32 d)
|
|
{
|
|
static UINT32 last_addr;
|
|
|
|
if (a >= 0x8E000000 && a <= 0x8E0FFFFF) // culling RAM
|
|
{
|
|
*(UINT32 *) &culling_ram_8e[a & 0xFFFFF] = BSWAP32(d);
|
|
return;
|
|
}
|
|
else if (a >= 0x8C000000 && a <= 0x8C3FFFFF) // culling RAM
|
|
{
|
|
*(UINT32 *) &culling_ram_8c[a & 0x3FFFFF] = BSWAP32(d);
|
|
return;
|
|
}
|
|
else if (a >= 0x98000000 && a <= 0x981FFFFF) // polygon RAM
|
|
{
|
|
// if(a >= 0x98001000 && a < 0x98002000)
|
|
// message(1, "color table: %08X = %08X", a, BSWAP32(d));
|
|
|
|
*(UINT32 *) &polygon_ram[a & 0x1FFFFF] = BSWAP32(d);
|
|
return;
|
|
}
|
|
else if (a >= 0x94000000 && a <= 0x940FFFFF) // texture buffer
|
|
{
|
|
d = BSWAP32(d);
|
|
*(UINT32 *)&texture_buffer_ram[texture_ram_ptr] = d;
|
|
|
|
texture_ram_ptr += 4;
|
|
return;
|
|
}
|
|
|
|
switch (a)
|
|
{
|
|
case 0x88000000: // trigger?
|
|
|
|
//message(0, "%08X (%08X): 88000000 = %08X", PPC_PC, PPC_LR, BSWAP32(d));
|
|
|
|
if( texture_ram_ptr > 0 ) {
|
|
int i=0;
|
|
while( i < texture_ram_ptr ) {
|
|
UINT32 length = (*(UINT32*)&texture_buffer_ram[i+0] / 2) + 2;
|
|
UINT32 header = *(UINT32*)&texture_buffer_ram[i+4];
|
|
upload_texture( header, &texture_buffer_ram[i+8], 1 );
|
|
i += length;
|
|
};
|
|
}
|
|
texture_ram_ptr = 0;
|
|
|
|
return;
|
|
case 0x90000000: // VROM texture address
|
|
vrom_texture_address = BSWAP32(d);
|
|
LOG("model3.log", "VROM1 ADDR = %08X\n", BSWAP32(d));
|
|
//message(0, "VROM texture address = %08X @ %08X (%08X)", BSWAP32(d), PPC_PC, PPC_LR);
|
|
return;
|
|
case 0x90000004:
|
|
vrom_texture_header = BSWAP32(d);
|
|
LOG("model3.log", "VROM1 HEAD = %08X\n", BSWAP32(d));
|
|
//message(0, "VROM texture header = %08X @ %08X (%08X)", BSWAP32(d), PPC_PC, PPC_LR);
|
|
return;
|
|
case 0x90000008:
|
|
upload_texture(vrom_texture_header, &vrom[(vrom_texture_address & 0xFFFFFF) * 4], 1);
|
|
LOG("model3.log", "VROM1 SIZE = %08X\n", BSWAP32(d));
|
|
//message(0, "VROM texture length = %08X @ %08X (%08X)", BSWAP32(d), PPC_PC, PPC_LR);
|
|
return;
|
|
case 0x9000000C: // ? Virtual On 2: These are almost certainly for VROM textures as well (I was too lazy to check :P)
|
|
vrom_texture_address = BSWAP32(d);
|
|
LOG("model3.log", "VROM2 ADDR = %08X\n", BSWAP32(d));
|
|
//message(0, "90000000C = %08X", BSWAP32(d));
|
|
return;
|
|
case 0x90000010: // ?
|
|
vrom_texture_header = BSWAP32(d);
|
|
LOG("model3.log", "VROM2 HEAD = %08X\n", BSWAP32(d));
|
|
//message(0, "900000010 = %08X", BSWAP32(d));
|
|
return;
|
|
case 0x90000014: // ?
|
|
upload_texture(vrom_texture_header, &vrom[(vrom_texture_address & 0xFFFFFF) * 4], 1);
|
|
LOG("model3.log", "VROM2 SIZE = %08X\n", BSWAP32(d));
|
|
//message(0, "900000014 = %08X", BSWAP32(d));
|
|
return;
|
|
case 0x9C000000: // ?
|
|
//message(0, "9C000000 = %08X", BSWAP32(d));
|
|
LOG("model3.log", "%08X = %08X\n", a, d);
|
|
_9C000000 = BSWAP32(d);
|
|
return;
|
|
case 0x9C000004: // ?
|
|
//message(0, "9C000004 = %08X", BSWAP32(d));
|
|
LOG("model3.log", "%08X = %08X\n", a, d);
|
|
_9C000004 = BSWAP32(d);
|
|
return;
|
|
case 0x9C000008: // ?
|
|
//message(0, "9C000008 = %08X", BSWAP32(d));
|
|
LOG("model3.log", "%08X = %08X\n", a, d);
|
|
_9C000008 = BSWAP32(d);
|
|
return;
|
|
}
|
|
|
|
error("Unknown R3D write: %08X: %08X = %08X\n", ppc_get_pc(), a, d);
|
|
}
|
|
|
|
void r3d_dma_culling_ram_8c(UINT32 *src, UINT32 dst, int length, BOOL swap_words)
|
|
{
|
|
int i;
|
|
|
|
dst &= 0xffffff;
|
|
|
|
if (swap_words)
|
|
{
|
|
for (i=0; i < length; i+=4)
|
|
{
|
|
UINT32 d = BSWAP32(*src++);
|
|
*(UINT32 *)&culling_ram_8c[dst] = d;
|
|
dst += 4;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
for (i=0; i < length; i+=4)
|
|
{
|
|
UINT32 d = (*src++);
|
|
*(UINT32 *)&culling_ram_8c[dst] = d;
|
|
dst += 4;
|
|
}
|
|
}
|
|
}
|
|
|
|
void r3d_dma_culling_ram_8e(UINT32 *src, UINT32 dst, int length, BOOL swap_words)
|
|
{
|
|
int i;
|
|
|
|
dst &= 0xffffff;
|
|
|
|
if (swap_words)
|
|
{
|
|
for (i=0; i < length; i+=4)
|
|
{
|
|
UINT32 d = BSWAP32(*src++);
|
|
*(UINT32 *)&culling_ram_8e[dst] = d;
|
|
dst += 4;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
for (i=0; i < length; i+=4)
|
|
{
|
|
UINT32 d = (*src++);
|
|
*(UINT32 *)&culling_ram_8e[dst] = d;
|
|
dst += 4;
|
|
}
|
|
}
|
|
}
|
|
|
|
void r3d_dma_polygon_ram(UINT32 *src, UINT32 dst, int length, BOOL swap_words)
|
|
{
|
|
int i;
|
|
|
|
dst &= 0xffffff;
|
|
|
|
if (swap_words)
|
|
{
|
|
for (i=0; i < length; i+=4)
|
|
{
|
|
UINT32 d = BSWAP32(*src++);
|
|
*(UINT32 *) &polygon_ram[dst] = d;
|
|
dst += 4;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
for (i=0; i < length; i+=4)
|
|
{
|
|
UINT32 d = (*src++);
|
|
*(UINT32 *) &polygon_ram[dst] = d;
|
|
dst += 4;
|
|
}
|
|
}
|
|
}
|
|
|
|
void r3d_dma_texture_ram(UINT32 *src, UINT32 dst, int length, BOOL swap_words)
|
|
{
|
|
int i;
|
|
|
|
dst &= 0xffffff;
|
|
|
|
if (swap_words)
|
|
{
|
|
for (i=0; i < length; i+=4)
|
|
{
|
|
UINT32 d = BSWAP32(*src++);
|
|
*(UINT32 *)&texture_buffer_ram[texture_ram_ptr] = d;
|
|
texture_ram_ptr += 4;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
for (i=0; i < length; i+=4)
|
|
{
|
|
UINT32 d = (*src++);
|
|
*(UINT32 *)&texture_buffer_ram[texture_ram_ptr] = d;
|
|
texture_ram_ptr += 4;
|
|
}
|
|
}
|
|
}
|
|
|
|
/******************************************************************/
|
|
/* Real3D TAP Port */
|
|
/******************************************************************/
|
|
|
|
/*
|
|
* State (corresponding to fsm[][] Y) and Instruction Names
|
|
*/
|
|
|
|
static char *state_name[] = { "Test-Logic/Reset", "Run-Test/Idle", "Select-DR-Scan",
|
|
"Capture-DR", "Shift-DR", "Exit1-DR", "Pause-DR",
|
|
"Exit2-DR", "Update-DR", "Select-IR-Scan",
|
|
"Capture-IR", "Shift-IR", "Exit1-IR", "Pause-IR",
|
|
"Exit2-IR", "Update-IR"
|
|
};
|
|
|
|
/*
|
|
* TAP Finite State Machine
|
|
*
|
|
* Y are states and X are outgoing paths. Constructed from information on page
|
|
* 167 of the 3D-RAM manual.
|
|
*/
|
|
|
|
#define NEXT(new_state) fsm[state][new_state]
|
|
|
|
static INT state; // current state
|
|
static INT fsm[][2] = {
|
|
{ 1, 0 }, // 0 Test-Logic/Reset
|
|
{ 1, 2 }, // 1 Run-Test/Idle
|
|
{ 3, 9 }, // 2 Select-DR-Scan
|
|
{ 4, 5 }, // 3 Capture-DR
|
|
{ 4, 5 }, // 4 Shift-DR
|
|
{ 6, 8 }, // 5 Exit1-DR
|
|
{ 6, 7 }, // 6 Pause-DR
|
|
{ 4, 8 }, // 7 Exit2-DR
|
|
{ 1, 2 }, // 8 Update-DR
|
|
{ 10, 0 }, // 9 Select-IR-Scan
|
|
{ 11, 12 }, // 10 Capture-IR
|
|
{ 11, 12 }, // 11 Shift-IR
|
|
{ 13, 15 }, // 12 Exit1-IR
|
|
{ 13, 14 }, // 13 Pause-IR
|
|
{ 11, 15 }, // 14 Exit2-IR
|
|
{ 1, 2 } // 15 Update-IR
|
|
};
|
|
|
|
/*
|
|
* TAP Registers
|
|
*/
|
|
|
|
static UINT64 current_instruction; // latched IR (not always equal to IR)
|
|
static UINT64 ir; // instruction register (46 bits)
|
|
|
|
static UINT8 id_data[32]; // ASIC ID code data buffer
|
|
static INT id_size; // size of ID data in bits
|
|
static INT ptr; // current bit ptr for data
|
|
|
|
static BOOL tdo; // bit shifted out to TDO
|
|
|
|
/*
|
|
* insert_bit():
|
|
*
|
|
* Inserts a bit into an arbitrarily long bit field. Bit 0 is assumed to be
|
|
* the MSB of the first byte in the buffer.
|
|
*/
|
|
|
|
static void insert_bit(UINT8 *buf, INT bit_num, INT bit)
|
|
{
|
|
INT bit_in_byte;
|
|
|
|
bit_in_byte = 7 - (bit_num & 7);
|
|
|
|
buf[bit_num / 8] &= ~(1 << bit_in_byte);
|
|
buf[bit_num / 8] |= (bit << bit_in_byte);
|
|
}
|
|
|
|
/*
|
|
* insert_id():
|
|
*
|
|
* Inserts a 32-bit ID code into the ID bit field.
|
|
*/
|
|
|
|
static void insert_id(UINT32 id, INT start_bit)
|
|
{
|
|
INT i;
|
|
|
|
for (i = 31; i >= 0; i--)
|
|
insert_bit(id_data, start_bit++, (id >> i) & 1);
|
|
}
|
|
|
|
/*
|
|
* shift():
|
|
*
|
|
* Shifts the data buffer right (towards LSB at byte 0) by 1 bit. The size of
|
|
* the number of bits must be specified. The bit shifted out of the LSB is
|
|
* returned.
|
|
*/
|
|
|
|
static BOOL shift(UINT8 *data, INT num_bits)
|
|
{
|
|
INT i;
|
|
BOOL shift_out, shift_in;
|
|
|
|
/*
|
|
* This loop takes care of all the fully-filled bytes
|
|
*/
|
|
|
|
shift_in = 0;
|
|
for (i = 0; i < num_bits / 8; i++)
|
|
{
|
|
shift_out = data[i] & 1;
|
|
data[i] >>= 1;
|
|
data[i] |= (shift_in << 7);
|
|
shift_in = shift_out; // carry over to next element's MSB
|
|
}
|
|
|
|
/*
|
|
* Take care of the last partial byte (if there is one)
|
|
*/
|
|
|
|
if ((num_bits & 7) != 0)
|
|
{
|
|
shift_out = (data[i] >> (8 - (num_bits & 7))) & 1;
|
|
data[i] >>= 1;
|
|
data[i] |= (shift_in << 7);
|
|
}
|
|
|
|
return shift_out;
|
|
}
|
|
|
|
/*
|
|
* BOOL tap_read(void);
|
|
*
|
|
* Reads TDO.
|
|
*
|
|
* Returns:
|
|
* TDO.
|
|
*/
|
|
|
|
BOOL tap_read(void)
|
|
{
|
|
return tdo;
|
|
}
|
|
|
|
/*
|
|
* void tap_write(BOOL tck, BOOL tms, BOOL tdi, BOOL trst);
|
|
*
|
|
* Writes to the TAP. State changes only occur on the rising edge of the clock
|
|
* (tck = 1.)
|
|
*
|
|
* Parameters:
|
|
* tck = Clock.
|
|
* tms = Test mode select.
|
|
* tdi = Serial data input. Must be 0 or 1 only!
|
|
* trst = Reset.
|
|
*/
|
|
|
|
void tap_write(BOOL tck, BOOL tms, BOOL tdi, BOOL trst)
|
|
{
|
|
if (!tck)
|
|
return;
|
|
|
|
state = NEXT(tms);
|
|
|
|
switch (state)
|
|
{
|
|
case 3: // Capture-DR
|
|
|
|
/*
|
|
* Read ASIC IDs.
|
|
*
|
|
* The ID Sequence is:
|
|
* - Jupiter
|
|
* - Mercury
|
|
* - Venus
|
|
* - Earth
|
|
* - Mars
|
|
* - Mars (again)
|
|
*
|
|
* Note that different Model 3 steps have different chip
|
|
* revisions, hence the different IDs returned below.
|
|
*
|
|
* On Step 1.5 and 1.0, instruction 0x0C631F8C7FFE is used to retrieve
|
|
* the ID codes but Step 2.0 is a little weirder. It seems to use this
|
|
* and either the state of the TAP after reset or other instructions
|
|
* to read the IDs as well. This can be emulated in one of 2 ways:
|
|
* Ignore the instruction and always load up the data or load the
|
|
* data on TAP reset and when the instruction is issued.
|
|
*/
|
|
|
|
if (m3_config.step == 0x10)
|
|
{
|
|
insert_id(0x116C7057, 1 + 0 * 32);
|
|
insert_id(0x216C3057, 1 + 1 * 32);
|
|
insert_id(0x116C4057, 1 + 2 * 32);
|
|
insert_id(0x216C5057, 1 + 3 * 32);
|
|
insert_id(0x116C6057, 1 + 4 * 32 + 1);
|
|
insert_id(0x116C6057, 1 + 5 * 32 + 1);
|
|
}
|
|
else if (m3_config.step == 0x15)
|
|
{
|
|
insert_id(0x316C7057, 1 + 0 * 32);
|
|
insert_id(0x316C3057, 1 + 1 * 32);
|
|
insert_id(0x216C4057, 1 + 2 * 32); // Lost World may to use 0x016C4057
|
|
insert_id(0x316C5057, 1 + 3 * 32);
|
|
insert_id(0x216C6057, 1 + 4 * 32 + 1);
|
|
insert_id(0x216C6057, 1 + 5 * 32 + 1);
|
|
}
|
|
else if (m3_config.step >= 0x20)
|
|
{
|
|
insert_id(0x416C7057, 1 + 0 * 32);
|
|
insert_id(0x416C3057, 1 + 1 * 32);
|
|
insert_id(0x316C4057, 1 + 2 * 32);
|
|
insert_id(0x416C5057, 1 + 3 * 32);
|
|
insert_id(0x316C6057, 1 + 4 * 32 + 1);
|
|
insert_id(0x316C6057, 1 + 5 * 32 + 1);
|
|
}
|
|
|
|
break;
|
|
|
|
case 4: // Shift-DR
|
|
|
|
tdo = shift(id_data, id_size);
|
|
break;
|
|
|
|
case 10: // Capture-IR
|
|
|
|
/*
|
|
* Load lower 2 bits with 01 as per IEEE 1149.1-1990
|
|
*/
|
|
|
|
ir = 1;
|
|
break;
|
|
|
|
case 11: // Shift-IR
|
|
|
|
/*
|
|
* Shift IR towards output and load in new data from TDI
|
|
*/
|
|
|
|
tdo = ir & 1; // shift LSB to output
|
|
ir >>= 1;
|
|
ir |= ((UINT64) tdi << 45);
|
|
break;
|
|
|
|
case 15: // Update-IR
|
|
|
|
/*
|
|
* Latch IR (technically, this should occur on the falling edge of
|
|
* TCK)
|
|
*/
|
|
|
|
ir &= 0x3fffffffffff;
|
|
current_instruction = ir;
|
|
|
|
#if 0
|
|
{
|
|
UINT8 *i = (UINT8 *) &ir;
|
|
LOG("tap.log", "current instruction set: %02X%02X%02X%02X%02X%02X\n", i[5], i[4], i[3], i[2], i[1], i[0]);
|
|
}
|
|
#endif
|
|
|
|
break;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
|
|
#if 0
|
|
if (state == 4)
|
|
LOG("tap.log", "state: Shift-DR %d\n", tdi);
|
|
else if (state == 11)
|
|
LOG("tap.log", "state: Shift-IR %d\n", tdi);
|
|
else
|
|
LOG("tap.log", "state: %s\n", state_name[state]);
|
|
#endif
|
|
}
|
|
|
|
|
|
/*
|
|
* void tap_reset(void);
|
|
*
|
|
* Resets the TAP (simulating a power up or SCAN_RST signal.)
|
|
*/
|
|
|
|
void tap_reset(void)
|
|
{
|
|
id_size = 197; // 197 bits
|
|
|
|
state = 0; // test-logic/reset
|
|
}
|