1
0
mirror of https://github.com/TomHarte/CLK.git synced 2024-07-30 07:29:06 +00:00

Merge pull request #113 from TomHarte/PagingTemplates

Pulls the Atari 2600 paging schemes out into individual classes
This commit is contained in:
Thomas Harte 2017-03-18 21:05:01 -04:00 committed by GitHub
commit 7d7b665be8
18 changed files with 910 additions and 403 deletions

View File

@ -130,7 +130,8 @@ template <class T> class MOS6532 {
interrupt_status_(0), interrupt_status_(0),
port_{{.output_mask = 0, .output = 0}, {.output_mask = 0, .output = 0}}, port_{{.output_mask = 0, .output = 0}, {.output_mask = 0, .output = 0}},
a7_interrupt_({.last_port_value = 0, .enabled = false}), a7_interrupt_({.last_port_value = 0, .enabled = false}),
interrupt_line_(false) interrupt_line_(false),
timer_{.value = 0, .activeShift = 0, .writtenShift = 0, .interrupt_enabled = false}
{} {}
inline void set_port_did_change(int port) inline void set_port_did_change(int port)

View File

@ -10,6 +10,18 @@
#include <algorithm> #include <algorithm>
#include <stdio.h> #include <stdio.h>
#include "Cartridges/CartridgeAtari8k.hpp"
#include "Cartridges/CartridgeAtari16k.hpp"
#include "Cartridges/CartridgeAtari32k.hpp"
#include "Cartridges/CartridgeActivisionStack.hpp"
#include "Cartridges/CartridgeCBSRAMPlus.hpp"
#include "Cartridges/CartridgeCommaVid.hpp"
#include "Cartridges/CartridgeMegaBoy.hpp"
#include "Cartridges/CartridgeMNetwork.hpp"
#include "Cartridges/CartridgeParkerBros.hpp"
#include "Cartridges/CartridgeTigervision.hpp"
#include "Cartridges/CartridgeUnpaged.hpp"
using namespace Atari2600; using namespace Atari2600;
namespace { namespace {
static const double NTSC_clock_rate = 1194720; static const double NTSC_clock_rate = 1194720;
@ -17,280 +29,41 @@ namespace {
} }
Machine::Machine() : Machine::Machine() :
rom_(nullptr),
rom_pages_{nullptr, nullptr, nullptr, nullptr},
tia_input_value_{0xff, 0xff},
cycles_since_speaker_update_(0),
cycles_since_video_update_(0),
cycles_since_6532_update_(0),
frame_record_pointer_(0), frame_record_pointer_(0),
is_ntsc_(true) { is_ntsc_(true) {
set_clock_rate(NTSC_clock_rate); set_clock_rate(NTSC_clock_rate);
} }
void Machine::setup_output(float aspect_ratio) { void Machine::setup_output(float aspect_ratio) {
tia_.reset(new TIA); bus_->tia_.reset(new TIA);
speaker_.reset(new Speaker); bus_->speaker_.reset(new Speaker);
speaker_->set_input_rate((float)(get_clock_rate() / 38.0)); bus_->speaker_->set_input_rate((float)(get_clock_rate() / 38.0));
tia_->get_crt()->set_delegate(this); bus_->tia_->get_crt()->set_delegate(this);
} }
void Machine::close_output() { void Machine::close_output() {
tia_ = nullptr; bus_.reset();
speaker_ = nullptr;
} }
Machine::~Machine() { Machine::~Machine() {
delete[] rom_;
close_output(); close_output();
} }
unsigned int Machine::perform_bus_operation(CPU6502::BusOperation operation, uint16_t address, uint8_t *value) {
uint8_t returnValue = 0xff;
unsigned int cycles_run_for = 3;
// this occurs as a feedback loop — the 2600 requests ready, then performs the cycles_run_for
// leap to the end of ready only once ready is signalled — because on a 6502 ready doesn't take
// 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 == CPU6502::BusOperation::Ready)
cycles_run_for = (unsigned int)tia_->get_cycles_until_horizontal_blank(cycles_since_video_update_);
cycles_since_speaker_update_ += cycles_run_for;
cycles_since_video_update_ += cycles_run_for;
cycles_since_6532_update_ += (cycles_run_for / 3);
if(operation != CPU6502::BusOperation::Ready) {
uint16_t masked_address = address & 0x1fff;
#define AtariPager(start, end) \
if(masked_address >= start && masked_address <= end) { \
uint8_t *base_ptr = &rom_[(masked_address - start) * 4096];\
if(base_ptr != rom_pages_[0]) {\
rom_pages_[0] = base_ptr;\
rom_pages_[1] = base_ptr + 1024;\
rom_pages_[2] = base_ptr + 2048;\
rom_pages_[3] = base_ptr + 3072;\
}\
}
// check for potential paging
switch(paging_model_) {
default:
break;
case StaticAnalyser::Atari2600PagingModel::Atari8k: AtariPager(0x1ff8, 0x1ff9); break;
case StaticAnalyser::Atari2600PagingModel::CBSRamPlus: AtariPager(0x1ff8, 0x1ffa); break;
case StaticAnalyser::Atari2600PagingModel::Atari16k: AtariPager(0x1ff6, 0x1ff9); break;
case StaticAnalyser::Atari2600PagingModel::Atari32k: AtariPager(0x1ff4, 0x1ffb); break;
case StaticAnalyser::Atari2600PagingModel::ParkerBros:
if(masked_address >= 0x1fe0 && masked_address < 0x1ff8) {
int slot = (masked_address >> 3) & 3;
int target = masked_address & 7;
rom_pages_[slot] = &rom_[target * 1024];
}
break;
case StaticAnalyser::Atari2600PagingModel::MegaBoy:
if(masked_address == 0x1fec && isReadOperation(operation)) {
*value = mega_boy_page_;
}
if(masked_address == 0x1ff0) {
mega_boy_page_ = (mega_boy_page_ + 1) & 15;
rom_pages_[0] = &rom_[mega_boy_page_ * 4096];
rom_pages_[1] = rom_pages_[0] + 1024;
rom_pages_[2] = rom_pages_[0] + 2048;
rom_pages_[3] = rom_pages_[0] + 3072;
}
break;
case StaticAnalyser::Atari2600PagingModel::MNetwork:
if(masked_address >= 0x1fe0 && masked_address < 0x1fe7) {
int target = (masked_address & 7) * 2048;
rom_pages_[0] = &rom_[target];
rom_pages_[1] = &rom_[target] + 1024;
} else if(masked_address == 0x1fe7) {
for(int c = 0; c < 8; c++) {
ram_write_targets_[c] = ram_.data() + 1024 + c * 128;
ram_read_targets_[c + 8] = ram_write_targets_[c];
}
} else if(masked_address >= 0x1fe8 && masked_address <= 0x1ffb) {
int offset = (masked_address - 0x1fe8) * 256;
ram_write_targets_[16] = ram_.data() + offset;
ram_write_targets_[17] = ram_write_targets_[16] + 128;
ram_read_targets_[18] = ram_write_targets_[16];
ram_read_targets_[19] = ram_write_targets_[17];
}
break;
case StaticAnalyser::Atari2600PagingModel::ActivisionStack:
if(operation == CPU6502::BusOperation::ReadOpcode) {
// if the last operation was either a JSR or an RTS, pick a new page
// based on the address now being accesses
if(last_opcode_ == 0x20 || last_opcode_ == 0x60) {
if(address & 0x2000) {
rom_pages_[0] = rom_;
} else {
rom_pages_[0] = &rom_[4096];
}
rom_pages_[1] = rom_pages_[0] + 1024;
rom_pages_[2] = rom_pages_[0] + 2048;
rom_pages_[3] = rom_pages_[0] + 3072;
}
}
break;
}
#undef AtariPager
// check for a ROM read
if(address&0x1000) {
int ram_page = (masked_address & 0xfff) >> 7;
ram_write_targets_[ram_page][masked_address & 0x7f] = *value;
if(isReadOperation(operation)) {
if(ram_read_targets_[ram_page]) {
returnValue &= ram_read_targets_[ram_page][masked_address & 0x7f];
} else {
returnValue &= rom_pages_[(address >> 10)&3][address&1023];
}
}
}
// check for a RIOT RAM access
if((address&0x1280) == 0x80) {
if(isReadOperation(operation)) {
returnValue &= mos6532_.get_ram(address);
} else {
mos6532_.set_ram(address, *value);
}
}
// check for a TIA access
if(!(address&0x1080)) {
if(isReadOperation(operation)) {
const uint16_t decodedAddress = address & 0xf;
switch(decodedAddress) {
case 0x00: // missile 0 / player collisions
case 0x01: // missile 1 / player collisions
case 0x02: // player 0 / playfield / ball collisions
case 0x03: // player 1 / playfield / ball collisions
case 0x04: // missile 0 / playfield / ball collisions
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);
break;
case 0x08:
case 0x09:
case 0x0a:
case 0x0b:
// TODO: pot ports
returnValue &= 0;
break;
case 0x0c:
case 0x0d:
returnValue &= tia_input_value_[decodedAddress - 0x0c];
break;
}
} 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 0x02: set_ready_line(true); break;
case 0x03: update_video(); tia_->reset_horizontal_counter(); 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 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 0x0b:
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 0x10:
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 0x1b:
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 0x20:
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 0x25:
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 0x15:
case 0x16: update_audio(); speaker_->set_control(decodedAddress - 0x15, *value); break;
case 0x17:
case 0x18: update_audio(); speaker_->set_divider(decodedAddress - 0x17, *value); break;
case 0x19:
case 0x1a: update_audio(); speaker_->set_volume(decodedAddress - 0x19, *value); break;
case 0x3f:
if(paging_model_ == StaticAnalyser::Atari2600PagingModel::Tigervision && (masked_address == 0x3f)) {
int selected_page = (*value) % (rom_size_ / 2048);
rom_pages_[0] = &rom_[selected_page * 2048];
rom_pages_[1] = rom_pages_[0] + 1024;
}
break;
}
}
}
// check for a PIA access
if((address&0x1280) == 0x280) {
update_6532();
if(isReadOperation(operation)) {
returnValue &= mos6532_.get_register(address);
} else {
mos6532_.set_register(address, *value);
}
}
if(isReadOperation(operation)) {
if(operation == CPU6502::BusOperation::ReadOpcode) last_opcode_ = returnValue;
*value = returnValue;
}
}
if(!tia_->get_cycles_until_horizontal_blank(cycles_since_video_update_)) set_ready_line(false);
return cycles_run_for / 3;
}
void Machine::set_digital_input(Atari2600DigitalInput input, bool state) { void Machine::set_digital_input(Atari2600DigitalInput input, bool state) {
switch (input) { switch (input) {
case Atari2600DigitalInputJoy1Up: mos6532_.update_port_input(0, 0x10, state); break; case Atari2600DigitalInputJoy1Up: bus_->mos6532_.update_port_input(0, 0x10, state); break;
case Atari2600DigitalInputJoy1Down: mos6532_.update_port_input(0, 0x20, state); break; case Atari2600DigitalInputJoy1Down: bus_->mos6532_.update_port_input(0, 0x20, state); break;
case Atari2600DigitalInputJoy1Left: mos6532_.update_port_input(0, 0x40, state); break; case Atari2600DigitalInputJoy1Left: bus_->mos6532_.update_port_input(0, 0x40, state); break;
case Atari2600DigitalInputJoy1Right: mos6532_.update_port_input(0, 0x80, state); break; case Atari2600DigitalInputJoy1Right: bus_->mos6532_.update_port_input(0, 0x80, state); break;
case Atari2600DigitalInputJoy2Up: mos6532_.update_port_input(0, 0x01, state); break; case Atari2600DigitalInputJoy2Up: bus_->mos6532_.update_port_input(0, 0x01, state); break;
case Atari2600DigitalInputJoy2Down: mos6532_.update_port_input(0, 0x02, state); break; case Atari2600DigitalInputJoy2Down: bus_->mos6532_.update_port_input(0, 0x02, state); break;
case Atari2600DigitalInputJoy2Left: mos6532_.update_port_input(0, 0x04, state); break; case Atari2600DigitalInputJoy2Left: bus_->mos6532_.update_port_input(0, 0x04, state); break;
case Atari2600DigitalInputJoy2Right: mos6532_.update_port_input(0, 0x08, state); break; case Atari2600DigitalInputJoy2Right: bus_->mos6532_.update_port_input(0, 0x08, state); break;
// TODO: latching // TODO: latching
case Atari2600DigitalInputJoy1Fire: if(state) tia_input_value_[0] &= ~0x80; else tia_input_value_[0] |= 0x80; break; case Atari2600DigitalInputJoy1Fire: if(state) bus_->tia_input_value_[0] &= ~0x80; else bus_->tia_input_value_[0] |= 0x80; break;
case Atari2600DigitalInputJoy2Fire: if(state) tia_input_value_[1] &= ~0x80; else tia_input_value_[1] |= 0x80; break; case Atari2600DigitalInputJoy2Fire: if(state) bus_->tia_input_value_[1] &= ~0x80; else bus_->tia_input_value_[1] |= 0x80; break;
default: break; default: break;
} }
@ -298,116 +71,47 @@ void Machine::set_digital_input(Atari2600DigitalInput input, bool state) {
void Machine::set_switch_is_enabled(Atari2600Switch input, bool state) { void Machine::set_switch_is_enabled(Atari2600Switch input, bool state) {
switch(input) { switch(input) {
case Atari2600SwitchReset: mos6532_.update_port_input(1, 0x01, state); break; case Atari2600SwitchReset: bus_->mos6532_.update_port_input(1, 0x01, state); break;
case Atari2600SwitchSelect: mos6532_.update_port_input(1, 0x02, state); break; case Atari2600SwitchSelect: bus_->mos6532_.update_port_input(1, 0x02, state); break;
case Atari2600SwitchColour: mos6532_.update_port_input(1, 0x08, state); break; case Atari2600SwitchColour: bus_->mos6532_.update_port_input(1, 0x08, state); break;
case Atari2600SwitchLeftPlayerDifficulty: mos6532_.update_port_input(1, 0x40, state); break; case Atari2600SwitchLeftPlayerDifficulty: bus_->mos6532_.update_port_input(1, 0x40, state); break;
case Atari2600SwitchRightPlayerDifficulty: mos6532_.update_port_input(1, 0x80, state); break; case Atari2600SwitchRightPlayerDifficulty: bus_->mos6532_.update_port_input(1, 0x80, state); break;
} }
} }
void Machine::configure_as_target(const StaticAnalyser::Target &target) { void Machine::configure_as_target(const StaticAnalyser::Target &target) {
if(!target.cartridges.front()->get_segments().size()) return; const std::vector<uint8_t> &rom = target.cartridges.front()->get_segments().front().data;
Storage::Cartridge::Cartridge::Segment segment = target.cartridges.front()->get_segments().front();
size_t length = segment.data.size();
rom_size_ = length;
delete[] rom_;
rom_ = new uint8_t[rom_size_];
size_t offset = 0;
const size_t copy_step = std::min(rom_size_, length);
while(offset < rom_size_) {
size_t copy_length = std::min(copy_step, rom_size_ - offset);
memcpy(&rom_[offset], &segment.data[0], copy_length);
offset += copy_length;
}
// On a real paged cartridge, any page may initially be visible. Various homebrew authors appear to have
// decided the last page will always be initially visible. So do that.
size_t rom_mask = rom_size_ - 1;
uint8_t *rom_base = rom_;
if(rom_size_ > 4096) rom_base = &rom_[rom_size_ - 4096];
rom_pages_[0] = rom_base;
rom_pages_[1] = &rom_base[1024 & rom_mask];
rom_pages_[2] = &rom_base[2048 & rom_mask];
rom_pages_[3] = &rom_base[3072 & rom_mask];
// By default, throw all stores away, and don't ever read from RAM
for(int c = 0; c < sizeof(ram_write_targets_) / sizeof(*ram_write_targets_); c++) {
ram_write_targets_[c] = throwaway_ram_;
ram_read_targets_[c] = nullptr;
}
switch(target.atari.paging_model) { switch(target.atari.paging_model) {
default: case StaticAnalyser::Atari2600PagingModel::None: bus_.reset(new CartridgeUnpaged(rom)); break;
case StaticAnalyser::Atari2600PagingModel::CommaVid: bus_.reset(new CartridgeCommaVid(rom)); break;
case StaticAnalyser::Atari2600PagingModel::ActivisionStack: bus_.reset(new CartridgeActivisionStack(rom)); break;
case StaticAnalyser::Atari2600PagingModel::ParkerBros: bus_.reset(new CartridgeParkerBros(rom)); break;
case StaticAnalyser::Atari2600PagingModel::Tigervision: bus_.reset(new CartridgeTigervision(rom)); break;
case StaticAnalyser::Atari2600PagingModel::CBSRamPlus: bus_.reset(new CartridgeCBSRAMPlus(rom)); break;
case StaticAnalyser::Atari2600PagingModel::MegaBoy: bus_.reset(new CartridgeMegaBoy(rom)); break;
case StaticAnalyser::Atari2600PagingModel::MNetwork: bus_.reset(new CartridgeMNetwork(rom)); break;
case StaticAnalyser::Atari2600PagingModel::Atari8k:
if(target.atari.uses_superchip) { if(target.atari.uses_superchip) {
// allocate 128 bytes of RAM; allow writing from 0x1000, reading from 0x1080 bus_.reset(new CartridgeAtari8kSuperChip(rom));
ram_.resize(128); } else {
ram_write_targets_[0] = ram_.data(); bus_.reset(new CartridgeAtari8k(rom));
ram_read_targets_[1] = ram_write_targets_[0];
} }
break; break;
case StaticAnalyser::Atari2600PagingModel::CBSRamPlus: case StaticAnalyser::Atari2600PagingModel::Atari16k:
// allocate 256 bytes of RAM; allow writing from 0x1000, reading from 0x1100 if(target.atari.uses_superchip) {
ram_.resize(256); bus_.reset(new CartridgeAtari16kSuperChip(rom));
ram_write_targets_[0] = ram_.data(); } else {
ram_write_targets_[1] = ram_write_targets_[0] + 128; bus_.reset(new CartridgeAtari16k(rom));
ram_read_targets_[2] = ram_write_targets_[0];
ram_read_targets_[3] = ram_write_targets_[1];
break;
case StaticAnalyser::Atari2600PagingModel::CommaVid:
// allocate 1kb of RAM; allow reading from 0x1000, writing from 0x1400
ram_.resize(1024);
for(int c = 0; c < 8; c++) {
ram_read_targets_[c] = ram_.data() + 128 * c;
ram_write_targets_[c + 8] = ram_.data() + 128 * c;
} }
break; break;
case StaticAnalyser::Atari2600PagingModel::MegaBoy: case StaticAnalyser::Atari2600PagingModel::Atari32k:
mega_boy_page_ = 15; if(target.atari.uses_superchip) {
break; bus_.reset(new CartridgeAtari32kSuperChip(rom));
case StaticAnalyser::Atari2600PagingModel::MNetwork: } else {
ram_.resize(2048); bus_.reset(new CartridgeAtari32k(rom));
// Put 256 bytes of RAM for writing at 0x1800 and reading at 0x1900 }
ram_write_targets_[16] = ram_.data();
ram_write_targets_[17] = ram_write_targets_[16] + 128;
ram_read_targets_[18] = ram_write_targets_[16];
ram_read_targets_[19] = ram_write_targets_[17];
rom_pages_[0] = rom_;
rom_pages_[1] = rom_pages_[0] + 1024;
rom_pages_[2] = rom_pages_[0] + 2048;
rom_pages_[3] = rom_pages_[0] + 3072;
break; break;
} }
paging_model_ = target.atari.paging_model;
}
#pragma mark - Audio and Video
void Machine::update_audio() {
unsigned int audio_cycles = cycles_since_speaker_update_ / 114;
speaker_->run_for_cycles(audio_cycles);
cycles_since_speaker_update_ %= 114;
}
void Machine::update_video() {
tia_->run_for_cycles((int)cycles_since_video_update_);
cycles_since_video_update_ = 0;
}
void Machine::update_6532() {
mos6532_.run_for_cycles(cycles_since_6532_update_);
cycles_since_6532_update_ = 0;
}
void Machine::synchronise() {
update_audio();
update_video();
speaker_->flush();
} }
#pragma mark - CRT delegate #pragma mark - CRT delegate
@ -436,14 +140,14 @@ void Machine::crt_did_end_batch_of_frames(Outputs::CRT::CRT *crt, unsigned int n
double clock_rate; double clock_rate;
if(is_ntsc_) { if(is_ntsc_) {
clock_rate = NTSC_clock_rate; clock_rate = NTSC_clock_rate;
tia_->set_output_mode(TIA::OutputMode::NTSC); bus_->tia_->set_output_mode(TIA::OutputMode::NTSC);
} else { } else {
clock_rate = PAL_clock_rate; clock_rate = PAL_clock_rate;
tia_->set_output_mode(TIA::OutputMode::PAL); bus_->tia_->set_output_mode(TIA::OutputMode::PAL);
} }
speaker_->set_input_rate((float)(clock_rate / 38.0)); bus_->speaker_->set_input_rate((float)(clock_rate / 38.0));
speaker_->set_high_frequency_cut_off((float)(clock_rate / (38.0 * 2.0))); bus_->speaker_->set_high_frequency_cut_off((float)(clock_rate / (38.0 * 2.0)));
set_clock_rate(clock_rate); set_clock_rate(clock_rate);
} }
} }

