Skip to content

Commit 8886aad

Browse files
Merge pull request #4 from SergioMartin86/controllers
Controllers
2 parents 9b276f8 + ed01b20 commit 8886aad

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

46 files changed

+203120
-197355
lines changed

.gitmodules

+2-2
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,3 @@
1-
[submodule "extern/QuickNES_Core"]
1+
[submodule "source/quickNES/QuickNES_Core"]
22
path = source/quickNES/QuickNES_Core
3-
url = https://github.com/libretro/QuickNES_Core.git
3+
url = git@github.com:SergioMartin86/QuickNES_Core.git

README.md

+5-5
Original file line numberDiff line numberDiff line change
@@ -12,16 +12,16 @@ quickerNES is an attempt to modernizing [quickNES](https://github.com/kode54/Qui
1212

1313
The main aim is to improve the performance of headless re-recording for TASing and botting (See: [JaffarPlus](https://github.com/SergioMartin86/jaffarPlus)) purposes. However, if this work can help regular play emulation, then much better.
1414

15-
Changes
16-
--------
15+
Improvements
16+
-------------
1717

1818
- Optimizations made in the CPU emulation core, including:
1919
+ Forced alignment at the start of a page to prevent crossing cache line boundaries
2020
+ Simplifying the 6502 CPU instruction fetching and decoding
2121
+ Multiple branch optimizations
22-
- Minimize compiled code size to reduce pressure on L1i cache
23-
- Assuming little endiannes to reduce unnecessary conversion operations (not portable to big endian systems)
24-
- Reduce heap allocations
22+
+ Assuming little endiannes to reduce unnecessary conversion operations (not portable to big endian systems)
23+
+ Minimize compiled code size to reduce pressure on L1i cache
24+
- Added support for FourScore controller
2525
- General code reorganization (make it header only to help compiler optimizations)
2626

2727
Credits

source/controller.hpp

+206
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,206 @@
1+
#pragma once
2+
3+
// Base controller class
4+
// by eien86
5+
6+
#include <cstdint>
7+
#include <string>
8+
#include <sstream>
9+
10+
class Controller
11+
{
12+
public:
13+
14+
enum controller_t { none, joypad, fourscore1, fourscore2 };
15+
16+
typedef uint32_t port_t;
17+
18+
struct input_t
19+
{
20+
bool power = false;
21+
bool reset = false;
22+
port_t port1 = 0;
23+
port_t port2 = 0;
24+
};
25+
26+
inline bool parseInputString(const std::string& input)
27+
{
28+
// Parse valid flag
29+
bool isValid = true;
30+
31+
// Converting input into a stream for parsing
32+
std::istringstream ss(input);
33+
34+
// Start separator
35+
if (ss.get() != '|') isValid = false;
36+
37+
// Parsing console inputs
38+
isValid &= parseConsoleInputs(_input.reset, _input.power, ss);
39+
40+
// Parsing controller 1 inputs
41+
isValid &= parseControllerInputs(_controller1Type, _input.port1, ss);
42+
43+
// Parsing controller 1 inputs
44+
isValid &= parseControllerInputs(_controller2Type, _input.port2, ss);
45+
46+
// End separator
47+
if (ss.get() != '|') isValid = false;
48+
49+
// If its not the end of the stream, then extra values remain and its invalid
50+
ss.get();
51+
if (ss.eof() == false) isValid = false;
52+
53+
// Returning valid flag
54+
return isValid;
55+
};
56+
57+
inline void setController1Type(const controller_t type) { _controller1Type = type; }
58+
inline void setController2Type(const controller_t type) { _controller2Type = type; }
59+
60+
inline bool getPowerButtonState() { return _input.power; }
61+
inline bool getResetButtonState() { return _input.reset; }
62+
inline port_t getController1Code() { return _input.port1; }
63+
inline port_t getController2Code() { return _input.port2; }
64+
65+
private:
66+
67+
static bool parseJoyPadInput(uint8_t& code, std::istringstream& ss)
68+
{
69+
// Currently read character
70+
char c;
71+
72+
// Cleaning code
73+
code = 0;
74+
75+
// Up
76+
c = ss.get();
77+
if (c != '.' && c != 'U') return false;
78+
if (c == 'U') code |= 0b00010000;
79+
80+
// Down
81+
c = ss.get();
82+
if (c != '.' && c != 'D') return false;
83+
if (c == 'D') code |= 0b00100000;
84+
85+
// Left
86+
c = ss.get();
87+
if (c != '.' && c != 'L') return false;
88+
if (c == 'L') code |= 0b01000000;
89+
90+
// Right
91+
c = ss.get();
92+
if (c != '.' && c != 'R') return false;
93+
if (c == 'R') code |= 0b10000000;
94+
95+
// Start
96+
c = ss.get();
97+
if (c != '.' && c != 'S') return false;
98+
if (c == 'S') code |= 0b00001000;
99+
100+
// Select
101+
c = ss.get();
102+
if (c != '.' && c != 's') return false;
103+
if (c == 's') code |= 0b00000100;
104+
105+
// B
106+
c = ss.get();
107+
if (c != '.' && c != 'B') return false;
108+
if (c == 'B') code |= 0b00000010;
109+
110+
// A
111+
c = ss.get();
112+
if (c != '.' && c != 'A') return false;
113+
if (c == 'A') code |= 0b00000001;
114+
115+
return true;
116+
}
117+
118+
static bool parseControllerInputs(const controller_t type, port_t& port, std::istringstream& ss)
119+
{
120+
// Parse valid flag
121+
bool isValid = true;
122+
123+
// If no controller assigned then, its port is all zeroes.
124+
if (type == controller_t::none) { port = 0; return true; }
125+
126+
// Controller separator
127+
if (ss.get() != '|') isValid = false;
128+
129+
// If normal joypad, parse its code now
130+
if (type == controller_t::joypad)
131+
{
132+
// Storage for joypad's code
133+
uint8_t code = 0;
134+
135+
// Parsing joypad code
136+
isValid &= parseJoyPadInput(code, ss);
137+
138+
// Pushing input code into the port
139+
port = code;
140+
141+
// Adding joypad signature
142+
port |= ~0xFF;
143+
}
144+
145+
// If its fourscore, its like two joypads separated by a |
146+
if (type == controller_t::fourscore1 || type == controller_t::fourscore2)
147+
{
148+
// Storage for joypad's code
149+
uint8_t code1 = 0;
150+
uint8_t code2 = 0;
151+
152+
// Parsing joypad code1
153+
isValid &= parseJoyPadInput(code1, ss);
154+
155+
// Separator
156+
if (ss.get() != '|') return false;
157+
158+
// Parsing joypad code1
159+
isValid &= parseJoyPadInput(code2, ss);
160+
161+
// Creating code
162+
port = code1;
163+
port |= (uint32_t)0 | code2 << 8;
164+
if (type == controller_t::fourscore1) port |= (uint32_t)0 | 1 << 19;
165+
if (type == controller_t::fourscore2) port |= (uint32_t)0 | 1 << 18;
166+
port |= (uint32_t)0 | 1 << 24;
167+
port |= (uint32_t)0 | 1 << 25;
168+
port |= (uint32_t)0 | 1 << 26;
169+
port |= (uint32_t)0 | 1 << 27;
170+
port |= (uint32_t)0 | 1 << 28;
171+
port |= (uint32_t)0 | 1 << 29;
172+
port |= (uint32_t)0 | 1 << 30;
173+
port |= (uint32_t)0 | 1 << 31;
174+
}
175+
// Return valid flag
176+
return isValid;
177+
}
178+
179+
static bool parseConsoleInputs(bool& reset, bool& power, std::istringstream& ss)
180+
{
181+
// Parse valid flag
182+
bool isValid = true;
183+
184+
// Currently read character
185+
char c;
186+
187+
// Power trigger
188+
c = ss.get();
189+
if (c != '.' && c != 'P') isValid = false;
190+
if (c == 'P') power = true;
191+
if (c == '.') power = false;
192+
193+
// Reset trigger
194+
c = ss.get();
195+
if (c != '.' && c != 'r') isValid = false;
196+
if (c == 'r') reset = true;
197+
if (c == '.') reset = false;
198+
199+
// Return valid flag
200+
return isValid;
201+
}
202+
203+
input_t _input;
204+
controller_t _controller1Type;
205+
controller_t _controller2Type;
206+
};

source/emuInstance.hpp

+39-60
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
#include "sha1/sha1.hpp"
44
#include <utils.hpp>
5+
#include <controller.hpp>
56

67
#define _LOW_MEM_SIZE 0x800
78
#define _HIGH_MEM_SIZE 0x2000
@@ -14,74 +15,50 @@ static const uint16_t image_height = 240;
1415
class EmuInstance
1516
{
1617
public:
17-
typedef uint8_t inputType;
1818

19-
// Deleting default constructors
19+
EmuInstance() = default;
2020
virtual ~EmuInstance() = default;
2121

22-
// Controller input bits
23-
// 0 - A / 1
24-
// 1 - B / 2
25-
// 2 - Select / 4
26-
// 3 - Start / 8
27-
// 4 - Up / 16
28-
// 5 - Down / 32
29-
// 6 - Left / 64
30-
// 7 - Right / 128
31-
32-
// Move Format:
33-
// RLDUTSBA
34-
// ........
35-
36-
static inline inputType moveStringToCode(const std::string &move)
22+
inline void advanceState(const std::string &move)
3723
{
38-
inputType moveCode = 0;
39-
40-
for (size_t i = 0; i < move.size(); i++) switch (move[i])
41-
{
42-
case 'U': moveCode |= 0b00010000; break;
43-
case 'D': moveCode |= 0b00100000; break;
44-
case 'L': moveCode |= 0b01000000; break;
45-
case 'R': moveCode |= 0b10000000; break;
46-
case 'S': moveCode |= 0b00001000; break;
47-
case 's': moveCode |= 0b00000100; break;
48-
case 'B': moveCode |= 0b00000010; break;
49-
case 'A': moveCode |= 0b00000001; break;
50-
case 'r': break;
51-
case '.': break;
52-
case '|': break;
53-
default: EXIT_WITH_ERROR("Move provided cannot be parsed: '%s', unrecognized character: '%c'\n", move.c_str(), move[i]);
54-
}
55-
56-
return moveCode;
24+
bool isInputValid = _controller.parseInputString(move);
25+
if (isInputValid == false) EXIT_WITH_ERROR("Move provided cannot be parsed: '%s'\n", move.c_str());
26+
27+
// Parsing power
28+
if (_controller.getPowerButtonState() == true) EXIT_WITH_ERROR("Power button pressed, but not supported: '%s'\n", move.c_str());
29+
30+
// Parsing reset
31+
if (_controller.getResetButtonState() == true) doSoftReset();
32+
33+
// Parsing Controllers
34+
const auto controller1 = _controller.getController1Code();
35+
const auto controller2 = _controller.getController2Code();
36+
37+
advanceStateImpl(controller1, controller2);
5738
}
5839

59-
static inline std::string moveCodeToString(const inputType move)
40+
inline void setController1Type(const std::string& type)
6041
{
61-
#ifndef _NES_PLAYER_2
62-
std::string moveString = "|..|";
63-
#else
64-
std::string moveString = "|..|........|";
65-
#endif
66-
67-
if (move & 0b00010000) moveString += 'U'; else moveString += '.';
68-
if (move & 0b00100000) moveString += 'D'; else moveString += '.';
69-
if (move & 0b01000000) moveString += 'L'; else moveString += '.';
70-
if (move & 0b10000000) moveString += 'R'; else moveString += '.';
71-
if (move & 0b00001000) moveString += 'S'; else moveString += '.';
72-
if (move & 0b00000100) moveString += 's'; else moveString += '.';
73-
if (move & 0b00000010) moveString += 'B'; else moveString += '.';
74-
if (move & 0b00000001) moveString += 'A'; else moveString += '.';
75-
76-
moveString += "|";
77-
return moveString;
42+
bool isTypeRecognized = false;
43+
44+
if (type == "None") { _controller.setController1Type(Controller::controller_t::none); isTypeRecognized = true; }
45+
if (type == "Joypad") { _controller.setController1Type(Controller::controller_t::joypad); isTypeRecognized = true; }
46+
if (type == "FourScore1") { _controller.setController1Type(Controller::controller_t::fourscore1); isTypeRecognized = true; }
47+
if (type == "FourScore2") { _controller.setController1Type(Controller::controller_t::fourscore2); isTypeRecognized = true; }
48+
49+
if (isTypeRecognized == false) EXIT_WITH_ERROR("Input type not recognized: '%s'\n", type.c_str());
7850
}
7951

80-
inline void advanceState(const std::string &move)
52+
inline void setController2Type(const std::string& type)
8153
{
82-
if (move.find("r") != std::string::npos) doSoftReset();
83-
84-
advanceStateImpl(moveStringToCode(move), 0);
54+
bool isTypeRecognized = false;
55+
56+
if (type == "None") { _controller.setController2Type(Controller::controller_t::none); isTypeRecognized = true; }
57+
if (type == "Joypad") { _controller.setController2Type(Controller::controller_t::joypad); isTypeRecognized = true; }
58+
if (type == "FourScore1") { _controller.setController2Type(Controller::controller_t::fourscore1); isTypeRecognized = true; }
59+
if (type == "FourScore2") { _controller.setController2Type(Controller::controller_t::fourscore2); isTypeRecognized = true; }
60+
61+
if (isTypeRecognized == false) EXIT_WITH_ERROR("Input type not recognized: '%s'\n", type.c_str());
8562
}
8663

8764
inline std::string getRomSHA1() const { return _romSHA1String; }
@@ -142,7 +119,7 @@ class EmuInstance
142119
// Virtual functions
143120

144121
virtual bool loadROMFileImpl(const std::string &romFilePath) = 0;
145-
virtual void advanceStateImpl(const inputType controller1, const inputType controller2) = 0;
122+
virtual void advanceStateImpl(const Controller::port_t controller1, const Controller::port_t controller2) = 0;
146123
virtual uint8_t *getLowMem() const = 0;
147124
virtual uint8_t *getNametableMem() const = 0;
148125
virtual uint8_t *getHighMem() const = 0;
@@ -163,7 +140,6 @@ class EmuInstance
163140
virtual void *getInternalEmulatorPointer() const = 0;
164141

165142
protected:
166-
EmuInstance() = default;
167143

168144
// Storage for the light state size
169145
size_t _liteStateSize;
@@ -180,4 +156,7 @@ class EmuInstance
180156

181157
// SHA1 rom hash
182158
std::string _romSHA1String;
159+
160+
// Controller class for input parsing
161+
Controller _controller;
183162
};

0 commit comments

Comments
 (0)