1
0
mirror of https://github.com/TomHarte/CLK.git synced 2024-12-24 12:30:17 +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 Inputs::Keyboard,
public AppleII::Machine,
public Activity::Source {
public Activity::Source,
public AppleII::Card::Delegate {
private:
struct VideoBusHandler : public AppleII::Video::BusHandler {
public:
@ -66,8 +67,8 @@ class ConcreteMachine:
speaker_.run_for(audio_queue_, cycles_since_audio_update_.divide(Cycles(audio_divider)));
}
void update_cards() {
for(const auto &card : cards_) {
if(card) card->run_for(cycles_since_card_update_, stretched_cycles_since_card_update_);
for(const auto &card : just_in_time_cards_) {
card->run_for(cycles_since_card_update_, stretched_cycles_since_card_update_);
}
cycles_since_card_update_ = 0;
stretched_cycles_since_card_update_ = 0;
@ -84,10 +85,42 @@ class ConcreteMachine:
Cycles cycles_since_audio_update_;
ROMMachine::ROMFetcher rom_fetcher_;
// MARK: - Cards
std::array<std::unique_ptr<AppleII::Card>, 7> cards_;
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;
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 {
uint8_t *read_pointer = nullptr;
uint8_t *write_pointer = nullptr;
@ -174,6 +207,19 @@ class ConcreteMachine:
++ cycles_since_card_update_;
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:
@ -286,7 +332,13 @@ class ConcreteMachine:
break;
}
/*
Communication with cards.
*/
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;
AppleII::Card::Select select = AppleII::Card::None;
@ -306,23 +358,29 @@ class ConcreteMachine:
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();
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
// 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;
if(!cycles_into_current_line_) {
++ cycles_since_audio_update_;
++ stretched_cycles_since_card_update_;
// Update all the every-cycle cards regardless, but send them a ::None select if they're
// not the one actually selected.
for(const auto &card: every_cycle_cards_) {
card->run_for(Cycles(1), is_stretched_cycle);
card->perform_bus_operation(
(card == target) ? select : AppleII::Card::None,
isReadOperation(operation), address, value);
}
} else {
// 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);
@ -395,7 +453,8 @@ class ConcreteMachine:
auto *const apple_target = dynamic_cast<const Target *>(target);
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_;