nes accuracy fixes
- pass more apu_test 4-jitter and 6-irq_flag_timing (necessary for timing on other tests) - pass all cpu interrupt tests - pass all sprite hit tests
This commit is contained in:
parent
f694959da9
commit
d3321f552f
|
@ -1,5 +1,4 @@
|
|||
//http://nesdev.parodius.com/6502_cpu.txt
|
||||
//TODO - correct brk/irq/nmi interrupting and prioritization
|
||||
//TODO - rename unofficial NOPs as DOPs? (see immediate instr tests)
|
||||
using System;
|
||||
|
||||
|
@ -9,7 +8,7 @@ namespace BizHawk.Emulation.CPUs.M6502
|
|||
{
|
||||
static Uop[][] Microcode = new Uop[][]{
|
||||
//0x00
|
||||
/*BRK [implied]*/ new Uop[] { Uop.Fetch2, Uop.PushPCH, Uop.PushPCL, Uop.PushP_BRK, Uop.FetchPCLVector, Uop.FetchPCHVector, Uop.End },
|
||||
/*BRK [implied]*/ new Uop[] { Uop.Fetch2, Uop.PushPCH, Uop.PushPCL, Uop.PushP_BRK, Uop.FetchPCLVector, Uop.FetchPCHVector, Uop.End_SuppressInterrupt },
|
||||
/*ORA (addr,X) [indexed indirect READ]*/ new Uop[] { Uop.Fetch2, Uop.IdxInd_Stage3, Uop.IdxInd_Stage4, Uop.IdxInd_Stage5, Uop.IdxInd_Stage6_READ_ORA, Uop.End },
|
||||
/*JAM*/ new Uop[] { Uop.End },
|
||||
/*SLO* (addr,X) [indexed indirect RMW] [unofficial]*/ new Uop[] { Uop.Fetch2, Uop.End },
|
||||
|
@ -282,14 +281,16 @@ namespace BizHawk.Emulation.CPUs.M6502
|
|||
/*ISB* addr,X [absolute indexed RMW X] [unofficial]*/ new Uop[] { Uop.Fetch2, Uop.AbsIdx_Stage3_X, Uop.AbsIdx_Stage4, Uop.AbsIdx_RMW_Stage5, Uop.AbsIdx_RMW_Stage6_Unofficial, Uop.AbsIdx_RMW_Stage7, Uop.End },
|
||||
//0x100
|
||||
/*VOP_Fetch1*/ new Uop[] { Uop.Fetch1 },
|
||||
/*VOP_RelativeStuff*/ new Uop[] { Uop.RelBranch_Stage3, Uop.End },
|
||||
/*VOP_RelativeStuff*/ new Uop[] { Uop.RelBranch_Stage3, Uop.End_BranchSpecial },
|
||||
/*VOP_RelativeStuff2*/ new Uop[] { Uop.RelBranch_Stage4, Uop.End },
|
||||
/*VOP_RelativeStuff2*/ new Uop[] { Uop.End_SuppressInterrupt },
|
||||
//i assume these are dummy fetches.... maybe theyre just nops? supposedly these take 7 cycles so thats the only way i can make sense of it
|
||||
//one of them might be the next instruction's fetch, and whatever fetch follows it.
|
||||
//the interrupt would then take place if necessary, using a cached PC. but im not so sure about that.
|
||||
/*VOP_NMI*/ new Uop[] { Uop.FetchDummy, Uop.FetchDummy, Uop.PushPCH, Uop.PushPCL, Uop.PushP_NMI, Uop.FetchPCLVector, Uop.FetchPCHVector, Uop.End },
|
||||
/*VOP_IRQ*/ new Uop[] { Uop.FetchDummy, Uop.FetchDummy, Uop.PushPCH, Uop.PushPCL, Uop.PushP_IRQ, Uop.FetchPCLVector, Uop.FetchPCHVector, Uop.End },
|
||||
/*VOP_RESET*/ new Uop[] { Uop.FetchDummy, Uop.FetchDummy, Uop.PushDummy, Uop.PushDummy, Uop.PushP_Reset, Uop.FetchPCLVector, Uop.FetchPCHVector, Uop.End },
|
||||
/*VOP_NMI*/ new Uop[] { Uop.FetchDummy, Uop.FetchDummy, Uop.PushPCH, Uop.PushPCL, Uop.PushP_NMI, Uop.FetchPCLVector, Uop.FetchPCHVector, Uop.End_SuppressInterrupt },
|
||||
/*VOP_IRQ*/ new Uop[] { Uop.FetchDummy, Uop.FetchDummy, Uop.PushPCH, Uop.PushPCL, Uop.PushP_IRQ, Uop.FetchPCLVector, Uop.FetchPCHVector, Uop.End_SuppressInterrupt },
|
||||
/*VOP_RESET*/ new Uop[] { Uop.FetchDummy, Uop.FetchDummy, Uop.PushDummy, Uop.PushDummy, Uop.PushP_Reset, Uop.FetchPCLVector, Uop.FetchPCHVector, Uop.End_SuppressInterrupt },
|
||||
/*VOP_Fetch1_NoInterrupt*/ new Uop[] { Uop.Fetch1_Real },
|
||||
};
|
||||
|
||||
enum Uop
|
||||
|
@ -297,7 +298,7 @@ namespace BizHawk.Emulation.CPUs.M6502
|
|||
//sometimes i used this as a marker for unsupported instructions, but it is very inconsistent
|
||||
Unsupported,
|
||||
|
||||
Fetch1, Fetch2, Fetch3,
|
||||
Fetch1, Fetch1_Real, Fetch2, Fetch3,
|
||||
//used by instructions with no second opcode byte (6502 fetches a byte anyway but won't increment PC for these)
|
||||
FetchDummy,
|
||||
|
||||
|
@ -385,50 +386,45 @@ namespace BizHawk.Emulation.CPUs.M6502
|
|||
|
||||
End,
|
||||
End_ISpecial, //same as end, but preserves the iflag set by the instruction
|
||||
End_BranchSpecial,
|
||||
End_SuppressInterrupt,
|
||||
}
|
||||
|
||||
const int VOP_Fetch1 = 256;
|
||||
const int VOP_RelativeStuff = 257;
|
||||
const int VOP_RelativeStuff2 = 258;
|
||||
const int VOP_NMI = 259;
|
||||
const int VOP_IRQ = 260;
|
||||
const int VOP_RESET = 261;
|
||||
const int VOP_RelativeStuff3 = 259;
|
||||
const int VOP_NMI = 260;
|
||||
const int VOP_IRQ = 261;
|
||||
const int VOP_RESET = 262;
|
||||
const int VOP_Fetch1_NoInterrupt = 263;
|
||||
|
||||
//opcode bytes.. theoretically redundant with the temp variables? who knows.
|
||||
int opcode;
|
||||
byte opcode2, opcode3; //opcode bytes.. theoretically redundant with the temp variables? who knows.
|
||||
byte opcode2, opcode3;
|
||||
|
||||
int ea, alu_temp; //cpu internal temp variables
|
||||
int mi; //microcode index
|
||||
//bool branch_taken; //only needed for the timing debug
|
||||
bool iflag_pending; //iflag must be stored after it is checked in some cases (CLI and SEI).
|
||||
|
||||
//tracks whether an interrupt condition has popped up recently.
|
||||
//not sure if this is real or not but it helps with the branch_irq_hack
|
||||
bool interrupt_pending;
|
||||
bool branch_irq_hack; //see Uop.RelBranch_Stage3 for more details
|
||||
|
||||
bool Interrupted
|
||||
{
|
||||
get
|
||||
{
|
||||
return NMI || (IRQ && !FlagI);
|
||||
}
|
||||
}
|
||||
|
||||
void FetchDummy()
|
||||
{
|
||||
DummyReadMemory(PC);
|
||||
}
|
||||
|
||||
////timing debug
|
||||
//int ctr = 0;
|
||||
//int realOpcode = 0;
|
||||
//public static byte[] CycTable = new byte[]
|
||||
//{
|
||||
///*0x00*/ 7,6,2,8,3,3,5,5,3,2,2,2,4,4,6,6,
|
||||
///*0x10*/ 2,5,2,8,4,4,6,6,2,4,2,7,4,4,7,7,
|
||||
///*0x20*/ 6,6,2,8,3,3,5,5,4,2,2,2,4,4,6,6,
|
||||
///*0x30*/ 2,5,2,8,4,4,6,6,2,4,2,7,4,4,7,7,
|
||||
///*0x40*/ 6,6,2,8,3,3,5,5,3,2,2,2,3,4,6,6,
|
||||
///*0x50*/ 2,5,2,8,4,4,6,6,2,4,2,7,4,4,7,7,
|
||||
///*0x60*/ 6,6,2,8,3,3,5,5,4,2,2,2,5,4,6,6,
|
||||
///*0x70*/ 2,5,2,8,4,4,6,6,2,4,2,7,4,4,7,7,
|
||||
///*0x80*/ 2,6,2,6,3,3,3,3,2,2,2,2,4,4,4,4,
|
||||
///*0x90*/ 2,6,2,6,4,4,4,4,2,5,2,5,5,5,5,5,
|
||||
///*0xA0*/ 2,6,2,6,3,3,3,3,2,2,2,2,4,4,4,4,
|
||||
///*0xB0*/ 2,5,2,5,4,4,4,4,2,4,2,4,4,4,4,4,
|
||||
///*0xC0*/ 2,6,2,8,3,3,5,5,2,2,2,2,4,4,6,6,
|
||||
///*0xD0*/ 2,5,2,8,4,4,6,6,2,4,2,7,4,4,7,7,
|
||||
///*0xE0*/ 2,6,3,8,3,3,5,5,2,2,2,2,4,4,6,6,
|
||||
///*0xF0*/ 2,5,2,8,4,4,6,6,2,4,2,7,4,4,7,7,
|
||||
//};
|
||||
|
||||
public void Execute(int cycles)
|
||||
{
|
||||
for (int i = 0; i < cycles; i++)
|
||||
|
@ -445,6 +441,8 @@ namespace BizHawk.Emulation.CPUs.M6502
|
|||
|
||||
TotalExecutedCycles++;
|
||||
|
||||
interrupt_pending |= Interrupted;
|
||||
|
||||
RETRY:
|
||||
Uop uop = Microcode[opcode][mi];
|
||||
switch (uop)
|
||||
|
@ -454,44 +452,34 @@ namespace BizHawk.Emulation.CPUs.M6502
|
|||
{
|
||||
bool my_iflag = FlagI;
|
||||
FlagI = iflag_pending;
|
||||
if (NMI)
|
||||
if (!branch_irq_hack)
|
||||
{
|
||||
ea = NMIVector;
|
||||
opcode = VOP_NMI;
|
||||
NMI = false;
|
||||
mi = 0;
|
||||
goto RETRY;
|
||||
interrupt_pending = false;
|
||||
if (NMI)
|
||||
{
|
||||
ea = NMIVector;
|
||||
opcode = VOP_NMI;
|
||||
NMI = false;
|
||||
mi = 0;
|
||||
goto RETRY;
|
||||
}
|
||||
else if (IRQ && !my_iflag)
|
||||
{
|
||||
ea = IRQVector;
|
||||
opcode = VOP_IRQ;
|
||||
mi = 0;
|
||||
goto RETRY;
|
||||
}
|
||||
}
|
||||
else if (IRQ && !my_iflag)
|
||||
{
|
||||
ea = IRQVector;
|
||||
opcode = VOP_IRQ;
|
||||
mi = 0;
|
||||
goto RETRY;
|
||||
}
|
||||
#if TIMINGDEBUG
|
||||
if (debug)
|
||||
{
|
||||
int ideal = CycTable[realOpcode] + (branch_taken ? 1 : 0);
|
||||
int actual = TotalExecutedCycles - ctr;
|
||||
Console.Write(" | ideal={0}", ideal);
|
||||
Console.Write(" actual={0}", actual);
|
||||
if (actual != ideal) Console.WriteLine(" !!!"); else Console.WriteLine();
|
||||
Console.Write(State());
|
||||
}
|
||||
branch_taken = false;
|
||||
opcode = ReadMemory(PC++);
|
||||
realOpcode = opcode;
|
||||
mi = -1;
|
||||
ctr = TotalExecutedCycles;
|
||||
break;
|
||||
#else
|
||||
if(debug) Console.WriteLine(State());
|
||||
opcode = ReadMemory(PC++);
|
||||
mi = -1;
|
||||
break;
|
||||
#endif
|
||||
goto case Uop.Fetch1_Real;
|
||||
}
|
||||
|
||||
case Uop.Fetch1_Real:
|
||||
if (debug) Console.WriteLine(State());
|
||||
branch_irq_hack = false;
|
||||
opcode = ReadMemory(PC++);
|
||||
mi = -1;
|
||||
break;
|
||||
|
||||
case Uop.Fetch2: opcode2 = ReadMemory(PC++); break;
|
||||
case Uop.Fetch3: opcode3 = ReadMemory(PC++); break;
|
||||
|
@ -526,6 +514,16 @@ namespace BizHawk.Emulation.CPUs.M6502
|
|||
S--;
|
||||
break;
|
||||
case Uop.FetchPCLVector:
|
||||
if (ea == BRKVector && FlagB && NMI)
|
||||
{
|
||||
NMI = false;
|
||||
ea = NMIVector;
|
||||
}
|
||||
if(ea == IRQVector && !FlagB && NMI)
|
||||
{
|
||||
NMI = false;
|
||||
ea = NMIVector;
|
||||
}
|
||||
alu_temp = ReadMemory((ushort)ea);
|
||||
break;
|
||||
case Uop.FetchPCHVector:
|
||||
|
@ -652,18 +650,26 @@ namespace BizHawk.Emulation.CPUs.M6502
|
|||
opcode = VOP_RelativeStuff;
|
||||
mi = -1;
|
||||
}
|
||||
|
||||
break;
|
||||
case Uop.RelBranch_Stage3:
|
||||
FetchDummy();
|
||||
alu_temp = (byte)PC + (int)(sbyte)opcode2;
|
||||
PC &= 0xFF00;
|
||||
PC |= (ushort)((alu_temp&0xFF));
|
||||
if(alu_temp.Bit(8))
|
||||
if (alu_temp.Bit(8))
|
||||
{
|
||||
//we need to carry the add, and then we'll be ready to fetch the next instruction
|
||||
opcode = VOP_RelativeStuff2;
|
||||
mi = -1;
|
||||
}
|
||||
else
|
||||
{
|
||||
//to pass cpu_interrupts_v2/5-branch_delays_irq we need to handle a quirk here
|
||||
//if we decide to interrupt in the next cycle, this condition will cause it to get deferred by one instruction
|
||||
if(!interrupt_pending)
|
||||
branch_irq_hack = true;
|
||||
}
|
||||
break;
|
||||
case Uop.RelBranch_Stage4:
|
||||
FetchDummy();
|
||||
|
@ -678,7 +684,7 @@ namespace BizHawk.Emulation.CPUs.M6502
|
|||
case Uop.JSR: PC = (ushort)((ReadMemory((ushort)(PC)) << 8) + opcode2); break;
|
||||
case Uop.PullP:
|
||||
P = ReadMemory((ushort)(S++ + 0x100));
|
||||
FlagT = true;
|
||||
FlagT = true; //force T always to remain true
|
||||
break;
|
||||
case Uop.PullPCL:
|
||||
PC &= 0xFF00;
|
||||
|
@ -968,7 +974,7 @@ namespace BizHawk.Emulation.CPUs.M6502
|
|||
P = ReadMemory((ushort)(S + 0x100));
|
||||
iflag_pending = FlagI;
|
||||
FlagI = my_iflag;
|
||||
FlagT = true; //why?
|
||||
FlagT = true; //force T always to remain true
|
||||
break;
|
||||
}
|
||||
|
||||
|
@ -1239,11 +1245,18 @@ namespace BizHawk.Emulation.CPUs.M6502
|
|||
mi = 0;
|
||||
goto RETRY;
|
||||
|
||||
case Uop.End_SuppressInterrupt:
|
||||
opcode = VOP_Fetch1_NoInterrupt;
|
||||
mi = 0;
|
||||
goto RETRY;
|
||||
|
||||
case Uop.End:
|
||||
opcode = VOP_Fetch1;
|
||||
mi = 0;
|
||||
iflag_pending = FlagI;
|
||||
goto RETRY;
|
||||
case Uop.End_BranchSpecial:
|
||||
goto case Uop.End;
|
||||
}
|
||||
|
||||
mi++;
|
||||
|
|
|
@ -96,6 +96,8 @@ namespace BizHawk.Emulation.CPUs.M6502
|
|||
ser.Sync("alu_temp", ref alu_temp);
|
||||
ser.Sync("mi", ref mi);
|
||||
ser.Sync("iflag_pending", ref iflag_pending);
|
||||
ser.Sync("interrupt_pending", ref interrupt_pending);
|
||||
ser.Sync("branch_irq_hack", ref branch_irq_hack);
|
||||
ser.EndSection();
|
||||
}
|
||||
|
||||
|
|
|
@ -47,11 +47,11 @@ namespace BizHawk.Emulation.Consoles.Nintendo
|
|||
//4, 7, 14, 30, 60, 88, 118, 148, 188, 236, 354, 472, 708, 944, 1890, 3778 //PAL
|
||||
};
|
||||
|
||||
|
||||
|
||||
class PulseUnit
|
||||
{
|
||||
public PulseUnit(int unit) { this.unit = unit; }
|
||||
public int unit;
|
||||
public int unit;
|
||||
|
||||
//reg0
|
||||
int duty_cnt, env_loop, env_constant, env_cnt_value;
|
||||
|
@ -87,10 +87,11 @@ namespace BizHawk.Emulation.Consoles.Nintendo
|
|||
|
||||
public bool IsLenCntNonZero() { return len_cnt > 0; }
|
||||
|
||||
|
||||
public void WriteReg(int addr, byte val)
|
||||
{
|
||||
//Console.WriteLine("write pulse {0:X} {1:X}", addr, val);
|
||||
switch(addr)
|
||||
switch (addr)
|
||||
{
|
||||
case 0:
|
||||
env_cnt_value = val & 0xF;
|
||||
|
@ -120,7 +121,7 @@ namespace BizHawk.Emulation.Consoles.Nintendo
|
|||
|
||||
//allow the lenctr_en to kill the len_cnt
|
||||
set_lenctr_en(lenctr_en);
|
||||
|
||||
|
||||
//serves as a useful note-on diagnostic
|
||||
//if(unit==1) Console.WriteLine("{0} timer_reload_value: {1}", unit, timer_reload_value);
|
||||
break;
|
||||
|
@ -163,7 +164,7 @@ namespace BizHawk.Emulation.Consoles.Nintendo
|
|||
if (swp_divider_counter == 0)
|
||||
{
|
||||
swp_divider_counter = sweep_divider_cnt + 1;
|
||||
|
||||
|
||||
//divider was clocked: process sweep pitch bend
|
||||
if (sweep_shiftcount != 0 && !swp_silence)
|
||||
{
|
||||
|
@ -180,7 +181,7 @@ namespace BizHawk.Emulation.Consoles.Nintendo
|
|||
sweep_reload = false;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//env_loopdoubles as "halt length counter"
|
||||
if (env_loop == 0 && len_cnt > 0)
|
||||
len_cnt--;
|
||||
|
@ -196,7 +197,7 @@ namespace BizHawk.Emulation.Consoles.Nintendo
|
|||
}
|
||||
else
|
||||
{
|
||||
if(env_divider != 0) env_divider--;
|
||||
if (env_divider != 0) env_divider--;
|
||||
if (env_divider == 0)
|
||||
{
|
||||
env_divider = (env_cnt_value + 1);
|
||||
|
@ -219,7 +220,7 @@ namespace BizHawk.Emulation.Consoles.Nintendo
|
|||
else env_output = env_counter;
|
||||
|
||||
if (timer_counter > 0) timer_counter--;
|
||||
if (timer_counter == 0 && timer_raw_reload_value!=0)
|
||||
if (timer_counter == 0 && timer_raw_reload_value != 0)
|
||||
{
|
||||
duty_step = (duty_step + 1) & 7;
|
||||
duty_value = PULSE_DUTY[duty_cnt, duty_step] == 1;
|
||||
|
@ -233,8 +234,8 @@ namespace BizHawk.Emulation.Consoles.Nintendo
|
|||
if (swp_silence)
|
||||
sample = 0;
|
||||
|
||||
if (len_cnt==0) //length counter is 0
|
||||
sample = 0; //silenced
|
||||
if (len_cnt == 0) //length counter is 0
|
||||
sample = 0; //silenced
|
||||
}
|
||||
else
|
||||
sample = 0; //duty cycle is 0, silenced.
|
||||
|
@ -297,13 +298,13 @@ namespace BizHawk.Emulation.Consoles.Nintendo
|
|||
case 0:
|
||||
env_cnt_value = val & 0xF;
|
||||
env_constant = (val >> 4) & 1;
|
||||
env_loop = (val>>5)&1;
|
||||
env_loop = (val >> 5) & 1;
|
||||
break;
|
||||
case 1:
|
||||
break;
|
||||
case 2:
|
||||
period_cnt = NOISE_TABLE[val & 0xF];
|
||||
mode_cnt = (val>>7)&1;
|
||||
mode_cnt = (val >> 7) & 1;
|
||||
//Console.WriteLine("noise period: {0}, vol: {1}", (val & 0xF), env_cnt_value);
|
||||
break;
|
||||
case 3:
|
||||
|
@ -350,7 +351,7 @@ namespace BizHawk.Emulation.Consoles.Nintendo
|
|||
}
|
||||
public void clock_length_and_sweep()
|
||||
{
|
||||
|
||||
|
||||
if (len_cnt > 0 && env_loop == 0)
|
||||
len_cnt--;
|
||||
}
|
||||
|
@ -373,10 +374,10 @@ namespace BizHawk.Emulation.Consoles.Nintendo
|
|||
shift_register >>= 1;
|
||||
shift_register &= ~(1 << 14);
|
||||
shift_register |= (feedback << 14);
|
||||
noise_bit = (shift_register & 1)!=0;
|
||||
noise_bit = (shift_register & 1) != 0;
|
||||
}
|
||||
|
||||
if (noise_bit || len_cnt==0) sample = 0;
|
||||
if (noise_bit || len_cnt == 0) sample = 0;
|
||||
else
|
||||
sample = env_output;
|
||||
}
|
||||
|
@ -463,21 +464,21 @@ namespace BizHawk.Emulation.Consoles.Nintendo
|
|||
//is clocked in frame counter.
|
||||
if (en)
|
||||
{
|
||||
if(timer>0) timer--;
|
||||
if (timer > 0) timer--;
|
||||
if (timer == 0)
|
||||
{
|
||||
seq = (seq + 1) & 0x1F;
|
||||
timer = timer_cnt_reload;
|
||||
}
|
||||
//if(CFG_DECLICK)
|
||||
//sample = TRIANGLE_TABLE[(seq+8)&0x1F];
|
||||
//sample = TRIANGLE_TABLE[(seq+8)&0x1F];
|
||||
//else
|
||||
sample = TRIANGLE_TABLE[seq];
|
||||
sample = TRIANGLE_TABLE[seq];
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
public void clock_length_and_sweep()
|
||||
{
|
||||
//env_loopdoubles as "halt length counter"
|
||||
|
@ -487,7 +488,7 @@ namespace BizHawk.Emulation.Consoles.Nintendo
|
|||
|
||||
public void clock_linear_counter()
|
||||
{
|
||||
// Console.WriteLine("linear_counter: {0}", linear_counter);
|
||||
// Console.WriteLine("linear_counter: {0}", linear_counter);
|
||||
if (halt_flag == 1)
|
||||
{
|
||||
linear_counter = linear_counter_reload;
|
||||
|
@ -543,7 +544,7 @@ namespace BizHawk.Emulation.Consoles.Nintendo
|
|||
ser.Sync("irq_enabled", ref irq_enabled);
|
||||
ser.Sync("loop_flag", ref loop_flag);
|
||||
ser.Sync("timer_reload", ref timer_reload);
|
||||
|
||||
|
||||
ser.Sync("timer", ref timer);
|
||||
ser.Sync("user_address", ref user_address);
|
||||
ser.Sync("user_length", ref user_length);
|
||||
|
@ -597,7 +598,7 @@ namespace BizHawk.Emulation.Consoles.Nintendo
|
|||
//apu.nes.LogLine("dmc out sample: {0}", out_deltacounter);
|
||||
}
|
||||
|
||||
if (out_bits_remaining==0)
|
||||
if (out_bits_remaining == 0)
|
||||
{
|
||||
out_bits_remaining = 7;
|
||||
if (sample_length > 0)
|
||||
|
@ -619,7 +620,7 @@ namespace BizHawk.Emulation.Consoles.Nintendo
|
|||
|
||||
public void set_lenctr_en(bool en)
|
||||
{
|
||||
if (!en)
|
||||
if (!en)
|
||||
//disable playback
|
||||
sample_length = 0;
|
||||
else
|
||||
|
@ -688,12 +689,20 @@ namespace BizHawk.Emulation.Consoles.Nintendo
|
|||
|
||||
public void SyncState(Serializer ser)
|
||||
{
|
||||
ser.Sync("irq_pending", ref irq_pending);
|
||||
ser.Sync("dmc_irq", ref dmc_irq);
|
||||
ser.Sync("pending_reg", ref pending_reg);
|
||||
ser.Sync("pending_val", ref pending_val);
|
||||
|
||||
ser.Sync("sequencer_counter", ref sequencer_counter);
|
||||
ser.Sync("sequencer_step", ref sequencer_step);
|
||||
ser.Sync("sequencer_mode", ref sequencer_mode);
|
||||
ser.Sync("sequencer_irq_inhibit", ref sequencer_irq_inhibit);
|
||||
ser.Sync("sequencer_irq_inhibit;", ref sequencer_irq_inhibit);
|
||||
ser.Sync("sequencer_irq", ref sequencer_irq);
|
||||
ser.Sync("dmc_irq", ref dmc_irq);
|
||||
ser.Sync("sequence_reset_pending", ref sequence_reset_pending);
|
||||
ser.Sync("sequencer_irq_clear_pending", ref sequencer_irq_clear_pending);
|
||||
ser.Sync("sequencer_irq_assert", ref sequencer_irq_assert);
|
||||
|
||||
pulse[0].SyncState(ser);
|
||||
pulse[1].SyncState(ser);
|
||||
triangle.SyncState(ser);
|
||||
|
@ -705,115 +714,153 @@ namespace BizHawk.Emulation.Consoles.Nintendo
|
|||
TriangleUnit triangle = new TriangleUnit();
|
||||
NoiseUnit noise = new NoiseUnit();
|
||||
DMCUnit dmc;
|
||||
bool sequencer_irq;
|
||||
public bool dmc_irq;
|
||||
|
||||
bool irq_pending;
|
||||
bool dmc_irq;
|
||||
int pending_reg = -1;
|
||||
byte pending_val = 0;
|
||||
|
||||
int sequencer_counter, sequencer_step, sequencer_mode, sequencer_irq_inhibit;
|
||||
bool sequencer_irq, sequence_reset_pending, sequencer_irq_clear_pending, sequencer_irq_assert;
|
||||
|
||||
public void RunDMCFetch()
|
||||
{
|
||||
dmc.Fetch();
|
||||
}
|
||||
|
||||
int sequencer_counter, sequencer_step, sequencer_mode, sequencer_irq_inhibit;
|
||||
void sequencer_reset()
|
||||
{
|
||||
sequencer_counter = 0;
|
||||
|
||||
if (sequencer_mode == 1)
|
||||
{
|
||||
sequencer_step = 5;
|
||||
sequencer_check();
|
||||
sequencer_step = 0;
|
||||
QuarterFrame();
|
||||
HalfFrame();
|
||||
}
|
||||
else
|
||||
sequencer_step = 1;
|
||||
sequencer_step = 0;
|
||||
}
|
||||
|
||||
//21477272 master clock
|
||||
//1789772 cpu clock (master / 12)
|
||||
//240 apu clock (master / 89490) = (cpu / 7457)
|
||||
//these figures are not valid for PAL. they must be recalculated with nintendulator's values above
|
||||
//these values (the NTSC at least) are derived from nintendulator. they are all 2 higher than the specifications, due to some shortcoming in the emulation
|
||||
//this is probably a hint that we're doing something a little wrong but making up for it with curcuitous chaos in other ways
|
||||
static int[][] sequencer_lut = new int[][]{
|
||||
new int[]{7458,14914,22372,29830},
|
||||
new int[]{7458,14914,22372,29830,37282}
|
||||
};
|
||||
|
||||
void sequencer_tick()
|
||||
{
|
||||
sequencer_counter++;
|
||||
//this figure is not valid for PAL. it must be recalculated
|
||||
if (sequencer_counter != 7457) return;
|
||||
sequencer_counter = 0;
|
||||
sequencer_step++;
|
||||
if (sequence_reset_pending)
|
||||
{
|
||||
sequencer_reset();
|
||||
sequence_reset_pending = false;
|
||||
}
|
||||
if (sequencer_lut[sequencer_mode][sequencer_step] != sequencer_counter)
|
||||
return;
|
||||
sequencer_check();
|
||||
}
|
||||
|
||||
public void SyncIRQ()
|
||||
{
|
||||
nes.irq_apu = sequencer_irq | dmc_irq;
|
||||
//if (nes.irq_apu) Console.WriteLine("apu irq");
|
||||
nes.sync_irq();
|
||||
irq_pending = sequencer_irq | dmc_irq;
|
||||
}
|
||||
|
||||
void sequencer_check()
|
||||
{
|
||||
//Console.WriteLine("sequencer mode {0} step {1}", sequencer_mode, sequencer_step);
|
||||
bool quarter, half, reset;
|
||||
switch (sequencer_mode)
|
||||
{
|
||||
case 0: //4-step
|
||||
pulse[0].clock_env();
|
||||
pulse[1].clock_env();
|
||||
triangle.clock_linear_counter();
|
||||
noise.clock_env();
|
||||
if (sequencer_step == 2 || sequencer_step == 4)
|
||||
quarter = true;
|
||||
half = (sequencer_step == 1 || sequencer_step == 3);
|
||||
reset = sequencer_step == 3;
|
||||
if (reset && sequencer_irq_inhibit == 0)
|
||||
{
|
||||
pulse[0].clock_length_and_sweep();
|
||||
pulse[1].clock_length_and_sweep();
|
||||
triangle.clock_length_and_sweep();
|
||||
noise.clock_length_and_sweep();
|
||||
}
|
||||
if (sequencer_step == 4)
|
||||
{
|
||||
if (sequencer_irq_inhibit == 0)
|
||||
{
|
||||
sequencer_irq = true;
|
||||
SyncIRQ();
|
||||
}
|
||||
sequencer_step = 1;
|
||||
//Console.WriteLine("{0} {1,5} set irq_assert", nes.Frame, sequencer_counter);
|
||||
sequencer_irq_assert = true;
|
||||
}
|
||||
break;
|
||||
|
||||
case 1: //5-step
|
||||
if (sequencer_step != 4)
|
||||
{
|
||||
pulse[0].clock_env();
|
||||
pulse[1].clock_env();
|
||||
triangle.clock_linear_counter();
|
||||
noise.clock_env();
|
||||
}
|
||||
if (sequencer_step == 2 || sequencer_step == 5)
|
||||
{
|
||||
pulse[0].clock_length_and_sweep();
|
||||
pulse[1].clock_length_and_sweep();
|
||||
triangle.clock_length_and_sweep();
|
||||
noise.clock_length_and_sweep();
|
||||
}
|
||||
if (sequencer_step == 5)
|
||||
sequencer_step = 1;
|
||||
quarter = sequencer_step != 3;
|
||||
half = (sequencer_step == 1 || sequencer_step == 4);
|
||||
reset = sequencer_step == 4;
|
||||
break;
|
||||
|
||||
default:
|
||||
throw new InvalidOperationException();
|
||||
}
|
||||
|
||||
if (reset)
|
||||
{
|
||||
sequencer_counter = 0;
|
||||
sequencer_step = 0;
|
||||
}
|
||||
else sequencer_step++;
|
||||
|
||||
if (quarter) QuarterFrame();
|
||||
if (half) HalfFrame();
|
||||
}
|
||||
|
||||
void HalfFrame()
|
||||
{
|
||||
pulse[0].clock_length_and_sweep();
|
||||
pulse[1].clock_length_and_sweep();
|
||||
triangle.clock_length_and_sweep();
|
||||
noise.clock_length_and_sweep();
|
||||
}
|
||||
|
||||
void QuarterFrame()
|
||||
{
|
||||
pulse[0].clock_env();
|
||||
pulse[1].clock_env();
|
||||
triangle.clock_linear_counter();
|
||||
noise.clock_env();
|
||||
}
|
||||
|
||||
public void WriteReg(int addr, byte val)
|
||||
{
|
||||
pending_reg = addr;
|
||||
pending_val = val;
|
||||
}
|
||||
|
||||
void _WriteReg(int addr, byte val)
|
||||
{
|
||||
//Console.WriteLine("{0:X4} = {1:X2}", addr, val);
|
||||
switch (addr)
|
||||
{
|
||||
case 0x4000: case 0x4001: case 0x4002: case 0x4003:
|
||||
case 0x4000:
|
||||
case 0x4001:
|
||||
case 0x4002:
|
||||
case 0x4003:
|
||||
pulse[0].WriteReg(addr - 0x4000, val);
|
||||
break;
|
||||
case 0x4004: case 0x4005: case 0x4006: case 0x4007:
|
||||
case 0x4004:
|
||||
case 0x4005:
|
||||
case 0x4006:
|
||||
case 0x4007:
|
||||
pulse[1].WriteReg(addr - 0x4004, val);
|
||||
break;
|
||||
case 0x4008: case 0x4009: case 0x400A: case 0x400B:
|
||||
case 0x4008:
|
||||
case 0x4009:
|
||||
case 0x400A:
|
||||
case 0x400B:
|
||||
triangle.WriteReg(addr - 0x4008, val);
|
||||
break;
|
||||
case 0x400C: case 0x400D: case 0x400E: case 0x400F:
|
||||
case 0x400C:
|
||||
case 0x400D:
|
||||
case 0x400E:
|
||||
case 0x400F:
|
||||
noise.WriteReg(addr - 0x400C, val);
|
||||
break;
|
||||
case 0x4010: case 0x4011: case 0x4012: case 0x4013:
|
||||
case 0x4010:
|
||||
case 0x4011:
|
||||
case 0x4012:
|
||||
case 0x4013:
|
||||
dmc.WriteReg(addr - 0x4010, val);
|
||||
break;
|
||||
case 0x4015:
|
||||
|
@ -824,14 +871,14 @@ namespace BizHawk.Emulation.Consoles.Nintendo
|
|||
dmc.set_lenctr_en(val.Bit(4));
|
||||
break;
|
||||
case 0x4017:
|
||||
sequencer_mode = (val>>7)&1;
|
||||
//Console.WriteLine("apu 4017 = {0:X2}", val);
|
||||
sequencer_mode = (val >> 7) & 1;
|
||||
sequencer_irq_inhibit = (val >> 6) & 1;
|
||||
if (sequencer_irq_inhibit == 1)
|
||||
{
|
||||
sequencer_irq = false;
|
||||
SyncIRQ();
|
||||
sequencer_irq_clear_pending = true;
|
||||
}
|
||||
sequencer_reset();
|
||||
sequence_reset_pending = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
@ -841,36 +888,32 @@ namespace BizHawk.Emulation.Consoles.Nintendo
|
|||
switch (addr)
|
||||
{
|
||||
case 0x4015:
|
||||
{
|
||||
//notice a missing bit here. should properly emulate with empty bus
|
||||
//if an interrupt flag was set at the same moment of the read, it will read back as 1 but it will not be cleared.
|
||||
int dmc_nonzero = dmc.IsLenCntNonZero() ? 1 : 0;
|
||||
int noise_nonzero = noise.IsLenCntNonZero() ? 1 : 0;
|
||||
int tri_nonzero = triangle.IsLenCntNonZero() ? 1 : 0;
|
||||
int pulse1_nonzero = pulse[1].IsLenCntNonZero() ? 1 : 0;
|
||||
int pulse0_nonzero = pulse[0].IsLenCntNonZero() ? 1 : 0;
|
||||
int ret = ((dmc_irq?1:0) << 7) | ((sequencer_irq?1:0) << 6) | (dmc_nonzero << 4) | (noise_nonzero << 3) | (tri_nonzero<<2) | (pulse1_nonzero<<1) | (pulse0_nonzero);
|
||||
sequencer_irq = false;
|
||||
SyncIRQ();
|
||||
return (byte)ret;
|
||||
}
|
||||
{
|
||||
//notice a missing bit here. should properly emulate with empty bus
|
||||
//if an interrupt flag was set at the same moment of the read, it will read back as 1 but it will not be cleared.
|
||||
int dmc_nonzero = dmc.IsLenCntNonZero() ? 1 : 0;
|
||||
int noise_nonzero = noise.IsLenCntNonZero() ? 1 : 0;
|
||||
int tri_nonzero = triangle.IsLenCntNonZero() ? 1 : 0;
|
||||
int pulse1_nonzero = pulse[1].IsLenCntNonZero() ? 1 : 0;
|
||||
int pulse0_nonzero = pulse[0].IsLenCntNonZero() ? 1 : 0;
|
||||
int ret = ((dmc_irq ? 1 : 0) << 7) | ((sequencer_irq ? 1 : 0) << 6) | (dmc_nonzero << 4) | (noise_nonzero << 3) | (tri_nonzero << 2) | (pulse1_nonzero << 1) | (pulse0_nonzero);
|
||||
//Console.WriteLine("{0} {1,5} $4015 clear irq, was at {2}", nes.Frame, sequencer_counter, sequencer_irq);
|
||||
sequencer_irq = false;
|
||||
SyncIRQ();
|
||||
return (byte)ret;
|
||||
}
|
||||
default:
|
||||
//don't return 0xFF here or SMB will break
|
||||
return 0x00;
|
||||
}
|
||||
}
|
||||
|
||||
public void Run(int cycles)
|
||||
{
|
||||
for (int i = 0; i < cycles; i++)
|
||||
RunOne();
|
||||
}
|
||||
|
||||
public void DiscardSamples()
|
||||
{
|
||||
metaspu.buffer.clear();
|
||||
}
|
||||
|
||||
int toggle = 0;
|
||||
public void RunOne()
|
||||
{
|
||||
pulse[0].Run();
|
||||
|
@ -883,12 +926,42 @@ namespace BizHawk.Emulation.Consoles.Nintendo
|
|||
mix += pulse[0].sample;
|
||||
mix += pulse[1].sample;
|
||||
mix += triangle.sample;
|
||||
mix += noise.sample>>1;
|
||||
mix += noise.sample >> 1;
|
||||
mix += dmc.sample;
|
||||
|
||||
EmitSample(mix);
|
||||
|
||||
//this (and the similar line below) is a crude hack
|
||||
//we should be generating logic to suppress the $4015 clear when the assert signal is set instead
|
||||
//be sure to test "apu_test" if you mess with this
|
||||
sequencer_irq |= sequencer_irq_assert;
|
||||
|
||||
if (toggle == 0)
|
||||
{
|
||||
//handle sequencer irq clear signal
|
||||
sequencer_irq_assert = false;
|
||||
if (sequencer_irq_clear_pending)
|
||||
{
|
||||
//Console.WriteLine("{0} {1,5} $4017 clear irq (delayed)", nes.Frame, sequencer_counter);
|
||||
sequencer_irq_clear_pending = false;
|
||||
sequencer_irq = false;
|
||||
SyncIRQ();
|
||||
}
|
||||
|
||||
//handle writes from the odd clock cycle
|
||||
if (pending_reg != -1) _WriteReg(pending_reg, pending_val);
|
||||
pending_reg = -1;
|
||||
toggle = 1;
|
||||
|
||||
//latch whatever irq logic we had and send to cpu
|
||||
nes.irq_apu = irq_pending;
|
||||
}
|
||||
else toggle = 0;
|
||||
|
||||
sequencer_tick();
|
||||
sequencer_irq |= sequencer_irq_assert;
|
||||
SyncIRQ();
|
||||
|
||||
//since the units run concurrently, the APU frame sequencer is ran last because
|
||||
//it can change the ouput values of the pulse/triangle channels
|
||||
//we want the changes to affect it on the *next* cycle.
|
||||
|
@ -909,9 +982,9 @@ namespace BizHawk.Emulation.Consoles.Nintendo
|
|||
samp = 0;
|
||||
else
|
||||
panic_sample = samp;
|
||||
|
||||
|
||||
int this_samp = samp;
|
||||
const double kMixRate = 44100.0/1789772.0;
|
||||
const double kMixRate = 44100.0 / 1789772.0;
|
||||
const double kInvMixRate = (1 / kMixRate);
|
||||
timer += kMixRate;
|
||||
accumulate += samp;
|
||||
|
@ -922,7 +995,7 @@ namespace BizHawk.Emulation.Consoles.Nintendo
|
|||
timer -= 1;
|
||||
double ratio = (timer / kMixRate);
|
||||
double fractional = (this_samp - last_hwsamp) * ratio;
|
||||
double factional_remainder = (this_samp - last_hwsamp) * (1-ratio);
|
||||
double factional_remainder = (this_samp - last_hwsamp) * (1 - ratio);
|
||||
accumulate += fractional;
|
||||
|
||||
accumulate *= 436; //32768/(15*4) -- adjust later for other sound channels
|
||||
|
@ -936,7 +1009,7 @@ namespace BizHawk.Emulation.Consoles.Nintendo
|
|||
}
|
||||
|
||||
MetaspuSoundProvider metaspu = new MetaspuSoundProvider(ESynchMethod.ESynchMethod_V);
|
||||
public int MaxVolume { get; set; } // not supported
|
||||
public int MaxVolume { get; set; } // not supported
|
||||
|
||||
void ISoundProvider.GetSamples(short[] samples)
|
||||
{
|
||||
|
@ -954,14 +1027,14 @@ namespace BizHawk.Emulation.Consoles.Nintendo
|
|||
{
|
||||
//Console.WriteLine("a: {0} with todo: {1}",squeue.Count,samples.Length/2);
|
||||
|
||||
for (int i = 0; i < samples.Length/2; i++)
|
||||
for (int i = 0; i < samples.Length / 2; i++)
|
||||
{
|
||||
int samp = 0;
|
||||
if (squeue.Count != 0)
|
||||
samp = squeue.Dequeue();
|
||||
|
||||
samples[i*2+0] = (short)(samp);
|
||||
samples[i*2+1] = (short)(samp);
|
||||
|
||||
samples[i * 2 + 0] = (short)(samp);
|
||||
samples[i * 2 + 1] = (short)(samp);
|
||||
//bw.Write((short)samp);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -23,7 +23,10 @@ namespace BizHawk.Emulation.Consoles.Nintendo
|
|||
CartInfo cart; //the current cart prototype. should be moved into the board, perhaps
|
||||
INESBoard board; //the board hardware that is currently driving things
|
||||
public bool SoundOn = true;
|
||||
bool _irq_apu, _irq_cart;
|
||||
int sprdma_countdown; //used to
|
||||
bool _irq_apu, _irq_cart; //various irq signals that get merged to the cpu irq pin
|
||||
|
||||
//irq state management
|
||||
public bool irq_apu { get { return _irq_apu; } set { _irq_apu = value; sync_irq(); } }
|
||||
public bool irq_cart { get { return _irq_cart; } set { _irq_cart = value; sync_irq(); } }
|
||||
void sync_irq()
|
||||
|
@ -95,10 +98,6 @@ namespace BizHawk.Emulation.Consoles.Nintendo
|
|||
static ByteBuffer cpu_sequence_NTSC = new ByteBuffer(new byte[]{3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3});
|
||||
static ByteBuffer cpu_sequence_PAL = new ByteBuffer(new byte[]{4,3,3,3,3,4,3,3,3,3,4,3,3,3,3,4,3,3,3,3,4,3,3,3,3,4,3,3,3,3,4,3,3,3,3,4,3,3,3,3});
|
||||
public int cpu_step, cpu_stepcounter, cpu_deadcounter;
|
||||
public void DmaTimingHack(int amount)
|
||||
{
|
||||
cpu_deadcounter += amount;
|
||||
}
|
||||
protected void RunCpuOne()
|
||||
{
|
||||
cpu_stepcounter++;
|
||||
|
@ -107,10 +106,24 @@ namespace BizHawk.Emulation.Consoles.Nintendo
|
|||
cpu_step++;
|
||||
cpu_step &= 31;
|
||||
cpu_stepcounter = 0;
|
||||
if (cpu_deadcounter == 0)
|
||||
|
||||
if (sprdma_countdown > 0)
|
||||
{
|
||||
sprdma_countdown--;
|
||||
if (sprdma_countdown == 0)
|
||||
{
|
||||
//its weird that this is 514.. normally itd be 512 (and people would say its wrong) or 513 (and people would say its right)
|
||||
//but 514 passes test 4-irq_and_dma
|
||||
cpu_deadcounter = 514;
|
||||
}
|
||||
}
|
||||
|
||||
if(cpu_deadcounter>0)
|
||||
cpu_deadcounter--;
|
||||
else
|
||||
cpu.ExecuteOne();
|
||||
else cpu_deadcounter--;
|
||||
if (SoundOn) apu.RunOne(); //THIS ISNT SAFE!!!!!!!!!
|
||||
|
||||
if (SoundOn) apu.RunOne(); //THIS ISNT SAFE!!!!!!!!! SOUND MUST ALWAYS RUN!!!!
|
||||
ppu.PostCpuInstructionOne();
|
||||
}
|
||||
}
|
||||
|
@ -176,10 +189,10 @@ namespace BizHawk.Emulation.Consoles.Nintendo
|
|||
{
|
||||
//read joystick port
|
||||
//many todos here
|
||||
lagged = false;
|
||||
lagged = false;
|
||||
byte ret;
|
||||
if(addr == 0x4016)
|
||||
ret = ports[0].Read();
|
||||
ret = ports[0].Read();
|
||||
else ret = ports[1].Read();
|
||||
return ret;
|
||||
}
|
||||
|
@ -193,7 +206,9 @@ namespace BizHawk.Emulation.Consoles.Nintendo
|
|||
WriteMemory(0x2004, db);
|
||||
addr++;
|
||||
}
|
||||
DmaTimingHack(513);
|
||||
//schedule a sprite dma event for beginning 1 cycle in the future.
|
||||
//this receives 2 because thats just the way it works out.
|
||||
sprdma_countdown = 2;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
|
|
@ -597,9 +597,11 @@ namespace BizHawk.Emulation.Consoles.Nintendo
|
|||
ser.Sync("cpu_accumulate", ref cpu_accumulate);
|
||||
ser.Sync("_irq_apu", ref _irq_apu);
|
||||
ser.Sync("_irq_cart", ref _irq_cart);
|
||||
ser.Sync("sprdma_countdown", ref sprdma_countdown);
|
||||
ser.Sync("cpu_step", ref cpu_step);
|
||||
ser.Sync("cpu_stepcounter", ref cpu_stepcounter);
|
||||
ser.Sync("cpu_deadcounter", ref cpu_deadcounter);
|
||||
sync_irq();
|
||||
//string inp = GetControllersAsMnemonic(); TODO sorry bout that
|
||||
//ser.SyncFixedString("input", ref inp, 32);
|
||||
board.SyncState(ser);
|
||||
ppu.SyncState(ser);
|
||||
apu.SyncState(ser);
|
||||
|
|
|
@ -95,6 +95,7 @@ namespace BizHawk.Emulation.Consoles.Nintendo
|
|||
ser.Sync("PPUGenLatch", ref PPUGenLatch);
|
||||
ser.Sync("vtoggle", ref vtoggle);
|
||||
ser.Sync("VRAMBuffer", ref VRAMBuffer);
|
||||
ser.Sync("ppu_addr_temp", ref ppu_addr_temp);
|
||||
|
||||
ser.Sync("OAM", ref OAM, false);
|
||||
ser.Sync("PALRAM", ref PALRAM, false);
|
||||
|
|
|
@ -27,39 +27,67 @@ namespace BizHawk.Emulation.Consoles.Nintendo
|
|||
|
||||
public short[] xbuf = new short[256*240];
|
||||
|
||||
void Read_bgdata(ref BGDataRecord bgdata) {
|
||||
int addr = ppur.get_ntread();
|
||||
bgdata.nt = ppubus_read(addr, true);
|
||||
runppu(kFetchTime);
|
||||
|
||||
addr = ppur.get_atread();
|
||||
byte at = ppubus_read(addr, true);
|
||||
|
||||
//modify at to get appropriate palette shift
|
||||
if((ppur.vt&2)!=0) at >>= 4;
|
||||
if((ppur.ht&2)!=0) at >>= 2;
|
||||
at &= 0x03;
|
||||
at <<= 2;
|
||||
|
||||
bgdata.at = at;
|
||||
|
||||
//horizontal scroll clocked at cycle 3 and then
|
||||
//vertical scroll at 251
|
||||
runppu(1);
|
||||
if (reg_2001.PPUON)
|
||||
int ppu_addr_temp;
|
||||
void Read_bgdata(ref BGDataRecord bgdata)
|
||||
{
|
||||
for (int i = 0; i < 8; i++)
|
||||
Read_bgdata(i,ref bgdata);
|
||||
}
|
||||
void Read_bgdata(int cycle, ref BGDataRecord bgdata)
|
||||
{
|
||||
switch (cycle)
|
||||
{
|
||||
ppur.increment_hsc();
|
||||
if (ppur.status.cycle == 251)
|
||||
ppur.increment_vs();
|
||||
}
|
||||
runppu(1);
|
||||
case 0:
|
||||
ppu_addr_temp = ppur.get_ntread();
|
||||
bgdata.nt = ppubus_read(ppu_addr_temp, true);
|
||||
runppu(1);
|
||||
break;
|
||||
case 1:
|
||||
runppu(1);
|
||||
break;
|
||||
case 2:
|
||||
{
|
||||
ppu_addr_temp = ppur.get_atread();
|
||||
byte at = ppubus_read(ppu_addr_temp, true);
|
||||
|
||||
addr = ppur.get_ptread(bgdata.nt);
|
||||
bgdata.pt_0 = ppubus_read(addr, true);
|
||||
runppu(kFetchTime);
|
||||
addr |= 8;
|
||||
bgdata.pt_1 = ppubus_read(addr, true);
|
||||
runppu(kFetchTime);
|
||||
//modify at to get appropriate palette shift
|
||||
if ((ppur.vt & 2) != 0) at >>= 4;
|
||||
if ((ppur.ht & 2) != 0) at >>= 2;
|
||||
at &= 0x03;
|
||||
at <<= 2;
|
||||
bgdata.at = at;
|
||||
|
||||
//horizontal scroll clocked at cycle 3 and then
|
||||
//vertical scroll at 251
|
||||
runppu(1);
|
||||
if (reg_2001.PPUON)
|
||||
{
|
||||
ppur.increment_hsc();
|
||||
if (ppur.status.cycle == 251)
|
||||
ppur.increment_vs();
|
||||
}
|
||||
break;
|
||||
}
|
||||
case 3:
|
||||
runppu(1);
|
||||
break;
|
||||
case 4:
|
||||
ppu_addr_temp = ppur.get_ptread(bgdata.nt);
|
||||
bgdata.pt_0 = ppubus_read(ppu_addr_temp, true);
|
||||
runppu(1);
|
||||
break;
|
||||
case 5:
|
||||
runppu(1);
|
||||
break;
|
||||
case 6:
|
||||
ppu_addr_temp |= 8;
|
||||
bgdata.pt_1 = ppubus_read(ppu_addr_temp, true);
|
||||
runppu(1);
|
||||
break;
|
||||
case 7:
|
||||
runppu(1);
|
||||
break;
|
||||
} //switch(cycle)
|
||||
}
|
||||
|
||||
unsafe struct TempOAM
|
||||
|
@ -124,12 +152,6 @@ namespace BizHawk.Emulation.Consoles.Nintendo
|
|||
{
|
||||
ppur.status.cycle = 0;
|
||||
|
||||
//if (!reg_2001.PPUON)
|
||||
//{
|
||||
// runppu(kLineTime);
|
||||
// continue;
|
||||
//}
|
||||
|
||||
ppur.status.sl = sl;
|
||||
|
||||
int yp = sl - 1;
|
||||
|
@ -150,12 +172,12 @@ namespace BizHawk.Emulation.Consoles.Nintendo
|
|||
//two of those tiles were read in the last scanline.
|
||||
for (int xt = 0; xt < 32; xt++)
|
||||
{
|
||||
Read_bgdata(ref bgdata[xt + 2]);
|
||||
|
||||
//ok, we're also going to draw here.
|
||||
//unless we're on the first dummy scanline
|
||||
if (sl != 0)
|
||||
{
|
||||
|
||||
|
||||
int xstart = xt << 3;
|
||||
oamcount = oamcounts[renderslot];
|
||||
int target = (yp << 8) + xstart;
|
||||
|
@ -167,6 +189,10 @@ namespace BizHawk.Emulation.Consoles.Nintendo
|
|||
|
||||
for (int xp = 0; xp < 8; xp++, rasterpos++)
|
||||
{
|
||||
//process the current clock's worth of bg data fetching
|
||||
//this needs to be split into 8 pieces or else exact sprite 0 hitting wont work due to the cpu not running while the sprite renders below
|
||||
Read_bgdata(xp, ref bgdata[xt + 2]);
|
||||
|
||||
//bg pos is different from raster pos due to its offsetability.
|
||||
//so adjust for that here
|
||||
int bgpos = rasterpos + ppur.fh;
|
||||
|
@ -187,18 +213,27 @@ namespace BizHawk.Emulation.Consoles.Nintendo
|
|||
}
|
||||
else
|
||||
{
|
||||
if (!renderspritenow)
|
||||
{
|
||||
//according to qeed's doc, use palette 0 or $2006's value if it is & 0x3Fxx
|
||||
int addr = ppur.get_2007access();
|
||||
if ((addr & 0x3F00) == 0x3F00)
|
||||
{
|
||||
pixel = addr & 0x1F;
|
||||
}
|
||||
}
|
||||
pixelcolor = PALRAM[pixel];
|
||||
pixelcolor |= 0x8000;
|
||||
pixelcolor |= 0x8000; //whats this? i think its a flag to indicate a hidden background to be used by the canvas filling logic later
|
||||
}
|
||||
|
||||
if (!nes.CoreInputComm.NES_ShowBG)
|
||||
pixelcolor = 0x8000;
|
||||
pixelcolor = 0x8000; //whats this? i think its a flag to indicate a hidden background to be used by the canvas filling logic later
|
||||
|
||||
//look for a sprite to be drawn
|
||||
bool havepixel = false;
|
||||
for (int s = 0; s < oamcount; s++)
|
||||
{
|
||||
TempOAM *oam = &oams[(renderslot<<6)+s];
|
||||
TempOAM* oam = &oams[(renderslot << 6) + s];
|
||||
{
|
||||
int x = oam->oam[3];
|
||||
if (rasterpos >= x && rasterpos < x + 8)
|
||||
|
@ -241,7 +276,7 @@ namespace BizHawk.Emulation.Consoles.Nintendo
|
|||
if ((pixel & 3) != 0)
|
||||
{
|
||||
drawsprite = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (drawsprite && nes.CoreInputComm.NES_ShowOBJ)
|
||||
|
@ -258,12 +293,20 @@ namespace BizHawk.Emulation.Consoles.Nintendo
|
|||
|
||||
}//oamcount loop
|
||||
|
||||
|
||||
if (reg_2001.color_disable)
|
||||
pixelcolor &= 0x30;
|
||||
|
||||
xbuf[target] = PaletteAdjustPixel(pixelcolor);
|
||||
|
||||
target++;
|
||||
|
||||
} //loop across 8 pixels
|
||||
} //scanline != 0
|
||||
else
|
||||
{
|
||||
Read_bgdata(ref bgdata[xt + 2]);
|
||||
}
|
||||
} //loop across 32 tiles
|
||||
|
||||
|
||||
|
|
Loading…
Reference in New Issue