diff --git a/SabreTools.Serialization/Extensions/NESCart.cs b/SabreTools.Serialization/Extensions/NESCart.cs index 22f72d8e..4fda5372 100644 --- a/SabreTools.Serialization/Extensions/NESCart.cs +++ b/SabreTools.Serialization/Extensions/NESCart.cs @@ -137,6 +137,19 @@ namespace SabreTools.Data.Extensions }; } + /// + /// Convert a value to string + /// + public static string FromNametableArrangement(this NametableArrangement type) + { + return type switch + { + NametableArrangement.Vertical => "Vertical", + NametableArrangement.Horizontal => "Horizontal", + _ => $"Unknown {(byte)type}", + }; + } + /// /// Convert a value to string /// diff --git a/SabreTools.Serialization/Models/NES/Enums.cs b/SabreTools.Serialization/Models/NES/Enums.cs index 687d5b00..c1c7dc86 100644 --- a/SabreTools.Serialization/Models/NES/Enums.cs +++ b/SabreTools.Serialization/Models/NES/Enums.cs @@ -2,70 +2,6 @@ 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 = 0b0000, - - /// - /// Horizontal arrangement ("vertically mirrored") - /// - /// CIRAM A10 = PPU A10 - NametableArrangementHorizontal = 0b0001, - - #endregion - - #region Bit 1 - - /// - /// Cartridge contains battery-backed PRG RAM ($6000-7FFF) - /// or other persistent memory not present - /// - BatteryBackedPRGRAMNotPresent = 0b0000, - - /// - /// Cartridge contains battery-backed PRG RAM ($6000-7FFF) - /// or other persistent memory present - /// - BatteryBackedPRGRAMPresent = 0b0010, - - #endregion - - #region Bit 2 - - /// - /// 512-byte trainer at $7000-$71FF - /// - TrainerNotPresent = 0b0000, - - /// - /// 512-byte trainer at $7000-$71FF - /// - /// Stored before PRG data - TrainerPresent = 0b0100, - - #endregion - - #region Bit 3 - - /// - /// Alternative nametable layout - /// - AlternativeNametableLayout = 0b1000, - - #endregion - } - /// /// Mapper, VS/Playchoice, NES 2.0 /// @@ -107,15 +43,6 @@ namespace SabreTools.Data.Models.NES #endregion } - /// - /// TV system (rarely used extension) - /// - public enum TVSystem : byte - { - NTSC = 0x00, - PAL = 0x01, - } - /// /// TV system, PRG-RAM presence (unofficial, rarely used extension) /// @@ -160,6 +87,33 @@ namespace SabreTools.Data.Models.NES #endregion } + /// + /// Console Type + /// + /// Actually only 2 bits (bits 0-1 of flag 7) + public enum ConsoleType : byte + { + /// + /// Nintendo Entertainment System/Family Computer + /// + StandardSystem = 0x00, + + /// + /// VS Unisystem + /// + VSUnisystem = 0x01, + + /// + /// PlayChoice-10 (8 KB of Hint Screen data stored after CHR data) + /// + PlayChoice10 = 0x02, + + /// + /// Extended Console Type + /// + ExtendedConsoleType = 0x03, + } + /// /// CPU/PPU Timing /// @@ -186,221 +140,6 @@ namespace SabreTools.Data.Models.NES UA6538 = 0x03, } - /// - /// Vs. System Type - /// - /// Actually only 4 bits - public enum VsSystemType : byte - { - /// - /// Any RP2C03/RC2C03 variant - /// - AnyRP2C03RC2C03Variant = 0x00, - - /// - /// Reserved - /// - Reserved1 = 0x01, - - /// - /// RP2C04-0001 - /// - RP2C040001 = 0x02, - - /// - /// RP2C04-0002 - /// - RP2C040002 = 0x03, - - /// - /// RP2C04-0003 - /// - RP2C040003 = 0x04, - - /// - /// RP2C04-0004 - /// - RP2C040004 = 0x05, - - /// - /// Reserved - /// - Reserved6 = 0x06, - - /// - /// Reserved - /// - Reserved7 = 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 - /// - ReservedC = 0x0C, - - /// - /// Reserved - /// - ReservedD = 0x0D, - - /// - /// Reserved - /// - ReservedE = 0x0E, - - /// - /// Reserved - /// - ReservedF = 0x0F, - } - - /// - /// Vs. Hardware Type - /// - /// Actually only 4 bits - public enum VsHardwareType : byte - { - /// - /// Vs. Unisystem (normal) - /// - VsUnisystem = 0x00, - - /// - /// Vs. Unisystem (RBI Baseball protection) - /// - VsUnisystemRBIBaseballProtection = 0x01, - - /// - /// Vs. Unisystem (TKO Boxing protection) - /// - VsUnisystemTKOBoxingProtection = 0x02, - - /// - /// Vs. Unisystem (Super Xevious protection) - /// - VsUnisystemSuperXeviousProtection = 0x03, - - /// - /// Vs. Unisystem (Vs. Ice Climber Japan protection) - /// - VsUnisystemVsIceClimberJapanProtection = 0x04, - - /// - /// Vs. Dual System (normal) - /// - VsDualSystem = 0x05, - - /// - /// Vs. Dual System (Raid on Bungeling Bay protection) - /// - VsDualSystemRaidOnBungelingBayProtection = 0x06, - } - - /// - /// Extended Console Type - /// - public enum ExtendedConsoleType : byte - { - /// - /// 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 - /// - ReservedD = 0x0D, - - /// - /// Reserved - /// - ReservedE = 0x0E, - - /// - /// Reserved - /// - ReservedF = 0x0F, - } - /// /// Default Expansion Device /// @@ -808,4 +547,257 @@ namespace SabreTools.Data.Models.NES /// SuborKeyboardPlusMegaBookMouse = 0x4F, } + + /// + /// Extended Console Type + /// + public enum ExtendedConsoleType : byte + { + /// + /// 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 + /// + ReservedD = 0x0D, + + /// + /// Reserved + /// + ReservedE = 0x0E, + + /// + /// Reserved + /// + ReservedF = 0x0F, + } + + /// + /// Nametable arrangement + /// + /// Actually only 1 bit (bit 0 of flag 6) + public enum NametableArrangement : byte + { + /// + /// Vertical arrangement ("horizontal mirrored") or mapper-controlled + /// + /// CIRAM A10 = PPU A11 + Vertical = 0x00, + + /// + /// Horizontal arrangement ("vertically mirrored") + /// + /// CIRAM A10 = PPU A10 + Horizontal = 0x01, + } + + /// + /// PRG RAM ($6000-$7FFF) + /// + /// Actually only 1 bit (bit 4 of flag 10) + public enum PRGRAMPresent : byte + { + Present = 0x00, + NotPresent = 0x01, + } + + /// + /// TV system (rarely used extension) + /// + public enum TVSystem : byte + { + NTSC = 0x00, + PAL = 0x01, + } + + /// + /// Vs. Hardware Type + /// + /// Actually only 4 bits + public enum VsHardwareType : byte + { + /// + /// Vs. Unisystem (normal) + /// + VsUnisystem = 0x00, + + /// + /// Vs. Unisystem (RBI Baseball protection) + /// + VsUnisystemRBIBaseballProtection = 0x01, + + /// + /// Vs. Unisystem (TKO Boxing protection) + /// + VsUnisystemTKOBoxingProtection = 0x02, + + /// + /// Vs. Unisystem (Super Xevious protection) + /// + VsUnisystemSuperXeviousProtection = 0x03, + + /// + /// Vs. Unisystem (Vs. Ice Climber Japan protection) + /// + VsUnisystemVsIceClimberJapanProtection = 0x04, + + /// + /// Vs. Dual System (normal) + /// + VsDualSystem = 0x05, + + /// + /// Vs. Dual System (Raid on Bungeling Bay protection) + /// + VsDualSystemRaidOnBungelingBayProtection = 0x06, + } + + /// + /// Vs. System Type + /// + /// Actually only 4 bits + public enum VsSystemType : byte + { + /// + /// Any RP2C03/RC2C03 variant + /// + AnyRP2C03RC2C03Variant = 0x00, + + /// + /// Reserved + /// + Reserved1 = 0x01, + + /// + /// RP2C04-0001 + /// + RP2C040001 = 0x02, + + /// + /// RP2C04-0002 + /// + RP2C040002 = 0x03, + + /// + /// RP2C04-0003 + /// + RP2C040003 = 0x04, + + /// + /// RP2C04-0004 + /// + RP2C040004 = 0x05, + + /// + /// Reserved + /// + Reserved6 = 0x06, + + /// + /// Reserved + /// + Reserved7 = 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 + /// + ReservedC = 0x0C, + + /// + /// Reserved + /// + ReservedD = 0x0D, + + /// + /// Reserved + /// + ReservedE = 0x0E, + + /// + /// Reserved + /// + ReservedF = 0x0F, + } } diff --git a/SabreTools.Serialization/Models/NES/Header.cs b/SabreTools.Serialization/Models/NES/Header.cs index 8d629775..41f7327c 100644 --- a/SabreTools.Serialization/Models/NES/Header.cs +++ b/SabreTools.Serialization/Models/NES/Header.cs @@ -23,10 +23,40 @@ namespace SabreTools.Data.Models.NES /// Value 0 means the board uses CHR RAM public byte CHRROMSize { get; set; } + #region Flag 6 + /// - /// Mapper, mirroring, battery, trainer + /// Nametable arrangement /// - public Flag6 Flag6 { get; set; } + /// Flag 6, Bit 0 + public NametableArrangement NametableArrangement { get; set; } + + /// + /// Cartridge contains battery-backed PRG RAM ($6000-7FFF) + /// or other persistent memory + /// + /// Flag 6, Bit 1 + public bool BatteryBackedPRGRAM { get; set; } + + /// + /// 512-byte trainer at $7000-$71FF + /// + /// Flag 6, Bit 2 + public bool TrainerPresent { get; set; } + + /// + /// Alternative nametable layout + /// + /// Flag 6, Bit 3 + public bool AlternativeNametableLayout { get; set; } + + /// + /// Lower nibble of Mapper number + /// + /// Flag 6, Bits 4-7 + public byte MapperLowerNibble { get; set; } + + #endregion /// /// Mapper, VS/Playchoice, NES 2.0 diff --git a/SabreTools.Serialization/Readers/NESCart.cs b/SabreTools.Serialization/Readers/NESCart.cs index 20546fdb..0edcf05e 100644 --- a/SabreTools.Serialization/Readers/NESCart.cs +++ b/SabreTools.Serialization/Readers/NESCart.cs @@ -35,11 +35,7 @@ namespace SabreTools.Serialization.Readers #endregion // Read the trainer data, if necessary -#if NET20 || NET35 - if ((cart.Header.Flag6 & Flag6.TrainerPresent) != 0) -#else - if (cart.Header.Flag6.HasFlag(Flag6.TrainerPresent)) -#endif + if (cart.Header.TrainerPresent) cart.Trainer = data.ReadBytes(512); // Derive the PRG-ROM and CHR-ROM data sizes @@ -102,7 +98,14 @@ namespace SabreTools.Serialization.Readers byte prgRomSize = data.ReadByteValue(); byte chrRomSize = data.ReadByteValue(); - Flag6 flag6 = (Flag6)data.ReadByteValue(); + + byte flag6 = data.ReadByteValue(); + NametableArrangement nametableArrangement = (NametableArrangement)(flag6 & 0x01); + bool batteryBackedPrgRam = ((flag6 >> 1) & 0x01) != 0; + bool trainerPresent = ((flag6 >> 2) & 0x01) != 0; + bool alternativeNametableLayout = ((flag6 >> 3) & 0x01) != 0; + byte mapperLowerNibble = (byte)(flag6 >> 4); + Flag7 flag7 = (Flag7)data.ReadByteValue(); // NES 2.0 @@ -117,7 +120,14 @@ namespace SabreTools.Serialization.Readers obj.IdentificationString = identificationString; obj.PRGROMSize = prgRomSize; obj.CHRROMSize = chrRomSize; - obj.Flag6 = flag6; + + // Flag 6 + obj.NametableArrangement = nametableArrangement; + obj.BatteryBackedPRGRAM = batteryBackedPrgRam; + obj.TrainerPresent = trainerPresent; + obj.AlternativeNametableLayout = alternativeNametableLayout; + obj.MapperLowerNibble = mapperLowerNibble; + obj.Flag7 = flag7; obj.MapperMSBSubmapper = data.ReadByteValue(); obj.PRGCHRMSB = data.ReadByteValue(); @@ -139,7 +149,14 @@ namespace SabreTools.Serialization.Readers obj.IdentificationString = identificationString; obj.PRGROMSize = prgRomSize; obj.CHRROMSize = chrRomSize; - obj.Flag6 = flag6; + + // Flag 6 + obj.NametableArrangement = nametableArrangement; + obj.BatteryBackedPRGRAM = batteryBackedPrgRam; + obj.TrainerPresent = trainerPresent; + obj.AlternativeNametableLayout = alternativeNametableLayout; + obj.MapperLowerNibble = mapperLowerNibble; + obj.Flag7 = flag7; obj.PRGRAMSize = data.ReadByteValue(); obj.TVSystem = (TVSystem)data.ReadByteValue(); diff --git a/SabreTools.Serialization/Wrappers/NESCart.Printing.cs b/SabreTools.Serialization/Wrappers/NESCart.Printing.cs index 8e5de1f3..9f428df7 100644 --- a/SabreTools.Serialization/Wrappers/NESCart.Printing.cs +++ b/SabreTools.Serialization/Wrappers/NESCart.Printing.cs @@ -53,44 +53,19 @@ namespace SabreTools.Serialization.Wrappers builder.AppendLine(" Flag 6:"); // Bit 0 -#if NET20 || NET35 - if ((header.Flag6 & Flag6.NametableArrangementHorizontal) != 0) -#else - if (header.Flag6.HasFlag(Flag6.NametableArrangementHorizontal)) -#endif - builder.AppendLine(" Nametable Arrangement: Horizontal"); - else - builder.AppendLine(" Nametable Arrangement: Vertical"); + string nametableArrangement = header.NametableArrangement.FromNametableArrangement(); + builder.AppendLine(nametableArrangement, " Nametable Arrangement"); // Bit 1 -#if NET20 || NET35 - if ((header.Flag6 & Flag6.BatteryBackedPRGRAMPresent) != 0) -#else - if (header.Flag6.HasFlag(Flag6.BatteryBackedPRGRAMPresent)) -#endif - builder.AppendLine(" Battery-Backed PRG RAM: Present"); - else - builder.AppendLine(" Battery-Backed PRG RAM: Not present"); + string batteryBackedPrgRam = header.BatteryBackedPRGRAM ? "Present" : "Not present"; + builder.AppendLine(batteryBackedPrgRam, " Battery-Backed PRG RAM"); // Bit 2 -#if NET20 || NET35 - if ((header.Flag6 & Flag6.TrainerPresent) != 0) -#else - if (header.Flag6.HasFlag(Flag6.TrainerPresent)) -#endif - builder.AppendLine(" Trainer: Present"); - else - builder.AppendLine(" Trainer: Not present"); + string trainerPresent = header.TrainerPresent ? "Present" : "Not present"; + builder.AppendLine(trainerPresent, " Trainer"); // Bit 3 -#if NET20 || NET35 - if ((header.Flag6 & Flag6.AlternativeNametableLayout) != 0) -#else - if (header.Flag6.HasFlag(Flag6.AlternativeNametableLayout)) -#endif - builder.AppendLine(" Alternative Nametable Layout: True"); - else - builder.AppendLine(" Alternative Nametable Layout: False"); + builder.AppendLine(header.AlternativeNametableLayout, " Alternative Nametable Layout"); #endregion @@ -132,7 +107,7 @@ namespace SabreTools.Serialization.Wrappers #endregion - byte mapperNumber = (byte)((((byte)header.Flag7 >> 4) << 4) | (byte)((byte)header.Flag6 >> 4)); + byte mapperNumber = (byte)((((byte)header.Flag7 >> 4) << 4) | header.MapperLowerNibble); builder.AppendLine(mapperNumber, " Mapper number"); if (header is Header1 header1) @@ -201,7 +176,7 @@ namespace SabreTools.Serialization.Wrappers byte mapperMsb = (byte)(header2.MapperMSBSubmapper & 0x0F); ushort extendedMapperNumber = (ushort)((mapperMsb << 8) | (byte)((((byte)header.Flag7 >> 4) << 4) - | (byte)((byte)header.Flag6 >> 4))); + | header.MapperLowerNibble)); byte submapperNumber = (byte)(header2.MapperMSBSubmapper >> 4); builder.AppendLine(mapperMsb, " Mapper MSB"); diff --git a/SabreTools.Serialization/Wrappers/NESCart.cs b/SabreTools.Serialization/Wrappers/NESCart.cs index 47ef4d2b..d9660af2 100644 --- a/SabreTools.Serialization/Wrappers/NESCart.cs +++ b/SabreTools.Serialization/Wrappers/NESCart.cs @@ -19,6 +19,12 @@ namespace SabreTools.Serialization.Wrappers /// public Header? Header => Model.Header; + /// + public bool AlternativeNametableLayout => Header?.AlternativeNametableLayout ?? false; + + /// + public bool BatteryBackedPRGRAM => Header?.BatteryBackedPRGRAM ?? false; + /// public byte[] CHRROMData => Model.CHRROMData; @@ -42,63 +48,6 @@ namespace SabreTools.Serialization.Wrappers } } - /// - /// Indicates if an alternative nametable layout is in use - /// - public bool HasAlternativeNametableLayout - { - get - { - // Missing header - if (Header is null) - return false; - -#if NET20 || NET35 - return (Header.Flag6 & Flag6.AlternativeNametableLayout) != 0; -#else - return Header.Flag6.HasFlag(Flag6.AlternativeNametableLayout); -#endif - } - } - - /// - /// Indicates if battery-backed PRG RAM is present - /// - public bool HasBatteryBackedPRGRAM - { - get - { - // Missing header - if (Header is null) - return false; - -#if NET20 || NET35 - return (Header.Flag6 & Flag6.BatteryBackedPRGRAMPresent) != 0; -#else - return Header.Flag6.HasFlag(Flag6.BatteryBackedPRGRAMPresent); -#endif - } - } - - /// - /// Indicates if trainer data is present - /// - public bool HasTrainer - { - get - { - // Missing header - if (Header is null) - return false; - -#if NET20 || NET35 - return (Header.Flag6 & Flag6.TrainerPresent) != 0; -#else - return Header.Flag6.HasFlag(Flag6.TrainerPresent); -#endif - } - } - /// /// Indicates if the game is meant for an extended console type /// @@ -213,7 +162,7 @@ namespace SabreTools.Serialization.Wrappers if (Header is null) return 0; - int mapperNumber = (((byte)Header.Flag7 >> 4) << 4) | ((byte)Header.Flag6 >> 4); + int mapperNumber = (((byte)Header.Flag7 >> 4) << 4) | Header.MapperLowerNibble; if (Header is Header2 header2) mapperNumber = ((header2.MapperMSBSubmapper & 0x0F) << 8) | mapperNumber; @@ -221,6 +170,10 @@ namespace SabreTools.Serialization.Wrappers } } + /// + public NametableArrangement NametableArrangement + => Header?.NametableArrangement ?? NametableArrangement.Vertical; + /// /// PRG-RAM size in bytes /// @@ -283,43 +236,8 @@ namespace SabreTools.Serialization.Wrappers /// public byte[] Trainer => Model.Trainer; - /// - /// Indicates if horizontal nametable arrangement is in use - /// - public bool UsesHorizontalNametableArrangement - { - get - { - // Missing header - if (Header is null) - return false; - -#if NET20 || NET35 - return (Header.Flag6 & Flag6.NametableArrangementHorizontal) != 0; -#else - return Header.Flag6.HasFlag(Flag6.NametableArrangementHorizontal); -#endif - } - } - - /// - /// Indicates if vertical nametable arrangement is in use - /// - public bool UsesVerticalNametableArrangement - { - get - { - // Missing header - if (Header is null) - return false; - -#if NET20 || NET35 - return (Header.Flag6 & Flag6.NametableArrangementHorizontal) == 0; -#else - return !Header.Flag6.HasFlag(Flag6.NametableArrangementHorizontal); -#endif - } - } + /// + public bool TrainerPresent => Header?.TrainerPresent ?? false; #endregion