From eeec516fa60a21a5e411035b065200f7cc6f8c0e Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Sun, 11 Sep 2016 17:09:00 -0400 Subject: [PATCH] Implemented seeking on tapes, mucked about a bit more with the Commodore analyser, at least temporarily removed cropping from the Vic emulator. --- Components/6560/6560.hpp | 15 ++- Machines/Commodore/Vic-20/Vic20.cpp | 4 + .../Clock Signal.xcodeproj/project.pbxproj | 6 + .../Commodore/CommodoreAnalyser.cpp | 112 ++++++++---------- StaticAnalyser/Commodore/File.cpp | 50 ++++++++ StaticAnalyser/Commodore/File.hpp | 36 ++++++ StaticAnalyser/Commodore/Tape.hpp | 14 +-- Storage/Tape/Formats/CommodoreTAP.cpp | 4 +- Storage/Tape/Formats/CommodoreTAP.hpp | 5 +- Storage/Tape/Formats/TapePRG.cpp | 4 +- Storage/Tape/Formats/TapePRG.hpp | 5 +- Storage/Tape/Formats/TapeUEF.cpp | 4 +- Storage/Tape/Formats/TapeUEF.hpp | 5 +- Storage/Tape/Tape.cpp | 31 ++++- Storage/Tape/Tape.hpp | 25 +++- 15 files changed, 224 insertions(+), 96 deletions(-) create mode 100644 StaticAnalyser/Commodore/File.cpp create mode 100644 StaticAnalyser/Commodore/File.hpp diff --git a/Components/6560/6560.hpp b/Components/6560/6560.hpp index b445c5dd3..1211f66ff 100644 --- a/Components/6560/6560.hpp +++ b/Components/6560/6560.hpp @@ -65,9 +65,6 @@ template class MOS6560 { // default to NTSC set_output_mode(OutputMode::NTSC); - - // show only the centre - _crt->set_visible_area(_crt->get_rect_for_area(16, 237, 11*4, 55*4, 4.0f / 3.0f)); } void set_clock_rate(double clock_rate) @@ -125,6 +122,18 @@ template class MOS6560 { } _crt->set_new_display_type((unsigned int)(_timing.cycles_per_line*4), display_type); +// _crt->set_visible_area(Outputs::CRT::Rect(0.1f, 0.1f, 0.8f, 0.8f)); + +// switch(output_mode) +// { +// case OutputMode::PAL: +// _crt->set_visible_area(_crt->get_rect_for_area(16, 237, 15*4, 55*4, 4.0f / 3.0f)); +// break; +// case OutputMode::NTSC: +// _crt->set_visible_area(_crt->get_rect_for_area(16, 237, 11*4, 55*4, 4.0f / 3.0f)); +// break; +// } + for(int c = 0; c < 16; c++) { _colours[c] = (uint8_t)((luminances[c] << 4) | chrominances[c]); diff --git a/Machines/Commodore/Vic-20/Vic20.cpp b/Machines/Commodore/Vic-20/Vic20.cpp index 76eedc4cd..6c0c91ae2 100644 --- a/Machines/Commodore/Vic-20/Vic20.cpp +++ b/Machines/Commodore/Vic-20/Vic20.cpp @@ -269,6 +269,10 @@ void Machine::set_prg(const char *file_name, size_t length, const uint8_t *data) #pragma mar - Tape +// LAB_FBDB = new tape byte setup; +// loops at LAB_F92F +// LAB_F8C0 = initiate tape read + void Machine::configure_as_target(const StaticAnalyser::Target &target) { if(target.tapes.size()) diff --git a/OSBindings/Mac/Clock Signal.xcodeproj/project.pbxproj b/OSBindings/Mac/Clock Signal.xcodeproj/project.pbxproj index 79bc323ab..5d29d7266 100644 --- a/OSBindings/Mac/Clock Signal.xcodeproj/project.pbxproj +++ b/OSBindings/Mac/Clock Signal.xcodeproj/project.pbxproj @@ -347,6 +347,7 @@ 4BC9E1EE1D23449A003FCEE4 /* 6502InterruptTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4BC9E1ED1D23449A003FCEE4 /* 6502InterruptTests.swift */; }; 4BD14B111D74627C0088EAD6 /* AcornAnalyser.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4BD14B0F1D74627C0088EAD6 /* AcornAnalyser.cpp */; }; 4BD5F1951D13528900631CD1 /* CSBestEffortUpdater.m in Sources */ = {isa = PBXBuildFile; fileRef = 4BD5F1941D13528900631CD1 /* CSBestEffortUpdater.m */; }; + 4BE77A2E1D84ADFB00BC3827 /* File.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4BE77A2C1D84ADFB00BC3827 /* File.cpp */; }; 4BEE0A6F1D72496600532C7B /* Cartridge.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4BEE0A6A1D72496600532C7B /* Cartridge.cpp */; }; 4BEE0A701D72496600532C7B /* PRG.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4BEE0A6D1D72496600532C7B /* PRG.cpp */; }; 4BEF6AAA1D35CE9E00E73575 /* DigitalPhaseLockedLoopBridge.mm in Sources */ = {isa = PBXBuildFile; fileRef = 4BEF6AA91D35CE9E00E73575 /* DigitalPhaseLockedLoopBridge.mm */; }; @@ -786,6 +787,8 @@ 4BD328FD1D7E3EB5003B8C44 /* TapeParser.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; name = TapeParser.hpp; path = ../../StaticAnalyser/TapeParser.hpp; sourceTree = ""; }; 4BD5F1931D13528900631CD1 /* CSBestEffortUpdater.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = CSBestEffortUpdater.h; path = Updater/CSBestEffortUpdater.h; sourceTree = ""; }; 4BD5F1941D13528900631CD1 /* CSBestEffortUpdater.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = CSBestEffortUpdater.m; path = Updater/CSBestEffortUpdater.m; sourceTree = ""; }; + 4BE77A2C1D84ADFB00BC3827 /* File.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = File.cpp; path = ../../StaticAnalyser/Commodore/File.cpp; sourceTree = ""; }; + 4BE77A2D1D84ADFB00BC3827 /* File.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; name = File.hpp; path = ../../StaticAnalyser/Commodore/File.hpp; sourceTree = ""; }; 4BEE0A6A1D72496600532C7B /* Cartridge.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = Cartridge.cpp; sourceTree = ""; }; 4BEE0A6B1D72496600532C7B /* Cartridge.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = Cartridge.hpp; sourceTree = ""; }; 4BEE0A6D1D72496600532C7B /* PRG.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = PRG.cpp; sourceTree = ""; }; @@ -1529,6 +1532,8 @@ 4BC830D01D6E7C690000A26F /* Tape.hpp */, 4BC5E4931D7EE0E0008CF980 /* Utilities.cpp */, 4BC5E4941D7EE0E0008CF980 /* Utilities.hpp */, + 4BE77A2C1D84ADFB00BC3827 /* File.cpp */, + 4BE77A2D1D84ADFB00BC3827 /* File.hpp */, ); name = Commodore; sourceTree = ""; @@ -2055,6 +2060,7 @@ 4BC830D11D6E7C690000A26F /* Tape.cpp in Sources */, 4B69FB441C4D941400B5F0AA /* TapeUEF.cpp in Sources */, 4B4DC8211D2C2425003C5BF8 /* Vic20.cpp in Sources */, + 4BE77A2E1D84ADFB00BC3827 /* File.cpp in Sources */, 4BAB62B51D327F7E00DF5BA0 /* G64.cpp in Sources */, 4BBF99141C8FBA6F0075DAFB /* CRTInputBufferBuilder.cpp in Sources */, 4B2409551C45AB05004DA684 /* Speaker.cpp in Sources */, diff --git a/StaticAnalyser/Commodore/CommodoreAnalyser.cpp b/StaticAnalyser/Commodore/CommodoreAnalyser.cpp index 22b9d74ec..f5da45244 100644 --- a/StaticAnalyser/Commodore/CommodoreAnalyser.cpp +++ b/StaticAnalyser/Commodore/CommodoreAnalyser.cpp @@ -36,54 +36,25 @@ void StaticAnalyser::Commodore::AddTargets( // continue if there are any files if(files.size()) { - bool is_basic = true; - - // decide whether this is a BASIC file based on the proposition that: - // (1) they're always relocatable; and - // (2) they have a per-line structure of: - // [4 bytes: address of start of next line] - // [4 bytes: this line number] - // ... null-terminated code ... - // (with a next line address of 0000 indicating end of program)ß - if(files.front().type != File::RelocatableProgram) is_basic = false; - else - { - uint16_t line_address = 0; - int line_number = -1; - - uint16_t starting_address = files.front().starting_address; - line_address = starting_address; - is_basic = false; - while(1) - { - if(line_address - starting_address >= files.front().data.size() + 2) break; - - uint16_t next_line_address = files.front().data[line_address - starting_address]; - next_line_address |= files.front().data[line_address - starting_address + 1] << 8; - - if(!next_line_address) - { - is_basic = true; - break; - } - if(next_line_address < line_address + 5) break; - - if(line_address - starting_address >= files.front().data.size() + 5) break; - uint16_t next_line_number = files.front().data[line_address - starting_address + 2]; - next_line_number |= files.front().data[line_address - starting_address + 3] << 8; - - if(next_line_number <= line_number) break; - - line_number = (uint16_t)next_line_number; - line_address = next_line_address; - } - } + target.tapes = tapes; target.vic20.memory_model = Vic20MemoryModel::Unexpanded; - if(is_basic) + if(files.front().is_basic()) { target.loadingCommand = "LOAD\"\",1,0\nRUN\n"; + // make a first guess based on file size + size_t file_size = files.front().data.size(); + if(file_size > 6655) target.vic20.memory_model = Vic20MemoryModel::ThirtyTwoKB; + else if(file_size > 3583) target.vic20.memory_model = Vic20MemoryModel::EightKB; + else target.vic20.memory_model = Vic20MemoryModel::Unexpanded; + } + else + { + // TODO: this is machine code. So, ummm? + printf("Need to deal with machine code from %04x to %04x???\n", files.front().starting_address, files.front().ending_address); + target.loadingCommand = "LOAD\"\",1,1\nRUN\n"; + // make a first guess based on loading address switch(files.front().starting_address) { @@ -96,30 +67,49 @@ void StaticAnalyser::Commodore::AddTargets( target.vic20.memory_model = Vic20MemoryModel::EightKB; break; } + } - // An unexpanded machine has 3583 bytes free for BASIC; - // a 3kb expanded machine has 6655 bytes free. - // we'll be relocating though, so up size if necessary - size_t file_size = files.front().data.size(); - if(file_size > 6655) + // General approach: increase memory size conservatively such that the largest file found will fit. + for(File &file : files) + { + size_t file_size = file.data.size(); + //bool is_basic = file.is_basic(); + + /*if(is_basic) { - target.vic20.memory_model = Vic20MemoryModel::ThirtyTwoKB; - } - else if(file_size > 3583) - { - if(target.vic20.memory_model == Vic20MemoryModel::Unexpanded) + // BASIC files may be relocated, so the only limit is size. + // + // An unexpanded machine has 3583 bytes free for BASIC; + // a 3kb expanded machine has 6655 bytes free. + if(file_size > 6655) + target.vic20.memory_model = Vic20MemoryModel::ThirtyTwoKB; + else if(target.vic20.memory_model == Vic20MemoryModel::Unexpanded && file_size > 3583) target.vic20.memory_model = Vic20MemoryModel::EightKB; } - } - else - { - // TODO: this is machine code. So, ummm? - printf("Need to deal with machine code from %04x to %04x???\n", files.front().starting_address, files.front().ending_address); - target.loadingCommand = "LOAD\"\",1,1\nRUN\n"; - } + else + {*/ +// if(!file.type == File::NonRelocatableProgram) +// { + // Non-BASIC files may be relocatable but, if so, by what logic? + // Given that this is unknown, take starting address as literal + // and check against memory windows. + // + // (ignoring colour memory...) + // An unexpanded Vic has memory between 0x0000 and 0x0400; and between 0x1000 and 0x2000. + // A 3kb expanded Vic fills in the gap and has memory between 0x0000 and 0x2000. + // A 32kb expanded Vic has memory in the entire low 32kb. + uint16_t starting_address = file.starting_address; - target.tapes = tapes; + // If anything above the 8kb mark is touched, mark as a 32kb machine; otherwise if the + // region 0x0400 to 0x1000 is touched and this is an unexpanded machine, mark as 3kb. + if(starting_address + file_size > 0x2000) + target.vic20.memory_model = Vic20MemoryModel::ThirtyTwoKB; + else if(target.vic20.memory_model == Vic20MemoryModel::Unexpanded && !(starting_address >= 0x1000 || starting_address+file_size < 0x0400)) + target.vic20.memory_model = Vic20MemoryModel::ThirtyTwoKB; +// } + // } + } } } diff --git a/StaticAnalyser/Commodore/File.cpp b/StaticAnalyser/Commodore/File.cpp new file mode 100644 index 000000000..c8b887bfd --- /dev/null +++ b/StaticAnalyser/Commodore/File.cpp @@ -0,0 +1,50 @@ +// +// File.cpp +// Clock Signal +// +// Created by Thomas Harte on 10/09/2016. +// Copyright © 2016 Thomas Harte. All rights reserved. +// + +#include "File.hpp" + +bool StaticAnalyser::Commodore::File::is_basic() +{ + // BASIC files are always relocatable (?) + if(type != File::RelocatableProgram) return false; + + uint16_t line_address = starting_address; + int line_number = -1; + + // decide whether this is a BASIC file based on the proposition that: + // (1) they're always relocatable; and + // (2) they have a per-line structure of: + // [4 bytes: address of start of next line] + // [4 bytes: this line number] + // ... null-terminated code ... + // (with a next line address of 0000 indicating end of program)ß + while(1) + { + if(line_address - starting_address >= data.size() + 2) break; + + uint16_t next_line_address = data[line_address - starting_address]; + next_line_address |= data[line_address - starting_address + 1] << 8; + + if(!next_line_address) + { + return true; + } + if(next_line_address < line_address + 5) break; + + if(line_address - starting_address >= data.size() + 5) break; + uint16_t next_line_number = data[line_address - starting_address + 2]; + next_line_number |= data[line_address - starting_address + 3] << 8; + + if(next_line_number <= line_number) break; + + line_number = (uint16_t)next_line_number; + line_address = next_line_address; + } + + return false; +} diff --git a/StaticAnalyser/Commodore/File.hpp b/StaticAnalyser/Commodore/File.hpp new file mode 100644 index 000000000..ea81aa493 --- /dev/null +++ b/StaticAnalyser/Commodore/File.hpp @@ -0,0 +1,36 @@ +// +// File.hpp +// Clock Signal +// +// Created by Thomas Harte on 10/09/2016. +// Copyright © 2016 Thomas Harte. All rights reserved. +// + +#ifndef File_hpp +#define File_hpp + +#include +#include + +namespace StaticAnalyser { +namespace Commodore { + +struct File { + std::wstring name; + std::vector raw_name; + uint16_t starting_address; + uint16_t ending_address; + enum { + RelocatableProgram, + NonRelocatableProgram, + DataSequence, + } type; + std::vector data; + + bool is_basic(); +}; + +} +} + +#endif /* File_hpp */ diff --git a/StaticAnalyser/Commodore/Tape.hpp b/StaticAnalyser/Commodore/Tape.hpp index f156fd02a..9a16bcdd0 100644 --- a/StaticAnalyser/Commodore/Tape.hpp +++ b/StaticAnalyser/Commodore/Tape.hpp @@ -11,23 +11,11 @@ #include #include "../StaticAnalyser.hpp" +#include "File.hpp" namespace StaticAnalyser { namespace Commodore { -struct File { - std::wstring name; - std::vector raw_name; - uint16_t starting_address; - uint16_t ending_address; - enum { - RelocatableProgram, - NonRelocatableProgram, - DataSequence, - } type; - std::vector data; -}; - std::list GetFiles(const std::shared_ptr &tape); } diff --git a/Storage/Tape/Formats/CommodoreTAP.cpp b/Storage/Tape/Formats/CommodoreTAP.cpp index 556c55fd0..9f7c50697 100644 --- a/Storage/Tape/Formats/CommodoreTAP.cpp +++ b/Storage/Tape/Formats/CommodoreTAP.cpp @@ -58,7 +58,7 @@ CommodoreTAP::~CommodoreTAP() fclose(_file); } -void CommodoreTAP::reset() +void CommodoreTAP::virtual_reset() { fseek(_file, 0x14, SEEK_SET); _current_pulse.type = Pulse::High; @@ -70,7 +70,7 @@ bool CommodoreTAP::is_at_end() return _is_at_end; } -Storage::Tape::Tape::Pulse CommodoreTAP::get_next_pulse() +Storage::Tape::Tape::Pulse CommodoreTAP::virtual_get_next_pulse() { if(_is_at_end) { diff --git a/Storage/Tape/Formats/CommodoreTAP.hpp b/Storage/Tape/Formats/CommodoreTAP.hpp index 836071447..0f62e8040 100644 --- a/Storage/Tape/Formats/CommodoreTAP.hpp +++ b/Storage/Tape/Formats/CommodoreTAP.hpp @@ -33,11 +33,12 @@ class CommodoreTAP: public Tape { }; // implemented to satisfy @c Tape - Pulse get_next_pulse(); - void reset(); bool is_at_end(); private: + void virtual_reset(); + Pulse virtual_get_next_pulse(); + FILE *_file; bool _updated_layout; uint32_t _file_size; diff --git a/Storage/Tape/Formats/TapePRG.cpp b/Storage/Tape/Formats/TapePRG.cpp index 80d723191..4013960f3 100644 --- a/Storage/Tape/Formats/TapePRG.cpp +++ b/Storage/Tape/Formats/TapePRG.cpp @@ -74,7 +74,7 @@ PRG::~PRG() if(_file) fclose(_file); } -Storage::Tape::Tape::Pulse PRG::get_next_pulse() +Storage::Tape::Tape::Pulse PRG::virtual_get_next_pulse() { // these are all microseconds per pole static const unsigned int leader_zero_length = 179; @@ -100,7 +100,7 @@ Storage::Tape::Tape::Pulse PRG::get_next_pulse() return pulse; } -void PRG::reset() +void PRG::virtual_reset() { _bitPhase = 3; fseek(_file, 2, SEEK_SET); diff --git a/Storage/Tape/Formats/TapePRG.hpp b/Storage/Tape/Formats/TapePRG.hpp index f9159fecf..f092532ed 100644 --- a/Storage/Tape/Formats/TapePRG.hpp +++ b/Storage/Tape/Formats/TapePRG.hpp @@ -35,11 +35,12 @@ class PRG: public Tape { }; // implemented to satisfy @c Tape - Pulse get_next_pulse(); - void reset(); bool is_at_end(); private: + Pulse virtual_get_next_pulse(); + void virtual_reset(); + FILE *_file; uint16_t _load_address; uint16_t _length; diff --git a/Storage/Tape/Formats/TapeUEF.cpp b/Storage/Tape/Formats/TapeUEF.cpp index 9ecf4e1ab..bd5e3075c 100644 --- a/Storage/Tape/Formats/TapeUEF.cpp +++ b/Storage/Tape/Formats/TapeUEF.cpp @@ -102,7 +102,7 @@ UEF::~UEF() #pragma mark - Public methods -void UEF::reset() +void UEF::virtual_reset() { gzseek(_file, 12, SEEK_SET); _is_at_end = false; @@ -114,7 +114,7 @@ bool UEF::is_at_end() return _is_at_end; } -Storage::Tape::Tape::Pulse UEF::get_next_pulse() +Storage::Tape::Tape::Pulse UEF::virtual_get_next_pulse() { Pulse next_pulse; diff --git a/Storage/Tape/Formats/TapeUEF.hpp b/Storage/Tape/Formats/TapeUEF.hpp index d879a4309..306f5273a 100644 --- a/Storage/Tape/Formats/TapeUEF.hpp +++ b/Storage/Tape/Formats/TapeUEF.hpp @@ -35,11 +35,12 @@ class UEF : public Tape { }; // implemented to satisfy @c Tape - Pulse get_next_pulse(); - void reset(); bool is_at_end(); private: + void virtual_reset(); + Pulse virtual_get_next_pulse(); + gzFile _file; unsigned int _time_base; bool _is_at_end; diff --git a/Storage/Tape/Tape.cpp b/Storage/Tape/Tape.cpp index e8856d2c1..8cff0314c 100644 --- a/Storage/Tape/Tape.cpp +++ b/Storage/Tape/Tape.cpp @@ -11,15 +11,38 @@ using namespace Storage::Tape; -void Storage::Tape::Tape::seek(Time seek_time) -{ - // TODO: as best we can -} +#pragma mark - Lifecycle TapePlayer::TapePlayer(unsigned int input_clock_rate) : TimedEventLoop(input_clock_rate) {} +#pragma mark - Seeking + +void Storage::Tape::Tape::seek(Time &seek_time) +{ + _current_time.set_zero(); + _next_time.set_zero(); + while(_next_time < seek_time) get_next_pulse(); +} + +void Storage::Tape::Tape::reset() +{ + _current_time.set_zero(); + _next_time.set_zero(); + virtual_reset(); +} + +Tape::Pulse Tape::get_next_pulse() +{ + Tape::Pulse pulse = virtual_get_next_pulse(); + _current_time = _next_time; + _next_time += pulse.length; + return pulse; +} + +#pragma mark - Player + void TapePlayer::set_tape(std::shared_ptr tape) { _tape = tape; diff --git a/Storage/Tape/Tape.hpp b/Storage/Tape/Tape.hpp index e7c4749a0..ca3100f9b 100644 --- a/Storage/Tape/Tape.hpp +++ b/Storage/Tape/Tape.hpp @@ -38,12 +38,31 @@ class Tape { Pulse() {} }; - virtual Pulse get_next_pulse() = 0; + /*! + If at the start of the tape returns the first stored pulse. Otherwise advances past + the last-returned pulse and returns the next. - virtual void reset() = 0; + @returns the pulse that begins at the current cursor position. + */ + Pulse get_next_pulse(); + + /// Returns the tape to the beginning. + void reset(); + + /// @returns @c true if the tape has progressed beyond all recorded content; @c false otherwise. virtual bool is_at_end() = 0; - virtual void seek(Time seek_time); // TODO + /// @returns the amount of time preceeding the most recently-returned pulse. + virtual Time get_current_time() { return _current_time; } + + /// Advances or reverses the tape to the last time before or at @c time from which a pulse starts. + virtual void seek(Time &time); + + private: + Time _current_time, _next_time; + + virtual Pulse virtual_get_next_pulse() = 0; + virtual void virtual_reset() = 0; }; /*!