mirror of
https://github.com/TomHarte/CLK.git
synced 2025-11-23 21:17:42 +00:00
86 lines
2.0 KiB
C++
86 lines
2.0 KiB
C++
//
|
|
// uPD7002.cpp
|
|
// Clock Signal
|
|
//
|
|
// Created by Thomas Harte on 17/09/2025.
|
|
// Copyright © 2025 Thomas Harte. All rights reserved.
|
|
//
|
|
|
|
#include "uPD7002.hpp"
|
|
|
|
using namespace NEC;
|
|
|
|
uPD7002::uPD7002(const HalfCycles clock_rate) {
|
|
// Per the BBC AUG: "8 bit conversions typically take 4 ms to complete whereas 10 bit
|
|
// conversions typically take 10 ms to complete".
|
|
fast_period_ = clock_rate / 250;
|
|
slow_period_ = clock_rate / 100;
|
|
}
|
|
|
|
void uPD7002::run_for(const HalfCycles count) {
|
|
if(!conversion_time_remaining_) {
|
|
return;
|
|
}
|
|
|
|
if(count >= conversion_time_remaining_) {
|
|
conversion_time_remaining_ = HalfCycles(0);
|
|
result_ = uint16_t(inputs_[channel_] * 65535.0f) & (high_precision_ ? 0xfff0 : 0xff00);
|
|
set_interrupt(true);
|
|
return;
|
|
}
|
|
|
|
conversion_time_remaining_ -= count;
|
|
}
|
|
|
|
bool uPD7002::interrupt() const {
|
|
return interrupt_;
|
|
}
|
|
|
|
void uPD7002::write(const uint16_t address, const uint8_t value) {
|
|
const auto local_address = address & 3;
|
|
if(!local_address) {
|
|
channel_ = value & 0b0000'0011;
|
|
spare_ = value & 0b0000'0100;
|
|
high_precision_ = value & 0b0000'1000;
|
|
conversion_time_remaining_ = high_precision_ ? slow_period_ : fast_period_;
|
|
set_interrupt(false);
|
|
return;
|
|
}
|
|
}
|
|
|
|
uint8_t uPD7002::read(const uint16_t address) {
|
|
switch(address & 3) {
|
|
default:
|
|
case 0: return status();
|
|
case 1:
|
|
set_interrupt(false);
|
|
return uint8_t(result_ >> 8);
|
|
case 2: return uint8_t(result_);
|
|
case 3: return 0xff;
|
|
}
|
|
}
|
|
|
|
uint8_t uPD7002::status() const {
|
|
return
|
|
channel_ |
|
|
spare_ |
|
|
(high_precision_ ? 0x08 : 0) |
|
|
((result_ >> 14) & 0x30) |
|
|
(conversion_time_remaining_ > HalfCycles(0) ? 0x00 : 0x40) |
|
|
(interrupt_ ? 0x00 : 0x80);
|
|
}
|
|
|
|
void uPD7002::set_delegate(Delegate *const delegate) {
|
|
delegate_ = delegate;
|
|
}
|
|
|
|
void uPD7002::set_interrupt(const bool value) {
|
|
if(interrupt_ == value) return;
|
|
interrupt_ = value;
|
|
if(delegate_) delegate_->did_change_interrupt_status(*this);
|
|
}
|
|
|
|
void uPD7002::set_input(const int channel, const float value) {
|
|
inputs_[channel] = value;
|
|
}
|