// license:BSD-3-Clause // copyright-holders:Ryan Holtz /****************************************************************************** SGI/Nintendo Reality Display Processor ------------------- by Ryan Holtz based on initial C code by Ville Linde contains additional improvements from angrylion, Ziggy, Gonetz and Orkin ******************************************************************************* STATUS: Much behavior needs verification against real hardware. Many edge cases must be verified on real hardware as well. TODO: - Further re-work class structure to avoid dependencies *******************************************************************************/ #include "emu.h" #include "video/n64.h" #include "video/rdpblend.h" #include "video/rdptpipe.h" #include #define LOG_RDP_EXECUTION 0 static FILE* rdp_exec; uint32_t n64_rdp::s_special_9bit_clamptable[512]; bool n64_rdp::rdp_range_check(uint32_t addr) { if(m_misc_state.m_fb_size == 0) return false; int32_t fbcount = ((m_misc_state.m_fb_width * m_scissor.m_yl) << (m_misc_state.m_fb_size - 1)) * 3; int32_t fbaddr = m_misc_state.m_fb_address & 0x007fffff; if ((addr >= fbaddr) && (addr < (fbaddr + fbcount))) { return false; } int32_t zbcount = m_misc_state.m_fb_width * m_scissor.m_yl * 2; int32_t zbaddr = m_misc_state.m_zb_address & 0x007fffff; if ((addr >= zbaddr) && (addr < (zbaddr + zbcount))) { return false; } printf("Check failed: %08x vs. %08x-%08x, %08x-%08x (%d, %d)\n", addr, fbaddr, fbaddr + fbcount, zbaddr, zbaddr + zbcount, m_misc_state.m_fb_width, m_scissor.m_yl); fflush(stdout); return true; } /*****************************************************************************/ // The functions in this file should be moved into the parent Processor class. #include "rdpfiltr.hxx" int32_t n64_rdp::get_alpha_cvg(int32_t comb_alpha, rdp_span_aux* userdata, const rdp_poly_state &object) { int32_t temp = comb_alpha; int32_t temp2 = userdata->m_current_pix_cvg; int32_t temp3 = 0; if (object.m_other_modes.cvg_times_alpha) { temp3 = (temp * temp2) + 4; userdata->m_current_pix_cvg = (temp3 >> 8) & 0xf; } if (object.m_other_modes.alpha_cvg_select) { temp = (m_other_modes.cvg_times_alpha) ? (temp3 >> 3) : (temp2 << 5); } if (temp > 0xff) { temp = 0xff; } return temp; } /*****************************************************************************/ void n64_state::video_start() { m_rdp = auto_alloc(machine(), n64_rdp(*this, m_rdram, m_rsp_dmem)); m_rdp->set_machine(machine()); m_rdp->init_internal_state(); m_rdp->set_n64_periphs(m_rcp_periphs); m_rdp->m_blender.set_machine(machine()); m_rdp->m_blender.set_processor(m_rdp); m_rdp->m_tex_pipe.set_machine(machine()); m_rdp->m_aux_buf = make_unique_clear(EXTENT_AUX_COUNT); if (LOG_RDP_EXECUTION) { rdp_exec = fopen("rdp_execute.txt", "wt"); } } uint32_t n64_state::screen_update_n64(screen_device &screen, bitmap_rgb32 &bitmap, const rectangle &cliprect) { //uint16_t* frame_buffer = (uint16_t*)&rdram[(m_rcp_periphs->vi_origin & 0xffffff) >> 2]; //uint8_t* cvg_buffer = &m_rdp.m_hidden_bits[((m_rcp_periphs->vi_origin & 0xffffff) >> 2) >> 1]; //int32_t vibuffering = ((m_rcp_periphs->vi_control & 2) && fsaa && divot); //vibuffering = 0; // Disabled for now /* if (vibuffering && ((m_rcp_periphs->vi_control & 3) == 2)) { if (frame_buffer) { for (j=0; j < vres; j++) { for (i=0; i < hres; i++) { uint16_t pix; pix = frame_buffer[pixels ^ WORD_ADDR_XOR]; curpixel_cvg = ((pix & 1) << 2) | (cvg_buffer[pixels ^ BYTE_ADDR_XOR] & 3); // Reuse of this variable if (curpixel_cvg < 7 && i > 1 && j > 1 && i < (hres - 2) && j < (vres - 2) && fsaa) { newc = video_filter16(&frame_buffer[pixels ^ WORD_ADDR_XOR], &cvg_buffer[pixels ^ BYTE_ADDR_XOR], m_rcp_periphs->vi_width); ViBuffer[i][j] = newc; } else { newc.i.r = ((pix >> 8) & 0xf8) | (pix >> 13); newc.i.g = ((pix >> 3) & 0xf8) | ((pix >> 8) & 0x07); newc.i.b = ((pix << 2) & 0xf8) | ((pix >> 3) & 0x07); ViBuffer[i][j] = newc; } pixels++; } pixels += invisiblewidth; } } } */ m_rdp->mark_frame(); if (m_rcp_periphs->vi_blank) { bitmap.fill(0, screen.visible_area()); return 0; } m_rcp_periphs->video_update(bitmap); return 0; } WRITE_LINE_MEMBER(n64_state::screen_vblank_n64) { } void n64_periphs::video_update(bitmap_rgb32 &bitmap) { if(vi_control & 0x40) /* Interlace */ { field ^= 1; } else { field = 0; } switch(vi_control & 0x3) { case PIXEL_SIZE_16BIT: video_update16(bitmap); break; case PIXEL_SIZE_32BIT: video_update32(bitmap); break; default: //fatalerror("Unsupported framebuffer depth: m_fb_size=%d\n", m_misc_state.m_fb_size); break; } } void n64_periphs::video_update16(bitmap_rgb32 &bitmap) { //int32_t fsaa = (((n64->vi_control >> 8) & 3) < 2); //int32_t divot = (n64->vi_control >> 4) & 1; //uint32_t prev_cvg = 0; //uint32_t next_cvg = 0; //int32_t dither_filter = (n64->vi_control >> 16) & 1; //int32_t vibuffering = ((n64->vi_control & 2) && fsaa && divot); uint16_t* frame_buffer = (uint16_t*)&m_rdram[(vi_origin & 0xffffff) >> 2]; //uint32_t hb = ((n64->vi_origin & 0xffffff) >> 2) >> 1; //uint8_t* hidden_buffer = &m_hidden_bits[hb]; int32_t hdiff = (vi_hstart & 0x3ff) - ((vi_hstart >> 16) & 0x3ff); float hcoeff = ((float)(vi_xscale & 0xfff) / (1 << 10)); uint32_t hres = ((float)hdiff * hcoeff); int32_t invisiblewidth = vi_width - hres; int32_t vdiff = ((vi_vstart & 0x3ff) - ((vi_vstart >> 16) & 0x3ff)) >> 1; float vcoeff = ((float)(vi_yscale & 0xfff) / (1 << 10)); uint32_t vres = ((float)vdiff * vcoeff); if (vdiff <= 0 || hdiff <= 0) { return; } //if (hres > 640) // Needed by Top Gear Overdrive (E) //{ // invisiblewidth += (hres - 640); // hres = 640; //} if (vres > bitmap.height()) // makes Perfect Dark boot w/o crashing { vres = bitmap.height(); } uint32_t pixels = 0; if (frame_buffer) { for(int32_t j = 0; j < vres; j++) { uint32_t* d = &bitmap.pix32(j); for(int32_t i = 0; i < hres; i++) { uint16_t pix = frame_buffer[pixels ^ WORD_ADDR_XOR]; const uint8_t r = ((pix >> 8) & 0xf8) | (pix >> 13); const uint8_t g = ((pix >> 3) & 0xf8) | ((pix >> 8) & 0x07); const uint8_t b = ((pix << 2) & 0xf8) | ((pix >> 3) & 0x07); d[i] = (r << 16) | (g << 8) | b; pixels++; } pixels += invisiblewidth; } } } void n64_periphs::video_update32(bitmap_rgb32 &bitmap) { int32_t gamma = (vi_control >> 3) & 1; int32_t gamma_dither = (vi_control >> 2) & 1; //int32_t vibuffering = ((n64->vi_control & 2) && fsaa && divot); uint32_t* frame_buffer32 = (uint32_t*)&m_rdram[(vi_origin & 0xffffff) >> 2]; const int32_t hdiff = (vi_hstart & 0x3ff) - ((vi_hstart >> 16) & 0x3ff); const float hcoeff = ((float)(vi_xscale & 0xfff) / (1 << 10)); uint32_t hres = ((float)hdiff * hcoeff); int32_t invisiblewidth = vi_width - hres; const int32_t vdiff = ((vi_vstart & 0x3ff) - ((vi_vstart >> 16) & 0x3ff)) >> 1; const float vcoeff = ((float)(vi_yscale & 0xfff) / (1 << 10)); const uint32_t vres = ((float)vdiff * vcoeff); if (vdiff <= 0 || hdiff <= 0) { return; } //if (hres > 640) // Needed by Top Gear Overdrive (E) //{ // invisiblewidth += (hres - 640); // hres = 640; //} if (frame_buffer32) { for (int32_t j = 0; j < vres; j++) { uint32_t* d = &bitmap.pix32(j); for (int32_t i = 0; i < hres; i++) { uint32_t pix = *frame_buffer32++; if (gamma || gamma_dither) { int32_t r = (pix >> 24) & 0xff; int32_t g = (pix >> 16) & 0xff; int32_t b = (pix >> 8) & 0xff; int32_t dith = 0; if (gamma_dither) { dith = get_random() & 0x3f; } if (gamma) { if (gamma_dither) { r = m_gamma_dither_table[(r << 6)| dith]; g = m_gamma_dither_table[(g << 6)| dith]; b = m_gamma_dither_table[(b << 6)| dith]; } else { r = m_gamma_table[r]; g = m_gamma_table[g]; b = m_gamma_table[b]; } } else if (gamma_dither) { if (r < 255) r += (dith & 1); if (g < 255) g += (dith & 1); if (b < 255) b += (dith & 1); } pix = (r << 24) | (g << 16) | (b << 8); } d[i] = (pix >> 8); } frame_buffer32 += invisiblewidth; } } } /*****************************************************************************/ void n64_rdp::tc_div_no_perspective(int32_t ss, int32_t st, int32_t sw, int32_t* sss, int32_t* sst) { *sss = (SIGN16(ss)) & 0x1ffff; *sst = (SIGN16(st)) & 0x1ffff; } void n64_rdp::tc_div(int32_t ss, int32_t st, int32_t sw, int32_t* sss, int32_t* sst) { int32_t w_carry = 0; if ((sw & 0x8000) || !(sw & 0x7fff)) { w_carry = 1; } sw &= 0x7fff; int32_t shift; for (shift = 1; shift <= 14 && !((sw << shift) & 0x8000); shift++); shift -= 1; int32_t normout = (sw << shift) & 0x3fff; int32_t wnorm = (normout & 0xff) << 2; normout >>= 8; int32_t temppoint = m_norm_point_rom[normout]; int32_t tempslope = m_norm_slope_rom[normout]; int32_t tlu_rcp = ((-(tempslope * wnorm)) >> 10) + temppoint; int32_t sprod = SIGN16(ss) * tlu_rcp; int32_t tprod = SIGN16(st) * tlu_rcp; int32_t tempmask = ((1 << (shift + 1)) - 1) << (29 - shift); int32_t shift_value = 13 - shift; int32_t outofbounds_s = sprod & tempmask; int32_t outofbounds_t = tprod & tempmask; if (shift == 0xe) { *sss = sprod << 1; *sst = tprod << 1; } else { *sss = sprod = (sprod >> shift_value); *sst = tprod = (tprod >> shift_value); } //compute clamp flags int32_t under_s = 0; int32_t under_t = 0; int32_t over_s = 0; int32_t over_t = 0; if (outofbounds_s != tempmask && outofbounds_s != 0) { if (sprod & (1 << 29)) { under_s = 1; } else { over_s = 1; } } if (outofbounds_t != tempmask && outofbounds_t != 0) { if (tprod & (1 << 29)) { under_t = 1; } else { over_t = 1; } } over_s |= w_carry; over_t |= w_carry; *sss = (*sss & 0x1ffff) | (over_s << 18) | (under_s << 17); *sst = (*sst & 0x1ffff) | (over_t << 18) | (under_t << 17); } int32_t n64_rdp::color_combiner_equation(int32_t a, int32_t b, int32_t c, int32_t d) { a = KURT_AKELEY_SIGN9(a); b = KURT_AKELEY_SIGN9(b); c = SIGN9(c); d = KURT_AKELEY_SIGN9(d); a = (((a - b) * c) + (d << 8) + 0x80); a = SIGN17(a) >> 8; a = s_special_9bit_clamptable[a & 0x1ff]; return a; } int32_t n64_rdp::alpha_combiner_equation(int32_t a, int32_t b, int32_t c, int32_t d) { a = KURT_AKELEY_SIGN9(a); b = KURT_AKELEY_SIGN9(b); c = SIGN9(c); d = KURT_AKELEY_SIGN9(d); a = (((a - b) * c) + (d << 8) + 0x80) >> 8; a = SIGN9(a); a = s_special_9bit_clamptable[a & 0x1ff]; return a; } void n64_rdp::set_suba_input_rgb(color_t** input, int32_t code, rdp_span_aux* userdata) { switch (code & 0xf) { case 0: *input = &userdata->m_combined_color; break; case 1: *input = &userdata->m_texel0_color; break; case 2: *input = &userdata->m_texel1_color; break; case 3: *input = &userdata->m_prim_color; break; case 4: *input = &userdata->m_shade_color; break; case 5: *input = &userdata->m_env_color; break; case 6: *input = &m_one; break; case 7: *input = &userdata->m_noise_color; break; case 8: case 9: case 10: case 11: case 12: case 13: case 14: case 15: { *input = &m_zero; break; } } } void n64_rdp::set_subb_input_rgb(color_t** input, int32_t code, rdp_span_aux* userdata) { switch (code & 0xf) { case 0: *input = &userdata->m_combined_color; break; case 1: *input = &userdata->m_texel0_color; break; case 2: *input = &userdata->m_texel1_color; break; case 3: *input = &userdata->m_prim_color; break; case 4: *input = &userdata->m_shade_color; break; case 5: *input = &userdata->m_env_color; break; case 6: fatalerror("SET_SUBB_RGB_INPUT: key_center\n"); case 7: *input = &userdata->m_k4; break; case 8: case 9: case 10: case 11: case 12: case 13: case 14: case 15: { *input = &m_zero; break; } } } void n64_rdp::set_mul_input_rgb(color_t** input, int32_t code, rdp_span_aux* userdata) { switch (code & 0x1f) { case 0: *input = &userdata->m_combined_color; break; case 1: *input = &userdata->m_texel0_color; break; case 2: *input = &userdata->m_texel1_color; break; case 3: *input = &userdata->m_prim_color; break; case 4: *input = &userdata->m_shade_color; break; case 5: *input = &userdata->m_env_color; break; case 6: *input = &userdata->m_key_scale; break; case 7: *input = &userdata->m_combined_alpha; break; case 8: *input = &userdata->m_texel0_alpha; break; case 9: *input = &userdata->m_texel1_alpha; break; case 10: *input = &userdata->m_prim_alpha; break; case 11: *input = &userdata->m_shade_alpha; break; case 12: *input = &userdata->m_env_alpha; break; case 13: *input = &userdata->m_lod_fraction; break; case 14: *input = &userdata->m_prim_lod_fraction; break; case 15: *input = &userdata->m_k5; break; case 16: case 17: case 18: case 19: case 20: case 21: case 22: case 23: case 24: case 25: case 26: case 27: case 28: case 29: case 30: case 31: { *input = &m_zero; break; } } } void n64_rdp::set_add_input_rgb(color_t** input, int32_t code, rdp_span_aux* userdata) { switch (code & 0x7) { case 0: *input = &userdata->m_combined_color; break; case 1: *input = &userdata->m_texel0_color; break; case 2: *input = &userdata->m_texel1_color; break; case 3: *input = &userdata->m_prim_color; break; case 4: *input = &userdata->m_shade_color; break; case 5: *input = &userdata->m_env_color; break; case 6: *input = &m_one; break; case 7: *input = &m_zero; break; } } void n64_rdp::set_sub_input_alpha(color_t** input, int32_t code, rdp_span_aux* userdata) { switch (code & 0x7) { case 0: *input = &userdata->m_combined_alpha; break; case 1: *input = &userdata->m_texel0_alpha; break; case 2: *input = &userdata->m_texel1_alpha; break; case 3: *input = &userdata->m_prim_alpha; break; case 4: *input = &userdata->m_shade_alpha; break; case 5: *input = &userdata->m_env_alpha; break; case 6: *input = &m_one; break; case 7: *input = &m_zero; break; } } void n64_rdp::set_mul_input_alpha(color_t** input, int32_t code, rdp_span_aux* userdata) { switch (code & 0x7) { case 0: *input = &userdata->m_lod_fraction; break; case 1: *input = &userdata->m_texel0_alpha; break; case 2: *input = &userdata->m_texel1_alpha; break; case 3: *input = &userdata->m_prim_alpha; break; case 4: *input = &userdata->m_shade_alpha; break; case 5: *input = &userdata->m_env_alpha; break; case 6: *input = &userdata->m_prim_lod_fraction; break; case 7: *input = &m_zero; break; } } void n64_rdp::set_blender_input(int32_t cycle, int32_t which, color_t** input_rgb, color_t** input_a, int32_t a, int32_t b, rdp_span_aux* userdata) { switch (a & 0x3) { case 0: *input_rgb = cycle == 0 ? &userdata->m_pixel_color : &userdata->m_blended_pixel_color; break; case 1: *input_rgb = &userdata->m_memory_color; break; case 2: *input_rgb = &userdata->m_blend_color; break; case 3: *input_rgb = &userdata->m_fog_color; break; } if (which == 0) { switch (b & 0x3) { case 0: *input_a = &userdata->m_pixel_color; break; case 1: *input_a = &userdata->m_fog_color; break; case 2: *input_a = &userdata->m_shade_color; break; case 3: *input_a = &m_zero; break; } } else { switch (b & 0x3) { case 0: *input_a = &userdata->m_inv_pixel_color; break; case 1: *input_a = &userdata->m_memory_color; break; case 2: *input_a = &m_one; break; case 3: *input_a = &m_zero; break; } } } uint8_t const n64_rdp::s_bayer_matrix[16] = { /* Bayer matrix */ 0, 4, 1, 5, 6, 2, 7, 3, 1, 5, 0, 4, 7, 3, 6, 2 }; uint8_t const n64_rdp::s_magic_matrix[16] = { /* Magic square matrix */ 0, 6, 1, 7, 4, 2, 5, 3, 3, 5, 2, 4, 7, 1, 6, 0 }; z_decompress_entry_t const n64_rdp::m_z_dec_table[8] = { { 6, 0x00000 }, { 5, 0x20000 }, { 4, 0x30000 }, { 3, 0x38000 }, { 2, 0x3c000 }, { 1, 0x3e000 }, { 0, 0x3f000 }, { 0, 0x3f800 }, }; /*****************************************************************************/ void n64_rdp::z_build_com_table(void) { uint16_t altmem = 0; for(int32_t z = 0; z < 0x40000; z++) { switch((z >> 11) & 0x7f) { case 0x00: case 0x01: case 0x02: case 0x03: case 0x04: case 0x05: case 0x06: case 0x07: case 0x08: case 0x09: case 0x0a: case 0x0b: case 0x0c: case 0x0d: case 0x0e: case 0x0f: case 0x10: case 0x11: case 0x12: case 0x13: case 0x14: case 0x15: case 0x16: case 0x17: case 0x18: case 0x19: case 0x1a: case 0x1b: case 0x1c: case 0x1d: case 0x1e: case 0x1f: case 0x20: case 0x21: case 0x22: case 0x23: case 0x24: case 0x25: case 0x26: case 0x27: case 0x28: case 0x29: case 0x2a: case 0x2b: case 0x2c: case 0x2d: case 0x2e: case 0x2f: case 0x30: case 0x31: case 0x32: case 0x33: case 0x34: case 0x35: case 0x36: case 0x37: case 0x38: case 0x39: case 0x3a: case 0x3b: case 0x3c: case 0x3d: case 0x3e: case 0x3f: altmem = (z >> 4) & 0x1ffc; break; case 0x40: case 0x41: case 0x42: case 0x43: case 0x44: case 0x45: case 0x46: case 0x47: case 0x48: case 0x49: case 0x4a: case 0x4b: case 0x4c: case 0x4d: case 0x4e: case 0x4f: case 0x50: case 0x51: case 0x52: case 0x53: case 0x54: case 0x55: case 0x56: case 0x57: case 0x58: case 0x59: case 0x5a: case 0x5b: case 0x5c: case 0x5d: case 0x5e: case 0x5f: altmem = ((z >> 3) & 0x1ffc) | 0x2000; break; case 0x60: case 0x61: case 0x62: case 0x63: case 0x64: case 0x65: case 0x66: case 0x67: case 0x68: case 0x69: case 0x6a: case 0x6b: case 0x6c: case 0x6d: case 0x6e: case 0x6f: altmem = ((z >> 2) & 0x1ffc) | 0x4000; break; case 0x70: case 0x71: case 0x72: case 0x73: case 0x74: case 0x75: case 0x76: case 0x77: altmem = ((z >> 1) & 0x1ffc) | 0x6000; break; case 0x78://uncompressed z = 0x3c000 case 0x79: case 0x7a: case 0x7b: altmem = (z & 0x1ffc) | 0x8000; break; case 0x7c://uncompressed z = 0x3e000 case 0x7d: altmem = ((z << 1) & 0x1ffc) | 0xa000; break; case 0x7e://uncompressed z = 0x3f000 altmem = ((z << 2) & 0x1ffc) | 0xc000; break; case 0x7f://uncompressed z = 0x3f000 altmem = ((z << 2) & 0x1ffc) | 0xe000; break; } m_z_com_table[z] = altmem; } } void n64_rdp::precalc_cvmask_derivatives(void) { const uint8_t yarray[16] = {0, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0}; const uint8_t xarray[16] = {0, 3, 2, 2, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0}; for (int32_t i = 0; i < 0x10000; i++) { m_compressed_cvmasks[i] = (i & 1) | ((i & 4) >> 1) | ((i & 0x20) >> 3) | ((i & 0x80) >> 4) | ((i & 0x100) >> 4) | ((i & 0x400) >> 5) | ((i & 0x2000) >> 7) | ((i & 0x8000) >> 8); } for (int32_t i = 0; i < 0x100; i++) { uint16_t mask = decompress_cvmask_frombyte(i); cvarray[i].cvg = cvarray[i].cvbit = 0; cvarray[i].cvbit = (i >> 7) & 1; for (int32_t k = 0; k < 8; k++) { cvarray[i].cvg += ((i >> k) & 1); } uint16_t masky = 0; for (int32_t k = 0; k < 4; k++) { masky |= ((mask & (0xf000 >> (k << 2))) > 0) << k; } uint8_t offy = yarray[masky]; uint16_t maskx = (mask & (0xf000 >> (offy << 2))) >> ((offy ^ 3) << 2); uint8_t offx = xarray[maskx]; cvarray[i].xoff = offx; cvarray[i].yoff = offy; } } uint16_t n64_rdp::decompress_cvmask_frombyte(uint8_t x) { uint16_t y = (x & 1) | ((x & 2) << 1) | ((x & 4) << 3) | ((x & 8) << 4) | ((x & 0x10) << 4) | ((x & 0x20) << 5) | ((x & 0x40) << 7) | ((x & 0x80) << 8); return y; } void n64_rdp::lookup_cvmask_derivatives(uint32_t mask, uint8_t* offx, uint8_t* offy, rdp_span_aux* userdata) { const uint32_t index = m_compressed_cvmasks[mask]; userdata->m_current_pix_cvg = cvarray[index].cvg; userdata->m_current_cvg_bit = cvarray[index].cvbit; *offx = cvarray[index].xoff; *offy = cvarray[index].yoff; } void n64_rdp::z_store(const rdp_poly_state &object, uint32_t zcurpixel, uint32_t dzcurpixel, uint32_t z, uint32_t enc) { uint16_t zval = m_z_com_table[z & 0x3ffff]|(enc >> 2); if(zcurpixel <= MEM16_LIMIT) { ((uint16_t*)m_rdram)[zcurpixel ^ WORD_ADDR_XOR] = zval; } if(dzcurpixel <= MEM8_LIMIT) { m_hidden_bits[dzcurpixel ^ BYTE_ADDR_XOR] = enc & 3; } } int32_t n64_rdp::normalize_dzpix(int32_t sum) { if (sum & 0xc000) { return 0x8000; } if (!(sum & 0xffff)) { return 1; } for(int32_t count = 0x2000; count > 0; count >>= 1) { if (sum & count) { return(count << 1); } } return 0; } uint32_t n64_rdp::z_decompress(uint32_t zcurpixel) { return m_z_complete_dec_table[(RREADIDX16(zcurpixel) >> 2) & 0x3fff]; } uint32_t n64_rdp::dz_decompress(uint32_t zcurpixel, uint32_t dzcurpixel) { const uint16_t zval = RREADIDX16(zcurpixel); const uint8_t dzval = (((dzcurpixel) <= 0x7fffff) ? (m_hidden_bits[(dzcurpixel) ^ BYTE_ADDR_XOR]) : 0); const uint32_t dz_compressed = ((zval & 3) << 2) | (dzval & 3); return (1 << dz_compressed); } uint32_t n64_rdp::dz_compress(uint32_t value) { int32_t j = 0; for (; value > 1; j++, value >>= 1); return j; } void n64_rdp::get_dither_values(int32_t x, int32_t y, int32_t* cdith, int32_t* adith, const rdp_poly_state& object) { const int32_t dithindex = ((y & 3) << 2) | (x & 3); switch((object.m_other_modes.rgb_dither_sel << 2) | object.m_other_modes.alpha_dither_sel) { case 0: *adith = *cdith = s_magic_matrix[dithindex]; break; case 1: *cdith = s_magic_matrix[dithindex]; *adith = (~(*cdith)) & 7; break; case 2: *cdith = s_magic_matrix[dithindex]; *adith = machine().rand() & 7; break; case 3: *cdith = s_magic_matrix[dithindex]; *adith = 0; break; case 4: *adith = *cdith = s_bayer_matrix[dithindex]; break; case 5: *cdith = s_bayer_matrix[dithindex]; *adith = (~(*cdith)) & 7; break; case 6: *cdith = s_bayer_matrix[dithindex]; *adith = machine().rand() & 7; break; case 7: *cdith = s_bayer_matrix[dithindex]; *adith = 0; break; case 8: *cdith = machine().rand() & 7; *adith = s_magic_matrix[dithindex]; break; case 9: *cdith = machine().rand() & 7; *adith = (~s_magic_matrix[dithindex]) & 7; break; case 10: *cdith = machine().rand() & 7; *adith = (*cdith + 17) & 7; break; case 11: *cdith = machine().rand() & 7; *adith = 0; break; case 12: *cdith = 0; *adith = s_bayer_matrix[dithindex]; break; case 13: *cdith = 0; *adith = (~s_bayer_matrix[dithindex]) & 7; break; case 14: *cdith = 0; *adith = machine().rand() & 7; break; case 15: *adith = *cdith = 0; break; } } int32_t CLAMP(int32_t in, int32_t min, int32_t max) { if(in < min) return min; if(in > max) return max; return in; } bool n64_rdp::z_compare(uint32_t zcurpixel, uint32_t dzcurpixel, uint32_t sz, uint16_t dzpix, rdp_span_aux* userdata, const rdp_poly_state &object) { bool force_coplanar = false; sz &= 0x3ffff; uint32_t oz; uint32_t dzmem; uint32_t zval; int32_t rawdzmem; if (object.m_other_modes.z_compare_en) { oz = z_decompress(zcurpixel); dzmem = dz_decompress(zcurpixel, dzcurpixel); zval = RREADIDX16(zcurpixel); rawdzmem = ((zval & 3) << 2) | ((((dzcurpixel) <= 0x3fffff) ? (m_hidden_bits[(dzcurpixel) ^ BYTE_ADDR_XOR]) : 0) & 3); } else { oz = 0; dzmem = 1 << 0xf; zval = 0x3; rawdzmem = 0xf; } userdata->m_dzpix_enc = dz_compress(dzpix & 0xffff); userdata->m_shift_a = CLAMP(userdata->m_dzpix_enc - rawdzmem, 0, 4); userdata->m_shift_b = CLAMP(rawdzmem - userdata->m_dzpix_enc, 0, 4); int32_t precision_factor = (zval >> 13) & 0xf; if (precision_factor < 3) { int32_t dzmemmodifier = 16 >> precision_factor; if (dzmem == 0x8000) { force_coplanar = true; } dzmem <<= 1; if (dzmem <= dzmemmodifier) { dzmem = dzmemmodifier; } if (!dzmem) { dzmem = 0xffff; } } if (dzmem > 0x8000) { dzmem = 0xffff; } uint32_t dznew = (dzmem > dzpix) ? dzmem : (uint32_t)dzpix; uint32_t dznotshift = dznew; dznew <<= 3; bool farther = (sz + dznew) >= oz; bool infront = sz < oz; if (force_coplanar) { farther = true; } bool overflow = ((userdata->m_current_mem_cvg + userdata->m_current_pix_cvg) & 8) > 0; userdata->m_blend_enable = (object.m_other_modes.force_blend || (!overflow && object.m_other_modes.antialias_en && farther)) ? 1 : 0; userdata->m_pre_wrap = overflow; int32_t cvgcoeff = 0; uint32_t dzenc = 0; if (object.m_other_modes.z_mode == 1 && infront && farther && overflow) { dzenc = dz_compress(dznotshift & 0xffff); cvgcoeff = ((oz >> dzenc) - (sz >> dzenc)) & 0xf; userdata->m_current_pix_cvg = ((cvgcoeff * userdata->m_current_pix_cvg) >> 3) & 0xf; } if (!object.m_other_modes.z_compare_en) { return true; } int32_t diff = (int32_t)sz - (int32_t)dznew; bool nearer = diff <= (int32_t)oz; bool max = (oz == 0x3ffff); if (force_coplanar) { nearer = true; } switch(object.m_other_modes.z_mode) { case 0: return (max || (overflow ? infront : nearer)); case 1: return (max || (overflow ? infront : nearer)); case 2: return (infront || max); case 3: return (farther && nearer && !max); } return false; } uint32_t n64_rdp::get_log2(uint32_t lod_clamp) { if (lod_clamp < 2) { return 0; } else { for (int32_t i = 7; i > 0; i--) { if ((lod_clamp >> i) & 1) { return i; } } } return 0; } /*****************************************************************************/ uint64_t n64_rdp::read_data(uint32_t address) { if (m_status & 0x1) // XBUS_DMEM_DMA enabled { return (uint64_t(m_dmem[(address & 0xfff) / 4]) << 32) | m_dmem[((address + 4) & 0xfff) / 4]; } else { return (uint64_t(m_rdram[((address & 0xffffff) / 4)]) << 32) | m_rdram[(((address + 4) & 0xffffff) / 4)]; } } char const *const n64_rdp::s_image_format[] = { "RGBA", "YUV", "CI", "IA", "I", "???", "???", "???" }; char const *const n64_rdp::s_image_size[] = { "4-bit", "8-bit", "16-bit", "32-bit" }; int32_t const n64_rdp::s_rdp_command_length[64] = { 8, // 0x00, No Op 8, // 0x01, ??? 8, // 0x02, ??? 8, // 0x03, ??? 8, // 0x04, ??? 8, // 0x05, ??? 8, // 0x06, ??? 8, // 0x07, ??? 32, // 0x08, Non-Shaded Triangle 32+16, // 0x09, Non-Shaded, Z-Buffered Triangle 32+64, // 0x0a, Textured Triangle 32+64+16, // 0x0b, Textured, Z-Buffered Triangle 32+64, // 0x0c, Shaded Triangle 32+64+16, // 0x0d, Shaded, Z-Buffered Triangle 32+64+64, // 0x0e, Shaded+Textured Triangle 32+64+64+16,// 0x0f, Shaded+Textured, Z-Buffered Triangle 8, // 0x10, ??? 8, // 0x11, ??? 8, // 0x12, ??? 8, // 0x13, ??? 8, // 0x14, ??? 8, // 0x15, ??? 8, // 0x16, ??? 8, // 0x17, ??? 8, // 0x18, ??? 8, // 0x19, ??? 8, // 0x1a, ??? 8, // 0x1b, ??? 8, // 0x1c, ??? 8, // 0x1d, ??? 8, // 0x1e, ??? 8, // 0x1f, ??? 8, // 0x20, ??? 8, // 0x21, ??? 8, // 0x22, ??? 8, // 0x23, ??? 16, // 0x24, Texture_Rectangle 16, // 0x25, Texture_Rectangle_Flip 8, // 0x26, Sync_Load 8, // 0x27, Sync_Pipe 8, // 0x28, Sync_Tile 8, // 0x29, Sync_Full 8, // 0x2a, Set_Key_GB 8, // 0x2b, Set_Key_R 8, // 0x2c, Set_Convert 8, // 0x2d, Set_Scissor 8, // 0x2e, Set_Prim_Depth 8, // 0x2f, Set_Other_Modes 8, // 0x30, Load_TLUT 8, // 0x31, ??? 8, // 0x32, Set_Tile_Size 8, // 0x33, Load_Block 8, // 0x34, Load_Tile 8, // 0x35, Set_Tile 8, // 0x36, Fill_Rectangle 8, // 0x37, Set_Fill_Color 8, // 0x38, Set_Fog_Color 8, // 0x39, Set_Blend_Color 8, // 0x3a, Set_Prim_Color 8, // 0x3b, Set_Env_Color 8, // 0x3c, Set_Combine 8, // 0x3d, Set_Texture_Image 8, // 0x3e, Set_Mask_Image 8 // 0x3f, Set_Color_Image }; void n64_rdp::disassemble(char* buffer) { char sl[32], tl[32], sh[32], th[32]; char s[32], t[32], w[32]; char dsdx[32], dtdx[32], dwdx[32]; char dsdy[32], dtdy[32], dwdy[32]; char dsde[32], dtde[32], dwde[32]; char yl[32], yh[32], ym[32], xl[32], xh[32], xm[32]; char dxldy[32], dxhdy[32], dxmdy[32]; char rt[32], gt[32], bt[32], at[32]; char drdx[32], dgdx[32], dbdx[32], dadx[32]; char drdy[32], dgdy[32], dbdy[32], dady[32]; char drde[32], dgde[32], dbde[32], dade[32]; uint64_t cmd[32]; const uint32_t length = m_cmd_ptr * 8; if (length < 8) { sprintf(buffer, "ERROR: length = %d\n", length); return; } cmd[0] = m_cmd_data[m_cmd_cur]; const int32_t tile = (cmd[0] >> 56) & 0x7; sprintf(sl, "%4.2f", (float)((cmd[0] >> 44) & 0xfff) / 4.0f); sprintf(tl, "%4.2f", (float)((cmd[0] >> 32) & 0xfff) / 4.0f); sprintf(sh, "%4.2f", (float)((cmd[0] >> 12) & 0xfff) / 4.0f); sprintf(th, "%4.2f", (float)((cmd[0] >> 0) & 0xfff) / 4.0f); const char* format = s_image_format[(cmd[0] >> 53) & 0x7]; const char* size = s_image_size[(cmd[0] >> 51) & 0x3]; const uint32_t r = (cmd[0] >> 24) & 0xff; const uint32_t g = (cmd[0] >> 16) & 0xff; const uint32_t b = (cmd[0] >> 8) & 0xff; const uint32_t a = (cmd[0] >> 0) & 0xff; const uint32_t command = (cmd[0] >> 56) & 0x3f; switch (command) { case 0x00: sprintf(buffer, "No Op"); break; case 0x08: // Tri_NoShade { const int32_t lft = (cmd[0] >> 55) & 0x1; if (length != s_rdp_command_length[command]) { sprintf(buffer, "ERROR: Tri_NoShade length = %d\n", length); return; } cmd[1] = m_cmd_data[m_cmd_cur+1]; cmd[2] = m_cmd_data[m_cmd_cur+2]; cmd[3] = m_cmd_data[m_cmd_cur+3]; sprintf(yl, "%4.4f", (float)((cmd[0] >> 32) & 0x1fff) / 4.0f); sprintf(ym, "%4.4f", (float)((cmd[0] >> 16) & 0x1fff) / 4.0f); sprintf(yh, "%4.4f", (float)((cmd[0] >> 0) & 0x1fff) / 4.0f); sprintf(xl, "%4.4f", (float)int32_t(cmd[1] >> 32) / 65536.0f); sprintf(dxldy, "%4.4f", (float)int32_t(cmd[1]) / 65536.0f); sprintf(xh, "%4.4f", (float)int32_t(cmd[2] >> 32) / 65536.0f); sprintf(dxhdy, "%4.4f", (float)int32_t(cmd[2]) / 65536.0f); sprintf(xm, "%4.4f", (float)int32_t(cmd[3] >> 32) / 65536.0f); sprintf(dxmdy, "%4.4f", (float)int32_t(cmd[3]) / 65536.0f); sprintf(buffer, "Tri_NoShade %d, XL: %s, XM: %s, XH: %s, YL: %s, YM: %s, YH: %s\n", lft, xl,xm,xh,yl,ym,yh); break; } case 0x09: // Tri_NoShadeZ { const int32_t lft = (cmd[0] >> 55) & 0x1; if (length != s_rdp_command_length[command]) { sprintf(buffer, "ERROR: Tri_NoShadeZ length = %d\n", length); return; } cmd[1] = m_cmd_data[m_cmd_cur+1]; cmd[2] = m_cmd_data[m_cmd_cur+2]; cmd[3] = m_cmd_data[m_cmd_cur+3]; sprintf(yl, "%4.4f", (float)((cmd[0] >> 32) & 0x1fff) / 4.0f); sprintf(ym, "%4.4f", (float)((cmd[0] >> 16) & 0x1fff) / 4.0f); sprintf(yh, "%4.4f", (float)((cmd[0] >> 0) & 0x1fff) / 4.0f); sprintf(xl, "%4.4f", (float)int32_t(cmd[1] >> 32) / 65536.0f); sprintf(dxldy, "%4.4f", (float)int32_t(cmd[1]) / 65536.0f); sprintf(xh, "%4.4f", (float)int32_t(cmd[2] >> 32) / 65536.0f); sprintf(dxhdy, "%4.4f", (float)int32_t(cmd[2]) / 65536.0f); sprintf(xm, "%4.4f", (float)int32_t(cmd[3] >> 32) / 65536.0f); sprintf(dxmdy, "%4.4f", (float)int32_t(cmd[3]) / 65536.0f); sprintf(buffer, "Tri_NoShadeZ %d, XL: %s, XM: %s, XH: %s, YL: %s, YM: %s, YH: %s\n", lft, xl,xm,xh,yl,ym,yh); break; } case 0x0a: // Tri_Tex { const int32_t lft = (cmd[0] >> 55) & 0x1; if (length < s_rdp_command_length[command]) { sprintf(buffer, "ERROR: Tri_Tex length = %d\n", length); return; } for (int32_t i = 1; i < 12; i++) { cmd[i] = m_cmd_data[m_cmd_cur+i]; } sprintf(yl, "%4.4f", (float)((cmd[0] >> 32) & 0x1fff) / 4.0f); sprintf(ym, "%4.4f", (float)((cmd[0] >> 16) & 0x1fff) / 4.0f); sprintf(yh, "%4.4f", (float)((cmd[0] >> 0) & 0x1fff) / 4.0f); sprintf(xl, "%4.4f", (float)int32_t(cmd[1] >> 32) / 65536.0f); sprintf(dxldy, "%4.4f", (float)int32_t(cmd[1]) / 65536.0f); sprintf(xh, "%4.4f", (float)int32_t(cmd[2] >> 32) / 65536.0f); sprintf(dxhdy, "%4.4f", (float)int32_t(cmd[2]) / 65536.0f); sprintf(xm, "%4.4f", (float)int32_t(cmd[3] >> 32) / 65536.0f); sprintf(dxmdy, "%4.4f", (float)int32_t(cmd[3]) / 65536.0f); sprintf(s, "%4.4f", (float)int32_t( ((cmd[4] >> 32) & 0xffff0000) | ((cmd[ 6] >> 48) & 0xffff)) / 65536.0f); sprintf(t, "%4.4f", (float)int32_t((((cmd[4] >> 32) & 0x0000ffff) << 16) | ((cmd[ 6] >> 32) & 0xffff)) / 65536.0f); sprintf(w, "%4.4f", (float)int32_t( (cmd[4] & 0xffff0000) | ((cmd[ 6] >> 16) & 0xffff)) / 65536.0f); sprintf(dsdx, "%4.4f", (float)int32_t( ((cmd[5] >> 32) & 0xffff0000) | ((cmd[ 7] >> 48) & 0xffff)) / 65536.0f); sprintf(dtdx, "%4.4f", (float)int32_t((((cmd[5] >> 32) & 0x0000ffff) << 16) | ((cmd[ 7] >> 32) & 0xffff)) / 65536.0f); sprintf(dwdx, "%4.4f", (float)int32_t( (cmd[5] & 0xffff0000) | ((cmd[ 7] >> 16) & 0xffff)) / 65536.0f); sprintf(dsde, "%4.4f", (float)int32_t( ((cmd[8] >> 32) & 0xffff0000) | ((cmd[10] >> 48) & 0xffff)) / 65536.0f); sprintf(dtde, "%4.4f", (float)int32_t((((cmd[8] >> 32) & 0x0000ffff) << 16) | ((cmd[10] >> 32) & 0xffff)) / 65536.0f); sprintf(dwde, "%4.4f", (float)int32_t( (cmd[8] & 0xffff0000) | ((cmd[10] >> 16) & 0xffff)) / 65536.0f); sprintf(dsdy, "%4.4f", (float)int32_t( ((cmd[9] >> 32) & 0xffff0000) | ((cmd[11] >> 48) & 0xffff)) / 65536.0f); sprintf(dtdy, "%4.4f", (float)int32_t((((cmd[9] >> 32) & 0x0000ffff) << 16) | ((cmd[11] >> 32) & 0xffff)) / 65536.0f); sprintf(dwdy, "%4.4f", (float)int32_t( (cmd[9] & 0xffff0000) | ((cmd[11] >> 16) & 0xffff)) / 65536.0f); buffer+=sprintf(buffer, "Tri_Tex %d, XL: %s, XM: %s, XH: %s, YL: %s, YM: %s, YH: %s\n", lft, xl,xm,xh,yl,ym,yh); buffer+=sprintf(buffer, " "); buffer+=sprintf(buffer, " S: %s, T: %s, W: %s\n", s, t, w); buffer+=sprintf(buffer, " "); buffer+=sprintf(buffer, " DSDX: %s, DTDX: %s, DWDX: %s\n", dsdx, dtdx, dwdx); buffer+=sprintf(buffer, " "); buffer+=sprintf(buffer, " DSDE: %s, DTDE: %s, DWDE: %s\n", dsde, dtde, dwde); buffer+=sprintf(buffer, " "); buffer+=sprintf(buffer, " DSDY: %s, DTDY: %s, DWDY: %s\n", dsdy, dtdy, dwdy); break; } case 0x0b: // Tri_TexZ { const int32_t lft = (cmd[0] >> 55) & 0x1; if (length < s_rdp_command_length[command]) { sprintf(buffer, "ERROR: Tri_TexZ length = %d\n", length); return; } for (int32_t i = 1; i < 12; i++) { cmd[i] = m_cmd_data[m_cmd_cur+i]; } sprintf(yl, "%4.4f", (float)((cmd[0] >> 32) & 0x1fff) / 4.0f); sprintf(ym, "%4.4f", (float)((cmd[0] >> 16) & 0x1fff) / 4.0f); sprintf(yh, "%4.4f", (float)((cmd[0] >> 0) & 0x1fff) / 4.0f); sprintf(xl, "%4.4f", (float)int32_t(cmd[1] >> 32) / 65536.0f); sprintf(dxldy, "%4.4f", (float)int32_t(cmd[1]) / 65536.0f); sprintf(xh, "%4.4f", (float)int32_t(cmd[2] >> 32) / 65536.0f); sprintf(dxhdy, "%4.4f", (float)int32_t(cmd[2]) / 65536.0f); sprintf(xm, "%4.4f", (float)int32_t(cmd[3] >> 32) / 65536.0f); sprintf(dxmdy, "%4.4f", (float)int32_t(cmd[3]) / 65536.0f); sprintf(s, "%4.4f", (float)int32_t( ((cmd[4] >> 32) & 0xffff0000) | ((cmd[ 6] >> 48) & 0xffff)) / 65536.0f); sprintf(t, "%4.4f", (float)int32_t((((cmd[4] >> 32) & 0x0000ffff) << 16) | ((cmd[ 6] >> 32) & 0xffff)) / 65536.0f); sprintf(w, "%4.4f", (float)int32_t( (cmd[4] & 0xffff0000) | ((cmd[ 6] >> 16) & 0xffff)) / 65536.0f); sprintf(dsdx, "%4.4f", (float)int32_t( ((cmd[5] >> 32) & 0xffff0000) | ((cmd[ 7] >> 48) & 0xffff)) / 65536.0f); sprintf(dtdx, "%4.4f", (float)int32_t((((cmd[5] >> 32) & 0x0000ffff) << 16) | ((cmd[ 7] >> 32) & 0xffff)) / 65536.0f); sprintf(dwdx, "%4.4f", (float)int32_t( (cmd[5] & 0xffff0000) | ((cmd[ 7] >> 16) & 0xffff)) / 65536.0f); sprintf(dsde, "%4.4f", (float)int32_t( ((cmd[8] >> 32) & 0xffff0000) | ((cmd[10] >> 48) & 0xffff)) / 65536.0f); sprintf(dtde, "%4.4f", (float)int32_t((((cmd[8] >> 32) & 0x0000ffff) << 16) | ((cmd[10] >> 32) & 0xffff)) / 65536.0f); sprintf(dwde, "%4.4f", (float)int32_t( (cmd[8] & 0xffff0000) | ((cmd[10] >> 16) & 0xffff)) / 65536.0f); sprintf(dsdy, "%4.4f", (float)int32_t( ((cmd[9] >> 32) & 0xffff0000) | ((cmd[11] >> 48) & 0xffff)) / 65536.0f); sprintf(dtdy, "%4.4f", (float)int32_t((((cmd[9] >> 32) & 0x0000ffff) << 16) | ((cmd[11] >> 32) & 0xffff)) / 65536.0f); sprintf(dwdy, "%4.4f", (float)int32_t( (cmd[9] & 0xffff0000) | ((cmd[11] >> 16) & 0xffff)) / 65536.0f); buffer+=sprintf(buffer, "Tri_TexZ %d, XL: %s, XM: %s, XH: %s, YL: %s, YM: %s, YH: %s\n", lft, xl,xm,xh,yl,ym,yh); buffer+=sprintf(buffer, " "); buffer+=sprintf(buffer, " S: %s, T: %s, W: %s\n", s, t, w); buffer+=sprintf(buffer, " "); buffer+=sprintf(buffer, " DSDX: %s, DTDX: %s, DWDX: %s\n", dsdx, dtdx, dwdx); buffer+=sprintf(buffer, " "); buffer+=sprintf(buffer, " DSDE: %s, DTDE: %s, DWDE: %s\n", dsde, dtde, dwde); buffer+=sprintf(buffer, " "); buffer+=sprintf(buffer, " DSDY: %s, DTDY: %s, DWDY: %s\n", dsdy, dtdy, dwdy); break; } case 0x0c: // Tri_Shade { const int32_t lft = (command >> 23) & 0x1; if (length != s_rdp_command_length[command]) { sprintf(buffer, "ERROR: Tri_Shade length = %d\n", length); return; } for (int32_t i = 1; i < 12; i++) { cmd[i] = m_cmd_data[i]; } sprintf(yl, "%4.4f", (float)((cmd[0] >> 32) & 0x1fff) / 4.0f); sprintf(ym, "%4.4f", (float)((cmd[0] >> 16) & 0x1fff) / 4.0f); sprintf(yh, "%4.4f", (float)((cmd[0] >> 0) & 0x1fff) / 4.0f); sprintf(xl, "%4.4f", (float)int32_t(cmd[1] >> 32) / 65536.0f); sprintf(dxldy, "%4.4f", (float)int32_t(cmd[1]) / 65536.0f); sprintf(xh, "%4.4f", (float)int32_t(cmd[2] >> 32) / 65536.0f); sprintf(dxhdy, "%4.4f", (float)int32_t(cmd[2]) / 65536.0f); sprintf(xm, "%4.4f", (float)int32_t(cmd[3] >> 32) / 65536.0f); sprintf(dxmdy, "%4.4f", (float)int32_t(cmd[3]) / 65536.0f); sprintf(rt, "%4.4f", (float)int32_t( ((cmd[4] >> 32) & 0xffff0000) | ((cmd[ 6] >> 48) & 0xffff)) / 65536.0f); sprintf(gt, "%4.4f", (float)int32_t((((cmd[4] >> 32) & 0x0000ffff) << 16) | ((cmd[ 6] >> 32) & 0xffff)) / 65536.0f); sprintf(bt, "%4.4f", (float)int32_t( (cmd[4] & 0xffff0000) | ((cmd[ 6] >> 16) & 0xffff)) / 65536.0f); sprintf(at, "%4.4f", (float)int32_t( ((cmd[4] & 0x0000ffff) << 16) | ( cmd[ 6] & 0xffff)) / 65536.0f); sprintf(drdx, "%4.4f", (float)int32_t( ((cmd[5] >> 32) & 0xffff0000) | ((cmd[ 7] >> 48) & 0xffff)) / 65536.0f); sprintf(dgdx, "%4.4f", (float)int32_t((((cmd[5] >> 32) & 0x0000ffff) << 16) | ((cmd[ 7] >> 32) & 0xffff)) / 65536.0f); sprintf(dbdx, "%4.4f", (float)int32_t( (cmd[5] & 0xffff0000) | ((cmd[ 7] >> 16) & 0xffff)) / 65536.0f); sprintf(dadx, "%4.4f", (float)int32_t( ((cmd[5] & 0x0000ffff) << 16) | ( cmd[ 7] & 0xffff)) / 65536.0f); sprintf(drde, "%4.4f", (float)int32_t( ((cmd[8] >> 32) & 0xffff0000) | ((cmd[10] >> 48) & 0xffff)) / 65536.0f); sprintf(dgde, "%4.4f", (float)int32_t((((cmd[8] >> 32) & 0x0000ffff) << 16) | ((cmd[10] >> 32) & 0xffff)) / 65536.0f); sprintf(dbde, "%4.4f", (float)int32_t( (cmd[8] & 0xffff0000) | ((cmd[10] >> 16) & 0xffff)) / 65536.0f); sprintf(dade, "%4.4f", (float)int32_t( ((cmd[8] & 0x0000ffff) << 16) | ( cmd[10] & 0xffff)) / 65536.0f); sprintf(drdy, "%4.4f", (float)int32_t( ((cmd[9] >> 32) & 0xffff0000) | ((cmd[11] >> 48) & 0xffff)) / 65536.0f); sprintf(dgdy, "%4.4f", (float)int32_t((((cmd[9] >> 32) & 0x0000ffff) << 16) | ((cmd[11] >> 32) & 0xffff)) / 65536.0f); sprintf(dbdy, "%4.4f", (float)int32_t( (cmd[9] & 0xffff0000) | ((cmd[11] >> 16) & 0xffff)) / 65536.0f); sprintf(dady, "%4.4f", (float)int32_t( ((cmd[9] & 0x0000ffff) << 16) | ( cmd[11] & 0xffff)) / 65536.0f); buffer+=sprintf(buffer, "Tri_Shade %d, XL: %s, XM: %s, XH: %s, YL: %s, YM: %s, YH: %s\n", lft, xl,xm,xh,yl,ym,yh); buffer+=sprintf(buffer, " "); buffer+=sprintf(buffer, " R: %s, G: %s, B: %s, A: %s\n", rt, gt, bt, at); buffer+=sprintf(buffer, " "); buffer+=sprintf(buffer, " DRDX: %s, DGDX: %s, DBDX: %s, DADX: %s\n", drdx, dgdx, dbdx, dadx); buffer+=sprintf(buffer, " "); buffer+=sprintf(buffer, " DRDE: %s, DGDE: %s, DBDE: %s, DADE: %s\n", drde, dgde, dbde, dade); buffer+=sprintf(buffer, " "); buffer+=sprintf(buffer, " DRDY: %s, DGDY: %s, DBDY: %s, DADY: %s\n", drdy, dgdy, dbdy, dady); break; } case 0x0d: // Tri_ShadeZ { const int32_t lft = (command >> 23) & 0x1; if (length != s_rdp_command_length[command]) { sprintf(buffer, "ERROR: Tri_ShadeZ length = %d\n", length); return; } for (int32_t i = 1; i < 12; i++) { cmd[i] = m_cmd_data[i]; } sprintf(yl, "%4.4f", (float)((cmd[0] >> 32) & 0x1fff) / 4.0f); sprintf(ym, "%4.4f", (float)((cmd[0] >> 16) & 0x1fff) / 4.0f); sprintf(yh, "%4.4f", (float)((cmd[0] >> 0) & 0x1fff) / 4.0f); sprintf(xl, "%4.4f", (float)int32_t(cmd[1] >> 32) / 65536.0f); sprintf(dxldy, "%4.4f", (float)int32_t(cmd[1]) / 65536.0f); sprintf(xh, "%4.4f", (float)int32_t(cmd[2] >> 32) / 65536.0f); sprintf(dxhdy, "%4.4f", (float)int32_t(cmd[2]) / 65536.0f); sprintf(xm, "%4.4f", (float)int32_t(cmd[3] >> 32) / 65536.0f); sprintf(dxmdy, "%4.4f", (float)int32_t(cmd[3]) / 65536.0f); sprintf(rt, "%4.4f", (float)int32_t( ((cmd[4] >> 32) & 0xffff0000) | ((cmd[ 6] >> 48) & 0xffff)) / 65536.0f); sprintf(gt, "%4.4f", (float)int32_t((((cmd[4] >> 32) & 0x0000ffff) << 16) | ((cmd[ 6] >> 32) & 0xffff)) / 65536.0f); sprintf(bt, "%4.4f", (float)int32_t( (cmd[4] & 0xffff0000) | ((cmd[ 6] >> 16) & 0xffff)) / 65536.0f); sprintf(at, "%4.4f", (float)int32_t( ((cmd[4] & 0x0000ffff) << 16) | ( cmd[ 6] & 0xffff)) / 65536.0f); sprintf(drdx, "%4.4f", (float)int32_t( ((cmd[5] >> 32) & 0xffff0000) | ((cmd[ 7] >> 48) & 0xffff)) / 65536.0f); sprintf(dgdx, "%4.4f", (float)int32_t((((cmd[5] >> 32) & 0x0000ffff) << 16) | ((cmd[ 7] >> 32) & 0xffff)) / 65536.0f); sprintf(dbdx, "%4.4f", (float)int32_t( (cmd[5] & 0xffff0000) | ((cmd[ 7] >> 16) & 0xffff)) / 65536.0f); sprintf(dadx, "%4.4f", (float)int32_t( ((cmd[5] & 0x0000ffff) << 16) | ( cmd[ 7] & 0xffff)) / 65536.0f); sprintf(drde, "%4.4f", (float)int32_t( ((cmd[8] >> 32) & 0xffff0000) | ((cmd[10] >> 48) & 0xffff)) / 65536.0f); sprintf(dgde, "%4.4f", (float)int32_t((((cmd[8] >> 32) & 0x0000ffff) << 16) | ((cmd[10] >> 32) & 0xffff)) / 65536.0f); sprintf(dbde, "%4.4f", (float)int32_t( (cmd[8] & 0xffff0000) | ((cmd[10] >> 16) & 0xffff)) / 65536.0f); sprintf(dade, "%4.4f", (float)int32_t( ((cmd[8] & 0x0000ffff) << 16) | ( cmd[10] & 0xffff)) / 65536.0f); sprintf(drdy, "%4.4f", (float)int32_t( ((cmd[9] >> 32) & 0xffff0000) | ((cmd[11] >> 48) & 0xffff)) / 65536.0f); sprintf(dgdy, "%4.4f", (float)int32_t((((cmd[9] >> 32) & 0x0000ffff) << 16) | ((cmd[11] >> 32) & 0xffff)) / 65536.0f); sprintf(dbdy, "%4.4f", (float)int32_t( (cmd[9] & 0xffff0000) | ((cmd[11] >> 16) & 0xffff)) / 65536.0f); sprintf(dady, "%4.4f", (float)int32_t( ((cmd[9] & 0x0000ffff) << 16) | ( cmd[11] & 0xffff)) / 65536.0f); buffer+=sprintf(buffer, "Tri_ShadeZ %d, XL: %s, XM: %s, XH: %s, YL: %s, YM: %s, YH: %s\n", lft, xl,xm,xh,yl,ym,yh); buffer+=sprintf(buffer, " "); buffer+=sprintf(buffer, " R: %s, G: %s, B: %s, A: %s\n", rt, gt, bt, at); buffer+=sprintf(buffer, " "); buffer+=sprintf(buffer, " DRDX: %s, DGDX: %s, DBDX: %s, DADX: %s\n", drdx, dgdx, dbdx, dadx); buffer+=sprintf(buffer, " "); buffer+=sprintf(buffer, " DRDE: %s, DGDE: %s, DBDE: %s, DADE: %s\n", drde, dgde, dbde, dade); buffer+=sprintf(buffer, " "); buffer+=sprintf(buffer, " DRDY: %s, DGDY: %s, DBDY: %s, DADY: %s\n", drdy, dgdy, dbdy, dady); break; } case 0x0e: // Tri_TexShade { const int32_t lft = (command >> 23) & 0x1; if (length < s_rdp_command_length[command]) { sprintf(buffer, "ERROR: Tri_TexShade length = %d\n", length); return; } for (int32_t i = 1; i < 20; i++) { cmd[i] = m_cmd_data[m_cmd_cur+i]; } sprintf(yl, "%4.4f", (float)((cmd[0] >> 32) & 0x1fff) / 4.0f); sprintf(ym, "%4.4f", (float)((cmd[0] >> 16) & 0x1fff) / 4.0f); sprintf(yh, "%4.4f", (float)((cmd[0] >> 0) & 0x1fff) / 4.0f); sprintf(xl, "%4.4f", (float)int32_t(cmd[1] >> 32) / 65536.0f); sprintf(dxldy, "%4.4f", (float)int32_t(cmd[1]) / 65536.0f); sprintf(xh, "%4.4f", (float)int32_t(cmd[2] >> 32) / 65536.0f); sprintf(dxhdy, "%4.4f", (float)int32_t(cmd[2]) / 65536.0f); sprintf(xm, "%4.4f", (float)int32_t(cmd[3] >> 32) / 65536.0f); sprintf(dxmdy, "%4.4f", (float)int32_t(cmd[3]) / 65536.0f); sprintf(rt, "%4.4f", (float)int32_t( ((cmd[4] >> 32) & 0xffff0000) | ((cmd[ 6] >> 48) & 0xffff)) / 65536.0f); sprintf(gt, "%4.4f", (float)int32_t((((cmd[4] >> 32) & 0x0000ffff) << 16) | ((cmd[ 6] >> 32) & 0xffff)) / 65536.0f); sprintf(bt, "%4.4f", (float)int32_t( (cmd[4] & 0xffff0000) | ((cmd[ 6] >> 16) & 0xffff)) / 65536.0f); sprintf(at, "%4.4f", (float)int32_t( ((cmd[4] & 0x0000ffff) << 16) | ( cmd[ 6] & 0xffff)) / 65536.0f); sprintf(drdx, "%4.4f", (float)int32_t( ((cmd[5] >> 32) & 0xffff0000) | ((cmd[ 7] >> 48) & 0xffff)) / 65536.0f); sprintf(dgdx, "%4.4f", (float)int32_t((((cmd[5] >> 32) & 0x0000ffff) << 16) | ((cmd[ 7] >> 32) & 0xffff)) / 65536.0f); sprintf(dbdx, "%4.4f", (float)int32_t( (cmd[5] & 0xffff0000) | ((cmd[ 7] >> 16) & 0xffff)) / 65536.0f); sprintf(dadx, "%4.4f", (float)int32_t( ((cmd[5] & 0x0000ffff) << 16) | ( cmd[ 7] & 0xffff)) / 65536.0f); sprintf(drde, "%4.4f", (float)int32_t( ((cmd[8] >> 32) & 0xffff0000) | ((cmd[10] >> 48) & 0xffff)) / 65536.0f); sprintf(dgde, "%4.4f", (float)int32_t((((cmd[8] >> 32) & 0x0000ffff) << 16) | ((cmd[10] >> 32) & 0xffff)) / 65536.0f); sprintf(dbde, "%4.4f", (float)int32_t( (cmd[8] & 0xffff0000) | ((cmd[10] >> 16) & 0xffff)) / 65536.0f); sprintf(dade, "%4.4f", (float)int32_t( ((cmd[8] & 0x0000ffff) << 16) | ( cmd[10] & 0xffff)) / 65536.0f); sprintf(drdy, "%4.4f", (float)int32_t( ((cmd[9] >> 32) & 0xffff0000) | ((cmd[11] >> 48) & 0xffff)) / 65536.0f); sprintf(dgdy, "%4.4f", (float)int32_t((((cmd[9] >> 32) & 0x0000ffff) << 16) | ((cmd[11] >> 32) & 0xffff)) / 65536.0f); sprintf(dbdy, "%4.4f", (float)int32_t( (cmd[9] & 0xffff0000) | ((cmd[11] >> 16) & 0xffff)) / 65536.0f); sprintf(dady, "%4.4f", (float)int32_t( ((cmd[9] & 0x0000ffff) << 16) | ( cmd[11] & 0xffff)) / 65536.0f); sprintf(s, "%4.4f", (float)int32_t( ((cmd[4] >> 32) & 0xffff0000) | ((cmd[ 6] >> 48) & 0xffff)) / 65536.0f); sprintf(t, "%4.4f", (float)int32_t((((cmd[4] >> 32) & 0x0000ffff) << 16) | ((cmd[ 6] >> 32) & 0xffff)) / 65536.0f); sprintf(w, "%4.4f", (float)int32_t( (cmd[4] & 0xffff0000) | ((cmd[ 6] >> 16) & 0xffff)) / 65536.0f); sprintf(dsdx, "%4.4f", (float)int32_t( ((cmd[5] >> 32) & 0xffff0000) | ((cmd[ 7] >> 48) & 0xffff)) / 65536.0f); sprintf(dtdx, "%4.4f", (float)int32_t((((cmd[5] >> 32) & 0x0000ffff) << 16) | ((cmd[ 7] >> 32) & 0xffff)) / 65536.0f); sprintf(dwdx, "%4.4f", (float)int32_t( (cmd[5] & 0xffff0000) | ((cmd[ 7] >> 16) & 0xffff)) / 65536.0f); sprintf(dsde, "%4.4f", (float)int32_t( ((cmd[8] >> 32) & 0xffff0000) | ((cmd[10] >> 48) & 0xffff)) / 65536.0f); sprintf(dtde, "%4.4f", (float)int32_t((((cmd[8] >> 32) & 0x0000ffff) << 16) | ((cmd[10] >> 32) & 0xffff)) / 65536.0f); sprintf(dwde, "%4.4f", (float)int32_t( (cmd[8] & 0xffff0000) | ((cmd[10] >> 16) & 0xffff)) / 65536.0f); sprintf(dsdy, "%4.4f", (float)int32_t( ((cmd[9] >> 32) & 0xffff0000) | ((cmd[11] >> 48) & 0xffff)) / 65536.0f); sprintf(dtdy, "%4.4f", (float)int32_t((((cmd[9] >> 32) & 0x0000ffff) << 16) | ((cmd[11] >> 32) & 0xffff)) / 65536.0f); sprintf(dwdy, "%4.4f", (float)int32_t( (cmd[9] & 0xffff0000) | ((cmd[11] >> 16) & 0xffff)) / 65536.0f); buffer+=sprintf(buffer, "Tri_TexShade %d, XL: %s, XM: %s, XH: %s, YL: %s, YM: %s, YH: %s\n", lft, xl,xm,xh,yl,ym,yh); buffer+=sprintf(buffer, " "); buffer+=sprintf(buffer, " R: %s, G: %s, B: %s, A: %s\n", rt, gt, bt, at); buffer+=sprintf(buffer, " "); buffer+=sprintf(buffer, " DRDX: %s, DGDX: %s, DBDX: %s, DADX: %s\n", drdx, dgdx, dbdx, dadx); buffer+=sprintf(buffer, " "); buffer+=sprintf(buffer, " DRDE: %s, DGDE: %s, DBDE: %s, DADE: %s\n", drde, dgde, dbde, dade); buffer+=sprintf(buffer, " "); buffer+=sprintf(buffer, " DRDY: %s, DGDY: %s, DBDY: %s, DADY: %s\n", drdy, dgdy, dbdy, dady); buffer+=sprintf(buffer, " "); buffer+=sprintf(buffer, " S: %s, T: %s, W: %s\n", s, t, w); buffer+=sprintf(buffer, " "); buffer+=sprintf(buffer, " DSDX: %s, DTDX: %s, DWDX: %s\n", dsdx, dtdx, dwdx); buffer+=sprintf(buffer, " "); buffer+=sprintf(buffer, " DSDE: %s, DTDE: %s, DWDE: %s\n", dsde, dtde, dwde); buffer+=sprintf(buffer, " "); buffer+=sprintf(buffer, " DSDY: %s, DTDY: %s, DWDY: %s\n", dsdy, dtdy, dwdy); break; } case 0x0f: // Tri_TexShadeZ { const int32_t lft = (command >> 23) & 0x1; if (length < s_rdp_command_length[command]) { sprintf(buffer, "ERROR: Tri_TexShadeZ length = %d\n", length); return; } for (int32_t i = 1; i < 20; i++) { cmd[i] = m_cmd_data[m_cmd_cur+i]; } sprintf(yl, "%4.4f", (float)((cmd[0] >> 32) & 0x1fff) / 4.0f); sprintf(ym, "%4.4f", (float)((cmd[0] >> 16) & 0x1fff) / 4.0f); sprintf(yh, "%4.4f", (float)((cmd[0] >> 0) & 0x1fff) / 4.0f); sprintf(xl, "%4.4f", (float)int32_t(cmd[1] >> 32) / 65536.0f); sprintf(dxldy, "%4.4f", (float)int32_t(cmd[1]) / 65536.0f); sprintf(xh, "%4.4f", (float)int32_t(cmd[2] >> 32) / 65536.0f); sprintf(dxhdy, "%4.4f", (float)int32_t(cmd[2]) / 65536.0f); sprintf(xm, "%4.4f", (float)int32_t(cmd[3] >> 32) / 65536.0f); sprintf(dxmdy, "%4.4f", (float)int32_t(cmd[3]) / 65536.0f); sprintf(rt, "%4.4f", (float)int32_t( ((cmd[4] >> 32) & 0xffff0000) | ((cmd[ 6] >> 48) & 0xffff)) / 65536.0f); sprintf(gt, "%4.4f", (float)int32_t((((cmd[4] >> 32) & 0x0000ffff) << 16) | ((cmd[ 6] >> 32) & 0xffff)) / 65536.0f); sprintf(bt, "%4.4f", (float)int32_t( (cmd[4] & 0xffff0000) | ((cmd[ 6] >> 16) & 0xffff)) / 65536.0f); sprintf(at, "%4.4f", (float)int32_t( ((cmd[4] & 0x0000ffff) << 16) | ( cmd[ 6] & 0xffff)) / 65536.0f); sprintf(drdx, "%4.4f", (float)int32_t( ((cmd[5] >> 32) & 0xffff0000) | ((cmd[ 7] >> 48) & 0xffff)) / 65536.0f); sprintf(dgdx, "%4.4f", (float)int32_t((((cmd[5] >> 32) & 0x0000ffff) << 16) | ((cmd[ 7] >> 32) & 0xffff)) / 65536.0f); sprintf(dbdx, "%4.4f", (float)int32_t( (cmd[5] & 0xffff0000) | ((cmd[ 7] >> 16) & 0xffff)) / 65536.0f); sprintf(dadx, "%4.4f", (float)int32_t( ((cmd[5] & 0x0000ffff) << 16) | ( cmd[ 7] & 0xffff)) / 65536.0f); sprintf(drde, "%4.4f", (float)int32_t( ((cmd[8] >> 32) & 0xffff0000) | ((cmd[10] >> 48) & 0xffff)) / 65536.0f); sprintf(dgde, "%4.4f", (float)int32_t((((cmd[8] >> 32) & 0x0000ffff) << 16) | ((cmd[10] >> 32) & 0xffff)) / 65536.0f); sprintf(dbde, "%4.4f", (float)int32_t( (cmd[8] & 0xffff0000) | ((cmd[10] >> 16) & 0xffff)) / 65536.0f); sprintf(dade, "%4.4f", (float)int32_t( ((cmd[8] & 0x0000ffff) << 16) | ( cmd[10] & 0xffff)) / 65536.0f); sprintf(drdy, "%4.4f", (float)int32_t( ((cmd[9] >> 32) & 0xffff0000) | ((cmd[11] >> 48) & 0xffff)) / 65536.0f); sprintf(dgdy, "%4.4f", (float)int32_t((((cmd[9] >> 32) & 0x0000ffff) << 16) | ((cmd[11] >> 32) & 0xffff)) / 65536.0f); sprintf(dbdy, "%4.4f", (float)int32_t( (cmd[9] & 0xffff0000) | ((cmd[11] >> 16) & 0xffff)) / 65536.0f); sprintf(dady, "%4.4f", (float)int32_t( ((cmd[9] & 0x0000ffff) << 16) | ( cmd[11] & 0xffff)) / 65536.0f); sprintf(s, "%4.4f", (float)int32_t( ((cmd[4] >> 32) & 0xffff0000) | ((cmd[ 6] >> 48) & 0xffff)) / 65536.0f); sprintf(t, "%4.4f", (float)int32_t((((cmd[4] >> 32) & 0x0000ffff) << 16) | ((cmd[ 6] >> 32) & 0xffff)) / 65536.0f); sprintf(w, "%4.4f", (float)int32_t( (cmd[4] & 0xffff0000) | ((cmd[ 6] >> 16) & 0xffff)) / 65536.0f); sprintf(dsdx, "%4.4f", (float)int32_t( ((cmd[5] >> 32) & 0xffff0000) | ((cmd[ 7] >> 48) & 0xffff)) / 65536.0f); sprintf(dtdx, "%4.4f", (float)int32_t((((cmd[5] >> 32) & 0x0000ffff) << 16) | ((cmd[ 7] >> 32) & 0xffff)) / 65536.0f); sprintf(dwdx, "%4.4f", (float)int32_t( (cmd[5] & 0xffff0000) | ((cmd[ 7] >> 16) & 0xffff)) / 65536.0f); sprintf(dsde, "%4.4f", (float)int32_t( ((cmd[8] >> 32) & 0xffff0000) | ((cmd[10] >> 48) & 0xffff)) / 65536.0f); sprintf(dtde, "%4.4f", (float)int32_t((((cmd[8] >> 32) & 0x0000ffff) << 16) | ((cmd[10] >> 32) & 0xffff)) / 65536.0f); sprintf(dwde, "%4.4f", (float)int32_t( (cmd[8] & 0xffff0000) | ((cmd[10] >> 16) & 0xffff)) / 65536.0f); sprintf(dsdy, "%4.4f", (float)int32_t( ((cmd[9] >> 32) & 0xffff0000) | ((cmd[11] >> 48) & 0xffff)) / 65536.0f); sprintf(dtdy, "%4.4f", (float)int32_t((((cmd[9] >> 32) & 0x0000ffff) << 16) | ((cmd[11] >> 32) & 0xffff)) / 65536.0f); sprintf(dwdy, "%4.4f", (float)int32_t( (cmd[9] & 0xffff0000) | ((cmd[11] >> 16) & 0xffff)) / 65536.0f); buffer+=sprintf(buffer, "Tri_TexShadeZ %d, XL: %s, XM: %s, XH: %s, YL: %s, YM: %s, YH: %s\n", lft, xl,xm,xh,yl,ym,yh); buffer+=sprintf(buffer, " "); buffer+=sprintf(buffer, " R: %s, G: %s, B: %s, A: %s\n", rt, gt, bt, at); buffer+=sprintf(buffer, " "); buffer+=sprintf(buffer, " DRDX: %s, DGDX: %s, DBDX: %s, DADX: %s\n", drdx, dgdx, dbdx, dadx); buffer+=sprintf(buffer, " "); buffer+=sprintf(buffer, " DRDE: %s, DGDE: %s, DBDE: %s, DADE: %s\n", drde, dgde, dbde, dade); buffer+=sprintf(buffer, " "); buffer+=sprintf(buffer, " DRDY: %s, DGDY: %s, DBDY: %s, DADY: %s\n", drdy, dgdy, dbdy, dady); buffer+=sprintf(buffer, " "); buffer+=sprintf(buffer, " S: %s, T: %s, W: %s\n", s, t, w); buffer+=sprintf(buffer, " "); buffer+=sprintf(buffer, " DSDX: %s, DTDX: %s, DWDX: %s\n", dsdx, dtdx, dwdx); buffer+=sprintf(buffer, " "); buffer+=sprintf(buffer, " DSDE: %s, DTDE: %s, DWDE: %s\n", dsde, dtde, dwde); buffer+=sprintf(buffer, " "); buffer+=sprintf(buffer, " DSDY: %s, DTDY: %s, DWDY: %s\n", dsdy, dtdy, dwdy); break; } case 0x24: case 0x25: { if (length < 16) { sprintf(buffer, "ERROR: Texture_Rectangle length = %d\n", length); return; } cmd[1] = m_cmd_data[m_cmd_cur+1]; sprintf(s, "%4.4f", (float)int16_t((cmd[1] >> 48) & 0xffff) / 32.0f); sprintf(t, "%4.4f", (float)int16_t((cmd[1] >> 32) & 0xffff) / 32.0f); sprintf(dsdx, "%4.4f", (float)int16_t((cmd[1] >> 16) & 0xffff) / 1024.0f); sprintf(dtdy, "%4.4f", (float)int16_t((cmd[1] >> 0) & 0xffff) / 1024.0f); if (command == 0x24) sprintf(buffer, "Texture_Rectangle %d, %s, %s, %s, %s, %s, %s, %s, %s", tile, sh, th, sl, tl, s, t, dsdx, dtdy); else sprintf(buffer, "Texture_Rectangle_Flip %d, %s, %s, %s, %s, %s, %s, %s, %s", tile, sh, th, sl, tl, s, t, dsdx, dtdy); break; } case 0x26: sprintf(buffer, "Sync_Load"); break; case 0x27: sprintf(buffer, "Sync_Pipe"); break; case 0x28: sprintf(buffer, "Sync_Tile"); break; case 0x29: sprintf(buffer, "Sync_Full"); break; case 0x2d: sprintf(buffer, "Set_Scissor %s, %s, %s, %s", sl, tl, sh, th); break; case 0x2e: sprintf(buffer, "Set_Prim_Depth %04X, %04X", uint32_t(cmd[0] >> 16) & 0xffff, (uint32_t)cmd[0] & 0xffff); break; case 0x2f: sprintf(buffer, "Set_Other_Modes %08X %08X", uint32_t(cmd[0] >> 32), (uint32_t)cmd[0]); break; case 0x30: sprintf(buffer, "Load_TLUT %d, %s, %s, %s, %s", tile, sl, tl, sh, th); break; case 0x32: sprintf(buffer, "Set_Tile_Size %d, %s, %s, %s, %s", tile, sl, tl, sh, th); break; case 0x33: sprintf(buffer, "Load_Block %d, %03X, %03X, %03X, %03X", tile, uint32_t(cmd[0] >> 44) & 0xfff, uint32_t(cmd[0] >> 32) & 0xfff, uint32_t(cmd[0] >> 12) & 0xfff, uint32_t(cmd[0]) & 0xfff); break; case 0x34: sprintf(buffer, "Load_Tile %d, %s, %s, %s, %s", tile, sl, tl, sh, th); break; case 0x35: sprintf(buffer, "Set_Tile %d, %s, %s, %d, %04X", tile, format, size, (uint32_t(cmd[0] >> 41) & 0x1ff) * 8, (uint32_t(cmd[0] >> 32) & 0x1ff) * 8); break; case 0x36: sprintf(buffer, "Fill_Rectangle %s, %s, %s, %s", sh, th, sl, tl); break; case 0x37: sprintf(buffer, "Set_Fill_Color R: %d, G: %d, B: %d, A: %d", r, g, b, a); break; case 0x38: sprintf(buffer, "Set_Fog_Color R: %d, G: %d, B: %d, A: %d", r, g, b, a); break; case 0x39: sprintf(buffer, "Set_Blend_Color R: %d, G: %d, B: %d, A: %d", r, g, b, a); break; case 0x3a: sprintf(buffer, "Set_Prim_Color %d, %d, R: %d, G: %d, B: %d, A: %d", uint32_t(cmd[0] >> 40) & 0x1f, uint32_t(cmd[0] >> 32) & 0xff, r, g, b, a); break; case 0x3b: sprintf(buffer, "Set_Env_Color R: %d, G: %d, B: %d, A: %d", r, g, b, a); break; case 0x3c: sprintf(buffer, "Set_Combine %08X %08X", uint32_t(cmd[0] >> 32), (uint32_t)cmd[0]); break; case 0x3d: sprintf(buffer, "Set_Texture_Image %s, %s, %d, %08X", format, size, (uint32_t(cmd[0] >> 32) & 0x1ff) + 1, (uint32_t)cmd[0]); break; case 0x3e: sprintf(buffer, "Set_Mask_Image %08X", (uint32_t)cmd[0]); break; case 0x3f: sprintf(buffer, "Set_Color_Image %s, %s, %d, %08X", format, size, (uint32_t(cmd[0] >> 32) & 0x1ff) + 1, (uint32_t)cmd[0]); break; default: sprintf(buffer, "Unknown (%08X %08X)", uint32_t(cmd[0] >> 32), (uint32_t)cmd[0]); break; } } /*****************************************************************************/ static uint32_t rightcvghex(uint32_t x, uint32_t fmask) { uint32_t stickybit = ((x >> 1) & 0x1fff) > 0; uint32_t covered = ((x >> 14) & 3) + stickybit; covered = (0xf0 >> covered) & 0xf; return (covered & fmask); } static uint32_t leftcvghex(uint32_t x, uint32_t fmask) { uint32_t stickybit = ((x >> 1) & 0x1fff) > 0; uint32_t covered = ((x >> 14) & 3) + stickybit; covered = 0xf >> covered; return (covered & fmask); } static int32_t CLIP(int32_t value,int32_t min,int32_t max) { if (value < min) { return min; } else if (value > max) { return max; } else { return value; } } void n64_rdp::compute_cvg_noflip(extent_t* spans, int32_t* majorx, int32_t* minorx, int32_t* majorxint, int32_t* minorxint, int32_t scanline, int32_t yh, int32_t yl, int32_t base) { int32_t purgestart = 0xfff; int32_t purgeend = 0; const bool writablescanline = !(scanline & ~0x3ff); const int32_t scanlinespx = scanline << 2; if (!writablescanline) return; for(int32_t i = 0; i < 4; i++) { if (minorxint[i] < purgestart) { purgestart = minorxint[i]; } if (majorxint[i] > purgeend) { purgeend = majorxint[i]; } } purgestart = CLIP(purgestart, 0, 1023); purgeend = CLIP(purgeend, 0, 1023); int32_t length = purgeend - purgestart; if (length < 0) return; rdp_span_aux* userdata = (rdp_span_aux*)spans[scanline - base].userdata; memset(&userdata->m_cvg[purgestart], 0, (length + 1) << 1); for(int32_t i = 0; i < 4; i++) { int32_t minorcur = minorx[i]; int32_t majorcur = majorx[i]; int32_t minorcurint = minorxint[i]; int32_t majorcurint = majorxint[i]; length = majorcurint - minorcurint; int32_t fmask = (i & 1) ? 5 : 0xa; int32_t maskshift = (i ^ 3) << 2; int32_t fmaskshifted = fmask << maskshift; int32_t fleft = CLIP(minorcurint + 1, 0, 647); int32_t fright = CLIP(majorcurint - 1, 0, 647); bool valid_y = ((scanlinespx + i) >= yh && (scanlinespx + i) < yl); if (valid_y && length >= 0) { if (minorcurint != majorcurint) { if (!(minorcurint & ~0x3ff)) { userdata->m_cvg[minorcurint] |= (leftcvghex(minorcur, fmask) << maskshift); } if (!(majorcurint & ~0x3ff)) { userdata->m_cvg[majorcurint] |= (rightcvghex(majorcur, fmask) << maskshift); } } else { if (!(majorcurint & ~0x3ff)) { int32_t samecvg = leftcvghex(minorcur, fmask) & rightcvghex(majorcur, fmask); userdata->m_cvg[majorcurint] |= (samecvg << maskshift); } } for (; fleft <= fright; fleft++) { userdata->m_cvg[fleft] |= fmaskshifted; } } } } void n64_rdp::compute_cvg_flip(extent_t* spans, int32_t* majorx, int32_t* minorx, int32_t* majorxint, int32_t* minorxint, int32_t scanline, int32_t yh, int32_t yl, int32_t base) { int32_t purgestart = 0xfff; int32_t purgeend = 0; const bool writablescanline = !(scanline & ~0x3ff); const int32_t scanlinespx = scanline << 2; if(!writablescanline) return; for(int32_t i = 0; i < 4; i++) { if (majorxint[i] < purgestart) { purgestart = majorxint[i]; } if (minorxint[i] > purgeend) { purgeend = minorxint[i]; } } purgestart = CLIP(purgestart, 0, 1023); purgeend = CLIP(purgeend, 0, 1023); int32_t length = purgeend - purgestart; if (length < 0) return; rdp_span_aux* userdata = (rdp_span_aux*)spans[scanline - base].userdata; memset(&userdata->m_cvg[purgestart], 0, (length + 1) << 1); for(int32_t i = 0; i < 4; i++) { int32_t minorcur = minorx[i]; int32_t majorcur = majorx[i]; int32_t minorcurint = minorxint[i]; int32_t majorcurint = majorxint[i]; length = minorcurint - majorcurint; int32_t fmask = (i & 1) ? 5 : 0xa; int32_t maskshift = (i ^ 3) << 2; int32_t fmaskshifted = fmask << maskshift; int32_t fleft = CLIP(majorcurint + 1, 0, 647); int32_t fright = CLIP(minorcurint - 1, 0, 647); bool valid_y = ((scanlinespx + i) >= yh && (scanlinespx + i) < yl); if (valid_y && length >= 0) { if (minorcurint != majorcurint) { if (!(minorcurint & ~0x3ff)) { userdata->m_cvg[minorcurint] |= (rightcvghex(minorcur, fmask) << maskshift); } if (!(majorcurint & ~0x3ff)) { userdata->m_cvg[majorcurint] |= (leftcvghex(majorcur, fmask) << maskshift); } } else { if (!(majorcurint & ~0x3ff)) { int32_t samecvg = rightcvghex(minorcur, fmask) & leftcvghex(majorcur, fmask); userdata->m_cvg[majorcurint] |= (samecvg << maskshift); } } for (; fleft <= fright; fleft++) { userdata->m_cvg[fleft] |= fmaskshifted; } } } } #define SIGN(x, numb) (((x) & ((1 << numb) - 1)) | -((x) & (1 << (numb - 1)))) void n64_rdp::draw_triangle(bool shade, bool texture, bool zbuffer, bool rect) { const uint64_t* cmd_data = rect ? m_temp_rect_data : m_cmd_data; const uint32_t fifo_index = rect ? 0 : m_cmd_cur; const uint64_t w1 = cmd_data[fifo_index + 0]; int32_t flip = int32_t(w1 >> 55) & 1; m_misc_state.m_max_level = uint32_t(w1 >> 51) & 7; int32_t tilenum = int32_t(w1 >> 48) & 0x7; int32_t dsdiff = 0, dtdiff = 0, dwdiff = 0, drdiff = 0, dgdiff = 0, dbdiff = 0, dadiff = 0, dzdiff = 0; int32_t dsdeh = 0, dtdeh = 0, dwdeh = 0, drdeh = 0, dgdeh = 0, dbdeh = 0, dadeh = 0, dzdeh = 0; int32_t dsdxh = 0, dtdxh = 0, dwdxh = 0, drdxh = 0, dgdxh = 0, dbdxh = 0, dadxh = 0, dzdxh = 0; int32_t dsdyh = 0, dtdyh = 0, dwdyh = 0, drdyh = 0, dgdyh = 0, dbdyh = 0, dadyh = 0, dzdyh = 0; int32_t maxxmx = 0; // maxxmx / minxhx very opaque names, consider re-naming int32_t minxmx = 0; int32_t maxxhx = 0; int32_t minxhx = 0; int32_t shade_base = fifo_index + 4; int32_t texture_base = fifo_index + 4; int32_t zbuffer_base = fifo_index + 4; if(shade) { texture_base += 8; zbuffer_base += 8; } if(texture) { zbuffer_base += 8; } uint64_t w2 = cmd_data[fifo_index + 1]; uint64_t w3 = cmd_data[fifo_index + 2]; uint64_t w4 = cmd_data[fifo_index + 3]; int32_t yl = int32_t(w1 >> 32) & 0x3fff; int32_t ym = int32_t(w1 >> 16) & 0x3fff; int32_t yh = int32_t(w1 >> 0) & 0x3fff; int32_t xl = (int32_t)(w2 >> 32) & 0x3fffffff; int32_t xh = (int32_t)(w3 >> 32) & 0x3fffffff; int32_t xm = (int32_t)(w4 >> 32) & 0x3fffffff; // Inverse slopes in 16.16 format int32_t dxldy = (int32_t)w2; int32_t dxhdy = (int32_t)w3; int32_t dxmdy = (int32_t)w4; if (yl & 0x2000) yl |= 0xffffc000; if (ym & 0x2000) ym |= 0xffffc000; if (yh & 0x2000) yh |= 0xffffc000; if (xl & 0x20000000) xl |= 0xc0000000; if (xm & 0x20000000) xm |= 0xc0000000; if (xh & 0x20000000) xh |= 0xc0000000; int32_t r = int32_t(((cmd_data[shade_base] >> 32) & 0xffff0000) | ((cmd_data[shade_base + 2] >> 48) & 0x0000ffff)); int32_t g = int32_t(((cmd_data[shade_base] >> 16) & 0xffff0000) | ((cmd_data[shade_base + 2] >> 32) & 0x0000ffff)); int32_t b = int32_t( (cmd_data[shade_base] & 0xffff0000) | ((cmd_data[shade_base + 2] >> 16) & 0x0000ffff)); int32_t a = int32_t(((cmd_data[shade_base] << 16) & 0xffff0000) | (cmd_data[shade_base + 2] & 0x0000ffff)); const int32_t drdx = int32_t(((cmd_data[shade_base + 1] >> 32) & 0xffff0000) | ((cmd_data[shade_base + 3] >> 48) & 0x0000ffff)); const int32_t dgdx = int32_t(((cmd_data[shade_base + 1] >> 16) & 0xffff0000) | ((cmd_data[shade_base + 3] >> 32) & 0x0000ffff)); const int32_t dbdx = int32_t( (cmd_data[shade_base + 1] & 0xffff0000) | ((cmd_data[shade_base + 3] >> 16) & 0x0000ffff)); const int32_t dadx = int32_t(((cmd_data[shade_base + 1] << 16) & 0xffff0000) | (cmd_data[shade_base + 3] & 0x0000ffff)); const int32_t drde = int32_t(((cmd_data[shade_base + 4] >> 32) & 0xffff0000) | ((cmd_data[shade_base + 6] >> 48) & 0x0000ffff)); const int32_t dgde = int32_t(((cmd_data[shade_base + 4] >> 16) & 0xffff0000) | ((cmd_data[shade_base + 6] >> 32) & 0x0000ffff)); const int32_t dbde = int32_t( (cmd_data[shade_base + 4] & 0xffff0000) | ((cmd_data[shade_base + 6] >> 16) & 0x0000ffff)); const int32_t dade = int32_t(((cmd_data[shade_base + 4] << 16) & 0xffff0000) | (cmd_data[shade_base + 6] & 0x0000ffff)); const int32_t drdy = int32_t(((cmd_data[shade_base + 5] >> 32) & 0xffff0000) | ((cmd_data[shade_base + 7] >> 48) & 0x0000ffff)); const int32_t dgdy = int32_t(((cmd_data[shade_base + 5] >> 16) & 0xffff0000) | ((cmd_data[shade_base + 7] >> 32) & 0x0000ffff)); const int32_t dbdy = int32_t( (cmd_data[shade_base + 5] & 0xffff0000) | ((cmd_data[shade_base + 7] >> 16) & 0x0000ffff)); const int32_t dady = int32_t(((cmd_data[shade_base + 5] << 16) & 0xffff0000) | (cmd_data[shade_base + 7] & 0x0000ffff)); int32_t s = int32_t(((cmd_data[texture_base] >> 32) & 0xffff0000) | ((cmd_data[texture_base+ 2 ] >> 48) & 0x0000ffff)); int32_t t = int32_t(((cmd_data[texture_base] >> 16) & 0xffff0000) | ((cmd_data[texture_base+ 2 ] >> 32) & 0x0000ffff)); int32_t w = int32_t( (cmd_data[texture_base] & 0xffff0000) | ((cmd_data[texture_base+ 2 ] >> 16) & 0x0000ffff)); const int32_t dsdx = int32_t(((cmd_data[texture_base + 1] >> 32) & 0xffff0000) | ((cmd_data[texture_base + 3] >> 48) & 0x0000ffff)); const int32_t dtdx = int32_t(((cmd_data[texture_base + 1] >> 16) & 0xffff0000) | ((cmd_data[texture_base + 3] >> 32) & 0x0000ffff)); const int32_t dwdx = int32_t( (cmd_data[texture_base + 1] & 0xffff0000) | ((cmd_data[texture_base + 3] >> 16) & 0x0000ffff)); const int32_t dsde = int32_t(((cmd_data[texture_base + 4] >> 32) & 0xffff0000) | ((cmd_data[texture_base + 6] >> 48) & 0x0000ffff)); const int32_t dtde = int32_t(((cmd_data[texture_base + 4] >> 16) & 0xffff0000) | ((cmd_data[texture_base + 6] >> 32) & 0x0000ffff)); const int32_t dwde = int32_t( (cmd_data[texture_base + 4] & 0xffff0000) | ((cmd_data[texture_base + 6] >> 16) & 0x0000ffff)); const int32_t dsdy = int32_t(((cmd_data[texture_base + 5] >> 32) & 0xffff0000) | ((cmd_data[texture_base + 7] >> 48) & 0x0000ffff)); const int32_t dtdy = int32_t(((cmd_data[texture_base + 5] >> 16) & 0xffff0000) | ((cmd_data[texture_base + 7] >> 32) & 0x0000ffff)); const int32_t dwdy = int32_t( (cmd_data[texture_base + 5] & 0xffff0000) | ((cmd_data[texture_base + 7] >> 16) & 0x0000ffff)); int32_t z = int32_t(cmd_data[zbuffer_base] >> 32); const int32_t dzdx = int32_t(cmd_data[zbuffer_base]); const int32_t dzde = int32_t(cmd_data[zbuffer_base+1] >> 32); const int32_t dzdy = int32_t(cmd_data[zbuffer_base+1]); const int32_t dzdy_dz = (dzdy >> 16) & 0xffff; const int32_t dzdx_dz = (dzdx >> 16) & 0xffff; extent_t spans[2048]; #ifdef MAME_DEBUG memset(spans, 0xcc, sizeof(spans)); #endif m_span_base.m_span_drdy = drdy; m_span_base.m_span_dgdy = dgdy; m_span_base.m_span_dbdy = dbdy; m_span_base.m_span_dady = dady; m_span_base.m_span_dzdy = m_other_modes.z_source_sel ? 0 : dzdy; uint32_t temp_dzpix = ((dzdy_dz & 0x8000) ? ((~dzdy_dz) & 0x7fff) : dzdy_dz) + ((dzdx_dz & 0x8000) ? ((~dzdx_dz) & 0x7fff) : dzdx_dz); m_span_base.m_span_dr = drdx & ~0x1f; m_span_base.m_span_dg = dgdx & ~0x1f; m_span_base.m_span_db = dbdx & ~0x1f; m_span_base.m_span_da = dadx & ~0x1f; m_span_base.m_span_ds = dsdx; m_span_base.m_span_dt = dtdx; m_span_base.m_span_dw = dwdx; m_span_base.m_span_dz = m_other_modes.z_source_sel ? 0 : dzdx; m_span_base.m_span_dymax = 0; m_span_base.m_span_dzpix = m_dzpix_normalize[temp_dzpix & 0xffff]; int32_t xleft_inc = (dxmdy >> 2) & ~1; int32_t xright_inc = (dxhdy >> 2) & ~1; int32_t xright = xh & ~1; int32_t xleft = xm & ~1; const int32_t sign_dxhdy = (dxhdy & 0x80000000) ? 1 : 0; const int32_t do_offset = !(sign_dxhdy ^ (flip)); if (do_offset) { dsdeh = dsde >> 9; dsdyh = dsdy >> 9; dtdeh = dtde >> 9; dtdyh = dtdy >> 9; dwdeh = dwde >> 9; dwdyh = dwdy >> 9; drdeh = drde >> 9; drdyh = drdy >> 9; dgdeh = dgde >> 9; dgdyh = dgdy >> 9; dbdeh = dbde >> 9; dbdyh = dbdy >> 9; dadeh = dade >> 9; dadyh = dady >> 9; dzdeh = dzde >> 9; dzdyh = dzdy >> 9; dsdiff = (dsdeh << 8) + (dsdeh << 7) - (dsdyh << 8) - (dsdyh << 7); dtdiff = (dtdeh << 8) + (dtdeh << 7) - (dtdyh << 8) - (dtdyh << 7); dwdiff = (dwdeh << 8) + (dwdeh << 7) - (dwdyh << 8) - (dwdyh << 7); drdiff = (drdeh << 8) + (drdeh << 7) - (drdyh << 8) - (drdyh << 7); dgdiff = (dgdeh << 8) + (dgdeh << 7) - (dgdyh << 8) - (dgdyh << 7); dbdiff = (dbdeh << 8) + (dbdeh << 7) - (dbdyh << 8) - (dbdyh << 7); dadiff = (dadeh << 8) + (dadeh << 7) - (dadyh << 8) - (dadyh << 7); dzdiff = (dzdeh << 8) + (dzdeh << 7) - (dzdyh << 8) - (dzdyh << 7); } else { dsdiff = dtdiff = dwdiff = drdiff = dgdiff = dbdiff = dadiff = dzdiff = 0; } dsdxh = dsdx >> 8; dtdxh = dtdx >> 8; dwdxh = dwdx >> 8; drdxh = drdx >> 8; dgdxh = dgdx >> 8; dbdxh = dbdx >> 8; dadxh = dadx >> 8; dzdxh = dzdx >> 8; const int32_t ycur = yh & ~3; const int32_t ylfar = yl | 3; const int32_t ldflag = (sign_dxhdy ^ flip) ? 0 : 3; int32_t majorx[4]; int32_t minorx[4]; int32_t majorxint[4]; int32_t minorxint[4]; int32_t xfrac = ((xright >> 8) & 0xff); const int32_t clipy1 = m_scissor.m_yh; const int32_t clipy2 = m_scissor.m_yl; // Trivial reject if((ycur >> 2) >= clipy2 && (ylfar >> 2) >= clipy2) { return; } if((ycur >> 2) < clipy1 && (ylfar >> 2) < clipy1) { return; } bool new_object = true; rdp_poly_state* object = nullptr; bool valid = false; int32_t* minx = flip ? &minxhx : &minxmx; int32_t* maxx = flip ? &maxxmx : &maxxhx; int32_t* startx = flip ? maxx : minx; int32_t* endx = flip ? minx : maxx; for (int32_t k = ycur; k <= ylfar; k++) { if (k == ym) { xleft = xl & ~1; xleft_inc = (dxldy >> 2) & ~1; } const int32_t xstart = xleft >> 16; const int32_t xend = xright >> 16; const int32_t j = k >> 2; const int32_t spanidx = (k - ycur) >> 2; const int32_t spix = k & 3; bool valid_y = !(k < yh || k >= yl); if (spanidx >= 0 && spanidx < 2048) { majorxint[spix] = xend; minorxint[spix] = xstart; majorx[spix] = xright; minorx[spix] = xleft; if (spix == 0) { *maxx = 0; *minx = 0xfff; } if (valid_y) { if (flip) { *maxx = std::max(xstart, *maxx); *minx = std::min(xend, *minx); } else { *minx = std::min(xstart, *minx); *maxx = std::max(xend, *maxx); } } if (spix == 0) { if(new_object) { object = &object_data_alloc(); memcpy(object->m_tmem, m_tmem.get(), 0x1000); new_object = false; } spans[spanidx].userdata = (void*)((uint8_t*)m_aux_buf.get() + m_aux_buf_ptr); valid = true; m_aux_buf_ptr += sizeof(rdp_span_aux); if(m_aux_buf_ptr >= EXTENT_AUX_COUNT) { fatalerror("n64_rdp::draw_triangle: span aux buffer overflow\n"); } rdp_span_aux* userdata = (rdp_span_aux*)spans[spanidx].userdata; userdata->m_tmem = object->m_tmem; userdata->m_blend_color = m_blend_color; userdata->m_prim_color = m_prim_color; userdata->m_env_color = m_env_color; userdata->m_fog_color = m_fog_color; userdata->m_prim_alpha = m_prim_alpha; userdata->m_env_alpha = m_env_alpha; userdata->m_key_scale = m_key_scale; userdata->m_lod_fraction = m_lod_fraction; userdata->m_prim_lod_fraction = m_prim_lod_fraction; // Setup blender data for this scanline set_blender_input(0, 0, &userdata->m_color_inputs.blender1a_rgb[0], &userdata->m_color_inputs.blender1b_a[0], m_other_modes.blend_m1a_0, m_other_modes.blend_m1b_0, userdata); set_blender_input(0, 1, &userdata->m_color_inputs.blender2a_rgb[0], &userdata->m_color_inputs.blender2b_a[0], m_other_modes.blend_m2a_0, m_other_modes.blend_m2b_0, userdata); set_blender_input(1, 0, &userdata->m_color_inputs.blender1a_rgb[1], &userdata->m_color_inputs.blender1b_a[1], m_other_modes.blend_m1a_1, m_other_modes.blend_m1b_1, userdata); set_blender_input(1, 1, &userdata->m_color_inputs.blender2a_rgb[1], &userdata->m_color_inputs.blender2b_a[1], m_other_modes.blend_m2a_1, m_other_modes.blend_m2b_1, userdata); // Setup color combiner data for this scanline set_suba_input_rgb(&userdata->m_color_inputs.combiner_rgbsub_a[0], m_combine.sub_a_rgb0, userdata); set_subb_input_rgb(&userdata->m_color_inputs.combiner_rgbsub_b[0], m_combine.sub_b_rgb0, userdata); set_mul_input_rgb(&userdata->m_color_inputs.combiner_rgbmul[0], m_combine.mul_rgb0, userdata); set_add_input_rgb(&userdata->m_color_inputs.combiner_rgbadd[0], m_combine.add_rgb0, userdata); set_sub_input_alpha(&userdata->m_color_inputs.combiner_alphasub_a[0], m_combine.sub_a_a0, userdata); set_sub_input_alpha(&userdata->m_color_inputs.combiner_alphasub_b[0], m_combine.sub_b_a0, userdata); set_mul_input_alpha(&userdata->m_color_inputs.combiner_alphamul[0], m_combine.mul_a0, userdata); set_sub_input_alpha(&userdata->m_color_inputs.combiner_alphaadd[0], m_combine.add_a0, userdata); set_suba_input_rgb(&userdata->m_color_inputs.combiner_rgbsub_a[1], m_combine.sub_a_rgb1, userdata); set_subb_input_rgb(&userdata->m_color_inputs.combiner_rgbsub_b[1], m_combine.sub_b_rgb1, userdata); set_mul_input_rgb(&userdata->m_color_inputs.combiner_rgbmul[1], m_combine.mul_rgb1, userdata); set_add_input_rgb(&userdata->m_color_inputs.combiner_rgbadd[1], m_combine.add_rgb1, userdata); set_sub_input_alpha(&userdata->m_color_inputs.combiner_alphasub_a[1], m_combine.sub_a_a1, userdata); set_sub_input_alpha(&userdata->m_color_inputs.combiner_alphasub_b[1], m_combine.sub_b_a1, userdata); set_mul_input_alpha(&userdata->m_color_inputs.combiner_alphamul[1], m_combine.mul_a1, userdata); set_sub_input_alpha(&userdata->m_color_inputs.combiner_alphaadd[1], m_combine.add_a1, userdata); } if (spix == 3) { spans[spanidx].startx = *startx; spans[spanidx].stopx = *endx; ((this)->*(m_compute_cvg[flip]))(spans, majorx, minorx, majorxint, minorxint, j, yh, yl, ycur >> 2); } if (spix == ldflag) { ((rdp_span_aux*)spans[spanidx].userdata)->m_unscissored_rx = xend; xfrac = ((xright >> 8) & 0xff); spans[spanidx].param[SPAN_R].start = ((r >> 9) << 9) + drdiff - (xfrac * drdxh); spans[spanidx].param[SPAN_G].start = ((g >> 9) << 9) + dgdiff - (xfrac * dgdxh); spans[spanidx].param[SPAN_B].start = ((b >> 9) << 9) + dbdiff - (xfrac * dbdxh); spans[spanidx].param[SPAN_A].start = ((a >> 9) << 9) + dadiff - (xfrac * dadxh); spans[spanidx].param[SPAN_S].start = (((s >> 9) << 9) + dsdiff - (xfrac * dsdxh)) & ~0x1f; spans[spanidx].param[SPAN_T].start = (((t >> 9) << 9) + dtdiff - (xfrac * dtdxh)) & ~0x1f; spans[spanidx].param[SPAN_W].start = (((w >> 9) << 9) + dwdiff - (xfrac * dwdxh)) & ~0x1f; spans[spanidx].param[SPAN_Z].start = ((z >> 9) << 9) + dzdiff - (xfrac * dzdxh); } } if (spix == 3) { r += drde; g += dgde; b += dbde; a += dade; s += dsde; t += dtde; w += dwde; z += dzde; } xleft += xleft_inc; xright += xright_inc; } if(!new_object && valid) { render_spans(yh >> 2, yl >> 2, tilenum, flip ? true : false, spans, rect, object); } m_aux_buf_ptr = 0; // Spans can be reused once render completes //wait("draw_triangle"); } /*****************************************************************************/ //////////////////////// // RDP COMMANDS //////////////////////// void n64_rdp::triangle(bool shade, bool texture, bool zbuffer) { draw_triangle(shade, texture, zbuffer, false); m_pipe_clean = false; } void n64_rdp::cmd_triangle(uint64_t w1) { triangle(false, false, false); } void n64_rdp::cmd_triangle_z(uint64_t w1) { triangle(false, false, true); } void n64_rdp::cmd_triangle_t(uint64_t w1) { triangle(false, true, false); } void n64_rdp::cmd_triangle_tz(uint64_t w1) { triangle(false, true, true); } void n64_rdp::cmd_triangle_s(uint64_t w1) { triangle(true, false, false); } void n64_rdp::cmd_triangle_sz(uint64_t w1) { triangle(true, false, true); } void n64_rdp::cmd_triangle_st(uint64_t w1) { triangle(true, true, false); } void n64_rdp::cmd_triangle_stz(uint64_t w1) { triangle(true, true, true); } void n64_rdp::cmd_tex_rect(uint64_t w1) { const uint64_t* data = m_cmd_data + m_cmd_cur; const uint64_t w2 = data[1]; const uint64_t tilenum = (w1 >> 24) & 0x7; const uint64_t xh = (w1 >> 12) & 0xfff; const uint64_t xl = (w1 >> 44) & 0xfff; const uint64_t yh = (w1 >> 0) & 0xfff; uint64_t yl = (w1 >> 32) & 0xfff; const uint64_t s = (w2 >> 48) & 0xffff; const uint64_t t = (w2 >> 32) & 0xffff; const uint64_t dsdx = SIGN16((w2 >> 16) & 0xffff); const uint64_t dtdy = SIGN16((w2 >> 0) & 0xffff); if (m_other_modes.cycle_type == CYCLE_TYPE_FILL || m_other_modes.cycle_type == CYCLE_TYPE_COPY) { yl |= 3; } const uint64_t xlint = (xl >> 2) & 0x3ff; const uint64_t xhint = (xh >> 2) & 0x3ff; uint64_t* ewdata = m_temp_rect_data; ewdata[0] = ((uint64_t)0x24 << 56) | ((0x80L | tilenum) << 48) | (yl << 32) | (yl << 16) | yh; // command, flipped, tile, yl ewdata[1] = (xlint << 48) | ((xl & 3) << 46); // xl, xl frac, dxldy (0), dxldy frac (0) ewdata[2] = (xhint << 48) | ((xh & 3) << 46); // xh, xh frac, dxhdy (0), dxhdy frac (0) ewdata[3] = (xlint << 48) | ((xl & 3) << 46); // xm, xm frac, dxmdy (0), dxmdy frac (0) memset(&ewdata[4], 0, 8 * sizeof(uint64_t)); // shade ewdata[12] = (s << 48) | (t << 32); // s, t, w (0) ewdata[13] = (dsdx >> 5) << 48; // dsdx, dtdx, dwdx (0) ewdata[14] = 0; // s frac (0), t frac (0), w frac (0) ewdata[15] = (dsdx & 0x1f) << 59; // dsdx frac, dtdx frac, dwdx frac (0) ewdata[16] = ((dtdy >> 5) & 0xffff) << 32; // dsde, dtde, dwde (0) ewdata[17] = ((dtdy >> 5) & 0xffff) << 32; // dsdy, dtdy, dwdy (0) ewdata[18] = ((dtdy & 0x1f) << 11) << 32; // dsde frac, dtde frac, dwde frac (0) ewdata[38] = ((dtdy & 0x1f) << 11) << 32; // dsdy frac, dtdy frac, dwdy frac (0) // ewdata[40-43] = 0; // depth draw_triangle(true, true, false, true); } void n64_rdp::cmd_tex_rect_flip(uint64_t w1) { const uint64_t* data = m_cmd_data + m_cmd_cur; const uint64_t w2 = data[1]; const uint64_t tilenum = (w1 >> 56) & 0x7; const uint64_t xh = (w1 >> 12) & 0xfff; const uint64_t xl = (w1 >> 44) & 0xfff; const uint64_t yh = (w1 >> 0) & 0xfff; uint64_t yl = (w1 >> 32) & 0xfff; const uint64_t s = (w2 >> 48) & 0xffff; const uint64_t t = (w2 >> 32) & 0xffff; const uint64_t dsdx = SIGN16((w2 >> 16) & 0xffff); const uint64_t dtdy = SIGN16((w2 >> 0) & 0xffff); if (m_other_modes.cycle_type == CYCLE_TYPE_FILL || m_other_modes.cycle_type == CYCLE_TYPE_COPY) { yl |= 3; } const uint64_t xlint = (xl >> 2) & 0x3ff; const uint64_t xhint = (xh >> 2) & 0x3ff; uint64_t* ewdata = m_temp_rect_data; ewdata[0] = ((uint64_t)0x25 << 56) | ((0x80L | tilenum) << 48) | (yl << 32) | (yl << 16) | yh; // command, flipped, tile, yl ewdata[1] = (xlint << 48) | ((xl & 3) << 46); // xl, xl frac, dxldy (0), dxldy frac (0) ewdata[2] = (xhint << 48) | ((xh & 3) << 46); // xh, xh frac, dxhdy (0), dxhdy frac (0) ewdata[3] = (xlint << 48) | ((xl & 3) << 46); // xm, xm frac, dxmdy (0), dxmdy frac (0) memset(&ewdata[4], 0, 8 * sizeof(uint64_t)); // shade ewdata[12] = (s << 48) | (t << 32); // s, t, w (0) ewdata[13] = ((dtdy >> 5) & 0xffff) << 32; // dsdx, dtdx, dwdx (0) ewdata[14] = 0; // s frac (0), t frac (0), w frac (0) ewdata[15] = ((dtdy & 0x1f) << 43); // dsdx frac, dtdx frac, dwdx frac (0) ewdata[16] = (dsdx >> 5) << 48; // dsde, dtde, dwde (0) ewdata[17] = (dsdx >> 5) << 48; // dsdy, dtdy, dwdy (0) ewdata[18] = (dsdx & 0x1f) << 59; // dsde frac, dtde frac, dwde frac (0) ewdata[19] = (dsdx & 0x1f) << 59; // dsdy frac, dtdy frac, dwdy frac (0) draw_triangle(true, true, false, true); } void n64_rdp::cmd_sync_load(uint64_t w1) { //wait("SyncLoad"); } void n64_rdp::cmd_sync_pipe(uint64_t w1) { //wait("SyncPipe"); } void n64_rdp::cmd_sync_tile(uint64_t w1) { //wait("SyncTile"); } void n64_rdp::cmd_sync_full(uint64_t w1) { //wait("SyncFull"); m_n64_periphs->dp_full_sync(); } void n64_rdp::cmd_set_key_gb(uint64_t w1) { m_key_scale.set_b(uint32_t(w1 >> 0) & 0xff); m_key_scale.set_g(uint32_t(w1 >> 16) & 0xff); } void n64_rdp::cmd_set_key_r(uint64_t w1) { m_key_scale.set_r(uint32_t(w1 & 0xff)); } void n64_rdp::cmd_set_fill_color32(uint64_t w1) { //wait("SetFillColor"); m_fill_color = (uint32_t)w1; } void n64_rdp::cmd_set_convert(uint64_t w1) { if(!m_pipe_clean) { m_pipe_clean = true; wait("SetConvert"); } int32_t k0 = int32_t(w1 >> 45) & 0x1ff; int32_t k1 = int32_t(w1 >> 36) & 0x1ff; int32_t k2 = int32_t(w1 >> 27) & 0x1ff; int32_t k3 = int32_t(w1 >> 18) & 0x1ff; int32_t k4 = int32_t(w1 >> 9) & 0x1ff; int32_t k5 = int32_t(w1 >> 0) & 0x1ff; k0 = (SIGN9(k0) << 1) + 1; k1 = (SIGN9(k1) << 1) + 1; k2 = (SIGN9(k2) << 1) + 1; k3 = (SIGN9(k3) << 1) + 1; set_yuv_factors(rgbaint_t(0, k0, k2, k3), rgbaint_t(0, 0, k1, 0), rgbaint_t(k4, k4, k4, k4), rgbaint_t(k5, k5, k5, k5)); } void n64_rdp::cmd_set_scissor(uint64_t w1) { m_scissor.m_xh = ((w1 >> 44) & 0xfff) >> 2; m_scissor.m_yh = ((w1 >> 32) & 0xfff) >> 2; m_scissor.m_xl = ((w1 >> 12) & 0xfff) >> 2; m_scissor.m_yl = ((w1 >> 0) & 0xfff) >> 2; // TODO: handle f & o? } void n64_rdp::cmd_set_prim_depth(uint64_t w1) { m_misc_state.m_primitive_z = (uint32_t)(w1 & 0x7fff0000); m_misc_state.m_primitive_dz = (uint16_t)(w1 >> 32); } void n64_rdp::cmd_set_other_modes(uint64_t w1) { //wait("SetOtherModes"); m_other_modes.cycle_type = (w1 >> 52) & 0x3; // 01 m_other_modes.persp_tex_en = (w1 >> 51) & 1; // 1 m_other_modes.detail_tex_en = (w1 >> 50) & 1; // 0 m_other_modes.sharpen_tex_en = (w1 >> 49) & 1; // 0 m_other_modes.tex_lod_en = (w1 >> 48) & 1; // 0 m_other_modes.en_tlut = (w1 >> 47) & 1; // 0 m_other_modes.tlut_type = (w1 >> 46) & 1; // 0 m_other_modes.sample_type = (w1 >> 45) & 1; // 1 m_other_modes.mid_texel = (w1 >> 44) & 1; // 0 m_other_modes.bi_lerp0 = (w1 >> 43) & 1; // 1 m_other_modes.bi_lerp1 = (w1 >> 42) & 1; // 1 m_other_modes.convert_one = (w1 >> 41) & 1; // 0 m_other_modes.key_en = (w1 >> 40) & 1; // 0 m_other_modes.rgb_dither_sel = (w1 >> 38) & 0x3; // 00 m_other_modes.alpha_dither_sel = (w1 >> 36) & 0x3; // 01 m_other_modes.blend_m1a_0 = (w1 >> 30) & 0x3; // 11 m_other_modes.blend_m1a_1 = (w1 >> 28) & 0x3; // 00 m_other_modes.blend_m1b_0 = (w1 >> 26) & 0x3; // 10 m_other_modes.blend_m1b_1 = (w1 >> 24) & 0x3; // 00 m_other_modes.blend_m2a_0 = (w1 >> 22) & 0x3; // 00 m_other_modes.blend_m2a_1 = (w1 >> 20) & 0x3; // 01 m_other_modes.blend_m2b_0 = (w1 >> 18) & 0x3; // 00 m_other_modes.blend_m2b_1 = (w1 >> 16) & 0x3; // 01 m_other_modes.force_blend = (w1 >> 14) & 1; // 0 m_other_modes.blend_shift = m_other_modes.force_blend ? 5 : 2; m_other_modes.alpha_cvg_select = (w1 >> 13) & 1; // 1 m_other_modes.cvg_times_alpha = (w1 >> 12) & 1; // 0 m_other_modes.z_mode = (w1 >> 10) & 0x3; // 00 m_other_modes.cvg_dest = (w1 >> 8) & 0x3; // 00 m_other_modes.color_on_cvg = (w1 >> 7) & 1; // 0 m_other_modes.image_read_en = (w1 >> 6) & 1; // 1 m_other_modes.z_update_en = (w1 >> 5) & 1; // 1 m_other_modes.z_compare_en = (w1 >> 4) & 1; // 1 m_other_modes.antialias_en = (w1 >> 3) & 1; // 1 m_other_modes.z_source_sel = (w1 >> 2) & 1; // 0 m_other_modes.dither_alpha_en = (w1 >> 1) & 1; // 0 m_other_modes.alpha_compare_en = (w1 >> 0) & 1; // 0 m_other_modes.alpha_dither_mode = (m_other_modes.alpha_compare_en << 1) | m_other_modes.dither_alpha_en; } void n64_rdp::cmd_load_tlut(uint64_t w1) { //wait("LoadTLUT"); n64_tile_t* tile = m_tiles; const int32_t tilenum = (w1 >> 24) & 0x7; const int32_t sl = tile[tilenum].sl = int32_t(w1 >> 44) & 0xfff; const int32_t tl = tile[tilenum].tl = int32_t(w1 >> 32) & 0xfff; const int32_t sh = tile[tilenum].sh = int32_t(w1 >> 12) & 0xfff; const int32_t th = tile[tilenum].th = int32_t(w1 >> 0) & 0xfff; if (tl != th) { fatalerror("Load tlut: tl=%d, th=%d\n",tl,th); } m_capture.data_begin(); const int32_t count = ((sh >> 2) - (sl >> 2) + 1) << 2; switch (m_misc_state.m_ti_size) { case PIXEL_SIZE_16BIT: { if (tile[tilenum].tmem < 256) { fatalerror("rdp_load_tlut: loading tlut into low half at %d qwords\n",tile[tilenum].tmem); } int32_t srcstart = (m_misc_state.m_ti_address + (tl >> 2) * (m_misc_state.m_ti_width << 1) + (sl >> 1)) >> 1; int32_t dststart = tile[tilenum].tmem << 2; uint16_t* dst = get_tmem16(); for (int32_t i = 0; i < count; i += 4) { if (dststart < 2048) { dst[dststart] = U_RREADIDX16(srcstart); m_capture.data_block()->put16(dst[dststart]); dst[dststart + 1] = dst[dststart]; dst[dststart + 2] = dst[dststart]; dst[dststart + 3] = dst[dststart]; dststart += 4; srcstart += 1; } } break; } default: fatalerror("RDP: load_tlut: size = %d\n", m_misc_state.m_ti_size); } m_capture.data_end(); m_tiles[tilenum].sth = rgbaint_t(m_tiles[tilenum].sh, m_tiles[tilenum].sh, m_tiles[tilenum].th, m_tiles[tilenum].th); m_tiles[tilenum].stl = rgbaint_t(m_tiles[tilenum].sl, m_tiles[tilenum].sl, m_tiles[tilenum].tl, m_tiles[tilenum].tl); } void n64_rdp::cmd_set_tile_size(uint64_t w1) { //wait("SetTileSize"); const int32_t tilenum = int32_t(w1 >> 24) & 0x7; m_tiles[tilenum].sl = int32_t(w1 >> 44) & 0xfff; m_tiles[tilenum].tl = int32_t(w1 >> 32) & 0xfff; m_tiles[tilenum].sh = int32_t(w1 >> 12) & 0xfff; m_tiles[tilenum].th = int32_t(w1 >> 0) & 0xfff; m_tiles[tilenum].sth = rgbaint_t(m_tiles[tilenum].sh, m_tiles[tilenum].sh, m_tiles[tilenum].th, m_tiles[tilenum].th); m_tiles[tilenum].stl = rgbaint_t(m_tiles[tilenum].sl, m_tiles[tilenum].sl, m_tiles[tilenum].tl, m_tiles[tilenum].tl); } void n64_rdp::cmd_load_block(uint64_t w1) { //wait("LoadBlock"); n64_tile_t* tile = m_tiles; const int32_t tilenum = int32_t(w1 >> 24) & 0x7; uint16_t* tc = get_tmem16(); int32_t sl = tile[tilenum].sl = int32_t(w1 >> 44) & 0xfff; int32_t tl = tile[tilenum].tl = int32_t(w1 >> 32) & 0xfff; int32_t sh = tile[tilenum].sh = int32_t(w1 >> 12) & 0xfff; const int32_t dxt = int32_t(w1 >> 0) & 0xfff; if (sh < sl) { fatalerror("load_block: sh < sl\n"); } int32_t width = (sh - sl) + 1; width = (width << m_misc_state.m_ti_size) >> 1; if (width & 7) { width = (width & ~7) + 8; } width >>= 3; const int32_t tb = tile[tilenum].tmem << 2; const int32_t tiwinwords = (m_misc_state.m_ti_width << m_misc_state.m_ti_size) >> 2; const int32_t slinwords = (sl << m_misc_state.m_ti_size) >> 2; const uint32_t src = (m_misc_state.m_ti_address >> 1) + (tl * tiwinwords) + slinwords; m_capture.data_begin(); if (dxt != 0) { int32_t j = 0; int32_t t = 0; int32_t oldt = 0; if (tile[tilenum].size != PIXEL_SIZE_32BIT && tile[tilenum].format != FORMAT_YUV) { for (int32_t i = 0; i < width; i ++) { oldt = t; t = ((j >> 11) & 1) ? WORD_XOR_DWORD_SWAP : WORD_ADDR_XOR; if (t != oldt) { i += tile[tilenum].line; } int32_t ptr = tb + (i << 2); int32_t srcptr = src + (i << 2); tc[(ptr ^ t) & 0x7ff] = U_RREADIDX16(srcptr); tc[((ptr + 1) ^ t) & 0x7ff] = U_RREADIDX16(srcptr + 1); tc[((ptr + 2) ^ t) & 0x7ff] = U_RREADIDX16(srcptr + 2); tc[((ptr + 3) ^ t) & 0x7ff] = U_RREADIDX16(srcptr + 3); m_capture.data_block()->put16(U_RREADIDX16(srcptr)); m_capture.data_block()->put16(U_RREADIDX16(srcptr+1)); m_capture.data_block()->put16(U_RREADIDX16(srcptr+2)); m_capture.data_block()->put16(U_RREADIDX16(srcptr+3)); j += dxt; } } else if (tile[tilenum].format == FORMAT_YUV) { for (int32_t i = 0; i < width; i ++) { oldt = t; t = ((j >> 11) & 1) ? WORD_XOR_DWORD_SWAP : WORD_ADDR_XOR; if (t != oldt) { i += tile[tilenum].line; } int32_t ptr = ((tb + (i << 1)) ^ t) & 0x3ff; int32_t srcptr = src + (i << 2); int32_t first = U_RREADIDX16(srcptr); int32_t sec = U_RREADIDX16(srcptr + 1); tc[ptr] = ((first >> 8) << 8) | (sec >> 8); tc[ptr | 0x400] = ((first & 0xff) << 8) | (sec & 0xff); ptr = ((tb + (i << 1) + 1) ^ t) & 0x3ff; first = U_RREADIDX16(srcptr + 2); sec = U_RREADIDX16(srcptr + 3); tc[ptr] = ((first >> 8) << 8) | (sec >> 8); tc[ptr | 0x400] = ((first & 0xff) << 8) | (sec & 0xff); m_capture.data_block()->put16(U_RREADIDX16(srcptr)); m_capture.data_block()->put16(U_RREADIDX16(srcptr+1)); m_capture.data_block()->put16(U_RREADIDX16(srcptr+2)); m_capture.data_block()->put16(U_RREADIDX16(srcptr+3)); j += dxt; } } else { for (int32_t i = 0; i < width; i ++) { oldt = t; t = ((j >> 11) & 1) ? WORD_XOR_DWORD_SWAP : WORD_ADDR_XOR; if (t != oldt) i += tile[tilenum].line; int32_t ptr = ((tb + (i << 1)) ^ t) & 0x3ff; int32_t srcptr = src + (i << 2); tc[ptr] = U_RREADIDX16(srcptr); tc[ptr | 0x400] = U_RREADIDX16(srcptr + 1); ptr = ((tb + (i << 1) + 1) ^ t) & 0x3ff; tc[ptr] = U_RREADIDX16(srcptr + 2); tc[ptr | 0x400] = U_RREADIDX16(srcptr + 3); m_capture.data_block()->put16(U_RREADIDX16(srcptr)); m_capture.data_block()->put16(U_RREADIDX16(srcptr+1)); m_capture.data_block()->put16(U_RREADIDX16(srcptr+2)); m_capture.data_block()->put16(U_RREADIDX16(srcptr+3)); j += dxt; } } tile[tilenum].th = tl + (j >> 11); } else { if (tile[tilenum].size != PIXEL_SIZE_32BIT && tile[tilenum].format != FORMAT_YUV) { for (int32_t i = 0; i < width; i ++) { int32_t ptr = tb + (i << 2); int32_t srcptr = src + (i << 2); tc[(ptr ^ WORD_ADDR_XOR) & 0x7ff] = U_RREADIDX16(srcptr); tc[((ptr + 1) ^ WORD_ADDR_XOR) & 0x7ff] = U_RREADIDX16(srcptr + 1); tc[((ptr + 2) ^ WORD_ADDR_XOR) & 0x7ff] = U_RREADIDX16(srcptr + 2); tc[((ptr + 3) ^ WORD_ADDR_XOR) & 0x7ff] = U_RREADIDX16(srcptr + 3); m_capture.data_block()->put16(U_RREADIDX16(srcptr)); m_capture.data_block()->put16(U_RREADIDX16(srcptr+1)); m_capture.data_block()->put16(U_RREADIDX16(srcptr+2)); m_capture.data_block()->put16(U_RREADIDX16(srcptr+3)); } } else if (tile[tilenum].format == FORMAT_YUV) { for (int32_t i = 0; i < width; i ++) { int32_t ptr = ((tb + (i << 1)) ^ WORD_ADDR_XOR) & 0x3ff; int32_t srcptr = src + (i << 2); int32_t first = U_RREADIDX16(srcptr); int32_t sec = U_RREADIDX16(srcptr + 1); tc[ptr] = ((first >> 8) << 8) | (sec >> 8);//UV pair tc[ptr | 0x400] = ((first & 0xff) << 8) | (sec & 0xff); ptr = ((tb + (i << 1) + 1) ^ WORD_ADDR_XOR) & 0x3ff; first = U_RREADIDX16(srcptr + 2); sec = U_RREADIDX16(srcptr + 3); tc[ptr] = ((first >> 8) << 8) | (sec >> 8); tc[ptr | 0x400] = ((first & 0xff) << 8) | (sec & 0xff); m_capture.data_block()->put16(U_RREADIDX16(srcptr)); m_capture.data_block()->put16(U_RREADIDX16(srcptr+1)); m_capture.data_block()->put16(U_RREADIDX16(srcptr+2)); m_capture.data_block()->put16(U_RREADIDX16(srcptr+3)); } } else { for (int32_t i = 0; i < width; i ++) { int32_t ptr = ((tb + (i << 1)) ^ WORD_ADDR_XOR) & 0x3ff; int32_t srcptr = src + (i << 2); tc[ptr] = U_RREADIDX16(srcptr); tc[ptr | 0x400] = U_RREADIDX16(srcptr + 1); ptr = ((tb + (i << 1) + 1) ^ WORD_ADDR_XOR) & 0x3ff; tc[ptr] = U_RREADIDX16(srcptr + 2); tc[ptr | 0x400] = U_RREADIDX16(srcptr + 3); m_capture.data_block()->put16(U_RREADIDX16(srcptr)); m_capture.data_block()->put16(U_RREADIDX16(srcptr+1)); m_capture.data_block()->put16(U_RREADIDX16(srcptr+2)); m_capture.data_block()->put16(U_RREADIDX16(srcptr+3)); } } tile[tilenum].th = tl; } m_capture.data_end(); m_tiles[tilenum].sth = rgbaint_t(m_tiles[tilenum].sh, m_tiles[tilenum].sh, m_tiles[tilenum].th, m_tiles[tilenum].th); m_tiles[tilenum].stl = rgbaint_t(m_tiles[tilenum].sl, m_tiles[tilenum].sl, m_tiles[tilenum].tl, m_tiles[tilenum].tl); } void n64_rdp::cmd_load_tile(uint64_t w1) { //wait("LoadTile"); n64_tile_t* tile = m_tiles; const int32_t tilenum = int32_t(w1 >> 24) & 0x7; tile[tilenum].sl = int32_t(w1 >> 44) & 0xfff; tile[tilenum].tl = int32_t(w1 >> 32) & 0xfff; tile[tilenum].sh = int32_t(w1 >> 12) & 0xfff; tile[tilenum].th = int32_t(w1 >> 0) & 0xfff; const int32_t sl = tile[tilenum].sl >> 2; const int32_t tl = tile[tilenum].tl >> 2; const int32_t sh = tile[tilenum].sh >> 2; const int32_t th = tile[tilenum].th >> 2; const int32_t width = (sh - sl) + 1; const int32_t height = (th - tl) + 1; /* int32_t topad; if (m_misc_state.m_ti_size < 3) { topad = (width * m_misc_state.m_ti_size) & 0x7; } else { topad = (width << 2) & 0x7; } topad = 0; // ???? */ m_capture.data_begin(); switch (m_misc_state.m_ti_size) { case PIXEL_SIZE_8BIT: { const uint32_t src = m_misc_state.m_ti_address; const int32_t tb = tile[tilenum].tmem << 3; uint8_t* tc = get_tmem8(); for (int32_t j = 0; j < height; j++) { const int32_t tline = tb + ((tile[tilenum].line << 3) * j); const int32_t s = ((j + tl) * m_misc_state.m_ti_width) + sl; const int32_t xorval8 = ((j & 1) ? BYTE_XOR_DWORD_SWAP : BYTE_ADDR_XOR); for (int32_t i = 0; i < width; i++) { const uint8_t data = U_RREADADDR8(src + s + i); m_capture.data_block()->put8(data); tc[((tline + i) ^ xorval8) & 0xfff] = data; } } break; } case PIXEL_SIZE_16BIT: { const uint32_t src = m_misc_state.m_ti_address >> 1; uint16_t* tc = get_tmem16(); if (tile[tilenum].format != FORMAT_YUV) { for (int32_t j = 0; j < height; j++) { const int32_t tb = tile[tilenum].tmem << 2; const int32_t tline = tb + ((tile[tilenum].line << 2) * j); const int32_t s = ((j + tl) * m_misc_state.m_ti_width) + sl; const int32_t xorval16 = (j & 1) ? WORD_XOR_DWORD_SWAP : WORD_ADDR_XOR; for (int32_t i = 0; i < width; i++) { const uint32_t taddr = (tline + i) ^ xorval16; const uint16_t data = U_RREADIDX16(src + s + i); m_capture.data_block()->put16(data); tc[taddr & 0x7ff] = data; } } } else { for (int32_t j = 0; j < height; j++) { const int32_t tb = tile[tilenum].tmem << 3; const int32_t tline = tb + ((tile[tilenum].line << 3) * j); const int32_t s = ((j + tl) * m_misc_state.m_ti_width) + sl; const int32_t xorval8 = (j & 1) ? BYTE_XOR_DWORD_SWAP : BYTE_ADDR_XOR; for (int32_t i = 0; i < width; i++) { uint32_t taddr = ((tline + i) ^ xorval8) & 0x7ff; uint16_t yuvword = U_RREADIDX16(src + s + i); m_capture.data_block()->put16(yuvword); get_tmem8()[taddr] = yuvword >> 8; get_tmem8()[taddr | 0x800] = yuvword & 0xff; } } } break; } case PIXEL_SIZE_32BIT: { const uint32_t src = m_misc_state.m_ti_address >> 2; const int32_t tb = (tile[tilenum].tmem << 2); uint16_t* tc16 = get_tmem16(); for (int32_t j = 0; j < height; j++) { const int32_t tline = tb + ((tile[tilenum].line << 2) * j); const int32_t s = ((j + tl) * m_misc_state.m_ti_width) + sl; const int32_t xorval32cur = (j & 1) ? WORD_XOR_DWORD_SWAP : WORD_ADDR_XOR; for (int32_t i = 0; i < width; i++) { uint32_t c = U_RREADIDX32(src + s + i); m_capture.data_block()->put32(c); uint32_t ptr = ((tline + i) ^ xorval32cur) & 0x3ff; tc16[ptr] = c >> 16; tc16[ptr | 0x400] = c & 0xffff; } } break; } default: fatalerror("RDP: load_tile: size = %d\n", m_misc_state.m_ti_size); } m_capture.data_end(); m_tiles[tilenum].sth = rgbaint_t(m_tiles[tilenum].sh, m_tiles[tilenum].sh, m_tiles[tilenum].th, m_tiles[tilenum].th); m_tiles[tilenum].stl = rgbaint_t(m_tiles[tilenum].sl, m_tiles[tilenum].sl, m_tiles[tilenum].tl, m_tiles[tilenum].tl); } void n64_rdp::cmd_set_tile(uint64_t w1) { //wait("SetTile"); const int32_t tilenum = int32_t(w1 >> 24) & 0x7; n64_tile_t* tex_tile = &m_tiles[tilenum]; tex_tile->format = int32_t(w1 >> 53) & 0x7; tex_tile->size = int32_t(w1 >> 51) & 0x3; tex_tile->line = int32_t(w1 >> 41) & 0x1ff; tex_tile->tmem = int32_t(w1 >> 32) & 0x1ff; tex_tile->palette = int32_t(w1 >> 20) & 0xf; tex_tile->ct = int32_t(w1 >> 19) & 0x1; tex_tile->mt = int32_t(w1 >> 18) & 0x1; tex_tile->mask_t = int32_t(w1 >> 14) & 0xf; tex_tile->shift_t = int32_t(w1 >> 10) & 0xf; tex_tile->cs = int32_t(w1 >> 9) & 0x1; tex_tile->ms = int32_t(w1 >> 8) & 0x1; tex_tile->mask_s = int32_t(w1 >> 4) & 0xf; tex_tile->shift_s = int32_t(w1 >> 0) & 0xf; tex_tile->lshift_s = (tex_tile->shift_s >= 11) ? (16 - tex_tile->shift_s) : 0; tex_tile->rshift_s = (tex_tile->shift_s < 11) ? tex_tile->shift_s : 0; tex_tile->lshift_t = (tex_tile->shift_t >= 11) ? (16 - tex_tile->shift_t) : 0; tex_tile->rshift_t = (tex_tile->shift_t < 11) ? tex_tile->shift_t : 0; tex_tile->wrapped_mask_s = (tex_tile->mask_s > 10 ? 10 : tex_tile->mask_s); tex_tile->wrapped_mask_t = (tex_tile->mask_t > 10 ? 10 : tex_tile->mask_t); tex_tile->wrapped_mask = rgbaint_t(tex_tile->wrapped_mask_s, tex_tile->wrapped_mask_s, tex_tile->wrapped_mask_t, tex_tile->wrapped_mask_t); tex_tile->clamp_s = tex_tile->cs || !tex_tile->mask_s; tex_tile->clamp_t = tex_tile->ct || !tex_tile->mask_t; tex_tile->mm = rgbaint_t(tex_tile->ms ? ~0 : 0, tex_tile->ms ? ~0 : 0, tex_tile->mt ? ~0 : 0, tex_tile->mt ? ~0 : 0); tex_tile->invmm = rgbaint_t(tex_tile->ms ? 0 : ~0, tex_tile->ms ? 0 : ~0, tex_tile->mt ? 0 : ~0, tex_tile->mt ? 0 : ~0); tex_tile->mask = rgbaint_t(tex_tile->mask_s ? ~0 : 0, tex_tile->mask_s ? ~0 : 0, tex_tile->mask_t ? ~0 : 0, tex_tile->mask_t ? ~0 : 0); tex_tile->invmask = rgbaint_t(tex_tile->mask_s ? 0 : ~0, tex_tile->mask_s ? 0 : ~0, tex_tile->mask_t ? 0 : ~0, tex_tile->mask_t ? 0 : ~0); tex_tile->lshift = rgbaint_t(tex_tile->lshift_s, tex_tile->lshift_s, tex_tile->lshift_t, tex_tile->lshift_t); tex_tile->rshift = rgbaint_t(tex_tile->rshift_s, tex_tile->rshift_s, tex_tile->rshift_t, tex_tile->rshift_t); tex_tile->clamp_st = rgbaint_t(tex_tile->clamp_s ? ~0 : 0, tex_tile->clamp_s ? ~0 : 0, tex_tile->clamp_t ? ~0 : 0, tex_tile->clamp_t ? ~0 : 0); if (tex_tile->format == FORMAT_I && tex_tile->size > PIXEL_SIZE_8BIT) { tex_tile->format = FORMAT_RGBA; // Used by Supercross 2000 (in-game) } if (tex_tile->format == FORMAT_CI && tex_tile->size > PIXEL_SIZE_8BIT) { tex_tile->format = FORMAT_RGBA; // Used by Clay Fighter - Sculptor's Cut } if (tex_tile->format == FORMAT_RGBA && tex_tile->size < PIXEL_SIZE_16BIT) { tex_tile->format = FORMAT_CI; // Used by Exterem-G2, Madden Football 64, and Rat Attack } //m_pending_mode_block = true; } void n64_rdp::cmd_fill_rect(uint64_t w1) { //if(m_pending_mode_block) { wait("Block on pending mode-change"); m_pending_mode_block = false; } const uint64_t xh = (w1 >> 12) & 0xfff; const uint64_t xl = (w1 >> 44) & 0xfff; const uint64_t yh = (w1 >> 0) & 0xfff; uint64_t yl = (w1 >> 32) & 0xfff; if (m_other_modes.cycle_type == CYCLE_TYPE_FILL || m_other_modes.cycle_type == CYCLE_TYPE_COPY) { yl |= 3; } const uint64_t xlint = (xl >> 2) & 0x3ff; const uint64_t xhint = (xh >> 2) & 0x3ff; uint64_t* ewdata = m_temp_rect_data; ewdata[0] = ((uint64_t)0x3680 << 48) | (yl << 32) | (yl << 16) | yh; // command, flipped, tile, yl, ym, yh ewdata[1] = (xlint << 48) | ((xl & 3) << 46); // xl, xl frac, dxldy (0), dxldy frac (0) ewdata[2] = (xhint << 48) | ((xh & 3) << 46); // xh, xh frac, dxhdy (0), dxhdy frac (0) ewdata[3] = (xlint << 48) | ((xl & 3) << 46); // xm, xm frac, dxmdy (0), dxmdy frac (0) memset(&ewdata[4], 0, 18 * sizeof(uint64_t));//shade, texture, depth draw_triangle(false, false, false, true); } void n64_rdp::cmd_set_fog_color(uint64_t w1) { m_fog_color.set(uint8_t(w1), uint8_t(w1 >> 24), uint8_t(w1 >> 16), uint8_t(w1 >> 8)); } void n64_rdp::cmd_set_blend_color(uint64_t w1) { m_blend_color.set(uint8_t(w1), uint8_t(w1 >> 24), uint8_t(w1 >> 16), uint8_t(w1 >> 8)); } void n64_rdp::cmd_set_prim_color(uint64_t w1) { m_misc_state.m_min_level = uint32_t(w1 >> 40) & 0x1f; const uint8_t prim_lod_fraction(w1 >> 32); m_prim_lod_fraction.set(prim_lod_fraction, prim_lod_fraction, prim_lod_fraction, prim_lod_fraction); const uint8_t alpha(w1); m_prim_color.set(alpha, uint8_t(w1 >> 24), uint8_t(w1 >> 16), uint8_t(w1 >> 8)); m_prim_alpha.set(alpha, alpha, alpha, alpha); } void n64_rdp::cmd_set_env_color(uint64_t w1) { const uint8_t alpha(w1); m_env_color.set(alpha, uint8_t(w1 >> 24), uint8_t(w1 >> 16), uint8_t(w1 >> 8)); m_env_alpha.set(alpha, alpha, alpha, alpha); } void n64_rdp::cmd_set_combine(uint64_t w1) { m_combine.sub_a_rgb0 = uint32_t(w1 >> 52) & 0xf; m_combine.mul_rgb0 = uint32_t(w1 >> 47) & 0x1f; m_combine.sub_a_a0 = uint32_t(w1 >> 44) & 0x7; m_combine.mul_a0 = uint32_t(w1 >> 41) & 0x7; m_combine.sub_a_rgb1 = uint32_t(w1 >> 37) & 0xf; m_combine.mul_rgb1 = uint32_t(w1 >> 32) & 0x1f; m_combine.sub_b_rgb0 = uint32_t(w1 >> 28) & 0xf; m_combine.sub_b_rgb1 = uint32_t(w1 >> 24) & 0xf; m_combine.sub_a_a1 = uint32_t(w1 >> 21) & 0x7; m_combine.mul_a1 = uint32_t(w1 >> 18) & 0x7; m_combine.add_rgb0 = uint32_t(w1 >> 15) & 0x7; m_combine.sub_b_a0 = uint32_t(w1 >> 12) & 0x7; m_combine.add_a0 = uint32_t(w1 >> 9) & 0x7; m_combine.add_rgb1 = uint32_t(w1 >> 6) & 0x7; m_combine.sub_b_a1 = uint32_t(w1 >> 3) & 0x7; m_combine.add_a1 = uint32_t(w1 >> 0) & 0x7; } void n64_rdp::cmd_set_texture_image(uint64_t w1) { m_misc_state.m_ti_format = uint32_t(w1 >> 53) & 0x7; m_misc_state.m_ti_size = uint32_t(w1 >> 51) & 0x3; m_misc_state.m_ti_width = (uint32_t(w1 >> 32) & 0x3ff) + 1; m_misc_state.m_ti_address = uint32_t(w1) & 0x01ffffff; } void n64_rdp::cmd_set_mask_image(uint64_t w1) { //wait("SetMaskImage"); m_misc_state.m_zb_address = uint32_t(w1) & 0x01ffffff; } void n64_rdp::cmd_set_color_image(uint64_t w1) { //wait("SetColorImage"); m_misc_state.m_fb_format = uint32_t(w1 >> 53) & 0x7; m_misc_state.m_fb_size = uint32_t(w1 >> 51) & 0x3; m_misc_state.m_fb_width = (uint32_t(w1 >> 32) & 0x3ff) + 1; m_misc_state.m_fb_address = uint32_t(w1) & 0x01ffffff; if (m_misc_state.m_fb_format < 2 || m_misc_state.m_fb_format > 32) // Jet Force Gemini sets the format to 4, Intensity. Protection? { m_misc_state.m_fb_format = 2; } } /*****************************************************************************/ void n64_rdp::cmd_invalid(uint64_t w1) { fatalerror("n64_rdp::Invalid: %d, %08x %08x\n", uint32_t(w1 >> 56) & 0x3f, uint32_t(w1 >> 32), (uint32_t)w1); } void n64_rdp::cmd_noop(uint64_t w1) { // Do nothing } void n64_rdp::process_command_list() { int32_t length = m_end - m_current; if(length < 0) { m_current = m_end; return; } // load command data for(int32_t i = 0; i < length; i += 8) { m_cmd_data[m_cmd_ptr++] = read_data((m_current & 0x1fffffff) + i); } m_current = m_end; uint32_t cmd = (m_cmd_data[0] >> 56) & 0x3f; uint32_t cmd_length = uint32_t(m_cmd_ptr + 1) * 8; set_status(get_status() &~ DP_STATUS_FREEZE); // check if more data is needed if (cmd_length < s_rdp_command_length[cmd]) { return; } while (m_cmd_cur < m_cmd_ptr) { cmd = (m_cmd_data[m_cmd_cur] >> 56) & 0x3f; if (((m_cmd_ptr - m_cmd_cur) * 8) < s_rdp_command_length[cmd]) { return; //fatalerror("rdp_process_list: not enough rdp command data: cur = %d, ptr = %d, expected = %d\n", m_cmd_cur, m_cmd_ptr, s_rdp_command_length[cmd]); } m_capture.command(&m_cmd_data[m_cmd_cur], s_rdp_command_length[cmd] / 8); if (LOG_RDP_EXECUTION) { char string[4000]; disassemble(string); fprintf(rdp_exec, "%08X: %08X%08X %s\n", m_start+(m_cmd_cur * 8), uint32_t(m_cmd_data[m_cmd_cur] >> 32), (uint32_t)m_cmd_data[m_cmd_cur], string); fflush(rdp_exec); } // execute the command uint64_t w = m_cmd_data[m_cmd_cur]; switch(cmd) { case 0x00: cmd_noop(w); break; case 0x08: cmd_triangle(w); break; case 0x09: cmd_triangle_z(w); break; case 0x0a: cmd_triangle_t(w); break; case 0x0b: cmd_triangle_tz(w); break; case 0x0c: cmd_triangle_s(w); break; case 0x0d: cmd_triangle_sz(w); break; case 0x0e: cmd_triangle_st(w); break; case 0x0f: cmd_triangle_stz(w); break; case 0x24: cmd_tex_rect(w); break; case 0x25: cmd_tex_rect_flip(w); break; case 0x26: cmd_sync_load(w); break; case 0x27: cmd_sync_pipe(w); break; case 0x28: cmd_sync_tile(w); break; case 0x29: cmd_sync_full(w); break; case 0x2a: cmd_set_key_gb(w); break; case 0x2b: cmd_set_key_r(w); break; case 0x2c: cmd_set_convert(w); break; case 0x3c: cmd_set_combine(w); break; case 0x2d: cmd_set_scissor(w); break; case 0x2e: cmd_set_prim_depth(w); break; case 0x2f: cmd_set_other_modes(w);break; case 0x30: cmd_load_tlut(w); break; case 0x33: cmd_load_block(w); break; case 0x34: cmd_load_tile(w); break; case 0x32: cmd_set_tile_size(w); break; case 0x35: cmd_set_tile(w); break; case 0x36: cmd_fill_rect(w); break; case 0x37: cmd_set_fill_color32(w); break; case 0x38: cmd_set_fog_color(w); break; case 0x39: cmd_set_blend_color(w);break; case 0x3a: cmd_set_prim_color(w); break; case 0x3b: cmd_set_env_color(w); break; case 0x3d: cmd_set_texture_image(w); break; case 0x3e: cmd_set_mask_image(w); break; case 0x3f: cmd_set_color_image(w); break; } m_cmd_cur += s_rdp_command_length[cmd] / 8; }; m_cmd_ptr = 0; m_cmd_cur = 0; m_start = m_current = m_end; } /*****************************************************************************/ n64_rdp::n64_rdp(n64_state &state, uint32_t* rdram, uint32_t* dmem) : poly_manager(state.machine()) { ignore = false; dolog = false; m_rdram = rdram; m_dmem = dmem; m_aux_buf_ptr = 0; m_aux_buf = nullptr; m_pipe_clean = true; m_pending_mode_block = false; m_cmd_ptr = 0; m_cmd_cur = 0; m_start = 0; m_end = 0; m_current = 0; m_status = 0x88; m_one.set(0xff, 0xff, 0xff, 0xff); m_zero.set(0, 0, 0, 0); m_tmem = nullptr; m_machine = nullptr; m_n64_periphs = nullptr; //memset(m_hidden_bits, 3, 8388608); m_prim_lod_fraction.set(0, 0, 0, 0); z_build_com_table(); memset(m_temp_rect_data, 0, sizeof(uint32_t) * 0x1000); for (int32_t i = 0; i < 0x4000; i++) { uint32_t exponent = (i >> 11) & 7; uint32_t mantissa = i & 0x7ff; m_z_complete_dec_table[i] = ((mantissa << m_z_dec_table[exponent].shift) + m_z_dec_table[exponent].add) & 0x3fffff; } precalc_cvmask_derivatives(); for(int32_t i = 0; i < 0x200; i++) { switch((i >> 7) & 3) { case 0: case 1: s_special_9bit_clamptable[i] = i & 0xff; break; case 2: s_special_9bit_clamptable[i] = 0xff; break; case 3: s_special_9bit_clamptable[i] = 0; break; } } for(int32_t i = 0; i < 32; i++) { m_replicated_rgba[i] = (i << 3) | ((i >> 2) & 7); } for(int32_t i = 0; i < 0x10000; i++) { m_dzpix_normalize[i] = (uint16_t)normalize_dzpix(i & 0xffff); } m_compute_cvg[0] = &n64_rdp::compute_cvg_noflip; m_compute_cvg[1] = &n64_rdp::compute_cvg_flip; } void n64_rdp::render_spans(int32_t start, int32_t end, int32_t tilenum, bool flip, extent_t* spans, bool rect, rdp_poly_state* object) { const int32_t clipy1 = m_scissor.m_yh; const int32_t clipy2 = m_scissor.m_yl; const rectangle clip(m_scissor.m_xh, m_scissor.m_xl, m_scissor.m_yh, m_scissor.m_yl); int32_t offset = 0; if (clipy2 <= 0) { return; } if (start < clipy1) { offset = clipy1 - start; start = clipy1; } if (start >= clipy2) { offset = start - (clipy2 - 1); start = clipy2 - 1; } if (end < clipy1) { end = clipy1; } if (end >= clipy2) { end = clipy2 - 1; } object->m_rdp = this; memcpy(&object->m_misc_state, &m_misc_state, sizeof(misc_state_t)); memcpy(&object->m_other_modes, &m_other_modes, sizeof(other_modes_t)); memcpy(&object->m_span_base, &m_span_base, sizeof(span_base_t)); memcpy(&object->m_scissor, &m_scissor, sizeof(rectangle_t)); memcpy(&object->m_tiles, &m_tiles, 8 * sizeof(n64_tile_t)); object->tilenum = tilenum; object->flip = flip; object->m_fill_color = m_fill_color; object->rect = rect; switch(m_other_modes.cycle_type) { case CYCLE_TYPE_1: render_triangle_custom(clip, render_delegate(&n64_rdp::span_draw_1cycle, this), start, (end - start) + 1, spans + offset); break; case CYCLE_TYPE_2: render_triangle_custom(clip, render_delegate(&n64_rdp::span_draw_2cycle, this), start, (end - start) + 1, spans + offset); break; case CYCLE_TYPE_COPY: render_triangle_custom(clip, render_delegate(&n64_rdp::span_draw_copy, this), start, (end - start) + 1, spans + offset); break; case CYCLE_TYPE_FILL: render_triangle_custom(clip, render_delegate(&n64_rdp::span_draw_fill, this), start, (end - start) + 1, spans + offset); break; } wait("render spans"); } void n64_rdp::rgbaz_clip(int32_t sr, int32_t sg, int32_t sb, int32_t sa, int32_t* sz, rdp_span_aux* userdata) { userdata->m_shade_color.set(sa, sr, sg, sb); userdata->m_shade_color.clamp_and_clear(0xfffffe00); uint32_t a = userdata->m_shade_color.get_a(); userdata->m_shade_alpha.set(a, a, a, a); int32_t zanded = (*sz) & 0x60000; zanded >>= 17; switch(zanded) { case 0: *sz &= 0x3ffff; break; case 1: *sz &= 0x3ffff; break; case 2: *sz = 0x3ffff; break; case 3: *sz = 0x3ffff; break; } } void n64_rdp::rgbaz_correct_triangle(int32_t offx, int32_t offy, int32_t* r, int32_t* g, int32_t* b, int32_t* a, int32_t* z, rdp_span_aux* userdata, const rdp_poly_state &object) { if (userdata->m_current_pix_cvg == 8) { *r >>= 2; *g >>= 2; *b >>= 2; *a >>= 2; *z = (*z >> 3) & 0x7ffff; } else { int32_t summand_xr = offx * SIGN13(object.m_span_base.m_span_dr >> 14); int32_t summand_yr = offy * SIGN13(object.m_span_base.m_span_drdy >> 14); int32_t summand_xb = offx * SIGN13(object.m_span_base.m_span_db >> 14); int32_t summand_yb = offy * SIGN13(object.m_span_base.m_span_dbdy >> 14); int32_t summand_xg = offx * SIGN13(object.m_span_base.m_span_dg >> 14); int32_t summand_yg = offy * SIGN13(object.m_span_base.m_span_dgdy >> 14); int32_t summand_xa = offx * SIGN13(object.m_span_base.m_span_da >> 14); int32_t summand_ya = offy * SIGN13(object.m_span_base.m_span_dady >> 14); int32_t summand_xz = offx * SIGN22(object.m_span_base.m_span_dz >> 10); int32_t summand_yz = offy * SIGN22(object.m_span_base.m_span_dzdy >> 10); *r = ((*r << 2) + summand_xr + summand_yr) >> 4; *g = ((*g << 2) + summand_xg + summand_yg) >> 4; *b = ((*b << 2) + summand_xb + summand_yb) >> 4; *a = ((*a << 2) + summand_xa + summand_ya) >> 4; *z = (((*z << 2) + summand_xz + summand_yz) >> 5) & 0x7ffff; } } inline void n64_rdp::write_pixel(uint32_t curpixel, color_t& color, rdp_span_aux* userdata, const rdp_poly_state &object) { if (object.m_misc_state.m_fb_size == 2) // 16-bit framebuffer { const uint32_t fb = (object.m_misc_state.m_fb_address >> 1) + curpixel; uint16_t finalcolor; if (object.m_other_modes.color_on_cvg && !userdata->m_pre_wrap) { finalcolor = RREADIDX16(fb) & 0xfffe; } else { color.shr_imm(3); finalcolor = (color.get_r() << 11) | (color.get_g() << 6) | (color.get_b() << 1); } switch (object.m_other_modes.cvg_dest) { case 0: if (userdata->m_blend_enable) { uint32_t finalcvg = userdata->m_current_pix_cvg + userdata->m_current_mem_cvg; if (finalcvg & 8) { finalcvg = 7; } RWRITEIDX16(fb, finalcolor | (finalcvg >> 2)); HWRITEADDR8(fb, finalcvg & 3); } else { const uint32_t finalcvg = (userdata->m_current_pix_cvg - 1) & 7; RWRITEIDX16(fb, finalcolor | (finalcvg >> 2)); HWRITEADDR8(fb, finalcvg & 3); } break; case 1: { const uint32_t finalcvg = (userdata->m_current_pix_cvg + userdata->m_current_mem_cvg) & 7; RWRITEIDX16(fb, finalcolor | (finalcvg >> 2)); HWRITEADDR8(fb, finalcvg & 3); break; } case 2: RWRITEIDX16(fb, finalcolor | 1); HWRITEADDR8(fb, 3); break; case 3: RWRITEIDX16(fb, finalcolor | (userdata->m_current_mem_cvg >> 2)); HWRITEADDR8(fb, userdata->m_current_mem_cvg & 3); break; } } else // 32-bit framebuffer { const uint32_t fb = (object.m_misc_state.m_fb_address >> 2) + curpixel; uint32_t finalcolor; if (object.m_other_modes.color_on_cvg && !userdata->m_pre_wrap) { finalcolor = RREADIDX32(fb) & 0xffffff00; } else { finalcolor = (color.get_r() << 24) | (color.get_g() << 16) | (color.get_b() << 8); } switch (object.m_other_modes.cvg_dest) { case 0: if (userdata->m_blend_enable) { uint32_t finalcvg = userdata->m_current_pix_cvg + userdata->m_current_mem_cvg; if (finalcvg & 8) { finalcvg = 7; } RWRITEIDX32(fb, finalcolor | (finalcvg << 5)); } else { RWRITEIDX32(fb, finalcolor | (((userdata->m_current_pix_cvg - 1) & 7) << 5)); } break; case 1: RWRITEIDX32(fb, finalcolor | (((userdata->m_current_pix_cvg + userdata->m_current_mem_cvg) & 7) << 5)); break; case 2: RWRITEIDX32(fb, finalcolor | 0xE0); break; case 3: RWRITEIDX32(fb, finalcolor | (userdata->m_current_mem_cvg << 5)); break; } } } inline void n64_rdp::read_pixel(uint32_t curpixel, rdp_span_aux* userdata, const rdp_poly_state &object) { if (object.m_misc_state.m_fb_size == 2) // 16-bit framebuffer { const uint16_t fword = RREADIDX16((object.m_misc_state.m_fb_address >> 1) + curpixel); userdata->m_memory_color.set(0, GETHICOL(fword), GETMEDCOL(fword), GETLOWCOL(fword)); if (object.m_other_modes.image_read_en) { uint8_t hbyte = HREADADDR8((object.m_misc_state.m_fb_address >> 1) + curpixel); userdata->m_memory_color.set_a(userdata->m_current_mem_cvg << 5); userdata->m_current_mem_cvg = ((fword & 1) << 2) | (hbyte & 3); } else { userdata->m_memory_color.set_a(0xff); userdata->m_current_mem_cvg = 7; } } else // 32-bit framebuffer { const uint32_t mem = RREADIDX32((object.m_misc_state.m_fb_address >> 2) + curpixel); userdata->m_memory_color.set(0, (mem >> 24) & 0xff, (mem >> 16) & 0xff, (mem >> 8) & 0xff); if (object.m_other_modes.image_read_en) { userdata->m_memory_color.set_a(mem & 0xff); userdata->m_current_mem_cvg = (mem >> 5) & 7; } else { userdata->m_memory_color.set_a(0xff); userdata->m_current_mem_cvg = 7; } } } inline void n64_rdp::copy_pixel(uint32_t curpixel, color_t& color, const rdp_poly_state &object) { const uint32_t current_pix_cvg = color.get_a() ? 7 : 0; const uint8_t r = color.get_r(); // Vectorize me const uint8_t g = color.get_g(); const uint8_t b = color.get_b(); if (object.m_misc_state.m_fb_size == 2) // 16-bit framebuffer { RWRITEIDX16((object.m_misc_state.m_fb_address >> 1) + curpixel, ((r >> 3) << 11) | ((g >> 3) << 6) | ((b >> 3) << 1) | ((current_pix_cvg >> 2) & 1)); HWRITEADDR8((object.m_misc_state.m_fb_address >> 1) + curpixel, current_pix_cvg & 3); } else // 32-bit framebuffer { RWRITEIDX32((object.m_misc_state.m_fb_address >> 2) + curpixel, (r << 24) | (g << 16) | (b << 8) | (current_pix_cvg << 5)); } } inline void n64_rdp::fill_pixel(uint32_t curpixel, const rdp_poly_state &object) { if (object.m_misc_state.m_fb_size == 2) // 16-bit framebuffer { uint16_t val; if (curpixel & 1) { val = object.m_fill_color & 0xffff; } else { val = (object.m_fill_color >> 16) & 0xffff; } RWRITEIDX16((object.m_misc_state.m_fb_address >> 1) + curpixel, val); HWRITEADDR8((object.m_misc_state.m_fb_address >> 1) + curpixel, ((val & 1) << 1) | (val & 1)); } else // 32-bit framebuffer { RWRITEIDX32((object.m_misc_state.m_fb_address >> 2) + curpixel, object.m_fill_color); HWRITEADDR8((object.m_misc_state.m_fb_address >> 1) + (curpixel << 1), (object.m_fill_color & 0x10000) ? 3 : 0); HWRITEADDR8((object.m_misc_state.m_fb_address >> 1) + (curpixel << 1) + 1, (object.m_fill_color & 0x1) ? 3 : 0); } } void n64_rdp::span_draw_1cycle(int32_t scanline, const extent_t &extent, const rdp_poly_state &object, int32_t threadid) { assert(object.m_misc_state.m_fb_size >= 2 && object.m_misc_state.m_fb_size < 4); const int32_t clipx1 = object.m_scissor.m_xh; const int32_t clipx2 = object.m_scissor.m_xl; const int32_t tilenum = object.tilenum; const bool flip = object.flip; span_param_t r; r.w = extent.param[SPAN_R].start; span_param_t g; g.w = extent.param[SPAN_G].start; span_param_t b; b.w = extent.param[SPAN_B].start; span_param_t a; a.w = extent.param[SPAN_A].start; span_param_t z; z.w = extent.param[SPAN_Z].start; span_param_t s; s.w = extent.param[SPAN_S].start; span_param_t t; t.w = extent.param[SPAN_T].start; span_param_t w; w.w = extent.param[SPAN_W].start; const uint32_t zb = object.m_misc_state.m_zb_address >> 1; const uint32_t zhb = object.m_misc_state.m_zb_address; #ifdef PTR64 assert(extent.userdata != (const void *)0xcccccccccccccccc); #else assert(extent.userdata != (const void *)0xcccccccc); #endif rdp_span_aux* userdata = (rdp_span_aux*)extent.userdata; m_tex_pipe.calculate_clamp_diffs(tilenum, userdata, object); const bool partialreject = (userdata->m_color_inputs.blender2b_a[0] == &userdata->m_inv_pixel_color && userdata->m_color_inputs.blender1b_a[0] == &userdata->m_pixel_color); const int32_t sel0 = (userdata->m_color_inputs.blender2b_a[0] == &userdata->m_memory_color) ? 1 : 0; int32_t drinc, dginc, dbinc, dainc; int32_t dzinc, dzpix; int32_t dsinc, dtinc, dwinc; int32_t xinc; if (!flip) { drinc = -object.m_span_base.m_span_dr; dginc = -object.m_span_base.m_span_dg; dbinc = -object.m_span_base.m_span_db; dainc = -object.m_span_base.m_span_da; dzinc = -object.m_span_base.m_span_dz; dsinc = -object.m_span_base.m_span_ds; dtinc = -object.m_span_base.m_span_dt; dwinc = -object.m_span_base.m_span_dw; xinc = -1; } else { drinc = object.m_span_base.m_span_dr; dginc = object.m_span_base.m_span_dg; dbinc = object.m_span_base.m_span_db; dainc = object.m_span_base.m_span_da; dzinc = object.m_span_base.m_span_dz; dsinc = object.m_span_base.m_span_ds; dtinc = object.m_span_base.m_span_dt; dwinc = object.m_span_base.m_span_dw; xinc = 1; } const int32_t fb_index = object.m_misc_state.m_fb_width * scanline; const int32_t xstart = extent.startx; const int32_t xend = userdata->m_unscissored_rx; const int32_t xend_scissored = extent.stopx; int32_t x = xend; const int32_t length = flip ? (xstart - xend) : (xend - xstart); if(object.m_other_modes.z_source_sel) { z.w = object.m_misc_state.m_primitive_z; dzpix = object.m_misc_state.m_primitive_dz; dzinc = 0; } else { dzpix = object.m_span_base.m_span_dzpix; } if (object.m_misc_state.m_fb_size < 2 || object.m_misc_state.m_fb_size > 4) fatalerror("unsupported m_fb_size %d\n", object.m_misc_state.m_fb_size); const int32_t blend_index = (object.m_other_modes.alpha_cvg_select ? 2 : 0) | ((object.m_other_modes.rgb_dither_sel < 3) ? 1 : 0); const int32_t cycle0 = ((object.m_other_modes.sample_type & 1) << 1) | (object.m_other_modes.bi_lerp0 & 1); int32_t sss = 0; int32_t sst = 0; if (object.m_other_modes.persp_tex_en) { tc_div(s.w >> 16, t.w >> 16, w.w >> 16, &sss, &sst); } else { tc_div_no_perspective(s.w >> 16, t.w >> 16, w.w >> 16, &sss, &sst); } userdata->m_start_span = true; for (int32_t j = 0; j <= length; j++) { int32_t sr = r.w >> 14; int32_t sg = g.w >> 14; int32_t sb = b.w >> 14; int32_t sa = a.w >> 14; int32_t sz = (z.w >> 10) & 0x3fffff; const bool valid_x = (flip) ? (x >= xend_scissored) : (x <= xend_scissored); if (x >= clipx1 && x < clipx2 && valid_x) { uint8_t offx, offy; lookup_cvmask_derivatives(userdata->m_cvg[x], &offx, &offy, userdata); m_tex_pipe.lod_1cycle(&sss, &sst, s.w, t.w, w.w, dsinc, dtinc, dwinc, userdata, object); rgbaz_correct_triangle(offx, offy, &sr, &sg, &sb, &sa, &sz, userdata, object); rgbaz_clip(sr, sg, sb, sa, &sz, userdata); ((m_tex_pipe).*(m_tex_pipe.m_cycle[cycle0]))(&userdata->m_texel0_color, &userdata->m_texel0_color, sss, sst, tilenum, 0, userdata, object); uint32_t t0a = userdata->m_texel0_color.get_a(); userdata->m_texel0_alpha.set(t0a, t0a, t0a, t0a); const uint8_t noise = machine().rand() << 3; // Not accurate userdata->m_noise_color.set(0, noise, noise, noise); rgbaint_t rgbsub_a(*userdata->m_color_inputs.combiner_rgbsub_a[1]); rgbaint_t rgbsub_b(*userdata->m_color_inputs.combiner_rgbsub_b[1]); rgbaint_t rgbmul(*userdata->m_color_inputs.combiner_rgbmul[1]); rgbaint_t rgbadd(*userdata->m_color_inputs.combiner_rgbadd[1]); rgbsub_a.merge_alpha(*userdata->m_color_inputs.combiner_alphasub_a[1]); rgbsub_b.merge_alpha(*userdata->m_color_inputs.combiner_alphasub_b[1]); rgbmul.merge_alpha(*userdata->m_color_inputs.combiner_alphamul[1]); rgbadd.merge_alpha(*userdata->m_color_inputs.combiner_alphaadd[1]); rgbsub_a.sign_extend(0x180, 0xfffffe00); rgbsub_b.sign_extend(0x180, 0xfffffe00); rgbadd.sign_extend(0x180, 0xfffffe00); rgbadd.shl_imm(8); rgbsub_a.sub(rgbsub_b); rgbsub_a.mul(rgbmul); rgbsub_a.add(rgbadd); rgbsub_a.add_imm(0x0080); rgbsub_a.sra_imm(8); rgbsub_a.clamp_and_clear(0xfffffe00); userdata->m_pixel_color = rgbsub_a; //Alpha coverage combiner userdata->m_pixel_color.set_a(get_alpha_cvg(userdata->m_pixel_color.get_a(), userdata, object)); const uint32_t curpixel = fb_index + x; const uint32_t zbcur = zb + curpixel; const uint32_t zhbcur = zhb + curpixel; read_pixel(curpixel, userdata, object); if(z_compare(zbcur, zhbcur, sz, dzpix, userdata, object)) { int32_t cdith = 0; int32_t adith = 0; get_dither_values(scanline, j, &cdith, &adith, object); color_t blended_pixel; bool rendered = ((&m_blender)->*(m_blender.blend1[(userdata->m_blend_enable << 2) | blend_index]))(blended_pixel, cdith, adith, partialreject, sel0, userdata, object); if (rendered) { write_pixel(curpixel, blended_pixel, userdata, object); if (object.m_other_modes.z_update_en) { z_store(object, zbcur, zhbcur, sz, userdata->m_dzpix_enc); } } } sss = userdata->m_precomp_s; sst = userdata->m_precomp_t; } r.w += drinc; g.w += dginc; b.w += dbinc; a.w += dainc; s.w += dsinc; t.w += dtinc; w.w += dwinc; z.w += dzinc; x += xinc; } } void n64_rdp::span_draw_2cycle(int32_t scanline, const extent_t &extent, const rdp_poly_state &object, int32_t threadid) { assert(object.m_misc_state.m_fb_size >= 2 && object.m_misc_state.m_fb_size < 4); const int32_t clipx1 = object.m_scissor.m_xh; const int32_t clipx2 = object.m_scissor.m_xl; const int32_t tilenum = object.tilenum; const bool flip = object.flip; span_param_t r; r.w = extent.param[SPAN_R].start; span_param_t g; g.w = extent.param[SPAN_G].start; span_param_t b; b.w = extent.param[SPAN_B].start; span_param_t a; a.w = extent.param[SPAN_A].start; span_param_t z; z.w = extent.param[SPAN_Z].start; span_param_t s; s.w = extent.param[SPAN_S].start; span_param_t t; t.w = extent.param[SPAN_T].start; span_param_t w; w.w = extent.param[SPAN_W].start; const uint32_t zb = object.m_misc_state.m_zb_address >> 1; const uint32_t zhb = object.m_misc_state.m_zb_address; int32_t tile2 = (tilenum + 1) & 7; int32_t tile1 = tilenum; const uint32_t prim_tile = tilenum; int32_t newtile1 = tile1; int32_t news = 0; int32_t newt = 0; #ifdef PTR64 assert(extent.userdata != (const void *)0xcccccccccccccccc); #else assert(extent.userdata != (const void *)0xcccccccc); #endif rdp_span_aux* userdata = (rdp_span_aux*)extent.userdata; m_tex_pipe.calculate_clamp_diffs(tile1, userdata, object); bool partialreject = (userdata->m_color_inputs.blender2b_a[1] == &userdata->m_inv_pixel_color && userdata->m_color_inputs.blender1b_a[1] == &userdata->m_pixel_color); int32_t sel0 = (userdata->m_color_inputs.blender2b_a[0] == &userdata->m_memory_color) ? 1 : 0; int32_t sel1 = (userdata->m_color_inputs.blender2b_a[1] == &userdata->m_memory_color) ? 1 : 0; int32_t drinc, dginc, dbinc, dainc; int32_t dzinc, dzpix; int32_t dsinc, dtinc, dwinc; int32_t xinc; if (!flip) { drinc = -object.m_span_base.m_span_dr; dginc = -object.m_span_base.m_span_dg; dbinc = -object.m_span_base.m_span_db; dainc = -object.m_span_base.m_span_da; dzinc = -object.m_span_base.m_span_dz; dsinc = -object.m_span_base.m_span_ds; dtinc = -object.m_span_base.m_span_dt; dwinc = -object.m_span_base.m_span_dw; xinc = -1; } else { drinc = object.m_span_base.m_span_dr; dginc = object.m_span_base.m_span_dg; dbinc = object.m_span_base.m_span_db; dainc = object.m_span_base.m_span_da; dzinc = object.m_span_base.m_span_dz; dsinc = object.m_span_base.m_span_ds; dtinc = object.m_span_base.m_span_dt; dwinc = object.m_span_base.m_span_dw; xinc = 1; } const int32_t fb_index = object.m_misc_state.m_fb_width * scanline; int32_t cdith = 0; int32_t adith = 0; const int32_t xstart = extent.startx; const int32_t xend = userdata->m_unscissored_rx; const int32_t xend_scissored = extent.stopx; int32_t x = xend; const int32_t length = flip ? (xstart - xend) : (xend - xstart); if(object.m_other_modes.z_source_sel) { z.w = object.m_misc_state.m_primitive_z; dzpix = object.m_misc_state.m_primitive_dz; dzinc = 0; } else { dzpix = object.m_span_base.m_span_dzpix; } if (object.m_misc_state.m_fb_size < 2 || object.m_misc_state.m_fb_size > 4) fatalerror("unsupported m_fb_size %d\n", object.m_misc_state.m_fb_size); const int32_t blend_index = (object.m_other_modes.alpha_cvg_select ? 2 : 0) | ((object.m_other_modes.rgb_dither_sel < 3) ? 1 : 0); const int32_t cycle0 = ((object.m_other_modes.sample_type & 1) << 1) | (object.m_other_modes.bi_lerp0 & 1); const int32_t cycle1 = ((object.m_other_modes.sample_type & 1) << 1) | (object.m_other_modes.bi_lerp1 & 1); int32_t sss = 0; int32_t sst = 0; if (object.m_other_modes.persp_tex_en) { tc_div(s.w >> 16, t.w >> 16, w.w >> 16, &sss, &sst); } else { tc_div_no_perspective(s.w >> 16, t.w >> 16, w.w >> 16, &sss, &sst); } userdata->m_start_span = true; for (int32_t j = 0; j <= length; j++) { int32_t sr = r.w >> 14; int32_t sg = g.w >> 14; int32_t sb = b.w >> 14; int32_t sa = a.w >> 14; int32_t sz = (z.w >> 10) & 0x3fffff; const bool valid_x = (flip) ? (x >= xend_scissored) : (x <= xend_scissored); if (x >= clipx1 && x < clipx2 && valid_x) { const uint32_t compidx = m_compressed_cvmasks[userdata->m_cvg[x]]; userdata->m_current_pix_cvg = cvarray[compidx].cvg; userdata->m_current_cvg_bit = cvarray[compidx].cvbit; const uint8_t offx = cvarray[compidx].xoff; const uint8_t offy = cvarray[compidx].yoff; //lookup_cvmask_derivatives(userdata->m_cvg[x], &offx, &offy, userdata); m_tex_pipe.lod_2cycle(&sss, &sst, s.w, t.w, w.w, dsinc, dtinc, dwinc, prim_tile, &tile1, &tile2, userdata, object); news = userdata->m_precomp_s; newt = userdata->m_precomp_t; m_tex_pipe.lod_2cycle_limited(&news, &newt, s.w + dsinc, t.w + dtinc, w.w + dwinc, dsinc, dtinc, dwinc, prim_tile, &newtile1, object); rgbaz_correct_triangle(offx, offy, &sr, &sg, &sb, &sa, &sz, userdata, object); rgbaz_clip(sr, sg, sb, sa, &sz, userdata); ((m_tex_pipe).*(m_tex_pipe.m_cycle[cycle0]))(&userdata->m_texel0_color, &userdata->m_texel0_color, sss, sst, tile1, 0, userdata, object); ((m_tex_pipe).*(m_tex_pipe.m_cycle[cycle1]))(&userdata->m_texel1_color, &userdata->m_texel0_color, sss, sst, tile2, 1, userdata, object); ((m_tex_pipe).*(m_tex_pipe.m_cycle[cycle1]))(&userdata->m_next_texel_color, &userdata->m_next_texel_color, sss, sst, tile2, 1, userdata, object); uint32_t t0a = userdata->m_texel0_color.get_a(); uint32_t t1a = userdata->m_texel1_color.get_a(); uint32_t tna = userdata->m_next_texel_color.get_a(); userdata->m_texel0_alpha.set(t0a, t0a, t0a, t0a); userdata->m_texel1_alpha.set(t1a, t1a, t1a, t1a); userdata->m_next_texel_alpha.set(tna, tna, tna, tna); const uint8_t noise = machine().rand() << 3; // Not accurate userdata->m_noise_color.set(0, noise, noise, noise); rgbaint_t rgbsub_a(*userdata->m_color_inputs.combiner_rgbsub_a[0]); rgbaint_t rgbsub_b(*userdata->m_color_inputs.combiner_rgbsub_b[0]); rgbaint_t rgbmul(*userdata->m_color_inputs.combiner_rgbmul[0]); rgbaint_t rgbadd(*userdata->m_color_inputs.combiner_rgbadd[0]); rgbsub_a.merge_alpha(*userdata->m_color_inputs.combiner_alphasub_a[0]); rgbsub_b.merge_alpha(*userdata->m_color_inputs.combiner_alphasub_b[0]); rgbmul.merge_alpha(*userdata->m_color_inputs.combiner_alphamul[0]); rgbadd.merge_alpha(*userdata->m_color_inputs.combiner_alphaadd[0]); rgbsub_a.sign_extend(0x180, 0xfffffe00); rgbsub_b.sign_extend(0x180, 0xfffffe00); rgbadd.sign_extend(0x180, 0xfffffe00); rgbadd.shl_imm(8); rgbsub_a.sub(rgbsub_b); rgbsub_a.mul(rgbmul); rgbsub_a.add(rgbadd); rgbsub_a.add_imm(0x0080); rgbsub_a.sra_imm(8); rgbsub_a.clamp_and_clear(0xfffffe00); userdata->m_combined_color.set(rgbsub_a); userdata->m_texel0_color.set(userdata->m_texel1_color); userdata->m_texel1_color.set(userdata->m_next_texel_color); uint32_t ca = userdata->m_combined_color.get_a(); userdata->m_combined_alpha.set(ca, ca, ca, ca); userdata->m_texel0_alpha.set(userdata->m_texel1_alpha); userdata->m_texel1_alpha.set(userdata->m_next_texel_alpha); rgbsub_a.set(*userdata->m_color_inputs.combiner_rgbsub_a[1]); rgbsub_b.set(*userdata->m_color_inputs.combiner_rgbsub_b[1]); rgbmul.set(*userdata->m_color_inputs.combiner_rgbmul[1]); rgbadd.set(*userdata->m_color_inputs.combiner_rgbadd[1]); rgbsub_a.merge_alpha(*userdata->m_color_inputs.combiner_alphasub_a[1]); rgbsub_b.merge_alpha(*userdata->m_color_inputs.combiner_alphasub_b[1]); rgbmul.merge_alpha(*userdata->m_color_inputs.combiner_alphamul[1]); rgbadd.merge_alpha(*userdata->m_color_inputs.combiner_alphaadd[1]); rgbsub_a.sign_extend(0x180, 0xfffffe00); rgbsub_b.sign_extend(0x180, 0xfffffe00); rgbadd.sign_extend(0x180, 0xfffffe00); rgbadd.shl_imm(8); rgbsub_a.sub(rgbsub_b); rgbsub_a.mul(rgbmul); rgbsub_a.add(rgbadd); rgbsub_a.add_imm(0x0080); rgbsub_a.sra_imm(8); rgbsub_a.clamp_and_clear(0xfffffe00); userdata->m_pixel_color.set(rgbsub_a); //Alpha coverage combiner userdata->m_pixel_color.set_a(get_alpha_cvg(userdata->m_pixel_color.get_a(), userdata, object)); const uint32_t curpixel = fb_index + x; const uint32_t zbcur = zb + curpixel; const uint32_t zhbcur = zhb + curpixel; read_pixel(curpixel, userdata, object); if(z_compare(zbcur, zhbcur, sz, dzpix, userdata, object)) { get_dither_values(scanline, j, &cdith, &adith, object); color_t blended_pixel; bool rendered = ((&m_blender)->*(m_blender.blend2[(userdata->m_blend_enable << 2) | blend_index]))(blended_pixel, cdith, adith, partialreject, sel0, sel1, userdata, object); if (rendered) { write_pixel(curpixel, blended_pixel, userdata, object); if (object.m_other_modes.z_update_en) { z_store(object, zbcur, zhbcur, sz, userdata->m_dzpix_enc); } } } sss = userdata->m_precomp_s; sst = userdata->m_precomp_t; } r.w += drinc; g.w += dginc; b.w += dbinc; a.w += dainc; s.w += dsinc; t.w += dtinc; w.w += dwinc; z.w += dzinc; x += xinc; } } void n64_rdp::span_draw_copy(int32_t scanline, const extent_t &extent, const rdp_poly_state &object, int32_t threadid) { const int32_t clipx1 = object.m_scissor.m_xh; const int32_t clipx2 = object.m_scissor.m_xl; const int32_t tilenum = object.tilenum; const bool flip = object.flip; rdp_span_aux* userdata = (rdp_span_aux*)extent.userdata; const int32_t xstart = extent.startx; const int32_t xend = userdata->m_unscissored_rx; const int32_t xend_scissored = extent.stopx; const int32_t xinc = flip ? 1 : -1; const int32_t length = flip ? (xstart - xend) : (xend - xstart); span_param_t s; s.w = extent.param[SPAN_S].start; span_param_t t; t.w = extent.param[SPAN_T].start; const int32_t ds = object.m_span_base.m_span_ds / 4; const int32_t dt = object.m_span_base.m_span_dt / 4; const int32_t dsinc = flip ? (ds) : -ds; const int32_t dtinc = flip ? (dt) : -dt; const int32_t fb_index = object.m_misc_state.m_fb_width * scanline; int32_t x = xend; for (int32_t j = 0; j <= length; j++) { const bool valid_x = (flip) ? (x >= xend_scissored) : (x <= xend_scissored); if (x >= clipx1 && x < clipx2 && valid_x) { int32_t sss = s.h.h; int32_t sst = t.h.h; m_tex_pipe.copy(&userdata->m_texel0_color, sss, sst, tilenum, object, userdata); uint32_t curpixel = fb_index + x; if ((userdata->m_texel0_color.get_a() != 0) || (!object.m_other_modes.alpha_compare_en)) { copy_pixel(curpixel, userdata->m_texel0_color, object); } } s.w += dsinc; t.w += dtinc; x += xinc; } } void n64_rdp::span_draw_fill(int32_t scanline, const extent_t &extent, const rdp_poly_state &object, int32_t threadid) { assert(object.m_misc_state.m_fb_size >= 2 && object.m_misc_state.m_fb_size < 4); const bool flip = object.flip; const int32_t clipx1 = object.m_scissor.m_xh; const int32_t clipx2 = object.m_scissor.m_xl; const int32_t xinc = flip ? 1 : -1; const int32_t fb_index = object.m_misc_state.m_fb_width * scanline; const int32_t xstart = extent.startx; const int32_t xend_scissored = extent.stopx; int32_t x = xend_scissored; const int32_t length = flip ? (xstart - xend_scissored) : (xend_scissored - xstart); for (int32_t j = 0; j <= length; j++) { if (x >= clipx1 && x < clipx2) { fill_pixel(fb_index + x, object); } x += xinc; } }