1
0
mirror of https://github.com/TomHarte/CLK.git synced 2024-07-06 01:28:57 +00:00

Merge branch 'master' into JITSleeper

This commit is contained in:
Thomas Harte 2021-04-04 15:37:19 -04:00
commit afb4e6d37d
12 changed files with 36 additions and 47 deletions

View File

@ -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));

View File

@ -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.

View File

@ -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);

View File

@ -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);

View File

@ -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:

View File

@ -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)));

View File

@ -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.

View File

@ -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);

View File

@ -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;

View File

@ -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.
![Loading a piece of software](READMEImages/JustDoubleClick.gif)
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.

Binary file not shown.

After

Width:  |  Height:  |  Size: 10 MiB

View File

@ -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();