mirror of
https://github.com/TomHarte/CLK.git
synced 2024-10-04 17:56:29 +00:00
Edges to within millimetres of CRAM dots.
... but all the way up to bedtime.
This commit is contained in:
parent
9621ba59ae
commit
0587b9f257
@ -326,6 +326,10 @@ void TMS9918::run_for(const HalfCycles cycles) {
|
|||||||
LineBuffer &line_buffer = line_buffers_[read_pointer_.row];
|
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.
|
||||||
|
upcoming_cram_dots_.clear();
|
||||||
|
|
||||||
|
|
||||||
// --------------------
|
// --------------------
|
||||||
// Output video stream.
|
// Output video stream.
|
||||||
@ -340,6 +344,8 @@ void TMS9918::run_for(const HalfCycles cycles) {
|
|||||||
}\
|
}\
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#define border(left, right) intersect(left, right, output_border(end - start))
|
||||||
|
|
||||||
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.
|
||||||
@ -348,7 +354,7 @@ void TMS9918::run_for(const HalfCycles cycles) {
|
|||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// Right border.
|
// Right border.
|
||||||
intersect(0, 15, output_border(end - start));
|
border(0, 15);
|
||||||
|
|
||||||
// Blanking region; total length is 58 cycles,
|
// Blanking region; total length is 58 cycles,
|
||||||
// and 58+15 = 73. So output the lot when the
|
// and 58+15 = 73. So output the lot when the
|
||||||
@ -362,11 +368,11 @@ void TMS9918::run_for(const HalfCycles cycles) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Border colour for the rest of the line.
|
// Border colour for the rest of the line.
|
||||||
intersect(73, 342, output_border(end - start));
|
border(73, 342);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// Right border.
|
// Right border.
|
||||||
intersect(0, 15, output_border(end - start));
|
border(0, 15);
|
||||||
|
|
||||||
// Blanking region.
|
// Blanking region.
|
||||||
if(read_pointer_.column < 73 && end_column >= 73) {
|
if(read_pointer_.column < 73 && end_column >= 73) {
|
||||||
@ -378,7 +384,7 @@ void TMS9918::run_for(const HalfCycles cycles) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Left border.
|
// Left border.
|
||||||
intersect(73, line_buffer.first_pixel_output_column, output_border(end - start));
|
border(73, line_buffer.first_pixel_output_column);
|
||||||
|
|
||||||
// Pixel region.
|
// Pixel region.
|
||||||
intersect(
|
intersect(
|
||||||
@ -395,7 +401,7 @@ void TMS9918::run_for(const HalfCycles cycles) {
|
|||||||
const int relative_start = start - line_buffer.first_pixel_output_column;
|
const int relative_start = start - line_buffer.first_pixel_output_column;
|
||||||
const int relative_end = end - line_buffer.first_pixel_output_column;
|
const int relative_end = end - line_buffer.first_pixel_output_column;
|
||||||
switch(line_buffer.line_mode) {
|
switch(line_buffer.line_mode) {
|
||||||
case LineMode::SMS: draw_sms(relative_start, relative_end); break;
|
case LineMode::SMS: draw_sms(relative_start, relative_end, 0); break;
|
||||||
case LineMode::Character: draw_tms_character(relative_start, relative_end); 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::Text: draw_tms_text(relative_start, relative_end); break;
|
||||||
|
|
||||||
@ -413,11 +419,12 @@ void TMS9918::run_for(const HalfCycles cycles) {
|
|||||||
|
|
||||||
// Additional right border, if called for.
|
// Additional right border, if called for.
|
||||||
if(line_buffer.next_border_column != 342) {
|
if(line_buffer.next_border_column != 342) {
|
||||||
intersect(line_buffer.next_border_column, 342, output_border(end - start));
|
border(line_buffer.next_border_column, 342);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#undef intersect
|
#undef border
|
||||||
|
#undef intersect
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@ -841,7 +848,7 @@ void Base::draw_tms_text(int start, int end) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void Base::draw_sms(int start, int end) {
|
void Base::draw_sms(int start, int end, uint32_t cram_dot) {
|
||||||
LineBuffer &line_buffer = line_buffers_[read_pointer_.row];
|
LineBuffer &line_buffer = line_buffers_[read_pointer_.row];
|
||||||
int colour_buffer[256];
|
int colour_buffer[256];
|
||||||
|
|
||||||
@ -974,8 +981,9 @@ void Base::draw_sms(int start, int end) {
|
|||||||
status_ |= StatusSpriteCollision;
|
status_ |= StatusSpriteCollision;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Map from the 32-colour buffer to real output pixels.
|
// Map from the 32-colour buffer to real output pixels, applying the specific CRAM dot if any.
|
||||||
for(int c = start; c < end; ++c) {
|
pixel_target_[start] = master_system_.colour_ram[colour_buffer[start] & 0x1f] | cram_dot;
|
||||||
|
for(int c = start+1; c < end; ++c) {
|
||||||
pixel_target_[c] = master_system_.colour_ram[colour_buffer[c] & 0x1f];
|
pixel_target_[c] = master_system_.colour_ram[colour_buffer[c] & 0x1f];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -253,6 +253,16 @@ class Base {
|
|||||||
int row, column;
|
int row, column;
|
||||||
} read_pointer_, write_pointer_;
|
} read_pointer_, write_pointer_;
|
||||||
|
|
||||||
|
// The SMS VDP has a programmer-set colour palette, with a dedicated patch of RAM. But the RAM is only exactly
|
||||||
|
// fast enough for the pixel clock. So when the programmer writes to it, that causes a one-pixel glitch; there
|
||||||
|
// isn't the bandwidth for the read both write to occur simultaneously. The following buffer therefore keeps
|
||||||
|
// track of pending collisions, for visual reproduction.
|
||||||
|
struct CRAMDot {
|
||||||
|
LineBufferPointer location;
|
||||||
|
uint32_t value;
|
||||||
|
};
|
||||||
|
std::vector<CRAMDot> upcoming_cram_dots_;
|
||||||
|
|
||||||
// Extra information that affects the Master System output mode.
|
// Extra information that affects the Master System output mode.
|
||||||
struct {
|
struct {
|
||||||
// Programmer-set flags.
|
// Programmer-set flags.
|
||||||
@ -314,17 +324,27 @@ class Base {
|
|||||||
screen_mode_ = ScreenMode::Blank;
|
screen_mode_ = ScreenMode::Blank;
|
||||||
}
|
}
|
||||||
|
|
||||||
void do_external_slot() {
|
void do_external_slot(int access_column) {
|
||||||
|
// TODO: is queued access ready yet?
|
||||||
|
|
||||||
switch(queued_access_) {
|
switch(queued_access_) {
|
||||||
default: return;
|
default: return;
|
||||||
|
|
||||||
case MemoryAccess::Write:
|
case MemoryAccess::Write:
|
||||||
if(master_system_.cram_is_selected) {
|
if(master_system_.cram_is_selected) {
|
||||||
|
// Adjust the palette.
|
||||||
master_system_.colour_ram[ram_pointer_ & 0x1f] = palette_pack(
|
master_system_.colour_ram[ram_pointer_ & 0x1f] = palette_pack(
|
||||||
static_cast<uint8_t>(((read_ahead_buffer_ >> 0) & 3) * 255 / 3),
|
static_cast<uint8_t>(((read_ahead_buffer_ >> 0) & 3) * 255 / 3),
|
||||||
static_cast<uint8_t>(((read_ahead_buffer_ >> 2) & 3) * 255 / 3),
|
static_cast<uint8_t>(((read_ahead_buffer_ >> 2) & 3) * 255 / 3),
|
||||||
static_cast<uint8_t>(((read_ahead_buffer_ >> 4) & 3) * 255 / 3)
|
static_cast<uint8_t>(((read_ahead_buffer_ >> 4) & 3) * 255 / 3)
|
||||||
);
|
);
|
||||||
|
|
||||||
|
// Schedule a CRAM dot.
|
||||||
|
upcoming_cram_dots_.emplace_back();
|
||||||
|
auto dot = upcoming_cram_dots_.back();
|
||||||
|
dot.location.row = write_pointer_.row;
|
||||||
|
dot.location.column = access_column;
|
||||||
|
dot.value = master_system_.colour_ram[ram_pointer_ & 0x1f];
|
||||||
} else {
|
} else {
|
||||||
ram_[ram_pointer_ & 16383] = read_ahead_buffer_;
|
ram_[ram_pointer_ & 16383] = read_ahead_buffer_;
|
||||||
}
|
}
|
||||||
@ -374,7 +394,7 @@ class Base {
|
|||||||
case n
|
case n
|
||||||
|
|
||||||
#define external_slot(n) \
|
#define external_slot(n) \
|
||||||
slot(n): do_external_slot();
|
slot(n): do_external_slot((n)*2);
|
||||||
|
|
||||||
#define external_slots_2(n) \
|
#define external_slots_2(n) \
|
||||||
external_slot(n); \
|
external_slot(n); \
|
||||||
@ -606,7 +626,7 @@ class Base {
|
|||||||
|
|
||||||
slot(31):
|
slot(31):
|
||||||
sprite_selection_buffer.reset_sprite_collection();
|
sprite_selection_buffer.reset_sprite_collection();
|
||||||
do_external_slot();
|
do_external_slot(31*2);
|
||||||
external_slots_2(32);
|
external_slots_2(32);
|
||||||
external_slot(34);
|
external_slot(34);
|
||||||
|
|
||||||
@ -758,7 +778,7 @@ class Base {
|
|||||||
|
|
||||||
slot(29):
|
slot(29):
|
||||||
sprite_selection_buffer.reset_sprite_collection();
|
sprite_selection_buffer.reset_sprite_collection();
|
||||||
do_external_slot();
|
do_external_slot(29*2);
|
||||||
external_slot(30);
|
external_slot(30);
|
||||||
|
|
||||||
sprite_y_read(31, 0);
|
sprite_y_read(31, 0);
|
||||||
@ -799,7 +819,7 @@ class Base {
|
|||||||
bool asked_for_write_area_ = false;
|
bool asked_for_write_area_ = false;
|
||||||
void draw_tms_character(int start, int end);
|
void draw_tms_character(int start, int end);
|
||||||
void draw_tms_text(int start, int end);
|
void draw_tms_text(int start, int end);
|
||||||
void draw_sms(int start, int end);
|
void draw_sms(int start, int end, uint32_t cram_dot);
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user