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.

+