1
0
mirror of https://github.com/TomHarte/CLK.git synced 2024-11-09 01:10:10 +00:00
CLK/Machines/Apple/AppleII/Card.hpp
2024-02-15 13:26:03 -05:00

121 lines
4.6 KiB
C++

//
// Card.h
// Clock Signal
//
// Created by Thomas Harte on 23/04/2018.
// Copyright 2018 Thomas Harte. All rights reserved.
//
#pragma once
#include "../../../Processors/6502/6502.hpp"
#include "../../../ClockReceiver/ClockReceiver.hpp"
#include "../../../Activity/Observer.hpp"
namespace Apple::II {
/*!
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 {
public:
virtual ~Card() {}
enum Select: int {
None = 0, // No select line is active.
Device = 1 << 0, // Device select ('DEVSEL') is active; i.e. access is in range $C0x0 to $C0xf.
IO = 1 << 1, // IO select ('IOSEL') is active; i.e. access is in range $Cx00 to $Cxff.
C8Region = 1 << 2, // Access is to the region $c800 to $cfff, was preceded by at least
// one Device access to this card, and has not yet been followed up
// by an access to $cfff. IOSTRB on original hardware.
};
/*!
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 @c perform_bus_operation every cycle and should use that for time keeping.
*/
virtual void run_for([[maybe_unused]] Cycles cycles, [[maybe_unused]] int stretches) {}
/// Requests a flush of any pending audio or video output.
virtual void flush() {}
/*!
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() const {
return select_constraints_;
}
/*! Cards may supply a target for activity observation if desired. */
virtual void set_activity_observer([[maybe_unused]] Activity::Observer *observer) {}
/// @returns The current semantic NMI line output of this card.
virtual bool nmi() { return false; }
/// @returns The current semantic IRQ line output of this card.
virtual bool irq() { return false; }
struct Delegate {
virtual void card_did_change_select_constraints(Card *card) = 0;
virtual void card_did_change_interrupt_flags(Card *card) = 0;
};
void set_delegate(Delegate *delegate) {
delegate_ = delegate;
}
protected:
int select_constraints_ = Device | IO;
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);
}
};
}