1
0
mirror of https://github.com/fadden/6502bench.git synced 2024-06-12 08:29:29 +00:00

Add Apple II screen memory chart

Show mapping between lines on the text and hi-res screens and
locations in memory.
This commit is contained in:
Andy McFadden 2020-08-20 13:18:46 -07:00
parent b168db7750
commit 2dfdb3234b
8 changed files with 382 additions and 0 deletions

View File

@ -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";

View File

@ -95,6 +95,16 @@ namespace SourceGen {
/// </summary>
public bool IsAsciiChartOpen { get { return mAsciiChartDialog != null; } }
/// <summary>
/// Apple II screen chart window. Not tied to the project.
/// </summary>
private Tools.WpfGui.Apple2ScreenChart mApple2ScreenChartDialog;
/// <summary>
/// Returns true if the ASCII chart window is currently open.
/// </summary>
public bool IsApple2ScreenChartOpen { get { return mApple2ScreenChartDialog != null; } }
/// <summary>
/// Instruction chart reference window. Not tied to the project.
/// </summary>
@ -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.

View File

@ -31,6 +31,14 @@ pop-up list at the bottom allows you to flip between standard and "high"
ASCII.</p>
<h2><a name="apple2-screen-chart">Apple II Screen Chart</a></h2>
<p>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.</p>
<h2><a name="hexdump">Hex Dump Viewer</a></h2>
<p>You can use this to view the contents of the project data file

View File

@ -98,6 +98,9 @@
<DependentUpon>OmfViewer.xaml</DependentUpon>
</Compile>
<Compile Include="Tools\VirtualHexDump.cs" />
<Compile Include="Tools\WpfGui\Apple2ScreenChart.xaml.cs">
<DependentUpon>Apple2ScreenChart.xaml</DependentUpon>
</Compile>
<Compile Include="Tools\WpfGui\FileConcatenator.xaml.cs">
<DependentUpon>FileConcatenator.xaml</DependentUpon>
</Compile>
@ -299,6 +302,10 @@
<SubType>Designer</SubType>
<Generator>MSBuild:Compile</Generator>
</Page>
<Page Include="Tools\WpfGui\Apple2ScreenChart.xaml">
<SubType>Designer</SubType>
<Generator>MSBuild:Compile</Generator>
</Page>
<Page Include="Tools\WpfGui\FileConcatenator.xaml">
<SubType>Designer</SubType>
<Generator>MSBuild:Compile</Generator>

View File

@ -0,0 +1,48 @@
<!--
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.
-->
<Window x:Class="SourceGen.Tools.WpfGui.Apple2ScreenChart"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:system="clr-namespace:System;assembly=mscorlib"
xmlns:local="clr-namespace:SourceGen.Tools.WpfGui"
mc:Ignorable="d"
Title="Apple II Screen Memory Chart"
SizeToContent="WidthAndHeight" ResizeMode="NoResize"
ShowInTaskbar="True"
Loaded="Window_Loaded"
PreviewKeyDown="Window_KeyEventHandler">
<Window.Resources>
<system:String x:Key="str_HiRes1_L">Hi-Res Graphics, page 1, by line</system:String>
<system:String x:Key="str_HiRes2_L">Hi-Res Graphics, page 2, by line</system:String>
<system:String x:Key="str_HiRes1_A">Hi-Res Graphics, page 1, by address</system:String>
<system:String x:Key="str_HiRes2_A">Hi-Res Graphics, page 2, by address</system:String>
<system:String x:Key="str_Text">Text Pages 1 &amp; 2</system:String>
</Window.Resources>
<StackPanel Margin="8">
<!-- using a TextBox instead of a TextBlock so it's copyable -->
<TextBox Name="chartTextBox" IsReadOnly="True"
Text="Line Addr | Line Addr | Line Addr | Line Addr "
FontFamily="{StaticResource GeneralMonoFont}"/>
<ComboBox Name="chartModeComboBox" Width="250" HorizontalAlignment="Left" Margin="0,4,0,0"
ItemsSource="{Binding ChartModeItems}" DisplayMemberPath="Name"
SelectionChanged="ChartModeComboBox_SelectionChanged"/>
</StackPanel>
</Window>

View File

@ -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 {
/// <summary>
/// Apple II text/graphics memory map chart.
/// </summary>
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;
}
/// <summary>
/// Draws chart for hi-res graphics.
/// </summary>
/// <param name="baseAddr">Base address ($2000/$4000).</param>
/// <param name="byLine">True if we want to sort by line number, false for address.</param>
/// <returns>String with entire chart.</returns>
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));
}
}
/// <summary>
/// Generates the address of a line on the hi-res screen.
/// </summary>
/// <param name="baseAddr">Base address ($2000 or $4000).</param>
/// <param name="row">Row number, 0-191.</param>
/// <returns>Address of start of line.</returns>
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;
}
/// <summary>
/// Generates a sorted list of hi-res row numbers. The ordering is determined by the
/// address in memory of the row.
/// </summary>
/// <returns>List of rows, in memory order.</returns>
private static int[] GenerateHiResRowsByAddr() {
SortedList<int, int> addrList = new SortedList<int, int>(NUM_HI_RES_ROWS);
for (int i = 0; i < NUM_HI_RES_ROWS; i++) {
addrList.Add(HiResRowToAddr(0, i), i);
}
return addrList.Values.ToArray();
}
/// <summary>
/// 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.
/// </summary>
/// <returns>String with entire chart.</returns>
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();
}
/// <summary>
/// Generates the address of a line on the text screen.
/// </summary>
/// <param name="baseAddr">Base address (0x0400 or 0x0800).</param>
/// <param name="row">Row number (0-23).</param>
/// <returns>Address of start of line.</returns>
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;
}
/// <summary>
/// Generates a sorted list of text row numbers. The ordering is determined by the
/// address in memory of the row.
/// </summary>
/// <returns>List of rows, in memory order.</returns>
private static int[] GenerateTextRowsByAddr() {
SortedList<int, int> addrList = new SortedList<int, int>(NUM_TEXT_ROWS);
for (int i = 0; i < NUM_TEXT_ROWS; i++) {
addrList.Add(TextRowToAddr(0, i), i);
}
return addrList.Values.ToArray();
}
}
}

