Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
hrydgard
GitHub Repository: hrydgard/ppsspp
Path: blob/master/Core/HLE/sceCtrl.cpp
5654 views
1
// Copyright (c) 2012- PPSSPP Project.
2
3
// This program is free software: you can redistribute it and/or modify
4
// it under the terms of the GNU General Public License as published by
5
// the Free Software Foundation, version 2.0 or later versions.
6
7
// This program is distributed in the hope that it will be useful,
8
// but WITHOUT ANY WARRANTY; without even the implied warranty of
9
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10
// GNU General Public License 2.0 for more details.
11
12
// A copy of the GPL 2.0 should have been included with the program.
13
// If not, see http://www.gnu.org/licenses/
14
15
// Official git repository and contact information can be found at
16
// https://github.com/hrydgard/ppsspp and http://www.ppsspp.org/.
17
18
#include <cmath>
19
#include <mutex>
20
21
#include "Common/Serialize/Serializer.h"
22
#include "Common/Serialize/SerializeFuncs.h"
23
#include "Common/Math/math_util.h"
24
#include "Core/CoreTiming.h"
25
#include "Core/HLE/HLE.h"
26
#include "Core/HLE/ErrorCodes.h"
27
#include "Core/HLE/FunctionWrappers.h"
28
#include "Core/HLE/sceCtrl.h"
29
#include "Core/HLE/sceKernel.h"
30
#include "Core/HLE/sceKernelThread.h"
31
#include "Core/HLE/sceKernelInterrupt.h"
32
#include "Core/HW/Display.h"
33
#include "Core/System.h"
34
#include "Core/MemMapHelpers.h"
35
#include "Core/MIPS/MIPS.h"
36
#include "Core/Replay.h"
37
#include "Core/Util/AudioFormat.h" // for clamp_u8
38
39
/* Index for the two analog directions */
40
#define CTRL_ANALOG_X 0
41
#define CTRL_ANALOG_Y 1
42
#define CTRL_ANALOG_CENTER 128
43
44
#define CTRL_MODE_DIGITAL 0
45
#define CTRL_MODE_ANALOG 1
46
47
const u32 NUM_CTRL_BUFFERS = 64;
48
49
enum {
50
CTRL_WAIT_POSITIVE = 1,
51
CTRL_WAIT_NEGATIVE = 2,
52
};
53
54
struct CtrlData {
55
u32_le frame;
56
u32_le buttons;
57
// The PSP has only one stick, but has space for more info.
58
// The second stick is populated for HD remasters and possibly in the PSP emulator on PS3/Vita.
59
u8 analog[2][2];
60
u8 unused[4];
61
};
62
63
struct CtrlLatch {
64
u32_le btnMake;
65
u32_le btnBreak;
66
u32_le btnPress;
67
u32_le btnRelease;
68
};
69
70
71
//////////////////////////////////////////////////////////////////////////
72
// STATE BEGIN
73
static bool analogEnabled = false;
74
static int ctrlLatchBufs = 0;
75
static u32 ctrlOldButtons = 0;
76
77
static CtrlData ctrlBufs[NUM_CTRL_BUFFERS];
78
static CtrlData ctrlCurrent;
79
static u32 ctrlBuf = 0;
80
static u32 ctrlBufRead = 0;
81
static CtrlLatch latch;
82
static u32 dialogBtnMake = 0;
83
84
static int ctrlIdleReset = -1;
85
static int ctrlIdleBack = -1;
86
87
static int ctrlCycle = 0;
88
89
static std::vector<SceUID> waitingThreads;
90
static std::mutex ctrlMutex;
91
92
static int ctrlTimer = -1;
93
94
static u16 leftVibration = 0;
95
static u16 rightVibration = 0;
96
// The higher the dropout, the longer Vibration will run
97
static u8 vibrationLeftDropout = 160;
98
static u8 vibrationRightDropout = 160;
99
100
// STATE END
101
//////////////////////////////////////////////////////////////////////////
102
103
// Not savestated, this is emu state.
104
// Not related to sceCtrl*RapidFire(), although it may do the same thing.
105
static bool emuRapidFire = false;
106
static u32 emuRapidFireFrames = 0;
107
static bool emuRapidFireToggle = true;
108
static u32 emuRapidFireInterval = 5;
109
110
// These buttons are not affected by rapid fire (neither is analog.)
111
const u32 CTRL_EMU_RAPIDFIRE_MASK = CTRL_UP | CTRL_DOWN | CTRL_LEFT | CTRL_RIGHT;
112
113
static void __CtrlUpdateLatch()
114
{
115
std::lock_guard<std::mutex> guard(ctrlMutex);
116
u64 t = CoreTiming::GetGlobalTimeUs();
117
118
u32 buttons = ctrlCurrent.buttons;
119
if (emuRapidFire && emuRapidFireToggle)
120
buttons &= CTRL_EMU_RAPIDFIRE_MASK;
121
122
ReplayApplyCtrl(buttons, ctrlCurrent.analog, t);
123
124
// Copy in the current data to the current buffer.
125
ctrlBufs[ctrlBuf] = ctrlCurrent;
126
127
if (PSP_CoreParameter().compat.flags().DaxterRotatedAnalogStick) {
128
// For some reason, Daxter rotates the analog input. See #17015
129
float angle = (15.0f / 360.0f) * (2.0f * M_PI);
130
float cosAngle = cosf(angle);
131
float sinAngle = sinf(angle);
132
float x = ctrlBufs[ctrlBuf].analog[0][0] - 128;
133
float y = ctrlBufs[ctrlBuf].analog[0][1] - 128;
134
float rX = x * cosAngle - y * sinAngle;
135
float rY = x * sinAngle + y * cosAngle;
136
ctrlBufs[ctrlBuf].analog[0][0] = (u8)Clamp(rX + 128, 0.0f, 255.0f);
137
ctrlBufs[ctrlBuf].analog[0][1] = (u8)Clamp(rY + 128, 0.0f, 255.0f);
138
}
139
140
ctrlBufs[ctrlBuf].buttons = buttons;
141
142
u32 changed = buttons ^ ctrlOldButtons;
143
latch.btnMake |= buttons & changed;
144
latch.btnBreak |= ctrlOldButtons & changed;
145
latch.btnPress |= buttons;
146
latch.btnRelease |= ~buttons;
147
dialogBtnMake |= buttons & changed;
148
ctrlLatchBufs++;
149
150
ctrlOldButtons = buttons;
151
152
ctrlBufs[ctrlBuf].frame = (u32)t;
153
if (!analogEnabled)
154
memset(ctrlBufs[ctrlBuf].analog, CTRL_ANALOG_CENTER, sizeof(ctrlBufs[ctrlBuf].analog));
155
156
ctrlBuf = (ctrlBuf + 1) % NUM_CTRL_BUFFERS;
157
158
// If we wrapped around, push the read head forward.
159
// TODO: Is this right?
160
if (ctrlBufRead == ctrlBuf)
161
ctrlBufRead = (ctrlBufRead + 1) % NUM_CTRL_BUFFERS;
162
}
163
164
static int __CtrlResetLatch()
165
{
166
int oldBufs = ctrlLatchBufs;
167
memset(&latch, 0, sizeof(CtrlLatch));
168
ctrlLatchBufs = 0;
169
return oldBufs;
170
}
171
172
u32 __CtrlPeekButtons()
173
{
174
std::lock_guard<std::mutex> guard(ctrlMutex);
175
176
return ctrlCurrent.buttons;
177
}
178
179
u32 __CtrlPeekButtonsVisual()
180
{
181
u32 buttons;
182
{
183
std::lock_guard<std::mutex> guard(ctrlMutex);
184
buttons = ctrlCurrent.buttons;
185
}
186
187
if (emuRapidFire && emuRapidFireToggle)
188
buttons &= CTRL_EMU_RAPIDFIRE_MASK;
189
return buttons;
190
}
191
192
void __CtrlPeekAnalog(int stick, float *x, float *y)
193
{
194
std::lock_guard<std::mutex> guard(ctrlMutex);
195
196
*x = (ctrlCurrent.analog[stick][CTRL_ANALOG_X] - 127.5f) / 127.5f;
197
*y = -(ctrlCurrent.analog[stick][CTRL_ANALOG_Y] - 127.5f) / 127.5f;
198
}
199
200
201
u32 __CtrlReadLatch()
202
{
203
u32 ret = dialogBtnMake;
204
dialogBtnMake = 0;
205
return ret;
206
}
207
208
void __CtrlUpdateButtons(u32 bitsToSet, u32 bitsToClear)
209
{
210
bitsToClear &= CTRL_MASK_USER;
211
bitsToSet &= CTRL_MASK_USER;
212
213
std::lock_guard<std::mutex> guard(ctrlMutex);
214
// There's no atomic operation for this, so mutex it is.
215
ctrlCurrent.buttons = (ctrlCurrent.buttons & ~bitsToClear) | bitsToSet;
216
}
217
218
void __CtrlSetAnalogXY(int stick, float x, float y)
219
{
220
u8 scaledX = clamp_u8((int)ceilf(x * 127.5f + 127.5f));
221
// TODO: We might have too many negations of Y...
222
u8 scaledY = clamp_u8((int)ceilf(-y * 127.5f + 127.5f));
223
224
std::lock_guard<std::mutex> guard(ctrlMutex);
225
ctrlCurrent.analog[stick][CTRL_ANALOG_X] = scaledX;
226
ctrlCurrent.analog[stick][CTRL_ANALOG_Y] = scaledY;
227
}
228
229
// not making XY to use these due to mutex guard usage
230
void __CtrlSetAnalogX(int stick, float x) {
231
u8 scaledX = clamp_u8((int)ceilf(x * 127.5f + 127.5f));
232
std::lock_guard<std::mutex> guard(ctrlMutex);
233
ctrlCurrent.analog[stick][CTRL_ANALOG_X] = scaledX;
234
}
235
236
void __CtrlSetAnalogY(int stick, float y) {
237
u8 scaledY = clamp_u8((int)ceilf(-y * 127.5f + 127.5f));
238
std::lock_guard<std::mutex> guard(ctrlMutex);
239
ctrlCurrent.analog[stick][CTRL_ANALOG_Y] = scaledY;
240
}
241
242
void __CtrlSetRapidFire(bool state, int interval) {
243
emuRapidFire = state;
244
emuRapidFireToggle = true;
245
emuRapidFireInterval = interval;
246
}
247
248
bool __CtrlGetRapidFire()
249
{
250
return emuRapidFire;
251
}
252
253
static int __CtrlReadSingleBuffer(PSPPointer<CtrlData> data, bool negative)
254
{
255
if (data.IsValid())
256
{
257
*data = ctrlBufs[ctrlBufRead];
258
ctrlBufRead = (ctrlBufRead + 1) % NUM_CTRL_BUFFERS;
259
260
// Mask out buttons games aren't allowed to see.
261
data->buttons &= CTRL_MASK_USER;
262
if (negative)
263
data->buttons = ~data->buttons;
264
265
return 1;
266
}
267
268
return 0;
269
}
270
271
static int __CtrlReadBuffer(u32 ctrlDataPtr, u32 nBufs, bool negative, bool peek)
272
{
273
if (nBufs > NUM_CTRL_BUFFERS)
274
return SCE_KERNEL_ERROR_INVALID_SIZE;
275
276
if (!peek && !__KernelIsDispatchEnabled())
277
return SCE_KERNEL_ERROR_CAN_NOT_WAIT;
278
if (!peek && __IsInInterrupt())
279
return SCE_KERNEL_ERROR_ILLEGAL_CONTEXT;
280
281
u32 resetRead = ctrlBufRead;
282
283
u32 availBufs;
284
// Peeks always work, they just go go from now X buffers.
285
if (peek)
286
availBufs = nBufs;
287
else
288
{
289
availBufs = (ctrlBuf - ctrlBufRead + NUM_CTRL_BUFFERS) % NUM_CTRL_BUFFERS;
290
if (availBufs > nBufs)
291
availBufs = nBufs;
292
}
293
ctrlBufRead = (ctrlBuf - availBufs + NUM_CTRL_BUFFERS) % NUM_CTRL_BUFFERS;
294
295
int done = 0;
296
auto data = PSPPointer<CtrlData>::Create(ctrlDataPtr);
297
for (u32 i = 0; i < availBufs; ++i)
298
done += __CtrlReadSingleBuffer(data++, negative);
299
300
if (peek)
301
ctrlBufRead = resetRead;
302
303
return done;
304
}
305
306
static void __CtrlDoSample()
307
{
308
// This samples the ctrl data into the buffers and updates the latch.
309
__CtrlUpdateLatch();
310
311
// Wake up a single thread that was waiting for the buffer.
312
retry:
313
if (!waitingThreads.empty() && ctrlBuf != ctrlBufRead)
314
{
315
SceUID threadID = waitingThreads[0];
316
waitingThreads.erase(waitingThreads.begin());
317
318
u32 error;
319
SceUID wVal = __KernelGetWaitID(threadID, WAITTYPE_CTRL, error);
320
// Make sure it didn't get woken or something.
321
if (wVal == 0)
322
goto retry;
323
324
PSPPointer<CtrlData> ctrlDataPtr;
325
ctrlDataPtr = __KernelGetWaitValue(threadID, error);
326
int retVal = __CtrlReadSingleBuffer(ctrlDataPtr, wVal == CTRL_WAIT_NEGATIVE);
327
__KernelResumeThreadFromWait(threadID, retVal);
328
__KernelReSchedule("ctrl buffers updated");
329
}
330
}
331
332
static void __CtrlVblank()
333
{
334
emuRapidFireFrames++;
335
if (emuRapidFireFrames >= emuRapidFireInterval) {
336
emuRapidFireFrames = 0;
337
emuRapidFireToggle = !emuRapidFireToggle;
338
}
339
340
// Reduce gamepad Vibration by set % each frame
341
leftVibration *= (float)vibrationLeftDropout / 256.0f;
342
rightVibration *= (float)vibrationRightDropout / 256.0f;
343
344
// This always runs, so make sure we're in vblank mode.
345
if (ctrlCycle == 0)
346
__CtrlDoSample();
347
}
348
349
static void __CtrlTimerUpdate(u64 userdata, int cyclesLate)
350
{
351
// This only runs in timer mode (ctrlCycle > 0.)
352
_dbg_assert_msg_(ctrlCycle > 0, "Ctrl: sampling cycle should be > 0");
353
354
CoreTiming::ScheduleEvent(usToCycles(ctrlCycle) - cyclesLate, ctrlTimer, 0);
355
356
__CtrlDoSample();
357
}
358
359
void __CtrlInit()
360
{
361
ctrlTimer = CoreTiming::RegisterEvent("CtrlSampleTimer", __CtrlTimerUpdate);
362
__DisplayListenVblank(__CtrlVblank);
363
364
ctrlIdleReset = -1;
365
ctrlIdleBack = -1;
366
ctrlCycle = 0;
367
368
std::lock_guard<std::mutex> guard(ctrlMutex);
369
370
ctrlBuf = 1;
371
ctrlBufRead = 0;
372
ctrlOldButtons = 0;
373
ctrlLatchBufs = 0;
374
dialogBtnMake = 0;
375
376
memset(&latch, 0, sizeof(latch));
377
// Start with everything released.
378
latch.btnRelease = 0xffffffff;
379
380
memset(&ctrlCurrent, 0, sizeof(ctrlCurrent));
381
memset(ctrlCurrent.analog, CTRL_ANALOG_CENTER, sizeof(ctrlCurrent.analog));
382
analogEnabled = false;
383
384
for (u32 i = 0; i < NUM_CTRL_BUFFERS; i++)
385
memcpy(&ctrlBufs[i], &ctrlCurrent, sizeof(CtrlData));
386
}
387
388
void __CtrlDoState(PointerWrap &p)
389
{
390
std::lock_guard<std::mutex> guard(ctrlMutex);
391
392
auto s = p.Section("sceCtrl", 1, 3);
393
if (!s)
394
return;
395
396
Do(p, analogEnabled);
397
Do(p, ctrlLatchBufs);
398
Do(p, ctrlOldButtons);
399
400
p.DoVoid(ctrlBufs, sizeof(ctrlBufs));
401
if (s <= 2) {
402
CtrlData dummy = {0};
403
Do(p, dummy);
404
}
405
Do(p, ctrlBuf);
406
Do(p, ctrlBufRead);
407
Do(p, latch);
408
if (s == 1) {
409
dialogBtnMake = 0;
410
} else {
411
Do(p, dialogBtnMake);
412
}
413
414
Do(p, ctrlIdleReset);
415
Do(p, ctrlIdleBack);
416
417
Do(p, ctrlCycle);
418
419
SceUID dv = 0;
420
Do(p, waitingThreads, dv);
421
422
Do(p, ctrlTimer);
423
CoreTiming::RestoreRegisterEvent(ctrlTimer, "CtrlSampleTimer", __CtrlTimerUpdate);
424
}
425
426
void __CtrlShutdown()
427
{
428
waitingThreads.clear();
429
}
430
431
static u32 sceCtrlSetSamplingCycle(u32 cycle)
432
{
433
if ((cycle > 0 && cycle < 5555) || cycle > 20000) {
434
return hleLogWarning(Log::sceCtrl, SCE_KERNEL_ERROR_INVALID_VALUE);
435
}
436
437
u32 prev = ctrlCycle;
438
ctrlCycle = cycle;
439
440
if (prev > 0)
441
CoreTiming::UnscheduleEvent(ctrlTimer, 0);
442
if (cycle > 0)
443
CoreTiming::ScheduleEvent(usToCycles(ctrlCycle), ctrlTimer, 0);
444
445
return hleLogDebug(Log::sceCtrl, prev);
446
}
447
448
static int sceCtrlGetSamplingCycle(u32 cyclePtr)
449
{
450
if (Memory::IsValidAddress(cyclePtr))
451
Memory::WriteUnchecked_U32(ctrlCycle, cyclePtr);
452
return hleLogDebug(Log::sceCtrl, 0);
453
}
454
455
static u32 sceCtrlSetSamplingMode(u32 mode)
456
{
457
u32 retVal = 0;
458
if (mode > 1)
459
return hleLogError(Log::sceCtrl, SCE_KERNEL_ERROR_INVALID_MODE);
460
461
retVal = analogEnabled == true ? CTRL_MODE_ANALOG : CTRL_MODE_DIGITAL;
462
analogEnabled = mode == CTRL_MODE_ANALOG ? true : false;
463
return hleLogDebug(Log::sceCtrl, retVal);
464
}
465
466
static int sceCtrlGetSamplingMode(u32 modePtr)
467
{
468
u32 retVal = analogEnabled == true ? CTRL_MODE_ANALOG : CTRL_MODE_DIGITAL;
469
470
if (Memory::IsValidAddress(modePtr))
471
Memory::WriteUnchecked_U32(retVal, modePtr);
472
473
return hleLogDebug(Log::sceCtrl, 0);
474
}
475
476
static int sceCtrlSetIdleCancelThreshold(int idleReset, int idleBack)
477
{
478
if (idleReset < -1 || idleBack < -1 || idleReset > 128 || idleBack > 128)
479
return hleLogError(Log::sceCtrl, SCE_KERNEL_ERROR_INVALID_VALUE);
480
481
ctrlIdleReset = idleReset;
482
ctrlIdleBack = idleBack;
483
return hleLogDebug(Log::sceCtrl, 0);
484
}
485
486
static int sceCtrlGetIdleCancelThreshold(u32 idleResetPtr, u32 idleBackPtr)
487
{
488
if (idleResetPtr && !Memory::IsValidAddress(idleResetPtr))
489
return hleLogError(Log::sceCtrl, SCE_KERNEL_ERROR_PRIV_REQUIRED);
490
if (idleBackPtr && !Memory::IsValidAddress(idleBackPtr))
491
return hleLogError(Log::sceCtrl, SCE_KERNEL_ERROR_PRIV_REQUIRED);
492
493
if (idleResetPtr)
494
Memory::Write_U32(ctrlIdleReset, idleResetPtr);
495
if (idleBackPtr)
496
Memory::Write_U32(ctrlIdleBack, idleBackPtr);
497
498
return hleLogDebug(Log::sceCtrl, 0);
499
}
500
501
static int sceCtrlReadBufferPositive(u32 ctrlDataPtr, u32 nBufs)
502
{
503
int done = __CtrlReadBuffer(ctrlDataPtr, nBufs, false, false);
504
hleEatCycles(330);
505
if (done != 0) {
506
return hleLogDebug(Log::sceCtrl, done);
507
} else {
508
waitingThreads.push_back(__KernelGetCurThread());
509
__KernelWaitCurThread(WAITTYPE_CTRL, CTRL_WAIT_POSITIVE, ctrlDataPtr, 0, false, "ctrl buffer waited");
510
return hleLogDebug(Log::sceCtrl, done, "waiting");
511
}
512
}
513
514
static int sceCtrlReadBufferNegative(u32 ctrlDataPtr, u32 nBufs)
515
{
516
int done = __CtrlReadBuffer(ctrlDataPtr, nBufs, true, false);
517
hleEatCycles(330);
518
if (done != 0) {
519
return hleLogDebug(Log::sceCtrl, done);
520
}
521
else
522
{
523
waitingThreads.push_back(__KernelGetCurThread());
524
__KernelWaitCurThread(WAITTYPE_CTRL, CTRL_WAIT_NEGATIVE, ctrlDataPtr, 0, false, "ctrl buffer waited");
525
return hleLogDebug(Log::sceCtrl, done, "waiting");
526
}
527
}
528
529
static int sceCtrlPeekBufferPositive(u32 ctrlDataPtr, u32 nBufs)
530
{
531
int done = __CtrlReadBuffer(ctrlDataPtr, nBufs, false, true);
532
// Some homebrew call this in a tight loop - so VERBOSE it is.
533
hleEatCycles(330);
534
return hleLogVerbose(Log::sceCtrl, done);
535
}
536
537
static int sceCtrlPeekBufferNegative(u32 ctrlDataPtr, u32 nBufs)
538
{
539
int done = __CtrlReadBuffer(ctrlDataPtr, nBufs, true, true);
540
// Some homebrew call this in a tight loop - so VERBOSE it is.
541
hleEatCycles(330);
542
return hleLogVerbose(Log::sceCtrl, done);
543
}
544
545
static void __CtrlWriteUserLatch(CtrlLatch *userLatch, int bufs) {
546
*userLatch = latch;
547
userLatch->btnBreak &= CTRL_MASK_USER;
548
userLatch->btnMake &= CTRL_MASK_USER;
549
userLatch->btnPress &= CTRL_MASK_USER;
550
if (bufs > 0) {
551
userLatch->btnRelease |= ~CTRL_MASK_USER;
552
}
553
}
554
555
static u32 sceCtrlPeekLatch(u32 latchDataPtr) {
556
auto userLatch = PSPPointer<CtrlLatch>::Create(latchDataPtr);
557
if (userLatch.IsValid()) {
558
__CtrlWriteUserLatch(userLatch, ctrlLatchBufs);
559
}
560
return hleLogDebug(Log::sceCtrl, ctrlLatchBufs);
561
}
562
563
static u32 sceCtrlReadLatch(u32 latchDataPtr) {
564
auto userLatch = PSPPointer<CtrlLatch>::Create(latchDataPtr);
565
if (userLatch.IsValid()) {
566
__CtrlWriteUserLatch(userLatch, ctrlLatchBufs);
567
}
568
return hleLogDebug(Log::sceCtrl, __CtrlResetLatch());
569
}
570
571
static const HLEFunction sceCtrl[] =
572
{
573
{0X3E65A0EA, nullptr, "sceCtrlInit", '?', "" }, //(int unknown), init with 0
574
{0X1F4011E6, &WrapU_U<sceCtrlSetSamplingMode>, "sceCtrlSetSamplingMode", 'x', "x" },
575
{0X6A2774F3, &WrapU_U<sceCtrlSetSamplingCycle>, "sceCtrlSetSamplingCycle", 'x', "x" },
576
{0X02BAAD91, &WrapI_U<sceCtrlGetSamplingCycle>, "sceCtrlGetSamplingCycle", 'i', "x" },
577
{0XDA6B76A1, &WrapI_U<sceCtrlGetSamplingMode>, "sceCtrlGetSamplingMode", 'i', "x" },
578
{0X1F803938, &WrapI_UU<sceCtrlReadBufferPositive>, "sceCtrlReadBufferPositive", 'i', "xx"},
579
{0X3A622550, &WrapI_UU<sceCtrlPeekBufferPositive>, "sceCtrlPeekBufferPositive", 'i', "xx"},
580
{0XC152080A, &WrapI_UU<sceCtrlPeekBufferNegative>, "sceCtrlPeekBufferNegative", 'i', "xx"},
581
{0X60B81F86, &WrapI_UU<sceCtrlReadBufferNegative>, "sceCtrlReadBufferNegative", 'i', "xx"},
582
{0XB1D0E5CD, &WrapU_U<sceCtrlPeekLatch>, "sceCtrlPeekLatch", 'i', "x" },
583
{0X0B588501, &WrapU_U<sceCtrlReadLatch>, "sceCtrlReadLatch", 'i', "x" },
584
{0X348D99D4, nullptr, "sceCtrlSetSuspendingExtraSamples", '?', "" },
585
{0XAF5960F3, nullptr, "sceCtrlGetSuspendingExtraSamples", '?', "" },
586
{0XA68FD260, nullptr, "sceCtrlClearRapidFire", '?', "" },
587
{0X6841BE1A, nullptr, "sceCtrlSetRapidFire", '?', "" },
588
{0XA7144800, &WrapI_II<sceCtrlSetIdleCancelThreshold>, "sceCtrlSetIdleCancelThreshold", 'i', "ii"},
589
{0X687660FA, &WrapI_UU<sceCtrlGetIdleCancelThreshold>, "sceCtrlGetIdleCancelThreshold", 'i', "xx"},
590
};
591
592
void Register_sceCtrl()
593
{
594
RegisterHLEModule("sceCtrl", ARRAY_SIZE(sceCtrl), sceCtrl);
595
}
596
597
void Register_sceCtrl_driver()
598
{
599
RegisterHLEModule("sceCtrl_driver", ARRAY_SIZE(sceCtrl), sceCtrl);
600
}
601
602
u16 sceCtrlGetRightVibration() {
603
return hleNoLog(rightVibration);
604
}
605
606
u16 sceCtrlGetLeftVibration() {
607
return hleNoLog(leftVibration);
608
}
609
610
namespace SceCtrl {
611
void SetRightVibration(u16 rVibration) {
612
rightVibration = rVibration;
613
}
614
void SetLeftVibration(u16 lVibration) {
615
leftVibration = lVibration;
616
}
617
void SetVibrationRightDropout(u8 vibrationRDropout) {
618
vibrationRightDropout = vibrationRDropout;
619
}
620
void SetVibrationLeftDropout(u8 vibrationLDropout) {
621
vibrationLeftDropout = vibrationLDropout;
622
}
623
}
624
625