View File

@ -13,6 +13,7 @@
#include "../../Processors/6502/CPU6502.hpp" #include "../../Processors/6502/CPU6502.hpp"
#include "../CRTMachine.hpp" #include "../CRTMachine.hpp"
#include "Bus.hpp"
#include "PIA.hpp" #include "PIA.hpp"
#include "Speaker.hpp" #include "Speaker.hpp"
#include "TIA.hpp" #include "TIA.hpp"
@ -26,7 +27,6 @@ const unsigned int number_of_upcoming_events = 6;
const unsigned int number_of_recorded_counters = 7; const unsigned int number_of_recorded_counters = 7;
class Machine: class Machine:
public CPU6502::Processor<Machine>,
public CRTMachine::Machine, public CRTMachine::Machine,
public ConfigurationTarget::Machine, public ConfigurationTarget::Machine,
public Outputs::CRT::Delegate { public Outputs::CRT::Delegate {
@ -40,57 +40,21 @@ class Machine:
void set_digital_input(Atari2600DigitalInput input, bool state); void set_digital_input(Atari2600DigitalInput input, bool state);
void set_switch_is_enabled(Atari2600Switch input, bool state); void set_switch_is_enabled(Atari2600Switch input, bool state);
void set_reset_line(bool state) { bus_->set_reset_line(state); }
// to satisfy CPU6502::Processor
unsigned int perform_bus_operation(CPU6502::BusOperation operation, uint16_t address, uint8_t *value);
void synchronise();
// to satisfy CRTMachine::Machine // to satisfy CRTMachine::Machine
virtual void setup_output(float aspect_ratio); virtual void setup_output(float aspect_ratio);
virtual void close_output(); virtual void close_output();
virtual std::shared_ptr<Outputs::CRT::CRT> get_crt() { return tia_->get_crt(); } virtual std::shared_ptr<Outputs::CRT::CRT> get_crt() { return bus_->tia_->get_crt(); }
virtual std::shared_ptr<Outputs::Speaker> get_speaker() { return speaker_; } virtual std::shared_ptr<Outputs::Speaker> get_speaker() { return bus_->speaker_; }
virtual void run_for_cycles(int number_of_cycles) { CPU6502::Processor<Machine>::run_for_cycles(number_of_cycles); } virtual void run_for_cycles(int number_of_cycles) { bus_->run_for_cycles(number_of_cycles); }
// to satisfy Outputs::CRT::Delegate // to satisfy Outputs::CRT::Delegate
virtual void crt_did_end_batch_of_frames(Outputs::CRT::CRT *crt, unsigned int number_of_frames, unsigned int number_of_unexpected_vertical_syncs); virtual void crt_did_end_batch_of_frames(Outputs::CRT::CRT *crt, unsigned int number_of_frames, unsigned int number_of_unexpected_vertical_syncs);
private: private:
// ROM information // the bus
uint8_t *rom_; std::unique_ptr<Bus> bus_;
size_t rom_size_;
// Memory model
uint8_t *rom_pages_[4], *ram_write_targets_[32], *ram_read_targets_[32];
uint8_t mega_boy_page_;
StaticAnalyser::Atari2600PagingModel paging_model_;
std::vector<uint8_t> ram_;
uint8_t throwaway_ram_[128];
// Activision stack records
uint8_t last_opcode_;
// the RIOT and TIA
PIA mos6532_;
std::unique_ptr<TIA> tia_;
// joystick state
uint8_t tia_input_value_[2];
// outputs
std::shared_ptr<Speaker> speaker_;
// speaker backlog accumlation counter
unsigned int cycles_since_speaker_update_;
inline void update_audio();
// video backlog accumulation counter
unsigned int cycles_since_video_update_;
inline void update_video();
// RIOT backlog accumulation counter
unsigned int cycles_since_6532_update_;
inline void update_6532();
// output frame rate tracker // output frame rate tracker
struct FrameRecord struct FrameRecord

View File

@ -0,0 +1,63 @@
//
// Bus.h
// Clock Signal
//
// Created by Thomas Harte on 18/03/2017.
// Copyright © 2017 Thomas Harte. All rights reserved.
//
#ifndef Atari2600_Bus_hpp
#define Atari2600_Bus_hpp
#include "PIA.hpp"
#include "TIA.hpp"
#include "Speaker.hpp"
namespace Atari2600 {
class Bus {
public:
Bus() :
tia_input_value_{0xff, 0xff},
cycles_since_speaker_update_(0),
cycles_since_video_update_(0),
cycles_since_6532_update_(0) {}
virtual void run_for_cycles(int number_of_cycles) = 0;
virtual void set_reset_line(bool state) = 0;
// the RIOT, TIA and speaker
PIA mos6532_;
std::shared_ptr<TIA> tia_;
std::shared_ptr<Speaker> speaker_;
// joystick state
uint8_t tia_input_value_[2];
protected:
// speaker backlog accumlation counter
unsigned int cycles_since_speaker_update_;
inline void update_audio() {
unsigned int audio_cycles = cycles_since_speaker_update_ / 114;
cycles_since_speaker_update_ %= 114;
speaker_->run_for_cycles(audio_cycles);
}
// video backlog accumulation counter
unsigned int cycles_since_video_update_;
inline void update_video() {
tia_->run_for_cycles((int)cycles_since_video_update_);
cycles_since_video_update_ = 0;
}
// RIOT backlog accumulation counter
unsigned int cycles_since_6532_update_;
inline void update_6532() {
mos6532_.run_for_cycles(cycles_since_6532_update_);
cycles_since_6532_update_ = 0;
}
};
}
#endif /* Atari2600_Bus_hpp */

View File

@ -0,0 +1,174 @@
//
// Cartridge.h
// Clock Signal
//
// Created by Thomas Harte on 17/03/2017.
// Copyright © 2017 Thomas Harte. All rights reserved.
//
#ifndef Atari2600_Cartridge_hpp
#define Atari2600_Cartridge_hpp
#include "../../../Processors/6502/CPU6502.hpp"
#include "../Bus.hpp"
namespace Atari2600 {
template<class T> class Cartridge:
public CPU6502::Processor<Cartridge<T>>,
public Bus {
public:
Cartridge(const std::vector<uint8_t> &rom) :
rom_(rom) {}
void run_for_cycles(int number_of_cycles) { CPU6502::Processor<Cartridge<T>>::run_for_cycles(number_of_cycles); }
void set_reset_line(bool state) { CPU6502::Processor<Cartridge<T>>::set_reset_line(state); }
// to satisfy CPU6502::Processor
unsigned int perform_bus_operation(CPU6502::BusOperation operation, uint16_t address, uint8_t *value) {
uint8_t returnValue = 0xff;
unsigned int cycles_run_for = 3;
// this occurs as a feedback loop — the 2600 requests ready, then performs the cycles_run_for
// leap to the end of ready only once ready is signalled — because on a 6502 ready doesn't take
// 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 == CPU6502::BusOperation::Ready)
cycles_run_for = (unsigned int)tia_->get_cycles_until_horizontal_blank(cycles_since_video_update_);
cycles_since_speaker_update_ += cycles_run_for;
cycles_since_video_update_ += cycles_run_for;
cycles_since_6532_update_ += (cycles_run_for / 3);
if(operation != CPU6502::BusOperation::Ready) {
// give the cartridge a chance to respond to the bus access
static_cast<T *>(this)->perform_bus_operation(operation, address, value);
// check for a RIOT RAM access
if((address&0x1280) == 0x80) {
if(isReadOperation(operation)) {
returnValue &= mos6532_.get_ram(address);
} else {
mos6532_.set_ram(address, *value);
}
}
// check for a TIA access
if(!(address&0x1080)) {
if(isReadOperation(operation)) {
const uint16_t decodedAddress = address & 0xf;
switch(decodedAddress) {
case 0x00: // missile 0 / player collisions
case 0x01: // missile 1 / player collisions
case 0x02: // player 0 / playfield / ball collisions
case 0x03: // player 1 / playfield / ball collisions
case 0x04: // missile 0 / playfield / ball collisions
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);
break;
case 0x08:
case 0x09:
case 0x0a:
case 0x0b:
// TODO: pot ports
returnValue &= 0;
break;
case 0x0c:
case 0x0d:
returnValue &= tia_input_value_[decodedAddress - 0x0c];
break;
}
} 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 0x02: CPU6502::Processor<Cartridge<T>>::set_ready_line(true); break;
case 0x03: update_video(); tia_->reset_horizontal_counter(); 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 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 0x0b:
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 0x10:
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 0x1b:
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 0x20:
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 0x25:
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 0x15:
case 0x16: update_audio(); speaker_->set_control(decodedAddress - 0x15, *value); break;
case 0x17:
case 0x18: update_audio(); speaker_->set_divider(decodedAddress - 0x17, *value); break;
case 0x19:
case 0x1a: update_audio(); speaker_->set_volume(decodedAddress - 0x19, *value); break;
}
}
}
// check for a PIA access
if((address&0x1280) == 0x280) {
update_6532();
if(isReadOperation(operation)) {
returnValue &= mos6532_.get_register(address);
} else {
mos6532_.set_register(address, *value);
}
}
if(isReadOperation(operation)) {
*value &= returnValue;
}
}
if(!tia_->get_cycles_until_horizontal_blank(cycles_since_video_update_)) CPU6502::Processor<Cartridge<T>>::set_ready_line(false);
return cycles_run_for / 3;
}
void synchronise() {
update_audio();
update_video();
speaker_->flush();
}
protected:
std::vector<uint8_t> rom_;
};
}
#endif /* Atari2600_Cartridge_hpp */

