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()); } }
/// <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 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()); }
public void FromTable(HDMATable table) { if (!table.HasEnded()) { table.Add(HDMATableEntry.End); } Table = table; UpdateImage(); }
/// <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; }
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); }
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; }