mirror of
https://github.com/TomHarte/CLK.git
synced 2025-02-28 06:29:37 +00:00
Attempts mostly to implement 6850 output.
This commit is contained in:
parent
4bf81d3b90
commit
8b50a7d6e3
@ -13,22 +13,39 @@
|
|||||||
|
|
||||||
using namespace Motorola::ACIA;
|
using namespace Motorola::ACIA;
|
||||||
|
|
||||||
|
ACIA::ACIA() {}
|
||||||
|
|
||||||
uint8_t ACIA::read(int address) {
|
uint8_t ACIA::read(int address) {
|
||||||
if(address&1) {
|
if(address&1) {
|
||||||
LOG("Read from receive register");
|
LOG("Read from receive register");
|
||||||
} else {
|
} else {
|
||||||
LOG("Read status");
|
LOG("Read status");
|
||||||
return status_;
|
return
|
||||||
|
((next_transmission_ == NoTransmission) ? 0x02 : 0x00) |
|
||||||
|
(data_carrier_detect.read() ? 0x04 : 0x00) |
|
||||||
|
(clear_to_send.read() ? 0x08 : 0x00)
|
||||||
|
;
|
||||||
|
|
||||||
|
// b0: receive data full.
|
||||||
|
// b1: transmit data empty.
|
||||||
|
// b2: DCD.
|
||||||
|
// b3: CTS.
|
||||||
|
// b4: framing error (i.e. no first stop bit where expected).
|
||||||
|
// b5: receiver overran.
|
||||||
|
// b6: parity error.
|
||||||
|
// b7: IRQ state.
|
||||||
}
|
}
|
||||||
return 0x00;
|
return 0x00;
|
||||||
}
|
}
|
||||||
|
|
||||||
void ACIA::write(int address, uint8_t value) {
|
void ACIA::write(int address, uint8_t value) {
|
||||||
if(address&1) {
|
if(address&1) {
|
||||||
LOG("Write to transmit register");
|
next_transmission_ = value;
|
||||||
|
consider_transmission();
|
||||||
} else {
|
} else {
|
||||||
if((value&3) == 3) {
|
if((value&3) == 3) {
|
||||||
LOG("Reset");
|
transmit.reset_writing();
|
||||||
|
request_to_send.reset_writing();
|
||||||
} else {
|
} else {
|
||||||
switch(value & 3) {
|
switch(value & 3) {
|
||||||
default:
|
default:
|
||||||
@ -38,30 +55,77 @@ void ACIA::write(int address, uint8_t value) {
|
|||||||
}
|
}
|
||||||
switch((value >> 2) & 7) {
|
switch((value >> 2) & 7) {
|
||||||
default:
|
default:
|
||||||
case 0: word_size_ = 7; stop_bits_ = 2; parity_ = Parity::Even; break;
|
case 0: data_bits_ = 7; stop_bits_ = 2; parity_ = Parity::Even; break;
|
||||||
case 1: word_size_ = 7; stop_bits_ = 2; parity_ = Parity::Odd; break;
|
case 1: data_bits_ = 7; stop_bits_ = 2; parity_ = Parity::Odd; break;
|
||||||
case 2: word_size_ = 7; stop_bits_ = 1; parity_ = Parity::Even; break;
|
case 2: data_bits_ = 7; stop_bits_ = 1; parity_ = Parity::Even; break;
|
||||||
case 3: word_size_ = 7; stop_bits_ = 1; parity_ = Parity::Odd; break;
|
case 3: data_bits_ = 7; stop_bits_ = 1; parity_ = Parity::Odd; break;
|
||||||
case 4: word_size_ = 8; stop_bits_ = 2; parity_ = Parity::None; break;
|
case 4: data_bits_ = 8; stop_bits_ = 2; parity_ = Parity::None; break;
|
||||||
case 5: word_size_ = 8; stop_bits_ = 1; parity_ = Parity::None; break;
|
case 5: data_bits_ = 8; stop_bits_ = 1; parity_ = Parity::None; break;
|
||||||
case 6: word_size_ = 8; stop_bits_ = 1; parity_ = Parity::Even; break;
|
case 6: data_bits_ = 8; stop_bits_ = 1; parity_ = Parity::Even; break;
|
||||||
case 7: word_size_ = 8; stop_bits_ = 1; parity_ = Parity::Odd; break;
|
case 7: data_bits_ = 8; stop_bits_ = 1; parity_ = Parity::Odd; break;
|
||||||
}
|
}
|
||||||
switch((value >> 5) & 3) {
|
switch((value >> 5) & 3) {
|
||||||
case 0: set_ready_to_transmit(false); transmit_interrupt_enabled_ = false; break;
|
case 0: request_to_send.write(false); transmit_interrupt_enabled_ = false; break;
|
||||||
case 1: set_ready_to_transmit(false); transmit_interrupt_enabled_ = true; break;
|
case 1: request_to_send.write(false); transmit_interrupt_enabled_ = true; break;
|
||||||
case 2: set_ready_to_transmit(true); transmit_interrupt_enabled_ = false; break;
|
case 2: request_to_send.write(true); transmit_interrupt_enabled_ = false; break;
|
||||||
case 3: set_ready_to_transmit(false); transmit_interrupt_enabled_ = false; break; /* TODO: transmit a break level on the transmit output. */
|
case 3:
|
||||||
|
request_to_send.write(false);
|
||||||
|
transmit_interrupt_enabled_ = false;
|
||||||
|
transmit.reset_writing();
|
||||||
|
transmit.write(false);
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
receive_interrupt_enabled_ = value & 0x80;
|
receive_interrupt_enabled_ = value & 0x80;
|
||||||
LOG("Write to control register");
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void ACIA::run_for(HalfCycles) {
|
void ACIA::run_for(HalfCycles length) {
|
||||||
|
// Transmission.
|
||||||
|
int transmit_advance = length.as_int();
|
||||||
|
if(next_transmission_ != NoTransmission) {
|
||||||
|
const auto write_data_time_remaining = transmit.write_data_time_remaining();
|
||||||
|
if(transmit_advance > write_data_time_remaining) {
|
||||||
|
transmit.flush_writing();
|
||||||
|
transmit_advance -= write_data_time_remaining;
|
||||||
|
consider_transmission();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
transmit.advance_writer(transmit_advance);
|
||||||
|
|
||||||
|
// Reception.
|
||||||
}
|
}
|
||||||
|
|
||||||
void ACIA::set_ready_to_transmit(bool) {
|
void ACIA::consider_transmission() {
|
||||||
|
if(next_transmission_ != NoTransmission && !transmit.write_data_time_remaining()) {
|
||||||
|
// Establish start bit and [7 or 8] data bits.
|
||||||
|
if(data_bits_ == 7) next_transmission_ &= 0x7f;
|
||||||
|
int transmission = next_transmission_ << 1;
|
||||||
|
|
||||||
|
// Add a parity bit, if any.
|
||||||
|
int mask = 0x2 << data_bits_;
|
||||||
|
if(parity_ != Parity::None) {
|
||||||
|
next_transmission_ ^= next_transmission_ >> 4;
|
||||||
|
next_transmission_ ^= next_transmission_ >> 2;
|
||||||
|
next_transmission_ ^= next_transmission_ >> 1;
|
||||||
|
|
||||||
|
if((next_transmission_&1) != (parity_ == Parity::Even)) {
|
||||||
|
transmission |= mask;
|
||||||
|
}
|
||||||
|
mask <<= 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add stop bits.
|
||||||
|
for(int c = 0; c < stop_bits_; ++c) {
|
||||||
|
transmission |= mask;
|
||||||
|
mask <<= 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Output all that.
|
||||||
|
const int total_bits = 1 + data_bits_ + stop_bits_ + (parity_ != Parity::None);
|
||||||
|
transmit.write(divider_, total_bits, transmission);
|
||||||
|
|
||||||
|
// Mark the transmit register as empty again.
|
||||||
|
next_transmission_ = NoTransmission;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -11,12 +11,15 @@
|
|||||||
|
|
||||||
#include <cstdint>
|
#include <cstdint>
|
||||||
#include "../../ClockReceiver/ClockReceiver.hpp"
|
#include "../../ClockReceiver/ClockReceiver.hpp"
|
||||||
|
#include "../SerialPort/SerialPort.hpp"
|
||||||
|
|
||||||
namespace Motorola {
|
namespace Motorola {
|
||||||
namespace ACIA {
|
namespace ACIA {
|
||||||
|
|
||||||
class ACIA {
|
class ACIA {
|
||||||
public:
|
public:
|
||||||
|
ACIA();
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
Reads from the ACIA.
|
Reads from the ACIA.
|
||||||
|
|
||||||
@ -37,17 +40,28 @@ class ACIA {
|
|||||||
|
|
||||||
void run_for(HalfCycles);
|
void run_for(HalfCycles);
|
||||||
|
|
||||||
|
// Input lines.
|
||||||
|
Serial::Line receive;
|
||||||
|
Serial::Line clear_to_send;
|
||||||
|
Serial::Line data_carrier_detect;
|
||||||
|
|
||||||
|
// Output lines.
|
||||||
|
Serial::Line transmit;
|
||||||
|
Serial::Line request_to_send;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
int divider_ = 1;
|
int divider_ = 1;
|
||||||
uint8_t status_ = 0x00;
|
|
||||||
enum class Parity {
|
enum class Parity {
|
||||||
Even, Odd, None
|
Even, Odd, None
|
||||||
} parity_ = Parity::None;
|
} parity_ = Parity::None;
|
||||||
int word_size_ = 7, stop_bits_ = 2;
|
int data_bits_ = 7, stop_bits_ = 2;
|
||||||
|
|
||||||
|
static const int NoTransmission = 0x100;
|
||||||
|
int next_transmission_ = NoTransmission;
|
||||||
|
void consider_transmission();
|
||||||
|
|
||||||
bool receive_interrupt_enabled_ = false;
|
bool receive_interrupt_enabled_ = false;
|
||||||
bool transmit_interrupt_enabled_ = false;
|
bool transmit_interrupt_enabled_ = false;
|
||||||
|
|
||||||
void set_ready_to_transmit(bool);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -7,3 +7,68 @@
|
|||||||
//
|
//
|
||||||
|
|
||||||
#include "SerialPort.hpp"
|
#include "SerialPort.hpp"
|
||||||
|
|
||||||
|
using namespace Serial;
|
||||||
|
|
||||||
|
void Line::advance_writer(int cycles) {
|
||||||
|
remaining_delays_ = std::max(remaining_delays_ - cycles, 0);
|
||||||
|
while(!events_.empty()) {
|
||||||
|
if(events_.front().delay < cycles) {
|
||||||
|
cycles -= events_.front().delay;
|
||||||
|
auto iterator = events_.begin() + 1;
|
||||||
|
while(iterator != events_.end() && iterator->type != Event::Delay) {
|
||||||
|
level_ = iterator->type == Event::SetHigh;
|
||||||
|
++iterator;
|
||||||
|
}
|
||||||
|
events_.erase(events_.begin(), iterator);
|
||||||
|
} else {
|
||||||
|
events_.front().delay -= cycles;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Line::write(bool level) {
|
||||||
|
if(!events_.empty()) {
|
||||||
|
events_.emplace_back();
|
||||||
|
events_.back().type = level ? Event::SetHigh : Event::SetLow;
|
||||||
|
} else {
|
||||||
|
level_ = level;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Line::write(int cycles, int count, int levels) {
|
||||||
|
remaining_delays_ += count*cycles;
|
||||||
|
|
||||||
|
auto event = events_.size();
|
||||||
|
events_.resize(events_.size() + size_t(count)*2);
|
||||||
|
while(count--) {
|
||||||
|
events_[event].type = Event::Delay;
|
||||||
|
events_[event].delay = cycles;
|
||||||
|
events_[event+1].type = (levels&1) ? Event::SetHigh : Event::SetLow;
|
||||||
|
event += 2;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int Line::write_data_time_remaining() {
|
||||||
|
return remaining_delays_;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Line::reset_writing() {
|
||||||
|
events_.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
void Line::flush_writing() {
|
||||||
|
for(const auto &event : events_) {
|
||||||
|
switch(event.type) {
|
||||||
|
default: break;
|
||||||
|
case Event::SetHigh: level_ = true; break;
|
||||||
|
case Event::SetLow: level_ = false; break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
events_.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Line::read() {
|
||||||
|
return level_;
|
||||||
|
}
|
||||||
|
@ -9,19 +9,9 @@
|
|||||||
#ifndef SerialPort_hpp
|
#ifndef SerialPort_hpp
|
||||||
#define SerialPort_hpp
|
#define SerialPort_hpp
|
||||||
|
|
||||||
namespace Serial {
|
#include <vector>
|
||||||
|
|
||||||
/// Signal is an amalgamation of the RS-232-esque signals and those available on the Macintosh
|
namespace Serial {
|
||||||
/// and therefore often associated with RS-422.
|
|
||||||
enum class Signal {
|
|
||||||
Receive,
|
|
||||||
Transmit,
|
|
||||||
ClearToSend,
|
|
||||||
RequestToSend,
|
|
||||||
DataCarrierDetect,
|
|
||||||
OutputHandshake,
|
|
||||||
InputHandshake
|
|
||||||
};
|
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
@c Line connects a single reader and a single writer, allowing timestamped events to be
|
@c Line connects a single reader and a single writer, allowing timestamped events to be
|
||||||
@ -34,29 +24,43 @@ enum class Signal {
|
|||||||
*/
|
*/
|
||||||
class Line {
|
class Line {
|
||||||
public:
|
public:
|
||||||
void connect_reader(int clock_rate);
|
/// Advances the read position by @c cycles relative to the writer's
|
||||||
void disconnect_reader();
|
/// clock rate.
|
||||||
|
void advance_writer(int cycles);
|
||||||
|
|
||||||
void connect_writer(int clock_rate);
|
/// Sets the line to @c level.
|
||||||
void disconnect_writer();
|
void write(bool level);
|
||||||
|
|
||||||
/// Sets the line to @c level after @c cycles relative to the writer's
|
/// Enqueues @c count level changes, the first occurring immediately
|
||||||
/// clock rate have elapsed from the final event currently posted.
|
|
||||||
void write(int cycles, bool level);
|
|
||||||
|
|
||||||
/// Enqueues @c count level changes, the first occurring @c cycles
|
|
||||||
/// after the final event currently posted and each subsequent event
|
/// after the final event currently posted and each subsequent event
|
||||||
/// occurring @c cycles after the previous. The levels to output are
|
/// occurring @c cycles after the previous. An additional gap of @c cycles
|
||||||
|
/// is scheduled after the final output. The levels to output are
|
||||||
/// taken from @c levels which is read from lsb to msb. @c cycles is
|
/// taken from @c levels which is read from lsb to msb. @c cycles is
|
||||||
/// relative to the writer's clock rate.
|
/// relative to the writer's clock rate.
|
||||||
void write(int cycles, int count, int levels);
|
void write(int cycles, int count, int levels);
|
||||||
|
|
||||||
/// Advances the read position by @c cycles relative to the reader's
|
/// @returns the number of cycles until currently enqueued write data is exhausted.
|
||||||
/// clock rate.
|
int write_data_time_remaining();
|
||||||
void advance_reader(int cycles);
|
|
||||||
|
|
||||||
/// @returns The instantaneous level of this line at the current read position.
|
/// Eliminates all future write states, leaving the output at whatever it is now.
|
||||||
|
void reset_writing();
|
||||||
|
|
||||||
|
/// Applies all pending write changes instantly.
|
||||||
|
void flush_writing();
|
||||||
|
|
||||||
|
/// @returns The instantaneous level of this line.
|
||||||
bool read();
|
bool read();
|
||||||
|
|
||||||
|
private:
|
||||||
|
struct Event {
|
||||||
|
enum Type {
|
||||||
|
Delay, SetHigh, SetLow
|
||||||
|
} type;
|
||||||
|
int delay;
|
||||||
|
};
|
||||||
|
std::vector<Event> events_;
|
||||||
|
int remaining_delays_ = 0;
|
||||||
|
bool level_ = false;
|
||||||
};
|
};
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
|
@ -39,6 +39,8 @@ class ConcreteMachine:
|
|||||||
public:
|
public:
|
||||||
ConcreteMachine(const Target &target, const ROMMachine::ROMFetcher &rom_fetcher) :
|
ConcreteMachine(const Target &target, const ROMMachine::ROMFetcher &rom_fetcher) :
|
||||||
mc68000_(*this),
|
mc68000_(*this),
|
||||||
|
keyboard_acia_(),
|
||||||
|
midi_acia_(),
|
||||||
ay_(audio_queue_),
|
ay_(audio_queue_),
|
||||||
speaker_(ay_) {
|
speaker_(ay_) {
|
||||||
set_clock_rate(CLOCK_RATE);
|
set_clock_rate(CLOCK_RATE);
|
||||||
|
Loading…
x
Reference in New Issue
Block a user