diff --git a/SourceGen/AppSettings.cs b/SourceGen/AppSettings.cs
index 9d17e8e..83a7adc 100644
--- a/SourceGen/AppSettings.cs
+++ b/SourceGen/AppSettings.cs
@@ -109,6 +109,9 @@ namespace SourceGen {
public const string HEXD_ASCII_ONLY = "hexd-ascii-only";
public const string HEXD_CHAR_CONV = "hexd-char-conv1";
+ // Apple II screen chart viewer settings.
+ public const string A2SC_MODE = "a2sc-mode";
+
// ASCII chart viewer settings.
public const string ASCCH_MODE = "ascch-mode1";
diff --git a/SourceGen/MainController.cs b/SourceGen/MainController.cs
index 1efe195..1c9fdb6 100644
--- a/SourceGen/MainController.cs
+++ b/SourceGen/MainController.cs
@@ -95,6 +95,16 @@ namespace SourceGen {
///
public bool IsAsciiChartOpen { get { return mAsciiChartDialog != null; } }
+ ///
+ /// Apple II screen chart window. Not tied to the project.
+ ///
+ private Tools.WpfGui.Apple2ScreenChart mApple2ScreenChartDialog;
+
+ ///
+ /// Returns true if the ASCII chart window is currently open.
+ ///
+ public bool IsApple2ScreenChartOpen { get { return mApple2ScreenChartDialog != null; } }
+
///
/// Instruction chart reference window. Not tied to the project.
///
@@ -1315,6 +1325,7 @@ namespace SourceGen {
// WPF won't exit until all windows are closed, so any unowned windows need
// to be cleaned up here.
+ mApple2ScreenChartDialog?.Close();
mAsciiChartDialog?.Close();
mInstructionChartDialog?.Close();
mHexDumpDialog?.Close();
@@ -4161,6 +4172,20 @@ namespace SourceGen {
#region Tools
+ public void ToggleApple2ScreenChart() {
+ if (mApple2ScreenChartDialog == null) {
+ // Create without owner so it doesn't have to be in front of main window.
+ mApple2ScreenChartDialog = new Tools.WpfGui.Apple2ScreenChart(null, mFormatter);
+ mApple2ScreenChartDialog.Closing += (sender, e) => {
+ Debug.WriteLine("Apple II screen chart closed");
+ mApple2ScreenChartDialog = null;
+ };
+ mApple2ScreenChartDialog.Show();
+ } else {
+ mApple2ScreenChartDialog.Close();
+ }
+ }
+
public void ToggleAsciiChart() {
if (mAsciiChartDialog == null) {
// Create without owner so it doesn't have to be in front of main window.
diff --git a/SourceGen/RuntimeData/Help/tools.html b/SourceGen/RuntimeData/Help/tools.html
index fc1d4a0..9bd0cc7 100644
--- a/SourceGen/RuntimeData/Help/tools.html
+++ b/SourceGen/RuntimeData/Help/tools.html
@@ -31,6 +31,14 @@ pop-up list at the bottom allows you to flip between standard and "high"
ASCII.
+
+
+The Apple II text and hi-res screens are mapped to memory in a way
+that makes sense to computers but is a little confusing for humans. This
+chart maps line numbers to addresses and vice-versa. Select different
+screens and sort orders from the list at the bottom.
+
+
You can use this to view the contents of the project data file
diff --git a/SourceGen/SourceGen.csproj b/SourceGen/SourceGen.csproj
index 060c42a..10094c7 100644
--- a/SourceGen/SourceGen.csproj
+++ b/SourceGen/SourceGen.csproj
@@ -98,6 +98,9 @@
OmfViewer.xaml
+
+ Apple2ScreenChart.xaml
+
FileConcatenator.xaml
@@ -299,6 +302,10 @@
Designer
MSBuild:Compile
+
+ Designer
+ MSBuild:Compile
+
Designer
MSBuild:Compile
diff --git a/SourceGen/Tools/WpfGui/Apple2ScreenChart.xaml b/SourceGen/Tools/WpfGui/Apple2ScreenChart.xaml
new file mode 100644
index 0000000..a8ae206
--- /dev/null
+++ b/SourceGen/Tools/WpfGui/Apple2ScreenChart.xaml
@@ -0,0 +1,48 @@
+
+
+
+
+
+ Hi-Res Graphics, page 1, by line
+ Hi-Res Graphics, page 2, by line
+ Hi-Res Graphics, page 1, by address
+ Hi-Res Graphics, page 2, by address
+ Text Pages 1 & 2
+
+
+
+
+
+
+
+
diff --git a/SourceGen/Tools/WpfGui/Apple2ScreenChart.xaml.cs b/SourceGen/Tools/WpfGui/Apple2ScreenChart.xaml.cs
new file mode 100644
index 0000000..35ca9dc
--- /dev/null
+++ b/SourceGen/Tools/WpfGui/Apple2ScreenChart.xaml.cs
@@ -0,0 +1,281 @@
+/*
+ * 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;
+using System.Linq;
+using System.Text;
+using System.Windows;
+using System.Windows.Controls;
+using System.Windows.Input;
+
+using Asm65;
+
+namespace SourceGen.Tools.WpfGui {
+ ///
+ /// Apple II text/graphics memory map chart.
+ ///
+ public partial class Apple2ScreenChart : Window {
+ private const int NUM_HI_RES_ROWS = 192;
+ private const int NUM_TEXT_ROWS = 24;
+ private Formatter mFormatter;
+
+ private static int[] sHiResRowsByAddr = GenerateHiResRowsByAddr();
+ private static int[] sTextRowsByAddr = GenerateTextRowsByAddr();
+
+ public enum ChartMode {
+ Unknown = 0,
+ HiRes1_L,
+ HiRes2_L,
+ HiRes1_A,
+ HiRes2_A,
+ Text
+ };
+ public class ChartModeItem {
+ public string Name { get; private set; }
+ public ChartMode Mode { get; private set; }
+
+ public ChartModeItem(string name, ChartMode mode) {
+ Name = name;
+ Mode = mode;
+ }
+ }
+ public ChartModeItem[] ChartModeItems { get; private set; }
+
+
+ public Apple2ScreenChart(Window owner, Formatter formatter) {
+ InitializeComponent();
+ Owner = owner;
+ DataContext = this;
+
+ mFormatter = formatter;
+
+ ChartModeItems = new ChartModeItem[] {
+ new ChartModeItem((string)FindResource("str_HiRes1_L"), ChartMode.HiRes1_L),
+ new ChartModeItem((string)FindResource("str_HiRes2_L"), ChartMode.HiRes2_L),
+ new ChartModeItem((string)FindResource("str_HiRes1_A"), ChartMode.HiRes1_A),
+ new ChartModeItem((string)FindResource("str_HiRes2_A"), ChartMode.HiRes2_A),
+ new ChartModeItem((string)FindResource("str_Text"), ChartMode.Text),
+ };
+ }
+
+ public void Window_Loaded(object sender, RoutedEventArgs e) {
+ // Restore chart mode setting.
+ ChartMode mode = (ChartMode)AppSettings.Global.GetEnum(
+ AppSettings.A2SC_MODE, typeof(ChartMode), (int)ChartMode.HiRes1_L);
+ int index = 0;
+ for (int i = 0; i < ChartModeItems.Length; i++) {
+ if (ChartModeItems[i].Mode == mode) {
+ index = i;
+ break;
+ }
+ }
+ chartModeComboBox.SelectedIndex = index;
+ // should call UpdateControls via SelectionChanged
+ }
+
+ // Catch ESC key.
+ private void Window_KeyEventHandler(object sender, KeyEventArgs e) {
+ if (e.Key == Key.Escape) {
+ Close();
+ }
+ }
+
+ private void ChartModeComboBox_SelectionChanged(object sender,
+ SelectionChangedEventArgs e) {
+ UpdateControls();
+ }
+
+ private void UpdateControls() {
+ ChartModeItem item = (ChartModeItem)chartModeComboBox.SelectedItem;
+ if (item == null) {
+ // initializing
+ return;
+ }
+
+ AppSettings.Global.SetEnum(AppSettings.A2SC_MODE, typeof(ChartMode), (int)item.Mode);
+
+ string text;
+ switch (item.Mode) {
+ case ChartMode.HiRes1_L:
+ text = DrawHiRes(0x2000, true);
+ break;
+ case ChartMode.HiRes2_L:
+ text = DrawHiRes(0x4000, true);
+ break;
+ case ChartMode.HiRes1_A:
+ text = DrawHiRes(0x2000, false);
+ break;
+ case ChartMode.HiRes2_A:
+ text = DrawHiRes(0x4000, false);
+ break;
+ case ChartMode.Text:
+ text = DrawText();
+ break;
+ default:
+ text = "UNKNOWN MODE";
+ break;
+ }
+
+ chartTextBox.Text = text;
+ chartTextBox.SelectionStart = text.Length;
+ chartTextBox.SelectionLength = 0;
+ }
+
+ ///
+ /// Draws chart for hi-res graphics.
+ ///
+ /// Base address ($2000/$4000).
+ /// True if we want to sort by line number, false for address.
+ /// String with entire chart.
+ private string DrawHiRes(int baseAddr, bool byLine) {
+ const string eol = " \r\n"; // add space for balance
+ const string div = " | ";
+ string hdr;
+ if (byLine) {
+ hdr = "Line Addr ";
+ } else {
+ hdr = "Addr Line";
+ }
+
+ StringBuilder sb = new StringBuilder(
+ (hdr.Length * 4 + div.Length * 3 + eol.Length) * (NUM_HI_RES_ROWS / 4));
+
+ sb.Append(hdr);
+ sb.Append(div);
+ sb.Append(hdr);
+ sb.Append(div);
+ sb.Append(hdr);
+ sb.Append(div);
+ sb.Append(hdr);
+ sb.Append(eol);
+ for (int i = 0; i < NUM_HI_RES_ROWS / 4; i++) {
+ DrawHiResEntry(baseAddr, byLine, i, sb);
+ sb.Append(div);
+ DrawHiResEntry(baseAddr, byLine, i + NUM_HI_RES_ROWS / 4, sb);
+ sb.Append(div);
+ DrawHiResEntry(baseAddr, byLine, i + (NUM_HI_RES_ROWS * 2) / 4, sb);
+ sb.Append(div);
+ DrawHiResEntry(baseAddr, byLine, i + (NUM_HI_RES_ROWS * 3) / 4, sb);
+ sb.Append(eol);
+ }
+ return sb.ToString();
+ }
+
+ private void DrawHiResEntry(int baseAddr, bool byLine, int index, StringBuilder sb) {
+ if (byLine) {
+ sb.AppendFormat("{0,3:D} {1}", index,
+ mFormatter.FormatHexValue(HiResRowToAddr(baseAddr, index), 4));
+ } else {
+ int row = sHiResRowsByAddr[index];
+ sb.AppendFormat("{1} {0,3:D}", row,
+ mFormatter.FormatHexValue(HiResRowToAddr(baseAddr, row), 4));
+ }
+ }
+
+ ///
+ /// Generates the address of a line on the hi-res screen.
+ ///
+ /// Base address ($2000 or $4000).
+ /// Row number, 0-191.
+ /// Address of start of line.
+ private static int HiResRowToAddr(int baseAddr, int row) {
+ // If row is ABCDEFGH, we want pppFGHCD EABAB000 (where p would be $20/$40).
+ int low = ((row & 0xc0) >> 1) | ((row & 0xc0) >> 3) | ((row & 0x08) << 4);
+ int high = ((row & 0x07) << 2) | ((row & 0x30) >> 4);
+ int rowAddr = baseAddr + ((high << 8) | low);
+ return rowAddr;
+ }
+
+ ///
+ /// Generates a sorted list of hi-res row numbers. The ordering is determined by the
+ /// address in memory of the row.
+ ///
+ /// List of rows, in memory order.
+ private static int[] GenerateHiResRowsByAddr() {
+ SortedList addrList = new SortedList(NUM_HI_RES_ROWS);
+ for (int i = 0; i < NUM_HI_RES_ROWS; i++) {
+ addrList.Add(HiResRowToAddr(0, i), i);
+ }
+
+ return addrList.Values.ToArray();
+ }
+
+ ///
+ /// Draws chart for the text screen. There are few enough rows that we can do
+ /// by-line and by-address for both pages in a reasonable amount of space.
+ ///
+ /// String with entire chart.
+ private string DrawText() {
+ const string eol = " \r\n"; // add space for balance
+ const string div = " | ";
+ const string hdr1 = "Line Page1 Page2";
+ const string hdr2 = "Page1 Page2 Line";
+
+ StringBuilder sb = new StringBuilder(
+ (hdr1.Length * 2 + div.Length * 1 + eol.Length) * NUM_TEXT_ROWS);
+
+ sb.Append(hdr1);
+ sb.Append(div);
+ sb.Append(hdr2);
+ sb.Append(eol);
+ for (int i = 0; i < NUM_TEXT_ROWS; i++) {
+ const int base1 = 0x400;
+ const int base2 = 0x800;
+
+ int textRow = sTextRowsByAddr[i];
+
+ sb.AppendFormat(" {0,2:D} {1} {2}", i,
+ mFormatter.FormatHexValue(TextRowToAddr(base1, i), 4),
+ mFormatter.FormatHexValue(TextRowToAddr(base2, i), 4));
+ sb.Append(div);
+ sb.AppendFormat("{1} {2} {0,2:D}", textRow,
+ mFormatter.FormatHexValue(TextRowToAddr(base1, textRow), 4),
+ mFormatter.FormatHexValue(TextRowToAddr(base2, textRow), 4));
+ sb.Append(eol);
+ }
+ return sb.ToString();
+ }
+
+ ///
+ /// Generates the address of a line on the text screen.
+ ///
+ /// Base address (0x0400 or 0x0800).
+ /// Row number (0-23).
+ /// Address of start of line.
+ private static int TextRowToAddr(int baseAddr, int row) {
+ // If row is 000ABCDE, we want 0000ppCD EABAB000 (where p is $04/$08).
+ int high = (row & 0x06) >> 1;
+ int low = (row & 0x18) | ((row & 0x18) << 2) | ((row & 0x01) << 7);
+ int rowAddr = baseAddr + ((high << 8) | low);
+ return rowAddr;
+ }
+
+ ///
+ /// Generates a sorted list of text row numbers. The ordering is determined by the
+ /// address in memory of the row.
+ ///
+ /// List of rows, in memory order.
+ private static int[] GenerateTextRowsByAddr() {
+ SortedList addrList = new SortedList(NUM_TEXT_ROWS);
+ for (int i = 0; i < NUM_TEXT_ROWS; i++) {
+ addrList.Add(TextRowToAddr(0, i), i);
+ }
+
+ return addrList.Values.ToArray();
+ }
+ }
+}
diff --git a/SourceGen/WpfGui/MainWindow.xaml b/SourceGen/WpfGui/MainWindow.xaml
index 9a1a9c7..075f85b 100644
--- a/SourceGen/WpfGui/MainWindow.xaml
+++ b/SourceGen/WpfGui/MainWindow.xaml
@@ -185,6 +185,7 @@ limitations under the License.
+
@@ -326,6 +327,8 @@ limitations under the License.
CanExecute="IsProjectOpen" Executed="ShowHexDumpCmd_Executed"/>
+
+
diff --git a/SourceGen/WpfGui/MainWindow.xaml.cs b/SourceGen/WpfGui/MainWindow.xaml.cs
index 35c57b4..f62ad67 100644
--- a/SourceGen/WpfGui/MainWindow.xaml.cs
+++ b/SourceGen/WpfGui/MainWindow.xaml.cs
@@ -1369,6 +1369,10 @@ namespace SourceGen.WpfGui {
mMainCtrl.SliceFiles();
}
+ private void ToggleApple2ScreenChartCmd_Executed(object sender, ExecutedRoutedEventArgs e) {
+ mMainCtrl.ToggleApple2ScreenChart();
+ }
+
private void ToggleAsciiChartCmd_Executed(object sender, ExecutedRoutedEventArgs e) {
mMainCtrl.ToggleAsciiChart();
}
@@ -1571,6 +1575,7 @@ namespace SourceGen.WpfGui {
toggleDataScanMenuItem.IsChecked = mMainCtrl.IsAnalyzeUncategorizedDataEnabled;
}
private void ToolsMenu_SubmenuOpened(object sender, RoutedEventArgs e) {
+ toggleApple2ScreenChartMenuItem.IsChecked = mMainCtrl.IsApple2ScreenChartOpen;
toggleAsciiChartMenuItem.IsChecked = mMainCtrl.IsAsciiChartOpen;
toggleInstructionChartMenuItem.IsChecked = mMainCtrl.IsInstructionChartOpen;
}