diff --git a/Components/6560/6560.hpp b/Components/6560/6560.hpp index bdf455108..cd786888c 100644 --- a/Components/6560/6560.hpp +++ b/Components/6560/6560.hpp @@ -82,19 +82,19 @@ template class MOS6560 { */ void set_output_mode(OutputMode output_mode) { output_mode_ = output_mode; - uint8_t luminances[16] = { // range is 0–4 + const uint8_t luminances[16] = { // range is 0–4 0, 4, 1, 3, 2, 2, 1, 3, 2, 1, 2, 1, 2, 3, 2, 3 }; - uint8_t pal_chrominances[16] = { // range is 0–15; 15 is a special case meaning "no chrominance" + const uint8_t pal_chrominances[16] = { // range is 0–15; 15 is a special case meaning "no chrominance" 15, 15, 5, 13, 2, 10, 0, 8, 6, 7, 5, 13, 2, 10, 0, 8, }; - uint8_t ntsc_chrominances[16] = { + const uint8_t ntsc_chrominances[16] = { 15, 15, 2, 10, 4, 12, 6, 14, 0, 8, 2, 10, 4, 12, 6, 14, }; - uint8_t *chrominances; + const uint8_t *chrominances; Outputs::CRT::DisplayType display_type; switch(output_mode) { diff --git a/Machines/Commodore/Vic-20/Vic20.cpp b/Machines/Commodore/Vic-20/Vic20.cpp index 6251545e5..84d8bd061 100644 --- a/Machines/Commodore/Vic-20/Vic20.cpp +++ b/Machines/Commodore/Vic-20/Vic20.cpp @@ -10,6 +10,7 @@ #include #include "../../../Storage/Tape/Formats/TapePRG.hpp" +#include "../../../Storage/Tape/Parsers/Commodore.hpp" #include "../../../StaticAnalyser/StaticAnalyser.hpp" using namespace Commodore::Vic20; @@ -17,12 +18,13 @@ using namespace Commodore::Vic20; Machine::Machine() : rom_(nullptr), is_running_at_zero_cost_(false), - tape_(1022727) { + tape_(new Storage::Tape::BinaryTapePlayer(1022727)) { // create 6522s, serial port and bus user_port_via_.reset(new UserPortVIA); keyboard_via_.reset(new KeyboardVIA); serial_port_.reset(new SerialPort); serial_bus_.reset(new ::Commodore::Serial::Bus); + user_port_via_->set_tape(tape_); // wire up the serial bus and serial port Commodore::Serial::AttachPortAndBus(serial_port_, serial_bus_); @@ -35,7 +37,7 @@ Machine::Machine() : // wire up the 6522s, tape and machine user_port_via_->set_interrupt_delegate(this); keyboard_via_->set_interrupt_delegate(this); - tape_.set_delegate(this); + tape_->set_delegate(this); // establish the memory maps set_memory_size(MemorySize::Default); @@ -124,11 +126,55 @@ unsigned int Machine::perform_bus_operation(CPU6502::BusOperation operation, uin // PC hits the start of the loop that just waits for an interesting tape interrupt to have // occurred then skip both 6522s and the tape ahead to the next interrupt without any further // CPU or 6560 costs. - if(use_fast_tape_hack_ && tape_.has_tape() && address == 0xf92f && operation == CPU6502::BusOperation::ReadOpcode) { - while(!user_port_via_->get_interrupt_line() && !keyboard_via_->get_interrupt_line() && !tape_.get_tape()->is_at_end()) { - user_port_via_->run_for_cycles(1); - keyboard_via_->run_for_cycles(1); - tape_.run_for_cycles(1); + if(use_fast_tape_hack_ && tape_->has_tape() && operation == CPU6502::BusOperation::ReadOpcode) { + if(address == 0xf7b2) { + // Address 0xf7b2 contains a JSR to 0xf8c0 that will fill the tape buffer with the next header. + // So cancel that via a double NOP and fill in the next header programmatically. + Storage::Tape::Commodore::Parser parser; + std::unique_ptr header = parser.get_next_header(tape_->get_tape()); + + // serialise to wherever b2:b3 points + uint16_t tape_buffer_pointer = (uint16_t)user_basic_memory_[0xb2] | (uint16_t)(user_basic_memory_[0xb3] << 8); + if(header) { + header->serialise(&user_basic_memory_[tape_buffer_pointer], 0x8000 - tape_buffer_pointer); + } else { + // no header found, so store end-of-tape + user_basic_memory_[tape_buffer_pointer] = 0x05; // i.e. end of tape + } + + // clear status and the verify flag + user_basic_memory_[0x90] = 0; + user_basic_memory_[0x93] = 0; + + *value = 0x0c; // i.e. NOP abs + } else if(address == 0xf90b) { + uint8_t x = (uint8_t)get_value_of_register(CPU6502::Register::X); + if(x == 0xe) { + Storage::Tape::Commodore::Parser parser; + std::unique_ptr data = parser.get_next_data(tape_->get_tape()); + uint16_t start_address, end_address; + start_address = (uint16_t)(user_basic_memory_[0xc1] | (user_basic_memory_[0xc2] << 8)); + end_address = (uint16_t)(user_basic_memory_[0xae] | (user_basic_memory_[0xaf] << 8)); + + // perform a via-processor_write_memory_map_ memcpy + uint8_t *data_ptr = data->data.data(); + while(start_address != end_address) { + processor_write_memory_map_[start_address >> 10][start_address & 0x3ff] = *data_ptr; + data_ptr++; + start_address++; + } + + // set tape status, carry and flag + user_basic_memory_[0x90] |= 0x40; + uint8_t flags = (uint8_t)get_value_of_register(CPU6502::Register::Flags); + flags &= ~(uint8_t)(CPU6502::Flag::Carry | CPU6502::Flag::Interrupt); + set_value_of_register(CPU6502::Register::Flags, flags); + + // to ensure that execution proceeds to 0xfccf, pretend a NOP was here and + // ensure that the PC leaps to 0xfccf + set_value_of_register(CPU6502::Register::ProgramCounter, 0xfccf); + *value = 0xea; // i.e. NOP implied + } } } } else { @@ -149,34 +195,9 @@ unsigned int Machine::perform_bus_operation(CPU6502::BusOperation operation, uin typer_.reset(); } } - tape_.run_for_cycles(1); + tape_->run_for_cycles(1); if(c1540_) c1540_->run_for_cycles(1); - // If using fast tape then: - // if the PC hits 0xf98e, the ROM's tape loading routine, then begin zero cost processing; - // if the PC heads into RAM - // - // Where 'zero cost processing' is taken to be taking the 6560 off the bus (because I know it's - // expensive, and not relevant) then running the tape, the CPU and both 6522s as usual but not - // counting cycles towards the processing budget. So the limit is the host machine. - // - // Note the additional test above for PC hitting 0xf92f, which is a loop in the ROM that waits - // for an interesting interrupt. Up there the fast tape hack goes even further in also cutting - // the CPU out of the action. - if(use_fast_tape_hack_ && tape_.has_tape()) { - if(address == 0xf98e && operation == CPU6502::BusOperation::ReadOpcode) { - is_running_at_zero_cost_ = true; - set_clock_is_unlimited(true); - } - if( - (address < 0xe000 && operation == CPU6502::BusOperation::ReadOpcode) || - tape_.get_tape()->is_at_end() - ) { - is_running_at_zero_cost_ = false; - set_clock_is_unlimited(false); - } - } - return 1; } @@ -265,7 +286,7 @@ void Machine::set_rom(ROMSlot slot, size_t length, const uint8_t *data) { void Machine::configure_as_target(const StaticAnalyser::Target &target) { if(target.tapes.size()) { - tape_.set_tape(target.tapes.front()); + tape_->set_tape(target.tapes.front()); } if(target.disks.size()) { @@ -292,8 +313,8 @@ void Machine::configure_as_target(const StaticAnalyser::Target &target) { write_to_map(processor_read_memory_map_, rom_, rom_address_, 0x2000); } - if(should_automatically_load_media_) { - if(target.loadingCommand.length()) { // TODO: and automatic loading option enabled +// if(should_automatically_load_media_) { + if(target.loadingCommand.length()) { set_typer_for_string(target.loadingCommand.c_str()); } @@ -308,7 +329,7 @@ void Machine::configure_as_target(const StaticAnalyser::Target &target) { set_memory_size(ThirtyTwoKB); break; } - } +// } } void Machine::tape_did_change_input(Storage::Tape::BinaryTapePlayer *tape) { @@ -329,15 +350,15 @@ void Machine::install_disk_rom() { uint8_t UserPortVIA::get_port_input(Port port) { if(!port) { - return port_a_; // TODO: bit 6 should be high if there is no tape, low otherwise + return port_a_ | (tape_->has_tape() ? 0x00 : 0x40); } return 0xff; } void UserPortVIA::set_control_line_output(Port port, Line line, bool value) { -// if(port == Port::A && line == Line::Two) { -// printf("Tape motor %s\n", value ? "on" : "off"); -// } + if(port == Port::A && line == Line::Two) { + tape_->set_motor_control(!value); + } } void UserPortVIA::set_serial_line_state(::Commodore::Serial::Line line, bool value) { @@ -369,6 +390,10 @@ void UserPortVIA::set_serial_port(std::shared_ptr<::Commodore::Serial::Port> ser serial_port_ = serialPort; } +void UserPortVIA::set_tape(std::shared_ptr tape) { + tape_ = tape; +} + #pragma mark - KeyboardVIA KeyboardVIA::KeyboardVIA() : port_b_(0xff) { diff --git a/Machines/Commodore/Vic-20/Vic20.hpp b/Machines/Commodore/Vic-20/Vic20.hpp index f226cd75e..c381b5441 100644 --- a/Machines/Commodore/Vic-20/Vic20.hpp +++ b/Machines/Commodore/Vic-20/Vic20.hpp @@ -87,10 +87,12 @@ class UserPortVIA: public MOS::MOS6522, public MOS::MOS6522IRQDeleg void set_port_output(Port port, uint8_t value, uint8_t mask); void set_serial_port(std::shared_ptr<::Commodore::Serial::Port> serialPort); + void set_tape(std::shared_ptr tape); private: uint8_t port_a_; std::weak_ptr<::Commodore::Serial::Port> serial_port_; + std::shared_ptr tape_; }; class KeyboardVIA: public MOS::MOS6522, public MOS::MOS6522IRQDelegate { @@ -164,7 +166,7 @@ class Machine: void set_region(Region region); inline void set_use_fast_tape_hack(bool activate) { use_fast_tape_hack_ = activate; } - inline void set_should_automatically_load_media(bool activate) { should_automatically_load_media_ = activate; } +// inline void set_should_automatically_load_media(bool activate) { should_automatically_load_media_ = activate; } // to satisfy CPU6502::Processor unsigned int perform_bus_operation(CPU6502::BusOperation operation, uint16_t address, uint8_t *value); @@ -213,15 +215,13 @@ class Machine: std::shared_ptr<::Commodore::Serial::Bus> serial_bus_; // Tape - Storage::Tape::BinaryTapePlayer tape_; - bool use_fast_tape_hack_, should_automatically_load_media_; + std::shared_ptr tape_; + bool use_fast_tape_hack_;//, should_automatically_load_media_; bool is_running_at_zero_cost_; // Disk std::shared_ptr<::Commodore::C1540::Machine> c1540_; void install_disk_rom(); - - // Autoload string }; } diff --git a/OSBindings/Mac/Clock Signal/Machine/Wrappers/CSVic20.mm b/OSBindings/Mac/Clock Signal/Machine/Wrappers/CSVic20.mm index bcc3866c5..46fac4973 100644 --- a/OSBindings/Mac/Clock Signal/Machine/Wrappers/CSVic20.mm +++ b/OSBindings/Mac/Clock Signal/Machine/Wrappers/CSVic20.mm @@ -181,7 +181,7 @@ using namespace Commodore::Vic20; - (void)setShouldLoadAutomatically:(BOOL)shouldLoadAutomatically { _shouldLoadAutomatically = shouldLoadAutomatically; @synchronized(self) { - _vic20.set_should_automatically_load_media(shouldLoadAutomatically ? true : false); +// _vic20.set_should_automatically_load_media(shouldLoadAutomatically ? true : false); } } diff --git a/StaticAnalyser/Acorn/StaticAnalyser.cpp b/StaticAnalyser/Acorn/StaticAnalyser.cpp index 362ff74bc..98edd262d 100644 --- a/StaticAnalyser/Acorn/StaticAnalyser.cpp +++ b/StaticAnalyser/Acorn/StaticAnalyser.cpp @@ -74,9 +74,7 @@ void StaticAnalyser::Acorn::AddTargets( // if there are any tapes, attempt to get data from the first if(tapes.size() > 0) { std::shared_ptr tape = tapes.front(); - tape->reset(); std::list files = GetFiles(tape); - tape->reset(); // continue if there are any files if(files.size()) { diff --git a/StaticAnalyser/Commodore/StaticAnalyser.cpp b/StaticAnalyser/Commodore/StaticAnalyser.cpp index 23018563a..c1d6aa472 100644 --- a/StaticAnalyser/Commodore/StaticAnalyser.cpp +++ b/StaticAnalyser/Commodore/StaticAnalyser.cpp @@ -13,6 +13,8 @@ #include "Disk.hpp" #include "../../Storage/Cartridge/Encodings/CommodoreROM.hpp" +#include + using namespace StaticAnalyser::Commodore; static std::list> @@ -75,15 +77,15 @@ void StaticAnalyser::Commodore::AddTargets( if(files.size()) { target.vic20.memory_model = Vic20MemoryModel::Unexpanded; - if(files.front().is_basic()) { - char command[16]; - snprintf(command, 16, "LOAD\"%s\",%d,0\nRUN\n", is_disk ? "*" : "", device); - target.loadingCommand = command; + std::ostringstream string_stream; + string_stream << "LOAD\"" << (is_disk ? "*" : "") << "\"," << device << ","; + if(files.front().is_basic()) { + string_stream << "0"; } else { - char command[16]; - snprintf(command, 16, "LOAD\"%s\",%d,1\nRUN\n", is_disk ? "*" : "", device); - target.loadingCommand = command; + string_stream << "1"; } + string_stream << "\nRUN\n"; + target.loadingCommand = string_stream.str(); // make a first guess based on loading address switch(files.front().starting_address) { diff --git a/StaticAnalyser/Oric/Tape.cpp b/StaticAnalyser/Oric/Tape.cpp index 0df23e064..8ba4e772c 100644 --- a/StaticAnalyser/Oric/Tape.cpp +++ b/StaticAnalyser/Oric/Tape.cpp @@ -15,7 +15,6 @@ std::list StaticAnalyser::Oric::GetFiles(const std::shared_ptr files; Storage::Tape::Oric::Parser parser; - tape->reset(); while(!tape->is_at_end()) { // sync to next lead-in, check that it's one of three 0x16s bool is_fast = parser.sync_and_get_encoding_speed(tape); @@ -81,7 +80,6 @@ std::list StaticAnalyser::Oric::GetFiles(const std::shared_ptrreset(); return files; } diff --git a/StaticAnalyser/StaticAnalyser.cpp b/StaticAnalyser/StaticAnalyser.cpp index 291187996..803890521 100644 --- a/StaticAnalyser/StaticAnalyser.cpp +++ b/StaticAnalyser/StaticAnalyser.cpp @@ -128,5 +128,13 @@ std::list StaticAnalyser::GetTargets(const char *file_name) free(lowercase_extension); } + + // Reset any tapes to their initial position + for(auto target : targets) { + for(auto tape : target.tapes) { + tape->reset(); + } + } + return targets; } diff --git a/Storage/Tape/Parsers/Commodore.cpp b/Storage/Tape/Parsers/Commodore.cpp index 332550638..628ad823d 100644 --- a/Storage/Tape/Parsers/Commodore.cpp +++ b/Storage/Tape/Parsers/Commodore.cpp @@ -119,6 +119,20 @@ std::unique_ptr
Parser::get_next_header_body(const std::shared_ptr Parser::get_next_data_body(const std::shared_ptr &tape, bool is_original) { std::unique_ptr data(new Data); diff --git a/Storage/Tape/Parsers/Commodore.hpp b/Storage/Tape/Parsers/Commodore.hpp index ebde8066d..5b36a83c1 100644 --- a/Storage/Tape/Parsers/Commodore.hpp +++ b/Storage/Tape/Parsers/Commodore.hpp @@ -10,7 +10,6 @@ #define Storage_Tape_Parsers_Commodore_hpp #include "TapeParser.hpp" -//#include "Utilities.hpp" #include #include @@ -43,6 +42,12 @@ struct Header { uint16_t ending_address; bool parity_was_valid; bool duplicate_matched; + + /*! + Writes a byte serialised version of this header to @c target, writing at most + @c length bytes. + */ + void serialise(uint8_t *target, uint16_t length); }; struct Data {