mirror of
https://github.com/TomHarte/CLK.git
synced 2024-11-22 12:33:29 +00:00
321 lines
8.1 KiB
C++
321 lines
8.1 KiB
C++
//
|
|
// ROMCatalogue.hpp
|
|
// Clock Signal
|
|
//
|
|
// Created by Thomas Harte on 01/06/2021.
|
|
// Copyright © 2021 Thomas Harte. All rights reserved.
|
|
//
|
|
|
|
#pragma once
|
|
|
|
#include <algorithm>
|
|
#include <cstdint>
|
|
#include <functional>
|
|
#include <map>
|
|
#include <optional>
|
|
#include <set>
|
|
#include <string>
|
|
#include <vector>
|
|
|
|
namespace ROM {
|
|
|
|
enum Name {
|
|
None,
|
|
|
|
// Acorn Electron.
|
|
AcornBASICII,
|
|
AcornElectronMOS100,
|
|
PRESADFSSlot1,
|
|
PRESADFSSlot2,
|
|
AcornADFS,
|
|
PRESAdvancedPlus6,
|
|
Acorn1770DFS,
|
|
|
|
// Acorn Archimedes.
|
|
AcornArthur030,
|
|
AcornRISCOS200,
|
|
AcornRISCOS311,
|
|
AcornRISCOS319,
|
|
|
|
// Amiga.
|
|
AmigaKickstart10,
|
|
AmigaKickstart11,
|
|
AmigaKickstart12,
|
|
AmigaA500Kickstart13,
|
|
AmigaA3000Kickstart13,
|
|
AmigaKickstart20,
|
|
AmigaA500PlusKickstart204,
|
|
AmigaA600Kickstart205,
|
|
AmigaA500Kickstart31,
|
|
|
|
AmigaDiagROM121,
|
|
|
|
// Amstrad CPC.
|
|
AMSDOS,
|
|
CPC464Firmware, CPC464BASIC,
|
|
CPC664Firmware, CPC664BASIC,
|
|
CPC6128Firmware, CPC6128BASIC,
|
|
|
|
// Apple II.
|
|
AppleIIOriginal,
|
|
AppleIIPlus,
|
|
AppleIICharacter,
|
|
AppleIIe,
|
|
AppleIIeCharacter,
|
|
AppleIIEnhancedE,
|
|
AppleIIEnhancedECharacter,
|
|
AppleIISCSICard,
|
|
|
|
// Apple IIgs.
|
|
AppleIIgsROM00,
|
|
AppleIIgsROM01,
|
|
AppleIIgsROM03,
|
|
AppleIIgsMicrocontrollerROM03,
|
|
AppleIIgsCharacter,
|
|
|
|
// Atari ST.
|
|
AtariSTTOS100,
|
|
AtariSTTOS104,
|
|
|
|
// ColecoVision.
|
|
ColecoVisionBIOS,
|
|
|
|
// Commodore 1540/1541.
|
|
Commodore1540,
|
|
Commodore1541,
|
|
|
|
// Disk II.
|
|
DiskIIStateMachine16Sector,
|
|
DiskIIBoot16Sector,
|
|
DiskIIStateMachine13Sector,
|
|
DiskIIBoot13Sector,
|
|
|
|
// Enterprise.
|
|
EnterpriseEXOS10,
|
|
EnterpriseEXOS20,
|
|
EnterpriseEXOS21,
|
|
EnterpriseEXOS23,
|
|
|
|
EnterpriseBASIC10,
|
|
EnterpriseBASIC10Part1,
|
|
EnterpriseBASIC10Part2,
|
|
EnterpriseBASIC11,
|
|
EnterpriseBASIC11Suffixed,
|
|
EnterpriseBASIC21,
|
|
|
|
EnterpriseEPDOS,
|
|
EnterpriseEXDOS,
|
|
|
|
// Macintosh.
|
|
Macintosh128k,
|
|
Macintosh512k,
|
|
MacintoshPlus,
|
|
|
|
// Master System.
|
|
MasterSystemJapaneseBIOS,
|
|
MasterSystemWesternBIOS,
|
|
|
|
// MSX.
|
|
MSXGenericBIOS,
|
|
MSXJapaneseBIOS,
|
|
MSXAmericanBIOS,
|
|
MSXEuropeanBIOS,
|
|
MSXDOS,
|
|
|
|
MSX2GenericBIOS,
|
|
MSX2Extension,
|
|
MSXMusic,
|
|
|
|
// Oric.
|
|
OricColourROM,
|
|
OricBASIC10,
|
|
OricBASIC11,
|
|
OricPravetzBASIC,
|
|
OricByteDrive500,
|
|
OricJasmin,
|
|
OricMicrodisc,
|
|
Oric8DOSBoot,
|
|
|
|
// PCCompatible.
|
|
PCCompatibleGLaBIOS,
|
|
PCCompatibleGLaTICK,
|
|
PCCompatiblePhoenix80286BIOS,
|
|
|
|
PCCompatibleMDAFont,
|
|
PCCompatibleCGAFont,
|
|
PCCompatibleEGABIOS,
|
|
PCCompatibleVGABIOS,
|
|
|
|
// Sinclair QL.
|
|
SinclairQLJS,
|
|
|
|
// Vic-20.
|
|
Vic20BASIC,
|
|
Vic20EnglishCharacters,
|
|
Vic20EnglishPALKernel,
|
|
Vic20EnglishNTSCKernel,
|
|
Vic20DanishCharacters,
|
|
Vic20DanishKernel,
|
|
Vic20JapaneseCharacters,
|
|
Vic20JapaneseKernel,
|
|
Vic20SwedishCharacters,
|
|
Vic20SwedishKernel,
|
|
|
|
// ZX80/81.
|
|
ZX80,
|
|
ZX81,
|
|
|
|
// ZX Spectrum.
|
|
Spectrum48k,
|
|
Spectrum128k,
|
|
SpecrumPlus2,
|
|
SpectrumPlus3,
|
|
|
|
};
|
|
|
|
using Map = std::map<ROM::Name, std::vector<uint8_t>>;
|
|
|
|
struct Description {
|
|
/// The ROM's enum name.
|
|
Name name = Name::None;
|
|
/// The machine with which this ROM is associated, in a form that is safe for using as
|
|
/// part of a file name.
|
|
std::string machine_name;
|
|
/// A descriptive name for this ROM, suitable for use in a bullet-point list, a bracket
|
|
/// clause, etc, e.g. "the Electron MOS 1.0".
|
|
std::string descriptive_name;
|
|
/// All idiomatic file name for this ROM, e.g. "os10.rom".
|
|
std::vector<std::string> file_names;
|
|
/// The expected size of this ROM in bytes, e.g. 32768.
|
|
size_t size = 0;
|
|
/// CRC32s for all known acceptable copies of this ROM; intended to allow a host platform
|
|
/// to test user-provided ROMs of unknown provenance. **Not** intended to be used
|
|
/// to exclude ROMs where the user's intent is otherwise clear.
|
|
std::set<uint32_t> crc32s;
|
|
|
|
/// Constructs the @c Description that correlates to @c name.
|
|
Description(Name name);
|
|
|
|
/// Constructs the @c Description that correlates to @c crc32, if any.
|
|
static std::optional<Description> from_crc(uint32_t crc32);
|
|
|
|
enum DescriptionFlag {
|
|
Size = 1 << 0,
|
|
CRC = 1 << 1,
|
|
Filename = 1 << 2,
|
|
};
|
|
|
|
/// Provides a single-line of text describing this ROM, including the usual base text
|
|
/// plus all the fields provided as @c flags .
|
|
std::string description(int flags) const;
|
|
|
|
private:
|
|
template <typename FileNameT, typename CRC32T> Description(
|
|
Name name, std::string machine_name, std::string descriptive_name, FileNameT file_names, size_t size, CRC32T crc32s = uint32_t(0)
|
|
) : name{name}, machine_name{machine_name}, descriptive_name{descriptive_name}, file_names{file_names}, size{size}, crc32s{crc32s} {
|
|
// Slightly lazy: deal with the case where the constructor wasn't provided with any
|
|
// CRCs by spotting that the set has exactly one member, which has value 0. The alternative
|
|
// would be to provide a partial specialisation that never put anything into the set.
|
|
if(this->crc32s.size() == 1 && !*this->crc32s.begin()) {
|
|
this->crc32s.clear();
|
|
}
|
|
}
|
|
};
|
|
|
|
/// @returns a vector of all possible instances of ROM::Description — i.e. descriptions of every ROM
|
|
/// currently known to the ROM catalogue.
|
|
std::vector<Description> all_descriptions();
|
|
|
|
struct Request {
|
|
Request(Name name, bool optional = false);
|
|
Request() = default;
|
|
|
|
/// Forms the request that would be satisfied by @c this plus the right-hand side.
|
|
Request operator &&(const Request &);
|
|
|
|
/// Forms the request that would be satisfied by either @c this or the right-hand side.
|
|
Request operator ||(const Request &);
|
|
|
|
/// Inspects the ROMMap to ensure that it satisfies this @c Request.
|
|
/// @c returns @c true if the request is satisfied; @c false otherwise.
|
|
///
|
|
/// All ROMs in the map will be resized to their idiomatic sizes.
|
|
bool validate(Map &) const;
|
|
|
|
/// Returns a flattened array of all @c ROM::Descriptions that relate to anything
|
|
/// anywhere in this ROM request.
|
|
std::vector<Description> all_descriptions() const;
|
|
|
|
/// @returns @c true if this request is empty, i.e. would be satisfied with no ROMs; @c false otherwise.
|
|
bool empty();
|
|
|
|
/// @returns what remains of this ROM request given that everything in @c map has been found.
|
|
Request subtract(const ROM::Map &map) const;
|
|
|
|
enum class ListType {
|
|
Any, All, Single
|
|
};
|
|
void visit(
|
|
const std::function<void(ListType, size_t size)> &enter_list,
|
|
const std::function<void(void)> &exit_list,
|
|
const std::function<void(ROM::Request::ListType type, const ROM::Description &, bool is_optional, size_t remaining)> &add_item
|
|
) const;
|
|
|
|
enum class LineItem {
|
|
NewList, Description
|
|
};
|
|
void visit(
|
|
const std::function<void(LineItem, ListType, int level, const ROM::Description *, bool is_optional, size_t remaining)> &add_item
|
|
) const;
|
|
|
|
/// @returns a full bullet-pointed list of the requirements of this request, including
|
|
/// appropriate conjuntives. This text is intended to be glued to the end of an opening
|
|
/// portion of a sentence, e.g. "Please supply" + request.description(0, L'*').
|
|
std::wstring description(int description_flags, wchar_t bullet_point);
|
|
|
|
private:
|
|
struct Node {
|
|
enum class Type {
|
|
Any, All, One
|
|
};
|
|
Type type = Type::One;
|
|
Name name = Name::None;
|
|
/// @c true if this ROM is optional for machine startup. Generally indicates something
|
|
/// that would make emulation more accurate, but not sufficiently so to make it
|
|
/// a necessity.
|
|
bool is_optional = false;
|
|
std::vector<Node> children;
|
|
|
|
bool empty() const {
|
|
return type == Type::One && name == Name::None;
|
|
}
|
|
|
|
void add_descriptions(std::vector<Description> &) const;
|
|
bool validate(Map &) const;
|
|
void visit(
|
|
const std::function<void(ListType, size_t)> &enter_list,
|
|
const std::function<void(void)> &exit_list,
|
|
const std::function<void(ROM::Request::ListType type, const ROM::Description &, bool is_optional, size_t remaining)> &add_item
|
|
) const;
|
|
bool subtract(const ROM::Map &map);
|
|
void sort() {
|
|
// Don't do a full sort, but move anything optional to the back.
|
|
// This makes them print more nicely; it's a human-facing tweak only.
|
|
ssize_t index = ssize_t(children.size() - 1);
|
|
bool has_seen_non_optional = false;
|
|
while(index >= 0) {
|
|
has_seen_non_optional |= !children[size_t(index)].is_optional;
|
|
if(children[size_t(index)].is_optional && has_seen_non_optional) {
|
|
std::rotate(children.begin() + index, children.begin() + index + 1, children.end());
|
|
}
|
|
--index;
|
|
}
|
|
}
|
|
};
|
|
Node node;
|
|
|
|
Request append(Node::Type type, const Request &rhs);
|
|
};
|
|
|
|
}
|