diff --git a/Analyser/Static/AppleII/StaticAnalyser.cpp b/Analyser/Static/AppleII/StaticAnalyser.cpp index c12292336..b3825d960 100644 --- a/Analyser/Static/AppleII/StaticAnalyser.cpp +++ b/Analyser/Static/AppleII/StaticAnalyser.cpp @@ -14,6 +14,8 @@ Analyser::Static::TargetList Analyser::Static::AppleII::GetTargets(const Media & target->machine = Machine::AppleII; target->media = media; + target->has_disk = !target->media.disks.empty(); + TargetList targets; targets.push_back(std::move(target)); return targets; diff --git a/Analyser/Static/AppleII/Target.hpp b/Analyser/Static/AppleII/Target.hpp index 7bd90107d..9d058ac75 100644 --- a/Analyser/Static/AppleII/Target.hpp +++ b/Analyser/Static/AppleII/Target.hpp @@ -16,7 +16,7 @@ namespace Static { namespace AppleII { struct Target: public ::Analyser::Static::Target { - bool has_disk_; // TODO: Disk II versus IWM? + bool has_disk; // TODO: Disk II versus IWM? }; } diff --git a/Machines/AppleII/AppleII.cpp b/Machines/AppleII/AppleII.cpp index f66fea105..f550d2247 100644 --- a/Machines/AppleII/AppleII.cpp +++ b/Machines/AppleII/AppleII.cpp @@ -18,8 +18,11 @@ #include "../../Outputs/Speaker/Implementation/LowpassSpeaker.hpp" +#include "Card.hpp" #include "Video.hpp" +#include "../../Analyser/Static/AppleII/Target.hpp" + #include namespace { @@ -57,6 +60,16 @@ class ConcreteMachine: void update_audio() { speaker_.run_for(audio_queue_, cycles_since_audio_update_.divide(Cycles(audio_divider))); } + void update_cards() { + cycles_since_card_update_ += stretched_cycles_since_card_update_ / 7; + stretched_cycles_since_card_update_ %= 7; + for(int c = 0; c < 7; ++c) { + if(cards_[c]) + cards_[c]->run_for(cycles_since_card_update_, stretched_cycles_since_card_update_); + } + cycles_since_card_update_ = 0; + stretched_cycles_since_card_update_ = 0; + } uint8_t ram_[48*1024]; std::vector rom_; @@ -69,6 +82,11 @@ class ConcreteMachine: Outputs::Speaker::LowpassSpeaker speaker_; Cycles cycles_since_audio_update_; + ROMMachine::ROMFetcher rom_fetcher_; + AppleII::Card *cards_[7] = {nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr}; + Cycles cycles_since_card_update_; + int stretched_cycles_since_card_update_ = 0; + public: ConcreteMachine(): m6502_(*this), @@ -110,6 +128,7 @@ class ConcreteMachine: Cycles perform_bus_operation(CPU::MOS6502::BusOperation operation, uint16_t address, uint8_t *value) { ++ cycles_since_video_update_; + ++ cycles_since_card_update_; cycles_since_audio_update_ += Cycles(7); switch(address) { @@ -123,7 +142,6 @@ class ConcreteMachine: switch(address) { default: // printf("Unknown access to %04x\n", address); - *value = 0xff; break; case 0xc000: *value = keyboard_input_; @@ -160,6 +178,28 @@ class ConcreteMachine: break; } + if(address >= 0xc100 && address < 0xc800) { + /* + Decode the area conventionally used by cards for ROMs: + 0xCn00 — 0xCnff: card n. + */ + const int card_number = (address - 0xc100) >> 8; + if(cards_[card_number]) { + update_cards(); + cards_[card_number]->perform_bus_operation(operation, address & 0xff, value); + } + } else if(address >= 0xc090 && address < 0xc100) { + /* + Decode the area conventionally used by cards for registers: + C0n0--C0nF: card n - 8. + */ + const int card_number = (address - 0xc080) >> 4; + if(cards_[card_number]) { + update_cards(); + cards_[card_number]->perform_bus_operation(operation, address, value); + } + } + // The Apple II has a slightly weird timing pattern: every 65th CPU cycle is stretched // by an extra 1/7th. That's because one cycle lasts 3.5 NTSC colour clocks, so after // 65 cycles a full line of 227.5 colour clocks have passed. But the high-rate binary @@ -169,6 +209,7 @@ class ConcreteMachine: cycles_into_current_line_ = (cycles_into_current_line_ + 1) % 65; if(!cycles_into_current_line_) { ++ cycles_since_audio_update_; + ++ stretched_cycles_since_card_update_; } return Cycles(1); @@ -180,7 +221,7 @@ class ConcreteMachine: audio_queue_.perform(); } - bool set_rom_fetcher(const std::function>>(const std::string &machine, const std::vector &names)> &roms_with_names) override { + bool set_rom_fetcher(const ROMMachine::ROMFetcher &roms_with_names) override { auto roms = roms_with_names( "AppleII", { @@ -194,6 +235,8 @@ class ConcreteMachine: character_rom_ = std::move(*roms[1]); + rom_fetcher_ = roms_with_names; + return true; } @@ -223,6 +266,10 @@ class ConcreteMachine: // MARK: ConfigurationTarget void configure_as_target(const Analyser::Static::Target *target) override { + auto *const apple_target = dynamic_cast(target); + if(apple_target->has_disk) { + // ... add Disk II + } } bool insert_media(const Analyser::Static::Media &media) override { diff --git a/Machines/AppleII/Card.hpp b/Machines/AppleII/Card.hpp new file mode 100644 index 000000000..7f02a270a --- /dev/null +++ b/Machines/AppleII/Card.hpp @@ -0,0 +1,27 @@ +// +// Card.h +// Clock Signal +// +// Created by Thomas Harte on 23/04/2018. +// Copyright © 2018 Thomas Harte. All rights reserved. +// + +#ifndef Card_h +#define Card_h + +#include "../../ClockReceiver/ClockReceiver.hpp" + +namespace AppleII { + +class Card { + public: + /*! Advances time by @c cycles, of which @c stretches were stretched. */ + virtual void run_for(Cycles half_cycles, int stretches) {} + + /*! Performs a bus operation; the card is implicitly selected. */ + virtual void perform_bus_operation(CPU::MOS6502::BusOperation operation, uint16_t address, uint8_t *value) = 0; +}; + +} + +#endif /* Card_h */ diff --git a/OSBindings/Mac/Clock Signal.xcodeproj/project.pbxproj b/OSBindings/Mac/Clock Signal.xcodeproj/project.pbxproj index bafaf0bc9..0785cb63f 100644 --- a/OSBindings/Mac/Clock Signal.xcodeproj/project.pbxproj +++ b/OSBindings/Mac/Clock Signal.xcodeproj/project.pbxproj @@ -1332,6 +1332,7 @@ 4BBFBB6A1EE8401E00C01E7A /* ZX8081.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = ZX8081.cpp; path = Parsers/ZX8081.cpp; sourceTree = ""; }; 4BBFBB6B1EE8401E00C01E7A /* ZX8081.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; name = ZX8081.hpp; path = Parsers/ZX8081.hpp; sourceTree = ""; }; 4BBFFEE51F7B27F1005F3FEB /* TrackSerialiser.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = TrackSerialiser.cpp; sourceTree = ""; }; + 4BC39565208EDFCE0044766B /* Card.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = Card.hpp; sourceTree = ""; }; 4BC3B74D1CD194CC00F86E85 /* Shader.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = Shader.cpp; sourceTree = ""; }; 4BC3B74E1CD194CC00F86E85 /* Shader.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = Shader.hpp; sourceTree = ""; }; 4BC3B7501CD1956900F86E85 /* OutputShader.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = OutputShader.cpp; sourceTree = ""; }; @@ -1555,6 +1556,7 @@ 4B15AA0A2082C799005E6C8D /* Video.cpp */, 4B15AA092082C799005E6C8D /* AppleII.hpp */, 4B15AA0B2082C799005E6C8D /* Video.hpp */, + 4BC39565208EDFCE0044766B /* Card.hpp */, ); path = AppleII; sourceTree = ""; diff --git a/OSBindings/Mac/Clock Signal/Machine/StaticAnalyser/CSStaticAnalyser.mm b/OSBindings/Mac/Clock Signal/Machine/StaticAnalyser/CSStaticAnalyser.mm index 2ed5386b0..29862a206 100644 --- a/OSBindings/Mac/Clock Signal/Machine/StaticAnalyser/CSStaticAnalyser.mm +++ b/OSBindings/Mac/Clock Signal/Machine/StaticAnalyser/CSStaticAnalyser.mm @@ -15,6 +15,7 @@ #include "../../../../../Analyser/Static/Acorn/Target.hpp" #include "../../../../../Analyser/Static/AmstradCPC/Target.hpp" +#include "../../../../../Analyser/Static/AppleII/Target.hpp" #include "../../../../../Analyser/Static/Commodore/Target.hpp" #include "../../../../../Analyser/Static/MSX/Target.hpp" #include "../../../../../Analyser/Static/Oric/Target.hpp" @@ -156,7 +157,7 @@ static Analyser::Static::ZX8081::Target::MemoryModel ZX8081MemoryModelFromSize(K - (instancetype)initWithAppleII { self = [super init]; if(self) { - using Target = Analyser::Static::Target; + using Target = Analyser::Static::AppleII::Target; std::unique_ptr target(new Target); target->machine = Analyser::Machine::AppleII; _targets.push_back(std::move(target));