From d3a8c22b676a9e85fb91bba3693436e3d2ccce68 Mon Sep 17 00:00:00 2001 From: gered Date: Mon, 15 Feb 2021 18:24:59 -0500 Subject: [PATCH] initial commit --- .gitignore | 24 +++++++++ CMakeLists.txt | 30 +++++++++++ Makefile | 134 ++++++++++++++++++++++++++++++++++++++++++++++++ README.md | 23 +++++++++ src/main.c | 136 +++++++++++++++++++++++++++++++++++++++++++++++++ 5 files changed, 347 insertions(+) create mode 100644 .gitignore create mode 100644 CMakeLists.txt create mode 100644 Makefile create mode 100644 README.md create mode 100644 src/main.c diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..df154bd --- /dev/null +++ b/.gitignore @@ -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 \ No newline at end of file diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100644 index 0000000..5be3850 --- /dev/null +++ b/CMakeLists.txt @@ -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) diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..213946e --- /dev/null +++ b/Makefile @@ -0,0 +1,134 @@ +#--------------------------------------------------------------------------------- +# Clear the implicit built in rules +#--------------------------------------------------------------------------------- +.SUFFIXES: +#--------------------------------------------------------------------------------- +ifeq ($(strip $(DEVKITPPC)),) +$(error "Please set DEVKITPPC in your environment. export DEVKITPPC=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 +#--------------------------------------------------------------------------------- diff --git a/README.md b/README.md new file mode 100644 index 0000000..eeb8c51 --- /dev/null +++ b/README.md @@ -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. diff --git a/src/main.c b/src/main.c new file mode 100644 index 0000000..308ebdb --- /dev/null +++ b/src/main.c @@ -0,0 +1,136 @@ +#include +#include +#include +#include + +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; +}