View File

@ -185,6 +185,7 @@ limitations under the License.
<RoutedUICommand x:Key="ShowFileHexDumpCmd" Text="File Hex Dump..."/>
<RoutedUICommand x:Key="ShowHexDumpCmd" Text="Show Hex Dump"/>
<RoutedUICommand x:Key="SliceFilesCmd" Text="Slice Files..."/>
<RoutedUICommand x:Key="ToggleApple2ScreenChartCmd" Text="Apple II Screen Chart"/>
<RoutedUICommand x:Key="ToggleAsciiChartCmd" Text="ASCII Chart"/>
<RoutedUICommand x:Key="ToggleDataScanCmd" Text="Toggle Data Scan">
<RoutedUICommand.InputGestures>
@ -326,6 +327,8 @@ limitations under the License.
CanExecute="IsProjectOpen" Executed="ShowHexDumpCmd_Executed"/>
<CommandBinding Command="{StaticResource SliceFilesCmd}"
Executed="SliceFilesCmd_Executed"/>
<CommandBinding Command="{StaticResource ToggleApple2ScreenChartCmd}"
Executed="ToggleApple2ScreenChartCmd_Executed"/>
<CommandBinding Command="{StaticResource ToggleAsciiChartCmd}"
Executed="ToggleAsciiChartCmd_Executed"/>
<CommandBinding Command="{StaticResource ToggleDataScanCmd}"
@ -447,6 +450,8 @@ limitations under the License.
Command="{StaticResource ToggleInstructionChartCmd}" IsCheckable="True"/>
<MenuItem Name="toggleAsciiChartMenuItem"
Command="{StaticResource ToggleAsciiChartCmd}" IsCheckable="True"/>
<MenuItem Name="toggleApple2ScreenChartMenuItem"
Command="{StaticResource ToggleApple2ScreenChartCmd}" IsCheckable="True"/>
<Separator/>
<MenuItem Command="{StaticResource ShowFileHexDumpCmd}"/>
<MenuItem Command="{StaticResource ConcatenateFilesCmd}"/>

View File

@ -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;
}