// // Chipset.hpp // Clock Signal // // Created by Thomas Harte on 22/07/2021. // Copyright © 2021 Thomas Harte. All rights reserved. // #ifndef Chipset_hpp #define Chipset_hpp #include #include #include #include #include #include "../../Activity/Source.hpp" #include "../../ClockReceiver/ClockingHintSource.hpp" #include "../../ClockReceiver/JustInTime.hpp" #include "../../Components/6526/6526.hpp" #include "../../Outputs/CRT/CRT.hpp" #include "../../Processors/68000Mk2/68000Mk2.hpp" #include "../../Storage/Disk/Controller/DiskController.hpp" #include "../../Storage/Disk/Drive.hpp" #include "Audio.hpp" #include "Bitplanes.hpp" #include "Blitter.hpp" #include "Copper.hpp" #include "DMADevice.hpp" #include "Flags.hpp" #include "Keyboard.hpp" #include "MouseJoystick.hpp" #include "MemoryMap.hpp" #include "Sprites.hpp" namespace Amiga { class Chipset: private ClockingHint::Observer { public: Chipset(MemoryMap &memory_map, int input_clock_rate); struct Changes { int interrupt_level = 0; HalfCycles duration; Changes &operator += (const Changes &rhs) { duration += rhs.duration; return *this; } }; /// Advances the stated amount of time. Changes run_for(HalfCycles); /// Advances to the end of the next available CPU slot. Changes run_until_after_cpu_slot(); /// Performs the provided microcycle, which the caller guarantees to be a memory access. void perform(const CPU::MC68000Mk2::Microcycle &); /// Sets the current state of the CIA interrupt lines. void set_cia_interrupts(bool cia_a, bool cia_b); /// Provides the chipset's current interrupt level. int get_interrupt_level() { return interrupt_level_; } /// Inserts the disks provided. /// @returns @c true if anything was inserted; @c false otherwise. bool insert(const std::vector> &disks); // The standard CRT set. void set_scan_target(Outputs::Display::ScanTarget *scan_target); Outputs::Display::ScanStatus get_scaled_scan_status() const; void set_display_type(Outputs::Display::DisplayType); Outputs::Display::DisplayType get_display_type() const; // Activity observation. void set_activity_observer(Activity::Observer *observer) { cia_a_handler_.set_activity_observer(observer); disk_controller_.set_activity_observer(observer); } // Keyboard and mouse exposure. Keyboard &get_keyboard() { return keyboard_; } const std::vector> &get_joysticks() { return joysticks_; } // Synchronisation. void flush(); // Input for receiving collected bitplanes. void post_bitplanes(const BitplaneData &data); // Obtains the source of audio output. Outputs::Speaker::Speaker *get_speaker() { return audio_.get_speaker(); } private: friend class DMADeviceBase; // MARK: - Register read/write functions. uint16_t read(uint32_t address, bool allow_conversion = true); void write(uint32_t address, uint16_t value, bool allow_conversion = true); static constexpr uint32_t ChipsetAddressMask = 0x1fe; friend class Copper; // MARK: - E Clock and keyboard dividers. HalfCycles cia_divider_; HalfCycles keyboard_divider_; // MARK: - Interrupts. uint16_t interrupt_enable_ = 0; uint16_t interrupt_requests_ = 0; int interrupt_level_ = 0; void update_interrupts(); void posit_interrupt(InterruptFlag::FlagT); // MARK: - Scheduler. template Changes run(HalfCycles duration = HalfCycles::max()); template int advance_slots(int, int); template bool perform_cycle(); template void output(); void output_pixels(int cycles_until_sync); void apply_ham(uint8_t); // MARK: - DMA Control, Scheduler and Blitter. uint16_t dma_control_ = 0; Blitter blitter_; // MARK: - Sprites and collision flags. std::array sprites_; std::array sprite_shifters_; uint16_t collisions_ = 0, collisions_flags_= 0; uint32_t playfield_collision_mask_ = 0, playfield_collision_complement_ = 0; // MARK: - Raster position and state. // Definitions related to PAL/NTSC. // (Default values are PAL). int line_length_ = 227; int short_field_height_ = 312; int vertical_blank_height_ = 25; // PAL = 25, NTSC = 20 // Current raster position. int line_cycle_ = 0, y_ = 0; // Parameters affecting bitplane collection and output. uint16_t display_window_start_[2] = {0, 0}; uint16_t display_window_stop_[2] = {0, 0}; uint16_t fetch_window_[2] = {0, 0}; // Ephemeral bitplane collection state. bool fetch_vertical_ = false; bool display_horizontal_ = false; bool did_fetch_ = false; int horizontal_offset_ = 0; enum HorizontalFetch { Started, WillRequestStop, StopRequested, Stopped } horizontal_fetch_ = HorizontalFetch::Stopped; // Output state. uint16_t border_colour_ = 0; bool is_border_ = true; int zone_duration_ = 0; uint16_t *pixels_ = nullptr; uint16_t last_colour_ = 0; // Retained for HAM mode. void flush_output(); Bitplanes bitplanes_; BitplaneData next_bitplanes_, previous_bitplanes_; bool has_next_bitplanes_ = false; int odd_priority_ = 0, even_priority_ = 0; bool even_over_odd_ = false; bool hold_and_modify_ = false; bool dual_playfields_ = false; bool interlace_ = false; bool is_long_field_ = false; BitplaneShifter bitplane_pixels_; int odd_delay_ = 0, even_delay_ = 0; bool is_high_res_ = false; // MARK: - Copper. Copper copper_; // MARK: - Audio. Audio audio_; // MARK: - Serial port. class SerialPort { public: void set_control(uint16_t); void set_data(uint16_t); uint16_t get_status(); private: uint16_t value = 0, reload = 0; uint16_t shift = 0, receive_shift = 0; uint16_t status; } serial_; // MARK: - Pixel output. Outputs::CRT::CRT crt_; uint16_t palette_[32]{}; uint16_t swizzled_palette_[64]{}; // MARK: - Mouse. private: Mouse mouse_; public: Inputs::Mouse &get_mouse() { return mouse_; } // MARK: - Joystick. private: std::vector> joysticks_; Joystick &joystick(size_t index) const { return *static_cast(joysticks_[index].get()); } // MARK: - CIAs. private: class DiskController; class CIAAHandler: public MOS::MOS6526::PortHandler { public: CIAAHandler(MemoryMap &map, DiskController &controller, Mouse &mouse); void set_port_output(MOS::MOS6526::Port port, uint8_t value); uint8_t get_port_input(MOS::MOS6526::Port port); void set_activity_observer(Activity::Observer *observer); // TEMPORARY. // TODO: generalise mice and joysticks. // This is a hack. A TEMPORARY HACK. void set_joystick(Joystick *joystick) { joystick_ = joystick; } private: MemoryMap &map_; DiskController &controller_; Mouse &mouse_; Joystick *joystick_ = nullptr; Activity::Observer *observer_ = nullptr; inline static const std::string led_name = "Power"; } cia_a_handler_; class CIABHandler: public MOS::MOS6526::PortHandler { public: CIABHandler(DiskController &controller); void set_port_output(MOS::MOS6526::Port port, uint8_t value); uint8_t get_port_input(MOS::MOS6526::Port); private: DiskController &controller_; } cia_b_handler_; public: using CIAA = MOS::MOS6526::MOS6526; using CIAB = MOS::MOS6526::MOS6526; // CIAs are provided for direct access; it's up to the caller properly // to distinguish relevant accesses. CIAA cia_a; CIAB cia_b; private: // MARK: - Disk drives. class DiskDMA: public DMADevice<1> { public: using DMADevice::DMADevice; void set_length(uint16_t); void set_control(uint16_t); bool advance_dma(); void enqueue(uint16_t value, bool matches_sync); private: uint16_t length_; bool dma_enable_ = false; bool write_ = false; uint16_t last_set_length_ = 0; bool sync_with_word_ = false; std::array buffer_; size_t buffer_read_ = 0, buffer_write_ = 0; enum class State { Inactive, WaitingForSync, Reading, } state_ = State::Inactive; } disk_; class DiskController: public Storage::Disk::Controller { public: DiskController(Cycles clock_rate, Chipset &chipset, DiskDMA &disk_dma, CIAB &cia); void set_mtr_sel_side_dir_step(uint8_t); uint8_t get_rdy_trk0_wpro_chng(); void run_for(Cycles duration) { Storage::Disk::Controller::run_for(duration); } bool insert(const std::shared_ptr &disk, size_t drive); void set_activity_observer(Activity::Observer *); void set_sync_word(uint16_t); void set_control(uint16_t); private: void process_input_bit(int value) final; void process_index_hole() final; // Implement the Amiga's drive ID shift registers // directly in the controller for now. uint32_t drive_ids_[4]{}; uint32_t previous_select_ = 0; uint16_t data_ = 0; int bit_count_ = 0; uint16_t sync_word_ = 0x4489; // TODO: confirm or deny guess. bool sync_with_word_ = false; Chipset &chipset_; DiskDMA &disk_dma_; CIAB &cia_; } disk_controller_; friend DiskController; void set_component_prefers_clocking(ClockingHint::Source *, ClockingHint::Preference) final; bool disk_controller_is_sleeping_ = false; uint16_t paula_disk_control_ = 0; // MARK: - Keyboard. Keyboard keyboard_; }; } #endif /* Chipset_hpp */