diff --git a/Machines/MSX/MSX.cpp b/Machines/MSX/MSX.cpp index d267f808d..58c161f31 100644 --- a/Machines/MSX/MSX.cpp +++ b/Machines/MSX/MSX.cpp @@ -181,26 +181,53 @@ class ConcreteMachine: case CPU::Z80::PartialMachineCycle::ReadOpcode: if(address == 0x1a63) { // TAPION - tape_player_.set_motor_control(true); + + // Enable the tape motor. + i8255_.set_register(0xab, 0x8); + + // Disable interrupts. + z80_.set_value_of_register(CPU::Z80::Register::IFF1, 0); + z80_.set_value_of_register(CPU::Z80::Register::IFF2, 0); + + // Use the parser to find a header, and if one is found then populate + // LOWLIM and WINWID, and reset carry. Otherwise set carry. using Parser = Storage::Tape::MSX::Parser; std::unique_ptr new_speed = Parser::find_header(tape_player_); - ram_[0xFCA4] = new_speed->minimum_start_bit_duration; - ram_[0xFCA5] = new_speed->low_high_disrimination_duration; + if(new_speed) { + ram_[0xfca4] = new_speed->minimum_start_bit_duration; + ram_[0xfca5] = new_speed->low_high_disrimination_duration; + z80_.set_value_of_register(CPU::Z80::Register::Flags, 0); + } else { + z80_.set_value_of_register(CPU::Z80::Register::Flags, 1); + } + // RET. *cycle.value = 0xc9; break; } if(address == 0x1abc) { // TAPIN + + // Grab the current values of LOWLIM and WINWID. using Parser = Storage::Tape::MSX::Parser; Parser::FileSpeed tape_speed; - tape_speed.minimum_start_bit_duration = ram_[0xFCA4]; - tape_speed.low_high_disrimination_duration = ram_[0xFCA5]; -// printf("Low lim: %02x / win wid: %02x\n", ram_[0xFCA4], ram_[0xFCA5]); - int next_byte = Parser::get_byte(tape_speed, tape_player_); - z80_.set_value_of_register(CPU::Z80::Register::A, static_cast(next_byte)); + tape_speed.minimum_start_bit_duration = ram_[0xfca4]; + tape_speed.low_high_disrimination_duration = ram_[0xfca5]; + // Ask the tape parser to grab a byte. + int next_byte = Parser::get_byte(tape_speed, tape_player_); + + // If a byte was found, return it with carry unset. Otherwise set carry to + // indicate error. + if(next_byte >= 0) { + z80_.set_value_of_register(CPU::Z80::Register::A, static_cast(next_byte)); + z80_.set_value_of_register(CPU::Z80::Register::Flags, 0); + } else { + z80_.set_value_of_register(CPU::Z80::Register::Flags, 1); + } + + // RET. *cycle.value = 0xc9; break; } diff --git a/Storage/Tape/Parsers/MSX.cpp b/Storage/Tape/Parsers/MSX.cpp index fe0edd8cb..e06ef7f1c 100644 --- a/Storage/Tape/Parsers/MSX.cpp +++ b/Storage/Tape/Parsers/MSX.cpp @@ -69,11 +69,11 @@ std::unique_ptr Parser::find_header(Storage::Tape::BinaryTape */ total_length = total_length / 256.0f; // To get the average, in microseconds. // To convert to the loop count format used by the MSX BIOS. - uint8_t int_result = static_cast(total_length / (0.00001145f * 1.5f)); + uint8_t int_result = static_cast(total_length / (0.00001145f * 0.75f)); std::unique_ptr result(new FileSpeed); - result->minimum_start_bit_duration = int_result + ((int_result + 1) >> 1); - result->low_high_disrimination_duration = int_result; + result->minimum_start_bit_duration = int_result; + result->low_high_disrimination_duration = (int_result * 3) >> 2; return result; } @@ -99,19 +99,27 @@ int Parser::get_byte(const FileSpeed &speed, Storage::Tape::BinaryTapePlayer &ta */ float minimum_start_bit_duration = static_cast(speed.minimum_start_bit_duration) * 0.00001145f; while(!tape_player.get_tape()->is_at_end()) { - while(!tape_player.get_tape()->is_at_end() && !tape_player.get_input()) { + // Find a negative transition. + while(!tape_player.get_tape()->is_at_end() && tape_player.get_input()) { tape_player.run_for_input_pulse(); } - bool level = true; + // Measure the following cycle (i.e. two transitions). + bool level = tape_player.get_input(); float time_to_transition = 0.0f; + int transitions = 0; while(!tape_player.get_tape()->is_at_end()) { time_to_transition += static_cast(tape_player.get_cycles_until_next_event()) / static_cast(tape_player.get_input_clock_rate()); tape_player.run_for_input_pulse(); - if(level != tape_player.get_input()) - break; + if(level != tape_player.get_input()) { + level = tape_player.get_input(); + transitions++; + if(transitions == 2) + break; + } } + // Check length against 'LOWLIM' (i.e. the minimum start bit duration). if(time_to_transition > minimum_start_bit_duration) { break; } @@ -125,10 +133,10 @@ int Parser::get_byte(const FileSpeed &speed, Storage::Tape::BinaryTapePlayer &ta */ int result = 0; const int cycles_per_window = static_cast( + 0.5f + static_cast(speed.low_high_disrimination_duration) * 0.0000173f * - static_cast(tape_player.get_input_clock_rate()) * - 2.0f + static_cast(tape_player.get_input_clock_rate()) ); int bits_left = 8; bool level = tape_player.get_input(); @@ -139,8 +147,10 @@ int Parser::get_byte(const FileSpeed &speed, Storage::Tape::BinaryTapePlayer &ta while(!tape_player.get_tape()->is_at_end() && cycles_remaining) { const int cycles_until_next_event = static_cast(tape_player.get_cycles_until_next_event()); const int cycles_to_run_for = std::min(cycles_until_next_event, cycles_remaining); + cycles_remaining -= cycles_to_run_for; tape_player.run_for(Cycles(cycles_to_run_for)); + if(level != tape_player.get_input()) { level = tape_player.get_input(); transitions++;