// license:BSD-3-Clause
// copyright-holders:Ryan Holtz
/******************************************************************************
SGI/Nintendo Reality Display Texture Fetch Unit (TF)
-------------------
by Ryan Holtz
based on initial C code by Ville Linde
contains additional improvements from angrylion, Ziggy, Gonetz and Orkin
******************************************************************************/
#include "emu.h"
#include "includes/n64.h"
#include "video/rdptpipe.h"
#include "video/n64.h"
#include "video/rgbutil.h"
#define RELATIVE(x, y) ((((x) >> 3) - (y)) << 3) | (x & 7);
void n64_texture_pipe_t::set_machine(running_machine &machine)
{
n64_state* state = machine.driver_data<n64_state>();
m_rdp = state->rdp();
for(int32_t i = 0; i < 0x10000; i++)
{
m_expand_16to32_table[i] = color_t((i & 1) ? 0xff : 0x00, m_rdp->m_replicated_rgba[(i >> 11) & 0x1f], m_rdp->m_replicated_rgba[(i >> 6) & 0x1f], m_rdp->m_replicated_rgba[(i >> 1) & 0x1f]);
}
for(uint32_t i = 0; i < 0x80000; i++)
{
if (i & 0x40000)
{
m_lod_lookup[i] = 0x7fff;
}
else if (i & 0x20000)
{
m_lod_lookup[i] = 0x8000;
}
else
{
if ((i & 0x18000) == 0x8000)
{
m_lod_lookup[i] = 0x7fff;
}
else if ((i & 0x18000) == 0x10000)
{
m_lod_lookup[i] = 0x8000;
}
else
{
m_lod_lookup[i] = i & 0xffff;
}
}
}
m_st2_add.set(1, 0, 1, 0);
m_v1.set(1, 1, 1, 1);
}
void n64_texture_pipe_t::mask(rgbaint_t& sstt, const n64_tile_t& tile)
{
uint32_t s_mask_bits = m_maskbits_table[tile.mask_s];
uint32_t t_mask_bits = m_maskbits_table[tile.mask_t];
rgbaint_t maskbits(s_mask_bits, s_mask_bits, t_mask_bits, t_mask_bits);
rgbaint_t do_wrap(sstt);
do_wrap.sra(tile.wrapped_mask);
do_wrap.and_reg(m_v1);
do_wrap.cmpeq(m_v1);
do_wrap.and_reg(tile.mm);
rgbaint_t wrapped(sstt);
wrapped.xor_reg(do_wrap);
wrapped.and_reg(maskbits);
wrapped.and_reg(tile.mask);
sstt.and_reg(tile.invmask);
sstt.or_reg(wrapped);
}
rgbaint_t n64_texture_pipe_t::shift_cycle(rgbaint_t& st, const n64_tile_t& tile)
{
st.sign_extend(0x00008000, 0xffff8000);
st.sra(tile.rshift);
st.shl(tile.lshift);
rgbaint_t maxst(st);
maxst.sra_imm(3);
rgbaint_t maxst_eq(maxst);
maxst.cmpgt(tile.sth);
maxst_eq.cmpeq(tile.sth);
maxst.or_reg(maxst_eq);
rgbaint_t stlsb(st);
stlsb.and_imm(7);
st.sra_imm(3);
st.sub(tile.stl);
st.shl_imm(3);
st.or_reg(stlsb);
return maxst;
}
inline void n64_texture_pipe_t::shift_copy(rgbaint_t& st, const n64_tile_t& tile)
{
st.shr(tile.rshift);
st.shl(tile.lshift);
}
void n64_texture_pipe_t::clamp_cycle(rgbaint_t& st, rgbaint_t& stfrac, rgbaint_t& maxst, const int32_t tilenum, const n64_tile_t& tile, rdp_span_aux* userdata)
{
rgbaint_t not_clamp(tile.clamp_st);
not_clamp.xor_imm(0xffffffff);
rgbaint_t highbit_mask(0x10000, 0x10000, 0x10000, 0x10000);
rgbaint_t highbit(st);
highbit.and_reg(highbit_mask);
highbit.cmpeq(highbit_mask);
rgbaint_t not_highbit(highbit);
not_highbit.xor_imm(0xffffffff);
rgbaint_t not_maxst(maxst);
not_maxst.xor_imm(0xffffffff);
not_maxst.and_reg(not_highbit);
not_maxst.or_reg(not_clamp);
rgbaint_t shifted_st(st);
shifted_st.sign_extend(0x00010000, 0xffff0000);
shifted_st.shr_imm(5);
shifted_st.and_imm(0x1fff);
shifted_st.and_reg(not_maxst);
stfrac.and_reg(not_maxst);
rgbaint_t clamp_diff(userdata->m_clamp_diff[tilenum]);
clamp_diff.and_reg(tile.clamp_st);
clamp_diff.and_reg(maxst);
st.set(shifted_st);
st.or_reg(clamp_diff);
}
void n64_texture_pipe_t::clamp_cycle_light(rgbaint_t& st, rgbaint_t& maxst, const int32_t tilenum, const n64_tile_t& tile, rdp_span_aux* userdata)
{
rgbaint_t not_clamp(tile.clamp_st);
not_clamp.xor_imm(0xffffffff);
rgbaint_t highbit_mask(0x10000, 0x10000, 0x10000, 0x10000);
rgbaint_t highbit(st);
highbit.and_reg(highbit_mask);
highbit.cmpeq(highbit_mask);
rgbaint_t not_highbit(highbit);
not_highbit.xor_imm(0xffffffff);
rgbaint_t not_maxst(maxst);
not_maxst.xor_imm(0xffffffff);
not_maxst.and_reg(not_highbit);
not_maxst.or_reg(not_clamp);
rgbaint_t shifted_st(st);
shifted_st.sign_extend(0x00010000, 0xffff0000);
shifted_st.shr_imm(5);
shifted_st.and_imm(0x1fff);
shifted_st.and_reg(not_maxst);
rgbaint_t clamp_diff(userdata->m_clamp_diff[tilenum]);
clamp_diff.and_reg(tile.clamp_st);
clamp_diff.and_reg(maxst);
st.set(shifted_st);
st.or_reg(clamp_diff);
}
void n64_texture_pipe_t::cycle_nearest(color_t* TEX, color_t* prev, int32_t SSS, int32_t SST, uint32_t tilenum, uint32_t cycle, rdp_span_aux* userdata, const rdp_poly_state& object)
{
const n64_tile_t& tile = object.m_tiles[tilenum];
const uint32_t index = (tile.format << 4) | (tile.size << 2) | ((uint32_t) object.m_other_modes.en_tlut << 1) | (uint32_t) object.m_other_modes.tlut_type;
rgbaint_t st(0, SSS, 0, SST);
rgbaint_t maxst = shift_cycle(st, tile);
clamp_cycle_light(st, maxst, tilenum, tile, userdata);
mask(st, tile);
uint32_t tbase = tile.tmem + ((tile.line * st.get_b32()) & 0x1ff);
rgbaint_t t0;
((this)->*(m_texel_fetch[index]))(t0, st.get_r32(), st.get_b32(), tbase, tile.palette, userdata);
if (object.m_other_modes.convert_one && cycle)
{
t0.set(*prev);
}
t0.sign_extend(0x00000100, 0xffffff00);
rgbaint_t k1r(m_rdp->get_k1());
k1r.mul_imm(t0.get_r32());
TEX->set(m_rdp->get_k023());
TEX->mul_imm(t0.get_g32());
TEX->add(k1r);
TEX->add_imm(0x80);
TEX->shr_imm(8);
TEX->add_imm(t0.get_b32());
TEX->and_imm(0x1ff);
}
void n64_texture_pipe_t::cycle_nearest_lerp(color_t* TEX, color_t* prev, int32_t SSS, int32_t SST, uint32_t tilenum, uint32_t cycle, rdp_span_aux* userdata, const rdp_poly_state& object)
{
const n64_tile_t& tile = object.m_tiles[tilenum];
const uint32_t index = (tile.format << 4) | (tile.size << 2) | ((uint32_t) object.m_other_modes.en_tlut << 1) | (uint32_t) object.m_other_modes.tlut_type;
rgbaint_t st(0, SSS, 0, SST);
rgbaint_t maxst = shift_cycle(st, tile);
clamp_cycle_light(st, maxst, tilenum, tile, userdata);
mask(st, tile);
uint32_t tbase = tile.tmem + ((tile.line * st.get_b32()) & 0x1ff);
((this)->*(m_texel_fetch[index]))(*TEX, st.get_r32(), st.get_b32(), tbase, tile.palette, userdata);
}
void n64_texture_pipe_t::cycle_linear(color_t* TEX, color_t* prev, int32_t SSS, int32_t SST, uint32_t tilenum, uint32_t cycle, rdp_span_aux* userdata, const rdp_poly_state& object)
{
const n64_tile_t& tile = object.m_tiles[tilenum];
const uint32_t index = (tile.format << 4) | (tile.size << 2) | ((uint32_t) object.m_other_modes.en_tlut << 1) | (uint32_t) object.m_other_modes.tlut_type;
rgbaint_t st(0, SSS, 0, SST);
rgbaint_t maxst = shift_cycle(st, tile);
clamp_cycle_light(st, maxst, tilenum, tile, userdata);
mask(st, tile);
const uint32_t tbase = tile.tmem + ((tile.line * st.get_b32()) & 0x1ff);
rgbaint_t t0;
((this)->*(m_texel_fetch[index]))(t0, st.get_r32(), st.get_b32(), tbase, tile.palette, userdata);
if (object.m_other_modes.convert_one && cycle)
{
t0.set(*prev);
}
t0.sign_extend(0x00000100, 0xffffff00);
rgbaint_t k1r(m_rdp->get_k1());
k1r.mul_imm(t0.get_r32());
TEX->set(m_rdp->get_k023());
TEX->mul_imm(t0.get_g32());
TEX->add(k1r);
TEX->add_imm(0x80);
TEX->shr_imm(8);
TEX->add_imm(t0.get_b32());
TEX->and_imm(0x1ff);
}
void n64_texture_pipe_t::cycle_linear_lerp(color_t* TEX, color_t* prev, int32_t SSS, int32_t SST, uint32_t tilenum, uint32_t cycle, rdp_span_aux* userdata, const rdp_poly_state& object)
{
const n64_tile_t& tile = object.m_tiles[tilenum];
uint32_t tpal = tile.palette;
uint32_t index = (tile.format << 4) | (tile.size << 2) | ((uint32_t) object.m_other_modes.en_tlut << 1) | (uint32_t) object.m_other_modes.tlut_type;
rgbaint_t sstt(SSS, SSS, SST, SST);
rgbaint_t maxst = shift_cycle(sstt, tile);
rgbaint_t stfrac = sstt;
stfrac.and_imm(0x1f);
clamp_cycle(sstt, stfrac, maxst, tilenum, tile, userdata);
sstt.add(m_st2_add);
mask(sstt, tile);
const uint32_t tbase1 = tile.tmem + ((tile.line * sstt.get_b32()) & 0x1ff);
const uint32_t tbase2 = tile.tmem + ((tile.line * sstt.get_g32()) & 0x1ff);
bool upper = ((stfrac.get_r32() + stfrac.get_b32()) >= 0x20);
rgbaint_t invstf(stfrac);
if (upper)
{
invstf.subr_imm(0x20);
invstf.shl_imm(3);
}
stfrac.shl_imm(3);
bool center = (stfrac.get_r32() == 0x10) && (stfrac.get_b32() == 0x10) && object.m_other_modes.mid_texel;
rgbaint_t t2;
((this)->*(m_texel_fetch[index]))(*TEX, sstt.get_a32(), sstt.get_b32(), tbase1, tpal, userdata);
((this)->*(m_texel_fetch[index]))(t2, sstt.get_r32(), sstt.get_g32(), tbase2, tpal, userdata);
if (!center)
{
if (upper)
{
rgbaint_t t3;
((this)->*(m_texel_fetch[index]))(t3, sstt.get_a32(), sstt.get_g32(), tbase2, tpal, userdata);
TEX->sub(t3);
t2.sub(t3);
TEX->mul_imm(invstf.get_b32());
t2.mul_imm(invstf.get_r32());
TEX->add(t2);
TEX->add_imm(0x0080);
TEX->sra_imm(8);
TEX->add(t3);
}
else
{
rgbaint_t t0;
((this)->*(m_texel_fetch[index]))(t0, sstt.get_r32(), sstt.get_b32(), tbase1, tpal, userdata);
TEX->sub(t0);
t2.sub(t0);
TEX->mul_imm(stfrac.get_r32());
t2.mul_imm(stfrac.get_b32());
TEX->add(t2);
TEX->add_imm(0x80);
TEX->sra_imm(8);
TEX->add(t0);
}
}
else
{
rgbaint_t t0, t3;
((this)->*(m_texel_fetch[index]))(t0, sstt.get_r32(), sstt.get_b32(), tbase1, tpal, userdata);
((this)->*(m_texel_fetch[index]))(t3, sstt.get_a32(), sstt.get_g32(), tbase2, tpal, userdata);
TEX->add(t0);
TEX->add(t2);
TEX->add(t3);
TEX->sra_imm(2);
}
}
void n64_texture_pipe_t::copy(color_t* TEX, int32_t SSS, int32_t SST, uint32_t tilenum, const rdp_poly_state& object, rdp_span_aux* userdata)
{
const n64_tile_t* tiles = object.m_tiles;
const n64_tile_t& tile = tiles[tilenum];
rgbaint_t st(0, SSS, 0, SST);
shift_copy(st, tile);
rgbaint_t stlsb(st);
stlsb.and_imm(7);
st.shr_imm(3);
st.sub(rgbaint_t(0, tile.sl, 0, tile.tl));
st.shl_imm(3);
st.add(stlsb);
st.sign_extend(0x00010000, 0xffff0000);
st.shr_imm(5);
st.and_imm(0x1fff);
mask(st, tile);
const uint32_t index = (tile.format << 4) | (tile.size << 2) | ((uint32_t) object.m_other_modes.en_tlut << 1) | (uint32_t) object.m_other_modes.tlut_type;
const uint32_t tbase = tile.tmem + ((tile.line * st.get_b32()) & 0x1ff);
((this)->*(m_texel_fetch[index]))(*TEX, st.get_r32(), st.get_b32(), tbase, tile.palette, userdata);
}
void n64_texture_pipe_t::lod_1cycle(int32_t* sss, int32_t* sst, const int32_t s, const int32_t t, const int32_t w, const int32_t dsinc, const int32_t dtinc, const int32_t dwinc, rdp_span_aux* userdata, const rdp_poly_state& object)
{
const int32_t nextsw = (w + dwinc) >> 16;
int32_t nexts = (s + dsinc) >> 16;
int32_t nextt = (t + dtinc) >> 16;
if (object.m_other_modes.persp_tex_en)
{
m_rdp->tc_div(nexts, nextt, nextsw, &nexts, &nextt);
}
else
{
m_rdp->tc_div_no_perspective(nexts, nextt, nextsw, &nexts, &nextt);
}
userdata->m_start_span = false;
userdata->m_precomp_s = nexts;
userdata->m_precomp_t = nextt;
const int32_t lodclamp = (((*sst & 0x60000) > 0) | ((nextt & 0x60000) > 0)) || (((*sss & 0x60000) > 0) | ((nexts & 0x60000) > 0));
int32_t horstep = SIGN17(nexts & 0x1ffff) - SIGN17(*sss & 0x1ffff);
int32_t vertstep = SIGN17(nextt & 0x1ffff) - SIGN17(*sst & 0x1ffff);
if (horstep & 0x20000)
{
horstep = ~horstep & 0x1ffff;
}
if (vertstep & 0x20000)
{
vertstep = ~vertstep & 0x1ffff;
}
int32_t lod = (horstep >= vertstep) ? horstep : vertstep;
*sss = m_lod_lookup[*sss & 0x7ffff];
*sst = m_lod_lookup[*sst & 0x7ffff];
if ((lod & 0x4000) || lodclamp)
{
lod = 0x7fff;
}
else if (lod < object.m_misc_state.m_min_level)
{
lod = object.m_misc_state.m_min_level;
}
int32_t l_tile = m_rdp->get_log2((lod >> 5) & 0xff);
const bool magnify = (lod < 32);
const bool distant = ((lod & 0x6000) || (l_tile >= object.m_misc_state.m_max_level));
uint8_t lod_fraction = ((lod << 3) >> l_tile) & 0xff;
if(!object.m_other_modes.sharpen_tex_en && !object.m_other_modes.detail_tex_en)
{
if (distant)
{
lod_fraction = 0xff;
}
else if (magnify)
{
lod_fraction = 0;
}
}
userdata->m_lod_fraction.set(lod_fraction, lod_fraction, lod_fraction, lod_fraction);
/* FIXME: ???
if(object.m_other_modes.sharpen_tex_en && magnify)
{
userdata->m_lod_fraction |= 0x100;
}
*/
}
void n64_texture_pipe_t::lod_2cycle(int32_t* sss, int32_t* sst, const int32_t s, const int32_t t, const int32_t w, const int32_t dsinc, const int32_t dtinc, const int32_t dwinc, const int32_t prim_tile, int32_t* t1, int32_t* t2, rdp_span_aux* userdata, const rdp_poly_state& object)
{
const int32_t nextsw = (w + dwinc) >> 16;
int32_t nexts = (s + dsinc) >> 16;
int32_t nextt = (t + dtinc) >> 16;
if (object.m_other_modes.persp_tex_en)
{
m_rdp->tc_div(nexts, nextt, nextsw, &nexts, &nextt);
}
else
{
m_rdp->tc_div_no_perspective(nexts, nextt, nextsw, &nexts, &nextt);
}
userdata->m_start_span = false;
userdata->m_precomp_s = nexts;
userdata->m_precomp_t = nextt;
const int32_t lodclamp = (((*sst & 0x60000) > 0) | ((nextt & 0x60000) > 0)) || (((*sss & 0x60000) > 0) | ((nexts & 0x60000) > 0));
int32_t horstep = SIGN17(nexts & 0x1ffff) - SIGN17(*sss & 0x1ffff);
int32_t vertstep = SIGN17(nextt & 0x1ffff) - SIGN17(*sst & 0x1ffff);
if (horstep & 0x20000)
{
horstep = ~horstep & 0x1ffff;
}
if (vertstep & 0x20000)
{
vertstep = ~vertstep & 0x1ffff;
}
int32_t lod = (horstep >= vertstep) ? horstep : vertstep;
*sss = m_lod_lookup[*sss & 0x7ffff];
*sst = m_lod_lookup[*sst & 0x7ffff];
if ((lod & 0x4000) || lodclamp)
{
lod = 0x7fff;
}
else if (lod < object.m_misc_state.m_min_level)
{
lod = object.m_misc_state.m_min_level;
}
int32_t l_tile = m_rdp->get_log2((lod >> 5) & 0xff);
const bool magnify = (lod < 32);
const bool distant = ((lod & 0x6000) || (l_tile >= object.m_misc_state.m_max_level));
uint8_t lod_fraction = ((lod << 3) >> l_tile) & 0xff;
if(!object.m_other_modes.sharpen_tex_en && !object.m_other_modes.detail_tex_en)
{
if (distant)
{
lod_fraction = 0xff;
}
else if (magnify)
{
lod_fraction = 0;
}
}
userdata->m_lod_fraction.set(lod_fraction, lod_fraction, lod_fraction, lod_fraction);
/* FIXME: ???
if(object.m_other_modes.sharpen_tex_en && magnify)
{
userdata->m_lod_fraction |= 0x100;
}*/
if (object.m_other_modes.tex_lod_en)
{
if (distant)
{
l_tile = object.m_misc_state.m_max_level;
}
if (!object.m_other_modes.detail_tex_en)
{
*t1 = (prim_tile + l_tile) & 7;
if (!(distant || (!object.m_other_modes.sharpen_tex_en && magnify)))
{
*t2 = (*t1 + 1) & 7;
}
else
{
*t2 = *t1; // World Driver Championship, Stunt Race 64, Beetle Adventure Racing
}
}
else // Beetle Adventure Racing, World Driver Championship (ingame_, NFL Blitz 2001, Pilotwings
{
if (!magnify)
{
*t1 = (prim_tile + l_tile + 1);
}
else
{
*t1 = (prim_tile + l_tile);
}
*t1 &= 7;
if (!distant && !magnify)
{
*t2 = (prim_tile + l_tile + 2) & 7;
}
else
{
*t2 = (prim_tile + l_tile + 1) & 7;
}
}
}
}
void n64_texture_pipe_t::lod_2cycle_limited(int32_t* sss, int32_t* sst, const int32_t s, const int32_t t, const int32_t w, const int32_t dsinc, const int32_t dtinc, const int32_t dwinc, const int32_t prim_tile, int32_t* t1, const rdp_poly_state& object)
{
const int32_t nextsw = (w + dwinc) >> 16;
int32_t nexts = (s + dsinc) >> 16;
int32_t nextt = (t + dtinc) >> 16;
if (object.m_other_modes.persp_tex_en)
{
m_rdp->tc_div(nexts, nextt, nextsw, &nexts, &nextt);
}
else
{
m_rdp->tc_div_no_perspective(nexts, nextt, nextsw, &nexts, &nextt);
}
const int32_t lodclamp = (((*sst & 0x60000) > 0) | ((nextt & 0x60000) > 0)) || (((*sss & 0x60000) > 0) | ((nexts & 0x60000) > 0));
int32_t horstep = SIGN17(nexts & 0x1ffff) - SIGN17(*sss & 0x1ffff);
int32_t vertstep = SIGN17(nextt & 0x1ffff) - SIGN17(*sst & 0x1ffff);
if (horstep & 0x20000)
{
horstep = ~horstep & 0x1ffff;
}
if (vertstep & 0x20000)
{
vertstep = ~vertstep & 0x1ffff;
}
int32_t lod = (horstep >= vertstep) ? horstep : vertstep;
*sss = m_lod_lookup[*sss & 0x7ffff];
*sst = m_lod_lookup[*sst & 0x7ffff];
if ((lod & 0x4000) || lodclamp)
{
lod = 0x7fff;
}
else if (lod < object.m_misc_state.m_min_level)
{
lod = object.m_misc_state.m_min_level;
}
int32_t l_tile = m_rdp->get_log2((lod >> 5) & 0xff);
const bool magnify = (lod < 32);
const bool distant = (lod & 0x6000) || (l_tile >= object.m_misc_state.m_max_level);
if (object.m_other_modes.tex_lod_en)
{
if (distant)
{
l_tile = object.m_misc_state.m_max_level;
}
if (!object.m_other_modes.detail_tex_en)
{
*t1 = (prim_tile + l_tile) & 7;
}
else
{
if (!magnify)
{
*t1 = (prim_tile + l_tile + 1);
}
else
{
*t1 = (prim_tile + l_tile);
}
*t1 &= 7;
}
}
}
void n64_texture_pipe_t::calculate_clamp_diffs(uint32_t prim_tile, rdp_span_aux* userdata, const rdp_poly_state& object)
{
const n64_tile_t* tiles = object.m_tiles;
if (object.m_other_modes.cycle_type == CYCLE_TYPE_2)
{
if (object.m_other_modes.tex_lod_en)
{
for (int32_t start = 0; start <= 7; start++)
{
userdata->m_clamp_diff[start].set((tiles[start].sh >> 2) - (tiles[start].sl >> 2), (tiles[start].sh >> 2) - (tiles[start].sl >> 2), (tiles[start].th >> 2) - (tiles[start].tl >> 2), (tiles[start].th >> 2) - (tiles[start].tl >> 2));
}
}
else
{
const int32_t start = prim_tile;
const int32_t end = (prim_tile + 1) & 7;
userdata->m_clamp_diff[start].set((tiles[start].sh >> 2) - (tiles[start].sl >> 2), (tiles[start].sh >> 2) - (tiles[start].sl >> 2), (tiles[start].th >> 2) - (tiles[start].tl >> 2), (tiles[start].th >> 2) - (tiles[start].tl >> 2));
userdata->m_clamp_diff[end].set((tiles[end].sh >> 2) - (tiles[end].sl >> 2), (tiles[end].sh >> 2) - (tiles[end].sl >> 2), (tiles[end].th >> 2) - (tiles[end].tl >> 2), (tiles[end].th >> 2) - (tiles[end].tl >> 2));
}
}
else//1-cycle or copy
{
userdata->m_clamp_diff[prim_tile].set((tiles[prim_tile].sh >> 2) - (tiles[prim_tile].sl >> 2), (tiles[prim_tile].sh >> 2) - (tiles[prim_tile].sl >> 2), (tiles[prim_tile].th >> 2) - (tiles[prim_tile].tl >> 2), (tiles[prim_tile].th >> 2) - (tiles[prim_tile].tl >> 2));
}
}
#define USE_64K_LUT (1)
static int32_t sTexAddrSwap16[2] = { WORD_ADDR_XOR, WORD_XOR_DWORD_SWAP };
static int32_t sTexAddrSwap8[2] = { BYTE_ADDR_XOR, BYTE_XOR_DWORD_SWAP };
void n64_texture_pipe_t::fetch_rgba16_tlut0(rgbaint_t& out, int32_t s, int32_t t, int32_t tbase, int32_t tpal, rdp_span_aux* userdata)
{
int32_t taddr = (((tbase << 2) + s) ^ sTexAddrSwap16[t & 1]) & 0x7ff;
uint16_t c = ((uint16_t*)userdata->m_tmem)[taddr];
c = ((uint16_t*)(userdata->m_tmem + 0x800))[(c >> 8) << 2];
#if USE_64K_LUT
out.set(m_expand_16to32_table[c]);
#else
out.set((c & 1) * 0xff, GET_HI_RGBA16_TMEM(c), GET_MED_RGBA16_TMEM(c), GET_LOW_RGBA16_TMEM(c));
#endif
}
void n64_texture_pipe_t::fetch_rgba16_tlut1(rgbaint_t& out, int32_t s, int32_t t, int32_t tbase, int32_t tpal, rdp_span_aux* userdata)
{
const int32_t taddr = (((tbase << 2) + s) ^ sTexAddrSwap16[t & 1]) & 0x7ff;
uint16_t c = ((uint16_t*)userdata->m_tmem)[taddr];
c = ((uint16_t*)(userdata->m_tmem + 0x800))[(c >> 8) << 2];
const uint8_t k = (c >> 8) & 0xff;
out.set(c & 0xff, k, k, k);
}
void n64_texture_pipe_t::fetch_rgba16_raw(rgbaint_t& out, int32_t s, int32_t t, int32_t tbase, int32_t tpal, rdp_span_aux* userdata)
{
const int32_t taddr = (((tbase << 2) + s) ^ sTexAddrSwap16[t & 1]) & 0x7ff;
const uint16_t c = ((uint16_t*)userdata->m_tmem)[taddr];
#if USE_64K_LUT
out.set(m_expand_16to32_table[c]);
#else
out.set((c & 1) * 0xff, GET_HI_RGBA16_TMEM(c), GET_MED_RGBA16_TMEM(c), GET_LOW_RGBA16_TMEM(c));
#endif
}
void n64_texture_pipe_t::fetch_rgba32_tlut0(rgbaint_t& out, int32_t s, int32_t t, int32_t tbase, int32_t tpal, rdp_span_aux* userdata)
{
const uint32_t *tc = ((uint32_t*)userdata->m_tmem);
const int32_t taddr = (((tbase << 2) + s) ^ sTexAddrSwap16[t & 1]) & 0x3ff;
uint32_t c = tc[taddr];
c = ((uint16_t*)(userdata->m_tmem + 0x800))[(c >> 24) << 2];
#if USE_64K_LUT
out.set(m_expand_16to32_table[c]);
#else
out.set((c & 1) * 0xff, GET_HI_RGBA16_TMEM(c), GET_MED_RGBA16_TMEM(c), GET_LOW_RGBA16_TMEM(c));
#endif
}
void n64_texture_pipe_t::fetch_rgba32_tlut1(rgbaint_t& out, int32_t s, int32_t t, int32_t tbase, int32_t tpal, rdp_span_aux* userdata)
{
const uint32_t *tc = ((uint32_t*)userdata->m_tmem);
const int32_t taddr = (((tbase << 2) + s) ^ sTexAddrSwap16[t & 1]) & 0x3ff;
uint32_t c = tc[taddr];
c = ((uint16_t*)(userdata->m_tmem + 0x800))[(c >> 24) << 2];
const uint8_t k = (c >> 8) & 0xff;
out.set(c & 0xff, k, k, k);
}
void n64_texture_pipe_t::fetch_rgba32_raw(rgbaint_t& out, int32_t s, int32_t t, int32_t tbase, int32_t tpal, rdp_span_aux* userdata)
{
const int32_t taddr = (((tbase << 2) + s) ^ sTexAddrSwap16[t & 1]) & 0x3ff;
const uint16_t cl = ((uint16_t*)userdata->m_tmem)[taddr];
const uint16_t ch = ((uint16_t*)userdata->m_tmem)[taddr | 0x400];
out.set(ch & 0xff, cl >> 8, cl & 0xff, ch >> 8);
}
void n64_texture_pipe_t::fetch_nop(rgbaint_t& out, int32_t s, int32_t t, int32_t tbase, int32_t tpal, rdp_span_aux* userdata) { }
void n64_texture_pipe_t::fetch_yuv(rgbaint_t& out, int32_t s, int32_t t, int32_t tbase, int32_t tpal, rdp_span_aux* userdata)
{
const uint16_t *tc = ((uint16_t*)userdata->m_tmem);
const int32_t taddr = (((tbase << 3) + s) ^ sTexAddrSwap8[t & 1]) & 0x7ff;
const int32_t taddrlow = ((taddr >> 1) ^ sTexAddrSwap16[t & 1]) & 0x3ff;
const uint16_t c = tc[taddrlow];
int32_t y = userdata->m_tmem[taddr | 0x800];
int32_t u = c >> 8;
int32_t v = c & 0xff;
v ^= 0x80; u ^= 0x80;
u |= ((u & 0x80) << 1);
v |= ((v & 0x80) << 1);
out.set(y & 0xff, y & 0xff, u & 0xff, v & 0xff);
}
void n64_texture_pipe_t::fetch_ci4_tlut0(rgbaint_t& out, int32_t s, int32_t t, int32_t tbase, int32_t tpal, rdp_span_aux* userdata)
{
const uint8_t *tc = userdata->m_tmem;
const int32_t taddr = ((((tbase << 4) + s) >> 1) ^ sTexAddrSwap8[t & 1]) & 0x7ff;
const uint8_t p = (s & 1) ? (tc[taddr] & 0xf) : (tc[taddr] >> 4);
const uint16_t c = ((uint16_t*)(userdata->m_tmem + 0x800))[((tpal << 4) | p) << 2];
#if USE_64K_LUT
out.set(m_expand_16to32_table[c]);
#else
out.set((c & 1) * 0xff, GET_HI_RGBA16_TMEM(c), GET_MED_RGBA16_TMEM(c), GET_LOW_RGBA16_TMEM(c));
#endif
}
void n64_texture_pipe_t::fetch_ci4_tlut1(rgbaint_t& out, int32_t s, int32_t t, int32_t tbase, int32_t tpal, rdp_span_aux* userdata)
{
const uint8_t *tc = userdata->m_tmem;
int32_t taddr = ((((tbase << 4) + s) >> 1) ^ sTexAddrSwap8[t & 1]) & 0x7ff;
const uint8_t p = (s & 1) ? (tc[taddr] & 0xf) : (tc[taddr] >> 4);
const uint16_t c = ((uint16_t*)(userdata->m_tmem + 0x800))[((tpal << 4) | p) << 2];
const uint8_t k = (c >> 8) & 0xff;
out.set(c & 0xff, k, k, k);
}
void n64_texture_pipe_t::fetch_ci4_raw(rgbaint_t& out, int32_t s, int32_t t, int32_t tbase, int32_t tpal, rdp_span_aux* userdata)
{
const uint8_t *tc = userdata->m_tmem;
const int32_t taddr = ((((tbase << 4) + s) >> 1) ^ sTexAddrSwap8[t & 1]) & 0xfff;
uint8_t p = (s & 1) ? (tc[taddr] & 0xf) : (tc[taddr] >> 4);
p = (tpal << 4) | p;
out.set(p, p, p, p);
}
void n64_texture_pipe_t::fetch_ci8_tlut0(rgbaint_t& out, int32_t s, int32_t t, int32_t tbase, int32_t tpal, rdp_span_aux* userdata)
{
const uint8_t *tc = userdata->m_tmem;
const int32_t taddr = (((tbase << 3) + s) ^ sTexAddrSwap8[t & 1]) & 0x7ff;
const uint8_t p = tc[taddr];
const uint16_t c = ((uint16_t*)(userdata->m_tmem + 0x800))[p << 2];
#if USE_64K_LUT
out.set(m_expand_16to32_table[c]);
#else
out.set((c & 1) * 0xff, GET_HI_RGBA16_TMEM(c), GET_MED_RGBA16_TMEM(c), GET_LOW_RGBA16_TMEM(c));
#endif
}
void n64_texture_pipe_t::fetch_ci8_tlut1(rgbaint_t& out, int32_t s, int32_t t, int32_t tbase, int32_t tpal, rdp_span_aux* userdata)
{
const uint8_t *tc = userdata->m_tmem;
const int32_t taddr = (((tbase << 3) + s) ^ sTexAddrSwap8[t & 1]) & 0x7ff;
const uint8_t p = tc[taddr];
const uint16_t c = ((uint16_t*)(userdata->m_tmem + 0x800))[p << 2];
const uint8_t k = (c >> 8) & 0xff;
out.set(c & 0xff, k, k, k);
}
void n64_texture_pipe_t::fetch_ci8_raw(rgbaint_t& out, int32_t s, int32_t t, int32_t tbase, int32_t tpal, rdp_span_aux* userdata)
{
const uint8_t *tc = userdata->m_tmem;
const int32_t taddr = (((tbase << 3) + s) ^ sTexAddrSwap8[t & 1]) & 0xfff;
const uint8_t p = tc[taddr];
out.set(p, p, p, p);
}
void n64_texture_pipe_t::fetch_ia4_tlut0(rgbaint_t& out, int32_t s, int32_t t, int32_t tbase, int32_t tpal, rdp_span_aux* userdata)
{
const uint8_t *tc = userdata->m_tmem;
const int32_t taddr = ((((tbase << 4) + s) >> 1) ^ sTexAddrSwap8[t & 1]) & 0x7ff;
const uint8_t p = ((s) & 1) ? (tc[taddr] & 0xf) : (tc[taddr] >> 4);
const uint16_t c = ((uint16_t*)(userdata->m_tmem + 0x800))[((tpal << 4) | p) << 2];
#if USE_64K_LUT
out.set(m_expand_16to32_table[c]);
#else
out.set((c & 1) * 0xff, GET_HI_RGBA16_TMEM(c), GET_MED_RGBA16_TMEM(c), GET_LOW_RGBA16_TMEM(c));
#endif
}
void n64_texture_pipe_t::fetch_ia4_tlut1(rgbaint_t& out, int32_t s, int32_t t, int32_t tbase, int32_t tpal, rdp_span_aux* userdata)
{
const uint8_t *tc = userdata->m_tmem;
const int32_t taddr = ((((tbase << 4) + s) >> 1) ^ sTexAddrSwap8[t & 1]) & 0x7ff;
const uint8_t p = ((s) & 1) ? (tc[taddr] & 0xf) : (tc[taddr] >> 4);
const uint16_t c = ((uint16_t*)(userdata->m_tmem + 0x800))[((tpal << 4) | p) << 2];
const uint8_t k = (c >> 8) & 0xff;
out.set(c & 0xff, k, k, k);
}
void n64_texture_pipe_t::fetch_ia4_raw(rgbaint_t& out, int32_t s, int32_t t, int32_t tbase, int32_t tpal, rdp_span_aux* userdata)
{
const uint8_t *tc = userdata->m_tmem;
const int32_t taddr = ((((tbase << 4) + s) >> 1) ^ sTexAddrSwap8[t & 1]) & 0xfff;
const uint8_t p = ((s) & 1) ? (tc[taddr] & 0xf) : (tc[taddr] >> 4);
uint8_t i = p & 0xe;
i = (i << 4) | (i << 1) | (i >> 2);
out.set((p & 1) * 0xff, i, i, i);
}
void n64_texture_pipe_t::fetch_ia8_tlut0(rgbaint_t& out, int32_t s, int32_t t, int32_t tbase, int32_t tpal, rdp_span_aux* userdata)
{
const uint8_t *tc = userdata->m_tmem;
const int32_t taddr = (((tbase << 3) + s) ^ sTexAddrSwap8[t & 1]) & 0x7ff;
const uint8_t p = tc[taddr];
const uint16_t c = ((uint16_t*)(userdata->m_tmem + 0x800))[p << 2];
#if USE_64K_LUT
out.set(m_expand_16to32_table[c]);
#else
out.set((c & 1) * 0xff, GET_HI_RGBA16_TMEM(c), GET_MED_RGBA16_TMEM(c), GET_LOW_RGBA16_TMEM(c));
#endif
}
void n64_texture_pipe_t::fetch_ia8_tlut1(rgbaint_t& out, int32_t s, int32_t t, int32_t tbase, int32_t tpal, rdp_span_aux* userdata)
{
const uint8_t *tc = userdata->m_tmem;
const int32_t taddr = (((tbase << 3) + s) ^ sTexAddrSwap8[t & 1]) & 0x7ff;
const uint8_t p = tc[taddr];
const uint16_t c = ((uint16_t*)(userdata->m_tmem + 0x800))[p << 2];
const uint8_t k = (c >> 8) & 0xff;
out.set(c & 0xff, k, k, k);
}
void n64_texture_pipe_t::fetch_ia8_raw(rgbaint_t& out, int32_t s, int32_t t, int32_t tbase, int32_t tpal, rdp_span_aux* userdata)
{
const uint8_t *tc = userdata->m_tmem;
const int32_t taddr = (((tbase << 3) + s) ^ sTexAddrSwap8[t & 1]) & 0xfff;
const uint8_t p = tc[taddr];
uint8_t i = p & 0xf0;
i |= (i >> 4);
out.set(((p << 4) | (p & 0xf)) & 0xff, i, i, i);
}
void n64_texture_pipe_t::fetch_ia16_tlut0(rgbaint_t& out, int32_t s, int32_t t, int32_t tbase, int32_t tpal, rdp_span_aux* userdata)
{
const uint16_t *tc = ((uint16_t*)userdata->m_tmem);
const int32_t taddr = (((tbase << 2) + s) ^ sTexAddrSwap16[t & 1]) & 0x3ff;
uint16_t c = tc[taddr];
c = ((uint16_t*)(userdata->m_tmem + 0x800))[(c >> 8) << 2];
#if USE_64K_LUT
out.set(m_expand_16to32_table[c]);
#else
out.set((c & 1) * 0xff, GET_HI_RGBA16_TMEM(c), GET_MED_RGBA16_TMEM(c), GET_LOW_RGBA16_TMEM(c));
#endif
}
void n64_texture_pipe_t::fetch_ia16_tlut1(rgbaint_t& out, int32_t s, int32_t t, int32_t tbase, int32_t tpal, rdp_span_aux* userdata)
{
const uint16_t *tc = ((uint16_t*)userdata->m_tmem);
const int32_t taddr = (((tbase << 2) + s) ^ sTexAddrSwap16[t & 1]) & 0x3ff;
uint16_t c = tc[taddr];
c = ((uint16_t*)(userdata->m_tmem + 0x800))[(c >> 8) << 2];
const uint8_t k = (c >> 8) & 0xff;
out.set(c & 0xff, k, k, k);
}
void n64_texture_pipe_t::fetch_ia16_raw(rgbaint_t& out, int32_t s, int32_t t, int32_t tbase, int32_t tpal, rdp_span_aux* userdata)
{
const uint16_t *tc = ((uint16_t*)userdata->m_tmem);
const int32_t taddr = (((tbase << 2) + s) ^ sTexAddrSwap16[t & 1]) & 0x7ff;
const uint16_t c = tc[taddr];
const uint8_t i = (c >> 8);
out.set(c & 0xff, i, i, i);
}
void n64_texture_pipe_t::fetch_i4_tlut0(rgbaint_t& out, int32_t s, int32_t t, int32_t tbase, int32_t tpal, rdp_span_aux* userdata)
{
const uint8_t *tc = userdata->m_tmem;
const int32_t taddr = ((((tbase << 4) + s) >> 1) ^ sTexAddrSwap8[t & 1]) & 0x7ff;
const uint8_t byteval = tc[taddr];
const uint8_t c = ((s & 1)) ? (byteval & 0xf) : ((byteval >> 4) & 0xf);
const uint16_t k = ((uint16_t*)(userdata->m_tmem + 0x800))[((tpal << 4) | c) << 2];
#if USE_64K_LUT
out.set(m_expand_16to32_table[k]);
#else
out.set((c & 1) * 0xff, GET_HI_RGBA16_TMEM(c), GET_MED_RGBA16_TMEM(c), GET_LOW_RGBA16_TMEM(c));
#endif
}
void n64_texture_pipe_t::fetch_i4_tlut1(rgbaint_t& out, int32_t s, int32_t t, int32_t tbase, int32_t tpal, rdp_span_aux* userdata)
{
const uint8_t *tc = userdata->m_tmem;
const int32_t taddr = ((((tbase << 4) + s) >> 1) ^ sTexAddrSwap8[t & 1]) & 0x7ff;
const uint8_t byteval = tc[taddr];
const uint8_t c = ((s & 1)) ? (byteval & 0xf) : ((byteval >> 4) & 0xf);
const uint16_t k = ((uint16_t*)(userdata->m_tmem + 0x800))[((tpal << 4) | c) << 2];
const uint8_t i = (k >> 8) & 0xff;
out.set(k & 0xff, i, i, i);
}
void n64_texture_pipe_t::fetch_i4_raw(rgbaint_t& out, int32_t s, int32_t t, int32_t tbase, int32_t tpal, rdp_span_aux* userdata)
{
const uint8_t *tc = userdata->m_tmem;
const int32_t taddr = ((((tbase << 4) + s) >> 1) ^ sTexAddrSwap8[t & 1]) & 0xfff;
const uint8_t byteval = tc[taddr];
uint8_t c = ((s & 1)) ? (byteval & 0xf) : ((byteval >> 4) & 0xf);
c |= (c << 4);
out.set(c, c, c, c);
}
void n64_texture_pipe_t::fetch_i8_tlut0(rgbaint_t& out, int32_t s, int32_t t, int32_t tbase, int32_t tpal, rdp_span_aux* userdata)
{
const uint8_t *tc = userdata->m_tmem;
const int32_t taddr = (((tbase << 3) + s) ^ sTexAddrSwap8[t & 1]) & 0x7ff;
const uint8_t c = tc[taddr];
const uint16_t k = ((uint16_t*)(userdata->m_tmem + 0x800))[c << 2];
#if USE_64K_LUT
out.set(m_expand_16to32_table[k]);
#else
out.set((c & 1) * 0xff, GET_HI_RGBA16_TMEM(c), GET_MED_RGBA16_TMEM(c), GET_LOW_RGBA16_TMEM(c));
#endif
}
void n64_texture_pipe_t::fetch_i8_tlut1(rgbaint_t& out, int32_t s, int32_t t, int32_t tbase, int32_t tpal, rdp_span_aux* userdata)
{
const uint8_t *tc = userdata->m_tmem;
const int32_t taddr = (((tbase << 3) + s) ^ sTexAddrSwap8[t & 1]) & 0x7ff;
const uint8_t c = tc[taddr];
const uint16_t k = ((uint16_t*)(userdata->m_tmem + 0x800))[c << 2];
const uint8_t i = (k >> 8) & 0xff;
out.set(k & 0xff, i, i, i);
}
void n64_texture_pipe_t::fetch_i8_raw(rgbaint_t& out, int32_t s, int32_t t, int32_t tbase, int32_t tpal, rdp_span_aux* userdata)
{
const uint8_t *tc = userdata->m_tmem;
const int32_t taddr = (((tbase << 3) + s) ^ sTexAddrSwap8[t & 1]) & 0xfff;
const uint8_t c = tc[taddr];
out.set(c, c, c, c);
}