mirror of
https://github.com/TomHarte/CLK.git
synced 2025-01-11 08:30:55 +00:00
Merge pull request #24 from TomHarte/RAMPRGs
Adds a check for whether PRGs should be loaded to RAM and, if so, installs them post-boot and performs a 'RUN'
This commit is contained in:
commit
ee050d71ec
58
Machines/Typer.cpp
Normal file
58
Machines/Typer.cpp
Normal file
@ -0,0 +1,58 @@
|
||||
//
|
||||
// Typer.cpp
|
||||
// Clock Signal
|
||||
//
|
||||
// Created by Thomas Harte on 19/06/2016.
|
||||
// Copyright © 2016 Thomas Harte. All rights reserved.
|
||||
//
|
||||
|
||||
#include "Typer.hpp"
|
||||
#include <stdlib.h>
|
||||
|
||||
using namespace Utility;
|
||||
|
||||
Typer::Typer(const char *string, int delay, int frequency, Delegate *delegate) :
|
||||
_counter(-delay), _frequency(frequency), _string(strdup(string)), _string_pointer(0), _delegate(delegate), _phase(0) {}
|
||||
|
||||
void Typer::update(int duration)
|
||||
{
|
||||
if(_string)
|
||||
{
|
||||
if(_counter < 0 && _counter + duration >= 0)
|
||||
{
|
||||
type_next_character();
|
||||
}
|
||||
|
||||
_counter += duration;
|
||||
while(_counter > _frequency)
|
||||
{
|
||||
_counter -= _frequency;
|
||||
type_next_character();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Typer::type_next_character()
|
||||
{
|
||||
if(_delegate->typer_set_next_character(this, _string[_string_pointer], _phase))
|
||||
{
|
||||
_phase = 0;
|
||||
if(!_string[_string_pointer])
|
||||
{
|
||||
free(_string);
|
||||
_string = nullptr;
|
||||
return;
|
||||
}
|
||||
|
||||
_string_pointer++;
|
||||
}
|
||||
else
|
||||
{
|
||||
_phase++;
|
||||
}
|
||||
}
|
||||
|
||||
Typer::~Typer()
|
||||
{
|
||||
free(_string);
|
||||
}
|
53
Machines/Typer.hpp
Normal file
53
Machines/Typer.hpp
Normal file
@ -0,0 +1,53 @@
|
||||
//
|
||||
// Typer.hpp
|
||||
// Clock Signal
|
||||
//
|
||||
// Created by Thomas Harte on 19/06/2016.
|
||||
// Copyright © 2016 Thomas Harte. All rights reserved.
|
||||
//
|
||||
|
||||
#ifndef Typer_hpp
|
||||
#define Typer_hpp
|
||||
|
||||
#include <memory>
|
||||
|
||||
namespace Utility {
|
||||
|
||||
class Typer {
|
||||
public:
|
||||
class Delegate {
|
||||
public:
|
||||
virtual bool typer_set_next_character(Typer *typer, char character, int phase) = 0;
|
||||
};
|
||||
|
||||
Typer(const char *string, int delay, int frequency, Delegate *delegate);
|
||||
~Typer();
|
||||
void update(int duration);
|
||||
|
||||
private:
|
||||
char *_string;
|
||||
int _frequency;
|
||||
int _counter;
|
||||
int _phase;
|
||||
Delegate *_delegate;
|
||||
size_t _string_pointer;
|
||||
|
||||
void type_next_character();
|
||||
};
|
||||
|
||||
class TypeRecipient: public Typer::Delegate {
|
||||
public:
|
||||
void set_typer_for_string(const char *string)
|
||||
{
|
||||
_typer = std::unique_ptr<Typer>(new Typer(string, get_typer_delay(), get_typer_frequency(), this));
|
||||
}
|
||||
|
||||
protected:
|
||||
virtual int get_typer_delay() = 0;
|
||||
virtual int get_typer_frequency() = 0;
|
||||
std::unique_ptr<Typer> _typer;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif /* Typer_hpp */
|
@ -81,6 +81,7 @@ unsigned int Machine::perform_bus_operation(CPU6502::BusOperation operation, uin
|
||||
|
||||
_userPortVIA.run_for_half_cycles(2);
|
||||
_keyboardVIA.run_for_half_cycles(2);
|
||||
if(_typer) _typer->update(1);
|
||||
return 1;
|
||||
}
|
||||
|
||||
@ -125,12 +126,125 @@ void Machine::add_prg(size_t length, const uint8_t *data)
|
||||
_rom_length = (uint16_t)(length - 2);
|
||||
if(_rom_address >= 0x1000 && _rom_address+_rom_length < 0x2000)
|
||||
{
|
||||
memcpy(&_screenMemory[_rom_address - 0x1000], &data[2], length - 2);
|
||||
}
|
||||
else
|
||||
{
|
||||
_rom = new uint8_t[length - 2];
|
||||
memcpy(_rom, &data[2], length - 2);
|
||||
set_typer_for_string("RUN\n");
|
||||
}
|
||||
|
||||
_rom = new uint8_t[length - 2];
|
||||
memcpy(_rom, &data[2], length - 2);
|
||||
}
|
||||
}
|
||||
|
||||
#pragma mark - Typer
|
||||
|
||||
int Machine::get_typer_delay()
|
||||
{
|
||||
return get_reset_line() ? 1*263*60*65 : 0; // wait two seconds if resetting
|
||||
}
|
||||
|
||||
int Machine::get_typer_frequency()
|
||||
{
|
||||
return 2*263*65; // accept a new character every two fields
|
||||
}
|
||||
|
||||
bool Machine::typer_set_next_character(::Utility::Typer *typer, char character, int phase)
|
||||
{
|
||||
// If there's a 'ROM' installed that can never be accessed, assume that this typing was scheduled because
|
||||
// it should be in RAM. So copy it there.
|
||||
if(_rom && _rom_address >= 0x1000 && _rom_address+_rom_length < 0x2000)
|
||||
{
|
||||
memcpy(&_screenMemory[_rom_address - 0x1000], _rom, _rom_length);
|
||||
delete[] _rom;
|
||||
_rom = nullptr;
|
||||
}
|
||||
|
||||
if(!phase) clear_all_keys();
|
||||
|
||||
// The following table is arranged in ASCII order
|
||||
Key key_sequences[][3] = {
|
||||
{NotMapped}, {NotMapped}, {NotMapped}, {NotMapped}, {NotMapped}, {NotMapped}, {NotMapped}, {NotMapped},
|
||||
{KeyDelete, TerminateSequence},
|
||||
{NotMapped},
|
||||
{KeyReturn, TerminateSequence},
|
||||
{NotMapped}, {NotMapped}, {NotMapped}, {NotMapped}, {NotMapped},
|
||||
{NotMapped}, {NotMapped}, {NotMapped}, {NotMapped},
|
||||
{NotMapped}, {NotMapped}, {NotMapped}, {NotMapped},
|
||||
{NotMapped}, {NotMapped}, {NotMapped}, {NotMapped},
|
||||
{NotMapped}, {NotMapped}, {NotMapped}, {NotMapped},
|
||||
|
||||
{KeySpace, TerminateSequence}, // space
|
||||
|
||||
{KeyLShift, Key1, TerminateSequence}, {KeyLShift, Key2, TerminateSequence}, // !, "
|
||||
{KeyLShift, Key3, TerminateSequence}, {KeyLShift, Key4, TerminateSequence}, // #, $
|
||||
{KeyLShift, Key5, TerminateSequence}, {KeyLShift, Key6, TerminateSequence}, // %, &
|
||||
{KeyLShift, Key7, TerminateSequence}, {KeyLShift, Key8, TerminateSequence}, // ', (
|
||||
{KeyLShift, Key9, TerminateSequence}, {KeyAsterisk, TerminateSequence}, // ), *
|
||||
{KeyPlus, TerminateSequence}, {KeyComma, TerminateSequence}, // +, ,
|
||||
{KeyDash, TerminateSequence}, {KeyFullStop, TerminateSequence}, // -, .
|
||||
{KeySlash, TerminateSequence}, // /
|
||||
|
||||
{Key0, TerminateSequence}, {Key1, TerminateSequence}, // 0, 1
|
||||
{Key2, TerminateSequence}, {Key3, TerminateSequence}, // 2, 3
|
||||
{Key4, TerminateSequence}, {Key5, TerminateSequence}, // 4, 5
|
||||
{Key6, TerminateSequence}, {Key7, TerminateSequence}, // 6, 7
|
||||
{Key8, TerminateSequence}, {Key9, TerminateSequence}, // 8, 9
|
||||
|
||||
{KeyColon, TerminateSequence}, {KeySemicolon, TerminateSequence}, // :, ;
|
||||
{KeyLShift, KeyComma, TerminateSequence}, {KeyEquals, TerminateSequence}, // <, =
|
||||
{KeyLShift, KeyFullStop, TerminateSequence}, {KeyLShift, KeySlash, TerminateSequence}, // >, ?
|
||||
{KeyAt, TerminateSequence}, // @
|
||||
|
||||
{KeyA, TerminateSequence}, {KeyB, TerminateSequence}, {KeyC, TerminateSequence}, {KeyD, TerminateSequence}, // A, B, C, D
|
||||
{KeyE, TerminateSequence}, {KeyF, TerminateSequence}, {KeyG, TerminateSequence}, {KeyH, TerminateSequence}, // E, F, G, H
|
||||
{KeyI, TerminateSequence}, {KeyJ, TerminateSequence}, {KeyK, TerminateSequence}, {KeyL, TerminateSequence}, // I, J, K L
|
||||
{KeyM, TerminateSequence}, {KeyN, TerminateSequence}, {KeyO, TerminateSequence}, {KeyP, TerminateSequence}, // M, N, O, P
|
||||
{KeyQ, TerminateSequence}, {KeyR, TerminateSequence}, {KeyS, TerminateSequence}, {KeyT, TerminateSequence}, // Q, R, S, T
|
||||
{KeyU, TerminateSequence}, {KeyV, TerminateSequence}, {KeyW, TerminateSequence}, {KeyX, TerminateSequence}, // U, V, W X
|
||||
{KeyY, TerminateSequence}, {KeyZ, TerminateSequence}, // Y, Z
|
||||
|
||||
{KeyLShift, KeyColon, TerminateSequence}, {NotMapped}, // [, '\'
|
||||
{KeyLShift, KeyFullStop, TerminateSequence}, {NotMapped}, // ], ^
|
||||
{NotMapped}, {NotMapped}, // _, `
|
||||
|
||||
{KeyA, TerminateSequence}, {KeyB, TerminateSequence}, {KeyC, TerminateSequence}, {KeyD, TerminateSequence}, // A, B, C, D
|
||||
{KeyE, TerminateSequence}, {KeyF, TerminateSequence}, {KeyG, TerminateSequence}, {KeyH, TerminateSequence}, // E, F, G, H
|
||||
{KeyI, TerminateSequence}, {KeyJ, TerminateSequence}, {KeyK, TerminateSequence}, {KeyL, TerminateSequence}, // I, J, K L
|
||||
{KeyM, TerminateSequence}, {KeyN, TerminateSequence}, {KeyO, TerminateSequence}, {KeyP, TerminateSequence}, // M, N, O, P
|
||||
{KeyQ, TerminateSequence}, {KeyR, TerminateSequence}, {KeyS, TerminateSequence}, {KeyT, TerminateSequence}, // Q, R, S, T
|
||||
{KeyU, TerminateSequence}, {KeyV, TerminateSequence}, {KeyW, TerminateSequence}, {KeyX, TerminateSequence}, // U, V, W X
|
||||
{KeyY, TerminateSequence}, {KeyZ, TerminateSequence}, // Y, Z
|
||||
// {KeyLShift, KeyA, TerminateSequence}, {KeyLShift, KeyB, TerminateSequence}, // a, b
|
||||
// {KeyLShift, KeyC, TerminateSequence}, {KeyLShift, KeyD, TerminateSequence}, // c, d
|
||||
// {KeyLShift, KeyE, TerminateSequence}, {KeyLShift, KeyF, TerminateSequence}, // e, f
|
||||
// {KeyLShift, KeyG, TerminateSequence}, {KeyLShift, KeyH, TerminateSequence}, // g, h
|
||||
// {KeyLShift, KeyI, TerminateSequence}, {KeyLShift, KeyJ, TerminateSequence}, // i, j
|
||||
// {KeyLShift, KeyK, TerminateSequence}, {KeyLShift, KeyL, TerminateSequence}, // k, l
|
||||
// {KeyLShift, KeyM, TerminateSequence}, {KeyLShift, KeyN, TerminateSequence}, // m, n
|
||||
// {KeyLShift, KeyO, TerminateSequence}, {KeyLShift, KeyP, TerminateSequence}, // o, p
|
||||
// {KeyLShift, KeyQ, TerminateSequence}, {KeyLShift, KeyR, TerminateSequence}, // q, r
|
||||
// {KeyLShift, KeyS, TerminateSequence}, {KeyLShift, KeyT, TerminateSequence}, // s, t
|
||||
// {KeyLShift, KeyU, TerminateSequence}, {KeyLShift, KeyV, TerminateSequence}, // u, v
|
||||
// {KeyLShift, KeyW, TerminateSequence}, {KeyLShift, KeyX, TerminateSequence}, // w, x
|
||||
// {KeyLShift, KeyY, TerminateSequence}, {KeyLShift, KeyZ, TerminateSequence}, // y, z
|
||||
|
||||
};
|
||||
Key *key_sequence = nullptr;
|
||||
|
||||
character &= 0x7f;
|
||||
if(character < sizeof(key_sequences) / sizeof(*key_sequences))
|
||||
{
|
||||
key_sequence = key_sequences[character];
|
||||
|
||||
if(key_sequence[0] != NotMapped)
|
||||
{
|
||||
if(phase > 0)
|
||||
{
|
||||
set_key_state(key_sequence[phase-1], true);
|
||||
return key_sequence[phase] == TerminateSequence;
|
||||
}
|
||||
else
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
@ -12,7 +12,9 @@
|
||||
#include "../../Processors/6502/CPU6502.hpp"
|
||||
#include "../../Components/6560/6560.hpp"
|
||||
#include "../../Components/6522/6522.hpp"
|
||||
|
||||
#include "../CRTMachine.hpp"
|
||||
#include "../Typer.hpp"
|
||||
|
||||
namespace Vic20 {
|
||||
|
||||
@ -42,6 +44,8 @@ enum Key: uint16_t {
|
||||
KeyI = key(1, 0x10), KeyP = key(1, 0x20), KeyAsterisk = key(1, 0x40), KeyReturn = key(1, 0x80),
|
||||
Key1 = key(0, 0x01), Key3 = key(0, 0x02), Key5 = key(0, 0x04), Key7 = key(0, 0x08),
|
||||
Key9 = key(0, 0x10), KeyPlus = key(0, 0x20), KeyGBP = key(0, 0x40), KeyDelete = key(0, 0x80),
|
||||
|
||||
TerminateSequence = 0, NotMapped = 0xffff
|
||||
};
|
||||
|
||||
class UserPortVIA: public MOS::MOS6522<UserPortVIA>, public MOS::MOS6522IRQDelegate {
|
||||
@ -88,7 +92,12 @@ class KeyboardVIA: public MOS::MOS6522<KeyboardVIA>, public MOS::MOS6522IRQDeleg
|
||||
uint8_t _activation_mask;
|
||||
};
|
||||
|
||||
class Machine: public CPU6502::Processor<Machine>, public CRTMachine::Machine, public MOS::MOS6522IRQDelegate::Delegate {
|
||||
class Machine:
|
||||
public CPU6502::Processor<Machine>,
|
||||
public CRTMachine::Machine,
|
||||
public MOS::MOS6522IRQDelegate::Delegate,
|
||||
public Utility::TypeRecipient {
|
||||
|
||||
public:
|
||||
Machine();
|
||||
~Machine();
|
||||
@ -114,6 +123,11 @@ class Machine: public CPU6502::Processor<Machine>, public CRTMachine::Machine, p
|
||||
// to satisfy MOS::MOS6522::Delegate
|
||||
virtual void mos6522_did_change_interrupt_status(void *mos6522);
|
||||
|
||||
// for Utility::TypeRecipient
|
||||
virtual int get_typer_delay();
|
||||
virtual int get_typer_frequency();
|
||||
virtual bool typer_set_next_character(Utility::Typer *typer, char character, int phase);
|
||||
|
||||
private:
|
||||
uint8_t _characterROM[0x1000];
|
||||
uint8_t _basicROM[0x2000];
|
||||
|
@ -13,6 +13,7 @@
|
||||
4B14145E1B5887AA00E04248 /* CPU6502AllRAM.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B1414591B58879D00E04248 /* CPU6502AllRAM.cpp */; };
|
||||
4B1414601B58885000E04248 /* WolfgangLorenzTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4B14145F1B58885000E04248 /* WolfgangLorenzTests.swift */; };
|
||||
4B1414621B58888700E04248 /* KlausDormannTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4B1414611B58888700E04248 /* KlausDormannTests.swift */; };
|
||||
4B1E85751D170228001EF87D /* Typer.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B1E85731D170228001EF87D /* Typer.cpp */; };
|
||||
4B2409551C45AB05004DA684 /* Speaker.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B2409531C45AB05004DA684 /* Speaker.cpp */; };
|
||||
4B2A539F1D117D36003C6002 /* CSAudioQueue.m in Sources */ = {isa = PBXBuildFile; fileRef = 4B2A53911D117D36003C6002 /* CSAudioQueue.m */; };
|
||||
4B2A53A01D117D36003C6002 /* CSMachine.mm in Sources */ = {isa = PBXBuildFile; fileRef = 4B2A53961D117D36003C6002 /* CSMachine.mm */; };
|
||||
@ -352,6 +353,8 @@
|
||||
4B14145A1B58879D00E04248 /* CPU6502AllRAM.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = CPU6502AllRAM.hpp; sourceTree = "<group>"; };
|
||||
4B14145F1B58885000E04248 /* WolfgangLorenzTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = WolfgangLorenzTests.swift; sourceTree = "<group>"; };
|
||||
4B1414611B58888700E04248 /* KlausDormannTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = KlausDormannTests.swift; sourceTree = "<group>"; };
|
||||
4B1E85731D170228001EF87D /* Typer.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = Typer.cpp; sourceTree = "<group>"; };
|
||||
4B1E85741D170228001EF87D /* Typer.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = Typer.hpp; sourceTree = "<group>"; };
|
||||
4B2409531C45AB05004DA684 /* Speaker.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = Speaker.cpp; path = ../../Outputs/Speaker.cpp; sourceTree = "<group>"; };
|
||||
4B2409541C45AB05004DA684 /* Speaker.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; name = Speaker.hpp; path = ../../Outputs/Speaker.hpp; sourceTree = "<group>"; };
|
||||
4B24095A1C45DF85004DA684 /* Stepper.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = Stepper.hpp; sourceTree = "<group>"; };
|
||||
@ -1251,6 +1254,8 @@
|
||||
4B2E2D961C3A06EC00138695 /* Atari2600 */,
|
||||
4B2E2D9E1C3A070900138695 /* Electron */,
|
||||
4B886FF61D03B632004291C3 /* Vic-20 */,
|
||||
4B1E85731D170228001EF87D /* Typer.cpp */,
|
||||
4B1E85741D170228001EF87D /* Typer.hpp */,
|
||||
);
|
||||
name = Machines;
|
||||
path = ../../Machines;
|
||||
@ -1764,6 +1769,7 @@
|
||||
4B69FB441C4D941400B5F0AA /* TapeUEF.cpp in Sources */,
|
||||
4BBF99141C8FBA6F0075DAFB /* CRTInputBufferBuilder.cpp in Sources */,
|
||||
4B2409551C45AB05004DA684 /* Speaker.cpp in Sources */,
|
||||
4B1E85751D170228001EF87D /* Typer.cpp in Sources */,
|
||||
4B2E2D9D1C3A070400138695 /* Electron.cpp in Sources */,
|
||||
4B69FB3D1C4D908A00B5F0AA /* Tape.cpp in Sources */,
|
||||
4B55CE5D1C3B7D6F0093A61B /* CSOpenGLView.m in Sources */,
|
||||
|
@ -80,6 +80,14 @@ class MachineDocument:
|
||||
super.close()
|
||||
}
|
||||
|
||||
// MARK: the pasteboard
|
||||
func paste(sender: AnyObject!) {
|
||||
let pasteboard = NSPasteboard.generalPasteboard()
|
||||
if let string = pasteboard.stringForType(NSPasteboardTypeString) {
|
||||
self.machine().paste(string)
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: CSBestEffortUpdaterDelegate
|
||||
final func bestEffortUpdater(bestEffortUpdater: CSBestEffortUpdater!, runForCycles cycles: UInt, didSkipPreviousUpdate: Bool) {
|
||||
runForNumberOfCycles(Int32(cycles))
|
||||
|
@ -30,4 +30,6 @@
|
||||
@property (nonatomic, weak) id<CSMachineDelegate> delegate;
|
||||
@property (nonatomic, readonly) double clockRate;
|
||||
|
||||
- (void)paste:(NSString *)string;
|
||||
|
||||
@end
|
||||
|
@ -8,6 +8,7 @@
|
||||
|
||||
#import "CSMachine.h"
|
||||
#import "CSMachine+Subclassing.h"
|
||||
#include "Typer.hpp"
|
||||
|
||||
@interface CSMachine()
|
||||
- (void)speaker:(Outputs::Speaker *)speaker didCompleteSamples:(const int16_t *)samples length:(int)length;
|
||||
@ -92,4 +93,10 @@ struct SpeakerDelegate: public Outputs::Speaker::Delegate {
|
||||
return self.machine->get_clock_rate();
|
||||
}
|
||||
|
||||
- (void)paste:(NSString *)paste {
|
||||
Utility::TypeRecipient *typeRecipient = dynamic_cast<Utility::TypeRecipient *>(self.machine);
|
||||
if(typeRecipient)
|
||||
typeRecipient->set_typer_for_string([paste UTF8String]);
|
||||
}
|
||||
|
||||
@end
|
||||
|
@ -1150,6 +1150,16 @@ template <class T> class Processor {
|
||||
_reset_line_is_enabled = active;
|
||||
}
|
||||
|
||||
/*!
|
||||
Gets the current level of the RST line.
|
||||
|
||||
@returns @c true if the line is logically active; @c false otherwise.
|
||||
*/
|
||||
inline bool get_reset_line()
|
||||
{
|
||||
return _reset_line_is_enabled;
|
||||
}
|
||||
|
||||
/*!
|
||||
Sets the current level of the IRQ line.
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user