diff --git a/Machines/Commodore/Vic-20/Vic20.cpp b/Machines/Commodore/Vic-20/Vic20.cpp index 555ae488c..380009ba9 100644 --- a/Machines/Commodore/Vic-20/Vic20.cpp +++ b/Machines/Commodore/Vic-20/Vic20.cpp @@ -9,6 +9,7 @@ #include "Vic20.hpp" #include +#include "../../../Storage/Tape/Formats/TapePRG.hpp" using namespace Commodore::Vic20; @@ -218,7 +219,7 @@ void Machine::set_rom(ROMSlot slot, size_t length, const uint8_t *data) } } -void Machine::add_prg(size_t length, const uint8_t *data) +void Machine::set_prg(const char *file_name, size_t length, const uint8_t *data) { if(length > 2) { @@ -237,16 +238,8 @@ void Machine::add_prg(size_t length, const uint8_t *data) } else { - // TODO: write to virtual media (tape, probably?), load normally. - data += 2; - while(_rom_length) - { - uint8_t *ram = _processorWriteMemoryMap[_rom_address >> 10]; - if(ram) ram[_rom_address & 0x3ff] = *data; - data++; - _rom_length--; - _rom_address++; - } + // if it's not a ROM then insert it as a tape + set_tape(std::shared_ptr(new Storage::TapePRG(file_name))); } } } diff --git a/Machines/Commodore/Vic-20/Vic20.hpp b/Machines/Commodore/Vic-20/Vic20.hpp index e4fb58386..bb4121dd9 100644 --- a/Machines/Commodore/Vic-20/Vic20.hpp +++ b/Machines/Commodore/Vic-20/Vic20.hpp @@ -260,7 +260,7 @@ class Machine: ~Machine(); void set_rom(ROMSlot slot, size_t length, const uint8_t *data); - void add_prg(size_t length, const uint8_t *data); + void set_prg(const char *file_name, size_t length, const uint8_t *data); void set_tape(std::shared_ptr tape); void set_disk(std::shared_ptr disk); diff --git a/OSBindings/Mac/Clock Signal.xcodeproj/project.pbxproj b/OSBindings/Mac/Clock Signal.xcodeproj/project.pbxproj index 9a9652a66..060d6ef83 100644 --- a/OSBindings/Mac/Clock Signal.xcodeproj/project.pbxproj +++ b/OSBindings/Mac/Clock Signal.xcodeproj/project.pbxproj @@ -22,6 +22,7 @@ 4B2A53A11D117D36003C6002 /* CSAtari2600.mm in Sources */ = {isa = PBXBuildFile; fileRef = 4B2A539A1D117D36003C6002 /* CSAtari2600.mm */; }; 4B2A53A21D117D36003C6002 /* CSElectron.mm in Sources */ = {isa = PBXBuildFile; fileRef = 4B2A539C1D117D36003C6002 /* CSElectron.mm */; }; 4B2A53A31D117D36003C6002 /* CSVic20.mm in Sources */ = {isa = PBXBuildFile; fileRef = 4B2A539E1D117D36003C6002 /* CSVic20.mm */; }; + 4B2BFC5F1D613E0200BA3AA9 /* TapePRG.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B2BFC5D1D613E0200BA3AA9 /* TapePRG.cpp */; }; 4B2E2D951C399D1200138695 /* ElectronDocument.xib in Resources */ = {isa = PBXBuildFile; fileRef = 4B2E2D931C399D1200138695 /* ElectronDocument.xib */; }; 4B2E2D9A1C3A06EC00138695 /* Atari2600.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B2E2D971C3A06EC00138695 /* Atari2600.cpp */; }; 4B2E2D9D1C3A070400138695 /* Electron.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B2E2D9B1C3A070400138695 /* Electron.cpp */; }; @@ -393,6 +394,8 @@ 4B2A539C1D117D36003C6002 /* CSElectron.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = CSElectron.mm; sourceTree = ""; }; 4B2A539D1D117D36003C6002 /* CSVic20.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CSVic20.h; sourceTree = ""; }; 4B2A539E1D117D36003C6002 /* CSVic20.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = CSVic20.mm; sourceTree = ""; }; + 4B2BFC5D1D613E0200BA3AA9 /* TapePRG.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = TapePRG.cpp; sourceTree = ""; }; + 4B2BFC5E1D613E0200BA3AA9 /* TapePRG.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = TapePRG.hpp; sourceTree = ""; }; 4B2E2D941C399D1200138695 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = Base; path = Base.lproj/ElectronDocument.xib; sourceTree = ""; }; 4B2E2D971C3A06EC00138695 /* Atari2600.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = Atari2600.cpp; sourceTree = ""; }; 4B2E2D981C3A06EC00138695 /* Atari2600.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = Atari2600.hpp; sourceTree = ""; }; @@ -1004,6 +1007,8 @@ 4B69FB431C4D941400B5F0AA /* TapeUEF.hpp */, 4BC91B811D1F160E00884B76 /* CommodoreTAP.cpp */, 4BC91B821D1F160E00884B76 /* CommodoreTAP.hpp */, + 4B2BFC5D1D613E0200BA3AA9 /* TapePRG.cpp */, + 4B2BFC5E1D613E0200BA3AA9 /* TapePRG.hpp */, ); path = Formats; sourceTree = ""; @@ -1908,6 +1913,7 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( + 4B2BFC5F1D613E0200BA3AA9 /* TapePRG.cpp in Sources */, 4BAB62AD1D3272D200DF5BA0 /* Disk.cpp in Sources */, 4BC9DF4F1D04691600F44158 /* 6560.cpp in Sources */, 4BB697CE1D4BA44400248BDF /* CommodoreGCR.cpp in Sources */, diff --git a/OSBindings/Mac/Clock Signal/Documents/Vic20Document.swift b/OSBindings/Mac/Clock Signal/Documents/Vic20Document.swift index b55a42427..6eb656b3b 100644 --- a/OSBindings/Mac/Clock Signal/Documents/Vic20Document.swift +++ b/OSBindings/Mac/Clock Signal/Documents/Vic20Document.swift @@ -47,6 +47,7 @@ class Vic20Document: MachineDocument { case "tap": vic20.openTAPAtURL(url) case "g64": vic20.openG64AtURL(url) case "d64": vic20.openD64AtURL(url) + case "prg": vic20.openPRGAtURL(url) default: let fileWrapper = try NSFileWrapper(URL: url, options: NSFileWrapperReadingOptions(rawValue: 0)) try self.readFromFileWrapper(fileWrapper, ofType: typeName) @@ -59,10 +60,6 @@ class Vic20Document: MachineDocument { return dataForResource(name, ofType: "bin", inDirectory: "ROMImages/Vic20") } - override func readFromData(data: NSData, ofType typeName: String) throws { - vic20.setPRG(data) - } - // MARK: automatic loading tick box @IBOutlet var loadAutomaticallyButton: NSButton? var autoloadingUserDefaultsKey: String { diff --git a/OSBindings/Mac/Clock Signal/Machine/Wrappers/CSVic20.h b/OSBindings/Mac/Clock Signal/Machine/Wrappers/CSVic20.h index c8ade6c34..c2e17fe61 100644 --- a/OSBindings/Mac/Clock Signal/Machine/Wrappers/CSVic20.h +++ b/OSBindings/Mac/Clock Signal/Machine/Wrappers/CSVic20.h @@ -30,7 +30,7 @@ typedef NS_ENUM(NSInteger, CSVic20MemorySize) - (void)setCharactersROM:(nonnull NSData *)rom; - (void)setDriveROM:(nonnull NSData *)rom; -- (void)setPRG:(nonnull NSData *)prg; +- (BOOL)openPRGAtURL:(nonnull NSURL *)URL; - (BOOL)openTAPAtURL:(nonnull NSURL *)URL; - (BOOL)openG64AtURL:(nonnull NSURL *)URL; - (BOOL)openD64AtURL:(nonnull NSURL *)URL; diff --git a/OSBindings/Mac/Clock Signal/Machine/Wrappers/CSVic20.mm b/OSBindings/Mac/Clock Signal/Machine/Wrappers/CSVic20.mm index 5108741a1..0f03fb7e2 100644 --- a/OSBindings/Mac/Clock Signal/Machine/Wrappers/CSVic20.mm +++ b/OSBindings/Mac/Clock Signal/Machine/Wrappers/CSVic20.mm @@ -82,9 +82,15 @@ using namespace Commodore::Vic20; } } -- (void)setPRG:(nonnull NSData *)prg { +- (BOOL)openPRGAtURL:(NSURL *)URL { + NSData *prg = [NSData dataWithContentsOfURL:URL]; @synchronized(self) { - _vic20.add_prg(prg.length, (const uint8_t *)prg.bytes); + try { + _vic20.set_prg(URL.fileSystemRepresentation, prg.length, (const uint8_t *)prg.bytes); + return YES; + } catch(...) { + return NO; + } } } diff --git a/Storage/Tape/Formats/TapePRG.cpp b/Storage/Tape/Formats/TapePRG.cpp new file mode 100644 index 000000000..07d89ee2f --- /dev/null +++ b/Storage/Tape/Formats/TapePRG.cpp @@ -0,0 +1,49 @@ +// +// TapePRG.cpp +// Clock Signal +// +// Created by Thomas Harte on 14/08/2016. +// Copyright © 2016 Thomas Harte. All rights reserved. +// + +#include "TapePRG.hpp" + +#include + +using namespace Storage; + +TapePRG::TapePRG(const char *file_name) : _file(nullptr) +{ + struct stat file_stats; + stat(file_name, &file_stats); + + // There's really no way to validate other than that if this file is larger than 64kb, + // of if load address + length > 65536 then it's broken. + if(file_stats.st_size >= 65538 || file_stats.st_size < 3) + throw ErrorBadFormat; + + _file = fopen(file_name, "rb"); + if(!_file) throw ErrorBadFormat; + + _load_address = (uint16_t)fgetc(_file); + _load_address |= (uint16_t)fgetc(_file) << 8; + + if (_load_address + file_stats.st_size >= 65536) + throw ErrorBadFormat; +} + +TapePRG::~TapePRG() +{ + if(_file) fclose(_file); +} + +Tape::Pulse TapePRG::get_next_pulse() +{ + Tape::Pulse pulse; + return pulse; +} + +void TapePRG::reset() +{ + // TODO +} diff --git a/Storage/Tape/Formats/TapePRG.hpp b/Storage/Tape/Formats/TapePRG.hpp new file mode 100644 index 000000000..1aaabfc80 --- /dev/null +++ b/Storage/Tape/Formats/TapePRG.hpp @@ -0,0 +1,58 @@ +// +// TapePRG.hpp +// Clock Signal +// +// Created by Thomas Harte on 14/08/2016. +// Copyright © 2016 Thomas Harte. All rights reserved. +// + +#ifndef TapePRG_hpp +#define TapePRG_hpp + +#include "../Tape.hpp" +#include + +namespace Storage { + +/*! + Provides a @c Tape containing a .PRG, which is a direct local file. +*/ +class TapePRG: public Tape { + public: + /*! + Constructs a @c T64 containing content from the file with name @c file_name, of type @c type. + + @param file_name The name of the file to load. + @param type The type of data the file should contain. + @throws ErrorBadFormat if this file could not be opened and recognised as the specified type. + */ + TapePRG(const char *file_name); + ~TapePRG(); + + enum { + ErrorBadFormat + }; + + // implemented to satisfy @c Tape + Pulse get_next_pulse(); + void reset(); + private: + FILE *_file; + uint16_t _load_address; + + enum FilePhase { + FilePhaseLeadIn, + FilePhaseHeader + } _filePhase; + + enum BitPhase { + BitPhase0, + BitPhase1, + BitPhase2, + BitPhase3 + } _bitPhase; +}; + +} + +#endif /* T64_hpp */