mirror of
https://github.com/TomHarte/CLK.git
synced 2024-08-19 21:29:04 +00:00
Merge pull request #186 from TomHarte/CPCTyper
Introduces typer support for the Amstrad CPC.
This commit is contained in:
commit
9c0b75faba
@ -8,6 +8,8 @@
|
||||
|
||||
#include "AmstradCPC.hpp"
|
||||
|
||||
#include "CharacterMapper.hpp"
|
||||
|
||||
#include "../../Processors/Z80/Z80.hpp"
|
||||
|
||||
#include "../../Components/6845/CRTC6845.hpp"
|
||||
@ -15,6 +17,9 @@
|
||||
#include "../../Components/8272/i8272.hpp"
|
||||
#include "../../Components/AY38910/AY38910.hpp"
|
||||
|
||||
#include "../MemoryFuzzer.hpp"
|
||||
#include "../Typer.hpp"
|
||||
|
||||
#include "../../Storage/Tape/Tape.hpp"
|
||||
|
||||
namespace AmstradCPC {
|
||||
@ -523,6 +528,7 @@ class i8255PortHandler : public Intel::i8255::PortHandler {
|
||||
The actual Amstrad CPC implementation; tying the 8255, 6845 and AY to the Z80.
|
||||
*/
|
||||
class ConcreteMachine:
|
||||
public Utility::TypeRecipient,
|
||||
public CPU::Z80::BusHandler,
|
||||
public Machine {
|
||||
public:
|
||||
@ -536,6 +542,9 @@ class ConcreteMachine:
|
||||
tape_player_(8000000) {
|
||||
// primary clock is 4Mhz
|
||||
set_clock_rate(4000000);
|
||||
|
||||
// ensure memory starts in a random state
|
||||
Memory::Fuzz(ram_, sizeof(ram_));
|
||||
}
|
||||
|
||||
/// The entry point for performing a partial Z80 machine cycle.
|
||||
@ -557,12 +566,15 @@ class ConcreteMachine:
|
||||
// run_for as HalfCycles
|
||||
tape_player_.run_for(cycle.length.as_int());
|
||||
|
||||
// Pump the AY.
|
||||
// Pump the AY
|
||||
ay_.run_for(cycle.length);
|
||||
|
||||
// Clock the FDC, if connected, using a lazy scale by two
|
||||
if(has_fdc_) fdc_.run_for(Cycles(cycle.length.as_int()));
|
||||
|
||||
// Update typing activity
|
||||
if(typer_) typer_->run_for(cycle.length);
|
||||
|
||||
// Stop now if no action is strictly required.
|
||||
if(!cycle.is_terminal()) return HalfCycles(0);
|
||||
|
||||
@ -742,6 +754,11 @@ class ConcreteMachine:
|
||||
c++;
|
||||
if(c == 4) break;
|
||||
}
|
||||
|
||||
// Type whatever is required.
|
||||
if(target.loadingCommand.length()) {
|
||||
set_typer_for_string(target.loadingCommand.c_str());
|
||||
}
|
||||
}
|
||||
|
||||
// See header; provides the system ROMs.
|
||||
@ -749,6 +766,21 @@ class ConcreteMachine:
|
||||
roms_[(int)type] = data;
|
||||
}
|
||||
|
||||
#pragma mark - Keyboard
|
||||
|
||||
void set_typer_for_string(const char *string) {
|
||||
std::unique_ptr<CharacterMapper> mapper(new CharacterMapper());
|
||||
Utility::TypeRecipient::set_typer_for_string(string, std::move(mapper));
|
||||
}
|
||||
|
||||
HalfCycles get_typer_delay() {
|
||||
return Cycles(4000000); // Wait 1 second before typing.
|
||||
}
|
||||
|
||||
HalfCycles get_typer_frequency() {
|
||||
return Cycles(80000); // Type one character per frame.
|
||||
}
|
||||
|
||||
// See header; sets a key as either pressed or released.
|
||||
void set_key_state(uint16_t key, bool isPressed) {
|
||||
int line = key >> 4;
|
||||
@ -849,3 +881,5 @@ using namespace AmstradCPC;
|
||||
Machine *Machine::AmstradCPC() {
|
||||
return new AmstradCPC::ConcreteMachine;
|
||||
}
|
||||
|
||||
Machine::~Machine() {}
|
||||
|
@ -52,6 +52,8 @@ class Machine:
|
||||
public ConfigurationTarget::Machine,
|
||||
public KeyboardMachine::Machine {
|
||||
public:
|
||||
virtual ~Machine();
|
||||
|
||||
/// Creates an returns an Amstrad CPC on the heap.
|
||||
static Machine *AmstradCPC();
|
||||
|
||||
|
89
Machines/AmstradCPC/CharacterMapper.cpp
Normal file
89
Machines/AmstradCPC/CharacterMapper.cpp
Normal file
@ -0,0 +1,89 @@
|
||||
//
|
||||
// CharacterMapper.cpp
|
||||
// Clock Signal
|
||||
//
|
||||
// Created by Thomas Harte on 11/08/2017.
|
||||
// Copyright © 2017 Thomas Harte. All rights reserved.
|
||||
//
|
||||
|
||||
#include "CharacterMapper.hpp"
|
||||
#include "AmstradCPC.hpp"
|
||||
|
||||
using namespace AmstradCPC;
|
||||
|
||||
uint16_t *CharacterMapper::sequence_for_character(char character) {
|
||||
#define KEYS(...) {__VA_ARGS__, EndSequence}
|
||||
#define SHIFT(...) {KeyShift, __VA_ARGS__, EndSequence}
|
||||
#define X {NotMapped}
|
||||
static KeySequence key_sequences[] = {
|
||||
/* NUL */ X, /* SOH */ X,
|
||||
/* STX */ X, /* ETX */ X,
|
||||
/* EOT */ X, /* ENQ */ X,
|
||||
/* ACK */ X, /* BEL */ X,
|
||||
/* BS */ KEYS(KeyDelete), /* HT */ X,
|
||||
/* LF */ KEYS(KeyReturn), /* VT */ X,
|
||||
/* FF */ X, /* CR */ X,
|
||||
/* SO */ X, /* SI */ X,
|
||||
/* DLE */ X, /* DC1 */ X,
|
||||
/* DC2 */ X, /* DC3 */ X,
|
||||
/* DC4 */ X, /* NAK */ X,
|
||||
/* SYN */ X, /* ETB */ X,
|
||||
/* CAN */ X, /* EM */ X,
|
||||
/* SUB */ X, /* ESC */ X,
|
||||
/* FS */ X, /* GS */ X,
|
||||
/* RS */ X, /* US */ X,
|
||||
/* space */ KEYS(KeySpace), /* ! */ SHIFT(Key1),
|
||||
/* " */ SHIFT(Key2), /* # */ SHIFT(Key3),
|
||||
/* $ */ SHIFT(Key4), /* % */ SHIFT(Key5),
|
||||
/* & */ SHIFT(Key6), /* ' */ SHIFT(Key7),
|
||||
/* ( */ SHIFT(Key8), /* ) */ SHIFT(Key9),
|
||||
/* * */ SHIFT(KeyColon), /* + */ SHIFT(KeySemicolon),
|
||||
/* , */ KEYS(KeyComma), /* - */ KEYS(KeyMinus),
|
||||
/* . */ KEYS(KeyFullStop), /* / */ KEYS(KeyForwardSlash),
|
||||
/* 0 */ KEYS(Key0), /* 1 */ KEYS(Key1),
|
||||
/* 2 */ KEYS(Key2), /* 3 */ KEYS(Key3),
|
||||
/* 4 */ KEYS(Key4), /* 5 */ KEYS(Key5),
|
||||
/* 6 */ KEYS(Key6), /* 7 */ KEYS(Key7),
|
||||
/* 8 */ KEYS(Key8), /* 9 */ KEYS(Key9),
|
||||
/* : */ KEYS(KeyColon), /* ; */ KEYS(KeySemicolon),
|
||||
/* < */ SHIFT(KeyComma), /* = */ SHIFT(KeyMinus),
|
||||
/* > */ SHIFT(KeyFullStop), /* ? */ SHIFT(KeyForwardSlash),
|
||||
/* @ */ SHIFT(KeyAt), /* A */ SHIFT(KeyA),
|
||||
/* B */ SHIFT(KeyB), /* C */ SHIFT(KeyC),
|
||||
/* D */ SHIFT(KeyD), /* E */ SHIFT(KeyE),
|
||||
/* F */ SHIFT(KeyF), /* G */ SHIFT(KeyG),
|
||||
/* H */ SHIFT(KeyH), /* I */ SHIFT(KeyI),
|
||||
/* J */ SHIFT(KeyJ), /* K */ SHIFT(KeyK),
|
||||
/* L */ SHIFT(KeyL), /* M */ SHIFT(KeyM),
|
||||
/* N */ SHIFT(KeyN), /* O */ SHIFT(KeyO),
|
||||
/* P */ SHIFT(KeyP), /* Q */ SHIFT(KeyQ),
|
||||
/* R */ SHIFT(KeyR), /* S */ SHIFT(KeyS),
|
||||
/* T */ SHIFT(KeyT), /* U */ SHIFT(KeyU),
|
||||
/* V */ SHIFT(KeyV), /* W */ SHIFT(KeyW),
|
||||
/* X */ SHIFT(KeyX), /* Y */ SHIFT(KeyY),
|
||||
/* Z */ SHIFT(KeyZ), /* [ */ KEYS(KeyLeftSquareBracket),
|
||||
/* \ */ KEYS(KeyBackSlash), /* ] */ KEYS(KeyRightSquareBracket),
|
||||
/* ^ */ SHIFT(KeyCaret), /* _ */ SHIFT(Key0),
|
||||
/* ` */ X, /* a */ KEYS(KeyA),
|
||||
/* b */ KEYS(KeyB), /* c */ KEYS(KeyC),
|
||||
/* d */ KEYS(KeyD), /* e */ KEYS(KeyE),
|
||||
/* f */ KEYS(KeyF), /* g */ KEYS(KeyG),
|
||||
/* h */ KEYS(KeyH), /* i */ KEYS(KeyI),
|
||||
/* j */ KEYS(KeyJ), /* k */ KEYS(KeyK),
|
||||
/* l */ KEYS(KeyL), /* m */ KEYS(KeyM),
|
||||
/* n */ KEYS(KeyN), /* o */ KEYS(KeyO),
|
||||
/* p */ KEYS(KeyP), /* q */ KEYS(KeyQ),
|
||||
/* r */ KEYS(KeyR), /* s */ KEYS(KeyS),
|
||||
/* t */ KEYS(KeyT), /* u */ KEYS(KeyU),
|
||||
/* v */ KEYS(KeyV), /* w */ KEYS(KeyW),
|
||||
/* x */ KEYS(KeyX), /* y */ KEYS(KeyY),
|
||||
/* z */ KEYS(KeyZ), /* { */ X,
|
||||
/* | */ SHIFT(KeyAt), /* } */ X,
|
||||
/* ~ */ X
|
||||
};
|
||||
#undef KEYS
|
||||
#undef SHIFT
|
||||
#undef X
|
||||
|
||||
return table_lookup_sequence_for_character(key_sequences, sizeof(key_sequences), character);
|
||||
}
|
23
Machines/AmstradCPC/CharacterMapper.hpp
Normal file
23
Machines/AmstradCPC/CharacterMapper.hpp
Normal file
@ -0,0 +1,23 @@
|
||||
//
|
||||
// CharacterMapper.hpp
|
||||
// Clock Signal
|
||||
//
|
||||
// Created by Thomas Harte on 11/08/2017.
|
||||
// Copyright © 2017 Thomas Harte. All rights reserved.
|
||||
//
|
||||
|
||||
#ifndef Machines_AmstradCPC_CharacterMapper_hpp
|
||||
#define Machines_AmstradCPC_CharacterMapper_hpp
|
||||
|
||||
#include "../Typer.hpp"
|
||||
|
||||
namespace AmstradCPC {
|
||||
|
||||
class CharacterMapper: public ::Utility::CharacterMapper {
|
||||
public:
|
||||
uint16_t *sequence_for_character(char character);
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif /* CharacterMapper_hpp */
|
@ -51,7 +51,7 @@ bool Typer::try_type_next_character() {
|
||||
if(!phase_) delegate_->clear_all_keys();
|
||||
else {
|
||||
delegate_->set_key_state(sequence[phase_ - 1], true);
|
||||
return sequence[phase_] == CharacterMapper::EndSequence;
|
||||
return sequence[phase_] != CharacterMapper::EndSequence;
|
||||
}
|
||||
|
||||
return true;
|
||||
|
@ -6,8 +6,8 @@
|
||||
// Copyright © 2017 Thomas Harte. All rights reserved.
|
||||
//
|
||||
|
||||
#ifndef CharacterMapper_hpp
|
||||
#define CharacterMapper_hpp
|
||||
#ifndef Machines_ZX8081_CharacterMapper_hpp
|
||||
#define Machines_ZX8081_CharacterMapper_hpp
|
||||
|
||||
#include "../Typer.hpp"
|
||||
|
||||
|
@ -384,3 +384,5 @@ using namespace ZX8081;
|
||||
Machine *Machine::ZX8081() {
|
||||
return new ZX8081::ConcreteMachine;
|
||||
}
|
||||
|
||||
Machine::~Machine() {}
|
||||
|
@ -39,6 +39,7 @@ class Machine:
|
||||
public KeyboardMachine::Machine {
|
||||
public:
|
||||
static Machine *ZX8081();
|
||||
virtual ~Machine();
|
||||
|
||||
virtual void set_rom(ROMType type, std::vector<uint8_t> data) = 0;
|
||||
|
||||
|
@ -120,6 +120,7 @@
|
||||
4BAB62AD1D3272D200DF5BA0 /* Disk.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4BAB62AB1D3272D200DF5BA0 /* Disk.cpp */; };
|
||||
4BAB62B51D327F7E00DF5BA0 /* G64.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4BAB62B31D327F7E00DF5BA0 /* G64.cpp */; };
|
||||
4BAB62B81D3302CA00DF5BA0 /* PCMTrack.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4BAB62B61D3302CA00DF5BA0 /* PCMTrack.cpp */; };
|
||||
4BACC5B11F3DFF7C0037C015 /* CharacterMapper.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4BACC5AF1F3DFF7C0037C015 /* CharacterMapper.cpp */; };
|
||||
4BB17D4E1ED7909F00ABD1E1 /* tests.expected.json in Resources */ = {isa = PBXBuildFile; fileRef = 4BB17D4C1ED7909F00ABD1E1 /* tests.expected.json */; };
|
||||
4BB17D4F1ED7909F00ABD1E1 /* tests.in.json in Resources */ = {isa = PBXBuildFile; fileRef = 4BB17D4D1ED7909F00ABD1E1 /* tests.in.json */; };
|
||||
4BB298F11B587D8400A49093 /* start in Resources */ = {isa = PBXBuildFile; fileRef = 4BB297E51B587D8300A49093 /* start */; };
|
||||
@ -670,6 +671,8 @@
|
||||
4BAB62B41D327F7E00DF5BA0 /* G64.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = G64.hpp; sourceTree = "<group>"; };
|
||||
4BAB62B61D3302CA00DF5BA0 /* PCMTrack.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = PCMTrack.cpp; sourceTree = "<group>"; };
|
||||
4BAB62B71D3302CA00DF5BA0 /* PCMTrack.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = PCMTrack.hpp; sourceTree = "<group>"; };
|
||||
4BACC5AF1F3DFF7C0037C015 /* CharacterMapper.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = CharacterMapper.cpp; path = AmstradCPC/CharacterMapper.cpp; sourceTree = "<group>"; };
|
||||
4BACC5B01F3DFF7C0037C015 /* CharacterMapper.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; name = CharacterMapper.hpp; path = AmstradCPC/CharacterMapper.hpp; sourceTree = "<group>"; };
|
||||
4BB06B211F316A3F00600C7A /* ForceInline.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = ForceInline.h; sourceTree = "<group>"; };
|
||||
4BB17D4C1ED7909F00ABD1E1 /* tests.expected.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; name = tests.expected.json; path = FUSE/tests.expected.json; sourceTree = "<group>"; };
|
||||
4BB17D4D1ED7909F00ABD1E1 /* tests.in.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; name = tests.in.json; path = FUSE/tests.in.json; sourceTree = "<group>"; };
|
||||
@ -1297,6 +1300,8 @@
|
||||
children = (
|
||||
4B38F3461F2EC11D00D9235D /* AmstradCPC.cpp */,
|
||||
4B38F3471F2EC11D00D9235D /* AmstradCPC.hpp */,
|
||||
4BACC5AF1F3DFF7C0037C015 /* CharacterMapper.cpp */,
|
||||
4BACC5B01F3DFF7C0037C015 /* CharacterMapper.hpp */,
|
||||
);
|
||||
name = AmstradCPC;
|
||||
sourceTree = "<group>";
|
||||
@ -2724,6 +2729,7 @@
|
||||
4B96F7221D75119A0058BB2D /* Tape.cpp in Sources */,
|
||||
4B0BE4281D3481E700D5256B /* DigitalPhaseLockedLoop.cpp in Sources */,
|
||||
4B8805F71DCFF6C9003085B1 /* Commodore.cpp in Sources */,
|
||||
4BACC5B11F3DFF7C0037C015 /* CharacterMapper.cpp in Sources */,
|
||||
4BD69F941D98760000243FE1 /* AcornADF.cpp in Sources */,
|
||||
4BBF99181C8FBA6F0075DAFB /* TextureTarget.cpp in Sources */,
|
||||
4BC76E691C98E31700E6EF73 /* FIRFilter.cpp in Sources */,
|
||||
|
@ -18,17 +18,28 @@ static void InspectDataCatalogue(
|
||||
return;
|
||||
}
|
||||
|
||||
// If only one file is [potentially] BASIC, run that one.
|
||||
// If only one file is [potentially] BASIC, run that one; otherwise if only one has no suffix,
|
||||
// pick that one.
|
||||
int basic_files = 0;
|
||||
int nonsuffixed_files = 0;
|
||||
size_t last_basic_file = 0;
|
||||
size_t last_nonsuffixed_file = 0;
|
||||
for(size_t c = 0; c < data_catalogue->files.size(); c++) {
|
||||
// Check for whether this is [potentially] BASIC.
|
||||
if(!((data_catalogue->files[c].data[18] >> 1) & 7)) {
|
||||
basic_files++;
|
||||
last_basic_file = c;
|
||||
}
|
||||
|
||||
// Check suffix for emptiness.
|
||||
if(data_catalogue->files[c].type == " ") {
|
||||
nonsuffixed_files++;
|
||||
last_nonsuffixed_file = c;
|
||||
}
|
||||
}
|
||||
if(basic_files == 1) {
|
||||
target.loadingCommand = "run\"" + data_catalogue->files[last_basic_file].name + "\n";
|
||||
if(basic_files == 1 || nonsuffixed_files == 1) {
|
||||
size_t selected_file = (basic_files == 1) ? last_basic_file : last_nonsuffixed_file;
|
||||
target.loadingCommand = "run\"" + data_catalogue->files[selected_file].name + "\n";
|
||||
return;
|
||||
}
|
||||
|
||||
@ -56,7 +67,10 @@ void StaticAnalyser::AmstradCPC::AddTargets(
|
||||
target.amstradcpc.model = AmstradCPCModel::CPC6128;
|
||||
|
||||
if(!target.tapes.empty()) {
|
||||
target.loadingCommand = "|tape\nrun\"\n";
|
||||
// Ugliness flows here: assume the CPC isn't smart enough to pause between pressing
|
||||
// enter and responding to the follow-on prompt to press a key, so just type for
|
||||
// a while. Yuck!
|
||||
target.loadingCommand = "|tape\nrun\"\n1234567890";
|
||||
}
|
||||
|
||||
if(!target.disks.empty()) {
|
||||
|
Loading…
Reference in New Issue
Block a user