1
0
mirror of https://github.com/TomHarte/CLK.git synced 2024-06-27 01:29:31 +00:00
CLK/Machines/Electron/Video.hpp
2024-01-16 23:34:46 -05:00

139 lines
3.7 KiB
C++

//
// Video.hpp
// Clock Signal
//
// Created by Thomas Harte on 10/12/2016.
// Copyright 2016 Thomas Harte. All rights reserved.
//
#pragma once
#include "../../Outputs/CRT/CRT.hpp"
#include "../../ClockReceiver/ClockReceiver.hpp"
#include "Interrupts.hpp"
#include <vector>
namespace Electron {
/*!
Implements the Electron's video subsystem plus appropriate signalling.
The Electron has an interlaced fully-bitmapped display with six different output modes,
running either at 40 or 80 columns. Memory is shared between video and CPU; when the video
is accessing it the CPU may not.
*/
class VideoOutput {
public:
/*!
Instantiates a VideoOutput that will read its pixels from @c memory.
The pointer supplied should be to address 0 in the unexpanded Electron's memory map.
*/
VideoOutput(uint8_t *memory);
/// Produces the next @c cycles of video output.
void run_for(const Cycles cycles);
/// Sets the destination for output.
void set_scan_target(Outputs::Display::ScanTarget *scan_target);
/// Gets the current scan status.
Outputs::Display::ScanStatus get_scaled_scan_status() const;
/// Sets the type of output.
void set_display_type(Outputs::Display::DisplayType);
/// Gets the type of output.
Outputs::Display::DisplayType get_display_type() const;
/*!
Writes @c value to the register at @c address. May mutate the results of @c get_next_interrupt,
@c get_cycles_until_next_ram_availability and @c get_memory_access_range.
*/
void write(int address, uint8_t value);
/*!
@returns the next interrupt that should be generated as a result of the video hardware.
The time until signalling returned is the number of cycles after the final one triggered
by the most recent call to @c run_for.
This result may be mutated by calls to @c write.
*/
Cycles next_sequence_point();
/*!
@returns a bit mask of all interrupts that have been triggered since the last call to get_interrupt().
*/
Electron::Interrupt get_interrupts();
/*!
@returns the number of cycles after (final cycle of last run_for batch + @c from_time)
before the video circuits will allow the CPU to access RAM.
*/
unsigned int get_cycles_until_next_ram_availability(int from_time);
struct Range {
uint16_t low_address, high_address;
};
/*!
@returns the range of addresses that the video might read from.
*/
Range get_memory_access_range();
private:
inline void start_pixel_line();
inline void end_pixel_line();
inline void output_pixels(int number_of_cycles);
inline void setup_base_address();
int output_position_ = 0;
uint8_t palette_[16];
uint8_t screen_mode_ = 6;
uint16_t screen_mode_base_address_ = 0;
uint16_t start_screen_address_ = 0;
uint8_t *ram_;
struct {
uint32_t forty1bpp[256];
uint16_t forty2bpp[256];
uint64_t eighty1bpp[256];
uint32_t eighty2bpp[256];
uint16_t eighty4bpp[256];
} palette_tables_;
// Display generation.
uint16_t start_line_address_ = 0;
uint16_t current_screen_address_ = 0;
int current_pixel_line_ = -1;
int current_pixel_column_ = 0;
int current_character_row_ = 0;
uint8_t last_pixel_byte_ = 0;
bool is_blank_line_ = false;
// CRT output
uint8_t *current_output_target_ = nullptr;
uint8_t *initial_output_target_ = nullptr;
int current_output_divider_ = 1;
Outputs::CRT::CRT crt_;
struct DrawAction {
enum Type {
Sync, ColourBurst, Blank, Pixels
} type;
int length;
DrawAction(Type type, int length) : type(type), length(length) {}
};
std::vector<DrawAction> screen_map_;
void setup_screen_map();
void emplace_blank_line();
void emplace_pixel_line();
std::size_t screen_map_pointer_ = 0;
int cycles_into_draw_action_ = 0;
Electron::Interrupt interrupts_ = Electron::Interrupt(0);
};
}