diff --git a/Components/6560/6560.hpp b/Components/6560/6560.hpp
index 4cb48f246..48f1f105d 100644
--- a/Components/6560/6560.hpp
+++ b/Components/6560/6560.hpp
@@ -287,7 +287,7 @@ template <class BusHandler> class MOS6560 {
 						case State::Sync:			crt_->output_sync(cycles_in_state_ * 4);														break;
 						case State::ColourBurst:	crt_->output_colour_burst(cycles_in_state_ * 4, (is_odd_frame_ || is_odd_line_) ? 128 : 0);		break;
 						case State::Border:			output_border(cycles_in_state_ * 4);															break;
-						case State::Pixels:			crt_->output_data(cycles_in_state_ * 4, 1);														break;
+						case State::Pixels:			crt_->output_data(cycles_in_state_ * 4);														break;
 					}
 					output_state_ = this_state_;
 					cycles_in_state_ = 0;
diff --git a/Components/9918/9918.cpp b/Components/9918/9918.cpp
index f6ba07db1..f72b785ff 100644
--- a/Components/9918/9918.cpp
+++ b/Components/9918/9918.cpp
@@ -536,7 +536,8 @@ void TMS9918::run_for(const HalfCycles cycles) {
 					}
 
 					if(output_column_ == first_right_border_column_) {
-						crt_->output_data(static_cast<unsigned int>(first_right_border_column_ - first_pixel_column_) * 4, 4);
+						const unsigned int data_length = static_cast<unsigned int>(first_right_border_column_ - first_pixel_column_);
+						crt_->output_data(data_length * 4, data_length);
 						pixel_target_ = nullptr;
 					}
 				}
