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