Add disassembly of GradientTool

This commit is contained in:
2026-04-21 10:40:12 +02:00
commit 1ef84fd132
55 changed files with 8994 additions and 0 deletions

500
HDMA/Generator2.cs Normal file
View File

@@ -0,0 +1,500 @@
// Decompiled with JetBrains decompiler
// Type: SFXProductions.GradientTool.HDMA.Generator2
// Assembly: GradientTool, Version=0.8.2.1, Culture=neutral, PublicKeyToken=null
// MVID: 818AB9B3-796A-4A49-8B90-C00D066A321B
// Assembly location: C:\Users\aless\Downloads\gradient-tool-v0.8.2.1\GradientTool.exe
using System;
using System.Collections.Generic;
#nullable disable
namespace SFXProductions.GradientTool.HDMA;
internal static class Generator2
{
private const int c_col2 = 5;
private const int REG_43x0 = 17152;
private const int REG_43x2 = 17154;
private const int REG_43x4 = 17156;
private const string c_defaultName = "Gradient";
private static ushort ApplyChannelFlag(int n, ChannelType flag)
{
return (ushort) ((ChannelType) n | flag);
}
private static ushort GetNormalModeDataValue(double r, double g, double b, ChannelType channels)
{
int num1 = 0;
double num2 = 0.0;
bool flag;
if ((flag = channels == ChannelType.Brightness) || channels == ChannelType.Grey)
{
num2 = r + g + b;
num1 = 3;
}
else
{
if ((channels & ChannelType.Red) == ChannelType.Red)
{
num2 += r;
++num1;
}
if ((channels & ChannelType.Green) == ChannelType.Green)
{
num2 += g;
++num1;
}
if ((channels & ChannelType.Blue) == ChannelType.Blue)
{
num2 += b;
++num1;
}
}
return Generator2.ApplyChannelFlag(Generator2.GetV5(num2 / (double) num1, flag ? 15 : 31 /*0x1F*/), channels);
}
private static int GetV5(double v, int range = 31 /*0x1F*/)
{
return (int) (v.Clamp() * (double) range + 0.5);
}
private static ushort GetRGBChannelValue(double r, double g, double b)
{
return (ushort) (Generator2.GetV5(r) | Generator2.GetV5(g) << 5 | Generator2.GetV5(b) << 10);
}
internal static void CreateMode0Table(
double[] chR,
double[] chG,
double[] chB,
ChannelType channels,
DataPoint[] result)
{
for (int index = 0; index < result.Length; ++index)
result[index] = new DataPoint()
{
Data1 = Generator2.GetNormalModeDataValue(chR[index], chG[index], chB[index], channels)
};
}
internal static void CreateMode2Table(
double[] chR,
double[] chG,
double[] chB,
ChannelType ch1,
ChannelType ch2,
DataPoint[] result)
{
for (int index = 0; index < result.Length; ++index)
{
double r = chR[index];
double g = chG[index];
double b = chB[index];
result[index] = new DataPoint()
{
Data1 = Generator2.GetNormalModeDataValue(r, g, b, ch1),
Data2 = Generator2.GetNormalModeDataValue(r, g, b, ch2)
};
}
}
internal static void CreateScrollingTable(
double[] chR,
double[] chG,
double[] chB,
DataPoint[] result)
{
for (int index = 0; index < result.Length; ++index)
{
double v1 = chR[index];
double v2 = chG[index];
double v3 = chB[index];
result[index] = new DataPoint()
{
Data1 = Generator2.ApplyChannelFlag(Generator2.GetV5(v1), ChannelType.Red),
Data2 = Generator2.ApplyChannelFlag(Generator2.GetV5(v2), ChannelType.Green),
Data3 = Generator2.ApplyChannelFlag(Generator2.GetV5(v3), ChannelType.Blue)
};
}
}
internal static void CreateMode1Table(
double[] chR,
double[] chG,
double[] chB,
ushort paletteIndex,
DataPoint[] result)
{
for (int index = 0; index < result.Length; ++index)
{
double r = chR[index];
double g = chG[index];
double b = chB[index];
result[index] = new DataPoint()
{
Data1 = paletteIndex,
Data2 = Generator2.GetRGBChannelValue(r, g, b)
};
}
}
private static void DoNormalMode(
ref int srcI,
DataPoint[] src,
List<CompressedDataPoint> dst,
bool allowCountAbove128)
{
DataPoint dataPoint = src[srcI];
CompressedDataPoint compressedDataPoint = new CompressedDataPoint(dataPoint);
for (int index = srcI + 1; index < src.Length && src[index] == dataPoint && (allowCountAbove128 && compressedDataPoint.Count < (int) byte.MaxValue || compressedDataPoint.Count < 128 /*0x80*/); ++index)
++compressedDataPoint.Count;
dst.Add(compressedDataPoint);
srcI += compressedDataPoint.Count;
}
private static bool CheckForContinuousMode(
ref int srcI,
DataPoint[] src,
List<CompressedDataPoint> dst)
{
if (srcI + 2 >= src.Length || !(src[srcI] != src[srcI + 1]) || srcI + 3 >= src.Length || !(src[srcI + 1] != src[srcI + 2]))
return false;
CompressedDataPoint compressedDataPoint = new CompressedDataPoint(src[srcI]);
for (int index = srcI + 1; compressedDataPoint.Data.Count < (int) sbyte.MaxValue && index < src.Length; ++index)
{
DataPoint dataPoint = src[index];
if (index + 2 >= src.Length || dataPoint != src[index + 1])
compressedDataPoint.Data.Add(dataPoint);
else
break;
}
dst.Add(compressedDataPoint);
srcI += compressedDataPoint.Data.Count;
return true;
}
private static List<CompressedDataPoint> CompressTable(
DataPoint[] dataPoints,
bool canUseContinuousMode)
{
List<CompressedDataPoint> dst = new List<CompressedDataPoint>();
int srcI = 0;
while (srcI < dataPoints.Length)
{
if (!canUseContinuousMode || !Generator2.CheckForContinuousMode(ref srcI, dataPoints, dst))
Generator2.DoNormalMode(ref srcI, dataPoints, dst, false);
}
return dst;
}
internal static int GetNChannels(GradientChannels channels)
{
switch (channels)
{
case GradientChannels.RedGreenBlue:
return 3;
case GradientChannels.RedGreen:
case GradientChannels.GreenBlue:
case GradientChannels.RedBlue:
case GradientChannels.CyanRed:
case GradientChannels.YellowBlue:
case GradientChannels.MagentaGreen:
return 2;
case GradientChannels.Red:
case GradientChannels.Green:
case GradientChannels.Blue:
case GradientChannels.Cyan:
case GradientChannels.Yellow:
case GradientChannels.Magenta:
case GradientChannels.Grey:
case GradientChannels.Brightness:
return 1;
default:
throw new ArgumentException("Invalid channels.");
}
}
internal static ChannelType GetChannel(GradientChannels channels, int index)
{
switch (channels)
{
case GradientChannels.RedGreenBlue:
if (index == 0)
return ChannelType.Red;
return index != 1 ? ChannelType.Blue : ChannelType.Green;
case GradientChannels.RedGreen:
return index != 0 ? ChannelType.Green : ChannelType.Red;
case GradientChannels.GreenBlue:
return index != 0 ? ChannelType.Blue : ChannelType.Green;
case GradientChannels.RedBlue:
return index != 0 ? ChannelType.Blue : ChannelType.Red;
case GradientChannels.CyanRed:
return index != 0 ? ChannelType.Red : ChannelType.Cyan;
case GradientChannels.YellowBlue:
return index != 0 ? ChannelType.Blue : ChannelType.Yellow;
case GradientChannels.MagentaGreen:
return index != 0 ? ChannelType.Green : ChannelType.Magenta;
case GradientChannels.Red:
return ChannelType.Red;
case GradientChannels.Green:
return ChannelType.Green;
case GradientChannels.Blue:
return ChannelType.Blue;
case GradientChannels.Cyan:
return ChannelType.Cyan;
case GradientChannels.Yellow:
return ChannelType.Yellow;
case GradientChannels.Magenta:
return ChannelType.Magenta;
case GradientChannels.Grey:
return ChannelType.Grey;
case GradientChannels.Brightness:
return ChannelType.Brightness;
default:
throw new ArgumentException("Invalid channels.");
}
}
public static void GenerateCode(
double[] rBuffer,
double[] gBuffer,
double[] bBuffer,
Settings2 settings,
CodeGen code)
{
uint size = 0;
List<CompressedDataPoint> outTable1 = (List<CompressedDataPoint>) null;
List<CompressedDataPoint> outTable2 = (List<CompressedDataPoint>) null;
List<CompressedDataPoint> outTable3 = (List<CompressedDataPoint>) null;
DataPoint[] tmpBuffer = new DataPoint[rBuffer.Length];
Generator2.DoChan(settings.Channel1, rBuffer, gBuffer, bBuffer, tmpBuffer, ref size, ref outTable1);
Generator2.DoChan(settings.Channel2, rBuffer, gBuffer, bBuffer, tmpBuffer, ref size, ref outTable2);
Generator2.DoChan(settings.Channel3, rBuffer, gBuffer, bBuffer, tmpBuffer, ref size, ref outTable3);
code.WriteBanner();
code.WriteComment(settings.GetGradientTypeBanner());
code.WriteComment("Channels: " + GradientChannelsHelper.GetChannelString(settings.Channels));
code.WriteComment("Table Size: " + size.ToString("N0"));
code.WriteComment("No. of Writes: " + rBuffer.Length.ToString("N0"));
code.WriteComment();
code.WriteComment("Generated by GradientTool");
code.WriteBanner();
Generator2.WriteActivator(settings, code);
string gradientName = settings.Name ?? "Gradient";
Generator2.WriteChan(settings.Channel1, outTable1, code, gradientName);
Generator2.WriteChan(settings.Channel2, outTable2, code, gradientName);
Generator2.WriteChan(settings.Channel3, outTable3, code, gradientName);
}
private static void DoChan(
Channel chan,
double[] rBuffer,
double[] gBuffer,
double[] bBuffer,
DataPoint[] tmpBuffer,
ref uint size,
ref List<CompressedDataPoint> outTable)
{
if (chan == null)
return;
chan.GetRawData(rBuffer, gBuffer, bBuffer, tmpBuffer);
outTable = Generator2.CompressTable(tmpBuffer, chan.CanUseContinuousMode);
size += chan.CalculateTableSize((IList<CompressedDataPoint>) outTable) + 1U;
}
private static void WriteChan(
Channel chan,
List<CompressedDataPoint> table,
CodeGen code,
string gradientName)
{
if (chan == null)
return;
code.AppendLine();
chan.WriteData((IList<CompressedDataPoint>) table, code, gradientName);
}
private static void WriteActivator(Settings2 settings, CodeGen code)
{
if (!settings.GenerateInitializationCode)
return;
code.AppendLine();
if (settings.Channel1 is CombinedChannel)
{
code.WriteComment("GradientTool doesnt generate the code");
code.WriteComment("to initialize scrolling gradients.");
code.WriteComment();
code.WriteComment("See the ASM files in the folder titled");
code.WriteComment("Example, located in the same folder as");
code.WriteComment("GradientTool.");
}
else if (settings.Channel1 is Mode3Channel)
Generator2.WriteActivatorInternal(settings, (ushort) 8451, (ushort) 8451, (ushort) 8451, code);
else if (settings.Channel1 is Mode0Channel)
{
ushort num = settings.Channels != GradientChannels.Brightness ? (ushort) 12800 : (ushort) 0;
Generator2.WriteActivatorInternal(settings, num, num, num, code);
}
else if (settings.Channel1 is Mode2Channel)
{
Generator2.WriteActivatorInternal(settings, (ushort) 12802, (ushort) 12800, (ushort) 12800, code);
}
else
{
code.WriteComment("Something went wrong and the type of");
code.WriteComment("HDMA gradient being generated couldnt");
code.WriteComment("be determined.");
}
}
private static void WriteActivatorInternal(
Settings2 settings,
ushort transferMode0,
ushort transferMode1,
ushort transferMode2,
CodeGen code)
{
string chans = Generator2.GetChans(settings);
code.WriteComment("Set up the HDMA gradient.");
code.WriteComment($"Uses HDMA {chans}{(object) '.'}");
code.AppendLabel("Init" + (settings.Name ?? "Gradient"));
code.AppendPlainText(':');
code.AppendLine();
code.Write16BitMode(5);
code.AppendLine();
code.WriteComment("Set transfer modes.", (true ? 1 : 0) != 0);
code.WriteLDAIfNonzero(transferMode0, 5);
CodeGen codeGen1 = code;
int num1 = 5;
string chanStr1 = Generator2.GetChanStr(settings.Channel1);
int register1 = (int) Generator2.GetRegister(17152, settings.Channel1);
int num2 = (int) transferMode0;
int w1 = num1;
string comment1 = chanStr1;
codeGen1.WriteSTAIfNonzero((ushort) register1, (ushort) num2, w1, comment1);
if (settings.Channel2 != null)
{
if ((int) transferMode1 != (int) transferMode0)
{
CodeGen codeGen2 = code;
int num3 = 5;
int num4 = (int) transferMode1;
int w2 = num3;
codeGen2.WriteLDAIfNonzero((ushort) num4, w2);
}
CodeGen codeGen3 = code;
int num5 = 5;
string chanStr2 = Generator2.GetChanStr(settings.Channel2);
int register2 = (int) Generator2.GetRegister(17152, settings.Channel2);
int num6 = (int) transferMode1;
int w3 = num5;
string comment2 = chanStr2;
codeGen3.WriteSTAIfNonzero((ushort) register2, (ushort) num6, w3, comment2);
if (settings.Channel3 != null)
{
if ((int) transferMode2 != (int) transferMode1)
{
CodeGen codeGen4 = code;
int num7 = 5;
int num8 = (int) transferMode2;
int w4 = num7;
codeGen4.WriteLDAIfNonzero((ushort) num8, w4);
}
CodeGen codeGen5 = code;
int num9 = 5;
string chanStr3 = Generator2.GetChanStr(settings.Channel3);
int register3 = (int) Generator2.GetRegister(17152, settings.Channel3);
int num10 = (int) transferMode2;
int w5 = num9;
string comment3 = chanStr3;
codeGen5.WriteSTAIfNonzero((ushort) register3, (ushort) num10, w5, comment3);
}
}
code.AppendLine();
code.WriteComment("Point to HDMA tables.", (true ? 1 : 0) != 0);
Generator2.WriteChanSourceLoader(settings.Channel1, settings.Name, code);
Generator2.WriteChanSourceLoader(settings.Channel2, settings.Name, code);
Generator2.WriteChanSourceLoader(settings.Channel3, settings.Name, code);
code.AppendLine();
code.Write8BitMode(5);
code.AppendLine();
code.WriteComment("Store program bank to $43x4.", (true ? 1 : 0) != 0);
code.WriteKeywordLine("PHK");
code.WriteKeywordLine("PLA");
Generator2.WriteChanBankLoader(settings.Channel1, code);
Generator2.WriteChanBankLoader(settings.Channel2, code);
Generator2.WriteChanBankLoader(settings.Channel3, code);
code.AppendLine();
CodeGen codeGen6 = code;
bool flag = true;
string comment4 = $"Enable {chans}{(object) '.'}";
int num11 = flag ? 1 : 0;
codeGen6.WriteComment(comment4, num11 != 0);
code.WriteLDA_b_c_bin(Generator2.GetHDMAFlags(settings), 5);
code.WriteTSB((ushort) 3487, 5);
code.AppendLine();
code.AppendHardTab();
code.AppendKeyword("RTS");
code.WriteComment("<-- Can also be RTL.", space: (true ? 1 : 0) != 0);
code.AppendLine();
code.AppendComment("; --- HDMA Tables below this line ---");
}
private static string GetChans(Settings2 settings)
{
if (settings.Channel2 == null)
return "channel " + settings.Channel1.HDMAChannel.ToString();
return settings.Channel3 != null ? $"channels {settings.Channel1.HDMAChannel}, {settings.Channel2.HDMAChannel}, and {settings.Channel3.HDMAChannel}" : $"channels {settings.Channel1.HDMAChannel} and {settings.Channel2.HDMAChannel}";
}
private static string GetChanStr(Channel channel) => "Channel " + channel.HDMAChannel.ToString();
private static ushort GetRegister(int reg, int channel) => (ushort) (reg | (channel & 7) << 4);
private static ushort GetRegister(int reg, Channel channel)
{
return (ushort) (reg | (channel.HDMAChannel & 7) << 4);
}
private static byte GetHDMAFlags(Settings2 settings)
{
int hdmaFlags = 1 << (settings.Channel1.HDMAChannel & 7);
if (settings.Channel2 != null)
{
hdmaFlags |= 1 << (settings.Channel2.HDMAChannel & 7);
if (settings.Channel3 != null)
hdmaFlags |= 1 << (settings.Channel3.HDMAChannel & 7);
}
return (byte) hdmaFlags;
}
private static void WriteChanSourceLoader(Channel channel, string gradientName, CodeGen code)
{
if (channel == null)
return;
string channelLabel = channel.GetChannelLabel(gradientName ?? "Gradient");
CodeGen codeGen1 = code;
int num1 = 5;
string label = channelLabel;
int w1 = num1;
codeGen1.WriteLDA_Label(label, w1);
CodeGen codeGen2 = code;
int num2 = 5;
int register = (int) Generator2.GetRegister(17154, channel);
int w2 = num2;
codeGen2.WriteSTA((ushort) register, w2);
}
private static void WriteChanBankLoader(Channel channel, CodeGen code)
{
if (channel == null)
return;
CodeGen codeGen = code;
int num = 5;
string chanStr = Generator2.GetChanStr(channel);
int register = (int) Generator2.GetRegister(17156, channel);
int w = num;
string comment = chanStr;
codeGen.WriteSTA((ushort) register, w, comment);
}
}