mirror of
https://github.com/TomHarte/CLK.git
synced 2025-09-23 11:24:29 +00:00
Compare commits
48 Commits
AddressWra
...
New6845
Author | SHA1 | Date | |
---|---|---|---|
|
ca226e4295 | ||
|
9261939f62 | ||
|
0349931953 | ||
|
d612a385d2 | ||
|
ed4f299d55 | ||
|
7cef789d41 | ||
|
66bfb86d42 | ||
|
c4a5bc12ef | ||
|
557631f6ba | ||
|
362ffaff7f | ||
|
fb5ef200fb | ||
|
5e78ac3af5 | ||
|
719a090b34 | ||
|
3af85da6e0 | ||
|
8fd62aa525 | ||
|
40747f51bd | ||
|
f3cef6bd73 | ||
|
eef0ee8180 | ||
|
503e974375 | ||
|
c959f2fee5 | ||
|
7d5e434cba | ||
|
2720bcdf18 | ||
|
c513b7262b | ||
|
57a795df96 | ||
|
6bdd9e4543 | ||
|
ede3def37f | ||
|
87d9022280 | ||
|
ff0ba7d48b | ||
|
b49c47425f | ||
|
3916ba1a42 | ||
|
0b3d22b97c | ||
|
9b8b0f2023 | ||
|
06e0d17be0 | ||
|
239c485f3c | ||
|
5e5fdda0ca | ||
|
4b2dddf3c6 | ||
|
c99ec745ca | ||
|
1ec2e455ec | ||
|
69304737c6 | ||
|
fe91670127 | ||
|
7a59f94f3d | ||
|
4efe3a333d | ||
|
421bf28582 | ||
|
4c49ffe3d1 | ||
|
26b1ef247b | ||
|
3aafba707a | ||
|
ae774e88fa | ||
|
ff56dd53cf |
@@ -38,12 +38,19 @@ private:
|
|||||||
|
|
||||||
struct BBCMicroTarget: public ::Analyser::Static::Target, public Reflection::StructImpl<BBCMicroTarget> {
|
struct BBCMicroTarget: public ::Analyser::Static::Target, public Reflection::StructImpl<BBCMicroTarget> {
|
||||||
std::string loading_command;
|
std::string loading_command;
|
||||||
|
bool has_1770dfs = false;
|
||||||
|
bool has_adfs = false;
|
||||||
|
bool has_sideways_ram = false;
|
||||||
|
|
||||||
BBCMicroTarget() : Analyser::Static::Target(Machine::BBCMicro) {}
|
BBCMicroTarget() : Analyser::Static::Target(Machine::BBCMicro) {}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
friend Reflection::StructImpl<BBCMicroTarget>;
|
friend Reflection::StructImpl<BBCMicroTarget>;
|
||||||
void declare_fields() {}
|
void declare_fields() {
|
||||||
|
DeclareField(has_1770dfs);
|
||||||
|
DeclareField(has_adfs);
|
||||||
|
DeclareField(has_sideways_ram);
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
struct ArchimedesTarget: public ::Analyser::Static::Target, public Reflection::StructImpl<ArchimedesTarget> {
|
struct ArchimedesTarget: public ::Analyser::Static::Target, public Reflection::StructImpl<ArchimedesTarget> {
|
||||||
|
@@ -114,6 +114,7 @@ uint8_t WD1770::read(const int address) {
|
|||||||
update_status([] (Status &status) {
|
update_status([] (Status &status) {
|
||||||
status.data_request = false;
|
status.data_request = false;
|
||||||
});
|
});
|
||||||
|
// Logger::info().append("Returned data %02x; [drq:%d]", data_, status_.data_request);
|
||||||
return data_;
|
return data_;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -486,8 +487,12 @@ void WD1770::posit_event(const int new_event_type) {
|
|||||||
WAIT_FOR_EVENT(Event::Token);
|
WAIT_FOR_EVENT(Event::Token);
|
||||||
if(get_latest_token().type != Token::Byte) goto type2_read_byte;
|
if(get_latest_token().type != Token::Byte) goto type2_read_byte;
|
||||||
data_ = get_latest_token().byte_value;
|
data_ = get_latest_token().byte_value;
|
||||||
|
// Logger::info().append("Posting %02x", data_);
|
||||||
update_status([] (Status &status) {
|
update_status([] (Status &status) {
|
||||||
status.lost_data |= status.data_request;
|
status.lost_data |= status.data_request;
|
||||||
|
// if(status.lost_data) {
|
||||||
|
// Logger::info().append("Lost data");
|
||||||
|
// }
|
||||||
status.data_request = true;
|
status.data_request = true;
|
||||||
});
|
});
|
||||||
distance_into_section_++;
|
distance_into_section_++;
|
||||||
|
@@ -9,23 +9,38 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include "ClockReceiver/ClockReceiver.hpp"
|
#include "ClockReceiver/ClockReceiver.hpp"
|
||||||
|
#include "Numeric/SizedCounter.hpp"
|
||||||
|
|
||||||
#include <cstdint>
|
#include <cstdint>
|
||||||
#include <cstdio>
|
#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 {
|
namespace Motorola::CRTC {
|
||||||
|
|
||||||
|
using RefreshAddress = Numeric::SizedCounter<14>;
|
||||||
|
using LineAddress = Numeric::SizedCounter<5>;
|
||||||
|
|
||||||
|
using SyncCounter = Numeric::SizedCounter<4>;
|
||||||
|
using CharacterAddress = Numeric::SizedCounter<8>;
|
||||||
|
using RowAddress = Numeric::SizedCounter<7>;
|
||||||
|
|
||||||
struct BusState {
|
struct BusState {
|
||||||
bool display_enable = false;
|
bool display_enable = false;
|
||||||
bool hsync = false;
|
bool hsync = false;
|
||||||
bool vsync = false;
|
bool vsync = false;
|
||||||
bool cursor = false;
|
bool cursor = false;
|
||||||
uint16_t refresh_address = 0;
|
RefreshAddress refresh; // ma_i
|
||||||
uint16_t row_address = 0;
|
LineAddress row;
|
||||||
|
|
||||||
// Not strictly part of the bus state; provided because the partition between 6845 and bus handler
|
// 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.
|
// 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 {
|
class BusHandler {
|
||||||
@@ -39,14 +54,8 @@ enum class Personality {
|
|||||||
UM6845R, // Type 1 in CPC parlance. Status register, fixed-length VSYNC.
|
UM6845R, // Type 1 in CPC parlance. Status register, fixed-length VSYNC.
|
||||||
MC6845, // Type 2. No status register, fixed-length VSYNC, no zero-length HSYNC.
|
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.
|
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
|
// 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".
|
// register is really card specific".
|
||||||
//
|
//
|
||||||
@@ -56,8 +65,6 @@ enum class CursorType {
|
|||||||
None,
|
None,
|
||||||
/// MDA style: 00 => symmetric blinking; 01 or 10 => no blinking; 11 => short on, long off.
|
/// MDA style: 00 => symmetric blinking; 01 or 10 => no blinking; 11 => short on, long off.
|
||||||
MDA,
|
MDA,
|
||||||
/// EGA style: ignore the bits completely.
|
|
||||||
EGA,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
// TODO UM6845R and R12/R13; see http://www.cpcwiki.eu/index.php/CRTC#CRTC_Differences
|
// TODO UM6845R and R12/R13; see http://www.cpcwiki.eu/index.php/CRTC#CRTC_Differences
|
||||||
@@ -90,16 +97,6 @@ public:
|
|||||||
}
|
}
|
||||||
|
|
||||||
void set_register(const uint8_t value) {
|
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_) {
|
switch(selected_register_) {
|
||||||
case 0: layout_.horizontal.total = value; break;
|
case 0: layout_.horizontal.total = value; break;
|
||||||
case 1: layout_.horizontal.displayed = value; break;
|
case 1: layout_.horizontal.displayed = value; break;
|
||||||
@@ -140,10 +137,10 @@ public:
|
|||||||
case 11:
|
case 11:
|
||||||
layout_.vertical.end_cursor = value & 0x1f;
|
layout_.vertical.end_cursor = value & 0x1f;
|
||||||
break;
|
break;
|
||||||
case 12: load_high(layout_.start_address); break;
|
case 12: layout_.start_address.template load<8>(value); break;
|
||||||
case 13: load_low(layout_.start_address); break;
|
case 13: layout_.start_address.template load<0>(value); break;
|
||||||
case 14: load_high(layout_.cursor_address); break;
|
case 14: layout_.cursor_address.template load<8>(value); break;
|
||||||
case 15: load_low(layout_.cursor_address); break;
|
case 15: layout_.cursor_address.template load<0>(value); break;
|
||||||
}
|
}
|
||||||
|
|
||||||
static constexpr uint8_t masks[] = {
|
static constexpr uint8_t masks[] = {
|
||||||
@@ -154,11 +151,11 @@ public:
|
|||||||
// EGA: b0–b4: end of horizontal blank;
|
// EGA: b0–b4: end of horizontal blank;
|
||||||
// b5–b6: "Number of character clocks to delay start of display after Horizontal Total has been reached."
|
// 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,
|
0x1f, 0x7f, 0x7f,
|
||||||
0xff, 0x1f, 0x7f, 0x1f,
|
0xff, 0x1f, 0x7f, 0x1f,
|
||||||
uint8_t(RefreshMask >> 8), uint8_t(RefreshMask),
|
uint8_t(RefreshAddress::Mask >> 8), uint8_t(RefreshAddress::Mask),
|
||||||
uint8_t(RefreshMask >> 8), uint8_t(RefreshMask),
|
uint8_t(RefreshAddress::Mask >> 8), uint8_t(RefreshAddress::Mask),
|
||||||
};
|
};
|
||||||
|
|
||||||
if(selected_register_ < 16) {
|
if(selected_register_ < 16) {
|
||||||
@@ -170,8 +167,8 @@ public:
|
|||||||
}
|
}
|
||||||
|
|
||||||
void trigger_light_pen() {
|
void trigger_light_pen() {
|
||||||
registers_[17] = bus_state_.refresh_address & 0xff;
|
registers_[17] = bus_state_.refresh.get() & 0xff;
|
||||||
registers_[16] = bus_state_.refresh_address >> 8;
|
registers_[16] = bus_state_.refresh.get() >> 8;
|
||||||
status_ |= 0x40;
|
status_ |= 0x40;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -184,7 +181,7 @@ public:
|
|||||||
|
|
||||||
// Do bus work.
|
// Do bus work.
|
||||||
bus_state_.cursor = is_cursor_line_ &&
|
bus_state_.cursor = is_cursor_line_ &&
|
||||||
bus_state_.refresh_address == layout_.cursor_address;
|
bus_state_.refresh == layout_.cursor_address;
|
||||||
bus_state_.display_enable = character_is_visible_ && line_is_visible_;
|
bus_state_.display_enable = character_is_visible_ && line_is_visible_;
|
||||||
bus_handler_.perform_bus_cycle(bus_state_);
|
bus_handler_.perform_bus_cycle(bus_state_);
|
||||||
|
|
||||||
@@ -192,10 +189,8 @@ public:
|
|||||||
// Shared, stateless signals.
|
// Shared, stateless signals.
|
||||||
//
|
//
|
||||||
const bool character_total_hit = character_counter_ == layout_.horizontal.total;
|
const bool character_total_hit = character_counter_ == layout_.horizontal.total;
|
||||||
const uint8_t lines_per_row =
|
const auto lines_per_row = layout_.end_row();
|
||||||
layout_.interlace_mode_ == InterlaceMode::InterlaceSyncAndVideo ?
|
const bool row_end_hit = bus_state_.row == lines_per_row && !is_in_adjustment_period_;
|
||||||
layout_.vertical.end_row & ~1 : layout_.vertical.end_row;
|
|
||||||
const bool row_end_hit = bus_state_.row_address == lines_per_row && !is_in_adjustment_period_;
|
|
||||||
const bool was_eof = eof_latched_;
|
const bool was_eof = eof_latched_;
|
||||||
const bool new_frame =
|
const bool new_frame =
|
||||||
character_total_hit && was_eof &&
|
character_total_hit && was_eof &&
|
||||||
@@ -269,7 +264,7 @@ public:
|
|||||||
(!odd_field_ && !character_counter_) ||
|
(!odd_field_ && !character_counter_) ||
|
||||||
(odd_field_ && character_counter_ == (layout_.horizontal.total >> 1));
|
(odd_field_ && character_counter_ == (layout_.horizontal.total >> 1));
|
||||||
if(vsync_horizontal) {
|
if(vsync_horizontal) {
|
||||||
if((row_counter_ == layout_.vertical.start_sync && !bus_state_.row_address) || bus_state_.vsync) {
|
if((row_counter_ == layout_.vertical.start_sync && !bus_state_.row) || bus_state_.vsync) {
|
||||||
bus_state_.vsync = true;
|
bus_state_.vsync = true;
|
||||||
vsync_counter_ = (vsync_counter_ + 1) & 0xf;
|
vsync_counter_ = (vsync_counter_ + 1) & 0xf;
|
||||||
} else {
|
} else {
|
||||||
@@ -285,14 +280,14 @@ public:
|
|||||||
// Row address.
|
// Row address.
|
||||||
if(character_total_hit) {
|
if(character_total_hit) {
|
||||||
if(was_eof) {
|
if(was_eof) {
|
||||||
bus_state_.row_address = 0;
|
bus_state_.row = 0;
|
||||||
eof_latched_ = eom_latched_ = false;
|
eof_latched_ = eom_latched_ = false;
|
||||||
} else if(row_end_hit) {
|
} else if(row_end_hit) {
|
||||||
bus_state_.row_address = 0;
|
bus_state_.row = 0;
|
||||||
} else if(layout_.interlace_mode_ == InterlaceMode::InterlaceSyncAndVideo) {
|
} else if(layout_.interlace_mode_ == InterlaceMode::InterlaceSyncAndVideo) {
|
||||||
bus_state_.row_address = (bus_state_.row_address + 2) & ~1 & 31;
|
bus_state_.row += 2;
|
||||||
} else {
|
} else {
|
||||||
bus_state_.row_address = (bus_state_.row_address + 1) & 31;
|
++bus_state_.row;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -316,12 +311,18 @@ public:
|
|||||||
++bus_state_.field_count;
|
++bus_state_.field_count;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// Cursor.
|
// Cursor.
|
||||||
if constexpr (cursor_type != CursorType::None) {
|
if constexpr (cursor_type != CursorType::None) {
|
||||||
// Check for cursor enable.
|
// Check for cursor enable.
|
||||||
is_cursor_line_ |= bus_state_.row_address == layout_.vertical.start_cursor;
|
is_cursor_line_ |= bus_state_.row == layout_.vertical.start_cursor;
|
||||||
is_cursor_line_ &= bus_state_.row_address != layout_.vertical.end_cursor;
|
is_cursor_line_ &= !(
|
||||||
|
(bus_state_.row == layout_.vertical.end_cursor) ||
|
||||||
|
(
|
||||||
|
character_total_hit &&
|
||||||
|
row_end_hit &&
|
||||||
|
layout_.vertical.end_cursor == (lines_per_row + LineAddress(1))
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
switch(cursor_type) {
|
switch(cursor_type) {
|
||||||
// MDA-style blinking.
|
// MDA-style blinking.
|
||||||
@@ -344,17 +345,17 @@ public:
|
|||||||
//
|
//
|
||||||
|
|
||||||
if(new_frame) {
|
if(new_frame) {
|
||||||
bus_state_.refresh_address = layout_.start_address;
|
bus_state_.refresh = layout_.start_address;
|
||||||
} else if(character_total_hit) {
|
} else if(character_total_hit) {
|
||||||
bus_state_.refresh_address = line_address_;
|
bus_state_.refresh = line_address_;
|
||||||
} else {
|
} else {
|
||||||
bus_state_.refresh_address = (bus_state_.refresh_address + 1) & RefreshMask;
|
++bus_state_.refresh;
|
||||||
}
|
}
|
||||||
|
|
||||||
if(new_frame) {
|
if(new_frame) {
|
||||||
line_address_ = layout_.start_address;
|
line_address_ = layout_.start_address;
|
||||||
} else if(character_counter_ == layout_.horizontal.displayed && row_end_hit) {
|
} else if(character_counter_ == layout_.horizontal.displayed && row_end_hit) {
|
||||||
line_address_ = bus_state_.refresh_address;
|
line_address_ = bus_state_.refresh;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -364,8 +365,6 @@ public:
|
|||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
static constexpr uint16_t RefreshMask = (personality >= Personality::EGA) ? 0xffff : 0x3fff;
|
|
||||||
|
|
||||||
BusHandlerT &bus_handler_;
|
BusHandlerT &bus_handler_;
|
||||||
BusState bus_state_;
|
BusState bus_state_;
|
||||||
|
|
||||||
@@ -377,56 +376,60 @@ private:
|
|||||||
enum class BlinkMode {
|
enum class BlinkMode {
|
||||||
// TODO.
|
// TODO.
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// Comments on the right provide the corresponding signal name in hoglet's VHDL implementation.
|
||||||
struct {
|
struct {
|
||||||
struct {
|
struct {
|
||||||
uint8_t total;
|
CharacterAddress total; // r00_h_total
|
||||||
uint8_t displayed;
|
CharacterAddress displayed; // r01_h_displayed
|
||||||
uint8_t start_sync;
|
CharacterAddress start_sync; // r02_h_sync_pos
|
||||||
uint8_t sync_width;
|
SyncCounter sync_width; // r03_h_sync_width
|
||||||
} horizontal;
|
} horizontal;
|
||||||
|
|
||||||
struct {
|
struct {
|
||||||
uint8_t total;
|
RowAddress total; // r04_v_total
|
||||||
uint8_t displayed;
|
RowAddress displayed; // r06_v_displayed
|
||||||
uint8_t start_sync;
|
RowAddress start_sync; // r07_v_sync_pos
|
||||||
uint8_t sync_lines;
|
SyncCounter sync_lines; // r03_v_sync_width
|
||||||
uint8_t adjust;
|
LineAddress adjust; // r05_v_total_adj
|
||||||
|
|
||||||
uint8_t end_row;
|
LineAddress end_row; // r09_max_scanline_addr
|
||||||
uint8_t start_cursor;
|
LineAddress start_cursor; // r10_cursor_start
|
||||||
uint8_t end_cursor;
|
LineAddress end_cursor; // r11_cursor_end
|
||||||
} vertical;
|
} vertical;
|
||||||
|
|
||||||
InterlaceMode interlace_mode_ = InterlaceMode::Off;
|
InterlaceMode interlace_mode_ = InterlaceMode::Off; // r08_interlace
|
||||||
uint8_t end_row() const {
|
LineAddress end_row() const {
|
||||||
return interlace_mode_ == InterlaceMode::InterlaceSyncAndVideo ? vertical.end_row & ~1 : vertical.end_row;
|
return interlace_mode_ == InterlaceMode::InterlaceSyncAndVideo ?
|
||||||
|
vertical.end_row & ~1 : vertical.end_row;
|
||||||
}
|
}
|
||||||
|
|
||||||
uint16_t start_address;
|
RefreshAddress start_address; // r12_start_addr_h + r13_start_addr_l
|
||||||
uint16_t cursor_address;
|
RefreshAddress cursor_address; // r14_cursor_h + r15_cursor_l
|
||||||
uint16_t light_pen_address;
|
RefreshAddress light_pen_address; // r16_light_pen_h + r17_light_pen_l
|
||||||
uint8_t cursor_flags;
|
uint8_t cursor_flags; // r10_cursor_mode
|
||||||
} layout_;
|
} layout_;
|
||||||
|
|
||||||
uint8_t registers_[18]{};
|
uint8_t registers_[18]{};
|
||||||
uint8_t dummy_register_ = 0;
|
uint8_t dummy_register_ = 0;
|
||||||
int selected_register_ = 0;
|
int selected_register_ = 0;
|
||||||
|
|
||||||
uint8_t character_counter_ = 0;
|
CharacterAddress character_counter_ = 0; // h_counter
|
||||||
uint32_t character_reset_history_ = 0;
|
uint32_t character_reset_history_ = 0;
|
||||||
uint8_t row_counter_ = 0, next_row_counter_ = 0;
|
RowAddress row_counter_ = 0; // row_counter
|
||||||
|
RowAddress next_row_counter_ = 0; // row_counter_next
|
||||||
uint8_t adjustment_counter_ = 0;
|
uint8_t adjustment_counter_ = 0;
|
||||||
|
|
||||||
bool character_is_visible_ = false;
|
bool character_is_visible_ = false; // h_display
|
||||||
bool line_is_visible_ = false;
|
bool line_is_visible_ = false; // v_display
|
||||||
bool is_first_scanline_ = false;
|
bool is_first_scanline_ = false;
|
||||||
bool is_cursor_line_ = false;
|
bool is_cursor_line_ = false;
|
||||||
|
|
||||||
int hsync_counter_ = 0;
|
SyncCounter hsync_counter_; // h_sync_counter
|
||||||
int vsync_counter_ = 0;
|
SyncCounter vsync_counter_; // v_sync_counter
|
||||||
bool is_in_adjustment_period_ = false;
|
bool is_in_adjustment_period_ = false;
|
||||||
|
|
||||||
uint16_t line_address_ = 0;
|
RefreshAddress line_address_;
|
||||||
uint8_t status_ = 0;
|
uint8_t status_ = 0;
|
||||||
|
|
||||||
int display_skew_mask_ = 1;
|
int display_skew_mask_ = 1;
|
||||||
@@ -434,8 +437,17 @@ private:
|
|||||||
|
|
||||||
bool eof_latched_ = false;
|
bool eof_latched_ = false;
|
||||||
bool eom_latched_ = false;
|
bool eom_latched_ = false;
|
||||||
uint16_t next_row_address_ = 0;
|
bool odd_field_ = false; // odd_field
|
||||||
bool odd_field_ = false;
|
|
||||||
|
// line_counter
|
||||||
|
// line_counter_next
|
||||||
|
// hs
|
||||||
|
// vs
|
||||||
|
// vs_hit
|
||||||
|
// vs_hit_last
|
||||||
|
// vs_even
|
||||||
|
// vs_odd
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
81
Components/uPD7002/uPD7002.cpp
Normal file
81
Components/uPD7002/uPD7002.cpp
Normal file
@@ -0,0 +1,81 @@
|
|||||||
|
//
|
||||||
|
// 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);
|
||||||
|
}
|
51
Components/uPD7002/uPD7002.hpp
Normal file
51
Components/uPD7002/uPD7002.hpp
Normal file
@@ -0,0 +1,51 @@
|
|||||||
|
//
|
||||||
|
// uPD7002.hpp
|
||||||
|
// Clock Signal
|
||||||
|
//
|
||||||
|
// Created by Thomas Harte on 17/09/2025.
|
||||||
|
// Copyright © 2025 Thomas Harte. All rights reserved.
|
||||||
|
//
|
||||||
|
|
||||||
|
#include "ClockReceiver/ClockReceiver.hpp"
|
||||||
|
|
||||||
|
namespace NEC {
|
||||||
|
|
||||||
|
class uPD7002 {
|
||||||
|
public:
|
||||||
|
/// Constructs a PD7002 that will receive @c run_for updates at the specified clock rate.
|
||||||
|
uPD7002(HalfCycles clock_rate);
|
||||||
|
void run_for(HalfCycles);
|
||||||
|
|
||||||
|
/// @returns The current state of the interrupt line.
|
||||||
|
bool interrupt() const;
|
||||||
|
|
||||||
|
/// Defines a mean for an observer to receive notifications upon updates to the interrupt line.
|
||||||
|
struct Delegate {
|
||||||
|
virtual void did_change_interrupt_status(uPD7002 &) = 0;
|
||||||
|
};
|
||||||
|
void set_delegate(Delegate *);
|
||||||
|
|
||||||
|
void write(uint16_t address, uint8_t value);
|
||||||
|
uint8_t read(uint16_t address);
|
||||||
|
|
||||||
|
/// Sets the floating point value, which should be in the range [0.0, 1.0], for the signal currently
|
||||||
|
/// being supplied to @c channel.
|
||||||
|
void set_input(int channel, float value);
|
||||||
|
|
||||||
|
private:
|
||||||
|
float inputs_[4]{};
|
||||||
|
uint16_t result_ = 0;
|
||||||
|
bool interrupt_ = false;
|
||||||
|
|
||||||
|
uint8_t channel_ = 0, spare_ = 0;
|
||||||
|
bool high_precision_ = false;
|
||||||
|
|
||||||
|
HalfCycles conversion_time_remaining_{};
|
||||||
|
HalfCycles fast_period_, slow_period_;
|
||||||
|
|
||||||
|
uint8_t status() const;
|
||||||
|
void set_interrupt(bool);
|
||||||
|
Delegate *delegate_ = nullptr;
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
@@ -7,7 +7,8 @@
|
|||||||
//
|
//
|
||||||
|
|
||||||
#include "BBCMicro.hpp"
|
#include "BBCMicro.hpp"
|
||||||
#include "Keyboard.hpp"
|
|
||||||
|
#include "Activity/Source.hpp"
|
||||||
|
|
||||||
#include "Machines/MachineTypes.hpp"
|
#include "Machines/MachineTypes.hpp"
|
||||||
#include "Machines/Utility/MemoryFuzzer.hpp"
|
#include "Machines/Utility/MemoryFuzzer.hpp"
|
||||||
@@ -18,6 +19,10 @@
|
|||||||
#include "Components/6845/CRTC6845.hpp"
|
#include "Components/6845/CRTC6845.hpp"
|
||||||
#include "Components/SN76489/SN76489.hpp"
|
#include "Components/SN76489/SN76489.hpp"
|
||||||
#include "Components/6850/6850.hpp"
|
#include "Components/6850/6850.hpp"
|
||||||
|
#include "Components/uPD7002/uPD7002.hpp"
|
||||||
|
|
||||||
|
// TODO: factor this more appropriately.
|
||||||
|
#include "Machines/Acorn/Electron/Plus3.hpp"
|
||||||
|
|
||||||
#include "Analyser/Static/Acorn/Target.hpp"
|
#include "Analyser/Static/Acorn/Target.hpp"
|
||||||
#include "Outputs/Log.hpp"
|
#include "Outputs/Log.hpp"
|
||||||
@@ -26,6 +31,8 @@
|
|||||||
#include "Outputs/Speaker/Implementation/LowpassSpeaker.hpp"
|
#include "Outputs/Speaker/Implementation/LowpassSpeaker.hpp"
|
||||||
#include "Concurrency/AsyncTaskQueue.hpp"
|
#include "Concurrency/AsyncTaskQueue.hpp"
|
||||||
|
|
||||||
|
#include "Keyboard.hpp"
|
||||||
|
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
#include <array>
|
#include <array>
|
||||||
#include <bitset>
|
#include <bitset>
|
||||||
@@ -42,10 +49,10 @@ using Logger = Log::Logger<Log::Source::BBCMicro>;
|
|||||||
*/
|
*/
|
||||||
struct Audio {
|
struct Audio {
|
||||||
Audio() :
|
Audio() :
|
||||||
sn76489_(TI::SN76489::Personality::SN76489, audio_queue_),
|
sn76489_(TI::SN76489::Personality::SN76489, audio_queue_, 2),
|
||||||
speaker_(sn76489_)
|
speaker_(sn76489_)
|
||||||
{
|
{
|
||||||
// I'm *VERY* unsure about this.
|
// Combined with the additional divider specified above, implies this chip is clocked at 4Mhz.
|
||||||
speaker_.set_input_rate(2'000'000.0f);
|
speaker_.set_input_rate(2'000'000.0f);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -54,16 +61,16 @@ struct Audio {
|
|||||||
}
|
}
|
||||||
|
|
||||||
TI::SN76489 *operator ->() {
|
TI::SN76489 *operator ->() {
|
||||||
flush();
|
speaker_.run_for(audio_queue_, time_since_update_.flush<Cycles>());
|
||||||
return &sn76489_;
|
return &sn76489_;
|
||||||
}
|
}
|
||||||
|
|
||||||
void operator +=(const HalfCycles duration) {
|
void operator +=(const Cycles duration) {
|
||||||
speaker_.run_for(audio_queue_, time_since_update_.flush<Cycles>());
|
|
||||||
time_since_update_ += duration;
|
time_since_update_ += duration;
|
||||||
}
|
}
|
||||||
|
|
||||||
void flush() {
|
void flush() {
|
||||||
|
speaker_.run_for(audio_queue_, time_since_update_.flush<Cycles>());
|
||||||
audio_queue_.perform();
|
audio_queue_.perform();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -75,7 +82,7 @@ private:
|
|||||||
Concurrency::AsyncTaskQueue<false> audio_queue_;
|
Concurrency::AsyncTaskQueue<false> audio_queue_;
|
||||||
TI::SN76489 sn76489_;
|
TI::SN76489 sn76489_;
|
||||||
Outputs::Speaker::PullLowpass<TI::SN76489> speaker_;
|
Outputs::Speaker::PullLowpass<TI::SN76489> speaker_;
|
||||||
HalfCycles time_since_update_;
|
Cycles time_since_update_;
|
||||||
};
|
};
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
@@ -157,7 +164,18 @@ struct SystemVIAPortHandler: public MOS::MOS6522::IRQDelegatePortHandler {
|
|||||||
|
|
||||||
// Update keyboard LEDs.
|
// Update keyboard LEDs.
|
||||||
if(mask >= 0x40) {
|
if(mask >= 0x40) {
|
||||||
Logger::info().append("CAPS: %d SHIFT: %d", bool(latch_ & 0x40), bool(latch_ & 0x40));
|
const bool new_caps = latch_ & 0x80;
|
||||||
|
const bool new_shift = latch_ & 0x40;
|
||||||
|
|
||||||
|
if(new_caps != caps_led_state_) {
|
||||||
|
caps_led_state_ = new_caps;
|
||||||
|
activity_observer_->set_led_status(caps_led, caps_led_state_);
|
||||||
|
}
|
||||||
|
|
||||||
|
if(new_shift != shift_led_state_) {
|
||||||
|
shift_led_state_ = new_shift;
|
||||||
|
activity_observer_->set_led_status(shift_led, shift_led_state_);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -199,6 +217,18 @@ struct SystemVIAPortHandler: public MOS::MOS6522::IRQDelegatePortHandler {
|
|||||||
keyboard_scan_column_ = ending_column;
|
keyboard_scan_column_ = ending_column;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void set_activity_observer(Activity::Observer *const observer) {
|
||||||
|
activity_observer_ = observer;
|
||||||
|
|
||||||
|
if(activity_observer_) {
|
||||||
|
activity_observer_->register_led(caps_led, Activity::Observer::LEDPresentation::Persistent);
|
||||||
|
activity_observer_->register_led(shift_led, Activity::Observer::LEDPresentation::Persistent);
|
||||||
|
|
||||||
|
activity_observer_->set_led_status(caps_led, caps_led_state_);
|
||||||
|
activity_observer_->set_led_status(shift_led, shift_led_state_);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
uint8_t latch_ = 0;
|
uint8_t latch_ = 0;
|
||||||
enum LatchFlags: uint8_t {
|
enum LatchFlags: uint8_t {
|
||||||
@@ -241,6 +271,12 @@ private:
|
|||||||
|
|
||||||
via_.set_control_line_input<MOS::MOS6522::Port::A, MOS::MOS6522::Line::Two>(state);
|
via_.set_control_line_input<MOS::MOS6522::Port::A, MOS::MOS6522::Line::Two>(state);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static inline const std::string caps_led = "CAPS";
|
||||||
|
static inline const std::string shift_led = "SHIFT";
|
||||||
|
bool caps_led_state_ = false;
|
||||||
|
bool shift_led_state_ = false;
|
||||||
|
Activity::Observer *activity_observer_ = nullptr;
|
||||||
};
|
};
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
@@ -255,16 +291,27 @@ public:
|
|||||||
|
|
||||||
void set_palette(const uint8_t value) {
|
void set_palette(const uint8_t value) {
|
||||||
const auto index = value >> 4;
|
const auto index = value >> 4;
|
||||||
Logger::info().append("Palette entry %d set to %x", index, value & 0xf);
|
palette_[index] = uint8_t(
|
||||||
|
7 ^ (
|
||||||
|
((value & 0b100) >> 2) |
|
||||||
|
((value & 0b001) << 2) |
|
||||||
|
(value & 0b010)
|
||||||
|
)
|
||||||
|
);
|
||||||
|
flash_flags_[size_t(index)] = value & 0b1000;
|
||||||
}
|
}
|
||||||
|
|
||||||
void set_control(const uint8_t value) {
|
void set_control(const uint8_t value) {
|
||||||
Logger::info().append("Video control set to %x", value);
|
crtc_clock_multiplier_ = (value & 0x10) ? 1 : 2;
|
||||||
cycle_length_ = (value & 0x10) ? 8 : 16;
|
|
||||||
Logger::info().append("TODO: video control => flash %d", bool(value & 0x01));
|
active_collation_.pixels_per_clock = 1 << ((value >> 2) & 0x03);
|
||||||
Logger::info().append("TODO: video control => teletext %d", bool(value & 0x02));
|
active_collation_.is_teletext = value & 0x02;
|
||||||
Logger::info().append("TODO: video control => columns %d", (value >> 2) & 0x03);
|
if(active_collation_.is_teletext) {
|
||||||
Logger::info().append("TODO: video control => cursor segment %d%d%d", bool(value & 0x80), bool(value & 0x40), bool(value & 0x20));
|
Logger::error().append("TODO: video control => teletext %d", bool(value & 0x02));
|
||||||
|
}
|
||||||
|
|
||||||
|
flash_mask_ = value & 0x01 ? 7 : 0;
|
||||||
|
cursor_mask_ = value & 0b1110'0000;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
@@ -274,41 +321,40 @@ public:
|
|||||||
void perform_bus_cycle(const Motorola::CRTC::BusState &state) {
|
void perform_bus_cycle(const Motorola::CRTC::BusState &state) {
|
||||||
system_via_.set_control_line_input<MOS::MOS6522::Port::A, MOS::MOS6522::Line::One>(state.vsync);
|
system_via_.set_control_line_input<MOS::MOS6522::Port::A, MOS::MOS6522::Line::One>(state.vsync);
|
||||||
|
|
||||||
// bool print = false;
|
|
||||||
// uint16_t start_address = 0x7c00;
|
|
||||||
// int rows = 24;
|
|
||||||
// if(print) {
|
|
||||||
// for(int y = 0; y < rows; y++) {
|
|
||||||
// for(int x = 0; x < 40; x++) {
|
|
||||||
// printf("%c", ram_[start_address + y*40 + x]);
|
|
||||||
// }
|
|
||||||
// printf("\n");
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
|
|
||||||
// Count cycles since horizontal sync to insert a colour burst.
|
// Count cycles since horizontal sync to insert a colour burst.
|
||||||
if(state.hsync) {
|
if(state.hsync) {
|
||||||
++cycles_into_hsync_;
|
++cycles_into_hsync_;
|
||||||
} else {
|
} else {
|
||||||
cycles_into_hsync_ = 0;
|
cycles_into_hsync_ = 0;
|
||||||
}
|
}
|
||||||
const bool is_colour_burst = (cycles_into_hsync_ >= 5 && cycles_into_hsync_ < 9);
|
const bool is_colour_burst = cycles_into_hsync_ >= 5 && cycles_into_hsync_ < 9;
|
||||||
|
|
||||||
// Sync is taken to override pixels, and is combined as a simple OR.
|
// Sync is taken to override pixels, and is combined as a simple OR.
|
||||||
const bool is_sync = state.hsync || state.vsync;
|
const bool is_sync = state.hsync || state.vsync;
|
||||||
const bool is_blank = !is_sync && state.hsync;
|
|
||||||
|
// Check for a cursor leading edge.
|
||||||
|
cursor_shifter_ >>= 4;
|
||||||
|
if(state.cursor != previous_cursor_enabled_) {
|
||||||
|
if(state.cursor && state.display_enable) { // TODO: should I have to test display enable here? Or should
|
||||||
|
// the CRTC already have factored that in?
|
||||||
|
cursor_shifter_ =
|
||||||
|
((cursor_mask_ & 0x80) ? 0x0007 : 0) |
|
||||||
|
((cursor_mask_ & 0x40) ? 0x0070 : 0) |
|
||||||
|
((cursor_mask_ & 0x20) ? 0x7700 : 0);
|
||||||
|
}
|
||||||
|
previous_cursor_enabled_ = state.cursor;
|
||||||
|
}
|
||||||
|
|
||||||
OutputMode output_mode;
|
OutputMode output_mode;
|
||||||
|
const bool should_fetch = state.display_enable && !(state.row.get() & 8);
|
||||||
if(is_sync) {
|
if(is_sync) {
|
||||||
output_mode = OutputMode::Sync;
|
output_mode = OutputMode::Sync;
|
||||||
} else if(is_colour_burst) {
|
} else if(is_colour_burst) {
|
||||||
output_mode = OutputMode::ColourBurst;
|
output_mode = OutputMode::ColourBurst;
|
||||||
} else if(is_blank) {
|
} else if(should_fetch || cursor_shifter_) {
|
||||||
output_mode = OutputMode::Blank;
|
|
||||||
} else if(state.display_enable) {
|
|
||||||
output_mode = OutputMode::Pixels;
|
output_mode = OutputMode::Pixels;
|
||||||
} else {
|
} else {
|
||||||
output_mode = OutputMode::Border;
|
output_mode = OutputMode::Blank;
|
||||||
}
|
}
|
||||||
|
|
||||||
// If a transition between sync/border/pixels just occurred, flush whatever was
|
// If a transition between sync/border/pixels just occurred, flush whatever was
|
||||||
@@ -319,13 +365,8 @@ public:
|
|||||||
default:
|
default:
|
||||||
case OutputMode::Blank: crt_.output_blank(cycles_); break;
|
case OutputMode::Blank: crt_.output_blank(cycles_); break;
|
||||||
case OutputMode::Sync: crt_.output_sync(cycles_); break;
|
case OutputMode::Sync: crt_.output_sync(cycles_); break;
|
||||||
case OutputMode::Border: crt_.output_blank(cycles_); break;
|
|
||||||
case OutputMode::ColourBurst: crt_.output_default_colour_burst(cycles_); break;
|
case OutputMode::ColourBurst: crt_.output_default_colour_burst(cycles_); break;
|
||||||
case OutputMode::Pixels:
|
case OutputMode::Pixels: flush_pixels(); break;
|
||||||
crt_.output_data(cycles_, pixels_);
|
|
||||||
pixel_pointer_ = pixel_data_ = nullptr;
|
|
||||||
pixels_ = 0;
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -334,57 +375,49 @@ public:
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Increment cycles since state changed.
|
// Increment cycles since state changed.
|
||||||
cycles_ += cycle_length_;
|
cycles_ += crtc_clock_multiplier_ << 3;
|
||||||
|
|
||||||
// Collect some more pixels if output is ongoing.
|
// Collect some more pixels if output is ongoing.
|
||||||
if(previous_output_mode_ == OutputMode::Pixels) {
|
if(previous_output_mode_ == OutputMode::Pixels) {
|
||||||
|
// Flush the current buffer pixel if full; the CRTC allows many different display
|
||||||
|
// widths so it's not necessarily possible to predict the correct number in advance
|
||||||
|
// and using the upper bound could lead to inefficient behaviour.
|
||||||
|
if(pixel_data_ && (pixels_collected() == 320 || active_collation_ != previous_collation_)) {
|
||||||
|
flush_pixels();
|
||||||
|
cycles_ = 0;
|
||||||
|
}
|
||||||
|
previous_collation_ = active_collation_;
|
||||||
|
|
||||||
if(!pixel_data_) {
|
if(!pixel_data_) {
|
||||||
pixel_pointer_ = pixel_data_ = crt_.begin_data(320, 8);
|
pixel_pointer_ = pixel_data_ = crt_.begin_data(320, 8);
|
||||||
}
|
}
|
||||||
if(pixel_pointer_) {
|
if(pixel_pointer_) {
|
||||||
uint16_t address;
|
uint16_t address;
|
||||||
|
|
||||||
if(state.refresh_address & (1 << 13)) {
|
if(state.refresh.get() & (1 << 13)) {
|
||||||
// Teletext address generation mode.
|
// Teletext address generation mode.
|
||||||
address = uint16_t(
|
address = uint16_t(
|
||||||
0x3c00 |
|
0x3c00 |
|
||||||
((state.refresh_address & 0x800) << 3) |
|
((state.refresh.get() & 0x800) << 3) |
|
||||||
(state.refresh_address & 0x3ff)
|
(state.refresh.get() & 0x3ff)
|
||||||
);
|
);
|
||||||
// TODO: wraparound?
|
// TODO: wraparound? Does that happen on Mode 7?
|
||||||
} else {
|
} else {
|
||||||
address = uint16_t((state.refresh_address << 3) | (state.row_address & 7));
|
address = uint16_t((state.refresh.get() << 3) | (state.row.get() & 7));
|
||||||
if(address & 0x8000) {
|
if(address & 0x8000) {
|
||||||
address = (address + video_base_) & 0x7fff;
|
address = (address + video_base_) & 0x7fff;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Hard coded from here for Mode 0!
|
// Hard coded: pixel mode!
|
||||||
if(state.row_address & 8) {
|
pixel_shifter_ = should_fetch ? ram_[address] : 0;
|
||||||
std::fill(pixel_pointer_, pixel_pointer_+8, 0);
|
switch(crtc_clock_multiplier_ * active_collation_.pixels_per_clock) {
|
||||||
} else {
|
case 1: shift_pixels<1>(cursor_shifter_ & 7); break;
|
||||||
const auto source = ram_[address];
|
case 2: shift_pixels<2>(cursor_shifter_ & 7); break;
|
||||||
pixel_pointer_[0] = (source & 0x80) ? 0xff : 0x00;
|
case 4: shift_pixels<4>(cursor_shifter_ & 7); break;
|
||||||
pixel_pointer_[1] = (source & 0x40) ? 0xff : 0x00;
|
case 8: shift_pixels<8>(cursor_shifter_ & 7); break;
|
||||||
pixel_pointer_[2] = (source & 0x20) ? 0xff : 0x00;
|
case 16: shift_pixels<16>(cursor_shifter_ & 7); break;
|
||||||
pixel_pointer_[3] = (source & 0x10) ? 0xff : 0x00;
|
default: break;
|
||||||
pixel_pointer_[4] = (source & 0x08) ? 0xff : 0x00;
|
|
||||||
pixel_pointer_[5] = (source & 0x04) ? 0xff : 0x00;
|
|
||||||
pixel_pointer_[6] = (source & 0x02) ? 0xff : 0x00;
|
|
||||||
pixel_pointer_[7] = (source & 0x01) ? 0xff : 0x00;
|
|
||||||
}
|
|
||||||
|
|
||||||
pixel_pointer_ += 8;
|
|
||||||
pixels_ += 8;
|
|
||||||
|
|
||||||
// Flush the current buffer pixel if full; the CRTC allows many different display
|
|
||||||
// widths so it's not necessarily possible to predict the correct number in advance
|
|
||||||
// and using the upper bound could lead to inefficient behaviour.
|
|
||||||
if(pixels_ == 320) {
|
|
||||||
crt_.output_data(cycles_, pixels_);
|
|
||||||
pixel_pointer_ = pixel_data_ = nullptr;
|
|
||||||
cycles_ = 0;
|
|
||||||
pixels_ = 0;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -410,23 +443,61 @@ public:
|
|||||||
return crt_.get_display_type();
|
return crt_.get_display_type();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
enum class OutputMode {
|
enum class OutputMode {
|
||||||
Sync,
|
Sync,
|
||||||
Blank,
|
Blank,
|
||||||
ColourBurst,
|
ColourBurst,
|
||||||
Border,
|
|
||||||
Pixels
|
Pixels
|
||||||
} previous_output_mode_ = OutputMode::Sync;
|
};
|
||||||
|
struct PixelCollation {
|
||||||
|
int pixels_per_clock;
|
||||||
|
bool is_teletext;
|
||||||
|
|
||||||
|
bool operator !=(const PixelCollation &rhs) {
|
||||||
|
if(is_teletext && rhs.is_teletext) return false;
|
||||||
|
return pixels_per_clock != rhs.pixels_per_clock;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
OutputMode previous_output_mode_ = OutputMode::Sync;
|
||||||
int cycles_ = 0;
|
int cycles_ = 0;
|
||||||
int cycles_into_hsync_ = 0;
|
int cycles_into_hsync_ = 0;
|
||||||
int cycle_length_ = 8;
|
|
||||||
|
|
||||||
Outputs::CRT::CRT crt_;
|
Outputs::CRT::CRT crt_;
|
||||||
|
|
||||||
uint8_t *pixel_data_ = nullptr, *pixel_pointer_ = nullptr;
|
uint8_t *pixel_data_ = nullptr, *pixel_pointer_ = nullptr;
|
||||||
size_t pixels_;
|
size_t pixels_collected() const {
|
||||||
|
return size_t(pixel_pointer_ - pixel_data_);
|
||||||
|
}
|
||||||
|
void flush_pixels() {
|
||||||
|
crt_.output_data(cycles_, pixels_collected());
|
||||||
|
pixel_pointer_ = pixel_data_ = nullptr;
|
||||||
|
}
|
||||||
|
PixelCollation previous_collation_;
|
||||||
|
uint8_t palette_[16];
|
||||||
|
std::bitset<16> flash_flags_;
|
||||||
|
uint8_t flash_mask_ = 0;
|
||||||
|
|
||||||
|
int crtc_clock_multiplier_ = 1;
|
||||||
|
PixelCollation active_collation_;
|
||||||
|
uint8_t pixel_shifter_ = 0;
|
||||||
|
|
||||||
|
uint8_t cursor_mask_ = 0;
|
||||||
|
uint32_t cursor_shifter_ = 0;
|
||||||
|
bool previous_cursor_enabled_ = false;
|
||||||
|
|
||||||
|
template <int count> void shift_pixels(const uint8_t cursor_mask) {
|
||||||
|
for(int c = 0; c < count; c++) {
|
||||||
|
const uint8_t colour =
|
||||||
|
((pixel_shifter_ & 0x80) >> 4) |
|
||||||
|
((pixel_shifter_ & 0x20) >> 3) |
|
||||||
|
((pixel_shifter_ & 0x08) >> 2) |
|
||||||
|
((pixel_shifter_ & 0x02) >> 1);
|
||||||
|
pixel_shifter_ <<= 1;
|
||||||
|
*pixel_pointer_++ = palette_[colour] ^ (flash_flags_[colour] ? flash_mask_ : 0x00) ^ cursor_mask;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
const uint8_t *const ram_ = nullptr;
|
const uint8_t *const ram_ = nullptr;
|
||||||
SystemVIA &system_via_;
|
SystemVIA &system_via_;
|
||||||
@@ -434,16 +505,21 @@ private:
|
|||||||
using CRTC = Motorola::CRTC::CRTC6845<
|
using CRTC = Motorola::CRTC::CRTC6845<
|
||||||
CRTCBusHandler,
|
CRTCBusHandler,
|
||||||
Motorola::CRTC::Personality::HD6845S,
|
Motorola::CRTC::Personality::HD6845S,
|
||||||
Motorola::CRTC::CursorType::None>;
|
Motorola::CRTC::CursorType::MDA>;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
template <bool has_1770>
|
||||||
class ConcreteMachine:
|
class ConcreteMachine:
|
||||||
|
public Activity::Source,
|
||||||
public Machine,
|
public Machine,
|
||||||
public MachineTypes::AudioProducer,
|
public MachineTypes::AudioProducer,
|
||||||
public MachineTypes::MappedKeyboardMachine,
|
public MachineTypes::MappedKeyboardMachine,
|
||||||
|
public MachineTypes::MediaTarget,
|
||||||
public MachineTypes::ScanProducer,
|
public MachineTypes::ScanProducer,
|
||||||
public MachineTypes::TimedMachine,
|
public MachineTypes::TimedMachine,
|
||||||
public MOS::MOS6522::IRQDelegatePortHandler::Delegate
|
public MOS::MOS6522::IRQDelegatePortHandler::Delegate,
|
||||||
|
public NEC::uPD7002::Delegate,
|
||||||
|
public WD::WD1770::Delegate
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
ConcreteMachine(
|
ConcreteMachine(
|
||||||
@@ -456,17 +532,27 @@ public:
|
|||||||
system_via_(system_via_port_handler_),
|
system_via_(system_via_port_handler_),
|
||||||
crtc_bus_handler_(ram_.data(), system_via_),
|
crtc_bus_handler_(ram_.data(), system_via_),
|
||||||
crtc_(crtc_bus_handler_),
|
crtc_(crtc_bus_handler_),
|
||||||
acia_(HalfCycles(2'000'000)) // TODO: look up real ACIA clock rate.
|
acia_(HalfCycles(2'000'000)), // TODO: look up real ACIA clock rate.
|
||||||
|
adc_(HalfCycles(2'000'000))
|
||||||
{
|
{
|
||||||
set_clock_rate(2'000'000);
|
set_clock_rate(2'000'000);
|
||||||
|
|
||||||
system_via_port_handler_.set_interrupt_delegate(this);
|
system_via_port_handler_.set_interrupt_delegate(this);
|
||||||
user_via_port_handler_.set_interrupt_delegate(this);
|
user_via_port_handler_.set_interrupt_delegate(this);
|
||||||
|
adc_.set_delegate(this);
|
||||||
|
|
||||||
// Grab ROMs.
|
// Grab ROMs.
|
||||||
using Request = ::ROM::Request;
|
using Request = ::ROM::Request;
|
||||||
using Name = ::ROM::Name;
|
using Name = ::ROM::Name;
|
||||||
const auto request = Request(Name::AcornBASICII) && Request(Name::BBCMicroMOS12);
|
|
||||||
|
auto request = Request(Name::AcornBASICII) && Request(Name::BBCMicroMOS12);
|
||||||
|
if(target.has_1770dfs) {
|
||||||
|
request = request && Request(Name::BBCMicroDFS226);
|
||||||
|
}
|
||||||
|
if(target.has_adfs) {
|
||||||
|
request = request && Request(Name::BBCMicroADFS130);
|
||||||
|
}
|
||||||
|
|
||||||
auto roms = rom_fetcher(request);
|
auto roms = rom_fetcher(request);
|
||||||
if(!request.validate(roms)) {
|
if(!request.validate(roms)) {
|
||||||
throw ROMMachine::Error::MissingROMs;
|
throw ROMMachine::Error::MissingROMs;
|
||||||
@@ -475,8 +561,27 @@ public:
|
|||||||
const auto os_data = roms.find(Name::BBCMicroMOS12)->second;
|
const auto os_data = roms.find(Name::BBCMicroMOS12)->second;
|
||||||
std::copy(os_data.begin(), os_data.end(), os_.begin());
|
std::copy(os_data.begin(), os_data.end(), os_.begin());
|
||||||
|
|
||||||
|
// Put BASIC in pole position.
|
||||||
install_sideways(15, roms.find(Name::AcornBASICII)->second, false);
|
install_sideways(15, roms.find(Name::AcornBASICII)->second, false);
|
||||||
|
|
||||||
|
// Install filing systems: put the DFS before the ADFS because it's more common on the BBC.
|
||||||
|
size_t fs_slot = 14;
|
||||||
|
if(target.has_1770dfs) {
|
||||||
|
install_sideways(fs_slot--, roms.find(Name::BBCMicroDFS226)->second, false);
|
||||||
|
}
|
||||||
|
if(target.has_adfs) {
|
||||||
|
install_sideways(fs_slot--, roms.find(Name::BBCMicroADFS130)->second, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Throw sideways RAM into all unused slots.
|
||||||
|
if(target.has_sideways_ram) {
|
||||||
|
for(size_t c = 0; c < 16; c++) {
|
||||||
|
if(!rom_inserted_[c]) {
|
||||||
|
rom_inserted_[c] = rom_write_masks_[c] = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Setup fixed parts of memory map.
|
// Setup fixed parts of memory map.
|
||||||
page(0, &ram_[0], true);
|
page(0, &ram_[0], true);
|
||||||
page(1, &ram_[16384], true);
|
page(1, &ram_[16384], true);
|
||||||
@@ -484,7 +589,11 @@ public:
|
|||||||
page(3, os_.data(), false);
|
page(3, os_.data(), false);
|
||||||
Memory::Fuzz(ram_);
|
Memory::Fuzz(ram_);
|
||||||
|
|
||||||
(void)target;
|
if constexpr (has_1770) {
|
||||||
|
wd1770_.set_delegate(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
insert_media(target.media);
|
||||||
}
|
}
|
||||||
|
|
||||||
// MARK: - 6502 bus.
|
// MARK: - 6502 bus.
|
||||||
@@ -516,7 +625,7 @@ public:
|
|||||||
};
|
};
|
||||||
|
|
||||||
// Determine whether this access hits the 1Mhz bus; if so then apply appropriate penalty, and update phase.
|
// Determine whether this access hits the 1Mhz bus; if so then apply appropriate penalty, and update phase.
|
||||||
const auto duration = is_1mhz(address) ? Cycles(2 + (phase_&1)) : Cycles(1);
|
const auto duration = Cycles(is_1mhz(address) ? 2 + (phase_&1) : 1);
|
||||||
phase_ += duration.as<int>();
|
phase_ += duration.as<int>();
|
||||||
|
|
||||||
|
|
||||||
@@ -524,7 +633,6 @@ public:
|
|||||||
// 1Mhz devices.
|
// 1Mhz devices.
|
||||||
//
|
//
|
||||||
const auto half_cycles = HalfCycles(duration.as_integral());
|
const auto half_cycles = HalfCycles(duration.as_integral());
|
||||||
audio_ += half_cycles;
|
|
||||||
system_via_.run_for(half_cycles);
|
system_via_.run_for(half_cycles);
|
||||||
system_via_port_handler_.advance_keyboard_scan(half_cycles);
|
system_via_port_handler_.advance_keyboard_scan(half_cycles);
|
||||||
user_via_.run_for(half_cycles);
|
user_via_.run_for(half_cycles);
|
||||||
@@ -533,7 +641,7 @@ public:
|
|||||||
//
|
//
|
||||||
// 2Mhz devices.
|
// 2Mhz devices.
|
||||||
//
|
//
|
||||||
// TODO: if CRTC clock is 1Mhz, adapt.
|
audio_ += duration;
|
||||||
if(crtc_2mhz_) {
|
if(crtc_2mhz_) {
|
||||||
crtc_.run_for(duration);
|
crtc_.run_for(duration);
|
||||||
} else {
|
} else {
|
||||||
@@ -541,8 +649,14 @@ public:
|
|||||||
const auto cycles = (phase_ >> 1) - ((phase_ - duration.as<int>()) >> 1);
|
const auto cycles = (phase_ >> 1) - ((phase_ - duration.as<int>()) >> 1);
|
||||||
crtc_.run_for(Cycles(cycles));
|
crtc_.run_for(Cycles(cycles));
|
||||||
}
|
}
|
||||||
|
adc_.run_for(duration);
|
||||||
|
|
||||||
|
|
||||||
|
if constexpr (has_1770) {
|
||||||
|
// The WD1770 is nominally clocked at 8Mhz.
|
||||||
|
wd1770_.run_for(duration * 4);
|
||||||
|
}
|
||||||
|
|
||||||
//
|
//
|
||||||
// Questionably-clocked devices.
|
// Questionably-clocked devices.
|
||||||
//
|
//
|
||||||
@@ -552,7 +666,6 @@ public:
|
|||||||
//
|
//
|
||||||
// Check for an IO access; if found then perform that and exit.
|
// Check for an IO access; if found then perform that and exit.
|
||||||
//
|
//
|
||||||
// static bool log = false;
|
|
||||||
if(address >= 0xfc00 && address < 0xff00) {
|
if(address >= 0xfc00 && address < 0xff00) {
|
||||||
if(address >= 0xfe40 && address < 0xfe60) {
|
if(address >= 0xfe40 && address < 0xfe60) {
|
||||||
if(is_read(operation)) {
|
if(is_read(operation)) {
|
||||||
@@ -615,6 +728,27 @@ public:
|
|||||||
// Logger::info().append("ACIA write: %02x", *value);
|
// Logger::info().append("ACIA write: %02x", *value);
|
||||||
acia_.write(address, *value);
|
acia_.write(address, *value);
|
||||||
}
|
}
|
||||||
|
} else if(address >= 0xfec0 && address < 0xfee0) {
|
||||||
|
if(is_read(operation)) {
|
||||||
|
*value = adc_.read(address);
|
||||||
|
} else {
|
||||||
|
adc_.write(address, *value);
|
||||||
|
}
|
||||||
|
} else if(has_1770 && address >= 0xfe80 && address < 0xfe88) {
|
||||||
|
switch(address) {
|
||||||
|
case 0xfe80:
|
||||||
|
if(!is_read(operation)) {
|
||||||
|
wd1770_.set_control_register(*value);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
if(is_read(operation)) {
|
||||||
|
*value = wd1770_.read(address);
|
||||||
|
} else {
|
||||||
|
wd1770_.write(address, *value);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
Logger::error()
|
Logger::error()
|
||||||
@@ -627,14 +761,6 @@ public:
|
|||||||
//
|
//
|
||||||
// ROM or RAM access.
|
// ROM or RAM access.
|
||||||
//
|
//
|
||||||
// if(operation == CPU::MOS6502Esque::BusOperation::ReadOpcode) {
|
|
||||||
// log |= address == 0xc4c0;
|
|
||||||
//
|
|
||||||
// if(log) {
|
|
||||||
// printf("%04x\n", address);
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
|
|
||||||
if(is_read(operation)) {
|
if(is_read(operation)) {
|
||||||
// TODO: probably don't do this with this condition? See how it compiles. If it's a CMOV somehow, no problem.
|
// TODO: probably don't do this with this condition? See how it compiles. If it's a CMOV somehow, no problem.
|
||||||
if((address >> 14) == 2 && !sideways_read_mask_) {
|
if((address >> 14) == 2 && !sideways_read_mask_) {
|
||||||
@@ -645,10 +771,6 @@ public:
|
|||||||
} else {
|
} else {
|
||||||
if(memory_write_masks_[address >> 14]) {
|
if(memory_write_masks_[address >> 14]) {
|
||||||
memory_[address >> 14][address] = *value;
|
memory_[address >> 14][address] = *value;
|
||||||
|
|
||||||
if(address >= 0x7c00 && *value) {
|
|
||||||
Logger::info().append("Output character: %c", *value);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -656,6 +778,14 @@ public:
|
|||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
// MARK: - Activity::Source.
|
||||||
|
void set_activity_observer(Activity::Observer *const observer) override {
|
||||||
|
if(has_1770) {
|
||||||
|
wd1770_.set_activity_observer(observer);
|
||||||
|
}
|
||||||
|
system_via_port_handler_.set_activity_observer(observer);
|
||||||
|
}
|
||||||
|
|
||||||
// MARK: - AudioProducer.
|
// MARK: - AudioProducer.
|
||||||
Outputs::Speaker::Speaker *get_speaker() override {
|
Outputs::Speaker::Speaker *get_speaker() override {
|
||||||
return audio_.speaker();
|
return audio_.speaker();
|
||||||
@@ -700,6 +830,20 @@ private:
|
|||||||
update_irq_line();
|
update_irq_line();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// MARK: - uPD7002::Delegate.
|
||||||
|
void did_change_interrupt_status(NEC::uPD7002 &) override {
|
||||||
|
system_via_.set_control_line_input<MOS::MOS6522::Port::B, MOS::MOS6522::Line::One>(adc_.interrupt());
|
||||||
|
}
|
||||||
|
|
||||||
|
// MARK: - MediaTarget.
|
||||||
|
bool insert_media(const Analyser::Static::Media &media) override {
|
||||||
|
if(!media.disks.empty() && has_1770) {
|
||||||
|
wd1770_.set_disk(media.disks.front(), 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
return !media.disks.empty();
|
||||||
|
}
|
||||||
|
|
||||||
// MARK: - Clock phase.
|
// MARK: - Clock phase.
|
||||||
int phase_ = 0;
|
int phase_ = 0;
|
||||||
|
|
||||||
@@ -755,6 +899,14 @@ private:
|
|||||||
bool crtc_2mhz_ = true;
|
bool crtc_2mhz_ = true;
|
||||||
|
|
||||||
Motorola::ACIA::ACIA acia_;
|
Motorola::ACIA::ACIA acia_;
|
||||||
|
|
||||||
|
NEC::uPD7002 adc_;
|
||||||
|
|
||||||
|
// MARK: - WD1770.
|
||||||
|
Electron::Plus3 wd1770_;
|
||||||
|
void wd1770_did_change_output(WD::WD1770 &) override {
|
||||||
|
m6502_.set_nmi_line(wd1770_.get_interrupt_request_line() || wd1770_.get_data_request_line());
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -767,5 +919,9 @@ std::unique_ptr<Machine> Machine::BBCMicro(
|
|||||||
) {
|
) {
|
||||||
using Target = Analyser::Static::Acorn::BBCMicroTarget;
|
using Target = Analyser::Static::Acorn::BBCMicroTarget;
|
||||||
const Target *const acorn_target = dynamic_cast<const Target *>(target);
|
const Target *const acorn_target = dynamic_cast<const Target *>(target);
|
||||||
return std::make_unique<BBCMicro::ConcreteMachine>(*acorn_target, rom_fetcher);
|
if(acorn_target->has_1770dfs || acorn_target->has_adfs) {
|
||||||
|
return std::make_unique<BBCMicro::ConcreteMachine<true>>(*acorn_target, rom_fetcher);
|
||||||
|
} else {
|
||||||
|
return std::make_unique<BBCMicro::ConcreteMachine<false>>(*acorn_target, rom_fetcher);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@@ -52,11 +52,13 @@ private:
|
|||||||
|
|
||||||
{Key::Hyphen, 0x17},
|
{Key::Hyphen, 0x17},
|
||||||
{Key::Equals, 0x18},
|
{Key::Equals, 0x18},
|
||||||
{Key::Quote, 0x69},
|
{Key::BackTick, 0x69},
|
||||||
|
|
||||||
{Key::OpenSquareBracket, 0x38},
|
{Key::OpenSquareBracket, 0x38},
|
||||||
{Key::CloseSquareBracket, 0x58},
|
{Key::CloseSquareBracket, 0x58},
|
||||||
{Key::Semicolon, 0x48},
|
|
||||||
|
{Key::Semicolon, 0x57},
|
||||||
|
{Key::Quote, 0x48},
|
||||||
|
|
||||||
{Key::Enter, 0x49},
|
{Key::Enter, 0x49},
|
||||||
{Key::Backspace, 0x59},
|
{Key::Backspace, 0x59},
|
||||||
|
@@ -778,7 +778,7 @@ private:
|
|||||||
bool speaker_is_enabled_ = false;
|
bool speaker_is_enabled_ = false;
|
||||||
|
|
||||||
// MARK: - Caps Lock status and the activity observer.
|
// MARK: - Caps Lock status and the activity observer.
|
||||||
const std::string caps_led = "CAPS";
|
static inline const std::string caps_led = "CAPS";
|
||||||
bool caps_led_state_ = false;
|
bool caps_led_state_ = false;
|
||||||
Activity::Observer *activity_observer_ = nullptr;
|
Activity::Observer *activity_observer_ = nullptr;
|
||||||
};
|
};
|
||||||
|
@@ -36,6 +36,7 @@ void Plus3::set_control_register(uint8_t control) {
|
|||||||
// bit 1 => enable or disable drive 2
|
// bit 1 => enable or disable drive 2
|
||||||
// bit 2 => side select
|
// bit 2 => side select
|
||||||
// bit 3 => single density select
|
// bit 3 => single density select
|
||||||
|
// bit 5 => reset?
|
||||||
|
|
||||||
uint8_t changes = control ^ last_control_;
|
uint8_t changes = control ^ last_control_;
|
||||||
last_control_ = control;
|
last_control_ = control;
|
||||||
|
@@ -275,7 +275,7 @@ private:
|
|||||||
Mouse &mouse_;
|
Mouse &mouse_;
|
||||||
Joystick *joystick_ = nullptr;
|
Joystick *joystick_ = nullptr;
|
||||||
Activity::Observer *observer_ = nullptr;
|
Activity::Observer *observer_ = nullptr;
|
||||||
inline static const std::string led_name = "Power";
|
static inline const std::string led_name = "Power";
|
||||||
} cia_a_handler_;
|
} cia_a_handler_;
|
||||||
|
|
||||||
class CIABHandler: public MOS::MOS6526::PortHandler {
|
class CIABHandler: public MOS::MOS6526::PortHandler {
|
||||||
|
@@ -251,9 +251,9 @@ public:
|
|||||||
// ... so form the real access address.
|
// ... so form the real access address.
|
||||||
const uint16_t address =
|
const uint16_t address =
|
||||||
uint16_t(
|
uint16_t(
|
||||||
((state.refresh_address & 0x3ff) << 1) |
|
((state.refresh.get() & 0x3ff) << 1) |
|
||||||
((state.row_address & 0x7) << 11) |
|
((state.row.get() & 0x7) << 11) |
|
||||||
((state.refresh_address & 0x3000) << 2)
|
((state.refresh.get() & 0x3000) << 2)
|
||||||
);
|
);
|
||||||
|
|
||||||
// Fetch two bytes and translate into pixels. Guaranteed: the mode can change only at
|
// Fetch two bytes and translate into pixels. Guaranteed: the mode can change only at
|
||||||
|
@@ -258,8 +258,8 @@ private:
|
|||||||
//
|
//
|
||||||
// Meanwhile, row address is used as a substitute 14th address line.
|
// Meanwhile, row address is used as a substitute 14th address line.
|
||||||
const auto base_address =
|
const auto base_address =
|
||||||
((state.refresh_address & 0xfff) << 1) +
|
((state.refresh.get() & 0xfff) << 1) +
|
||||||
((state.row_address & 1) << 13);
|
((state.row.get() & 1) << 13);
|
||||||
const uint8_t bitmap[] = {
|
const uint8_t bitmap[] = {
|
||||||
ram[base_address],
|
ram[base_address],
|
||||||
ram[base_address + 1],
|
ram[base_address + 1],
|
||||||
@@ -295,9 +295,9 @@ private:
|
|||||||
}
|
}
|
||||||
|
|
||||||
void serialise_text(const Motorola::CRTC::BusState &state) {
|
void serialise_text(const Motorola::CRTC::BusState &state) {
|
||||||
const uint8_t attributes = ram[((state.refresh_address << 1) + 1) & 0x3fff];
|
const uint8_t attributes = ram[((state.refresh.get() << 1) + 1) & 0x3fff];
|
||||||
const uint8_t glyph = ram[((state.refresh_address << 1) + 0) & 0x3fff];
|
const uint8_t glyph = ram[((state.refresh.get() << 1) + 0) & 0x3fff];
|
||||||
const uint8_t row = font[(glyph * 8) + state.row_address];
|
const uint8_t row = font[(glyph * 8) + state.row.get()];
|
||||||
|
|
||||||
uint8_t colours[2] = { rgb(attributes >> 4), rgbi(attributes) };
|
uint8_t colours[2] = { rgb(attributes >> 4), rgbi(attributes) };
|
||||||
|
|
||||||
|
@@ -158,9 +158,9 @@ private:
|
|||||||
pixel_pointer[4] = pixel_pointer[5] = pixel_pointer[6] = pixel_pointer[7] =
|
pixel_pointer[4] = pixel_pointer[5] = pixel_pointer[6] = pixel_pointer[7] =
|
||||||
pixel_pointer[8] = low_intensity;
|
pixel_pointer[8] = low_intensity;
|
||||||
} else {
|
} else {
|
||||||
const uint8_t attributes = ram[((state.refresh_address << 1) + 1) & 0xfff];
|
const uint8_t attributes = ram[((state.refresh.get() << 1) + 1) & 0xfff];
|
||||||
const uint8_t glyph = ram[((state.refresh_address << 1) + 0) & 0xfff];
|
const uint8_t glyph = ram[((state.refresh.get() << 1) + 0) & 0xfff];
|
||||||
uint8_t row = font[(glyph * 14) + state.row_address];
|
uint8_t row = font[(glyph * 14) + state.row.get()];
|
||||||
|
|
||||||
const uint8_t intensity = (attributes & 0x08) ? high_intensity : low_intensity;
|
const uint8_t intensity = (attributes & 0x08) ? high_intensity : low_intensity;
|
||||||
uint8_t blank = off;
|
uint8_t blank = off;
|
||||||
@@ -183,7 +183,7 @@ private:
|
|||||||
blank = (blank == off) ? intensity : off;
|
blank = (blank == off) ? intensity : off;
|
||||||
}
|
}
|
||||||
|
|
||||||
if(((attributes & 7) == 1) && state.row_address == 13) {
|
if(((attributes & 7) == 1) && state.row.get() == 13) {
|
||||||
// Draw as underline.
|
// Draw as underline.
|
||||||
std::fill(pixel_pointer, pixel_pointer + 9, intensity);
|
std::fill(pixel_pointer, pixel_pointer + 9, intensity);
|
||||||
} else {
|
} else {
|
||||||
|
@@ -1130,7 +1130,7 @@ using namespace PCCompatible;
|
|||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
#ifndef NDEBUG
|
#ifndef NDEBUG
|
||||||
static constexpr bool ForceAT = true;
|
static constexpr bool ForceAT = false;//true;
|
||||||
#else
|
#else
|
||||||
static constexpr bool ForceAT = false;
|
static constexpr bool ForceAT = false;
|
||||||
#endif
|
#endif
|
||||||
|
@@ -429,6 +429,22 @@ const std::vector<Description> &Description::all_roms() {
|
|||||||
16_kb,
|
16_kb,
|
||||||
0x3c14fc70u
|
0x3c14fc70u
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
BBCMicroDFS226,
|
||||||
|
"BBCMicro",
|
||||||
|
"the Acorn 1770 DFS 2.26 ROM",
|
||||||
|
"dfs-2.26.rom",
|
||||||
|
16_kb,
|
||||||
|
0x5ae33e94u
|
||||||
|
},
|
||||||
|
{
|
||||||
|
BBCMicroADFS130,
|
||||||
|
"BBCMicro",
|
||||||
|
"the Acorn ADFS 1.30 ROM",
|
||||||
|
"adfs-1.30.rom",
|
||||||
|
16_kb,
|
||||||
|
0xd3855588u
|
||||||
|
},
|
||||||
|
|
||||||
//
|
//
|
||||||
// ColecoVision.
|
// ColecoVision.
|
||||||
|
@@ -82,6 +82,8 @@ enum Name {
|
|||||||
|
|
||||||
// BBC Micro.
|
// BBC Micro.
|
||||||
BBCMicroMOS12,
|
BBCMicroMOS12,
|
||||||
|
BBCMicroDFS226,
|
||||||
|
BBCMicroADFS130,
|
||||||
|
|
||||||
// ColecoVision.
|
// ColecoVision.
|
||||||
ColecoVisionBIOS,
|
ColecoVisionBIOS,
|
||||||
|
85
Numeric/SizedCounter.hpp
Normal file
85
Numeric/SizedCounter.hpp
Normal file
@@ -0,0 +1,85 @@
|
|||||||
|
//
|
||||||
|
// 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;
|
||||||
|
|
||||||
|
IntT get() const {
|
||||||
|
return counter_;
|
||||||
|
}
|
||||||
|
|
||||||
|
SizedCounter operator +(const SizedCounter offset) const {
|
||||||
|
return SizedCounter<bits>(counter_ + offset.counter_);
|
||||||
|
}
|
||||||
|
|
||||||
|
SizedCounter operator &(const SizedCounter offset) const {
|
||||||
|
return SizedCounter<bits>(counter_ & offset.counter_);
|
||||||
|
}
|
||||||
|
|
||||||
|
SizedCounter operator >>(const int shift) const {
|
||||||
|
return SizedCounter<bits>(counter_ >> shift);
|
||||||
|
}
|
||||||
|
|
||||||
|
SizedCounter operator <<(const int shift) const {
|
||||||
|
return SizedCounter<bits>(counter_ << shift);
|
||||||
|
}
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool operator!() const {
|
||||||
|
return !counter_;
|
||||||
|
}
|
||||||
|
|
||||||
|
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_{};
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
@@ -360,6 +360,9 @@
|
|||||||
4B4518A51F75FD1C00926311 /* SSD.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B4518991F75FD1B00926311 /* SSD.cpp */; };
|
4B4518A51F75FD1C00926311 /* SSD.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B4518991F75FD1B00926311 /* SSD.cpp */; };
|
||||||
4B47770B268FBE4D005C2340 /* FAT.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B477709268FBE4D005C2340 /* FAT.cpp */; };
|
4B47770B268FBE4D005C2340 /* FAT.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B477709268FBE4D005C2340 /* FAT.cpp */; };
|
||||||
4B47770D26900685005C2340 /* EnterpriseDaveTests.mm in Sources */ = {isa = PBXBuildFile; fileRef = 4B47770C26900685005C2340 /* EnterpriseDaveTests.mm */; };
|
4B47770D26900685005C2340 /* EnterpriseDaveTests.mm in Sources */ = {isa = PBXBuildFile; fileRef = 4B47770C26900685005C2340 /* EnterpriseDaveTests.mm */; };
|
||||||
|
4B47F3B62E7BAB94005D4DEC /* uPD7002.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B47F3B52E7BAB94005D4DEC /* uPD7002.cpp */; };
|
||||||
|
4B47F3B72E7BAB94005D4DEC /* uPD7002.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B47F3B52E7BAB94005D4DEC /* uPD7002.cpp */; };
|
||||||
|
4B47F3B82E7BAB94005D4DEC /* uPD7002.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B47F3B52E7BAB94005D4DEC /* uPD7002.cpp */; };
|
||||||
4B47F6C5241C87A100ED06F7 /* Struct.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B47F6C4241C87A100ED06F7 /* Struct.cpp */; };
|
4B47F6C5241C87A100ED06F7 /* Struct.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B47F6C4241C87A100ED06F7 /* Struct.cpp */; };
|
||||||
4B47F6C6241C87A100ED06F7 /* Struct.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B47F6C4241C87A100ED06F7 /* Struct.cpp */; };
|
4B47F6C6241C87A100ED06F7 /* Struct.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B47F6C4241C87A100ED06F7 /* Struct.cpp */; };
|
||||||
4B49F0A923346F7A0045E6A6 /* MacintoshOptions.xib in Resources */ = {isa = PBXBuildFile; fileRef = 4B49F0A723346F7A0045E6A6 /* MacintoshOptions.xib */; };
|
4B49F0A923346F7A0045E6A6 /* MacintoshOptions.xib in Resources */ = {isa = PBXBuildFile; fileRef = 4B49F0A723346F7A0045E6A6 /* MacintoshOptions.xib */; };
|
||||||
@@ -1597,6 +1600,8 @@
|
|||||||
4B477709268FBE4D005C2340 /* FAT.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; name = FAT.cpp; path = Parsers/FAT.cpp; sourceTree = "<group>"; };
|
4B477709268FBE4D005C2340 /* FAT.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; name = FAT.cpp; path = Parsers/FAT.cpp; sourceTree = "<group>"; };
|
||||||
4B47770A268FBE4D005C2340 /* FAT.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; name = FAT.hpp; path = Parsers/FAT.hpp; sourceTree = "<group>"; };
|
4B47770A268FBE4D005C2340 /* FAT.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; name = FAT.hpp; path = Parsers/FAT.hpp; sourceTree = "<group>"; };
|
||||||
4B47770C26900685005C2340 /* EnterpriseDaveTests.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = EnterpriseDaveTests.mm; sourceTree = "<group>"; };
|
4B47770C26900685005C2340 /* EnterpriseDaveTests.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = EnterpriseDaveTests.mm; sourceTree = "<group>"; };
|
||||||
|
4B47F3B32E7B9A14005D4DEC /* uPD7002.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = uPD7002.hpp; sourceTree = "<group>"; };
|
||||||
|
4B47F3B52E7BAB94005D4DEC /* uPD7002.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = uPD7002.cpp; sourceTree = "<group>"; };
|
||||||
4B47F6C4241C87A100ED06F7 /* Struct.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = Struct.cpp; sourceTree = "<group>"; };
|
4B47F6C4241C87A100ED06F7 /* Struct.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = Struct.cpp; sourceTree = "<group>"; };
|
||||||
4B49F0A823346F7A0045E6A6 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = Base; path = "Clock Signal/Base.lproj/MacintoshOptions.xib"; sourceTree = SOURCE_ROOT; };
|
4B49F0A823346F7A0045E6A6 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = Base; path = "Clock Signal/Base.lproj/MacintoshOptions.xib"; sourceTree = SOURCE_ROOT; };
|
||||||
4B4A762E1DB1A3FA007AAE2E /* AY38910.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = AY38910.cpp; sourceTree = "<group>"; };
|
4B4A762E1DB1A3FA007AAE2E /* AY38910.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = AY38910.cpp; sourceTree = "<group>"; };
|
||||||
@@ -1789,6 +1794,7 @@
|
|||||||
4B8805FA1DCFF807003085B1 /* Oric.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; name = Oric.hpp; path = Parsers/Oric.hpp; sourceTree = "<group>"; };
|
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>"; };
|
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>"; };
|
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>"; };
|
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>"; };
|
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>"; };
|
4B8944E5201967B4007DE474 /* ConfidenceSource.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = ConfidenceSource.hpp; sourceTree = "<group>"; };
|
||||||
@@ -3316,6 +3322,15 @@
|
|||||||
path = Formats;
|
path = Formats;
|
||||||
sourceTree = "<group>";
|
sourceTree = "<group>";
|
||||||
};
|
};
|
||||||
|
4B47F3B42E7B9A14005D4DEC /* uPD7002 */ = {
|
||||||
|
isa = PBXGroup;
|
||||||
|
children = (
|
||||||
|
4B47F3B52E7BAB94005D4DEC /* uPD7002.cpp */,
|
||||||
|
4B47F3B32E7B9A14005D4DEC /* uPD7002.hpp */,
|
||||||
|
);
|
||||||
|
path = uPD7002;
|
||||||
|
sourceTree = "<group>";
|
||||||
|
};
|
||||||
4B4A762D1DB1A35C007AAE2E /* AY38910 */ = {
|
4B4A762D1DB1A35C007AAE2E /* AY38910 */ = {
|
||||||
isa = PBXGroup;
|
isa = PBXGroup;
|
||||||
children = (
|
children = (
|
||||||
@@ -3727,15 +3742,16 @@
|
|||||||
children = (
|
children = (
|
||||||
4B43984129674943006B0BFC /* BitReverse.hpp */,
|
4B43984129674943006B0BFC /* BitReverse.hpp */,
|
||||||
4BD155312716362A00410C6E /* BitSpread.hpp */,
|
4BD155312716362A00410C6E /* BitSpread.hpp */,
|
||||||
|
4B0B239F2D658C9400153879 /* BitStream.hpp */,
|
||||||
4281572E2AA0334300E16AA1 /* Carry.hpp */,
|
4281572E2AA0334300E16AA1 /* Carry.hpp */,
|
||||||
4B7BA03E23D55E7900B98D9E /* CRC.hpp */,
|
4B7BA03E23D55E7900B98D9E /* CRC.hpp */,
|
||||||
4B7BA03F23D55E7900B98D9E /* LFSR.hpp */,
|
4B7BA03F23D55E7900B98D9E /* LFSR.hpp */,
|
||||||
4B66E1A8297719270057ED0F /* NumericCoder.hpp */,
|
4B66E1A8297719270057ED0F /* NumericCoder.hpp */,
|
||||||
4BB5B995281B1D3E00522DA9 /* RegisterSizes.hpp */,
|
4BB5B995281B1D3E00522DA9 /* RegisterSizes.hpp */,
|
||||||
|
4B88559C2E8185BF00E251DD /* SizedCounter.hpp */,
|
||||||
4BFEA2F12682A90200EBF94C /* Sizes.hpp */,
|
4BFEA2F12682A90200EBF94C /* Sizes.hpp */,
|
||||||
4BD9713A2BFD7E7100C907AA /* StringSimilarity.hpp */,
|
4BD9713A2BFD7E7100C907AA /* StringSimilarity.hpp */,
|
||||||
4B0329202D0A78DC00C51EB5 /* UpperBound.hpp */,
|
4B0329202D0A78DC00C51EB5 /* UpperBound.hpp */,
|
||||||
4B0B239F2D658C9400153879 /* BitStream.hpp */,
|
|
||||||
);
|
);
|
||||||
name = Numeric;
|
name = Numeric;
|
||||||
path = ../../Numeric;
|
path = ../../Numeric;
|
||||||
@@ -5075,6 +5091,7 @@
|
|||||||
4BF0BC6E2973318E00CCA2B5 /* RP5C01 */,
|
4BF0BC6E2973318E00CCA2B5 /* RP5C01 */,
|
||||||
4B0ACBFF237756EC008902D0 /* Serial */,
|
4B0ACBFF237756EC008902D0 /* Serial */,
|
||||||
4BB0A6582044FD3000FB3688 /* SN76489 */,
|
4BB0A6582044FD3000FB3688 /* SN76489 */,
|
||||||
|
4B47F3B42E7B9A14005D4DEC /* uPD7002 */,
|
||||||
);
|
);
|
||||||
name = Components;
|
name = Components;
|
||||||
path = ../../Components;
|
path = ../../Components;
|
||||||
@@ -6064,6 +6081,7 @@
|
|||||||
4BB505832B962DDF0031C43C /* Keyboard.cpp in Sources */,
|
4BB505832B962DDF0031C43C /* Keyboard.cpp in Sources */,
|
||||||
4BF0BC69297108D600CCA2B5 /* MemorySlotHandler.cpp in Sources */,
|
4BF0BC69297108D600CCA2B5 /* MemorySlotHandler.cpp in Sources */,
|
||||||
4B894521201967B4007DE474 /* StaticAnalyser.cpp in Sources */,
|
4B894521201967B4007DE474 /* StaticAnalyser.cpp in Sources */,
|
||||||
|
4B47F3B62E7BAB94005D4DEC /* uPD7002.cpp in Sources */,
|
||||||
4B8318B522D3E548006DB630 /* Macintosh.cpp in Sources */,
|
4B8318B522D3E548006DB630 /* Macintosh.cpp in Sources */,
|
||||||
4B8DF4FA254E36AE00F3433C /* Video.cpp in Sources */,
|
4B8DF4FA254E36AE00F3433C /* Video.cpp in Sources */,
|
||||||
4B7BA03123C2B19C00B98D9E /* Jasmin.cpp in Sources */,
|
4B7BA03123C2B19C00B98D9E /* Jasmin.cpp in Sources */,
|
||||||
@@ -6434,6 +6452,7 @@
|
|||||||
4B89452C201967B4007DE474 /* Tape.cpp in Sources */,
|
4B89452C201967B4007DE474 /* Tape.cpp in Sources */,
|
||||||
4B448E811F1C45A00009ABD6 /* TZX.cpp in Sources */,
|
4B448E811F1C45A00009ABD6 /* TZX.cpp in Sources */,
|
||||||
4BBFE83D21015D9C00BF1C40 /* CSJoystickManager.m in Sources */,
|
4BBFE83D21015D9C00BF1C40 /* CSJoystickManager.m in Sources */,
|
||||||
|
4B47F3B72E7BAB94005D4DEC /* uPD7002.cpp in Sources */,
|
||||||
4BEBFB512002DB30000708CC /* DiskROM.cpp in Sources */,
|
4BEBFB512002DB30000708CC /* DiskROM.cpp in Sources */,
|
||||||
4B89451C201967B4007DE474 /* Disk.cpp in Sources */,
|
4B89451C201967B4007DE474 /* Disk.cpp in Sources */,
|
||||||
4B302184208A550100773308 /* DiskII.cpp in Sources */,
|
4B302184208A550100773308 /* DiskII.cpp in Sources */,
|
||||||
@@ -6792,6 +6811,7 @@
|
|||||||
4BDA8235261E8E000021AA19 /* Z80ContentionTests.mm in Sources */,
|
4BDA8235261E8E000021AA19 /* Z80ContentionTests.mm in Sources */,
|
||||||
4B882F592C2F9C6A00D84031 /* CPCShakerTests.mm in Sources */,
|
4B882F592C2F9C6A00D84031 /* CPCShakerTests.mm in Sources */,
|
||||||
4B7752C328217F720073E2C5 /* Z80.cpp in Sources */,
|
4B7752C328217F720073E2C5 /* Z80.cpp in Sources */,
|
||||||
|
4B47F3B82E7BAB94005D4DEC /* uPD7002.cpp in Sources */,
|
||||||
4B06AACE2C645EEC0034D014 /* PCCompatible.cpp in Sources */,
|
4B06AACE2C645EEC0034D014 /* PCCompatible.cpp in Sources */,
|
||||||
4B06AB072C6461160034D014 /* StaticAnalyser.cpp in Sources */,
|
4B06AB072C6461160034D014 /* StaticAnalyser.cpp in Sources */,
|
||||||
4B778F1A23A5ED320000D260 /* Video.cpp in Sources */,
|
4B778F1A23A5ED320000D260 /* Video.cpp in Sources */,
|
||||||
|
@@ -67,7 +67,6 @@
|
|||||||
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
|
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
|
||||||
enableASanStackUseAfterReturn = "YES"
|
enableASanStackUseAfterReturn = "YES"
|
||||||
disableMainThreadChecker = "YES"
|
disableMainThreadChecker = "YES"
|
||||||
disablePerformanceAntipatternChecker = "YES"
|
|
||||||
launchStyle = "0"
|
launchStyle = "0"
|
||||||
useCustomWorkingDirectory = "NO"
|
useCustomWorkingDirectory = "NO"
|
||||||
ignoresPersistentStateOnLaunch = "NO"
|
ignoresPersistentStateOnLaunch = "NO"
|
||||||
@@ -75,7 +74,8 @@
|
|||||||
migratedStopOnEveryIssue = "YES"
|
migratedStopOnEveryIssue = "YES"
|
||||||
debugServiceExtension = "internal"
|
debugServiceExtension = "internal"
|
||||||
enableGPUValidationMode = "1"
|
enableGPUValidationMode = "1"
|
||||||
allowLocationSimulation = "NO">
|
allowLocationSimulation = "NO"
|
||||||
|
disablePerformanceAntipatternChecker = "YES">
|
||||||
<BuildableProductRunnable
|
<BuildableProductRunnable
|
||||||
runnableDebuggingMode = "0">
|
runnableDebuggingMode = "0">
|
||||||
<BuildableReference
|
<BuildableReference
|
||||||
|
@@ -164,14 +164,9 @@
|
|||||||
self = [super init];
|
self = [super init];
|
||||||
if(self) {
|
if(self) {
|
||||||
auto target = std::make_unique<Analyser::Static::Acorn::BBCMicroTarget>();
|
auto target = std::make_unique<Analyser::Static::Acorn::BBCMicroTarget>();
|
||||||
// TODO.
|
target->has_1770dfs = dfs;
|
||||||
(void)dfs;
|
target->has_adfs = adfs;
|
||||||
(void)adfs;
|
target->has_sideways_ram = sidewaysRAM;
|
||||||
(void)sidewaysRAM;
|
|
||||||
// target->has_dfs = dfs;
|
|
||||||
// target->has_pres_adfs = adfs;
|
|
||||||
// target->has_ap6_rom = ap6;
|
|
||||||
// target->has_sideways_ram = sidewaysRAM;
|
|
||||||
_targets.push_back(std::move(target));
|
_targets.push_back(std::move(target));
|
||||||
}
|
}
|
||||||
return self;
|
return self;
|
||||||
|
@@ -78,6 +78,7 @@ SOURCES += \
|
|||||||
$$SRC/Components/RP5C01/*.cpp \
|
$$SRC/Components/RP5C01/*.cpp \
|
||||||
$$SRC/Components/Serial/*.cpp \
|
$$SRC/Components/Serial/*.cpp \
|
||||||
$$SRC/Components/SN76489/*.cpp \
|
$$SRC/Components/SN76489/*.cpp \
|
||||||
|
$$SRC/Components/uPD7002/*.cpp \
|
||||||
\
|
\
|
||||||
$$SRC/Inputs/*.cpp \
|
$$SRC/Inputs/*.cpp \
|
||||||
\
|
\
|
||||||
@@ -213,6 +214,7 @@ HEADERS += \
|
|||||||
$$SRC/Components/RP5C01/*.hpp \
|
$$SRC/Components/RP5C01/*.hpp \
|
||||||
$$SRC/Components/Serial/*.hpp \
|
$$SRC/Components/Serial/*.hpp \
|
||||||
$$SRC/Components/SN76489/*.hpp \
|
$$SRC/Components/SN76489/*.hpp \
|
||||||
|
$$SRC/Components/uPD7002/*.hpp \
|
||||||
\
|
\
|
||||||
$$SRC/Concurrency/*.hpp \
|
$$SRC/Concurrency/*.hpp \
|
||||||
\
|
\
|
||||||
|
@@ -62,6 +62,7 @@ SOURCES += glob.glob('../../Components/OPx/*.cpp')
|
|||||||
SOURCES += glob.glob('../../Components/RP5C01/*.cpp')
|
SOURCES += glob.glob('../../Components/RP5C01/*.cpp')
|
||||||
SOURCES += glob.glob('../../Components/SN76489/*.cpp')
|
SOURCES += glob.glob('../../Components/SN76489/*.cpp')
|
||||||
SOURCES += glob.glob('../../Components/Serial/*.cpp')
|
SOURCES += glob.glob('../../Components/Serial/*.cpp')
|
||||||
|
SOURCES += glob.glob('../../Components/uPD7002/*.cpp')
|
||||||
|
|
||||||
SOURCES += glob.glob('../../Configurable/*.cpp')
|
SOURCES += glob.glob('../../Configurable/*.cpp')
|
||||||
|
|
||||||
|
@@ -194,13 +194,10 @@ void Processor<personality, T, uses_ready_line>::run_for(const Cycles cycles) {
|
|||||||
case OperationRSTPickVector: next_address_.full = 0xfffc; continue;
|
case OperationRSTPickVector: next_address_.full = 0xfffc; continue;
|
||||||
case CycleReadVectorLow: read_mem(pc_.halves.low, next_address_.full); break;
|
case CycleReadVectorLow: read_mem(pc_.halves.low, next_address_.full); break;
|
||||||
case CycleReadVectorHigh: read_mem(pc_.halves.high, next_address_.full+1); break;
|
case CycleReadVectorHigh: read_mem(pc_.halves.high, next_address_.full+1); break;
|
||||||
case OperationSetIRQFlags:
|
case OperationSetInterruptFlags:
|
||||||
flags_.inverse_interrupt = 0;
|
flags_.inverse_interrupt = 0;
|
||||||
if(is_65c02(personality)) flags_.decimal = 0;
|
if(is_65c02(personality)) flags_.decimal = 0;
|
||||||
continue;
|
continue;
|
||||||
case OperationSetNMIRSTFlags:
|
|
||||||
if(is_65c02(personality)) flags_.decimal = 0;
|
|
||||||
continue;
|
|
||||||
|
|
||||||
case CyclePullPCL: s_++; read_mem(pc_.halves.low, s_ | 0x100); break;
|
case CyclePullPCL: s_++; read_mem(pc_.halves.low, s_ | 0x100); break;
|
||||||
case CyclePullPCH: s_++; read_mem(pc_.halves.high, s_ | 0x100); break;
|
case CyclePullPCH: s_++; read_mem(pc_.halves.high, s_ | 0x100); break;
|
||||||
|
@@ -77,7 +77,7 @@ using namespace CPU::MOS6502;
|
|||||||
|
|
||||||
ProcessorStorage::ProcessorStorage(Personality personality) {
|
ProcessorStorage::ProcessorStorage(Personality personality) {
|
||||||
const InstructionList operations_6502[] = {
|
const InstructionList operations_6502[] = {
|
||||||
/* 0x00 BRK */ Program(CycleIncPCPushPCH, CyclePushPCL, OperationBRKPickVector, OperationSetOperandFromFlagsWithBRKSet, CyclePushOperand, OperationSetIRQFlags, CycleReadVectorLow, CycleReadVectorHigh),
|
/* 0x00 BRK */ Program(CycleIncPCPushPCH, CyclePushPCL, OperationBRKPickVector, OperationSetOperandFromFlagsWithBRKSet, CyclePushOperand, OperationSetInterruptFlags, CycleReadVectorLow, CycleReadVectorHigh),
|
||||||
/* 0x01 ORA x, ind */ IndexedIndirectRead(OperationORA),
|
/* 0x01 ORA x, ind */ IndexedIndirectRead(OperationORA),
|
||||||
/* 0x02 JAM */ JAM, /* 0x03 ASO x, ind */ IndexedIndirectReadModifyWrite(OperationASO),
|
/* 0x02 JAM */ JAM, /* 0x03 ASO x, ind */ IndexedIndirectReadModifyWrite(OperationASO),
|
||||||
/* 0x04 NOP zpg */ ZeroNop(), /* 0x05 ORA zpg */ ZeroRead(OperationORA),
|
/* 0x04 NOP zpg */ ZeroNop(), /* 0x05 ORA zpg */ ZeroRead(OperationORA),
|
||||||
@@ -228,7 +228,7 @@ ProcessorStorage::ProcessorStorage(Personality personality) {
|
|||||||
CycleNoWritePush,
|
CycleNoWritePush,
|
||||||
OperationRSTPickVector,
|
OperationRSTPickVector,
|
||||||
CycleNoWritePush,
|
CycleNoWritePush,
|
||||||
OperationSetNMIRSTFlags,
|
OperationSetInterruptFlags,
|
||||||
CycleReadVectorLow,
|
CycleReadVectorLow,
|
||||||
CycleReadVectorHigh
|
CycleReadVectorHigh
|
||||||
),
|
),
|
||||||
@@ -242,7 +242,7 @@ ProcessorStorage::ProcessorStorage(Personality personality) {
|
|||||||
OperationBRKPickVector,
|
OperationBRKPickVector,
|
||||||
OperationSetOperandFromFlags,
|
OperationSetOperandFromFlags,
|
||||||
CyclePushOperand,
|
CyclePushOperand,
|
||||||
OperationSetIRQFlags,
|
OperationSetInterruptFlags,
|
||||||
CycleReadVectorLow,
|
CycleReadVectorLow,
|
||||||
CycleReadVectorHigh
|
CycleReadVectorHigh
|
||||||
),
|
),
|
||||||
@@ -256,7 +256,7 @@ ProcessorStorage::ProcessorStorage(Personality personality) {
|
|||||||
OperationNMIPickVector,
|
OperationNMIPickVector,
|
||||||
OperationSetOperandFromFlags,
|
OperationSetOperandFromFlags,
|
||||||
CyclePushOperand,
|
CyclePushOperand,
|
||||||
OperationSetNMIRSTFlags,
|
OperationSetInterruptFlags,
|
||||||
CycleReadVectorLow,
|
CycleReadVectorLow,
|
||||||
CycleReadVectorHigh
|
CycleReadVectorHigh
|
||||||
),
|
),
|
||||||
|
@@ -35,8 +35,7 @@ class ProcessorStorage {
|
|||||||
CyclePushY, // pushes Y to the stack
|
CyclePushY, // pushes Y to the stack
|
||||||
CyclePushOperand, // pushes operand_ to the stack
|
CyclePushOperand, // pushes operand_ to the stack
|
||||||
|
|
||||||
OperationSetIRQFlags, // 6502: sets I; 65C02: sets I and resets D
|
OperationSetInterruptFlags, // 6502: sets I; 65C02: sets I and resets D
|
||||||
OperationSetNMIRSTFlags, // 6502: no-op. 65C02: resets D
|
|
||||||
|
|
||||||
OperationBRKPickVector, // 65C02: sets next_address_ to the BRK vector location; 6502: as 65C02 if no NMI is pending; otherwise sets next_address_ to the NMI address and resets the internal NMI-pending flag
|
OperationBRKPickVector, // 65C02: sets next_address_ to the BRK vector location; 6502: as 65C02 if no NMI is pending; otherwise sets next_address_ to the NMI address and resets the internal NMI-pending flag
|
||||||
OperationNMIPickVector, // sets next_address_ to the NMI vector
|
OperationNMIPickVector, // sets next_address_ to the NMI vector
|
||||||
|
@@ -3,3 +3,5 @@ ROM files would ordinarily go here; the copyright status of these is uncertain s
|
|||||||
Expected files:
|
Expected files:
|
||||||
|
|
||||||
os12.rom
|
os12.rom
|
||||||
|
dfs-2.26.rom
|
||||||
|
adfs-1.30.rom
|
||||||
|
@@ -60,6 +60,7 @@ set(CLK_SOURCES
|
|||||||
Components/RP5C01/RP5C01.cpp
|
Components/RP5C01/RP5C01.cpp
|
||||||
Components/SN76489/SN76489.cpp
|
Components/SN76489/SN76489.cpp
|
||||||
Components/Serial/Line.cpp
|
Components/Serial/Line.cpp
|
||||||
|
Components/uPD7002/uPD7002.cpp
|
||||||
|
|
||||||
Inputs/Keyboard.cpp
|
Inputs/Keyboard.cpp
|
||||||
|
|
||||||
|
Reference in New Issue
Block a user