1
0
mirror of https://github.com/TomHarte/CLK.git synced 2024-11-26 08:49:37 +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
commit 518cd69d5c
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 73 additions and 45 deletions

View File

@ -49,15 +49,12 @@
namespace PCCompatible {
using VideoAdaptor = Analyser::Static::PCCompatible::Target::VideoAdaptor;
using Target = Analyser::Static::PCCompatible::Target;
template <VideoAdaptor adaptor> struct Adaptor;
template <> struct Adaptor<VideoAdaptor::MDA> {
using type = MDA;
};
template <> struct Adaptor<VideoAdaptor::CGA> {
using type = CGA;
};
// Map from members of the VideoAdaptor enum to concrete class types.
template <Target::VideoAdaptor adaptor> struct Adaptor;
template <> struct Adaptor<Target::VideoAdaptor::MDA> { using type = MDA; };
template <> struct Adaptor<Target::VideoAdaptor::CGA> { using type = CGA; };
class FloppyController {
public:
@ -500,7 +497,7 @@ using PIT = i8253<false, PITObserver>;
class i8255PortHandler : public Intel::i8255::PortHandler {
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) {
// High switches:
//
@ -508,8 +505,8 @@ class i8255PortHandler : public Intel::i8255::PortHandler {
// 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.
case Target::VideoAdaptor::MDA: high_switches_ |= 0b11; break;
case Target::VideoAdaptor::CGA: high_switches_ |= 0b10; break; // Assume 80 columns.
}
high_switches_ |= uint8_t(drive_count << 2);
@ -593,7 +590,7 @@ class i8255PortHandler : public Intel::i8255::PortHandler {
};
using PPI = Intel::i8255::i8255<i8255PortHandler>;
template <VideoAdaptor video>
template <Target::VideoAdaptor video>
class IO {
public:
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) {
static constexpr uint16_t crtc_base =
video == VideoAdaptor::MDA ? 0x03b0 : 0x03d0;
video == Target::VideoAdaptor::MDA ? 0x03b0 : 0x03d0;
switch(port) {
default:
@ -772,13 +769,13 @@ class IO {
case 0x03f5: return fdc_.read();
case 0x03b8:
if constexpr (video == VideoAdaptor::MDA) {
if constexpr (video == Target::VideoAdaptor::MDA) {
return video_.template read<0x8>();
}
break;
case 0x3da:
if constexpr (video == VideoAdaptor::CGA) {
if constexpr (video == Target::VideoAdaptor::CGA) {
return video_.template read<0xa>();
}
break;
@ -860,7 +857,7 @@ class FlowController {
bool halted_ = false;
};
template <VideoAdaptor video, bool turbo>
template <Target::VideoAdaptor video>
class ConcreteMachine:
public Machine,
public MachineTypes::TimedMachine,
@ -887,6 +884,9 @@ class ConcreteMachine:
ppi_(ppi_handler_),
context(pit_, dma_, ppi_, pic_, video_, fdc_, rtc_)
{
// Capture speed.
speed_ = target.speed;
// Set up DMA source/target.
dma_.set_memory(&context.memory);
@ -900,17 +900,21 @@ class ConcreteMachine:
const auto tick = ROM::Name::PCCompatibleGLaTICK;
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);
if(!request.validate(roms)) {
throw ROMMachine::Error::MissingROMs;
}
// A BIOS is mandatory.
const auto &bios_contents = roms.find(bios)->second;
context.memory.install(0x10'0000 - bios_contents.size(), bios_contents.data(), bios_contents.size());
const auto &tick_contents = roms.find(tick)->second;
context.memory.install(0xd'0000, tick_contents.data(), tick_contents.size());
// If found, install GlaTICK at 0xd'0000.
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.
const auto &font_contents = roms.find(font)->second;
@ -926,15 +930,23 @@ class ConcreteMachine:
// MARK: - TimedMachine.
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>();
int ticks;
if constexpr (!turbo) {
if constexpr (speed == Target::Speed::Fast) {
ticks = pit_ticks;
} else {
cpu_divisor_ += pit_ticks;
ticks = cpu_divisor_ / 3;
cpu_divisor_ %= 3;
} else {
ticks = pit_ticks;
}
while(ticks--) {
@ -948,7 +960,9 @@ class ConcreteMachine:
pit_.run_for(1);
++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);
++speaker_.cycles_since_update;
pit_.run_for(1);
@ -958,7 +972,7 @@ class ConcreteMachine:
//
// 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,
@ -992,8 +1006,8 @@ class ConcreteMachine:
continue;
}
if constexpr (turbo) {
// There's no divider applied, so this makes for 2*PI = around 2.4 MIPS.
if constexpr (speed == Target::Speed::Fast) {
// 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.
perform_instruction();
perform_instruction();
@ -1176,6 +1190,7 @@ class ConcreteMachine:
std::pair<int, InstructionSet::x86::Instruction<false>> decoded;
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.
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);
if(pc_target->speed == Target::Speed::Fast) {
switch(pc_target->adaptor) {
case VideoAdaptor::MDA: return new PCCompatible::ConcreteMachine<VideoAdaptor::MDA, true>(*pc_target, rom_fetcher);
case VideoAdaptor::CGA: return new PCCompatible::ConcreteMachine<VideoAdaptor::CGA, true>(*pc_target, rom_fetcher);
case Target::VideoAdaptor::MDA: return new PCCompatible::ConcreteMachine<Target::VideoAdaptor::MDA>(*pc_target, rom_fetcher);
case Target::VideoAdaptor::CGA: return new PCCompatible::ConcreteMachine<Target::VideoAdaptor::CGA>(*pc_target, rom_fetcher);
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() {}

View File

@ -581,6 +581,13 @@ Description::Description(Name name) {
case Name::PCCompatibleMDAFont:
*this = Description(name, "PCCompatible", "IBM's MDA font", "EUMDA9.F14", 14 * 256, 0x7754882au);
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.
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,
PCCompatibleGLaTICK,
PCCompatiblePhoenix80286BIOS,
PCCompatibleMDAFont,
PCCompatibleCGAFont,
PCCompatibleEGABIOS,
PCCompatibleVGABIOS,
// Sinclair QL.
SinclairQLJS,

View File

@ -1,14 +1,24 @@
Expected files:
For XT-class emulation:
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.
EUMDA9.F14 — a dump of the MDA font.
CGA.F08 — a dump of the CGA font.
ibm_vga.bin — the VGA BIOS.
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/
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) {
switch(density) {
default:
case Density::Single: return FMBitLength;
case Density::Double: return MFMBitLength;
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
) {
switch(density) {
default:
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::High: return TTrackWithSectors<Density::High>(sectors, sector_gap_length, sector_gap_filler_byte);