View File

@ -0,0 +1,50 @@
//
// CartridgeActivisionStack.h
// Clock Signal
//
// Created by Thomas Harte on 18/03/2017.
// Copyright © 2017 Thomas Harte. All rights reserved.
//
#ifndef Atari2600_CartridgeActivisionStack_hpp
#define Atari2600_CartridgeActivisionStack_hpp
namespace Atari2600 {
class CartridgeActivisionStack: public Cartridge<CartridgeActivisionStack> {
public:
CartridgeActivisionStack(const std::vector<uint8_t> &rom) :
Cartridge(rom),
last_opcode_(0x00) {
rom_ptr_ = rom_.data();
}
void perform_bus_operation(CPU6502::BusOperation operation, uint16_t address, uint8_t *value) {
if(!(address & 0x1000)) return;
// This is a bit of a hack; a real cartridge can't see either the sync or read lines, and can't see
// address line 13. Instead it looks for a pattern in recent address accesses that would imply an
// RST or JSR.
if(operation == CPU6502::BusOperation::ReadOpcode && (last_opcode_ == 0x20 || last_opcode_ == 0x60)) {
if(address & 0x2000) {
rom_ptr_ = rom_.data();
} else {
rom_ptr_ = rom_.data() + 4096;
}
}
if(isReadOperation(operation)) {
*value = rom_ptr_[address & 4095];
}
if(operation == CPU6502::BusOperation::ReadOpcode) last_opcode_ = *value;
}
private:
uint8_t *rom_ptr_;
uint8_t last_opcode_;
};
}
#endif /* Atari2600_CartridgeActivisionStack_hpp */

