1
0
mirror of https://github.com/TomHarte/CLK.git synced 2025-01-11 08:30:55 +00:00

Implemented seeking on tapes, mucked about a bit more with the Commodore analyser, at least temporarily removed cropping from the Vic emulator.

This commit is contained in:
Thomas Harte 2016-09-11 17:09:00 -04:00
parent 1ca4a2a012
commit eeec516fa6
15 changed files with 224 additions and 96 deletions

View File

@ -65,9 +65,6 @@ template <class T> class MOS6560 {
// default to NTSC // default to NTSC
set_output_mode(OutputMode::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) void set_clock_rate(double clock_rate)
@ -125,6 +122,18 @@ template <class T> class MOS6560 {
} }
_crt->set_new_display_type((unsigned int)(_timing.cycles_per_line*4), display_type); _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++) for(int c = 0; c < 16; c++)
{ {
_colours[c] = (uint8_t)((luminances[c] << 4) | chrominances[c]); _colours[c] = (uint8_t)((luminances[c] << 4) | chrominances[c]);

View File

@ -269,6 +269,10 @@ void Machine::set_prg(const char *file_name, size_t length, const uint8_t *data)
#pragma mar - Tape #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) void Machine::configure_as_target(const StaticAnalyser::Target &target)
{ {
if(target.tapes.size()) if(target.tapes.size())

View File

@ -347,6 +347,7 @@
4BC9E1EE1D23449A003FCEE4 /* 6502InterruptTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4BC9E1ED1D23449A003FCEE4 /* 6502InterruptTests.swift */; }; 4BC9E1EE1D23449A003FCEE4 /* 6502InterruptTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4BC9E1ED1D23449A003FCEE4 /* 6502InterruptTests.swift */; };
4BD14B111D74627C0088EAD6 /* AcornAnalyser.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4BD14B0F1D74627C0088EAD6 /* AcornAnalyser.cpp */; }; 4BD14B111D74627C0088EAD6 /* AcornAnalyser.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4BD14B0F1D74627C0088EAD6 /* AcornAnalyser.cpp */; };
4BD5F1951D13528900631CD1 /* CSBestEffortUpdater.m in Sources */ = {isa = PBXBuildFile; fileRef = 4BD5F1941D13528900631CD1 /* CSBestEffortUpdater.m */; }; 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 */; }; 4BEE0A6F1D72496600532C7B /* Cartridge.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4BEE0A6A1D72496600532C7B /* Cartridge.cpp */; };
4BEE0A701D72496600532C7B /* PRG.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4BEE0A6D1D72496600532C7B /* PRG.cpp */; }; 4BEE0A701D72496600532C7B /* PRG.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4BEE0A6D1D72496600532C7B /* PRG.cpp */; };
4BEF6AAA1D35CE9E00E73575 /* DigitalPhaseLockedLoopBridge.mm in Sources */ = {isa = PBXBuildFile; fileRef = 4BEF6AA91D35CE9E00E73575 /* DigitalPhaseLockedLoopBridge.mm */; }; 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 = "<group>"; }; 4BD328FD1D7E3EB5003B8C44 /* TapeParser.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; name = TapeParser.hpp; path = ../../StaticAnalyser/TapeParser.hpp; sourceTree = "<group>"; };
4BD5F1931D13528900631CD1 /* CSBestEffortUpdater.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = CSBestEffortUpdater.h; path = Updater/CSBestEffortUpdater.h; sourceTree = "<group>"; }; 4BD5F1931D13528900631CD1 /* CSBestEffortUpdater.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = CSBestEffortUpdater.h; path = Updater/CSBestEffortUpdater.h; sourceTree = "<group>"; };
4BD5F1941D13528900631CD1 /* CSBestEffortUpdater.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = CSBestEffortUpdater.m; path = Updater/CSBestEffortUpdater.m; sourceTree = "<group>"; }; 4BD5F1941D13528900631CD1 /* CSBestEffortUpdater.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = CSBestEffortUpdater.m; path = Updater/CSBestEffortUpdater.m; sourceTree = "<group>"; };
4BE77A2C1D84ADFB00BC3827 /* File.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = File.cpp; path = ../../StaticAnalyser/Commodore/File.cpp; sourceTree = "<group>"; };
4BE77A2D1D84ADFB00BC3827 /* File.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; name = File.hpp; path = ../../StaticAnalyser/Commodore/File.hpp; sourceTree = "<group>"; };
4BEE0A6A1D72496600532C7B /* Cartridge.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = Cartridge.cpp; sourceTree = "<group>"; }; 4BEE0A6A1D72496600532C7B /* Cartridge.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = Cartridge.cpp; sourceTree = "<group>"; };
4BEE0A6B1D72496600532C7B /* Cartridge.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = Cartridge.hpp; sourceTree = "<group>"; }; 4BEE0A6B1D72496600532C7B /* Cartridge.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = Cartridge.hpp; sourceTree = "<group>"; };
4BEE0A6D1D72496600532C7B /* PRG.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = PRG.cpp; sourceTree = "<group>"; }; 4BEE0A6D1D72496600532C7B /* PRG.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = PRG.cpp; sourceTree = "<group>"; };
@ -1529,6 +1532,8 @@
4BC830D01D6E7C690000A26F /* Tape.hpp */, 4BC830D01D6E7C690000A26F /* Tape.hpp */,
4BC5E4931D7EE0E0008CF980 /* Utilities.cpp */, 4BC5E4931D7EE0E0008CF980 /* Utilities.cpp */,
4BC5E4941D7EE0E0008CF980 /* Utilities.hpp */, 4BC5E4941D7EE0E0008CF980 /* Utilities.hpp */,
4BE77A2C1D84ADFB00BC3827 /* File.cpp */,
4BE77A2D1D84ADFB00BC3827 /* File.hpp */,
); );
name = Commodore; name = Commodore;
sourceTree = "<group>"; sourceTree = "<group>";
@ -2055,6 +2060,7 @@
4BC830D11D6E7C690000A26F /* Tape.cpp in Sources */, 4BC830D11D6E7C690000A26F /* Tape.cpp in Sources */,
4B69FB441C4D941400B5F0AA /* TapeUEF.cpp in Sources */, 4B69FB441C4D941400B5F0AA /* TapeUEF.cpp in Sources */,
4B4DC8211D2C2425003C5BF8 /* Vic20.cpp in Sources */, 4B4DC8211D2C2425003C5BF8 /* Vic20.cpp in Sources */,
4BE77A2E1D84ADFB00BC3827 /* File.cpp in Sources */,
4BAB62B51D327F7E00DF5BA0 /* G64.cpp in Sources */, 4BAB62B51D327F7E00DF5BA0 /* G64.cpp in Sources */,
4BBF99141C8FBA6F0075DAFB /* CRTInputBufferBuilder.cpp in Sources */, 4BBF99141C8FBA6F0075DAFB /* CRTInputBufferBuilder.cpp in Sources */,
4B2409551C45AB05004DA684 /* Speaker.cpp in Sources */, 4B2409551C45AB05004DA684 /* Speaker.cpp in Sources */,

