mirror of
https://github.com/TomHarte/CLK.git
synced 2024-11-26 08:49:37 +00:00
Added a lot of commenting to the CPU6502 definition, simplifying its construction. Added missing nullability modifier to CSElectron. Fixed bad-habit Objective-C style naming on the Electron's Interrupt enum.
This commit is contained in:
parent
4c16d34063
commit
de7218cdf0
@ -26,7 +26,6 @@ Machine::Machine() :
|
|||||||
{
|
{
|
||||||
_crt = new Outputs::CRT(228, 262, 1, 2);
|
_crt = new Outputs::CRT(228, 262, 1, 2);
|
||||||
memset(_collisions, 0xff, sizeof(_collisions));
|
memset(_collisions, 0xff, sizeof(_collisions));
|
||||||
setup6502();
|
|
||||||
set_reset_line(true);
|
set_reset_line(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -34,7 +34,6 @@ Machine::Machine() :
|
|||||||
memset(_roms[c], 0xff, 16384);
|
memset(_roms[c], 0xff, 16384);
|
||||||
|
|
||||||
_speaker.set_input_rate(125000);
|
_speaker.set_input_rate(125000);
|
||||||
setup6502();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Machine::~Machine()
|
Machine::~Machine()
|
||||||
@ -104,7 +103,7 @@ unsigned int Machine::perform_bus_operation(CPU6502::BusOperation operation, uin
|
|||||||
if(isReadOperation(operation))
|
if(isReadOperation(operation))
|
||||||
{
|
{
|
||||||
*value = (uint8_t)(_tape.dataRegister >> 2);
|
*value = (uint8_t)(_tape.dataRegister >> 2);
|
||||||
_interruptStatus &= ~InterruptTransmitDataEmpty;
|
_interruptStatus &= ~Interrupt::TransmitDataEmpty;
|
||||||
evaluate_interrupts();
|
evaluate_interrupts();
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
@ -118,9 +117,9 @@ unsigned int Machine::perform_bus_operation(CPU6502::BusOperation operation, uin
|
|||||||
const uint8_t interruptDisable = (*value)&0xf0;
|
const uint8_t interruptDisable = (*value)&0xf0;
|
||||||
if( interruptDisable )
|
if( interruptDisable )
|
||||||
{
|
{
|
||||||
if( interruptDisable&0x10 ) _interruptStatus &= ~InterruptDisplayEnd;
|
if( interruptDisable&0x10 ) _interruptStatus &= ~Interrupt::DisplayEnd;
|
||||||
if( interruptDisable&0x20 ) _interruptStatus &= ~InterruptRealTimeClock;
|
if( interruptDisable&0x20 ) _interruptStatus &= ~Interrupt::RealTimeClock;
|
||||||
if( interruptDisable&0x40 ) _interruptStatus &= ~InterruptHighToneDetect;
|
if( interruptDisable&0x40 ) _interruptStatus &= ~Interrupt::HighToneDetect;
|
||||||
evaluate_interrupts();
|
evaluate_interrupts();
|
||||||
// TODO: NMI (?)
|
// TODO: NMI (?)
|
||||||
}
|
}
|
||||||
@ -265,12 +264,12 @@ unsigned int Machine::perform_bus_operation(CPU6502::BusOperation operation, uin
|
|||||||
|
|
||||||
case 128*128:
|
case 128*128:
|
||||||
update_audio();
|
update_audio();
|
||||||
signal_interrupt(InterruptRealTimeClock);
|
signal_interrupt(Interrupt::RealTimeClock);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 284*128:
|
case 284*128:
|
||||||
update_audio();
|
update_audio();
|
||||||
signal_interrupt(InterruptDisplayEnd);
|
signal_interrupt(Interrupt::DisplayEnd);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case cycles_per_frame:
|
case cycles_per_frame:
|
||||||
@ -347,21 +346,21 @@ inline void Machine::push_tape_bit(uint16_t bit)
|
|||||||
|
|
||||||
if(_tape.bits_since_start == 7)
|
if(_tape.bits_since_start == 7)
|
||||||
{
|
{
|
||||||
_interruptStatus &= ~InterruptTransmitDataEmpty;
|
_interruptStatus &= ~Interrupt::TransmitDataEmpty;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
if((_tape.dataRegister&0x3) == 0x1)
|
if((_tape.dataRegister&0x3) == 0x1)
|
||||||
{
|
{
|
||||||
_interruptStatus |= InterruptTransmitDataEmpty;
|
_interruptStatus |= Interrupt::TransmitDataEmpty;
|
||||||
_tape.bits_since_start = 9;
|
_tape.bits_since_start = 9;
|
||||||
}
|
}
|
||||||
|
|
||||||
if(_tape.dataRegister == 0x3ff)
|
if(_tape.dataRegister == 0x3ff)
|
||||||
_interruptStatus |= InterruptHighToneDetect;
|
_interruptStatus |= Interrupt::HighToneDetect;
|
||||||
else
|
else
|
||||||
_interruptStatus &= ~InterruptHighToneDetect;
|
_interruptStatus &= ~Interrupt::HighToneDetect;
|
||||||
}
|
}
|
||||||
// printf(".");
|
// printf(".");
|
||||||
|
|
||||||
|
@ -14,7 +14,6 @@
|
|||||||
#include "../../Outputs/Speaker.hpp"
|
#include "../../Outputs/Speaker.hpp"
|
||||||
#include "../../Storage/Tape/Tape.hpp"
|
#include "../../Storage/Tape/Tape.hpp"
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
#include "Atari2600Inputs.h"
|
|
||||||
|
|
||||||
namespace Electron {
|
namespace Electron {
|
||||||
|
|
||||||
@ -32,11 +31,11 @@ enum ROMSlot: uint8_t {
|
|||||||
};
|
};
|
||||||
|
|
||||||
enum Interrupt: uint8_t {
|
enum Interrupt: uint8_t {
|
||||||
InterruptDisplayEnd = 0x04,
|
DisplayEnd = 0x04,
|
||||||
InterruptRealTimeClock = 0x08,
|
RealTimeClock = 0x08,
|
||||||
InterruptTransmitDataEmpty = 0x10,
|
TransmitDataEmpty = 0x10,
|
||||||
InterruptReceiveDataFull = 0x20,
|
ReceiveDataFull = 0x20,
|
||||||
InterruptHighToneDetect = 0x40
|
HighToneDetect = 0x40
|
||||||
};
|
};
|
||||||
|
|
||||||
enum Key: uint16_t {
|
enum Key: uint16_t {
|
||||||
@ -58,6 +57,12 @@ enum Key: uint16_t {
|
|||||||
KeyBreak = 0xffff
|
KeyBreak = 0xffff
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/*!
|
||||||
|
@abstract Represents an Acorn Electron.
|
||||||
|
|
||||||
|
@discussion An instance of Electron::Machine represents the current state of an
|
||||||
|
Acorn Electron.
|
||||||
|
*/
|
||||||
class Machine: public CPU6502::Processor<Machine> {
|
class Machine: public CPU6502::Processor<Machine> {
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
@ -14,7 +14,7 @@
|
|||||||
- (void)setOSROM:(nonnull NSData *)rom;
|
- (void)setOSROM:(nonnull NSData *)rom;
|
||||||
- (void)setBASICROM:(nonnull NSData *)rom;
|
- (void)setBASICROM:(nonnull NSData *)rom;
|
||||||
- (void)setROM:(nonnull NSData *)rom slot:(int)slot;
|
- (void)setROM:(nonnull NSData *)rom slot:(int)slot;
|
||||||
- (BOOL)openUEFAtURL:(NSURL *)URL;
|
- (BOOL)openUEFAtURL:(nonnull NSURL *)URL;
|
||||||
|
|
||||||
- (void)setKey:(uint16_t)key isPressed:(BOOL)isPressed;
|
- (void)setKey:(uint16_t)key isPressed:(BOOL)isPressed;
|
||||||
|
|
||||||
|
@ -25,7 +25,6 @@ enum Register {
|
|||||||
S
|
S
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
enum Flag {
|
enum Flag {
|
||||||
Sign = 0x80,
|
Sign = 0x80,
|
||||||
Overflow = 0x40,
|
Overflow = 0x40,
|
||||||
@ -41,10 +40,24 @@ enum BusOperation {
|
|||||||
Read, ReadOpcode, Write, Ready, None
|
Read, ReadOpcode, Write, Ready, None
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/*!
|
||||||
|
Evaluates to `true` if the operation is a read; `false` if it is a write.
|
||||||
|
*/
|
||||||
#define isReadOperation(v) (v == CPU6502::BusOperation::Read || v == CPU6502::BusOperation::ReadOpcode)
|
#define isReadOperation(v) (v == CPU6502::BusOperation::Read || v == CPU6502::BusOperation::ReadOpcode)
|
||||||
|
|
||||||
|
/*!
|
||||||
|
An opcode that is guaranteed to cause the CPU to jam.
|
||||||
|
*/
|
||||||
extern const uint8_t JamOpcode;
|
extern const uint8_t JamOpcode;
|
||||||
|
|
||||||
|
/*!
|
||||||
|
@abstact An abstract base class for emulation of a 6502 processor.
|
||||||
|
|
||||||
|
@discussion Subclasses should implement @c perform_bus_operation(BusOperation operation, uint16_t address, uint8_t *value) in
|
||||||
|
order to provde the bus on which the 6502 operates. Additional functionality can be provided by the host machine by providing
|
||||||
|
a jam handler and inserting jam opcodes where appropriate; that will cause call outs when the program counter reaches those
|
||||||
|
addresses. @c return_from_subroutine can be used to exit from a jammed state.
|
||||||
|
*/
|
||||||
template <class T> class Processor {
|
template <class T> class Processor {
|
||||||
public:
|
public:
|
||||||
|
|
||||||
@ -384,18 +397,6 @@ template <class T> class Processor {
|
|||||||
bool _nmi_line_is_enabled;
|
bool _nmi_line_is_enabled;
|
||||||
bool _ready_is_active;
|
bool _ready_is_active;
|
||||||
|
|
||||||
public:
|
|
||||||
Processor() :
|
|
||||||
_scheduleProgramsReadPointer(0),
|
|
||||||
_scheduleProgramsWritePointer(0),
|
|
||||||
_is_jammed(false),
|
|
||||||
_jam_handler(nullptr),
|
|
||||||
_cycles_left_to_run(0),
|
|
||||||
_ready_line_is_enabled(false),
|
|
||||||
_ready_is_active(false),
|
|
||||||
_scheduledPrograms{nullptr, nullptr, nullptr, nullptr}
|
|
||||||
{}
|
|
||||||
|
|
||||||
const MicroOp *get_reset_program() {
|
const MicroOp *get_reset_program() {
|
||||||
static const MicroOp reset[] = {
|
static const MicroOp reset[] = {
|
||||||
CycleFetchOperand,
|
CycleFetchOperand,
|
||||||
@ -423,6 +424,41 @@ template <class T> class Processor {
|
|||||||
return reset;
|
return reset;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public:
|
||||||
|
Processor() :
|
||||||
|
_scheduleProgramsReadPointer(0),
|
||||||
|
_scheduleProgramsWritePointer(0),
|
||||||
|
_is_jammed(false),
|
||||||
|
_jam_handler(nullptr),
|
||||||
|
_cycles_left_to_run(0),
|
||||||
|
_ready_line_is_enabled(false),
|
||||||
|
_ready_is_active(false),
|
||||||
|
_scheduledPrograms{nullptr, nullptr, nullptr, nullptr},
|
||||||
|
_interruptFlag(Flag::Interrupt),
|
||||||
|
_s(0),
|
||||||
|
_nextBusOperation(BusOperation::None)
|
||||||
|
|
||||||
|
{
|
||||||
|
// only the interrupt flag is defined upon reset but get_flags isn't going to
|
||||||
|
// mask the other flags so we need to do that, at least
|
||||||
|
_carryFlag &= Flag::Carry;
|
||||||
|
_decimalFlag &= Flag::Decimal;
|
||||||
|
_overflowFlag &= Flag::Overflow;
|
||||||
|
|
||||||
|
// TODO: is this accurate? It feels more likely that a CPU would need to wait
|
||||||
|
// on an explicit reset command, since the relative startup times of different
|
||||||
|
// components from power on would be a bit unpredictable.
|
||||||
|
schedule_program(get_reset_program());
|
||||||
|
}
|
||||||
|
|
||||||
|
/*!
|
||||||
|
Runs the 6502 for a supplied number of cycles.
|
||||||
|
|
||||||
|
@discussion Subclasses must implement @c perform_bus_operation(BusOperation operation, uint16_t address, uint8_t *value) .
|
||||||
|
The 6502 will call that method for all bus accesses. The 6502 is guaranteed to perform one bus operation call per cycle.
|
||||||
|
|
||||||
|
@param number_of_cycles The number of cycles to run the 6502 for.
|
||||||
|
*/
|
||||||
void run_for_cycles(int number_of_cycles)
|
void run_for_cycles(int number_of_cycles)
|
||||||
{
|
{
|
||||||
static const MicroOp doBranch[] = {
|
static const MicroOp doBranch[] = {
|
||||||
@ -932,6 +968,14 @@ template <class T> class Processor {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*!
|
||||||
|
Gets the value of a register.
|
||||||
|
|
||||||
|
@see set_value_of_register
|
||||||
|
|
||||||
|
@param r The register to set.
|
||||||
|
@returns The value of the register. 8-bit registers will be returned as unsigned.
|
||||||
|
*/
|
||||||
uint16_t get_value_of_register(Register r)
|
uint16_t get_value_of_register(Register r)
|
||||||
{
|
{
|
||||||
switch (r) {
|
switch (r) {
|
||||||
@ -947,6 +991,14 @@ template <class T> class Processor {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*!
|
||||||
|
Sets the value of a register.
|
||||||
|
|
||||||
|
@see get_value_of_register
|
||||||
|
|
||||||
|
@param r The register to set.
|
||||||
|
@param value The value to set. If the register is only 8 bit, the value will be truncated.
|
||||||
|
*/
|
||||||
void set_value_of_register(Register r, uint16_t value)
|
void set_value_of_register(Register r, uint16_t value)
|
||||||
{
|
{
|
||||||
switch (r) {
|
switch (r) {
|
||||||
@ -961,23 +1013,10 @@ template <class T> class Processor {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void setup6502()
|
/*!
|
||||||
{
|
Interrupts current execution flow to perform an RTS and, if the 6502 is currently jammed,
|
||||||
// only the interrupt flag is defined upon reset but get_flags isn't going to
|
to unjam it.
|
||||||
// mask the other flags so we need to do that, at least
|
*/
|
||||||
_interruptFlag = Flag::Interrupt;
|
|
||||||
_carryFlag &= Flag::Carry;
|
|
||||||
_decimalFlag &= Flag::Decimal;
|
|
||||||
_overflowFlag &= Flag::Overflow;
|
|
||||||
_s = 0;
|
|
||||||
_nextBusOperation = BusOperation::None;
|
|
||||||
|
|
||||||
// TODO: is this accurate? It feels more likely that a CPU would need to wait
|
|
||||||
// on an explicit reset command, since the relative startup times of different
|
|
||||||
// components from power on would be a bit unpredictable.
|
|
||||||
schedule_program(get_reset_program());
|
|
||||||
}
|
|
||||||
|
|
||||||
void return_from_subroutine()
|
void return_from_subroutine()
|
||||||
{
|
{
|
||||||
_s++;
|
_s++;
|
||||||
@ -990,6 +1029,11 @@ template <class T> class Processor {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*!
|
||||||
|
Sets the current level of the RDY line.
|
||||||
|
|
||||||
|
@param active @c true if the line is logically active; @c false otherwise.
|
||||||
|
*/
|
||||||
void set_ready_line(bool active)
|
void set_ready_line(bool active)
|
||||||
{
|
{
|
||||||
if(active)
|
if(active)
|
||||||
@ -1001,26 +1045,54 @@ template <class T> class Processor {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*!
|
||||||
|
Sets the current level of the RST line.
|
||||||
|
|
||||||
|
@param active @c true if the line is logically active; @c false otherwise.
|
||||||
|
*/
|
||||||
void set_reset_line(bool active)
|
void set_reset_line(bool active)
|
||||||
{
|
{
|
||||||
_reset_line_is_enabled = active;
|
_reset_line_is_enabled = active;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*!
|
||||||
|
Sets the current level of the IRQ line.
|
||||||
|
|
||||||
|
@param active @c true if the line is logically active; @c false otherwise.
|
||||||
|
*/
|
||||||
void set_irq_line(bool active)
|
void set_irq_line(bool active)
|
||||||
{
|
{
|
||||||
_irq_line_is_enabled = active;
|
_irq_line_is_enabled = active;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*!
|
||||||
|
Sets the current level of the NMI line.
|
||||||
|
|
||||||
|
@param active `true` if the line is logically active; `false` otherwise.
|
||||||
|
*/
|
||||||
void set_nmi_line(bool active)
|
void set_nmi_line(bool active)
|
||||||
{
|
{
|
||||||
|
// TODO: NMI is edge triggered, not level, and in any case _nmi_line_is_enabled
|
||||||
|
// is not honoured elsewhere. So NMI is yet to be implemented.
|
||||||
_nmi_line_is_enabled = active;
|
_nmi_line_is_enabled = active;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*!
|
||||||
|
Queries whether the 6502 is now 'jammed'; i.e. has entered an invalid state
|
||||||
|
such that it will not of itself perform any more meaningful processing.
|
||||||
|
|
||||||
|
@returns @c true if the 6502 is jammed; @c false otherwise.
|
||||||
|
*/
|
||||||
bool is_jammed()
|
bool is_jammed()
|
||||||
{
|
{
|
||||||
return _is_jammed;
|
return _is_jammed;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*!
|
||||||
|
Installs a jam handler. Jam handlers are notified if a running 6502 jams.
|
||||||
|
|
||||||
|
@param handler The class instance that will be this 6502's jam handler from now on.
|
||||||
|
*/
|
||||||
void set_jam_handler(JamHandler *handler)
|
void set_jam_handler(JamHandler *handler)
|
||||||
{
|
{
|
||||||
_jam_handler = handler;
|
_jam_handler = handler;
|
||||||
|
Loading…
Reference in New Issue
Block a user