1
0
mirror of https://github.com/TomHarte/CLK.git synced 2024-07-08 15:29:09 +00:00

Attempts to tie an intelligent keyboard to the other end of its serial line.

This commit is contained in:
Thomas Harte 2019-10-16 23:21:14 -04:00
parent b69180ba01
commit 0fd8813ddb
4 changed files with 89 additions and 4 deletions

View File

@ -57,6 +57,7 @@ void ACIA::write(int address, uint8_t value) {
} else {
if((value&3) == 3) {
transmit.reset_writing();
transmit.write(true);
request_to_send.reset_writing();
} else {
switch(value & 3) {

View File

@ -19,14 +19,25 @@ void Line::advance_writer(int cycles) {
while(!events_.empty()) {
if(events_.front().delay < cycles) {
cycles -= events_.front().delay;
write_cycles_since_delegate_call_ += events_.front().delay;
const auto old_level = level_;
auto iterator = events_.begin() + 1;
while(iterator != events_.end() && iterator->type != Event::Delay) {
level_ = iterator->type == Event::SetHigh;
++iterator;
}
events_.erase(events_.begin(), iterator);
if(old_level != level_) {
if(read_delegate_) {
read_delegate_->serial_line_did_change_output(this, Storage::Time(write_cycles_since_delegate_call_, clock_rate_), level_);
write_cycles_since_delegate_call_ = 0;
}
}
} else {
events_.front().delay -= cycles;
write_cycles_since_delegate_call_ += cycles;
break;
}
}
@ -50,6 +61,7 @@ void Line::write(int cycles, int count, int levels) {
events_[event].type = Event::Delay;
events_[event].delay = cycles;
events_[event+1].type = (levels&1) ? Event::SetHigh : Event::SetLow;
levels >>= 1;
event += 2;
}
}
@ -66,10 +78,22 @@ void Line::reset_writing() {
void Line::flush_writing() {
remaining_delays_ = 0;
for(const auto &event : events_) {
bool new_level = level_;
switch(event.type) {
default: break;
case Event::SetHigh: level_ = true; break;
case Event::SetLow: level_ = false; break;
case Event::SetHigh: new_level = true; break;
case Event::SetLow: new_level = false; break;
case Event::Delay:
write_cycles_since_delegate_call_ += event.delay;
continue;
}
if(new_level != level_) {
level_ = new_level;
if(read_delegate_) {
read_delegate_->serial_line_did_change_output(this, Storage::Time(write_cycles_since_delegate_call_, clock_rate_), level_);
write_cycles_since_delegate_call_ = 0;
}
}
}
events_.clear();
@ -78,3 +102,8 @@ void Line::flush_writing() {
bool Line::read() {
return level_;
}
void Line::set_read_delegate(ReadDelegate *delegate) {
read_delegate_ = delegate;
write_cycles_since_delegate_call_ = 0;
}

View File

@ -10,6 +10,7 @@
#define SerialPort_hpp
#include <vector>
#include "../../Storage/Storage.hpp"
namespace Serial {
@ -53,6 +54,11 @@ class Line {
/// @returns The instantaneous level of this line.
bool read();
struct ReadDelegate {
virtual void serial_line_did_change_output(Line *line, Storage::Time time_since_last_change, bool new_level) = 0;
};
void set_read_delegate(ReadDelegate *delegate);
private:
struct Event {
enum Type {
@ -64,6 +70,9 @@ class Line {
int remaining_delays_ = 0;
bool level_ = false;
int clock_rate_ = 0;
ReadDelegate *read_delegate_ = nullptr;
int write_cycles_since_delegate_call_ = 0;
};
/*!

View File

@ -21,6 +21,7 @@
#include "../../ClockReceiver/ForceInline.hpp"
#include "../../Outputs/Speaker/Implementation/LowpassSpeaker.hpp"
#include "../../Outputs/Log.hpp"
#include "../Utility/MemoryPacker.hpp"
#include "../Utility/MemoryFuzzer.hpp"
@ -30,8 +31,50 @@ namespace ST {
const int CLOCK_RATE = 8000000;
using Target = Analyser::Static::Target;
/*!
A receiver for the Atari ST's "intelligent keyboard" commands, which actually cover
keyboard input and output and mouse handling.
*/
class IntelligentKeyboard:
public Serial::Line::ReadDelegate {
public:
IntelligentKeyboard(Serial::Line &input, Serial::Line &output) : output_line_(output) {
input.set_read_delegate(this);
}
void serial_line_did_change_output(Serial::Line *, Storage::Time time_since_last_change, bool new_level) final {
// Figure out how many bits have passed. TODO: in fixed point?
const float number_of_bits = time_since_last_change.get<float>() * 7812.5f + bit_offset_;
bit_offset_ = fmodf(number_of_bits, 1.0f);
int bits_remaining = int(number_of_bits);
while(bits_remaining--) {
if(!bit_count_) {
// Check for a potential start bit.
if(!new_level) {
bit_count_ = 10;
command_ = 0;
}
} else {
command_ >>= 1;
command_ |= new_level ? 0 : 0x200;
--bit_count_;
if(!bit_count_) {
LOG("[IKBD] Should perform " << PADHEX(2) << ((command_ >> 1) & 0xff));
}
}
}
}
private:
int bit_count_ = 0;
int command_ = 0;
Serial::Line &output_line_;
float bit_offset_ = 0.0f;
};
using Target = Analyser::Static::Target;
class ConcreteMachine:
public Atari::ST::Machine,
public CPU::MC68000::BusHandler,
@ -42,7 +85,8 @@ class ConcreteMachine:
keyboard_acia_(Cycles(500000)),
midi_acia_(Cycles(500000)),
ay_(audio_queue_),
speaker_(ay_) {
speaker_(ay_),
ikbd_(keyboard_acia_->transmit, keyboard_acia_->receive) {
set_clock_rate(CLOCK_RATE);
speaker_.set_input_rate(CLOCK_RATE / 4);
@ -380,6 +424,8 @@ class ConcreteMachine:
Outputs::Speaker::LowpassSpeaker<GI::AY38910::AY38910> speaker_;
HalfCycles cycles_since_audio_update_;
IntelligentKeyboard ikbd_;
std::vector<uint16_t> ram_;
std::vector<uint16_t> rom_;