View File

@ -0,0 +1,66 @@
//
// CartridgeAtari8k.h
// Clock Signal
//
// Created by Thomas Harte on 18/03/2017.
// Copyright © 2017 Thomas Harte. All rights reserved.
//
#ifndef Atari2600_CartridgeAtari16k_hpp
#define Atari2600_CartridgeAtari16k_hpp
#include "Cartridge.hpp"
namespace Atari2600 {
class CartridgeAtari16k: public Cartridge<CartridgeAtari16k> {
public:
CartridgeAtari16k(const std::vector<uint8_t> &rom) :
Cartridge(rom) {
rom_ptr_ = rom_.data();
}
void perform_bus_operation(CPU6502::BusOperation operation, uint16_t address, uint8_t *value) {
address &= 0x1fff;
if(!(address & 0x1000)) return;
if(address >= 0x1ff6 && address <= 0x1ff9) rom_ptr_ = rom_.data() + (address - 0x1ff6) * 4096;
if(isReadOperation(operation)) {
*value = rom_ptr_[address & 4095];
}
}
private:
uint8_t *rom_ptr_;
};
class CartridgeAtari16kSuperChip: public Cartridge<CartridgeAtari16kSuperChip> {
public:
CartridgeAtari16kSuperChip(const std::vector<uint8_t> &rom) :
Cartridge(rom) {
rom_ptr_ = rom_.data();
}
void perform_bus_operation(CPU6502::BusOperation operation, uint16_t address, uint8_t *value) {
address &= 0x1fff;
if(!(address & 0x1000)) return;
if(address >= 0x1ff6 && address <= 0x1ff9) rom_ptr_ = rom_.data() + (address - 0x1ff6) * 4096;
if(isReadOperation(operation)) {
*value = rom_ptr_[address & 4095];
}
if(address < 0x1080) ram_[address & 0x7f] = *value;
else if(address < 0x1100 && isReadOperation(operation)) *value = ram_[address & 0x7f];
}
private:
uint8_t *rom_ptr_;
uint8_t ram_[128];
};
}
#endif /* Atari2600_CartridgeAtari16k_hpp */

