From f4d15d0640298084ca737159db2ff0bbf4e618cb Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Tue, 4 Nov 2025 17:36:30 -0500 Subject: [PATCH] Mostly wire in a Z80 second processor. --- Analyser/Static/Acorn/Target.hpp | 2 +- Machines/Acorn/BBCMicro/BBCMicro.cpp | 29 +++--- Machines/Acorn/Tube/Tube6502.hpp | 6 ++ Machines/Acorn/Tube/TubeZ80.hpp | 90 +++++++++++++++++++ .../Clock Signal.xcodeproj/project.pbxproj | 4 +- .../Machine/StaticAnalyser/CSStaticAnalyser.h | 1 + .../StaticAnalyser/CSStaticAnalyser.mm | 1 + .../Base.lproj/MachinePicker.xib | 3 +- .../MachinePicker/MachinePicker.swift | 1 + 9 files changed, 121 insertions(+), 16 deletions(-) create mode 100644 Machines/Acorn/Tube/TubeZ80.hpp diff --git a/Analyser/Static/Acorn/Target.hpp b/Analyser/Static/Acorn/Target.hpp index ba9c386f7..8509f3f09 100644 --- a/Analyser/Static/Acorn/Target.hpp +++ b/Analyser/Static/Acorn/Target.hpp @@ -45,7 +45,7 @@ struct BBCMicroTarget: public ::Analyser::Static::Target, public Reflection::Str bool has_adfs = false; bool has_sideways_ram = true; - ReflectableEnum(TubeProcessor, None, WDC65C02); + ReflectableEnum(TubeProcessor, None, WDC65C02, Z80); TubeProcessor tube_processor = TubeProcessor::None; BBCMicroTarget() : Analyser::Static::Target(Machine::BBCMicro) {} diff --git a/Machines/Acorn/BBCMicro/BBCMicro.cpp b/Machines/Acorn/BBCMicro/BBCMicro.cpp index 17770b602..6927bbebf 100644 --- a/Machines/Acorn/BBCMicro/BBCMicro.cpp +++ b/Machines/Acorn/BBCMicro/BBCMicro.cpp @@ -19,6 +19,7 @@ #include "Machines/Acorn/Tube/ULA.hpp" #include "Machines/Acorn/Tube/Tube6502.hpp" +#include "Machines/Acorn/Tube/TubeZ80.hpp" #include "Components/6522/6522.hpp" #include "Components/6845/CRTC6845.hpp" @@ -704,6 +705,17 @@ struct Tube { processor(ula) {} }; +template +struct Tube { + using TubeULA = Acorn::Tube::ULA; + TubeULA ula; + Acorn::Tube::TubeZ80 processor; + + Tube(HostT &owner) : + ula(owner), + processor(ula) {} +}; + // MARK: - ConcreteMachine. template @@ -760,11 +772,8 @@ public: request = request && Request(Name::BBCMicroADFS130); } - switch(tube_processor) { - default: break; - case TubeProcessor::WDC65C02: - request = request && Request(Name::BBCMicroTube110); - break; + if constexpr (tube_processor != TubeProcessor::None) { + request = request && Request(tube_.processor.ROM); } auto roms = rom_fetcher(request); @@ -796,14 +805,7 @@ public: // Throw the tube ROM to its target. if constexpr (tube_processor != TubeProcessor::None) { - switch(tube_processor) { - default: - __builtin_unreachable(); - - case TubeProcessor::WDC65C02: - tube_.processor.set_rom(roms.find(Name::BBCMicroTube110)->second); - break; - } + tube_.processor.set_rom(roms.find(tube_.processor.ROM)->second); } // Install the ADT ROM if available, but don't error if it's missing. It's very optional. @@ -1294,6 +1296,7 @@ std::unique_ptr Machine::BBCMicro( switch(acorn_target->tube_processor) { case TubeProcessor::None: return machine(*acorn_target, rom_fetcher); case TubeProcessor::WDC65C02: return machine(*acorn_target, rom_fetcher); + case TubeProcessor::Z80: return machine(*acorn_target, rom_fetcher); default: return nullptr; } } diff --git a/Machines/Acorn/Tube/Tube6502.hpp b/Machines/Acorn/Tube/Tube6502.hpp index d55c1b1f7..099b0939f 100644 --- a/Machines/Acorn/Tube/Tube6502.hpp +++ b/Machines/Acorn/Tube/Tube6502.hpp @@ -9,6 +9,7 @@ #pragma once #include "Processors/6502Mk2/6502Mk2.hpp" +#include "Machines/Utility/ROMCatalogue.hpp" #include @@ -18,9 +19,14 @@ namespace Acorn::Tube { // of sense to me; it is copied into RAM (by whom?) and then copied in again upon every reset // (again: by whom?). +// TODO: based on the service manual I believe correct behaviour is to page the ROM upon reset, and keep +// it paged until a tube register is hit. It'll copy itself into the underlying RAM in the meantime. + template struct Tube6502 { public: + static constexpr auto ROM = ROM::Name::BBCMicroTube110; + Tube6502(ULAT &ula) : m6502_(*this), ula_(ula) {} // By convention, these are cycles relative to the host's 2Mhz bus. diff --git a/Machines/Acorn/Tube/TubeZ80.hpp b/Machines/Acorn/Tube/TubeZ80.hpp new file mode 100644 index 000000000..b5686bbfb --- /dev/null +++ b/Machines/Acorn/Tube/TubeZ80.hpp @@ -0,0 +1,90 @@ +// +// TubeZ80.hpp +// Clock Signal +// +// Created by Thomas Harte on 04/11/2025. +// Copyright © 2025 Thomas Harte. All rights reserved. +// + +#pragma once + +#include "Processors/Z80/Z80.hpp" +#include "Machines/Utility/ROMCatalogue.hpp" + +namespace Acorn::Tube { + +template +struct TubeZ80: public CPU::Z80::BusHandler { +public: + // TODO. + static constexpr auto ROM = ROM::Name::BBCMicroTube110; + void set_rom(const std::vector &) {} + + TubeZ80(ULAT &ula) : z80_(*this), ula_(ula) {} + + void run_for(const Cycles cycles) { + // Map from 2Mhz to 6Mhz. + z80_.run_for(cycles * 3); + } + + void set_irq() { z80_.set_interrupt_line(true); } + void set_nmi() { z80_.set_non_maskable_interrupt_line(true); } + void set_reset(const bool reset) { + z80_.set_reset_line(reset); + rom_visible_ |= reset; + } + + HalfCycles perform_machine_cycle(const CPU::Z80::PartialMachineCycle &cycle) { + if(!cycle.is_terminal()) { + return HalfCycles(0); + } + + const uint16_t address = *cycle.address; + rom_visible_ &= address < 0x8000; + + switch(cycle.operation) { + case CPU::Z80::PartialMachineCycle::ReadOpcode: + if(address == 0x66) { + rom_visible_ = true; + } + case CPU::Z80::PartialMachineCycle::Read: + if(rom_visible_ && address <= sizeof(rom_)) { + *cycle.value = rom_[address]; + return HalfCycles(2); + } else { + *cycle.value = ram_[address]; + } + break; + + case CPU::Z80::PartialMachineCycle::Write: + ram_[address] = cycle.value; + break; + + case CPU::Z80::PartialMachineCycle::Interrupt: + *cycle.value = 0xff; + break; + + case CPU::Z80::PartialMachineCycle::Input: + *cycle.value = ula_.read_parasite(address); + break; + + case CPU::Z80::PartialMachineCycle::Output: + ula_.write_parasite(address, *cycle.value); + break; + + default: break; + } + + return HalfCycles(0); + } + +private: + CPU::Z80::Processor z80_; + bool rom_visible_ = true; + + uint8_t rom_[4096]; + uint8_t ram_[65536]; + ULAT &ula_; +}; + +} diff --git a/OSBindings/Mac/Clock Signal.xcodeproj/project.pbxproj b/OSBindings/Mac/Clock Signal.xcodeproj/project.pbxproj index 215afc286..716427ce5 100644 --- a/OSBindings/Mac/Clock Signal.xcodeproj/project.pbxproj +++ b/OSBindings/Mac/Clock Signal.xcodeproj/project.pbxproj @@ -1563,6 +1563,7 @@ 4B3FE75D1F3CF68B00448EE4 /* CPM.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; name = CPM.hpp; path = Parsers/CPM.hpp; sourceTree = ""; }; 4B4195F42EB8F061001C966D /* ULA.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = ULA.hpp; sourceTree = ""; }; 4B4195F52EB92630001C966D /* Tube6502.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = Tube6502.hpp; sourceTree = ""; }; + 4B4195F62EBAB06C001C966D /* TubeZ80.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = TubeZ80.hpp; sourceTree = ""; }; 4B43983829620FB1006B0BFC /* 9918.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = 9918.cpp; sourceTree = ""; }; 4B43983C29621024006B0BFC /* ClockConverter.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = ClockConverter.hpp; sourceTree = ""; }; 4B43983E29628538006B0BFC /* Fetch.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = Fetch.hpp; sourceTree = ""; }; @@ -3361,8 +3362,9 @@ isa = PBXGroup; children = ( 4B4A75C22EB43F1C00EA398F /* FIFO.hpp */, - 4B4195F42EB8F061001C966D /* ULA.hpp */, 4B4195F52EB92630001C966D /* Tube6502.hpp */, + 4B4195F62EBAB06C001C966D /* TubeZ80.hpp */, + 4B4195F42EB8F061001C966D /* ULA.hpp */, ); path = Tube; sourceTree = ""; diff --git a/OSBindings/Mac/Clock Signal/Machine/StaticAnalyser/CSStaticAnalyser.h b/OSBindings/Mac/Clock Signal/Machine/StaticAnalyser/CSStaticAnalyser.h index 33dbda85d..e797fd42c 100644 --- a/OSBindings/Mac/Clock Signal/Machine/StaticAnalyser/CSStaticAnalyser.h +++ b/OSBindings/Mac/Clock Signal/Machine/StaticAnalyser/CSStaticAnalyser.h @@ -42,6 +42,7 @@ typedef NS_ENUM(NSInteger, CSMachineArchimedesModel) { typedef NS_ENUM(NSInteger, CSMachineBBCMicroSecondProcessor) { CSMachineBBCMicroSecondProcessorNone, CSMachineBBCMicroSecondProcessor65C02, + CSMachineBBCMicroSecondProcessorZ80, }; typedef NS_ENUM(NSInteger, CSMachineCommodoreTEDModel) { diff --git a/OSBindings/Mac/Clock Signal/Machine/StaticAnalyser/CSStaticAnalyser.mm b/OSBindings/Mac/Clock Signal/Machine/StaticAnalyser/CSStaticAnalyser.mm index b4076e206..578004c53 100644 --- a/OSBindings/Mac/Clock Signal/Machine/StaticAnalyser/CSStaticAnalyser.mm +++ b/OSBindings/Mac/Clock Signal/Machine/StaticAnalyser/CSStaticAnalyser.mm @@ -172,6 +172,7 @@ switch(secondProcessor) { case CSMachineBBCMicroSecondProcessorNone: target->tube_processor = Target::TubeProcessor::None; break; case CSMachineBBCMicroSecondProcessor65C02: target->tube_processor = Target::TubeProcessor::WDC65C02; break; + case CSMachineBBCMicroSecondProcessorZ80: target->tube_processor = Target::TubeProcessor::Z80; break; } _targets.push_back(std::move(target)); } diff --git a/OSBindings/Mac/Clock Signal/MachinePicker/Base.lproj/MachinePicker.xib b/OSBindings/Mac/Clock Signal/MachinePicker/Base.lproj/MachinePicker.xib index ae39a1573..78f22391e 100644 --- a/OSBindings/Mac/Clock Signal/MachinePicker/Base.lproj/MachinePicker.xib +++ b/OSBindings/Mac/Clock Signal/MachinePicker/Base.lproj/MachinePicker.xib @@ -459,11 +459,12 @@ Gw - + + diff --git a/OSBindings/Mac/Clock Signal/MachinePicker/MachinePicker.swift b/OSBindings/Mac/Clock Signal/MachinePicker/MachinePicker.swift index c1bd142ea..b11565580 100644 --- a/OSBindings/Mac/Clock Signal/MachinePicker/MachinePicker.swift +++ b/OSBindings/Mac/Clock Signal/MachinePicker/MachinePicker.swift @@ -352,6 +352,7 @@ class MachinePicker: NSObject, NSTableViewDataSource, NSTableViewDelegate { var secondProcessor: CSMachineBBCMicroSecondProcessor = .processorNone switch bbcSecondProcessorButton.selectedTag() { case 6502: secondProcessor = .processor65C02 + case 80: secondProcessor = .processorZ80 case 0: fallthrough default: secondProcessor = .processorNone }