dev_4VirtualNes #18
@ -15,7 +15,7 @@ namespace VirtualNes.Core
|
||||
protected int[] lowpass_filter = new int[4];
|
||||
protected QUEUE queue;
|
||||
protected QUEUE exqueue;
|
||||
protected bool[] m_bMute = new bool[16];
|
||||
protected bool[] m_bMute = new bool[16];
|
||||
|
||||
public APU(NES parent)
|
||||
{
|
||||
@ -43,6 +43,11 @@ namespace VirtualNes.Core
|
||||
{
|
||||
@internal.Sync(cycles);
|
||||
}
|
||||
|
||||
internal byte Read(ushort addr)
|
||||
{
|
||||
return @internal.SyncRead(addr);
|
||||
}
|
||||
}
|
||||
|
||||
public struct QUEUEDATA
|
||||
|
@ -1,11 +1,26 @@
|
||||
using System;
|
||||
|
||||
namespace VirtualNes.Core
|
||||
{
|
||||
public class APU_INTERNAL : APU_INTERFACE
|
||||
{
|
||||
private NES nes;
|
||||
// Frame Counter
|
||||
private int FrameCycle;
|
||||
private int FrameCount;
|
||||
private int FrameType;
|
||||
private byte FrameIRQ;
|
||||
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)
|
||||
{
|
||||
nes = parent;
|
||||
@ -55,5 +70,37 @@ namespace VirtualNes.Core
|
||||
{
|
||||
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:
|
||||
MR_ZX(ref DT, ref EA, ref R); ADC(ref WT, ref DT, ref R);
|
||||
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;
|
||||
}
|
||||
}
|
||||
@ -157,26 +164,51 @@ namespace VirtualNes.Core
|
||||
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)
|
||||
{
|
||||
DT = OP6502(R.PC++);
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
private void MR_ZP(ref ushort EA, ref byte DT, ref R6502 R)
|
||||
{
|
||||
EA = OP6502(R.PC++);
|
||||
DT = ZPRD(ref EA);
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
private byte ZPRD(ref ushort A)
|
||||
{
|
||||
return MMU.RAM[A];
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
private void ADC(ref ushort WT, ref byte DT, ref R6502 R)
|
||||
{
|
||||
WT = (ushort)(R.A + DT + (R.P & C_FLAG));
|
||||
@ -187,7 +219,6 @@ namespace VirtualNes.Core
|
||||
SET_ZN_FLAG(R.A, ref R);
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
private void TST_FLAG(bool F, byte V, ref R6502 R)
|
||||
{
|
||||
byte temp = (byte)~V;
|
||||
@ -196,7 +227,6 @@ namespace VirtualNes.Core
|
||||
if (F) R.P |= V;
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
private void SET_ZN_FLAG(byte A, ref R6502 R)
|
||||
{
|
||||
byte temp = unchecked((byte)(~(Z_FLAG | N_FLAG)));
|
||||
@ -204,19 +234,30 @@ namespace VirtualNes.Core
|
||||
R.P |= ZN_Table[A];
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
private void ADD_CYCLE(int V, ref int exec_cycles)
|
||||
{
|
||||
exec_cycles += V;
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
private void MR_ZX(ref byte DT, ref ushort EA, ref R6502 R)
|
||||
{
|
||||
DT = OP6502(R.PC++);
|
||||
EA = (ushort)(DT + R.X);
|
||||
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
|
||||
|
@ -1,4 +1,5 @@
|
||||
using System;
|
||||
using Codice.CM.Client.Differences;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
@ -11,8 +12,34 @@ namespace VirtualNes
|
||||
// CPU 儊儌儕僶儞僋
|
||||
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儊儌儕
|
||||
public static byte[] RAM = new byte[8 * 1024]; // NES撪憻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.Linq;
|
||||
using System.Text;
|
||||
@ -8,6 +9,85 @@ namespace VirtualNes.Core
|
||||
{
|
||||
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,优先级很低)
|
||||
}
|
||||
|
||||
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
|
||||
{
|
||||
protected NES nes;
|
||||
protected int excontroller_select;
|
||||
protected EXPAD expad;
|
||||
protected bool bStrobe;
|
||||
protected bool bSwapButton;
|
||||
protected bool bSwapPlayer;
|
||||
protected bool bZapperMode;
|
||||
protected VSType nVSSwapType;
|
||||
protected byte[] padbit = new byte[4];
|
||||
protected byte micbit;
|
||||
protected byte[] padbitsync = new byte[4];
|
||||
protected byte micbitsync;
|
||||
private NES nes;
|
||||
private int excontroller_select;
|
||||
private EXPAD expad;
|
||||
private bool bStrobe;
|
||||
private bool bSwapButton;
|
||||
private bool bSwapPlayer;
|
||||
private bool bZapperMode;
|
||||
private VSType nVSSwapType;
|
||||
private byte[] padbit = new byte[4];
|
||||
private byte micbit;
|
||||
private byte[] padbitsync = new byte[4];
|
||||
private byte micbitsync;
|
||||
private bool bBarcodeWorld;
|
||||
|
||||
public uint pad1bit, pad2bit, pad3bit, pad4bit;
|
||||
|
||||
public PAD(NES parent)
|
||||
{
|
||||
@ -35,9 +38,47 @@ namespace VirtualNes.Core
|
||||
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
|
||||
|
@ -1,9 +1,39 @@
|
||||
using System;
|
||||
using Codice.CM.Client.Differences;
|
||||
using System;
|
||||
|
||||
namespace VirtualNes.Core
|
||||
{
|
||||
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 byte[] lpScreen;
|
||||
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)
|
||||
{
|
||||
ScanlineNo = scanline;
|
||||
|
@ -1,3 +1,4 @@
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
@ -6,5 +7,23 @@ namespace VirtualNes.Core
|
||||
{
|
||||
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;
|
||||
}
|
||||
|
||||
internal bool IsVSUNISYSTEM()
|
||||
{
|
||||
return (header.control2 & (byte)EnumRomControlByte2.ROM_VSUNISYSTEM) != 0;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user