Merge pull request #1777 from TomHarte/Prodest128
Support Olivetti Prodest 128.
This commit is contained in:
10 files changed
+238
-19
No files matched your search
@@ -21,7 +21,7 @@ struct MOTarget: public Analyser::Static::Target, public Reflection::StructImpl<
|
||||
ReflectableEnum(Floppy, None, CD90_640);
|
||||
Floppy floppy = Floppy::None;
|
||||
|
||||
ReflectableEnum(Model, MO5v1, MO5v11, MO6v1, MO6v2, MO6v3);
|
||||
ReflectableEnum(Model, MO5v1, MO5v11, MO6v1, MO6v2, MO6v3, Prodest128);
|
||||
Model model = Model::MO5v11;
|
||||
|
||||
MOTarget() : Analyser::Static::Target(Machine::ThomsonMO) {}
|
||||
|
||||
@@ -14,7 +14,7 @@
|
||||
namespace Thomson::MO::Keyboard {
|
||||
|
||||
enum class Machine {
|
||||
MO5, MO6
|
||||
MO5, MO6, Prodest128
|
||||
};
|
||||
|
||||
namespace MO5 {
|
||||
@@ -214,6 +214,111 @@ static inline std::unordered_map<wchar_t, const std::vector<uint16_t>> sequences
|
||||
};
|
||||
}
|
||||
|
||||
namespace Prodest128 {
|
||||
enum Key: uint16_t {
|
||||
k0 = 0x1e, k1 = 0x2f, k2 = 0x27, k3 = 0x1f,
|
||||
k4 = 0x17, k5 = 0x0f, k6 = 0x07, k7 = 0x06,
|
||||
k8 = 0x0e, k9 = 0x16,
|
||||
|
||||
Q = 0x2d, W = 0x25, E = 0x1d, R = 0x15,
|
||||
T = 0x0d, Y = 0x05, U = 0x04, I = 0x0c,
|
||||
O = 0x14, P = 0x1c, A = 0x2b, S = 0x23,
|
||||
D = 0x1b, F = 0x13, G = 0x0b, H = 0x03,
|
||||
J = 0x02, K = 0x0a, L = 0x12, Z = 0x30,
|
||||
X = 0x28, C = 0x32, V = 0x2a, B = 0x22,
|
||||
N = 0x00, M = 0x08, NWithLine = 0x1a,
|
||||
|
||||
OpenSquareBracket = 0x18,
|
||||
CCedilla = 0x50, // ç
|
||||
Quote = 0x26,
|
||||
CloseSquareBracket = 0x2e,
|
||||
ACC = 0x36,
|
||||
|
||||
Stop = 0x37,
|
||||
InvertedQuestionMark = 0x58,
|
||||
Plus = 0x2c,
|
||||
|
||||
CloseAngleBracket = 0x40,
|
||||
InvertedExclamationMark = 0x60,
|
||||
Hash = 0x48,
|
||||
|
||||
Comma = 0x10,
|
||||
Dot = 0x24,
|
||||
Minus = 0x11,
|
||||
RAZ = 0x33,
|
||||
INS = 0x09,
|
||||
EFF = 0x01,
|
||||
|
||||
Enter = 0x34,
|
||||
Space = 0x20,
|
||||
|
||||
Up = 0x31,
|
||||
Down = 0x21,
|
||||
Left = 0x29,
|
||||
Right = 0x19,
|
||||
|
||||
F1 = 0x3b,
|
||||
F2 = 0x3c,
|
||||
F3 = 0x3d,
|
||||
F4 = 0x3e,
|
||||
F5 = 0x3f,
|
||||
|
||||
ShiftLock = 0x3a,
|
||||
BASIC = 0x39,
|
||||
Shift = 0x38,
|
||||
Control = 0x35,
|
||||
};
|
||||
|
||||
static inline std::unordered_map<wchar_t, const std::vector<uint16_t>> sequences = {
|
||||
{L'\n', {Enter}}, {L'\r', {Enter}},
|
||||
{L' ', {Space}},
|
||||
|
||||
{L'0', {k0}}, {L'1', {k1}}, {L'2', {k2}}, {L'3', {k3}}, {L'4', {k4}},
|
||||
{L'5', {k5}}, {L'6', {k6}}, {L'7', {k7}}, {L'8', {k8}}, {L'9', {k9}},
|
||||
|
||||
{L'A', {Shift, A}}, {L'B', {Shift, B}}, {L'C', {Shift, C}},
|
||||
{L'D', {Shift, D}}, {L'E', {Shift, E}}, {L'F', {Shift, F}},
|
||||
{L'G', {Shift, G}}, {L'H', {Shift, H}}, {L'I', {Shift, I}},
|
||||
{L'J', {Shift, J}}, {L'K', {Shift, K}}, {L'L', {Shift, L}},
|
||||
{L'M', {Shift, M}}, {L'N', {Shift, N}}, {L'O', {Shift, O}},
|
||||
{L'P', {Shift, P}}, {L'Q', {Shift, Q}}, {L'R', {Shift, R}},
|
||||
{L'S', {Shift, S}}, {L'T', {Shift, T}}, {L'U', {Shift, U}},
|
||||
{L'V', {Shift, V}}, {L'W', {Shift, W}}, {L'X', {Shift, X}},
|
||||
{L'Y', {Shift, Y}}, {L'Z', {Shift, Z}},
|
||||
|
||||
{L'a', {A}}, {L'b', {B}}, {L'c', {C}},
|
||||
{L'd', {D}}, {L'e', {E}}, {L'f', {F}},
|
||||
{L'g', {G}}, {L'h', {H}}, {L'i', {I}},
|
||||
{L'j', {J}}, {L'k', {K}}, {L'l', {L}},
|
||||
{L'm', {M}}, {L'n', {N}}, {L'o', {O}},
|
||||
{L'p', {P}}, {L'q', {Q}}, {L'r', {R}},
|
||||
{L's', {S}}, {L't', {T}}, {L'u', {U}},
|
||||
{L'v', {V}}, {L'w', {W}}, {L'x', {X}},
|
||||
{L'y', {Y}}, {L'z', {Z}},
|
||||
|
||||
{L'{', {Shift, OpenSquareBracket}}, {L'[', {OpenSquareBracket}},
|
||||
{L'=', {Shift, k0}}, {L'!', {Shift, k1}}, {L'\"', {Shift, k2}}, {L'§', {Shift, k3}}, {L'$', {Shift, k4}},
|
||||
{L'%', {Shift, k5}}, {L'&', {Shift, k6}}, {L'/', {Shift, k7}}, {L'(', {Shift, k8}}, {L')', {Shift, k9}},
|
||||
|
||||
{L'?', {Shift, CCedilla}}, {L'ç', {CCedilla}},
|
||||
{L'£', {Shift, Quote}}, {L'\'', {Quote}}, {L'`', {RAZ, Quote}},
|
||||
{L'}', {Shift, CloseSquareBracket}}, {L']', {CloseSquareBracket}},
|
||||
|
||||
{L'\\', {Shift, Stop}},
|
||||
|
||||
{L'@', {Shift, InvertedQuestionMark}}, {L'¿', {InvertedQuestionMark}},
|
||||
{L'*', {Shift, Plus}}, {L'+', {Plus}},
|
||||
|
||||
{L'<', {Shift, CloseAngleBracket}}, {L'>', {CloseAngleBracket}},
|
||||
{L'¨', {Shift, InvertedExclamationMark}}, {L'¡', {InvertedExclamationMark}},
|
||||
{L'^', {Shift, Hash}}, {L'#', {Hash}},
|
||||
|
||||
{L';', {Shift, Comma}}, {L',', {Comma}},
|
||||
{L':', {Shift, Dot}}, {L'.', {Dot}},
|
||||
{L'_', {Shift, Minus}}, {L'-', {Minus}},
|
||||
};
|
||||
}
|
||||
|
||||
struct KeyboardMapper: public MachineTypes::MappedKeyboardMachine::KeyboardMapper {
|
||||
KeyboardMapper(const Machine machine) : machine_(machine) {}
|
||||
|
||||
@@ -306,7 +411,8 @@ struct KeyboardMapper: public MachineTypes::MappedKeyboardMachine::KeyboardMappe
|
||||
case In::V: return V; case In::B: return B;
|
||||
case In::N: return N; case In::M: return M;
|
||||
|
||||
case In::Space: return Space;
|
||||
case In::Space: return Space;
|
||||
case In::Enter: return Enter;
|
||||
|
||||
case In::BackTick: return Hash;
|
||||
case In::Backslash: return CloseBracket;
|
||||
@@ -332,9 +438,80 @@ struct KeyboardMapper: public MachineTypes::MappedKeyboardMachine::KeyboardMappe
|
||||
case In::Insert: return INS;
|
||||
case In::Delete: return EFF;
|
||||
|
||||
case In::Tab: return Stop;
|
||||
|
||||
case In::LeftShift:
|
||||
case In::RightShift: return Shift;
|
||||
case In::LeftControl:
|
||||
case In::RightControl: return Control;
|
||||
case In::LeftOption:
|
||||
case In::RightOption: return BASIC;
|
||||
case In::LeftMeta:
|
||||
case In::RightMeta: return RAZ;
|
||||
case In::CapsLock: return ShiftLock;
|
||||
|
||||
case In::F1: return F1;
|
||||
case In::F2: return F2;
|
||||
case In::F3: return F3;
|
||||
case In::F4: return F4;
|
||||
case In::F5: return F5;
|
||||
}
|
||||
break;
|
||||
|
||||
case Machine::Prodest128:
|
||||
switch(key) {
|
||||
default: return MachineTypes::MappedKeyboardMachine::KeyNotMapped;
|
||||
|
||||
using enum Prodest128::Key;
|
||||
case In::k0: return k0; case In::k1: return k1;
|
||||
case In::k2: return k2; case In::k3: return k3;
|
||||
case In::k4: return k4; case In::k5: return k5;
|
||||
case In::k6: return k6; case In::k7: return k7;
|
||||
case In::k8: return k8; case In::k9: return k9;
|
||||
|
||||
case In::Q: return Q; case In::W: return W;
|
||||
case In::E: return E; case In::R: return R;
|
||||
case In::T: return T; case In::Y: return Y;
|
||||
case In::U: return U; case In::I: return I;
|
||||
case In::O: return O; case In::P: return P;
|
||||
case In::A: return A; case In::S: return S;
|
||||
case In::D: return D; case In::F: return F;
|
||||
case In::G: return G; case In::H: return H;
|
||||
case In::J: return J; case In::K: return K;
|
||||
case In::L: return L; case In::Z: return Z;
|
||||
case In::X: return X; case In::C: return C;
|
||||
case In::V: return V; case In::B: return B;
|
||||
case In::N: return N; case In::M: return M;
|
||||
|
||||
case In::Space: return Space;
|
||||
case In::Enter: return Enter;
|
||||
|
||||
case In::BackTick: return OpenSquareBracket;
|
||||
case In::Hyphen: return CCedilla;
|
||||
case In::Equals: return Quote;
|
||||
case In::Backslash: return CloseSquareBracket;
|
||||
|
||||
case In::Tab: return Stop;
|
||||
|
||||
case In::OpenSquareBracket: return InvertedQuestionMark;
|
||||
case In::CloseSquareBracket: return Plus;
|
||||
|
||||
case In::Semicolon: return NWithLine;
|
||||
case In::Quote: return InvertedExclamationMark;
|
||||
|
||||
case In::Comma: return Comma;
|
||||
case In::FullStop: return Dot;
|
||||
case In::ForwardSlash: return Minus;
|
||||
|
||||
case In::Up: return Up; case In::Down: return Down;
|
||||
case In::Left: return Left; case In::Right: return Right;
|
||||
|
||||
case In::Backspace: return ACC;
|
||||
case In::Insert: return INS;
|
||||
case In::Delete: return EFF;
|
||||
|
||||
case In::LeftShift:
|
||||
case In::RightShift: return Shift;
|
||||
case In::Tab:
|
||||
case In::LeftControl:
|
||||
case In::RightControl: return Control;
|
||||
case In::LeftOption:
|
||||
@@ -363,7 +540,15 @@ struct CharacterMapper: public ::Utility::CharacterMapper {
|
||||
CharacterMapper(const Machine machine) : machine_(machine) {}
|
||||
|
||||
std::span<const uint16_t> sequence_for_character(const wchar_t character) const final {
|
||||
return lookup_sequence(machine_ == Machine::MO6 ? MO6::sequences : MO5::sequences, character);
|
||||
const auto sequences = [&]() -> const std::unordered_map<wchar_t, const std::vector<uint16_t>> & {
|
||||
switch(machine_) {
|
||||
case Machine::MO5: return MO5::sequences;
|
||||
case Machine::MO6: return MO6::sequences;
|
||||
case Machine::Prodest128: return Prodest128::sequences;
|
||||
default: __builtin_unreachable();
|
||||
}
|
||||
};
|
||||
return lookup_sequence(sequences(), character);
|
||||
}
|
||||
|
||||
private:
|
||||
|
||||
@@ -39,6 +39,20 @@ static constexpr uint8_t MusicExpansionMask = 63;
|
||||
|
||||
using Target = Analyser::Static::Thomson::MOTarget;
|
||||
|
||||
constexpr Keyboard::Machine machine(const Target::Model model) {
|
||||
switch(model) {
|
||||
case Target::Model::MO5v1:
|
||||
case Target::Model::MO5v11:
|
||||
return Keyboard::Machine::MO5;
|
||||
case Target::Model::MO6v1:
|
||||
case Target::Model::MO6v2:
|
||||
case Target::Model::MO6v3:
|
||||
return Keyboard::Machine::MO6;
|
||||
case Target::Model::Prodest128:
|
||||
return Keyboard::Machine::Prodest128;
|
||||
}
|
||||
}
|
||||
|
||||
template <bool has_floppy, bool is_mo6>
|
||||
struct ConcreteMachine:
|
||||
public Activity::Source,
|
||||
@@ -54,7 +68,7 @@ struct ConcreteMachine:
|
||||
public Utility::TypeRecipient<Thomson::MO::Keyboard::CharacterMapper>
|
||||
{
|
||||
ConcreteMachine(const Target &target, const ROMMachine::ROMFetcher &rom_fetcher) :
|
||||
Utility::TypeRecipient<Thomson::MO::Keyboard::CharacterMapper>(is_mo6 ? Thomson::MO::Keyboard::Machine::MO6 : Thomson::MO::Keyboard::Machine::MO5),
|
||||
Utility::TypeRecipient<Thomson::MO::Keyboard::CharacterMapper>(machine(target.model)),
|
||||
m6809_(*this),
|
||||
system_pia_port_handler_(*this),
|
||||
system_pia_(system_pia_port_handler_),
|
||||
@@ -64,7 +78,7 @@ struct ConcreteMachine:
|
||||
tape_player_(ClockRate),
|
||||
audio_(audio_queue_, MusicExpansionMask),
|
||||
speaker_(audio_),
|
||||
keyboard_mapper_(is_mo6 ? Thomson::MO::Keyboard::Machine::MO6 : Thomson::MO::Keyboard::Machine::MO5)
|
||||
keyboard_mapper_(machine(target.model))
|
||||
{
|
||||
set_clock_rate(ClockRate);
|
||||
speaker_.set_input_rate(ClockRate);
|
||||
@@ -74,12 +88,13 @@ struct ConcreteMachine:
|
||||
switch(model) {
|
||||
using enum Target::Model;
|
||||
|
||||
default: __builtin_unreachable();
|
||||
case MO5v1: return ROM::Name::ThomsonMO5v1;
|
||||
case MO5v11: return ROM::Name::ThomsonMO5v11;
|
||||
case MO6v1: return ROM::Name::ThomsonMO6v1;
|
||||
case MO6v2: return ROM::Name::ThomsonMO6v2;
|
||||
case MO6v3: return ROM::Name::ThomsonMO6v3;
|
||||
default: __builtin_unreachable();
|
||||
case MO5v1: return ROM::Name::ThomsonMO5v1;
|
||||
case MO5v11: return ROM::Name::ThomsonMO5v11;
|
||||
case MO6v1: return ROM::Name::ThomsonMO6v1;
|
||||
case MO6v2: return ROM::Name::ThomsonMO6v2;
|
||||
case MO6v3: return ROM::Name::ThomsonMO6v3;
|
||||
case Prodest128: return ROM::Name::OlivettiProdest128;
|
||||
}
|
||||
} (target.model);
|
||||
|
||||
|
||||
@@ -1060,6 +1060,14 @@ const std::vector<Description> &Description::all_roms() {
|
||||
64_kb,
|
||||
0x7a012322u
|
||||
},
|
||||
{
|
||||
OlivettiProdest128,
|
||||
"Thomson",
|
||||
"the Olivetti Prodest 127 ROM",
|
||||
"pc128.rom",
|
||||
64_kb,
|
||||
0xfc173504u
|
||||
},
|
||||
|
||||
{
|
||||
ThomsonCD90_640,
|
||||
|
||||
@@ -181,6 +181,7 @@ enum Name {
|
||||
ThomsonMO6v1,
|
||||
ThomsonMO6v2,
|
||||
ThomsonMO6v3,
|
||||
OlivettiProdest128,
|
||||
|
||||
ThomsonCD90_640,
|
||||
|
||||
|
||||
@@ -157,6 +157,7 @@ typedef NS_ENUM(NSInteger, CSPCCompatibleVideoAdaptor) {
|
||||
typedef NS_ENUM(NSInteger, CSMachineThomsonModel) {
|
||||
CSMachineThomsonModelMO5,
|
||||
CSMachineThomsonModelMO6,
|
||||
CSMachineThomsonModelProdest128,
|
||||
};
|
||||
|
||||
typedef int Kilobytes;
|
||||
|
||||
@@ -552,7 +552,14 @@ PermissionDelegate permission_delegate;
|
||||
if(self) {
|
||||
using Target = Analyser::Static::Thomson::MOTarget;
|
||||
auto target = std::make_unique<Target>();
|
||||
target->model = model == CSMachineThomsonModelMO5 ? Target::Model::MO5v11 : Target::Model::MO6v3;
|
||||
target->model = [&]() {
|
||||
switch(model) {
|
||||
case CSMachineThomsonModelMO5: return Target::Model::MO5v11;
|
||||
case CSMachineThomsonModelMO6: return Target::Model::MO6v3;
|
||||
case CSMachineThomsonModelProdest128: return Target::Model::Prodest128;
|
||||
default: __builtin_unreachable();
|
||||
}
|
||||
} ();
|
||||
target->floppy = hasDiskDrive ? Target::Floppy::CD90_640 : Target::Floppy::None;
|
||||
_targets.push_back(std::move(target));
|
||||
}
|
||||
|
||||
@@ -1016,7 +1016,7 @@ Gw
|
||||
</textFieldCell>
|
||||
</textField>
|
||||
<popUpButton verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="FoB-T7-dKp">
|
||||
<rect key="frame" x="80" y="264" width="69" height="25"/>
|
||||
<rect key="frame" x="80" y="264" width="158" height="25"/>
|
||||
<popUpButtonCell key="cell" type="push" title="MO5" bezelStyle="rounded" alignment="left" lineBreakMode="truncatingTail" state="on" borderStyle="borderAndBezel" tag="5" imageScaling="proportionallyDown" inset="2" selectedItem="wjb-gR-57f" id="a51-DE-sYL">
|
||||
<behavior key="behavior" lightByBackground="YES" lightByGray="YES"/>
|
||||
<font key="font" metaFont="message"/>
|
||||
@@ -1024,6 +1024,7 @@ Gw
|
||||
<items>
|
||||
<menuItem title="MO5" state="on" tag="5" id="wjb-gR-57f"/>
|
||||
<menuItem title="MO6" tag="6" id="8Hf-Pj-AM3"/>
|
||||
<menuItem title="Olivetti Prodest 128" tag="128" id="7GI-C0-Snt"/>
|
||||
</items>
|
||||
</menu>
|
||||
</popUpButtonCell>
|
||||
|
||||
@@ -495,6 +495,7 @@ class MachinePicker: NSObject, NSTableViewDataSource, NSTableViewDelegate, NSPat
|
||||
switch thomsonModelTypeButton.selectedTag() {
|
||||
case 5: model = .MO5
|
||||
case 6: model = .MO6
|
||||
case 128: model = .prodest128
|
||||
default: break
|
||||
}
|
||||
return CSStaticAnalyser(thomsonMOModel: model, hasDiskDrive: thomsonDiskButton.state == .on)
|
||||
|
||||
@@ -27,10 +27,10 @@
|
||||
Aside: since both +127 and -127 can be stored in a byte, it's unclear to me how this special case adds anything.
|
||||
It's therefore possible that machine translation has failed here.
|
||||
|
||||
On "units of time" the documentation says merely:
|
||||
On "units of time" the documentation says opaquely:
|
||||
"This duration N is expressed in the unit specified as a parameter"
|
||||
|
||||
So it isn't obviously captured in the file itself. Great work! I've picked an empirical value below.
|
||||
But I think it's usually or always 50µs.
|
||||
*/
|
||||
|
||||
using namespace Storage::Tape;
|
||||
@@ -44,8 +44,8 @@ std::unique_ptr<FormatSerialiser> LEP::format_serialiser() const {
|
||||
LEP::Serialiser::Serialiser(const std::string &name) : file_(name, FileMode::Read) {
|
||||
// Empirically: a length of about 16 in a LEP means "a full pulse", i.e. 833µs or thereabouts.
|
||||
//
|
||||
// So this scales 16 pulses to be exactly 1/1200th of a second.
|
||||
pulse_.length.clock_rate = 16 * 1200;
|
||||
// That seems to gel with the 50µs clock precision guess.
|
||||
pulse_.length.clock_rate = 20'000;
|
||||
}
|
||||
|
||||
void LEP::Serialiser::reset() {
|
||||
|
||||
Reference in new issue
Block a user