Skip to content

Commit 34ba7f5

Browse files
committed
Add basic Nintendo 64 Controller support
1 parent 7e7ea81 commit 34ba7f5

5 files changed

Lines changed: 391 additions & 12 deletions

File tree

Makefile

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -144,7 +144,7 @@ OGCOBJ := \
144144
console_font_8x16.o timesupp.o lock_supp.o newlibc.o usbgecko.o usbmouse.o \
145145
sbrk.o kprintf.o stm.o ios.o es.o isfs.o usb.o network_common.o \
146146
sdgecko_io.o sdgecko_buf.o gcsd.o argv.o network_wii.o wiisd.o conf.o usbstorage.o \
147-
texconv.o wiilaunch.o mic.o system_report.o mmce.o threads_supp.o \
147+
texconv.o wiilaunch.o mic.o system_report.o mmce.o n64.o threads_supp.o \
148148
malloc.o mallocr.o strdup.o strndup.o
149149

150150
#---------------------------------------------------------------------------------

include/ogc/n64.h

Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
1+
/*-------------------------------------------------------------
2+
3+
n64.h -- N64 controller subsystem
4+
5+
Copyright (C) 2019 - 2026 Extrems' Corner.org
6+
7+
This software is provided 'as-is', without any express or implied
8+
warranty. In no event will the authors be held liable for any
9+
damages arising from the use of this software.
10+
11+
Permission is granted to anyone to use this software for any
12+
purpose, including commercial applications, and to alter it and
13+
redistribute it freely, subject to the following restrictions:
14+
15+
1. The origin of this software must not be misrepresented; you
16+
must not claim that you wrote the original software. If you use
17+
this software in a product, an acknowledgment in the product
18+
documentation would be appreciated but is not required.
19+
20+
2. Altered source versions must be plainly marked as such, and
21+
must not be misrepresented as being the original software.
22+
23+
3. This notice may not be removed or altered from any source
24+
distribution.
25+
26+
-------------------------------------------------------------*/
27+
28+
#ifndef __OGC_N64_H__
29+
#define __OGC_N64_H__
30+
31+
#include <gctypes.h>
32+
33+
#define N64_ERR_READY 0
34+
#define N64_ERR_NO_CONTROLLER -1
35+
#define N64_ERR_BUSY -2
36+
#define N64_ERR_TRANSFER -3
37+
38+
#define N64_BUTTON_RIGHT 0x0001
39+
#define N64_BUTTON_LEFT 0x0002
40+
#define N64_BUTTON_DOWN 0x0004
41+
#define N64_BUTTON_UP 0x0008
42+
#define N64_BUTTON_START 0x0010
43+
#define N64_BUTTON_Z 0x0020
44+
#define N64_BUTTON_B 0x0040
45+
#define N64_BUTTON_A 0x0080
46+
#define N64_BUTTON_C_RIGHT 0x0100
47+
#define N64_BUTTON_C_LEFT 0x0200
48+
#define N64_BUTTON_C_DOWN 0x0400
49+
#define N64_BUTTON_C_UP 0x0800
50+
#define N64_BUTTON_R 0x1000
51+
#define N64_BUTTON_L 0x2000
52+
53+
#ifdef __cplusplus
54+
extern "C" {
55+
#endif
56+
57+
typedef void (*N64Callback)(s32 chan, s32 result);
58+
59+
typedef struct N64Status {
60+
u16 button;
61+
s8 stickX;
62+
s8 stickY;
63+
s8 err;
64+
} N64Status;
65+
66+
s32 N64_GetStatus(s32 chan, u8 *status);
67+
s32 N64_GetStatusAsync(s32 chan, u8 *status, N64Callback callback);
68+
s32 N64_Reset(s32 chan, u8 *status);
69+
s32 N64_ResetAsync(s32 chan, u8 *status, N64Callback callback);
70+
s32 N64_Read(s32 chan, N64Status *status);
71+
s32 N64_ReadAsync(s32 chan, N64Status *status, N64Callback callback);
72+
73+
#ifdef __cplusplus
74+
}
75+
#endif
76+
77+
#endif

libogc/mmce.c

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -60,9 +60,8 @@ static vu32 *const _piReg = (u32 *)0xCC003000;
6060

