diff --git a/HeadlessEmulator/HeadlessEmulator.csproj b/HeadlessEmulator/HeadlessEmulator.csproj
new file mode 100644
index 0000000..7018ce3
--- /dev/null
+++ b/HeadlessEmulator/HeadlessEmulator.csproj
@@ -0,0 +1,39 @@
+
+
+
+ Exe
+ net7.0
+ enable
+ enable
+
+
+
+
+
+
+
+
+
+
+
+
+ True
+ True
+ MainResources.resx
+
+
+
+
+
+ ResXFileCodeGenerator
+ MainResources.Designer.cs
+
+
+
+
+
+ PreserveNewest
+
+
+
+
diff --git a/HeadlessEmulator/MainResources.Designer.cs b/HeadlessEmulator/MainResources.Designer.cs
new file mode 100644
index 0000000..0cd7d53
--- /dev/null
+++ b/HeadlessEmulator/MainResources.Designer.cs
@@ -0,0 +1,113 @@
+//------------------------------------------------------------------------------
+//
+// Este código fue generado por una herramienta.
+// Versión de runtime:4.0.30319.42000
+//
+// Los cambios en este archivo podrían causar un comportamiento incorrecto y se perderán si
+// se vuelve a generar el código.
+//
+//------------------------------------------------------------------------------
+
+namespace HeadlessEmulator {
+ using System;
+
+
+ ///
+ /// Clase de recurso fuertemente tipado, para buscar cadenas traducidas, etc.
+ ///
+ // StronglyTypedResourceBuilder generó automáticamente esta clase
+ // a través de una herramienta como ResGen o Visual Studio.
+ // Para agregar o quitar un miembro, edite el archivo .ResX y, a continuación, vuelva a ejecutar ResGen
+ // con la opción /str o recompile su proyecto de VS.
+ [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "17.0.0.0")]
+ [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
+ [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
+ internal class MainResources {
+
+ private static global::System.Resources.ResourceManager resourceMan;
+
+ private static global::System.Globalization.CultureInfo resourceCulture;
+
+ [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
+ internal MainResources() {
+ }
+
+ ///
+ /// Devuelve la instancia de ResourceManager almacenada en caché utilizada por esta clase.
+ ///
+ [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
+ internal static global::System.Resources.ResourceManager ResourceManager {
+ get {
+ if (object.ReferenceEquals(resourceMan, null)) {
+ global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("HeadlessEmulator.MainResources", typeof(MainResources).Assembly);
+ resourceMan = temp;
+ }
+ return resourceMan;
+ }
+ }
+
+ ///
+ /// Reemplaza la propiedad CurrentUICulture del subproceso actual para todas las
+ /// búsquedas de recursos mediante esta clase de recurso fuertemente tipado.
+ ///
+ [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
+ internal static global::System.Globalization.CultureInfo Culture {
+ get {
+ return resourceCulture;
+ }
+ set {
+ resourceCulture = value;
+ }
+ }
+
+ ///
+ /// Busca un recurso adaptado de tipo System.Byte[].
+ ///
+ internal static byte[] _128k_0_rom {
+ get {
+ object obj = ResourceManager.GetObject("_128k_0_rom", resourceCulture);
+ return ((byte[])(obj));
+ }
+ }
+
+ ///
+ /// Busca un recurso adaptado de tipo System.Byte[].
+ ///
+ internal static byte[] _128k_1_rom {
+ get {
+ object obj = ResourceManager.GetObject("_128k_1_rom", resourceCulture);
+ return ((byte[])(obj));
+ }
+ }
+
+ ///
+ /// Busca un recurso adaptado de tipo System.Byte[].
+ ///
+ internal static byte[] _48k_rom {
+ get {
+ object obj = ResourceManager.GetObject("_48k_rom", resourceCulture);
+ return ((byte[])(obj));
+ }
+ }
+
+ ///
+ /// Busca un recurso adaptado de tipo System.Byte[].
+ ///
+ internal static byte[] Plus2_0_rom {
+ get {
+ object obj = ResourceManager.GetObject("Plus2_0_rom", resourceCulture);
+ return ((byte[])(obj));
+ }
+ }
+
+ ///
+ /// Busca un recurso adaptado de tipo System.Byte[].
+ ///
+ internal static byte[] Plus2_1_rom {
+ get {
+ object obj = ResourceManager.GetObject("Plus2_1_rom", resourceCulture);
+ return ((byte[])(obj));
+ }
+ }
+ }
+}
diff --git a/HeadlessEmulator/MainResources.resx b/HeadlessEmulator/MainResources.resx
new file mode 100644
index 0000000..c78e3ac
--- /dev/null
+++ b/HeadlessEmulator/MainResources.resx
@@ -0,0 +1,136 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ text/microsoft-resx
+
+
+ 2.0
+
+
+ System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
+
+
+ System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
+
+
+
+ Resources\Plus2_0_rom.bin;System.Byte[], mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
+
+
+ Resources\Plus2_1_rom.bin;System.Byte[], mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
+
+
+ Resources\128k_0_rom.bin;System.Byte[], mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
+
+
+ Resources\128k_1_rom.bin;System.Byte[], mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
+
+
+ Resources\48k_rom.bin;System.Byte[], mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
+
+
\ No newline at end of file
diff --git a/HeadlessEmulator/Program.cs b/HeadlessEmulator/Program.cs
new file mode 100644
index 0000000..38c73ed
--- /dev/null
+++ b/HeadlessEmulator/Program.cs
@@ -0,0 +1,187 @@
+using CommandLine;
+using CoreSpectrum.Hardware;
+using CoreSpectrum.SupportClasses;
+using HeadlessEmulator;
+using System.Text;
+
+const ushort reset128k = 0x2656;
+const ushort resetPlus2 = 0x2675;
+
+int bpCounter = 0;
+
+Options? mainOptions = null;
+
+CommandLine.Parser.Default.ParseArguments(args)
+ .WithParsed(RunOptions);
+
+if (mainOptions == null)
+ return;
+
+SpectrumBase emulator;
+
+switch (mainOptions.Model)
+{
+ case SpectrumModel.M128k:
+ Load128k();
+ break;
+ case SpectrumModel.MPlus2:
+ LoadPlus2();
+ break;
+ default:
+ Load48k();
+ break;
+}
+
+emulator.BreakpointHit += Emulator_BreakpointHit;
+emulator.ProgramReady += Emulator_ProgramReady;
+
+emulator.Start(true);
+emulator.Pause();
+emulator.Turbo(true, false);
+
+ushort org = mainOptions.Org == null ? mainOptions.Address : mainOptions.Org.Value;
+
+Console.WriteLine($"Injecting binary image {mainOptions.Binary} at address {mainOptions.Address} org {org}");
+
+byte[] image = File.ReadAllBytes(mainOptions.Binary);
+
+ProgramImage program = new ProgramImage { InitialBank = 0, Chunks = new ImageChunk[] { new ImageChunk { Address = mainOptions.Address, Bank = 0, Data = image } }, Org = org };
+
+emulator.InjectProgram(program);
+emulator.Start();
+
+Console.WriteLine("Running emulator, press a key to abort...");
+Console.ReadKey();
+
+Console.WriteLine("Terminating...");
+
+emulator.Stop();
+emulator.Dispose();
+return;
+
+void Emulator_ProgramReady(object? sender, EventArgs e)
+{
+ emulator.Pause();
+
+ Console.WriteLine("Program ready, creating breakpoints...");
+ /*
+ emulator.AddBreakpoint(new CoreSpectrum.Debug.Breakpoint { Id = "RST00h", Address = 0x00, Temporary = false });
+ emulator.AddBreakpoint(new CoreSpectrum.Debug.Breakpoint { Id = "RST08h", Address = 0x08, Temporary = false });
+ emulator.AddBreakpoint(new CoreSpectrum.Debug.Breakpoint { Id = "RST10h", Address = 0x10, Temporary = false });
+ emulator.AddBreakpoint(new CoreSpectrum.Debug.Breakpoint { Id = "RST18h", Address = 0x18, Temporary = false });
+ emulator.AddBreakpoint(new CoreSpectrum.Debug.Breakpoint { Id = "RST20h", Address = 0x20, Temporary = false });
+ emulator.AddBreakpoint(new CoreSpectrum.Debug.Breakpoint { Id = "RST28h", Address = 0x28, Temporary = false });
+ emulator.AddBreakpoint(new CoreSpectrum.Debug.Breakpoint { Id = "RST30h", Address = 0x30, Temporary = false });
+ emulator.AddBreakpoint(new CoreSpectrum.Debug.Breakpoint { Id = "RST38h", Address = 0x38, Temporary = false });*/
+
+ foreach (var addr in mainOptions.Breakpoints)
+ {
+ Console.WriteLine($"Creating breakpoint at 0x{addr.ToString("X4")}...");
+ emulator.AddBreakpoint(new CoreSpectrum.Debug.Breakpoint { Id = $"0x{addr.ToString("X4")}", Address = addr, Temporary = false });
+ }
+
+ Console.WriteLine("Running program...");
+ emulator.Resume();
+}
+
+void Emulator_BreakpointHit(object? sender, BreakPointEventArgs e)
+{
+ Console.WriteLine($"Breakpoint hit: {e.Breakpoint.Id}.");
+
+ Console.WriteLine("Dumping memory and registers...");
+
+ byte[] mem = emulator.Memory.GetContents(0, 65536);
+ File.WriteAllBytes($"{e.Breakpoint.Id}_{bpCounter++}.mem", mem);
+
+ StringBuilder sb = new StringBuilder();
+
+ sb.AppendLine($"PC: 0x{((ushort)emulator.Z80.Registers.PC).ToString("X4")}");
+ sb.AppendLine($"SP: 0x{((ushort)emulator.Z80.Registers.SP).ToString("X4")}");
+ sb.AppendLine($"IX: 0x{((ushort)emulator.Z80.Registers.IX).ToString("X4")}");
+ sb.AppendLine($"IY: 0x{((ushort)emulator.Z80.Registers.IY).ToString("X4")}");
+
+ sb.AppendLine($"AF: 0x{((ushort)emulator.Z80.Registers.AF).ToString("X4")}");
+ sb.AppendLine($"BC: 0x{((ushort)emulator.Z80.Registers.BC).ToString("X4")}");
+ sb.AppendLine($"DE: 0x{((ushort)emulator.Z80.Registers.DE).ToString("X4")}");
+ sb.AppendLine($"HL: 0x{((ushort)emulator.Z80.Registers.HL).ToString("X4")}");
+
+ sb.AppendLine($"AF': 0x{((ushort)emulator.Z80.Registers.Alternate.AF).ToString("X4")}");
+ sb.AppendLine($"BC': 0x{((ushort)emulator.Z80.Registers.Alternate.BC).ToString("X4")}");
+ sb.AppendLine($"DE': 0x{((ushort)emulator.Z80.Registers.Alternate.DE).ToString("X4")}");
+ sb.AppendLine($"HL': 0x{((ushort)emulator.Z80.Registers.Alternate.HL).ToString("X4")}");
+
+ File.WriteAllText($"{e.Breakpoint.Id}_{bpCounter++}.reg", sb.ToString());
+
+ Console.WriteLine("Resuming emulation...");
+
+ emulator.Resume();
+}
+
+void ProcessRST08()
+{
+ ushort value = emulator.Memory.GetUshort(0x5C5D);
+ Console.WriteLine($"CH_ADD: {value}");
+
+ ushort stack = (ushort)emulator.Z80.Registers.SP;
+ value = emulator.Memory.GetUshort(stack);
+
+ byte errCode = emulator.Memory.GetByte(value);
+ Console.WriteLine($"ERR_NR: {errCode}");
+
+ value = emulator.Memory.GetUshort(0x5C3C);
+ Console.WriteLine($"ERR_SP: {value}");
+}
+
+void Load48k()
+{
+ Console.WriteLine("Creating 48k emulator...");
+ byte[] rom0 = MainResources._48k_rom;
+
+ var emu = new Spectrum48k(new[] { rom0 });
+ emulator = emu;
+}
+
+void LoadPlus2()
+{
+ Console.WriteLine("Creating +2 emulator...");
+ byte[] rom0 = MainResources.Plus2_0_rom;
+ byte[] rom1 = MainResources.Plus2_1_rom;
+
+ var emu = new Spectrum128k(new[] { rom0, rom1 }, resetPlus2);
+ emulator = emu;
+}
+
+void Load128k()
+{
+ Console.WriteLine("Creating 128k emulator...");
+ byte[] rom0 = MainResources._128k_0_rom;
+ byte[] rom1 = MainResources._128k_1_rom;
+
+ var emu = new Spectrum128k(new[] { rom0, rom1 }, reset128k);
+ emulator = emu;
+}
+
+void RunOptions(Options opts)
+{
+ mainOptions = opts;
+}
+public class Options
+{
+ [Option('b', "binary", Required = true, HelpText = "Binary file to load")]
+ public string Binary { get; set; }
+ [Option('a', "address", Required = true, HelpText = "Address of the binary")]
+ public ushort Address { get; set; }
+ [Option('o', "org", Required = false, Default = null, HelpText = "Startup address, if omited the binary address will be used")]
+ public ushort? Org { get; set; }
+ [Option('m', "model", Required = false, Default = SpectrumModel.M48k, HelpText = "Spectrum model. Possible options: M48k, M128k and MPlus2")]
+ public SpectrumModel Model { get; set; }
+ [Option('p', "breakpoints", Required = true, HelpText = "List of breakpoint addresses")]
+ public IEnumerable Breakpoints { get; set; }
+}
+
+public enum SpectrumModel
+{
+ M48k,
+ M128k,
+ MPlus2
+}
\ No newline at end of file
diff --git a/HeadlessEmulator/Properties/launchSettings.json b/HeadlessEmulator/Properties/launchSettings.json
new file mode 100644
index 0000000..f1cf089
--- /dev/null
+++ b/HeadlessEmulator/Properties/launchSettings.json
@@ -0,0 +1,8 @@
+{
+ "profiles": {
+ "HeadlessEmulator": {
+ "commandName": "Project",
+ "commandLineArgs": "-b overflow_i8_add.bin -a 32768 -m M128k -p 8 10"
+ }
+ }
+}
\ No newline at end of file
diff --git a/HeadlessEmulator/Resources/128k_0_rom.bin b/HeadlessEmulator/Resources/128k_0_rom.bin
new file mode 100644
index 0000000..c4a04e8
Binary files /dev/null and b/HeadlessEmulator/Resources/128k_0_rom.bin differ
diff --git a/HeadlessEmulator/Resources/128k_1_rom.bin b/HeadlessEmulator/Resources/128k_1_rom.bin
new file mode 100644
index 0000000..64c3e73
Binary files /dev/null and b/HeadlessEmulator/Resources/128k_1_rom.bin differ
diff --git a/HeadlessEmulator/Resources/48k_rom.bin b/HeadlessEmulator/Resources/48k_rom.bin
new file mode 100644
index 0000000..4d6895e
Binary files /dev/null and b/HeadlessEmulator/Resources/48k_rom.bin differ
diff --git a/HeadlessEmulator/Resources/Plus2_0_rom.bin b/HeadlessEmulator/Resources/Plus2_0_rom.bin
new file mode 100644
index 0000000..feff233
Binary files /dev/null and b/HeadlessEmulator/Resources/Plus2_0_rom.bin differ
diff --git a/HeadlessEmulator/Resources/Plus2_1_rom.bin b/HeadlessEmulator/Resources/Plus2_1_rom.bin
new file mode 100644
index 0000000..2c20647
Binary files /dev/null and b/HeadlessEmulator/Resources/Plus2_1_rom.bin differ
diff --git a/HeadlessEmulator/overflow_i8_add.bin b/HeadlessEmulator/overflow_i8_add.bin
new file mode 100644
index 0000000..211ec4a
Binary files /dev/null and b/HeadlessEmulator/overflow_i8_add.bin differ
diff --git a/ZXBStudio/IntegratedDocumentTypes/Resources/ZXRamDisk/ZXRamDiskBuilder.cs b/ZXBStudio/IntegratedDocumentTypes/Resources/ZXRamDisk/ZXRamDiskBuilder.cs
index 1cf900b..23ecca6 100644
--- a/ZXBStudio/IntegratedDocumentTypes/Resources/ZXRamDisk/ZXRamDiskBuilder.cs
+++ b/ZXBStudio/IntegratedDocumentTypes/Resources/ZXRamDisk/ZXRamDiskBuilder.cs
@@ -120,39 +120,6 @@ OR C
END ASM
-'Call this function the first in your program in order to
-'load the banks data
-SUB LoadRamDisk()
-
- DIM buc AS UBYTE
-
-
- FOR buc = 0 TO {banks_count}
-
- rdkTransferBank = banks(buc)
-
- ASM
- LD A, (_rdkTransferBank)
- RDK_PROC_PROLOGUE()
- CALL RDK_SWITCH_BANK
- EI
-
- END ASM
-
- LOAD """" CODE $C000
-
- ASM
-
- DI
- CALL RDK_SWITCH_BANK
- RDK_PROC_EPILOGUE()
-
- END ASM
-
- NEXT buc
-
-END SUB
-
{basic_functions_template}
";
@@ -495,6 +462,7 @@ private bool BuildDiskAndCode(string buildPath, ZXBuildType BuildType, TextWrite
StringBuilder sb = new StringBuilder();
+ /*
sb.Append($"#define Load{diskName}()");
foreach (var bank in diskFile.Banks)
@@ -504,6 +472,7 @@ private bool BuildDiskAndCode(string buildPath, ZXBuildType BuildType, TextWrite
}
sb.Append("\r\n");
+ */
List usedBanks = new List();
diff --git a/ZXBStudio/IntegratedDocumentTypes/TapeDocuments/ZXTapeBuilder/ZXTapeBuilderBuilder.cs b/ZXBStudio/IntegratedDocumentTypes/TapeDocuments/ZXTapeBuilder/ZXTapeBuilderBuilder.cs
index a419789..76bf1a9 100644
--- a/ZXBStudio/IntegratedDocumentTypes/TapeDocuments/ZXTapeBuilder/ZXTapeBuilderBuilder.cs
+++ b/ZXBStudio/IntegratedDocumentTypes/TapeDocuments/ZXTapeBuilder/ZXTapeBuilderBuilder.cs
@@ -102,12 +102,47 @@ public bool Build(string BuildPath, ZXBuildStage Stage, ZXBuildType BuildType, Z
line.AddTokens(I.LOAD, "\"\"", I.CODE, CompiledProgram.Org);
+ if (buildFile.IncludeRAMDisk && buildFile.RAMDiskOrder == ZXRAMDiskOrder.Before)
+ {
+ foreach (var bank in CompiledProgram.Banks)
+ {
+ line.AddTokens(I.POKE, 23388, ",", 16 + (int)bank.Bank);
+ line.AddTokens(I.OUT, 32765, ",", 16 + (int)bank.Bank);
+ line.AddTokens(I.LOAD, "\"\"", I.CODE, 0xC000);
+ }
+
+ line.AddTokens(I.POKE, 23388, ",", 16);
+ line.AddTokens(I.OUT, 32765, ",", 16);
+ }
+
if (buildFile.DataBlocks != null && buildFile.DataBlocks.Length > 0 && buildFile.DataBlocks.Any(b => b.BasicLoad))
{
foreach(var block in buildFile.DataBlocks.Where(b => b.BasicLoad))
line.AddTokens(I.LOAD, "\"\"", I.CODE, block.BlockAddress);
}
+ if (buildFile.IncludeRAMDisk && buildFile.RAMDiskOrder == ZXRAMDiskOrder.After)
+ {
+ foreach (var bank in CompiledProgram.Banks)
+ {
+ line.AddTokens(I.POKE, 23388, ",", 16 + (int)bank.Bank);
+ line.AddTokens(I.OUT, 32765, ",", 16 + (int)bank.Bank);
+ line.AddTokens(I.LOAD, "\"\"", I.CODE, 0xC000);
+ }
+
+ line.AddTokens(I.POKE, 23388, ",", 16);
+ line.AddTokens(I.OUT, 32765, ",", 16);
+ }
+
+
+
+
+
+
+
+
+
+
if (buildFile.HideHeaders)
line.AddTokens(I.POKE, 23739, ",", 244);
diff --git a/ZXBasicStudio.sln b/ZXBasicStudio.sln
index 83bcb0b..4c8ad37 100644
--- a/ZXBasicStudio.sln
+++ b/ZXBasicStudio.sln
@@ -25,6 +25,8 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Elementos de la solución",
README.md = README.md
EndProjectSection
EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "HeadlessEmulator", "HeadlessEmulator\HeadlessEmulator.csproj", "{D99C36AE-072A-4885-9585-CFD78FC6763E}"
+EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
@@ -97,6 +99,14 @@ Global
{2493E207-2E56-48AB-8064-0E7A805F7A62}.Release|Any CPU.Build.0 = Release|Any CPU
{2493E207-2E56-48AB-8064-0E7A805F7A62}.Release|x64.ActiveCfg = Release|Any CPU
{2493E207-2E56-48AB-8064-0E7A805F7A62}.Release|x64.Build.0 = Release|Any CPU
+ {D99C36AE-072A-4885-9585-CFD78FC6763E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {D99C36AE-072A-4885-9585-CFD78FC6763E}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {D99C36AE-072A-4885-9585-CFD78FC6763E}.Debug|x64.ActiveCfg = Debug|Any CPU
+ {D99C36AE-072A-4885-9585-CFD78FC6763E}.Debug|x64.Build.0 = Debug|Any CPU
+ {D99C36AE-072A-4885-9585-CFD78FC6763E}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {D99C36AE-072A-4885-9585-CFD78FC6763E}.Release|Any CPU.Build.0 = Release|Any CPU
+ {D99C36AE-072A-4885-9585-CFD78FC6763E}.Release|x64.ActiveCfg = Release|Any CPU
+ {D99C36AE-072A-4885-9585-CFD78FC6763E}.Release|x64.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE