2015-07-16 23:56:02 +00:00
|
|
|
//
|
|
|
|
// Atari2600.cpp
|
2015-07-26 19:25:11 +00:00
|
|
|
// CLK
|
2015-07-16 23:56:02 +00:00
|
|
|
//
|
|
|
|
// Created by Thomas Harte on 14/07/2015.
|
|
|
|
// Copyright © 2015 Thomas Harte. All rights reserved.
|
|
|
|
//
|
|
|
|
|
|
|
|
#include "Atari2600.hpp"
|
2017-11-11 20:28:40 +00:00
|
|
|
|
2015-07-16 23:56:02 +00:00
|
|
|
#include <algorithm>
|
2017-11-11 20:28:40 +00:00
|
|
|
#include <cstdio>
|
2015-07-16 23:56:02 +00:00
|
|
|
|
2017-08-16 16:39:15 +00:00
|
|
|
#include "Cartridges/Atari8k.hpp"
|
|
|
|
#include "Cartridges/Atari16k.hpp"
|
|
|
|
#include "Cartridges/Atari32k.hpp"
|
|
|
|
#include "Cartridges/ActivisionStack.hpp"
|
|
|
|
#include "Cartridges/CBSRAMPlus.hpp"
|
|
|
|
#include "Cartridges/CommaVid.hpp"
|
|
|
|
#include "Cartridges/MegaBoy.hpp"
|
|
|
|
#include "Cartridges/MNetwork.hpp"
|
|
|
|
#include "Cartridges/ParkerBros.hpp"
|
|
|
|
#include "Cartridges/Pitfall2.hpp"
|
|
|
|
#include "Cartridges/Tigervision.hpp"
|
|
|
|
#include "Cartridges/Unpaged.hpp"
|
2017-03-18 18:01:04 +00:00
|
|
|
|
2016-05-16 12:01:29 +00:00
|
|
|
namespace {
|
2016-08-14 17:33:20 +00:00
|
|
|
static const double NTSC_clock_rate = 1194720;
|
|
|
|
static const double PAL_clock_rate = 1182298;
|
2016-05-16 12:01:29 +00:00
|
|
|
}
|
2015-07-16 23:56:02 +00:00
|
|
|
|
2017-08-16 18:52:40 +00:00
|
|
|
namespace Atari2600 {
|
2016-04-24 10:56:08 +00:00
|
|
|
|
2017-10-16 00:44:59 +00:00
|
|
|
class Joystick: public Inputs::Joystick {
|
|
|
|
public:
|
2017-11-11 20:28:40 +00:00
|
|
|
Joystick(Bus *bus, std::size_t shift, std::size_t fire_tia_input) :
|
2017-10-16 00:44:59 +00:00
|
|
|
bus_(bus), shift_(shift), fire_tia_input_(fire_tia_input) {}
|
|
|
|
|
|
|
|
void set_digital_input(DigitalInput digital_input, bool is_active) {
|
|
|
|
switch(digital_input) {
|
|
|
|
case DigitalInput::Up: bus_->mos6532_.update_port_input(0, 0x10 >> shift_, is_active); break;
|
|
|
|
case DigitalInput::Down: bus_->mos6532_.update_port_input(0, 0x20 >> shift_, is_active); break;
|
|
|
|
case DigitalInput::Left: bus_->mos6532_.update_port_input(0, 0x40 >> shift_, is_active); break;
|
|
|
|
case DigitalInput::Right: bus_->mos6532_.update_port_input(0, 0x80 >> shift_, is_active); break;
|
|
|
|
|
|
|
|
// TODO: latching
|
|
|
|
case DigitalInput::Fire:
|
|
|
|
if(is_active)
|
|
|
|
bus_->tia_input_value_[fire_tia_input_] &= ~0x80;
|
|
|
|
else
|
|
|
|
bus_->tia_input_value_[fire_tia_input_] |= 0x80;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
private:
|
|
|
|
Bus *bus_;
|
2017-11-11 20:28:40 +00:00
|
|
|
std::size_t shift_, fire_tia_input_;
|
2017-10-16 00:44:59 +00:00
|
|
|
};
|
|
|
|
|
2017-08-16 18:52:40 +00:00
|
|
|
class ConcreteMachine:
|
|
|
|
public Machine,
|
|
|
|
public Outputs::CRT::Delegate {
|
|
|
|
public:
|
|
|
|
ConcreteMachine() :
|
|
|
|
frame_record_pointer_(0),
|
|
|
|
is_ntsc_(true) {
|
|
|
|
set_clock_rate(NTSC_clock_rate);
|
|
|
|
}
|
2016-05-12 01:07:18 +00:00
|
|
|
|
2017-08-16 18:52:40 +00:00
|
|
|
~ConcreteMachine() {
|
|
|
|
close_output();
|
|
|
|
}
|
2016-04-25 00:34:25 +00:00
|
|
|
|
2017-10-16 00:44:59 +00:00
|
|
|
void configure_as_target(const StaticAnalyser::Target &target) override {
|
2017-08-17 14:48:29 +00:00
|
|
|
const std::vector<uint8_t> &rom = target.media.cartridges.front()->get_segments().front().data;
|
2017-08-16 18:52:40 +00:00
|
|
|
switch(target.atari.paging_model) {
|
|
|
|
case StaticAnalyser::Atari2600PagingModel::ActivisionStack: bus_.reset(new Cartridge::Cartridge<Cartridge::ActivisionStack>(rom)); break;
|
|
|
|
case StaticAnalyser::Atari2600PagingModel::CBSRamPlus: bus_.reset(new Cartridge::Cartridge<Cartridge::CBSRAMPlus>(rom)); break;
|
|
|
|
case StaticAnalyser::Atari2600PagingModel::CommaVid: bus_.reset(new Cartridge::Cartridge<Cartridge::CommaVid>(rom)); break;
|
|
|
|
case StaticAnalyser::Atari2600PagingModel::MegaBoy: bus_.reset(new Cartridge::Cartridge<Cartridge::MegaBoy>(rom)); break;
|
|
|
|
case StaticAnalyser::Atari2600PagingModel::MNetwork: bus_.reset(new Cartridge::Cartridge<Cartridge::MNetwork>(rom)); break;
|
|
|
|
case StaticAnalyser::Atari2600PagingModel::None: bus_.reset(new Cartridge::Cartridge<Cartridge::Unpaged>(rom)); break;
|
|
|
|
case StaticAnalyser::Atari2600PagingModel::ParkerBros: bus_.reset(new Cartridge::Cartridge<Cartridge::ParkerBros>(rom)); break;
|
|
|
|
case StaticAnalyser::Atari2600PagingModel::Pitfall2: bus_.reset(new Cartridge::Cartridge<Cartridge::Pitfall2>(rom)); break;
|
|
|
|
case StaticAnalyser::Atari2600PagingModel::Tigervision: bus_.reset(new Cartridge::Cartridge<Cartridge::Tigervision>(rom)); break;
|
|
|
|
|
|
|
|
case StaticAnalyser::Atari2600PagingModel::Atari8k:
|
|
|
|
if(target.atari.uses_superchip) {
|
|
|
|
bus_.reset(new Cartridge::Cartridge<Cartridge::Atari8kSuperChip>(rom));
|
|
|
|
} else {
|
|
|
|
bus_.reset(new Cartridge::Cartridge<Cartridge::Atari8k>(rom));
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case StaticAnalyser::Atari2600PagingModel::Atari16k:
|
|
|
|
if(target.atari.uses_superchip) {
|
|
|
|
bus_.reset(new Cartridge::Cartridge<Cartridge::Atari16kSuperChip>(rom));
|
|
|
|
} else {
|
|
|
|
bus_.reset(new Cartridge::Cartridge<Cartridge::Atari16k>(rom));
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case StaticAnalyser::Atari2600PagingModel::Atari32k:
|
|
|
|
if(target.atari.uses_superchip) {
|
|
|
|
bus_.reset(new Cartridge::Cartridge<Cartridge::Atari32kSuperChip>(rom));
|
|
|
|
} else {
|
|
|
|
bus_.reset(new Cartridge::Cartridge<Cartridge::Atari32k>(rom));
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
2017-10-16 00:44:59 +00:00
|
|
|
|
2017-10-16 00:49:47 +00:00
|
|
|
joysticks_.emplace_back(new Joystick(bus_.get(), 0, 0));
|
|
|
|
joysticks_.emplace_back(new Joystick(bus_.get(), 4, 1));
|
2017-08-16 18:52:40 +00:00
|
|
|
}
|
2015-07-28 01:15:10 +00:00
|
|
|
|
2017-10-16 00:44:59 +00:00
|
|
|
bool insert_media(const StaticAnalyser::Media &media) override {
|
2017-08-17 14:48:29 +00:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2017-10-16 00:49:47 +00:00
|
|
|
std::vector<std::unique_ptr<Inputs::Joystick>> &get_joysticks() override {
|
2017-10-16 00:44:59 +00:00
|
|
|
return joysticks_;
|
2017-08-16 18:52:40 +00:00
|
|
|
}
|
2015-08-19 00:33:24 +00:00
|
|
|
|
2017-10-16 00:44:59 +00:00
|
|
|
void set_switch_is_enabled(Atari2600Switch input, bool state) override {
|
2017-08-16 18:52:40 +00:00
|
|
|
switch(input) {
|
|
|
|
case Atari2600SwitchReset: bus_->mos6532_.update_port_input(1, 0x01, state); break;
|
|
|
|
case Atari2600SwitchSelect: bus_->mos6532_.update_port_input(1, 0x02, state); break;
|
|
|
|
case Atari2600SwitchColour: bus_->mos6532_.update_port_input(1, 0x08, state); break;
|
|
|
|
case Atari2600SwitchLeftPlayerDifficulty: bus_->mos6532_.update_port_input(1, 0x40, state); break;
|
|
|
|
case Atari2600SwitchRightPlayerDifficulty: bus_->mos6532_.update_port_input(1, 0x80, state); break;
|
2017-03-18 21:34:34 +00:00
|
|
|
}
|
2017-08-16 18:52:40 +00:00
|
|
|
}
|
2017-02-20 02:20:37 +00:00
|
|
|
|
2017-10-16 00:44:59 +00:00
|
|
|
void set_reset_switch(bool state) override {
|
2017-08-16 18:52:40 +00:00
|
|
|
bus_->set_reset_line(state);
|
|
|
|
}
|
2017-02-20 02:20:37 +00:00
|
|
|
|
2017-08-16 18:52:40 +00:00
|
|
|
// to satisfy CRTMachine::Machine
|
2017-10-16 00:44:59 +00:00
|
|
|
void setup_output(float aspect_ratio) override {
|
2017-08-16 18:52:40 +00:00
|
|
|
bus_->tia_.reset(new TIA);
|
|
|
|
bus_->speaker_.reset(new Speaker);
|
2017-10-21 23:49:04 +00:00
|
|
|
bus_->speaker_->set_input_rate(static_cast<float>(get_clock_rate() / static_cast<double>(CPUTicksPerAudioTick)));
|
2017-08-16 18:52:40 +00:00
|
|
|
bus_->tia_->get_crt()->set_delegate(this);
|
|
|
|
}
|
2017-02-20 02:20:37 +00:00
|
|
|
|
2017-10-16 00:44:59 +00:00
|
|
|
void close_output() override {
|
2017-08-16 18:52:40 +00:00
|
|
|
bus_.reset();
|
2017-02-20 02:20:37 +00:00
|
|
|
}
|
|
|
|
|
2017-10-16 00:44:59 +00:00
|
|
|
std::shared_ptr<Outputs::CRT::CRT> get_crt() override {
|
2017-08-16 18:52:40 +00:00
|
|
|
return bus_->tia_->get_crt();
|
|
|
|
}
|
|
|
|
|
2017-10-16 00:44:59 +00:00
|
|
|
std::shared_ptr<Outputs::Speaker> get_speaker() override {
|
2017-08-16 18:52:40 +00:00
|
|
|
return bus_->speaker_;
|
|
|
|
}
|
2017-03-07 00:37:35 +00:00
|
|
|
|
2017-10-16 00:44:59 +00:00
|
|
|
void run_for(const Cycles cycles) override {
|
2017-08-16 18:52:40 +00:00
|
|
|
bus_->run_for(cycles);
|
2017-03-07 00:37:35 +00:00
|
|
|
}
|
2017-08-16 18:52:40 +00:00
|
|
|
|
|
|
|
// to satisfy Outputs::CRT::Delegate
|
2017-10-16 00:44:59 +00:00
|
|
|
void crt_did_end_batch_of_frames(Outputs::CRT::CRT *crt, unsigned int number_of_frames, unsigned int number_of_unexpected_vertical_syncs) override {
|
2017-11-11 20:28:40 +00:00
|
|
|
const std::size_t number_of_frame_records = sizeof(frame_records_) / sizeof(frame_records_[0]);
|
2017-08-16 18:52:40 +00:00
|
|
|
frame_records_[frame_record_pointer_ % number_of_frame_records].number_of_frames = number_of_frames;
|
|
|
|
frame_records_[frame_record_pointer_ % number_of_frame_records].number_of_unexpected_vertical_syncs = number_of_unexpected_vertical_syncs;
|
|
|
|
frame_record_pointer_ ++;
|
|
|
|
|
|
|
|
if(frame_record_pointer_ >= 6) {
|
|
|
|
unsigned int total_number_of_frames = 0;
|
|
|
|
unsigned int total_number_of_unexpected_vertical_syncs = 0;
|
2017-11-11 20:28:40 +00:00
|
|
|
for(std::size_t c = 0; c < number_of_frame_records; c++) {
|
2017-08-16 18:52:40 +00:00
|
|
|
total_number_of_frames += frame_records_[c].number_of_frames;
|
|
|
|
total_number_of_unexpected_vertical_syncs += frame_records_[c].number_of_unexpected_vertical_syncs;
|
|
|
|
}
|
|
|
|
|
|
|
|
if(total_number_of_unexpected_vertical_syncs >= total_number_of_frames >> 1) {
|
2017-11-11 20:28:40 +00:00
|
|
|
for(std::size_t c = 0; c < number_of_frame_records; c++) {
|
2017-08-16 18:52:40 +00:00
|
|
|
frame_records_[c].number_of_frames = 0;
|
|
|
|
frame_records_[c].number_of_unexpected_vertical_syncs = 0;
|
|
|
|
}
|
|
|
|
is_ntsc_ ^= true;
|
|
|
|
|
|
|
|
double clock_rate;
|
|
|
|
if(is_ntsc_) {
|
|
|
|
clock_rate = NTSC_clock_rate;
|
|
|
|
bus_->tia_->set_output_mode(TIA::OutputMode::NTSC);
|
|
|
|
} else {
|
|
|
|
clock_rate = PAL_clock_rate;
|
|
|
|
bus_->tia_->set_output_mode(TIA::OutputMode::PAL);
|
|
|
|
}
|
|
|
|
|
2017-10-21 23:49:04 +00:00
|
|
|
bus_->speaker_->set_input_rate(static_cast<float>(clock_rate / static_cast<double>(CPUTicksPerAudioTick)));
|
|
|
|
bus_->speaker_->set_high_frequency_cut_off(static_cast<float>(clock_rate / (static_cast<double>(CPUTicksPerAudioTick) * 2.0)));
|
2017-08-16 18:52:40 +00:00
|
|
|
set_clock_rate(clock_rate);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
private:
|
|
|
|
// the bus
|
|
|
|
std::unique_ptr<Bus> bus_;
|
|
|
|
|
|
|
|
// output frame rate tracker
|
|
|
|
struct FrameRecord {
|
|
|
|
unsigned int number_of_frames;
|
|
|
|
unsigned int number_of_unexpected_vertical_syncs;
|
|
|
|
|
|
|
|
FrameRecord() : number_of_frames(0), number_of_unexpected_vertical_syncs(0) {}
|
|
|
|
} frame_records_[4];
|
|
|
|
unsigned int frame_record_pointer_;
|
|
|
|
bool is_ntsc_;
|
2017-10-16 00:49:47 +00:00
|
|
|
std::vector<std::unique_ptr<Inputs::Joystick>> joysticks_;
|
2017-08-16 18:52:40 +00:00
|
|
|
};
|
|
|
|
|
2017-02-20 02:20:37 +00:00
|
|
|
}
|
2017-08-16 18:52:40 +00:00
|
|
|
|
|
|
|
using namespace Atari2600;
|
|
|
|
|
|
|
|
Machine *Machine::Atari2600() {
|
|
|
|
return new Atari2600::ConcreteMachine;
|
|
|
|
}
|
|
|
|
|
|
|
|
Machine::~Machine() {}
|