1
0
mirror of https://github.com/TomHarte/CLK.git synced 2024-11-29 12:50:28 +00:00

Loosen the assumption of MDA.

This commit is contained in:
Thomas Harte 2023-12-05 16:38:09 -05:00
parent 6659a1dec5
commit bdf9c4765b
3 changed files with 304 additions and 266 deletions

View File

@ -0,0 +1,216 @@
//
// MDA.hpp
// Clock Signal
//
// Created by Thomas Harte on 05/12/2023.
// Copyright © 2023 Thomas Harte. All rights reserved.
//
#ifndef MDA_h
#define MDA_h
#include "../../Components/6845/CRTC6845.hpp"
#include "../../Outputs/CRT/CRT.hpp"
namespace PCCompatible {
class MDA {
public:
MDA() : crtc_(Motorola::CRTC::Personality::HD6845S, outputter_) {}
void set_source(const uint8_t *ram, std::vector<uint8_t> font) {
outputter_.ram = ram;
outputter_.font = font;
}
void run_for(Cycles cycles) {
// I _think_ the MDA's CRTC is clocked at 14/9ths the PIT clock.
// Do that conversion here.
full_clock_ += 14 * cycles.as<int>();
crtc_.run_for(Cycles(full_clock_ / 9));
full_clock_ %= 9;
}
template <int address>
void write(uint8_t value) {
if constexpr (address & 0x8) {
outputter_.set_control(value);
} else {
if constexpr (address & 0x1) {
crtc_.set_register(value);
} else {
crtc_.select_register(value);
}
}
}
template <int address>
uint8_t read() {
if constexpr (address & 0x8) {
return outputter_.control();
} else {
return crtc_.get_register();
}
}
// MARK: - Call-ins for ScanProducer.
void set_scan_target(Outputs::Display::ScanTarget *scan_target) {
outputter_.crt.set_scan_target(scan_target);
}
Outputs::Display::ScanStatus get_scaled_scan_status() const {
return outputter_.crt.get_scaled_scan_status() * 9.0f / 14.0f;
}
private:
struct CRTCOutputter {
CRTCOutputter() :
crt(882, 9, 382, 3, Outputs::Display::InputDataType::Red2Green2Blue2)
// TODO: really this should be a Luminance8 and set an appropriate modal tint colour;
// consider whether that's worth building into the scan target.
{
// crt.set_visible_area(Outputs::Display::Rect(0.1072f, 0.1f, 0.842105263157895f, 0.842105263157895f));
crt.set_display_type(Outputs::Display::DisplayType::RGB);
}
void set_control(uint8_t control) {
// b0: 'high resolution' (probably breaks if not 1)
// b3: video enable
// b5: enable blink
control_ = control;
}
uint8_t control() {
return control_;
}
void perform_bus_cycle_phase1(const Motorola::CRTC::BusState &state) {
// Determine new output state.
const OutputState new_state =
(state.hsync | state.vsync) ? OutputState::Sync :
((state.display_enable && control_&0x08) ? OutputState::Pixels : OutputState::Border);
// Upon either a state change or just having accumulated too much local time...
if(new_state != output_state || count > 882) {
// (1) flush preexisting state.
if(count) {
switch(output_state) {
case OutputState::Sync: crt.output_sync(count); break;
case OutputState::Border: crt.output_blank(count); break;
case OutputState::Pixels:
crt.output_data(count);
pixels = pixel_pointer = nullptr;
break;
}
}
// (2) adopt new state.
output_state = new_state;
count = 0;
}
// Collect pixels if applicable.
if(output_state == OutputState::Pixels) {
if(!pixels) {
pixel_pointer = pixels = crt.begin_data(DefaultAllocationSize);
// Flush any period where pixels weren't recorded due to back pressure.
if(pixels && count) {
crt.output_blank(count);
count = 0;
}
}
if(pixels) {
static constexpr uint8_t high_intensity = 0x0d;
static constexpr uint8_t low_intensity = 0x09;
static constexpr uint8_t off = 0x00;
if(state.cursor) {
pixel_pointer[0] = pixel_pointer[1] = pixel_pointer[2] = pixel_pointer[3] =
pixel_pointer[4] = pixel_pointer[5] = pixel_pointer[6] = pixel_pointer[7] =
pixel_pointer[8] = low_intensity;
} else {
const uint8_t attributes = ram[((state.refresh_address << 1) + 1) & 0xfff];
const uint8_t glyph = ram[((state.refresh_address << 1) + 0) & 0xfff];
uint8_t row = font[(glyph * 14) + state.row_address];
const uint8_t intensity = (attributes & 0x08) ? high_intensity : low_intensity;
uint8_t blank = off;
// Handle irregular attributes.
// Cf. http://www.seasip.info/VintagePC/mda.html#memmap
switch(attributes) {
case 0x00: case 0x08: case 0x80: case 0x88:
row = 0;
break;
case 0x70: case 0x78: case 0xf0: case 0xf8:
row ^= 0xff;
blank = intensity;
break;
}
// Apply blink if enabled.
if((control_ & 0x20) && (attributes & 0x80) && (state.field_count & 16)) {
row ^= 0xff;
blank = (blank == off) ? intensity : off;
}
if(((attributes & 7) == 1) && state.row_address == 13) {
// Draw as underline.
std::fill(pixel_pointer, pixel_pointer + 9, intensity);
} else {
// Draw according to ROM contents, possibly duplicating final column.
pixel_pointer[0] = (row & 0x80) ? intensity : off;
pixel_pointer[1] = (row & 0x40) ? intensity : off;
pixel_pointer[2] = (row & 0x20) ? intensity : off;
pixel_pointer[3] = (row & 0x10) ? intensity : off;
pixel_pointer[4] = (row & 0x08) ? intensity : off;
pixel_pointer[5] = (row & 0x04) ? intensity : off;
pixel_pointer[6] = (row & 0x02) ? intensity : off;
pixel_pointer[7] = (row & 0x01) ? intensity : off;
pixel_pointer[8] = (glyph >= 0xc0 && glyph < 0xe0) ? pixel_pointer[7] : blank;
}
}
pixel_pointer += 9;
}
}
// Advance.
count += 9;
// Output pixel row prematurely if storage is exhausted.
if(output_state == OutputState::Pixels && pixel_pointer == pixels + DefaultAllocationSize) {
crt.output_data(count);
count = 0;
pixels = pixel_pointer = nullptr;
}
}
void perform_bus_cycle_phase2(const Motorola::CRTC::BusState &) {}
Outputs::CRT::CRT crt;
enum class OutputState {
Sync, Pixels, Border
} output_state = OutputState::Sync;
int count = 0;
uint8_t *pixels = nullptr;
uint8_t *pixel_pointer = nullptr;
static constexpr size_t DefaultAllocationSize = 720;
const uint8_t *ram = nullptr;
std::vector<uint8_t> font;
uint8_t control_ = 0;
} outputter_;
Motorola::CRTC::CRTC6845<CRTCOutputter, Motorola::CRTC::CursorType::MDA> crtc_;
int full_clock_;
};
}
#endif /* MDA_h */

