Skip to content

Commit

Permalink
C64 Basic token parser to be able to extract Basic source code. Integ…
Browse files Browse the repository at this point in the history
…rated in the UIs with a Copy button.
  • Loading branch information
highbyte committed Sep 7, 2024
1 parent 1cd8b08 commit fbc99e3
Show file tree
Hide file tree
Showing 8 changed files with 454 additions and 4 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@
using Microsoft.Extensions.Logging;
using Highbyte.DotNet6502.Utils;
using TextCopy;
using Highbyte.DotNet6502.Systems.Commodore64.Utils;
using Microsoft.Extensions.Logging.Abstractions;

namespace Highbyte.DotNet6502.App.SadConsole.ConfigUI;
public class C64MenuConsole : ControlsConsole
Expand Down Expand Up @@ -60,17 +62,24 @@ private void DrawUIItems()
c64SaveBasicButton.Click += C64SaveBasicButton_Click;
Controls.Add(c64SaveBasicButton);

// Copy Basic Source Code
var c64CopyBasicSourceCodeButton = new Button("Copy")
{
Name = "c64CopyBasicSourceCodeButton",
Position = (1, c64SaveBasicButton.Bounds.MaxExtentY + 2),
};
c64CopyBasicSourceCodeButton.Click += C64CopyBasicSourceCodeButton_Click;
Controls.Add(c64CopyBasicSourceCodeButton);

// Save Basic
// Paste
var c64PasteTextButton = new Button("Paste")
{
Name = "c64PasteTextButton",
Position = (1, c64SaveBasicButton.Bounds.MaxExtentY + 2),
Position = (c64CopyBasicSourceCodeButton.Bounds.MaxExtentX + 9, c64CopyBasicSourceCodeButton.Position.Y),
};
c64PasteTextButton.Click += C64PasteTextButton_Click;
Controls.Add(c64PasteTextButton);


// Config
var c64ConfigButton = new Button("C64 Config")
{
Expand Down Expand Up @@ -213,6 +222,13 @@ private void C64ConfigButton_Click(object sender, EventArgs e)
window.Show(true);
}

private void C64CopyBasicSourceCodeButton_Click(object sender, EventArgs e)
{
var c64 = (C64)_sadConsoleHostApp.CurrentRunningSystem!;
var basicSourceCode = c64.BasicTokenParser.GetBasicTextLines();
ClipboardService.SetText(basicSourceCode.ToLower());
}

private void C64PasteTextButton_Click(object sender, EventArgs e)
{
var c64 = (C64)_sadConsoleHostApp.CurrentRunningSystem!;
Expand All @@ -239,6 +255,9 @@ private void SetControlStates()
var c64ConfigButton = Controls["c64ConfigButton"];
c64ConfigButton.IsEnabled = _sadConsoleHostApp.EmulatorState == Systems.EmulatorState.Uninitialized;

var c64CopyBasicSourceCodeButton = Controls["c64CopyBasicSourceCodeButton"];
c64CopyBasicSourceCodeButton.IsEnabled = _sadConsoleHostApp.EmulatorState == Systems.EmulatorState.Running;

var c64PasteTextButton = Controls["c64PasteTextButton"];
c64PasteTextButton.IsEnabled = _sadConsoleHostApp.EmulatorState == Systems.EmulatorState.Running;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -444,7 +444,18 @@ private void DrawC64Config()
}
ImGui.EndDisabled();

// C64 copy basic source code
ImGui.BeginDisabled(disabled: EmulatorState == EmulatorState.Uninitialized);
if (ImGui.Button("Copy"))
{
var c64 = (C64)_silkNetHostApp.CurrentRunningSystem!;
var sourceCode = c64.BasicTokenParser.GetBasicTextLines();
ClipboardService.SetText(sourceCode.ToLower());
}
ImGui.EndDisabled();

// C64 paste text
ImGui.SameLine();
ImGui.BeginDisabled(disabled: EmulatorState == EmulatorState.Uninitialized);
if (ImGui.Button("Paste"))
{
Expand All @@ -453,7 +464,6 @@ private void DrawC64Config()
if (string.IsNullOrEmpty(text))
return;
c64.TextPaste.Paste(text);

}
ImGui.EndDisabled();

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,7 @@
</div>

<p>Misc.</p>
<button @onclick="CopyBasicSourceCode" disabled=@OnCopySourceCodeDisabled>Copy</button>
<button @onclick="PasteText" disabled=@OnPasteTextDisabled>Paste</button>

</div>
Expand Down Expand Up @@ -214,8 +215,11 @@

protected bool OnBasicFilePickerDisabled => Parent.CurrentEmulatorState == EmulatorState.Uninitialized;

protected bool OnCopySourceCodeDisabled=> Parent.CurrentEmulatorState == EmulatorState.Uninitialized;

protected bool OnPasteTextDisabled => Parent.CurrentEmulatorState == EmulatorState.Uninitialized;


