2016-12-11 02:07:52 +00:00
|
|
|
//
|
|
|
|
// Video.hpp
|
|
|
|
// Clock Signal
|
|
|
|
//
|
|
|
|
// Created by Thomas Harte on 10/12/2016.
|
2018-05-13 19:19:52 +00:00
|
|
|
// Copyright 2016 Thomas Harte. All rights reserved.
|
2016-12-11 02:07:52 +00:00
|
|
|
//
|
|
|
|
|
|
|
|
#ifndef Machines_Electron_Video_hpp
|
|
|
|
#define Machines_Electron_Video_hpp
|
|
|
|
|
|
|
|
#include "../../Outputs/CRT/CRT.hpp"
|
2017-07-26 00:20:55 +00:00
|
|
|
#include "../../ClockReceiver/ClockReceiver.hpp"
|
2016-12-11 02:07:52 +00:00
|
|
|
#include "Interrupts.hpp"
|
|
|
|
|
2018-11-04 01:54:25 +00:00
|
|
|
#include <vector>
|
|
|
|
|
2016-12-11 02:07:52 +00:00
|
|
|
namespace Electron {
|
|
|
|
|
2016-12-16 00:43:04 +00:00
|
|
|
/*!
|
|
|
|
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.
|
|
|
|
*/
|
2017-07-27 11:40:02 +00:00
|
|
|
class VideoOutput {
|
2016-12-11 02:07:52 +00:00
|
|
|
public:
|
2016-12-16 00:43:04 +00:00
|
|
|
/*!
|
2018-11-15 02:52:57 +00:00
|
|
|
Instantiates a VideoOutput that will read its pixels from @c memory.
|
2018-11-04 03:40:39 +00:00
|
|
|
|
|
|
|
The pointer supplied should be to address 0 in the unexpanded Electron's memory map.
|
2016-12-16 00:43:04 +00:00
|
|
|
*/
|
2018-11-15 02:52:57 +00:00
|
|
|
VideoOutput(uint8_t *memory);
|
2016-12-16 00:43:04 +00:00
|
|
|
|
2017-07-25 02:36:42 +00:00
|
|
|
/// Produces the next @c cycles of video output.
|
2017-07-28 02:05:29 +00:00
|
|
|
void run_for(const Cycles cycles);
|
2016-12-11 02:07:52 +00:00
|
|
|
|
2018-11-15 02:52:57 +00:00
|
|
|
/// Sets the destination for output.
|
|
|
|
void set_scan_target(Outputs::Display::ScanTarget *scan_target);
|
|
|
|
|
2020-01-21 02:45:10 +00:00
|
|
|
/// Gets the current scan status.
|
2020-01-22 03:28:25 +00:00
|
|
|
Outputs::Display::ScanStatus get_scaled_scan_status() const;
|
2020-01-21 02:45:10 +00:00
|
|
|
|
2018-11-30 04:44:21 +00:00
|
|
|
/// Sets the type of output.
|
|
|
|
void set_display_type(Outputs::Display::DisplayType);
|
|
|
|
|
2020-03-18 02:06:20 +00:00
|
|
|
/// Gets the type of output.
|
2020-05-21 03:34:26 +00:00
|
|
|
Outputs::Display::DisplayType get_display_type() const;
|
2020-03-18 02:06:20 +00:00
|
|
|
|
2016-12-16 00:43:04 +00:00
|
|
|
/*!
|
|
|
|
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.
|
|
|
|
*/
|
2020-01-05 18:40:02 +00:00
|
|
|
void write(int address, uint8_t value);
|
2016-12-16 00:43:04 +00:00
|
|
|
|
|
|
|
/*!
|
|
|
|
Describes an interrupt the video hardware will generate by its identity and scheduling time.
|
|
|
|
*/
|
2016-12-11 23:34:49 +00:00
|
|
|
struct Interrupt {
|
2016-12-16 00:43:04 +00:00
|
|
|
/// The interrupt that will be signalled.
|
2016-12-11 23:34:49 +00:00
|
|
|
Electron::Interrupt interrupt;
|
2016-12-16 00:43:04 +00:00
|
|
|
/// The number of cycles until it is signalled.
|
2016-12-11 23:34:49 +00:00
|
|
|
int cycles;
|
|
|
|
};
|
2016-12-16 00:43:04 +00:00
|
|
|
/*!
|
|
|
|
@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
|
2017-07-25 02:36:42 +00:00
|
|
|
by the most recent call to @c run_for.
|
2016-12-11 02:07:52 +00:00
|
|
|
|
2020-01-05 18:40:02 +00:00
|
|
|
This result may be mutated by calls to @c write.
|
2016-12-16 00:43:04 +00:00
|
|
|
*/
|
|
|
|
Interrupt get_next_interrupt();
|
2016-12-11 02:07:52 +00:00
|
|
|
|
2016-12-16 00:43:04 +00:00
|
|
|
/*!
|
2017-07-25 02:36:42 +00:00
|
|
|
@returns the number of cycles after (final cycle of last run_for batch + @c from_time)
|
2016-12-16 00:43:04 +00:00
|
|
|
before the video circuits will allow the CPU to access RAM.
|
|
|
|
*/
|
2016-12-12 13:01:10 +00:00
|
|
|
unsigned int get_cycles_until_next_ram_availability(int from_time);
|
2016-12-11 23:34:49 +00:00
|
|
|
|
2016-12-16 00:20:14 +00:00
|
|
|
struct Range {
|
|
|
|
uint16_t low_address, high_address;
|
|
|
|
};
|
2016-12-16 00:43:04 +00:00
|
|
|
/*!
|
|
|
|
@returns the range of addresses that the video might read from.
|
|
|
|
*/
|
2016-12-16 00:20:14 +00:00
|
|
|
Range get_memory_access_range();
|
|
|
|
|
2016-12-11 02:07:52 +00:00
|
|
|
private:
|
|
|
|
inline void start_pixel_line();
|
|
|
|
inline void end_pixel_line();
|
2018-11-03 23:58:44 +00:00
|
|
|
inline void output_pixels(int number_of_cycles);
|
2016-12-23 03:46:02 +00:00
|
|
|
inline void setup_base_address();
|
2016-12-11 02:07:52 +00:00
|
|
|
|
2017-09-01 02:29:24 +00:00
|
|
|
int output_position_ = 0;
|
2016-12-11 02:07:52 +00:00
|
|
|
|
|
|
|
uint8_t palette_[16];
|
2017-09-01 02:29:24 +00:00
|
|
|
uint8_t screen_mode_ = 6;
|
|
|
|
uint16_t screen_mode_base_address_ = 0;
|
|
|
|
uint16_t start_screen_address_ = 0;
|
2016-12-11 02:07:52 +00:00
|
|
|
|
|
|
|
uint8_t *ram_;
|
|
|
|
struct {
|
2018-09-13 00:25:30 +00:00
|
|
|
uint32_t forty1bpp[256];
|
|
|
|
uint16_t forty2bpp[256];
|
|
|
|
uint64_t eighty1bpp[256];
|
|
|
|
uint32_t eighty2bpp[256];
|
|
|
|
uint16_t eighty4bpp[256];
|
2016-12-11 02:07:52 +00:00
|
|
|
} palette_tables_;
|
|
|
|
|
|
|
|
// Display generation.
|
2017-09-01 02:29:24 +00:00
|
|
|
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;
|
2016-12-11 02:07:52 +00:00
|
|
|
|
|
|
|
// CRT output
|
2017-09-01 02:29:24 +00:00
|
|
|
uint8_t *current_output_target_ = nullptr;
|
|
|
|
uint8_t *initial_output_target_ = nullptr;
|
2018-11-03 23:58:44 +00:00
|
|
|
int current_output_divider_ = 1;
|
2018-11-15 02:52:57 +00:00
|
|
|
Outputs::CRT::CRT crt_;
|
2016-12-15 23:07:46 +00:00
|
|
|
|
|
|
|
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();
|
2017-11-11 20:28:40 +00:00
|
|
|
std::size_t screen_map_pointer_ = 0;
|
2017-09-01 02:29:24 +00:00
|
|
|
int cycles_into_draw_action_ = 0;
|
2016-12-11 02:07:52 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
#endif /* Video_hpp */
|