mirror of
				https://github.com/TomHarte/CLK.git
				synced 2025-10-31 05:16:08 +00:00 
			
		
		
		
	Compare commits
	
		
			36 Commits
		
	
	
		
			2021-07-03
			...
			2021-07-09
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
|  | 5810a1a98e | ||
|  | a4c011e3c0 | ||
|  | 337fd15dc0 | ||
|  | 9bc94f4536 | ||
|  | 3f4cf35384 | ||
|  | 4dd7f2cc09 | ||
|  | 1b29cc34c4 | ||
|  | 53c3c1f5ab | ||
|  | 6225abd751 | ||
|  | c6fcd9a1eb | ||
|  | 30fbb6ea53 | ||
|  | 0e49258546 | ||
|  | 264b8dfb28 | ||
|  | 6a15b8f695 | ||
|  | 5167d256cc | ||
|  | 16bd826491 | ||
|  | 55af8fa5d9 | ||
|  | 1ec8ff20af | ||
|  | 99a65d3297 | ||
|  | 94907b51aa | ||
|  | 0085265d13 | ||
|  | 8e0893bd42 | ||
|  | 704dc9bdcb | ||
|  | 7a673a2448 | ||
|  | 33e2a4b21c | ||
|  | 3e6b804896 | ||
|  | e98165a657 | ||
|  | 2a7727d12b | ||
|  | c20e8f4062 | ||
|  | 4ca9db7d49 | ||
|  | 4add48cffb | ||
|  | adbfb009f8 | ||
|  | 43ceca8711 | ||
|  | 3ef28a4f03 | ||
|  | adcd580d5b | ||
|  | 5715c9183f | 
| @@ -67,7 +67,7 @@ Analyser::Static::TargetList Analyser::Static::Enterprise::GetTargets(const Medi | |||||||
|  |  | ||||||
| 			if(!has_exdos_ini) { | 			if(!has_exdos_ini) { | ||||||
| 				if(did_pick_file) { | 				if(did_pick_file) { | ||||||
| 					target->loading_command = std::string("run \"") + selected_file->name + "." + selected_file->extension + "\""; | 					target->loading_command = std::string("run \"") + selected_file->name + "." + selected_file->extension + "\"\n"; | ||||||
| 				} else { | 				} else { | ||||||
| 					target->loading_command = ":dir\n"; | 					target->loading_command = ":dir\n"; | ||||||
| 				} | 				} | ||||||
|   | |||||||
| @@ -24,11 +24,13 @@ struct Target: public Analyser::Static::Target, public Reflection::StructImpl<Ta | |||||||
| 	ReflectableEnum(EXOSVersion, v10, v20, v21, v23, Any); | 	ReflectableEnum(EXOSVersion, v10, v20, v21, v23, Any); | ||||||
| 	ReflectableEnum(BASICVersion, v10, v11, v21, Any, None); | 	ReflectableEnum(BASICVersion, v10, v11, v21, Any, None); | ||||||
| 	ReflectableEnum(DOS, EXDOS, None); | 	ReflectableEnum(DOS, EXDOS, None); | ||||||
|  | 	ReflectableEnum(Speed, FourMHz, SixMHz); | ||||||
|  |  | ||||||
| 	Model model = Model::Enterprise128; | 	Model model = Model::Enterprise128; | ||||||
| 	EXOSVersion exos_version = EXOSVersion::Any; | 	EXOSVersion exos_version = EXOSVersion::Any; | ||||||
| 	BASICVersion basic_version = BASICVersion::None; | 	BASICVersion basic_version = BASICVersion::None; | ||||||
| 	DOS dos = DOS::None; | 	DOS dos = DOS::None; | ||||||
|  | 	Speed speed = Speed::FourMHz; | ||||||
| 	std::string loading_command; | 	std::string loading_command; | ||||||
|  |  | ||||||
| 	Target() : Analyser::Static::Target(Machine::Enterprise) { | 	Target() : Analyser::Static::Target(Machine::Enterprise) { | ||||||
| @@ -37,11 +39,13 @@ struct Target: public Analyser::Static::Target, public Reflection::StructImpl<Ta | |||||||
| 			AnnounceEnum(EXOSVersion); | 			AnnounceEnum(EXOSVersion); | ||||||
| 			AnnounceEnum(BASICVersion); | 			AnnounceEnum(BASICVersion); | ||||||
| 			AnnounceEnum(DOS); | 			AnnounceEnum(DOS); | ||||||
|  | 			AnnounceEnum(Speed); | ||||||
|  |  | ||||||
| 			DeclareField(model); | 			DeclareField(model); | ||||||
| 			DeclareField(exos_version); | 			DeclareField(exos_version); | ||||||
| 			DeclareField(basic_version); | 			DeclareField(basic_version); | ||||||
| 			DeclareField(dos); | 			DeclareField(dos); | ||||||
|  | 			DeclareField(speed); | ||||||
| 		} | 		} | ||||||
| 	} | 	} | ||||||
| }; | }; | ||||||
|   | |||||||
| @@ -16,7 +16,7 @@ Audio::Audio(Concurrency::DeferringAsyncTaskQueue &audio_queue) : | |||||||
| 	audio_queue_(audio_queue) {} | 	audio_queue_(audio_queue) {} | ||||||
|  |  | ||||||
| void Audio::write(uint16_t address, uint8_t value) { | void Audio::write(uint16_t address, uint8_t value) { | ||||||
| 	address &= 0xf; | 	address &= 0x1f; | ||||||
| 	audio_queue_.defer([address, value, this] { | 	audio_queue_.defer([address, value, this] { | ||||||
| 		switch(address) { | 		switch(address) { | ||||||
| 			case 0:	case 2:	case 4: | 			case 0:	case 2:	case 4: | ||||||
| @@ -54,6 +54,10 @@ void Audio::write(uint16_t address, uint8_t value) { | |||||||
| 			break; | 			break; | ||||||
| 			case 11:	noise_.amplitude[0] = value & 0x3f;		break; | 			case 11:	noise_.amplitude[0] = value & 0x3f;		break; | ||||||
| 			case 15:	noise_.amplitude[1] = value & 0x3f;		break; | 			case 15:	noise_.amplitude[1] = value & 0x3f;		break; | ||||||
|  |  | ||||||
|  | 			case 31: | ||||||
|  | 				global_divider_reload_ = 2 + ((value >> 1)&1); | ||||||
|  | 			break; | ||||||
| 		} | 		} | ||||||
| 	}); | 	}); | ||||||
| } | } | ||||||
| @@ -65,37 +69,74 @@ void Audio::set_sample_volume_range(int16_t range) { | |||||||
| } | } | ||||||
|  |  | ||||||
| void Audio::update_channel(int c) { | void Audio::update_channel(int c) { | ||||||
| 	if(channels_[c].sync) { |  | ||||||
| 		channels_[c].count = channels_[c].reload; |  | ||||||
| 		channels_[c].output <<= 1; |  | ||||||
| 		return; |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	auto output = channels_[c].output & 1; | 	auto output = channels_[c].output & 1; | ||||||
| 	channels_[c].output <<= 1; | 	channels_[c].output <<= 1; | ||||||
| 	if(!channels_[c].count) { | 	if(channels_[c].sync) { | ||||||
| 		channels_[c].count = channels_[c].reload; | 		channels_[c].count = channels_[c].reload; | ||||||
|  | 		output = 0; | ||||||
|  | 	} else { | ||||||
|  | 		if(!channels_[c].count) { | ||||||
|  | 			channels_[c].count = channels_[c].reload; | ||||||
|  |  | ||||||
| 		if(channels_[c].distortion == Channel::Distortion::None) | 			if(channels_[c].distortion == Channel::Distortion::None) | ||||||
| 			output ^= 1; | 				output ^= 1; | ||||||
| 		else | 			else | ||||||
| 			output = poly_state_[int(channels_[c].distortion)]; | 				output = poly_state_[int(channels_[c].distortion)]; | ||||||
|  | 		} else { | ||||||
|  | 			--channels_[c].count; | ||||||
|  | 		} | ||||||
|  |  | ||||||
| 		if(channels_[c].high_pass && (channels_[(c+1)%3].output&3) == 2) { | 		if(channels_[c].high_pass && (channels_[(c+1)%3].output&3) == 2) { | ||||||
| 			output = 0; | 			output = 0; | ||||||
| 		} | 		} | ||||||
| 		if(channels_[c].ring_modulate) { | 	} | ||||||
| 			output = ~(output ^ channels_[(c+2)%3].output) & 1; |  | ||||||
| 		} | 	// Ring modulation applies even when sync is enabled, per SIDBasic. | ||||||
| 	} else { | 	if(channels_[c].ring_modulate) { | ||||||
| 		--channels_[c].count; | 		output = ~(output ^ channels_[(c+2)%3].output) & 1; | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	channels_[c].output |= output; | 	channels_[c].output |= output; | ||||||
| } | } | ||||||
|  |  | ||||||
| void Audio::get_samples(std::size_t number_of_samples, int16_t *target) { | void Audio::get_samples(std::size_t number_of_samples, int16_t *target) { | ||||||
| 	for(size_t c = 0; c < number_of_samples; c++) { | 	int16_t output_level[2]; | ||||||
|  | 	size_t c = 0; | ||||||
|  | 	while(c < number_of_samples) { | ||||||
|  | 		// I'm unclear on the details of the time division multiplexing so, | ||||||
|  | 		// for now, just sum the outputs. | ||||||
|  | 		output_level[0] = | ||||||
|  | 			volume_ * | ||||||
|  | 				(use_direct_output_[0] ? | ||||||
|  | 					channels_[0].amplitude[0] | ||||||
|  | 					: ( | ||||||
|  | 						channels_[0].amplitude[0] * (channels_[0].output & 1) + | ||||||
|  | 						channels_[1].amplitude[0] * (channels_[1].output & 1) + | ||||||
|  | 						channels_[2].amplitude[0] * (channels_[2].output & 1) + | ||||||
|  | 						noise_.amplitude[0] * noise_.final_output | ||||||
|  | 				)); | ||||||
|  |  | ||||||
|  | 		output_level[1] = | ||||||
|  | 			volume_ * | ||||||
|  | 				(use_direct_output_[1] ? | ||||||
|  | 					channels_[0].amplitude[1] | ||||||
|  | 					: ( | ||||||
|  | 						channels_[0].amplitude[1] * (channels_[0].output & 1) + | ||||||
|  | 						channels_[1].amplitude[1] * (channels_[1].output & 1) + | ||||||
|  | 						channels_[2].amplitude[1] * (channels_[2].output & 1) + | ||||||
|  | 						noise_.amplitude[1] * noise_.final_output | ||||||
|  | 				)); | ||||||
|  |  | ||||||
|  | 		while(global_divider_ && c < number_of_samples) { | ||||||
|  | 			--global_divider_; | ||||||
|  | 			*reinterpret_cast<uint32_t *>(&target[c << 1]) = *reinterpret_cast<uint32_t *>(output_level); | ||||||
|  | 			++c; | ||||||
|  | 		} | ||||||
|  |  | ||||||
|  | 		global_divider_ = global_divider_reload_; | ||||||
|  | 		if(!global_divider_) { | ||||||
|  | 			global_divider_ = global_divider_reload_; | ||||||
|  | 		} | ||||||
| 		poly_state_[int(Channel::Distortion::FourBit)] = poly4_.next(); | 		poly_state_[int(Channel::Distortion::FourBit)] = poly4_.next(); | ||||||
| 		poly_state_[int(Channel::Distortion::FiveBit)] = poly5_.next(); | 		poly_state_[int(Channel::Distortion::FiveBit)] = poly5_.next(); | ||||||
| 		poly_state_[int(Channel::Distortion::SevenBit)] = poly7_.next(); | 		poly_state_[int(Channel::Distortion::SevenBit)] = poly7_.next(); | ||||||
| @@ -162,30 +203,6 @@ void Audio::get_samples(std::size_t number_of_samples, int16_t *target) { | |||||||
| 		} else { | 		} else { | ||||||
| 			noise_.final_output = noise_.output & 1; | 			noise_.final_output = noise_.output & 1; | ||||||
| 		} | 		} | ||||||
|  |  | ||||||
| 		// I'm unclear on the details of the time division multiplexing so, |  | ||||||
| 		// for now, just sum the outputs. |  | ||||||
| 		target[(c << 1) + 0] = |  | ||||||
| 			volume_ * |  | ||||||
| 				(use_direct_output_[0] ? |  | ||||||
| 					channels_[0].amplitude[0] |  | ||||||
| 					: ( |  | ||||||
| 						channels_[0].amplitude[0] * (channels_[0].output & 1) + |  | ||||||
| 						channels_[1].amplitude[0] * (channels_[1].output & 1) + |  | ||||||
| 						channels_[2].amplitude[0] * (channels_[2].output & 1) + |  | ||||||
| 						noise_.amplitude[0] * noise_.final_output |  | ||||||
| 				)); |  | ||||||
|  |  | ||||||
| 		target[(c << 1) + 1] = |  | ||||||
| 			volume_ * |  | ||||||
| 				(use_direct_output_[1] ? |  | ||||||
| 					channels_[0].amplitude[1] |  | ||||||
| 					: ( |  | ||||||
| 						channels_[0].amplitude[1] * (channels_[0].output & 1) + |  | ||||||
| 						channels_[1].amplitude[1] * (channels_[1].output & 1) + |  | ||||||
| 						channels_[2].amplitude[1] * (channels_[2].output & 1) + |  | ||||||
| 						noise_.amplitude[1] * noise_.final_output |  | ||||||
| 				)); |  | ||||||
| 	} | 	} | ||||||
| } | } | ||||||
|  |  | ||||||
| @@ -198,7 +215,7 @@ uint8_t TimedInterruptSource::get_new_interrupts() { | |||||||
| } | } | ||||||
|  |  | ||||||
| void TimedInterruptSource::write(uint16_t address, uint8_t value) { | void TimedInterruptSource::write(uint16_t address, uint8_t value) { | ||||||
| 	address &= 15; | 	address &= 0x1f; | ||||||
| 	switch(address) { | 	switch(address) { | ||||||
| 		default: break; | 		default: break; | ||||||
|  |  | ||||||
| @@ -209,21 +226,15 @@ void TimedInterruptSource::write(uint16_t address, uint8_t value) { | |||||||
| 			channels_[address >> 1].reload = uint16_t((channels_[address >> 1].reload & 0x00ff) | ((value & 0xf) << 8)); | 			channels_[address >> 1].reload = uint16_t((channels_[address >> 1].reload & 0x00ff) | ((value & 0xf) << 8)); | ||||||
| 		break; | 		break; | ||||||
|  |  | ||||||
| 		case 7: { | 		case 7: | ||||||
| 			channels_[0].sync = value & 0x01; | 			channels_[0].sync = value & 0x01; | ||||||
| 			channels_[1].sync = value & 0x02; | 			channels_[1].sync = value & 0x02; | ||||||
|  | 			rate_ = InterruptRate((value >> 5) & 3); | ||||||
|  | 		break; | ||||||
|  |  | ||||||
| 			const InterruptRate rate = InterruptRate((value >> 5) & 3); | 		case 31: | ||||||
| 			if(rate != rate_) { | 			global_divider_ = Cycles(2 + ((value >> 1)&1)); | ||||||
| 				rate_ = rate; | 		break; | ||||||
|  |  | ||||||
| 				if(rate_ >= InterruptRate::ToneGenerator0) { |  | ||||||
| 					programmable_level_ = channels_[int(rate_) - int(InterruptRate::ToneGenerator0)].level; |  | ||||||
| 				} else { |  | ||||||
| 					programmable_offset_ = programmble_reload(rate_); |  | ||||||
| 				} |  | ||||||
| 			} |  | ||||||
| 		} break; |  | ||||||
| 	} | 	} | ||||||
| } | } | ||||||
|  |  | ||||||
| @@ -249,6 +260,7 @@ void TimedInterruptSource::update_channel(int c, bool is_linked, int decrement) | |||||||
| 			// from high to low is amongst the included flips. | 			// from high to low is amongst the included flips. | ||||||
| 			if(is_linked && num_flips + channels_[c].level >= 2) { | 			if(is_linked && num_flips + channels_[c].level >= 2) { | ||||||
| 				interrupts_ |= uint8_t(Interrupt::VariableFrequency); | 				interrupts_ |= uint8_t(Interrupt::VariableFrequency); | ||||||
|  | 				programmable_level_ ^= true; | ||||||
| 			} | 			} | ||||||
| 			channels_[c].level ^= (num_flips & 1); | 			channels_[c].level ^= (num_flips & 1); | ||||||
|  |  | ||||||
| @@ -259,68 +271,67 @@ void TimedInterruptSource::update_channel(int c, bool is_linked, int decrement) | |||||||
| 	} | 	} | ||||||
| } | } | ||||||
|  |  | ||||||
| void TimedInterruptSource::run_for(Cycles cycles) { | void TimedInterruptSource::run_for(Cycles duration) { | ||||||
| 	// Update the 1Hz interrupt. | 	// Determine total number of ticks. | ||||||
| 	one_hz_offset_ -= cycles; | 	run_length_ += duration; | ||||||
| 	if(one_hz_offset_ <= Cycles(0)) { | 	const Cycles cycles = run_length_.divide(global_divider_); | ||||||
|  | 	if(cycles == Cycles(0)) { | ||||||
|  | 		return; | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	// Update the two-second counter, from which the 1Hz, 50Hz and 1000Hz signals | ||||||
|  | 	// are derived. | ||||||
|  | 	const int previous_counter = two_second_counter_; | ||||||
|  | 	two_second_counter_ = (two_second_counter_ + cycles.as<int>()) % 500'000; | ||||||
|  |  | ||||||
|  | 	// Check for a 1Hz rollover. | ||||||
|  | 	if(previous_counter / 250'000 != two_second_counter_ / 250'000) { | ||||||
| 		interrupts_ |= uint8_t(Interrupt::OneHz); | 		interrupts_ |= uint8_t(Interrupt::OneHz); | ||||||
| 		one_hz_offset_ += clock_rate; | 	} | ||||||
|  |  | ||||||
|  | 	// Check for 1kHz or 50Hz rollover; | ||||||
|  | 	switch(rate_) { | ||||||
|  | 		default: break; | ||||||
|  | 		case InterruptRate::OnekHz: | ||||||
|  | 			if(previous_counter / 250 != two_second_counter_ / 250) { | ||||||
|  | 				interrupts_ |= uint8_t(Interrupt::VariableFrequency); | ||||||
|  | 				programmable_level_ ^= true; | ||||||
|  | 			} | ||||||
|  | 		break; | ||||||
|  | 		case InterruptRate::FiftyHz: | ||||||
|  | 			if(previous_counter / 5'000 != two_second_counter_ / 5'000) { | ||||||
|  | 				interrupts_ |= uint8_t(Interrupt::VariableFrequency); | ||||||
|  | 				programmable_level_ ^= true; | ||||||
|  | 			} | ||||||
|  | 		break; | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	// Update the two tone channels. | 	// Update the two tone channels. | ||||||
| 	update_channel(0, rate_ == InterruptRate::ToneGenerator0, cycles.as<int>()); | 	update_channel(0, rate_ == InterruptRate::ToneGenerator0, cycles.as<int>()); | ||||||
| 	update_channel(1, rate_ == InterruptRate::ToneGenerator1, cycles.as<int>()); | 	update_channel(1, rate_ == InterruptRate::ToneGenerator1, cycles.as<int>()); | ||||||
|  |  | ||||||
| 	// Update the programmable-frequency interrupt. |  | ||||||
| 	if(rate_ < InterruptRate::ToneGenerator0) { |  | ||||||
| 		programmable_offset_ -= cycles.as<int>(); |  | ||||||
| 		if(programmable_offset_ <= 0) { |  | ||||||
| 			if(programmable_level_) { |  | ||||||
| 				interrupts_ |= uint8_t(Interrupt::VariableFrequency); |  | ||||||
| 			} |  | ||||||
| 			programmable_level_ ^= true; |  | ||||||
| 			programmable_offset_ = programmble_reload(rate_); |  | ||||||
| 		} |  | ||||||
| 	} |  | ||||||
| } | } | ||||||
|  |  | ||||||
| Cycles TimedInterruptSource::get_next_sequence_point() const { | Cycles TimedInterruptSource::get_next_sequence_point() const { | ||||||
| 	int result = one_hz_offset_.as<int>(); | 	// Since both the 1kHz and 50Hz timers are integer dividers of the 1Hz timer, there's no need | ||||||
|  | 	// to factor that one in when determining the next sequence point for either of those. | ||||||
| 	switch(rate_) { | 	switch(rate_) { | ||||||
| 		case InterruptRate::OnekHz: | 		default: | ||||||
| 		case InterruptRate::FiftyHz: | 		case InterruptRate::OnekHz:		return Cycles(250 - (two_second_counter_ % 250)); | ||||||
| 			result = std::min(result, programmable_offset_ + (!programmable_level_) * programmble_reload(rate_)); | 		case InterruptRate::FiftyHz:	return Cycles(5000 - (two_second_counter_ % 5000)); | ||||||
| 		break; |  | ||||||
| 		case InterruptRate::ToneGenerator0: | 		case InterruptRate::ToneGenerator0: | ||||||
| 		case InterruptRate::ToneGenerator1: { | 		case InterruptRate::ToneGenerator1: { | ||||||
| 			const auto &channel = channels_[int(rate_) - int(InterruptRate::ToneGenerator0)]; | 			const auto &channel = channels_[int(rate_) - int(InterruptRate::ToneGenerator0)]; | ||||||
| 			const int cycles_until_interrupt = channel.value + 1 + (!channel.level) * (channel.reload + 1); | 			const int cycles_until_interrupt = channel.value + 1 + (!channel.level) * (channel.reload + 1); | ||||||
| 			result = std::min(result, cycles_until_interrupt); |  | ||||||
| 		} break; |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	return Cycles(result); | 			return Cycles(std::min( | ||||||
|  | 				250'000 - (two_second_counter_ % 250'000), | ||||||
|  | 				cycles_until_interrupt | ||||||
|  | 			)); | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
| } | } | ||||||
|  |  | ||||||
| uint8_t TimedInterruptSource::get_divider_state() { | uint8_t TimedInterruptSource::get_divider_state() { | ||||||
| 	bool programmable_flag = false; | 	return uint8_t((two_second_counter_ / 250'000) * 4 | programmable_level_); | ||||||
| 	switch(rate_) { |  | ||||||
| 		case InterruptRate::OnekHz: |  | ||||||
| 		case InterruptRate::FiftyHz: |  | ||||||
| 			programmable_flag = programmable_level_; |  | ||||||
| 		break; |  | ||||||
| 		case InterruptRate::ToneGenerator0: |  | ||||||
| 			programmable_flag = channels_[0].level; |  | ||||||
| 		break; |  | ||||||
| 		case InterruptRate::ToneGenerator1: |  | ||||||
| 			programmable_flag = channels_[1].level; |  | ||||||
| 		break; |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	// one_hz_offset_ counts downwards, so when it crosses the halfway mark |  | ||||||
| 	// it enters the high part of its wave. |  | ||||||
| 	return |  | ||||||
| 		(one_hz_offset_ < half_clock_rate ? 0x4 : 0x0) | |  | ||||||
| 		(programmable_flag ? 0x1 : 0x0); |  | ||||||
| } | } | ||||||
|   | |||||||
| @@ -45,6 +45,10 @@ class Audio: public Outputs::Speaker::SampleSource { | |||||||
| 	private: | 	private: | ||||||
| 		Concurrency::DeferringAsyncTaskQueue &audio_queue_; | 		Concurrency::DeferringAsyncTaskQueue &audio_queue_; | ||||||
|  |  | ||||||
|  | 		// Global divider (i.e. 8MHz/12Mhz switch). | ||||||
|  | 		uint8_t global_divider_; | ||||||
|  | 		uint8_t global_divider_reload_ = 2; | ||||||
|  |  | ||||||
| 		// Tone channels. | 		// Tone channels. | ||||||
| 		struct Channel { | 		struct Channel { | ||||||
| 			// User-set values. | 			// User-set values. | ||||||
| @@ -147,12 +151,16 @@ class TimedInterruptSource { | |||||||
| 		static constexpr Cycles clock_rate{250000}; | 		static constexpr Cycles clock_rate{250000}; | ||||||
| 		static constexpr Cycles half_clock_rate{125000}; | 		static constexpr Cycles half_clock_rate{125000}; | ||||||
|  |  | ||||||
|  | 		// Global divider (i.e. 8MHz/12Mhz switch). | ||||||
|  | 		Cycles global_divider_ = Cycles(2); | ||||||
|  | 		Cycles run_length_; | ||||||
|  |  | ||||||
| 		// Interrupts that have fired since get_new_interrupts() | 		// Interrupts that have fired since get_new_interrupts() | ||||||
| 		// was last called. | 		// was last called. | ||||||
| 		uint8_t interrupts_ = 0; | 		uint8_t interrupts_ = 0; | ||||||
|  |  | ||||||
| 		// A counter for the 1Hz interrupt. | 		// A counter for the 1Hz interrupt. | ||||||
| 		Cycles one_hz_offset_ = clock_rate; | 		int two_second_counter_ = 0; | ||||||
|  |  | ||||||
| 		// A counter specific to the 1kHz and 50Hz timers, if in use. | 		// A counter specific to the 1kHz and 50Hz timers, if in use. | ||||||
| 		enum class InterruptRate { | 		enum class InterruptRate { | ||||||
| @@ -161,7 +169,6 @@ class TimedInterruptSource { | |||||||
| 			ToneGenerator0, | 			ToneGenerator0, | ||||||
| 			ToneGenerator1, | 			ToneGenerator1, | ||||||
| 		} rate_ = InterruptRate::OnekHz; | 		} rate_ = InterruptRate::OnekHz; | ||||||
| 		int programmable_offset_ = programmble_reload(InterruptRate::OnekHz); |  | ||||||
| 		bool programmable_level_ = false; | 		bool programmable_level_ = false; | ||||||
|  |  | ||||||
| 		// A local duplicate of the counting state of the first two audio | 		// A local duplicate of the counting state of the first two audio | ||||||
| @@ -173,13 +180,6 @@ class TimedInterruptSource { | |||||||
| 			bool level = false; | 			bool level = false; | ||||||
| 		} channels_[2]; | 		} channels_[2]; | ||||||
| 		void update_channel(int c, bool is_linked, int decrement); | 		void update_channel(int c, bool is_linked, int decrement); | ||||||
| 		static constexpr int programmble_reload(InterruptRate rate) { |  | ||||||
| 			switch(rate) { |  | ||||||
| 				default: return 0; |  | ||||||
| 				case InterruptRate::OnekHz:		return 125; |  | ||||||
| 				case InterruptRate::FiftyHz:	return 2500; |  | ||||||
| 			} |  | ||||||
| 		} |  | ||||||
| }; | }; | ||||||
|  |  | ||||||
| } | } | ||||||
|   | |||||||
| @@ -65,7 +65,7 @@ namespace Enterprise { | |||||||
|  |  | ||||||
| */ | */ | ||||||
|  |  | ||||||
| template <bool has_disk_controller> class ConcreteMachine: | template <bool has_disk_controller, bool is_6mhz> class ConcreteMachine: | ||||||
| 	public Activity::Source, | 	public Activity::Source, | ||||||
| 	public Configurable::Device, | 	public Configurable::Device, | ||||||
| 	public CPU::Z80::BusHandler, | 	public CPU::Z80::BusHandler, | ||||||
| @@ -88,15 +88,23 @@ template <bool has_disk_controller> class ConcreteMachine: | |||||||
| 			return uint8_t(0x100 - ram_size / 0x4000); | 			return uint8_t(0x100 - ram_size / 0x4000); | ||||||
| 		} | 		} | ||||||
|  |  | ||||||
|  | 		static constexpr double clock_rate = is_6mhz ? 6'000'000.0 : 4'000'000.0; | ||||||
|  | 		using NickType = | ||||||
|  | 			std::conditional_t<is_6mhz, | ||||||
|  | 				JustInTimeActor<Nick, HalfCycles, 13478201, 5680000>, | ||||||
|  | 				JustInTimeActor<Nick, HalfCycles, 40434603, 11360000>>; | ||||||
|  |  | ||||||
| 	public: | 	public: | ||||||
| 		ConcreteMachine([[maybe_unused]] const Analyser::Static::Enterprise::Target &target, const ROMMachine::ROMFetcher &rom_fetcher) : | 		ConcreteMachine(const Analyser::Static::Enterprise::Target &target, const ROMMachine::ROMFetcher &rom_fetcher) : | ||||||
| 			min_ram_slot_(min_ram_slot(target)), | 			min_ram_slot_(min_ram_slot(target)), | ||||||
| 			z80_(*this), | 			z80_(*this), | ||||||
| 			nick_(ram_.end() - 65536), | 			nick_(ram_.end() - 65536), | ||||||
| 			dave_audio_(audio_queue_), | 			dave_audio_(audio_queue_), | ||||||
| 			speaker_(dave_audio_) { | 			speaker_(dave_audio_) { | ||||||
| 			// Request a clock of 4Mhz; this'll be mapped upwards for Nick and Dave elsewhere. |  | ||||||
| 			set_clock_rate(4'000'000); | 			// Request a clock of 4Mhz; this'll be mapped upwards for Nick and downwards for Dave elsewhere. | ||||||
|  | 			set_clock_rate(clock_rate); | ||||||
|  | 			speaker_.set_input_rate(float(clock_rate) / float(dave_divider)); | ||||||
|  |  | ||||||
| 			ROM::Request request; | 			ROM::Request request; | ||||||
| 			using Target = Analyser::Static::Enterprise::Target; | 			using Target = Analyser::Static::Enterprise::Target; | ||||||
| @@ -216,9 +224,6 @@ template <bool has_disk_controller> class ConcreteMachine: | |||||||
| 			page<2>(0x00); | 			page<2>(0x00); | ||||||
| 			page<3>(0x00); | 			page<3>(0x00); | ||||||
|  |  | ||||||
| 			// Set up audio. |  | ||||||
| 			speaker_.set_input_rate(250000.0f);	// TODO: a bigger number, and respect the programmable divider. |  | ||||||
|  |  | ||||||
| 			// Pass on any media. | 			// Pass on any media. | ||||||
| 			insert_media(target.media); | 			insert_media(target.media); | ||||||
| 			if(!target.loading_command.empty()) { | 			if(!target.loading_command.empty()) { | ||||||
| @@ -270,26 +275,26 @@ template <bool has_disk_controller> class ConcreteMachine: | |||||||
| 				default: break; | 				default: break; | ||||||
|  |  | ||||||
| 				// For non-video pauses, insert during the initial part of the bus cycle. | 				// For non-video pauses, insert during the initial part of the bus cycle. | ||||||
| 				case CPU::Z80::PartialMachineCycle::ReadStart: | 				case PartialMachineCycle::ReadStart: | ||||||
| 				case CPU::Z80::PartialMachineCycle::WriteStart: | 				case PartialMachineCycle::WriteStart: | ||||||
| 					if(!is_video_[address >> 14] && wait_mode_ == WaitMode::OnAllAccesses) { | 					if(!is_video_[address >> 14] && wait_mode_ == WaitMode::OnAllAccesses) { | ||||||
| 						penalty = HalfCycles(2); | 						penalty = dave_delay_; | ||||||
| 					} | 					} | ||||||
| 				break; | 				break; | ||||||
| 				case CPU::Z80::PartialMachineCycle::ReadOpcodeStart: | 				case PartialMachineCycle::ReadOpcodeStart: { | ||||||
| 					if(!is_video_[address >> 14] && wait_mode_ != WaitMode::None) { | 					if(is_video_[address >> 14]) { | ||||||
| 						penalty = HalfCycles(2); |  | ||||||
| 					} else { |  | ||||||
| 						// Query Nick for the amount of delay that would occur with one cycle left | 						// Query Nick for the amount of delay that would occur with one cycle left | ||||||
| 						// in this read opcode. | 						// in this read opcode. | ||||||
| 						const auto delay_time = nick_.time_since_flush(HalfCycles(2)); | 						const auto delay_time = nick_.time_since_flush(HalfCycles(2)); | ||||||
| 						const auto delay = nick_.last_valid()->get_time_until_z80_slot(delay_time); | 						const auto delay = nick_.last_valid()->get_time_until_z80_slot(delay_time); | ||||||
| 						penalty = nick_.back_map(delay, delay_time); | 						penalty = nick_.back_map(delay, delay_time); | ||||||
|  | 					} else if(wait_mode_ != WaitMode::None) { | ||||||
|  | 						penalty = dave_delay_; | ||||||
| 					} | 					} | ||||||
| 				break; | 				} break; | ||||||
|  |  | ||||||
| 				// Video pauses: insert right at the end of the bus cycle. | 				// Video pauses: insert right at the end of the bus cycle. | ||||||
| 				case CPU::Z80::PartialMachineCycle::Write: | 				case PartialMachineCycle::Write: | ||||||
| 					// Ensure all video that should have been collected prior to | 					// Ensure all video that should have been collected prior to | ||||||
| 					// this write has been. | 					// this write has been. | ||||||
| 					if(is_video_[address >> 14]) { | 					if(is_video_[address >> 14]) { | ||||||
| @@ -297,7 +302,7 @@ template <bool has_disk_controller> class ConcreteMachine: | |||||||
| 					} | 					} | ||||||
| 					[[fallthrough]]; | 					[[fallthrough]]; | ||||||
|  |  | ||||||
| 				case CPU::Z80::PartialMachineCycle::Read: | 				case PartialMachineCycle::Read: | ||||||
| 					if(is_video_[address >> 14]) { | 					if(is_video_[address >> 14]) { | ||||||
| 						// Get delay, in Nick cycles, for a Z80 access that occurs in 0.5 | 						// Get delay, in Nick cycles, for a Z80 access that occurs in 0.5 | ||||||
| 						// cycles from now (i.e. with one cycle left to run). | 						// cycles from now (i.e. with one cycle left to run). | ||||||
| @@ -307,8 +312,8 @@ template <bool has_disk_controller> class ConcreteMachine: | |||||||
| 					} | 					} | ||||||
| 				break; | 				break; | ||||||
|  |  | ||||||
| 				case CPU::Z80::PartialMachineCycle::Input: | 				case PartialMachineCycle::Input: | ||||||
| 				case CPU::Z80::PartialMachineCycle::Output: { | 				case PartialMachineCycle::Output: { | ||||||
| 					if((address & 0xf0) == 0x80) { | 					if((address & 0xf0) == 0x80) { | ||||||
| 						// Get delay, in Nick cycles, for a Z80 access that occurs in 0.5 | 						// Get delay, in Nick cycles, for a Z80 access that occurs in 0.5 | ||||||
| 						// cycles from now (i.e. with one cycle left to run). | 						// cycles from now (i.e. with one cycle left to run). | ||||||
| @@ -334,7 +339,11 @@ template <bool has_disk_controller> class ConcreteMachine: | |||||||
| 			switch(cycle.operation) { | 			switch(cycle.operation) { | ||||||
| 				default: break; | 				default: break; | ||||||
|  |  | ||||||
| 				case CPU::Z80::PartialMachineCycle::Input: | 				case PartialMachineCycle::Interrupt: | ||||||
|  | 					*cycle.value = 0xff; | ||||||
|  | 				break; | ||||||
|  |  | ||||||
|  | 				case PartialMachineCycle::Input: | ||||||
| 					switch(address & 0xff) { | 					switch(address & 0xff) { | ||||||
| 						default: | 						default: | ||||||
| 							LOG("Unhandled input from " << PADHEX(2) << (address & 0xff)); | 							LOG("Unhandled input from " << PADHEX(2) << (address & 0xff)); | ||||||
| @@ -372,7 +381,7 @@ template <bool has_disk_controller> class ConcreteMachine: | |||||||
|  |  | ||||||
| 						case 0xb4: | 						case 0xb4: | ||||||
| 							*cycle.value = | 							*cycle.value = | ||||||
| 								(nick_->get_interrupt_line() ? 0x00 : 0x10) | | 								(nick_->get_interrupt_line() ? 0x10 : 0x00) | | ||||||
| 								dave_timer_->get_divider_state() | | 								dave_timer_->get_divider_state() | | ||||||
| 								interrupt_state_; | 								interrupt_state_; | ||||||
| 						break; | 						break; | ||||||
| @@ -399,7 +408,7 @@ template <bool has_disk_controller> class ConcreteMachine: | |||||||
| 					} | 					} | ||||||
| 				break; | 				break; | ||||||
|  |  | ||||||
| 				case CPU::Z80::PartialMachineCycle::Output: | 				case PartialMachineCycle::Output: | ||||||
| 					switch(address & 0xff) { | 					switch(address & 0xff) { | ||||||
| 						default: | 						default: | ||||||
| 							LOG("Unhandled output: " << PADHEX(2) << *cycle.value << " to " << PADHEX(2) << (address & 0xff)); | 							LOG("Unhandled output: " << PADHEX(2) << *cycle.value << " to " << PADHEX(2) << (address & 0xff)); | ||||||
| @@ -430,6 +439,19 @@ template <bool has_disk_controller> class ConcreteMachine: | |||||||
| 						case 0xb2:	page<2>(*cycle.value);	break; | 						case 0xb2:	page<2>(*cycle.value);	break; | ||||||
| 						case 0xb3:	page<3>(*cycle.value);	break; | 						case 0xb3:	page<3>(*cycle.value);	break; | ||||||
|  |  | ||||||
|  | 						case 0xbf: | ||||||
|  | 							switch((*cycle.value >> 2)&3) { | ||||||
|  | 								default:	wait_mode_ = WaitMode::None;			break; | ||||||
|  | 								case 0:		wait_mode_ = WaitMode::OnAllAccesses;	break; | ||||||
|  | 								case 1:		wait_mode_ = WaitMode::OnM1;			break; | ||||||
|  | 							} | ||||||
|  |  | ||||||
|  | 							// Dave delays (i.e. those affecting memory areas not associated with Nick) | ||||||
|  | 							// are one cycle in 8Mhz mode, two cycles in 12Mhz mode. | ||||||
|  | 							dave_delay_ = HalfCycles(2 + (*cycle.value)&2); | ||||||
|  |  | ||||||
|  | 							[[fallthrough]]; | ||||||
|  |  | ||||||
| 						case 0xa0:	case 0xa1:	case 0xa2:	case 0xa3: | 						case 0xa0:	case 0xa1:	case 0xa2:	case 0xa3: | ||||||
| 						case 0xa4:	case 0xa5:	case 0xa6:	case 0xa7: | 						case 0xa4:	case 0xa5:	case 0xa6:	case 0xa7: | ||||||
| 						case 0xa8:	case 0xa9:	case 0xaa:	case 0xab: | 						case 0xa8:	case 0xa9:	case 0xaa:	case 0xab: | ||||||
| @@ -495,19 +517,11 @@ template <bool has_disk_controller> class ConcreteMachine: | |||||||
| 							// b1 = serial status out | 							// b1 = serial status out | ||||||
| 							LOG("TODO: serial output " << PADHEX(2) << *cycle.value); | 							LOG("TODO: serial output " << PADHEX(2) << *cycle.value); | ||||||
| 						break; | 						break; | ||||||
| 						case 0xbf: |  | ||||||
| 							// TODO: onboard RAM, Dave 8/12Mhz select. |  | ||||||
| 							switch((*cycle.value >> 2)&3) { |  | ||||||
| 								default:	wait_mode_ = WaitMode::None;			break; |  | ||||||
| 								case 0:		wait_mode_ = WaitMode::OnAllAccesses;	break; |  | ||||||
| 								case 1:		wait_mode_ = WaitMode::OnM1;			break; |  | ||||||
| 							} |  | ||||||
| 						break; |  | ||||||
| 					} | 					} | ||||||
| 				break; | 				break; | ||||||
|  |  | ||||||
| 				case CPU::Z80::PartialMachineCycle::Read: | 				case PartialMachineCycle::Read: | ||||||
| 				case CPU::Z80::PartialMachineCycle::ReadOpcode: | 				case PartialMachineCycle::ReadOpcode: | ||||||
| 					if(read_pointers_[address >> 14]) { | 					if(read_pointers_[address >> 14]) { | ||||||
| 						*cycle.value = read_pointers_[address >> 14][address]; | 						*cycle.value = read_pointers_[address >> 14][address]; | ||||||
| 					} else { | 					} else { | ||||||
| @@ -515,7 +529,7 @@ template <bool has_disk_controller> class ConcreteMachine: | |||||||
| 					} | 					} | ||||||
| 				break; | 				break; | ||||||
|  |  | ||||||
| 				case CPU::Z80::PartialMachineCycle::Write: | 				case PartialMachineCycle::Write: | ||||||
| 					if(write_pointers_[address >> 14]) { | 					if(write_pointers_[address >> 14]) { | ||||||
| 						write_pointers_[address >> 14][address] = *cycle.value; | 						write_pointers_[address >> 14][address] = *cycle.value; | ||||||
| 					} | 					} | ||||||
| @@ -588,7 +602,7 @@ template <bool has_disk_controller> class ConcreteMachine: | |||||||
| 			None, | 			None, | ||||||
| 			OnM1, | 			OnM1, | ||||||
| 			OnAllAccesses | 			OnAllAccesses | ||||||
| 		} wait_mode_ = WaitMode::None; | 		} wait_mode_ = WaitMode::OnAllAccesses; | ||||||
| 		bool is_video_[4]{}; | 		bool is_video_[4]{}; | ||||||
|  |  | ||||||
| 		// MARK: - ScanProducer | 		// MARK: - ScanProducer | ||||||
| @@ -683,7 +697,7 @@ template <bool has_disk_controller> class ConcreteMachine: | |||||||
|  |  | ||||||
| 		// MARK: - Chips. | 		// MARK: - Chips. | ||||||
| 		CPU::Z80::Processor<ConcreteMachine, false, false> z80_; | 		CPU::Z80::Processor<ConcreteMachine, false, false> z80_; | ||||||
| 		JustInTimeActor<Nick, HalfCycles, 40434603, 11360000> nick_; | 		NickType nick_; | ||||||
| 		bool previous_nick_interrupt_line_ = false; | 		bool previous_nick_interrupt_line_ = false; | ||||||
| 		// Cf. timing guesses above. | 		// Cf. timing guesses above. | ||||||
|  |  | ||||||
| @@ -692,12 +706,14 @@ template <bool has_disk_controller> class ConcreteMachine: | |||||||
| 		Outputs::Speaker::LowpassSpeaker<Dave::Audio> speaker_; | 		Outputs::Speaker::LowpassSpeaker<Dave::Audio> speaker_; | ||||||
| 		HalfCycles time_since_audio_update_; | 		HalfCycles time_since_audio_update_; | ||||||
|  |  | ||||||
| 		// The following two should both use the same divider. | 		HalfCycles dave_delay_ = HalfCycles(2); | ||||||
| 		JustInTimeActor<Dave::TimedInterruptSource, HalfCycles, 1, 16> dave_timer_; |  | ||||||
|  | 		// The divider supplied to the JustInTimeActor and the manual divider used in | ||||||
|  | 		// update_audio() should match. | ||||||
|  | 		static constexpr int dave_divider = 8; | ||||||
|  | 		JustInTimeActor<Dave::TimedInterruptSource, HalfCycles, 1, dave_divider> dave_timer_; | ||||||
| 		inline void update_audio() { | 		inline void update_audio() { | ||||||
| 			// TODO: divide by only 8, letting Dave divide itself by a further 2 or 3 | 			speaker_.run_for(audio_queue_, time_since_audio_update_.divide_cycles(Cycles(dave_divider))); | ||||||
| 			// as per its own register. |  | ||||||
| 			speaker_.run_for(audio_queue_, time_since_audio_update_.divide_cycles(Cycles(16))); |  | ||||||
| 		} | 		} | ||||||
|  |  | ||||||
| 		// MARK: - EXDos card. | 		// MARK: - EXDos card. | ||||||
| @@ -731,10 +747,19 @@ Machine *Machine::Enterprise(const Analyser::Static::Target *target, const ROMMa | |||||||
| 	using Target = Analyser::Static::Enterprise::Target; | 	using Target = Analyser::Static::Enterprise::Target; | ||||||
| 	const Target *const enterprise_target = dynamic_cast<const Target *>(target); | 	const Target *const enterprise_target = dynamic_cast<const Target *>(target); | ||||||
|  |  | ||||||
| 	if(enterprise_target->dos == Target::DOS::None) | #define BuildMachine(exdos, sixmhz)																									\ | ||||||
| 		return new Enterprise::ConcreteMachine<false>(*enterprise_target, rom_fetcher); | 	if((enterprise_target->dos == Target::DOS::None) != exdos && (enterprise_target->speed == Target::Speed::SixMHz) == sixmhz) {	\ | ||||||
| 	else | 		return new Enterprise::ConcreteMachine<exdos, sixmhz>(*enterprise_target, rom_fetcher);										\ | ||||||
| 		return new Enterprise::ConcreteMachine<true>(*enterprise_target, rom_fetcher); | 	} | ||||||
|  |  | ||||||
|  | 	BuildMachine(false, false); | ||||||
|  | 	BuildMachine(false, true); | ||||||
|  | 	BuildMachine(true, false); | ||||||
|  | 	BuildMachine(true, true); | ||||||
|  |  | ||||||
|  | #undef BuildMachine | ||||||
|  |  | ||||||
|  | 	return nullptr; | ||||||
| } | } | ||||||
|  |  | ||||||
| Machine::~Machine() {} | Machine::~Machine() {} | ||||||
|   | |||||||
| @@ -94,9 +94,6 @@ class Nick { | |||||||
| 		bool vres_ = false; | 		bool vres_ = false; | ||||||
| 		bool reload_line_parameter_pointer_ = false; | 		bool reload_line_parameter_pointer_ = false; | ||||||
|  |  | ||||||
| 		// An accumulator for border output regions. |  | ||||||
| 		int border_duration_ = 0; |  | ||||||
|  |  | ||||||
| 		// The destination for new pixels. | 		// The destination for new pixels. | ||||||
| 		static constexpr int allocation_size = 336; | 		static constexpr int allocation_size = 336; | ||||||
| 		static_assert((allocation_size % 16) == 0, "Allocation size must be a multiple of 16"); | 		static_assert((allocation_size % 16) == 0, "Allocation size must be a multiple of 16"); | ||||||
|   | |||||||
| @@ -47,6 +47,11 @@ typedef NS_ENUM(NSInteger, CSMachineEnterpriseModel) { | |||||||
| 	CSMachineEnterpriseModel256, | 	CSMachineEnterpriseModel256, | ||||||
| }; | }; | ||||||
|  |  | ||||||
|  | typedef NS_ENUM(NSInteger, CSMachineEnterpriseSpeed) { | ||||||
|  | 	CSMachineEnterpriseSpeed4MHz, | ||||||
|  | 	CSMachineEnterpriseSpeed6MHz | ||||||
|  | }; | ||||||
|  |  | ||||||
| typedef NS_ENUM(NSInteger, CSMachineEnterpriseEXOS) { | typedef NS_ENUM(NSInteger, CSMachineEnterpriseEXOS) { | ||||||
| 	CSMachineEnterpriseEXOSVersion21, | 	CSMachineEnterpriseEXOSVersion21, | ||||||
| 	CSMachineEnterpriseEXOSVersion20, | 	CSMachineEnterpriseEXOSVersion20, | ||||||
| @@ -120,7 +125,7 @@ typedef int Kilobytes; | |||||||
| - (instancetype)initWithAppleIIgsModel:(CSMachineAppleIIgsModel)model memorySize:(Kilobytes)memorySize; | - (instancetype)initWithAppleIIgsModel:(CSMachineAppleIIgsModel)model memorySize:(Kilobytes)memorySize; | ||||||
| - (instancetype)initWithAtariSTModel:(CSMachineAtariSTModel)model; | - (instancetype)initWithAtariSTModel:(CSMachineAtariSTModel)model; | ||||||
| - (instancetype)initWithElectronDFS:(BOOL)dfs adfs:(BOOL)adfs ap6:(BOOL)ap6 sidewaysRAM:(BOOL)sidewaysRAM; | - (instancetype)initWithElectronDFS:(BOOL)dfs adfs:(BOOL)adfs ap6:(BOOL)ap6 sidewaysRAM:(BOOL)sidewaysRAM; | ||||||
| - (instancetype)initWithEnterpriseModel:(CSMachineEnterpriseModel)model exosVersion:(CSMachineEnterpriseEXOS)exosVersion basicVersion:(CSMachineEnterpriseBASIC)basicVersion dos:(CSMachineEnterpriseDOS)dos; | - (instancetype)initWithEnterpriseModel:(CSMachineEnterpriseModel)model speed:(CSMachineEnterpriseSpeed)speed exosVersion:(CSMachineEnterpriseEXOS)exosVersion basicVersion:(CSMachineEnterpriseBASIC)basicVersion dos:(CSMachineEnterpriseDOS)dos; | ||||||
| - (instancetype)initWithMacintoshModel:(CSMachineMacintoshModel)model; | - (instancetype)initWithMacintoshModel:(CSMachineMacintoshModel)model; | ||||||
| - (instancetype)initWithMSXRegion:(CSMachineMSXRegion)region hasDiskDrive:(BOOL)hasDiskDrive; | - (instancetype)initWithMSXRegion:(CSMachineMSXRegion)region hasDiskDrive:(BOOL)hasDiskDrive; | ||||||
| - (instancetype)initWithOricModel:(CSMachineOricModel)model diskInterface:(CSMachineOricDiskInterface)diskInterface; | - (instancetype)initWithOricModel:(CSMachineOricModel)model diskInterface:(CSMachineOricDiskInterface)diskInterface; | ||||||
|   | |||||||
| @@ -131,7 +131,7 @@ | |||||||
| 	return self; | 	return self; | ||||||
| } | } | ||||||
|  |  | ||||||
| - (instancetype)initWithEnterpriseModel:(CSMachineEnterpriseModel)model exosVersion:(CSMachineEnterpriseEXOS)exosVersion basicVersion:(CSMachineEnterpriseBASIC)basicVersion dos:(CSMachineEnterpriseDOS)dos { | - (instancetype)initWithEnterpriseModel:(CSMachineEnterpriseModel)model speed:(CSMachineEnterpriseSpeed)speed exosVersion:(CSMachineEnterpriseEXOS)exosVersion basicVersion:(CSMachineEnterpriseBASIC)basicVersion dos:(CSMachineEnterpriseDOS)dos { | ||||||
| 	self = [super init]; | 	self = [super init]; | ||||||
| 	if(self) { | 	if(self) { | ||||||
| 		using Target = Analyser::Static::Enterprise::Target; | 		using Target = Analyser::Static::Enterprise::Target; | ||||||
| @@ -144,6 +144,12 @@ | |||||||
| 			case CSMachineEnterpriseModel256:		target->model = Target::Model::Enterprise256;		break; | 			case CSMachineEnterpriseModel256:		target->model = Target::Model::Enterprise256;		break; | ||||||
| 		} | 		} | ||||||
|  |  | ||||||
|  | 		switch(speed) { | ||||||
|  | 			case CSMachineEnterpriseSpeed6MHz:		target->speed = Target::Speed::SixMHz;				break; | ||||||
|  | 			default: | ||||||
|  | 			case CSMachineEnterpriseSpeed4MHz:		target->speed = Target::Speed::FourMHz;				break; | ||||||
|  | 		} | ||||||
|  |  | ||||||
| 		switch(exosVersion) { | 		switch(exosVersion) { | ||||||
| 			case CSMachineEnterpriseEXOSVersion21:	target->exos_version = Target::EXOSVersion::v21;	break; | 			case CSMachineEnterpriseEXOSVersion21:	target->exos_version = Target::EXOSVersion::v21;	break; | ||||||
| 			default: | 			default: | ||||||
|   | |||||||
| @@ -322,8 +322,21 @@ Gw | |||||||
|                                                 </menu> |                                                 </menu> | ||||||
|                                             </popUpButtonCell> |                                             </popUpButtonCell> | ||||||
|                                         </popUpButton> |                                         </popUpButton> | ||||||
|  |                                         <popUpButton verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="Lfl-5c-b8j"> | ||||||
|  |                                             <rect key="frame" x="67" y="149" width="77" height="25"/> | ||||||
|  |                                             <popUpButtonCell key="cell" type="push" title="4 Mhz" bezelStyle="rounded" alignment="left" lineBreakMode="truncatingTail" state="on" borderStyle="borderAndBezel" tag="4" imageScaling="axesIndependently" inset="2" selectedItem="5N6-tD-uN7" id="BU2-NJ-Kii"> | ||||||
|  |                                                 <behavior key="behavior" lightByBackground="YES" lightByGray="YES"/> | ||||||
|  |                                                 <font key="font" metaFont="menu"/> | ||||||
|  |                                                 <menu key="menu" id="ANK-p0-M77"> | ||||||
|  |                                                     <items> | ||||||
|  |                                                         <menuItem title="4 Mhz" state="on" tag="4" id="5N6-tD-uN7"/> | ||||||
|  |                                                         <menuItem title="6 Mhz" tag="6" id="bVT-qO-U7n"/> | ||||||
|  |                                                     </items> | ||||||
|  |                                                 </menu> | ||||||
|  |                                             </popUpButtonCell> | ||||||
|  |                                         </popUpButton> | ||||||
|                                         <popUpButton verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="nen-Za-7zH"> |                                         <popUpButton verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="nen-Za-7zH"> | ||||||
|                                             <rect key="frame" x="64" y="149" width="107" height="25"/> |                                             <rect key="frame" x="64" y="119" width="107" height="25"/> | ||||||
|                                             <popUpButtonCell key="cell" type="push" title="Version 2.1" bezelStyle="rounded" alignment="left" lineBreakMode="truncatingTail" state="on" borderStyle="borderAndBezel" tag="21" imageScaling="axesIndependently" inset="2" selectedItem="Qja-xZ-wVM" id="xGG-ri-8Sb"> |                                             <popUpButtonCell key="cell" type="push" title="Version 2.1" bezelStyle="rounded" alignment="left" lineBreakMode="truncatingTail" state="on" borderStyle="borderAndBezel" tag="21" imageScaling="axesIndependently" inset="2" selectedItem="Qja-xZ-wVM" id="xGG-ri-8Sb"> | ||||||
|                                                 <behavior key="behavior" lightByBackground="YES" lightByGray="YES"/> |                                                 <behavior key="behavior" lightByBackground="YES" lightByGray="YES"/> | ||||||
|                                                 <font key="font" metaFont="menu"/> |                                                 <font key="font" metaFont="menu"/> | ||||||
| @@ -337,7 +350,7 @@ Gw | |||||||
|                                             </popUpButtonCell> |                                             </popUpButtonCell> | ||||||
|                                         </popUpButton> |                                         </popUpButton> | ||||||
|                                         <popUpButton verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="hIr-GH-7xi"> |                                         <popUpButton verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="hIr-GH-7xi"> | ||||||
|                                             <rect key="frame" x="67" y="119" width="105" height="25"/> |                                             <rect key="frame" x="67" y="89" width="105" height="25"/> | ||||||
|                                             <popUpButtonCell key="cell" type="push" title="Version 2.1" bezelStyle="rounded" alignment="left" lineBreakMode="truncatingTail" state="on" borderStyle="borderAndBezel" tag="21" imageScaling="axesIndependently" inset="2" selectedItem="TME-cv-Jh1" id="9mQ-GW-lq9"> |                                             <popUpButtonCell key="cell" type="push" title="Version 2.1" bezelStyle="rounded" alignment="left" lineBreakMode="truncatingTail" state="on" borderStyle="borderAndBezel" tag="21" imageScaling="axesIndependently" inset="2" selectedItem="TME-cv-Jh1" id="9mQ-GW-lq9"> | ||||||
|                                                 <behavior key="behavior" lightByBackground="YES" lightByGray="YES"/> |                                                 <behavior key="behavior" lightByBackground="YES" lightByGray="YES"/> | ||||||
|                                                 <font key="font" metaFont="menu"/> |                                                 <font key="font" metaFont="menu"/> | ||||||
| @@ -352,20 +365,20 @@ Gw | |||||||
|                                             </popUpButtonCell> |                                             </popUpButtonCell> | ||||||
|                                         </popUpButton> |                                         </popUpButton> | ||||||
|                                         <popUpButton verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="syE-e7-TjU"> |                                         <popUpButton verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="syE-e7-TjU"> | ||||||
|                                             <rect key="frame" x="57" y="89" width="83" height="25"/> |                                             <rect key="frame" x="57" y="59" width="83" height="25"/> | ||||||
|                                             <popUpButtonCell key="cell" type="push" title="EXDOS" bezelStyle="rounded" alignment="left" lineBreakMode="truncatingTail" borderStyle="borderAndBezel" tag="1" imageScaling="axesIndependently" inset="2" selectedItem="8rP-2w-PdU" id="NvO-Zm-2Rq"> |                                             <popUpButtonCell key="cell" type="push" title="EXDOS" bezelStyle="rounded" alignment="left" lineBreakMode="truncatingTail" state="on" borderStyle="borderAndBezel" tag="1" imageScaling="axesIndependently" inset="2" selectedItem="8rP-2w-PdU" id="NvO-Zm-2Rq"> | ||||||
|                                                 <behavior key="behavior" lightByBackground="YES" lightByGray="YES"/> |                                                 <behavior key="behavior" lightByBackground="YES" lightByGray="YES"/> | ||||||
|                                                 <font key="font" metaFont="menu"/> |                                                 <font key="font" metaFont="menu"/> | ||||||
|                                                 <menu key="menu" title="DOS" id="sdr-al-7mi"> |                                                 <menu key="menu" title="DOS" id="sdr-al-7mi"> | ||||||
|                                                     <items> |                                                     <items> | ||||||
|                                                         <menuItem title="EXDOS" tag="1" id="8rP-2w-PdU"/> |                                                         <menuItem title="EXDOS" state="on" tag="1" id="8rP-2w-PdU"/> | ||||||
|                                                         <menuItem title="None" state="on" id="qoS-KO-iEZ"/> |                                                         <menuItem title="None" id="qoS-KO-iEZ"/> | ||||||
|                                                     </items> |                                                     </items> | ||||||
|                                                 </menu> |                                                 </menu> | ||||||
|                                             </popUpButtonCell> |                                             </popUpButtonCell> | ||||||
|                                         </popUpButton> |                                         </popUpButton> | ||||||
|                                         <textField horizontalHuggingPriority="251" verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="ykc-W1-YaS"> |                                         <textField horizontalHuggingPriority="251" verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="ykc-W1-YaS"> | ||||||
|                                             <rect key="frame" x="18" y="155" width="43" height="16"/> |                                             <rect key="frame" x="18" y="125" width="43" height="16"/> | ||||||
|                                             <textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" sendsActionOnEndEditing="YES" title="EXOS:" id="gUC-PN-zVL"> |                                             <textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" sendsActionOnEndEditing="YES" title="EXOS:" id="gUC-PN-zVL"> | ||||||
|                                                 <font key="font" metaFont="system"/> |                                                 <font key="font" metaFont="system"/> | ||||||
|                                                 <color key="textColor" name="labelColor" catalog="System" colorSpace="catalog"/> |                                                 <color key="textColor" name="labelColor" catalog="System" colorSpace="catalog"/> | ||||||
| @@ -381,7 +394,7 @@ Gw | |||||||
|                                             </textFieldCell> |                                             </textFieldCell> | ||||||
|                                         </textField> |                                         </textField> | ||||||
|                                         <textField horizontalHuggingPriority="251" verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="dzd-tH-BjX"> |                                         <textField horizontalHuggingPriority="251" verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="dzd-tH-BjX"> | ||||||
|                                             <rect key="frame" x="18" y="125" width="46" height="16"/> |                                             <rect key="frame" x="18" y="95" width="46" height="16"/> | ||||||
|                                             <textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" sendsActionOnEndEditing="YES" title="BASIC:" id="ai1-oR-X6Y"> |                                             <textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" sendsActionOnEndEditing="YES" title="BASIC:" id="ai1-oR-X6Y"> | ||||||
|                                                 <font key="font" metaFont="system"/> |                                                 <font key="font" metaFont="system"/> | ||||||
|                                                 <color key="textColor" name="labelColor" catalog="System" colorSpace="catalog"/> |                                                 <color key="textColor" name="labelColor" catalog="System" colorSpace="catalog"/> | ||||||
| @@ -389,13 +402,21 @@ Gw | |||||||
|                                             </textFieldCell> |                                             </textFieldCell> | ||||||
|                                         </textField> |                                         </textField> | ||||||
|                                         <textField horizontalHuggingPriority="251" verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="pxr-Bq-yh0"> |                                         <textField horizontalHuggingPriority="251" verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="pxr-Bq-yh0"> | ||||||
|                                             <rect key="frame" x="18" y="95" width="36" height="16"/> |                                             <rect key="frame" x="18" y="65" width="36" height="16"/> | ||||||
|                                             <textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" sendsActionOnEndEditing="YES" title="DOS:" id="NFk-cp-DfS"> |                                             <textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" sendsActionOnEndEditing="YES" title="DOS:" id="NFk-cp-DfS"> | ||||||
|                                                 <font key="font" metaFont="system"/> |                                                 <font key="font" metaFont="system"/> | ||||||
|                                                 <color key="textColor" name="labelColor" catalog="System" colorSpace="catalog"/> |                                                 <color key="textColor" name="labelColor" catalog="System" colorSpace="catalog"/> | ||||||
|                                                 <color key="backgroundColor" name="controlColor" catalog="System" colorSpace="catalog"/> |                                                 <color key="backgroundColor" name="controlColor" catalog="System" colorSpace="catalog"/> | ||||||
|                                             </textFieldCell> |                                             </textFieldCell> | ||||||
|                                         </textField> |                                         </textField> | ||||||
|  |                                         <textField horizontalHuggingPriority="251" verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="rHr-bh-QMV"> | ||||||
|  |                                             <rect key="frame" x="17" y="155" width="47" height="16"/> | ||||||
|  |                                             <textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" sendsActionOnEndEditing="YES" title="Speed:" id="sAw-C9-Sf7"> | ||||||
|  |                                                 <font key="font" metaFont="system"/> | ||||||
|  |                                                 <color key="textColor" name="labelColor" catalog="System" colorSpace="catalog"/> | ||||||
|  |                                                 <color key="backgroundColor" name="controlColor" catalog="System" colorSpace="catalog"/> | ||||||
|  |                                             </textFieldCell> | ||||||
|  |                                         </textField> | ||||||
|                                     </subviews> |                                     </subviews> | ||||||
|                                     <constraints> |                                     <constraints> | ||||||
|                                         <constraint firstItem="dzd-tH-BjX" firstAttribute="centerY" secondItem="hIr-GH-7xi" secondAttribute="centerY" id="3TV-RU-Kgh"/> |                                         <constraint firstItem="dzd-tH-BjX" firstAttribute="centerY" secondItem="hIr-GH-7xi" secondAttribute="centerY" id="3TV-RU-Kgh"/> | ||||||
| @@ -403,11 +424,13 @@ Gw | |||||||
|                                         <constraint firstItem="frx-nk-c3P" firstAttribute="centerY" secondItem="PhH-bu-pb5" secondAttribute="centerY" id="6Wc-aR-wuL"/> |                                         <constraint firstItem="frx-nk-c3P" firstAttribute="centerY" secondItem="PhH-bu-pb5" secondAttribute="centerY" id="6Wc-aR-wuL"/> | ||||||
|                                         <constraint firstItem="dzd-tH-BjX" firstAttribute="leading" secondItem="1cs-PX-RAH" secondAttribute="leading" constant="20" symbolic="YES" id="7RZ-Om-TAa"/> |                                         <constraint firstItem="dzd-tH-BjX" firstAttribute="leading" secondItem="1cs-PX-RAH" secondAttribute="leading" constant="20" symbolic="YES" id="7RZ-Om-TAa"/> | ||||||
|                                         <constraint firstItem="ykc-W1-YaS" firstAttribute="centerY" secondItem="nen-Za-7zH" secondAttribute="centerY" id="CLa-6E-8BB"/> |                                         <constraint firstItem="ykc-W1-YaS" firstAttribute="centerY" secondItem="nen-Za-7zH" secondAttribute="centerY" id="CLa-6E-8BB"/> | ||||||
|  |                                         <constraint firstItem="nen-Za-7zH" firstAttribute="top" secondItem="Lfl-5c-b8j" secondAttribute="bottom" constant="10" symbolic="YES" id="Df8-qv-3dY"/> | ||||||
|                                         <constraint firstItem="PhH-bu-pb5" firstAttribute="leading" secondItem="frx-nk-c3P" secondAttribute="trailing" constant="8" symbolic="YES" id="ENF-TY-TQ7"/> |                                         <constraint firstItem="PhH-bu-pb5" firstAttribute="leading" secondItem="frx-nk-c3P" secondAttribute="trailing" constant="8" symbolic="YES" id="ENF-TY-TQ7"/> | ||||||
|                                         <constraint firstItem="nen-Za-7zH" firstAttribute="leading" secondItem="ykc-W1-YaS" secondAttribute="trailing" constant="8" symbolic="YES" id="GWR-VI-9PG"/> |                                         <constraint firstItem="nen-Za-7zH" firstAttribute="leading" secondItem="ykc-W1-YaS" secondAttribute="trailing" constant="8" symbolic="YES" id="GWR-VI-9PG"/> | ||||||
|                                         <constraint firstAttribute="bottom" relation="greaterThanOrEqual" secondItem="syE-e7-TjU" secondAttribute="bottom" constant="20" symbolic="YES" id="K3s-FA-zMB"/> |                                         <constraint firstAttribute="bottom" relation="greaterThanOrEqual" secondItem="syE-e7-TjU" secondAttribute="bottom" constant="20" symbolic="YES" id="K3s-FA-zMB"/> | ||||||
|                                         <constraint firstAttribute="trailing" relation="greaterThanOrEqual" secondItem="PhH-bu-pb5" secondAttribute="trailing" constant="20" symbolic="YES" id="LwB-ef-uF4"/> |                                         <constraint firstAttribute="trailing" relation="greaterThanOrEqual" secondItem="PhH-bu-pb5" secondAttribute="trailing" constant="20" symbolic="YES" id="LwB-ef-uF4"/> | ||||||
|                                         <constraint firstItem="PhH-bu-pb5" firstAttribute="top" secondItem="1cs-PX-RAH" secondAttribute="top" constant="20" symbolic="YES" id="Myt-i4-jVq"/> |                                         <constraint firstItem="PhH-bu-pb5" firstAttribute="top" secondItem="1cs-PX-RAH" secondAttribute="top" constant="20" symbolic="YES" id="Myt-i4-jVq"/> | ||||||
|  |                                         <constraint firstItem="Lfl-5c-b8j" firstAttribute="top" secondItem="PhH-bu-pb5" secondAttribute="bottom" constant="10" symbolic="YES" id="Nl3-hL-jwg"/> | ||||||
|                                         <constraint firstItem="pxr-Bq-yh0" firstAttribute="leading" secondItem="1cs-PX-RAH" secondAttribute="leading" constant="20" symbolic="YES" id="Qzp-IY-Pa0"/> |                                         <constraint firstItem="pxr-Bq-yh0" firstAttribute="leading" secondItem="1cs-PX-RAH" secondAttribute="leading" constant="20" symbolic="YES" id="Qzp-IY-Pa0"/> | ||||||
|                                         <constraint firstItem="PhH-bu-pb5" firstAttribute="leading" secondItem="frx-nk-c3P" secondAttribute="trailing" constant="8" symbolic="YES" id="T3e-u7-fiQ"/> |                                         <constraint firstItem="PhH-bu-pb5" firstAttribute="leading" secondItem="frx-nk-c3P" secondAttribute="trailing" constant="8" symbolic="YES" id="T3e-u7-fiQ"/> | ||||||
|                                         <constraint firstAttribute="trailing" relation="greaterThanOrEqual" secondItem="syE-e7-TjU" secondAttribute="trailing" constant="20" symbolic="YES" id="TEX-Nw-y2K"/> |                                         <constraint firstAttribute="trailing" relation="greaterThanOrEqual" secondItem="syE-e7-TjU" secondAttribute="trailing" constant="20" symbolic="YES" id="TEX-Nw-y2K"/> | ||||||
| @@ -415,11 +438,13 @@ Gw | |||||||
|                                         <constraint firstItem="pxr-Bq-yh0" firstAttribute="centerY" secondItem="syE-e7-TjU" secondAttribute="centerY" id="UYw-uz-Am0"/> |                                         <constraint firstItem="pxr-Bq-yh0" firstAttribute="centerY" secondItem="syE-e7-TjU" secondAttribute="centerY" id="UYw-uz-Am0"/> | ||||||
|                                         <constraint firstItem="hIr-GH-7xi" firstAttribute="top" secondItem="nen-Za-7zH" secondAttribute="bottom" constant="10" symbolic="YES" id="VOc-2v-s3u"/> |                                         <constraint firstItem="hIr-GH-7xi" firstAttribute="top" secondItem="nen-Za-7zH" secondAttribute="bottom" constant="10" symbolic="YES" id="VOc-2v-s3u"/> | ||||||
|                                         <constraint firstItem="syE-e7-TjU" firstAttribute="top" secondItem="hIr-GH-7xi" secondAttribute="bottom" constant="10" symbolic="YES" id="W9S-on-Heq"/> |                                         <constraint firstItem="syE-e7-TjU" firstAttribute="top" secondItem="hIr-GH-7xi" secondAttribute="bottom" constant="10" symbolic="YES" id="W9S-on-Heq"/> | ||||||
|                                         <constraint firstItem="nen-Za-7zH" firstAttribute="top" secondItem="PhH-bu-pb5" secondAttribute="bottom" constant="10" symbolic="YES" id="Yes-o3-V0m"/> |                                         <constraint firstItem="rHr-bh-QMV" firstAttribute="leading" secondItem="1cs-PX-RAH" secondAttribute="leading" constant="19" id="XZf-lB-NnA"/> | ||||||
|                                         <constraint firstItem="ykc-W1-YaS" firstAttribute="leading" secondItem="1cs-PX-RAH" secondAttribute="leading" constant="20" symbolic="YES" id="bAP-lx-pdi"/> |                                         <constraint firstItem="ykc-W1-YaS" firstAttribute="leading" secondItem="1cs-PX-RAH" secondAttribute="leading" constant="20" symbolic="YES" id="bAP-lx-pdi"/> | ||||||
|                                         <constraint firstAttribute="trailing" relation="greaterThanOrEqual" secondItem="nen-Za-7zH" secondAttribute="trailing" constant="20" symbolic="YES" id="eEI-5Q-TnO"/> |                                         <constraint firstAttribute="trailing" relation="greaterThanOrEqual" secondItem="nen-Za-7zH" secondAttribute="trailing" constant="20" symbolic="YES" id="eEI-5Q-TnO"/> | ||||||
|                                         <constraint firstItem="syE-e7-TjU" firstAttribute="leading" secondItem="pxr-Bq-yh0" secondAttribute="trailing" constant="8" symbolic="YES" id="fmM-Ma-Jyu"/> |                                         <constraint firstItem="syE-e7-TjU" firstAttribute="leading" secondItem="pxr-Bq-yh0" secondAttribute="trailing" constant="8" symbolic="YES" id="fmM-Ma-Jyu"/> | ||||||
|                                         <constraint firstItem="hIr-GH-7xi" firstAttribute="leading" secondItem="dzd-tH-BjX" secondAttribute="trailing" constant="8" symbolic="YES" id="jDQ-TF-Ogf"/> |                                         <constraint firstItem="hIr-GH-7xi" firstAttribute="leading" secondItem="dzd-tH-BjX" secondAttribute="trailing" constant="8" symbolic="YES" id="jDQ-TF-Ogf"/> | ||||||
|  |                                         <constraint firstItem="rHr-bh-QMV" firstAttribute="centerY" secondItem="Lfl-5c-b8j" secondAttribute="centerY" id="mnp-JF-dVQ"/> | ||||||
|  |                                         <constraint firstItem="Lfl-5c-b8j" firstAttribute="leading" secondItem="rHr-bh-QMV" secondAttribute="trailing" constant="8" symbolic="YES" id="yfb-SL-v1H"/> | ||||||
|                                     </constraints> |                                     </constraints> | ||||||
|                                 </view> |                                 </view> | ||||||
|                             </tabViewItem> |                             </tabViewItem> | ||||||
| @@ -874,6 +899,7 @@ Gw | |||||||
|                 <outlet property="enterpriseDOSButton" destination="syE-e7-TjU" id="sCW-Bj-ZTW"/> |                 <outlet property="enterpriseDOSButton" destination="syE-e7-TjU" id="sCW-Bj-ZTW"/> | ||||||
|                 <outlet property="enterpriseEXOSButton" destination="nen-Za-7zH" id="NwS-ua-FdA"/> |                 <outlet property="enterpriseEXOSButton" destination="nen-Za-7zH" id="NwS-ua-FdA"/> | ||||||
|                 <outlet property="enterpriseModelButton" destination="PhH-bu-pb5" id="8wD-sW-aBw"/> |                 <outlet property="enterpriseModelButton" destination="PhH-bu-pb5" id="8wD-sW-aBw"/> | ||||||
|  |                 <outlet property="enterpriseSpeedButton" destination="Lfl-5c-b8j" id="ClN-yN-Nbx"/> | ||||||
|                 <outlet property="machineNameTable" destination="3go-Eb-GOy" id="Ppf-S0-IP1"/> |                 <outlet property="machineNameTable" destination="3go-Eb-GOy" id="Ppf-S0-IP1"/> | ||||||
|                 <outlet property="machineSelector" destination="VUb-QG-x7c" id="crR-hB-jGd"/> |                 <outlet property="machineSelector" destination="VUb-QG-x7c" id="crR-hB-jGd"/> | ||||||
|                 <outlet property="macintoshModelTypeButton" destination="xa6-NA-JY5" id="2jX-PY-v2z"/> |                 <outlet property="macintoshModelTypeButton" destination="xa6-NA-JY5" id="2jX-PY-v2z"/> | ||||||
|   | |||||||
| @@ -38,6 +38,7 @@ class MachinePicker: NSObject, NSTableViewDataSource, NSTableViewDelegate { | |||||||
|  |  | ||||||
| 	// MARK: - Enterprise properties | 	// MARK: - Enterprise properties | ||||||
| 	@IBOutlet var enterpriseModelButton: NSPopUpButton! | 	@IBOutlet var enterpriseModelButton: NSPopUpButton! | ||||||
|  | 	@IBOutlet var enterpriseSpeedButton: NSPopUpButton! | ||||||
| 	@IBOutlet var enterpriseEXOSButton: NSPopUpButton! | 	@IBOutlet var enterpriseEXOSButton: NSPopUpButton! | ||||||
| 	@IBOutlet var enterpriseBASICButton: NSPopUpButton! | 	@IBOutlet var enterpriseBASICButton: NSPopUpButton! | ||||||
| 	@IBOutlet var enterpriseDOSButton: NSPopUpButton! | 	@IBOutlet var enterpriseDOSButton: NSPopUpButton! | ||||||
| @@ -110,6 +111,7 @@ class MachinePicker: NSObject, NSTableViewDataSource, NSTableViewDelegate { | |||||||
|  |  | ||||||
| 		// Enterprise settings | 		// Enterprise settings | ||||||
| 		enterpriseModelButton.selectItem(withTag: standardUserDefaults.integer(forKey: "new.enterpriseModel")) | 		enterpriseModelButton.selectItem(withTag: standardUserDefaults.integer(forKey: "new.enterpriseModel")) | ||||||
|  | 		enterpriseSpeedButton.selectItem(withTag: standardUserDefaults.integer(forKey: "new.enterpriseSpeed")) | ||||||
| 		enterpriseEXOSButton.selectItem(withTag: standardUserDefaults.integer(forKey: "new.enterpriseEXOSVersion")) | 		enterpriseEXOSButton.selectItem(withTag: standardUserDefaults.integer(forKey: "new.enterpriseEXOSVersion")) | ||||||
| 		enterpriseBASICButton.selectItem(withTag: standardUserDefaults.integer(forKey: "new.enterpriseBASICVersion")) | 		enterpriseBASICButton.selectItem(withTag: standardUserDefaults.integer(forKey: "new.enterpriseBASICVersion")) | ||||||
| 		enterpriseDOSButton.selectItem(withTag: standardUserDefaults.integer(forKey: "new.enterpriseDOS")) | 		enterpriseDOSButton.selectItem(withTag: standardUserDefaults.integer(forKey: "new.enterpriseDOS")) | ||||||
| @@ -166,6 +168,7 @@ class MachinePicker: NSObject, NSTableViewDataSource, NSTableViewDelegate { | |||||||
|  |  | ||||||
| 		// Enterprise settings | 		// Enterprise settings | ||||||
| 		standardUserDefaults.set(enterpriseModelButton.selectedTag(), forKey: "new.enterpriseModel") | 		standardUserDefaults.set(enterpriseModelButton.selectedTag(), forKey: "new.enterpriseModel") | ||||||
|  | 		standardUserDefaults.set(enterpriseSpeedButton.selectedTag(), forKey: "new.enterpriseSpeed") | ||||||
| 		standardUserDefaults.set(enterpriseEXOSButton.selectedTag(), forKey: "new.enterpriseEXOSVersion") | 		standardUserDefaults.set(enterpriseEXOSButton.selectedTag(), forKey: "new.enterpriseEXOSVersion") | ||||||
| 		standardUserDefaults.set(enterpriseBASICButton.selectedTag(), forKey: "new.enterpriseBASICVersion") | 		standardUserDefaults.set(enterpriseBASICButton.selectedTag(), forKey: "new.enterpriseBASICVersion") | ||||||
| 		standardUserDefaults.set(enterpriseDOSButton.selectedTag(), forKey: "new.enterpriseDOS") | 		standardUserDefaults.set(enterpriseDOSButton.selectedTag(), forKey: "new.enterpriseDOS") | ||||||
| @@ -253,14 +256,14 @@ class MachinePicker: NSObject, NSTableViewDataSource, NSTableViewDelegate { | |||||||
| 					default:	model = .ROM00 | 					default:	model = .ROM00 | ||||||
| 				} | 				} | ||||||
|  |  | ||||||
| 				let memorySize = Kilobytes(appleIIgsMemorySizeButton.selectedItem!.tag) | 				let memorySize = Kilobytes(appleIIgsMemorySizeButton.selectedTag()) | ||||||
| 				return CSStaticAnalyser(appleIIgsModel: model, memorySize: memorySize) | 				return CSStaticAnalyser(appleIIgsModel: model, memorySize: memorySize) | ||||||
|  |  | ||||||
| 			case "atarist": | 			case "atarist": | ||||||
| 				return CSStaticAnalyser(atariSTModel: .model512k) | 				return CSStaticAnalyser(atariSTModel: .model512k) | ||||||
|  |  | ||||||
| 			case "cpc": | 			case "cpc": | ||||||
| 				switch cpcModelTypeButton.selectedItem!.tag { | 				switch cpcModelTypeButton.selectedTag() { | ||||||
| 					case 464:	return CSStaticAnalyser(amstradCPCModel: .model464) | 					case 464:	return CSStaticAnalyser(amstradCPCModel: .model464) | ||||||
| 					case 664:	return CSStaticAnalyser(amstradCPCModel: .model664) | 					case 664:	return CSStaticAnalyser(amstradCPCModel: .model664) | ||||||
| 					case 6128:	fallthrough | 					case 6128:	fallthrough | ||||||
| @@ -276,15 +279,22 @@ class MachinePicker: NSObject, NSTableViewDataSource, NSTableViewDelegate { | |||||||
|  |  | ||||||
| 			case "enterprise": | 			case "enterprise": | ||||||
| 				var model: CSMachineEnterpriseModel = .model128 | 				var model: CSMachineEnterpriseModel = .model128 | ||||||
| 				switch enterpriseModelButton.selectedItem!.tag { | 				switch enterpriseModelButton.selectedTag() { | ||||||
| 					case 64:	model = .model64 | 					case 64:	model = .model64 | ||||||
| 					case 256:	model = .model256 | 					case 256:	model = .model256 | ||||||
| 					case 128:	fallthrough | 					case 128:	fallthrough | ||||||
| 					default:	model = .model128 | 					default:	model = .model128 | ||||||
| 				} | 				} | ||||||
|  |  | ||||||
|  | 				var speed: CSMachineEnterpriseSpeed = .speed4MHz | ||||||
|  | 				switch enterpriseSpeedButton.selectedTag() { | ||||||
|  | 					case 6:		speed = .speed6MHz | ||||||
|  | 					case 4:		fallthrough | ||||||
|  | 					default:	speed = .speed4MHz | ||||||
|  | 				} | ||||||
|  |  | ||||||
| 				var exos: CSMachineEnterpriseEXOS = .version21 | 				var exos: CSMachineEnterpriseEXOS = .version21 | ||||||
| 				switch enterpriseEXOSButton.selectedItem!.tag { | 				switch enterpriseEXOSButton.selectedTag() { | ||||||
| 					case 10:	exos = .version10 | 					case 10:	exos = .version10 | ||||||
| 					case 20:	exos = .version20 | 					case 20:	exos = .version20 | ||||||
| 					case 21:	fallthrough | 					case 21:	fallthrough | ||||||
| @@ -307,10 +317,10 @@ class MachinePicker: NSObject, NSTableViewDataSource, NSTableViewDelegate { | |||||||
| 					default:	dos = .dosNone | 					default:	dos = .dosNone | ||||||
| 				} | 				} | ||||||
|  |  | ||||||
| 				return CSStaticAnalyser(enterpriseModel: model, exosVersion: exos, basicVersion: basic, dos: dos) | 				return CSStaticAnalyser(enterpriseModel: model, speed: speed, exosVersion: exos, basicVersion: basic, dos: dos) | ||||||
|  |  | ||||||
| 			case "mac": | 			case "mac": | ||||||
| 				switch macintoshModelTypeButton.selectedItem!.tag { | 				switch macintoshModelTypeButton.selectedTag() { | ||||||
| 					case 0:		return CSStaticAnalyser(macintoshModel: .model128k) | 					case 0:		return CSStaticAnalyser(macintoshModel: .model128k) | ||||||
| 					case 1:		return CSStaticAnalyser(macintoshModel: .model512k) | 					case 1:		return CSStaticAnalyser(macintoshModel: .model512k) | ||||||
| 					case 2:		return CSStaticAnalyser(macintoshModel: .model512ke) | 					case 2:		return CSStaticAnalyser(macintoshModel: .model512ke) | ||||||
| @@ -320,7 +330,7 @@ class MachinePicker: NSObject, NSTableViewDataSource, NSTableViewDelegate { | |||||||
|  |  | ||||||
| 			case "msx": | 			case "msx": | ||||||
| 				let hasDiskDrive = msxHasDiskDriveButton.state == .on | 				let hasDiskDrive = msxHasDiskDriveButton.state == .on | ||||||
| 				switch msxRegionButton.selectedItem!.tag { | 				switch msxRegionButton.selectedTag() { | ||||||
| 					case 2: | 					case 2: | ||||||
| 						return CSStaticAnalyser(msxRegion: .japanese, hasDiskDrive: hasDiskDrive) | 						return CSStaticAnalyser(msxRegion: .japanese, hasDiskDrive: hasDiskDrive) | ||||||
| 					case 1: | 					case 1: | ||||||
| @@ -341,7 +351,7 @@ class MachinePicker: NSObject, NSTableViewDataSource, NSTableViewDelegate { | |||||||
|  |  | ||||||
| 				} | 				} | ||||||
| 				var model: CSMachineOricModel = .oric1 | 				var model: CSMachineOricModel = .oric1 | ||||||
| 				switch oricModelTypeButton.selectedItem!.tag { | 				switch oricModelTypeButton.selectedTag() { | ||||||
| 					case 1:		model = .oricAtmos | 					case 1:		model = .oricAtmos | ||||||
| 					case 2:		model = .pravetz | 					case 2:		model = .pravetz | ||||||
| 					default:	break | 					default:	break | ||||||
| @@ -351,7 +361,7 @@ class MachinePicker: NSObject, NSTableViewDataSource, NSTableViewDelegate { | |||||||
|  |  | ||||||
| 			case "spectrum": | 			case "spectrum": | ||||||
| 				var model: CSMachineSpectrumModel = .plus2a | 				var model: CSMachineSpectrumModel = .plus2a | ||||||
| 				switch spectrumModelTypeButton.selectedItem!.tag { | 				switch spectrumModelTypeButton.selectedTag() { | ||||||
| 					case 16:	model = .sixteenK | 					case 16:	model = .sixteenK | ||||||
| 					case 48:	model = .fortyEightK | 					case 48:	model = .fortyEightK | ||||||
| 					case 128:	model = .oneTwoEightK | 					case 128:	model = .oneTwoEightK | ||||||
| @@ -364,9 +374,9 @@ class MachinePicker: NSObject, NSTableViewDataSource, NSTableViewDelegate { | |||||||
| 				return CSStaticAnalyser(spectrumModel: model) | 				return CSStaticAnalyser(spectrumModel: model) | ||||||
|  |  | ||||||
| 			case "vic20": | 			case "vic20": | ||||||
| 				let memorySize = Kilobytes(vic20MemorySizeButton.selectedItem!.tag) | 				let memorySize = Kilobytes(vic20MemorySizeButton.selectedTag()) | ||||||
| 				let hasC1540 = vic20HasC1540Button.state == .on | 				let hasC1540 = vic20HasC1540Button.state == .on | ||||||
| 				switch vic20RegionButton.selectedItem!.tag { | 				switch vic20RegionButton.selectedTag() { | ||||||
| 					case 1: | 					case 1: | ||||||
| 						return CSStaticAnalyser(vic20Region: .american, memorySize: memorySize, hasC1540: hasC1540) | 						return CSStaticAnalyser(vic20Region: .american, memorySize: memorySize, hasC1540: hasC1540) | ||||||
| 					case 2: | 					case 2: | ||||||
| @@ -381,10 +391,10 @@ class MachinePicker: NSObject, NSTableViewDataSource, NSTableViewDelegate { | |||||||
| 				} | 				} | ||||||
|  |  | ||||||
| 			case "zx80": | 			case "zx80": | ||||||
| 				return CSStaticAnalyser(zx80MemorySize: Kilobytes(zx80MemorySizeButton.selectedItem!.tag), useZX81ROM: zx80UsesZX81ROMButton.state == .on) | 				return CSStaticAnalyser(zx80MemorySize: Kilobytes(zx80MemorySizeButton.selectedTag()), useZX81ROM: zx80UsesZX81ROMButton.state == .on) | ||||||
|  |  | ||||||
| 			case "zx81": | 			case "zx81": | ||||||
| 				return CSStaticAnalyser(zx81MemorySize: Kilobytes(zx81MemorySizeButton.selectedItem!.tag)) | 				return CSStaticAnalyser(zx81MemorySize: Kilobytes(zx81MemorySizeButton.selectedTag())) | ||||||
|  |  | ||||||
| 			default: return CSStaticAnalyser() | 			default: return CSStaticAnalyser() | ||||||
| 		} | 		} | ||||||
|   | |||||||
| @@ -23,78 +23,80 @@ | |||||||
| 	_interruptSource = std::make_unique<Enterprise::Dave::TimedInterruptSource>(); | 	_interruptSource = std::make_unique<Enterprise::Dave::TimedInterruptSource>(); | ||||||
| } | } | ||||||
|  |  | ||||||
| - (void)performTestExpectedToggles:(int)expectedToggles expectedInterrupts:(int)expectedInterrupts { | /// Tests that the programmable timer flag toggles and produces interrupts | ||||||
| 	// Check that the programmable timer flag toggles at a rate | /// at the rate specified, and that the flag toggles when interrupts are signalled. | ||||||
| 	// of 2kHz, causing 1000 interrupts, and that sequence points | - (void)performTestExpectedInterrupts:(double)expectedInterruptsPerSecond mode:(int)mode { | ||||||
| 	// are properly predicted. | 	// If a programmable timer mode is requested, synchronise both channels. | ||||||
|  | 	if(mode >= 2) { | ||||||
|  | 		_interruptSource->write(0xa7, 3); | ||||||
|  | 		_interruptSource->run_for(Cycles(2)); | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	// Set mode (and disable sync, if it was applied). | ||||||
|  | 	_interruptSource->write(0xa7, mode << 5); | ||||||
|  |  | ||||||
| 	int toggles = 0; | 	int toggles = 0; | ||||||
| 	int interrupts = 0; | 	int interrupts = 0; | ||||||
| 	uint8_t dividerState = _interruptSource->get_divider_state(); | 	uint8_t dividerState = _interruptSource->get_divider_state() & 1; | ||||||
| 	int nextSequencePoint = _interruptSource->get_next_sequence_point().as<int>(); | 	int nextSequencePoint = _interruptSource->get_next_sequence_point().as<int>(); | ||||||
| 	for(int c = 0; c < 250000; c++) { |  | ||||||
| 		// Advance one cycle. Clock is 250,000 Hz. |  | ||||||
| 		_interruptSource->run_for(Cycles(1)); |  | ||||||
|  |  | ||||||
| 		const uint8_t newDividerState = _interruptSource->get_divider_state(); |  | ||||||
| 		if((dividerState^newDividerState)&0x1) { |  | ||||||
| 			++toggles; |  | ||||||
| 		} |  | ||||||
| 		dividerState = newDividerState; |  | ||||||
|  |  | ||||||
|  | 	for(int c = 0; c < 250000 * 5; c++) { | ||||||
|  | 		// Advance one cycle. Clock is 500,000 Hz. | ||||||
|  | 		_interruptSource->run_for(Cycles(2)); | ||||||
| 		--nextSequencePoint; | 		--nextSequencePoint; | ||||||
|  |  | ||||||
|  | 		// Check for a status bit change. | ||||||
|  | 		const uint8_t newDividerState = _interruptSource->get_divider_state(); | ||||||
|  | 		const bool didToggle = (dividerState^newDividerState)&0x1; | ||||||
|  | 		dividerState = newDividerState; | ||||||
|  | 		toggles += didToggle; | ||||||
|  |  | ||||||
| 		// Check for the relevant interrupt. | 		// Check for the relevant interrupt. | ||||||
| 		const uint8_t newInterrupts = _interruptSource->get_new_interrupts(); | 		const uint8_t newInterrupts = _interruptSource->get_new_interrupts(); | ||||||
| 		if(newInterrupts & 0x02) { | 		if(newInterrupts) { | ||||||
| 			++interrupts; |  | ||||||
| 			XCTAssertEqual(nextSequencePoint, 0); | 			XCTAssertEqual(nextSequencePoint, 0); | ||||||
| 			XCTAssertEqual(dividerState&0x1, 0); |  | ||||||
| 			nextSequencePoint = _interruptSource->get_next_sequence_point().as<int>(); | 			nextSequencePoint = _interruptSource->get_next_sequence_point().as<int>(); | ||||||
| 		} |  | ||||||
|  |  | ||||||
| 		// Failing that, confirm that the other interrupt happend. | 			if(newInterrupts & 0x02) { | ||||||
| 		if(!nextSequencePoint) { | 				++interrupts; | ||||||
| 			XCTAssertTrue(newInterrupts & 0x08); | 				XCTAssertTrue(didToggle); | ||||||
| 			nextSequencePoint = _interruptSource->get_next_sequence_point().as<int>(); | 			} else { | ||||||
|  | 				// Failing that, confirm that the other interrupt happend. | ||||||
|  | 				XCTAssertTrue(newInterrupts & 0x08); | ||||||
|  | 			} | ||||||
| 		} | 		} | ||||||
|  |  | ||||||
| 		XCTAssertEqual(nextSequencePoint, _interruptSource->get_next_sequence_point().as<int>(), @"At cycle %d", c); | 		XCTAssertEqual(nextSequencePoint, _interruptSource->get_next_sequence_point().as<int>(), @"At cycle %d", c); | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	XCTAssertEqual(toggles, expectedToggles); | 	XCTAssertEqual(toggles, int(expectedInterruptsPerSecond * 5.0)); | ||||||
| 	XCTAssertEqual(interrupts, expectedInterrupts); | 	XCTAssertEqual(interrupts, int(expectedInterruptsPerSecond * 5.0)); | ||||||
| } | } | ||||||
|  |  | ||||||
| - (void)test1kHzTimer { | - (void)test1kHzTimer { | ||||||
| 	// Set 1kHz timer. | 	[self performTestExpectedInterrupts:1000.0 mode:0]; | ||||||
| 	_interruptSource->write(7, 0 << 5); |  | ||||||
| 	[self performTestExpectedToggles:2000 expectedInterrupts:1000]; |  | ||||||
| } | } | ||||||
|  |  | ||||||
| - (void)test50HzTimer { | - (void)test50HzTimer { | ||||||
| 	// Set 50Hz timer. | 	[self performTestExpectedInterrupts:50.0 mode:1]; | ||||||
| 	_interruptSource->write(7, 1 << 5); |  | ||||||
| 	[self performTestExpectedToggles:100 expectedInterrupts:50]; |  | ||||||
| } | } | ||||||
|  |  | ||||||
| - (void)testTone0Timer { | - (void)testTone0Timer { | ||||||
| 	// Set tone generator 0 as the interrupt source, with a divider of 137; | 	// Set tone generator 0 as the interrupt source, with a divider of 137; | ||||||
| 	// apply sync momentarily. | 	// apply sync momentarily. | ||||||
| 	_interruptSource->write(7, 2 << 5); |  | ||||||
| 	_interruptSource->write(0, 137); | 	_interruptSource->write(0, 137); | ||||||
| 	_interruptSource->write(1, 0); | 	_interruptSource->write(1, 0); | ||||||
|  |  | ||||||
| 	[self performTestExpectedToggles:250000/138 expectedInterrupts:250000/(138*2)]; | 	[self performTestExpectedInterrupts:250000.0/(138.0 * 2.0) mode:2]; | ||||||
| } | } | ||||||
|  |  | ||||||
| - (void)testTone1Timer { | - (void)testTone1Timer { | ||||||
| 	// Set tone generator 1 as the interrupt source, with a divider of 961; | 	// Set tone generator 1 as the interrupt source, with a divider of 961; | ||||||
| 	// apply sync momentarily. | 	// apply sync momentarily. | ||||||
| 	_interruptSource->write(7, 3 << 5); |  | ||||||
| 	_interruptSource->write(2, 961 & 0xff); | 	_interruptSource->write(2, 961 & 0xff); | ||||||
| 	_interruptSource->write(3, (961 >> 8) & 0xff); | 	_interruptSource->write(3, (961 >> 8) & 0xff); | ||||||
|  |  | ||||||
| 	[self performTestExpectedToggles:250000/961 expectedInterrupts:250000/(961*2)]; | 	[self performTestExpectedInterrupts:250000.0/(962.0 * 2.0) mode:3]; | ||||||
| } | } | ||||||
|  |  | ||||||
| @end | @end | ||||||
|   | |||||||
| @@ -1073,6 +1073,11 @@ void MainWindow::start_enterprise() { | |||||||
| 		case 2:		target->model = Target::Model::Enterprise256;	break; | 		case 2:		target->model = Target::Model::Enterprise256;	break; | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
|  | 	switch(ui->enterpriseSpeedComboBox->currentIndex()) { | ||||||
|  | 		default:	target->speed = Target::Speed::FourMHz;	break; | ||||||
|  | 		case 1:		target->speed = Target::Speed::SixMHz;	break; | ||||||
|  | 	} | ||||||
|  |  | ||||||
| 	switch(ui->enterpriseEXOSComboBox->currentIndex()) { | 	switch(ui->enterpriseEXOSComboBox->currentIndex()) { | ||||||
| 		default:	target->exos_version = Target::EXOSVersion::v10;	break; | 		default:	target->exos_version = Target::EXOSVersion::v10;	break; | ||||||
| 		case 1:		target->exos_version = Target::EXOSVersion::v20;	break; | 		case 1:		target->exos_version = Target::EXOSVersion::v20;	break; | ||||||
| @@ -1252,6 +1257,7 @@ void MainWindow::launchTarget(std::unique_ptr<Analyser::Static::Target> &&target | |||||||
| 																		\ | 																		\ | ||||||
| 	/* Enterprise. */													\ | 	/* Enterprise. */													\ | ||||||
| 	ComboBox(enterpriseModelComboBox, "enterprise.model");				\ | 	ComboBox(enterpriseModelComboBox, "enterprise.model");				\ | ||||||
|  | 	ComboBox(enterpriseSpeedComboBox, "enterprise.speed");				\ | ||||||
| 	ComboBox(enterpriseEXOSComboBox, "enterprise.exos");				\ | 	ComboBox(enterpriseEXOSComboBox, "enterprise.exos");				\ | ||||||
| 	ComboBox(enterpriseBASICComboBox, "enterprise.basic");				\ | 	ComboBox(enterpriseBASICComboBox, "enterprise.basic");				\ | ||||||
| 	ComboBox(enterpriseDOSComboBox, "enterprise.dos");					\ | 	ComboBox(enterpriseDOSComboBox, "enterprise.dos");					\ | ||||||
| @@ -1309,6 +1315,7 @@ void MainWindow::restoreSelections() { | |||||||
| // MARK: - Activity observation | // MARK: - Activity observation | ||||||
|  |  | ||||||
| void MainWindow::addActivityObserver() { | void MainWindow::addActivityObserver() { | ||||||
|  | 	ledStatuses.clear(); | ||||||
| 	auto activitySource = machine->activity_source(); | 	auto activitySource = machine->activity_source(); | ||||||
| 	if(!activitySource) return; | 	if(!activitySource) return; | ||||||
|  |  | ||||||
|   | |||||||
| @@ -27,7 +27,7 @@ | |||||||
|     <item> |     <item> | ||||||
|      <widget class="QTabWidget" name="machineSelectionTabs"> |      <widget class="QTabWidget" name="machineSelectionTabs"> | ||||||
|       <property name="currentIndex"> |       <property name="currentIndex"> | ||||||
|        <number>1</number> |        <number>5</number> | ||||||
|       </property> |       </property> | ||||||
|       <widget class="QWidget" name="appleIITab"> |       <widget class="QWidget" name="appleIITab"> | ||||||
|        <attribute name="title"> |        <attribute name="title"> | ||||||
| @@ -346,13 +346,34 @@ | |||||||
|              </widget> |              </widget> | ||||||
|             </item> |             </item> | ||||||
|             <item row="1" column="0"> |             <item row="1" column="0"> | ||||||
|  |              <widget class="QLabel" name="enterpriseSpeedLabel"> | ||||||
|  |               <property name="text"> | ||||||
|  |                <string>Speed:</string> | ||||||
|  |               </property> | ||||||
|  |              </widget> | ||||||
|  |             </item> | ||||||
|  |             <item row="1" column="1"> | ||||||
|  |              <widget class="QComboBox" name="enterpriseSpeedComboBox"> | ||||||
|  |               <item> | ||||||
|  |                <property name="text"> | ||||||
|  |                 <string>4 MHz</string> | ||||||
|  |                </property> | ||||||
|  |               </item> | ||||||
|  |               <item> | ||||||
|  |                <property name="text"> | ||||||
|  |                 <string>6 MHz</string> | ||||||
|  |                </property> | ||||||
|  |               </item> | ||||||
|  |              </widget> | ||||||
|  |             </item> | ||||||
|  |             <item row="2" column="0"> | ||||||
|              <widget class="QLabel" name="enterpriseEXOSLabel"> |              <widget class="QLabel" name="enterpriseEXOSLabel"> | ||||||
|               <property name="text"> |               <property name="text"> | ||||||
|                <string>EXOS:</string> |                <string>EXOS:</string> | ||||||
|               </property> |               </property> | ||||||
|              </widget> |              </widget> | ||||||
|             </item> |             </item> | ||||||
|             <item row="1" column="1"> |             <item row="2" column="1"> | ||||||
|              <widget class="QComboBox" name="enterpriseEXOSComboBox"> |              <widget class="QComboBox" name="enterpriseEXOSComboBox"> | ||||||
|               <item> |               <item> | ||||||
|                <property name="text"> |                <property name="text"> | ||||||
| @@ -371,14 +392,14 @@ | |||||||
|               </item> |               </item> | ||||||
|              </widget> |              </widget> | ||||||
|             </item> |             </item> | ||||||
|             <item row="2" column="0"> |             <item row="3" column="0"> | ||||||
|              <widget class="QLabel" name="enterpriseBASICLabel"> |              <widget class="QLabel" name="enterpriseBASICLabel"> | ||||||
|               <property name="text"> |               <property name="text"> | ||||||
|                <string>BASIC:</string> |                <string>BASIC:</string> | ||||||
|               </property> |               </property> | ||||||
|              </widget> |              </widget> | ||||||
|             </item> |             </item> | ||||||
|             <item row="2" column="1"> |             <item row="3" column="1"> | ||||||
|              <widget class="QComboBox" name="enterpriseBASICComboBox"> |              <widget class="QComboBox" name="enterpriseBASICComboBox"> | ||||||
|               <item> |               <item> | ||||||
|                <property name="text"> |                <property name="text"> | ||||||
| @@ -402,14 +423,14 @@ | |||||||
|               </item> |               </item> | ||||||
|              </widget> |              </widget> | ||||||
|             </item> |             </item> | ||||||
|             <item row="3" column="0"> |             <item row="4" column="0"> | ||||||
|              <widget class="QLabel" name="enterpriseDOSLabel"> |              <widget class="QLabel" name="enterpriseDOSLabel"> | ||||||
|               <property name="text"> |               <property name="text"> | ||||||
|                <string>DOS:</string> |                <string>DOS:</string> | ||||||
|               </property> |               </property> | ||||||
|              </widget> |              </widget> | ||||||
|             </item> |             </item> | ||||||
|             <item row="3" column="1"> |             <item row="4" column="1"> | ||||||
|              <widget class="QComboBox" name="enterpriseDOSComboBox"> |              <widget class="QComboBox" name="enterpriseDOSComboBox"> | ||||||
|               <item> |               <item> | ||||||
|                <property name="text"> |                <property name="text"> | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user