1
0
mirror of https://github.com/TomHarte/CLK.git synced 2024-12-21 23:29:25 +00:00

Attempts to implement TSX support for the MSX.

This commit is contained in:
Thomas Harte 2017-12-19 21:53:04 -05:00
parent 51e82c10c5
commit db6d9b59d0
6 changed files with 119 additions and 16 deletions

View File

@ -17,6 +17,8 @@
#include "../../Components/8255/i8255.hpp"
#include "../../Components/AY38910/AY38910.hpp"
#include "../../Storage/Tape/Tape.hpp"
#include "../CRTMachine.hpp"
#include "../ConfigurationTarget.hpp"
#include "../KeyboardMachine.hpp"
@ -61,15 +63,32 @@ class AudioToggle: public Outputs::Speaker::SampleSource {
Concurrency::DeferringAsyncTaskQueue &audio_queue_;
};
struct AYPortHandler: public GI::AY38910::PortHandler {
void set_port_output(bool port_b, uint8_t value) {
// printf("AY port %c output: %02x\n", port_b ? 'b' : 'a', value);
}
class AYPortHandler: public GI::AY38910::PortHandler {
public:
AYPortHandler(Storage::Tape::BinaryTapePlayer &tape_player) : tape_player_(tape_player) {}
uint8_t get_port_input(bool port_b) {
// printf("AY port %c input\n", port_b ? 'b' : 'a');
return 0xff;
}
void set_port_output(bool port_b, uint8_t value) {
if(port_b) {
// Bits 03: touchpad handshaking (?)
// Bit 4—5: monostable timer pulses
// Bit 6: joystick select
// Bit 7: code LED, if any
}
}
uint8_t get_port_input(bool port_b) {
if(!port_b) {
// Bits 05: Joystick (up, down, left, right, A, B)
// Bit 6: keyboard switch (not universal)
// Bit 7: tape input
return 0x7f | (tape_player_.get_input() ? 0x80 : 0x00);
}
return 0xff;
}
private:
Storage::Tape::BinaryTapePlayer &tape_player_;
};
class ConcreteMachine:
@ -86,7 +105,9 @@ class ConcreteMachine:
audio_toggle_(audio_queue_),
mixer_(ay_, audio_toggle_),
speaker_(mixer_),
i8255_port_handler_(*this, audio_toggle_) {
tape_player_(3579545 * 2),
i8255_port_handler_(*this, audio_toggle_, tape_player_),
ay_port_handler_(tape_player_) {
set_clock_rate(3579545);
std::memset(unpopulated_, 0xff, sizeof(unpopulated_));
clear_all_keys();
@ -130,6 +151,11 @@ class ConcreteMachine:
memory_slots_[1].read_pointers[(c >> 14) + base] = cartridge_.data() + c;
}
}
if(!media.tapes.empty()) {
tape_player_.set_tape(media.tapes.front());
}
return true;
}
@ -222,6 +248,9 @@ class ConcreteMachine:
default: break;
}
// Update the tape. (TODO: allow for sleeping)
tape_player_.run_for(cycle.length.as_int());
// Per the best information I currently have, the MSX inserts an extra cycle into each opcode read,
// but otherwise runs without pause.
HalfCycles addition((cycle.operation == CPU::Z80::PartialMachineCycle::ReadOpcode) ? 2 : 0);
@ -299,8 +328,8 @@ class ConcreteMachine:
class i8255PortHandler: public Intel::i8255::PortHandler {
public:
i8255PortHandler(ConcreteMachine &machine, AudioToggle &audio_toggle) :
machine_(machine), audio_toggle_(audio_toggle) {}
i8255PortHandler(ConcreteMachine &machine, AudioToggle &audio_toggle, Storage::Tape::BinaryTapePlayer &tape_player) :
machine_(machine), audio_toggle_(audio_toggle), tape_player_(tape_player) {}
void set_value(int port, uint8_t value) {
switch(port) {
@ -309,7 +338,9 @@ class ConcreteMachine:
// TODO:
// b6 caps lock LED
// b5 audio output
// b4 cassette motor relay
// b4: cassette motor relay
tape_player_.set_motor_control(!!(value & 0x10));
// b7: keyboard click
bool new_audio_level = !!(value & 0x80);
@ -335,6 +366,7 @@ class ConcreteMachine:
private:
ConcreteMachine &machine_;
AudioToggle &audio_toggle_;
Storage::Tape::BinaryTapePlayer &tape_player_;
};
CPU::Z80::Processor<ConcreteMachine, false, false> z80_;
@ -347,6 +379,8 @@ class ConcreteMachine:
Outputs::Speaker::CompoundSource<GI::AY38910::AY38910, AudioToggle> mixer_;
Outputs::Speaker::LowpassSpeaker<Outputs::Speaker::CompoundSource<GI::AY38910::AY38910, AudioToggle>> speaker_;
Storage::Tape::BinaryTapePlayer tape_player_;
i8255PortHandler i8255_port_handler_;
AYPortHandler ay_port_handler_;

View File

@ -265,6 +265,7 @@
<key>CFBundleTypeExtensions</key>
<array>
<string>cas</string>
<string>tsx</string>
</array>
<key>CFBundleTypeIconFile</key>
<string>cassette</string>
@ -272,6 +273,8 @@
<string>MSX Tape Image</string>
<key>CFBundleTypeRole</key>
<string>Viewer</string>
<key>LSTypeIsPackage</key>
<integer>0</integer>
<key>NSDocumentClass</key>
<string>$(PRODUCT_MODULE_NAME).MachineDocument</string>
</dict>

View File

@ -65,6 +65,9 @@ void StaticAnalyser::MSX::AddTargets(const Media &media, std::list<Target> &dest
target.media.cartridges = MSXCartridgesFrom(media.cartridges);
// TODO: tape parsing. Be dumb for now.
target.media.tapes = media.tapes;
if(!target.media.empty()) {
target.machine = Target::MSX;
target.probability = 1.0;

View File

@ -114,6 +114,7 @@ static Media GetMediaAndPlatforms(const char *file_name, TargetPlatform::IntType
Format("ssd", result.disks, Disk::DiskImageHolder<Storage::Disk::SSD>, TargetPlatform::Acorn) // SSD
Format("tap", result.tapes, Tape::CommodoreTAP, TargetPlatform::Commodore) // TAP (Commodore)
Format("tap", result.tapes, Tape::OricTAP, TargetPlatform::Oric) // TAP (Oric)
Format("tsx", result.tapes, Tape::TZX, TargetPlatform::MSX) // TSX
Format("tzx", result.tapes, Tape::TZX, TargetPlatform::ZX8081) // TZX
Format("uef", result.tapes, Tape::UEF, TargetPlatform::Acorn) // UEF (tape)

View File

@ -76,6 +76,8 @@ void TZX::get_next_pulses() {
case 0x31: ignore_message_block(); break;
case 0x33: get_hardware_type(); break;
case 0x4b: get_kansas_city_block(); break;
default:
// In TZX each chunk has a different way of stating or implying its length,
// so there is no route past an unimplemented chunk.
@ -203,9 +205,7 @@ void TZX::get_turbo_speed_data_block() {
void TZX::get_data_block(const DataBlock &data_block) {
// Output pilot tone.
for(unsigned int c = 0; c < data_block.length_of_pilot_tone; c++) {
post_pulse(data_block.length_of_pilot_pulse);
}
post_pulses(data_block.length_of_pilot_tone, data_block.length_of_pilot_pulse);
// Output sync pulses.
post_pulse(data_block.length_of_sync_first_pulse);
@ -237,7 +237,7 @@ void TZX::get_pure_tone_data_block() {
uint16_t length_of_pulse = file_.get16le();
uint16_t nunber_of_pulses = file_.get16le();
while(nunber_of_pulses--) post_pulse(length_of_pulse);
post_pulses(nunber_of_pulses, length_of_pulse);
}
void TZX::get_pure_data_block() {
@ -267,8 +267,68 @@ void TZX::get_pause() {
}
}
void TZX::get_kansas_city_block() {
uint32_t block_length = file_.get32le();
const uint16_t pause_after_block = file_.get16le();
const uint16_t pilot_pulse_duration = file_.get16le();
const uint16_t pilot_length = file_.get16le();
uint16_t pulse_durations[2];
pulse_durations[0] = file_.get16le();
pulse_durations[1] = file_.get16le();
const uint8_t packed_pulse_counts = file_.get8();
const unsigned int pulse_counts[2] = {
static_cast<unsigned int>((((packed_pulse_counts >> 4) - 1) & 15) + 1),
static_cast<unsigned int>((((packed_pulse_counts & 15) - 1) & 15) + 1)
};
const uint8_t padding_flags = file_.get8();
const unsigned int number_of_leading_pulses = ((padding_flags >> 6)&3) * pulse_counts[(padding_flags >> 5) & 1];
const unsigned int leading_pulse_length = pulse_durations[(padding_flags >> 5) & 1];
const unsigned int number_of_trailing_pulses = ((padding_flags >> 3)&3) * pulse_counts[(padding_flags >> 2) & 1];
const unsigned int trailing_pulse_length = pulse_durations[(padding_flags >> 2) & 1];
block_length -= 12;
// Output pilot tone.
post_pulses(pilot_length, pilot_pulse_duration);
// Output data.
while(block_length--) {
post_pulses(number_of_leading_pulses, leading_pulse_length);
uint8_t new_byte = file_.get8();
int bits = 8;
if(padding_flags & 1) {
// Output MSB first.
while(bits--) {
int bit = (new_byte >> 7) & 1;
new_byte <<= 1;
post_pulses(pulse_counts[bit], pulse_durations[bit]);
}
} else {
// Output LSB first.
while(bits--) {
int bit = new_byte & 1;
new_byte >>= 1;
post_pulses(pulse_counts[bit], pulse_durations[bit]);
}
}
post_pulses(number_of_trailing_pulses, trailing_pulse_length);
}
// Output gap.
post_gap(pause_after_block);
}
// MARK: - Output
void TZX::post_pulses(unsigned int count, unsigned int length) {
while(count--) post_pulse(length);
}
void TZX::post_pulse(unsigned int length) {
post_pulse(Storage::Time(length, StandardTZXClock));
}

View File

@ -46,6 +46,7 @@ class TZX: public PulseQueuedTape {
void get_pure_data_block();
void get_generalised_data_block();
void get_pause();
void get_kansas_city_block();
void get_hardware_type();
@ -80,6 +81,7 @@ class TZX: public PulseQueuedTape {
void get_data_block(const DataBlock &);
void get_data(const Data &);
void post_pulses(unsigned int count, unsigned int length);
void post_pulse(unsigned int length);
void post_gap(unsigned int milliseconds);