// // AtariST.cpp // Clock Signal // // Created by Thomas Harte on 03/10/2019. // Copyright © 2019 Thomas Harte. All rights reserved. // #include "AtariST.hpp" #include "../CRTMachine.hpp" #include "../../Processors/68000/68000.hpp" #include "Video.hpp" #include "../../ClockReceiver/JustInTime.hpp" #include "../Utility/MemoryPacker.hpp" #include "../Utility/MemoryFuzzer.hpp" namespace Atari { namespace ST { const int CLOCK_RATE = 8000000; using Target = Analyser::Static::Target; class ConcreteMachine: public Atari::ST::Machine, public CPU::MC68000::BusHandler, public CRTMachine::Machine { public: ConcreteMachine(const Target &target, const ROMMachine::ROMFetcher &rom_fetcher) : mc68000_(*this) { set_clock_rate(CLOCK_RATE); ram_.resize(512 * 512); Memory::Fuzz(ram_); std::vector rom_descriptions = { {"AtariST", "the TOS ROM", "tos100.img", 192*1024, 0x1a586c64} }; const auto roms = rom_fetcher(rom_descriptions); if(!roms[0]) { throw ROMMachine::Error::MissingROMs; } Memory::PackBigEndian16(*roms[0], rom_); } // MARK: CRTMachine::Machine void set_scan_target(Outputs::Display::ScanTarget *scan_target) final { video_->set_scan_target(scan_target); } Outputs::Speaker::Speaker *get_speaker() final { return nullptr; } void run_for(const Cycles cycles) final { mc68000_.run_for(cycles); } // MARK: MC68000::BusHandler using Microcycle = CPU::MC68000::Microcycle; HalfCycles perform_bus_operation(const CPU::MC68000::Microcycle &cycle, int is_supervisor) { // Advance time. video_ += cycle.length; // A null cycle leaves nothing else to do. if(!(cycle.operation & (Microcycle::NewAddress | Microcycle::SameAddress))) return HalfCycles(0); auto address = cycle.word_address(); uint16_t *memory; if(address < 4) { memory = rom_.data(); } else if(address < 0x700000) { memory = ram_.data(); address &= ram_.size() - 1; // TODO: align with the next access window. } else if(address < 0x780000) { // TOS 2.0+ address memory = rom_.data(); address &= rom_.size() - 1; } else if(address >= 0x7e0000 && address < 0x7f8000) { // TOS 1.0 address memory = rom_.data(); address &= rom_.size() - 1; } else { assert(false); } // If control has fallen through to here, the access is either a read from ROM, or a read or write to RAM. switch(cycle.operation & (Microcycle::SelectWord | Microcycle::SelectByte | Microcycle::Read)) { default: break; case Microcycle::SelectWord | Microcycle::Read: cycle.value->full = memory[address]; break; case Microcycle::SelectByte | Microcycle::Read: cycle.value->halves.low = uint8_t(memory[address] >> cycle.byte_shift()); break; case Microcycle::SelectWord: memory[address] = cycle.value->full; break; case Microcycle::SelectByte: memory[address] = uint16_t( (cycle.value->halves.low << cycle.byte_shift()) | (memory[address] & cycle.untouched_byte_mask()) ); break; } return HalfCycles(0); } private: CPU::MC68000::Processor mc68000_; JustInTimeActor video_; std::vector ram_; std::vector rom_; }; } } using namespace Atari::ST; Machine *Machine::AtariST(const Analyser::Static::Target *target, const ROMMachine::ROMFetcher &rom_fetcher) { return new ConcreteMachine(*target, rom_fetcher); } Machine::~Machine() {}