View File

@ -0,0 +1,66 @@
//
// CartridgeAtari8k.h
// Clock Signal
//
// Created by Thomas Harte on 18/03/2017.
// Copyright © 2017 Thomas Harte. All rights reserved.
//
#ifndef Atari2600_CartridgeAtari32k_hpp
#define Atari2600_CartridgeAtari32k_hpp
#include "Cartridge.hpp"
namespace Atari2600 {
class CartridgeAtari32k: public Cartridge<CartridgeAtari32k> {
public:
CartridgeAtari32k(const std::vector<uint8_t> &rom) :
Cartridge(rom) {
rom_ptr_ = rom_.data();
}
void perform_bus_operation(CPU6502::BusOperation operation, uint16_t address, uint8_t *value) {
address &= 0x1fff;
if(!(address & 0x1000)) return;
if(address >= 0x1ff4 && address <= 0x1ffb) rom_ptr_ = rom_.data() + (address - 0x1ff4) * 4096;
if(isReadOperation(operation)) {
*value = rom_ptr_[address & 4095];
}
}
private:
uint8_t *rom_ptr_;
};
class CartridgeAtari32kSuperChip: public Cartridge<CartridgeAtari32kSuperChip> {
public:
CartridgeAtari32kSuperChip(const std::vector<uint8_t> &rom) :
Cartridge(rom) {
rom_ptr_ = rom_.data();
}
void perform_bus_operation(CPU6502::BusOperation operation, uint16_t address, uint8_t *value) {
address &= 0x1fff;
if(!(address & 0x1000)) return;
if(address >= 0x1ff4 && address <= 0x1ffb) rom_ptr_ = rom_.data() + (address - 0x1ff4) * 4096;
if(isReadOperation(operation)) {
*value = rom_ptr_[address & 4095];
}
if(address < 0x1080) ram_[address & 0x7f] = *value;
else if(address < 0x1100 && isReadOperation(operation)) *value = ram_[address & 0x7f];
}
private:
uint8_t *rom_ptr_;
uint8_t ram_[128];
};
}
#endif /* Atari2600_CartridgeAtari32k_hpp */

