From 08a27bdec77689cc5226be24170e83607a25a663 Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Sun, 8 Dec 2019 11:51:12 -0500 Subject: [PATCH 01/12] NTSC frame length is correct; removes TODO. --- Machines/Atari/ST/Video.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Machines/Atari/ST/Video.cpp b/Machines/Atari/ST/Video.cpp index 2dc861a53..e0950219a 100644 --- a/Machines/Atari/ST/Video.cpp +++ b/Machines/Atari/ST/Video.cpp @@ -28,7 +28,7 @@ const struct VerticalParams { const int height; } vertical_params[3] = { {63, 263, 313}, // 47 rather than 63 on early machines. - {34, 234, 263}, // TODO: is 262 correct? If it's 263, how does that interact with opening the bottom border? + {34, 234, 263}, {1, 401, 500} // 72 Hz mode: who knows? }; From 332f0d6167580b7e67bad9d2d4a084df23d412b9 Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Sun, 8 Dec 2019 11:52:15 -0500 Subject: [PATCH 02/12] Ensures MSAs are explicitly read-only. --- Storage/Disk/DiskImage/Formats/MSA.hpp | 1 + 1 file changed, 1 insertion(+) diff --git a/Storage/Disk/DiskImage/Formats/MSA.hpp b/Storage/Disk/DiskImage/Formats/MSA.hpp index 5bd8542f0..b79ecfe41 100644 --- a/Storage/Disk/DiskImage/Formats/MSA.hpp +++ b/Storage/Disk/DiskImage/Formats/MSA.hpp @@ -29,6 +29,7 @@ class MSA final: public DiskImage { HeadPosition get_maximum_head_position() override; int get_head_count() override; std::shared_ptr<::Storage::Disk::Track> get_track_at_position(::Storage::Disk::Track::Address address) override; + bool get_is_read_only() override { return false; } private: FileHolder file_; From 0ed87c61bddbf1d162f0f29ae99535cd1473061d Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Sun, 8 Dec 2019 11:52:43 -0500 Subject: [PATCH 03/12] Introduces an explicit area of floating bus, starts adding bus errors. --- Machines/Atari/ST/AtariST.cpp | 54 +++++++++++++++++++++++++---------- 1 file changed, 39 insertions(+), 15 deletions(-) diff --git a/Machines/Atari/ST/AtariST.cpp b/Machines/Atari/ST/AtariST.cpp index 618431f27..9cd91badd 100644 --- a/Machines/Atari/ST/AtariST.cpp +++ b/Machines/Atari/ST/AtariST.cpp @@ -83,7 +83,8 @@ class ConcreteMachine: // Set up basic memory map. memory_map_[0] = BusDevice::MostlyRAM; int c = 1; - for(; c < 0x08; ++c) memory_map_[c] = BusDevice::RAM; + for(; c < int(ram_.size() >> 15); ++c) memory_map_[c] = BusDevice::RAM; + for(; c < 0x40; ++c) memory_map_[c] = BusDevice::Floating; for(; c < 0xff; ++c) memory_map_[c] = BusDevice::Unassigned; const bool is_early_tos = true; @@ -174,19 +175,30 @@ class ConcreteMachine: // If this is a new strobing of the address signal, test for bus error and pre-DTack delay. // - // DTack delay rule: if accessing RAM or the shifter, align with the two cycles next available - // for the CPU to access that side of the bus. HalfCycles delay(0); - if((cycle.operation & Microcycle::NewAddress) && (address < ram_.size() || (address == (0xff8260 >> 1)))) { - // DTack will be implicit; work out how long until that should be, - // and apply bus error constraints. - const int i_phase = bus_phase_.as() & 7; - if(i_phase < 4) { - delay = HalfCycles(4 - i_phase); - advance_time(delay); + if(cycle.operation & Microcycle::NewAddress) { + if( + // Anything unassigned should generate a bus error. + (memory_map_[address >> 15] == BusDevice::Unassigned) || + + // Bus errors also apply to unprivileged access to the first 0x800 bytes, or the IO area. + (!is_supervisor && (address < 0x400 || memory_map_[address >> 15] == BusDevice::IO)) + ) { + mc68000_.set_bus_error(true); + return delay; // TODO: there should be an extra delay here. } - // TODO: presumably test is if(after declared memory size and (not supervisor or before hardware space)) bus_error? + // DTack delay rule: if accessing RAM or the shifter, align with the two cycles next available + // for the CPU to access that side of the bus. + if(address < ram_.size() || (address == (0xff8260 >> 1))) { + // DTack will be implicit; work out how long until that should be, + // and apply bus error constraints. + const int i_phase = bus_phase_.as() & 7; + if(i_phase < 4) { + delay = HalfCycles(4 - i_phase); + advance_time(delay); + } + } } uint16_t *memory = nullptr; @@ -199,8 +211,6 @@ class ConcreteMachine: } case BusDevice::RAM: memory = ram_.data(); - address &= ram_.size() - 1; - // TODO: align with the next access window. break; case BusDevice::ROM: @@ -208,8 +218,9 @@ class ConcreteMachine: address %= rom_.size(); break; + case BusDevice::Floating: + // TODO: provide vapour reads here. But: will these always be of the last video fetch? case BusDevice::Unassigned: - // TODO: figure out the rules about bus errors. case BusDevice::Cartridge: /* TOS 1.0 appears to attempt to read from the catridge before it has setup @@ -443,7 +454,20 @@ class ConcreteMachine: std::vector rom_; enum class BusDevice { - MostlyRAM, RAM, ROM, Cartridge, IO, Unassigned + /// A mostly RAM page is one that returns ROM for the first 8 bytes, RAM elsewhere. + MostlyRAM, + /// Allows reads and writes to ram_. + RAM, + /// Nothing is mapped to this area, and it also doesn't trigger an exception upon access. + Floating, + /// Allows reading from rom_; writes do nothing. + ROM, + /// Allows interaction with a cartrige_. + Cartridge, + /// Marks the IO page, in which finer decoding will occur. + IO, + /// An unassigned page has nothing below it, in a way that triggers exceptions. + Unassigned }; BusDevice memory_map_[256]; From 4536c6a224c5a0561cfc4319ba6c4cba0296c245 Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Sun, 8 Dec 2019 11:56:05 -0500 Subject: [PATCH 04/12] Resolves printf type errors. --- .../Mac/Clock SignalTests/MasterSystemVDPTests.mm | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/OSBindings/Mac/Clock SignalTests/MasterSystemVDPTests.mm b/OSBindings/Mac/Clock SignalTests/MasterSystemVDPTests.mm index 09de07d02..fc584108b 100644 --- a/OSBindings/Mac/Clock SignalTests/MasterSystemVDPTests.mm +++ b/OSBindings/Mac/Clock SignalTests/MasterSystemVDPTests.mm @@ -149,7 +149,10 @@ NSAssert(time_until_interrupt >= 0, @"Interrupt is scheduled in the past; position %d %d @ %d", c, with_eof, half_cycles); if(last_time_until_interrupt) { - NSAssert(time_until_interrupt == (last_time_until_interrupt - 1), @"Discontinuity found in interrupt prediction; from %d to %d; position %d %d @ %d", last_time_until_interrupt, time_until_interrupt, c, with_eof, half_cycles); + NSAssert( + time_until_interrupt == (last_time_until_interrupt - 1), + @"Discontinuity found in interrupt prediction; from %@ to %@; position %d %d @ %d", + @(last_time_until_interrupt), @(time_until_interrupt), c, with_eof, half_cycles); } last_time_until_interrupt = time_until_interrupt; } @@ -167,7 +170,10 @@ const auto time_remaining_until_line = vdp.get_time_until_line(-1).as_integral(); --time_until_line; if(time_until_line) { - NSAssert(time_remaining_until_line == time_until_line, @"Discontinuity found in distance-to-line prediction; expected %d but got %d", time_until_line, time_remaining_until_line); + NSAssert( + time_remaining_until_line == time_until_line, + @"Discontinuity found in distance-to-line prediction; expected %@ but got %@", + @(time_until_line), @(time_remaining_until_line)); } time_until_line = time_remaining_until_line; } From 407cc78c78fa4e1317689c33d280c94010653978 Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Sun, 8 Dec 2019 20:19:44 -0500 Subject: [PATCH 05/12] Extends to offer simpler 8-bit access handling. --- Processors/68000/68000.hpp | 69 ++++++++++++++++++++++++++++++++------ 1 file changed, 59 insertions(+), 10 deletions(-) diff --git a/Processors/68000/68000.hpp b/Processors/68000/68000.hpp index 10dd8d71e..ca087411a 100644 --- a/Processors/68000/68000.hpp +++ b/Processors/68000/68000.hpp @@ -49,23 +49,25 @@ namespace MC68000 { avoid the runtime cost of actual DTack emulation. But such as the bus allows.) */ struct Microcycle { + /// Indicates that the address strobe and exactly one of the data strobes are active; you can determine + /// which by inspecting the low bit of the provided address. The RW line indicates a read. + static const int SelectByte = 1 << 0; + // Maintenance note: this is bit 0 to reduce the cost of getting a host-endian + // bytewise address. See implementation of host_endian_byte_address(). + + /// Indicates that the address and both data select strobes are active. + static const int SelectWord = 1 << 1; + /// A NewAddress cycle is one in which the address strobe is initially low but becomes high; /// this correlates to states 0 to 5 of a standard read/write cycle. - static const int NewAddress = 1 << 0; + static const int NewAddress = 1 << 2; /// A SameAddress cycle is one in which the address strobe is continuously asserted, but neither /// of the data strobes are. - static const int SameAddress = 1 << 1; + static const int SameAddress = 1 << 3; /// A Reset cycle is one in which the RESET output is asserted. - static const int Reset = 1 << 2; - - /// Indicates that the address and both data select strobes are active. - static const int SelectWord = 1 << 3; - - /// Indicates that the address strobe and exactly one of the data strobes are active; you can determine - /// which by inspecting the low bit of the provided address. The RW line indicates a read. - static const int SelectByte = 1 << 4; + static const int Reset = 1 << 4; /// If set, indicates a read. Otherwise, a write. static const int Read = 1 << 5; @@ -196,6 +198,25 @@ struct Microcycle { return (address ? (*address) & 0x00fffffe : 0) >> 1; } + /*! + @returns the address of the word or byte being accessed at byte precision, + in the endianness of the host platform. + + So: if this is a word access, and the 68000 wants to select the word at address + @c n, this will evaluate to @c n regardless of the host machine's endianness.. + + If this is a byte access and the host machine is big endian it will evalue to @c n. + + If the host machine is little endian then it will evaluate to @c n^1. + */ + forceinline uint32_t host_endian_byte_address() const { + #if TARGET_RT_BIG_ENDIAN + return *address & 0xffffff; + #else + return (*address ^ (1 & operation & SelectByte)) & 0xffffff; + #endif + } + /*! @returns the value on the data bus — all 16 bits, with any inactive lines (as er the upper and lower data selects) being represented by 1s. Assumes @@ -273,6 +294,34 @@ struct Microcycle { return ((*address) & 0x00fffffe) >> 1; } + /*! + Assuming this to be a cycle with a data select active, applies it to @c target, + where 'applies' means: + + * if this is a byte read, reads a single byte from @c target; + * if this is a word read, reads a word (in the host platform's endianness) from @c target; and + * if this is a write, does the converse of a read. + */ + forceinline void apply(uint8_t *target) const { + switch(operation & (SelectWord | SelectByte | Read)) { + default: + break; + + case SelectWord | Read: + value->full = *reinterpret_cast(target); + break; + case SelectByte | Read: + value->halves.low = *target; + break; + case Microcycle::SelectWord: + *reinterpret_cast(target) = value->full; + break; + case Microcycle::SelectByte: + *target = value->halves.low; + break; + } + } + #ifndef NDEBUG bool is_resizeable = false; #endif From 0408592ada89ec56c061d1bcda75921db0b3f966 Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Sun, 8 Dec 2019 20:20:13 -0500 Subject: [PATCH 06/12] Switches to byte buffers and seeks to reduce unnecessary video flushing. --- Machines/Atari/ST/AtariST.cpp | 78 +++++++++++++++++-------------- Machines/Atari/ST/Video.cpp | 24 ++++++++++ Machines/Atari/ST/Video.hpp | 18 +++++++ Machines/Utility/MemoryPacker.cpp | 9 ++++ Machines/Utility/MemoryPacker.hpp | 13 ++++++ 5 files changed, 107 insertions(+), 35 deletions(-) diff --git a/Machines/Atari/ST/AtariST.cpp b/Machines/Atari/ST/AtariST.cpp index 9cd91badd..7d7f650ba 100644 --- a/Machines/Atari/ST/AtariST.cpp +++ b/Machines/Atari/ST/AtariST.cpp @@ -54,7 +54,8 @@ class ConcreteMachine: public KeyboardMachine::MappedMachine, public Activity::Source, public MediaTarget::Machine, - public GI::AY38910::PortHandler { + public GI::AY38910::PortHandler, + public Video::RangeObserver { public: ConcreteMachine(const Target &target, const ROMMachine::ROMFetcher &rom_fetcher) : mc68000_(*this), @@ -66,8 +67,8 @@ class ConcreteMachine: set_clock_rate(CLOCK_RATE); speaker_.set_input_rate(CLOCK_RATE / 4); - ram_.resize(512 * 512); // i.e. 512kb - video_->set_ram(ram_.data(), ram_.size()); + ram_.resize(512 * 1024); // i.e. 512kb + video_->set_ram(reinterpret_cast(ram_.data()), ram_.size()); Memory::Fuzz(ram_); std::vector rom_descriptions = { @@ -83,7 +84,7 @@ class ConcreteMachine: // Set up basic memory map. memory_map_[0] = BusDevice::MostlyRAM; int c = 1; - for(; c < int(ram_.size() >> 15); ++c) memory_map_[c] = BusDevice::RAM; + for(; c < int(ram_.size() >> 16); ++c) memory_map_[c] = BusDevice::RAM; for(; c < 0x40; ++c) memory_map_[c] = BusDevice::Floating; for(; c < 0xff; ++c) memory_map_[c] = BusDevice::Unassigned; @@ -112,6 +113,8 @@ class ConcreteMachine: set_gpip_input(); + video_->set_range_observer(this); + // Insert any supplied media. insert_media(target.media); } @@ -171,18 +174,18 @@ class ConcreteMachine: } } - auto address = cycle.word_address(); + auto address = cycle.host_endian_byte_address(); // If this is a new strobing of the address signal, test for bus error and pre-DTack delay. - // HalfCycles delay(0); if(cycle.operation & Microcycle::NewAddress) { + // Bus error test. if( // Anything unassigned should generate a bus error. - (memory_map_[address >> 15] == BusDevice::Unassigned) || + (memory_map_[address >> 16] == BusDevice::Unassigned) || // Bus errors also apply to unprivileged access to the first 0x800 bytes, or the IO area. - (!is_supervisor && (address < 0x400 || memory_map_[address >> 15] == BusDevice::IO)) + (!is_supervisor && (address < 0x800 || memory_map_[address >> 16] == BusDevice::IO)) ) { mc68000_.set_bus_error(true); return delay; // TODO: there should be an extra delay here. @@ -190,7 +193,7 @@ class ConcreteMachine: // DTack delay rule: if accessing RAM or the shifter, align with the two cycles next available // for the CPU to access that side of the bus. - if(address < ram_.size() || (address == (0xff8260 >> 1))) { + if(address < ram_.size() || (address == 0xff8260)) { // DTack will be implicit; work out how long until that should be, // and apply bus error constraints. const int i_phase = bus_phase_.as() & 7; @@ -201,11 +204,11 @@ class ConcreteMachine: } } - uint16_t *memory = nullptr; - switch(memory_map_[address >> 15]) { + uint8_t *memory = nullptr; + switch(memory_map_[address >> 16]) { default: case BusDevice::MostlyRAM: - if(address < 4) { + if(address < 8) { memory = rom_.data(); break; } @@ -238,7 +241,7 @@ class ConcreteMachine: return delay; case BusDevice::IO: - switch(address) { + switch(address >> 1) { default: // assert(false); @@ -266,7 +269,7 @@ class ConcreteMachine: cycle.set_value8_high(ay_.get_data_output()); ay_.set_control_lines(GI::AY38910::ControlLines(0)); } else { - if(address == 0x7fc400) { + if((address >> 1) == 0x7fc400) { ay_.set_control_lines(GI::AY38910::BC1); } else { ay_.set_control_lines(GI::AY38910::ControlLines(GI::AY38910::BC2 | GI::AY38910::BDIR)); @@ -288,9 +291,9 @@ class ConcreteMachine: if(!cycle.data_select_active()) return delay; if(cycle.operation & Microcycle::Read) { - cycle.set_value8_low(mfp_->read(int(address))); + cycle.set_value8_low(mfp_->read(int(address >> 1))); } else { - mfp_->write(int(address), cycle.value8_low()); + mfp_->write(int(address >> 1), cycle.value8_low()); } break; @@ -311,9 +314,9 @@ class ConcreteMachine: if(!cycle.data_select_active()) return delay; if(cycle.operation & Microcycle::Read) { - cycle.set_value16(video_->read(int(address))); + cycle.set_value16(video_->read(int(address >> 1))); } else { - video_->write(int(address), cycle.value16()); + video_->write(int(address >> 1), cycle.value16()); } break; @@ -323,11 +326,11 @@ class ConcreteMachine: mc68000_.set_is_peripheral_address(!cycle.data_select_active()); if(!cycle.data_select_active()) return delay; - const auto acia_ = (address < 0x7ffe02) ? &keyboard_acia_ : &midi_acia_; + const auto acia_ = ((address >> 1) < 0x7ffe02) ? &keyboard_acia_ : &midi_acia_; if(cycle.operation & Microcycle::Read) { - cycle.set_value8_high((*acia_)->read(int(address))); + cycle.set_value8_high((*acia_)->read(int(address >> 1))); } else { - (*acia_)->write(int(address), cycle.value8_high()); + (*acia_)->write(int(address >> 1), cycle.value8_high()); } } break; @@ -336,9 +339,9 @@ class ConcreteMachine: if(!cycle.data_select_active()) return delay; if(cycle.operation & Microcycle::Read) { - cycle.set_value16(dma_->read(int(address))); + cycle.set_value16(dma_->read(int(address >> 1))); } else { - dma_->write(int(address), cycle.value16()); + dma_->write(int(address >> 1), cycle.value16()); } break; } @@ -351,21 +354,20 @@ class ConcreteMachine: break; case Microcycle::SelectWord | Microcycle::Read: - cycle.value->full = memory[address]; + cycle.value->full = *reinterpret_cast(&memory[address]); break; case Microcycle::SelectByte | Microcycle::Read: - cycle.value->halves.low = uint8_t(memory[address] >> cycle.byte_shift()); + cycle.value->halves.low = memory[address]; break; case Microcycle::SelectWord: - video_.flush(); // TODO: (and below), a range check to determine whether this is really necesary. - memory[address] = cycle.value->full; + if(address >= video_range_.low_address && address < video_range_.high_address) + video_.flush(); + *reinterpret_cast(&memory[address]) = cycle.value->full; break; case Microcycle::SelectByte: - video_.flush(); - memory[address] = uint16_t( - (cycle.value->halves.low << cycle.byte_shift()) | - (memory[address] & cycle.untouched_byte_mask()) - ); + if(address >= video_range_.low_address && address < video_range_.high_address) + video_.flush(); + memory[address] = cycle.value->halves.low; break; } @@ -450,8 +452,8 @@ class ConcreteMachine: HalfCycles cycles_since_ikbd_update_; IntelligentKeyboard ikbd_; - std::vector ram_; - std::vector rom_; + std::vector ram_; + std::vector rom_; enum class BusDevice { /// A mostly RAM page is one that returns ROM for the first 8 bytes, RAM elsewhere. @@ -498,7 +500,7 @@ class ConcreteMachine: // that's implemented, just offers magical zero-cost DMA insertion and // extrication. if(dma_->get_bus_request_line()) { - dma_->bus_grant(ram_.data(), ram_.size()); + dma_->bus_grant(reinterpret_cast(ram_.data()), ram_.size()); } } void set_gpip_input() { @@ -598,6 +600,12 @@ class ConcreteMachine: void set_activity_observer(Activity::Observer *observer) override { dma_->set_activity_observer(observer); } + + // MARK: - Video Range + Video::Range video_range_; + void video_did_change_access_range(Video *video) final { + video_range_ = video->get_memory_access_range(); + } }; } diff --git a/Machines/Atari/ST/Video.cpp b/Machines/Atari/ST/Video.cpp index e0950219a..a97e2be12 100644 --- a/Machines/Atari/ST/Video.cpp +++ b/Machines/Atari/ST/Video.cpp @@ -224,6 +224,14 @@ void Video::run_for(HalfCycles duration) { } else if(next_y_ == vertical_timings.height) { next_y_ = 0; current_address_ = base_address_ >> 1; + + // Consider a shout out to the range observer. + if(previous_base_address_ != base_address_) { + previous_base_address_ = base_address_; + if(range_observer_) { + range_observer_->video_did_change_access_range(this); + } + } } else if(y_ == 0) { next_vertical_.sync_schedule = VerticalState::SyncSchedule::Begin; } else if(y_ == 3) { @@ -589,3 +597,19 @@ void Video::Shifter::output_pixels(int duration, OutputBpp bpp) { void Video::Shifter::load(uint64_t value) { output_shifter_ = value; } + +// MARK: - Range observer. + +Video::Range Video::get_memory_access_range() { + Range range; + range.low_address = uint32_t(previous_base_address_); + range.high_address = range.low_address + 56994; + // 56994 is pessimistic but unscientific, being derived from the resolution of the largest + // fullscreen demo I could quickly find documentation of. TODO: calculate real number. + return range; +} + +void Video::set_range_observer(RangeObserver *observer) { + range_observer_ = observer; + observer->video_did_change_access_range(this); +} diff --git a/Machines/Atari/ST/Video.hpp b/Machines/Atari/ST/Video.hpp index 2859650cd..fc8a4b476 100644 --- a/Machines/Atari/ST/Video.hpp +++ b/Machines/Atari/ST/Video.hpp @@ -86,12 +86,30 @@ class Video { Fifty = 0, Sixty = 1, SeventyTwo = 2 }; + struct RangeObserver { + /// Indicates to the observer that the memory access range has changed. + virtual void video_did_change_access_range(Video *) = 0; + }; + + /// Sets a range observer, which is an actor that will be notified if the memory access range changes. + void set_range_observer(RangeObserver *); + + struct Range { + uint32_t low_address, high_address; + }; + /*! + @returns the range of addresses that the video might read from. + */ + Range get_memory_access_range(); + private: Outputs::CRT::CRT crt_; + RangeObserver *range_observer_ = nullptr; uint16_t raw_palette_[16]; uint16_t palette_[16]; int base_address_ = 0; + int previous_base_address_ = 0; int current_address_ = 0; uint16_t *ram_ = nullptr; diff --git a/Machines/Utility/MemoryPacker.cpp b/Machines/Utility/MemoryPacker.cpp index 101a68b5c..31f099aaa 100644 --- a/Machines/Utility/MemoryPacker.cpp +++ b/Machines/Utility/MemoryPacker.cpp @@ -14,7 +14,16 @@ void Memory::PackBigEndian16(const std::vector &source, uint16_t *targe } } +void Memory::PackBigEndian16(const std::vector &source, uint8_t *target) { + PackBigEndian16(source, reinterpret_cast(target)); +} + void Memory::PackBigEndian16(const std::vector &source, std::vector &target) { target.resize(source.size() >> 1); PackBigEndian16(source, target.data()); } + +void Memory::PackBigEndian16(const std::vector &source, std::vector &target) { + target.resize(source.size()); + PackBigEndian16(source, target.data()); +} diff --git a/Machines/Utility/MemoryPacker.hpp b/Machines/Utility/MemoryPacker.hpp index dbfd624a0..7bb675bc7 100644 --- a/Machines/Utility/MemoryPacker.hpp +++ b/Machines/Utility/MemoryPacker.hpp @@ -20,6 +20,12 @@ namespace Memory { */ void PackBigEndian16(const std::vector &source, uint16_t *target); +/*! + Copies the bytes from @c source to @c target, interpreting them as + big-endian 16-bit data, and writing them as host-endian 16-bit data. +*/ +void PackBigEndian16(const std::vector &source, uint8_t *target); + /*! Copies the bytes from @c source into @c target, interpreting them as big-endian 16-bit data. @c target will be resized to the proper size @@ -27,5 +33,12 @@ void PackBigEndian16(const std::vector &source, uint16_t *target); */ void PackBigEndian16(const std::vector &source, std::vector &target); +/*! + Copies the bytes from @c source into @c target, interpreting them + as big-endian 16-bit data and writing them as host-endian 16-bit data. + @c target will be resized to the proper size exactly to contain the contents of @c source. +*/ +void PackBigEndian16(const std::vector &source, std::vector &target); + } #endif /* MemoryPacker_hpp */ From c785797da665732415de03556c25c5ea3b9a43d0 Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Sun, 8 Dec 2019 21:01:30 -0500 Subject: [PATCH 07/12] Adds a warning for unhandled reset. --- Machines/Atari/ST/AtariST.cpp | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/Machines/Atari/ST/AtariST.cpp b/Machines/Atari/ST/AtariST.cpp index 7d7f650ba..322d35c7e 100644 --- a/Machines/Atari/ST/AtariST.cpp +++ b/Machines/Atari/ST/AtariST.cpp @@ -30,6 +30,8 @@ #include "../../../ClockReceiver/ForceInline.hpp" #include "../../../Outputs/Speaker/Implementation/LowpassSpeaker.hpp" + +#define LOG_PREFIX "[ST] " #include "../../../Outputs/Log.hpp" #include "../../Utility/MemoryPacker.hpp" @@ -151,6 +153,11 @@ class ConcreteMachine: // Advance time. advance_time(cycle.length); + // Check for assertion of reset. + if(cycle.operation & Microcycle::Reset) { + LOG("Unhandled Reset"); + } + // A null cycle leaves nothing else to do. if(!(cycle.operation & (Microcycle::NewAddress | Microcycle::SameAddress))) return HalfCycles(0); From b192381928aef20cf42b4623c8b986ea4dda50f1 Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Sun, 8 Dec 2019 21:20:06 -0500 Subject: [PATCH 08/12] Implements a fuller reset, takes a run at the overran flag. --- Components/6850/6850.cpp | 23 +++++++++++++++++++---- Components/6850/6850.hpp | 2 ++ 2 files changed, 21 insertions(+), 4 deletions(-) diff --git a/Components/6850/6850.cpp b/Components/6850/6850.cpp index fed063e48..6c1c9b1b4 100644 --- a/Components/6850/6850.cpp +++ b/Components/6850/6850.cpp @@ -21,6 +21,7 @@ ACIA::ACIA(HalfCycles transmit_clock_rate, HalfCycles receive_clock_rate) : uint8_t ACIA::read(int address) { if(address&1) { + overran_ = false; received_data_ |= NoValueMask; update_interrupt_line(); return uint8_t(received_data_); @@ -29,6 +30,19 @@ uint8_t ACIA::read(int address) { } } +void ACIA::reset() { + transmit.reset_writing(); + transmit.write(true); + request_to_send.reset_writing(); + + bits_received_ = bits_incoming_ = 0; + receive_interrupt_enabled_ = transmit_interrupt_enabled_ = false; + overran_ = false; + next_transmission_ = received_data_ = NoValueMask; + + update_interrupt_line(); +} + void ACIA::write(int address, uint8_t value) { if(address&1) { next_transmission_ = value; @@ -36,9 +50,7 @@ void ACIA::write(int address, uint8_t value) { update_interrupt_line(); } else { if((value&3) == 3) { - transmit.reset_writing(); - transmit.write(true); - request_to_send.reset_writing(); + reset(); } else { switch(value & 3) { default: @@ -143,6 +155,7 @@ bool ACIA::serial_line_did_produce_bit(Serial::Line *line, int bit) { const int bit_target = expected_bits(); if(bits_received_ >= bit_target) { bits_received_ = 0; + overran_ |= get_status() & 1; received_data_ = uint8_t(bits_incoming_ >> (12 - bit_target)); update_interrupt_line(); update_clocking_observer(); @@ -186,8 +199,9 @@ void ACIA::update_interrupt_line() { (receive_interrupt_enabled_ && (status & 0x25)) || (transmit_interrupt_enabled_ && (status & 0x02)); - if(interrupt_delegate_ && old_line != interrupt_line_) + if(interrupt_delegate_ && old_line != interrupt_line_) { interrupt_delegate_->acia6850_did_change_interrupt_status(this); + } } uint8_t ACIA::get_status() { @@ -196,6 +210,7 @@ uint8_t ACIA::get_status() { ((next_transmission_ == NoValueMask) ? 0x02 : 0x00) | // (data_carrier_detect.read() ? 0x04 : 0x00) | // (clear_to_send.read() ? 0x08 : 0x00) | + (overran_ ? 0x20 : 0x00) | (interrupt_line_ ? 0x80 : 0x00) ; diff --git a/Components/6850/6850.hpp b/Components/6850/6850.hpp index e6c3eb92e..6f3734cd6 100644 --- a/Components/6850/6850.hpp +++ b/Components/6850/6850.hpp @@ -74,6 +74,7 @@ class ACIA: public ClockingHint::Source, private Serial::Line::ReadDelegate { } bool get_interrupt_line() const; + void reset(); // Input lines. Serial::Line receive; @@ -105,6 +106,7 @@ class ACIA: public ClockingHint::Source, private Serial::Line::ReadDelegate { int bits_received_ = 0; int bits_incoming_ = 0; + bool overran_ = false; void consider_transmission(); int expected_bits(); From acfe2c63b8c02a2a51b9da3a1739d731258f3d6d Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Sun, 8 Dec 2019 22:34:19 -0500 Subject: [PATCH 09/12] Adds an assert to verify the interrupt line is clear after a full reset. --- Components/6850/6850.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/Components/6850/6850.cpp b/Components/6850/6850.cpp index 6c1c9b1b4..dfc71a61b 100644 --- a/Components/6850/6850.cpp +++ b/Components/6850/6850.cpp @@ -41,6 +41,7 @@ void ACIA::reset() { next_transmission_ = received_data_ = NoValueMask; update_interrupt_line(); + assert(!interrupt_line_); } void ACIA::write(int address, uint8_t value) { From a7cfb840ef639e2d8dd7b6a5591cd38fdbd90f41 Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Sun, 8 Dec 2019 22:34:42 -0500 Subject: [PATCH 10/12] Adds but presently disables a diagnostic for border elimination. --- Machines/Atari/ST/Video.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/Machines/Atari/ST/Video.cpp b/Machines/Atari/ST/Video.cpp index a97e2be12..6fa3a9aa0 100644 --- a/Machines/Atari/ST/Video.cpp +++ b/Machines/Atari/ST/Video.cpp @@ -419,7 +419,11 @@ void Video::update_output_mode() { return; } +// const auto old_frequency = field_frequency_; field_frequency_ = (sync_mode_ & 0x200) ? FieldFrequency::Fifty : FieldFrequency::Sixty; +// if(field_frequency_ != old_frequency) { +// printf("%d, %d -> %d\n", x_, y_, field_frequency_); +// } } // MARK: - The shifter From 4dd235f677e97670a223e93cf6fc5009cb8b1b24 Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Sun, 8 Dec 2019 22:39:10 -0500 Subject: [PATCH 11/12] Adds supervisor/user to logged flags in trace mode. --- Processors/68000/Implementation/68000Implementation.hpp | 1 + 1 file changed, 1 insertion(+) diff --git a/Processors/68000/Implementation/68000Implementation.hpp b/Processors/68000/Implementation/68000Implementation.hpp index 6d2d1e358..7d240db46 100644 --- a/Processors/68000/Implementation/68000Implementation.hpp +++ b/Processors/68000/Implementation/68000Implementation.hpp @@ -280,6 +280,7 @@ template void Proces std::cout << std::setfill('0'); std::cout << (extend_flag_ ? 'x' : '-') << (negative_flag_ ? 'n' : '-') << (zero_result_ ? '-' : 'z'); std::cout << (overflow_flag_ ? 'v' : '-') << (carry_flag_ ? 'c' : '-') << '\t'; + std::cout << (is_supervisor_ ? 's' : 'u') << '\t'; for(int c = 0; c < 8; ++ c) std::cout << "d" << c << ":" << std::setw(8) << data_[c].full << " "; for(int c = 0; c < 8; ++ c) std::cout << "a" << c << ":" << std::setw(8) << address_[c].full << " "; if(is_supervisor_) { From 7cd11ecb7f8bcdd62631cba3abe8ad3407543e37 Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Sun, 8 Dec 2019 22:43:39 -0500 Subject: [PATCH 12/12] Adds necessary #include for `assert`. --- Components/6850/6850.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Components/6850/6850.cpp b/Components/6850/6850.cpp index dfc71a61b..0c486a169 100644 --- a/Components/6850/6850.cpp +++ b/Components/6850/6850.cpp @@ -8,6 +8,8 @@ #include "6850.hpp" +#include + using namespace Motorola::ACIA; const HalfCycles ACIA::SameAsTransmit;