mirror of
https://github.com/TomHarte/CLK.git
synced 2025-10-26 17:17:58 +00:00
Compare commits
14 Commits
2020-11-21
...
2020-11-29
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
32ccce3040 | ||
|
|
ab3fcb3ea0 | ||
|
|
9610672615 | ||
|
|
5ee9630624 | ||
|
|
1b3836eb1c | ||
|
|
1302a046e9 | ||
|
|
33dec3c220 | ||
|
|
7c29c3a944 | ||
|
|
c9ca1fc7a0 | ||
|
|
a965c8de9f | ||
|
|
0b4b271e3d | ||
|
|
5fc6dd1a4d | ||
|
|
79ef026b93 | ||
|
|
a4ab5b0b49 |
@@ -804,38 +804,62 @@ template <Analyser::Static::AppleII::Target::Model model> class ConcreteMachine:
|
|||||||
m6502_.run_for(cycles);
|
m6502_.run_for(cycles);
|
||||||
}
|
}
|
||||||
|
|
||||||
void reset_all_keys(Inputs::Keyboard *) final {
|
void reset_all_keys() final {
|
||||||
open_apple_is_pressed_ = closed_apple_is_pressed_ = key_is_down_ = false;
|
open_apple_is_pressed_ = closed_apple_is_pressed_ = key_is_down_ = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool set_key_pressed(Key key, char value, bool is_pressed) final {
|
bool prefers_logical_input() final {
|
||||||
switch(key) {
|
|
||||||
default: break;
|
|
||||||
case Key::F12:
|
|
||||||
m6502_.set_reset_line(is_pressed);
|
|
||||||
return true;
|
|
||||||
case Key::LeftOption:
|
|
||||||
open_apple_is_pressed_ = is_pressed;
|
|
||||||
return true;
|
|
||||||
case Key::RightOption:
|
|
||||||
closed_apple_is_pressed_ = is_pressed;
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool set_key_pressed(Key key, char value, bool is_pressed) final {
|
||||||
// If no ASCII value is supplied, look for a few special cases.
|
// If no ASCII value is supplied, look for a few special cases.
|
||||||
if(!value) {
|
|
||||||
switch(key) {
|
switch(key) {
|
||||||
case Key::Left: value = 0x08; break;
|
case Key::Left: value = 0x08; break;
|
||||||
case Key::Right: value = 0x15; break;
|
case Key::Right: value = 0x15; break;
|
||||||
case Key::Down: value = 0x0a; break;
|
case Key::Down: value = 0x0a; break;
|
||||||
case Key::Up: value = 0x0b; break;
|
case Key::Up: value = 0x0b; break;
|
||||||
case Key::Backspace: value = 0x7f; break;
|
case Key::Backspace: value = 0x7f; break;
|
||||||
default: return false;
|
case Key::Enter: value = 0x0d; break;
|
||||||
}
|
case Key::Tab: value = '\t'; break;
|
||||||
|
case Key::Escape: value = 0x1b; break;
|
||||||
|
|
||||||
|
case Key::LeftOption:
|
||||||
|
case Key::RightMeta:
|
||||||
|
open_apple_is_pressed_ = is_pressed;
|
||||||
|
return true;
|
||||||
|
|
||||||
|
case Key::RightOption:
|
||||||
|
case Key::LeftMeta:
|
||||||
|
closed_apple_is_pressed_ = is_pressed;
|
||||||
|
return true;
|
||||||
|
|
||||||
|
case Key::F1: case Key::F2: case Key::F3: case Key::F4:
|
||||||
|
case Key::F5: case Key::F6: case Key::F7: case Key::F8:
|
||||||
|
case Key::F9: case Key::F10: case Key::F11: case Key::F12:
|
||||||
|
case Key::PrintScreen:
|
||||||
|
case Key::ScrollLock:
|
||||||
|
case Key::Pause:
|
||||||
|
case Key::Insert:
|
||||||
|
case Key::Home:
|
||||||
|
case Key::PageUp:
|
||||||
|
case Key::PageDown:
|
||||||
|
case Key::End:
|
||||||
|
// Accept a bunch non-symbolic other keys, as
|
||||||
|
// reset, in the hope that the user can find
|
||||||
|
// at least one usable key.
|
||||||
|
m6502_.set_reset_line(is_pressed);
|
||||||
|
return true;
|
||||||
|
|
||||||
|
default:
|
||||||
|
if(!value) {
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Prior to the IIe, the keyboard could produce uppercase only.
|
// Prior to the IIe, the keyboard could produce uppercase only.
|
||||||
if(!is_iie()) value = char(toupper(value));
|
if(!is_iie()) value = char(toupper(value));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
if(is_pressed) {
|
if(is_pressed) {
|
||||||
keyboard_input_ = uint8_t(value | 0x80);
|
keyboard_input_ = uint8_t(value | 0x80);
|
||||||
|
|||||||
@@ -32,6 +32,11 @@ struct KeyActions {
|
|||||||
Instructs that all keys should now be treated as released.
|
Instructs that all keys should now be treated as released.
|
||||||
*/
|
*/
|
||||||
virtual void clear_all_keys() {}
|
virtual void clear_all_keys() {}
|
||||||
|
|
||||||
|
/*!
|
||||||
|
Indicates whether a machine most naturally accepts logical rather than physical input.
|
||||||
|
*/
|
||||||
|
virtual bool prefers_logical_input() { return false; }
|
||||||
};
|
};
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
|
|||||||
@@ -5175,6 +5175,7 @@
|
|||||||
"$(USER_LIBRARY_DIR)/Frameworks",
|
"$(USER_LIBRARY_DIR)/Frameworks",
|
||||||
);
|
);
|
||||||
GCC_C_LANGUAGE_STANDARD = gnu11;
|
GCC_C_LANGUAGE_STANDARD = gnu11;
|
||||||
|
MACOSX_DEPLOYMENT_TARGET = 10.13;
|
||||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||||
};
|
};
|
||||||
name = Debug;
|
name = Debug;
|
||||||
@@ -5197,6 +5198,8 @@
|
|||||||
GCC_C_LANGUAGE_STANDARD = gnu11;
|
GCC_C_LANGUAGE_STANDARD = gnu11;
|
||||||
GCC_OPTIMIZATION_LEVEL = 2;
|
GCC_OPTIMIZATION_LEVEL = 2;
|
||||||
GCC_PREPROCESSOR_DEFINITIONS = NDEBUG;
|
GCC_PREPROCESSOR_DEFINITIONS = NDEBUG;
|
||||||
|
MACOSX_DEPLOYMENT_TARGET = 10.13;
|
||||||
|
ONLY_ACTIVE_ARCH = YES;
|
||||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||||
};
|
};
|
||||||
name = Release;
|
name = Release;
|
||||||
|
|||||||
@@ -31,7 +31,7 @@
|
|||||||
</Testables>
|
</Testables>
|
||||||
</TestAction>
|
</TestAction>
|
||||||
<LaunchAction
|
<LaunchAction
|
||||||
buildConfiguration = "Release"
|
buildConfiguration = "Debug"
|
||||||
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
|
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
|
||||||
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
|
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
|
||||||
disableMainThreadChecker = "YES"
|
disableMainThreadChecker = "YES"
|
||||||
@@ -58,8 +58,16 @@
|
|||||||
</CommandLineArgument>
|
</CommandLineArgument>
|
||||||
<CommandLineArgument
|
<CommandLineArgument
|
||||||
argument = ""/Users/thomasharte/Library/Mobile Documents/com~apple~CloudDocs/Desktop/Soft/Macintosh/MusicWorks 0.42.image""
|
argument = ""/Users/thomasharte/Library/Mobile Documents/com~apple~CloudDocs/Desktop/Soft/Macintosh/MusicWorks 0.42.image""
|
||||||
|
isEnabled = "NO">
|
||||||
|
</CommandLineArgument>
|
||||||
|
<CommandLineArgument
|
||||||
|
argument = "/Users/thomasharte/Library/Mobile\ Documents/com\~apple\~CloudDocs/Desktop/Soft/Apple\ II/Keplermatik.dsk"
|
||||||
isEnabled = "YES">
|
isEnabled = "YES">
|
||||||
</CommandLineArgument>
|
</CommandLineArgument>
|
||||||
|
<CommandLineArgument
|
||||||
|
argument = "/Users/thomasharte/Library/Mobile\ Documents/com\~apple\~CloudDocs/Desktop/Soft/Apple\ II/WOZs/Prince\ of\ Persia\ side\ A.woz"
|
||||||
|
isEnabled = "NO">
|
||||||
|
</CommandLineArgument>
|
||||||
<CommandLineArgument
|
<CommandLineArgument
|
||||||
argument = "--volume=0.001"
|
argument = "--volume=0.001"
|
||||||
isEnabled = "NO">
|
isEnabled = "NO">
|
||||||
|
|||||||
@@ -1020,7 +1020,7 @@ bool MainWindow::processEvent(QKeyEvent *event) {
|
|||||||
const auto keyboardMachine = machine->keyboard_machine();
|
const auto keyboardMachine = machine->keyboard_machine();
|
||||||
if(!keyboardMachine) return true;
|
if(!keyboardMachine) return true;
|
||||||
|
|
||||||
auto keyboard = keyboardMachine->get_keyboard();
|
auto &keyboard = keyboardMachine->get_keyboard();
|
||||||
keyboard.set_key_pressed(*key, event->text().size() ? event->text()[0].toLatin1() : '\0', isPressed);
|
keyboard.set_key_pressed(*key, event->text().size() ? event->text()[0].toLatin1() : '\0', isPressed);
|
||||||
if(keyboard.is_exclusive() || keyboard.observed_keys().find(*key) != keyboard.observed_keys().end()) {
|
if(keyboard.is_exclusive() || keyboard.observed_keys().find(*key) != keyboard.observed_keys().end()) {
|
||||||
return false;
|
return false;
|
||||||
|
|||||||
@@ -783,8 +783,10 @@ int main(int argc, char *argv[]) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check whether a 'logical' keyboard has been requested.
|
// Check whether a 'logical' keyboard has been requested, or the machine would prefer one anyway.
|
||||||
const bool logical_keyboard = arguments.selections.find("logical-keyboard") != arguments.selections.end();
|
const bool logical_keyboard =
|
||||||
|
(arguments.selections.find("logical-keyboard") != arguments.selections.end()) ||
|
||||||
|
(machine->keyboard_machine() && machine->keyboard_machine()->prefers_logical_input());
|
||||||
if(logical_keyboard) {
|
if(logical_keyboard) {
|
||||||
SDL_StartTextInput();
|
SDL_StartTextInput();
|
||||||
}
|
}
|
||||||
@@ -1162,13 +1164,8 @@ int main(int argc, char *argv[]) {
|
|||||||
} else {
|
} else {
|
||||||
// This is a slightly terrible way of obtaining a symbol for the key, e.g. for letters it will always return
|
// This is a slightly terrible way of obtaining a symbol for the key, e.g. for letters it will always return
|
||||||
// the capital letter version, at least empirically. But it'll have to do for now.
|
// the capital letter version, at least empirically. But it'll have to do for now.
|
||||||
//
|
|
||||||
// TODO: ideally have a keyboard machine declare whether it wants either key events or text input? But that
|
|
||||||
// doesn't match machines like the IIe that, to some extent, expose both. So then eliding as attempted above,
|
|
||||||
// and keeping ephemeral track of which symbols have been tied to which keys for the benefit of future key up
|
|
||||||
// events is probably the way forward?
|
|
||||||
const char *key_name = SDL_GetKeyName(keypress.keycode);
|
const char *key_name = SDL_GetKeyName(keypress.keycode);
|
||||||
if(keyboard_machine->get_keyboard().set_key_pressed(key, key_name[0], keypress.is_down)) {
|
if(keyboard_machine->get_keyboard().set_key_pressed(key, (strlen(key_name) == 1) ? key_name[0] : 0, keypress.is_down)) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -194,8 +194,9 @@ std::vector<std::string> ScanTarget::bindings(ShaderType type) const {
|
|||||||
std::string ScanTarget::sampling_function() const {
|
std::string ScanTarget::sampling_function() const {
|
||||||
std::string fragment_shader;
|
std::string fragment_shader;
|
||||||
const auto modals = BufferingScanTarget::modals();
|
const auto modals = BufferingScanTarget::modals();
|
||||||
|
const bool is_svideo = modals.display_type == DisplayType::SVideo;
|
||||||
|
|
||||||
if(modals.display_type == DisplayType::SVideo) {
|
if(is_svideo) {
|
||||||
fragment_shader +=
|
fragment_shader +=
|
||||||
"vec2 svideo_sample(vec2 coordinate, float angle) {";
|
"vec2 svideo_sample(vec2 coordinate, float angle) {";
|
||||||
} else {
|
} else {
|
||||||
@@ -203,7 +204,6 @@ std::string ScanTarget::sampling_function() const {
|
|||||||
"float composite_sample(vec2 coordinate, float angle) {";
|
"float composite_sample(vec2 coordinate, float angle) {";
|
||||||
}
|
}
|
||||||
|
|
||||||
const bool is_svideo = modals.display_type == DisplayType::SVideo;
|
|
||||||
switch(modals.input_data_type) {
|
switch(modals.input_data_type) {
|
||||||
case InputDataType::Luminance1:
|
case InputDataType::Luminance1:
|
||||||
case InputDataType::Luminance8:
|
case InputDataType::Luminance8:
|
||||||
@@ -341,7 +341,7 @@ std::unique_ptr<Shader> ScanTarget::conversion_shader() const {
|
|||||||
vertex_shader +=
|
vertex_shader +=
|
||||||
"compositeAngle = (mix(startCompositeAngle, endCompositeAngle, lateral) / 32.0) * 3.141592654;"
|
"compositeAngle = (mix(startCompositeAngle, endCompositeAngle, lateral) / 32.0) * 3.141592654;"
|
||||||
"compositeAmplitude = lineCompositeAmplitude / 255.0;"
|
"compositeAmplitude = lineCompositeAmplitude / 255.0;"
|
||||||
"oneOverCompositeAmplitude = mix(0.0, 255.0 / lineCompositeAmplitude, step(0.01, lineCompositeAmplitude));";
|
"oneOverCompositeAmplitude = mix(0.0, 255.0 / lineCompositeAmplitude, step(0.95, lineCompositeAmplitude));";
|
||||||
}
|
}
|
||||||
|
|
||||||
vertex_shader +=
|
vertex_shader +=
|
||||||
@@ -379,40 +379,42 @@ std::unique_ptr<Shader> ScanTarget::conversion_shader() const {
|
|||||||
|
|
||||||
switch(modals.display_type) {
|
switch(modals.display_type) {
|
||||||
case DisplayType::CompositeColour:
|
case DisplayType::CompositeColour:
|
||||||
fragment_shader +=
|
fragment_shader += R"x(
|
||||||
"vec4 angles = compositeAngle + compositeAngleOffsets;"
|
vec4 angles = compositeAngle + compositeAngleOffsets;
|
||||||
|
|
||||||
// Sample four times over, at proper angle offsets.
|
// Sample four times over, at proper angle offsets.
|
||||||
"vec4 samples = vec4("
|
vec4 samples = vec4(
|
||||||
"composite_sample(textureCoordinates[0], angles.x),"
|
composite_sample(textureCoordinates[0], angles.x),
|
||||||
"composite_sample(textureCoordinates[1], angles.y),"
|
composite_sample(textureCoordinates[1], angles.y),
|
||||||
"composite_sample(textureCoordinates[2], angles.z),"
|
composite_sample(textureCoordinates[2], angles.z),
|
||||||
"composite_sample(textureCoordinates[3], angles.w)"
|
composite_sample(textureCoordinates[3], angles.w)
|
||||||
");"
|
);
|
||||||
|
|
||||||
// Compute a luminance for use if there's no colour information, now, before
|
|
||||||
// modifying samples.
|
|
||||||
"float mono_luminance = dot(samples, vec4(0.15, 0.35, 0.35, 0.15));"
|
|
||||||
|
|
||||||
|
// The outer structure of the OpenGL scan target means in practice that
|
||||||
|
// compositeAmplitude will be the same value across a piece of
|
||||||
|
// geometry. I am therefore optimistic that this conditional will not
|
||||||
|
// cause a divergence in fragment execution.
|
||||||
|
if(compositeAmplitude < 0.01) {
|
||||||
|
// Compute only a luminance for use if there's no colour information.
|
||||||
|
fragColour3 = vec3(dot(samples, vec4(0.15, 0.35, 0.35, 0.15)));
|
||||||
|
} else {
|
||||||
// Take the average to calculate luminance, then subtract that from all four samples to
|
// Take the average to calculate luminance, then subtract that from all four samples to
|
||||||
// give chrominance.
|
// give chrominance.
|
||||||
"float luminance = dot(samples, vec4(0.25));"
|
float luminance = dot(samples, vec4(0.25));
|
||||||
|
|
||||||
// Split and average chrominance.
|
// Split and average chrominance.
|
||||||
"vec2 chrominances[4] = vec2[4]("
|
vec2 chrominances[4] = vec2[4](
|
||||||
"textureLod(qamTextureName, qamTextureCoordinates[0], 0).gb,"
|
textureLod(qamTextureName, qamTextureCoordinates[0], 0).gb,
|
||||||
"textureLod(qamTextureName, qamTextureCoordinates[1], 0).gb,"
|
textureLod(qamTextureName, qamTextureCoordinates[1], 0).gb,
|
||||||
"textureLod(qamTextureName, qamTextureCoordinates[2], 0).gb,"
|
textureLod(qamTextureName, qamTextureCoordinates[2], 0).gb,
|
||||||
"textureLod(qamTextureName, qamTextureCoordinates[3], 0).gb"
|
textureLod(qamTextureName, qamTextureCoordinates[3], 0).gb
|
||||||
");"
|
);
|
||||||
"vec2 channels = (chrominances[0] + chrominances[1] + chrominances[2] + chrominances[3])*0.5 - vec2(1.0);"
|
vec2 channels = (chrominances[0] + chrominances[1] + chrominances[2] + chrominances[3])*0.5 - vec2(1.0);
|
||||||
|
|
||||||
// Apply a colour space conversion to get RGB.
|
// Apply a colour space conversion to get RGB.
|
||||||
"fragColour3 = mix("
|
fragColour3 = lumaChromaToRGB * vec3(luminance / (1.0 - compositeAmplitude), channels);
|
||||||
"lumaChromaToRGB * vec3(luminance / (1.0 - compositeAmplitude), channels),"
|
}
|
||||||
"vec3(mono_luminance),"
|
)x";
|
||||||
"step(oneOverCompositeAmplitude, 0.01)"
|
|
||||||
");";
|
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case DisplayType::CompositeMonochrome:
|
case DisplayType::CompositeMonochrome:
|
||||||
@@ -622,7 +624,7 @@ std::unique_ptr<Shader> ScanTarget::qam_separation_shader() const {
|
|||||||
|
|
||||||
"compositeAngle = compositeAngle * 2.0 * 3.141592654;"
|
"compositeAngle = compositeAngle * 2.0 * 3.141592654;"
|
||||||
"compositeAmplitude = lineCompositeAmplitude / 255.0;"
|
"compositeAmplitude = lineCompositeAmplitude / 255.0;"
|
||||||
"oneOverCompositeAmplitude = mix(0.0, 255.0 / lineCompositeAmplitude, step(0.01, lineCompositeAmplitude));";
|
"oneOverCompositeAmplitude = mix(0.0, 255.0 / lineCompositeAmplitude, step(0.95, lineCompositeAmplitude));";
|
||||||
|
|
||||||
if(is_svideo) {
|
if(is_svideo) {
|
||||||
vertex_shader +=
|
vertex_shader +=
|
||||||
|
|||||||
Reference in New Issue
Block a user