diff --git a/Machines/Acorn/Archimedes/Archimedes.cpp b/Machines/Acorn/Archimedes/Archimedes.cpp index 567ac85c4..6fba1369a 100644 --- a/Machines/Acorn/Archimedes/Archimedes.cpp +++ b/Machines/Acorn/Archimedes/Archimedes.cpp @@ -11,6 +11,7 @@ #include "HalfDuplexSerial.hpp" #include "InputOutputController.hpp" #include "Keyboard.hpp" +#include "KeyboardMapper.hpp" #include "MemoryController.hpp" #include "Sound.hpp" @@ -40,6 +41,7 @@ namespace Archimedes { class ConcreteMachine: public Machine, + public MachineTypes::MappedKeyboardMachine, public MachineTypes::MediaTarget, public MachineTypes::TimedMachine, public MachineTypes::ScanProducer @@ -250,6 +252,18 @@ class ConcreteMachine: return false; } + // MARK: - MappedKeyboardMachine. + MappedKeyboardMachine::KeyboardMapper *get_keyboard_mapper() override { + return &keyboard_mapper_; + } + Archimedes::KeyboardMapper keyboard_mapper_; + + void set_key_state(uint16_t key, bool is_pressed) override { + const int row = Archimedes::KeyboardMapper::row(key); + const int column = Archimedes::KeyboardMapper::column(key); + executor_.bus.keyboard().set_key_state(row, column, is_pressed); + } + // MARK: - ARM execution static constexpr auto arm_model = InstructionSet::ARM::Model::ARMv2; InstructionSet::ARM::Executor> executor_; diff --git a/Machines/Acorn/Archimedes/InputOutputController.hpp b/Machines/Acorn/Archimedes/InputOutputController.hpp index 8bf5a6124..421ac6c83 100644 --- a/Machines/Acorn/Archimedes/InputOutputController.hpp +++ b/Machines/Acorn/Archimedes/InputOutputController.hpp @@ -328,10 +328,12 @@ struct InputOutputController { update_interrupts(); } - auto &sound() { return sound_; } - const auto &sound() const { return sound_; } - auto &video() { return video_; } - const auto &video() const { return video_; } + auto &sound() { return sound_; } + const auto &sound() const { return sound_; } + auto &video() { return video_; } + const auto &video() const { return video_; } + auto &keyboard() { return keyboard_; } + const auto &keyboard() const { return keyboard_; } void update_interrupts() { if(sound_.interrupt()) { diff --git a/Machines/Acorn/Archimedes/Keyboard.hpp b/Machines/Acorn/Archimedes/Keyboard.hpp index 5140e772b..b69d45a14 100644 --- a/Machines/Acorn/Archimedes/Keyboard.hpp +++ b/Machines/Acorn/Archimedes/Keyboard.hpp @@ -16,6 +16,10 @@ namespace Archimedes { struct Keyboard { Keyboard(HalfDuplexSerial &serial) : serial_(serial) {} + void set_key_state([[maybe_unused]] int row, [[maybe_unused]] int column, [[maybe_unused]] bool is_pressed) { + + } + void update() { if(serial_.events(KeyboardParty) & HalfDuplexSerial::Receive) { const uint8_t input = serial_.input(KeyboardParty); diff --git a/Machines/Acorn/Archimedes/KeyboardMapper.hpp b/Machines/Acorn/Archimedes/KeyboardMapper.hpp new file mode 100644 index 000000000..416ece362 --- /dev/null +++ b/Machines/Acorn/Archimedes/KeyboardMapper.hpp @@ -0,0 +1,148 @@ +// +// KeyboardMapper.hpp +// Clock Signal +// +// Created by Thomas Harte on 23/03/2024. +// Copyright © 2024 Thomas Harte. All rights reserved. +// + +#pragma once + +#include "../../KeyboardMachine.hpp" + +namespace Archimedes { + +class KeyboardMapper: public MachineTypes::MappedKeyboardMachine::KeyboardMapper { + public: + static constexpr uint16_t map(int row, int column) { + return static_cast((row << 4) | column); + } + + static constexpr int row(uint16_t key) { + return key >> 4; + } + + static constexpr int column(uint16_t key) { + return key & 0xf; + } + + // Adapted from the A500 Series Technical Reference Manual. + uint16_t mapped_key_for_key(Inputs::Keyboard::Key key) const override { + using k = Inputs::Keyboard::Key; + switch(key) { + case k::Escape: return map(0, 0); + case k::F1: return map(0, 1); + case k::F2: return map(0, 2); + case k::F3: return map(0, 3); + case k::F4: return map(0, 4); + case k::F5: return map(0, 5); + case k::F6: return map(0, 6); + case k::F7: return map(0, 7); + case k::F8: return map(0, 8); + case k::F9: return map(0, 9); + case k::F10: return map(0, 10); + case k::F11: return map(0, 11); + case k::F12: return map(0, 12); + case k::PrintScreen: return map(0, 13); + case k::ScrollLock: return map(0, 14); + case k::Pause: return map(0, 15); + + case k::BackTick: return map(1, 0); + case k::k1: return map(1, 1); + case k::k2: return map(1, 2); + case k::k3: return map(1, 3); + case k::k4: return map(1, 4); + case k::k5: return map(1, 5); + case k::k6: return map(1, 6); + case k::k7: return map(1, 7); + case k::k8: return map(1, 8); + case k::k9: return map(1, 9); + case k::k0: return map(1, 10); + case k::Hyphen: return map(1, 11); + case k::Equals: return map(1, 12); + // TODO: pound key. + case k::Backspace: return map(1, 14); + case k::Insert: return map(1, 15); + + case k::Home: return map(2, 0); + case k::PageUp: return map(2, 1); + case k::NumLock: return map(2, 2); + case k::KeypadSlash: return map(2, 3); + case k::KeypadAsterisk: return map(2, 4); + // TODO: keypad hash key + case k::Tab: return map(2, 6); + case k::Q: return map(2, 7); + case k::W: return map(2, 8); + case k::E: return map(2, 9); + case k::R: return map(2, 10); + case k::T: return map(2, 11); + case k::Y: return map(2, 12); + case k::U: return map(2, 13); + case k::I: return map(2, 14); + case k::O: return map(2, 15); + + case k::P: return map(3, 0); + case k::OpenSquareBracket: return map(3, 1); + case k::CloseSquareBracket: return map(3, 2); + case k::Backslash: return map(3, 3); + case k::Delete: return map(3, 4); + case k::End: return map(3, 5); + case k::PageDown: return map(3, 6); + case k::Keypad7: return map(3, 7); + case k::Keypad8: return map(3, 8); + case k::Keypad9: return map(3, 9); + case k::KeypadMinus: return map(3, 10); + case k::LeftControl: return map(3, 11); + case k::A: return map(3, 12); + case k::S: return map(3, 13); + case k::D: return map(3, 14); + case k::F: return map(3, 15); + + case k::G: return map(4, 0); + case k::H: return map(4, 1); + case k::J: return map(4, 2); + case k::K: return map(4, 3); + case k::L: return map(4, 4); + case k::Semicolon: return map(4, 5); + case k::Quote: return map(4, 6); + case k::Enter: return map(4, 7); + case k::Keypad4: return map(4, 8); + case k::Keypad5: return map(4, 9); + case k::Keypad6: return map(4, 10); + case k::KeypadPlus: return map(4, 11); + case k::LeftShift: return map(4, 12); + case k::Z: return map(4, 14); + case k::X: return map(4, 15); + + case k::C: return map(5, 0); + case k::V: return map(5, 1); + case k::B: return map(5, 2); + case k::N: return map(5, 3); + case k::M: return map(5, 4); + case k::Comma: return map(5, 5); + case k::FullStop: return map(5, 6); + case k::ForwardSlash: return map(5, 7); + case k::RightShift: return map(5, 8); + case k::Up: return map(5, 9); + case k::Keypad1: return map(5, 10); + case k::Keypad2: return map(5, 11); + case k::Keypad3: return map(5, 12); + case k::CapsLock: return map(5, 13); + case k::LeftOption: return map(5, 14); + case k::Space: return map(5, 15); + + case k::RightOption: return map(6, 0); + case k::RightControl: return map(6, 1); + case k::Left: return map(6, 2); + case k::Down: return map(6, 3); + case k::Right: return map(6, 4); + case k::Keypad0: return map(6, 5); + case k::KeypadDecimalPoint: return map(6, 6); + case k::KeypadEnter: return map(6, 7); + + default: return MachineTypes::MappedKeyboardMachine::KeyNotMapped; + } + } +}; + +} diff --git a/Machines/Acorn/Archimedes/MemoryController.hpp b/Machines/Acorn/Archimedes/MemoryController.hpp index 9cff88ee8..05c5aecec 100644 --- a/Machines/Acorn/Archimedes/MemoryController.hpp +++ b/Machines/Acorn/Archimedes/MemoryController.hpp @@ -199,10 +199,13 @@ struct MemoryController { } void tick_timers() { ioc_.tick_timers(); } - auto &sound() { return ioc_.sound(); } - const auto &sound() const { return ioc_.sound(); } - auto &video() { return ioc_.video(); } - const auto &video() const { return ioc_.video(); } + + auto &sound() { return ioc_.sound(); } + const auto &sound() const { return ioc_.sound(); } + auto &video() { return ioc_.video(); } + const auto &video() const { return ioc_.video(); } + auto &keyboard() { return ioc_.keyboard(); } + const auto &keyboard() const { return ioc_.keyboard(); } private: Log::Logger logger; diff --git a/OSBindings/Mac/Clock Signal.xcodeproj/project.pbxproj b/OSBindings/Mac/Clock Signal.xcodeproj/project.pbxproj index 54a36d96d..0c016c128 100644 --- a/OSBindings/Mac/Clock Signal.xcodeproj/project.pbxproj +++ b/OSBindings/Mac/Clock Signal.xcodeproj/project.pbxproj @@ -1815,6 +1815,7 @@ 4BAB1E582BAB5C210002C9B9 /* MemoryController.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = MemoryController.hpp; sourceTree = ""; }; 4BAB1E592BAB5CB90002C9B9 /* CMOSRAM.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = CMOSRAM.hpp; sourceTree = ""; }; 4BAB1E5A2BAB5F400002C9B9 /* InputOutputController.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = InputOutputController.hpp; sourceTree = ""; }; + 4BAB1E5B2BAF59CB0002C9B9 /* KeyboardMapper.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = KeyboardMapper.hpp; sourceTree = ""; }; 4BAB62AC1D3272D200DF5BA0 /* Disk.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = Disk.hpp; sourceTree = ""; }; 4BAB62AE1D32730D00DF5BA0 /* Storage.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = Storage.hpp; sourceTree = ""; }; 4BAF2B4C2004580C00480230 /* DMK.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = DMK.cpp; sourceTree = ""; }; @@ -4405,6 +4406,7 @@ 4BAB1E552BAB5B6D0002C9B9 /* HalfDuplexSerial.hpp */, 4BAB1E5A2BAB5F400002C9B9 /* InputOutputController.hpp */, 4BAB1E542BAB5B3F0002C9B9 /* Keyboard.hpp */, + 4BAB1E5B2BAF59CB0002C9B9 /* KeyboardMapper.hpp */, 4BAB1E582BAB5C210002C9B9 /* MemoryController.hpp */, 4BAB1E532BAB5B040002C9B9 /* Sound.hpp */, 4BAB1E562BAB5BC60002C9B9 /* Video.hpp */,