mirror of
https://github.com/TomHarte/CLK.git
synced 2025-01-27 06:35:04 +00:00
Mostly restores Atari 2600 output. PAL colours need work.
This commit is contained in:
parent
a25470ee41
commit
6be46ae921
@ -122,10 +122,6 @@ class ConcreteMachine:
|
||||
joysticks_.emplace_back(new Joystick(bus_.get(), 4, 1));
|
||||
}
|
||||
|
||||
~ConcreteMachine() {
|
||||
// close_output();
|
||||
}
|
||||
|
||||
std::vector<std::unique_ptr<Inputs::Joystick>> &get_joysticks() override {
|
||||
return joysticks_;
|
||||
}
|
||||
@ -158,19 +154,11 @@ class ConcreteMachine:
|
||||
|
||||
// to satisfy CRTMachine::Machine
|
||||
void set_scan_target(Outputs::Display::ScanTarget *scan_target) override {
|
||||
bus_->tia_.reset(new TIA);
|
||||
bus_->speaker_.set_input_rate(static_cast<float>(get_clock_rate() / static_cast<double>(CPUTicksPerAudioTick)));
|
||||
bus_->tia_->get_crt()->set_delegate(this);
|
||||
bus_->tia_.set_crt_delegate(this);
|
||||
bus_->tia_.set_scan_target(scan_target);
|
||||
}
|
||||
|
||||
// void close_output() override {
|
||||
// bus_.reset();
|
||||
// }
|
||||
//
|
||||
// Outputs::CRT::CRT *get_crt() override {
|
||||
// return bus_->tia_->get_crt();
|
||||
// }
|
||||
|
||||
Outputs::Speaker::Speaker *get_speaker() override {
|
||||
return &bus_->speaker_;
|
||||
}
|
||||
@ -205,10 +193,10 @@ class ConcreteMachine:
|
||||
double clock_rate;
|
||||
if(is_ntsc_) {
|
||||
clock_rate = NTSC_clock_rate;
|
||||
bus_->tia_->set_output_mode(TIA::OutputMode::NTSC);
|
||||
bus_->tia_.set_output_mode(TIA::OutputMode::NTSC);
|
||||
} else {
|
||||
clock_rate = PAL_clock_rate;
|
||||
bus_->tia_->set_output_mode(TIA::OutputMode::PAL);
|
||||
bus_->tia_.set_output_mode(TIA::OutputMode::PAL);
|
||||
}
|
||||
|
||||
bus_->speaker_.set_input_rate(static_cast<float>(clock_rate / static_cast<double>(CPUTicksPerAudioTick)));
|
||||
|
@ -36,7 +36,7 @@ class Bus {
|
||||
|
||||
// the RIOT, TIA and speaker
|
||||
PIA mos6532_;
|
||||
std::shared_ptr<TIA> tia_;
|
||||
TIA tia_;
|
||||
|
||||
Concurrency::DeferringAsyncTaskQueue audio_queue_;
|
||||
TIASound tia_sound_;
|
||||
@ -55,7 +55,7 @@ class Bus {
|
||||
// video backlog accumulation counter
|
||||
Cycles cycles_since_video_update_;
|
||||
inline void update_video() {
|
||||
tia_->run_for(cycles_since_video_update_.flush());
|
||||
tia_.run_for(cycles_since_video_update_.flush());
|
||||
}
|
||||
|
||||
// RIOT backlog accumulation counter
|
||||
|
@ -68,7 +68,7 @@ template<class T> class Cartridge:
|
||||
// effect until the next read; therefore it isn't safe to assume that signalling ready immediately
|
||||
// skips to the end of the line.
|
||||
if(operation == CPU::MOS6502::BusOperation::Ready)
|
||||
cycles_run_for = tia_->get_cycles_until_horizontal_blank(cycles_since_video_update_);
|
||||
cycles_run_for = tia_.get_cycles_until_horizontal_blank(cycles_since_video_update_);
|
||||
|
||||
cycles_since_speaker_update_ += Cycles(cycles_run_for);
|
||||
cycles_since_video_update_ += Cycles(cycles_run_for);
|
||||
@ -101,7 +101,7 @@ template<class T> class Cartridge:
|
||||
case 0x05: // missile 1 / playfield / ball collisions
|
||||
case 0x06: // ball / playfield collisions
|
||||
case 0x07: // player / player, missile / missile collisions
|
||||
returnValue &= tia_->get_collision_flags(decodedAddress);
|
||||
returnValue &= tia_.get_collision_flags(decodedAddress);
|
||||
break;
|
||||
|
||||
case 0x08:
|
||||
@ -120,52 +120,52 @@ template<class T> class Cartridge:
|
||||
} else {
|
||||
const uint16_t decodedAddress = address & 0x3f;
|
||||
switch(decodedAddress) {
|
||||
case 0x00: update_video(); tia_->set_sync(*value & 0x02); break;
|
||||
case 0x01: update_video(); tia_->set_blank(*value & 0x02); break;
|
||||
case 0x00: update_video(); tia_.set_sync(*value & 0x02); break;
|
||||
case 0x01: update_video(); tia_.set_blank(*value & 0x02); break;
|
||||
|
||||
case 0x02: m6502_.set_ready_line(true); break;
|
||||
case 0x03:
|
||||
update_video();
|
||||
tia_->reset_horizontal_counter();
|
||||
tia_.reset_horizontal_counter();
|
||||
horizontal_counter_resets_++;
|
||||
break;
|
||||
// TODO: audio will now be out of synchronisation. Fix.
|
||||
|
||||
case 0x04:
|
||||
case 0x05: update_video(); tia_->set_player_number_and_size(decodedAddress - 0x04, *value); break;
|
||||
case 0x05: update_video(); tia_.set_player_number_and_size(decodedAddress - 0x04, *value); break;
|
||||
case 0x06:
|
||||
case 0x07: update_video(); tia_->set_player_missile_colour(decodedAddress - 0x06, *value); break;
|
||||
case 0x08: update_video(); tia_->set_playfield_ball_colour(*value); break;
|
||||
case 0x09: update_video(); tia_->set_background_colour(*value); break;
|
||||
case 0x0a: update_video(); tia_->set_playfield_control_and_ball_size(*value); break;
|
||||
case 0x07: update_video(); tia_.set_player_missile_colour(decodedAddress - 0x06, *value); break;
|
||||
case 0x08: update_video(); tia_.set_playfield_ball_colour(*value); break;
|
||||
case 0x09: update_video(); tia_.set_background_colour(*value); break;
|
||||
case 0x0a: update_video(); tia_.set_playfield_control_and_ball_size(*value); break;
|
||||
case 0x0b:
|
||||
case 0x0c: update_video(); tia_->set_player_reflected(decodedAddress - 0x0b, !((*value)&8)); break;
|
||||
case 0x0c: update_video(); tia_.set_player_reflected(decodedAddress - 0x0b, !((*value)&8)); break;
|
||||
case 0x0d:
|
||||
case 0x0e:
|
||||
case 0x0f: update_video(); tia_->set_playfield(decodedAddress - 0x0d, *value); break;
|
||||
case 0x0f: update_video(); tia_.set_playfield(decodedAddress - 0x0d, *value); break;
|
||||
case 0x10:
|
||||
case 0x11: update_video(); tia_->set_player_position(decodedAddress - 0x10); break;
|
||||
case 0x11: update_video(); tia_.set_player_position(decodedAddress - 0x10); break;
|
||||
case 0x12:
|
||||
case 0x13: update_video(); tia_->set_missile_position(decodedAddress - 0x12); break;
|
||||
case 0x14: update_video(); tia_->set_ball_position(); break;
|
||||
case 0x13: update_video(); tia_.set_missile_position(decodedAddress - 0x12); break;
|
||||
case 0x14: update_video(); tia_.set_ball_position(); break;
|
||||
case 0x1b:
|
||||
case 0x1c: update_video(); tia_->set_player_graphic(decodedAddress - 0x1b, *value); break;
|
||||
case 0x1c: update_video(); tia_.set_player_graphic(decodedAddress - 0x1b, *value); break;
|
||||
case 0x1d:
|
||||
case 0x1e: update_video(); tia_->set_missile_enable(decodedAddress - 0x1d, (*value)&2); break;
|
||||
case 0x1f: update_video(); tia_->set_ball_enable((*value)&2); break;
|
||||
case 0x1e: update_video(); tia_.set_missile_enable(decodedAddress - 0x1d, (*value)&2); break;
|
||||
case 0x1f: update_video(); tia_.set_ball_enable((*value)&2); break;
|
||||
case 0x20:
|
||||
case 0x21: update_video(); tia_->set_player_motion(decodedAddress - 0x20, *value); break;
|
||||
case 0x21: update_video(); tia_.set_player_motion(decodedAddress - 0x20, *value); break;
|
||||
case 0x22:
|
||||
case 0x23: update_video(); tia_->set_missile_motion(decodedAddress - 0x22, *value); break;
|
||||
case 0x24: update_video(); tia_->set_ball_motion(*value); break;
|
||||
case 0x23: update_video(); tia_.set_missile_motion(decodedAddress - 0x22, *value); break;
|
||||
case 0x24: update_video(); tia_.set_ball_motion(*value); break;
|
||||
case 0x25:
|
||||
case 0x26: tia_->set_player_delay(decodedAddress - 0x25, (*value)&1); break;
|
||||
case 0x27: tia_->set_ball_delay((*value)&1); break;
|
||||
case 0x26: tia_.set_player_delay(decodedAddress - 0x25, (*value)&1); break;
|
||||
case 0x27: tia_.set_ball_delay((*value)&1); break;
|
||||
case 0x28:
|
||||
case 0x29: update_video(); tia_->set_missile_position_to_player(decodedAddress - 0x28, (*value)&2); break;
|
||||
case 0x2a: update_video(); tia_->move(); break;
|
||||
case 0x2b: update_video(); tia_->clear_motion(); break;
|
||||
case 0x2c: update_video(); tia_->clear_collision_flags(); break;
|
||||
case 0x29: update_video(); tia_.set_missile_position_to_player(decodedAddress - 0x28, (*value)&2); break;
|
||||
case 0x2a: update_video(); tia_.move(); break;
|
||||
case 0x2b: update_video(); tia_.clear_motion(); break;
|
||||
case 0x2c: update_video(); tia_.clear_collision_flags(); break;
|
||||
|
||||
case 0x15:
|
||||
case 0x16: update_audio(); tia_sound_.set_control(decodedAddress - 0x15, *value); break;
|
||||
@ -192,7 +192,7 @@ template<class T> class Cartridge:
|
||||
}
|
||||
}
|
||||
|
||||
if(!tia_->get_cycles_until_horizontal_blank(cycles_since_video_update_)) m6502_.set_ready_line(false);
|
||||
if(!tia_.get_cycles_until_horizontal_blank(cycles_since_video_update_)) m6502_.set_ready_line(false);
|
||||
|
||||
return Cycles(cycles_run_for / 3);
|
||||
}
|
||||
|
@ -22,12 +22,10 @@ namespace {
|
||||
uint8_t reverse_table[256];
|
||||
}
|
||||
|
||||
TIA::TIA(bool create_crt) {
|
||||
if(create_crt) {
|
||||
// crt_.reset(new Outputs::CRT::CRT(cycles_per_line * 2 - 1, 1, Outputs::Display::Type::NTSC60, 1));
|
||||
// crt_->set_video_signal(Outputs::Display::VideoSignal::Composite);
|
||||
set_output_mode(OutputMode::NTSC);
|
||||
}
|
||||
TIA::TIA():
|
||||
crt_(cycles_per_line * 2 - 1, 1, Outputs::Display::Type::NTSC60, Outputs::Display::InputDataType::Luminance8Phase8) {
|
||||
|
||||
set_output_mode(OutputMode::NTSC);
|
||||
|
||||
for(int c = 0; c < 256; c++) {
|
||||
reverse_table[c] = static_cast<uint8_t>(
|
||||
@ -113,51 +111,35 @@ TIA::TIA(bool create_crt) {
|
||||
}
|
||||
}
|
||||
|
||||
TIA::TIA() : TIA(true) {}
|
||||
|
||||
TIA::TIA(std::function<void(uint8_t *output_buffer)> line_end_function) : TIA(false) {
|
||||
line_end_function_ = line_end_function;
|
||||
}
|
||||
|
||||
void TIA::set_output_mode(Atari2600::TIA::OutputMode output_mode) {
|
||||
Outputs::Display::Type display_type;
|
||||
tv_standard_ = output_mode;
|
||||
|
||||
if(output_mode == OutputMode::NTSC) {
|
||||
// crt_->set_svideo_sampling_function(
|
||||
// "vec2 svideo_sample(usampler2D texID, vec2 coordinate, float phase, float amplitude)"
|
||||
// "{"
|
||||
// "uint c = texture(texID, coordinate).r;"
|
||||
// "uint y = c & 14u;"
|
||||
// "uint iPhase = (c >> 4);"
|
||||
//
|
||||
// "float phaseOffset = 6.283185308 * float(iPhase) / 13.0 + 5.074880441076923;"
|
||||
// "return vec2(float(y) / 14.0, step(1, iPhase) * cos(phase - phaseOffset));"
|
||||
// "}");
|
||||
display_type = Outputs::Display::Type::NTSC60;
|
||||
} else {
|
||||
// crt_->set_svideo_sampling_function(
|
||||
// "vec2 svideo_sample(usampler2D texID, vec2 coordinate, float phase, float amplitude)"
|
||||
// "{"
|
||||
// "uint c = texture(texID, coordinate).r;"
|
||||
// "uint y = c & 14u;"
|
||||
// "uint iPhase = (c >> 4);"
|
||||
//
|
||||
// "uint direction = iPhase & 1u;"
|
||||
// "float phaseOffset = float(7u - direction) + (float(direction) - 0.5) * 2.0 * float(iPhase >> 1);"
|
||||
// "phaseOffset *= 6.283185308 / 12.0;"
|
||||
// "return vec2(float(y) / 14.0, step(4, (iPhase + 2u) & 15u) * cos(phase + phaseOffset));"
|
||||
// "}");
|
||||
display_type = Outputs::Display::Type::PAL50;
|
||||
}
|
||||
// crt_->set_video_signal(Outputs::Display::VideoSignal::Composite);
|
||||
crt_.set_display_type(Outputs::Display::DisplayType::CompositeColour);
|
||||
|
||||
// line number of cycles in a line of video is one less than twice the number of clock cycles per line; the Atari
|
||||
// outputs 228 colour cycles of material per line when an NTSC line 227.5. Since all clock numbers will be doubled
|
||||
// later, cycles_per_line * 2 - 1 is therefore the real length of an NTSC line, even though we're going to supply
|
||||
// cycles_per_line * 2 cycles of information from one sync edge to the next
|
||||
crt_->set_new_display_type(cycles_per_line * 2 - 1, display_type);
|
||||
crt_.set_new_display_type(cycles_per_line * 2 - 1, display_type);
|
||||
|
||||
/* speaker_->set_input_rate(static_cast<float>(get_clock_rate() / 38.0));*/
|
||||
// Update the luminance/phase mappings of the current palette.
|
||||
for(size_t c = 0; c < colour_palette_.size(); ++c) {
|
||||
set_colour_palette_entry(c, colour_palette_[c].original);
|
||||
}
|
||||
}
|
||||
|
||||
void TIA::set_crt_delegate(Outputs::CRT::Delegate *delegate) {
|
||||
crt_.set_delegate(delegate);
|
||||
}
|
||||
|
||||
void TIA::set_scan_target(Outputs::Display::ScanTarget *scan_target) {
|
||||
crt_.set_scan_target(scan_target);
|
||||
}
|
||||
|
||||
void TIA::run_for(const Cycles cycles) {
|
||||
@ -198,7 +180,53 @@ int TIA::get_cycles_until_horizontal_blank(const Cycles from_offset) {
|
||||
}
|
||||
|
||||
void TIA::set_background_colour(uint8_t colour) {
|
||||
colour_palette_[static_cast<int>(ColourIndex::Background)] = colour;
|
||||
set_colour_palette_entry(size_t(ColourIndex::Background), colour);
|
||||
}
|
||||
|
||||
void TIA::set_colour_palette_entry(size_t index, uint8_t colour) {
|
||||
const uint8_t luminance = ((colour & 14) * 255) / 14;
|
||||
|
||||
uint8_t phase = colour >> 4;
|
||||
|
||||
if(tv_standard_ == OutputMode::NTSC) {
|
||||
if(!phase) phase = 255;
|
||||
else {
|
||||
phase = -(phase * 127) / 13;
|
||||
phase -= 102;
|
||||
phase &= 127;
|
||||
}
|
||||
} else {
|
||||
// TODO: PAL colours.
|
||||
// crt_.set_svideo_sampling_function(
|
||||
// "vec2 svideo_sample(usampler2D texID, vec2 coordinate, float phase, float amplitude)"
|
||||
// "{"
|
||||
// "uint c = texture(texID, coordinate).r;"
|
||||
// "uint y = c & 14u;"
|
||||
// "uint iPhase = (c >> 4);"
|
||||
//
|
||||
// "uint direction = iPhase & 1u;"
|
||||
// "float phaseOffset = float(7u - direction) + (float(direction) - 0.5) * 2.0 * float(iPhase >> 1);"
|
||||
// "phaseOffset *= 6.283185308 / 12.0;"
|
||||
// "return vec2(float(y) / 14.0, step(4, (iPhase + 2u) & 15u) * cos(phase + phaseOffset));"
|
||||
// "}");
|
||||
if(phase < 2 || phase > 13) {
|
||||
phase = 255;
|
||||
} else {
|
||||
const auto direction = phase & 1;
|
||||
|
||||
phase >>= 1;
|
||||
if(!direction) phase ^= 0xf;
|
||||
phase += 7 - direction;
|
||||
|
||||
phase = (((phase - 3) & 0xf) * 127) / 12;
|
||||
phase &= 127;
|
||||
}
|
||||
}
|
||||
|
||||
colour_palette_[index].original = colour;
|
||||
uint8_t *target = reinterpret_cast<uint8_t *>(&colour_palette_[index].luminance_phase);
|
||||
target[0] = luminance;
|
||||
target[1] = phase;
|
||||
}
|
||||
|
||||
void TIA::set_playfield(uint16_t offset, uint8_t value) {
|
||||
@ -238,7 +266,7 @@ void TIA::set_playfield_control_and_ball_size(uint8_t value) {
|
||||
}
|
||||
|
||||
void TIA::set_playfield_ball_colour(uint8_t colour) {
|
||||
colour_palette_[static_cast<int>(ColourIndex::PlayfieldBall)] = colour;
|
||||
set_colour_palette_entry(size_t(ColourIndex::PlayfieldBall), colour);
|
||||
}
|
||||
|
||||
void TIA::set_player_number_and_size(int player, uint8_t value) {
|
||||
@ -300,7 +328,7 @@ void TIA::set_player_motion(int player, uint8_t motion) {
|
||||
|
||||
void TIA::set_player_missile_colour(int player, uint8_t colour) {
|
||||
assert(player >= 0 && player < 2);
|
||||
colour_palette_[static_cast<int>(ColourIndex::PlayerMissile0) + player] = colour;
|
||||
set_colour_palette_entry(size_t(ColourIndex::PlayerMissile0) + size_t(player), colour);
|
||||
}
|
||||
|
||||
void TIA::set_missile_enable(int missile, bool enabled) {
|
||||
@ -382,7 +410,6 @@ void TIA::output_for_cycles(int number_of_cycles) {
|
||||
bool is_reset = output_cursor < 224 && horizontal_counter_ >= 224;
|
||||
|
||||
if(!output_cursor) {
|
||||
if(line_end_function_) line_end_function_(collision_buffer_);
|
||||
std::memset(collision_buffer_, 0, sizeof(collision_buffer_));
|
||||
|
||||
ball_.motion_time %= 228;
|
||||
@ -407,11 +434,11 @@ void TIA::output_for_cycles(int number_of_cycles) {
|
||||
#define Period(function, target) \
|
||||
if(output_cursor < target) { \
|
||||
if(horizontal_counter_ <= target) { \
|
||||
if(crt_) crt_->function((horizontal_counter_ - output_cursor) * 2); \
|
||||
crt_.function((horizontal_counter_ - output_cursor) * 2); \
|
||||
horizontal_counter_ %= cycles_per_line; \
|
||||
return; \
|
||||
} else { \
|
||||
if(crt_) crt_->function((target - output_cursor) * 2); \
|
||||
crt_.function((target - output_cursor) * 2); \
|
||||
output_cursor = target; \
|
||||
} \
|
||||
}
|
||||
@ -437,19 +464,17 @@ void TIA::output_for_cycles(int number_of_cycles) {
|
||||
if(output_mode_ & blank_flag) {
|
||||
if(pixel_target_) {
|
||||
output_pixels(pixels_start_location_, output_cursor);
|
||||
if(crt_) {
|
||||
const int data_length = int(output_cursor - pixels_start_location_);
|
||||
crt_->output_data(data_length * 2, size_t(data_length));
|
||||
}
|
||||
const int data_length = int(output_cursor - pixels_start_location_);
|
||||
crt_.output_data(data_length * 2, size_t(data_length));
|
||||
pixel_target_ = nullptr;
|
||||
pixels_start_location_ = 0;
|
||||
}
|
||||
int duration = std::min(228, horizontal_counter_) - output_cursor;
|
||||
if(crt_) crt_->output_blank(duration * 2);
|
||||
crt_.output_blank(duration * 2);
|
||||
} else {
|
||||
if(!pixels_start_location_ && crt_) {
|
||||
if(!pixels_start_location_) {
|
||||
pixels_start_location_ = output_cursor;
|
||||
pixel_target_ = crt_->begin_data(160);
|
||||
pixel_target_ = reinterpret_cast<uint16_t *>(crt_.begin_data(160));
|
||||
}
|
||||
|
||||
// convert that into pixels
|
||||
@ -461,9 +486,9 @@ void TIA::output_for_cycles(int number_of_cycles) {
|
||||
output_cursor++;
|
||||
}
|
||||
|
||||
if(horizontal_counter_ == cycles_per_line && crt_) {
|
||||
if(horizontal_counter_ == cycles_per_line) {
|
||||
const int data_length = int(output_cursor - pixels_start_location_);
|
||||
crt_->output_data(data_length * 2, size_t(data_length));
|
||||
crt_.output_data(data_length * 2, size_t(data_length));
|
||||
pixel_target_ = nullptr;
|
||||
pixels_start_location_ = 0;
|
||||
}
|
||||
@ -480,7 +505,7 @@ void TIA::output_pixels(int start, int end) {
|
||||
|
||||
if(start < first_pixel_cycle+8 && horizontal_blank_extend_) {
|
||||
while(start < end && start < first_pixel_cycle+8) {
|
||||
pixel_target_[target_position] = 0;
|
||||
pixel_target_[target_position] = 0xff00; // TODO: this assumes little endianness.
|
||||
start++;
|
||||
target_position++;
|
||||
}
|
||||
@ -489,13 +514,13 @@ void TIA::output_pixels(int start, int end) {
|
||||
if(playfield_priority_ == PlayfieldPriority::Score) {
|
||||
while(start < end && start < first_pixel_cycle + 80) {
|
||||
uint8_t buffer_value = collision_buffer_[start - first_pixel_cycle];
|
||||
pixel_target_[target_position] = colour_palette_[colour_mask_by_mode_collision_flags_[static_cast<int>(ColourMode::ScoreLeft)][buffer_value]];
|
||||
pixel_target_[target_position] = colour_palette_[colour_mask_by_mode_collision_flags_[static_cast<int>(ColourMode::ScoreLeft)][buffer_value]].luminance_phase;
|
||||
start++;
|
||||
target_position++;
|
||||
}
|
||||
while(start < end) {
|
||||
uint8_t buffer_value = collision_buffer_[start - first_pixel_cycle];
|
||||
pixel_target_[target_position] = colour_palette_[colour_mask_by_mode_collision_flags_[static_cast<int>(ColourMode::ScoreRight)][buffer_value]];
|
||||
pixel_target_[target_position] = colour_palette_[colour_mask_by_mode_collision_flags_[static_cast<int>(ColourMode::ScoreRight)][buffer_value]].luminance_phase;
|
||||
start++;
|
||||
target_position++;
|
||||
}
|
||||
@ -503,7 +528,7 @@ void TIA::output_pixels(int start, int end) {
|
||||
int table_index = static_cast<int>((playfield_priority_ == PlayfieldPriority::Standard) ? ColourMode::Standard : ColourMode::OnTop);
|
||||
while(start < end) {
|
||||
uint8_t buffer_value = collision_buffer_[start - first_pixel_cycle];
|
||||
pixel_target_[target_position] = colour_palette_[colour_mask_by_mode_collision_flags_[table_index][buffer_value]];
|
||||
pixel_target_[target_position] = colour_palette_[colour_mask_by_mode_collision_flags_[table_index][buffer_value]].luminance_phase;
|
||||
start++;
|
||||
target_position++;
|
||||
}
|
||||
@ -518,20 +543,16 @@ void TIA::output_line() {
|
||||
break;
|
||||
case sync_flag:
|
||||
case sync_flag | blank_flag:
|
||||
if(crt_) {
|
||||
crt_->output_sync(32);
|
||||
crt_->output_blank(32);
|
||||
crt_->output_sync(392);
|
||||
}
|
||||
crt_.output_sync(32);
|
||||
crt_.output_blank(32);
|
||||
crt_.output_sync(392);
|
||||
horizontal_blank_extend_ = false;
|
||||
break;
|
||||
case blank_flag:
|
||||
if(crt_) {
|
||||
crt_->output_blank(32);
|
||||
crt_->output_sync(32);
|
||||
crt_->output_default_colour_burst(32);
|
||||
crt_->output_blank(360);
|
||||
}
|
||||
crt_.output_blank(32);
|
||||
crt_.output_sync(32);
|
||||
crt_.output_default_colour_burst(32);
|
||||
crt_.output_blank(360);
|
||||
horizontal_blank_extend_ = false;
|
||||
break;
|
||||
}
|
||||
|
@ -10,6 +10,7 @@
|
||||
#define TIA_hpp
|
||||
|
||||
#include <algorithm>
|
||||
#include <array>
|
||||
#include <cstdint>
|
||||
#include <functional>
|
||||
|
||||
@ -21,10 +22,6 @@ namespace Atari2600 {
|
||||
class TIA {
|
||||
public:
|
||||
TIA();
|
||||
// The supplied hook is for unit testing only; if instantiated with a line_end_function then it will
|
||||
// be called with the latest collision buffer upon the conclusion of each line. What's a collision
|
||||
// buffer? It's an implementation detail. If you're not writing a unit test, leave it alone.
|
||||
TIA(std::function<void(uint8_t *output_buffer)> line_end_function);
|
||||
|
||||
enum class OutputMode {
|
||||
NTSC, PAL
|
||||
@ -76,12 +73,11 @@ class TIA {
|
||||
uint8_t get_collision_flags(int offset);
|
||||
void clear_collision_flags();
|
||||
|
||||
Outputs::CRT::CRT *get_crt() { return crt_.get(); }
|
||||
void set_crt_delegate(Outputs::CRT::Delegate *);
|
||||
void set_scan_target(Outputs::Display::ScanTarget *);
|
||||
|
||||
private:
|
||||
TIA(bool create_crt);
|
||||
std::unique_ptr<Outputs::CRT::CRT> crt_;
|
||||
std::function<void(uint8_t *output_buffer)> line_end_function_;
|
||||
Outputs::CRT::CRT crt_;
|
||||
|
||||
// the master counter; counts from 0 to 228 with all visible pixels being in the final 160
|
||||
int horizontal_counter_ = 0;
|
||||
@ -110,7 +106,7 @@ class TIA {
|
||||
ScoreRight,
|
||||
OnTop
|
||||
};
|
||||
uint8_t colour_mask_by_mode_collision_flags_[4][64]; // maps from [ColourMode][CollisionMark] to colour_pallete_ entry
|
||||
uint8_t colour_mask_by_mode_collision_flags_[4][64]; // maps from [ColourMode][CollisionMark] to colour_palette_ entry
|
||||
|
||||
enum class ColourIndex {
|
||||
Background = 0,
|
||||
@ -118,7 +114,13 @@ class TIA {
|
||||
PlayerMissile0,
|
||||
PlayerMissile1
|
||||
};
|
||||
uint8_t colour_palette_[4];
|
||||
struct Colour {
|
||||
uint16_t luminance_phase;
|
||||
uint8_t original;
|
||||
};
|
||||
std::array<Colour, 4> colour_palette_;
|
||||
void set_colour_palette_entry(size_t index, uint8_t colour);
|
||||
OutputMode tv_standard_;
|
||||
|
||||
// playfield state
|
||||
int background_half_mask_ = 0;
|
||||
@ -305,7 +307,7 @@ class TIA {
|
||||
inline void output_line();
|
||||
|
||||
int pixels_start_location_ = 0;
|
||||
uint8_t *pixel_target_ = nullptr;
|
||||
uint16_t *pixel_target_ = nullptr;
|
||||
inline void output_pixels(int start, int end);
|
||||
};
|
||||
|
||||
|
@ -16,9 +16,9 @@ namespace {
|
||||
/// The texture unit from which to source 1bpp input data.
|
||||
constexpr GLenum SourceData1BppTextureUnit = GL_TEXTURE0;
|
||||
/// The texture unit from which to source 2bpp input data.
|
||||
constexpr GLenum SourceData2BppTextureUnit = GL_TEXTURE1;
|
||||
//constexpr GLenum SourceData2BppTextureUnit = GL_TEXTURE1;
|
||||
/// The texture unit from which to source 4bpp input data.
|
||||
constexpr GLenum SourceData4BppTextureUnit = GL_TEXTURE2;
|
||||
//constexpr GLenum SourceData4BppTextureUnit = GL_TEXTURE2;
|
||||
|
||||
/// The texture unit which contains raw line-by-line composite, S-Video or RGB data.
|
||||
constexpr GLenum UnprocessedLineBufferTextureUnit = GL_TEXTURE3;
|
||||
@ -102,6 +102,9 @@ ScanTarget::~ScanTarget() {
|
||||
void ScanTarget::set_modals(Modals modals) {
|
||||
modals_ = modals;
|
||||
|
||||
// TODO: almost none of the below can occur here, as this is not necessarily an OpenGL thread.
|
||||
// Whoops!
|
||||
|
||||
const auto data_type_size = Outputs::Display::size_for_data_type(modals.input_data_type);
|
||||
if(data_type_size != data_type_size_) {
|
||||
// TODO: flush output.
|
||||
|
Loading…
x
Reference in New Issue
Block a user