summaryrefslogtreecommitdiffstatshomepage
path: root/src/mame/video/3dom2_te.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/mame/video/3dom2_te.cpp')
-rw-r--r--src/mame/video/3dom2_te.cpp3960
1 files changed, 3960 insertions, 0 deletions
diff --git a/src/mame/video/3dom2_te.cpp b/src/mame/video/3dom2_te.cpp
new file mode 100644
index 00000000000..6dde9f57093
--- /dev/null
+++ b/src/mame/video/3dom2_te.cpp
@@ -0,0 +1,3960 @@
+// license:BSD-3-Clause
+// copyright-holders:Philip Bennett
+/***************************************************************************
+
+ 3DO M2 BDA Triangle Engine
+
+***************************************************************************/
+
+#include "math.h"
+#include "machine/3dom2.h"
+#include "video/3dom2_te.h"
+
+/*
+ TODO:
+
+ VTX_FLAGS do not get passed to the TMAPPER: Should use master control
+ bits to disable shading and texturing.
+
+ - Evil Night attract mode sky is missing. Why?
+ - What is 1/w when 0?
+ - What do we do about RL flat-topped triangles?
+ - Heat of 11 color check has dodgy pixels <- HAS IT?
+ - Polystar blending is incorrect (intro)
+*/
+
+#define TEST_TIMING 1
+
+#if TEST_TIMING
+enum
+{
+ STAT_TRIANGLES_PROCESSED,
+ STAT_TEXEL_LOADS,
+ STAT_TEXEL_READS,
+ STAT_PIXELS_PROCESSED,
+ STAT_PIXEL_LOADS,
+ STAT_PIXEL_STORES,
+ STAT_TEXEL_BYTES,
+ STAT_ZBUFFER_LOADS,
+ STAT_ZBUFFER_STORES,
+};
+static uint32_t g_statistics[16];
+#endif
+
+static bool g_debug = false; // TODO
+
+//**************************************************************************
+// MACROS / CONSTANTS
+//**************************************************************************
+
+// Device type definition
+DEFINE_DEVICE_TYPE(M2_TE, m2_te_device, "m2te", "BDA Triangle Engine")
+
+
+
+//static const uint32_t fixed_bits = 23;
+
+static const uint32_t xy_bits = 11;
+static const uint32_t color_bits = 8;
+static const uint32_t text_bits[] = { 10, 7, 4, 1 };
+static const int32_t depth_bits[] = { 0, -3, -6, -9 };
+
+// For right-aligning
+static const uint32_t xy_rshift = 12;
+static const uint32_t color_rshift = 4;
+
+
+// 11.0 format
+static const uint32_t xy_one = 1;
+
+
+/***************************************************************************
+ REGISTER DEFINITIONS
+ ***************************************************************************/
+
+// TODO: SUFFIX bits with _BIT?
+// Move to another file?
+
+//-------------------------------------------------
+// General Control
+//-------------------------------------------------
+
+// Triangle Engine Master Mode
+#define TEMASTER_MODE_RESET 0x00000001
+#define TEMASTER_MODE_DTEXT 0x00000002
+#define TEMASTER_MODE_DSHADE 0x00000004
+#define TEMASTER_MODE_DBLEND 0x00000008
+#define TEMASTER_MODE_DZBUF 0x00000010
+#define TEMASTER_MODE_DDITH 0x00000020
+
+// Triangle Engine Immediate Control
+#define TEICNTL_INT 0x00000001
+#define TEICNTL_STEP 0x00000002
+#define TEICNTL_STPL 0x00000004
+#define TEICNTL_STPI 0x00000008
+#define TEICNTL_RSTRT 0x00000010
+#define TEICNTL_STRT 0x00000020
+
+#define TEDCNTL_TLD 0x00000001
+#define TEDCNTL_JA 0x00000002
+#define TEDCNTL_JR 0x00000004
+#define TEDCNTL_INT 0x00000008
+#define TEDCNTL_PSE 0x00000010
+#define TEDCNTL_SYNC 0x00000020
+
+#define INTSTAT_DEFERRED_INSTR 0x00000100
+#define INTSTAT_IMMEDIATE_INSTR 0x00000200
+#define INTSTAT_LIST_END 0x00000400
+#define INTSTAT_WINDOW_CLIP 0x00000800
+#define INTSTAT_SPECIAL_INSTR 0x00001000
+#define INTSTAT_UNIMPLEMENTED_INSTR 0x00002000
+#define INTSTAT_SUPERVISOR 0x00004000
+#define INTSTAT_ANY_RENDER 0x00008000
+#define INTSTAT_Z_FUNC 0x00010000
+#define INTSTAT_ALU_STATUS 0x00020000
+#define INTSTAT_FB_CLIP 0x00040000
+#define INTSTAT_IMMEDIATE 0x00080000
+
+// IWP
+// IRP
+// Interrupt Enable
+// Interrupt Status
+// Vertex Control
+
+
+
+//-------------------------------------------------
+// Setup Engine
+//-------------------------------------------------
+
+// Vertex State
+#define VERTEXSTATE_TSORT_MASK 0x00000007
+#define VERTEXSTATE_TSORT_OMN 0x00000001
+#define VERTEXSTATE_TSORT_MNO 0x00000002
+#define VERTEXSTATE_TSORT_ONM 0x00000003
+#define VERTEXSTATE_TSORT_NOM 0x00000004
+#define VERTEXSTATE_TSORT_MON 0x00000005
+#define VERTEXSTATE_TSORT_NMO 0x00000006
+
+#define VERTEXSTATE_VCNT_SHIFT 3
+#define VERTEXSTATE_VCNT_MASK 0x00000018
+
+
+//-------------------------------------------------
+// Edge Walker
+//-------------------------------------------------
+
+// Edge and Span Walker Control
+#define ESCNTL_DSPOFF 0x00000001
+#define ESCNTL_DUSCAN 0x00000002
+#define ESCNTL_PERSPECTIVEOFF 0x00000004
+
+
+//-------------------------------------------------
+// Texture Mapper
+//-------------------------------------------------
+
+// Texture Mapper Master Control (0x00046400)
+#define TXTCNTL_MMDMA_TRAM_ON 0x00000004
+#define TXTCNTL_MMDMA_PIP_ON 0x00000008
+#define TXTCNTL_SNOOP_ON 0x00000020
+
+// Texture Load Control (0x00046404)
+#define TXTLDCNTL_SRCBITOFFS 0x00000007
+#define TXTLDCNTL_LDMODE_MASK 0x00000300
+#define TXTLDCNTL_LDMODE_TEXLOAD 0x00000000
+#define TXTLDCNTL_LDMODE_MMDMA 0x00000100
+#define TXTLDCNTL_LDMODE_PIPLOAD 0x00000200
+#define TXTLDCNTL_LDMODE_RESERVED 0x00000300
+#define TXTLDCNTL_COMPRESSED 0x00000400
+
+// Address Control (0x00046408)
+#define TXTADDRCNTL_LODMAX_MASK 0x0000000f
+#define TXTADDRCNTL_FILTERSEL_MASK 0x00000003
+#define TXTADDRCNTL_FILTERSEL_POINT 0x00000000
+#define TXTADDRCNTL_FILTERSEL_LINEAR 0x00000001
+#define TXTADDRCNTL_FILTERSEL_BILINEAR 0x00000002
+#define TXTADDRCNTL_FILTERSEL_QUASITRI 0x00000003
+#define TXTADDRCNTL_R12FILTERSEL_SHIFT 4
+#define TXTADDRCNTL_R3FILTERSEL_SHIFT 7
+#define TXTADDRCNTL_R45FILTERSEL_SHIFT 10
+#define TXTADDRCNTL_LOOKUP_EN 0x00002000
+
+// PIP Control (0x0004640C)
+#define TXTPIPCNTL_INDEX_OFFSET 0x000000ff
+#define TXTPIPCNTL_COLORSEL_MASK 0x00000700
+#define TXTPIPCNTL_COLORSEL_SHIFT 8
+#define TXTPIPCNTL_ALPHASEL_MASK 0x00003800
+#define TXTPIPCNTL_ALPHASEL_SHIFT 11
+#define TXTPIPCNTL_SSBSEL_MASK 0x0001c000
+#define TXTPIPCNTL_SSBSEL_SHIFT 14
+
+#define TXTPIPCNTL_SEL_CONSTANT 0
+#define TXTPIPCNTL_SEL_TRAM 1
+#define TXTPIPCNTL_SEL_PIP 2
+
+// Texture Application Control (0x00046410)
+#define TXTTABCNTL_C_ASEL_MASK 0x00000007
+#define TXTTABCNTL_C_ASEL_SHIFT 0
+#define TXTTABCNTL_C_BSEL_MASK 0x00000038
+#define TXTTABCNTL_C_BSEL_SHIFT 3
+#define TXTTABCNTL_C_TSEL_MASK 0x000001c0
+#define TXTTABCNTL_C_TSEL_SHIFT 6
+
+#define TXTTABCNTL_C_ABTSEL_AITER 0
+#define TXTTABCNTL_C_ABTSEL_CITER 1
+#define TXTTABCNTL_C_ABTSEL_AT 2
+#define TXTTABCNTL_C_ABTSEL_CT 3
+#define TXTTABCNTL_C_ABTSEL_ACONST 4
+#define TXTTABCNTL_C_ABTSEL_CCONST 5
+
+#define TXTTABCNTL_C_OSEL_MASK 0x00000600
+#define TXTTABCNTL_C_OSEL_SHIFT 9
+
+#define TXTTABCNTL_CO_SEL_CITER 0
+#define TXTTABCNTL_CO_SEL_CT 1
+#define TXTTABCNTL_CO_SEL_BLEND 2
+#define TXTTABCNTL_CO_SEL_RESERVED 3
+
+#define TXTTABCNTL_A_ASEL_MASK 0x00001800
+#define TXTTABCNTL_A_ASEL_SHIFT 11
+#define TXTTABCNTL_A_BSEL_MASK 0x00006000
+#define TXTTABCNTL_A_BSEL_SHIFT 13
+
+#define TXTTABCNTL_A_ABSEL_AITER 0
+#define TXTTABCNTL_A_ABSEL_AT 1
+#define TXTTABCNTL_A_ABSEL_ACONST 2
+
+#define TXTTABCNTL_A_OSEL_MASK 0x00018000
+#define TXTTABCNTL_A_OSEL_SHIFT 15
+
+#define TXTTABCNTL_AO_SEL_AITER 0
+#define TXTTABCNTL_AO_SEL_AT 1
+#define TXTTABCNTL_AO_SEL_BLEND 2
+#define TXTTABCNTL_AO_SEL_RESERVED 3
+
+#define TXTTABCNTL_BLENDOP_MASK 0x00010000
+#define TXTTABCNTL_BLENDOP_SHIFT 16
+#define TXTTABCNTL_BLENDOP_LERP 0
+#define TXTTABCNTL_BLENDOP_MULT 1
+
+// TAB Constants
+#define TXTTABCONST_BLUE 0x000000ff
+#define TXTTABCONST_BLUE_SHIFT 0
+#define TXTTABCONST_GREEN 0x0000ff00
+#define TXTTABCONST_GREEN_SHIFT 8
+#define TXTTABCONST_RED 0x00ff0000
+#define TXTTABCONST_RED_SHIFT 16
+#define TXTTABCONST_ALPHA 0x7f000000
+#define TXTTABCONST_ALPHA_SHIFT 24
+#define TXTTABCONST_SSB 0x80000000
+
+// Texture Loader Destination Base (0x00046414)
+#define TXTLDDSTBASE_ADDR 0x00003ffc
+
+// Texture Lod Base 0 (0x00046414)
+// Texture Lod Base 1 (0x00046418)
+// Texture Lod Base 2 (0x0004641C)
+// Texture Lod Base 3 (0x00046420)
+#define TXTLODBASE_MASK 0x00003ffc
+
+// Texture Loader Source Base (0x00046424)
+#define TXTLDSRCBASE_ADDR 0x00003fff
+
+// Texture Loader Counts (0x00046428)
+#define TXTLDBYTECNT_COUNT 0x0fffffff
+#define TXTLDROWCNT_COUNT 0x0fffffff
+#define TXTLDTEXCNT_COUNT 0x0fffffff
+
+// Texture Loader Width (0x00046428)
+#define TxTLDWIDTH_SRCROW 0x0000ffff
+#define TxTLDWIDTH_DSTROW_SHIFT 16
+#define TxTLDWIDTH_DSTROW 0xffff0000
+
+// Texture Size (0x0004642C)
+#define TXTUVMAX_VMAX_MASK 0x000003ff
+#define TXTUVMAX_VMAX_SHIFT 0
+#define TXTUVMAX_UMAX_MASK 0x03ff0000
+#define TXTUVMAX_UMAX_SHIFT 16
+
+// Texture Mask (0x00046430)
+#define TXTUVMASK_VMASK_MASK 0x000003ff
+#define TXTUVMASK_VMASK_SHIFT 0
+#define TXTUVMASK_UMASK_MASK 0x03ff0000
+#define TXTUVMASK_UMASK_SHIFT 16
+
+// TRAM Format (0x0004643C)
+// TODO: Expansion formats
+#define TXTEXPFORM_CDEPTH_MASK 0x0000000f
+#define TXTEXPFORM_CDEPTH_SHIFT 0
+#define TXTEXPFORM_IDEPTH_MASK 0x0000000f
+#define TXTEXPFORM_IDEPTH_SHIFT 0
+#define TXTEXPFORM_ADEPTH_MASK 0x000000f0
+#define TXTEXPFORM_ADEPTH_SHIFT 4
+#define TXTEXPFORM_TRANSPARENT 0x00000100
+#define TXTEXPFORM_SSBON 0x00000200
+#define TXTEXPFORM_COLORON 0x00000400
+#define TXTEXPFORM_INDEXON 0x00000400
+#define TXTEXPFORM_ALPHAON 0x00000800
+#define TXTEXPFORM_LITERAL 0x00001000
+
+
+// Format Registers
+
+
+//-------------------------------------------------
+// Destination Blender
+//-------------------------------------------------
+
+// Snoop (0x0048000)
+#define DBSNOOP_DESTWRSNOOP 0x00000001
+#define DBSNOOP_SRCRDSNOOP 0x00000002
+#define DBSNOOP_ZWRSNOOP 0x00000004
+#define DBSNOOP_ZRDSNOOP 0x00000008
+
+// Supervisor General Control (0x0048004)
+#define DBSUPERGENCTL_DESTOUTEN 0x00000001
+#define DBSUPERGENCTL_DESTWR16BEN 0x00000002
+#define DBSUPERGENCTL_ZWR16BEN 0x00000004
+
+// User General Control (0x0048008)
+#define DBUSERGENCTL_DESTOUT_MASK 0x0000000f
+#define DBUSERGENCTL_DITHEREN 0x00000010
+#define DBUSERGENCTL_SRCINEN 0x00000020
+#define DBUSERGENCTL_BLENDEN 0x00000040
+#define DBUSERGENCTL_WCLIPOUTEN 0x00000080
+#define DBUSERGENCTL_WCLIPINEN 0x00000100
+#define DBUSERGENCTL_ZOUTEN 0x00000200
+#define DBUSERGENCTL_ZBUFEN 0x00000400
+
+// Discard Control (0x004800C)
+#define DBDISCARDCTL_ADISEN 0x00000001
+#define DBDISCARDCTL_RGBDISEN 0x00000002
+#define DBDISCARDCTL_SSBDISEN 0x00000004
+#define DBDISCARDCTL_ZCLIPDISEN 0x00000008
+
+// Status (0x0048010)
+#define DBSTATUS_ANYREND 0x00000001
+#define DBSTATUS_ZFUNC_GT 0x00000002
+#define DBSTATUS_ZFUNC_EQ 0x00000004
+#define DBSTATUS_ZFUNC_LT 0x00000008
+#define DBSTATUS_ALUSTAT_BLUE_GT 0x00000010
+#define DBSTATUS_ALUSTAT_BLUE_EQ 0x00000020
+#define DBSTATUS_ALUSTAT_BLUE_LT 0x00000040
+#define DBSTATUS_ALUSTAT_GREEN_GT 0x00000080
+#define DBSTATUS_ALUSTAT_GREEN_EQ 0x00000100
+#define DBSTATUS_ALUSTAT_GREEN_LT 0x00000200
+#define DBSTATUS_ALUSTAT_RED_GT 0x00000400
+#define DBSTATUS_ALUSTAT_RED_EQ 0x00000800
+#define DBSTATUS_ALUSTAT_RED_LT 0x00001000
+#define DBSTATUS_ZCLIP 0x00002000
+#define DBSTATUS_WINCLIP 0x00004000
+#define DBSTATUS_FBCLIP 0x00008000
+
+// Interrupt Control (0x00048014)
+#define DBINTCNTL_ZFUNCSTATINTEN_MASK 0x00000003
+#define DBINTCNTL_ZFUNCSTATINTEN_MASK 0x00000003
+
+// Framebuffer XY Clip Control (0x00048018)
+#define DBFBCLIP_YFBCLIP_MASK 0x000007ff
+#define DBFBCLIP_YFBCLIP_SHIFT 0
+#define DBFBCLIP_XFBCLIP_MASK 0x07ff0000
+#define DBFBCLIP_XFBCLIP_SHIFT 16
+
+// Window X Clip Control (0x0004801C)
+#define DBFBXWINCLIP_XMAX_MASK 0x000007ff
+#define DBFBXWINCLIP_XMAX_SHIFT 0
+#define DBFBXWINCLIP_XMIN_MASK 0x07ff0000
+#define DBFBXWINCLIP_XMIN_SHIFT 16
+
+// Window Y Clip Control (0x00048020)
+#define DBFBYWINCLIP_YMAX_MASK 0x000007ff
+#define DBFBYWINCLIP_YMAX_SHIFT 0
+#define DBFBYWINCLIP_YMIN_MASK 0x07ff0000
+#define DBFBYWINCLIP_YMIN_SHIFT 16
+
+// Destination Write Control (0x0048024)
+#define DBDESTCNTL_32BPP 0x00000001
+
+// Destination Write Base Address (0x0048028)
+
+// Destination X Stride (0x004802C)
+#define DBDEST_XSTRIDE 0x000007ff
+
+// Source Read Control (0x00048030)
+#define DBSRCCNTL_32BPP 0x00000001
+#define DBSRCCNTL_MSBREP 0x00000002
+
+// Source Read Base Address (0x00048034)
+
+// Source X Stride (0x00048038)
+#define DBSRCXSTRIDE 0x000007ff
+
+// Source XY Offset (0x0004803C)
+#define DBSRCOFFS_YOFFS_MASK 0x00000fff
+#define DBSRCOFFS_YOFFS_SHIFT 0
+#define DBSRCOFFS_XOFFS_MASK 0x0fff0000
+#define DBSRCOFFS_XOFFS_SHIFT 16
+
+// Z Buffer Control (0x00048040)
+#define DBZCNTL_ZFUNCCNTL_MASK 0x0000003f
+#define DBZCNTL_ZPIXOUT_LT 0x00000001
+#define DBZCNTL_ZBUFOUT_LT 0x00000002
+#define DBZCNTL_ZPIXOUT_EQ 0x00000004
+#define DBZCNTL_ZBUFOUT_EQ 0x00000008
+#define DBZCNTL_ZPIXOUT_GT 0x00000010
+#define DBZCNTL_ZBUFOUT_GT 0x00000020
+
+// Z Buffer Base Address (0x00048044)
+#define DBZBASEADDR_MASK 0x00ffffff
+
+// Z Buffer XY Offset (0x00048048)
+#define DBZOFFS_YOFFS_MASK 0x00000fff
+#define DBZOFFS_YOFFS_SHIFT 0
+#define DBZOFFS_XOFFS_MASK 0x0fff0000
+#define DBZOFFS_XOFFS_SHIFT 16
+
+// Z Buffer Clip (0x0004804C)
+#define DBZCLIP_YCLIP_MASK 0x000007ff
+#define DBZCLIP_YCLIP_SHIFT 0
+#define DBZCLIP_XCLIP_MASK 0x07ff0000
+#define DBZCLIP_XCLIP_SHIFT 16
+
+// SSB/DSB Control (0x00048050)
+#define DBSSBDSBCNTL_DSBSELECT_MASK 0x00000007
+#define DBSSBDSBCNTL_DSBSELECT_SHIFT 0
+#define DBSSBDSBCNTL_DSBSELECT_SSB 0
+#define DBSSBDSBCNTL_DSBSELECT_CONSTANT 1
+#define DBSSBDSBCNTL_DSBSELECT_SRC 2
+
+#define DBSSBDSBCNTL_DSBCONST 0x00000004
+
+// RGB constants (0x00048054)
+#define DBCONSTIN_B_MASK 0x000000ff
+#define DBCONSTIN_B_SHIFT 0
+#define DBCONSTIN_G_MASK 0x0000ff00
+#define DBCONSTIN_G_SHIFT 8
+#define DBCONSTIN_R_MASK 0x00ff0000
+#define DBCONSTIN_R_SHIFT 16
+
+// Texture Multiplication Control (0x00048058)
+#define DBTXTMULTCNTL_TXTRJUST 0x00000001
+#define DBTXTMULTCNTL_TXTCOEFCMP 0x00000002
+
+#define DBTXTMULTCNTL_TXTCONSTCNTL_MASK 0x0000000c
+#define DBTXTMULTCNTL_TXTCONSTCNTL_SHIFT 2
+#define DBTXTMULTCNTL_TXTCONSTCNTL_TEXSSB 0
+#define DBTXTMULTCNTL_TXTCONSTCNTL_SRCDSB 1
+
+#define DBTXTMULTCNTL_COEFSEL_MASK 0x00000030
+#define DBTXTMULTCNTL_COEFSEL_SHIFT 4
+#define DBTXTMULTCNTL_COEFSEL_ATI 0
+#define DBTXTMULTCNTL_COEFSEL_ASRC 1
+#define DBTXTMULTCNTL_COEFSEL_CONSTANT 2
+#define DBTXTMULTCNTL_COEFSEL_CSRC 3
+
+#define DBTXTMULTCNTL_INSEL_MASK 0x000000c0
+#define DBTXTMULTCNTL_INSEL_SHIFT 6
+#define DBTXTMULTCNTL_INSEL_CTI 0
+#define DBTXTMULTCNTL_INSEL_CONSTANT 1
+#define DBTXTMULTCNTL_INSEL_COMPSRC 2
+#define DBTXTMULTCNTL_INSEL_ATI 3
+
+// Source Multiplication Control (0x00048058)
+#define DBSRCMULTCNTL_SRCRJUST 0x00000001
+#define DBSRCMULTCNTL_SRCCOEFCMP 0x00000002
+
+#define DBSRCMULTCNTL_SRCCONSTCNTL_MASK 0x0000000c
+#define DBSRCMULTCNTL_SRCCONSTCNTL_SHIFT 2
+#define DBSRCMULTCNTL_SRCCONSTCNTL_TEXSSB 0
+#define DBSRCMULTCNTL_SRCCONSTCNTL_SRCDSB 1
+
+#define DBSRCMULTCNTL_COEFSEL_MASK 0x00000030
+#define DBSRCMULTCNTL_COEFSEL_SHIFT 4
+#define DBSRCMULTCNTL_COEFSEL_ATI 0
+#define DBSRCMULTCNTL_COEFSEL_ASRC 1
+#define DBSRCMULTCNTL_COEFSEL_CONSTANT 2
+#define DBSRCMULTCNTL_COEFSEL_CTI 3
+
+#define DBSRCMULTCNTL_INSEL_MASK 0x000000c0
+#define DBSRCMULTCNTL_INSEL_SHIFT 6
+#define DBSRCMULTCNTL_INSEL_SRC 0
+#define DBSRCMULTCNTL_INSEL_CONSTANT 1
+#define DBSRCMULTCNTL_INSEL_COMPCTI 2
+#define DBSRCMULTCNTL_INSEL_TEXALPHA 3
+
+// ALU Control (0x00048070) TODO
+#define DBALUCNTL_FINALDIVIDE_MASK 0x00000007
+#define DBALUCNTL_FINALDIVIDE_SHIFT 0
+
+#define DBALUCNTL_ALUOP_MASK 0x000000f8
+#define DBALUCNTL_ALUOP_SHIFT 5
+
+// Source Alpha Control (0x00048074)
+#define DBDSTACNTL_ADESTSEL_MASK 0x00000003
+#define DBDSTACNTL_ADESTSEL_SHIFT 0
+#define DBDSTACNTL_ADESTCONSTCNTL_MASK 0x0000000c
+#define DBDSTACNTL_ADESTCONSTCNTL_SHIFT 2
+
+#define DBDSTALPHACONST_CONST1_MASK 0x000000ff
+#define DBDSTALPHACONST_CONST1_SHIFT 0
+#define DBDSTALPHACONST_CONST0_MASK 0x00ff0000
+#define DBDSTALPHACONST_CONST0_SHIFT 16
+
+#define DBSSBDSBCNTL_DSBSEL_MASK 0x00000003
+#define DBSSBDSBCNTL_DSBSEL_SHIFT 0
+#define DBSSBDSBCNTL_DSBCONST_MASK 0x00000004
+#define DBSSBDSBCNTL_DSBCONST_SHIFT 2
+
+
+
+//**************************************************************************
+// SUPPORT FUNCTIONS
+//**************************************************************************
+
+static inline float int_trunc(float x)
+{
+ return (float)(int)x;
+}
+
+static inline uint8_t extract_exp(float x)
+{
+ uint32_t u32 = *reinterpret_cast<uint32_t *>(&x);
+ return (u32 >> 23) & 0xff;
+}
+
+// Convert a regular float to sign-magnitude fixed point
+static int32_t ieee754_to_tefix(float f, int32_t bits)
+{
+ float format = powf(2.0f, bits);
+ float tmp1 = format * 2.0f;
+
+ // Check to see if number is within range
+ float tmp2 = fmodf(fabsf(f), tmp1);
+ float tmp3 = (tmp2 >= format) ? tmp2 : tmp2 + format;
+
+ // Extract the result
+ uint32_t res = *reinterpret_cast<uint32_t *>(&tmp3) & 0x007fffff;
+
+ // What is this?
+ if (tmp2 >= format || tmp3 == tmp1)
+ res |= 0x00800000;
+
+ // Negate
+ if (f < 0.0f)
+ res = -res;
+
+ return res;
+}
+
+static void write_te_reg(uint32_t &reg, uint32_t data, m2_te_device::te_reg_wmode mode)
+{
+ switch (mode)
+ {
+ case REG_WRITE:
+ {
+ reg = data;
+ break;
+ }
+ case REG_SET:
+ {
+ reg |= data;
+ break;
+ }
+ case REG_CLEAR:
+ {
+ reg &= ~data;
+ break;
+ }
+ default:
+ assert_always(false, "Bad register write mode");
+ }
+}
+
+#if 0
+static const char *get_reg_name(uint32_t unit, uint32_t reg)
+{
+ static const char *gc_regs[] =
+ {
+ "TEMasterMode",
+ "Reserved",
+ "TEICntlData",
+ "TEICntl",
+ "TEDCntlData",
+ "TEDCntl",
+ "IWP",
+ "IRP",
+ "IntEn",
+ "IntStat",
+ "Vertex Control",
+ };
+
+ static const char *db_regs[] =
+ {
+ "Snoop",
+ "SuperGenControl",
+ "usergen_ctrl",
+ "Discard Control",
+ "Status",
+ "Int Cntl",
+ "FBClip",
+ "XWinClip",
+ "YWinClip",
+ "DestCntl",
+ "DestBaseAddr",
+ "DestXStride",
+ "SrcCntl",
+ "SrcBaseAddr",
+ "SrcXStride",
+ "SrcOffset",
+ "ZCntl",
+ "ZBaseAddr",
+ "ZOffset",
+ "ZClip",
+ "SSBDSBCntl",
+ "ConstIn",
+ "DBTXTMULTCNTL",
+ "TxtCoefConst0",
+ "TxtCoefConst1",
+ "src_mult_cntl",
+ "SrcCoefConst0",
+ "SrcCoefConst1",
+ "src_mult_cntl",
+ "SrcCoefconst0",
+ "SrcCoefconst1",
+ "ALUCntl",
+ "SrcAlphaCntl",
+ "DestAlphaCntl",
+ "DestAlphaConst",
+ "DitherMatA",
+ "DitherMatB",
+ };
+
+ static const char *es_regs[] =
+ {
+ "ESCntl",
+ "ESCapAddr",
+ "ESCapData",
+ };
+
+ static char buffer[128];
+
+ switch (unit)
+ {
+ case 0:
+ {
+ if (reg < sizeof(gc_regs))
+ {
+ sprintf(buffer, "GC:%s", gc_regs[reg]);
+ return buffer;
+ }
+ break;
+ }
+ case 1:
+ {
+ sprintf(buffer, "SE:????");
+ return buffer;
+ }
+ case 2:
+ {
+ if (reg < sizeof(es_regs))
+ {
+ sprintf(buffer, "ES:%s", es_regs[reg]);
+ return buffer;
+ }
+ break;
+ }
+ case 3:
+ {
+// if (reg < sizeof(tm_regs))
+ {
+ sprintf(buffer, "TM:????");
+ return buffer;
+ }
+ break;
+ }
+ case 4:
+ {
+ if (reg < sizeof(db_regs))
+ {
+ sprintf(buffer, "DB:%s", db_regs[reg]);
+ return buffer;
+ }
+ break;
+ }
+ }
+
+ return "????";
+}
+#endif
+//**************************************************************************
+// TRIANGLE ENGINE DEVICE
+//**************************************************************************
+
+//-------------------------------------------------
+// m2_te_device - constructor
+//-------------------------------------------------
+
+m2_te_device::m2_te_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock) :
+ device_t(mconfig, M2_TE, tag, owner, clock),
+ m_general_int_handler(*this),
+ m_dfinstr_int_handler(*this),
+ m_iminstr_int_handler(*this),
+ m_listend_int_handler(*this),
+ m_winclip_int_handler(*this)
+{
+}
+
+
+//-------------------------------------------------
+// device_start - device-specific startup
+//-------------------------------------------------
+
+void m2_te_device::device_start()
+{
+ // Find our parent
+ m_bda = downcast<m2_bda_device *>(owner());
+
+ // Resolve callbacks
+ m_general_int_handler.resolve_safe();
+ m_dfinstr_int_handler.resolve_safe();
+ m_iminstr_int_handler.resolve_safe();
+ m_listend_int_handler.resolve_safe();
+ m_winclip_int_handler.resolve_safe();
+
+ // Allocate texture RAM
+ m_tram = auto_alloc_array(machine(), uint32_t, TEXTURE_RAM_WORDS);
+
+ // Allocate PIP RAM
+ m_pipram = auto_alloc_array(machine(), uint32_t, PIP_RAM_WORDS);
+
+ // TODO
+ memset(&m_gc, 0, sizeof(m_gc));
+ memset(&m_se, 0, sizeof(m_se));
+ memset(&m_es, 0, sizeof(m_es));
+ memset(&m_tm, 0, sizeof(m_tm));
+ memset(&m_db, 0, sizeof(m_db));
+
+
+ // Register state for saving
+ save_pointer(NAME(m_tram), TEXTURE_RAM_WORDS);
+ save_pointer(NAME(m_pipram), PIP_RAM_WORDS);
+
+ save_item(NAME(m_gc.m_regs));
+ save_item(NAME(m_se.m_regs));
+ save_item(NAME(m_es.m_regs));
+ save_item(NAME(m_tm.m_regs));
+ save_item(NAME(m_db.m_regs));
+}
+
+
+//-------------------------------------------------
+// device_reset - device-specific reset
+//-------------------------------------------------
+
+void m2_te_device::device_reset()
+{
+ m_state = TE_STOPPED;
+
+ // TODO
+}
+
+
+//-------------------------------------------------
+// device_post_load - device-specific post-load
+//-------------------------------------------------
+
+void m2_te_device::device_post_load()
+{
+
+}
+
+
+
+/***************************************************************************
+ PUBLIC FUNCTIONS
+***************************************************************************/
+
+READ32_MEMBER( m2_te_device::read )
+{
+ uint32_t unit = (offset >> 11) & 7;
+ uint32_t reg = offset & 0x1ff;
+
+ logerror("%s: TE R[%x]\n", machine().describe_context(), 0x00040000 + (offset << 2));
+
+ switch (unit)
+ {
+ case 0:
+ {
+ if (reg < sizeof(m_gc) / 4)
+ return m_gc.m_regs[reg];
+
+ break;
+ }
+ case 1:
+ {
+ if (reg < sizeof(m_se) / 4)
+ return m_se.m_regs[reg];
+
+ break;
+ }
+ case 2:
+ {
+ if (reg < sizeof(m_es) / 4)
+ return m_es.m_regs[reg];
+
+ break;
+ }
+ case 3:
+ {
+ if (reg < 0x400/4)
+ {
+ return m_pipram[reg];
+ }
+ else if ((reg - 0x400) < sizeof(m_tm) / 4)
+ {
+ return m_tm.m_regs[reg];
+ }
+
+ break;
+ }
+ case 4:
+ {
+ if (reg < sizeof(m_db) / 4)
+ return m_db.m_regs[reg];
+
+ break;
+ }
+ }
+
+ logerror("%s: Unknown TE register read from %d:[%x]", machine().describe_context(), unit, reg);
+ return 0;
+}
+
+WRITE32_MEMBER( m2_te_device::write )
+{
+ uint32_t unit = (offset >> 11) & 7;
+ uint32_t reg = offset & 0x1ff;
+ te_reg_wmode wmode = static_cast<te_reg_wmode>((offset >> 9) & 3);
+
+// logerror("%s: TE W[%.8x] (%s) %.8x\n", machine().describe_context(), 0x00040000 + (offset << 2), get_reg_name(unit, reg), data);
+
+ switch (unit)
+ {
+ case 0:
+ {
+ switch (reg)
+ {
+ case 0:
+ {
+ master_mode_w(data, wmode);
+ return;
+ }
+ case 0x0c/4:
+ {
+ teicntl_w(data, wmode);
+ return;
+ }
+ case 0x14/4:
+ {
+ tedcntl_w(data, wmode);
+ return;
+ }
+ case 0x24/4:
+ {
+ write_te_reg(m_gc.int_status, data, wmode);
+ update_interrupts();
+ return;
+ }
+ default:
+ {
+ if (reg < sizeof(m_gc) / 4)
+ {
+ write_te_reg(m_gc.m_regs[reg], data, wmode);
+ return;
+ }
+ }
+ }
+ break;
+ }
+ case 1:
+ {
+ if (reg < sizeof(m_se) / 4)
+ {
+ write_te_reg(m_se.m_regs[reg], data, wmode);
+ return;
+ }
+ break;
+ }
+ case 2:
+ {
+ if (reg < sizeof(m_es) / 4)
+ {
+ write_te_reg(m_es.m_regs[reg], data, wmode);
+ return;
+ }
+ break;
+ }
+ case 3:
+ {
+ if (reg < 0x400 / 4)
+ {
+ m_pipram[reg] = data;
+ return;
+ }
+ else if ((reg - 0x400 / 4) < sizeof(m_tm) / 4)
+ {
+ write_te_reg(m_tm.m_regs[reg - 0x400 / 4], data, wmode);
+ return;
+ }
+ break;
+ }
+ case 4:
+ {
+ if (reg < sizeof(m_db) / 4)
+ {
+ write_te_reg(m_db.m_regs[reg], data, wmode);
+ return;
+ }
+ break;
+ }
+ }
+
+ logerror("%s: Unknown TE register write to %d:[%x] with %x\n", machine().describe_context(), unit, reg, data);
+}
+
+
+
+/***************************************************************************
+ PRIVATE FUNCTIONS
+***************************************************************************/
+
+/***************************************************************************
+ INTERRUPTS
+***************************************************************************/
+
+//-------------------------------------------------
+// set_interrupt -
+//-------------------------------------------------
+
+void m2_te_device::set_interrupt(uint32_t mask)
+{
+ m_gc.int_status |= mask;
+ update_interrupts();
+}
+
+
+//-------------------------------------------------
+// update_interrupts -
+//-------------------------------------------------
+
+void m2_te_device::update_interrupts()
+{
+ bool gen_int = ((m_gc.int_status & m_gc.int_enable) & 0x0000ff00) != 0;
+
+ // TODO: ALU status and FB clip are controlled by dest blender
+ m_general_int_handler(gen_int);
+
+ // From DBL (Page 265)
+ // FBClip
+ // WinClip
+ // ZClip
+ // ALUStat
+ // ZFuncStat
+
+ // Page 43
+
+ // PAGE 26
+ m_dfinstr_int_handler((m_gc.int_status >> 8) & 1);
+ m_iminstr_int_handler((m_gc.int_status >> 9) & 1);
+ m_listend_int_handler((m_gc.int_status >> 10) & 1);
+ m_winclip_int_handler((m_gc.int_status >> 11) & 1);
+}
+
+
+
+/***************************************************************************
+ SPECIAL REGISTER WRITES
+***************************************************************************/
+
+//-------------------------------------------------
+// master_mode_w -
+//-------------------------------------------------
+
+void m2_te_device::master_mode_w(uint32_t data, te_reg_wmode wmode)
+{
+ write_te_reg(m_gc.te_master_mode, data, wmode);
+
+ if (m_gc.te_master_mode & TEMASTER_MODE_RESET)
+ device_reset();
+}
+
+
+//-------------------------------------------------
+// teicntl_w -
+//-------------------------------------------------
+
+void m2_te_device::teicntl_w(uint32_t data, te_reg_wmode wmode)
+{
+ uint32_t newreg = 0;
+
+ write_te_reg(newreg, data, wmode);
+ write_te_reg(m_gc.te_master_mode, data, wmode);
+
+ if (newreg & TEICNTL_STRT)
+ {
+ m_gc.irp = m_gc.teicntl_data;
+ m_state = TE_RUNNING;
+ execute();
+ }
+ else if (newreg & TEICNTL_RSTRT)
+ {
+ m_state = TE_RUNNING;
+ execute();
+ }
+}
+
+
+//-------------------------------------------------
+// tedcntl_w -
+//-------------------------------------------------
+
+void m2_te_device::tedcntl_w(uint32_t data, te_reg_wmode wmode)
+{
+ write_te_reg(m_gc.tedcntl, data, wmode);
+
+ if (m_gc.tedcntl & TEDCNTL_SYNC)
+ {
+ // TODO
+ }
+ else if (m_gc.tedcntl & TEDCNTL_PSE)
+ {
+ m_state = TE_PAUSED;
+ }
+ else if (m_gc.tedcntl & TEDCNTL_INT)
+ {
+ set_interrupt(INTSTAT_IMMEDIATE);
+ }
+ else if (m_gc.tedcntl & TEDCNTL_JR)
+ {
+ m_gc.irp += m_gc.tedcntl_data;
+ }
+ else if (m_gc.tedcntl & TEDCNTL_JA)
+ {
+ m_gc.irp = m_gc.tedcntl_data;
+ }
+ else if (m_gc.tedcntl & TEDCNTL_TLD)
+ {
+ load_texture();
+ }
+}
+
+
+
+/***************************************************************************
+ VERTEX PROCESSING
+***************************************************************************/
+
+//-------------------------------------------------
+// add_vertex -
+//-------------------------------------------------
+
+void m2_te_device::add_vertex(const se_vtx &vtx, uint32_t flags)
+{
+ uint32_t tsort;
+ uint32_t vcnt;
+
+ if (flags & VTX_FLAG_NEW)
+ {
+ // Reset state for a new triangle
+ tsort = 0;
+ vcnt = 0;
+ }
+ else
+ {
+ // Continue current triangle
+ tsort = m_se.vertex_state & VERTEXSTATE_TSORT_MASK;
+ vcnt = (m_se.vertex_state & VERTEXSTATE_VCNT_MASK) >> VERTEXSTATE_VCNT_SHIFT;
+ }
+
+ if (vcnt < 3)
+ {
+ // Add vertex to incomplete triangle
+ m_se.vertices[vcnt++] = vtx;
+
+ // Are we now complete?
+ if (vcnt == 3)
+ tsort = VERTEXSTATE_TSORT_OMN;
+ }
+ else
+ {
+ // Replace an existing vertex
+ if (flags & VTX_FLAG_RM)
+ {
+ // Triangle fan mode
+ switch (tsort)
+ {
+ case VERTEXSTATE_TSORT_OMN: m_se.vertices[1] = vtx; tsort = VERTEXSTATE_TSORT_ONM; break;
+ case VERTEXSTATE_TSORT_MNO: m_se.vertices[0] = vtx; tsort = VERTEXSTATE_TSORT_NMO; break;
+ case VERTEXSTATE_TSORT_ONM: m_se.vertices[2] = vtx; tsort = VERTEXSTATE_TSORT_OMN; break;
+ case VERTEXSTATE_TSORT_NOM: m_se.vertices[2] = vtx; tsort = VERTEXSTATE_TSORT_MON; break;
+ case VERTEXSTATE_TSORT_MON: m_se.vertices[0] = vtx; tsort = VERTEXSTATE_TSORT_NOM; break;
+ case VERTEXSTATE_TSORT_NMO: m_se.vertices[1] = vtx; tsort = VERTEXSTATE_TSORT_MNO; break;
+ }
+ }
+ else
+ {
+ // Triangle strip mode
+ switch (tsort)
+ {
+ case VERTEXSTATE_TSORT_OMN: m_se.vertices[0] = vtx; tsort = VERTEXSTATE_TSORT_NOM; break;
+ case VERTEXSTATE_TSORT_MNO: m_se.vertices[2] = vtx; tsort = VERTEXSTATE_TSORT_OMN; break;
+ case VERTEXSTATE_TSORT_ONM: m_se.vertices[0] = vtx; tsort = VERTEXSTATE_TSORT_NMO; break;
+ case VERTEXSTATE_TSORT_NOM: m_se.vertices[1] = vtx; tsort = VERTEXSTATE_TSORT_MNO; break;
+ case VERTEXSTATE_TSORT_MON: m_se.vertices[1] = vtx; tsort = VERTEXSTATE_TSORT_ONM; break;
+ case VERTEXSTATE_TSORT_NMO: m_se.vertices[2] = vtx; tsort = VERTEXSTATE_TSORT_MON; break;
+ }
+ }
+
+ }
+
+ // Update fields
+ m_se.vertex_state = (vcnt << VERTEXSTATE_VCNT_SHIFT) | tsort;
+
+ // Do we have three valid vertices?
+ if (vcnt == 3)
+ {
+ // Send the triangle to the setup engine
+ setup_triangle(flags);
+ }
+}
+
+
+//-------------------------------------------------
+// calculate_slope -
+//-------------------------------------------------
+
+void m2_te_device::calculate_slope(const slope_params &sp, float q1, float q2, float q3, float &slope_out, float &ddx_out)
+{
+ float detx = q1 * sp.y23 + q2 * sp.y31 + q3 * sp.y12;
+ float dety = q1 * sp.x23 + q2 * sp.x31 + q3 * sp.x12;
+ float detxy = sp.xstep_long * detx - dety;
+
+ slope_out = detxy * sp.iAria;
+ ddx_out = detx * sp.iAria;
+}
+
+
+//-------------------------------------------------
+// log_triangle -
+//-------------------------------------------------
+
+void m2_te_device::log_triangle(uint32_t flags)
+{
+ logerror("[Triangle]\n");
+
+ for (uint32_t i = 0; i < 3; ++i)
+ {
+ char s[64];
+ char t[64];
+ char p[64];
+
+ s[0] = '\0';
+ t[0] = '\0';
+ p[0] = '\0';
+
+ const se_vtx &vtx = m_se.vertices[i];
+
+ if (flags & VTX_FLAG_SHAD)
+ sprintf(s, "COLR[R:%.6f G:%.6f B:%.6f A:%.6f]", vtx.r, vtx.g, vtx.b, vtx.a);
+
+ if (flags & VTX_FLAG_TEXT)
+ sprintf(t, "TEXT[UW:%.6f VW:%.6f]", vtx.uw, vtx.vw);
+
+ if (flags & VTX_FLAG_PRSP)
+ sprintf(p, "PRSP[W:%.6f]", vtx.w);
+
+ logerror("V%d: X:%.6f Y:%.6f %s %s %s\n", i, vtx.x, vtx.y, s, t, p);
+ }
+}
+
+
+//-------------------------------------------------
+// setup_triangle -
+//-------------------------------------------------
+
+void m2_te_device::setup_triangle(uint32_t flags)
+{
+// log_triangle(flags);
+
+ se_vtx va = m_se.vertices[0];
+ se_vtx vb = m_se.vertices[1];
+ se_vtx vc = m_se.vertices[2];
+
+ // Truncate XY coordinates to integers
+ va.x = int_trunc(va.x);
+ vb.x = int_trunc(vb.x);
+ vc.x = int_trunc(vc.x);
+
+ va.y = int_trunc(va.y);
+ vb.y = int_trunc(vb.y);
+ vc.y = int_trunc(vc.y);
+
+
+ // Scale and truncate colors
+ if (flags & VTX_FLAG_SHAD)
+ {
+ va.r = int_trunc(va.r * 255.0f);
+ va.g = int_trunc(va.g * 255.0f);
+ va.b = int_trunc(va.b * 255.0f);
+ va.a = int_trunc(va.a * 255.0f);
+
+ vb.r = int_trunc(vb.r * 255.0f);
+ vb.g = int_trunc(vb.g * 255.0f);
+ vb.b = int_trunc(vb.b * 255.0f);
+ vb.a = int_trunc(vb.a * 255.0f);
+
+ vc.r = int_trunc(vc.r * 255.0f);
+ vc.g = int_trunc(vc.g * 255.0f);
+ vc.b = int_trunc(vc.b * 255.0f);
+ vc.a = int_trunc(vc.a * 255.0f);
+ }
+
+ // Sort the vertices into top, middle and bottom
+ uint32_t a = 0;
+
+ if ((va.y - vb.y) < 0.0f) a |= 4;
+ if ((vb.y - vc.y) < 0.0f) a |= 2;
+ if ((vc.y - va.y) < 0.0f) a |= 1;
+
+ const se_vtx *v1 = NULL;
+ const se_vtx *v2 = NULL;
+ const se_vtx *v3 = NULL;
+
+ switch (a)
+ {
+ case 1: v1 = &vc; v2 = &vb; v3 = &va; break;
+ case 2: v1 = &vb; v2 = &va; v3 = &vc; break;
+ case 3: v1 = &vb; v2 = &vc; v3 = &va; break;
+ case 4: v1 = &va; v2 = &vc; v3 = &vb; break;
+ case 5: v1 = &vc; v2 = &va; v3 = &vb; break;
+ case 6: v1 = &va; v2 = &vb; v3 = &vc; break;
+ default:
+ return; // Reject degenerates
+ }
+
+
+ // Determine the W range for depth and texture values
+ uint32_t wrange = 0;
+
+ if (flags & VTX_FLAG_PRSP)
+ {
+ const uint8_t exp_zero = 0x7f;
+ const uint8_t wexp0 = exp_zero - 3;
+ const uint8_t wexp1 = exp_zero - 6;
+ const uint8_t wexp2 = exp_zero - 9;
+
+ uint8_t w1exp = extract_exp(v1->w);
+ uint8_t w2exp = extract_exp(v2->w);
+ uint8_t w3exp = extract_exp(v3->w);
+
+ if (w1exp >= wexp0 || w2exp >= wexp0 || w3exp >= wexp0)
+ wrange = 0;
+ else if (w1exp >= wexp1 || w2exp >= wexp1 || w3exp >= wexp1)
+ wrange = 1;
+ else if (w1exp >= wexp2 || w2exp >= wexp2 || w3exp >= wexp2)
+ wrange = 2;
+ else
+ wrange = 3;
+ }
+
+ // Calculate the coordinate deltas
+ float x12 = v1->x - v2->x;
+ float x23 = v2->x - v3->x;
+ float x31 = v3->x - v1->x;
+ float y12 = v1->y - v2->y;
+ float y23 = v2->y - v3->y;
+ float y31 = v3->y - v1->y;
+
+ // Calculate the triangle area
+ float aria = v1->x * y23 + v2->x * y31 + v3->x * y12;
+
+ // Reject degenerates
+ if (aria == 0.0f)
+ return;
+
+ float iaria = 1.0f / aria;
+
+ // Determine scan direction
+ bool scan_lr = aria > 0.0f;
+
+ // Calculate vertex slopes
+ float abs_y12 = fabs(y12);
+ float abs_y23 = fabs(y23);
+ float abs_y31 = fabs(y31);
+
+
+ // Avoid division by zero
+ float xslope_0 = (abs_y12 > 0.0f) ? (-x12 / abs_y12) : 0.0f;
+ float xslope_1 = (abs_y23 > 0.0f) ? (-x23 / abs_y23) : 0.0f;
+ float xslope_long = (abs_y31 > 0.0f) ? (x31 / abs_y31) : 0.0f;
+
+
+ // Calculate x steps
+ float xstep_long;
+ float xstep_0;
+ float xstep_1;
+
+ if (scan_lr)
+ {
+ xstep_long = floor(xslope_long);
+ xstep_0 = ceil(xslope_0);
+ xstep_1 = ceil(xslope_1);
+ }
+ else
+ {
+ xstep_long = ceil(xslope_long);
+ xstep_0 = floor(xslope_0);
+ xstep_1 = floor(xslope_1);
+ }
+
+ float xystep_0 = x12 - xstep_0 * y12;
+ float xystep_1 = x23 - xstep_1 * y23;
+ float xystep_long = x31 - xstep_long * y31;
+
+ float dy_0 = -y12;
+ float dy_1 = -y23;
+ float dy_long = -y31;
+
+
+ // Calculate color and texture slopes
+ slope_params sparams = { y23, y31, y12, x23, x31, x12, xstep_long, iaria };
+
+ float r_slope = 0.0f, r_ddx = 0.0f;
+ float g_slope = 0.0f, g_ddx = 0.0f;
+ float b_slope = 0.0f, b_ddx = 0.0f;
+ float a_slope = 0.0f, a_ddx = 0.0f;
+ float w_slope = 0.0f, w_ddx = 0.0f;
+ float uw_slope = 0.0f, uw_ddx = 0.0f;
+ float vw_slope = 0.0f, vw_ddx = 0.0f;
+
+ if (flags & VTX_FLAG_SHAD)
+ {
+ calculate_slope(sparams, v1->r, v2->r, v3->r, r_slope, r_ddx);
+ calculate_slope(sparams, v1->g, v2->g, v3->g, g_slope, g_ddx);
+ calculate_slope(sparams, v1->b, v2->b, v3->b, b_slope, b_ddx);
+ calculate_slope(sparams, v1->a, v2->a, v3->a, a_slope, a_ddx);
+ }
+
+ if (flags & VTX_FLAG_TEXT)
+ {
+ calculate_slope(sparams, v1->uw, v2->uw, v3->uw, uw_slope, uw_ddx);
+ calculate_slope(sparams, v1->vw, v2->vw, v3->vw, vw_slope, vw_ddx);
+ }
+
+ if (flags & VTX_FLAG_PRSP)
+ {
+ calculate_slope(sparams, v1->w, v2->w, v3->w, w_slope, w_ddx);
+ }
+
+ const uint32_t textbits = text_bits[wrange];
+ const uint32_t depthbits = depth_bits[wrange];
+
+ // Convert everything to fixed point and pass to the edge walker
+
+ /*
+ NOT SURE THESE ARE RIGHT. See P209
+
+ XY 12.0
+ SL s12.0
+ RGBA 9.0
+ RGBA SLOPES = s9.11
+ TEX: 11.13
+ DEPTH: 1.23
+ TEX SLOPES: s11.13
+ DEPTH SLOPES: s1.23
+ AREA : s23.0
+
+ Numbers output to the edge walker are 24 bits
+
+ IEE: 1.8.23
+ BDA: 1.7.24 (includes hidden bit)
+
+ However, we're using 23 bits.
+ */
+
+
+ // XY: 0000 0000 0iii iiii iiii 0000 0000 0000
+ // Colors: 0000 0000 0iii iiii ifff ffff ffff 0000
+ // UV:
+ // W: 0000 0000 0iii iiii iiii iiii iiii iiii
+
+
+ // Converted numbers are left-aligned into the mantissa
+ // However, we want them right-aligned for convenience
+ // TODO: Is there a better way to do this?
+ m_es.x1 = ieee754_to_tefix(v1->x, xy_bits) >> xy_rshift;
+ m_es.y1 = ieee754_to_tefix(v1->y, xy_bits) >> xy_rshift;
+ m_es.x2 = ieee754_to_tefix(v2->x, xy_bits) >> xy_rshift;
+ m_es.y2 = ieee754_to_tefix(v2->y, xy_bits) >> xy_rshift;
+ m_es.y3 = ieee754_to_tefix(v3->y, xy_bits) >> xy_rshift;
+
+ m_es.xstep_0 = ieee754_to_tefix(xstep_0, xy_bits) >> xy_rshift;
+ m_es.xstep_1 = ieee754_to_tefix(xstep_1, xy_bits) >> xy_rshift;
+ m_es.xstep_long = ieee754_to_tefix(xstep_long, xy_bits) >> xy_rshift;
+ m_es.xystep_0 = ieee754_to_tefix(xystep_0, xy_bits) >> xy_rshift;
+ m_es.xystep_1 = ieee754_to_tefix(xystep_1, xy_bits) >> xy_rshift;
+ m_es.xystep_long = ieee754_to_tefix(xystep_long, xy_bits) >> xy_rshift;
+ m_es.dy_0 = ieee754_to_tefix(dy_0, xy_bits) >> xy_rshift;
+ m_es.dy_1 = ieee754_to_tefix(dy_1, xy_bits) >> xy_rshift;
+ m_es.dy_long = ieee754_to_tefix(dy_long, xy_bits) >> xy_rshift;
+
+ m_es.r1 = ieee754_to_tefix(v1->r, color_bits) >> color_rshift;
+ m_es.g1 = ieee754_to_tefix(v1->g, color_bits) >> color_rshift;
+ m_es.b1 = ieee754_to_tefix(v1->b, color_bits) >> color_rshift;
+ m_es.a1 = ieee754_to_tefix(v1->a, color_bits) >> color_rshift;
+ m_es.ddx_r = ieee754_to_tefix(r_ddx, color_bits) >> color_rshift;
+ m_es.ddx_g = ieee754_to_tefix(g_ddx, color_bits) >> color_rshift;
+ m_es.ddx_b = ieee754_to_tefix(b_ddx, color_bits) >> color_rshift;
+ m_es.ddx_a = ieee754_to_tefix(a_ddx, color_bits) >> color_rshift;
+ m_es.slope_r = ieee754_to_tefix(r_slope, color_bits) >> color_rshift;
+ m_es.slope_g = ieee754_to_tefix(g_slope, color_bits) >> color_rshift;
+ m_es.slope_b = ieee754_to_tefix(b_slope, color_bits) >> color_rshift;
+ m_es.slope_a = ieee754_to_tefix(a_slope, color_bits) >> color_rshift;
+
+ // No need to shift these
+ m_es.uw1 = ieee754_to_tefix(v1->uw, textbits);
+ m_es.vw1 = ieee754_to_tefix(v1->vw, textbits);
+ m_es.w1 = ieee754_to_tefix(v1->w, depthbits);
+ m_es.ddx_uw = ieee754_to_tefix(uw_ddx, textbits);
+ m_es.ddx_vw = ieee754_to_tefix(vw_ddx, textbits);
+ m_es.ddx_w = ieee754_to_tefix(w_ddx, depthbits);
+ m_es.slope_uw = ieee754_to_tefix(uw_slope, textbits);
+ m_es.slope_vw = ieee754_to_tefix(vw_slope, textbits);
+ m_es.slope_w = ieee754_to_tefix(w_slope, depthbits);
+
+ m_es.r2l = !scan_lr;
+
+#if TEST_TIMING
+ g_statistics[STAT_TRIANGLES_PROCESSED]++;
+#endif
+
+ // Proceed to walk the edges
+ walk_edges(wrange);
+}
+
+static inline bool ins(bool lr, int32_t v)
+{
+ return lr ? v <= 0 : v >= 0;
+}
+
+static inline bool ein(bool lr, int32_t v)
+{
+ return lr ? v < 0 : v > 0;
+}
+
+static const uint8_t m_nr_rom[128] =
+{
+ 127, 125, 123, 121, 119, 118,
+ 116, 114, 112, 110, 109, 107,
+ 105, 104, 102, 100, 99, 97,
+ 96, 94, 93, 91, 90, 88,
+ 87, 86, 84, 83, 81, 80,
+ 79, 78, 76, 75, 74, 73,
+ 71, 70, 69, 68, 67, 65,
+ 64, 63, 62, 61, 60, 59,
+ 58, 57, 56, 55, 54, 53,
+ 52, 51, 50, 49, 48, 47,
+ 46, 45, 44, 43, 42, 41,
+ 40, 40, 39, 38, 37, 36,
+ 35, 35, 34, 33, 32, 31,
+ 31, 30, 29, 28, 28, 27,
+ 26, 25, 25, 24, 23, 23,
+ 22, 21, 21,20, 19, 19,
+ 18, 17, 17, 16, 15, 15,
+ 14, 13, 13, 12, 12, 11,
+ 11, 10, 9, 9, 8, 8,
+ 7, 7, 6, 5, 5, 4,
+ 4, 3, 3, 2, 2, 1,
+ 1, 0
+};
+
+
+//-------------------------------------------------
+// nr_invert - Newton Rhapson reciprocal
+//-------------------------------------------------
+
+uint32_t nr_invert(uint32_t num, uint32_t & shift_amount)
+{
+ uint32_t original;
+ uint32_t first_guess;
+ uint32_t m;
+ uint32_t shfnum;
+
+ // TODO: Unconfirmed but seems sensible
+ if (num == 0)
+ {
+ shift_amount = 0;
+ return 0;
+ }
+
+ shift_amount = -1;
+ while ((num << ++shift_amount) < 0x800000);
+
+ shfnum = num << shift_amount;
+ original = shfnum >> 8;
+ first_guess = m_nr_rom[(shfnum >> 16) & ((1 << 7) - 1)];
+ first_guess = first_guess + 128;
+ m = first_guess * original;
+ m = (1 << 24) - m;
+ m = m >> 3;
+ m = first_guess * m;
+ m = m >> 8;
+
+ return m;
+}
+
+
+//-------------------------------------------------
+// clamp -
+//-------------------------------------------------
+
+uint32_t clamp(int32_t v, int32_t min, int32_t max)
+{
+ if (v < min)
+ return min;
+ else if (v > max)
+ return max;
+
+ return v;
+}
+
+
+//-------------------------------------------------
+// walk_edges -
+//-------------------------------------------------
+
+void m2_te_device::walk_edges(uint32_t wrange)
+{
+ bool dsp_off = m_es.es_cntl & ESCNTL_DSPOFF;
+ bool du_scan = m_es.es_cntl & ESCNTL_DUSCAN;
+ bool omit_right = true;
+ bool scan_lr = m_es.r2l == false;
+
+ uint32_t cf_long = 0;
+ uint32_t cf_short = 0;
+ uint32_t xystep;
+ uint32_t xstep;
+ uint32_t dy;
+ uint32_t xs;
+ uint32_t xe;
+
+ int32_t r, g, b, a;
+ int32_t uw, vw;
+ int32_t w;
+
+ // Determine starting values
+ uint32_t y = m_es.y1;
+
+ // Flat-top triangles are a special case
+ if (m_es.y1 == m_es.y2)
+ {
+ xystep = m_es.xystep_1;
+ xstep = m_es.xstep_1;
+ dy = m_es.dy_1;
+
+ if (scan_lr ^ (m_es.x1 < m_es.x2))
+ {
+ // TODO: Is this possible?
+ assert_always(false, "SPECIAL CASE: WHAT DO?");
+ r = m_es.r1; // Where do the colors come from?
+ g = m_es.g1;
+ b = m_es.b1;
+ a = m_es.a1;
+ uw = m_es.uw1;
+ vw = m_es.vw1;
+ w = m_es.w1;
+
+ xs = m_es.x2;
+ xe = m_es.x1;
+ }
+ else
+ {
+ r = m_es.r1;
+ g = m_es.g1;
+ b = m_es.b1;
+ a = m_es.a1;
+ uw = m_es.uw1;
+ vw = m_es.vw1;
+ w = m_es.w1;
+
+ xs = m_es.x1;
+ xe = m_es.x2;
+ }
+ }
+ else
+ {
+ // Regular triangles
+ xystep = m_es.xystep_0;
+ xstep = m_es.xstep_0;
+ dy = m_es.dy_0;
+ r = m_es.r1;
+ g = m_es.g1;
+ b = m_es.b1;
+ a = m_es.a1;
+ uw = m_es.uw1;
+ vw = m_es.vw1;
+ w = m_es.w1;
+
+ xs = m_es.x1;
+ xe = xs;
+ }
+
+ do
+ {
+ // Render the pixels from this span
+ walk_span(wrange, omit_right, y, xs, xe, r, g, b, a, uw, vw, w);
+
+ // Now update the values
+ omit_right = false;
+ cf_short += xystep;
+ cf_long += m_es.xystep_long;
+
+ bool step_back = false;
+
+ if (scan_lr)
+ {
+ xs += m_es.xstep_long;
+ xe += xstep;
+
+ if (!ins(scan_lr, cf_long))
+ {
+ step_back = true;
+ cf_long += m_es.dy_long;
+ xs += xy_one;
+ }
+
+ if (dsp_off ? !ins(scan_lr, cf_short) : !ein(scan_lr, cf_short))
+ {
+ cf_short -= dy;
+ xe -= xy_one;
+ }
+ }
+ else
+ {
+ xe += xstep;
+ xs += m_es.xstep_long;
+
+ if (!ins(scan_lr, cf_short))
+ {
+ cf_short += dy;
+ xe += xy_one;
+ }
+
+ if (dsp_off ? !ins(scan_lr, cf_long) : !ein(scan_lr, cf_long))
+ {
+ step_back = true;
+ cf_long -= m_es.dy_long;
+ xs -= xy_one;
+ }
+ }
+
+ // Update the color values
+ if (!(m_gc.te_master_mode & TEMASTER_MODE_DSHADE))
+ {
+ if (scan_lr)
+ {
+ r += step_back ? m_es.slope_r + m_es.ddx_r : m_es.slope_r;
+ g += step_back ? m_es.slope_g + m_es.ddx_g : m_es.slope_g;
+ b += step_back ? m_es.slope_b + m_es.ddx_b : m_es.slope_b;
+ a += step_back ? m_es.slope_a + m_es.ddx_a : m_es.slope_a;
+ }
+ else
+ {
+ r += step_back ? m_es.slope_r - m_es.ddx_r : m_es.slope_r;
+ g += step_back ? m_es.slope_g - m_es.ddx_g : m_es.slope_g;
+ b += step_back ? m_es.slope_b - m_es.ddx_b : m_es.slope_b;
+ a += step_back ? m_es.slope_a - m_es.ddx_a : m_es.slope_a;
+ }
+
+ // Clamp to 8.11
+ r = clamp(r, 0, 0x0007ffff);
+ g = clamp(g, 0, 0x0007ffff);
+ b = clamp(b, 0, 0x0007ffff);
+ a = clamp(a, 0, 0x0007ffff);
+ }
+
+ if (!(m_es.es_cntl & TEMASTER_MODE_DTEXT))
+ {
+ if (scan_lr)
+ {
+ uw += step_back ? m_es.slope_uw + m_es.ddx_uw : m_es.slope_uw;
+ vw += step_back ? m_es.slope_vw + m_es.ddx_vw : m_es.slope_vw;
+ }
+ else
+ {
+ uw += step_back ? m_es.slope_uw - m_es.ddx_uw : m_es.slope_uw;
+ vw += step_back ? m_es.slope_vw - m_es.ddx_vw : m_es.slope_vw;
+ }
+
+ // Clamp to 10.13
+ uw = clamp(uw, 0, 0x007fffff);
+ vw = clamp(vw, 0, 0x007fffff);
+ }
+
+ if (!(m_es.es_cntl & ESCNTL_PERSPECTIVEOFF))
+ {
+ if (scan_lr)
+ w += step_back ? m_es.slope_w + m_es.ddx_w : m_es.slope_w;
+ else
+ w += step_back ? m_es.slope_w - m_es.ddx_w : m_es.slope_w;
+
+ // Clamp to 0.23
+ w = clamp(w, 0, 0x007fffff);
+ }
+
+ // Update Y
+ uint32_t next_y = y + (du_scan ? -xy_one : xy_one);
+
+ if (next_y == m_es.y2)
+ {
+ cf_short = 0;
+ xystep = m_es.xystep_1;
+ xstep = m_es.xstep_1;
+ dy = m_es.dy_1;
+ xe = m_es.x2;
+
+ if (scan_lr)
+ omit_right = true;
+ }
+
+ y = next_y;
+
+ } while (y != m_es.y3);
+}
+
+
+
+//-------------------------------------------------
+// texcoord_gen -
+//-------------------------------------------------
+
+void m2_te_device::texcoord_gen(uint32_t wrange, uint32_t uw, uint32_t vw, uint32_t w,
+ uint32_t & uo, uint32_t & vo, uint32_t & wo)
+{
+
+ // Perspective correction
+ if (!(m_es.es_cntl & ESCNTL_PERSPECTIVEOFF))
+ {
+ uint32_t wshift;
+
+ // Calculate the inverse of 1/w. Output is 0.20
+ uint32_t winv = nr_invert(w, wshift);
+
+ // 10.13 normalize then reduce to 10.6
+ // Normalization occurs by detecting leading zeroes and
+ // left-shifting until 1 in MSB
+
+ uint32_t uwshift = 0;
+ uint32_t vwshift = 0;
+
+ while ((uw < 0x00400000) && uwshift < 7)
+ {
+ uw <<= 1;
+ ++uwshift;
+ }
+
+ while ((vw < 0x00400000) && vwshift < 7)
+ {
+ vw <<= 1;
+ ++vwshift;
+ }
+
+ uint32_t normalized_uw = uw >> 7;
+ uint32_t normalized_vw = vw >> 7;
+
+ // 10.6 * 0.20 = 10.26
+ uint64_t ur = (uint64_t)normalized_uw * winv;
+ uint64_t vr = (uint64_t)normalized_vw * winv;
+
+ // De-normalize
+ if (uwshift > wshift)
+ ur >>= (uwshift - wshift);
+ else
+ ur <<= (wshift - uwshift);
+
+ if (vwshift > wshift)
+ vr >>= (vwshift - wshift);
+ else
+ vr <<= (wshift - vwshift);
+
+ // Reduce from 10.26 to 10.6
+ uo = ur >> 20;
+ vo = vr >> 20;
+ }
+ else
+ {
+ // Reduce from 10.13 to 10.6
+ // TODO: Is this actually correct?
+ uo = uw >> (7 + (10 - text_bits[wrange]));
+ vo = vw >> (7 + (10 - text_bits[wrange]));
+ }
+
+ // Output to texture mapper is 10.4
+ uo = uo >> 2;
+ vo = vo >> 2;
+}
+
+
+//-------------------------------------------------
+// lod_calc -
+//-------------------------------------------------
+
+uint32_t m2_te_device::lod_calc(uint32_t u0, uint32_t v0, uint32_t u1, uint32_t v1)
+{
+ return 0;
+}
+
+
+//-------------------------------------------------
+// get_tram_bitdepth
+//-------------------------------------------------
+
+uint32_t m2_te_device::get_tram_bitdepth()
+{
+ // TODO: Could cache this
+ const uint32_t tex_type = m_tm.tex_exptype;
+
+ uint32_t bitdepth = 0;
+
+ if (tex_type & TXTEXPFORM_COLORON)
+ bitdepth += (tex_type & TXTEXPFORM_CDEPTH_MASK) >> TXTEXPFORM_CDEPTH_SHIFT;
+ if (tex_type & TXTEXPFORM_ALPHAON)
+ bitdepth += (tex_type & TXTEXPFORM_ADEPTH_MASK) >> TXTEXPFORM_ADEPTH_SHIFT;
+ if (tex_type & TXTEXPFORM_SSBON)
+ ++bitdepth;
+
+ return bitdepth;
+}
+
+
+//-------------------------------------------------
+// get_texture_color -
+//-------------------------------------------------
+void m2_te_device::get_texture_color(uint32_t u, uint32_t v, uint32_t lod,
+ uint32_t & r, uint32_t & g, uint32_t & b, uint32_t & a, uint32_t & s)
+{
+ uint32_t texaddr;
+ uint32_t texbit;
+ uint32_t texdepth = get_tram_bitdepth();
+
+ // TODO: Multiple LODs
+ uint32_t filter = (m_tm.tex_addr_cntl >> TXTADDRCNTL_R12FILTERSEL_SHIFT) & TXTADDRCNTL_FILTERSEL_MASK;
+
+ switch (filter)
+ {
+ case TXTADDRCNTL_FILTERSEL_POINT:
+ case TXTADDRCNTL_FILTERSEL_LINEAR: // TODO
+ {
+ addr_calc(u, v, lod, texaddr, texbit, texdepth);
+ get_texel(texaddr, texbit, texdepth, r, g, b, a, s);
+ break;
+ }
+
+ case TXTADDRCNTL_FILTERSEL_BILINEAR:
+ case TXTADDRCNTL_FILTERSEL_QUASITRI: // TODO
+ {
+ // See p170 for implemenation
+ uint32_t r0, g0, b0, a0, s0;
+ uint32_t r1, g1, b1, a1, s1;
+ uint32_t r2, g2, b2, a2, s2;
+ uint32_t r3, g3, b3, a3, s3;
+
+ addr_calc(u, v, lod, texaddr, texbit, texdepth);
+ get_texel(texaddr, texbit, texdepth, r0, g0, b0, a0, s0);
+ addr_calc(u + 0x10, v, lod, texaddr, texbit, texdepth);
+ get_texel(texaddr, texbit, texdepth, r1, g1, b1, a1, s1);
+ addr_calc(u, v + 0x10, lod, texaddr, texbit, texdepth);
+ get_texel(texaddr, texbit, texdepth, r2, g2, b2, a2, s2);
+ addr_calc(u + 0x10, v + 0x10, lod, texaddr, texbit, texdepth);
+ get_texel(texaddr, texbit, texdepth, r3, g3, b3, a3, s3);
+
+ // LERP
+ uint32_t ufrac = u & 0xf;
+ uint32_t vfrac = v & 0xf;
+ uint32_t om_ufrac = 0x10 - ufrac;
+ uint32_t om_vfrac = 0x10 - vfrac;
+
+ // This is probably wrong
+ s0 = s0 * 0xff;
+ s1 = s1 * 0xff;
+ s2 = s2 * 0xff;
+ s3 = s3 * 0xff;
+
+ // 8.0 * 1.4 + 8.0 * 1.4 = 9.4
+ uint32_t rl = r0 * (om_ufrac) + r1 * ufrac;
+ uint32_t gl = g0 * (om_ufrac) + g1 * ufrac;
+ uint32_t bl = b0 * (om_ufrac) + b1 * ufrac;
+ uint32_t al = a0 * (om_ufrac) + a1 * ufrac;
+ uint32_t sl = s0 * (om_ufrac) + s1 * ufrac;
+
+ uint32_t ru = r2 * (om_ufrac) + r3 * ufrac;
+ uint32_t gu = g2 * (om_ufrac) + g3 * ufrac;
+ uint32_t bu = b2 * (om_ufrac) + b3 * ufrac;
+ uint32_t au = a2 * (om_ufrac) + a3 * ufrac;
+ uint32_t su = s2 * (om_ufrac) + s3 * ufrac;
+
+ // 9.4 * 0.4 + 9.4 * 0.4 = 10.8?
+ r = (rl * om_vfrac + ru * vfrac) >> 8;
+ g = (gl * om_vfrac + gu * vfrac) >> 8;
+ b = (bl * om_vfrac + bu * vfrac) >> 8;
+ a = (al * om_vfrac + au * vfrac) >> 8;
+
+ s = ((sl * om_vfrac + su * vfrac) >> 10) ? 1 : 0;
+ break;
+ }
+ }
+}
+
+
+//-------------------------------------------------
+// addr_calc -
+//-------------------------------------------------
+void m2_te_device::addr_calc(uint32_t u, uint32_t v, uint32_t lod,
+ uint32_t & texaddr, uint32_t & texbit, uint32_t & tdepth)
+{
+ uint32_t u_mask = (m_tm.uv_mask & TXTUVMASK_UMASK_MASK) >> TXTUVMASK_UMASK_SHIFT;
+ uint32_t v_mask = (m_tm.uv_mask & TXTUVMASK_VMASK_MASK) >> TXTUVMASK_VMASK_SHIFT;
+ uint32_t u_max = (m_tm.uv_max & TXTUVMAX_UMAX_MASK) >> TXTUVMAX_UMAX_SHIFT;
+ uint32_t v_max = (m_tm.uv_max & TXTUVMAX_VMAX_MASK) >> TXTUVMAX_VMAX_SHIFT;
+
+ // Remove fractional parts (10.0)
+ uint32_t u0 = u >> 4;
+ uint32_t v0 = v >> 4;
+
+ u0 = u0 & u_mask;
+ v0 = v0 & v_mask;
+
+ u_max &= u_mask;
+ v_max &= v_mask;
+
+ u0 = clamp(u0, 0, u_max);
+ v0 = clamp(v0, 0, v_max);
+
+ // LOD
+ uint32_t lodmax = m_tm.tex_addr_cntl & TXTADDRCNTL_LODMAX_MASK;
+ uint32_t lod_shift = (lodmax - lod) & 3;
+
+ // 20.0
+ uint32_t voffset = (v0 * (u_max + 1)) << lod_shift;
+ uint32_t offset = (voffset + u0) >> lod_shift;
+
+ offset = offset * tdepth;
+
+ uint32_t base_addr = 0;
+
+ switch (lod)
+ {
+ case 0: base_addr = m_tm.tex_lod_base0; break;
+ case 1: base_addr = m_tm.tex_lod_base1; break;
+ case 2: base_addr = m_tm.tex_lod_base2; break;
+ case 3: base_addr = m_tm.tex_lod_base3; break;
+ }
+
+ uint32_t adsum = (base_addr << 3) + offset;
+
+ texaddr = adsum / 32;
+ texbit = adsum & 31;
+}
+
+
+void m2_te_device::get_texel(uint32_t tex_addr, uint32_t tex_bit, uint32_t tdepth,
+ uint32_t & r, uint32_t & g, uint32_t & b, uint32_t & a, uint32_t & ssb)
+{
+ const uint32_t tex_type = m_tm.tex_exptype;
+
+ uint8_t rtex, gtex, btex, atex;
+ uint8_t ssbtex;
+
+ uint8_t rpip, gpip, bpip, apip;
+ uint8_t ssbpip;
+
+ // Page 127
+ // See M2_2_7_part3_clt_ocrc.pdf page 95 for better expl.
+
+ uint32_t tram_data = m_tram[tex_addr];
+
+#if TEST_TIMING
+ g_statistics[STAT_TEXEL_READS]++;
+#endif
+
+
+ // Align the texel
+ uint32_t texel = tram_data >> (tex_bit & ~7);
+
+ // Align within byte (TODO: Fix for 32-bits!)
+ texel >>= (tex_bit ^ (8 - tdepth)) & 7;
+
+ uint32_t c_depth = (tex_type & TXTEXPFORM_CDEPTH_MASK) >> TXTEXPFORM_CDEPTH_SHIFT;
+ uint32_t a_depth = (tex_type & TXTEXPFORM_ADEPTH_MASK) >> TXTEXPFORM_ADEPTH_SHIFT;
+ uint32_t i_depth = c_depth;
+
+ // Color is either sourced from tram or PIP
+ if (tex_type & TXTEXPFORM_LITERAL)
+ {
+ if (tex_type & TXTEXPFORM_COLORON)
+ {
+ // Literal can only be 32-bit or 16bpp
+ uint32_t mask = (1 << c_depth) - 1;
+
+ btex = texel & mask;
+ texel >>= c_depth;
+ gtex = texel & mask;
+ texel >>= c_depth;
+ rtex = texel & mask;
+ texel >>= c_depth;
+ }
+ else
+ {
+ rtex = gtex = btex = 0;
+ }
+ }
+ else
+ {
+ rtex = gtex = btex = 0;
+ }
+
+ if (tex_type & TXTEXPFORM_INDEXON)
+ {
+ uint32_t pipidx = texel & ((1 << i_depth) - 1);
+ uint32_t pipaddr = (m_tm.tex_pip_cntl & TXTPIPCNTL_INDEX_OFFSET) + pipidx;
+
+ uint32_t pipval = m_pipram[pipaddr & 0xff];
+
+ // Color, alpha and SSB always present in PIP
+ bpip = pipval & 0xff;
+ gpip = (pipval >> 8) & 0xff;
+ rpip = (pipval >> 16) & 0xff;
+ apip = (pipval >> 24) & 0x7f;
+ ssbpip = pipval >> 31;
+
+ texel >>= i_depth;
+ }
+ else
+ {
+ // Nothing coming from the PIP
+ rpip = gpip = bpip = 0;
+ apip = ssbpip = 0;
+ }
+
+ // Alpha and SSB could be present
+ if (tex_type & TXTEXPFORM_ALPHAON)
+ {
+ uint32_t mask = (1 << a_depth) - 1;
+
+ atex = texel & mask;
+
+ // Need to expand alpha if 4-bits
+ if (a_depth == 4)
+ atex = (atex << 3) | (atex >> 1);
+
+ texel >>= a_depth;
+ }
+ else
+ {
+ atex = 0;
+ }
+
+ if (tex_type & TXTEXPFORM_SSBON)
+ ssbtex = texel & 1;
+ else
+ ssbtex = 0;
+
+ // Select the SSB
+ switch ((m_tm.tex_pip_cntl & TXTPIPCNTL_SSBSEL_MASK) >> TXTPIPCNTL_SSBSEL_SHIFT)
+ {
+ case TXTPIPCNTL_SEL_CONSTANT:
+ {
+ uint32_t cnst = ssbtex ? m_tm.tex_srcconst1 : m_tm.tex_srcconst0;
+ ssb = cnst >> 31;
+ break;
+ }
+ case TXTPIPCNTL_SEL_TRAM:
+ {
+ ssb = ssbtex;
+ break;
+ }
+ case TXTPIPCNTL_SEL_PIP:
+ {
+ ssb = ssbpip;
+ break;
+ }
+ }
+
+ // PIP color control
+ switch ((m_tm.tex_pip_cntl & TXTPIPCNTL_COLORSEL_MASK) >> TXTPIPCNTL_COLORSEL_SHIFT)
+ {
+ case TXTPIPCNTL_SEL_CONSTANT:
+ {
+ uint32_t cnst = ssb ? m_tm.tex_srcconst1 : m_tm.tex_srcconst0;
+ r = (cnst >> 16) & 0xff;
+ g = (cnst >> 8) & 0xff;
+ b = (cnst >> 0) & 0xff;
+ break;
+ }
+ case TXTPIPCNTL_SEL_TRAM:
+ {
+ r = rtex;
+ g = gtex;
+ b = btex;
+ break;
+ }
+ case TXTPIPCNTL_SEL_PIP:
+ {
+ r = rpip;
+ g = gpip;
+ b = bpip;
+ break;
+ }
+ }
+
+ // Alpha control
+ switch ((m_tm.tex_pip_cntl & TXTPIPCNTL_ALPHASEL_MASK) >> TXTPIPCNTL_ALPHASEL_SHIFT)
+ {
+ case TXTPIPCNTL_SEL_CONSTANT:
+ {
+ uint32_t cnst = ssb ? m_tm.tex_srcconst1 : m_tm.tex_srcconst0;
+ a = (cnst >> 24) & 0x7f;
+ break;
+ }
+ case TXTPIPCNTL_SEL_TRAM:
+ {
+ a = atex;
+ break;
+ }
+ case TXTPIPCNTL_SEL_PIP:
+ {
+ a = apip;
+ break;
+ }
+ }
+
+ // Expand alpha to 8-bits
+ a = (a << 1) | (a >> 6);
+}
+
+
+//-------------------------------------------------
+// lerp
+//-------------------------------------------------
+
+static inline uint8_t lerp(uint8_t a, uint8_t b, uint8_t t)
+{
+ if (t == 255)
+ return b;
+ if (t == 0)
+ return a;
+
+ return (((255 - t) * a) >> 8) + ((t * b) >> 8);
+}
+
+
+//-------------------------------------------------
+// multiply
+//-------------------------------------------------
+
+static inline uint8_t multiply(uint8_t a, uint8_t b)
+{
+ if (b == 255)
+ return a;
+ if (a == 255)
+ return b;
+ else
+ return (a * b) >> 8;
+}
+
+
+//-------------------------------------------------
+// texture_blend -
+//-------------------------------------------------
+
+void m2_te_device::texture_blend(
+ uint32_t ri, uint32_t gi, uint32_t bi, uint32_t ai,
+ uint32_t rt, uint32_t gt, uint32_t bt, uint32_t at, uint32_t ssbt,
+ uint32_t &ro, uint32_t &go, uint32_t &bo, uint32_t &ao, uint32_t &ssbo)
+{
+ uint32_t rbl = 0, gbl = 0, bbl = 0, abl = 0;
+
+ {
+ uint32_t ar, ab, ag, aa;
+ uint32_t br, bb, bg, ba;
+ uint32_t tr, tb, tg;
+
+ select_lerp( (m_tm.tex_tab_cntl & TXTTABCNTL_C_ASEL_MASK) >> TXTTABCNTL_C_ASEL_SHIFT,
+ ri, gi, bi, ai,
+ rt, gt, bt, at, ssbt,
+ ar, ag, ab );
+
+ select_lerp( (m_tm.tex_tab_cntl & TXTTABCNTL_C_BSEL_MASK) >> TXTTABCNTL_C_BSEL_SHIFT,
+ ri, gi, bi, ai,
+ rt, gt, bt, at, ssbt,
+ br, bg, bb );
+
+ select_lerp( (m_tm.tex_tab_cntl & TXTTABCNTL_C_TSEL_MASK) >> TXTTABCNTL_C_TSEL_SHIFT,
+ ri, gi, bi, ai,
+ rt, gt, bt, at, ssbt,
+ tr, tg, tb );
+
+ if (((m_tm.tex_tab_cntl & TXTTABCNTL_BLENDOP_MASK) >> TXTTABCNTL_BLENDOP_SHIFT) == TXTTABCNTL_BLENDOP_LERP)
+ {
+ rbl = lerp(ar, br, tr);
+ gbl = lerp(ag, bg, tg);
+ bbl = lerp(ab, bb, tb);
+ }
+ else
+ {
+ // TODO: CHECK ME
+ // Alpha is multiply only
+ select_mul( (m_tm.tex_tab_cntl & TXTTABCNTL_A_ASEL_MASK) >> TXTTABCNTL_A_ASEL_SHIFT,
+ ai, at, ssbt,
+ aa);
+
+ select_mul( (m_tm.tex_tab_cntl & TXTTABCNTL_A_BSEL_MASK) >> TXTTABCNTL_A_BSEL_SHIFT,
+ ai, at, ssbt,
+ ba);
+
+ rbl = multiply(ar, br);
+ gbl = multiply(ag, bg);
+ bbl = multiply(ab, bb);
+ abl = multiply(aa, ba);
+ }
+ }
+
+ // Now select the output
+ switch ((m_tm.tex_tab_cntl & TXTTABCNTL_C_OSEL_MASK) >> TXTTABCNTL_C_OSEL_SHIFT)
+ {
+ case TXTTABCNTL_CO_SEL_CITER:
+ {
+ ro = ri;
+ go = gi;
+ bo = bi;
+ break;
+ }
+ case TXTTABCNTL_CO_SEL_CT:
+ {
+ ro = rt;
+ go = gt;
+ bo = bt;
+ break;
+ }
+ case TXTTABCNTL_CO_SEL_BLEND:
+ {
+ ro = rbl;
+ go = gbl;
+ bo = bbl;
+ break;
+ }
+ case TXTTABCNTL_CO_SEL_RESERVED:
+ {
+ ro = go = bo = 0;
+ break;
+ }
+ }
+
+ // Select alpha output
+ switch ((m_tm.tex_tab_cntl & TXTTABCNTL_A_OSEL_MASK) >> TXTTABCNTL_A_OSEL_SHIFT)
+ {
+ case TXTTABCNTL_AO_SEL_AITER:
+ {
+ ao = ai;
+ break;
+ }
+ case TXTTABCNTL_AO_SEL_AT:
+ {
+ ao = at;
+ break;
+ }
+ case TXTTABCNTL_AO_SEL_BLEND:
+ {
+ ao = abl;
+ break;
+ }
+ case TXTTABCNTL_AO_SEL_RESERVED:
+ {
+ ao = 0;
+ break;
+ }
+ }
+}
+
+void m2_te_device::select_lerp( uint32_t sel,
+ uint32_t ri, uint32_t gi, uint32_t bi, uint32_t ai,
+ uint32_t rt, uint32_t gt, uint32_t bt, uint32_t at, uint32_t ssbt,
+ uint32_t & ar, uint32_t & ag, uint32_t & ab )
+{
+ switch (sel)
+ {
+ case TXTTABCNTL_C_ABTSEL_AITER:
+ {
+ ar = ai;
+ ag = ai;
+ ab = ai;
+ break;
+ }
+ case TXTTABCNTL_C_ABTSEL_CITER:
+ {
+ ar = ri;
+ ag = gi;
+ ab = bi;
+ break;
+ }
+ case TXTTABCNTL_C_ABTSEL_AT:
+ {
+ ar = at;
+ ag = at;
+ ab = at;
+ break;
+ }
+ case TXTTABCNTL_C_ABTSEL_CT:
+ {
+ ar = rt;
+ ag = gt;
+ ab = bt;
+ break;
+ }
+ case TXTTABCNTL_C_ABTSEL_ACONST:
+ {
+ uint32_t cnst = ssbt ? m_tm.tex_srcconst3 : m_tm.tex_srcconst2;
+ uint8_t aval = (cnst >> 24) & 0x7f;
+
+ // Expand to 8-bit
+ aval = (aval << 1) | (aval >> 6);
+
+ ar = aval;
+ ag = aval;
+ ab = aval;
+ break;
+ }
+ case TXTTABCNTL_C_ABTSEL_CCONST:
+ {
+ uint32_t cnst = ssbt ? m_tm.tex_srcconst3 : m_tm.tex_srcconst2;
+
+ ar = (cnst >> 24) & 0xff;
+ ag = (cnst >> 16) & 0xff;
+ ab = (cnst >> 0) & 0xff;
+ break;
+ }
+ default:
+ {
+ ar = 0;
+ ag = 0;
+ ab = 0;
+ break;
+ }
+ }
+}
+
+
+void m2_te_device::select_mul(uint32_t sel, uint32_t ai, uint32_t at, uint32_t ssbt,
+ uint32_t & a )
+{
+ switch (sel)
+ {
+ case TXTTABCNTL_A_ABSEL_AITER:
+ {
+ a = ai;
+ break;
+ }
+ case TXTTABCNTL_A_ABSEL_AT:
+ {
+ a = at;
+ break;
+ }
+ case TXTTABCNTL_A_ABSEL_ACONST:
+ {
+ uint32_t cnst = ssbt ? m_tm.tex_srcconst3 : m_tm.tex_srcconst2;
+ uint8_t aval = (cnst >> 24) & 0x7f;
+
+ // Expand to 8-bit
+ a = (aval << 1) | (aval >> 6);
+ break;
+ }
+ default:
+ {
+ a = 0;
+ break;
+ }
+ }
+}
+
+
+//-------------------------------------------------
+// write_dst_pixel - Write pixel to framebuffer
+//-------------------------------------------------
+
+void m2_te_device::write_dst_pixel()
+{
+ uint32_t mask = m_db.usergen_ctrl & DBUSERGENCTL_DESTOUT_MASK;
+
+ if (m_db.dst_ctrl & DBDESTCNTL_32BPP)
+ {
+ uint32_t dstaddr = m_db.dst_baseaddr + (m_dbstate.y * m_db.dst_xstride + m_dbstate.x) * sizeof(uint32_t);
+
+ uint32_t old = m_bda->read_bus32(dstaddr);
+ uint32_t out = 0;
+
+ uint8_t sdsb_a = (old >> 24) & 0xff;
+ uint8_t sr = (old >> 16) & 0xff;
+ uint8_t sg = (old >> 8) & 0xff;
+ uint8_t sb = old & 0xff;
+
+ out |= (mask & 8 ? (m_dbstate.dsb << 7) | (m_dbstate.dst.a >> 1) : sdsb_a) << 24;
+ out |= (mask & 4 ? m_dbstate.dst.r : sr) << 16;
+ out |= (mask & 2 ? m_dbstate.dst.g : sg) << 8;
+ out |= mask & 1 ? m_dbstate.dst.b : sb;
+
+ m_bda->write_bus32(dstaddr, out);
+ }
+ else
+ {
+ uint32_t dstaddr = m_db.dst_baseaddr + (m_dbstate.y * m_db.dst_xstride + m_dbstate.x) * sizeof(uint16_t);
+
+ uint16_t old = m_bda->read_bus16(dstaddr);
+ uint16_t out = 0;
+
+ uint8_t sdsb_a = (old >> 15) & 0x1;
+ uint8_t sr = (old >> 10) & 0x1f;
+ uint8_t sg = (old >> 5) & 0x1f;
+ uint8_t sb = old & 0x1f;
+
+ out |= (mask & 8 ? m_dbstate.dsb : sdsb_a) << 15;
+ out |= (mask & 4 ? m_dbstate.dst.r >> 3 : sr) << 10;
+ out |= (mask & 2 ? m_dbstate.dst.g >> 3 : sg) << 5;
+ out |= mask & 1 ? m_dbstate.dst.b >> 3 : sb;
+
+ m_bda->write_bus16(dstaddr, out);
+ }
+
+#if TEST_TIMING
+ g_statistics[STAT_PIXEL_STORES]++;
+#endif
+}
+
+
+//-------------------------------------------------
+// destination_blend -
+//-------------------------------------------------
+
+void m2_te_device::destination_blend(uint32_t x, uint32_t y, uint32_t w, const rgba & ti_color, uint8_t ssb)
+{
+ m_dbstate.x = x;
+ m_dbstate.y = y;
+ m_dbstate.w = w;
+ m_dbstate.ti = ti_color;
+ m_dbstate.ssb = ssb;
+
+ bool dis = false;
+ bool zpixout = true;
+ bool zbufout = true;
+ bool zclip = false;
+ bool zclipdis = false;
+ bool fbclipdis = false;
+ bool winclipdis = false;
+
+ // Z Status
+ uint32_t zgel = 0;
+
+ uint32_t zaddr;
+
+ // FBCLIP
+ {
+ uint32_t xclip = (m_db.fbclip & DBFBCLIP_XFBCLIP_MASK) >> DBFBCLIP_XFBCLIP_SHIFT;
+ uint32_t yclip = (m_db.fbclip & DBFBCLIP_YFBCLIP_MASK) >> DBFBCLIP_YFBCLIP_SHIFT;
+
+ fbclipdis = (x >= xclip) || (y >= yclip);
+
+ if (fbclipdis)
+ m_db.status |= DBSTATUS_FBCLIP;
+ }
+
+ // WINCLIP
+ {
+ uint32_t xmin = (m_db.x_winclip & DBFBXWINCLIP_XMIN_MASK) >> DBFBXWINCLIP_XMIN_SHIFT;
+ uint32_t xmax = (m_db.x_winclip & DBFBXWINCLIP_XMAX_MASK) >> DBFBXWINCLIP_XMAX_SHIFT;
+ uint32_t ymin = (m_db.y_winclip & DBFBYWINCLIP_YMIN_MASK) >> DBFBYWINCLIP_YMIN_SHIFT;
+ uint32_t ymax = (m_db.y_winclip & DBFBYWINCLIP_YMAX_MASK) >> DBFBYWINCLIP_YMAX_SHIFT;
+
+ bool inside = (x >= xmin) && (x < xmax) && (y >= ymin) && (y < ymax);
+
+ winclipdis = ((m_db.usergen_ctrl & DBUSERGENCTL_WCLIPINEN) && inside) ||
+ ((m_db.usergen_ctrl & DBUSERGENCTL_WCLIPOUTEN) && !inside);
+
+ if (winclipdis)
+ m_db.status |= DBSTATUS_WINCLIP;
+ }
+
+ select_src_pixel();
+
+ select_tex_pixel();
+
+ select_alpha_dsb();
+
+
+ {
+ uint8_t dm10, dm11, dm20, dm21;
+ uint32_t txtcnst0 = m_db.txt_coef_const0;
+ uint32_t txtcnst1 = m_db.txt_coef_const1;
+ uint32_t srccnst0 = m_db.src_coef_const0;
+ uint32_t srccnst1 = m_db.src_coef_const1;
+
+ if ((m_db.usergen_ctrl & DBUSERGENCTL_BLENDEN) && !(m_gc.te_master_mode & TEMASTER_MODE_DBLEND))
+ {
+ // Blue
+ dm10 = txtcnst0 & 0xff; txtcnst0 >>= 8;
+ dm11 = txtcnst1 & 0xff; txtcnst1 >>= 8;
+ dm20 = srccnst0 & 0xff; srccnst0 >>= 8;
+ dm21 = srccnst1 & 0xff; srccnst1 >>= 8;
+ m_dbstate.blend.b = color_blend(m_dbstate.texpath.b, m_dbstate.ti.b, m_dbstate.srcpath.b, m_dbstate.src.b, dm10, dm11, dm20, dm21);
+
+ // TODO: ALURGEL for each
+
+ // Green
+ dm10 = txtcnst0 & 0xff; txtcnst0 >>= 8;
+ dm11 = txtcnst1 & 0xff; txtcnst1 >>= 8;
+ dm20 = srccnst0 & 0xff; srccnst0 >>= 8;
+ dm21 = srccnst1 & 0xff; srccnst1 >>= 8;
+ m_dbstate.blend.g = color_blend(m_dbstate.texpath.g, m_dbstate.ti.g, m_dbstate.srcpath.g, m_dbstate.src.g, dm10, dm11, dm20, dm21);
+
+ // Red
+ dm10 = txtcnst0 & 0xff;
+ dm11 = txtcnst1 & 0xff;
+ dm20 = srccnst0 & 0xff;
+ dm21 = srccnst1 & 0xff;
+ m_dbstate.blend.r = color_blend(m_dbstate.texpath.r, m_dbstate.ti.r, m_dbstate.srcpath.r, m_dbstate.src.r, dm10, dm11, dm20, dm21);
+ }
+ else
+ {
+ m_dbstate.blend.r = m_dbstate.ti.r;
+ m_dbstate.blend.g = m_dbstate.ti.g;
+ m_dbstate.blend.b = m_dbstate.ti.b;
+ }
+
+ // Dithering
+ if (m_db.usergen_ctrl & DBUSERGENCTL_DITHEREN)
+ {
+ uint32_t dith_x = x & 3;
+ uint32_t dith_y = y & 3;
+
+ uint32_t dith_mtx = dith_y & 2 ? m_db.dither_mat_b : m_db.dither_mat_a;
+ uint32_t idx = 7 ^ (((dith_y & 1) << 2) | dith_x);
+ uint8_t val = (dith_mtx >> (idx * 4)) & 0xf;
+
+ m_dbstate.dst.r = dither(m_dbstate.blend.r, val);
+ m_dbstate.dst.g = dither(m_dbstate.blend.g, val);
+ m_dbstate.dst.b = dither(m_dbstate.blend.b, val);
+ }
+ else
+ {
+ m_dbstate.dst.r = m_dbstate.blend.r;
+ m_dbstate.dst.g = m_dbstate.blend.g;
+ m_dbstate.dst.b = m_dbstate.blend.b;
+ }
+ }
+
+
+ if (!(m_gc.te_master_mode & TEMASTER_MODE_DZBUF) &&
+ (m_db.usergen_ctrl & DBUSERGENCTL_ZBUFEN))
+ {
+ int32_t x_offs = (m_db.z_offset & DBZOFFS_XOFFS_MASK) >> DBZOFFS_XOFFS_SHIFT;
+ int32_t y_offs = (m_db.z_offset & DBZOFFS_YOFFS_MASK) >> DBZOFFS_YOFFS_SHIFT;
+
+ // Sign extend
+ x_offs = (x_offs << 20) >> 20;
+ y_offs = (x_offs << 20) >> 20;
+
+ x_offs += m_dbstate.x;
+ y_offs += m_dbstate.y;
+
+ uint32_t x_clip = (m_db.z_clip & DBZCLIP_XCLIP_MASK) >> DBZCLIP_XCLIP_SHIFT;
+ uint32_t y_clip = (m_db.z_clip & DBZCLIP_YCLIP_MASK) >> DBZCLIP_YCLIP_SHIFT;
+
+ zclip = (x_offs < 0) || (y_offs < 0) || (x_offs >= x_clip) || (y_offs >= y_clip);
+
+ zaddr = m_db.z_baseaddr + (x_offs + y_offs * m_db.dst_xstride) * 2;
+ }
+ else
+ {
+ zaddr = 0;
+ zclip = false;
+ }
+
+ zclipdis = zclip && (m_db.discard_ctrl & DBDISCARDCTL_ZCLIPDISEN);
+
+ if (zclipdis)
+ m_db.status |= DBSTATUS_ZCLIP;
+
+ // Z-path
+ if (!(m_gc.te_master_mode & TEMASTER_MODE_DZBUF) &&
+ (m_db.usergen_ctrl & DBUSERGENCTL_ZBUFEN) &&
+ !zclip)
+ {
+#if TEST_TIMING
+ g_statistics[STAT_ZBUFFER_LOADS]++;
+#endif
+
+#if 1 // TODO: Why are we using this?
+ int32_t zdiff = w - m_bda->read_bus16(zaddr);
+#else
+ // Shift W back to 0.23
+ uint16_t oldw = m_bda->read_bus16(zaddr);
+ uint32_t exp = (oldw & 0xc000) >> 14;
+ uint32_t man = (oldw & 0x3fff) << 17;
+
+ uint32_t oldz = man >> (exp * 3);
+
+ exp = (w & 0xc000) >> 14;
+ man = (w & 0x3fff) << 17;
+ uint32_t curz = man >> (exp * 3);
+
+ int32_t zdiff = curz - oldz;
+#endif
+
+ // TODO: Why isn't this working?
+ // W vs Z?
+ if (zdiff > 0)
+ {
+ zpixout = m_db.z_ctrl & 1;//DBZCNTL_ZPIXOUT_GT;
+ zbufout = m_db.z_ctrl & 2;//DBZCNTL_ZBUFOUT_GT;
+ zgel |= 4;
+ m_db.status |= DBSTATUS_ZFUNC_GT;
+ }
+ else if (zdiff == 0)
+ {
+ zpixout = m_db.z_ctrl & DBZCNTL_ZPIXOUT_EQ;
+ zbufout = m_db.z_ctrl & DBZCNTL_ZBUFOUT_EQ;
+ zgel |= 2;
+ m_db.status |= DBSTATUS_ZFUNC_EQ;
+ }
+ else
+ {
+ zpixout = m_db.z_ctrl & 0x10;//DBZCNTL_ZPIXOUT_LT;
+ zbufout = m_db.z_ctrl & 0x20;//DBZCNTL_ZBUFOUT_LT;
+ zgel |= 1;
+ m_db.status |= DBSTATUS_ZFUNC_LT;
+ }
+ }
+ else
+ {
+ zpixout = true;
+ zbufout = false;
+ }
+
+ // Discard logic
+ {
+ bool ssbdis = (m_db.discard_ctrl & DBDISCARDCTL_SSBDISEN) && (m_dbstate.ssb == 0);
+
+ bool adis = (m_db.discard_ctrl & DBDISCARDCTL_ADISEN) && (m_dbstate.dst.a == 0);
+
+ bool rgbdis = (m_db.discard_ctrl & DBDISCARDCTL_RGBDISEN) &&
+ (m_dbstate.dst.r == 0) && (m_dbstate.dst.g == 0) && (m_dbstate.dst.b == 0);
+
+ dis = fbclipdis || winclipdis || zclipdis || ssbdis || adis || rgbdis;
+ }
+
+ // Write output depth and color
+ if (!dis)
+ {
+ // Z-buffer
+ if (zbufout && (m_db.usergen_ctrl & DBUSERGENCTL_ZOUTEN))
+ {
+#if TEST_TIMING
+ g_statistics[STAT_ZBUFFER_STORES]++;
+#endif
+ m_bda->write_bus16(zaddr, w & 0xffff);
+ }
+
+ // Color
+ if (zpixout && (m_db.supergen_ctrl & DBSUPERGENCTL_DESTOUTEN))
+ {
+ write_dst_pixel();
+ }
+ }
+
+ // TODO: Status
+/*
+ {
+ fbClipStat = fbClipDis
+ winClipStat = winClipDis
+ zClipStat = zClipDis
+ alurstat
+ alugstat
+ alubstat
+ zFuncStat
+ anyRender
+ set_interrupt
+ }
+*/
+}
+
+// Select between texture unit and source pixel
+void m2_te_device::select_tex_pixel()
+{
+ uint32_t cntl;
+
+ // TODO: REGBITS
+ if (m_dbstate.ti.a == 0)
+ cntl = (m_db.src_alpha_ctrl >> 4) & 3;
+ else if (m_dbstate.ti.a == 255)
+ cntl = m_db.src_alpha_ctrl & 3;
+ else
+ cntl = (m_db.src_alpha_ctrl >> 2) & 3;
+
+ switch (cntl)
+ {
+ case 0: m_dbstate.texpath.a = m_dbstate.ti.a; break;
+ case 1: m_dbstate.texpath.a = 255; break;
+ case 2: m_dbstate.texpath.a = 0; break;
+ }
+
+ switch ((m_db.txt_mult_cntl & DBTXTMULTCNTL_INSEL_MASK) >> DBTXTMULTCNTL_INSEL_SHIFT)
+ {
+ case DBTXTMULTCNTL_INSEL_CTI:
+ {
+ m_dbstate.texpath.r = m_dbstate.ti.r;
+ m_dbstate.texpath.g = m_dbstate.ti.g;
+ m_dbstate.texpath.b = m_dbstate.ti.b;
+ break;
+ }
+ case DBTXTMULTCNTL_INSEL_CONSTANT:
+ {
+ uint32_t cnst = m_db.const_in;
+ m_dbstate.texpath.r = (cnst >> 16) & 0xff;
+ m_dbstate.texpath.g = (cnst >> 8) & 0xff;
+ m_dbstate.texpath.b = cnst & 0xff;
+ break;
+ }
+ case DBTXTMULTCNTL_INSEL_COMPSRC:
+ {
+ m_dbstate.texpath.r = ~m_dbstate.src.r;
+ m_dbstate.texpath.g = ~m_dbstate.src.g;
+ m_dbstate.texpath.b = ~m_dbstate.src.b;
+ break;
+ }
+ case DBTXTMULTCNTL_INSEL_ATI:
+ {
+ m_dbstate.texpath.r = m_dbstate.ti.a;
+ m_dbstate.texpath.g = m_dbstate.ti.a;
+ m_dbstate.texpath.b = m_dbstate.ti.a;
+ break;
+ }
+ }
+
+ if (m_db.txt_mult_cntl & DBTXTMULTCNTL_TXTRJUST)
+ {
+ m_dbstate.texpath.r >>= 3;
+ m_dbstate.texpath.g >>= 3;
+ m_dbstate.texpath.b >>= 3;
+ }
+}
+
+void m2_te_device::select_src_pixel()
+{
+ if ((m_db.usergen_ctrl & DBUSERGENCTL_SRCINEN)
+ && (m_db.usergen_ctrl & DBUSERGENCTL_BLENDEN)
+ && !(m_gc.te_master_mode & TEMASTER_MODE_DBLEND))
+ {
+ int32_t x_offs = (m_db.src_offset & DBSRCOFFS_XOFFS_MASK) >> DBSRCOFFS_YOFFS_SHIFT;
+ int32_t y_offs = (m_db.src_offset & DBSRCOFFS_YOFFS_MASK) >> DBSRCOFFS_YOFFS_SHIFT;
+
+ // Sign extend
+ x_offs = (x_offs << 20) >> 20;
+ y_offs = (y_offs << 20) >> 20;
+
+ x_offs += m_dbstate.x;
+ y_offs += m_dbstate.y;
+
+ uint32_t addr = y_offs * m_db.src_xstride + x_offs;
+
+ if (m_db.src_ctrl & DBSRCCNTL_32BPP)
+ {
+ uint32_t srcaddr = m_db.src_baseaddr + addr * sizeof(uint32_t);
+ uint32_t srcval = m_bda->read_bus32(srcaddr);
+
+ m_dbstate.src.a = ((srcval >> 24) & 0x7f) << 1;
+ m_dbstate.src.r = (srcval >> 16) & 0xff;
+ m_dbstate.src.g = (srcval >> 8) & 0xff;
+ m_dbstate.src.b = (srcval >> 0) & 0xff;
+
+ if (m_db.src_ctrl & DBSRCCNTL_MSBREP)
+ m_dbstate.src.a |= (m_dbstate.src.a >> 6) & 1;
+
+ m_dbstate.dsb = (srcval >> 31) & 1;
+ }
+ else
+ {
+ uint32_t srcaddr = m_db.src_baseaddr + addr * sizeof(uint16_t);
+ uint32_t srcval = m_bda->read_bus16(srcaddr);
+
+ m_dbstate.src.r = (srcval >> 10) & 0x1f;
+ m_dbstate.src.g = (srcval >> 5) & 0x1f;
+ m_dbstate.src.b = srcval & 0x1f;
+ m_dbstate.src.a = 0;
+
+ m_dbstate.dsb = srcval & 0x8000;
+ m_dbstate.src.r <<= 3;
+ m_dbstate.src.g <<= 3;
+ m_dbstate.src.b <<= 3;
+
+ if (m_db.src_ctrl & DBSRCCNTL_MSBREP)
+ {
+ m_dbstate.src.r |= (m_dbstate.src.r >> 5);
+ m_dbstate.src.g |= (m_dbstate.src.g >> 5);
+ m_dbstate.src.b |= (m_dbstate.src.b >> 5);
+ }
+ }
+#if TEST_TIMING
+ g_statistics[STAT_PIXEL_LOADS]++;
+#endif
+ }
+ else
+ {
+ // Source input disabled
+ m_dbstate.dsb = 0;
+ m_dbstate.src.r = 0;
+ m_dbstate.src.g = 0;
+ m_dbstate.src.b = 0;
+ m_dbstate.src.a = 0;
+ }
+
+ // Now
+ switch ((m_db.src_mult_cntl & DBSRCMULTCNTL_INSEL_MASK) >> DBSRCMULTCNTL_INSEL_SHIFT)
+ {
+ case DBSRCMULTCNTL_INSEL_SRC:
+ {
+ m_dbstate.srcpath.r = m_dbstate.src.r;
+ m_dbstate.srcpath.g = m_dbstate.src.g;
+ m_dbstate.srcpath.b = m_dbstate.src.b;
+ break;
+ }
+ case DBSRCMULTCNTL_INSEL_CONSTANT:
+ {
+ uint32_t cnst = m_db.const_in;
+ m_dbstate.srcpath.r = (cnst >> 16) & 0xff;
+ m_dbstate.srcpath.g = (cnst >> 8) & 0xff;
+ m_dbstate.srcpath.b = (cnst >> 0) & 0xff;
+ break;
+ }
+ case DBSRCMULTCNTL_INSEL_COMPCTI:
+ {
+ m_dbstate.srcpath.r = ~m_dbstate.ti.r;
+ m_dbstate.srcpath.g = ~m_dbstate.ti.g;
+ m_dbstate.srcpath.b = ~m_dbstate.ti.b;
+ break;
+ }
+ case DBSRCMULTCNTL_INSEL_TEXALPHA:
+ {
+ m_dbstate.srcpath.r = m_dbstate.src.a;
+ m_dbstate.srcpath.g = m_dbstate.src.a;
+ m_dbstate.srcpath.b = m_dbstate.src.a;
+ break;
+ }
+ }
+
+ if (m_db.src_mult_cntl & DBSRCMULTCNTL_SRCRJUST)
+ {
+ m_dbstate.srcpath.r >>= 3;
+ m_dbstate.srcpath.g >>= 3;
+ m_dbstate.srcpath.b >>= 3;
+ }
+
+ m_dbstate.srcpath.a = m_dbstate.src.a;
+}
+
+uint8_t m2_te_device::dither(uint8_t in, uint8_t dithval)
+{
+ int32_t res;
+ int32_t sgn_val;
+
+ if (dithval & 8)
+ sgn_val = -8 + (dithval & 7);
+ else
+ sgn_val = dithval;
+
+ res = (int32_t)in + sgn_val;
+
+ if (res > 255)
+ res = 255;
+ else if (res < 0)
+ res = 0;
+
+ return (uint8_t)res;
+}
+
+uint8_t m2_te_device::get_src_coef(uint8_t cti, uint8_t dm2const0, uint8_t dm2const1)
+{
+ uint32_t sel=0;
+ uint8_t cnst, coef=0;
+
+ switch ((m_db.src_mult_cntl & DBSRCMULTCNTL_SRCCONSTCNTL_MASK) >> DBSRCMULTCNTL_SRCCONSTCNTL_SHIFT)
+ {
+ case DBSRCMULTCNTL_SRCCONSTCNTL_TEXSSB: sel = m_dbstate.ssb; break;
+ case DBSRCMULTCNTL_SRCCONSTCNTL_SRCDSB: sel = m_dbstate.dsb; break;
+ }
+
+ cnst = sel ? dm2const1 : dm2const0;
+
+ switch ((m_db.src_mult_cntl & DBSRCMULTCNTL_COEFSEL_MASK) >> DBSRCMULTCNTL_COEFSEL_SHIFT)
+ {
+ case DBSRCMULTCNTL_COEFSEL_ATI: coef = m_dbstate.texpath.a; break;
+ case DBSRCMULTCNTL_COEFSEL_ASRC: coef = m_dbstate.srcpath.a; break;
+ case DBSRCMULTCNTL_COEFSEL_CONSTANT: coef = cnst; break;
+ case DBSRCMULTCNTL_COEFSEL_CTI: coef = cti; break;
+ }
+
+ if (m_db.src_mult_cntl & DBSRCMULTCNTL_SRCCOEFCMP)
+ return ~coef;
+ else
+ return coef;
+}
+
+uint8_t m2_te_device::get_tex_coef(uint8_t cs, uint8_t dm1const0, uint8_t dm1const1)
+{
+ uint32_t sel=0;
+ uint8_t cnst, coef=0;
+
+ // TODO: Make like src coefficient
+ uint32_t cntl = ((m_db.txt_mult_cntl & DBTXTMULTCNTL_TXTCONSTCNTL_MASK) >> DBTXTMULTCNTL_TXTCONSTCNTL_SHIFT);
+
+ if (cntl == DBTXTMULTCNTL_TXTCONSTCNTL_TEXSSB)
+ sel = m_dbstate.ssb;
+ else if (cntl == DBTXTMULTCNTL_TXTCONSTCNTL_SRCDSB)
+ sel = m_dbstate.dsb;
+
+ cnst = sel ? dm1const1 : dm1const0;
+
+ switch ((m_db.txt_mult_cntl & DBTXTMULTCNTL_COEFSEL_MASK) >> DBTXTMULTCNTL_COEFSEL_SHIFT)
+ {
+ case DBTXTMULTCNTL_COEFSEL_ATI: coef = m_dbstate.texpath.a; break;
+ case DBTXTMULTCNTL_COEFSEL_ASRC: coef = m_dbstate.srcpath.a; break;
+ case DBTXTMULTCNTL_COEFSEL_CONSTANT: coef = cnst; break;
+ case DBTXTMULTCNTL_COEFSEL_CSRC: coef = cs; break;
+ }
+
+ if (m_db.txt_mult_cntl & DBTXTMULTCNTL_TXTCOEFCMP)
+ return ~coef;
+ else
+ return coef;
+}
+
+void m2_te_device::select_alpha_dsb()
+{
+ if ((m_db.usergen_ctrl & DBUSERGENCTL_BLENDEN) && !(m_gc.te_master_mode & TEMASTER_MODE_DBLEND))
+ {
+ uint32_t sel = 0;
+ uint8_t aconst;
+
+ switch ((m_db.dst_alpha_ctrl & DBDSTACNTL_ADESTCONSTCNTL_MASK) >> DBDSTACNTL_ADESTCONSTCNTL_SHIFT)
+ {
+ case 0: sel = m_dbstate.ssb; break;
+ case 1: sel = m_dbstate.dsb; break;
+ }
+
+ if (sel)
+ aconst = (m_db.dst_alpha_const & DBDSTALPHACONST_CONST1_MASK) >> DBDSTALPHACONST_CONST1_SHIFT;
+ else
+ aconst = (m_db.dst_alpha_const & DBDSTALPHACONST_CONST0_MASK) >> DBDSTALPHACONST_CONST0_SHIFT;
+
+ switch ((m_db.dst_alpha_ctrl & DBDSTACNTL_ADESTSEL_MASK) >> DBDSTACNTL_ADESTSEL_SHIFT)
+ {
+ case 0: m_dbstate.dst.a = m_dbstate.texpath.a; break;
+ case 1: m_dbstate.dst.a = aconst; break;
+ case 2: m_dbstate.dst.a = m_dbstate.srcpath.a; break;
+ case 3: m_dbstate.dst.a = m_dbstate.blend.r; break;
+ }
+
+ switch ((m_db.ssbdsb_ctrl & DBSSBDSBCNTL_DSBSEL_MASK) >> DBSSBDSBCNTL_DSBSEL_SHIFT)
+ {
+ case 0: m_dbstate.dsb = m_dbstate.ssb; break;
+ case 1: m_dbstate.dsb = (m_db.ssbdsb_ctrl & DBSSBDSBCNTL_DSBCONST_MASK) >> DBSSBDSBCNTL_DSBCONST_SHIFT; break;
+ case 2: m_dbstate.dsb = m_dbstate.dsb; break;
+ }
+ }
+ else
+ {
+ m_dbstate.dst.a = m_dbstate.texpath.a;
+ m_dbstate.dsb = m_dbstate.ssb;
+ }
+}
+
+
+uint8_t m2_te_device::color_blend(uint8_t ct, uint8_t cti, uint8_t cs, uint8_t csrc,
+ uint8_t dm10, uint8_t dm11,
+ uint8_t dm20, uint8_t dm21)
+{
+ uint8_t tcoef, scoef;
+ uint16_t tm, sm;
+
+ tcoef = get_tex_coef(csrc, dm10, dm11);
+ scoef = get_src_coef(cti, dm20, dm21);
+
+ tm = (tcoef == 255) ? ct : ((ct == 255) ? tcoef : ((tcoef * ct) >> 8));
+ sm = (scoef == 255) ? cs : ((cs == 255) ? scoef : ((scoef * cs) >> 8));
+
+ return alu_calc(tm, sm);
+}
+
+#if 1
+
+uint8_t m2_te_device::alu_calc(uint16_t a, uint16_t b)
+{
+ uint16_t cinv;
+ uint16_t c = 0;
+ uint32_t blendout;
+ uint32_t result;
+ uint32_t carry = 0;
+ uint32_t borrow = 0;
+ uint32_t cntl = (m_db.alu_ctrl & DBALUCNTL_ALUOP_MASK) >> DBALUCNTL_ALUOP_SHIFT;
+
+ // p271
+
+ /* ALU */
+ if ((cntl & 8) == 0)
+ {
+ if (!(cntl & 4))
+ {
+ result = a + b;
+ }
+ else if (cntl & 2)
+ {
+ result = b - a;
+ if (result < 0)
+ borrow = 1;
+ }
+ else
+ {
+ result = a - b;
+ if (result < 0)
+ borrow = 1;
+ }
+ }
+ /* Boolean */
+ else
+ {
+ cinv = 0;
+ int i, j;
+
+ for (i = 0; i < 8; ++i)
+ {
+ j = (a & 1) *2 + (b & 1);
+
+ switch (j)
+ {
+ case 0: cinv |= (cntl & 1); break;
+ case 1: cinv |= (cntl & 2) && 1; break;
+ case 2: cinv |= (cntl & 4) && 1; break;
+ case 3: cinv |= (cntl & 8) && 1; break;
+ }
+
+ cinv <<= 1;
+ a >>= 1;
+ b >>= 1;
+ }
+ cinv >>= 1;
+
+ for (i = 0; i < 8; ++i)
+ {
+ c |= cinv & 1;
+ c <<= 1;
+ cinv >>= 1;
+ }
+
+ c >>= 1;
+ result = (int)c;
+ }
+
+ result &= 0x1ff;
+
+ switch ((m_db.alu_ctrl & DBALUCNTL_FINALDIVIDE_MASK) >> DBALUCNTL_FINALDIVIDE_SHIFT)
+ {
+ case 1: blendout = result << 1; break;
+ case 2: blendout = result << 2; break;
+ case 3: blendout = result << 3; break;
+ case 7: blendout = result >> 1; break;
+ case 6: blendout = result >> 2; break;
+ case 5: blendout = result >> 3; break;
+ default: blendout = result; break;
+ }
+
+ if (blendout > 255)
+ carry = 1;
+
+#if 0
+ int alugel;
+ // TODO: FIX ALUGEL - Needs to be propagated to ALU status
+ if (borrow == 1)
+ alugel = 1;
+ else if (blendout == 0)
+ alugel = 2;
+ else
+ alugel = 4;
+#endif
+
+ if ((cntl & 8) == 0)
+ {
+ /* Clamp? */
+ if (!(cntl & 1))
+ {
+ blendout = carry ? 255 : blendout;
+ blendout = borrow ? 0 : blendout;
+ }
+ else
+ {
+ if (cntl & 2)
+ {
+ blendout = carry ? 255 : blendout;
+ blendout = borrow ? 0 : blendout;
+ }
+ }
+ }
+
+ return blendout & 0xff;
+}
+
+
+
+#endif
+
+//-------------------------------------------------
+// walk_span -
+//-------------------------------------------------
+
+void m2_te_device::walk_span(uint32_t wrange, bool omit_right,
+ uint32_t y, uint32_t xs, uint32_t xe,
+ int32_t r, int32_t g, int32_t b, int32_t a,
+ uint32_t uw, uint32_t vw,
+ uint32_t w)
+{
+ bool scan_lr = !m_es.r2l;
+
+ // TODO: Is this correct?
+ xe = scan_lr ? xe + 1 : xe - 1;
+
+ /*
+ Edge to Span walker
+ X/Y 11.0
+ RGBA 8.11
+ U/W, V/W 10.13
+ 1/W 0.23
+ RGBA DDX s8.11
+ UV DDX s10.13
+ 1/W DDX s0.23
+
+ To destination blender:
+ X/Y 11.0
+ W 0.16
+
+ To texture mapper:
+ RGBA 8.0
+ UV 10.4
+ */
+
+ if (omit_right)
+ {
+ if (scan_lr)
+ {
+ xe -= 1;
+ }
+ else
+ {
+ xs -= 1;
+ r -= m_es.ddx_r;
+ g -= m_es.ddx_g;
+ b -= m_es.ddx_b;
+ a -= m_es.ddx_a;
+ uw -= m_es.ddx_uw;
+ vw -= m_es.ddx_vw;
+ w -= m_es.ddx_w;
+ }
+ }
+
+ if (g_debug)
+ {
+ g_debug = true;
+ }
+
+ while (xs != xe)
+ {
+ uint32_t sx = xs;
+ uint32_t sy = y;
+
+#if 1 // DEBUG
+ if (sx == 320/2 && sy == 200)
+ {
+ ;
+ }
+#endif
+
+ // Fetch RGB and A
+ uint32_t rt, gt, bt, at, ssbt;
+
+ uint32_t w16 = 0;
+
+ // W
+ {
+ // Undo shifting that took place during setup and reduce 0.23 to 0.16
+ uint32_t normalized_w = w >> -depth_bits[wrange];
+ w16 = (normalized_w >> 7) & 0xffff;
+ }
+
+ if (!(m_gc.te_master_mode & TEMASTER_MODE_DTEXT) && (m_tm.tex_addr_cntl & TXTADDRCNTL_LOOKUP_EN))
+ {
+ uint32_t u, v;
+
+ // UV and W
+ texcoord_gen(wrange, uw, vw, w, u, v, w16);
+
+ // TODO: FIXME
+ uint32_t lod = lod_calc(u, v, u, v);
+
+ get_texture_color(u, v, lod, rt, gt, bt, at, ssbt);
+ }
+ else
+ {
+ rt = gt = bt = at = ssbt = 0;
+ }
+
+ uint32_t ri, gi, bi, ai;
+
+ if (!(m_gc.te_master_mode & TEMASTER_MODE_DSHADE))
+ {
+ // Remove the fractional parts of iterated ARGB
+ ri = r >> 11;
+ gi = g >> 11;
+ bi = b >> 11;
+ ai = a >> 11;
+ }
+ else
+ {
+ ri = 0xff;
+ gi = 0xff;
+ bi = 0xff;
+ ai = 0xff;
+ }
+
+ uint32_t ro, go, bo, ao;
+
+ // Note: SSB may be overriden
+ uint32_t ssbo = ssbt;
+
+ // Blend iterated RGB with texel
+ texture_blend(ri, gi, bi, ai,
+ rt, gt, bt, at, ssbt,
+ ro, go, bo, ao, ssbo);
+
+ // Destination blend and write-out
+ // Interface:
+ // Span length
+ // Left 2 Right
+ // W0, W1
+
+ rgba texout;
+ texout.r = ro;
+ texout.g = go;
+ texout.b = bo;
+ texout.a = ao;
+
+ destination_blend(sx, sy, w16, texout, ssbo);
+
+ // Update interpolated paramters
+ if (scan_lr)
+ {
+ xs += 1;
+ r += m_es.ddx_r;
+ g += m_es.ddx_g;
+ b += m_es.ddx_b;
+ a += m_es.ddx_a;
+ uw += m_es.ddx_uw;
+ vw += m_es.ddx_vw;
+ w += m_es.ddx_w;
+ }
+ else
+ {
+ xs -= 1;
+ r -= m_es.ddx_r;
+ g -= m_es.ddx_g;
+ b -= m_es.ddx_b;
+ a -= m_es.ddx_a;
+ uw -= m_es.ddx_uw;
+ vw -= m_es.ddx_vw;
+ w -= m_es.ddx_w;
+ }
+
+ // Clamp to 11.8
+ r = clamp(r, 0, 0x0007ffff);
+ g = clamp(g, 0, 0x0007ffff);
+ b = clamp(b, 0, 0x0007ffff);
+ a = clamp(a, 0, 0x0007ffff);
+
+ // Clamp to 10.13
+ uw = clamp(uw, 0, 0x007fffff);
+ vw = clamp(vw, 0, 0x007fffff);
+
+ // Clamp to 0.23
+ w = clamp(w, 0, 0x007fffff);
+
+#if TEST_TIMING
+ g_statistics[STAT_PIXELS_PROCESSED]++;
+#endif
+ }
+}
+
+
+
+/***************************************************************************
+ INSTRUCTION PROCESSING
+***************************************************************************/
+
+//-------------------------------------------------
+// irp_fetch -
+//-------------------------------------------------
+
+uint32_t m2_te_device::irp_fetch()
+{
+ uint32_t data = m_bda->read_bus32(m_gc.irp);
+ m_gc.irp += 4;
+
+ return data;
+}
+
+
+//-------------------------------------------------
+// irp_fetch_float -
+//-------------------------------------------------
+
+float m2_te_device::irp_fetch_float()
+{
+ uint32_t data = m_bda->read_bus32(m_gc.irp);
+ m_gc.irp += 4;
+
+ return *reinterpret_cast<float*>(&data);
+}
+
+
+//-------------------------------------------------
+// illegal_inst -
+//-------------------------------------------------
+
+void m2_te_device::illegal_inst()
+{
+ set_interrupt(INTSTAT_UNIMPLEMENTED_INSTR);
+ m_state = TE_STOPPED;
+}
+
+
+//-------------------------------------------------
+// execute -
+//-------------------------------------------------
+
+void m2_te_device::execute()
+{
+ address_space &space = machine().driver_data()->generic_space();
+
+#if TEST_TIMING
+ memset(g_statistics, 0, sizeof(g_statistics));
+#endif
+
+ while (m_state == TE_RUNNING)
+ {
+ uint32_t inst = irp_fetch();
+
+ switch (inst & INST_MASK)
+ {
+ case INST_WRITE_REG:
+ {
+ uint32_t offs = inst & 0xffff;
+ int32_t cnt = (inst >> 16) & 0xff;
+
+ while (cnt-- >= 0)
+ {
+ write(space, offs >> 2, irp_fetch(), 0xffffffff);
+ offs += 4;
+
+ if (m_state != TE_RUNNING)
+ break;
+ }
+ break;
+ }
+
+ case INST_VTX_SHORT:
+ {
+ int32_t cnt = inst & 0xffff;
+ uint32_t flags = inst & 0x001f0000;
+ uint32_t ver = (inst >> 24) & 0xf;
+
+ if (ver != 0)
+ {
+ illegal_inst();
+ break;
+ }
+
+ while (cnt-- >= 0)
+ {
+ se_vtx vtx = { 0 };
+
+ vtx.x = irp_fetch_float();
+ vtx.y = irp_fetch_float();
+
+ if (flags & VTX_FLAG_SHAD)
+ {
+ vtx.r = irp_fetch_float();
+ vtx.g = irp_fetch_float();
+ vtx.b = irp_fetch_float();
+ vtx.a = irp_fetch_float();
+ }
+ if (flags & VTX_FLAG_PRSP)
+ {
+ vtx.w = irp_fetch_float();
+ }
+ if (flags & VTX_FLAG_TEXT)
+ {
+ vtx.uw = irp_fetch_float();
+ vtx.vw = irp_fetch_float();
+ }
+
+ // Send this vertex for processing
+ add_vertex(vtx, flags);
+
+ // Clear the new triangle flag if set
+ flags &= ~VTX_FLAG_NEW;
+ }
+
+ break;
+ }
+ case INST_VTX_LONG:
+ {
+ const uint32_t ver = (inst >> 24) & 0xf;
+
+ if (ver != 0)
+ {
+ illegal_inst();
+ break;
+ }
+
+ fatalerror("Long format unimplemented");
+
+ break;
+ }
+ case INST_VTX_POINT:
+ {
+ const uint32_t ver = (inst >> 24) & 0xf;
+
+ if (ver != 0)
+ {
+ illegal_inst();
+ break;
+ }
+
+ fatalerror("Point format unimplemented");
+
+ break;
+ }
+ default:
+ {
+ illegal_inst();
+ break;
+ }
+ }
+
+ // Stop or pause execution?
+ if (m_gc.irp == m_gc.iwp)
+ {
+ m_state = m_gc.te_master_mode & TEICNTL_STPL ? TE_STOPPED : TE_PAUSED;
+ set_interrupt(INTSTAT_LIST_END);
+ }
+ else if (m_gc.te_master_mode & (TEICNTL_STPI | TEICNTL_STEP))
+ {
+ m_state = TE_STOPPED;
+ }
+ };
+
+#if TEST_TIMING
+/*
+ TESetup Engine: 600-700 triangles/sec (?)
+
+ Pixel Rates:
+ Point - 132Mpix/s
+ Linear - 66Mpix/s
+ Bilin - 33Mpix
+ QTril - 22M
+
+ No blend, no Z - 120MPix
+ Zbuffer - 66-120M pix
+ Bend - 66 M pix
+*/
+
+ uint32_t total_cycles = (g_statistics[STAT_TRIANGLES_PROCESSED] * 100) +
+ (g_statistics[STAT_TEXEL_READS]) +
+// (g_statistics[STAT_PIXELS_PROCESSED]) +
+ (g_statistics[STAT_PIXEL_LOADS]) +
+ (g_statistics[STAT_PIXEL_STORES]) +
+ (g_statistics[STAT_TEXEL_BYTES]/4) +
+ (g_statistics[STAT_ZBUFFER_LOADS]) +
+ (g_statistics[STAT_ZBUFFER_STORES]);
+#if 0
+ logerror(">>> END OF LIST <<<\n");
+ logerror("Triangles: %u\n", g_statistics[STAT_TRIANGLES_PROCESSED]);
+ logerror("Texture samples: %u\n", g_statistics[STAT_TEXEL_READS]);
+ logerror("Texture bytes loaded: %u\n", g_statistics[STAT_TEXEL_BYTES]);
+ logerror("Pixels rasterized: %u\n", g_statistics[STAT_PIXELS_PROCESSED]);
+ logerror("Pixel reads: %u\n", g_statistics[STAT_PIXEL_LOADS]);
+ logerror("Pixel writes: %u\n", g_statistics[STAT_PIXEL_STORES]);
+ logerror("Z reads: %u\n", g_statistics[STAT_ZBUFFER_LOADS]);
+ logerror("Z writes: %u\n", g_statistics[STAT_ZBUFFER_STORES]);
+ logerror("Total: %u cycles (%fusec)\n", total_cycles, clocks_to_attotime(total_cycles).as_double()*1.0e6);
+#endif
+ timer_set(clocks_to_attotime(total_cycles), 0);
+#else
+ // Interrupt after stopping?
+ if (m_gc.te_master_mode & TEICNTL_INT)
+ {
+ set_interrupt(INTSTAT_IMMEDIATE_INSTR);
+ }
+#endif
+}
+
+
+
+/***************************************************************************
+ TEXTURE RAM ACCESSORS
+***************************************************************************/
+
+uint8_t m2_te_device::read_tram8(offs_t address) const
+{
+ address &= TEXTURE_RAM_BYTEMASK;
+
+ uint8_t *ptr = reinterpret_cast<uint8_t *>(&m_tram[0]) + BYTE4_XOR_BE(address);
+ return *ptr;
+}
+
+uint16_t m2_te_device::read_tram16(offs_t address) const
+{
+ address &= TEXTURE_RAM_BYTEMASK;
+
+ uint8_t *ptr = reinterpret_cast<uint8_t *>(&m_tram[0]) + address;//WORD_XOR_BE(address);
+ return *reinterpret_cast<uint16_t *>(ptr);
+}
+
+uint32_t m2_te_device::read_tram32(offs_t address) const
+{
+ address &= TEXTURE_RAM_BYTEMASK;
+
+ uint8_t *ptr = reinterpret_cast<uint8_t *>(&m_tram[0]) + address;//DWORD_XOR_BE(address);
+ return *reinterpret_cast<uint32_t *>(ptr);
+}
+
+void m2_te_device::write_tram8(offs_t address, uint8_t data)
+{
+ assert(address <= TEXTURE_RAM_WORDS * 4);
+
+ uint8_t *ptr = reinterpret_cast<uint8_t *>(&m_tram[0]) + address;//BYTE8_XOR_BE(address);
+ *ptr = data;
+}
+
+void m2_te_device::write_tram16(offs_t address, uint16_t data)
+{
+ assert(address < TEXTURE_RAM_WORDS * 4);
+
+ uint8_t *ptr = reinterpret_cast<uint8_t *>(&m_tram[0]) + WORD2_XOR_BE(address);
+ *reinterpret_cast<uint16_t *>(ptr) = data;
+}
+
+void m2_te_device::write_tram32(offs_t address, uint32_t data)
+{
+ address &= TEXTURE_RAM_BYTEMASK;
+
+ uint8_t *ptr = reinterpret_cast<uint8_t *>(&m_tram[0]) + address;//DWORD_XOR_BE(address);
+ *reinterpret_cast<uint32_t *>(ptr) = data;
+}
+
+
+
+/***************************************************************************
+ PIP RAM ACCESSORS
+***************************************************************************/
+
+uint8_t m2_te_device::read_pipram8(offs_t address) const
+{
+ address &= PIP_RAM_BYTEMASK;
+
+ uint8_t *ptr = reinterpret_cast<uint8_t *>(&m_pipram[0]) + address;//BYTE4_XOR_BE(address);
+ return *ptr;
+}
+
+uint16_t m2_te_device::read_pipram16(offs_t address) const
+{
+ address &= PIP_RAM_BYTEMASK;
+
+ uint8_t *ptr = reinterpret_cast<uint8_t *>(&m_pipram[0]) + address;//WORD_XOR_BE(address);
+ return *reinterpret_cast<uint16_t *>(ptr);
+}
+
+uint32_t m2_te_device::read_pipram32(offs_t address) const
+{
+ address &= PIP_RAM_BYTEMASK;
+
+ uint8_t *ptr = reinterpret_cast<uint8_t *>(&m_pipram[0]) + address;//DWORD_XOR_BE(address);
+ return *reinterpret_cast<uint32_t *>(ptr);
+}
+
+void m2_te_device::write_pipram32(offs_t address, uint32_t data)
+{
+ address &= PIP_RAM_BYTEMASK;
+
+ uint8_t *ptr = reinterpret_cast<uint8_t *>(&m_pipram[0]) + address;//DWORD_XOR_BE(address);
+ *reinterpret_cast<uint32_t *>(ptr) = data;
+}
+
+
+// For extracting bit fields from RAM
+uint32_t m2_te_device::readbits_from_ram(uint32_t & src_addr, uint32_t & bit_offs, uint32_t bits)
+{
+ uint32_t src_val = m_bda->read_bus32(src_addr);
+
+ if (((bit_offs & 31) + bits) > 32)
+ {
+ uint32_t src_val2 = m_bda->read_bus32(src_addr + 4);
+
+ uint32_t r_bits = ((bit_offs & 31) + bits) - 32;
+ uint32_t l_bits = bits - r_bits;
+
+ src_val &= (1 << l_bits) - 1;
+ src_val <<= r_bits;
+
+ // Position the right hand side
+ src_val2 >>= (32 - r_bits);
+ src_val2 &= (1 << r_bits) - 1;
+
+ src_val |= src_val2;
+ }
+ else
+ {
+ src_val = src_val >> (32 - (bits + (bit_offs & 31)));
+ }
+
+ return src_val &= (1 << bits) - 1;
+}
+
+void m2_te_device::load_texture()
+{
+ switch (m_tm.texld_cntl & TXTLDCNTL_LDMODE_MASK)
+ {
+ case TXTLDCNTL_LDMODE_TEXLOAD:
+ {
+ if (m_tm.texld_cntl & TXTLDCNTL_COMPRESSED)
+ {
+ // Source address is byte aligned
+ uint32_t src_addr = m_tm.texld_srcbase;
+ uint32_t bit_offs = m_tm.texld_cntl & TXTLDCNTL_SRCBITOFFS;
+
+ // Destination address is dword-aligned
+ uint32_t dst_addr = m_tm.texld_dstbase << 3;
+ int32_t texels = m_tm.tex_bytecnt;
+
+ uint32_t dst_type = m_tm.tex_exptype;
+ uint32_t dst_cdepth = (dst_type & TXTEXPFORM_CDEPTH_MASK) >> TXTEXPFORM_CDEPTH_SHIFT;
+ uint32_t dst_adepth = (dst_type & TXTEXPFORM_ADEPTH_MASK) >> TXTEXPFORM_ADEPTH_SHIFT;
+ uint32_t dst_bits = 0;
+
+ if (dst_type & TXTEXPFORM_COLORON)
+ {
+ if (dst_type & TXTEXPFORM_LITERAL)
+ dst_bits += dst_cdepth * 3;
+ else
+ dst_bits += dst_cdepth;
+ }
+
+ if (dst_type & TXTEXPFORM_SSBON)
+ ++dst_bits;
+
+ if (dst_bits & TXTEXPFORM_ALPHAON)
+ dst_bits += dst_adepth;
+
+ while (texels > 0)
+ {
+ uint8_t cntl = readbits_from_ram(src_addr, bit_offs, 8);
+
+ uint32_t type = (cntl >> 6) & 3;
+ uint32_t cnst;
+
+ // Select constant register
+ if (type == 0)
+ cnst = m_tm.tex_srcconst0;
+ else if (type == 1)
+ cnst = m_tm.tex_srcconst1;
+ else if (type == 2)
+ cnst = m_tm.tex_srcconst2;
+ else
+ cnst = m_tm.tex_srcconst3;
+
+ // Select pixel type register
+ uint32_t src_type = type & 2 ? m_tm.tex_srctype23 : m_tm.tex_srctype01;
+
+ if (type & 1)
+ src_type = src_type >> 16;
+
+ const bool has_color = src_type & TXTEXPFORM_COLORON;
+ const bool has_alpha = src_type & TXTEXPFORM_ALPHAON;
+ const bool has_ssb = src_type & TXTEXPFORM_SSBON;
+ const bool is_transparent = src_type & TXTEXPFORM_TRANSPARENT;
+ const bool is_literal = src_type & TXTEXPFORM_LITERAL;
+
+ uint32_t cnt = (cntl & (is_transparent ? 0x3f : 0x1f)) + 1;
+
+ uint32_t src_cdepth = (src_type & TXTEXPFORM_CDEPTH_MASK) >> TXTEXPFORM_CDEPTH_SHIFT;
+ uint32_t src_adepth = (src_type & TXTEXPFORM_ADEPTH_MASK) >> TXTEXPFORM_ADEPTH_SHIFT;
+ uint32_t src_bits = 0;
+
+ if (is_literal)
+ src_cdepth *= 3;
+
+ uint32_t src_val = 0;
+
+ if (is_transparent)
+ {
+ src_val = cnst;
+ }
+ else
+ {
+ src_bits += src_cdepth;
+
+ if (has_ssb)
+ ++src_bits;
+
+ if (has_alpha)
+ src_bits += src_adepth;
+ }
+
+ // String
+ if (!is_transparent && (cntl & 0x20))
+ {
+ for (uint32_t i = 0; i < cnt; ++i)
+ {
+ uint32_t dst_val = 0;
+ uint32_t src_ssb = 0;
+ uint32_t src_color = 0;
+ uint32_t src_alpha = 0;
+ uint32_t src_val = readbits_from_ram(src_addr, bit_offs, src_bits);
+
+#if TEST_TIMING // FIXME
+ g_statistics[STAT_TEXEL_BYTES] += 4;
+#endif
+
+ if (has_color)
+ {
+ src_color = src_val & ((1 << src_cdepth) - 1);
+ src_val >>= src_cdepth;
+
+ // Indexed format - add an offset
+ if (!(src_type & TXTEXPFORM_INDEXON))
+ src_color += cnst & 0xff;
+ }
+
+ if (has_alpha)
+ {
+ if (is_transparent)
+ {
+ src_alpha = (cnst >> 24) & ((1 << src_adepth) - 1);
+ }
+ else
+ {
+ src_alpha = src_val & ((1 << src_adepth) - 1);
+ src_val >>= src_adepth;
+ }
+ }
+
+ if (has_ssb)
+ {
+ if (is_transparent)
+ src_ssb = (cnst >> 31) & 1;
+ else
+ src_ssb = src_val & 1;
+ }
+
+ // Now create the destination value
+ dst_val = src_color
+ | (src_alpha << src_cdepth)
+ | (src_ssb << (src_cdepth + src_adepth));
+
+ uint32_t oldval = m_tram[(dst_addr >> 3) >> 2];
+
+ // Clear out the old value
+ uint32_t shift = (32 - (dst_bits + (dst_addr & 31)));
+ oldval &= ~(((1 << dst_bits) - 1) << shift);
+ oldval |= dst_val << shift;
+
+ m_tram[(dst_addr >> 3) >> 2] = oldval;
+
+ //src_addr += src_bits;
+ dst_addr += dst_bits;
+ }
+ }
+ else
+ {
+ uint32_t dst_val;
+
+ if (!is_transparent)
+ {
+ // Read one texel
+ src_val = readbits_from_ram(src_addr, bit_offs, src_bits);
+
+#if TEST_TIMING // FIXME
+ g_statistics[STAT_TEXEL_BYTES] += 4;
+#endif
+
+ // Indexed format - add an offset
+ if (!is_literal)
+ src_val += cnst & 0xff;
+ }
+
+ // HACK: Bits don't match?
+ dst_val = src_val;
+
+ for (uint32_t i = 0; i < cnt; ++i)
+ {
+ uint32_t oldval = m_tram[(dst_addr >> 3) & ~3];
+
+ // Clear out the old value
+ uint32_t shift = (32 - (dst_bits + (dst_addr & 31)));
+ oldval &= ~(((1 << dst_bits) - 1) << shift);
+ oldval |= dst_val << shift;
+
+ m_tram[(dst_addr >> 3) >> 2] = oldval;
+
+ dst_addr += dst_bits;
+ }
+
+ // Copy
+ //src_addr += src_bits;
+ }
+
+ texels -= cnt;
+ // assert if negative?
+ }
+ }
+ else
+ {
+ // Regular
+ fatalerror("REGULAR TEXTURE NOT SUPPORTED\n");
+ }
+
+ break;
+ }
+
+ case TXTLDCNTL_LDMODE_MMDMA:
+ {
+ uint32_t srcaddr = m_tm.tex_mm_srcbase;
+ uint32_t dstaddr = m_tm.tex_mm_dstbase;
+ uint32_t bytes = m_tm.tex_bytecnt;
+
+ // TODO: Do TRAM and PIPRAM only allow 32-bit accesses?
+ assert((bytes & 3) == 0);
+
+ if (m_tm.tex_cntl & TXTCNTL_MMDMA_TRAM_ON)
+ {
+ // TRAM destination must be 32-bit aligned
+ assert((dstaddr & 3) == 0);
+
+ while (bytes > 0)
+ {
+#if 0
+ uint32_t data = m_bda->read_bus32(srcaddr);
+ write_tram32(dstaddr, data);
+ dstaddr += 4;
+ srcaddr += 4;
+ bytes -= 4;
+#else
+ uint32_t data = m_bda->read_bus8(srcaddr);
+ write_tram8(dstaddr, data);
+ dstaddr ++;
+ srcaddr ++;
+ bytes--;
+
+#if TEST_TIMING
+ g_statistics[STAT_TEXEL_BYTES]++;
+#endif
+
+#endif
+ }
+ }
+ else if (m_tm.tex_cntl & TXTCNTL_MMDMA_PIP_ON)
+ {
+ // TODO: Should PIP destination be 32-bit aligned?
+ assert((dstaddr & 3) == 0);
+
+ while (bytes > 0)
+ {
+ uint32_t data = m_bda->read_bus32(srcaddr);
+ write_pipram32(dstaddr, data);
+ dstaddr += 4;
+ srcaddr += 4;
+ bytes -= 4;
+
+#if TEST_TIMING
+ g_statistics[STAT_TEXEL_BYTES] += 4;
+#endif
+ }
+ }
+
+ break;
+ }
+
+ case TXTLDCNTL_LDMODE_PIPLOAD:
+ {
+ // TODO: What is the difference between this and the MMDMA?
+ uint32_t srcaddr = m_tm.texld_srcbase;
+ uint32_t dstaddr = m_tm.texld_dstbase;
+ uint32_t bytes = m_tm.tex_bytecnt;
+
+ // Assuming byte count must be aligned?
+ assert((m_tm.tex_bytecnt & 3) == 0);
+
+ while (bytes > 0)
+ {
+ uint32_t data = m_bda->read_bus32(srcaddr);
+ write_pipram32(dstaddr, data);
+ dstaddr += 4;
+ srcaddr += 4;
+ bytes -= 4;
+#if TEST_TIMING
+ g_statistics[STAT_TEXEL_BYTES] += 4;
+#endif
+ }
+
+ break;
+ }
+
+ default:
+ {
+ logerror("UNHANDLED TEXTURE LOAD!\n");
+ break;
+ }
+ }
+}
+
+
+
+/***************************************************************************
+ TIMERS
+***************************************************************************/
+
+void m2_te_device::device_timer(emu_timer &timer, device_timer_id id, int param, void *ptr)
+{
+ switch (id)
+ {
+ case 0:
+ if (m_gc.te_master_mode & TEICNTL_INT)
+ set_interrupt(INTSTAT_IMMEDIATE_INSTR);
+ break;
+ }
+
+}