mirror of
https://github.com/TomHarte/CLK.git
synced 2025-01-12 00:30:31 +00:00
Specs out a new AppleII::Card
interface.
Doesn't yet fully implement it on the Apple II side though.
This commit is contained in:
parent
e482929da8
commit
80d34f5511
@ -286,25 +286,29 @@ class ConcreteMachine:
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
if(address >= 0xc100 && address < 0xc800) {
|
if(address >= 0xc090 && address < 0xc800) {
|
||||||
|
size_t card_number = 0;
|
||||||
|
AppleII::Card::Select select = AppleII::Card::None;
|
||||||
|
|
||||||
|
if(address >= 0xc100) {
|
||||||
/*
|
/*
|
||||||
Decode the area conventionally used by cards for ROMs:
|
Decode the area conventionally used by cards for ROMs:
|
||||||
0xCn00 to 0xCnff: card n.
|
0xCn00 to 0xCnff: card n.
|
||||||
*/
|
*/
|
||||||
const size_t card_number = (address - 0xc100) >> 8;
|
card_number = (address - 0xc100) >> 8;
|
||||||
if(cards_[card_number]) {
|
select = AppleII::Card::Device;
|
||||||
update_cards();
|
} else {
|
||||||
cards_[card_number]->perform_bus_operation(operation, address & 0xff, value);
|
|
||||||
}
|
|
||||||
} else if(address >= 0xc090 && address < 0xc100) {
|
|
||||||
/*
|
/*
|
||||||
Decode the area conventionally used by cards for registers:
|
Decode the area conventionally used by cards for registers:
|
||||||
C0n0 to C0nF: card n - 8.
|
C0n0 to C0nF: card n - 8.
|
||||||
*/
|
*/
|
||||||
const size_t card_number = (address - 0xc090) >> 4;
|
card_number = (address - 0xc090) >> 4;
|
||||||
|
select = AppleII::Card::IO;
|
||||||
|
}
|
||||||
|
|
||||||
if(cards_[card_number]) {
|
if(cards_[card_number]) {
|
||||||
update_cards();
|
update_cards();
|
||||||
cards_[card_number]->perform_bus_operation(operation, 0x100 | (address&0xf), value);
|
cards_[card_number]->perform_bus_operation(select, isReadOperation(operation), address, value);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -15,16 +15,97 @@
|
|||||||
|
|
||||||
namespace AppleII {
|
namespace AppleII {
|
||||||
|
|
||||||
|
/*!
|
||||||
|
This provides a small subset of the interface offered to cards installed in
|
||||||
|
an Apple II, oriented pragmatically around the cards that are implemented.
|
||||||
|
|
||||||
|
The main underlying rule is as it is elsewhere in the emulator: no
|
||||||
|
_inaccurate_ simplifications — no provision of information that shouldn't
|
||||||
|
actually commute, no interfaces that say they do one thing but which by both
|
||||||
|
both sides are coupled through an unwritten understanding of abuse.
|
||||||
|
|
||||||
|
Special notes:
|
||||||
|
|
||||||
|
Devices that announce a select constraint, being interested in acting only
|
||||||
|
when their IO or Device select is active will receive just-in-time @c run_for
|
||||||
|
notifications, as well as being updated at the end of each of the Apple's
|
||||||
|
@c run_for periods, prior to a @c flush.
|
||||||
|
|
||||||
|
Devices that do not announce a select constraint will prima facie receive a
|
||||||
|
@c perform_bus_operation every cycle. They'll also receive a @c flush.
|
||||||
|
It is **highly** recomended that such devices also implement @c Sleeper
|
||||||
|
as they otherwise prima facie require a virtual method call every
|
||||||
|
single cycle.
|
||||||
|
*/
|
||||||
class Card {
|
class Card {
|
||||||
public:
|
public:
|
||||||
/*! Advances time by @c cycles, of which @c stretches were stretched. */
|
enum Select: int {
|
||||||
|
None = 0, // No select line is active
|
||||||
|
IO = 1 << 0, // IO select is active
|
||||||
|
Device = 1 << 1, // Device select is active
|
||||||
|
};
|
||||||
|
|
||||||
|
/*!
|
||||||
|
Advances time by @c cycles, of which @c stretches were stretched.
|
||||||
|
|
||||||
|
This is posted only to cards that announced a select constraint. Cards with
|
||||||
|
no constraints, that want to be informed of every machine cycle, will receive
|
||||||
|
a call to perform_bus_operation every cycle and should use that for time keeping.
|
||||||
|
*/
|
||||||
virtual void run_for(Cycles half_cycles, int stretches) {}
|
virtual void run_for(Cycles half_cycles, int stretches) {}
|
||||||
|
|
||||||
/*! Performs a bus operation; the card is implicitly selected. */
|
/// Requests a flush of any pending audio or video output.
|
||||||
virtual void perform_bus_operation(CPU::MOS6502::BusOperation operation, uint16_t address, uint8_t *value) = 0;
|
virtual void flush() {}
|
||||||
|
|
||||||
/*! Supplies a target for observers. */
|
/*!
|
||||||
|
Performs a bus operation.
|
||||||
|
|
||||||
|
@param select The state of the card's select lines: indicates whether the Apple II
|
||||||
|
thinks this card should respond as though this were an IO access, a Device access,
|
||||||
|
or it thinks that the card shouldn't respond.
|
||||||
|
@param is_read @c true if this is a read cycle; @c false otherwise.
|
||||||
|
@param address The current value of the address bus.
|
||||||
|
@param value A pointer to the value of the data bus, not accounting input from cards.
|
||||||
|
If this is a read cycle, the card is permitted to replace this value with the value
|
||||||
|
output by the card, if any. If this is a write cycle, the card should only read
|
||||||
|
this value.
|
||||||
|
*/
|
||||||
|
virtual void perform_bus_operation(Select select, bool is_read, uint16_t address, uint8_t *value) = 0;
|
||||||
|
|
||||||
|
/*!
|
||||||
|
Returns the type of bus selects this card is actually interested in.
|
||||||
|
As specified, the default is that cards will ask to receive perform_bus_operation
|
||||||
|
only when their select lines are active.
|
||||||
|
|
||||||
|
There's a substantial caveat here: cards that register to receive @c None
|
||||||
|
will receive a perform_bus_operation every cycle. To reduce the number of
|
||||||
|
virtual method calls, they **will not** receive run_for. run_for will propagate
|
||||||
|
only to cards that register for IO and/or Device accesses only.
|
||||||
|
|
||||||
|
|
||||||
|
*/
|
||||||
|
int get_select_constraints() {
|
||||||
|
return select_constraints_;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*! Cards may supply a target for activity observation if desired. */
|
||||||
virtual void set_activity_observer(Activity::Observer *observer) {}
|
virtual void set_activity_observer(Activity::Observer *observer) {}
|
||||||
|
|
||||||
|
struct Delegate {
|
||||||
|
virtual void card_did_change_select_constraints(Card *card) = 0;
|
||||||
|
};
|
||||||
|
void set_delegate(Delegate *delegate) {
|
||||||
|
delegate_ = delegate;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected:
|
||||||
|
int select_constraints_ = IO | Device;
|
||||||
|
Delegate *delegate_ = nullptr;
|
||||||
|
void set_select_constraints(int constraints) {
|
||||||
|
if(constraints == select_constraints_) return;
|
||||||
|
select_constraints_ = constraints;
|
||||||
|
if(delegate_) delegate_->card_did_change_select_constraints(this);
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -19,19 +19,23 @@ DiskIICard::DiskIICard(const ROMMachine::ROMFetcher &rom_fetcher, bool is_16_sec
|
|||||||
});
|
});
|
||||||
boot_ = std::move(*roms[0]);
|
boot_ = std::move(*roms[0]);
|
||||||
diskii_.set_state_machine(*roms[1]);
|
diskii_.set_state_machine(*roms[1]);
|
||||||
|
set_select_constraints(None);
|
||||||
}
|
}
|
||||||
|
|
||||||
void DiskIICard::perform_bus_operation(CPU::MOS6502::BusOperation operation, uint16_t address, uint8_t *value) {
|
void DiskIICard::perform_bus_operation(Select select, bool is_read, uint16_t address, uint8_t *value) {
|
||||||
if(address < 0x100) {
|
|
||||||
if(isReadOperation(operation)) *value = boot_[address];
|
|
||||||
} else {
|
|
||||||
// TODO: data input really shouldn't happen only upon a write.
|
|
||||||
diskii_.set_data_input(*value);
|
diskii_.set_data_input(*value);
|
||||||
|
switch(select) {
|
||||||
|
default: break;
|
||||||
|
case IO: {
|
||||||
const int disk_value = diskii_.read_address(address);
|
const int disk_value = diskii_.read_address(address);
|
||||||
if(isReadOperation(operation)) {
|
if(is_read) {
|
||||||
if(disk_value != diskii_.DidNotLoad)
|
if(disk_value != diskii_.DidNotLoad)
|
||||||
*value = static_cast<uint8_t>(disk_value);
|
*value = static_cast<uint8_t>(disk_value);
|
||||||
}
|
}
|
||||||
|
} break;
|
||||||
|
case Device:
|
||||||
|
if(is_read) *value = boot_[address & 0xff];
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -14,6 +14,7 @@
|
|||||||
|
|
||||||
#include "../../Components/DiskII/DiskII.hpp"
|
#include "../../Components/DiskII/DiskII.hpp"
|
||||||
#include "../../Storage/Disk/Disk.hpp"
|
#include "../../Storage/Disk/Disk.hpp"
|
||||||
|
#include "../../ClockReceiver/Sleeper.hpp"
|
||||||
|
|
||||||
#include <cstdint>
|
#include <cstdint>
|
||||||
#include <memory>
|
#include <memory>
|
||||||
@ -25,8 +26,9 @@ class DiskIICard: public Card {
|
|||||||
public:
|
public:
|
||||||
DiskIICard(const ROMMachine::ROMFetcher &rom_fetcher, bool is_16_sector);
|
DiskIICard(const ROMMachine::ROMFetcher &rom_fetcher, bool is_16_sector);
|
||||||
|
|
||||||
void perform_bus_operation(CPU::MOS6502::BusOperation operation, uint16_t address, uint8_t *value) override;
|
void perform_bus_operation(Select select, bool is_read, uint16_t address, uint8_t *value) override;
|
||||||
void run_for(Cycles cycles, int stretches) override;
|
void run_for(Cycles cycles, int stretches) override;
|
||||||
|
|
||||||
void set_activity_observer(Activity::Observer *observer) override;
|
void set_activity_observer(Activity::Observer *observer) override;
|
||||||
|
|
||||||
void set_disk(const std::shared_ptr<Storage::Disk::Disk> &disk, int drive);
|
void set_disk(const std::shared_ptr<Storage::Disk::Disk> &disk, int drive);
|
||||||
|
Loading…
x
Reference in New Issue
Block a user