diff --git a/Machines/Amiga/Keyboard.cpp b/Machines/Amiga/Keyboard.cpp new file mode 100644 index 000000000..fee6d68f3 --- /dev/null +++ b/Machines/Amiga/Keyboard.cpp @@ -0,0 +1,92 @@ +// +// Keyboard.cpp +// Clock Signal +// +// Created by Thomas Harte on 29/07/2021. +// Copyright © 2021 Thomas Harte. All rights reserved. +// + +#include "Keyboard.hpp" + +// Notes to self: +// +// +// Before +// the transmission starts, both KCLK and KDAT are high. The keyboard starts +// the transmission by putting out the first data bit (on KDAT), followed by +// a pulse on KCLK (low then high); then it puts out the second data bit and +// pulses KCLK until all eight data bits have been sent. +// +// When the computer has received the eighth bit, it must pulse KDAT low for +// at least 1 (one) microsecond, as a handshake signal to the keyboard. The +// keyboard must be able to detect pulses greater than or equal +// to 1 microsecond. Software MUST pulse the line low for 85 microseconds to +// ensure compatibility with all keyboard models. +// +// +// If the handshake pulse does not arrive within +// 143 ms of the last clock of the transmission, the keyboard will assume +// that the computer is still waiting for the rest of the transmission and is +// therefore out of sync. The keyboard will then attempt to restore sync by +// going into "resync mode." In this mode, the keyboard clocks out a 1 and +// waits for a handshake pulse. If none arrives within 143 ms, it clocks out +// another 1 and waits again. +// +// The keyboard Hard Resets the Amiga by pulling KCLK low and starting a 500 +// millisecond timer. When one or more of the keys is released and 500 +// milliseconds have passed, the keyboard will release KCLK. +// +// The usual sequence of events will therefore be: power-up; synchronize; +// transmit "initiate power-up key stream" ($FD); transmit "terminate key +// stream" ($FE). + +using namespace Amiga; + +uint8_t Keyboard::update(uint8_t input) { + // If a bit transmission is ongoing, continue that, up to and including + // the handshake. If no handshake comes, set a macro state of synchronising. + switch(shift_state_) { + case ShiftState::Shifting: + // The keyboard processor sets the KDAT line about 20 microseconds before it + // pulls KCLK low. KCLK stays low for about 20 microseconds, then goes high + // again. The processor waits another 20 microseconds before changing KDAT. + switch(bit_phase_) { + default: break; + case 0: lines_ = Lines::Clock | (shift_sequence_ & 1); break; + case 20: lines_ = (shift_sequence_ & 1); break; + case 40: lines_ = Lines::Clock | (shift_sequence_ & 1); break; + } + bit_phase_ = (bit_phase_ + 1) % 60; + + if(!bit_phase_) { + --bits_remaining_; + shift_sequence_ >>= 1; + if(!bits_remaining_) { + shift_state_ = ShiftState::AwaitingHandshake; + } + } + return lines_; + + case ShiftState::AwaitingHandshake: + if(!(input & Lines::Data)) { + shift_state_ = ShiftState::Idle; + } + ++bit_phase_; + if(bit_phase_ == 143) { +// shift_state_ = ShiftState::Synchronising; + } + return lines_; + + default: break; + } + + switch(state_) { + case State::Startup: + bit_phase_ = 0; + shift_sequence_ = 0xff; + shift_state_ = ShiftState::Shifting; + break; + } + + return lines_; +} diff --git a/Machines/Amiga/Keyboard.hpp b/Machines/Amiga/Keyboard.hpp new file mode 100644 index 000000000..f2529c257 --- /dev/null +++ b/Machines/Amiga/Keyboard.hpp @@ -0,0 +1,45 @@ +// +// Keyboard.hpp +// Clock Signal +// +// Created by Thomas Harte on 29/07/2021. +// Copyright © 2021 Thomas Harte. All rights reserved. +// + +#ifndef Keyboard_hpp +#define Keyboard_hpp + +#include + +namespace Amiga { + +class Keyboard { + public: + enum Lines: uint8_t { + Data = (1 << 0), + Clock = (1 << 1), + }; + + uint8_t update(uint8_t); + + private: + enum class ShiftState { + Shifting, + AwaitingHandshake, + Idle, + } shift_state_ = ShiftState::Idle; + + enum class State { + Startup, + } state_ = State::Startup; + + int bit_phase_ = 0; + uint32_t shift_sequence_ = 0; + int bits_remaining_ = 0; + + uint8_t lines_ = 0; +}; + +} + +#endif /* Keyboard_hpp */ diff --git a/OSBindings/Mac/Clock Signal.xcodeproj/project.pbxproj b/OSBindings/Mac/Clock Signal.xcodeproj/project.pbxproj index fa90acaf5..fd295ad7c 100644 --- a/OSBindings/Mac/Clock Signal.xcodeproj/project.pbxproj +++ b/OSBindings/Mac/Clock Signal.xcodeproj/project.pbxproj @@ -584,6 +584,8 @@ 4B9EC0E326AA27BA0060A31F /* Blitter.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B9EC0E126AA27BA0060A31F /* Blitter.cpp */; }; 4B9EC0E626AA4A660060A31F /* Chipset.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B9EC0E426AA4A660060A31F /* Chipset.cpp */; }; 4B9EC0E726AA4A660060A31F /* Chipset.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B9EC0E426AA4A660060A31F /* Chipset.cpp */; }; + 4B9EC0EA26B384080060A31F /* Keyboard.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B9EC0E826B384080060A31F /* Keyboard.cpp */; }; + 4B9EC0EB26B384080060A31F /* Keyboard.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B9EC0E826B384080060A31F /* Keyboard.cpp */; }; 4B9F11C92272375400701480 /* qltrace.txt.gz in Resources */ = {isa = PBXBuildFile; fileRef = 4B9F11C82272375400701480 /* qltrace.txt.gz */; }; 4B9F11CA2272433900701480 /* libz.tbd in Frameworks */ = {isa = PBXBuildFile; fileRef = 4B69FB451C4D950F00B5F0AA /* libz.tbd */; }; 4B9F11CC22729B3600701480 /* OPCLOGR2.BIN in Resources */ = {isa = PBXBuildFile; fileRef = 4B9F11CB22729B3500701480 /* OPCLOGR2.BIN */; }; @@ -1592,6 +1594,8 @@ 4B9EC0E126AA27BA0060A31F /* Blitter.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = Blitter.cpp; sourceTree = ""; }; 4B9EC0E426AA4A660060A31F /* Chipset.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = Chipset.cpp; sourceTree = ""; }; 4B9EC0E526AA4A660060A31F /* Chipset.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = Chipset.hpp; sourceTree = ""; }; + 4B9EC0E826B384080060A31F /* Keyboard.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = Keyboard.cpp; sourceTree = ""; }; + 4B9EC0E926B384080060A31F /* Keyboard.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = Keyboard.hpp; sourceTree = ""; }; 4B9F11C82272375400701480 /* qltrace.txt.gz */ = {isa = PBXFileReference; lastKnownFileType = archive.gzip; path = qltrace.txt.gz; sourceTree = ""; }; 4B9F11CB22729B3500701480 /* OPCLOGR2.BIN */ = {isa = PBXFileReference; lastKnownFileType = archive.macbinary; name = OPCLOGR2.BIN; path = "68000 Coverage/OPCLOGR2.BIN"; sourceTree = ""; }; 4BA0F68C1EEA0E8400E9489E /* ZX8081.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = ZX8081.cpp; sourceTree = ""; }; @@ -4275,9 +4279,11 @@ 4BC080D826A25ADA00D03FD8 /* Amiga.cpp */, 4B9EC0E126AA27BA0060A31F /* Blitter.cpp */, 4B9EC0E426AA4A660060A31F /* Chipset.cpp */, + 4B9EC0E826B384080060A31F /* Keyboard.cpp */, 4BC080D726A25ADA00D03FD8 /* Amiga.hpp */, 4B9EC0E026AA260C0060A31F /* Blitter.hpp */, 4B9EC0E526AA4A660060A31F /* Chipset.hpp */, + 4B9EC0E926B384080060A31F /* Keyboard.hpp */, ); path = Amiga; sourceTree = ""; @@ -5286,6 +5292,7 @@ 4B055A9B1FAE85DA0060FFFF /* AcornADF.cpp in Sources */, 4B0E04F11FC9EA9500F43484 /* MSX.cpp in Sources */, 4B055AD51FAE9B0B0060FFFF /* Video.cpp in Sources */, + 4B9EC0EB26B384080060A31F /* Keyboard.cpp in Sources */, 4B894521201967B4007DE474 /* StaticAnalyser.cpp in Sources */, 4B8318B522D3E548006DB630 /* Macintosh.cpp in Sources */, 4B8DF4FA254E36AE00F3433C /* Video.cpp in Sources */, @@ -5601,6 +5608,7 @@ 4B55CE5F1C3B7D960093A61B /* MachineDocument.swift in Sources */, 4B1B58FF246E19FD009C171E /* State.cpp in Sources */, 4B2B3A4C1F9B8FA70062DABF /* MemoryFuzzer.cpp in Sources */, + 4B9EC0EA26B384080060A31F /* Keyboard.cpp in Sources */, 4B7913CC1DFCD80E00175A82 /* Video.cpp in Sources */, 4BC57CD92436A62900FBC404 /* State.cpp in Sources */, 4BDA00E622E699B000AC3CD0 /* CSMachine.mm in Sources */,