diff --git a/Machines/AmstradCPC/AmstradCPC.cpp b/Machines/AmstradCPC/AmstradCPC.cpp index 7859fdd58..c4053c023 100644 --- a/Machines/AmstradCPC/AmstradCPC.cpp +++ b/Machines/AmstradCPC/AmstradCPC.cpp @@ -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 mapper(new CharacterMapper()); + Utility::TypeRecipient::set_typer_for_string(string, std::move(mapper)); + } + + HalfCycles get_typer_delay() { + return Cycles(625*25*128); + } + + HalfCycles get_typer_frequency() { + return Cycles(625*128*2); + } + // See header; sets a key as either pressed or released. void set_key_state(uint16_t key, bool isPressed) { int line = key >> 4; diff --git a/Machines/AmstradCPC/CharacterMapper.cpp b/Machines/AmstradCPC/CharacterMapper.cpp new file mode 100644 index 000000000..e1fef2118 --- /dev/null +++ b/Machines/AmstradCPC/CharacterMapper.cpp @@ -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); +} diff --git a/Machines/AmstradCPC/CharacterMapper.hpp b/Machines/AmstradCPC/CharacterMapper.hpp new file mode 100644 index 000000000..89f985ae2 --- /dev/null +++ b/Machines/AmstradCPC/CharacterMapper.hpp @@ -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 */ diff --git a/OSBindings/Mac/Clock Signal.xcodeproj/project.pbxproj b/OSBindings/Mac/Clock Signal.xcodeproj/project.pbxproj index 263dd9bb3..f7a8f670a 100644 --- a/OSBindings/Mac/Clock Signal.xcodeproj/project.pbxproj +++ b/OSBindings/Mac/Clock Signal.xcodeproj/project.pbxproj @@ -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 = ""; }; 4BAB62B61D3302CA00DF5BA0 /* PCMTrack.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = PCMTrack.cpp; sourceTree = ""; }; 4BAB62B71D3302CA00DF5BA0 /* PCMTrack.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = PCMTrack.hpp; sourceTree = ""; }; + 4BACC5AF1F3DFF7C0037C015 /* CharacterMapper.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = CharacterMapper.cpp; path = AmstradCPC/CharacterMapper.cpp; sourceTree = ""; }; + 4BACC5B01F3DFF7C0037C015 /* CharacterMapper.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; name = CharacterMapper.hpp; path = AmstradCPC/CharacterMapper.hpp; sourceTree = ""; }; 4BB06B211F316A3F00600C7A /* ForceInline.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = ForceInline.h; sourceTree = ""; }; 4BB17D4C1ED7909F00ABD1E1 /* tests.expected.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; name = tests.expected.json; path = FUSE/tests.expected.json; sourceTree = ""; }; 4BB17D4D1ED7909F00ABD1E1 /* tests.in.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; name = tests.in.json; path = FUSE/tests.in.json; sourceTree = ""; }; @@ -1297,6 +1300,8 @@ children = ( 4B38F3461F2EC11D00D9235D /* AmstradCPC.cpp */, 4B38F3471F2EC11D00D9235D /* AmstradCPC.hpp */, + 4BACC5AF1F3DFF7C0037C015 /* CharacterMapper.cpp */, + 4BACC5B01F3DFF7C0037C015 /* CharacterMapper.hpp */, ); name = AmstradCPC; sourceTree = ""; @@ -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 */,