From 100d2ffc13b0848c641b2fe507deb3e6b9a61a9c Mon Sep 17 00:00:00 2001
From: Andy McFadden
Date: Thu, 14 May 2020 15:34:05 -0700
Subject: [PATCH] Add NES visualization generator
Added a visualizer for the CHR ROM pattern tables, and a semi-useful
visualizer for tile grids.
Also added a few chars in an 8x8 font that visualizers can use to
label things.
---
CommonUtil/Font8x8.cs | 244 ++++++++++++++
PluginCommon/VisBitmap8.cs | 31 ++
SourceGen/RuntimeData/Help/visualization.html | 26 +-
SourceGen/RuntimeData/Nintendo/VisNES.cs | 307 ++++++++++++++++++
4 files changed, 606 insertions(+), 2 deletions(-)
create mode 100644 CommonUtil/Font8x8.cs
create mode 100644 SourceGen/RuntimeData/Nintendo/VisNES.cs
diff --git a/CommonUtil/Font8x8.cs b/CommonUtil/Font8x8.cs
new file mode 100644
index 0000000..cee9e6a
--- /dev/null
+++ b/CommonUtil/Font8x8.cs
@@ -0,0 +1,244 @@
+/*
+ * Copyright 2020 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.Generic;
+using System.Diagnostics;
+
+namespace CommonUtil {
+ public static class Font8x8 {
+ public static int[] GetBitData(char ch) {
+ if (sBitData == null) {
+ InitBitData();
+ }
+
+ int index = MapChar(ch);
+ return sBitData[index];
+ }
+
+ private static int MapChar(char ch) {
+ if (ch == ' ') {
+ return 1;
+ } else if (ch >= '0' && ch <= '9') {
+ return ch - '0' + 2;
+ } else if (ch >= 'A' && ch <= 'F') {
+ return ch - 'A' + 12;
+ } else {
+ return 0;
+ }
+ }
+
+ private static List sBitData;
+
+ ///
+ /// Converts the easy-to-edit string data into easy-to-process bitmaps.
+ ///
+ private static void InitBitData() {
+ Debug.Assert(sBitData == null);
+ sBitData = new List(sFontData.Length);
+
+ for (int i = 0; i < sFontData.Length; i++) {
+ int[] bits = new int[8];
+ string str = sFontData[i];
+
+ for (int row = 0; row < 8; row++) {
+ byte data = 0;
+ for (int col = 0; col < 8; col++) {
+ data <<= 1;
+
+ char ch = str[row * 8 + col];
+ if (ch == '#') {
+ data |= 1;
+ } else if (ch != '.') {
+ Debug.WriteLine("Unknown char '" + ch + "' in Font8x8 data " + i);
+ }
+ }
+
+ bits[row] = data;
+ }
+
+ sBitData.Add(bits);
+ }
+ }
+
+ private static string[] sFontData = {
+ // unknown value (U+FFFD)
+ "..###..." +
+ ".#####.." +
+ "###.###." +
+ "##.#.##." +
+ "###.###." +
+ ".#####.." +
+ "..##...." +
+ "........",
+
+ // ' '
+ "........" +
+ "........" +
+ "........" +
+ "........" +
+ "........" +
+ "........" +
+ "........" +
+ "........",
+
+ // '0'
+ ".#####.." +
+ "#.....#." +
+ "#...#.#." +
+ "#..#..#." +
+ "#.#...#." +
+ "#.....#." +
+ ".#####.." +
+ "........",
+ // '1'
+ "...#...." +
+ "..##...." +
+ ".#.#...." +
+ "...#...." +
+ "...#...." +
+ "...#...." +
+ ".#####.." +
+ "........",
+ // '2'
+ ".#####.." +
+ "#.....#." +
+ "......#." +
+ ".#####.." +
+ "#......." +
+ "#......." +
+ "#######." +
+ "........",
+ // '3'
+ "######.." +
+ "......#." +
+ "......#." +
+ ".#####.." +
+ "......#." +
+ "......#." +
+ "######.." +
+ "........",
+ // '4'
+ ".....#.." +
+ "....##.." +
+ "...#.#.." +
+ "..#..#.." +
+ ".#...#.." +
+ "#######." +
+ ".....#.." +
+ "........",
+ // '5'
+ "#######." +
+ "#......." +
+ "#......." +
+ ".#####.." +
+ "......#." +
+ "#.....#." +
+ ".#####.." +
+ "........",
+ // '6'
+ ".#####.." +
+ "#.....#." +
+ "#......." +
+ "######.." +
+ "#.....#." +
+ "#.....#." +
+ ".#####.." +
+ "........",
+ // ' '
+ "#######." +
+ "......#." +
+ ".....#.." +
+ "....#..." +
+ "...#...." +
+ "..#....." +
+ ".#......" +
+ "........",
+ // ' '
+ ".#####.." +
+ "#.....#." +
+ "#.....#." +
+ ".#####.." +
+ "#.....#." +
+ "#.....#." +
+ ".#####.." +
+ "........",
+ // '9'
+ ".#####.." +
+ "#.....#." +
+ "#.....#." +
+ ".######." +
+ "......#." +
+ "......#." +
+ ".#####.." +
+ "........",
+
+ // 'A'
+ "...#...." +
+ "..#.#..." +
+ ".#...#.." +
+ "#######." +
+ "#.....#." +
+ "#.....#." +
+ "#.....#." +
+ "........",
+ // 'B'
+ "######.." +
+ "#.....#." +
+ "#.....#." +
+ "######.." +
+ "#.....#." +
+ "#.....#." +
+ "######.." +
+ "........",
+ // 'C'
+ ".#####.." +
+ "#.....#." +
+ "#......." +
+ "#......." +
+ "#......." +
+ "#.....#." +
+ ".#####.." +
+ "........",
+ // 'D'
+ "######.." +
+ "#.....#." +
+ "#.....#." +
+ "#.....#." +
+ "#.....#." +
+ "#.....#." +
+ "######.." +
+ "........",
+ // 'E'
+ "#######." +
+ "#......." +
+ "#......." +
+ "######.." +
+ "#......." +
+ "#......." +
+ "#######." +
+ "........",
+ // 'F'
+ "#######." +
+ "#......." +
+ "#......." +
+ "######.." +
+ "#......." +
+ "#......." +
+ "#......." +
+ "........",
+ };
+ }
+}
diff --git a/PluginCommon/VisBitmap8.cs b/PluginCommon/VisBitmap8.cs
index 3f7d9c2..8f5318e 100644
--- a/PluginCommon/VisBitmap8.cs
+++ b/PluginCommon/VisBitmap8.cs
@@ -16,6 +16,8 @@
using System;
using System.Diagnostics;
+using CommonUtil;
+
namespace PluginCommon {
///
/// Bitmap with 8-bit palette indices, for use with visualization generators.
@@ -137,5 +139,34 @@ namespace PluginCommon {
public void AddColor(byte a, byte r, byte g, byte b) {
AddColor(Util.MakeARGB(a, r, g, b));
}
+
+ ///
+ /// Draws an 8x8 character on the bitmap.
+ ///
+ /// Bitma to draw on.
+ /// Character to draw.
+ /// X coord of upper-left pixel.
+ /// Y coord of upper-left pixel.
+ /// Foreground color index.
+ /// Background color index.
+ public static void DrawChar(VisBitmap8 vb, char ch, int xc, int yc,
+ byte foreColor, byte backColor) {
+ int origXc = xc;
+ int[] charBits = Font8x8.GetBitData(ch);
+ for (int row = 0; row < 8; row++) {
+ int rowBits = charBits[row];
+ for (int col = 7; col >= 0; col--) {
+ if ((rowBits & (1 << col)) != 0) {
+ vb.SetPixelIndex(xc, yc, foreColor);
+ } else {
+ vb.SetPixelIndex(xc, yc, backColor);
+ }
+ xc++;
+ }
+
+ xc = origXc;
+ yc++;
+ }
+ }
}
}
diff --git a/SourceGen/RuntimeData/Help/visualization.html b/SourceGen/RuntimeData/Help/visualization.html
index 4bf4c46..7f1b08e 100644
--- a/SourceGen/RuntimeData/Help/visualization.html
+++ b/SourceGen/RuntimeData/Help/visualization.html
@@ -229,8 +229,9 @@ is the shape number.
The Atari 2600 graphics system has registers that determine the
appearance of a sprite or playfield on a single row. The register
values are typically changed as the screen is drawn to get different
-data on successive rows. The visualization generator works for data
-stored in a straightforward fashion.
+data on successive rows. The visualization generator doesn't attempt
+to emulate this behavior, but works well for data stored in a
+straightforward fashion.
- Sprite - basic 1xN sprite, converted to an image 8 pixels
@@ -245,6 +246,17 @@ stored in a straightforward fashion.
repeated as-is or flipped.
+Atari Arcade - Atari/VisAVG
+
+Different versions of Atari's Analog Vector Graphics were used in
+several games, notably Battlezone, Tempest, and Star Wars. The commands
+drove a vector display monitor. SourceGen visualizes them as 2D
+wireframes, which isn't a perfect fit since they can describe points as
+well as lines, but works fine for annotating a disassembly.
+The visualizer takes two arguments: the offset of the start of
+the commands to visualize, and the base address of vector RAM. The latter
+is necessary to convert AVG JMP/JSR commands into offsets.
+
Commodore 64 - Commodore/VisC64
The Commodore 64 has a 64-bit sprite format defined by the hardware.
@@ -280,6 +292,16 @@ It comes in two basic varieties:
a sprite that is doubled in both width and height will look exactly like
a sprite that is not doubled at all.
+Nintendo Entertainment System - Nintendo/VisNES
+
+NES PPU pattern tables hold 8x8 tiles with 2 bits of color per pixel.
+Converting the full collection to a reference bitmap is straightforward.
+A few color palette options are offered.
+
+Sprites and backgrounds are formed from collections of tiles. In
+some cases this is straightfoward, in others it's not. A visualization
+generator that renders a "tile grid" is available for simpler cases.
+