From 0bb8179bf7d9da2bb9e5b7e7899619a90c87142c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Agust=C3=ADn=20Gimenez?= Date: Mon, 15 Jan 2024 22:25:32 +0100 Subject: [PATCH] RamDisk Reowrk --- ZXBStudio/BuildSystem/ZXProgram.cs | 3 +- .../ZXRamDisk/Classes/ZXRamDiskFile.cs | 36 +- .../ZXRamDisk/Controls/ZXRamDiskEditor.axaml | 46 +- .../Controls/ZXRamDiskEditor.axaml.cs | 114 ++-- ZXBStudio/Emulator/Classes/ZXBinaryBank.cs | 25 + .../Emulator/Controls/ZXEmulator.axaml.cs | 6 +- .../Resources/ZXRamDisk/ZXRamDiskBuilder.cs | 533 ++++++++++++++++-- .../Resources/ZXRamDisk/ZXRamDiskFactory.cs | 3 +- ZXBStudio/MainWindow.axaml.cs | 5 +- ZXBStudio/ZXBasicStudio.csproj | 16 +- 10 files changed, 634 insertions(+), 153 deletions(-) create mode 100644 ZXBStudio/Emulator/Classes/ZXBinaryBank.cs diff --git a/ZXBStudio/BuildSystem/ZXProgram.cs b/ZXBStudio/BuildSystem/ZXProgram.cs index af06980..0e9da3e 100644 --- a/ZXBStudio/BuildSystem/ZXProgram.cs +++ b/ZXBStudio/BuildSystem/ZXProgram.cs @@ -5,6 +5,7 @@ using System.Threading.Tasks; using ZXBasicStudio.Classes; using ZXBasicStudio.DocumentEditors.ZXRamDisk.Classes; +using ZXBasicStudio.Emulator.Classes; namespace ZXBasicStudio.BuildSystem { @@ -15,7 +16,7 @@ public class ZXProgram public ZXMemoryMap? ProgramMap { get; set; } public ZXMemoryMap? DisassemblyMap { get; set; } public ZXVariableMap? Variables { get; set; } - public List RamDisks { get; } = new List(); + public List Banks { get; } = new List(); public byte[] Binary { get; set; } public ushort Org { get; set; } public bool Debug { get; set; } diff --git a/ZXBStudio/DocumentEditors/ZXRamDisk/Classes/ZXRamDiskFile.cs b/ZXBStudio/DocumentEditors/ZXRamDisk/Classes/ZXRamDiskFile.cs index d0a8476..008d899 100644 --- a/ZXBStudio/DocumentEditors/ZXRamDisk/Classes/ZXRamDiskFile.cs +++ b/ZXBStudio/DocumentEditors/ZXRamDisk/Classes/ZXRamDiskFile.cs @@ -1,39 +1,43 @@ using System; using System.Collections.Generic; using System.ComponentModel.DataAnnotations; +using System.IO; using System.Linq; using System.Text; using System.Threading.Tasks; +using ZXBasicStudio.Classes; +using ZXBasicStudio.Emulator.Classes; namespace ZXBasicStudio.DocumentEditors.ZXRamDisk.Classes { public class ZXRamDiskFile { - public required string DiskName { get; set; } - public RamDiskBank Bank { get; set; } - public List Files { get; set; } = new List(); + public bool EnableIndirect { get; set; } + public int IndirectBufferSize { get; set; } + public bool RelocateStack { get; set; } + public ZXRamDiskLogicBank[] Banks { get; set; } = new ZXRamDiskLogicBank[] + { + new ZXRamDiskLogicBank{ Bank = ZXMemoryBank.Bank4 }, + new ZXRamDiskLogicBank{ Bank = ZXMemoryBank.Bank6 }, + new ZXRamDiskLogicBank{ Bank = ZXMemoryBank.Bank1 }, + new ZXRamDiskLogicBank{ Bank = ZXMemoryBank.Bank3 }, + new ZXRamDiskLogicBank{ Bank = ZXMemoryBank.Bank7 }, + }; } - public class ZXRamDisk + public class ZXRamDiskLogicBank { - public RamDiskBank Bank { get; set; } - public required byte[] Data { get; set; } + public ZXMemoryBank Bank { get; set; } + public List Files { get; set; } = new List(); } public class ZXRamDiskContainedFile { public required string Name { get; set; } public required string SourcePath { get; set; } - public required byte[] Content { get; set; } - public int Size => Content?.Length ?? 0; + public byte[] Content { get { return File.ReadAllBytes(Path.Combine(ZXProjectManager.Current.ProjectPath, SourcePath)); } } + public int Size => Content.Length; } - public enum RamDiskBank - { - Bank1 = 1, - Bank3 = 3, - Bank4 = 4, - Bank6 = 6, - Bank7 = 7 - } + } diff --git a/ZXBStudio/DocumentEditors/ZXRamDisk/Controls/ZXRamDiskEditor.axaml b/ZXBStudio/DocumentEditors/ZXRamDisk/Controls/ZXRamDiskEditor.axaml index 45583eb..1dd1fd1 100644 --- a/ZXBStudio/DocumentEditors/ZXRamDisk/Controls/ZXRamDiskEditor.axaml +++ b/ZXBStudio/DocumentEditors/ZXRamDisk/Controls/ZXRamDiskEditor.axaml @@ -6,24 +6,38 @@ x:Class="ZXBasicStudio.DocumentEditors.ZXRamDisk.Controls.ZXRamDiskEditor"> - - Disk name: - - Target bank: - - Bank 4 - Bank 6 - Bank 1 - Bank 3 - Bank 7 - + + Enable indirect load: + + + Indirect buffer size: + + + + Relocate stack: + + + - - Add file - File list - + + + Active bank: + + Bank 4 + Bank 6 + Bank 1 + Bank 3 + Bank 7 + + + + + + Add file + File list + File: @@ -34,7 +48,7 @@ - + File name diff --git a/ZXBStudio/DocumentEditors/ZXRamDisk/Controls/ZXRamDiskEditor.axaml.cs b/ZXBStudio/DocumentEditors/ZXRamDisk/Controls/ZXRamDiskEditor.axaml.cs index c2e3ff2..669a158 100644 --- a/ZXBStudio/DocumentEditors/ZXRamDisk/Controls/ZXRamDiskEditor.axaml.cs +++ b/ZXBStudio/DocumentEditors/ZXRamDisk/Controls/ZXRamDiskEditor.axaml.cs @@ -1,3 +1,4 @@ +using Avalonia; using Avalonia.Controls; using Avalonia.Platform.Storage; using AvaloniaEdit.Utils; @@ -8,6 +9,8 @@ using System.Linq; using System.Text.RegularExpressions; using System.Threading.Tasks; +using ZXBasicStudio.Classes; +using ZXBasicStudio.DebuggingTools.Registers.Binding; using ZXBasicStudio.DocumentEditors.ZXRamDisk.Classes; using ZXBasicStudio.DocumentEditors.ZXTapeBuilder.Classes; using ZXBasicStudio.DocumentModel.Classes; @@ -18,12 +21,14 @@ namespace ZXBasicStudio.DocumentEditors.ZXRamDisk.Controls { public partial class ZXRamDiskEditor : ZXDocumentEditorBase { + public static StyledProperty> FilesProperty = AvaloniaProperty.Register>("Files"); + #region Private variables string _docName; string _docPath; bool _modified; bool _internalUpdate; - ObservableCollection _files = new ObservableCollection(); + ObservableCollection[] _files = new ObservableCollection[5]; #endregion #region Events @@ -34,7 +39,11 @@ public partial class ZXRamDiskEditor : ZXDocumentEditorBase #endregion #region Binding properties - public ObservableCollection Files => _files; + public ObservableCollection Files + { + get { return GetValue(FilesProperty); } + set { SetValue(FilesProperty, value); } + } #endregion #region ZXDocumentBase properties @@ -44,16 +53,31 @@ public partial class ZXRamDiskEditor : ZXDocumentEditorBase #endregion public ZXRamDiskEditor() { + for (int buc = 0; buc < 5; buc++) + _files[buc] = new ObservableCollection(); + + //Order: 4, 6, 1, 3, 7 + Files = _files[0]; + DataContext = this; InitializeComponent(); } public ZXRamDiskEditor(string DocumentPath) { + for (int buc = 0; buc < 5; buc++) + _files[buc] = new ObservableCollection(); + + //Order: 4, 6, 1, 3, 7 + Files = _files[0]; + DataContext = this; InitializeComponent(); - txtDiskName.TextChanged += DocumentChanged; + ckIndirect.IsCheckedChanged += DocumentChanged; + ckRelocate.IsCheckedChanged += DocumentChanged; + nudIndSize.ValueChanged += DocumentChanged; + cbBank.SelectionChanged += ChangeSelectedBank; btnSelectFile.Click += BtnSelectFile_Click; btnAddFile.Click += BtnAddFile_Click; @@ -67,6 +91,11 @@ public ZXRamDiskEditor(string DocumentPath) throw new Exception("Error opening document"); } + private void ChangeSelectedBank(object? sender, SelectionChangedEventArgs e) + { + int bank = cbBank.SelectedIndex; + Files = _files[bank]; + } private async void BtnAddFile_Click(object? sender, Avalonia.Interactivity.RoutedEventArgs e) { @@ -76,7 +105,9 @@ private async void BtnAddFile_Click(object? sender, Avalonia.Interactivity.Route return; } - if (!File.Exists(txtFilePath.Text)) + string path = Path.Combine(ZXProjectManager.Current.ProjectPath, txtFilePath.Text); + + if (!File.Exists(path)) { await Window.GetTopLevel(this).ShowError("File not found", "Cannot find the specified file."); return; @@ -88,9 +119,9 @@ private async void BtnAddFile_Click(object? sender, Avalonia.Interactivity.Route return; } - ZXRamDiskContainedFile file = new ZXRamDiskContainedFile { Name = txtFileName.Text, SourcePath = txtFilePath.Text, Content = File.ReadAllBytes(txtFilePath.Text) }; + ZXRamDiskContainedFile file = new ZXRamDiskContainedFile { Name = txtFileName.Text, SourcePath = txtFilePath.Text }; - _files.Add(file); + Files.Add(file); DocumentChanged(this, e); } @@ -121,6 +152,8 @@ private async void BtnSelectFile_Click(object? sender, Avalonia.Interactivity.Ro if (string.IsNullOrWhiteSpace(file)) return; + file = Path.GetRelativePath(ZXProjectManager.Current.ProjectPath, file); + txtFilePath.Text = file; string fileName = Path.GetFileNameWithoutExtension(txtFilePath.Text); @@ -132,7 +165,7 @@ private void BtnRemoveFile_Click(object? sender, Avalonia.Interactivity.RoutedEv { if (lstFiles.SelectedItem != null) { - _files.Remove((ZXRamDiskContainedFile)lstFiles.SelectedItem); + Files.Remove((ZXRamDiskContainedFile)lstFiles.SelectedItem); DocumentChanged(this, e); } } @@ -183,30 +216,18 @@ bool UpdateFileName(string NewPath) _internalUpdate = true; - txtDiskName.Text = fileContent.DiskName; + ckIndirect.IsChecked = fileContent.EnableIndirect; + ckRelocate.IsChecked = fileContent.RelocateStack; + nudIndSize.Value = fileContent.IndirectBufferSize; - switch (fileContent.Bank) + for (int buc = 0; buc < 5; buc++) { - case RamDiskBank.Bank1: - cbBank.SelectedIndex = 2; - break; - case RamDiskBank.Bank3: - cbBank.SelectedIndex = 3; - break; - case RamDiskBank.Bank4: - cbBank.SelectedIndex = 0; - break; - case RamDiskBank.Bank6: - cbBank.SelectedIndex = 1; - break; - case RamDiskBank.Bank7: - cbBank.SelectedIndex = 4; - break; + _files[buc].Clear(); + _files[buc].AddRange(fileContent.Banks[buc].Files); } - _files.Clear(); - _files.AddRange(fileContent.Files); - + cbBank.SelectedIndex = 0; + Task.Run(async () => { await Task.Delay(100); @@ -219,47 +240,26 @@ bool UpdateFileName(string NewPath) #region ZXDocumentBase implementation public override bool SaveDocument(TextWriter OutputLog) { - if (string.IsNullOrWhiteSpace(txtDiskName.Text)) - { - OutputLog.WriteLine("Missing disk name, aborting..."); - return false; - } - - if(_files.Count == 0) + if(_files.Sum(f => f.Count) == 0) { OutputLog.WriteLine("No files on disk, aborting..."); return false; } - if (_files.Sum(f => f.Size) > 16 * 1024) + if (_files.Any(f => f.Sum(ff => ff.Size) > 16 * 1024)) { - OutputLog.WriteLine("Total size exceeds 16Kb, aborting..."); + OutputLog.WriteLine("Bank size exceeds 16Kb, aborting..."); return false; } - RamDiskBank selBank = new RamDiskBank(); + ZXRamDiskFile fileContent = new ZXRamDiskFile(); - switch (cbBank.SelectedIndex) - { - case 2: - selBank = RamDiskBank.Bank1; - break; - case 3: - selBank = RamDiskBank.Bank3; - break; - case 0: - selBank = RamDiskBank.Bank4; - break; - case 1: - selBank = RamDiskBank.Bank6; - break; - case 4: - selBank = RamDiskBank.Bank7; - break; - } + for (int buc = 0; buc < 5; buc++) + fileContent.Banks[buc].Files.AddRange(_files[buc]); - //TODO: Make paths relative to project - ZXRamDiskFile fileContent = new ZXRamDiskFile { DiskName = txtDiskName.Text, Bank = selBank, Files = _files.ToList() }; + fileContent.IndirectBufferSize = (int)(nudIndSize.Value ?? 0); + fileContent.EnableIndirect = ckIndirect.IsChecked ?? false; + fileContent.RelocateStack = ckRelocate.IsChecked ?? false; string content = JsonConvert.SerializeObject(fileContent, Formatting.Indented); try diff --git a/ZXBStudio/Emulator/Classes/ZXBinaryBank.cs b/ZXBStudio/Emulator/Classes/ZXBinaryBank.cs new file mode 100644 index 0000000..a7785f5 --- /dev/null +++ b/ZXBStudio/Emulator/Classes/ZXBinaryBank.cs @@ -0,0 +1,25 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace ZXBasicStudio.Emulator.Classes +{ + public class ZXBinaryBank + { + public ZXMemoryBank Bank { get; set; } + public required byte[] Data { get; set; } + } + public enum ZXMemoryBank + { + Bank1 = 1, + Bank2 = 2, + Bank3 = 3, + Bank4 = 4, + Bank5 = 5, + Bank6 = 6, + Bank7 = 7 + } + +} diff --git a/ZXBStudio/Emulator/Controls/ZXEmulator.axaml.cs b/ZXBStudio/Emulator/Controls/ZXEmulator.axaml.cs index 7bf3b49..e3ad347 100644 --- a/ZXBStudio/Emulator/Controls/ZXEmulator.axaml.cs +++ b/ZXBStudio/Emulator/Controls/ZXEmulator.axaml.cs @@ -416,15 +416,15 @@ public bool UpdateBreakpoints(IEnumerable? Breakpoints) } } - public bool InjectProgram(ushort Address, byte[] Data, ZXRamDisk[]? RAMDisks, bool ImmediateJump) + public bool InjectProgram(ushort Address, byte[] Data, ZXBinaryBank[]? PagedBanks, bool ImmediateJump) { List Chunks = new List(); Chunks.Add(new ImageChunk { Address = Address, Bank = 0, Data = Data }); - if(RAMDisks != null) - Chunks.AddRange(RAMDisks.Select(d => new ImageChunk { Address = 0xC000, Data = d.Data, Bank = (byte)d.Bank })); + if(PagedBanks != null) + Chunks.AddRange(PagedBanks.Select(d => new ImageChunk { Address = 0xC000, Data = d.Data, Bank = (byte)d.Bank })); ProgramImage img = new ProgramImage { diff --git a/ZXBStudio/IntegratedDocumentTypes/Resources/ZXRamDisk/ZXRamDiskBuilder.cs b/ZXBStudio/IntegratedDocumentTypes/Resources/ZXRamDisk/ZXRamDiskBuilder.cs index 85d27c9..1a2b4e0 100644 --- a/ZXBStudio/IntegratedDocumentTypes/Resources/ZXRamDisk/ZXRamDiskBuilder.cs +++ b/ZXBStudio/IntegratedDocumentTypes/Resources/ZXRamDisk/ZXRamDiskBuilder.cs @@ -11,12 +11,387 @@ using ZXBasicStudio.DocumentModel.Classes; using ZXBasicStudio.DocumentModel.Enums; using ZXBasicStudio.DocumentModel.Interfaces; +using ZXBasicStudio.Emulator.Classes; using ZXBasicStudio.IntegratedDocumentTypes.TapeDocuments.ZXTapeBuilder; namespace ZXBasicStudio.IntegratedDocumentTypes.Resources.ZXRamDisk { public class ZXRamDiskBuilder : IZXDocumentBuilder { + + static readonly string mainTemplate = @"'''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''' +'INCLUDE THIS FILE AT THE FIRST LINE IN THE MAIN PROGRAM TO ' +'ENSURE THE CODE IS NOT STORED IN THE BANKING AREA ' +' ' +'THIS FILE IS AUTOGENERATED, ANY CHANGE WILL BE LOST ON BUILD' +' ' +'THIS LIBRARY ASSUMES BASE 0 FOR ARRAYS ' +'''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''' + +{disk_template} + +DIM dummy AS UINTEGER +{indirect_defs_template}{stack_defs_template} +'transfer source bank +DIM rdkTransferBank AS UBYTE = 0 +'transfer source address +DIM rdkSrcAddress AS UINTEGER = 0 +'transfer destination address +DIM rdkDstAddress AS UINTEGER = 0 +'transfer size +DIM rdkSize AS UINTEGER = 0 +'used banks +DIM banks({banks_count}) AS UBYTE => {{banks}} + +LET dummy = @rdkTransferBank +LET dummy = @rdkSrcAddress +LET dummy = @rdkDstAddress +LET dummy = @rdkSize +LET dummy = @banks + +'Assembler code for memory transfers +ASM + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;Macros ; +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +{prologue_epilogue_template} + +#DEFINE CP16() \ +OR A \ +SBC HL, DE \ +ADD HL, DE + +#define BANKM 23388 + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;Skip assembler code at boot ; +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +JP RAMDSK_PROTECTED_END + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;Transfer data directly from/to a bank ; +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +RDK_DIRECT_TRANSFER: +PROC + +RDK_PROC_PROLOGUE() + +LD A, (_rdkTransferBank) +CALL RDK_SWITCH_BANK ;enable bank + +LD DE, (_rdkDstAddress) ;load transfer parameters +LD HL, (_rdkSrcAddress) +LD BC, (_rdkSize) + +LDIR ;transfer data + +XOR A +CALL RDK_SWITCH_BANK ;restore bank 0 + +RDK_PROC_EPILOGUE() + +RET ;return +ENDP + +{indirect_functions_template} +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;Switch to bank specified in A ; +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +RDK_SWITCH_BANK: +PROC + +LD C, A +LD A, (BANKM) +AND $F8 +OR C +LD BC, $7ffd +OUT (C), A +LD (BANKM), A + +RET +ENDP + +RAMDSK_PROTECTED_END: + +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} +"; + + static readonly string proEpTemplateRelocate = @"#DEFINE RDK_PROC_PROLOGUE() \ +DI ;disable interrupts \ +LD (_rdkOldStack), SP ;preserve old stack pointer \ +LD SP, _rdkTmpStack.__DATA__ + 32 ;place the stack at the temp location + +#DEFINE RDK_PROC_EPILOGUE() \ +LD SP, (_rdkOldStack) ;restore stack \ +EI ;enable interrupts"; + static readonly string proEpTemplateNoRelocate = @"#DEFINE RDK_PROC_PROLOGUE() \ +DI ;disable interrupts + +#DEFINE RDK_PROC_EPILOGUE() \ +EI ;enable interrupts"; + + static readonly string relocateDefsTemplate = @"'stack preservation for ram disk operations +DIM rdkTmpStack(15) AS UINTEGER +DIM rdkOldStack AS UINTEGER = 0 +LET dummy = @rdkTmpStack +LET dummy = @rdkOldStack"; + static readonly string indirectDefsTemplate = @"'Indirect buffer size, the bigger the faster the indirect +'copies will work +#DEFINE INDIRECT_BUFFER_SIZE {indirect_size} +'indirect transfer buffer +DIM rdkTransferBuffer(INDIRECT_BUFFER_SIZE - 1) AS UBYTE +LET dummy = @rdkTransferBuffer +"; + + static readonly string indirectAsmFuncsTemplate = @";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;Transfer data from a bank to bank zero via buffer; +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +RDK_INDIRECT_READ: +PROC +LOCAL indirect_loop +LOCAL copy_block +LOCAL transfer_finished +LOCAL finalblock + +RDK_PROC_PROLOGUE() + +LD HL, (_rdkSize) ;load size to copy, only for first loop + +indirect_loop: + +LD A, (_rdkTransferBank) ;change to source bank +CALL RDK_SWITCH_BANK + +LD DE, INDIRECT_BUFFER_SIZE + +CP16() ;compare it with the indirect buffer size + +JP Z, finalblock ;if it's bigger than the buffer restrict the transfer size +JP C, finalblock +LD BC, INDIRECT_BUFFER_SIZE +JP copy_block + +finalblock: + +LD BC, (_rdkSize) ;else use it + +copy_block: + +PUSH BC ;preserve transfer size + +LD DE, _rdkTransferBuffer.__DATA__ ;copy to temp buffer +LD HL, (_rdkSrcAddress) +LDIR + +LD (_rdkSrcAddress), HL ;preserve next source address + +XOR A ;change to bank 0 +CALL RDK_SWITCH_BANK + +POP BC ;restore transfer size +PUSH BC ;preserve again + +LD DE, (_rdkDstAddress) ;copy from temp buffer +LD HL, _rdkTransferBuffer.__DATA__ +LDIR + +LD (_rdkDstAddress), DE ;preserve next destination address + +POP DE ;restore the size we have transferred and subtract it from the transfer size +LD HL, (_rdkSize) +OR A +SBC HL, DE + +JP Z, transfer_finished ;if there is no more to copy, end procedure + +LD (_rdkSize), HL ;store size left + +JP indirect_loop ;next loop + +transfer_finished: + +RDK_PROC_EPILOGUE() + +RET +ENDP + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;Transfer data from bank zero to a bank via buffer; +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +RDK_INDIRECT_WRITE: +PROC +LOCAL indirect_loop +LOCAL copy_block +LOCAL transfer_finished +LOCAL finalblock + +RDK_PROC_PROLOGUE() + +LD HL, (_rdkSize) ;load size to copy, only for first loop + +indirect_loop: + +LD DE, INDIRECT_BUFFER_SIZE + +CP16() ;compare it with the indirect buffer size + +JP Z, finalblock ;if it's bigger than the buffer restrict the transfer size +JP C, finalblock +LD BC, INDIRECT_BUFFER_SIZE +JP copy_block + +finalblock: + +LD BC, (_rdkSize) ;else use it + +copy_block: + +PUSH BC ;preserve transfer size + +LD DE, _rdkTransferBuffer.__DATA__ ;copy to temp buffer +LD HL, (_rdkSrcAddress) +LDIR + +LD (_rdkSrcAddress), HL ;preserve next source address + +LD A, (_rdkTransferBank) ;change to destination bank +CALL RDK_SWITCH_BANK + +POP BC ;restore transfer size +PUSH BC ;preserve again + +LD DE, (_rdkDstAddress) ;copy from temp buffer +LD HL, _rdkTransferBuffer.__DATA__ +LDIR + +XOR A ;change to bank 0 +CALL RDK_SWITCH_BANK + +LD (_rdkDstAddress), DE ;preserve next destination address + +POP DE ;restore the size we have transferred and subtract it from the transfer size +LD HL, (_rdkSize) +OR A +SBC HL, DE + +JP Z, transfer_finished ;if there is no more to copy, end procedure + +LD (_rdkSize), HL ;store size left + +JP indirect_loop ;next loop + +transfer_finished: + +RDK_PROC_EPILOGUE() + +RET +ENDP +"; + + static readonly string indirectBasicSubsTemplate = @"'Loads data from a RAM disk +SUB LoadRamData(BYVAL DiskNumber AS UBYTE, BYVAL Source as UINTEGER, BYVAL Destination as UINTEGER, BYVAL Length as UINTEGER) + + rdkTransferBank = DiskNumber + rdkSrcAddress = Source + rdkDstAddress = Destination + rdkSize = Length + + IF Destination + Length - 1 >= $c000 then + + 'indirect copy + ASM + CALL RDK_INDIRECT_READ + END ASM + + ELSE + + 'direct copy + ASM + CALL RDK_DIRECT_TRANSFER + END ASM + + END IF + +END SUB + +'Saves data to a RAM disk +SUB SaveRamData (BYVAL DiskNumber AS UBYTE, BYVAL Source as UINTEGER, BYVAL Destination as UINTEGER, BYVAL Length as UINTEGER) + + rdkTransferBank = DiskNumber + rdkSrcAddress = Source + rdkDstAddress = Destination + rdkSize = Length + + IF Source + Length - 1 >= $c000 then + + 'indirect copy + ASM + CALL RDK_INDIRECT_WRITE + END ASM + + ELSE + + 'direct copy + ASM + CALL RDK_DIRECT_TRANSFER + END ASM + + END IF + +END SUB"; + static readonly string noIndirectBasicSubsTemplate = @"'Loads data from a RAM disk +SUB LoadRamData(BYVAL DiskNumber AS UBYTE, BYVAL Source as UINTEGER, BYVAL Destination as UINTEGER, BYVAL Length as UINTEGER) + + rdkTransferBank = DiskNumber + rdkSrcAddress = Source + rdkDstAddress = Destination + rdkSize = Length + + 'indirect copy + ASM + CALL RDK_DIRECT_TRANSFER + END ASM + +END SUB + +#define SaveRamData (DiskNumber,Source,Destination,Length) LoadRamData(DiskNumber,Source,Destination,Length)"; + public bool Build(string BuildPath, ZXBuildStage Stage, ZXBuildType BuildType, ZXProgram? CompiledProgram, TextWriter OutputLog) { switch (Stage) @@ -43,24 +418,38 @@ private bool InjectDisk(string buildPath, ZXProgram compiledProgram, TextWriter if (diskBuilds == null || diskBuilds.Length == 0) return true; - foreach(var diskPath in diskBuilds) + if(diskBuilds.Length > 1) { - string fileName = Path.GetFileName(diskPath); - outputLog.WriteLine($"Injecting RAM disk {fileName}..."); + outputLog.WriteLine("Found more than one RAM disk, aborting..."); + return false; + } - try - { - ZXRamDiskFile diskFile = JsonConvert.DeserializeObject(File.ReadAllText(diskPath)); - byte[] binFile = File.ReadAllBytes(diskPath.Substring(0, diskPath.Length - 4) + ".zxrbin"); + var diskPath = diskBuilds[0]; + + string fileName = Path.GetFileName(diskPath); + outputLog.WriteLine($"Injecting RAM disk {fileName}..."); - compiledProgram.RamDisks.Add(new DocumentEditors.ZXRamDisk.Classes.ZXRamDisk { Bank = diskFile.Bank, Data = binFile }); - } - catch (Exception ex) + try + { + ZXRamDiskFile diskFile = JsonConvert.DeserializeObject(File.ReadAllText(diskPath)); + + foreach (var bank in diskFile.Banks) { - outputLog.WriteLine($"Error injecting RAM disk {diskPath}: \r\n" + ex.ToString()); - return false; + + if (bank.Files.Count == 0) + continue; + + byte[] binFile = File.ReadAllBytes(diskPath.Substring(0, diskPath.Length - 4) + $"_{(int)bank.Bank}.zxrbin"); + + compiledProgram.Banks.Add(new ZXBinaryBank { Bank = bank.Bank, Data = binFile }); } } + catch (Exception ex) + { + outputLog.WriteLine($"Error injecting RAM disk {diskPath}: \r\n" + ex.ToString()); + return false; + } + return true; } @@ -72,63 +461,109 @@ private bool BuildDiskAndCode(string buildPath, ZXBuildType BuildType, TextWrite if (diskBuilds == null || diskBuilds.Length == 0) return true; - foreach (var diskPath in diskBuilds) + if (diskBuilds.Length > 1) { - string fileName = Path.GetFileName(diskPath); - outputLog.WriteLine($"Generating RAM disk {fileName}..."); + outputLog.WriteLine("Found more than one RAM disk, aborting..."); + return false; + } + + var diskPath = diskBuilds[0]; + + string fileName = Path.GetFileName(diskPath); + string diskName = Path.GetFileNameWithoutExtension(diskPath); + + outputLog.WriteLine($"Generating RAM disk {fileName}..."); - try + try + { + ZXRamDiskFile diskFile = JsonConvert.DeserializeObject(File.ReadAllText(diskPath)); + + if (diskFile == null) { - ZXRamDiskFile diskFile = JsonConvert.DeserializeObject(File.ReadAllText(diskPath)); + outputLog.WriteLine($"Error reading RAM disk {diskPath}, aborted."); + return false; + } - if (diskFile == null) - { - outputLog.WriteLine($"Error reading RAM disk {diskPath}, aborted."); - return false; - } + StringBuilder sb = new StringBuilder(); - StringBuilder sb = new StringBuilder(); + sb.Append($"#define Load{diskName}()"); - List data = new List(); + foreach (var bank in diskFile.Banks) + { + if(bank.Files.Count > 0) + sb.Append($" \\\r\nLoadRamBank({(int)bank.Bank})"); + } - sb.AppendLine($"#define {diskFile.DiskName} {(int)diskFile.Bank}"); + sb.Append("\r\n"); - foreach(var file in diskFile.Files) - { - sb.AppendLine($"#define {file.Name} {data.Count}"); - byte[] fileData; + List usedBanks = new List(); - if (File.Exists(file.SourcePath)) - fileData = File.ReadAllBytes(file.SourcePath); - else - { - outputLog.WriteLine($"File {file.SourcePath} not found, using original content..."); - fileData = file.Content; - } + foreach (var bank in diskFile.Banks) + { + if (bank.Files.Count == 0) + continue; + + usedBanks.Add((int)bank.Bank); + + List data = new List(); + + foreach (var file in bank.Files) + { + sb.AppendLine($"#define {file.Name}Address {data.Count + 0xC000}"); + byte[] fileData = file.Content; data.AddRange(fileData); sb.AppendLine($"#define {file.Name}Size {fileData.Length}"); + sb.AppendLine($"#define {file.Name}Bank {(int)bank.Bank}"); - sb.AppendLine($"#define Load{file.Name}From{diskFile.DiskName}(Dest) LoadRamData({diskFile.DiskName}, {file.Name} + $C000, Dest, {file.Name}Size)"); + sb.AppendLine($"#define Load{file.Name}From{diskName}(Dest) LoadRamData({file.Name}Bank, {file.Name}Address, Dest, {file.Name}Size)"); - sb.AppendLine($"#define LoadPartial{file.Name}From{diskFile.DiskName}(Dest, Size) LoadRamData({diskFile.DiskName}, {file.Name} + $C000, Dest, Size)"); + sb.AppendLine($"#define LoadPartial{file.Name}From{diskName}(Dest, Size) LoadRamData({file.Name}Bank, {file.Name}Address, Dest, Size)"); } - if(BuildType == ZXBuildType.Release) - sb.AppendLine($"\r\nLoadRamDisk({diskFile.DiskName})"); + outputLog.WriteLine($"Writting binary bank {(int)bank.Bank}..."); + File.WriteAllBytes(diskPath.Substring(0, diskPath.Length - 4) + $"_{(int)bank.Bank}.zxrbin", data.ToArray()); + } + + outputLog.WriteLine("Writting include file..."); - outputLog.WriteLine("Writting binary disk..."); - File.WriteAllBytes(diskPath.Substring(0, diskPath.Length - 4) + ".zxrbin", data.ToArray()); - outputLog.WriteLine("Writting include file..."); - File.WriteAllText(diskPath.Substring(0, diskPath.Length - 4) + ".zxbas", sb.ToString()); - outputLog.WriteLine($"RAM disk {fileName} successfully built."); - } - catch (Exception ex) + string mainContent = mainTemplate.Replace("{disk_template}", sb.ToString()) + .Replace("{banks_count}", (usedBanks.Count - 1).ToString()) + .Replace("{banks}", string.Join(", ", usedBanks.Select(b => b.ToString()))); + + if (diskFile.EnableIndirect) { - outputLog.WriteLine($"Error generating RAM disk {diskPath}: \r\n" + ex.ToString()); - return false; + mainContent = mainContent.Replace("{indirect_defs_template}", indirectDefsTemplate.Replace("{indirect_size}", diskFile.IndirectBufferSize.ToString())) + .Replace("{indirect_functions_template}", indirectAsmFuncsTemplate) + .Replace("{basic_functions_template}", indirectBasicSubsTemplate); + } + else + { + mainContent = mainContent.Replace("{indirect_defs_template}", "") + .Replace("{indirect_functions_template}", "") + .Replace("{basic_functions_template}", noIndirectBasicSubsTemplate); } + + if (diskFile.RelocateStack) + { + mainContent = mainContent.Replace("{stack_defs_template}", relocateDefsTemplate) + .Replace("{prologue_epilogue_template}", proEpTemplateRelocate); + } + else + { + mainContent = mainContent.Replace("{stack_defs_template}", "") + .Replace("{prologue_epilogue_template}", proEpTemplateNoRelocate); + } + + File.WriteAllText(diskPath.Substring(0, diskPath.Length - 4) + ".zxbas", mainContent); + outputLog.WriteLine($"RAM disk {fileName} successfully built."); + } + catch (Exception ex) + { + outputLog.WriteLine($"Error generating RAM disk {diskPath}: \r\n" + ex.ToString()); + return false; } + return true; } diff --git a/ZXBStudio/IntegratedDocumentTypes/Resources/ZXRamDisk/ZXRamDiskFactory.cs b/ZXBStudio/IntegratedDocumentTypes/Resources/ZXRamDisk/ZXRamDiskFactory.cs index be7c6c4..a3c5cbe 100644 --- a/ZXBStudio/IntegratedDocumentTypes/Resources/ZXRamDisk/ZXRamDiskFactory.cs +++ b/ZXBStudio/IntegratedDocumentTypes/Resources/ZXRamDisk/ZXRamDiskFactory.cs @@ -20,7 +20,8 @@ public bool CreateDocument(string Path, TextWriter OutputLog) { try { - ZXRamDiskFile doc = new ZXRamDiskFile { DiskName = "Unnamed", Bank = RamDiskBank.Bank4 }; + ZXRamDiskFile doc = new ZXRamDiskFile { RelocateStack = true, EnableIndirect = true, IndirectBufferSize = 64 }; + string content = JsonConvert.SerializeObject(doc); File.WriteAllText(Path, content); return true; diff --git a/ZXBStudio/MainWindow.axaml.cs b/ZXBStudio/MainWindow.axaml.cs index 11c3ba4..9894d5a 100644 --- a/ZXBStudio/MainWindow.axaml.cs +++ b/ZXBStudio/MainWindow.axaml.cs @@ -1330,6 +1330,7 @@ private async void CheckSpectrumModel() if (emu.ModelDefinition?.Model != sModel) { emu.SetModel(sModel); + memView.Initialize(emu.Memory); CreateRomBreakpoints(); await CloseDocumentByFile(ZXConstants.DISASSEMBLY_DOC); _player.Datacorder = emu.Datacorder; @@ -1541,7 +1542,7 @@ private async void BuildAndRun(object? sender, Avalonia.Interactivity.RoutedEven disas.Text = loadedProgram.Disassembly.Content; } - if (!emu.InjectProgram(program.Org, program.Binary, program.RamDisks?.ToArray(), true)) + if (!emu.InjectProgram(program.Org, program.Binary, program.Banks?.ToArray(), true)) { await this.ShowError("Error", "Cannot inject program! Check program size and address."); } @@ -1641,7 +1642,7 @@ private async void BuildAndDebug(object? sender, Avalonia.Interactivity.RoutedEv UpdateUserBreakpoints(); - if (!emu.InjectProgram(program.Org, program.Binary, program.RamDisks?.ToArray(), true)) + if (!emu.InjectProgram(program.Org, program.Binary, program.Banks?.ToArray(), true)) { await this.ShowError("Error", "Cannot inject program! Check program size and address."); } diff --git a/ZXBStudio/ZXBasicStudio.csproj b/ZXBStudio/ZXBasicStudio.csproj index cbd05dc..0099a39 100644 --- a/ZXBStudio/ZXBasicStudio.csproj +++ b/ZXBStudio/ZXBasicStudio.csproj @@ -527,15 +527,15 @@ - - - - - + + + + + - - - + + +