diff --git a/SabreTools.Serialization/Models/NES/Cart.cs b/SabreTools.Serialization/Models/NES/Cart.cs new file mode 100644 index 00000000..55b9f78a --- /dev/null +++ b/SabreTools.Serialization/Models/NES/Cart.cs @@ -0,0 +1,47 @@ +namespace SabreTools.Data.Models.NES +{ + /// + /// NES headered cart + /// + /// + /// + public class Cart + { + /// + /// NES 1.0 or 2.0 header + /// + public Header? Header { get; set; } + + /// + /// Trainer, if present (0 or 512 bytes) + /// + public byte[] Trainer { get; set; } = []; + + /// + /// PRG ROM data (16384 * x bytes) + /// + public byte[] PRGROMData { get; set; } = []; + + /// + /// CHR ROM data, if present (8192 * y bytes) + /// + public byte[] CHRROMData { get; set; } = []; + + /// + /// PlayChoice INST-ROM, if present (0 or 8192 bytes) + /// + public byte[] PlayChoiceINSTROM { get; set; } = []; + + /// + /// PlayChoice PROM, if present (16 bytes Data, 16 bytes CounterOut) + /// + /// This is often missing; see PC10 ROM-Images for details + public byte[] PlayChoicePROM { get; set; } = []; + + /// + /// Some ROM-Images additionally contain a 128-byte (or sometimes 127-byte) + /// title at the end of the file. + /// + public byte[] Title { get; set; } = []; + } +} diff --git a/SabreTools.Serialization/Models/NES/Enums.cs b/SabreTools.Serialization/Models/NES/Enums.cs new file mode 100644 index 00000000..b66b9c16 --- /dev/null +++ b/SabreTools.Serialization/Models/NES/Enums.cs @@ -0,0 +1,816 @@ +using System; + +namespace SabreTools.Data.Models.NES +{ + /// + /// Mapper, mirroring, battery, trainer + /// + /// Bits 4-7 are the lower nybble of mapper number + [Flags] + public enum Flag6 : byte + { + #region Bit 0 + + /// + /// Vertical arrangement ("horizontal mirrored") or mapper-controlled + /// + /// CIRAM A10 = PPU A11 + NametableArrangementVertical = 0b00000000, + + /// + /// Horizontal arrangement ("vertically mirrored") + /// + /// CIRAM A10 = PPU A10 + NametableArrangementHorizontal = 0b00000001, + + #endregion + + #region Bit 1 + + /// + /// Cartridge contains battery-backed PRG RAM ($6000-7FFF) + /// or other persistent memory not present + /// + BatteryBackedPRGRAMNotPresent = 0b00000000, + + /// + /// Cartridge contains battery-backed PRG RAM ($6000-7FFF) + /// or other persistent memory present + /// + BatteryBackedPRGRAMPresent = 0b00000010, + + #endregion + + #region Bit 2 + + /// + /// 512-byte trainer at $7000-$71FF + /// + TrainerNotPresent = 0b00000000, + + /// + /// 512-byte trainer at $7000-$71FF + /// + /// Stored before PRG data + TrainerPresent = 0b00000100, + + #endregion + + #region Bit 3 + + /// + /// Alternative nametable layout + /// + AlternativeNametableLayout = 0b00001000, + + #endregion + } + + /// + /// Mapper, VS/Playchoice, NES 2.0 + /// + /// Bits 4-7 are the upper nybble of mapper number + [Flags] + public enum Flag7 : byte + { + #region Bits 0-1 + + /// + /// Nintendo Entertainment System/Family Computer + /// + StandardSystem = 0b00000000, + + /// + /// VS Unisystem + /// + VSUnisystem = 0b00000001, + + /// + /// PlayChoice-10 (8 KB of Hint Screen data stored after CHR data) + /// + PlayChoice10 = 0b00000010, + + /// + /// Extended Console Type + /// + ExtendedConsoleType = 0b00000011, + + #endregion + + #region Bits 2-3 + + /// + /// If equal to 2, flags 8-15 are in NES 2.0 format + /// + NES20 = 0b00001000, + + #endregion + } + + /// + /// TV system (rarely used extension) + /// + [Flags] + public enum TVSystem : byte + { + NTSC = 0b00000000, + PAL = 0b00000001, + } + + /// + /// TV system, PRG-RAM presence (unofficial, rarely used extension) + /// + [Flags] + public enum Flag10 : byte + { + #region Bits 0-1 + + NTSC = 0b00000000, + DualCompatible1 = 0b00000001, + PAL = 0b00000010, + DualCompatible2 = 0b00000011, + + #endregion + + #region Bit 4 + + /// + /// PRG RAM ($6000-$7FFF) present + /// + PRGRAMPresent = 0b00000000, + + /// + /// PRG RAM ($6000-$7FFF) not present + /// + PRGRAMNotPresent = 0b00010000, + + #endregion + + #region Bit 5 + + /// + /// Board has no bus conflicts + /// + BoardHasNoBusConflicts = 0b00000000, + + /// + /// Board has bus conflicts + /// + BoardHasBusConflicts = 0b00100000, + + #endregion + } + + /// + /// CPU/PPU Timing + /// + [Flags] + public enum CPUPPUTiming : byte + { + #region Bits 0-1 + + /// + /// NTSC NES + /// + RP2C02 = 0b00000000, + + /// + /// Licensed PAL NES + /// + RP2C07 = 0b00000001, + + /// + /// Multiple-region + /// + MultipleRegion = 0b00000010, + + /// + /// Dendy + /// + UA6538 = 0b00000011, + + #endregion + } + + /// + /// Vs. System Type and Extended Console Type + /// + [Flags] + public enum ExtendedSystemType : byte + { + #region Bits 0-3 (Vs. System Type) + + /// + /// Any RP2C03/RC2C03 variant + /// + AnyRP2C03RC2C03Variant = 0x00, + + /// + /// Reserved + /// + VsReserved1 = 0x01, + + /// + /// RP2C04-0001 + /// + RP2C040001 = 0x02, + + /// + /// RP2C04-0002 + /// + RP2C040002 = 0x03, + + /// + /// RP2C04-0003 + /// + RP2C040003 = 0x04, + + /// + /// RP2C04-0004 + /// + RP2C040004 = 0x05, + + /// + /// Reserved + /// + Reserved6 = 0x06, + + /// + /// Reserved + /// + VsReserved7 = 0x07, + + /// + /// RC2C05-01 (signature unknown) + /// + RC2C0501 = 0x08, + + /// + /// RC2C05-02 ($2002 AND $3F =$3D) + /// + RC2C0502 = 0x09, + + /// + /// RC2C05-03 ($2002 AND $1F =$1C) + /// + RC2C0503 = 0x0A, + + /// + /// RC2C05-04 ($2002 AND $1F =$1B) + /// + RC2C0504 = 0x0B, + + /// + /// Reserved + /// + VsReservedC = 0x0C, + + /// + /// Reserved + /// + VsReservedD = 0x0D, + + /// + /// Reserved + /// + VsReservedE = 0x0E, + + /// + /// Reserved + /// + VsReservedF = 0x0F, + + #endregion + + #region Bits 4-7 (Vs. System Type) + + /// + /// Vs. Unisystem (normal) + /// + VsUnisystem = 0x00, + + /// + /// Vs. Unisystem (RBI Baseball protection) + /// + VsUnisystemRBIBaseballProtection = 0x10, + + /// + /// Vs. Unisystem (TKO Boxing protection) + /// + VsUnisystemTKOBoxingProtection = 0x20, + + /// + /// Vs. Unisystem (Super Xevious protection) + /// + VsUnisystemSuperXeviousProtection = 0x30, + + /// + /// Vs. Unisystem (Vs. Ice Climber Japan protection) + /// + VsUnisystemVsIceClimberJapanProtection = 0x40, + + /// + /// Vs. Dual System (normal) + /// + VsDualSystem = 0x50, + + /// + /// Vs. Dual System (Raid on Bungeling Bay protection) + /// + VsDualSystemRaidOnBungelingBayProtection = 0x60, + + #endregion + + #region Bits 0-3 (Extended Console Type) + + /// + /// Regular NES/Famicom/Dendy + /// + RegularSystem = 0x00, + + /// + /// Nintendo Vs. System + /// + NintendoVsSystem = 0x01, + + /// + /// Playchoice 10 + /// + Playchoice10 = 0x02, + + /// + /// Regular Famiclone, but with CPU that supports Decimal Mode + /// + RegularFamicloneDecimalMode = 0x03, + + /// + /// Regular NES/Famicom with EPSM module or plug-through cartridge + /// + RegularNESWithEPSM = 0x04, + + /// + /// V.R. Technology VT01 with red/cyan STN palette + /// + VRTechnologyVT01 = 0x05, + + /// + /// V.R. Technology VT02 + /// + VRTechnologyVT02 = 0x06, + + /// + /// V.R. Technology VT03 + /// + VRTechnologyVT03 = 0x07, + + /// + /// V.R. Technology VT09 + /// + VRTechnologyVT09 = 0x08, + + /// + /// V.R. Technology VT32 + /// + VRTechnologyVT32 = 0x09, + + /// + /// V.R. Technology VT369 + /// + VRTechnologyVT369 = 0x0A, + + /// + /// UMC UM6578 + /// + UMCUM6578 = 0x0B, + + /// + /// Famicom Network System + /// + FamicomNetworkSystem = 0x0C, + + /// + /// Reserved + /// + ExtendedConsoleReservedD = 0x0D, + + /// + /// Reserved + /// + ExtendedConsoleReservedE = 0x0E, + + /// + /// Reserved + /// + ExtendedConsoleReservedF = 0x0F, + + #endregion + } + + /// + /// Default Expansion Device + /// + public enum DefaultExpansionDevice : byte + { + /// + /// Unspecified + /// + Unspecified = 0x00, + + /// + /// Standard NES/Famicom controllers + /// + StandardControllers = 0x01, + + /// + /// NES Four Score/Satellite with two additional standard controllers + /// + NESFourScore = 0x02, + + /// + /// Famicom Four Players Adapter with two additional standard controllers + /// using the "simple" protocol + /// + FamicomFourPlayersAdapter = 0x03, + + /// + /// Vs. System (1P via $4016) + /// + VsSystem4016 = 0x04, + + /// + /// Vs. System (1P via $4017) + /// + VsSystem4017 = 0x05, + + /// + /// Reserved + /// + Reserved06 = 0x06, + + /// + /// Vs. Zapper + /// + VsZapper = 0x07, + + /// + /// Zapper ($4017) + /// + Zapper4017 = 0x08, + + /// + /// Two Zappers + /// + TwoZappers = 0x09, + + /// + /// Bandai Hyper Shot Lightgun + /// + BandaiHyperShotLightgun = 0x0A, + + /// + /// Power Pad Side A + /// + PowerPadSideA = 0x0B, + + /// + /// Power Pad Side B + /// + PowerPadSideB = 0x0C, + + /// + /// Family Trainer Side A + /// + FamilyTrainerSideA = 0x0D, + + /// + /// Family Trainer Side B + /// + FamilyTrainerSideB = 0x0E, + + /// + /// Arkanoid Vaus Controller (NES) + /// + ArkanoidVausControllerNES = 0x0F, + + /// + /// Arkanoid Vaus Controller (Famicom) + /// + ArkanoidVausControllerFamicom = 0x10, + + /// + /// Two Vaus Controllers plus Famicom Data Recorder + /// + TwoVausControllers = 0x11, + + /// + /// Konami Hyper Shot Controller + /// + KonamiHyperShotController = 0x12, + + /// + /// Coconuts Pachinko Controller + /// + CoconutsPachinkoController = 0x13, + + /// + /// Exciting Boxing Punching Bag (Blowup Doll) + /// + ExcitingBoxingPunchingBag = 0x14, + + /// + /// Jissen Mahjong Controller + /// + JissenMahjongController = 0x15, + + /// + /// 米澤 (Yonezawa) Party Tap + /// + YonezawaPartyTap = 0x16, + + /// + /// Oeka Kids Tablet + /// + OekaKidsTablet = 0x17, + + /// + /// Sunsoft Barcode Battler + /// + SunsoftBarcodeBattler = 0x18, + + /// + /// Miracle Piano Keyboard + /// + MiraclePianoKeyboard = 0x19, + + /// + /// Pokkun Moguraa Tap-tap Mat (Whack-a-Mole Mat and Mallet) + /// + PokkunMoguraaTapTapMat = 0x1A, + + /// + /// Top Rider (Inflatable Bicycle) + /// + TopRider = 0x1B, + + /// + /// Double-Fisted (Requires or allows use of two controllers by one player) + /// + DoubleFisted = 0x1C, + + /// + /// Famicom 3D System + /// + Famicom3DSystem = 0x1D, + + /// + /// Doremikko Keyboard + /// + DoremikkoKeyboard = 0x1E, + + /// + /// R.O.B. Gyromite + /// + ROBGyromite = 0x1F, + + /// + /// Famicom Data Recorder ("silent" keyboard) + /// + FamicomDataRecorder = 0x20, + + /// + /// ASCII Turbo File + /// + ASCIITurboFile = 0x21, + + /// + /// IGS Storage Battle Box + /// + IGSStorageBattleBox = 0x22, + + /// + /// Family BASIC Keyboard plus Famicom Data Recorder + /// + FamilyBASICKeyboard = 0x23, + + /// + /// 东达 (Dōngdá) PEC Keyboard + /// + DongdaPECKeyboard = 0x24, + + /// + /// 普澤 (Pǔzé, a.k.a. Bit Corp.) Bit-79 Keyboard + /// + BitCorpBit79Keyboard = 0x25, + + /// + /// 小霸王 (Xiǎobàwáng, a.k.a. Subor) Keyboard + /// + SuborKeyboard = 0x26, + + /// + /// 小霸王 (Xiǎobàwáng, a.k.a. Subor) Keyboard plus Macro Winners Mouse + /// + SuborKeyboardPlusMacroWinnersMouse = 0x27, + + /// + /// 小霸王 (Xiǎobàwáng, a.k.a. Subor) Keyboard plus Subor Mouse via $4016 + /// + SuborKeyboardPlusSuborMouse4016 = 0x28, + + /// + /// SNES Mouse ($4016) + /// + SNESMouse4016 = 0x29, + + /// + /// Multicart + /// + Multicart = 0x2A, + + /// + /// Two SNES controllers replacing the two standard NES controllers + /// + TwoSNESControllers = 0x2B, + + /// + /// RacerMate Bicycle + /// + RacerMateBicycle = 0x2C, + + /// + /// U-Force + /// + UForce = 0x2D, + + /// + /// R.O.B. Stack-Up + /// + ROBStackUp = 0x2E, + + /// + /// City Patrolman Lightgun + /// + CityPatrolmanLightgun = 0x2F, + + /// + /// Sharp C1 Cassette Interface + /// + SharpC1CassetteInterface = 0x30, + + /// + /// Standard Controller with swapped Left-Right/Up-Down/B-A + /// + StandardControllerWithSwappedInputs = 0x31, + + /// + /// Excalibur Sudoku Pad + /// + ExcaliburSudokuPad = 0x32, + + /// + /// ABL Pinball + /// + ABLPinball = 0x33, + + /// + /// Golden Nugget Casino extra buttons + /// + GoldenNuggetCasinoExtraButtons = 0x34, + + /// + /// 科达 (Kēdá) Keyboard + /// + KedaKeyboard = 0x35, + + /// + /// 小霸王 (Xiǎobàwáng, a.k.a. Subor) Keyboard plus Subor Mouse via $4017 + /// + SuborKeyboardPlusSuborMouse4017 = 0x36, + + /// + /// Port test controller + /// + PortTestController = 0x37, + + /// + /// Bandai Multi Game Player Gamepad buttons + /// + BandaiMultiGamePlayerGamepad = 0x38, + + /// + /// Venom TV Dance Mat + /// + VenomTVDanceMat = 0x39, + + /// + /// LG TV Remote Control + /// + LGTVRemoteControl = 0x3A, + + /// + /// Famicom Network Controller + /// + FamicomNetworkController = 0x3B, + + /// + /// King Fishing Controller + /// + KingFishingController = 0x3C, + + /// + /// Croaky Karaoke Controller + /// + CroakyKaraokeController = 0x3D, + + /// + /// 科王 (Kēwáng, a.k.a. Kingwon) Keyboard + /// + KingwonKeyboard = 0x3E, + + /// + /// 泽诚 (Zéchéng) Keyboard + /// + ZechengKeyboard = 0x3F, + + /// + /// 小霸王 (Xiǎobàwáng, a.k.a. Subor) Keyboard plus L90-rotated PS/2 mouse in $4017 + /// + SuborKeyboardPlusL90RotatedPS2Mouse4017 = 0x40, + + /// + /// PS/2 Keyboard in UM6578 PS/2 port, PS/2 Mouse via $4017 + /// + PS2KeyboardInUM6578PS2Port = 0x41, + + /// + /// PS/2 Mouse in UM6578 PS/2 port + /// + PS2MouseInUM6578PS2Port = 0x42, + + /// + /// 裕兴 (Yùxìng) Mouse via $4016 + /// + YuxingMouse = 0x43, + + /// + /// 小霸王 (Xiǎobàwáng, a.k.a. Subor) Keyboard plus 裕兴 (Yùxìng) + /// Mouse mouse in $4016 + /// + SuborKeyboardPlusYuxingMouse = 0x44, + + /// + /// Gigggle TV Pump + /// + GigggleTVPump = 0x45, + + /// + /// 步步高 (Bùbùgāo, a.k.a. BBK) Keyboard plus R90-rotated PS/2 mouse in $4017 + /// + BBKKeyboardPlusR90RotatedPS2Mouse = 0x46, + + /// + /// Magical Cooking + /// + MagicalCooking = 0x47, + + /// + /// SNES Mouse ($4017) + /// + SNESMouse4017 = 0x48, + + /// + /// Zapper ($4016) + /// + Zapper4016 = 0x49, + + /// + /// Arkanoid Vaus Controller (Prototype) + /// + ArkanoidVausControllerPrototype = 0x4A, + + /// + /// TV 麻雀 Game (TV Mahjong Game) Controller + /// + TVMahjongGameController = 0x4B, + + /// + /// 麻雀激闘伝説 (Mahjong Gekitou Densetsu) Controller + /// + MahjongGekitouDensetsuController = 0x4C, + + /// + /// 小霸王 (Xiǎobàwáng, a.k.a. Subor) Keyboard plus X-inverted PS/2 mouse in $4017 + /// + SuborKeyboardPlusXInvertedPS2Mouse = 0x4D, + + /// + /// IBM PC/XT Keyboard + /// + IBMPCXTKeyboard = 0x4E, + + /// + /// 小霸王 (Xiǎobàwáng, a.k.a. Subor) Keyboard plus Mega Book Mouse + /// + SuborKeyboardPlusMegaBookMouse = 0x4F, + } +} diff --git a/SabreTools.Serialization/Models/NES/Header.cs b/SabreTools.Serialization/Models/NES/Header.cs new file mode 100644 index 00000000..8d629775 --- /dev/null +++ b/SabreTools.Serialization/Models/NES/Header.cs @@ -0,0 +1,48 @@ +namespace SabreTools.Data.Models.NES +{ + /// + /// Common NES header pieces + /// + /// + /// + public abstract class Header + { + /// + /// Constant $4E $45 $53 $1A (ASCII "NES" followed by MS-DOS end-of-file) + /// + public byte[] IdentificationString { get; set; } = new byte[4]; + + /// + /// Size of PRG ROM in 16 KB units + /// + public byte PRGROMSize { get; set; } + + /// + /// Size of CHR ROM in 8 KB units + /// + /// Value 0 means the board uses CHR RAM + public byte CHRROMSize { get; set; } + + /// + /// Mapper, mirroring, battery, trainer + /// + public Flag6 Flag6 { get; set; } + + /// + /// Mapper, VS/Playchoice, NES 2.0 + /// + /// + /// The PlayChoice-10 bit is not part of the official specification, + /// and most emulators simply ignore the extra 8 KB of data. PlayChoice + /// games are designed to look good with the 2C03 RGB PPU, which handles + /// color emphasis differently from a standard NES PPU. + /// + /// Vs. games have a coin slot and different palettes. The detection + /// of which palette a particular game uses is left unspecified. + /// + /// NES 2.0 is a more recent extension to the format that allows more + /// flexibility in ROM and RAM size, among other things. + /// + public Flag7 Flag7 { get; set; } + } +} diff --git a/SabreTools.Serialization/Models/NES/Header1.cs b/SabreTools.Serialization/Models/NES/Header1.cs new file mode 100644 index 00000000..a2db94a4 --- /dev/null +++ b/SabreTools.Serialization/Models/NES/Header1.cs @@ -0,0 +1,53 @@ +namespace SabreTools.Data.Models.NES +{ + /// + /// NES 1.0 header information + /// + /// + /// + /// Older versions of the iNES emulator ignored bytes 7-15, and several ROM management + /// tools wrote messages in there. Commonly, these will be filled with "DiskDude!", + /// which results in 64 being added to the mapper number. + /// + /// A general rule of thumb: if the last 4 bytes are not all zero, and the header is + /// not marked for NES 2.0 format, an emulator should either mask off the upper 4 bits + /// of the mapper number or simply refuse to load the ROM. + /// + public class Header1 : Header + { + // All common header parts take up bytes 0-7 + + /// + /// Size of PRG RAM in 8 KB units + /// + /// + /// Value 0 infers 8 KB for compatibility; see PRG RAM circuit. + /// This was a later extension to the iNES format and not widely used. + /// NES 2.0 is recommended for specifying PRG RAM size instead. + /// + public byte PRGRAMSize { get; set; } + + /// + /// TV system (rarely used extension) + /// + /// + /// Though in the official specification, very few emulators honor this bit + /// as virtually no ROM images in circulation make use of it. + /// + public TVSystem TVSystem { get; set; } + + /// + /// TV system, PRG-RAM presence (unofficial, rarely used extension) + /// + /// + /// This byte is not part of the official specification, and relatively + /// few emulators honor it. + /// + public Flag10 Flag10 { get; set; } + + /// + /// Unused padding to align to 16 bytes + /// + public byte[] Padding { get; set; } = new byte[5]; + } +} diff --git a/SabreTools.Serialization/Models/NES/Header2.cs b/SabreTools.Serialization/Models/NES/Header2.cs new file mode 100644 index 00000000..d4013d3c --- /dev/null +++ b/SabreTools.Serialization/Models/NES/Header2.cs @@ -0,0 +1,80 @@ +namespace SabreTools.Data.Models.NES +{ + /// + /// NES 2.0 header information + /// + /// + public class Header2 : Header + { + // All common header parts take up bytes 0-7 + + /// + /// Mapper MSB/Submapper + /// + /// + /// Bits 0-3 - Mapper number bits 8-11 + /// Bits 4-7 - Submapper number + /// + public byte MapperMSBSubmapper { get; set; } + + /// + /// PRG-ROM/CHR-ROM size MSB + /// + /// + /// Bits 0-3 - PRG-ROM size MSB + /// Bits 4-7 - CHR-ROM size MSB + /// + public byte PRGCHRMSB { get; set; } + + /// + /// PRG-RAM/EEPROM size + /// + /// + /// Bits 0-3 - PRG-RAM (volatile) shift count + /// Bits 4-7 - PRG-NVRAM/EEPROM (non-volatile) shift count + /// + /// If the shift count is zero, there is no CHR-(NV)RAM. + /// If the shift count is non-zero, the actual size is + /// "64 << shift count" bytes, i.e. 8192 bytes for a shift count of 7. + /// + public byte PRGRAMEEPROMSize { get; set; } + + /// + /// CHR-RAM size + /// + /// + /// Bits 0-3 - CHR-RAM size (volatile) shift count + /// Bits 4-7 - CHR-NVRAM size (non-volatile) shift count + /// + /// If the shift count is zero, there is no CHR-(NV)RAM. + /// If the shift count is non-zero, the actual size is + /// "64 << shift count" bytes, i.e. 8192 bytes for a shift count of 7. + /// + public byte CHRRAMSize { get; set; } + + /// + /// CPU/PPU timing mode + /// + public CPUPPUTiming CPUPPUTiming { get; set; } + + /// + /// Vs. System Type and Extended Console Type + /// + /// + /// When Byte 7 AND 3 =1: Vs. System Type + /// When Byte 7 AND 3 =3: Extended Console Type + /// + public ExtendedSystemType ExtendedSystemType { get; set; } + + /// + /// Number of miscellaneous ROMs present + /// + /// Only bits 0-1 are used + public bool MiscellaneousROMs { get; set; } + + /// + /// Default Expansion Device + /// + public DefaultExpansionDevice DefaultExpansionDevice { get; set; } + } +}