1
0
mirror of https://github.com/TomHarte/CLK.git synced 2024-09-27 18:55:48 +00:00

Merge pull request #1126 from TomHarte/NewLineLayout

Definitively switch 9918 to counting cycle 0 as start-of-sync.
This commit is contained in:
Thomas Harte 2023-04-25 11:26:28 -04:00 committed by GitHub
commit 8cd38094fc
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 62 additions and 95 deletions

View File

@ -31,6 +31,8 @@ Base<personality>::Base() :
// into whether there's a more natural form. It feels unlikely given the diversity of chips modelled.
if constexpr (is_sega_vdp(personality)) {
// TODO: all these relate to the old line timing; resource and review.
mode_timing_.line_interrupt_position = 64;
mode_timing_.end_of_frame_interrupt_position.column = 63;
@ -39,13 +41,13 @@ Base<personality>::Base() :
if constexpr (is_yamaha_vdp(personality)) {
// TODO: start of sync, or end of sync?
mode_timing_.line_interrupt_position = Timing<personality>::StartOfSync;
mode_timing_.line_interrupt_position = 0;//Timing<personality>::StartOfSync;
}
// Establish that output is delayed after reading by `output_lag` cycles,
// i.e. the fetch pointer is currently _ahead_ of the output pointer.
//
// Start at a random position.
// TODO: Start at a random position.
output_pointer_.row = output_pointer_.column = 0;
// output_pointer_.row = rand() % 262;
// output_pointer_.column = rand() % (Timing<personality>::CyclesPerLine - output_lag);
@ -367,8 +369,8 @@ void TMS9918<personality>::run_for(const HalfCycles cycles) {
}
// Based on the output mode, pick a line mode.
this->fetch_line_buffer_->first_pixel_output_column = Timing<personality>::FirstPixelCycle;
this->fetch_line_buffer_->next_border_column = Timing<personality>::CyclesPerLine;
this->fetch_line_buffer_->first_pixel_output_column = LineLayout<personality>::EndOfLeftBorder;
this->fetch_line_buffer_->next_border_column = LineLayout<personality>::EndOfPixels;
this->fetch_line_buffer_->pixel_count = 256;
this->fetch_line_buffer_->screen_mode = this->screen_mode_;
this->mode_timing_.maximum_visible_sprites = 4;
@ -379,14 +381,14 @@ void TMS9918<personality>::run_for(const HalfCycles cycles) {
} else {
this->fetch_line_buffer_->fetch_mode = FetchMode::Text;
}
this->fetch_line_buffer_->first_pixel_output_column = Timing<personality>::FirstTextCycle;
this->fetch_line_buffer_->next_border_column = Timing<personality>::LastTextCycle;
this->fetch_line_buffer_->first_pixel_output_column = LineLayout<personality>::TextModeEndOfLeftBorder;
this->fetch_line_buffer_->next_border_column = LineLayout<personality>::TextModeEndOfPixels;
this->fetch_line_buffer_->pixel_count = 240;
break;
case ScreenMode::YamahaText80:
this->fetch_line_buffer_->fetch_mode = FetchMode::Yamaha;
this->fetch_line_buffer_->first_pixel_output_column = Timing<personality>::FirstTextCycle;
this->fetch_line_buffer_->next_border_column = Timing<personality>::LastTextCycle;
this->fetch_line_buffer_->first_pixel_output_column = LineLayout<personality>::TextModeEndOfLeftBorder;
this->fetch_line_buffer_->next_border_column = LineLayout<personality>::TextModeEndOfPixels;
this->fetch_line_buffer_->pixel_count = 480;
break;
@ -417,6 +419,11 @@ void TMS9918<personality>::run_for(const HalfCycles cycles) {
break;
}
if constexpr (is_yamaha_vdp(personality)) {
this->fetch_line_buffer_->first_pixel_output_column += Storage<personality>::adjustment_[0];
this->fetch_line_buffer_->next_border_column += Storage<personality>::adjustment_[0];
}
this->fetch_line_buffer_->vertical_state =
this->screen_mode_ == ScreenMode::Blank ?
VerticalState::Blank :
@ -497,6 +504,26 @@ void TMS9918<personality>::run_for(const HalfCycles cycles) {
#define border(left, right) intersect(left, right, this->output_border(end - start, cram_value))
const auto left_blank = [&]() {
// Blanking region: output the entire sequence when the cursor
// crosses the start-of-border point.
if(
this->output_pointer_.column < LineLayout<personality>::EndOfLeftErase &&
end_column >= LineLayout<personality>::EndOfLeftErase
) {
output_sync(LineLayout<personality>::EndOfSync);
output_blank(LineLayout<personality>::StartOfColourBurst - LineLayout<personality>::EndOfSync);
output_default_colour_burst(LineLayout<personality>::EndOfColourBurst - LineLayout<personality>::StartOfColourBurst);
output_blank(LineLayout<personality>::EndOfLeftErase - LineLayout<personality>::EndOfColourBurst);
}
};
const auto right_blank = [&]() {
if(end_column == Timing<personality>::CyclesPerLine) {
output_blank(Timing<personality>::CyclesPerLine - LineLayout<personality>::EndOfRightBorder);
}
};
if(this->draw_line_buffer_->vertical_state != VerticalState::Pixels) {
if(
this->output_pointer_.row >= this->mode_timing_.first_vsync_line &&
@ -508,43 +535,15 @@ void TMS9918<personality>::run_for(const HalfCycles cycles) {
output_sync(Timing<personality>::CyclesPerLine);
}
} else {
// Right border.
border(0, Timing<personality>::EndOfRightBorder);
// Blanking region: output the entire sequence when the cursor
// crosses the start-of-border point.
if(
this->output_pointer_.column < Timing<personality>::StartOfLeftBorder &&
end_column >= Timing<personality>::StartOfLeftBorder
) {
output_blank(Timing<personality>::StartOfSync - Timing<personality>::EndOfRightBorder);
output_sync(Timing<personality>::EndOfSync - Timing<personality>::StartOfSync);
output_blank(Timing<personality>::StartOfColourBurst - Timing<personality>::EndOfSync);
output_default_colour_burst(Timing<personality>::EndOfColourBurst - Timing<personality>::StartOfColourBurst);
output_blank(Timing<personality>::StartOfLeftBorder - Timing<personality>::EndOfColourBurst);
}
// Border colour for the rest of the line.
border(Timing<personality>::StartOfLeftBorder, Timing<personality>::CyclesPerLine);
left_blank();
border(LineLayout<personality>::EndOfLeftErase, LineLayout<personality>::EndOfRightBorder);
right_blank();
}
} else {
// Right border.
border(0, Timing<personality>::EndOfRightBorder);
// Blanking region.
if(
this->output_pointer_.column < Timing<personality>::StartOfLeftBorder &&
end_column >= Timing<personality>::StartOfLeftBorder
) {
output_blank(Timing<personality>::StartOfSync - Timing<personality>::EndOfRightBorder);
output_sync(Timing<personality>::EndOfSync - Timing<personality>::StartOfSync);
output_blank(Timing<personality>::StartOfColourBurst - Timing<personality>::EndOfSync);
output_default_colour_burst(Timing<personality>::EndOfColourBurst - Timing<personality>::StartOfColourBurst);
output_blank(Timing<personality>::StartOfLeftBorder - Timing<personality>::EndOfColourBurst);
}
left_blank();
// Left border.
border(Timing<personality>::StartOfLeftBorder, this->draw_line_buffer_->first_pixel_output_column);
border(LineLayout<personality>::EndOfLeftErase, this->draw_line_buffer_->first_pixel_output_column);
#define draw(function, clock) { \
const int relative_start = from_internal<personality, clock>(start - this->draw_line_buffer_->first_pixel_output_column); \
@ -588,10 +587,10 @@ void TMS9918<personality>::run_for(const HalfCycles cycles) {
#undef draw
// Additional right border, if called for.
if(this->draw_line_buffer_->next_border_column != Timing<personality>::CyclesPerLine) {
border(this->draw_line_buffer_->next_border_column, Timing<personality>::CyclesPerLine);
}
// Right border.
border(this->draw_line_buffer_->next_border_column, LineLayout<personality>::EndOfRightBorder);
right_blank();
}
#undef border
@ -880,9 +879,8 @@ void Base<personality>::commit_register(int reg, uint8_t value) {
break;
case 18:
if(value) {
LOG("TODO: Yamaha position adjustment; " << PADHEX(2) << +value);
}
Storage<personality>::adjustment_[0] = (8 - ((value & 15) ^ 8)) * 4;
Storage<personality>::adjustment_[1] = 8 - ((value >> 4) ^ 8);
// b0-b3: horizontal adjustment
// b4-b7: vertical adjustment
break;
@ -1203,7 +1201,7 @@ VerticalState Base<personality>::vertical_state() const {
template <Personality personality>
bool Base<personality>::is_horizontal_blank() const {
return fetch_pointer_.column < StandardTiming<personality>::FirstPixelCycle;
return fetch_pointer_.column < LineLayout<personality>::EndOfLeftErase || fetch_pointer_.column >= LineLayout<personality>::EndOfRightBorder;
}
template <Personality personality>

View File

@ -60,35 +60,6 @@ template <Personality personality> struct StandardTiming {
/// The number of internal cycles that must elapse between a request to read or write and
/// it becoming a candidate for action.
constexpr static int VRAMAccessDelay = 6;
/// The first internal cycle at which pixels will be output in any mode other than text.
/// Pixels implicitly run from here to the end of the line.
constexpr static int FirstPixelCycle = 86 * CyclesPerLine / 342;
/// The first internal cycle at which pixels will be output text mode.
constexpr static int FirstTextCycle = 94 * CyclesPerLine / 342;
/// The final internal cycle at which pixels will be output text mode.
constexpr static int LastTextCycle = 334 * CyclesPerLine / 342;
// For the below, the fixed portion of line layout is:
//
// [0, EndOfRightBorder): right border colour
// [EndOfRightBorder, StartOfSync): blank
// [StartOfSync, EndOfSync): sync
// [EndOfSync, StartOfColourBurst): blank
// [StartOfColourBurst, EndOfColourBurst): the colour burst
// [EndOfColourBurst, StartOfLeftBorder): blank
//
// The region from StartOfLeftBorder until the end is then filled with
// some combination of pixels and more border, depending on the vertical
// position of this line and the current screen mode.
constexpr static int EndOfRightBorder = 15 * CyclesPerLine / 342;
constexpr static int StartOfSync = 23 * CyclesPerLine / 342;
constexpr static int EndOfSync = 49 * CyclesPerLine / 342;
constexpr static int StartOfColourBurst = 51 * CyclesPerLine / 342;
constexpr static int EndOfColourBurst = 65 * CyclesPerLine / 342;
constexpr static int StartOfLeftBorder = 73 * CyclesPerLine / 342;
};
/// Provides concrete, specific timing for the nominated personality.
@ -191,8 +162,9 @@ template <Personality personality, typename Enable = void> struct LineLayout;
// [EndOfRightBorder, <end of line>] blank
//
// ... with minor caveats:
// * horizontal adjust on the Yamaha VDPs is applied to EndOfLeftBorder and EndOfPixels; and
// * the Sega VDPs may programatically extend the left border.
// * horizontal adjust on the Yamaha VDPs is applied to EndOfLeftBorder and EndOfPixels;
// * the Sega VDPs may programatically extend the left border; and
// * text mode on all VDPs adjusts border width.
template <Personality personality> struct LineLayout<personality, std::enable_if_t<is_classic_vdp(personality)>> {
constexpr static int EndOfSync = 26;
@ -202,6 +174,9 @@ template <Personality personality> struct LineLayout<personality, std::enable_if
constexpr static int EndOfLeftBorder = 63;
constexpr static int EndOfPixels = 319;
constexpr static int EndOfRightBorder = 334;
constexpr static int TextModeEndOfLeftBorder = 69;
constexpr static int TextModeEndOfPixels = 309;
};
template <Personality personality> struct LineLayout<personality, std::enable_if_t<is_yamaha_vdp(personality)>> {
@ -212,6 +187,9 @@ template <Personality personality> struct LineLayout<personality, std::enable_if
constexpr static int EndOfLeftBorder = 258;
constexpr static int EndOfPixels = 1282;
constexpr static int EndOfRightBorder = 1341;
constexpr static int TextModeEndOfLeftBorder = 294;
constexpr static int TextModeEndOfPixels = 1254;
};
}

View File

@ -71,8 +71,7 @@ template<bool use_end, typename SequencerT> void Base<personality>::dispatch(Seq
#define index(n) \
if(use_end && end == n) return; \
[[fallthrough]]; \
case n: fetcher.template fetch<(n + 171 - 16) % 171>(n);
// `template fetch` call includes an in-place internal -> sync-aligned conversion for now, during transition.
case n: fetcher.template fetch<n>(n);
switch(start) {
default: assert(false);

View File

@ -38,6 +38,8 @@ template <Personality personality> struct Storage<personality, std::enable_if_t<
int indirect_register_ = 0;
bool increment_indirect_register_ = false;
int adjustment_[2]{};
std::array<uint32_t, 16> palette_{};
std::array<uint32_t, 16> background_palette_{};
bool solid_background_ = true;
@ -216,20 +218,10 @@ template <Personality personality> struct Storage<personality, std::enable_if_t<
}
private:
// This emulator treats position 0 as being immediately after the standard pixel area.
// i.e. offset 1282 on Grauw's http://map.grauw.nl/articles/vdp-vram-timing/vdp-timing.png
static constexpr int ZeroAsGrauwIndex = 1282;
constexpr static int grauw_to_internal(int offset) {
return (offset + 1368 - ZeroAsGrauwIndex) % 1368;
}
constexpr static int internal_to_grauw(int offset) {
return (offset + ZeroAsGrauwIndex) % 1368;
}
template <typename GeneratorT> static constexpr size_t events_size() {
size_t size = 0;
for(int c = 0; c < 1368; c++) {
const auto event_type = GeneratorT::event(internal_to_grauw(c));
const auto event_type = GeneratorT::event(c);
size += event_type.has_value();
}
return size + 1;
@ -240,7 +232,7 @@ template <Personality personality> struct Storage<personality, std::enable_if_t<
std::array<Event, size> result{};
size_t index = 0;
for(int c = 0; c < 1368; c++) {
const auto event = GeneratorT::event(internal_to_grauw(c));
const auto event = GeneratorT::event(c);
if(!event) {
continue;
}

View File

@ -1,7 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
LastUpgradeVersion = "1400"
version = "1.8">
version = "1.3">
<BuildAction
parallelizeBuildables = "YES"
buildImplicitDependencies = "YES">