mirror of
https://github.com/TomHarte/CLK.git
synced 2024-12-25 18:30:21 +00:00
Attempts to get the Apple II to honour the AppleII::Card select constraints appropriately.
This commit is contained in:
parent
015f692bd3
commit
ea92363e6c
@ -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,25 +358,31 @@ 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);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 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_;
|
|
||||||
}
|
|
||||||
|
|
||||||
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_;
|
||||||
|
Loading…
Reference in New Issue
Block a user