mirror of
https://github.com/TomHarte/CLK.git
synced 2025-09-25 08:25:09 +00:00
Introduce SizedCounter
as start of CRTC reworking.
This commit is contained in:
@@ -9,23 +9,33 @@
|
||||
#pragma once
|
||||
|
||||
#include "ClockReceiver/ClockReceiver.hpp"
|
||||
#include "Numeric/SizedCounter.hpp"
|
||||
|
||||
#include <cstdint>
|
||||
#include <cstdio>
|
||||
|
||||
//
|
||||
// WARNING: code is in flux. I'm attempting to use hoglet's FPGA implementation at
|
||||
// https://github.com/hoglet67/BeebFpga/blob/master/src/common/mc6845.vhd as an authoritative guide to proper behaviour,
|
||||
// having found his Electron ULA to be excellent. This is starting by mapping various bits of internal state here
|
||||
// to hoglet's equivalents; cf. comments.
|
||||
//
|
||||
|
||||
namespace Motorola::CRTC {
|
||||
|
||||
struct BusState {
|
||||
using RACounter = Numeric::SizedCounter<14>;
|
||||
|
||||
bool display_enable = false;
|
||||
bool hsync = false;
|
||||
bool vsync = false;
|
||||
bool cursor = false;
|
||||
uint16_t refresh_address = 0;
|
||||
RACounter refresh_address = 0; // ma_i
|
||||
uint16_t row_address = 0;
|
||||
|
||||
// Not strictly part of the bus state; provided because the partition between 6845 and bus handler
|
||||
// doesn't quite hold up in some emulated systems where the two are integrated and share more state.
|
||||
int field_count = 0;
|
||||
int field_count = 0; // field_counter
|
||||
};
|
||||
|
||||
class BusHandler {
|
||||
@@ -39,14 +49,8 @@ enum class Personality {
|
||||
UM6845R, // Type 1 in CPC parlance. Status register, fixed-length VSYNC.
|
||||
MC6845, // Type 2. No status register, fixed-length VSYNC, no zero-length HSYNC.
|
||||
AMS40226, // Type 3. Status is get register, fixed-length VSYNC, no zero-length HSYNC.
|
||||
|
||||
EGA, // Extended EGA-style CRTC; uses 16-bit addressing throughout.
|
||||
};
|
||||
|
||||
constexpr bool is_egavga(const Personality p) {
|
||||
return p >= Personality::EGA;
|
||||
}
|
||||
|
||||
// https://www.pcjs.org/blog/2018/03/20/ advises that "the behavior of bits 5 and 6 [of register 10, the cursor start
|
||||
// register is really card specific".
|
||||
//
|
||||
@@ -56,8 +60,6 @@ enum class CursorType {
|
||||
None,
|
||||
/// MDA style: 00 => symmetric blinking; 01 or 10 => no blinking; 11 => short on, long off.
|
||||
MDA,
|
||||
/// EGA style: ignore the bits completely.
|
||||
EGA,
|
||||
};
|
||||
|
||||
// TODO UM6845R and R12/R13; see http://www.cpcwiki.eu/index.php/CRTC#CRTC_Differences
|
||||
@@ -90,16 +92,6 @@ public:
|
||||
}
|
||||
|
||||
void set_register(const uint8_t value) {
|
||||
static constexpr bool is_ega = is_egavga(personality);
|
||||
|
||||
const auto load_low = [value](uint16_t &target) {
|
||||
target = (target & 0xff00) | value;
|
||||
};
|
||||
const auto load_high = [value](uint16_t &target) {
|
||||
static constexpr uint8_t mask = RefreshMask >> 8;
|
||||
target = uint16_t((target & 0x00ff) | ((value & mask) << 8));
|
||||
};
|
||||
|
||||
switch(selected_register_) {
|
||||
case 0: layout_.horizontal.total = value; break;
|
||||
case 1: layout_.horizontal.displayed = value; break;
|
||||
@@ -140,10 +132,10 @@ public:
|
||||
case 11:
|
||||
layout_.vertical.end_cursor = value & 0x1f;
|
||||
break;
|
||||
case 12: load_high(layout_.start_address); break;
|
||||
case 13: load_low(layout_.start_address); break;
|
||||
case 14: load_high(layout_.cursor_address); break;
|
||||
case 15: load_low(layout_.cursor_address); break;
|
||||
case 12: layout_.start_address.template load<8>(value); break;
|
||||
case 13: layout_.start_address.template load<0>(value); break;
|
||||
case 14: layout_.cursor_address.template load<8>(value); break;
|
||||
case 15: layout_.cursor_address.template load<0>(value); break;
|
||||
}
|
||||
|
||||
static constexpr uint8_t masks[] = {
|
||||
@@ -154,11 +146,11 @@ public:
|
||||
// EGA: b0–b4: end of horizontal blank;
|
||||
// b5–b6: "Number of character clocks to delay start of display after Horizontal Total has been reached."
|
||||
|
||||
is_ega ? 0xff : 0x7f, // Start horizontal retrace.
|
||||
0x7f, // Start horizontal retrace.
|
||||
0x1f, 0x7f, 0x7f,
|
||||
0xff, 0x1f, 0x7f, 0x1f,
|
||||
uint8_t(RefreshMask >> 8), uint8_t(RefreshMask),
|
||||
uint8_t(RefreshMask >> 8), uint8_t(RefreshMask),
|
||||
uint8_t(BusState::RACounter::Mask >> 8), uint8_t(BusState::RACounter::Mask),
|
||||
uint8_t(BusState::RACounter::Mask >> 8), uint8_t(BusState::RACounter::Mask),
|
||||
};
|
||||
|
||||
if(selected_register_ < 16) {
|
||||
@@ -350,7 +342,7 @@ public:
|
||||
} else if(character_total_hit) {
|
||||
bus_state_.refresh_address = line_address_;
|
||||
} else {
|
||||
bus_state_.refresh_address = (bus_state_.refresh_address + 1) & RefreshMask;
|
||||
++bus_state_.refresh_address;
|
||||
}
|
||||
|
||||
if(new_frame) {
|
||||
@@ -366,8 +358,6 @@ public:
|
||||
}
|
||||
|
||||
private:
|
||||
static constexpr uint16_t RefreshMask = (personality >= Personality::EGA) ? 0xffff : 0x3fff;
|
||||
|
||||
BusHandlerT &bus_handler_;
|
||||
BusState bus_state_;
|
||||
|
||||
@@ -379,56 +369,59 @@ private:
|
||||
enum class BlinkMode {
|
||||
// TODO.
|
||||
};
|
||||
|
||||
// Comments on the right provide the corresponding signal name in hoglet's VHDL implementation.
|
||||
struct {
|
||||
struct {
|
||||
uint8_t total;
|
||||
uint8_t displayed;
|
||||
uint8_t start_sync;
|
||||
uint8_t sync_width;
|
||||
uint8_t total; // r00_h_total
|
||||
uint8_t displayed; // r01_h_displayed
|
||||
uint8_t start_sync; // r02_h_sync_pos
|
||||
uint8_t sync_width; // r03_h_sync_width
|
||||
} horizontal;
|
||||
|
||||
struct {
|
||||
uint8_t total;
|
||||
uint8_t displayed;
|
||||
uint8_t start_sync;
|
||||
uint8_t sync_lines;
|
||||
uint8_t adjust;
|
||||
uint8_t total; // r04_v_total
|
||||
uint8_t displayed; // r06_v_displayed
|
||||
uint8_t start_sync; // r07_v_sync_pos
|
||||
uint8_t sync_lines; // r03_v_sync_width
|
||||
uint8_t adjust; // r05_v_total_adj
|
||||
|
||||
uint8_t end_row;
|
||||
uint8_t start_cursor;
|
||||
uint8_t end_cursor;
|
||||
uint8_t end_row; // r09_max_scanline_addr
|
||||
uint8_t start_cursor; // r10_cursor_start
|
||||
uint8_t end_cursor; // r11_cursor_end
|
||||
} vertical;
|
||||
|
||||
InterlaceMode interlace_mode_ = InterlaceMode::Off;
|
||||
InterlaceMode interlace_mode_ = InterlaceMode::Off; // r08_interlace
|
||||
uint8_t end_row() const {
|
||||
return interlace_mode_ == InterlaceMode::InterlaceSyncAndVideo ? vertical.end_row & ~1 : vertical.end_row;
|
||||
}
|
||||
|
||||
uint16_t start_address;
|
||||
uint16_t cursor_address;
|
||||
uint16_t light_pen_address;
|
||||
uint8_t cursor_flags;
|
||||
BusState::RACounter start_address; // r12_start_addr_h + r13_start_addr_l
|
||||
BusState::RACounter cursor_address; // r14_cursor_h + r15_cursor_l
|
||||
BusState::RACounter light_pen_address; // r16_light_pen_h + r17_light_pen_l
|
||||
uint8_t cursor_flags; // r10_cursor_mode
|
||||
} layout_;
|
||||
|
||||
uint8_t registers_[18]{};
|
||||
uint8_t dummy_register_ = 0;
|
||||
int selected_register_ = 0;
|
||||
|
||||
uint8_t character_counter_ = 0;
|
||||
uint8_t character_counter_ = 0; // h_counter
|
||||
uint32_t character_reset_history_ = 0;
|
||||
uint8_t row_counter_ = 0, next_row_counter_ = 0;
|
||||
uint8_t row_counter_ = 0; // row_counter
|
||||
uint8_t next_row_counter_ = 0; // row_counter_next
|
||||
uint8_t adjustment_counter_ = 0;
|
||||
|
||||
bool character_is_visible_ = false;
|
||||
bool line_is_visible_ = false;
|
||||
bool character_is_visible_ = false; // h_display
|
||||
bool line_is_visible_ = false; // v_display
|
||||
bool is_first_scanline_ = false;
|
||||
bool is_cursor_line_ = false;
|
||||
|
||||
int hsync_counter_ = 0;
|
||||
int vsync_counter_ = 0;
|
||||
int hsync_counter_ = 0; // h_sync_counter
|
||||
int vsync_counter_ = 0; // v_sync_counter
|
||||
bool is_in_adjustment_period_ = false;
|
||||
|
||||
uint16_t line_address_ = 0;
|
||||
BusState::RACounter line_address_;
|
||||
uint8_t status_ = 0;
|
||||
|
||||
int display_skew_mask_ = 1;
|
||||
@@ -437,7 +430,17 @@ private:
|
||||
bool eof_latched_ = false;
|
||||
bool eom_latched_ = false;
|
||||
uint16_t next_row_address_ = 0;
|
||||
bool odd_field_ = false;
|
||||
bool odd_field_ = false; // odd_field
|
||||
|
||||
// line_counter
|
||||
// line_counter_next
|
||||
// hs
|
||||
// vs
|
||||
// vs_hit
|
||||
// vs_hit_last
|
||||
// vs_even
|
||||
// vs_odd
|
||||
|
||||
};
|
||||
|
||||
}
|
||||
|
65
Numeric/SizedCounter.hpp
Normal file
65
Numeric/SizedCounter.hpp
Normal file
@@ -0,0 +1,65 @@
|
||||
//
|
||||
// SizedCounter.hpp
|
||||
// Clock Signal
|
||||
//
|
||||
// Created by Thomas Harte on 22/09/2025.
|
||||
// Copyright © 2025 Thomas Harte. All rights reserved.
|
||||
//
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "Sizes.hpp"
|
||||
|
||||
namespace Numeric {
|
||||
|
||||
/*!
|
||||
Provides a counter that is strictly limited to the requested of bits but attempts otherwise
|
||||
to act like a standard C++ numeric type.
|
||||
*/
|
||||
template <int bits>
|
||||
struct SizedCounter {
|
||||
using IntT = MinIntForValue<1 << bits>::type;
|
||||
inline static constexpr IntT Mask = (1 << bits) - 1;
|
||||
|
||||
constexpr SizedCounter(const IntT start_value) noexcept : counter_(start_value & Mask) {}
|
||||
SizedCounter() = default;
|
||||
|
||||
operator IntT() const {
|
||||
return counter_;
|
||||
}
|
||||
|
||||
SizedCounter &operator++(int) {
|
||||
(*this) ++;
|
||||
return *this;
|
||||
}
|
||||
|
||||
SizedCounter &operator++() {
|
||||
counter_ = (counter_ + 1) & Mask;
|
||||
return *this;
|
||||
}
|
||||
|
||||
SizedCounter &operator+=(const IntT rhs) {
|
||||
counter_ = (counter_ + rhs) & Mask;
|
||||
return *this;
|
||||
}
|
||||
|
||||
auto operator <=>(const SizedCounter &) const = default;
|
||||
|
||||
/// Replaces the bits in the range [begin, end) with those in the low-order bits of @c vlaue.
|
||||
template <int begin, int end>
|
||||
void load(const MinIntForValue<1 << (end - begin)>::type value) {
|
||||
const auto mask = (1 << end) - (1 << begin);
|
||||
counter_ &= ~mask;
|
||||
counter_ |= IntT((value << begin) & mask);
|
||||
}
|
||||
|
||||
template <int begin, typename IntT>
|
||||
void load(const IntT value) {
|
||||
load<begin, begin + sizeof(IntT)*8>(value);
|
||||
}
|
||||
|
||||
private:
|
||||
IntT counter_{};
|
||||
};
|
||||
|
||||
}
|
@@ -1794,6 +1794,7 @@
|
||||
4B8805FA1DCFF807003085B1 /* Oric.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; name = Oric.hpp; path = Parsers/Oric.hpp; sourceTree = "<group>"; };
|
||||
4B882F582C2F9C6900D84031 /* CPCShakerTests.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = CPCShakerTests.mm; sourceTree = "<group>"; };
|
||||
4B882F5A2C2F9C7700D84031 /* Shaker */ = {isa = PBXFileReference; lastKnownFileType = folder; path = Shaker; sourceTree = "<group>"; };
|
||||
4B88559C2E8185BF00E251DD /* SizedCounter.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = SizedCounter.hpp; sourceTree = "<group>"; };
|
||||
4B89449220194A47007DE474 /* CSStaticAnalyser+TargetVector.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = "CSStaticAnalyser+TargetVector.h"; path = "StaticAnalyser/CSStaticAnalyser+TargetVector.h"; sourceTree = "<group>"; };
|
||||
4B8944E4201967B4007DE474 /* ConfidenceSummary.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = ConfidenceSummary.hpp; sourceTree = "<group>"; };
|
||||
4B8944E5201967B4007DE474 /* ConfidenceSource.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = ConfidenceSource.hpp; sourceTree = "<group>"; };
|
||||
@@ -3741,15 +3742,16 @@
|
||||
children = (
|
||||
4B43984129674943006B0BFC /* BitReverse.hpp */,
|
||||
4BD155312716362A00410C6E /* BitSpread.hpp */,
|
||||
4B0B239F2D658C9400153879 /* BitStream.hpp */,
|
||||
4281572E2AA0334300E16AA1 /* Carry.hpp */,
|
||||
4B7BA03E23D55E7900B98D9E /* CRC.hpp */,
|
||||
4B7BA03F23D55E7900B98D9E /* LFSR.hpp */,
|
||||
4B66E1A8297719270057ED0F /* NumericCoder.hpp */,
|
||||
4BB5B995281B1D3E00522DA9 /* RegisterSizes.hpp */,
|
||||
4B88559C2E8185BF00E251DD /* SizedCounter.hpp */,
|
||||
4BFEA2F12682A90200EBF94C /* Sizes.hpp */,
|
||||
4BD9713A2BFD7E7100C907AA /* StringSimilarity.hpp */,
|
||||
4B0329202D0A78DC00C51EB5 /* UpperBound.hpp */,
|
||||
4B0B239F2D658C9400153879 /* BitStream.hpp */,
|
||||
);
|
||||
name = Numeric;
|
||||
path = ../../Numeric;
|
||||
|
Reference in New Issue
Block a user