mirror of
https://github.com/TomHarte/CLK.git
synced 2025-10-25 09:27:01 +00:00
Compare commits
364 Commits
2018-04-07
...
2018-07-05
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
3e2d271566 | ||
|
|
c97c5fa03a | ||
|
|
fa63f7ffc3 | ||
|
|
bfccadd356 | ||
|
|
5b3512f1df | ||
|
|
6e34e60f8a | ||
|
|
a391d0f4ae | ||
|
|
abc5c50b2e | ||
|
|
1fcb461c42 | ||
|
|
abca38a548 | ||
|
|
b4be2cd063 | ||
|
|
2d83eeb9c4 | ||
|
|
4d9e897cc3 | ||
|
|
be664b5695 | ||
|
|
c3751066b7 | ||
|
|
77feee8197 | ||
|
|
f75af3b45e | ||
|
|
1471a35bb8 | ||
|
|
555c2a4377 | ||
|
|
16bef0dcd5 | ||
|
|
cd464fc7de | ||
|
|
5b88207477 | ||
|
|
df8c896193 | ||
|
|
5d3e1f7084 | ||
|
|
59f8eeb05a | ||
|
|
0b14850467 | ||
|
|
f72e260915 | ||
|
|
640a84d456 | ||
|
|
04f6cb1750 | ||
|
|
87d688b7e3 | ||
|
|
26141a59b0 | ||
|
|
a93f8103ad | ||
|
|
4a3d7c338a | ||
|
|
55ab305dbf | ||
|
|
e48ba89721 | ||
|
|
9bb55b6b61 | ||
|
|
c33308bdc5 | ||
|
|
44a33941bf | ||
|
|
cc34cd2133 | ||
|
|
52c9f9e89e | ||
|
|
2363deb19c | ||
|
|
1c6af279b2 | ||
|
|
6e96275e1c | ||
|
|
9968342a11 | ||
|
|
c248ecde48 | ||
|
|
370952ab33 | ||
|
|
154c89e041 | ||
|
|
d45f1a793d | ||
|
|
9800951f18 | ||
|
|
17251997c2 | ||
|
|
5ab4cfee84 | ||
|
|
a9eb0d02c6 | ||
|
|
1f8b69a5b0 | ||
|
|
8b83f58d7a | ||
|
|
9a91ae38c1 | ||
|
|
ad57caed5e | ||
|
|
283ed8dbae | ||
|
|
acb74185d5 | ||
|
|
7a5d16ccf8 | ||
|
|
adca862166 | ||
|
|
1bdc718527 | ||
|
|
685a80f95b | ||
|
|
62eef8cb40 | ||
|
|
6ed3a49fe1 | ||
|
|
17702bfb89 | ||
|
|
292e02702a | ||
|
|
5a56d8a5d0 | ||
|
|
3da1d5700c | ||
|
|
d437e06e15 | ||
|
|
6a3702a5c7 | ||
|
|
83a654540a | ||
|
|
678bd93c52 | ||
|
|
1bf0c1891a | ||
|
|
b899a22c7d | ||
|
|
1bd6bbca8d | ||
|
|
14a2e470e4 | ||
|
|
41dcf1de42 | ||
|
|
0c65385c82 | ||
|
|
4aaf43150a | ||
|
|
f05ee525cb | ||
|
|
1172c4fd97 | ||
|
|
15deef50c8 | ||
|
|
7728adfc5a | ||
|
|
eff67f2250 | ||
|
|
64e3cf5de2 | ||
|
|
31a6d620e8 | ||
|
|
dfd37e7dec | ||
|
|
8d8f244bf5 | ||
|
|
037b4802db | ||
|
|
51da21b844 | ||
|
|
0be19d8de7 | ||
|
|
f26e4734b3 | ||
|
|
f1b430338e | ||
|
|
2954373115 | ||
|
|
42d21ea3a9 | ||
|
|
7d761f145f | ||
|
|
27657fcde0 | ||
|
|
3ea2a4ccb8 | ||
|
|
a1c60152d4 | ||
|
|
69da00fcfb | ||
|
|
c4108efc5c | ||
|
|
d576ff1172 | ||
|
|
af54666c23 | ||
|
|
e0b75b6e3d | ||
|
|
12c59ede09 | ||
|
|
b4d0d4fff6 | ||
|
|
a694844190 | ||
|
|
28f2d331a8 | ||
|
|
dde9b73a22 | ||
|
|
fb4bb21bf6 | ||
|
|
744c35b617 | ||
|
|
9ac21a4e71 | ||
|
|
94359e9c75 | ||
|
|
076fa55651 | ||
|
|
d380595ad4 | ||
|
|
d84b8700a3 | ||
|
|
80b281d9f1 | ||
|
|
69dc3cc4d8 | ||
|
|
1a9cea050e | ||
|
|
0833412df9 | ||
|
|
35e84ff1a8 | ||
|
|
8dd7c6ef23 | ||
|
|
a26ab7086d | ||
|
|
b2464598d0 | ||
|
|
6812a001d8 | ||
|
|
6c16754a6b | ||
|
|
75f9e3caeb | ||
|
|
ad5afe21ee | ||
|
|
8a566cc1dd | ||
|
|
928aab13dc | ||
|
|
f3fe711542 | ||
|
|
db8d8d8404 | ||
|
|
6220ccb5d3 | ||
|
|
20843305dd | ||
|
|
8f6c0f6a8d | ||
|
|
ede2df7e70 | ||
|
|
d45231c1a8 | ||
|
|
772812b35f | ||
|
|
f443fd44b5 | ||
|
|
79c60b8984 | ||
|
|
2dc2c2ce79 | ||
|
|
523ca3264b | ||
|
|
4036c60b45 | ||
|
|
7d652e53e2 | ||
|
|
7c3dd55e5c | ||
|
|
1b43381be0 | ||
|
|
8f78e5039e | ||
|
|
57ee6d4e41 | ||
|
|
2868b1eca7 | ||
|
|
a4d7703efd | ||
|
|
ca4bc92c33 | ||
|
|
853261364e | ||
|
|
d3c5e4267f | ||
|
|
086b801c29 | ||
|
|
f9c25372c2 | ||
|
|
ea92363e6c | ||
|
|
015f692bd3 | ||
|
|
80d34f5511 | ||
|
|
e482929da8 | ||
|
|
4952657b31 | ||
|
|
46fae1a761 | ||
|
|
a09fb01d71 | ||
|
|
7cee3b7449 | ||
|
|
8263c48a1d | ||
|
|
ed06533e60 | ||
|
|
7b7beb13a3 | ||
|
|
c46007332a | ||
|
|
908d3b0ee5 | ||
|
|
8a031b1137 | ||
|
|
1aba9f807e | ||
|
|
4c49963988 | ||
|
|
821d40fe74 | ||
|
|
6ab1cf9325 | ||
|
|
076c0a48e9 | ||
|
|
fde613a5c4 | ||
|
|
44ad0970be | ||
|
|
b3f4d0ed8c | ||
|
|
bfdd3468ea | ||
|
|
f7decd80b6 | ||
|
|
7c2721d54d | ||
|
|
8907d0a9a7 | ||
|
|
6c8e6e9303 | ||
|
|
85c4e009f3 | ||
|
|
76802b5e38 | ||
|
|
9f2f388e5a | ||
|
|
729f53d84f | ||
|
|
d2d7ab5d04 | ||
|
|
5107c7c23d | ||
|
|
4dbd1f1358 | ||
|
|
7996040f35 | ||
|
|
0055efb720 | ||
|
|
dfa5eef20d | ||
|
|
3053acb4f3 | ||
|
|
dea9892a85 | ||
|
|
b9b6327707 | ||
|
|
84ae2964fd | ||
|
|
149b940f84 | ||
|
|
7226d8d4f7 | ||
|
|
ad9b0cd4e3 | ||
|
|
484e640d43 | ||
|
|
5d6b5d9f10 | ||
|
|
0b771ce61a | ||
|
|
72e07d4e83 | ||
|
|
2252c29495 | ||
|
|
39c0bc6c47 | ||
|
|
8f1a516a2c | ||
|
|
a6b8e88406 | ||
|
|
c19b50619f | ||
|
|
3747d96b22 | ||
|
|
8b23a08fc4 | ||
|
|
3fdefb94e4 | ||
|
|
49592ebaf3 | ||
|
|
f410dcb3f3 | ||
|
|
bd27f61a03 | ||
|
|
d703328114 | ||
|
|
afe222cb16 | ||
|
|
d0fd4dd4db | ||
|
|
3ba6b6f1ee | ||
|
|
bc464e247f | ||
|
|
c23f6d8d19 | ||
|
|
39d779edf0 | ||
|
|
0cb5362c6f | ||
|
|
a43ca0db35 | ||
|
|
9089bf6535 | ||
|
|
ef19a03efc | ||
|
|
85e1610627 | ||
|
|
d16ae84d0b | ||
|
|
95f859cf5c | ||
|
|
578a5b3e69 | ||
|
|
25f7e3af31 | ||
|
|
86192b18d1 | ||
|
|
c3144382c5 | ||
|
|
6bb9b7be04 | ||
|
|
8ee34fafa6 | ||
|
|
312171fa59 | ||
|
|
a8dbfb0569 | ||
|
|
b09b4b4433 | ||
|
|
45bd24ada0 | ||
|
|
c3a2f7717b | ||
|
|
70e6c3b2f6 | ||
|
|
d1b889aa61 | ||
|
|
f65c65569a | ||
|
|
1139caa83f | ||
|
|
d613c3c187 | ||
|
|
36f8b165cf | ||
|
|
d6e8b34942 | ||
|
|
4c4ab25d0e | ||
|
|
9ff34d90f4 | ||
|
|
9593e0f7fe | ||
|
|
1293d8b69e | ||
|
|
3e0055737e | ||
|
|
ba7fbc4032 | ||
|
|
c36d7b4972 | ||
|
|
1c0b5bb02b | ||
|
|
0dece80b5d | ||
|
|
e3b4aebf1a | ||
|
|
2e20191c01 | ||
|
|
59718e132b | ||
|
|
4d070fbfe3 | ||
|
|
723ee88043 | ||
|
|
65ba9ee6e7 | ||
|
|
fcc750784a | ||
|
|
3787d094ec | ||
|
|
4b4ea4a103 | ||
|
|
af0cf0d40a | ||
|
|
eecea93b3b | ||
|
|
ac4948c4b1 | ||
|
|
5e34c1b6b8 | ||
|
|
05e31d7594 | ||
|
|
f4097290c2 | ||
|
|
9da481b060 | ||
|
|
79002d6962 | ||
|
|
dbd9282efc | ||
|
|
b32538f3c8 | ||
|
|
e7618bb32e | ||
|
|
aacf26f05d | ||
|
|
265bc80d44 | ||
|
|
10c0e687f5 | ||
|
|
a9d4fe0b41 | ||
|
|
5cd15147eb | ||
|
|
c62db6665a | ||
|
|
fabcb261dc | ||
|
|
45cf28e0eb | ||
|
|
5b35c88be2 | ||
|
|
7f03f5d02f | ||
|
|
b98d5b790a | ||
|
|
5c74044e62 | ||
|
|
992a99d792 | ||
|
|
41075356e2 | ||
|
|
850a394eb5 | ||
|
|
244721a6f8 | ||
|
|
d59db504a3 | ||
|
|
c90e122eb2 | ||
|
|
4c6dc597f4 | ||
|
|
b4f6dee954 | ||
|
|
2685e9087e | ||
|
|
376b26c1e4 | ||
|
|
7061537ff5 | ||
|
|
2f2390b5aa | ||
|
|
99de8f1c5c | ||
|
|
af61bbc3e2 | ||
|
|
56d88f23ef | ||
|
|
4bff44377a | ||
|
|
7463edaa1b | ||
|
|
e92e06a5f4 | ||
|
|
4cbe5068a9 | ||
|
|
38b2302b59 | ||
|
|
bce0702745 | ||
|
|
d447e81abd | ||
|
|
6592745e53 | ||
|
|
e87a3cffd4 | ||
|
|
fa0b6e8a08 | ||
|
|
074b4c3500 | ||
|
|
5968c9a391 | ||
|
|
72bc5f8d7b | ||
|
|
0a0d81cd5a | ||
|
|
75e9c3678b | ||
|
|
aebe8a64a2 | ||
|
|
1aacf437b5 | ||
|
|
7e8e3fdd39 | ||
|
|
b8ae283049 | ||
|
|
6621e54952 | ||
|
|
e03a403a51 | ||
|
|
ba43b3e6b8 | ||
|
|
b4a2d1395c | ||
|
|
f5ae8d0f79 | ||
|
|
5f1c210746 | ||
|
|
f6c2f6e896 | ||
|
|
6547560e52 | ||
|
|
a167e3849b | ||
|
|
f22c23cb4c | ||
|
|
a07c99d778 | ||
|
|
1c605d58e3 | ||
|
|
6a79ce9eb1 | ||
|
|
465c38f03c | ||
|
|
be05d51e07 | ||
|
|
9bc470027e | ||
|
|
335c633884 | ||
|
|
cd26f11818 | ||
|
|
abe47b6ed8 | ||
|
|
61659faeaa | ||
|
|
71adb964e5 | ||
|
|
e599e65087 | ||
|
|
7efee9b52b | ||
|
|
079dc671e1 | ||
|
|
a32a7d1374 | ||
|
|
467cd5450f | ||
|
|
1580874a55 | ||
|
|
15f7cbe8c1 | ||
|
|
428b6145fa | ||
|
|
3ad0b31db8 | ||
|
|
8d4d5d1f46 | ||
|
|
4c8a68c6a4 | ||
|
|
0b4b6f4aec | ||
|
|
bb4db6b382 | ||
|
|
94b1c37fb2 | ||
|
|
cf6f6c5c15 | ||
|
|
f541986333 | ||
|
|
44513d6912 | ||
|
|
b20cbcd5fe | ||
|
|
1c5972f7b0 | ||
|
|
28947bb3c4 | ||
|
|
865c47a1ac | ||
|
|
3821679efd |
51
Activity/Observer.hpp
Normal file
51
Activity/Observer.hpp
Normal file
@@ -0,0 +1,51 @@
|
||||
//
|
||||
// ActivityObserver.h
|
||||
// Clock Signal
|
||||
//
|
||||
// Created by Thomas Harte on 07/05/2018.
|
||||
// Copyright 2018 Thomas Harte. All rights reserved.
|
||||
//
|
||||
|
||||
#ifndef ActivityObserver_h
|
||||
#define ActivityObserver_h
|
||||
|
||||
#include <string>
|
||||
|
||||
namespace Activity {
|
||||
|
||||
/*!
|
||||
Provides a purely virtual base class for anybody that wants to receive notifications of
|
||||
'activity': any feedback from an emulated system which a user could perceive other than
|
||||
through the machine's native audio and video outputs.
|
||||
|
||||
So: status LEDs, drive activity, etc. A receiver may choose to make appropriate noises
|
||||
and/or to show or unshow status indicators.
|
||||
*/
|
||||
class Observer {
|
||||
public:
|
||||
/// Announces to the receiver that there is an LED of name @c name.
|
||||
virtual void register_led(const std::string &name) = 0;
|
||||
|
||||
/// Announces to the receiver that there is a drive of name @c name.
|
||||
virtual void register_drive(const std::string &name) = 0;
|
||||
|
||||
/// Informs the receiver of the new state of the LED with name @c name.
|
||||
virtual void set_led_status(const std::string &name, bool lit) = 0;
|
||||
|
||||
enum class DriveEvent {
|
||||
StepNormal,
|
||||
StepBelowZero,
|
||||
StepBeyondMaximum
|
||||
};
|
||||
|
||||
/// Informs the receiver that the named event just occurred for the drive with name @c name.
|
||||
virtual void announce_drive_event(const std::string &name, DriveEvent event) = 0;
|
||||
|
||||
/// Informs the receiver of the motor-on status of the drive with name @c name.
|
||||
virtual void set_drive_motor_status(const std::string &name, bool is_on) = 0;
|
||||
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif /* ActivityObserver_h */
|
||||
24
Activity/Source.hpp
Normal file
24
Activity/Source.hpp
Normal file
@@ -0,0 +1,24 @@
|
||||
//
|
||||
// ActivitySource.h
|
||||
// Clock Signal
|
||||
//
|
||||
// Created by Thomas Harte on 07/05/2018.
|
||||
// Copyright 2018 Thomas Harte. All rights reserved.
|
||||
//
|
||||
|
||||
#ifndef ActivitySource_h
|
||||
#define ActivitySource_h
|
||||
|
||||
#include "Observer.hpp"
|
||||
|
||||
namespace Activity {
|
||||
|
||||
class Source {
|
||||
public:
|
||||
virtual void set_activity_observer(Observer *observer) = 0;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
|
||||
#endif /* ActivitySource_h */
|
||||
@@ -3,7 +3,7 @@
|
||||
// Clock Signal
|
||||
//
|
||||
// Created by Thomas Harte on 21/01/2018.
|
||||
// Copyright © 2018 Thomas Harte. All rights reserved.
|
||||
// Copyright 2018 Thomas Harte. All rights reserved.
|
||||
//
|
||||
|
||||
#include "ConfidenceCounter.hpp"
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
// Clock Signal
|
||||
//
|
||||
// Created by Thomas Harte on 21/01/2018.
|
||||
// Copyright © 2018 Thomas Harte. All rights reserved.
|
||||
// Copyright 2018 Thomas Harte. All rights reserved.
|
||||
//
|
||||
|
||||
#ifndef ConfidenceCounter_hpp
|
||||
@@ -24,10 +24,10 @@ class ConfidenceCounter: public ConfidenceSource {
|
||||
/*! @returns The computed probability, based on the history of events. */
|
||||
float get_confidence() override;
|
||||
|
||||
/*! Records an event that implies this is the appropriate class — pushes probability up towards 1.0. */
|
||||
/*! Records an event that implies this is the appropriate class: pushes probability up towards 1.0. */
|
||||
void add_hit();
|
||||
|
||||
/*! Records an event that implies this is not the appropriate class — pushes probability down towards 0.0. */
|
||||
/*! Records an event that implies this is not the appropriate class: pushes probability down towards 0.0. */
|
||||
void add_miss();
|
||||
|
||||
/*!
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
// Clock Signal
|
||||
//
|
||||
// Created by Thomas Harte on 21/01/2018.
|
||||
// Copyright © 2018 Thomas Harte. All rights reserved.
|
||||
// Copyright 2018 Thomas Harte. All rights reserved.
|
||||
//
|
||||
|
||||
#ifndef ConfidenceSource_hpp
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
// Clock Signal
|
||||
//
|
||||
// Created by Thomas Harte on 21/01/2018.
|
||||
// Copyright © 2018 Thomas Harte. All rights reserved.
|
||||
// Copyright 2018 Thomas Harte. All rights reserved.
|
||||
//
|
||||
|
||||
#include "ConfidenceSummary.hpp"
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
// Clock Signal
|
||||
//
|
||||
// Created by Thomas Harte on 21/01/2018.
|
||||
// Copyright © 2018 Thomas Harte. All rights reserved.
|
||||
// Copyright 2018 Thomas Harte. All rights reserved.
|
||||
//
|
||||
|
||||
#ifndef ConfidenceSummary_hpp
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
// Clock Signal
|
||||
//
|
||||
// Created by Thomas Harte on 29/01/2018.
|
||||
// Copyright © 2018 Thomas Harte. All rights reserved.
|
||||
// Copyright 2018 Thomas Harte. All rights reserved.
|
||||
//
|
||||
|
||||
#include "MultiCRTMachine.hpp"
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
// Clock Signal
|
||||
//
|
||||
// Created by Thomas Harte on 29/01/2018.
|
||||
// Copyright © 2018 Thomas Harte. All rights reserved.
|
||||
// Copyright 2018 Thomas Harte. All rights reserved.
|
||||
//
|
||||
|
||||
#ifndef MultiCRTMachine_hpp
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
// Clock Signal
|
||||
//
|
||||
// Created by Thomas Harte on 09/02/2018.
|
||||
// Copyright © 2018 Thomas Harte. All rights reserved.
|
||||
// Copyright 2018 Thomas Harte. All rights reserved.
|
||||
//
|
||||
|
||||
#include "MultiConfigurable.hpp"
|
||||
@@ -16,7 +16,7 @@ MultiConfigurable::MultiConfigurable(const std::vector<std::unique_ptr<::Machine
|
||||
for(const auto &machine: machines) {
|
||||
Configurable::Device *device = machine->configurable_device();
|
||||
if(device) devices_.push_back(device);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
std::vector<std::unique_ptr<Configurable::Option>> MultiConfigurable::get_options() {
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
// Clock Signal
|
||||
//
|
||||
// Created by Thomas Harte on 09/02/2018.
|
||||
// Copyright © 2018 Thomas Harte. All rights reserved.
|
||||
// Copyright 2018 Thomas Harte. All rights reserved.
|
||||
//
|
||||
|
||||
#ifndef MultiConfigurable_hpp
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
// Clock Signal
|
||||
//
|
||||
// Created by Thomas Harte on 29/01/2018.
|
||||
// Copyright © 2018 Thomas Harte. All rights reserved.
|
||||
// Copyright 2018 Thomas Harte. All rights reserved.
|
||||
//
|
||||
|
||||
#include "MultiConfigurationTarget.hpp"
|
||||
@@ -14,7 +14,7 @@ MultiConfigurationTarget::MultiConfigurationTarget(const std::vector<std::unique
|
||||
for(const auto &machine: machines) {
|
||||
ConfigurationTarget::Machine *configuration_target = machine->configuration_target();
|
||||
if(configuration_target) targets_.push_back(configuration_target);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void MultiConfigurationTarget::configure_as_target(const Analyser::Static::Target *target) {
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
// Clock Signal
|
||||
//
|
||||
// Created by Thomas Harte on 29/01/2018.
|
||||
// Copyright © 2018 Thomas Harte. All rights reserved.
|
||||
// Copyright 2018 Thomas Harte. All rights reserved.
|
||||
//
|
||||
|
||||
#ifndef MultiConfigurationTarget_hpp
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
// Clock Signal
|
||||
//
|
||||
// Created by Thomas Harte on 09/02/2018.
|
||||
// Copyright © 2018 Thomas Harte. All rights reserved.
|
||||
// Copyright 2018 Thomas Harte. All rights reserved.
|
||||
//
|
||||
|
||||
#include "MultiJoystickMachine.hpp"
|
||||
@@ -25,14 +25,14 @@ class MultiJoystick: public Inputs::Joystick {
|
||||
}
|
||||
}
|
||||
|
||||
std::vector<DigitalInput> get_inputs() override {
|
||||
std::vector<DigitalInput> inputs;
|
||||
|
||||
for(const auto &joystick: joysticks_) {
|
||||
std::vector<DigitalInput> joystick_inputs = joystick->get_inputs();
|
||||
for(const auto &input: joystick_inputs) {
|
||||
if(std::find(inputs.begin(), inputs.end(), input) != inputs.end()) {
|
||||
inputs.push_back(input);
|
||||
std::vector<Input> &get_inputs() override {
|
||||
if(inputs.empty()) {
|
||||
for(const auto &joystick: joysticks_) {
|
||||
std::vector<Input> joystick_inputs = joystick->get_inputs();
|
||||
for(const auto &input: joystick_inputs) {
|
||||
if(std::find(inputs.begin(), inputs.end(), input) != inputs.end()) {
|
||||
inputs.push_back(input);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -40,11 +40,18 @@ class MultiJoystick: public Inputs::Joystick {
|
||||
return inputs;
|
||||
}
|
||||
|
||||
void set_digital_input(const DigitalInput &digital_input, bool is_active) override {
|
||||
void set_input(const Input &digital_input, bool is_active) override {
|
||||
for(const auto &joystick: joysticks_) {
|
||||
joystick->set_digital_input(digital_input, is_active);
|
||||
joystick->set_input(digital_input, is_active);
|
||||
}
|
||||
}
|
||||
|
||||
void set_input(const Input &digital_input, float value) override {
|
||||
for(const auto &joystick: joysticks_) {
|
||||
joystick->set_input(digital_input, value);
|
||||
}
|
||||
}
|
||||
|
||||
void reset_all_inputs() override {
|
||||
for(const auto &joystick: joysticks_) {
|
||||
joystick->reset_all_inputs();
|
||||
@@ -52,6 +59,7 @@ class MultiJoystick: public Inputs::Joystick {
|
||||
}
|
||||
|
||||
private:
|
||||
std::vector<Input> inputs;
|
||||
std::vector<Inputs::Joystick *> joysticks_;
|
||||
};
|
||||
|
||||
@@ -66,7 +74,7 @@ MultiJoystickMachine::MultiJoystickMachine(const std::vector<std::unique_ptr<::M
|
||||
joystick_machines.push_back(joystick_machine);
|
||||
total_joysticks = std::max(total_joysticks, joystick_machine->get_joysticks().size());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for(std::size_t index = 0; index < total_joysticks; ++index) {
|
||||
joysticks_.emplace_back(new MultiJoystick(joystick_machines, index));
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
// Clock Signal
|
||||
//
|
||||
// Created by Thomas Harte on 09/02/2018.
|
||||
// Copyright © 2018 Thomas Harte. All rights reserved.
|
||||
// Copyright 2018 Thomas Harte. All rights reserved.
|
||||
//
|
||||
|
||||
#ifndef MultiJoystickMachine_hpp
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
// Clock Signal
|
||||
//
|
||||
// Created by Thomas Harte on 09/02/2018.
|
||||
// Copyright © 2018 Thomas Harte. All rights reserved.
|
||||
// Copyright 2018 Thomas Harte. All rights reserved.
|
||||
//
|
||||
|
||||
#include "MultiKeyboardMachine.hpp"
|
||||
@@ -14,30 +14,30 @@ MultiKeyboardMachine::MultiKeyboardMachine(const std::vector<std::unique_ptr<::M
|
||||
for(const auto &machine: machines) {
|
||||
KeyboardMachine::Machine *keyboard_machine = machine->keyboard_machine();
|
||||
if(keyboard_machine) machines_.push_back(keyboard_machine);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void MultiKeyboardMachine::clear_all_keys() {
|
||||
for(const auto &machine: machines_) {
|
||||
machine->clear_all_keys();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void MultiKeyboardMachine::set_key_state(uint16_t key, bool is_pressed) {
|
||||
for(const auto &machine: machines_) {
|
||||
machine->set_key_state(key, is_pressed);
|
||||
}
|
||||
for(const auto &machine: machines_) {
|
||||
machine->set_key_state(key, is_pressed);
|
||||
}
|
||||
}
|
||||
|
||||
void MultiKeyboardMachine::type_string(const std::string &string) {
|
||||
for(const auto &machine: machines_) {
|
||||
machine->type_string(string);
|
||||
}
|
||||
for(const auto &machine: machines_) {
|
||||
machine->type_string(string);
|
||||
}
|
||||
}
|
||||
|
||||
void MultiKeyboardMachine::keyboard_did_change_key(Inputs::Keyboard *keyboard, Inputs::Keyboard::Key key, bool is_pressed) {
|
||||
for(const auto &machine: machines_) {
|
||||
for(const auto &machine: machines_) {
|
||||
uint16_t mapped_key = machine->get_keyboard_mapper()->mapped_key_for_key(key);
|
||||
if(mapped_key != KeyNotMapped) machine->set_key_state(mapped_key, is_pressed);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
// Clock Signal
|
||||
//
|
||||
// Created by Thomas Harte on 09/02/2018.
|
||||
// Copyright © 2018 Thomas Harte. All rights reserved.
|
||||
// Copyright 2018 Thomas Harte. All rights reserved.
|
||||
//
|
||||
|
||||
#ifndef MultiKeyboardMachine_hpp
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
// Clock Signal
|
||||
//
|
||||
// Created by Thomas Harte on 18/02/2018.
|
||||
// Copyright © 2018 Thomas Harte. All rights reserved.
|
||||
// Copyright 2018 Thomas Harte. All rights reserved.
|
||||
//
|
||||
|
||||
#include "MultiSpeaker.hpp"
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
// Clock Signal
|
||||
//
|
||||
// Created by Thomas Harte on 18/02/2018.
|
||||
// Copyright © 2018 Thomas Harte. All rights reserved.
|
||||
// Copyright 2018 Thomas Harte. All rights reserved.
|
||||
//
|
||||
|
||||
#ifndef MultiSpeaker_hpp
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
// Clock Signal
|
||||
//
|
||||
// Created by Thomas Harte on 28/01/2018.
|
||||
// Copyright © 2018 Thomas Harte. All rights reserved.
|
||||
// Copyright 2018 Thomas Harte. All rights reserved.
|
||||
//
|
||||
|
||||
#include "MultiMachine.hpp"
|
||||
@@ -22,6 +22,10 @@ MultiMachine::MultiMachine(std::vector<std::unique_ptr<DynamicMachine>> &&machin
|
||||
crt_machine_.set_delegate(this);
|
||||
}
|
||||
|
||||
Activity::Source *MultiMachine::activity_source() {
|
||||
return nullptr; // TODO
|
||||
}
|
||||
|
||||
ConfigurationTarget::Machine *MultiMachine::configuration_target() {
|
||||
if(has_picked_) {
|
||||
return machines_.front()->configuration_target();
|
||||
@@ -82,11 +86,11 @@ void MultiMachine::multi_crt_did_run_machines() {
|
||||
|
||||
DynamicMachine *front = machines_.front().get();
|
||||
std::stable_sort(machines_.begin(), machines_.end(),
|
||||
[] (const std::unique_ptr<DynamicMachine> &lhs, const std::unique_ptr<DynamicMachine> &rhs){
|
||||
CRTMachine::Machine *lhs_crt = lhs->crt_machine();
|
||||
CRTMachine::Machine *rhs_crt = rhs->crt_machine();
|
||||
return lhs_crt->get_confidence() > rhs_crt->get_confidence();
|
||||
});
|
||||
[] (const std::unique_ptr<DynamicMachine> &lhs, const std::unique_ptr<DynamicMachine> &rhs){
|
||||
CRTMachine::Machine *lhs_crt = lhs->crt_machine();
|
||||
CRTMachine::Machine *rhs_crt = rhs->crt_machine();
|
||||
return lhs_crt->get_confidence() > rhs_crt->get_confidence();
|
||||
});
|
||||
|
||||
if(machines_.front().get() != front) {
|
||||
crt_machine_.did_change_machine_order();
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
// Clock Signal
|
||||
//
|
||||
// Created by Thomas Harte on 28/01/2018.
|
||||
// Copyright © 2018 Thomas Harte. All rights reserved.
|
||||
// Copyright 2018 Thomas Harte. All rights reserved.
|
||||
//
|
||||
|
||||
#ifndef MultiMachine_hpp
|
||||
@@ -50,6 +50,7 @@ class MultiMachine: public ::Machine::DynamicMachine, public MultiCRTMachine::De
|
||||
static bool would_collapse(const std::vector<std::unique_ptr<DynamicMachine>> &machines);
|
||||
MultiMachine(std::vector<std::unique_ptr<DynamicMachine>> &&machines);
|
||||
|
||||
Activity::Source *activity_source() override;
|
||||
ConfigurationTarget::Machine *configuration_target() override;
|
||||
CRTMachine::Machine *crt_machine() override;
|
||||
JoystickMachine::Machine *joystick_machine() override;
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
// Clock Signal
|
||||
//
|
||||
// Created by Thomas Harte on 24/01/2018.
|
||||
// Copyright © 2018 Thomas Harte. All rights reserved.
|
||||
// Copyright 2018 Thomas Harte. All rights reserved.
|
||||
//
|
||||
|
||||
#ifndef Machines_h
|
||||
@@ -13,6 +13,7 @@ namespace Analyser {
|
||||
|
||||
enum class Machine {
|
||||
AmstradCPC,
|
||||
AppleII,
|
||||
Atari2600,
|
||||
ColecoVision,
|
||||
Electron,
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
// Clock Signal
|
||||
//
|
||||
// Created by Thomas Harte on 18/09/2016.
|
||||
// Copyright © 2016 Thomas Harte. All rights reserved.
|
||||
// Copyright 2016 Thomas Harte. All rights reserved.
|
||||
//
|
||||
|
||||
#include "Disk.hpp"
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
// Clock Signal
|
||||
//
|
||||
// Created by Thomas Harte on 18/09/2016.
|
||||
// Copyright © 2016 Thomas Harte. All rights reserved.
|
||||
// Copyright 2016 Thomas Harte. All rights reserved.
|
||||
//
|
||||
|
||||
#ifndef StaticAnalyser_Acorn_Disk_hpp
|
||||
@@ -16,7 +16,7 @@ namespace Analyser {
|
||||
namespace Static {
|
||||
namespace Acorn {
|
||||
|
||||
/// Describes a DFS- or ADFS-format catalogue(/directory) — the list of files available and the catalogue's boot option.
|
||||
/// Describes a DFS- or ADFS-format catalogue(/directory): the list of files available and the catalogue's boot option.
|
||||
struct Catalogue {
|
||||
std::string name;
|
||||
std::vector<File> files;
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
// Clock Signal
|
||||
//
|
||||
// Created by Thomas Harte on 18/09/2016.
|
||||
// Copyright © 2016 Thomas Harte. All rights reserved.
|
||||
// Copyright 2016 Thomas Harte. All rights reserved.
|
||||
//
|
||||
|
||||
#ifndef StaticAnalyser_Acorn_File_hpp
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
// Clock Signal
|
||||
//
|
||||
// Created by Thomas Harte on 29/08/2016.
|
||||
// Copyright © 2016 Thomas Harte. All rights reserved.
|
||||
// Copyright 2016 Thomas Harte. All rights reserved.
|
||||
//
|
||||
|
||||
#include "StaticAnalyser.hpp"
|
||||
@@ -50,14 +50,14 @@ static std::vector<std::shared_ptr<Storage::Cartridge::Cartridge>>
|
||||
// 1/(2^32) *
|
||||
// ( ((2^24)-1)/(2^24)*(1/4) + 1/(2^24) ) *
|
||||
// 1/4
|
||||
// = something very improbable — around 1/16th of 1 in 2^32, but not exactly.
|
||||
// = something very improbable, around 1/16th of 1 in 2^32, but not exactly.
|
||||
acorn_cartridges.push_back(cartridge);
|
||||
}
|
||||
|
||||
return acorn_cartridges;
|
||||
}
|
||||
|
||||
void Analyser::Static::Acorn::AddTargets(const Media &media, std::vector<std::unique_ptr<::Analyser::Static::Target>> &destination) {
|
||||
Analyser::Static::TargetList Analyser::Static::Acorn::GetTargets(const Media &media, const std::string &file_name, TargetPlatform::IntType potential_platforms) {
|
||||
std::unique_ptr<Target> target(new Target);
|
||||
target->machine = Machine::Electron;
|
||||
target->confidence = 0.5; // TODO: a proper estimation
|
||||
@@ -121,7 +121,9 @@ void Analyser::Static::Acorn::AddTargets(const Media &media, std::vector<std::un
|
||||
}
|
||||
}
|
||||
|
||||
if(target->media.tapes.size() || target->media.disks.size() || target->media.cartridges.size()) {
|
||||
destination.push_back(std::move(target));
|
||||
TargetList targets;
|
||||
if(!target->media.empty()) {
|
||||
targets.push_back(std::move(target));
|
||||
}
|
||||
return targets;
|
||||
}
|
||||
|
||||
@@ -3,19 +3,21 @@
|
||||
// Clock Signal
|
||||
//
|
||||
// Created by Thomas Harte on 29/08/2016.
|
||||
// Copyright © 2016 Thomas Harte. All rights reserved.
|
||||
// Copyright 2016 Thomas Harte. All rights reserved.
|
||||
//
|
||||
|
||||
#ifndef StaticAnalyser_Acorn_StaticAnalyser_hpp
|
||||
#define StaticAnalyser_Acorn_StaticAnalyser_hpp
|
||||
|
||||
#include "../StaticAnalyser.hpp"
|
||||
#include "../../../Storage/TargetPlatforms.hpp"
|
||||
#include <string>
|
||||
|
||||
namespace Analyser {
|
||||
namespace Static {
|
||||
namespace Acorn {
|
||||
|
||||
void AddTargets(const Media &media, std::vector<std::unique_ptr<Target>> &destination);
|
||||
TargetList GetTargets(const Media &media, const std::string &file_name, TargetPlatform::IntType potential_platforms);
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
// Clock Signal
|
||||
//
|
||||
// Created by Thomas Harte on 29/08/2016.
|
||||
// Copyright © 2016 Thomas Harte. All rights reserved.
|
||||
// Copyright 2016 Thomas Harte. All rights reserved.
|
||||
//
|
||||
|
||||
#include "Tape.hpp"
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
// Clock Signal
|
||||
//
|
||||
// Created by Thomas Harte on 29/08/2016.
|
||||
// Copyright © 2016 Thomas Harte. All rights reserved.
|
||||
// Copyright 2016 Thomas Harte. All rights reserved.
|
||||
//
|
||||
|
||||
#ifndef StaticAnalyser_Acorn_Tape_hpp
|
||||
|
||||
@@ -3,13 +3,14 @@
|
||||
// Clock Signal
|
||||
//
|
||||
// Created by Thomas Harte on 09/03/2018.
|
||||
// Copyright © 2018 Thomas Harte. All rights reserved.
|
||||
// Copyright 2018 Thomas Harte. All rights reserved.
|
||||
//
|
||||
|
||||
#ifndef Analyser_Static_Acorn_Target_h
|
||||
#define Analyser_Static_Acorn_Target_h
|
||||
|
||||
#include "../StaticAnalyser.hpp"
|
||||
#include <string>
|
||||
|
||||
namespace Analyser {
|
||||
namespace Static {
|
||||
@@ -19,6 +20,7 @@ struct Target: public ::Analyser::Static::Target {
|
||||
bool has_adfs = false;
|
||||
bool has_dfs = false;
|
||||
bool should_shift_restart = false;
|
||||
std::string loading_command;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
// Clock Signal
|
||||
//
|
||||
// Created by Thomas Harte on 30/07/2017.
|
||||
// Copyright © 2017 Thomas Harte. All rights reserved.
|
||||
// Copyright 2017 Thomas Harte. All rights reserved.
|
||||
//
|
||||
|
||||
#include "StaticAnalyser.hpp"
|
||||
@@ -35,8 +35,8 @@ static bool is_implied_extension(const std::string &extension) {
|
||||
|
||||
static void right_trim(std::string &string) {
|
||||
string.erase(std::find_if(string.rbegin(), string.rend(), [](int ch) {
|
||||
return !std::isspace(ch);
|
||||
}).base(), string.end());
|
||||
return !std::isspace(ch);
|
||||
}).base(), string.end());
|
||||
}
|
||||
|
||||
static std::string RunCommandFor(const Storage::Disk::CPM::File &file) {
|
||||
@@ -64,14 +64,14 @@ static void InspectCatalogue(
|
||||
|
||||
std::vector<const Storage::Disk::CPM::File *> candidate_files;
|
||||
candidate_files.reserve(catalogue.files.size());
|
||||
for(auto &file : catalogue.files) {
|
||||
for(const auto &file : catalogue.files) {
|
||||
candidate_files.push_back(&file);
|
||||
}
|
||||
|
||||
// Remove all files with untypable characters.
|
||||
candidate_files.erase(
|
||||
std::remove_if(candidate_files.begin(), candidate_files.end(), [](const Storage::Disk::CPM::File *file) {
|
||||
for(auto c : file->name + file->type) {
|
||||
for(const auto c : file->name + file->type) {
|
||||
if(c < 32) return true;
|
||||
}
|
||||
return false;
|
||||
@@ -80,7 +80,7 @@ static void InspectCatalogue(
|
||||
|
||||
// If that leaves a mix of 'system' (i.e. hidden) and non-system files, remove the system files.
|
||||
bool are_all_system = true;
|
||||
for(auto file : candidate_files) {
|
||||
for(const auto &file : candidate_files) {
|
||||
if(!file->system) {
|
||||
are_all_system = false;
|
||||
break;
|
||||
@@ -137,13 +137,13 @@ static void InspectCatalogue(
|
||||
std::map<std::string, int> name_counts;
|
||||
std::map<std::string, std::size_t> indices_by_name;
|
||||
std::size_t index = 0;
|
||||
for(auto file : candidate_files) {
|
||||
for(const auto &file : candidate_files) {
|
||||
name_counts[file->name]++;
|
||||
indices_by_name[file->name] = index;
|
||||
index++;
|
||||
}
|
||||
if(name_counts.size() == 2) {
|
||||
for(auto &pair : name_counts) {
|
||||
for(const auto &pair : name_counts) {
|
||||
if(pair.second == 1) {
|
||||
target->loading_command = RunCommandFor(*candidate_files[indices_by_name[pair.first]]);
|
||||
return;
|
||||
@@ -179,7 +179,8 @@ static bool CheckBootSector(const std::shared_ptr<Storage::Disk::Disk> &disk, co
|
||||
return false;
|
||||
}
|
||||
|
||||
void Analyser::Static::AmstradCPC::AddTargets(const Media &media, std::vector<std::unique_ptr<Analyser::Static::Target>> &destination) {
|
||||
Analyser::Static::TargetList Analyser::Static::AmstradCPC::GetTargets(const Media &media, const std::string &file_name, TargetPlatform::IntType potential_platforms) {
|
||||
TargetList destination;
|
||||
std::unique_ptr<Target> target(new Target);
|
||||
target->machine = Machine::AmstradCPC;
|
||||
target->confidence = 0.5;
|
||||
@@ -213,7 +214,7 @@ void Analyser::Static::AmstradCPC::AddTargets(const Media &media, std::vector<st
|
||||
system_format.catalogue_allocation_bitmap = 0xc000;
|
||||
system_format.reserved_tracks = 2;
|
||||
|
||||
for(const auto &disk: media.disks) {
|
||||
for(auto &disk: media.disks) {
|
||||
// Check for an ordinary catalogue.
|
||||
std::unique_ptr<Storage::Disk::CPM::Catalogue> data_catalogue = Storage::Disk::CPM::GetCatalogue(disk, data_format);
|
||||
if(data_catalogue) {
|
||||
@@ -241,4 +242,6 @@ void Analyser::Static::AmstradCPC::AddTargets(const Media &media, std::vector<st
|
||||
// If any media survived, add the target.
|
||||
if(!target->media.empty())
|
||||
destination.push_back(std::move(target));
|
||||
|
||||
return destination;
|
||||
}
|
||||
|
||||
@@ -3,22 +3,24 @@
|
||||
// Clock Signal
|
||||
//
|
||||
// Created by Thomas Harte on 30/07/2017.
|
||||
// Copyright © 2017 Thomas Harte. All rights reserved.
|
||||
// Copyright 2017 Thomas Harte. All rights reserved.
|
||||
//
|
||||
|
||||
#ifndef StaticAnalyser_AmstradCPC_StaticAnalyser_hpp
|
||||
#define StaticAnalyser_AmstradCPC_StaticAnalyser_hpp
|
||||
#ifndef Analyser_Static_AmstradCPC_StaticAnalyser_hpp
|
||||
#define Analyser_Static_AmstradCPC_StaticAnalyser_hpp
|
||||
|
||||
#include "../StaticAnalyser.hpp"
|
||||
#include "../../../Storage/TargetPlatforms.hpp"
|
||||
#include <string>
|
||||
|
||||
namespace Analyser {
|
||||
namespace Static {
|
||||
namespace AmstradCPC {
|
||||
|
||||
void AddTargets(const Media &media, std::vector<std::unique_ptr<Target>> &destination);
|
||||
TargetList GetTargets(const Media &media, const std::string &file_name, TargetPlatform::IntType potential_platforms);
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endif /* StaticAnalyser_AmstradCPC_StaticAnalyser_hpp */
|
||||
#endif /* Analyser_Static_AmstradCPC_StaticAnalyser_hpp */
|
||||
|
||||
@@ -3,13 +3,14 @@
|
||||
// Clock Signal
|
||||
//
|
||||
// Created by Thomas Harte on 09/03/2018.
|
||||
// Copyright © 2018 Thomas Harte. All rights reserved.
|
||||
// Copyright 2018 Thomas Harte. All rights reserved.
|
||||
//
|
||||
|
||||
#ifndef Analyser_Static_AmstradCPC_Target_h
|
||||
#define Analyser_Static_AmstradCPC_Target_h
|
||||
|
||||
#include "../StaticAnalyser.hpp"
|
||||
#include <string>
|
||||
|
||||
namespace Analyser {
|
||||
namespace Static {
|
||||
@@ -23,6 +24,7 @@ struct Target: public ::Analyser::Static::Target {
|
||||
};
|
||||
|
||||
Model model = Model::CPC464;
|
||||
std::string loading_command;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
23
Analyser/Static/AppleII/StaticAnalyser.cpp
Normal file
23
Analyser/Static/AppleII/StaticAnalyser.cpp
Normal file
@@ -0,0 +1,23 @@
|
||||
//
|
||||
// StaticAnalyser.cpp
|
||||
// Clock Signal
|
||||
//
|
||||
// Created by Thomas Harte on 14/04/2018.
|
||||
// Copyright 2018 Thomas Harte. All rights reserved.
|
||||
//
|
||||
|
||||
#include "StaticAnalyser.hpp"
|
||||
#include "Target.hpp"
|
||||
|
||||
Analyser::Static::TargetList Analyser::Static::AppleII::GetTargets(const Media &media, const std::string &file_name, TargetPlatform::IntType potential_platforms) {
|
||||
auto target = std::unique_ptr<Target>(new Target);
|
||||
target->machine = Machine::AppleII;
|
||||
target->media = media;
|
||||
|
||||
if(!target->media.disks.empty())
|
||||
target->disk_controller = Target::DiskController::SixteenSector;
|
||||
|
||||
TargetList targets;
|
||||
targets.push_back(std::move(target));
|
||||
return targets;
|
||||
}
|
||||
26
Analyser/Static/AppleII/StaticAnalyser.hpp
Normal file
26
Analyser/Static/AppleII/StaticAnalyser.hpp
Normal file
@@ -0,0 +1,26 @@
|
||||
//
|
||||
// StaticAnalyser.hpp
|
||||
// Clock Signal
|
||||
//
|
||||
// Created by Thomas Harte on 14/04/2018.
|
||||
// Copyright 2018 Thomas Harte. All rights reserved.
|
||||
//
|
||||
|
||||
#ifndef Analyser_Static_AppleII_StaticAnalyser_hpp
|
||||
#define Analyser_Static_AppleII_StaticAnalyser_hpp
|
||||
|
||||
#include "../StaticAnalyser.hpp"
|
||||
#include "../../../Storage/TargetPlatforms.hpp"
|
||||
#include <string>
|
||||
|
||||
namespace Analyser {
|
||||
namespace Static {
|
||||
namespace AppleII {
|
||||
|
||||
TargetList GetTargets(const Media &media, const std::string &file_name, TargetPlatform::IntType potential_platforms);
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endif /* Analyser_Static_AppleII_StaticAnalyser_hpp */
|
||||
37
Analyser/Static/AppleII/Target.hpp
Normal file
37
Analyser/Static/AppleII/Target.hpp
Normal file
@@ -0,0 +1,37 @@
|
||||
//
|
||||
// Target.hpp
|
||||
// Clock Signal
|
||||
//
|
||||
// Created by Thomas Harte on 21/04/2018.
|
||||
// Copyright 2018 Thomas Harte. All rights reserved.
|
||||
//
|
||||
|
||||
#ifndef Target_h
|
||||
#define Target_h
|
||||
|
||||
#include "../StaticAnalyser.hpp"
|
||||
|
||||
namespace Analyser {
|
||||
namespace Static {
|
||||
namespace AppleII {
|
||||
|
||||
struct Target: public ::Analyser::Static::Target {
|
||||
enum class Model {
|
||||
II,
|
||||
IIplus
|
||||
};
|
||||
enum class DiskController {
|
||||
None,
|
||||
SixteenSector,
|
||||
ThirteenSector
|
||||
};
|
||||
|
||||
Model model = Model::IIplus;
|
||||
DiskController disk_controller = DiskController::None;
|
||||
};
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endif /* Target_h */
|
||||
@@ -3,7 +3,7 @@
|
||||
// Clock Signal
|
||||
//
|
||||
// Created by Thomas Harte on 15/09/2016.
|
||||
// Copyright © 2016 Thomas Harte. All rights reserved.
|
||||
// Copyright 2016 Thomas Harte. All rights reserved.
|
||||
//
|
||||
|
||||
#include "StaticAnalyser.hpp"
|
||||
@@ -180,7 +180,7 @@ static void DeterminePagingForCartridge(Analyser::Static::Atari::Target &target,
|
||||
}
|
||||
}
|
||||
|
||||
void Analyser::Static::Atari::AddTargets(const Media &media, std::vector<std::unique_ptr<Analyser::Static::Target>> &destination) {
|
||||
Analyser::Static::TargetList Analyser::Static::Atari::GetTargets(const Media &media, const std::string &file_name, TargetPlatform::IntType potential_platforms) {
|
||||
// TODO: sanity checking; is this image really for an Atari 2600?
|
||||
std::unique_ptr<Analyser::Static::Atari::Target> target(new Analyser::Static::Atari::Target);
|
||||
target->machine = Machine::Atari2600;
|
||||
@@ -198,6 +198,7 @@ void Analyser::Static::Atari::AddTargets(const Media &media, std::vector<std::un
|
||||
DeterminePagingForCartridge(*target, segment);
|
||||
}
|
||||
}
|
||||
|
||||
destination.push_back(std::move(target));
|
||||
TargetList destinations;
|
||||
destinations.push_back(std::move(target));
|
||||
return destinations;
|
||||
}
|
||||
|
||||
@@ -3,19 +3,21 @@
|
||||
// Clock Signal
|
||||
//
|
||||
// Created by Thomas Harte on 15/09/2016.
|
||||
// Copyright © 2016 Thomas Harte. All rights reserved.
|
||||
// Copyright 2016 Thomas Harte. All rights reserved.
|
||||
//
|
||||
|
||||
#ifndef StaticAnalyser_Atari_StaticAnalyser_hpp
|
||||
#define StaticAnalyser_Atari_StaticAnalyser_hpp
|
||||
|
||||
#include "../StaticAnalyser.hpp"
|
||||
#include "../../../Storage/TargetPlatforms.hpp"
|
||||
#include <string>
|
||||
|
||||
namespace Analyser {
|
||||
namespace Static {
|
||||
namespace Atari {
|
||||
|
||||
void AddTargets(const Media &media, std::vector<std::unique_ptr<Target>> &destination);
|
||||
TargetList GetTargets(const Media &media, const std::string &file_name, TargetPlatform::IntType potential_platforms);
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
// Clock Signal
|
||||
//
|
||||
// Created by Thomas Harte on 09/03/2018.
|
||||
// Copyright © 2018 Thomas Harte. All rights reserved.
|
||||
// Copyright 2018 Thomas Harte. All rights reserved.
|
||||
//
|
||||
|
||||
#ifndef Analyser_Static_Atari_Target_h
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
// Clock Signal
|
||||
//
|
||||
// Created by Thomas Harte on 23/02/2018.
|
||||
// Copyright © 2018 Thomas Harte. All rights reserved.
|
||||
// Copyright 2018 Thomas Harte. All rights reserved.
|
||||
//
|
||||
|
||||
#include "StaticAnalyser.hpp"
|
||||
@@ -52,11 +52,13 @@ static std::vector<std::shared_ptr<Storage::Cartridge::Cartridge>>
|
||||
return coleco_cartridges;
|
||||
}
|
||||
|
||||
void Analyser::Static::Coleco::AddTargets(const Media &media, std::vector<std::unique_ptr<Target>> &destination) {
|
||||
Analyser::Static::TargetList Analyser::Static::Coleco::GetTargets(const Media &media, const std::string &file_name, TargetPlatform::IntType potential_platforms) {
|
||||
TargetList targets;
|
||||
std::unique_ptr<Target> target(new Target);
|
||||
target->machine = Machine::ColecoVision;
|
||||
target->confidence = 1.0f - 1.0f / 32768.0f;
|
||||
target->media.cartridges = ColecoCartridgesFrom(media.cartridges);
|
||||
if(!target->media.empty())
|
||||
destination.push_back(std::move(target));
|
||||
targets.push_back(std::move(target));
|
||||
return targets;
|
||||
}
|
||||
|
||||
@@ -3,19 +3,21 @@
|
||||
// Clock Signal
|
||||
//
|
||||
// Created by Thomas Harte on 23/02/2018.
|
||||
// Copyright © 2018 Thomas Harte. All rights reserved.
|
||||
// Copyright 2018 Thomas Harte. All rights reserved.
|
||||
//
|
||||
|
||||
#ifndef StaticAnalyser_Coleco_StaticAnalyser_hpp
|
||||
#define StaticAnalyser_Coleco_StaticAnalyser_hpp
|
||||
|
||||
#include "../StaticAnalyser.hpp"
|
||||
#include "../../../Storage/TargetPlatforms.hpp"
|
||||
#include <string>
|
||||
|
||||
namespace Analyser {
|
||||
namespace Static {
|
||||
namespace Coleco {
|
||||
|
||||
void AddTargets(const Media &media, std::vector<std::unique_ptr<Target>> &destination);
|
||||
TargetList GetTargets(const Media &media, const std::string &file_name, TargetPlatform::IntType potential_platforms);
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
// Clock Signal
|
||||
//
|
||||
// Created by Thomas Harte on 13/09/2016.
|
||||
// Copyright © 2016 Thomas Harte. All rights reserved.
|
||||
// Copyright 2016 Thomas Harte. All rights reserved.
|
||||
//
|
||||
|
||||
#include "Disk.hpp"
|
||||
@@ -45,9 +45,11 @@ class CommodoreGCRParser: public Storage::Disk::Controller {
|
||||
|
||||
if(difference) {
|
||||
int direction = difference < 0 ? -1 : 1;
|
||||
difference *= 2 * direction;
|
||||
difference *= direction;
|
||||
|
||||
for(int c = 0; c < difference; c++) get_drive().step(direction);
|
||||
for(int c = 0; c < difference; c++) {
|
||||
get_drive().step(Storage::Disk::HeadPosition(direction));
|
||||
}
|
||||
|
||||
unsigned int zone = 3;
|
||||
if(track >= 18) zone = 2;
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
// Clock Signal
|
||||
//
|
||||
// Created by Thomas Harte on 13/09/2016.
|
||||
// Copyright © 2016 Thomas Harte. All rights reserved.
|
||||
// Copyright 2016 Thomas Harte. All rights reserved.
|
||||
//
|
||||
|
||||
#ifndef StaticAnalyser_Commodore_Disk_hpp
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
// Clock Signal
|
||||
//
|
||||
// Created by Thomas Harte on 10/09/2016.
|
||||
// Copyright © 2016 Thomas Harte. All rights reserved.
|
||||
// Copyright 2016 Thomas Harte. All rights reserved.
|
||||
//
|
||||
|
||||
#include "File.hpp"
|
||||
@@ -21,7 +21,7 @@ bool Analyser::Static::Commodore::File::is_basic() {
|
||||
// [4 bytes: address of start of next line]
|
||||
// [4 bytes: this line number]
|
||||
// ... null-terminated code ...
|
||||
// (with a next line address of 0000 indicating end of program)ß
|
||||
// (with a next line address of 0000 indicating end of program)
|
||||
while(1) {
|
||||
if(static_cast<size_t>(line_address - starting_address) >= data.size() + 2) break;
|
||||
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
// Clock Signal
|
||||
//
|
||||
// Created by Thomas Harte on 10/09/2016.
|
||||
// Copyright © 2016 Thomas Harte. All rights reserved.
|
||||
// Copyright 2016 Thomas Harte. All rights reserved.
|
||||
//
|
||||
|
||||
#ifndef File_hpp
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
// Clock Signal
|
||||
//
|
||||
// Created by Thomas Harte on 06/09/2016.
|
||||
// Copyright © 2016 Thomas Harte. All rights reserved.
|
||||
// Copyright 2016 Thomas Harte. All rights reserved.
|
||||
//
|
||||
|
||||
#include "StaticAnalyser.hpp"
|
||||
@@ -40,7 +40,9 @@ static std::vector<std::shared_ptr<Storage::Cartridge::Cartridge>>
|
||||
return vic20_cartridges;
|
||||
}
|
||||
|
||||
void Analyser::Static::Commodore::AddTargets(const Media &media, std::vector<std::unique_ptr<Analyser::Static::Target>> &destination, const std::string &file_name) {
|
||||
Analyser::Static::TargetList Analyser::Static::Commodore::GetTargets(const Media &media, const std::string &file_name, TargetPlatform::IntType potential_platforms) {
|
||||
TargetList destination;
|
||||
|
||||
std::unique_ptr<Target> target(new Target);
|
||||
target->machine = Machine::Vic20; // TODO: machine estimation
|
||||
target->confidence = 0.5; // TODO: a proper estimation
|
||||
@@ -149,6 +151,11 @@ void Analyser::Static::Commodore::AddTargets(const Media &media, std::vector<std
|
||||
target->region = Analyser::Static::Commodore::Target::Region::American;
|
||||
}
|
||||
|
||||
// Attach a 1540 if there are any disks here.
|
||||
target->has_c1540 = !target->media.disks.empty();
|
||||
|
||||
destination.push_back(std::move(target));
|
||||
}
|
||||
|
||||
return destination;
|
||||
}
|
||||
|
||||
@@ -3,20 +3,21 @@
|
||||
// Clock Signal
|
||||
//
|
||||
// Created by Thomas Harte on 06/09/2016.
|
||||
// Copyright © 2016 Thomas Harte. All rights reserved.
|
||||
// Copyright 2016 Thomas Harte. All rights reserved.
|
||||
//
|
||||
|
||||
#ifndef StaticAnalyser_Commodore_StaticAnalyser_hpp
|
||||
#define StaticAnalyser_Commodore_StaticAnalyser_hpp
|
||||
|
||||
#include "../StaticAnalyser.hpp"
|
||||
#include "../../../Storage/TargetPlatforms.hpp"
|
||||
#include <string>
|
||||
|
||||
namespace Analyser {
|
||||
namespace Static {
|
||||
namespace Commodore {
|
||||
|
||||
void AddTargets(const Media &media, std::vector<std::unique_ptr<Target>> &destination, const std::string &file_name);
|
||||
TargetList GetTargets(const Media &media, const std::string &file_name, TargetPlatform::IntType potential_platforms);
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
// Clock Signal
|
||||
//
|
||||
// Created by Thomas Harte on 24/08/2016.
|
||||
// Copyright © 2016 Thomas Harte. All rights reserved.
|
||||
// Copyright 2016 Thomas Harte. All rights reserved.
|
||||
//
|
||||
|
||||
#include "Tape.hpp"
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
// Clock Signal
|
||||
//
|
||||
// Created by Thomas Harte on 24/08/2016.
|
||||
// Copyright © 2016 Thomas Harte. All rights reserved.
|
||||
// Copyright 2016 Thomas Harte. All rights reserved.
|
||||
//
|
||||
|
||||
#ifndef StaticAnalyser_Commodore_Tape_hpp
|
||||
|
||||
@@ -3,13 +3,14 @@
|
||||
// Clock Signal
|
||||
//
|
||||
// Created by Thomas Harte on 09/03/2018.
|
||||
// Copyright © 2018 Thomas Harte. All rights reserved.
|
||||
// Copyright 2018 Thomas Harte. All rights reserved.
|
||||
//
|
||||
|
||||
#ifndef Analyser_Static_Commodore_Target_h
|
||||
#define Analyser_Static_Commodore_Target_h
|
||||
|
||||
#include "../StaticAnalyser.hpp"
|
||||
#include <string>
|
||||
|
||||
namespace Analyser {
|
||||
namespace Static {
|
||||
@@ -33,6 +34,7 @@ struct Target: public ::Analyser::Static::Target {
|
||||
MemoryModel memory_model = MemoryModel::Unexpanded;
|
||||
Region region = Region::European;
|
||||
bool has_c1540 = false;
|
||||
std::string loading_command;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
// Clock Signal
|
||||
//
|
||||
// Created by Thomas Harte on 10/11/2016.
|
||||
// Copyright © 2016 Thomas Harte. All rights reserved.
|
||||
// Copyright 2016 Thomas Harte. All rights reserved.
|
||||
//
|
||||
|
||||
#include "6502.hpp"
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
// Clock Signal
|
||||
//
|
||||
// Created by Thomas Harte on 10/11/2016.
|
||||
// Copyright © 2016 Thomas Harte. All rights reserved.
|
||||
// Copyright 2016 Thomas Harte. All rights reserved.
|
||||
//
|
||||
|
||||
#ifndef StaticAnalyser_Disassembler_6502_hpp
|
||||
@@ -21,7 +21,7 @@ namespace Static {
|
||||
namespace MOS6502 {
|
||||
|
||||
/*!
|
||||
Describes a 6502 instruciton — its address, the operation it performs, its addressing mode
|
||||
Describes a 6502 instruciton: its address, the operation it performs, its addressing mode
|
||||
and its operand, if any.
|
||||
*/
|
||||
struct Instruction {
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
// Clock Signal
|
||||
//
|
||||
// Created by Thomas Harte on 30/12/2017.
|
||||
// Copyright © 2017 Thomas Harte. All rights reserved.
|
||||
// Copyright 2017 Thomas Harte. All rights reserved.
|
||||
//
|
||||
|
||||
#include "AddressMapper.hpp"
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
// Clock Signal
|
||||
//
|
||||
// Created by Thomas Harte on 30/12/2017.
|
||||
// Copyright © 2017 Thomas Harte. All rights reserved.
|
||||
// Copyright 2017 Thomas Harte. All rights reserved.
|
||||
//
|
||||
|
||||
#ifndef AddressMapper_hpp
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
// Clock Signal
|
||||
//
|
||||
// Created by Thomas Harte on 31/12/2017.
|
||||
// Copyright © 2017 Thomas Harte. All rights reserved.
|
||||
// Copyright 2017 Thomas Harte. All rights reserved.
|
||||
//
|
||||
|
||||
#ifndef Kernel_hpp
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
// Clock Signal
|
||||
//
|
||||
// Created by Thomas Harte on 30/12/2017.
|
||||
// Copyright © 2017 Thomas Harte. All rights reserved.
|
||||
// Copyright 2017 Thomas Harte. All rights reserved.
|
||||
//
|
||||
|
||||
#include "Z80.hpp"
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
// Clock Signal
|
||||
//
|
||||
// Created by Thomas Harte on 30/12/2017.
|
||||
// Copyright © 2017 Thomas Harte. All rights reserved.
|
||||
// Copyright 2017 Thomas Harte. All rights reserved.
|
||||
//
|
||||
|
||||
#ifndef StaticAnalyser_Disassembler_Z80_hpp
|
||||
|
||||
125
Analyser/Static/DiskII/StaticAnalyser.cpp
Normal file
125
Analyser/Static/DiskII/StaticAnalyser.cpp
Normal file
@@ -0,0 +1,125 @@
|
||||
//
|
||||
// StaticAnalyser.cpp
|
||||
// Clock Signal
|
||||
//
|
||||
// Created by Thomas Harte on 03/05/2018.
|
||||
// Copyright 2018 Thomas Harte. All rights reserved.
|
||||
//
|
||||
|
||||
#include "StaticAnalyser.hpp"
|
||||
|
||||
#include "../AppleII/Target.hpp"
|
||||
#include "../Oric/Target.hpp"
|
||||
#include "../Disassembler/6502.hpp"
|
||||
#include "../Disassembler/AddressMapper.hpp"
|
||||
|
||||
#include "../../../Storage/Disk/Track/TrackSerialiser.hpp"
|
||||
#include "../../../Storage/Disk/Encodings/AppleGCR/SegmentParser.hpp"
|
||||
|
||||
namespace {
|
||||
|
||||
Analyser::Static::Target *AppleTarget(const Storage::Encodings::AppleGCR::Sector *sector_zero) {
|
||||
using Target = Analyser::Static::AppleII::Target;
|
||||
auto *target = new Target;
|
||||
target->machine = Analyser::Machine::AppleII;
|
||||
|
||||
if(sector_zero && sector_zero->encoding == Storage::Encodings::AppleGCR::Sector::Encoding::FiveAndThree) {
|
||||
target->disk_controller = Target::DiskController::ThirteenSector;
|
||||
} else {
|
||||
target->disk_controller = Target::DiskController::SixteenSector;
|
||||
}
|
||||
|
||||
return target;
|
||||
}
|
||||
|
||||
Analyser::Static::Target *OricTarget(const Storage::Encodings::AppleGCR::Sector *sector_zero) {
|
||||
using Target = Analyser::Static::Oric::Target;
|
||||
auto *target = new Target;
|
||||
target->machine = Analyser::Machine::Oric;
|
||||
target->rom = Target::ROM::Pravetz;
|
||||
target->disk_interface = Target::DiskInterface::Pravetz;
|
||||
target->loading_command = "CALL 800\n";
|
||||
return target;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Analyser::Static::TargetList Analyser::Static::DiskII::GetTargets(const Media &media, const std::string &file_name, TargetPlatform::IntType potential_platforms) {
|
||||
// This analyser can comprehend disks only.
|
||||
if(media.disks.empty()) return {};
|
||||
|
||||
// Grab track 0, sector 0: the boot sector.
|
||||
auto track_zero = media.disks.front()->get_track_at_position(Storage::Disk::Track::Address(0, Storage::Disk::HeadPosition(0)));
|
||||
auto sector_map = Storage::Encodings::AppleGCR::sectors_from_segment(
|
||||
Storage::Disk::track_serialisation(*track_zero, Storage::Time(1, 50000)));
|
||||
|
||||
const Storage::Encodings::AppleGCR::Sector *sector_zero = nullptr;
|
||||
for(const auto &pair: sector_map) {
|
||||
if(!pair.second.address.sector) {
|
||||
sector_zero = &pair.second;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// If there's no boot sector then if there are also no sectors at all,
|
||||
// decline to nominate a machine. Otherwise go with an Apple as the default.
|
||||
TargetList targets;
|
||||
if(!sector_zero) {
|
||||
if(sector_map.empty()) {
|
||||
return targets;
|
||||
} else {
|
||||
targets.push_back(std::unique_ptr<Analyser::Static::Target>(AppleTarget(nullptr)));
|
||||
targets.back()->media = media;
|
||||
return targets;
|
||||
}
|
||||
}
|
||||
|
||||
// If the boot sector looks like it's intended for the Oric, create an Oric.
|
||||
// Otherwise go with the Apple II.
|
||||
|
||||
auto disassembly = Analyser::Static::MOS6502::Disassemble(sector_zero->data, Analyser::Static::Disassembler::OffsetMapper(0xb800), {0xb800});
|
||||
|
||||
bool did_read_shift_register = false;
|
||||
bool is_oric = false;
|
||||
|
||||
// Look for a tight BPL loop reading the Oric's shift register address of 0x31c. The Apple II just has RAM there,
|
||||
// so the probability of such a loop is infinitesimal.
|
||||
for(const auto &instruction: disassembly.instructions_by_address) {
|
||||
// Is this a read of the shift register?
|
||||
if(
|
||||
(
|
||||
(instruction.second.operation == Analyser::Static::MOS6502::Instruction::LDA) ||
|
||||
(instruction.second.operation == Analyser::Static::MOS6502::Instruction::LDX) ||
|
||||
(instruction.second.operation == Analyser::Static::MOS6502::Instruction::LDY)
|
||||
) &&
|
||||
instruction.second.addressing_mode == Analyser::Static::MOS6502::Instruction::Absolute &&
|
||||
instruction.second.address == 0x031c) {
|
||||
did_read_shift_register = true;
|
||||
continue;
|
||||
}
|
||||
|
||||
if(did_read_shift_register) {
|
||||
if(
|
||||
instruction.second.operation == Analyser::Static::MOS6502::Instruction::BPL &&
|
||||
instruction.second.address == 0xfb) {
|
||||
is_oric = true;
|
||||
break;
|
||||
}
|
||||
|
||||
did_read_shift_register = false;
|
||||
}
|
||||
}
|
||||
|
||||
// Check also for calls into the 0x3xx page above 0x320, as that's where the Oric's boot ROM is.
|
||||
for(const auto address: disassembly.outward_calls) {
|
||||
is_oric |= address >= 0x320 && address < 0x400;
|
||||
}
|
||||
|
||||
if(is_oric) {
|
||||
targets.push_back(std::unique_ptr<Analyser::Static::Target>(OricTarget(sector_zero)));
|
||||
} else {
|
||||
targets.push_back(std::unique_ptr<Analyser::Static::Target>(AppleTarget(sector_zero)));
|
||||
}
|
||||
targets.back()->media = media;
|
||||
return targets;
|
||||
}
|
||||
27
Analyser/Static/DiskII/StaticAnalyser.hpp
Normal file
27
Analyser/Static/DiskII/StaticAnalyser.hpp
Normal file
@@ -0,0 +1,27 @@
|
||||
//
|
||||
// StaticAnalyser.hpp
|
||||
// Clock Signal
|
||||
//
|
||||
// Created by Thomas Harte on 03/05/2018.
|
||||
// Copyright 2018 Thomas Harte. All rights reserved.
|
||||
//
|
||||
|
||||
#ifndef Analyser_Static_DiskII_StaticAnalyser_hpp
|
||||
#define Analyser_Static_DiskII_StaticAnalyser_hpp
|
||||
|
||||
#include "../StaticAnalyser.hpp"
|
||||
#include "../../../Storage/TargetPlatforms.hpp"
|
||||
#include <string>
|
||||
|
||||
namespace Analyser {
|
||||
namespace Static {
|
||||
namespace DiskII {
|
||||
|
||||
TargetList GetTargets(const Media &media, const std::string &file_name, TargetPlatform::IntType potential_platforms);
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#endif /* Analyser_Static_DiskII_StaticAnalyser_hpp */
|
||||
@@ -3,7 +3,7 @@
|
||||
// Clock Signal
|
||||
//
|
||||
// Created by Thomas Harte on 25/01/2018.
|
||||
// Copyright © 2018 Thomas Harte. All rights reserved.
|
||||
// Copyright 2018 Thomas Harte. All rights reserved.
|
||||
//
|
||||
|
||||
#ifndef Cartridge_hpp
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
// Clock Signal
|
||||
//
|
||||
// Created by Thomas Harte on 25/11/2017.
|
||||
// Copyright © 2017 Thomas Harte. All rights reserved.
|
||||
// Copyright 2017 Thomas Harte. All rights reserved.
|
||||
//
|
||||
|
||||
#include "StaticAnalyser.hpp"
|
||||
@@ -63,14 +63,14 @@ static std::unique_ptr<Analyser::Static::Target> CartridgeTarget(
|
||||
|
||||
(additional audio hardware is also sometimes included, but it's implied by the banking hardware)
|
||||
*/
|
||||
static std::vector<std::unique_ptr<Analyser::Static::Target>> CartridgeTargetsFrom(
|
||||
static Analyser::Static::TargetList CartridgeTargetsFrom(
|
||||
const std::vector<std::shared_ptr<Storage::Cartridge::Cartridge>> &cartridges) {
|
||||
// No cartridges implies no targets.
|
||||
if(cartridges.empty()) {
|
||||
return {};
|
||||
}
|
||||
|
||||
std::vector<std::unique_ptr<Analyser::Static::Target>> targets;
|
||||
Analyser::Static::TargetList targets;
|
||||
for(const auto &cartridge : cartridges) {
|
||||
const auto &segments = cartridge->get_segments();
|
||||
|
||||
@@ -261,7 +261,9 @@ static std::vector<std::unique_ptr<Analyser::Static::Target>> CartridgeTargetsFr
|
||||
return targets;
|
||||
}
|
||||
|
||||
void Analyser::Static::MSX::AddTargets(const Media &media, std::vector<std::unique_ptr<::Analyser::Static::Target>> &destination) {
|
||||
Analyser::Static::TargetList Analyser::Static::MSX::GetTargets(const Media &media, const std::string &file_name, TargetPlatform::IntType potential_platforms) {
|
||||
TargetList destination;
|
||||
|
||||
// Append targets for any cartridges that look correct.
|
||||
auto cartridge_targets = CartridgeTargetsFrom(media.cartridges);
|
||||
std::move(cartridge_targets.begin(), cartridge_targets.end(), std::back_inserter(destination));
|
||||
@@ -270,7 +272,7 @@ void Analyser::Static::MSX::AddTargets(const Media &media, std::vector<std::uniq
|
||||
std::unique_ptr<Target> target(new Target);
|
||||
|
||||
// Check tapes for loadable files.
|
||||
for(const auto &tape : media.tapes) {
|
||||
for(auto &tape : media.tapes) {
|
||||
std::vector<File> files_on_tape = GetFiles(tape);
|
||||
if(!files_on_tape.empty()) {
|
||||
switch(files_on_tape.front().type) {
|
||||
@@ -292,4 +294,6 @@ void Analyser::Static::MSX::AddTargets(const Media &media, std::vector<std::uniq
|
||||
target->confidence = 0.5;
|
||||
destination.push_back(std::move(target));
|
||||
}
|
||||
|
||||
return destination;
|
||||
}
|
||||
|
||||
@@ -3,19 +3,21 @@
|
||||
// Clock Signal
|
||||
//
|
||||
// Created by Thomas Harte on 25/11/2017.
|
||||
// Copyright © 2017 Thomas Harte. All rights reserved.
|
||||
// Copyright 2017 Thomas Harte. All rights reserved.
|
||||
//
|
||||
|
||||
#ifndef StaticAnalyser_MSX_StaticAnalyser_hpp
|
||||
#define StaticAnalyser_MSX_StaticAnalyser_hpp
|
||||
|
||||
#include "../StaticAnalyser.hpp"
|
||||
#include "../../../Storage/TargetPlatforms.hpp"
|
||||
#include <string>
|
||||
|
||||
namespace Analyser {
|
||||
namespace Static {
|
||||
namespace MSX {
|
||||
|
||||
void AddTargets(const Media &media, std::vector<std::unique_ptr<Target>> &destination);
|
||||
TargetList GetTargets(const Media &media, const std::string &file_name, TargetPlatform::IntType potential_platforms);
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
// Clock Signal
|
||||
//
|
||||
// Created by Thomas Harte on 25/12/2017.
|
||||
// Copyright © 2017 Thomas Harte. All rights reserved.
|
||||
// Copyright 2017 Thomas Harte. All rights reserved.
|
||||
//
|
||||
|
||||
#include "Tape.hpp"
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
// Clock Signal
|
||||
//
|
||||
// Created by Thomas Harte on 25/12/2017.
|
||||
// Copyright © 2017 Thomas Harte. All rights reserved.
|
||||
// Copyright 2017 Thomas Harte. All rights reserved.
|
||||
//
|
||||
|
||||
#ifndef StaticAnalyser_MSX_Tape_hpp
|
||||
|
||||
@@ -3,13 +3,14 @@
|
||||
// Clock Signal
|
||||
//
|
||||
// Created by Thomas Harte on 02/04/2018.
|
||||
// Copyright © 2018 Thomas Harte. All rights reserved.
|
||||
// Copyright 2018 Thomas Harte. All rights reserved.
|
||||
//
|
||||
|
||||
#ifndef Analyser_Static_MSX_Target_h
|
||||
#define Analyser_Static_MSX_Target_h
|
||||
|
||||
#include "../StaticAnalyser.hpp"
|
||||
#include <string>
|
||||
|
||||
namespace Analyser {
|
||||
namespace Static {
|
||||
@@ -17,6 +18,7 @@ namespace MSX {
|
||||
|
||||
struct Target: public ::Analyser::Static::Target {
|
||||
bool has_disk_drive = false;
|
||||
std::string loading_command;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
// Clock Signal
|
||||
//
|
||||
// Created by Thomas Harte on 11/10/2016.
|
||||
// Copyright © 2016 Thomas Harte. All rights reserved.
|
||||
// Copyright 2016 Thomas Harte. All rights reserved.
|
||||
//
|
||||
|
||||
#include "StaticAnalyser.hpp"
|
||||
@@ -23,15 +23,15 @@ using namespace Analyser::Static::Oric;
|
||||
static int Score(const Analyser::Static::MOS6502::Disassembly &disassembly, const std::set<uint16_t> &rom_functions, const std::set<uint16_t> &variable_locations) {
|
||||
int score = 0;
|
||||
|
||||
for(auto address : disassembly.outward_calls) score += (rom_functions.find(address) != rom_functions.end()) ? 1 : -1;
|
||||
for(auto address : disassembly.external_stores) score += (variable_locations.find(address) != variable_locations.end()) ? 1 : -1;
|
||||
for(auto address : disassembly.external_loads) score += (variable_locations.find(address) != variable_locations.end()) ? 1 : -1;
|
||||
for(const auto address : disassembly.outward_calls) score += (rom_functions.find(address) != rom_functions.end()) ? 1 : -1;
|
||||
for(const auto address : disassembly.external_stores) score += (variable_locations.find(address) != variable_locations.end()) ? 1 : -1;
|
||||
for(const auto address : disassembly.external_loads) score += (variable_locations.find(address) != variable_locations.end()) ? 1 : -1;
|
||||
|
||||
return score;
|
||||
}
|
||||
|
||||
static int Basic10Score(const Analyser::Static::MOS6502::Disassembly &disassembly) {
|
||||
std::set<uint16_t> rom_functions = {
|
||||
const std::set<uint16_t> rom_functions = {
|
||||
0x0228, 0x022b,
|
||||
0xc3ca, 0xc3f8, 0xc448, 0xc47c, 0xc4b5, 0xc4e3, 0xc4e0, 0xc524, 0xc56f, 0xc5a2, 0xc5f8, 0xc60a, 0xc6a5, 0xc6de, 0xc719, 0xc738,
|
||||
0xc773, 0xc824, 0xc832, 0xc841, 0xc8c1, 0xc8fe, 0xc91f, 0xc93f, 0xc941, 0xc91e, 0xc98b, 0xc996, 0xc9b3, 0xc9e0, 0xca0a, 0xca1c,
|
||||
@@ -47,7 +47,7 @@ static int Basic10Score(const Analyser::Static::MOS6502::Disassembly &disassembl
|
||||
0xf43c, 0xf4ef, 0xf523, 0xf561, 0xf535, 0xf57b, 0xf5d3, 0xf71a, 0xf73f, 0xf7e4, 0xf7e0, 0xf82f, 0xf88f, 0xf8af, 0xf8b5, 0xf920,
|
||||
0xf967, 0xf960, 0xf9c9, 0xfa14, 0xfa85, 0xfa9b, 0xfab1, 0xfac7, 0xfafa, 0xfb10, 0xfb26, 0xfbb6, 0xfbfe
|
||||
};
|
||||
std::set<uint16_t> variable_locations = {
|
||||
const std::set<uint16_t> variable_locations = {
|
||||
0x0228, 0x0229, 0x022a, 0x022b, 0x022c, 0x022d, 0x0230
|
||||
};
|
||||
|
||||
@@ -55,7 +55,7 @@ static int Basic10Score(const Analyser::Static::MOS6502::Disassembly &disassembl
|
||||
}
|
||||
|
||||
static int Basic11Score(const Analyser::Static::MOS6502::Disassembly &disassembly) {
|
||||
std::set<uint16_t> rom_functions = {
|
||||
const std::set<uint16_t> rom_functions = {
|
||||
0x0238, 0x023b, 0x023e, 0x0241, 0x0244, 0x0247,
|
||||
0xc3c6, 0xc3f4, 0xc444, 0xc47c, 0xc4a8, 0xc4d3, 0xc4e0, 0xc524, 0xc55f, 0xc592, 0xc5e8, 0xc5fa, 0xc692, 0xc6b3, 0xc6ee, 0xc70d,
|
||||
0xc748, 0xc7fd, 0xc809, 0xc816, 0xc82f, 0xc855, 0xc8c1, 0xc915, 0xc952, 0xc971, 0xc973, 0xc9a0, 0xc9bd, 0xc9c8, 0xc9e5, 0xca12,
|
||||
@@ -72,7 +72,7 @@ static int Basic11Score(const Analyser::Static::MOS6502::Disassembly &disassembl
|
||||
0xf88f, 0xf8af, 0xf8b5, 0xf920, 0xf967, 0xf9aa, 0xf9c9, 0xfa14, 0xfa9f, 0xfab5, 0xfacb, 0xfae1, 0xfb14, 0xfb2a, 0xfb40, 0xfbd0,
|
||||
0xfc18
|
||||
};
|
||||
std::set<uint16_t> variable_locations = {
|
||||
const std::set<uint16_t> variable_locations = {
|
||||
0x0244, 0x0245, 0x0246, 0x0247, 0x0248, 0x0249, 0x024a, 0x024b, 0x024c
|
||||
};
|
||||
|
||||
@@ -100,7 +100,7 @@ static bool IsMicrodisc(Storage::Encodings::MFM::Parser &parser) {
|
||||
return !std::memcmp(signature, first_sample.data(), sizeof(signature));
|
||||
}
|
||||
|
||||
void Analyser::Static::Oric::AddTargets(const Media &media, std::vector<std::unique_ptr<Analyser::Static::Target>> &destination) {
|
||||
Analyser::Static::TargetList Analyser::Static::Oric::GetTargets(const Media &media, const std::string &file_name, TargetPlatform::IntType potential_platforms) {
|
||||
std::unique_ptr<Target> target(new Target);
|
||||
target->machine = Machine::Oric;
|
||||
target->confidence = 0.5;
|
||||
@@ -112,7 +112,7 @@ void Analyser::Static::Oric::AddTargets(const Media &media, std::vector<std::uni
|
||||
std::vector<File> tape_files = GetFiles(tape);
|
||||
tape->reset();
|
||||
if(tape_files.size()) {
|
||||
for(auto file : tape_files) {
|
||||
for(const auto &file : tape_files) {
|
||||
if(file.data_type == File::MachineCode) {
|
||||
std::vector<uint16_t> entry_points = {file.starting_address};
|
||||
Analyser::Static::MOS6502::Disassembly disassembly =
|
||||
@@ -131,21 +131,25 @@ void Analyser::Static::Oric::AddTargets(const Media &media, std::vector<std::uni
|
||||
|
||||
if(!media.disks.empty()) {
|
||||
// Only the Microdisc is emulated right now, so accept only disks that it can boot from.
|
||||
for(const auto &disk: media.disks) {
|
||||
for(auto &disk: media.disks) {
|
||||
Storage::Encodings::MFM::Parser parser(true, disk);
|
||||
if(IsMicrodisc(parser)) {
|
||||
target->has_microdrive = true;
|
||||
target->disk_interface = Target::DiskInterface::Microdisc;
|
||||
target->media.disks.push_back(disk);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
target->has_microdrive = false;
|
||||
}
|
||||
else
|
||||
target->disk_interface = Target::DiskInterface::None;
|
||||
|
||||
// TODO: really this should add two targets if not all votes agree
|
||||
target->use_atmos_rom = basic11_votes >= basic10_votes;
|
||||
if(target->has_microdrive) target->use_atmos_rom = true;
|
||||
if(basic11_votes >= basic10_votes || target->disk_interface == Target::DiskInterface::Microdisc)
|
||||
target->rom = Target::ROM::BASIC11;
|
||||
else
|
||||
target->rom = Target::ROM::BASIC10;
|
||||
|
||||
TargetList targets;
|
||||
if(target->media.tapes.size() || target->media.disks.size() || target->media.cartridges.size())
|
||||
destination.push_back(std::move(target));
|
||||
targets.push_back(std::move(target));
|
||||
return targets;
|
||||
}
|
||||
|
||||
@@ -3,19 +3,21 @@
|
||||
// Clock Signal
|
||||
//
|
||||
// Created by Thomas Harte on 11/10/2016.
|
||||
// Copyright © 2016 Thomas Harte. All rights reserved.
|
||||
// Copyright 2016 Thomas Harte. All rights reserved.
|
||||
//
|
||||
|
||||
#ifndef StaticAnalyser_Oric_StaticAnalyser_hpp
|
||||
#define StaticAnalyser_Oric_StaticAnalyser_hpp
|
||||
|
||||
#include "../StaticAnalyser.hpp"
|
||||
#include "../../../Storage/TargetPlatforms.hpp"
|
||||
#include <string>
|
||||
|
||||
namespace Analyser {
|
||||
namespace Static {
|
||||
namespace Oric {
|
||||
|
||||
void AddTargets(const Media &media, std::vector<std::unique_ptr<Target>> &destination);
|
||||
TargetList GetTargets(const Media &media, const std::string &file_name, TargetPlatform::IntType potential_platforms);
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
// Clock Signal
|
||||
//
|
||||
// Created by Thomas Harte on 06/11/2016.
|
||||
// Copyright © 2016 Thomas Harte. All rights reserved.
|
||||
// Copyright 2016 Thomas Harte. All rights reserved.
|
||||
//
|
||||
|
||||
#include "Tape.hpp"
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
// Clock Signal
|
||||
//
|
||||
// Created by Thomas Harte on 06/11/2016.
|
||||
// Copyright © 2016 Thomas Harte. All rights reserved.
|
||||
// Copyright 2016 Thomas Harte. All rights reserved.
|
||||
//
|
||||
|
||||
#ifndef StaticAnalyser_Oric_Tape_hpp
|
||||
|
||||
@@ -3,19 +3,35 @@
|
||||
// Clock Signal
|
||||
//
|
||||
// Created by Thomas Harte on 09/03/2018.
|
||||
// Copyright © 2018 Thomas Harte. All rights reserved.
|
||||
// Copyright 2018 Thomas Harte. All rights reserved.
|
||||
//
|
||||
|
||||
#ifndef Analyser_Static_Oric_Target_h
|
||||
#define Analyser_Static_Oric_Target_h
|
||||
|
||||
#include "../StaticAnalyser.hpp"
|
||||
#include <string>
|
||||
|
||||
namespace Analyser {
|
||||
namespace Static {
|
||||
namespace Oric {
|
||||
|
||||
struct Target: public ::Analyser::Static::Target {
|
||||
bool use_atmos_rom = false;
|
||||
bool has_microdrive = false;
|
||||
enum class ROM {
|
||||
BASIC10,
|
||||
BASIC11,
|
||||
Pravetz
|
||||
};
|
||||
|
||||
enum class DiskInterface {
|
||||
Microdisc,
|
||||
Pravetz,
|
||||
None
|
||||
};
|
||||
|
||||
ROM rom = ROM::BASIC11;
|
||||
DiskInterface disk_interface = DiskInterface::None;
|
||||
std::string loading_command;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
// Clock Signal
|
||||
//
|
||||
// Created by Thomas Harte on 23/08/2016.
|
||||
// Copyright © 2016 Thomas Harte. All rights reserved.
|
||||
// Copyright 2016 Thomas Harte. All rights reserved.
|
||||
//
|
||||
|
||||
#include "StaticAnalyser.hpp"
|
||||
@@ -11,13 +11,16 @@
|
||||
#include <algorithm>
|
||||
#include <cstdlib>
|
||||
#include <cstring>
|
||||
#include <iterator>
|
||||
|
||||
// Analysers
|
||||
#include "Acorn/StaticAnalyser.hpp"
|
||||
#include "AmstradCPC/StaticAnalyser.hpp"
|
||||
#include "AppleII/StaticAnalyser.hpp"
|
||||
#include "Atari/StaticAnalyser.hpp"
|
||||
#include "Coleco/StaticAnalyser.hpp"
|
||||
#include "Commodore/StaticAnalyser.hpp"
|
||||
#include "DiskII/StaticAnalyser.hpp"
|
||||
#include "MSX/StaticAnalyser.hpp"
|
||||
#include "Oric/StaticAnalyser.hpp"
|
||||
#include "ZX8081/StaticAnalyser.hpp"
|
||||
@@ -28,14 +31,17 @@
|
||||
|
||||
// Disks
|
||||
#include "../../Storage/Disk/DiskImage/Formats/AcornADF.hpp"
|
||||
#include "../../Storage/Disk/DiskImage/Formats/AppleDSK.hpp"
|
||||
#include "../../Storage/Disk/DiskImage/Formats/CPCDSK.hpp"
|
||||
#include "../../Storage/Disk/DiskImage/Formats/D64.hpp"
|
||||
#include "../../Storage/Disk/DiskImage/Formats/G64.hpp"
|
||||
#include "../../Storage/Disk/DiskImage/Formats/DMK.hpp"
|
||||
#include "../../Storage/Disk/DiskImage/Formats/HFE.hpp"
|
||||
#include "../../Storage/Disk/DiskImage/Formats/MSXDSK.hpp"
|
||||
#include "../../Storage/Disk/DiskImage/Formats/NIB.hpp"
|
||||
#include "../../Storage/Disk/DiskImage/Formats/OricMFMDSK.hpp"
|
||||
#include "../../Storage/Disk/DiskImage/Formats/SSD.hpp"
|
||||
#include "../../Storage/Disk/DiskImage/Formats/WOZ.hpp"
|
||||
|
||||
// Tapes
|
||||
#include "../../Storage/Tape/Formats/CAS.hpp"
|
||||
@@ -89,8 +95,10 @@ static Media GetMediaAndPlatforms(const std::string &file_name, TargetPlatform::
|
||||
Format("csw", result.tapes, Tape::CSW, TargetPlatform::AllTape) // CSW
|
||||
Format("d64", result.disks, Disk::DiskImageHolder<Storage::Disk::D64>, TargetPlatform::Commodore) // D64
|
||||
Format("dmk", result.disks, Disk::DiskImageHolder<Storage::Disk::DMK>, TargetPlatform::MSX) // DMK
|
||||
Format("do", result.disks, Disk::DiskImageHolder<Storage::Disk::AppleDSK>, TargetPlatform::DiskII) // DO
|
||||
Format("dsd", result.disks, Disk::DiskImageHolder<Storage::Disk::SSD>, TargetPlatform::Acorn) // DSD
|
||||
Format("dsk", result.disks, Disk::DiskImageHolder<Storage::Disk::CPCDSK>, TargetPlatform::AmstradCPC) // DSK (Amstrad CPC)
|
||||
Format("dsk", result.disks, Disk::DiskImageHolder<Storage::Disk::AppleDSK>, TargetPlatform::DiskII) // DSK (Apple)
|
||||
Format("dsk", result.disks, Disk::DiskImageHolder<Storage::Disk::MSXDSK>, TargetPlatform::MSX) // DSK (MSX)
|
||||
Format("dsk", result.disks, Disk::DiskImageHolder<Storage::Disk::OricMFMDSK>, TargetPlatform::Oric) // DSK (Oric)
|
||||
Format("g64", result.disks, Disk::DiskImageHolder<Storage::Disk::G64>, TargetPlatform::Commodore) // G64
|
||||
@@ -99,8 +107,10 @@ static Media GetMediaAndPlatforms(const std::string &file_name, TargetPlatform::
|
||||
Disk::DiskImageHolder<Storage::Disk::HFE>,
|
||||
TargetPlatform::Acorn | TargetPlatform::AmstradCPC | TargetPlatform::Commodore | TargetPlatform::Oric)
|
||||
// HFE (TODO: switch to AllDisk once the MSX stops being so greedy)
|
||||
Format("nib", result.disks, Disk::DiskImageHolder<Storage::Disk::NIB>, TargetPlatform::DiskII) // NIB
|
||||
Format("o", result.tapes, Tape::ZX80O81P, TargetPlatform::ZX8081) // O
|
||||
Format("p", result.tapes, Tape::ZX80O81P, TargetPlatform::ZX8081) // P
|
||||
Format("po", result.disks, Disk::DiskImageHolder<Storage::Disk::AppleDSK>, TargetPlatform::DiskII) // PO
|
||||
Format("p81", result.tapes, Tape::ZX80O81P, TargetPlatform::ZX8081) // P81
|
||||
|
||||
// PRG
|
||||
@@ -125,6 +135,7 @@ static Media GetMediaAndPlatforms(const std::string &file_name, TargetPlatform::
|
||||
Format("tsx", result.tapes, Tape::TZX, TargetPlatform::MSX) // TSX
|
||||
Format("tzx", result.tapes, Tape::TZX, TargetPlatform::ZX8081) // TZX
|
||||
Format("uef", result.tapes, Tape::UEF, TargetPlatform::Acorn) // UEF (tape)
|
||||
Format("woz", result.disks, Disk::DiskImageHolder<Storage::Disk::WOZ>, TargetPlatform::DiskII) // WOZ
|
||||
|
||||
#undef Format
|
||||
#undef Insert
|
||||
@@ -138,8 +149,8 @@ Media Analyser::Static::GetMedia(const std::string &file_name) {
|
||||
return GetMediaAndPlatforms(file_name, throwaway);
|
||||
}
|
||||
|
||||
std::vector<std::unique_ptr<Target>> Analyser::Static::GetTargets(const std::string &file_name) {
|
||||
std::vector<std::unique_ptr<Target>> targets;
|
||||
TargetList Analyser::Static::GetTargets(const std::string &file_name) {
|
||||
TargetList targets;
|
||||
|
||||
// Collect all disks, tapes and ROMs as can be extrapolated from this file, forming the
|
||||
// union of all platforms this file might be a target for.
|
||||
@@ -148,17 +159,24 @@ std::vector<std::unique_ptr<Target>> Analyser::Static::GetTargets(const std::str
|
||||
|
||||
// Hand off to platform-specific determination of whether these things are actually compatible and,
|
||||
// if so, how to load them.
|
||||
if(potential_platforms & TargetPlatform::Acorn) Acorn::AddTargets(media, targets);
|
||||
if(potential_platforms & TargetPlatform::AmstradCPC) AmstradCPC::AddTargets(media, targets);
|
||||
if(potential_platforms & TargetPlatform::Atari2600) Atari::AddTargets(media, targets);
|
||||
if(potential_platforms & TargetPlatform::ColecoVision) Coleco::AddTargets(media, targets);
|
||||
if(potential_platforms & TargetPlatform::Commodore) Commodore::AddTargets(media, targets, file_name);
|
||||
if(potential_platforms & TargetPlatform::MSX) MSX::AddTargets(media, targets);
|
||||
if(potential_platforms & TargetPlatform::Oric) Oric::AddTargets(media, targets);
|
||||
if(potential_platforms & TargetPlatform::ZX8081) ZX8081::AddTargets(media, targets, potential_platforms);
|
||||
#define Append(x) {\
|
||||
auto new_targets = x::GetTargets(media, file_name, potential_platforms);\
|
||||
std::move(new_targets.begin(), new_targets.end(), std::back_inserter(targets));\
|
||||
}
|
||||
if(potential_platforms & TargetPlatform::Acorn) Append(Acorn);
|
||||
if(potential_platforms & TargetPlatform::AmstradCPC) Append(AmstradCPC);
|
||||
if(potential_platforms & TargetPlatform::AppleII) Append(AppleII);
|
||||
if(potential_platforms & TargetPlatform::Atari2600) Append(Atari);
|
||||
if(potential_platforms & TargetPlatform::ColecoVision) Append(Coleco);
|
||||
if(potential_platforms & TargetPlatform::Commodore) Append(Commodore);
|
||||
if(potential_platforms & TargetPlatform::DiskII) Append(DiskII);
|
||||
if(potential_platforms & TargetPlatform::MSX) Append(MSX);
|
||||
if(potential_platforms & TargetPlatform::Oric) Append(Oric);
|
||||
if(potential_platforms & TargetPlatform::ZX8081) Append(ZX8081);
|
||||
#undef Append
|
||||
|
||||
// Reset any tapes to their initial position
|
||||
for(auto &target : targets) {
|
||||
for(const auto &target : targets) {
|
||||
for(auto &tape : target->media.tapes) {
|
||||
tape->reset();
|
||||
}
|
||||
@@ -167,9 +185,9 @@ std::vector<std::unique_ptr<Target>> Analyser::Static::GetTargets(const std::str
|
||||
// Sort by initial confidence. Use a stable sort in case any of the machine-specific analysers
|
||||
// picked their insertion order carefully.
|
||||
std::stable_sort(targets.begin(), targets.end(),
|
||||
[] (const std::unique_ptr<Target> &a, const std::unique_ptr<Target> &b) {
|
||||
return a->confidence > b->confidence;
|
||||
});
|
||||
[] (const std::unique_ptr<Target> &a, const std::unique_ptr<Target> &b) {
|
||||
return a->confidence > b->confidence;
|
||||
});
|
||||
|
||||
return targets;
|
||||
}
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
// Clock Signal
|
||||
//
|
||||
// Created by Thomas Harte on 23/08/2016.
|
||||
// Copyright © 2016 Thomas Harte. All rights reserved.
|
||||
// Copyright 2016 Thomas Harte. All rights reserved.
|
||||
//
|
||||
|
||||
#ifndef StaticAnalyser_hpp
|
||||
@@ -15,6 +15,7 @@
|
||||
#include "../../Storage/Disk/Disk.hpp"
|
||||
#include "../../Storage/Cartridge/Cartridge.hpp"
|
||||
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
@@ -43,17 +44,16 @@ struct Target {
|
||||
|
||||
Machine machine;
|
||||
Media media;
|
||||
|
||||
float confidence;
|
||||
std::string loading_command;
|
||||
};
|
||||
typedef std::vector<std::unique_ptr<Target>> TargetList;
|
||||
|
||||
/*!
|
||||
Attempts, through any available means, to return a list of potential targets for the file with the given name.
|
||||
|
||||
@returns The list of potential targets, sorted from most to least probable.
|
||||
*/
|
||||
std::vector<std::unique_ptr<Target>> GetTargets(const std::string &file_name);
|
||||
TargetList GetTargets(const std::string &file_name);
|
||||
|
||||
/*!
|
||||
Inspects the supplied file and determines the media included.
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
// Clock Signal
|
||||
//
|
||||
// Created by Thomas Harte on 04/06/2017.
|
||||
// Copyright © 2017 Thomas Harte. All rights reserved.
|
||||
// Copyright 2017 Thomas Harte. All rights reserved.
|
||||
//
|
||||
|
||||
#include "StaticAnalyser.hpp"
|
||||
@@ -28,7 +28,8 @@ static std::vector<Storage::Data::ZX8081::File> GetFiles(const std::shared_ptr<S
|
||||
return files;
|
||||
}
|
||||
|
||||
void Analyser::Static::ZX8081::AddTargets(const Media &media, std::vector<std::unique_ptr<::Analyser::Static::Target>> &destination, TargetPlatform::IntType potential_platforms) {
|
||||
Analyser::Static::TargetList Analyser::Static::ZX8081::GetTargets(const Media &media, const std::string &file_name, TargetPlatform::IntType potential_platforms) {
|
||||
TargetList destination;
|
||||
if(!media.tapes.empty()) {
|
||||
std::vector<Storage::Data::ZX8081::File> files = GetFiles(media.tapes.front());
|
||||
media.tapes.front()->reset();
|
||||
@@ -65,4 +66,5 @@ void Analyser::Static::ZX8081::AddTargets(const Media &media, std::vector<std::u
|
||||
|
||||
}
|
||||
}
|
||||
return destination;
|
||||
}
|
||||
|
||||
@@ -3,20 +3,21 @@
|
||||
// Clock Signal
|
||||
//
|
||||
// Created by Thomas Harte on 04/06/2017.
|
||||
// Copyright © 2017 Thomas Harte. All rights reserved.
|
||||
// Copyright 2017 Thomas Harte. All rights reserved.
|
||||
//
|
||||
|
||||
#ifndef StaticAnalyser_ZX8081_StaticAnalyser_hpp
|
||||
#define StaticAnalyser_ZX8081_StaticAnalyser_hpp
|
||||
#ifndef Analyser_Static_ZX8081_StaticAnalyser_hpp
|
||||
#define Analyser_Static_ZX8081_StaticAnalyser_hpp
|
||||
|
||||
#include "../StaticAnalyser.hpp"
|
||||
#include "../../../Storage/TargetPlatforms.hpp"
|
||||
#include <string>
|
||||
|
||||
namespace Analyser {
|
||||
namespace Static {
|
||||
namespace ZX8081 {
|
||||
|
||||
void AddTargets(const Media &media, std::vector<std::unique_ptr<Target>> &destination, TargetPlatform::IntType potential_platforms);
|
||||
TargetList GetTargets(const Media &media, const std::string &file_name, TargetPlatform::IntType potential_platforms);
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,13 +3,14 @@
|
||||
// Clock Signal
|
||||
//
|
||||
// Created by Thomas Harte on 09/03/2018.
|
||||
// Copyright © 2018 Thomas Harte. All rights reserved.
|
||||
// Copyright 2018 Thomas Harte. All rights reserved.
|
||||
//
|
||||
|
||||
#ifndef Analyser_Static_ZX8081_Target_h
|
||||
#define Analyser_Static_ZX8081_Target_h
|
||||
|
||||
#include "../StaticAnalyser.hpp"
|
||||
#include <string>
|
||||
|
||||
namespace Analyser {
|
||||
namespace Static {
|
||||
@@ -25,6 +26,7 @@ struct Target: public ::Analyser::Static::Target {
|
||||
MemoryModel memory_model = MemoryModel::Unexpanded;
|
||||
bool is_ZX81 = false;
|
||||
bool ZX80_uses_ZX81_ROM = false;
|
||||
std::string loading_command;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
// Clock Signal
|
||||
//
|
||||
// Created by Thomas Harte on 22/07/2017.
|
||||
// Copyright © 2017 Thomas Harte. All rights reserved.
|
||||
// Copyright 2017 Thomas Harte. All rights reserved.
|
||||
//
|
||||
|
||||
#ifndef ClockReceiver_hpp
|
||||
@@ -52,79 +52,79 @@
|
||||
*/
|
||||
template <class T> class WrappedInt {
|
||||
public:
|
||||
inline WrappedInt(int l) : length_(l) {}
|
||||
inline WrappedInt() : length_(0) {}
|
||||
constexpr WrappedInt(int l) : length_(l) {}
|
||||
constexpr WrappedInt() : length_(0) {}
|
||||
|
||||
inline T &operator =(const T &rhs) {
|
||||
T &operator =(const T &rhs) {
|
||||
length_ = rhs.length_;
|
||||
return *this;
|
||||
}
|
||||
|
||||
inline T &operator +=(const T &rhs) {
|
||||
T &operator +=(const T &rhs) {
|
||||
length_ += rhs.length_;
|
||||
return *static_cast<T *>(this);
|
||||
}
|
||||
|
||||
inline T &operator -=(const T &rhs) {
|
||||
T &operator -=(const T &rhs) {
|
||||
length_ -= rhs.length_;
|
||||
return *static_cast<T *>(this);
|
||||
}
|
||||
|
||||
inline T &operator ++() {
|
||||
T &operator ++() {
|
||||
++ length_;
|
||||
return *static_cast<T *>(this);
|
||||
}
|
||||
|
||||
inline T &operator ++(int) {
|
||||
T &operator ++(int) {
|
||||
length_ ++;
|
||||
return *static_cast<T *>(this);
|
||||
}
|
||||
|
||||
inline T &operator --() {
|
||||
T &operator --() {
|
||||
-- length_;
|
||||
return *static_cast<T *>(this);
|
||||
}
|
||||
|
||||
inline T &operator --(int) {
|
||||
T &operator --(int) {
|
||||
length_ --;
|
||||
return *static_cast<T *>(this);
|
||||
}
|
||||
|
||||
inline T &operator %=(const T &rhs) {
|
||||
T &operator %=(const T &rhs) {
|
||||
length_ %= rhs.length_;
|
||||
return *static_cast<T *>(this);
|
||||
}
|
||||
|
||||
inline T &operator &=(const T &rhs) {
|
||||
T &operator &=(const T &rhs) {
|
||||
length_ &= rhs.length_;
|
||||
return *static_cast<T *>(this);
|
||||
}
|
||||
|
||||
inline T operator +(const T &rhs) const { return T(length_ + rhs.length_); }
|
||||
inline T operator -(const T &rhs) const { return T(length_ - rhs.length_); }
|
||||
constexpr T operator +(const T &rhs) const { return T(length_ + rhs.length_); }
|
||||
constexpr T operator -(const T &rhs) const { return T(length_ - rhs.length_); }
|
||||
|
||||
inline T operator %(const T &rhs) const { return T(length_ % rhs.length_); }
|
||||
inline T operator &(const T &rhs) const { return T(length_ & rhs.length_); }
|
||||
constexpr T operator %(const T &rhs) const { return T(length_ % rhs.length_); }
|
||||
constexpr T operator &(const T &rhs) const { return T(length_ & rhs.length_); }
|
||||
|
||||
inline T operator -() const { return T(- length_); }
|
||||
constexpr T operator -() const { return T(- length_); }
|
||||
|
||||
inline bool operator <(const T &rhs) const { return length_ < rhs.length_; }
|
||||
inline bool operator >(const T &rhs) const { return length_ > rhs.length_; }
|
||||
inline bool operator <=(const T &rhs) const { return length_ <= rhs.length_; }
|
||||
inline bool operator >=(const T &rhs) const { return length_ >= rhs.length_; }
|
||||
inline bool operator ==(const T &rhs) const { return length_ == rhs.length_; }
|
||||
inline bool operator !=(const T &rhs) const { return length_ != rhs.length_; }
|
||||
constexpr bool operator <(const T &rhs) const { return length_ < rhs.length_; }
|
||||
constexpr bool operator >(const T &rhs) const { return length_ > rhs.length_; }
|
||||
constexpr bool operator <=(const T &rhs) const { return length_ <= rhs.length_; }
|
||||
constexpr bool operator >=(const T &rhs) const { return length_ >= rhs.length_; }
|
||||
constexpr bool operator ==(const T &rhs) const { return length_ == rhs.length_; }
|
||||
constexpr bool operator !=(const T &rhs) const { return length_ != rhs.length_; }
|
||||
|
||||
inline bool operator !() const { return !length_; }
|
||||
constexpr bool operator !() const { return !length_; }
|
||||
// bool operator () is not supported because it offers an implicit cast to int, which is prone silently to permit misuse
|
||||
|
||||
inline int as_int() const { return length_; }
|
||||
constexpr int as_int() const { return length_; }
|
||||
|
||||
/*!
|
||||
Severs from @c this the effect of dividing by @c divisor — @c this will end up with
|
||||
Severs from @c this the effect of dividing by @c divisor; @c this will end up with
|
||||
the value of @c this modulo @c divisor and @c divided by @c divisor is returned.
|
||||
*/
|
||||
inline T divide(const T &divisor) {
|
||||
T divide(const T &divisor) {
|
||||
T result(length_ / divisor.length_);
|
||||
length_ %= divisor.length_;
|
||||
return result;
|
||||
@@ -134,7 +134,7 @@ template <class T> class WrappedInt {
|
||||
Flushes the value in @c this. The current value is returned, and the internal value
|
||||
is reset to zero.
|
||||
*/
|
||||
inline T flush() {
|
||||
T flush() {
|
||||
T result(length_);
|
||||
length_ = 0;
|
||||
return result;
|
||||
@@ -147,47 +147,47 @@ template <class T> class WrappedInt {
|
||||
int length_;
|
||||
};
|
||||
|
||||
/// Describes an integer number of whole cycles — pairs of clock signal transitions.
|
||||
/// Describes an integer number of whole cycles: pairs of clock signal transitions.
|
||||
class Cycles: public WrappedInt<Cycles> {
|
||||
public:
|
||||
inline Cycles(int l) : WrappedInt<Cycles>(l) {}
|
||||
inline Cycles() : WrappedInt<Cycles>() {}
|
||||
inline Cycles(const Cycles &cycles) : WrappedInt<Cycles>(cycles.length_) {}
|
||||
constexpr Cycles(int l) : WrappedInt<Cycles>(l) {}
|
||||
constexpr Cycles() : WrappedInt<Cycles>() {}
|
||||
constexpr Cycles(const Cycles &cycles) : WrappedInt<Cycles>(cycles.length_) {}
|
||||
};
|
||||
|
||||
/// Describes an integer number of half cycles — single clock signal transitions.
|
||||
/// Describes an integer number of half cycles: single clock signal transitions.
|
||||
class HalfCycles: public WrappedInt<HalfCycles> {
|
||||
public:
|
||||
inline HalfCycles(int l) : WrappedInt<HalfCycles>(l) {}
|
||||
inline HalfCycles() : WrappedInt<HalfCycles>() {}
|
||||
constexpr HalfCycles(int l) : WrappedInt<HalfCycles>(l) {}
|
||||
constexpr HalfCycles() : WrappedInt<HalfCycles>() {}
|
||||
|
||||
inline HalfCycles(const Cycles cycles) : WrappedInt<HalfCycles>(cycles.as_int() * 2) {}
|
||||
inline HalfCycles(const HalfCycles &half_cycles) : WrappedInt<HalfCycles>(half_cycles.length_) {}
|
||||
constexpr HalfCycles(const Cycles cycles) : WrappedInt<HalfCycles>(cycles.as_int() * 2) {}
|
||||
constexpr HalfCycles(const HalfCycles &half_cycles) : WrappedInt<HalfCycles>(half_cycles.length_) {}
|
||||
|
||||
/// @returns The number of whole cycles completely covered by this span of half cycles.
|
||||
inline Cycles cycles() {
|
||||
constexpr Cycles cycles() const {
|
||||
return Cycles(length_ >> 1);
|
||||
}
|
||||
|
||||
/// Flushes the whole cycles in @c this, subtracting that many from the total stored here.
|
||||
inline Cycles flush_cycles() {
|
||||
Cycles flush_cycles() {
|
||||
Cycles result(length_ >> 1);
|
||||
length_ &= 1;
|
||||
return result;
|
||||
}
|
||||
|
||||
/// Flushes the half cycles in @c this, returning the number stored and setting this total to zero.
|
||||
inline HalfCycles flush() {
|
||||
HalfCycles flush() {
|
||||
HalfCycles result(length_);
|
||||
length_ = 0;
|
||||
return result;
|
||||
}
|
||||
|
||||
/*!
|
||||
Severs from @c this the effect of dividing by @c divisor — @c this will end up with
|
||||
Severs from @c this the effect of dividing by @c divisor; @c this will end up with
|
||||
the value of @c this modulo @c divisor and @c divided by @c divisor is returned.
|
||||
*/
|
||||
inline Cycles divide_cycles(const Cycles &divisor) {
|
||||
Cycles divide_cycles(const Cycles &divisor) {
|
||||
HalfCycles half_divisor = HalfCycles(divisor);
|
||||
Cycles result(length_ / half_divisor.length_);
|
||||
length_ %= half_divisor.length_;
|
||||
@@ -203,7 +203,6 @@ template <class T> class HalfClockReceiver: public T {
|
||||
public:
|
||||
using T::T;
|
||||
|
||||
using T::run_for;
|
||||
inline void run_for(const HalfCycles half_cycles) {
|
||||
half_cycles_ += half_cycles;
|
||||
T::run_for(half_cycles_.flush_cycles());
|
||||
|
||||
88
ClockReceiver/ClockingHintSource.hpp
Normal file
88
ClockReceiver/ClockingHintSource.hpp
Normal file
@@ -0,0 +1,88 @@
|
||||
//
|
||||
// ClockingHintSource.h
|
||||
// Clock Signal
|
||||
//
|
||||
// Created by Thomas Harte on 20/08/2017.
|
||||
// Copyright 2017 Thomas Harte. All rights reserved.
|
||||
//
|
||||
|
||||
#ifndef ClockingHintSource_hpp
|
||||
#define ClockingHintSource_hpp
|
||||
|
||||
namespace ClockingHint {
|
||||
|
||||
enum class Preference {
|
||||
/// The component doesn't currently require a clock signal.
|
||||
None,
|
||||
/// The component can be clocked only immediate prior to (explicit) accesses.
|
||||
JustInTime,
|
||||
/// The component require real-time clocking.
|
||||
RealTime
|
||||
};
|
||||
|
||||
class Source;
|
||||
|
||||
struct Observer {
|
||||
/// Called to inform an observer that the component @c component has changed its clocking requirements.
|
||||
virtual void set_component_prefers_clocking(Source *component, Preference clocking) = 0;
|
||||
};
|
||||
|
||||
/*!
|
||||
An clocking hint source is any component that can provide hints as to the type of
|
||||
clocking required for accurate emulation. A disk controller is an archetypal example.
|
||||
|
||||
Types of clocking are:
|
||||
|
||||
- none:
|
||||
a component that acts and reacts to direct contact but does not have a state that autonomously evolves.
|
||||
E.g. a ROM, RAM, or some kinds of disk controller when not in the process of performing a command.
|
||||
|
||||
- just-in-time:
|
||||
a component that has an evolving state but can receive clock updates only immediately before a
|
||||
direct contact. This is possibly the most common kind of component.
|
||||
|
||||
- real-time:
|
||||
a component that needs to be clocked in 'real time' (i.e. in terms of the emulated machine). For example
|
||||
so that it can announce an interrupt at the proper moment, because it is monitoring some aspect of
|
||||
the machine rather than waiting to be called upon, or because there's some other non-obvious relationship
|
||||
at play.
|
||||
|
||||
A clocking hint source can signal changes in preferred clocking to an observer.
|
||||
|
||||
This is intended to allow for performance improvements to machines with components that can be messaged selectively.
|
||||
The observer callout is virtual so the intended use case is that a machine holds a component that might go through
|
||||
periods of different clocking requirements.
|
||||
|
||||
Transitions should be sufficiently infrequent that a virtual call to announce them costs little enough that
|
||||
the saved or deferred ::run_fors add up to a substantial amount.
|
||||
|
||||
The hint provided is just that: a hint. Owners may perform ::run_for at a greater frequency.
|
||||
*/
|
||||
class Source {
|
||||
public:
|
||||
/// Registers @c observer as the new clocking observer.
|
||||
void set_clocking_hint_observer(Observer *observer) {
|
||||
observer_ = observer;
|
||||
update_clocking_observer();
|
||||
}
|
||||
|
||||
/// @returns the current preferred clocking strategy.
|
||||
virtual Preference preferred_clocking() = 0;
|
||||
|
||||
private:
|
||||
Observer *observer_ = nullptr;
|
||||
|
||||
protected:
|
||||
/*!
|
||||
Provided for subclasses; call this whenever the clocking preference might have changed.
|
||||
This will notify the observer if there is one.
|
||||
*/
|
||||
void update_clocking_observer() {
|
||||
if(!observer_) return;
|
||||
observer_->set_component_prefers_clocking(this, preferred_clocking());
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif /* ClockingHintSource_h */
|
||||
@@ -3,7 +3,7 @@
|
||||
// Clock Signal
|
||||
//
|
||||
// Created by Thomas Harte on 01/08/2017.
|
||||
// Copyright © 2017 Thomas Harte. All rights reserved.
|
||||
// Copyright 2017 Thomas Harte. All rights reserved.
|
||||
//
|
||||
|
||||
#ifndef ForceInline_hpp
|
||||
|
||||
@@ -1,60 +0,0 @@
|
||||
//
|
||||
// Sleeper.h
|
||||
// Clock Signal
|
||||
//
|
||||
// Created by Thomas Harte on 20/08/2017.
|
||||
// Copyright © 2017 Thomas Harte. All rights reserved.
|
||||
//
|
||||
|
||||
#ifndef Sleeper_hpp
|
||||
#define Sleeper_hpp
|
||||
|
||||
/*!
|
||||
A sleeper is any component that sometimes requires a clock but at other times is 'asleep' — i.e. is not doing
|
||||
any clock-derived work, so needn't receive a clock. A disk controller is an archetypal example.
|
||||
|
||||
A sleeper will signal sleeps and wakes to an observer.
|
||||
|
||||
This is intended to allow for performance improvements to machines with components that can sleep. The observer
|
||||
callout is virtual so the intended use case is that a machine holds a component that might sleep. Its transitions
|
||||
into and out of sleep are sufficiently infrequent that a virtual call to announce them costs sufficiently little that
|
||||
the saved ::run_fors add up to a substantial amount.
|
||||
|
||||
By convention, sleeper components must be willing to accept ::run_for even after announcing sleep. It's a hint,
|
||||
not a command.
|
||||
*/
|
||||
class Sleeper {
|
||||
public:
|
||||
Sleeper() : sleep_observer_(nullptr) {}
|
||||
|
||||
class SleepObserver {
|
||||
public:
|
||||
/// Called to inform an observer that the component @c component has either gone to sleep or become awake.
|
||||
virtual void set_component_is_sleeping(void *component, bool is_sleeping) = 0;
|
||||
};
|
||||
|
||||
/// Registers @c observer as the new sleep observer;
|
||||
void set_sleep_observer(SleepObserver *observer) {
|
||||
sleep_observer_ = observer;
|
||||
}
|
||||
|
||||
/// @returns @c true if the component is currently sleeping; @c false otherwise.
|
||||
virtual bool is_sleeping() = 0;
|
||||
|
||||
protected:
|
||||
/// Provided for subclasses; send sleep announcements to the sleep_observer_.
|
||||
SleepObserver *sleep_observer_;
|
||||
|
||||
/*!
|
||||
Provided for subclasses; call this whenever is_sleeping might have changed, and the observer will be notified,
|
||||
if one exists.
|
||||
|
||||
@c is_sleeping will be called only if there is an observer.
|
||||
*/
|
||||
void update_sleep_observer() {
|
||||
if(!sleep_observer_) return;
|
||||
sleep_observer_->set_component_is_sleeping(this, is_sleeping());
|
||||
}
|
||||
};
|
||||
|
||||
#endif /* Sleeper_h */
|
||||
@@ -3,7 +3,7 @@
|
||||
// Clock Signal
|
||||
//
|
||||
// Created by Thomas Harte on 21/03/2018.
|
||||
// Copyright © 2018 Thomas Harte. All rights reserved.
|
||||
// Copyright 2018 Thomas Harte. All rights reserved.
|
||||
//
|
||||
|
||||
#ifndef TimeTypes_h
|
||||
|
||||
@@ -3,11 +3,13 @@
|
||||
// Clock Signal
|
||||
//
|
||||
// Created by Thomas Harte on 17/09/2016.
|
||||
// Copyright © 2016 Thomas Harte. All rights reserved.
|
||||
// Copyright 2016 Thomas Harte. All rights reserved.
|
||||
//
|
||||
|
||||
#include "1770.hpp"
|
||||
|
||||
#include "../../Storage/Disk/Encodings/MFM/Constants.hpp"
|
||||
#include "../../Outputs/Log.hpp"
|
||||
|
||||
using namespace WD;
|
||||
|
||||
@@ -25,10 +27,10 @@ void WD1770::set_register(int address, uint8_t value) {
|
||||
if((value&0xf0) == 0xd0) {
|
||||
if(value == 0xd0) {
|
||||
// Force interrupt **immediately**.
|
||||
printf("Force interrupt immediately\n");
|
||||
LOG("Force interrupt immediately");
|
||||
posit_event(static_cast<int>(Event1770::ForceInterrupt));
|
||||
} else {
|
||||
printf("!!!TODO: force interrupt!!!\n");
|
||||
ERROR("!!!TODO: force interrupt!!!");
|
||||
update_status([] (Status &status) {
|
||||
status.type = Status::One;
|
||||
});
|
||||
@@ -193,7 +195,7 @@ void WD1770::posit_event(int new_event_type) {
|
||||
// Wait for a new command, branch to the appropriate handler.
|
||||
case 0:
|
||||
wait_for_command:
|
||||
printf("Idle...\n");
|
||||
LOG("Idle...");
|
||||
set_data_mode(DataMode::Scanning);
|
||||
index_hole_count_ = 0;
|
||||
|
||||
@@ -209,7 +211,7 @@ void WD1770::posit_event(int new_event_type) {
|
||||
status.interrupt_request = false;
|
||||
});
|
||||
|
||||
printf("Starting %02x\n", command_);
|
||||
LOG("Starting " << std::hex << command_ << std::endl);
|
||||
|
||||
if(!(command_ & 0x80)) goto begin_type_1;
|
||||
if(!(command_ & 0x40)) goto begin_type_2;
|
||||
@@ -282,7 +284,7 @@ void WD1770::posit_event(int new_event_type) {
|
||||
track_ = 0;
|
||||
goto verify;
|
||||
}
|
||||
get_drive().step(step_direction_ ? 1 : -1);
|
||||
get_drive().step(Storage::Disk::HeadPosition(step_direction_ ? 1 : -1));
|
||||
unsigned int time_to_wait;
|
||||
switch(command_ & 3) {
|
||||
default:
|
||||
@@ -327,7 +329,7 @@ void WD1770::posit_event(int new_event_type) {
|
||||
}
|
||||
|
||||
if(header_[0] == track_) {
|
||||
printf("Reached track %d\n", track_);
|
||||
LOG("Reached track " << std::dec << track_);
|
||||
update_status([] (Status &status) {
|
||||
status.crc_error = false;
|
||||
});
|
||||
@@ -396,20 +398,20 @@ void WD1770::posit_event(int new_event_type) {
|
||||
READ_ID();
|
||||
|
||||
if(index_hole_count_ == 5) {
|
||||
printf("Failed to find sector %d\n", sector_);
|
||||
LOG("Failed to find sector " << std::dec << sector_);
|
||||
update_status([] (Status &status) {
|
||||
status.record_not_found = true;
|
||||
});
|
||||
goto wait_for_command;
|
||||
}
|
||||
if(distance_into_section_ == 7) {
|
||||
printf("Considering %d/%d\n", header_[0], header_[2]);
|
||||
LOG("Considering " << std::dec << header_[0] << "/" << header_[2]);
|
||||
set_data_mode(DataMode::Scanning);
|
||||
if( header_[0] == track_ && header_[2] == sector_ &&
|
||||
(has_motor_on_line() || !(command_&0x02) || ((command_&0x08) >> 3) == header_[1])) {
|
||||
printf("Found %d/%d\n", header_[0], header_[2]);
|
||||
LOG("Found " << std::dec << header_[0] << "/" << header_[2]);
|
||||
if(get_crc_generator().get_value()) {
|
||||
printf("CRC error; back to searching\n");
|
||||
LOG("CRC error; back to searching");
|
||||
update_status([] (Status &status) {
|
||||
status.crc_error = true;
|
||||
});
|
||||
@@ -465,7 +467,7 @@ void WD1770::posit_event(int new_event_type) {
|
||||
distance_into_section_++;
|
||||
if(distance_into_section_ == 2) {
|
||||
if(get_crc_generator().get_value()) {
|
||||
printf("CRC error; terminating\n");
|
||||
LOG("CRC error; terminating");
|
||||
update_status([this] (Status &status) {
|
||||
status.crc_error = true;
|
||||
});
|
||||
@@ -476,7 +478,7 @@ void WD1770::posit_event(int new_event_type) {
|
||||
sector_++;
|
||||
goto test_type2_write_protection;
|
||||
}
|
||||
printf("Finished reading sector %d\n", sector_);
|
||||
LOG("Finished reading sector " << std::dec << sector_);
|
||||
goto wait_for_command;
|
||||
}
|
||||
goto type2_check_crc;
|
||||
@@ -522,7 +524,7 @@ void WD1770::posit_event(int new_event_type) {
|
||||
type2_write_loop:
|
||||
/*
|
||||
This deviates from the data sheet slightly since that would prima facie request one more byte
|
||||
of data than is actually written — the last time around the loop it has transferred from the
|
||||
of data than is actually written; the last time around the loop it has transferred from the
|
||||
data register to the data shift register, set data request, written the byte, checked that data
|
||||
request has been satified, then finally considers whether all bytes are done. Based on both
|
||||
natural expectations and the way that emulated machines responded, I believe that to be a
|
||||
@@ -558,7 +560,7 @@ void WD1770::posit_event(int new_event_type) {
|
||||
sector_++;
|
||||
goto test_type2_write_protection;
|
||||
}
|
||||
printf("Wrote sector %d\n", sector_);
|
||||
LOG("Wrote sector " << std::dec << sector_);
|
||||
goto wait_for_command;
|
||||
|
||||
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
// Clock Signal
|
||||
//
|
||||
// Created by Thomas Harte on 17/09/2016.
|
||||
// Copyright © 2016 Thomas Harte. All rights reserved.
|
||||
// Copyright 2016 Thomas Harte. All rights reserved.
|
||||
//
|
||||
|
||||
#ifndef _770_hpp
|
||||
@@ -43,7 +43,6 @@ class WD1770: public Storage::Disk::MFMController {
|
||||
|
||||
/// Runs the controller for @c number_of_cycles cycles.
|
||||
void run_for(const Cycles cycles);
|
||||
using Storage::Disk::Controller::run_for;
|
||||
|
||||
enum Flag: uint8_t {
|
||||
NotReady = 0x80,
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
// Clock Signal
|
||||
//
|
||||
// Created by Thomas Harte on 06/06/2016.
|
||||
// Copyright © 2016 Thomas Harte. All rights reserved.
|
||||
// Copyright 2016 Thomas Harte. All rights reserved.
|
||||
//
|
||||
|
||||
#ifndef _522_hpp
|
||||
@@ -113,6 +113,9 @@ template <class T> class MOS6522: public MOS6522Base {
|
||||
/*! Gets a register value. */
|
||||
uint8_t get_register(int address);
|
||||
|
||||
/*! @returns the bus handler. */
|
||||
T &bus_handler();
|
||||
|
||||
private:
|
||||
T &bus_handler_;
|
||||
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
// Clock Signal
|
||||
//
|
||||
// Created by Thomas Harte on 04/09/2017.
|
||||
// Copyright © 2017 Thomas Harte. All rights reserved.
|
||||
// Copyright 2017 Thomas Harte. All rights reserved.
|
||||
//
|
||||
|
||||
#include "../6522.hpp"
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
// Clock Signal
|
||||
//
|
||||
// Created by Thomas Harte on 04/09/2017.
|
||||
// Copyright © 2017 Thomas Harte. All rights reserved.
|
||||
// Copyright 2017 Thomas Harte. All rights reserved.
|
||||
//
|
||||
|
||||
template <typename T> void MOS6522<T>::set_register(int address, uint8_t value) {
|
||||
@@ -145,6 +145,10 @@ template <typename T> uint8_t MOS6522<T>::get_port_input(Port port, uint8_t outp
|
||||
return (input & ~output_mask) | (output & output_mask);
|
||||
}
|
||||
|
||||
template <typename T> T &MOS6522<T>::bus_handler() {
|
||||
return bus_handler_;
|
||||
}
|
||||
|
||||
// Delegate and communications
|
||||
template <typename T> void MOS6522<T>::reevaluate_interrupts() {
|
||||
bool new_interrupt_status = get_interrupt_line();
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
// Clock Signal
|
||||
//
|
||||
// Created by Thomas Harte on 04/09/2017.
|
||||
// Copyright © 2017 Thomas Harte. All rights reserved.
|
||||
// Copyright 2017 Thomas Harte. All rights reserved.
|
||||
//
|
||||
|
||||
#ifndef _522Storage_hpp
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
// Clock Signal
|
||||
//
|
||||
// Created by Thomas Harte on 04/09/2017.
|
||||
// Copyright © 2017 Thomas Harte. All rights reserved.
|
||||
// Copyright 2017 Thomas Harte. All rights reserved.
|
||||
//
|
||||
|
||||
#include "../6522.hpp"
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
// Clock Signal
|
||||
//
|
||||
// Created by Thomas Harte on 19/06/2016.
|
||||
// Copyright © 2016 Thomas Harte. All rights reserved.
|
||||
// Copyright 2016 Thomas Harte. All rights reserved.
|
||||
//
|
||||
|
||||
#ifndef _532_hpp
|
||||
|
||||
@@ -3,14 +3,14 @@
|
||||
// Clock Signal
|
||||
//
|
||||
// Created by Thomas Harte on 05/06/2016.
|
||||
// Copyright © 2016 Thomas Harte. All rights reserved.
|
||||
// Copyright 2016 Thomas Harte. All rights reserved.
|
||||
//
|
||||
|
||||
#include "6560.hpp"
|
||||
|
||||
#include <cstring>
|
||||
|
||||
using namespace MOS;
|
||||
using namespace MOS::MOS6560;
|
||||
|
||||
AudioGenerator::AudioGenerator(Concurrency::DeferringAsyncTaskQueue &audio_queue) :
|
||||
audio_queue_(audio_queue) {}
|
||||
@@ -106,7 +106,7 @@ static uint8_t noise_pattern[] = {
|
||||
// means every second cycle, etc.
|
||||
|
||||
void AudioGenerator::get_samples(std::size_t number_of_samples, int16_t *target) {
|
||||
for(unsigned int c = 0; c < number_of_samples; c++) {
|
||||
for(unsigned int c = 0; c < number_of_samples; ++c) {
|
||||
update(0, 2, shift);
|
||||
update(1, 1, shift);
|
||||
update(2, 0, shift);
|
||||
@@ -124,7 +124,7 @@ void AudioGenerator::get_samples(std::size_t number_of_samples, int16_t *target)
|
||||
}
|
||||
|
||||
void AudioGenerator::skip_samples(std::size_t number_of_samples) {
|
||||
for(unsigned int c = 0; c < number_of_samples; c++) {
|
||||
for(unsigned int c = 0; c < number_of_samples; ++c) {
|
||||
update(0, 2, shift);
|
||||
update(1, 1, shift);
|
||||
update(2, 0, shift);
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
// Clock Signal
|
||||
//
|
||||
// Created by Thomas Harte on 05/06/2016.
|
||||
// Copyright © 2016 Thomas Harte. All rights reserved.
|
||||
// Copyright 2016 Thomas Harte. All rights reserved.
|
||||
//
|
||||
|
||||
#ifndef _560_hpp
|
||||
@@ -16,6 +16,7 @@
|
||||
#include "../../Outputs/Speaker/Implementation/SampleSource.hpp"
|
||||
|
||||
namespace MOS {
|
||||
namespace MOS6560 {
|
||||
|
||||
// audio state
|
||||
class AudioGenerator: public ::Outputs::Speaker::SampleSource {
|
||||
@@ -40,6 +41,17 @@ class AudioGenerator: public ::Outputs::Speaker::SampleSource {
|
||||
int16_t range_multiplier_ = 1;
|
||||
};
|
||||
|
||||
struct BusHandler {
|
||||
void perform_read(uint16_t address, uint8_t *pixel_data, uint8_t *colour_data) {
|
||||
*pixel_data = 0xff;
|
||||
*colour_data = 0xff;
|
||||
}
|
||||
};
|
||||
|
||||
enum class OutputMode {
|
||||
PAL, NTSC
|
||||
};
|
||||
|
||||
/*!
|
||||
The 6560 Video Interface Chip ('VIC') is a video and audio output chip; it therefore vends both a @c CRT and a @c Speaker.
|
||||
|
||||
@@ -48,15 +60,16 @@ class AudioGenerator: public ::Outputs::Speaker::SampleSource {
|
||||
|
||||
@c set_register and @c get_register provide register access.
|
||||
*/
|
||||
template <class T> class MOS6560 {
|
||||
template <class BusHandler> class MOS6560 {
|
||||
public:
|
||||
MOS6560() :
|
||||
MOS6560(BusHandler &bus_handler) :
|
||||
bus_handler_(bus_handler),
|
||||
crt_(new Outputs::CRT::CRT(65*4, 4, Outputs::CRT::DisplayType::NTSC60, 2)),
|
||||
audio_generator_(audio_queue_),
|
||||
speaker_(audio_generator_)
|
||||
{
|
||||
crt_->set_svideo_sampling_function(
|
||||
"vec2 svideo_sample(usampler2D texID, vec2 coordinate, vec2 iCoordinate, float phase)"
|
||||
"vec2 svideo_sample(usampler2D texID, vec2 coordinate, vec2 iCoordinate, float phase, float amplitude)"
|
||||
"{"
|
||||
"vec2 yc = texture(texID, coordinate).rg / vec2(255.0);"
|
||||
|
||||
@@ -88,25 +101,21 @@ template <class T> class MOS6560 {
|
||||
speaker_.set_high_frequency_cutoff(cutoff);
|
||||
}
|
||||
|
||||
enum OutputMode {
|
||||
PAL, NTSC
|
||||
};
|
||||
|
||||
/*!
|
||||
Sets the output mode to either PAL or NTSC.
|
||||
*/
|
||||
void set_output_mode(OutputMode output_mode) {
|
||||
output_mode_ = output_mode;
|
||||
|
||||
// Luminances are encoded trivially: on a 0–255 scale.
|
||||
// Luminances are encoded trivially: on a 0-255 scale.
|
||||
const uint8_t luminances[16] = {
|
||||
0, 255, 60, 189,
|
||||
80, 144, 40, 227,
|
||||
90, 161, 207, 227,
|
||||
200, 196, 160, 196
|
||||
0, 255, 64, 192,
|
||||
128, 128, 64, 192,
|
||||
128, 192, 128, 255,
|
||||
192, 192, 128, 255
|
||||
};
|
||||
|
||||
// Chrominances are encoded such that 0–128 is a complete revolution of phase;
|
||||
// Chrominances are encoded such that 0-128 is a complete revolution of phase;
|
||||
// anything above 191 disables the colour subcarrier. Phase is relative to the
|
||||
// colour burst, so 0 is green.
|
||||
const uint8_t pal_chrominances[16] = {
|
||||
@@ -116,10 +125,10 @@ template <class T> class MOS6560 {
|
||||
19, 86, 123, 59,
|
||||
};
|
||||
const uint8_t ntsc_chrominances[16] = {
|
||||
255, 255, 7, 71,
|
||||
25, 86, 48, 112,
|
||||
0, 119, 7, 71,
|
||||
25, 86, 48, 112,
|
||||
255, 255, 121, 57,
|
||||
103, 42, 80, 16,
|
||||
0, 9, 121, 57,
|
||||
103, 42, 80, 16,
|
||||
};
|
||||
const uint8_t *chrominances;
|
||||
Outputs::CRT::DisplayType display_type;
|
||||
@@ -129,7 +138,8 @@ template <class T> class MOS6560 {
|
||||
chrominances = pal_chrominances;
|
||||
display_type = Outputs::CRT::DisplayType::PAL50;
|
||||
timing_.cycles_per_line = 71;
|
||||
timing_.line_counter_increment_offset = 0;
|
||||
timing_.line_counter_increment_offset = 4;
|
||||
timing_.final_line_increment_position = timing_.cycles_per_line - timing_.line_counter_increment_offset;
|
||||
timing_.lines_per_progressive_field = 312;
|
||||
timing_.supports_interlacing = false;
|
||||
break;
|
||||
@@ -138,7 +148,8 @@ template <class T> class MOS6560 {
|
||||
chrominances = ntsc_chrominances;
|
||||
display_type = Outputs::CRT::DisplayType::NTSC60;
|
||||
timing_.cycles_per_line = 65;
|
||||
timing_.line_counter_increment_offset = 65 - 33; // TODO: this is a bit of a hack; separate vertical and horizontal counting
|
||||
timing_.line_counter_increment_offset = 40;
|
||||
timing_.final_line_increment_position = 58;
|
||||
timing_.lines_per_progressive_field = 261;
|
||||
timing_.supports_interlacing = true;
|
||||
break;
|
||||
@@ -148,7 +159,7 @@ template <class T> class MOS6560 {
|
||||
|
||||
switch(output_mode) {
|
||||
case OutputMode::PAL:
|
||||
crt_->set_visible_area(Outputs::CRT::Rect(0.1f, 0.05f, 0.9f, 0.9f));
|
||||
crt_->set_visible_area(Outputs::CRT::Rect(0.1f, 0.07f, 0.9f, 0.9f));
|
||||
break;
|
||||
case OutputMode::NTSC:
|
||||
crt_->set_visible_area(Outputs::CRT::Rect(0.05f, 0.05f, 0.9f, 0.9f));
|
||||
@@ -176,7 +187,6 @@ template <class T> class MOS6560 {
|
||||
|
||||
// keep track of internal time relative to this scanline
|
||||
horizontal_counter_++;
|
||||
full_frame_counter_++;
|
||||
if(horizontal_counter_ == timing_.cycles_per_line) {
|
||||
if(horizontal_drawing_latch_) {
|
||||
current_character_row_++;
|
||||
@@ -198,9 +208,8 @@ template <class T> class MOS6560 {
|
||||
horizontal_drawing_latch_ = false;
|
||||
|
||||
vertical_counter_ ++;
|
||||
if(vertical_counter_ == (registers_.interlaced ? (is_odd_frame_ ? 262 : 263) : timing_.lines_per_progressive_field)) {
|
||||
if(vertical_counter_ == lines_this_field()) {
|
||||
vertical_counter_ = 0;
|
||||
full_frame_counter_ = 0;
|
||||
|
||||
if(output_mode_ == OutputMode::NTSC) is_odd_frame_ ^= true;
|
||||
current_row_ = 0;
|
||||
@@ -248,7 +257,7 @@ template <class T> class MOS6560 {
|
||||
|
||||
uint8_t pixel_data;
|
||||
uint8_t colour_data;
|
||||
static_cast<T *>(this)->perform_read(fetch_address, &pixel_data, &colour_data);
|
||||
bus_handler_.perform_read(fetch_address, &pixel_data, &colour_data);
|
||||
|
||||
// TODO: there should be a further two-cycle delay on pixels being output; the reverse bit should
|
||||
// divide the byte it is set for 3:1 and then continue as usual.
|
||||
@@ -262,7 +271,7 @@ template <class T> class MOS6560 {
|
||||
|
||||
// apply vertical sync
|
||||
if(
|
||||
(vertical_counter_ < 3 && (is_odd_frame_ || !registers_.interlaced)) ||
|
||||
(vertical_counter_ < 3 && is_odd_frame()) ||
|
||||
(registers_.interlaced &&
|
||||
(
|
||||
(vertical_counter_ == 0 && horizontal_counter_ > 32) ||
|
||||
@@ -278,7 +287,7 @@ template <class T> class MOS6560 {
|
||||
case State::Sync: crt_->output_sync(cycles_in_state_ * 4); break;
|
||||
case State::ColourBurst: crt_->output_colour_burst(cycles_in_state_ * 4, (is_odd_frame_ || is_odd_line_) ? 128 : 0); break;
|
||||
case State::Border: output_border(cycles_in_state_ * 4); break;
|
||||
case State::Pixels: crt_->output_data(cycles_in_state_ * 4, 1); break;
|
||||
case State::Pixels: crt_->output_data(cycles_in_state_ * 4); break;
|
||||
}
|
||||
output_state_ = this_state_;
|
||||
cycles_in_state_ = 0;
|
||||
@@ -291,6 +300,9 @@ template <class T> class MOS6560 {
|
||||
cycles_in_state_++;
|
||||
|
||||
if(this_state_ == State::Pixels) {
|
||||
// TODO: palette changes can happen within half-characters; the below needs to be divided.
|
||||
// Also: a perfect opportunity to rearrange this inner loop for no longer needing to be
|
||||
// two parts with a cooperative owner?
|
||||
if(column_counter_&1) {
|
||||
character_value_ = pixel_data;
|
||||
|
||||
@@ -331,7 +343,10 @@ template <class T> class MOS6560 {
|
||||
character_code_ = pixel_data;
|
||||
character_colour_ = colour_data;
|
||||
}
|
||||
}
|
||||
|
||||
// Keep counting columns even if sync or the colour burst have interceded.
|
||||
if(column_counter_ >= 0 && column_counter_ < columns_this_line_*2) {
|
||||
column_counter_++;
|
||||
}
|
||||
}
|
||||
@@ -414,15 +429,15 @@ template <class T> class MOS6560 {
|
||||
*/
|
||||
uint8_t get_register(int address) {
|
||||
address &= 0xf;
|
||||
int current_line = (full_frame_counter_ + timing_.line_counter_increment_offset) / timing_.cycles_per_line;
|
||||
switch(address) {
|
||||
default: return registers_.direct_values[address];
|
||||
case 0x03: return static_cast<uint8_t>(current_line << 7) | (registers_.direct_values[3] & 0x7f);
|
||||
case 0x04: return (current_line >> 1) & 0xff;
|
||||
case 0x03: return static_cast<uint8_t>(raster_value() << 7) | (registers_.direct_values[3] & 0x7f);
|
||||
case 0x04: return (raster_value() >> 1) & 0xff;
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
BusHandler &bus_handler_;
|
||||
std::unique_ptr<Outputs::CRT::CRT> crt_;
|
||||
|
||||
Concurrency::DeferringAsyncTaskQueue audio_queue_;
|
||||
@@ -453,7 +468,29 @@ template <class T> class MOS6560 {
|
||||
unsigned int cycles_in_state_;
|
||||
|
||||
// counters that cover an entire field
|
||||
int horizontal_counter_ = 0, vertical_counter_ = 0, full_frame_counter_;
|
||||
int horizontal_counter_ = 0, vertical_counter_ = 0;
|
||||
const int lines_this_field() {
|
||||
// Necessary knowledge here: only the NTSC 6560 supports interlaced video.
|
||||
return registers_.interlaced ? (is_odd_frame_ ? 262 : 263) : timing_.lines_per_progressive_field;
|
||||
}
|
||||
const int raster_value() {
|
||||
const int bonus_line = (horizontal_counter_ + timing_.line_counter_increment_offset) / timing_.cycles_per_line;
|
||||
const int line = vertical_counter_ + bonus_line;
|
||||
const int final_line = lines_this_field();
|
||||
|
||||
if(line < final_line)
|
||||
return line;
|
||||
|
||||
if(is_odd_frame()) {
|
||||
return (horizontal_counter_ >= timing_.final_line_increment_position) ? 0 : final_line - 1;
|
||||
} else {
|
||||
return line % final_line;
|
||||
}
|
||||
// Cf. http://www.sleepingelephant.com/ipw-web/bulletin/bb/viewtopic.php?f=14&t=7237&start=15#p80737
|
||||
}
|
||||
bool is_odd_frame() {
|
||||
return is_odd_frame_ || !registers_.interlaced;
|
||||
}
|
||||
|
||||
// latches dictating start and length of drawing
|
||||
bool vertical_drawing_latch_, horizontal_drawing_latch_;
|
||||
@@ -483,12 +520,14 @@ template <class T> class MOS6560 {
|
||||
struct {
|
||||
int cycles_per_line;
|
||||
int line_counter_increment_offset;
|
||||
int final_line_increment_position;
|
||||
int lines_per_progressive_field;
|
||||
bool supports_interlacing;
|
||||
} timing_;
|
||||
OutputMode output_mode_;
|
||||
};
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
#endif /* _560_hpp */
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
// Clock Signal
|
||||
//
|
||||
// Created by Thomas Harte on 31/07/2017.
|
||||
// Copyright © 2017 Thomas Harte. All rights reserved.
|
||||
// Copyright 2017 Thomas Harte. All rights reserved.
|
||||
//
|
||||
|
||||
#ifndef CRTC6845_hpp
|
||||
@@ -35,7 +35,7 @@ class BusHandler {
|
||||
void perform_bus_cycle_phase1(const BusState &) {}
|
||||
|
||||
/*!
|
||||
Performs the second phase of a 6845 bus cycle. Some bus state — including sync — is updated
|
||||
Performs the second phase of a 6845 bus cycle. Some bus state, including sync, is updated
|
||||
directly after phase 1 and hence is visible to an observer during phase 2. Handlers may therefore
|
||||
implement @c perform_bus_cycle_phase2 to be notified of the availability of that state without
|
||||
having to wait until the next cycle has begun.
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
// Clock Signal
|
||||
//
|
||||
// Created by Thomas Harte on 01/08/2017.
|
||||
// Copyright © 2017 Thomas Harte. All rights reserved.
|
||||
// Copyright 2017 Thomas Harte. All rights reserved.
|
||||
//
|
||||
|
||||
#ifndef i8255_hpp
|
||||
|
||||
@@ -3,13 +3,12 @@
|
||||
// Clock Signal
|
||||
//
|
||||
// Created by Thomas Harte on 05/08/2017.
|
||||
// Copyright © 2017 Thomas Harte. All rights reserved.
|
||||
// Copyright 2017 Thomas Harte. All rights reserved.
|
||||
//
|
||||
|
||||
#include "i8272.hpp"
|
||||
//#include "../../Storage/Disk/Encodings/MFM/Encoder.hpp"
|
||||
|
||||
#include <cstdio>
|
||||
#include "../../Outputs/Log.hpp"
|
||||
|
||||
using namespace Intel::i8272;
|
||||
|
||||
@@ -83,8 +82,10 @@ i8272::i8272(BusHandler &bus_handler, Cycles clock_rate) :
|
||||
posit_event(static_cast<int>(Event8272::CommandByte));
|
||||
}
|
||||
|
||||
bool i8272::is_sleeping() {
|
||||
return is_sleeping_ && Storage::Disk::MFMController::is_sleeping();
|
||||
ClockingHint::Preference i8272::preferred_clocking() {
|
||||
const auto mfm_controller_preferred_clocking = Storage::Disk::MFMController::preferred_clocking();
|
||||
if(mfm_controller_preferred_clocking != ClockingHint::Preference::None) return mfm_controller_preferred_clocking;
|
||||
return is_sleeping_ ? ClockingHint::Preference::None : ClockingHint::Preference::JustInTime;
|
||||
}
|
||||
|
||||
void i8272::run_for(Cycles cycles) {
|
||||
@@ -113,9 +114,9 @@ void i8272::run_for(Cycles cycles) {
|
||||
while(steps--) {
|
||||
// Perform a step.
|
||||
int direction = (drives_[c].target_head_position < drives_[c].head_position) ? -1 : 1;
|
||||
printf("Target %d versus believed %d\n", drives_[c].target_head_position, drives_[c].head_position);
|
||||
LOG("Target " << PADDEC(0) << drives_[c].target_head_position << " versus believed " << static_cast<int>(drives_[c].head_position));
|
||||
select_drive(c);
|
||||
get_drive().step(direction);
|
||||
get_drive().step(Storage::Disk::HeadPosition(direction));
|
||||
if(drives_[c].target_head_position >= 0) drives_[c].head_position += direction;
|
||||
|
||||
// Check for completion.
|
||||
@@ -159,7 +160,7 @@ void i8272::run_for(Cycles cycles) {
|
||||
}
|
||||
|
||||
is_sleeping_ = !delay_time_ && !drives_seeking_ && !head_timers_running_;
|
||||
if(is_sleeping_) update_sleep_observer();
|
||||
if(is_sleeping_) update_clocking_observer();
|
||||
}
|
||||
|
||||
void i8272::set_register(int address, uint8_t value) {
|
||||
@@ -198,7 +199,7 @@ uint8_t i8272::get_register(int address) {
|
||||
|
||||
#define MS_TO_CYCLES(x) x * 8000
|
||||
#define WAIT_FOR_EVENT(mask) resume_point_ = __LINE__; interesting_event_mask_ = static_cast<int>(mask); return; case __LINE__:
|
||||
#define WAIT_FOR_TIME(ms) resume_point_ = __LINE__; interesting_event_mask_ = static_cast<int>(Event8272::Timer); delay_time_ = MS_TO_CYCLES(ms); is_sleeping_ = false; update_sleep_observer(); case __LINE__: if(delay_time_) return;
|
||||
#define WAIT_FOR_TIME(ms) resume_point_ = __LINE__; interesting_event_mask_ = static_cast<int>(Event8272::Timer); delay_time_ = MS_TO_CYCLES(ms); is_sleeping_ = false; update_clocking_observer(); case __LINE__: if(delay_time_) return;
|
||||
|
||||
#define PASTE(x, y) x##y
|
||||
#define CONCAT(x, y) PASTE(x, y)
|
||||
@@ -257,7 +258,7 @@ uint8_t i8272::get_register(int address) {
|
||||
if(drives_[active_drive_].head_unload_delay[active_head_] == 0) { \
|
||||
head_timers_running_++; \
|
||||
is_sleeping_ = false; \
|
||||
update_sleep_observer(); \
|
||||
update_clocking_observer(); \
|
||||
} \
|
||||
drives_[active_drive_].head_unload_delay[active_head_] = MS_TO_CYCLES(head_unload_time_);\
|
||||
}
|
||||
@@ -384,17 +385,17 @@ void i8272::posit_event(int event_type) {
|
||||
// the index hole limit is breached or a sector is found with a cylinder, head, sector and size equal to the
|
||||
// values in the internal registers.
|
||||
index_hole_limit_ = 2;
|
||||
// printf("Seeking %02x %02x %02x %02x\n", cylinder_, head_, sector_, size_);
|
||||
// LOG("Seeking " << PADDEC(0) << cylinder_ << " " << head_ " " << sector_ << " " << size_);
|
||||
find_next_sector:
|
||||
FIND_HEADER();
|
||||
if(!index_hole_limit_) {
|
||||
// Two index holes have passed wihout finding the header sought.
|
||||
// printf("Not found\n");
|
||||
// LOG("Not found");
|
||||
SetNoData();
|
||||
goto abort;
|
||||
}
|
||||
index_hole_count_ = 0;
|
||||
// printf("Header\n");
|
||||
// LOG("Header");
|
||||
READ_HEADER();
|
||||
if(index_hole_count_) {
|
||||
// This implies an index hole was sighted within the header. Error out.
|
||||
@@ -405,11 +406,11 @@ void i8272::posit_event(int event_type) {
|
||||
// This implies a CRC error in the header; mark as such but continue.
|
||||
SetDataError();
|
||||
}
|
||||
// printf("Considering %02x %02x %02x %02x [%04x]\n", header_[0], header_[1], header_[2], header_[3], get_crc_generator().get_value());
|
||||
// LOG("Considering << PADHEX(2) << header_[0] << " " << header_[1] << " " << header_[2] << " " << header_[3] << " [" << get_crc_generator().get_value() << "]");
|
||||
if(header_[0] != cylinder_ || header_[1] != head_ || header_[2] != sector_ || header_[3] != size_) goto find_next_sector;
|
||||
|
||||
// Branch to whatever is supposed to happen next
|
||||
// printf("Proceeding\n");
|
||||
// LOG("Proceeding");
|
||||
switch(command_[0] & 0x1f) {
|
||||
case CommandReadData:
|
||||
case CommandReadDeletedData:
|
||||
@@ -423,7 +424,13 @@ void i8272::posit_event(int event_type) {
|
||||
|
||||
// Performs the read data or read deleted data command.
|
||||
read_data:
|
||||
printf("Read [deleted] data [%02x %02x %02x %02x ... %02x %02x]\n", command_[2], command_[3], command_[4], command_[5], command_[6], command_[8]);
|
||||
LOG(PADHEX(2) << "Read [deleted] data ["
|
||||
<< static_cast<int>(command_[2]) << " "
|
||||
<< static_cast<int>(command_[3]) << " "
|
||||
<< static_cast<int>(command_[4]) << " "
|
||||
<< static_cast<int>(command_[5]) << " ... "
|
||||
<< static_cast<int>(command_[6]) << " "
|
||||
<< static_cast<int>(command_[8]) << "]");
|
||||
read_next_data:
|
||||
goto read_write_find_header;
|
||||
|
||||
@@ -434,7 +441,7 @@ void i8272::posit_event(int event_type) {
|
||||
ClearControlMark();
|
||||
if(event_type == static_cast<int>(Event::Token)) {
|
||||
if(get_latest_token().type != Token::Data && get_latest_token().type != Token::DeletedData) {
|
||||
// Something other than a data mark came next — impliedly an ID or index mark.
|
||||
// Something other than a data mark came next, impliedly an ID or index mark.
|
||||
SetMissingAddressMark();
|
||||
SetMissingDataAddressMark();
|
||||
goto abort; // TODO: or read_next_data?
|
||||
@@ -507,7 +514,13 @@ void i8272::posit_event(int event_type) {
|
||||
goto post_st012chrn;
|
||||
|
||||
write_data:
|
||||
printf("Write [deleted] data [%02x %02x %02x %02x ... %02x %02x]\n", command_[2], command_[3], command_[4], command_[5], command_[6], command_[8]);
|
||||
LOG(PADHEX(2) << "Write [deleted] data ["
|
||||
<< static_cast<int>(command_[2]) << " "
|
||||
<< static_cast<int>(command_[3]) << " "
|
||||
<< static_cast<int>(command_[4]) << " "
|
||||
<< static_cast<int>(command_[5]) << " ... "
|
||||
<< static_cast<int>(command_[6]) << " "
|
||||
<< static_cast<int>(command_[8]) << "]");
|
||||
|
||||
if(get_drive().get_is_read_only()) {
|
||||
SetNotWriteable();
|
||||
@@ -542,7 +555,7 @@ void i8272::posit_event(int event_type) {
|
||||
goto write_loop;
|
||||
}
|
||||
|
||||
printf("Wrote %d bytes\n", distance_into_section_);
|
||||
LOG("Wrote " << PADDEC(0) << distance_into_section_ << " bytes");
|
||||
write_crc();
|
||||
expects_input_ = false;
|
||||
WAIT_FOR_EVENT(Event::DataWritten);
|
||||
@@ -558,7 +571,7 @@ void i8272::posit_event(int event_type) {
|
||||
// Performs the read ID command.
|
||||
read_id:
|
||||
// Establishes the drive and head being addressed, and whether in double density mode.
|
||||
printf("Read ID [%02x %02x]\n", command_[0], command_[1]);
|
||||
LOG(PADHEX(2) << "Read ID [" << static_cast<int>(command_[0]) << " " << static_cast<int>(command_[1]) << "]");
|
||||
|
||||
// Sets a maximum index hole limit of 2 then waits either until it finds a header mark or sees too many index holes.
|
||||
// If a header mark is found, reads in the following bytes that produce a header. Otherwise branches to data not found.
|
||||
@@ -580,7 +593,11 @@ void i8272::posit_event(int event_type) {
|
||||
|
||||
// Performs read track.
|
||||
read_track:
|
||||
printf("Read track [%02x %02x %02x %02x]\n", command_[2], command_[3], command_[4], command_[5]);
|
||||
LOG(PADHEX(2) << "Read track ["
|
||||
<< static_cast<int>(command_[2]) << " "
|
||||
<< static_cast<int>(command_[3]) << " "
|
||||
<< static_cast<int>(command_[4]) << " "
|
||||
<< static_cast<int>(command_[5]) << "]");
|
||||
|
||||
// Wait for the index hole.
|
||||
WAIT_FOR_EVENT(Event::IndexHole);
|
||||
@@ -621,7 +638,7 @@ void i8272::posit_event(int event_type) {
|
||||
|
||||
// Performs format [/write] track.
|
||||
format_track:
|
||||
printf("Format track\n");
|
||||
LOG("Format track");
|
||||
if(get_drive().get_is_read_only()) {
|
||||
SetNotWriteable();
|
||||
goto abort;
|
||||
@@ -665,7 +682,12 @@ void i8272::posit_event(int event_type) {
|
||||
break;
|
||||
}
|
||||
|
||||
printf("W: %02x %02x %02x %02x, %04x\n", header_[0], header_[1], header_[2], header_[3], get_crc_generator().get_value());
|
||||
LOG(PADHEX(2) << "W:"
|
||||
<< static_cast<int>(header_[0]) << " "
|
||||
<< static_cast<int>(header_[1]) << " "
|
||||
<< static_cast<int>(header_[2]) << " "
|
||||
<< static_cast<int>(header_[3]) << ", "
|
||||
<< get_crc_generator().get_value());
|
||||
write_crc();
|
||||
|
||||
// Write the sector body.
|
||||
@@ -697,15 +719,15 @@ void i8272::posit_event(int event_type) {
|
||||
goto post_st012chrn;
|
||||
|
||||
scan_low:
|
||||
printf("Scan low unimplemented!!\n");
|
||||
ERROR("Scan low unimplemented!!");
|
||||
goto wait_for_command;
|
||||
|
||||
scan_low_or_equal:
|
||||
printf("Scan low or equal unimplemented!!\n");
|
||||
ERROR("Scan low or equal unimplemented!!");
|
||||
goto wait_for_command;
|
||||
|
||||
scan_high_or_equal:
|
||||
printf("Scan high or equal unimplemented!!\n");
|
||||
ERROR("Scan high or equal unimplemented!!");
|
||||
goto wait_for_command;
|
||||
|
||||
// Performs both recalibrate and seek commands. These commands occur asynchronously, so the actual work
|
||||
@@ -720,7 +742,7 @@ void i8272::posit_event(int event_type) {
|
||||
if(drives_[drive].phase != Drive::Seeking) {
|
||||
drives_seeking_++;
|
||||
is_sleeping_ = false;
|
||||
update_sleep_observer();
|
||||
update_clocking_observer();
|
||||
}
|
||||
|
||||
// Set currently seeking, with a step to occur right now (yes, it sounds like jamming these
|
||||
@@ -736,11 +758,11 @@ void i8272::posit_event(int event_type) {
|
||||
// up in run_for understands to mean 'keep going until track 0 is active').
|
||||
if(command_.size() > 2) {
|
||||
drives_[drive].target_head_position = command_[2];
|
||||
printf("Seek to %02x\n", command_[2]);
|
||||
LOG(PADHEX(2) << "Seek to " << static_cast<int>(command_[2]));
|
||||
} else {
|
||||
drives_[drive].target_head_position = -1;
|
||||
drives_[drive].head_position = 0;
|
||||
printf("Recalibrate\n");
|
||||
LOG("Recalibrate");
|
||||
}
|
||||
|
||||
// Check whether any steps are even needed; if not then mark as completed already.
|
||||
@@ -753,7 +775,7 @@ void i8272::posit_event(int event_type) {
|
||||
|
||||
// Performs sense interrupt status.
|
||||
sense_interrupt_status:
|
||||
printf("Sense interrupt status\n");
|
||||
LOG("Sense interrupt status");
|
||||
{
|
||||
// Find the first drive that is in the CompletedSeeking state.
|
||||
int found_drive = -1;
|
||||
@@ -781,7 +803,7 @@ void i8272::posit_event(int event_type) {
|
||||
// Performs specify.
|
||||
specify:
|
||||
// Just store the values, and terminate the command.
|
||||
printf("Specify\n");
|
||||
LOG("Specify");
|
||||
step_rate_time_ = 16 - (command_[1] >> 4); // i.e. 1 to 16ms
|
||||
head_unload_time_ = (command_[1] & 0x0f) << 4; // i.e. 16 to 240ms
|
||||
head_load_time_ = command_[2] & ~1; // i.e. 2 to 254 ms in increments of 2ms
|
||||
@@ -792,7 +814,7 @@ void i8272::posit_event(int event_type) {
|
||||
goto wait_for_command;
|
||||
|
||||
sense_drive_status:
|
||||
printf("Sense drive status\n");
|
||||
LOG("Sense drive status");
|
||||
{
|
||||
int drive = command_[1] & 3;
|
||||
select_drive(drive);
|
||||
@@ -828,14 +850,14 @@ void i8272::posit_event(int event_type) {
|
||||
|
||||
goto post_result;
|
||||
|
||||
// Posts whatever is in result_stack_ as a result phase. Be aware that it is a stack — the
|
||||
// Posts whatever is in result_stack_ as a result phase. Be aware that it is a stack, so the
|
||||
// last thing in it will be returned first.
|
||||
post_result:
|
||||
printf("Result to %02x, main %02x: ", command_[0] & 0x1f, main_status_);
|
||||
LOGNBR(PADHEX(2) << "Result to " << static_cast<int>(command_[0] & 0x1f) << ", main " << static_cast<int>(main_status_) << "; ");
|
||||
for(std::size_t c = 0; c < result_stack_.size(); c++) {
|
||||
printf("%02x ", result_stack_[result_stack_.size() - 1 - c]);
|
||||
LOGNBR(" " << static_cast<int>(result_stack_[result_stack_.size() - 1 - c]));
|
||||
}
|
||||
printf("\n");
|
||||
LOGNBR(std::endl);
|
||||
|
||||
// Set ready to send data to the processor, no longer in non-DMA execution phase.
|
||||
is_executing_ = false;
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
// Clock Signal
|
||||
//
|
||||
// Created by Thomas Harte on 05/08/2017.
|
||||
// Copyright © 2017 Thomas Harte. All rights reserved.
|
||||
// Copyright 2017 Thomas Harte. All rights reserved.
|
||||
//
|
||||
|
||||
#ifndef i8272_hpp
|
||||
@@ -39,7 +39,7 @@ class i8272: public Storage::Disk::MFMController {
|
||||
void set_dma_acknowledge(bool dack);
|
||||
void set_terminal_count(bool tc);
|
||||
|
||||
bool is_sleeping();
|
||||
ClockingHint::Preference preferred_clocking() override;
|
||||
|
||||
protected:
|
||||
virtual void select_drive(int number) = 0;
|
||||
@@ -67,7 +67,7 @@ class i8272: public Storage::Disk::MFMController {
|
||||
ResultEmpty = (1 << 5),
|
||||
NoLongerReady = (1 << 6)
|
||||
};
|
||||
void posit_event(int type);
|
||||
void posit_event(int type) override;
|
||||
int interesting_event_mask_ = static_cast<int>(Event8272::CommandByte);
|
||||
int resume_point_ = 0;
|
||||
bool is_access_command_ = false;
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
// Clock Signal
|
||||
//
|
||||
// Created by Thomas Harte on 25/11/2017.
|
||||
// Copyright © 2017 Thomas Harte. All rights reserved.
|
||||
// Copyright 2017 Thomas Harte. All rights reserved.
|
||||
//
|
||||
|
||||
#include "9918.hpp"
|
||||
@@ -536,7 +536,8 @@ void TMS9918::run_for(const HalfCycles cycles) {
|
||||
}
|
||||
|
||||
if(output_column_ == first_right_border_column_) {
|
||||
crt_->output_data(static_cast<unsigned int>(first_right_border_column_ - first_pixel_column_) * 4, 4);
|
||||
const unsigned int data_length = static_cast<unsigned int>(first_right_border_column_ - first_pixel_column_);
|
||||
crt_->output_data(data_length * 4, data_length);
|
||||
pixel_target_ = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
// Clock Signal
|
||||
//
|
||||
// Created by Thomas Harte on 25/11/2017.
|
||||
// Copyright © 2017 Thomas Harte. All rights reserved.
|
||||
// Copyright 2017 Thomas Harte. All rights reserved.
|
||||
//
|
||||
|
||||
#ifndef TMS9918_hpp
|
||||
@@ -56,7 +56,7 @@ class TMS9918: public TMS9918Base {
|
||||
|
||||
/*!
|
||||
Runs the VCP for the number of cycles indicate; it is an implicit assumption of the code
|
||||
that the input clock rate is 3579545 Hz — the NTSC colour clock rate.
|
||||
that the input clock rate is 3579545 Hz, the NTSC colour clock rate.
|
||||
*/
|
||||
void run_for(const HalfCycles cycles);
|
||||
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
// Clock Signal
|
||||
//
|
||||
// Created by Thomas Harte on 14/12/2017.
|
||||
// Copyright © 2017 Thomas Harte. All rights reserved.
|
||||
// Copyright 2017 Thomas Harte. All rights reserved.
|
||||
//
|
||||
|
||||
#ifndef TMS9918Base_hpp
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
// Clock Signal
|
||||
//
|
||||
// Created by Thomas Harte on 14/10/2016.
|
||||
// Copyright © 2016 Thomas Harte. All rights reserved.
|
||||
// Copyright 2016 Thomas Harte. All rights reserved.
|
||||
//
|
||||
|
||||
#include "AY38910.hpp"
|
||||
@@ -103,7 +103,7 @@ void AY38910::get_samples(std::size_t number_of_samples, int16_t *target) {
|
||||
noise_shift_register_ >>= 1;
|
||||
}
|
||||
|
||||
// ... and the envelope generator. Table based for pattern lookup, with a 'refill' step — a way of
|
||||
// ... and the envelope generator. Table based for pattern lookup, with a 'refill' step: a way of
|
||||
// implementing non-repeating patterns by locking them to table position 0x1f.
|
||||
if(envelope_divider_) envelope_divider_--;
|
||||
else {
|
||||
@@ -130,7 +130,7 @@ void AY38910::evaluate_output_volume() {
|
||||
// The output level for a channel is:
|
||||
// 1 if neither tone nor noise is enabled;
|
||||
// 0 if either tone or noise is enabled and its value is low.
|
||||
// The tone/noise enable bits use inverse logic — 0 = on, 1 = off — permitting the OR logic below.
|
||||
// The tone/noise enable bits use inverse logic; 0 = on, 1 = off; permitting the OR logic below.
|
||||
#define tone_level(c, tone_bit) (tone_outputs_[c] | (output_registers_[7] >> tone_bit))
|
||||
#define noise_level(c, noise_bit) (noise_output_ | (output_registers_[7] >> noise_bit))
|
||||
|
||||
@@ -225,14 +225,10 @@ uint8_t AY38910::get_register_value() {
|
||||
};
|
||||
|
||||
if(selected_register_ > 15) return 0xff;
|
||||
switch(selected_register_) {
|
||||
default: return registers_[selected_register_] & register_masks[selected_register_];
|
||||
case 14: return (registers_[0x7] & 0x40) ? registers_[14] : port_inputs_[0];
|
||||
case 15: return (registers_[0x7] & 0x80) ? registers_[15] : port_inputs_[1];
|
||||
}
|
||||
return registers_[selected_register_] & register_masks[selected_register_];
|
||||
}
|
||||
|
||||
// MARK: - Port handling
|
||||
// MARK: - Port querying
|
||||
|
||||
uint8_t AY38910::get_port_output(bool port_b) {
|
||||
return registers_[port_b ? 15 : 14];
|
||||
@@ -250,11 +246,16 @@ void AY38910::set_data_input(uint8_t r) {
|
||||
}
|
||||
|
||||
uint8_t AY38910::get_data_output() {
|
||||
if(control_state_ == Read && selected_register_ >= 14) {
|
||||
if(port_handler_) {
|
||||
return port_handler_->get_port_input(selected_register_ == 15);
|
||||
} else {
|
||||
return 0xff;
|
||||
if(control_state_ == Read && selected_register_ >= 14 && selected_register_ < 16) {
|
||||
// Per http://cpctech.cpc-live.com/docs/psgnotes.htm if a port is defined as output then the
|
||||
// value returned to the CPU when reading it is the and of the output value and any input.
|
||||
// If it's defined as input then you just get the input.
|
||||
const uint8_t mask = port_handler_ ? port_handler_->get_port_input(selected_register_ == 15) : 0xff;
|
||||
|
||||
switch(selected_register_) {
|
||||
default: break;
|
||||
case 14: return mask & ((registers_[0x7] & 0x40) ? registers_[14] : 0xff);
|
||||
case 15: return mask & ((registers_[0x7] & 0x80) ? registers_[15] : 0xff);
|
||||
}
|
||||
}
|
||||
return data_output_;
|
||||
@@ -276,8 +277,10 @@ void AY38910::set_control_lines(ControlLines control_lines) {
|
||||
}
|
||||
|
||||
void AY38910::update_bus() {
|
||||
// Assume no output, unless this turns out to be a read.
|
||||
data_output_ = 0xff;
|
||||
switch(control_state_) {
|
||||
default: break;
|
||||
default: break;
|
||||
case LatchAddress: select_register(data_input_); break;
|
||||
case Write: set_register_value(data_input_); break;
|
||||
case Read: data_output_ = get_register_value(); break;
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
// Clock Signal
|
||||
//
|
||||
// Created by Thomas Harte on 14/10/2016.
|
||||
// Copyright © 2016 Thomas Harte. All rights reserved.
|
||||
// Copyright 2016 Thomas Harte. All rights reserved.
|
||||
//
|
||||
|
||||
#ifndef AY_3_8910_hpp
|
||||
@@ -92,9 +92,8 @@ class AY38910: public ::Outputs::Speaker::SampleSource {
|
||||
Concurrency::DeferringAsyncTaskQueue &task_queue_;
|
||||
|
||||
int selected_register_ = 0;
|
||||
uint8_t registers_[16];
|
||||
uint8_t registers_[16] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
|
||||
uint8_t output_registers_[16] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
|
||||
uint8_t port_inputs_[2];
|
||||
|
||||
int master_divider_ = 0;
|
||||
|
||||
|
||||
38
Components/AudioToggle/AudioToggle.cpp
Normal file
38
Components/AudioToggle/AudioToggle.cpp
Normal file
@@ -0,0 +1,38 @@
|
||||
//
|
||||
// AudioToggle.cpp
|
||||
// Clock Signal
|
||||
//
|
||||
// Created by Thomas Harte on 17/04/2018.
|
||||
// Copyright 2018 Thomas Harte. All rights reserved.
|
||||
//
|
||||
|
||||
#include "AudioToggle.hpp"
|
||||
|
||||
using namespace Audio;
|
||||
|
||||
Audio::Toggle::Toggle(Concurrency::DeferringAsyncTaskQueue &audio_queue) :
|
||||
audio_queue_(audio_queue) {}
|
||||
|
||||
void Toggle::get_samples(std::size_t number_of_samples, std::int16_t *target) {
|
||||
for(std::size_t sample = 0; sample < number_of_samples; ++sample) {
|
||||
target[sample] = level_;
|
||||
}
|
||||
}
|
||||
|
||||
void Toggle::set_sample_volume_range(std::int16_t range) {
|
||||
volume_ = range;
|
||||
}
|
||||
|
||||
void Toggle::skip_samples(const std::size_t number_of_samples) {}
|
||||
|
||||
void Toggle::set_output(bool enabled) {
|
||||
if(is_enabled_ == enabled) return;
|
||||
is_enabled_ = enabled;
|
||||
audio_queue_.defer([=] {
|
||||
level_ = enabled ? volume_ : 0;
|
||||
});
|
||||
}
|
||||
|
||||
bool Toggle::get_output() {
|
||||
return is_enabled_;
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user