Split flag 7

This commit is contained in:
Matt Nadareski
2026-03-06 15:16:41 -05:00
parent 756d5fe03c
commit 5e758d4b38
6 changed files with 84 additions and 230 deletions

View File

@@ -4,6 +4,21 @@ namespace SabreTools.Data.Extensions
{
public static class NESCart
{
/// <summary>
/// Convert a <see cref="CPUPPConsoleTypeUTiming"/> value to string
/// </summary>
public static string FromConsoleType(this ConsoleType type)
{
return type switch
{
ConsoleType.StandardSystem => "Nintendo Entertainment System/Family Computer",
ConsoleType.VSUnisystem => "VS Unisystem",
ConsoleType.PlayChoice10 => "PlayChoice-10 (8 KB of Hint Screen data stored after CHR data)",
ConsoleType.ExtendedConsoleType => "Extended Console Type",
_ => $"Unknown {(byte)type}",
};
}
/// <summary>
/// Convert a <see cref="CPUPPUTiming"/> value to string
/// </summary>

View File

@@ -2,47 +2,6 @@ using System;
namespace SabreTools.Data.Models.NES
{
/// <summary>
/// Mapper, VS/Playchoice, NES 2.0
/// </summary>
/// <remarks>Bits 4-7 are the upper nybble of mapper number</remarks>
[Flags]
public enum Flag7 : byte
{
#region Bits 0-1
/// <summary>
/// Nintendo Entertainment System/Family Computer
/// </summary>
StandardSystem = 0b0000,
/// <summary>
/// VS Unisystem
/// </summary>
VSUnisystem = 0b0001,
/// <summary>
/// PlayChoice-10 (8 KB of Hint Screen data stored after CHR data)
/// </summary>
PlayChoice10 = 0b0010,
/// <summary>
/// Extended Console Type
/// </summary>
ExtendedConsoleType = 0b0011,
#endregion
#region Bits 2-3
/// <summary>
/// If equal to 2, flags 8-15 are in NES 2.0 format
/// </summary>
NES20 = 0b1000,
#endregion
}
/// <summary>
/// TV system, PRG-RAM presence (unofficial, rarely used extension)
/// </summary>

View File

@@ -23,45 +23,49 @@ namespace SabreTools.Data.Models.NES
/// <remarks>Value 0 means the board uses CHR RAM</remarks>
public byte CHRROMSize { get; set; }
#region Flag 6
#region Byte 6
/// <summary>
/// Nametable arrangement
/// </summary>
/// <remarks>Flag 6, Bit 0</remarks>
/// <remarks>Byte 6, Bit 0</remarks>
public NametableArrangement NametableArrangement { get; set; }
/// <summary>
/// Cartridge contains battery-backed PRG RAM ($6000-7FFF)
/// or other persistent memory
/// </summary>
/// <remarks>Flag 6, Bit 1</remarks>
/// <remarks>Byte 6, Bit 1</remarks>
public bool BatteryBackedPRGRAM { get; set; }
/// <summary>
/// 512-byte trainer at $7000-$71FF
/// </summary>
/// <remarks>Flag 6, Bit 2</remarks>
/// <remarks>Byte 6, Bit 2</remarks>
public bool TrainerPresent { get; set; }
/// <summary>
/// Alternative nametable layout
/// </summary>
/// <remarks>Flag 6, Bit 3</remarks>
/// <remarks>Byte 6, Bit 3</remarks>
public bool AlternativeNametableLayout { get; set; }
/// <summary>
/// Lower nibble of Mapper number
/// </summary>
/// <remarks>Flag 6, Bits 4-7</remarks>
/// <remarks>Byte 6, Bits 4-7</remarks>
public byte MapperLowerNibble { get; set; }
#endregion
#region Byte 7
/// <summary>
/// Mapper, VS/Playchoice, NES 2.0
/// Nametable arrangement
/// </summary>
/// <remarks>
/// Byte 7, Bits 0-1
///
/// 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
@@ -69,10 +73,26 @@ namespace SabreTools.Data.Models.NES
///
/// Vs. games have a coin slot and different palettes. The detection
/// of which palette a particular game uses is left unspecified.
/// </remarks>
public ConsoleType ConsoleType { get; set; }
/// <summary>
/// Indicates NES 2.0 format
/// </summary>
/// <remarks>
/// Byte 7, Bits 2-3 == 0x02
///
/// NES 2.0 is a more recent extension to the format that allows more
/// flexibility in ROM and RAM size, among other things.
/// </remarks>
public Flag7 Flag7 { get; set; }
public bool NES20 { get; set; }
/// <summary>
/// Upper nibble of Mapper number
/// </summary>
/// <remarks>Byte 7, Bits 4-7</remarks>
public byte MapperUpperNibble { get; set; }
#endregion
}
}

View File

@@ -60,13 +60,7 @@ namespace SabreTools.Serialization.Readers
cart.CHRROMData = data.ReadBytes(chrRomSize);
// Read the PlayChoice INST-ROM and PROM data, if necessary
#if NET20 || NET35
if ((cart.Header.Flag7 & Flag7.PlayChoice10) != 0
&& (cart.Header.Flag7 & Flag7.VSUnisystem) == 0)
#else
if (cart.Header.Flag7.HasFlag(Flag7.PlayChoice10)
&& !cart.Header.Flag7.HasFlag(Flag7.VSUnisystem))
#endif
if (cart.Header.ConsoleType == ConsoleType.PlayChoice10)
{
cart.PlayChoiceINSTROM = data.ReadBytes(8192);
cart.PlayChoicePROM = data.ReadBytes(32);
@@ -106,14 +100,13 @@ namespace SabreTools.Serialization.Readers
bool alternativeNametableLayout = ((flag6 >> 3) & 0x01) != 0;
byte mapperLowerNibble = (byte)(flag6 >> 4);
Flag7 flag7 = (Flag7)data.ReadByteValue();
byte flag7 = data.ReadByteValue();
ConsoleType consoleType = (ConsoleType)(flag7 & 0x03);
bool nes20 = ((flag7 >> 2) & 0x02) == 0x02;
byte mapperUpperNibble = (byte)(flag7 >> 4);
// NES 2.0
#if NET20 || NET35
if ((flag7 & Flag7.NES20) != 0)
#else
if (flag7.HasFlag(Flag7.NES20))
#endif
if (nes20)
{
var obj = new Header2();
@@ -128,7 +121,11 @@ namespace SabreTools.Serialization.Readers
obj.AlternativeNametableLayout = alternativeNametableLayout;
obj.MapperLowerNibble = mapperLowerNibble;
obj.Flag7 = flag7;
// Flag 7
obj.ConsoleType = consoleType;
obj.NES20 = nes20;
obj.MapperUpperNibble = mapperUpperNibble;
obj.MapperMSBSubmapper = data.ReadByteValue();
obj.PRGCHRMSB = data.ReadByteValue();
obj.PRGRAMEEPROMSize = data.ReadByteValue();
@@ -157,7 +154,11 @@ namespace SabreTools.Serialization.Readers
obj.AlternativeNametableLayout = alternativeNametableLayout;
obj.MapperLowerNibble = mapperLowerNibble;
obj.Flag7 = flag7;
// Flag 7
obj.ConsoleType = consoleType;
obj.NES20 = nes20;
obj.MapperUpperNibble = mapperUpperNibble;
obj.PRGRAMSize = data.ReadByteValue();
obj.TVSystem = (TVSystem)data.ReadByteValue();
obj.Flag10 = (Flag10)data.ReadByteValue();

View File

@@ -74,40 +74,15 @@ namespace SabreTools.Serialization.Wrappers
builder.AppendLine(" Flag 7:");
// Bits 0-1
#if NET20 || NET35
if ((header.Flag7 & Flag7.ExtendedConsoleType) != 0)
#else
if (header.Flag7.HasFlag(Flag7.ExtendedConsoleType))
#endif
builder.AppendLine(" System Type: Extended Console Type");
#if NET20 || NET35
else if ((header.Flag7 & Flag7.PlayChoice10) != 0)
#else
else if (header.Flag7.HasFlag(Flag7.PlayChoice10))
#endif
builder.AppendLine(" System Type: PlayChoice-10");
#if NET20 || NET35
else if ((header.Flag7 & Flag7.VSUnisystem) != 0)
#else
else if (header.Flag7.HasFlag(Flag7.VSUnisystem))
#endif
builder.AppendLine(" System Type: Vs. Unisystem");
else
builder.AppendLine(" System Type: Nintendo Entertainment System/Family Computer");
string consoleType = header.ConsoleType.FromConsoleType();
builder.AppendLine(consoleType, " System Type");
// Bits 2-3
#if NET20 || NET35
if ((header.Flag7 & Flag7.NES20) != 0)
#else
if (header.Flag7.HasFlag(Flag7.NES20))
#endif
builder.AppendLine(" NES 2.0: True");
else
builder.AppendLine(" NES 2.0: False");
builder.AppendLine(header.NES20, " NES 2.0");
#endregion
byte mapperNumber = (byte)((((byte)header.Flag7 >> 4) << 4) | header.MapperLowerNibble);
byte mapperNumber = (byte)((header.MapperUpperNibble << 4) | header.MapperLowerNibble);
builder.AppendLine(mapperNumber, " Mapper number");
if (header is Header1 header1)
@@ -175,7 +150,7 @@ namespace SabreTools.Serialization.Wrappers
// Byte 8
byte mapperMsb = (byte)(header2.MapperMSBSubmapper & 0x0F);
ushort extendedMapperNumber = (ushort)((mapperMsb << 8)
| (byte)((((byte)header.Flag7 >> 4) << 4)
| (byte)((header.MapperUpperNibble << 4)
| header.MapperLowerNibble));
byte submapperNumber = (byte)(header2.MapperMSBSubmapper >> 4);
@@ -221,21 +196,13 @@ namespace SabreTools.Serialization.Wrappers
builder.AppendLine(cpuTiming, " CPU timing");
// Byte 13
#if NET20 || NET35
if ((header.Flag7 & Flag7.ExtendedConsoleType) != 0)
#else
if (header.Flag7.HasFlag(Flag7.ExtendedConsoleType))
#endif
if (header.ConsoleType == ConsoleType.ExtendedConsoleType)
{
ExtendedConsoleType extendedConsoleType = (ExtendedConsoleType)(header2.ExtendedSystemType & 0x0F);
string extendedConsoleTypeString = extendedConsoleType.FromExtendedConsoleType();
builder.AppendLine(extendedConsoleTypeString, " Extended console type");
}
#if NET20 || NET35
else if ((header.Flag7 & Flag7.VSUnisystem) != 0)
#else
else if (header.Flag7.HasFlag(Flag7.VSUnisystem))
#endif
else if (header.ConsoleType == ConsoleType.VSUnisystem)
{
VsSystemType vsSystemType = (VsSystemType)(header2.ExtendedSystemType & 0x0F);
string vsSystemTypeString = vsSystemType.FromVsSystemType();

View File

@@ -48,107 +48,8 @@ namespace SabreTools.Serialization.Wrappers
}
}
/// <summary>
/// Indicates if the game is meant for an extended console type
/// </summary>
/// <remarks>Possibly only valid for NES 2.0</remarks>
public bool IsExtendedConsole
{
get
{
// Missing header
if (Header is null)
return false;
#if NET20 || NET35
return (Header.Flag7 & Flag7.ExtendedConsoleType) != 0;
#else
return Header.Flag7.HasFlag(Flag7.ExtendedConsoleType);
#endif
}
}
/// <summary>
/// Indicates if the cart is using an NES 2.0 header
/// </summary>
public bool IsNES20
{
get
{
// Missing header
if (Header is null)
return false;
#if NET20 || NET35
return (Header.Flag7 & Flag7.NES20) != 0;
#else
return Header.Flag7.HasFlag(Flag7.NES20);
#endif
}
}
/// <summary>
/// Indicates if the game is meant for PlayChoice-10
/// </summary>
public bool IsPlayChoice10
{
get
{
// Missing header
if (Header is null)
return false;
#if NET20 || NET35
return (Header.Flag7 & Flag7.PlayChoice10) != 0
&& (Header.Flag7 & Flag7.VSUnisystem) == 0;
#else
return Header.Flag7.HasFlag(Flag7.PlayChoice10)
&& !Header.Flag7.HasFlag(Flag7.VSUnisystem);
#endif
}
}
/// <summary>
/// Indicates if the game is meant for a standard console
/// </summary>
public bool IsStandardConsole
{
get
{
// Missing header
if (Header is null)
return false;
#if NET20 || NET35
return (Header.Flag7 & Flag7.PlayChoice10) == 0
&& (Header.Flag7 & Flag7.VSUnisystem) == 0;
#else
return !Header.Flag7.HasFlag(Flag7.PlayChoice10)
&& !Header.Flag7.HasFlag(Flag7.VSUnisystem);
#endif
}
}
/// <summary>
/// Indicates if the game is meant for Vs. Unisystem
/// </summary>
public bool IsVsUnisystem
{
get
{
// Missing header
if (Header is null)
return false;
#if NET20 || NET35
return (Header.Flag7 & Flag7.PlayChoice10) == 0
&& (Header.Flag7 & Flag7.VSUnisystem) != 0;
#else
return !Header.Flag7.HasFlag(Flag7.PlayChoice10)
&& Header.Flag7.HasFlag(Flag7.VSUnisystem);
#endif
}
}
/// <inheritdoc cref="Header.ConsoleType"/>
public ConsoleType ConsoleType => Header?.ConsoleType ?? ConsoleType.StandardSystem;
/// <summary>
/// Mapper number
@@ -162,7 +63,7 @@ namespace SabreTools.Serialization.Wrappers
if (Header is null)
return 0;
int mapperNumber = (((byte)Header.Flag7 >> 4) << 4) | Header.MapperLowerNibble;
int mapperNumber = (Header.MapperUpperNibble << 4) | Header.MapperLowerNibble;
if (Header is Header2 header2)
mapperNumber = ((header2.MapperMSBSubmapper & 0x0F) << 8) | mapperNumber;
@@ -174,6 +75,9 @@ namespace SabreTools.Serialization.Wrappers
public NametableArrangement NametableArrangement
=> Header?.NametableArrangement ?? NametableArrangement.Vertical;
/// <inheritdoc cref="Header.NES20"/>
public bool NES20 => Header?.NES20 ?? false;
/// <summary>
/// PRG-RAM size in bytes
/// </summary>
@@ -366,15 +270,11 @@ namespace SabreTools.Serialization.Wrappers
if (Header is null || Header is not Header2 header2)
return ExtendedConsoleType.RegularSystem;
#if NET20 || NET35
if ((Header.Flag7 & Flag7.ExtendedConsoleType) != 0)
#else
if (Header.Flag7.HasFlag(Flag7.ExtendedConsoleType))
#endif
return (ExtendedConsoleType)(header2.ExtendedSystemType & 0x0F);
// Invalid console type
if (ConsoleType != ConsoleType.ExtendedConsoleType)
return ExtendedConsoleType.RegularSystem;
// If flag is unset
return ExtendedConsoleType.RegularSystem;
return (ExtendedConsoleType)(header2.ExtendedSystemType & 0x0F);
}
}
@@ -440,15 +340,11 @@ namespace SabreTools.Serialization.Wrappers
if (Header is null || Header is not Header2 header2)
return VsHardwareType.VsUnisystem;
#if NET20 || NET35
else if ((Header.Flag7 & Flag7.VSUnisystem) != 0)
#else
else if (Header.Flag7.HasFlag(Flag7.VSUnisystem))
#endif
return (VsHardwareType)(header2.ExtendedSystemType >> 4);
// Invalid console type
if (ConsoleType != ConsoleType.VSUnisystem)
return VsHardwareType.VsUnisystem;
// If flag is unset
return VsHardwareType.VsUnisystem;
return (VsHardwareType)(header2.ExtendedSystemType >> 4);
}
}
@@ -465,15 +361,11 @@ namespace SabreTools.Serialization.Wrappers
if (Header is null || Header is not Header2 header2)
return VsSystemType.AnyRP2C03RC2C03Variant;
#if NET20 || NET35
else if ((Header.Flag7 & Flag7.VSUnisystem) != 0)
#else
else if (Header.Flag7.HasFlag(Flag7.VSUnisystem))
#endif
return (VsSystemType)(header2.ExtendedSystemType & 0x0F);
// Invalid console type
if (ConsoleType != ConsoleType.VSUnisystem)
return VsSystemType.AnyRP2C03RC2C03Variant;
// If flag is unset
return VsSystemType.AnyRP2C03RC2C03Variant;
return (VsSystemType)(header2.ExtendedSystemType & 0x0F);
}
}