diff --git a/CommonWPF/AnimatedGifEncoder.cs b/CommonWPF/AnimatedGifEncoder.cs index 2ce9bd9..4618dfa 100644 --- a/CommonWPF/AnimatedGifEncoder.cs +++ b/CommonWPF/AnimatedGifEncoder.cs @@ -125,7 +125,7 @@ namespace CommonWPF { // Step 2: determine the size of the largest image. This will become the logical // size of the animated GIF. // - // TODO: We have an opportunity to replace all of the local color tables with a + // TODO(maybe): We have an opportunity to replace all of the local color tables with a // single global color table. This is only possible if all of the local tables are // identical and the transparency values in the GCE also match up. (Well, it's // otherwise *possible*, but we'd need to decode, update palettes and pixels, and diff --git a/SourceGen/DisplayList.cs b/SourceGen/DisplayList.cs index 06eb3da..0d7e26f 100644 --- a/SourceGen/DisplayList.cs +++ b/SourceGen/DisplayList.cs @@ -314,7 +314,8 @@ namespace SourceGen { for (int i = 0; i < newCount; i++) { mList.Insert(startIndex, null); } - // TODO: can we null out existing entries, and just insert/remove when counts differ? + // TODO(someday): can we null out existing entries, and just insert/remove when + // counts differ? if (oldCount != newCount) { SelectedIndices = new DisplayListSelection(mList.Count); @@ -323,9 +324,9 @@ namespace SourceGen { OnPropertyChanged(CountString); OnPropertyChanged(IndexerName); - // TODO(performance): this causes the ListView to format the entire listing, despite + // TODO: this causes the ListView to format the entire listing, despite // being virtual. So we're regenerating the entire list after something trivial, - // like renaming a label. Need to figure this out. + // like renaming a label, which hampers performance. Need to figure this out. OnCollectionReset(); } diff --git a/SourceGen/Examples/Tutorial/Source/Tutorial5.S b/SourceGen/Examples/Tutorial/Source/Tutorial5.S new file mode 100644 index 0000000..46939b6 --- /dev/null +++ b/SourceGen/Examples/Tutorial/Source/Tutorial5.S @@ -0,0 +1,80 @@ +; Copyright 2019 faddenSoft. All Rights Reserved. +; See the LICENSE.txt file for distribution terms (Apache 2.0). +; +; Assembler: Merlin 32 + + org $1000 + +ENTRY + lda bitmapX + lda bitmapO + lda bitmapBoard + rts + +; Each pixel is represented by a single bit. Leftmost pixel is in high bit. + +bitmapX ;1x8 + dfb %10000010 ; X.....X. + dfb %01000100 ; .X...X.. + dfb %00101000 ; ..X.X... + dfb %00010000 ; ...X.... + dfb %00101000 ; ..X.X... + dfb %01000100 ; .X...X.. + dfb %10000010 ; X.....X. + dfb %00000000 ; ........ + +bitmapO ;1x8 + dfb %00111000 ; ..OOO... + dfb %01000100 ; .O...O.. + dfb %10000010 ; O.....O. + dfb %10000010 ; O.....O. + dfb %10000010 ; O.....O. + dfb %01000100 ; .O...O.. + dfb %00111000 ; ..OOO... + dfb %00000000 ; ........ + +bitmapBoard ;5x40, stride=8 + hex 00fe00fe00CCCCCC ; ........ #######. ........ #######. ........ + hex 00fe00fe00CCCCCC ; ........ #######. ........ #######. ........ + hex 00fe00fe00CCCCCC ; ........ #######. ........ #######. ........ + hex 00fe00fe00CCCCCC ; ........ #######. ........ #######. ........ + hex 00fe00fe00CCCCCC ; ........ #######. ........ #######. ........ + hex 00fe00fe00CCCCCC ; ........ #######. ........ #######. ........ + hex 00fe00fe00CCCCCC ; ........ #######. ........ #######. ........ + hex 00fe00fe00CCCCCC ; ........ #######. ........ #######. ........ + + hex fffffffffeCCCCCC ; ######## ######## ######## ######## #######. + hex fffffffffeCCCCCC ; ######## ######## ######## ######## #######. + hex fffffffffeCCCCCC ; ######## ######## ######## ######## #######. + hex fffffffffeCCCCCC ; ######## ######## ######## ######## #######. + hex fffffffffeCCCCCC ; ######## ######## ######## ######## #######. + hex fffffffffeCCCCCC ; ######## ######## ######## ######## #######. + hex fffffffffeCCCCCC ; ######## ######## ######## ######## #######. + hex 00fe00fe00CCCCCC ; ........ #######. ........ #######. ........ + + hex 00fe00fe00CCCCCC ; ........ #######. ........ #######. ........ + hex 00fe00fe00CCCCCC ; ........ #######. ........ #######. ........ + hex 00fe00fe00CCCCCC ; ........ #######. ........ #######. ........ + hex 00fe00fe00CCCCCC ; ........ #######. ........ #######. ........ + hex 00fe00fe00CCCCCC ; ........ #######. ........ #######. ........ + hex 00fe00fe00CCCCCC ; ........ #######. ........ #######. ........ + hex 00fe00fe00CCCCCC ; ........ #######. ........ #######. ........ + hex 00fe00fe00CCCCCC ; ........ #######. ........ #######. ........ + + hex fffffffffeCCCCCC ; ######## ######## ######## ######## #######. + hex fffffffffeCCCCCC ; ######## ######## ######## ######## #######. + hex fffffffffeCCCCCC ; ######## ######## ######## ######## #######. + hex fffffffffeCCCCCC ; ######## ######## ######## ######## #######. + hex fffffffffeCCCCCC ; ######## ######## ######## ######## #######. + hex fffffffffeCCCCCC ; ######## ######## ######## ######## #######. + hex fffffffffeCCCCCC ; ######## ######## ######## ######## #######. + hex 00fe00fe00CCCCCC ; ........ #######. ........ #######. ........ + + hex 00fe00fe00CCCCCC ; ........ #######. ........ #######. ........ + hex 00fe00fe00CCCCCC ; ........ #######. ........ #######. ........ + hex 00fe00fe00CCCCCC ; ........ #######. ........ #######. ........ + hex 00fe00fe00CCCCCC ; ........ #######. ........ #######. ........ + hex 00fe00fe00CCCCCC ; ........ #######. ........ #######. ........ + hex 00fe00fe00CCCCCC ; ........ #######. ........ #######. ........ + hex 00fe00fe00CCCCCC ; ........ #######. ........ #######. ........ + hex 0000000000CCCCCC ; ........ ........ ........ ........ ........ diff --git a/SourceGen/Examples/Tutorial/Tutorial5 b/SourceGen/Examples/Tutorial/Tutorial5 new file mode 100644 index 0000000..9fa06e0 Binary files /dev/null and b/SourceGen/Examples/Tutorial/Tutorial5 differ diff --git a/SourceGen/Examples/Tutorial/VisTutorial5.cs b/SourceGen/Examples/Tutorial/VisTutorial5.cs new file mode 100644 index 0000000..745be14 --- /dev/null +++ b/SourceGen/Examples/Tutorial/VisTutorial5.cs @@ -0,0 +1,155 @@ +/* + * Copyright 2019 faddenSoft + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +using System; +using System.Collections.ObjectModel; + +using PluginCommon; + +namespace Tutorial { + public class VisTutorial5 : MarshalByRefObject, IPlugin, IPlugin_Visualizer { + // IPlugin + public string Identifier { + get { return "Tutorial Visualizer"; } + } + private IApplication mAppRef; + private byte[] mFileData; + + // Visualization identifiers; DO NOT change or projects that use them will break. + private const string VIS_GEN_BITMAP = "tutorial-bitmap"; + + private const string P_OFFSET = "offset"; + private const string P_BYTE_WIDTH = "byteWidth"; + private const string P_HEIGHT = "height"; + private const string P_ROW_STRIDE = "rowStride"; + + private const int MAX_DIM = 4096; + + // Visualization descriptors. + private VisDescr[] mDescriptors = new VisDescr[] { + new VisDescr(VIS_GEN_BITMAP, "Tutorial Bitmap", VisDescr.VisType.Bitmap, + new VisParamDescr[] { + new VisParamDescr("File offset (hex)", + P_OFFSET, typeof(int), 0, 0x00ffffff, VisParamDescr.SpecialMode.Offset, 0), + new VisParamDescr("Width (in bytes)", + P_BYTE_WIDTH, typeof(int), 1, 64, 0, 1), + new VisParamDescr("Height", + P_HEIGHT, typeof(int), 1, 512, 0, 1), + new VisParamDescr("Row stride (bytes)", + P_ROW_STRIDE, typeof(int), 0, 256, 0, 0), + }) + }; + + + // IPlugin + public void Prepare(IApplication appRef, byte[] fileData, AddressTranslate addrTrans) { + mAppRef = appRef; + mFileData = fileData; + } + + // IPlugin + public void Unprepare() { + mAppRef = null; + mFileData = null; + } + + // IPlugin_Visualizer + public VisDescr[] GetVisGenDescrs() { + if (mFileData == null) { + return null; + } + return mDescriptors; + } + + // IPlugin_Visualizer + public IVisualization2d Generate2d(VisDescr descr, + ReadOnlyDictionary parms) { + switch (descr.Ident) { + case VIS_GEN_BITMAP: + return GenerateBitmap(parms); + default: + mAppRef.ReportError("Unknown ident " + descr.Ident); + return null; + } + } + + private IVisualization2d GenerateBitmap(ReadOnlyDictionary parms) { + int offset = Util.GetFromObjDict(parms, P_OFFSET, 0); + int byteWidth = Util.GetFromObjDict(parms, P_BYTE_WIDTH, 1); + int height = Util.GetFromObjDict(parms, P_HEIGHT, 1); + int rowStride = Util.GetFromObjDict(parms, P_ROW_STRIDE, 0); + + if (rowStride == 0) { + rowStride = byteWidth; // provide nice default when stride==0 + } + if (offset < 0 || offset >= mFileData.Length || + byteWidth <= 0 || byteWidth > MAX_DIM || + height <= 0 || height > MAX_DIM) { + mAppRef.ReportError("Invalid parameter"); + return null; + } + if (rowStride < byteWidth || rowStride > MAX_DIM) { + mAppRef.ReportError("Invalid row stride"); + return null; + } + + int lastOffset = offset + rowStride * height - 1; + if (lastOffset >= mFileData.Length) { + mAppRef.ReportError("Bitmap runs off end of file (last offset +" + + lastOffset.ToString("x6") + ")"); + return null; + } + + VisBitmap8 vb = new VisBitmap8(byteWidth * 8, height); + SetPalette(vb); + + // Convert bits to pixels. + for (int row = 0; row < height; row++) { + for (int byteCol = 0; byteCol < byteWidth; byteCol++) { + byte val = mFileData[offset + row * rowStride + byteCol]; + for (int bit = 0; bit < 8; bit++) { + if ((val & 0x80) != 0) { + vb.SetPixelIndex(byteCol * 8 + bit, row, (byte)Color.Solid); + } else { + vb.SetPixelIndex(byteCol * 8 + bit, row, (byte)Color.Transparent); + } + val <<= 1; + } + } + } + return vb; + } + + private int RevBits(int val, int count) { + int result = 0; + for (int i = 0; i < count; i++) { + result <<= 1; + result |= val & 0x01; + val >>= 1; + } + return result; + } + + private enum Color : byte { + Transparent = 0, + Solid = 1, + } + + private void SetPalette(VisBitmap8 vb) { + vb.AddColor(0, 0, 0, 0); // 0=transparent + vb.AddColor(0xff, 0x20, 0x20, 0xff); // 1=solid + } + } +} diff --git a/SourceGen/LineListGen.cs b/SourceGen/LineListGen.cs index 2138fe2..0f89746 100644 --- a/SourceGen/LineListGen.cs +++ b/SourceGen/LineListGen.cs @@ -1140,7 +1140,7 @@ namespace SourceGen { // isn't the ORG at the start of the file. (This may temporarily do // double-spacing if we do a partial update, because we won't be able to // "see" the previous line. Harmless.) - // TODO: consider always adding blanks, and doing a fix-up pass afterward. + // TODO(maybe): consider always adding blanks, and doing a fix-up pass afterward. // (but keep in mind that blank lines should always come above things) // // Interesting case: diff --git a/SourceGen/MainController.cs b/SourceGen/MainController.cs index a4f6629..61a82bf 100644 --- a/SourceGen/MainController.cs +++ b/SourceGen/MainController.cs @@ -3380,7 +3380,8 @@ namespace SourceGen { /// Converts the ListView's selected items into a set of offsets. If a line /// spans multiple offsets (e.g. a 3-byte instruction), offsets for every /// byte are included. - /// + /// + /// /// Contiguous regions with user labels or address changes are split into /// independent regions by using a serial number for the range type. Same for /// long comments and notes. @@ -3390,7 +3391,7 @@ namespace SourceGen { /// or a string. It should not be possible to select part of a formatted section, /// unless the user has been playing weird games with type hints to get overlapping /// format descriptors. - /// + /// /// TypedRangeSet with all offsets. private TypedRangeSet GroupedOffsetSetFromSelected() { TypedRangeSet rs = new TypedRangeSet(); diff --git a/SourceGen/RuntimeData/Help/tutorials.html b/SourceGen/RuntimeData/Help/tutorials.html index 978f45c..7811bfa 100644 --- a/SourceGen/RuntimeData/Help/tutorials.html +++ b/SourceGen/RuntimeData/Help/tutorials.html @@ -23,6 +23,7 @@ manual is recommended.

  • #2: Advanced Features
  • #3: Address Table Formatting
  • #4: Extension Scripts
  • +
  • #5: Visualizations
  • @@ -203,9 +204,9 @@ comment, and hit Enter. Your comment appears in the "comment" column.

    Select the line with address $2003 ("CMP #$04"), then Actions > Edit Operand. This allows you to pick how you want the -operand to look. It's currently set to Default, which for an 8-bit +operand to look. It's currently set to "Default", which for an 8-bit immediate argument means it's shown as a hexadecimal value. Click -Binary, then OK. It now appears as a binary value.

    +"Binary", then "OK". It now appears as a binary value.

    The operand in the LDA instruction at line $2000 refers to an address ($3000) that isn't part of the file. We want to create an equate directive to @@ -375,6 +376,8 @@ it should always match exactly.)

    At this point you know enough to work with a SourceGen project. Continue on to the next tutorial to learn more.

    +
    +

    Tutorial #2: Advanced Features

    @@ -573,6 +576,8 @@ used extensively in Microsoft BASICs, such as Applesoft.) When you see the extra symbol in the opcode field, you need to look closely at what's going on.

    +
    +

    Tutorial #3: Address Table Formatting

    @@ -654,6 +659,8 @@ code entry point hint -- but did several of them at once.

    We don't want to save this project, so select File > Close. When SourceGen asks for confirmation, click Discard & Continue.

    +
    +

    Tutorial #4: Extension Scripts

    @@ -681,7 +688,7 @@ you'll see that it's looking for a JSR to a function called "PrintInlineL1String". So let's give it one.

    Double-click the JSR operand ("L1026"), click "Create Label", and enter "PrintInlineL1String". Remember that labels are case-sensitive; -you must enter it exactly as shown. Hit OK to accept the label, and OK +you must enter it exactly as shown. Hit "OK" to accept the label, and "OK" to close the operand editor. If all went well, address $1003 should now be an L1 string "How long?", and adress $100D should be another JSR.

    @@ -692,7 +699,7 @@ and add the script "InlineNullTermString.cs".

    that starts with "PrintInlineNullString". So let's give it a couple of those.

    Double-click the operand on line $100D ("L1027"), click Create Label, -and set the label to "PrintInlineNullStringOne". Hit OK twice. That +and set the label to "PrintInlineNullStringOne". Hit "OK" twice. That formatted the first one and got us to the next JSR. Repeat the process on line $1019 ("L1028"), setting the label to "PrintInlineNullStringTwo".

    @@ -706,6 +713,127 @@ strings and clean up the disassembly automatically.

    some programming experience. See the manual for more details.

    +
    + + +

    Tutorial #5: Visualizations

    + +

    This tutorial covers one specific feature.

    + +

    Many programs contain a significant amount of graphical data. This is +especially true for games, where the space used for bitmaps is often +larger than the space required for the code. When disassembling a program +it can be very helpful to be able to see the contents of the data +regions in graphical form.

    + +

    Start a new project with "Generic 6502", and in the SourceGen Tutorial +directory select "Tutorial5". We'll need to load an extension script from +the project directory, so immediately save the project, using the +default name ("Tutorial5.dis65").

    +

    Normally a project will give you some sort of hint as to the data +format, e.g. the graphics might be a platform-specific sprite. For +non-standard formats you can glean dimensions from the drawing code. For +the purposes of this tutorial we're just using a simple monochrome bitmap +format, with 8 pixels per byte, and we'll know that our images are for +a Tic-Tac-Toe game. The 'X' and the 'O' are 8x8, the game board is 40x40. +The bitmaps are sprites with transparency, so pixels are either solid +or transparent.

    +

    The first thing we need to do is load an extension script that can +decode this format. The RuntimeData directory has a few, but for this +tutorial we're using a custom one. Select Edit > Project Properties, +select the Extension Scripts tab, and click "Add Scripts from Project". +Double-click on "VisTutorial5.cs", then click "OK".

    + +

    The address of the three bitmaps are helpfully identified by the +load instructions at the top of the file. Select the first one at +address $100A, then Actions > Create/Edit Visualization Set. In +the window that opens, click "New Bitmap".

    +

    We're going to ignore most of what's going on and just focus on the +list of parameters at the bottom. The file offset indicates where in +the file the bitmap starts; note this is an offset, not an address +(that way, if you change the address, your visualizations don't break). +This is followed by the bitmap's width in bytes, and the bitmap's height. +Because we have 8 pixels per byte, we're currently showing a 1x1 image. +We'll come back to row stride.

    +

    We happen to know (by playing the game and/or reading the fictitious +drawing code) that the image is 8x8, so change the value in the height +field to 8. As soon as you do, the preview window shows a big blue 'X'. +(The 'X' is 7x7; the last row/column of pixels are transparent so adjacent +images don't blend into each other.)

    +

    Let's try doing it wrong. Add a 0 to make the height 80. You can see +some additional bitmap data. Add another 0 to make it 800. Now you get +a big red X, and the "Height" parameter is shown in red. That's because +the maximum value for the height is 512, as shown by "[1,512]" on the +right.

    +

    Change it back to 8, and hit "OK". Hit "OK" in the Edit Visualization +Set window as well. You should now see the blue 'X' in the code listing +above line $100A.

    + +

    Repeat the process at line $1012: select the line, create a visualization +set, create a new bitmap, set the height to 8, click "OK" twice.

    + +

    Repeat the process at line $101A, but this time the image is 40x40 +rather than 8x8. Set the width to 5, and the height to 40. This makes +a mess.

    +

    In this case, the bitmap data is 5 bytes wide, but the data is stored +as 8 bytes per row. This is known as the "stride" or "pitch" of the row. +To tell the visualizer to skip the last 3 bytes on each row, set the +"Row stride (bytes)" field to 8. Now we have a proper Tic-Tac-Toe grid. +Note that it fills the preview window just as the 'X' and 'O' did, even +though it's 5x as large. The preview window scales everything up.

    +

    Let's format the bitmap data. Select line $101A, then shift-click the +last line in the file ($1159). Actions > Edit Operand. Select +"densely-packed bytes", and click "OK". This is perhaps a little too +dense. Open the operand editor again, but this time select the +densely-packed bytes sub-option "...with a limit", and set the limit +to 8 bytes per line. Instead of one very dense statement spread across +a few lines, you get one line of code per row of bitmap. If you prefer +to see individual bytes, you can use Edit > Settings, select the +Display Format tab, and check "use comma-separated format for bulk data". +This can make it a bit easier to read.

    + +

    Animations

    + +

    Some graphics represent individual frames in an animated sequence. +You can convert those as well. Double-click on the blue 'X' to open +the visualization set editor, then click "New Bitmap Animation". This +opens the Bitmap Animation Editor.

    +

    Let's try it with our Tic-Tac-Toe board pieces. From the list on the +left, select the blue 'X' and click "Add", then click the 'O' and click +"Add". Below the list, set the frame delay to 500. Near the bottom, +click "Start / Stop. This causes the animation to play in a loop. You +can use the controls to add and remove items, change their order, and change +the animation speed. You can add the grid to the animation set, but the +preview scales the bitmaps up to full size, so it may not look the way +you expect.

    +

    Hit "OK" to save the animation, then "OK" to update the visualization set. +The code list now shows two entries, one of which has a blue triangle +superimposed. You can have as many bitmaps an animations on a line +as you want.

    +

    If you have a lot of bitmaps it can be helpful to give them meaningful +names, so that they're easy to identify and sort together in the list. +The "tag" field at the top of the editor windows lets you give things +names. Tags must be unique.

    + +

    Other Notes

    + +

    The visualization editor is intended to be very dynamic, showing the +results of parameter changes immediately. This can be helpful if you're +not exactly sure what the size or format of a bitmap is. Just keep +tweaking values until it looks right.

    + +

    Visualization generators are defined by extension scripts. If you're +disassembling a program with a totally custom way of storing graphics, +you can write a totally custom visualizer and distribute it with the +project. Because the file offset is a parameter, you're not limited to +placing visualizations at the start of the graphic data -- you can put +them on any code or data line.

    + +

    Visualizations have no effect on assembly source code generation, +but they do appear in code exported to HTML. Bitmaps are converted to GIF +images, and animations become animated GIFs.

    + +

    End of Tutorials