/// <summary>
/// Open Load binary file dialog
/// </summary>
Expand Down Expand Up @@ -469,6 +473,13 @@
await Parent.OnStart(new());
}

private async Task CopyBasicSourceCode()
{
var c64 = (C64)Parent.WasmHost.CurrentRunningSystem!;
var sourceCode = c64.BasicTokenParser.GetBasicTextLines();
await Clipboard.SetTextAsync(sourceCode.ToLower());
await Parent.FocusEmulator();
}

private async Task PasteText()
{
Expand Down
2 changes: 2 additions & 0 deletions src/libraries/Highbyte.DotNet6502.Systems.Commodore64/C64.cs
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,7 @@ public class C64 : ISystem, ISystemMonitor

public bool RememberVic2RegistersPerRasterLine { get; set; } = true;

public C64BasicTokenParser BasicTokenParser { get; private set; }
public C64TextPaste TextPaste { get; private set; }

//public static ROM[] ROMS = new ROM[]
Expand Down Expand Up @@ -224,6 +225,7 @@ public static C64 BuildC64(C64Config c64Config, ILoggerFactory loggerFactory)
var mem = c64.CreateC64Memory(ram, io, romData);
c64.Mem = mem;

c64.BasicTokenParser = new C64BasicTokenParser(c64, loggerFactory);
c64.TextPaste = new C64TextPaste(c64, loggerFactory);

// Configure the current memory configuration on startup
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,116 @@
using System.Text;
using Highbyte.DotNet6502.Utils;
using Microsoft.Extensions.Logging;

namespace Highbyte.DotNet6502.Systems.Commodore64.Utils;

/// <summary>
/// Parser for C64 Basic program in binary (token) format.
///
/// Based on: https://github.com/abbrev/prg-tools/blob/master/src/prg2bas.c
/// </summary>
public class C64BasicTokenParser
{
private readonly ILogger<C64BasicTokenParser> _logger;
private readonly C64 _c64;

public C64BasicTokenParser(C64 c64, ILoggerFactory loggerFactory)
{
_logger = loggerFactory.CreateLogger<C64BasicTokenParser>();
_c64 = c64;
}

public string GetBasicTextLines(bool spaceAfterLineNumber = true, bool addNewLineAfterLastCharacter = true)
{
ushort startAddressValue = C64.BASIC_LOAD_ADDRESS;
var endAddressValue = _c64.GetBasicProgramEndAddress();
var prgBytes = BinarySaver.BuildSaveData(_c64.Mem, startAddressValue, endAddressValue, addFileHeaderWithLoadAddress: true);

return GetBasicTextLines(prgBytes, spaceAfterLineNumber, addNewLineAfterLastCharacter);
}

public string GetBasicTextLines(byte[] basicPrg, bool spaceAfterLineNumber = true, bool addNewLineAfterLastCharacter = true)
{
if (basicPrg.Length < 2)
throw new ArgumentException("Basic program is too short to contain a load address.");

using MemoryStream stream = new MemoryStream(basicPrg);

// First to bytes is the load address
byte[] prgHeader = new byte[2];
stream.ReadExactly(prgHeader, 0, 2);
var loadAddress = ByteHelpers.ToLittleEndianWord(prgHeader[0], prgHeader[1]);

_logger.LogInformation($"Basic load address: {loadAddress}");
if (loadAddress != C64.BASIC_LOAD_ADDRESS)
{
_logger.LogWarning($"Basic load address is not the expected {C64.BASIC_LOAD_ADDRESS}, probably not a Basic file. Skipping parsing.");
return string.Empty;
}

StringBuilder sb = new StringBuilder();

// Loop each basic line
while (true)
{

// Get next line address
var addr = stream.ReadWord();
if (addr < 0 || addr == 0) // Negative -> end of stream, 0 -> end of basic program
break;

// Get next line number
var lineNumber = stream.ReadWord();
if (lineNumber < 0) // Negative -> end of stream,
break;

// Add new line if not first line
if (sb.Length > 0)
sb.AppendLine();

sb.Append(lineNumber);
if (spaceAfterLineNumber)
sb.Append(' ');


// Loop each token in the basic line
bool quoted = false;
bool endOfProgram = false;
bool nextLine = false;
while (!endOfProgram || !nextLine)
{
var token = stream.ReadByte();
if (token < 0) // Negative -> end of stream
{
endOfProgram = true;
break;
}
if (token == 0) // Next line
{
nextLine = true;
break;
}

if (token == '"')
quoted = !quoted;

if (!quoted && token >= 0x80)
{
sb.Append(C64BasicTokens.Tokens[(byte)token]);
}
else
{
sb.Append((char)token);
}
}

if (endOfProgram)
break;
}

if (addNewLineAfterLastCharacter)
sb.AppendLine();

return sb.ToString();
}
}
Loading

0 comments on commit fbc99e3

Please sign in to comment.