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
}
};
}