mirror of
https://github.com/TomHarte/CLK.git
synced 2024-11-23 03:32:32 +00:00
Merge pull request #678 from TomHarte/DEDelay
Introduces a 28-cycle delay on DE propagation
This commit is contained in:
commit
c91495d068
@ -137,7 +137,7 @@ template <class T> class WrappedInt {
|
||||
// bool operator () is not supported because it offers an implicit cast to int, which is prone silently to permit misuse
|
||||
|
||||
/// @returns The underlying int, cast to an integral type of your choosing.
|
||||
template<typename Type = IntType> forceinline constexpr Type as() { return Type(length_); }
|
||||
template<typename Type = IntType> forceinline constexpr Type as() const { return Type(length_); }
|
||||
|
||||
/// @returns The underlying int, in its native form.
|
||||
forceinline constexpr IntType as_integral() const { return length_; }
|
||||
|
@ -232,7 +232,10 @@ void MFP68901::set_timer_event_input(int channel, bool value) {
|
||||
if(timers_[channel].event_input == value) return;
|
||||
|
||||
timers_[channel].event_input = value;
|
||||
if(timers_[channel].mode == TimerMode::EventCount && !value) { /* TODO: which edge is counted? "as defined by the associated Interrupt Channel’s edge bit"? */
|
||||
if(timers_[channel].mode == TimerMode::EventCount && (value == !!(gpip_active_edge_ & (0x10 >> channel)))) {
|
||||
// "The active state of the signal on TAI or TBI is dependent upon the associated
|
||||
// Interrupt Channel’s edge bit (GPIP 4 for TAI and GPIP 3 for TBI [...] ).
|
||||
// If the edge bit associated with the TAI or TBI input is a one, it will be active high.
|
||||
decrement_timer(channel, 1);
|
||||
}
|
||||
}
|
||||
|
@ -170,9 +170,14 @@ class ConcreteMachine:
|
||||
}
|
||||
}
|
||||
|
||||
auto address = cycle.word_address();
|
||||
|
||||
// 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) {
|
||||
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<int>() & 7;
|
||||
@ -184,7 +189,6 @@ class ConcreteMachine:
|
||||
// TODO: presumably test is if(after declared memory size and (not supervisor or before hardware space)) bus_error?
|
||||
}
|
||||
|
||||
auto address = cycle.word_address();
|
||||
uint16_t *memory = nullptr;
|
||||
switch(memory_map_[address >> 15]) {
|
||||
default:
|
||||
|
@ -72,9 +72,12 @@ void IntelligentKeyboard::run_for(HalfCycles duration) {
|
||||
key_queue_.clear();
|
||||
}
|
||||
|
||||
// Check for joystick changes.
|
||||
// Check for joystick changes; slight complexity here: the joystick that the emulated
|
||||
// machine advertises as joystick 1 is mapped to the Atari ST's joystick 2, so as to
|
||||
// maintain both the normal emulation expections that the first joystick is the primary
|
||||
// one and the Atari ST's convention that the main joystick is in port 2.
|
||||
for(size_t c = 0; c < 2; ++c) {
|
||||
const auto joystick = static_cast<Joystick *>(joysticks_[c].get());
|
||||
const auto joystick = static_cast<Joystick *>(joysticks_[c ^ 1].get());
|
||||
if(joystick->has_event()) {
|
||||
output_bytes({
|
||||
uint8_t(0xfe | c),
|
||||
@ -386,8 +389,8 @@ void IntelligentKeyboard::interrogate_joysticks() {
|
||||
|
||||
output_bytes({
|
||||
0xfd,
|
||||
joystick1->get_state(),
|
||||
joystick2->get_state()
|
||||
joystick2->get_state(),
|
||||
joystick1->get_state()
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -28,12 +28,12 @@ const struct VerticalParams {
|
||||
const int height;
|
||||
} vertical_params[3] = {
|
||||
{63, 263, 313}, // 47 rather than 63 on early machines.
|
||||
{34, 234, 263},
|
||||
{34, 234, 262}, // TODO: is 262 correct? If it's 263, how does that interact with opening the bottom border?
|
||||
{1, 401, 500} // 72 Hz mode: who knows?
|
||||
};
|
||||
|
||||
/// @returns The correct @c VerticalParams for output at @c frequency.
|
||||
const VerticalParams &vertical_parameters(FieldFrequency frequency) {
|
||||
const VerticalParams &vertical_parameters(Video::FieldFrequency frequency) {
|
||||
return vertical_params[int(frequency)];
|
||||
}
|
||||
|
||||
@ -63,7 +63,7 @@ const struct HorizontalParams {
|
||||
{4*2, 164*2, 184*2, 2*2, 224*2}
|
||||
};
|
||||
|
||||
const HorizontalParams &horizontal_parameters(FieldFrequency frequency) {
|
||||
const HorizontalParams &horizontal_parameters(Video::FieldFrequency frequency) {
|
||||
return horizontal_params[int(frequency)];
|
||||
}
|
||||
|
||||
@ -72,14 +72,14 @@ struct Checker {
|
||||
Checker() {
|
||||
for(int c = 0; c < 3; ++c) {
|
||||
// Expected horizontal order of events: reset blank, enable display, disable display, enable blank (at least 50 before end of line), end of line
|
||||
const auto horizontal = horizontal_parameters(FieldFrequency(c));
|
||||
const auto horizontal = horizontal_parameters(Video::FieldFrequency(c));
|
||||
assert(horizontal.reset_blank < horizontal.set_enable);
|
||||
assert(horizontal.set_enable < horizontal.reset_enable);
|
||||
assert(horizontal.reset_enable < horizontal.set_blank);
|
||||
assert(horizontal.set_blank+50 < horizontal.length);
|
||||
|
||||
// Expected vertical order of events: reset blank, enable display, disable display, enable blank (at least 50 before end of line), end of line
|
||||
const auto vertical = vertical_parameters(FieldFrequency(c));
|
||||
const auto vertical = vertical_parameters(Video::FieldFrequency(c));
|
||||
assert(vertical.set_enable < vertical.reset_enable);
|
||||
assert(vertical.reset_enable < vertical.height);
|
||||
}
|
||||
@ -87,6 +87,8 @@ struct Checker {
|
||||
} checker;
|
||||
#endif
|
||||
|
||||
const int de_delay_period = 28*2;
|
||||
|
||||
}
|
||||
|
||||
Video::Video() :
|
||||
@ -109,8 +111,27 @@ void Video::set_scan_target(Outputs::Display::ScanTarget *scan_target) {
|
||||
void Video::run_for(HalfCycles duration) {
|
||||
const auto horizontal_timings = horizontal_parameters(field_frequency_);
|
||||
const auto vertical_timings = vertical_parameters(field_frequency_);
|
||||
|
||||
int integer_duration = int(duration.as_integral());
|
||||
|
||||
// Effect any changes in visible state out here; they're not relevant in the inner loop.
|
||||
if(!pending_events_.empty()) {
|
||||
auto erase_iterator = pending_events_.begin();
|
||||
int duration_remaining = integer_duration;
|
||||
while(erase_iterator != pending_events_.end()) {
|
||||
erase_iterator->delay -= duration_remaining;
|
||||
if(erase_iterator->delay <= 0) {
|
||||
duration_remaining = -erase_iterator->delay;
|
||||
erase_iterator->apply(public_state_);
|
||||
++erase_iterator;
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
if(erase_iterator != pending_events_.begin()) {
|
||||
pending_events_.erase(pending_events_.begin(), erase_iterator);
|
||||
}
|
||||
}
|
||||
|
||||
while(integer_duration) {
|
||||
// Seed next event to end of line.
|
||||
int next_event = line_length_;
|
||||
@ -132,6 +153,7 @@ void Video::run_for(HalfCycles duration) {
|
||||
|
||||
// Determine current output mode and number of cycles to output for.
|
||||
const int run_length = std::min(integer_duration, next_event - x_);
|
||||
const bool display_enable = vertical_.enable && horizontal_.enable;
|
||||
|
||||
if(horizontal_.sync || vertical_.sync) {
|
||||
shifter_.output_sync(run_length);
|
||||
@ -225,6 +247,12 @@ void Video::run_for(HalfCycles duration) {
|
||||
vertical_ = next_vertical_;
|
||||
y_ = next_y_;
|
||||
}
|
||||
|
||||
// Chuck any deferred output changes into the queue.
|
||||
const bool next_display_enable = vertical_.enable && horizontal_.enable;
|
||||
if(display_enable != next_display_enable) {
|
||||
add_event(de_delay_period - integer_duration, next_display_enable ? Event::Type::SetDisplayEnable : Event::Type::ResetDisplayEnable);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -252,7 +280,7 @@ bool Video::vsync() {
|
||||
}
|
||||
|
||||
bool Video::display_enabled() {
|
||||
return horizontal_.enable && vertical_.enable;
|
||||
return public_state_.display_enable;
|
||||
}
|
||||
|
||||
HalfCycles Video::get_next_sequence_point() {
|
||||
@ -273,29 +301,35 @@ HalfCycles Video::get_next_sequence_point() {
|
||||
// visible area.
|
||||
|
||||
const auto horizontal_timings = horizontal_parameters(field_frequency_);
|
||||
// const auto vertical_timings = vertical_parameters(field_frequency_);
|
||||
|
||||
// If this is a vertically-enabled line, check for the display enable boundaries.
|
||||
int event_time = line_length_; // Worst case: report end of line.
|
||||
|
||||
// If any events are pending, give the first of those the chance to be next.
|
||||
if(!pending_events_.empty()) {
|
||||
event_time = std::min(event_time, x_ + event_time);
|
||||
}
|
||||
|
||||
// If this is a vertically-enabled line, check for the display enable boundaries, + the standard delay.
|
||||
if(vertical_.enable) {
|
||||
// TODO: what if there's a sync event scheduled for this line?
|
||||
if(x_ < horizontal_timings.set_enable) return HalfCycles(horizontal_timings.set_enable - x_);
|
||||
if(x_ < horizontal_timings.reset_enable) return HalfCycles(horizontal_timings.reset_enable - x_);
|
||||
} else {
|
||||
if(vertical_.sync_schedule != VerticalState::SyncSchedule::None && (x_ < 30*2)) {
|
||||
return HalfCycles(30*2 - x_);
|
||||
if(x_ < horizontal_timings.set_enable + de_delay_period) {
|
||||
event_time = std::min(event_time, horizontal_timings.set_enable + de_delay_period);
|
||||
}
|
||||
else if(x_ < horizontal_timings.reset_enable + de_delay_period) {
|
||||
event_time = std::min(event_time, horizontal_timings.reset_enable + de_delay_period);
|
||||
}
|
||||
}
|
||||
|
||||
// Test for beginning and end of sync.
|
||||
if(x_ < line_length_ - 50) return HalfCycles(line_length_ - 50 - x_);
|
||||
if(x_ < line_length_ - 10) return HalfCycles(line_length_ - 10 - x_);
|
||||
// If a vertical sync event is scheduled, test for that.
|
||||
if(vertical_.sync_schedule != VerticalState::SyncSchedule::None && (x_ < 30*2)) {
|
||||
event_time = std::min(event_time, 30*2);
|
||||
}
|
||||
|
||||
// Okay, then, it depends on the next line. If the next line is the start or end of vertical sync,
|
||||
// it's that.
|
||||
// if(y_+1 == vertical_timings.height || y_+1 == 3) return HalfCycles(line_length_ - x_);
|
||||
// Test for beginning and end of horizontal sync.
|
||||
if(x_ < line_length_ - 50*2) event_time = std::min(line_length_ - 50*2, event_time);
|
||||
else if(x_ < line_length_ - 10*2) event_time = std::min(line_length_ - 10*2, event_time);
|
||||
|
||||
// It wasn't any of those, so as a temporary expedient, just supply end of line.
|
||||
return HalfCycles(line_length_ - x_);
|
||||
return HalfCycles(event_time - x_);
|
||||
}
|
||||
|
||||
// MARK: - IO dispatch
|
||||
@ -357,11 +391,11 @@ void Video::write(int address, uint16_t value) {
|
||||
void Video::update_output_mode() {
|
||||
// If this is black and white mode, that's that.
|
||||
switch((video_mode_ >> 8) & 3) {
|
||||
default:
|
||||
case 0: output_bpp_ = OutputBpp::Four; break;
|
||||
case 1: output_bpp_ = OutputBpp::Two; break;
|
||||
|
||||
// 1bpp mode ignores the otherwise-programmed frequency.
|
||||
default:
|
||||
case 2:
|
||||
output_bpp_ = OutputBpp::One;
|
||||
field_frequency_ = FieldFrequency::SeventyTwo;
|
||||
|
@ -12,17 +12,25 @@
|
||||
#include "../../../Outputs/CRT/CRT.hpp"
|
||||
#include "../../../ClockReceiver/ClockReceiver.hpp"
|
||||
|
||||
#include <vector>
|
||||
|
||||
namespace Atari {
|
||||
namespace ST {
|
||||
|
||||
enum class FieldFrequency {
|
||||
Fifty = 0, Sixty = 1, SeventyTwo = 2
|
||||
};
|
||||
|
||||
/*!
|
||||
Models a combination of the parts of the GLUE, MMU and Shifter that in net
|
||||
form the video subsystem of the Atari ST. So not accurate to a real chip, but
|
||||
(hopefully) to a subsystem.
|
||||
*/
|
||||
class Video {
|
||||
public:
|
||||
Video();
|
||||
|
||||
/*!
|
||||
Sets the memory pool that provides video, and its size.
|
||||
*/
|
||||
void set_ram(uint16_t *, size_t size);
|
||||
|
||||
/*!
|
||||
Sets the target device for video data.
|
||||
*/
|
||||
@ -33,21 +41,51 @@ class Video {
|
||||
*/
|
||||
void run_for(HalfCycles duration);
|
||||
|
||||
|
||||
/*!
|
||||
@returns the number of cycles until there is next a change in the hsync,
|
||||
vsync or display_enable outputs.
|
||||
*/
|
||||
HalfCycles get_next_sequence_point();
|
||||
|
||||
/*!
|
||||
@returns @c true if the horizontal sync output is currently active; @c false otherwise.
|
||||
|
||||
@discussion On an Atari ST, this generates a VPA-style interrupt, which is often erroneously
|
||||
documented as being triggered by horizontal blank.
|
||||
*/
|
||||
bool hsync();
|
||||
|
||||
/*!
|
||||
@returns @c true if the vertical sync output is currently active; @c false otherwise.
|
||||
|
||||
@discussion On an Atari ST, this generates a VPA-style interrupt, which is often erroneously
|
||||
documented as being triggered by vertical blank.
|
||||
*/
|
||||
bool vsync();
|
||||
|
||||
/*!
|
||||
@returns @c true if the display enabled output is currently active; @c false otherwise.
|
||||
|
||||
@discussion On an Atari ST this is fed to the MFP. The documentation that I've been able to
|
||||
find implies a total 28-cycle delay between the real delay enabled signal changing and its effect
|
||||
on the 68000 interrupt input via the MFP. As I have yet to determine how much delay is caused
|
||||
by the MFP a full 28-cycle delay is applied by this class. This should be dialled down when the
|
||||
MFP's responsibility is clarified.
|
||||
*/
|
||||
bool display_enabled();
|
||||
|
||||
void set_ram(uint16_t *, size_t size);
|
||||
|
||||
/// @returns the effect of reading from @c address; only the low 6 bits are decoded.
|
||||
uint16_t read(int address);
|
||||
|
||||
/// Writes @c value to @c address, of which only the low 6 bits are decoded.
|
||||
void write(int address, uint16_t value);
|
||||
|
||||
/// Used internally to track state.
|
||||
enum class FieldFrequency {
|
||||
Fifty = 0, Sixty = 1, SeventyTwo = 2
|
||||
};
|
||||
|
||||
private:
|
||||
Outputs::CRT::CRT crt_;
|
||||
|
||||
@ -56,7 +94,7 @@ class Video {
|
||||
int base_address_ = 0;
|
||||
int current_address_ = 0;
|
||||
|
||||
uint16_t *ram_;
|
||||
uint16_t *ram_ = nullptr;
|
||||
uint16_t line_buffer_[256];
|
||||
|
||||
int x_ = 0, y_ = 0, next_y_ = 0;
|
||||
@ -67,7 +105,7 @@ class Video {
|
||||
FieldFrequency field_frequency_ = FieldFrequency::Fifty;
|
||||
enum class OutputBpp {
|
||||
One, Two, Four
|
||||
} output_bpp_;
|
||||
} output_bpp_ = OutputBpp::Four;
|
||||
void update_output_mode();
|
||||
|
||||
struct HorizontalState {
|
||||
@ -92,7 +130,7 @@ class Video {
|
||||
int line_length_ = 1024;
|
||||
|
||||
int data_latch_position_ = 0;
|
||||
uint16_t data_latch_[4];
|
||||
uint16_t data_latch_[4] = {0, 0, 0, 0};
|
||||
void latch_word();
|
||||
|
||||
class Shifter {
|
||||
@ -110,8 +148,8 @@ class Video {
|
||||
enum class OutputMode {
|
||||
Sync, Blank, Border, Pixels
|
||||
} output_mode_ = OutputMode::Sync;
|
||||
uint16_t border_colour_;
|
||||
OutputBpp bpp_;
|
||||
uint16_t border_colour_ = 0;
|
||||
OutputBpp bpp_ = OutputBpp::Four;
|
||||
union {
|
||||
uint64_t output_shifter_;
|
||||
uint32_t shifter_halves_[2];
|
||||
@ -119,12 +157,59 @@ class Video {
|
||||
|
||||
void flush_output(OutputMode next_mode);
|
||||
|
||||
uint16_t *pixel_buffer_;
|
||||
uint16_t *pixel_buffer_ = nullptr;
|
||||
size_t pixel_pointer_ = 0;
|
||||
|
||||
Outputs::CRT::CRT &crt_;
|
||||
uint16_t *palette_ = nullptr;
|
||||
} shifter_;
|
||||
|
||||
/// Contains copies of the various observeable fields, after the relevant propagation delay.
|
||||
struct PublicState {
|
||||
bool display_enable = false;
|
||||
} public_state_;
|
||||
|
||||
struct Event {
|
||||
int delay;
|
||||
enum class Type {
|
||||
SetDisplayEnable, ResetDisplayEnable
|
||||
} type;
|
||||
|
||||
Event(Type type, int delay) : delay(delay), type(type) {}
|
||||
|
||||
void apply(PublicState &state) {
|
||||
apply(type, state);
|
||||
}
|
||||
|
||||
static void apply(Type type, PublicState &state) {
|
||||
switch(type) {
|
||||
default:
|
||||
case Type::SetDisplayEnable: state.display_enable = true; break;
|
||||
case Type::ResetDisplayEnable: state.display_enable = false; break;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
std::vector<Event> pending_events_;
|
||||
void add_event(int delay, Event::Type type) {
|
||||
// Apply immediately if there's no delay (or a negative delay).
|
||||
if(delay <= 0) {
|
||||
Event::apply(type, public_state_);
|
||||
return;
|
||||
}
|
||||
|
||||
// Otherwise enqueue, having subtracted the delay for any preceding events,
|
||||
// and subtracting from the subsequent, if any.
|
||||
auto insertion_point = pending_events_.begin();
|
||||
while(insertion_point != pending_events_.end() && insertion_point->delay > delay) {
|
||||
delay -= insertion_point->delay;
|
||||
++insertion_point;
|
||||
}
|
||||
if(insertion_point != pending_events_.end()) {
|
||||
insertion_point->delay -= delay;
|
||||
}
|
||||
pending_events_.emplace(insertion_point, type, delay);
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
|
@ -709,6 +709,7 @@
|
||||
4BDDBA991EF3451200347E61 /* Z80MachineCycleTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4BDDBA981EF3451200347E61 /* Z80MachineCycleTests.swift */; };
|
||||
4BE0A3EE237BB170002AB46F /* ST.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4BE0A3EC237BB170002AB46F /* ST.cpp */; };
|
||||
4BE0A3EF237BB170002AB46F /* ST.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4BE0A3EC237BB170002AB46F /* ST.cpp */; };
|
||||
4BE34438238389E10058E78F /* AtariSTVideoTests.mm in Sources */ = {isa = PBXBuildFile; fileRef = 4BE34437238389E10058E78F /* AtariSTVideoTests.mm */; };
|
||||
4BE76CF922641ED400ACD6FA /* QLTests.mm in Sources */ = {isa = PBXBuildFile; fileRef = 4BE76CF822641ED300ACD6FA /* QLTests.mm */; };
|
||||
4BE90FFD22D5864800FB464D /* MacintoshVideoTests.mm in Sources */ = {isa = PBXBuildFile; fileRef = 4BE90FFC22D5864800FB464D /* MacintoshVideoTests.mm */; };
|
||||
4BE9A6B11EDE293000CBCB47 /* zexdoc.com in Resources */ = {isa = PBXBuildFile; fileRef = 4BE9A6B01EDE293000CBCB47 /* zexdoc.com */; };
|
||||
@ -1582,6 +1583,7 @@
|
||||
4BE32314205328FF006EF799 /* Target.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = Target.hpp; sourceTree = "<group>"; };
|
||||
4BE3231520532AA7006EF799 /* Target.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = Target.hpp; sourceTree = "<group>"; };
|
||||
4BE3231620532BED006EF799 /* Target.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = Target.hpp; sourceTree = "<group>"; };
|
||||
4BE34437238389E10058E78F /* AtariSTVideoTests.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = AtariSTVideoTests.mm; sourceTree = "<group>"; };
|
||||
4BE76CF822641ED300ACD6FA /* QLTests.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = QLTests.mm; sourceTree = "<group>"; };
|
||||
4BE845201F2FF7F100A5EA22 /* CRTC6845.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; name = CRTC6845.hpp; path = 6845/CRTC6845.hpp; sourceTree = "<group>"; };
|
||||
4BE90FFC22D5864800FB464D /* MacintoshVideoTests.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; path = MacintoshVideoTests.mm; sourceTree = "<group>"; };
|
||||
@ -3138,6 +3140,7 @@
|
||||
4BB73EB51B587A5100552FC2 /* Clock SignalTests */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
4BE34437238389E10058E78F /* AtariSTVideoTests.mm */,
|
||||
4B85322922778E4200F26553 /* Comparative68000.hpp */,
|
||||
4B90467222C6FA31000E2074 /* TestRunner68000.hpp */,
|
||||
4B97ADC722C6FD9B00A22A41 /* 68000ArithmeticTests.mm */,
|
||||
@ -4492,6 +4495,7 @@
|
||||
4BD388882239E198002D14B5 /* 68000Tests.mm in Sources */,
|
||||
4BA91E1D216D85BA00F79557 /* MasterSystemVDPTests.mm in Sources */,
|
||||
4B98A0611FFADCDE00ADF63B /* MSXStaticAnalyserTests.mm in Sources */,
|
||||
4BE34438238389E10058E78F /* AtariSTVideoTests.mm in Sources */,
|
||||
4BEF6AAC1D35D1C400E73575 /* DPLLTests.swift in Sources */,
|
||||
4BE76CF922641ED400ACD6FA /* QLTests.mm in Sources */,
|
||||
4B3BA0CF1D318B44005DD7A7 /* MOS6522Bridge.mm in Sources */,
|
||||
|
@ -67,7 +67,7 @@
|
||||
</Testables>
|
||||
</TestAction>
|
||||
<LaunchAction
|
||||
buildConfiguration = "Release"
|
||||
buildConfiguration = "Debug"
|
||||
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
|
||||
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
|
||||
enableASanStackUseAfterReturn = "YES"
|
||||
|
@ -10,9 +10,9 @@
|
||||
|
||||
#import <CommonCrypto/CommonDigest.h>
|
||||
#include "../../../Analyser/Static/StaticAnalyser.hpp"
|
||||
#include "../../../Analyser/Static/Atari/Target.hpp"
|
||||
#include "../../../Analyser/Static/Atari2600/Target.hpp"
|
||||
|
||||
using PagingModel = Analyser::Static::Atari::Target::PagingModel;
|
||||
using PagingModel = Analyser::Static::Atari2600::Target::PagingModel;
|
||||
|
||||
@interface AtariROMRecord : NSObject
|
||||
@property(nonatomic, readonly) PagingModel pagingModel;
|
||||
@ -601,7 +601,7 @@ static NSDictionary<NSString *, AtariROMRecord *> *romRecordsBySHA1 = @{
|
||||
if(!romRecord) continue;
|
||||
|
||||
// assert equality
|
||||
Analyser::Static::Atari::Target *atari_target = dynamic_cast<Analyser::Static::Atari::Target *>(targets.front().get());
|
||||
auto *const atari_target = dynamic_cast<Analyser::Static::Atari2600::Target *>(targets.front().get());
|
||||
XCTAssert(atari_target != nullptr);
|
||||
XCTAssert(atari_target->paging_model == romRecord.pagingModel, @"%@; should be %d, is %d", testFile, romRecord.pagingModel, atari_target->paging_model);
|
||||
XCTAssert(atari_target->uses_superchip == romRecord.usesSuperchip, @"%@; should be %@", testFile, romRecord.usesSuperchip ? @"true" : @"false");
|
||||
|
@ -3040,7 +3040,7 @@ struct ProcessorStorageConstructor {
|
||||
// Throw in the interrupt program.
|
||||
const auto interrupt_pointer = storage_.all_micro_ops_.size();
|
||||
|
||||
// WORKAROUND FOR THE BE68000 MAIN LOOP. Hopefully temporary.
|
||||
// WORKAROUND FOR THE 68000 MAIN LOOP. Hopefully temporary.
|
||||
op(Action::None, seq(""));
|
||||
|
||||
// Perform a single write and then a cycle that will obtain an interrupt vector, or else dictate an autovector or a spurious interrupt.
|
||||
|
Loading…
Reference in New Issue
Block a user