AxibugEmuOnline/AxibugEmuOnline.Client/Assets/VirtualNes.Core/PAD.cs

551 lines
20 KiB
C#
Raw Normal View History

2024-08-16 10:20:00 +08:00
using System.Collections.Generic;
2024-07-25 11:03:58 +08:00
namespace VirtualNes.Core
2024-07-23 18:31:59 +08:00
{
public class PAD
{
2024-07-25 14:03:52 +08:00
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;
2024-07-26 17:52:33 +08:00
private int[][] padcnt = new int[4][]
{
new int[2],new int[2],new int[2],new int[2],
};
2024-07-25 14:03:52 +08:00
public uint pad1bit, pad2bit, pad3bit, pad4bit;
2024-07-23 18:31:59 +08:00
2024-08-06 18:09:32 +08:00
private static int[] ren10fps = new int[6] { 1, 1, 1, 0, 0, 0 };
private static int[] ren15fps = new int[4] { 1, 1, 0, 0 };
private static int[] ren20fps = new int[3] { 1, 1, 0 };
private static int[] ren30fps = new int[2] { 1, 0 };
private static int[] renmask = new int[4] { 6, 4, 3, 2 };
public static Dictionary<int, int[]> rentbl = new Dictionary<int, int[]>()
{
{0,ren10fps },
{1,ren15fps },
{2,ren20fps },
{3,ren30fps },
};
2024-07-23 18:31:59 +08:00
public PAD(NES parent)
{
nes = parent;
excontroller_select = 0;
expad = null;
bStrobe = false;
bSwapButton = false;
bSwapPlayer = false;
bZapperMode = false;
nVSSwapType = VSType.VS_TYPE0;
padbit[0] = padbit[1] = padbit[2] = padbit[3] = 0;
micbit = 0;
padbitsync[0] = padbitsync[1] = padbitsync[2] = padbitsync[3] = 0;
micbitsync = 0;
}
2024-07-25 11:03:58 +08:00
2024-07-25 14:03:52 +08:00
internal byte Read(ushort addr)
2024-07-25 11:03:58 +08:00
{
2024-07-25 14:03:52 +08:00
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;
2024-07-25 11:03:58 +08:00
}
2024-07-25 14:03:52 +08:00
public void Dispose() { }
2024-07-26 17:52:33 +08:00
internal void Write(ushort addr, byte data)
{
if (addr == 0x4016)
{
if ((data & 0x01) != 0)
{
bStrobe = true;
}
else if (bStrobe)
{
bStrobe = false;
Strobe();
if (expad != null)
{
expad.Strobe();
}
}
if (expad != null)
{
expad.Write4016(data);
}
}
if (addr == 0x4017)
{
if (expad != null)
{
expad.Write4017(data);
}
}
}
private void Strobe()
{
// For VS-Unisystem
if (nes.rom.IsVSUNISYSTEM())
{
uint pad1 = (uint)(padbitsync[0] & 0xF3);
uint pad2 = (uint)(padbitsync[1] & 0xF3);
uint st1 = (uint)(padbitsync[0] & 0x08) >> 3;
uint st2 = (uint)(padbitsync[1] & 0x08) >> 3;
switch (nVSSwapType)
{
case VSType.VS_TYPE0:
pad1bit = pad1 | (st1 << 2);
pad2bit = pad2 | (st2 << 2);
break;
case VSType.VS_TYPE1:
pad1bit = pad2 | (st1 << 2);
pad2bit = pad1 | (st2 << 2);
break;
case VSType.VS_TYPE2:
pad1bit = pad1 | (st1 << 2) | (st2 << 3);
pad2bit = pad2;
break;
case VSType.VS_TYPE3:
pad1bit = pad2 | (st1 << 2) | (st2 << 3);
pad2bit = pad1;
break;
case VSType.VS_TYPE4:
pad1bit = pad1 | (st1 << 2) | 0x08; // 0x08=Start Protect
pad2bit = pad2 | (st2 << 2) | 0x08; // 0x08=Start Protect
break;
case VSType.VS_TYPE5:
pad1bit = pad2 | (st1 << 2) | 0x08; // 0x08=Start Protect
pad2bit = pad1 | (st2 << 2) | 0x08; // 0x08=Start Protect
break;
case VSType.VS_TYPE6:
pad1bit = pad1 | (st1 << 2) | (((uint)padbitsync[0] & 0x04) << 1);
pad2bit = pad2 | (st2 << 2) | (((uint)padbitsync[1] & 0x04) << 1);
break;
case VSType.VS_TYPEZ:
pad1bit = 0;
pad2bit = 0;
break;
}
// Coin 2偲旐傞堊偵徚偡
micbit = 0;
}
else
{
if (Supporter.Config.emulator.bFourPlayer)
{
// NES type
pad1bit = padbitsync[0] | ((uint)padbitsync[2] << 8) | 0x00080000;
pad2bit = padbitsync[1] | ((uint)padbitsync[3] << 8) | 0x00040000;
}
else
{
// Famicom type
pad1bit = padbitsync[0];
pad2bit = padbitsync[1];
}
}
pad3bit = padbitsync[2];
pad4bit = padbitsync[3];
}
internal void Reset()
{
pad1bit = pad2bit = 0;
bStrobe = false;
bBarcodeWorld = false;
for (int x = 0; x < 4; x++)
{
for (int y = 0; y < 2; y++)
{
padcnt[x][y] = 0;
}
}
// Select Extension Devices
uint crc = nes.rom.GetPROM_CRC();
if (crc == 0xfbfc6a6c // Adventures of Bayou Billy, The(E)
|| crc == 0xcb275051 // Adventures of Bayou Billy, The(U)
|| crc == 0xfb69c131 // Baby Boomer(Unl)(U)
|| crc == 0xf2641ad0 // Barker Bill's Trick Shooting(U)
|| crc == 0xbc1dce96 // Chiller (Unl)(U)
|| crc == 0x90ca616d // Duck Hunt(JUE)
|| crc == 0x59e3343f // Freedom Force(U)
|| crc == 0x242a270c // Gotcha!(U)
|| crc == 0x7b5bd2de // Gumshoe(UE)
|| crc == 0x255b129c // Gun Sight(J)
|| crc == 0x8963ae6e // Hogan's Alley(JU)
|| crc == 0x51d2112f // Laser Invasion(U)
|| crc == 0x0a866c94 // Lone Ranger, The(U)
// || crc == 0xe4c04eea // Mad City(J)
|| crc == 0x9eef47aa // Mechanized Attack(U)
|| crc == 0xc2db7551 // Shooting Range(U)
|| crc == 0x163e86c0 // To The Earth(U)
|| crc == 0x42d893e4 // Operation Wolf(J)
|| crc == 0x1388aeb9 // Operation Wolf(U)
|| crc == 0x0d3cf705 // Wild Gunman(J)
|| crc == 0x389960db)
{ // Wild Gunman(JUE)
SetExController(EXCONTROLLER.EXCONTROLLER_ZAPPER);
}
if (crc == 0x35893b67 // Arkanoid(J)
|| crc == 0x6267fbd1)
{ // Arkanoid 2(J)
SetExController(EXCONTROLLER.EXCONTROLLER_PADDLE);
}
if (crc == 0xff6621ce // Hyper Olympic(J)
|| crc == 0xdb9418e8 // Hyper Olympic(Tonosama Ban)(J)
|| crc == 0xac98cd70)
{ // Hyper Sports(J)
SetExController(EXCONTROLLER.EXCONTROLLER_HYPERSHOT);
}
if (crc == 0xf9def527 // Family BASIC(Ver2.0)
|| crc == 0xde34526e // Family BASIC(Ver2.1a)
|| crc == 0xf050b611 // Family BASIC(Ver3)
|| crc == 0x3aaeed3f // Family BASIC(Ver3)(Alt)
|| crc == 0x868FCD89 // Family BASIC(Ver1.0)
|| crc == 0x2D6B7E5A // PLAYBOX BASIC(J) (Prototype_v0.0)
|| crc == 0xDA03D908)
{ // PLAYBOX BASIC (J)
SetExController(EXCONTROLLER.EXCONTROLLER_KEYBOARD);
}
if (crc == 0x589b6b0d // Supor Computer V3.0
|| crc == 0x8b265862 // Supor English
|| crc == 0x41401c6d // Supor Computer V4.0
|| crc == 0x82F1Fb96 // Supor Computer(Russia) V1.0
|| crc == 0xd5d6eac4)
{ // EDU(C) Computer
SetExController(EXCONTROLLER.EXCONTROLLER_SUPOR_KEYBOARD);
nes.SetVideoMode(true);
}
if (crc == 0xc68363f6 // Crazy Climber(J)
|| crc == 0x2989ead6 // Smash TV(U) [!]
|| crc == 0x0b8f8128)
{ // Smash TV(E) [!]
SetExController(EXCONTROLLER.EXCONTROLLER_CRAZYCLIMBER);
}
if (crc == 0x20d22251)
{ // Top rider(J)
SetExController(EXCONTROLLER.EXCONTROLLER_TOPRIDER);
}
if (crc == 0x0cd00488)
{ // Space Shadow(J)
SetExController(EXCONTROLLER.EXCONTROLLER_SPACESHADOWGUN);
}
if (crc == 0x8c8fa83b // Family Trainer - Athletic World (J)
|| crc == 0x7e704a14 // Family Trainer - Jogging Race (J)
|| crc == 0x2330a5d3)
{ // Family Trainer - Rairai Kyonshiizu (J)
SetExController(EXCONTROLLER.EXCONTROLLER_FAMILYTRAINER_A);
}
if (crc == 0xf8da2506 // Family Trainer - Aerobics Studio (J)
|| crc == 0xca26a0f1 // Family Trainer - Dai Undoukai (J)
|| crc == 0x28068b8c // Family Trainer - Fuuun Takeshi Jou 2 (J)
|| crc == 0x10bb8f9a // Family Trainer - Manhattan Police (J)
|| crc == 0xad3df455 // Family Trainer - Meiro Dai Sakusen (J)
|| crc == 0x8a5b72c0 // Family Trainer - Running Stadium (J)
|| crc == 0x59794f2d)
{ // Family Trainer - Totsugeki Fuuun Takeshi Jou (J)
SetExController(EXCONTROLLER.EXCONTROLLER_FAMILYTRAINER_B);
}
if (crc == 0x9fae4d46 // Ide Yousuke Meijin no Jissen Mahjong (J)
|| crc == 0x7b44fb2a)
{ // Ide Yousuke Meijin no Jissen Mahjong 2 (J)
SetExController(EXCONTROLLER.EXCONTROLLER_MAHJANG);
}
if (crc == 0x786148b6)
{ // Exciting Boxing (J)
SetExController(EXCONTROLLER.EXCONTROLLER_EXCITINGBOXING);
}
if (crc == 0xc3c0811d // Oeka Kids - Anpanman no Hiragana Daisuki (J)
|| crc == 0x9d048ea4)
{ // Oeka Kids - Anpanman to Oekaki Shiyou!! (J)
SetExController(EXCONTROLLER.EXCONTROLLER_OEKAKIDS_TABLET);
}
if (crc == 0x67898319)
{ // Barcode World (J)
bBarcodeWorld = true;
}
// VS-Unisystem
if (nes.rom.IsVSUNISYSTEM())
{
if (crc == 0xff5135a3 // VS Hogan's Alley
|| crc == 0xed588f00 // VS Duck Hunt
|| crc == 0x17ae56be)
{ // VS Freedom Force
SetExController(EXCONTROLLER.EXCONTROLLER_VSZAPPER);
}
else
{
SetExController(EXCONTROLLER.EXCONTROLLER_VSUNISYSTEM);
}
}
if (crc == 0x21b099f3)
{ // Gyromite (JUE)
SetExController(EXCONTROLLER.EXCONTROLLER_GYROMITE);
}
}
2024-07-30 18:53:36 +08:00
internal void SetExController(EXCONTROLLER type)
2024-07-26 17:52:33 +08:00
{
excontroller_select = (int)type;
expad?.Dispose();
expad = null;
bZapperMode = false;
// ExPad Instance create
switch (type)
{
case EXCONTROLLER.EXCONTROLLER_ZAPPER:
expad = new EXPAD_Zapper(nes);
bZapperMode = true;
break;
case EXCONTROLLER.EXCONTROLLER_PADDLE:
expad = new EXPAD_Paddle(nes);
break;
case EXCONTROLLER.EXCONTROLLER_HYPERSHOT:
expad = new EXPAD_HyperShot(nes);
break;
case EXCONTROLLER.EXCONTROLLER_KEYBOARD:
expad = new EXPAD_Keyboard(nes);
break;
case EXCONTROLLER.EXCONTROLLER_SUPOR_KEYBOARD:
expad = new EXPAD_Supor_Keyboard(nes);
break;
case EXCONTROLLER.EXCONTROLLER_CRAZYCLIMBER:
expad = new EXPAD_CrazyClimber(nes);
break;
case EXCONTROLLER.EXCONTROLLER_TOPRIDER:
expad = new EXPAD_Toprider(nes);
break;
case EXCONTROLLER.EXCONTROLLER_SPACESHADOWGUN:
expad = new EXPAD_SpaceShadowGun(nes);
bZapperMode = true;
break;
case EXCONTROLLER.EXCONTROLLER_FAMILYTRAINER_A:
case EXCONTROLLER.EXCONTROLLER_FAMILYTRAINER_B:
expad = new EXPAD_FamlyTrainer(nes);
break;
case EXCONTROLLER.EXCONTROLLER_EXCITINGBOXING:
expad = new EXPAD_ExcitingBoxing(nes);
break;
case EXCONTROLLER.EXCONTROLLER_MAHJANG:
expad = new EXPAD_Mahjang(nes);
break;
case EXCONTROLLER.EXCONTROLLER_OEKAKIDS_TABLET:
expad = new EXPAD_OekakidsTablet(nes);
break;
case EXCONTROLLER.EXCONTROLLER_TURBOFILE:
expad = new EXPAD_TurboFile(nes);
break;
case EXCONTROLLER.EXCONTROLLER_VSUNISYSTEM:
expad = new EXPAD_VSUnisystem(nes);
break;
case EXCONTROLLER.EXCONTROLLER_VSZAPPER:
expad = new EXPAD_VSZapper(nes);
bZapperMode = true;
break;
case EXCONTROLLER.EXCONTROLLER_GYROMITE:
expad = new EXPAD_Gyromite(nes);
break;
default:
break;
}
if (expad != null)
{
expad.Reset();
}
}
2024-07-30 11:57:09 +08:00
2024-08-06 18:09:32 +08:00
public void Sync(ControllerState state)
{
padbit[0] = SyncSub(0, state);
padbit[1] = SyncSub(1, state);
padbit[2] = SyncSub(2, state);
padbit[3] = SyncSub(3, state);
// Mic
micbit = 0;
if (state.HasButton(1, EnumButtonType.MIC)) micbit |= 4;
// For Excontroller
if (expad != null)
{
expad.Sync();
}
}
private byte SyncSub(int no, ControllerState state)
{
ushort bit = 0;
2024-08-16 10:20:00 +08:00
2024-08-06 18:09:32 +08:00
// Up
if (state.HasButton(no, EnumButtonType.UP))
bit |= 1 << 4;
// Down
if (state.HasButton(no, EnumButtonType.DOWN))
bit |= 1 << 5;
// Left
if (state.HasButton(no, EnumButtonType.LEFT))
bit |= 1 << 6;
// Right
if (state.HasButton(no, EnumButtonType.RIGHT))
bit |= 1 << 7;
// 同時入力を禁止する
// if( (bit&((1<<4)|(1<<5))) == ((1<<4)|(1<<5)) )
// bit &= ~((1<<4)|(1<<5));
if ((bit & ((1 << 6) | (1 << 7))) == ((1 << 6) | (1 << 7)))
bit = (byte)(bit & ~((1 << 6) | (1 << 7)));
// A
if (state.HasButton(no, EnumButtonType.A)) bit |= 1 << 0;
// B
if (state.HasButton(no, EnumButtonType.B)) bit |= 1 << 1;
// Select
if (state.HasButton(no, EnumButtonType.SELECT)) bit |= 1 << 2;
// Start
if (state.HasButton(no, EnumButtonType.START)) bit |= 1 << 3;
return (byte)(bit & 0xFF);
}
2024-07-30 11:57:09 +08:00
internal bool IsZapperMode()
{
return bZapperMode;
}
internal void VSync()
{
padbitsync[0] = padbit[0];
padbitsync[1] = padbit[1];
padbitsync[2] = padbit[2];
padbitsync[3] = padbit[3];
micbitsync = micbit;
}
internal uint GetSyncData()
{
uint ret;
ret = (uint)(padbit[0] | (padbit[1] << 8) | (padbit[2] << 16) | (padbit[3] << 24));
ret |= (uint)(micbit << 8);
return ret;
}
internal void SetSyncData(uint data)
{
micbit = (byte)((data & 0x00000400) >> 8);
padbit[0] = (byte)data;
padbit[1] = (byte)(data >> 8);
padbit[2] = (byte)(data >> 16);
padbit[3] = (byte)(data >> 24);
}
internal int GetExController()
{
return excontroller_select;
}
2024-07-23 18:31:59 +08:00
}
public enum VSType
{
VS_TYPE0 = 0, // SELECT1P=START1P/SELECT2P=START2P 1P/2P No reverse
VS_TYPE1, // SELECT1P=START1P/SELECT2P=START2P 1P/2P Reverse
VS_TYPE2, // SELECT1P=START1P/START1P =START2P 1P/2P No reverse
VS_TYPE3, // SELECT1P=START1P/START1P =START2P 1P/2P Reverse
VS_TYPE4, // SELECT1P=START1P/SELECT2P=START2P 1P/2P No reverse (Protection)
VS_TYPE5, // SELECT1P=START1P/SELECT2P=START2P 1P/2P Reverse (Protection)
VS_TYPE6, // SELECT1P=START1P/SELECT2P=START2P 1P/2P Reverse (For Golf)
VS_TYPEZ, // ZAPPER
}
2024-07-26 17:52:33 +08:00
public enum EXCONTROLLER
{
EXCONTROLLER_NONE = 0,
EXCONTROLLER_PADDLE,
EXCONTROLLER_HYPERSHOT,
EXCONTROLLER_ZAPPER,
EXCONTROLLER_KEYBOARD,
EXCONTROLLER_CRAZYCLIMBER,
EXCONTROLLER_TOPRIDER,
EXCONTROLLER_SPACESHADOWGUN,
EXCONTROLLER_FAMILYTRAINER_A,
EXCONTROLLER_FAMILYTRAINER_B,
EXCONTROLLER_EXCITINGBOXING,
EXCONTROLLER_MAHJANG,
EXCONTROLLER_OEKAKIDS_TABLET,
EXCONTROLLER_TURBOFILE,
EXCONTROLLER_VSUNISYSTEM,
EXCONTROLLER_VSZAPPER,
EXCONTROLLER_GYROMITE,
EXCONTROLLER_STACKUP,
EXCONTROLLER_SUPOR_KEYBOARD,
}
2024-07-23 18:31:59 +08:00
}