From 655809517c4f65abb796cb79f9c142162342d826 Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Sun, 4 Jun 2017 16:37:03 -0400 Subject: [PATCH 01/77] Ensured that there is a subclass of file that is entrusted to load .O/.80 files, and that the code routes such files to it, noting that it should consider whether a ZX80 is required. --- .../Clock Signal.xcodeproj/project.pbxproj | 6 +++ OSBindings/Mac/Clock Signal/Info.plist | 21 +++++++-- StaticAnalyser/StaticAnalyser.cpp | 6 ++- Storage/Tape/Formats/ZX80O.cpp | 32 +++++++++++++ Storage/Tape/Formats/ZX80O.hpp | 46 +++++++++++++++++++ 5 files changed, 107 insertions(+), 4 deletions(-) create mode 100644 Storage/Tape/Formats/ZX80O.cpp create mode 100644 Storage/Tape/Formats/ZX80O.hpp diff --git a/OSBindings/Mac/Clock Signal.xcodeproj/project.pbxproj b/OSBindings/Mac/Clock Signal.xcodeproj/project.pbxproj index ddb07f5b0..553aac6f1 100644 --- a/OSBindings/Mac/Clock Signal.xcodeproj/project.pbxproj +++ b/OSBindings/Mac/Clock Signal.xcodeproj/project.pbxproj @@ -19,6 +19,7 @@ 4B14145E1B5887AA00E04248 /* 6502AllRAM.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B1414591B58879D00E04248 /* 6502AllRAM.cpp */; }; 4B1414601B58885000E04248 /* WolfgangLorenzTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4B14145F1B58885000E04248 /* WolfgangLorenzTests.swift */; }; 4B1414621B58888700E04248 /* KlausDormannTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4B1414611B58888700E04248 /* KlausDormannTests.swift */; }; + 4B1497881EE4A1DA00CE2596 /* ZX80O.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B1497861EE4A1DA00CE2596 /* ZX80O.cpp */; }; 4B1D08061E0F7A1100763741 /* TimeTests.mm in Sources */ = {isa = PBXBuildFile; fileRef = 4B1D08051E0F7A1100763741 /* TimeTests.mm */; }; 4B1E85751D170228001EF87D /* Typer.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B1E85731D170228001EF87D /* Typer.cpp */; }; 4B1E85811D176468001EF87D /* 6532Tests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4B1E85801D176468001EF87D /* 6532Tests.swift */; }; @@ -464,6 +465,8 @@ 4B14145A1B58879D00E04248 /* 6502AllRAM.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = 6502AllRAM.hpp; sourceTree = ""; }; 4B14145F1B58885000E04248 /* WolfgangLorenzTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = WolfgangLorenzTests.swift; sourceTree = ""; }; 4B1414611B58888700E04248 /* KlausDormannTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = KlausDormannTests.swift; sourceTree = ""; }; + 4B1497861EE4A1DA00CE2596 /* ZX80O.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = ZX80O.cpp; sourceTree = ""; }; + 4B1497871EE4A1DA00CE2596 /* ZX80O.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = ZX80O.hpp; sourceTree = ""; }; 4B1D08051E0F7A1100763741 /* TimeTests.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = TimeTests.mm; sourceTree = ""; }; 4B1E85731D170228001EF87D /* Typer.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = Typer.cpp; sourceTree = ""; }; 4B1E85741D170228001EF87D /* Typer.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = Typer.hpp; sourceTree = ""; }; @@ -1349,6 +1352,8 @@ 4B2BFC5E1D613E0200BA3AA9 /* TapePRG.hpp */, 4B59199A1DAC6C46005BB85C /* OricTAP.cpp */, 4B59199B1DAC6C46005BB85C /* OricTAP.hpp */, + 4B1497861EE4A1DA00CE2596 /* ZX80O.cpp */, + 4B1497871EE4A1DA00CE2596 /* ZX80O.hpp */, ); path = Formats; sourceTree = ""; @@ -2550,6 +2555,7 @@ 4B5FADBA1DE3151600AEC565 /* FileHolder.cpp in Sources */, 4B6C73BD1D387AE500AFCFCA /* DiskController.cpp in Sources */, 4B643F3A1D77AD1900D431D6 /* CSStaticAnalyser.mm in Sources */, + 4B1497881EE4A1DA00CE2596 /* ZX80O.cpp in Sources */, 4B4DC8281D2C2470003C5BF8 /* C1540.cpp in Sources */, 4B5A12571DD55862007A2231 /* Disassembler6502.cpp in Sources */, 4BE7C9181E3D397100A5496D /* TIA.cpp in Sources */, diff --git a/OSBindings/Mac/Clock Signal/Info.plist b/OSBindings/Mac/Clock Signal/Info.plist index c369683c0..cb3dbb978 100644 --- a/OSBindings/Mac/Clock Signal/Info.plist +++ b/OSBindings/Mac/Clock Signal/Info.plist @@ -104,7 +104,7 @@ CFBundleTypeName Commodore Disk CFBundleTypeRole - Viewer + Editor LSTypeIsPackage 0 NSDocumentClass @@ -120,7 +120,7 @@ CFBundleTypeName Commodore 1540/1 Disk CFBundleTypeRole - Viewer + Editor LSTypeIsPackage 0 NSDocumentClass @@ -140,7 +140,7 @@ CFBundleTypeName Electron/BBC Disk Image CFBundleTypeRole - Viewer + Editor NSDocumentClass $(PRODUCT_MODULE_NAME).MachineDocument @@ -154,6 +154,21 @@ CFBundleTypeName Disk Image CFBundleTypeRole + Editor + NSDocumentClass + $(PRODUCT_MODULE_NAME).MachineDocument + + + CFBundleTypeExtensions + + o + 80 + + CFBundleTypeIconFile + cassette + CFBundleTypeName + ZX80 Tape Image + CFBundleTypeRole Viewer NSDocumentClass $(PRODUCT_MODULE_NAME).MachineDocument diff --git a/StaticAnalyser/StaticAnalyser.cpp b/StaticAnalyser/StaticAnalyser.cpp index 803890521..8b3df4c7e 100644 --- a/StaticAnalyser/StaticAnalyser.cpp +++ b/StaticAnalyser/StaticAnalyser.cpp @@ -32,13 +32,15 @@ #include "../Storage/Tape/Formats/OricTAP.hpp" #include "../Storage/Tape/Formats/TapePRG.hpp" #include "../Storage/Tape/Formats/TapeUEF.hpp" +#include "../Storage/Tape/Formats/ZX80O.hpp" typedef int TargetPlatformType; enum class TargetPlatform: TargetPlatformType { Acorn = 1 << 0, Atari2600 = 1 << 1, Commodore = 1 << 2, - Oric = 1 << 3 + Oric = 1 << 3, + ZX80 = 1 << 4, }; using namespace StaticAnalyser; @@ -86,6 +88,7 @@ std::list StaticAnalyser::GetTargets(const char *file_name) if(lowercase_extension) { + Format("80", tapes, Tape::ZX80O, TargetPlatform::ZX80) // 80 Format("a26", cartridges, Cartridge::BinaryDump, TargetPlatform::Atari2600) // A26 Format("adf", disks, Disk::AcornADF, TargetPlatform::Acorn) // ADF Format("bin", cartridges, Cartridge::BinaryDump, TargetPlatform::Atari2600) // BIN @@ -93,6 +96,7 @@ std::list StaticAnalyser::GetTargets(const char *file_name) Format("dsd", disks, Disk::SSD, TargetPlatform::Acorn) // DSD Format("dsk", disks, Disk::OricMFMDSK, TargetPlatform::Oric) // DSK Format("g64", disks, Disk::G64, TargetPlatform::Commodore) // G64 + Format("o", tapes, Tape::ZX80O, TargetPlatform::ZX80) // O // PRG if(!strcmp(lowercase_extension, "prg")) diff --git a/Storage/Tape/Formats/ZX80O.cpp b/Storage/Tape/Formats/ZX80O.cpp new file mode 100644 index 000000000..74407dd00 --- /dev/null +++ b/Storage/Tape/Formats/ZX80O.cpp @@ -0,0 +1,32 @@ +// +// ZX80O.cpp +// Clock Signal +// +// Created by Thomas Harte on 04/06/2017. +// Copyright © 2017 Thomas Harte. All rights reserved. +// + +#include "ZX80O.hpp" + +using namespace Storage::Tape; + +ZX80O::ZX80O(const char *file_name) : + Storage::FileHolder(file_name) { + + // then rewind and start again + virtual_reset(); +} + +void ZX80O::virtual_reset() { + fseek(file_, 0, SEEK_SET); +} + +bool ZX80O::is_at_end() { + return feof(file_); +} + +Tape::Pulse ZX80O::virtual_get_next_pulse() { + Tape::Pulse pulse; + + return pulse; +} diff --git a/Storage/Tape/Formats/ZX80O.hpp b/Storage/Tape/Formats/ZX80O.hpp new file mode 100644 index 000000000..e486c0b96 --- /dev/null +++ b/Storage/Tape/Formats/ZX80O.hpp @@ -0,0 +1,46 @@ +// +// ZX80O.hpp +// Clock Signal +// +// Created by Thomas Harte on 04/06/2017. +// Copyright © 2017 Thomas Harte. All rights reserved. +// + +#ifndef ZX80O_hpp +#define ZX80O_hpp + +#include "../Tape.hpp" +#include "../../FileHolder.hpp" +#include + +namespace Storage { +namespace Tape { + +/*! + Provides a @c Tape containing a ZX80-format .O tape image, which is a byte stream capture. +*/ +class ZX80O: public Tape, public Storage::FileHolder { + public: + /*! + Constructs an @c ZX80O containing content from the file with name @c file_name. + + @throws ErrorNotZX80O if this file could not be opened and recognised as a valid ZX80-format .O. + */ + ZX80O(const char *file_name); + + enum { + ErrorNotZX80O + }; + + // implemented to satisfy @c Tape + bool is_at_end(); + + private: + void virtual_reset(); + Pulse virtual_get_next_pulse(); +}; + +} +} + +#endif /* ZX80O_hpp */ From 8c1769f1579db8b3a7d6ed2db55a42c6fd90d336 Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Sun, 4 Jun 2017 16:59:26 -0400 Subject: [PATCH 02/77] Made a quick attempt at serialising from ZX80 .O to waves. --- .../xcschemes/Clock Signal.xcscheme | 2 +- Storage/Tape/Formats/ZX80O.cpp | 70 ++++++++++++++++++- Storage/Tape/Formats/ZX80O.hpp | 8 +++ 3 files changed, 78 insertions(+), 2 deletions(-) diff --git a/OSBindings/Mac/Clock Signal.xcodeproj/xcshareddata/xcschemes/Clock Signal.xcscheme b/OSBindings/Mac/Clock Signal.xcodeproj/xcshareddata/xcschemes/Clock Signal.xcscheme index 2cfe830df..6609a35c0 100644 --- a/OSBindings/Mac/Clock Signal.xcodeproj/xcshareddata/xcschemes/Clock Signal.xcscheme +++ b/OSBindings/Mac/Clock Signal.xcodeproj/xcshareddata/xcschemes/Clock Signal.xcscheme @@ -68,7 +68,7 @@ 16384) throw ErrorNotZX80O; + + // skip the system area + fseek(file_, 8, SEEK_SET); + + // read the pointer to VARS and the alleged pointer to end of file + uint16_t vars = fgetc16le(); + end_of_file_ = fgetc16le(); + + // VARs should be before end of file + if(vars > end_of_file_) throw ErrorNotZX80O; + + // end of file should be no further than the actual file size + if(end_of_file_ - 0x4000 > file_stats_.st_size) throw ErrorNotZX80O; + + // TODO: does it make sense to inspect the tokenised BASIC? + // It starts at 0x4028 and proceeds as [16-bit line number] [tokens] [0x76], + // but I'm as yet unable to find documentation of the tokens. + // then rewind and start again virtual_reset(); } void ZX80O::virtual_reset() { fseek(file_, 0, SEEK_SET); + is_past_silence_ = false; + is_high_ = true; + bit_pointer_ = 9; } bool ZX80O::is_at_end() { - return feof(file_); + return ftell(file_) == end_of_file_ - 0x4000; } Tape::Pulse ZX80O::virtual_get_next_pulse() { Tape::Pulse pulse; + // Start with 1 second of silence. + if(!is_past_silence_) { + pulse.type = Pulse::Type::Zero; + pulse.length.length = 1; + pulse.length.clock_rate = 1; + is_past_silence_ = true; + return pulse; + } + + // For each byte, output 8 bits and then silence. + if(bit_pointer_ == 9) { + byte_ = (uint8_t)fgetc(file_); + bit_pointer_ = 0; + wave_pointer_ = 0; + } + + // Bytes are stored MSB first. + if(bit_pointer_ < 8) { + // waves are of length 150µs + pulse.length.length = 3; + pulse.length.clock_rate = 20000; + + if(is_high_) { + pulse.type = Pulse::Type::High; + is_high_ = false; + } else { + pulse.type = Pulse::Type::Low; + is_high_ = true; + + int wave_count = (byte_ & (0x80 >> bit_pointer_)) ? 9 : 4; + wave_pointer_++; + if(wave_pointer_ == wave_count) { + bit_pointer_++; + wave_pointer_ = 0; + } + } + } else { + // post-waves silence is 1300µs + pulse.length.length = 13; + pulse.length.clock_rate = 10000; + pulse.type = Pulse::Type::Zero; + + bit_pointer_++; + } + return pulse; } diff --git a/Storage/Tape/Formats/ZX80O.hpp b/Storage/Tape/Formats/ZX80O.hpp index e486c0b96..5535adea5 100644 --- a/Storage/Tape/Formats/ZX80O.hpp +++ b/Storage/Tape/Formats/ZX80O.hpp @@ -38,6 +38,14 @@ class ZX80O: public Tape, public Storage::FileHolder { private: void virtual_reset(); Pulse virtual_get_next_pulse(); + + uint8_t byte_; + int bit_pointer_; + int wave_pointer_; + bool is_past_silence_; + bool is_high_; + + uint16_t end_of_file_; }; } From 02b7c3d1b094560deae511810749785bbf6d5c4a Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Sun, 4 Jun 2017 17:04:06 -0400 Subject: [PATCH 03/77] Added the necessary wiring to get into a ZX80/81-oriented part of the static analyser, which could in principle post a ZX80 target. --- .../Clock Signal.xcodeproj/project.pbxproj | 16 ++++++++++- StaticAnalyser/StaticAnalyser.cpp | 2 ++ StaticAnalyser/StaticAnalyser.hpp | 3 ++- StaticAnalyser/ZX8081/StaticAnalyser.cpp | 16 +++++++++++ StaticAnalyser/ZX8081/StaticAnalyser.hpp | 27 +++++++++++++++++++ 5 files changed, 62 insertions(+), 2 deletions(-) create mode 100644 StaticAnalyser/ZX8081/StaticAnalyser.cpp create mode 100644 StaticAnalyser/ZX8081/StaticAnalyser.hpp diff --git a/OSBindings/Mac/Clock Signal.xcodeproj/project.pbxproj b/OSBindings/Mac/Clock Signal.xcodeproj/project.pbxproj index 553aac6f1..f7f699843 100644 --- a/OSBindings/Mac/Clock Signal.xcodeproj/project.pbxproj +++ b/OSBindings/Mac/Clock Signal.xcodeproj/project.pbxproj @@ -20,6 +20,7 @@ 4B1414601B58885000E04248 /* WolfgangLorenzTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4B14145F1B58885000E04248 /* WolfgangLorenzTests.swift */; }; 4B1414621B58888700E04248 /* KlausDormannTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4B1414611B58888700E04248 /* KlausDormannTests.swift */; }; 4B1497881EE4A1DA00CE2596 /* ZX80O.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B1497861EE4A1DA00CE2596 /* ZX80O.cpp */; }; + 4B14978B1EE4AC5E00CE2596 /* StaticAnalyser.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B1497891EE4AC5E00CE2596 /* StaticAnalyser.cpp */; }; 4B1D08061E0F7A1100763741 /* TimeTests.mm in Sources */ = {isa = PBXBuildFile; fileRef = 4B1D08051E0F7A1100763741 /* TimeTests.mm */; }; 4B1E85751D170228001EF87D /* Typer.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B1E85731D170228001EF87D /* Typer.cpp */; }; 4B1E85811D176468001EF87D /* 6532Tests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4B1E85801D176468001EF87D /* 6532Tests.swift */; }; @@ -467,6 +468,8 @@ 4B1414611B58888700E04248 /* KlausDormannTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = KlausDormannTests.swift; sourceTree = ""; }; 4B1497861EE4A1DA00CE2596 /* ZX80O.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = ZX80O.cpp; sourceTree = ""; }; 4B1497871EE4A1DA00CE2596 /* ZX80O.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = ZX80O.hpp; sourceTree = ""; }; + 4B1497891EE4AC5E00CE2596 /* StaticAnalyser.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = StaticAnalyser.cpp; path = ../../StaticAnalyser/ZX8081/StaticAnalyser.cpp; sourceTree = ""; }; + 4B14978A1EE4AC5E00CE2596 /* StaticAnalyser.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; name = StaticAnalyser.hpp; path = ../../StaticAnalyser/ZX8081/StaticAnalyser.hpp; sourceTree = ""; }; 4B1D08051E0F7A1100763741 /* TimeTests.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = TimeTests.mm; sourceTree = ""; }; 4B1E85731D170228001EF87D /* Typer.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = Typer.cpp; sourceTree = ""; }; 4B1E85741D170228001EF87D /* Typer.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = Typer.hpp; sourceTree = ""; }; @@ -1067,6 +1070,15 @@ name = "Test Binaries"; sourceTree = ""; }; + 4B14978C1EE4AC6200CE2596 /* ZX80/81 */ = { + isa = PBXGroup; + children = ( + 4B1497891EE4AC5E00CE2596 /* StaticAnalyser.cpp */, + 4B14978A1EE4AC5E00CE2596 /* StaticAnalyser.hpp */, + ); + name = ZX80/81; + sourceTree = ""; + }; 4B1E85791D174DEC001EF87D /* 6532 */ = { isa = PBXGroup; children = ( @@ -2077,8 +2089,9 @@ 4BD14B121D7462810088EAD6 /* Acorn */, 4BA799961D8B65730045123D /* Atari */, 4BC830D21D6E7C6D0000A26F /* Commodore */, - 4BCF1FAC1DADD41F0039D2E7 /* Oric */, 4B5A12581DD55873007A2231 /* Disassembler */, + 4BCF1FAC1DADD41F0039D2E7 /* Oric */, + 4B14978C1EE4AC6200CE2596 /* ZX80/81 */, ); name = StaticAnalyser; sourceTree = ""; @@ -2547,6 +2560,7 @@ 4BF8295D1D8F048B001BAE39 /* MFM.cpp in Sources */, 4BE77A2E1D84ADFB00BC3827 /* File.cpp in Sources */, 4B5FADBD1DE31D1500AEC565 /* OricMFMDSK.cpp in Sources */, + 4B14978B1EE4AC5E00CE2596 /* StaticAnalyser.cpp in Sources */, 4B77069D1EC904570053B588 /* Z80.cpp in Sources */, 4BAB62B51D327F7E00DF5BA0 /* G64.cpp in Sources */, 4BD468F71D8DF41D0084958B /* 1770.cpp in Sources */, diff --git a/StaticAnalyser/StaticAnalyser.cpp b/StaticAnalyser/StaticAnalyser.cpp index 8b3df4c7e..d5c777ba8 100644 --- a/StaticAnalyser/StaticAnalyser.cpp +++ b/StaticAnalyser/StaticAnalyser.cpp @@ -15,6 +15,7 @@ #include "Atari/StaticAnalyser.hpp" #include "Commodore/StaticAnalyser.hpp" #include "Oric/StaticAnalyser.hpp" +#include "ZX8081/StaticAnalyser.hpp" // Cartridges #include "../Storage/Cartridge/Formats/BinaryDump.hpp" @@ -129,6 +130,7 @@ std::list StaticAnalyser::GetTargets(const char *file_name) if(potential_platforms & (TargetPlatformType)TargetPlatform::Atari2600) Atari::AddTargets(disks, tapes, cartridges, targets); if(potential_platforms & (TargetPlatformType)TargetPlatform::Commodore) Commodore::AddTargets(disks, tapes, cartridges, targets); if(potential_platforms & (TargetPlatformType)TargetPlatform::Oric) Oric::AddTargets(disks, tapes, cartridges, targets); + if(potential_platforms & (TargetPlatformType)TargetPlatform::ZX80) ZX8081::AddTargets(disks, tapes, cartridges, targets); free(lowercase_extension); } diff --git a/StaticAnalyser/StaticAnalyser.hpp b/StaticAnalyser/StaticAnalyser.hpp index 3d5cf2f61..8aef59c62 100644 --- a/StaticAnalyser/StaticAnalyser.hpp +++ b/StaticAnalyser/StaticAnalyser.hpp @@ -49,7 +49,8 @@ struct Target { Atari2600, Electron, Vic20, - Oric + Oric, + ZX80 } machine; float probability; diff --git a/StaticAnalyser/ZX8081/StaticAnalyser.cpp b/StaticAnalyser/ZX8081/StaticAnalyser.cpp new file mode 100644 index 000000000..505ea4811 --- /dev/null +++ b/StaticAnalyser/ZX8081/StaticAnalyser.cpp @@ -0,0 +1,16 @@ +// +// StaticAnalyser.cpp +// Clock Signal +// +// Created by Thomas Harte on 04/06/2017. +// Copyright © 2017 Thomas Harte. All rights reserved. +// + +#include "StaticAnalyser.hpp" + +void StaticAnalyser::ZX8081::AddTargets( + const std::list> &disks, + const std::list> &tapes, + const std::list> &cartridges, + std::list &destination) { +} diff --git a/StaticAnalyser/ZX8081/StaticAnalyser.hpp b/StaticAnalyser/ZX8081/StaticAnalyser.hpp new file mode 100644 index 000000000..470e5920f --- /dev/null +++ b/StaticAnalyser/ZX8081/StaticAnalyser.hpp @@ -0,0 +1,27 @@ +// +// StaticAnalyser.hpp +// Clock Signal +// +// Created by Thomas Harte on 04/06/2017. +// Copyright © 2017 Thomas Harte. All rights reserved. +// + +#ifndef StaticAnalyser_ZX8081_StaticAnalyser_hpp +#define StaticAnalyser_ZX8081_StaticAnalyser_hpp + +#include "../StaticAnalyser.hpp" + +namespace StaticAnalyser { +namespace ZX8081 { + +void AddTargets( + const std::list> &disks, + const std::list> &tapes, + const std::list> &cartridges, + std::list &destination +); + +} +} + +#endif /* StaticAnalyser_hpp */ From d2637123c4dfbdd3498f51bd73f3b7ce2afafe1b Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Sun, 4 Jun 2017 17:55:19 -0400 Subject: [PATCH 04/77] Added necessary support to get as far as an empty window when attempting to load a piece of ZX80 software. --- Machines/ZX8081/ZX8081.cpp | 46 ++++++++++ Machines/ZX8081/ZX8081.hpp | 44 +++++++++ .../Clock Signal.xcodeproj/project.pbxproj | 30 +++++- .../Clock Signal/Base.lproj/ZX8081Options.xib | 92 +++++++++++++++++++ .../StaticAnalyser/CSStaticAnalyser.mm | 3 + .../Clock Signal/Machine/Wrappers/CSZX8081.h | 17 ++++ .../Clock Signal/Machine/Wrappers/CSZX8081.mm | 38 ++++++++ Processors/Z80/Z80.hpp | 2 +- StaticAnalyser/ZX8081/StaticAnalyser.cpp | 5 + 9 files changed, 273 insertions(+), 4 deletions(-) create mode 100644 Machines/ZX8081/ZX8081.cpp create mode 100644 Machines/ZX8081/ZX8081.hpp create mode 100644 OSBindings/Mac/Clock Signal/Base.lproj/ZX8081Options.xib create mode 100644 OSBindings/Mac/Clock Signal/Machine/Wrappers/CSZX8081.h create mode 100644 OSBindings/Mac/Clock Signal/Machine/Wrappers/CSZX8081.mm diff --git a/Machines/ZX8081/ZX8081.cpp b/Machines/ZX8081/ZX8081.cpp new file mode 100644 index 000000000..ac9483e16 --- /dev/null +++ b/Machines/ZX8081/ZX8081.cpp @@ -0,0 +1,46 @@ +// +// ZX8081.cpp +// Clock Signal +// +// Created by Thomas Harte on 04/06/2017. +// Copyright © 2017 Thomas Harte. All rights reserved. +// + +#include "ZX8081.hpp" + +using namespace ZX8081; + +Machine::Machine() { + // run at 3.25 Mhz + set_clock_rate(3250000); +} + +int Machine::perform_machine_cycle(const CPU::Z80::MachineCycle &cycle) { + return 0; +} + +void Machine::setup_output(float aspect_ratio) { + crt_.reset(new Outputs::CRT::CRT(207 * 8, 8, Outputs::CRT::DisplayType::PAL50, 1)); + crt_->set_rgb_sampling_function( + "vec3 rgb_sample(usampler2D sampler, vec2 coordinate, vec2 icoordinate)" + "{" + "return vec3(1.0);" + "}"); +} + +void Machine::close_output() { +} + +std::shared_ptr Machine::get_crt() { + return crt_; +} + +std::shared_ptr Machine::get_speaker() { + return nullptr; +} + +void Machine::run_for_cycles(int number_of_cycles) { +} + +void Machine::configure_as_target(const StaticAnalyser::Target &target) { +} diff --git a/Machines/ZX8081/ZX8081.hpp b/Machines/ZX8081/ZX8081.hpp new file mode 100644 index 000000000..c2ccc8df4 --- /dev/null +++ b/Machines/ZX8081/ZX8081.hpp @@ -0,0 +1,44 @@ +// +// ZX8081.hpp +// Clock Signal +// +// Created by Thomas Harte on 04/06/2017. +// Copyright © 2017 Thomas Harte. All rights reserved. +// + +#ifndef ZX8081_hpp +#define ZX8081_hpp + +#include "../ConfigurationTarget.hpp" +#include "../CRTMachine.hpp" + +#include "../../Processors/Z80/Z80.hpp" + +namespace ZX8081 { + +class Machine: + public CPU::Z80::Processor, + public CRTMachine::Machine, + public ConfigurationTarget::Machine { + public: + Machine(); + + int perform_machine_cycle(const CPU::Z80::MachineCycle &cycle); + + void setup_output(float aspect_ratio); + void close_output(); + + std::shared_ptr get_crt(); + std::shared_ptr get_speaker(); + + void run_for_cycles(int number_of_cycles); + + void configure_as_target(const StaticAnalyser::Target &target); + + private: + std::shared_ptr crt_; +}; + +} + +#endif /* ZX8081_hpp */ diff --git a/OSBindings/Mac/Clock Signal.xcodeproj/project.pbxproj b/OSBindings/Mac/Clock Signal.xcodeproj/project.pbxproj index f7f699843..cdd3bb44f 100644 --- a/OSBindings/Mac/Clock Signal.xcodeproj/project.pbxproj +++ b/OSBindings/Mac/Clock Signal.xcodeproj/project.pbxproj @@ -21,6 +21,9 @@ 4B1414621B58888700E04248 /* KlausDormannTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4B1414611B58888700E04248 /* KlausDormannTests.swift */; }; 4B1497881EE4A1DA00CE2596 /* ZX80O.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B1497861EE4A1DA00CE2596 /* ZX80O.cpp */; }; 4B14978B1EE4AC5E00CE2596 /* StaticAnalyser.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B1497891EE4AC5E00CE2596 /* StaticAnalyser.cpp */; }; + 4B14978F1EE4B4D200CE2596 /* CSZX8081.mm in Sources */ = {isa = PBXBuildFile; fileRef = 4B14978E1EE4B4D200CE2596 /* CSZX8081.mm */; }; + 4B1497921EE4B5A800CE2596 /* ZX8081.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B1497901EE4B5A800CE2596 /* ZX8081.cpp */; }; + 4B1497951EE4B7D300CE2596 /* ZX8081Options.xib in Resources */ = {isa = PBXBuildFile; fileRef = 4B1497941EE4B7D300CE2596 /* ZX8081Options.xib */; }; 4B1D08061E0F7A1100763741 /* TimeTests.mm in Sources */ = {isa = PBXBuildFile; fileRef = 4B1D08051E0F7A1100763741 /* TimeTests.mm */; }; 4B1E85751D170228001EF87D /* Typer.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B1E85731D170228001EF87D /* Typer.cpp */; }; 4B1E85811D176468001EF87D /* 6532Tests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4B1E85801D176468001EF87D /* 6532Tests.swift */; }; @@ -470,6 +473,11 @@ 4B1497871EE4A1DA00CE2596 /* ZX80O.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = ZX80O.hpp; sourceTree = ""; }; 4B1497891EE4AC5E00CE2596 /* StaticAnalyser.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = StaticAnalyser.cpp; path = ../../StaticAnalyser/ZX8081/StaticAnalyser.cpp; sourceTree = ""; }; 4B14978A1EE4AC5E00CE2596 /* StaticAnalyser.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; name = StaticAnalyser.hpp; path = ../../StaticAnalyser/ZX8081/StaticAnalyser.hpp; sourceTree = ""; }; + 4B14978D1EE4B4D200CE2596 /* CSZX8081.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CSZX8081.h; sourceTree = ""; }; + 4B14978E1EE4B4D200CE2596 /* CSZX8081.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = CSZX8081.mm; sourceTree = ""; }; + 4B1497901EE4B5A800CE2596 /* ZX8081.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = ZX8081.cpp; path = ZX8081/ZX8081.cpp; sourceTree = ""; }; + 4B1497911EE4B5A800CE2596 /* ZX8081.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; name = ZX8081.hpp; path = ZX8081/ZX8081.hpp; sourceTree = ""; }; + 4B1497941EE4B7D300CE2596 /* ZX8081Options.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = ZX8081Options.xib; sourceTree = ""; }; 4B1D08051E0F7A1100763741 /* TimeTests.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = TimeTests.mm; sourceTree = ""; }; 4B1E85731D170228001EF87D /* Typer.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = Typer.cpp; sourceTree = ""; }; 4B1E85741D170228001EF87D /* Typer.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = Typer.hpp; sourceTree = ""; }; @@ -1079,6 +1087,15 @@ name = ZX80/81; sourceTree = ""; }; + 4B1497931EE4B5AC00CE2596 /* ZX8081 */ = { + isa = PBXGroup; + children = ( + 4B1497901EE4B5A800CE2596 /* ZX8081.cpp */, + 4B1497911EE4B5A800CE2596 /* ZX8081.hpp */, + ); + name = ZX8081; + sourceTree = ""; + }; 4B1E85791D174DEC001EF87D /* 6532 */ = { isa = PBXGroup; children = ( @@ -1144,13 +1161,15 @@ isa = PBXGroup; children = ( 4B2A53991D117D36003C6002 /* CSAtari2600.h */, - 4B2A539A1D117D36003C6002 /* CSAtari2600.mm */, 4B2A539B1D117D36003C6002 /* CSElectron.h */, - 4B2A539C1D117D36003C6002 /* CSElectron.mm */, 4BCF1FA61DADC5250039D2E7 /* CSOric.h */, - 4BCF1FA71DADC5250039D2E7 /* CSOric.mm */, 4B2A539D1D117D36003C6002 /* CSVic20.h */, + 4B14978D1EE4B4D200CE2596 /* CSZX8081.h */, + 4B2A539A1D117D36003C6002 /* CSAtari2600.mm */, + 4B2A539C1D117D36003C6002 /* CSElectron.mm */, + 4BCF1FA71DADC5250039D2E7 /* CSOric.mm */, 4B2A539E1D117D36003C6002 /* CSVic20.mm */, + 4B14978E1EE4B4D200CE2596 /* CSZX8081.mm */, ); path = Wrappers; sourceTree = ""; @@ -1280,6 +1299,7 @@ 4B8FE2211DA19FB20090D3CE /* MachinePanel.swift */, 4B2A332E1DB86869002876E3 /* OricOptionsPanel.swift */, 4B9CCDA01DA279CA0098B625 /* Vic20OptionsPanel.swift */, + 4B1497941EE4B7D300CE2596 /* ZX8081Options.xib */, 4B8FE2131DA19D5F0090D3CE /* Atari2600Options.xib */, 4B8FE2171DA19D5F0090D3CE /* ElectronOptions.xib */, 4B8FE2151DA19D5F0090D3CE /* MachineDocument.xib */, @@ -1851,6 +1871,7 @@ 4B4DC81D1D2C2425003C5BF8 /* Commodore */, 4B2E2D9E1C3A070900138695 /* Electron */, 4BCF1FA51DADC3E10039D2E7 /* Oric */, + 4B1497931EE4B5AC00CE2596 /* ZX8081 */, ); name = Machines; path = ../../Machines; @@ -2221,6 +2242,7 @@ 4B8FE21D1DA19D5F0090D3CE /* ElectronOptions.xib in Resources */, 4B79E4461E3AF38600141F11 /* floppy525.png in Resources */, 4BC9DF451D044FCA00F44158 /* ROMImages in Resources */, + 4B1497951EE4B7D300CE2596 /* ZX8081Options.xib in Resources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -2530,6 +2552,7 @@ 4BCF1FA41DADC3DD0039D2E7 /* Oric.cpp in Sources */, 4BEA525E1DF33323007E74F2 /* Tape.cpp in Sources */, 4BC8A62D1DCE60E000DAC693 /* Typer.cpp in Sources */, + 4B1497921EE4B5A800CE2596 /* ZX8081.cpp in Sources */, 4B643F3F1D77B88000D431D6 /* DocumentController.swift in Sources */, 4BA799951D8B656E0045123D /* StaticAnalyser.cpp in Sources */, 4B2BFDB21DAEF5FF001A68B8 /* Video.cpp in Sources */, @@ -2608,6 +2631,7 @@ 4BEE0A701D72496600532C7B /* PRG.cpp in Sources */, 4B8FE2271DA1DE2D0090D3CE /* NSBundle+DataResource.m in Sources */, 4B2A53A01D117D36003C6002 /* CSMachine.mm in Sources */, + 4B14978F1EE4B4D200CE2596 /* CSZX8081.mm in Sources */, 4BC91B831D1F160E00884B76 /* CommodoreTAP.cpp in Sources */, 4BCF1FAB1DADD41B0039D2E7 /* StaticAnalyser.cpp in Sources */, 4B2A539F1D117D36003C6002 /* CSAudioQueue.m in Sources */, diff --git a/OSBindings/Mac/Clock Signal/Base.lproj/ZX8081Options.xib b/OSBindings/Mac/Clock Signal/Base.lproj/ZX8081Options.xib new file mode 100644 index 000000000..c578c2188 --- /dev/null +++ b/OSBindings/Mac/Clock Signal/Base.lproj/ZX8081Options.xib @@ -0,0 +1,92 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/OSBindings/Mac/Clock Signal/Machine/StaticAnalyser/CSStaticAnalyser.mm b/OSBindings/Mac/Clock Signal/Machine/StaticAnalyser/CSStaticAnalyser.mm index f8c07c466..08c354208 100644 --- a/OSBindings/Mac/Clock Signal/Machine/StaticAnalyser/CSStaticAnalyser.mm +++ b/OSBindings/Mac/Clock Signal/Machine/StaticAnalyser/CSStaticAnalyser.mm @@ -18,6 +18,7 @@ #import "CSElectron.h" #import "CSOric.h" #import "CSVic20.h" +#import "CSZX8081.h" #import "Clock_Signal-Swift.h" @@ -49,6 +50,7 @@ case StaticAnalyser::Target::Electron: return @"ElectronOptions"; case StaticAnalyser::Target::Oric: return @"OricOptions"; case StaticAnalyser::Target::Vic20: return @"Vic20Options"; + case StaticAnalyser::Target::ZX80: return @"ZX8081Options"; default: return nil; } } @@ -61,6 +63,7 @@ case StaticAnalyser::Target::Electron: return [[CSElectron alloc] init]; case StaticAnalyser::Target::Oric: return [[CSOric alloc] init]; case StaticAnalyser::Target::Vic20: return [[CSVic20 alloc] init]; + case StaticAnalyser::Target::ZX80: return [[CSZX8081 alloc] init]; default: return nil; } } diff --git a/OSBindings/Mac/Clock Signal/Machine/Wrappers/CSZX8081.h b/OSBindings/Mac/Clock Signal/Machine/Wrappers/CSZX8081.h new file mode 100644 index 000000000..502bf21a8 --- /dev/null +++ b/OSBindings/Mac/Clock Signal/Machine/Wrappers/CSZX8081.h @@ -0,0 +1,17 @@ +// +// CSZX8081.h +// Clock Signal +// +// Created by Thomas Harte on 04/06/2017. +// Copyright © 2017 Thomas Harte. All rights reserved. +// + +#import "CSMachine.h" +#import "CSKeyboardMachine.h" +#import "CSFastLoading.h" + +@interface CSZX8081 : CSMachine + +@property (nonatomic, assign) BOOL useFastLoadingHack; + +@end diff --git a/OSBindings/Mac/Clock Signal/Machine/Wrappers/CSZX8081.mm b/OSBindings/Mac/Clock Signal/Machine/Wrappers/CSZX8081.mm new file mode 100644 index 000000000..32bb5703c --- /dev/null +++ b/OSBindings/Mac/Clock Signal/Machine/Wrappers/CSZX8081.mm @@ -0,0 +1,38 @@ +// +// CSZX8081.m +// Clock Signal +// +// Created by Thomas Harte on 04/06/2017. +// Copyright © 2017 Thomas Harte. All rights reserved. +// + +#import "CSZX8081.h" + +#include "ZX8081.hpp" + +@implementation CSZX8081 { + ZX8081::Machine zx8081; +} + +- (CRTMachine::Machine * const)machine { + return &zx8081; +} + +- (instancetype)init { + self = [super init]; + if(self) { + } + return self; +} + +#pragma mark - Keyboard Mapping + +- (void)clearAllKeys { +} + +- (void)setKey:(uint16_t)key isPressed:(BOOL)isPressed { +} + +- (NSString *)userDefaultsPrefix { return @"zx8081"; } + +@end diff --git a/Processors/Z80/Z80.hpp b/Processors/Z80/Z80.hpp index 44bc4ed8e..d901a361a 100644 --- a/Processors/Z80/Z80.hpp +++ b/Processors/Z80/Z80.hpp @@ -345,7 +345,7 @@ template class Processor { // Copy in all programs and set pointers. size_t destination = 0; - for(int c = 0; c < 256; c++) { + for(size_t c = 0; c < 256; c++) { target.instructions[c] = &target.all_operations[destination]; for(int t = 0; t < lengths[c];) { // Skip zero-length bus cycles. diff --git a/StaticAnalyser/ZX8081/StaticAnalyser.cpp b/StaticAnalyser/ZX8081/StaticAnalyser.cpp index 505ea4811..ce8fa0383 100644 --- a/StaticAnalyser/ZX8081/StaticAnalyser.cpp +++ b/StaticAnalyser/ZX8081/StaticAnalyser.cpp @@ -13,4 +13,9 @@ void StaticAnalyser::ZX8081::AddTargets( const std::list> &tapes, const std::list> &cartridges, std::list &destination) { + // Temporary: be entirely trusting. + StaticAnalyser::Target target; + target.machine = Target::ZX80; + target.tapes = tapes; + destination.push_back(target); } From b0a7c582870929bf67523ba89eb09bd0cdf2a600 Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Sun, 4 Jun 2017 17:57:37 -0400 Subject: [PATCH 05/77] Fixed project to point to the XIB I actually want to keep; fixed that XIB to have the correct contents. --- .../Clock Signal.xcodeproj/project.pbxproj | 16 +++-- .../Clock Signal/Base.lproj/ZX8081Options.xib | 60 +++---------------- 2 files changed, 20 insertions(+), 56 deletions(-) diff --git a/OSBindings/Mac/Clock Signal.xcodeproj/project.pbxproj b/OSBindings/Mac/Clock Signal.xcodeproj/project.pbxproj index cdd3bb44f..94dbe958e 100644 --- a/OSBindings/Mac/Clock Signal.xcodeproj/project.pbxproj +++ b/OSBindings/Mac/Clock Signal.xcodeproj/project.pbxproj @@ -23,7 +23,7 @@ 4B14978B1EE4AC5E00CE2596 /* StaticAnalyser.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B1497891EE4AC5E00CE2596 /* StaticAnalyser.cpp */; }; 4B14978F1EE4B4D200CE2596 /* CSZX8081.mm in Sources */ = {isa = PBXBuildFile; fileRef = 4B14978E1EE4B4D200CE2596 /* CSZX8081.mm */; }; 4B1497921EE4B5A800CE2596 /* ZX8081.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B1497901EE4B5A800CE2596 /* ZX8081.cpp */; }; - 4B1497951EE4B7D300CE2596 /* ZX8081Options.xib in Resources */ = {isa = PBXBuildFile; fileRef = 4B1497941EE4B7D300CE2596 /* ZX8081Options.xib */; }; + 4B1497981EE4B97F00CE2596 /* ZX8081Options.xib in Resources */ = {isa = PBXBuildFile; fileRef = 4B1497961EE4B97F00CE2596 /* ZX8081Options.xib */; }; 4B1D08061E0F7A1100763741 /* TimeTests.mm in Sources */ = {isa = PBXBuildFile; fileRef = 4B1D08051E0F7A1100763741 /* TimeTests.mm */; }; 4B1E85751D170228001EF87D /* Typer.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B1E85731D170228001EF87D /* Typer.cpp */; }; 4B1E85811D176468001EF87D /* 6532Tests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4B1E85801D176468001EF87D /* 6532Tests.swift */; }; @@ -477,7 +477,7 @@ 4B14978E1EE4B4D200CE2596 /* CSZX8081.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = CSZX8081.mm; sourceTree = ""; }; 4B1497901EE4B5A800CE2596 /* ZX8081.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = ZX8081.cpp; path = ZX8081/ZX8081.cpp; sourceTree = ""; }; 4B1497911EE4B5A800CE2596 /* ZX8081.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; name = ZX8081.hpp; path = ZX8081/ZX8081.hpp; sourceTree = ""; }; - 4B1497941EE4B7D300CE2596 /* ZX8081Options.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = ZX8081Options.xib; sourceTree = ""; }; + 4B1497971EE4B97F00CE2596 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = Base; path = "Clock Signal/Base.lproj/ZX8081Options.xib"; sourceTree = SOURCE_ROOT; }; 4B1D08051E0F7A1100763741 /* TimeTests.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = TimeTests.mm; sourceTree = ""; }; 4B1E85731D170228001EF87D /* Typer.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = Typer.cpp; sourceTree = ""; }; 4B1E85741D170228001EF87D /* Typer.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = Typer.hpp; sourceTree = ""; }; @@ -1299,12 +1299,12 @@ 4B8FE2211DA19FB20090D3CE /* MachinePanel.swift */, 4B2A332E1DB86869002876E3 /* OricOptionsPanel.swift */, 4B9CCDA01DA279CA0098B625 /* Vic20OptionsPanel.swift */, - 4B1497941EE4B7D300CE2596 /* ZX8081Options.xib */, 4B8FE2131DA19D5F0090D3CE /* Atari2600Options.xib */, 4B8FE2171DA19D5F0090D3CE /* ElectronOptions.xib */, 4B8FE2151DA19D5F0090D3CE /* MachineDocument.xib */, 4B2A332B1DB86821002876E3 /* OricOptions.xib */, 4B8FE2191DA19D5F0090D3CE /* Vic20Options.xib */, + 4B1497961EE4B97F00CE2596 /* ZX8081Options.xib */, ); path = Documents; sourceTree = ""; @@ -2242,7 +2242,7 @@ 4B8FE21D1DA19D5F0090D3CE /* ElectronOptions.xib in Resources */, 4B79E4461E3AF38600141F11 /* floppy525.png in Resources */, 4BC9DF451D044FCA00F44158 /* ROMImages in Resources */, - 4B1497951EE4B7D300CE2596 /* ZX8081Options.xib in Resources */, + 4B1497981EE4B97F00CE2596 /* ZX8081Options.xib in Resources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -2704,6 +2704,14 @@ /* End PBXTargetDependency section */ /* Begin PBXVariantGroup section */ + 4B1497961EE4B97F00CE2596 /* ZX8081Options.xib */ = { + isa = PBXVariantGroup; + children = ( + 4B1497971EE4B97F00CE2596 /* Base */, + ); + name = ZX8081Options.xib; + sourceTree = ""; + }; 4B2A332B1DB86821002876E3 /* OricOptions.xib */ = { isa = PBXVariantGroup; children = ( diff --git a/OSBindings/Mac/Clock Signal/Base.lproj/ZX8081Options.xib b/OSBindings/Mac/Clock Signal/Base.lproj/ZX8081Options.xib index c578c2188..b1dcdf861 100644 --- a/OSBindings/Mac/Clock Signal/Base.lproj/ZX8081Options.xib +++ b/OSBindings/Mac/Clock Signal/Base.lproj/ZX8081Options.xib @@ -1,7 +1,7 @@ - + - + @@ -12,17 +12,17 @@ - + - + - + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + - - - - - + From c485c460f7abd9c53a8e4570a4fb48b2c3f055aa Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Sun, 4 Jun 2017 18:08:35 -0400 Subject: [PATCH 06/77] Imported the ZX80 and 81 system ROMs (though not publicly), added enough code to post their contents into C++ world. --- Machines/ZX8081/ZX8081.cpp | 10 ++++++++++ Machines/ZX8081/ZX8081.hpp | 12 ++++++++++++ .../Mac/Clock Signal/Machine/Wrappers/CSZX8081.mm | 10 ++++++++++ Processors/Z80/Z80.hpp | 7 +++++-- ROMImages/ZX8081/readme.txt | 6 ++++++ 5 files changed, 43 insertions(+), 2 deletions(-) create mode 100644 ROMImages/ZX8081/readme.txt diff --git a/Machines/ZX8081/ZX8081.cpp b/Machines/ZX8081/ZX8081.cpp index ac9483e16..417823a55 100644 --- a/Machines/ZX8081/ZX8081.cpp +++ b/Machines/ZX8081/ZX8081.cpp @@ -28,6 +28,9 @@ void Machine::setup_output(float aspect_ratio) { "}"); } +void Machine::flush() { +} + void Machine::close_output() { } @@ -44,3 +47,10 @@ void Machine::run_for_cycles(int number_of_cycles) { void Machine::configure_as_target(const StaticAnalyser::Target &target) { } + +void Machine::set_rom(ROMType type, std::vector data) { + switch(type) { + case ZX80: zx80_rom_ = data; break; + case ZX81: zx81_rom_ = data; break; + } +} diff --git a/Machines/ZX8081/ZX8081.hpp b/Machines/ZX8081/ZX8081.hpp index c2ccc8df4..41c90ef31 100644 --- a/Machines/ZX8081/ZX8081.hpp +++ b/Machines/ZX8081/ZX8081.hpp @@ -14,8 +14,15 @@ #include "../../Processors/Z80/Z80.hpp" +#include +#include + namespace ZX8081 { +enum ROMType: uint8_t { + ZX80, ZX81 +}; + class Machine: public CPU::Z80::Processor, public CRTMachine::Machine, @@ -24,6 +31,7 @@ class Machine: Machine(); int perform_machine_cycle(const CPU::Z80::MachineCycle &cycle); + void flush(); void setup_output(float aspect_ratio); void close_output(); @@ -35,8 +43,12 @@ class Machine: void configure_as_target(const StaticAnalyser::Target &target); + void set_rom(ROMType type, std::vector data); + private: std::shared_ptr crt_; + std::vector zx81_rom_, zx80_rom_, rom_; + std::vector ram_; }; } diff --git a/OSBindings/Mac/Clock Signal/Machine/Wrappers/CSZX8081.mm b/OSBindings/Mac/Clock Signal/Machine/Wrappers/CSZX8081.mm index 32bb5703c..c5801d3ce 100644 --- a/OSBindings/Mac/Clock Signal/Machine/Wrappers/CSZX8081.mm +++ b/OSBindings/Mac/Clock Signal/Machine/Wrappers/CSZX8081.mm @@ -10,6 +10,10 @@ #include "ZX8081.hpp" +#import "CSMachine+Subclassing.h" +#import "NSData+StdVector.h" +#import "NSBundle+DataResource.h" + @implementation CSZX8081 { ZX8081::Machine zx8081; } @@ -21,10 +25,16 @@ - (instancetype)init { self = [super init]; if(self) { + zx8081.set_rom(ZX8081::ROMType::ZX80, [self rom:@"zx80"].stdVector8); + zx8081.set_rom(ZX8081::ROMType::ZX81, [self rom:@"zx81"].stdVector8); } return self; } +- (NSData *)rom:(NSString *)name { + return [[NSBundle mainBundle] dataForResource:name withExtension:@"rom" subdirectory:@"ROMImages/ZX8081"]; +} + #pragma mark - Keyboard Mapping - (void)clearAllKeys { diff --git a/Processors/Z80/Z80.hpp b/Processors/Z80/Z80.hpp index d901a361a..396258122 100644 --- a/Processors/Z80/Z80.hpp +++ b/Processors/Z80/Z80.hpp @@ -775,7 +775,10 @@ template class Processor { while(bus_request_line_) { static MachineCycle bus_acknowledge_cycle = {BusOperation::BusAcknowledge, 1}; number_of_cycles_ -= static_cast(this)->perform_machine_cycle(bus_acknowledge_cycle) + 1; - if(!number_of_cycles_) return; + if(!number_of_cycles_) { + flush(); + return; + } } while(!bus_request_line_) { @@ -790,7 +793,7 @@ template class Processor { switch(operation->type) { case MicroOp::BusOperation: - if(number_of_cycles_ < operation->machine_cycle.length) { scheduled_program_counter_--; return; } + if(number_of_cycles_ < operation->machine_cycle.length) { scheduled_program_counter_--; flush(); return; } number_of_cycles_ -= operation->machine_cycle.length; last_request_status_ = request_status_; number_of_cycles_ -= static_cast(this)->perform_machine_cycle(operation->machine_cycle); diff --git a/ROMImages/ZX8081/readme.txt b/ROMImages/ZX8081/readme.txt new file mode 100644 index 000000000..1813d6efa --- /dev/null +++ b/ROMImages/ZX8081/readme.txt @@ -0,0 +1,6 @@ +ROM files would ordinarily go here; the copyright status of these is uncertain so they have not been included in this repository. + +Expected files: + +zx80.rom +zx81.rom \ No newline at end of file From 096551ab3e9248658576a46544d83cd1b98c1fd7 Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Sun, 4 Jun 2017 18:32:23 -0400 Subject: [PATCH 07/77] Made a first attempt to hash out the ZX80's bus. Video output isn't yet going though. Can't seem to find clarity on whether horizontal sync is really programmatic. Let's see. --- Machines/ZX8081/ZX8081.cpp | 67 ++++++++++++++++++++++++++++++++++++-- Machines/ZX8081/ZX8081.hpp | 7 ++++ Processors/Z80/Z80.hpp | 6 ++-- 3 files changed, 76 insertions(+), 4 deletions(-) diff --git a/Machines/ZX8081/ZX8081.cpp b/Machines/ZX8081/ZX8081.cpp index 417823a55..f332b1eb1 100644 --- a/Machines/ZX8081/ZX8081.cpp +++ b/Machines/ZX8081/ZX8081.cpp @@ -10,17 +10,64 @@ using namespace ZX8081; -Machine::Machine() { +Machine::Machine() : + vertical_sync_(false), + ram_(65536) { // run at 3.25 Mhz set_clock_rate(3250000); } int Machine::perform_machine_cycle(const CPU::Z80::MachineCycle &cycle) { + cycles_since_display_update_ += cycle.length; + + uint8_t r; + switch(cycle.operation) { + case CPU::Z80::BusOperation::Output: + if(*cycle.address == 0xff) { + update_display(); + set_sync(false); + } + break; + + case CPU::Z80::BusOperation::Input: + if(*cycle.address == 0xfe) { + update_display(); + set_sync(true); + } + break; + + case CPU::Z80::BusOperation::Interrupt: + *cycle.value = 0xff; + break; + + case CPU::Z80::BusOperation::ReadOpcode: + r = (uint8_t)get_value_of_register(CPU::Z80::Register::R); + set_interrupt_line(!(r & 0x40)); + case CPU::Z80::BusOperation::Read: + if(*cycle.address < rom_.size()) *cycle.value = rom_[*cycle.address]; + else { + uint8_t value = ram_[*cycle.address]; + if(*cycle.address > 32768 && !(value & 0x40) && cycle.operation == CPU::Z80::BusOperation::ReadOpcode) { + // TODO: character lookup. + output_byte(value); + *cycle.value = 0; + } + else *cycle.value = value; + } + break; + + case CPU::Z80::BusOperation::Write: + ram_[*cycle.address] = *cycle.value; + break; + + default: break; + } + return 0; } void Machine::setup_output(float aspect_ratio) { - crt_.reset(new Outputs::CRT::CRT(207 * 8, 8, Outputs::CRT::DisplayType::PAL50, 1)); + crt_.reset(new Outputs::CRT::CRT(207, 1, Outputs::CRT::DisplayType::PAL50, 1)); crt_->set_rgb_sampling_function( "vec3 rgb_sample(usampler2D sampler, vec2 coordinate, vec2 icoordinate)" "{" @@ -29,6 +76,7 @@ void Machine::setup_output(float aspect_ratio) { } void Machine::flush() { + update_display(); } void Machine::close_output() { @@ -46,6 +94,8 @@ void Machine::run_for_cycles(int number_of_cycles) { } void Machine::configure_as_target(const StaticAnalyser::Target &target) { + // TODO: pay attention to the target + rom_ = zx80_rom_; } void Machine::set_rom(ROMType type, std::vector data) { @@ -54,3 +104,16 @@ void Machine::set_rom(ROMType type, std::vector data) { case ZX81: zx81_rom_ = data; break; } } + +#pragma mark - Video + +void Machine::update_display() { + // TODO. + cycles_since_display_update_ = 0; +} + +void Machine::set_sync(bool sync) { +} + +void Machine::output_byte(uint8_t byte) { +} diff --git a/Machines/ZX8081/ZX8081.hpp b/Machines/ZX8081/ZX8081.hpp index 41c90ef31..3edd4791c 100644 --- a/Machines/ZX8081/ZX8081.hpp +++ b/Machines/ZX8081/ZX8081.hpp @@ -49,6 +49,13 @@ class Machine: std::shared_ptr crt_; std::vector zx81_rom_, zx80_rom_, rom_; std::vector ram_; + + bool vertical_sync_; + + int cycles_since_display_update_; + void update_display(); + void set_sync(bool sync); + void output_byte(uint8_t byte); }; } diff --git a/Processors/Z80/Z80.hpp b/Processors/Z80/Z80.hpp index 396258122..a9e68c454 100644 --- a/Processors/Z80/Z80.hpp +++ b/Processors/Z80/Z80.hpp @@ -38,7 +38,7 @@ enum Register { IXh, IXl, IX, IYh, IYl, IY, - R, I, + R, I, Refresh, IFF1, IFF2, IM }; @@ -1652,10 +1652,11 @@ template class Processor { case Register::R: return r_; case Register::I: return i_; + case Register::Refresh: return (uint16_t)(r_ | (i_ << 8)); case Register::IFF1: return iff1_ ? 1 : 0; case Register::IFF2: return iff2_ ? 1 : 0; - case Register::IM: return interrupt_mode_; + case Register::IM: return (uint16_t)interrupt_mode_; default: return 0; } @@ -1710,6 +1711,7 @@ template class Processor { case Register::R: r_ = (uint8_t)value; break; case Register::I: i_ = (uint8_t)value; break; + case Register::Refresh: r_ = (uint8_t)value; i_ = (uint8_t)(value >> 8); break; case Register::IFF1: iff1_ = !!value; break; case Register::IFF2: iff2_ = !!value; break; From 73654d51dd20addc862c1bb0fd2235ab76caf4e5 Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Sun, 4 Jun 2017 18:37:13 -0400 Subject: [PATCH 08/77] Wired up actually to run. --- Machines/ZX8081/ZX8081.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/Machines/ZX8081/ZX8081.cpp b/Machines/ZX8081/ZX8081.cpp index f332b1eb1..a5fd50498 100644 --- a/Machines/ZX8081/ZX8081.cpp +++ b/Machines/ZX8081/ZX8081.cpp @@ -91,6 +91,7 @@ std::shared_ptr Machine::get_speaker() { } void Machine::run_for_cycles(int number_of_cycles) { + CPU::Z80::Processor::run_for_cycles(number_of_cycles); } void Machine::configure_as_target(const StaticAnalyser::Target &target) { From 7f743c6fb00fde2bfb5cb3050191b9aed90c8f3d Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Sun, 4 Jun 2017 18:40:59 -0400 Subject: [PATCH 09/77] Got explicit about permitted type conversions. --- Processors/Z80/Z80.hpp | 46 +++++++++++++++++++++--------------------- 1 file changed, 23 insertions(+), 23 deletions(-) diff --git a/Processors/Z80/Z80.hpp b/Processors/Z80/Z80.hpp index a9e68c454..a822e62a3 100644 --- a/Processors/Z80/Z80.hpp +++ b/Processors/Z80/Z80.hpp @@ -786,7 +786,7 @@ template class Processor { scheduled_program_counter_++; #define set_parity(v) \ - parity_overflow_result_ = v^1;\ + parity_overflow_result_ = (uint8_t)(v^1);\ parity_overflow_result_ ^= parity_overflow_result_ >> 4;\ parity_overflow_result_ ^= parity_overflow_result_ << 2;\ parity_overflow_result_ ^= parity_overflow_result_ >> 1; @@ -857,7 +857,7 @@ template class Processor { break; case MicroOp::CCF: - half_carry_result_ = carry_result_ << 4; + half_carry_result_ = (uint8_t)(carry_result_ << 4); carry_result_ ^= Flag::Carry; subtract_flag_ = 0; bit53_result_ = a_; @@ -887,9 +887,9 @@ template class Processor { #define set_arithmetic_flags(sub, b53) \ sign_result_ = zero_result_ = (uint8_t)result; \ - carry_result_ = result >> 8; \ - half_carry_result_ = half_result; \ - parity_overflow_result_ = overflow >> 5; \ + carry_result_ = (uint8_t)(result >> 8); \ + half_carry_result_ = (uint8_t)half_result; \ + parity_overflow_result_ = (uint8_t)(overflow >> 5); \ subtract_flag_ = sub; \ bit53_result_ = (uint8_t)b53; @@ -969,8 +969,8 @@ template class Processor { bit53_result_ = sign_result_ = zero_result_ = a_; parity_overflow_result_ = overflow ? Flag::Overflow : 0; subtract_flag_ = Flag::Subtract; - carry_result_ = result >> 8; - half_carry_result_ = halfResult; + carry_result_ = (uint8_t)(result >> 8); + half_carry_result_ = (uint8_t)halfResult; } break; case MicroOp::Increment8: { @@ -986,8 +986,8 @@ template class Processor { // sign, zero and 5 & 3 are set directly from the result bit53_result_ = sign_result_ = zero_result_ = (uint8_t)result; - half_carry_result_ = half_result; - parity_overflow_result_ = overflow >> 5; + half_carry_result_ = (uint8_t)half_result; + parity_overflow_result_ = (uint8_t)(overflow >> 5); subtract_flag_ = 0; } break; @@ -1004,8 +1004,8 @@ template class Processor { // sign, zero and 5 & 3 are set directly from the result bit53_result_ = sign_result_ = zero_result_ = (uint8_t)result; - half_carry_result_ = half_result; - parity_overflow_result_ = overflow >> 5; + half_carry_result_ = (uint8_t)half_result; + parity_overflow_result_ = (uint8_t)(overflow >> 5); subtract_flag_ = Flag::Subtract; } break; @@ -1074,8 +1074,8 @@ template class Processor { int halfResult = (sourceValue&0xfff) + (destinationValue&0xfff); bit53_result_ = (uint8_t)(result >> 8); - carry_result_ = result >> 16; - half_carry_result_ = (halfResult >> 8); + carry_result_ = (uint8_t)(result >> 16); + half_carry_result_ = (uint8_t)(halfResult >> 8); subtract_flag_ = 0; *(uint16_t *)operation->destination = (uint16_t)result; @@ -1094,9 +1094,9 @@ template class Processor { sign_result_ = (uint8_t)(result >> 8); zero_result_ = (uint8_t)(result | sign_result_); subtract_flag_ = 0; - carry_result_ = result >> 16; - half_carry_result_ = halfResult >> 8; - parity_overflow_result_ = overflow >> 13; + carry_result_ = (uint8_t)(result >> 16); + half_carry_result_ = (uint8_t)(halfResult >> 8); + parity_overflow_result_ = (uint8_t)(overflow >> 13); *(uint16_t *)operation->destination = (uint16_t)result; } break; @@ -1117,9 +1117,9 @@ template class Processor { sign_result_ = (uint8_t)(result >> 8); zero_result_ = (uint8_t)(result | sign_result_); subtract_flag_ = Flag::Subtract; - carry_result_ = result >> 16; - half_carry_result_ = halfResult >> 8; - parity_overflow_result_ = overflow >> 13; + carry_result_ = (uint8_t)(result >> 16); + half_carry_result_ = (uint8_t)(halfResult >> 8); + parity_overflow_result_ = (uint8_t)(overflow >> 13); *(uint16_t *)operation->destination = (uint16_t)result; } break; @@ -1176,7 +1176,7 @@ template class Processor { de_.full += dir; \ hl_.full += dir; \ uint8_t sum = a_ + temp8_; \ - bit53_result_ = (sum&0x8) | ((sum & 0x02) << 4); \ + bit53_result_ = (uint8_t)((sum&0x8) | ((sum & 0x02) << 4)); \ subtract_flag_ = 0; \ half_carry_result_ = 0; \ parity_overflow_result_ = bc_.full ? Flag::Parity : 0; @@ -1441,7 +1441,7 @@ template class Processor { memptr_.full = hl_.full + 1; uint8_t low_nibble = a_ & 0xf; a_ = (a_ & 0xf0) | (temp8_ & 0xf); - temp8_ = (temp8_ >> 4) | (low_nibble << 4); + temp8_ = (uint8_t)((temp8_ >> 4) | (low_nibble << 4)); set_decimal_rotate_flags(); } break; @@ -1449,7 +1449,7 @@ template class Processor { memptr_.full = hl_.full + 1; uint8_t low_nibble = a_ & 0xf; a_ = (a_ & 0xf0) | (temp8_ >> 4); - temp8_ = (temp8_ << 4) | low_nibble; + temp8_ = (uint8_t)((temp8_ << 4) | low_nibble); set_decimal_rotate_flags(); } break; @@ -1544,7 +1544,7 @@ template class Processor { break; case MicroOp::CalculateIndexAddress: - memptr_.full = *(uint16_t *)operation->source + (int8_t)temp8_; + memptr_.full = (uint16_t)(*(uint16_t *)operation->source + (int8_t)temp8_); break; case MicroOp::IndexedPlaceHolder: From 7e3a46c33efe0a2781bf4a8756ce49a41a46d84e Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Sun, 4 Jun 2017 21:54:55 -0400 Subject: [PATCH 10/77] [Re]discovered that sync may also be a product of the interrupt cycle. So started looking into that. --- Machines/ZX8081/ZX8081.cpp | 27 +++++++++++++++++++-------- Machines/ZX8081/ZX8081.hpp | 5 +++-- 2 files changed, 22 insertions(+), 10 deletions(-) diff --git a/Machines/ZX8081/ZX8081.cpp b/Machines/ZX8081/ZX8081.cpp index a5fd50498..ab6cd09cf 100644 --- a/Machines/ZX8081/ZX8081.cpp +++ b/Machines/ZX8081/ZX8081.cpp @@ -11,7 +11,8 @@ using namespace ZX8081; Machine::Machine() : - vertical_sync_(false), + vsync_(false), + hsync_(false), ram_(65536) { // run at 3.25 Mhz set_clock_rate(3250000); @@ -23,24 +24,26 @@ int Machine::perform_machine_cycle(const CPU::Z80::MachineCycle &cycle) { uint8_t r; switch(cycle.operation) { case CPU::Z80::BusOperation::Output: - if(*cycle.address == 0xff) { - update_display(); - set_sync(false); + if((*cycle.address&0xff) == 0xff) { + set_vsync(false); } break; case CPU::Z80::BusOperation::Input: - if(*cycle.address == 0xfe) { - update_display(); - set_sync(true); + if((*cycle.address&0xff) == 0xfe) { + set_vsync(true); } + *cycle.value = 0xff; break; case CPU::Z80::BusOperation::Interrupt: + set_hsync(true); *cycle.value = 0xff; break; case CPU::Z80::BusOperation::ReadOpcode: + set_hsync(false); +// printf("%04x\n", *cycle.address); r = (uint8_t)get_value_of_register(CPU::Z80::Register::R); set_interrupt_line(!(r & 0x40)); case CPU::Z80::BusOperation::Read: @@ -113,8 +116,16 @@ void Machine::update_display() { cycles_since_display_update_ = 0; } -void Machine::set_sync(bool sync) { +void Machine::set_vsync(bool sync) { + if(sync && !vsync_) printf("\n---\n"); + vsync_ = sync; +} + +void Machine::set_hsync(bool sync) { + if(sync && !hsync_) printf("\n"); + hsync_ = sync; } void Machine::output_byte(uint8_t byte) { + printf("%02x ", byte); } diff --git a/Machines/ZX8081/ZX8081.hpp b/Machines/ZX8081/ZX8081.hpp index 3edd4791c..2cb3da838 100644 --- a/Machines/ZX8081/ZX8081.hpp +++ b/Machines/ZX8081/ZX8081.hpp @@ -50,11 +50,12 @@ class Machine: std::vector zx81_rom_, zx80_rom_, rom_; std::vector ram_; - bool vertical_sync_; + bool vsync_, hsync_; int cycles_since_display_update_; void update_display(); - void set_sync(bool sync); + void set_vsync(bool sync); + void set_hsync(bool sync); void output_byte(uint8_t byte); }; From e940e02126fc83e172de3e896d03d4bcf13b79e9 Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Mon, 5 Jun 2017 09:37:19 -0400 Subject: [PATCH 11/77] Added a short circuit to set_interrupt_line, mostly to make breakpoints slightly more convenient to place. --- Processors/Z80/Z80.hpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Processors/Z80/Z80.hpp b/Processors/Z80/Z80.hpp index a822e62a3..3cb8db6cf 100644 --- a/Processors/Z80/Z80.hpp +++ b/Processors/Z80/Z80.hpp @@ -1732,6 +1732,8 @@ template class Processor { Sets the logical value of the interrupt line. */ void set_interrupt_line(bool value) { + if(irq_line_ == value) return; + // IRQ requests are level triggered and masked. irq_line_ = value; if(irq_line_ && iff1_) { From 893f61b490caa26ca77ede224b9deb6139b7762d Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Mon, 5 Jun 2017 09:38:49 -0400 Subject: [PATCH 12/77] Attempted specifically to reproduce the 1kb ZX80 memory map in the hope of getting compact lines and in case mirroring is why I'm getting completely empty video reads. Still no action. --- Machines/ZX8081/ZX8081.cpp | 28 ++++++++++++++++++---------- 1 file changed, 18 insertions(+), 10 deletions(-) diff --git a/Machines/ZX8081/ZX8081.cpp b/Machines/ZX8081/ZX8081.cpp index ab6cd09cf..579788b72 100644 --- a/Machines/ZX8081/ZX8081.cpp +++ b/Machines/ZX8081/ZX8081.cpp @@ -8,6 +8,8 @@ #include "ZX8081.hpp" +//static int logging_delay = 3250000 * 10; + using namespace ZX8081; Machine::Machine() : @@ -20,8 +22,10 @@ Machine::Machine() : int Machine::perform_machine_cycle(const CPU::Z80::MachineCycle &cycle) { cycles_since_display_update_ += cycle.length; +// logging_delay -= cycle.length; uint8_t r; + uint16_t address = cycle.address ? *cycle.address : 0; switch(cycle.operation) { case CPU::Z80::BusOperation::Output: if((*cycle.address&0xff) == 0xff) { @@ -43,24 +47,26 @@ int Machine::perform_machine_cycle(const CPU::Z80::MachineCycle &cycle) { case CPU::Z80::BusOperation::ReadOpcode: set_hsync(false); -// printf("%04x\n", *cycle.address); r = (uint8_t)get_value_of_register(CPU::Z80::Register::R); set_interrupt_line(!(r & 0x40)); case CPU::Z80::BusOperation::Read: - if(*cycle.address < rom_.size()) *cycle.value = rom_[*cycle.address]; - else { - uint8_t value = ram_[*cycle.address]; - if(*cycle.address > 32768 && !(value & 0x40) && cycle.operation == CPU::Z80::BusOperation::ReadOpcode) { + if((address & 0xc000) == 0x0000) *cycle.value = rom_[address & (rom_.size() - 1)]; + else if((address & 0x4000) == 0x4000) { + uint8_t value = ram_[address & 1023]; + if(address&0x8000 && !(value & 0x40) && cycle.operation == CPU::Z80::BusOperation::ReadOpcode && !get_halt_line()) { // TODO: character lookup. +// if(logging_delay < 0) printf("%02x ", value); + if(value) printf("!"); output_byte(value); *cycle.value = 0; } - else *cycle.value = value; + else + *cycle.value = value; } break; case CPU::Z80::BusOperation::Write: - ram_[*cycle.address] = *cycle.value; + if((address & 0x4000) == 0x4000) ram_[address & 1023] = *cycle.value; break; default: break; @@ -117,15 +123,17 @@ void Machine::update_display() { } void Machine::set_vsync(bool sync) { - if(sync && !vsync_) printf("\n---\n"); + if(sync == vsync_) return; +// if(logging_delay < 0) if(!sync) printf("\n---\n"); vsync_ = sync; } void Machine::set_hsync(bool sync) { - if(sync && !hsync_) printf("\n"); + if(sync == hsync_) return; +// if(logging_delay < 0) if(sync) printf("\n"); hsync_ = sync; } void Machine::output_byte(uint8_t byte) { - printf("%02x ", byte); +// printf("%02x ", byte); } From 3df6eba237f41344f26a90837c6f21e4bd6c1516 Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Mon, 5 Jun 2017 10:35:03 -0400 Subject: [PATCH 13/77] Fixed: my HALT line wasn't actually halting. NOPs followed, but the PC just kept counting. --- Processors/Z80/Z80.hpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Processors/Z80/Z80.hpp b/Processors/Z80/Z80.hpp index 3cb8db6cf..53cff7f37 100644 --- a/Processors/Z80/Z80.hpp +++ b/Processors/Z80/Z80.hpp @@ -803,7 +803,7 @@ template class Processor { break; case MicroOp::DecodeOperation: r_ = (r_ & 0x80) | ((r_ + current_instruction_page_->r_step) & 0x7f); - pc_.full += pc_increment_; + pc_.full += pc_increment_ & (uint16_t)halt_mask_; case MicroOp::DecodeOperationNoRChange: scheduled_program_counter_ = current_instruction_page_->instructions[operation_ & halt_mask_]; break; From 23ca00fd9a8c2520f5a6cbf989e8f3ecaadf522e Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Mon, 5 Jun 2017 10:36:07 -0400 Subject: [PATCH 14/77] Added memory fuzzing as a way to verify state being written by the Z80. Eventually discovered the HALT problem as fixed in the last commit, so have stripped away the caveman stuff again. --- Machines/MemoryFuzzer.cpp | 4 ++++ Machines/MemoryFuzzer.hpp | 2 ++ Machines/ZX8081/ZX8081.cpp | 19 ++++++------------- 3 files changed, 12 insertions(+), 13 deletions(-) diff --git a/Machines/MemoryFuzzer.cpp b/Machines/MemoryFuzzer.cpp index 60dd8e388..467d27045 100644 --- a/Machines/MemoryFuzzer.cpp +++ b/Machines/MemoryFuzzer.cpp @@ -22,3 +22,7 @@ void Memory::Fuzz(uint8_t *buffer, size_t size) { buffer[c] = (uint8_t)(std::rand() >> shift); } } + +void Memory::Fuzz(std::vector &buffer) { + Fuzz(buffer.data(), buffer.size()); +} diff --git a/Machines/MemoryFuzzer.hpp b/Machines/MemoryFuzzer.hpp index 648915033..3de6bdcbd 100644 --- a/Machines/MemoryFuzzer.hpp +++ b/Machines/MemoryFuzzer.hpp @@ -11,10 +11,12 @@ #include #include +#include namespace Memory { void Fuzz(uint8_t *buffer, size_t size); +void Fuzz(std::vector &buffer); } diff --git a/Machines/ZX8081/ZX8081.cpp b/Machines/ZX8081/ZX8081.cpp index 579788b72..06053bace 100644 --- a/Machines/ZX8081/ZX8081.cpp +++ b/Machines/ZX8081/ZX8081.cpp @@ -8,33 +8,33 @@ #include "ZX8081.hpp" -//static int logging_delay = 3250000 * 10; +#include "../MemoryFuzzer.hpp" using namespace ZX8081; Machine::Machine() : vsync_(false), hsync_(false), - ram_(65536) { + ram_(1024) { // run at 3.25 Mhz set_clock_rate(3250000); + Memory::Fuzz(ram_); } int Machine::perform_machine_cycle(const CPU::Z80::MachineCycle &cycle) { cycles_since_display_update_ += cycle.length; -// logging_delay -= cycle.length; uint8_t r; uint16_t address = cycle.address ? *cycle.address : 0; switch(cycle.operation) { case CPU::Z80::BusOperation::Output: - if((*cycle.address&0xff) == 0xff) { + if((address&7) == 7) { set_vsync(false); } break; case CPU::Z80::BusOperation::Input: - if((*cycle.address&0xff) == 0xfe) { + if((address&7) == 6) { set_vsync(true); } *cycle.value = 0xff; @@ -55,13 +55,9 @@ int Machine::perform_machine_cycle(const CPU::Z80::MachineCycle &cycle) { uint8_t value = ram_[address & 1023]; if(address&0x8000 && !(value & 0x40) && cycle.operation == CPU::Z80::BusOperation::ReadOpcode && !get_halt_line()) { // TODO: character lookup. -// if(logging_delay < 0) printf("%02x ", value); - if(value) printf("!"); output_byte(value); *cycle.value = 0; - } - else - *cycle.value = value; + } else *cycle.value = value; } break; @@ -124,16 +120,13 @@ void Machine::update_display() { void Machine::set_vsync(bool sync) { if(sync == vsync_) return; -// if(logging_delay < 0) if(!sync) printf("\n---\n"); vsync_ = sync; } void Machine::set_hsync(bool sync) { if(sync == hsync_) return; -// if(logging_delay < 0) if(sync) printf("\n"); hsync_ = sync; } void Machine::output_byte(uint8_t byte) { -// printf("%02x ", byte); } From 4983718df75aa22adddca6034daf10ebddbb8c91 Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Mon, 5 Jun 2017 10:47:42 -0400 Subject: [PATCH 15/77] Got to outputting something to the CRT. Should be just proper syncs and a paper background. It's not synchronising properly, so something is amiss in my timing. --- Machines/ZX8081/ZX8081.cpp | 31 ++++++++++++++++++++++++++++--- Machines/ZX8081/ZX8081.hpp | 6 +++++- 2 files changed, 33 insertions(+), 4 deletions(-) diff --git a/Machines/ZX8081/ZX8081.cpp b/Machines/ZX8081/ZX8081.cpp index 06053bace..dd8e75fee 100644 --- a/Machines/ZX8081/ZX8081.cpp +++ b/Machines/ZX8081/ZX8081.cpp @@ -15,7 +15,8 @@ using namespace ZX8081; Machine::Machine() : vsync_(false), hsync_(false), - ram_(1024) { + ram_(1024), + line_data_(nullptr) { // run at 3.25 Mhz set_clock_rate(3250000); Memory::Fuzz(ram_); @@ -114,19 +115,43 @@ void Machine::set_rom(ROMType type, std::vector data) { #pragma mark - Video void Machine::update_display() { - // TODO. - cycles_since_display_update_ = 0; +// cycles_since_display_update_ = 0; } void Machine::set_vsync(bool sync) { if(sync == vsync_) return; vsync_ = sync; + update_sync(); } void Machine::set_hsync(bool sync) { if(sync == hsync_) return; hsync_ = sync; + update_sync(); +} + +void Machine::update_sync() { + bool is_sync = hsync_ || vsync_; + if(is_sync == is_sync_) return; + + if(is_sync_) { + crt_->output_sync(cycles_since_display_update_); + } else { + output_level(cycles_since_display_update_); + } + cycles_since_display_update_ = 0; + is_sync_ = is_sync; +} + +void Machine::output_level(unsigned int number_of_cycles) { + uint8_t *colour_pointer = (uint8_t *)crt_->allocate_write_area(1); + if(colour_pointer) *colour_pointer = 0; + crt_->output_level(number_of_cycles); } void Machine::output_byte(uint8_t byte) { + if(!line_data_) { + } +// printf("%d\n", cycles_since_display_update_); +// cycles_since_display_update_ = 0; } diff --git a/Machines/ZX8081/ZX8081.hpp b/Machines/ZX8081/ZX8081.hpp index 2cb3da838..a1464a714 100644 --- a/Machines/ZX8081/ZX8081.hpp +++ b/Machines/ZX8081/ZX8081.hpp @@ -51,11 +51,15 @@ class Machine: std::vector ram_; bool vsync_, hsync_; + bool is_sync_; + uint8_t *line_data_; - int cycles_since_display_update_; + unsigned int cycles_since_display_update_; void update_display(); void set_vsync(bool sync); void set_hsync(bool sync); + void update_sync(); + void output_level(unsigned int number_of_cycles); void output_byte(uint8_t byte); }; From 2fbc7a286952b9abdfd5849a8f770c28a8ddadd5 Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Mon, 5 Jun 2017 23:32:49 -0400 Subject: [PATCH 16/77] Made a very basic attempt at getting something that at least demarcates proper graphics output. --- Machines/ZX8081/ZX8081.cpp | 38 +++++++++++++++++++++++++++++++------- Machines/ZX8081/ZX8081.hpp | 3 ++- 2 files changed, 33 insertions(+), 8 deletions(-) diff --git a/Machines/ZX8081/ZX8081.cpp b/Machines/ZX8081/ZX8081.cpp index dd8e75fee..93761e761 100644 --- a/Machines/ZX8081/ZX8081.cpp +++ b/Machines/ZX8081/ZX8081.cpp @@ -73,11 +73,11 @@ int Machine::perform_machine_cycle(const CPU::Z80::MachineCycle &cycle) { } void Machine::setup_output(float aspect_ratio) { - crt_.reset(new Outputs::CRT::CRT(207, 1, Outputs::CRT::DisplayType::PAL50, 1)); + crt_.reset(new Outputs::CRT::CRT(210, 1, Outputs::CRT::DisplayType::PAL50, 1)); crt_->set_rgb_sampling_function( "vec3 rgb_sample(usampler2D sampler, vec2 coordinate, vec2 icoordinate)" "{" - "return vec3(1.0);" + "return vec3(float(texture(texID, coordinate).r) / 255.0);" "}"); } @@ -134,6 +134,10 @@ void Machine::update_sync() { bool is_sync = hsync_ || vsync_; if(is_sync == is_sync_) return; + if(line_data_) { + output_data(); + } + if(is_sync_) { crt_->output_sync(cycles_since_display_update_); } else { @@ -149,9 +153,29 @@ void Machine::output_level(unsigned int number_of_cycles) { crt_->output_level(number_of_cycles); } -void Machine::output_byte(uint8_t byte) { - if(!line_data_) { - } -// printf("%d\n", cycles_since_display_update_); -// cycles_since_display_update_ = 0; +void Machine::output_data() { + unsigned int data_length = (unsigned int)(line_data_pointer_ - line_data_) * 4; + crt_->output_data(data_length, 4); + line_data_pointer_ = line_data_ = nullptr; + cycles_since_display_update_ -= data_length; +} + +void Machine::output_byte(uint8_t byte) { + if(line_data_) { + if(cycles_since_display_update_ > 4) { + output_data(); + } + } + + if(!line_data_) { + line_data_pointer_ = line_data_ = crt_->allocate_write_area(320); + } + + if(line_data_) { + line_data_pointer_[0] = byte; + line_data_pointer_[1] = byte; + line_data_pointer_[2] = byte; + line_data_pointer_[3] = byte; + line_data_pointer_ += 4; + } } diff --git a/Machines/ZX8081/ZX8081.hpp b/Machines/ZX8081/ZX8081.hpp index a1464a714..da5580e8c 100644 --- a/Machines/ZX8081/ZX8081.hpp +++ b/Machines/ZX8081/ZX8081.hpp @@ -52,7 +52,7 @@ class Machine: bool vsync_, hsync_; bool is_sync_; - uint8_t *line_data_; + uint8_t *line_data_, *line_data_pointer_; unsigned int cycles_since_display_update_; void update_display(); @@ -61,6 +61,7 @@ class Machine: void update_sync(); void output_level(unsigned int number_of_cycles); void output_byte(uint8_t byte); + void output_data(); }; } From 97f3ff03b6392ec680c1bbdfbd7b46aa86a58aa7 Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Mon, 5 Jun 2017 23:50:04 -0400 Subject: [PATCH 17/77] Restored white background and attempted to correct output timing deficiencies. Incomplete success. --- Machines/ZX8081/ZX8081.cpp | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/Machines/ZX8081/ZX8081.cpp b/Machines/ZX8081/ZX8081.cpp index 93761e761..e395d3217 100644 --- a/Machines/ZX8081/ZX8081.cpp +++ b/Machines/ZX8081/ZX8081.cpp @@ -149,13 +149,13 @@ void Machine::update_sync() { void Machine::output_level(unsigned int number_of_cycles) { uint8_t *colour_pointer = (uint8_t *)crt_->allocate_write_area(1); - if(colour_pointer) *colour_pointer = 0; + if(colour_pointer) *colour_pointer = 0xff; crt_->output_level(number_of_cycles); } void Machine::output_data() { - unsigned int data_length = (unsigned int)(line_data_pointer_ - line_data_) * 4; - crt_->output_data(data_length, 4); + unsigned int data_length = (unsigned int)(line_data_pointer_ - line_data_); + crt_->output_data(data_length, 1); line_data_pointer_ = line_data_ = nullptr; cycles_since_display_update_ -= data_length; } @@ -165,6 +165,8 @@ void Machine::output_byte(uint8_t byte) { if(cycles_since_display_update_ > 4) { output_data(); } + } else { + output_level(cycles_since_display_update_); } if(!line_data_) { From ef4b2f963dd2ffc7e8d48ca5f273428c14e93dcf Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Mon, 5 Jun 2017 23:52:56 -0400 Subject: [PATCH 18/77] Probably more-or-less corrected. But this is all a bit too interdependent. --- Machines/ZX8081/ZX8081.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/Machines/ZX8081/ZX8081.cpp b/Machines/ZX8081/ZX8081.cpp index e395d3217..55bbe1141 100644 --- a/Machines/ZX8081/ZX8081.cpp +++ b/Machines/ZX8081/ZX8081.cpp @@ -167,6 +167,7 @@ void Machine::output_byte(uint8_t byte) { } } else { output_level(cycles_since_display_update_); + cycles_since_display_update_ = 0; } if(!line_data_) { From 6f7037b2b1f8febdbb68a1674369133b8a72486b Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Tue, 6 Jun 2017 08:55:07 -0400 Subject: [PATCH 19/77] Made an initial stab at outputting half the correct pixels. --- Machines/ZX8081/ZX8081.cpp | 27 ++++++++++++++++++--------- Machines/ZX8081/ZX8081.hpp | 1 + 2 files changed, 19 insertions(+), 9 deletions(-) diff --git a/Machines/ZX8081/ZX8081.cpp b/Machines/ZX8081/ZX8081.cpp index 55bbe1141..ae0466188 100644 --- a/Machines/ZX8081/ZX8081.cpp +++ b/Machines/ZX8081/ZX8081.cpp @@ -23,9 +23,9 @@ Machine::Machine() : } int Machine::perform_machine_cycle(const CPU::Z80::MachineCycle &cycle) { - cycles_since_display_update_ += cycle.length; + cycles_since_display_update_ += (unsigned int)cycle.length; - uint8_t r; + uint16_t r = 0; uint16_t address = cycle.address ? *cycle.address : 0; switch(cycle.operation) { case CPU::Z80::BusOperation::Output: @@ -37,24 +37,32 @@ int Machine::perform_machine_cycle(const CPU::Z80::MachineCycle &cycle) { case CPU::Z80::BusOperation::Input: if((address&7) == 6) { set_vsync(true); + line_counter_ = 0; } *cycle.value = 0xff; break; case CPU::Z80::BusOperation::Interrupt: set_hsync(true); + line_counter_ = (line_counter_ + 1) & 7; *cycle.value = 0xff; break; case CPU::Z80::BusOperation::ReadOpcode: set_hsync(false); - r = (uint8_t)get_value_of_register(CPU::Z80::Register::R); + r = (uint8_t)get_value_of_register(CPU::Z80::Register::Refresh); set_interrupt_line(!(r & 0x40)); case CPU::Z80::BusOperation::Read: if((address & 0xc000) == 0x0000) *cycle.value = rom_[address & (rom_.size() - 1)]; else if((address & 0x4000) == 0x4000) { uint8_t value = ram_[address & 1023]; if(address&0x8000 && !(value & 0x40) && cycle.operation == CPU::Z80::BusOperation::ReadOpcode && !get_halt_line()) { + size_t char_address = (size_t)((r & 0xff00) | ((value & 0x3f) << 3) | line_counter_); + if((char_address & 0xc000) == 0x0000) { + uint8_t mask = (value & 0x80) ? 0xff : 0x00; + value = rom_[char_address & (rom_.size() - 1)] ^ mask; + } + // TODO: character lookup. output_byte(value); *cycle.value = 0; @@ -154,8 +162,8 @@ void Machine::output_level(unsigned int number_of_cycles) { } void Machine::output_data() { - unsigned int data_length = (unsigned int)(line_data_pointer_ - line_data_); - crt_->output_data(data_length, 1); + unsigned int data_length = ((unsigned int)(line_data_pointer_ - line_data_)) ; + crt_->output_data(data_length * 1, 1); line_data_pointer_ = line_data_ = nullptr; cycles_since_display_update_ -= data_length; } @@ -175,10 +183,11 @@ void Machine::output_byte(uint8_t byte) { } if(line_data_) { - line_data_pointer_[0] = byte; - line_data_pointer_[1] = byte; - line_data_pointer_[2] = byte; - line_data_pointer_[3] = byte; + uint8_t mask = 0x80; + for(int c = 0; c < 4; c++) { + line_data_pointer_[c] = (byte & mask) ? 0xff : 0x00; + mask >>= 1; + } line_data_pointer_ += 4; } } diff --git a/Machines/ZX8081/ZX8081.hpp b/Machines/ZX8081/ZX8081.hpp index da5580e8c..44d9f58e1 100644 --- a/Machines/ZX8081/ZX8081.hpp +++ b/Machines/ZX8081/ZX8081.hpp @@ -53,6 +53,7 @@ class Machine: bool vsync_, hsync_; bool is_sync_; uint8_t *line_data_, *line_data_pointer_; + int line_counter_; unsigned int cycles_since_display_update_; void update_display(); From cba07dec7e0d7972d20dd8f8a65aba12d2c18380 Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Tue, 6 Jun 2017 08:59:00 -0400 Subject: [PATCH 20/77] Doubled up to display all eight pixels. To confirm that they are the wrong pixels. --- Machines/ZX8081/ZX8081.cpp | 38 +++++++++++++++++++++----------------- 1 file changed, 21 insertions(+), 17 deletions(-) diff --git a/Machines/ZX8081/ZX8081.cpp b/Machines/ZX8081/ZX8081.cpp index ae0466188..56d1d451a 100644 --- a/Machines/ZX8081/ZX8081.cpp +++ b/Machines/ZX8081/ZX8081.cpp @@ -80,15 +80,6 @@ int Machine::perform_machine_cycle(const CPU::Z80::MachineCycle &cycle) { return 0; } -void Machine::setup_output(float aspect_ratio) { - crt_.reset(new Outputs::CRT::CRT(210, 1, Outputs::CRT::DisplayType::PAL50, 1)); - crt_->set_rgb_sampling_function( - "vec3 rgb_sample(usampler2D sampler, vec2 coordinate, vec2 icoordinate)" - "{" - "return vec3(float(texture(texID, coordinate).r) / 255.0);" - "}"); -} - void Machine::flush() { update_display(); } @@ -147,9 +138,9 @@ void Machine::update_sync() { } if(is_sync_) { - crt_->output_sync(cycles_since_display_update_); + crt_->output_sync(cycles_since_display_update_ << 1); } else { - output_level(cycles_since_display_update_); + output_level(cycles_since_display_update_ << 1); } cycles_since_display_update_ = 0; is_sync_ = is_sync; @@ -162,10 +153,10 @@ void Machine::output_level(unsigned int number_of_cycles) { } void Machine::output_data() { - unsigned int data_length = ((unsigned int)(line_data_pointer_ - line_data_)) ; - crt_->output_data(data_length * 1, 1); + unsigned int data_length = (unsigned int)(line_data_pointer_ - line_data_); + crt_->output_data(data_length, 1); line_data_pointer_ = line_data_ = nullptr; - cycles_since_display_update_ -= data_length; + cycles_since_display_update_ -= data_length >> 1; } void Machine::output_byte(uint8_t byte) { @@ -174,7 +165,7 @@ void Machine::output_byte(uint8_t byte) { output_data(); } } else { - output_level(cycles_since_display_update_); + output_level(cycles_since_display_update_ << 1); cycles_since_display_update_ = 0; } @@ -184,10 +175,23 @@ void Machine::output_byte(uint8_t byte) { if(line_data_) { uint8_t mask = 0x80; - for(int c = 0; c < 4; c++) { + for(int c = 0; c < 8; c++) { line_data_pointer_[c] = (byte & mask) ? 0xff : 0x00; mask >>= 1; } - line_data_pointer_ += 4; + line_data_pointer_ += 8; + + if(line_data_pointer_ - line_data_ == 320) { + output_data(); + } } } + +void Machine::setup_output(float aspect_ratio) { + crt_.reset(new Outputs::CRT::CRT(210 * 2, 1, Outputs::CRT::DisplayType::PAL50, 1)); + crt_->set_rgb_sampling_function( + "vec3 rgb_sample(usampler2D sampler, vec2 coordinate, vec2 icoordinate)" + "{" + "return vec3(float(texture(texID, coordinate).r) / 255.0);" + "}"); +} From ebbf6e6133f350a9db9d774379232376f8547f54 Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Tue, 6 Jun 2017 09:03:09 -0400 Subject: [PATCH 21/77] Surprisingly, I think this may actually be the correct output: stopped throwing away the I part of the refresh register and flipped black and white. --- Machines/ZX8081/ZX8081.cpp | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/Machines/ZX8081/ZX8081.cpp b/Machines/ZX8081/ZX8081.cpp index 56d1d451a..9b3b89882 100644 --- a/Machines/ZX8081/ZX8081.cpp +++ b/Machines/ZX8081/ZX8081.cpp @@ -25,7 +25,7 @@ Machine::Machine() : int Machine::perform_machine_cycle(const CPU::Z80::MachineCycle &cycle) { cycles_since_display_update_ += (unsigned int)cycle.length; - uint16_t r = 0; + uint16_t refresh = 0; uint16_t address = cycle.address ? *cycle.address : 0; switch(cycle.operation) { case CPU::Z80::BusOperation::Output: @@ -50,16 +50,16 @@ int Machine::perform_machine_cycle(const CPU::Z80::MachineCycle &cycle) { case CPU::Z80::BusOperation::ReadOpcode: set_hsync(false); - r = (uint8_t)get_value_of_register(CPU::Z80::Register::Refresh); - set_interrupt_line(!(r & 0x40)); + refresh = get_value_of_register(CPU::Z80::Register::Refresh); + set_interrupt_line(!(refresh & 0x40)); case CPU::Z80::BusOperation::Read: if((address & 0xc000) == 0x0000) *cycle.value = rom_[address & (rom_.size() - 1)]; else if((address & 0x4000) == 0x4000) { uint8_t value = ram_[address & 1023]; if(address&0x8000 && !(value & 0x40) && cycle.operation == CPU::Z80::BusOperation::ReadOpcode && !get_halt_line()) { - size_t char_address = (size_t)((r & 0xff00) | ((value & 0x3f) << 3) | line_counter_); + size_t char_address = (size_t)((refresh & 0xff00) | ((value & 0x3f) << 3) | line_counter_); if((char_address & 0xc000) == 0x0000) { - uint8_t mask = (value & 0x80) ? 0xff : 0x00; + uint8_t mask = (value & 0x80) ? 0x00 : 0xff; value = rom_[char_address & (rom_.size() - 1)] ^ mask; } From cc4cb45e9df3ca9693281a4727baa2f68c4353da Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Tue, 6 Jun 2017 09:25:18 -0400 Subject: [PATCH 22/77] Implemented keyboard input and ensured that the signal generated is marked as composite, putting the colour-suppression ball into the CRT's court. --- Machines/ZX8081/ZX8081.cpp | 35 ++++++++-- Machines/ZX8081/ZX8081.hpp | 15 +++++ .../Clock Signal/Machine/Wrappers/CSZX8081.mm | 64 +++++++++++++++++-- 3 files changed, 103 insertions(+), 11 deletions(-) diff --git a/Machines/ZX8081/ZX8081.cpp b/Machines/ZX8081/ZX8081.cpp index 9b3b89882..f08733ce0 100644 --- a/Machines/ZX8081/ZX8081.cpp +++ b/Machines/ZX8081/ZX8081.cpp @@ -16,7 +16,8 @@ Machine::Machine() : vsync_(false), hsync_(false), ram_(1024), - line_data_(nullptr) { + line_data_(nullptr), + key_states_{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff} { // run at 3.25 Mhz set_clock_rate(3250000); Memory::Fuzz(ram_); @@ -34,13 +35,20 @@ int Machine::perform_machine_cycle(const CPU::Z80::MachineCycle &cycle) { } break; - case CPU::Z80::BusOperation::Input: + case CPU::Z80::BusOperation::Input: { + uint8_t value = 0xff; if((address&7) == 6) { set_vsync(true); line_counter_ = 0; + + uint16_t mask = 0x100; + for(int c = 0; c < 8; c++) { + if(!(address & mask)) value &= key_states_[c]; + mask <<= 1; + } } - *cycle.value = 0xff; - break; + *cycle.value = value; + } break; case CPU::Z80::BusOperation::Interrupt: set_hsync(true); @@ -189,9 +197,22 @@ void Machine::output_byte(uint8_t byte) { void Machine::setup_output(float aspect_ratio) { crt_.reset(new Outputs::CRT::CRT(210 * 2, 1, Outputs::CRT::DisplayType::PAL50, 1)); - crt_->set_rgb_sampling_function( - "vec3 rgb_sample(usampler2D sampler, vec2 coordinate, vec2 icoordinate)" + crt_->set_composite_sampling_function( + "float composite_sample(usampler2D sampler, vec2 coordinate, vec2 icoordinate, float phase, float amplitude)" "{" - "return vec3(float(texture(texID, coordinate).r) / 255.0);" + "return float(texture(texID, coordinate).r) / 255.0;" "}"); } + +#pragma mark - Keyboard + +void Machine::set_key_state(uint16_t key, bool isPressed) { + if(isPressed) + key_states_[key >> 8] &= (uint8_t)(~key); + else + key_states_[key >> 8] |= (uint8_t)key; +} + +void Machine::clear_all_keys() { + memset(key_states_, 0xff, 8); +} diff --git a/Machines/ZX8081/ZX8081.hpp b/Machines/ZX8081/ZX8081.hpp index 44d9f58e1..2a873ba6f 100644 --- a/Machines/ZX8081/ZX8081.hpp +++ b/Machines/ZX8081/ZX8081.hpp @@ -23,6 +23,17 @@ enum ROMType: uint8_t { ZX80, ZX81 }; +enum Key: uint16_t { + KeyShift = 0x0000 | 0x01, KeyZ = 0x0000 | 0x02, KeyX = 0x0000 | 0x04, KeyC = 0x0000 | 0x08, KeyV = 0x0000 | 0x10, + KeyA = 0x0100 | 0x01, KeyS = 0x0100 | 0x02, KeyD = 0x0100 | 0x04, KeyF = 0x0100 | 0x08, KeyG = 0x0100 | 0x10, + KeyQ = 0x0200 | 0x01, KeyW = 0x0200 | 0x02, KeyE = 0x0200 | 0x04, KeyR = 0x0200 | 0x08, KeyT = 0x0200 | 0x10, + Key1 = 0x0300 | 0x01, Key2 = 0x0300 | 0x02, Key3 = 0x0300 | 0x04, Key4 = 0x0300 | 0x08, Key5 = 0x0300 | 0x10, + Key0 = 0x0400 | 0x01, Key9 = 0x0400 | 0x02, Key8 = 0x0400 | 0x04, Key7 = 0x0400 | 0x08, Key6 = 0x0400 | 0x10, + KeyP = 0x0500 | 0x01, KeyO = 0x0500 | 0x02, KeyI = 0x0500 | 0x04, KeyU = 0x0500 | 0x08, KeyY = 0x0500 | 0x10, + KeyEnter = 0x0600 | 0x01, KeyL = 0x0600 | 0x02, KeyK = 0x0600 | 0x04, KeyJ = 0x0600 | 0x08, KeyH = 0x0600 | 0x10, + KeySpace = 0x0700 | 0x01, KeyDot = 0x0700 | 0x02, KeyM = 0x0700 | 0x04, KeyN = 0x0700 | 0x08, KeyB = 0x0700 | 0x10, +}; + class Machine: public CPU::Z80::Processor, public CRTMachine::Machine, @@ -44,6 +55,8 @@ class Machine: void configure_as_target(const StaticAnalyser::Target &target); void set_rom(ROMType type, std::vector data); + void set_key_state(uint16_t key, bool isPressed); + void clear_all_keys(); private: std::shared_ptr crt_; @@ -55,6 +68,8 @@ class Machine: uint8_t *line_data_, *line_data_pointer_; int line_counter_; + uint8_t key_states_[8]; + unsigned int cycles_since_display_update_; void update_display(); void set_vsync(bool sync); diff --git a/OSBindings/Mac/Clock Signal/Machine/Wrappers/CSZX8081.mm b/OSBindings/Mac/Clock Signal/Machine/Wrappers/CSZX8081.mm index c5801d3ce..7a1248818 100644 --- a/OSBindings/Mac/Clock Signal/Machine/Wrappers/CSZX8081.mm +++ b/OSBindings/Mac/Clock Signal/Machine/Wrappers/CSZX8081.mm @@ -15,18 +15,18 @@ #import "NSBundle+DataResource.h" @implementation CSZX8081 { - ZX8081::Machine zx8081; + ZX8081::Machine _zx8081; } - (CRTMachine::Machine * const)machine { - return &zx8081; + return &_zx8081; } - (instancetype)init { self = [super init]; if(self) { - zx8081.set_rom(ZX8081::ROMType::ZX80, [self rom:@"zx80"].stdVector8); - zx8081.set_rom(ZX8081::ROMType::ZX81, [self rom:@"zx81"].stdVector8); + _zx8081.set_rom(ZX8081::ROMType::ZX80, [self rom:@"zx80"].stdVector8); + _zx8081.set_rom(ZX8081::ROMType::ZX81, [self rom:@"zx81"].stdVector8); } return self; } @@ -38,9 +38,65 @@ #pragma mark - Keyboard Mapping - (void)clearAllKeys { + @synchronized(self) { + _zx8081.clear_all_keys(); + } } - (void)setKey:(uint16_t)key isPressed:(BOOL)isPressed { + @synchronized(self) { + switch(key) + { + case VK_ANSI_0: _zx8081.set_key_state(ZX8081::Key::Key0, isPressed); break; + case VK_ANSI_1: _zx8081.set_key_state(ZX8081::Key::Key1, isPressed); break; + case VK_ANSI_2: _zx8081.set_key_state(ZX8081::Key::Key2, isPressed); break; + case VK_ANSI_3: _zx8081.set_key_state(ZX8081::Key::Key3, isPressed); break; + case VK_ANSI_4: _zx8081.set_key_state(ZX8081::Key::Key4, isPressed); break; + case VK_ANSI_5: _zx8081.set_key_state(ZX8081::Key::Key5, isPressed); break; + case VK_ANSI_6: _zx8081.set_key_state(ZX8081::Key::Key6, isPressed); break; + case VK_ANSI_7: _zx8081.set_key_state(ZX8081::Key::Key7, isPressed); break; + case VK_ANSI_8: _zx8081.set_key_state(ZX8081::Key::Key8, isPressed); break; + case VK_ANSI_9: _zx8081.set_key_state(ZX8081::Key::Key9, isPressed); break; + + case VK_ANSI_Q: _zx8081.set_key_state(ZX8081::Key::KeyQ, isPressed); break; + case VK_ANSI_W: _zx8081.set_key_state(ZX8081::Key::KeyW, isPressed); break; + case VK_ANSI_E: _zx8081.set_key_state(ZX8081::Key::KeyE, isPressed); break; + case VK_ANSI_R: _zx8081.set_key_state(ZX8081::Key::KeyR, isPressed); break; + case VK_ANSI_T: _zx8081.set_key_state(ZX8081::Key::KeyT, isPressed); break; + case VK_ANSI_Y: _zx8081.set_key_state(ZX8081::Key::KeyY, isPressed); break; + case VK_ANSI_U: _zx8081.set_key_state(ZX8081::Key::KeyU, isPressed); break; + case VK_ANSI_I: _zx8081.set_key_state(ZX8081::Key::KeyI, isPressed); break; + case VK_ANSI_O: _zx8081.set_key_state(ZX8081::Key::KeyO, isPressed); break; + case VK_ANSI_P: _zx8081.set_key_state(ZX8081::Key::KeyP, isPressed); break; + + case VK_ANSI_A: _zx8081.set_key_state(ZX8081::Key::KeyA, isPressed); break; + case VK_ANSI_S: _zx8081.set_key_state(ZX8081::Key::KeyS, isPressed); break; + case VK_ANSI_D: _zx8081.set_key_state(ZX8081::Key::KeyD, isPressed); break; + case VK_ANSI_F: _zx8081.set_key_state(ZX8081::Key::KeyF, isPressed); break; + case VK_ANSI_G: _zx8081.set_key_state(ZX8081::Key::KeyG, isPressed); break; + case VK_ANSI_H: _zx8081.set_key_state(ZX8081::Key::KeyH, isPressed); break; + case VK_ANSI_J: _zx8081.set_key_state(ZX8081::Key::KeyJ, isPressed); break; + case VK_ANSI_K: _zx8081.set_key_state(ZX8081::Key::KeyK, isPressed); break; + case VK_ANSI_L: _zx8081.set_key_state(ZX8081::Key::KeyL, isPressed); break; + + case VK_ANSI_Z: _zx8081.set_key_state(ZX8081::Key::KeyZ, isPressed); break; + case VK_ANSI_X: _zx8081.set_key_state(ZX8081::Key::KeyX, isPressed); break; + case VK_ANSI_C: _zx8081.set_key_state(ZX8081::Key::KeyC, isPressed); break; + case VK_ANSI_V: _zx8081.set_key_state(ZX8081::Key::KeyV, isPressed); break; + case VK_ANSI_B: _zx8081.set_key_state(ZX8081::Key::KeyB, isPressed); break; + case VK_ANSI_N: _zx8081.set_key_state(ZX8081::Key::KeyN, isPressed); break; + case VK_ANSI_M: _zx8081.set_key_state(ZX8081::Key::KeyM, isPressed); break; + + case VK_Shift: + case VK_RightShift: + _zx8081.set_key_state(ZX8081::Key::KeyShift, isPressed); break; + break; + + case VK_ANSI_Period:_zx8081.set_key_state(ZX8081::Key::KeyDot, isPressed); break; + case VK_Return: _zx8081.set_key_state(ZX8081::Key::KeyEnter, isPressed); break; + case VK_Space: _zx8081.set_key_state(ZX8081::Key::KeySpace, isPressed); break; + } + } } - (NSString *)userDefaultsPrefix { return @"zx8081"; } From ca9e8aecd663feec59749c67e08db6e1f56db8b7 Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Tue, 6 Jun 2017 10:13:32 -0400 Subject: [PATCH 23/77] Made a seemingly unsuccessful attempt to add tape input. --- Machines/ZX8081/ZX8081.cpp | 25 +++++++++++++++++++------ Machines/ZX8081/ZX8081.hpp | 4 ++++ 2 files changed, 23 insertions(+), 6 deletions(-) diff --git a/Machines/ZX8081/ZX8081.cpp b/Machines/ZX8081/ZX8081.cpp index f08733ce0..e2d1ca4c1 100644 --- a/Machines/ZX8081/ZX8081.cpp +++ b/Machines/ZX8081/ZX8081.cpp @@ -12,40 +12,49 @@ using namespace ZX8081; +namespace { + const unsigned int ZX8081ClockRate = 3250000; +} + Machine::Machine() : vsync_(false), hsync_(false), ram_(1024), line_data_(nullptr), - key_states_{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff} { + key_states_{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff}, + tape_player_(ZX8081ClockRate) { // run at 3.25 Mhz - set_clock_rate(3250000); + set_clock_rate(ZX8081ClockRate); Memory::Fuzz(ram_); + tape_player_.set_motor_control(true); } int Machine::perform_machine_cycle(const CPU::Z80::MachineCycle &cycle) { cycles_since_display_update_ += (unsigned int)cycle.length; + tape_player_.run_for_cycles(cycle.length); uint16_t refresh = 0; uint16_t address = cycle.address ? *cycle.address : 0; switch(cycle.operation) { case CPU::Z80::BusOperation::Output: - if((address&7) == 7) { +// if((address&7) == 7) { set_vsync(false); - } + line_counter_ = 0; +// } break; case CPU::Z80::BusOperation::Input: { uint8_t value = 0xff; - if((address&7) == 6) { + if(!(address&1)) { set_vsync(true); - line_counter_ = 0; uint16_t mask = 0x100; for(int c = 0; c < 8; c++) { if(!(address & mask)) value &= key_states_[c]; mask <<= 1; } + + mask &= ~(tape_player_.get_input() ? 0x80 : 0); } *cycle.value = value; } break; @@ -110,6 +119,10 @@ void Machine::run_for_cycles(int number_of_cycles) { void Machine::configure_as_target(const StaticAnalyser::Target &target) { // TODO: pay attention to the target rom_ = zx80_rom_; + + if(target.tapes.size()) { + tape_player_.set_tape(target.tapes.front()); + } } void Machine::set_rom(ROMType type, std::vector data) { diff --git a/Machines/ZX8081/ZX8081.hpp b/Machines/ZX8081/ZX8081.hpp index 2a873ba6f..4aa755a0b 100644 --- a/Machines/ZX8081/ZX8081.hpp +++ b/Machines/ZX8081/ZX8081.hpp @@ -13,6 +13,7 @@ #include "../CRTMachine.hpp" #include "../../Processors/Z80/Z80.hpp" +#include "../../Storage/Tape/Tape.hpp" #include #include @@ -78,6 +79,9 @@ class Machine: void output_level(unsigned int number_of_cycles); void output_byte(uint8_t byte); void output_data(); + + Storage::Tape::BinaryTapePlayer tape_player_; + bool tape_level_; }; } From b55579c34882bfb46604eb4b82bf657b04929240 Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Tue, 6 Jun 2017 17:52:44 -0400 Subject: [PATCH 24/77] Fixed usage of `flush`: the subclass version is definitively used. --- Processors/Z80/Z80.hpp | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/Processors/Z80/Z80.hpp b/Processors/Z80/Z80.hpp index 53cff7f37..5f0467140 100644 --- a/Processors/Z80/Z80.hpp +++ b/Processors/Z80/Z80.hpp @@ -776,7 +776,7 @@ template class Processor { static MachineCycle bus_acknowledge_cycle = {BusOperation::BusAcknowledge, 1}; number_of_cycles_ -= static_cast(this)->perform_machine_cycle(bus_acknowledge_cycle) + 1; if(!number_of_cycles_) { - flush(); + static_cast(this)->flush(); return; } } @@ -793,7 +793,11 @@ template class Processor { switch(operation->type) { case MicroOp::BusOperation: - if(number_of_cycles_ < operation->machine_cycle.length) { scheduled_program_counter_--; flush(); return; } + if(number_of_cycles_ < operation->machine_cycle.length) { + scheduled_program_counter_--; + static_cast(this)->flush(); + return; + } number_of_cycles_ -= operation->machine_cycle.length; last_request_status_ = request_status_; number_of_cycles_ -= static_cast(this)->perform_machine_cycle(operation->machine_cycle); From 8c66e1d99de6f94afa1ddd9686a437bdd22a2038 Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Tue, 6 Jun 2017 17:53:23 -0400 Subject: [PATCH 25/77] Factored out ZX80/81 video and rejigged to ensure it will keep ticking over irrespective of whether the machine is supplying data. --- Machines/ZX8081/Video.cpp | 84 ++++++++++++++++++ Machines/ZX8081/Video.hpp | 36 ++++++++ Machines/ZX8081/ZX8081.cpp | 86 +++---------------- Machines/ZX8081/ZX8081.hpp | 11 +-- .../Clock Signal.xcodeproj/project.pbxproj | 6 ++ 5 files changed, 139 insertions(+), 84 deletions(-) create mode 100644 Machines/ZX8081/Video.cpp create mode 100644 Machines/ZX8081/Video.hpp diff --git a/Machines/ZX8081/Video.cpp b/Machines/ZX8081/Video.cpp new file mode 100644 index 000000000..a35a44bf5 --- /dev/null +++ b/Machines/ZX8081/Video.cpp @@ -0,0 +1,84 @@ +// +// Video.cpp +// Clock Signal +// +// Created by Thomas Harte on 06/06/2017. +// Copyright © 2017 Thomas Harte. All rights reserved. +// + +#include "Video.hpp" + +using namespace ZX8081; + +Video::Video() : + crt_(new Outputs::CRT::CRT(210 * 2, 1, Outputs::CRT::DisplayType::PAL50, 1)), + line_data_(nullptr), + line_data_pointer_(nullptr), + cycles_since_update_(0), + sync_(false) { + + crt_->set_composite_sampling_function( + "float composite_sample(usampler2D sampler, vec2 coordinate, vec2 icoordinate, float phase, float amplitude)" + "{" + "return float(texture(texID, coordinate).r) / 255.0;" + "}"); +} + +void Video::run_for_cycles(int number_of_cycles) { + cycles_since_update_ += (unsigned int)number_of_cycles << 1; +} + +void Video::flush() { + if(sync_) { + crt_->output_sync(cycles_since_update_); + } else { + if(line_data_) { + unsigned int data_length = (unsigned int)(line_data_pointer_ - line_data_); + if(data_length < cycles_since_update_) { + crt_->output_data(data_length, 1); + line_data_pointer_ = line_data_ = nullptr; + cycles_since_update_ -= data_length; + } else return; + } + + uint8_t *colour_pointer = (uint8_t *)crt_->allocate_write_area(1); + if(colour_pointer) *colour_pointer = 0xff; + crt_->output_level(cycles_since_update_); + } + + cycles_since_update_ = 0; +} + +void Video::set_sync(bool sync) { + if(sync_ == sync) return; + flush(); + sync_ = sync; + if(cycles_since_update_) flush(); +} + +void Video::output_byte(uint8_t byte) { + flush(); + + if(!line_data_) { + line_data_pointer_ = line_data_ = crt_->allocate_write_area(320); + } + + if(line_data_) { + uint8_t mask = 0x80; + for(int c = 0; c < 8; c++) { + line_data_pointer_[c] = (byte & mask) ? 0xff : 0x00; + mask >>= 1; + } + line_data_pointer_ += 8; + + if(line_data_pointer_ - line_data_ == 320) { + crt_->output_data(320, 1); + line_data_pointer_ = line_data_ = nullptr; + cycles_since_update_ -= 160; + } + } +} + +std::shared_ptr Video::get_crt() { + return crt_; +} diff --git a/Machines/ZX8081/Video.hpp b/Machines/ZX8081/Video.hpp new file mode 100644 index 000000000..dc3a4d699 --- /dev/null +++ b/Machines/ZX8081/Video.hpp @@ -0,0 +1,36 @@ +// +// Video.hpp +// Clock Signal +// +// Created by Thomas Harte on 06/06/2017. +// Copyright © 2017 Thomas Harte. All rights reserved. +// + +#ifndef Machines_ZX8081_Video_hpp +#define Machines_ZX8081_Video_hpp + +#include "../../Outputs/CRT/CRT.hpp" + +namespace ZX8081 { + +class Video { + public: + Video(); + + std::shared_ptr get_crt(); + void run_for_cycles(int number_of_cycles); + void flush(); + + void set_sync(bool sync); + void output_byte(uint8_t byte); + + private: + bool sync_; + uint8_t *line_data_, *line_data_pointer_; + unsigned int cycles_since_update_; + std::shared_ptr crt_; +}; + +} + +#endif /* Video_hpp */ diff --git a/Machines/ZX8081/ZX8081.cpp b/Machines/ZX8081/ZX8081.cpp index e2d1ca4c1..9bdee989e 100644 --- a/Machines/ZX8081/ZX8081.cpp +++ b/Machines/ZX8081/ZX8081.cpp @@ -20,7 +20,6 @@ Machine::Machine() : vsync_(false), hsync_(false), ram_(1024), - line_data_(nullptr), key_states_{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff}, tape_player_(ZX8081ClockRate) { // run at 3.25 Mhz @@ -30,7 +29,7 @@ Machine::Machine() : } int Machine::perform_machine_cycle(const CPU::Z80::MachineCycle &cycle) { - cycles_since_display_update_ += (unsigned int)cycle.length; + video_->run_for_cycles(cycle.length); tape_player_.run_for_cycles(cycle.length); uint16_t refresh = 0; @@ -80,8 +79,7 @@ int Machine::perform_machine_cycle(const CPU::Z80::MachineCycle &cycle) { value = rom_[char_address & (rom_.size() - 1)] ^ mask; } - // TODO: character lookup. - output_byte(value); + video_->output_byte(value); *cycle.value = 0; } else *cycle.value = value; } @@ -98,14 +96,19 @@ int Machine::perform_machine_cycle(const CPU::Z80::MachineCycle &cycle) { } void Machine::flush() { - update_display(); + video_->flush(); +} + +void Machine::setup_output(float aspect_ratio) { + video_.reset(new Video); } void Machine::close_output() { + video_.reset(); } std::shared_ptr Machine::get_crt() { - return crt_; + return video_->get_crt(); } std::shared_ptr Machine::get_speaker() { @@ -134,87 +137,18 @@ void Machine::set_rom(ROMType type, std::vector data) { #pragma mark - Video -void Machine::update_display() { -// cycles_since_display_update_ = 0; -} - void Machine::set_vsync(bool sync) { - if(sync == vsync_) return; vsync_ = sync; update_sync(); } void Machine::set_hsync(bool sync) { - if(sync == hsync_) return; hsync_ = sync; update_sync(); } void Machine::update_sync() { - bool is_sync = hsync_ || vsync_; - if(is_sync == is_sync_) return; - - if(line_data_) { - output_data(); - } - - if(is_sync_) { - crt_->output_sync(cycles_since_display_update_ << 1); - } else { - output_level(cycles_since_display_update_ << 1); - } - cycles_since_display_update_ = 0; - is_sync_ = is_sync; -} - -void Machine::output_level(unsigned int number_of_cycles) { - uint8_t *colour_pointer = (uint8_t *)crt_->allocate_write_area(1); - if(colour_pointer) *colour_pointer = 0xff; - crt_->output_level(number_of_cycles); -} - -void Machine::output_data() { - unsigned int data_length = (unsigned int)(line_data_pointer_ - line_data_); - crt_->output_data(data_length, 1); - line_data_pointer_ = line_data_ = nullptr; - cycles_since_display_update_ -= data_length >> 1; -} - -void Machine::output_byte(uint8_t byte) { - if(line_data_) { - if(cycles_since_display_update_ > 4) { - output_data(); - } - } else { - output_level(cycles_since_display_update_ << 1); - cycles_since_display_update_ = 0; - } - - if(!line_data_) { - line_data_pointer_ = line_data_ = crt_->allocate_write_area(320); - } - - if(line_data_) { - uint8_t mask = 0x80; - for(int c = 0; c < 8; c++) { - line_data_pointer_[c] = (byte & mask) ? 0xff : 0x00; - mask >>= 1; - } - line_data_pointer_ += 8; - - if(line_data_pointer_ - line_data_ == 320) { - output_data(); - } - } -} - -void Machine::setup_output(float aspect_ratio) { - crt_.reset(new Outputs::CRT::CRT(210 * 2, 1, Outputs::CRT::DisplayType::PAL50, 1)); - crt_->set_composite_sampling_function( - "float composite_sample(usampler2D sampler, vec2 coordinate, vec2 icoordinate, float phase, float amplitude)" - "{" - "return float(texture(texID, coordinate).r) / 255.0;" - "}"); + video_->set_sync(vsync_ || hsync_); } #pragma mark - Keyboard diff --git a/Machines/ZX8081/ZX8081.hpp b/Machines/ZX8081/ZX8081.hpp index 4aa755a0b..d58e12999 100644 --- a/Machines/ZX8081/ZX8081.hpp +++ b/Machines/ZX8081/ZX8081.hpp @@ -15,6 +15,8 @@ #include "../../Processors/Z80/Z80.hpp" #include "../../Storage/Tape/Tape.hpp" +#include "Video.hpp" + #include #include @@ -60,25 +62,18 @@ class Machine: void clear_all_keys(); private: - std::shared_ptr crt_; + std::shared_ptr + + CFBundleTypeExtensions + + p + 81 + + CFBundleTypeIconFile + cassette + CFBundleTypeName + ZX81 Tape Image + CFBundleTypeRole + Viewer + NSDocumentClass + MachineDocument + CFBundleExecutable $(EXECUTABLE_NAME) From a48616a138f52cd17faca906aa7e6a7aa6702472 Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Mon, 12 Jun 2017 18:51:11 -0400 Subject: [PATCH 67/77] Fixed reference to Swift-world MachineDocument for the ZX81 file type. --- OSBindings/Mac/Clock Signal/Info.plist | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/OSBindings/Mac/Clock Signal/Info.plist b/OSBindings/Mac/Clock Signal/Info.plist index 20b8c8b80..42a1aec16 100644 --- a/OSBindings/Mac/Clock Signal/Info.plist +++ b/OSBindings/Mac/Clock Signal/Info.plist @@ -186,7 +186,7 @@ CFBundleTypeRole Viewer NSDocumentClass - MachineDocument + $(PRODUCT_MODULE_NAME).MachineDocument CFBundleExecutable From b9dbb6bcf87dc6535d2ab423ffcfdf5dac40936c Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Mon, 12 Jun 2017 18:55:04 -0400 Subject: [PATCH 68/77] Discovered my timing error: the I/R <-> A loads should take an extra cycle. This means the ZX80 now finally takes the correct 207 cycles per line. Fixed the video output wave to be clocked at the appropriate rate. --- Machines/ZX8081/Video.cpp | 2 +- Processors/Z80/Z80.hpp | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/Machines/ZX8081/Video.cpp b/Machines/ZX8081/Video.cpp index 51e352bd5..5102cdffd 100644 --- a/Machines/ZX8081/Video.cpp +++ b/Machines/ZX8081/Video.cpp @@ -11,7 +11,7 @@ using namespace ZX8081; Video::Video() : - crt_(new Outputs::CRT::CRT(206 * 2, 1, Outputs::CRT::DisplayType::PAL50, 1)), + crt_(new Outputs::CRT::CRT(207 * 2, 1, Outputs::CRT::DisplayType::PAL50, 1)), line_data_(nullptr), line_data_pointer_(nullptr), cycles_since_update_(0), diff --git a/Processors/Z80/Z80.hpp b/Processors/Z80/Z80.hpp index 244ca1807..04cdbc65a 100644 --- a/Processors/Z80/Z80.hpp +++ b/Processors/Z80/Z80.hpp @@ -385,19 +385,19 @@ template class Processor { /* 0x40 IN B, (C); 0x41 OUT (C), B */ IN_OUT(bc_.bytes.high), /* 0x42 SBC HL, BC */ SBC16(hl_, bc_), /* 0x43 LD (nn), BC */ Program(FETCH16(temp16_, pc_), STORE16L(bc_, temp16_)), /* 0x44 NEG */ Program({MicroOp::NEG}), /* 0x45 RETN */ Program(POP(pc_), {MicroOp::RETN}), - /* 0x46 IM 0 */ Program({MicroOp::IM}), /* 0x47 LD I, A */ LD(i_, a_), + /* 0x46 IM 0 */ Program({MicroOp::IM}), /* 0x47 LD I, A */ Program(WAIT(1), {MicroOp::Move8, &a_, &i_}), /* 0x40 IN B, (C); 0x41 OUT (C), B */ IN_OUT(bc_.bytes.low), /* 0x4a ADC HL, BC */ ADC16(hl_, bc_), /* 0x4b LD BC, (nn) */ Program(FETCH16(temp16_, pc_), FETCH16L(bc_, temp16_)), /* 0x4c NEG */ Program({MicroOp::NEG}), /* 0x4d RETI */ Program(POP(pc_), {MicroOp::RETN}), - /* 0x4e IM 0/1 */ Program({MicroOp::IM}), /* 0x4f LD R, A */ LD(r_, a_), + /* 0x4e IM 0/1 */ Program({MicroOp::IM}), /* 0x4f LD R, A */ Program(WAIT(1), {MicroOp::Move8, &a_, &r_}), /* 0x40 IN B, (C); 0x41 OUT (C), B */ IN_OUT(de_.bytes.high), /* 0x52 SBC HL, DE */ SBC16(hl_, de_), /* 0x53 LD (nn), DE */ Program(FETCH16(temp16_, pc_), STORE16L(de_, temp16_)), /* 0x54 NEG */ Program({MicroOp::NEG}), /* 0x55 RETN */ Program(POP(pc_), {MicroOp::RETN}), - /* 0x56 IM 1 */ Program({MicroOp::IM}), /* 0x57 LD A, I */ Program({MicroOp::Move8, &i_, &a_}, {MicroOp::SetAFlags}), + /* 0x56 IM 1 */ Program({MicroOp::IM}), /* 0x57 LD A, I */ Program(WAIT(1), {MicroOp::Move8, &i_, &a_}, {MicroOp::SetAFlags}), /* 0x40 IN B, (C); 0x41 OUT (C), B */ IN_OUT(de_.bytes.low), /* 0x5a ADC HL, DE */ ADC16(hl_, de_), /* 0x5b LD DE, (nn) */ Program(FETCH16(temp16_, pc_), FETCH16L(de_, temp16_)), /* 0x5c NEG */ Program({MicroOp::NEG}), /* 0x5d RETN */ Program(POP(pc_), {MicroOp::RETN}), - /* 0x5e IM 2 */ Program({MicroOp::IM}), /* 0x5f LD A, R */ Program({MicroOp::Move8, &r_, &a_}, {MicroOp::SetAFlags}), + /* 0x5e IM 2 */ Program({MicroOp::IM}), /* 0x5f LD A, R */ Program(WAIT(1), {MicroOp::Move8, &r_, &a_}, {MicroOp::SetAFlags}), /* 0x40 IN B, (C); 0x41 OUT (C), B */ IN_OUT(hl_.bytes.high), /* 0x62 SBC HL, HL */ SBC16(hl_, hl_), /* 0x63 LD (nn), HL */ Program(FETCH16(temp16_, pc_), STORE16L(hl_, temp16_)), /* 0x64 NEG */ Program({MicroOp::NEG}), /* 0x65 RETN */ Program(POP(pc_), {MicroOp::RETN}), From 22de4815577a41785487beeb722dc09367fae4c6 Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Mon, 12 Jun 2017 19:41:59 -0400 Subject: [PATCH 69/77] Made an attempt to get .p/.80 checked and as far as the emulated machine. --- Storage/Data/ZX8081.cpp | 44 +++++++++++++++++++++++++++++-- Storage/Tape/Formats/ZX80O81P.cpp | 17 +++++++----- 2 files changed, 53 insertions(+), 8 deletions(-) diff --git a/Storage/Data/ZX8081.cpp b/Storage/Data/ZX8081.cpp index 0bf6049d7..7df811cc6 100644 --- a/Storage/Data/ZX8081.cpp +++ b/Storage/Data/ZX8081.cpp @@ -45,8 +45,48 @@ static std::shared_ptr ZX80FileFromData(const std::vector &data) return file; } +static std::shared_ptr ZX81FileFromData(const std::vector &data) { + // Does this look like a ZX81 file? + + // Look for a file name. + size_t data_pointer = 0; + int c = 11; + while(c--) { + if(data[data_pointer] & 0x80) break; + data_pointer++; + } + if(!c) return nullptr; + data_pointer++; + + if(data.size() < data_pointer + 0x405e - 0x4009) return nullptr; + + if(data[data_pointer]) return nullptr; + + uint16_t vars = short_at(data_pointer + 0x4010 - 0x4009, data); + uint16_t end_of_file = short_at(data_pointer + 0x4014 - 0x4009, data); +// uint16_t display_address = short_at(0x400c - 0x4009, data); + + // check that the end of file is contained within the supplied data + if(data_pointer + end_of_file - 0x4009 > data.size()) return nullptr; + + // check for the proper ordering of buffers + if(vars > end_of_file) return nullptr; +// if(end_of_file > display_address) return nullptr; + + // TODO: does it make sense to inspect the tokenised BASIC? + // It starts at 0x4028 and proceeds as [16-bit line number] [tokens] [0x76], + // but I'm as yet unable to find documentation of the tokens. + + // TODO: check that the line numbers declared above exist (?) + + std::shared_ptr file(new File); + file->data = data; + file->isZX81 = true; + return file; +} + std::shared_ptr Storage::Data::ZX8081::FileFromData(const std::vector &data) { std::shared_ptr result = ZX80FileFromData(data); - - return result; + if(result) return result; + return ZX81FileFromData(data); } diff --git a/Storage/Tape/Formats/ZX80O81P.cpp b/Storage/Tape/Formats/ZX80O81P.cpp index 83fa6273f..53b64222b 100644 --- a/Storage/Tape/Formats/ZX80O81P.cpp +++ b/Storage/Tape/Formats/ZX80O81P.cpp @@ -14,14 +14,19 @@ using namespace Storage::Tape; ZX80O81P::ZX80O81P(const char *file_name) : Storage::FileHolder(file_name) { - // Check that contents look like a ZX80 file - std::vector whole_file((size_t)file_stats_.st_size); - fread(whole_file.data(), 1, (size_t)file_stats_.st_size, file_); - std::shared_ptr<::Storage::Data::ZX8081::File> file = Storage::Data::ZX8081::FileFromData(whole_file); + // Grab the actual file contents + data_.resize((size_t)file_stats_.st_size); + fread(data_.data(), 1, (size_t)file_stats_.st_size, file_); - if(!file || file->isZX81) throw ErrorNotZX80O81P; + // If it's a ZX81 file, prepend a file name. + char type = (char)tolower(file_name[strlen(file_name) - 1]); + if(type == 'p' || type == '1') { + // TODO, maybe: prefix a proper file name; this is leaving the file nameless. + data_.insert(data_.begin(), 0x80); + } - data_ = file->data; + std::shared_ptr<::Storage::Data::ZX8081::File> file = Storage::Data::ZX8081::FileFromData(data_); + if(!file) throw ErrorNotZX80O81P; // then rewind and start again virtual_reset(); From 626737b9fa7845ae2c62007ea79af1704c245aab Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Mon, 12 Jun 2017 21:32:36 -0400 Subject: [PATCH 70/77] Started mucking about with some string conversion routines. Not finished yet. --- Storage/Data/ZX8081.cpp | 34 ++++++++++++++++++++++++++++++++++ Storage/Data/ZX8081.hpp | 4 ++++ 2 files changed, 38 insertions(+) diff --git a/Storage/Data/ZX8081.cpp b/Storage/Data/ZX8081.cpp index 7df811cc6..81d647e48 100644 --- a/Storage/Data/ZX8081.cpp +++ b/Storage/Data/ZX8081.cpp @@ -90,3 +90,37 @@ std::shared_ptr Storage::Data::ZX8081::FileFromData(const std::vector &data, bool is_zx81) { + std::wstring string; + + wchar_t zx80_map[] = { + ' ', u'\u2598', u'\u259d', u'\u2580', u'\u2596', u'\u258c', u'\u259e', u'\u259b', u'\u2588', u'\u2584', u'\u2580', '"', u'£', '$', ':', '?', + '(', ')', '>', '<', '=', '+', '-', '*', '/', ';', ',', '.', '0', '1', '2', '3', + '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', + 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z' + }; + wchar_t zx81_map[] = { + ' ', u'\u2598', u'\u259d', u'\u2580', u'\u2596', u'\u258c', u'\u259e', u'\u259b', u'\u2588', u'\u2584', u'\u2580', u'£', '$', ':', '?', + '(', ')', '-', '+', '*', '/', '=', '>', '<', ';', ',', '.', '0', '1', '2', '3', + '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', + 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z' + }; + wchar_t *map = is_zx81 ? zx81_map : zx80_map; + + for(uint8_t byte : data) { + string.push_back(map[byte & 0x3f]); + } + + return string; +} + +std::vector DataFromString(const std::wstring &string, bool is_zx81) { + std::vector data; + + // TODO + + return data; +} diff --git a/Storage/Data/ZX8081.hpp b/Storage/Data/ZX8081.hpp index 5003e710a..148479556 100644 --- a/Storage/Data/ZX8081.hpp +++ b/Storage/Data/ZX8081.hpp @@ -25,6 +25,10 @@ struct File { std::shared_ptr FileFromData(const std::vector &data); +std::wstring StringFromData(const std::vector &data, bool is_zx81); +std::vector DataFromString(const std::wstring &string, bool is_zx81); + + } } } From 8b09b4180bc8d5aa51ea2da41283f606c3a3d645 Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Mon, 12 Jun 2017 21:33:16 -0400 Subject: [PATCH 71/77] This now at least remembers whether it is meant to be a ZX81 and has storage for a horizontal counter. --- Machines/ZX8081/ZX8081.cpp | 5 ++--- Machines/ZX8081/ZX8081.hpp | 3 +++ 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/Machines/ZX8081/ZX8081.cpp b/Machines/ZX8081/ZX8081.cpp index 4fc60c433..550d21b69 100644 --- a/Machines/ZX8081/ZX8081.cpp +++ b/Machines/ZX8081/ZX8081.cpp @@ -142,9 +142,8 @@ void Machine::run_for_cycles(int number_of_cycles) { } void Machine::configure_as_target(const StaticAnalyser::Target &target) { - // TODO: pay attention to the target; it can't currently specify a ZX81 - // so assume a ZX80 if we got here. - if(target.zx8081.isZX81) { + is_zx81_ = target.zx8081.isZX81; + if(is_zx81_) { rom_ = zx81_rom_; tape_trap_address_ = 0x37c; tape_return_address_ = 0x380; diff --git a/Machines/ZX8081/ZX8081.hpp b/Machines/ZX8081/ZX8081.hpp index 9f94580ee..c921fd79a 100644 --- a/Machines/ZX8081/ZX8081.hpp +++ b/Machines/ZX8081/ZX8081.hpp @@ -84,6 +84,9 @@ class Machine: Storage::Tape::BinaryTapePlayer tape_player_; Storage::Tape::ZX8081::Parser parser_; + + int horizontal_counter_; + bool is_zx81_; }; } From e6e6e4e62ba9ee43a0543198544ec594581e7067 Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Mon, 12 Jun 2017 22:08:11 -0400 Subject: [PATCH 72/77] Adds an extra character for padding the ZX81 table. --- Storage/Data/ZX8081.cpp | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/Storage/Data/ZX8081.cpp b/Storage/Data/ZX8081.cpp index 81d647e48..76d54f77c 100644 --- a/Storage/Data/ZX8081.cpp +++ b/Storage/Data/ZX8081.cpp @@ -96,14 +96,15 @@ std::shared_ptr Storage::Data::ZX8081::FileFromData(const std::vector &data, bool is_zx81) { std::wstring string; - wchar_t zx80_map[] = { + wchar_t zx80_map[64] = { ' ', u'\u2598', u'\u259d', u'\u2580', u'\u2596', u'\u258c', u'\u259e', u'\u259b', u'\u2588', u'\u2584', u'\u2580', '"', u'£', '$', ':', '?', '(', ')', '>', '<', '=', '+', '-', '*', '/', ';', ',', '.', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z' }; - wchar_t zx81_map[] = { - ' ', u'\u2598', u'\u259d', u'\u2580', u'\u2596', u'\u258c', u'\u259e', u'\u259b', u'\u2588', u'\u2584', u'\u2580', u'£', '$', ':', '?', + // TODO: the block character conversions shown here are in the wrong order + wchar_t zx81_map[64] = { + ' ', u'\u2598', u'\u259d', u'\u2580', u'\u2596', u'\u258c', u'\u259e', u'\u259b', u'\u2588', u'\u2584', u'\u2580', '"', u'£', '$', ':', '?', '(', ')', '-', '+', '*', '/', '=', '>', '<', ';', ',', '.', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z' From aed2827e7b48305a491ffb38aa70712afba9a19c Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Mon, 12 Jun 2017 22:22:00 -0400 Subject: [PATCH 73/77] Implemented a rudimentary way to test that instructions take as long as the FUSE tests think they should. Hence discovered that the (HL)-accessing BIT, RES and SET weren't. Corrected. --- .../Mac/Clock SignalTests/Bridges/TestMachineZ80.h | 1 + .../Mac/Clock SignalTests/Bridges/TestMachineZ80.mm | 4 ++++ OSBindings/Mac/Clock SignalTests/FUSETests.swift | 4 ++++ Processors/Z80/Z80.hpp | 9 ++++++++- Processors/Z80/Z80AllRAM.cpp | 11 ++++++++++- Processors/Z80/Z80AllRAM.hpp | 2 ++ 6 files changed, 29 insertions(+), 2 deletions(-) diff --git a/OSBindings/Mac/Clock SignalTests/Bridges/TestMachineZ80.h b/OSBindings/Mac/Clock SignalTests/Bridges/TestMachineZ80.h index b935e51ce..cbb1a9c60 100644 --- a/OSBindings/Mac/Clock SignalTests/Bridges/TestMachineZ80.h +++ b/OSBindings/Mac/Clock SignalTests/Bridges/TestMachineZ80.h @@ -59,6 +59,7 @@ typedef NS_ENUM(NSInteger, CSTestMachineZ80Register) { @property(nonatomic, readonly, nonnull) NSArray *busOperationCaptures; @property(nonatomic, readonly) BOOL isHalted; +@property(nonatomic, readonly) int completedCycles; @property(nonatomic) BOOL nmiLine; @property(nonatomic) BOOL irqLine; diff --git a/OSBindings/Mac/Clock SignalTests/Bridges/TestMachineZ80.mm b/OSBindings/Mac/Clock SignalTests/Bridges/TestMachineZ80.mm index 0fac2def5..9284c6917 100644 --- a/OSBindings/Mac/Clock SignalTests/Bridges/TestMachineZ80.mm +++ b/OSBindings/Mac/Clock SignalTests/Bridges/TestMachineZ80.mm @@ -149,6 +149,10 @@ static CPU::Z80::Register registerForRegister(CSTestMachineZ80Register reg) { return _processor->get_halt_line() ? YES : NO; } +- (int)completedCycles { + return _processor->get_length_of_completed_machine_cycles(); +} + - (void)setNmiLine:(BOOL)nmiLine { _nmiLine = nmiLine; _processor->set_non_maskable_interrupt_line(nmiLine ? true : false); diff --git a/OSBindings/Mac/Clock SignalTests/FUSETests.swift b/OSBindings/Mac/Clock SignalTests/FUSETests.swift index 14b4290e3..3f2d0e26b 100644 --- a/OSBindings/Mac/Clock SignalTests/FUSETests.swift +++ b/OSBindings/Mac/Clock SignalTests/FUSETests.swift @@ -194,6 +194,10 @@ class FUSETests: XCTestCase { machine.runForNumber(ofCycles: Int32(targetState.tStates)) + // Verify that exactly the right number of cycles was hit; this is a primitive cycle length tester. + let cyclesRun = machine.completedCycles + XCTAssert(cyclesRun == Int32(targetState.tStates), "Instruction length off; was \(machine.completedCycles) but should be \(targetState.tStates): \(name)") + let finalState = RegisterState(machine: machine) // Compare processor state. diff --git a/Processors/Z80/Z80.hpp b/Processors/Z80/Z80.hpp index 04cdbc65a..7e4a2ab71 100644 --- a/Processors/Z80/Z80.hpp +++ b/Processors/Z80/Z80.hpp @@ -285,6 +285,13 @@ template class Processor { Program(INDEX(), FETCHL(temp8_, INDEX_ADDR()), {MicroOp::op, &temp8_}), \ Program({MicroOp::op, &a_}) +#define READ_OP_GROUP_D(op) \ + Program({MicroOp::op, &bc_.bytes.high}), Program({MicroOp::op, &bc_.bytes.low}), \ + Program({MicroOp::op, &de_.bytes.high}), Program({MicroOp::op, &de_.bytes.low}), \ + Program({MicroOp::op, &index.bytes.high}), Program({MicroOp::op, &index.bytes.low}), \ + Program(INDEX(), FETCHL(temp8_, INDEX_ADDR()), WAIT(1), {MicroOp::op, &temp8_}), \ + Program({MicroOp::op, &a_}) + #define RMW(x, op, ...) Program(INDEX(), FETCHL(x, INDEX_ADDR()), {MicroOp::op, &x}, WAIT(1), STOREL(x, INDEX_ADDR())) #define RMWI(x, op, ...) Program(WAIT(2), FETCHL(x, INDEX_ADDR()), {MicroOp::op, &x}, WAIT(1), STOREL(x, INDEX_ADDR())) @@ -461,7 +468,7 @@ template class Processor { /* 0x40 – 0x7f: BIT */ /* 0x80 – 0xcf: RES */ /* 0xd0 – 0xdf: SET */ - CB_PAGE(MODIFY_OP_GROUP, READ_OP_GROUP) + CB_PAGE(MODIFY_OP_GROUP, READ_OP_GROUP_D) }; InstructionTable offsets_cb_program_table = { CB_PAGE(IX_MODIFY_OP_GROUP, IX_READ_OP_GROUP) diff --git a/Processors/Z80/Z80AllRAM.cpp b/Processors/Z80/Z80AllRAM.cpp index 9ad4f46c2..8594a2fbf 100644 --- a/Processors/Z80/Z80AllRAM.cpp +++ b/Processors/Z80/Z80AllRAM.cpp @@ -14,9 +14,10 @@ namespace { class ConcreteAllRAMProcessor: public AllRAMProcessor, public Processor { public: - ConcreteAllRAMProcessor() : AllRAMProcessor() {} + ConcreteAllRAMProcessor() : AllRAMProcessor(), completed_cycles(0) {} inline int perform_machine_cycle(const MachineCycle &cycle) { + completed_cycles += cycle.length; uint16_t address = cycle.address ? *cycle.address : 0x0000; switch(cycle.operation) { case BusOperation::ReadOpcode: @@ -88,6 +89,14 @@ class ConcreteAllRAMProcessor: public AllRAMProcessor, public Processor::set_non_maskable_interrupt_line(value); } + + int get_length_of_completed_machine_cycles() { + return completed_cycles; + } + + private: + int completed_cycles; + }; } diff --git a/Processors/Z80/Z80AllRAM.hpp b/Processors/Z80/Z80AllRAM.hpp index e31e41768..8999fc5bb 100644 --- a/Processors/Z80/Z80AllRAM.hpp +++ b/Processors/Z80/Z80AllRAM.hpp @@ -36,6 +36,8 @@ class AllRAMProcessor: virtual void set_interrupt_line(bool value) = 0; virtual void set_non_maskable_interrupt_line(bool value) = 0; + virtual int get_length_of_completed_machine_cycles() = 0; + protected: MemoryAccessDelegate *delegate_; AllRAMProcessor() : ::CPU::AllRAMProcessor(65536), delegate_(nullptr) {} From 4c5261bfa042f296c583f3fc5c8f2c9a6beef3cb Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Mon, 12 Jun 2017 22:28:30 -0400 Subject: [PATCH 74/77] Made first attempt to use the horizontal counter for something; here for sync timing only, even though I've gone exclusively with '81-style timing for now. --- Machines/ZX8081/ZX8081.cpp | 20 +++++++++++++++++--- 1 file changed, 17 insertions(+), 3 deletions(-) diff --git a/Machines/ZX8081/ZX8081.cpp b/Machines/ZX8081/ZX8081.cpp index 550d21b69..c3c323434 100644 --- a/Machines/ZX8081/ZX8081.cpp +++ b/Machines/ZX8081/ZX8081.cpp @@ -27,7 +27,20 @@ Machine::Machine() : } int Machine::perform_machine_cycle(const CPU::Z80::MachineCycle &cycle) { - video_->run_for_cycles(cycle.length); + int previous_counter = horizontal_counter_; + horizontal_counter_ += cycle.length; + if(previous_counter < 16 && horizontal_counter_ >= 16) { + video_->run_for_cycles(16 - previous_counter); + set_hsync(true); + video_->run_for_cycles(horizontal_counter_ - 16); + } else if(previous_counter < 32 && horizontal_counter_ >= 32) { + video_->run_for_cycles(32 - previous_counter); + set_hsync(false); + video_->run_for_cycles(horizontal_counter_ - 32); + } else { + video_->run_for_cycles(cycle.length); + } + // tape_player_.run_for_cycles(cycle.length); uint16_t refresh = 0; @@ -55,13 +68,14 @@ int Machine::perform_machine_cycle(const CPU::Z80::MachineCycle &cycle) { } break; case CPU::Z80::BusOperation::Interrupt: - set_hsync(true); +// set_hsync(true); line_counter_ = (line_counter_ + 1) & 7; *cycle.value = 0xff; + horizontal_counter_ = 0; // TODO: more than this? break; case CPU::Z80::BusOperation::ReadOpcode: - set_hsync(false); +// set_hsync(false); // The ZX80 and 81 signal an interrupt while refresh is active and bit 6 of the refresh // address is low. The Z80 signals a refresh, providing the refresh address during the From 1e975859c286f5330df41c27e4a47f67ee1d69c9 Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Tue, 13 Jun 2017 20:09:09 -0400 Subject: [PATCH 75/77] Started splitting ZX80 and ZX81 paths. Also the '80 fires its horizontal sync a little earlier than the '81, so pulled that back a little. --- Machines/ZX8081/ZX8081.cpp | 27 ++++++++++++++++++--------- 1 file changed, 18 insertions(+), 9 deletions(-) diff --git a/Machines/ZX8081/ZX8081.cpp b/Machines/ZX8081/ZX8081.cpp index c3c323434..ace532409 100644 --- a/Machines/ZX8081/ZX8081.cpp +++ b/Machines/ZX8081/ZX8081.cpp @@ -27,18 +27,27 @@ Machine::Machine() : } int Machine::perform_machine_cycle(const CPU::Z80::MachineCycle &cycle) { + const int vsync_length = 16; + int previous_counter = horizontal_counter_; horizontal_counter_ += cycle.length; - if(previous_counter < 16 && horizontal_counter_ >= 16) { - video_->run_for_cycles(16 - previous_counter); - set_hsync(true); - video_->run_for_cycles(horizontal_counter_ - 16); - } else if(previous_counter < 32 && horizontal_counter_ >= 32) { - video_->run_for_cycles(32 - previous_counter); - set_hsync(false); - video_->run_for_cycles(horizontal_counter_ - 32); + + if(is_zx81_) { } else { - video_->run_for_cycles(cycle.length); + const int vsync_start_cycle = 13; + const int vsync_end_cycle = vsync_start_cycle + vsync_length; + + if(previous_counter < vsync_start_cycle && horizontal_counter_ >= vsync_start_cycle) { + video_->run_for_cycles(vsync_start_cycle - previous_counter); + set_hsync(true); + video_->run_for_cycles(horizontal_counter_ - vsync_start_cycle); + } else if(previous_counter < vsync_end_cycle && horizontal_counter_ >= vsync_end_cycle) { + video_->run_for_cycles(vsync_end_cycle - previous_counter); + set_hsync(false); + video_->run_for_cycles(horizontal_counter_ - vsync_end_cycle); + } else { + video_->run_for_cycles(cycle.length); + } } // tape_player_.run_for_cycles(cycle.length); From 76a64d13a0bf0f09086ce437268751b2fc21a629 Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Tue, 13 Jun 2017 21:25:55 -0400 Subject: [PATCH 76/77] Made a first attempt at ZX81 emulation. --- Machines/ZX8081/ZX8081.cpp | 56 +++++++++++++++++++++++--------------- Machines/ZX8081/ZX8081.hpp | 2 ++ 2 files changed, 36 insertions(+), 22 deletions(-) diff --git a/Machines/ZX8081/ZX8081.cpp b/Machines/ZX8081/ZX8081.cpp index ace532409..a1ab3ecd5 100644 --- a/Machines/ZX8081/ZX8081.cpp +++ b/Machines/ZX8081/ZX8081.cpp @@ -20,6 +20,7 @@ namespace { Machine::Machine() : vsync_(false), hsync_(false), + nmi_is_enabled_(false), tape_player_(ZX8081ClockRate) { set_clock_rate(ZX8081ClockRate); tape_player_.set_motor_control(true); @@ -27,30 +28,34 @@ Machine::Machine() : } int Machine::perform_machine_cycle(const CPU::Z80::MachineCycle &cycle) { - const int vsync_length = 16; + int wait_cycles = 0; int previous_counter = horizontal_counter_; horizontal_counter_ += cycle.length; - if(is_zx81_) { - } else { - const int vsync_start_cycle = 13; - const int vsync_end_cycle = vsync_start_cycle + vsync_length; - - if(previous_counter < vsync_start_cycle && horizontal_counter_ >= vsync_start_cycle) { - video_->run_for_cycles(vsync_start_cycle - previous_counter); - set_hsync(true); - video_->run_for_cycles(horizontal_counter_ - vsync_start_cycle); - } else if(previous_counter < vsync_end_cycle && horizontal_counter_ >= vsync_end_cycle) { - video_->run_for_cycles(vsync_end_cycle - previous_counter); - set_hsync(false); - video_->run_for_cycles(horizontal_counter_ - vsync_end_cycle); - } else { - video_->run_for_cycles(cycle.length); + if(previous_counter < vsync_start_cycle_ && horizontal_counter_ >= vsync_start_cycle_) { + video_->run_for_cycles(vsync_start_cycle_ - previous_counter); + set_hsync(true); + if(nmi_is_enabled_) { + set_non_maskable_interrupt_line(true); + if(get_halt_line()) { + wait_cycles = vsync_start_cycle_ - horizontal_counter_; + } } + video_->run_for_cycles(horizontal_counter_ - vsync_start_cycle_ + wait_cycles); + } else if(previous_counter < vsync_end_cycle_ && horizontal_counter_ >= vsync_end_cycle_) { + video_->run_for_cycles(vsync_end_cycle_ - previous_counter); + set_hsync(false); + if(nmi_is_enabled_) set_non_maskable_interrupt_line(false); + video_->run_for_cycles(horizontal_counter_ - vsync_end_cycle_); + } else { + video_->run_for_cycles(cycle.length); } -// tape_player_.run_for_cycles(cycle.length); + horizontal_counter_ += wait_cycles; + if(is_zx81_) horizontal_counter_ %= 207; + +// tape_player_.run_for_cycles(cycle.length + wait_cycles); uint16_t refresh = 0; uint16_t address = cycle.address ? *cycle.address : 0; @@ -58,6 +63,12 @@ int Machine::perform_machine_cycle(const CPU::Z80::MachineCycle &cycle) { case CPU::Z80::BusOperation::Output: set_vsync(false); line_counter_ = 0; + + switch(address & 7) { + default: break; + case 0x5: nmi_is_enabled_ = false; break; + case 0x6: nmi_is_enabled_ = is_zx81_; break; + } break; case CPU::Z80::BusOperation::Input: { @@ -77,15 +88,12 @@ int Machine::perform_machine_cycle(const CPU::Z80::MachineCycle &cycle) { } break; case CPU::Z80::BusOperation::Interrupt: -// set_hsync(true); line_counter_ = (line_counter_ + 1) & 7; *cycle.value = 0xff; - horizontal_counter_ = 0; // TODO: more than this? + horizontal_counter_ = 0; break; case CPU::Z80::BusOperation::ReadOpcode: -// set_hsync(false); - // The ZX80 and 81 signal an interrupt while refresh is active and bit 6 of the refresh // address is low. The Z80 signals a refresh, providing the refresh address during the // final two cycles of an opcode fetch. Therefore communicate a transient signalling @@ -137,7 +145,7 @@ int Machine::perform_machine_cycle(const CPU::Z80::MachineCycle &cycle) { default: break; } - return 0; + return wait_cycles; } void Machine::flush() { @@ -170,10 +178,14 @@ void Machine::configure_as_target(const StaticAnalyser::Target &target) { rom_ = zx81_rom_; tape_trap_address_ = 0x37c; tape_return_address_ = 0x380; + vsync_start_cycle_ = 13; + vsync_end_cycle_ = 33; } else { rom_ = zx80_rom_; tape_trap_address_ = 0x220; tape_return_address_ = 0x248; + vsync_start_cycle_ = 16; + vsync_end_cycle_ = 32; } rom_mask_ = (uint16_t)(rom_.size() - 1); diff --git a/Machines/ZX8081/ZX8081.hpp b/Machines/ZX8081/ZX8081.hpp index c921fd79a..5778b85fe 100644 --- a/Machines/ZX8081/ZX8081.hpp +++ b/Machines/ZX8081/ZX8081.hpp @@ -87,6 +87,8 @@ class Machine: int horizontal_counter_; bool is_zx81_; + bool nmi_is_enabled_; + int vsync_start_cycle_, vsync_end_cycle_; }; } From d8e3103a2bbff1f694238d4f5d0084adcc358167 Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Tue, 13 Jun 2017 21:48:17 -0400 Subject: [PATCH 77/77] Fixes: switched ZX80 and ZX81 timing to the correct way around, ensured that my wait takes effect if HALT **isn't** set, and made sure to recover from it. --- Machines/ZX8081/ZX8081.cpp | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/Machines/ZX8081/ZX8081.cpp b/Machines/ZX8081/ZX8081.cpp index a1ab3ecd5..680b78817 100644 --- a/Machines/ZX8081/ZX8081.cpp +++ b/Machines/ZX8081/ZX8081.cpp @@ -38,12 +38,12 @@ int Machine::perform_machine_cycle(const CPU::Z80::MachineCycle &cycle) { set_hsync(true); if(nmi_is_enabled_) { set_non_maskable_interrupt_line(true); - if(get_halt_line()) { - wait_cycles = vsync_start_cycle_ - horizontal_counter_; + if(!get_halt_line()) { + wait_cycles = vsync_end_cycle_ - horizontal_counter_; } } video_->run_for_cycles(horizontal_counter_ - vsync_start_cycle_ + wait_cycles); - } else if(previous_counter < vsync_end_cycle_ && horizontal_counter_ >= vsync_end_cycle_) { + } else if(previous_counter <= vsync_end_cycle_ && horizontal_counter_ > vsync_end_cycle_) { video_->run_for_cycles(vsync_end_cycle_ - previous_counter); set_hsync(false); if(nmi_is_enabled_) set_non_maskable_interrupt_line(false); @@ -180,12 +180,14 @@ void Machine::configure_as_target(const StaticAnalyser::Target &target) { tape_return_address_ = 0x380; vsync_start_cycle_ = 13; vsync_end_cycle_ = 33; + vsync_start_cycle_ = 16; + vsync_end_cycle_ = 32; } else { rom_ = zx80_rom_; tape_trap_address_ = 0x220; tape_return_address_ = 0x248; - vsync_start_cycle_ = 16; - vsync_end_cycle_ = 32; + vsync_start_cycle_ = 13; + vsync_end_cycle_ = 33; } rom_mask_ = (uint16_t)(rom_.size() - 1);