/// <summary> /// Calculates the code for the Brightness HDMA with the desired channel, Table and possibly SA-1 compatible /// </summary> /// <param name="channel"></param> /// <param name="table"></param> /// <param name="sa1"></param> /// <returns></returns> public override string Code(int channel, HDMATable table, bool sa1) { DMAMode mode = (table[0].Values.Length == 1 ? DMAMode.P : DMAMode.PP); ASMCodeBuilder code = new ASMCodeBuilder(); int channelAdd = 16 * channel; table.Name = ".BrightTable"; code.AppendLabel(INITLabel, "Code to be inserted INIT"); code.OpenNewBlock(); code.AppendCode("\tREP #$20", "16 bit mode"); code.AppendCode("\tLDA #$" + (((Registers.Brightness & 0xFF) << 8) + (int)mode).ToString("X4")); code.AppendCode("\tSTA $" + (Registers.DMAMode + channelAdd).ToString("X4")); code.AppendCode("\tLDA #" + table.Name, "load high and low byte of table address"); code.AppendCode("\tSTA $" + (Registers.DMALowByte + channelAdd).ToString("X4")); code.AppendCode("\tSEP #$20", "back to 8 bit mode"); code.AppendCode("\tLDA.b #" + table.Name + ">>16", "load bank byte of table address"); code.AppendCode("\tSTA $" + (Registers.DMABankByte + channelAdd).ToString("X4")); code.AppendCode("\tLDA #$" + (1 << channel).ToString("X2")); code.AppendCode("\tTSB $" + RAM.HDMAEnable[sa1].ToString("X4"), "enable HDMA channel " + channel); code.AppendCode("\tRTS"); code.CloseBlock(); code.AppendEmptyLine(); code.AppendTable(table); return(code.ToString()); }
public static bool Merge(HDMATable Table1, HDMATable Table2, out HDMATable Merged) { Merged = null; if (Table1.Count != Table2.Count) { return(false); } if (Table1.SequenceEqual(Table2, new HDMATableEntryComparer())) { Merged = new HDMATable(); for (int i = 0; i < Table1.Count; i++) { List <byte> values = new List <byte>(); values.AddRange(Table1[i].Values); values.AddRange(Table2[i].Values); Merged.Add(new HDMATableEntry(TableValueType.db, Table1[i].Scanlines, values.ToArray())); } if (!Merged.HasEnded()) { Merged.Add(HDMATableEntry.End); } return(true); } return(false); }
/// <summary> /// Calculates the code for overlapping multiple brightness gradients. /// <para>The final code will use the channel of the first BrightnessHDMA object passed.</para> /// </summary> /// <param name="hdma">An array of BrightnessHDMA object that will be overlapped for the final code.</param> /// <returns>The final code.</returns> public static string MultiCode(params BrightnessHDMA[] hdma) { if (hdma == null || hdma.Length == 0) { return(null); } int channel = hdma[0].Channel; EffectClasses.HDMATable table = new HDMATable(".BrightTable"); using (Bitmap b = BitmapEffects.OverlapImages(hdma.Select(h => h.EffectImage).ToArray())) { byte scanlines = 1; int now = 0; int compare = 0x0F - (b.GetPixel(0, 0).A / 17); for (int y = 1; y < b.Height; y++, scanlines++) { now = 0x0F - (b.GetPixel(0, y).A / 17); if (compare != now || scanlines >= 0x80) { table.Add(new HDMATableEntry(TableValueType.db, scanlines, (byte)compare)); compare = now; scanlines = 0; } } if (table.TotalScanlines != Scanlines) { table.Add(new HDMATableEntry(TableValueType.db, scanlines, (byte)now)); } table.Add(HDMATableEntry.End); DMAMode mode = (table[0].Values.Length == 1 ? DMAMode.P : DMAMode.PP); ASMCodeBuilder code = new ASMCodeBuilder(); int channelAdd = 16 * channel; table.Name = ".BrightTable"; code.AppendLabel(INITLabel, "Code to be inserted INIT"); code.OpenNewBlock(); code.AppendCode("\tREP #$20", "16 bit mode"); code.AppendCode("\tLDA #$" + (((Registers.Brightness & 0xFF) << 8) + (int)mode).ToString("X4")); code.AppendCode("\tSTA $" + (Registers.DMAMode + channelAdd).ToString("X4")); code.AppendCode("\tLDA #" + table.Name, "load high and low byte of table address"); code.AppendCode("\tSTA $" + (Registers.DMALowByte + channelAdd).ToString("X4")); code.AppendCode("\tSEP #$20", "back to 8 bit mode"); code.AppendCode("\tLDA.b #" + table.Name + ">>16", "load bank byte of table address"); code.AppendCode("\tSTA $" + (Registers.DMABankByte + channelAdd).ToString("X4")); code.AppendCode("\tLDA #$" + (1 << channel).ToString("X2")); code.AppendCode("\tTSB $" + RAM.HDMAEnable[RAM.SA1].ToString("X4"), "enable HDMA channel " + channel); code.AppendCode("\tRTS"); code.CloseBlock(); code.AppendEmptyLine(); code.AppendTable(table); return(code.ToString()); } }
public PixelationHDMA() { Values = new List <PixelScanline>(); Table = new HDMATable(".PixelTable"); ColorMath = new ColorMath(); OriginalImages = new BitmapCollection(); }
public void AppendTable(HDMATable table) { AppendLabel(table.Name); foreach (var entry in table) { AppendCode(entry.ToString()); } }
public void FromTable(HDMATable table) { if (!table.HasEnded()) { table.Add(HDMATableEntry.End); } Table = table; UpdateImage(); }
/// <summary> /// Draws the image and sets the internal table based on a string array. /// Returns every line that couldn't be translated. /// </summary> /// <param name="Lines">The lines that will be turned into an image</param> /// <param name="eightBit">Whether the values should be interpreted as 8 or 5 bit.</param> /// <returns>An array of error containing lines.</returns> public string[] FromString(string[] Lines, bool eightBit) { Table = new HDMATable(); List <string> errorLines = new List <string>(); foreach (string s in Lines) { try { string[] values = s.Split(", ".ToCharArray(), StringSplitOptions.RemoveEmptyEntries); if (values.Length < 2) { throw new ArgumentException("Too few arguments."); } byte[] bytes = new byte[values.Length - 1]; for (int i = 1; i < values.Length; i++) { if (!eightBit) { bytes[i - 1] = (byte)((byte)ColorEffect | (0x1F & values[i].ToHexInt())); } else { bytes[i - 1] = (byte)((byte)ColorEffect | values[i].ToHexInt() / 8); } } if (bytes.Any(b => b < (byte)ColorEffect)) { throw new Exception("Wrong Convertion"); } Table.Add(new HDMATableEntry(TableValueType.db, (byte)values[0].ToHexInt(), bytes)); if (Table.TotalScanlines > Scanlines) { break; } } catch (Exception ex) { #if (DEBUG) Console.WriteLine("ColorHDMA.FromString ex: " + ex.Message); #endif errorLines.Add(s); } } if (Table.TotalScanlines < Scanlines) { Table.Add(new HDMATableEntry(TableValueType.db, 1, (byte)ColorEffect)); } Table.Add(HDMATableEntry.End); UpdateImage(); return(errorLines.ToArray()); }
public void FromImage(Bitmap image) { if (Table == null) { Table = new HDMATable(); } else { Table.Clear(); } if ((Math.Log((double)ColorEffect) / Math.Log(2)) % 1 != 0) { throw new ArgumentException("Can't call method with ColorEffect having multiple colors.\nIt's currently set to: " + ColorEffect); } string color = ""; if (ColorEffect == ColorHDMAValues.Red) { color = "R"; } else if (ColorEffect == ColorHDMAValues.Green) { color = "G"; } else if (ColorEffect == ColorHDMAValues.Blue) { color = "B"; } else { throw new ArgumentException("No color selected"); } byte check_color = (byte)(Convert.ToInt32(typeof(Color).GetProperty(color).GetValue(image.GetPixel(0, 0), null)) / 8); for (int y = 1, lines = 1; y < image.Height; y++, lines++) { byte cur_color = (byte)(Convert.ToInt32(typeof(Color).GetProperty(color).GetValue(image.GetPixel(0, y), null)) / 8); if (cur_color != check_color || lines >= 0x80) { Table.Add(new HDMATableEntry(TableValueType.db, (byte)lines, (byte)((byte)ColorEffect | check_color))); lines = 0; check_color = cur_color; } } if (Table.TotalScanlines != Scanlines) { Table.Add(new HDMATableEntry(TableValueType.db, (byte)(Scanlines - Table.TotalScanlines), (byte)((byte)ColorEffect | check_color))); } Table.Add(HDMATableEntry.End); UpdateImage(); }
public string[] FromString(string[] lines) { Table = new HDMATable(); List <string> errorLines = new List <string>(); foreach (string s in lines) { try { string[] values = s.Split(",".ToCharArray(), StringSplitOptions.RemoveEmptyEntries); if (values.Length < 2) { throw new ArgumentException("Too few arguments."); } byte scanlines = (byte)values[0].ToHexInt(); if (scanlines > 0x80 && scanlines - 0x80 != values.Length - 1) { throw new ArgumentException("Invalid use of scanline counter $80+"); } byte[] bytes = new byte[values.Length - 1]; for (int i = 1; i < values.Length; i++) { bytes[i - 1] = (byte)(values[i].ToHexInt().Range(0, 15)); } Table.Add(new HDMATableEntry(TableValueType.db, scanlines, bytes)); if (Table.TotalScanlines > Scanlines) { break; } } catch (Exception ex) { #if (DEBUG) Console.WriteLine("BrightnessHDMA.FromString ex: " + ex.Message); #endif errorLines.Add(s); } } if (Table.TotalScanlines < Scanlines) { Table.Add(new HDMATableEntry(TableValueType.db, 1, 0xF)); } Table.Add(HDMATableEntry.End); UpdateImage(); return(errorLines.ToArray()); }
/// <summary> /// /// </summary> /// <param name="channel"></param> /// <param name="table"></param> /// <returns></returns> public override string Code(int Channel, HDMATable Table, bool SA1) { string tablename = ""; if (ColorEffect.HasFlag(ColorHDMAValues.Red)) { tablename += "Red"; } if (ColorEffect.HasFlag(ColorHDMAValues.Green)) { tablename += "Green"; } if (ColorEffect.HasFlag(ColorHDMAValues.Blue)) { tablename += "Blue"; } return(ColorHDMA.Code(Channel, Table, SA1, "." + tablename + "Table")); }
/// <summary> /// Checks if the color HDMA table contains date that verifies it's need in the final code /// </summary> /// <returns></returns> public bool IsEmpty() { if (Table == null) { Table = new HDMATable(); return(true); } foreach (HDMATableEntry entry in Table) { foreach (byte b in entry.Values) { if ((b & 0x1F) != 0) { return(false); } } } return(true); }
public override string Code(int channel, HDMATable table, bool SA1) { Setup(); ASMCodeBuilder CodeBuilder = new ASMCodeBuilder(); if (Values.Count == 1 && Values[0].Scanline == Scanlines) { CodeBuilder.AppendLabel(MAINLabel, "Caution, not INIT"); CodeBuilder.OpenNewBlock(); CodeBuilder.AppendCode("LDA #$" + table[0].Values[0].ToString("X2"), "pixelise BGs"); CodeBuilder.AppendCode("STA $" + Registers.Pixelation.ToString("X4")); CodeBuilder.AppendCode("RTS", "Return"); CodeBuilder.CloseBlock(); return(CodeBuilder.ToString()); } int channelAdd = 16 * channel; CodeBuilder.AppendLabel(INITLabel, "Code to be put in INIT ASM"); CodeBuilder.OpenNewBlock(); CodeBuilder.AppendCode("\tREP #$20"); CodeBuilder.AppendCode("\tLDA #$" + ((Registers.Pixelation & 0xFF) << 8).ToString("X4")); CodeBuilder.AppendCode("\tSTA $" + (Registers.DMAMode + channelAdd).ToString("X4")); CodeBuilder.AppendCode("\tLDA #" + table.Name); CodeBuilder.AppendCode("\tSTA $" + (Registers.DMALowByte + channelAdd).ToString("X4")); CodeBuilder.AppendCode("\tLDY.b #" + table.Name + ">>16"); CodeBuilder.AppendCode("\tSTY $" + (Registers.DMABankByte + channelAdd).ToString("X4")); CodeBuilder.AppendCode("\tSEP #$20"); CodeBuilder.AppendCode("\tLDA #$" + (1 << channel).ToString("X2")); CodeBuilder.AppendCode("\tTSB $" + RAM.HDMAEnable[SA1].ToString("X4")); CodeBuilder.AppendCode("\tRTS"); CodeBuilder.CloseBlock(); CodeBuilder.AppendEmptyLine(); CodeBuilder.AppendTable(table); return(CodeBuilder.ToString()); //return PixelationHDMA.Code(channel, table, SA1); }
public static string Code(int Channel, HDMATable Table, bool SA1, string TableName) { DMAMode mode = (Table[0].Values.Length == 1 ? DMAMode.P : DMAMode.PP); StringBuilder sbCode = new StringBuilder(); int channelAdd = 16 * Channel; string tableName = "." + TableName.TrimStart('.'); sbCode.AppendLine("\tREP #$20"); sbCode.AppendLine("\tLDA #$" + (((Registers.FixedColor & 0xFF) << 8) + (int)mode).ToString("X4")); sbCode.AppendLine("\tSTA $" + (Registers.DMAMode + channelAdd).ToString("X4")); sbCode.AppendLine("\tLDA #" + tableName); sbCode.AppendLine("\tSTA $" + (Registers.DMALowByte + channelAdd).ToString("X4")); sbCode.AppendLine("\tLDY.b #" + tableName + ">>16"); sbCode.AppendLine("\tSTY $" + (Registers.DMABankByte + channelAdd).ToString("X4")); sbCode.AppendLine("\tSEP #$20"); sbCode.AppendLine("\tLDA #$" + (1 << Channel).ToString("X2")); sbCode.AppendLine("\tTSB $" + RAM.HDMAEnable[SA1].ToString("X4")); sbCode.AppendLine("\tRTS"); sbCode.AppendLine(); sbCode.Append(Table.ToString(tableName)); return(sbCode.ToString()); }
public static string Code(int Channel, HDMATable Table, string TableName) { return(ColorHDMA.Code(Channel, Table, RAM.SA1, TableName)); }
public override string Code(int channel, HDMATable table, bool sa1) { bool bothWindows = (Table.Any(t => (t.ValueType == TableValueType.db && (t.Values[2] != 0xFF || t.Values[3] != 0x00)))); int mode = GetMode(false, bothWindows ? DMAMode.PP1P2P3 : DMAMode.PP1); int register = Registers.WindowMask1Left; if (!bothWindows && OneWindowEvent != null) { OneWindowEventArgs e = new OneWindowEventArgs(Window.Window1); OneWindowEvent(this, e); if (e.Cancel) { return(""); } register += (int)e.Window; } if (!bothWindows) { foreach (HDMATableEntry entry in table) { if (entry.ValueType == TableValueType.db) { byte[] values = entry.Values; Array.Resize(ref values, 2); entry.Values = values; } } } int regmode = ((register & 0xFF) << 8) + mode; int baseChannel = 0x4300 + (channel * 0x10); ASMCodeBuilder code = new ASMCodeBuilder(); code.OpenNewBlock(); code.AppendCode("REP #$20", "Get into 16 bit mode"); code.AppendCode("LDA #$" + regmode.ToASMString(), "Register $" + register.ToASMString() + " using mode " + mode); code.AppendCode("STA $" + baseChannel.ToASMString(), baseChannel.ToASMString() + " = transfer mode, " + (baseChannel + 1).ToASMString() + " = register"); code.AppendCode("LDA #" + table.Name, "High byte and low byte of table addresse."); code.AppendCode("STA $" + (baseChannel + 2).ToASMString(), (baseChannel + 2).ToASMString() + " = low byte, " + (baseChannel + 3).ToASMString() + " = high byte"); code.AppendCode("SEP #$20", "Back to 8 bit mode"); code.AppendCode("LDA.b #" + table.Name + ">>16", "Bank byte of table addresse."); code.AppendCode("STA $" + (baseChannel + 4).ToASMString(), "= bank byte"); code.CloseBlock(); code.OpenNewBlock(); code.AppendCode("LDA #$" + (1 << channel).ToASMString()); code.AppendCode("TSB $" + RAM.HDMAEnable[sa1].ToASMString() + "|!addr", "enable HDMA channel " + channel); code.CloseBlock(); code.AppendCode("RTL", "Return"); code.AppendEmptyLine(); code.AppendTable(table); return(code.ToString()); }
/// <summary> /// /// </summary> internal void UseCollection() { //http://www.codeproject.com/Articles/20018/Gradients-made-easy List <ColorPosition> _list = new List <ColorPosition>(DarknessPositions); _list.Sort(); if (_list[0].Position != 0) { _list.Insert(0, new ColorPosition(0, 0xF)); } if (_list[_list.Count - 1].Position < Scanlines) { _list.Add(new ColorPosition(Scanlines, 0xF)); } List <float> positions = new List <float>(); //positions.Add(0.0f); int maxpos = _list.Max(cp => cp.Position); foreach (ColorPosition cp in _list) { positions.Add((float)cp.Position / (float)maxpos); } //positions.Add(1.0f); List <float> factors = new List <float>(); //factors.Add(0.0f); foreach (ColorPosition cp in _list) { factors.Add((float)cp.Orignal8Bit / 15.0f); } //factors.Add(1.0f); Bitmap bm = new Bitmap(1, maxpos); using (Graphics g = Graphics.FromImage(bm)) { Rectangle r = new Rectangle(0, 0, bm.Width, maxpos); using (LinearGradientBrush lgb = new LinearGradientBrush(r, Color.FromArgb(_list.Min(cp => cp.Orignal8Bit) * 17, 0, 0), Color.FromArgb(_list.Max(cp => cp.Orignal8Bit) * 17, 0, 0), 90.0f)) { Blend blend = new Blend(); blend.Factors = factors.ToArray(); blend.Positions = positions.ToArray(); lgb.Blend = blend; g.FillRectangle(lgb, r); } } byte Compare = (byte)(bm.GetPixel(0, 0).R / 17); Table = new HDMATable(); for (int y = 0, scan = 1; y < maxpos; y++, scan++) { byte darkness = (byte)(bm.GetPixel(0, y).R / 17); if (Compare != darkness || scan >= 0x80 || y == maxpos - 1) { Table.Add(new HDMATableEntry(TableValueType.db, (byte)scan, Compare)); Compare = darkness; scan = 0; } for (int x = 0; x < bm.Width; x++) { bm.SetPixel(x, y, Color.FromArgb(255 - darkness * 17, 0, 0, 0)); } } Table.Add(HDMATableEntry.End); Bitmap retImg = new Bitmap(256, maxpos); using (TextureBrush brush = new TextureBrush(bm, WrapMode.Tile)) using (Graphics gr = Graphics.FromImage(retImg)) gr.FillRectangle(brush, 0, 0, retImg.Width, retImg.Height); bm.Dispose(); EffectImage = retImg; }
/// <summary> /// Generates the code for the desired effect /// </summary> /// <param name="channel"></param> /// <param name="table"></param> /// <param name="sa1"></param> /// <returns></returns> public override string Code(int channel, HDMATable table, bool sa1) { int Base = 0x4300 + (channel * 0x10); int Register = (int)Layers; int BaseAddress = RAM.Layer1X[sa1] + ((int)Layers - (int)LayerRegister.Layer1_X) * 2; int RegMode = ((Register & 0xFF) << 8) + 0x02; int LSRs = (int)((1.0 / _speed) / 2.0); int Tablesize = (Scanlines / _width) >= 15 ? 15 : (Scanlines / _width); int TableInRAM = CountRAMBytes(); string tableString = "The Table takes up " + TableInRAM + " bytes of the free RAM\n" + "It ranges from $" + FreeRAM.ToString("X6") + " - $" + (FreeRAM + TableInRAM - 1).ToString("X6") + " (both addresses included)"; ASMTable codeTable = new ASMTable(".WaveTable"); for (int i = 0; i <= Tablesize; i++) { codeTable.Add(new ASMTableEntry((byte)_IArr[i])); } char xy = (((int)Layers & 0x01) == 0 ? 'Y' : 'X'); ASMCodeBuilder CodeBuilder = new ASMCodeBuilder(); CodeBuilder.OpenNewBlock(); CodeBuilder.AppendLabel(INITLabel, "This section is to be used in the INIT code of levelASM"); CodeBuilder.AppendCode("REP #$20"); CodeBuilder.AppendCode("LDA #$" + RegMode.ToASMString(), "Use Mode 02 on register " + Register.ToASMString()); CodeBuilder.AppendCode("STA $" + Base.ToASMString(), "43" + Channel + "0 = Mode, 43" + Channel + "1 = Register"); CodeBuilder.AppendCode("LDA #$" + (FreeRAM & 0xFFFF).ToASMString(), "Address of HDMA table"); CodeBuilder.AppendCode("STA $" + (Base + 2).ToASMString(), "43" + Channel + "2 = Low-Byte of table, 43" + Channel + "3 = High-Byte of table"); CodeBuilder.AppendCode("SEP #$20"); CodeBuilder.AppendCode("LDA.b #$" + (FreeRAM >> 16).ToASMString(), "Address of HDMA table, get bank byte"); CodeBuilder.AppendCode("STA $" + (Base + 4).ToASMString(), "43" + Channel + "4 = Bank-Byte of table"); CodeBuilder.AppendCode("LDA #$" + (0x01 << Channel).ToASMString()); CodeBuilder.AppendCode("TSB $" + RAM.HDMAEnable[sa1].ToASMString(), "Enable HDMA channel " + Channel); CodeBuilder.AppendCode("RTS", "End HDMA setup" + MAINSeperator); CodeBuilder.CloseBlock(); CodeBuilder.AppendCommentLine(tableString); CodeBuilder.AppendEmptyLine(); CodeBuilder.OpenNewBlock(); CodeBuilder.AppendLabel(MAINLabel, "This section is to be used in the MAIN code of levelASM"); CodeBuilder.AppendCode("LDY #$00", "Y will be the loop counter."); CodeBuilder.AppendCode("LDX #$00", "X the index for writing the table to the RAM"); CodeBuilder.AppendCode("LDA $" + RAM.FrameCounter[sa1].ToASMString(), "Speed of waves"); CodeBuilder.AppendCode("LSR #" + LSRs, "Slowing down A"); CodeBuilder.AppendCode("STA $00", "Save for later use."); CodeBuilder.CloseBlock(); CodeBuilder.AppendEmptyLine(); CodeBuilder.OpenNewBlock(); CodeBuilder.AppendCode("PHB : PHK : PLB", "Preservev bank"); CodeBuilder.AppendLabel(".Loop", "Jump back if not finished writing table"); CodeBuilder.AppendCode("LDA #$" + _width.ToASMString(), "Set scanline height"); CodeBuilder.AppendCode("STA $" + FreeRAM.ToASMString() + ",x", "for each wave"); CodeBuilder.AppendCode("TYA", "Transfer Y to A, to calculate next index"); CodeBuilder.AppendCode("ADC $00", "Add frame counter"); CodeBuilder.AppendCode("AND #$" + Tablesize.ToASMString()); CodeBuilder.AppendCode("PHY", "Preserve loop counter"); CodeBuilder.AppendCode("TAY", "Get the index in Y"); CodeBuilder.CloseBlock(); CodeBuilder.AppendEmptyLine(); CodeBuilder.OpenNewBlock(); CodeBuilder.AppendCode("LDA.w " + codeTable.Name + ",y", "Load in wave value"); CodeBuilder.AppendCode("LSR", "Half only"); CodeBuilder.AppendCode("CLC", "Clear Carry for addition"); CodeBuilder.AppendCode("ADC $" + (BaseAddress).ToASMString(), "Add value to layer " + xy + " position (low byte)"); CodeBuilder.AppendCode("STA $" + (FreeRAM + 1).ToASMString() + ",x", "store to HDMA table (low byte)"); CodeBuilder.AppendCode("LDA $" + (BaseAddress + 1).ToASMString(), "Get high byte of X position"); CodeBuilder.AppendCode("ADC #$00", "Add value to layer X position (low byte)"); CodeBuilder.AppendCode("STA $" + (FreeRAM + 2).ToASMString() + ",x", "store to HDMA table (high byte)"); CodeBuilder.CloseBlock(); CodeBuilder.AppendEmptyLine(); CodeBuilder.OpenNewBlock(); CodeBuilder.AppendCode("PLY", "Pull original loop counter"); CodeBuilder.AppendCode("CPY #$" + (Scanlines / _width).ToASMString(), "Compare if we have written enough HDMA entries."); CodeBuilder.AppendCode("BPL .End", "If bigger, end HDMA"); CodeBuilder.AppendCode("INX", "Increase X, so that in the next loop, it writes the new table data..."); CodeBuilder.AppendCode("INX", "... at the end of the old one instead of overwritting it."); CodeBuilder.AppendCode("INX"); CodeBuilder.AppendCode("INY", "Increase loop counter"); CodeBuilder.AppendCode("BRA .Loop", "Repeat loop"); CodeBuilder.CloseBlock(); CodeBuilder.AppendEmptyLine(); CodeBuilder.OpenNewBlock(); CodeBuilder.AppendLabel(".End", "Jump here when at the end of HDMA"); CodeBuilder.AppendCode("PLB", "Pull back data bank."); CodeBuilder.AppendCode("LDA #$00", "End HDMA by writting 00..."); CodeBuilder.AppendCode("STA $" + (FreeRAM + 3).ToASMString() + ",x", "...at the end of the table."); CodeBuilder.AppendCode("RTS"); CodeBuilder.CloseBlock(); CodeBuilder.AppendEmptyLine(); CodeBuilder.OpenNewBlock(); CodeBuilder.AppendTable(codeTable); CodeBuilder.CloseBlock(); return(CodeBuilder.ToString()); // +"\n\n" + tableString; }
public static string Code(ColorHDMA red, ColorHDMA green, ColorHDMA blue, PickSingleChannel single, PickMultiChannel multi, PickMultiChannel tripple) { bool redEmpty = red.IsEmpty(); bool greenEmpty = green.IsEmpty(); bool blueEmpty = blue.IsEmpty(); if (redEmpty && blueEmpty && greenEmpty) { //fixme Exception throw new ArgumentException("Cannot generate code from the provided arguments"); } if (redEmpty && blueEmpty) { return(green.Code(single())); } if (greenEmpty && blueEmpty) { return(red.Code(single())); } if (redEmpty && greenEmpty) { return(blue.Code(single())); } int[] newChannels = new int[] { 3, 4, 5 }; Dictionary <string, HDMATable> dicTable = new Dictionary <string, HDMATable>(); HDMATable merged = null; if (greenEmpty) { if (HDMATable.Merge(red.Table, blue.Table, out merged)) { return(ColorHDMA.Code(single(), merged, ".RedBlueTable")); } else { newChannels = multi(); dicTable.Add(".RedTable", red.Table); dicTable.Add(".BlueTable", blue.Table); } } else if (redEmpty) { if (HDMATable.Merge(green.Table, blue.Table, out merged)) { return(ColorHDMA.Code(single(), merged, ".GreenBlueTable")); } else { newChannels = multi(); dicTable.Add(".GreenTable", green.Table); dicTable.Add(".BlueTable", blue.Table); } } else if (blueEmpty) { if (HDMATable.Merge(green.Table, red.Table, out merged)) { return(ColorHDMA.Code(single(), merged, ".RedGreenTable")); } else { newChannels = multi(); dicTable.Add(".GreenTable", green.Table); dicTable.Add(".RedTable", red.Table); } } else { if (HDMATable.Merge(green.Table, red.Table, out merged)) { newChannels = multi(); dicTable.Add(".RedGreenTable", merged); dicTable.Add(".BlueTable", blue.Table); } else if (HDMATable.Merge(green.Table, blue.Table, out merged)) { newChannels = multi(); dicTable.Add(".GreenBlueTable", merged); dicTable.Add(".RedTable", red.Table); } else if (HDMATable.Merge(red.Table, blue.Table, out merged)) { newChannels = multi(); dicTable.Add(".RedBlueTable", merged); dicTable.Add(".GreenTable", red.Table); } else { newChannels = tripple(); dicTable.Add(".RedTable", red.Table); dicTable.Add(".GreenTable", green.Table); dicTable.Add(".BlueTable", blue.Table); } } if (dicTable.Count != newChannels.Length) { throw new ArgumentException("Number of channels doesn't match number of tables."); } Func <KeyValuePair <string, HDMATable>, string> getTablename = old => "." + old.Key.TrimStart('.'); int channelTrigger = 0; StringBuilder sbCode = new StringBuilder(); //sbCode.AppendLine("init:"); sbCode.AppendLine("\tREP #$20"); for (int i = 0; i < dicTable.Count; i++) { KeyValuePair <string, HDMATable> tablePair = dicTable.ElementAt(i); DMAMode mode = (tablePair.Value[0].Values.Length == 1 ? DMAMode.P : DMAMode.PP); int channelAdd = 16 * newChannels[i]; string tableName = getTablename(tablePair); sbCode.AppendLine("\tLDA #$" + (((Registers.FixedColor & 0xFF) << 8) + (int)mode).ToString("X4")); sbCode.AppendLine("\tSTA $" + (Registers.DMAMode + channelAdd).ToString("X4")); sbCode.AppendLine("\tLDA #" + tableName); sbCode.AppendLine("\tSTA $" + (Registers.DMALowByte + channelAdd).ToString("X4")); sbCode.AppendLine("\tLDY.b #" + tableName + ">>16"); sbCode.AppendLine("\tSTY $" + (Registers.DMABankByte + channelAdd).ToString("X4")); channelTrigger += (1 << newChannels[i]); } sbCode.AppendLine("\tSEP #$20"); sbCode.AppendLine("\tLDA #$" + channelTrigger.ToString("X2")); sbCode.AppendLine("\tTSB $" + RAM.HDMAEnable[RAM.SA1].ToString("X4") + "|!addr"); sbCode.AppendLine("\tRTL"); for (int i = 0; i < dicTable.Count; i++) { KeyValuePair <string, HDMATable> tablePair = dicTable.ElementAt(i); string tableName = getTablename(tablePair); sbCode.AppendLine(); sbCode.Append(tablePair.Value.ToString(tableName)); } return(sbCode.ToString()); }
/// <summary> /// Creates a HDMATable instance that has the exact same values as this one /// </summary> /// <returns>The object that can be casted to an HDMATable</returns> public object Clone() { HDMATable table = new HDMATable(this.Name, this._entries); return(table); }
/// <summary> /// /// </summary> protected HDMA() { Table = new HDMATable(); }
/// <summary> /// /// </summary> /// <param name="table"></param> /// <returns></returns> public virtual string Code(HDMATable table) { return(Code(this.Channel, table, RAM.SA1)); }
public bool CheckAndSplitMask(Bitmap mask, ColorMath math) { //array of with array for each line. //each line has entrys with the color and the start position of that color in that line ColPos[][] C_Arr = new ColPos[Scanlines][]; BitmapData data = mask.LockBits(new Rectangle(new Point(0, 0), mask.Size), ImageLockMode.ReadWrite, mask.PixelFormat); byte[] bytes = new byte[data.Height * data.Stride]; System.Runtime.InteropServices.Marshal.Copy(data.Scan0, bytes, 0, bytes.Length); for (int y = 0; y < mask.Height; y++) { Color ColorToCheck = BitmapEffects.GetPixel(data, bytes, 0, y); //mask.GetPixel(0, y); List <ColPos> CList = new List <ColPos>(); CList.Add(new ColPos(ColorToCheck, 0)); for (int x = 1; x < mask.Width; x++) { Color c = BitmapEffects.GetPixel(data, bytes, x, y); if (ColorToCheck == c) //mask.GetPixel(x, y)) { continue; } ColorToCheck = c; //mask.GetPixel(x, y); CList.Add(new ColPos(ColorToCheck, x)); } C_Arr[y] = CList.ToArray(); } mask.UnlockBits(data); //if more then two black/white transitions appear in (any) line bool black3 = C_Arr.Any((CA => CA.Count(c => c.Color.Name == "ff000000") > 2)); bool white3 = C_Arr.Any((CA => CA.Count(c => c.Color.Name == "ffffffff") > 2)); //3 transitions in both won't work eg. black-white-black-white-black-white if (black3 && white3) { return(false); } int[] BlackLines; int[] WhiteLines; //get all the lines with 3 black transitions. //can't be more then 3 because that would result in more then 3 white trans. in the same line. IEnumerable <ColPos[]> Puff = C_Arr.Where((CA => CA.Count(c => c.Color.Name == "ff000000") > 2)); //an array of indeces to those lines. BlackLines = new int[Puff.Count()]; int i = 0; foreach (ColPos[] cp in Puff) { BlackLines[i] = Array.IndexOf(C_Arr, cp); i++; } //same for white. Puff = C_Arr.Where((CA => CA.Count(c => c.Color.Name == "ffffffff") > 2)); WhiteLines = new int[Puff.Count()]; i = 0; foreach (ColPos[] cp in Puff) { WhiteLines[i] = Array.IndexOf(C_Arr, cp); i++; } Bitmap w1 = BitmapEffects.FromColor(Color.White, 256, 224); Bitmap w2 = BitmapEffects.FromColor(Color.White, 256, 224); BitmapData w1Data = w1.LockBits(new Rectangle(0, 0, w1.Width, w1.Height), ImageLockMode.ReadWrite, w1.PixelFormat); BitmapData w2Data = w2.LockBits(new Rectangle(0, 0, w2.Width, w2.Height), ImageLockMode.ReadWrite, w2.PixelFormat); byte[] w1Bytes = new byte[w1Data.Height * w1Data.Stride]; byte[] w2Bytes = new byte[w2Data.Height * w2Data.Stride]; System.Runtime.InteropServices.Marshal.Copy(w1Data.Scan0, w1Bytes, 0, w1Bytes.Length); System.Runtime.InteropServices.Marshal.Copy(w2Data.Scan0, w2Bytes, 0, w2Bytes.Length); Table = new HDMATable(".windowTable"); HDMATableEntry entry = null; HDMATableEntry lastEntry = null; //if any line in this image requires 3 black and 2 white, one window (bm1) has to be inverted. bool WindowInverted = C_Arr.Any(cp => (cp.Count(c => c.Color.Name == "ff000000") == 3 && cp.Count(c => c.Color.Name == "ffffffff") == 2)); for (int y = 0; y < Scanlines; y++) { entry = new HDMATableEntry(TableValueType.db, (byte)(1 + y - Table.TotalScanlines), 0xFF, 0x00, 0xFF, 0x00); int countBlack = C_Arr[y].Count(cp => cp.Color.Name == "ff000000"); int countWhite = C_Arr[y].Count(cp => cp.Color.Name == "ffffffff"); Func <int, int, int, bool> BlackFromTill = (window, from, till) => { if (till == 0x100) { till = 0xFF; } if (window == 1) { for (int x = from; x < till; x++) { BitmapEffects.SetPixel(w1Data, ref w1Bytes, x, y, Color.Black); } entry.Values[0] = (byte)from; entry.Values[1] = (byte)till; return(true); } else if (window == 2) { for (int x = from; x < till; x++) { BitmapEffects.SetPixel(w2Data, ref w2Bytes, x, y, Color.Black); } entry.Values[2] = (byte)from; entry.Values[3] = (byte)till; return(true); } return(false); }; if (countBlack == 1 && countWhite == 0) { // whole line black. // if window get's inverted, line can be left unchanged. if (!WindowInverted) { BlackFromTill(1, 0, w1.Width); // if not, whole line black } } else if (countBlack == 2 && countWhite == 1) { //outer black, inner white (black-white-black) if (WindowInverted) //if window 1 is inverted it can be done using one window. { BlackFromTill(1, C_Arr[y][1].Position, C_Arr[y][2].Position); //The part white in the mask will be black in w1 } else //window 1 not inveted: { BlackFromTill(1, C_Arr[y][0].Position, C_Arr[y][1].Position); // first black part of mask in window 1, BlackFromTill(2, C_Arr[y][2].Position, w2.Width); // and the second in window 2. } } else if (countBlack == 3 && countWhite == 2) { //black-white-black-white-black //1 window inverted, the other not (not possible otherwise). //meaning window 1 will be inverted. BlackFromTill(1, C_Arr[y][1].Position, C_Arr[y][4].Position); // windows 1 will be black from the beginning of the first white BlackFromTill(2, C_Arr[y][2].Position, C_Arr[y][3].Position); // till the end of the second. Windows 2 is the middle black part. } //else if (countBlack == 3 && countWhite == 3) //... IMPOSSIBRU <.< else if (countBlack == 2 && countWhite == 3) { //white-black-white-black-white //both windows have to be uninverted. (or playing with XOR setting) BlackFromTill(1, C_Arr[y][1].Position, C_Arr[y][2].Position); // window 1 black for first black part of mask BlackFromTill(2, C_Arr[y][3].Position, C_Arr[y][4].Position); // windows 2 black for second part. } else if (countBlack == 1 && countWhite == 2) { //outer white inner black (white-black-white) if (WindowInverted) //if windows 1 is inverted, we have to use window 2 and set window 1 black on the whole line { BlackFromTill(1, 0, w1.Width); // window 1 completly black BlackFromTill(2, C_Arr[y][1].Position, C_Arr[y][2].Position); // window 2 black where mask is black. } else { BlackFromTill(1, C_Arr[y][1].Position, C_Arr[y][2].Position); // if not inverted, simply use window 1. } } else if (countBlack == 0 && countWhite == 1) { //whole line visible if (WindowInverted) { BlackFromTill(1, 0, w1.Width); // if window 1 inverted, whole line black } } else if (countBlack == 2 && countWhite == 2) { //problem: unknown if black-white-black-white or white-black-white-black #region 2/2 if (C_Arr[y][0].Color.Name == "ff000000") //if fisrt part black then B-W-B-W { if (WindowInverted) //inverted: { BlackFromTill(1, C_Arr[y][1].Position, w1.Width); // the -W-B-W area will be black, so inverted makes the first bar black BlackFromTill(2, C_Arr[y][2].Position, C_Arr[y][3].Position); // window 2 simply makes second bar black } else //not inverted { BlackFromTill(1, C_Arr[y][0].Position, C_Arr[y][1].Position); // first bar in window 1 BlackFromTill(2, C_Arr[y][2].Position, C_Arr[y][3].Position); // guess what } } else // first bar not black, thus W-B-W-B { if (WindowInverted) //inverted: { BlackFromTill(1, C_Arr[y][0].Position, C_Arr[y][3].Position); // the W-B-W- area will be black, inverting makes the last bar black. BlackFromTill(2, C_Arr[y][1].Position, C_Arr[y][2].Position); // window 2 will be black where the first bar is. } else //not inverted { BlackFromTill(1, C_Arr[y][1].Position, C_Arr[y][2].Position); // first bar in window 2 BlackFromTill(2, C_Arr[y][3].Position, w2.Width); // ... } } #endregion } else if (countBlack == 1 && countWhite == 1) { //problem: unknown if black-white or white-black if (C_Arr[y][0].Color.Name == "ff000000") //first bar is black thus B-W { if (WindowInverted) //inverted: { BlackFromTill(1, C_Arr[y][1].Position, w1.Width); //set second bar (white) to black due to inverting } else //not inverted { BlackFromTill(1, C_Arr[y][0].Position, C_Arr[y][1].Position); //first bar (black) in window one to black } } else //first bar not black thus W-B { if (WindowInverted) //inverted: { BlackFromTill(1, C_Arr[y][0].Position, C_Arr[y][1].Position); // set white part black; } else //not inverted { BlackFromTill(1, C_Arr[y][1].Position, w1.Width); //set second bar black. } } } if ((lastEntry != null && !entry.Values.SequenceEqual(lastEntry.Values)) || entry.Scanlines == 0x81) { Table.Add(lastEntry); entry.Scanlines = 1; } lastEntry = (HDMATableEntry)entry.Clone(); } //end of for-loop Table.Add(lastEntry); //the last last entry Table.Add(HDMATableEntry.End); //the actual end. System.Runtime.InteropServices.Marshal.Copy(w1Bytes, 0, w1Data.Scan0, w1Bytes.Length); System.Runtime.InteropServices.Marshal.Copy(w2Bytes, 0, w2Data.Scan0, w2Bytes.Length); w1.UnlockBits(w1Data); w2.UnlockBits(w2Data); math.WindowingMask1 = w1; math.WindowingMask2 = w2; math.Window1Inverted = WindowInverted ? (WindowingLayers)0x3F : 0; return(true); }
/// <summary> /// /// </summary> /// <param name="channel"></param> /// <param name="table"></param> /// <returns></returns> public abstract string Code(int channel, HDMATable table, bool sa1);
public override string Code(int channel, HDMATable table, bool sa1) { int Base = 0x4300 + (channel * 0x10); int Register = (int)Layers; int BaseAddress = RAM.Layer1X[sa1] + ((int)Layers - (int)LayerRegister.Layer1_X) * 2; int RegMode = ((Register & 0xFF) << 8) + GetMode(true, DMAMode.PP); var grouped = Bars.GroupBy(b => b.Multiplier); HDMATable _table = new HDMATable("ParallaxTable_" + DateTime.Now.ToString("HHmmssfff")); int tableInRAM = grouped.Count() * 2; Dictionary <int, int> multiplierAddressMapping = new Dictionary <int, int>(); int addresseForOne = 0; //address that has the scrollrate of 1 to finish the table. string tableString = "The Table takes up " + tableInRAM + " bytes of the free RAM\n" + "It ranges from $" + FreeRAM.ToString("X6") + " - $" + (FreeRAM + tableInRAM - 1).ToString("X6") + " (both addresses included)"; ASMCodeBuilder CodeBuilder = new ASMCodeBuilder(); if (Original.Height > Scanlines) { CodeBuilder.AppendCommentLine("IMPORTANT! Please edit the JMP command below for the level you use this on"); } CodeBuilder.AppendEmptyLine(); CodeBuilder.OpenNewBlock(); CodeBuilder.AppendLabel(INITLabel, "This section is to be used in the INIT code of levelASM"); CodeBuilder.AppendCode("REP #$20"); CodeBuilder.AppendCode("LDA #$" + RegMode.ToASMString(), "Use indeirect and mode " + (int)DMAMode.PP + " on register " + Register.ToASMString()); CodeBuilder.AppendCode("STA $" + Base.ToASMString(), "43" + Channel + "0 = Mode, 43" + Channel + "1 = Register"); if (Original.Height <= Scanlines) { CodeBuilder.AppendCode("LDA #" + _table.Name, "Address of HDMA table, get high and low byte"); CodeBuilder.AppendCode("STA $" + (Base + 2).ToASMString(), "43" + Channel + "2 = Low-Byte of table, 43" + Channel + "3 = High-Byte of table"); } CodeBuilder.AppendCode("SEP #$20"); CodeBuilder.AppendCode("LDA.b #" + _table.Name + ">>16", "Address of HDMA table, get bank byte"); CodeBuilder.AppendCode("STA $" + (Base + 4).ToASMString(), "43" + Channel + "4 = Bank-Byte of table"); CodeBuilder.AppendCode("LDA #$" + (FreeRAM >> 16).ToASMString(), "Address of indirect table in RAM bank byte"); CodeBuilder.AppendCode("STA $" + (Base + 7).ToASMString(), "43" + Channel + "4 = Bank-Byte of indirect table"); CodeBuilder.AppendCode("LDA #$" + (0x01 << Channel).ToASMString()); CodeBuilder.AppendCode("TSB $" + RAM.HDMAEnable[sa1].ToASMString() + "|!addr", "Enable HDMA channel " + Channel); if (Original.Height > Scanlines) { CodeBuilder.AppendCode("JMP level105_" + MAINLabel.TrimStart('.'), "Jump to main code" + MAINSeperator); } else { CodeBuilder.AppendCode("RTL", "Return" + MAINSeperator); } CodeBuilder.CloseBlock(); CodeBuilder.AppendCommentLine(tableString); CodeBuilder.AppendEmptyLine(); CodeBuilder.OpenNewBlock(); CodeBuilder.AppendLabel(MAINLabel, "This section is to be used in the MAIN code of levelASM"); CodeBuilder.AppendCode("REP #$20", "16 bit action starts here. (To load the x position of the BG)"); CodeBuilder.CloseBlock(); //index for HDMA table entry calculation if (Original.Height > Scanlines) { CodeBuilder.OpenNewBlock(); CodeBuilder.AppendCode("LDA $" + (BaseAddress + 2).ToASMString(), "Get Y position of BG"); CodeBuilder.AppendCode("ASL", "times 2"); CodeBuilder.AppendCode("CLC : ADC $" + (BaseAddress + 2).ToASMString(), "+1 = times 3"); CodeBuilder.AppendCode("CLC : ADC #" + _table.Name + "+3", "plus Address of HDMA table +3"); //CodeBuilder.AppendCode("INC : INC : INC", "I have no idea why, but the result is off by 1 entry (3 bytes) so this is to fix it."); CodeBuilder.AppendCode("STA $" + (Base + 2).ToASMString(), "43" + Channel + "2 = Low-Byte of table, 43" + Channel + "3 = High-Byte of table"); CodeBuilder.CloseBlock(); } CodeBuilder.AppendEmptyLine(); int i = 0; //grouped by multiplier foreach (var bar in grouped) { CodeBuilder.OpenNewBlock(); if (bar.Key != 0.0) { CodeBuilder.AppendCode("LDA $" + BaseAddress.ToASMString(), "Load BG x Position"); } CodeBuilder.AppendCode(ParallaxHDMAEntry.LsrAsl(bar.Key), "Multiplied by " + bar.Key); var windGroup = bar.GroupBy(b => b.Autospeed); foreach (var singleBar in windGroup) { //if there is no wind, don't add or preserve A if (singleBar.ElementAt(0).Autoscroll&& singleBar.Key != 0) { CodeBuilder.AppendCode("PHA", "Preserve A (current multiplication result)"); CodeBuilder.AppendCode("CLC : ADC #$" + singleBar.Key.ToString("X4"), "Add rate."); } CodeBuilder.AppendCode("STA $" + (FreeRAM + i).ToASMString(), "Store to FreeRAM for indirect HDMA"); //also don't restore A if there is no wind. if (singleBar.ElementAt(0).Autoscroll&& singleBar.Key != 0) { CodeBuilder.AppendCode("PLA", "Restore A (current multiplication result)"); } //keep track of the address for normal scrolling behaviour (multiplier = 1, no auto scroll) if (bar.Key == 1.0 && singleBar.ElementAt(0).Autoscroll&& singleBar.Key != 0) { addresseForOne = FreeRAM + i; } //remember which address has this specs in dictionary with hashcode multiplierAddressMapping.Add(singleBar.ElementAt(0).GetHashCode(), FreeRAM + i); i += 2; } CodeBuilder.CloseBlock(); } //if the normal scrolling hasn't been set yet, make one. if (addresseForOne == 0) { CodeBuilder.OpenNewBlock(); CodeBuilder.AppendCode("LDA $" + BaseAddress.ToASMString(), "Load BG x Position"); CodeBuilder.AppendCode("STA $" + (FreeRAM + i).ToASMString(), "Store to FreeRAM for indirect HDMA"); CodeBuilder.CloseBlock(); addresseForOne = FreeRAM + i; i += 2; } //build the table foreach (var bar in Bars) { //fetch indirect HDMA address int adr = multiplierAddressMapping[bar.GetHashCode()] & 0xFFFF; //split it byte low = (byte)(adr & 0xFF); byte high = (byte)((adr >> 8) & 0xFF); //make as many 1 scanline high entries as needed. var entry = new HDMATableEntry(TableValueType.dw, 1, low, high); _table.AddRange(Enumerable.Repeat <HDMATableEntry>(entry, bar.Scanline)); } //if there aren't enough entries for the whole screen yet, fill it up with scrollrate 1. if (_table.Count < Original.Height) { //fetch indirect HDMA address for normal scrolling int adr = addresseForOne & 0xFFFF; //split it byte low = (byte)(adr & 0xFF); byte high = (byte)((adr >> 8) & 0xFF); var entry = new HDMATableEntry(TableValueType.dw, 1, low, high); _table.AddRange(Enumerable.Repeat <HDMATableEntry>(entry, Original.Height - _table.Count)); } //cut down table for (int l = _table.Count - 1; l >= Original.Height; l--) { _table.RemoveAt(l); } //add end. _table.Add(HDMATableEntry.End); CodeBuilder.AppendCode("SEP #$20", "Back to 8bit"); CodeBuilder.AppendCode("RTL", "Return"); CodeBuilder.CloseBlock(); CodeBuilder.AppendEmptyLine(); CodeBuilder.OpenNewBlock(); CodeBuilder.AppendTable(_table); CodeBuilder.CloseBlock(); return(CodeBuilder.ToString()); // +"\n\n" + tableString; }