mirror of
https://github.com/fadden/6502bench.git
synced 2024-05-31 22:41:37 +00:00
Improve visualization
Various improvements: - Switched to ReadOnlyDictionary in Visualization to make it clear that the parameter dictionary should not be modified. - Added a warning to the Visualization Set editor that appears when there are no plugins that implement a visualizer. - Make sure an item is selected in the set editor after edit/remove. - Replaced the checkerboard background with one that's a little bit more grey, so it's more distinct from white pixel data. - Added a new Apple II hi-res color converter whose output more closely matches KEGS and AppleWin RGB. - Added VisHiRes.cs to some Apple II system definitions. - Added some test bitmaps for Apple II hi-res to the test directory. (These are not part of an automated test.)
This commit is contained in:
parent
2f56c56e5c
commit
df04de61e6
|
@ -15,6 +15,7 @@
|
||||||
*/
|
*/
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
using System.Collections.ObjectModel;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
|
|
||||||
namespace CommonUtil {
|
namespace CommonUtil {
|
||||||
|
@ -78,5 +79,21 @@ namespace CommonUtil {
|
||||||
return !dict1.Except(dict2).Any();
|
return !dict1.Except(dict2).Any();
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static bool CompareDicts<TKey, TValue>(
|
||||||
|
ReadOnlyDictionary<TKey, TValue> dict1, ReadOnlyDictionary<TKey, TValue> dict2) {
|
||||||
|
if (dict1 == dict2) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (dict1 == null || dict2 == null) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (dict1.Count != dict2.Count) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check to see if there are any elements in the first that are not in the second.
|
||||||
|
return !dict1.Except(dict2).Any();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -15,6 +15,7 @@
|
||||||
*/
|
*/
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
using System.Collections.ObjectModel;
|
||||||
|
|
||||||
namespace PluginCommon {
|
namespace PluginCommon {
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
@ -150,13 +151,18 @@ namespace PluginCommon {
|
||||||
/// <param name="parms">Parameter set.</param>
|
/// <param name="parms">Parameter set.</param>
|
||||||
/// <returns>2D visualization object reference, or null if something went
|
/// <returns>2D visualization object reference, or null if something went
|
||||||
/// wrong (unknown ident, bad parameters, etc).</returns>
|
/// wrong (unknown ident, bad parameters, etc).</returns>
|
||||||
IVisualization2d Generate2d(VisDescr descr, Dictionary<string, object> parms);
|
IVisualization2d Generate2d(VisDescr descr, ReadOnlyDictionary<string, object> parms);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Visualization generator descriptor. IPlugin_Visualizer instances publish a list of
|
||||||
|
/// these to tell the application about the generators it supports.
|
||||||
|
/// </summary>
|
||||||
[Serializable]
|
[Serializable]
|
||||||
public class VisDescr {
|
public class VisDescr {
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Unique identifier. This is stored in the project file.
|
/// Unique identifier. This is stored in the project file. Names beginning with
|
||||||
|
/// underscores ('_') are reserved.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public string Ident { get; private set; }
|
public string Ident { get; private set; }
|
||||||
|
|
||||||
|
@ -181,6 +187,9 @@ namespace PluginCommon {
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public VisParamDescr[] VisParamDescrs { get; private set; }
|
public VisParamDescr[] VisParamDescrs { get; private set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Constructor.
|
||||||
|
/// </summary>
|
||||||
public VisDescr(string ident, string uiName, VisType type, VisParamDescr[] descrs) {
|
public VisDescr(string ident, string uiName, VisType type, VisParamDescr[] descrs) {
|
||||||
Ident = ident;
|
Ident = ident;
|
||||||
UiName = uiName;
|
UiName = uiName;
|
||||||
|
@ -192,24 +201,59 @@ namespace PluginCommon {
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Visualization parameter descriptor.
|
/// Visualization parameter descriptor.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
/// <remarks>
|
||||||
|
/// We provide min/max for individual numeric fields, but there's no way to check other
|
||||||
|
/// fields to see if e.g. Stride >= Width. We'd need a "verify" function and a way to
|
||||||
|
/// report errors that the GUI could use to point out what was wrong.
|
||||||
|
/// </remarks>
|
||||||
[Serializable]
|
[Serializable]
|
||||||
public class VisParamDescr {
|
public class VisParamDescr {
|
||||||
|
/// <summary>
|
||||||
|
/// Special feature enumeration.
|
||||||
|
/// </summary>
|
||||||
public enum SpecialMode {
|
public enum SpecialMode {
|
||||||
None = 0, Offset
|
None = 0, Offset
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Label to show in the UI.
|
||||||
|
/// </summary>
|
||||||
public string UiLabel { get; private set; }
|
public string UiLabel { get; private set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Name to use internally. Do not use names that start with an underscore ('_'), as
|
||||||
|
/// these are reserved for future internal use.
|
||||||
|
/// </summary>
|
||||||
public string Name { get; private set; }
|
public string Name { get; private set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Parameter data type.
|
||||||
|
/// </summary>
|
||||||
public Type CsType { get; private set; }
|
public Type CsType { get; private set; }
|
||||||
|
|
||||||
// Min/Max values for ints and floats. Could also be length for strings.
|
/// <summary>
|
||||||
// NOTE: ideally we'd provide a "verify" function that tested individual fields and
|
/// Minimum allowable value for int/float (and perhaps string length).
|
||||||
// could also see other fields, e.g. to confirm that Stride >= Width.
|
/// </summary>
|
||||||
public object Min { get; private set; }
|
public object Min { get; private set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Maximum allowable value for int/float (and perhaps string length).
|
||||||
|
/// </summary>
|
||||||
public object Max { get; private set; }
|
public object Max { get; private set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Set to a value if the field requires special treatment.
|
||||||
|
/// </summary>
|
||||||
public SpecialMode Special { get; private set; }
|
public SpecialMode Special { get; private set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Default value for this field.
|
||||||
|
/// </summary>
|
||||||
public object DefaultValue { get; private set; }
|
public object DefaultValue { get; private set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Constructor.
|
||||||
|
/// </summary>
|
||||||
public VisParamDescr(string uiLabel, string name, Type csType, object min, object max,
|
public VisParamDescr(string uiLabel, string name, Type csType, object min, object max,
|
||||||
SpecialMode special, object defVal) {
|
SpecialMode special, object defVal) {
|
||||||
UiLabel = uiLabel;
|
UiLabel = uiLabel;
|
||||||
|
|
|
@ -15,6 +15,7 @@
|
||||||
*/
|
*/
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
using System.Collections.ObjectModel;
|
||||||
using CommonUtil;
|
using CommonUtil;
|
||||||
|
|
||||||
namespace PluginCommon {
|
namespace PluginCommon {
|
||||||
|
@ -107,7 +108,8 @@ namespace PluginCommon {
|
||||||
/// <param name="defVal">Default value.</param>
|
/// <param name="defVal">Default value.</param>
|
||||||
/// <returns>Value found, or the default if the key doesn't exist or the value has the
|
/// <returns>Value found, or the default if the key doesn't exist or the value has the
|
||||||
/// wrong type.</returns>
|
/// wrong type.</returns>
|
||||||
public static T GetFromObjDict<T>(Dictionary<string, object> dict, string key, T defVal) {
|
public static T GetFromObjDict<T>(ReadOnlyDictionary<string, object> dict, string key,
|
||||||
|
T defVal) {
|
||||||
if (dict.TryGetValue(key, out object objVal)) {
|
if (dict.TryGetValue(key, out object objVal)) {
|
||||||
if (objVal is T) {
|
if (objVal is T) {
|
||||||
return (T)objVal;
|
return (T)objVal;
|
||||||
|
|
|
@ -22,17 +22,33 @@ limitations under the License.
|
||||||
<ResourceDictionary>
|
<ResourceDictionary>
|
||||||
<FontFamily x:Key="GeneralMonoFont">Consolas</FontFamily>
|
<FontFamily x:Key="GeneralMonoFont">Consolas</FontFamily>
|
||||||
|
|
||||||
|
<!-- gradient background for bitmap images -->
|
||||||
<LinearGradientBrush x:Key="BitmapBackground" StartPoint="0,0" EndPoint="1,1">
|
<LinearGradientBrush x:Key="BitmapBackground" StartPoint="0,0" EndPoint="1,1">
|
||||||
<GradientStop Color="#707070" Offset="0.0"/>
|
<GradientStop Color="#707070" Offset="0.0"/>
|
||||||
<GradientStop Color="#f0f0f0" Offset="1.0"/>
|
<GradientStop Color="#f0f0f0" Offset="1.0"/>
|
||||||
</LinearGradientBrush>
|
</LinearGradientBrush>
|
||||||
|
|
||||||
|
<!-- checkerboard background for bitmap images -->
|
||||||
<!-- https://stackoverflow.com/a/47049174/294248 -->
|
<!-- https://stackoverflow.com/a/47049174/294248 -->
|
||||||
<DrawingBrush x:Key="CheckerBackground" TileMode="Tile" Viewport="0,0,16,16" ViewportUnits="Absolute">
|
<DrawingBrush x:Key="CheckerBackgroundHG" TileMode="Tile" Viewport="0,0,16,16" ViewportUnits="Absolute">
|
||||||
|
<!-- draw two squares, leaving the other two untouched (probably window bkgnd color) -->
|
||||||
<DrawingBrush.Drawing>
|
<DrawingBrush.Drawing>
|
||||||
<GeometryDrawing Geometry="M0,0 H1 V1 H2 V2 H1 V1 H0Z" Brush="LightGray"/>
|
<GeometryDrawing Geometry="M0,0 H1 V1 H2 V2 H1 V1 H0Z" Brush="LightGray"/>
|
||||||
</DrawingBrush.Drawing>
|
</DrawingBrush.Drawing>
|
||||||
</DrawingBrush>
|
</DrawingBrush>
|
||||||
|
<DrawingBrush x:Key="CheckerBackground" TileMode="Tile" Viewport="0,0,16,16" ViewportUnits="Absolute">
|
||||||
|
<DrawingBrush.Drawing>
|
||||||
|
<DrawingGroup>
|
||||||
|
<DrawingGroup.Children>
|
||||||
|
<!-- not sure if it's better to do this with overdraw (background
|
||||||
|
then two squares) or by explicitly drawing four squares -->
|
||||||
|
<GeometryDrawing Geometry="M0,0 H2 V2 H0Z" Brush="#e8e8e8"/>
|
||||||
|
<GeometryDrawing Geometry="M0,0 H1 V1 H2 V2 H1 V1 H0Z" Brush="#f0f0f0"/>
|
||||||
|
<!--<GeometryDrawing Geometry="M0,2 H1 V1 H2 V0 H1 V1 H0Z" Brush="#e8e8e8"/>-->
|
||||||
|
</DrawingGroup.Children>
|
||||||
|
</DrawingGroup>
|
||||||
|
</DrawingBrush.Drawing>
|
||||||
|
</DrawingBrush>
|
||||||
|
|
||||||
<ResourceDictionary.MergedDictionaries>
|
<ResourceDictionary.MergedDictionaries>
|
||||||
<ResourceDictionary Source="Res/Strings.xaml"/>
|
<ResourceDictionary Source="Res/Strings.xaml"/>
|
||||||
|
|
|
@ -15,6 +15,7 @@
|
||||||
*/
|
*/
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
using System.Collections.ObjectModel;
|
||||||
using System.Diagnostics;
|
using System.Diagnostics;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
|
@ -368,7 +369,7 @@ namespace SourceGen {
|
||||||
public SerVisualization(Visualization vis) {
|
public SerVisualization(Visualization vis) {
|
||||||
Tag = vis.Tag;
|
Tag = vis.Tag;
|
||||||
VisGenIdent = vis.VisGenIdent;
|
VisGenIdent = vis.VisGenIdent;
|
||||||
VisGenParams = vis.VisGenParams; // Dictionary
|
VisGenParams = new Dictionary<string, object>(vis.VisGenParams);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
public class SerVisualizationSet {
|
public class SerVisualizationSet {
|
||||||
|
@ -1002,7 +1003,8 @@ namespace SourceGen {
|
||||||
parms.Add(kvp.Key, val);
|
parms.Add(kvp.Key, val);
|
||||||
}
|
}
|
||||||
|
|
||||||
Visualization vis = new Visualization(serVis.Tag, serVis.VisGenIdent, parms);
|
Visualization vis = new Visualization(serVis.Tag, serVis.VisGenIdent,
|
||||||
|
new ReadOnlyDictionary<string, object>(parms));
|
||||||
outVisSet.Add(vis);
|
outVisSet.Add(vis);
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
|
|
|
@ -15,6 +15,7 @@
|
||||||
*/
|
*/
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
using System.Collections.ObjectModel;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
|
|
||||||
using PluginCommon;
|
using PluginCommon;
|
||||||
|
@ -65,8 +66,8 @@ namespace RuntimeData.Apple {
|
||||||
P_IS_COLOR, typeof(bool), 0, 0, 0, true),
|
P_IS_COLOR, typeof(bool), 0, 0, 0, true),
|
||||||
new VisParamDescr("First col odd",
|
new VisParamDescr("First col odd",
|
||||||
P_IS_FIRST_ODD, typeof(bool), 0, 0, 0, false),
|
P_IS_FIRST_ODD, typeof(bool), 0, 0, 0, false),
|
||||||
new VisParamDescr("Test Float",
|
//new VisParamDescr("Test Float",
|
||||||
"floaty", typeof(float), -5.0f, 5.0f, 0, 0.1f),
|
// "floaty", typeof(float), -5.0f, 5.0f, 0, 0.1f),
|
||||||
}),
|
}),
|
||||||
new VisDescr(VIS_GEN_BITMAP_FONT, "Apple II Hi-Res Bitmap Font", VisDescr.VisType.Bitmap,
|
new VisDescr(VIS_GEN_BITMAP_FONT, "Apple II Hi-Res Bitmap Font", VisDescr.VisType.Bitmap,
|
||||||
new VisParamDescr[] {
|
new VisParamDescr[] {
|
||||||
|
@ -103,7 +104,7 @@ namespace RuntimeData.Apple {
|
||||||
|
|
||||||
// IPlugin_Visualizer
|
// IPlugin_Visualizer
|
||||||
public IVisualization2d Generate2d(VisDescr descr,
|
public IVisualization2d Generate2d(VisDescr descr,
|
||||||
Dictionary<string, object> parms) {
|
ReadOnlyDictionary<string, object> parms) {
|
||||||
switch (descr.Ident) {
|
switch (descr.Ident) {
|
||||||
case VIS_GEN_BITMAP:
|
case VIS_GEN_BITMAP:
|
||||||
return GenerateBitmap(parms);
|
return GenerateBitmap(parms);
|
||||||
|
@ -116,7 +117,7 @@ namespace RuntimeData.Apple {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private IVisualization2d GenerateBitmap(Dictionary<string, object> parms) {
|
private IVisualization2d GenerateBitmap(ReadOnlyDictionary<string, object> parms) {
|
||||||
int offset, byteWidth, height, colStride, rowStride;
|
int offset, byteWidth, height, colStride, rowStride;
|
||||||
bool isColor, isFirstOdd;
|
bool isColor, isFirstOdd;
|
||||||
|
|
||||||
|
@ -147,7 +148,7 @@ namespace RuntimeData.Apple {
|
||||||
mAppRef.DebugLog("Invalid column stride");
|
mAppRef.DebugLog("Invalid column stride");
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
if (rowStride < byteWidth * colStride - (colStride-1) || rowStride > MAX_DIM) {
|
if (rowStride < byteWidth * colStride - (colStride - 1) || rowStride > MAX_DIM) {
|
||||||
mAppRef.DebugLog("Invalid row stride");
|
mAppRef.DebugLog("Invalid row stride");
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
@ -159,187 +160,283 @@ namespace RuntimeData.Apple {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
VisBitmap8 vb = new VisBitmap8(byteWidth * 7, height);
|
VisBitmap8 vb = new VisBitmap8(byteWidth * 7, height);
|
||||||
SetHiResPalette(vb);
|
SetHiResPalette(vb);
|
||||||
|
|
||||||
if (!isColor) {
|
RenderBitmap(offset, byteWidth, height, colStride, rowStride,
|
||||||
// B&W mode. Since we're not displaying this we don't need to worry about
|
isColor ? ColorMode.SimpleColor : ColorMode.Mono, isFirstOdd, vb);
|
||||||
// half-pixel shifts, and can just convert 7 bits to pixels.
|
|
||||||
int bx = 0;
|
|
||||||
int by = 0;
|
|
||||||
for (int row = 0; row < height; row++) {
|
|
||||||
int colIdx = 0;
|
|
||||||
for (int col = 0; col < byteWidth; col++) {
|
|
||||||
byte val = mFileData[offset + colIdx];
|
|
||||||
for (int bit = 0; bit < 7; bit++) {
|
|
||||||
vb.SetPixelIndex(bx, by, (byte)((val & 0x01) + 1)); // black or white
|
|
||||||
val >>= 1;
|
|
||||||
bx++;
|
|
||||||
}
|
|
||||||
colIdx += colStride;
|
|
||||||
}
|
|
||||||
bx = 0;
|
|
||||||
by++;
|
|
||||||
offset += rowStride;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
int bx = 0;
|
|
||||||
int by = 0;
|
|
||||||
|
|
||||||
#if false
|
|
||||||
// Color mode. We treat the data as a strictly 140-mode bitmap, which doesn't
|
|
||||||
// quite match up with how the pixels will be displayed, but does allow a
|
|
||||||
// straightforward conversion between file formats. Color fringing is severe.
|
|
||||||
for (int row = 0; row < height; row++) {
|
|
||||||
int lastBit;
|
|
||||||
if (isFirstOdd) {
|
|
||||||
lastBit = 0; // pretend we already have one bit
|
|
||||||
} else {
|
|
||||||
lastBit = -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
for (int colByte = 0; colByte < byteWidth; colByte += colStride) {
|
|
||||||
byte val = mFileData[offset + colByte];
|
|
||||||
bool hiBitSet = (val & 0x80) != 0;
|
|
||||||
|
|
||||||
// Grab 3 or 4 pairs of bits.
|
|
||||||
int pairCount = (lastBit < 0) ? 3 : 4;
|
|
||||||
while (pairCount-- > 0) {
|
|
||||||
int twoBits;
|
|
||||||
if (lastBit >= 0) {
|
|
||||||
// merge with bit from previous byte
|
|
||||||
twoBits = (lastBit << 1) | (val & 0x01);
|
|
||||||
val >>= 1;
|
|
||||||
lastBit = -1;
|
|
||||||
} else {
|
|
||||||
// grab two bits
|
|
||||||
twoBits = (val & 0x03);
|
|
||||||
val >>= 2;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (hiBitSet) {
|
|
||||||
twoBits += 4;
|
|
||||||
}
|
|
||||||
|
|
||||||
// We're in 140 mode, so set two adjacent pixels.
|
|
||||||
vb.SetPixelIndex(bx++, by, sHiResColorMap[twoBits]);
|
|
||||||
vb.SetPixelIndex(bx++, by, sHiResColorMap[twoBits]);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool thisEven = ((colByte & 0x01) == 0) ^ isFirstOdd;
|
|
||||||
if (thisEven) {
|
|
||||||
// started in even column we have one bit left over
|
|
||||||
lastBit = val & 0x01;
|
|
||||||
} else {
|
|
||||||
// started in odd column, all bits consumed
|
|
||||||
lastBit = -1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
bx = 0;
|
|
||||||
by++;
|
|
||||||
offset += rowStride;
|
|
||||||
}
|
|
||||||
#else
|
|
||||||
// Color conversion similar to what CiderPress does, but without the half-pixel
|
|
||||||
// shift (we're trying to create a 1:1 bitmap, not 1:2).
|
|
||||||
bool[] lineBits = new bool[byteWidth * 7];
|
|
||||||
bool[] hiFlags = new bool[byteWidth * 7]; // overkill, but simplifies things
|
|
||||||
int[] colorBuf = new int[byteWidth * 7];
|
|
||||||
for (int row = 0; row < height; row++) {
|
|
||||||
// Unravel the bits.
|
|
||||||
int idx = 0;
|
|
||||||
int colIdx = 0;
|
|
||||||
for (int col = 0; col < byteWidth; col++) {
|
|
||||||
byte val = mFileData[offset + colIdx];
|
|
||||||
bool hiBitSet = (val & 0x80) != 0;
|
|
||||||
|
|
||||||
for (int bit = 0; bit < 7; bit++) {
|
|
||||||
hiFlags[idx] = hiBitSet;
|
|
||||||
lineBits[idx] = (val & 0x01) != 0;
|
|
||||||
idx++;
|
|
||||||
val >>= 1;
|
|
||||||
}
|
|
||||||
colIdx += colStride;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Convert to color.
|
|
||||||
int lastBit = byteWidth * 7;
|
|
||||||
for (idx = 0; idx < lastBit; idx++) {
|
|
||||||
int colorShift = hiFlags[idx] ? 4 : 0;
|
|
||||||
if (!lineBits[idx]) {
|
|
||||||
// Bit not set, set pixel to black.
|
|
||||||
colorBuf[idx] = (int)HiResColors.Black0 + colorShift;
|
|
||||||
} else {
|
|
||||||
// Bit set, set pixel to white or color.
|
|
||||||
if (idx > 0 && colorBuf[idx - 1] != (int)HiResColors.Black0 &&
|
|
||||||
colorBuf[idx - 1] != (int)HiResColors.Black1) {
|
|
||||||
// previous bit was also set, this is white
|
|
||||||
colorBuf[idx] = (int)HiResColors.White0 + colorShift;
|
|
||||||
|
|
||||||
// the previous pixel is part of a run of white
|
|
||||||
colorBuf[idx - 1] = (int)HiResColors.White0 + colorShift;
|
|
||||||
} else {
|
|
||||||
// previous bit not set *or* was first pixel in line;
|
|
||||||
// set color based on whether this is even or odd pixel col
|
|
||||||
bool isOdd = ((idx & 0x01) != 0) ^ isFirstOdd;
|
|
||||||
if (isOdd) {
|
|
||||||
colorBuf[idx] = (int)HiResColors.Green + colorShift;
|
|
||||||
} else {
|
|
||||||
colorBuf[idx] = (int)HiResColors.Purple + colorShift;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Do we have a run of the same color? If so, smooth the color out.
|
|
||||||
// Note that white blends smoothly with everything.
|
|
||||||
if (idx > 1 && (colorBuf[idx - 2] == colorBuf[idx] ||
|
|
||||||
colorBuf[idx - 2] == (int)HiResColors.White0 ||
|
|
||||||
colorBuf[idx - 2] == (int)HiResColors.White1)) {
|
|
||||||
|
|
||||||
//if (colorBuf[idx - 1] != (int)HiResColors.Black0 &&
|
|
||||||
// colorBuf[idx - 1] != (int) HiResColors.Black1) {
|
|
||||||
// mAppRef.DebugLog("Unexpected color at row=" + by +
|
|
||||||
// " idx=" + idx + ": " + colorBuf[idx - 1]);
|
|
||||||
//}
|
|
||||||
|
|
||||||
colorBuf[idx - 1] = colorBuf[idx];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Write to bitmap.
|
|
||||||
for (idx = 0; idx < lastBit; idx++) {
|
|
||||||
vb.SetPixelIndex(bx++, by, sHiResColorMap[colorBuf[idx]]);
|
|
||||||
}
|
|
||||||
|
|
||||||
// move to next row
|
|
||||||
bx = 0;
|
|
||||||
by++;
|
|
||||||
offset += rowStride;
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
return vb;
|
return vb;
|
||||||
}
|
}
|
||||||
|
|
||||||
private enum HiResColors {
|
private enum ColorMode { Mono, ClunkyColor, SimpleColor, IIgsRGB };
|
||||||
Black0 = 0,
|
|
||||||
Green = 1,
|
private void RenderBitmap(int offset, int byteWidth, int height, int colStride,
|
||||||
Purple = 2,
|
int rowStride, ColorMode colorMode, bool isFirstOdd, VisBitmap8 vb) {
|
||||||
White0 = 3,
|
int bx = 0;
|
||||||
Black1 = 4,
|
int by = 0;
|
||||||
Orange = 5,
|
switch (colorMode) {
|
||||||
Blue = 6,
|
case ColorMode.Mono: {
|
||||||
White1 = 7
|
// Since we're not displaying this we don't need to worry about
|
||||||
|
// half-pixel shifts, and can just convert 7 bits to pixels.
|
||||||
|
for (int row = 0; row < height; row++) {
|
||||||
|
int colIdx = 0;
|
||||||
|
for (int col = 0; col < byteWidth; col++) {
|
||||||
|
byte val = mFileData[offset + colIdx];
|
||||||
|
for (int bit = 0; bit < 7; bit++) {
|
||||||
|
if ((val & 0x01) == 0) {
|
||||||
|
vb.SetPixelIndex(bx, by, (int)HiResColors.Black0);
|
||||||
|
} else {
|
||||||
|
vb.SetPixelIndex(bx, by, (int)HiResColors.White0);
|
||||||
|
}
|
||||||
|
val >>= 1;
|
||||||
|
bx++;
|
||||||
|
}
|
||||||
|
colIdx += colStride;
|
||||||
|
}
|
||||||
|
bx = 0;
|
||||||
|
by++;
|
||||||
|
offset += rowStride;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case ColorMode.ClunkyColor: {
|
||||||
|
// We treat the data as a strictly 140-mode bitmap, which doesn't match
|
||||||
|
// up well with how the pixels will be displayed, but does allow a
|
||||||
|
// straightforward conversion between file formats. Color fringing is
|
||||||
|
// severe.
|
||||||
|
for (int row = 0; row < height; row++) {
|
||||||
|
int lastBit;
|
||||||
|
if (isFirstOdd) {
|
||||||
|
lastBit = 0; // pretend we already have one bit
|
||||||
|
} else {
|
||||||
|
lastBit = -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int colByte = 0; colByte < byteWidth; colByte += colStride) {
|
||||||
|
byte val = mFileData[offset + colByte];
|
||||||
|
bool hiBitSet = (val & 0x80) != 0;
|
||||||
|
|
||||||
|
// Grab 3 or 4 pairs of bits.
|
||||||
|
int pairCount = (lastBit < 0) ? 3 : 4;
|
||||||
|
while (pairCount-- > 0) {
|
||||||
|
int twoBits;
|
||||||
|
if (lastBit >= 0) {
|
||||||
|
// merge with bit from previous byte
|
||||||
|
twoBits = (lastBit << 1) | (val & 0x01);
|
||||||
|
val >>= 1;
|
||||||
|
lastBit = -1;
|
||||||
|
} else {
|
||||||
|
// grab two bits
|
||||||
|
twoBits = (val & 0x03);
|
||||||
|
val >>= 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (hiBitSet) {
|
||||||
|
twoBits += 4;
|
||||||
|
}
|
||||||
|
|
||||||
|
// We're in 140 mode, so set two adjacent pixels.
|
||||||
|
vb.SetPixelIndex(bx++, by, sHiResColorMap[twoBits]);
|
||||||
|
vb.SetPixelIndex(bx++, by, sHiResColorMap[twoBits]);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool thisEven = ((colByte & 0x01) == 0) ^ isFirstOdd;
|
||||||
|
if (thisEven) {
|
||||||
|
// started in even column we have one bit left over
|
||||||
|
lastBit = val & 0x01;
|
||||||
|
} else {
|
||||||
|
// started in odd column, all bits consumed
|
||||||
|
lastBit = -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
bx = 0;
|
||||||
|
by++;
|
||||||
|
offset += rowStride;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case ColorMode.SimpleColor: {
|
||||||
|
// Straightforward conversion, with no funky border effects. This
|
||||||
|
// represents an idealized version of the hardware.
|
||||||
|
|
||||||
|
// Bits for every byte, plus a couple of "fake" bits on the ends so
|
||||||
|
// we don't have to throw range-checks everywhere.
|
||||||
|
const int OVER = 2;
|
||||||
|
bool[] lineBits = new bool[OVER + byteWidth * 7 + OVER];
|
||||||
|
bool[] hiFlags = new bool[OVER + byteWidth * 7 + OVER];
|
||||||
|
for (int row = 0; row < height; row++) {
|
||||||
|
// Unravel the bits. Note we do each byte "backwards", i.e. the
|
||||||
|
// low bit (which is generally considered to be on the right) is
|
||||||
|
// the leftmost pixel.
|
||||||
|
int idx = OVER; // start past "fake" bits
|
||||||
|
int colIdx = 0;
|
||||||
|
for (int col = 0; col < byteWidth; col++) {
|
||||||
|
byte val = mFileData[offset + colIdx];
|
||||||
|
bool hiBitSet = (val & 0x80) != 0;
|
||||||
|
|
||||||
|
for (int bit = 0; bit < 7; bit++) {
|
||||||
|
hiFlags[idx] = hiBitSet;
|
||||||
|
lineBits[idx] = (val & 0x01) != 0;
|
||||||
|
idx++;
|
||||||
|
val >>= 1;
|
||||||
|
}
|
||||||
|
colIdx += colStride;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Convert to color.
|
||||||
|
int lastBit = byteWidth * 7;
|
||||||
|
for (idx = OVER; idx < lastBit + OVER; idx++) {
|
||||||
|
int colorShift = hiFlags[idx] ? 2 : 0;
|
||||||
|
if (lineBits[idx] && (lineBits[idx - 1] || lineBits[idx + 1])) {
|
||||||
|
// [X]11 or [1]1X; two 1s in a row is always white
|
||||||
|
vb.SetPixelIndex(bx++, by, (byte)HiResColors.White0);
|
||||||
|
} else if (lineBits[idx]) {
|
||||||
|
// [0]10, color pixel
|
||||||
|
bool isOdd = ((idx & 0x01) != 0) ^ isFirstOdd;
|
||||||
|
if (isOdd) {
|
||||||
|
vb.SetPixelIndex(bx++, by,
|
||||||
|
(byte)((int)HiResColors.Green + colorShift));
|
||||||
|
} else {
|
||||||
|
vb.SetPixelIndex(bx++, by,
|
||||||
|
(byte)((int)HiResColors.Purple + colorShift));
|
||||||
|
}
|
||||||
|
} else if (lineBits[idx - 1] && lineBits[idx + 1]) {
|
||||||
|
// [1]01, keep color going
|
||||||
|
bool isOdd = ((idx & 0x01) != 0) ^ isFirstOdd;
|
||||||
|
if (isOdd) {
|
||||||
|
vb.SetPixelIndex(bx++, by,
|
||||||
|
(byte)((int)HiResColors.Purple + colorShift));
|
||||||
|
} else {
|
||||||
|
vb.SetPixelIndex(bx++, by,
|
||||||
|
(byte)((int)HiResColors.Green + colorShift));
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// [0]0X or [X]01
|
||||||
|
vb.SetPixelIndex(bx++, by, (byte)HiResColors.Black0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// move to next row
|
||||||
|
bx = 0;
|
||||||
|
by++;
|
||||||
|
offset += rowStride;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case ColorMode.IIgsRGB: {
|
||||||
|
// Color conversion similar to what CiderPress does, but without the
|
||||||
|
// half-pixel shift (we're trying to create a 1:1 bitmap, not 1:2).
|
||||||
|
//
|
||||||
|
// This replicates some of the oddness in Apple IIgs RGB monitor output,
|
||||||
|
// but it's not quite right though. For example:
|
||||||
|
//
|
||||||
|
// observed generated
|
||||||
|
// d5 2a: blue [dk blue] purple ... black ...
|
||||||
|
// aa 55: orange [yellow] green ... white ...
|
||||||
|
// 55 aa: purple [lt blue] blue ... black ...
|
||||||
|
// 2a d5: green [brown] orange ... black ...
|
||||||
|
//
|
||||||
|
// KEGS doesn't seem to try to model this; it shows solid colors with no
|
||||||
|
// wackiness. AppleWin in "Color TV" mode shows similar effects, but is
|
||||||
|
// much blurrier (by design).
|
||||||
|
bool[] lineBits = new bool[byteWidth * 7];
|
||||||
|
bool[] hiFlags = new bool[byteWidth * 7]; // overkill, but simpler
|
||||||
|
int[] colorBuf = new int[byteWidth * 7];
|
||||||
|
for (int row = 0; row < height; row++) {
|
||||||
|
// Unravel the bits.
|
||||||
|
int idx = 0;
|
||||||
|
int colIdx = 0;
|
||||||
|
for (int col = 0; col < byteWidth; col++) {
|
||||||
|
byte val = mFileData[offset + colIdx];
|
||||||
|
bool hiBitSet = (val & 0x80) != 0;
|
||||||
|
|
||||||
|
for (int bit = 0; bit < 7; bit++) {
|
||||||
|
hiFlags[idx] = hiBitSet;
|
||||||
|
lineBits[idx] = (val & 0x01) != 0;
|
||||||
|
idx++;
|
||||||
|
val >>= 1;
|
||||||
|
}
|
||||||
|
colIdx += colStride;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Convert to color.
|
||||||
|
int lastBit = byteWidth * 7;
|
||||||
|
for (idx = 0; idx < lastBit; idx++) {
|
||||||
|
int colorShift = hiFlags[idx] ? 2 : 0;
|
||||||
|
if (!lineBits[idx]) {
|
||||||
|
// Bit not set, set pixel to black.
|
||||||
|
colorBuf[idx] = (int)HiResColors.Black0;
|
||||||
|
} else {
|
||||||
|
// Bit set, set pixel to white or color.
|
||||||
|
if (idx > 0 && colorBuf[idx - 1] != (int)HiResColors.Black0) {
|
||||||
|
// previous bit was also set, this is white
|
||||||
|
colorBuf[idx] = (int)HiResColors.White0;
|
||||||
|
|
||||||
|
// the previous pixel is part of a run of white
|
||||||
|
colorBuf[idx - 1] = (int)HiResColors.White0;
|
||||||
|
} else {
|
||||||
|
// previous bit not set *or* was first pixel in line;
|
||||||
|
// set color based on whether this is even or odd pixel col
|
||||||
|
bool isOdd = ((idx & 0x01) != 0) ^ isFirstOdd;
|
||||||
|
if (isOdd) {
|
||||||
|
colorBuf[idx] = (int)HiResColors.Green + colorShift;
|
||||||
|
} else {
|
||||||
|
colorBuf[idx] = (int)HiResColors.Purple + colorShift;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Do we have a run of the same color? If so, smooth the
|
||||||
|
// color out. Note that white blends smoothly with everything.
|
||||||
|
if (idx > 1 && (colorBuf[idx - 2] == colorBuf[idx] ||
|
||||||
|
colorBuf[idx - 2] == (int)HiResColors.White0)) {
|
||||||
|
colorBuf[idx - 1] = colorBuf[idx];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Write to bitmap.
|
||||||
|
for (idx = 0; idx < lastBit; idx++) {
|
||||||
|
vb.SetPixelIndex(bx++, by, (byte)colorBuf[idx]);
|
||||||
|
}
|
||||||
|
|
||||||
|
// move to next row
|
||||||
|
bx = 0;
|
||||||
|
by++;
|
||||||
|
offset += rowStride;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
// just leave the bitmap empty
|
||||||
|
mAppRef.DebugLog("Unknown ColorMode " + colorMode);
|
||||||
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Maps HiResColors to the palette entries.
|
/// <summary>
|
||||||
|
/// Map hi-res colors to palette entries.
|
||||||
|
/// </summary>
|
||||||
|
private enum HiResColors : byte {
|
||||||
|
Black0 = 1,
|
||||||
|
Green = 3,
|
||||||
|
Purple = 4,
|
||||||
|
White0 = 2,
|
||||||
|
Black1 = 1,
|
||||||
|
Orange = 5,
|
||||||
|
Blue = 6,
|
||||||
|
White1 = 2
|
||||||
|
}
|
||||||
|
|
||||||
|
// Maps HiResColors to the palette entries. Used for mapping 2-bit values to colors.
|
||||||
private static readonly byte[] sHiResColorMap = new byte[8] {
|
private static readonly byte[] sHiResColorMap = new byte[8] {
|
||||||
1, 3, 4, 2, 1, 5, 6, 2
|
1, 3, 4, 2, 1, 5, 6, 2
|
||||||
};
|
};
|
||||||
|
|
||||||
private void SetHiResPalette(VisBitmap8 vb) {
|
private void SetHiResPalette(VisBitmap8 vb) {
|
||||||
// These don't match directly to hi-res color numbers because we want to
|
// These don't match directly to hi-res color numbers because we want to
|
||||||
// avoid adding black/white twice.
|
// avoid adding black/white twice. The colors correspond to Apple IIgs RGB
|
||||||
|
// monitor output.
|
||||||
vb.AddColor(0, 0, 0, 0); // 0=transparent
|
vb.AddColor(0, 0, 0, 0); // 0=transparent
|
||||||
vb.AddColor(0xff, 0x00, 0x00, 0x00); // 1=black0/black1
|
vb.AddColor(0xff, 0x00, 0x00, 0x00); // 1=black0/black1
|
||||||
vb.AddColor(0xff, 0xff, 0xff, 0xff); // 2=white0/white1
|
vb.AddColor(0xff, 0xff, 0xff, 0xff); // 2=white0/white1
|
||||||
|
|
|
@ -15,7 +15,8 @@
|
||||||
"RT:Apple/ProDOS8.sym65"
|
"RT:Apple/ProDOS8.sym65"
|
||||||
],
|
],
|
||||||
"ExtensionScripts" : [
|
"ExtensionScripts" : [
|
||||||
"RT:Apple/ProDOS8.cs"
|
"RT:Apple/ProDOS8.cs",
|
||||||
|
"RT:Apple/VisHiRes.cs"
|
||||||
],
|
],
|
||||||
"Parameters" : {
|
"Parameters" : {
|
||||||
"load-address":"0x2000"
|
"load-address":"0x2000"
|
||||||
|
@ -33,6 +34,7 @@
|
||||||
"RT:Apple/DOS33.sym65"
|
"RT:Apple/DOS33.sym65"
|
||||||
],
|
],
|
||||||
"ExtensionScripts" : [
|
"ExtensionScripts" : [
|
||||||
|
"RT:Apple/VisHiRes.cs"
|
||||||
],
|
],
|
||||||
"Parameters" : {
|
"Parameters" : {
|
||||||
"load-address":"0x2000"
|
"load-address":"0x2000"
|
||||||
|
@ -51,7 +53,8 @@
|
||||||
"RT:Apple/IIgs-ROM.sym65"
|
"RT:Apple/IIgs-ROM.sym65"
|
||||||
],
|
],
|
||||||
"ExtensionScripts" : [
|
"ExtensionScripts" : [
|
||||||
"RT:Apple/ProDOS8.cs"
|
"RT:Apple/ProDOS8.cs",
|
||||||
|
"RT:Apple/VisHiRes.cs"
|
||||||
],
|
],
|
||||||
"Parameters" : {
|
"Parameters" : {
|
||||||
"load-address":"0x2000",
|
"load-address":"0x2000",
|
||||||
|
|
|
@ -93,3 +93,9 @@ This is a collection of project files with deliberate errors. These exist
|
||||||
to exercise the load-time error reporting. See the README in that directory
|
to exercise the load-time error reporting. See the README in that directory
|
||||||
for a full explanation.
|
for a full explanation.
|
||||||
|
|
||||||
|
|
||||||
|
## Visualization ##
|
||||||
|
|
||||||
|
Some test projects and data files for exercising the visualization generators.
|
||||||
|
Not part of a formal test; load the projects and eyeball the results.
|
||||||
|
|
||||||
|
|
BIN
SourceGen/SGTestData/Visualization/apple2-bitmap-test#061000
Normal file
BIN
SourceGen/SGTestData/Visualization/apple2-bitmap-test#061000
Normal file
Binary file not shown.
166
SourceGen/SGTestData/Visualization/apple2-bitmap-test#061000.S
Normal file
166
SourceGen/SGTestData/Visualization/apple2-bitmap-test#061000.S
Normal file
|
@ -0,0 +1,166 @@
|
||||||
|
; Copyright 2019 faddenSoft. All Rights Reserved.
|
||||||
|
; See the LICENSE.txt file for distribution terms (Apache 2.0).
|
||||||
|
;
|
||||||
|
; Assembler: Merlin 32
|
||||||
|
|
||||||
|
HGR equ $f3e2
|
||||||
|
src_ptr equ $00
|
||||||
|
xoff equ $02
|
||||||
|
width equ $03
|
||||||
|
line equ $04
|
||||||
|
count equ $05
|
||||||
|
|
||||||
|
org $1000
|
||||||
|
|
||||||
|
bit bitmap1 ;give the disassembler a reason to label them
|
||||||
|
bit bitmap2
|
||||||
|
bit bitmap3
|
||||||
|
bit bitmap4
|
||||||
|
bit bitmap5
|
||||||
|
|
||||||
|
jsr HGR ;clear screen, set hi-res mode
|
||||||
|
lda #<bitmap1
|
||||||
|
sta src_ptr
|
||||||
|
lda #>bitmap1
|
||||||
|
sta src_ptr+1
|
||||||
|
lda #$00
|
||||||
|
sta xoff
|
||||||
|
lda #$01 ;1x8
|
||||||
|
sta width
|
||||||
|
jsr draw
|
||||||
|
|
||||||
|
lda #<bitmap2
|
||||||
|
sta src_ptr
|
||||||
|
lda #>bitmap2
|
||||||
|
sta src_ptr+1
|
||||||
|
lda #$03 ;start in odd column
|
||||||
|
sta xoff
|
||||||
|
lda #$01 ;1x8
|
||||||
|
sta width
|
||||||
|
jsr draw
|
||||||
|
|
||||||
|
lda #<bitmap3
|
||||||
|
sta src_ptr
|
||||||
|
lda #>bitmap3
|
||||||
|
sta src_ptr+1
|
||||||
|
lda #$06
|
||||||
|
sta xoff
|
||||||
|
lda #$02 ;2x8
|
||||||
|
sta width
|
||||||
|
jsr draw
|
||||||
|
|
||||||
|
lda #<bitmap4
|
||||||
|
sta src_ptr
|
||||||
|
lda #>bitmap4
|
||||||
|
sta src_ptr+1
|
||||||
|
lda #$0a
|
||||||
|
sta xoff
|
||||||
|
lda #$02 ;2x8
|
||||||
|
sta width
|
||||||
|
jsr draw
|
||||||
|
|
||||||
|
lda #<bitmap5
|
||||||
|
sta src_ptr
|
||||||
|
lda #>bitmap5
|
||||||
|
sta src_ptr+1
|
||||||
|
lda #$0e
|
||||||
|
sta xoff
|
||||||
|
lda #$03 ;3x8
|
||||||
|
sta width
|
||||||
|
jsr draw
|
||||||
|
|
||||||
|
rts
|
||||||
|
|
||||||
|
; Copies an Nx8 bitmap to the hi-res screen.
|
||||||
|
;
|
||||||
|
; Put X offset in "xoff", width-1 in "width_m1", and a pointer to the bitmap
|
||||||
|
; data in "src_ptr".
|
||||||
|
draw
|
||||||
|
ldy #$00 ;index to input byte
|
||||||
|
sty line ;init line index to zero
|
||||||
|
:loop ldx line
|
||||||
|
lda addrs,x
|
||||||
|
beq :done
|
||||||
|
sta _copy+2
|
||||||
|
lda xoff
|
||||||
|
sta _copy+1
|
||||||
|
|
||||||
|
ldx #$00
|
||||||
|
:loop1 lda (src_ptr),y
|
||||||
|
_copy sta $2000,x
|
||||||
|
iny
|
||||||
|
inx
|
||||||
|
cpx width
|
||||||
|
bne :loop1
|
||||||
|
inc line
|
||||||
|
bne :loop ;always (more or less)
|
||||||
|
|
||||||
|
:done rts
|
||||||
|
|
||||||
|
; hi byte of base addresses on hi-res screen; must be 8 of them
|
||||||
|
addrs
|
||||||
|
dfb $20
|
||||||
|
dfb $24
|
||||||
|
dfb $28
|
||||||
|
dfb $2c
|
||||||
|
dfb $30
|
||||||
|
dfb $34
|
||||||
|
dfb $38
|
||||||
|
dfb $3c
|
||||||
|
dfb $00
|
||||||
|
|
||||||
|
; 1x8
|
||||||
|
bitmap1
|
||||||
|
dfb $00
|
||||||
|
dfb $01
|
||||||
|
dfb $02
|
||||||
|
dfb $04
|
||||||
|
dfb $08
|
||||||
|
dfb $10
|
||||||
|
dfb $20
|
||||||
|
dfb $40
|
||||||
|
|
||||||
|
; 1x8 (odd byte)
|
||||||
|
bitmap2
|
||||||
|
dfb $c0
|
||||||
|
dfb $a0
|
||||||
|
dfb $90
|
||||||
|
dfb $88
|
||||||
|
dfb $84
|
||||||
|
dfb $82
|
||||||
|
dfb $81
|
||||||
|
dfb $80
|
||||||
|
|
||||||
|
; 2x8
|
||||||
|
bitmap3
|
||||||
|
dfb $d5,$aa ;pure colors
|
||||||
|
dfb $aa,$d5
|
||||||
|
dfb $55,$2a
|
||||||
|
dfb $2a,$55
|
||||||
|
dfb $d5,$2a ;high bit split
|
||||||
|
dfb $aa,$55
|
||||||
|
dfb $55,$aa
|
||||||
|
dfb $2a,$d5
|
||||||
|
|
||||||
|
; 2x8
|
||||||
|
bitmap4
|
||||||
|
dfb $40,$00 ;bits at the edge
|
||||||
|
dfb $00,$01
|
||||||
|
dfb $40,$01
|
||||||
|
dfb $60,$03
|
||||||
|
dfb $40,$02
|
||||||
|
dfb $d5,$d5 ;same byte odd/even
|
||||||
|
dfb $aa,$aa
|
||||||
|
dfb $d5,$55
|
||||||
|
|
||||||
|
; 3x8 (do in B&W)
|
||||||
|
bitmap5
|
||||||
|
dfb $7f,$7f,$7f
|
||||||
|
dfb $03,$08,$60
|
||||||
|
dfb $03,$1c,$60
|
||||||
|
dfb $03,$3e,$60
|
||||||
|
dfb $03,$7f,$60
|
||||||
|
dfb $43,$77,$61
|
||||||
|
dfb $63,$63,$63
|
||||||
|
dfb $7f,$7f,$7f
|
||||||
|
|
|
@ -0,0 +1,47 @@
|
||||||
|
### 6502bench SourceGen dis65 v1.0 ###
|
||||||
|
{
|
||||||
|
"_ContentVersion":3,"FileDataLength":232,"FileDataCrc32":-2081570119,"ProjectProps":{
|
||||||
|
"CpuName":"65C02","IncludeUndocumentedInstr":false,"TwoByteBrk":false,"EntryFlags":32702671,"AutoLabelStyle":"Simple","AnalysisParams":{
|
||||||
|
"AnalyzeUncategorizedData":true,"DefaultTextScanMode":"LowHighAscii","MinCharsForString":4,"SeekNearbyTargets":true,"SmartPlpHandling":true},
|
||||||
|
"PlatformSymbolFileIdentifiers":["RT:Apple/F8-ROM.sym65","RT:Apple/Cxxx-IO.sym65"],"ExtensionScriptFileIdentifiers":["RT:Apple/VisHiRes.cs"],"ProjectSyms":{
|
||||||
|
}},
|
||||||
|
"AddressMap":[{
|
||||||
|
"Offset":0,"Addr":4096}],"TypeHints":[{
|
||||||
|
"Low":0,"High":0,"Hint":"Code"}],"StatusFlagOverrides":{
|
||||||
|
},
|
||||||
|
"Comments":{
|
||||||
|
},
|
||||||
|
"LongComments":{
|
||||||
|
"-2147483647":{
|
||||||
|
"Text":"6502bench SourceGen v1.5.0-dev1","BoxMode":false,"MaxWidth":80,"BackgroundColor":0}},
|
||||||
|
"Notes":{
|
||||||
|
},
|
||||||
|
"UserLabels":{
|
||||||
|
"114":{
|
||||||
|
"Label":"Draw","Value":4210,"Source":"User","Type":"GlobalAddr","LabelAnno":"None"}},
|
||||||
|
"OperandFormats":{
|
||||||
|
"151":{
|
||||||
|
"Length":9,"Format":"Dense","SubFormat":"None","SymbolRef":null}},
|
||||||
|
"LvTables":{
|
||||||
|
},
|
||||||
|
"VisualizationSets":{
|
||||||
|
"160":{
|
||||||
|
"Items":[{
|
||||||
|
"Tag":"vis0000a0","VisGenIdent":"apple2-hi-res-bitmap","VisGenParams":{
|
||||||
|
"offset":160,"byteWidth":1,"height":8,"colStride":0,"rowStride":0,"isColor":true,"isFirstOdd":false}}]},
|
||||||
|
"168":{
|
||||||
|
"Items":[{
|
||||||
|
"Tag":"vis0000a8","VisGenIdent":"apple2-hi-res-bitmap","VisGenParams":{
|
||||||
|
"offset":168,"byteWidth":1,"height":8,"colStride":0,"rowStride":0,"isColor":true,"isFirstOdd":true}}]},
|
||||||
|
"176":{
|
||||||
|
"Items":[{
|
||||||
|
"Tag":"vis0000b0","VisGenIdent":"apple2-hi-res-bitmap","VisGenParams":{
|
||||||
|
"offset":176,"byteWidth":2,"height":8,"colStride":0,"rowStride":0,"isColor":true,"isFirstOdd":false}}]},
|
||||||
|
"192":{
|
||||||
|
"Items":[{
|
||||||
|
"Tag":"vis0000c0","VisGenIdent":"apple2-hi-res-bitmap","VisGenParams":{
|
||||||
|
"offset":192,"byteWidth":2,"height":8,"colStride":0,"rowStride":0,"isColor":true,"isFirstOdd":false}}]},
|
||||||
|
"208":{
|
||||||
|
"Items":[{
|
||||||
|
"Tag":"vis0000d0","VisGenIdent":"apple2-hi-res-bitmap","VisGenParams":{
|
||||||
|
"offset":208,"byteWidth":3,"height":8,"colStride":0,"rowStride":0,"isColor":true,"isFirstOdd":false}}]}}}
|
BIN
SourceGen/SGTestData/Visualization/apple2-bitmap-test-AW-RGB.png
Normal file
BIN
SourceGen/SGTestData/Visualization/apple2-bitmap-test-AW-RGB.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 713 B |
|
@ -15,6 +15,7 @@
|
||||||
*/
|
*/
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
using System.Collections.ObjectModel;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
using System.Windows.Media;
|
using System.Windows.Media;
|
||||||
using System.Windows.Media.Imaging;
|
using System.Windows.Media.Imaging;
|
||||||
|
@ -26,12 +27,12 @@ namespace SourceGen {
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Graphical visualization object. Useful for displaying 2D bitmaps and 3D objects.
|
/// Graphical visualization object. Useful for displaying 2D bitmaps and 3D objects.
|
||||||
///
|
///
|
||||||
/// Treat this as generally immutable, i.e. don't modify VisGenParams. The CachedImage
|
/// This is generally immutable, except for the CachedImage field.
|
||||||
/// field is mutable.
|
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public class Visualization {
|
public class Visualization {
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Unique user-specified tag. Contents are arbitrary, but may not be empty.
|
/// Unique user-specified tag. This may be any valid string that is at least two
|
||||||
|
/// characters long after the leading and trailing whitespace have been trimmed.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public string Tag { get; private set; }
|
public string Tag { get; private set; }
|
||||||
|
|
||||||
|
@ -41,9 +42,9 @@ namespace SourceGen {
|
||||||
public string VisGenIdent { get; private set; }
|
public string VisGenIdent { get; private set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Parameters passed to the visualization generator.
|
/// Parameters to be passed to the visualization generator.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public Dictionary<string, object> VisGenParams { get; private set; }
|
public ReadOnlyDictionary<string, object> VisGenParams { get; private set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Cached reference to 2D image, useful for thumbnails. Not serialized.
|
/// Cached reference to 2D image, useful for thumbnails. Not serialized.
|
||||||
|
@ -68,7 +69,7 @@ namespace SourceGen {
|
||||||
/// <param name="visGenIdent">Visualization generator identifier.</param>
|
/// <param name="visGenIdent">Visualization generator identifier.</param>
|
||||||
/// <param name="visGenParams">Parameters for visualization generator.</param>
|
/// <param name="visGenParams">Parameters for visualization generator.</param>
|
||||||
public Visualization(string tag, string visGenIdent,
|
public Visualization(string tag, string visGenIdent,
|
||||||
Dictionary<string, object> visGenParams) {
|
ReadOnlyDictionary<string, object> visGenParams) {
|
||||||
Tag = tag;
|
Tag = tag;
|
||||||
VisGenIdent = visGenIdent;
|
VisGenIdent = visGenIdent;
|
||||||
VisGenParams = visGenParams;
|
VisGenParams = visGenParams;
|
||||||
|
|
|
@ -16,6 +16,7 @@
|
||||||
using System;
|
using System;
|
||||||
using System.Collections;
|
using System.Collections;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
using System.Collections.ObjectModel;
|
||||||
using System.Diagnostics;
|
using System.Diagnostics;
|
||||||
|
|
||||||
using PluginCommon;
|
using PluginCommon;
|
||||||
|
@ -147,7 +148,8 @@ namespace SourceGen {
|
||||||
|
|
||||||
IVisualization2d vis2d;
|
IVisualization2d vis2d;
|
||||||
try {
|
try {
|
||||||
vis2d = vplug.Generate2d(visDescr, vis.VisGenParams);
|
vis2d = vplug.Generate2d(visDescr,
|
||||||
|
new ReadOnlyDictionary<string, object>(vis.VisGenParams));
|
||||||
if (vis2d == null) {
|
if (vis2d == null) {
|
||||||
Debug.WriteLine("Vis generator returned null");
|
Debug.WriteLine("Vis generator returned null");
|
||||||
}
|
}
|
||||||
|
|
|
@ -254,7 +254,7 @@ namespace SourceGen.WpfGui {
|
||||||
private void OkButton_Click(object sender, RoutedEventArgs e) {
|
private void OkButton_Click(object sender, RoutedEventArgs e) {
|
||||||
VisualizationItem item = (VisualizationItem)visComboBox.SelectedItem;
|
VisualizationItem item = (VisualizationItem)visComboBox.SelectedItem;
|
||||||
Debug.Assert(item != null);
|
Debug.Assert(item != null);
|
||||||
Dictionary<string, object> valueDict = CreateVisGenParams();
|
ReadOnlyDictionary<string, object> valueDict = CreateVisGenParams();
|
||||||
string trimTag = Visualization.TrimAndValidateTag(TagString, out bool isTagValid);
|
string trimTag = Visualization.TrimAndValidateTag(TagString, out bool isTagValid);
|
||||||
Debug.Assert(isTagValid);
|
Debug.Assert(isTagValid);
|
||||||
NewVis = new Visualization(trimTag, item.VisDescriptor.Ident, valueDict);
|
NewVis = new Visualization(trimTag, item.VisDescriptor.Ident, valueDict);
|
||||||
|
@ -263,7 +263,7 @@ namespace SourceGen.WpfGui {
|
||||||
DialogResult = true;
|
DialogResult = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
private Dictionary<string, object> CreateVisGenParams() {
|
private ReadOnlyDictionary<string, object> CreateVisGenParams() {
|
||||||
// Generate value dictionary.
|
// Generate value dictionary.
|
||||||
Dictionary<string, object> valueDict =
|
Dictionary<string, object> valueDict =
|
||||||
new Dictionary<string, object>(ParameterList.Count);
|
new Dictionary<string, object>(ParameterList.Count);
|
||||||
|
@ -285,7 +285,7 @@ namespace SourceGen.WpfGui {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return valueDict;
|
return new ReadOnlyDictionary<string, object>(valueDict);
|
||||||
}
|
}
|
||||||
|
|
||||||
private bool ParseIntObj(object val, VisParamDescr.SpecialMode special, out int intVal) {
|
private bool ParseIntObj(object val, VisParamDescr.SpecialMode special, out int intVal) {
|
||||||
|
|
|
@ -23,7 +23,7 @@ limitations under the License.
|
||||||
xmlns:local="clr-namespace:SourceGen.WpfGui"
|
xmlns:local="clr-namespace:SourceGen.WpfGui"
|
||||||
mc:Ignorable="d"
|
mc:Ignorable="d"
|
||||||
Title="Edit Visualization Set"
|
Title="Edit Visualization Set"
|
||||||
Width="560" Height="400" ResizeMode="NoResize"
|
Width="600" Height="400" ResizeMode="NoResize"
|
||||||
ShowInTaskbar="False" WindowStartupLocation="CenterOwner"
|
ShowInTaskbar="False" WindowStartupLocation="CenterOwner"
|
||||||
Closing="Window_Closing">
|
Closing="Window_Closing">
|
||||||
|
|
||||||
|
@ -38,11 +38,16 @@ limitations under the License.
|
||||||
<ColumnDefinition Width="Auto"/>
|
<ColumnDefinition Width="Auto"/>
|
||||||
</Grid.ColumnDefinitions>
|
</Grid.ColumnDefinitions>
|
||||||
<Grid.RowDefinitions>
|
<Grid.RowDefinitions>
|
||||||
|
<RowDefinition Height="Auto"/>
|
||||||
<RowDefinition Height="*"/>
|
<RowDefinition Height="*"/>
|
||||||
<RowDefinition Height="Auto"/>
|
<RowDefinition Height="Auto"/>
|
||||||
</Grid.RowDefinitions>
|
</Grid.RowDefinitions>
|
||||||
|
|
||||||
<DataGrid Name="visualizationGrid" Grid.Column="0" Grid.Row="0" Margin="4,4,4,0"
|
<TextBlock Grid.Row="0" Foreground="Red" Margin="0,0,0,4"
|
||||||
|
Visibility="{Binding ScriptWarningVisible}"
|
||||||
|
Text="NOTE: no extension scripts with visualization generators are loaded"/>
|
||||||
|
|
||||||
|
<DataGrid Name="visualizationGrid" Grid.Column="0" Grid.Row="1" Margin="4,4,4,0"
|
||||||
ItemsSource="{Binding VisualizationList}"
|
ItemsSource="{Binding VisualizationList}"
|
||||||
IsReadOnly="True"
|
IsReadOnly="True"
|
||||||
FontFamily="{StaticResource GeneralMonoFont}"
|
FontFamily="{StaticResource GeneralMonoFont}"
|
||||||
|
@ -76,26 +81,27 @@ limitations under the License.
|
||||||
</DataTemplate>
|
</DataTemplate>
|
||||||
</DataGridTemplateColumn.CellTemplate>
|
</DataGridTemplateColumn.CellTemplate>
|
||||||
</DataGridTemplateColumn>
|
</DataGridTemplateColumn>
|
||||||
<DataGridTextColumn Header="Tag" Width="155" Binding="{Binding Tag}"/>
|
<DataGridTextColumn Header="Tag" Width="170" Binding="{Binding Tag}"/>
|
||||||
<DataGridTextColumn Header="Visualization Generator" Width="220" Binding="{Binding VisGenIdent}"/>
|
<DataGridTextColumn Header="Visualization Generator" Width="212" Binding="{Binding VisGenIdent}"/>
|
||||||
</DataGrid.Columns>
|
</DataGrid.Columns>
|
||||||
</DataGrid>
|
</DataGrid>
|
||||||
|
|
||||||
<StackPanel Grid.Column="1" Grid.Row="0">
|
<StackPanel Grid.Column="1" Grid.Row="1">
|
||||||
<Button Width="75" Margin="4" Content="_New..."
|
<Button Width="110" Margin="4" Content="_New Bitmap..."
|
||||||
IsEnabled="{Binding HasVisPlugins}" Click="NewButton_Click"/>
|
IsEnabled="{Binding HasVisPlugins}" Click="NewButton_Click"/>
|
||||||
<Button Width="75" Margin="4" Content="_Edit"
|
|
||||||
|
<Button Width="110" Margin="4,20,4,4" Content="_Edit..."
|
||||||
IsEnabled="{Binding IsEditEnabled}" Click="EditButton_Click"/>
|
IsEnabled="{Binding IsEditEnabled}" Click="EditButton_Click"/>
|
||||||
<Button Width="75" Margin="4" Content="_Remove"
|
<Button Width="110" Margin="4" Content="_Remove"
|
||||||
IsEnabled="{Binding IsRemoveEnabled}" Click="RemoveButton_Click"/>
|
IsEnabled="{Binding IsRemoveEnabled}" Click="RemoveButton_Click"/>
|
||||||
|
|
||||||
<Button Width="75" Margin="4,20,4,4" Content="_Up"
|
<Button Width="110" Margin="4,20,4,4" Content="_Up"
|
||||||
IsEnabled="{Binding IsUpEnabled}" Click="UpButton_Click"/>
|
IsEnabled="{Binding IsUpEnabled}" Click="UpButton_Click"/>
|
||||||
<Button Width="75" Margin="4,4" Content="_Down"
|
<Button Width="110" Margin="4,4" Content="_Down"
|
||||||
IsEnabled="{Binding IsDownEnabled}" Click="DownButton_Click"/>
|
IsEnabled="{Binding IsDownEnabled}" Click="DownButton_Click"/>
|
||||||
</StackPanel>
|
</StackPanel>
|
||||||
|
|
||||||
<DockPanel Grid.Column="0" Grid.Row="1" Grid.ColumnSpan="2" Margin="0,8,0,0" LastChildFill="False">
|
<DockPanel Grid.Column="0" Grid.Row="2" Grid.ColumnSpan="2" Margin="0,8,0,0" LastChildFill="False">
|
||||||
<Button DockPanel.Dock="Right" Content="Cancel" Width="70" Margin="8,0,0,0" IsCancel="True"/>
|
<Button DockPanel.Dock="Right" Content="Cancel" Width="70" Margin="8,0,0,0" IsCancel="True"/>
|
||||||
<Button DockPanel.Dock="Right" Grid.Column="1" Content="OK" Width="70"
|
<Button DockPanel.Dock="Right" Grid.Column="1" Content="OK" Width="70"
|
||||||
IsDefault="True" Click="OkButton_Click"/>
|
IsDefault="True" Click="OkButton_Click"/>
|
||||||
|
|
|
@ -50,6 +50,11 @@ namespace SourceGen.WpfGui {
|
||||||
}
|
}
|
||||||
private bool mHasVisPlugins;
|
private bool mHasVisPlugins;
|
||||||
|
|
||||||
|
public Visibility ScriptWarningVisible {
|
||||||
|
get { return mHasVisPlugins ? Visibility.Collapsed : Visibility.Visible; }
|
||||||
|
// this can't change while the dialog is open, so don't need OnPropertyChanged
|
||||||
|
}
|
||||||
|
|
||||||
public bool IsEditEnabled {
|
public bool IsEditEnabled {
|
||||||
get { return mIsEditEnabled; }
|
get { return mIsEditEnabled; }
|
||||||
set { mIsEditEnabled = value; OnPropertyChanged(); }
|
set { mIsEditEnabled = value; OnPropertyChanged(); }
|
||||||
|
@ -92,11 +97,14 @@ namespace SourceGen.WpfGui {
|
||||||
mOffset = offset;
|
mOffset = offset;
|
||||||
|
|
||||||
if (curSet != null) {
|
if (curSet != null) {
|
||||||
// Populate the ItemsSource.
|
// Populate the data grid ItemsSource.
|
||||||
foreach (Visualization vis in curSet) {
|
foreach (Visualization vis in curSet) {
|
||||||
VisualizationList.Add(vis);
|
VisualizationList.Add(vis);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if (VisualizationList.Count > 0) {
|
||||||
|
visualizationGrid.SelectedIndex = 0;
|
||||||
|
}
|
||||||
|
|
||||||
// Check to see if we have any relevant plugins. If not, disable New/Edit.
|
// Check to see if we have any relevant plugins. If not, disable New/Edit.
|
||||||
List<IPlugin> plugins = project.GetActivePlugins();
|
List<IPlugin> plugins = project.GetActivePlugins();
|
||||||
|
@ -163,6 +171,7 @@ namespace SourceGen.WpfGui {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
VisualizationList.Add(dlg.NewVis);
|
VisualizationList.Add(dlg.NewVis);
|
||||||
|
visualizationGrid.SelectedIndex = VisualizationList.Count - 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void EditButton_Click(object sender, RoutedEventArgs e) {
|
private void EditButton_Click(object sender, RoutedEventArgs e) {
|
||||||
|
@ -185,11 +194,19 @@ namespace SourceGen.WpfGui {
|
||||||
int index = VisualizationList.IndexOf(item);
|
int index = VisualizationList.IndexOf(item);
|
||||||
VisualizationList.Remove(item);
|
VisualizationList.Remove(item);
|
||||||
VisualizationList.Insert(index, dlg.NewVis);
|
VisualizationList.Insert(index, dlg.NewVis);
|
||||||
|
visualizationGrid.SelectedIndex = index;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void RemoveButton_Click(object sender, RoutedEventArgs e) {
|
private void RemoveButton_Click(object sender, RoutedEventArgs e) {
|
||||||
Visualization item = (Visualization)visualizationGrid.SelectedItem;
|
Visualization item = (Visualization)visualizationGrid.SelectedItem;
|
||||||
|
int index = VisualizationList.IndexOf(item);
|
||||||
VisualizationList.Remove(item);
|
VisualizationList.Remove(item);
|
||||||
|
if (index == VisualizationList.Count) {
|
||||||
|
index--;
|
||||||
|
}
|
||||||
|
if (index >= 0) {
|
||||||
|
visualizationGrid.SelectedIndex = index;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void UpButton_Click(object sender, RoutedEventArgs e) {
|
private void UpButton_Click(object sender, RoutedEventArgs e) {
|
||||||
|
|
Loading…
Reference in New Issue
Block a user