mirror of
https://github.com/libretro/RetroArch.git
synced 2026-11-08 08:10:30 -10:00
gekko: RetroArch builds and runs on os/gekko
Makefile.wii and Makefile.ngc take HAVE_LIBOGC: 1, the default for now, builds on libogc as before (EXTERNAL_LIBOGC choosing devkitPro's or the in-tree copy) and defines HAVE_LIBOGC; 0 builds on os/gekko, with no libogc at all, defining GEKKO_NATIVE. RetroArch's own files choose by HAVE_LIBOGC; libretro-common, which cores compile too, by GEKKO_NATIVE, so libogc core builds keep its libogc branches. The native drivers keep the libogc drivers' idents, button numbers and mode list, so configurations, autoconfig profiles and binds carry over: - platform_gekko: SD and USB (Wii) or the SD adapters (GameCube) mounted as sd:, usb:, carda:, cardb:; the directory layout under the program's own; exit back to the loader. - gekko_gfx: the frame as a textured quad, RGUI's surface and overlays over it, text poked into the EFB, two XFBs swapped on the retrace. - gekko_audio: the libogc driver's chunk ring on the audio DMA. - gekko_joypad: GameCube controllers, and remotes with Nunchuk or Classic Controller; rumble; reset opens the menu, power quits. - dispserv_gekko: the console's standard from the VI and SYSCONF, with the shared mode rules. Threads are libsysbase's pthreads on the kernel's, the clock and memory figures come from os/gekko, and stdout and stderr go to the debug channel. Not there yet: networking, USB HID pads, starting another program (core switching), the IR pointer. os/gekko/gekko.mk lists the system layer's sources for these builds and its own tests.
This commit is contained in:
1 parent
a62e5f4895
commit
e62d8258cb
16 files changed
+2411
-39
No files matched your search
+29
-9
@@ -63,9 +63,14 @@ endif
|
||||
|
||||
LIBS := $(WHOLE_START) $(LIB_CORE) $(WHOLE_END)
|
||||
|
||||
libogc_platform := 1
|
||||
|
||||
# HAVE_LIBOGC=1 builds on libogc (EXTERNAL_LIBOGC=1: devkitPro's, else
|
||||
# the copy in wii/libogc); HAVE_LIBOGC=0 on RetroArch's own system layer,
|
||||
# os/gekko.
|
||||
HAVE_LIBOGC ?= 1
|
||||
EXTERNAL_LIBOGC ?= 0
|
||||
ifeq ($(HAVE_LIBOGC), 1)
|
||||
libogc_platform := 1
|
||||
endif
|
||||
HAVE_OGG_OGC ?= 0
|
||||
CC = $(DEVKITPPC)/bin/powerpc-eabi-gcc$(EXE_EXT)
|
||||
CXX = $(DEVKITPPC)/bin/powerpc-eabi-g++$(EXE_EXT)
|
||||
@@ -82,14 +87,18 @@ endif
|
||||
|
||||
INCLUDE += -I.
|
||||
|
||||
ifeq ($(EXTERNAL_LIBOGC), 1)
|
||||
CFLAGS += -DEXTERNAL_LIBOGC
|
||||
CXXFLAGS += -DEXTERNAL_LIBOGC
|
||||
ifeq ($(HAVE_LIBOGC), 0)
|
||||
CFLAGS += -DGEKKO_NATIVE
|
||||
CXXFLAGS += -DGEKKO_NATIVE
|
||||
INCLUDE += -Ios/gekko/include
|
||||
else ifeq ($(EXTERNAL_LIBOGC), 1)
|
||||
CFLAGS += -DHAVE_LIBOGC -DEXTERNAL_LIBOGC
|
||||
CXXFLAGS += -DHAVE_LIBOGC -DEXTERNAL_LIBOGC
|
||||
LIBDIRS += -L$(DEVKITPRO)/libogc/lib/cube
|
||||
INCLUDE += -I$(DEVKITPRO)/libogc/include
|
||||
else
|
||||
CFLAGS += -DINTERNAL_LIBOGC
|
||||
CXXFLAGS += -DINTERNAL_LIBOGC
|
||||
CFLAGS += -DHAVE_LIBOGC -DINTERNAL_LIBOGC
|
||||
CXXFLAGS += -DHAVE_LIBOGC -DINTERNAL_LIBOGC
|
||||
LIBDIRS += -Lwii/libogc/libs/cube
|
||||
INCLUDE += -Iwii/libogc/include
|
||||
endif
|
||||
@@ -102,7 +111,9 @@ MACHDEP += -DGEKKO -mcpu=750 -meabi -mhard-float
|
||||
|
||||
LDFLAGS += $(MACHDEP) -Wl,-Map,$(notdir $(EXT_INTER_TARGET)).map
|
||||
|
||||
ifeq ($(BIG_STACK), 1)
|
||||
ifeq ($(HAVE_LIBOGC), 0)
|
||||
LDFLAGS += -T os/gekko/boot/dol.ld -Los/gekko/boot
|
||||
else ifeq ($(BIG_STACK), 1)
|
||||
LDFLAGS += -T bootstrap/gx/ogc.ld
|
||||
endif
|
||||
|
||||
@@ -110,6 +121,7 @@ ifeq ($(HAVE_OGG_OGC), 1)
|
||||
LIBS += -L$(DEVKITPRO)/portlibs/ppc/lib -lvorbisfile -lvorbis -logg
|
||||
endif
|
||||
|
||||
ifeq ($(HAVE_LIBOGC), 1)
|
||||
ifeq ($(EXTERNAL_LIBOGC), 1)
|
||||
LIBS += -lfat
|
||||
endif
|
||||
@@ -119,6 +131,7 @@ ifeq ($(HAVE_LOGGER), 1)
|
||||
endif
|
||||
|
||||
LIBS += -logc
|
||||
endif
|
||||
|
||||
CFLAGS += -DGEKKO -U__INT32_TYPE__ -U __UINT32_TYPE__ -D__INT32_TYPE__=int
|
||||
|
||||
@@ -170,9 +183,16 @@ INCLUDE += -I./libretro-common/include \
|
||||
-Ideps
|
||||
CFLAGS += -Wall -std=gnu99 $(MACHDEP) $(PLATCFLAGS) $(INCLUDE)
|
||||
|
||||
ifeq ($(HAVE_LIBOGC), 1)
|
||||
OBJ = $(PLATOBJS) audio/drivers/gx_audio_libogc.o gfx/drivers/gx_gfx_libogc.o gfx/display_servers/dispserv_gx_libogc.o gfx/display_servers/dispserv_gx_modes.o input/drivers/gx_input.o frontend/drivers/platform_gx_libogc.o input/drivers_joypad/gx_joypad_libogc.o
|
||||
else
|
||||
GEKKO_DIR := os/gekko
|
||||
GEKKO_PLATFORM := dol
|
||||
include os/gekko/gekko.mk
|
||||
OBJ = audio/drivers/gekko_audio.o gfx/drivers/gekko_gfx.o gfx/display_servers/dispserv_gekko.o gfx/display_servers/dispserv_gx_modes.o input/drivers/gx_input.o frontend/drivers/platform_gekko.o input/drivers_joypad/gekko_joypad.o $(addsuffix .o,$(basename $(GEKKO_SOURCES)))
|
||||
endif
|
||||
|
||||
ifeq ($(EXTERNAL_LIBOGC), 0)
|
||||
ifeq ($(HAVE_LIBOGC)$(EXTERNAL_LIBOGC), 10)
|
||||
# Bridge the prebuilt in-tree libogc's __syscalls table onto the
|
||||
# per-symbol __syscall_* hooks modern devkitPPC libsysbase dispatches
|
||||
# through (see wii/libogc_syscalls_compat.c).
|
||||
|
||||
+50
-11
@@ -58,9 +58,14 @@ endif
|
||||
|
||||
LIBS := $(WHOLE_START) $(LIB_CORE) $(WHOLE_END)
|
||||
|
||||
libogc_platform := 1
|
||||
|
||||
# HAVE_LIBOGC=1 builds on libogc (EXTERNAL_LIBOGC=1: devkitPro's, else
|
||||
# the copy in wii/libogc); HAVE_LIBOGC=0 on RetroArch's own system layer,
|
||||
# os/gekko.
|
||||
HAVE_LIBOGC ?= 1
|
||||
EXTERNAL_LIBOGC ?= 0
|
||||
ifeq ($(HAVE_LIBOGC), 1)
|
||||
libogc_platform := 1
|
||||
endif
|
||||
HAVE_OGG_OGC ?= 0
|
||||
CC = $(DEVKITPPC)/bin/powerpc-eabi-gcc$(EXE_EXT)
|
||||
CXX = $(DEVKITPPC)/bin/powerpc-eabi-g++$(EXE_EXT)
|
||||
@@ -77,14 +82,18 @@ endif
|
||||
|
||||
INCLUDE += -I.
|
||||
|
||||
ifeq ($(EXTERNAL_LIBOGC), 1)
|
||||
CFLAGS += -DEXTERNAL_LIBOGC
|
||||
CXXFLAGS += -DEXTERNAL_LIBOGC
|
||||
ifeq ($(HAVE_LIBOGC), 0)
|
||||
CFLAGS += -DGEKKO_NATIVE
|
||||
CXXFLAGS += -DGEKKO_NATIVE
|
||||
INCLUDE += -Ios/gekko/include
|
||||
else ifeq ($(EXTERNAL_LIBOGC), 1)
|
||||
CFLAGS += -DHAVE_LIBOGC -DEXTERNAL_LIBOGC
|
||||
CXXFLAGS += -DHAVE_LIBOGC -DEXTERNAL_LIBOGC
|
||||
LIBDIRS += -L$(DEVKITPRO)/libogc/lib/wii
|
||||
INCLUDE += -I$(DEVKITPRO)/libogc/include
|
||||
else
|
||||
CFLAGS += -DINTERNAL_LIBOGC
|
||||
CXXFLAGS += -DINTERNAL_LIBOGC
|
||||
CFLAGS += -DHAVE_LIBOGC -DINTERNAL_LIBOGC
|
||||
CXXFLAGS += -DHAVE_LIBOGC -DINTERNAL_LIBOGC
|
||||
LIBDIRS += -Lwii/libogc/libs/wii
|
||||
INCLUDE += -Iwii/libogc/include
|
||||
endif
|
||||
@@ -95,16 +104,23 @@ MACHDEP := -DHW_RVL -mrvl
|
||||
LIBDIRS += -L.
|
||||
MACHDEP += -DGEKKO -mcpu=750 -meabi -mhard-float
|
||||
|
||||
ifeq ($(HAVE_LIBOGC), 1)
|
||||
LDFLAGS += $(MACHDEP) -Wl,-Map,$(notdir $(EXT_INTER_TARGET)).map,-wrap,malloc,-wrap,free,-wrap,memalign,-wrap,calloc,-wrap,realloc,-wrap,strdup,-wrap,strndup,-wrap,malloc_usable_size
|
||||
|
||||
ifeq ($(BIG_STACK), 1)
|
||||
LDFLAGS += -T bootstrap/gx/rvl.ld
|
||||
endif
|
||||
else
|
||||
# os/gekko's heap runs from MEM1 into MEM2 by itself.
|
||||
LDFLAGS += $(MACHDEP) -Wl,-Map,$(notdir $(EXT_INTER_TARGET)).map \
|
||||
-T os/gekko/boot/rvl.ld -Los/gekko/boot
|
||||
endif
|
||||
|
||||
ifeq ($(HAVE_OGG_OGC), 1)
|
||||
LIBS += -L$(DEVKITPRO)/portlibs/ppc/lib -lvorbisfile -lvorbis -logg
|
||||
endif
|
||||
|
||||
ifeq ($(HAVE_LIBOGC), 1)
|
||||
ifeq ($(EXTERNAL_LIBOGC), 1)
|
||||
LIBS += -lfat
|
||||
endif
|
||||
@@ -120,6 +136,7 @@ ifeq ($(HAVE_LOGGER), 1)
|
||||
endif
|
||||
|
||||
LIBS += -logc
|
||||
endif
|
||||
|
||||
CFLAGS += -DGEKKO -U__INT32_TYPE__ -U __UINT32_TYPE__ -D__INT32_TYPE__=int
|
||||
|
||||
@@ -155,20 +172,33 @@ HAVE_STATIC_AUDIO_FILTERS := 1
|
||||
HAVE_STATIC_VIDEO_FILTERS := 1
|
||||
|
||||
#HAVE_LANGEXTRA := 1
|
||||
HAVE_WIIUSB_HID := 1
|
||||
HAVE_HID := 1
|
||||
HAVE_RARCH_EXEC := 1
|
||||
HAVE_RSOUND := 0
|
||||
HAVE_XMB := 0
|
||||
HAVE_OZONE := 0
|
||||
HAVE_RGUI := 1
|
||||
HAVE_MATERIALUI := 0
|
||||
|
||||
ifeq ($(HAVE_LIBOGC), 1)
|
||||
HAVE_WIIUSB_HID := 1
|
||||
HAVE_HID := 1
|
||||
HAVE_RARCH_EXEC := 1
|
||||
HAVE_CHEEVOS := 1
|
||||
|
||||
CFLAGS += -DHAVE_SOCKET_LEGACY -DHAVE_CHEEVOS
|
||||
|
||||
APP_BOOTER_DIR = wii/app_booter
|
||||
PLATOBJS := $(APP_BOOTER_DIR)/app_booter.binobj
|
||||
else
|
||||
# Not on os/gekko yet: networking, USB HID pads, and starting another
|
||||
# program (a core switch needs it).
|
||||
HAVE_NETWORKING := 0
|
||||
HAVE_IFINFO := 0
|
||||
HAVE_WIIUSB_HID := 0
|
||||
HAVE_HID := 0
|
||||
HAVE_RARCH_EXEC := 0
|
||||
HAVE_CHEEVOS := 0
|
||||
PLATOBJS :=
|
||||
endif
|
||||
|
||||
ifeq ($(USBGECKO), 1)
|
||||
CFLAGS += -DUSBGECKO
|
||||
@@ -179,9 +209,16 @@ INCLUDE += -I./libretro-common/include \
|
||||
-Ideps/rcheevos/include
|
||||
CFLAGS += -Wall -std=gnu99 $(MACHDEP) $(PLATCFLAGS) $(INCLUDE)
|
||||
|
||||
ifeq ($(HAVE_LIBOGC), 1)
|
||||
OBJ = $(PLATOBJS) audio/drivers/gx_audio_libogc.o gfx/drivers/gx_gfx_libogc.o gfx/display_servers/dispserv_gx_libogc.o gfx/display_servers/dispserv_gx_modes.o input/drivers/gx_input.o frontend/drivers/platform_gx_libogc.o input/drivers_joypad/gx_joypad_libogc.o libretro-common/memory/mem2_manager_libogc.o gfx/drivers/gx_gfx_vi_encoder_libogc.o frontend/drivers/platform_wii_libogc.o input/drivers_hid/wiiusb_hid_libogc.o
|
||||
else
|
||||
GEKKO_DIR := os/gekko
|
||||
GEKKO_PLATFORM := rvl
|
||||
include os/gekko/gekko.mk
|
||||
OBJ = audio/drivers/gekko_audio.o gfx/drivers/gekko_gfx.o gfx/display_servers/dispserv_gekko.o gfx/display_servers/dispserv_gx_modes.o input/drivers/gx_input.o frontend/drivers/platform_gekko.o input/drivers_joypad/gekko_joypad.o $(addsuffix .o,$(basename $(GEKKO_SOURCES)))
|
||||
endif
|
||||
|
||||
ifeq ($(EXTERNAL_LIBOGC), 0)
|
||||
ifeq ($(HAVE_LIBOGC)$(EXTERNAL_LIBOGC), 10)
|
||||
# Bridge the prebuilt in-tree libogc's __syscalls table onto the
|
||||
# per-symbol __syscall_* hooks modern devkitPPC libsysbase dispatches
|
||||
# through (see wii/libogc_syscalls_compat.c).
|
||||
@@ -320,7 +357,9 @@ clean:
|
||||
$(Q)rm -f $(EXT_TARGET)
|
||||
$(Q)rm -f $(EXT_INTER_TARGET)
|
||||
$(Q)rm -f $(OBJ)
|
||||
ifeq ($(HAVE_LIBOGC), 1)
|
||||
$(Q)$(MAKE) -C $(APP_BOOTER_DIR) clean
|
||||
endif
|
||||
|
||||
.PHONY: clean
|
||||
|
||||
|
||||
@@ -0,0 +1,366 @@
|
||||
/* RetroArch - A frontend for libretro.
|
||||
*
|
||||
* RetroArch is free software: you can redistribute it and/or modify it under the terms
|
||||
* of the GNU General Public License as published by the Free Software Found-
|
||||
* ation, either version 3 of the License, or (at your option) any later version.
|
||||
*
|
||||
* RetroArch is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
|
||||
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
|
||||
* PURPOSE. See the GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along with RetroArch.
|
||||
* If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
/* Audio on os/gekko: a ring of small chunks the audio DMA plays in
|
||||
* turn. The DMA's callback, at the start of each chunk, silences the
|
||||
* one it retired and queues the next, so a writer that falls behind
|
||||
* is heard as silence rather than a repeat. */
|
||||
|
||||
#include <malloc.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include <gekko/audio.h>
|
||||
#include <gekko/thread.h>
|
||||
|
||||
#include <boolean.h>
|
||||
#include <retro_inline.h>
|
||||
|
||||
#include "../audio_driver.h"
|
||||
|
||||
#define CHUNK_FRAMES 64
|
||||
#define CHUNK_SIZE (CHUNK_FRAMES * sizeof(uint32_t))
|
||||
#define BLOCKS 16
|
||||
|
||||
typedef struct
|
||||
{
|
||||
uint32_t data[BLOCKS][CHUNK_FRAMES];
|
||||
/* The device clock fit, touched only by device_clock_ppm(): sums
|
||||
* in seconds and frames from an anchor, so cancellation does not
|
||||
* eat the answer. */
|
||||
double clk_sx, clk_sy, clk_sxx, clk_sxy, clk_n;
|
||||
size_t write_ptr;
|
||||
/* Shared with the callback. One core and no store buffer, so
|
||||
* volatile is enough; 32-bit so reads do not tear. */
|
||||
volatile uint32_t dma_busy;
|
||||
volatile uint32_t dma_next;
|
||||
volatile uint32_t dma_write;
|
||||
volatile uint32_t chunks; /* bumped per chunk; writers sleep on it */
|
||||
volatile uint32_t consumed; /* frames played since the start */
|
||||
volatile uint32_t underruns; /* chunks played that were not written */
|
||||
/* (position, time) per chunk for the clock fit, between two bumps
|
||||
* of clk_seq so a reader can tell a torn pair. */
|
||||
volatile uint32_t clk_pos;
|
||||
volatile uint32_t clk_tb;
|
||||
volatile uint32_t clk_seq;
|
||||
uint32_t clk_last_seq;
|
||||
uint32_t clk_anchor_pos;
|
||||
uint32_t clk_anchor_tb;
|
||||
unsigned rate;
|
||||
int clk_ppm;
|
||||
uint8_t clk_have_anchor;
|
||||
uint8_t clk_valid;
|
||||
bool nonblock;
|
||||
bool is_paused;
|
||||
bool stopping;
|
||||
} gekko_audio_t;
|
||||
|
||||
/* Interrupt context: the DMA has started on dma_next. */
|
||||
static void gekko_audio_chunk(void *data)
|
||||
{
|
||||
gekko_audio_t *wa = (gekko_audio_t*)data;
|
||||
if (wa->stopping)
|
||||
return;
|
||||
memset(wa->data[wa->dma_busy], 0, CHUNK_SIZE);
|
||||
wa->dma_busy = wa->dma_next;
|
||||
wa->dma_next = (wa->dma_next + 1) & (BLOCKS - 1);
|
||||
if (wa->dma_next == wa->dma_write)
|
||||
wa->underruns++;
|
||||
gk_dcache_flush(wa->data[wa->dma_next], CHUNK_SIZE);
|
||||
gk_audio_queue(wa->data[wa->dma_next], CHUNK_SIZE);
|
||||
wa->consumed += CHUNK_FRAMES;
|
||||
wa->clk_seq++;
|
||||
wa->clk_pos = wa->consumed;
|
||||
wa->clk_tb = (uint32_t)gk_ticks();
|
||||
wa->clk_seq++;
|
||||
wa->chunks++;
|
||||
gk_futex_wake(&wa->chunks, 0x7fffffff);
|
||||
}
|
||||
|
||||
static void *gekko_audio_init(const char *device, unsigned rate,
|
||||
unsigned latency, unsigned *new_rate)
|
||||
{
|
||||
gekko_audio_t *wa = (gekko_audio_t*)memalign(32, sizeof(*wa));
|
||||
if (!wa)
|
||||
return NULL;
|
||||
memset(wa, 0, sizeof(*wa));
|
||||
|
||||
/* The AI runs at 32 or 48 kHz: below 32001, and the 40000-47999
|
||||
* range the setting steps down through from 48000, take 32000. */
|
||||
wa->rate = (rate <= 32000 || (rate >= 40000 && rate < 48000))
|
||||
? 32000 : 48000;
|
||||
*new_rate = wa->rate;
|
||||
|
||||
wa->dma_write = BLOCKS - 1;
|
||||
gk_dcache_flush(wa->data, sizeof(wa->data));
|
||||
gk_audio_init(wa->rate);
|
||||
gk_audio_set_cb(gekko_audio_chunk, wa);
|
||||
gk_audio_queue(wa->data[wa->dma_next], CHUNK_SIZE);
|
||||
gk_audio_start();
|
||||
return wa;
|
||||
}
|
||||
|
||||
/* The AI takes each frame right channel first: a half-word rotate,
|
||||
* not a byte swap. */
|
||||
static INLINE void gekko_audio_lr_swap(uint32_t *dst, const uint32_t *src,
|
||||
size_t len)
|
||||
{
|
||||
size_t n4 = len >> 2;
|
||||
while (n4--)
|
||||
{
|
||||
uint32_t s0 = src[0], s1 = src[1], s2 = src[2], s3 = src[3];
|
||||
dst[0] = (s0 >> 16) | (s0 << 16);
|
||||
dst[1] = (s1 >> 16) | (s1 << 16);
|
||||
dst[2] = (s2 >> 16) | (s2 << 16);
|
||||
dst[3] = (s3 >> 16) | (s3 << 16);
|
||||
src += 4;
|
||||
dst += 4;
|
||||
}
|
||||
for (len &= 3; len; --len)
|
||||
{
|
||||
uint32_t s = *src++;
|
||||
*dst++ = (s >> 16) | (s << 16);
|
||||
}
|
||||
}
|
||||
|
||||
/* Sleep until the callback retires another chunk. */
|
||||
static void gekko_audio_wait_chunk(gekko_audio_t *wa)
|
||||
{
|
||||
uint32_t seen = wa->chunks;
|
||||
gk_futex_wait(&wa->chunks, seen, GK_US_TO_TICKS(20000));
|
||||
}
|
||||
|
||||
static ssize_t gekko_audio_write(void *data, const void *buf_, size_t len)
|
||||
{
|
||||
gekko_audio_t *wa = (gekko_audio_t*)data;
|
||||
const uint32_t *buf = (const uint32_t*)buf_;
|
||||
size_t frames = len >> 2;
|
||||
|
||||
while (frames)
|
||||
{
|
||||
size_t to_write = CHUNK_FRAMES - wa->write_ptr;
|
||||
if (frames < to_write)
|
||||
to_write = frames;
|
||||
|
||||
/* The chunk to fill is queued or playing: the ring is full. */
|
||||
while (wa->dma_write == wa->dma_next || wa->dma_write == wa->dma_busy)
|
||||
{
|
||||
if (wa->nonblock || wa->stopping || wa->is_paused)
|
||||
return (ssize_t)(len - (frames << 2));
|
||||
gekko_audio_wait_chunk(wa);
|
||||
}
|
||||
|
||||
gekko_audio_lr_swap(wa->data[wa->dma_write] + wa->write_ptr, buf,
|
||||
to_write);
|
||||
wa->write_ptr += to_write;
|
||||
frames -= to_write;
|
||||
buf += to_write;
|
||||
|
||||
if (wa->write_ptr >= CHUNK_FRAMES)
|
||||
{
|
||||
wa->write_ptr -= CHUNK_FRAMES;
|
||||
wa->dma_write = (wa->dma_write + 1) & (BLOCKS - 1);
|
||||
}
|
||||
}
|
||||
return (ssize_t)len;
|
||||
}
|
||||
|
||||
static bool gekko_audio_stop(void *data)
|
||||
{
|
||||
gekko_audio_t *wa = (gekko_audio_t*)data;
|
||||
if (!wa)
|
||||
return false;
|
||||
gk_audio_stop();
|
||||
memset(wa->data, 0, sizeof(wa->data));
|
||||
gk_dcache_flush(wa->data, sizeof(wa->data));
|
||||
wa->is_paused = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool gekko_audio_start(void *data, bool is_shutdown)
|
||||
{
|
||||
gekko_audio_t *wa = (gekko_audio_t*)data;
|
||||
if (!wa)
|
||||
return false;
|
||||
gk_audio_start();
|
||||
wa->is_paused = false;
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool gekko_audio_alive(void *data)
|
||||
{
|
||||
gekko_audio_t *wa = (gekko_audio_t*)data;
|
||||
return wa && !wa->is_paused;
|
||||
}
|
||||
|
||||
static void gekko_audio_set_nonblock_state(void *data, bool state)
|
||||
{
|
||||
gekko_audio_t *wa = (gekko_audio_t*)data;
|
||||
if (wa)
|
||||
wa->nonblock = state;
|
||||
}
|
||||
|
||||
static void gekko_audio_free(void *data)
|
||||
{
|
||||
gekko_audio_t *wa = (gekko_audio_t*)data;
|
||||
if (!wa)
|
||||
return;
|
||||
wa->stopping = true;
|
||||
gk_audio_stop();
|
||||
gk_audio_set_cb(NULL, NULL);
|
||||
free(wa);
|
||||
}
|
||||
|
||||
static size_t gekko_audio_write_avail(void *data)
|
||||
{
|
||||
gekko_audio_t *wa = (gekko_audio_t*)data;
|
||||
return ((wa->dma_busy - wa->dma_write + BLOCKS) & (BLOCKS - 1))
|
||||
* CHUNK_SIZE;
|
||||
}
|
||||
|
||||
static size_t gekko_audio_buffer_size(void *data)
|
||||
{
|
||||
return BLOCKS * CHUNK_SIZE;
|
||||
}
|
||||
|
||||
/* Until len bytes are free (at most half the ring, so it ends), or 0
|
||||
* while stopped. */
|
||||
static size_t gekko_audio_wait_writable(void *data, size_t len)
|
||||
{
|
||||
gekko_audio_t *wa = (gekko_audio_t*)data;
|
||||
if (len > (BLOCKS * CHUNK_SIZE) / 2)
|
||||
len = (BLOCKS * CHUNK_SIZE) / 2;
|
||||
for (;;)
|
||||
{
|
||||
size_t avail;
|
||||
if (wa->stopping || wa->is_paused)
|
||||
return 0;
|
||||
if ((avail = gekko_audio_write_avail(wa)) >= len)
|
||||
return avail;
|
||||
gekko_audio_wait_chunk(wa);
|
||||
}
|
||||
}
|
||||
|
||||
static size_t gekko_audio_frames_consumed(void *data)
|
||||
{
|
||||
gekko_audio_t *wa = (gekko_audio_t*)data;
|
||||
return wa ? (size_t)wa->consumed : 0;
|
||||
}
|
||||
|
||||
/* 16-bit PCM is all the AI takes. */
|
||||
static bool gekko_audio_use_float(void *data) { return false; }
|
||||
|
||||
static size_t gekko_audio_underruns(void *data)
|
||||
{
|
||||
gekko_audio_t *wa = (gekko_audio_t*)data;
|
||||
return wa ? (size_t)wa->underruns : 0;
|
||||
}
|
||||
|
||||
static double ticks_to_seconds(uint32_t ticks)
|
||||
{
|
||||
return (double)ticks / (double)gk_tb_hz;
|
||||
}
|
||||
|
||||
/* The AI's clock against the rate reported, in parts per million: a
|
||||
* least-squares fit of frames played against time over a window of a
|
||||
* second or more. A measurement; nothing acts on it. */
|
||||
static bool gekko_audio_device_clock_ppm(void *data, double *ppm)
|
||||
{
|
||||
gekko_audio_t *wa = (gekko_audio_t*)data;
|
||||
uint32_t s1 = 0, s2 = 0, pos = 0, tb = 0;
|
||||
int tries = 4;
|
||||
|
||||
if (!wa || !wa->rate)
|
||||
return false;
|
||||
|
||||
do
|
||||
{
|
||||
s1 = wa->clk_seq;
|
||||
pos = wa->clk_pos;
|
||||
tb = wa->clk_tb;
|
||||
s2 = wa->clk_seq;
|
||||
} while ((s1 != s2 || (s1 & 1u)) && --tries > 0);
|
||||
|
||||
if (s1 == s2 && !(s1 & 1u) && s1 != wa->clk_last_seq)
|
||||
{
|
||||
wa->clk_last_seq = s1;
|
||||
/* A new anchor at the start, after the DMA restarted, and well
|
||||
* inside the wrap of the 32-bit time base sampled. */
|
||||
if ( !wa->clk_have_anchor
|
||||
|| pos - wa->clk_anchor_pos > 0x80000000u
|
||||
|| (uint32_t)(pos - wa->clk_anchor_pos) > wa->rate * 31u
|
||||
|| ticks_to_seconds(tb - wa->clk_anchor_tb) > 30.0)
|
||||
{
|
||||
wa->clk_anchor_pos = pos;
|
||||
wa->clk_anchor_tb = tb;
|
||||
wa->clk_have_anchor = 1;
|
||||
wa->clk_valid = 0;
|
||||
wa->clk_sx = wa->clk_sy = wa->clk_sxx = wa->clk_sxy = 0.0;
|
||||
wa->clk_n = 0.0;
|
||||
}
|
||||
else
|
||||
{
|
||||
double x = ticks_to_seconds(tb - wa->clk_anchor_tb);
|
||||
double y = (double)(uint32_t)(pos - wa->clk_anchor_pos);
|
||||
wa->clk_sx += x;
|
||||
wa->clk_sy += y;
|
||||
wa->clk_sxx += x * x;
|
||||
wa->clk_sxy += x * y;
|
||||
wa->clk_n += 1.0;
|
||||
if (wa->clk_n >= 4.0 && x >= 1.0)
|
||||
{
|
||||
double denom = wa->clk_n * wa->clk_sxx - wa->clk_sx * wa->clk_sx;
|
||||
if (denom > 0.0)
|
||||
{
|
||||
double slope = (wa->clk_n * wa->clk_sxy
|
||||
- wa->clk_sx * wa->clk_sy) / denom;
|
||||
double est = (slope / (double)wa->rate - 1.0) * 1000000.0;
|
||||
if (est > -100000.0 && est < 100000.0)
|
||||
{
|
||||
wa->clk_ppm = (int)est;
|
||||
wa->clk_valid = 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!wa->clk_valid)
|
||||
return false;
|
||||
*ppm = (double)wa->clk_ppm;
|
||||
return true;
|
||||
}
|
||||
|
||||
audio_driver_t audio_gx = {
|
||||
gekko_audio_init,
|
||||
gekko_audio_write,
|
||||
gekko_audio_stop,
|
||||
gekko_audio_start,
|
||||
gekko_audio_alive,
|
||||
gekko_audio_set_nonblock_state,
|
||||
gekko_audio_free,
|
||||
gekko_audio_use_float,
|
||||
"gx",
|
||||
NULL,
|
||||
NULL,
|
||||
gekko_audio_write_avail,
|
||||
gekko_audio_buffer_size,
|
||||
NULL, /* write_raw */
|
||||
gekko_audio_wait_writable,
|
||||
gekko_audio_frames_consumed,
|
||||
gekko_audio_underruns,
|
||||
NULL, /* layout */
|
||||
NULL, /* frames_consumed_fallback */
|
||||
gekko_audio_device_clock_ppm
|
||||
};
|
||||
@@ -0,0 +1,325 @@
|
||||
/* RetroArch - A frontend for libretro.
|
||||
*
|
||||
* RetroArch is free software: you can redistribute it and/or modify it under the terms
|
||||
* of the GNU General Public License as published by the Free Software Found-
|
||||
* ation, either version 3 of the License, or (at your option) any later version.
|
||||
*
|
||||
* RetroArch is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
|
||||
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
|
||||
* PURPOSE. See the GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along with RetroArch.
|
||||
* If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
/* The GameCube and Wii on os/gekko: storage mounted as C library
|
||||
* devices, the directory layout under the one RetroArch started from,
|
||||
* and leaving through the loader.
|
||||
*
|
||||
* Wii: the front SD slot is "sd:", the first USB drive "usb:".
|
||||
* GameCube: an SD adapter in serial port 2 is "sd:", in the memory
|
||||
* card slots "carda:" and "cardb:". */
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include <gekko/disk.h>
|
||||
#ifdef HW_RVL
|
||||
#include <gekko/usb.h>
|
||||
#endif
|
||||
|
||||
#include <boolean.h>
|
||||
#include <compat/strl.h>
|
||||
#include <file/file_path.h>
|
||||
#ifndef IS_SALAMANDER
|
||||
#include <lists/file_list.h>
|
||||
#endif
|
||||
#include <string/stdstring.h>
|
||||
|
||||
#include "../frontend_driver.h"
|
||||
|
||||
#include "../../defaults.h"
|
||||
#include "../../msg_hash.h"
|
||||
#include "../../retroarch_types.h"
|
||||
#include "../../verbosity.h"
|
||||
|
||||
#if !defined(IS_SALAMANDER)
|
||||
#include "../../paths.h"
|
||||
#include "../../menu/menu_entries.h"
|
||||
#endif
|
||||
|
||||
struct volume
|
||||
{
|
||||
const char *name;
|
||||
uint8_t mounted;
|
||||
};
|
||||
|
||||
#ifdef HW_RVL
|
||||
enum { VOL_SD = 0, VOL_USB, VOL_COUNT };
|
||||
static struct volume volumes[VOL_COUNT] = { { "sd", 0 }, { "usb", 0 } };
|
||||
#else
|
||||
enum { VOL_SD = 0, VOL_CARDA, VOL_CARDB, VOL_COUNT };
|
||||
static struct volume volumes[VOL_COUNT] = {
|
||||
{ "sd", 0 }, { "carda", 0 }, { "cardb", 0 } };
|
||||
/* The EXI channel each GameCube volume sits on. */
|
||||
static const unsigned volume_channel[VOL_COUNT] = { 2, 0, 1 };
|
||||
#endif
|
||||
|
||||
static void mount(unsigned i, gk_blockdev_t *dev)
|
||||
{
|
||||
int ret;
|
||||
if (!dev)
|
||||
return;
|
||||
if ((ret = gk_fat_mount(volumes[i].name, dev)))
|
||||
RARCH_WARN("[Gekko] %s: no FAT volume (%d).\n", volumes[i].name, ret);
|
||||
else
|
||||
volumes[i].mounted = 1;
|
||||
}
|
||||
|
||||
static void frontend_gekko_init(void *data)
|
||||
{
|
||||
(void)data;
|
||||
#ifdef HW_RVL
|
||||
mount(VOL_SD, gk_sd_open());
|
||||
mount(VOL_USB, gk_usbstorage_open());
|
||||
#else
|
||||
{
|
||||
unsigned i;
|
||||
for (i = 0; i < VOL_COUNT; i++)
|
||||
mount(i, gk_sdgecko_open(volume_channel[i]));
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
static void frontend_gekko_deinit(void *data)
|
||||
{
|
||||
unsigned i;
|
||||
(void)data;
|
||||
for (i = 0; i < VOL_COUNT; i++)
|
||||
if (volumes[i].mounted)
|
||||
{
|
||||
gk_fat_unmount(volumes[i].name);
|
||||
volumes[i].mounted = 0;
|
||||
}
|
||||
}
|
||||
|
||||
/* Where RetroArch lives: the directory of the program the loader
|
||||
* started, else retroarch on the first volume mounted. */
|
||||
static void base_dir(int argc, char *argv[], char *s, size_t len)
|
||||
{
|
||||
unsigned i;
|
||||
if (argc >= 1 && argv && argv[0] && strstr(argv[0], ":/"))
|
||||
{
|
||||
char *slash;
|
||||
strlcpy(s, argv[0], len);
|
||||
if ((slash = strrchr(s, '/')))
|
||||
*slash = '\0';
|
||||
return;
|
||||
}
|
||||
for (i = 0; i < VOL_COUNT; i++)
|
||||
if (volumes[i].mounted)
|
||||
{
|
||||
snprintf(s, len, "%s:/retroarch", volumes[i].name);
|
||||
return;
|
||||
}
|
||||
strlcpy(s, "sd:/retroarch", len);
|
||||
}
|
||||
|
||||
static void frontend_gekko_get_env(int *argc, char *argv[],
|
||||
void *args, void *params_data)
|
||||
{
|
||||
char *slash;
|
||||
#ifndef IS_SALAMANDER
|
||||
struct rarch_main_wrap *params = (struct rarch_main_wrap*)params_data;
|
||||
#endif
|
||||
(void)args;
|
||||
|
||||
base_dir(*argc, argv, g_defaults.dirs[DEFAULT_DIR_CORE],
|
||||
sizeof(g_defaults.dirs[DEFAULT_DIR_CORE]));
|
||||
chdir(g_defaults.dirs[DEFAULT_DIR_CORE]);
|
||||
|
||||
#ifndef IS_SALAMANDER
|
||||
/* Some loaders pass nothing at all; RetroArch's argument parsing
|
||||
* wants something to look at. */
|
||||
if (*argc <= 0 || !argv)
|
||||
{
|
||||
if (params)
|
||||
{
|
||||
params->content_path = NULL;
|
||||
params->sram_path = NULL;
|
||||
params->state_path = NULL;
|
||||
params->config_path = NULL;
|
||||
params->libretro_path = NULL;
|
||||
params->flags &= ~(RARCH_MAIN_WRAP_FLAG_VERBOSE
|
||||
| RARCH_MAIN_WRAP_FLAG_NO_CONTENT);
|
||||
params->flags |= RARCH_MAIN_WRAP_FLAG_TOUCHED;
|
||||
}
|
||||
}
|
||||
else if (*argc > 2 && argv[1] && *argv[1] && argv[2] && *argv[2]
|
||||
&& params)
|
||||
{
|
||||
/* Loaders that start content pass its directory and file name
|
||||
* as two arguments. */
|
||||
static char path[PATH_MAX_LENGTH];
|
||||
fill_pathname_join(path, argv[1], argv[2], sizeof(path));
|
||||
params->content_path = path;
|
||||
params->sram_path = NULL;
|
||||
params->state_path = NULL;
|
||||
params->config_path = NULL;
|
||||
params->libretro_path = NULL;
|
||||
params->flags &= ~(RARCH_MAIN_WRAP_FLAG_VERBOSE
|
||||
| RARCH_MAIN_WRAP_FLAG_NO_CONTENT);
|
||||
params->flags |= RARCH_MAIN_WRAP_FLAG_TOUCHED;
|
||||
}
|
||||
#endif
|
||||
|
||||
/* The port directory: retroarch at the root of the volume the
|
||||
* program is on. */
|
||||
strlcpy(g_defaults.dirs[DEFAULT_DIR_PORT],
|
||||
g_defaults.dirs[DEFAULT_DIR_CORE],
|
||||
sizeof(g_defaults.dirs[DEFAULT_DIR_PORT]));
|
||||
if ((slash = strchr(g_defaults.dirs[DEFAULT_DIR_PORT], '/')))
|
||||
*slash = '\0';
|
||||
fill_pathname_join(g_defaults.dirs[DEFAULT_DIR_PORT],
|
||||
g_defaults.dirs[DEFAULT_DIR_PORT], "retroarch",
|
||||
sizeof(g_defaults.dirs[DEFAULT_DIR_PORT]));
|
||||
|
||||
fill_pathname_join(g_defaults.dirs[DEFAULT_DIR_CORE_INFO],
|
||||
g_defaults.dirs[DEFAULT_DIR_CORE], "info",
|
||||
sizeof(g_defaults.dirs[DEFAULT_DIR_CORE_INFO]));
|
||||
fill_pathname_join(g_defaults.dirs[DEFAULT_DIR_AUTOCONFIG],
|
||||
g_defaults.dirs[DEFAULT_DIR_CORE], "autoconfig",
|
||||
sizeof(g_defaults.dirs[DEFAULT_DIR_AUTOCONFIG]));
|
||||
fill_pathname_join(g_defaults.dirs[DEFAULT_DIR_OVERLAY],
|
||||
g_defaults.dirs[DEFAULT_DIR_CORE], "overlays",
|
||||
sizeof(g_defaults.dirs[DEFAULT_DIR_OVERLAY]));
|
||||
fill_pathname_join(g_defaults.dirs[DEFAULT_DIR_OSK_OVERLAY],
|
||||
g_defaults.dirs[DEFAULT_DIR_CORE], "overlays/keyboards",
|
||||
sizeof(g_defaults.dirs[DEFAULT_DIR_OSK_OVERLAY]));
|
||||
fill_pathname_join(g_defaults.dirs[DEFAULT_DIR_VIDEO_FILTER],
|
||||
g_defaults.dirs[DEFAULT_DIR_CORE], "filters/video",
|
||||
sizeof(g_defaults.dirs[DEFAULT_DIR_VIDEO_FILTER]));
|
||||
fill_pathname_join(g_defaults.dirs[DEFAULT_DIR_AUDIO_FILTER],
|
||||
g_defaults.dirs[DEFAULT_DIR_CORE], "filters/audio",
|
||||
sizeof(g_defaults.dirs[DEFAULT_DIR_AUDIO_FILTER]));
|
||||
fill_pathname_join(g_defaults.dirs[DEFAULT_DIR_ASSETS],
|
||||
g_defaults.dirs[DEFAULT_DIR_CORE], "assets",
|
||||
sizeof(g_defaults.dirs[DEFAULT_DIR_ASSETS]));
|
||||
fill_pathname_join(g_defaults.dirs[DEFAULT_DIR_CHEATS],
|
||||
g_defaults.dirs[DEFAULT_DIR_CORE], "cheats",
|
||||
sizeof(g_defaults.dirs[DEFAULT_DIR_CHEATS]));
|
||||
fill_pathname_join(g_defaults.path_config,
|
||||
g_defaults.dirs[DEFAULT_DIR_CORE], "retroarch.cfg",
|
||||
sizeof(g_defaults.path_config));
|
||||
fill_pathname_join(g_defaults.dirs[DEFAULT_DIR_SYSTEM],
|
||||
g_defaults.dirs[DEFAULT_DIR_PORT], "system",
|
||||
sizeof(g_defaults.dirs[DEFAULT_DIR_SYSTEM]));
|
||||
fill_pathname_join(g_defaults.dirs[DEFAULT_DIR_SRAM],
|
||||
g_defaults.dirs[DEFAULT_DIR_PORT], "savefiles",
|
||||
sizeof(g_defaults.dirs[DEFAULT_DIR_SRAM]));
|
||||
fill_pathname_join(g_defaults.dirs[DEFAULT_DIR_SAVESTATE],
|
||||
g_defaults.dirs[DEFAULT_DIR_PORT], "savestates",
|
||||
sizeof(g_defaults.dirs[DEFAULT_DIR_SAVESTATE]));
|
||||
fill_pathname_join(g_defaults.dirs[DEFAULT_DIR_PLAYLIST],
|
||||
g_defaults.dirs[DEFAULT_DIR_PORT], "playlists",
|
||||
sizeof(g_defaults.dirs[DEFAULT_DIR_PLAYLIST]));
|
||||
fill_pathname_join(g_defaults.dirs[DEFAULT_DIR_LOGS],
|
||||
g_defaults.dirs[DEFAULT_DIR_PORT], "logs",
|
||||
sizeof(g_defaults.dirs[DEFAULT_DIR_LOGS]));
|
||||
/* REMAP lives under MENU_CONFIG, so that comes first. */
|
||||
fill_pathname_join(g_defaults.dirs[DEFAULT_DIR_MENU_CONFIG],
|
||||
g_defaults.dirs[DEFAULT_DIR_PORT], "config",
|
||||
sizeof(g_defaults.dirs[DEFAULT_DIR_MENU_CONFIG]));
|
||||
fill_pathname_join(g_defaults.dirs[DEFAULT_DIR_REMAP],
|
||||
g_defaults.dirs[DEFAULT_DIR_MENU_CONFIG], "remaps",
|
||||
sizeof(g_defaults.dirs[DEFAULT_DIR_REMAP]));
|
||||
|
||||
#ifndef IS_SALAMANDER
|
||||
dir_check_defaults("custom.ini");
|
||||
#endif
|
||||
}
|
||||
|
||||
static void frontend_gekko_process_args(int *argc, char *argv[])
|
||||
{
|
||||
#ifndef IS_SALAMANDER
|
||||
/* The core is the one linked into the program the loader ran. */
|
||||
if (*argc >= 1 && argv && argv[0])
|
||||
{
|
||||
const char *last_slash = strrchr(argv[0], '/');
|
||||
if (last_slash && path_is_valid(last_slash + 1))
|
||||
path_set(RARCH_PATH_CORE, last_slash + 1);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
static enum frontend_architecture frontend_gekko_get_arch(void)
|
||||
{
|
||||
return FRONTEND_ARCH_PPC;
|
||||
}
|
||||
|
||||
static int frontend_gekko_parse_drive_list(void *data, bool load_content)
|
||||
{
|
||||
#ifndef IS_SALAMANDER
|
||||
file_list_t *list = (file_list_t*)data;
|
||||
enum msg_hash_enums enum_idx = load_content
|
||||
? MENU_ENUM_LABEL_FILE_DETECT_CORE_LIST_PUSH_DIR
|
||||
: MENU_ENUM_LABEL_FILE_BROWSER_DIRECTORY;
|
||||
unsigned i;
|
||||
for (i = 0; i < VOL_COUNT; i++)
|
||||
{
|
||||
char root[16];
|
||||
if (!volumes[i].mounted)
|
||||
continue;
|
||||
snprintf(root, sizeof(root), "%s:/", volumes[i].name);
|
||||
menu_entries_append(list, root,
|
||||
msg_hash_to_str(MSG_EXTERNAL_APPLICATION_DIR),
|
||||
enum_idx, FILE_TYPE_DIRECTORY, 0, 0, NULL);
|
||||
}
|
||||
#endif
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void frontend_gekko_shutdown(bool unused)
|
||||
{
|
||||
(void)unused;
|
||||
#ifndef IS_SALAMANDER
|
||||
/* exit() goes back to the loader, or the system menu. */
|
||||
exit(0);
|
||||
#endif
|
||||
}
|
||||
|
||||
frontend_ctx_driver_t frontend_ctx_gx = {
|
||||
frontend_gekko_get_env, /* get_env */
|
||||
frontend_gekko_init, /* init */
|
||||
frontend_gekko_deinit, /* deinit */
|
||||
NULL, /* exitspawn */
|
||||
frontend_gekko_process_args, /* process_args */
|
||||
NULL, /* exec */
|
||||
NULL, /* set_fork */
|
||||
frontend_gekko_shutdown, /* shutdown */
|
||||
NULL, /* get_name */
|
||||
NULL, /* get_os */
|
||||
NULL, /* load_content */
|
||||
frontend_gekko_get_arch, /* get_architecture */
|
||||
NULL, /* get_powerstate */
|
||||
frontend_gekko_parse_drive_list, /* parse_drive_list */
|
||||
NULL, /* install_signal_handler */
|
||||
NULL, /* get_sighandler_state */
|
||||
NULL, /* set_sighandler_state */
|
||||
NULL, /* destroy_signal_handler_state */
|
||||
NULL, /* attach_console */
|
||||
NULL, /* detach_console */
|
||||
NULL, /* get_lakka_version */
|
||||
NULL, /* set_screen_brightness */
|
||||
NULL, /* set_sustained_performance_mode */
|
||||
NULL, /* get_cpu_model_name */
|
||||
NULL, /* get_user_language */
|
||||
NULL, /* is_narrator_running */
|
||||
NULL, /* accessibility_speak */
|
||||
NULL, /* set_gamemode */
|
||||
NULL, /* get_display_type */
|
||||
"gx", /* ident */
|
||||
NULL /* get_video_driver */
|
||||
};
|
||||
@@ -0,0 +1,167 @@
|
||||
/* RetroArch - A frontend for libretro.
|
||||
*
|
||||
* RetroArch is free software: you can redistribute it and/or modify it under the terms
|
||||
* of the GNU General Public License as published by the Free Software Found-
|
||||
* ation, either version 3 of the License, or (at your option) any later version.
|
||||
*
|
||||
* RetroArch is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
|
||||
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
|
||||
* PURPOSE. See the GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along with RetroArch.
|
||||
* If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
/* Display server for the GameCube and Wii on os/gekko. The modes the
|
||||
* VI offers are dispserv_gx_modes.c's, shared with the libogc build;
|
||||
* the standard the console is set to comes from the VI and (Wii) the
|
||||
* system settings. The video driver programs the VI: set_resolution
|
||||
* records the choice in current_resolution_id and hands the mode to
|
||||
* it. Only whole modes switch, so a refresh rate change alone fails. */
|
||||
|
||||
#include <stdlib.h>
|
||||
|
||||
#include <gekko/video.h>
|
||||
|
||||
#include "dispserv_gx.h"
|
||||
#include "../video_driver.h"
|
||||
#include "../../retroarch.h"
|
||||
#include "../../verbosity.h"
|
||||
|
||||
/* Nothing to keep; any non-NULL pointer marks the server as up */
|
||||
static char gx_display_server_state;
|
||||
|
||||
void gx_display_server_query(gx_vi_standard_t *std, unsigned *tvmode)
|
||||
{
|
||||
gk_vi_mode_t pref;
|
||||
|
||||
gk_vi_preferred(&pref);
|
||||
std->progressive = pref.scan == GK_VI_PROGRESSIVE;
|
||||
std->max_width = 720;
|
||||
std->max_height = gk_vi_max_lines(pref.std);
|
||||
std->fifty_hz = pref.std == GK_VI_PAL;
|
||||
std->pref_width = pref.fb_width;
|
||||
std->pref_height = pref.fb_lines;
|
||||
|
||||
if (tvmode)
|
||||
*tvmode = pref.std;
|
||||
}
|
||||
|
||||
static unsigned gx_display_server_current_id(void)
|
||||
{
|
||||
global_t *global = global_get_ptr();
|
||||
unsigned id = gx_modes_clamp_id(
|
||||
global->console.screen.resolutions.current.id);
|
||||
/* A config naming a mode that does not exist starts over at
|
||||
* the default, as the driver would run it */
|
||||
global->console.screen.resolutions.current.id = id;
|
||||
return id;
|
||||
}
|
||||
|
||||
static void *gx_display_server_init(void)
|
||||
{
|
||||
return &gx_display_server_state;
|
||||
}
|
||||
|
||||
static void gx_display_server_destroy(void *data) { }
|
||||
|
||||
static void *gx_display_server_get_resolution_list(void *data,
|
||||
unsigned *len)
|
||||
{
|
||||
gx_vi_standard_t std;
|
||||
unsigned count;
|
||||
unsigned current = gx_display_server_current_id();
|
||||
video_display_config_t *list;
|
||||
|
||||
gx_display_server_query(&std, NULL);
|
||||
|
||||
count = gx_modes_list(&std, current, NULL, 0);
|
||||
list = (video_display_config_t*)malloc(count * sizeof(*list));
|
||||
if (!list)
|
||||
{
|
||||
*len = 0;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
*len = gx_modes_list(&std, current, list, count);
|
||||
return list;
|
||||
}
|
||||
|
||||
static bool gx_display_server_set_resolution(void *data,
|
||||
unsigned dims, int int_hz, float hz, int center,
|
||||
int monitor_index, int xoffset, int padjust)
|
||||
{
|
||||
gx_vi_standard_t std;
|
||||
int id;
|
||||
global_t *global = global_get_ptr();
|
||||
|
||||
/* No dims is a refresh rate change on its own, which the VI
|
||||
* cannot make without changing the mode */
|
||||
if (!VIDEO_SCALE_W(dims) || !VIDEO_SCALE_H(dims))
|
||||
return false;
|
||||
|
||||
gx_display_server_query(&std, NULL);
|
||||
|
||||
if ((id = gx_modes_find(&std, dims)) < 0)
|
||||
{
|
||||
RARCH_WARN("[GX] No %ux%u mode on this TV standard.\n",
|
||||
VIDEO_SCALE_W(dims), VIDEO_SCALE_H(dims));
|
||||
return false;
|
||||
}
|
||||
|
||||
global->console.screen.resolutions.current.id = (unsigned)id;
|
||||
return video_driver_set_video_mode(gx_modes_dims((unsigned)id), true);
|
||||
}
|
||||
|
||||
static float gx_display_server_get_refresh_rate(void *data)
|
||||
{
|
||||
gx_vi_standard_t std;
|
||||
gx_vi_mode_t mode;
|
||||
|
||||
gx_display_server_query(&std, NULL);
|
||||
gx_modes_resolve(&std,
|
||||
gx_modes_dims(gx_display_server_current_id()), &mode);
|
||||
return mode.hz;
|
||||
}
|
||||
|
||||
static void gx_display_server_get_video_output_size(void *data,
|
||||
unsigned *dims, char *s, size_t len)
|
||||
{
|
||||
/* 0x0 for the default: the menu shows it as such, and it is what
|
||||
* resetting the setting hands back to the driver */
|
||||
*dims = gx_modes_dims(gx_display_server_current_id());
|
||||
}
|
||||
|
||||
const video_display_server_t dispserv_gx = {
|
||||
gx_display_server_init,
|
||||
gx_display_server_destroy,
|
||||
NULL, /* set_window_opacity */
|
||||
NULL, /* set_window_progress */
|
||||
NULL, /* set_window_decorations */
|
||||
gx_display_server_set_resolution,
|
||||
gx_display_server_get_resolution_list,
|
||||
NULL, /* get_output_options */
|
||||
NULL, /* set_screen_orientation */
|
||||
NULL, /* get_screen_orientation */
|
||||
gx_display_server_get_refresh_rate,
|
||||
gx_display_server_get_video_output_size,
|
||||
NULL, /* get_video_output_prev */
|
||||
NULL, /* get_video_output_next */
|
||||
NULL, /* get_metrics */
|
||||
NULL, /* get_flags */
|
||||
NULL, /* get_scanline */
|
||||
NULL, /* wait_vblank */
|
||||
NULL, /* modeline_list_outputs */
|
||||
NULL, /* modeline_open */
|
||||
NULL, /* modeline_close */
|
||||
NULL, /* modeline_caps */
|
||||
NULL, /* modeline_enum */
|
||||
NULL, /* modeline_add */
|
||||
NULL, /* modeline_update */
|
||||
NULL, /* modeline_delete */
|
||||
NULL, /* modeline_set */
|
||||
NULL, /* modeline_flush */
|
||||
NULL, /* get_edid */
|
||||
NULL, /* idle_wait */
|
||||
"gx"
|
||||
};
|
||||
@@ -0,0 +1,916 @@
|
||||
/* RetroArch - A frontend for libretro.
|
||||
*
|
||||
* RetroArch is free software: you can redistribute it and/or modify it under the terms
|
||||
* of the GNU General Public License as published by the Free Software Found-
|
||||
* ation, either version 3 of the License, or (at your option) any later version.
|
||||
*
|
||||
* RetroArch is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
|
||||
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
|
||||
* PURPOSE. See the GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along with RetroArch.
|
||||
* If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
/* Video on os/gekko. Each frame is a textured quad into the embedded
|
||||
* framebuffer, in pixel coordinates; the menu's surface and overlays
|
||||
* go on top; on-screen text is poked into the EFB; then the EFB is
|
||||
* copied to whichever of two XFBs the video interface is not showing,
|
||||
* and that one is shown from the next field. With vsync, a frame
|
||||
* waits for the retrace that put the previous one up. */
|
||||
|
||||
#include <malloc.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include <gekko/gx.h>
|
||||
#include <gekko/video.h>
|
||||
#ifdef HW_RVL
|
||||
#include <gekko/conf.h>
|
||||
#endif
|
||||
|
||||
#include <libretro.h>
|
||||
#include <retro_miscellaneous.h>
|
||||
#include <encodings/utf.h>
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include "../../config.h"
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_MENU
|
||||
#include "../../menu/menu_driver.h"
|
||||
#endif
|
||||
|
||||
#include "../bitmapfont.h"
|
||||
#include "../display_servers/dispserv_gx.h"
|
||||
#include "../../configuration.h"
|
||||
#include "../../driver.h"
|
||||
#include "../../verbosity.h"
|
||||
|
||||
#define FIFO_BYTES (256 * 1024)
|
||||
#define XFB_BYTES (720 * 576 * 2)
|
||||
#define EFB_LINES 480
|
||||
|
||||
/* RGUI's surface, at most 560 x 240 RGB5A3. */
|
||||
#define MENU_MAX_WIDTH 560
|
||||
#define MENU_MAX_HEIGHT 240
|
||||
|
||||
struct gekko_overlay
|
||||
{
|
||||
gk_gx_tex_t tex;
|
||||
float tex_coord[4]; /* x, y, w, h */
|
||||
float vertex_coord[4]; /* x, y, w, h; 0..1, y down */
|
||||
float alpha;
|
||||
};
|
||||
|
||||
typedef struct gekko_video
|
||||
{
|
||||
video_viewport_t vp;
|
||||
gk_vi_mode_t mode;
|
||||
gk_gx_tex_t frame_tex;
|
||||
gk_gx_tex_t menu_tex;
|
||||
const uint16_t *menu_data;
|
||||
#ifdef HAVE_OVERLAY
|
||||
struct gekko_overlay *overlay;
|
||||
unsigned overlays;
|
||||
#endif
|
||||
uint32_t *tex_data;
|
||||
uint32_t swap_retrace;
|
||||
unsigned tex_size; /* texels a side the buffer holds */
|
||||
unsigned scale;
|
||||
unsigned efb_lines;
|
||||
unsigned tex_w, tex_h; /* what frame_tex describes */
|
||||
unsigned overscan_top, overscan_bottom;
|
||||
unsigned orientation;
|
||||
unsigned cur_xfb;
|
||||
int system_xorigin;
|
||||
bool should_resize;
|
||||
bool double_strike;
|
||||
bool rgb32;
|
||||
bool menu_enable;
|
||||
bool vsync;
|
||||
bool smooth;
|
||||
#ifdef HAVE_OVERLAY
|
||||
bool overlay_enable;
|
||||
bool overlay_full_screen;
|
||||
#endif
|
||||
} gekko_video_t;
|
||||
|
||||
/* The XFBs and the GPU's ring outlive driver instances: the VI keeps
|
||||
* showing an XFB between a free and the next init. */
|
||||
static void *g_xfb[2];
|
||||
static uint16_t g_menu_tiles[MENU_MAX_WIDTH * MENU_MAX_HEIGHT]
|
||||
__attribute__((aligned(32)));
|
||||
static bool g_gx_up;
|
||||
|
||||
/* ---- texels ---- */
|
||||
|
||||
/* 16-bit texels into 4x4 tiles, rows of tiles left to right. */
|
||||
static void tile16(const uint16_t *src, uint16_t *dst, unsigned w,
|
||||
unsigned h, unsigned pitch)
|
||||
{
|
||||
unsigned x, y, r;
|
||||
size_t stride = pitch / 2;
|
||||
for (y = 0; y < h; y += 4)
|
||||
for (x = 0; x < w; x += 4)
|
||||
for (r = 0; r < 4; r++)
|
||||
{
|
||||
const uint32_t *s = (const uint32_t*)(src + (y + r) * stride + x);
|
||||
uint32_t *d = (uint32_t*)dst;
|
||||
d[0] = s[0];
|
||||
d[1] = s[1];
|
||||
dst += 4;
|
||||
}
|
||||
}
|
||||
|
||||
/* XRGB8888 into RGBA8 tiles: per 4x4 tile, the 16 AR pairs then the
|
||||
* 16 GB pairs. */
|
||||
static void tile32(const uint32_t *src, uint16_t *dst, unsigned w,
|
||||
unsigned h, unsigned pitch)
|
||||
{
|
||||
unsigned x, y, r, c;
|
||||
size_t stride = pitch / 4;
|
||||
for (y = 0; y < h; y += 4)
|
||||
for (x = 0; x < w; x += 4)
|
||||
{
|
||||
for (r = 0; r < 4; r++)
|
||||
{
|
||||
const uint32_t *s = src + (y + r) * stride + x;
|
||||
for (c = 0; c < 4; c++)
|
||||
{
|
||||
uint32_t p = s[c];
|
||||
dst[r * 4 + c] = (uint16_t)(0xff00 | ((p >> 16) & 0xff));
|
||||
dst[16 + r * 4 + c] = (uint16_t)p;
|
||||
}
|
||||
}
|
||||
dst += 32;
|
||||
}
|
||||
}
|
||||
|
||||
/* ---- the screen ---- */
|
||||
|
||||
static void wait_swap(gekko_video_t *gx)
|
||||
{
|
||||
/* The XFB shown before the last swap is free once a retrace has
|
||||
* put the new one up. */
|
||||
while (gk_vi_retraces() == gx->swap_retrace)
|
||||
gk_vi_wait_retrace();
|
||||
}
|
||||
|
||||
static void gekko_set_video_mode(void *data, unsigned dims, bool fullscreen)
|
||||
{
|
||||
static const uint8_t vfilter_soft[7] = { 8, 8, 10, 12, 10, 8, 8 };
|
||||
gx_vi_standard_t std;
|
||||
gx_vi_mode_t vi;
|
||||
unsigned tvmode, lines, vi_width, menu_w, menu_h;
|
||||
int x;
|
||||
float hz;
|
||||
settings_t *settings = config_get_ptr();
|
||||
gekko_video_t *gx = (gekko_video_t*)data;
|
||||
|
||||
if (!gx || !settings)
|
||||
return;
|
||||
|
||||
gx_display_server_query(&std, &tvmode);
|
||||
gx_modes_resolve(&std, dims, &vi);
|
||||
|
||||
lines = vi.lines;
|
||||
if (vi.double_strike && lines > std.max_height / 2)
|
||||
lines = std.max_height / 2;
|
||||
else if (!vi.double_strike && lines > std.max_height)
|
||||
lines = std.max_height;
|
||||
|
||||
vi_width = settings->uints.video_viwidth;
|
||||
if (vi_width < 640 || vi_width > 720)
|
||||
vi_width = 640;
|
||||
|
||||
memset(&gx->mode, 0, sizeof(gx->mode));
|
||||
gx->mode.std = (uint8_t)tvmode;
|
||||
gx->mode.scan = vi.double_strike ? GK_VI_DOUBLE_STRIKE
|
||||
: vi.interlaced ? GK_VI_INTERLACED : GK_VI_PROGRESSIVE;
|
||||
gx->mode.fb_width = (uint16_t)((vi.width + 15) & ~15u);
|
||||
gx->mode.fb_lines = (uint16_t)lines;
|
||||
gx->mode.width = (uint16_t)vi_width;
|
||||
gx->mode.lines = (uint16_t)lines;
|
||||
/* Centred in the 720-pixel line, moved by the console's offset as
|
||||
* far as the line allows. */
|
||||
x = (int)(720 - vi_width) / 2 + gx->system_xorigin;
|
||||
if (x < 0)
|
||||
x = 0;
|
||||
if (x + (int)vi_width > 720)
|
||||
x = (int)(720 - vi_width);
|
||||
gx->mode.x = (int16_t)x;
|
||||
gx->mode.y = -1;
|
||||
|
||||
gx->efb_lines = MIN(lines, EFB_LINES);
|
||||
gx->double_strike = vi.double_strike;
|
||||
|
||||
gk_vi_set_black(1);
|
||||
if (gk_vi_configure(&gx->mode))
|
||||
{
|
||||
RARCH_ERR("[Gekko] The VI cannot show %ux%u.\n",
|
||||
gx->mode.fb_width, gx->mode.lines);
|
||||
gk_vi_preferred(&gx->mode);
|
||||
gk_vi_configure(&gx->mode);
|
||||
gx->efb_lines = MIN(gx->mode.lines, EFB_LINES);
|
||||
gx->double_strike = false;
|
||||
}
|
||||
gk_vi_clear_fb(g_xfb[0], &gx->mode, GK_YUYV_BLACK);
|
||||
gk_vi_clear_fb(g_xfb[1], &gx->mode, GK_YUYV_BLACK);
|
||||
gk_vi_set_fb(g_xfb[0]);
|
||||
gx->cur_xfb = 0;
|
||||
gx->swap_retrace = gk_vi_retraces() - 1;
|
||||
gk_vi_set_black(0);
|
||||
|
||||
/* Interlaced output can take the soft vertical filter; a field on
|
||||
* its own has nothing to blend with. */
|
||||
gk_gx_copy_filter(!vi.double_strike && settings->bools.video_vfilter
|
||||
? vfilter_soft : NULL);
|
||||
|
||||
gx->vp.full_dims = VIDEO_SCALE_PACK(gx->mode.fb_width, gx->efb_lines);
|
||||
gx->should_resize = true;
|
||||
|
||||
/* The menu's surface: as tall as the EFB allows up to RGUI's 240,
|
||||
* as wide as its aspect ratio asks, in whole tiles. */
|
||||
menu_h = (gx->efb_lines / (gx->double_strike ? 1 : 2)) & ~3u;
|
||||
if (menu_h > MENU_MAX_HEIGHT)
|
||||
menu_h = MENU_MAX_HEIGHT;
|
||||
switch (settings->uints.menu_rgui_aspect_ratio)
|
||||
{
|
||||
case RGUI_ASPECT_RATIO_16_9:
|
||||
case RGUI_ASPECT_RATIO_16_9_CENTRE:
|
||||
menu_w = menu_h == 240 ? 424
|
||||
: (unsigned)((16.0f / 9.0f) * (float)menu_h) & ~3u;
|
||||
break;
|
||||
case RGUI_ASPECT_RATIO_16_10:
|
||||
case RGUI_ASPECT_RATIO_16_10_CENTRE:
|
||||
menu_w = menu_h == 240 ? 384
|
||||
: (unsigned)((16.0f / 10.0f) * (float)menu_h) & ~3u;
|
||||
break;
|
||||
case RGUI_ASPECT_RATIO_21_9:
|
||||
case RGUI_ASPECT_RATIO_21_9_CENTRE:
|
||||
menu_w = menu_h == 240 ? 560
|
||||
: (unsigned)((21.0f / 9.0f) * (float)menu_h) & ~3u;
|
||||
break;
|
||||
default:
|
||||
menu_w = menu_h == 240 ? 320
|
||||
: (unsigned)((4.0f / 3.0f) * (float)menu_h) & ~3u;
|
||||
break;
|
||||
}
|
||||
if (menu_w > MENU_MAX_WIDTH)
|
||||
menu_w = MENU_MAX_WIDTH;
|
||||
{
|
||||
gfx_display_t *p_disp = disp_get_ptr();
|
||||
p_disp->framebuf_dims = VIDEO_SCALE_PACK(menu_w, menu_h);
|
||||
p_disp->framebuf_pitch = menu_w * 2;
|
||||
}
|
||||
|
||||
RARCH_LOG("[Gekko] Resolution: %ux%u (%s).\n", gx->mode.fb_width,
|
||||
gx->mode.lines, gx->mode.scan == GK_VI_INTERLACED ? "interlaced"
|
||||
: gx->mode.scan == GK_VI_DOUBLE_STRIKE ? "double strike"
|
||||
: "progressive");
|
||||
|
||||
hz = vi.hz;
|
||||
driver_ctl(RARCH_DRIVER_CTL_SET_REFRESH_RATE, &hz);
|
||||
}
|
||||
|
||||
static void setup_gx(gekko_video_t *gx)
|
||||
{
|
||||
gk_mtx44 p;
|
||||
gk_mtx34 m;
|
||||
unsigned w = gx->mode.fb_width, h = gx->efb_lines;
|
||||
|
||||
gk_mtx_ortho(p, 0, (float)h, 0, (float)w, 0, 1);
|
||||
gk_gx_projection(p, 1);
|
||||
gk_mtx_identity(m);
|
||||
gk_gx_load_pos_mtx(m, 0);
|
||||
gk_gx_current_mtx(0);
|
||||
gk_gx_viewport(0, 0, (float)w, (float)h, 0, 1, 0);
|
||||
gk_gx_scissor(0, 0, w, h);
|
||||
gk_gx_cull(GK_GX_CULL_NONE);
|
||||
gk_gx_clip(0);
|
||||
gk_gx_z_mode(0, GK_GX_ALWAYS, 0);
|
||||
gk_gx_color_update(1);
|
||||
gk_gx_alpha_update(0);
|
||||
gk_gx_blend(1, GK_GX_BL_SRCALPHA, GK_GX_BL_INVSRCALPHA);
|
||||
|
||||
gk_gx_vtx_clear();
|
||||
gk_gx_vtx_desc(GK_GX_POS, GK_GX_DIRECT);
|
||||
gk_gx_vtx_desc(GK_GX_CLR0, GK_GX_DIRECT);
|
||||
gk_gx_vtx_desc(GK_GX_TEX0, GK_GX_DIRECT);
|
||||
gk_gx_vtx_fmt(0, GK_GX_POS, 3, GK_GX_F32, 0);
|
||||
gk_gx_vtx_fmt(0, GK_GX_CLR0, 4, GK_GX_RGBA8, 0);
|
||||
gk_gx_vtx_fmt(0, GK_GX_TEX0, 2, GK_GX_F32, 0);
|
||||
gk_gx_num_texgens(1);
|
||||
gk_gx_num_stages(1);
|
||||
gk_gx_tev_order(0, 0, 0);
|
||||
gk_gx_tev(0, GK_GX_MODULATE);
|
||||
gk_gx_copy_clear(0x000000ffu, 0xffffff);
|
||||
}
|
||||
|
||||
static void viewport_resize(gekko_video_t *gx, unsigned overscan_top,
|
||||
unsigned overscan_bottom)
|
||||
{
|
||||
const global_t *global = global_get_ptr();
|
||||
unsigned width = VIDEO_SCALE_W(gx->vp.full_dims);
|
||||
unsigned height = VIDEO_SCALE_H(gx->vp.full_dims);
|
||||
int x = 0, y = 0;
|
||||
int gamma = global->console.screen.gamma_correction;
|
||||
|
||||
gk_gx_copy_gamma((unsigned)MAX(0, MIN(2, gamma)));
|
||||
|
||||
/* Below 192 lines is a custom mode: as it is. */
|
||||
if (gx->efb_lines >= 192)
|
||||
{
|
||||
float device_aspect = 4.0f / 3.0f;
|
||||
float desired_aspect = video_driver_get_aspect_ratio();
|
||||
#ifdef HW_RVL
|
||||
if (gk_conf_wide() > 0)
|
||||
device_aspect = 16.0f / 9.0f;
|
||||
#endif
|
||||
if (desired_aspect == 0.0f)
|
||||
desired_aspect = 1.0f;
|
||||
if ( gx->orientation == ORIENTATION_VERTICAL
|
||||
|| gx->orientation == ORIENTATION_FLIPPED_ROTATED)
|
||||
desired_aspect = 1.0f / desired_aspect;
|
||||
video_viewport_get_scaled_aspect2(&gx->vp, gx->vp.full_dims, true,
|
||||
device_aspect, desired_aspect);
|
||||
x = VIDEO_POS_X(gx->vp.pos);
|
||||
y = VIDEO_POS_Y(gx->vp.pos);
|
||||
width = VIDEO_SCALE_W(gx->vp.dims);
|
||||
height = VIDEO_SCALE_H(gx->vp.dims);
|
||||
}
|
||||
|
||||
if (overscan_top || overscan_bottom)
|
||||
{
|
||||
float aspect = (float)width / (float)height;
|
||||
int new_height = (int)height - (int)(overscan_top + overscan_bottom);
|
||||
int new_width = (int)((float)new_height * aspect + 0.5f);
|
||||
if (new_height > 0 && new_width > 0)
|
||||
{
|
||||
x += (int)((float)((int)width - new_width) * 0.5f);
|
||||
y += (int)overscan_top;
|
||||
width = (unsigned)new_width;
|
||||
height = (unsigned)new_height;
|
||||
}
|
||||
}
|
||||
|
||||
gx->vp.pos = VIDEO_POS_PACK(x, y);
|
||||
gx->vp.dims = VIDEO_SCALE_PACK(width, height);
|
||||
gx->should_resize = false;
|
||||
}
|
||||
|
||||
/* A textured quad from (x, y), corners' texture coordinates turned
|
||||
* for the orientation. */
|
||||
static void quad(float x, float y, float w, float h, const float *u,
|
||||
const float *v, uint32_t rgba)
|
||||
{
|
||||
gk_gx_begin(GK_GX_QUADS, 0, 4);
|
||||
gk_gx_f32(x); gk_gx_f32(y); gk_gx_f32(0);
|
||||
gk_gx_u32(rgba); gk_gx_f32(u[0]); gk_gx_f32(v[0]);
|
||||
gk_gx_f32(x + w); gk_gx_f32(y); gk_gx_f32(0);
|
||||
gk_gx_u32(rgba); gk_gx_f32(u[1]); gk_gx_f32(v[1]);
|
||||
gk_gx_f32(x + w); gk_gx_f32(y + h); gk_gx_f32(0);
|
||||
gk_gx_u32(rgba); gk_gx_f32(u[2]); gk_gx_f32(v[2]);
|
||||
gk_gx_f32(x); gk_gx_f32(y + h); gk_gx_f32(0);
|
||||
gk_gx_u32(rgba); gk_gx_f32(u[3]); gk_gx_f32(v[3]);
|
||||
}
|
||||
|
||||
static void draw_frame(gekko_video_t *gx)
|
||||
{
|
||||
/* Corners top-left, top-right, bottom-right, bottom-left; each
|
||||
* quarter turn moves the texture one corner on. */
|
||||
static const float cu[4] = { 0, 1, 1, 0 }, cv[4] = { 0, 0, 1, 1 };
|
||||
float u[4], v[4];
|
||||
unsigned i, turn;
|
||||
switch (gx->orientation)
|
||||
{
|
||||
case ORIENTATION_VERTICAL: turn = 1; break;
|
||||
case ORIENTATION_FLIPPED: turn = 2; break;
|
||||
case ORIENTATION_FLIPPED_ROTATED: turn = 3; break;
|
||||
default: turn = 0; break;
|
||||
}
|
||||
for (i = 0; i < 4; i++)
|
||||
{
|
||||
u[i] = cu[(i + turn) & 3];
|
||||
v[i] = cv[(i + turn) & 3];
|
||||
}
|
||||
gk_gx_tex_load(&gx->frame_tex, 0, 0);
|
||||
quad((float)VIDEO_POS_X(gx->vp.pos), (float)VIDEO_POS_Y(gx->vp.pos),
|
||||
(float)VIDEO_SCALE_W(gx->vp.dims), (float)VIDEO_SCALE_H(gx->vp.dims),
|
||||
u, v, 0xffffffffu);
|
||||
}
|
||||
|
||||
static void draw_menu(gekko_video_t *gx)
|
||||
{
|
||||
static const float u[4] = { 0, 1, 1, 0 }, v[4] = { 0, 0, 1, 1 };
|
||||
gfx_display_t *p_disp = disp_get_ptr();
|
||||
unsigned w = VIDEO_SCALE_W(p_disp->framebuf_dims) & ~3u;
|
||||
unsigned h = VIDEO_SCALE_H(p_disp->framebuf_dims) & ~3u;
|
||||
if (!w || !h)
|
||||
return;
|
||||
tile16(gx->menu_data, g_menu_tiles, w, h, (unsigned)p_disp->framebuf_pitch);
|
||||
gk_dcache_flush(g_menu_tiles, (size_t)w * h * 2);
|
||||
gk_gx_invalidate_tex();
|
||||
gk_gx_tex_init(&gx->menu_tex, g_menu_tiles, w, h, GK_GX_TF_RGB5A3,
|
||||
GK_GX_CLAMP, GK_GX_CLAMP);
|
||||
gk_gx_tex_filter(&gx->menu_tex, gx->smooth ? GK_GX_LINEAR : GK_GX_NEAR,
|
||||
gx->smooth ? GK_GX_LINEAR : GK_GX_NEAR);
|
||||
gk_gx_tex_load(&gx->menu_tex, 0, 0);
|
||||
quad(0, 0, (float)gx->mode.fb_width, (float)gx->efb_lines, u, v,
|
||||
0xffffffffu);
|
||||
}
|
||||
|
||||
#ifdef HAVE_OVERLAY
|
||||
static void draw_overlays(gekko_video_t *gx)
|
||||
{
|
||||
unsigned i;
|
||||
float bx = 0, by = 0;
|
||||
float bw = (float)gx->mode.fb_width, bh = (float)gx->efb_lines;
|
||||
if (!gx->overlay_full_screen)
|
||||
{
|
||||
bx = (float)VIDEO_POS_X(gx->vp.pos);
|
||||
by = (float)VIDEO_POS_Y(gx->vp.pos);
|
||||
bw = (float)VIDEO_SCALE_W(gx->vp.dims);
|
||||
bh = (float)VIDEO_SCALE_H(gx->vp.dims);
|
||||
}
|
||||
for (i = 0; i < gx->overlays; i++)
|
||||
{
|
||||
const struct gekko_overlay *o = &gx->overlay[i];
|
||||
float u[4], v[4];
|
||||
uint32_t a = (uint32_t)VIDEO_ALPHA_BYTE(o->alpha);
|
||||
u[0] = u[3] = o->tex_coord[0];
|
||||
u[1] = u[2] = o->tex_coord[0] + o->tex_coord[2];
|
||||
v[0] = v[1] = o->tex_coord[1];
|
||||
v[2] = v[3] = o->tex_coord[1] + o->tex_coord[3];
|
||||
gk_gx_tex_load(&o->tex, 0, 0);
|
||||
quad(bx + o->vertex_coord[0] * bw, by + o->vertex_coord[1] * bh,
|
||||
o->vertex_coord[2] * bw, o->vertex_coord[3] * bh, u, v,
|
||||
0xffffff00u | a);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
/* Text straight into the EFB, after the GPU is done with it: the
|
||||
* bitmap font on black, twice as tall on interlaced output and twice
|
||||
* as wide on wide framebuffers. */
|
||||
static void blit_line(gekko_video_t *gx, unsigned x, unsigned y,
|
||||
const char *msg)
|
||||
{
|
||||
const unsigned sx = gx->mode.fb_width > 400 ? 2 : 1;
|
||||
const unsigned sy = gx->double_strike ? 1 : 2;
|
||||
unsigned i, j, a, b;
|
||||
|
||||
for (j = 0; j < FONT_HEIGHT * sy; j++)
|
||||
for (a = 0; a < sx; a++)
|
||||
gk_gx_poke(x + a, y + j, 0x000000ffu);
|
||||
x += sx;
|
||||
|
||||
while (*msg)
|
||||
{
|
||||
uint32_t code = utf8_walk(&msg);
|
||||
/* The font's 256 codepoints are extended ASCII, with the 'oe'
|
||||
* ligatures where Windows-1252 puts them. */
|
||||
if (code == 339)
|
||||
code = 156;
|
||||
else if (code == 338)
|
||||
code = 140;
|
||||
else if (code > 255)
|
||||
code = '?';
|
||||
if (x + FONT_WIDTH_STRIDE * sx >= gx->mode.fb_width)
|
||||
break;
|
||||
for (j = 0; j < FONT_HEIGHT; j++)
|
||||
for (i = 0; i < FONT_WIDTH_STRIDE; i++)
|
||||
{
|
||||
unsigned bit = i + j * FONT_WIDTH;
|
||||
int on = i < FONT_WIDTH && (bitmap_bin[FONT_OFFSET(code)
|
||||
+ (bit >> 3)] & (1u << (bit & 7)));
|
||||
uint32_t c = on ? 0xffffffffu : 0x000000ffu;
|
||||
for (a = 0; a < sx; a++)
|
||||
for (b = 0; b < sy; b++)
|
||||
gk_gx_poke(x + i * sx + a, y + j * sy + b, c);
|
||||
}
|
||||
x += FONT_WIDTH_STRIDE * sx;
|
||||
}
|
||||
}
|
||||
|
||||
/* ---- the driver ---- */
|
||||
|
||||
static void *gekko_init(const video_info_t *video, input_driver_t **input,
|
||||
void **input_data)
|
||||
{
|
||||
void *gxinput;
|
||||
settings_t *settings = config_get_ptr();
|
||||
global_t *global = global_get_ptr();
|
||||
gekko_video_t *gx = (gekko_video_t*)calloc(1, sizeof(*gx));
|
||||
unsigned id;
|
||||
|
||||
if (!gx)
|
||||
return NULL;
|
||||
|
||||
gxinput = input_driver_init_wrap(&input_gx,
|
||||
settings->arrays.input_joypad_driver);
|
||||
*input = gxinput ? &input_gx : NULL;
|
||||
*input_data = gxinput;
|
||||
|
||||
if (!g_xfb[0])
|
||||
{
|
||||
g_xfb[0] = gk_arena_take_top(&gk_mem1, XFB_BYTES, 32);
|
||||
g_xfb[1] = gk_arena_take_top(&gk_mem1, XFB_BYTES, 32);
|
||||
}
|
||||
if (!g_gx_up)
|
||||
{
|
||||
if (!g_xfb[0] || !g_xfb[1] || gk_gx_init(FIFO_BYTES))
|
||||
{
|
||||
RARCH_ERR("[Gekko] No memory for the framebuffers.\n");
|
||||
free(gx);
|
||||
return NULL;
|
||||
}
|
||||
g_gx_up = true;
|
||||
}
|
||||
|
||||
#ifdef HW_RVL
|
||||
gx->system_xorigin = gk_conf_display_offset_h();
|
||||
#endif
|
||||
gx->vsync = video->vsync;
|
||||
gx->smooth = settings->bools.video_smooth;
|
||||
gx->rgb32 = video->rgb32;
|
||||
gx->scale = video->input_scale;
|
||||
gx->orientation = ORIENTATION_NORMAL;
|
||||
gx->tex_size = RARCH_SCALE_BASE * video->input_scale;
|
||||
gx->tex_data = (uint32_t*)memalign(32, (size_t)gx->tex_size
|
||||
* gx->tex_size * (gx->rgb32 ? 4 : 2));
|
||||
if (!gx->tex_data)
|
||||
{
|
||||
RARCH_ERR("[Gekko] No memory for the frame texture.\n");
|
||||
free(gx);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
id = gx_modes_clamp_id(global->console.screen.resolutions.current.id);
|
||||
global->console.screen.resolutions.current.id = id;
|
||||
gekko_set_video_mode(gx, gx_modes_dims(id), true);
|
||||
setup_gx(gx);
|
||||
return gx;
|
||||
}
|
||||
|
||||
static bool gekko_frame(void *data, const void *frame, unsigned dims,
|
||||
uint64_t frame_count, unsigned pitch, const char *msg,
|
||||
video_frame_info_t *video_info)
|
||||
{
|
||||
gekko_video_t *gx = (gekko_video_t*)data;
|
||||
unsigned width = VIDEO_SCALE_W(dims) & ~3u;
|
||||
unsigned height = VIDEO_SCALE_H(dims) & ~3u;
|
||||
#ifdef HAVE_MENU
|
||||
bool menu_is_alive = (video_info->menu_st_flags & MENU_ST_FLAG_ALIVE)
|
||||
? true : false;
|
||||
#endif
|
||||
|
||||
if (!gx || (!frame && !gx->menu_enable))
|
||||
return true;
|
||||
|
||||
if ( gx->overscan_top != video_info->overscan_correction_top
|
||||
|| gx->overscan_bottom != video_info->overscan_correction_bottom)
|
||||
{
|
||||
gx->overscan_top = video_info->overscan_correction_top;
|
||||
gx->overscan_bottom = video_info->overscan_correction_bottom;
|
||||
gx->should_resize = true;
|
||||
}
|
||||
if (gx->smooth != video_info->video_smooth)
|
||||
{
|
||||
gx->smooth = video_info->video_smooth;
|
||||
gx->tex_w = 0;
|
||||
}
|
||||
if (gx->should_resize)
|
||||
{
|
||||
setup_gx(gx);
|
||||
viewport_resize(gx, gx->overscan_top, gx->overscan_bottom);
|
||||
}
|
||||
|
||||
if (gx->vsync || gx->menu_enable)
|
||||
wait_swap(gx);
|
||||
|
||||
if (frame && width && height)
|
||||
{
|
||||
width = MIN(width, gx->tex_size);
|
||||
height = MIN(height, gx->tex_size);
|
||||
if (gx->rgb32)
|
||||
tile32((const uint32_t*)frame, (uint16_t*)gx->tex_data, width,
|
||||
height, pitch);
|
||||
else
|
||||
tile16((const uint16_t*)frame, (uint16_t*)gx->tex_data, width,
|
||||
height, pitch);
|
||||
gk_dcache_flush(gx->tex_data, (size_t)width * height
|
||||
* (gx->rgb32 ? 4 : 2));
|
||||
gk_gx_invalidate_tex();
|
||||
if (width != gx->tex_w || height != gx->tex_h)
|
||||
{
|
||||
gk_gx_tex_init(&gx->frame_tex, gx->tex_data, width, height,
|
||||
gx->rgb32 ? GK_GX_TF_RGBA8 : GK_GX_TF_RGB565,
|
||||
GK_GX_CLAMP, GK_GX_CLAMP);
|
||||
gk_gx_tex_filter(&gx->frame_tex,
|
||||
gx->smooth ? GK_GX_LINEAR : GK_GX_NEAR,
|
||||
gx->smooth ? GK_GX_LINEAR : GK_GX_NEAR);
|
||||
gx->tex_w = width;
|
||||
gx->tex_h = height;
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef HAVE_MENU
|
||||
menu_driver_frame(menu_is_alive, video_info);
|
||||
#endif
|
||||
|
||||
if (frame && gx->tex_w)
|
||||
draw_frame(gx);
|
||||
if (gx->menu_enable && gx->menu_data)
|
||||
draw_menu(gx);
|
||||
#ifdef HAVE_OVERLAY
|
||||
if (gx->overlay_enable && gx->overlay)
|
||||
draw_overlays(gx);
|
||||
#endif
|
||||
|
||||
gk_gx_draw_done();
|
||||
if (msg && *msg && !gx->menu_enable)
|
||||
blit_line(gx, 7 * (gx->double_strike ? 1 : 2),
|
||||
gx->efb_lines - 35 * (gx->double_strike ? 1 : 2), msg);
|
||||
|
||||
gx->cur_xfb ^= 1;
|
||||
gk_gx_copy_xfb(g_xfb[gx->cur_xfb], gx->mode.fb_width, 0, 0,
|
||||
gx->mode.fb_width, gx->efb_lines, gx->mode.fb_lines, 1);
|
||||
gk_gx_draw_done();
|
||||
gk_vi_set_fb(g_xfb[gx->cur_xfb]);
|
||||
gx->swap_retrace = gk_vi_retraces();
|
||||
return true;
|
||||
}
|
||||
|
||||
static void gekko_set_nonblock_state(void *data, bool state,
|
||||
bool adaptive_vsync_enabled, unsigned swap_interval)
|
||||
{
|
||||
gekko_video_t *gx = (gekko_video_t*)data;
|
||||
if (gx)
|
||||
gx->vsync = !state;
|
||||
}
|
||||
|
||||
static bool gekko_alive(void *data) { return true; }
|
||||
static bool gekko_focus(void *data) { return true; }
|
||||
static bool gekko_suppress_screensaver(void *data, bool enable) { return false; }
|
||||
|
||||
static void gekko_set_rotation(void *data, unsigned orientation)
|
||||
{
|
||||
gekko_video_t *gx = (gekko_video_t*)data;
|
||||
if (gx)
|
||||
{
|
||||
gx->orientation = orientation;
|
||||
gx->should_resize = true;
|
||||
}
|
||||
}
|
||||
|
||||
static void gekko_viewport_info(void *data, struct video_viewport *vp)
|
||||
{
|
||||
gekko_video_t *gx = (gekko_video_t*)data;
|
||||
if (gx)
|
||||
*vp = gx->vp;
|
||||
}
|
||||
|
||||
static void gekko_free(void *data)
|
||||
{
|
||||
gekko_video_t *gx = (gekko_video_t*)data;
|
||||
if (!gx)
|
||||
return;
|
||||
gk_gx_draw_done();
|
||||
#ifdef HAVE_OVERLAY
|
||||
free(gx->overlay);
|
||||
#endif
|
||||
/* The VI goes on showing a black XFB, so whatever comes next (a
|
||||
* crash screen, say) is not hidden behind a blanked display. */
|
||||
gk_vi_clear_fb(g_xfb[gx->cur_xfb], &gx->mode, GK_YUYV_BLACK);
|
||||
free(gx->tex_data);
|
||||
free(gx);
|
||||
}
|
||||
|
||||
static bool gekko_set_shader(void *data, enum rarch_shader_type type,
|
||||
const char *path)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
/* ---- poke ---- */
|
||||
|
||||
static uint32_t gekko_get_flags(void *data)
|
||||
{
|
||||
uint32_t flags = 0;
|
||||
BIT32_SET(flags, GFX_CTX_FLAGS_SCREENSHOTS_SUPPORTED);
|
||||
return flags;
|
||||
}
|
||||
|
||||
static void gekko_set_aspect_ratio(void *data, unsigned aspect_ratio_idx)
|
||||
{
|
||||
gekko_video_t *gx = (gekko_video_t*)data;
|
||||
if (gx)
|
||||
gx->should_resize = true;
|
||||
}
|
||||
|
||||
static void gekko_apply_state_changes(void *data)
|
||||
{
|
||||
gekko_video_t *gx = (gekko_video_t*)data;
|
||||
if (gx)
|
||||
gx->should_resize = true;
|
||||
}
|
||||
|
||||
static void gekko_set_texture_frame(void *data, const void *frame,
|
||||
bool rgb32, unsigned dims, float alpha)
|
||||
{
|
||||
gekko_video_t *gx = (gekko_video_t*)data;
|
||||
if (gx)
|
||||
gx->menu_data = (const uint16_t*)frame;
|
||||
}
|
||||
|
||||
static void gekko_set_texture_enable(void *data, bool enable,
|
||||
bool full_screen)
|
||||
{
|
||||
gekko_video_t *gx = (gekko_video_t*)data;
|
||||
if (gx)
|
||||
gx->menu_enable = enable;
|
||||
}
|
||||
|
||||
static void gekko_set_video_mode_poke(void *data, unsigned dims,
|
||||
bool fullscreen)
|
||||
{
|
||||
gekko_video_t *gx = (gekko_video_t*)data;
|
||||
if (!gx)
|
||||
return;
|
||||
gk_gx_draw_done();
|
||||
gekko_set_video_mode(gx, dims, fullscreen);
|
||||
setup_gx(gx);
|
||||
}
|
||||
|
||||
static const video_poke_interface_t gekko_poke_interface = {
|
||||
gekko_get_flags,
|
||||
NULL, /* load_texture */
|
||||
NULL, /* unload_texture */
|
||||
gekko_set_video_mode_poke,
|
||||
NULL, /* get_refresh_rate */
|
||||
NULL, /* set_filtering */
|
||||
NULL, /* get_video_output_size: the display server */
|
||||
NULL, /* get_video_output_prev */
|
||||
NULL, /* get_video_output_next */
|
||||
NULL, /* get_current_framebuffer */
|
||||
NULL, /* get_proc_address */
|
||||
gekko_set_aspect_ratio,
|
||||
gekko_apply_state_changes,
|
||||
gekko_set_texture_frame,
|
||||
gekko_set_texture_enable,
|
||||
NULL, /* set_osd_msg */
|
||||
NULL, /* show_mouse */
|
||||
NULL, /* grab_mouse_toggle */
|
||||
NULL, /* get_current_shader */
|
||||
NULL, /* get_current_software_framebuffer */
|
||||
NULL, /* get_hw_render_interface */
|
||||
NULL, /* set_hdr_menu_nits */
|
||||
NULL, /* set_hdr_paper_white_nits */
|
||||
NULL, /* set_hdr_expand_gamut */
|
||||
NULL, /* set_hdr_scanlines */
|
||||
NULL /* set_hdr_subpixel_layout */
|
||||
};
|
||||
|
||||
static void gekko_get_poke_interface(void *data,
|
||||
const video_poke_interface_t **iface)
|
||||
{
|
||||
*iface = &gekko_poke_interface;
|
||||
}
|
||||
|
||||
/* ---- overlays ---- */
|
||||
|
||||
#ifdef HAVE_OVERLAY
|
||||
static struct gekko_overlay *overlay_at(void *data, unsigned image)
|
||||
{
|
||||
gekko_video_t *gx = (gekko_video_t*)data;
|
||||
if (gx && gx->overlay && image < gx->overlays)
|
||||
return &gx->overlay[image];
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static void gekko_overlay_tex_geom(void *data, unsigned image,
|
||||
float x, float y, float w, float h)
|
||||
{
|
||||
struct gekko_overlay *o = overlay_at(data, image);
|
||||
if (o)
|
||||
{
|
||||
o->tex_coord[0] = x;
|
||||
o->tex_coord[1] = y;
|
||||
o->tex_coord[2] = w;
|
||||
o->tex_coord[3] = h;
|
||||
}
|
||||
}
|
||||
|
||||
static void gekko_overlay_vertex_geom(void *data, unsigned image,
|
||||
float x, float y, float w, float h)
|
||||
{
|
||||
struct gekko_overlay *o = overlay_at(data, image);
|
||||
if (o)
|
||||
{
|
||||
o->vertex_coord[0] = x;
|
||||
o->vertex_coord[1] = y;
|
||||
o->vertex_coord[2] = w;
|
||||
o->vertex_coord[3] = h;
|
||||
}
|
||||
}
|
||||
|
||||
static bool gekko_overlay_load(void *data, const void *image_data,
|
||||
unsigned num_images)
|
||||
{
|
||||
unsigned i;
|
||||
gekko_video_t *gx = (gekko_video_t*)data;
|
||||
/* The image loader has already tiled these as RGBA8. */
|
||||
const struct texture_image *images =
|
||||
(const struct texture_image*)image_data;
|
||||
if (!gx)
|
||||
return false;
|
||||
|
||||
free(gx->overlay);
|
||||
gx->overlays = 0;
|
||||
if (!(gx->overlay = (struct gekko_overlay*)calloc(num_images,
|
||||
sizeof(*gx->overlay))))
|
||||
return false;
|
||||
gx->overlays = num_images;
|
||||
|
||||
for (i = 0; i < num_images; i++)
|
||||
{
|
||||
struct gekko_overlay *o = &gx->overlay[i];
|
||||
gk_dcache_flush(images[i].pixels,
|
||||
(size_t)images[i].width * images[i].height * 4);
|
||||
gk_gx_tex_init(&o->tex, images[i].pixels, images[i].width,
|
||||
images[i].height, GK_GX_TF_RGBA8, GK_GX_CLAMP, GK_GX_CLAMP);
|
||||
gk_gx_tex_filter(&o->tex, GK_GX_LINEAR, GK_GX_LINEAR);
|
||||
gekko_overlay_tex_geom(gx, i, 0, 0, 1, 1);
|
||||
gekko_overlay_vertex_geom(gx, i, 0, 0, 1, 1);
|
||||
o->alpha = 1.0f;
|
||||
}
|
||||
gk_gx_invalidate_tex();
|
||||
return true;
|
||||
}
|
||||
|
||||
static void gekko_overlay_enable(void *data, bool state)
|
||||
{
|
||||
gekko_video_t *gx = (gekko_video_t*)data;
|
||||
if (gx)
|
||||
gx->overlay_enable = state;
|
||||
}
|
||||
|
||||
static void gekko_overlay_full_screen(void *data, bool enable)
|
||||
{
|
||||
gekko_video_t *gx = (gekko_video_t*)data;
|
||||
if (gx)
|
||||
gx->overlay_full_screen = enable;
|
||||
}
|
||||
|
||||
static void gekko_overlay_set_alpha(void *data, unsigned image, float mod)
|
||||
{
|
||||
struct gekko_overlay *o = overlay_at(data, image);
|
||||
if (o)
|
||||
o->alpha = mod;
|
||||
}
|
||||
|
||||
static const video_overlay_interface_t gekko_overlay_interface = {
|
||||
gekko_overlay_enable,
|
||||
gekko_overlay_load,
|
||||
NULL, /* load_textures */
|
||||
gekko_overlay_tex_geom,
|
||||
gekko_overlay_vertex_geom,
|
||||
gekko_overlay_full_screen,
|
||||
gekko_overlay_set_alpha,
|
||||
};
|
||||
|
||||
static void gekko_get_overlay_interface(void *data,
|
||||
const video_overlay_interface_t **iface)
|
||||
{
|
||||
*iface = &gekko_overlay_interface;
|
||||
}
|
||||
#endif
|
||||
|
||||
video_driver_t video_gx = {
|
||||
gekko_init,
|
||||
gekko_frame,
|
||||
gekko_set_nonblock_state,
|
||||
gekko_alive,
|
||||
gekko_focus,
|
||||
gekko_suppress_screensaver,
|
||||
NULL, /* has_windowed */
|
||||
gekko_set_shader,
|
||||
gekko_free,
|
||||
"gx",
|
||||
NULL, /* set_viewport */
|
||||
gekko_set_rotation,
|
||||
gekko_viewport_info,
|
||||
NULL, /* read_viewport */
|
||||
#ifdef HAVE_OVERLAY
|
||||
gekko_get_overlay_interface,
|
||||
#endif
|
||||
gekko_get_poke_interface,
|
||||
NULL, /* wrap_type_to_enum */
|
||||
NULL, /* shader_load_begin */
|
||||
NULL, /* shader_load_step */
|
||||
#ifdef HAVE_GFX_WIDGETS
|
||||
NULL /* gfx_widgets_enabled */
|
||||
#endif
|
||||
};
|
||||
+16
-3
@@ -628,7 +628,7 @@ VIDEO DRIVER
|
||||
#include "../gfx/common/dxgi_common.c"
|
||||
#endif
|
||||
|
||||
#if defined(GEKKO)
|
||||
#if defined(GEKKO) && defined(HAVE_LIBOGC)
|
||||
#ifdef HW_RVL
|
||||
#include "../gfx/drivers/gx_gfx_vi_encoder_libogc.c"
|
||||
#include "../libretro-common/memory/mem2_manager_libogc.c"
|
||||
@@ -705,8 +705,13 @@ VIDEO DRIVER
|
||||
#include "../gfx/drivers/rsx_gfx.c"
|
||||
#elif defined(GEKKO)
|
||||
#include "../gfx/display_servers/dispserv_gx_modes.c"
|
||||
#ifdef HAVE_LIBOGC
|
||||
#include "../gfx/display_servers/dispserv_gx_libogc.c"
|
||||
#include "../gfx/drivers/gx_gfx_libogc.c"
|
||||
#else
|
||||
#include "../gfx/display_servers/dispserv_gekko.c"
|
||||
#include "../gfx/drivers/gekko_gfx.c"
|
||||
#endif
|
||||
#elif defined(PSP)
|
||||
#include "../gfx/drivers/psp1_gfx.c"
|
||||
#elif defined(PS2)
|
||||
@@ -803,7 +808,11 @@ INPUT
|
||||
#include "../input/drivers_joypad/ctr_joypad.c"
|
||||
#elif defined(GEKKO)
|
||||
#include "../input/drivers/gx_input.c"
|
||||
#ifdef HAVE_LIBOGC
|
||||
#include "../input/drivers_joypad/gx_joypad_libogc.c"
|
||||
#else
|
||||
#include "../input/drivers_joypad/gekko_joypad.c"
|
||||
#endif
|
||||
#elif defined(__wiiu__)
|
||||
#include "../input/common/hid/hid_device_driver.c"
|
||||
#include "../input/common/hid/device_wiiu_gca.c"
|
||||
@@ -1040,8 +1049,10 @@ AUDIO
|
||||
#include "../audio/drivers/ps3_audio.c"
|
||||
#elif defined(XENON)
|
||||
#include "../audio/drivers/xenon360_audio.c"
|
||||
#elif defined(GEKKO)
|
||||
#elif defined(GEKKO) && defined(HAVE_LIBOGC)
|
||||
#include "../audio/drivers/gx_audio_libogc.c"
|
||||
#elif defined(GEKKO)
|
||||
#include "../audio/drivers/gekko_audio.c"
|
||||
#elif defined(__wiiu__)
|
||||
#include "../audio/drivers/wiiu_audio.c"
|
||||
#elif defined(HAVE_RWEBAUDIO)
|
||||
@@ -1360,11 +1371,13 @@ FRONTEND
|
||||
#include "../frontend/drivers/platform_xdk.c"
|
||||
#endif
|
||||
|
||||
#if defined(GEKKO)
|
||||
#if defined(GEKKO) && defined(HAVE_LIBOGC)
|
||||
#include "../frontend/drivers/platform_gx_libogc.c"
|
||||
#ifdef HW_RVL
|
||||
#include "../frontend/drivers/platform_wii_libogc.c"
|
||||
#endif
|
||||
#elif defined(GEKKO)
|
||||
#include "../frontend/drivers/platform_gekko.c"
|
||||
#elif defined(__wiiu__)
|
||||
#include "../frontend/drivers/platform_wiiu.c"
|
||||
#elif defined(PS2)
|
||||
|
||||
@@ -0,0 +1,426 @@
|
||||
/* RetroArch - A frontend for libretro.
|
||||
*
|
||||
* RetroArch is free software: you can redistribute it and/or modify it under the terms
|
||||
* of the GNU General Public License as published by the Free Software Found-
|
||||
* ation, either version 3 of the License, or (at your option) any later version.
|
||||
*
|
||||
* RetroArch is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
|
||||
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
|
||||
* PURPOSE. See the GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along with RetroArch.
|
||||
* If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
/* GameCube controllers and (Wii) remotes with their extensions, on
|
||||
* os/gekko. The button numbering is the libogc driver's, so the
|
||||
* built-in autoconfig profiles and saved binds carry over. */
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include <gekko/pad.h>
|
||||
#include <gekko/power.h>
|
||||
#ifdef HW_RVL
|
||||
#include <gekko/wiimote.h>
|
||||
#endif
|
||||
|
||||
#include "../../config.def.h"
|
||||
|
||||
#include "../input_driver.h"
|
||||
#include "../../retroarch.h"
|
||||
#include "../../tasks/tasks_internal.h"
|
||||
|
||||
enum
|
||||
{
|
||||
GX_GC_A = 0,
|
||||
GX_GC_B = 1,
|
||||
GX_GC_X = 2,
|
||||
GX_GC_Y = 3,
|
||||
GX_GC_START = 4,
|
||||
GX_GC_HOME = 5, /* the menu, from a button combination */
|
||||
GX_GC_Z_TRIGGER = 6,
|
||||
GX_GC_L_TRIGGER = 7,
|
||||
GX_GC_R_TRIGGER = 8,
|
||||
GX_GC_UP = 9,
|
||||
GX_GC_DOWN = 10,
|
||||
GX_GC_LEFT = 11,
|
||||
GX_GC_RIGHT = 12,
|
||||
GX_CLASSIC_A = 13,
|
||||
GX_CLASSIC_B = 14,
|
||||
GX_CLASSIC_X = 15,
|
||||
GX_CLASSIC_Y = 16,
|
||||
GX_CLASSIC_PLUS = 17,
|
||||
GX_CLASSIC_MINUS = 18,
|
||||
GX_CLASSIC_HOME = 19,
|
||||
GX_CLASSIC_L_TRIGGER = 20,
|
||||
GX_CLASSIC_R_TRIGGER = 21,
|
||||
GX_CLASSIC_ZL_TRIGGER = 22,
|
||||
GX_CLASSIC_ZR_TRIGGER = 23,
|
||||
GX_CLASSIC_UP = 24,
|
||||
GX_CLASSIC_DOWN = 25,
|
||||
GX_CLASSIC_LEFT = 26,
|
||||
GX_CLASSIC_RIGHT = 27,
|
||||
GX_WIIMOTE_A = 28,
|
||||
GX_WIIMOTE_B = 29,
|
||||
GX_WIIMOTE_1 = 30,
|
||||
GX_WIIMOTE_2 = 31,
|
||||
GX_WIIMOTE_PLUS = 32,
|
||||
GX_WIIMOTE_MINUS = 33,
|
||||
GX_WIIMOTE_HOME = 34,
|
||||
GX_WIIMOTE_UP = 35,
|
||||
GX_WIIMOTE_DOWN = 36,
|
||||
GX_WIIMOTE_LEFT = 37,
|
||||
GX_WIIMOTE_RIGHT = 38,
|
||||
GX_NUNCHUK_Z = 39,
|
||||
GX_NUNCHUK_C = 40
|
||||
};
|
||||
|
||||
enum pad_kind
|
||||
{
|
||||
KIND_NONE = 0,
|
||||
KIND_GAMECUBE,
|
||||
KIND_WIIMOTE,
|
||||
KIND_NUNCHUK,
|
||||
KIND_CLASSIC
|
||||
};
|
||||
|
||||
#define BIT(n) (UINT64_C(1) << (n))
|
||||
|
||||
/* TODO/FIXME - global referenced outside */
|
||||
extern uint64_t lifecycle_state;
|
||||
|
||||
static uint64_t pad_state[DEFAULT_MAX_PADS];
|
||||
static int16_t analog_state[DEFAULT_MAX_PADS][2][2];
|
||||
static uint8_t pad_kind[DEFAULT_MAX_PADS];
|
||||
static volatile uint8_t reset_pressed;
|
||||
static volatile uint8_t power_pressed;
|
||||
|
||||
static void on_reset(void *data) { (void)data; reset_pressed = 1; }
|
||||
static void on_power(void *data) { (void)data; power_pressed = 1; }
|
||||
|
||||
#ifdef HW_RVL
|
||||
/* There is no pointer yet; gx_input asks. */
|
||||
bool gxpad_mousevalid(unsigned port) { (void)port; return false; }
|
||||
|
||||
void gx_joypad_read_mouse(unsigned port, int *irx, int *iry,
|
||||
uint32_t *button)
|
||||
{
|
||||
(void)port;
|
||||
*irx = 0;
|
||||
*iry = 0;
|
||||
*button = 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
static const char *gekko_joypad_name(unsigned pad)
|
||||
{
|
||||
if (pad >= DEFAULT_MAX_PADS)
|
||||
return NULL;
|
||||
switch (pad_kind[pad])
|
||||
{
|
||||
case KIND_GAMECUBE: return "GameCube Controller";
|
||||
case KIND_WIIMOTE: return "Wiimote Controller";
|
||||
case KIND_NUNCHUK: return "Nunchuk Controller";
|
||||
case KIND_CLASSIC: return "Classic Controller";
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static bool gekko_joypad_query_pad(unsigned pad)
|
||||
{
|
||||
return pad < MAX_USERS && pad < DEFAULT_MAX_PADS
|
||||
&& pad_kind[pad] != KIND_NONE;
|
||||
}
|
||||
|
||||
static int32_t gekko_joypad_button(unsigned port, uint16_t joykey)
|
||||
{
|
||||
if (port >= DEFAULT_MAX_PADS || joykey >= 64)
|
||||
return 0;
|
||||
return (pad_state[port] & BIT(joykey)) != 0;
|
||||
}
|
||||
|
||||
static void gekko_joypad_get_buttons(unsigned port, input_bits_t *state)
|
||||
{
|
||||
if (port < DEFAULT_MAX_PADS)
|
||||
{
|
||||
BITS_COPY64_PTR(state, pad_state[port]);
|
||||
}
|
||||
else
|
||||
BIT256_CLEAR_ALL_PTR(state);
|
||||
}
|
||||
|
||||
static int16_t axis_value(unsigned port, unsigned axis)
|
||||
{
|
||||
return axis < 2 ? analog_state[port][0][axis]
|
||||
: analog_state[port][1][axis - 2];
|
||||
}
|
||||
|
||||
static int16_t gekko_joypad_axis(unsigned port, uint32_t joyaxis)
|
||||
{
|
||||
int16_t v;
|
||||
if (port >= DEFAULT_MAX_PADS)
|
||||
return 0;
|
||||
if (AXIS_NEG_GET(joyaxis) < 4)
|
||||
{
|
||||
v = axis_value(port, AXIS_NEG_GET(joyaxis));
|
||||
return v < 0 ? v : 0;
|
||||
}
|
||||
if (AXIS_POS_GET(joyaxis) < 4)
|
||||
{
|
||||
v = axis_value(port, AXIS_POS_GET(joyaxis));
|
||||
return v > 0 ? v : 0;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int16_t gekko_joypad_state(rarch_joypad_info_t *joypad_info,
|
||||
const struct retro_keybind *binds, unsigned port)
|
||||
{
|
||||
unsigned i;
|
||||
int16_t ret = 0;
|
||||
uint16_t port_idx = joypad_info->joy_idx;
|
||||
|
||||
if (port_idx >= DEFAULT_MAX_PADS)
|
||||
return 0;
|
||||
|
||||
for (i = 0; i < RARCH_FIRST_CUSTOM_BIND; i++)
|
||||
{
|
||||
/* Auto-binds are per joypad, not per user. */
|
||||
const uint64_t joykey = (binds[i].joykey != NO_BTN)
|
||||
? binds[i].joykey : joypad_info->auto_binds[i].joykey;
|
||||
const uint32_t joyaxis = (binds[i].joyaxis != AXIS_NONE)
|
||||
? binds[i].joyaxis : joypad_info->auto_binds[i].joyaxis;
|
||||
if ((uint16_t)joykey != NO_BTN && joykey < 64
|
||||
&& (pad_state[port_idx] & BIT(joykey)))
|
||||
ret |= (1 << i);
|
||||
else if (joyaxis != AXIS_NONE
|
||||
&& ((float)abs(gekko_joypad_axis(port_idx, joyaxis)) / 0x8000)
|
||||
> joypad_info->axis_threshold)
|
||||
ret |= (1 << i);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void gamecube_state(unsigned port, const gk_pad_t *p)
|
||||
{
|
||||
uint64_t *s = &pad_state[port];
|
||||
const uint64_t combo = BIT(GX_GC_START) | BIT(GX_GC_Z_TRIGGER)
|
||||
| BIT(GX_GC_L_TRIGGER) | BIT(GX_GC_R_TRIGGER);
|
||||
uint16_t b = p->buttons;
|
||||
|
||||
if (b & GK_PAD_A) *s |= BIT(GX_GC_A);
|
||||
if (b & GK_PAD_B) *s |= BIT(GX_GC_B);
|
||||
if (b & GK_PAD_X) *s |= BIT(GX_GC_X);
|
||||
if (b & GK_PAD_Y) *s |= BIT(GX_GC_Y);
|
||||
if (b & GK_PAD_UP) *s |= BIT(GX_GC_UP);
|
||||
if (b & GK_PAD_DOWN) *s |= BIT(GX_GC_DOWN);
|
||||
if (b & GK_PAD_LEFT) *s |= BIT(GX_GC_LEFT);
|
||||
if (b & GK_PAD_RIGHT) *s |= BIT(GX_GC_RIGHT);
|
||||
if (b & GK_PAD_START) *s |= BIT(GX_GC_START);
|
||||
if (b & GK_PAD_Z) *s |= BIT(GX_GC_Z_TRIGGER);
|
||||
if ((b & GK_PAD_L) || p->trigger_l > 127) *s |= BIT(GX_GC_L_TRIGGER);
|
||||
if ((b & GK_PAD_R) || p->trigger_r > 127) *s |= BIT(GX_GC_R_TRIGGER);
|
||||
|
||||
analog_state[port][0][0] = (int16_t)(p->stick_x * 256);
|
||||
analog_state[port][0][1] = (int16_t)(p->stick_y * -256);
|
||||
analog_state[port][1][0] = (int16_t)(p->sub_x * 256);
|
||||
analog_state[port][1][1] = (int16_t)(p->sub_y * -256);
|
||||
|
||||
/* Start, Z, L and R together open the menu. */
|
||||
if ((*s & combo) == combo)
|
||||
*s |= BIT(GX_GC_HOME);
|
||||
}
|
||||
|
||||
#ifdef HW_RVL
|
||||
static void wiimote_state(unsigned port, const gk_wiimote_t *w)
|
||||
{
|
||||
uint64_t *s = &pad_state[port];
|
||||
uint16_t b = w->buttons;
|
||||
uint16_t e = w->ext_buttons;
|
||||
|
||||
if (b & GK_WM_A) *s |= BIT(GX_WIIMOTE_A);
|
||||
if (b & GK_WM_B) *s |= BIT(GX_WIIMOTE_B);
|
||||
if (b & GK_WM_ONE) *s |= BIT(GX_WIIMOTE_1);
|
||||
if (b & GK_WM_TWO) *s |= BIT(GX_WIIMOTE_2);
|
||||
if (b & GK_WM_PLUS) *s |= BIT(GX_WIIMOTE_PLUS);
|
||||
if (b & GK_WM_MINUS) *s |= BIT(GX_WIIMOTE_MINUS);
|
||||
if (b & GK_WM_HOME) *s |= BIT(GX_WIIMOTE_HOME);
|
||||
|
||||
if (w->ext == GK_WM_EXT_NUNCHUK)
|
||||
{
|
||||
/* Held upright beside a Nunchuk: the d-pad as it is. */
|
||||
if (b & GK_WM_UP) *s |= BIT(GX_WIIMOTE_UP);
|
||||
if (b & GK_WM_DOWN) *s |= BIT(GX_WIIMOTE_DOWN);
|
||||
if (b & GK_WM_LEFT) *s |= BIT(GX_WIIMOTE_LEFT);
|
||||
if (b & GK_WM_RIGHT) *s |= BIT(GX_WIIMOTE_RIGHT);
|
||||
if (e & GK_NC_Z) *s |= BIT(GX_NUNCHUK_Z);
|
||||
if (e & GK_NC_C) *s |= BIT(GX_NUNCHUK_C);
|
||||
analog_state[port][0][0] = (int16_t)(w->stick[0][0] * 256);
|
||||
analog_state[port][0][1] = (int16_t)(w->stick[0][1] * -256);
|
||||
return;
|
||||
}
|
||||
|
||||
/* Held sideways: the d-pad turns with it. */
|
||||
if (b & GK_WM_UP) *s |= BIT(GX_WIIMOTE_LEFT);
|
||||
if (b & GK_WM_DOWN) *s |= BIT(GX_WIIMOTE_RIGHT);
|
||||
if (b & GK_WM_LEFT) *s |= BIT(GX_WIIMOTE_DOWN);
|
||||
if (b & GK_WM_RIGHT) *s |= BIT(GX_WIIMOTE_UP);
|
||||
|
||||
if (w->ext == GK_WM_EXT_CLASSIC)
|
||||
{
|
||||
if (e & GK_CC_A) *s |= BIT(GX_CLASSIC_A);
|
||||
if (e & GK_CC_B) *s |= BIT(GX_CLASSIC_B);
|
||||
if (e & GK_CC_X) *s |= BIT(GX_CLASSIC_X);
|
||||
if (e & GK_CC_Y) *s |= BIT(GX_CLASSIC_Y);
|
||||
if (e & GK_CC_UP) *s |= BIT(GX_CLASSIC_UP);
|
||||
if (e & GK_CC_DOWN) *s |= BIT(GX_CLASSIC_DOWN);
|
||||
if (e & GK_CC_LEFT) *s |= BIT(GX_CLASSIC_LEFT);
|
||||
if (e & GK_CC_RIGHT) *s |= BIT(GX_CLASSIC_RIGHT);
|
||||
if (e & GK_CC_PLUS) *s |= BIT(GX_CLASSIC_PLUS);
|
||||
if (e & GK_CC_MINUS) *s |= BIT(GX_CLASSIC_MINUS);
|
||||
if (e & GK_CC_HOME) *s |= BIT(GX_CLASSIC_HOME);
|
||||
if (e & GK_CC_L) *s |= BIT(GX_CLASSIC_L_TRIGGER);
|
||||
if (e & GK_CC_R) *s |= BIT(GX_CLASSIC_R_TRIGGER);
|
||||
if (e & GK_CC_ZL) *s |= BIT(GX_CLASSIC_ZL_TRIGGER);
|
||||
if (e & GK_CC_ZR) *s |= BIT(GX_CLASSIC_ZR_TRIGGER);
|
||||
analog_state[port][0][0] = (int16_t)(w->stick[0][0] * 256);
|
||||
analog_state[port][0][1] = (int16_t)(w->stick[0][1] * -256);
|
||||
analog_state[port][1][0] = (int16_t)(w->stick[1][0] * 256);
|
||||
analog_state[port][1][1] = (int16_t)(w->stick[1][1] * -256);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
static void hotplug(unsigned port, uint8_t kind)
|
||||
{
|
||||
if (kind == KIND_NONE)
|
||||
input_autoconfigure_disconnect(port, gekko_joypad_name(port));
|
||||
pad_kind[port] = kind;
|
||||
if (kind != KIND_NONE)
|
||||
input_autoconfigure_connect(gekko_joypad_name(port), NULL, NULL,
|
||||
gx_joypad.ident, port, 0, 0);
|
||||
}
|
||||
|
||||
static void gekko_joypad_poll(void)
|
||||
{
|
||||
gk_pad_t pads[GK_PAD_PORTS];
|
||||
uint64_t menu_keys;
|
||||
unsigned port, i, j;
|
||||
|
||||
if (power_pressed)
|
||||
{
|
||||
power_pressed = 0;
|
||||
retroarch_ctl(RARCH_CTL_SET_SHUTDOWN, NULL);
|
||||
return;
|
||||
}
|
||||
|
||||
gk_pad_read(pads);
|
||||
|
||||
for (port = 0; port < DEFAULT_MAX_PADS; port++)
|
||||
{
|
||||
uint8_t kind = KIND_NONE;
|
||||
pad_state[port] = 0;
|
||||
memset(analog_state[port], 0, sizeof(analog_state[port]));
|
||||
|
||||
if (port < GK_PAD_PORTS && pads[port].connected)
|
||||
{
|
||||
gamecube_state(port, &pads[port]);
|
||||
kind = KIND_GAMECUBE;
|
||||
}
|
||||
#ifdef HW_RVL
|
||||
else
|
||||
{
|
||||
gk_wiimote_t w;
|
||||
if (port < GK_WIIMOTE_SLOTS && !gk_wiimote_read(port, &w))
|
||||
{
|
||||
wiimote_state(port, &w);
|
||||
kind = w.ext == GK_WM_EXT_NUNCHUK ? KIND_NUNCHUK
|
||||
: w.ext == GK_WM_EXT_CLASSIC ? KIND_CLASSIC : KIND_WIIMOTE;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
if (kind != pad_kind[port])
|
||||
hotplug(port, kind);
|
||||
|
||||
for (i = 0; i < 2; i++)
|
||||
for (j = 0; j < 2; j++)
|
||||
if (analog_state[port][i][j] == -0x8000)
|
||||
analog_state[port][i][j] = -0x7fff;
|
||||
}
|
||||
|
||||
/* The reset button opens the menu, as each pad's home does. */
|
||||
if (reset_pressed)
|
||||
{
|
||||
reset_pressed = 0;
|
||||
pad_state[0] |= BIT(GX_GC_HOME);
|
||||
}
|
||||
|
||||
menu_keys = BIT(GX_GC_HOME);
|
||||
#ifdef HW_RVL
|
||||
menu_keys |= BIT(GX_WIIMOTE_HOME) | BIT(GX_CLASSIC_HOME);
|
||||
#endif
|
||||
BIT64_CLEAR(lifecycle_state, RARCH_MENU_TOGGLE);
|
||||
if (pad_state[0] & menu_keys)
|
||||
BIT64_SET(lifecycle_state, RARCH_MENU_TOGGLE);
|
||||
}
|
||||
|
||||
static bool gekko_joypad_set_rumble(unsigned pad,
|
||||
enum retro_rumble_effect effect, uint16_t strength)
|
||||
{
|
||||
static uint16_t level[DEFAULT_MAX_PADS][2];
|
||||
if (pad >= DEFAULT_MAX_PADS)
|
||||
return false;
|
||||
level[pad][effect == RETRO_RUMBLE_STRONG ? 0 : 1] = strength;
|
||||
/* Motors that are on or off: on while either effect is asked for. */
|
||||
if (pad_kind[pad] == KIND_GAMECUBE)
|
||||
gk_pad_rumble(pad, level[pad][0] || level[pad][1]);
|
||||
#ifdef HW_RVL
|
||||
else if (pad_kind[pad] != KIND_NONE)
|
||||
gk_wiimote_rumble(pad, level[pad][0] || level[pad][1]);
|
||||
#endif
|
||||
else
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
static void *gekko_joypad_init(void *data)
|
||||
{
|
||||
(void)data;
|
||||
gk_power_set_callbacks(on_power, on_reset, NULL);
|
||||
gk_pad_init();
|
||||
#ifdef HW_RVL
|
||||
gk_wiimote_init();
|
||||
#endif
|
||||
gekko_joypad_poll();
|
||||
return (void*)-1;
|
||||
}
|
||||
|
||||
static void gekko_joypad_destroy(void)
|
||||
{
|
||||
unsigned i;
|
||||
for (i = 0; i < DEFAULT_MAX_PADS; i++)
|
||||
{
|
||||
gekko_joypad_set_rumble(i, RETRO_RUMBLE_STRONG, 0);
|
||||
gekko_joypad_set_rumble(i, RETRO_RUMBLE_WEAK, 0);
|
||||
}
|
||||
}
|
||||
|
||||
input_device_driver_t gx_joypad = {
|
||||
gekko_joypad_init,
|
||||
gekko_joypad_query_pad,
|
||||
gekko_joypad_destroy,
|
||||
gekko_joypad_button,
|
||||
gekko_joypad_state,
|
||||
gekko_joypad_get_buttons,
|
||||
gekko_joypad_axis,
|
||||
gekko_joypad_poll,
|
||||
gekko_joypad_set_rumble,
|
||||
NULL, /* set_rumble_gain */
|
||||
NULL, /* set_sensor_state */
|
||||
NULL, /* get_sensor_input */
|
||||
gekko_joypad_name,
|
||||
"gx",
|
||||
};
|
||||
@@ -97,7 +97,9 @@ extern long syscall(long number, ...);
|
||||
#include <sys/sys_time.h>
|
||||
#endif
|
||||
|
||||
#ifdef GEKKO
|
||||
#if defined(GEKKO_NATIVE)
|
||||
#include <gekko/gekko.h>
|
||||
#elif defined(GEKKO)
|
||||
#include <ogc/lwp_watchdog.h>
|
||||
#endif
|
||||
|
||||
@@ -198,6 +200,8 @@ retro_perf_tick_t cpu_features_get_perf_counter(void)
|
||||
QueryPerformanceCounter(&c);
|
||||
time_ticks = (retro_perf_tick_t)c.QuadPart;
|
||||
}
|
||||
#elif defined(GEKKO_NATIVE)
|
||||
time_ticks = gk_ticks();
|
||||
#elif defined(GEKKO)
|
||||
time_ticks = gettime();
|
||||
#elif !defined(__MACH__) && !defined(__FreeBSD__) && (defined(_XBOX360) || defined(__powerpc__) || defined(__ppc__) || defined(__POWERPC__) || defined(__PSL1GHT__) || defined(__PPC64__) || defined(__powerpc64__))
|
||||
@@ -245,6 +249,8 @@ retro_time_t cpu_features_get_time_usec(void)
|
||||
return sysGetSystemTime();
|
||||
#elif !defined(__PSL1GHT__) && defined(__PS3__)
|
||||
return sys_time_get_system_time();
|
||||
#elif defined(GEKKO_NATIVE)
|
||||
return gk_ticks_to_us(gk_ticks());
|
||||
#elif defined(GEKKO)
|
||||
return ticks_to_microsecs(gettime());
|
||||
#elif defined(WIIU)
|
||||
|
||||
@@ -46,6 +46,8 @@
|
||||
* a different arm's code. */
|
||||
#if defined(_3DS)
|
||||
#define MEM_STATS_CTR 1
|
||||
#elif defined(GEKKO_NATIVE)
|
||||
#define MEM_STATS_GEKKO 1
|
||||
#elif defined(GEKKO)
|
||||
#define MEM_STATS_GX 1
|
||||
#elif defined(VITA)
|
||||
@@ -75,6 +77,13 @@
|
||||
#include <3ds.h>
|
||||
/* osGetMemRegionSize/Free and MEMREGION_ALL are declared here */
|
||||
#include <3ds/os.h>
|
||||
#elif defined(MEM_STATS_GEKKO)
|
||||
/* os/gekko's heap grows through MEM1 and then (Wii) MEM2; what is not
|
||||
* yet the heap is what the arenas still hold. */
|
||||
#include <malloc.h>
|
||||
#include <gekko/gekko.h>
|
||||
#define GEKKO_MEM1_SIZE 0x01800000u
|
||||
#define GEKKO_MEM2_SIZE 0x04000000u
|
||||
#elif defined(MEM_STATS_GX)
|
||||
/* SYSMEM1_SIZE is RetroArch's own, not the SDK's - platform_gx_libogc.c and
|
||||
* gx_gfx_libogc.c both take it from here. SYS_GetArena1Size is gccore's. */
|
||||
@@ -295,6 +304,12 @@ uint64_t mem_stats_total(void)
|
||||
{
|
||||
#if defined(MEM_STATS_CTR)
|
||||
return osGetMemRegionSize(MEMREGION_ALL);
|
||||
#elif defined(MEM_STATS_GEKKO)
|
||||
#ifdef HW_RVL
|
||||
return GEKKO_MEM1_SIZE + GEKKO_MEM2_SIZE;
|
||||
#else
|
||||
return GEKKO_MEM1_SIZE;
|
||||
#endif
|
||||
#elif defined(MEM_STATS_GX)
|
||||
#if defined(HW_RVL) && !defined(IS_SALAMANDER)
|
||||
return SYSMEM1_SIZE + gx_mem2_total();
|
||||
@@ -411,6 +426,12 @@ uint64_t mem_stats_free(void)
|
||||
{
|
||||
#if defined(MEM_STATS_CTR)
|
||||
return osGetMemRegionFree(MEMREGION_ALL);
|
||||
#elif defined(MEM_STATS_GEKKO)
|
||||
{
|
||||
struct mallinfo mi = mallinfo();
|
||||
return (uint64_t)(gk_mem1.hi - gk_mem1.lo)
|
||||
+ (uint64_t)(gk_mem2.hi - gk_mem2.lo) + (uint64_t)mi.fordblks;
|
||||
}
|
||||
#elif defined(MEM_STATS_GX)
|
||||
{
|
||||
/* SYS_GetArena1Size() reports remaining MEM1 directly. */
|
||||
|
||||
@@ -56,7 +56,7 @@
|
||||
#endif
|
||||
#include <windows.h>
|
||||
#endif
|
||||
#elif defined(GEKKO)
|
||||
#elif defined(GEKKO) && !defined(GEKKO_NATIVE)
|
||||
#define USE_GX_THREADS
|
||||
#include <gccore.h>
|
||||
#include <ogc/lwp.h>
|
||||
@@ -1274,6 +1274,8 @@ static sthread_t *sthread_create_ex(void (*thread_func)(void*),
|
||||
#ifdef HAVE_THREAD_ATTR
|
||||
pthread_attr_init(&thread_attr);
|
||||
|
||||
/* devkitPPC's pthreads have no scheduling policy to set. */
|
||||
#ifndef GEKKO_NATIVE
|
||||
if ((thread_priority >= 1) && (thread_priority <= 100))
|
||||
{
|
||||
struct sched_param sp;
|
||||
@@ -1284,6 +1286,7 @@ static sthread_t *sthread_create_ex(void (*thread_func)(void*),
|
||||
|
||||
thread_attr_needed = true;
|
||||
}
|
||||
#endif
|
||||
|
||||
#if defined(__APPLE__)
|
||||
/* Default stack size on Apple is 512Kb;
|
||||
|
||||
+9
-1
@@ -70,7 +70,11 @@
|
||||
/* Required for the Wii build, since we have
|
||||
* to query the hardware for the actual display
|
||||
* aspect ratio... */
|
||||
#if defined(HAVE_LIBOGC)
|
||||
#include <ogc/conf.h>
|
||||
#elif defined(HW_RVL)
|
||||
#include <gekko/conf.h>
|
||||
#endif
|
||||
|
||||
/* When running on the Wii, need to round down the
|
||||
* frame buffer width value such that the last two
|
||||
@@ -6818,10 +6822,14 @@ static void rgui_update_menu_viewport(
|
||||
* widescreen. The display aspect ratio cannot therefore
|
||||
* be determined simply by dividing viewport width by height */
|
||||
float delta;
|
||||
#ifdef HW_RVL
|
||||
#if defined(HW_RVL) && defined(HAVE_LIBOGC)
|
||||
float device_aspect = (CONF_GetAspectRatio() == CONF_ASPECT_4_3)
|
||||
? (4.0f / 3.0f)
|
||||
: (16.0f / 9.0f);
|
||||
#elif defined(HW_RVL)
|
||||
float device_aspect = gk_conf_wide() > 0
|
||||
? (16.0f / 9.0f)
|
||||
: (4.0f / 3.0f);
|
||||
#else
|
||||
float device_aspect = (4.0f / 3.0f);
|
||||
#endif
|
||||
|
||||
@@ -0,0 +1,18 @@
|
||||
# os/gekko's sources, for the builds that include this. Set GEKKO_DIR
|
||||
# to where os/gekko is and GEKKO_PLATFORM to rvl (Wii) or dol
|
||||
# (GameCube) first; GEKKO_SOURCES is the list. The console's text
|
||||
# uses RetroArch's gfx/bitmapfont.c, which the including build adds.
|
||||
|
||||
GEKKO_SRC := boot/crt0.S cpu/exception.S cpu/cache.S \
|
||||
kernel/boot.c kernel/exception.c kernel/irq.c kernel/sched.c \
|
||||
kernel/newlib.c kernel/power.c kernel/vi.c kernel/console.c \
|
||||
kernel/gx.c kernel/audio.c kernel/pad.c kernel/exi.c \
|
||||
disk/sdspi.c disk/sdgecko.c disk/usbmsc.c \
|
||||
fs/fat.c fs/fat_devoptab.c
|
||||
|
||||
ifeq ($(GEKKO_PLATFORM),rvl)
|
||||
GEKKO_SRC += rvl/ipc.c rvl/stm.c rvl/es.c rvl/conf.c rvl/ave.c rvl/sdio.c \
|
||||
rvl/iobuf.c rvl/usb.c rvl/usbstorage.c rvl/bt.c rvl/wiimote.c
|
||||
endif
|
||||
|
||||
GEKKO_SOURCES := $(addprefix $(GEKKO_DIR)/,$(GEKKO_SRC))
|
||||
@@ -77,6 +77,13 @@ extern uint32_t gk_tb_hz;
|
||||
#define GK_US_TO_TICKS(us) ((uint64_t)(us) * gk_tb_hz / 1000000u)
|
||||
#define GK_TICKS_TO_US(t) ((uint64_t)(t) * 1000000u / gk_tb_hz)
|
||||
|
||||
/* Ticks to microseconds for any span, uptime included: the short
|
||||
* form above overflows after a few days of ticks. */
|
||||
static INLINE uint64_t gk_ticks_to_us(uint64_t t)
|
||||
{
|
||||
return t / gk_tb_hz * 1000000u + t % gk_tb_hz * 1000000u / gk_tb_hz;
|
||||
}
|
||||
|
||||
/* Cache maintenance over [p, p + len). */
|
||||
void gk_dcache_flush(const void *p, size_t len);
|
||||
void gk_dcache_store(const void *p, size_t len);
|
||||
|
||||
@@ -13,6 +13,7 @@
|
||||
#include <sys/lock.h>
|
||||
#include <sys/time.h>
|
||||
|
||||
#include <gekko/console.h>
|
||||
#include <gekko/power.h>
|
||||
|
||||
#include "kernel.h"
|
||||
@@ -369,6 +370,50 @@ void __syscall_assert_func(const char *file, int line, const char *func,
|
||||
func ? func : "");
|
||||
}
|
||||
|
||||
/* ---- stdout and stderr: lines to the debug channel ---- */
|
||||
|
||||
void OSReport(const char *fmt, ...);
|
||||
|
||||
static char out_line[256];
|
||||
static unsigned out_len;
|
||||
|
||||
static void out_flush(void)
|
||||
{
|
||||
out_line[out_len] = '\0';
|
||||
OSReport("%s\n", out_line);
|
||||
gk_console_write(out_line, out_len);
|
||||
gk_console_write("\n", 1);
|
||||
out_len = 0;
|
||||
}
|
||||
|
||||
static ssize_t out_write(struct _reent *r, void *fd, const char *p,
|
||||
size_t len)
|
||||
{
|
||||
size_t i;
|
||||
uint32_t level = gk_irq_disable();
|
||||
(void)r;
|
||||
(void)fd;
|
||||
for (i = 0; i < len; i++)
|
||||
{
|
||||
if (p[i] == '\n')
|
||||
out_flush();
|
||||
else
|
||||
{
|
||||
out_line[out_len++] = p[i];
|
||||
if (out_len == sizeof(out_line) - 1)
|
||||
out_flush();
|
||||
}
|
||||
}
|
||||
gk_irq_restore(level);
|
||||
return (ssize_t)len;
|
||||
}
|
||||
|
||||
static devoptab_t dotab_out;
|
||||
|
||||
void gk_newlib_init(void)
|
||||
{
|
||||
dotab_out.name = "stdout";
|
||||
dotab_out.write_r = out_write;
|
||||
devoptab_list[STD_OUT] = &dotab_out;
|
||||
devoptab_list[STD_ERR] = &dotab_out;
|
||||
}
|
||||
+5
-13
@@ -23,27 +23,19 @@ CFLAGS := -O2 -Wall -Wextra -Wno-unused-parameter -std=gnu89 $(MACHDEP) \
|
||||
-I$(GK)/include -I$(ROOT)/libretro-common/include
|
||||
LDFLAGS := $(MACHDEP) -T $(GK)/boot/$(PLATFORM).ld -L$(GK)/boot -Wl,--gc-sections
|
||||
|
||||
KERNEL := $(GK)/boot/crt0.S $(GK)/cpu/exception.S $(GK)/cpu/cache.S \
|
||||
$(GK)/kernel/boot.c $(GK)/kernel/exception.c $(GK)/kernel/irq.c \
|
||||
$(GK)/kernel/sched.c $(GK)/kernel/newlib.c $(GK)/kernel/power.c \
|
||||
$(GK)/kernel/vi.c $(GK)/kernel/console.c $(GK)/kernel/gx.c \
|
||||
$(GK)/kernel/audio.c $(GK)/kernel/pad.c $(GK)/kernel/exi.c \
|
||||
$(GK)/disk/sdspi.c $(GK)/disk/sdgecko.c $(GK)/disk/usbmsc.c \
|
||||
$(GK)/fs/fat.c $(GK)/fs/fat_devoptab.c \
|
||||
$(ROOT)/gfx/bitmapfont.c
|
||||
GEKKO_DIR := $(GK)
|
||||
GEKKO_PLATFORM := $(PLATFORM)
|
||||
include $(GK)/gekko.mk
|
||||
KERNEL := $(GEKKO_SOURCES) $(ROOT)/gfx/bitmapfont.c
|
||||
|
||||
TESTS := kernel_test vi_test gx_test io_test exi_test
|
||||
ifeq ($(PLATFORM),rvl)
|
||||
KERNEL += $(GK)/rvl/ipc.c $(GK)/rvl/stm.c $(GK)/rvl/es.c $(GK)/rvl/conf.c \
|
||||
$(GK)/rvl/ave.c $(GK)/rvl/sdio.c $(GK)/rvl/iobuf.c \
|
||||
$(GK)/rvl/usb.c $(GK)/rvl/usbstorage.c $(GK)/rvl/bt.c \
|
||||
$(GK)/rvl/wiimote.c
|
||||
TESTS += ios_test sd_test usb_test wiimote_test
|
||||
endif
|
||||
|
||||
all: $(addsuffix _$(PLATFORM).elf,$(TESTS))
|
||||
|
||||
%_$(PLATFORM).elf: %.c $(KERNEL) $(wildcard $(GK)/include/gekko/*.h $(GK)/kernel/*.h $(GK)/fs/*.h $(GK)/disk/*.h) Makefile
|
||||
%_$(PLATFORM).elf: %.c $(KERNEL) $(wildcard $(GK)/include/gekko/*.h $(GK)/kernel/*.h $(GK)/fs/*.h $(GK)/disk/*.h) Makefile $(GK)/gekko.mk
|
||||
$(CC) $(CFLAGS) $(LDFLAGS) $< $(KERNEL) -lm -o $@
|
||||
|
||||
host:
|
||||
|
||||
Reference in new issue
Block a user