initial commit
This commit is contained in:
commit
d3a8c22b67
24
.gitignore
vendored
Normal file
24
.gitignore
vendored
Normal file
|
@ -0,0 +1,24 @@
|
||||||
|
*.o
|
||||||
|
*.dol
|
||||||
|
*.elf
|
||||||
|
*.map
|
||||||
|
build/
|
||||||
|
|
||||||
|
# CMake
|
||||||
|
|
||||||
|
/cmake-build-debug
|
||||||
|
/cmake-build-release
|
||||||
|
CMakeLists.txt.user
|
||||||
|
CMakeCache.txt
|
||||||
|
CMakeFiles
|
||||||
|
CMakeScripts
|
||||||
|
Testing
|
||||||
|
cmake_install.cmake
|
||||||
|
install_manifest.txt
|
||||||
|
compile_commands.json
|
||||||
|
CTestTestfile.cmake
|
||||||
|
_deps
|
||||||
|
|
||||||
|
# IDE stuff
|
||||||
|
|
||||||
|
/.idea
|
30
CMakeLists.txt
Normal file
30
CMakeLists.txt
Normal file
|
@ -0,0 +1,30 @@
|
||||||
|
cmake_minimum_required(VERSION 3.17)
|
||||||
|
set(CMAKE_TOOLCHAIN_FILE $ENV{DEVKITPPC}/wii.toolchain)
|
||||||
|
|
||||||
|
set(PROJECT_NAME gc-ascii-keyboard)
|
||||||
|
|
||||||
|
project(${PROJECT_NAME} C ASM)
|
||||||
|
|
||||||
|
set(CMAKE_VERBOSE_MAKEFILE OFF)
|
||||||
|
set(CMAKE_C_STANDARD 99)
|
||||||
|
|
||||||
|
add_executable(${PROJECT_NAME}.elf src/main.c)
|
||||||
|
|
||||||
|
target_link_directories(${PROJECT_NAME}.elf PRIVATE ${DEVKITPRO}/libogc/lib/wii)
|
||||||
|
target_link_libraries(${PROJECT_NAME}.elf PRIVATE db wiiuse bte ogc m)
|
||||||
|
target_include_directories(${PROJECT_NAME}.elf PRIVATE ${DEVKITPRO}/libogc/include)
|
||||||
|
|
||||||
|
#add_subdirectory(${DEVKITPRO}/src/libogc-2.1.0 libogc)
|
||||||
|
#target_link_libraries(
|
||||||
|
# ${PROJECT_NAME}.elf
|
||||||
|
# ${DEVKITPRO}/src/libogc-2.1.0/lib/wii/libdb.a
|
||||||
|
# ${DEVKITPRO}/src/libogc-2.1.0/lib/wii/libwiiuse.a
|
||||||
|
# ${DEVKITPRO}/src/libogc-2.1.0/lib/wii/libbte.a
|
||||||
|
# ${DEVKITPRO}/src/libogc-2.1.0/lib/wii/libogc.a
|
||||||
|
# m
|
||||||
|
#)
|
||||||
|
#target_include_directories(${PROJECT_NAME}.elf PRIVATE ${DEVKITPRO}/src/libogc-2.1.0/gc)
|
||||||
|
|
||||||
|
target_make_dol(${PROJECT_NAME}.elf)
|
||||||
|
add_wiiload_target(wiiload ${PROJECT_NAME}.elf)
|
||||||
|
add_dolphin_target(dolphin ${PROJECT_NAME}.elf)
|
134
Makefile
Normal file
134
Makefile
Normal file
|
@ -0,0 +1,134 @@
|
||||||
|
#---------------------------------------------------------------------------------
|
||||||
|
# Clear the implicit built in rules
|
||||||
|
#---------------------------------------------------------------------------------
|
||||||
|
.SUFFIXES:
|
||||||
|
#---------------------------------------------------------------------------------
|
||||||
|
ifeq ($(strip $(DEVKITPPC)),)
|
||||||
|
$(error "Please set DEVKITPPC in your environment. export DEVKITPPC=<path to>devkitPPC")
|
||||||
|
endif
|
||||||
|
|
||||||
|
include $(DEVKITPPC)/wii_rules
|
||||||
|
|
||||||
|
#---------------------------------------------------------------------------------
|
||||||
|
# TARGET is the name of the output
|
||||||
|
# BUILD is the directory where object files & intermediate files will be placed
|
||||||
|
# SOURCES is a list of directories containing source code
|
||||||
|
# INCLUDES is a list of directories containing extra header files
|
||||||
|
#---------------------------------------------------------------------------------
|
||||||
|
TARGET := $(notdir $(CURDIR))
|
||||||
|
BUILD := build
|
||||||
|
SOURCES := src
|
||||||
|
DATA := data
|
||||||
|
INCLUDES :=
|
||||||
|
|
||||||
|
#---------------------------------------------------------------------------------
|
||||||
|
# options for code generation
|
||||||
|
#---------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
CFLAGS = -g -O2 -Wall $(MACHDEP) $(INCLUDE)
|
||||||
|
CXXFLAGS = $(CFLAGS)
|
||||||
|
|
||||||
|
LDFLAGS = -g $(MACHDEP) -Wl,-Map,$(notdir $@).map
|
||||||
|
|
||||||
|
#---------------------------------------------------------------------------------
|
||||||
|
# any extra libraries we wish to link with the project
|
||||||
|
#---------------------------------------------------------------------------------
|
||||||
|
LIBS := -ldb -lwiiuse -lbte -logc -lm
|
||||||
|
|
||||||
|
#---------------------------------------------------------------------------------
|
||||||
|
# list of directories containing libraries, this must be the top level containing
|
||||||
|
# include and lib
|
||||||
|
#---------------------------------------------------------------------------------
|
||||||
|
LIBDIRS :=
|
||||||
|
|
||||||
|
#---------------------------------------------------------------------------------
|
||||||
|
# no real need to edit anything past this point unless you need to add additional
|
||||||
|
# rules for different file extensions
|
||||||
|
#---------------------------------------------------------------------------------
|
||||||
|
ifneq ($(BUILD),$(notdir $(CURDIR)))
|
||||||
|
#---------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
export OUTPUT := $(CURDIR)/$(TARGET)
|
||||||
|
|
||||||
|
export VPATH := $(foreach dir,$(SOURCES),$(CURDIR)/$(dir)) \
|
||||||
|
$(foreach dir,$(DATA),$(CURDIR)/$(dir))
|
||||||
|
|
||||||
|
export DEPSDIR := $(CURDIR)/$(BUILD)
|
||||||
|
|
||||||
|
#---------------------------------------------------------------------------------
|
||||||
|
# automatically build a list of object files for our project
|
||||||
|
#---------------------------------------------------------------------------------
|
||||||
|
CFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.c)))
|
||||||
|
CPPFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.cpp)))
|
||||||
|
sFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.s)))
|
||||||
|
SFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.S)))
|
||||||
|
BINFILES := $(foreach dir,$(DATA),$(notdir $(wildcard $(dir)/*.*)))
|
||||||
|
|
||||||
|
#---------------------------------------------------------------------------------
|
||||||
|
# use CXX for linking C++ projects, CC for standard C
|
||||||
|
#---------------------------------------------------------------------------------
|
||||||
|
ifeq ($(strip $(CPPFILES)),)
|
||||||
|
export LD := $(CC)
|
||||||
|
else
|
||||||
|
export LD := $(CXX)
|
||||||
|
endif
|
||||||
|
|
||||||
|
export OFILES := $(addsuffix .o,$(BINFILES)) \
|
||||||
|
$(CPPFILES:.cpp=.o) $(CFILES:.c=.o) \
|
||||||
|
$(sFILES:.s=.o) $(SFILES:.S=.o)
|
||||||
|
|
||||||
|
#---------------------------------------------------------------------------------
|
||||||
|
# build a list of include paths
|
||||||
|
#---------------------------------------------------------------------------------
|
||||||
|
export INCLUDE := $(foreach dir,$(INCLUDES), -iquote $(CURDIR)/$(dir)) \
|
||||||
|
$(foreach dir,$(LIBDIRS),-I$(dir)/include) \
|
||||||
|
-I$(CURDIR)/$(BUILD) \
|
||||||
|
-I$(LIBOGC_INC)
|
||||||
|
|
||||||
|
#---------------------------------------------------------------------------------
|
||||||
|
# build a list of library paths
|
||||||
|
#---------------------------------------------------------------------------------
|
||||||
|
export LIBPATHS := $(foreach dir,$(LIBDIRS),-L$(dir)/lib) \
|
||||||
|
-L$(LIBOGC_LIB)
|
||||||
|
|
||||||
|
export OUTPUT := $(CURDIR)/$(TARGET)
|
||||||
|
.PHONY: $(BUILD) clean
|
||||||
|
|
||||||
|
#---------------------------------------------------------------------------------
|
||||||
|
$(BUILD):
|
||||||
|
@[ -d $@ ] || mkdir -p $@
|
||||||
|
@$(MAKE) --no-print-directory -C $(BUILD) -f $(CURDIR)/Makefile
|
||||||
|
|
||||||
|
#---------------------------------------------------------------------------------
|
||||||
|
clean:
|
||||||
|
@echo clean ...
|
||||||
|
@rm -fr $(BUILD) $(OUTPUT).elf $(OUTPUT).dol
|
||||||
|
|
||||||
|
#---------------------------------------------------------------------------------
|
||||||
|
run:
|
||||||
|
wiiload $(TARGET).dol
|
||||||
|
|
||||||
|
#---------------------------------------------------------------------------------
|
||||||
|
else
|
||||||
|
|
||||||
|
DEPENDS := $(OFILES:.o=.d)
|
||||||
|
|
||||||
|
#---------------------------------------------------------------------------------
|
||||||
|
# main targets
|
||||||
|
#---------------------------------------------------------------------------------
|
||||||
|
$(OUTPUT).dol: $(OUTPUT).elf
|
||||||
|
$(OUTPUT).elf: $(OFILES)
|
||||||
|
|
||||||
|
#---------------------------------------------------------------------------------
|
||||||
|
# This rule links in binary data with the .jpg extension
|
||||||
|
#---------------------------------------------------------------------------------
|
||||||
|
%.jpg.o : %.jpg
|
||||||
|
#---------------------------------------------------------------------------------
|
||||||
|
@echo $(notdir $<)
|
||||||
|
$(bin2o)
|
||||||
|
|
||||||
|
-include $(DEPENDS)
|
||||||
|
|
||||||
|
#---------------------------------------------------------------------------------
|
||||||
|
endif
|
||||||
|
#---------------------------------------------------------------------------------
|
23
README.md
Normal file
23
README.md
Normal file
|
@ -0,0 +1,23 @@
|
||||||
|
# devkitPPC Gamecube ASCII Keyboard Controller Usage Example
|
||||||
|
|
||||||
|
This is a very bare-bones project that shows how to read the [Gamecube ASCII Keyboard Controller](https://nintendo.fandom.com/wiki/ASCII_Keyboard_Controller)
|
||||||
|
using devkitPPC and libogc (from [devkitPro](https://devkitpro.org/), of course)
|
||||||
|
|
||||||
|
Currently, libogc contains no support for reading keyboard input from this keyboard controller, and only somewhat
|
||||||
|
"wonky" support for detecting it. Though, to be fair to the libogc authors, my feeling of "wonky support" for detecting
|
||||||
|
the presence of a keyboard controller could very well just be due to libogc's SI API being undocumented.
|
||||||
|
|
||||||
|
The **only** working example of any Gamecube code that could read this keyboard is located in the [gc-linux sources](https://github.com/DeltaResero/GC-Wii-Linux-Kernels/blob/GC-Wii-Linux-Kernel-3.15.y-EOL/drivers/input/si/gcn-si.c)
|
||||||
|
but, since it is a driver implemented in the Linux kernel, it basically re-implements the SI functionality that libogc
|
||||||
|
provides so it is of _somewhat_ limited direct use, but still useful to read anyway.
|
||||||
|
|
||||||
|
The only other pieces of documentation for this keyboard controller can be found on the
|
||||||
|
[Yet Another Gamecube Documentation](http://hitmen.c02.at/files/yagcd/yagcd/index.html) website. Specifically,
|
||||||
|
[section 9](http://hitmen.c02.at/files/yagcd/yagcd/chap9.html#sec9) and [section 5.8](http://hitmen.c02.at/files/yagcd/yagcd/chap5.html#sec5.8)
|
||||||
|
are both useful. However, the explanations can be fairly light for the un-initiated (a.k.a. me).
|
||||||
|
|
||||||
|
Anyway!
|
||||||
|
|
||||||
|
The code in this repository is the result of bashing my head against this. It has **only** been tested on real
|
||||||
|
hardware. More specifically, on my launch-day Nintendo Wii and keyboard controller model ASC-1901PO. I have not done
|
||||||
|
any testing on Dolphin whatsoever.
|
136
src/main.c
Normal file
136
src/main.c
Normal file
|
@ -0,0 +1,136 @@
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <gccore.h>
|
||||||
|
#include <debug.h>
|
||||||
|
|
||||||
|
static void *xfb = NULL;
|
||||||
|
static GXRModeObj *rmode = NULL;
|
||||||
|
|
||||||
|
#if defined(HW_DOL)
|
||||||
|
#define SI_REG_BASE 0xCC006400
|
||||||
|
#elif defined(HW_RVL)
|
||||||
|
#define SI_REG_BASE 0xCD006400
|
||||||
|
#else
|
||||||
|
#error HW model unknown.
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#define SIREG(n) ((vu32*)(SI_REG_BASE + (n)))
|
||||||
|
|
||||||
|
#define SICOUTBUF(n) (SIREG(0x00 + (n)*12)) /* SI Channel n Output Buffer (Joy-channel n Command) (4 bytes) */
|
||||||
|
#define SICINBUFH(i) (SIREG(0x04 + (i)*12)) /* SI Channel n Input Buffer High (Joy-channel n Buttons 1) (4 bytes) */
|
||||||
|
#define SICINBUFL(i) (SIREG(0x08 + (i)*12)) /* SI Channel n Input Buffer Low (Joy-channel n Buttons 2) (4 bytes) */
|
||||||
|
#define SIPOLL (SIREG(0x30)) /* SI Poll Register (4 bytes) */
|
||||||
|
#define SICOMCSR (SIREG(0x34)) /* SI Communication Control Status Register (command) (4 bytes) */
|
||||||
|
#define SISR (SIREG(0x38)) /* SI Status Register (4 bytes) */
|
||||||
|
#define SIIOBUF (SIREG(0x80)) /* SI I/O buffer (access by word) (128 bytes) */
|
||||||
|
|
||||||
|
#define PAD_ENABLEDMASK(chan) (0x80000000 >> chan)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
static void SI_AwaitPendingCommands(void) {
|
||||||
|
while(*SICOMCSR & 0x1);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int SI_DetectGCKeyboard(void) {
|
||||||
|
SI_AwaitPendingCommands();
|
||||||
|
|
||||||
|
u32 buf[2];
|
||||||
|
for (int i = 0; i < 4; ++i) {
|
||||||
|
SI_GetResponse(i, buf);
|
||||||
|
SI_SetCommand(i, 0x00400300);
|
||||||
|
SI_EnablePolling(PAD_ENABLEDMASK(i));
|
||||||
|
}
|
||||||
|
SI_AwaitPendingCommands();
|
||||||
|
|
||||||
|
int keyboardChan = -1;
|
||||||
|
|
||||||
|
for (int i = 0; i < 4; ++i) {
|
||||||
|
u32 type = SI_DecodeType(SI_GetType(i));
|
||||||
|
if (type == SI_GC_KEYBOARD)
|
||||||
|
keyboardChan = i;
|
||||||
|
}
|
||||||
|
|
||||||
|
return keyboardChan;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int SI_InitGCKeyboard(int chan) {
|
||||||
|
if (chan == -1)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
u32 buf[2];
|
||||||
|
|
||||||
|
SI_GetResponse(chan, buf);
|
||||||
|
SI_SetCommand(chan, 0x00540000);
|
||||||
|
SI_EnablePolling(PAD_ENABLEDMASK(chan));
|
||||||
|
SI_TransferCommands();
|
||||||
|
SI_AwaitPendingCommands();
|
||||||
|
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
int main(int argc, char **argv) {
|
||||||
|
|
||||||
|
VIDEO_Init();
|
||||||
|
|
||||||
|
rmode = VIDEO_GetPreferredMode(NULL);
|
||||||
|
xfb = MEM_K0_TO_K1(SYS_AllocateFramebuffer(rmode));
|
||||||
|
|
||||||
|
console_init(xfb,20,20,rmode->fbWidth,rmode->xfbHeight,rmode->fbWidth*VI_DISPLAY_PIX_SZ);
|
||||||
|
|
||||||
|
VIDEO_Configure(rmode);
|
||||||
|
VIDEO_SetNextFramebuffer(xfb);
|
||||||
|
VIDEO_SetBlack(FALSE);
|
||||||
|
VIDEO_Flush();
|
||||||
|
VIDEO_WaitVSync();
|
||||||
|
if(rmode->viTVMode&VI_NON_INTERLACE) VIDEO_WaitVSync();
|
||||||
|
|
||||||
|
printf("\n\nHello ...\n");
|
||||||
|
|
||||||
|
DEBUG_Init(GDBSTUB_DEVICE_USB,1);
|
||||||
|
printf("Waiting for debugger ...\n");
|
||||||
|
_break();
|
||||||
|
printf("debugger connected ...\n");
|
||||||
|
|
||||||
|
PAD_Init();
|
||||||
|
|
||||||
|
int keyboardChan = SI_DetectGCKeyboard();
|
||||||
|
printf("Keyboard located at chan %d\n", keyboardChan);
|
||||||
|
|
||||||
|
int keyboardEnabled = SI_InitGCKeyboard(keyboardChan);
|
||||||
|
if (keyboardEnabled)
|
||||||
|
printf("keyboard initialized on chan %d\n", keyboardChan);
|
||||||
|
|
||||||
|
printf("start of main loop ...\n");
|
||||||
|
while(1) {
|
||||||
|
if (keyboardEnabled) {
|
||||||
|
u32 buf[2];
|
||||||
|
if (SI_GetResponse(keyboardChan, buf)) {
|
||||||
|
u8 key1 = buf[1] >> 24;
|
||||||
|
u8 key2 = (buf[1] >> 16) & 0xff;
|
||||||
|
u8 key3 = (buf[1] >> 8) & 0xff;
|
||||||
|
if (key1 | key2 | key3)
|
||||||
|
printf("keyboard data - raw: 0x%08x 0x%08x - keys: 0x%02x, 0x%02x, 0x%02x\n",
|
||||||
|
buf[0], buf[1], key1, key2, key3);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
PAD_ScanPads();
|
||||||
|
u32 pressed = PAD_ButtonsDown(0);
|
||||||
|
if (pressed)
|
||||||
|
printf("PAD 0 buttons pressed: 0x%08x\n", pressed);
|
||||||
|
|
||||||
|
if ( pressed & PAD_BUTTON_START ) {
|
||||||
|
printf("exiting ...\n");
|
||||||
|
exit(0);
|
||||||
|
}
|
||||||
|
if (pressed & PAD_BUTTON_A) {
|
||||||
|
_break();
|
||||||
|
printf("A pressed\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
VIDEO_WaitVSync();
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
Loading…
Reference in a new issue