View File

@ -0,0 +1,68 @@
//
// CartridgeAtari8k.h
// Clock Signal
//
// Created by Thomas Harte on 18/03/2017.
// Copyright © 2017 Thomas Harte. All rights reserved.
//
#ifndef Atari2600_CartridgeAtari8k_hpp
#define Atari2600_CartridgeAtari8k_hpp
#include "Cartridge.hpp"
namespace Atari2600 {
class CartridgeAtari8k: public Cartridge<CartridgeAtari8k> {
public:
CartridgeAtari8k(const std::vector<uint8_t> &rom) :
Cartridge(rom) {
rom_ptr_ = rom_.data();
}
void perform_bus_operation(CPU6502::BusOperation operation, uint16_t address, uint8_t *value) {
address &= 0x1fff;
if(!(address & 0x1000)) return;
if(address == 0x1ff8) rom_ptr_ = rom_.data();
else if(address == 0x1ff9) rom_ptr_ = rom_.data() + 4096;
if(isReadOperation(operation)) {
*value = rom_ptr_[address & 4095];
}
}
private:
uint8_t *rom_ptr_;
};
class CartridgeAtari8kSuperChip: public Cartridge<CartridgeAtari8kSuperChip> {
public:
CartridgeAtari8kSuperChip(const std::vector<uint8_t> &rom) :
Cartridge(rom) {
rom_ptr_ = rom_.data();
}
void perform_bus_operation(CPU6502::BusOperation operation, uint16_t address, uint8_t *value) {
address &= 0x1fff;
if(!(address & 0x1000)) return;
if(address == 0x1ff8) rom_ptr_ = rom_.data();
if(address == 0x1ff9) rom_ptr_ = rom_.data() + 4096;
if(isReadOperation(operation)) {
*value = rom_ptr_[address & 4095];
}
if(address < 0x1080) ram_[address & 0x7f] = *value;
else if(address < 0x1100 && isReadOperation(operation)) *value = ram_[address & 0x7f];
}
private:
uint8_t *rom_ptr_;
uint8_t ram_[128];
};
}
#endif /* Atari2600_CartridgeAtari8k_hpp */

View File

@ -0,0 +1,44 @@
//
// CartridgeCBSRAMPlus.h
// Clock Signal
//
// Created by Thomas Harte on 18/03/2017.
// Copyright © 2017 Thomas Harte. All rights reserved.
//
#ifndef Atari2600_CartridgeCBSRAMPlus_hpp
#define Atari2600_CartridgeCBSRAMPlus_hpp
#include "Cartridge.hpp"
namespace Atari2600 {
class CartridgeCBSRAMPlus: public Cartridge<CartridgeCBSRAMPlus> {
public:
CartridgeCBSRAMPlus(const std::vector<uint8_t> &rom) :
Cartridge(rom) {
rom_ptr_ = rom_.data();
}
void perform_bus_operation(CPU6502::BusOperation operation, uint16_t address, uint8_t *value) {
address &= 0x1fff;
if(!(address & 0x1000)) return;
if(address >= 0x1ff8 && address <= 0x1ffa) rom_ptr_ = rom_.data() + (address - 0x1ff8) * 4096;
if(isReadOperation(operation)) {
*value = rom_ptr_[address & 4095];
}
if(address < 0x1100) ram_[address & 0xff] = *value;
else if(address < 0x1200 && isReadOperation(operation)) *value = ram_[address & 0xff];
}
private:
uint8_t *rom_ptr_;
uint8_t ram_[256];
};
}
#endif /* Atari2600_CartridgeCBSRAMPlus_hpp */

View File

@ -0,0 +1,42 @@
//
// CartridgeCommaVid.h
// Clock Signal
//
// Created by Thomas Harte on 18/03/2017.
// Copyright © 2017 Thomas Harte. All rights reserved.
//
#ifndef Atari2600_CartridgeCommaVid_hpp
#define Atari2600_CartridgeCommaVid_hpp
namespace Atari2600 {
class CartridgeCommaVid: public Cartridge<CartridgeCommaVid> {
public:
CartridgeCommaVid(const std::vector<uint8_t> &rom) :
Cartridge(rom) {}
void perform_bus_operation(CPU6502::BusOperation operation, uint16_t address, uint8_t *value) {
if(!(address & 0x1000)) return;
address &= 0x1fff;
if(address < 0x1400) {
if(isReadOperation(operation)) *value = ram_[address & 1023];
return;
}
if(address < 0x1800) {
ram_[address & 1023] = *value;
return;
}
if(isReadOperation(operation)) *value = rom_[address & 2047];
}
private:
uint8_t ram_[1024];
};
}
#endif /* Atari2600_CartridgeCommaVid_hpp */

View File

@ -0,0 +1,68 @@
//
// CartridgeMNetwork.h
// Clock Signal
//
// Created by Thomas Harte on 18/03/2017.
// Copyright © 2017 Thomas Harte. All rights reserved.
//
#ifndef Atari2600_CartridgeMNetwork_hpp
#define Atari2600_CartridgeMNetwork_hpp
#include "Cartridge.hpp"
namespace Atari2600 {
class CartridgeMNetwork: public Cartridge<CartridgeMNetwork> {
public:
CartridgeMNetwork(const std::vector<uint8_t> &rom) :
Cartridge(rom) {
rom_ptr_[0] = rom_.data() + rom_.size() - 4096;
rom_ptr_[1] = rom_ptr_[0] + 2048;
high_ram_ptr_ = high_ram_;
}
void perform_bus_operation(CPU6502::BusOperation operation, uint16_t address, uint8_t *value) {
address &= 0x1fff;
if(!(address & 0x1000)) return;
if(address >= 0x1fe0 && address <= 0x1fe6) {
rom_ptr_[0] = rom_.data() + (address - 0x1fe0) * 2048;
} else if(address == 0x1fe7) {
rom_ptr_[0] = nullptr;
} else if(address >= 0x1ff8 && address <= 0x1ffb) {
int offset = (address - 0x1ff8) * 256;
high_ram_ptr_ = &high_ram_[offset];
}
if(address & 0x800) {
if(address < 0x1900) {
high_ram_ptr_[address & 255] = *value;
} else if(address < 0x1a00) {
if(isReadOperation(operation)) *value = high_ram_ptr_[address & 255];
} else {
if(isReadOperation(operation)) *value = rom_ptr_[1][address & 2047];
}
} else {
if(rom_ptr_[0]) {
if(isReadOperation(operation)) *value = rom_ptr_[0][address & 2047];
} else {
if(address < 0x1400) {
low_ram_[address & 1023] = *value;
} else {
if(isReadOperation(operation)) *value = low_ram_[address & 1023];
}
}
}
}
private:
uint8_t *rom_ptr_[2];
uint8_t *high_ram_ptr_;
uint8_t low_ram_[1024], high_ram_[1024];
};
}
#endif /* Atari2600_CartridgeMNetwork_hpp */

