// license:BSD-3-Clause
// copyright-holders:Samuele Zannoli
#include "emu.h"
#include "video/poly.h"
#include "bitmap.h"
#include "machine/pic8259.h"
#include "includes/xbox_nv2a.h"
#include <bitset>
//#define LOG_NV2A
#define DEBUG_CHECKS // enable for debugging
const char *vertex_program_disassembler::srctypes[] = { "??", "Rn", "Vn", "Cn" };
const char *vertex_program_disassembler::scaops[] = { "NOP", "IMV", "RCP", "RCC", "RSQ", "EXP", "LOG", "LIT", "???", "???", "???", "???", "???", "???", "???", "???", "???" };
const int vertex_program_disassembler::scapar2[] = { 0, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
const char *vertex_program_disassembler::vecops[] = { "NOP", "MOV", "MUL", "ADD", "MAD", "DP3", "DPH", "DP4", "DST", "MIN", "MAX", "SLT", "SGE", "ARL", "???", "???", "???" };
const int vertex_program_disassembler::vecpar2[] = { 0, 4, 6, 5, 7, 6, 6, 6, 6, 6, 6, 6, 6, 4, 0, 0, 0 };
const char *vertex_program_disassembler::vecouts[] = { "oPos", "???", "???", "oD0", "oD1", "oFog", "oPts", "oB0", "oB1", "oT0", "oT1", "oT2", "oT3" };
const char vertex_program_disassembler::compchar[] = { 'x', 'y', 'z', 'w' };
/*
Each vertex program instruction is a 128 bit word made of the fields:
d f
w b i
o i e
r t l
d s d
+-+-----+-------
|0|31-0 |not used
+-+-----+-------
| |31-29|not used
| +-----+-------
| |28-25|scalar operation
| +-----+-------
| |24-21|vectorial operation
| +-----+-------
| |20-13|index for source constant C[]
| +-----+-------
| |12-9 |input vector index
| +-----+-------
|1| 8 |parameter A:sign
| +-----+-------
| | 7-6 |parameter A:swizzle x
| +-----+-------
| | 5-4 |parameter A:swizzle y
| +-----+-------
| | 3-2 |parameter A:swizzle z
| +-----+-------
| | 1-0 |parameter A:swizzle w
|-+-----+-------
| |31-28|parameter A:parameter Rn index
| +-----+-------
| |27-26|parameter A:input type 1:Rn 2:Vn 3:C[n]
| +-----+-------
| | 25 |parameter B:sign
| +-----+-------
| |24-23|parameter B:swizzle x
| +-----+-------
| |22-21|parameter B:swizzle y
| +-----+-------
| |20-19|parameter B:swizzle z
| +-----+-------
|2|18-17|parameter B:swizzle w
| +-----+-------
| |16-13|parameter B:parameter Rn index
| +-----+-------
| |12-11|parameter B:input type 1:Rn 2:Vn 3:C[n]
| +-----+-------
| | 10 |parameter C:sign
| +-----+-------
| | 9-8 |parameter C:swizzle x
| +-----+-------
| | 7-6 |parameter C:swizzle y
| +-----+-------
| | 5-4 |parameter C:swizzle z
| +-----+-------
| | 3-2 |parameter C:swizzle w
| +-----+-------
| | 1-0 |
|-+ |parameter C:parameter Rn index
| |31-30|
| +-----+-------
| |29-28|parameter C:input type 1:Rn 2:Vn 3:C[n]
| +-----+-------
| |27-24|output Rn mask from vectorial operation
| +-----+-------
| |23-20|output Rn index from vectorial operation
| +-----+-------
| |19-16|output Rn mask from scalar operation
| +-----+-------
|3|15-12|output vector write mask
| +-----+-------
| | 11 |1:output is output vector 0:output is constant C[]
| +-----+-------
| |10-3 |output vector/constant index
| +-----+-------
| | 2 |0:output Rn from vectorial operation 1:output Rn from scalar operation
| +-----+-------
| | 1 |1:add a0x to index for source constant C[]
| +-----+-------
| | 0 |1:end of program
+-+-----+-------
Each vertex program instruction can generate up to three destination values using up to three source values.
The first possible destination is to Rn from a vectorial operation.
The second possible destination is to a vertex shader output or C[n] from a vectorial or scalar operation.
The third possible destination is to Rn from a scalar operation.
*/
void vertex_program_disassembler::decodefields(unsigned int *dwords, int offset, fields &decoded)
{
unsigned int srcbits[3];
int a;
srcbits[0] = ((dwords[1 + offset] & 0x1ff) << 6) | (dwords[2 + offset] >> 26);
srcbits[1] = (dwords[2 + offset] >> 11) & 0x7fff;
srcbits[2] = ((dwords[2 + offset] & 0x7ff) << 4) | (dwords[3 + offset] >> 28);
decoded.ScaOperation = (int)(dwords[1 + offset] >> 25) & 0xf;
decoded.VecOperation = (int)(dwords[1 + offset] >> 21) & 0xf;
decoded.SourceConstantIndex = (int)(dwords[1 + offset] >> 13) & 0xff;
decoded.InputIndex = (int)(dwords[1 + offset] >> 9) & 0xf;
for (a = 0; a < 3; a++)
{
decoded.src[a].Sign = (int)(srcbits[a] >> 14) & 1;
decoded.src[a].SwizzleX = (int)(srcbits[a] >> 12) & 3;
decoded.src[a].SwizzleY = (int)(srcbits[a] >> 10) & 3;
decoded.src[a].SwizzleZ = (int)(srcbits[a] >> 8) & 3;
decoded.src[a].SwizzleW = (int)(srcbits[a] >> 6) & 3;
decoded.src[a].TempIndex = (int)(srcbits[a] >> 2) & 0xf;
decoded.src[a].ParameterType = (int)(srcbits[a] >> 0) & 3;
}
decoded.VecTempWriteMask = (int)(dwords[3 + offset] >> 24) & 0xf;
decoded.VecTempIndex = (int)(dwords[3 + offset] >> 20) & 0xf;
decoded.ScaTempWriteMask = (int)(dwords[3 + offset] >> 16) & 0xf;
decoded.OutputWriteMask = (int)(dwords[3 + offset] >> 12) & 0xf;
decoded.OutputSelect = (int)(dwords[3 + offset] >> 11) & 0x1;
decoded.OutputIndex = (int)(dwords[3 + offset] >> 3) & 0xff;
decoded.MultiplexerControl = (int)(dwords[3 + offset] >> 2) & 0x1;
decoded.Usea0x = (int)(dwords[3 + offset] >> 1) & 0x1;
decoded.EndOfProgram = (int)(dwords[3 + offset] >> 0) & 0x1;
}
int vertex_program_disassembler::disassemble_mask(int mask, char *s)
{
int l;
*s = 0;
if (mask == 15)
return 0;
s[0] = '.';
l = 1;
if ((mask & 8) != 0)
{
s[l] = 'x';
l++;
}
if ((mask & 4) != 0)
{
s[l] = 'y';
l++;
}
if ((mask & 2) != 0)
{
s[l] = 'z';
l++;
}
if ((mask & 1) != 0)
{
s[l] = 'w';
l++;
}
s[l] = 0;
return l;
}
int vertex_program_disassembler::disassemble_swizzle(sourcefields f, char *s)
{
int t, l;
t = 4;
if (f.SwizzleW == 3)
{
t = t - 1;
if (f.SwizzleZ == 2)
{
t = t - 1;
if (f.SwizzleY == 1)
{
t = t - 1;
if (f.SwizzleX == 0)
{
t = t - 1;
}
}
}
}
*s = 0;
if (t == 0)
return 0;
s[0] = '.';
l = 1;
if (t > 0)
{
s[l] = compchar[f.SwizzleX];
l++;
}
if (t > 1)
{
s[l] = compchar[f.SwizzleY];
l++;
}
if (t > 2)
{
s[l] = compchar[f.SwizzleZ];
l++;
}
if (t > 3)
{
s[l] = compchar[f.SwizzleW];
l++;
}
s[l] = 0;
return l;
}
int vertex_program_disassembler::disassemble_source(sourcefields f, fields fi, char *s)
{
int l;
if (f.ParameterType == 0) {
strcpy(s, ",???");
return 4;
}
l = 0;
if (f.Sign != 0) {
s[l] = '-';
l++;
}
if (f.ParameterType == 1) {
s[l] = 'r';
l = l + 1 + sprintf(s + l + 1, "%d", f.TempIndex);
}
else if (f.ParameterType == 2){
s[l] = 'v';
l = l + 1 + sprintf(s + l + 1, "%d", fi.InputIndex);
}
else
{
if (fi.Usea0x != 0)
{
if (fi.SourceConstantIndex >= 96) {
strcpy(s + l, "c[");
l = l + 2;
l = l + sprintf(s + l, "%d", fi.SourceConstantIndex - 96);
strcpy(s + l, "+a0.x]");
l = l + 6;
}
else {
strcpy(s + l, "c[a0.x");
l = l + 6;
l = l + sprintf(s + l, "%d", fi.SourceConstantIndex - 96);
s[l] = ']';
l++;
}
}
else {
strcpy(s + l, "c[");
l = l + 2;
l = l + sprintf(s + l, "%d", fi.SourceConstantIndex - 96);
s[l] = ']';
l++;
}
}
l = l + disassemble_swizzle(f, s + l);
s[l] = 0;
return l;
}
int vertex_program_disassembler::disassemble_output(fields f, char *s)
{
int l;
if (f.OutputSelect == 1) {
strcpy(s, vecouts[f.OutputIndex]);
return strlen(s);
}
else {
strcpy(s, "c[");
l = 2;
l = l + sprintf(s + l, "%d", f.OutputIndex - 96);
s[l] = ']';
l++;
}
s[l] = 0;
return l;
}
int vertex_program_disassembler::output_types(fields f, int *o)
{
o[0] = o[1] = o[2] = o[3] = o[4] = o[5] = 0;
if ((f.VecOperation > 0) && (f.VecTempWriteMask != 0))
o[0] = 1;
if ((f.VecOperation > 0) && (f.OutputWriteMask != 0) && (f.MultiplexerControl == 0))
o[1] = 1;
if ((f.ScaOperation > 0) && (f.OutputWriteMask != 0) && (f.MultiplexerControl == 1))
o[2] = 1;
if ((f.ScaOperation > 0) && (f.ScaTempWriteMask != 0))
o[3] = 1;
if (f.VecOperation == 13)
o[4] = 1;
if (f.EndOfProgram == 1)
o[5] = 1;
return o[0] + o[1] + o[2] + o[3] + o[4] + o[5];
}
int vertex_program_disassembler::disassemble(unsigned int *instruction, char *line)
{
int b, p;
char *c;
if (state == 0) {
decodefields(instruction, 0, f);
output_types(f, o);
state = 1;
}
if (o[0] != 0)
{
o[0] = 0;
c = line;
strcpy(c, vecops[f.VecOperation]);
c = c + strlen(c);
strcpy(c, " r");
c = c + 2;
c = c + sprintf(c, "%d", f.VecTempIndex);
c = c + disassemble_mask(f.VecTempWriteMask, c);
b = 0;
for (p = 4; p != 0; p = p >> 1)
{
if ((vecpar2[f.VecOperation] & p) != 0) {
c[0] = ',';
c++;
c = c + disassemble_source(f.src[b], f, c);
}
b++;
}
*c = 0;
return 1;
}
if (o[1] != 0)
{
o[1] = 0;
c = line;
strcpy(c, vecops[f.VecOperation]);
c = c + strlen(c);
*c = ' ';
c++;
c = c + disassemble_output(f, c);
c = c + disassemble_mask(f.OutputWriteMask, c);
b = 0;
for (p = 4; p != 0; p = p >> 1)
{
if ((vecpar2[f.VecOperation] & p) != 0) {
*c = ',';
c++;
c = c + disassemble_source(f.src[b], f, c);
}
b++;
}
*c = 0;
return 1;
}
if (o[2] != 0)
{
o[2] = 0;
c = line;
strcpy(c, scaops[f.ScaOperation]);
c = c + strlen(c);
*c = ' ';
c++;
c = c + disassemble_output(f, c);
c = c + disassemble_mask(f.OutputWriteMask, c);
b = 0;
for (p = 4; p != 0; p = p >> 1)
{
if ((scapar2[f.ScaOperation] & p) != 0) {
*c = ',';
c++;
c = c + disassemble_source(f.src[b], f, c);
}
b++;
}
*c = 0;
return 1;
}
if (o[3] != 0)
{
if (f.VecOperation > 0)
b = 1;
else
b = f.VecTempIndex;
o[3] = 0;
c = line;
strcpy(c, scaops[f.ScaOperation]);
c = c + strlen(c);
strcpy(c, " r");
c = c + 2;
c = c + sprintf(c, "%d", b);
c = c + disassemble_mask(f.ScaTempWriteMask, c);
b = 0;
for (p = 4; p != 0; p = p >> 1)
{
if ((scapar2[f.ScaOperation] & p) != 0) {
*c = ',';
c++;
c = c + disassemble_source(f.src[b], f, c);
}
b++;
}
*c = 0;
return 1;
}
if (o[4] != 0)
{
o[4] = 0;
c = line;
c = c + sprintf(c, "MOV a0.x,");
c = c + disassemble_source(f.src[0], f, c);
*c = 0;
return 1;
}
if (o[5] != 0)
{
o[5] = 0;
strcpy(line, "END");
return 1;
}
state = 0;
return 0;
}
vertex_program_simulator::vertex_program_simulator()
{
for (auto & elem : op)
elem.modified = 0;
initialize_constants();
}
void vertex_program_simulator::set_data(vertex_nv *in, vertex_nv *out)
{
input = in;
output = out;
}
void vertex_program_simulator::reset()
{
ip = 0;
a0x = 0;
initialize_outputs();
initialize_temps();
}
void vertex_program_simulator::decode_instruction(int address)
{
instruction *i;
i = &op[address];
i->d.SignA = i->i[1] & (1 << 8);
i->d.ParameterTypeA = (i->i[2] >> 26) & 3;
i->d.TempIndexA = (i->i[2] >> 28) & 15;
i->d.SwizzleA[0] = (i->i[1] >> 6) & 3;
i->d.SwizzleA[1] = (i->i[1] >> 4) & 3;
i->d.SwizzleA[2] = (i->i[1] >> 2) & 3;
i->d.SwizzleA[3] = (i->i[1] >> 0) & 3;
i->d.SignB = i->i[2] & (1 << 25);
i->d.ParameterTypeB = (i->i[2] >> 11) & 3;
i->d.TempIndexB = (i->i[2] >> 13) & 15;
i->d.SwizzleB[0] = (i->i[2] >> 23) & 3;
i->d.SwizzleB[1] = (i->i[2] >> 21) & 3;
i->d.SwizzleB[2] = (i->i[2] >> 19) & 3;
i->d.SwizzleB[3] = (i->i[2] >> 17) & 3;
i->d.SignC = i->i[2] & (1 << 10);
i->d.ParameterTypeC = (i->i[3] >> 28) & 3;
i->d.TempIndexC = ((i->i[2] & 3) << 2) + (i->i[3] >> 30);
i->d.SwizzleC[0] = (i->i[2] >> 8) & 3;
i->d.SwizzleC[1] = (i->i[2] >> 6) & 3;
i->d.SwizzleC[2] = (i->i[2] >> 4) & 3;
i->d.SwizzleC[3] = (i->i[2] >> 2) & 3;
i->d.VecOperation = (VectorialOperation)((i->i[1] >> 21) & 15);
i->d.ScaOperation = (ScalarOperation)((i->i[1] >> 25) & 15);
i->d.OutputWriteMask = ((i->i[3] >> 12) & 15);
i->d.MultiplexerControl = i->i[3] & 4; // 0 : output Rn from vectorial operation 4 : output Rn from scalar operation
i->d.VecTempIndex = (i->i[3] >> 20) & 15;
i->d.OutputIndex = (i->i[3] >> 3) & 255;
i->d.OutputSelect = i->i[3] & 0x800;
i->d.VecTempWriteMask = (i->i[3] >> 24) & 15;
i->d.ScaTempWriteMask = (i->i[3] >> 16) & 15;
i->d.InputIndex = (i->i[1] >> 9) & 15;
i->d.SourceConstantIndex = (i->i[1] >> 13) & 255;
i->d.Usea0x = i->i[3] & 2;
i->d.EndOfProgram = i->i[3] & 1;
}
int vertex_program_simulator::step()
{
int p1, p2;
float tmp[3 * 4];
float tmpv[4] = { 0, 0, 0, 0};
float tmps[4] = { 0, 0, 0, 0};
instruction::decoded *d;
#if 0 // useful while debugging to see what instrucion is being executed
static int debugvpi = 0;
char disbuffer[256];
if (debugvpi) {
char *pp;
vertex_program_disassembler vdis;
pp = disbuffer;
while (vdis.disassemble(op[ip].i, pp) != 0) {
pp = pp + strlen(pp);
*pp = '\n';
pp++;
*pp = 0;
}
}
#endif
if (op[ip].modified)
decode_instruction(ip);
d = &(op[ip].d);
// prepare inputs
// input A
generate_input(&tmp[0], d->SignA, d->ParameterTypeA, d->TempIndexA, d->SwizzleA);
// input B
generate_input(&tmp[4], d->SignB, d->ParameterTypeB, d->TempIndexB, d->SwizzleB);
// input C
generate_input(&tmp[8], d->SignC, d->ParameterTypeC, d->TempIndexC, d->SwizzleC);
// compute 2 instructions
// vectorial
compute_vectorial_operation(tmpv, d->VecOperation, tmp);
// scalar
compute_scalar_operation(tmps, d->ScaOperation, tmp);
// assign destinations
if (d->VecOperation > 0) {
if (d->VecOperation == 13)
//o[4] = 1;
a0x = (int)tmpv[0];
else {
if (d->VecTempWriteMask != 0) { // assign to Rn
//o[0] = 1;
int wm = d->VecTempWriteMask;
for (p1 = 0; p1 < 4; p1++) {
if (wm & 8)
r_temp[d->VecTempIndex].fv[p1] = tmpv[p1];
wm = wm << 1;
}
}
if ((d->OutputWriteMask != 0) && (d->MultiplexerControl == 0)) {
//o[1] = 1;
if (d->OutputSelect) { // assign to output
int wm = d->OutputWriteMask;
for (p1 = 0; p1 < 4; p1++) {
if (wm & 8)
output->attribute[d->OutputIndex].fv[p1] = tmpv[p1];
wm = wm << 1;
}
// remeber, output position == r12
if (d->OutputIndex == 0)
for (p1 = 0; p1 < 4; p1++) {
r_temp[12].fv[p1] = output->attribute[d->OutputIndex].fv[p1];
}
}
else { // assign to constant
int wm = d->OutputWriteMask;
for (p1 = 0; p1 < 4; p1++) {
if (wm & 8)
c_constant[d->OutputIndex].fv[p1] = tmpv[p1];
wm = wm << 1;
}
}
}
}
}
if (d->ScaOperation > 0) {
if (d->ScaTempWriteMask != 0) { // assign to Rn
//o[3] = 1;
if (d->VecOperation > 0)
p2 = 1;
else
p2 = d->VecTempIndex;
int wm = d->ScaTempWriteMask;
for (p1 = 0; p1 < 4; p1++) {
if (wm & 8)
r_temp[p2].fv[p1] = tmps[p1];
wm = wm << 1;
}
}
if ((d->OutputWriteMask != 0) && (d->MultiplexerControl != 0)) { // assign to output
//o[2] = 1;
int wm = d->OutputWriteMask;
for (p1 = 0; p1 < 4; p1++) {
if (wm & 8)
output->attribute[d->OutputIndex].fv[p1] = tmps[p1];
wm = wm << 1;
}
// remeber, output position == r12
if (d->OutputIndex == 0) {
for (p1 = 0; p1 < 4; p1++) {
r_temp[12].fv[p1] = output->attribute[d->OutputIndex].fv[p1];
}
}
}
}
return d->EndOfProgram;
}
void vertex_program_simulator::execute()
{
int c;
c = 0;
do {
c = step();
ip++;
} while (c == 0);
}
void vertex_program_simulator::jump(int address)
{
ip = address;
}
void vertex_program_simulator::process(int address, vertex_nv *in, vertex_nv *out, int count)
{
#if 1 // useful while debugging to see what is being executed
static int debugvps = 0;
if (debugvps) {
char *pp;
vertex_program_disassembler vdis;
char disbuffer[128];
jump(address);
debugvps--;
for (int t = 0; t < 128; t++) {
pp = disbuffer;
while (vdis.disassemble(op[ip + t].i, pp) != 0) {
pp = pp + strlen(pp);
*pp = '\n';
pp++;
*pp = 0;
}
printf("%08X %08X %08X %s", op[ip + t].i[1], op[ip + t].i[2], op[ip + t].i[3], disbuffer);
if (op[ip + t].i[3] & 1)
break;
}
}
#endif
set_data(in, out);
while (count > 0) {
reset();
jump(address);
execute();
input++;
output++;
count--;
}
}
int vertex_program_simulator::status()
{
return ip;
}
void vertex_program_simulator::initialize_outputs()
{
for (int n = 0; n < 16; n++) {
output->attribute[n].fv[0] = output->attribute[n].fv[1] = output->attribute[n].fv[2] = 0;
output->attribute[n].fv[3] = 1;
}
}
void vertex_program_simulator::initialize_temps()
{
for (auto & elem : r_temp) {
for (int m = 0; m < 4; m++)
elem.fv[m] = 0;
}
}
void vertex_program_simulator::initialize_constants()
{
for (auto & elem : c_constant) {
for (int m = 0; m < 4;m++)
elem.fv[m] = 0;
}
}
void vertex_program_simulator::generate_input(float t[4], int sign, int type, int temp, int swizzle[4])
{
float sgn = 1;
if (sign)
sgn = -1;
if (type == 1) {
t[0] = sgn*r_temp[temp].fv[swizzle[0]];
t[1] = sgn*r_temp[temp].fv[swizzle[1]];
t[2] = sgn*r_temp[temp].fv[swizzle[2]];
t[3] = sgn*r_temp[temp].fv[swizzle[3]];
}
else if (type == 2) {
int InputIndex = op[ip].d.InputIndex;
t[0] = sgn*input->attribute[InputIndex].fv[swizzle[0]];
t[1] = sgn*input->attribute[InputIndex].fv[swizzle[1]];
t[2] = sgn*input->attribute[InputIndex].fv[swizzle[2]];
t[3] = sgn*input->attribute[InputIndex].fv[swizzle[3]];
}
else if (type == 3) {
int SourceConstantIndex = op[ip].d.SourceConstantIndex;
if (op[ip].d.Usea0x)
SourceConstantIndex = SourceConstantIndex + a0x;
t[0] = sgn*c_constant[SourceConstantIndex].fv[swizzle[0]];
t[1] = sgn*c_constant[SourceConstantIndex].fv[swizzle[1]];
t[2] = sgn*c_constant[SourceConstantIndex].fv[swizzle[2]];
t[3] = sgn*c_constant[SourceConstantIndex].fv[swizzle[3]];
}
}
void vertex_program_simulator::compute_vectorial_operation(float t_out[4], int instruction, float par_in[3 * 4])
{
const int p1_A = 0;
const int p2_B = 4;
const int p3_C = 8;
// t_out <= instruction(par_in)
switch (instruction) {
case 0: // "NOP"
break;
case 1: // "MOV"
t_out[0] = par_in[p1_A + 0];
t_out[1] = par_in[p1_A + 1];
t_out[2] = par_in[p1_A + 2];
t_out[3] = par_in[p1_A + 3];
break;
case 2: // "MUL"
t_out[0] = par_in[p1_A + 0] * par_in[p2_B + 0];
t_out[1] = par_in[p1_A + 1] * par_in[p2_B + 1];
t_out[2] = par_in[p1_A + 2] * par_in[p2_B + 2];
t_out[3] = par_in[p1_A + 3] * par_in[p2_B + 3];
break;
case 3: // "ADD"
t_out[0] = par_in[p1_A + 0] + par_in[p3_C + 0];
t_out[1] = par_in[p1_A + 1] + par_in[p3_C + 1];
t_out[2] = par_in[p1_A + 2] + par_in[p3_C + 2];
t_out[3] = par_in[p1_A + 3] + par_in[p3_C + 3];
break;
case 4: // "MAD"
t_out[0] = par_in[p1_A + 0] * par_in[p2_B + 0] + par_in[p3_C + 0];
t_out[1] = par_in[p1_A + 1] * par_in[p2_B + 1] + par_in[p3_C + 1];
t_out[2] = par_in[p1_A + 2] * par_in[p2_B + 2] + par_in[p3_C + 2];
t_out[3] = par_in[p1_A + 3] * par_in[p2_B + 3] + par_in[p3_C + 3];
break;
case 5: // "DP3"
t_out[0] = par_in[p1_A + 0] * par_in[p2_B + 0] + par_in[p1_A + 1] * par_in[p2_B + 1] + par_in[p1_A + 2] * par_in[p2_B + 2];
t_out[1] = t_out[2] = t_out[3] = t_out[0];
break;
case 6: // "DPH"
t_out[0] = par_in[p1_A + 0] * par_in[p2_B + 0] + par_in[p1_A + 1] * par_in[p2_B + 1] + par_in[p1_A + 2] * par_in[p2_B + 2] + par_in[p2_B + 3];
t_out[1] = t_out[2] = t_out[3] = t_out[0];
break;
case 7: // "DP4"
t_out[0] = par_in[p1_A + 0] * par_in[p2_B + 0] + par_in[p1_A + 1] * par_in[p2_B + 1] + par_in[p1_A + 2] * par_in[p2_B + 2] + par_in[p1_A + 3] * par_in[p2_B + 3];
t_out[1] = t_out[2] = t_out[3] = t_out[0];
break;
case 8: // "DST"
t_out[0] = 1.0;
t_out[1] = par_in[p1_A + 1] * par_in[p2_B + 1];
t_out[2] = par_in[p1_A + 2];
t_out[3] = par_in[p2_B + 3];
break;
case 9: // "MIN"
t_out[0] = fmin(par_in[p1_A + 0], par_in[p2_B + 0]);
t_out[1] = fmin(par_in[p1_A + 1], par_in[p2_B + 1]);
t_out[2] = fmin(par_in[p1_A + 2], par_in[p2_B + 2]);
t_out[3] = fmin(par_in[p1_A + 3], par_in[p2_B + 3]);
break;
case 10: // "MAX"
t_out[0] = fmax(par_in[p1_A + 0], par_in[p2_B + 0]);
t_out[1] = fmax(par_in[p1_A + 1], par_in[p2_B + 1]);
t_out[2] = fmax(par_in[p1_A + 2], par_in[p2_B + 2]);
t_out[3] = fmax(par_in[p1_A + 3], par_in[p2_B + 3]);
break;
case 11: // "SLT"
t_out[0] = (par_in[p1_A + 0] < par_in[p2_B + 0]) ? 1.0 : 0;
t_out[1] = (par_in[p1_A + 1] < par_in[p2_B + 1]) ? 1.0 : 0;
t_out[2] = (par_in[p1_A + 2] < par_in[p2_B + 2]) ? 1.0 : 0;
t_out[3] = (par_in[p1_A + 3] < par_in[p2_B + 3]) ? 1.0 : 0;
break;
case 12: // "SGE"
t_out[0] = (par_in[p1_A + 0] >= par_in[p2_B + 0]) ? 1.0 : 0;
t_out[1] = (par_in[p1_A + 1] >= par_in[p2_B + 1]) ? 1.0 : 0;
t_out[2] = (par_in[p1_A + 2] >= par_in[p2_B + 2]) ? 1.0 : 0;
t_out[3] = (par_in[p1_A + 3] >= par_in[p2_B + 3]) ? 1.0 : 0;
break;
case 13: // "ARL"
t_out[0] = par_in[p1_A + 0];
}
}
void vertex_program_simulator::compute_scalar_operation(float t_out[4], int instruction, float par_in[3 * 4])
{
//const int p1_A = 0;
//const int p2_B = 4;
const int p3_C = 8;
union {
float f;
unsigned int i;
} t;
int e;
// t_out <= instruction(par_in)
switch (instruction) {
case 0: // "NOP"
break;
case 1: // "IMV"
t_out[0] = par_in[p3_C + 0];
t_out[1] = par_in[p3_C + 1];
t_out[2] = par_in[p3_C + 2];
t_out[3] = par_in[p3_C + 3];
break;
case 2: // "RCP"
t_out[0] = t_out[1] = t_out[2] = t_out[3] = 1.0f / par_in[p3_C + 0];
break;
case 3: // "RCC"
t_out[0] = t_out[1] = t_out[2] = t_out[3] = 1.0f / par_in[p3_C + 0]; // ?
break;
case 4: // "RSQ"
/*
* NOTE: this was abs which is "int abs(int x)" - and changed to fabsf due to clang 3.6 warning
*/
t_out[0] = t_out[1] = t_out[2] = t_out[3] = 1.0f / sqrtf(fabsf(par_in[p3_C + 0]));
break;
case 5: // "EXP"
t_out[0] = pow(2, floor(par_in[p3_C + 0]));
t_out[1] = par_in[p3_C + 0] - floorf(par_in[p3_C + 0]);
t.f = pow(2, par_in[p3_C + 0]);
t.i = t.i & 0xffffff00;
t_out[2] = t.f;
t_out[3] = 1.0;
break;
case 6: // "LOG"
t_out[1] = frexp(par_in[p3_C + 0], &e)*2.0; // frexp gives mantissa as 0.5....1
t_out[0] = e - 1;
#ifndef __OS2__
/*
* NOTE: this was abs which is "int abs(int x)" - and changed to fabsf due to clang 3.6 warning
*/
t.f = log2(fabsf(par_in[p3_C + 0]));
#else
static double log_2 = 0.0;
if (log_2 == 0.0)
log_2 = log(2);
t.f = log(abs(par_in[p3_C + 0])) / log_2;
#endif
t.i = t.i & 0xffffff00;
t_out[2] = t.f;
t_out[3] = 1.0;
break;
case 7: // "LIT"
t_out[0] = 1.0;
t_out[1] = fmax(0, fmin(par_in[p3_C + 0], 1.0f));
t_out[2] = par_in[p3_C + 0] > 0 ? pow(fmax(par_in[p3_C + 1], 0), par_in[p3_C + 3]) : 0;
t_out[3] = 1.0;
break;
}
}
/*
* Graphics
*/
uint32_t nv2a_renderer::dilate0(uint32_t value, int bits) // dilate first "bits" bits in "value"
{
uint32_t x, m1, m2, m3;
int a;
x = value;
for (a = 0; a < bits; a++)
{
m2 = 1 << (a << 1);
m1 = m2 - 1;
m3 = (~m1) << 1;
x = (x & m1) + (x & m2) + ((x & m3) << 1);
}
return x;
}
uint32_t nv2a_renderer::dilate1(uint32_t value, int bits) // dilate first "bits" bits in "value"
{
uint32_t x, m1, m2, m3;
int a;
x = value;
for (a = 0; a < bits; a++)
{
m2 = 1 << (a << 1);
m1 = m2 - 1;
m3 = (~m1) << 1;
x = (x & m1) + ((x & m2) << 1) + ((x & m3) << 1);
}
return x;
}
void nv2a_renderer::computedilated(void)
{
int a, b;
for (b = 0; b < 16; b++)
for (a = 0; a < 2048; a++) {
dilated0[b][a] = dilate0(a, b);
dilated1[b][a] = dilate1(a, b);
}
for (b = 0; b < 16; b++)
for (a = 0; a < 16; a++)
dilatechose[(b << 4) + a] = (a < b ? a : b);
}
inline uint8_t *nv2a_renderer::direct_access_ptr(offs_t address)
{
#ifdef DEBUG_CHECKS
if (address >= 512*1024*1024)
machine().logerror("Bad address in direct_access_ptr !\n");
#endif
return basemempointer + address;
}
int nv2a_renderer::geforce_commandkind(uint32_t word)
{
if ((word & 0x00000003) == 0x00000002)
return 7; // call
if ((word & 0x00000003) == 0x00000001)
return 6; // jump
if ((word & 0xE0030003) == 0x40000000)
return 5; // non increasing
if ((word & 0xE0000003) == 0x20000000)
return 4; // old jump
if ((word & 0xFFFF0003) == 0x00030000)
return 3; // long non icreasing
if ((word & 0xFFFFFFFF) == 0x00020000)
return 2; // return
if ((word & 0xFFFF0003) == 0x00010000)
return 1; // sli conditional
if ((word & 0xE0030003) == 0x00000000)
return 0; // increasing
return -1;
}
uint32_t nv2a_renderer::geforce_object_offset(uint32_t handle)
{
uint32_t h = ((((handle >> 11) ^ handle) >> 11) ^ handle) & 0x7ff;
uint32_t o = (pfifo[0x210 / 4] & 0x1ff) << 8; // 0x1ff is not certain
uint32_t e = o + h * 8; // at 0xfd000000+0x00700000
uint32_t w;
if (ramin[e / 4] != handle) {
// this should never happen
for (uint32_t aa = o / 4; aa < (sizeof(ramin) / 4); aa = aa + 2) {
if (ramin[aa] == handle) {
e = aa * 4;
}
}
}
w = ramin[e / 4 + 1];
return (w & 0xffff) * 0x10; // 0xffff is not certain
}
void nv2a_renderer::geforce_read_dma_object(uint32_t handle, uint32_t &offset, uint32_t &size)
{
//uint32_t objclass,pt_present,pt_linear,access,target,rorw;
uint32_t dma_adjust, dma_frame;
uint32_t o = geforce_object_offset(handle);
o = o / 4;
//objclass=ramin[o] & 0xfff;
//pt_present=(ramin[o] >> 12) & 1;
//pt_linear=(ramin[o] >> 13) & 1;
//access=(ramin[o] >> 14) & 3;
//target=(ramin[o] >> 16) & 3;
dma_adjust = (ramin[o] >> 20) & 0xfff;
size = ramin[o + 1];
//rorw=ramin[o+2] & 1;
dma_frame = ramin[o + 2] & 0xfffff000;
offset = dma_frame + dma_adjust;
}
/*void myline(bitmap_rgb32 &bmp,float x1,float y1,float x2,float y2)
{
int xx1,yy1,xx2,yy2;
xx1=x1;
xx2=x2;
yy1=y1;
yy2=y2;
if (xx1 == xx2) {
if (yy1 > yy2) {
int t=yy1;
yy1=yy2;
yy2=t;
}
for (int y=yy1;y <= yy2;y++)
*((uint32_t *)bmp.raw_pixptr(y,xx1))= -1;
} else if (yy1 == yy2) {
if (xx1 > xx2) {
int t=xx1;
xx1=xx2;
xx2=t;
}
for (int x=xx1;x <= xx2;x++)
*((uint32_t *)bmp.raw_pixptr(yy1,x))= -1;
}
}*/
inline uint32_t convert_a4r4g4b4_a8r8g8b8(uint32_t a4r4g4b4)
{
uint32_t a8r8g8b8;
int ca, cr, cg, cb;
cb = pal4bit(a4r4g4b4 & 0x000f);
cg = pal4bit((a4r4g4b4 & 0x00f0) >> 4);
cr = pal4bit((a4r4g4b4 & 0x0f00) >> 8);
ca = pal4bit((a4r4g4b4 & 0xf000) >> 12);
a8r8g8b8 = (ca << 24) | (cr << 16) | (cg << 8) | (cb); // color converted to 8 bits per component
return a8r8g8b8;
}
inline uint32_t convert_a1r5g5b5_a8r8g8b8(uint32_t a1r5g5b5)
{
uint32_t a8r8g8b8;
int ca, cr, cg, cb;
cb = pal5bit(a1r5g5b5 & 0x001f);
cg = pal5bit((a1r5g5b5 & 0x03e0) >> 5);
cr = pal5bit((a1r5g5b5 & 0x7c00) >> 10);
ca = a1r5g5b5 & 0x8000 ? 0xff : 0;
a8r8g8b8 = (ca << 24) | (cr << 16) | (cg << 8) | (cb); // color converted to 8 bits per component
return a8r8g8b8;
}
inline uint32_t convert_r5g6b5_r8g8b8(uint32_t r5g6b5)
{
uint32_t r8g8b8;
int cr, cg, cb;
cb = pal5bit(r5g6b5 & 0x001f);
cg = pal6bit((r5g6b5 & 0x07e0) >> 5);
cr = pal5bit((r5g6b5 & 0xf800) >> 11);
r8g8b8 = (cr << 16) | (cg << 8) | (cb); // color converted to 8 bits per component
return r8g8b8;
}
uint32_t nv2a_renderer::texture_get_texel(int number, int x, int y)
{
uint32_t to, s, c, sa, ca;
uint32_t a4r4g4b4, a1r5g5b5, r5g6b5;
int bx, by;
int color0, color1, color0m2, color1m2, alpha0, alpha1;
uint32_t codes;
uint64_t alphas;
int cr, cg, cb;
int sizeu, sizev;
// force to [0,size-1]
if (texture[number].rectangle == false) {
sizeu = texture[number].sizeu;
sizev = texture[number].sizev;
x = (unsigned int)x & (sizeu - 1);
y = (unsigned int)y & (sizev - 1);
} else
{
sizeu = texture[number].rectwidth;
sizev = texture[number].rectheight;
x = x % sizeu;
if (x < 0)
x = sizeu + x;
y = y % sizev;
if (y < 0)
y = sizev + y;
}
switch (texture[number].format) {
case NV2A_TEX_FORMAT::A8R8G8B8:
to = dilated0[texture[number].dilate][x] + dilated1[texture[number].dilate][y]; // offset of texel in texture memory
return *(((uint32_t *)texture[number].buffer) + to); // get texel color
case NV2A_TEX_FORMAT::DXT1:
bx = x >> 2;
by = y >> 2;
x = x & 3;
y = y & 3;
to = bx + by*(sizeu >> 2);
color0 = *((uint16_t *)(((uint64_t *)texture[number].buffer) + to) + 0);
color1 = *((uint16_t *)(((uint64_t *)texture[number].buffer) + to) + 1);
codes = *((uint32_t *)(((uint64_t *)texture[number].buffer) + to) + 1);
s = (y << 3) + (x << 1);
c = (codes >> s) & 3;
c = c + (color0 > color1 ? 0 : 4);
color0m2 = color0 << 1;
color1m2 = color1 << 1;
switch (c) {
case 0:
return 0xff000000 + convert_r5g6b5_r8g8b8(color0);
case 1:
return 0xff000000 + convert_r5g6b5_r8g8b8(color1);
case 2:
cb = pal5bit(((color0m2 & 0x003e) + (color1 & 0x001f)) / 3);
cg = pal6bit(((color0m2 & 0x0fc0) + (color1 & 0x07e0)) / 3 >> 5);
cr = pal5bit(((color0m2 & 0x1f000) + color1) / 3 >> 11);
return 0xff000000 | (cr << 16) | (cg << 8) | (cb);
case 3:
cb = pal5bit(((color1m2 & 0x003e) + (color0 & 0x001f)) / 3);
cg = pal6bit(((color1m2 & 0x0fc0) + (color0 & 0x07e0)) / 3 >> 5);
cr = pal5bit(((color1m2 & 0x1f000) + color0) / 3 >> 11);
return 0xff000000 | (cr << 16) | (cg << 8) | (cb);
case 4:
return 0xff000000 + convert_r5g6b5_r8g8b8(color0);
case 5:
return 0xff000000 + convert_r5g6b5_r8g8b8(color1);
case 6:
cb = pal5bit(((color0 & 0x001f) + (color1 & 0x001f)) / 2);
cg = pal6bit(((color0 & 0x07e0) + (color1 & 0x07e0)) / 2 >> 5);
cr = pal5bit(((color0 & 0xf800) + (color1 & 0xf800)) / 2 >> 11);
return 0xff000000 | (cr << 16) | (cg << 8) | (cb);
default:
return 0xff000000;
}
case NV2A_TEX_FORMAT::DXT3:
bx = x >> 2;
by = y >> 2;
x = x & 3;
y = y & 3;
to = (bx + by*(sizeu >> 2)) << 1;
color0 = *((uint16_t *)(((uint64_t *)texture[number].buffer) + to) + 4);
color1 = *((uint16_t *)(((uint64_t *)texture[number].buffer) + to) + 5);
codes = *((uint32_t *)(((uint64_t *)texture[number].buffer) + to) + 3);
alphas = *(((uint64_t *)texture[number].buffer) + to);
s = (y << 3) + (x << 1);
sa = ((y << 2) + x) << 2;
c = (codes >> s) & 3;
ca = (alphas >> sa) & 15;
switch (c) {
case 0:
return ((ca + (ca << 4)) << 24) + convert_r5g6b5_r8g8b8(color0);
case 1:
return ((ca + (ca << 4)) << 24) + convert_r5g6b5_r8g8b8(color1);
case 2:
cb = pal5bit((2 * (color0 & 0x001f) + (color1 & 0x001f)) / 3);
cg = pal6bit((2 * (color0 & 0x07e0) + (color1 & 0x07e0)) / 3 >> 5);
cr = pal5bit((2 * (color0 & 0xf800) + (color1 & 0xf800)) / 3 >> 11);
return ((ca + (ca << 4)) << 24) | (cr << 16) | (cg << 8) | (cb);
default:
cb = pal5bit(((color0 & 0x001f) + 2 * (color1 & 0x001f)) / 3);
cg = pal6bit(((color0 & 0x07e0) + 2 * (color1 & 0x07e0)) / 3 >> 5);
cr = pal5bit(((color0 & 0xf800) + 2 * (color1 & 0xf800)) / 3 >> 11);
return ((ca + (ca << 4)) << 24) | (cr << 16) | (cg << 8) | (cb);
}
case NV2A_TEX_FORMAT::A4R4G4B4:
to = dilated0[texture[number].dilate][x] + dilated1[texture[number].dilate][y]; // offset of texel in texture memory
a4r4g4b4 = *(((uint16_t *)texture[number].buffer) + to); // get texel color
return convert_a4r4g4b4_a8r8g8b8(a4r4g4b4);
case NV2A_TEX_FORMAT::A1R5G5B5:
to = dilated0[texture[number].dilate][x] + dilated1[texture[number].dilate][y]; // offset of texel in texture memory
a1r5g5b5 = *(((uint16_t *)texture[number].buffer) + to); // get texel color
return convert_a1r5g5b5_a8r8g8b8(a1r5g5b5);
case NV2A_TEX_FORMAT::R5G6B5:
to = dilated0[texture[number].dilate][x] + dilated1[texture[number].dilate][y]; // offset of texel in texture memory
r5g6b5 = *(((uint16_t *)texture[number].buffer) + to); // get texel color
return 0xff000000 + convert_r5g6b5_r8g8b8(r5g6b5);
case NV2A_TEX_FORMAT::R8G8B8_RECT:
to = texture[number].rectangle_pitch*y + (x << 2);
return *((uint32_t *)(((uint8_t *)texture[number].buffer) + to));
case NV2A_TEX_FORMAT::A8R8G8B8_RECT:
to = texture[number].rectangle_pitch*y + (x << 2);
return *((uint32_t *)(((uint8_t *)texture[number].buffer) + to));
case NV2A_TEX_FORMAT::DXT5:
bx = x >> 2;
by = y >> 2;
x = x & 3;
y = y & 3;
to = (bx + by*(sizeu >> 2)) << 1;
color0 = *((uint16_t *)(((uint64_t *)texture[number].buffer) + to) + 4);
color1 = *((uint16_t *)(((uint64_t *)texture[number].buffer) + to) + 5);
codes = *((uint32_t *)(((uint64_t *)texture[number].buffer) + to) + 3);
alpha0 = *((uint8_t *)(((uint64_t *)texture[number].buffer) + to) + 0);
alpha1 = *((uint8_t *)(((uint64_t *)texture[number].buffer) + to) + 1);
alphas = *(((uint64_t *)texture[number].buffer) + to);
s = (y << 3) + (x << 1);
sa = ((y << 2) + x) * 3;
c = (codes >> s) & 3;
ca = (alphas >> sa) & 7;
ca = ca + (alpha0 > alpha1 ? 0 : 8);
switch (ca) {
case 0:
ca = alpha0;
break;
case 1:
ca = alpha1;
break;
case 2:
ca = (6 * alpha0 + 1 * alpha1) / 7;
break;
case 3:
ca = (5 * alpha0 + 2 * alpha1) / 7;
break;
case 4:
ca = (4 * alpha0 + 3 * alpha1) / 7;
break;
case 5:
ca = (3 * alpha0 + 4 * alpha1) / 7;
break;
case 6:
ca = (2 * alpha0 + 5 * alpha1) / 7;
break;
case 7:
ca = (1 * alpha0 + 6 * alpha1) / 7;
break;
case 8:
ca = alpha0;
break;
case 9:
ca = alpha1;
break;
case 10:
ca = (4 * alpha0 + 1 * alpha1) / 5;
break;
case 11:
ca = (3 * alpha0 + 2 * alpha1) / 5;
break;
case 12:
ca = (2 * alpha0 + 3 * alpha1) / 5;
break;
case 13:
ca = (1 * alpha0 + 4 * alpha1) / 5;
break;
case 14:
ca = 0;
break;
case 15:
ca = 255;
break;
}
switch (c) {
case 0:
return (ca << 24) + convert_r5g6b5_r8g8b8(color0);
case 1:
return (ca << 24) + convert_r5g6b5_r8g8b8(color1);
case 2:
cb = pal5bit((2 * (color0 & 0x001f) + (color1 & 0x001f)) / 3);
cg = pal6bit((2 * (color0 & 0x07e0) + (color1 & 0x07e0)) / 3 >> 5);
cr = pal5bit((2 * (color0 & 0xf800) + (color1 & 0xf800)) / 3 >> 11);
return (ca << 24) | (cr << 16) | (cg << 8) | (cb);
default:
cb = pal5bit(((color0 & 0x001f) + 2 * (color1 & 0x001f)) / 3);
cg = pal6bit(((color0 & 0x07e0) + 2 * (color1 & 0x07e0)) / 3 >> 5);
cr = pal5bit(((color0 & 0xf800) + 2 * (color1 & 0xf800)) / 3 >> 11);
return (ca << 24) | (cr << 16) | (cg << 8) | (cb);
}
default:
return 0xff00ff00;
}
}
inline uint8_t *nv2a_renderer::read_pixel(int x, int y, int32_t c[4])
{
uint32_t offset;
uint32_t color;
uint32_t *addr;
uint16_t *addr16;
uint8_t *addr8;
if (type_rendertarget == NV2A_RT_TYPE::SWIZZLED)
offset = (dilated0[dilate_rendertarget][x] + dilated1[dilate_rendertarget][y]) * bytespixel_rendertarget;
else // type_rendertarget == LINEAR
offset = pitch_rendertarget * y + x * bytespixel_rendertarget;
#ifdef DEBUG_CHECKS
if (offset >= size_rendertarget)
{
machine().logerror("Bad offset computed in read_pixel !\n");
offset = 0;
}
#endif
switch (colorformat_rendertarget) {
case NV2A_COLOR_FORMAT::R5G6B5:
addr16 = (uint16_t *)((uint8_t *)rendertarget + offset);
color = *addr16;
c[3] = 0xff;
c[2] = pal5bit((color & 0xf800) >> 11);
c[1] = pal6bit((color & 0x07e0) >> 5);
c[0] = pal5bit(color & 0x1f);
return (uint8_t *)addr16;
case NV2A_COLOR_FORMAT::X8R8G8B8_Z8R8G8B8:
case NV2A_COLOR_FORMAT::X8R8G8B8_X8R8G8B8:
addr = (uint32_t *)((uint8_t *)rendertarget + offset);
color = *addr;
c[3] = 0xff;
c[2] = (color >> 16) & 255;
c[1] = (color >> 8) & 255;
c[0] = color & 255;
return (uint8_t *)addr;
case NV2A_COLOR_FORMAT::A8R8G8B8:
addr = (uint32_t *)((uint8_t *)rendertarget + offset);
color = *addr;
c[3] = color >> 24;
c[2] = (color >> 16) & 255;
c[1] = (color >> 8) & 255;
c[0] = color & 255;
return (uint8_t *)addr;
case NV2A_COLOR_FORMAT::B8:
addr8 = (uint8_t *)rendertarget + offset;
c[0] = *addr8;
c[1] = c[2] = 0;
c[3] = 0xff;
return addr8;
default:
return nullptr;
}
return nullptr;
}
void nv2a_renderer::write_pixel(int x, int y, uint32_t color, int depth)
{
uint8_t *addr;
uint32_t *daddr32;
uint16_t *daddr16;
uint32_t deptsten;
int32_t c[4], fb[4], s[4], d[4], cc[4];
uint32_t dep, sten, stenc, stenv;
bool stencil_passed;
bool depth_passed;
if ((depth > 0xffffff) || (depth < 0) || (x < 0))
return;
fb[3] = fb[2] = fb[1] = fb[0] = 0;
addr = nullptr;
if (color_mask != 0)
addr = read_pixel(x, y, fb);
if (depthformat_rendertarget == NV2A_RT_DEPTH_FORMAT::Z24S8) {
#ifdef DEBUG_CHECKS
if (((pitch_depthbuffer / 4) * y + x) >= size_depthbuffer)
{
machine().logerror("Bad depthbuffer offset computed in write_pixel !\n");
return;
}
#endif
daddr32 = depthbuffer + (pitch_depthbuffer / 4) * y + x;
deptsten = *daddr32;
dep = deptsten >> 8;
sten = deptsten & 255;
daddr16 = nullptr;
}
else if (depthformat_rendertarget == NV2A_RT_DEPTH_FORMAT::Z16) {
#ifdef DEBUG_CHECKS
if (((pitch_depthbuffer / 2) * y + x) >= size_depthbuffer)
{
machine().logerror("Bad depthbuffer offset computed in write_pixel !\n");
return;
}
#endif
daddr16 = (uint16_t *)depthbuffer + (pitch_depthbuffer / 2) * y + x;
deptsten = *daddr16;
dep = (deptsten << 8) | 0xff;
sten = 0;
daddr32 = nullptr;
}
else {
daddr32 = nullptr;
daddr16 = nullptr;
dep = 0xffffff;
sten = 0;
}
c[3] = color >> 24;
c[2] = (color >> 16) & 255;
c[1] = (color >> 8) & 255;
c[0] = color & 255;
cc[3] = blend_color >> 24;
cc[2] = (blend_color >> 16) & 255;
cc[1] = (blend_color >> 8) & 255;
cc[0] = blend_color & 255;
// ownership test and scissor test not done
// alpha test
if (alpha_test_enabled) {
switch (alpha_func) {
case NV2A_COMPARISON_OP::NEVER:
return;
case NV2A_COMPARISON_OP::ALWAYS:
default:
break;
case NV2A_COMPARISON_OP::LESS:
if (c[3] >= alpha_reference)
return;
break;
case NV2A_COMPARISON_OP::LEQUAL:
if (c[3] > alpha_reference)
return;
break;
case NV2A_COMPARISON_OP::EQUAL:
if (c[3] != alpha_reference)
return;
break;
case NV2A_COMPARISON_OP::GEQUAL:
if (c[3] < alpha_reference)
return;
break;
case NV2A_COMPARISON_OP::GREATER:
if (c[3] <= alpha_reference)
return;
break;
case NV2A_COMPARISON_OP::NOTEQUAL:
if (c[3] == alpha_reference)
return;
break;
}
}
// stencil test
stencil_passed = true;
if (stencil_test_enabled) {
stenc=stencil_mask & stencil_ref;
stenv=stencil_mask & sten;
switch (stencil_func) {
case NV2A_COMPARISON_OP::NEVER:
stencil_passed = false;
break;
case NV2A_COMPARISON_OP::LESS:
if (stenc >= stenv)
stencil_passed = false;
break;
case NV2A_COMPARISON_OP::EQUAL:
if (stenc != stenv)
stencil_passed = false;
break;
case NV2A_COMPARISON_OP::LEQUAL:
if (stenc > stenv)
stencil_passed = false;
break;
case NV2A_COMPARISON_OP::GREATER:
if (stenc <= stenv)
stencil_passed = false;
break;
case NV2A_COMPARISON_OP::NOTEQUAL:
if (stenc == stenv)
stencil_passed = false;
break;
case NV2A_COMPARISON_OP::GEQUAL:
if (stenc < stenv)
stencil_passed = false;
break;
case NV2A_COMPARISON_OP::ALWAYS:
default:
break;
}
if (stencil_passed == false) {
switch (stencil_op_fail) {
case NV2A_STENCIL_OP::ZEROOP:
sten = 0;
break;
case NV2A_STENCIL_OP::INVERTOP:
sten = sten ^ 255;
break;
case NV2A_STENCIL_OP::KEEP:
default:
break;
case NV2A_STENCIL_OP::REPLACE:
sten = stencil_ref;
break;
case NV2A_STENCIL_OP::INCR:
if (sten < 255)
sten++;
break;
case NV2A_STENCIL_OP::DECR:
if (sten > 0)
sten--;
break;
case NV2A_STENCIL_OP::INCR_WRAP:
if (sten < 255)
sten++;
else
sten = 0;
break;
case NV2A_STENCIL_OP::DECR_WRAP:
if (sten > 0)
sten--;
else
sten = 255;
break;
}
if (depthformat_rendertarget == NV2A_RT_DEPTH_FORMAT::Z24S8) {
deptsten = (dep << 8) | sten;
*daddr32 = deptsten;
}
else if (depthformat_rendertarget == NV2A_RT_DEPTH_FORMAT::Z16) {
deptsten = dep >> 8;
*daddr16 = (uint16_t)deptsten;
}
return;
}
}
// depth buffer test
depth_passed = true;
if (depth_test_enabled) {
switch (depth_function) {
case NV2A_COMPARISON_OP::NEVER:
depth_passed = false;
break;
case NV2A_COMPARISON_OP::LESS:
if (depth >= dep)
depth_passed = false;
break;
case NV2A_COMPARISON_OP::EQUAL:
if (depth != dep)
depth_passed = false;
break;
case NV2A_COMPARISON_OP::LEQUAL:
if (depth > dep)
depth_passed = false;
break;
case NV2A_COMPARISON_OP::GREATER:
if (depth <= dep)
depth_passed = false;
break;
case NV2A_COMPARISON_OP::NOTEQUAL:
if (depth == dep)
depth_passed = false;
break;
case NV2A_COMPARISON_OP::GEQUAL:
if (depth < dep)
depth_passed = false;
break;
case NV2A_COMPARISON_OP::ALWAYS:
default:
break;
}
if (depth_passed == false) {
switch (stencil_op_zfail) {
case NV2A_STENCIL_OP::ZEROOP:
sten = 0;
break;
case NV2A_STENCIL_OP::INVERTOP:
sten = sten ^ 255;
break;
case NV2A_STENCIL_OP::KEEP:
default:
break;
case NV2A_STENCIL_OP::REPLACE:
sten = stencil_ref;
break;
case NV2A_STENCIL_OP::INCR:
if (sten < 255)
sten++;
break;
case NV2A_STENCIL_OP::DECR:
if (sten > 0)
sten--;
break;
case NV2A_STENCIL_OP::INCR_WRAP:
if (sten < 255)
sten++;
else
sten = 0;
break;
case NV2A_STENCIL_OP::DECR_WRAP:
if (sten > 0)
sten--;
else
sten = 255;
break;
}
if (depthformat_rendertarget == NV2A_RT_DEPTH_FORMAT::Z24S8) {
deptsten = (dep << 8) | sten;
*daddr32 = deptsten;
}
else if (depthformat_rendertarget == NV2A_RT_DEPTH_FORMAT::Z16) {
deptsten = dep >> 8;
*daddr16 = (uint16_t)deptsten;
}
return;
}
switch (stencil_op_zpass) {
case NV2A_STENCIL_OP::ZEROOP:
sten = 0;
break;
case NV2A_STENCIL_OP::INVERTOP:
sten = sten ^ 255;
break;
case NV2A_STENCIL_OP::KEEP:
default:
break;
case NV2A_STENCIL_OP::REPLACE:
sten = stencil_ref;
break;
case NV2A_STENCIL_OP::INCR:
if (sten < 255)
sten++;
break;
case NV2A_STENCIL_OP::DECR:
if (sten > 0)
sten--;
break;
case NV2A_STENCIL_OP::INCR_WRAP:
if (sten < 255)
sten++;
else
sten = 0;
break;
case NV2A_STENCIL_OP::DECR_WRAP:
if (sten > 0)
sten--;
else
sten = 255;
break;
}
}
// blending
if (blending_enabled) {
switch (blend_function_source) {
case NV2A_BLEND_FACTOR::ZERO:
s[3] = s[2] = s[1] = s[0] = 0;
break;
case NV2A_BLEND_FACTOR::ONE:
default:
s[3] = s[2] = s[1] = s[0] = 255;
break;
case NV2A_BLEND_FACTOR::DST_COLOR:
s[3] = fb[3];
s[2] = fb[2];
s[1] = fb[1];
s[0] = fb[0];
break;
case NV2A_BLEND_FACTOR::ONE_MINUS_DST_COLOR:
s[3] = fb[3] ^ 255;
s[2] = fb[2] ^ 255;
s[1] = fb[1] ^ 255;
s[0] = fb[0] ^ 255;
break;
case NV2A_BLEND_FACTOR::SRC_ALPHA:
s[3] = s[2] = s[1] = s[0] = c[3];
break;
case NV2A_BLEND_FACTOR::ONE_MINUS_SRC_ALPHA:
s[3] = s[2] = s[1] = s[0] = c[3] ^ 255;
break;
case NV2A_BLEND_FACTOR::DST_ALPHA:
s[3] = s[2] = s[1] = s[0] = fb[3];
break;
case NV2A_BLEND_FACTOR::ONE_MINUS_DST_ALPHA:
s[3] = s[2] = s[1] = s[0] = fb[3] ^ 255;
break;
case NV2A_BLEND_FACTOR::CONSTANT_COLOR:
s[3] = cc[3];
s[2] = cc[2];
s[1] = cc[1];
s[0] = cc[0];
break;
case NV2A_BLEND_FACTOR::ONE_MINUS_CONSTANT_COLOR:
s[3] = cc[3] ^ 255;
s[2] = cc[2] ^ 255;
s[1] = cc[1] ^ 255;
s[0] = cc[0] ^ 255;
break;
case NV2A_BLEND_FACTOR::CONSTANT_ALPHA:
s[3] = s[2] = s[1] = s[0] = cc[3];
break;
case NV2A_BLEND_FACTOR::ONE_MINUS_CONSTANT_ALPHA:
s[3] = s[2] = s[1] = s[0] = cc[3] ^ 255;
break;
case NV2A_BLEND_FACTOR::SRC_ALPHA_SATURATE:
s[3] = 255;
if (c[3] < (fb[3] ^ 255))
s[2] = c[3];
else
s[2] = fb[3];
s[1] = s[0] = s[2];
break;
}
switch (blend_function_destination) {
case NV2A_BLEND_FACTOR::ZERO:
default:
d[3] = d[2] = d[1] = d[0] = 0;
break;
case NV2A_BLEND_FACTOR::ONE:
d[3] = d[2] = d[1] = d[0] = 255;
break;
case NV2A_BLEND_FACTOR::SRC_COLOR:
d[3] = c[3];
d[2] = c[2];
d[1] = c[1];
d[0] = c[0];
break;
case NV2A_BLEND_FACTOR::ONE_MINUS_SRC_COLOR:
d[3] = c[3] ^ 255;
d[2] = c[2] ^ 255;
d[1] = c[1] ^ 255;
d[0] = c[0] ^ 255;
break;
case NV2A_BLEND_FACTOR::SRC_ALPHA:
d[3] = d[2] = d[1] = d[0] = c[3];
break;
case NV2A_BLEND_FACTOR::ONE_MINUS_SRC_ALPHA:
d[3] = d[2] = d[1] = d[0] = c[3] ^ 255;
break;
case NV2A_BLEND_FACTOR::DST_ALPHA:
d[3] = d[2] = d[1] = d[0] = fb[3];
break;
case NV2A_BLEND_FACTOR::ONE_MINUS_DST_ALPHA:
d[3] = d[2] = d[1] = d[0] = fb[3] ^ 255;
break;
case NV2A_BLEND_FACTOR::CONSTANT_COLOR:
d[3] = cc[3];
d[2] = cc[2];
d[1] = cc[1];
d[0] = cc[0];
break;
case NV2A_BLEND_FACTOR::ONE_MINUS_CONSTANT_COLOR:
d[3] = cc[3] ^ 255;
d[2] = cc[2] ^ 255;
d[1] = cc[1] ^ 255;
d[0] = cc[0] ^ 255;
break;
case NV2A_BLEND_FACTOR::CONSTANT_ALPHA:
d[3] = d[2] = d[1] = d[0] = cc[3];
break;
case NV2A_BLEND_FACTOR::ONE_MINUS_CONSTANT_ALPHA:
d[3] = d[2] = d[1] = d[0] = cc[3] ^ 255;
break;
}
switch (blend_equation) {
case NV2A_BLEND_EQUATION::FUNC_ADD:
c[3] = (c[3] * s[3] + fb[3] * d[3]) / 255;
if (c[3] > 255)
c[3] = 255;
c[2] = (c[2] * s[2] + fb[2] * d[2]) / 255;
if (c[2] > 255)
c[2] = 255;
c[1] = (c[1] * s[1] + fb[1] * d[1]) / 255;
if (c[1] > 255)
c[1] = 255;
c[0] = (c[0] * s[0] + fb[0] * d[0]) / 255;
if (c[0] > 255)
c[0] = 255;
break;
case NV2A_BLEND_EQUATION::FUNC_SUBTRACT:
c[3] = (c[3] * s[3] - fb[3] * d[3]) / 255;
if (c[3] < 0)
c[3] = 255;
c[2] = (c[2] * s[2] - fb[2] * d[2]) / 255;
if (c[2] < 0)
c[2] = 255;
c[1] = (c[1] * s[1] - fb[1] * d[1]) / 255;
if (c[1] < 0)
c[1] = 255;
c[0] = (c[0] * s[0] - fb[0] * d[0]) / 255;
if (c[0] < 0)
c[0] = 255;
break;
case NV2A_BLEND_EQUATION::FUNC_REVERSE_SUBTRACT:
c[3] = (fb[3] * d[3] - c[3] * s[3]) / 255;
if (c[3] < 0)
c[3] = 255;
c[2] = (fb[2] * d[2] - c[2] * s[2]) / 255;
if (c[2] < 0)
c[2] = 255;
c[1] = (fb[1] * d[1] - c[1] * s[1]) / 255;
if (c[1] < 0)
c[1] = 255;
c[0] = (fb[0] * d[0] - c[0] * s[0]) / 255;
if (c[0] < 0)
c[0] = 255;
break;
case NV2A_BLEND_EQUATION::MIN:
c[3] = s[3];
if (d[3] < c[3])
c[3] = d[3];
c[2] = s[2];
if (d[2] < c[2])
c[2] = d[2];
c[1] = s[1];
if (d[1] < c[1])
c[1] = d[1];
c[0] = s[0];
if (d[0] < c[0])
c[0] = d[0];
break;
case NV2A_BLEND_EQUATION::MAX:
c[3] = s[3];
if (d[3] > c[3])
c[3] = d[3];
c[2] = s[2];
if (d[2] > c[2])
c[2] = d[2];
c[1] = s[1];
if (d[1] > c[1])
c[1] = d[1];
c[0] = s[0];
if (d[0] > c[0])
c[0] = d[0];
break;
}
}
// dithering not done
// logical operation
if (logical_operation_enabled) {
switch (logical_operation) {
case NV2A_LOGIC_OP::CLEAR:
c[3] = 0;
c[2] = 0;
c[1] = 0;
c[0] = 0;
break;
case NV2A_LOGIC_OP::AND:
c[3] = c[3] & fb[3];
c[2] = c[2] & fb[2];
c[1] = c[1] & fb[1];
c[0] = c[0] & fb[0];
break;
case NV2A_LOGIC_OP::AND_REVERSE:
c[3] = c[3] & (fb[3] ^ 255);
c[2] = c[2] & (fb[2] ^ 255);
c[1] = c[1] & (fb[1] ^ 255);
c[0] = c[0] & (fb[0] ^ 255);
break;
case NV2A_LOGIC_OP::COPY:
default:
break;
case NV2A_LOGIC_OP::AND_INVERTED:
c[3] = (c[3] ^ 255) & fb[3];
c[2] = (c[2] ^ 255) & fb[2];
c[1] = (c[1] ^ 255) & fb[1];
c[0] = (c[0] ^ 255) & fb[0];
break;
case NV2A_LOGIC_OP::NOOP:
c[3] = fb[3];
c[2] = fb[2];
c[1] = fb[1];
c[0] = fb[0];
break;
case NV2A_LOGIC_OP::XOR:
c[3] = c[3] ^ fb[3];
c[2] = c[2] ^ fb[2];
c[1] = c[1] ^ fb[1];
c[0] = c[0] ^ fb[0];
break;
case NV2A_LOGIC_OP::OR:
c[3] = c[3] | fb[3];
c[2] = c[2] | fb[2];
c[1] = c[1] | fb[1];
c[0] = c[0] | fb[0];
break;
case NV2A_LOGIC_OP::NOR:
c[3] = (c[3] | fb[3]) ^ 255;
c[2] = (c[2] | fb[2]) ^ 255;
c[1] = (c[1] | fb[1]) ^ 255;
c[0] = (c[0] | fb[0]) ^ 255;
break;
case NV2A_LOGIC_OP::EQUIV:
c[3] = (c[3] ^ fb[3]) ^ 255;
c[2] = (c[2] ^ fb[2]) ^ 255;
c[1] = (c[1] ^ fb[1]) ^ 255;
c[0] = (c[0] ^ fb[0]) ^ 255;
break;
case NV2A_LOGIC_OP::INVERT:
c[3] = fb[3] ^ 255;
c[2] = fb[2] ^ 255;
c[1] = fb[1] ^ 255;
c[0] = fb[0] ^ 255;
break;
case NV2A_LOGIC_OP::OR_REVERSE:
c[3] = c[3] | (fb[3] ^ 255);
c[2] = c[2] | (fb[2] ^ 255);
c[1] = c[1] | (fb[1] ^ 255);
c[0] = c[0] | (fb[0] ^ 255);
break;
case NV2A_LOGIC_OP::COPY_INVERTED:
c[3] = c[3] ^ 255;
c[2] = c[2] ^ 255;
c[1] = c[1] ^ 255;
c[0] = c[0] ^ 255;
break;
case NV2A_LOGIC_OP::OR_INVERTED:
c[3] = (c[3] ^ 255) | fb[3];
c[2] = (c[2] ^ 255) | fb[2];
c[1] = (c[1] ^ 255) | fb[1];
c[0] = (c[0] ^ 255) | fb[0];
break;
case NV2A_LOGIC_OP::NAND:
c[3] = (c[3] & fb[3]) ^ 255;
c[2] = (c[2] & fb[2]) ^ 255;
c[1] = (c[1] & fb[1]) ^ 255;
c[0] = (c[0] & fb[0]) ^ 255;
break;
case NV2A_LOGIC_OP::SET:
c[3] = 255;
c[2] = 255;
c[1] = 255;
c[0] = 255;
break;
}
}
if (color_mask != 0) {
uint32_t ct,ft,w;
ct = ((uint32_t)c[3] << 24) | ((uint32_t)c[2] << 16) | ((uint32_t)c[1] << 8) | (uint32_t)c[0];
ft = ((uint32_t)fb[3] << 24) | ((uint32_t)fb[2] << 16) | ((uint32_t)fb[1] << 8) | (uint32_t)fb[0];
w = (ft & ~color_mask) | (ct & color_mask);
switch (colorformat_rendertarget) {
case NV2A_COLOR_FORMAT::R5G6B5:
w = ((w >> 8) & 0xf800) + ((w >> 5) & 0x7e0) + ((w >> 3) & 0x1f);
*((uint16_t *)addr) = (uint16_t)w;
break;
case NV2A_COLOR_FORMAT::X8R8G8B8_Z8R8G8B8:
case NV2A_COLOR_FORMAT::X8R8G8B8_X8R8G8B8:
*((uint32_t *)addr) = w;
break;
case NV2A_COLOR_FORMAT::A8R8G8B8:
*((uint32_t *)addr) = w;
break;
case NV2A_COLOR_FORMAT::B8:
*addr = (uint8_t)w;
break;
default:
break;
}
}
if (depth_write_enabled)
dep = depth;
if (depthformat_rendertarget == NV2A_RT_DEPTH_FORMAT::Z24S8) {
deptsten = (dep << 8) | sten;
*daddr32 = deptsten;
}
else if (depthformat_rendertarget == NV2A_RT_DEPTH_FORMAT::Z16) {
deptsten = dep >> 8;
*daddr16 = (uint16_t)deptsten;
}
}
void nv2a_renderer::render_color(int32_t scanline, const extent_t &extent, const nvidia_object_data &objectdata, int threadid)
{
int x, lx;
lx = limits_rendertarget.right();
if ((extent.startx < 0) && (extent.stopx <= 0))
return;
if ((extent.startx > lx) && (extent.stopx > lx))
return;
x = extent.stopx - extent.startx; // number of pixels to draw (start inclusive, end exclusive)
if (extent.stopx > lx)
x = x - (extent.stopx - lx - 1);
x--;
while (x >= 0) {
uint32_t a8r8g8b8;
int z;
int ca, cr, cg, cb;
int xp = extent.startx + x; // x coordinate of current pixel
cb = ((extent.param[(int)VERTEX_PARAMETER::PARAM_COLOR_B].start + (double)x*extent.param[(int)VERTEX_PARAMETER::PARAM_COLOR_B].dpdx))*255.0f;
cg = ((extent.param[(int)VERTEX_PARAMETER::PARAM_COLOR_G].start + (double)x*extent.param[(int)VERTEX_PARAMETER::PARAM_COLOR_G].dpdx))*255.0f;
cr = ((extent.param[(int)VERTEX_PARAMETER::PARAM_COLOR_R].start + (double)x*extent.param[(int)VERTEX_PARAMETER::PARAM_COLOR_R].dpdx))*255.0f;
ca = ((extent.param[(int)VERTEX_PARAMETER::PARAM_COLOR_A].start + (double)x*extent.param[(int)VERTEX_PARAMETER::PARAM_COLOR_A].dpdx))*255.0f;
a8r8g8b8 = (ca << 24) + (cr << 16) + (cg << 8) + cb; // pixel color obtained by interpolating the colors of the vertices
z = (extent.param[(int)VERTEX_PARAMETER::PARAM_Z].start + (double)x*extent.param[(int)VERTEX_PARAMETER::PARAM_Z].dpdx);
write_pixel(xp, scanline, a8r8g8b8, z);
x--;
}
}
void nv2a_renderer::render_texture_simple(int32_t scanline, const extent_t &extent, const nvidia_object_data &objectdata, int threadid)
{
int x, lx;
uint32_t a8r8g8b8;
if (!objectdata.data->texture[0].enabled) {
return;
}
lx = limits_rendertarget.right();
if ((extent.startx < 0) && (extent.stopx <= 0))
return;
if ((extent.startx > lx) && (extent.stopx > lx))
return;
x = extent.stopx - extent.startx; // number of pixels to draw (start inclusive, end exclusive)
if (extent.stopx > lx)
x = x - (extent.stopx - lx - 1);
x--;
while (x >= 0) {
int up, vp;
int z;
int xp = extent.startx + x; // x coordinate of current pixel
if (objectdata.data->texture[0].rectangle == false) {
up = (extent.param[(int)VERTEX_PARAMETER::PARAM_TEXTURE0_U].start + (double)x*extent.param[(int)VERTEX_PARAMETER::PARAM_TEXTURE0_U].dpdx)*(double)(objectdata.data->texture[0].sizeu - 1); // x coordinate of texel in texture
vp = (extent.param[(int)VERTEX_PARAMETER::PARAM_TEXTURE0_V].start + (double)x*extent.param[(int)VERTEX_PARAMETER::PARAM_TEXTURE0_V].dpdx)*(double)(objectdata.data->texture[0].sizev - 1); // y coordinate of texel in texture
} else
{
up = extent.param[(int)VERTEX_PARAMETER::PARAM_TEXTURE0_U].start + (double)x*extent.param[(int)VERTEX_PARAMETER::PARAM_TEXTURE0_U].dpdx; // x coordinate of texel in texture
vp = extent.param[(int)VERTEX_PARAMETER::PARAM_TEXTURE0_V].start + (double)x*extent.param[(int)VERTEX_PARAMETER::PARAM_TEXTURE0_V].dpdx; // y coordinate of texel in texture
}
a8r8g8b8 = texture_get_texel(0, up, vp);
z = (extent.param[(int)VERTEX_PARAMETER::PARAM_Z].start + (double)x*extent.param[(int)VERTEX_PARAMETER::PARAM_Z].dpdx);
write_pixel(xp, scanline, a8r8g8b8, z);
x--;
}
}
void nv2a_renderer::render_register_combiners(int32_t scanline, const extent_t &extent, const nvidia_object_data &objectdata, int threadid)
{
int x, lx, xp;
int up, vp;
int ca, cr, cg, cb;
uint32_t color[6];
uint32_t a8r8g8b8;
int z;
int n;
color[0] = color[1] = color[2] = color[3] = color[4] = color[5] = 0;
lx = limits_rendertarget.right();
if ((extent.startx < 0) && (extent.stopx <= 0))
return;
if ((extent.startx > lx) && (extent.stopx > lx))
return;
x = extent.stopx - extent.startx; // number of pixels to draw (start inclusive, end exclusive)
if (extent.stopx > lx)
x = x - (extent.stopx - lx - 1);
x--;
std::lock_guard<std::mutex> lock(combiner.lock); // needed since multithreading is not supported yet
while (x >= 0) {
xp = extent.startx + x;
// 1: fetch data
// 1.1: interpolated color from vertices
cb = ((extent.param[(int)VERTEX_PARAMETER::PARAM_COLOR_B].start + (double)x*extent.param[(int)VERTEX_PARAMETER::PARAM_COLOR_B].dpdx))*255.0f;
cg = ((extent.param[(int)VERTEX_PARAMETER::PARAM_COLOR_G].start + (double)x*extent.param[(int)VERTEX_PARAMETER::PARAM_COLOR_G].dpdx))*255.0f;
cr = ((extent.param[(int)VERTEX_PARAMETER::PARAM_COLOR_R].start + (double)x*extent.param[(int)VERTEX_PARAMETER::PARAM_COLOR_R].dpdx))*255.0f;
ca = ((extent.param[(int)VERTEX_PARAMETER::PARAM_COLOR_A].start + (double)x*extent.param[(int)VERTEX_PARAMETER::PARAM_COLOR_A].dpdx))*255.0f;
color[0] = (ca << 24) + (cr << 16) + (cg << 8) + cb; // pixel color obtained by interpolating the colors of the vertices
color[1] = 0; // lighting not yet
// 1.2: color for each of the 4 possible textures
for (n = 0; n < 4; n++) {
if (texture[n].enabled) {
if (texture[n].rectangle == false) {
up = (extent.param[(int)VERTEX_PARAMETER::PARAM_TEXTURE0_U + n * 2].start + (double)x*extent.param[(int)VERTEX_PARAMETER::PARAM_TEXTURE0_U + n * 2].dpdx)*(double)(objectdata.data->texture[n].sizeu - 1);
vp = (extent.param[(int)VERTEX_PARAMETER::PARAM_TEXTURE0_V + n * 2].start + (double)x*extent.param[(int)VERTEX_PARAMETER::PARAM_TEXTURE0_V + n * 2].dpdx)*(double)(objectdata.data->texture[n].sizev - 1);
} else
{
up = extent.param[(int)VERTEX_PARAMETER::PARAM_TEXTURE0_U + n * 2].start + (double)x*extent.param[(int)VERTEX_PARAMETER::PARAM_TEXTURE0_U + n * 2].dpdx;
vp = extent.param[(int)VERTEX_PARAMETER::PARAM_TEXTURE0_V + n * 2].start + (double)x*extent.param[(int)VERTEX_PARAMETER::PARAM_TEXTURE0_V + n * 2].dpdx;
}
color[n + 2] = texture_get_texel(n, up, vp);
}
}
// 2: compute
// 2.1: initialize
combiner_initialize_registers(color);
// 2.2: general cmbiner stages
for (n = 0; n < combiner.stages; n++) {
// 2.2.1 initialize
combiner_initialize_stage(n);
// 2.2.2 map inputs
combiner_map_input(n);
// 2.2.3 compute possible outputs
combiner_compute_rgb_outputs(n);
combiner_compute_a_outputs(n);
// 2.2.4 map outputs to registers
combiner_map_output(n);
}
// 2.3: final cmbiner stage
combiner_initialize_final();
combiner_map_final_input();
combiner_final_output();
a8r8g8b8 = combiner_float_argb8(combiner.output);
// 3: write pixel
z = (extent.param[(int)VERTEX_PARAMETER::PARAM_Z].start + (double)x*extent.param[(int)VERTEX_PARAMETER::PARAM_Z].dpdx);
write_pixel(xp, scanline, a8r8g8b8, z);
x--;
}
}
#if 0
const char *rc_mapping_str[] = {
"UNSIGNED_IDENTITY",
"UNSIGNED_INVERT",
"EXPAND_NORMAL",
"EXPAND_NEGATE",
"HALF_BIAS_NORMAL",
"HALF_BIAS_NEGATE",
"SIGNED_IDENTITY",
"SIGNED_NEGATE"
};
const char *rc_usage_rgb_str[] = {
"RGB",
"ALPHA"
};
const char *rc_usage_alpha_str[] = {
"BLUE",
"ALPHA"
};
const char *rc_variable_str[] = {
"ZERO",
"CONSTANT_COLOR0",
"CONSTANT_COLOR1",
"FOG",
"PRIMARY_COLOR",
"SECONDARY_COLOR",
"???",
"???",
"TEXTURE0",
"TEXTURE1",
"TEXTURE2",
"TEXTURE3",
"SPARE0",
"SPARE1",
"SPARE0_PLUS_SECONDARY_COLOR",
"E_TIMES_F"
};
const char *rc_bias_str[] = {
"NONE",
"BIAS_BY_NEGATIVE_ONE_HALF"
};
const char *rc_scale_str[] = {
"NONE",
"SCALE_BY_TWO",
"SCALE_BY_FOUR",
"SCALE_BY_ONE_HALF"
};
/* Dump the current setup of the register combiners */
void dumpcombiners(uint32_t *m)
{
int a, b, n, v;
n = m[0x1e60 / 4] & 0xf;
printf("Combiners active: %d\n\r", n);
for (a = 0; a < n; a++) {
printf("Combiner %d\n\r", a + 1);
printf(" RC_IN_ALPHA %08X\n\r", m[0x0260 / 4 + a]);
for (b = 24; b >= 0; b = b - 8) {
v = (m[0x0260 / 4 + a] >> b) & 0xf;
printf(" %c_INPUT %s\n\r", 'A' + 3 - b / 8, rc_variable_str[v]);
v = (m[0x0260 / 4 + a] >> (b + 4)) & 1;
printf(" %c_COMPONENT_USAGE %s\n\r", 'A' + 3 - b / 8, rc_usage_alpha_str[v]);
v = (m[0x0260 / 4 + a] >> (b + 5)) & 7;
printf(" %c_MAPPING %s\n\r", 'A' + 3 - b / 8, rc_mapping_str[v]);
}
printf(" RC_IN_RGB %08X\n\r", m[0x0ac0 / 4 + a]);
for (b = 24; b >= 0; b = b - 8) {
v = (m[0x0ac0 / 4 + a] >> b) & 0xf;
printf(" %c_INPUT %s\n\r", 'A' + 3 - b / 8, rc_variable_str[v]);
v = (m[0x0ac0 / 4 + a] >> (b + 4)) & 1;
printf(" %c_COMPONENT_USAGE %s\n\r", 'A' + 3 - b / 8, rc_usage_rgb_str[v]);
v = (m[0x0ac0 / 4 + a] >> (b + 5)) & 7;
printf(" %c_MAPPING %s\n\r", 'A' + 3 - b / 8, rc_mapping_str[v]);
}
printf(" RC_OUT_ALPHA %08X\n\r", m[0x0aa0 / 4 + a]);
v = m[0x0aa0 / 4 + a] & 0xf;
printf(" CD_OUTPUT %s\n\r", rc_variable_str[v]);
v = (m[0x0aa0 / 4 + a] >> 4) & 0xf;
printf(" AB_OUTPUT %s\n\r", rc_variable_str[v]);
v = (m[0x0aa0 / 4 + a] >> 8) & 0xf;
printf(" SUM_OUTPUT %s\n\r", rc_variable_str[v]);
v = (m[0x0aa0 / 4 + a] >> 12) & 1;
printf(" CD_DOT_PRODUCT %d\n\r", v);
v = (m[0x0aa0 / 4 + a] >> 13) & 1;
printf(" AB_DOT_PRODUCT %d\n\r", v);
v = (m[0x0aa0 / 4 + a] >> 14) & 1;
printf(" MUX_SUM %d\n\r", v);
v = (m[0x0aa0 / 4 + a] >> 15) & 1;
printf(" BIAS %s\n\r", rc_bias_str[v]);
v = (m[0x0aa0 / 4 + a] >> 16) & 3;
printf(" SCALE %s\n\r", rc_scale_str[v]);
//v=(m[0x0aa0/4+a] >> 27) & 7;
printf(" RC_OUT_RGB %08X\n\r", m[0x1e40 / 4 + a]);
v = m[0x1e40 / 4 + a] & 0xf;
printf(" CD_OUTPUT %s\n\r", rc_variable_str[v]);
v = (m[0x1e40 / 4 + a] >> 4) & 0xf;
printf(" AB_OUTPUT %s\n\r", rc_variable_str[v]);
v = (m[0x1e40 / 4 + a] >> 8) & 0xf;
printf(" SUM_OUTPUT %s\n\r", rc_variable_str[v]);
v = (m[0x1e40 / 4 + a] >> 12) & 1;
printf(" CD_DOT_PRODUCT %d\n\r", v);
v = (m[0x1e40 / 4 + a] >> 13) & 1;
printf(" AB_DOT_PRODUCT %d\n\r", v);
v = (m[0x1e40 / 4 + a] >> 14) & 1;
printf(" MUX_SUM %d\n\r", v);
v = (m[0x1e40 / 4 + a] >> 15) & 1;
printf(" BIAS %s\n\r", rc_bias_str[v]);
v = (m[0x1e40 / 4 + a] >> 16) & 3;
printf(" SCALE %s\n\r", rc_scale_str[v]);
//v=(m[0x1e40/4+a] >> 27) & 7;
printf("\n\r");
}
printf("Combiner final %08X %08X\n\r", m[0x0288 / 4], m[0x028c / 4]);
for (a = 24; a >= 0; a = a - 8) {
n = (m[0x0288 / 4] >> a) & 0xf;
printf(" %c_INPUT %s\n\r", 'A' + 3 - a / 8, rc_variable_str[n]);
n = (m[0x0288 / 4] >> (a + 4)) & 1;
printf(" %c_COMPONENT_USAGE %s\n\r", 'A' + 3 - a / 8, rc_usage_rgb_str[n]);
n = (m[0x0288 / 4] >> (a + 5)) & 7;
printf(" %c_MAPPING %s\n\r", 'A' + 3 - a / 8, rc_mapping_str[n]);
}
for (a = 24; a >= 8; a = a - 8) {
n = (m[0x028c / 4] >> a) & 0xf;
printf(" %c_INPUT %s\n\r", 'E' + 3 - a / 8, rc_variable_str[n]);
n = (m[0x028c / 4] >> (a + 4)) & 1;
printf(" %c_COMPONENT_USAGE %s\n\r", 'E' + 3 - a / 8, rc_usage_rgb_str[n]);
n = (m[0x028c / 4] >> (a + 5)) & 7;
printf(" %c_MAPPING %s\n\r", 'E' + 3 - a / 8, rc_mapping_str[n]);
}
n = (m[0x028c / 4] >> 7) & 1;
printf(" color sum clamp: %d\n\r", n);
}
#endif
void nv2a_renderer::extract_packed_float(uint32_t data, float &first, float &second, float &third)
{
int32_t p1, p2, p3;
int32_t e1, e2, e3;
int32_t m1, m2, m3;
float scale, decimal;
union
{
float f;
uint32_t i;
} i2f;
// convert r11g11b10f to 3 float values
// each 32 bit words contains 2 11 bit float values and one 10 bit float value
p1 = data & 0b11111111111;
p2 = (data >> 11) & 0b11111111111;
p3 = (data >> 22) & 0b1111111111;
// 11 bit values have 6 bits of mantissa and 5 of exponent, 10 bit values have 5 bits of mantissa and 5 of exponent
m1 = p1 & 0b111111;
e1 = (p1 >> 6) & 0b11111;
m2 = p2 & 0b111111;
e2 = (p2 >> 6) & 0b11111;
m3 = p3 & 0b11111;
e3 = (p3 >> 5) & 0b11111;
// the following is based on routine UF11toF32 in appendix G of the "OpenGL Programming Guide 8th edition" book
if (e1 == 0) {
if (m1 != 0) {
scale = 1.0 / (1 << 20);
first = scale * m1;
}
else
first = 0;
}
else if (e1 == 31) {
i2f.i = 0x7f800000 | m1;
first = i2f.f;
}
else {
e1 -= 15;
if (e1 < 0) {
scale = 1.0 / (1 << -e1);
}
else {
scale = 1 << e1;
}
decimal = 1.0 + (float)m1 / 64;
first = scale * decimal;
}
if (e2 == 0) {
if (m2 != 0) {
scale = 1.0 / (1 << 20);
second = scale * m2;
}
else
second = 0;
}
else if (e2 == 31) {
i2f.i = 0x7f800000 | m2;
second = i2f.f;
}
else {
e2 -= 15;
if (e2 < 0) {
scale = 1.0 / (1 << -e2);
}
else {
scale = 1 << e2;
}
decimal = 1.0 + (float)m2 / 64;
second = scale * decimal;
}
if (e3 == 0) {
if (m3 != 0) {
scale = 1.0 / (1 << 20);
third = scale * m3;
}
else
third = 0;
}
else if (e3 == 31) {
i2f.i = 0x7f800000 | m3;
third = i2f.f;
}
else {
e3 -= 15;
if (e3 < 0) {
scale = 1.0 / (1 << -e3);
}
else {
scale = 1 << e3;
}
decimal = 1.0 + (float)m3 / 32;
third = scale * decimal;
}
}
void nv2a_renderer::read_vertex(address_space & space, offs_t address, vertex_nv &vertex, int attrib)
{
uint32_t u;
int c, d, l;
l = vertexbuffer_size[attrib];
switch (vertexbuffer_kind[attrib]) {
case NV2A_VTXBUF_TYPE::FLOAT:
vertex.attribute[attrib].fv[0] = 0;
vertex.attribute[attrib].fv[1] = 0;
vertex.attribute[attrib].fv[2] = 0;
vertex.attribute[attrib].fv[3] = 1.0;
for (c = d = 0; c < l; c++) {
vertex.attribute[attrib].iv[c] = space.read_dword(address + d);
d = d + 4;
}
break;
case NV2A_VTXBUF_TYPE::UBYTE_OGL:
u = space.read_dword(address + 0);
for (c = l-1; c >= 0; c--) {
vertex.attribute[attrib].fv[c] = (u & 0xff) / 255.0;
u = u >> 8;
}
break;
case NV2A_VTXBUF_TYPE::UBYTE_D3D:
u = space.read_dword(address + 0);
for (c = 0; c < l; c++) {
vertex.attribute[attrib].fv[c] = (u & 0xff) / 255.0;
u = u >> 8;
}
break;
case NV2A_VTXBUF_TYPE::FLOAT_PACKED: // 3 floating point numbers packed into 32 bits
u = space.read_dword(address + 0);
extract_packed_float(u, vertex.attribute[attrib].fv[0], vertex.attribute[attrib].fv[1], vertex.attribute[attrib].fv[2]);
vertex.attribute[attrib].fv[3] = 1.0;
break;
default:
vertex.attribute[attrib].fv[0] = 0;
vertex.attribute[attrib].fv[1] = 0;
vertex.attribute[attrib].fv[2] = 0;
vertex.attribute[attrib].fv[3] = 1.0;
machine().logerror("Unsupported vertex type in read_vertex !\n");
}
}
/* Read vertices data from system memory. Method 0x1800 and 0x1808 */
int nv2a_renderer::read_vertices_0x180x(address_space & space, vertex_nv *destination, uint32_t address, int limit)
{
uint32_t m;
int a, b;
#ifdef MAME_DEBUG
memset(destination, 0, sizeof(vertex_nv)*limit);
#endif
for (m = 0; m < limit; m++) {
memcpy(&destination[m], &persistvertexattr, sizeof(persistvertexattr));
b = enabled_vertex_attributes;
for (a = 0; a < 16; a++) {
if (b & 1) {
read_vertex(space, vertexbuffer_address[a] + vertex_indexes[indexesleft_first] * vertexbuffer_stride[a], destination[m], a);
}
b = b >> 1;
}
indexesleft_first = (indexesleft_first + 1) & 1023;
indexesleft_count--;
}
return limit;
}
/* Read vertices data from system memory. Method 0x1810 */
int nv2a_renderer::read_vertices_0x1810(address_space & space, vertex_nv *destination, int offset, int limit)
{
uint32_t m;
int a, b;
#ifdef MAME_DEBUG
memset(destination, 0, sizeof(vertex_nv)*limit);
#endif
for (m = 0; m < limit; m++) {
memcpy(&destination[m], &persistvertexattr, sizeof(persistvertexattr));
b = enabled_vertex_attributes;
for (a = 0; a < 16; a++) {
if (b & 1) {
read_vertex(space, vertexbuffer_address[a] + (m + offset)*vertexbuffer_stride[a], destination[m], a);
}
b = b >> 1;
}
}
return m;
}
/* Read vertices data from system memory. Method 0x1818 */
int nv2a_renderer::read_vertices_0x1818(address_space & space, vertex_nv *destination, uint32_t address, int limit)
{
uint32_t m, vwords;
int a, b;
#ifdef MAME_DEBUG
memset(destination, 0, sizeof(vertex_nv)*limit);
#endif
vwords = vertex_attribute_words[15] + vertex_attribute_offset[15];
for (m = 0; m < limit; m++) {
memcpy(&destination[m], &persistvertexattr, sizeof(persistvertexattr));
b = enabled_vertex_attributes;
for (a = 0; a < 16; a++) {
if (b & 1) {
read_vertex(space, address + vertex_attribute_offset[a] * 4, destination[m], a);
}
b = b >> 1;
}
address = address + vwords * 4;
}
return (int)(m*vwords);
}
void nv2a_renderer::compute_supersample_factors(float &horizontal, float &vertical)
{
float mx, my;
mx = 1;
my = 1;
switch (((antialias_control & 1) << 2) | antialiasing_rendertarget)
{
case 0:
mx = my = 1;
break;
case 1:
mx = 2; my = 1;
break;
case 2:
mx = my = 2;
break;
case 4:
mx = my = 1;
break;
case 5:
mx = 2;
my = 1;
break;
case 6:
mx = 2;
my = 2;
break;
default:
mx = my = 1;
}
horizontal = mx;
vertical = my;
}
void nv2a_renderer::convert_vertices_poly(vertex_nv *source, nv2avertex_t *destination, int count)
{
vertex_nv vert[4];
int m, u;
float v[4];
// take each vertex with its attributes and obtain data for drawing
// should use either the vertex program or transformation matrices
if (vertex_pipeline == 4) {
// transformation matrices
// this part needs more testing
for (m = 0; m < count; m++) {
for (int i = 0; i < 4; i++) {
v[i] = 0;
for (int j = 0; j < 4; j++)
v[i] += matrix.composite[i][j] * source[m].attribute[0].fv[j];
};
destination[m].w = v[3];
destination[m].x = (v[0] / v[3])*supersample_factor_x; // source[m].attribute[0].fv[0];
destination[m].y = (v[1] / v[3])*supersample_factor_y; // source[m].attribute[0].fv[1];
destination[m].p[(int)VERTEX_PARAMETER::PARAM_Z] = v[2] / v[3];
for (u = (int)VERTEX_PARAMETER::PARAM_COLOR_B; u <= (int)VERTEX_PARAMETER::PARAM_COLOR_A; u++) // 0=b 1=g 2=r 3=a
destination[m].p[u] = source[m].attribute[3].fv[u];
for (u = 0; u < 4; u++) {
destination[m].p[(int)VERTEX_PARAMETER::PARAM_TEXTURE0_U + u * 2] = source[m].attribute[9 + u].fv[0];
destination[m].p[(int)VERTEX_PARAMETER::PARAM_TEXTURE0_V + u * 2] = source[m].attribute[9 + u].fv[1];
}
}
}
else {
// vertex program
// run vertex program
vertexprogram.exec.process(vertexprogram.start_instruction, source, vert, count);
// the output of the vertex program has the perspective divide, viewport scale and offset already applied
// copy data for poly.c
for (m = 0; m < count; m++) {
destination[m].w = vert[m].attribute[0].fv[3];
destination[m].x = (vert[m].attribute[0].fv[0] - 0.53125) * supersample_factor_x;
destination[m].y = (vert[m].attribute[0].fv[1] - 0.53125) * supersample_factor_y;
for (u = (int)VERTEX_PARAMETER::PARAM_COLOR_B; u <= (int)VERTEX_PARAMETER::PARAM_COLOR_A; u++) // 0=b 1=g 2=r 3=a
destination[m].p[u] = vert[m].attribute[3].fv[u];
for (u = 0; u < 4; u++) {
destination[m].p[(int)VERTEX_PARAMETER::PARAM_TEXTURE0_U + u * 2] = vert[m].attribute[9 + u].fv[0];
destination[m].p[(int)VERTEX_PARAMETER::PARAM_TEXTURE0_V + u * 2] = vert[m].attribute[9 + u].fv[1];
}
destination[m].p[(int)VERTEX_PARAMETER::PARAM_Z] = vert[m].attribute[0].fv[2];
}
}
}
void nv2a_renderer::clear_render_target(int what, uint32_t value)
{
int xi, yi, xf, yf;
int x, y;
uint32_t color;
uint8_t *addr;
uint32_t mask;
uint32_t offset;
if (what == 0)
return;
mask = 0;
switch (colorformat_rendertarget) {
case NV2A_COLOR_FORMAT::R5G6B5:
if (what & 1)
mask = 0x1f;
if (what & 2)
mask = mask | 0x07e0;
if (what & 4)
mask = mask | 0xf800;
break;
case NV2A_COLOR_FORMAT::X8R8G8B8_Z8R8G8B8:
case NV2A_COLOR_FORMAT::X8R8G8B8_X8R8G8B8:
for (x = 3; x >= 0; x--) {
if (what & 8)
mask = (mask << 8) | 255;
what = what << 1;
}
break;
case NV2A_COLOR_FORMAT::A8R8G8B8:
for (x = 3; x >= 0; x--) {
if (what & 8)
mask = (mask << 8) | 255;
what = what << 1;
}
break;
case NV2A_COLOR_FORMAT::B8:
if (what & 1)
mask = 255;
break;
default:
return;
}
xi = clear_rendertarget.left()*supersample_factor_x;
yi = clear_rendertarget.top()*supersample_factor_y;
xf = clear_rendertarget.right()*supersample_factor_x;
yf = clear_rendertarget.bottom()*supersample_factor_y;
if ((xi < limits_rendertarget.left()) && (xf < limits_rendertarget.left()))
return;
if ((xi > limits_rendertarget.right()) && (xf > limits_rendertarget.right()))
return;
if ((yi < limits_rendertarget.top()) && (yf < limits_rendertarget.top()))
return;
if ((yi > limits_rendertarget.bottom()) && (yf > limits_rendertarget.bottom()))
return;
if (xi < limits_rendertarget.left())
xi = limits_rendertarget.left();
if (xf > limits_rendertarget.right())
xf = limits_rendertarget.right();
if (yi < limits_rendertarget.top())
yi = limits_rendertarget.top();
if (yf > limits_rendertarget.bottom())
yf = limits_rendertarget.bottom();
if (type_rendertarget == NV2A_RT_TYPE::SWIZZLED)
offset = (dilated0[dilate_rendertarget][xf] + dilated1[dilate_rendertarget][yf]) * bytespixel_rendertarget;
else // type_rendertarget == LINEAR
offset = pitch_rendertarget * yf + xf * bytespixel_rendertarget;
addr = (uint8_t *)rendertarget + offset;
if ((addr < basemempointer) || (addr > topmempointer))
{
machine().logerror("Bad memory pointer computed in clear_render_target !\n");
return;
}
for (y = yi; y <= yf; y++)
for (x = xi; x <= xf; x++) {
if (type_rendertarget == NV2A_RT_TYPE::SWIZZLED)
offset = (dilated0[dilate_rendertarget][x] + dilated1[dilate_rendertarget][y]) * bytespixel_rendertarget;
else // type_rendertarget == LINEAR
offset = pitch_rendertarget * y + x * bytespixel_rendertarget;
switch (colorformat_rendertarget) {
case NV2A_COLOR_FORMAT::R5G6B5:
addr = (uint8_t *)rendertarget + offset;
color = *((uint16_t *)addr);
break;
case NV2A_COLOR_FORMAT::X8R8G8B8_Z8R8G8B8:
case NV2A_COLOR_FORMAT::X8R8G8B8_X8R8G8B8:
addr = (uint8_t *)rendertarget + offset;
color = *((uint32_t *)addr);
break;
case NV2A_COLOR_FORMAT::A8R8G8B8:
addr = (uint8_t *)rendertarget + offset;
color = *((uint32_t *)addr);
break;
case NV2A_COLOR_FORMAT::B8:
addr = (uint8_t *)rendertarget + offset;
color = *addr;
break;
default:
return;
}
color = (color & ~mask) | (value & mask);
switch (colorformat_rendertarget) {
case NV2A_COLOR_FORMAT::R5G6B5:
*((uint16_t *)addr) = color;
break;
case NV2A_COLOR_FORMAT::X8R8G8B8_Z8R8G8B8:
case NV2A_COLOR_FORMAT::X8R8G8B8_X8R8G8B8:
*((uint32_t *)addr) = color;
break;
case NV2A_COLOR_FORMAT::A8R8G8B8:
*((uint32_t *)addr) = color;
break;
case NV2A_COLOR_FORMAT::B8:
*addr = color;
break;
default:
return;
}
}
#ifdef LOG_NV2A
printf("clearscreen\n\r");
#endif
}
void nv2a_renderer::clear_depth_buffer(int what, uint32_t value)
{
int xi, yi, xf, yf;
int x, y;
uint32_t color;
uint8_t *addr;
uint32_t mask;
uint32_t offset;
uint32_t bpp;
if (what == 0)
return;
mask = 0;
switch (depthformat_rendertarget) {
case NV2A_RT_DEPTH_FORMAT::Z24S8:
if (what & 1)
mask = 0xffffff00;
if (what & 2)
mask = mask | 0xff;
bpp = 4;
break;
case NV2A_RT_DEPTH_FORMAT::Z16:
if (what & 1)
mask = 0xffff;
bpp = 2;
break;
default:
return;
}
xi = clear_rendertarget.left()*supersample_factor_x;
yi = clear_rendertarget.top()*supersample_factor_y;
xf = clear_rendertarget.right()*supersample_factor_x;
yf = clear_rendertarget.bottom()*supersample_factor_y;
if ((xi < limits_rendertarget.left()) && (xf < limits_rendertarget.left()))
return;
if ((xi > limits_rendertarget.right()) && (xf > limits_rendertarget.right()))
return;
if ((yi < limits_rendertarget.top()) && (yf < limits_rendertarget.top()))
return;
if ((yi > limits_rendertarget.bottom()) && (yf > limits_rendertarget.bottom()))
return;
if (xi < limits_rendertarget.left())
xi = limits_rendertarget.left();
if (xf > limits_rendertarget.right())
xf = limits_rendertarget.right();
if (yi < limits_rendertarget.top())
yi = limits_rendertarget.top();
if (yf > limits_rendertarget.bottom())
yf = limits_rendertarget.bottom();
offset = pitch_depthbuffer * yf + xf * bpp;
addr = (uint8_t *)depthbuffer + offset;
if ((addr < basemempointer) || (addr > topmempointer))
{
machine().logerror("Bad memory pointer computed in clear_depth_buffer !\n");
return;
}
for (y = yi; y <= yf; y++)
for (x = xi; x <= xf; x++) {
offset = pitch_depthbuffer * y + x * bpp;
switch (depthformat_rendertarget) {
case NV2A_RT_DEPTH_FORMAT::Z16:
addr = (uint8_t *)depthbuffer + offset;
color = *((uint16_t *)addr);
break;
case NV2A_RT_DEPTH_FORMAT::Z24S8:
addr = (uint8_t *)depthbuffer + offset;
color = *((uint32_t *)addr);
break;
default:
return;
}
color = (color & ~mask) | (value & mask);
switch (depthformat_rendertarget) {
case NV2A_RT_DEPTH_FORMAT::Z16:
addr = (uint8_t *)depthbuffer + offset;
*((uint16_t *)addr) = color;
break;
case NV2A_RT_DEPTH_FORMAT::Z24S8:
addr = (uint8_t *)depthbuffer + offset;
*((uint32_t *)addr) = color;
break;
default:
return;
}
}
}
uint32_t nv2a_renderer::render_triangle_culling(const rectangle &cliprect, render_delegate callback, int paramcount, nv2avertex_t &_v1, nv2avertex_t &_v2, nv2avertex_t &_v3)
{
float areax2;
NV2A_GL_CULL_FACE face = NV2A_GL_CULL_FACE::FRONT;
if (backface_culling_enabled == false)
return render_triangle(cliprect, callback, paramcount, _v1, _v2, _v3);
if (backface_culling_culled == NV2A_GL_CULL_FACE::FRONT_AND_BACK)
{
triangles_bfculled++;
return 0;
}
areax2 = _v1.x*(_v2.y - _v3.y) + _v2.x*(_v3.y - _v1.y) + _v3.x*(_v1.y - _v2.y);
if (backface_culling_winding == NV2A_GL_FRONT_FACE::CCW)
{
if (-areax2 <= 0)
face = NV2A_GL_CULL_FACE::BACK;
else
face = NV2A_GL_CULL_FACE::FRONT;
} else
{
if (areax2 <= 0)
face = NV2A_GL_CULL_FACE::BACK;
else
face = NV2A_GL_CULL_FACE::FRONT;
}
if (face == NV2A_GL_CULL_FACE::FRONT)
if (backface_culling_culled == NV2A_GL_CULL_FACE::BACK)
return render_triangle(cliprect, callback, paramcount, _v1, _v2, _v3);
if (face == NV2A_GL_CULL_FACE::BACK)
if (backface_culling_culled == NV2A_GL_CULL_FACE::FRONT)
return render_triangle(cliprect, callback, paramcount, _v1, _v2, _v3);
triangles_bfculled++;
return 0;
}
int nv2a_renderer::clip_triangle_w(nv2avertex_t *vi[3], nv2avertex_t *vo)
{
int idx_prev, idx_curr;
int neg_prev, neg_curr;
double tfactor;
int idx;
const double wthreshold = 0.000001;
idx_prev = 2;
idx_curr = 0;
idx = 0;
neg_prev = vi[idx_prev]->w < wthreshold ? 1 : 0;
while (idx_curr < 3)
{
neg_curr = vi[idx_curr]->w < wthreshold ? 1 : 0;
if (neg_curr ^ neg_prev)
{
tfactor = (wthreshold - vi[idx_prev]->w) / (vi[idx_curr]->w - vi[idx_prev]->w);
// compute values for the new intermediate point
vo[idx].x = ((vi[idx_curr]->x - vi[idx_prev]->x) * tfactor) + vi[idx_prev]->x;
vo[idx].y = ((vi[idx_curr]->y - vi[idx_prev]->y) * tfactor) + vi[idx_prev]->y;
vo[idx].w = ((vi[idx_curr]->w - vi[idx_prev]->w) * tfactor) + vi[idx_prev]->w;
for (int n = 0; n < 13; n++)
vo[idx].p[n] = ((vi[idx_curr]->p[n] - vi[idx_prev]->p[n]) * tfactor) + vi[idx_prev]->p[n];
idx++;
}
if (neg_curr == 0)
{
vo[idx].x = vi[idx_curr]->x;
vo[idx].y = vi[idx_curr]->y;
vo[idx].w = vi[idx_curr]->w;
for (int n = 0; n < 13; n++)
vo[idx].p[n] = vi[idx_curr]->p[n];
idx++;
}
neg_prev = neg_curr;
idx_prev = idx_curr;
idx_curr++;
}
return idx;
}
uint32_t nv2a_renderer::render_triangle_clipping(const rectangle &cliprect, render_delegate callback, int paramcount, nv2avertex_t &_v1, nv2avertex_t &_v2, nv2avertex_t &_v3)
{
nv2avertex_t *vi[3];
nv2avertex_t vo[8];
int nv;
if ((_v1.w > 0) && (_v2.w > 0) && (_v3.w > 0))
return render_triangle_culling(cliprect, callback, paramcount, _v1, _v2, _v3);
if (enable_clipping_w == false)
return 0;
if ((_v1.w <= 0) && (_v2.w <= 0) && (_v3.w <= 0))
return 0;
// assign the elements of the array
vi[0] = &_v1;
vi[1] = &_v2;
vi[2] = &_v3;
// go back to the state before perpective divide
if (vertex_pipeline == 4)
{
for (int n = 0; n < 3; n++)
{
vi[n]->x = (vi[n]->x / (double)supersample_factor_x)*vi[n]->w;
vi[n]->y = (vi[n]->y / (double)supersample_factor_y)*vi[n]->w;
vi[n]->p[(int)VERTEX_PARAMETER::PARAM_Z] = vi[n]->p[(int)VERTEX_PARAMETER::PARAM_Z] * vi[n]->w;
}
} else
{
for (int n = 0; n < 3; n++)
{
// remove translate
vi[n]->x = vi[n]->x - matrix.translate[0];
vi[n]->y = vi[n]->y - matrix.translate[1];
vi[n]->p[(int)VERTEX_PARAMETER::PARAM_Z] = vi[n]->p[(int)VERTEX_PARAMETER::PARAM_Z] - matrix.translate[2];
// remove perspective divide
vi[n]->x = vi[n]->x * vi[n]->w;
vi[n]->y = vi[n]->y * vi[n]->w;
vi[n]->p[(int)VERTEX_PARAMETER::PARAM_Z] = vi[n]->p[(int)VERTEX_PARAMETER::PARAM_Z] * vi[n]->w;
}
}
// do the clipping
nv = clip_triangle_w(vi, vo);
// screen coordinates for the new points
if (vertex_pipeline == 4)
{
for (int n = 0; n < nv; n++)
{
vo[n].x = vo[n].x*(double)supersample_factor_x / vo[n].w;
vo[n].y = vo[n].y*(double)supersample_factor_y / vo[n].w;
vo[n].p[(int)VERTEX_PARAMETER::PARAM_Z] = vo[n].p[(int)VERTEX_PARAMETER::PARAM_Z] / vo[n].w;
}
} else
{
for (int n = 0; n < nv; n++)
{
// apply perspective divide
vo[n].x = vo[n].x / vo[n].w;
vo[n].y = vo[n].y / vo[n].w;
vo[n].p[(int)VERTEX_PARAMETER::PARAM_Z] = vo[n].p[(int)VERTEX_PARAMETER::PARAM_Z] / vo[n].w;
// apply translate
vo[n].x = vo[n].x + matrix.translate[0];
vo[n].y = vo[n].y + matrix.translate[1];
vo[n].p[(int)VERTEX_PARAMETER::PARAM_Z] = vo[n].p[(int)VERTEX_PARAMETER::PARAM_Z] + matrix.translate[2];
}
}
for (int n = 1; n <= (nv - 2); n++)
render_triangle_culling(cliprect, callback, paramcount, vo[0], vo[n], vo[n + 1]);
return 0;
}
void nv2a_renderer::assemble_primitive(vertex_nv *source, int count, render_delegate &renderspans)
{
uint32_t pc = primitives_count;
for (; count > 0; count--) {
if (primitive_type == NV2A_BEGIN_END::QUADS) {
convert_vertices_poly(source, vertex_xy + vertex_count + vertex_accumulated, 1);
vertex_accumulated++;
if (vertex_accumulated == 4) {
primitives_count++;
vertex_accumulated = 0;
render_triangle_clipping(limits_rendertarget, renderspans, 5 + 4 * 2, vertex_xy[vertex_count], vertex_xy[vertex_count + 1], vertex_xy[vertex_count + 2]);
render_triangle_clipping(limits_rendertarget, renderspans, 5 + 4 * 2, vertex_xy[vertex_count], vertex_xy[vertex_count + 2], vertex_xy[vertex_count + 3]);
vertex_count = (vertex_count + 4) & 1023;
wait();
}
}
else if (primitive_type == NV2A_BEGIN_END::TRIANGLES) {
convert_vertices_poly(source, vertex_xy + vertex_count + vertex_accumulated, 1);
vertex_accumulated++;
if (vertex_accumulated == 3) {
primitives_count++;
vertex_accumulated = 0;
render_triangle_clipping(limits_rendertarget, renderspans, 5 + 4 * 2, vertex_xy[vertex_count], vertex_xy[(vertex_count + 1) & 1023], vertex_xy[(vertex_count + 2) & 1023]); // 4 rgba, 4 texture units 2 uv
vertex_count = (vertex_count + 3) & 1023;
wait();
}
}
else if (primitive_type == NV2A_BEGIN_END::TRIANGLE_FAN) {
if (vertex_accumulated == 0)
{
convert_vertices_poly(source, vertex_xy + 1024, 1);
vertex_accumulated = 1;
}
else if (vertex_accumulated == 1)
{
convert_vertices_poly(source, vertex_xy, 1);
vertex_accumulated = 2;
vertex_count = 1;
}
else
{
primitives_count++;
// if software sends the vertices 0 1 2 3 4 5 6
// hardware will draw triangles made by (0,1,2) (0,2,3) (0,3,4) (0,4,5) (0,5,6)
convert_vertices_poly(source, vertex_xy + vertex_count, 1);
render_triangle_clipping(limits_rendertarget, renderspans, 5 + 4 * 2, vertex_xy[1024], vertex_xy[(vertex_count - 1) & 1023], vertex_xy[vertex_count]);
vertex_count = (vertex_count + 1) & 1023;
wait();
}
}
else if (primitive_type == NV2A_BEGIN_END::TRIANGLE_STRIP) {
if (vertex_accumulated == 0)
{
convert_vertices_poly(source, vertex_xy, 1);
vertex_accumulated = 1;
}
else if (vertex_accumulated == 1)
{
convert_vertices_poly(source, vertex_xy + 1, 1);
vertex_accumulated = 2;
vertex_count = 2;
}
else
{
primitives_count++;
// if software sends the vertices 0 1 2 3 4 5 6
// hardware will draw triangles made by (0,1,2) (1,3,2) (2,3,4) (3,5,4) (4,5,6)
convert_vertices_poly(source, vertex_xy + vertex_count, 1);
if ((vertex_count & 1) == 0)
render_triangle_clipping(limits_rendertarget, renderspans, 5 + 4 * 2, vertex_xy[(vertex_count - 2) & 1023], vertex_xy[(vertex_count - 1) & 1023], vertex_xy[vertex_count]);
else
render_triangle_clipping(limits_rendertarget, renderspans, 5 + 4 * 2, vertex_xy[(vertex_count - 2) & 1023], vertex_xy[vertex_count], vertex_xy[(vertex_count - 1) & 1023]);
vertex_count = (vertex_count + 1) & 1023;
wait();
}
}
else if (primitive_type == NV2A_BEGIN_END::QUAD_STRIP) {
if (vertex_accumulated == 0)
{
convert_vertices_poly(source, vertex_xy, 1);
vertex_accumulated = 1;
}
else if (vertex_accumulated == 1)
{
convert_vertices_poly(source, vertex_xy + 1, 1);
vertex_accumulated = 2;
vertex_count = 0;
}
else
{
convert_vertices_poly(source, vertex_xy + ((vertex_count + vertex_accumulated) & 1023), 1);
vertex_accumulated++;
if (vertex_accumulated == 4)
{
primitives_count++;
// quad is made of vertices vertex count +0 +1 +3 +2
render_triangle_clipping(limits_rendertarget, renderspans, 5 + 4 * 2, vertex_xy[vertex_count + 0], vertex_xy[vertex_count + 1], vertex_xy[(vertex_count + 3) & 1023]);
render_triangle_clipping(limits_rendertarget, renderspans, 5 + 4 * 2, vertex_xy[(vertex_count + 3) & 1023], vertex_xy[(vertex_count + 2) & 1023], vertex_xy[vertex_count + 0]);
vertex_accumulated = 2;
vertex_count = (vertex_count + 2) & 1023;
wait();
}
}
}
else {
if (vertex_count == 0)
machine().logerror("Unsupported primitive %d\n", int(primitive_type));
vertex_count++;
}
source++;
}
primitives_total_count += primitives_count - pc;
}
void nv2a_renderer::compute_limits_rendertarget(uint32_t chanel, uint32_t subchannel)
{
uint32_t data;
int x, w;
int y, h;
data = channel[chanel][subchannel].object.method[0x0200 / 4];
x = data & 0xffff;
w = (data >> 16) & 0xffff;
x = x*supersample_factor_x;
w = w*supersample_factor_x;
limits_rendertarget.setx(x, x + w - 1);
data = channel[chanel][subchannel].object.method[0x0204 / 4];
y = data & 0xffff;
h = (data >> 16) & 0xffff;
y = y*supersample_factor_y;
h = h*supersample_factor_y;
limits_rendertarget.sety(y, y + h - 1);
}
void nv2a_renderer::compute_size_rendertarget(uint32_t chanel, uint32_t subchannel)
{
size_rendertarget = pitch_rendertarget*(limits_rendertarget.bottom() + 1);
size_depthbuffer = pitch_depthbuffer*(limits_rendertarget.bottom() + 1);
}
int nv2a_renderer::geforce_exec_method(address_space & space, uint32_t chanel, uint32_t subchannel, uint32_t method, uint32_t address, int &countlen)
{
uint32_t maddress;
uint32_t data;
maddress = method * 4;
data = space.read_dword(address);
channel[chanel][subchannel].object.method[method] = data;
#ifdef LOG_NV2A
//printf("A:%08X MTHD:%08X D:%08X\n\r",address,maddress,data);
#endif
if (maddress == 0x17fc) {
#if 0 // useful while debugging to see what coordinates have been used
static int debugvc = 0;
if (debugvc)
if (data == 0)
{
printf("%d %d\n\r", (int)primitive_type, vertex_first);
for (int n = 0; n < vertex_first; n++)
{
if (indexesleft_count > 0)
printf("%d i:%d ", n, vertex_indexes[n]);
else
printf("%d ", n);
printf("X:%f Y:%f Z:%f W:%f x:%f y:%f\n\r", vertex_software[n].attribute[0].fv[0], vertex_software[n].attribute[0].fv[1], vertex_software[n].attribute[0].fv[2], vertex_software[n].attribute[0].fv[3], vertex_xy[n].x, vertex_xy[n].y);
}
}
#endif
vertex_count = 0;
vertex_first = 0;
vertex_accumulated = 0;
indexesleft_count = 0;
indexesleft_first = 0;
primitives_count = 0;
primitive_type = (NV2A_BEGIN_END)data;
if (data != 0) {
if (((channel[chanel][subchannel].object.method[0x1e60 / 4] & 7) > 0) && (combiner.used != 0))
render_spans_callback = render_delegate(&nv2a_renderer::render_register_combiners, this);
else if (texture[0].enabled)
render_spans_callback = render_delegate(&nv2a_renderer::render_texture_simple, this);
else
render_spans_callback = render_delegate(&nv2a_renderer::render_color, this);
}
countlen--;
}
if (maddress == 0x1810) {
// draw vertices
int offset, count;
uint32_t n;
offset = data & 0xffffff;
count = (data >> 24) & 0xff;
#ifdef LOG_NV2A
printf("vertex %d %d\n\r", offset, count);
#endif
for (n = 0; n <= count; n++) {
read_vertices_0x1810(space, vertex_software + vertex_first, n + offset, 1);
assemble_primitive(vertex_software + vertex_first, 1, render_spans_callback);
vertex_first = (vertex_first + 1) & 1023;
}
countlen--;
}
if ((maddress == 0x1800) || (maddress == 0x1808)) {
int mult;
if (maddress == 0x1800)
mult = 2;
else
mult = 1;
// vertices are selected from the vertex buffer using an array of indexes
// each dword after 1800 contains two 16 bit index values to select the vartices
// each dword after 1808 contains a 32 bit index value to select the vartices
while (countlen > 0) {
int n;
data = space.read_dword(address);
n = indexesleft_first + indexesleft_count;
if (mult == 2) {
vertex_indexes[n & 1023] = data & 0xffff;
vertex_indexes[(n + 1) & 1023] = (data >> 16) & 0xffff;
indexesleft_count = indexesleft_count + 2;
}
else {
vertex_indexes[n & 1023] = data;
indexesleft_count = indexesleft_count + 1;
}
address += 4;
countlen--;
read_vertices_0x180x(space, vertex_software + vertex_first, address, mult);
assemble_primitive(vertex_software + vertex_first, mult, render_spans_callback);
vertex_first = (vertex_first + mult) & 1023;
}
}
if (maddress == 0x1818) {
// vertices are taken from the next words, not from a vertex buffer
// first send primitive type with 17fc
// then countlen number of dwords with 1818
// end with 17fc primitive type 0
// at 1760 16 words specify the vertex format:for each possible vertex attribute the number of components (0=not present) and type of each
while (countlen > 0) {
int c;
c = read_vertices_0x1818(space, vertex_software + vertex_first, address, 1);
countlen = countlen - c;
if (countlen < 0) {
machine().logerror("Method 0x1818 missing %d words\n", -countlen);
countlen = 0;
break;
}
address = address + c * 4;
assemble_primitive(vertex_software + vertex_first, 1, render_spans_callback);
vertex_first = (vertex_first + 1) & 1023;
}
}
if ((maddress >= 0x1880) && (maddress < 0x1900))
{
int v = maddress - 0x1880; // 16 couples,2 float per couple,16*2*4=128
int attr = v >> 3;
int comp = (v >> 2) & 1;
persistvertexattr.attribute[attr].iv[comp] = data;
if (comp == 1)
{
persistvertexattr.attribute[attr].fv[2] = 0;
persistvertexattr.attribute[attr].fv[3] = 1;
if (attr == 0)
assemble_primitive(&persistvertexattr, 1, render_spans_callback);
}
}
if ((maddress >= 0x1900) && (maddress < 0x1940))
{
int v = maddress - 0x1900; // 16 dwords,2 values per dword
int attr = v >> 2;
uint16_t d1 = data & 0xffff;
uint16_t d2 = data >> 16;
persistvertexattr.attribute[attr].fv[0] = (float)((int16_t)d1);
persistvertexattr.attribute[attr].fv[1] = (float)((int16_t)d2);
persistvertexattr.attribute[attr].fv[2] = 0;
persistvertexattr.attribute[attr].fv[3] = 1;
if (attr == 0)
assemble_primitive(&persistvertexattr, 1, render_spans_callback);
}
if ((maddress >= 0x1940) && (maddress < 0x1980))
{
int v = maddress - 0x1940; // 16 dwords,4 values per dword
int attr = v >> 2;
uint8_t d1 = data & 255;
uint8_t d2 = (data >> 8) & 255;
uint8_t d3 = (data >> 16) & 255;
uint8_t d4 = data >> 24;
persistvertexattr.attribute[attr].fv[0] = (float)d1;
persistvertexattr.attribute[attr].fv[1] = (float)d2;
persistvertexattr.attribute[attr].fv[2] = (float)d3;
persistvertexattr.attribute[attr].fv[3] = (float)d4;
if (attr == 0)
assemble_primitive(&persistvertexattr, 1, render_spans_callback);
}
if ((maddress >= 0x1980) && (maddress < 0x1a00))
{
int v = maddress - 0x1980; // 16 couples,4 values per couple,16*2*4=128
int attr = v >> 3;
int comp = (v >> 1) & 2;
uint16_t d1 = data & 0xffff;
uint16_t d2 = data >> 16;
persistvertexattr.attribute[attr].fv[comp] = (float)((int16_t)d1);
persistvertexattr.attribute[attr].fv[comp+1] = (float)((int16_t)d2);
if (comp == 2)
if (attr == 0)
assemble_primitive(&persistvertexattr, 1, render_spans_callback);
}
if ((maddress >= 0x1a00) && (maddress < 0x1b00))
{
int v = maddress - 0x1a00; // 16 groups,4 float per group
int attr = v >> 4;
int comp = (v >> 2) & 3;
persistvertexattr.attribute[attr].iv[comp] = data;
if (comp == 3)
if (attr == 0)
assemble_primitive(&persistvertexattr, 1, render_spans_callback);
}
if ((maddress >= 0x1518) && (maddress < 0x1528))
{
int v = maddress - 0x1518;
int comp = v >> 2;
persistvertexattr.attribute[(int)NV2A_VERTEX_ATTR::POS].iv[comp] = data;
if (comp == 3)
assemble_primitive(&persistvertexattr, 1, render_spans_callback);
}
if ((maddress >= 0x1720) && (maddress < 0x1760)) {
int bit = method - 0x1720 / 4;
if (data & 0x80000000)
vertexbuffer_address[bit] = (data & 0x0fffffff) + dma_offset[1];
else
vertexbuffer_address[bit] = (data & 0x0fffffff) + dma_offset[0];
}
if ((maddress >= 0x1760) && (maddress < 0x17A0)) {
int bit = method - 0x1760 / 4;
vertexbuffer_stride[bit] = (data >> 8) & 255;
vertexbuffer_kind[bit] = (NV2A_VTXBUF_TYPE)(data & 15);
vertexbuffer_size[bit] = (data >> 4) & 15;
switch (vertexbuffer_kind[bit]) {
case NV2A_VTXBUF_TYPE::UBYTE_D3D:
vertex_attribute_words[bit] = (vertexbuffer_size[bit] * 1) >> 2;
break;
case NV2A_VTXBUF_TYPE::FLOAT:
vertex_attribute_words[bit] = (vertexbuffer_size[bit] * 4) >> 2;
break;
case NV2A_VTXBUF_TYPE::UBYTE_OGL:
vertex_attribute_words[bit] = (vertexbuffer_size[bit] * 1) >> 2;
break;
case NV2A_VTXBUF_TYPE::USHORT:
vertex_attribute_words[bit] = (vertexbuffer_size[bit] * 2) >> 2;
break;
case NV2A_VTXBUF_TYPE::FLOAT_PACKED:
vertex_attribute_words[bit] = (vertexbuffer_size[bit] * 4) >> 2;
break;
default:
vertex_attribute_words[bit] = 0;
}
if (vertexbuffer_size[bit] > 0)
enabled_vertex_attributes |= (1 << bit);
else
enabled_vertex_attributes &= ~(1 << bit);
for (int n = bit + 1; n < 16; n++) {
if ((enabled_vertex_attributes & (1 << (n - 1))) != 0)
vertex_attribute_offset[n] = vertex_attribute_offset[n - 1] + vertex_attribute_words[n - 1];
else
vertex_attribute_offset[n] = vertex_attribute_offset[n - 1];
}
countlen--;
}
if ((maddress == 0x1d6c) || (maddress == 0x1a4))
countlen--;
if (maddress == 0x0308) {
backface_culling_enabled = data != 0 ? true : false;
}
if (maddress == 0x03a0) {
backface_culling_winding = (NV2A_GL_FRONT_FACE)data;
}
if (maddress == 0x039c) {
backface_culling_culled = (NV2A_GL_CULL_FACE)data;
}
if (maddress == 0x019c) {
geforce_read_dma_object(data, dma_offset[0], dma_size[0]);
}
if (maddress == 0x01a0) {
geforce_read_dma_object(data, dma_offset[1], dma_size[1]);
}
if (maddress == 0x1d70) {
// with 1d70 write the value at offest [1d6c] inside dma object [1a4]
uint32_t offset, base;
uint32_t dmahand, dmaoff, smasiz;
offset = channel[chanel][subchannel].object.method[0x1d6c / 4];
dmahand = channel[chanel][subchannel].object.method[0x1a4 / 4];
geforce_read_dma_object(dmahand, dmaoff, smasiz);
base = dmaoff;
space.write_dword(base + offset, data);
// software expects to find the parameter of this method at pgraph offset b10
pgraph[0xb10 / 4] = data << 2;
countlen--;
}
if (maddress == 0x1d7c) {
antialias_control = data;
compute_supersample_factors(supersample_factor_x, supersample_factor_y);
compute_limits_rendertarget(chanel, subchannel);
countlen--;
}
if (maddress == 0x1d98) {
int x, w;
x = data & 0xffff;
w = (data >> 16) & 0xffff;
clear_rendertarget.setx(x, w);
countlen--;
}
if (maddress == 0x1d9c) {
int y, h;
y = data & 0xffff;
h = (data >> 16) & 0xffff;
clear_rendertarget.sety(y, h);
countlen--;
}
if (maddress == 0x1d94) {
// possible buffers: color, depth, stencil
// clear framebuffer
clear_render_target((data >> 4) & 15, channel[chanel][subchannel].object.method[0x1d90 / 4]);
clear_depth_buffer(data & 3, channel[chanel][subchannel].object.method[0x1d8c / 4]);
countlen--;
}
if ((maddress >= 0x02c0) && (maddress < 0x2e0)) {
int x, w, i;
i = (maddress - 0x2c0) / 4;
x = data & 0xffff;
w = (data >> 16) & 0xffff;
clippingwindows[i].setx(x, x + w - 1);
}
if ((maddress >= 0x02e0) && (maddress < 0x300)) {
int y, h, i;
i = (maddress - 0x2e0) / 4;
y = data & 0xffff;
h = (data >> 16) & 0xffff;
clippingwindows[i].sety(y, y + h - 1);
}
if (maddress == 0x0200) {
compute_limits_rendertarget(chanel, subchannel);
compute_size_rendertarget(chanel, subchannel);
}
if (maddress == 0x0204) {
compute_limits_rendertarget(chanel, subchannel);
compute_size_rendertarget(chanel, subchannel);
}
if (maddress == 0x0208) {
log2height_rendertarget = (data >> 24) & 255;
log2width_rendertarget = (data >> 16) & 255;
antialiasing_rendertarget = (data >> 12) & 15;
type_rendertarget = (NV2A_RT_TYPE)((data >> 8) & 15);
depthformat_rendertarget = (NV2A_RT_DEPTH_FORMAT)((data >> 4) & 15);
colorformat_rendertarget = (NV2A_COLOR_FORMAT)((data >> 0) & 15);
compute_supersample_factors(supersample_factor_x, supersample_factor_y);
compute_limits_rendertarget(chanel, subchannel);
compute_size_rendertarget(chanel, subchannel);
switch (colorformat_rendertarget) {
case NV2A_COLOR_FORMAT::R5G6B5:
bytespixel_rendertarget = 2;
break;
case NV2A_COLOR_FORMAT::X8R8G8B8_Z8R8G8B8:
case NV2A_COLOR_FORMAT::X8R8G8B8_X8R8G8B8:
case NV2A_COLOR_FORMAT::A8R8G8B8:
bytespixel_rendertarget = 4;
break;
case NV2A_COLOR_FORMAT::B8:
bytespixel_rendertarget = 1;
break;
default:
machine().logerror("Unknown render target color format %d\n\r", int(colorformat_rendertarget));
bytespixel_rendertarget = 4;
break;
}
dilate_rendertarget = dilatechose[(log2width_rendertarget << 4) + log2height_rendertarget];
}
if (maddress == 0x020c) {
pitch_rendertarget=data & 0xffff;
pitch_depthbuffer=(data >> 16) & 0xffff;
compute_size_rendertarget(chanel, subchannel);
#ifdef LOG_NV2A
printf("Pitch color %04X zbuffer %04X\n\r", pitch_rendertarget, pitch_depthbuffer);
#endif
countlen--;
}
if (maddress == 0x0100) {
countlen--;
if (data != 0) {
pgraph[0x704 / 4] = 0x100;
pgraph[0x708 / 4] = data;
pgraph[0x100 / 4] |= 1;
pgraph[0x108 / 4] |= 1;
if (update_interrupts() == true)
irq_callback(1); // IRQ 3
else
irq_callback(0); // IRQ 3
return 2;
}
else
return 0;
}
if (maddress == 0x0130) {
countlen--;
if (enable_waitvblank == true)
return 1; // block until next vblank
else
return 0;
}
if (maddress == 0x1d8c) {
countlen--;
// it is used to specify the clear value for the depth buffer (zbuffer)
// but also as a parameter for interrupt routines
pgraph[0x1a88 / 4] = data;
}
if (maddress == 0x1d90) {
countlen--;
// it is used to specify the clear value for the color buffer
// but also as a parameter for interrupt routines
pgraph[0x186c / 4] = data;
}
if (maddress == 0x0210) {
// framebuffer offset ?
rendertarget = (uint32_t *)direct_access_ptr(data);
#ifdef LOG_NV2A
printf("Render target at %08X\n\r", data);
#endif
countlen--;
}
if (maddress == 0x0214) {
// zbuffer offset ?
depthbuffer = (uint32_t *)direct_access_ptr(data);
#ifdef LOG_NV2A
printf("Depth buffer at %08X\n\r",data);
#endif
if ((data == 0) || (data > 0x7ffffffc))
depth_write_enabled = false;
else if (channel[chanel][subchannel].object.method[0x035c / 4] != 0)
depth_write_enabled = true;
else
depth_write_enabled = false;
countlen--;
}
if (maddress == 0x0300) {
alpha_test_enabled = data != 0;
}
if (maddress == 0x033c) {
alpha_func = (NV2A_COMPARISON_OP)data;
}
if (maddress == 0x0340) {
alpha_reference = data;
}
if (maddress == 0x0304) {
if (logical_operation_enabled)
blending_enabled = false;
else
blending_enabled = data != 0;
}
if (maddress == 0x030c) {
depth_test_enabled = data != 0;
}
if (maddress == 0x0354) {
depth_function = (NV2A_COMPARISON_OP)data;
}
if (maddress == 0x0358) {
//color_mask = data;
if (data & 0x000000ff)
data |= 0x000000ff;
if (data & 0x0000ff00)
data |= 0x0000ff00;
if (data & 0x00ff0000)
data |= 0x00ff0000;
if (data & 0xff000000)
data |= 0xff000000;
color_mask = data;
}
if (maddress == 0x035c) {
uint32_t g = channel[chanel][subchannel].object.method[0x0214 / 4];
depth_write_enabled = data != 0;
if ((g == 0) || (g > 0x7ffffffc))
depth_write_enabled = false;
}
if (maddress == 0x032c) {
stencil_test_enabled = data != 0;
}
if (maddress == 0x0364) {
stencil_func = (NV2A_COMPARISON_OP)data;
}
if (maddress == 0x0368) {
if (data > 255)
data = 255;
stencil_ref = data;
}
if (maddress == 0x036c) {
stencil_mask = data;
}
if (maddress == 0x0370) {
stencil_op_fail = (NV2A_STENCIL_OP)data;
}
if (maddress == 0x0374) {
stencil_op_zfail = (NV2A_STENCIL_OP)data;
}
if (maddress == 0x0378) {
stencil_op_zpass = (NV2A_STENCIL_OP)data;
}
if (maddress == 0x0344) {
blend_function_source = (NV2A_BLEND_FACTOR)data;
}
if (maddress == 0x0348) {
blend_function_destination = (NV2A_BLEND_FACTOR)data;
}
if (maddress == 0x034c) {
blend_color = data;
}
if (maddress == 0x0350) {
blend_equation = (NV2A_BLEND_EQUATION)data;
}
if (maddress == 0x0d40) {
if (data != 0)
blending_enabled = false;
else
blending_enabled = channel[chanel][subchannel].object.method[0x0304 / 4] != 0;
logical_operation_enabled = data != 0;
}
if (maddress == 0x0d44) {
logical_operation = (NV2A_LOGIC_OP)data;
}
// Texture Units
if ((maddress >= 0x1b00) && (maddress < 0x1c00)) {
int unit;//,off;
unit = (maddress >> 6) & 3;
//off=maddress & 0xc0;
maddress = maddress & ~0xc0;
if (maddress == 0x1b00) {
uint32_t offset;//,base;
//uint32_t dmahand,dmaoff,dmasiz;
offset = data;
texture[unit].buffer = direct_access_ptr(offset);
/*if (dma0 != 0) {
dmahand=channel[channel][subchannel].object.method[0x184/4];
geforce_read_dma_object(dmahand,dmaoff,smasiz);
} else if (dma1 != 0) {
dmahand=channel[channel][subchannel].object.method[0x188/4];
geforce_read_dma_object(dmahand,dmaoff,smasiz);
}*/
}
if (maddress == 0x1b04) {
int basesizeu, basesizev, basesizew, format;
bool rectangle;
texture[unit].dma0 = (data >> 0) & 1;
texture[unit].dma1 = (data >> 1) & 1;
texture[unit].cubic = (data >> 2) & 1;
texture[unit].noborder = (data >> 3) & 1;
texture[unit].dims = (data >> 4) & 15;
texture[unit].mipmap = (data >> 19) & 1;
format = (data >> 8) & 255;
basesizeu = (data >> 20) & 15;
basesizev = (data >> 24) & 15;
basesizew = (data >> 28) & 15;
texture[unit].sizeu = 1 << basesizeu;
texture[unit].sizev = 1 << basesizev;
texture[unit].sizew = 1 << basesizew;
texture[unit].dilate = dilatechose[(basesizeu << 4) + basesizev];
texture[unit].format = (NV2A_TEX_FORMAT)format;
switch (texture[unit].format)
{
case NV2A_TEX_FORMAT::A1R5G5B5_RECT:
case NV2A_TEX_FORMAT::R5G6B5_RECT:
case NV2A_TEX_FORMAT::A8R8G8B8_RECT:
case NV2A_TEX_FORMAT::DSDT8_RECT:
case NV2A_TEX_FORMAT::A4R4G4B4_RECT:
case NV2A_TEX_FORMAT::R8G8B8_RECT:
case NV2A_TEX_FORMAT::A8L8_RECT:
case NV2A_TEX_FORMAT::Z24_RECT:
case NV2A_TEX_FORMAT::Z16_RECT:
case NV2A_TEX_FORMAT::HILO16_RECT:
case NV2A_TEX_FORMAT::SIGNED_HILO8_RECT:
rectangle = true;
break;
default:
rectangle = false;
}
texture[unit].rectangle = rectangle;
if (debug_grab_texttype == format) {
FILE *f;
int written;
debug_grab_texttype = -1;
f = fopen(debug_grab_textfile, "wb");
if (f) {
written = (int)fwrite(texture[unit].buffer, texture[unit].sizeu*texture[unit].sizev * 4, 1, f);
fclose(f);
machine().logerror("Written %d bytes of texture to specified file\n", written);
}
else
machine().logerror("Unable to save texture to specified file\n");
}
}
/*if (maddress == 0x1b08) {
(data >> 0) & 15;
(data >> 4) & 15;
(data >> 8) & 15;
(data >> 12) & 15;
(data >> 16) & 15;
(data >> 20) & 15;
(data >> 24) & 255;
}*/
if (maddress == 0x1b0c) {
texture[unit].colorkey = (data >> 0) & 3;
texture[unit].imagefield = (data >> 3) & 1;
texture[unit].aniso = (data >> 4) & 3;
texture[unit].mipmapmaxlod = (data >> 6) & 0xfff;
texture[unit].mipmapminlod = (data >> 18) & 0xfff;
// enable texture
texture[unit].enabled = (data >> 30) & 3;
}
if (maddress == 0x1b10) {
texture[unit].rectangle_pitch = data >> 16;
}
if (maddress == 0x1b1c) {
texture[unit].rectheight = data & 0xffff;
texture[unit].rectwidth = data >> 16;
}
countlen--;
}
// projection matrix
if ((maddress >= 0x0440) && (maddress < 0x0480)) {
maddress = (maddress - 0x0440) / 4;
*(uint32_t *)(&matrix.projection[maddress >> 2][maddress & 3]) = data;
countlen--;
}
// modelview matrix
if ((maddress >= 0x0480) && (maddress < 0x04c0)) {
maddress = (maddress - 0x0480) / 4;
/* the modelview matrix is obtained by direct3d by multiplying the world matrix and the view matrix
modelview = world * view
given a point in 3d space with coordinates x y and z, to find te transformed coordinates
first create a row vector with components (x,y,z,1) then multiply the vector by the matrix
transformed = rowvector * matrix
in direct3d the matrix is stored as the sequence (first digit row, second digit column)
11 12 13 14
21 22 23 24
31 32 33 34
41 42 43 44
but it is sent transposed as the sequence
11 21 31 41 12 22 32 42 13 23 33 43 14 24 34 44
so in matrix.modelview[x][y] x is the column and y is the row of the direct3d matrix
*/
*(uint32_t *)(&matrix.modelview[maddress >> 2][maddress & 3]) = data;
countlen--;
}
// inverse modelview matrix
if ((maddress >= 0x0580) && (maddress < 0x05c0)) {
maddress = (maddress - 0x0580) / 4;
*(uint32_t *)(&matrix.modelview_inverse[maddress >> 2][maddress & 3]) = data;
countlen--;
}
// composite matrix
if ((maddress >= 0x0680) && (maddress < 0x06c0)) {
maddress = (maddress - 0x0680) / 4;
/* the composite matrix is computed by direct3d by multiplying the
world, view, projection and viewport matrices
composite = world * view * projection * viewport
the viewport matrix applies the viewport scale and offset
*/
*(uint32_t *)(&matrix.composite[maddress >> 2][maddress & 3]) = data;
countlen--;
}
// viewport translate
if ((maddress >= 0x0a20) && (maddress < 0x0a30)) {
maddress = (maddress - 0x0a20) / 4;
*(uint32_t *)(&matrix.translate[maddress]) = data;
// set corresponding vertex shader constant too
vertexprogram.exec.c_constant[59].iv(maddress, data); // constant -37
#ifdef LOG_NV2A
if (maddress == 3)
machine().logerror("viewport translate = {%f %f %f %f}\n", matrix.translate[0], matrix.translate[1], matrix.translate[2], matrix.translate[3]);
#endif
countlen--;
}
// viewport scale
if ((maddress >= 0x0af0) && (maddress < 0x0b00)) {
maddress = (maddress - 0x0af0) / 4;
*(uint32_t *)(&matrix.scale[maddress]) = data;
// set corresponding vertex shader constant too
vertexprogram.exec.c_constant[58].iv(maddress, data); // constant -38
#ifdef LOG_NV2A
if (maddress == 3)
machine().logerror("viewport scale = {%f %f %f %f}\n", matrix.scale[0], matrix.scale[1], matrix.scale[2], matrix.scale[3]);
#endif
countlen--;
}
// Vertex program (shader)
if (maddress == 0x1e94) {
/*if (data == 2)
machine().logerror("Enabled vertex program\n");
else if (data == 4)
machine().logerror("Enabled fixed function pipeline\n");
else if (data == 6)
machine().logerror("Enabled both fixed function pipeline and vertex program ?\n");
else
machine().logerror("Unknown value %d to method 0x1e94\n",data);*/
vertex_pipeline = data & 6;
countlen--;
}
if (maddress == 0x1e9c) {
//machine().logerror("VP_UPLOAD_FROM_ID %d\n",data);
vertexprogram.upload_instruction_index = data;
vertexprogram.upload_instruction_component = 0;
countlen--;
}
if (maddress == 0x1ea0) {
//machine().logerror("VP_START_FROM_ID %d\n",data);
vertexprogram.instructions = vertexprogram.upload_instruction_index;
vertexprogram.start_instruction = data;
countlen--;
}
if (maddress == 0x1ea4) {
//machine().logerror("VP_UPLOAD_CONST_ID %d\n",data);
vertexprogram.upload_parameter_index = data;
vertexprogram.upload_parameter_component = 0;
countlen--;
}
if ((maddress >= 0x0b00) && (maddress < 0x0b80)) {
//machine().logerror("VP_UPLOAD_INST\n");
if (vertexprogram.upload_instruction_index < 256) {
vertexprogram.exec.op[vertexprogram.upload_instruction_index].i[vertexprogram.upload_instruction_component] = data;
vertexprogram.exec.op[vertexprogram.upload_instruction_index].modified |= (1 << vertexprogram.upload_instruction_component);
}
else
machine().logerror("Need to increase size of vertexprogram.instruction to %d\n\r", vertexprogram.upload_instruction_index);
if (vertexprogram.exec.op[vertexprogram.upload_instruction_index].modified == 15) {
vertexprogram.exec.op[vertexprogram.upload_instruction_index].modified = 0;
vertexprogram.exec.decode_instruction(vertexprogram.upload_instruction_index);
}
vertexprogram.upload_instruction_component++;
if (vertexprogram.upload_instruction_component >= 4) {
vertexprogram.upload_instruction_component = 0;
vertexprogram.upload_instruction_index++;
}
}
if ((maddress >= 0x0b80) && (maddress < 0x0c00)) {
//machine().logerror("VP_UPLOAD_CONST\n");
if (vertexprogram.upload_parameter_index < 192) {
vertexprogram.exec.c_constant[vertexprogram.upload_parameter_index].iv(vertexprogram.upload_parameter_component, data);
}
else
machine().logerror("Need to increase size of vertexprogram.parameter to %d\n\r", vertexprogram.upload_parameter_index);
vertexprogram.upload_parameter_component++;
if (vertexprogram.upload_parameter_component >= 4) {
#ifdef LOG_NV2A
if ((vertexprogram.upload_parameter_index == 58) || (vertexprogram.upload_parameter_index == 59))
machine().logerror("vp constant %d (%s) = {%f %f %f %f}\n", vertexprogram.upload_parameter_index,
vertexprogram.upload_parameter_index == 58 ? "viewport scale" : "viewport translate",
vertexprogram.exec.c_constant[vertexprogram.upload_parameter_index].fv[0],
vertexprogram.exec.c_constant[vertexprogram.upload_parameter_index].fv[1],
vertexprogram.exec.c_constant[vertexprogram.upload_parameter_index].fv[2],
vertexprogram.exec.c_constant[vertexprogram.upload_parameter_index].fv[3]);
#endif
vertexprogram.upload_parameter_component = 0;
vertexprogram.upload_parameter_index++;
}
}
// Register combiners
if (maddress == 0x1e60) {
combiner.stages = data & 15;
countlen--;
}
if (maddress == 0x0288) {
combiner.final.mapin_rgbD_input = data & 15;
combiner.final.mapin_rgbD_component = (data >> 4) & 1;
combiner.final.mapin_rgbD_mapping = (data >> 5) & 7;
combiner.final.mapin_rgbC_input = (data >> 8) & 15;
combiner.final.mapin_rgbC_component = (data >> 12) & 1;
combiner.final.mapin_rgbC_mapping = (data >> 13) & 7;
combiner.final.mapin_rgbB_input = (data >> 16) & 15;
combiner.final.mapin_rgbB_component = (data >> 20) & 1;
combiner.final.mapin_rgbB_mapping = (data >> 21) & 7;
combiner.final.mapin_rgbA_input = (data >> 24) & 15;
combiner.final.mapin_rgbA_component = (data >> 28) & 1;
combiner.final.mapin_rgbA_mapping = (data >> 29) & 7;
countlen--;
}
if (maddress == 0x028c) {
combiner.final.color_sum_clamp = (data >> 7) & 1;
combiner.final.mapin_aG_input = (data >> 8) & 15;
combiner.final.mapin_aG_component = (data >> 12) & 1;
combiner.final.mapin_aG_mapping = (data >> 13) & 7;
combiner.final.mapin_rgbF_input = (data >> 16) & 15;
combiner.final.mapin_rgbF_component = (data >> 20) & 1;
combiner.final.mapin_rgbF_mapping = (data >> 21) & 7;
combiner.final.mapin_rgbE_input = (data >> 24) & 15;
combiner.final.mapin_rgbE_component = (data >> 28) & 1;
combiner.final.mapin_rgbE_mapping = (data >> 29) & 7;
countlen--;
}
if (maddress == 0x1e20) {
combiner_argb8_float(data, combiner.final.register_constantcolor0);
countlen--;
}
if (maddress == 0x1e24) {
combiner_argb8_float(data, combiner.final.register_constantcolor1);
countlen--;
}
if ((maddress >= 0x0260) && (maddress < 0x0280)) {
int n;
n = (maddress - 0x0260) >> 2;
combiner.stage[n].mapin_aD_input = data & 15;
combiner.stage[n].mapin_aD_component = (data >> 4) & 1;
combiner.stage[n].mapin_aD_mapping = (data >> 5) & 7;
combiner.stage[n].mapin_aC_input = (data >> 8) & 15;
combiner.stage[n].mapin_aC_component = (data >> 12) & 1;
combiner.stage[n].mapin_aC_mapping = (data >> 13) & 7;
combiner.stage[n].mapin_aB_input = (data >> 16) & 15;
combiner.stage[n].mapin_aB_component = (data >> 20) & 1;
combiner.stage[n].mapin_aB_mapping = (data >> 21) & 7;
combiner.stage[n].mapin_aA_input = (data >> 24) & 15;
combiner.stage[n].mapin_aA_component = (data >> 28) & 1;
combiner.stage[n].mapin_aA_mapping = (data >> 29) & 7;
countlen--;
}
if ((maddress >= 0x0ac0) && (maddress < 0x0ae0)) {
int n;
n = (maddress - 0x0ac0) >> 2;
combiner.stage[n].mapin_rgbD_input = data & 15;
combiner.stage[n].mapin_rgbD_component = (data >> 4) & 1;
combiner.stage[n].mapin_rgbD_mapping = (data >> 5) & 7;
combiner.stage[n].mapin_rgbC_input = (data >> 8) & 15;
combiner.stage[n].mapin_rgbC_component = (data >> 12) & 1;
combiner.stage[n].mapin_rgbC_mapping = (data >> 13) & 7;
combiner.stage[n].mapin_rgbB_input = (data >> 16) & 15;
combiner.stage[n].mapin_rgbB_component = (data >> 20) & 1;
combiner.stage[n].mapin_rgbB_mapping = (data >> 21) & 7;
combiner.stage[n].mapin_rgbA_input = (data >> 24) & 15;
combiner.stage[n].mapin_rgbA_component = (data >> 28) & 1;
combiner.stage[n].mapin_rgbA_mapping = (data >> 29) & 7;
countlen--;
}
if ((maddress >= 0x0a60) && (maddress < 0x0a80)) {
int n;
n = (maddress - 0x0a60) >> 2;
combiner_argb8_float(data, combiner.stage[n].register_constantcolor0);
countlen--;
}
if ((maddress >= 0x0a80) && (maddress < 0x0aa0)) {
int n;
n = (maddress - 0x0a80) >> 2;
combiner_argb8_float(data, combiner.stage[n].register_constantcolor1);
countlen--;
}
if ((maddress >= 0x0aa0) && (maddress < 0x0ac0)) {
int n;
n = (maddress - 0x0aa0) >> 2;
combiner.stage[n].mapout_aCD_output = data & 15;
combiner.stage[n].mapout_aAB_output = (data >> 4) & 15;
combiner.stage[n].mapout_aSUM_output = (data >> 8) & 15;
combiner.stage[n].mapout_aCD_dotproduct = (data >> 12) & 1;
combiner.stage[n].mapout_aAB_dotproduct = (data >> 13) & 1;
combiner.stage[n].mapout_a_muxsum = (data >> 14) & 1;
combiner.stage[n].mapout_a_bias = (data >> 15) & 1;
combiner.stage[n].mapout_a_scale = (data >> 16) & 3;
//combiner.=(data >> 27) & 7;
countlen--;
}
if ((maddress >= 0x1e40) && (maddress < 0x1e60)) {
int n;
n = (maddress - 0x1e40) >> 2;
combiner.stage[n].mapout_rgbCD_output = data & 15;
combiner.stage[n].mapout_rgbAB_output = (data >> 4) & 15;
combiner.stage[n].mapout_rgbSUM_output = (data >> 8) & 15;
combiner.stage[n].mapout_rgbCD_dotproduct = (data >> 12) & 1;
combiner.stage[n].mapout_rgbAB_dotproduct = (data >> 13) & 1;
combiner.stage[n].mapout_rgb_muxsum = (data >> 14) & 1;
combiner.stage[n].mapout_rgb_bias = (data >> 15) & 1;
combiner.stage[n].mapout_rgb_scale = (data >> 16) & 3;
//combiner.=(data >> 27) & 7;
countlen--;
}
return 0;
}
bool nv2a_renderer::toggle_register_combiners_usage()
{
combiner.used = 1 - combiner.used;
return combiner.used != 0;
}
bool nv2a_renderer::toggle_wait_vblank_support()
{
enable_waitvblank = !enable_waitvblank;
return enable_waitvblank;
}
bool nv2a_renderer::toggle_clipping_w_support()
{
enable_clipping_w = !enable_clipping_w;
return enable_clipping_w;
}
void nv2a_renderer::debug_grab_texture(int type, const char *filename)
{
debug_grab_texttype = type;
if (debug_grab_textfile == nullptr)
debug_grab_textfile = (char *)malloc(128);
strncpy(debug_grab_textfile, filename, 127);
}
void nv2a_renderer::debug_grab_vertex_program_slot(int slot, uint32_t *instruction)
{
if (slot >= 1024 / 4)
return;
instruction[0] = vertexprogram.exec.op[slot].i[0];
instruction[1] = vertexprogram.exec.op[slot].i[1];
instruction[2] = vertexprogram.exec.op[slot].i[2];
instruction[3] = vertexprogram.exec.op[slot].i[3];
}
void nv2a_renderer::combiner_argb8_float(uint32_t color, float reg[4])
{
reg[0] = (float)(color & 0xff) / 255.0f;
reg[1] = (float)((color >> 8) & 0xff) / 255.0f;
reg[2] = (float)((color >> 16) & 0xff) / 255.0f;
reg[3] = (float)((color >> 24) & 0xff) / 255.0f;
}
uint32_t nv2a_renderer::combiner_float_argb8(float reg[4])
{
uint32_t r, g, b, a;
a = reg[3] * 255.0f;
r = reg[2] * 255.0f;
g = reg[1] * 255.0f;
b = reg[0] * 255.0f;
return (a << 24) | (r << 16) | (g << 8) | b;
}
float nv2a_renderer::combiner_map_input_select(int code, int index)
{
switch (code) {
case 0:
default:
return combiner.register_zero[index];
case 1:
return combiner.register_color0[index];
case 2:
return combiner.register_color1[index];
case 3:
return combiner.register_fogcolor[index];
case 4:
return combiner.register_primarycolor[index];
case 5:
return combiner.register_secondarycolor[index];
case 8:
return combiner.register_texture0color[index];
case 9:
return combiner.register_texture1color[index];
case 10:
return combiner.register_texture2color[index];
case 11:
return combiner.register_texture3color[index];
case 12:
return combiner.register_spare0[index];
case 13:
return combiner.register_spare1[index];
case 14:
return combiner.variable_sumclamp[index];
case 15:
return combiner.variable_EF[index];
}
// never executed
//return 0;
}
float *nv2a_renderer::combiner_map_input_select3(int code)
{
switch (code) {
case 0:
default:
return combiner.register_zero;
case 1:
return combiner.register_color0;
case 2:
return combiner.register_color1;
case 3:
return combiner.register_fogcolor;
case 4:
return combiner.register_primarycolor;
case 5:
return combiner.register_secondarycolor;
case 8:
return combiner.register_texture0color;
case 9:
return combiner.register_texture1color;
case 10:
return combiner.register_texture2color;
case 11:
return combiner.register_texture3color;
case 12:
return combiner.register_spare0;
case 13:
return combiner.register_spare1;
case 14:
return combiner.variable_sumclamp;
case 15:
return combiner.variable_EF;
}
// never executed
//return 0;
}
float *nv2a_renderer::combiner_map_output_select3(int code)
{
switch (code) {
case 0:
return nullptr;
case 1:
return nullptr;
case 2:
return nullptr;
case 3:
return nullptr;
case 4:
return combiner.register_primarycolor;
case 5:
return combiner.register_secondarycolor;
case 8:
return combiner.register_texture0color;
case 9:
return combiner.register_texture1color;
case 10:
return combiner.register_texture2color;
case 11:
return combiner.register_texture3color;
case 12:
return combiner.register_spare0;
case 13:
return combiner.register_spare1;
case 14:
return nullptr;
case 15:
default:
return nullptr;
}
}
float nv2a_renderer::combiner_map_input_function(int code, float value)
{
float t;
switch (code) {
case 0:
return std::max(0.0f, value);
case 1:
t = std::max(value, 0.0f);
return 1.0f - std::min(t, 1.0f);
case 2:
return 2.0f * std::max(0.0f, value) - 1.0f;
case 3:
return -2.0f * std::max(0.0f, value) + 1.0f;
case 4:
return std::max(0.0f, value) - 0.5f;
case 5:
return -std::max(0.0f, value) + 0.5f;
case 6:
return value;
case 7:
default:
return -value;
}
// never executed
//return 0;
}
void nv2a_renderer::combiner_map_input_function3(int code, float *data)
{
float t;
switch (code) {
case 0:
data[0] = std::max(0.0f, data[0]);
data[1] = std::max(0.0f, data[1]);
data[2] = std::max(0.0f, data[2]);
break;
case 1:
t = std::max(data[0], 0.0f);
data[0] = 1.0f - std::min(t, 1.0f);
t = std::max(data[1], 0.0f);
data[1] = 1.0f - std::min(t, 1.0f);
t = std::max(data[2], 0.0f);
data[2] = 1.0f - std::min(t, 1.0f);
break;
case 2:
data[0] = 2.0f * std::max(0.0f, data[0]) - 1.0f;
data[1] = 2.0f * std::max(0.0f, data[1]) - 1.0f;
data[2] = 2.0f * std::max(0.0f, data[2]) - 1.0f;
break;
case 3:
data[0] = -2.0f * std::max(0.0f, data[0]) + 1.0f;
data[1] = -2.0f * std::max(0.0f, data[1]) + 1.0f;
data[2] = -2.0f * std::max(0.0f, data[2]) + 1.0f;
break;
case 4:
data[0] = std::max(0.0f, data[0]) - 0.5f;
data[1] = std::max(0.0f, data[1]) - 0.5f;
data[2] = std::max(0.0f, data[2]) - 0.5f;
break;
case 5:
data[0] = -std::max(0.0f, data[0]) + 0.5f;
data[1] = -std::max(0.0f, data[1]) + 0.5f;
data[2] = -std::max(0.0f, data[2]) + 0.5f;
break;
case 6:
return;
case 7:
default:
data[0] = -data[0];
data[1] = -data[1];
data[2] = -data[2];
break;
}
}
void nv2a_renderer::combiner_initialize_registers(uint32_t argb8[6])
{
combiner_argb8_float(argb8[0], combiner.register_primarycolor);
combiner_argb8_float(argb8[1], combiner.register_secondarycolor);
combiner_argb8_float(argb8[2], combiner.register_texture0color);
combiner_argb8_float(argb8[3], combiner.register_texture1color);
combiner_argb8_float(argb8[4], combiner.register_texture2color);
combiner_argb8_float(argb8[5], combiner.register_texture3color);
combiner.register_spare0[3] = combiner.register_texture0color[3];
combiner.register_zero[0] = combiner.register_zero[1] = combiner.register_zero[2] = combiner.register_zero[3] = 0;
}
void nv2a_renderer::combiner_initialize_stage(int stage_number)
{
int n = stage_number;
// put register_constantcolor0 in register_color0
combiner.register_color0[0] = combiner.stage[n].register_constantcolor0[0];
combiner.register_color0[1] = combiner.stage[n].register_constantcolor0[1];
combiner.register_color0[2] = combiner.stage[n].register_constantcolor0[2];
combiner.register_color0[3] = combiner.stage[n].register_constantcolor0[3];
// put register_constantcolor1 in register_color1
combiner.register_color1[0] = combiner.stage[n].register_constantcolor1[0];
combiner.register_color1[1] = combiner.stage[n].register_constantcolor1[1];
combiner.register_color1[2] = combiner.stage[n].register_constantcolor1[2];
combiner.register_color1[3] = combiner.stage[n].register_constantcolor1[3];
}
void nv2a_renderer::combiner_initialize_final()
{
// put register_constantcolor0 in register_color0
combiner.register_color0[0] = combiner.final.register_constantcolor0[0];
combiner.register_color0[1] = combiner.final.register_constantcolor0[1];
combiner.register_color0[2] = combiner.final.register_constantcolor0[2];
combiner.register_color0[3] = combiner.final.register_constantcolor0[3];
// put register_constantcolor1 in register_color1
combiner.register_color1[0] = combiner.final.register_constantcolor1[0];
combiner.register_color1[1] = combiner.final.register_constantcolor1[1];
combiner.register_color1[2] = combiner.final.register_constantcolor1[2];
combiner.register_color1[3] = combiner.final.register_constantcolor1[3];
}
void nv2a_renderer::combiner_map_input(int stage_number)
{
int n = stage_number;
int c, d, i;
float v, *pv;
// A
v = combiner_map_input_select(combiner.stage[n].mapin_aA_input, 2 + combiner.stage[n].mapin_aA_component);
combiner.variable_A[3] = combiner_map_input_function(combiner.stage[n].mapin_aA_mapping, v);
// B
v = combiner_map_input_select(combiner.stage[n].mapin_aB_input, 2 + combiner.stage[n].mapin_aB_component);
combiner.variable_B[3] = combiner_map_input_function(combiner.stage[n].mapin_aB_mapping, v);
// C
v = combiner_map_input_select(combiner.stage[n].mapin_aC_input, 2 + combiner.stage[n].mapin_aC_component);
combiner.variable_C[3] = combiner_map_input_function(combiner.stage[n].mapin_aC_mapping, v);
// D
v = combiner_map_input_select(combiner.stage[n].mapin_aD_input, 2 + combiner.stage[n].mapin_aD_component);
combiner.variable_D[3] = combiner_map_input_function(combiner.stage[n].mapin_aD_mapping, v);
// A
pv = combiner_map_input_select3(combiner.stage[n].mapin_rgbA_input);
c = combiner.stage[n].mapin_rgbA_component * 3;
i = ~combiner.stage[n].mapin_rgbA_component & 1;
for (d = 0; d < 3; d++) {
combiner.variable_A[d] = pv[c];
c = c + i;
}
combiner_map_input_function3(combiner.stage[n].mapin_rgbA_mapping, combiner.variable_A);
// B
pv = combiner_map_input_select3(combiner.stage[n].mapin_rgbB_input);
c = combiner.stage[n].mapin_rgbB_component * 3;
i = ~combiner.stage[n].mapin_rgbB_component & 1;
for (d = 0; d < 3; d++) {
combiner.variable_B[d] = pv[c];
c = c + i;
}
combiner_map_input_function3(combiner.stage[n].mapin_rgbB_mapping, combiner.variable_B);
// C
pv = combiner_map_input_select3(combiner.stage[n].mapin_rgbC_input);
c = combiner.stage[n].mapin_rgbC_component * 3;
i = ~combiner.stage[n].mapin_rgbC_component & 1;
for (d = 0; d < 3; d++) {
combiner.variable_C[d] = pv[c];
c = c + i;
}
combiner_map_input_function3(combiner.stage[n].mapin_rgbC_mapping, combiner.variable_C);
// D
pv = combiner_map_input_select3(combiner.stage[n].mapin_rgbD_input);
c = combiner.stage[n].mapin_rgbD_component * 3;
i = ~combiner.stage[n].mapin_rgbD_component & 1;
for (d = 0; d < 3; d++) {
combiner.variable_D[d] = pv[c];
c = c + i;
}
combiner_map_input_function3(combiner.stage[n].mapin_rgbD_mapping, combiner.variable_D);
}
void nv2a_renderer::combiner_map_output(int stage_number)
{
int n = stage_number;
float *f;
// rgb
f = combiner_map_output_select3(combiner.stage[n].mapout_rgbAB_output);
if (f) {
f[0] = combiner.function_RGBop1[0];
f[1] = combiner.function_RGBop1[1];
f[2] = combiner.function_RGBop1[2];
}
f = combiner_map_output_select3(combiner.stage[n].mapout_rgbCD_output);
if (f) {
f[0] = combiner.function_RGBop2[0];
f[1] = combiner.function_RGBop2[1];
f[2] = combiner.function_RGBop2[2];
}
if ((combiner.stage[n].mapout_rgbAB_dotproduct | combiner.stage[n].mapout_rgbCD_dotproduct) == 0) {
f = combiner_map_output_select3(combiner.stage[n].mapout_rgbSUM_output);
if (f) {
f[0] = combiner.function_RGBop3[0];
f[1] = combiner.function_RGBop3[1];
f[2] = combiner.function_RGBop3[2];
}
}
// a
f = combiner_map_output_select3(combiner.stage[n].mapout_aAB_output);
if (f)
f[3] = combiner.function_Aop1;
f = combiner_map_output_select3(combiner.stage[n].mapout_aCD_output);
if (f)
f[3] = combiner.function_Aop2;
f = combiner_map_output_select3(combiner.stage[n].mapout_aSUM_output);
if (f)
f[3] = combiner.function_Aop3;
}
void nv2a_renderer::combiner_map_final_input()
{
int i, c, d;
float *pv;
// E
pv = combiner_map_input_select3(combiner.final.mapin_rgbE_input);
c = combiner.final.mapin_rgbE_component * 3;
i = ~combiner.final.mapin_rgbE_component & 1;
for (d = 0; d < 3; d++) {
combiner.variable_E[d] = pv[c];
c = c + i;
}
combiner_map_input_function3(combiner.final.mapin_rgbE_mapping, combiner.variable_E);
// F
pv = combiner_map_input_select3(combiner.final.mapin_rgbF_input);
c = combiner.final.mapin_rgbF_component * 3;
i = ~combiner.final.mapin_rgbF_component & 1;
for (d = 0; d < 3; d++) {
combiner.variable_F[d] = pv[c];
c = c + i;
}
combiner_map_input_function3(combiner.final.mapin_rgbF_mapping, combiner.variable_F);
// EF
combiner.variable_EF[0] = combiner.variable_E[0] * combiner.variable_F[0];
combiner.variable_EF[1] = combiner.variable_E[1] * combiner.variable_F[1];
combiner.variable_EF[2] = combiner.variable_E[2] * combiner.variable_F[2];
// sumclamp
combiner.variable_sumclamp[0] = std::max(0.0f, combiner.register_spare0[0]) + std::max(0.0f, combiner.register_secondarycolor[0]);
combiner.variable_sumclamp[1] = std::max(0.0f, combiner.register_spare0[1]) + std::max(0.0f, combiner.register_secondarycolor[1]);
combiner.variable_sumclamp[2] = std::max(0.0f, combiner.register_spare0[2]) + std::max(0.0f, combiner.register_secondarycolor[2]);
if (combiner.final.color_sum_clamp != 0) {
combiner.variable_sumclamp[0] = std::min(combiner.variable_sumclamp[0], 1.0f);
combiner.variable_sumclamp[1] = std::min(combiner.variable_sumclamp[1], 1.0f);
combiner.variable_sumclamp[2] = std::min(combiner.variable_sumclamp[2], 1.0f);
}
// A
pv = combiner_map_input_select3(combiner.final.mapin_rgbA_input);
c = combiner.final.mapin_rgbA_component * 3;
i = ~combiner.final.mapin_rgbA_component & 1;
for (d = 0; d < 3; d++) {
combiner.variable_A[d] = pv[c];
c = c + i;
}
combiner_map_input_function3(combiner.final.mapin_rgbA_mapping, combiner.variable_A);
// B
pv = combiner_map_input_select3(combiner.final.mapin_rgbB_input);
c = combiner.final.mapin_rgbB_component * 3;
i = ~combiner.final.mapin_rgbB_component & 1;
for (d = 0; d < 3; d++) {
combiner.variable_B[d] = pv[c];
c = c + i;
}
combiner_map_input_function3(combiner.final.mapin_rgbB_mapping, combiner.variable_B);
// C
pv = combiner_map_input_select3(combiner.final.mapin_rgbC_input);
c = combiner.final.mapin_rgbC_component * 3;
i = ~combiner.final.mapin_rgbC_component & 1;
for (d = 0; d < 3; d++) {
combiner.variable_C[d] = pv[c];
c = c + i;
}
combiner_map_input_function3(combiner.final.mapin_rgbC_mapping, combiner.variable_C);
// D
pv = combiner_map_input_select3(combiner.final.mapin_rgbD_input);
c = combiner.final.mapin_rgbD_component * 3;
i = ~combiner.final.mapin_rgbD_component & 1;
for (d = 0; d < 3; d++) {
combiner.variable_D[d] = pv[c];
c = c + i;
}
combiner_map_input_function3(combiner.final.mapin_rgbD_mapping, combiner.variable_D);
// G
combiner.variable_G = combiner_map_input_select(combiner.final.mapin_aG_input, 2 + combiner.final.mapin_aG_component);
}
void nv2a_renderer::combiner_final_output()
{
// rgb
combiner.output[0] = combiner.variable_A[0] * combiner.variable_B[0] + (1.0f - combiner.variable_A[0])*combiner.variable_C[0] + combiner.variable_D[0];
combiner.output[1] = combiner.variable_A[1] * combiner.variable_B[1] + (1.0f - combiner.variable_A[1])*combiner.variable_C[1] + combiner.variable_D[1];
combiner.output[2] = combiner.variable_A[2] * combiner.variable_B[2] + (1.0f - combiner.variable_A[2])*combiner.variable_C[2] + combiner.variable_D[2];
combiner.output[0] = std::min(combiner.output[0], 1.0f);
combiner.output[1] = std::min(combiner.output[1], 1.0f);
combiner.output[2] = std::min(combiner.output[2], 1.0f);
// a
combiner.output[3] = combiner_map_input_function(combiner.final.mapin_aG_mapping, combiner.variable_G);
}
void nv2a_renderer::combiner_function_AB(float result[4])
{
result[0] = combiner.variable_A[0] * combiner.variable_B[0];
result[1] = combiner.variable_A[1] * combiner.variable_B[1];
result[2] = combiner.variable_A[2] * combiner.variable_B[2];
}
void nv2a_renderer::combiner_function_AdotB(float result[4])
{
result[0] = combiner.variable_A[0] * combiner.variable_B[0] + combiner.variable_A[1] * combiner.variable_B[1] + combiner.variable_A[2] * combiner.variable_B[2];
result[1] = result[0];
result[2] = result[0];
}
void nv2a_renderer::combiner_function_CD(float result[4])
{
result[0] = combiner.variable_C[0] * combiner.variable_D[0];
result[1] = combiner.variable_C[1] * combiner.variable_D[1];
result[2] = combiner.variable_C[2] * combiner.variable_D[2];
}
void nv2a_renderer::combiner_function_CdotD(float result[4])
{
result[0] = combiner.variable_C[0] * combiner.variable_D[0] + combiner.variable_C[1] * combiner.variable_D[1] + combiner.variable_C[2] * combiner.variable_D[2];
result[1] = result[0];
result[2] = result[0];
}
void nv2a_renderer::combiner_function_ABmuxCD(float result[4])
{
if (combiner.register_spare0[3] >= 0.5f)
combiner_function_AB(result);
else
combiner_function_CD(result);
}
void nv2a_renderer::combiner_function_ABsumCD(float result[4])
{
result[0] = combiner.variable_A[0] * combiner.variable_B[0] + combiner.variable_C[0] * combiner.variable_D[0];
result[1] = combiner.variable_A[1] * combiner.variable_B[1] + combiner.variable_C[1] * combiner.variable_D[1];
result[2] = combiner.variable_A[2] * combiner.variable_B[2] + combiner.variable_C[2] * combiner.variable_D[2];
}
void nv2a_renderer::combiner_compute_rgb_outputs(int stage_number)
{
int n = stage_number;
int m;
float biasrgb, scalergb;
if (combiner.stage[n].mapout_rgb_bias)
biasrgb = -0.5;
else
biasrgb = 0;
switch (combiner.stage[n].mapout_rgb_scale) {
case 0:
default:
scalergb = 1.0;
break;
case 1:
scalergb = 2.0;
break;
case 2:
scalergb = 4.0;
break;
case 3:
scalergb = 0.5;
break;
}
if (combiner.stage[n].mapout_rgbAB_dotproduct) {
m = 1;
combiner_function_AdotB(combiner.function_RGBop1);
}
else {
m = 0;
combiner_function_AB(combiner.function_RGBop1);
}
combiner.function_RGBop1[0] = std::max(std::min((combiner.function_RGBop1[0] + biasrgb) * scalergb, 1.0f), -1.0f);
combiner.function_RGBop1[1] = std::max(std::min((combiner.function_RGBop1[1] + biasrgb) * scalergb, 1.0f), -1.0f);
combiner.function_RGBop1[2] = std::max(std::min((combiner.function_RGBop1[2] + biasrgb) * scalergb, 1.0f), -1.0f);
if (combiner.stage[n].mapout_rgbCD_dotproduct) {
m = m | 1;
combiner_function_CdotD(combiner.function_RGBop2);
}
else
combiner_function_CD(combiner.function_RGBop2);
combiner.function_RGBop2[0] = std::max(std::min((combiner.function_RGBop2[0] + biasrgb) * scalergb, 1.0f), -1.0f);
combiner.function_RGBop2[1] = std::max(std::min((combiner.function_RGBop2[1] + biasrgb) * scalergb, 1.0f), -1.0f);
combiner.function_RGBop2[2] = std::max(std::min((combiner.function_RGBop2[2] + biasrgb) * scalergb, 1.0f), -1.0f);
if (m == 0) {
if (combiner.stage[n].mapout_rgb_muxsum)
combiner_function_ABmuxCD(combiner.function_RGBop3);
else
combiner_function_ABsumCD(combiner.function_RGBop3);
combiner.function_RGBop3[0] = std::max(std::min((combiner.function_RGBop3[0] + biasrgb) * scalergb, 1.0f), -1.0f);
combiner.function_RGBop3[1] = std::max(std::min((combiner.function_RGBop3[1] + biasrgb) * scalergb, 1.0f), -1.0f);
combiner.function_RGBop3[2] = std::max(std::min((combiner.function_RGBop3[2] + biasrgb) * scalergb, 1.0f), -1.0f);
}
}
void nv2a_renderer::combiner_compute_a_outputs(int stage_number)
{
int n = stage_number;
float biasa, scalea;
if (combiner.stage[n].mapout_a_bias)
biasa = -0.5;
else
biasa = 0;
switch (combiner.stage[n].mapout_a_scale) {
case 0:
default:
scalea = 1.0;
break;
case 1:
scalea = 2.0;
break;
case 2:
scalea = 4.0;
break;
case 3:
scalea = 0.5;
break;
}
combiner.function_Aop1 = combiner.variable_A[3] * combiner.variable_B[3];
combiner.function_Aop1 = std::max(std::min((combiner.function_Aop1 + biasa) * scalea, 1.0f), -1.0f);
combiner.function_Aop2 = combiner.variable_C[3] * combiner.variable_D[3];
combiner.function_Aop2 = std::max(std::min((combiner.function_Aop2 + biasa) * scalea, 1.0f), -1.0f);
if (combiner.stage[n].mapout_a_muxsum) {
if (combiner.register_spare0[3] >= 0.5f)
combiner.function_Aop3 = combiner.variable_A[3] * combiner.variable_B[3];
else
combiner.function_Aop3 = combiner.variable_C[3] * combiner.variable_D[3];
}
else
combiner.function_Aop3 = combiner.variable_A[3] * combiner.variable_B[3] + combiner.variable_C[3] * combiner.variable_D[3];
combiner.function_Aop3 = std::max(std::min((combiner.function_Aop3 + biasa) * scalea, 1.0f), -1.0f);
}
WRITE_LINE_MEMBER(nv2a_renderer::vblank_callback)
{
#ifdef LOG_NV2A
printf("vblank_callback\n\r");
#endif
if ((state == true) && (puller_waiting == 1)) {
puller_waiting = 0;
puller_timer_work(nullptr, 0);
}
if (state == true) {
pcrtc[0x100 / 4] |= 1;
pcrtc[0x808 / 4] |= 0x10000;
}
else {
pcrtc[0x100 / 4] &= ~1;
pcrtc[0x808 / 4] &= ~0x10000;
}
if (update_interrupts() == true)
irq_callback(1); // IRQ 3
else
irq_callback(0); // IRQ 3
}
bool nv2a_renderer::update_interrupts()
{
if (pcrtc[0x100 / 4] & pcrtc[0x140 / 4])
pmc[0x100 / 4] |= 0x1000000;
else
pmc[0x100 / 4] &= ~0x1000000;
if (pgraph[0x100 / 4] & pgraph[0x140 / 4])
pmc[0x100 / 4] |= 0x1000;
else
pmc[0x100 / 4] &= ~0x1000;
if (((pmc[0x100 / 4] & 0x7fffffff) && (pmc[0x140 / 4] & 1)) || ((pmc[0x100 / 4] & 0x80000000) && (pmc[0x140 / 4] & 2))) {
// send interrupt
return true;
}
else
return false;
}
uint32_t nv2a_renderer::screen_update_callback(screen_device &screen, bitmap_rgb32 &bitmap, const rectangle &cliprect)
{
if (displayedtarget != nullptr) {
bitmap_rgb32 bm(displayedtarget, 640, 480, 640);
uint32_t *dst = (uint32_t *)bitmap.raw_pixptr(0, 0);
//printf("updatescreen %08X\n\r",pcrtc[0x800/4]);
memcpy(dst, displayedtarget, bitmap.rowbytes()*bitmap.height());
}
return 0;
}
void nv2a_renderer::geforce_assign_object(address_space & space, uint32_t chanel, uint32_t subchannel, uint32_t address)
{
int handle, objclass;
handle = space.read_dword(address);
handle = geforce_object_offset(handle);
#ifdef LOG_NV2A
machine().logerror(" assign to subchannel %d object at %d", subchannel, handle);
#endif
channel[chanel][subchannel].object.objhandle = handle;
handle = ramin[handle / 4];
objclass = handle & 0xff;
#ifdef LOG_NV2A
machine().logerror(" class %03X\n", objclass);
#endif
channel[chanel][subchannel].object.objclass = objclass;
}
TIMER_CALLBACK_MEMBER(nv2a_renderer::puller_timer_work)
{
int chanel;
int method, count;
uint32_t *dmaput, *dmaget;
uint32_t cmd, cmdtype;
int countlen;
int ret;
address_space *space = puller_space;
uint32_t subch;
for (chanel = 0; chanel < 32; chanel++) {
dmaput = &channel[chanel][0].regs[0x40 / 4];
dmaget = &channel[chanel][0].regs[0x44 / 4];
while (*dmaget != *dmaput) {
cmd = space->read_dword(*dmaget);
*dmaget += 4;
cmdtype = geforce_commandkind(cmd);
switch (cmdtype)
{
case 6: // jump
#ifdef LOG_NV2A
machine().logerror("jump dmaget %08X", *dmaget);
#endif
*dmaget = cmd & 0xfffffffc;
#ifdef LOG_NV2A
machine().logerror(" -> %08X\n\r", *dmaget);
#endif
break;
case 0: // increasing method
method = (cmd >> 2) & 2047; // method*4 is address // if method >= 0x40 send it to assigned object
subch = (cmd >> 13) & 7;
count = (cmd >> 18) & 2047;
if ((method == 0) && (count == 1)) {
geforce_assign_object(*space, chanel, subch, *dmaget);
*dmaget += 4;
}
else {
#ifdef LOG_NV2A
machine().logerror(" subch. %d method %04x offset %04x count %d\n", subch, method, method * 4, count);
#endif
ret = 0;
while (count > 0) {
countlen = 1;
ret=geforce_exec_method(*space, chanel, subch, method, *dmaget, countlen);
count--;
method++;
*dmaget += 4;
if (ret != 0)
break;
}
if (ret != 0) {
puller_timer->enable(false);
puller_waiting = ret;
return;
}
}
break;
case 5: // non-increasing method
method = (cmd >> 2) & 2047;
subch = (cmd >> 13) & 7;
count = (cmd >> 18) & 2047;
if ((method == 0) && (count == 1)) {
geforce_assign_object(*space, chanel, subch, *dmaget);
*dmaget += 4;
}
else {
#ifdef LOG_NV2A
machine().logerror(" subch. %d method %04x offset %04x count %d\n", subch, method, method * 4, count);
#endif
while (count > 0) {
countlen = count;
ret=geforce_exec_method(*space, chanel, subch, method, *dmaget, countlen);
*dmaget += 4 * (count - countlen);
count = countlen;
}
}
break;
case 3: // long non-increasing method
method = (cmd >> 2) & 2047;
subch = (cmd >> 13) & 7;
count = space->read_dword(*dmaget);
*dmaget += 4;
if ((method == 0) && (count == 1)) {
geforce_assign_object(*space, chanel, subch, *dmaget);
*dmaget += 4;
}
else {
#ifdef LOG_NV2A
machine().logerror(" subch. %d method %04x offset %04x count %d\n", subch, method, method * 4, count);
#endif
while (count > 0) {
countlen = count;
ret=geforce_exec_method(*space, chanel, subch, method, *dmaget, countlen);
*dmaget += 4 * (count - countlen);
count = countlen;
}
}
break;
default:
machine().logerror(" unimplemented command %08X\n", cmd);
}
}
}
}
READ32_MEMBER(nv2a_renderer::geforce_r)
{
static int x, ret;
ret = 0;
if (offset == 0x1804f6) {
x = x ^ 0x08080808;
ret = x;
}
if ((offset >= 0x00100000 / 4) && (offset < 0x00101000 / 4)) {
//machine().logerror("NV_2A: read 100000[%06X] mask %08X value %08X\n",offset*4-0x00101000,mem_mask,ret);
if (offset == 0x100200 / 4)
return 3;
}
else if ((offset >= 0x00101000 / 4) && (offset < 0x00102000 / 4)) {
//machine().logerror("NV_2A: read STRAPS[%06X] mask %08X value %08X\n",offset*4-0x00101000,mem_mask,ret);
}
else if ((offset >= 0x00002000 / 4) && (offset < 0x00004000 / 4)) {
ret = pfifo[offset - 0x00002000 / 4];
// PFIFO.CACHE1.STATUS or PFIFO.RUNOUT_STATUS
if ((offset == 0x3214 / 4) || (offset == 0x2400 / 4))
ret = 0x10;
//machine().logerror("NV_2A: read PFIFO[%06X] value %08X\n",offset*4-0x00002000,ret);
}
else if ((offset >= 0x00700000 / 4) && (offset < 0x00800000 / 4)) {
ret = ramin[offset - 0x00700000 / 4];
//machine().logerror("NV_2A: read PRAMIN[%06X] value %08X\n",offset*4-0x00700000,ret);
}
else if ((offset >= 0x00400000 / 4) && (offset < 0x00402000 / 4)) {
ret = pgraph[offset - 0x00400000 / 4];
//machine().logerror("NV_2A: read PGRAPH[%06X] value %08X\n",offset*4-0x00400000,ret);
}
else if ((offset >= 0x00600000 / 4) && (offset < 0x00601000 / 4)) {
ret = pcrtc[offset - 0x00600000 / 4];
//machine().logerror("NV_2A: read PCRTC[%06X] value %08X\n",offset*4-0x00600000,ret);
}
else if ((offset >= 0x00000000 / 4) && (offset < 0x00001000 / 4)) {
ret = pmc[offset - 0x00000000 / 4];
//machine().logerror("NV_2A: read PMC[%06X] value %08X\n",offset*4-0x00000000,ret);
}
else if ((offset >= 0x00800000 / 4) && (offset < 0x00900000 / 4)) {
// 32 channels size 0x10000 each, 8 subchannels per channel size 0x2000 each
int chanel, subchannel, suboffset;
suboffset = offset - 0x00800000 / 4;
chanel = (suboffset >> (16 - 2)) & 31;
subchannel = (suboffset >> (13 - 2)) & 7;
suboffset = suboffset & 0x7ff;
if (suboffset < 0x80 / 4)
ret = channel[chanel][subchannel].regs[suboffset];
//machine().logerror("NV_2A: read channel[%02X,%d,%04X]=%08X\n",chanel,subchannel,suboffset*4,ret);
return ret;
}
//machine().logerror("NV_2A: read at %08X mask %08X value %08X\n",0xfd000000+offset*4,mem_mask,ret);
return ret;
}
WRITE32_MEMBER(nv2a_renderer::geforce_w)
{
uint32_t old;
bool update_int;
update_int = false;
if ((offset >= 0x00101000 / 4) && (offset < 0x00102000 / 4)) {
//machine().logerror("NV_2A: write STRAPS[%06X] mask %08X value %08X\n",offset*4-0x00101000,mem_mask,data);
}
else if ((offset >= 0x00002000 / 4) && (offset < 0x00004000 / 4)) {
int e = offset - 0x00002000 / 4;
if (e >= (sizeof(pfifo) / sizeof(uint32_t)))
return;
COMBINE_DATA(pfifo + e);
//machine().logerror("NV_2A: read PFIFO[%06X]=%08X\n",offset*4-0x00002000,data & mem_mask); // 2210 pfifo ramht & 1f0 << 12
}
else if ((offset >= 0x00700000 / 4) && (offset < 0x00800000 / 4)) {
int e = offset - 0x00700000 / 4;
if (e >= (sizeof(ramin) / sizeof(uint32_t)))
return;
COMBINE_DATA(ramin + e);
//machine().logerror("NV_2A: write PRAMIN[%06X]=%08X\n",offset*4-0x00700000,data & mem_mask);
}
else if ((offset >= 0x00400000 / 4) && (offset < 0x00402000 / 4)) {
int e = offset - 0x00400000 / 4;
if (e >= (sizeof(pgraph) / sizeof(uint32_t)))
return;
old = pgraph[e];
COMBINE_DATA(pgraph + e);
if (e == 0x100 / 4) {
pgraph[e] = old & ~data;
if (data & 1)
pgraph[0x108 / 4] = 0;
update_int = true;
}
if (e == 0x140 / 4)
update_int = true;
if (e == 0x720 / 4) {
if ((data & 1) && (puller_waiting == 2)) {
puller_waiting = 0;
puller_timer->enable();
puller_timer->adjust(attotime::zero);
}
}
if ((e >= 0x900 / 4) && (e < 0xa00 / 4))
pgraph[e] = 0;
//machine().logerror("NV_2A: write PGRAPH[%06X]=%08X\n",offset*4-0x00400000,data & mem_mask);
}
else if ((offset >= 0x00600000 / 4) && (offset < 0x00601000 / 4)) {
int e = offset - 0x00600000 / 4;
if (e >= (sizeof(pcrtc) / sizeof(uint32_t)))
return;
old = pcrtc[e];
COMBINE_DATA(pcrtc + e);
if (e == 0x100 / 4) {
pcrtc[e] = old & ~data;
update_int = true;
}
if (e == 0x140 / 4)
update_int = true;
if (e == 0x800 / 4) {
displayedtarget = (uint32_t *)direct_access_ptr(pcrtc[e]);
#ifdef LOG_NV2A
printf("crtc buffer %08X\n\r", data);
#endif
}
//machine().logerror("NV_2A: write PCRTC[%06X]=%08X\n",offset*4-0x00600000,data & mem_mask);
}
else if ((offset >= 0x00000000 / 4) && (offset < 0x00001000 / 4)) {
int e = offset - 0x00000000 / 4;
if (e >= (sizeof(pmc) / sizeof(uint32_t)))
return;
COMBINE_DATA(pmc + e);
//machine().logerror("NV_2A: write PMC[%06X]=%08X\n",offset*4-0x00000000,data & mem_mask);
}
else if ((offset >= 0x00800000 / 4) && (offset < 0x00900000 / 4)) {
// 32 channels size 0x10000 each, 8 subchannels per channel size 0x2000 each
int chanel, subchannel, suboffset;
//int method, count, handle, objclass;
suboffset = offset - 0x00800000 / 4;
chanel = (suboffset >> (16 - 2)) & 31;
subchannel = (suboffset >> (13 - 2)) & 7;
suboffset = suboffset & 0x7ff;
//machine().logerror("NV_2A: write channel[%02X,%d,%04X]=%08X\n",chanel,subchannel,suboffset*4,data & mem_mask);
COMBINE_DATA(&channel[chanel][subchannel].regs[suboffset]);
if (suboffset >= 0x80 / 4)
return;
if ((suboffset == 0x40 / 4) || (suboffset == 0x44 / 4)) { // DMA_PUT or DMA_GET
uint32_t *dmaput, *dmaget;
dmaput = &channel[chanel][0].regs[0x40 / 4];
dmaget = &channel[chanel][0].regs[0x44 / 4];
//printf("dmaget %08X dmaput %08X\n\r",*dmaget,*dmaput);
if (((*dmaput == 0x048cf000) && (*dmaget == 0x07f4d000)) || // only for outr2
((*dmaput == 0x045cd000) && (*dmaget == 0x07f4d000)) || // only for scg06nt
((*dmaput == 0x0494c000) && (*dmaget == 0x07f4d000)) || // only for wangmid
((*dmaput == 0x05acd000) && (*dmaget == 0x07f4d000)) || // only for ghostsqu
((*dmaput == 0x0574d000) && (*dmaget == 0x07f4d000)) || // only for mj2c
((*dmaput == 0x07ca3000) && (*dmaget == 0x07f4d000)) || // only for hotd3
((*dmaput == 0x063cd000) && (*dmaget == 0x07f4d000)) || // only for vcop3
((*dmaput == 0x07f4f000) && (*dmaget == 0x07f4d000)) || // only for ccfboxa
((*dmaput == 0x07dca000) && (*dmaget == 0x07f4d000))) // only for crtaxihr
{
*dmaget = *dmaput;
puller_waiting = 0;
puller_timer->enable(false);
return;
}
if (*dmaget != *dmaput) {
if (puller_waiting == 0) {
puller_space = &space;
puller_timer->enable();
puller_timer->adjust(attotime::zero);
}
}
}
}
//else
// machine().logerror("NV_2A: write at %08X mask %08X value %08X\n",0xfd000000+offset*4,mem_mask,data);
if (update_int == true) {
if (update_interrupts() == true)
irq_callback(1); // IRQ 3
else
irq_callback(0); // IRQ 3
}
}
void nv2a_renderer::savestate_items()
{
}
void nv2a_renderer::start(address_space *cpu_space)
{
basemempointer = (uint8_t *)cpu_space->get_read_ptr(0);
topmempointer = basemempointer + 512 * 1024 * 1024 - 1;
puller_timer = machine().scheduler().timer_alloc(timer_expired_delegate(FUNC(nv2a_renderer::puller_timer_work), this), (void *)"NV2A Puller Timer");
puller_timer->enable(false);
}