1
0
mirror of https://github.com/TomHarte/CLK.git synced 2024-06-25 18:30:07 +00:00

Factors out joystick logic.

This commit is contained in:
Thomas Harte 2021-02-16 19:17:32 -05:00
parent fa8236741d
commit b117df3367
4 changed files with 139 additions and 70 deletions

View File

@ -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();
}
};

View 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"

View 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 */

View File

@ -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 */,