6161
static void __attribute__((constructor)) __MMCE_Init(void)
6262
{
63-
LWP_InitQueue(&__MMCE[0].syncQueue);
64-
LWP_InitQueue(&__MMCE[1].syncQueue);
65-
LWP_InitQueue(&__MMCE[2].syncQueue);
63+
for (s32 chan = EXI_CHANNEL_0; chan < EXI_CHANNEL_MAX; chan++)
64+
LWP_InitQueue(&__MMCE[chan].syncQueue);
6665
}
6766

6867
static s32 MMCE_Sync(s32 chan)

libogc/n64.c

Lines changed: 246 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,246 @@
1+
/*-------------------------------------------------------------
2+
3+
n64.c -- N64 controller subsystem
4+
5+
Copyright (C) 2019 - 2026 Extrems' Corner.org
6+
7+
This software is provided 'as-is', without any express or implied
8+
warranty. In no event will the authors be held liable for any
9+
damages arising from the use of this software.
10+
11+
Permission is granted to anyone to use this software for any
12+
purpose, including commercial applications, and to alter it and
13+
redistribute it freely, subject to the following restrictions:
14+
15+
1. The origin of this software must not be misrepresented; you
16+
must not claim that you wrote the original software. If you use
17+
this software in a product, an acknowledgment in the product
18+
documentation would be appreciated but is not required.
19+
20+
2. Altered source versions must be plainly marked as such, and
21+
must not be misrepresented as being the original software.
22+
23+
3. This notice may not be removed or altered from any source
24+
distribution.
25+
26+
-------------------------------------------------------------*/
27+
28+
#include <gctypes.h>
29+
#include <ogc/irq.h>
30+
#include <ogc/lwp.h>
31+
#include <ogc/n64.h>
32+
#include <ogc/si.h>
33+
#include <ogc/system.h>
34+
35+
typedef struct {
36+
s32 result;
37+
u8 out[35];
38+
u8 in[33];
39+
u32 outLen;
40+
u32 inLen;
41+
u8 *status;
42+
void *ptr;
43+
N64Callback proc;
44+
N64Callback callback;
45+
lwpq_t syncQueue;
46+
} N64ControlBlock;
47+
48+
static N64ControlBlock __N64[SI_MAX_CHAN];
49+
static bool Reset;
50+
51+
static s32 OnReset(s32 final)
52+
{
53+
Reset = true;
54+
return TRUE;
55+
}
56+
57+
static sys_resetinfo ResetInfo = {
58+
.func = OnReset,
59+
.prio = 127
60+
};
61+
62+
static void __attribute__((constructor)) __N64_Init(void)
63+
{
64+
for (s32 chan = SI_CHAN0; chan < SI_MAX_CHAN; chan++)
65+
LWP_InitQueue(&__N64[chan].syncQueue);
66+
67+
SYS_RegisterResetFunc(&ResetInfo);
68+
}
69+
70+
static void __N64_SyncCallback(s32 chan, s32 result)
71+
{
72+
LWP_ThreadBroadcast(__N64[chan].syncQueue);
73+
}
74+
75+
static s32 N64_Sync(s32 chan)
76+
{
77+
s32 result;
78+
u32 level = IRQ_Disable();
79+
80+
while (__N64[chan].callback)
81+
if (LWP_ThreadSleep(__N64[chan].syncQueue)) break;
82+
83+
result = __N64[chan].result;
84+
85+
IRQ_Restore(level);
86+
return result;
87+
}
88+
89+
static void __N64_Handler(s32 chan, u32 type)
90+
{
91+
N64ControlBlock *cb = &__N64[chan];
92+
N64Callback callback, proc;
93+
94+
if (Reset)
95+
return;
96+
else if (type & SI_ERROR_NO_RESPONSE)
97+
cb->result = N64_ERR_NO_CONTROLLER;
98+
else if (type & (SI_ERROR_COLLISION | SI_ERROR_OVER_RUN | SI_ERROR_UNDER_RUN))
99+
cb->result = N64_ERR_TRANSFER;
100+
else
101+
cb->result = N64_ERR_READY;
102+
103+
if (cb->proc) {
104+
proc = cb->proc;
105+
cb->proc = NULL;
106+
proc(chan, cb->result);
107+
}
108+
109+
if (cb->callback) {
110+
callback = cb->callback;
111+
cb->callback = NULL;
112+
callback(chan, cb->result);
113+
}
114+
}
115+
116+
static void TypeCallback(s32 chan, u32 type)
117+
{
118+
N64ControlBlock *cb = &__N64[chan];
119+
N64Callback callback, proc;
120+
121+
if (Reset)
122+
return;
123+
else if (SI_DecodeType(type) != SI_N64_CONTROLLER)
124+
cb->result = N64_ERR_NO_CONTROLLER;
125+
else if (!SI_Transfer(chan, cb->out, cb->outLen, cb->in, cb->inLen, __N64_Handler, 0))
126+
cb->result = N64_ERR_BUSY;
127+
else
128+
return;
129+
130+
if (cb->proc) {
131+
proc = cb->proc;
132+
cb->proc = NULL;
133+
proc(chan, cb->result);
134+
}
135+
136+
if (cb->callback) {
137+
callback = cb->callback;
138+
cb->callback = NULL;
139+
callback(chan, cb->result);
140+
}
141+
}
142+
143+
static s32 N64_Transfer(s32 chan, u32 outLen, u32 inLen, N64Callback proc)
144+
{
145+
u32 level = IRQ_Disable();
146+
147+
__N64[chan].outLen = outLen;
148+
__N64[chan].inLen = inLen;
149+
__N64[chan].proc = proc;
150+
SI_GetTypeAsync(chan, TypeCallback);
151+
152+
IRQ_Restore(level);
153+
return N64_ERR_READY;
154+
}
155+
156+
static void DefaultCallback(s32 chan, s32 result)
157+
{
158+
}
159+
160+
static void ShortCommandProc(s32 chan, s32 result)
161+
{
162+
N64ControlBlock *cb = &__N64[chan];
163+
164+
if (result == N64_ERR_READY) {
165+
if (cb->in[0] != 0x05 || cb->in[1] != 0x00) {
166+
cb->result = N64_ERR_NO_CONTROLLER;
167+
return;
168+
}
169+
170+
*cb->status = cb->in[2];
171+
}
172+
}
173+
174+
s32 N64_GetStatusAsync(s32 chan, u8 *status, N64Callback callback)
175+
{
176+
N64ControlBlock *cb = &__N64[chan];
177+
178+
if (cb->callback)
179+
return N64_ERR_BUSY;
180+
181+
cb->out[0] = 0x00;
182+
cb->status = status;
183+
cb->callback = callback ? : DefaultCallback;
184+
return N64_Transfer(chan, 1, 3, ShortCommandProc);
185+
}
186+
187+
s32 N64_GetStatus(s32 chan, u8 *status)
188+
{
189+
s32 result = N64_GetStatusAsync(chan, status, __N64_SyncCallback);
190+
if (result == N64_ERR_READY) return N64_Sync(chan);
191+
return result;
192+
}
193+
194+
s32 N64_ResetAsync(s32 chan, u8 *status, N64Callback callback)
195+
{
196+
N64ControlBlock *cb = &__N64[chan];
197+
198+
if (cb->callback)
199+
return N64_ERR_BUSY;
200+
201+
cb->out[0] = 0xFF;
202+
cb->status = status;
203+
cb->callback = callback ? : DefaultCallback;
204+
return N64_Transfer(chan, 1, 3, ShortCommandProc);
205+
}
206+
207+
s32 N64_Reset(s32 chan, u8 *status)
208+
{
209+
s32 result = N64_ResetAsync(chan, status, __N64_SyncCallback);
210+
if (result == N64_ERR_READY) return N64_Sync(chan);
211+
return result;
212+
}
213+
214+
static void ReadProc(s32 chan, s32 result)
215+
{
216+
N64ControlBlock *cb = &__N64[chan];
217+
N64Status *status = (N64Status *)cb->ptr;
218+
219+
if (result == N64_ERR_READY) {
220+
status->button = cb->in[1] << 8 | cb->in[0];
221+
status->stickX = cb->in[2];
222+
status->stickY = cb->in[3];
223+
}
224+
225+
status->err = result;
226+
}
227+
228+
s32 N64_ReadAsync(s32 chan, N64Status *status, N64Callback callback)
229+
{
230+
N64ControlBlock *cb = &__N64[chan];
231+
232+
if (cb->callback)
233+
return N64_ERR_BUSY;
234+
235+
cb->out[0] = 0x01;
236+
cb->ptr = status;
237+
cb->callback = callback ? : DefaultCallback;
238+
return N64_Transfer(chan, 1, 4, ReadProc);
239+
}
240+
241+
s32 N64_Read(s32 chan, N64Status *status)
242+
{
243+
s32 result = N64_ReadAsync(chan, status, __N64_SyncCallback);
244+
if (result == N64_ERR_READY) return N64_Sync(chan);
245+
return result;
246+
}

0 commit comments

Comments
 (0)