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
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 T> 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]);

View File

@ -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())

View File

@ -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 = "<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>"; };
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>"; };
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>"; };
@ -1529,6 +1532,8 @@
4BC830D01D6E7C690000A26F /* Tape.hpp */,
4BC5E4931D7EE0E0008CF980 /* Utilities.cpp */,
4BC5E4941D7EE0E0008CF980 /* Utilities.hpp */,
4BE77A2C1D84ADFB00BC3827 /* File.cpp */,
4BE77A2D1D84ADFB00BC3827 /* File.hpp */,
);
name = Commodore;
sourceTree = "<group>";
@ -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 */,

View File

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

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 "../StaticAnalyser.hpp"
#include "File.hpp"
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;
};
std::list<File> GetFiles(const std::shared_ptr<Storage::Tape::Tape> &tape);
}

View File

@ -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)
{

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -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<Storage::Tape::Tape> tape)
{
_tape = tape;

View File

@ -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;
};
/*!