View File

@ -0,0 +1,45 @@
//
// CartridgeMegaBoy.h
// Clock Signal
//
// Created by Thomas Harte on 18/03/2017.
// Copyright © 2017 Thomas Harte. All rights reserved.
//
#ifndef Atari2600_CartridgeMegaBoy_hpp
#define Atari2600_CartridgeMegaBoy_hpp
#include "Cartridge.hpp"
namespace Atari2600 {
class CartridgeMegaBoy: public Cartridge<CartridgeMegaBoy> {
public:
CartridgeMegaBoy(const std::vector<uint8_t> &rom) :
Cartridge(rom),
current_page_(0) {
rom_ptr_ = rom_.data();
}
void perform_bus_operation(CPU6502::BusOperation operation, uint16_t address, uint8_t *value) {
address &= 0x1fff;
if(!(address & 0x1000)) return;
if(address == 0x1ff0) {
current_page_ = (current_page_ + 1) & 15;
rom_ptr_ = rom_.data() + current_page_ * 4096;
}
if(isReadOperation(operation)) {
*value = rom_ptr_[address & 4095];
}
}
private:
uint8_t *rom_ptr_;
uint8_t current_page_;
};
}
#endif /* CartridgeMegaBoy_h */

View File

@ -0,0 +1,46 @@
//
// CartridgeParkerBros.h
// Clock Signal
//
// Created by Thomas Harte on 18/03/2017.
// Copyright © 2017 Thomas Harte. All rights reserved.
//
#ifndef Atari2600_CartridgeParkerBros_hpp
#define Atari2600_CartridgeParkerBros_hpp
#include "Cartridge.hpp"
namespace Atari2600 {
class CartridgeParkerBros: public Cartridge<CartridgeParkerBros> {
public:
CartridgeParkerBros(const std::vector<uint8_t> &rom) :
Cartridge(rom) {
rom_ptr_[0] = rom_.data() + 4096;
rom_ptr_[1] = rom_ptr_[0] + 1024;
rom_ptr_[2] = rom_ptr_[1] + 1024;
rom_ptr_[3] = rom_ptr_[2] + 1024;
}
void perform_bus_operation(CPU6502::BusOperation operation, uint16_t address, uint8_t *value) {
address &= 0x1fff;
if(!(address & 0x1000)) return;
if(address >= 0x1fe0 && address < 0x1ff8) {
int slot = (address >> 3)&3;
rom_ptr_[slot] = rom_.data() + ((address & 7) * 1024);
}
if(isReadOperation(operation)) {
*value = rom_ptr_[(address >> 10)&3][address & 1023];
}
}
private:
uint8_t *rom_ptr_[4];
};
}
#endif /* Atari2600_CartridgeParkerBros_hpp */

View File

@ -0,0 +1,40 @@
//
// CartridgeTigervision.h
// Clock Signal
//
// Created by Thomas Harte on 18/03/2017.
// Copyright © 2017 Thomas Harte. All rights reserved.
//
#ifndef Atari2600_CartridgeTigervision_hpp
#define Atari2600_CartridgeTigervision_hpp
#include "Cartridge.hpp"
namespace Atari2600 {
class CartridgeTigervision: public Cartridge<CartridgeTigervision> {
public:
CartridgeTigervision(const std::vector<uint8_t> &rom) :
Cartridge(rom) {
rom_ptr_[0] = rom_.data() + rom_.size() - 4096;
rom_ptr_[1] = rom_ptr_[0] + 2048;
}
void perform_bus_operation(CPU6502::BusOperation operation, uint16_t address, uint8_t *value) {
if((address&0x1fff) == 0x3f) {
int offset = ((*value) * 2048) & (rom_.size() - 1);
rom_ptr_[0] = rom_.data() + offset;
return;
} else if((address&0x1000) && isReadOperation(operation)) {
*value = rom_ptr_[(address >> 11)&1][address & 2047];
}
}
private:
uint8_t *rom_ptr_[2];
};
}
#endif /* Atari2600_CartridgeTigervision_hpp */

View File

@ -0,0 +1,30 @@
//
// CartridgeUnpaged.h
// Clock Signal
//
// Created by Thomas Harte on 17/03/2017.
// Copyright © 2017 Thomas Harte. All rights reserved.
//
#ifndef Atari2600_CartridgeUnpaged_hpp
#define Atari2600_CartridgeUnpaged_hpp
#include "Cartridge.hpp"
namespace Atari2600 {
class CartridgeUnpaged: public Cartridge<CartridgeUnpaged> {
public:
CartridgeUnpaged(const std::vector<uint8_t> &rom) :
Cartridge(rom) {}
void perform_bus_operation(CPU6502::BusOperation operation, uint16_t address, uint8_t *value) {
if(isReadOperation(operation) && (address & 0x1000)) {
*value = rom_[address & (rom_.size() - 1)];
}
}
};
}
#endif /* Atari2600_CartridgeUnpaged_hpp */

View File