View File

@ -13,13 +13,13 @@
#include "PIT.hpp" #include "PIT.hpp"
#include "DMA.hpp" #include "DMA.hpp"
#include "Memory.hpp" #include "Memory.hpp"
#include "MDA.hpp"
#include "../../InstructionSets/x86/Decoder.hpp" #include "../../InstructionSets/x86/Decoder.hpp"
#include "../../InstructionSets/x86/Flags.hpp" #include "../../InstructionSets/x86/Flags.hpp"
#include "../../InstructionSets/x86/Instruction.hpp" #include "../../InstructionSets/x86/Instruction.hpp"
#include "../../InstructionSets/x86/Perform.hpp" #include "../../InstructionSets/x86/Perform.hpp"
#include "../../Components/6845/CRTC6845.hpp"
#include "../../Components/8255/i8255.hpp" #include "../../Components/8255/i8255.hpp"
#include "../../Components/8272/CommandDecoder.hpp" #include "../../Components/8272/CommandDecoder.hpp"
#include "../../Components/8272/Results.hpp" #include "../../Components/8272/Results.hpp"
@ -50,22 +50,24 @@ namespace PCCompatible {
using VideoAdaptor = Analyser::Static::PCCompatible::Target::VideoAdaptor; using VideoAdaptor = Analyser::Static::PCCompatible::Target::VideoAdaptor;
//bool log = false; template <VideoAdaptor adaptor> struct Adaptor;
//std::string previous; template <> struct Adaptor<VideoAdaptor::MDA> {
using type = MDA;
};
template <> struct Adaptor<VideoAdaptor::CGA> {
using type = MDA;
};
class FloppyController { class FloppyController {
public: public:
FloppyController(PIC &pic, DMA &dma) : pic_(pic), dma_(dma) { FloppyController(PIC &pic, DMA &dma, int drive_count) : pic_(pic), dma_(dma) {
// Default: one floppy drive only. // Default: one floppy drive only.
drives_[0].exists = true; for(int c = 0; c < 4; c++) {
drives_[1].exists = false; drives_[0].exists = drive_count >= c;
drives_[2].exists = false; }
drives_[3].exists = false;
} }
void set_digital_output(uint8_t control) { void set_digital_output(uint8_t control) {
// printf("FDC DOR: %02x\n", control);
// b7, b6, b5, b4: enable motor for drive 4, 3, 2, 1; // b7, b6, b5, b4: enable motor for drive 4, 3, 2, 1;
// b3: 1 => enable DMA; 0 => disable; // b3: 1 => enable DMA; 0 => disable;
// b2: 1 => enable FDC; 0 => hold at reset; // b2: 1 => enable FDC; 0 => hold at reset;
@ -96,7 +98,6 @@ class FloppyController {
} }
uint8_t status() const { uint8_t status() const {
// printf("FDC: read status %02x\n", status_.main());
return status_.main(); return status_.main();
} }
@ -187,7 +188,6 @@ class FloppyController {
} break; } break;
case Command::Recalibrate: case Command::Recalibrate:
printf("FDC: Recalibrate\n");
drives_[decoder_.target().drive].track = 0; drives_[decoder_.target().drive].track = 0;
drives_[decoder_.target().drive].raised_interrupt = true; drives_[decoder_.target().drive].raised_interrupt = true;
@ -195,7 +195,6 @@ class FloppyController {
pic_.apply_edge<6>(true); pic_.apply_edge<6>(true);
break; break;
case Command::Seek: case Command::Seek:
printf("FDC: Seek %d:%d to %d\n", decoder_.target().drive, decoder_.target().head, decoder_.seek_target());
drives_[decoder_.target().drive].track = decoder_.seek_target(); drives_[decoder_.target().drive].track = decoder_.seek_target();
drives_[decoder_.target().drive].raised_interrupt = true; drives_[decoder_.target().drive].raised_interrupt = true;
@ -204,8 +203,6 @@ class FloppyController {
break; break;
case Command::SenseInterruptStatus: { case Command::SenseInterruptStatus: {
printf("FDC: SenseInterruptStatus\n");
int c = 0; int c = 0;
for(; c < 4; c++) { for(; c < 4; c++) {
if(drives_[c].raised_interrupt) { if(drives_[c].raised_interrupt) {
@ -224,14 +221,12 @@ class FloppyController {
} }
} break; } break;
case Command::Specify: case Command::Specify:
printf("FDC: Specify\n");
specify_specs_ = decoder_.specify_specs(); specify_specs_ = decoder_.specify_specs();
break; break;
// case Command::SenseDriveStatus: { // case Command::SenseDriveStatus: {
// } break; // } break;
case Command::Invalid: case Command::Invalid:
printf("FDC: Invalid\n");
results_.serialise_none(); results_.serialise_none();
break; break;
} }
@ -256,11 +251,9 @@ class FloppyController {
status_.set(MainStatus::DataIsToProcessor, false); status_.set(MainStatus::DataIsToProcessor, false);
status_.set(MainStatus::CommandInProgress, false); status_.set(MainStatus::CommandInProgress, false);
} }
// printf("FDC read: %02x\n", result);
return result; return result;
} }
printf("FDC read?\n");
return 0x80; return 0x80;
} }
@ -463,203 +456,6 @@ class KeyboardController {
int reset_delay_ = 0; int reset_delay_ = 0;
}; };
class MDA {
public:
MDA() : crtc_(Motorola::CRTC::Personality::HD6845S, outputter_) {}
void set_source(const uint8_t *ram, std::vector<uint8_t> font) {
outputter_.ram = ram;
outputter_.font = font;
}
void run_for(Cycles cycles) {
// I _think_ the MDA's CRTC is clocked at 14/9ths the PIT clock.
// Do that conversion here.
full_clock_ += 14 * cycles.as<int>();
crtc_.run_for(Cycles(full_clock_ / 9));
full_clock_ %= 9;
}
template <int address>
void write(uint8_t value) {
if constexpr (address & 0x8) {
outputter_.set_control(value);
} else {
if constexpr (address & 0x1) {
crtc_.set_register(value);
} else {
crtc_.select_register(value);
}
}
}
template <int address>
uint8_t read() {
if constexpr (address & 0x8) {
return outputter_.control();
} else {
return crtc_.get_register();
}
}
// MARK: - Call-ins for ScanProducer.
void set_scan_target(Outputs::Display::ScanTarget *scan_target) {
outputter_.crt.set_scan_target(scan_target);
}
Outputs::Display::ScanStatus get_scaled_scan_status() const {
return outputter_.crt.get_scaled_scan_status() * 9.0f / 14.0f;
}
private:
struct CRTCOutputter {
CRTCOutputter() :
crt(882, 9, 382, 3, Outputs::Display::InputDataType::Red2Green2Blue2)
// TODO: really this should be a Luminance8 and set an appropriate modal tint colour;
// consider whether that's worth building into the scan target.
{
// crt.set_visible_area(Outputs::Display::Rect(0.1072f, 0.1f, 0.842105263157895f, 0.842105263157895f));
crt.set_display_type(Outputs::Display::DisplayType::RGB);
}
void set_control(uint8_t control) {
// b0: 'high resolution' (probably breaks if not 1)
// b3: video enable
// b5: enable blink
control_ = control;
}
uint8_t control() {
return control_;
}
void perform_bus_cycle_phase1(const Motorola::CRTC::BusState &state) {
// Determine new output state.
const OutputState new_state =
(state.hsync | state.vsync) ? OutputState::Sync :
((state.display_enable && control_&0x08) ? OutputState::Pixels : OutputState::Border);
// Upon either a state change or just having accumulated too much local time...
if(new_state != output_state || count > 882) {
// (1) flush preexisting state.
if(count) {
switch(output_state) {
case OutputState::Sync: crt.output_sync(count); break;
case OutputState::Border: crt.output_blank(count); break;
case OutputState::Pixels:
crt.output_data(count);
pixels = pixel_pointer = nullptr;
break;
}
}
// (2) adopt new state.
output_state = new_state;
count = 0;
}
// Collect pixels if applicable.
if(output_state == OutputState::Pixels) {
if(!pixels) {
pixel_pointer = pixels = crt.begin_data(DefaultAllocationSize);
// Flush any period where pixels weren't recorded due to back pressure.
if(pixels && count) {
crt.output_blank(count);
count = 0;
}
}
if(pixels) {
static constexpr uint8_t high_intensity = 0x0d;
static constexpr uint8_t low_intensity = 0x09;
static constexpr uint8_t off = 0x00;
if(state.cursor) {
pixel_pointer[0] = pixel_pointer[1] = pixel_pointer[2] = pixel_pointer[3] =
pixel_pointer[4] = pixel_pointer[5] = pixel_pointer[6] = pixel_pointer[7] =
pixel_pointer[8] = low_intensity;
} else {
const uint8_t attributes = ram[((state.refresh_address << 1) + 1) & 0xfff];
const uint8_t glyph = ram[((state.refresh_address << 1) + 0) & 0xfff];
uint8_t row = font[(glyph * 14) + state.row_address];
const uint8_t intensity = (attributes & 0x08) ? high_intensity : low_intensity;
uint8_t blank = off;
// Handle irregular attributes.
// Cf. http://www.seasip.info/VintagePC/mda.html#memmap
switch(attributes) {
case 0x00: case 0x08: case 0x80: case 0x88:
row = 0;
break;
case 0x70: case 0x78: case 0xf0: case 0xf8:
row ^= 0xff;
blank = intensity;
break;
}
// Apply blink if enabled.
if((control_ & 0x20) && (attributes & 0x80) && (state.field_count & 16)) {
row ^= 0xff;
blank = (blank == off) ? intensity : off;
}
if(((attributes & 7) == 1) && state.row_address == 13) {
// Draw as underline.
std::fill(pixel_pointer, pixel_pointer + 9, intensity);
} else {
// Draw according to ROM contents, possibly duplicating final column.
pixel_pointer[0] = (row & 0x80) ? intensity : off;
pixel_pointer[1] = (row & 0x40) ? intensity : off;
pixel_pointer[2] = (row & 0x20) ? intensity : off;
pixel_pointer[3] = (row & 0x10) ? intensity : off;
pixel_pointer[4] = (row & 0x08) ? intensity : off;
pixel_pointer[5] = (row & 0x04) ? intensity : off;
pixel_pointer[6] = (row & 0x02) ? intensity : off;
pixel_pointer[7] = (row & 0x01) ? intensity : off;
pixel_pointer[8] = (glyph >= 0xc0 && glyph < 0xe0) ? pixel_pointer[7] : blank;
}
}
pixel_pointer += 9;
}
}
// Advance.
count += 9;
// Output pixel row prematurely if storage is exhausted.
if(output_state == OutputState::Pixels && pixel_pointer == pixels + DefaultAllocationSize) {
crt.output_data(count);
count = 0;
pixels = pixel_pointer = nullptr;
}
}
void perform_bus_cycle_phase2(const Motorola::CRTC::BusState &) {}
Outputs::CRT::CRT crt;
enum class OutputState {
Sync, Pixels, Border
} output_state = OutputState::Sync;
int count = 0;
uint8_t *pixels = nullptr;
uint8_t *pixel_pointer = nullptr;
static constexpr size_t DefaultAllocationSize = 720;
const uint8_t *ram = nullptr;
std::vector<uint8_t> font;
uint8_t control_ = 0;
} outputter_;
Motorola::CRTC::CRTC6845<CRTCOutputter, Motorola::CRTC::CursorType::MDA> crtc_;
int full_clock_;
};
struct PCSpeaker { struct PCSpeaker {
PCSpeaker() : PCSpeaker() :
toggle(queue), toggle(queue),
@ -729,10 +525,28 @@ class PITObserver {
using PIT = i8253<false, PITObserver>; using PIT = i8253<false, PITObserver>;
class i8255PortHandler : public Intel::i8255::PortHandler { class i8255PortHandler : public Intel::i8255::PortHandler {
// Likely to be helpful: https://github.com/tmk/tmk_keyboard/wiki/IBM-PC-XT-Keyboard-Protocol
public: public:
i8255PortHandler(PCSpeaker &speaker, KeyboardController &keyboard) : i8255PortHandler(PCSpeaker &speaker, KeyboardController &keyboard, VideoAdaptor adaptor, int drive_count) :
speaker_(speaker), keyboard_(keyboard) {} speaker_(speaker), keyboard_(keyboard) {
// High switches:
//
// b3, b2: drive count; 00 = 1, 01 = 2, etc
// b1, b0: video mode (00 = ROM; 01 = CGA40; 10 = CGA80; 11 = MDA)
switch(adaptor) {
default: break;
case VideoAdaptor::MDA: high_switches_ |= 0b11; break;
case VideoAdaptor::CGA: high_switches_ |= 0b10; break; // Assume 80 columns.
}
high_switches_ |= uint8_t(drive_count << 2);
// Low switches:
//
// b3, b2: RAM on motherboard (64 * bit pattern)
// b1: 1 => FPU present; 0 => absent;
// b0: 1 => floppy drive present; 0 => absent.
low_switches_ |= 0b1100; // Assume maximum RAM.
if(drive_count) low_switches_ |= 0xb0001;
}
void set_value(int port, uint8_t value) { void set_value(int port, uint8_t value) {
switch(port) { switch(port) {
@ -747,43 +561,35 @@ class i8255PortHandler : public Intel::i8255::PortHandler {
enable_keyboard_ = !(value & 0x80); enable_keyboard_ = !(value & 0x80);
keyboard_.set_mode(value >> 6); keyboard_.set_mode(value >> 6);
high_switches_ = value & 0x08; use_high_switches_ = value & 0x08;
speaker_.set_control(value & 0x01, value & 0x02); speaker_.set_control(value & 0x01, value & 0x02);
break; break;
} }
// printf("PPI: %02x to %d\n", value, port);
} }
uint8_t get_value(int port) { uint8_t get_value(int port) {
switch(port) { switch(port) {
case 0: case 0:
// printf("PPI: from keyboard\n"); return enable_keyboard_ ? keyboard_.read() : uint8_t((high_switches_ << 4) | low_switches_);
return enable_keyboard_ ? keyboard_.read() : 0b0011'1101;
// Guesses that switches is high and low combined as below. // Guesses that switches is high and low combined as below.
case 2: case 2:
// Common:
//
// b7: 1 => memory parity error; 0 => none; // b7: 1 => memory parity error; 0 => none;
// b6: 1 => IO channel error; 0 => none; // b6: 1 => IO channel error; 0 => none;
// b5: timer 2 output; [TODO] // b5: timer 2 output; [TODO]
// b4: cassette data input; [TODO] // b4: cassette data input; [TODO]
// b3...b0: whichever of the high and low switches is selected.
return return
high_switches_ ? use_high_switches_ ? high_switches_ : low_switches_;
// b3, b2: drive count; 00 = 1, 01 = 2, etc
// b1, b0: video mode (00 = ROM; 01 = CGA40; 10 = CGA80; 11 = MDA)
0b0000'0011
:
// b3, b2: RAM on motherboard (64 * bit pattern)
// b1: 1 => FPU present; 0 => absent;
// b0: 1 => floppy drive present; 0 => absent.
0b0000'1101;
} }
return 0; return 0;
}; };
private: private:
bool high_switches_ = false; uint8_t high_switches_ = 0;
uint8_t low_switches_ = 0;
bool use_high_switches_ = false;
PCSpeaker &speaker_; PCSpeaker &speaker_;
KeyboardController &keyboard_; KeyboardController &keyboard_;
@ -794,8 +600,8 @@ using PPI = Intel::i8255::i8255<i8255PortHandler>;
template <VideoAdaptor video> template <VideoAdaptor video>
class IO { class IO {
public: public:
IO(PIT &pit, DMA &dma, PPI &ppi, PIC &pic, MDA &mda, FloppyController &fdc) : IO(PIT &pit, DMA &dma, PPI &ppi, PIC &pic, typename Adaptor<video>::type &card, FloppyController &fdc) :
pit_(pit), dma_(dma), ppi_(ppi), pic_(pic), mda_(mda), fdc_(fdc) {} pit_(pit), dma_(dma), ppi_(ppi), pic_(pic), video_(card), fdc_(fdc) {}
template <typename IntT> void out(uint16_t port, IntT value) { template <typename IntT> void out(uint16_t port, IntT value) {
switch(port) { switch(port) {
@ -853,33 +659,40 @@ class IO {
case 0x0086: dma_.pages.set_page<6>(uint8_t(value)); break; case 0x0086: dma_.pages.set_page<6>(uint8_t(value)); break;
case 0x0087: dma_.pages.set_page<7>(uint8_t(value)); break; case 0x0087: dma_.pages.set_page<7>(uint8_t(value)); break;
//
// MDA block, with slightly laboured 16-bit to 8-bit mapping.
//
case 0x03b0: case 0x03b2: case 0x03b4: case 0x03b6: case 0x03b0: case 0x03b2: case 0x03b4: case 0x03b6:
if constexpr (std::is_same_v<IntT, uint16_t>) { if constexpr (video == VideoAdaptor::MDA) {
mda_.write<0>(uint8_t(value)); if constexpr (std::is_same_v<IntT, uint16_t>) {
mda_.write<1>(uint8_t(value >> 8)); video_.template write<0>(uint8_t(value));
} else { video_.template write<1>(uint8_t(value >> 8));
mda_.write<0>(value); } else {
video_.template write<0>(value);
}
} }
break; break;
case 0x03b1: case 0x03b3: case 0x03b5: case 0x03b7: case 0x03b1: case 0x03b3: case 0x03b5: case 0x03b7:
if constexpr (std::is_same_v<IntT, uint16_t>) { if constexpr (video == VideoAdaptor::MDA) {
mda_.write<1>(uint8_t(value)); if constexpr (std::is_same_v<IntT, uint16_t>) {
mda_.write<0>(uint8_t(value >> 8)); video_.template write<1>(uint8_t(value));
} else { video_.template write<0>(uint8_t(value >> 8));
mda_.write<1>(value); } else {
video_.template write<1>(value);
}
} }
break; break;
case 0x03b8: case 0x03b8:
mda_.write<8>(uint8_t(value)); if constexpr (video == VideoAdaptor::MDA) {
video_.template write<8>(uint8_t(value));
}
break; break;
case 0x03d0: case 0x03d1: case 0x03d2: case 0x03d3: case 0x03d0: case 0x03d1: case 0x03d2: case 0x03d3:
case 0x03d4: case 0x03d5: case 0x03d6: case 0x03d7: case 0x03d4: case 0x03d5: case 0x03d6: case 0x03d7:
case 0x03d8: case 0x03d9: case 0x03da: case 0x03db: case 0x03d8: case 0x03d9: case 0x03da: case 0x03db:
case 0x03dc: case 0x03dd: case 0x03de: case 0x03df: case 0x03dc: case 0x03dd: case 0x03de: case 0x03df:
// Ignore CGA accesses. // TODO: CGA accesses.
break; break;
case 0x03f2: case 0x03f2:
@ -965,7 +778,11 @@ class IO {
case 0x03f4: return fdc_.status(); case 0x03f4: return fdc_.status();
case 0x03f5: return fdc_.read(); case 0x03f5: return fdc_.read();
case 0x03b8: return mda_.read<8>(); case 0x03b8:
if constexpr (video == VideoAdaptor::MDA) {
return video_.template read<8>();
}
break;
case 0x02e8: case 0x02e9: case 0x02ea: case 0x02eb: case 0x02e8: case 0x02e9: case 0x02ea: case 0x02eb:
case 0x02ec: case 0x02ed: case 0x02ee: case 0x02ef: case 0x02ec: case 0x02ed: case 0x02ee: case 0x02ef:
@ -986,7 +803,7 @@ class IO {
DMA &dma_; DMA &dma_;
PPI &ppi_; PPI &ppi_;
PIC &pic_; PIC &pic_;
MDA &mda_; typename Adaptor<video>::type &video_;
FloppyController &fdc_; FloppyController &fdc_;
}; };
@ -1049,18 +866,21 @@ class ConcreteMachine:
public MachineTypes::ScanProducer, public MachineTypes::ScanProducer,
public Activity::Source public Activity::Source
{ {
static constexpr int DriveCount = 1;
using Video = typename Adaptor<video>::type;
public: public:
ConcreteMachine( ConcreteMachine(
[[maybe_unused]] const Analyser::Static::Target &target, const Analyser::Static::PCCompatible::Target &target,
const ROMMachine::ROMFetcher &rom_fetcher const ROMMachine::ROMFetcher &rom_fetcher
) : ) :
keyboard_(pic_), keyboard_(pic_),
fdc_(pic_, dma_), fdc_(pic_, dma_, DriveCount),
pit_observer_(pic_, speaker_), pit_observer_(pic_, speaker_),
ppi_handler_(speaker_, keyboard_), ppi_handler_(speaker_, keyboard_, video, DriveCount),
pit_(pit_observer_), pit_(pit_observer_),
ppi_(ppi_handler_), ppi_(ppi_handler_),
context(pit_, dma_, ppi_, pic_, mda_, fdc_) context(pit_, dma_, ppi_, pic_, video_, fdc_)
{ {
// Set up DMA source/target. // Set up DMA source/target.
dma_.set_memory(&context.memory); dma_.set_memory(&context.memory);
@ -1072,7 +892,7 @@ class ConcreteMachine:
// Fetch the BIOS. [8088 only, for now] // Fetch the BIOS. [8088 only, for now]
const auto bios = ROM::Name::PCCompatibleGLaBIOS; const auto bios = ROM::Name::PCCompatibleGLaBIOS;
const auto font = ROM::Name::PCCompatibleMDAFont; const auto font = ROM::Name::PCCompatibleMDAFont; // ... or CGA.
ROM::Request request = ROM::Request(bios) && ROM::Request(font); ROM::Request request = ROM::Request(bios) && ROM::Request(font);
auto roms = rom_fetcher(request); auto roms = rom_fetcher(request);
@ -1083,9 +903,9 @@ class ConcreteMachine:
const auto &bios_contents = roms.find(bios)->second; const auto &bios_contents = roms.find(bios)->second;
context.memory.install(0x10'0000 - bios_contents.size(), bios_contents.data(), bios_contents.size()); context.memory.install(0x10'0000 - bios_contents.size(), bios_contents.data(), bios_contents.size());
// Give the MDA something to read from. // Give the video card something to read from.
const auto &font_contents = roms.find(font)->second; const auto &font_contents = roms.find(font)->second;
mda_.set_source(context.memory.at(0xb'0000), font_contents); video_.set_source(context.memory.at(0xb'0000), font_contents); // TODO: get RAM base address from the card.
// ... and insert media. // ... and insert media.
insert_media(target.media); insert_media(target.media);
@ -1120,7 +940,7 @@ class ConcreteMachine:
// //
// Advance CRTC at a more approximate rate. // Advance CRTC at a more approximate rate.
// //
mda_.run_for(Cycles(3)); video_.run_for(Cycles(3));
// //
// Perform one CPU instruction every three PIT cycles. // Perform one CPU instruction every three PIT cycles.
@ -1199,10 +1019,10 @@ class ConcreteMachine:
// MARK: - ScanProducer. // MARK: - ScanProducer.
void set_scan_target(Outputs::Display::ScanTarget *scan_target) override { void set_scan_target(Outputs::Display::ScanTarget *scan_target) override {
mda_.set_scan_target(scan_target); video_.set_scan_target(scan_target);
} }
Outputs::Display::ScanStatus get_scaled_scan_status() const override { Outputs::Display::ScanStatus get_scaled_scan_status() const override {
return mda_.get_scaled_scan_status(); return video_.get_scaled_scan_status();
} }
// MARK: - AudioProducer. // MARK: - AudioProducer.
@ -1246,7 +1066,7 @@ class ConcreteMachine:
PIC pic_; PIC pic_;
DMA dma_; DMA dma_;
PCSpeaker speaker_; PCSpeaker speaker_;
MDA mda_; Video video_;
KeyboardController keyboard_; KeyboardController keyboard_;
FloppyController fdc_; FloppyController fdc_;
@ -1304,8 +1124,8 @@ Machine *Machine::PCCompatible(const Analyser::Static::Target *target, const ROM
const Target *const pc_target = dynamic_cast<const Target *>(target); const Target *const pc_target = dynamic_cast<const Target *>(target);
switch(pc_target->adaptor) { switch(pc_target->adaptor) {
case VideoAdaptor::MDA: return new PCCompatible::ConcreteMachine<VideoAdaptor::MDA>(*target, rom_fetcher); case VideoAdaptor::MDA: return new PCCompatible::ConcreteMachine<VideoAdaptor::MDA>(*pc_target, rom_fetcher);
case VideoAdaptor::CGA: return new PCCompatible::ConcreteMachine<VideoAdaptor::CGA>(*target, rom_fetcher); case VideoAdaptor::CGA: return new PCCompatible::ConcreteMachine<VideoAdaptor::CGA>(*pc_target, rom_fetcher);
default: return nullptr; default: return nullptr;
} }
} }

View File

@ -1168,6 +1168,7 @@
428168392A37AFB4008ECD27 /* DispatcherTests.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = DispatcherTests.mm; sourceTree = "<group>"; }; 428168392A37AFB4008ECD27 /* DispatcherTests.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = DispatcherTests.mm; sourceTree = "<group>"; };
429B135E2B1F7BDA006BB4CB /* StaticAnalyser.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = StaticAnalyser.hpp; sourceTree = "<group>"; }; 429B135E2B1F7BDA006BB4CB /* StaticAnalyser.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = StaticAnalyser.hpp; sourceTree = "<group>"; };
429B135F2B1F7BDA006BB4CB /* StaticAnalyser.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = StaticAnalyser.cpp; sourceTree = "<group>"; }; 429B135F2B1F7BDA006BB4CB /* StaticAnalyser.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = StaticAnalyser.cpp; sourceTree = "<group>"; };
429B13622B1FCA96006BB4CB /* MDA.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = MDA.hpp; sourceTree = "<group>"; };
42A5E80B2ABBE04600A0DD5D /* NeskellTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = NeskellTests.swift; sourceTree = "<group>"; }; 42A5E80B2ABBE04600A0DD5D /* NeskellTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = NeskellTests.swift; sourceTree = "<group>"; };
42A5E8332ABBE16F00A0DD5D /* illegal_rmw_test.bin */ = {isa = PBXFileReference; lastKnownFileType = archive.macbinary; path = illegal_rmw_test.bin; sourceTree = "<group>"; }; 42A5E8332ABBE16F00A0DD5D /* illegal_rmw_test.bin */ = {isa = PBXFileReference; lastKnownFileType = archive.macbinary; path = illegal_rmw_test.bin; sourceTree = "<group>"; };
42A5E8342ABBE16F00A0DD5D /* arr_bcd_test.bin */ = {isa = PBXFileReference; lastKnownFileType = archive.macbinary; path = arr_bcd_test.bin; sourceTree = "<group>"; }; 42A5E8342ABBE16F00A0DD5D /* arr_bcd_test.bin */ = {isa = PBXFileReference; lastKnownFileType = archive.macbinary; path = arr_bcd_test.bin; sourceTree = "<group>"; };
@ -2396,6 +2397,7 @@
425739372B051EA800B7D1E4 /* PCCompatible.cpp */, 425739372B051EA800B7D1E4 /* PCCompatible.cpp */,
4267A9C92B0D4F17008A59BB /* DMA.hpp */, 4267A9C92B0D4F17008A59BB /* DMA.hpp */,
4267A9CA2B111ED2008A59BB /* KeyboardMapper.hpp */, 4267A9CA2B111ED2008A59BB /* KeyboardMapper.hpp */,
429B13622B1FCA96006BB4CB /* MDA.hpp */,
423820132B1A235200964EFE /* Memory.hpp */, 423820132B1A235200964EFE /* Memory.hpp */,
425739362B051EA800B7D1E4 /* PCCompatible.hpp */, 425739362B051EA800B7D1E4 /* PCCompatible.hpp */,
4267A9C82B0D4EC2008A59BB /* PIC.hpp */, 4267A9C82B0D4EC2008A59BB /* PIC.hpp */,