1
0
mirror of https://github.com/TomHarte/CLK.git synced 2025-08-15 14:27:29 +00:00

Merge pull request #1288 from TomHarte/EGAVGAROMs

Add EGA and VGA ROM references; mildly clean up PC.
This commit is contained in:
Thomas Harte
2023-12-27 11:14:38 -05:00
committed by GitHub
6 changed files with 73 additions and 45 deletions

View File

@@ -49,15 +49,12 @@
namespace PCCompatible { namespace PCCompatible {
using VideoAdaptor = Analyser::Static::PCCompatible::Target::VideoAdaptor; using Target = Analyser::Static::PCCompatible::Target;
template <VideoAdaptor adaptor> struct Adaptor; // Map from members of the VideoAdaptor enum to concrete class types.
template <> struct Adaptor<VideoAdaptor::MDA> { template <Target::VideoAdaptor adaptor> struct Adaptor;
using type = MDA; template <> struct Adaptor<Target::VideoAdaptor::MDA> { using type = MDA; };
}; template <> struct Adaptor<Target::VideoAdaptor::CGA> { using type = CGA; };
template <> struct Adaptor<VideoAdaptor::CGA> {
using type = CGA;
};
class FloppyController { class FloppyController {
public: public:
@@ -500,7 +497,7 @@ using PIT = i8253<false, PITObserver>;
class i8255PortHandler : public Intel::i8255::PortHandler { class i8255PortHandler : public Intel::i8255::PortHandler {
public: public:
i8255PortHandler(PCSpeaker &speaker, KeyboardController &keyboard, VideoAdaptor adaptor, int drive_count) : i8255PortHandler(PCSpeaker &speaker, KeyboardController &keyboard, Target::VideoAdaptor adaptor, int drive_count) :
speaker_(speaker), keyboard_(keyboard) { speaker_(speaker), keyboard_(keyboard) {
// High switches: // High switches:
// //
@@ -508,8 +505,8 @@ class i8255PortHandler : public Intel::i8255::PortHandler {
// b1, b0: video mode (00 = ROM; 01 = CGA40; 10 = CGA80; 11 = MDA) // b1, b0: video mode (00 = ROM; 01 = CGA40; 10 = CGA80; 11 = MDA)
switch(adaptor) { switch(adaptor) {
default: break; default: break;
case VideoAdaptor::MDA: high_switches_ |= 0b11; break; case Target::VideoAdaptor::MDA: high_switches_ |= 0b11; break;
case VideoAdaptor::CGA: high_switches_ |= 0b10; break; // Assume 80 columns. case Target::VideoAdaptor::CGA: high_switches_ |= 0b10; break; // Assume 80 columns.
} }
high_switches_ |= uint8_t(drive_count << 2); high_switches_ |= uint8_t(drive_count << 2);
@@ -593,7 +590,7 @@ class i8255PortHandler : public Intel::i8255::PortHandler {
}; };
using PPI = Intel::i8255::i8255<i8255PortHandler>; using PPI = Intel::i8255::i8255<i8255PortHandler>;
template <VideoAdaptor video> template <Target::VideoAdaptor video>
class IO { class IO {
public: public:
IO(PIT &pit, DMA &dma, PPI &ppi, PIC &pic, typename Adaptor<video>::type &card, FloppyController &fdc, RTC &rtc) : IO(PIT &pit, DMA &dma, PPI &ppi, PIC &pic, typename Adaptor<video>::type &card, FloppyController &fdc, RTC &rtc) :
@@ -601,7 +598,7 @@ class IO {
template <typename IntT> void out(uint16_t port, IntT value) { template <typename IntT> void out(uint16_t port, IntT value) {
static constexpr uint16_t crtc_base = static constexpr uint16_t crtc_base =
video == VideoAdaptor::MDA ? 0x03b0 : 0x03d0; video == Target::VideoAdaptor::MDA ? 0x03b0 : 0x03d0;
switch(port) { switch(port) {
default: default:
@@ -772,13 +769,13 @@ class IO {
case 0x03f5: return fdc_.read(); case 0x03f5: return fdc_.read();
case 0x03b8: case 0x03b8:
if constexpr (video == VideoAdaptor::MDA) { if constexpr (video == Target::VideoAdaptor::MDA) {
return video_.template read<0x8>(); return video_.template read<0x8>();
} }
break; break;
case 0x3da: case 0x3da:
if constexpr (video == VideoAdaptor::CGA) { if constexpr (video == Target::VideoAdaptor::CGA) {
return video_.template read<0xa>(); return video_.template read<0xa>();
} }
break; break;
@@ -860,7 +857,7 @@ class FlowController {
bool halted_ = false; bool halted_ = false;
}; };
template <VideoAdaptor video, bool turbo> template <Target::VideoAdaptor video>
class ConcreteMachine: class ConcreteMachine:
public Machine, public Machine,
public MachineTypes::TimedMachine, public MachineTypes::TimedMachine,
@@ -887,6 +884,9 @@ class ConcreteMachine:
ppi_(ppi_handler_), ppi_(ppi_handler_),
context(pit_, dma_, ppi_, pic_, video_, fdc_, rtc_) context(pit_, dma_, ppi_, pic_, video_, fdc_, rtc_)
{ {
// Capture speed.
speed_ = target.speed;
// Set up DMA source/target. // Set up DMA source/target.
dma_.set_memory(&context.memory); dma_.set_memory(&context.memory);
@@ -900,17 +900,21 @@ class ConcreteMachine:
const auto tick = ROM::Name::PCCompatibleGLaTICK; const auto tick = ROM::Name::PCCompatibleGLaTICK;
const auto font = Video::FontROM; const auto font = Video::FontROM;
ROM::Request request = ROM::Request(bios) && ROM::Request(tick) && ROM::Request(font); ROM::Request request = ROM::Request(bios) && ROM::Request(tick, true) && ROM::Request(font);
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;
} }
// A BIOS is mandatory.
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());
const auto &tick_contents = roms.find(tick)->second; // If found, install GlaTICK at 0xd'0000.
context.memory.install(0xd'0000, tick_contents.data(), tick_contents.size()); auto tick_contents = roms.find(tick);
if(tick_contents != roms.end()) {
context.memory.install(0xd'0000, tick_contents->second.data(), tick_contents->second.size());
}
// Give the video card 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;
@@ -926,15 +930,23 @@ class ConcreteMachine:
// MARK: - TimedMachine. // MARK: - TimedMachine.
void run_for(const Cycles duration) override { void run_for(const Cycles duration) override {
switch(speed_) {
case Target::Speed::ApproximatelyOriginal: run_for<Target::Speed::ApproximatelyOriginal>(duration); break;
case Target::Speed::Fast: run_for<Target::Speed::Fast>(duration); break;
}
}
template <Target::Speed speed>
void run_for(const Cycles duration) {
const auto pit_ticks = duration.as<int>(); const auto pit_ticks = duration.as<int>();
int ticks; int ticks;
if constexpr (!turbo) { if constexpr (speed == Target::Speed::Fast) {
ticks = pit_ticks;
} else {
cpu_divisor_ += pit_ticks; cpu_divisor_ += pit_ticks;
ticks = cpu_divisor_ / 3; ticks = cpu_divisor_ / 3;
cpu_divisor_ %= 3; cpu_divisor_ %= 3;
} else {
ticks = pit_ticks;
} }
while(ticks--) { while(ticks--) {
@@ -948,7 +960,9 @@ class ConcreteMachine:
pit_.run_for(1); pit_.run_for(1);
++speaker_.cycles_since_update; ++speaker_.cycles_since_update;
if constexpr (!turbo) { // For original speed, the CPU performs instructions at a 1/3rd divider of the PIT clock,
// so run the PIT three times per 'tick'.
if constexpr (speed == Target::Speed::ApproximatelyOriginal) {
pit_.run_for(1); pit_.run_for(1);
++speaker_.cycles_since_update; ++speaker_.cycles_since_update;
pit_.run_for(1); pit_.run_for(1);
@@ -958,7 +972,7 @@ class ConcreteMachine:
// //
// Advance CRTC at a more approximate rate. // Advance CRTC at a more approximate rate.
// //
video_.run_for(turbo ? Cycles(1) : Cycles(3)); video_.run_for(speed == Target::Speed::Fast ? Cycles(1) : Cycles(3));
// //
// Give the keyboard a notification of passing time; it's very approximately clocked, // Give the keyboard a notification of passing time; it's very approximately clocked,
@@ -992,8 +1006,8 @@ class ConcreteMachine:
continue; continue;
} }
if constexpr (turbo) { if constexpr (speed == Target::Speed::Fast) {
// There's no divider applied, so this makes for 2*PI = around 2.4 MIPS. // There's no divider applied, so this makes for 2*PIT = around 2.4 MIPS.
// That's broadly 80286 speed, if MIPS were a valid measure. // That's broadly 80286 speed, if MIPS were a valid measure.
perform_instruction(); perform_instruction();
perform_instruction(); perform_instruction();
@@ -1176,6 +1190,7 @@ class ConcreteMachine:
std::pair<int, InstructionSet::x86::Instruction<false>> decoded; std::pair<int, InstructionSet::x86::Instruction<false>> decoded;
int cpu_divisor_ = 0; int cpu_divisor_ = 0;
Target::Speed speed_{};
}; };
@@ -1185,22 +1200,13 @@ using namespace PCCompatible;
// See header; constructs and returns an instance of the Amstrad CPC. // See header; constructs and returns an instance of the Amstrad CPC.
Machine *Machine::PCCompatible(const Analyser::Static::Target *target, const ROMMachine::ROMFetcher &rom_fetcher) { Machine *Machine::PCCompatible(const Analyser::Static::Target *target, const ROMMachine::ROMFetcher &rom_fetcher) {
using Target = Analyser::Static::PCCompatible::Target;
const Target *const pc_target = dynamic_cast<const Target *>(target); const Target *const pc_target = dynamic_cast<const Target *>(target);
if(pc_target->speed == Target::Speed::Fast) {
switch(pc_target->adaptor) { switch(pc_target->adaptor) {
case VideoAdaptor::MDA: return new PCCompatible::ConcreteMachine<VideoAdaptor::MDA, true>(*pc_target, rom_fetcher); case Target::VideoAdaptor::MDA: return new PCCompatible::ConcreteMachine<Target::VideoAdaptor::MDA>(*pc_target, rom_fetcher);
case VideoAdaptor::CGA: return new PCCompatible::ConcreteMachine<VideoAdaptor::CGA, true>(*pc_target, rom_fetcher); case Target::VideoAdaptor::CGA: return new PCCompatible::ConcreteMachine<Target::VideoAdaptor::CGA>(*pc_target, rom_fetcher);
default: return nullptr; default: return nullptr;
} }
} else {
switch(pc_target->adaptor) {
case VideoAdaptor::MDA: return new PCCompatible::ConcreteMachine<VideoAdaptor::MDA, false>(*pc_target, rom_fetcher);
case VideoAdaptor::CGA: return new PCCompatible::ConcreteMachine<VideoAdaptor::CGA, false>(*pc_target, rom_fetcher);
default: return nullptr;
}
}
} }
Machine::~Machine() {} Machine::~Machine() {}

View File

@@ -581,6 +581,13 @@ Description::Description(Name name) {
case Name::PCCompatibleMDAFont: case Name::PCCompatibleMDAFont:
*this = Description(name, "PCCompatible", "IBM's MDA font", "EUMDA9.F14", 14 * 256, 0x7754882au); *this = Description(name, "PCCompatible", "IBM's MDA font", "EUMDA9.F14", 14 * 256, 0x7754882au);
break; break;
case Name::PCCompatibleEGABIOS:
*this = Description(name, "PCCompatible", "IBM's EGA BIOS", "ibm_6277356_ega_card_u44_27128.bin", 16 * 1024, 0x2f2fbc40u);
break;
case Name::PCCompatibleVGABIOS:
*this = Description(name, "PCCompatible", "IBM's VGA BIOS", "ibm_vga.bin", 32 * 1024, 0x03b3f90du);
break;
// TODO: CRCs below are incomplete, at best. // TODO: CRCs below are incomplete, at best.
case Name::MSXGenericBIOS: *this = Description(name, "MSX", "a generix MSX BIOS", "msx.rom", 32*1024, 0x94ee12f3u); break; case Name::MSXGenericBIOS: *this = Description(name, "MSX", "a generix MSX BIOS", "msx.rom", 32*1024, 0x94ee12f3u); break;

View File

@@ -135,8 +135,11 @@ enum Name {
PCCompatibleGLaBIOS, PCCompatibleGLaBIOS,
PCCompatibleGLaTICK, PCCompatibleGLaTICK,
PCCompatiblePhoenix80286BIOS, PCCompatiblePhoenix80286BIOS,
PCCompatibleMDAFont, PCCompatibleMDAFont,
PCCompatibleCGAFont, PCCompatibleCGAFont,
PCCompatibleEGABIOS,
PCCompatibleVGABIOS,
// Sinclair QL. // Sinclair QL.
SinclairQLJS, SinclairQLJS,

View File

@@ -1,14 +1,24 @@
Expected files: Expected files:
For XT-class emulation:
GLABIOS_0.2.5_8T.ROM — the 8088 GLaBIOS ROM. GLABIOS_0.2.5_8T.ROM — the 8088 GLaBIOS ROM.
GLaTICK_0.8.5_AT.ROM — the GLaBIOS AT RTC option ROM. GLaTICK_0.8.5_AT.ROM (optionally) — the GLaBIOS AT RTC option ROM.
For specific video cards:
EUMDA9.F14 — the MDA font.
CGA.F08 — the CGA font.
In the future:
Phoenix 80286 ROM BIOS Version 3.05.bin — Phoenix's 80286 AT-clone BIOS. Phoenix 80286 ROM BIOS Version 3.05.bin — Phoenix's 80286 AT-clone BIOS.
EUMDA9.F14 — a dump of the MDA font. ibm_vga.bin — the VGA BIOS.
CGA.F08 — a dump of the CGA font. ibm_6277356_ega_card_u44_27128.bin — the EGA BIOS.
GLaBIOS is an open-source GPLv3 alternative BIOS for XT clones, available from https://glabios.org/ GLaBIOS is an open-source GPLv3 alternative BIOS for XT clones, available from https://glabios.org/
GLaTICK is a real-time clock option ROM, also available from available from https://glabios.org/ GLaTICK is a real-time clock option ROM, also available from available from https://glabios.org/
The MDA and CGA fonts are in the form offered at https://github.com/viler-int10h/vga-text-mode-fonts i.e. it's 256 lots of 14 bytes, the first 14 being the content of character 0, the next 14 being the content of character 1, etc. The MDA and CGA fonts are in the form offered at https://github.com/viler-int10h/vga-text-mode-fonts i.e. the MDA font is 256 lots of 14 bytes, the first 14 being the content of character 0, the next 14 being the content of character 1, etc; the CGA font is 256 lots of 8 bytes with a corresponding arrangement.

View File

@@ -44,6 +44,7 @@ const Time FMBitLength = Time(1, 50000);
constexpr Time bit_length(Density density) { constexpr Time bit_length(Density density) {
switch(density) { switch(density) {
default:
case Density::Single: return FMBitLength; case Density::Single: return FMBitLength;
case Density::Double: return MFMBitLength; case Density::Double: return MFMBitLength;
case Density::High: return HDMFMBitLength; case Density::High: return HDMFMBitLength;

View File

@@ -382,6 +382,7 @@ std::shared_ptr<Storage::Disk::Track> Storage::Encodings::MFM::TrackWithSectors(
std::optional<uint8_t> sector_gap_filler_byte std::optional<uint8_t> sector_gap_filler_byte
) { ) {
switch(density) { switch(density) {
default:
case Density::Single: return TTrackWithSectors<Density::Single>(sectors, sector_gap_length, sector_gap_filler_byte); case Density::Single: return TTrackWithSectors<Density::Single>(sectors, sector_gap_length, sector_gap_filler_byte);
case Density::Double: return TTrackWithSectors<Density::Double>(sectors, sector_gap_length, sector_gap_filler_byte); case Density::Double: return TTrackWithSectors<Density::Double>(sectors, sector_gap_length, sector_gap_filler_byte);
case Density::High: return TTrackWithSectors<Density::High>(sectors, sector_gap_length, sector_gap_filler_byte); case Density::High: return TTrackWithSectors<Density::High>(sectors, sector_gap_length, sector_gap_filler_byte);