1
0
mirror of https://github.com/TomHarte/CLK.git synced 2024-12-25 03:32:01 +00:00

Attempts to get the Apple II to honour the AppleII::Card select constraints appropriately.

This commit is contained in:
Thomas Harte 2018-05-22 20:34:59 -04:00
parent 015f692bd3
commit ea92363e6c

View File

@ -38,7 +38,8 @@ class ConcreteMachine:
public CPU::MOS6502::BusHandler, public CPU::MOS6502::BusHandler,
public Inputs::Keyboard, public Inputs::Keyboard,
public AppleII::Machine, public AppleII::Machine,
public Activity::Source { public Activity::Source,
public AppleII::Card::Delegate {
private: private:
struct VideoBusHandler : public AppleII::Video::BusHandler { struct VideoBusHandler : public AppleII::Video::BusHandler {
public: public:
@ -66,8 +67,8 @@ class ConcreteMachine:
speaker_.run_for(audio_queue_, cycles_since_audio_update_.divide(Cycles(audio_divider))); speaker_.run_for(audio_queue_, cycles_since_audio_update_.divide(Cycles(audio_divider)));
} }
void update_cards() { void update_cards() {
for(const auto &card : cards_) { for(const auto &card : just_in_time_cards_) {
if(card) card->run_for(cycles_since_card_update_, stretched_cycles_since_card_update_); card->run_for(cycles_since_card_update_, stretched_cycles_since_card_update_);
} }
cycles_since_card_update_ = 0; cycles_since_card_update_ = 0;
stretched_cycles_since_card_update_ = 0; stretched_cycles_since_card_update_ = 0;
@ -84,10 +85,42 @@ class ConcreteMachine:
Cycles cycles_since_audio_update_; Cycles cycles_since_audio_update_;
ROMMachine::ROMFetcher rom_fetcher_; ROMMachine::ROMFetcher rom_fetcher_;
// MARK: - Cards
std::array<std::unique_ptr<AppleII::Card>, 7> cards_; std::array<std::unique_ptr<AppleII::Card>, 7> cards_;
Cycles cycles_since_card_update_; Cycles cycles_since_card_update_;
std::vector<AppleII::Card *> every_cycle_cards_;
std::vector<AppleII::Card *> just_in_time_cards_;
int stretched_cycles_since_card_update_ = 0; int stretched_cycles_since_card_update_ = 0;
void install_card(std::size_t slot, AppleII::Card *card) {
assert(slot >= 1 && slot < 8);
cards_[slot - 1].reset(card);
card->set_delegate(this);
pick_card_messaging_group(card);
}
bool is_every_cycle_card(AppleII::Card *card) {
return !card->get_select_constraints();
}
void pick_card_messaging_group(AppleII::Card *card) {
const bool is_every_cycle = is_every_cycle_card(card);
std::vector<AppleII::Card *> &intended = is_every_cycle ? every_cycle_cards_ : just_in_time_cards_;
std::vector<AppleII::Card *> &undesired = is_every_cycle ? just_in_time_cards_ : every_cycle_cards_;
if(std::find(intended.begin(), intended.end(), card) != intended.end()) return;
auto old_membership = std::find(undesired.begin(), undesired.end(), card);
if(old_membership != undesired.end()) undesired.erase(old_membership);
intended.push_back(card);
}
void card_did_change_select_constraints(AppleII::Card *card) override {
pick_card_messaging_group(card);
}
// MARK: - Memory Map
struct MemoryBlock { struct MemoryBlock {
uint8_t *read_pointer = nullptr; uint8_t *read_pointer = nullptr;
uint8_t *write_pointer = nullptr; uint8_t *write_pointer = nullptr;
@ -174,6 +207,19 @@ class ConcreteMachine:
++ cycles_since_card_update_; ++ cycles_since_card_update_;
cycles_since_audio_update_ += Cycles(7); cycles_since_audio_update_ += Cycles(7);
// The Apple II has a slightly weird timing pattern: every 65th CPU cycle is stretched
// by an extra 1/7th. That's because one cycle lasts 3.5 NTSC colour clocks, so after
// 65 cycles a full line of 227.5 colour clocks have passed. But the high-rate binary
// signal approximation that produces colour needs to be in phase, so a stretch of exactly
// 0.5 further colour cycles is added. The video class handles that implicitly, but it
// needs to be accumulated here for the audio.
cycles_into_current_line_ = (cycles_into_current_line_ + 1) % 65;
const bool is_stretched_cycle = !cycles_into_current_line_;
if(is_stretched_cycle) {
++ cycles_since_audio_update_;
++ stretched_cycles_since_card_update_;
}
/* /*
There are five distinct zones of memory on an Apple II: There are five distinct zones of memory on an Apple II:
@ -286,7 +332,13 @@ class ConcreteMachine:
break; break;
} }
/*
Communication with cards.
*/
if(address >= 0xc090 && address < 0xc800) { if(address >= 0xc090 && address < 0xc800) {
// If this is a card access, figure out which card is at play before determining
// the totality of who needs messaging.
size_t card_number = 0; size_t card_number = 0;
AppleII::Card::Select select = AppleII::Card::None; AppleII::Card::Select select = AppleII::Card::None;
@ -306,23 +358,29 @@ class ConcreteMachine:
select = AppleII::Card::IO; select = AppleII::Card::IO;
} }
if(cards_[card_number]) { // If the selected card is a just-in-time card, update the just-in-time cards,
// and then message it specifically.
AppleII::Card *const target = cards_[card_number].get();
if(target && !is_every_cycle_card(target)) {
update_cards(); update_cards();
cards_[card_number]->perform_bus_operation(select, isReadOperation(operation), address, value); target->perform_bus_operation(select, isReadOperation(operation), address, value);
}
}
} }
// The Apple II has a slightly weird timing pattern: every 65th CPU cycle is stretched // Update all the every-cycle cards regardless, but send them a ::None select if they're
// by an extra 1/7th. That's because one cycle lasts 3.5 NTSC colour clocks, so after // not the one actually selected.
// 65 cycles a full line of 227.5 colour clocks have passed. But the high-rate binary for(const auto &card: every_cycle_cards_) {
// signal approximation that produces colour needs to be in phase, so a stretch of exactly card->run_for(Cycles(1), is_stretched_cycle);
// 0.5 further colour cycles is added. The video class handles that implicitly, but it card->perform_bus_operation(
// needs to be accumulated here for the audio. (card == target) ? select : AppleII::Card::None,
cycles_into_current_line_ = (cycles_into_current_line_ + 1) % 65; isReadOperation(operation), address, value);
if(!cycles_into_current_line_) { }
++ cycles_since_audio_update_; } else {
++ stretched_cycles_since_card_update_; // Update all every-cycle cards and give them the cycle.
for(const auto &card: every_cycle_cards_) {
card->run_for(Cycles(1), is_stretched_cycle);
card->perform_bus_operation(AppleII::Card::None, isReadOperation(operation), address, value);
}
}
} }
return Cycles(1); return Cycles(1);
@ -395,7 +453,8 @@ class ConcreteMachine:
auto *const apple_target = dynamic_cast<const Target *>(target); auto *const apple_target = dynamic_cast<const Target *>(target);
if(apple_target->disk_controller != Target::DiskController::None) { if(apple_target->disk_controller != Target::DiskController::None) {
cards_[5].reset(new AppleII::DiskIICard(rom_fetcher_, apple_target->disk_controller == Target::DiskController::SixteenSector)); // Apple recommended slot 6 for the (first) Disk II.
install_card(6, new AppleII::DiskIICard(rom_fetcher_, apple_target->disk_controller == Target::DiskController::SixteenSector));
} }
rom_ = (apple_target->model == Target::Model::II) ? apple2_rom_ : apple2plus_rom_; rom_ = (apple_target->model == Target::Model::II) ? apple2_rom_ : apple2plus_rom_;