diff --git a/Machines/AmstradCPC/AmstradCPC.cpp b/Machines/AmstradCPC/AmstradCPC.cpp
index cfd9c2e8f..e5278fec6 100644
--- a/Machines/AmstradCPC/AmstradCPC.cpp
+++ b/Machines/AmstradCPC/AmstradCPC.cpp
@@ -203,7 +203,7 @@ class CRTCBusHandler {
 				} else {
 					if(was_enabled_) {
 						if(cycles_) {
-							crt_->output_data(cycles_ * 16, pixel_divider_);
+							crt_->output_data(cycles_ * 16, cycles_ * 16 / pixel_divider_);
 							pixel_pointer_ = pixel_data_ = nullptr;
 						}
 					} else {
@@ -267,7 +267,7 @@ class CRTCBusHandler {
 					// widths so it's not necessarily possible to predict the correct number in advance
 					// and using the upper bound could lead to inefficient behaviour
 					if(pixel_pointer_ == pixel_data_ + 320) {
-						crt_->output_data(cycles_ * 16, pixel_divider_);
+						crt_->output_data(cycles_ * 16, cycles_ * 16 / pixel_divider_);
 						pixel_pointer_ = pixel_data_ = nullptr;
 						cycles_ = 0;
 					}
diff --git a/Machines/AppleII/Video.hpp b/Machines/AppleII/Video.hpp
index b2ec40a1f..2394e333c 100644
--- a/Machines/AppleII/Video.hpp
+++ b/Machines/AppleII/Video.hpp
@@ -38,9 +38,14 @@ template <class BusHandler> class Video {
 //					"uint texValue = texture(sampler, coordinate).r;"
 //					"texValue <<= int(icoordinate.x * 8) & 7;"
 //					"return float(texValue & 128u);"
+
 					"uint texValue = texture(sampler, coordinate).r;"
 					"texValue <<= uint(icoordinate.x * 7.0) % 7u;"
 					"return float(texValue & 64u);"
+
+//					"uint texValue = texture(sampler, coordinate).r;"
+//					"texValue <<= uint(mod(icoordinate.x, 1.0) * 7.0);"
+//					"return float(texValue & 64u);"
 				"}");
 
 				// TODO: the above has precision issues. Fix!
@@ -91,7 +96,7 @@ template <class BusHandler> class Video {
 							const int pixel_end = std::min(40, ending_column);
 							const int character_row = row_ >> 3;
 							const int pixel_row = row_ & 7;
-							const uint16_t line_address = static_cast<uint16_t>(0x400 + (character_row >> 3) * 40 + ((character_row&7) << 7));
+							const uint16_t line_address = static_cast<uint16_t>(0x400 + (video_page_ * 0x400) + (character_row >> 3) * 40 + ((character_row&7) << 7));
 
 							for(int c = column_; c < pixel_end; ++c) {
 								const uint16_t address = static_cast<uint16_t>(line_address + c);
@@ -102,8 +107,10 @@ template <class BusHandler> class Video {
 								pixel_pointer_[c] = character_rom_[character_address] ^ ((character & 0x80) ? 0x00 : 0xff);
 							}
 
+							// TODO: graphics; 0x2000+ for high resolution
+
 							if(ending_column >= 40) {
-								crt_->output_data(280, 7);
+								crt_->output_data(280, 40);
 							}
 						} else {
 							if(ending_column >= 40) {
@@ -163,6 +170,7 @@ template <class BusHandler> class Video {
 		}
 
 		void set_video_page(int page) {
+			video_page_ = page;
 			printf("Video page: %d\n", page);
 		}
 
diff --git a/Machines/Atari2600/TIA.cpp b/Machines/Atari2600/TIA.cpp
index c26580a98..af92c71e9 100644
--- a/Machines/Atari2600/TIA.cpp
+++ b/Machines/Atari2600/TIA.cpp
@@ -437,7 +437,10 @@ void TIA::output_for_cycles(int number_of_cycles) {
 	if(output_mode_ & blank_flag) {
 		if(pixel_target_) {
 			output_pixels(pixels_start_location_, output_cursor);
-			if(crt_) crt_->output_data(static_cast<unsigned int>(output_cursor - pixels_start_location_) * 2, 2);
+			if(crt_) {
+				const unsigned int data_length = static_cast<unsigned int>(output_cursor - pixels_start_location_);
+				crt_->output_data(data_length * 2, data_length);
+			}
 			pixel_target_ = nullptr;
 			pixels_start_location_ = 0;
 		}
@@ -459,7 +462,8 @@ void TIA::output_for_cycles(int number_of_cycles) {
 		}
 
 		if(horizontal_counter_ == cycles_per_line && crt_) {
-			crt_->output_data(static_cast<unsigned int>(output_cursor - pixels_start_location_) * 2, 2);
+			const unsigned int data_length = static_cast<unsigned int>(output_cursor - pixels_start_location_);
+			crt_->output_data(data_length * 2, data_length);
 			pixel_target_ = nullptr;
 			pixels_start_location_ = 0;
 		}
diff --git a/Machines/Electron/Video.cpp b/Machines/Electron/Video.cpp
index fb3e3eefe..a7c218546 100644
--- a/Machines/Electron/Video.cpp
+++ b/Machines/Electron/Video.cpp
@@ -97,7 +97,10 @@ void VideoOutput::start_pixel_line() {
 }
 
 void VideoOutput::end_pixel_line() {
-	if(current_output_target_) crt_->output_data(static_cast<unsigned int>((current_output_target_ - initial_output_target_) * current_output_divider_), current_output_divider_);
+	if(current_output_target_) {
+		const unsigned int data_length = static_cast<unsigned int>(current_output_target_ - initial_output_target_);
+		crt_->output_data(data_length * current_output_divider_, data_length);
+	}
 	current_character_row_++;
 }
 
@@ -115,7 +118,10 @@ void VideoOutput::output_pixels(unsigned int number_of_cycles) {
 		}
 
 		if(!initial_output_target_ || divider != current_output_divider_) {
-			if(current_output_target_) crt_->output_data(static_cast<unsigned int>((current_output_target_ - initial_output_target_) * current_output_divider_), current_output_divider_);
+			if(current_output_target_) {
+				const unsigned int data_length = static_cast<unsigned int>(current_output_target_ - initial_output_target_);
+				crt_->output_data(data_length * current_output_divider_, data_length);
+			}
 			current_output_divider_ = divider;
 			initial_output_target_ = current_output_target_ = crt_->allocate_write_area(640 / current_output_divider_, 4);
 		}
diff --git a/Machines/Oric/Video.cpp b/Machines/Oric/Video.cpp
index efb8fbfa2..6fbe91382 100644
--- a/Machines/Oric/Video.cpp
+++ b/Machines/Oric/Video.cpp
@@ -191,7 +191,7 @@ void VideoOutput::run_for(const Cycles cycles) {
 			}
 
 			if(h_counter == 40) {
-				crt_->output_data(40 * 6, 1);
+				crt_->output_data(40 * 6);
 			}
 		} else {
 			// this is a blank line (or the equivalent part of a pixel line)
diff --git a/Machines/ZX8081/Video.cpp b/Machines/ZX8081/Video.cpp
index 6abbb2a56..6617e6ac8 100644
--- a/Machines/ZX8081/Video.cpp
+++ b/Machines/ZX8081/Video.cpp
@@ -62,7 +62,7 @@ void Video::flush(bool next_sync) {
 			unsigned int data_length = static_cast<unsigned int>(line_data_pointer_ - line_data_) * HalfCyclesPerByte;
 			if(data_length < cycles_since_update_ || next_sync) {
 				unsigned int output_length = std::min(data_length, cycles_since_update_);
-				crt_->output_data(output_length, HalfCyclesPerByte);
+				crt_->output_data(output_length, output_length / HalfCyclesPerByte);
 				line_data_pointer_ = line_data_ = nullptr;
 				cycles_since_update_ -= output_length;
 			} else return;
@@ -100,7 +100,7 @@ void Video::output_byte(uint8_t byte) {
 	if(line_data_) {
 		// If the buffer is full, output it now and obtain a new one
 		if(line_data_pointer_ - line_data_ == StandardAllocationSize) {
-			crt_->output_data(StandardAllocationSize, HalfCyclesPerByte);
+			crt_->output_data(StandardAllocationSize * HalfCyclesPerByte, StandardAllocationSize);
 			cycles_since_update_ -= StandardAllocationSize * HalfCyclesPerByte;
 			line_data_pointer_ = line_data_ = crt_->allocate_write_area(StandardAllocationSize);
 			if(!line_data_) return;
diff --git a/Outputs/CRT/CRT.cpp b/Outputs/CRT/CRT.cpp
index 82f582f6b..579820d17 100644
--- a/Outputs/CRT/CRT.cpp
+++ b/Outputs/CRT/CRT.cpp
@@ -125,11 +125,8 @@ Flywheel::SyncEvent CRT::get_next_horizontal_sync_event(bool hsync_is_requested,
 #define output_position_y()	(*reinterpret_cast<uint16_t *>(&next_output_run[OutputVertexOffsetOfVertical + 0]))
 #define output_tex_y()		(*reinterpret_cast<uint16_t *>(&next_output_run[OutputVertexOffsetOfVertical + 2]))
 
-#define source_input_position_x1()	(*reinterpret_cast<uint16_t *>(&next_run[SourceVertexOffsetOfInputStart + 0]))
 #define source_input_position_y()	(*reinterpret_cast<uint16_t *>(&next_run[SourceVertexOffsetOfInputStart + 2]))
-#define source_input_position_x2()	(*reinterpret_cast<uint16_t *>(&next_run[SourceVertexOffsetOfEnds + 0]))
 #define source_output_position_x1()	(*reinterpret_cast<uint16_t *>(&next_run[SourceVertexOffsetOfOutputStart + 0]))
-#define source_output_position_y()	(*reinterpret_cast<uint16_t *>(&next_run[SourceVertexOffsetOfOutputStart + 2]))
 #define source_output_position_x2()	(*reinterpret_cast<uint16_t *>(&next_run[SourceVertexOffsetOfEnds + 2]))
 #define source_phase()				next_run[SourceVertexOffsetOfPhaseTimeAndAmplitude + 0]
 #define source_amplitude()			next_run[SourceVertexOffsetOfPhaseTimeAndAmplitude + 1]
@@ -217,6 +214,9 @@ void CRT::advance_cycles(unsigned int number_of_cycles, bool hsync_requested, bo
 						output_tex_y() = output_y;
 						output_x2() = static_cast<uint16_t>(horizontal_flywheel_->get_current_output_position());
 					}
+
+					// TODO: below I've assumed a one-to-one correspondance with output runs and input data; that's
+					// obviously not completely sustainable. It's a latent bug.
 					openGL_output_builder_.array_builder.flush(
 						[=] (uint8_t *input_buffer, std::size_t input_size, uint8_t *output_buffer, std::size_t output_size) {
 							openGL_output_builder_.texture_builder.flush(
@@ -264,15 +264,11 @@ void CRT::advance_cycles(unsigned int number_of_cycles, bool hsync_requested, bo
 #undef output_position_y
 #undef output_tex_y
 
-#undef source_input_position_x1
 #undef source_input_position_y
-#undef source_input_position_x2
 #undef source_output_position_x1
-#undef source_output_position_y
 #undef source_output_position_x2
 #undef source_phase
 #undef source_amplitude
-#undef source_phase_time
 
 // MARK: - stream feeding methods
 
@@ -384,8 +380,8 @@ void CRT::set_immediate_default_phase(float phase) {
 	phase_numerator_ = static_cast<unsigned int>(phase * static_cast<float>(phase_denominator_));
 }
 
-void CRT::output_data(unsigned int number_of_cycles, unsigned int source_divider) {
-	openGL_output_builder_.texture_builder.reduce_previous_allocation_to(number_of_cycles / source_divider);
+void CRT::output_data(unsigned int number_of_cycles, unsigned int number_of_samples) {
+	openGL_output_builder_.texture_builder.reduce_previous_allocation_to(number_of_samples);
 	Scan scan;
 	scan.type = Scan::Type::Data;
 	scan.number_of_cycles = number_of_cycles;
diff --git a/Outputs/CRT/CRT.hpp b/Outputs/CRT/CRT.hpp
index be156e071..a03f29c71 100644
--- a/Outputs/CRT/CRT.hpp
+++ b/Outputs/CRT/CRT.hpp
@@ -180,17 +180,21 @@ class CRT {
 		void output_level(unsigned int number_of_cycles);
 
 		/*!	Declares that the caller has created a run of data via @c allocate_write_area and @c get_write_target_for_buffer
-			that is at least @c number_of_cycles long, and that the first @c number_of_cycles/source_divider should be spread
-			over that amount of time.
+			that is at least @c number_of_samples long, and that the first @c number_of_samples should be spread
+			over @c number_of_cycles.
 
 			@param number_of_cycles The amount of data to output.
 
-			@param source_divider A divider for source data; if the divider is 1 then one source pixel is output every cycle,
-			if it is 2 then one source pixel covers two cycles; if it is n then one source pixel covers n cycles.
+			@param number_of_samples The number of samples of input data to output.
 
 			@see @c allocate_write_area , @c get_write_target_for_buffer
 		*/
-		void output_data(unsigned int number_of_cycles, unsigned int source_divider);
+		void output_data(unsigned int number_of_cycles, unsigned int number_of_samples);
+
+		/*! A shorthand form for output_data that assumes the number of cycles to output for is the same as the number of samples. */
+		void output_data(unsigned int number_of_cycles) {
+			output_data(number_of_cycles, number_of_cycles);
+		}
 
 		/*!	Outputs a colour burst.