mirror of
https://github.com/TomHarte/CLK.git
synced 2025-02-16 18:30:32 +00:00
Merge pull request #183 from TomHarte/6845Address
Corrects 6845 address loading
This commit is contained in:
commit
f258d6fbb2
@ -31,9 +31,18 @@ class BusHandler {
|
|||||||
void perform_bus_cycle(const BusState &) {}
|
void perform_bus_cycle(const BusState &) {}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
enum Personality {
|
||||||
|
HD6845S, //
|
||||||
|
UM6845R, //
|
||||||
|
MC6845, //
|
||||||
|
AMS40226 //
|
||||||
|
};
|
||||||
|
|
||||||
template <class T> class CRTC6845 {
|
template <class T> class CRTC6845 {
|
||||||
public:
|
public:
|
||||||
CRTC6845(T &bus_handler) : bus_handler_(bus_handler) {}
|
|
||||||
|
CRTC6845(Personality p, T &bus_handler) :
|
||||||
|
personality_(p), bus_handler_(bus_handler) {}
|
||||||
|
|
||||||
void run_for(Cycles cycles) {
|
void run_for(Cycles cycles) {
|
||||||
int cyles_remaining = cycles.as_int();
|
int cyles_remaining = cycles.as_int();
|
||||||
@ -58,22 +67,18 @@ template <class T> class CRTC6845 {
|
|||||||
if(hsync_down_counter_) bus_state_.hsync = true;
|
if(hsync_down_counter_) bus_state_.hsync = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// update refresh address
|
||||||
|
if(character_is_visible_) {
|
||||||
|
bus_state_.refresh_address++;
|
||||||
|
}
|
||||||
|
|
||||||
// check for end of visible characters
|
// check for end of visible characters
|
||||||
if(character_counter_ == registers_[1]) {
|
if(character_counter_ == registers_[1]) {
|
||||||
bus_state_.refresh_address++;
|
|
||||||
character_is_visible_ = false;
|
character_is_visible_ = false;
|
||||||
} else {
|
|
||||||
// update refresh address
|
|
||||||
if(character_is_visible_) {
|
|
||||||
bus_state_.refresh_address++;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// check for end-of-line
|
// check for end-of-line
|
||||||
if(is_end_of_line) {
|
if(is_end_of_line) {
|
||||||
character_counter_ = 0;
|
|
||||||
character_is_visible_ = true;
|
|
||||||
|
|
||||||
// check for end of vertical sync
|
// check for end of vertical sync
|
||||||
if(vsync_down_counter_) {
|
if(vsync_down_counter_) {
|
||||||
vsync_down_counter_--;
|
vsync_down_counter_--;
|
||||||
@ -94,7 +99,8 @@ template <class T> class CRTC6845 {
|
|||||||
} else {
|
} else {
|
||||||
// advance vertical counter
|
// advance vertical counter
|
||||||
if(bus_state_.row_address == registers_[9]) {
|
if(bus_state_.row_address == registers_[9]) {
|
||||||
line_address_ = bus_state_.refresh_address;
|
if(!character_is_visible_)
|
||||||
|
line_address_ = bus_state_.refresh_address;
|
||||||
bus_state_.row_address = 0;
|
bus_state_.row_address = 0;
|
||||||
|
|
||||||
bool is_at_end_of_frame = line_counter_ == registers_[4];
|
bool is_at_end_of_frame = line_counter_ == registers_[4];
|
||||||
@ -126,9 +132,12 @@ template <class T> class CRTC6845 {
|
|||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
bus_state_.row_address++;
|
bus_state_.row_address++;
|
||||||
bus_state_.refresh_address = line_address_;
|
|
||||||
}
|
}
|
||||||
|
bus_state_.refresh_address = line_address_;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
character_counter_ = 0;
|
||||||
|
character_is_visible_ = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bus_state_.display_enable = character_is_visible_ && line_is_visible_;
|
bus_state_.display_enable = character_is_visible_ && line_is_visible_;
|
||||||
@ -137,7 +146,7 @@ template <class T> class CRTC6845 {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void select_register(uint8_t r) {
|
void select_register(uint8_t r) {
|
||||||
selected_register_ = (int)r & 15;
|
selected_register_ = r;
|
||||||
}
|
}
|
||||||
|
|
||||||
uint8_t get_status() {
|
uint8_t get_status() {
|
||||||
@ -145,18 +154,26 @@ template <class T> class CRTC6845 {
|
|||||||
}
|
}
|
||||||
|
|
||||||
uint8_t get_register() {
|
uint8_t get_register() {
|
||||||
|
if(selected_register_ < 12 || selected_register_ > 17) return 0xff;
|
||||||
return registers_[selected_register_];
|
return registers_[selected_register_];
|
||||||
}
|
}
|
||||||
|
|
||||||
void set_register(uint8_t value) {
|
void set_register(uint8_t value) {
|
||||||
registers_[selected_register_] = value;
|
static uint8_t masks[] = {
|
||||||
|
0xff, 0xff, 0xff, 0xff, 0x7f, 0x1f, 0x7f, 0x7f,
|
||||||
|
0xff, 0x1f, 0x7f, 0x1f, 0x3f, 0xff, 0x3f, 0xff
|
||||||
|
};
|
||||||
|
|
||||||
|
if(selected_register_ < 16)
|
||||||
|
registers_[selected_register_] = value & masks[selected_register_];
|
||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
Personality personality_;
|
||||||
T &bus_handler_;
|
T &bus_handler_;
|
||||||
BusState bus_state_;
|
BusState bus_state_;
|
||||||
|
|
||||||
uint8_t registers_[16];
|
uint8_t registers_[18];
|
||||||
int selected_register_;
|
int selected_register_;
|
||||||
|
|
||||||
uint8_t character_counter_;
|
uint8_t character_counter_;
|
||||||
|
@ -528,7 +528,7 @@ class ConcreteMachine:
|
|||||||
ConcreteMachine() :
|
ConcreteMachine() :
|
||||||
z80_(*this),
|
z80_(*this),
|
||||||
crtc_counter_(HalfCycles(4)), // This starts the CRTC exactly out of phase with the memory accesses
|
crtc_counter_(HalfCycles(4)), // This starts the CRTC exactly out of phase with the memory accesses
|
||||||
crtc_(crtc_bus_handler_),
|
crtc_(Motorola::CRTC::HD6845S, crtc_bus_handler_),
|
||||||
crtc_bus_handler_(ram_, interrupt_timer_),
|
crtc_bus_handler_(ram_, interrupt_timer_),
|
||||||
i8255_(i8255_port_handler_),
|
i8255_(i8255_port_handler_),
|
||||||
i8255_port_handler_(key_state_, crtc_bus_handler_, ay_, tape_player_),
|
i8255_port_handler_(key_state_, crtc_bus_handler_, ay_, tape_player_),
|
||||||
@ -579,48 +579,7 @@ class ConcreteMachine:
|
|||||||
case CPU::Z80::PartialMachineCycle::Output:
|
case CPU::Z80::PartialMachineCycle::Output:
|
||||||
// Check for a gate array access.
|
// Check for a gate array access.
|
||||||
if((address & 0xc000) == 0x4000) {
|
if((address & 0xc000) == 0x4000) {
|
||||||
switch(*cycle.value >> 6) {
|
write_to_gate_array(*cycle.value);
|
||||||
case 0: crtc_bus_handler_.select_pen(*cycle.value & 0x1f); break;
|
|
||||||
case 1: crtc_bus_handler_.set_colour(*cycle.value & 0x1f); break;
|
|
||||||
case 2:
|
|
||||||
// Perform ROM paging.
|
|
||||||
read_pointers_[0] = (*cycle.value & 4) ? write_pointers_[0] : roms_[rom_model_].data();
|
|
||||||
|
|
||||||
upper_rom_is_paged_ = !(*cycle.value & 8);
|
|
||||||
read_pointers_[3] = upper_rom_is_paged_ ? roms_[upper_rom_].data() : write_pointers_[3];
|
|
||||||
|
|
||||||
// Reset the interrupt timer if requested.
|
|
||||||
if(*cycle.value & 0x10) interrupt_timer_.reset_count();
|
|
||||||
|
|
||||||
// Post the next mode.
|
|
||||||
crtc_bus_handler_.set_next_mode(*cycle.value & 3);
|
|
||||||
break;
|
|
||||||
case 3:
|
|
||||||
// Perform RAM paging, if 128kb is permitted.
|
|
||||||
if(has_128k_) {
|
|
||||||
bool adjust_low_read_pointer = read_pointers_[0] == write_pointers_[0];
|
|
||||||
bool adjust_high_read_pointer = read_pointers_[3] == write_pointers_[3];
|
|
||||||
#define RAM_BANK(x) &ram_[x * 16384]
|
|
||||||
#define RAM_CONFIG(a, b, c, d) write_pointers_[0] = RAM_BANK(a); write_pointers_[1] = RAM_BANK(b); write_pointers_[2] = RAM_BANK(c); write_pointers_[3] = RAM_BANK(d);
|
|
||||||
switch(*cycle.value & 7) {
|
|
||||||
case 0: RAM_CONFIG(0, 1, 2, 3); break;
|
|
||||||
case 1: RAM_CONFIG(0, 1, 2, 7); break;
|
|
||||||
case 2: RAM_CONFIG(4, 5, 6, 7); break;
|
|
||||||
case 3: RAM_CONFIG(0, 3, 2, 7); break;
|
|
||||||
case 4: RAM_CONFIG(0, 4, 2, 3); break;
|
|
||||||
case 5: RAM_CONFIG(0, 5, 2, 3); break;
|
|
||||||
case 6: RAM_CONFIG(0, 6, 2, 3); break;
|
|
||||||
case 7: RAM_CONFIG(0, 7, 2, 3); break;
|
|
||||||
}
|
|
||||||
#undef RAM_CONFIG
|
|
||||||
#undef RAM_BANK
|
|
||||||
if(adjust_low_read_pointer) read_pointers_[0] = write_pointers_[0];
|
|
||||||
read_pointers_[1] = write_pointers_[1];
|
|
||||||
read_pointers_[2] = write_pointers_[2];
|
|
||||||
if(adjust_high_read_pointer) read_pointers_[3] = write_pointers_[3];
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check for an upper ROM selection
|
// Check for an upper ROM selection
|
||||||
@ -634,7 +593,7 @@ class ConcreteMachine:
|
|||||||
switch((address >> 8) & 3) {
|
switch((address >> 8) & 3) {
|
||||||
case 0: crtc_.select_register(*cycle.value); break;
|
case 0: crtc_.select_register(*cycle.value); break;
|
||||||
case 1: crtc_.set_register(*cycle.value); break;
|
case 1: crtc_.set_register(*cycle.value); break;
|
||||||
case 2: case 3: printf("Illegal CRTC write?\n"); break;
|
default: break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -657,23 +616,31 @@ class ConcreteMachine:
|
|||||||
// Default to nothing answering
|
// Default to nothing answering
|
||||||
*cycle.value = 0xff;
|
*cycle.value = 0xff;
|
||||||
|
|
||||||
// Check for a CRTC access
|
|
||||||
if(!(address & 0x4000)) {
|
|
||||||
switch((address >> 8) & 3) {
|
|
||||||
case 0: case 1: printf("Illegal CRTC read?\n"); break;
|
|
||||||
case 2: *cycle.value = crtc_.get_status(); break;
|
|
||||||
case 3: *cycle.value = crtc_.get_register(); break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check for a PIO access
|
// Check for a PIO access
|
||||||
if(!(address & 0x800)) {
|
if(!(address & 0x800)) {
|
||||||
*cycle.value = i8255_.get_register((address >> 8) & 3);
|
*cycle.value &= i8255_.get_register((address >> 8) & 3);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check for an FDC access
|
// Check for an FDC access
|
||||||
if(has_fdc_ && (address & 0x580) == 0x100) {
|
if(has_fdc_ && (address & 0x580) == 0x100) {
|
||||||
*cycle.value = fdc_.get_register(address & 1);
|
*cycle.value &= fdc_.get_register(address & 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check for a CRTC access; the below is not a typo — the CRTC can be selected
|
||||||
|
// for writing via an input, and will sample whatever happens to be available
|
||||||
|
if(!(address & 0x4000)) {
|
||||||
|
switch((address >> 8) & 3) {
|
||||||
|
case 0: crtc_.select_register(*cycle.value); break;
|
||||||
|
case 1: crtc_.set_register(*cycle.value); break;
|
||||||
|
case 2: *cycle.value &= crtc_.get_status(); break;
|
||||||
|
case 3: *cycle.value &= crtc_.get_register(); break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// As with the CRTC, the gate array will sample the bus if the address decoding
|
||||||
|
// implies that it should, unaware of data direction
|
||||||
|
if((address & 0xc000) == 0x4000) {
|
||||||
|
write_to_gate_array(*cycle.value);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
@ -794,6 +761,51 @@ class ConcreteMachine:
|
|||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
inline void write_to_gate_array(uint8_t value) {
|
||||||
|
switch(value >> 6) {
|
||||||
|
case 0: crtc_bus_handler_.select_pen(value & 0x1f); break;
|
||||||
|
case 1: crtc_bus_handler_.set_colour(value & 0x1f); break;
|
||||||
|
case 2:
|
||||||
|
// Perform ROM paging.
|
||||||
|
read_pointers_[0] = (value & 4) ? write_pointers_[0] : roms_[rom_model_].data();
|
||||||
|
|
||||||
|
upper_rom_is_paged_ = !(value & 8);
|
||||||
|
read_pointers_[3] = upper_rom_is_paged_ ? roms_[upper_rom_].data() : write_pointers_[3];
|
||||||
|
|
||||||
|
// Reset the interrupt timer if requested.
|
||||||
|
if(value & 0x10) interrupt_timer_.reset_count();
|
||||||
|
|
||||||
|
// Post the next mode.
|
||||||
|
crtc_bus_handler_.set_next_mode(value & 3);
|
||||||
|
break;
|
||||||
|
case 3:
|
||||||
|
// Perform RAM paging, if 128kb is permitted.
|
||||||
|
if(has_128k_) {
|
||||||
|
bool adjust_low_read_pointer = read_pointers_[0] == write_pointers_[0];
|
||||||
|
bool adjust_high_read_pointer = read_pointers_[3] == write_pointers_[3];
|
||||||
|
#define RAM_BANK(x) &ram_[x * 16384]
|
||||||
|
#define RAM_CONFIG(a, b, c, d) write_pointers_[0] = RAM_BANK(a); write_pointers_[1] = RAM_BANK(b); write_pointers_[2] = RAM_BANK(c); write_pointers_[3] = RAM_BANK(d);
|
||||||
|
switch(value & 7) {
|
||||||
|
case 0: RAM_CONFIG(0, 1, 2, 3); break;
|
||||||
|
case 1: RAM_CONFIG(0, 1, 2, 7); break;
|
||||||
|
case 2: RAM_CONFIG(4, 5, 6, 7); break;
|
||||||
|
case 3: RAM_CONFIG(0, 3, 2, 7); break;
|
||||||
|
case 4: RAM_CONFIG(0, 4, 2, 3); break;
|
||||||
|
case 5: RAM_CONFIG(0, 5, 2, 3); break;
|
||||||
|
case 6: RAM_CONFIG(0, 6, 2, 3); break;
|
||||||
|
case 7: RAM_CONFIG(0, 7, 2, 3); break;
|
||||||
|
}
|
||||||
|
#undef RAM_CONFIG
|
||||||
|
#undef RAM_BANK
|
||||||
|
if(adjust_low_read_pointer) read_pointers_[0] = write_pointers_[0];
|
||||||
|
read_pointers_[1] = write_pointers_[1];
|
||||||
|
read_pointers_[2] = write_pointers_[2];
|
||||||
|
if(adjust_high_read_pointer) read_pointers_[3] = write_pointers_[3];
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
CPU::Z80::Processor<ConcreteMachine> z80_;
|
CPU::Z80::Processor<ConcreteMachine> z80_;
|
||||||
|
|
||||||
CRTCBusHandler crtc_bus_handler_;
|
CRTCBusHandler crtc_bus_handler_;
|
||||||
|
Loading…
x
Reference in New Issue
Block a user