dev_4VirtualNes #18
@ -43,6 +43,11 @@ namespace VirtualNes.Core
|
|||||||
{
|
{
|
||||||
@internal.Sync(cycles);
|
@internal.Sync(cycles);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
internal byte Read(ushort addr)
|
||||||
|
{
|
||||||
|
return @internal.SyncRead(addr);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public struct QUEUEDATA
|
public struct QUEUEDATA
|
||||||
|
@ -1,11 +1,26 @@
|
|||||||
|
using System;
|
||||||
|
|
||||||
namespace VirtualNes.Core
|
namespace VirtualNes.Core
|
||||||
{
|
{
|
||||||
public class APU_INTERNAL : APU_INTERFACE
|
public class APU_INTERNAL : APU_INTERFACE
|
||||||
{
|
{
|
||||||
private NES nes;
|
private NES nes;
|
||||||
|
// Frame Counter
|
||||||
private int FrameCycle;
|
private int FrameCycle;
|
||||||
|
private int FrameCount;
|
||||||
|
private int FrameType;
|
||||||
|
private byte FrameIRQ;
|
||||||
private byte FrameIRQoccur;
|
private byte FrameIRQoccur;
|
||||||
|
|
||||||
|
// Channels
|
||||||
|
private RECTANGLE ch0 = new RECTANGLE();
|
||||||
|
private RECTANGLE ch1 = new RECTANGLE();
|
||||||
|
private TRIANGLE ch2 = new TRIANGLE();
|
||||||
|
private NOISE ch3 = new NOISE();
|
||||||
|
private DPCM ch4 = new DPCM();
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
public void SetParent(NES parent)
|
public void SetParent(NES parent)
|
||||||
{
|
{
|
||||||
nes = parent;
|
nes = parent;
|
||||||
@ -55,5 +70,37 @@ namespace VirtualNes.Core
|
|||||||
{
|
{
|
||||||
throw new System.NotImplementedException();
|
throw new System.NotImplementedException();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
internal byte SyncRead(ushort addr)
|
||||||
|
{
|
||||||
|
byte data = (byte)(addr >> 8);
|
||||||
|
|
||||||
|
if (addr == 0x4015)
|
||||||
|
{
|
||||||
|
data = 0;
|
||||||
|
if ((ch0.sync_enable != 0) && ch0.sync_len_count > 0) data |= (1 << 0);
|
||||||
|
if ((ch1.sync_enable != 0) && ch1.sync_len_count > 0) data |= (1 << 1);
|
||||||
|
if ((ch2.sync_enable != 0) && ch2.sync_len_count > 0) data |= (1 << 2);
|
||||||
|
if ((ch3.sync_enable != 0) && ch3.sync_len_count > 0) data |= (1 << 3);
|
||||||
|
if ((ch4.sync_enable != 0) && (ch4.sync_dmalength != 0)) data |= (1 << 4);
|
||||||
|
if (FrameIRQoccur != 0) data |= (1 << 6);
|
||||||
|
if (ch4.sync_irq_enable != 0) data |= (1 << 7);
|
||||||
|
FrameIRQoccur = 0;
|
||||||
|
|
||||||
|
nes.cpu.ClrIRQ(CPU.IRQ_FRAMEIRQ);
|
||||||
|
}
|
||||||
|
if (addr == 0x4017)
|
||||||
|
{
|
||||||
|
if (FrameIRQoccur != 0)
|
||||||
|
{
|
||||||
|
data = 0;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
data |= (1 << 6);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return data;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
35
AxibugEmuOnline.Client/Assets/VirtualNes.Core/ApuEX/DPCM.cs
Normal file
35
AxibugEmuOnline.Client/Assets/VirtualNes.Core/ApuEX/DPCM.cs
Normal file
@ -0,0 +1,35 @@
|
|||||||
|
using Codice.CM.Client.Differences;
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
namespace VirtualNes.Core
|
||||||
|
{
|
||||||
|
public class DPCM
|
||||||
|
{
|
||||||
|
public byte[] reg = new byte[4];
|
||||||
|
public byte enable;
|
||||||
|
public byte looping;
|
||||||
|
public byte cur_byte;
|
||||||
|
public byte dpcm_value;
|
||||||
|
|
||||||
|
public int freq;
|
||||||
|
public int phaseacc;
|
||||||
|
public int output;
|
||||||
|
|
||||||
|
ushort address, cache_addr;
|
||||||
|
public int dmalength, cache_dmalength;
|
||||||
|
public int dpcm_output_real, dpcm_output_fake, dpcm_output_old, dpcm_output_offset;
|
||||||
|
|
||||||
|
// For sync
|
||||||
|
public byte[] sync_reg = new byte[4];
|
||||||
|
public byte sync_enable;
|
||||||
|
public byte sync_looping;
|
||||||
|
public byte sync_irq_gen;
|
||||||
|
public byte sync_irq_enable;
|
||||||
|
public int sync_cycles, sync_cache_cycles;
|
||||||
|
public int sync_dmalength, sync_cache_dmalength;
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,11 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: e6289a516ac91b541b2b1807bb07e2b0
|
||||||
|
MonoImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
serializedVersion: 2
|
||||||
|
defaultReferences: []
|
||||||
|
executionOrder: 0
|
||||||
|
icon: {instanceID: 0}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
42
AxibugEmuOnline.Client/Assets/VirtualNes.Core/ApuEX/NOISE.cs
Normal file
42
AxibugEmuOnline.Client/Assets/VirtualNes.Core/ApuEX/NOISE.cs
Normal file
@ -0,0 +1,42 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
namespace VirtualNes.Core
|
||||||
|
{
|
||||||
|
public class NOISE
|
||||||
|
{
|
||||||
|
public byte[] reg = new byte[4]; // register
|
||||||
|
|
||||||
|
public byte enable; // enable
|
||||||
|
public byte holdnote; // holdnote
|
||||||
|
public byte volume; // volume
|
||||||
|
public byte xor_tap;
|
||||||
|
public int shift_reg;
|
||||||
|
|
||||||
|
// For Render
|
||||||
|
public int phaseacc;
|
||||||
|
public int freq;
|
||||||
|
public int len_count;
|
||||||
|
|
||||||
|
public int nowvolume;
|
||||||
|
public int output;
|
||||||
|
|
||||||
|
// For Envelope
|
||||||
|
public byte env_fixed;
|
||||||
|
public byte env_decay;
|
||||||
|
public byte env_count;
|
||||||
|
public byte dummy0;
|
||||||
|
public int env_vol;
|
||||||
|
|
||||||
|
// For sync;
|
||||||
|
public byte[] sync_reg = new byte[4];
|
||||||
|
public byte sync_output_enable;
|
||||||
|
public byte sync_enable;
|
||||||
|
public byte sync_holdnote;
|
||||||
|
public byte dummy1;
|
||||||
|
public int sync_len_count;
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,11 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 8680ce7dbdceb504dbda3b98dbdb1297
|
||||||
|
MonoImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
serializedVersion: 2
|
||||||
|
defaultReferences: []
|
||||||
|
executionOrder: 0
|
||||||
|
icon: {instanceID: 0}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
@ -0,0 +1,51 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
namespace VirtualNes.Core
|
||||||
|
{
|
||||||
|
public class RECTANGLE
|
||||||
|
{
|
||||||
|
public byte[] reg = new byte[4]; // register
|
||||||
|
|
||||||
|
public byte enable; // enable
|
||||||
|
public byte holdnote; // holdnote
|
||||||
|
public byte volume; // volume
|
||||||
|
public byte complement;
|
||||||
|
|
||||||
|
// For Render
|
||||||
|
public int phaseacc;
|
||||||
|
public int freq;
|
||||||
|
public int freqlimit;
|
||||||
|
public int adder;
|
||||||
|
public int duty;
|
||||||
|
public int len_count;
|
||||||
|
|
||||||
|
public int nowvolume;
|
||||||
|
|
||||||
|
// For Envelope
|
||||||
|
public byte env_fixed;
|
||||||
|
public byte env_decay;
|
||||||
|
public byte env_count;
|
||||||
|
public byte dummy0;
|
||||||
|
public int env_vol;
|
||||||
|
|
||||||
|
// For Sweep
|
||||||
|
public byte swp_on;
|
||||||
|
public byte swp_inc;
|
||||||
|
public byte swp_shift;
|
||||||
|
public byte swp_decay;
|
||||||
|
public byte swp_count;
|
||||||
|
public byte[] dummy1 = new byte[3];
|
||||||
|
|
||||||
|
// For sync;
|
||||||
|
public byte[] sync_reg = new byte[4];
|
||||||
|
public byte sync_output_enable;
|
||||||
|
public byte sync_enable;
|
||||||
|
public byte sync_holdnote;
|
||||||
|
public byte dummy2;
|
||||||
|
public int sync_len_count;
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,11 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 6e50831f6c445fe489d7e1737269296e
|
||||||
|
MonoImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
serializedVersion: 2
|
||||||
|
defaultReferences: []
|
||||||
|
executionOrder: 0
|
||||||
|
icon: {instanceID: 0}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
@ -0,0 +1,29 @@
|
|||||||
|
namespace VirtualNes.Core
|
||||||
|
{
|
||||||
|
public class TRIANGLE
|
||||||
|
{
|
||||||
|
public byte[] reg = new byte[4];
|
||||||
|
|
||||||
|
public byte enable;
|
||||||
|
public byte holdnote;
|
||||||
|
public byte counter_start;
|
||||||
|
public byte dummy0;
|
||||||
|
|
||||||
|
public int phaseacc;
|
||||||
|
public int freq;
|
||||||
|
public int len_count;
|
||||||
|
public int lin_count;
|
||||||
|
public int adder;
|
||||||
|
|
||||||
|
public int nowvolume;
|
||||||
|
|
||||||
|
// For sync;
|
||||||
|
public byte[] sync_reg = new byte[4];
|
||||||
|
public byte sync_enable;
|
||||||
|
public byte sync_holdnote;
|
||||||
|
public byte sync_counter_start;
|
||||||
|
// public byte dummy1;
|
||||||
|
public int sync_len_count;
|
||||||
|
public int sync_lin_count;
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,11 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 4ed2788da33fe474facc1d7ce1b34d03
|
||||||
|
MonoImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
serializedVersion: 2
|
||||||
|
defaultReferences: []
|
||||||
|
executionOrder: 0
|
||||||
|
icon: {instanceID: 0}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
@ -135,6 +135,13 @@ namespace VirtualNes.Core
|
|||||||
case 0x75:
|
case 0x75:
|
||||||
MR_ZX(ref DT, ref EA, ref R); ADC(ref WT, ref DT, ref R);
|
MR_ZX(ref DT, ref EA, ref R); ADC(ref WT, ref DT, ref R);
|
||||||
ADD_CYCLE(4, ref exec_cycles);
|
ADD_CYCLE(4, ref exec_cycles);
|
||||||
|
break;
|
||||||
|
case 0x6D:
|
||||||
|
MR_AB(ref EA, ref DT, ref R);ADC(ref WT, ref DT, ref R);
|
||||||
|
ADD_CYCLE(4, ref exec_cycles);
|
||||||
|
break;
|
||||||
|
case 0x7D:
|
||||||
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -157,26 +164,51 @@ namespace VirtualNes.Core
|
|||||||
return MMU.CPU_MEM_BANK[addr >> 13][addr & 0x1FFF];
|
return MMU.CPU_MEM_BANK[addr >> 13][addr & 0x1FFF];
|
||||||
}
|
}
|
||||||
|
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
internal ushort OP6502W(ushort addr)
|
||||||
|
{
|
||||||
|
var bytePage = MMU.CPU_MEM_BANK[addr >> 13];
|
||||||
|
|
||||||
|
return BitConverter.ToUInt16(bytePage, addr & 0x1FFF);
|
||||||
|
}
|
||||||
|
|
||||||
|
internal byte RD6502(ushort addr)
|
||||||
|
{
|
||||||
|
if (addr < 0x2000)
|
||||||
|
{
|
||||||
|
// RAM (Mirror $0800, $1000, $1800)
|
||||||
|
return MMU.RAM[addr & 0x07FF];
|
||||||
|
}
|
||||||
|
else if (addr < 0x8000)
|
||||||
|
{
|
||||||
|
// Others
|
||||||
|
return nes.Read(addr);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// Dummy access
|
||||||
|
mapper.Read(addr, MMU.CPU_MEM_BANK[addr >> 13][addr & 0x1FFF]);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Quick bank read
|
||||||
|
return MMU.CPU_MEM_BANK[addr >> 13][addr & 0x1FFF];
|
||||||
|
}
|
||||||
|
|
||||||
private void MR_IM(ref byte DT, ref R6502 R)
|
private void MR_IM(ref byte DT, ref R6502 R)
|
||||||
{
|
{
|
||||||
DT = OP6502(R.PC++);
|
DT = OP6502(R.PC++);
|
||||||
}
|
}
|
||||||
|
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
|
||||||
private void MR_ZP(ref ushort EA, ref byte DT, ref R6502 R)
|
private void MR_ZP(ref ushort EA, ref byte DT, ref R6502 R)
|
||||||
{
|
{
|
||||||
EA = OP6502(R.PC++);
|
EA = OP6502(R.PC++);
|
||||||
DT = ZPRD(ref EA);
|
DT = ZPRD(ref EA);
|
||||||
}
|
}
|
||||||
|
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
|
||||||
private byte ZPRD(ref ushort A)
|
private byte ZPRD(ref ushort A)
|
||||||
{
|
{
|
||||||
return MMU.RAM[A];
|
return MMU.RAM[A];
|
||||||
}
|
}
|
||||||
|
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
|
||||||
private void ADC(ref ushort WT, ref byte DT, ref R6502 R)
|
private void ADC(ref ushort WT, ref byte DT, ref R6502 R)
|
||||||
{
|
{
|
||||||
WT = (ushort)(R.A + DT + (R.P & C_FLAG));
|
WT = (ushort)(R.A + DT + (R.P & C_FLAG));
|
||||||
@ -187,7 +219,6 @@ namespace VirtualNes.Core
|
|||||||
SET_ZN_FLAG(R.A, ref R);
|
SET_ZN_FLAG(R.A, ref R);
|
||||||
}
|
}
|
||||||
|
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
|
||||||
private void TST_FLAG(bool F, byte V, ref R6502 R)
|
private void TST_FLAG(bool F, byte V, ref R6502 R)
|
||||||
{
|
{
|
||||||
byte temp = (byte)~V;
|
byte temp = (byte)~V;
|
||||||
@ -196,7 +227,6 @@ namespace VirtualNes.Core
|
|||||||
if (F) R.P |= V;
|
if (F) R.P |= V;
|
||||||
}
|
}
|
||||||
|
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
|
||||||
private void SET_ZN_FLAG(byte A, ref R6502 R)
|
private void SET_ZN_FLAG(byte A, ref R6502 R)
|
||||||
{
|
{
|
||||||
byte temp = unchecked((byte)(~(Z_FLAG | N_FLAG)));
|
byte temp = unchecked((byte)(~(Z_FLAG | N_FLAG)));
|
||||||
@ -204,19 +234,30 @@ namespace VirtualNes.Core
|
|||||||
R.P |= ZN_Table[A];
|
R.P |= ZN_Table[A];
|
||||||
}
|
}
|
||||||
|
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
|
||||||
private void ADD_CYCLE(int V, ref int exec_cycles)
|
private void ADD_CYCLE(int V, ref int exec_cycles)
|
||||||
{
|
{
|
||||||
exec_cycles += V;
|
exec_cycles += V;
|
||||||
}
|
}
|
||||||
|
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
|
||||||
private void MR_ZX(ref byte DT, ref ushort EA, ref R6502 R)
|
private void MR_ZX(ref byte DT, ref ushort EA, ref R6502 R)
|
||||||
{
|
{
|
||||||
DT = OP6502(R.PC++);
|
DT = OP6502(R.PC++);
|
||||||
EA = (ushort)(DT + R.X);
|
EA = (ushort)(DT + R.X);
|
||||||
DT = ZPRD(ref EA);
|
DT = ZPRD(ref EA);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void MR_AB(ref ushort EA, ref byte DT, ref R6502 R)
|
||||||
|
{
|
||||||
|
EA = OP6502W(R.PC);
|
||||||
|
R.PC += 2;
|
||||||
|
DT = RD6502(EA);
|
||||||
|
}
|
||||||
|
|
||||||
|
internal void ClrIRQ(byte mask)
|
||||||
|
{
|
||||||
|
byte temp = (byte)~mask;
|
||||||
|
R.Int_Pending &= temp;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public enum StatusFlag6502 : int
|
public enum StatusFlag6502 : int
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
using System;
|
using Codice.CM.Client.Differences;
|
||||||
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
@ -11,8 +12,34 @@ namespace VirtualNes
|
|||||||
// CPU 儊儌儕僶儞僋
|
// CPU 儊儌儕僶儞僋
|
||||||
public static byte[][] CPU_MEM_BANK = new byte[8][]; // 8K扨埵
|
public static byte[][] CPU_MEM_BANK = new byte[8][]; // 8K扨埵
|
||||||
|
|
||||||
|
// PPU 儊儌儕僶儞僋
|
||||||
|
public static byte[][] PPU_MEM_BANK = new byte[12][]; // 1K扨埵
|
||||||
|
public static byte[] PPU_MEM_TYPE = new byte[12];
|
||||||
|
public static int[] PPU_MEM_PAGE = new int[12]; // 僗僥乕僩僙乕僽梡
|
||||||
|
public static byte[] CRAM_USED = new byte[16]; // 僗僥乕僩僙乕僽梡
|
||||||
|
|
||||||
// NES儊儌儕
|
// NES儊儌儕
|
||||||
public static byte[] RAM = new byte[8 * 1024]; // NES撪憻RAM
|
public static byte[] RAM = new byte[8 * 1024]; // NES撪憻RAM
|
||||||
public static byte[] WARM = new byte[128 * 1024]; // 儚乕僋/僶僢僋傾僢僾RAM
|
public static byte[] WARM = new byte[128 * 1024]; // 儚乕僋/僶僢僋傾僢僾RAM
|
||||||
|
public static byte[] DRAM = new byte[40 * 1024]; // 僨傿僗僋僔僗僥儉RAM
|
||||||
|
public static byte[] XRAM = new byte[8 * 1024]; // 僟儈乕僶儞僋
|
||||||
|
public static byte[] ERAM = new byte[32 * 1024]; // 奼挘婡婍梡RAM
|
||||||
|
|
||||||
|
public static byte[] CRAM = new byte[32 * 1024]; // 僉儍儔僋僞僷僞乕儞RAM
|
||||||
|
public static byte[] VRAM = new byte[4 * 1024]; // 僱乕儉僥乕僽儖/傾僩儕價儏乕僩RAM
|
||||||
|
|
||||||
|
public static byte[] SPRAM = new byte[0x100]; // 僗僾儔僀僩RAM
|
||||||
|
public static byte[] BGPAL = new byte[0x10]; // BG僷儗僢僩
|
||||||
|
public static byte[] SPPAL = new byte[0x10]; // SP僷儗僢僩
|
||||||
|
// 儗僕僗僞
|
||||||
|
public static byte[] CPUREG = new byte[0x18]; // Nes $4000-$4017
|
||||||
|
public static byte[] PPUREG = new byte[0x04]; // Nes $2000-$2003
|
||||||
|
|
||||||
|
// PPU撪晹儗僕僗僞
|
||||||
|
public static byte PPU56Toggle; // $2005-$2006 Toggle
|
||||||
|
public static byte PPU7_Temp; // $2007 read buffer
|
||||||
|
public static ushort loopy_t; // same as $2005/$2006
|
||||||
|
public static ushort loopy_v; // same as $2005/$2006
|
||||||
|
public static ushort loopy_x; // tile x offset
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
using System;
|
using Codice.CM.Client.Differences;
|
||||||
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
@ -8,6 +9,85 @@ namespace VirtualNes.Core
|
|||||||
{
|
{
|
||||||
public abstract class Mapper
|
public abstract class Mapper
|
||||||
{
|
{
|
||||||
internal virtual void Clock(int cycles) { }
|
protected NES nes;
|
||||||
|
public Mapper(NES parent)
|
||||||
|
{
|
||||||
|
nes = parent;
|
||||||
|
}
|
||||||
|
|
||||||
|
public virtual void Dispose() { }
|
||||||
|
|
||||||
|
public abstract void Reset();
|
||||||
|
|
||||||
|
// $8000-$FFFF Memory write
|
||||||
|
public virtual void Write(ushort addr, byte data) { }
|
||||||
|
// $8000-$FFFF Memory read(Dummy)
|
||||||
|
public virtual void Read(ushort addr, byte data) { }
|
||||||
|
|
||||||
|
// $4100-$7FFF Lower Memory read/write
|
||||||
|
public virtual byte ReadLow(ushort addr)
|
||||||
|
{
|
||||||
|
// $6000-$7FFF WRAM
|
||||||
|
if (addr >= 0x6000 && addr <= 0x7FFF)
|
||||||
|
{
|
||||||
|
return MMU.CPU_MEM_BANK[addr >> 13][addr & 0x1FFF];
|
||||||
|
}
|
||||||
|
|
||||||
|
return (byte)(addr >> 8);
|
||||||
|
}
|
||||||
|
public virtual void WriteLow(ushort addr, byte data)
|
||||||
|
{
|
||||||
|
if (addr >= 0x6000 && addr <= 0x7FFF)
|
||||||
|
{
|
||||||
|
MMU.CPU_MEM_BANK[addr >> 13][addr & 0x1FFF] = data;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// $4018-$40FF Extention register read/write
|
||||||
|
public virtual byte ExRead(ushort addr) { return 0x00; }
|
||||||
|
public virtual void ExWrite(ushort addr, byte data) { }
|
||||||
|
|
||||||
|
public virtual byte ExCmdRead(EXCMDRD cmd) { return 0x00; }
|
||||||
|
public virtual void ExCmdWrite(EXCMDWR cmd, byte data) { }
|
||||||
|
|
||||||
|
// H sync/V sync/Clock sync
|
||||||
|
public virtual void HSync(int scanline) { }
|
||||||
|
public virtual void VSync() { }
|
||||||
|
public virtual void Clock(int cycles) { }
|
||||||
|
// PPU address bus latch
|
||||||
|
public virtual void PPU_Latch(ushort addr) { }
|
||||||
|
// PPU Character latch
|
||||||
|
public virtual void PPU_ChrLatch(ushort addr) { }
|
||||||
|
// PPU Extension character/palette
|
||||||
|
public virtual void PPU_ExtLatchX(int x) { }
|
||||||
|
public virtual void PPU_ExtLatch(ushort addr, ref byte chr_l, ref byte chr_h, ref byte attr) { }
|
||||||
|
// For State save
|
||||||
|
public virtual bool IsStateSave() { return false; }
|
||||||
|
public virtual void SaveState(byte[] p) { }
|
||||||
|
public virtual void LoadState(byte[] p) { }
|
||||||
|
|
||||||
|
// Extension commands
|
||||||
|
// For ExCmdRead command
|
||||||
|
public enum EXCMDRD
|
||||||
|
{
|
||||||
|
EXCMDRD_NONE = 0,
|
||||||
|
EXCMDRD_DISKACCESS,
|
||||||
|
}
|
||||||
|
// For ExCmdWrite command
|
||||||
|
public enum EXCMDWR
|
||||||
|
{
|
||||||
|
EXCMDWR_NONE = 0,
|
||||||
|
EXCMDWR_DISKINSERT,
|
||||||
|
EXCMDWR_DISKEJECT,
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Mapper CreateMapper(NES parent, int no)
|
||||||
|
{
|
||||||
|
switch (no)
|
||||||
|
{
|
||||||
|
default:
|
||||||
|
throw new NotImplementedException($"Mapper#{no} is not Impl");
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -358,6 +358,135 @@ namespace VirtualNes.Core
|
|||||||
{
|
{
|
||||||
//todo : 实现Tape (目测是记录玩家操作再Play,优先级很低)
|
//todo : 实现Tape (目测是记录玩家操作再Play,优先级很低)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
internal byte Read(ushort addr)
|
||||||
|
{
|
||||||
|
switch (addr >> 13)
|
||||||
|
{
|
||||||
|
case 0x00: // $0000-$1FFF
|
||||||
|
return MMU.RAM[addr & 0x07FF];
|
||||||
|
case 0x01: // $2000-$3FFF
|
||||||
|
return ppu.Read((ushort)(addr & 0xE007));
|
||||||
|
case 0x02: // $4000-$5FFF
|
||||||
|
if (addr < 0x4100)
|
||||||
|
{
|
||||||
|
return ReadReg(addr);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return mapper.ReadLow(addr);
|
||||||
|
}
|
||||||
|
case 0x03: // $6000-$7FFF
|
||||||
|
return mapper.ReadLow(addr);
|
||||||
|
case 0x04: // $8000-$9FFF
|
||||||
|
case 0x05: // $A000-$BFFF
|
||||||
|
case 0x06: // $C000-$DFFF
|
||||||
|
case 0x07: // $E000-$FFFF
|
||||||
|
return MMU.CPU_MEM_BANK[addr >> 13][addr & 0x1FFF];
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0x00; // Warning—\–h
|
||||||
|
}
|
||||||
|
|
||||||
|
private byte ReadReg(ushort addr)
|
||||||
|
{
|
||||||
|
switch (addr & 0xFF)
|
||||||
|
{
|
||||||
|
case 0x00:
|
||||||
|
case 0x01:
|
||||||
|
case 0x02:
|
||||||
|
case 0x03:
|
||||||
|
case 0x04:
|
||||||
|
case 0x05:
|
||||||
|
case 0x06:
|
||||||
|
case 0x07:
|
||||||
|
case 0x08:
|
||||||
|
case 0x09:
|
||||||
|
case 0x0A:
|
||||||
|
case 0x0B:
|
||||||
|
case 0x0C:
|
||||||
|
case 0x0D:
|
||||||
|
case 0x0E:
|
||||||
|
case 0x0F:
|
||||||
|
case 0x10:
|
||||||
|
case 0x11:
|
||||||
|
case 0x12:
|
||||||
|
case 0x13:
|
||||||
|
return apu.Read(addr);
|
||||||
|
case 0x15:
|
||||||
|
return apu.Read(addr);
|
||||||
|
case 0x14:
|
||||||
|
return (byte)(addr & 0xFF);
|
||||||
|
case 0x16:
|
||||||
|
if (rom.IsVSUNISYSTEM())
|
||||||
|
{
|
||||||
|
return pad.Read(addr);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return (byte)(pad.Read(addr) | 0x40 | m_TapeOut);
|
||||||
|
}
|
||||||
|
case 0x17:
|
||||||
|
if (rom.IsVSUNISYSTEM())
|
||||||
|
{
|
||||||
|
return pad.Read(addr);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return (byte)(pad.Read(addr) | apu.Read(addr));
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
return mapper.ExRead(addr);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
internal byte Barcode2()
|
||||||
|
{
|
||||||
|
byte ret = 0x00;
|
||||||
|
|
||||||
|
if (!m_bBarcode2 || m_Barcode2seq < 0)
|
||||||
|
return ret;
|
||||||
|
|
||||||
|
switch (m_Barcode2seq)
|
||||||
|
{
|
||||||
|
case 0:
|
||||||
|
m_Barcode2seq++;
|
||||||
|
m_Barcode2ptr = 0;
|
||||||
|
ret = 0x04; // d3
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 1:
|
||||||
|
m_Barcode2seq++;
|
||||||
|
m_Barcode2bit = m_Barcode2data[m_Barcode2ptr];
|
||||||
|
m_Barcode2cnt = 0;
|
||||||
|
ret = 0x04; // d3
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 2:
|
||||||
|
ret = (byte)((m_Barcode2bit & 0x01) != 0 ? 0x00 : 0x04); // Bit rev.
|
||||||
|
m_Barcode2bit >>= 1;
|
||||||
|
if (++m_Barcode2cnt > 7)
|
||||||
|
{
|
||||||
|
m_Barcode2seq++;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case 3:
|
||||||
|
if (++m_Barcode2ptr > 19)
|
||||||
|
{
|
||||||
|
m_bBarcode2 = false;
|
||||||
|
m_Barcode2seq = -1;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
m_Barcode2seq = 1;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -4,18 +4,21 @@ namespace VirtualNes.Core
|
|||||||
{
|
{
|
||||||
public class PAD
|
public class PAD
|
||||||
{
|
{
|
||||||
protected NES nes;
|
private NES nes;
|
||||||
protected int excontroller_select;
|
private int excontroller_select;
|
||||||
protected EXPAD expad;
|
private EXPAD expad;
|
||||||
protected bool bStrobe;
|
private bool bStrobe;
|
||||||
protected bool bSwapButton;
|
private bool bSwapButton;
|
||||||
protected bool bSwapPlayer;
|
private bool bSwapPlayer;
|
||||||
protected bool bZapperMode;
|
private bool bZapperMode;
|
||||||
protected VSType nVSSwapType;
|
private VSType nVSSwapType;
|
||||||
protected byte[] padbit = new byte[4];
|
private byte[] padbit = new byte[4];
|
||||||
protected byte micbit;
|
private byte micbit;
|
||||||
protected byte[] padbitsync = new byte[4];
|
private byte[] padbitsync = new byte[4];
|
||||||
protected byte micbitsync;
|
private byte micbitsync;
|
||||||
|
private bool bBarcodeWorld;
|
||||||
|
|
||||||
|
public uint pad1bit, pad2bit, pad3bit, pad4bit;
|
||||||
|
|
||||||
public PAD(NES parent)
|
public PAD(NES parent)
|
||||||
{
|
{
|
||||||
@ -35,9 +38,47 @@ namespace VirtualNes.Core
|
|||||||
micbitsync = 0;
|
micbitsync = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Dispose()
|
internal byte Read(ushort addr)
|
||||||
{
|
{
|
||||||
|
byte data = 0x00;
|
||||||
|
|
||||||
|
if (addr == 0x4016)
|
||||||
|
{
|
||||||
|
data = (byte)(pad1bit & 1);
|
||||||
|
pad1bit >>= 1;
|
||||||
|
data |= (byte)(((pad3bit & 1)) << 1);
|
||||||
|
pad3bit >>= 1;
|
||||||
|
// Mic
|
||||||
|
if (!nes.rom.IsVSUNISYSTEM())
|
||||||
|
{
|
||||||
|
data |= micbitsync;
|
||||||
}
|
}
|
||||||
|
if (expad != null)
|
||||||
|
{
|
||||||
|
data |= expad.Read4016();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (addr == 0x4017)
|
||||||
|
{
|
||||||
|
data = (byte)(pad2bit & 1);
|
||||||
|
pad2bit >>= 1;
|
||||||
|
data |= (byte)((pad4bit & 1) << 1);
|
||||||
|
pad4bit >>= 1;
|
||||||
|
|
||||||
|
if (expad != null)
|
||||||
|
{
|
||||||
|
data |= expad.Read4017();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (bBarcodeWorld)
|
||||||
|
{
|
||||||
|
data |= nes.Barcode2();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
public void Dispose() { }
|
||||||
}
|
}
|
||||||
|
|
||||||
public enum VSType
|
public enum VSType
|
||||||
|
@ -1,9 +1,39 @@
|
|||||||
using System;
|
using Codice.CM.Client.Differences;
|
||||||
|
using System;
|
||||||
|
|
||||||
namespace VirtualNes.Core
|
namespace VirtualNes.Core
|
||||||
{
|
{
|
||||||
public class PPU
|
public class PPU
|
||||||
{
|
{
|
||||||
|
// PPU Control Register #1 PPU #0
|
||||||
|
public const byte PPU_VBLANK_BIT = 0x80;
|
||||||
|
public const byte PPU_SPHIT_BIT = 0x40; // 堘偆丠
|
||||||
|
public const byte PPU_SP16_BIT = 0x20;
|
||||||
|
public const byte PPU_BGTBL_BIT = 0x10;
|
||||||
|
public const byte PPU_SPTBL_BIT = 0x08;
|
||||||
|
public const byte PPU_INC32_BIT = 0x04;
|
||||||
|
public const byte PPU_NAMETBL_BIT = 0x03;
|
||||||
|
|
||||||
|
// PPU Control Register #2 PPU #1
|
||||||
|
public const byte PPU_BGCOLOR_BIT = 0xE0;
|
||||||
|
public const byte PPU_SPDISP_BIT = 0x10;
|
||||||
|
public const byte PPU_BGDISP_BIT = 0x08;
|
||||||
|
public const byte PPU_SPCLIP_BIT = 0x04;
|
||||||
|
public const byte PPU_BGCLIP_BIT = 0x02;
|
||||||
|
public const byte PPU_COLORMODE_BIT = 0x01;
|
||||||
|
|
||||||
|
// PPU Status Register PPU #2
|
||||||
|
public const byte PPU_VBLANK_FLAG = 0x80;
|
||||||
|
public const byte PPU_SPHIT_FLAG = 0x40;
|
||||||
|
public const byte PPU_SPMAX_FLAG = 0x20;
|
||||||
|
public const byte PPU_WENABLE_FLAG = 0x10;
|
||||||
|
|
||||||
|
// SPRITE Attribute
|
||||||
|
public const byte SP_VMIRROR_BIT = 0x80;
|
||||||
|
public const byte SP_HMIRROR_BIT = 0x40;
|
||||||
|
public const byte SP_PRIORITY_BIT = 0x20;
|
||||||
|
public const byte SP_COLOR_BIT = 0x03;
|
||||||
|
|
||||||
private NES m_nes;
|
private NES m_nes;
|
||||||
private byte[] lpScreen;
|
private byte[] lpScreen;
|
||||||
private byte[] lpColormode;
|
private byte[] lpColormode;
|
||||||
@ -42,6 +72,59 @@ namespace VirtualNes.Core
|
|||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
internal byte Read(ushort addr)
|
||||||
|
{
|
||||||
|
byte data = 0x00;
|
||||||
|
|
||||||
|
switch (addr)
|
||||||
|
{
|
||||||
|
// Write only Register
|
||||||
|
case 0x2000: // PPU Control Register #1(W)
|
||||||
|
case 0x2001: // PPU Control Register #2(W)
|
||||||
|
case 0x2003: // SPR-RAM Address Register(W)
|
||||||
|
case 0x2005: // PPU Scroll Register(W2)
|
||||||
|
case 0x2006: // VRAM Address Register(W2)
|
||||||
|
data = MMU.PPU7_Temp; // 懡暘
|
||||||
|
break;
|
||||||
|
// Read/Write Register
|
||||||
|
case 0x2002: // PPU Status Register(R)
|
||||||
|
//DEBUGOUT( "2002 RD L:%3d C:%8d\n", ScanlineNo, nes->cpu->GetTotalCycles() );
|
||||||
|
data = (byte)(MMU.PPUREG[2] | VSSecurityData);
|
||||||
|
MMU.PPU56Toggle = 0;
|
||||||
|
byte temp = unchecked((byte)~PPU_VBLANK_FLAG);
|
||||||
|
MMU.PPUREG[2] &= temp;
|
||||||
|
break;
|
||||||
|
case 0x2004: // SPR_RAM I/O Register(RW)
|
||||||
|
data = MMU.SPRAM[MMU.PPUREG[3]++];
|
||||||
|
break;
|
||||||
|
case 0x2007: // VRAM I/O Register(RW)
|
||||||
|
addr = (ushort)(MMU.loopy_v & 0x3FFF);
|
||||||
|
data = MMU.PPU7_Temp;
|
||||||
|
if ((MMU.PPUREG[0] & PPU_INC32_BIT) != 0) MMU.loopy_v += 32;
|
||||||
|
else MMU.loopy_v++;
|
||||||
|
if (addr >= 0x3000)
|
||||||
|
{
|
||||||
|
if (addr >= 0x3F00)
|
||||||
|
{
|
||||||
|
// data &= 0x3F;
|
||||||
|
if ((addr & 0x0010) == 0)
|
||||||
|
{
|
||||||
|
return MMU.BGPAL[addr & 0x000F];
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return MMU.SPPAL[addr & 0x000F];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
addr &= 0xEFFF;
|
||||||
|
}
|
||||||
|
MMU.PPU7_Temp = MMU.PPU_MEM_BANK[addr >> 10][addr & 0x03FF];
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
|
||||||
internal void SetRenderScanline(int scanline)
|
internal void SetRenderScanline(int scanline)
|
||||||
{
|
{
|
||||||
ScanlineNo = scanline;
|
ScanlineNo = scanline;
|
||||||
|
@ -1,3 +1,4 @@
|
|||||||
|
using System;
|
||||||
using System.Collections;
|
using System.Collections;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using UnityEngine;
|
using UnityEngine;
|
||||||
@ -6,5 +7,23 @@ namespace VirtualNes.Core
|
|||||||
{
|
{
|
||||||
public class EXPAD
|
public class EXPAD
|
||||||
{
|
{
|
||||||
|
protected NES nes;
|
||||||
|
|
||||||
|
public EXPAD(NES parent)
|
||||||
|
{
|
||||||
|
nes = parent;
|
||||||
|
}
|
||||||
|
|
||||||
|
public virtual void Dispose() { }
|
||||||
|
|
||||||
|
public virtual void Reset() { }
|
||||||
|
public virtual void Strobe() { }
|
||||||
|
public virtual byte Read4016() { return 0x00; }
|
||||||
|
public virtual byte Read4017() { return 0x00; }
|
||||||
|
public virtual void Write4016(byte data) { }
|
||||||
|
public virtual void Write4017(byte data) { }
|
||||||
|
public virtual void Sync() { }
|
||||||
|
public virtual void SetSyncData(int type,int data) { }
|
||||||
|
public virtual int GetSyncData(int type) { return 0x00; }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -350,6 +350,11 @@ namespace VirtualNes.Core
|
|||||||
{
|
{
|
||||||
return fdsmakerID;
|
return fdsmakerID;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
internal bool IsVSUNISYSTEM()
|
||||||
|
{
|
||||||
|
return (header.control2 & (byte)EnumRomControlByte2.ROM_VSUNISYSTEM) != 0;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user