1
0
mirror of https://github.com/TomHarte/CLK.git synced 2024-11-26 08:49:37 +00:00

Merge pull request #186 from TomHarte/CPCTyper

Introduces typer support for the Amstrad CPC.
This commit is contained in:
Thomas Harte 2017-08-11 12:35:43 -04:00 committed by GitHub
commit 9c0b75faba
10 changed files with 179 additions and 8 deletions

View File

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

View File

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

View 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);
}

View 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 */

View File

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

View File

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

View File

@ -384,3 +384,5 @@ using namespace ZX8081;
Machine *Machine::ZX8081() {
return new ZX8081::ConcreteMachine;
}
Machine::~Machine() {}

View File

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

View File

@ -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 */,

View File

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