mirror of
https://github.com/TomHarte/CLK.git
synced 2024-12-26 09:29:45 +00:00
Finally started on generalising the C++ stuff so as to be able to be able to get a working audio binding on the OS-specific side without further repetition by factoring an appropriate protocol out from the Electron and sketching out the correct speaker class for the Atari. Added a method to ask it what a good output frequency would be.
This commit is contained in:
parent
3fc80ea01f
commit
5c4f35e13f
@ -71,6 +71,8 @@ void Machine::setup_output(float aspect_ratio)
|
|||||||
"return (float(y) / 14.0) * (1.0 - amplitude) + step(1, iPhase) * amplitude * cos(phase + phaseOffset);"
|
"return (float(y) / 14.0) * (1.0 - amplitude) + step(1, iPhase) * amplitude * cos(phase + phaseOffset);"
|
||||||
"}");
|
"}");
|
||||||
_crt->set_output_device(Outputs::CRT::Television);
|
_crt->set_output_device(Outputs::CRT::Television);
|
||||||
|
|
||||||
|
_speaker.set_input_rate(2 * 263 * 60);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Machine::switch_region()
|
void Machine::switch_region()
|
||||||
@ -89,6 +91,8 @@ void Machine::switch_region()
|
|||||||
"return (float(y) / 14.0) * (1.0 - amplitude) + step(4, (iPhase + 2u) & 15u) * amplitude * cos(phase + phaseOffset);"
|
"return (float(y) / 14.0) * (1.0 - amplitude) + step(4, (iPhase + 2u) & 15u) * amplitude * cos(phase + phaseOffset);"
|
||||||
"}");
|
"}");
|
||||||
_crt->set_new_timing(228, 312, Outputs::CRT::ColourSpace::YUV, 228, 1);
|
_crt->set_new_timing(228, 312, Outputs::CRT::ColourSpace::YUV, 228, 1);
|
||||||
|
|
||||||
|
_speaker.set_input_rate(2 * 312 * 50);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Machine::close_output()
|
void Machine::close_output()
|
||||||
@ -445,6 +449,10 @@ unsigned int Machine::perform_bus_operation(CPU6502::BusOperation operation, uin
|
|||||||
returnValue &= _ram[address&0x7f];
|
returnValue &= _ram[address&0x7f];
|
||||||
} else {
|
} else {
|
||||||
_ram[address&0x7f] = *value;
|
_ram[address&0x7f] = *value;
|
||||||
|
// if((address&0x7f) == (0x9a&0x7f))
|
||||||
|
// {
|
||||||
|
// printf("[9a] <- %02x\n", *value);
|
||||||
|
// }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -585,7 +593,10 @@ unsigned int Machine::perform_bus_operation(CPU6502::BusOperation operation, uin
|
|||||||
_playerGraphics[1][index^1] = _playerGraphics[0][index^1];
|
_playerGraphics[1][index^1] = _playerGraphics[0][index^1];
|
||||||
} break;
|
} break;
|
||||||
case 0x1d:
|
case 0x1d:
|
||||||
case 0x1e: _missileGraphicsEnable[decodedAddress - 0x1d] = ((*value) >> 1)&1; break;
|
case 0x1e:
|
||||||
|
_missileGraphicsEnable[decodedAddress - 0x1d] = ((*value) >> 1)&1;
|
||||||
|
// printf("e:%02x <- %c\n", decodedAddress - 0x1d, ((*value)&1) ? 'E' : '-');
|
||||||
|
break;
|
||||||
case 0x1f:
|
case 0x1f:
|
||||||
_ballGraphicsEnable[0] = ((*value) >> 1)&1;
|
_ballGraphicsEnable[0] = ((*value) >> 1)&1;
|
||||||
break;
|
break;
|
||||||
@ -623,15 +634,17 @@ unsigned int Machine::perform_bus_operation(CPU6502::BusOperation operation, uin
|
|||||||
_objectCounter[_objectCounterPointer][index + 2].count = (_objectCounter[_objectCounterPointer][index + 2].count + extra_offset)%160;
|
_objectCounter[_objectCounterPointer][index + 2].count = (_objectCounter[_objectCounterPointer][index + 2].count + extra_offset)%160;
|
||||||
}
|
}
|
||||||
_missileGraphicsReset[index] = !!((*value) & 0x02);
|
_missileGraphicsReset[index] = !!((*value) & 0x02);
|
||||||
|
// printf("r:%02x <- %c\n", decodedAddress - 0x28, ((*value)&2) ? 'R' : '-');
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 0x2a:
|
case 0x2a: {
|
||||||
// justification for +5: "we need to wait at least 71 [clocks] before the HMOVE operation is complete";
|
// justification for +5: "we need to wait at least 71 [clocks] before the HMOVE operation is complete";
|
||||||
// which will take 16*4 + 2 = 66 cycles from the first compare, implying the first compare must be
|
// which will take 16*4 + 2 = 66 cycles from the first compare, implying the first compare must be
|
||||||
// in five cycles from now
|
// in five cycles from now
|
||||||
|
// int start_pause = ((_horizontalTimer + 3)&3) + 4;
|
||||||
_upcomingEvents[(_upcomingEventsPointer + 5)%number_of_upcoming_events].updates |= Event::Action::HMoveSetup;
|
_upcomingEvents[(_upcomingEventsPointer + 5)%number_of_upcoming_events].updates |= Event::Action::HMoveSetup;
|
||||||
break;
|
} break;
|
||||||
case 0x2b:
|
case 0x2b:
|
||||||
_objectMotion[0] =
|
_objectMotion[0] =
|
||||||
_objectMotion[1] =
|
_objectMotion[1] =
|
||||||
|
@ -10,31 +10,52 @@
|
|||||||
#define Atari2600_cpp
|
#define Atari2600_cpp
|
||||||
|
|
||||||
#include "../../Processors/6502/CPU6502.hpp"
|
#include "../../Processors/6502/CPU6502.hpp"
|
||||||
#include "../../Outputs/CRT/CRT.hpp"
|
#include "../CRTMachine.hpp"
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
#include "Atari2600Inputs.h"
|
#include "Atari2600Inputs.h"
|
||||||
|
|
||||||
namespace Atari2600 {
|
namespace Atari2600 {
|
||||||
|
|
||||||
const unsigned int number_of_upcoming_events = 6;
|
const unsigned int number_of_upcoming_events = 16;
|
||||||
const unsigned int number_of_recorded_counters = 7;
|
const unsigned int number_of_recorded_counters = 7;
|
||||||
|
|
||||||
class Machine: public CPU6502::Processor<Machine> {
|
class Speaker: public ::Outputs::Filter<Speaker> {
|
||||||
|
public:
|
||||||
|
void set_volume(int channel, uint8_t volume);
|
||||||
|
void set_divider(int channel, uint8_t divider);
|
||||||
|
void set_control(int channel, uint8_t control);
|
||||||
|
|
||||||
|
void get_samples(unsigned int number_of_samples, int16_t *target);
|
||||||
|
void skip_samples(unsigned int number_of_samples);
|
||||||
|
|
||||||
|
private:
|
||||||
|
uint8_t _volume[2];
|
||||||
|
uint8_t _divider[2];
|
||||||
|
uint8_t _control[2];
|
||||||
|
int _shift_counters[2];
|
||||||
|
};
|
||||||
|
|
||||||
|
class Machine: public CPU6502::Processor<Machine>, CRTMachine::Machine {
|
||||||
|
|
||||||
public:
|
public:
|
||||||
Machine();
|
Machine();
|
||||||
~Machine();
|
~Machine();
|
||||||
|
|
||||||
unsigned int perform_bus_operation(CPU6502::BusOperation operation, uint16_t address, uint8_t *value);
|
|
||||||
|
|
||||||
void set_rom(size_t length, const uint8_t *data);
|
void set_rom(size_t length, const uint8_t *data);
|
||||||
void switch_region();
|
void switch_region();
|
||||||
|
|
||||||
void set_digital_input(Atari2600DigitalInput input, bool state);
|
void set_digital_input(Atari2600DigitalInput input, bool state);
|
||||||
|
|
||||||
Outputs::CRT::CRT *get_crt() { return _crt; }
|
// to satisfy CPU6502::Processor
|
||||||
void setup_output(float aspect_ratio);
|
unsigned int perform_bus_operation(CPU6502::BusOperation operation, uint16_t address, uint8_t *value);
|
||||||
void close_output();
|
void synchronise() {}
|
||||||
|
|
||||||
|
// to satisfy CRTMachine::Machine
|
||||||
|
virtual void setup_output(float aspect_ratio);
|
||||||
|
virtual void close_output();
|
||||||
|
virtual Outputs::CRT::CRT *get_crt() { return _crt; }
|
||||||
|
virtual Outputs::Speaker *get_speaker() { return &_speaker; }
|
||||||
|
virtual void run_for_cycles(int number_of_cycles) { CPU6502::Processor<Machine>::run_for_cycles(number_of_cycles); }
|
||||||
|
|
||||||
private:
|
private:
|
||||||
uint8_t *_rom, *_romPages[4], _ram[128];
|
uint8_t *_rom, *_romPages[4], _ram[128];
|
||||||
@ -137,7 +158,10 @@ class Machine: public CPU6502::Processor<Machine> {
|
|||||||
void output_pixels(unsigned int count);
|
void output_pixels(unsigned int count);
|
||||||
uint8_t get_output_pixel();
|
uint8_t get_output_pixel();
|
||||||
void update_timers(int mask);
|
void update_timers(int mask);
|
||||||
|
|
||||||
|
// Outputs
|
||||||
Outputs::CRT::CRT *_crt;
|
Outputs::CRT::CRT *_crt;
|
||||||
|
Speaker _speaker;
|
||||||
|
|
||||||
// latched output state
|
// latched output state
|
||||||
unsigned int _lastOutputStateDuration;
|
unsigned int _lastOutputStateDuration;
|
||||||
|
30
Machines/CRTMachine.hpp
Normal file
30
Machines/CRTMachine.hpp
Normal file
@ -0,0 +1,30 @@
|
|||||||
|
//
|
||||||
|
// CRTMachine.hpp
|
||||||
|
// Clock Signal
|
||||||
|
//
|
||||||
|
// Created by Thomas Harte on 31/05/2016.
|
||||||
|
// Copyright © 2016 Thomas Harte. All rights reserved.
|
||||||
|
//
|
||||||
|
|
||||||
|
#ifndef CRTMachine_hpp
|
||||||
|
#define CRTMachine_hpp
|
||||||
|
|
||||||
|
#include "../Outputs/CRT/CRT.hpp"
|
||||||
|
#include "../Outputs/Speaker.hpp"
|
||||||
|
|
||||||
|
namespace CRTMachine {
|
||||||
|
|
||||||
|
class Machine {
|
||||||
|
public:
|
||||||
|
virtual void setup_output(float aspect_ratio) = 0;
|
||||||
|
virtual void close_output() = 0;
|
||||||
|
|
||||||
|
virtual Outputs::CRT::CRT *get_crt() = 0;
|
||||||
|
virtual Outputs::Speaker *get_speaker() = 0;
|
||||||
|
|
||||||
|
virtual void run_for_cycles(int number_of_cycles) = 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif /* CRTMachine_hpp */
|
@ -450,7 +450,7 @@ unsigned int Machine::perform_bus_operation(CPU6502::BusOperation operation, uin
|
|||||||
return cycles;
|
return cycles;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Machine::update_output()
|
void Machine::synchronise()
|
||||||
{
|
{
|
||||||
update_display();
|
update_display();
|
||||||
update_audio();
|
update_audio();
|
||||||
|
@ -10,9 +10,8 @@
|
|||||||
#define Electron_hpp
|
#define Electron_hpp
|
||||||
|
|
||||||
#include "../../Processors/6502/CPU6502.hpp"
|
#include "../../Processors/6502/CPU6502.hpp"
|
||||||
#include "../../Outputs/CRT/CRT.hpp"
|
|
||||||
#include "../../Outputs/Speaker.hpp"
|
|
||||||
#include "../../Storage/Tape/Tape.hpp"
|
#include "../../Storage/Tape/Tape.hpp"
|
||||||
|
#include "../CRTMachine.hpp"
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
|
|
||||||
namespace Electron {
|
namespace Electron {
|
||||||
@ -142,30 +141,34 @@ class Speaker: public ::Outputs::Filter<Speaker> {
|
|||||||
@discussion An instance of Electron::Machine represents the current state of an
|
@discussion An instance of Electron::Machine represents the current state of an
|
||||||
Acorn Electron.
|
Acorn Electron.
|
||||||
*/
|
*/
|
||||||
class Machine: public CPU6502::Processor<Machine>, Tape::Delegate {
|
class Machine: public CPU6502::Processor<Machine>, Tape::Delegate, CRTMachine::Machine {
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
|
||||||
Machine();
|
Machine();
|
||||||
|
|
||||||
unsigned int perform_bus_operation(CPU6502::BusOperation operation, uint16_t address, uint8_t *value);
|
|
||||||
|
|
||||||
void set_rom(ROMSlot slot, size_t length, const uint8_t *data);
|
void set_rom(ROMSlot slot, size_t length, const uint8_t *data);
|
||||||
void set_tape(std::shared_ptr<Storage::Tape> tape);
|
void set_tape(std::shared_ptr<Storage::Tape> tape);
|
||||||
|
|
||||||
void set_key_state(Key key, bool isPressed);
|
void set_key_state(Key key, bool isPressed);
|
||||||
void clear_all_keys();
|
void clear_all_keys();
|
||||||
|
|
||||||
void setup_output(float aspect_ratio);
|
|
||||||
void close_output();
|
|
||||||
Outputs::CRT::CRT *get_crt() { return _crt.get(); }
|
|
||||||
Outputs::Speaker *get_speaker() { return &_speaker; }
|
|
||||||
|
|
||||||
virtual void tape_did_change_interrupt_status(Tape *tape);
|
|
||||||
|
|
||||||
void update_output();
|
|
||||||
inline void set_use_fast_tape_hack(bool activate) { _use_fast_tape_hack = activate; }
|
inline void set_use_fast_tape_hack(bool activate) { _use_fast_tape_hack = activate; }
|
||||||
|
|
||||||
|
// to satisfy CPU6502::Processor
|
||||||
|
unsigned int perform_bus_operation(CPU6502::BusOperation operation, uint16_t address, uint8_t *value);
|
||||||
|
void synchronise();
|
||||||
|
|
||||||
|
// to satisfy CRTMachine::Machine
|
||||||
|
virtual void setup_output(float aspect_ratio);
|
||||||
|
virtual void close_output();
|
||||||
|
virtual Outputs::CRT::CRT *get_crt() { return _crt.get(); }
|
||||||
|
virtual Outputs::Speaker *get_speaker() { return &_speaker; }
|
||||||
|
virtual void run_for_cycles(int number_of_cycles) { CPU6502::Processor<Machine>::run_for_cycles(number_of_cycles); }
|
||||||
|
|
||||||
|
// to satisfy Tape::Delegate
|
||||||
|
virtual void tape_did_change_interrupt_status(Tape *tape);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
|
||||||
inline void update_display();
|
inline void update_display();
|
||||||
@ -215,12 +218,12 @@ class Machine: public CPU6502::Processor<Machine>, Tape::Delegate {
|
|||||||
uint8_t *_current_output_target, *_initial_output_target;
|
uint8_t *_current_output_target, *_initial_output_target;
|
||||||
unsigned int _current_output_divider;
|
unsigned int _current_output_divider;
|
||||||
|
|
||||||
// Tape.
|
// Tape
|
||||||
Tape _tape;
|
Tape _tape;
|
||||||
bool _use_fast_tape_hack;
|
bool _use_fast_tape_hack;
|
||||||
bool _fast_load_is_in_data;
|
bool _fast_load_is_in_data;
|
||||||
|
|
||||||
// Outputs.
|
// Outputs
|
||||||
std::unique_ptr<Outputs::CRT::CRT> _crt;
|
std::unique_ptr<Outputs::CRT::CRT> _crt;
|
||||||
Speaker _speaker;
|
Speaker _speaker;
|
||||||
};
|
};
|
||||||
|
@ -334,6 +334,7 @@
|
|||||||
/* End PBXContainerItemProxy section */
|
/* End PBXContainerItemProxy section */
|
||||||
|
|
||||||
/* Begin PBXFileReference section */
|
/* Begin PBXFileReference section */
|
||||||
|
4B046DC31CFE651500E9E45E /* CRTMachine.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = CRTMachine.hpp; sourceTree = "<group>"; };
|
||||||
4B0B6E121C9DBD5D00FFB60D /* CRTConstants.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = CRTConstants.hpp; sourceTree = "<group>"; };
|
4B0B6E121C9DBD5D00FFB60D /* CRTConstants.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = CRTConstants.hpp; sourceTree = "<group>"; };
|
||||||
4B0CCC421C62D0B3001CAC5F /* CRT.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = CRT.cpp; sourceTree = "<group>"; };
|
4B0CCC421C62D0B3001CAC5F /* CRT.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = CRT.cpp; sourceTree = "<group>"; };
|
||||||
4B0CCC431C62D0B3001CAC5F /* CRT.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = CRT.hpp; sourceTree = "<group>"; };
|
4B0CCC431C62D0B3001CAC5F /* CRT.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = CRT.hpp; sourceTree = "<group>"; };
|
||||||
@ -1192,6 +1193,7 @@
|
|||||||
children = (
|
children = (
|
||||||
4B2E2D961C3A06EC00138695 /* Atari2600 */,
|
4B2E2D961C3A06EC00138695 /* Atari2600 */,
|
||||||
4B2E2D9E1C3A070900138695 /* Electron */,
|
4B2E2D9E1C3A070900138695 /* Electron */,
|
||||||
|
4B046DC31CFE651500E9E45E /* CRTMachine.hpp */,
|
||||||
);
|
);
|
||||||
name = Machines;
|
name = Machines;
|
||||||
path = ../../Machines;
|
path = ../../Machines;
|
||||||
|
@ -19,7 +19,6 @@
|
|||||||
- (void)runForNumberOfCycles:(int)numberOfCycles {
|
- (void)runForNumberOfCycles:(int)numberOfCycles {
|
||||||
@synchronized(self) {
|
@synchronized(self) {
|
||||||
_electron.run_for_cycles(numberOfCycles);
|
_electron.run_for_cycles(numberOfCycles);
|
||||||
_electron.update_output();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -24,6 +24,18 @@ class Speaker {
|
|||||||
virtual void speaker_did_complete_samples(Speaker *speaker, const int16_t *buffer, int buffer_size) = 0;
|
virtual void speaker_did_complete_samples(Speaker *speaker, const int16_t *buffer, int buffer_size) = 0;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
int get_ideal_clock_rate_in_range(int minimum, int maximum)
|
||||||
|
{
|
||||||
|
// return exactly the input rate if possible
|
||||||
|
if(_input_cycles_per_second >= minimum && _input_cycles_per_second <= maximum) return _input_cycles_per_second;
|
||||||
|
|
||||||
|
// if the input rate is lower, return the minimum
|
||||||
|
if(_input_cycles_per_second < minimum) return minimum;
|
||||||
|
|
||||||
|
// otherwise, return the maximum
|
||||||
|
return maximum;
|
||||||
|
}
|
||||||
|
|
||||||
void set_output_rate(int cycles_per_second, int buffer_size)
|
void set_output_rate(int cycles_per_second, int buffer_size)
|
||||||
{
|
{
|
||||||
_output_cycles_per_second = cycles_per_second;
|
_output_cycles_per_second = cycles_per_second;
|
||||||
|
@ -67,9 +67,12 @@ extern const uint8_t JamOpcode;
|
|||||||
@abstact An abstract base class for emulation of a 6502 processor via the curiously recurring template pattern/f-bounded polymorphism.
|
@abstact An abstract base class for emulation of a 6502 processor via the curiously recurring template pattern/f-bounded polymorphism.
|
||||||
|
|
||||||
@discussion Subclasses should implement @c perform_bus_operation(BusOperation operation, uint16_t address, uint8_t *value) in
|
@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
|
order to provide the bus on which the 6502 operates and @c synchronise(), which is called upon completion of a continuous run
|
||||||
a jam handler and inserting jam opcodes where appropriate; that will cause call outs when the program counter reaches those
|
of cycles to allow a subclass to bring any on-demand activities up to date.
|
||||||
addresses. @c return_from_subroutine can be used to exit from a jammed state.
|
|
||||||
|
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:
|
||||||
@ -597,7 +600,7 @@ template <class T> class Processor {
|
|||||||
|
|
||||||
case CycleFetchOperation: {
|
case CycleFetchOperation: {
|
||||||
_lastOperationPC = _pc;
|
_lastOperationPC = _pc;
|
||||||
// printf("%04x\n", _pc.full);
|
// printf("%04x x:%02x\n", _pc.full, _x);
|
||||||
_pc.full++;
|
_pc.full++;
|
||||||
read_op(_operation, _lastOperationPC.full);
|
read_op(_operation, _lastOperationPC.full);
|
||||||
|
|
||||||
@ -1041,15 +1044,17 @@ template <class T> class Processor {
|
|||||||
_ready_is_active = true;
|
_ready_is_active = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
_cycles_left_to_run = number_of_cycles;
|
|
||||||
_scheduleProgramsReadPointer = scheduleProgramsReadPointer;
|
|
||||||
_scheduleProgramProgramCounter = scheduleProgramProgramCounter;
|
|
||||||
_nextAddress = nextAddress;
|
|
||||||
_nextBusOperation = nextBusOperation;
|
|
||||||
_busAddress = busAddress;
|
|
||||||
_busValue = busValue;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
_cycles_left_to_run = number_of_cycles;
|
||||||
|
_scheduleProgramsReadPointer = scheduleProgramsReadPointer;
|
||||||
|
_scheduleProgramProgramCounter = scheduleProgramProgramCounter;
|
||||||
|
_nextAddress = nextAddress;
|
||||||
|
_nextBusOperation = nextBusOperation;
|
||||||
|
_busAddress = busAddress;
|
||||||
|
_busValue = busValue;
|
||||||
|
|
||||||
|
static_cast<T *>(this)->synchronise();
|
||||||
}
|
}
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
|
Loading…
Reference in New Issue
Block a user