1
0
mirror of https://github.com/TomHarte/CLK.git synced 2024-11-26 08:49:37 +00:00

Adds a first attempt at CRAM dot output. With a TODO.

This commit is contained in:
Thomas Harte 2018-10-26 19:26:46 -04:00
parent 916710353a
commit 521d603902
2 changed files with 116 additions and 88 deletions

View File

@ -93,7 +93,6 @@ TMS9918::TMS9918(Personality p):
"}"); "}");
crt_->set_video_signal(Outputs::CRT::VideoSignal::RGB); crt_->set_video_signal(Outputs::CRT::VideoSignal::RGB);
crt_->set_visible_area(Outputs::CRT::Rect(0.055f, 0.025f, 0.9f, 0.9f)); crt_->set_visible_area(Outputs::CRT::Rect(0.055f, 0.025f, 0.9f, 0.9f));
crt_->set_input_gamma(2.8f);
// The TMS remains in-phase with the NTSC colour clock; this is an empirical measurement // The TMS remains in-phase with the NTSC colour clock; this is an empirical measurement
// intended to produce the correct relationship between the hard edges between pixels and // intended to produce the correct relationship between the hard edges between pixels and
@ -321,19 +320,34 @@ void TMS9918::run_for(const HalfCycles cycles) {
if(read_cycles_pool) { if(read_cycles_pool) {
// Determine how much time has passed in the remainder of this line, and proceed. // Determine how much time has passed in the remainder of this line, and proceed.
const int read_cycles = std::min(342 - read_pointer_.column, read_cycles_pool); const int target_read_cycles = std::min(342 - read_pointer_.column, read_cycles_pool);
const int end_column = read_pointer_.column + read_cycles; int read_cycles_performed = 0;
LineBuffer &line_buffer = line_buffers_[read_pointer_.row]; uint32_t next_cram_value = 0;
while(read_cycles_performed < target_read_cycles) {
const uint32_t cram_value = next_cram_value;
next_cram_value = 0;
int read_cycles = target_read_cycles - read_cycles_performed;
if(!upcoming_cram_dots_.empty() && upcoming_cram_dots_.front().location.row == read_pointer_.row) {
int time_until_dot = upcoming_cram_dots_.front().location.column - read_pointer_.column;
if(time_until_dot < read_cycles) {
read_cycles = time_until_dot;
next_cram_value = upcoming_cram_dots_.front().value;
upcoming_cram_dots_.erase(upcoming_cram_dots_.begin());
}
}
if(!read_cycles) continue;
read_cycles_performed += read_cycles;
const int end_column = read_pointer_.column + read_cycles;
LineBuffer &line_buffer = line_buffers_[read_pointer_.row];
// TODO: actually perform these dots, at least in part by further subdividing // --------------------
// the period to run for. // Output video stream.
upcoming_cram_dots_.clear(); // --------------------
// --------------------
// Output video stream.
// --------------------
#define intersect(left, right, code) \ #define intersect(left, right, code) \
{ \ { \
@ -344,21 +358,37 @@ void TMS9918::run_for(const HalfCycles cycles) {
}\ }\
} }
#define border(left, right) intersect(left, right, output_border(end - start)) #define border(left, right) intersect(left, right, output_border(end - start, cram_value))
if(line_buffer.line_mode == LineMode::Refresh || read_pointer_.row > mode_timing_.pixel_lines) { if(line_buffer.line_mode == LineMode::Refresh || read_pointer_.row > mode_timing_.pixel_lines) {
if(read_pointer_.row >= mode_timing_.first_vsync_line && read_pointer_.row < mode_timing_.first_vsync_line+4) { if(read_pointer_.row >= mode_timing_.first_vsync_line && read_pointer_.row < mode_timing_.first_vsync_line+4) {
// Vertical sync. // Vertical sync.
if(end_column == 342) { if(end_column == 342) {
crt_->output_sync(342 * 4); crt_->output_sync(342 * 4);
}
} else {
// Right border.
border(0, 15);
// Blanking region; total length is 58 cycles,
// and 58+15 = 73. So output the lot when the
// cursor passes 73.
if(read_pointer_.column < 73 && end_column >= 73) {
crt_->output_blank(8*4);
crt_->output_sync(26*4);
crt_->output_blank(2*4);
crt_->output_default_colour_burst(14*4);
crt_->output_blank(8*4);
}
// Border colour for the rest of the line.
border(73, 342);
} }
} else { } else {
// Right border. // Right border.
border(0, 15); border(0, 15);
// Blanking region; total length is 58 cycles, // Blanking region.
// and 58+15 = 73. So output the lot when the
// cursor passes 73.
if(read_pointer_.column < 73 && end_column >= 73) { if(read_pointer_.column < 73 && end_column >= 73) {
crt_->output_blank(8*4); crt_->output_blank(8*4);
crt_->output_sync(26*4); crt_->output_sync(26*4);
@ -367,73 +397,58 @@ void TMS9918::run_for(const HalfCycles cycles) {
crt_->output_blank(8*4); crt_->output_blank(8*4);
} }
// Border colour for the rest of the line. // Left border.
border(73, 342); border(73, line_buffer.first_pixel_output_column);
}
} else {
// Right border.
border(0, 15);
// Blanking region. // Pixel region.
if(read_pointer_.column < 73 && end_column >= 73) { intersect(
crt_->output_blank(8*4); line_buffer.first_pixel_output_column,
crt_->output_sync(26*4); line_buffer.next_border_column,
crt_->output_blank(2*4); if(!asked_for_write_area_) {
crt_->output_default_colour_burst(14*4); asked_for_write_area_ = true;
crt_->output_blank(8*4); pixel_origin_ = pixel_target_ = reinterpret_cast<uint32_t *>(
} crt_->allocate_write_area(static_cast<unsigned int>(line_buffer.next_border_column - line_buffer.first_pixel_output_column))
);
// Left border.
border(73, line_buffer.first_pixel_output_column);
// Pixel region.
intersect(
line_buffer.first_pixel_output_column,
line_buffer.next_border_column,
if(!asked_for_write_area_) {
asked_for_write_area_ = true;
pixel_origin_ = pixel_target_ = reinterpret_cast<uint32_t *>(
crt_->allocate_write_area(static_cast<unsigned int>(line_buffer.next_border_column - line_buffer.first_pixel_output_column))
);
}
if(pixel_target_) {
const int relative_start = start - line_buffer.first_pixel_output_column;
const int relative_end = end - line_buffer.first_pixel_output_column;
switch(line_buffer.line_mode) {
case LineMode::SMS: draw_sms(relative_start, relative_end, 0); break;
case LineMode::Character: draw_tms_character(relative_start, relative_end); break;
case LineMode::Text: draw_tms_text(relative_start, relative_end); break;
case LineMode::Refresh: break; /* Dealt with elsewhere. */
} }
}
if(end == line_buffer.next_border_column) { if(pixel_target_) {
const unsigned int length = static_cast<unsigned int>(line_buffer.next_border_column - line_buffer.first_pixel_output_column); const int relative_start = start - line_buffer.first_pixel_output_column;
crt_->output_data(length * 4, length); const int relative_end = end - line_buffer.first_pixel_output_column;
pixel_origin_ = pixel_target_ = nullptr; switch(line_buffer.line_mode) {
asked_for_write_area_ = false; case LineMode::SMS: draw_sms(relative_start, relative_end, cram_value); break;
} case LineMode::Character: draw_tms_character(relative_start, relative_end); break;
); case LineMode::Text: draw_tms_text(relative_start, relative_end); break;
// Additional right border, if called for. case LineMode::Refresh: break; /* Dealt with elsewhere. */
if(line_buffer.next_border_column != 342) { }
border(line_buffer.next_border_column, 342); }
if(end == line_buffer.next_border_column) {
const unsigned int length = static_cast<unsigned int>(line_buffer.next_border_column - line_buffer.first_pixel_output_column);
crt_->output_data(length * 4, length);
pixel_origin_ = pixel_target_ = nullptr;
asked_for_write_area_ = false;
}
);
// Additional right border, if called for.
if(line_buffer.next_border_column != 342) {
border(line_buffer.next_border_column, 342);
}
} }
}
#undef border #undef border
#undef intersect #undef intersect
// ------------- // -------------
// Advance time. // Advance time.
// ------------- // -------------
read_pointer_.column = end_column; read_pointer_.column = end_column;
read_cycles_pool -= read_cycles; }
read_cycles_pool -= target_read_cycles;
if(read_pointer_.column == 342) { if(read_pointer_.column == 342) {
read_pointer_.column = 0; read_pointer_.column = 0;
read_pointer_.row = (read_pointer_.row + 1) % mode_timing_.total_lines; read_pointer_.row = (read_pointer_.row + 1) % mode_timing_.total_lines;
@ -444,16 +459,25 @@ void TMS9918::run_for(const HalfCycles cycles) {
} }
} }
void Base::output_border(int cycles) { void Base::output_border(int cycles, uint32_t cram_dot) {
uint32_t *const pixel_target = reinterpret_cast<uint32_t *>(crt_->allocate_write_area(1)); cycles *= 4;
if(pixel_target) { uint32_t border_colour =
if(is_sega_vdp(personality_)) { is_sega_vdp(personality_) ?
*pixel_target = master_system_.colour_ram[16 + background_colour_]; master_system_.colour_ram[16 + background_colour_] :
} else { palette[background_colour_];
*pixel_target = palette[background_colour_];
} if(cram_dot) {
uint32_t *const pixel_target = reinterpret_cast<uint32_t *>(crt_->allocate_write_area(1));
*pixel_target = border_colour | cram_dot;
crt_->output_level(4);
cycles -= 4;
}
if(cycles) {
uint32_t *const pixel_target = reinterpret_cast<uint32_t *>(crt_->allocate_write_area(1));
*pixel_target = border_colour;
crt_->output_level(static_cast<unsigned int>(cycles));
} }
crt_->output_level(static_cast<unsigned int>(cycles) * 4);
} }
void TMS9918::set_register(int address, uint8_t value) { void TMS9918::set_register(int address, uint8_t value) {

View File

@ -127,7 +127,7 @@ class Base {
// A helper function to output the current border colour for // A helper function to output the current border colour for
// the number of cycles supplied. // the number of cycles supplied.
void output_border(int cycles); void output_border(int cycles, uint32_t cram_dot);
// A struct to contain timing information for the current mode. // A struct to contain timing information for the current mode.
struct { struct {
@ -343,9 +343,13 @@ class Base {
// Schedule a CRAM dot. // Schedule a CRAM dot.
upcoming_cram_dots_.emplace_back(); upcoming_cram_dots_.emplace_back();
CRAMDot &dot = upcoming_cram_dots_.back(); CRAMDot &dot = upcoming_cram_dots_.back();
dot.location.row = write_pointer_.row; dot.location.row = write_pointer_.row + (access_column / 342);
dot.location.column = access_column; dot.location.column = access_column % 342;
dot.value = master_system_.colour_ram[ram_pointer_ & 0x1f]; dot.value = master_system_.colour_ram[ram_pointer_ & 0x1f];
// TODO: the location should actually be slightly in the past, as
// output trails memory reading; expose the length of that gap
// somewhere that makes it visible to here and adjust.
} else { } else {
ram_[ram_pointer_ & 16383] = read_ahead_buffer_; ram_[ram_pointer_ & 16383] = read_ahead_buffer_;
} }