gint/src/kernel/world.c

253 lines
5.7 KiB
C

#include <gint/drivers.h>
#include <gint/cpu.h>
#include <gint/gint.h>
#include <gint/exc.h>
#include <gint/defs/call.h>
#include <gint/hardware.h>
#include <gint/display.h>
#include "kernel.h"
#include <stdlib.h>
#include <string.h>
//---
// World buffer
//---
gint_world_t gint_world_alloc(void)
{
size_t header_size = gint_driver_count() * sizeof(void *);
size_t data_size = 0;
for(int i = 0; i < gint_driver_count(); i++)
data_size += (gint_drivers[i].state_size + 3) & ~3;
void *buffer = malloc(header_size + data_size);
if(!buffer) return NULL;
gint_world_t world = buffer;
buffer += header_size;
for(int i = 0; i < gint_driver_count(); i++)
{
world[i] = buffer;
buffer += (gint_drivers[i].state_size + 3) & ~3;
}
return world;
}
void gint_world_free(gint_world_t world)
{
free(world);
}
//---
// Synchronization
//---
void gint_world_sync(void)
{
/* Unbind all drivers, which waits for async tasks to complete */
for(int i = gint_driver_count() - 1; i >= 0; i--)
{
gint_driver_t *d = &gint_drivers[i];
if(d->unbind) d->unbind();
}
}
//---
// World switch with driver state saves
//---
static int onchip_save_mode = GINT_ONCHIP_REINITIALIZE;
static void *onchip_save_buffer = NULL;
void gint_world_switch_in(gint_world_t world_os, gint_world_t world_addin)
{
/* Unbind from the OS driver and complete foreign asynchronous tasks */
for(int i = gint_driver_count() - 1; i >= 0; i--)
{
gint_driver_t *d = &gint_drivers[i];
if(d->funbind) d->funbind();
}
cpu_atomic_start();
for(int i = 0; i < gint_driver_count(); i++)
{
gint_driver_t *d = &gint_drivers[i];
uint8_t *f = &gint_driver_flags[i];
bool foreign_powered = (!d->hpowered || d->hpowered());
if(foreign_powered)
*f |= GINT_DRV_FOREIGN_POWERED;
else
*f &= ~GINT_DRV_FOREIGN_POWERED;
/* Power the device if it was unpowered previously */
if(!foreign_powered && d->hpoweron) d->hpoweron();
/* For non-shared devices, save previous device state and
consider restoring the preserved one */
if(!(*f & GINT_DRV_SHARED))
{
if(d->hsave)
d->hsave(world_os[i]);
if(!(*f & GINT_DRV_CLEAN) && d->hrestore)
d->hrestore(world_addin[i]);
}
/* Bind the driver, configure if needed. Note that we either
configure or restore the new world's state, not both */
if(d->bind) d->bind();
if(*f & GINT_DRV_CLEAN)
{
if(d->configure) d->configure();
*f &= ~GINT_DRV_CLEAN;
}
}
cpu_atomic_end();
}
void gint_world_switch_out(gint_world_t world_addin, gint_world_t world_os)
{
for(int i = gint_driver_count() - 1; i >= 0; i--)
{
gint_driver_t *d = &gint_drivers[i];
if(d->unbind) d->unbind();
}
cpu_atomic_start();
for(int i = gint_driver_count() - 1; i >= 0; i--)
{
gint_driver_t *d = &gint_drivers[i];
uint8_t *f = &gint_driver_flags[i];
/* Power the device if it was unpowered previously */
if(d->hpowered && !d->hpowered() && d->hpoweron) d->hpoweron();
/* For non-shared devices, save previous device state and
consider restoring the preserved one */
if(!(*f & GINT_DRV_SHARED))
{
if(d->hsave) d->hsave(world_addin[i]);
if(d->hrestore) d->hrestore(world_os[i]);
}
/* Restore the power state of the device */
if(!(*f & GINT_DRV_FOREIGN_POWERED) && d->hpoweroff)
d->hpoweroff();
}
cpu_atomic_end();
}
int gint_world_switch(gint_call_t call)
{
extern void *gint_stack_top;
gint_world_switch_out(gint_world_addin, gint_world_os);
void *ILRAM = (void *)0xe5200000;
void *XRAM = (void *)0xe500e000;
void *YRAM = (void *)0xe5010000;
/* Watch out for stack overflows */
uint32_t *canary = gint_stack_top;
if(canary)
*canary = 0xb7c0ffee;
/* Save on-chip memory if requested */
if(!isSH3() && onchip_save_mode == GINT_ONCHIP_BACKUP) {
void *ptr = onchip_save_buffer;
memcpy(ptr, ILRAM, 4096);
ptr += 4096;
memcpy(ptr, XRAM, 8192);
ptr += 8192;
memcpy(ptr, YRAM, 8192);
ptr += 8192;
}
int rc = gint_call(call);
/* Restore or reinitialize on-chip memory */
if(!isSH3() && onchip_save_mode == GINT_ONCHIP_BACKUP) {
void *ptr = onchip_save_buffer;
memcpy(ILRAM, ptr, 4096);
ptr += 4096;
memcpy(XRAM, ptr, 8192);
ptr += 8192;
memcpy(YRAM, ptr, 8192);
ptr += 8192;
}
else if(!isSH3() && onchip_save_mode == GINT_ONCHIP_REINITIALIZE)
gint_load_onchip_sections();
/* The canary check needs to occur before switching in the gint world;
otherwise we just crash due to the overflow. gint_panic() isn't
really designed to work from the OS world, but it does fine on the
fx-9860G series and sometimes also on the fx-CG series; better crash
attempting to show a panic message than just crash */
if(canary && *canary != 0xb7c0ffee)
gint_panic(0x1080);
gint_world_switch_in(gint_world_os, gint_world_addin);
return rc;
}
void gint_switch(void (*function)(void))
{
gint_world_switch(GINT_CALL(function));
}
void gint_set_onchip_save_mode(int mode, void *ptr)
{
onchip_save_mode = mode;
onchip_save_buffer = ptr;
}
int gint_get_onchip_save_mode(void **ptr)
{
if(ptr)
*ptr = onchip_save_buffer;
return onchip_save_mode;
}
void gint_copy_vram(void)
{
void *__GetVRAMAddress(void);
#if GINT_OS_FX
memcpy(__GetVRAMAddress(), gint_vram, 1024);
#endif
#if GINT_OS_CG && GINT_RENDER_RGB
/* TODO: Improve copied VRAM behavior in gint_osmenu() on fxcg50 */
uint16_t *vram1, *vram2;
dgetvram(&vram1, &vram2);
uint16_t *dst = __GetVRAMAddress();
uint16_t *src = (gint_vram == vram1) ? vram2 + 6 : vram1 + 6;
for(int y = 0; y < 216; y++, dst+=384, src+=396)
for(int x = 0; x < 384; x++)
{
dst[x] = src[x];
}
#endif
#if GINT_OS_CG && GINT_RENDER_MONO
// TODO: VRAM save mechanism for mono video mode on R61524
#endif
}
void gint_poweroff(bool show_logo)
{
void __PowerOff(int show_logo);
gint_copy_vram();
gint_world_switch(GINT_CALL(__PowerOff, (int)show_logo));
}