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:
@@ -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() {}
|
||||||
|
@@ -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;
|
||||||
|
@@ -135,8 +135,11 @@ enum Name {
|
|||||||
PCCompatibleGLaBIOS,
|
PCCompatibleGLaBIOS,
|
||||||
PCCompatibleGLaTICK,
|
PCCompatibleGLaTICK,
|
||||||
PCCompatiblePhoenix80286BIOS,
|
PCCompatiblePhoenix80286BIOS,
|
||||||
|
|
||||||
PCCompatibleMDAFont,
|
PCCompatibleMDAFont,
|
||||||
PCCompatibleCGAFont,
|
PCCompatibleCGAFont,
|
||||||
|
PCCompatibleEGABIOS,
|
||||||
|
PCCompatibleVGABIOS,
|
||||||
|
|
||||||
// Sinclair QL.
|
// Sinclair QL.
|
||||||
SinclairQLJS,
|
SinclairQLJS,
|
||||||
|
@@ -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.
|
@@ -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;
|
||||||
|
@@ -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);
|
||||||
|
Reference in New Issue
Block a user