diff --git a/Machines/ColecoVision/ColecoVision.cpp b/Machines/ColecoVision/ColecoVision.cpp index 8452dd124..1b0958817 100644 --- a/Machines/ColecoVision/ColecoVision.cpp +++ b/Machines/ColecoVision/ColecoVision.cpp @@ -197,131 +197,134 @@ class ConcreteMachine: time_since_vdp_update_ += length; time_since_sn76489_update_ += length; - uint16_t address = cycle.address ? *cycle.address : 0x0000; - switch(cycle.operation) { - case CPU::Z80::PartialMachineCycle::ReadOpcode: - if(!address) pc_zero_accesses_++; - case CPU::Z80::PartialMachineCycle::Read: - if(address < 0x2000) { - if(super_game_module_.replace_bios) { + // Act only if necessary. + if(cycle.is_terminal()) { + uint16_t address = cycle.address ? *cycle.address : 0x0000; + switch(cycle.operation) { + case CPU::Z80::PartialMachineCycle::ReadOpcode: + if(!address) pc_zero_accesses_++; + case CPU::Z80::PartialMachineCycle::Read: + if(address < 0x2000) { + if(super_game_module_.replace_bios) { + *cycle.value = super_game_module_.ram[address]; + } else { + *cycle.value = bios_[address]; + } + } else if(super_game_module_.replace_ram && address < 0x8000) { *cycle.value = super_game_module_.ram[address]; + } else if(address >= 0x6000 && address < 0x8000) { + *cycle.value = ram_[address & 1023]; + } else if(address >= 0x8000 && address <= cartridge_address_limit_) { + if(is_megacart_ && address >= 0xffc0) { + page_megacart(address); + } + *cycle.value = cartridge_pages_[(address >> 14)&1][address&0x3fff]; } else { - *cycle.value = bios_[address]; + *cycle.value = 0xff; } - } else if(super_game_module_.replace_ram && address < 0x8000) { - *cycle.value = super_game_module_.ram[address]; - } else if(address >= 0x6000 && address < 0x8000) { - *cycle.value = ram_[address & 1023]; - } else if(address >= 0x8000 && address <= cartridge_address_limit_) { - if(is_megacart_ && address >= 0xffc0) { + break; + + case CPU::Z80::PartialMachineCycle::Write: + if(super_game_module_.replace_bios && address < 0x2000) { + super_game_module_.ram[address] = *cycle.value; + } else if(super_game_module_.replace_ram && address >= 0x2000 && address < 0x8000) { + super_game_module_.ram[address] = *cycle.value; + } else if(address >= 0x6000 && address < 0x8000) { + ram_[address & 1023] = *cycle.value; + } else if(is_megacart_ && address >= 0xffc0) { page_megacart(address); } - *cycle.value = cartridge_pages_[(address >> 14)&1][address&0x3fff]; - } else { - *cycle.value = 0xff; - } - break; + break; - case CPU::Z80::PartialMachineCycle::Write: - if(super_game_module_.replace_bios && address < 0x2000) { - super_game_module_.ram[address] = *cycle.value; - } else if(super_game_module_.replace_ram && address >= 0x2000 && address < 0x8000) { - super_game_module_.ram[address] = *cycle.value; - } else if(address >= 0x6000 && address < 0x8000) { - ram_[address & 1023] = *cycle.value; - } else if(is_megacart_ && address >= 0xffc0) { - page_megacart(address); - } - break; + case CPU::Z80::PartialMachineCycle::Input: + switch((address >> 5) & 7) { + case 5: + update_video(); + *cycle.value = vdp_->get_register(address); + z80_.set_non_maskable_interrupt_line(vdp_->get_interrupt_line()); + time_until_interrupt_ = vdp_->get_time_until_interrupt(); + break; - case CPU::Z80::PartialMachineCycle::Input: - switch((address >> 5) & 7) { - case 5: - update_video(); - *cycle.value = vdp_->get_register(address); - z80_.set_non_maskable_interrupt_line(vdp_->get_interrupt_line()); - time_until_interrupt_ = vdp_->get_time_until_interrupt(); - break; + case 7: { + const std::size_t joystick_id = (address&2) >> 1; + Joystick *joystick = static_cast(joysticks_[joystick_id].get()); + if(joysticks_in_keypad_mode_) { + *cycle.value = joystick->get_keypad_input(); + } else { + *cycle.value = joystick->get_direction_input(); + } - case 7: { - const std::size_t joystick_id = (address&2) >> 1; - Joystick *joystick = static_cast(joysticks_[joystick_id].get()); - if(joysticks_in_keypad_mode_) { - *cycle.value = joystick->get_keypad_input(); - } else { - *cycle.value = joystick->get_direction_input(); - } + // Hitting exactly the recommended joypad input port is an indicator that + // this really is a ColecoVision game. The BIOS won't do this when just waiting + // to start a game (unlike accessing the VDP and SN). + if((address&0xfc) == 0xfc) confidence_counter_.add_hit(); + } break; - // Hitting exactly the recommended joypad input port is an indicator that - // this really is a ColecoVision game. The BIOS won't do this when just waiting - // to start a game (unlike accessing the VDP and SN). - if((address&0xfc) == 0xfc) confidence_counter_.add_hit(); - } break; + default: + switch(address&0xff) { + default: *cycle.value = 0xff; break; + 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)); + break; + } + break; + } + break; - default: - switch(address&0xff) { - default: *cycle.value = 0xff; break; - 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)); - break; - } - break; - } - break; + case CPU::Z80::PartialMachineCycle::Output: { + const int eighth = (address >> 5) & 7; + switch(eighth) { + case 4: case 6: + joysticks_in_keypad_mode_ = eighth == 4; + break; - case CPU::Z80::PartialMachineCycle::Output: { - const int eighth = (address >> 5) & 7; - switch(eighth) { - case 4: case 6: - joysticks_in_keypad_mode_ = eighth == 4; - break; + case 5: + update_video(); + vdp_->set_register(address, *cycle.value); + z80_.set_non_maskable_interrupt_line(vdp_->get_interrupt_line()); + time_until_interrupt_ = vdp_->get_time_until_interrupt(); + break; - case 5: - update_video(); - vdp_->set_register(address, *cycle.value); - z80_.set_non_maskable_interrupt_line(vdp_->get_interrupt_line()); - time_until_interrupt_ = vdp_->get_time_until_interrupt(); - break; + case 7: + update_audio(); + sn76489_.set_register(*cycle.value); + break; - case 7: - update_audio(); - sn76489_.set_register(*cycle.value); - break; + default: + // Catch Super Game Module accesses; it decodes more thoroughly. + switch(address&0xff) { + default: break; + case 0x7f: + super_game_module_.replace_bios = !((*cycle.value)&0x2); + break; + 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)); + 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)); + break; + case 0x53: + super_game_module_.replace_ram = !!((*cycle.value)&0x1); + break; + } + break; + } + } break; - default: - // Catch Super Game Module accesses; it decodes more thoroughly. - switch(address&0xff) { - default: break; - case 0x7f: - super_game_module_.replace_bios = !((*cycle.value)&0x2); - break; - 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)); - 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)); - break; - case 0x53: - super_game_module_.replace_ram = !!((*cycle.value)&0x1); - break; - } - break; - } - } break; - - default: break; + default: break; + } } if(time_until_interrupt_ > 0) { diff --git a/Machines/MSX/MSX.cpp b/Machines/MSX/MSX.cpp index f26f7ce9a..24fe36c73 100644 --- a/Machines/MSX/MSX.cpp +++ b/Machines/MSX/MSX.cpp @@ -361,183 +361,185 @@ class ConcreteMachine: memory_slots_[2].cycles_since_update += total_length; memory_slots_[3].cycles_since_update += total_length; - uint16_t address = cycle.address ? *cycle.address : 0x0000; - switch(cycle.operation) { - case CPU::Z80::PartialMachineCycle::ReadOpcode: - if(use_fast_tape_) { - if(address == 0x1a63) { - // TAPION + if(cycle.is_terminal()) { + uint16_t address = cycle.address ? *cycle.address : 0x0000; + switch(cycle.operation) { + case CPU::Z80::PartialMachineCycle::ReadOpcode: + if(use_fast_tape_) { + if(address == 0x1a63) { + // TAPION - // Enable the tape motor. - i8255_.set_register(0xab, 0x8); + // 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); + // 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_); - 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); + // 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_); + 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; } - // RET. - *cycle.value = 0xc9; - break; - } + if(address == 0x1abc) { + // TAPIN - 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]; - // 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]; + // Ask the tape parser to grab a byte. + int next_byte = Parser::get_byte(tape_speed, tape_player_); - // 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); + } - // 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; } - - // RET. - *cycle.value = 0xc9; - break; } - } - if(!address) { - pc_zero_accesses_++; - } - if(read_pointers_[address >> 13] == unpopulated_) { - performed_unmapped_access_ = true; - } - pc_address_ = address; // This is retained so as to be able to name the source of an access to cartridge handlers. - case CPU::Z80::PartialMachineCycle::Read: - if(read_pointers_[address >> 13]) { - *cycle.value = read_pointers_[address >> 13][address & 8191]; - } else { + if(!address) { + pc_zero_accesses_++; + } + if(read_pointers_[address >> 13] == unpopulated_) { + performed_unmapped_access_ = true; + } + pc_address_ = address; // This is retained so as to be able to name the source of an access to cartridge handlers. + case CPU::Z80::PartialMachineCycle::Read: + if(read_pointers_[address >> 13]) { + *cycle.value = read_pointers_[address >> 13][address & 8191]; + } else { + int slot_hit = (paged_memory_ >> ((address >> 14) * 2)) & 3; + memory_slots_[slot_hit].handler->run_for(memory_slots_[slot_hit].cycles_since_update.flush()); + *cycle.value = memory_slots_[slot_hit].handler->read(address); + } + break; + + case CPU::Z80::PartialMachineCycle::Write: { + write_pointers_[address >> 13][address & 8191] = *cycle.value; + int slot_hit = (paged_memory_ >> ((address >> 14) * 2)) & 3; - memory_slots_[slot_hit].handler->run_for(memory_slots_[slot_hit].cycles_since_update.flush()); - *cycle.value = memory_slots_[slot_hit].handler->read(address); - } - break; - - case CPU::Z80::PartialMachineCycle::Write: { - write_pointers_[address >> 13][address & 8191] = *cycle.value; - - int slot_hit = (paged_memory_ >> ((address >> 14) * 2)) & 3; - if(memory_slots_[slot_hit].handler) { - update_audio(); - memory_slots_[slot_hit].handler->run_for(memory_slots_[slot_hit].cycles_since_update.flush()); - memory_slots_[slot_hit].handler->write(address, *cycle.value, read_pointers_[pc_address_ >> 13] != memory_slots_[0].read_pointers[pc_address_ >> 13]); - } - } break; - - case CPU::Z80::PartialMachineCycle::Input: - switch(address & 0xff) { - case 0x98: case 0x99: - vdp_->run_for(time_since_vdp_update_.flush()); - *cycle.value = vdp_->get_register(address); - z80_.set_interrupt_line(vdp_->get_interrupt_line()); - time_until_interrupt_ = vdp_->get_time_until_interrupt(); - break; - - case 0xa2: + if(memory_slots_[slot_hit].handler) { update_audio(); - ay_.set_control_lines(static_cast(GI::AY38910::BC2 | GI::AY38910::BC1)); - *cycle.value = ay_.get_data_output(); - ay_.set_control_lines(static_cast(0)); - break; - - case 0xa8: case 0xa9: - case 0xaa: case 0xab: - *cycle.value = i8255_.get_register(address); - break; - - default: - *cycle.value = 0xff; - break; - } - break; - - case CPU::Z80::PartialMachineCycle::Output: { - const int port = address & 0xff; - switch(port) { - case 0x98: case 0x99: - vdp_->run_for(time_since_vdp_update_.flush()); - vdp_->set_register(address, *cycle.value); - z80_.set_interrupt_line(vdp_->get_interrupt_line()); - time_until_interrupt_ = vdp_->get_time_until_interrupt(); - break; - - case 0xa0: case 0xa1: - update_audio(); - ay_.set_control_lines(static_cast(GI::AY38910::BDIR | GI::AY38910::BC2 | ((port == 0xa0) ? GI::AY38910::BC1 : 0))); - ay_.set_data_input(*cycle.value); - ay_.set_control_lines(static_cast(0)); - break; - - case 0xa8: case 0xa9: - case 0xaa: case 0xab: - i8255_.set_register(address, *cycle.value); - break; - - case 0xfc: case 0xfd: case 0xfe: case 0xff: -// printf("RAM banking %02x: %02x\n", port, *cycle.value); - break; - } - } break; - - case CPU::Z80::PartialMachineCycle::Interrupt: - *cycle.value = 0xff; - - // Take this as a convenient moment to jump into the keyboard buffer, if desired. - if(!input_text_.empty()) { - // The following are KEYBUF per the Red Book; its address and its definition as DEFS 40. - const int buffer_start = 0xfbf0; - const int buffer_size = 40; - - // Also from the Red Book: GETPNT is at F3FAH and PUTPNT is at F3F8H. - int read_address = ram_[0xf3fa] | (ram_[0xf3fb] << 8); - int write_address = ram_[0xf3f8] | (ram_[0xf3f9] << 8); - - // Write until either the string is exhausted or the write_pointer is immediately - // behind the read pointer; temporarily map write_address and read_address into - // buffer-relative values. - std::size_t characters_written = 0; - write_address -= buffer_start; - read_address -= buffer_start; - while(characters_written < input_text_.size()) { - const int next_write_address = (write_address + 1) % buffer_size; - if(next_write_address == read_address) break; - ram_[write_address + buffer_start] = static_cast(input_text_[characters_written]); - ++characters_written; - write_address = next_write_address; + memory_slots_[slot_hit].handler->run_for(memory_slots_[slot_hit].cycles_since_update.flush()); + memory_slots_[slot_hit].handler->write(address, *cycle.value, read_pointers_[pc_address_ >> 13] != memory_slots_[0].read_pointers[pc_address_ >> 13]); } - input_text_.erase(input_text_.begin(), input_text_.begin() + static_cast(characters_written)); + } break; - // Map the write address back into absolute terms and write it out again as PUTPNT. - write_address += buffer_start; - ram_[0xf3f8] = static_cast(write_address); - ram_[0xf3f9] = static_cast(write_address >> 8); - } - break; + case CPU::Z80::PartialMachineCycle::Input: + switch(address & 0xff) { + case 0x98: case 0x99: + vdp_->run_for(time_since_vdp_update_.flush()); + *cycle.value = vdp_->get_register(address); + z80_.set_interrupt_line(vdp_->get_interrupt_line()); + time_until_interrupt_ = vdp_->get_time_until_interrupt(); + break; - default: break; + case 0xa2: + update_audio(); + ay_.set_control_lines(static_cast(GI::AY38910::BC2 | GI::AY38910::BC1)); + *cycle.value = ay_.get_data_output(); + ay_.set_control_lines(static_cast(0)); + break; + + case 0xa8: case 0xa9: + case 0xaa: case 0xab: + *cycle.value = i8255_.get_register(address); + break; + + default: + *cycle.value = 0xff; + break; + } + break; + + case CPU::Z80::PartialMachineCycle::Output: { + const int port = address & 0xff; + switch(port) { + case 0x98: case 0x99: + vdp_->run_for(time_since_vdp_update_.flush()); + vdp_->set_register(address, *cycle.value); + z80_.set_interrupt_line(vdp_->get_interrupt_line()); + time_until_interrupt_ = vdp_->get_time_until_interrupt(); + break; + + case 0xa0: case 0xa1: + update_audio(); + ay_.set_control_lines(static_cast(GI::AY38910::BDIR | GI::AY38910::BC2 | ((port == 0xa0) ? GI::AY38910::BC1 : 0))); + ay_.set_data_input(*cycle.value); + ay_.set_control_lines(static_cast(0)); + break; + + case 0xa8: case 0xa9: + case 0xaa: case 0xab: + i8255_.set_register(address, *cycle.value); + break; + + case 0xfc: case 0xfd: case 0xfe: case 0xff: + // printf("RAM banking %02x: %02x\n", port, *cycle.value); + break; + } + } break; + + case CPU::Z80::PartialMachineCycle::Interrupt: + *cycle.value = 0xff; + + // Take this as a convenient moment to jump into the keyboard buffer, if desired. + if(!input_text_.empty()) { + // The following are KEYBUF per the Red Book; its address and its definition as DEFS 40. + const int buffer_start = 0xfbf0; + const int buffer_size = 40; + + // Also from the Red Book: GETPNT is at F3FAH and PUTPNT is at F3F8H. + int read_address = ram_[0xf3fa] | (ram_[0xf3fb] << 8); + int write_address = ram_[0xf3f8] | (ram_[0xf3f9] << 8); + + // Write until either the string is exhausted or the write_pointer is immediately + // behind the read pointer; temporarily map write_address and read_address into + // buffer-relative values. + std::size_t characters_written = 0; + write_address -= buffer_start; + read_address -= buffer_start; + while(characters_written < input_text_.size()) { + const int next_write_address = (write_address + 1) % buffer_size; + if(next_write_address == read_address) break; + ram_[write_address + buffer_start] = static_cast(input_text_[characters_written]); + ++characters_written; + write_address = next_write_address; + } + input_text_.erase(input_text_.begin(), input_text_.begin() + static_cast(characters_written)); + + // Map the write address back into absolute terms and write it out again as PUTPNT. + write_address += buffer_start; + ram_[0xf3f8] = static_cast(write_address); + ram_[0xf3f9] = static_cast(write_address >> 8); + } + break; + + default: break; + } } if(!tape_player_is_sleeping_)