mirror of
https://github.com/TomHarte/CLK.git
synced 2025-03-20 03:29:47 +00:00
Merge branch 'master' into JITSleeper
This commit is contained in:
commit
afb4e6d37d
@ -169,19 +169,21 @@ template <bool is_stereo> class AY38910: public ::Outputs::Speaker::SampleSource
|
||||
AY-deploying machines of the era.
|
||||
*/
|
||||
struct Utility {
|
||||
template <typename AY> static void select_register(AY &ay, uint8_t reg) {
|
||||
ay.set_control_lines(GI::AY38910::ControlLines(GI::AY38910::BDIR | GI::AY38910::BC2 | GI::AY38910::BC1));
|
||||
ay.set_data_input(reg);
|
||||
template <typename AY> static void write(AY &ay, bool is_data_write, uint8_t data) {
|
||||
ay.set_control_lines(GI::AY38910::ControlLines(GI::AY38910::BDIR | GI::AY38910::BC2 | (is_data_write ? 0 : GI::AY38910::BC1)));
|
||||
ay.set_data_input(data);
|
||||
ay.set_control_lines(GI::AY38910::ControlLines(0));
|
||||
}
|
||||
|
||||
template <typename AY> static void select_register(AY &ay, uint8_t reg) {
|
||||
write(ay, false, reg);
|
||||
}
|
||||
|
||||
template <typename AY> static void write_data(AY &ay, uint8_t reg) {
|
||||
ay.set_control_lines(GI::AY38910::ControlLines(GI::AY38910::BDIR | GI::AY38910::BC2));
|
||||
ay.set_data_input(reg);
|
||||
ay.set_control_lines(GI::AY38910::ControlLines(0));
|
||||
write(ay, true, reg);
|
||||
}
|
||||
|
||||
template <typename AY> static uint8_t read_data(AY &ay) {
|
||||
template <typename AY> static uint8_t read(AY &ay) {
|
||||
ay.set_control_lines(GI::AY38910::ControlLines(GI::AY38910::BC2 | GI::AY38910::BC1));
|
||||
const uint8_t result = ay.get_data_output();
|
||||
ay.set_control_lines(GI::AY38910::ControlLines(0));
|
||||
|
@ -1088,6 +1088,7 @@ template <bool has_fdc> class ConcreteMachine:
|
||||
// If there are any tapes supplied, use the first of them.
|
||||
if(!media.tapes.empty()) {
|
||||
tape_player_.set_tape(media.tapes.front());
|
||||
set_use_fast_tape_hack();
|
||||
}
|
||||
|
||||
// Insert up to four disks.
|
||||
|
@ -347,18 +347,11 @@ class ConcreteMachine:
|
||||
update_audio();
|
||||
|
||||
if(cycle.operation & Microcycle::Read) {
|
||||
ay_.set_control_lines(GI::AY38910::ControlLines(GI::AY38910::BC2 | GI::AY38910::BC1));
|
||||
cycle.set_value8_high(ay_.get_data_output());
|
||||
ay_.set_control_lines(GI::AY38910::ControlLines(0));
|
||||
cycle.set_value8_high(GI::AY38910::Utility::read(ay_));
|
||||
} else {
|
||||
// Net effect here: addresses with bit 1 set write to a register,
|
||||
// addresses with bit 1 clear select a register.
|
||||
ay_.set_control_lines(GI::AY38910::ControlLines(
|
||||
GI::AY38910::BC2 | GI::AY38910::BDIR
|
||||
| ((address&2) ? 0 : GI::AY38910::BC1)
|
||||
));
|
||||
ay_.set_data_input(cycle.value8_high());
|
||||
ay_.set_control_lines(GI::AY38910::ControlLines(0));
|
||||
GI::AY38910::Utility::write(ay_, address&2, cycle.value8_high());
|
||||
}
|
||||
return delay + HalfCycles(2);
|
||||
|
||||
|
@ -287,9 +287,7 @@ class ConcreteMachine:
|
||||
case 0x52:
|
||||
// Read AY data.
|
||||
update_audio();
|
||||
ay_.set_control_lines(GI::AY38910::ControlLines(GI::AY38910::BC2 | GI::AY38910::BC1));
|
||||
*cycle.value = ay_.get_data_output();
|
||||
ay_.set_control_lines(GI::AY38910::ControlLines(0));
|
||||
*cycle.value = GI::AY38910::Utility::read(ay_);
|
||||
break;
|
||||
}
|
||||
break;
|
||||
@ -324,16 +322,12 @@ class ConcreteMachine:
|
||||
case 0x50:
|
||||
// Set AY address.
|
||||
update_audio();
|
||||
ay_.set_control_lines(GI::AY38910::BC1);
|
||||
ay_.set_data_input(*cycle.value);
|
||||
ay_.set_control_lines(GI::AY38910::ControlLines(0));
|
||||
GI::AY38910::Utility::select_register(ay_, *cycle.value);
|
||||
break;
|
||||
case 0x51:
|
||||
// Set AY data.
|
||||
update_audio();
|
||||
ay_.set_control_lines(GI::AY38910::ControlLines(GI::AY38910::BC2 | GI::AY38910::BDIR));
|
||||
ay_.set_data_input(*cycle.value);
|
||||
ay_.set_control_lines(GI::AY38910::ControlLines(0));
|
||||
GI::AY38910::Utility::write_data(ay_, *cycle.value);
|
||||
break;
|
||||
case 0x53:
|
||||
super_game_module_.replace_ram = !!((*cycle.value)&0x1);
|
||||
|
@ -525,9 +525,7 @@ class ConcreteMachine:
|
||||
|
||||
case 0xa2:
|
||||
update_audio();
|
||||
ay_.set_control_lines(GI::AY38910::ControlLines(GI::AY38910::BC2 | GI::AY38910::BC1));
|
||||
*cycle.value = ay_.get_data_output();
|
||||
ay_.set_control_lines(GI::AY38910::ControlLines(0));
|
||||
*cycle.value = GI::AY38910::Utility::read(ay_);
|
||||
break;
|
||||
|
||||
case 0xa8: case 0xa9:
|
||||
@ -552,9 +550,7 @@ class ConcreteMachine:
|
||||
|
||||
case 0xa0: case 0xa1:
|
||||
update_audio();
|
||||
ay_.set_control_lines(GI::AY38910::ControlLines(GI::AY38910::BDIR | GI::AY38910::BC2 | ((port == 0xa0) ? GI::AY38910::BC1 : 0)));
|
||||
ay_.set_data_input(*cycle.value);
|
||||
ay_.set_control_lines(GI::AY38910::ControlLines(0));
|
||||
GI::AY38910::Utility::write(ay_, port == 0xa1, *cycle.value);
|
||||
break;
|
||||
|
||||
case 0xa8: case 0xa9:
|
||||
|
@ -472,22 +472,15 @@ template<bool is_zx81> class ConcreteMachine:
|
||||
HalfCycles time_since_ay_update_;
|
||||
inline void ay_set_register(uint8_t value) {
|
||||
update_audio();
|
||||
ay_.set_control_lines(GI::AY38910::BC1);
|
||||
ay_.set_data_input(value);
|
||||
ay_.set_control_lines(GI::AY38910::ControlLines(0));
|
||||
GI::AY38910::Utility::select_register(ay_, value);
|
||||
}
|
||||
inline void ay_set_data(uint8_t value) {
|
||||
update_audio();
|
||||
ay_.set_control_lines(GI::AY38910::ControlLines(GI::AY38910::BC2 | GI::AY38910::BDIR));
|
||||
ay_.set_data_input(value);
|
||||
ay_.set_control_lines(GI::AY38910::ControlLines(0));
|
||||
GI::AY38910::Utility::write_data(ay_, value);
|
||||
}
|
||||
inline uint8_t ay_read_data() {
|
||||
update_audio();
|
||||
ay_.set_control_lines(GI::AY38910::ControlLines(GI::AY38910::BC2 | GI::AY38910::BC1));
|
||||
const uint8_t value = ay_.get_data_output();
|
||||
ay_.set_control_lines(GI::AY38910::ControlLines(0));
|
||||
return value;
|
||||
return GI::AY38910::Utility::read(ay_);
|
||||
}
|
||||
inline void update_audio() {
|
||||
speaker_.run_for(audio_queue_, time_since_ay_update_.divide_cycles(Cycles(2)));
|
||||
|
@ -335,7 +335,7 @@ template<Model model> class ConcreteMachine:
|
||||
if((address & 0xc002) == 0xc000) {
|
||||
// Read from AY register.
|
||||
update_audio();
|
||||
*cycle.value &= GI::AY38910::Utility::read_data(ay_);
|
||||
*cycle.value &= GI::AY38910::Utility::read(ay_);
|
||||
}
|
||||
|
||||
// Check for a floating bus read; these are particularly arcane
|
||||
@ -431,6 +431,7 @@ template<Model model> class ConcreteMachine:
|
||||
// If there are any tapes supplied, use the first of them.
|
||||
if(!media.tapes.empty()) {
|
||||
tape_player_.set_tape(media.tapes.front());
|
||||
set_use_fast_tape();
|
||||
}
|
||||
|
||||
// Insert up to four disks.
|
||||
|
@ -19,6 +19,7 @@ State::State(const ProcessorBase &src): State() {
|
||||
registers.bc = src.bc_.full;
|
||||
registers.de = src.de_.full;
|
||||
registers.hl = src.hl_.full;
|
||||
registers.afDash = src.afDash_.full;
|
||||
registers.bcDash = src.bcDash_.full;
|
||||
registers.deDash = src.deDash_.full;
|
||||
registers.hlDash = src.hlDash_.full;
|
||||
@ -107,6 +108,7 @@ void State::apply(ProcessorBase &target) {
|
||||
target.bc_.full = registers.bc;
|
||||
target.de_.full = registers.de;
|
||||
target.hl_.full = registers.hl;
|
||||
target.afDash_.full = registers.afDash;
|
||||
target.bcDash_.full = registers.bcDash;
|
||||
target.deDash_.full = registers.deDash;
|
||||
target.hlDash_.full = registers.hlDash;
|
||||
@ -177,6 +179,7 @@ State::Registers::Registers() {
|
||||
DeclareField(bc);
|
||||
DeclareField(de);
|
||||
DeclareField(hl);
|
||||
DeclareField(afDash);
|
||||
DeclareField(bcDash);
|
||||
DeclareField(deDash);
|
||||
DeclareField(hlDash);
|
||||
|
@ -31,7 +31,7 @@ struct State: public Reflection::StructImpl<State> {
|
||||
uint8_t a;
|
||||
uint8_t flags;
|
||||
uint16_t bc, de, hl;
|
||||
uint16_t bcDash, deDash, hlDash;
|
||||
uint16_t afDash, bcDash, deDash, hlDash;
|
||||
uint16_t ix, iy, ir;
|
||||
uint16_t program_counter, stack_pointer;
|
||||
uint16_t memptr;
|
||||
|
10
README.md
10
README.md
@ -24,15 +24,21 @@ It currently contains emulations of the:
|
||||
* Sinclair ZX80/81; and
|
||||
* Sinclair ZX Spectrum +2a/+3.
|
||||
|
||||
## Single-click Loading
|
||||
## Single-step Loading
|
||||
|
||||
Through static and runtime analysis CLK seeks automatically to select and configure the appropriate machine to run any provided disk, tape or ROM; to issue any commands necessary to run the software contained on the disk, tape or ROM; and to provide accelerated loading where feasible.
|
||||
|
||||
The full process of loading a title — even if you've never used the emulated machine before — is therefore:
|
||||
With CLK installed the full process of loading a piece of software — even if you've never used the machine it runs on before — is therefore:
|
||||
|
||||
1. locate it in your OS;
|
||||
2. double click it.
|
||||
|
||||

|
||||
|
||||
So there's no need to wade through creating a new machine, inserting media into it or figuring out which loading command goes with this piece of software, and no import procedure — CLK does not attempt to take ownership of your files or to usurp your OS.
|
||||
|
||||
Keep your emulated titles on your desktop, in your dock, or wherever else you usually prefer to launch software from, and launch in a single step. Just like you'd expect from any other piece of desktop software.
|
||||
|
||||
## Signal Processing
|
||||
|
||||
Consider an ordinary, unmodified Commodore Vic-20. Its only video output is composite. Therefore the emulated machine's only video output is composite. In order to display the video output, your GPU must decode composite video. Therefore composite video artefacts are present and correct not because of a post hoc filter but because the real signal is really being processed.
|
||||
|
BIN
READMEImages/JustDoubleClick.gif
Normal file
BIN
READMEImages/JustDoubleClick.gif
Normal file
Binary file not shown.
After Width: | Height: | Size: 10 MiB |
@ -38,7 +38,7 @@ ZXSpectrumTAP::ZXSpectrumTAP(const std::string &file_name) :
|
||||
}
|
||||
|
||||
bool ZXSpectrumTAP::is_at_end() {
|
||||
return file_.tell() == file_.stats().st_size;
|
||||
return file_.tell() == file_.stats().st_size && phase_ == Phase::Gap;
|
||||
}
|
||||
|
||||
void ZXSpectrumTAP::virtual_reset() {
|
||||
@ -111,7 +111,7 @@ Tape::Pulse ZXSpectrumTAP::virtual_get_next_pulse() {
|
||||
}
|
||||
|
||||
void ZXSpectrumTAP::read_next_block() {
|
||||
if(is_at_end()) {
|
||||
if(file_.tell() == file_.stats().st_size) {
|
||||
phase_ = Phase::Gap;
|
||||
} else {
|
||||
block_length_ = file_.get16le();
|
||||
|
Loading…
x
Reference in New Issue
Block a user