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:
LibretroAdmin authored and libretroadmin committed 2026-10-03 06:18:56 +02:00
1 parent a62e5f4895
commit e62d8258cb
16 files changed
+2411 -39

No files matched your search

+29 -9
View File
@@ -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
View File
@@ -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
+366
View File
@@ -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
};
+325
View File
@@ -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 */
};
+167
View File
@@ -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"
};
+916
View File
@@ -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
View File
@@ -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)
+426
View File
@@ -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",
};
+7 -1
View File
@@ -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)
+21
View File
@@ -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. */
+4 -1
View File
@@ -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
View File
@@ -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
+18
View File
@@ -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))
+7
View File
@@ -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);
+45
View File
@@ -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
View File
@@ -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: