mirror of
https://github.com/TomHarte/CLK.git
synced 2025-08-09 05:25:01 +00:00
Merge pull request #1238 from TomHarte/MissingIncludes
Add 6845/MDA cursor.
This commit is contained in:
@@ -50,9 +50,22 @@ enum Personality {
|
|||||||
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.
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// 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".
|
||||||
|
//
|
||||||
|
// This enum captures those specifics.
|
||||||
|
enum CursorType {
|
||||||
|
/// No cursor signal is generated.
|
||||||
|
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
|
// TODO UM6845R and R12/R13; see http://www.cpcwiki.eu/index.php/CRTC#CRTC_Differences
|
||||||
|
|
||||||
template <class T> class CRTC6845 {
|
template <class T, CursorType cursor_type> class CRTC6845 {
|
||||||
public:
|
public:
|
||||||
|
|
||||||
CRTC6845(Personality p, T &bus_handler) noexcept :
|
CRTC6845(Personality p, T &bus_handler) noexcept :
|
||||||
@@ -123,7 +136,7 @@ template <class T> class CRTC6845 {
|
|||||||
bus_state_.refresh_address = (bus_state_.refresh_address + 1) & 0x3fff;
|
bus_state_.refresh_address = (bus_state_.refresh_address + 1) & 0x3fff;
|
||||||
|
|
||||||
bus_state_.cursor = is_cursor_line_ &&
|
bus_state_.cursor = is_cursor_line_ &&
|
||||||
bus_state_.refresh_address == ((registers_[15] | (registers_[14] << 8))&0x3fff);
|
bus_state_.refresh_address == (registers_[15] | (registers_[14] << 8));
|
||||||
|
|
||||||
// Check for end-of-line.
|
// Check for end-of-line.
|
||||||
if(character_counter_ == registers_[0]) {
|
if(character_counter_ == registers_[0]) {
|
||||||
@@ -179,9 +192,11 @@ template <class T> class CRTC6845 {
|
|||||||
}
|
}
|
||||||
|
|
||||||
inline void do_end_of_line() {
|
inline void do_end_of_line() {
|
||||||
|
if constexpr (cursor_type != CursorType::None) {
|
||||||
// Check for cursor disable.
|
// Check for cursor disable.
|
||||||
// TODO: this is handled differently on the EGA, should I ever implement that.
|
// TODO: this is handled differently on the EGA, should I ever implement that.
|
||||||
is_cursor_line_ &= bus_state_.row_address != (registers_[11] & 0x1f);
|
is_cursor_line_ &= bus_state_.row_address != (registers_[11] & 0x1f);
|
||||||
|
}
|
||||||
|
|
||||||
// Check for end of vertical sync.
|
// Check for end of vertical sync.
|
||||||
if(bus_state_.vsync) {
|
if(bus_state_.vsync) {
|
||||||
@@ -242,8 +257,21 @@ template <class T> class CRTC6845 {
|
|||||||
character_counter_ = 0;
|
character_counter_ = 0;
|
||||||
character_is_visible_ = (registers_[1] != 0);
|
character_is_visible_ = (registers_[1] != 0);
|
||||||
|
|
||||||
|
if constexpr (cursor_type != CursorType::None) {
|
||||||
// Check for cursor enable.
|
// Check for cursor enable.
|
||||||
is_cursor_line_ |= bus_state_.row_address == (registers_[10] & 0x1f);
|
is_cursor_line_ |= bus_state_.row_address == (registers_[10] & 0x1f);
|
||||||
|
|
||||||
|
switch(cursor_type) {
|
||||||
|
// MDA-style blinking; timings are a bit of a guess for now.
|
||||||
|
case CursorType::MDA:
|
||||||
|
switch(registers_[10] >> 5) {
|
||||||
|
case 0b11: is_cursor_line_ &= (field_count_ & 15) < 5; break;
|
||||||
|
case 0b00: is_cursor_line_ &= bool(field_count_ & 16); break;
|
||||||
|
default: break;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
inline void do_end_of_frame() {
|
inline void do_end_of_frame() {
|
||||||
@@ -251,6 +279,9 @@ template <class T> class CRTC6845 {
|
|||||||
line_is_visible_ = true;
|
line_is_visible_ = true;
|
||||||
line_address_ = uint16_t((registers_[12] << 8) | registers_[13]);
|
line_address_ = uint16_t((registers_[12] << 8) | registers_[13]);
|
||||||
bus_state_.refresh_address = line_address_;
|
bus_state_.refresh_address = line_address_;
|
||||||
|
if constexpr (cursor_type != CursorType::None) {
|
||||||
|
++field_count_;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Personality personality_;
|
Personality personality_;
|
||||||
@@ -278,6 +309,8 @@ template <class T> class CRTC6845 {
|
|||||||
unsigned int character_is_visible_shifter_ = 0;
|
unsigned int character_is_visible_shifter_ = 0;
|
||||||
|
|
||||||
bool is_cursor_line_ = false;
|
bool is_cursor_line_ = false;
|
||||||
|
|
||||||
|
int field_count_ = 0;
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@@ -128,7 +128,7 @@ template <bool perform_automatically, bool start_immediately = true, typename Pe
|
|||||||
while(!should_quit_) {
|
while(!should_quit_) {
|
||||||
// Wait for new actions to be signalled, and grab them.
|
// Wait for new actions to be signalled, and grab them.
|
||||||
std::unique_lock lock(condition_mutex_);
|
std::unique_lock lock(condition_mutex_);
|
||||||
while(actions_.empty()) {
|
while(actions_.empty() && !should_quit_) {
|
||||||
condition_.wait(lock);
|
condition_.wait(lock);
|
||||||
}
|
}
|
||||||
std::swap(actions, actions_);
|
std::swap(actions, actions_);
|
||||||
|
@@ -585,6 +585,7 @@ class CRTCBusHandler {
|
|||||||
|
|
||||||
InterruptTimer &interrupt_timer_;
|
InterruptTimer &interrupt_timer_;
|
||||||
};
|
};
|
||||||
|
using CRTC = Motorola::CRTC::CRTC6845<CRTCBusHandler, Motorola::CRTC::CursorType::None>;
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
Holds and vends the current keyboard state, acting as the AY's port handler.
|
Holds and vends the current keyboard state, acting as the AY's port handler.
|
||||||
@@ -683,7 +684,7 @@ class i8255PortHandler : public Intel::i8255::PortHandler {
|
|||||||
public:
|
public:
|
||||||
i8255PortHandler(
|
i8255PortHandler(
|
||||||
KeyboardState &key_state,
|
KeyboardState &key_state,
|
||||||
const Motorola::CRTC::CRTC6845<CRTCBusHandler> &crtc,
|
const CRTC &crtc,
|
||||||
AYDeferrer &ay,
|
AYDeferrer &ay,
|
||||||
Storage::Tape::BinaryTapePlayer &tape_player) :
|
Storage::Tape::BinaryTapePlayer &tape_player) :
|
||||||
ay_(ay),
|
ay_(ay),
|
||||||
@@ -742,7 +743,7 @@ class i8255PortHandler : public Intel::i8255::PortHandler {
|
|||||||
|
|
||||||
private:
|
private:
|
||||||
AYDeferrer &ay_;
|
AYDeferrer &ay_;
|
||||||
const Motorola::CRTC::CRTC6845<CRTCBusHandler> &crtc_;
|
const CRTC &crtc_;
|
||||||
KeyboardState &key_state_;
|
KeyboardState &key_state_;
|
||||||
Storage::Tape::BinaryTapePlayer &tape_player_;
|
Storage::Tape::BinaryTapePlayer &tape_player_;
|
||||||
};
|
};
|
||||||
@@ -1219,7 +1220,7 @@ template <bool has_fdc> class ConcreteMachine:
|
|||||||
CPU::Z80::Processor<ConcreteMachine, false, true> z80_;
|
CPU::Z80::Processor<ConcreteMachine, false, true> z80_;
|
||||||
|
|
||||||
CRTCBusHandler crtc_bus_handler_;
|
CRTCBusHandler crtc_bus_handler_;
|
||||||
Motorola::CRTC::CRTC6845<CRTCBusHandler> crtc_;
|
CRTC crtc_;
|
||||||
|
|
||||||
AYDeferrer ay_;
|
AYDeferrer ay_;
|
||||||
i8255PortHandler i8255_port_handler_;
|
i8255PortHandler i8255_port_handler_;
|
||||||
|
@@ -625,7 +625,7 @@ class MDA {
|
|||||||
const uint8_t *ram = nullptr;
|
const uint8_t *ram = nullptr;
|
||||||
std::vector<uint8_t> font;
|
std::vector<uint8_t> font;
|
||||||
} outputter_;
|
} outputter_;
|
||||||
Motorola::CRTC::CRTC6845<CRTCOutputter> crtc_;
|
Motorola::CRTC::CRTC6845<CRTCOutputter, Motorola::CRTC::CursorType::MDA> crtc_;
|
||||||
|
|
||||||
int full_clock_;
|
int full_clock_;
|
||||||
};
|
};
|
||||||
@@ -885,14 +885,14 @@ class IO {
|
|||||||
printf("Unhandled in: %04x\n", port);
|
printf("Unhandled in: %04x\n", port);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 0x0000: return dma_.controller.read<0>();
|
case 0x0000: return dma_.controller.template read<0>();
|
||||||
case 0x0001: return dma_.controller.read<1>();
|
case 0x0001: return dma_.controller.template read<1>();
|
||||||
case 0x0002: return dma_.controller.read<2>();
|
case 0x0002: return dma_.controller.template read<2>();
|
||||||
case 0x0003: return dma_.controller.read<3>();
|
case 0x0003: return dma_.controller.template read<3>();
|
||||||
case 0x0004: return dma_.controller.read<4>();
|
case 0x0004: return dma_.controller.template read<4>();
|
||||||
case 0x0005: return dma_.controller.read<5>();
|
case 0x0005: return dma_.controller.template read<5>();
|
||||||
case 0x0006: return dma_.controller.read<6>();
|
case 0x0006: return dma_.controller.template read<6>();
|
||||||
case 0x0007: return dma_.controller.read<7>();
|
case 0x0007: return dma_.controller.template read<7>();
|
||||||
|
|
||||||
case 0x0008: return dma_.controller.status();
|
case 0x0008: return dma_.controller.status();
|
||||||
|
|
||||||
|
Reference in New Issue
Block a user