@ -940,6 +940,19 @@
4BEA52641DF3472B007E74F2 /* Speaker.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = Speaker.cpp; sourceTree = "<group>"; }; 4BEA52641DF3472B007E74F2 /* Speaker.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = Speaker.cpp; sourceTree = "<group>"; };
4BEA52651DF3472B007E74F2 /* Speaker.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = Speaker.hpp; sourceTree = "<group>"; }; 4BEA52651DF3472B007E74F2 /* Speaker.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = Speaker.hpp; sourceTree = "<group>"; };
4BEA52671DF34909007E74F2 /* PIA.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = PIA.hpp; sourceTree = "<group>"; }; 4BEA52671DF34909007E74F2 /* PIA.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = PIA.hpp; sourceTree = "<group>"; };
4BEAC0811E7E0DF800EE56B2 /* Cartridge.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = Cartridge.hpp; sourceTree = "<group>"; };
4BEAC0821E7E0DF800EE56B2 /* CartridgeActivisionStack.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = CartridgeActivisionStack.hpp; sourceTree = "<group>"; };
4BEAC0831E7E0DF800EE56B2 /* CartridgeAtari16k.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = CartridgeAtari16k.hpp; sourceTree = "<group>"; };
4BEAC0841E7E0DF800EE56B2 /* CartridgeAtari32k.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = CartridgeAtari32k.hpp; sourceTree = "<group>"; };
4BEAC0851E7E0DF800EE56B2 /* CartridgeAtari8k.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = CartridgeAtari8k.hpp; sourceTree = "<group>"; };
4BEAC0861E7E0DF800EE56B2 /* CartridgeCBSRAMPlus.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = CartridgeCBSRAMPlus.hpp; sourceTree = "<group>"; };
4BEAC0871E7E0DF800EE56B2 /* CartridgeCommaVid.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = CartridgeCommaVid.hpp; sourceTree = "<group>"; };
4BEAC0881E7E0DF800EE56B2 /* CartridgeMegaBoy.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = CartridgeMegaBoy.hpp; sourceTree = "<group>"; };
4BEAC0891E7E0DF800EE56B2 /* CartridgeMNetwork.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = CartridgeMNetwork.hpp; sourceTree = "<group>"; };
4BEAC08A1E7E0DF800EE56B2 /* CartridgeParkerBros.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = CartridgeParkerBros.hpp; sourceTree = "<group>"; };
4BEAC08B1E7E0DF800EE56B2 /* CartridgeTigervision.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = CartridgeTigervision.hpp; sourceTree = "<group>"; };
4BEAC08C1E7E0DF800EE56B2 /* CartridgeUnpaged.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = CartridgeUnpaged.hpp; sourceTree = "<group>"; };
4BEAC08D1E7E0E1A00EE56B2 /* Bus.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = Bus.hpp; sourceTree = "<group>"; };
4BEE0A6A1D72496600532C7B /* Cartridge.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = Cartridge.cpp; sourceTree = "<group>"; }; 4BEE0A6A1D72496600532C7B /* Cartridge.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = Cartridge.cpp; sourceTree = "<group>"; };
4BEE0A6B1D72496600532C7B /* Cartridge.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = Cartridge.hpp; sourceTree = "<group>"; }; 4BEE0A6B1D72496600532C7B /* Cartridge.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = Cartridge.hpp; sourceTree = "<group>"; };
4BEE0A6D1D72496600532C7B /* PRG.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = PRG.cpp; sourceTree = "<group>"; }; 4BEE0A6D1D72496600532C7B /* PRG.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = PRG.cpp; sourceTree = "<group>"; };
@ -1107,9 +1120,11 @@
4BE7C9161E3D397100A5496D /* TIA.cpp */, 4BE7C9161E3D397100A5496D /* TIA.cpp */,
4B2E2D991C3A06EC00138695 /* Atari2600Inputs.h */, 4B2E2D991C3A06EC00138695 /* Atari2600Inputs.h */,
4B2E2D981C3A06EC00138695 /* Atari2600.hpp */, 4B2E2D981C3A06EC00138695 /* Atari2600.hpp */,
4BEAC08D1E7E0E1A00EE56B2 /* Bus.hpp */,
4BEA52671DF34909007E74F2 /* PIA.hpp */, 4BEA52671DF34909007E74F2 /* PIA.hpp */,
4BEA52651DF3472B007E74F2 /* Speaker.hpp */, 4BEA52651DF3472B007E74F2 /* Speaker.hpp */,
4BE7C9171E3D397100A5496D /* TIA.hpp */, 4BE7C9171E3D397100A5496D /* TIA.hpp */,
4BEAC0801E7E0DF800EE56B2 /* Cartridges */,
); );
path = Atari2600; path = Atari2600;
sourceTree = "<group>"; sourceTree = "<group>";
@ -1941,6 +1956,25 @@
path = Resources; path = Resources;
sourceTree = "<group>"; sourceTree = "<group>";
}; };
4BEAC0801E7E0DF800EE56B2 /* Cartridges */ = {
isa = PBXGroup;
children = (
4BEAC0811E7E0DF800EE56B2 /* Cartridge.hpp */,
4BEAC0821E7E0DF800EE56B2 /* CartridgeActivisionStack.hpp */,
4BEAC0831E7E0DF800EE56B2 /* CartridgeAtari16k.hpp */,
4BEAC0841E7E0DF800EE56B2 /* CartridgeAtari32k.hpp */,
4BEAC0851E7E0DF800EE56B2 /* CartridgeAtari8k.hpp */,
4BEAC0861E7E0DF800EE56B2 /* CartridgeCBSRAMPlus.hpp */,
4BEAC0871E7E0DF800EE56B2 /* CartridgeCommaVid.hpp */,
4BEAC0881E7E0DF800EE56B2 /* CartridgeMegaBoy.hpp */,
4BEAC0891E7E0DF800EE56B2 /* CartridgeMNetwork.hpp */,
4BEAC08A1E7E0DF800EE56B2 /* CartridgeParkerBros.hpp */,
4BEAC08B1E7E0DF800EE56B2 /* CartridgeTigervision.hpp */,
4BEAC08C1E7E0DF800EE56B2 /* CartridgeUnpaged.hpp */,
);
path = Cartridges;
sourceTree = "<group>";
};
4BEE0A691D72496600532C7B /* Cartridge */ = { 4BEE0A691D72496600532C7B /* Cartridge */ = {
isa = PBXGroup; isa = PBXGroup;
children = ( children = (

View File

@ -548,6 +548,7 @@ template <class T> class Processor {
ready_is_active_(false), ready_is_active_(false),
scheduled_programs_{nullptr, nullptr, nullptr, nullptr}, scheduled_programs_{nullptr, nullptr, nullptr, nullptr},
inverse_interrupt_flag_(0), inverse_interrupt_flag_(0),
irq_request_history_(0),
s_(0), s_(0),
next_bus_operation_(BusOperation::None), next_bus_operation_(BusOperation::None),
interrupt_requests_(InterruptRequestFlags::PowerOn), interrupt_requests_(InterruptRequestFlags::PowerOn),
@ -568,6 +569,7 @@ template <class T> class Processor {
@discussion Subclasses must implement @c perform_bus_operation(BusOperation operation, uint16_t address, uint8_t *value) . @discussion Subclasses must implement @c perform_bus_operation(BusOperation operation, uint16_t address, uint8_t *value) .
The 6502 will call that method for all bus accesses. The 6502 is guaranteed to perform one bus operation call per cycle. The 6502 will call that method for all bus accesses. The 6502 is guaranteed to perform one bus operation call per cycle.
If it is a read operation then @c value will be seeded with the value 0xff.
@param number_of_cycles The number of cycles to run the 6502 for. @param number_of_cycles The number of cycles to run the 6502 for.
*/ */
@ -646,8 +648,8 @@ template <class T> class Processor {
scheduleProgramProgramCounter++; scheduleProgramProgramCounter++;
#define read_op(val, addr) nextBusOperation = BusOperation::ReadOpcode; busAddress = addr; busValue = &val #define read_op(val, addr) nextBusOperation = BusOperation::ReadOpcode; busAddress = addr; busValue = &val
#define read_mem(val, addr) nextBusOperation = BusOperation::Read; busAddress = addr; busValue = &val #define read_mem(val, addr) nextBusOperation = BusOperation::Read; busAddress = addr; busValue = &val; val = 0xff
#define throwaway_read(addr) nextBusOperation = BusOperation::Read; busAddress = addr; busValue = &throwaway_target #define throwaway_read(addr) nextBusOperation = BusOperation::Read; busAddress = addr; busValue = &throwaway_target; throwaway_target = 0xff
#define write_mem(val, addr) nextBusOperation = BusOperation::Write; busAddress = addr; busValue = &val #define write_mem(val, addr) nextBusOperation = BusOperation::Write; busAddress = addr; busValue = &val
switch(cycle) { switch(cycle) {
@ -656,7 +658,7 @@ template <class T> class Processor {
case CycleFetchOperation: { case CycleFetchOperation: {
last_operation_pc_ = pc_; last_operation_pc_ = pc_;
// printf("%04x x:%02x\n", pc_.full, x_); // printf("%04x\n", pc_.full);
pc_.full++; pc_.full++;
read_op(operation_, last_operation_pc_.full); read_op(operation_, last_operation_pc_.full);