1
0
mirror of https://github.com/TomHarte/CLK.git synced 2026-01-23 16:16:16 +00:00

Mostly wire in a Z80 second processor.

This commit is contained in:
Thomas Harte
2025-11-04 17:36:30 -05:00
parent 64842d4de2
commit f4d15d0640
9 changed files with 121 additions and 16 deletions

View File

@@ -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) {}

View File

@@ -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<HostT, TubeProcessor::WDC65C02> {
processor(ula) {}
};
template <typename HostT>
struct Tube<HostT, TubeProcessor::Z80> {
using TubeULA = Acorn::Tube::ULA<HostT>;
TubeULA ula;
Acorn::Tube::TubeZ80<TubeULA> processor;
Tube(HostT &owner) :
ula(owner),
processor(ula) {}
};
// MARK: - ConcreteMachine.
template <TubeProcessor tube_processor, bool has_1770>
@@ -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> Machine::BBCMicro(
switch(acorn_target->tube_processor) {
case TubeProcessor::None: return machine<TubeProcessor::None>(*acorn_target, rom_fetcher);
case TubeProcessor::WDC65C02: return machine<TubeProcessor::WDC65C02>(*acorn_target, rom_fetcher);
case TubeProcessor::Z80: return machine<TubeProcessor::Z80>(*acorn_target, rom_fetcher);
default: return nullptr;
}
}

View File

@@ -9,6 +9,7 @@
#pragma once
#include "Processors/6502Mk2/6502Mk2.hpp"
#include "Machines/Utility/ROMCatalogue.hpp"
#include <algorithm>
@@ -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 <typename ULAT>
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.

View File

@@ -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 <typename ULAT>
struct TubeZ80: public CPU::Z80::BusHandler {
public:
// TODO.
static constexpr auto ROM = ROM::Name::BBCMicroTube110;
void set_rom(const std::vector<uint8_t> &) {}
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<TubeZ80, false, false> z80_;
bool rom_visible_ = true;
uint8_t rom_[4096];
uint8_t ram_[65536];
ULAT &ula_;
};
}

View File

@@ -1563,6 +1563,7 @@
4B3FE75D1F3CF68B00448EE4 /* CPM.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; name = CPM.hpp; path = Parsers/CPM.hpp; sourceTree = "<group>"; };
4B4195F42EB8F061001C966D /* ULA.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = ULA.hpp; sourceTree = "<group>"; };
4B4195F52EB92630001C966D /* Tube6502.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = Tube6502.hpp; sourceTree = "<group>"; };
4B4195F62EBAB06C001C966D /* TubeZ80.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = TubeZ80.hpp; sourceTree = "<group>"; };
4B43983829620FB1006B0BFC /* 9918.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = 9918.cpp; sourceTree = "<group>"; };
4B43983C29621024006B0BFC /* ClockConverter.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = ClockConverter.hpp; sourceTree = "<group>"; };
4B43983E29628538006B0BFC /* Fetch.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = Fetch.hpp; sourceTree = "<group>"; };
@@ -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 = "<group>";

View File

@@ -42,6 +42,7 @@ typedef NS_ENUM(NSInteger, CSMachineArchimedesModel) {
typedef NS_ENUM(NSInteger, CSMachineBBCMicroSecondProcessor) {
CSMachineBBCMicroSecondProcessorNone,
CSMachineBBCMicroSecondProcessor65C02,
CSMachineBBCMicroSecondProcessorZ80,
};
typedef NS_ENUM(NSInteger, CSMachineCommodoreTEDModel) {

View File

@@ -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));
}

View File

@@ -459,11 +459,12 @@ Gw
<rect key="frame" x="141" y="168" width="81" height="25"/>
<popUpButtonCell key="cell" type="push" title="None" bezelStyle="rounded" alignment="left" lineBreakMode="truncatingTail" state="on" borderStyle="borderAndBezel" imageScaling="proportionallyDown" inset="2" selectedItem="Rpc-1u-8X2" id="vk3-Qo-uxV">
<behavior key="behavior" lightByBackground="YES" lightByGray="YES"/>
<font key="font" metaFont="menu"/>
<font key="font" metaFont="message"/>
<menu key="menu" id="Qyy-YZ-beq">
<items>
<menuItem title="None" state="on" id="Rpc-1u-8X2"/>
<menuItem title="65C02" tag="6502" id="k5e-d2-O0X"/>
<menuItem title="Z80" tag="80" id="oWF-1C-PLs"/>
</items>
</menu>
</popUpButtonCell>

View File

@@ -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
}