mirror of
https://github.com/fadden/6502bench.git
synced 2024-12-28 01:29:29 +00:00
Add an Applesoft to HTML converter
This is not an official thing, so I'm hiding it in the DEBUG menu for now.
This commit is contained in:
parent
eb99a716f1
commit
8505a5cdd4
@ -3813,6 +3813,33 @@ namespace SourceGen {
|
||||
ScriptManager.UseKeepAliveHack = !ScriptManager.UseKeepAliveHack;
|
||||
}
|
||||
|
||||
public void Debug_ApplesoftToHtml() {
|
||||
OpenFileDialog fileDlg = new OpenFileDialog() {
|
||||
Filter = Res.Strings.FILE_FILTER_ALL,
|
||||
FilterIndex = 1
|
||||
};
|
||||
if (fileDlg.ShowDialog() != true) {
|
||||
return;
|
||||
}
|
||||
|
||||
byte[] data;
|
||||
string basPathName = Path.GetFullPath(fileDlg.FileName);
|
||||
try {
|
||||
data = File.ReadAllBytes(basPathName);
|
||||
} catch (Exception ex) {
|
||||
// not expecting this to happen
|
||||
MessageBox.Show(ex.Message);
|
||||
return;
|
||||
}
|
||||
|
||||
Tools.ApplesoftToHtml conv = new Tools.ApplesoftToHtml();
|
||||
string html = conv.Convert(data);
|
||||
|
||||
Tools.WpfGui.ShowText showTextDlg = new Tools.WpfGui.ShowText(mMainWin, html);
|
||||
showTextDlg.Title = "Applesoft to HTML";
|
||||
showTextDlg.ShowDialog();
|
||||
}
|
||||
|
||||
#endregion Debug features
|
||||
}
|
||||
}
|
||||
|
@ -84,6 +84,7 @@
|
||||
<Compile Include="Tests\WpfGui\GenTestRunner.xaml.cs">
|
||||
<DependentUpon>GenTestRunner.xaml</DependentUpon>
|
||||
</Compile>
|
||||
<Compile Include="Tools\ApplesoftToHtml.cs" />
|
||||
<Compile Include="Tools\VirtualHexDump.cs" />
|
||||
<Compile Include="Tools\WpfGui\InstructionChart.xaml.cs">
|
||||
<DependentUpon>InstructionChart.xaml</DependentUpon>
|
||||
|
249
SourceGen/Tools/ApplesoftToHtml.cs
Normal file
249
SourceGen/Tools/ApplesoftToHtml.cs
Normal file
@ -0,0 +1,249 @@
|
||||
/*
|
||||
* Copyright 2019 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.Diagnostics;
|
||||
using System.Text;
|
||||
using System.Windows.Media;
|
||||
|
||||
using CommonUtil;
|
||||
|
||||
namespace SourceGen.Tools {
|
||||
/// <summary>
|
||||
/// Convert Applesoft BASIC to HTML.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// This is essentially a port of the CiderPress converter
|
||||
/// (https://github.com/fadden/ciderpress/blob/master/reformat/BASIC.cpp).
|
||||
/// </remarks>
|
||||
public class ApplesoftToHtml {
|
||||
/*
|
||||
* Applesoft BASIC file format:
|
||||
*
|
||||
* <16-bit file length> [DOS 3.3 only; not visible here]
|
||||
* <line> ...
|
||||
* <EOF marker ($0000)>
|
||||
*
|
||||
* Each line consists of:
|
||||
* <16-bit address of next line (relative to start)>
|
||||
* <16-bit line number, usually 0-63999>
|
||||
* <tokens | characters> ...
|
||||
* <EOL marker ($00)>
|
||||
*
|
||||
* All values are little-endian. Numbers are stored as characters.
|
||||
*/
|
||||
|
||||
private static readonly string[] TOKENS = new string[128] {
|
||||
"END", "FOR", "NEXT", "DATA", "INPUT", "DEL", "DIM", "READ",
|
||||
"GR", "TEXT", "PR#", "IN#", "CALL", "PLOT", "HLIN", "VLIN",
|
||||
"HGR2", "HGR", "HCOLOR=", "HPLOT", "DRAW", "XDRAW", "HTAB", "HOME",
|
||||
"ROT=", "SCALE=", "SHLOAD", "TRACE", "NOTRACE", "NORMAL", "INVERSE", "FLASH",
|
||||
"COLOR=", "POP", "VTAB", "HIMEM:", "LOMEM:", "ONERR", "RESUME", "RECALL",
|
||||
"STORE", "SPEED=", "LET", "GOTO", "RUN", "IF", "RESTORE", "&",
|
||||
"GOSUB", "RETURN", "REM", "STOP", "ON", "WAIT", "LOAD", "SAVE",
|
||||
"DEF", "POKE", "PRINT", "CONT", "LIST", "CLEAR", "GET", "NEW",
|
||||
"TAB(", "TO", "FN", "SPC(", "THEN", "AT", "NOT", "STEP",
|
||||
"+", "-", "*", "/", "^", "AND", "OR", ">",
|
||||
"=", "<", "SGN", "INT", "ABS", "USR", "FRE", "SCRN(",
|
||||
"PDL", "POS", "SQR", "RND", "LOG", "EXP", "COS", "SIN",
|
||||
"TAN", "ATN", "PEEK", "LEN", "STR$", "VAL", "ASC", "CHR$",
|
||||
"LEFT$", "RIGHT$", "MID$", "ERROR", "ERROR", "ERROR", "ERROR", "ERROR",
|
||||
"ERROR", "ERROR", "ERROR", "ERROR", "ERROR", "ERROR", "ERROR", "ERROR",
|
||||
"ERROR", "ERROR", "ERROR", "ERROR", "ERROR", "ERROR", "ERROR", "ERROR"
|
||||
};
|
||||
private const int TOK_REM = 0xb2;
|
||||
|
||||
private Color mDefaultColor = Color.FromArgb(0xff, 0x40, 0x40, 0x40); // Dark Grey
|
||||
private Color mLineNumColor = Color.FromArgb(0xff, 0x40, 0x40, 0x40); // Dark Grey
|
||||
private Color mKeywordColor = Color.FromArgb(0xff, 0x00, 0x00, 0x00); // Black
|
||||
private Color mCommentColor = Color.FromArgb(0xff, 0x00, 0x80, 0x00); // Medium Green
|
||||
private Color mStringColor = Color.FromArgb(0xff, 0x00, 0x00, 0x80); // Medium Blue
|
||||
private Color mColonColor = Color.FromArgb(0xff, 0xff, 0x00, 0x00); // Red
|
||||
|
||||
private Color mCurrentColor;
|
||||
private bool mInSpan;
|
||||
|
||||
public ApplesoftToHtml() {
|
||||
// maybe do something with alternate color maps?
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Converts a buffer with an Applesoft program into colorful HTML.
|
||||
/// </summary>
|
||||
/// <param name="data">Applesoft program.</param>
|
||||
/// <returns>String with formatted text.</returns>
|
||||
public string Convert(byte[] data) {
|
||||
Debug.WriteLine("Conversion starting");
|
||||
|
||||
if (data.Length < 2) {
|
||||
return "BAS truncated?";
|
||||
}
|
||||
|
||||
InitColor();
|
||||
|
||||
StringBuilder sb = new StringBuilder();
|
||||
sb.Append("<div>\r\n");
|
||||
|
||||
// Set the default color in the <pre> style.
|
||||
sb.Append("<pre style=\"");
|
||||
OutputHtmlColor(mDefaultColor, sb);
|
||||
sb.Append("\">");
|
||||
|
||||
int offset = 0;
|
||||
while (offset < data.Length) {
|
||||
int nextAddr, lineNum;
|
||||
bool inQuote = false;
|
||||
bool inRem = false;
|
||||
|
||||
nextAddr = Read16(data, ref offset);
|
||||
if (nextAddr == 0) {
|
||||
if (data.Length - offset > 1) {
|
||||
Debug.WriteLine("WARNING: BAS ended early, at +" + offset.ToString("x6"));
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
// print line number
|
||||
lineNum = Read16(data, ref offset);
|
||||
SetColor(mLineNumColor, sb);
|
||||
sb.Append(' ');
|
||||
sb.Append(lineNum);
|
||||
sb.Append(' ');
|
||||
SetColor(mDefaultColor, sb);
|
||||
|
||||
while (offset < data.Length && data[offset] != 0) {
|
||||
char tokVal = (char)data[offset];
|
||||
if ((tokVal & 0x80) != 0) {
|
||||
// token
|
||||
SetColor(mKeywordColor, sb);
|
||||
sb.Append(' ');
|
||||
sb.Append(TOKENS[tokVal & 0x7f]);
|
||||
sb.Append(' ');
|
||||
SetColor(mDefaultColor, sb);
|
||||
|
||||
if (tokVal == TOK_REM) {
|
||||
// REM -- do rest of line in green
|
||||
SetColor(mCommentColor, sb);
|
||||
inRem = true;
|
||||
}
|
||||
} else {
|
||||
// non-token character
|
||||
if (tokVal == '"' && !inRem) {
|
||||
if (!inQuote) {
|
||||
SetColor(mStringColor, sb);
|
||||
sb.Append(tokVal);
|
||||
} else {
|
||||
sb.Append(tokVal);
|
||||
SetColor(mDefaultColor, sb);
|
||||
}
|
||||
inQuote = !inQuote;
|
||||
} else if (tokVal == ':' && !inRem && !inQuote) {
|
||||
SetColor(mColonColor, sb);
|
||||
sb.Append(tokVal);
|
||||
SetColor(mDefaultColor, sb);
|
||||
} else if (inRem && tokVal == '\r') {
|
||||
sb.Append("\r\n"); // embedded CR
|
||||
} else if (tokVal < 0x20) {
|
||||
// control character, in string or REM
|
||||
//sb.Append("•");
|
||||
// output with Unicode "control pictures" block
|
||||
sb.Append("&#x");
|
||||
sb.Append((tokVal + 0x2400).ToString("x4"));
|
||||
sb.Append(";");
|
||||
} else {
|
||||
// Output as ASCII value.
|
||||
sb.Append(tokVal);
|
||||
}
|
||||
}
|
||||
|
||||
offset++;
|
||||
}
|
||||
|
||||
SetColor(mDefaultColor, sb);
|
||||
inQuote = inRem = false;
|
||||
|
||||
offset++;
|
||||
|
||||
sb.Append("\r\n");
|
||||
}
|
||||
|
||||
sb.Append("</pre>\r\n");
|
||||
sb.Append("</div>\r\n");
|
||||
|
||||
return sb.ToString();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes the color management.
|
||||
/// </summary>
|
||||
private void InitColor() {
|
||||
mCurrentColor = mDefaultColor;
|
||||
mInSpan = false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Changes the current text color. Does nothing if the new color is the same as the
|
||||
/// old color. Special-cases the default color.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// This approach is sub-optimal -- if we have two tokens in a row they'll be put
|
||||
/// in separate spans. It works well enough for now.
|
||||
/// </remarks>
|
||||
/// <param name="newColor">Color to change to.</param>
|
||||
/// <param name="sb">StringBuilder that holds output.</param>
|
||||
private void SetColor(Color newColor, StringBuilder sb) {
|
||||
if (newColor == mCurrentColor) {
|
||||
return;
|
||||
}
|
||||
if (mInSpan) {
|
||||
sb.Append("</span>");
|
||||
mInSpan = false;
|
||||
}
|
||||
if (newColor != mDefaultColor) {
|
||||
sb.Append("<span style=\"");
|
||||
OutputHtmlColor(newColor, sb);
|
||||
sb.Append("\">");
|
||||
mInSpan = true;
|
||||
}
|
||||
mCurrentColor = newColor;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Outputs a color in the form "#rrggbb".
|
||||
/// </summary>
|
||||
private void OutputHtmlColor(Color color, StringBuilder sb) {
|
||||
sb.Append("color:#");
|
||||
sb.Append(color.R.ToString("x2"));
|
||||
sb.Append(color.G.ToString("x2"));
|
||||
sb.Append(color.B.ToString("x2"));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Reads 16 bits of little-endian data from the buffer.
|
||||
/// </summary>
|
||||
/// <param name="data">Data buffer.</param>
|
||||
/// <param name="offset">Initial offset. Will be increased by two on a successful
|
||||
/// read.</param>
|
||||
/// <returns>A value between 0 and 65535, or -1 if we ran off the end.</returns>
|
||||
private static int Read16(byte[] data, ref int offset) {
|
||||
if (offset + 2 > data.Length) {
|
||||
Debug.WriteLine("ERROR: overran Applesoft");
|
||||
return -1;
|
||||
}
|
||||
offset += 2;
|
||||
return data[offset-2] | (data[offset - 1] << 8);
|
||||
}
|
||||
}
|
||||
}
|
@ -174,6 +174,7 @@ limitations under the License.
|
||||
</RoutedUICommand.InputGestures>
|
||||
</RoutedUICommand>
|
||||
|
||||
<RoutedUICommand x:Key="Debug_ApplesoftToHtmlCmd" Text="Applesoft to HTML..."/>
|
||||
<RoutedUICommand x:Key="Debug_ExtensionScriptInfoCmd" Text="Extension Script Info..."/>
|
||||
<RoutedUICommand x:Key="Debug_ShowAnalysisTimersCmd" Text="Show Analysis Timers"/>
|
||||
<RoutedUICommand x:Key="Debug_ShowAnalyzerOutputCmd" Text="Show Analyzer Output"/>
|
||||
@ -290,6 +291,8 @@ limitations under the License.
|
||||
|
||||
<CommandBinding Command="Refresh"
|
||||
CanExecute="IsProjectOpen" Executed="Debug_RefreshCmd_Executed"/>
|
||||
<CommandBinding Command="{StaticResource Debug_ApplesoftToHtmlCmd}"
|
||||
Executed="Debug_ApplesoftToHtmlCmd_Executed"/>
|
||||
<CommandBinding Command="{StaticResource Debug_ExtensionScriptInfoCmd}"
|
||||
CanExecute="IsProjectOpen" Executed="Debug_ExtensionScriptInfoCmd_Executed"/>
|
||||
<CommandBinding Command="{StaticResource Debug_ShowAnalysisTimersCmd}"
|
||||
@ -385,12 +388,14 @@ limitations under the License.
|
||||
<MenuItem Header="_DEBUG" Name="DebugMenu" SubmenuOpened="DebugMenu_SubmenuOpened"
|
||||
Visibility="{Binding ShowDebugMenu, Converter={StaticResource BoolToVis}}">
|
||||
<MenuItem Command="Refresh" Header="Re-analyze"/>
|
||||
<MenuItem Name ="debugUndoRedoHistoryMenuItem"
|
||||
Command="{StaticResource Debug_ShowUndoRedoHistoryCmd}" IsCheckable="True"/>
|
||||
<MenuItem Command="{StaticResource Debug_SourceGenerationTestsCmd}"/>
|
||||
<Separator/>
|
||||
<MenuItem Name="debugAnalyzerOutputMenuItem"
|
||||
Command="{StaticResource Debug_ShowAnalyzerOutputCmd}" IsCheckable="True"/>
|
||||
<MenuItem Name="debugAnalysisTimersMenuItem"
|
||||
Command="{StaticResource Debug_ShowAnalysisTimersCmd}" IsCheckable="True"/>
|
||||
<MenuItem Name ="debugUndoRedoHistoryMenuItem"
|
||||
Command="{StaticResource Debug_ShowUndoRedoHistoryCmd}" IsCheckable="True"/>
|
||||
<MenuItem Command="{StaticResource Debug_ExtensionScriptInfoCmd}"/>
|
||||
<Separator/>
|
||||
<MenuItem Name="debugCommentRulersMenuItem"
|
||||
@ -398,7 +403,7 @@ limitations under the License.
|
||||
<MenuItem Name="debugKeepAliveHackMenuItem"
|
||||
Command="{StaticResource Debug_ToggleKeepAliveHackCmd}" IsCheckable="True"/>
|
||||
<Separator/>
|
||||
<MenuItem Command="{StaticResource Debug_SourceGenerationTestsCmd}"/>
|
||||
<MenuItem Command="{StaticResource Debug_ApplesoftToHtmlCmd}"/>
|
||||
</MenuItem>
|
||||
</Menu>
|
||||
|
||||
|
@ -1277,7 +1277,12 @@ namespace SourceGen.WpfGui {
|
||||
mMainCtrl.UndoChanges();
|
||||
}
|
||||
|
||||
private void Debug_ExtensionScriptInfoCmd_Executed(object sender, ExecutedRoutedEventArgs e) {
|
||||
private void Debug_ApplesoftToHtmlCmd_Executed(object sender, ExecutedRoutedEventArgs e) {
|
||||
mMainCtrl.Debug_ApplesoftToHtml();
|
||||
}
|
||||
|
||||
private void Debug_ExtensionScriptInfoCmd_Executed(object sender,
|
||||
ExecutedRoutedEventArgs e) {
|
||||
mMainCtrl.Debug_ExtensionScriptInfo();
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user