initial commit

This commit is contained in:
Gered 2021-02-15 18:24:59 -05:00
commit d3a8c22b67
5 changed files with 347 additions and 0 deletions

24
.gitignore vendored Normal file
View 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
View 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
View 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
View 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
View 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;
}