From 465c38f03c76b763a7290fbc01a4087793fc39bb Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Sun, 15 Apr 2018 21:11:30 -0400 Subject: [PATCH] Extends the keyboard protocol and adds keyboard input to the Apple II. --- Inputs/Keyboard.cpp | 2 +- Inputs/Keyboard.hpp | 2 +- Machines/AppleII/AppleII.cpp | 21 +++++++++++++++++-- Machines/AppleII/Video.hpp | 2 +- Machines/KeyboardMachine.hpp | 4 ++-- .../Mac/Clock Signal/Machine/CSMachine.mm | 19 ++++++++++++++++- OSBindings/SDL/main.cpp | 9 ++++++-- 7 files changed, 49 insertions(+), 10 deletions(-) diff --git a/Inputs/Keyboard.cpp b/Inputs/Keyboard.cpp index d9384ce05..65a28a14f 100644 --- a/Inputs/Keyboard.cpp +++ b/Inputs/Keyboard.cpp @@ -12,7 +12,7 @@ using namespace Inputs; Keyboard::Keyboard() {} -void Keyboard::set_key_pressed(Key key, bool is_pressed) { +void Keyboard::set_key_pressed(Key key, char value, bool is_pressed) { std::size_t key_offset = static_cast(key); if(key_offset >= key_states_.size()) { key_states_.resize(key_offset+1, false); diff --git a/Inputs/Keyboard.hpp b/Inputs/Keyboard.hpp index 9214bd985..2f1249efb 100644 --- a/Inputs/Keyboard.hpp +++ b/Inputs/Keyboard.hpp @@ -40,7 +40,7 @@ class Keyboard { }; // Host interface. - virtual void set_key_pressed(Key key, bool is_pressed); + virtual void set_key_pressed(Key key, char value, bool is_pressed); virtual void reset_all_keys(); // Delegate interface. diff --git a/Machines/AppleII/AppleII.cpp b/Machines/AppleII/AppleII.cpp index f74c002c6..f996ffd81 100644 --- a/Machines/AppleII/AppleII.cpp +++ b/Machines/AppleII/AppleII.cpp @@ -9,6 +9,7 @@ #include "AppleII.hpp" #include "../CRTMachine.hpp" +#include "../KeyboardMachine.hpp" #include "../Utility/MemoryFuzzer.hpp" #include "../../Processors/6502/6502.hpp" @@ -21,7 +22,9 @@ namespace { class ConcreteMachine: public CRTMachine::Machine, + public KeyboardMachine::Machine, public CPU::MOS6502::BusHandler, + public Inputs::Keyboard, public AppleII::Machine { private: struct VideoBusHandler : public AppleII::Video::BusHandler { @@ -50,6 +53,7 @@ class ConcreteMachine: std::vector rom_; std::vector character_rom_; uint16_t rom_start_address_; + uint8_t keyboard_input_ = 0x00; public: ConcreteMachine(): @@ -94,8 +98,7 @@ class ConcreteMachine: break; case 0xc000: // TODO: read keyboard. -// printf("Keyboard poll\n"); - *value = 0x00;//0x80 | 'A'; + *value = keyboard_input_; break; } } @@ -116,6 +119,10 @@ class ConcreteMachine: case 0xc055: update_video(); video_->set_video_page(1); break; case 0xc056: update_video(); video_->set_low_resolution(); break; case 0xc057: update_video(); video_->set_high_resolution(); break; + + case 0xc010: + keyboard_input_ &= 0x7f; + break; } // The Apple II has a slightly weird timing pattern: every 65th CPU cycle is stretched @@ -155,6 +162,16 @@ class ConcreteMachine: void run_for(const Cycles cycles) override { m6502_.run_for(cycles); } + + void set_key_pressed(Key key, char value, bool is_pressed) override { + if(is_pressed) { + keyboard_input_ = static_cast(value | 0x80); + } + } + + Inputs::Keyboard &get_keyboard() override { + return *this; + } }; } diff --git a/Machines/AppleII/Video.hpp b/Machines/AppleII/Video.hpp index 9b9fc1533..cd6d048ea 100644 --- a/Machines/AppleII/Video.hpp +++ b/Machines/AppleII/Video.hpp @@ -45,7 +45,7 @@ template class Video { // Show only the centre 75% of the TV frame. crt_->set_video_signal(Outputs::CRT::VideoSignal::Composite); - crt_->set_visible_area(Outputs::CRT::Rect(0.115f, 0.115f, 0.77f, 0.77f)); + crt_->set_visible_area(Outputs::CRT::Rect(0.115f, 0.117f, 0.77f, 0.77f)); } /// @returns The CRT this video feed is feeding. diff --git a/Machines/KeyboardMachine.hpp b/Machines/KeyboardMachine.hpp index c4fab7c78..63d465430 100644 --- a/Machines/KeyboardMachine.hpp +++ b/Machines/KeyboardMachine.hpp @@ -24,12 +24,12 @@ struct KeyActions { Indicates that the key @c key has been either pressed or released, according to the state of @c isPressed. */ - virtual void set_key_state(uint16_t key, bool is_pressed) = 0; + virtual void set_key_state(uint16_t key, bool is_pressed) {} /*! Instructs that all keys should now be treated as released. */ - virtual void clear_all_keys() = 0; + virtual void clear_all_keys() {} }; /*! diff --git a/OSBindings/Mac/Clock Signal/Machine/CSMachine.mm b/OSBindings/Mac/Clock Signal/Machine/CSMachine.mm index fae8fece6..ca561ae7e 100644 --- a/OSBindings/Mac/Clock Signal/Machine/CSMachine.mm +++ b/OSBindings/Mac/Clock Signal/Machine/CSMachine.mm @@ -23,6 +23,8 @@ #import "NSBundle+DataResource.h" #import "NSData+StdVector.h" +#include + @interface CSMachine() - (void)speaker:(Outputs::Speaker::Speaker *)speaker didCompleteSamples:(const int16_t *)samples length:(int)length; - (void)speakerDidChangeInputClock:(Outputs::Speaker::Speaker *)speaker; @@ -54,6 +56,8 @@ struct SpeakerDelegate: public Outputs::Speaker::Speaker::Delegate, public LockP CSStaticAnalyser *_analyser; std::unique_ptr _machine; + + std::bitset<65536> _depressedKeys; } - (instancetype)initWithAnalyser:(CSStaticAnalyser *)result { @@ -168,12 +172,25 @@ struct SpeakerDelegate: public Outputs::Speaker::Speaker::Delegate, public LockP - (void)setKey:(uint16_t)key characters:(NSString *)characters isPressed:(BOOL)isPressed { auto keyboard_machine = _machine->keyboard_machine(); if(keyboard_machine) { + // Don't pass anything on if this is not new information. + if(_depressedKeys[key] == !!isPressed) return; + _depressedKeys[key] = !!isPressed; + + // Pick an ASCII code, if any. + char pressedKey = '\0'; + if(characters.length) { + unichar firstCharacter = [characters characterAtIndex:0]; + if(firstCharacter < 128) { + pressedKey = (char)firstCharacter; + } + } + @synchronized(self) { Inputs::Keyboard &keyboard = keyboard_machine->get_keyboard(); // Connect the Carbon-era Mac keyboard scancodes to Clock Signal's 'universal' enumeration in order // to pass into the platform-neutral realm. -#define BIND(source, dest) case source: keyboard.set_key_pressed(Inputs::Keyboard::Key::dest, isPressed); break +#define BIND(source, dest) case source: keyboard.set_key_pressed(Inputs::Keyboard::Key::dest, pressedKey, isPressed); break switch(key) { BIND(VK_ANSI_0, k0); BIND(VK_ANSI_1, k1); BIND(VK_ANSI_2, k2); BIND(VK_ANSI_3, k3); BIND(VK_ANSI_4, k4); BIND(VK_ANSI_5, k5); BIND(VK_ANSI_6, k6); BIND(VK_ANSI_7, k7); BIND(VK_ANSI_8, k8); BIND(VK_ANSI_9, k9); diff --git a/OSBindings/SDL/main.cpp b/OSBindings/SDL/main.cpp index cf9080127..3631ce3ac 100644 --- a/OSBindings/SDL/main.cpp +++ b/OSBindings/SDL/main.cpp @@ -239,7 +239,7 @@ int main(int argc, char *argv[]) { } // Determine the machine for the supplied file. - TargetList targets = Analyser::Static::GetTargets(arguments.file_name); + Analyser::Static::TargetList targets = Analyser::Static::GetTargets(arguments.file_name); if(targets.empty()) { std::cerr << "Cannot open " << arguments.file_name << "; no target machine found" << std::endl; return -1; @@ -450,7 +450,12 @@ int main(int argc, char *argv[]) { if(keyboard_machine) { Inputs::Keyboard::Key key = Inputs::Keyboard::Key::Space; if(!KeyboardKeyForSDLScancode(event.key.keysym.scancode, key)) break; - keyboard_machine->get_keyboard().set_key_pressed(key, is_pressed); + + char key_value = '\0'; + const char *key_name = SDL_GetKeyName(event.key.keysym.sym); + if(key_name[0] >= 0) key_value = key_name[0]; + + keyboard_machine->get_keyboard().set_key_pressed(key, key_value, is_pressed); break; }