diff --git a/DotN64/DotN64.csproj b/DotN64/DotN64.csproj index 0f2e613..1f5c49a 100644 --- a/DotN64/DotN64.csproj +++ b/DotN64/DotN64.csproj @@ -95,6 +95,12 @@ + + + + + + diff --git a/DotN64/RCP/VI/RealityCoprocessor.VideoInterface.ControlRegister.cs b/DotN64/RCP/VI/RealityCoprocessor.VideoInterface.ControlRegister.cs new file mode 100644 index 0000000..b9c2fcd --- /dev/null +++ b/DotN64/RCP/VI/RealityCoprocessor.VideoInterface.ControlRegister.cs @@ -0,0 +1,108 @@ +using System; +using System.Collections.Specialized; + +namespace DotN64.RCP +{ + public partial class RealityCoprocessor + { + public partial class VideoInterface + { + public struct ControlRegister + { + #region Fields + private BitVector32 bits; + + private static readonly BitVector32.Section type = BitVector32.CreateSection((1 << 2) - 1), + gammaDitherEnable = BitVector32.CreateSection(1, type), + gammaEnable = BitVector32.CreateSection(1, gammaDitherEnable), + divotEnable = BitVector32.CreateSection(1, gammaEnable), + reserved1 = BitVector32.CreateSection(1, divotEnable), // Always off. + serrate = BitVector32.CreateSection(1, reserved1), + reserved2 = BitVector32.CreateSection(1, serrate), // Diagnostics only. + antiAliasMode = BitVector32.CreateSection((1 << 2) - 1, reserved2); + #endregion + + #region Properties + public PixelSize Type + { + get => (PixelSize)bits[type]; + set => bits[type] = (byte)value; + } + + /// + /// Normally on, unless "special effect". + /// + public bool GammaDitherEnable + { + get => Convert.ToBoolean(bits[gammaDitherEnable]); + set => bits[gammaDitherEnable] = Convert.ToInt32(value); + } + + /// + /// Normally on, unless MPEG/JPEG. + /// + public bool GammaEnable + { + get => Convert.ToBoolean(bits[gammaEnable]); + set => bits[gammaEnable] = Convert.ToInt32(value); + } + + /// + /// Normally on if antialiased, unless decal lines. + /// + public bool DivotEnable + { + get => Convert.ToBoolean(bits[divotEnable]); + set => bits[divotEnable] = Convert.ToInt32(value); + } + + /// + /// Always on if interlaced, off if not. + /// + public bool Serrate + { + get => Convert.ToBoolean(bits[serrate]); + set => bits[serrate] = Convert.ToInt32(value); + } + + public AntiAliasingMode AntiAliasMode + { + get => (AntiAliasingMode)bits[antiAliasMode]; + set => bits[antiAliasMode] = (byte)value; + } + #endregion + + #region Operators + public static implicit operator ControlRegister(ushort data) => new ControlRegister { bits = new BitVector32(data) }; + + public static implicit operator ushort(ControlRegister status) => (ushort)status.bits.Data; + #endregion + + #region Enumerations + public enum PixelSize : byte + { + /// No data, no sync. + Blank = 0, + Reserved = 1, + /// "16" bit. + RGBA5553 = 2, + /// 32 bit. + RGBA8888 = 3 + } + + public enum AntiAliasingMode : byte + { + /// AA & resamp (always fetch extra lines). + AntiAliasAndResampleAlways = 0, + /// AA & resamp (fetch extra lines if needed). + AntiAliasAndResampleIfNeeded = 1, + /// Resamp only (treat as all fully covered). + ResampleOnly = 2, + /// Neither (replicate pixels, no interpolate). + Neither = 3 + } + #endregion + } + } + } +} diff --git a/DotN64/RCP/VI/RealityCoprocessor.VideoInterface.HorizontalSyncRegister.cs b/DotN64/RCP/VI/RealityCoprocessor.VideoInterface.HorizontalSyncRegister.cs new file mode 100644 index 0000000..db88928 --- /dev/null +++ b/DotN64/RCP/VI/RealityCoprocessor.VideoInterface.HorizontalSyncRegister.cs @@ -0,0 +1,46 @@ +namespace DotN64.RCP +{ + using static Helpers.BitHelper; + + public partial class RealityCoprocessor + { + public partial class VideoInterface + { + public struct HorizontalSyncRegister + { + #region Fields + private uint data; + + private const ushort TotalLineDurationShift = 0, TotalLineDurationSize = (1 << 12) - 1; + private const byte LeapPatternShift = 16, LeapPatternSize = (1 << 5) - 1; + #endregion + + #region Properties + /// + /// Total duration of a line in 1/4 pixel. + /// + public ushort TotalLineDuration + { + get => (ushort)Get(data, TotalLineDurationShift, TotalLineDurationSize); + set => Set(ref data, TotalLineDurationShift, TotalLineDurationSize, value); + } + + /// + /// A 5-bit leap pattern used for PAL only (h_sync_period). + /// + public byte LeapPattern + { + get => (byte)Get(data, LeapPatternShift, LeapPatternSize); + set => Set(ref data, LeapPatternShift, LeapPatternSize, value); + } + #endregion + + #region Operators + public static implicit operator HorizontalSyncRegister(uint data) => new HorizontalSyncRegister { data = data }; + + public static implicit operator uint(HorizontalSyncRegister register) => register.data; + #endregion + } + } + } +} diff --git a/DotN64/RCP/VI/RealityCoprocessor.VideoInterface.HorizontalVideoRegister.cs b/DotN64/RCP/VI/RealityCoprocessor.VideoInterface.HorizontalVideoRegister.cs index 0499cb4..cce736e 100644 --- a/DotN64/RCP/VI/RealityCoprocessor.VideoInterface.HorizontalVideoRegister.cs +++ b/DotN64/RCP/VI/RealityCoprocessor.VideoInterface.HorizontalVideoRegister.cs @@ -1,7 +1,7 @@ -using System.Collections.Specialized; - -namespace DotN64.RCP +namespace DotN64.RCP { + using static Helpers.BitHelper; + public partial class RealityCoprocessor { public partial class VideoInterface @@ -9,11 +9,10 @@ namespace DotN64.RCP public struct HorizontalVideoRegister { #region Fields - private BitVector32 bits; + private uint data; - private static readonly BitVector32.Section activeVideoEnd = BitVector32.CreateSection((1 << 10) - 1), - unknown1 = BitVector32.CreateSection((1 << 6) - 1, activeVideoEnd), - activeVideoStart = BitVector32.CreateSection((1 << 10) - 1, unknown1); + private const ushort ActiveVideoEndShift = 0, ActiveVideoEndSize = (1 << 10) - 1; + private const ushort ActiveVideoStartShift = 16, ActiveVideoStartSize = (1 << 10) - 1; #endregion #region Properties @@ -22,8 +21,8 @@ namespace DotN64.RCP /// public ushort ActiveVideoEnd { - get => (ushort)bits[activeVideoEnd]; - set => bits[activeVideoEnd] = value; + get => (ushort)Get(data, ActiveVideoEndShift, ActiveVideoEndSize); + set => Set(ref data, ActiveVideoEndShift, ActiveVideoEndSize, value); } /// @@ -31,15 +30,15 @@ namespace DotN64.RCP /// public ushort ActiveVideoStart { - get => (ushort)bits[activeVideoStart]; - set => bits[activeVideoStart] = value; + get => (ushort)Get(data, ActiveVideoStartShift, ActiveVideoStartSize); + set => Set(ref data, ActiveVideoStartShift, ActiveVideoStartSize, value); } #endregion #region Operators - public static implicit operator HorizontalVideoRegister(uint data) => new HorizontalVideoRegister { bits = new BitVector32((int)data) }; + public static implicit operator HorizontalVideoRegister(uint data) => new HorizontalVideoRegister { data = data }; - public static implicit operator uint(HorizontalVideoRegister register) => (uint)register.bits.Data; + public static implicit operator uint(HorizontalVideoRegister register) => register.data; #endregion } } diff --git a/DotN64/RCP/VI/RealityCoprocessor.VideoInterface.ScaleRegister.cs b/DotN64/RCP/VI/RealityCoprocessor.VideoInterface.ScaleRegister.cs new file mode 100644 index 0000000..17772c7 --- /dev/null +++ b/DotN64/RCP/VI/RealityCoprocessor.VideoInterface.ScaleRegister.cs @@ -0,0 +1,46 @@ +namespace DotN64.RCP +{ + using static Helpers.BitHelper; + + public partial class RealityCoprocessor + { + public partial class VideoInterface + { + public struct ScaleRegister + { + #region Fields + private uint data; + + private const ushort ScaleUpFactorShift = 0, ScaleUpFactorSize = (1 << 12) - 1; + private const ushort SubpixelOffsetShift = 16, SubpixelOffsetSize = (1 << 12) - 1; + #endregion + + #region Properties + /// + /// 1/scale up factor (2.10 format). + /// + public ushort ScaleUpFactor + { + get => (ushort)Get(data, ScaleUpFactorShift, ScaleUpFactorSize); + set => Set(ref data, ScaleUpFactorShift, ScaleUpFactorSize, value); + } + + /// + /// Subpixel offset (2.10 format). + /// + public ushort SubpixelOffset + { + get => (ushort)Get(data, SubpixelOffsetShift, SubpixelOffsetSize); + set => Set(ref data, SubpixelOffsetShift, SubpixelOffsetSize, value); + } + #endregion + + #region Operators + public static implicit operator ScaleRegister(uint data) => new ScaleRegister { data = data }; + + public static implicit operator uint(ScaleRegister register) => register.data; + #endregion + } + } + } +} diff --git a/DotN64/RCP/VI/RealityCoprocessor.VideoInterface.TimingRegister.cs b/DotN64/RCP/VI/RealityCoprocessor.VideoInterface.TimingRegister.cs new file mode 100644 index 0000000..ca5aed1 --- /dev/null +++ b/DotN64/RCP/VI/RealityCoprocessor.VideoInterface.TimingRegister.cs @@ -0,0 +1,66 @@ +using System.Collections.Specialized; + +namespace DotN64.RCP +{ + public partial class RealityCoprocessor + { + public partial class VideoInterface + { + public struct TimingRegister + { + #region Fields + private BitVector32 bits; + + private static readonly BitVector32.Section horizontalSyncWidth = BitVector32.CreateSection((1 << 8) - 1), + colorBurstWidth = BitVector32.CreateSection((1 << 8) - 1, horizontalSyncWidth), + verticalSyncWidth = BitVector32.CreateSection((1 << 4) - 1, colorBurstWidth), + colorBurstStart = BitVector32.CreateSection((1 << 10) - 1, verticalSyncWidth); + #endregion + + #region Properties + /// + /// Horizontal sync width in pixels. + /// + public byte HorizontalSyncWidth + { + get => (byte)bits[horizontalSyncWidth]; + set => bits[horizontalSyncWidth] = value; + } + + /// + /// Color burst width in pixels. + /// + public byte ColorBurstWidth + { + get => (byte)bits[colorBurstWidth]; + set => bits[colorBurstWidth] = value; + } + + /// + /// Vertical sync width in half lines. + /// + public byte VerticalSyncWidth + { + get => (byte)bits[verticalSyncWidth]; + set => bits[verticalSyncWidth] = value; + } + + /// + /// Start of color burst in pixels from h-sync. + /// + public ushort ColorBurstStart + { + get => (ushort)bits[colorBurstStart]; + set => bits[colorBurstStart] = value; + } + #endregion + + #region Operators + public static implicit operator TimingRegister(uint data) => new TimingRegister { bits = new BitVector32((int)data) }; + + public static implicit operator uint(TimingRegister timing) => (uint)timing.bits.Data; + #endregion + } + } + } +} diff --git a/DotN64/RCP/VI/RealityCoprocessor.VideoInterface.VerticalBurstRegister.cs b/DotN64/RCP/VI/RealityCoprocessor.VideoInterface.VerticalBurstRegister.cs new file mode 100644 index 0000000..9f49e0a --- /dev/null +++ b/DotN64/RCP/VI/RealityCoprocessor.VideoInterface.VerticalBurstRegister.cs @@ -0,0 +1,46 @@ +namespace DotN64.RCP +{ + using static Helpers.BitHelper; + + public partial class RealityCoprocessor + { + public partial class VideoInterface + { + public struct VerticalBurstRegister + { + #region Fields + private uint data; + + private const ushort ColorBurstEnableEndShift = 0, ColorBurstEnableEndSize = (1 << 10) - 1; + private const ushort ColorBurstEnableStartShift = 16, ColorBurstEnableStartSize = (1 << 10) - 1; + #endregion + + #region Properties + /// + /// End of color burst enable in half-lines. + /// + public ushort ColorBurstEnableEnd + { + get => (ushort)Get(data, ColorBurstEnableEndShift, ColorBurstEnableEndSize); + set => Set(ref data, ColorBurstEnableEndShift, ColorBurstEnableEndSize, value); + } + + /// + /// Start of color burst enable in half-lines. + /// + public ushort ColorBurstEnableStart + { + get => (ushort)Get(data, ColorBurstEnableStartShift, ColorBurstEnableStartSize); + set => Set(ref data, ColorBurstEnableStartShift, ColorBurstEnableStartSize, value); + } + #endregion + + #region Operators + public static implicit operator VerticalBurstRegister(uint data) => new VerticalBurstRegister { data = data }; + + public static implicit operator uint(VerticalBurstRegister register) => register.data; + #endregion + } + } + } +} diff --git a/DotN64/RCP/VI/RealityCoprocessor.VideoInterface.VerticalVideoRegister.cs b/DotN64/RCP/VI/RealityCoprocessor.VideoInterface.VerticalVideoRegister.cs new file mode 100644 index 0000000..f1a2a88 --- /dev/null +++ b/DotN64/RCP/VI/RealityCoprocessor.VideoInterface.VerticalVideoRegister.cs @@ -0,0 +1,46 @@ +namespace DotN64.RCP +{ + using static Helpers.BitHelper; + + public partial class RealityCoprocessor + { + public partial class VideoInterface + { + public struct VerticalVideoRegister + { + #region Fields + private uint data; + + private const ushort ActiveVideoEndShift = 0, ActiveVideoEndSize = (1 << 10) - 1; + private const ushort ActiveVideoStartShift = 16, ActiveVideoStartSize = (1 << 10) - 1; + #endregion + + #region Properties + /// + /// End of active video in screen half-lines. + /// + public ushort ActiveVideoEnd + { + get => (ushort)Get(data, ActiveVideoEndShift, ActiveVideoEndSize); + set => Set(ref data, ActiveVideoEndShift, ActiveVideoEndSize, value); + } + + /// + /// Start of active video in screen half-lines. + /// + public ushort ActiveVideoStart + { + get => (ushort)Get(data, ActiveVideoStartShift, ActiveVideoStartSize); + set => Set(ref data, ActiveVideoStartShift, ActiveVideoStartSize, value); + } + #endregion + + #region Operators + public static implicit operator VerticalVideoRegister(uint data) => new VerticalVideoRegister { data = data }; + + public static implicit operator uint(VerticalVideoRegister register) => register.data; + #endregion + } + } + } +} diff --git a/DotN64/RCP/VI/RealityCoprocessor.VideoInterface.cs b/DotN64/RCP/VI/RealityCoprocessor.VideoInterface.cs index bbfd798..64aa809 100644 --- a/DotN64/RCP/VI/RealityCoprocessor.VideoInterface.cs +++ b/DotN64/RCP/VI/RealityCoprocessor.VideoInterface.cs @@ -16,6 +16,41 @@ /// Current half line, sampled once per line (the lsb of V_CURRENT is constant within a field, and in interlaced modes gives the field number - which is constant for non-interlaced modes). /// public ushort CurrentVerticalLine { get; set; } + + public ControlRegister Control { get; set; } + + /// + /// Frame buffer origin in bytes. + /// + public uint DRAMAddress { get; set; } + + /// + /// Frame buffer line width in pixels. + /// + public ushort Width { get; set; } + + public TimingRegister Timing { get; set; } + + /// + /// Number of half-lines per field. + /// + public ushort VerticalSync { get; set; } + + public HorizontalSyncRegister HorizontalSync { get; set; } + + /// + /// [11:0] identical to h_sync_period. + /// [27:16] identical to h_sync_period. + /// + public uint HorizontalSyncLeap { get; set; } + + public VerticalVideoRegister VerticalVideo { get; set; } + + public VerticalBurstRegister VerticalBurst { get; set; } + + public ScaleRegister HorizontalScale { get; set; } + + public ScaleRegister VerticalScale { get; set; } #endregion #region Constructors @@ -24,14 +59,22 @@ { MemoryMaps = new[] { + new MappingEntry(0x04400000, 0x04400003) // VI status/control. + { + Write = (o, d) => Control = (ushort)d + }, + new MappingEntry(0x04400004, 0x04400007) // VI origin. + { + Write = (o, d) => DRAMAddress = d & ((1 << 24) - 1) + }, + new MappingEntry(0x04400008, 0x0440000B) // VI width. + { + Write = (o, d) => Width = (ushort)(d & ((1 << 12) - 1)) + }, new MappingEntry(0x0440000C, 0x0440000F) // VI vertical intr. { Write = (o, d) => VerticalInterrupt = (ushort)(d & ((1 << 10) - 1)) }, - new MappingEntry(0x04400024, 0x04400027) // VI horizontal video. - { - Write = (o, d) => HorizontalVideo = d - }, new MappingEntry(0x04400010, 0x04400013) // VI current vertical line. { Write = (o, d) => @@ -39,6 +82,42 @@ CurrentVerticalLine = (ushort)(d & ((1 << 10) - 1)); rcp.MI.Interrupt &= ~MIPSInterface.Interrupts.VI; } + }, + new MappingEntry(0x04400014, 0x04400017) // VI video timing. + { + Write = (o, d) => Timing = d + }, + new MappingEntry(0x04400018, 0x0440001B) // VI vertical sync. + { + Write = (o, d) => VerticalSync = (ushort)(d & ((1 << 10) - 1)) + }, + new MappingEntry(0x0440001C, 0x0440001F) // VI horizontal sync. + { + Write = (o, d) => HorizontalSync = d + }, + new MappingEntry(0x04400020, 0x04400023) // VI horizontal sync leap. + { + Write = (o, d) => HorizontalSyncLeap = d + }, + new MappingEntry(0x04400024, 0x04400027) // VI horizontal video. + { + Write = (o, d) => HorizontalVideo = d + }, + new MappingEntry(0x04400028, 0x0440002B) // VI vertical video. + { + Write = (o, d) => VerticalVideo = d + }, + new MappingEntry(0x0440002C, 0x0440002F) // VI vertical burst. + { + Write = (o, d) => VerticalBurst = d + }, + new MappingEntry(0x04400030, 0x04400033) // VI x-scale. + { + Write = (o, d) => HorizontalScale = d + }, + new MappingEntry(0x04400034, 0x04400037) // VI y-scale. + { + Write = (o, d) => VerticalScale = d } }; }