View File

@ -36,54 +36,25 @@ void StaticAnalyser::Commodore::AddTargets(
// continue if there are any files // continue if there are any files
if(files.size()) if(files.size())
{ {
bool is_basic = true; target.tapes = tapes;
// 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.vic20.memory_model = Vic20MemoryModel::Unexpanded; target.vic20.memory_model = Vic20MemoryModel::Unexpanded;
if(is_basic) if(files.front().is_basic())
{ {
target.loadingCommand = "LOAD\"\",1,0\nRUN\n"; 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 // make a first guess based on loading address
switch(files.front().starting_address) switch(files.front().starting_address)
{ {
@ -96,30 +67,49 @@ void StaticAnalyser::Commodore::AddTargets(
target.vic20.memory_model = Vic20MemoryModel::EightKB; target.vic20.memory_model = Vic20MemoryModel::EightKB;
break; 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 // General approach: increase memory size conservatively such that the largest file found will fit.
size_t file_size = files.front().data.size(); for(File &file : files)
if(file_size > 6655) {
size_t file_size = file.data.size();
//bool is_basic = file.is_basic();
/*if(is_basic)
{ {
target.vic20.memory_model = Vic20MemoryModel::ThirtyTwoKB; // BASIC files may be relocated, so the only limit is size.
} //
else if(file_size > 3583) // An unexpanded machine has 3583 bytes free for BASIC;
{ // a 3kb expanded machine has 6655 bytes free.
if(target.vic20.memory_model == Vic20MemoryModel::Unexpanded) 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; target.vic20.memory_model = Vic20MemoryModel::EightKB;
} }
} else
else {*/
{ // if(!file.type == File::NonRelocatableProgram)
// 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); // Non-BASIC files may be relocatable but, if so, by what logic?
target.loadingCommand = "LOAD\"\",1,1\nRUN\n"; // 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;
// }
// }
}
} }
} }

View File

@ -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;
}

View File

@ -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 <string>
#include <vector>
namespace StaticAnalyser {
namespace Commodore {
struct File {
std::wstring name;
std::vector<uint8_t> raw_name;
uint16_t starting_address;
uint16_t ending_address;
enum {
RelocatableProgram,
NonRelocatableProgram,
DataSequence,
} type;
std::vector<uint8_t> data;
bool is_basic();
};
}
}
#endif /* File_hpp */

View File

@ -11,23 +11,11 @@
#include <cstdint> #include <cstdint>
#include "../StaticAnalyser.hpp" #include "../StaticAnalyser.hpp"
#include "File.hpp"
namespace StaticAnalyser { namespace StaticAnalyser {
namespace Commodore { namespace Commodore {
struct File {
std::wstring name;
std::vector<uint8_t> raw_name;
uint16_t starting_address;
uint16_t ending_address;
enum {
RelocatableProgram,
NonRelocatableProgram,
DataSequence,
} type;
std::vector<uint8_t> data;
};
std::list<File> GetFiles(const std::shared_ptr<Storage::Tape::Tape> &tape); std::list<File> GetFiles(const std::shared_ptr<Storage::Tape::Tape> &tape);
} }

View File

@ -58,7 +58,7 @@ CommodoreTAP::~CommodoreTAP()
fclose(_file); fclose(_file);
} }
void CommodoreTAP::reset() void CommodoreTAP::virtual_reset()
{ {
fseek(_file, 0x14, SEEK_SET); fseek(_file, 0x14, SEEK_SET);
_current_pulse.type = Pulse::High; _current_pulse.type = Pulse::High;
@ -70,7 +70,7 @@ bool CommodoreTAP::is_at_end()
return _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) if(_is_at_end)
{ {

View File

@ -33,11 +33,12 @@ class CommodoreTAP: public Tape {
}; };
// implemented to satisfy @c Tape // implemented to satisfy @c Tape
Pulse get_next_pulse();
void reset();
bool is_at_end(); bool is_at_end();
private: private:
void virtual_reset();
Pulse virtual_get_next_pulse();
FILE *_file; FILE *_file;
bool _updated_layout; bool _updated_layout;
uint32_t _file_size; uint32_t _file_size;

View File

@ -74,7 +74,7 @@ PRG::~PRG()
if(_file) fclose(_file); 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 // these are all microseconds per pole
static const unsigned int leader_zero_length = 179; static const unsigned int leader_zero_length = 179;
@ -100,7 +100,7 @@ Storage::Tape::Tape::Pulse PRG::get_next_pulse()
return pulse; return pulse;
} }
void PRG::reset() void PRG::virtual_reset()
{ {
_bitPhase = 3; _bitPhase = 3;
fseek(_file, 2, SEEK_SET); fseek(_file, 2, SEEK_SET);

View File

@ -35,11 +35,12 @@ class PRG: public Tape {
}; };
// implemented to satisfy @c Tape // implemented to satisfy @c Tape
Pulse get_next_pulse();
void reset();
bool is_at_end(); bool is_at_end();
private: private:
Pulse virtual_get_next_pulse();
void virtual_reset();
FILE *_file; FILE *_file;
uint16_t _load_address; uint16_t _load_address;
uint16_t _length; uint16_t _length;

View File

@ -102,7 +102,7 @@ UEF::~UEF()
#pragma mark - Public methods #pragma mark - Public methods
void UEF::reset() void UEF::virtual_reset()
{ {
gzseek(_file, 12, SEEK_SET); gzseek(_file, 12, SEEK_SET);
_is_at_end = false; _is_at_end = false;
@ -114,7 +114,7 @@ bool UEF::is_at_end()
return _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; Pulse next_pulse;

View File

@ -35,11 +35,12 @@ class UEF : public Tape {
}; };
// implemented to satisfy @c Tape // implemented to satisfy @c Tape
Pulse get_next_pulse();
void reset();
bool is_at_end(); bool is_at_end();
private: private:
void virtual_reset();
Pulse virtual_get_next_pulse();
gzFile _file; gzFile _file;
unsigned int _time_base; unsigned int _time_base;
bool _is_at_end; bool _is_at_end;

View File

@ -11,15 +11,38 @@
using namespace Storage::Tape; using namespace Storage::Tape;
void Storage::Tape::Tape::seek(Time seek_time) #pragma mark - Lifecycle
{
// TODO: as best we can
}
TapePlayer::TapePlayer(unsigned int input_clock_rate) : TapePlayer::TapePlayer(unsigned int input_clock_rate) :
TimedEventLoop(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<Storage::Tape::Tape> tape) void TapePlayer::set_tape(std::shared_ptr<Storage::Tape::Tape> tape)
{ {
_tape = tape; _tape = tape;

View File

@ -38,12 +38,31 @@ class Tape {
Pulse() {} 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 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;
}; };
/*! /*!