mirror of
https://github.com/TomHarte/CLK.git
synced 2025-02-27 00:30:26 +00:00
Factors out joystick logic.
This commit is contained in:
parent
fa8236741d
commit
b117df3367
@ -22,6 +22,7 @@
|
||||
#include "AuxiliaryMemorySwitches.hpp"
|
||||
#include "Card.hpp"
|
||||
#include "DiskIICard.hpp"
|
||||
#include "Joystick.hpp"
|
||||
#include "LanguageCardSwitches.hpp"
|
||||
#include "Video.hpp"
|
||||
|
||||
@ -260,55 +261,8 @@ template <Analyser::Static::AppleII::Target::Model model> class ConcreteMachine:
|
||||
// MARK - typing
|
||||
std::unique_ptr<Utility::StringSerialiser> string_serialiser_;
|
||||
|
||||
// MARK - joysticks
|
||||
class Joystick: public Inputs::ConcreteJoystick {
|
||||
public:
|
||||
Joystick() :
|
||||
ConcreteJoystick({
|
||||
Input(Input::Horizontal),
|
||||
Input(Input::Vertical),
|
||||
|
||||
// The Apple II offers three buttons between two joysticks;
|
||||
// this emulator puts three buttons on each joystick and
|
||||
// combines them.
|
||||
Input(Input::Fire, 0),
|
||||
Input(Input::Fire, 1),
|
||||
Input(Input::Fire, 2),
|
||||
}) {}
|
||||
|
||||
void did_set_input(const Input &input, float value) final {
|
||||
if(!input.info.control.index && (input.type == Input::Type::Horizontal || input.type == Input::Type::Vertical))
|
||||
axes[(input.type == Input::Type::Horizontal) ? 0 : 1] = 1.0f - value;
|
||||
}
|
||||
|
||||
void did_set_input(const Input &input, bool value) final {
|
||||
if(input.type == Input::Type::Fire && input.info.control.index < 3) {
|
||||
buttons[input.info.control.index] = value;
|
||||
}
|
||||
}
|
||||
|
||||
bool buttons[3] = {false, false, false};
|
||||
float axes[2] = {0.5f, 0.5f};
|
||||
};
|
||||
|
||||
// On an Apple II, the programmer strobes 0xc070 and that causes each analogue input
|
||||
// to begin a charge and discharge cycle **if they are not already charging**.
|
||||
// The greater the analogue input, the faster they will charge and therefore the sooner
|
||||
// they will discharge.
|
||||
//
|
||||
// This emulator models that with analogue_charge_ being essentially the amount of time,
|
||||
// in charge threshold units, since 0xc070 was last strobed. But if any of the analogue
|
||||
// inputs were already partially charged then they gain a bias in analogue_biases_.
|
||||
//
|
||||
// It's a little indirect, but it means only having to increment the one value in the
|
||||
// main loop.
|
||||
float analogue_charge_ = 0.0f;
|
||||
float analogue_biases_[4] = {0.0f, 0.0f, 0.0f, 0.0f};
|
||||
|
||||
std::vector<std::unique_ptr<Inputs::Joystick>> joysticks_;
|
||||
bool analogue_channel_is_discharged(size_t channel) {
|
||||
return (1.0f - static_cast<Joystick *>(joysticks_[channel >> 1].get())->axes[channel & 1]) < analogue_charge_ + analogue_biases_[channel];
|
||||
}
|
||||
// MARK - Joysticks.
|
||||
JoystickPair joysticks_;
|
||||
|
||||
// The IIe has three keys that are wired directly to the same input as the joystick buttons.
|
||||
bool open_apple_is_pressed_ = false;
|
||||
@ -344,10 +298,6 @@ template <Analyser::Static::AppleII::Target::Model model> class ConcreteMachine:
|
||||
Memory::Fuzz(ram_, sizeof(ram_));
|
||||
Memory::Fuzz(aux_ram_, sizeof(aux_ram_));
|
||||
|
||||
// Add a couple of joysticks.
|
||||
joysticks_.emplace_back(new Joystick);
|
||||
joysticks_.emplace_back(new Joystick);
|
||||
|
||||
// Pick the required ROMs.
|
||||
using Target = Analyser::Static::AppleII::Target;
|
||||
const std::string machine_name = "AppleII";
|
||||
@ -495,7 +445,7 @@ template <Analyser::Static::AppleII::Target::Model model> class ConcreteMachine:
|
||||
case 0xc061: // Switch input 0.
|
||||
*value &= 0x7f;
|
||||
if(
|
||||
static_cast<Joystick *>(joysticks_[0].get())->buttons[0] || static_cast<Joystick *>(joysticks_[1].get())->buttons[2] ||
|
||||
joysticks_.button(0) ||
|
||||
(is_iie() && open_apple_is_pressed_)
|
||||
)
|
||||
*value |= 0x80;
|
||||
@ -503,14 +453,14 @@ template <Analyser::Static::AppleII::Target::Model model> class ConcreteMachine:
|
||||
case 0xc062: // Switch input 1.
|
||||
*value &= 0x7f;
|
||||
if(
|
||||
static_cast<Joystick *>(joysticks_[0].get())->buttons[1] || static_cast<Joystick *>(joysticks_[1].get())->buttons[1] ||
|
||||
joysticks_.button(1) ||
|
||||
(is_iie() && closed_apple_is_pressed_)
|
||||
)
|
||||
*value |= 0x80;
|
||||
break;
|
||||
case 0xc063: // Switch input 2.
|
||||
*value &= 0x7f;
|
||||
if(static_cast<Joystick *>(joysticks_[0].get())->buttons[2] || static_cast<Joystick *>(joysticks_[1].get())->buttons[0])
|
||||
if(joysticks_.button(2))
|
||||
*value |= 0x80;
|
||||
break;
|
||||
|
||||
@ -520,7 +470,7 @@ template <Analyser::Static::AppleII::Target::Model model> class ConcreteMachine:
|
||||
case 0xc067: { // Analogue input 3.
|
||||
const size_t input = address - 0xc064;
|
||||
*value &= 0x7f;
|
||||
if(!analogue_channel_is_discharged(input)) {
|
||||
if(!joysticks_.analogue_channel_is_discharged(input)) {
|
||||
*value |= 0x80;
|
||||
}
|
||||
} break;
|
||||
@ -577,17 +527,7 @@ template <Analyser::Static::AppleII::Target::Model model> class ConcreteMachine:
|
||||
}
|
||||
break;
|
||||
|
||||
case 0xc070: { // Permit analogue inputs that are currently discharged to begin a charge cycle.
|
||||
// Ensure those that were still charging retain that state.
|
||||
for(size_t c = 0; c < 4; ++c) {
|
||||
if(analogue_channel_is_discharged(c)) {
|
||||
analogue_biases_[c] = 0.0f;
|
||||
} else {
|
||||
analogue_biases_[c] += analogue_charge_;
|
||||
}
|
||||
}
|
||||
analogue_charge_ = 0.0f;
|
||||
} break;
|
||||
case 0xc070: joysticks_.access_c070(); break;
|
||||
|
||||
/* Switches triggered by reading or writing. */
|
||||
case 0xc050:
|
||||
@ -727,7 +667,7 @@ template <Analyser::Static::AppleII::Target::Model model> class ConcreteMachine:
|
||||
}
|
||||
|
||||
// Update analogue charge level.
|
||||
analogue_charge_ = std::min(analogue_charge_ + 1.0f / 2820.0f, 1.1f);
|
||||
joysticks_.update_charge();
|
||||
|
||||
return Cycles(1);
|
||||
}
|
||||
@ -855,7 +795,7 @@ template <Analyser::Static::AppleII::Target::Model model> class ConcreteMachine:
|
||||
|
||||
// MARK: JoystickMachine
|
||||
const std::vector<std::unique_ptr<Inputs::Joystick>> &get_joysticks() final {
|
||||
return joysticks_;
|
||||
return joysticks_.get_joysticks();
|
||||
}
|
||||
};
|
||||
|
||||
|
9
Machines/Apple/AppleII/Joystick.cpp
Normal file
9
Machines/Apple/AppleII/Joystick.cpp
Normal file
@ -0,0 +1,9 @@
|
||||
//
|
||||
// Joystick.cpp
|
||||
// Clock Signal
|
||||
//
|
||||
// Created by Thomas Harte on 16/02/2021.
|
||||
// Copyright © 2021 Thomas Harte. All rights reserved.
|
||||
//
|
||||
|
||||
#include "Joystick.hpp"
|
112
Machines/Apple/AppleII/Joystick.hpp
Normal file
112
Machines/Apple/AppleII/Joystick.hpp
Normal file
@ -0,0 +1,112 @@
|
||||
//
|
||||
// Joystick.hpp
|
||||
// Clock Signal
|
||||
//
|
||||
// Created by Thomas Harte on 16/02/2021.
|
||||
// Copyright © 2021 Thomas Harte. All rights reserved.
|
||||
//
|
||||
|
||||
#ifndef AppleII_Joystick_hpp
|
||||
#define AppleII_Joystick_hpp
|
||||
|
||||
#include "../../../Inputs/Joystick.hpp"
|
||||
|
||||
#include <memory>
|
||||
#include <vector>
|
||||
|
||||
namespace Apple {
|
||||
namespace II {
|
||||
|
||||
class JoystickPair {
|
||||
public:
|
||||
JoystickPair() {
|
||||
// Add a couple of joysticks.
|
||||
joysticks_.emplace_back(new Joystick);
|
||||
joysticks_.emplace_back(new Joystick);
|
||||
}
|
||||
|
||||
class Joystick: public Inputs::ConcreteJoystick {
|
||||
public:
|
||||
Joystick() :
|
||||
ConcreteJoystick({
|
||||
Input(Input::Horizontal),
|
||||
Input(Input::Vertical),
|
||||
|
||||
// The Apple II offers three buttons between two joysticks;
|
||||
// this emulator puts three buttons on each joystick and
|
||||
// combines them.
|
||||
Input(Input::Fire, 0),
|
||||
Input(Input::Fire, 1),
|
||||
Input(Input::Fire, 2),
|
||||
}) {}
|
||||
|
||||
void did_set_input(const Input &input, float value) final {
|
||||
if(!input.info.control.index && (input.type == Input::Type::Horizontal || input.type == Input::Type::Vertical))
|
||||
axes[(input.type == Input::Type::Horizontal) ? 0 : 1] = 1.0f - value;
|
||||
}
|
||||
|
||||
void did_set_input(const Input &input, bool value) final {
|
||||
if(input.type == Input::Type::Fire && input.info.control.index < 3) {
|
||||
buttons[input.info.control.index] = value;
|
||||
}
|
||||
}
|
||||
|
||||
bool buttons[3] = {false, false, false};
|
||||
float axes[2] = {0.5f, 0.5f};
|
||||
};
|
||||
|
||||
inline bool button(size_t index) {
|
||||
return joystick(0)->buttons[index] || joystick(1)->buttons[2-index];
|
||||
}
|
||||
|
||||
inline bool analogue_channel_is_discharged(size_t channel) {
|
||||
return (1.0f - static_cast<Joystick *>(joysticks_[channel >> 1].get())->axes[channel & 1]) < analogue_charge_ + analogue_biases_[channel];
|
||||
}
|
||||
|
||||
inline void update_charge() {
|
||||
analogue_charge_ = std::min(analogue_charge_ + 1.0f / 2820.0f, 1.1f);
|
||||
}
|
||||
|
||||
inline void access_c070() {
|
||||
// Permit analogue inputs that are currently discharged to begin a charge cycle.
|
||||
// Ensure those that were still charging retain that state.
|
||||
for(size_t c = 0; c < 4; ++c) {
|
||||
if(analogue_channel_is_discharged(c)) {
|
||||
analogue_biases_[c] = 0.0f;
|
||||
} else {
|
||||
analogue_biases_[c] += analogue_charge_;
|
||||
}
|
||||
}
|
||||
analogue_charge_ = 0.0f;
|
||||
}
|
||||
|
||||
const std::vector<std::unique_ptr<Inputs::Joystick>> &get_joysticks() {
|
||||
return joysticks_;
|
||||
}
|
||||
|
||||
private:
|
||||
// On an Apple II, the programmer strobes 0xc070 and that causes each analogue input
|
||||
// to begin a charge and discharge cycle **if they are not already charging**.
|
||||
// The greater the analogue input, the faster they will charge and therefore the sooner
|
||||
// they will discharge.
|
||||
//
|
||||
// This emulator models that with analogue_charge_ being essentially the amount of time,
|
||||
// in charge threshold units, since 0xc070 was last strobed. But if any of the analogue
|
||||
// inputs were already partially charged then they gain a bias in analogue_biases_.
|
||||
//
|
||||
// It's a little indirect, but it means only having to increment the one value in the
|
||||
// main loop.
|
||||
float analogue_charge_ = 0.0f;
|
||||
float analogue_biases_[4] = {0.0f, 0.0f, 0.0f, 0.0f};
|
||||
|
||||
std::vector<std::unique_ptr<Inputs::Joystick>> joysticks_;
|
||||
|
||||
inline Joystick *joystick(size_t index) {
|
||||
return static_cast<Joystick *>(joysticks_[index].get());
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
#endif /* AppleII_Joystick_hpp */
|
@ -179,6 +179,8 @@
|
||||
4B2E86C925D892EF0024F1E9 /* DAT.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4BE8EB6425C750B50040BC40 /* DAT.cpp */; };
|
||||
4B2E86CF25D8D8C70024F1E9 /* Keyboard.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B2E86CD25D8D8C70024F1E9 /* Keyboard.cpp */; };
|
||||
4B2E86D025D8D8C70024F1E9 /* Keyboard.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B2E86CD25D8D8C70024F1E9 /* Keyboard.cpp */; };
|
||||
4B2E86E225DC95150024F1E9 /* Joystick.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B2E86E025DC95150024F1E9 /* Joystick.cpp */; };
|
||||
4B2E86E325DC95150024F1E9 /* Joystick.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B2E86E025DC95150024F1E9 /* Joystick.cpp */; };
|
||||
4B302184208A550100773308 /* DiskII.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B302183208A550100773308 /* DiskII.cpp */; };
|
||||
4B302185208A550100773308 /* DiskII.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B302183208A550100773308 /* DiskII.cpp */; };
|
||||
4B30512D1D989E2200B4FED8 /* Drive.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B30512B1D989E2200B4FED8 /* Drive.cpp */; };
|
||||
@ -1129,6 +1131,8 @@
|
||||
4B2E86BD25D74F160024F1E9 /* Mouse.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = Mouse.hpp; sourceTree = "<group>"; };
|
||||
4B2E86CD25D8D8C70024F1E9 /* Keyboard.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = Keyboard.cpp; sourceTree = "<group>"; };
|
||||
4B2E86CE25D8D8C70024F1E9 /* Keyboard.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = Keyboard.hpp; sourceTree = "<group>"; };
|
||||
4B2E86E025DC95150024F1E9 /* Joystick.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = Joystick.cpp; sourceTree = "<group>"; };
|
||||
4B2E86E125DC95150024F1E9 /* Joystick.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = Joystick.hpp; sourceTree = "<group>"; };
|
||||
4B302182208A550100773308 /* DiskII.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = DiskII.hpp; sourceTree = "<group>"; };
|
||||
4B302183208A550100773308 /* DiskII.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = DiskII.cpp; sourceTree = "<group>"; };
|
||||
4B30512B1D989E2200B4FED8 /* Drive.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = Drive.cpp; sourceTree = "<group>"; };
|
||||
@ -4167,11 +4171,13 @@
|
||||
children = (
|
||||
4BCE0050227CE8CA000CA200 /* AppleII.cpp */,
|
||||
4BCE004E227CE8CA000CA200 /* DiskIICard.cpp */,
|
||||
4B2E86E025DC95150024F1E9 /* Joystick.cpp */,
|
||||
4BCE004D227CE8CA000CA200 /* Video.cpp */,
|
||||
4BCE004A227CE8CA000CA200 /* AppleII.hpp */,
|
||||
4BF40A5A254263140033EA39 /* AuxiliaryMemorySwitches.hpp */,
|
||||
4BCE004B227CE8CA000CA200 /* Card.hpp */,
|
||||
4BCE004C227CE8CA000CA200 /* DiskIICard.hpp */,
|
||||
4B2E86E125DC95150024F1E9 /* Joystick.hpp */,
|
||||
4BF40A5525424C770033EA39 /* LanguageCardSwitches.hpp */,
|
||||
4BCE004F227CE8CA000CA200 /* Video.hpp */,
|
||||
4B8DF4F2254E141700F3433C /* VideoSwitches.hpp */,
|
||||
@ -5106,6 +5112,7 @@
|
||||
4B055AD11FAE9B030060FFFF /* Video.cpp in Sources */,
|
||||
4BB4BFBA22A4372F0069048D /* StaticAnalyser.cpp in Sources */,
|
||||
4B055AA21FAE85DA0060FFFF /* SSD.cpp in Sources */,
|
||||
4B2E86E325DC95150024F1E9 /* Joystick.cpp in Sources */,
|
||||
4BEBFB4E2002C4BF000708CC /* MSXDSK.cpp in Sources */,
|
||||
4B055ADD1FAE9B460060FFFF /* i8272.cpp in Sources */,
|
||||
4B055A9C1FAE85DA0060FFFF /* CPCDSK.cpp in Sources */,
|
||||
@ -5301,6 +5308,7 @@
|
||||
4B80CD6F2568A82C00176FCC /* DiskIIDrive.cpp in Sources */,
|
||||
4B894532201967B4007DE474 /* 6502.cpp in Sources */,
|
||||
4BDB61EC203285AE0048AF91 /* Atari2600OptionsPanel.swift in Sources */,
|
||||
4B2E86E225DC95150024F1E9 /* Joystick.cpp in Sources */,
|
||||
4BBB70A8202014E2002FE009 /* MultiProducer.cpp in Sources */,
|
||||
4B8805F71DCFF6C9003085B1 /* Commodore.cpp in Sources */,
|
||||
4BC76E691C98E31700E6EF73 /* FIRFilter.cpp in Sources */,
|
||||
|
Loading…
x
Reference in New Issue
Block a user